JIGS (Jigglymoose's Intelligent Gear Simulator)

Automates running multiple simulations on the MWI Combat Simulator with a dynamic, grouped UI and cost-analysis.

// ==UserScript==
// @name         JIGS (Jigglymoose's Intelligent Gear Simulator)
// @namespace    http://tampermonkey.net/
// @version      30.1
// @description  Automates running multiple simulations on the MWI Combat Simulator with a dynamic, grouped UI and cost-analysis.
// @author       Gemini & Jigglymoose
// @license      MIT
// @match        https://shykai.github.io/MWICombatSimulatorTest/dist/
// @match        https://shykai.github.io/MWICombatSimulator/dist/
// @connect      gist.githubusercontent.com
// @connect      www.milkywayidle.com
// @grant        GM_xmlhttpRequest
// @grant        GM_addStyle
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_deleteValue
// ==/UserScript==

(function() {
    'use strict';

    console.log("JIGS (Jigglymoose's Intelligent Gear Simulator) v30.1 Loaded");

    // --- CONFIGURATION ---
    const MARKET_API_URL = 'https://www.milkywayidle.com/game_data/marketplace.json';
    const JIGS_DATA_URL = 'https://gist.githubusercontent.com/JigglyMoose/79db9d275a73a26dec30305865692525/raw/2a527e359bb442b0f5972cbf3a92387774e3339c/jigs_data.json';

    // --- DATA VARIABLES ---
    let HOUSE_RECIPES = {};
    let ITEM_ID_TO_NAME_MAP = {};
    let SPELL_BOOK_XP = {};
    let SIMULATOR_TO_MARKET_MAP = {};
    let ABILITY_XP_LEVELS = [];
    let baselineDps = 0;
    let baselineProfit = 0;
    let baselineExp = 0;
    let baselineEph = 0;
    let baselineDph = 0;
    let baselineSkillXpRates = {};
    let baselineRunTime = 0;
    let marketData = null;
    let isBatchRunning = false;
    let detailedResults = [];
    let simulationQueue = [];
    let jigsNameToPageElementMap = new Map();
    const skillKeywords = ["Stamina", "Intelligence", "Attack", "Melee", "Defense", "Ranged", "Magic"];
    const equipmentKeywords = ["Head", "Necklace", "Earrings", "Body", "Legs", "Feet", "Hands", "Ring", "Main Hand", "Off Hand", "Pouch", "Back", "Charm"];
    const specialIdMap = { 'Zone': 'selectZone', 'Difficulty': 'selectDifficulty', 'Duration': 'inputSimulationTime' };
    let houseKeywords = [];

    // --- 1. UI & STYLES ---
    const controlsPanel = document.createElement('div');
    controlsPanel.id = 'batch-panel';
    controlsPanel.innerHTML = `
        <div id="batch-header" title="Jigglymoose's Intelligent Gear Simulator">
            <span>JIGS</span>
            <div id="jigs-header-buttons">
                <button id="reset-panels-button" title="Reset Panel Positions">⟲</button>
                <button id="batch-toggle">-</button>
            </div>
        </div>
        <div id="batch-content">
            <div id="controls-grid">
                <button id="capture-setup-button" disabled>Manual Capture</button>
                <div id="import-triggers-wrapper">
                    <input type="checkbox" id="import-triggers-checkbox" title="Check this to include trigger import during baseline update.">
                    <label for="import-triggers-checkbox">Import Triggers</label>
                </div>
                <button id="update-baseline-button" disabled>Update Baseline</button>
                <div id="baseline-container">
                    <label for="baseline-dps-input">Baseline DPS</label><input type="text" id="baseline-dps-input" value="0">
                    <label for="baseline-profit-input">Profit/Day</label><input type="text" id="baseline-profit-input" value="0">
                    <label for="baseline-exp-input">Exp/Hour</label><input type="text" id="baseline-exp-input" value="0">
                    <label for="baseline-eph-input">EPH</label><input type="text" id="baseline-eph-input" value="0">
                    <label for="baseline-dph-input">DPH</label><input type="text" id="baseline-dph-input" value="0">
                </div>
                <button id="run-batch-button" disabled>Run Queue</button>
                <div id="queue-actions-group">
                    <button id="add-to-queue-button" disabled>Add to Queue</button>
                    <button id="reset-button" disabled>Reset Inputs</button>
                </div>
                <button id="stop-batch-button" style="display: none;">Stop</button>
            </div>
            <div id="batch-status">Status: Loading game data...</div>
            <div id="jigs-progress-container" style="display: none;">
                <div id="jigs-progress-bar"></div>
            </div>
            <div id="batch-inputs-container">
                 <div id="jigs-player-select-container"></div>
                <details id="sim-settings-group" open><summary>Simulation Settings</summary></details>
                <details id="skills-group" open><summary>Skills</summary></details>
                <details id="equipment-group" open><summary>Equipment</summary></details>
                <details id="abilities-group" open><summary>Abilities</summary></details>
                <details id="food-drink-group" open><summary>Food & Drink</summary></details>
                <details id="house-group" open><summary>House</summary><div id="house-grid-container"></div></details>
            </div>
        </div>
        <div class="jigs-resizer"></div>
    `;
    document.body.appendChild(controlsPanel);

    const resultsPanel = document.createElement('div');
    resultsPanel.id = 'jigs-results-panel';
    resultsPanel.innerHTML = `
        <div id="jigs-results-header"><span>Results</span><button id="results-toggle">-</button></div>
        <div id="jigs-results-content">
             <div id="column-toggle-container">
                  <button id="clear-results-button">Clear Results</button>
                  <div id="column-checkboxes">
                        Show Columns:
                        <label><input type="checkbox" class="column-toggle" data-col="ttp-col" checked> Time to Purchase</label>
                        <label><input type="checkbox" class="column-toggle" data-col="dps-col" checked> DPS</label>
                        <label><input type="checkbox" class="column-toggle" data-col="profit-col" checked> Profit</label>
                        <label><input type="checkbox" class="column-toggle" data-col="exp-col" checked> Experience</label>
                        <label><input type="checkbox" class="column-toggle" data-col="eph-col" checked> EPH</label>
                        <label><input type="checkbox" class="column-toggle" data-col="dph-col" checked> DPH</label>
                  </div>
                  <button id="export-csv-button" disabled>Export to CSV</button>
             </div>
            <div id="batch-results-container">
                <table id="batch-results-table">
                    <thead>
                        <tr>
                            <th class="upgrade-col" data-sort-key="upgrade" title="The specific upgrade being tested">Upgrade</th>
                            <th class="cost-col" data-sort-key="cost" title="The net cost of the upgrade.&#013;Formula: (New Item Buy Price - Old Item Sell Price)">Upgrade Cost</th>
                            <th class="ttp-col" data-sort-key="timeToPurchase" title="Estimated time to afford this upgrade.&#013;Formula: (Upgrade Cost / Baseline Profit Per Day)">Time to Purchase</th>
                            <th class="dps-col" data-sort-key="dpsChange" title="The raw DPS increase from this change.&#013;Formula: New DPS - Baseline DPS">DPS Change</th>
                            <th class="dps-col" data-sort-key="percentChange" title="The percentage of DPS gained.&#013;Formula: (DPS Change / Baseline DPS) * 100">% DPS Change</th>
                            <th class="dps-col" data-sort-key="costPerDps" title="Gold cost for every 0.01% increase in total DPS. Lower is better!&#013;Formula: (Upgrade Cost / % DPS Change) * 0.01">Gold per 0.01% DPS</th>
                            <th class="profit-col" data-sort-key="profitChange" title="The raw profit increase from this change.&#013;Formula: New Profit - Baseline Profit">Profit Change</th>
                            <th class="profit-col" data-sort-key="percentProfitChange" title="The percentage of profit gained.&#013;Formula: (Profit Change / Baseline Profit) * 100">% Profit Change</th>
                            <th class="profit-col" data-sort-key="costPerProfit" title="Gold cost for every 0.01% increase in total Profit. Lower is better!&#013;Formula: (Upgrade Cost / % Profit Change) * 0.01">Gold per 0.01% Profit</th>
                            <th class="exp-col" data-sort-key="expChange" title="The raw experience per hour increase from this change.&#013;Formula: New Exp/Hr - Baseline Exp/Hr">Exp/Hr Change</th>
                            <th class="exp-col" data-sort-key="percentExpChange" title="The percentage of experience per hour gained.&#013;Formula: (Exp Change / Baseline Exp) * 100">% Exp/Hr Change</th>
                            <th class="exp-col" data-sort-key="costPerExp" title="Gold cost for every 0.01% increase in total Exp/Hr. Lower is better!&#013;Formula: (Upgrade Cost / % Exp Change) * 0.01">Gold per 0.01% Exp/Hr</th>
                            <th class="eph-col" data-sort-key="ephChange" title="The raw EPH increase from this change.&#013;Formula: New EPH - Baseline EPH">EPH Change</th>
                            <th class="eph-col" data-sort-key="percentEphChange" title="The percentage of EPH gained.&#013;Formula: (EPH Change / Baseline EPH) * 100">% EPH Change</th>
                            <th class="eph-col" data-sort-key="costPerEph" title="Gold cost for every 0.01% increase in total EPH. Lower is better!&#013;Formula: (Upgrade Cost / % EPH Change) * 0.01">Gold per 0.01% EPH</th>
                            <th class="dph-col" data-sort-key="dphChange" title="The raw DPH change from this change.&#013;Note: Negative is good!&#013;Formula: New DPH - Baseline DPH">DPH Change</th>
                            <th class="dph-col" data-sort-key="percentDphChange" title="The percentage of DPH changed.&#013;Formula: (DPH Change / Baseline DPH) * 100">% DPH Change</th>
                        </tr>
                    </thead>
                    <tbody></tbody>
                </table>
            </div>
        </div>
        <div class="jigs-resizer"></div>
    `;
    document.body.appendChild(resultsPanel);

    const queuePanel = document.createElement('div');
    queuePanel.id = 'jigs-queue-panel';
    queuePanel.innerHTML = `
        <div id="jigs-queue-header"><span>Simulation Queue</span><button id="queue-toggle">-</button></div>
        <div id="jigs-queue-content">
             <div id="jigs-queue-estimate"></div>
             <button id="clear-queue-button">Clear Queue</button>
             <ul id="jigs-queue-list"></ul>
        </div>
        <div class="jigs-resizer"></div>
    `;
    document.body.appendChild(queuePanel);

    GM_addStyle(`
        #batch-panel { position: fixed; bottom: 10px; right: 10px; width: 600px; max-height: 90vh; background-color: #2c2c2c; border: 1px solid #444; border-radius: 5px; color: #eee; z-index: 9999; font-family: sans-serif; display: flex; flex-direction: column; overflow: hidden; }
        #jigs-results-panel { position: fixed; bottom: 10px; left: 10px; width: 1050px; max-height: 90vh; background-color: #2c2c2c; border: 1px solid #444; border-radius: 5px; color: #eee; z-index: 9998; font-family: sans-serif; display: flex; flex-direction: column; overflow: hidden; }
        #jigs-queue-panel { position: fixed; top: 10px; right: 10px; width: 600px; height: 300px; max-height: 45vh; background-color: #2c2c2c; border: 1px solid #444; border-radius: 5px; color: #eee; z-index: 9997; font-family: sans-serif; display: flex; flex-direction: column; overflow: hidden; }
        #jigs-results-header, #jigs-queue-header { background-color: #333; padding: 8px; cursor: move; display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid #444; }
        #jigs-results-header span, #jigs-queue-header span { font-weight: bold; }
        #results-toggle, #queue-toggle { background: #555; border: 1px solid #777; color: white; border-radius: 3px; cursor: pointer; margin-left: 5px; }
        #jigs-results-content, #jigs-queue-content { padding: 10px; display: flex; flex-direction: column; overflow-y: auto; }
        #jigs-queue-list { list-style: decimal; padding-left: 20px; margin: 10px 0 0; font-size: 0.9em; }
        #jigs-queue-estimate { text-align: center; font-style: italic; color: #ccc; margin-bottom: 10px; }
        #clear-queue-button { background-color: #dc3545; color: white; border: none; padding: 5px 10px; border-radius: 4px; cursor: pointer; align-self: flex-start; }
        #batch-header { background-color: #333; padding: 8px; cursor: move; display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid #444; }
        #batch-header span { font-weight: bold; }
        #jigs-header-buttons { display: flex; gap: 5px; }
        #batch-toggle, #reset-panels-button { background: #555; border: 1px solid #777; color: white; border-radius: 3px; cursor: pointer; }
        #batch-content { padding: 10px; display: flex; flex-direction: column; overflow-y: auto; position: relative; }
        .jigs-resizer { position: absolute; width: 12px; height: 12px; right: 0; bottom: 0; cursor: se-resize; }
        #batch-panel.jigs-minimized, #jigs-results-panel.jigs-minimized, #jigs-queue-panel.jigs-minimized { height: auto !important; width: auto !important; bottom: auto !important; left: auto !important; right: 10px !important; }
        #batch-panel.jigs-minimized #batch-content, #jigs-results-panel.jigs-minimized #jigs-results-content, #jigs-queue-panel.jigs-minimized #jigs-queue-content { display: none; }
        #batch-panel.jigs-minimized .jigs-resizer, #jigs-results-panel.jigs-minimized .jigs-resizer, #jigs-queue-panel.jigs-minimized .jigs-resizer { display: none; }
        #batch-panel.jigs-minimized { top: 10px !important; }
        #jigs-results-panel.jigs-minimized { top: 60px !important; }
        #jigs-queue-panel.jigs-minimized { top: 110px !important; }
        #controls-grid { display: grid; grid-template-columns: 1fr 1fr 1fr; grid-template-rows: auto auto; gap: 8px; margin-bottom: 10px; }
        #stop-batch-button { background-color: #c9302c; grid-column: 2 / 3; grid-row: 2/3;}
        #controls-grid button { width: 100%; padding: 8px; color: white; border: none; border-radius: 4px; cursor: pointer; }
        #capture-setup-button { grid-column: 1 / 2; grid-row: 1 / 2; }
        #import-triggers-wrapper { grid-column: 2 / 3; grid-row: 1 / 2; display: flex; align-items: center; justify-content: center; font-size: 0.9em; }
        #update-baseline-button { grid-column: 3 / 4; grid-row: 1 / 2; }
        #baseline-container { grid-column: 1 / 2; grid-row: 2 / 3; display: grid; grid-template-columns: auto 1fr; gap: 4px 8px; align-items: center; font-size: 0.9em; color: #ccc; }
        #baseline-container input { background-color: #1e1e1e; color: #ddd; border: 1px solid #555; width: 100%; box-sizing: border-box; text-align: right; padding: 2px 4px; }
        #run-batch-button { grid-column: 2 / 3; grid-row: 2 / 3; }
        #queue-actions-group { grid-column: 3 / 4; grid-row: 2 / 3; display: flex; gap: 5px; }
        #queue-actions-group > button { flex: 1; }
        #capture-setup-button { background-color: #337ab7; }
        #update-baseline-button { background-color: #f44336; }
        #run-batch-button { background-color: #4CAF50; }
        #add-to-queue-button { background-color: #6f42c1; }
        #reset-button { background-color: #f0ad4e; }
        #run-batch-button:disabled, #capture-setup-button:disabled, #update-baseline-button:disabled, #export-csv-button:disabled, #reset-button:disabled, #add-to-queue-button:disabled, #import-triggers-checkbox:disabled { cursor: not-allowed; }
        #run-batch-button:disabled, #capture-setup-button:disabled, #update-baseline-button:disabled, #export-csv-button:disabled, #reset-button:disabled, #add-to-queue-button:disabled { background-color: #555; }
        #batch-status { margin-bottom: 5px; font-style: italic; color: #aaa; text-align: center; }
        #jigs-progress-container { width: 100%; background-color: #555; border-radius: 5px; height: 10px; margin-bottom: 10px; border: 1px solid #333; }
        #jigs-progress-bar { width: 0%; height: 100%; background-color: #4CAF50; border-radius: 5px; transition: width 0.1s linear; }
        #batch-inputs-container { display: flex; flex-direction: column; gap: 5px; max-height: 40vh; overflow-y: auto; border: 1px solid #444; padding: 10px; margin-bottom: 10px; }
        #jigs-player-select-container { display: grid; grid-template-columns: 100px 1fr; align-items: center; margin-bottom: 10px; gap: 5px; padding-bottom: 10px; border-bottom: 1px solid #444;}
        summary { font-weight: bold; cursor: pointer; padding: 4px; background-color: #333; margin-bottom: 5px; }
        details { border-left: 1px solid #444; padding-left: 10px; margin-bottom: 5px;}
        .batch-input-row { display: grid; grid-template-columns: 100px 1fr auto; align-items: center; margin-bottom: 5px; gap: 5px; }
        .batch-input-row-equip { display: grid; grid-template-columns: 60px 1fr 80px auto; grid-template-rows: auto auto; align-items: center; margin-bottom: 10px; row-gap: 5px; column-gap: 5px; }
        .batch-input-row-ability { display: grid; grid-template-columns: 60px 1fr 80px auto; align-items: center; margin-bottom: 5px; gap: 5px; }
        .batch-input-row label, .batch-input-row-equip > label, .batch-input-row-ability > label { font-size: 0.9em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; grid-row: 1; }
        .batch-input-row select, .batch-input-row input, .batch-input-row-equip > select, .batch-input-row-equip > input[type=number], .batch-input-row-ability > select, .batch-input-row-ability > input { background-color: #1e1e1e; color: #ddd; border: 1px solid #555; width: 100%; box-sizing: border-box; }
        .price-info-container { grid-column: 2 / 4; grid-row: 2 / 3; display: flex; align-items: center; gap: 10px; }
        .jigs-price-override { width: 90px !important; background-color: #1e1e1e; color: #ddd; border: 1px solid #555; box-sizing: border-box; text-align: right; padding: 2px 4px; }
        .constant-checkbox-container { display: flex; align-items: center; justify-content: center; padding: 0 5px; grid-row: 1; grid-column: 4; }
        .market-indicators { flex-grow: 1; display: flex; flex-wrap: wrap; gap: 4px; }
        .market-dot { background-color: #555; color: #ddd; font-size: 0.8em; padding: 1px 5px; border-radius: 4px; cursor: pointer; border: 1px solid #777; }
        .market-dot:hover { background-color: #777; border-color: #999; }
        .market-dot.selected { border-color: #28a745 !important; border-width: 2px !important; padding: 0px 4px !important; }
        .jigs-modified { border-left: 3px solid #f0ad4e !important; }
        #house-grid-container { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 10px; }
        .house-grid-item { display: grid; grid-template-columns: 1fr auto; grid-template-rows: auto auto; gap: 4px 8px; }
        .house-grid-item label { grid-column: 1 / -1; font-size: 0.8em; margin-bottom: 4px; height: 2.5em; overflow: hidden; text-align: center; }
        .house-grid-item input { grid-column: 1 / 2; width: 100%; text-align: center; }
        .house-grid-item .constant-checkbox-container { grid-column: 2 / 3; grid-row: 2 / 3; }
        #column-toggle-container { margin: 5px 0 10px 0; display: flex; justify-content: space-between; align-items: center; font-size: 0.9em; }
        #column-checkboxes { display: flex; gap: 15px; flex-wrap: wrap; justify-content: center; align-items: center; }
        #clear-results-button, #export-csv-button { padding: 4px 8px; color: white; border: none; border-radius: 4px; cursor: pointer; }
        #export-csv-button { background-color: #5bc0de; }
        #export-csv-button:hover { background-color: #46b8da; }
        #clear-results-button { background-color: #dc3545; }
        #clear-results-button:hover { background-color: #c82333; }
        #batch-results-container { margin-top: 10px; max-height: 80vh; overflow-y: auto; }
        #batch-results-table { width: 100%; border-collapse: collapse; }
        #batch-results-table td.best-upgrade { background-color: #28a745 !important; color: #fff !important; }
        #batch-results-table td.worst-upgrade { background-color: #dc3545 !important; color: #fff !important; }
        #batch-results-table th, #batch-results-table td { border: 1px solid #444; padding: 5px; text-align: left; font-size: 0.9em; }
        #batch-results-table th { background-color: #333; cursor: pointer; position: sticky; top: 0; z-index: 1; }
        #batch-results-table th:hover { background-color: #444; }
        .sorted-asc::after { content: ' ▲'; }
        .sorted-desc::after { content: ' ▼'; }
        .trigger-container { margin-left: 20px; margin-bottom: 10px; padding-top: 5px; border-top: 1px solid #444; }
        .trigger-row, .trigger-range-row { display: grid; grid-template-columns: 60px 1fr 1fr 1fr 1fr; gap: 5px; align-items: center; }
        .trigger-row label { font-size: 0.9em; font-style: italic; }
        .trigger-range-row { margin-top: 5px; grid-template-columns: 60px auto 1fr auto 1fr; }
        .trigger-range-row .jigs-range-label, .trigger-range-row .jigs-increment-label { font-size: 0.8em; font-style: italic; text-align: right; padding-right: 5px; }
        .trigger-row select, .trigger-row input, .trigger-range-row input { background-color: #1e1e1e; color: #ddd; border: 1px solid #555; width: 100%; box-sizing: border-box; }
        #jigs-queue-list li {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 5px;
        }
        .jigs-queue-item-label {
            flex-grow: 1;
            padding-right: 10px;
            word-break: break-all;
        }
        .jigs-remove-queue-item-button {
            background-color: #c9302c;
            color: white;
            border: none;
            border-radius: 50%;
            cursor: pointer;
            font-weight: bold;
            width: 20px;
            height: 20px;
            line-height: 20px;
            text-align: center;
            padding: 0;
            flex-shrink: 0;
        }
        .jigs-remove-queue-item-button:hover {
            background-color: #dc3545;
        }
    `);

    // --- 3. HELPER FUNCTIONS ---
    const statusDiv = document.getElementById('batch-status');
    const groupContainers = { skills: document.querySelector('#skills-group'), house: document.querySelector('#house-grid-container'), abilities: document.querySelector('#abilities-group'), equipment: document.querySelector('#equipment-group'), food: document.querySelector('#food-drink-group'), sim: document.querySelector('#sim-settings-group'), };

    function exportResultsToCSV() {
        if (detailedResults.length === 0) {
            alert("No results to export!");
            return;
        }

        const table = document.getElementById('batch-results-table');
        // 1. Get the visible headers and their corresponding data keys
        const headers = Array.from(table.querySelectorAll('thead th'))
            .filter(th => th.style.display !== 'none') // Respects the column visibility toggles
            .map(th => ({
                text: th.getAttribute('title').split(/\r?\n/)[0] || th.textContent, // Use the title for a clean header name
                key: th.dataset.sortKey // This key links the header to the data in the `detailedResults` array
            }));

        // 2. Create the header row for the CSV file
        const headerRow = headers.map(h => `"${h.text.replace(/"/g, '""')}"`).join(',');

        // 3. Create a data row for each result
        const rows = detailedResults.map(result => {
            return headers.map(header => {
                // Use the key from the header to get the correct data point from the result object
                let value;
                switch (header.key) {
                    case 'upgrade':           value = result.upgrade; break;
                    case 'cost':              value = result.cost; break;
                    case 'timeToPurchase':    value = result.timeToPurchase; break;
                    case 'dpsChange':         value = result.dps; break;
                    case 'percentChange':     value = result.percent; break;
                    case 'costPerDps':        value = result.costPerDps; break;
                    case 'profitChange':      value = result.profitChange; break;
                    case 'percentProfitChange':value = result.percentProfitChange; break;
                    case 'costPerProfit':     value = result.costPerProfit; break;
                    case 'expChange':         value = result.expChange; break;
                    case 'percentExpChange':  value = result.percentExpChange; break;
                    case 'costPerExp':        value = result.costPerExp; break;
                    case 'ephChange':         value = result.ephChange; break;
                    case 'percentEphChange':  value = result.percentEphChange; break;
                    case 'costPerEph':        value = result.costPerEph; break;
                    case 'dphChange':         value = result.dphChange; break;
                    case 'percentDphChange':  value = result.percentDphChange; break;
                    default:                  value = '';
                }

                // Format special values for the CSV
                if (value === null || value === undefined || value === "N/A" || !isFinite(value)) {
                    value = ''; // Represent non-finite numbers or N/A as empty cells
                } else if (value === "Free") {
                    value = 0;
                }

                const valueStr = String(value);
                // Enclose the value in quotes if it contains a comma
                if (valueStr.includes(',')) {
                    return `"${valueStr.replace(/"/g, '""')}"`;
                }
                return valueStr;
            }).join(',');
        });

        // 4. Combine headers and rows, create a file, and trigger the download
        const csvContent = [headerRow, ...rows].join('\n');
        const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
        const link = document.createElement("a");
        const url = URL.createObjectURL(blob);
        const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
        link.setAttribute("href", url);
        link.setAttribute("download", `jigs_results_${timestamp}.csv`);
        link.style.visibility = 'hidden';
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
        URL.revokeObjectURL(url);
    }

    function resetPanelPositions() {
        console.log("JIGS DEBUG: Resetting panel positions to default.");
        const panelIds = ['batch-panel', 'jigs-results-panel', 'jigs-queue-panel'];
        panelIds.forEach(id => {
            GM_deleteValue(`jigs_panel_positions_${id}`);
            const panel = document.getElementById(id);
            if (panel) {
                panel.style.top = '';
                panel.style.left = '';
                panel.style.width = '';
                panel.style.height = '';
                panel.style.bottom = '';
                panel.style.right = '';
            }
        });
        statusDiv.textContent = 'Status: Panel positions reset.';
    }

    const groupAllChanges = (itemChanges, triggerChanges) => {
        const upgrades = [];
        const processed = new Set();
        const nameMap = new Map(itemChanges.map(c => [c.name, c]));
        for (const change of itemChanges) {
            if (processed.has(change.name)) continue;
            let baseName = change.name.replace(' Enhancement', '').replace(' Level', '');
            if (processed.has(baseName)) continue;
            processed.add(baseName);
            const mainChange = nameMap.get(baseName);
            const enhChange = nameMap.get(`${baseName} Enhancement`);
            const lvlChange = nameMap.get(`${baseName} Level`);
            const baseChangeObject = mainChange || enhChange || lvlChange;
            let combinedUpgrade = { ...baseChangeObject,
                name: baseName
            };
            if (mainChange) {
                combinedUpgrade.value = mainChange.value;
                combinedUpgrade.originalValue = mainChange.originalValue;
            } else {
                delete combinedUpgrade.value;
                delete combinedUpgrade.originalValue;
            }
            if (enhChange) {
                combinedUpgrade.enhancement = enhChange;
            }
            if (lvlChange) {
                combinedUpgrade.level = lvlChange;
            }

            const priceOverride = mainChange?.priceOverride || enhChange?.priceOverride;
            if (priceOverride) {
                combinedUpgrade.priceOverride = priceOverride;
            }

            if (baseName.startsWith('Ability') || baseName.startsWith('Food') || baseName.startsWith('Drink')) {
                const type = baseName.startsWith('Ability') ? 'ability' : (baseName.startsWith('Food') ? 'food' : 'drink');
                const index = parseInt(baseName.match(/\d+/)[0]) - 1;
                const triggerIndex = triggerChanges.findIndex(t => t.type === type && t.index == index);
                if (triggerIndex > -1) {
                    combinedUpgrade.triggerChange = triggerChanges[triggerIndex];
                    triggerChanges.splice(triggerIndex, 1);
                }
            }
            upgrades.push(combinedUpgrade);
        }
        for (const trigger of triggerChanges) {
            upgrades.push({
                name: `Trigger ${trigger.type} ${parseInt(trigger.index) + 1}`,
                isTriggerOnly: true,
                triggerChange: trigger,
                isConstant: trigger.isConstant
            });
        }
        return upgrades;
    };

    function makeDraggable(panel, handle) {
        let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
        handle.onmousedown = dragMouseDown;

        function dragMouseDown(e) {
            e = e || window.event;
            if (e.target.tagName === 'BUTTON') return;
            e.preventDefault();
            pos3 = e.clientX;
            pos4 = e.clientY;

            if (!panel.style.top && !panel.style.left) {
                const rect = panel.getBoundingClientRect();
                panel.style.top = rect.top + 'px';
                panel.style.left = rect.left + 'px';
                panel.style.right = 'auto';
                panel.style.bottom = 'auto';
            }

            document.onmouseup = closeDragElement;
            document.onmousemove = elementDrag;
        }

        function elementDrag(e) {
            e = e || window.event;
            e.preventDefault();
            pos1 = pos3 - e.clientX;
            pos2 = pos4 - e.clientY;
            pos3 = e.clientX;
            pos4 = e.clientY;
            panel.style.top = (panel.offsetTop - pos2) + "px";
            panel.style.left = (panel.offsetLeft - pos1) + "px";
        }

        function closeDragElement() {
            document.onmouseup = null;
            document.onmousemove = null;
            const savedPositions = GM_getValue(`jigs_panel_positions_${panel.id}`, {});
            savedPositions.top = panel.style.top;
            savedPositions.left = panel.style.left;
            GM_setValue(`jigs_panel_positions_${panel.id}`, savedPositions);
        }
    }

    function makeResizable(panel, resizer) {
        let startX, startY, startWidth, startHeight;

        resizer.addEventListener('mousedown', initDrag, false);

        function initDrag(e) {
            startX = e.clientX;
            startY = e.clientY;
            startWidth = parseInt(document.defaultView.getComputedStyle(panel).width, 10);
            startHeight = parseInt(document.defaultView.getComputedStyle(panel).height, 10);
            document.documentElement.addEventListener('mousemove', doDrag, false);
            document.documentElement.addEventListener('mouseup', stopDrag, false);
        }

        function doDrag(e) {
            panel.style.width = (startWidth + e.clientX - startX) + 'px';
            panel.style.height = (startHeight + e.clientY - startY) + 'px';
        }

        function stopDrag() {
            document.documentElement.removeEventListener('mousemove', doDrag, false);
            document.documentElement.removeEventListener('mouseup', stopDrag, false);
            const savedPositions = GM_getValue(`jigs_panel_positions_${panel.id}`, {});
            savedPositions.width = panel.style.width;
            savedPositions.height = panel.style.height;
            GM_setValue(`jigs_panel_positions_${panel.id}`, savedPositions);
        }
    }

    function createTriggerRow(type, index) {
        const container = document.createElement('div');
        container.className = 'trigger-container';
        const mainRow = document.createElement('div');
        mainRow.className = 'trigger-row';
        mainRow.dataset.triggerType = type;
        mainRow.dataset.triggerIndex = index;
        mainRow.innerHTML = `
            <label>Trigger:</label>
            <select class="jigs-trigger-dependency" data-field="dependency" title="Condition 1" data-original-value=""><option value=""></option></select>
            <select class="jigs-trigger-condition" data-field="condition" title="Condition 2" data-original-value=""><option value=""></option></select>
            <select class="jigs-trigger-comparator" data-field="comparator" title="Condition 3" data-original-value=""><option value=""></option></select>
            <input type="number" class="jigs-trigger-value" data-field="value" placeholder="Value" title="Condition 4" data-original-value="">
        `;
        const rangeRow = document.createElement('div');
        rangeRow.className = 'trigger-range-row';
        rangeRow.innerHTML = `
            <label></label> <label class="jigs-range-label">Range:</label>
            <input type="text" class="jigs-trigger-range" placeholder="e.g., 2000-2500" data-original-value="">
            <label class="jigs-increment-label">Increment:</label>
            <input type="number" class="jigs-trigger-increment" placeholder="e.g., 100" data-original-value="">
        `;
        container.appendChild(mainRow);
        container.appendChild(rangeRow);
        return container;
    }

    function createConstantCheckbox() {
        const container = document.createElement('div');
        container.className = 'constant-checkbox-container';
        const checkbox = document.createElement('input');
        checkbox.type = 'checkbox';
        checkbox.className = 'jigs-constant-checkbox';
        checkbox.title = 'Include this change in every simulation';
        container.appendChild(checkbox);
        return container;
    }

    function updateModifiedIndicator(element) {
        if (!element || typeof element.dataset.originalValue === 'undefined') {
            return;
        }
        const hasChanged = element.value !== element.dataset.originalValue;
        if (hasChanged) {
            element.classList.add('jigs-modified');
        } else {
            element.classList.remove('jigs-modified');
        }
    }

    function normalizeAndParseFloat(s) {
        if (typeof s !== 'string' || !s) return NaN;
        s = s.trim();

        if (s.lastIndexOf(',') > s.lastIndexOf('.')) {
            return parseFloat(s.replace(/\./g, '').replace(',', '.'));
        }

        if (s.indexOf(',') === -1 && (s.match(/\./g) || []).length > 1) {
            return parseFloat(s.replace(/\./g, ''));
        }

        return parseFloat(s.replace(/,/g, ''));
    }

    function parseGold(value) {
        if (typeof value !== 'string') return NaN;
        value = value.toLowerCase().trim();
        const suffix = value.slice(-1);
        let multiplier = 1;
        let numPart = value;

        if (['k', 'm', 'b'].includes(suffix)) {
            numPart = value.slice(0, -1);
            if (suffix === 'k') multiplier = 1e3;
            if (suffix === 'm') multiplier = 1e6;
            if (suffix === 'b') multiplier = 1e9;
        }

        const num = normalizeAndParseFloat(numPart);
        return num * multiplier;
    }

    function updateBaselinesFromInputs() {
        console.log("JIGS DEBUG: Reading baseline values from input fields.");
        const dpsStr = document.getElementById('baseline-dps-input').value;
        const profitStr = document.getElementById('baseline-profit-input').value;
        const expStr = document.getElementById('baseline-exp-input').value;
        const ephStr = document.getElementById('baseline-eph-input').value;
        const dphStr = document.getElementById('baseline-dph-input').value;
        baselineDps = parseGold(dpsStr) || 0;
        baselineProfit = parseGold(profitStr) || 0;
        baselineExp = parseInt(expStr.replace(/\D/g, ''), 10) || 0;
        baselineEph = normalizeAndParseFloat(ephStr) || 0;
        baselineDph = normalizeAndParseFloat(dphStr) || 0;
        console.log(`JIGS DEBUG: Baselines updated to DPS: ${baselineDps} (from '${dpsStr}'), Profit: ${baselineProfit} (from '${profitStr}'), Exp: ${baselineExp} (from '${expStr}'), EPH: ${baselineEph} (from '${ephStr}'), DPH: ${baselineDph} (from '${dphStr}')`);
    }
    function createNumberInput(name, value, min, max, isHouse = false, withCheckbox = true) {
        const container = document.createElement('div');
        container.className = isHouse ? 'house-grid-item' : 'batch-input-row';
        const label = document.createElement('label');
        label.textContent = name;
        label.title = name;
        const input = document.createElement('input');
        input.type = 'number';
        input.value = value;
        input.min = min ?? 1;
        input.max = max ?? 400;
        input.dataset.originalValue = value;
        input.dataset.name = name;
        container.appendChild(label);
        container.appendChild(input);
        if (withCheckbox) {
            container.appendChild(createConstantCheckbox());
        } else {
             container.style.gridTemplateColumns = '100px 1fr';
        }
        return container;
    }
    function createSelect(name, value, options, withCheckbox = true) {
        const row = document.createElement('div');
        row.className = 'batch-input-row';
        const label = document.createElement('label');
        label.textContent = name;
        label.title = name;
        const select = document.createElement('select');
        select.dataset.originalValue = value;
        select.dataset.name = name;
        options.forEach(opt => {
            const option = document.createElement('option');
            option.value = opt;
            option.textContent = opt;
            select.appendChild(option);
        });
        select.value = value;
        row.appendChild(label);
        row.appendChild(select);
        if (withCheckbox) {
            row.appendChild(createConstantCheckbox());
        } else {
            row.style.gridTemplateColumns = '100px 1fr';
        }
        return row;
    }
    function createEquipmentRow(name, itemValue, itemOptions, enhValue) {
        const row = document.createElement('div');
        row.className = 'batch-input-row-equip';
        const label = document.createElement('label');
        label.textContent = name;
        label.title = name;
        const itemSelect = document.createElement('select');
        itemSelect.dataset.originalValue = itemValue;
        itemSelect.dataset.name = name;
        itemOptions.forEach(opt => {
            const option = document.createElement('option');
            option.value = opt;
            option.textContent = opt;
            itemSelect.appendChild(option);
        });
        itemSelect.value = itemValue;
        const enhInput = document.createElement('input');
        enhInput.type = 'number';
        enhInput.value = enhValue;
        enhInput.min = 0;
        enhInput.max = 20;
        enhInput.dataset.originalValue = enhValue;
        enhInput.dataset.name = `${name} Enhancement`;
        const priceInfoContainer = document.createElement('div');
        priceInfoContainer.className = 'price-info-container';
        const priceOverrideInput = document.createElement('input');
        priceOverrideInput.type = 'text';
        priceOverrideInput.className = 'jigs-price-override';
        priceOverrideInput.placeholder = 'Auto Price';
        priceOverrideInput.title = 'Manual price override. Use "k", "m", "b". Clears on item/enh change.';
        const indicatorContainer = document.createElement('div');
        indicatorContainer.className = 'market-indicators';
        priceInfoContainer.appendChild(indicatorContainer);
        priceInfoContainer.appendChild(priceOverrideInput);
        row.appendChild(label);
        row.appendChild(itemSelect);
        row.appendChild(enhInput);
        row.appendChild(createConstantCheckbox());
        row.appendChild(priceInfoContainer);
        itemSelect.addEventListener('change', () => {
            updateMarketIndicators(row);
            updatePriceOverrideField(row);
        });
        enhInput.addEventListener('change', () => {
            updateMarketIndicators(row);
            updatePriceOverrideField(row);
        });
        return row;
    }
    function createAbilityRow(name, itemValue, itemOptions, lvlValue) {
        const row = document.createElement('div');
        row.className = 'batch-input-row-ability';
        const label = document.createElement('label');
        label.textContent = name;
        label.title = name;
        const itemSelect = document.createElement('select');
        itemSelect.dataset.originalValue = itemValue;
        itemSelect.dataset.name = name;
        itemOptions.forEach(opt => {
            if (opt !== 'Promote') {
                const option = document.createElement('option');
                option.value = opt;
                option.textContent = opt;
                itemSelect.appendChild(option);
            }
        });
        itemSelect.value = itemValue;
        const lvlInput = document.createElement('input');
        lvlInput.type = 'number';
        lvlInput.value = lvlValue;
        lvlInput.min = 1;
        lvlInput.max = 200;
        lvlInput.dataset.originalValue = lvlValue;
        lvlInput.dataset.name = `${name} Level`;
        row.appendChild(label);
        row.appendChild(itemSelect);
        row.appendChild(lvlInput);
        row.appendChild(createConstantCheckbox());
        return row;
    }
    function findPageElementByName(name, tag = 'input, select') { if (specialIdMap[name]) { return document.getElementById(specialIdMap[name]); } const labels = Array.from(document.querySelectorAll('label')); const targetLabel = labels.find(l => l.textContent && l.textContent.trim().toLowerCase() === name.toLowerCase()); if (!targetLabel) return null; const parentRow = targetLabel.closest('.row'); if (parentRow) { return parentRow.querySelector(tag); } return null; }
    function getDpsValue() { const resultsContainer = document.getElementById('simulationResultTotalDamageDone'); if (!resultsContainer || !resultsContainer.hasChildNodes()) { return null; } const totalLabelElement = Array.from(resultsContainer.querySelectorAll('div.col-md-5')).find(el => el.textContent.trim() === 'Total'); if (!totalLabelElement) { return null; } const dpsElement = totalLabelElement.nextElementSibling?.nextElementSibling; if (dpsElement) { return dpsElement.textContent.trim(); } return null; }
    function formatGold(value) { if (value === 'N/A' || value === 'Free') return value; if (!isFinite(value) || value === Infinity) return "N/A"; if (value < 1000) return Math.round(value).toLocaleString(); if (value < 1000000) return `${(value / 1000).toFixed(1)}k`; return `${(value / 1000000).toFixed(2)}M`; }
    function addResultRow(result) {
        const resultsTbody = document.querySelector('#batch-results-table tbody');
        const row = resultsTbody.insertRow();
        let costText = formatGold(result.cost);
        let costPerDpsText = formatGold(result.costPerDps);
        let costPerProfitText = formatGold(result.costPerProfit);
        let costPerExpText = formatGold(result.costPerExp);
        let costPerEphText = formatGold(result.costPerEph);
        if (result.cost === Infinity) {
            costText = 'No Seller';
            costPerDpsText = 'N/A';
            costPerProfitText = 'N/A';
            costPerExpText = 'N/A';
            costPerEphText = 'N/A';
        }
        let upgradeText = result.upgrade;
        if (result.books > 0) {
            upgradeText += ` (${result.books.toLocaleString()} books)`;
        } else if (result.timeToLevelText) {
            upgradeText += ` ${result.timeToLevelText}`;
        }
        row.dataset.upgrade = result.upgrade;
        row.dataset.cost = isFinite(result.cost) ? result.cost : Infinity;
        row.dataset.timeToPurchase = isFinite(result.timeToPurchase) ? result.timeToPurchase : Infinity;
        row.dataset.dpsChange = result.dps;
        row.dataset.percentChange = isFinite(result.percent) ? result.percent : Infinity;
        row.dataset.costPerDps = result.costPerDps === 'Free' ? 0 : (isFinite(result.costPerDps) ? result.costPerDps : Infinity);
        row.dataset.profitChange = result.profitChange;
        row.dataset.percentProfitChange = isFinite(result.percentProfitChange) ? result.percentProfitChange : Infinity;
        row.dataset.costPerProfit = result.costPerProfit === 'Free' ? 0 : (isFinite(result.costPerProfit) ? result.costPerProfit : Infinity);
        row.dataset.expChange = result.expChange;
        row.dataset.percentExpChange = isFinite(result.percentExpChange) ? result.percentExpChange : Infinity;
        row.dataset.costPerExp = result.costPerExp === 'Free' ? 0 : (isFinite(result.costPerExp) ? result.costPerExp : Infinity);
        row.dataset.ephChange = result.ephChange;
        row.dataset.percentEphChange = isFinite(result.percentEphChange) ? result.percentEphChange : Infinity;
        row.dataset.costPerEph = result.costPerEph === 'Free' ? 0 : (isFinite(result.costPerEph) ? result.costPerEph : Infinity);
        row.dataset.dphChange = result.dphChange;
        row.dataset.percentDphChange = isFinite(result.percentDphChange) ? result.percentDphChange : Infinity;

        row.innerHTML = ` <td class="upgrade-col">${upgradeText}</td>
                                <td class="cost-col">${costText}</td>
                                <td class="ttp-col">${formatTime(result.timeToPurchase)}</td>
                                <td class="dps-col">${result.dps > 0 ? '+' : ''}${result.dps.toFixed(2)}</td>
                                <td class="dps-col">${isFinite(result.percent) ? result.percent.toFixed(2)+'%' : '∞'}</td>
                                <td class="dps-col">${costPerDpsText}</td>
                                <td class="profit-col">${result.profitChange > 0 ? '+' : ''}${formatGold(result.profitChange)}</td>
                                <td class="profit-col">${isFinite(result.percentProfitChange) ? result.percentProfitChange.toFixed(2)+'%' : '∞'}</td>
                                <td class="profit-col">${costPerProfitText}</td>
                                <td class="exp-col">${result.expChange > 0 ? '+' : ''}${result.expChange.toLocaleString()}</td>
                                <td class="exp-col">${isFinite(result.percentExpChange) ? result.percentExpChange.toFixed(2)+'%' : '∞'}</td>
                                <td class="exp-col">${costPerExpText}</td>
                                <td class="eph-col">${result.ephChange > 0 ? '+' : ''}${result.ephChange.toFixed(2)}</td>
                                <td class="eph-col">${isFinite(result.percentEphChange) ? result.percentEphChange.toFixed(2)+'%' : '∞'}</td>
                                <td class="eph-col">${costPerEphText}</td>
                                <td class="dph-col">${result.dphChange > 0 ? '+' : ''}${result.dphChange.toFixed(2)}</td>
                                <td class="dph-col">${isFinite(result.percentDphChange) ? result.percentDphChange.toFixed(2)+'%' : '∞'}</td>`;
    }
    function getProfitValue() {
        const durationInput = document.getElementById('inputSimulationTime');
        const durationHours = durationInput ? parseFloat(durationInput.value) : 24;
        if (isNaN(durationHours) || durationHours <= 0) { return null; }
        const durationInDays = durationHours / 24;
        const newProfitElement = document.getElementById('noRngProfitPreview');
        if (newProfitElement) {
            const profitString = newProfitElement.textContent.trim();
            const totalProfit = normalizeAndParseFloat(profitString);
            if (!isNaN(totalProfit)) {
                return totalProfit / durationInDays;
            }
        }
        const profitElement = document.querySelector('div[i18n-id="i18n-realDailyProfitTitle"]');
        if (!profitElement) { return null; }
        let profitString = '';
        if (profitElement.hasAttribute('i18n-data')) {
            profitString = profitElement.getAttribute('i18n-data');
        } else {
            profitString = profitElement.textContent;
        }
        profitString = (profitString.split(':')[1] || '').trim();
        if (profitString === '') { return null; }
        const profitValue = normalizeAndParseFloat(profitString);
        return isNaN(profitValue) ? null : profitValue;
    }
    function getExpValue() {
        const allTotalLabels = Array.from(document.querySelectorAll('div')).filter(el => el.textContent.trim() === 'Total');
        const totalLabelElement = allTotalLabels.find(el => el.parentElement.classList.contains('row') && el.nextElementSibling);
        if (!totalLabelElement) {
            return null;
        }
        const expElement = totalLabelElement.nextElementSibling;
        if (expElement) {
            const expString = expElement.textContent.trim().replace(/\D/g, '');
            const expValue = parseInt(expString, 10);
            return isNaN(expValue) ? null : expValue;
        }
        return null;
    }
    function getEphValue() { const ephLabel = document.querySelector('div[data-i18n="common:simulationResults.encounters"]'); if (!ephLabel) { return null; } const ephElement = ephLabel.nextElementSibling; if (ephElement) { const ephString = ephElement.textContent.trim(); const ephValue = normalizeAndParseFloat(ephString); return isNaN(ephValue) ? null : ephValue; } return null; }
    function getDphValue() {
        const dphHeader = Array.from(document.querySelectorAll('div')).find(el => el.textContent.trim() === 'Deaths Per Hour');
        if (!dphHeader) { return null; }
        const dphContainer = dphHeader.nextElementSibling;
        if (!dphContainer) return null;
        const valueElement = dphContainer.querySelector('.row > :last-child');
        if (valueElement) {
            const dphString = valueElement.textContent.trim();
            const dphValue = normalizeAndParseFloat(dphString);
            return isNaN(dphValue) ? null : dphValue;
        }
        return null;
    }
    function captureBaselineSkillRates() {
        console.log("JIGS DEBUG: Capturing baseline skill XP rates...");
        baselineSkillXpRates = {}; // Clear previous rates
        const container = document.getElementById('simulationResultExperienceGain');
        if (!container) {
            console.error("JIGS DEBUG: Could not find skill XP results container '#simulationResultExperienceGain'.");
            return;
        }

        const skillRows = container.querySelectorAll('.row');
        skillRows.forEach(row => {
            const children = row.children;
            if (children.length === 2) {
                const skillName = children[0].textContent.trim().toLowerCase();
                const xpValue = children[1].textContent.trim();
                // Exclude the "Total" row
                if (skillName !== 'total') {
                    const rate = parseInt(xpValue.replace(/,/g, ''), 10) || 0;
                    baselineSkillXpRates[skillName] = rate;
                }
            }
        });
        console.log("JIGS DEBUG: Captured rates:", baselineSkillXpRates);
    }
    function getSkillXpRate(skillName) {
        const rate = baselineSkillXpRates[skillName.toLowerCase()] || 0;
        if (rate === 0) {
            console.warn(`JIGS DEBUG: No baseline XP rate found for skill "${skillName}". Time-to-level will not be calculated.`);
        }
        return rate;
    }
    function resetInputsToBaseline() {
        document.querySelectorAll('#batch-inputs-container input, #batch-inputs-container select:not(#jigs-player-select)').forEach(el => {
            if (el.dataset.originalValue !== undefined) {
                el.value = el.dataset.originalValue;
                el.dispatchEvent(new Event('change', { bubbles: true }));
            }
        });
        setTimeout(() => {
            document.querySelectorAll('.batch-input-row-equip').forEach(row => {
                updatePriceOverrideField(row);
            });
        }, 100);
        statusDiv.textContent = 'Status: All inputs reset to baseline.';
    }
    function updateQueuePanelUI() {
        const queueList = document.getElementById('jigs-queue-list');
        queueList.innerHTML = '';
        simulationQueue.forEach((item, index) => {
            const li = document.createElement('li');

            const labelSpan = document.createElement('span');
            labelSpan.textContent = item.label;
            labelSpan.className = 'jigs-queue-item-label';

            const removeButton = document.createElement('button');
            removeButton.textContent = '✖';
            removeButton.className = 'jigs-remove-queue-item-button';
            removeButton.title = 'Remove this item from the queue';
            removeButton.dataset.index = index; // Store the item's index on the button

            li.appendChild(labelSpan);
            li.appendChild(removeButton);
            queueList.appendChild(li);
        });
        updateQueueEstimate();
    }
    function updateQueueEstimate() {
        const estimateDiv = document.getElementById('jigs-queue-estimate');
        if (baselineRunTime <= 0 || simulationQueue.length === 0) {
            estimateDiv.textContent = '';
            return;
        }
        const multiplier = parseInt(document.querySelector('#sim-settings-group [data-name="Multiplier"]')?.value) || 1;
        const totalMs = simulationQueue.length * multiplier * baselineRunTime;
        const totalSeconds = totalMs / 1000;
        if (totalSeconds < 60) {
            estimateDiv.textContent = `Estimated time: ~${Math.round(totalSeconds)} seconds`;
        } else {
            const totalMinutes = totalSeconds / 60;
            estimateDiv.textContent = `Estimated time: ~${totalMinutes.toFixed(1)} minutes`;
        }
    }
    function updateColumnVisibility() { document.querySelectorAll('.column-toggle').forEach(checkbox => { const columnClass = checkbox.dataset.col; const isVisible = checkbox.checked; document.querySelectorAll(`.${columnClass}`).forEach(el => el.style.display = isVisible ? '' : 'none'); }); }
    function formatTime(days) { if (!isFinite(days) || days === Infinity) { return 'Never'; } if (days <= 0) { return 'Free'; } const hours = days * 24; if (hours < 1) { const minutes = hours * 60; return `${minutes.toFixed(0)} min`; } if (days < 1) { return `${hours.toFixed(1)} hrs`; } const months = days / 30.44; if (months >= 1) { return `${months.toFixed(1)} mon`; } return `${days.toFixed(1)} days`; }
    function switchPlayerAndCapture(playerName) { const playerTabs = Array.from(document.querySelectorAll('a[id^="player"][id$="-tab"]')); const targetTab = playerTabs.find(tab => tab.textContent.trim() === playerName); if (targetTab) { statusDiv.textContent = `Switching to ${playerName}...`; targetTab.click(); setTimeout(() => { buildInputsUI(); }, 500); } else { statusDiv.textContent = `Error: Could not find player ${playerName}.`; } }
    function populatePlayerDropdown() {
        const container = document.getElementById('jigs-player-select-container');
        if (!container) return;
        container.innerHTML = '';
        const playerTabs = Array.from(document.querySelectorAll('a[id^="player"][id$="-tab"]'));
        if (playerTabs.length > 1) {
            const playerNames = playerTabs.map(tab => tab.textContent.trim());
            const activeTab = playerTabs.find(tab => tab.classList.contains('active'));
            const currentPlayer = activeTab ? activeTab.textContent.trim() : playerNames[0];
            const playerSelectRow = createSelect('Select Player', currentPlayer, playerNames, false);
            const selectEl = playerSelectRow.querySelector('select');
            if (selectEl) selectEl.id = 'jigs-player-select';
            container.append(...playerSelectRow.childNodes);
            container.className = 'batch-input-row';
            container.style.gridTemplateColumns = '100px 1fr';
        } else {
            container.className = '';
        }
    }
    function highlightResults() {
        const rows = document.querySelectorAll('#batch-results-table tbody tr');
        if (rows.length < 2) { return; }
        document.querySelectorAll('#batch-results-table td').forEach(td => {
            td.classList.remove('best-upgrade', 'worst-upgrade');
        });
        const metrics = [
            { key: 'costPerDps', colIndex: 5 },
            { key: 'costPerProfit', colIndex: 8 },
            { key: 'costPerExp', colIndex: 11 },
            { key: 'costPerEph', colIndex: 14 }
        ];
        for (const metric of metrics) {
            let values = [];
            for (const row of rows) {
                const cost = parseFloat(row.dataset.cost);
                const metricValue = parseFloat(row.dataset[metric.key]);

                if (cost === 0 || !isFinite(metricValue)) {
                    continue;
                }
                values.push({ value: metricValue, row: row });
            }
            if (values.length < 2) { continue; }
            values.sort((a, b) => a.value - b.value);
            const minVal = values[0].value;
            const maxVal = values[values.length - 1].value;
            if (minVal === maxVal) { continue; }
            const minRow = values[0].row;
            const maxRow = values[values.length - 1].row;
            const minCell = minRow.children[metric.colIndex];
            const maxCell = maxRow.children[metric.colIndex];
            if (minCell) minCell.classList.add('best-upgrade');
            if (maxCell) maxCell.classList.add('worst-upgrade');
        }
    }
    function updateMarketIndicators(equipmentRow) {
        if (!marketData) return;
        const itemSelect = equipmentRow.querySelector('select');
        const enhInput = equipmentRow.querySelector('input[type="number"]');
        const indicatorContainer = equipmentRow.querySelector('.market-indicators');
        if (!itemSelect || !enhInput || !indicatorContainer) return;
        indicatorContainer.innerHTML = '';
        const simItemName = itemSelect.value;
        if (simItemName === 'Empty') return;
        const marketItemName = SIMULATOR_TO_MARKET_MAP[simItemName] || simItemName;
        const itemKeyBase = marketItemName.replace(/'/g, '').toLowerCase();
        const currentEnh = parseInt(enhInput.value, 10);
        for (let i = 1; i <= 20; i++) {
            const marketKey = `${itemKeyBase} +${i}`;
            const priceData = marketData[marketKey];
            if (priceData && priceData.seller && priceData.seller !== -1) {
                const dot = document.createElement('div');
                dot.className = 'market-dot';
                dot.textContent = `+${i}`;
                dot.title = `+${i}: ${formatGold(priceData.seller)}`;
                if (i === currentEnh) {
                    dot.classList.add('selected');
                }
                dot.addEventListener('click', () => {
                    enhInput.value = i;
                    enhInput.dispatchEvent(new Event('change', { bubbles: true }));
                });
                indicatorContainer.appendChild(dot);
            }
        }
    }
    function updatePriceOverrideField(equipmentRow) {
        if (!marketData) return;
        const itemSelect = equipmentRow.querySelector('select');
        const enhInput = equipmentRow.querySelector('input[type="number"]');
        const priceInput = equipmentRow.querySelector('.jigs-price-override');
        if (!itemSelect || !enhInput || !priceInput) return;
        const simItemName = itemSelect.value;
        const enhLevel = enhInput.value;
        priceInput.value = '';
        if (simItemName === 'Empty' || enhLevel === '0') return;
        const marketItemName = SIMULATOR_TO_MARKET_MAP[simItemName] || simItemName;
        const itemKeyBase = marketItemName.replace(/'/g, '').toLowerCase();
        const marketKey = enhLevel === '0' ? itemKeyBase : `${itemKeyBase} +${enhLevel}`;
        const priceData = marketData[marketKey];
        if (priceData && priceData.seller && priceData.seller !== -1) {
            priceInput.value = formatGold(priceData.seller);
        }
    }

    // --- 4. CORE LOGIC ---
    async function fetchJigsData() { return new Promise((resolve) => { GM_xmlhttpRequest({ method: "GET", url: JIGS_DATA_URL, onload: function(response) { if (response.status === 200) { try { const data = JSON.parse(response.responseText); HOUSE_RECIPES = data.recipes; ITEM_ID_TO_NAME_MAP = data.itemMap; SPELL_BOOK_XP = data.spellBookXp; SIMULATOR_TO_MARKET_MAP = data.refinedMap; ABILITY_XP_LEVELS = data.abilityXp; resolve(true); } catch(e) { console.error("JIGS: Failed to parse JIGS data.", e); resolve(false); } } else { console.error("JIGS: Failed to fetch JIGS data.", response.status); resolve(false); } }, onerror: function() { console.error("JIGS: Error fetching JIGS data."); resolve(false); } }); }); }
    async function fetchMarketData() { if (marketData) { return marketData; } return new Promise((resolve) => { GM_xmlhttpRequest({ method: "GET", url: MARKET_API_URL, onload: function(response) { if (response.status === 200) { try { const responseObject = JSON.parse(response.responseText); const rawMarketData = responseObject.marketData; if (typeof rawMarketData !== 'object' || rawMarketData === null) { resolve(null); return; } marketData = {}; for (const itemId in rawMarketData) { const itemName = ITEM_ID_TO_NAME_MAP[itemId]; if (!itemName) continue; const itemEnhancements = rawMarketData[itemId]; for (const enhancementLevel in itemEnhancements) { const prices = itemEnhancements[enhancementLevel]; const fullName = enhancementLevel === "0" ? itemName : `${itemName} +${enhancementLevel}`; marketData[fullName.replace(/'/g, '').toLowerCase()] = { buyer: prices.b, seller: prices.a }; } } resolve(marketData); } catch (e) { resolve(null); } } else { resolve(null); } }, onerror: function() { resolve(null); } }); }); }
    async function runSimulation(progressCallback) { return new Promise((resolve) => { let progressWatcher, dpsWatcher; const cleanup = () => { clearInterval(progressWatcher); clearInterval(dpsWatcher); }; const setupButton = document.getElementById('buttonSimulationSetup'); if (!setupButton) { cleanup(); resolve(null); return; } setupButton.click(); setTimeout(() => { const startButton = document.getElementById('buttonStartSimulation'); if (!startButton) { cleanup(); resolve(null); return; } const resultsContainer = document.getElementById('simulationResultTotalDamageDone'); if (resultsContainer) resultsContainer.innerHTML = ''; startButton.click(); progressWatcher = setInterval(() => { if (!isBatchRunning) { console.log("JIGS DEBUG: Simulation stopped by user request."); cleanup(); resolve(null); return; } const progressBar = document.getElementById('simulationProgressBar'); if (progressBar) { const progress = parseInt(progressBar.textContent) || 0; if (progressCallback) progressCallback(progress); } if (progressBar && progressBar.textContent.includes('100%')) { clearInterval(progressWatcher); dpsWatcher = setInterval(() => { const dpsVal = getDpsValue(); if (dpsVal) { cleanup(); resolve({ dps: normalizeAndParseFloat(dpsVal), profit: getProfitValue() || 0, exp: getExpValue() || 0, eph: getEphValue() || 0, dph: getDphValue() || 0, }); } }, 100); } }, 200); }, 300); }); }
    async function runSimulationMultiple(multiplier, progressCallback) {
        let totals = { dps: 0, profit: 0, exp: 0, eph: 0, dph: 0 };
        let successfulRuns = 0;
        let allDpsResults = [];
        for (let i = 0; i < multiplier; i++) {
            if (!isBatchRunning) break;
            const singleRunProgress = (progress) => {
                if (progressCallback) {
                    const overallProgress = ((i * 100) + progress) / multiplier;
                    progressCallback(overallProgress);
                }
            };
            const result = await runSimulation(singleRunProgress);
            if (result && !isNaN(result.dps)) {
                totals.dps += result.dps;
                totals.profit += result.profit;
                totals.exp += result.exp;
                totals.eph += result.eph;
                totals.dph += result.dph;
                successfulRuns++;
                allDpsResults.push(result.dps);
            } else {
                console.error(`JIGS: Simulation run ${i + 1} of ${multiplier} failed.`);
            }
        }

        if (successfulRuns === 0) {
            return { averageDps: NaN, averageProfit: NaN, averageExp: NaN, averageEph: NaN, averageDph: NaN, individualRuns: [] };
        }
        return {
            averageDps: totals.dps / successfulRuns,
            averageProfit: totals.profit / successfulRuns,
            averageExp: totals.exp / successfulRuns,
            averageEph: totals.eph / successfulRuns,
            averageDph: totals.dph / successfulRuns,
            individualRuns: allDpsResults
        };
    }
    function setRunningState(isRunning) {
        isBatchRunning = isRunning;
        document.getElementById('run-batch-button').style.display = isRunning ? 'none' : 'block';
        document.getElementById('stop-batch-button').style.display = isRunning ? 'block' : 'none';
        document.getElementById('capture-setup-button').disabled = isRunning;
        document.getElementById('update-baseline-button').disabled = isRunning;
        document.getElementById('import-triggers-checkbox').disabled = isRunning;
        if (isRunning) {
            document.getElementById('export-csv-button').disabled = true;
        }
        document.getElementById('reset-button').disabled = isRunning;
        document.getElementById('add-to-queue-button').disabled = isRunning;
    }
    async function buildInputsUI() {
        statusDiv.textContent = 'Status: Reading data and fetching market prices...';
        jigsNameToPageElementMap = new Map();
        await fetchMarketData();
        document.getElementById('run-batch-button').disabled = true;
        document.getElementById('capture-setup-button').disabled = true;
        document.getElementById('update-baseline-button').disabled = true;
        document.getElementById('reset-button').disabled = true;
        document.getElementById('add-to-queue-button').disabled = true;
        document.getElementById('import-triggers-checkbox').disabled = true;

        const excludedRooms = [ "Shed", "Dairy Barn", "Garden", "Forge", "Workshop", "Sewing Parlor", "Kitchen", "Brewery", "Laboratory", "Observatory", "Log Shed" ];
        const currentMultiplier = document.querySelector('#sim-settings-group [data-name="Multiplier"]')?.value || 1;
        Object.values(groupContainers).forEach(c => { if (c.id !== 'house-grid-container') c.innerHTML = `<summary>${c.querySelector('summary').textContent}</summary>`; else c.innerHTML = ''; });
        let itemsFound = 0;
        houseKeywords = [];
        populatePlayerDropdown();
        skillKeywords.forEach(name => { const pageEl = findPageElementByName(name); if (pageEl) { groupContainers.skills.appendChild(createNumberInput(name, pageEl.value, pageEl.min, pageEl.max)); jigsNameToPageElementMap.set(name, pageEl); itemsFound++; } });
        equipmentKeywords.forEach(name => { const itemSelect = findPageElementByName(name, 'select'); const enhInput = findPageElementByName(name, 'input'); if (itemSelect && enhInput) { const itemValue = itemSelect.options[itemSelect.selectedIndex].text; const itemOptions = Array.from(itemSelect.options).map(opt => opt.text); const enhValue = enhInput.value; const equipmentRow = createEquipmentRow(name, itemValue, itemOptions, enhValue); groupContainers.equipment.appendChild(equipmentRow); updateMarketIndicators(equipmentRow); updatePriceOverrideField(equipmentRow); jigsNameToPageElementMap.set(name, itemSelect); jigsNameToPageElementMap.set(`${name} Enhancement`, enhInput); itemsFound++; } });
        for (let i = 0; i < 5; i++) { const abilitySelect = document.getElementById(`selectAbility_${i}`); const levelInput = document.getElementById(`inputAbilityLevel_${i}`); if (abilitySelect && levelInput) { const name = `Ability ${i + 1}`; const itemValue = abilitySelect.options[abilitySelect.selectedIndex].text; const itemOptions = Array.from(abilitySelect.options).map(opt => opt.text); const lvlValue = levelInput.value; groupContainers.abilities.appendChild(createAbilityRow(name, itemValue, itemOptions, lvlValue)); groupContainers.abilities.appendChild(createTriggerRow('ability', i)); jigsNameToPageElementMap.set(name, abilitySelect); jigsNameToPageElementMap.set(`${name} Level`, levelInput); itemsFound++; } }
        document.querySelectorAll('select[id^="selectFood_"], select[id^="selectDrink_"]').forEach(el => {
            const isFood = el.id.includes('Food');
            const type = isFood ? 'food' : 'drink';
            const indexFromId = parseInt(el.id.split('_')[1], 10);
            const name = `${type} ${indexFromId + 1}`;
            const currentValue = el.options[el.selectedIndex].text;
            const options = Array.from(el.options).map(opt => opt.text);
            groupContainers.food.appendChild(createSelect(name, currentValue, options));
            groupContainers.food.appendChild(createTriggerRow(type, indexFromId));
            jigsNameToPageElementMap.set(name, el);
            itemsFound++;
        });
        document.querySelectorAll('#houseRoomsList .row').forEach(row => {
            const labelEl = row.querySelector('div[data-i18n]');
            const inputEl = row.querySelector('input');
            if (labelEl && inputEl) {
                const name = labelEl.textContent.trim();
                if (excludedRooms.includes(name)) return;
                houseKeywords.push(name);
                groupContainers.house.appendChild(createNumberInput(name, inputEl.value, inputEl.min, inputEl.max, true));
                jigsNameToPageElementMap.set(name, inputEl);
                itemsFound++;
            }
        });
        for (const name of Object.keys(specialIdMap)) {
            const pageEl = findPageElementByName(name);
            if (pageEl) {
                if (pageEl.tagName === 'SELECT') {
                    const currentValue = pageEl.options[pageEl.selectedIndex].text;
                    const options = Array.from(pageEl.options).map(opt => opt.text);
                    const selectEl = createSelect(name, currentValue, options, false);
                    groupContainers.sim.appendChild(selectEl);
                } else {
                    const numInput = createNumberInput(name, pageEl.value, pageEl.min, pageEl.max, false, false);
                    groupContainers.sim.appendChild(numInput);
                }
                jigsNameToPageElementMap.set(name, pageEl);
                itemsFound++;
            }
        }
        const multInput = createNumberInput('Multiplier', currentMultiplier, 1, 100, false, false);
        groupContainers.sim.appendChild(multInput);

        if (itemsFound > 0) {
            statusDiv.textContent = 'Status: Idle.';
            document.getElementById('run-batch-button').disabled = false;
            document.getElementById('update-baseline-button').disabled = false;
            document.getElementById('reset-button').disabled = false;
            document.getElementById('add-to-queue-button').disabled = false;
            document.getElementById('import-triggers-checkbox').disabled = false;
        } else {
            statusDiv.textContent = 'Status: No data found. Import or use Capture Setup.';
        }
        document.getElementById('capture-setup-button').disabled = false;
    }
    async function updateBaseline(withTriggers = true) {
        const getPricesButton = document.getElementById('buttonGetPrices');
        if (getPricesButton) {
            console.log("JIGS DEBUG: Clicking 'Get Prices' button as a backup.");
            getPricesButton.click();
        }
        console.log("JIGS DEBUG: updateBaseline started.");
        setRunningState(true);
        const jigsProgressContainer = document.getElementById('jigs-progress-container');
        const jigsProgressBar = document.getElementById('jigs-progress-bar');

        if (withTriggers) {
            statusDiv.textContent = 'Status: Importing triggers for baseline...';
            await importTriggers(true);
        }

        statusDiv.textContent = 'Status: Applying settings and updating baseline...';
        jigsProgressContainer.style.display = 'block';
        jigsProgressBar.style.width = '0%';
        try {
            const simSettings = document.querySelectorAll('#sim-settings-group select, #sim-settings-group input');
            console.log("JIGS DEBUG: Applying sim setting changes before baseline run.");
            simSettings.forEach(uiEl => {
                if (!uiEl.dataset.name) return;
                if (uiEl.value !== uiEl.dataset.originalValue) {
                    const pageEl = jigsNameToPageElementMap.get(uiEl.dataset.name);
                    if (pageEl) {
                        console.log(`JIGS DEBUG: Changing '${uiEl.dataset.name}' on page to '${uiEl.value}'`);
                        if (pageEl.tagName === 'SELECT') {
                            const opt = Array.from(pageEl.options).find(o => o.text === uiEl.value);
                            if (opt) pageEl.value = opt.value;
                        } else {
                            pageEl.value = uiEl.value;
                        }
                        pageEl.dispatchEvent(new Event('change', { bubbles: true }));
                        pageEl.dispatchEvent(new Event('input', { bubbles: true }));
                    }
                }
            });
            const multiplier = parseInt(document.querySelector('#sim-settings-group [data-name="Multiplier"]').value) || 1;
            console.log(`JIGS DEBUG: Running baseline simulation with multiplier: ${multiplier}`);

            const startTime = performance.now();
            const simResult = await runSimulationMultiple(multiplier, progress => { jigsProgressBar.style.width = `${progress}%`; });
            const endTime = performance.now();
            if (multiplier > 0) {
                baselineRunTime = (endTime - startTime) / multiplier;
            }

            console.log(`JIGS DEBUG: Baseline sim complete. Result:`, simResult);

            if (!isNaN(simResult.averageDps)) {
                baselineDps = simResult.averageDps;
                baselineProfit = simResult.averageProfit;
                baselineExp = simResult.averageExp;
                baselineEph = simResult.averageEph;
                baselineDph = simResult.averageDph;
                console.log(`JIGS DEBUG: New baselines set -> DPS: ${baselineDps}, Profit: ${baselineProfit}, Exp: ${baselineExp}, EPH: ${baselineEph}, DPH: ${baselineDph}`);
                document.getElementById('baseline-dps-input').value = baselineDps.toFixed(2);
                document.getElementById('baseline-profit-input').value = formatGold(baselineProfit);
                document.getElementById('baseline-exp-input').value = baselineExp.toLocaleString();
                document.getElementById('baseline-eph-input').value = baselineEph.toFixed(2);
                document.getElementById('baseline-dph-input').value = baselineDph.toFixed(2);

                captureBaselineSkillRates();
                updateQueueEstimate();

                document.querySelectorAll('#batch-inputs-container input, #batch-inputs-container select').forEach(el => {
                    if (el.dataset.originalValue !== undefined) {
                        el.dataset.originalValue = el.value;
                        el.classList.remove('jigs-modified');
                    }
                });
                statusDiv.textContent = 'Status: Baseline updated.';
            } else if (isBatchRunning) {
                statusDiv.textContent = 'Error: Failed to update baseline. Try again.';
                console.error("JIGS DEBUG: Failed to update baseline; simulation returned NaN.");
            }
        } finally {
            setRunningState(false);
        }
    }

    async function applyTriggerChanges(triggerData) {
        const type = triggerData.type;
        const index = triggerData.index;
        const typeCap = type.charAt(0).toUpperCase() + type.slice(1);
        const buttonId = `button${typeCap}Trigger_${index}`;
        const triggerButton = document.getElementById(buttonId);

        if (!triggerButton) {
            console.error(`JIGS: Could not find trigger button ${buttonId} to apply changes.`);
            return;
        }

        triggerButton.click();
        await new Promise(r => setTimeout(r, 100));

        const modal = document.getElementById('triggerModal');
        if (!modal || !modal.classList.contains('show')) {
            const closeBtn = document.querySelector('button.btn-close[data-bs-dismiss="modal"]');
            if(closeBtn) closeBtn.click();
            throw new Error(`Modal for ${buttonId} did not open for applying trigger changes.`);
        }

        const setPageSelect = (selectElement, textToFind) => {
            const option = Array.from(selectElement.options).find(o => o.text === textToFind);
            if (option) {
                selectElement.value = option.value;
            } else if (textToFind === "") {
                selectElement.value = "";
            }
            selectElement.dispatchEvent(new Event('change', { bubbles: true }));
        };

        setPageSelect(document.getElementById('selectTriggerDependency_0'), triggerData.dependency);
        setPageSelect(document.getElementById('selectTriggerCondition_0'), triggerData.condition);
        setPageSelect(document.getElementById('selectTriggerComparator_0'), triggerData.comparator);

        const valInput = document.getElementById('inputTriggerValue_0');
        valInput.value = triggerData.value;
        valInput.dispatchEvent(new Event('input', { bubbles: true }));
        valInput.dispatchEvent(new Event('change', { bubbles: true }));

        document.getElementById('buttonTriggerModalSave').click();
        await new Promise(r => setTimeout(r, 100));
    }

    function addChangesToQueue() {
        let allChanges = [];
        document.querySelectorAll("#batch-inputs-container input:not([type=checkbox]), #batch-inputs-container select").forEach(el => {
            const triggerContainer = el.closest('.trigger-container');
            if (el.id !== 'jigs-player-select' && !el.classList.contains('jigs-price-override') && !triggerContainer && !el.disabled && el.dataset.name && el.value !== el.dataset.originalValue) {
                const row = el.closest('.batch-input-row, .batch-input-row-equip, .batch-input-row-ability, .house-grid-item');
                const isConstant = row.querySelector('.jigs-constant-checkbox')?.checked || false;
                const change = {
                    element: el,
                    name: el.dataset.name,
                    value: el.value,
                    originalValue: el.dataset.originalValue,
                    isConstant: isConstant
                };
                if (row.classList.contains('batch-input-row-equip')) {
                    const priceInput = row.querySelector('.jigs-price-override');
                    if (priceInput && priceInput.value.trim() !== '') {
                        change.priceOverride = priceInput.value.trim();
                    }
                }
                allChanges.push(change);
            }
        });

        let allTriggerChanges = [];
        document.querySelectorAll('.trigger-container').forEach(container => {
            const mainRow = container.querySelector('.trigger-row');
            const rangeRow = container.querySelector('.trigger-range-row');
            const depEl = mainRow.querySelector('.jigs-trigger-dependency');
            const condEl = mainRow.querySelector('.jigs-trigger-condition');
            const compEl = mainRow.querySelector('.jigs-trigger-comparator');
            const valEl = mainRow.querySelector('.jigs-trigger-value');
            const rangeEl = rangeRow.querySelector('.jigs-trigger-range');
            const incEl = rangeRow.querySelector('.jigs-trigger-increment');
            const hasChanged = depEl.value !== depEl.dataset.originalValue ||
                                 condEl.value !== condEl.dataset.originalValue ||
                                 compEl.value !== compEl.dataset.originalValue ||
                                 valEl.value !== valEl.dataset.originalValue ||
                                 rangeEl.value !== rangeEl.dataset.originalValue ||
                                 incEl.value !== incEl.dataset.originalValue;
            if (hasChanged) {
                allTriggerChanges.push({
                    type: mainRow.dataset.triggerType,
                    index: mainRow.dataset.triggerIndex,
                    isConstant: false,
                    data: { dependency: depEl.value, condition: condEl.value, comparator: compEl.value, value: valEl.value },
                    range: rangeEl.value.trim(),
                    increment: incEl.value.trim()
                });
            }
        });

        const allGroupedChanges = groupAllChanges(allChanges, allTriggerChanges);
        if (allGroupedChanges.length === 0) {
            statusDiv.textContent = 'Status: No changes detected to add to queue.';
            return;
        }

        const generateLabel = (upgrades) => {
            let labelParts = [];
            for (const upgrade of upgrades) {
                let partLabel = upgrade.customLabel || '';
                if (!partLabel) {
                    if (!upgrade.isTriggerOnly) {
                        const baseName = upgrade.name;
                        const isConsumableOrAbility = baseName.startsWith('Ability') || baseName.startsWith('Food') || baseName.startsWith('Drink');
                        const itemChanged = upgrade.value && upgrade.originalValue && upgrade.value !== upgrade.originalValue;
                        const enhChanged = upgrade.enhancement;
                        const levelChanged = upgrade.level;

                        if (itemChanged) {
                            partLabel = isConsumableOrAbility ? `${upgrade.originalValue} -> ${upgrade.value}` : `${baseName}: ${upgrade.originalValue} -> ${upgrade.value}`;
                        }

                        if (enhChanged) {
                            const staticItemName = upgrade.value || document.querySelector(`#batch-inputs-container [data-name="${baseName}"]`)?.dataset.originalValue;
                            let enhLabel = `Enh ${upgrade.enhancement.originalValue} -> ${upgrade.enhancement.value}`;
                            partLabel = itemChanged ? `${partLabel} & ${enhLabel}` : `${staticItemName}: ${enhLabel}`;
                        } else if (levelChanged) {
                            const staticItemName = upgrade.value || document.querySelector(`#batch-inputs-container [data-name="${baseName}"]`)?.dataset.originalValue;
                            let levelLabel = `Lvl ${upgrade.level.originalValue} -> ${upgrade.level.value}`;
                            partLabel = itemChanged ? `${partLabel} & ${levelLabel}` : `${staticItemName}: ${levelLabel}`;
                        }
                    }
                    if (upgrade.triggerChange) {
                        let triggerLabel = 'Trigger Change';
                         if (upgrade.triggerChange.data && upgrade.triggerChange.data.value) {
                            triggerLabel += ` (Val: ${upgrade.triggerChange.data.value})`;
                        }
                        if (!upgrade.isTriggerOnly) {
                            const triggerBaseName = upgrade.value || document.querySelector(`#batch-inputs-container [data-name="${upgrade.name}"]`)?.dataset.originalValue;
                            triggerLabel = `${triggerBaseName} ${triggerLabel}`;
                        } else {
                            const tc = upgrade.triggerChange;
                            const jigsName = `${tc.type.charAt(0).toUpperCase() + tc.type.slice(1)} ${parseInt(tc.index) + 1}`;
                            const associatedSelect = document.querySelector(`#batch-inputs-container [data-name="${jigsName}"]`);
                            if (associatedSelect) {
                                triggerLabel = `${associatedSelect.value} ${triggerLabel}`;
                            }
                        }
                        partLabel = partLabel ? `${partLabel} & ${triggerLabel}` : triggerLabel;
                    }
                }
                if(partLabel) labelParts.push(partLabel);
            }
            return labelParts.filter(p => p).join(' & ');
        };

        const constantUpgrades = allGroupedChanges.filter(c => c.isConstant);
        const individualUpgrades = allGroupedChanges.filter(c => !c.isConstant);
        let itemsAdded = 0;

        if (individualUpgrades.length === 0 && constantUpgrades.length > 0) {
            const queueItem = { upgrades: constantUpgrades, label: generateLabel(constantUpgrades) || 'Constants Only' };
            simulationQueue.push(queueItem);
            itemsAdded = 1;
        } else {
             for (const individual of individualUpgrades) {
                const tc = individual.triggerChange;
                const hasRange = tc && tc.range && tc.increment;

                if (hasRange) {
                    const [startStr, endStr] = tc.range.split('-').map(s => s.trim());
                    const start = parseInt(startStr);
                    const end = parseInt(endStr);
                    const increment = parseInt(tc.increment);

                    if (!isNaN(start) && !isNaN(end) && !isNaN(increment) && increment > 0 && end >= start) {
                        for (let value = start; value <= end; value += increment) {
                            const simUpgrade = JSON.parse(JSON.stringify(individual));
                            simUpgrade.triggerChange.data.value = value;

                            const upgrades = [...constantUpgrades, simUpgrade];
                            simulationQueue.push({ upgrades, label: generateLabel(upgrades) });
                            itemsAdded++;
                        }
                    } else {
                        const upgrades = [...constantUpgrades, individual];
                        simulationQueue.push({ upgrades, label: generateLabel(upgrades) });
                        itemsAdded++;
                    }
                } else {
                    const upgrades = [...constantUpgrades, individual];
                    simulationQueue.push({ upgrades, label: generateLabel(upgrades) });
                    itemsAdded++;
                }
            }
        }

        statusDiv.textContent = `Status: Added ${itemsAdded} simulation(s) to queue.`;
        updateQueuePanelUI();
        resetInputsToBaseline();
    }

    async function startBatch() {
        if (simulationQueue.length === 0) {
            statusDiv.textContent = 'Status: Queue is empty. Add simulations first.';
            return;
        }
        const getPricesButton = document.getElementById('buttonGetPrices');
        if (getPricesButton) {
            console.log("JIGS DEBUG: Clicking 'Get Prices' button as a backup.");
            getPricesButton.click();
        }
        console.log("JIGS DEBUG: startBatch started.");
        updateBaselinesFromInputs();
        let lastModifiedTriggers = [];
        setRunningState(true);

        try {
            await fetchMarketData();
            if (!isBatchRunning) { statusDiv.textContent = 'Status: Stopped by user.'; return; }
            if (!marketData) { console.error("JIGS DEBUG: Market data not available."); setRunningState(false); return; }
            if (baselineDps === 0) { statusDiv.textContent = 'Error: Please set a baseline first.'; console.warn("JIGS DEBUG: baselineDps is 0."); setRunningState(false); return; }

            const jigsProgressContainer = document.getElementById('jigs-progress-container');
            const jigsProgressBar = document.getElementById('jigs-progress-bar');
            jigsProgressContainer.style.display = 'block';
            jigsProgressBar.style.width = '0%';

            const simulationsToRun = [...simulationQueue];
            console.log(`JIGS DEBUG: Found ${simulationsToRun.length} simulations to run from queue.`);

            const multiplier = parseInt(document.querySelector('#sim-settings-group [data-name="Multiplier"]').value) || 1;
            let simsCompleted = 0;

            for (const simulation of simulationsToRun) {
                if (!isBatchRunning) break;

                jigsNameToPageElementMap.forEach((pageEl, name) => {
                    const jigsEl = document.querySelector(`#batch-inputs-container [data-name="${name}"]`);
                    if(jigsEl && pageEl) {
                        const originalValue = jigsEl.dataset.originalValue;
                          if (pageEl.tagName === 'SELECT') {
                              const opt = Array.from(pageEl.options).find(o => o.text === originalValue);
                              if (opt) pageEl.value = opt.value;
                          } else {
                              pageEl.value = originalValue;
                          }
                        pageEl.dispatchEvent(new Event('change', { bubbles: true }));
                    }
                });

                for (const triggerToReset of lastModifiedTriggers) {
                    const type = triggerToReset.type;
                    const index = triggerToReset.index;
                    const triggerRow = document.querySelector(`.trigger-row[data-trigger-type="${type}"][data-trigger-index="${index}"]`);
                    if (triggerRow) {
                        const originalState = {
                            type: type, index: index,
                            dependency: triggerRow.querySelector('.jigs-trigger-dependency').dataset.originalValue,
                            condition: triggerRow.querySelector('.jigs-trigger-condition').dataset.originalValue,
                            comparator: triggerRow.querySelector('.jigs-trigger-comparator').dataset.originalValue,
                            value: triggerRow.querySelector('.jigs-trigger-value').dataset.originalValue
                        };
                        await applyTriggerChanges(originalState);
                    }
                }
                lastModifiedTriggers = [];
                await new Promise(r => setTimeout(r, 50));

                let totalCost = 0;
                let totalBooks = 0;
                let timeToLevelText = '';

                for (const upgrade of simulation.upgrades) {
                    if (!upgrade.isTriggerOnly) {
                        const applyChange = (name, newValue) => {
                            const el = jigsNameToPageElementMap.get(name);
                            if (!el) { console.warn(`JIGS: Could not find page element for "${name}" to apply change.`); return; }
                            if (el.tagName === 'SELECT') {
                                const opt = Array.from(el.options).find(o => o.text === newValue);
                                if (opt) el.value = opt.value;
                            } else {
                                el.value = newValue;
                            }
                            el.dispatchEvent(new Event('change', { bubbles: true }));
                            el.dispatchEvent(new Event('input', { bubbles: true }));
                        };

                        if (upgrade.value) { applyChange(upgrade.name, upgrade.value); }
                        if (upgrade.enhancement) { applyChange(upgrade.enhancement.name, upgrade.enhancement.value); }
                        if (upgrade.level) { applyChange(upgrade.level.name, upgrade.level.value); }
                    }
                    if (upgrade.triggerChange) {
                        const tc = upgrade.triggerChange;
                        await applyTriggerChanges({ ...tc.data, type: tc.type, index: tc.index });
                        lastModifiedTriggers.push(tc);
                    }
                }

                const finalLabel = simulation.label;
                statusDiv.textContent = `Status: Simulating (${simsCompleted + 1}/${simulationsToRun.length}): ${finalLabel}`;

                const simResult = await runSimulationMultiple(multiplier, progress => { const totalProgress = ((simsCompleted + (progress / 100)) / simulationsToRun.length) * 100; jigsProgressBar.style.width = `${totalProgress}%`; });
                const newDps = simResult.averageDps;
                const newProfit = simResult.averageProfit;
                const newExp = simResult.averageExp;
                const newEph = simResult.averageEph;
                const newDph = simResult.averageDph;

                simsCompleted++;
                if (isNaN(newDps)) { console.error(`JIGS DEBUG: DPS is NaN for this upgrade. Skipping.`); continue; }

                for (const upgrade of simulation.upgrades) {
                    if (upgrade.isTriggerOnly) continue;
                    let cost = 0;
                    let booksNeeded = 0;
                    try {
                        if (houseKeywords.includes(upgrade.name)) {
                            const startLvl = parseInt(upgrade.originalValue);
                            const endLvl = parseInt(upgrade.value);
                            const roomRecipes = HOUSE_RECIPES[upgrade.name];
                            if (roomRecipes) {
                                for (let i = startLvl; i < endLvl; i++) {
                                    if (!isFinite(cost)) break;
                                    const recipe = roomRecipes[i + 1];
                                    if (recipe) {
                                        cost += recipe.gold;
                                        for (const materialId in recipe.materials) {
                                            const materialName = ITEM_ID_TO_NAME_MAP[materialId];
                                            if (!materialName) { cost = Infinity; break; }
                                            const materialKey = materialName.replace(/'/g, '').toLowerCase();
                                            const price = marketData[materialKey]?.seller === -1 ? Infinity : marketData[materialKey]?.seller || Infinity;
                                            if (price === Infinity) { cost = Infinity; break; }
                                            cost += price * recipe.materials[materialId];
                                        }
                                    }
                                }
                            }
                        } else if (skillKeywords.includes(upgrade.name)) {
                            cost = 0;
                            const startLvl = parseInt(upgrade.originalValue);
                            const endLvl = parseInt(upgrade.value);
                            if (endLvl > startLvl) {
                                const xpRate = getSkillXpRate(upgrade.name);
                                if (xpRate > 0) {
                                    const startXp = ABILITY_XP_LEVELS[startLvl] || 0;
                                    const endXp = ABILITY_XP_LEVELS[endLvl] || 0;
                                    const xpNeeded = endXp - startXp;
                                    if (xpNeeded > 0) {
                                        const hoursNeeded = xpNeeded / xpRate;
                                        const daysNeeded = hoursNeeded / 24;
                                        timeToLevelText = daysNeeded < 1 ? `(${(hoursNeeded).toFixed(1)} hrs)` : `(${(daysNeeded).toFixed(1)} days)`;
                                    }
                                }
                            }
                        } else if (equipmentKeywords.includes(upgrade.name)) {
                            let newPrice = Infinity;
                            if (upgrade.priceOverride) {
                                const parsedPrice = parseGold(upgrade.priceOverride);
                                if (isFinite(parsedPrice)) { newPrice = parsedPrice; }
                            } else {
                                const baseName = upgrade.name;
                                const newSimName = upgrade.value || document.querySelector(`#batch-inputs-container [data-name="${baseName}"]`)?.dataset.originalValue;
                                const newMarketName = SIMULATOR_TO_MARKET_MAP[newSimName] || newSimName;
                                const newEnhName = `${baseName} Enhancement`;
                                const newEnh = upgrade.enhancement ? upgrade.enhancement.value : document.querySelector(`#batch-inputs-container [data-name="${newEnhName}"]`)?.dataset.originalValue || 0;
                                const newKey = newEnh == 0 ? newMarketName.replace(/'/g, '').toLowerCase() : `${newMarketName.replace(/'/g, '').toLowerCase()} +${newEnh}`;
                                const newPriceRaw = marketData[newKey]?.seller;
                                newPrice = (newPriceRaw === undefined || newPriceRaw === -1) ? Infinity : newPriceRaw;
                            }

                            const baseName = upgrade.name;
                            const oldSimName = upgrade.originalValue || document.querySelector(`#batch-inputs-container [data-name="${baseName}"]`)?.dataset.originalValue;
                            if (!oldSimName) { throw new Error(`Could not determine original item name for ${baseName}`); }

                            const oldMarketName = SIMULATOR_TO_MARKET_MAP[oldSimName] || oldSimName;
                            const oldEnhName = `${baseName} Enhancement`;
                            const oldEnh = upgrade.enhancement ? upgrade.enhancement.originalValue : document.querySelector(`#batch-inputs-container [data-name="${oldEnhName}"]`)?.dataset.originalValue || 0;
                            const oldKey = oldEnh == 0 ? oldMarketName.replace(/'/g, '').toLowerCase() : `${oldMarketName.replace(/'/g, '').toLowerCase()} +${oldEnh}`;
                            const oldPriceRaw = marketData[oldKey]?.buyer;
                            const oldPrice = (oldPriceRaw === undefined || oldPriceRaw === -1 || oldMarketName === 'Empty') ? 0 : oldPriceRaw;

                            cost = newPrice - oldPrice;
                        } else if (upgrade.name.startsWith('Ability')) {
                            const baseName = upgrade.name;
                            const abilityName = upgrade.value || document.querySelector(`#batch-inputs-container [data-name="${baseName}"]`)?.dataset.originalValue;
                            const marketItemName = abilityName;
                            const correctlyCasedKey = Object.keys(SPELL_BOOK_XP).find(k => k.toLowerCase() === marketItemName.toLowerCase());
                            const xpPerBook = correctlyCasedKey ? SPELL_BOOK_XP[correctlyCasedKey] : undefined;
                            if (abilityName === 'Empty' || xpPerBook === undefined) { cost = 0; } else {
                                const materialKey = marketItemName.replace(/'/g, '').toLowerCase();
                                const priceOfThisBook = marketData[materialKey]?.seller === -1 ? Infinity : marketData[materialKey]?.seller || Infinity;
                                const levelName = `${baseName} Level`;
                                const startLvl = upgrade.level ? upgrade.level.originalValue : document.querySelector(`#batch-inputs-container [data-name="${levelName}"]`)?.dataset.originalValue;
                                const endLvl = upgrade.level ? upgrade.level.value : document.querySelector(`#batch-inputs-container [data-name="${levelName}"]`)?.dataset.originalValue;
                                if (Number(endLvl) > Number(startLvl)) {
                                    const startXp = ABILITY_XP_LEVELS[startLvl] || 0;
                                    const endXp = ABILITY_XP_LEVELS[endLvl] || 0;
                                    const xpNeeded = endXp - startXp;
                                    booksNeeded = Math.ceil(xpNeeded / xpPerBook);
                                    cost = booksNeeded * priceOfThisBook;
                                }
                            }
                        }
                    } catch (e) {
                        cost = Infinity;
                    }
                    if (isFinite(cost)) {
                        totalCost += cost;
                    } else {
                        totalCost = Infinity;
                    }
                    totalBooks += booksNeeded;
                }

                const dpsGain = newDps - baselineDps; const percentChange = (baselineDps > 0) ? (dpsGain / baselineDps) * 100 : (dpsGain > 0 ? Infinity : 0);
                const profitChange = newProfit - baselineProfit; const percentProfitChange = (baselineProfit > 0) ? (profitChange / baselineProfit) * 100 : (profitChange > 0 ? Infinity : 0);
                const expChange = newExp - baselineExp; const percentExpChange = (baselineExp > 0) ? (expChange / baselineExp) * 100 : (expChange > 0 ? Infinity : 0);
                const ephChange = newEph - baselineEph; const percentEphChange = (baselineEph > 0) ? (ephChange / baselineEph) * 100 : (ephChange > 0 ? Infinity : 0);
                const dphChange = newDph - baselineDph; const percentDphChange = (baselineDph > 0) ? (dphChange / baselineDph) * 100 : (dphChange !== 0 ? Infinity : 0);
                const costPerPercent = (percentChange > 0 && isFinite(totalCost) && totalCost !== 0) ? (totalCost / percentChange) * 0.01 : (totalCost === 0 && dpsGain > 0 ? "Free" : "N/A");
                const costPerProfitPercent = (percentProfitChange > 0 && isFinite(totalCost) && totalCost !== 0) ? (totalCost / percentProfitChange) * 0.01 : (totalCost === 0 && profitChange > 0 ? "Free" : "N/A");
                const costPerExpPercent = (percentExpChange > 0 && isFinite(totalCost) && totalCost !== 0) ? (totalCost / percentExpChange) * 0.01 : (totalCost === 0 && expChange > 0 ? "Free" : "N/A");
                const costPerEphPercent = (percentEphChange > 0 && isFinite(totalCost) && totalCost !== 0) ? (totalCost / percentEphChange) * 0.01 : (totalCost === 0 && ephChange > 0 ? "Free" : "N/A");
                const timeToPurchaseDays = (baselineProfit > 0 && isFinite(totalCost)) ? (totalCost / baselineProfit) : Infinity;
                const resultData = { upgrade: finalLabel, cost: totalCost, timeToPurchase: timeToPurchaseDays, dps: dpsGain, percent: percentChange, costPerDps: costPerPercent, books: totalBooks, averageDps: newDps, individualRuns: simResult.individualRuns, profitChange: profitChange, percentProfitChange: percentProfitChange, costPerProfit: costPerProfitPercent, expChange: expChange, percentExpChange: percentExpChange, costPerExp: costPerExpPercent, ephChange: ephChange, percentEphChange: percentEphChange, costPerEph: costPerEphPercent, dphChange: dphChange, percentDphChange: percentDphChange, timeToLevelText: timeToLevelText };
                addResultRow(resultData);
                detailedResults.push(resultData);
            }
        } finally {
            document.querySelectorAll('#batch-inputs-container [data-original-value]').forEach(jigsEl => {
                const name = jigsEl.dataset.name;
                const originalValue = jigsEl.dataset.originalValue;
                const pageEl = jigsNameToPageElementMap.get(name);
                if (pageEl) {
                    if (pageEl.tagName === 'SELECT') {
                        const opt = Array.from(pageEl.options).find(o => o.text === originalValue);
                        if (opt) pageEl.value = opt.value;
                    } else {
                        pageEl.value = originalValue;
                    }
                    pageEl.dispatchEvent(new Event('change', { bubbles: true }));
                    pageEl.dispatchEvent(new Event('input', { bubbles: true }));
                }
            });
            for (const triggerToReset of lastModifiedTriggers) {
                 const type = triggerToReset.type;
                 const index = triggerToReset.index;
                 const triggerRow = document.querySelector(`.trigger-container [data-trigger-type="${type}"][data-trigger-index="${index}"]`);
                 if (triggerRow) {
                       const originalState = {
                           type: type, index: index,
                           dependency: triggerRow.querySelector('.jigs-trigger-dependency').dataset.originalValue,
                           condition: triggerRow.querySelector('.jigs-trigger-condition').dataset.originalValue,
                           comparator: triggerRow.querySelector('.jigs-trigger-comparator').dataset.originalValue,
                           value: triggerRow.querySelector('.jigs-trigger-value').dataset.originalValue
                       };
                       await applyTriggerChanges(originalState);
                 }
            }

            if (isBatchRunning) { statusDiv.textContent = 'Status: Done!'; }
            simulationQueue = [];
            updateQueuePanelUI();
            if(detailedResults.length > 0) { document.getElementById('export-csv-button').disabled = false; }
            setRunningState(false);
            highlightResults();
            updateColumnVisibility();
        }
    }

    async function importTriggers(isCalledFromBaseline = false) {
        if (!isCalledFromBaseline) {
            console.log("JIGS DEBUG: Starting trigger import.");
            statusDiv.textContent = 'Status: Importing triggers...';
            setRunningState(true);
        }

        const waitForModalContent = () => {
            return new Promise((resolve) => {
                let attempts = 0;
                const interval = setInterval(() => {
                    attempts++;
                    const dependencySelect = document.getElementById('selectTriggerDependency_0');
                    if (dependencySelect && dependencySelect.options.length > 1) {
                        clearInterval(interval);
                        resolve(true);
                    } else if (attempts > 20) { // Timeout after ~5 seconds
                        clearInterval(interval);
                        resolve(false);
                    }
                }, 250);
            });
        };

        try {
            const itemTypes = [
                { type: 'ability', count: 5, prefix: 'buttonAbilityTrigger_' },
                { type: 'food', count: 3, prefix: 'buttonFoodTrigger_' },
                { type: 'drink', count: 3, prefix: 'buttonDrinkTrigger_' }
            ];

            for (const item of itemTypes) {
                for (let i = 0; i < item.count; i++) {
                    if (!isBatchRunning && !isCalledFromBaseline) throw new Error('User stopped');

                    const buttonId = `${item.prefix}${i}`;
                    const triggerButton = document.getElementById(buttonId);
                    const jigsTriggerContainer = document.querySelector(`.trigger-container [data-trigger-type="${item.type}"][data-trigger-index="${i}"]`)?.closest('.trigger-container');
                    if (!triggerButton || !jigsTriggerContainer) continue;

                    if (!isCalledFromBaseline) {
                        statusDiv.textContent = `Importing for ${item.type} ${i + 1}...`;
                    }

                    triggerButton.click();

                    if (!await waitForModalContent()) {
                          console.warn(`JIGS: Modal content for ${buttonId} did not load.`);
                          const closeBtn = document.querySelector('#triggerModal button.btn-close');
                          if (closeBtn) closeBtn.click();
                          await new Promise(r => setTimeout(r, 250));
                          continue;
                    }

                    const scrapeAndSetSelect = (pageSelect, jigsSelect) => {
                        jigsSelect.innerHTML = '';
                        const selectedText = pageSelect.selectedOptions.length > 0 ? pageSelect.selectedOptions[0].text : '';
                        for (const option of pageSelect.options) {
                            jigsSelect.add(new Option(option.text, option.text));
                        }
                        jigsSelect.value = selectedText;
                        jigsSelect.dataset.originalValue = selectedText;
                    };

                    scrapeAndSetSelect(document.getElementById('selectTriggerDependency_0'), jigsTriggerContainer.querySelector('.jigs-trigger-dependency'));
                    scrapeAndSetSelect(document.getElementById('selectTriggerCondition_0'), jigsTriggerContainer.querySelector('.jigs-trigger-condition'));
                    scrapeAndSetSelect(document.getElementById('selectTriggerComparator_0'), jigsTriggerContainer.querySelector('.jigs-trigger-comparator'));

                    const valEl = document.getElementById('inputTriggerValue_0');
                    const jigsVal = jigsTriggerContainer.querySelector('.jigs-trigger-value');
                    jigsVal.value = valEl.value;
                    jigsVal.dataset.originalValue = valEl.value;

                    const rangeEl = jigsTriggerContainer.querySelector('.jigs-trigger-range');
                    rangeEl.value = '';
                    rangeEl.dataset.originalValue = '';
                    const incEl = jigsTriggerContainer.querySelector('.jigs-trigger-increment');
                    incEl.value = '';
                    incEl.dataset.originalValue = '';

                    document.querySelector('#triggerModal button.btn-close').click();
                    await new Promise(r => setTimeout(r, 250));
                }
            }
            if (!isCalledFromBaseline) {
                statusDiv.textContent = 'Status: Trigger import complete.';
            }
        } catch (e) {
            if (e.message === 'User stopped') {
                statusDiv.textContent = 'Status: Trigger import stopped by user.';
            } else {
                console.error('JIGS: Error during trigger import.', e);
                statusDiv.textContent = 'Error during trigger import. Check console.';
            }
        } finally {
            if (!isCalledFromBaseline) {
                setRunningState(false);
            }
        }
    }

    function applySavedPanelStates() {
        const panels = [
            { id: 'batch-panel', toggleId: 'batch-toggle' },
            { id: 'jigs-results-panel', toggleId: 'results-toggle' },
            { id: 'jigs-queue-panel', toggleId: 'queue-toggle' }
        ];
        panels.forEach(p => {
            const panel = document.getElementById(p.id);
            if (!panel) return;

            const savedData = GM_getValue(`jigs_panel_positions_${p.id}`);
            if (savedData) {
                if (savedData.top && savedData.left) {
                    panel.style.top = savedData.top;
                    panel.style.left = savedData.left;
                    panel.style.bottom = 'auto';
                    panel.style.right = 'auto';
                }
                if (savedData.width) panel.style.width = savedData.width;
                if (savedData.height) panel.style.height = savedData.height;
            }

            const isMinimized = GM_getValue(`jigs_panel_minimized_${p.id}`, false);
            if (isMinimized) {
                panel.classList.add('jigs-minimized');
                const toggleButton = document.getElementById(p.toggleId);
                if (toggleButton) toggleButton.textContent = '+';
            }
        });
    }

    // --- 5. INITIALIZATION ---
    async function initializeScript() {
        console.log("JIGS DEBUG: Step 1 - initializeScript started.");
        if (!await fetchJigsData()) {
            statusDiv.textContent = 'Error: Could not load critical JIGS data. The script cannot continue.';
            return;
        }
        console.log("JIGS DEBUG: Step 2 - fetchJigsData successful.");
        const controlsPanel = document.getElementById('batch-panel');
        controlsPanel.addEventListener('click', (event) => {
            const button = event.target.closest('button');
            if (!button) return;
            switch (button.id) {
                case 'batch-toggle':
                    controlsPanel.classList.toggle('jigs-minimized');
                    button.textContent = controlsPanel.classList.contains('jigs-minimized') ? '+' : '-';
                    GM_setValue(`jigs_panel_minimized_batch-panel`, controlsPanel.classList.contains('jigs-minimized'));
                    break;
                case 'run-batch-button':
                    startBatch();
                    break;
                case 'stop-batch-button':
                    isBatchRunning = false;
                    statusDiv.textContent = 'Status: Stopping...';
                    const stopSimButton = document.getElementById('buttonStopSimulation');
                    if (stopSimButton) {
                        stopSimButton.click();
                    }
                    break;
                case 'capture-setup-button':
                    buildInputsUI();
                    break;
                case 'update-baseline-button':
                    const importTriggers = document.getElementById('import-triggers-checkbox').checked;
                    updateBaseline(importTriggers);
                    break;
                case 'reset-button':
                    resetInputsToBaseline();
                    break;
                case 'add-to-queue-button':
                    addChangesToQueue();
                    break;
                case 'reset-panels-button':
                    resetPanelPositions();
                    break;
            }
        });
        const resultsPanel = document.getElementById('jigs-results-panel');
        resultsPanel.addEventListener('click', (event) => {
            const button = event.target.closest('button');
            if (!button) return;
            switch (button.id) {
                case 'results-toggle':
                    resultsPanel.classList.toggle('jigs-minimized');
                    button.textContent = resultsPanel.classList.contains('jigs-minimized') ? '+' : '-';
                    GM_setValue(`jigs_panel_minimized_jigs-results-panel`, resultsPanel.classList.contains('jigs-minimized'));
                    break;
                case 'clear-results-button':
                    console.log("JIGS DEBUG: Clearing all results.");
                    document.querySelector('#batch-results-table tbody').innerHTML = '';
                    detailedResults = [];
                    document.getElementById('export-csv-button').disabled = true;
                    break;
                case 'export-csv-button':
                    exportResultsToCSV();
                    break;
            }
        });

        const queuePanel = document.getElementById('jigs-queue-panel');
        queuePanel.addEventListener('click', (event) => {
            const target = event.target;

            // Handle the main panel buttons
            if (target.matches('#queue-toggle') || target.matches('#clear-queue-button')) {
                switch (target.id) {
                    case 'queue-toggle':
                        queuePanel.classList.toggle('jigs-minimized');
                        target.textContent = queuePanel.classList.contains('jigs-minimized') ? '+' : '-';
                        GM_setValue(`jigs_panel_minimized_jigs-queue-panel`, queuePanel.classList.contains('jigs-minimized'));
                        break;
                    case 'clear-queue-button':
                        console.log("JIGS DEBUG: Clearing simulation queue.");
                        simulationQueue = [];
                        updateQueuePanelUI();
                        break;
                }
                return; // Exit after handling main buttons
            }

            // Handle individual remove buttons
            if (target.classList.contains('jigs-remove-queue-item-button')) {
                const indexToRemove = parseInt(target.dataset.index, 10);
                if (!isNaN(indexToRemove) && indexToRemove >= 0 && indexToRemove < simulationQueue.length) {
                    console.log(`JIGS DEBUG: Removing item at index ${indexToRemove} from queue.`);
                    simulationQueue.splice(indexToRemove, 1);
                    updateQueuePanelUI(); // Refresh the queue display
                }
            }
        });

        const inputsContainer = document.getElementById('batch-inputs-container');
        if (inputsContainer) {
            inputsContainer.addEventListener('change', (event) => {
                if (event.target.dataset.name === 'Multiplier') {
                    updateQueueEstimate();
                }
                if (event.target.tagName === 'INPUT' || event.target.tagName === 'SELECT') {
                    updateModifiedIndicator(event.target);
                }
            }, true);
        }

        controlsPanel.addEventListener('change', async (event) => {
            if (event.target.id === 'jigs-player-select') {
                const selectedPlayerName = event.target.value;
                switchPlayerAndCapture(selectedPlayerName);
            }
        });
        const columnToggleContainer = document.getElementById('column-toggle-container');
        if (columnToggleContainer) {
            columnToggleContainer.addEventListener('change', updateColumnVisibility);
        }
        document.getElementById('baseline-dps-input').addEventListener('change', updateBaselinesFromInputs);
        document.getElementById('baseline-profit-input').addEventListener('change', updateBaselinesFromInputs);
        document.getElementById('baseline-exp-input').addEventListener('change', updateBaselinesFromInputs);
        document.getElementById('baseline-eph-input').addEventListener('change', updateBaselinesFromInputs);
        document.getElementById('baseline-dph-input').addEventListener('change', updateBaselinesFromInputs);
        const resultsThead = document.querySelector('#batch-results-table thead');
        if (resultsThead) {
            resultsThead.addEventListener('click', (event) => {
                const headerCell = event.target.closest('th');
                if (!headerCell) return;
                const sortKey = headerCell.dataset.sortKey;
                if (!sortKey) return;
                const tbody = headerCell.closest('table').querySelector('tbody');
                const rows = Array.from(tbody.querySelectorAll('tr'));
                const isDesc = headerCell.classList.contains('sorted-desc');
                const direction = isDesc ? 1 : -1;
                rows.sort((a, b) => {
                    const valA = a.dataset[sortKey];
                    const valB = b.dataset[sortKey];
                    const numA = parseFloat(valA);
                    const numB = parseFloat(valB);
                    if (!isNaN(numA) && !isNaN(numB)) {
                        return (numA - numB) * direction;
                    }
                    return valA.localeCompare(valB) * direction;
                });
                tbody.innerHTML = '';
                rows.forEach(row => tbody.appendChild(row));
                headerCell.parentElement.querySelectorAll('th').forEach(th => th.classList.remove('sorted-asc', 'sorted-desc'));
                if (direction === 1) {
                    headerCell.classList.add('sorted-asc');
                } else {
                    headerCell.classList.add('sorted-desc');
                }
            });
        } else {
            console.error("JIGS Error: Could not find the results table header to attach the sort listener.");
        }
        statusDiv.textContent = 'Status: Ready. Please import a character.';
        document.getElementById('capture-setup-button').disabled = false;
        const findButtonInterval = setInterval(() => {
            const importButton = Array.from(document.querySelectorAll('button')).find(btn => btn.textContent.includes('Import solo/group'));
            if (importButton) {
                clearInterval(findButtonInterval);
                importButton.addEventListener('click', () => {
                    statusDiv.textContent = 'Import initiated! Waiting for player names and initial sim...';
                    const resultsContainer = document.getElementById('simulationResultTotalDamageDone');
                    if (resultsContainer) resultsContainer.innerHTML = '';
                    const baselineObserver = new MutationObserver(() => {
                        const dpsVal = getDpsValue();
                        if (dpsVal) {
                            baselineObserver.disconnect();
                            baselineDps = normalizeAndParseFloat(dpsVal);
                            baselineProfit = getProfitValue() || 0;
                            baselineExp = getExpValue() || 0;
                            baselineEph = getEphValue() || 0;
                            baselineDph = getDphValue() || 0;
                            document.getElementById('baseline-dps-input').value = baselineDps.toFixed(2);
                            document.getElementById('baseline-profit-input').value = formatGold(baselineProfit);
                            document.getElementById('baseline-exp-input').value = baselineExp.toLocaleString();
                            document.getElementById('baseline-eph-input').value = baselineEph.toFixed(2);
                            document.getElementById('baseline-dph-input').value = baselineDph.toFixed(2);

                            captureBaselineSkillRates();
                            updateQueueEstimate();
                        }
                    });
                    baselineObserver.observe(document.body, {
                        childList: true,
                        subtree: true
                    });
                    let uiBuildAttempt = 0;
                    const uiBuilderInterval = setInterval(() => {
                        uiBuildAttempt++;
                        const playerTabs = Array.from(document.querySelectorAll('a[id^="player"][id$="-tab"]'));
                        const namesAreLoaded = playerTabs.length > 0 && playerTabs.some(tab => !tab.textContent.trim().startsWith('Player '));
                        if (namesAreLoaded) {
                            clearInterval(uiBuilderInterval);
                            buildInputsUI();
                        } else if (uiBuildAttempt > 50) {
                            clearInterval(uiBuilderInterval);
                            statusDiv.textContent = 'Status: Could not detect player names. Please use Capture Setup.';
                        }
                    }, 200);
                });
            }
        }, 500);
        updateColumnVisibility();

        makeDraggable(document.getElementById('batch-panel'), document.getElementById('batch-header'));
        makeResizable(document.getElementById('batch-panel'), document.getElementById('batch-panel').querySelector('.jigs-resizer'));

        makeDraggable(document.getElementById('jigs-results-panel'), document.getElementById('jigs-results-header'));
        makeResizable(document.getElementById('jigs-results-panel'), document.getElementById('jigs-results-panel').querySelector('.jigs-resizer'));

        makeDraggable(document.getElementById('jigs-queue-panel'), document.getElementById('jigs-queue-header'));
        makeResizable(document.getElementById('jigs-queue-panel'), document.getElementById('jigs-queue-panel').querySelector('.jigs-resizer'));

        applySavedPanelStates();
    }
    initializeScript();
})();