✈️ Sets Tracker

Overseas sets companion — Sets · Xanax Runs · Travel Planner · Pure Profit · Museum Day · Live YATA Stock — Torn

Fra og med 18.03.2026. Se den nyeste version.

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         ✈️ Sets Tracker
// @namespace    https://osdevscape.com
// @version      9.8.0
// @author       Phillip_J_Fry [2184575] (OSMays8338) — OSDevscape
// @license      All Rights Reserved © 2026 OSDevscape
// @homepageURL  https://greatest.deepsurf.us/users/OSMays8338
// @description  Overseas sets companion — Sets · Xanax Runs · Travel Planner · Pure Profit · Museum Day · Live YATA Stock — Torn
// @match        https://www.torn.com/*
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_xmlhttpRequest
// @grant        unsafeWindow
// @connect      yata.yt
// @connect      api.torn.com
// @connect      flagcdn.com
// @run-at       document-end
// ==/UserScript==
 
// ╔══════════════════════════════════════════════════════════════╗
// ║              ✈️  SETS TRACKER  v9.8.0                          ║
// ║                                                              ║
// ║  Author  :  Phillip_J_Fry [2184575] (Torn) · OSMays8338     ║
// ║  Company :  OSDevscape                                       ║
// ║  License :  All Rights Reserved © 2026 OSDevscape           ║
// ║                                                              ║
// ║  Based on "Points Museum" by SuperNovae [2637223]            ║
// ║  Rebuilt under OSDevscape suite architecture v7              ║
// ╚══════════════════════════════════════════════════════════════╝
 
(function () {
'use strict';
 
/* ─────────────────────────────────────────
   STORAGE — dual layer: localStorage + GM
   Mirrors Target Tracker / Race Tracker
───────────────────────────────────────── */
const store = {
    get(k, d) {
        try { const v = localStorage.getItem('lt_' + k); if (v !== null) return v; } catch(e) {}
        try { return GM_getValue(k, d); } catch(e) {}
        return d;
    },
    set(k, v) {
        try { localStorage.setItem('lt_' + k, v); } catch(e) {}
        try { GM_setValue(k, v); } catch(e) {}
    },
    getJSON(k, d) {
        try { const v = localStorage.getItem('lt_' + k); if (v !== null) return JSON.parse(v); } catch(e) {}
        try { return JSON.parse(GM_getValue(k, JSON.stringify(d))); } catch(e) {}
        return d;
    },
    setJSON(k, v) {
        const s = JSON.stringify(v);
        try { localStorage.setItem('lt_' + k, s); } catch(e) {}
        try { GM_setValue(k, s); } catch(e) {}
    }
};
 
/* ─────────────────────────────────────────
   CONFIG
───────────────────────────────────────── */
const cfg = {
    get apiKey()  { return store.get('lt_api', ''); },
    set apiKey(v) { store.set('lt_api', v); },
    get userId()  { return store.get('lt_uid', ''); },
    set userId(v) { store.set('lt_uid', v); },
    getSectionVis() { return store.getJSON('lt_sections', { prehistoric: true, plushies: true, flowers: true, special: true, xanax: true }); },
    setSectionVis(v){ store.setJSON('lt_sections', v); },
    getXanCount()   { return store.getJSON('lt_xan_count', 0); },
    setXanCount(v)  { store.setJSON('lt_xan_count', v); },
    getXanCarry()       { return store.getJSON('lt_xan_carry', 0); },
    setXanCarry(v)      { store.setJSON('lt_xan_carry', v); },
    getXanPriority()    { return store.getJSON('lt_xan_priority', false); },
    setXanPriority(v)   { store.setJSON('lt_xan_priority', v); },
    getXanThreshold()   { return store.getJSON('lt_xan_threshold', 50); },
    setXanThreshold(v)  { store.setJSON('lt_xan_threshold', v); },
    getXanRuns()        { return store.getJSON('lt_xan_runs', []); },
    setXanRuns(v)       { store.setJSON('lt_xan_runs', v); },
    getXanCountry()     { return store.getJSON('lt_xan_country', 'South Africa'); },
    setXanCountry(v)    { store.setJSON('lt_xan_country', v); },
    getForceTheme()     { return store.getJSON('lt_force_theme', 'auto'); }, // 'auto' | 'light' | 'dark'
    setForceTheme(v)    { store.setJSON('lt_force_theme', v); },
    getMuseumPin()      { return store.getJSON('lt_museum_pin', false); },
    setMuseumPin(v)     { store.setJSON('lt_museum_pin', v); },
    getTravelSpeed()    { return store.getJSON('lt_travel_speed', 0); },   // 0=Standard 1=Airstrip 2=PrivateJet 3=WindLines
    setTravelSpeed(v)   { store.setJSON('lt_travel_speed', v); },
    getProfitCarry()    { return store.getJSON('lt_profit_carry', 1); },
    setProfitCarry(v)   { store.setJSON('lt_profit_carry', v); },
    getProfitItemTypes(){ return store.getJSON('lt_profit_types', { plushies: true, flowers: true, prehistoric: true, special: true, drugs: false }); },
    setProfitItemTypes(v){ store.setJSON('lt_profit_types', v); },
    getProfitCountries(){ return store.getJSON('lt_profit_countries', { short: true, medium: true, long: true }); },
    setProfitCountries(v){ store.setJSON('lt_profit_countries', v); },
    getVaultDest()      { return store.getJSON('lt_vault_dest', 'faction'); }, // 'faction' | 'company' | 'property'
    setVaultDest(v)     { store.setJSON('lt_vault_dest', v); },
    getFlagCache()      { return store.getJSON('lt_flag_cache', {}); },
    setFlagCache(v)     { store.setJSON('lt_flag_cache', v); },
    getPriceHistory(name) { return store.getJSON('lt_ph_' + name.replace(/[^a-z0-9]/gi,'_'), []); },
    addPriceHistory(name, price) {
        const h = store.getJSON('lt_ph_' + name.replace(/[^a-z0-9]/gi,'_'), []);
        h.push({ ts: Date.now(), price: Number(price) });
        if (h.length > 12) h.splice(0, h.length - 12);
        store.setJSON('lt_ph_' + name.replace(/[^a-z0-9]/gi,'_'), h);
    },
    getStockHistory(name) { return store.getJSON('lt_sh_' + name.replace(/[^a-z0-9]/gi,'_'), []); },
    addStockHistory(name, qty) {
        const h = store.getJSON('lt_sh_' + name.replace(/[^a-z0-9]/gi,'_'), []);
        h.push({ ts: Date.now(), qty: Number(qty) });
        if (h.length > 12) h.splice(0, h.length - 12);
        store.setJSON('lt_sh_' + name.replace(/[^a-z0-9]/gi,'_'), h);
    },
};
 
/* ─────────────────────────────────────────
   TORN THEME DETECTION
   ─────────────────────────────────────────
   Torn signals light mode via one or more of:
     • document.body classList  → check TORN_LIGHT_CLASSES
     • document.documentElement → same list
     • data-theme / data-color-scheme attribute
 
   ⚠️  CONFIRM YOUR CLASS:
   Open DevTools → Elements → inspect <html> or <body>
   Switch Torn between light/dark and watch what class
   appears/disappears. Then add it to TORN_LIGHT_CLASSES.
───────────────────────────────────────── */
// TornPDA dark mode  → body has class 'dark-mode'
// TornPDA light mode → body has class 'light-mode'  (or no dark-mode class)
function isLightMode() {
    // User-forced override always wins
    try {
        const forced = store.getJSON('lt_force_theme', 'auto');
        if (forced === 'light') return true;
        if (forced === 'dark')  return false;
        // auto-reverse: invert the page's actual theme
        const pageIsLight = !document.body.classList.contains('dark-mode') && !document.documentElement.classList.contains('dark-mode');
        if (forced === 'auto-reverse') return !pageIsLight;
    } catch(e) {}

    // Dark mode = body has 'dark-mode' class. Everything else = light.
    const b = document.body;
    const h = document.documentElement;
    if (!b) return false;
    if (b.classList.contains('dark-mode') || h.classList.contains('dark-mode')) return false;
    return true;
}

 
/* ─────────────────────────────────────────
   COLOUR PALETTES  (dark = default)
───────────────────────────────────────── */
const DARK_PALETTE = {
    bg:        '#001214',
    bg2:       '#001e24',
    border:    'rgba(0,180,210,0.35)',
    gold:      '#00c8e0',
    goldDim:   'rgba(0,200,224,0.85)',
    goldGlow:  'rgba(0,200,224,0.10)',
    green:     '#8BC34A',
    greenDim:  'rgba(139,195,74,0.7)',
    text:      '#d0f0f8',
    textDim:   'rgba(140,220,235,0.80)',
    abroad:    '#7fe0f0',
    stockHi:   '#00ff00',
    stockMid:  '#ffa500',
    stockLo:   '#ff0000',
    okay:      '#66dd66',
    card:      'rgba(0,16,26,0.65)',   // card background
    cardBorder:'rgba(0,200,224,0.28)', // card border
    mono:      '"Share Tech Mono",Consolas,monospace',
    sans:      'Rajdhani,"Segoe UI",Arial,sans-serif',
    // settings popup extras
    settBg:    '#000e12',
    settBorder:'rgba(0,180,210,0.55)',
    settNote:  'rgba(0,80,100,0.3)',
    settHdr:   'rgba(0,40,58,1.0)',
};
 
const LIGHT_PALETTE = {
    bg:        '#b8dff0',       // noticeably deeper blue-tint base
    bg2:       '#9dd0e6',       // deeper secondary surface
    border:    'rgba(0,70,100,0.55)',
    gold:      '#002a36',       // very deep teal heading colour
    goldDim:   'rgba(0,55,75,0.90)',
    goldGlow:  'rgba(0,75,105,0.18)',
    green:     '#163800',
    greenDim:  'rgba(20,55,4,0.90)',
    text:      '#000a10',
    textDim:   'rgba(0,32,46,0.78)',
    abroad:    '#002d40',
    stockHi:   '#0d3600',
    stockMid:  '#442500',
    stockLo:   '#680000',
    okay:      '#0d3600',
    card:      'rgba(0,40,60,0.18)',   // card background
    cardBorder:'rgba(0,65,95,0.40)',   // card border
    mono:      '"Share Tech Mono",Consolas,monospace',
    sans:      'Rajdhani,"Segoe UI",Arial,sans-serif',
    // settings popup extras
    settBg:    '#c2e8f8',
    settBorder:'rgba(0,90,120,0.65)',
    settNote:  'rgba(0,110,145,0.22)',
    settHdr:   'rgba(0,70,100,0.38)',
};
 
// C is a live proxy — always reflects current Torn theme
let C = isLightMode() ? { ...LIGHT_PALETTE } : { ...DARK_PALETTE };
 
function syncTheme(forceRebuild) {
    const light  = isLightMode();
    const newPal = light ? LIGHT_PALETTE : DARK_PALETTE;
    const changed = Object.keys(newPal).some(k => C[k] !== newPal[k]);
    Object.assign(C, newPal);
    // Header background is always #00283a — update it in case panel was rebuilt
    const _ptEl = panelEl && panelEl.querySelector('#lt-panel-title');
    if (_ptEl) { _ptEl.style.color = '#00b7cf'; _ptEl.closest('div').style.background = '#00283a'; }
    if ((changed || forceRebuild) && panelEl && toggleEl) {
        const panelBg = light
            ? 'linear-gradient(158deg,rgba(185,225,245,0.99),rgba(165,215,238,0.98))'
            : 'linear-gradient(158deg,rgba(0,14,18,0.98),rgba(0,8,12,0.97))';
        panelEl.style.setProperty('background', panelBg, 'important');
        panelEl.style.setProperty('border-color', C.border, 'important');
        panelEl.style.setProperty('color', C.text, 'important');
        const toggleBg = light
            ? 'radial-gradient(circle at 38% 34%,#d0f8ff,#a8eef8)'
            : 'radial-gradient(circle at 38% 34%,#001a20,#000c10)';
        toggleEl.style.setProperty('background', toggleBg, 'important');
        toggleEl.style.setProperty('border-color', C.border, 'important');
        if (panelOpen) renderPanel();
    }
}
 
// Watch for URL/page changes to trigger items page scrape
(function watchNavigation() {
    let lastHref = window.location.href;
    const navObs = new MutationObserver(() => {
        const href = window.location.href;
        if (href !== lastHref) {
            lastHref = href;
            watchItemsPage();
            // Scrape travel status immediately on page change
            setTimeout(scrapeTravelPage, 800);
            setTimeout(scrapeTravelPage, 2000);
            // Scrape flag images whenever we land on the travel page
            if (href.includes('travel')) {
                setTimeout(scrapeCountryFlags, 1000);
                setTimeout(scrapeCountryFlags, 3500);
            }
            // Auto-fill carry qty in shop inputs when abroad
            try { if (abroadCountry && !href.includes('travel')) {
                setTimeout(fillCarryQty, 1200);
                setTimeout(fillCarryQty, 3000);
            } } catch(e) {}
        }
    });
    navObs.observe(document.body, { childList: true, subtree: true });
    // Also check on load in case we're already on items page
    watchItemsPage();
    // Check if we're already on the travel page on load
    setTimeout(scrapeTravelPage, 1000);
    if (document.location.href.includes('travel')) {
        setTimeout(scrapeCountryFlags, 1200);
        setTimeout(scrapeCountryFlags, 3500);
    }
    // Shop input auto-fill observer — deferred so abroadCountry is initialized
    setTimeout(function() {
        try {
            if (!abroadCountry) return;
            setTimeout(fillCarryQty, 1500);
            const _shopObs = new MutationObserver(function() {
                try {
                    if (!abroadCountry) return;
                    const doc2 = (typeof unsafeWindow !== 'undefined' && unsafeWindow.document) ? unsafeWindow.document : document;
                    if (doc2.querySelector('input[placeholder="Qty"]')) {
                        fillCarryQty();
                        // Don't disconnect — new items load dynamically as you scroll/navigate
                    }
                } catch(e) {}
            });
            _shopObs.observe(document.body, { childList: true, subtree: true });
        } catch(e) {}
    }, 2000);
})();
 
// Watch for dark-mode class being added/removed on body or html
(function watchTheme() {
    let _lastLight = isLightMode();

    function checkAndSync() {
        const nowLight = isLightMode();
        if (_lastLight === nowLight) return;
        _lastLight = nowLight;
        syncTheme(true);
    }

    // Watch class attribute on body and html
    const obs = new MutationObserver(checkAndSync);
    obs.observe(document.body, { attributes: true, attributeFilter: ['class'] });
    obs.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] });
})();
 
/* ─────────────────────────────────────────
   POINTS & THRESHOLDS
───────────────────────────────────────── */
const PRE_PTS = 25, FLO_PTS = 10, PLU_PTS = 10, MET_PTS = 15, FOS_PTS = 20;
const PLU_THRESH = 2000, FLO_THRESH = 5000;
const XANAX_ID   = 206;
const POINTS_ENDPOINT = 'https://api.torn.com/v2/market/pointsmarket';
 
const POINTS_CACHE_DUR  = 300000;
const POINTS_HIST_SIZE  = 5;
let pointsPriceCache    = { time: 0, price: 0, history: [] };

/* ─────────────────────────────────────────
   TRAVEL TIMES (hours, one-way) per speed tier
   0=Standard  1=Airstrip  2=Private Jet  3=Wind Lines
───────────────────────────────────────── */
const TRAVEL_TIMES = {
    // country → [Standard, Airstrip, PrivateJet, WindLines] hours one-way
    // Airstrip times confirmed from Torn Travel Agency page (player's actual times)
    // Other tiers calculated from Torn's speed ratios: 1 : 0.75 : 0.5 : 0.33
    'Mexico':         [0.40, 0.30, 0.20, 0.13 ],
    'Cayman Islands': [0.56, 0.42, 0.28, 0.18 ],
    'Canada':         [0.64, 0.48, 0.32, 0.21 ],
    'Hawaii':         [2.09, 1.57, 1.04, 0.69 ],
    'UK':             [2.47, 1.85, 1.23, 0.81 ],
    'United Kingdom': [2.47, 1.85, 1.23, 0.81 ],
    'Argentina':      [2.60, 1.95, 1.30, 0.86 ],
    'Switzerland':    [2.73, 2.05, 1.37, 0.90 ],
    'Japan':          [3.51, 2.63, 1.76, 1.16 ],
    'China':          [3.76, 2.82, 1.88, 1.24 ],
    'UAE':            [4.22, 3.17, 2.11, 1.39 ],
    'South Africa':   [4.62, 3.47, 2.31, 1.53 ],
};
 
/* ─────────────────────────────────────────
   LOCATION MAP
───────────────────────────────────────── */
const LOCATIONS = {
    'Mexico':         { flag: '🇲🇽', label: 'Mexico' },
    'Hawaii':         { flag: '🇺🇸',  label: 'Hawaii' },
    'South Africa':   { flag: '🇿🇦', label: 'South Africa' },
    'Japan':          { flag: '🇯🇵', label: 'Japan' },
    'China':          { flag: '🇨🇳', label: 'China' },
    'Argentina':      { flag: '🇦🇷', label: 'Argentina' },
    'Switzerland':    { flag: '🇨🇭', label: 'Switzerland' },
    'Canada':         { flag: '🇨🇦', label: 'Canada' },
    'UK':             { flag: '🇬🇧', label: 'United Kingdom' },
    'United Kingdom': { flag: '🇬🇧', label: 'United Kingdom' }, // alias
    'UAE':            { flag: '🇦🇪', label: 'UAE' },
    'Cayman Islands': { flag: '🇰🇾', label: 'Cayman Islands' },
    'BoB':            { flag: '🏪',  label: "Bits n' Bobs" },
};
 
const FLAG_IMGS = {
    'Mexico': 'https://flagcdn.com/w40/mx.png',
    'Hawaii': 'https://flagcdn.com/w40/us.png',
    'South Africa': 'https://flagcdn.com/w40/za.png',
    'Japan': 'https://flagcdn.com/w40/jp.png',
    'China': 'https://flagcdn.com/w40/cn.png',
    'Argentina': 'https://flagcdn.com/w40/ar.png',
    'Switzerland': 'https://flagcdn.com/w40/ch.png',
    'Canada': 'https://flagcdn.com/w40/ca.png',
    'UK': 'https://flagcdn.com/w40/gb.png',
    'United Kingdom': 'https://flagcdn.com/w40/gb.png',
    'UAE': 'https://flagcdn.com/w40/ae.png',
    'Cayman Islands': 'https://flagcdn.com/w40/ky.png',
    'Torn City': 'https://www.torn.com/favicon.ico',
    'BoB': null,
};

/* ─────────────────────────────────────────
   ITEM GROUPS
───────────────────────────────────────── */
const GROUPS = {
    Prehistoric: {
        pts: PRE_PTS, icon: '🪨',
        items: {
            'Quartz Point':     { id: 619, s: 'Quartz',   loc: 'Canada'       },
            'Chalcedony Point': { id: 620, s: 'Chalced',  loc: 'Argentina'    },
            'Basalt Point':     { id: 621, s: 'Basalt',   loc: 'Hawaii'       },
            'Quartzite Point':  { id: 622, s: 'Quartzit', loc: 'South Africa' },
            'Chert Point':      { id: 623, s: 'Chert',    loc: 'UK'           },
            'Obsidian Point':   { id: 624, s: 'Obsidian', loc: 'Mexico'       },
        }
    },
    Plushies: {
        pts: PLU_PTS, icon: '🧸',
        items: {
            'Sheep Plushie':      { id: 186, s: 'Sheep',     loc: 'BoB'           },
            'Teddy Bear Plushie': { id: 187, s: 'Teddy',     loc: 'BoB'           },
            'Kitten Plushie':     { id: 215, s: 'Kitten',    loc: 'BoB'           },
            'Jaguar Plushie':     { id: 258, s: 'Jaguar',    loc: 'Mexico'        },
            'Wolverine Plushie':  { id: 261, s: 'Wolverine', loc: 'Canada'        },
            'Nessie Plushie':     { id: 266, s: 'Nessie',    loc: 'UK'            },
            'Red Fox Plushie':    { id: 268, s: 'Fox',       loc: 'UK'            },
            'Monkey Plushie':     { id: 269, s: 'Monkey',    loc: 'Argentina'     },
            'Chamois Plushie':    { id: 273, s: 'Chamois',   loc: 'Switzerland'   },
            'Panda Plushie':      { id: 274, s: 'Panda',     loc: 'China'         },
            'Lion Plushie':       { id: 281, s: 'Lion',      loc: 'South Africa'  },
            'Camel Plushie':      { id: 384, s: 'Camel',     loc: 'UAE'           },
            'Stingray Plushie':   { id: 618, s: 'Stingray',  loc: 'Cayman Islands'},
        }
    },
    Flowers: {
        pts: FLO_PTS, icon: '🌸',
        items: {
            'Dahlia':            { id: 260, s: 'Dahlia',    loc: 'Mexico'        },
            'Orchid':            { id: 264, s: 'Orchid',    loc: 'Hawaii'        },
            'African Violet':    { id: 282, s: 'Violet',    loc: 'South Africa'  },
            'Cherry Blossom':    { id: 277, s: 'Blossoms',  loc: 'Japan'         },
            'Peony':             { id: 276, s: 'Peony',     loc: 'China'         },
            'Ceibo Flower':      { id: 271, s: 'Ceibo',     loc: 'Argentina'     },
            'Edelweiss':         { id: 272, s: 'Edelweiss', loc: 'Switzerland'   },
            'Crocus':            { id: 263, s: 'Crocus',    loc: 'Canada'        },
            'Heather':           { id: 267, s: 'Heather',   loc: 'UK'            },
            'Tribulus Omanense': { id: 385, s: 'Tribulus',  loc: 'UAE'           },
            'Banana Orchid':     { id: 617, s: 'B.Orchid',  loc: 'Cayman Islands'},
        }
    },
};
 
const SPECIAL_ITEMS = {
    'Meteorite Fragment': { id: 512, s: 'Meteor', loc: 'Argentina', pts: MET_PTS },
    'Patagonian Fossil':  { id: 513, s: 'Fossil', loc: 'Argentina', pts: FOS_PTS },
};
 
const BOB_IDS = new Set([186, 187, 215]);
const itemImg = id => `https://www.torn.com/images/items/${id}/large.png`;
 
/* ─────────────────────────────────────────
   BUILD ID→NAME LOOKUP for YATA parsing
───────────────────────────────────────── */
function buildIdMap() {
    const m = {};
    Object.values(GROUPS).forEach(g => Object.entries(g.items).forEach(([name, d]) => { m[d.id] = name; }));
    Object.entries(SPECIAL_ITEMS).forEach(([name, d]) => { m[d.id] = name; });
    m[XANAX_ID] = 'Xanax';
    return m;
}
const ID_TO_NAME = buildIdMap();
 
/* ─────────────────────────────────────────
   YATA COUNTRY CODE → CITY NAME
   YATA uses 3-letter country codes; we map to city names
   matching Torn's in-game city names exactly
───────────────────────────────────────── */
const YATA_CODE_TO_CITY = {
    'mex': 'Mexico City',
    'haw': 'Honolulu',
    'sou': 'Johannesburg',
    'jap': 'Tokyo',
    'chi': 'Shanghai',
    'arg': 'Buenos Aires',
    'swi': 'Zurich',
    'can': 'Toronto',
    'uni': 'London',
    'uae': 'Dubai',
    'cay': 'Grand Cayman',
};

// Actual in-game city names as shown by Torn (authoritative — e.g. Mexico = Ciudad Juarez not Mexico City)
const TORN_CITIES = {
    'Mexico':         'Ciudad Juarez',
    'Hawaii':         'Honolulu',
    'South Africa':   'Johannesburg',
    'Japan':          'Tokyo',
    'China':          'Shanghai',
    'Argentina':      'Buenos Aires',
    'Switzerland':    'Zurich',
    'Canada':         'Toronto',
    'United Kingdom': 'London',
    'UAE':            'Dubai',
    'Cayman Islands': 'Grand Cayman',
};

// Torn travel API destination name → YATA code (for matching)
const TORN_DEST_TO_CODE = {
    'Mexico':         'mex',
    'Hawaii':         'haw',
    'South Africa':   'sou',
    'Japan':          'jap',
    'China':          'chi',
    'Argentina':      'arg',
    'Switzerland':    'swi',
    'Canada':         'can',
    'United Kingdom': 'uni',
    'UAE':            'uae',
    'Cayman Islands': 'cay',
};

// City name (as shown on Torn travel page) → YATA code
// Torn displays the actual city name in the "X to Torn" flight string
const CITY_TO_CODE = {
    'Mexico City':    'mex',
    'Ciudad Juarez':  'mex',   // actual Torn city name for Mexico
    'Honolulu':     'haw',
    'Johannesburg': 'sou',
    'Cape Town':    'sou',   // fallback alias
    'Tokyo':        'jap',
    'Shanghai':     'chi',
    'Buenos Aires': 'arg',
    'Zurich':       'swi',
    'Toronto':      'can',
    'London':       'uni',
    'Dubai':        'uae',
    'Grand Cayman': 'cay',
};

/* ─────────────────────────────────────────
   STATE
───────────────────────────────────────── */
let toggleEl  = null;
let panelEl   = null;
let panelOpen = false;
let activeTab = 'sets';   // sets | xanax | travel | abroad
let abroadCountry = '';   // country name when landed abroad (e.g. 'Mexico')
let isLoading = false;
let pollTimer = null;
let countryFlagCache = {}; // { 'Mexico': 'https://...', ... } — scraped from travel page DOM
 
// cached data
let invCache    = {};   // display items { name: qty }
let abroadCache = {};   // BoB shop stock { name: qty } via torn/?selections=shopsandstocks
let bobCache    = {};   // same source — BoB specific quantities for display
let xanSACache  = { qty: 0, price: 0 };
let xanPersonal = 0; // populated from items page scrape, persisted via cfg.setXanCount
let xanFacCache = null;
let pointsPrice = 0;
let yataPriceCache = {};  // { itemName: price } — raw YATA overseas prices for profit calc
let countdownTimer = null; // setInterval handle for live flight countdown
let marketValueCache = {}; // { itemName: marketValue } — Torn market value for sell price
let yataCityCache   = {}; // { cityCode: { city, country, stocks: [{id,name,qty,cost}] } } — per-city raw YATA data
let yataLastFetch   = 0;  // timestamp of last successful YATA fetch
let travelStatus    = null; // null | { traveling: bool, destination: string, time_left: number }
let abroadPlayerData = null; // full travelStatus object when abroad, includes bars/money
 
/* ─────────────────────────────────────────
   STYLESHEET — only keyframes & scrollbar
   All layout uses inline styles (CSP-safe)
───────────────────────────────────────── */
function injectCSS() {
    if (document.getElementById('lt-css')) return;
    const s = document.createElement('style');
    s.id = 'lt-css';
    s.textContent = `
#lt-toggle { position:fixed; z-index:999990; user-select:none; touch-action:none; cursor:grab; }
#lt-toggle:active { cursor:grabbing; }
#lt-panel  { position:fixed; z-index:999989; overflow:hidden; display:flex; flex-direction:column; }
#lt-panel .lt-body { overflow-y:auto; overflow-x:hidden; flex:1; scrollbar-width:thin; scrollbar-color:rgba(0,180,210,0.4) transparent; touch-action:pan-y; -webkit-overflow-scrolling:touch; }
#lt-panel .lt-body::-webkit-scrollbar { width:3px; }
#lt-panel .lt-body::-webkit-scrollbar-thumb { background:rgba(0,180,210,0.5); border-radius:2px; }
#lt-panel a { text-decoration:none !important; color:inherit; }
@keyframes lt-spin     { to{transform:rotate(360deg)} }
@keyframes lt-pulse    { 0%,100%{box-shadow:0 0 0 0 rgba(0,200,224,0.5)} 50%{box-shadow:0 0 0 5px rgba(0,200,224,0)} }
@keyframes lt-hi-pulse { 0%,100%{box-shadow:0 0 0 0 rgba(0,255,0,0.4)}   50%{box-shadow:0 0 0 3px rgba(0,255,0,0)} }
@keyframes lt-wa-pulse { 0%,100%{box-shadow:0 0 0 0 rgba(255,165,0,0.4)} 50%{box-shadow:0 0 0 3px rgba(255,165,0,0)} }
@keyframes lt-da-pulse { 0%,100%{box-shadow:0 0 0 0 rgba(255,0,0,0.4)}   50%{box-shadow:0 0 0 3px rgba(255,0,0,0)} }
@keyframes lt-float    { 0%,100%{transform:translateY(0)} 50%{transform:translateY(-2px)} }
.lt-spin   { animation:lt-spin  0.8s linear     infinite; display:inline-block; }
.lt-float  { animation:lt-float 3s   ease-in-out infinite; }
.lt-hi     { animation:lt-hi-pulse 2s ease-in-out infinite; }
.lt-wa     { animation:lt-wa-pulse 2s ease-in-out infinite; }
.lt-da     { animation:lt-da-pulse 2s ease-in-out infinite; }
`;
    (document.head || document.documentElement).appendChild(s);
}
 
/* ─────────────────────────────────────────
   TOAST
───────────────────────────────────────── */
function toast(msg, dur) {
    dur = dur || 3000;
    const old = document.getElementById('lt-toast'); if (old) old.remove();
    const el = document.createElement('div');
    el.id = 'lt-toast'; el.textContent = msg;
    el.setAttribute('style',
        'position:fixed !important;top:18px !important;left:50% !important;' +
        `transform:translateX(-50%) !important;background:${C.settBg} !important;` +
        `border:1px solid ${C.gold} !important;border-radius:8px !important;` +
        `padding:10px 20px !important;color:${C.gold} !important;font-size:13px !important;` +
        'font-weight:700 !important;font-family:Arial,sans-serif !important;' +
        'z-index:2147483647 !important;max-width:360px !important;text-align:center !important;' +
        'box-shadow:0 4px 20px rgba(0,0,0,0.8) !important;pointer-events:none !important;'
    );
    document.body.appendChild(el);
    setTimeout(() => {
        el.style.setProperty('opacity','0','important');
        el.style.setProperty('transition','opacity 0.3s','important');
        setTimeout(() => el.remove(), 320);
    }, dur);
}
 
/* ─────────────────────────────────────────
   SETTINGS POPUP — fully inline, CSP-safe
───────────────────────────────────────── */
function openSettings() {
    const existing = document.getElementById('lt-settings-wrap');
    if (existing) { existing.remove(); return; }
 
    function el(tag, css) { const e = document.createElement(tag); if (css) e.setAttribute('style', css); return e; }
    function imp(css) { return css.split(';').filter(Boolean).map(r => r.trim() + ' !important').join(';') + ';'; }
 
    const S = {
        wrap:   'position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.85);z-index:2147483647;display:flex;align-items:flex-start;justify-content:center;padding-top:5vh;box-sizing:border-box;overflow-y:auto;',
        box:    `background:${C.settBg};border:2px solid ${C.settBorder};border-radius:12px;width:370px;max-width:94vw;color:${C.text};font-family:Arial,sans-serif;overflow:hidden;box-shadow:0 16px 60px rgba(0,0,0,0.85);margin-bottom:20px;`,
        hdr:    `background:#00283a;padding:10px 14px;font-size:11px;font-weight:700;letter-spacing:1.2px;text-transform:uppercase;color:#00b7cf;border-bottom:1px solid rgba(0,180,210,0.3);`,
        body:   'padding:16px;',
        note:   `background:${C.settNote};border-left:3px solid ${C.border};padding:9px 12px;border-radius:4px;font-size:11px;color:${C.goldDim};margin-bottom:16px;line-height:1.5;`,
        secHdr: `font-size:9px;font-weight:700;letter-spacing:1.2px;text-transform:uppercase;color:${C.goldDim};padding-bottom:5px;margin:16px 0 8px;border-bottom:1px solid ${C.border};font-family:Consolas,monospace;`,
        lbl:    `display:block;margin-bottom:5px;font-size:10px;font-weight:700;letter-spacing:1px;text-transform:uppercase;color:${C.green};`,
        inp:    `display:block;width:100%;box-sizing:border-box;padding:9px 11px;background:${C.bg};border:1px solid ${C.border};border-radius:5px;color:${C.text};font-size:13px;font-family:Consolas,monospace;outline:none;`,
        hint:   `font-size:9px;color:${C.textDim};margin-top:4px;font-family:Consolas,monospace;`,
        btnRow: 'display:flex;gap:8px;margin-top:18px;',
        btn:    `flex:1;padding:10px 0;border-radius:7px;font-size:12px;font-weight:700;cursor:pointer;border:1px solid ${C.border};font-family:Arial,sans-serif;letter-spacing:0.5px;`,
    };
 
    const wrap = el('div'); wrap.id = 'lt-settings-wrap'; wrap.setAttribute('style', imp(S.wrap));
    const box  = el('div'); box.setAttribute('style', imp(S.box));
    const hdr = buildHdr('⚙  LOOT TRACKER — Settings');
    const body = el('div'); body.setAttribute('style', imp(S.body));
 
    const note = el('div');
    note.innerHTML = '&#128274; Requires a <b>Limited Access</b> API key (Display permission only). Your API key and all item data stay on your device — nothing is transmitted externally.';
    note.setAttribute('style', imp(S.note));
 
    function field(labelText, id, placeholder, value, hint, isSecret) {
        const g = el('div'); g.setAttribute('style', imp('margin-bottom:12px;'));
        const lbl = el('label'); lbl.textContent = labelText; lbl.setAttribute('style', imp(S.lbl));
        const inp = document.createElement('input');
        inp.setAttribute('type', isSecret ? 'password' : 'text');
        inp.id = id; inp.placeholder = placeholder; inp.value = value || '';
        inp.setAttribute('style', imp(S.inp));
        inp.addEventListener('focus', () => inp.style.setProperty('border-color','rgba(0,190,215,0.8)','important'));
        inp.addEventListener('blur',  () => inp.style.setProperty('border-color','rgba(0,140,170,0.4)','important'));
        if (isSecret) {
            const row = el('div'); row.setAttribute('style', imp('display:flex;align-items:center;gap:6px;'));
            const tog = el('div'); tog.textContent = '👁'; tog.setAttribute('style', imp('cursor:pointer;font-size:14px;flex-shrink:0;opacity:0.45;user-select:none;padding:2px;'));
            tog.addEventListener('click', () => {
                const shown = inp.getAttribute('type') === 'text';
                inp.setAttribute('type', shown ? 'password' : 'text');
                tog.style.setProperty('opacity', shown ? '0.45' : '1', 'important');
            });
            row.appendChild(inp); row.appendChild(tog);
            const h = el('div'); h.textContent = hint; h.setAttribute('style', imp(S.hint));
            g.appendChild(lbl); g.appendChild(row); g.appendChild(h);
        } else {
            const h = el('div'); h.textContent = hint; h.setAttribute('style', imp(S.hint));
            g.appendChild(lbl); g.appendChild(inp); g.appendChild(h);
        }
        return g;
    }
 
    // ── Theme Toggle ──
    const themeHdr = el('div'); themeHdr.textContent = '🌙 Appearance'; themeHdr.setAttribute('style', imp(S.secHdr));
    const themeRow = el('div'); themeRow.setAttribute('style', imp('display:flex;align-items:center;gap:0;margin-bottom:4px;background:' + C.bg + ';border:1px solid ' + C.border + ';border-radius:8px;overflow:hidden;'));
    const themeOpts = [
        { val: 'auto',         label: '⚙ Auto' },
        { val: 'auto-reverse', label: '↕ Reverse' },
        { val: 'light',        label: '☀️ Light' },
        { val: 'dark',         label: '🌙 Dark'  },
    ];
    const curTheme = cfg.getForceTheme();
    themeOpts.forEach(({ val, label }) => {
        const isActive = curTheme === val;
        const btn = el('button'); btn.type = 'button'; btn.textContent = label;
        btn.setAttribute('style', imp('flex:1;padding:8px 4px;border:none;border-right:1px solid ' + C.border + ';font-size:11px;font-weight:' + (isActive ? '700' : '400') + ';cursor:pointer;font-family:Arial,sans-serif;background:' + (isActive ? C.settNote : C.card) + ';color:' + (isActive ? C.gold : C.text) + ';transition:all 0.15s;'));
        btn.addEventListener('click', () => {
            cfg.setForceTheme(val);
            syncTheme(true); // force full palette + panel rebuild
            wrap.remove();
            openSettings();
        });
        themeRow.appendChild(btn);
    });
    // remove border-right from last btn
    themeRow.lastChild.style.setProperty('border-right', 'none', 'important');
    const themeHint = el('div'); themeHint.textContent = "Auto follows Torn's theme. Reverse inverts it (dark page = light script). Override with Light/Dark if detection is off.";
    themeHint.setAttribute('style', imp('font-size:9px;color:' + C.textDim + ';margin-top:4px;margin-bottom:4px;font-family:Consolas,monospace;'));

    // ── Credentials ──
    const credHdr = el('div'); credHdr.textContent = '🔑 Credentials'; credHdr.setAttribute('style', imp(S.secHdr));
    const fAPI = field('API Key (Limited — Display only)', 'lt-si-api', 'Your 16-char Torn API key', cfg.apiKey, 'Torn → Settings → API → Limited (Display access)', true);
    const fUID = field('Your User ID', 'lt-si-uid', 'Your Torn player ID', cfg.userId, 'Used to fetch your display items from the API');
 
    const apiLink = el('div');
    apiLink.setAttribute('style', imp('margin-top:-8px;margin-bottom:12px;font-size:10px;color:rgba(0,170,200,0.75);'));
    apiLink.innerHTML = `🔑 No key? <a href="https://www.torn.com/preferences.php#tab=api?step=addNewKey&title=LootTracker&access_level=1" style="color:${C.gold};font-weight:700;text-decoration:underline !important;">Create a Limited (Display) key on Torn</a>`;
 
    // ── Section Visibility ──
    const secHdr = el('div'); secHdr.textContent = '👁 Section Visibility'; secHdr.setAttribute('style', imp(S.secHdr));
 
    const vis = cfg.getSectionVis();
    const sections = [
        { key: 'prehistoric', label: '🪨 Prehistoric Points' },
        { key: 'plushies',    label: '🧸 Plushies'           },
        { key: 'flowers',     label: '🌸 Flowers'            },
        { key: 'special',     label: '☄️ Special Items'     },
        { key: 'xanax',       label: '🧪 Xanax'             },
    ];
 
    const visGrid = el('div'); visGrid.setAttribute('style', imp('display:flex;flex-direction:column;gap:4px;'));
    sections.forEach(sec => {
        const row = el('div'); row.setAttribute('style', imp(`display:flex;align-items:center;gap:8px;padding:5px 8px;border-radius:5px;background:${C.card};border:1px solid ${C.border};cursor:pointer;`));
        const chk = document.createElement('input'); chk.type = 'checkbox'; chk.id = 'lt-vis-' + sec.key; chk.checked = vis[sec.key] !== false;
        chk.setAttribute('style', imp('width:14px;height:14px;cursor:pointer;accent-color:#00c8e0;flex-shrink:0;'));
        const lbl = el('label'); lbl.textContent = sec.label; lbl.setAttribute('for', 'lt-vis-' + sec.key);
        lbl.setAttribute('style', imp(`font-size:10px;font-weight:600;color:${C.text};cursor:pointer;flex:1;`));
        row.appendChild(chk); row.appendChild(lbl);
        row.addEventListener('click', e => { if (e.target !== chk) chk.checked = !chk.checked; });
        visGrid.appendChild(row);
    });
 
    // ── Preferences ──
    const prefHdr = el('div'); prefHdr.textContent = '💡 Preferences'; prefHdr.setAttribute('style', imp(S.secHdr));
 
    const tooltipRow = el('div');
    tooltipRow.setAttribute('style', imp(`display:flex;align-items:center;gap:8px;padding:5px 8px;border-radius:5px;background:${C.card};border:1px solid ${C.border};cursor:pointer;`));
    const tooltipChk = document.createElement('input'); tooltipChk.type = 'checkbox'; tooltipChk.id = 'lt-pref-tooltip';
    let tooltipAlreadySeen = false;
    try { tooltipAlreadySeen = !!localStorage.getItem('lt_tooltip_seen'); } catch(e) {}
    tooltipChk.checked = !tooltipAlreadySeen;
    tooltipChk.setAttribute('style', imp('width:14px;height:14px;cursor:pointer;accent-color:#00c8e0;flex-shrink:0;'));
    const tooltipLbl = el('label'); tooltipLbl.textContent = '✈️ Show welcome tooltip on next load'; tooltipLbl.setAttribute('for', 'lt-pref-tooltip');
    tooltipLbl.setAttribute('style', imp(`font-size:10px;font-weight:600;color:${C.text};cursor:pointer;flex:1;`));
    tooltipRow.appendChild(tooltipChk); tooltipRow.appendChild(tooltipLbl);
    tooltipRow.addEventListener('click', e => { if (e.target !== tooltipChk) tooltipChk.checked = !tooltipChk.checked; });
 
    // ── 🧪 Xanax ──
    const xanSettHdr = el('div'); xanSettHdr.textContent = '🧪 Xanax'; xanSettHdr.setAttribute('style', imp(S.secHdr));
 
    // Adjust Count row
    const xanCarryGroup = el('div'); xanCarryGroup.setAttribute('style', imp('margin-bottom:10px;'));
    const xanCarryLbl = el('label'); xanCarryLbl.textContent = 'Carry Limit'; xanCarryLbl.setAttribute('for','lt-si-xan-carry'); xanCarryLbl.setAttribute('style', imp(S.lbl));
    const xanCarryRow = el('div'); xanCarryRow.setAttribute('style', imp('display:flex;align-items:center;gap:6px;'));
    const xanCarDecBtn = el('button'); xanCarDecBtn.textContent = '−'; xanCarDecBtn.setAttribute('type','button');
    xanCarDecBtn.setAttribute('style', imp(`padding:6px 12px;border-radius:5px;border:1px solid ${C.border};background:${C.card};color:${C.gold};font-weight:800;font-size:15px;cursor:pointer;line-height:1;`));
    const xanCarryInp = document.createElement('input'); xanCarryInp.type = 'number'; xanCarryInp.min = '0';
    xanCarryInp.id = 'lt-si-xan-carry'; xanCarryInp.value = cfg.getXanCarry();
    xanCarryInp.setAttribute('style', imp(`font-family:Consolas,monospace;font-size:14px;font-weight:700;text-align:center;width:72px;padding:6px 4px;background:${C.bg};border:1px solid ${C.border};border-radius:5px;color:${C.text};outline:none;-moz-appearance:textfield;`));
    xanCarryInp.addEventListener('focus', () => xanCarryInp.style.setProperty('border-color','rgba(0,190,215,0.8)','important'));
    xanCarryInp.addEventListener('blur',  () => xanCarryInp.style.setProperty('border-color','rgba(0,140,170,0.4)','important'));
    const xanCarIncBtn = el('button'); xanCarIncBtn.textContent = '+'; xanCarIncBtn.setAttribute('type','button');
    xanCarIncBtn.setAttribute('style', imp('padding:6px 12px;border-radius:5px;border:1px solid rgba(0,200,224,0.35);background:rgba(0,40,60,0.5);color:#00c8e0;font-weight:800;font-size:15px;cursor:pointer;line-height:1;'));
    xanCarDecBtn.addEventListener('click', () => { xanCarryInp.value = Math.max(0,(parseInt(xanCarryInp.value)||0)-1); });
    xanCarIncBtn.addEventListener('click', () => { xanCarryInp.value = (parseInt(xanCarryInp.value)||0)+1; });
    xanCarryRow.appendChild(xanCarDecBtn); xanCarryRow.appendChild(xanCarryInp); xanCarryRow.appendChild(xanCarIncBtn);
    const xanCarryHint = el('div'); xanCarryHint.textContent = 'Max items you can carry per trip (also used by Pure Profit calculator)'; xanCarryHint.setAttribute('style', imp(S.hint));
    xanCarryGroup.appendChild(xanCarryLbl); xanCarryGroup.appendChild(xanCarryRow); xanCarryGroup.appendChild(xanCarryHint);
 
 
    // ── Buttons ──
    const btnRow    = el('div'); btnRow.setAttribute('style', imp(S.btnRow));
    const btnCancel = el('button'); btnCancel.textContent = 'Cancel';       btnCancel.setAttribute('type','button');
    const btnSave   = el('button'); btnSave.textContent   = 'Save & Refresh'; btnSave.setAttribute('type','button');
    btnCancel.setAttribute('style', imp(S.btn + `background:${C.bg};color:${C.green};`));
    btnSave.setAttribute('style',   imp(S.btn + `background:${C.settNote};color:${C.gold};`));
    btnRow.appendChild(btnCancel); btnRow.appendChild(btnSave);
 
    body.appendChild(note);
    body.appendChild(themeHdr);
    body.appendChild(themeRow);
    body.appendChild(themeHint);
    body.appendChild(credHdr);
    body.appendChild(fAPI);
    body.appendChild(apiLink);
    body.appendChild(fUID);
    body.appendChild(secHdr);
    body.appendChild(visGrid);
    body.appendChild(xanSettHdr);
    body.appendChild(xanCarryGroup);
 
    // ── Museum Day toggle ──
    const museumSettHdr = el('div'); museumSettHdr.textContent = '🏛️ Museum Day'; museumSettHdr.setAttribute('style', imp(S.secHdr));
    const museumPinRow = el('div'); museumPinRow.setAttribute('style', imp('display:flex;align-items:center;gap:8px;padding:5px 8px;border-radius:5px;background:rgba(255,184,48,0.04);border:1px solid rgba(255,184,48,0.18);cursor:pointer;margin-bottom:8px;'));
    const museumPinChk = document.createElement('input'); museumPinChk.type = 'checkbox'; museumPinChk.id = 'lt-museum-pin';
    museumPinChk.checked = cfg.getMuseumPin();
    museumPinChk.setAttribute('style', imp('width:14px;height:14px;cursor:pointer;accent-color:#ffb830;flex-shrink:0;'));
    const museumPinLbl = el('label'); museumPinLbl.textContent = '🏛️ Always show Museum Day bonus'; museumPinLbl.setAttribute('for', 'lt-museum-pin');
    museumPinLbl.setAttribute('style', imp(`font-size:10px;font-weight:600;color:${C.text};cursor:pointer;flex:1;`));
    museumPinRow.appendChild(museumPinChk); museumPinRow.appendChild(museumPinLbl);
    museumPinRow.addEventListener('click', e => { if (e.target !== museumPinChk) museumPinChk.checked = !museumPinChk.checked; });
    body.appendChild(museumSettHdr);
    body.appendChild(museumPinRow);


    // ── 💡 Preferences ──
    const prefHdr2 = el('div'); prefHdr2.textContent = '💡 Tooltip Carousel'; prefHdr2.setAttribute('style', imp(S.secHdr));
    body.appendChild(prefHdr2);

    body.appendChild(tooltipRow);
    body.appendChild(btnRow);
    box.appendChild(hdr); box.appendChild(body);
    wrap.appendChild(box);
    document.body.appendChild(wrap);
 
    setTimeout(() => { const i = document.getElementById('lt-si-api'); if (i) i.focus(); }, 50);
 
    function doSave() {
        const key = (document.getElementById('lt-si-api').value || '').trim();
        const uid = (document.getElementById('lt-si-uid').value || '').trim();
        if (!key) { toast('⚠ API key is required'); return; }
        cfg.apiKey = key; cfg.userId = uid;
        const newVis = {};
        sections.forEach(sec => { newVis[sec.key] = document.getElementById('lt-vis-' + sec.key).checked; });
        cfg.setSectionVis(newVis);
        // Xanax carry, priority, threshold
        cfg.setXanCarry(Math.max(0, parseInt(document.getElementById('lt-si-xan-carry').value)||0));

        // Retrigger tooltip: clear seen key so carousel shows again on next load
        const showTooltip   = document.getElementById('lt-pref-tooltip').checked;
            const museumPin     = document.getElementById('lt-museum-pin').checked;
            cfg.setMuseumPin(museumPin);

        try {
            if (showTooltip) localStorage.removeItem('lt_tooltip_seen');
            else             localStorage.setItem('lt_tooltip_seen', '1');
        } catch(e) {}
        wrap.remove();
        toast('✓ Saved!');
        invCache = {}; abroadCache = {}; xanSACache = { qty:0, price:0 }; xanFacCache = null; xanPersonal = 0; // bobCache intentionally kept — shows last known BoB stock while refreshing
        if (panelEl) renderPanel();
        refreshAll();
    }
    btnSave.addEventListener('click', doSave);
    btnCancel.addEventListener('click', () => wrap.remove());
    wrap.addEventListener('click', e => { if (e.target === wrap) wrap.remove(); });
    wrap.addEventListener('keydown', e => { if (e.key === 'Escape') wrap.remove(); });
}
 
/* ─────────────────────────────────────────
   API / FETCH
───────────────────────────────────────── */
function gmFetch(url, timeoutMs) {
    timeoutMs = timeoutMs || 12000;
    const req = new Promise(resolve => {
        if (typeof GM_xmlhttpRequest !== 'undefined') {
            GM_xmlhttpRequest({
                method: 'GET', url, timeout: timeoutMs,
                onload:    r => { try { resolve(JSON.parse(r.responseText)); } catch { resolve({}); } },
                onerror:   () => resolve({}),
                ontimeout: () => resolve({}),
            });
        } else {
            fetch(url).then(r => r.json()).then(resolve).catch(() => resolve({}));
        }
    });
    return Promise.race([req, new Promise(r => setTimeout(() => r({}), timeoutMs))]);
}
 
async function fetchInventory() {
    if (!cfg.apiKey || !cfg.userId) throw new Error('No API key or User ID');
    const uid = parseInt(String(cfg.userId).replace(/\D/g,''));
    const d = await gmFetch(`https://api.torn.com/user/${uid}?selections=display&key=${cfg.apiKey}`);
    if (d.error) throw new Error(d.error.error || 'API error');
    const items = {};
    (d.display || []).forEach(item => { items[item.name] = (items[item.name] || 0) + item.quantity; });
    return items;
}
 
async function fetchTravelStatus() {
    if (!cfg.apiKey || !cfg.userId) return null;
    try {
        const uid = parseInt(String(cfg.userId).replace(/\D/g,''));
        // travel selection returns: { destination, time_left, departed, enroute }
        // destination = "South Africa" (outbound) or "Torn" (returning)
        // time_left   = seconds remaining
        // departed    = unix timestamp of departure
        // travel+basic via v1; bars+money fetched separately via v2 when abroad
        const d = await gmFetch(`https://api.torn.com/user/?selections=travel,basic&key=${cfg.apiKey}`);
        if (d.error || !d.travel) return null;

        const t           = d.travel;
        const destination = (t.destination || '').trim();
        const timeLeft    = Number(t.time_left  || 0);
        const departed    = Number(t.departed   || 0);

        // status.state = "Traveling" (in flight) | "Abroad" (landed, not flying) | "Okay" (home)
        const state    = (d.status && d.status.state) ? d.status.state : '';
        const isAbroad = state === 'Abroad';

        // Abroad: player is landed in a foreign country, not in flight
        if (isAbroad) {
            // Return minimal abroad marker — bars/money fetched separately via v2
            return {
                traveling: false, abroad: true, destination,
                timeLeft: 0, departed: 0, isReturn: false, origin: '', state,
                playerName: d.name  || '',
                playerLevel:d.level || 0,
            };
        }

        if (!timeLeft) return { traveling: false, destination: '', timeLeft: 0, departed: 0, isReturn: false, origin: '', state };

        const isReturn = !TORN_DEST_TO_CODE[destination];

        // For return flights the API only says destination="Torn".
        // Try status.description which says e.g. "Returning from South Africa"
        let origin = '';
        if (isReturn) {
            const desc = (d.status && d.status.description) ? d.status.description : '';
            // "Returning from South Africa" or "Traveling to Torn" etc
            const m = desc.match(/from\s+([A-Za-z ]+)/i);
            if (m) {
                const fromCountry = m[1].trim();
                // Validate it's a known country
                if (TORN_DEST_TO_CODE[fromCountry]) origin = fromCountry;
            }
            // If still empty, scrapeTravelPage will fill it in from the DOM
        } else {
            origin = 'Torn City';
        }

        console.log('[SetsTracker] travel API:', destination, 'timeLeft:', timeLeft, 'isReturn:', isReturn, 'origin:', origin, 'state:', state);
        return { traveling: true, abroad: false, destination, timeLeft, departed, isReturn, origin, state };
    } catch(e) { console.warn('[SetsTracker] fetchTravelStatus threw:', e); return null; }
}

/* ─────────────────────────────────────────
   AUTO-FILL CARRY QTY IN TORN SHOP INPUTS
   When abroad, finds Torn's shop qty input fields and pre-fills
   them with the player's carry limit setting.
───────────────────────────────────────── */
function fillCarryQty() {
    if (!abroadCountry) return;
    const carry = String(cfg.getXanCarry() || 1);
    try {
        const doc2 = (typeof unsafeWindow !== 'undefined' && unsafeWindow.document) ? unsafeWindow.document : document;
        const inputs = doc2.querySelectorAll('input[placeholder="Qty"]');
        let filled = 0;
        inputs.forEach(function(inp) {
            // Only fill if not already set to carry value
            if (inp.value === carry) return;
            try {
                const setter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value') &&
                               Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value').set;
                if (setter) { setter.call(inp, carry); }
                else        { inp.value = carry; }
                inp.dispatchEvent(new Event('input',  { bubbles: true }));
                inp.dispatchEvent(new Event('change', { bubbles: true }));
                filled++;
            } catch(e) {}
        });
        if (filled > 0) console.log('[SetsTracker] fillCarryQty: filled', filled, 'inputs with carry:', carry);
    } catch(e) { console.warn('[SetsTracker] fillCarryQty threw:', e); }
}

/* ─────────────────────────────────────────
   SCRAPE COUNTRY FLAG IMAGES FROM TRAVEL PAGE
   Runs once on the travel page. Tries desktop pin___  divs first,
   then TornPDA flagAndName divs. Caches img src by country name.
───────────────────────────────────────── */
function scrapeCountryFlags() {
    if (!document.location.href.includes('travel')) return;
    try {
        const doc2 = (typeof unsafeWindow !== 'undefined' && unsafeWindow.document) ? unsafeWindow.document : document;
        let found = 0;

        // ── Desktop: pins use CSS background-image — scrape the URL from style attr ──
        const pins = doc2.querySelectorAll('div[class*="pin___"]');
        pins.forEach(pin => {
            const bgImg = pin.style.backgroundImage || pin.getAttribute('style') || '';
            const urlMatch = bgImg.match(/url\(["']?([^"')]+)["']?\)/);
            if (!urlMatch) return;
            const rawUrl = urlMatch[1];
            const fileMatch = rawUrl.match(/pinpoints_([^./]+)\.svg/i);
            if (!fileMatch) return;
            const slug = fileMatch[1].toLowerCase();
            const slugMap = {
                torn:'Torn City', mexico:'Mexico', hawaii:'Hawaii',
                south_africa:'South Africa', japan:'Japan', china:'China',
                argentina:'Argentina', switzerland:'Switzerland', canada:'Canada',
                uk:'UK', uae:'UAE', cayman_islands:'Cayman Islands',
            };
            const cName = slugMap[slug];
            if (cName) {
                const absUrl = rawUrl.startsWith('http') ? rawUrl : 'https://www.torn.com' + rawUrl;
                countryFlagCache[cName] = absUrl;
                if (cName === 'UK') countryFlagCache['United Kingdom'] = absUrl;
                found++;
            }
        });

        // ── TornPDA: flagAndName divs — img child ──
        if (found === 0) {
            const cells = doc2.querySelectorAll('div[class*="flagAndName"]');
            cells.forEach(cell => {
                const img = cell.querySelector('img');
                if (!img || !img.src) return;
                const fullText = (cell.textContent || '').trim();
                for (const cName of Object.keys(LOCATIONS)) {
                    if (fullText.toLowerCase().includes(cName.toLowerCase())) {
                        countryFlagCache[cName] = img.src;
                        found++;
                        break;
                    }
                }
            });
        }

        if (found > 0) {
            cfg.setFlagCache(countryFlagCache);
            console.log('[SetsTracker] Scraped', found, 'country flag images from travel page');
            if (panelEl) renderPanel(); // refresh to show real images
        }
    } catch(e) { console.warn('[SetsTracker] scrapeCountryFlags threw:', e); }
}

function scrapeTravelPage() {
    if (!document.location.href.includes('travel')) return;
    try {
        const raw = (document.body.innerText || '').replace(/[\r\n\t]+/g, ' ').replace(/ {2,}/g, ' ');
        const timeMatch = raw.match(/Remaining Flight Time\s*[-\u2013]\s*(\d+):(\d+):(\d+)/i);
        if (!timeMatch) return;
        const secs = parseInt(timeMatch[1])*3600 + parseInt(timeMatch[2])*60 + parseInt(timeMatch[3]);
        if (!secs) return;

        const landMatch  = raw.match(/Landing at\s+(\d+:\d+(?::\d+)?\s*(?:AM|PM)?)/i);
        const landingStr = landMatch ? landMatch[1].trim() : (travelStatus ? travelStatus.landingStr || '' : '');

        // "CityName to Torn" → return flight, origin = CityName
        const retMatch = raw.match(/([A-Za-z][A-Za-z \-']+?)\s+to\s+Torn\b/i);
        const scrapedOriginCity = retMatch ? retMatch[1].trim().replace(/^traveling\s+/i, '').trim() : '';

        // "Torn to CityName" → outbound flight, destination = CityName
        const outMatch = raw.match(/Torn\s+to\s+([A-Za-z][A-Za-z \-']+?)\s*[\.\/\-]/i);
        const scrapedDestCity = outMatch ? outMatch[1].trim() : '';
        // Resolve city name → country name for API matching
        const scrapedDestCountry = scrapedDestCity
            ? (Object.keys(TORN_DEST_TO_CODE).find(k => YATA_CODE_TO_CITY[TORN_DEST_TO_CODE[k]] === scrapedDestCity) || scrapedDestCity)
            : '';

        if (travelStatus && travelStatus.traveling) {
            // Patch return flight origin from DOM
            const betterOrigin = (travelStatus.isReturn && scrapedOriginCity && (!travelStatus.origin || travelStatus.origin === 'Torn' || travelStatus.origin === 'Torn City'))
                ? scrapedOriginCity
                : travelStatus.origin;
            // Patch outbound destination if API gave us empty/wrong destination
            const betterDest = (!travelStatus.isReturn && scrapedDestCountry && !TORN_DEST_TO_CODE[travelStatus.destination])
                ? scrapedDestCountry
                : travelStatus.destination;
            travelStatus = { ...travelStatus, timeLeft: secs, landingStr, origin: betterOrigin, destination: betterDest };
        } else if (secs > 0) {
            // API hasn't loaded yet — bootstrap from scrape
            const toIsHome = !!scrapedOriginCity;
            travelStatus = {
                traveling:   true,
                destination: toIsHome ? 'Torn City' : scrapedDestCountry,
                timeLeft:    secs,
                departed:    0,
                isReturn:    toIsHome,
                origin:      scrapedOriginCity || 'Torn City',
                landingStr,
            };
        }
        if (panelEl) renderPanel();
        manageTravelCountdown();
    } catch(e) { console.warn('[SetsTracker] scrapeTravelPage threw:', e); }
}


function scrapeXanaxFromItemsPage() {
    // Only scrape if we're on the items page
    if (!window.location.href.includes('item')) return;
    try {
        // Torn items page renders item names and quantities in the DOM
        // Look for any element containing "Xanax" and grab the adjacent quantity
        const allText = document.querySelectorAll('[class*="name"],[class*="title"],[class*="item"]');
        for (const el of allText) {
            if (el.textContent.trim() !== 'Xanax') continue;
            // Try siblings and parent children for quantity
            const parent = el.closest('[class*="item"],[class*="row"],[class*="wrap"]') || el.parentElement;
            if (!parent) continue;
            const qtyEl = parent.querySelector('[class*="qty"],[class*="amount"],[class*="quantity"],[class*="count"]');
            if (qtyEl) {
                const qty = parseInt(qtyEl.textContent.replace(/[^0-9]/g,'')) || 0;
                if (qty > 0) {
                    cfg.setXanCount(qty);
                    xanPersonal = qty;
                    console.log('[SetsTracker] scraped xanax from items page:', qty);
                    if (panelEl) renderPanel();
                    return;
                }
            }
            // Fallback: look for a number near the Xanax text
            const nearby = parent.textContent.match(/[xX](?:\s*)(\d+)|quantity[:\s]*(\d+)|(\d+)\s*[xX]/);
            if (nearby) {
                const qty = parseInt(nearby[1] || nearby[2] || nearby[3]) || 0;
                if (qty > 0) {
                    cfg.setXanCount(qty);
                    xanPersonal = qty;
                    console.log('[SetsTracker] scraped xanax (fallback):', qty);
                    if (panelEl) renderPanel();
                    return;
                }
            }
        }
    } catch(e) { console.warn('[SetsTracker] scrapeXanaxFromItemsPage threw:', e); }
}
 
function watchItemsPage() {
    // Watch for DOM changes on items page to trigger scrape
    if (!window.location.href.includes('item')) return;
    console.log('[SetsTracker] on items page — scraping xanax count');
    // Give the page time to render items
    setTimeout(scrapeXanaxFromItemsPage, 1500);
    setTimeout(scrapeXanaxFromItemsPage, 3000);
}
 
async function fetchYataData() {
    // Single YATA fetch — populates both abroadCache and xanSACache
    // YATA: { stocks: { "mex": { stocks: [{id, name, quantity, cost}] }, "sou": {...} } }
    const map = {};
    let sa = { qty: 0, price: 0 };
    try {
        const data = await gmFetch('https://yata.yt/api/v1/travel/export/');
        if (!data || !data.stocks) return { map, sa };
        Object.entries(data.stocks).forEach(([code, country]) => {
            const isSA   = code === 'sou';
            const city   = YATA_CODE_TO_CITY[code] || code;
            const stocks = [];
            (country.stocks || []).forEach(item => {
                const name = ID_TO_NAME[Number(item.id)];
                const qty  = Number(item.quantity || 0);
                const cost = Number(item.cost || 0);
                if (name) {
                    map[name] = (map[name] || 0) + qty;
                    if (cost > 0 && (!yataPriceCache[name] || cost > yataPriceCache[name].price)) {
                        yataPriceCache[name] = { price: cost, country: country.country_name || code };
                    }
                    if (qty >= 0) cfg.addStockHistory(name + '_' + code, qty);
                    stocks.push({ id: Number(item.id), name, qty, cost });
                }
                if (isSA && Number(item.id) === XANAX_ID) {
                    sa = { qty, price: Number(item.cost || 0) };
                }
            });
            yataCityCache[code] = { city, code, stocks };
        });
        yataLastFetch = Date.now();
        console.log('[SetsTracker] YATA loaded — countries:', Object.keys(data.stocks).length, '| SA xanax qty:', sa.qty, 'price:', sa.price);
    } catch(e) { console.warn('[SetsTracker] fetchYataData threw:', e); }
    return { map, sa };
}
 
// fetchAbroad and fetchXanaxSA are handled by a single fetchYataData() call in refreshAll()
 
async function fetchBobStock() {
    // torn/?selections=cityshops — confirmed working
    // Shop name: "Bits 'n' Bobs" (id 103)
    // Items absent when out of stock, present with in_stock count when available
    if (!cfg.apiKey) return {};
    const bobMap = {};
    try {
        const data = await gmFetch(`https://api.torn.com/torn/?selections=cityshops&key=${cfg.apiKey}`);
        if (!data || data.error) { console.warn('[SetsTracker] fetchBobStock:', data && data.error ? data.error.error : 'no data'); return bobMap; }
        const shops = data.cityshops || {};
        Object.values(shops).forEach(shop => {
            const n = (shop.name || '').toLowerCase();
            if (!n.includes('bit') && !n.includes('bob')) return;
            const inv = shop.inventory || {};
            // Map all items we know about — plushies will appear here when in stock
            Object.entries(inv).forEach(([idStr, item]) => {
                const name = ID_TO_NAME[Number(idStr)];
                if (name) bobMap[name] = Number(item.in_stock || 0);
            });
            // Explicitly zero out BoB plushies not in the response (= out of stock)
            [186, 187, 215].forEach(id => {
                const name = ID_TO_NAME[id];
                if (name && bobMap[name] === undefined) bobMap[name] = 0;
            });
        });
        // Record BoB stock history (Torn API source, keyed with 'bob' suffix)
        Object.entries(bobMap).forEach(([name, qty]) => { cfg.addStockHistory(name + '_bob', qty); });
        console.log('[SetsTracker] BoB stock:', JSON.stringify(bobMap));
    } catch(e) { console.warn('[SetsTracker] fetchBobStock threw:', e); }
    return bobMap;
}
 
async function fetchXanaxFaction() {
    if (!cfg.apiKey) return null;
    try {
        // faction/?selections=drugs — confirmed working with Full Access key
        // Returns: { drugs: [ { ID, name, type, quantity }, ... ] }
        const data = await gmFetch(`https://api.torn.com/faction/?selections=drugs&key=${cfg.apiKey}`);
        if (!data || data.error) { console.warn('[SetsTracker] fetchXanaxFaction:', data && data.error ? JSON.stringify(data.error) : 'no data'); return null; }
        const drugs = Array.isArray(data.drugs) ? data.drugs : Object.values(data.drugs || {});
        const xan   = drugs.find(d => Number(d.ID || d.id) === XANAX_ID);
        if (xan) {
            console.log('[SetsTracker] faction Xanax qty:', xan.quantity);
            return Number(xan.quantity || 0);
        }
        console.log('[SetsTracker] faction drugs found but no Xanax. IDs:', drugs.map(d => d.ID || d.id));
        return 0;
    } catch(e) { console.warn('[SetsTracker] fetchXanaxFaction threw:', e); }
    return null;
}
 
async function fetchPointsPrice() {
    const now = Date.now();
    if (pointsPriceCache.time && now - pointsPriceCache.time < POINTS_CACHE_DUR) return pointsPriceCache.price;
    if (!cfg.apiKey) return 0;
    try {
        const data = await gmFetch(`${POINTS_ENDPOINT}?key=${cfg.apiKey}`);
        if (data.pointsmarket) {
            const listings = Object.values(data.pointsmarket).filter(l => l.quantity > 0).map(l => l.cost).sort((a,b) => a-b);
            if (listings.length) {
                const top = listings.slice(0, Math.min(5, listings.length));
                const avg = Math.round(top.reduce((s,p) => s+p, 0) / top.length);
                pointsPriceCache.history.push(avg);
                if (pointsPriceCache.history.length > POINTS_HIST_SIZE) pointsPriceCache.history.shift();
                const stable = Math.round(pointsPriceCache.history.reduce((s,p) => s+p, 0) / pointsPriceCache.history.length);
                pointsPriceCache = { time: now, price: stable, history: pointsPriceCache.history };
                pointsPrice = stable;
                return stable;
            }
        }
    } catch(e) {}
    return pointsPrice || 0;
}
 
/* ─────────────────────────────────────────
   FETCH MARKET VALUES
   Torn v2 market endpoint — batch all tracked item IDs in one call
   Returns { itemName: lowestMarketPrice }
───────────────────────────────────────── */
async function fetchMarketValues() {
    if (!cfg.apiKey) return {};
    // Collect all item IDs we care about (excluding BoB-only items which aren't overseas)
    const ids = [];
    Object.values(GROUPS).forEach(g => Object.entries(g.items).forEach(([, d]) => {
        if (!BOB_IDS.has(d.id)) ids.push(d.id);
    }));
    Object.values(SPECIAL_ITEMS).forEach(d => ids.push(d.id));
    if (!ids.includes(XANAX_ID)) ids.push(XANAX_ID); // include Xanax for sell value

    const result = {};
    try {
        // Torn v1: /torn/[id1,id2,...]?selections=items
        // Returns { items: { "id": { name, market_value, ... } } }
        // market_value is Torn's own calculated market price — reliable single call
        const data = await gmFetch(
            `https://api.torn.com/torn/${ids.join(',')}?selections=items&key=${cfg.apiKey}`,
            20000
        );
        const items = data.items || {};
        Object.entries(items).forEach(([idStr, item]) => {
            const name = ID_TO_NAME[Number(idStr)];
            if (!name) return;
            const mv = Number(item.market_value || 0);
            if (mv > 0) result[name] = mv;
        });
        console.log('[SetsTracker] market values fetched:', Object.keys(result).length, 'items');
        // Record history for sparklines
        Object.entries(result).forEach(([name, price]) => { if (price > 0) cfg.addPriceHistory(name, price); });
    } catch(e) { console.warn('[SetsTracker] fetchMarketValues threw:', e); }
    return result;
}
function calcSet(inv, items) {
    const vals = Object.keys(items).map(k => inv[k] || 0);
    return vals.length ? Math.min(...vals) : 0;
}
 
function getSortedItems(inv, items, sets) {
    return Object.entries(items)
        .map(([name, data]) => ({ name, data, remaining: (inv[name] || 0) - sets }))
        .sort((a, b) => a.remaining - b.remaining);
}
 
function getBottleneck(inv, items, sets) {
    const sorted = getSortedItems(inv, items, sets).filter(i => i.remaining < 5);
    if (!sorted.length) return null;
    const parts = sorted.map(i => {
        const locLabel = LOCATIONS[i.data.loc]?.label || i.data.loc;
        return `${i.data.s} → ${locLabel}`;
    });
    return 'Need ' + parts.join(' & ');
}
 
function stockClass(name, qty) {
    if (qty === 0) return 'lt-da';
    if (GROUPS.Plushies.items[name]) return qty >= PLU_THRESH ? 'lt-hi' : 'lt-wa';
    if (GROUPS.Flowers.items[name])  return qty >= FLO_THRESH ? 'lt-hi' : 'lt-wa';
    return qty > 0 ? 'lt-hi' : 'lt-da';
}
 
function stockColor(name, qty) {
    if (qty === 0) return C.stockLo;
    if (GROUPS.Plushies.items[name]) return qty >= PLU_THRESH ? C.stockHi : C.stockMid;
    if (GROUPS.Flowers.items[name])  return qty >= FLO_THRESH ? C.stockHi : C.stockMid;
    return qty > 0 ? C.stockHi : C.stockLo;
}
 
/* ─────────────────────────────────────────
   RENDER HELPERS
───────────────────────────────────────── */
function makeEmpty(icon, msg) {
    const el = document.createElement('div');
    el.style.cssText = `padding:28px 16px;text-align:center;color:${C.textDim};font-size:11px;line-height:1.6;font-family:Arial,sans-serif;`;
    el.innerHTML = `<div style="font-size:26px;margin-bottom:8px;">${icon}</div>${msg}`;
    return el;
}
 
function makeSectionLabel(text, setCount, pts) {
    const el = document.createElement('div');
    el.style.cssText = `display:flex;justify-content:space-between;align-items:center;padding:4px 10px;font-size:9px;font-weight:700;letter-spacing:1px;text-transform:uppercase;color:${C.goldDim};background:${C.goldGlow};border-top:1px solid ${C.border};border-bottom:1px solid ${C.border};font-family:Consolas,monospace;`;
    const left = document.createElement('span'); left.textContent = text;
    const right = document.createElement('span');
    right.style.cssText = `color:${C.gold};font-weight:700;font-size:8.5px;`;
    right.textContent = setCount !== undefined ? `${setCount} sets · ${setCount * pts} pts` : '';
    el.appendChild(left); el.appendChild(right);
    return el;
}
 
function makeAlertRow(msg) {
    const el = document.createElement('div');
    const _aL = typeof isLightMode === 'function' && isLightMode();
    el.style.cssText = `margin:3px 8px;padding:5px 8px 5px 20px;position:relative;background:${_aL?'rgba(140,0,0,0.07)':'rgba(160,20,20,0.18)'};border-left:2px solid ${_aL?'rgba(160,0,0,0.55)':'rgba(220,50,50,0.8)'};border-radius:3px;font-size:9.5px;font-weight:600;color:${_aL?'#7a0000':'#ff8888'};line-height:1.3;font-family:Arial,sans-serif;`;
    el.innerHTML = `<span style="position:absolute;left:6px;top:50%;transform:translateY(-50%);font-size:9px;opacity:0.9;color:${_aL?'#7a0000':'#ff6666'};">!</span>${msg}`;
    return el;
}
 
/* ─────────────────────────────────────────
   ITEM ROW BUILDER
───────────────────────────────────────── */

// Render a country flag — real flag image (flagcdn.com) with emoji fallback
// flagcdn.com serves individual country flag PNGs, works on all platforms including Windows
function makeFlagEl(countryName, sizePx) {
    sizePx = sizePx || 22;
    const loc    = LOCATIONS[countryName] || { flag: '❓', label: countryName };
    const imgUrl = FLAG_IMGS[countryName];
    if (imgUrl) {
        const img = document.createElement('img');
        img.src   = imgUrl;
        img.alt   = loc.label || countryName;
        img.title = loc.label || countryName;
        const isTornCity = countryName === 'Torn City';
        // Flags: rectangular (1.4:1 ratio, cover). Torn City favicon: square circle (contain).
        const imgW = isTornCity ? Math.round(sizePx * 0.85) : Math.round(sizePx * 1.25);
        const imgH = isTornCity ? Math.round(sizePx * 0.85) : Math.round(sizePx * 0.85);
        const fit  = isTornCity ? 'contain' : 'cover';
        const rad  = isTornCity ? '50%' : '2px';
        img.style.cssText = `width:${imgW}px;height:${imgH}px;object-fit:${fit};border-radius:${rad};flex-shrink:0;display:inline-block;vertical-align:middle;`;
        img.addEventListener('error', function() {
            const span = document.createElement('span');
            span.textContent = loc.flag || '?';
            span.title = loc.label || countryName;
            span.style.cssText = `font-size:${Math.round(sizePx * 0.75)}px;line-height:1;flex-shrink:0;`;
            img.replaceWith(span);
        });
        return img;
    }
    // BoB and unknowns — emoji/symbol
    const span = document.createElement('span');
    span.textContent = loc.flag || '?';
    span.title = loc.label || countryName;
    span.style.cssText = `font-size:${Math.round(sizePx * 0.75)}px;line-height:1;flex-shrink:0;`;
    return span;
}

function makeTornCityEl(sizePx) {
    return makeFlagEl('Torn City', sizePx || 22);
}

// Format a history array's real time span for sparkline labels
function fmtHistorySpan(history) {
    if (history.length < 2) return history.length + ' fetch' + (history.length === 1 ? '' : 'es');
    const ms  = history[history.length - 1].ts - history[0].ts;
    const mins = Math.round(ms / 60000);
    if (mins < 60)  return '~' + mins + 'm';
    const hrs = Math.floor(mins / 60);
    const rem = mins % 60;
    if (hrs < 24)   return rem > 0 ? '~' + hrs + 'h ' + rem + 'm' : '~' + hrs + 'h';
    const days = Math.floor(hrs / 24);
    const rh   = hrs % 24;
    return rh > 0 ? '~' + days + 'd ' + rh + 'h' : '~' + days + 'd';
}
function makeItemRow(name, data, remaining, abroadQty, isBob) {
    const row = document.createElement('div');
    row.style.cssText = `display:grid;grid-template-columns:34px 34px 36px 1fr;gap:6px;align-items:center;padding:4px 8px;border-bottom:1px solid rgba(0,80,100,0.2);min-height:38px;transition:background 0.15s;`;
    row.addEventListener('mouseover', () => row.style.background = 'rgba(0,200,224,0.05)');
    row.addEventListener('mouseout',  () => row.style.background = 'transparent');
 
    // col 1 — item image
    const imgWrap = document.createElement('div');
    imgWrap.style.cssText = 'position:relative;width:32px;height:32px;';
    const img = document.createElement('img');
    img.src = itemImg(data.id); img.alt = data.s; img.title = name;
    img.style.cssText = `width:30px;height:30px;object-fit:contain;border-radius:2px;background:${C.card};border:1px solid ${C.cardBorder};display:block;transition:transform 0.2s;`;
    img.addEventListener('mouseover', () => img.style.transform = 'scale(1.15)');
    img.addEventListener('mouseout',  () => img.style.transform = 'scale(1)');
    const imgFallback = document.createElement('span');
    imgFallback.style.cssText = 'display:none;width:30px;height:30px;font-size:7px;font-weight:700;text-align:center;line-height:30px;border-radius:2px;border:1px solid rgba(255,255,255,0.1);background:rgba(255,255,255,0.05);color:#c0e0ff;font-family:Consolas,monospace;';
    imgFallback.textContent = data.s;
    img.addEventListener('error', () => { img.style.display = 'none'; imgFallback.style.display = 'block'; });
    imgWrap.appendChild(img); imgWrap.appendChild(imgFallback);
 
    // col 2 — own count (remaining after sets)
    const ownEl = document.createElement('div');
    ownEl.style.cssText = `color:${C.green};background:rgba(0,180,210,0.08);font-weight:700;text-align:center;border:1px solid rgba(0,180,210,0.15);font-family:Consolas,monospace;padding:2px 3px;border-radius:2px;font-size:10px;`;
    ownEl.title = `You have ${remaining} extra after completing sets`;
    ownEl.textContent = remaining;
 
    // col 3 — abroad / BoB button
    let abroadEl;
    if (isBob) {
        const bobQty = bobCache[name] !== undefined ? bobCache[name] : null;
        const bobCol = bobQty === null ? C.gold : bobQty > 0 ? C.okay : C.textDim;
        abroadEl = document.createElement('a');
        abroadEl.href = 'https://www.torn.com/shops.php?step=bitsnbobs';
        abroadEl.style.cssText = `display:flex;align-items:center;justify-content:center;font-size:9px;font-weight:700;color:${bobCol};background:${bobCol}1a;border:1px solid ${bobCol}44;border-radius:3px;padding:2px 4px;cursor:pointer;white-space:nowrap;text-decoration:none !important;transition:all 0.15s;font-family:Consolas,monospace;`;
        abroadEl.textContent = bobQty !== null ? '🏪 ' + bobQty : '🏪 BoB';
        abroadEl.title = bobQty !== null ? "Bits n' Bobs stock: " + bobQty + "\nClick to open shop" : "Open Bits n' Bobs shop";
        abroadEl.addEventListener('mouseover', () => { abroadEl.style.background = bobCol + '33'; abroadEl.style.borderColor = bobCol + '88'; });
        abroadEl.addEventListener('mouseout',  () => { abroadEl.style.background = bobCol + '1a'; abroadEl.style.borderColor = bobCol + '44'; });
    } else {
        const sc = stockClass(name, abroadQty);
        const col = stockColor(name, abroadQty);
        abroadEl = document.createElement('div');
        abroadEl.className = sc;
        abroadEl.style.cssText = `color:${col};background:${col}1a;font-weight:700;text-align:center;border:1px solid ${col}44;font-family:Consolas,monospace;padding:2px 3px;border-radius:2px;font-size:10px;transition:all 0.3s;text-shadow:0 0 4px ${col}66;`;
        abroadEl.title = `Overseas stock (Torn API): ${abroadQty}`;
        abroadEl.textContent = abroadQty;
    }
 
    // col 4 — item name + flag/location
    const loc    = LOCATIONS[data.loc] || { flag: '❓', label: data.loc };
    const infoEl = document.createElement('div');
    infoEl.style.cssText = 'display:flex;flex-direction:column;justify-content:center;min-width:0;gap:2px;';

    const nameSpan = document.createElement('div');
    nameSpan.textContent = name;
    nameSpan.style.cssText = `font-size:9px;font-weight:700;color:${C.text};font-family:Arial,sans-serif;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;line-height:1.2;`;

    const locRow = document.createElement('div');
    locRow.style.cssText = 'display:flex;align-items:center;gap:3px;';
    const flagSmall = makeFlagEl(data.loc, 12);
    const locSpan = document.createElement('span');
    locSpan.textContent = loc.label;
    locSpan.style.cssText = `font-size:7.5px;color:${C.textDim};font-family:Consolas,monospace;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;`;
    locRow.appendChild(flagSmall); locRow.appendChild(locSpan);

    infoEl.appendChild(nameSpan); infoEl.appendChild(locRow);

    row.appendChild(imgWrap);
    row.appendChild(ownEl);
    row.appendChild(abroadEl);
    row.appendChild(infoEl);
    return row;
}
 
/* ─────────────────────────────────────────
   EXPANDABLE ITEM DETAIL PANEL
   Wraps a makeItemRow with a tap-to-expand detail section
   showing all known data: ID, buy/sell, profit, stock, history sparkline
───────────────────────────────────────── */
function makeExpandableItem(name, data, remaining, abroadQty, isBob) {
    const wrap = document.createElement('div');
    wrap.style.cssText = 'border-bottom:1px solid rgba(0,80,100,0.2);';

    const row = makeItemRow(name, data, remaining, abroadQty, isBob);
    // Remove border-bottom from inner row since wrap handles it
    row.style.borderBottom = 'none';
    row.style.cursor = 'pointer';

    // ── Chevron indicator ──
    const chevron = document.createElement('span');
    chevron.style.cssText = `position:absolute;right:6px;top:50%;transform:translateY(-50%) rotate(0deg);font-size:9px;color:${C.textDim};transition:transform 0.18s;pointer-events:none;`;
    chevron.textContent = '▾';
    row.style.position = 'relative';
    row.style.paddingRight = '18px';
    row.appendChild(chevron);

    // ── Detail panel ──
    let isOpen = false;
    const detail = document.createElement('div');
    detail.style.cssText = `overflow:hidden;max-height:0;transition:max-height 0.22s ease;background:${C.bg};`;

    const inner = document.createElement('div');
    inner.style.cssText = 'padding:8px 10px;display:flex;flex-direction:column;gap:6px;';

    // Populate detail lazily on first open
    let populated = false;
    function populateDetail() {
        if (populated) return; populated = true;
        const buyPrice  = yataPriceCache[name] ? yataPriceCache[name].price : 0;
        const sellPrice = marketValueCache[name] || 0;
        const profit    = sellPrice > buyPrice ? sellPrice - buyPrice : 0;
        // Use per-source history key: BoB items use '_bob', overseas items use YATA country code
        const itemCode = (data && data.loc === 'BoB') ? 'bob'
            : (data && data.loc ? (Object.values(TORN_DEST_TO_CODE).find((c,i)=>Object.keys(TORN_DEST_TO_CODE)[i]===data.loc) || '') : '');
        const history   = cfg.getStockHistory(name + (itemCode ? '_'+itemCode : ''));
        const fmt = n => n >= 1e6 ? '$' + (n/1e6).toFixed(2) + 'M' : n >= 1e3 ? '$' + Math.round(n/1000) + 'k' : '$' + n;
        const lastUpdated = history.length ? new Date(history[history.length-1].ts).toLocaleString([], { month:'short', day:'numeric', hour:'2-digit', minute:'2-digit' }) : '—';

        // Stat grid
        const grid = document.createElement('div');
        grid.style.cssText = 'display:grid;grid-template-columns:repeat(3,1fr);gap:4px;';
        [
            { label: 'Item ID',    value: '#' + data.id,                       color: C.textDim },
            { label: 'Buy Price',  value: buyPrice  > 0 ? fmt(buyPrice)  : '—', color: C.stockLo },
            { label: 'Sell Value', value: sellPrice > 0 ? fmt(sellPrice) : '—', color: C.okay    },
            { label: 'Profit/ea',  value: profit    > 0 ? fmt(profit)    : '—', color: profit > 0 ? C.okay : C.textDim },
            { label: 'YATA Stock', value: abroadQty !== undefined ? abroadQty.toLocaleString() : '—', color: C.abroad },
            { label: 'Updated',    value: lastUpdated,                           color: C.textDim },
        ].forEach(({ label, value, color }) => {
            const cell = document.createElement('div');
            cell.style.cssText = `background:${C.card};border:1px solid ${C.cardBorder};border-radius:4px;padding:5px 4px;text-align:center;`;
            cell.innerHTML = `<div style="font-size:7.5px;color:${C.textDim};font-family:Consolas,monospace;letter-spacing:.4px;margin-bottom:2px;">${label}</div>
                              <div style="font-size:10px;font-weight:700;color:${color};font-family:Consolas,monospace;">${value}</div>`;
            grid.appendChild(cell);
        });
        inner.appendChild(grid);

        // Sparkline — price history
        if (history.length >= 2) {
            const sparkWrap = document.createElement('div');
            sparkWrap.style.cssText = 'display:flex;flex-direction:column;gap:3px;';
            const sparkLbl = document.createElement('div');
            sparkLbl.style.cssText = `font-size:8px;font-weight:700;letter-spacing:.5px;text-transform:uppercase;color:${C.goldDim};font-family:Consolas,monospace;`;
            sparkLbl.textContent = (isBob ? '🏪 Torn API Stock History' : '📦 YATA Stock History') + ' (' + fmtHistorySpan(history) + ')';
            sparkWrap.appendChild(sparkLbl);

            const vals  = history.map(h => h.qty !== undefined ? h.qty : (h.price || 0));
            const minP  = Math.min(...vals);
            const maxP  = Math.max(...vals);
            const range = maxP - minP || 1;
            const W = 260, H = 36, pad = 4;

            const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
            svg.setAttribute('viewBox', `0 0 ${W} ${H}`);
            svg.setAttribute('width', '100%');
            svg.setAttribute('height', H);
            svg.style.cssText = 'display:block;overflow:visible;';

            const pts = vals.map((v, i) => {
                const x = pad + (i / (vals.length - 1)) * (W - pad*2);
                const y = pad + (1 - (v - minP) / range) * (H - pad*2);
                return [x, y];
            });
            const pathD = 'M' + pts.map(([x,y]) => `${x.toFixed(1)},${y.toFixed(1)}`).join('L');
            const fillD = pathD + `L${pts[pts.length-1][0].toFixed(1)},${H}L${pts[0][0].toFixed(1)},${H}Z`;

            const fill = document.createElementNS('http://www.w3.org/2000/svg','path');
            fill.setAttribute('d', fillD); fill.setAttribute('fill','rgba(0,200,224,0.08)');
            const line = document.createElementNS('http://www.w3.org/2000/svg','path');
            line.setAttribute('d', pathD); line.setAttribute('fill','none');
            line.setAttribute('stroke','rgba(0,200,224,0.7)'); line.setAttribute('stroke-width','1.5');
            line.setAttribute('stroke-linejoin','round'); line.setAttribute('stroke-linecap','round');

            svg.appendChild(fill); svg.appendChild(line);

            const lastPt = pts[pts.length - 1];
            const dot = document.createElementNS('http://www.w3.org/2000/svg','circle');
            dot.setAttribute('cx', lastPt[0].toFixed(1)); dot.setAttribute('cy', lastPt[1].toFixed(1));
            dot.setAttribute('r','3'); dot.setAttribute('fill','#00c8e0');
            svg.appendChild(dot);

            const mkTxt = (txt, x, y, anchor) => {
                const t = document.createElementNS('http://www.w3.org/2000/svg','text');
                t.textContent = txt; t.setAttribute('x', x); t.setAttribute('y', y);
                t.setAttribute('text-anchor', anchor);
                t.style.cssText = 'font-size:7px;font-family:Consolas,monospace;fill:rgba(140,220,235,0.45);';
                return t;
            };
            const fmtShort = n => n >= 1e6 ? (n/1e6).toFixed(1)+'M' : n >= 1e3 ? Math.round(n/1000)+'k' : String(n);
            svg.appendChild(mkTxt(fmtShort(maxP), pad+2, pad+6, 'start'));
            svg.appendChild(mkTxt(fmtShort(minP), pad+2, H-2, 'start'));

            sparkWrap.appendChild(svg);
            inner.appendChild(sparkWrap);
        } else {
            const noHistory = document.createElement('div');
            noHistory.style.cssText = `font-size:8px;color:${C.textDim};font-family:Consolas,monospace;text-align:center;padding:4px 0;`;
            noHistory.textContent = 'Stock history builds up over refreshes';
            inner.appendChild(noHistory);
        }

        detail.appendChild(inner);
    }

    row.addEventListener('click', () => {
        isOpen = !isOpen;
        if (isOpen) populateDetail();
        detail.style.maxHeight = isOpen ? '300px' : '0';
        chevron.style.transform = isOpen ? 'translateY(-50%) rotate(180deg)' : 'translateY(-50%) rotate(0deg)';
    });

    wrap.appendChild(row);
    wrap.appendChild(detail);
    return wrap;
}

/* ─────────────────────────────────────────
   COLUMN HEADER ROW
───────────────────────────────────────── */
function makeColHeader() {
    const row = document.createElement('div');
    row.style.cssText = `display:grid;grid-template-columns:34px 34px 36px 1fr;gap:6px;padding:3px 8px;background:${C.goldGlow};border-bottom:1px solid ${C.border};`;
    ['', 'Own', 'Abroad', ''].forEach((txt, i) => {
        const s = document.createElement('span');
        s.textContent = txt;
        s.style.cssText = `font:600 8px Consolas,monospace;color:${C.goldDim};text-align:center;text-transform:uppercase;letter-spacing:.4px;`;
        if (txt === 'Own')    s.title = 'Your display items minus completed sets. Lowest = bottleneck.';
        if (txt === 'Abroad') s.title = 'Live overseas stock from Torn API.\n🟢 Plushie ≥2000 / Flower ≥5000\n🟠 Below threshold  🔴 Zero\n🏪 BoB = Bits n\' Bobs shop shortcut';
        row.appendChild(s);
    });
    return row;
}
 
/* ─────────────────────────────────────────
   TAB: SETS
───────────────────────────────────────── */
function renderSetsBody() {
    const body = panelEl.querySelector('.lt-body');
    body.innerHTML = '';
 
    if (!cfg.apiKey) {
        body.appendChild(makeEmpty('🔑', 'Set your API key in Settings<br>to track your item sets'));
        return;
    }
    if (!Object.keys(invCache).length) {
        body.appendChild(makeEmpty('◌', 'Loading your items…'));
        return;
    }
 
    const vis          = cfg.getSectionVis();
    const runs         = cfg.getXanRuns();
    const activeRun    = runs.find(r => r.active);
    const xanPriority  = cfg.getXanPriority();
    const _xanFocusCtry = cfg.getXanCountry();
    const focusMode    = !!(activeRun || xanPriority);
 
    // ── FOCUS BANNER ──
    if (focusMode) {
        const isRun    = !!activeRun;
        const bannerCol = isRun ? '#00c8e0' : C.gold;
        const banner   = document.createElement('div');
        banner.style.cssText = `margin:6px 8px 4px;padding:7px 10px;border-radius:6px;background:${bannerCol}0f;border:1px solid ${bannerCol}44;display:flex;align-items:center;gap:8px;`;
 
        if (isRun) {
            const total     = activeRun.trips.reduce((s,t)=>s+t.bought,0);
            const remaining = Math.max(0, activeRun.contractQty - total);
            banner.innerHTML = `<span style="font-size:14px;flex-shrink:0;">✈</span>
                <div style="flex:1;font-family:Consolas,monospace;">
                    <div style="font-size:9.5px;font-weight:700;color:${bannerCol};letter-spacing:.4px;">RUN: ${activeRun.client}</div>
                    <div style="font-size:8.5px;color:rgba(0,200,224,0.6);margin-top:1px;">
                        ${total} collected · ${remaining} needed · ${activeRun.contractQty} total
                    </div>
                </div>
                <div style="font-size:11px;font-weight:700;font-family:Consolas,monospace;color:${bannerCol};">${Math.min(100,Math.round(total/activeRun.contractQty*100))}%</div>`;
        } else {
            // personal priority banner
            const threshold = cfg.getXanThreshold();
            const personal  = xanPersonal;
            const needed    = Math.max(0, threshold - personal);
            banner.innerHTML = `<span style="font-size:14px;flex-shrink:0;">🧪</span>
                <div style="flex:1;font-family:Consolas,monospace;">
                    <div style="font-size:9.5px;font-weight:700;color:${bannerCol};letter-spacing:.4px;">XANAX PRIORITY</div>
                    <div style="font-size:8.5px;color:rgba(255,180,0,0.6);margin-top:1px;">
                        ${personal} collected · ${needed} needed · ${threshold} target
                    </div>
                </div>
                <div style="font-size:11px;font-weight:700;font-family:Consolas,monospace;color:${needed===0?'#66bb66':bannerCol};">${personal}/${threshold}</div>`;
        }
        body.appendChild(banner);
    }
 
    // ── Museum Day Bonus Line ──
    (function() {
        const now         = new Date();
        const museumStart = new Date(Date.UTC(2026, 4, 17, 10, 0, 0)); // May 17 10:00 TCT
        const museumEnd   = new Date(Date.UTC(2026, 4, 19,  0, 0, 0)); // May 19 (48h window)
        const msUntil     = museumStart - now;
        const daysUntil   = msUntil / 86400000;
        const isActive    = now >= museumStart && now <= museumEnd;
        const isPast      = now > museumEnd;
        const inWindow    = isActive || (!isPast && daysUntil <= 7);
        const pinned      = cfg.getMuseumPin();
 
        if (!(inWindow || pinned)) return;
        if (!pointsPrice || !Object.keys(invCache).length) return;
 
        const vis2 = cfg.getSectionVis();
        let tSets = 0, tPts = 0;
        Object.entries(GROUPS).forEach(([n, g]) => {
            if (vis2[n.toLowerCase()] === false) return;
            const s = calcSet(invCache, g.items);
            tSets += s;
            tPts  += s * g.pts;
        });
        if (vis2.special !== false) {
            Object.entries(SPECIAL_ITEMS).forEach(([name, data]) => {
                const qty = invCache[name] || 0;
                tSets += qty;
                tPts  += qty * data.pts;
            });
        }
        if (!tSets) return;
 
        const bonusSets = Math.floor(tSets * 0.1);          // 200 sets ÷ 10% = 20
        const musSets   = tSets + bonusSets;                 // 200 + 20 = 220
        const musPts    = Math.round(tPts * 1.1);            // pts scale proportionally
        const musVal    = musPts * pointsPrice;
        const musFmt    = musVal >= 1e9 ? `$${(musVal/1e9).toFixed(2)}B`
                        : musVal >= 1e6 ? `$${(musVal/1e6).toFixed(1)}M`
                        : `$${Math.round(musVal/1000)}k`;
 
        let timeTag = '';
        if (isActive)             timeTag = ' · 🟢 ACTIVE';
        else if (daysUntil <= 1)  timeTag = ' · tomorrow';
        else if (daysUntil <= 7)  timeTag = ` · in ${Math.ceil(daysUntil)}d`;
 
        const GOLD     = '#ffb830';
        const GOLD_DIM = 'rgba(255,184,48,0.7)';
        const GOLD_BG  = 'rgba(255,184,48,0.06)';
        const GOLD_BDR = 'rgba(255,184,48,0.22)';
 
        const museumRow = document.createElement('div');
        museumRow.style.cssText = `display:flex;align-items:center;gap:6px;padding:4px 8px;background:${GOLD_BG};border-bottom:1px solid ${GOLD_BDR};font-family:Consolas,monospace;font-size:8px;`;
        museumRow.title = "Museum Day (May 17-19) gives 10% bonus on point redemptions. The bonus shown is what you would earn extra by waiting.";
 
        const icon = document.createElement('span');
        icon.textContent = '🏛️';
        icon.style.cssText = 'font-size:10px;flex-shrink:0;';
 
        const label = document.createElement('span');
        label.style.cssText = `color:${GOLD};font-weight:700;letter-spacing:.5px;text-transform:uppercase;flex:1;`;
        label.textContent = `Museum Day Bonus: ${musSets.toLocaleString()} sets · ${musPts.toLocaleString()} pts · ${musFmt}${timeTag}`;
 
        museumRow.appendChild(icon);
        museumRow.appendChild(label);
        body.appendChild(museumRow);
    })();
 
    // ── GROUPS (hidden in focus mode) ──
    if (!focusMode) {
        Object.entries(GROUPS).forEach(([groupName, g]) => {
            if (vis[groupName.toLowerCase()] === false) return;
            const sets = calcSet(invCache, g.items);
            const warn = getBottleneck(invCache, g.items, sets);
            body.appendChild(makeSectionLabel(g.icon + ' ' + groupName.toUpperCase(), sets, g.pts));
            if (warn) body.appendChild(makeAlertRow(warn));
            body.appendChild(makeColHeader());
            getSortedItems(invCache, g.items, sets).forEach(({ name, data, remaining }) => {
                const isBob = BOB_IDS.has(data.id);
                const abroad = abroadCache[name] || 0;
                body.appendChild(makeExpandableItem(name, data, remaining, abroad, isBob));
            });
        });
 
        // ── SPECIAL ──
        if (vis.special !== false) {
            const specCount = Object.values(SPECIAL_ITEMS).reduce((s, d) => {
                const name = Object.keys(SPECIAL_ITEMS).find(k => SPECIAL_ITEMS[k] === d);
                return s + (invCache[name] || 0);
            }, 0);
            body.appendChild(makeSectionLabel('☄️ SPECIAL', specCount, 0));
            body.appendChild(makeColHeader());
            Object.entries(SPECIAL_ITEMS).forEach(([name, data]) => {
                const own    = invCache[name] || 0;
                const abroad = abroadCache[name] || 0;
                body.appendChild(makeExpandableItem(name, data, own, abroad, false));
            });
        }
    }
 
    // ── XANAX FOCUS ROW (shown in focus mode only) ──
    if (focusMode) {
        const xanOwn    = xanPersonal;
        const xanAbroad = xanSACache.qty || 0;
        body.appendChild(makeSectionLabel('🧪 XANAX', xanOwn, 0));
        body.appendChild(makeColHeader());
        body.appendChild(makeItemRow('Xanax', { id: XANAX_ID, s: 'Xanax', loc: _xanFocusCtry || 'South Africa' }, xanOwn, xanAbroad, false));
    }
}
 
/* ─────────────────────────────────────────
   TAB: XANAX
───────────────────────────────────────── */
 
/* ─────────────────────────────────────────
   XANAX RUN — helper functions
───────────────────────────────────────── */
function loadActiveXanRun() {
    const runs = cfg.getXanRuns();
    activeXanRun = runs.find(r => r.active) || null;
}
 
function saveXanRun(run) {
    const runs = cfg.getXanRuns();
    const idx  = runs.findIndex(r => r.id === run.id);
    if (idx >= 0) runs[idx] = run; else runs.push(run);
    cfg.setXanRuns(runs);
}
 
function openXanTripLogModal(run, zIndex) {
    const overlay = document.createElement('div');
    overlay.style.cssText = `position:fixed;inset:0;background:rgba(0,0,0,0.78);z-index:${zIndex||1000020};display:flex;align-items:center;justify-content:center;padding:16px;`;
    overlay.addEventListener('click', e => { if (e.target === overlay) overlay.remove(); });
 
    const box = document.createElement('div');
    box.style.cssText = `background:${C.settBg};border:1px solid ${C.settBorder};border-radius:12px;padding:18px;width:100%;max-width:290px;max-height:75vh;display:flex;flex-direction:column;font-family:Arial,sans-serif;`;
 
    const hdr = buildHdr('✈ Trip Log — ' + run.client, () => overlay.remove());
    box.appendChild(hdr);
 
    // Summary strip
    const total = run.trips.reduce((s,t) => s+t.bought, 0);
    const carry = cfg.getXanCarry() || 0;
    const summary = document.createElement('div');
    summary.style.cssText = 'display:flex;gap:6px;margin-bottom:10px;font-size:8.5px;font-family:Consolas,monospace;flex-wrap:wrap;';
    const mkBadge = (txt, col) => {
        const s = document.createElement('span');
        s.textContent = txt;
        s.style.cssText = `color:${col};background:${col}18;border:1px solid ${col}44;border-radius:3px;padding:2px 7px;`;
        return s;
    };
    summary.appendChild(mkBadge(run.trips.length + ' trips', C.goldDim));
    summary.appendChild(mkBadge(total + ' 🧪 total', C.okay));
    box.appendChild(summary);
 
    // Trip list
    const list = document.createElement('div');
    list.style.cssText = 'overflow-y:auto;flex:1;display:flex;flex-direction:column;gap:4px;margin-bottom:12px;';
 
    if (run.trips.length === 0) {
        const empty = document.createElement('div');
        empty.textContent = 'No trips logged yet.';
        empty.style.cssText = `font-size:10px;color:${C.textDim};font-family:Consolas,monospace;text-align:center;padding:20px 0;`;
        list.appendChild(empty);
    } else {
        [...run.trips].reverse().forEach((t, i) => {
            const tripNum = run.trips.length - i;
            const d = new Date(t.ts);
            const time = d.toLocaleTimeString([], { hour:'2-digit', minute:'2-digit' });
            const date = d.toLocaleDateString([], { month:'short', day:'numeric' });
            const row = document.createElement('div');
            row.style.cssText = `display:flex;align-items:center;gap:8px;padding:6px 8px;border-radius:5px;background:${C.card};border:1px solid ${C.cardBorder};font-size:8.5px;font-family:Consolas,monospace;`;
            row.innerHTML = `<span style="color:${C.goldDim};width:28px;flex-shrink:0;">T${tripNum}</span>
                <span style="flex:1;color:${C.textDim};">${date} ${time}</span>
                <span style="color:${C.okay};font-weight:700;">+${t.bought} 🧪</span>`;
            list.appendChild(row);
        });
    }
    box.appendChild(list);
 
    // Log trip + close buttons
    const btnRow = document.createElement('div'); btnRow.style.cssText = 'display:flex;gap:8px;';
    const doneBtn = document.createElement('button'); doneBtn.type='button'; doneBtn.textContent='Done';
    doneBtn.style.cssText = `flex:1;padding:9px 0;border-radius:7px;border:1px solid ${C.border};background:${C.bg};color:${C.textDim};font-size:11px;cursor:pointer;font-family:Arial,sans-serif;`;
    doneBtn.addEventListener('click', () => overlay.remove());
    if (run.active) {
        const logBtn = document.createElement('button'); logBtn.type='button';
        logBtn.textContent = carry > 0 ? `✈ Log Trip (+${carry})` : '✈ Log Trip';
        logBtn.style.cssText = `flex:1;padding:9px 0;border-radius:7px;border:1px solid ${C.border};background:${C.card};color:${C.gold};font-size:11px;font-weight:700;cursor:pointer;font-family:Arial,sans-serif;`;
        logBtn.addEventListener('click', () => {
            logXanTrip(run.id);
            overlay.remove();
            openXanTripLogModal(cfg.getXanRuns().find(r => r.id === run.id) || run, zIndex);
        });
        btnRow.appendChild(logBtn);
    }
    btnRow.appendChild(doneBtn);
    box.appendChild(btnRow);
 
    overlay.appendChild(box);
    document.body.appendChild(overlay);
}
 
function openXanCountryPicker() {
    const existing = document.getElementById('lt-xan-ctry-picker');
    if (existing) { existing.remove(); return; }
    const overlay = document.createElement('div');
    overlay.id = 'lt-xan-ctry-picker';
    overlay.style.cssText = `position:fixed;inset:0;background:rgba(0,0,0,0.6);z-index:1000060;display:flex;align-items:center;justify-content:center;padding:16px;`;
    overlay.addEventListener('click', e => { if (e.target===overlay) overlay.remove(); });
    const box = document.createElement('div');
    box.style.cssText = `background:${C.settBg};border:1px solid ${C.settBorder};border-radius:12px;padding:18px;width:100%;max-width:280px;font-family:Arial,sans-serif;`;
    const ttl = document.createElement('div'); ttl.textContent = '✈ Xanax Run Country';
    ttl.style.cssText = `font-size:11px;font-weight:700;letter-spacing:1.2px;text-transform:uppercase;color:#00b7cf;margin-bottom:14px;`;
    box.appendChild(ttl);
    const FLAGS = {'Mexico':'🇲🇽','Hawaii':'🇺🇸','South Africa':'🇿🇦','Japan':'🇯🇵','China':'🇨🇳','Argentina':'🇦🇷','Switzerland':'🇨🇭','Canada':'🇨🇦','United Kingdom':'🇬🇧','UAE':'🇦🇪','Cayman Islands':'🇰🇾'};
    // Only countries confirmed to have Xanax in YATA overseas stock
    // Dynamically check yataCityCache, fallback to known list
    const XANAX_COUNTRIES_FALLBACK = ['South Africa','Canada','United Kingdom','Japan'];
    const xanaxCountries = Object.entries(yataCityCache).length > 0
        ? Object.entries(yataCityCache)
            .filter(([code, data]) => data.stocks && data.stocks.some(s => s.id === XANAX_ID))
            .map(([code]) => Object.keys(TORN_DEST_TO_CODE).find(k => TORN_DEST_TO_CODE[k] === code))
            .filter(Boolean)
        : XANAX_COUNTRIES_FALLBACK;
    const cur = cfg.getXanCountry();
    xanaxCountries.forEach(country => {
        const btn = document.createElement('button'); btn.type='button';
        const isActive = country === cur;
        btn.style.cssText = `display:flex;align-items:center;gap:8px;width:100%;padding:8px 10px;border-radius:7px;border:1px solid ${isActive ? C.gold : C.border};background:${isActive ? C.goldGlow : C.card};margin-bottom:5px;cursor:pointer;font-family:Arial,sans-serif;`;
        btn.innerHTML = `<span style="font-size:16px;">${FLAGS[country]||'✈️'}</span><span style="font-size:10.5px;font-weight:${isActive?'700':'400'};color:${isActive?C.gold:C.text};">${country}</span>${isActive?`<span style="margin-left:auto;font-size:9px;color:${C.gold};">✓ Selected</span>`:''}`;
        btn.addEventListener('click', () => {
            cfg.setXanCountry(country);
            overlay.remove();
            if (panelEl && activeTab==='xanax') setTimeout(()=>renderPanel(), 0);
        });
        box.appendChild(btn);
    });
    const cancelBtn = document.createElement('button'); cancelBtn.type='button'; cancelBtn.textContent='Cancel';
    cancelBtn.style.cssText = `width:100%;padding:8px 0;border-radius:7px;border:1px solid ${C.border};background:${C.bg};color:${C.textDim};font-size:11px;cursor:pointer;font-family:Arial,sans-serif;margin-top:4px;`;
    cancelBtn.addEventListener('click', () => overlay.remove());
    box.appendChild(cancelBtn);
    overlay.appendChild(box);
    document.body.appendChild(overlay);
}

function openXanRunModal(existing) {
    const overlay = document.createElement('div');
    overlay.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,0.72);z-index:1000010;display:flex;align-items:center;justify-content:center;padding:16px;';
 
    const box = document.createElement('div');
    box.style.cssText = `background:${C.settBg};border:1px solid ${C.settBorder};border-radius:12px;padding:18px;width:100%;max-width:290px;font-family:Arial,sans-serif;`;
 
    const title = document.createElement('div');
    title.textContent = existing ? '✎ Edit Run' : '✈ New Xanax Run';
    title.style.cssText = `font-size:11px;font-weight:700;letter-spacing:1.2px;text-transform:uppercase;color:#00b7cf;margin-bottom:14px;`;
    box.appendChild(title);
 
    function field(lbl, placeholder, val, type) {
        const g = document.createElement('div'); g.style.cssText = 'margin-bottom:11px;';
        const l = document.createElement('div'); l.textContent = lbl;
        l.style.cssText = `font-size:9px;font-weight:700;letter-spacing:.8px;text-transform:uppercase;color:${C.goldDim};margin-bottom:4px;`;
        const inp = document.createElement('input'); inp.type = type||'text'; inp.placeholder = placeholder;
        inp.value = val !== undefined && val !== null ? val : '';
        inp.style.cssText = `width:100%;box-sizing:border-box;background:${C.bg};border:1px solid ${C.border};border-radius:6px;color:${C.text};font-size:13px;font-family:Consolas,monospace;padding:7px 10px;outline:none;`;
        inp.addEventListener('focus', () => inp.style.borderColor = C.gold);
        inp.addEventListener('blur',  () => inp.style.borderColor = C.border);
        g.appendChild(l); g.appendChild(inp);
        box.appendChild(g);
        return inp;
    }
 
    const clientInp = field('Client / Faction Name', 'Client or Faction name', existing ? existing.client : '');
    const qtyInp    = field('Contract Qty (xanax)', 'Total Xanax to deliver', existing ? existing.contractQty : '', 'number');
    const priceInp  = field('Your Sell Price ($ / xanax)', 'Price per Xanax', existing ? existing.manualPrice : '', 'number');
 
    const _modalCtryCache = (() => { const code = TORN_DEST_TO_CODE[cfg.getXanCountry()]||'sou'; const city = yataCityCache[code]; const s = city ? city.stocks.find(x=>x.id===XANAX_ID) : null; return s ? {qty:s.qty,price:s.cost} : (code==='sou'?xanSACache:{qty:0,price:0}); })();
    if (_modalCtryCache.price > 0) {
        const hint = document.createElement('div');
        hint.style.cssText = `font-size:8.5px;color:${C.textDim};font-family:Consolas,monospace;margin-top:-7px;margin-bottom:11px;`;
        const updateHint = () => {
            const margin = (parseInt(priceInp.value)||0) - _modalCtryCache.price;
            hint.textContent = `${cfg.getXanCountry()}: $${_modalCtryCache.price.toLocaleString()} — margin: ${margin>=0?'+':''}$${margin.toLocaleString()} /xan`;
        };
        updateHint();
        priceInp.addEventListener('input', updateHint);
        box.appendChild(hint);
    }
 
    const btnRow = document.createElement('div'); btnRow.style.cssText = 'display:flex;gap:8px;margin-top:4px;';
    const saveBtn = document.createElement('button'); saveBtn.type='button';
    saveBtn.textContent = existing ? 'Save Changes' : '⚡ Start Run';
    saveBtn.style.cssText = `flex:1;padding:9px 0;border-radius:7px;border:1px solid ${C.border};background:${C.card};color:${C.gold};font-size:11px;font-weight:700;cursor:pointer;font-family:Arial,sans-serif;`;
    const cancelBtn = document.createElement('button'); cancelBtn.type='button'; cancelBtn.textContent='Cancel';
    cancelBtn.style.cssText = `padding:9px 14px;border-radius:7px;border:1px solid ${C.border};background:${C.bg};color:${C.textDim};font-size:11px;cursor:pointer;font-family:Arial,sans-serif;`;
    cancelBtn.addEventListener('click', () => overlay.remove());
    overlay.addEventListener('click', e => { if (e.target === overlay) overlay.remove(); });
 
    saveBtn.addEventListener('click', () => {
        const client = clientInp.value.trim();
        const qty    = parseInt(qtyInp.value)   || 0;
        const price  = parseInt(priceInp.value) || 0;
        if (!client) { clientInp.style.borderColor = '#ff4444'; return; }
        if (!qty)    { qtyInp.style.borderColor    = '#ff4444'; return; }
        const run = existing ? { ...existing } : {
            id: Date.now(), active: true, startedAt: Date.now(),
            endedAt: null, trips: [], payment: 'unpaid',
        };
        run.client = client; run.contractQty = qty;
        run.manualPrice = price; run.saPrice = _modalCtryCache.price; run.country = cfg.getXanCountry();
        saveXanRun(run);
        overlay.remove();
        if (panelEl && activeTab === 'xanax') renderXanaxBody();
        toast('✈ Run started!', 2000);
    });
 
    btnRow.appendChild(saveBtn); btnRow.appendChild(cancelBtn);
    box.appendChild(btnRow);
    overlay.appendChild(box);
    document.body.appendChild(overlay);
    setTimeout(() => clientInp.focus(), 80);
}
 
function logXanTrip(runId) {
    const runs = cfg.getXanRuns();
    const run  = runs.find(r => r.id === runId);
    if (!run) return;
    const carry = cfg.getXanCarry() || 0;
    run.trips.push({ ts: Date.now(), bought: carry });
    saveXanRun(run);
    if (panelEl && activeTab === 'xanax') renderXanaxBody();
    toast(`✈ Trip logged — +${carry} 🧪`, 1800);
}
 
function endXanRun(runId) {
    const runs = cfg.getXanRuns();
    const run  = runs.find(r => r.id === runId);
    if (!run) return;
    run.active = false; run.endedAt = Date.now();
    saveXanRun(run);
    if (panelEl && activeTab === 'xanax') renderXanaxBody();
    toast('✓ Run complete!', 2000);
}
 
function renderXanRunSection(wrap, secTitle) {
    const runs   = cfg.getXanRuns();
    const active = runs.filter(r => r.active);
    const past   = runs.filter(r => !r.active);
 
    const sec = document.createElement('div');
    // Xanax Runs header with country selector gear
    const xanRunsHdr = document.createElement('div');
    xanRunsHdr.style.cssText = `display:flex;align-items:center;justify-content:space-between;margin-bottom:6px;padding-bottom:4px;border-bottom:1px solid ${C.border};`;
    const xanRunsTitle = document.createElement('div');
    xanRunsTitle.textContent = '✈ Xanax Runs';
    xanRunsTitle.style.cssText = `font-size:9px;font-weight:700;letter-spacing:1.2px;text-transform:uppercase;color:${C.goldDim};font-family:Consolas,monospace;`;
    // Country selector pill
    const xanCtryBtn = document.createElement('button'); xanCtryBtn.type='button';
    const _curCtry = cfg.getXanCountry();
    const _ctryFlag = (Object.entries(TORN_DEST_TO_CODE).find(([k])=>k===_curCtry)||[])[0] ? 
        ({'Mexico':'🇲🇽','Hawaii':'🇺🇸','South Africa':'🇿🇦','Japan':'🇯🇵','China':'🇨🇳','Argentina':'🇦🇷','Switzerland':'🇨🇭','Canada':'🇨🇦','United Kingdom':'🇬🇧','UAE':'🇦🇪','Cayman Islands':'🇰🇾'}[_curCtry]||'✈️') : '✈️';
    xanCtryBtn.textContent = _ctryFlag + ' ' + _curCtry + ' ⚙';
    xanCtryBtn.style.cssText = `font-size:8px;font-family:Consolas,monospace;font-weight:600;color:${C.gold};background:${C.goldGlow};border:1px solid ${C.border};border-radius:10px;padding:2px 8px;cursor:pointer;`;
    xanCtryBtn.addEventListener('click', () => openXanCountryPicker());
    xanRunsHdr.appendChild(xanRunsTitle); xanRunsHdr.appendChild(xanCtryBtn);
    sec.appendChild(xanRunsHdr);
 
    // ── Start button + Priority toggle area ──
    const startWrap = document.createElement('div');
    startWrap.style.cssText = `background:${C.bg2};border:1px solid ${C.border};border-radius:8px;overflow:hidden;margin-bottom:${(active.length||past.length)?'10px':'0'};`;

    // Main start button
    const startBtn = document.createElement('button'); startBtn.type='button';
    startBtn.style.cssText = `width:100%;padding:10px 0;border-radius:0;font-size:11px;font-weight:700;cursor:pointer;font-family:Arial,sans-serif;background:transparent;border:none;border-bottom:1px solid ${C.border};color:${C.gold};letter-spacing:.5px;`;
    startBtn.textContent = '⚡ Start New Run';
    startBtn.addEventListener('click', () => openXanRunModal(null));

    // Priority toggle row
    const priRow = document.createElement('div');
    priRow.style.cssText = 'display:flex;align-items:center;gap:8px;padding:7px 10px;cursor:pointer;user-select:none;';

    const priChk = document.createElement('input'); priChk.type = 'checkbox';
    priChk.checked = cfg.getXanPriority();
    priChk.style.cssText = 'width:14px;height:14px;cursor:pointer;accent-color:#00c8e0;flex-shrink:0;';

    const priLbl = document.createElement('span');
    priLbl.style.cssText = `font-size:9.5px;font-weight:600;color:${cfg.getXanPriority() ? '#00c8e0' : C.text};font-family:Arial,sans-serif;flex:1;transition:color 0.15s;`;
    priLbl.textContent = '🚨 Prioritize Personal Runs in Travel Planner';

    // Threshold input — only visible when priority is on
    const threshWrap = document.createElement('div');
    threshWrap.style.cssText = `overflow:hidden;max-height:${cfg.getXanPriority() ? '80px' : '0'};transition:max-height 0.2s ease;border-top:${cfg.getXanPriority() ? `1px solid ${C.border}` : 'none'};`;

    const threshInner = document.createElement('div');
    threshInner.style.cssText = 'display:flex;align-items:center;gap:8px;padding:7px 10px;';

    const threshLblEl = document.createElement('span');
    threshLblEl.style.cssText = `font-size:8.5px;color:${C.textDim};font-family:Consolas,monospace;flex:1;`;
    threshLblEl.textContent = 'Stop above';

    const threshInp = document.createElement('input'); threshInp.type = 'number'; threshInp.min = '0';
    threshInp.value = cfg.getXanThreshold();
    threshInp.style.cssText = `font-family:Consolas,monospace;font-size:12px;font-weight:700;text-align:center;width:56px;padding:4px 3px;background:${C.bg};border:1px solid ${C.border};border-radius:4px;color:${C.text};outline:none;-moz-appearance:textfield;`;
    threshInp.addEventListener('focus', () => threshInp.style.borderColor = 'rgba(0,190,215,0.8)');
    threshInp.addEventListener('blur',  () => { threshInp.style.borderColor = 'rgba(0,140,170,0.4)'; cfg.setXanThreshold(Math.max(0, parseInt(threshInp.value)||0)); });
    threshInp.addEventListener('change', () => cfg.setXanThreshold(Math.max(0, parseInt(threshInp.value)||0)));

    const threshUnit = document.createElement('span');
    threshUnit.style.cssText = `font-size:8.5px;color:${C.textDim};font-family:Consolas,monospace;`;
    threshUnit.textContent = 'Xanax';

    threshInner.appendChild(threshLblEl); threshInner.appendChild(threshInp); threshInner.appendChild(threshUnit);
    threshWrap.appendChild(threshInner);

    const togglePriority = () => {
        const on = priChk.checked;
        cfg.setXanPriority(on);
        priLbl.style.color = on ? '#00c8e0' : C.text;
        threshWrap.style.maxHeight  = on ? '80px' : '0';
        threshWrap.style.borderTop  = on ? `1px solid ${C.border}` : 'none';
        // Defer renderPanel — calling it synchronously destroys the element firing this event
        if (panelEl) setTimeout(() => renderPanel(), 0);
    };

    priRow.addEventListener('click', e => {
        if (e.target !== priChk) {
            // Toggling checked fires the 'change' event which calls togglePriority
            priChk.checked = !priChk.checked;
            priChk.dispatchEvent(new Event('change'));
        }
        // If target IS priChk, the browser already fired 'change' natively — don't double-call
    });
    priChk.addEventListener('change', togglePriority);

    priRow.appendChild(priChk); priRow.appendChild(priLbl);
    startWrap.appendChild(startBtn);
    startWrap.appendChild(priRow);
    startWrap.appendChild(threshWrap);
    sec.appendChild(startWrap);
 
    // ── Active runs ──
    if (active.length > 0) {
        const actHdr = document.createElement('div');
        actHdr.textContent = `⚡ ACTIVE (${active.length})`;
        actHdr.style.cssText = `font-size:9px;font-weight:700;letter-spacing:1px;color:${C.goldDim};font-family:Consolas,monospace;margin-bottom:6px;`;
        sec.appendChild(actHdr);
 
        active.forEach(r => {
            const total = r.trips.reduce((s,t) => s+t.bought, 0);
            const carry = cfg.getXanCarry() || 0;
            const pct   = r.contractQty > 0 ? Math.min(100, Math.round(total / r.contractQty * 100)) : 0;
            const margin  = r.manualPrice && r.saPrice ? r.manualPrice - r.saPrice : null;
            const profit  = margin !== null && total > 0 ? margin * total : null;
            const payCol  = r.payment === 'paid' ? C.okay : C.stockMid;
            const barCol  = pct >= 100 ? C.okay : pct > 50 ? C.gold : C.stockMid;
 
            const card = document.createElement('div');
            card.style.cssText = `background:${C.bg2};border:1px solid ${C.border};border-radius:8px;padding:11px;margin-bottom:8px;box-shadow:0 2px 8px rgba(0,0,0,0.12);`;
 
            // Header: client name + edit btn
            const hdr = document.createElement('div'); hdr.style.cssText = 'display:flex;align-items:center;gap:6px;margin-bottom:7px;';
            const nm = document.createElement('span'); nm.textContent = r.client;
            nm.style.cssText = `font-size:13px;font-weight:700;color:${C.text};font-family:Arial,sans-serif;flex:1;`;
            const editBtn = document.createElement('button'); editBtn.type='button'; editBtn.textContent='✎';
            editBtn.style.cssText = `padding:2px 7px;border-radius:4px;font-size:11px;cursor:pointer;background:${C.card};border:1px solid ${C.border};color:${C.goldDim};font-family:Arial,sans-serif;`;
            editBtn.addEventListener('click', () => openXanRunModal(r));
            hdr.appendChild(nm); hdr.appendChild(editBtn);
            card.appendChild(hdr);
 
            // Progress bar
            const pw = document.createElement('div'); pw.style.cssText = `background:${C.card};border-radius:4px;height:7px;margin-bottom:7px;overflow:hidden;`;
            const pb = document.createElement('div'); pb.style.cssText = `height:100%;width:${pct}%;background:${barCol};border-radius:4px;transition:width 0.4s;`;
            pw.appendChild(pb); card.appendChild(pw);
 
            // Stats grid
            const stats = document.createElement('div'); stats.style.cssText = 'display:grid;grid-template-columns:1fr 1fr 1fr;gap:5px;margin-bottom:7px;';
            [
                { label:'Bought',   val:`${total}/${r.contractQty}`, col: pct>=100?'#66bb66':C.okay },
                { label:'Trips',    val: r.trips.length,              col: C.gold },
                { label:'Progress', val: pct+'%',                     col: barCol },
            ].forEach(({ label, val, col }) => {
                const c = document.createElement('div');
                c.style.cssText = `background:${C.card};border:1px solid ${C.cardBorder};border-radius:5px;padding:5px 3px;text-align:center;`;
                c.innerHTML = `<div style="font-size:7.5px;color:${C.textDim};font-family:Consolas,monospace;margin-bottom:1px;">${label}</div>
                               <div style="font-size:12px;font-weight:700;color:${col};font-family:Consolas,monospace;">${val}</div>`;
                stats.appendChild(c);
            });
            card.appendChild(stats);
 
            // Price strip
            if (r.manualPrice || r.saPrice) {
                const ps = document.createElement('div'); ps.style.cssText = 'display:flex;gap:5px;flex-wrap:wrap;font-size:8px;font-family:Consolas,monospace;margin-bottom:7px;';
                const mkTag = (t, col) => { const s=document.createElement('span'); s.textContent=t; s.style.cssText=`color:${col};background:${col}18;border:1px solid ${col}44;border-radius:3px;padding:2px 5px;`; return s; };
                if (r.manualPrice) ps.appendChild(mkTag('Sell $'+r.manualPrice.toLocaleString(), C.gold));
                if (r.saPrice)     ps.appendChild(mkTag('SA $'+r.saPrice.toLocaleString(), C.textDim));
                if (margin!==null) { const col=margin>=0?C.okay:C.stockLo; ps.appendChild(mkTag((margin>=0?'+':'')+'$'+margin.toLocaleString()+'/xan', col)); }
                if (profit!==null) { const col=profit>=0?C.okay:C.stockLo; ps.appendChild(mkTag((profit>=0?'+':'')+'$'+profit.toLocaleString()+' profit', col)); }
                card.appendChild(ps);
            }
 
            // Action row: 📋 Log | ✈ Log Trip | payment toggle | ✓ End
            const btnRow = document.createElement('div'); btnRow.style.cssText = 'display:flex;gap:6px;flex-wrap:wrap;';
 
            const logBtn = document.createElement('button'); logBtn.type='button'; logBtn.textContent='📋 Log';
            logBtn.style.cssText = `padding:7px 10px;border-radius:6px;font-size:10px;font-weight:700;cursor:pointer;font-family:Arial,sans-serif;background:${C.card};border:1px solid ${C.border};color:${C.goldDim};`;
            logBtn.addEventListener('click', () => openXanTripLogModal(cfg.getXanRuns().find(x=>x.id===r.id)||r));
 
            const tripBtn = document.createElement('button'); tripBtn.type='button';
            tripBtn.textContent = carry > 0 ? `✈ +${carry}` : '✈ Trip';
            tripBtn.style.cssText = `flex:1;padding:7px 0;border-radius:6px;font-size:10.5px;font-weight:700;cursor:pointer;font-family:Arial,sans-serif;background:${C.card};border:1px solid ${C.border};color:${C.gold};`;
            tripBtn.addEventListener('click', () => logXanTrip(r.id));
 
            const payBtn = document.createElement('button'); payBtn.type='button';
            payBtn.textContent = r.payment === 'paid' ? '✓ Paid' : '$ Unpaid';
            payBtn.style.cssText = `padding:7px 9px;border-radius:6px;font-size:9.5px;font-weight:700;cursor:pointer;font-family:Arial,sans-serif;background:${payCol}18;border:1px solid ${payCol}55;color:${payCol};`;
            payBtn.addEventListener('click', () => {
                const all = cfg.getXanRuns();
                const idx = all.findIndex(x=>x.id===r.id);
                if (idx<0) return;
                all[idx].payment = all[idx].payment === 'paid' ? 'unpaid' : 'paid';
                cfg.setXanRuns(all);
                renderXanaxBody();
            });
 
            const endBtn = document.createElement('button'); endBtn.type='button'; endBtn.textContent='✓ End';
            endBtn.style.cssText = `padding:7px 10px;border-radius:6px;font-size:10px;font-weight:700;cursor:pointer;font-family:Arial,sans-serif;background:rgba(102,187,102,0.12);border:1px solid rgba(102,187,102,0.45);color:${C.okay};`;
            endBtn.addEventListener('click', () => { if (confirm(`End run for ${r.client}?`)) endXanRun(r.id); });
 
            btnRow.appendChild(logBtn); btnRow.appendChild(tripBtn); btnRow.appendChild(payBtn); btnRow.appendChild(endBtn);
            card.appendChild(btnRow);
            sec.appendChild(card);
        });
    }
 
    // ── Past runs button ──
    if (past.length > 0) {
        const pastBtn = document.createElement('button'); pastBtn.type='button';
        pastBtn.textContent = `📋 Past Runs (${past.length})`;
        pastBtn.style.cssText = `width:100%;padding:9px 0;border-radius:7px;font-size:11px;font-weight:700;cursor:pointer;font-family:Arial,sans-serif;background:${C.card};border:1px solid ${C.border};color:${C.goldDim};letter-spacing:.4px;margin-top:${active.length?'4px':'0'};`;
        pastBtn.addEventListener('click', () => openPastRunsOverlay());
        sec.appendChild(pastBtn);
    }
 
    wrap.appendChild(sec);
}
 
function openPastRunsOverlay() {
    const runs = cfg.getXanRuns();
    const past = runs.filter(r => !r.active).slice().reverse();
 
    const overlay = document.createElement('div');
    overlay.style.cssText = `position:fixed;inset:0;background:${C.settBg};z-index:1000030;display:flex;flex-direction:column;font-family:Arial,sans-serif;color:${C.text};`;
 
    // ── Header bar ──
    const hdr = buildHdr(`📋 Past Runs (${past.length})`, () => overlay.remove());
    overlay.appendChild(hdr);
 
    // ── Scrollable list ──
    const list = document.createElement('div');
    list.style.cssText = `flex:1;overflow-y:auto;padding:12px 14px;display:flex;flex-direction:column;gap:8px;background:${C.bg};`;
 
    if (past.length === 0) {
        const empty = document.createElement('div');
        empty.textContent = 'No completed runs yet.';
        empty.style.cssText = `font-size:11px;color:${C.textDim};font-family:Consolas,monospace;text-align:center;padding:40px 0;`;
        list.appendChild(empty);
    } else {
        past.forEach(r => {
            const total  = r.trips.reduce((s,t)=>s+t.bought,0);
            const profit = r.manualPrice && r.saPrice ? (r.manualPrice - r.saPrice) * total : null;
            const payCol = r.payment === 'paid' ? C.okay : C.stockMid;
 
            const card = document.createElement('div');
            card.style.cssText = `background:${C.bg2};border:1px solid ${C.border};border-radius:9px;padding:12px 14px;box-shadow:0 2px 8px rgba(0,0,0,0.12);`;
 
            // Top row: client + date
            const top = document.createElement('div'); top.style.cssText = 'display:flex;align-items:baseline;gap:6px;margin-bottom:7px;';
            const nm  = document.createElement('span'); nm.textContent = r.client;
            nm.style.cssText = `font-size:14px;font-weight:700;color:${C.text};font-family:Arial,sans-serif;flex:1;`;
            const dt  = document.createElement('span');
            dt.textContent = new Date(r.startedAt).toLocaleDateString([],{month:'short',day:'numeric',year:'numeric'});
            dt.style.cssText = `font-size:9px;color:${C.textDim};font-family:Consolas,monospace;`;
            top.appendChild(nm); top.appendChild(dt);
 
            // Tags row
            const mid = document.createElement('div'); mid.style.cssText = 'display:flex;gap:6px;flex-wrap:wrap;font-size:8.5px;font-family:Consolas,monospace;margin-bottom:10px;';
            const mkTag = (t, col) => { const s=document.createElement('span'); s.textContent=t; s.style.cssText=`color:${col};background:${col}18;border:1px solid ${col}44;border-radius:3px;padding:2px 7px;`; return s; };
            mkTag(r.contractQty + ' contracted', C.textDim);
            mid.appendChild(mkTag(r.trips.length + ' trips', C.goldDim));
            mid.appendChild(mkTag(total + ' 🧪 delivered', C.okay));
            if (r.manualPrice) mid.appendChild(mkTag('$' + r.manualPrice.toLocaleString() + '/xan', C.gold));
            if (profit !== null) { const col = profit >= 0 ? C.okay : C.stockLo; mid.appendChild(mkTag((profit>=0?'+':'')+'$'+profit.toLocaleString()+' profit', col)); }
            mid.appendChild(mkTag(r.payment === 'paid' ? '✓ Paid' : '$ Unpaid', payCol));
 
            // Actions
            const acts = document.createElement('div'); acts.style.cssText = 'display:flex;gap:7px;';
 
            const viewBtn = document.createElement('button'); viewBtn.type='button'; viewBtn.textContent='📋 Trip Log';
            viewBtn.style.cssText = `padding:6px 12px;border-radius:5px;font-size:10px;font-weight:700;cursor:pointer;font-family:Arial,sans-serif;background:${C.card};border:1px solid ${C.border};color:${C.goldDim};`;
            viewBtn.addEventListener('click', () => openXanTripLogModal(r, 1000040));
 
            if (r.payment !== 'paid') {
                const mkPaid = document.createElement('button'); mkPaid.type='button'; mkPaid.textContent='✓ Mark Paid';
                mkPaid.style.cssText = `padding:6px 12px;border-radius:5px;font-size:10px;font-weight:700;cursor:pointer;font-family:Arial,sans-serif;background:rgba(102,187,102,0.12);border:1px solid rgba(102,187,102,0.45);color:${C.okay};`;
                mkPaid.addEventListener('click', () => {
                    const all = cfg.getXanRuns(); const idx = all.findIndex(x=>x.id===r.id);
                    if (idx>=0) { all[idx].payment='paid'; cfg.setXanRuns(all); }
                    // refresh overlay
                    overlay.remove(); openPastRunsOverlay();
                    if (panelEl && activeTab === 'xanax') renderXanaxBody();
                });
                acts.appendChild(mkPaid);
            }
 
            const delBtn = document.createElement('button'); delBtn.type='button'; delBtn.textContent='🗑 Delete';
            delBtn.style.cssText = `padding:6px 12px;border-radius:5px;font-size:10px;cursor:pointer;font-family:Arial,sans-serif;background:rgba(120,30,30,0.15);border:1px solid rgba(180,50,50,0.4);color:${C.stockLo};margin-left:auto;`;
            delBtn.addEventListener('click', () => {
                if (!confirm('Delete this run?')) return;
                cfg.setXanRuns(cfg.getXanRuns().filter(x=>x.id!==r.id));
                overlay.remove();
                if (past.length - 1 > 0) openPastRunsOverlay();
                if (panelEl && activeTab === 'xanax') renderXanaxBody();
                toast('🗑 Deleted');
            });
 
            acts.appendChild(viewBtn); acts.appendChild(delBtn);
            card.appendChild(top); card.appendChild(mid); card.appendChild(acts);
            list.appendChild(card);
        });
    }
 
    overlay.appendChild(list);
    document.body.appendChild(overlay);
}
 
function renderXanaxBody() {
    const body = panelEl.querySelector('.lt-body');
    body.innerHTML = '';

    const xanCount = xanPersonal;
    const xanCarry = cfg.getXanCarry();
    // Get live data for selected xanax country (not just SA)
    const xanSelectedCountry = cfg.getXanCountry();
    const xanSelectedCode    = TORN_DEST_TO_CODE[xanSelectedCountry] || 'sou';
    const xanSelectedCity    = yataCityCache[xanSelectedCode];
    const xanSelectedStock   = xanSelectedCity ? xanSelectedCity.stocks.find(s => s.id === XANAX_ID) : null;
    // Use selected country's YATA data, fallback to xanSACache for South Africa
    const xanDisplayCache = xanSelectedCode === 'sou'
        ? xanSACache
        : xanSelectedStock
            ? { qty: xanSelectedStock.qty, price: xanSelectedStock.cost }
            : { qty: 0, price: 0 };
    const vis = cfg.getSectionVis();
 
    if (vis.xanax === false) {
        body.appendChild(makeEmpty('🧪', 'Xanax section is hidden.<br>Enable it in Settings.'));
        return;
    }
 
    const wrap = document.createElement('div');
    wrap.style.cssText = 'padding:10px;display:flex;flex-direction:column;gap:10px;';
 
    function secTitle(text) {
        const t = document.createElement('div'); t.textContent = text;
        t.style.cssText = `font-size:9px;font-weight:700;letter-spacing:1.2px;text-transform:uppercase;color:${C.goldDim};margin-bottom:6px;padding-bottom:4px;border-bottom:1px solid rgba(0,140,170,0.3);font-family:Consolas,monospace;`;
        return t;
    }
 
    // ── Personal count card ──
    const cardSec = document.createElement('div'); cardSec.appendChild(secTitle('💊 Supply Count'));
 
    // Never-scraped notice — show redirect banner if count has never been set
    const neverScraped = xanPersonal === 0 && cfg.getXanCount() === 0;
    if (neverScraped) {
        const notice = document.createElement('a');
        notice.href = 'https://www.torn.com/item.php';
        notice.style.cssText = `display:flex;align-items:center;gap:10px;padding:10px 12px;border-radius:8px;background:rgba(255,160,0,0.08);border:1px solid rgba(255,160,0,0.4);text-decoration:none !important;cursor:pointer;margin-bottom:2px;`;
        notice.innerHTML = `<span style="font-size:20px;flex-shrink:0;">📦</span>
            <div style="flex:1;">
                <div style="font-size:9.5px;font-weight:700;color:#ffaa00;font-family:Arial,sans-serif;letter-spacing:.3px;">VISIT YOUR ITEMS PAGE</div>
                <div style="font-size:8.5px;color:rgba(255,160,0,0.9);font-family:Consolas,monospace;margin-top:2px;">Tap to open Items — Sets Tracker will<br>auto-read your Xanax count on arrival.</div>
            </div>
            <span style="font-size:14px;color:rgba(255,160,0,0.6);">›</span>`;
        cardSec.appendChild(notice);
    }
 
    const card = document.createElement('div');
    card.style.cssText = `background:${C.bg2};border:1px solid ${C.border};border-radius:8px;padding:12px;display:flex;align-items:center;gap:8px;box-shadow:0 2px 8px rgba(0,0,0,0.1);`;

    const hasFaction = xanFacCache !== null;

    const xImg = document.createElement('img');
    xImg.src = itemImg(XANAX_ID); xImg.alt = 'Xanax';
    xImg.style.cssText = 'width:44px;height:44px;object-fit:contain;border-radius:3px;flex-shrink:0;';

    if (hasFaction) {
        // Layout: [Personal] [🧪 img centred] [Faction]
        // Personal — left
        const persEl = document.createElement('div'); persEl.style.cssText = 'flex:1;text-align:left;';
        const persVal = document.createElement('div');
        persVal.style.cssText = `font-size:34px;font-weight:700;color:${C.gold};font-family:Consolas,monospace;line-height:1;`;
        persVal.textContent = xanCount;
        const persLbl = document.createElement('div');
        persLbl.style.cssText = `font-size:8px;color:${C.textDim};font-family:Consolas,monospace;margin-top:2px;`;
        persLbl.textContent = 'Personal';
        persEl.appendChild(persVal); persEl.appendChild(persLbl);

        // Xanax image — centred between the two counts
        xImg.style.cssText = 'width:44px;height:44px;object-fit:contain;border-radius:3px;flex-shrink:0;margin:0 4px;';

        // Faction — right
        const facEl = document.createElement('div'); facEl.style.cssText = 'flex:1;text-align:right;';
        const facVal = document.createElement('div');
        facVal.style.cssText = `font-size:34px;font-weight:700;color:${C.green};font-family:Consolas,monospace;line-height:1;`;
        facVal.textContent = xanFacCache.toLocaleString();
        const facLbl = document.createElement('div');
        facLbl.style.cssText = `font-size:8px;color:${C.textDim};font-family:Consolas,monospace;margin-top:2px;`;
        facLbl.textContent = 'Faction';
        facEl.appendChild(facVal); facEl.appendChild(facLbl);

        card.appendChild(persEl); card.appendChild(xImg); card.appendChild(facEl);
    } else {
        // No faction perms — image left, personal count pushed right
        const spacer = document.createElement('div'); spacer.style.cssText = 'flex:1;';
        const persEl = document.createElement('div'); persEl.style.cssText = 'text-align:right;flex-shrink:0;';
        const persVal = document.createElement('div');
        persVal.style.cssText = `font-size:36px;font-weight:700;color:${C.gold};font-family:Consolas,monospace;line-height:1;`;
        persVal.textContent = xanCount;
        const persLbl = document.createElement('div');
        persLbl.style.cssText = `font-size:8px;color:${C.textDim};font-family:Consolas,monospace;margin-top:2px;`;
        persLbl.textContent = 'Personal';
        persEl.appendChild(persVal); persEl.appendChild(persLbl);

        card.appendChild(xImg); card.appendChild(spacer); card.appendChild(persEl);
    }

    cardSec.appendChild(card);
 
    // ── Live Data ──
    const infSec = document.createElement('div'); infSec.appendChild(secTitle('📡 Live Data'));
    const infGrid = document.createElement('div'); infGrid.style.cssText = 'display:grid;grid-template-columns:1fr 1fr;gap:8px;';
    const ptsEquiv = (xanDisplayCache.price > 0 && pointsPrice > 0)
        ? (xanDisplayCache.price / pointsPrice).toFixed(2) + ' pts'
        : '—';
    const ctryShort = xanSelectedCountry === 'South Africa' ? 'SA'
        : xanSelectedCountry === 'United Kingdom' ? 'UK'
        : xanSelectedCountry === 'Cayman Islands' ? 'KY'
        : xanSelectedCountry.slice(0,3).toUpperCase();
    [
        { label: ctryShort+' Stock', value: xanDisplayCache.qty > 0 ? xanDisplayCache.qty.toLocaleString() : '0', color: xanDisplayCache.qty > 0 ? C.okay : C.textDim, sub: null },
        { label: ctryShort+' Price', value: xanDisplayCache.price > 0 ? '$' + xanDisplayCache.price.toLocaleString() : '—', color: C.gold, sub: ptsEquiv !== '—' ? ptsEquiv : null },
        { label: 'Carry Lmt', value: xanCarry || '—',                                                       color: C.goldDim, sub: null },
    ].forEach(item => {
        const c = document.createElement('div');
        c.style.cssText = `background:${C.bg2};border:1px solid ${C.border};border-radius:6px;padding:8px 10px;text-align:center;`;
        c.innerHTML = `<div style="font-size:8.5px;color:${C.textDim};font-family:Consolas,monospace;letter-spacing:.5px;margin-bottom:3px;">${item.label}</div>
                       <div style="font-size:16px;font-weight:700;color:${item.color};font-family:Consolas,monospace;">${item.value}</div>
                       ${item.sub ? `<div style="font-size:8px;color:${C.goldDim};font-family:Consolas,monospace;margin-top:2px;">${item.sub}</div>` : ''}`;
        infGrid.appendChild(c);
    });
    infSec.appendChild(infGrid);

    // ── Trip Cost card (SA Price × Carry Limit) ──
    const saPrice  = xanDisplayCache.price || 0;
    const tripCost = saPrice * (xanCarry || 0);
    const tripCostFormatted  = tripCost > 0 ? '$' + tripCost.toLocaleString() : '—';
    const tripCostRaw        = tripCost > 0 ? String(tripCost) : '';

    // Vault destination URLs
    // Vault always = Property Vault, pre-fill with trip cost (buy cost + profit target)
    const vaultUrl   = tripCost > 0
        ? 'https://www.torn.com/properties.php#/p=options&tab=vault&amount=' + tripCost
        : 'https://www.torn.com/properties.php#/p=options&tab=vault';
    const vaultLabel = 'Property Vault';

    const tripCard = document.createElement('div');
    tripCard.style.cssText = `margin-top:8px;background:${C.bg2};border:1px solid ${C.border};border-radius:6px;padding:8px 12px;display:flex;align-items:center;gap:8px;`;

    // Left: label
    const tripLabel = document.createElement('div');
    tripLabel.style.cssText = `font-size:8.5px;color:${C.textDim};font-family:Consolas,monospace;letter-spacing:.5px;flex-shrink:0;`;
    tripLabel.textContent = 'Trip Cost';

    // Middle: vault icon link (centred in the remaining space)
    const vaultLink = document.createElement('a');
    vaultLink.href = vaultUrl;
    vaultLink.title = 'Open ' + vaultLabel;
    vaultLink.style.cssText = 'flex:1;display:flex;align-items:center;justify-content:center;text-decoration:none !important;';
    // Vault door SVG
    vaultLink.innerHTML = `<svg width="22" height="22" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" style="opacity:0.55;transition:opacity 0.15s;">
        <rect x="2" y="3" width="18" height="18" rx="2" stroke="currentColor" stroke-width="1.5"/>
        <circle cx="11" cy="12" r="4" stroke="currentColor" stroke-width="1.4"/>
        <circle cx="11" cy="12" r="1.5" fill="currentColor"/>
        <line x1="11" y1="8" x2="11" y2="6" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/>
        <line x1="11" y1="18" x2="11" y2="16" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/>
        <line x1="7" y1="12" x2="5" y2="12" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/>
        <line x1="17" y1="12" x2="15" y2="12" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/>
        <line x1="20" y1="8" x2="22" y2="8" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/>
        <line x1="20" y1="12" x2="22" y2="12" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/>
        <line x1="20" y1="16" x2="22" y2="16" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/>
    </svg>`;
    vaultLink.style.color = C.goldDim;
    vaultLink.addEventListener('mouseover', () => { vaultLink.querySelector('svg').style.opacity = '1'; vaultLink.style.color = C.gold; });
    vaultLink.addEventListener('mouseout',  () => { vaultLink.querySelector('svg').style.opacity = '0.55'; vaultLink.style.color = C.goldDim; });

    // Right: value + copy icon
    const tripValueWrap = document.createElement('div');
    tripValueWrap.style.cssText = 'display:flex;align-items:center;gap:6px;flex-shrink:0;';

    const tripValue = document.createElement('div');
    tripValue.style.cssText = `font-size:16px;font-weight:700;color:${C.gold};font-family:Consolas,monospace;`;
    tripValue.textContent = tripCostFormatted;

    const copyIcon = document.createElement('span');
    copyIcon.style.cssText = `color:${C.goldDim};font-size:11px;opacity:${tripCost > 0 ? '0.55' : '0'};transition:opacity 0.15s;user-select:none;flex-shrink:0;cursor:${tripCost > 0 ? 'pointer' : 'default'};`;
    copyIcon.title = 'Click to copy amount (no $ sign)';
    copyIcon.textContent = '⎘';

    tripValueWrap.appendChild(tripValue);
    tripValueWrap.appendChild(copyIcon);

    tripCard.appendChild(tripLabel);
    tripCard.appendChild(vaultLink);
    tripCard.appendChild(tripValueWrap);

    if (tripCost > 0) {
        // Hover feedback on whole card (but not when hovering the vault link)
        tripCard.addEventListener('mouseover', () => { tripCard.style.borderColor = 'rgba(0,200,224,0.4)'; copyIcon.style.opacity = '1'; });
        tripCard.addEventListener('mouseout',  () => { tripCard.style.borderColor = C.border; copyIcon.style.opacity = '0.55'; });

        // Copy on click of the copy icon only
        const doCopy = () => {
            navigator.clipboard.writeText(tripCostRaw).then(() => {
                tripValue.textContent = '✓ Copied!';
                tripValue.style.color = C.okay;
                setTimeout(() => { tripValue.textContent = tripCostFormatted; tripValue.style.color = C.gold; }, 1400);
            }).catch(() => {
                try {
                    const ta = document.createElement('textarea');
                    ta.value = tripCostRaw; ta.style.cssText = 'position:fixed;opacity:0;top:0;left:0;';
                    document.body.appendChild(ta); ta.select(); document.execCommand('copy'); ta.remove();
                    tripValue.textContent = '✓ Copied!';
                    tripValue.style.color = C.okay;
                    setTimeout(() => { tripValue.textContent = tripCostFormatted; tripValue.style.color = C.gold; }, 1400);
                } catch(e) {}
            });
        };
        copyIcon.addEventListener('click', e => { e.stopPropagation(); doCopy(); });
        tripValue.style.cursor = 'pointer';
        tripValue.addEventListener('click', e => { e.stopPropagation(); doCopy(); });
    }

    infSec.appendChild(tripCard);
 
    wrap.appendChild(cardSec); wrap.appendChild(infSec);
    renderXanRunSection(wrap, secTitle);
    body.appendChild(wrap);
}
 

/* ─────────────────────────────────────────
   PURE PROFIT POPUP
───────────────────────────────────────── */
function openProfitPopup() {
    const existing = document.getElementById('lt-profit-popup');
    if (existing) { existing.remove(); return; }

    const SPEED_NAMES    = ['Standard', 'Airstrip', 'Private Jet', 'Wind Lines'];
    const SPEED_SUB      = ['No upgrades', 'Level 1', 'Level 2', 'Level 3'];
    // Country buckets (one-way hours at Standard)
    const SHORT_CTRY  = ['Mexico', 'Hawaii'];
    const MEDIUM_CTRY = ['Canada', 'UK', 'Cayman Islands', 'Switzerland', 'Japan'];
    const LONG_CTRY   = ['Argentina', 'China', 'UAE', 'South Africa'];
    // Item type → group key
    const TYPE_KEYS   = { plushies: 'Plushies', flowers: 'Flowers', prehistoric: 'Prehistoric', special: 'special' };

    // Load persisted settings
    let curSpeed    = cfg.getTravelSpeed();
    let curCarry    = cfg.getXanCarry() || 1;
    let curTypes    = { ...cfg.getProfitItemTypes() };
    let curCountries= { ...cfg.getProfitCountries() };

    // ── Overlay ──
    const overlay = document.createElement('div');
    overlay.id = 'lt-profit-popup';
    overlay.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,0.72);z-index:1000050;display:flex;align-items:flex-start;justify-content:center;padding-top:4vh;box-sizing:border-box;overflow-y:auto;';
    overlay.addEventListener('click', e => { if (e.target === overlay) overlay.remove(); });

    const box = document.createElement('div');
    box.style.cssText = `background:${C.settBg};border:1px solid ${C.settBorder};border-radius:13px;width:310px;max-width:96vw;font-family:Arial,sans-serif;overflow:hidden;box-shadow:0 16px 60px rgba(0,0,0,0.9);margin-bottom:20px;color:${C.text};`;

    // ── Header ──
    const hdr = buildHdr('💰 Pure Profit', () => overlay.remove());
    box.appendChild(hdr);

    // ── Results area ──
    const resultsDiv = document.createElement('div');
    resultsDiv.style.cssText = 'padding:10px 12px 2px;';

    function fmtMoney(n) {
        return n >= 1e9 ? `$${(n/1e9).toFixed(2)}B` : n >= 1e6 ? `$${(n/1e6).toFixed(1)}M` : n >= 1e3 ? `$${Math.round(n/1000)}k` : `$${n}`;
    }

    function renderResults() {
        resultsDiv.innerHTML = '';
        // Build allowed country set
        const allowed = new Set();
        if (curCountries.short)  SHORT_CTRY.forEach(c  => allowed.add(c));
        if (curCountries.medium) MEDIUM_CTRY.forEach(c => allowed.add(c));
        if (curCountries.long)   LONG_CTRY.forEach(c   => allowed.add(c));

        // Build item list from selected types
        const allItems = [];
        Object.entries(TYPE_KEYS).forEach(([typeKey, groupName]) => {
            if (!curTypes[typeKey]) return;
            if (groupName === 'special') {
                Object.entries(SPECIAL_ITEMS).forEach(([name, data]) => {
                    if (allowed.has(data.loc)) allItems.push({ name, loc: data.loc, id: data.id });
                });
            } else {
                const g = GROUPS[groupName];
                if (!g) return;
                Object.entries(g.items).forEach(([name, data]) => {
                    if (!BOB_IDS.has(data.id) && allowed.has(data.loc)) allItems.push({ name, loc: data.loc, id: data.id });
                });
            }
        });
        // Drugs — Xanax only
        if (curTypes.drugs && allowed.has('South Africa')) {
            allItems.push({ name: 'Xanax', loc: 'South Africa', id: XANAX_ID });
        }

        const scored = [];
        allItems.forEach(item => {
            const cached    = yataPriceCache[item.name];
            if (!cached || !cached.price) return;
            const buyPrice  = cached.price;
            const sellPrice = item.name === 'Xanax' ? (xanSACache.price || 0) : (marketValueCache[item.name] || 0);
            if (!sellPrice || sellPrice <= buyPrice) return;
            const profit    = sellPrice - buyPrice;
            const times     = TRAVEL_TIMES[item.loc];
            if (!times) return;
            const travelHr    = times[curSpeed] || times[0];
            const totalHr     = travelHr * 2 + (90 / 3600);
            const profitPerHr = (profit * curCarry) / totalHr;
            scored.push({ name: item.name, id: item.id, loc: item.loc, buyPrice, sellPrice, profit, profitPerHr: Math.round(profitPerHr), tripProfit: profit * curCarry, travelHr });
        });
        scored.sort((a, b) => b.profitPerHr - a.profitPerHr);
        const top3 = scored.slice(0, 3);

        if (!top3.length) {
            const empty = document.createElement('div');
            const hasYata   = Object.keys(yataPriceCache).length > 0;
            const hasMarket = Object.keys(marketValueCache).length > 0;
            empty.style.cssText = `font-size:9.5px;color:${C.textDim};font-family:Consolas,monospace;padding:14px 0;text-align:center;`;
            empty.textContent = !hasYata ? 'Waiting for YATA price data…' : !hasMarket ? 'Waiting for Torn market values…' : 'No profitable items match your filters';
            resultsDiv.appendChild(empty);
            return;
        }

        // Speed pill
        const pill = document.createElement('div');
        pill.style.cssText = `display:inline-flex;align-items:center;padding:2px 8px;border-radius:10px;background:${C.goldGlow};border:1px solid ${C.gold};font-size:8px;font-family:Consolas,monospace;color:${C.gold};margin-bottom:8px;`;
        pill.textContent = '✈ ' + SPEED_NAMES[curSpeed] + '  ·  carry ' + curCarry;
        resultsDiv.appendChild(pill);

        const medals = ['🥇','🥈','🥉'];
        const medalColors = ['#ffd700','#c0c0c0','#cd7f32'];
        top3.forEach((item, idx) => {
            const loc    = LOCATIONS[item.loc] || { flag:'❓', label: item.loc };
            const col    = medalColors[idx];
            const card   = document.createElement('div');
            card.style.cssText = `display:flex;align-items:center;gap:8px;padding:8px 10px;border-radius:7px;background:${col}0d;border:1px solid ${col}33;margin-bottom:5px;`;

            const imgWrap = document.createElement('div'); imgWrap.style.cssText = 'position:relative;flex-shrink:0;';
            const img = document.createElement('img'); img.src = itemImg(item.id); img.alt = item.name;
            img.style.cssText = 'width:32px;height:32px;object-fit:contain;border-radius:3px;border:1px solid rgba(255,255,255,0.08);display:block;';
            img.addEventListener('error', () => { img.style.display='none'; });
            const medal = document.createElement('span'); medal.textContent = medals[idx];
            medal.style.cssText = 'position:absolute;bottom:-4px;right:-4px;font-size:11px;line-height:1;';
            imgWrap.appendChild(img); imgWrap.appendChild(medal);

            const mid = document.createElement('div'); mid.style.cssText = 'flex:1;min-width:0;';
            const nameEl = document.createElement('div'); nameEl.textContent = item.name;
            nameEl.style.cssText = `font-size:10px;font-weight:700;color:${C.text};font-family:Arial,sans-serif;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;`;
            const subRow = document.createElement('div'); subRow.style.cssText = 'display:flex;align-items:center;gap:4px;margin-top:2px;';
            const flagEl = makeFlagEl(item.loc, 14);
            const locEl  = document.createElement('span'); locEl.textContent = loc.label; locEl.style.cssText = `font-size:8px;color:${C.textDim};font-family:Consolas,monospace;`;
            const timeEl = document.createElement('span'); timeEl.textContent = item.travelHr + 'h'; timeEl.style.cssText = `font-size:8px;color:${C.textDim};font-family:Consolas,monospace;opacity:0.6;`;
            subRow.appendChild(flagEl); subRow.appendChild(locEl); subRow.appendChild(timeEl);
            const buyFmt  = fmtMoney(item.buyPrice); const sellFmt = fmtMoney(item.sellPrice);
            const priceRow = document.createElement('div'); priceRow.style.cssText = 'display:flex;align-items:center;gap:3px;margin-top:1px;';
            const buyEl  = document.createElement('span'); buyEl.textContent = buyFmt;   buyEl.style.cssText  = `font-size:7.5px;color:${C.stockLo};font-family:Consolas,monospace;`;
            const arrEl  = document.createElement('span'); arrEl.textContent = '→';      arrEl.style.cssText  = `font-size:7px;color:${C.textDim};`;
            const sellEl = document.createElement('span'); sellEl.textContent = sellFmt; sellEl.style.cssText = `font-size:7.5px;color:${C.okay};font-family:Consolas,monospace;`;
            priceRow.appendChild(buyEl); priceRow.appendChild(arrEl); priceRow.appendChild(sellEl);
            mid.appendChild(nameEl); mid.appendChild(subRow); mid.appendChild(priceRow);

            const right = document.createElement('div'); right.style.cssText = 'text-align:right;flex-shrink:0;';
            const phrEl = document.createElement('div'); phrEl.textContent = fmtMoney(item.profitPerHr) + '/hr';
            phrEl.style.cssText = `font-size:12px;font-weight:700;color:${col};font-family:Consolas,monospace;`;
            const tripEl = document.createElement('div'); tripEl.textContent = fmtMoney(item.tripProfit) + ' profit';
            tripEl.style.cssText = `font-size:8px;color:${C.textDim};font-family:Consolas,monospace;margin-top:1px;`;
            right.appendChild(phrEl); right.appendChild(tripEl);

            card.appendChild(imgWrap); card.appendChild(mid); card.appendChild(right);
            resultsDiv.appendChild(card);
        });
    }

    box.appendChild(resultsDiv);

    // ── Settings section ──
    const settDiv = document.createElement('div');
    settDiv.style.cssText = 'padding:10px 12px 14px;border-top:1px solid rgba(255,184,0,0.15);';

    function settSecHdr(txt) {
        const d = document.createElement('div'); d.textContent = txt;
        d.style.cssText = `font-size:9px;font-weight:700;letter-spacing:1px;text-transform:uppercase;color:rgba(255,184,0,0.6);margin:10px 0 6px;padding-bottom:4px;border-bottom:1px solid rgba(255,184,0,0.15);font-family:Consolas,monospace;`;
        return d;
    }

    // Item types
    settDiv.appendChild(settSecHdr('Items'));
    const typeGrid = document.createElement('div'); typeGrid.style.cssText = 'display:flex;flex-wrap:wrap;gap:4px;';
    const typeOpts = [
        { key:'plushies',    label:'🧸 Plushies'    },
        { key:'flowers',     label:'🌸 Flowers'     },
        { key:'prehistoric', label:'🪨 Prehistoric' },
        { key:'special',     label:'☄️ Special'    },
        { key:'drugs',       label:'💊 Drugs'       },
    ];
    typeOpts.forEach(opt => {
        const btn = document.createElement('button'); btn.type='button';
        const active = () => curTypes[opt.key];
        const setStyle = () => {
            btn.style.cssText = `padding:4px 9px;border-radius:5px;font-size:9px;font-weight:700;cursor:pointer;font-family:Arial,sans-serif;transition:all 0.15s;` +
                (active() ? 'background:rgba(255,184,0,0.18);border:1px solid rgba(255,184,0,0.55);color:#ffb800;'
                           : 'background:${C.card};border:1px solid ${C.border};color:${C.textDim};');
        };
        btn.textContent = opt.label; setStyle();
        btn.addEventListener('click', () => {
            curTypes[opt.key] = !curTypes[opt.key];
            setStyle();
            cfg.setProfitItemTypes({ ...curTypes });
            renderResults();
        });
        typeGrid.appendChild(btn);
    });
    settDiv.appendChild(typeGrid);

    // Country filter
    settDiv.appendChild(settSecHdr('Countries'));
    const countryGrid = document.createElement('div'); countryGrid.style.cssText = 'display:flex;gap:4px;';
    const countryOpts = [
        { key:'short',  label:'Short',  countries:['Mexico','Hawaii'] },
        { key:'medium', label:'Medium', countries:['Canada','UK','Switzerland','Japan','Cayman Islands'] },
        { key:'long',   label:'Long',   countries:['Argentina','China','UAE','South Africa'] },
    ];
    countryOpts.forEach(opt => {
        const btn = document.createElement('button'); btn.type='button';
        const active = () => curCountries[opt.key];
        const setStyle = () => {
            btn.style.cssText = `flex:1;padding:5px 3px;border-radius:5px;cursor:pointer;text-align:center;font-family:Arial,sans-serif;transition:all 0.15s;display:flex;flex-direction:column;align-items:center;gap:3px;` +
                (active() ? 'background:rgba(255,184,0,0.18);border:1px solid rgba(255,184,0,0.55);'
                           : `background:${C.card};border:1px solid ${C.border};`);
        };
        const buildInner = () => {
            btn.innerHTML = '';
            const lbl = document.createElement('div');
            lbl.textContent = opt.label;
            lbl.style.cssText = `font-size:9px;font-weight:700;color:${active() ? C.gold : C.text};font-family:Arial,sans-serif;`;
            btn.appendChild(lbl);
            const flagRow = document.createElement('div');
            flagRow.style.cssText = 'display:flex;flex-wrap:wrap;justify-content:center;gap:2px;';
            opt.countries.forEach(cName => {
                const f = makeFlagEl(cName, 14);
                f.style.borderRadius = '50%';
                flagRow.appendChild(f);
            });
            btn.appendChild(flagRow);
        };
        setStyle(); buildInner();
        btn.addEventListener('click', () => {
            curCountries[opt.key] = !curCountries[opt.key];
            setStyle(); buildInner();
            cfg.setProfitCountries({ ...curCountries });
            renderResults();
        });
        countryGrid.appendChild(btn);
    });
    settDiv.appendChild(countryGrid);

    // Travel speed
    settDiv.appendChild(settSecHdr('Travel Speed'));
    const speedGrid = document.createElement('div'); speedGrid.style.cssText = 'display:grid;grid-template-columns:1fr 1fr;gap:4px;';
    SPEED_NAMES.forEach((sName, si) => {
        const btn = document.createElement('button'); btn.type='button';
        const isActive = () => curSpeed === si;
        const setStyle = () => {
            btn.style.cssText = `padding:6px 4px;border-radius:5px;cursor:pointer;text-align:center;font-family:Arial,sans-serif;transition:all 0.15s;` +
                (isActive() ? 'background:rgba(0,200,224,0.15);border:1px solid rgba(0,200,224,0.55);'
                             : 'background:${C.card};border:1px solid ${C.border};');
        };
        const renderInner = () => {
            btn.innerHTML = `<div style="font-size:9px;font-weight:700;color:${isActive() ? C.gold : C.text};font-family:Arial,sans-serif;">${sName}</div><div style="font-size:7px;color:${C.textDim};font-family:Consolas,monospace;margin-top:1px;">${SPEED_SUB[si]}</div>`;
        };
        setStyle(); renderInner();
        btn.addEventListener('click', () => {
            curSpeed = si;
            cfg.setTravelSpeed(si);
            speedGrid.querySelectorAll('button').forEach((b, bi) => {
                b.style.cssText = `padding:6px 4px;border-radius:5px;cursor:pointer;text-align:center;font-family:Arial,sans-serif;transition:all 0.15s;` +
                    (bi === si ? 'background:rgba(0,200,224,0.15);border:1px solid rgba(0,200,224,0.55);'
                               : 'background:${C.card};border:1px solid ${C.border};');
                const nameDiv = b.querySelector('div');
                if (nameDiv) nameDiv.style.color = bi === si ? C.gold : C.textDim;
            });
            renderResults();
        });
        speedGrid.appendChild(btn);
    });
    settDiv.appendChild(speedGrid);

    box.appendChild(settDiv);
    overlay.appendChild(box);
    document.body.appendChild(overlay);

    renderResults();
}

/* ─────────────────────────────────────────
   TAB: TRAVEL
───────────────────────────────────────── */
function renderTravelBody() {
    const body = panelEl.querySelector('.lt-body');
    body.innerHTML = '';
 
    const vis = cfg.getSectionVis();
 
    const wrap = document.createElement('div');
    wrap.style.cssText = 'padding:10px;display:flex;flex-direction:column;gap:10px;';
 
    function secTitle(text) {
        const t = document.createElement('div'); t.textContent = text;
        t.style.cssText = `font-size:9px;font-weight:700;letter-spacing:1.2px;text-transform:uppercase;color:${C.goldDim};margin-bottom:6px;padding-bottom:4px;border-bottom:1px solid rgba(0,140,170,0.3);font-family:Consolas,monospace;`;
        return t;
    }
 
    // ── Pure Profit / Xanax Priority section ──
    const _prioCheck = cfg.getXanPriority() && xanPersonal < cfg.getXanThreshold();
    if (_prioCheck) {
        // Always open section when priority is active
        if (typeof renderTravelBody._xanOpen === 'undefined') renderTravelBody._xanOpen = true;
        // Priority active — render xanax country cards DIRECTLY (no collapsible wrapper)
        const xanSec = document.createElement('div');

        // Collapsible header — same pattern as Pure Profit
        if (typeof renderTravelBody._xanOpen === 'undefined') renderTravelBody._xanOpen = true;
        let xanSecOpen = renderTravelBody._xanOpen;

        const xanSecHdr = document.createElement('div');
        xanSecHdr.style.cssText = `display:flex;justify-content:space-between;align-items:center;padding:4px 10px;font-size:9px;font-weight:700;letter-spacing:1px;text-transform:uppercase;color:${C.goldDim};background:${C.goldGlow};border-top:1px solid ${C.border};border-bottom:1px solid ${C.border};font-family:Consolas,monospace;cursor:pointer;user-select:none;`;
        const xanSecLbl = document.createElement('span'); xanSecLbl.textContent = '🧪 Xanax Runs — sorted by $/hr'; xanSecLbl.style.cssText = 'flex:1;';
        const xanSecChev = document.createElement('span');
        xanSecChev.style.cssText = `color:${C.goldDim};font-size:10px;display:inline-block;transition:transform 0.2s;transform:${xanSecOpen ? 'rotate(180deg)' : 'rotate(0deg)'};`;
        xanSecChev.textContent = '▾';
        xanSecHdr.appendChild(xanSecLbl); xanSecHdr.appendChild(xanSecChev);
        xanSec.appendChild(xanSecHdr);

        const xanContent = document.createElement('div');
        xanContent.style.cssText = `overflow:hidden;max-height:${xanSecOpen ? '2000px' : '0'};transition:max-height 0.25s ease;padding:${xanSecOpen ? '8px 0 2px' : '0'};`;

        xanSecHdr.addEventListener('click', () => {
            xanSecOpen = !xanSecOpen;
            renderTravelBody._xanOpen = xanSecOpen;
            xanContent.style.maxHeight  = xanSecOpen ? '2000px' : '0';
            xanContent.style.padding    = xanSecOpen ? '8px 0 2px' : '0';
            xanSecChev.style.transform  = xanSecOpen ? 'rotate(180deg)' : 'rotate(0deg)';
        });

        const speedTier2 = cfg.getTravelSpeed();
        const carry2     = cfg.getXanCarry() || 1;
        const fmt2 = n => n >= 1e6 ? '$'+(n/1e6).toFixed(1)+'M' : n >= 1e3 ? '$'+Math.round(n/1000)+'k' : '$'+n;

        // Use active run sell price as the best proxy for Xanax sell value.
        // Torn market_value for drugs is 0 — we need a user-defined or run-based price.
        const _xanRuns2 = cfg.getXanRuns();
        const _activeRun2 = _xanRuns2.find(r => r.active && r.manualPrice > 0);
        const xanSellPrice2 = _activeRun2 ? _activeRun2.manualPrice
                            : (marketValueCache['Xanax'] > 0 ? marketValueCache['Xanax'] : 0);

        const xanCtries2 = Object.entries(yataCityCache)
            .map(([code, data]) => {
                const s = data.stocks && data.stocks.find(st => st.id === XANAX_ID);
                if (!s) return null; // show even 0-stock countries so user can plan
                const cKey = Object.keys(TORN_DEST_TO_CODE).find(k => TORN_DEST_TO_CODE[k] === code);
                const times = cKey ? TRAVEL_TIMES[cKey] : null;
                const tHr = times ? (times[speedTier2] || times[0]) : 99;
                const totalHr = tHr * 2 + (90/3600);
                const profit = xanSellPrice2 > 0 && xanSellPrice2 > s.cost ? xanSellPrice2 - s.cost : 0;
                // If no sell price set, use YATA buy price / r/t as efficiency metric
                const profPerHr = profit > 0 ? Math.round((profit * carry2) / totalHr) : 0;
                const hasRealProfit = profit > 0;
                const loc = cKey ? (LOCATIONS[cKey] || {flag:'✈️', label:cKey}) : {flag:'✈️', label:data.city};
                return { code, data, s, cKey, loc, tHr, profit, tripP: profit*carry2, tripCostV: s.cost*carry2, profPerHr, sellPrice: xanSellPrice2, hasRealProfit };
            })
            .filter(Boolean)
            .sort((a, b) => b.profPerHr - a.profPerHr);

        if (!xanCtries2.length) {
            const empty = document.createElement('div');
            empty.style.cssText = `font-size:9.5px;color:${C.textDim};font-family:Consolas,monospace;padding:8px 10px;text-align:center;`;
            empty.textContent = 'No Xanax overseas stock found — try refreshing';
            xanContent.appendChild(empty);
        } else {
            xanCtries2.forEach(({ code, data, s, loc, tHr, profit, tripP, tripCostV, profPerHr, hasRealProfit }) => {
                const stockH = cfg.getStockHistory('Xanax_' + code);
                const xWrap = document.createElement('div');
                xWrap.style.cssText = `border-radius:7px;border:1px solid ${C.border};background:${C.card};margin-bottom:5px;overflow:hidden;`;
                const xHdr = document.createElement('div');
                xHdr.style.cssText = 'display:flex;align-items:center;gap:8px;padding:8px 10px;cursor:pointer;position:relative;padding-right:20px;';
                xHdr.addEventListener('mouseover', () => xHdr.style.background = C.goldGlow);
                xHdr.addEventListener('mouseout',  () => xHdr.style.background = 'transparent');
                const xImg = document.createElement('img'); xImg.src = itemImg(XANAX_ID); xImg.alt='Xanax';
                xImg.style.cssText = 'width:32px;height:32px;object-fit:contain;border-radius:3px;border:1px solid rgba(255,255,255,0.08);flex-shrink:0;';
                xImg.addEventListener('error', ()=>xImg.style.display='none');
                const xMid = document.createElement('div'); xMid.style.cssText = 'flex:1;min-width:0;';
                xMid.innerHTML = `<div style="font-size:10px;font-weight:700;color:${C.text};font-family:Arial,sans-serif;">${data.city}</div>
                    <div style="display:flex;align-items:center;gap:4px;margin-top:2px;">
                        <span style="font-size:13px;">${loc.flag}</span>
                        <span style="font-size:8px;color:${C.textDim};font-family:Consolas,monospace;">${loc.label} · ${tHr}h r/t</span>
                    </div>`;
                const xRight = document.createElement('div'); xRight.style.cssText = 'text-align:right;flex-shrink:0;';
                const prColor = hasRealProfit ? C.okay : C.goldDim;
                const prLabel = hasRealProfit ? fmt2(profPerHr)+'/hr' : fmt2(profPerHr)+'/hr ⓘ';
                xRight.innerHTML = `<div style="font-size:12px;font-weight:700;color:${prColor};font-family:Consolas,monospace;" title="${hasRealProfit ? 'Profit/hr based on run price' : 'Buy cost/hr — set a run price for real profit'}">${prLabel}</div>
                    <div style="font-size:8px;color:${C.textDim};font-family:Consolas,monospace;margin-top:1px;">${s.qty.toLocaleString()} 🧪</div>`;
                const xChev = document.createElement('span');
                xChev.style.cssText = `position:absolute;right:6px;top:50%;transform:translateY(-50%);font-size:9px;color:${C.textDim};transition:transform 0.18s;`;
                xChev.textContent = '▾';
                xHdr.appendChild(xImg); xHdr.appendChild(xMid); xHdr.appendChild(xRight); xHdr.appendChild(xChev);
                let xOpen=false, xPop=false;
                const xDetail = document.createElement('div');
                xDetail.style.cssText = `overflow:hidden;max-height:0;transition:max-height 0.25s ease;background:${C.bg};`;
                xHdr.addEventListener('click', () => {
                    xOpen=!xOpen;
                    xChev.style.transform = xOpen?'translateY(-50%) rotate(180deg)':'translateY(-50%)';
                    xDetail.style.maxHeight = xOpen?'400px':'0';
                    if (xOpen && !xPop) {
                        xPop=true;
                        const xInner = document.createElement('div'); xInner.style.cssText='padding:8px 10px;display:flex;flex-direction:column;gap:6px;';
                        const xGrid = document.createElement('div'); xGrid.style.cssText='display:grid;grid-template-columns:repeat(3,1fr);gap:4px;';
                        [
                            {label:'Stock',      value:s.qty.toLocaleString(),  color:C.okay},
                            {label:'Buy/ea',     value:fmt2(s.cost),            color:C.stockLo},
                            {label:'Sell/ea',    value:xanSellPrice2>0?fmt2(xanSellPrice2):'Set run price', color:xanSellPrice2>0?C.okay:C.textDim},
                            {label:'Profit/ea',  value:profit>0?fmt2(profit):'—', color:'#66dd66'},
                            {label:'Trip Cost',  value:fmt2(tripCostV),         color:C.textDim},
                            {label:'Trip Profit',value:profit>0?fmt2(tripP):'—',color:'#66dd66'},
                            {label:'Travel',     value:tHr+'h r/t',             color:C.textDim},
                            {label:'$/hr',       value:profPerHr>0?fmt2(profPerHr):'—',color:C.okay},
                            {label:'Carry',      value:carry2+' 🧪',           color:C.goldDim},
                        ].forEach(({label,value,color})=>{
                            const cell=document.createElement('div');
                            cell.style.cssText=`background:${C.card};border:1px solid ${C.cardBorder};border-radius:4px;padding:5px 4px;text-align:center;`;
                            cell.innerHTML=`<div style="font-size:7px;color:${C.textDim};font-family:Consolas,monospace;margin-bottom:2px;">${label}</div><div style="font-size:9px;font-weight:700;color:${color};font-family:Consolas,monospace;">${value}</div>`;
                            xGrid.appendChild(cell);
                        });
                        xInner.appendChild(xGrid);
                        if (stockH.length>=2) {
                            const sLbl=document.createElement('div'); sLbl.style.cssText=`font-size:8px;font-weight:700;color:${C.goldDim};font-family:Consolas,monospace;`;
                            sLbl.textContent='📦 YATA Xanax Stock History (' + fmtHistorySpan(stockH) + ')';
                            xInner.appendChild(sLbl);
                            const vals=stockH.map(h=>h.qty!==undefined?h.qty:h.price);
                            const minV=Math.min(...vals),maxV=Math.max(...vals),rng=maxV-minV||1;
                            const svg=document.createElementNS('http://www.w3.org/2000/svg','svg');
                            svg.setAttribute('viewBox','0 0 260 36'); svg.setAttribute('width','100%'); svg.setAttribute('height','36');
                            const pts=vals.map((v,i)=>[4+(i/(vals.length-1))*252, 4+(1-(v-minV)/rng)*28]);
                            const pd='M'+pts.map(([x,y])=>x.toFixed(1)+','+y.toFixed(1)).join('L');
                            const f=document.createElementNS('http://www.w3.org/2000/svg','path'); f.setAttribute('d',pd+'L'+pts[pts.length-1][0].toFixed(1)+',36L'+pts[0][0].toFixed(1)+',36Z'); f.setAttribute('fill','rgba(102,187,102,0.1)');
                            const l=document.createElementNS('http://www.w3.org/2000/svg','path'); l.setAttribute('d',pd); l.setAttribute('fill','none'); l.setAttribute('stroke','rgba(102,187,102,0.8)'); l.setAttribute('stroke-width','1.5');
                            const d=document.createElementNS('http://www.w3.org/2000/svg','circle'); d.setAttribute('cx',pts[pts.length-1][0].toFixed(1)); d.setAttribute('cy',pts[pts.length-1][1].toFixed(1)); d.setAttribute('r','3'); d.setAttribute('fill','#66cc66');
                            svg.appendChild(f); svg.appendChild(l); svg.appendChild(d);
                            xInner.appendChild(svg);
                        }
                        xDetail.appendChild(xInner);
                    }
                });
                xWrap.appendChild(xHdr); xWrap.appendChild(xDetail);
                xanContent.appendChild(xWrap);
            });
        }
        xanSec.appendChild(xanContent);
        wrap.appendChild(xanSec);
    } else {
    (function renderPureProfit() {
        const profitSec = document.createElement('div');

        const speedTier  = cfg.getTravelSpeed();
        const carry      = cfg.getXanCarry() || 1;
        const speedNames = ['Standard', 'Airstrip', 'Private Jet', 'Wind Lines'];
        const curTypes   = cfg.getProfitItemTypes();
        const curCtry    = cfg.getProfitCountries();
        const SHORT_CTRY  = ['Mexico', 'Hawaii'];
        const MEDIUM_CTRY = ['Canada', 'UK', 'Cayman Islands', 'Switzerland', 'Japan'];
        const LONG_CTRY   = ['Argentina', 'China', 'UAE', 'South Africa'];
        const TYPE_KEYS   = { plushies: 'Plushies', flowers: 'Flowers', prehistoric: 'Prehistoric', special: 'special' };

        if (typeof renderPureProfit._open === 'undefined') renderPureProfit._open = false;
        let isOpen = renderPureProfit._open;

        // Check xanax priority early — force section open if active
        const _xp2thresh_early = cfg.getXanThreshold();
        const xanPrioActive2   = cfg.getXanPriority() && xanPersonal < _xp2thresh_early;
        if (xanPrioActive2 && !isOpen) {
            isOpen = true;
            renderPureProfit._open = true;
        }

        // ── Header row: title | ⚙ | ▾ ──
        const hdrRow = document.createElement('div');
        hdrRow.style.cssText = `display:flex;justify-content:space-between;align-items:center;padding:4px 10px;font-size:9px;font-weight:700;letter-spacing:1px;text-transform:uppercase;color:${C.goldDim};background:rgba(0,60,80,0.15);border-top:1px solid rgba(0,140,180,0.2);border-bottom:1px solid rgba(0,140,180,0.15);font-family:Consolas,monospace;user-select:none;`;

        const hdrLeft = document.createElement('span');
        hdrLeft.style.cssText = 'cursor:pointer;flex:1;';
        hdrLeft.textContent = '💰 Pure Profit';

        const hdrRight = document.createElement('div');
        hdrRight.style.cssText = 'display:flex;align-items:center;gap:6px;';

        // ⚙ settings icon
        const gearBtn = document.createElement('span');
        gearBtn.title = 'Profit settings';
        gearBtn.innerHTML = '<svg width="13" height="13" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg"><path d="M12 15.5A3.5 3.5 0 0 1 8.5 12 3.5 3.5 0 0 1 12 8.5a3.5 3.5 0 0 1 3.5 3.5 3.5 3.5 0 0 1-3.5 3.5m7.43-2.92c.04-.34.07-.69.07-1.08s-.03-.73-.07-1.08l2.33-1.82c.21-.16.27-.45.13-.69l-2.2-3.81c-.14-.24-.42-.32-.67-.24l-2.75 1.1c-.57-.44-1.18-.8-1.85-1.08L14.14 2.1c-.05-.27-.27-.47-.55-.47H10.41c-.28 0-.5.2-.55.47L9.42 5.08C8.75 5.36 8.14 5.72 7.57 6.16l-2.75-1.1c-.25-.08-.53 0-.67.24l-2.2 3.81c-.14.24-.08.53.13.69l2.33 1.82C4.37 11.27 4.34 11.62 4.34 12s.03.73.07 1.08L2.08 14.9c-.21.16-.27.45-.13.69l2.2 3.81c.14.24.42.32.67.24l2.75-1.1c.57.44 1.18.8 1.85 1.08l.43 2.98c.05.27.27.47.55.47h4.18c.28 0 .5-.2.55-.47l.43-2.98c.67-.28 1.28-.64 1.85-1.08l2.75 1.1c.25.08.53 0 .67-.24l2.2-3.81c.14-.24.08-.53-.13-.69l-2.33-1.82z"/></svg>';
        gearBtn.style.cssText = `cursor:pointer;display:flex;align-items:center;color:${C.goldDim};opacity:0.7;transition:opacity 0.15s;padding:2px;`;
        gearBtn.addEventListener('mouseover', () => { gearBtn.style.opacity = '1'; gearBtn.style.color = C.gold; });
        gearBtn.addEventListener('mouseout',  () => { gearBtn.style.opacity = '0.7'; gearBtn.style.color = C.goldDim; });
        gearBtn.addEventListener('click', e => { e.stopPropagation(); openProfitPopup(); });

        // ▾ chevron
        const chevron = document.createElement('span');
        chevron.style.cssText = `color:${C.gold};font-size:10px;display:inline-block;transition:transform 0.2s;transform:${isOpen ? 'rotate(180deg)' : 'rotate(0deg)'};cursor:pointer;`;
        chevron.textContent = '▾';

        hdrRight.appendChild(gearBtn);
        hdrRight.appendChild(chevron);
        hdrRow.appendChild(hdrLeft);
        hdrRow.appendChild(hdrRight);
        profitSec.appendChild(hdrRow);

        // clicking title or chevron toggles open/close
        const toggleOpen = () => {
            isOpen = !isOpen;
            renderPureProfit._open = isOpen;
            content.style.maxHeight = isOpen ? '600px' : '0';
            content.style.padding   = isOpen ? '8px 0 2px' : '0';
            chevron.style.transform = isOpen ? 'rotate(180deg)' : 'rotate(0deg)';
        };
        hdrLeft.addEventListener('click', toggleOpen);
        chevron.addEventListener('click', toggleOpen);

        // Collapsible content
        const content = document.createElement('div');
        content.style.cssText = `overflow:hidden;transition:max-height 0.25s ease;max-height:${isOpen ? '600px' : '0'};padding:${isOpen ? '8px 0 2px' : '0'};`;

        // Build allowed country set
        const allowed = new Set();
        if (curCtry.short)  SHORT_CTRY.forEach(c  => allowed.add(c));
        if (curCtry.medium) MEDIUM_CTRY.forEach(c => allowed.add(c));
        if (curCtry.long)   LONG_CTRY.forEach(c   => allowed.add(c));

        // Build item list
        const allItems = [];
        Object.entries(TYPE_KEYS).forEach(([typeKey, groupName]) => {
            if (!curTypes[typeKey]) return;
            if (groupName === 'special') {
                Object.entries(SPECIAL_ITEMS).forEach(([name, data]) => {
                    if (allowed.has(data.loc)) allItems.push({ name, loc: data.loc, id: data.id });
                });
            } else {
                const g = GROUPS[groupName]; if (!g) return;
                Object.entries(g.items).forEach(([name, data]) => {
                    if (!BOB_IDS.has(data.id) && allowed.has(data.loc)) allItems.push({ name, loc: data.loc, id: data.id });
                });
            }
        });
        if (curTypes.drugs && allowed.has('South Africa')) {
            allItems.push({ name: 'Xanax', loc: 'South Africa', id: XANAX_ID });
        }

        // Score
        const scored = [];
        allItems.forEach(item => {
            const cached    = yataPriceCache[item.name];
            if (!cached || !cached.price) return;
            const buyPrice  = cached.price;
            const sellPrice = item.name === 'Xanax' ? (xanSACache.price || 0) : (marketValueCache[item.name] || 0);
            if (!sellPrice || sellPrice <= buyPrice) return;
            const profit    = sellPrice - buyPrice;
            const times     = TRAVEL_TIMES[item.loc]; if (!times) return;
            const travelHr  = times[speedTier] || times[0];
            const totalHr   = travelHr * 2 + (90 / 3600);
            scored.push({
                name: item.name, id: item.id, loc: item.loc,
                buyPrice, sellPrice, profit,
                profitPerHr: Math.round((profit * carry) / totalHr),
                tripProfit:  profit * carry,
                travelHr,
            });
        });
        scored.sort((a, b) => b.profitPerHr - a.profitPerHr);
        const top3 = scored.slice(0, 3);

        // ── When xanax priority active AND below threshold, replace top-3 with xanax country cards ──
        // (xanPrioActive2 already computed above, isOpen already forced open)
        if (xanPrioActive2) {
            const _xanRuns3 = cfg.getXanRuns();
            const _activeRun3 = _xanRuns3.find(r => r.active && r.manualPrice > 0);
            const xanSellPrice3 = _activeRun3 ? _activeRun3.manualPrice
                                : (marketValueCache['Xanax'] > 0 ? marketValueCache['Xanax'] : 0);
            const xanCtries = Object.entries(yataCityCache)
                .map(([code, data]) => {
                    const s = data.stocks && data.stocks.find(st => st.id === XANAX_ID);
                    if (!s || s.qty <= 0) return null;
                    const cKey    = Object.keys(TORN_DEST_TO_CODE).find(k => TORN_DEST_TO_CODE[k] === code);
                    const times   = cKey ? TRAVEL_TIMES[cKey] : null;
                    const tHr     = times ? (times[speedTier] || times[0]) : 99;
                    const totalHr = tHr * 2 + (90/3600);
                    const profit  = xanSellPrice3 > s.cost ? xanSellPrice3 - s.cost : 0;
                    const tripP   = profit * carry;
                    const tripCostV = s.cost * carry;
                    const profPerHr = profit > 0 ? Math.round((profit * carry) / totalHr) : 0;
                    const hasRealProfit3 = profit > 0;
                    const loc = cKey ? (LOCATIONS[cKey] || {flag:'✈️', label: cKey}) : {flag:'✈️', label: data.city};
                    return { code, data, s, cKey, loc, tHr, profit, tripP, tripCostV, profPerHr };
                })
                .filter(Boolean)
                .sort((a, b) => b.profPerHr - a.profPerHr);

            if (!xanCtries.length) {
                const empty2 = document.createElement('div');
                empty2.style.cssText = `font-size:9.5px;color:${C.textDim};font-family:Consolas,monospace;padding:8px 10px;text-align:center;`;
                empty2.textContent = 'No Xanax overseas stock found — try refreshing';
                content.appendChild(empty2);
            } else {
                const xHdr2 = document.createElement('div');
                xHdr2.style.cssText = `display:inline-flex;align-items:center;gap:4px;padding:2px 8px;border-radius:10px;background:${C.goldGlow};border:1px solid ${C.border};font-size:8px;font-family:Consolas,monospace;color:${C.goldDim};margin:0 10px 7px;`;
                xHdr2.textContent = '🧪 Xanax Runs — sorted by $/hr';
                content.appendChild(xHdr2);

                const fmt2 = n => n >= 1e6 ? '$'+(n/1e6).toFixed(1)+'M' : n >= 1e3 ? '$'+Math.round(n/1000)+'k' : '$'+n;
                xanCtries.forEach(({ data, s, loc, tHr, profit, tripP, tripCostV, profPerHr }) => {
                    const stockH2 = cfg.getStockHistory('Xanax_' + code);
                    const xWrap = document.createElement('div');
                    xWrap.style.cssText = `border-radius:7px;border:1px solid ${C.border};background:${C.card};margin:0 8px 5px;overflow:hidden;`;
                    const xCardHdr = document.createElement('div');
                    xCardHdr.style.cssText = 'display:flex;align-items:center;gap:8px;padding:8px 10px;cursor:pointer;position:relative;padding-right:20px;';
                    xCardHdr.addEventListener('mouseover', () => xCardHdr.style.background = C.goldGlow);
                    xCardHdr.addEventListener('mouseout',  () => xCardHdr.style.background = 'transparent');

                    const xImgEl = document.createElement('img'); xImgEl.src = itemImg(XANAX_ID); xImgEl.alt = 'Xanax';
                    xImgEl.style.cssText = 'width:32px;height:32px;object-fit:contain;border-radius:3px;border:1px solid rgba(255,255,255,0.08);flex-shrink:0;';
                    xImgEl.addEventListener('error', () => xImgEl.style.display='none');

                    const xMidEl = document.createElement('div'); xMidEl.style.cssText = 'flex:1;min-width:0;';
                    xMidEl.innerHTML = `<div style="font-size:10px;font-weight:700;color:${C.text};font-family:Arial,sans-serif;">${data.city}</div>
                        <div style="display:flex;align-items:center;gap:4px;margin-top:2px;">
                            <span style="font-size:13px;">${loc.flag}</span>
                            <span style="font-size:8px;color:${C.textDim};font-family:Consolas,monospace;">${loc.label} · ${tHr}h r/t</span>
                        </div>`;

                    const xRightEl = document.createElement('div'); xRightEl.style.cssText = 'text-align:right;flex-shrink:0;';
                    xRightEl.innerHTML = `<div style="font-size:12px;font-weight:700;color:${C.okay};font-family:Consolas,monospace;">${fmt2(profPerHr)}/hr</div>
                        <div style="font-size:8px;color:${C.textDim};font-family:Consolas,monospace;margin-top:1px;">${s.qty.toLocaleString()} 🧪</div>`;

                    const xChev = document.createElement('span');
                    xChev.style.cssText = `position:absolute;right:6px;top:50%;transform:translateY(-50%);font-size:9px;color:${C.textDim};transition:transform 0.18s;`;
                    xChev.textContent = '▾';

                    xCardHdr.appendChild(xImgEl); xCardHdr.appendChild(xMidEl); xCardHdr.appendChild(xRightEl); xCardHdr.appendChild(xChev);

                    let xOpen = false;
                    const xDetailEl = document.createElement('div');
                    xDetailEl.style.cssText = `overflow:hidden;max-height:0;transition:max-height 0.25s ease;background:${C.bg};`;
                    let xPop = false;

                    xCardHdr.addEventListener('click', () => {
                        xOpen = !xOpen;
                        xChev.style.transform = xOpen ? 'translateY(-50%) rotate(180deg)' : 'translateY(-50%)';
                        xDetailEl.style.maxHeight = xOpen ? '400px' : '0';
                        if (xOpen && !xPop) {
                            xPop = true;
                            const xInner = document.createElement('div'); xInner.style.cssText = 'padding:8px 10px;display:flex;flex-direction:column;gap:6px;';
                            const xGrid = document.createElement('div'); xGrid.style.cssText = 'display:grid;grid-template-columns:repeat(3,1fr);gap:4px;';
                            [
                                { label:'Stock',       value:s.qty.toLocaleString(),    color:C.okay    },
                                { label:'Buy/ea',      value:fmt2(s.cost),              color:C.stockLo },
                                { label:'Sell/ea',     value:xanSellPrice3>0?fmt2(xanSellPrice3):'Set run price', color:xanSellPrice3>0?C.okay:C.textDim },
                                { label:'Profit/ea',   value:profit>0?fmt2(profit):'—', color:'#66dd66' },
                                { label:'Trip Cost',   value:fmt2(tripCostV),           color:C.textDim },
                                { label:'Trip Profit', value:profit>0?fmt2(tripP):'—',  color:'#66dd66' },
                                { label:'Travel',      value:tHr+'h r/t',               color:C.textDim },
                                { label:'$/hr',        value:profPerHr>0?fmt2(profPerHr):'—', color:'#66cc66' },
                                { label:'Carry',       value:carry+' 🧪',              color:C.goldDim },
                            ].forEach(({ label, value, color }) => {
                                const cell = document.createElement('div');
                                cell.style.cssText = `background:${C.card};border:1px solid ${C.cardBorder};border-radius:4px;padding:5px 4px;text-align:center;`;
                                cell.innerHTML = `<div style="font-size:7px;color:${C.textDim};font-family:Consolas,monospace;margin-bottom:2px;">${label}</div><div style="font-size:9px;font-weight:700;color:${color};font-family:Consolas,monospace;">${value}</div>`;
                                xGrid.appendChild(cell);
                            });
                            xInner.appendChild(xGrid);
                            if (stockH2.length >= 2) {
                                const sLbl2 = document.createElement('div'); sLbl2.style.cssText = `font-size:8px;font-weight:700;letter-spacing:.5px;color:${C.goldDim};font-family:Consolas,monospace;`;
                                sLbl2.textContent = '📦 YATA Xanax Stock History (' + fmtHistorySpan(stockH2) + ')';
                                xInner.appendChild(sLbl2);
                                const vals2 = stockH2.map(h => h.qty !== undefined ? h.qty : h.price);
                                const minV2=Math.min(...vals2), maxV2=Math.max(...vals2), rng2=maxV2-minV2||1;
                                const W3=260,H3=36,p3=4;
                                const svg3=document.createElementNS('http://www.w3.org/2000/svg','svg');
                                svg3.setAttribute('viewBox','0 0 '+W3+' '+H3); svg3.setAttribute('width','100%'); svg3.setAttribute('height',H3);
                                const pts3=vals2.map((v,i)=>[p3+(i/(vals2.length-1))*(W3-p3*2), p3+(1-(v-minV2)/rng2)*(H3-p3*2)]);
                                const pd3='M'+pts3.map(([x,y])=>x.toFixed(1)+','+y.toFixed(1)).join('L');
                                const f3=document.createElementNS('http://www.w3.org/2000/svg','path'); f3.setAttribute('d',pd3+'L'+pts3[pts3.length-1][0].toFixed(1)+','+H3+'L'+pts3[0][0].toFixed(1)+','+H3+'Z'); f3.setAttribute('fill','rgba(102,187,102,0.1)');
                                const l3=document.createElementNS('http://www.w3.org/2000/svg','path'); l3.setAttribute('d',pd3); l3.setAttribute('fill','none'); l3.setAttribute('stroke','rgba(102,187,102,0.8)'); l3.setAttribute('stroke-width','1.5');
                                const d3=document.createElementNS('http://www.w3.org/2000/svg','circle'); d3.setAttribute('cx',pts3[pts3.length-1][0].toFixed(1)); d3.setAttribute('cy',pts3[pts3.length-1][1].toFixed(1)); d3.setAttribute('r','3'); d3.setAttribute('fill','#66cc66');
                                svg3.appendChild(f3); svg3.appendChild(l3); svg3.appendChild(d3);
                                xInner.appendChild(svg3);
                            }
                            xDetailEl.appendChild(xInner);
                        }
                    });
                    xWrap.appendChild(xCardHdr); xWrap.appendChild(xDetailEl);
                    content.appendChild(xWrap);
                });
            }
        } else if (!top3.length) {
            const empty = document.createElement('div');
            empty.style.cssText = `font-size:9.5px;color:${C.textDim};font-family:Consolas,monospace;padding:8px 10px;text-align:center;`;
            const hasYata   = Object.keys(yataPriceCache).length > 0;
            const hasMarket = Object.keys(marketValueCache).length > 0;
            empty.textContent = !hasYata ? 'Waiting for YATA price data…'
                              : !hasMarket ? 'Waiting for Torn market values…'
                              : 'No profitable items match your filters';
            content.appendChild(empty);
        } else {
            const tierPill = document.createElement('div');
            tierPill.style.cssText = `display:inline-flex;align-items:center;gap:4px;padding:2px 8px;border-radius:10px;background:rgba(0,200,224,0.08);border:1px solid rgba(0,200,224,0.25);font-size:8px;font-family:Consolas,monospace;color:${C.goldDim};margin:0 10px 7px;`;
            tierPill.textContent = '✈ ' + speedNames[speedTier];
            content.appendChild(tierPill);

            const medals      = ['🥇','🥈','🥉'];
            const medalColors = ['#ffd700','#c0c0c0','#cd7f32'];

            top3.forEach((item, idx) => {
                const loc     = LOCATIONS[item.loc] || { flag:'❓', label: item.loc };
                const col     = medalColors[idx];
                const fmt     = n => n >= 1e9 ? `$${(n/1e9).toFixed(2)}B` : n >= 1e6 ? `$${(n/1e6).toFixed(1)}M` : n >= 1e3 ? `$${Math.round(n/1000)}k` : `$${n}`;
                const fmtPts  = n => pointsPrice > 0 ? (n/pointsPrice).toFixed(2)+' pts' : '';
                const phrFmt  = fmt(item.profitPerHr) + '/hr';
                const tripFmt = fmt(item.tripProfit)  + ' profit';
                const buyFmt  = fmt(item.buyPrice);
                const sellFmt = fmt(item.sellPrice);
                const stockH  = cfg.getStockHistory(item.name);

                // Expandable wrapper
                const cardWrap = document.createElement('div');
                cardWrap.style.cssText = `border-radius:7px;border:1px solid ${col}33;background:${col}0d;margin:0 8px 5px;overflow:hidden;`;

                // Header (always visible)
                const cardHdr = document.createElement('div');
                cardHdr.style.cssText = `display:flex;align-items:center;gap:8px;padding:8px 10px;cursor:pointer;position:relative;padding-right:20px;`;
                cardHdr.addEventListener('mouseover', () => cardHdr.style.background = col+'18');
                cardHdr.addEventListener('mouseout',  () => cardHdr.style.background = 'transparent');

                const imgWrap = document.createElement('div'); imgWrap.style.cssText = 'position:relative;flex-shrink:0;';
                const img = document.createElement('img'); img.src = itemImg(item.id); img.alt = item.name;
                img.style.cssText = 'width:32px;height:32px;object-fit:contain;border-radius:3px;border:1px solid rgba(255,255,255,0.08);display:block;';
                img.addEventListener('error', () => { img.style.display='none'; });
                const medal = document.createElement('span'); medal.textContent = medals[idx];
                medal.style.cssText = 'position:absolute;bottom:-4px;right:-4px;font-size:11px;line-height:1;';
                imgWrap.appendChild(img); imgWrap.appendChild(medal);

                const mid = document.createElement('div'); mid.style.cssText = 'flex:1;min-width:0;';
                const nameEl = document.createElement('div'); nameEl.textContent = item.name;
                nameEl.style.cssText = `font-size:10px;font-weight:700;color:${C.text};font-family:Arial,sans-serif;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;`;
                const subRow = document.createElement('div'); subRow.style.cssText = 'display:flex;align-items:center;gap:4px;margin-top:2px;';
                const flagEl = makeFlagEl(item.loc, 14);
                const locEl  = document.createElement('span'); locEl.textContent = loc.label; locEl.style.cssText = `font-size:8px;color:${C.textDim};font-family:Consolas,monospace;`;
                const timeEl = document.createElement('span'); timeEl.textContent = item.travelHr+'h'; timeEl.style.cssText = `font-size:8px;color:${C.textDim};font-family:Consolas,monospace;opacity:0.6;`;
                subRow.appendChild(flagEl); subRow.appendChild(locEl); subRow.appendChild(timeEl);
                mid.appendChild(nameEl); mid.appendChild(subRow);

                const right = document.createElement('div'); right.style.cssText = 'text-align:right;flex-shrink:0;';
                const phrEl = document.createElement('div'); phrEl.textContent = phrFmt;
                phrEl.style.cssText = `font-size:12px;font-weight:700;color:${col};font-family:Consolas,monospace;`;
                const tripEl2 = document.createElement('div'); tripEl2.textContent = tripFmt;
                tripEl2.style.cssText = `font-size:8px;color:${C.textDim};font-family:Consolas,monospace;margin-top:1px;`;
                right.appendChild(phrEl); right.appendChild(tripEl2);

                const profChev = document.createElement('span');
                profChev.style.cssText = `position:absolute;right:6px;top:50%;transform:translateY(-50%);font-size:9px;color:${C.textDim};transition:transform 0.18s;`;
                profChev.textContent = '▾';

                cardHdr.appendChild(imgWrap); cardHdr.appendChild(mid); cardHdr.appendChild(right); cardHdr.appendChild(profChev);

                // Expandable detail
                let pOpen = false;
                const pDetail = document.createElement('div');
                pDetail.style.cssText = `overflow:hidden;max-height:0;transition:max-height 0.25s ease;background:${C.bg};border-top:0px solid ${col}22;`;
                let pPopulated = false;

                cardHdr.addEventListener('click', () => {
                    pOpen = !pOpen;
                    profChev.style.transform = pOpen ? 'translateY(-50%) rotate(180deg)' : 'translateY(-50%)';
                    pDetail.style.maxHeight  = pOpen ? '400px' : '0';
                    pDetail.style.borderTopWidth = pOpen ? '1px' : '0';
                    if (pOpen && !pPopulated) {
                        pPopulated = true;
                        const pInner = document.createElement('div'); pInner.style.cssText = 'padding:8px 10px;display:flex;flex-direction:column;gap:6px;';

                        // Stat grid
                        const pGrid = document.createElement('div'); pGrid.style.cssText = 'display:grid;grid-template-columns:repeat(3,1fr);gap:4px;';
                        const ptsEq = fmtPts(item.buyPrice);
                        [
                            { label:'Item ID',      value:'#'+item.id,           color:C.textDim },
                            { label:'Buy Price',    value:buyFmt,                color:C.stockLo },
                            { label:'Sell Value',   value:sellFmt,               color:C.okay    },
                            { label:'Profit/item',  value:fmt(item.profit),      color:'#66dd66' },
                            { label:'Trip Profit',  value:tripFmt,               color:col       },
                            { label:'$/hr',         value:phrFmt,                color:col       },
                            { label:'Trip Cost',    value:fmt(item.buyPrice*carry), color:C.textDim },
                            { label:'Travel Time',  value:item.travelHr+'h r/t', color:C.textDim },
                            { label:'Pts Equiv',    value:ptsEq||'—',            color:C.goldDim },
                        ].forEach(({ label, value, color }) => {
                            const cell = document.createElement('div');
                            cell.style.cssText = `background:${C.card};border:1px solid ${C.cardBorder};border-radius:4px;padding:5px 4px;text-align:center;`;
                            cell.innerHTML = `<div style="font-size:7px;color:${C.textDim};font-family:Consolas,monospace;margin-bottom:2px;">${label}</div><div style="font-size:9px;font-weight:700;color:${color};font-family:Consolas,monospace;">${value}</div>`;
                            pGrid.appendChild(cell);
                        });
                        pInner.appendChild(pGrid);

                        // YATA Stock sparkline
                        if (stockH.length >= 2) {
                            const sLbl = document.createElement('div'); sLbl.style.cssText = `font-size:8px;font-weight:700;letter-spacing:.5px;color:${C.goldDim};font-family:Consolas,monospace;`;
                            sLbl.textContent = '📦 YATA Stock History (' + fmtHistorySpan(stockH) + ')';
                            pInner.appendChild(sLbl);
                            const vals = stockH.map(h => h.qty !== undefined ? h.qty : h.price);
                            const minV = Math.min(...vals), maxV = Math.max(...vals), rng = maxV-minV||1;
                            const W2=260,H2=36,p2=4;
                            const svg2 = document.createElementNS('http://www.w3.org/2000/svg','svg');
                            svg2.setAttribute('viewBox','0 0 '+W2+' '+H2); svg2.setAttribute('width','100%'); svg2.setAttribute('height',H2);
                            const pts2 = vals.map((v,i)=>[p2+(i/(vals.length-1))*(W2-p2*2), p2+(1-(v-minV)/rng)*(H2-p2*2)]);
                            const pd2 = 'M'+pts2.map(([x,y])=>x.toFixed(1)+','+y.toFixed(1)).join('L');
                            const f2=document.createElementNS('http://www.w3.org/2000/svg','path'); f2.setAttribute('d',pd2+'L'+pts2[pts2.length-1][0].toFixed(1)+','+H2+'L'+pts2[0][0].toFixed(1)+','+H2+'Z'); f2.setAttribute('fill','rgba(0,200,224,0.08)');
                            const l2=document.createElementNS('http://www.w3.org/2000/svg','path'); l2.setAttribute('d',pd2); l2.setAttribute('fill','none'); l2.setAttribute('stroke',col); l2.setAttribute('stroke-width','1.5');
                            const d2=document.createElementNS('http://www.w3.org/2000/svg','circle'); d2.setAttribute('cx',pts2[pts2.length-1][0].toFixed(1)); d2.setAttribute('cy',pts2[pts2.length-1][1].toFixed(1)); d2.setAttribute('r','3'); d2.setAttribute('fill',col);
                            svg2.appendChild(f2); svg2.appendChild(l2); svg2.appendChild(d2);
                            pInner.appendChild(svg2);
                        } else {
                            const noH = document.createElement('div'); noH.style.cssText = `font-size:8px;color:${C.textDim};font-family:Consolas,monospace;text-align:center;padding:4px 0;`;
                            noH.textContent = 'Stock history builds up over refreshes';
                            pInner.appendChild(noH);
                        }

                        pDetail.appendChild(pInner);
                    }
                });

                cardWrap.appendChild(cardHdr); cardWrap.appendChild(pDetail);
                content.appendChild(cardWrap);
            });
        } // end normal top3 (inside xanPrioActive2 else)

        profitSec.appendChild(content);
        wrap.appendChild(profitSec);
    })();
    } // end else (not priority)

    // ── Loot Run Planner ──
    const escSec = document.createElement('div'); escSec.appendChild(secTitle('💰 Loot Run Planner'));
 
    const lootRuns = [
        {
            flag: '🇲🇽', name: 'Mexico', col: '#ffb830', time: '~1.5h',
            loot: ['Dahlia', 'Jaguar Plushie', 'Obsidian Point'],
        },
        {
            flag: '🇦🇷', name: 'Argentina', col: '#74c9ff', time: '~14h',
            loot: ['Ceibo Flower', 'Monkey Plushie', 'Chalcedony Point', 'Meteorite Fragment', 'Patagonian Fossil'],
        },
        {
            flag: '🇬🇧', name: 'United Kingdom', col: '#cc88ff', time: '~10h',
            loot: ['Heather', 'Nessie Plushie', 'Red Fox Plushie', 'Chert Point', 'Xanax'],
        },
        {
            flag: '🇨🇦', name: 'Canada', col: '#ff7070', time: '~9h',
            loot: ['Crocus', 'Wolverine Plushie', 'Quartz Point', 'Xanax'],
        },
        {
            flag: '🇿🇦', name: 'South Africa', col: '#60cc60', time: '~16h',
            loot: ['African Violet', 'Lion Plushie', 'Quartzite Point', 'Xanax'],
        },
        {
            flag: '🇨🇭', name: 'Switzerland', col: '#ff9999', time: '~11h',
            loot: ['Edelweiss', 'Chamois Plushie'],
        },
        {
            flag: '🇯🇵', name: 'Japan', col: '#ffaacc', time: '~12h',
            loot: ['Cherry Blossom', 'Xanax'],
        },
        {
            flag: '🇨🇳', name: 'China', col: '#ff6040', time: '~14h',
            loot: ['Peony', 'Panda Plushie'],
        },
        {
            flag: '🇺🇸', name: 'Hawaii', col: '#ffe066', time: '~6h',
            loot: ['Orchid', 'Basalt Point'],
        },
        {
            flag: '🇦🇪', name: 'UAE', col: '#88ddaa', time: '~14h',
            loot: ['Tribulus Omanense', 'Camel Plushie'],
        },
        {
            flag: '🇰🇾', name: 'Cayman Islands', col: '#66ccff', time: '~10h',
            loot: ['Banana Orchid', 'Stingray Plushie'],
        },
    ];
 
    lootRuns.forEach(run => {
        let needed = 0, total = 0;
        run.loot.forEach(itemName => {
            // Determine which group (and thus which section key) this item belongs to
            let groupKey = null;
            for (const [gName, g] of Object.entries(GROUPS)) {
                if (g.items[itemName]) { groupKey = gName.toLowerCase(); break; }
            }
            if (itemName === 'Meteorite Fragment' || itemName === 'Patagonian Fossil') groupKey = 'special';
 
            // Skip items whose section is hidden — don't count them at all
            if (groupKey && vis[groupKey] === false) return;
 
            total++;
            let sets = 0;
            for (const g of Object.values(GROUPS)) {
                if (g.items[itemName]) { sets = calcSet(invCache, g.items); break; }
            }
            const have = (invCache[itemName] || 0) - sets;
            if (have < 5) needed++;
        });
        run.needed = needed; run.total = total;
    });
 
    // Focus mode: active run beats personal priority
    const _xanFocusCtryLoot = cfg.getXanCountry(); // declare early — used in banner innerHTML
    const runs          = cfg.getXanRuns();
    const activeRun     = runs.find(r => r.active);
    const xanPriority   = cfg.getXanPriority();
    const xanThreshold  = cfg.getXanThreshold();
    const xanBelowThresh = xanPriority && xanPersonal < xanThreshold;
    const focusMode      = !!(activeRun || xanBelowThresh);
 
    lootRuns.sort((a, b) => {
        if (focusMode) {
            const aIsSA = a.name === 'South Africa';
            const bIsSA = b.name === 'South Africa';
            if (aIsSA && !bIsSA) return -1;
            if (bIsSA && !aIsSA) return  1;
        }
        return b.needed - a.needed;
    });
 
    const runGrid = document.createElement('div'); runGrid.style.cssText = 'display:flex;flex-direction:column;gap:5px;';
 
    // Focus banner
    if (focusMode) {
        const bannerCol = activeRun ? C.gold : C.gold;
        const xanBanner = document.createElement('div');
        xanBanner.style.cssText = `display:flex;align-items:center;gap:8px;padding:7px 10px;border-radius:6px;background:${bannerCol}12;border:1px solid ${bannerCol}44;margin-bottom:2px;`;
        if (activeRun) {
            const total     = activeRun.trips.reduce((s,t)=>s+t.bought,0);
            const remaining = Math.max(0, activeRun.contractQty - total);
            xanBanner.innerHTML = `<span style="font-size:14px;">✈</span><div style="flex:1;"><div style="font-size:9.5px;font-weight:700;color:${bannerCol};font-family:Arial,sans-serif;">RUN: ${activeRun.client}</div><div style="font-size:8.5px;color:${bannerCol}99;font-family:Consolas,monospace;margin-top:1px;">${total} collected · ${remaining} needed · ${_xanFocusCtryLoot} only</div></div>`;
        } else {
            xanBanner.innerHTML = `<span style="font-size:14px;">🧪</span><div style="flex:1;"><div style="font-size:9.5px;font-weight:700;color:${bannerCol};font-family:Arial,sans-serif;">Xanax Priority Active</div><div style="font-size:8.5px;color:${C.textDim};font-family:Consolas,monospace;margin-top:1px;">${_xanFocusCtryLoot} only — ${xanPersonal} / ${xanThreshold} collected</div></div>`;
        }
        runGrid.appendChild(xanBanner);
    }

    lootRuns.forEach(run => {
        if (run.total === 0) return;
        if (focusMode && run.name !== _xanFocusCtryLoot) return;
        const urgency = run.needed / run.total;
        const isSAxanPinned = focusMode && run.name === _xanFocusCtryLoot;
 
        const a = document.createElement('div');
        // SA xanax-pinned: always full opacity with green xanax border, even if loot is complete
        const borderCol = isSAxanPinned ? C.border : `${run.col}${urgency > 0.5 ? '66' : '28'}`;
        const bgCol     = isSAxanPinned ? C.goldGlow : `${run.col}${urgency > 0.5 ? '14' : '07'}`;
        const opacity   = (urgency === 0 && !isSAxanPinned) ? '0.35' : '1';
        a.style.cssText = `display:flex;align-items:center;gap:8px;padding:8px 10px;border-radius:7px;cursor:pointer;border:1px solid ${borderCol};background:${bgCol};transition:opacity 0.2s;opacity:${opacity};`;
        a.addEventListener('mouseover', () => { if (urgency > 0 || isSAxanPinned) a.style.opacity = '0.8'; });
        a.addEventListener('mouseout',  () => { a.style.opacity = opacity; });
        a.onclick = function() {
            var FULL_NAMES = {};
            var travelName = FULL_NAMES[run.name] || run.name;
            var onTravelPage = (location.pathname + location.search).indexOf('sid=travel') !== -1;
            if (onTravelPage) {
                clickCountry(travelName);
            } else {
                window.location.href = 'https://www.torn.com/page.php?sid=travel#lt_travel=' + encodeURIComponent(travelName);
            }
        };
 
        const flag = makeFlagEl(run.name, 22);
 
        const mid = document.createElement('div'); mid.style.cssText = 'flex:1;min-width:0;';
        const nameRow = document.createElement('div'); nameRow.style.cssText = 'display:flex;align-items:baseline;gap:5px;';
        const nameEl  = document.createElement('span'); nameEl.textContent = run.name; nameEl.style.cssText = `font-size:10.5px;font-weight:700;color:${isSAxanPinned ? C.gold : run.col};font-family:Arial,sans-serif;`;
        const timeEl  = document.createElement('span'); timeEl.textContent = run.time; timeEl.style.cssText = `font-size:8.5px;color:${C.textDim};font-family:Consolas,monospace;`;
        nameRow.appendChild(nameEl); nameRow.appendChild(timeEl);
        if (isSAxanPinned) {
            const xanTag = document.createElement('span');
            xanTag.textContent = '🧪 Xanax';
            xanTag.style.cssText = `font-size:7.5px;font-family:Consolas,monospace;font-weight:700;color:${C.goldDim};background:${C.goldGlow};border:1px solid ${C.border};border-radius:3px;padding:1px 5px;margin-left:2px;`;
            nameRow.appendChild(xanTag);
        }
 
        const tagWrap = document.createElement('div'); tagWrap.style.cssText = 'display:flex;flex-wrap:wrap;gap:3px;margin-top:3px;';
        // In focus mode SA: show regular items + Xanax stock info
        // In focus mode: show loot items + always show Xanax tag for the pinned xanax country
        const lootItems = run.loot;
        lootItems.forEach(itemName => {
            // Xanax tag — show as special pill, not normal loot tag
            if (itemName === 'Xanax') {
                const code = TORN_DEST_TO_CODE[run.name];
                const hasXan = code && yataCityCache[code] && yataCityCache[code].stocks.some(s => s.id === XANAX_ID);
                if (!hasXan) return;
                // In focus mode the isSAxanPinned block already shows the Xanax tag
                if (!isSAxanPinned) {
                    const xTag = document.createElement('span');
                    xTag.textContent = '🧪 Xanax';
                    xTag.style.cssText = `font-size:8px;font-family:Consolas,monospace;color:${C.green};background:rgba(102,187,102,0.15);border:1px solid rgba(102,187,102,0.4);border-radius:3px;padding:1px 4px;`;
                    tagWrap.appendChild(xTag);
                }
                return;
            }
            // Skip items whose section is hidden
            let groupKey = null;
            for (const [gName, g] of Object.entries(GROUPS)) { if (g.items[itemName]) { groupKey = gName.toLowerCase(); break; } }
            if (itemName === 'Meteorite Fragment' || itemName === 'Patagonian Fossil') groupKey = 'special';
            if (groupKey && vis[groupKey] === false) return;
 
            let sets = 0;
            for (const g of Object.values(GROUPS)) { if (g.items[itemName]) { sets = calcSet(invCache, g.items); break; } }
            const have     = (invCache[itemName] || 0) - sets;
            // In focus mode never highlight xanax as "needed" based on loot surplus — run progress handles that
            const isNeeded = focusMode ? false : have < 5;
            const tag = document.createElement('span');
            tag.textContent = itemName;
            tag.style.cssText = isNeeded
                ? `font-size:8px;font-family:Consolas,monospace;color:#ff9966;background:rgba(200,80,20,0.2);border:1px solid rgba(200,80,20,0.45);border-radius:3px;padding:1px 4px;`
                : `font-size:8px;font-family:Consolas,monospace;color:${C.textDim};background:${C.card};border:1px solid ${C.border};border-radius:3px;padding:1px 4px;opacity:0.7;`;
            tagWrap.appendChild(tag);
        });
        mid.appendChild(nameRow); mid.appendChild(tagWrap);
 
        const badge = document.createElement('div'); badge.style.cssText = 'flex-shrink:0;text-align:center;';
        if (isSAxanPinned) {
            // Focus mode: show trips needed
            const carry = cfg.getXanCarry() || 1;
            let xanNeeded = 0;
            if (activeRun) {
                const collected = activeRun.trips.reduce((s,t)=>s+t.bought,0);
                xanNeeded = Math.max(0, activeRun.contractQty - collected);
            } else {
                xanNeeded = Math.max(0, xanThreshold - xanPersonal);
            }
            const tripsExact  = xanNeeded / carry;
            const tripsNeeded = Math.ceil(tripsExact);
            const overage     = tripsNeeded > 0 ? (tripsNeeded * carry) - xanNeeded : 0;
            if (xanNeeded <= 0) {
                badge.innerHTML = `<div style="font-size:11px;color:${C.green};font-family:Consolas,monospace;">✓</div>`;
            } else {
                badge.innerHTML = `<div style="font-size:16px;font-weight:700;color:#00c8e0;font-family:Consolas,monospace;line-height:1;">${tripsNeeded}</div>
                    <div style="font-size:7px;color:${C.textDim};font-family:Consolas,monospace;">trips</div>
                    ${overage > 0 ? `<div style="font-size:7px;color:${C.goldDim};font-family:Consolas,monospace;margin-top:1px;">+${overage} over</div>` : ''}`;
            }
        } else if (run.needed > 0) {
            badge.innerHTML = `<div style="font-size:14px;font-weight:700;color:${run.col};font-family:Consolas,monospace;line-height:1;">${run.needed}</div><div style="font-size:7.5px;color:${C.textDim};font-family:Consolas,monospace;">needed</div>`;
        } else {
            badge.innerHTML = `<div style="font-size:11px;color:${C.green};font-family:Consolas,monospace;">✓</div>`;
        }
 
        a.appendChild(flag); a.appendChild(mid); a.appendChild(badge);
        runGrid.appendChild(a);
    });
 
    escSec.appendChild(runGrid);
 
    // ── Items per Country (collapsible) ──
    const carSec = document.createElement('div');

    if (typeof renderTravelBody._itemsOpen === 'undefined') renderTravelBody._itemsOpen = false;
    let itemsOpen = renderTravelBody._itemsOpen;

    const carHdrRow = document.createElement('div');
    carHdrRow.style.cssText = `display:flex;justify-content:space-between;align-items:center;padding:4px 10px;font-size:9px;font-weight:700;letter-spacing:1px;text-transform:uppercase;color:${C.goldDim};background:rgba(0,60,80,0.15);border-top:1px solid rgba(0,140,180,0.2);border-bottom:1px solid rgba(0,140,180,0.15);font-family:Consolas,monospace;cursor:pointer;user-select:none;`;
    const carHdrLeft = document.createElement('span'); carHdrLeft.textContent = '🎯 Items per Country';
    const carChevron = document.createElement('span');
    carChevron.style.cssText = `color:${C.gold};font-size:10px;display:inline-block;transition:transform 0.2s;transform:${itemsOpen ? 'rotate(180deg)' : 'rotate(0deg)'};`;
    carChevron.textContent = '▾';
    carHdrRow.appendChild(carHdrLeft); carHdrRow.appendChild(carChevron);
    carSec.appendChild(carHdrRow);

    const carContent = document.createElement('div');
    carContent.style.cssText = `overflow:hidden;max-height:${itemsOpen ? '2000px' : '0'};transition:max-height 0.25s ease;padding:${itemsOpen ? '4px 8px 6px' : '0 8px'};`;

    carHdrRow.addEventListener('click', () => {
        itemsOpen = !itemsOpen;
        renderTravelBody._itemsOpen = itemsOpen;
        carContent.style.maxHeight  = itemsOpen ? '2000px' : '0';
        carContent.style.padding    = itemsOpen ? '4px 8px 6px' : '0 8px';
        carChevron.style.transform  = itemsOpen ? 'rotate(180deg)' : 'rotate(0deg)';
    });

    const countryGuide = [
        { flag: '🇲🇽', name: 'Mexico',        items: ['Dahlia', 'Jaguar Plushie', 'Obsidian Point'] },
        { flag: '🇺🇸',  name: 'Hawaii',         items: ['Orchid', 'Basalt Point'] },
        { flag: '🇿🇦', name: 'South Africa',   items: ['African Violet', 'Lion Plushie', 'Quartzite Point'] },
        { flag: '🇯🇵', name: 'Japan',          items: ['Cherry Blossom'] },
        { flag: '🇨🇳', name: 'China',          items: ['Peony', 'Panda Plushie'] },
        { flag: '🇦🇷', name: 'Argentina',      items: ['Ceibo Flower', 'Monkey Plushie', 'Chalcedony Point', 'Meteorite Fragment', 'Patagonian Fossil'] },
        { flag: '🇨🇭', name: 'Switzerland',    items: ['Edelweiss', 'Chamois Plushie'] },
        { flag: '🇨🇦', name: 'Canada',         items: ['Crocus', 'Wolverine Plushie', 'Quartz Point'] },
        { flag: '🇬🇧', name: 'United Kingdom', items: ['Heather', 'Nessie Plushie', 'Red Fox Plushie', 'Chert Point'] },
        { flag: '🇦🇪', name: 'UAE',            items: ['Tribulus Omanense', 'Camel Plushie'] },
        { flag: '🇰🇾', name: 'Cayman Islands', items: ['Banana Orchid', 'Stingray Plushie'] },
        { flag: '🏪',  name: "Bits n' Bobs",   items: ['Sheep Plushie', 'Teddy Bear Plushie', 'Kitten Plushie'] },
    ];
 
    // Build full item list per country from GROUPS + SPECIAL_ITEMS data
    const countryItemMap = {};
    Object.entries(GROUPS).forEach(([, g]) => {
        Object.entries(g.items).forEach(([iName, iData]) => {
            if (!countryItemMap[iData.loc]) countryItemMap[iData.loc] = [];
            if (!countryItemMap[iData.loc].includes(iName)) countryItemMap[iData.loc].push(iName);
        });
    });
    Object.entries(SPECIAL_ITEMS).forEach(([iName, iData]) => {
        if (!countryItemMap[iData.loc]) countryItemMap[iData.loc] = [];
        if (!countryItemMap[iData.loc].includes(iName)) countryItemMap[iData.loc].push(iName);
    });

    countryGuide.forEach(({ flag, name, items: fallbackItems }) => {
        // BoB items use loc='BoB', not the display name
        const lookupKey = name === "Bits n' Bobs" ? 'BoB' : name;
        const allForCountry = countryItemMap[lookupKey] || fallbackItems || [];
        const visibleItems = allForCountry.filter(iName => {
            let groupKey = null;
            for (const [gName, g] of Object.entries(GROUPS)) { if (g.items[iName]) { groupKey = gName.toLowerCase(); break; } }
            if (iName === 'Meteorite Fragment' || iName === 'Patagonian Fossil') groupKey = 'special';
            return !(groupKey && vis[groupKey] === false);
        });
        if (!visibleItems.length) return;

        // Country wrapper — header tap-to-expand
        const cWrap = document.createElement('div');
        cWrap.style.cssText = 'border-bottom:1px solid rgba(0,80,100,0.2);';

        const cHdr = document.createElement('div');
        cHdr.style.cssText = `display:flex;align-items:center;gap:8px;padding:5px 6px;cursor:pointer;user-select:none;transition:background 0.15s;`;
        cHdr.addEventListener('mouseover', () => cHdr.style.background = 'rgba(0,200,224,0.04)');
        cHdr.addEventListener('mouseout',  () => cHdr.style.background = 'transparent');

        const cFlagEl = document.createElement('div');
        cFlagEl.textContent = flag; cFlagEl.style.cssText = 'font-size:15px;flex-shrink:0;';
        const cNameEl = document.createElement('div');
        cNameEl.style.cssText = `font-size:10px;font-weight:700;color:${C.text};font-family:Arial,sans-serif;flex:1;`;
        cNameEl.textContent = name;
        const cBadge = document.createElement('span');
        cBadge.style.cssText = `font-size:8px;color:${C.textDim};font-family:Consolas,monospace;margin-right:4px;`;
        cBadge.textContent = visibleItems.length + (visibleItems.length === 1 ? ' item' : ' items');
        const cChev = document.createElement('span');
        cChev.style.cssText = 'font-size:9px;color:rgba(0,180,210,0.5);transition:transform 0.18s;display:inline-block;';
        cChev.textContent = '▾';
        cHdr.appendChild(cFlagEl); cHdr.appendChild(cNameEl); cHdr.appendChild(cBadge); cHdr.appendChild(cChev);

        let cOpen = false;
        const cDetail = document.createElement('div');
        cDetail.style.cssText = 'overflow:hidden;max-height:0;transition:max-height 0.25s ease;visibility:hidden;';
        let cPopulated = false;

        cHdr.addEventListener('click', () => {
            cOpen = !cOpen;
            cChev.style.transform = cOpen ? 'rotate(180deg)' : 'rotate(0deg)';
            cDetail.style.maxHeight  = cOpen ? '800px' : '0';
            cDetail.style.visibility = cOpen ? 'visible' : 'hidden';
            if (cOpen && !cPopulated) {
                cPopulated = true;
                const fmt = n => n >= 1e6 ? '$'+(n/1e6).toFixed(1)+'M' : n >= 1e3 ? '$'+Math.round(n/1000)+'k' : n > 0 ? '$'+n : '—';
                visibleItems.forEach(iName => {
                    let iData = null;
                    for (const g of Object.values(GROUPS)) { if (g.items[iName]) { iData = g.items[iName]; break; } }
                    if (!iData) iData = SPECIAL_ITEMS[iName];
                    const iId    = iData ? iData.id : null;
                    const abroad = abroadCache[iName] !== undefined ? abroadCache[iName] : null;
                    const buyP   = yataPriceCache[iName] ? yataPriceCache[iName].price : 0;
                    const sellP  = marketValueCache[iName] || 0;
                    const profit = sellP > buyP && buyP > 0 ? sellP - buyP : 0;
                    const carry  = cfg.getXanCarry() || 1;
                    const isBoBItem = !iData || BOB_IDS.has(iData.id); // BoB items have no overseas stock

                    const iRow = document.createElement('div');
                    iRow.style.cssText = `display:flex;align-items:center;gap:8px;padding:6px 8px;border-top:1px solid ${C.border};background:${C.card};`;

                    if (iId) {
                        const iImg = document.createElement('img');
                        iImg.src = itemImg(iId); iImg.alt = iName;
                        iImg.style.cssText = 'width:26px;height:26px;object-fit:contain;border-radius:2px;flex-shrink:0;';
                        iImg.addEventListener('error', () => { iImg.style.display='none'; });
                        iRow.appendChild(iImg);
                    }

                    const iMid = document.createElement('div'); iMid.style.cssText = 'flex:1;min-width:0;';
                    const iNameEl = document.createElement('div'); iNameEl.textContent = iName;
                    iNameEl.style.cssText = `font-size:9px;font-weight:700;color:${C.text};font-family:Arial,sans-serif;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;`;
                    const iSub = document.createElement('div'); iSub.style.cssText = 'display:flex;gap:4px;margin-top:3px;flex-wrap:wrap;';
                    const mkPill = (txt, col) => { const s = document.createElement('span'); s.textContent = txt; s.style.cssText = `font-size:7.5px;font-weight:600;color:${col};background:${col}28;border:1px solid ${col}55;border-radius:3px;padding:1px 5px;font-family:Consolas,monospace;`; return s; };
                    if (isBoBItem) {
                        const bobQty = iId !== null ? (bobCache[iName] !== undefined ? bobCache[iName] : null) : null;
                        const bobCol = bobQty === null ? C.goldDim : bobQty > 0 ? C.okay : C.stockLo;
                        iSub.appendChild(mkPill('🏪 ' + (bobQty !== null ? 'BoB: '+bobQty : 'BoB only'), bobCol));
                        if (sellP > 0) iSub.appendChild(mkPill('Market: '+fmt(sellP), C.okay));
                    } else {
                        if (abroad !== null && abroad > 0) iSub.appendChild(mkPill('Stock: '+Number(abroad).toLocaleString(), C.abroad));
                        else if (abroad === 0) iSub.appendChild(mkPill('No stock', C.stockLo));
                        if (buyP  > 0) iSub.appendChild(mkPill('Buy: '+fmt(buyP),  C.stockLo));
                        if (sellP > 0) iSub.appendChild(mkPill('Sell: '+fmt(sellP), C.okay));
                        if (profit> 0) iSub.appendChild(mkPill('Profit: '+fmt(profit)+' · trip: '+fmt(profit*carry), '#66dd66'));
                    }
                    iMid.appendChild(iNameEl); iMid.appendChild(iSub);
                    iRow.appendChild(iMid);
                    cDetail.appendChild(iRow);
                });
            }
        });

        cWrap.appendChild(cHdr); cWrap.appendChild(cDetail);
        carContent.appendChild(cWrap);
    });
 
    carSec.appendChild(carContent);
    wrap.appendChild(escSec); wrap.appendChild(carSec);
    body.appendChild(wrap);
}
 
/* ─────────────────────────────────────────
   TAB: DESTINATION (shown when traveling)
───────────────────────────────────────── */
function renderDestBody() {
    const body = panelEl.querySelector('.lt-body');
    body.innerHTML = '';

    if (!travelStatus || !travelStatus.traveling) {
        body.appendChild(makeEmpty('✈️', 'You are not currently traveling.'));
        return;
    }

    const dest      = travelStatus.destination || '';
    // Guard: if outbound but destination not resolved yet, show loading state
    const isReturn  = travelStatus.isReturn || !TORN_DEST_TO_CODE[dest];
    if (!isReturn && !dest) {
        body.appendChild(makeEmpty('◌', 'Resolving destination…'));
        return;
    }
    const code      = TORN_DEST_TO_CODE[dest];
    const cityData  = code ? yataCityCache[code] : null;
    const secsLeft  = travelStatus.timeLeft || 0;

    // ── Resolve From / To display info ──
    // travelStatus.origin is either a country name (API) or city name (DOM scrape)
    // Resolve it to a YATA code so we can get the flag + label from LOCATIONS

    const TORN_LOC = { flag: '🌐', label: 'Torn City', countryName: 'Torn City' };

    function resolveLocation(nameOrCity) {
        if (!nameOrCity || nameOrCity === 'Torn City' || nameOrCity === 'Torn') return null;
        // Try as country name first (API returns country names)
        const codeByCountry = TORN_DEST_TO_CODE[nameOrCity];
        if (codeByCountry) return { code: codeByCountry, loc: LOCATIONS[nameOrCity] || { flag: '✈️', label: nameOrCity } };
        // Try as city name (DOM scrape returns city names e.g. "Johannesburg")
        const codeByCity = CITY_TO_CODE[nameOrCity];
        if (codeByCity) {
            // Find the LOCATIONS country key for this code
            const countryKey = Object.keys(TORN_DEST_TO_CODE).find(k => TORN_DEST_TO_CODE[k] === codeByCity);
            return { code: codeByCity, loc: countryKey ? LOCATIONS[countryKey] : { flag: '✈️', label: nameOrCity } };
        }
        return { code: null, loc: { flag: '✈️', label: nameOrCity } };
    }

    let fromDisplay, toDisplay, fromCityName, toCityName;

    if (isReturn) {
        // Returning: from foreign city → to Torn City
        // For return flights: travelStatus.destination = the foreign country (e.g. "South Africa")
        // travelStatus.origin = same (set by fetchTravelStatus)
        const originName = travelStatus.origin || dest || '';
        const resolved   = resolveLocation(originName);
        const code2      = resolved ? resolved.code : null;
        // Map YATA code back to country name, then use TORN_CITIES for correct city name
        const countryKey2 = code2 ? Object.keys(TORN_DEST_TO_CODE).find(function(k){ return TORN_DEST_TO_CODE[k] === code2; }) : null;
        fromCityName     = countryKey2 ? (TORN_CITIES[countryKey2] || originName) : originName || 'Abroad';
        fromDisplay      = resolved ? resolved.loc : { flag: '✈️', label: originName || 'Abroad' };
        toDisplay        = TORN_LOC;
        toCityName       = 'Torn City';
    } else {
        // Outbound: from Torn City → to foreign city
        fromDisplay    = TORN_LOC;
        fromCityName   = 'Torn City';
        const resolved = resolveLocation(dest);
        toDisplay      = resolved ? resolved.loc : { flag: '✈️', label: dest };
        toCityName     = TORN_CITIES[dest] || (cityData ? cityData.city : dest);
    }


    const landingStr = travelStatus.landingStr || '';

    // ── Arrival time — always computed as now + timeLeft ──
    // departed (unix ts) + time_left = arrival unix ts, but departed may be 0 from old scrape data
    // Most reliable: just use Date.now() + secsLeft * 1000
    const arrivalDate  = new Date(Date.now() + secsLeft * 1000);
    const arrivalLocal = arrivalDate.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
    const arrivalDay   = arrivalDate.toLocaleDateString([], { weekday: 'short', month: 'short', day: 'numeric' });

    // ── Trip duration: departed is a unix ts from the API ──
    const departed = travelStatus.departed || 0;
    let tripDurStr = '';
    if (departed > 0) {
        const totalSecs = (Date.now() / 1000 - departed) + secsLeft;
        const th = Math.floor(totalSecs / 3600);
        const tm = Math.floor((totalSecs % 3600) / 60);
        tripDurStr = th > 0 ? `${th}h ${tm}m total` : `${tm}m total`;
    }

    // ── Remaining time countdown ──
    const remHrs  = Math.floor(secsLeft / 3600);
    const remMin  = Math.floor((secsLeft % 3600) / 60);
    const remSecs = secsLeft % 60;
    const remStr  = remHrs > 0 ? `${remHrs}h ${remMin}m ${remSecs}s` : remMin > 0 ? `${remMin}m ${remSecs}s` : `${remSecs}s`;

    // ── Travel Card ──
    const card = document.createElement('div');
    card.style.cssText = `margin:10px 10px 6px;background:${C.card};border:1px solid ${C.cardBorder};border-radius:10px;overflow:hidden;`;

    // Route row: From → [plane] → To
    const routeRow = document.createElement('div');
    routeRow.style.cssText = 'display:flex;align-items:center;padding:12px 14px 10px;gap:0;';

    // mkCity takes a display object { flag, label, countryName? } to support real flag images
    const mkCity = (display, city, label, align) => {
        const d = document.createElement('div');
        d.style.cssText = `flex:1;text-align:${align};display:flex;flex-direction:column;align-items:${align === 'left' ? 'flex-start' : 'flex-end'};`;
        const flagWrap = document.createElement('div'); flagWrap.style.cssText = 'margin-bottom:3px;line-height:1;display:flex;justify-content:center;';
        // Use real image if we have a country name key, else emoji
        // Use display.countryName if available (Torn City), else match via LOCATIONS
        const flagKey = display.countryName || Object.keys(LOCATIONS).find(k => LOCATIONS[k].flag === display.flag || k === display.label);
        flagWrap.appendChild(flagKey ? makeFlagEl(flagKey, 26) : (() => { const s = document.createElement('span'); s.textContent = display.flag; s.style.fontSize = '22px'; return s; })());
        const cityEl = document.createElement('div'); cityEl.textContent = city;
        cityEl.style.cssText = `font-size:11px;font-weight:700;color:${C.text};font-family:Arial,sans-serif;`;
        const lblEl = document.createElement('div'); lblEl.textContent = label;
        lblEl.style.cssText = `font-size:8px;color:${C.textDim};font-family:Consolas,monospace;`;
        d.appendChild(flagWrap); d.appendChild(cityEl); d.appendChild(lblEl);
        return d;
    };

    const fromEl   = mkCity(fromDisplay, fromCityName, fromDisplay.label, 'left');
    const toEl     = mkCity(toDisplay,   toCityName,   toDisplay.label,   'right');

    const planeWrap = document.createElement('div');
    planeWrap.style.cssText = 'flex:1;display:flex;flex-direction:column;align-items:center;gap:3px;';
    // Dashed line with plane
    const lineWrap = document.createElement('div');
    lineWrap.style.cssText = 'width:100%;display:flex;align-items:center;gap:2px;';
    const dash1 = document.createElement('div'); dash1.style.cssText = `flex:1;height:1px;background:linear-gradient(to right,${C.border},rgba(0,200,224,0.6));`;
    const planeIcon = document.createElement('span'); planeIcon.textContent = '✈'; planeIcon.style.cssText = `color:${C.gold};font-size:13px;`;
    const dash2 = document.createElement('div'); dash2.style.cssText = `flex:1;height:1px;background:linear-gradient(to right,rgba(0,200,224,0.6),${C.border});`;
    lineWrap.appendChild(dash1); lineWrap.appendChild(planeIcon); lineWrap.appendChild(dash2);
    const durLabel = document.createElement('div');
    durLabel.style.cssText = `font-size:8px;color:${C.goldDim};font-family:Consolas,monospace;`;
    durLabel.textContent = tripDurStr || '';
    planeWrap.appendChild(lineWrap); planeWrap.appendChild(durLabel);

    routeRow.appendChild(fromEl); routeRow.appendChild(planeWrap); routeRow.appendChild(toEl);
    card.appendChild(routeRow);

    // Stats row: time remaining | arrival
    const statsRow = document.createElement('div');
    statsRow.style.cssText = `display:grid;grid-template-columns:1fr 1fr;border-top:1px solid rgba(0,140,170,0.2);`;
    [
        { label: 'Time Remaining', value: remStr, id: 'lt-dest-time', color: secsLeft < 600 ? C.okay : C.gold },
        { label: 'Arrives (local)', value: arrivalLocal, sub: arrivalDay, color: C.text },
    ].forEach((item, i) => {
        const cell = document.createElement('div');
        cell.style.cssText = `padding:8px 12px;${i === 0 ? 'border-right:1px solid rgba(0,140,170,0.2);' : ''}`;
        const idAttr = item.id ? ` id="${item.id}"` : '';
        cell.innerHTML = `<div style="font-size:8px;color:${C.textDim};font-family:Consolas,monospace;letter-spacing:.4px;margin-bottom:3px;">${item.label}</div>
            <div${idAttr} style="font-size:13px;font-weight:700;color:${item.color};font-family:Consolas,monospace;">${item.value}</div>
            ${item.sub ? `<div style="font-size:8px;color:${C.textDim};font-family:Consolas,monospace;margin-top:1px;">${item.sub}</div>` : ''}`;
        statsRow.appendChild(cell);
    });
    card.appendChild(statsRow);
    body.appendChild(card);

    // ── Return flight — no item list ──
    if (isReturn || !cityData || !cityData.stocks.length) {
        if (!isReturn && (!cityData || !cityData.stocks.length)) {
            body.appendChild(makeEmpty('📡', 'No YATA stock data for this destination.<br>Try refreshing.'));
        }
        return;
    }

    // ── Column header ──
    const colHdr = document.createElement('div');
    colHdr.style.cssText = `display:grid;grid-template-columns:34px 1fr 52px 60px;gap:6px;padding:3px 10px;background:rgba(0,200,224,0.04);border-bottom:1px solid rgba(0,150,180,0.15);`;
    ['', 'Item', 'Stock', 'Profit'].forEach(txt => {
        const s = document.createElement('span');
        s.textContent = txt;
        s.style.cssText = `font:600 8px Consolas,monospace;color:rgba(0,200,224,0.45);text-align:${txt === 'Stock' || txt === 'Profit' ? 'center' : 'left'};text-transform:uppercase;letter-spacing:.4px;`;
        colHdr.appendChild(s);
    });
    body.appendChild(colHdr);

    // ── Item rows — sorted by profit desc ──
    const rows = cityData.stocks.map(stock => {
        const sellPrice = marketValueCache[stock.name] || 0;
        const profit    = sellPrice > stock.cost ? sellPrice - stock.cost : 0;
        return { ...stock, sellPrice, profit };
    }).sort((a, b) => b.profit - a.profit);

    rows.forEach(stock => {
        // Build a data-compatible object for makeExpandableItem
        // Dest tab uses stock objects from YATA; expandable needs data.id and data.s
        const destItemData = { id: stock.id, s: stock.name.slice(0, 8), loc: dest };
        const destWrap = document.createElement('div');
        destWrap.style.cssText = 'border-bottom:1px solid rgba(0,80,100,0.2);';

        // Header row: image | name+buyprice | stock | profit — with expand chevron
        const destRow = document.createElement('div');
        destRow.style.cssText = `display:grid;grid-template-columns:34px 1fr 52px 60px;gap:6px;align-items:center;padding:5px 10px;min-height:38px;transition:background 0.15s;cursor:pointer;position:relative;padding-right:18px;`;
        destRow.addEventListener('mouseover', () => destRow.style.background = 'rgba(0,200,224,0.05)');
        destRow.addEventListener('mouseout',  () => destRow.style.background = 'transparent');

        const destImgWrap = document.createElement('div'); destImgWrap.style.cssText = 'position:relative;width:32px;height:32px;';
        const destImg = document.createElement('img'); destImg.src = itemImg(stock.id); destImg.alt = stock.name;
        destImg.style.cssText = 'width:30px;height:30px;object-fit:contain;border-radius:2px;background:rgba(255,255,255,0.04);border:1px solid rgba(255,255,255,0.08);display:block;';
        destImg.addEventListener('error', () => { destImg.style.display='none'; });
        destImgWrap.appendChild(destImg);

        const destNameWrap = document.createElement('div'); destNameWrap.style.cssText = 'min-width:0;';
        const destNameEl = document.createElement('div'); destNameEl.textContent = stock.name;
        destNameEl.style.cssText = `font-size:9.5px;font-weight:700;color:${C.text};font-family:Arial,sans-serif;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;`;
        const destBuyFmt = stock.cost >= 1e3 ? '$'+Math.round(stock.cost/1000)+'k' : '$'+stock.cost;
        const destPriceEl = document.createElement('div');
        destPriceEl.style.cssText = `font-size:8px;color:${C.textDim};font-family:Consolas,monospace;margin-top:1px;`;
        destPriceEl.textContent = 'Buy ' + destBuyFmt;
        destNameWrap.appendChild(destNameEl); destNameWrap.appendChild(destPriceEl);

        const destStockEl = document.createElement('div');
        const destStockCol = stock.qty === 0 ? C.stockLo : stock.qty < 50 ? C.stockMid : C.stockHi;
        destStockEl.style.cssText = `color:${destStockCol};background:${destStockCol}1a;font-weight:700;text-align:center;border:1px solid ${destStockCol}44;font-family:Consolas,monospace;padding:2px 3px;border-radius:3px;font-size:10px;`;
        destStockEl.textContent = stock.qty.toLocaleString();

        const destProfitEl = document.createElement('div'); destProfitEl.style.cssText = 'text-align:center;';
        if (stock.profit > 0) {
            const pFmt = stock.profit >= 1e6 ? '$'+(stock.profit/1e6).toFixed(1)+'M' : stock.profit >= 1e3 ? '$'+Math.round(stock.profit/1000)+'k' : '$'+stock.profit;
            destProfitEl.innerHTML = `<div style="font-size:10px;font-weight:700;color:${C.okay};font-family:Consolas,monospace;">${pFmt}</div>`;
        } else { destProfitEl.innerHTML = `<div style="font-size:9px;color:${C.textDim};font-family:Consolas,monospace;">—</div>`; }

        const destChevron = document.createElement('span');
        destChevron.style.cssText = `position:absolute;right:6px;top:50%;transform:translateY(-50%) rotate(0deg);font-size:9px;color:${C.textDim};transition:transform 0.18s;pointer-events:none;`;
        destChevron.textContent = '▾';

        destRow.appendChild(destImgWrap); destRow.appendChild(destNameWrap);
        destRow.appendChild(destStockEl); destRow.appendChild(destProfitEl); destRow.appendChild(destChevron);

        // Expandable detail
        let destExpOpen = false;
        const destDetail = document.createElement('div');
        destDetail.style.cssText = `overflow:hidden;max-height:0;transition:max-height 0.22s ease;background:${C.bg};`;
        const destInner = document.createElement('div'); destInner.style.cssText = 'padding:8px 10px;display:flex;flex-direction:column;gap:6px;';
        let destPopulated = false;

        destRow.addEventListener('click', () => {
            destExpOpen = !destExpOpen;
            if (destExpOpen && !destPopulated) {
                destPopulated = true;
                const destCode = TORN_DEST_TO_CODE[dest] || '';
                const history   = cfg.getStockHistory(stock.name + (destCode ? '_'+destCode : ''));
                const sellPrice = marketValueCache[stock.name] || 0;
                const profit    = sellPrice > stock.cost ? sellPrice - stock.cost : 0;
                const fmt = n => n >= 1e6 ? '$'+(n/1e6).toFixed(2)+'M' : n >= 1e3 ? '$'+Math.round(n/1000)+'k' : '$'+n;
                const lastUpdated = history.length ? new Date(history[history.length-1].ts).toLocaleString([], { month:'short', day:'numeric', hour:'2-digit', minute:'2-digit' }) : '—';
                const grid = document.createElement('div'); grid.style.cssText = 'display:grid;grid-template-columns:repeat(3,1fr);gap:4px;';
                [
                    { label:'Item ID',    value:'#'+stock.id,                   color: C.textDim },
                    { label:'Buy Price',  value: fmt(stock.cost),               color: C.stockLo },
                    { label:'Sell Value', value: sellPrice > 0 ? fmt(sellPrice): '—', color: C.okay },
                    { label:'Profit/ea',  value: profit > 0 ? fmt(profit) : '—', color: profit > 0 ? C.okay : C.textDim },
                    { label:'YATA Stock', value: stock.qty.toLocaleString(),    color: C.abroad },
                    { label:'Updated',    value: lastUpdated,                   color: C.textDim },
                ].forEach(({ label, value, color }) => {
                    const cell = document.createElement('div');
                    cell.style.cssText = `background:${C.card};border:1px solid ${C.cardBorder};border-radius:4px;padding:5px 4px;text-align:center;`;
                    cell.innerHTML = `<div style="font-size:7.5px;color:${C.textDim};font-family:Consolas,monospace;letter-spacing:.4px;margin-bottom:2px;">${label}</div><div style="font-size:10px;font-weight:700;color:${color};font-family:Consolas,monospace;">${value}</div>`;
                    grid.appendChild(cell);
                });
                destInner.appendChild(grid);
                if (history.length >= 2) {
                    const vals = history.map(h => h.qty !== undefined ? h.qty : (h.price || 0));
                    const minP = Math.min(...vals), maxP = Math.max(...vals), range = maxP - minP || 1;
                    const W = 260, H = 36, pad = 4;
                    const svg = document.createElementNS('http://www.w3.org/2000/svg','svg');
                    svg.setAttribute('viewBox','0 0 '+W+' '+H); svg.setAttribute('width','100%'); svg.setAttribute('height',H);
                    const pts = vals.map((v,i) => [pad+(i/(vals.length-1))*(W-pad*2), pad+(1-(v-minP)/range)*(H-pad*2)]);
                    const pd = 'M'+pts.map(([x,y])=>x.toFixed(1)+','+y.toFixed(1)).join('L');
                    const fill = document.createElementNS('http://www.w3.org/2000/svg','path');
                    fill.setAttribute('d', pd+'L'+pts[pts.length-1][0].toFixed(1)+','+H+'L'+pts[0][0].toFixed(1)+','+H+'Z');
                    fill.setAttribute('fill','rgba(0,200,224,0.08)');
                    const line = document.createElementNS('http://www.w3.org/2000/svg','path');
                    line.setAttribute('d',pd); line.setAttribute('fill','none'); line.setAttribute('stroke','rgba(0,200,224,0.7)'); line.setAttribute('stroke-width','1.5');
                    const dot = document.createElementNS('http://www.w3.org/2000/svg','circle');
                    dot.setAttribute('cx',pts[pts.length-1][0].toFixed(1)); dot.setAttribute('cy',pts[pts.length-1][1].toFixed(1)); dot.setAttribute('r','3'); dot.setAttribute('fill','#00c8e0');
                    svg.appendChild(fill); svg.appendChild(line); svg.appendChild(dot);
                    const sparkLbl = document.createElement('div'); sparkLbl.style.cssText = `font-size:8px;font-weight:700;letter-spacing:.5px;text-transform:uppercase;color:${C.goldDim};font-family:Consolas,monospace;`;
                    sparkLbl.textContent = '📦 YATA Stock history (' + fmtHistorySpan(history) + ')';
                    destInner.appendChild(sparkLbl); destInner.appendChild(svg);
                }
                destDetail.appendChild(destInner);
            }
            destDetail.style.maxHeight = destExpOpen ? '300px' : '0';
            destChevron.style.transform = destExpOpen ? 'translateY(-50%) rotate(180deg)' : 'translateY(-50%) rotate(0deg)';
        });
        destWrap.appendChild(destRow); destWrap.appendChild(destDetail);
        body.appendChild(destWrap);
    });
}

/* ─────────────────────────────────────────
   STATUS BAR
───────────────────────────────────────── */
function renderStatusBar() {
    const bar = panelEl ? panelEl.querySelector('#lt-sbar') : null;
    if (!bar) return;
    bar.innerHTML = '';
 
    if (activeTab === 'dest') {
        // no status bar text
    } else if (activeTab === 'sets') {
        const vis = cfg.getSectionVis();
        let totalSets = 0, totalPts = 0;
        Object.entries(GROUPS).forEach(([n, g]) => {
            if (vis[n.toLowerCase()] === false) return;
            const s = calcSet(invCache, g.items); totalSets += s; totalPts += s * g.pts;
        });
        // Special items: each individual item counts toward pts (no "set" mechanic)
        if (vis.special !== false) {
            Object.entries(SPECIAL_ITEMS).forEach(([name, data]) => {
                const qty = invCache[name] || 0;
                totalPts += qty * data.pts;
                totalSets += qty; // each special item counts as its own unit
            });
        }
        const sSpan = document.createElement('span'); sSpan.style.cssText = `color:${C.goldDim};font-weight:700;`; sSpan.textContent = totalSets + ' sets · ' + totalPts + ' pts';
        bar.appendChild(sSpan);
        if (pointsPrice > 0 && totalPts > 0) {
            const pv    = totalPts * pointsPrice;
            const pvFmt = pv >= 1e6 ? `$${(pv/1e6).toFixed(1)}M` : pv >= 1000 ? `$${Math.round(pv/1000)}k` : `$${pv}`;
            const vSpan = document.createElement('span');
            vSpan.style.cssText = `margin-left:auto;color:${C.gold};font-weight:700;`;
            vSpan.title = `${totalPts} pts × $${pointsPrice.toLocaleString()} per pt`;
            vSpan.textContent = pvFmt;
            bar.appendChild(vSpan);
        }
    } else if (activeTab === 'xanax') {
        const span = document.createElement('span'); span.style.cssText = `color:${C.goldDim};font-weight:700;`;
        const _xanCtryBar = cfg.getXanCountry();
        const _xanFlagBar = {'Mexico':'🇲🇽','Hawaii':'🇺🇸','South Africa':'🇿🇦','Japan':'🇯🇵','China':'🇨🇳','Argentina':'🇦🇷','Switzerland':'🇨🇭','Canada':'🇨🇦','United Kingdom':'🇬🇧','UAE':'🇦🇪','Cayman Islands':'🇰🇾'}[_xanCtryBar] || '✈️';
        span.textContent = _xanFlagBar + ' ' + _xanCtryBar + ' runs';
        bar.appendChild(span);
    } else if (activeTab === 'travel') {
        const span = document.createElement('span'); span.style.cssText = `color:${C.goldDim};`;
        span.textContent = 'Loot runs ranked by items needed';
        bar.appendChild(span);
    }
 
    if (isLoading) {
        const sp = document.createElement('span'); sp.className = 'lt-spin'; sp.textContent = '◌';
        sp.style.cssText = `margin-left:auto;color:${C.goldDim};`;
        bar.appendChild(sp);
    }
}
 
/* ─────────────────────────────────────────
   MAIN PANEL RENDER
───────────────────────────────────────── */

/* ─────────────────────────────────────────
   TAB: ABROAD (shown when landed in foreign country)
   Shows all YATA items available at the current location
   with expandable cards: stock, buy price, sell value, profit
─────────────────────────────────────────── */
function renderAbroadBody() {
    const body = panelEl.querySelector('.lt-body');
    body.innerHTML = '';

    const country  = abroadCountry;
    const code     = country ? TORN_DEST_TO_CODE[country] : null;
    const cityData = code ? yataCityCache[code] : null;
    const cityName = country ? (TORN_CITIES[country] || country) : '?';
    const locInfo  = country ? (LOCATIONS[country] || { flag: '📍', label: country }) : { flag: '📍', label: country };

    const wrap = document.createElement('div');
    wrap.style.cssText = 'padding:10px;display:flex;flex-direction:column;gap:10px;';

    // ── Header card — flag, city, travel home button ──


    // ── Situation Report — cash, return ETA, bars ──
    const pd = abroadPlayerData;
    // If we don't have player data yet, fetch bars+money directly
    if (cfg.apiKey && (!pd || !pd.energy) && !isLoading) {
        Promise.all([
            gmFetch(`https://api.torn.com/v2/user/bars?key=${cfg.apiKey}`),
            gmFetch(`https://api.torn.com/v2/user/money?key=${cfg.apiKey}`),
        ]).then(function(results) {
            const barsResp  = results[0] || {};
            const moneyResp = results[1] || {};
            console.log('[SetsTracker] v2/bars raw:', JSON.stringify(barsResp).substring(0, 200));
            console.log('[SetsTracker] v2/money raw:', JSON.stringify(moneyResp).substring(0, 200));
            const bars  = barsResp.bars  || {};
            const money = moneyResp.money || {};
            abroadPlayerData = {
                abroad: true, destination: abroadCountry,
                energy:    bars.energy  ? { current: bars.energy.current,  maximum: bars.energy.maximum  } : null,
                nerve:     bars.nerve   ? { current: bars.nerve.current,   maximum: bars.nerve.maximum   } : null,
                happy:     bars.happy   ? { current: bars.happy.current,   maximum: bars.happy.maximum   } : null,
                life:      bars.life    ? { current: bars.life.current,    maximum: bars.life.maximum    } : null,
                cash:      money.wallet != null ? money.wallet : null,
                vaultCash: money.vault  != null ? money.vault  : null,
            };
            console.log('[SetsTracker] abroadPlayerData set:', JSON.stringify(abroadPlayerData).substring(0, 200));
            if (panelEl && activeTab === 'abroad') renderPanel();
        }).catch(function(e) { console.warn('[SetsTracker] bars/money fetch failed:', e); });
    }
    const sitCard = document.createElement('div');
    sitCard.style.cssText = `background:${C.bg2};border:1px solid ${C.border};border-radius:8px;overflow:hidden;`;

    const sitHdrEl = document.createElement('div');
    sitHdrEl.style.cssText = `padding:6px 12px;background:${C.goldGlow};border-bottom:1px solid ${C.border};font-size:9px;font-weight:700;letter-spacing:1px;text-transform:uppercase;color:${C.goldDim};font-family:Consolas,monospace;`;
    sitHdrEl.textContent = '📊 Situation Report';
    sitCard.appendChild(sitHdrEl);

    const fmtCash = n => n == null ? '—' : n >= 1e9 ? '$'+(n/1e9).toFixed(2)+'B' : n >= 1e6 ? '$'+(n/1e6).toFixed(2)+'M' : n >= 1e3 ? '$'+Math.round(n/1000)+'k' : '$'+n;

    // ── Return ETA — use TRAVEL_TIMES with player's speed setting ──
    const _speedIdx  = cfg.getTravelSpeed ? cfg.getTravelSpeed() : 0;
    const _times     = TRAVEL_TIMES[country];
    const flightHr   = _times ? (_times[_speedIdx] || _times[0]) : 2;
    const nowMs      = Date.now();
    const returnMs   = nowMs + (flightHr * 3600 * 1000);
    const returnDate = new Date(returnMs);
    const returnStr  = returnDate.toLocaleString([], { weekday:'short', month:'short', day:'numeric', hour:'2-digit', minute:'2-digit' });
    const returnInHr = flightHr;

    // ── Info grid ──
    const sitGrid = document.createElement('div');
    sitGrid.style.cssText = 'display:grid;grid-template-columns:1fr 1fr;gap:1px;background:' + C.border + ';';

    function mkStat(icon, label, value, valueColor, subtext) {
        const cell = document.createElement('div');
        cell.style.cssText = `background:${C.bg2};padding:8px 10px;`;
        cell.innerHTML = `<div style="font-size:7.5px;color:${C.textDim};font-family:Consolas,monospace;letter-spacing:.4px;margin-bottom:3px;">${icon} ${label}</div>
            <div style="font-size:11px;font-weight:700;color:${valueColor};font-family:Consolas,monospace;line-height:1.2;">${value}</div>
            ${subtext ? `<div style="font-size:8px;color:${C.textDim};font-family:Consolas,monospace;margin-top:2px;">${subtext}</div>` : ''}`;
        return cell;
    }

    // Cash on hand
    sitGrid.appendChild(mkStat('💵', 'Cash on Hand',
        pd && pd.cash != null ? fmtCash(pd.cash) : '—',
        pd && pd.cash > 0 ? C.okay : C.textDim,
        pd && pd.vaultCash != null ? 'Vault: ' + fmtCash(pd.vaultCash) : null
    ));

    // Earliest return time
    sitGrid.appendChild(mkStat('🛬', 'Earliest Return',
        returnStr,
        C.abroad,
        'Flight ~' + (returnInHr % 1 === 0 ? returnInHr : returnInHr.toFixed(1)) + 'h from now'
    ));

    // Energy
    if (pd && pd.energy) {
        const ePct = Math.round((pd.energy.current / pd.energy.maximum) * 100);
        sitGrid.appendChild(mkStat('⚡', 'Energy',
            pd.energy.current + ' / ' + pd.energy.maximum,
            ePct >= 90 ? C.okay : ePct >= 50 ? C.gold : C.stockMid,
            ePct + '%'
        ));
    } else {
        sitGrid.appendChild(mkStat('⚡', 'Energy', '—', C.textDim, null));
    }

    // Nerve
    if (pd && pd.nerve) {
        const nPct = Math.round((pd.nerve.current / pd.nerve.maximum) * 100);
        sitGrid.appendChild(mkStat('🔴', 'Nerve',
            pd.nerve.current + ' / ' + pd.nerve.maximum,
            nPct >= 80 ? C.okay : nPct >= 40 ? C.gold : C.stockMid,
            nPct + '%'
        ));
    } else {
        sitGrid.appendChild(mkStat('🔴', 'Nerve', '—', C.textDim, null));
    }

    // Happy
    if (pd && pd.happy) {
        const hPct = Math.round((pd.happy.current / pd.happy.maximum) * 100);
        sitGrid.appendChild(mkStat('😊', 'Happy',
            pd.happy.current.toLocaleString() + ' / ' + pd.happy.maximum.toLocaleString(),
            hPct >= 80 ? C.okay : hPct >= 40 ? C.gold : C.stockMid,
            hPct + '%'
        ));
    } else {
        sitGrid.appendChild(mkStat('😊', 'Happy', '—', C.textDim, null));
    }

    // Life
    if (pd && pd.life) {
        const lPct = Math.round((pd.life.current / pd.life.maximum) * 100);
        sitGrid.appendChild(mkStat('❤️', 'Life',
            pd.life.current + ' / ' + pd.life.maximum,
            lPct >= 80 ? C.okay : lPct >= 40 ? C.gold : C.stockLo,
            lPct + '%'
        ));
    } else {
        sitGrid.appendChild(mkStat('❤️', 'Life', '—', C.textDim, null));
    }

    sitCard.appendChild(sitGrid);

    wrap.appendChild(sitCard);

    // ── Budget card ──
    const cheapestItem = cityData && cityData.stocks && cityData.stocks.length
        ? cityData.stocks.slice().sort((a,b) => a.cost - b.cost).find(s => s.cost > 0)
        : null;
    const carry = cfg.getXanCarry() || 1;
    if (cheapestItem && pd && pd.cash != null && pd.cash > 0) {
        const maxBuy     = Math.floor(pd.cash / cheapestItem.cost);
        const affordable = Math.min(maxBuy, carry);
        const totalCost  = affordable * cheapestItem.cost;
        const col        = affordable >= carry ? C.okay : affordable > 0 ? C.gold : C.stockLo;

        const budgetCard = document.createElement('div');
        budgetCard.style.cssText = `background:${C.bg2};border:1px solid ${C.border};border-radius:8px;overflow:hidden;`;

        const budgetHdr = document.createElement('div');
        budgetHdr.style.cssText = `padding:6px 12px;background:${C.goldGlow};border-bottom:1px solid ${C.border};font-size:9px;font-weight:700;letter-spacing:1px;text-transform:uppercase;color:${C.goldDim};font-family:Consolas,monospace;`;
        budgetHdr.textContent = '💰 Budget';
        budgetCard.appendChild(budgetHdr);

        const budgetBody = document.createElement('div');
        budgetBody.style.cssText = `padding:10px 12px;font-family:Consolas,monospace;`;
        budgetBody.innerHTML =
            `<div style="font-size:11px;color:${C.text};margin-bottom:4px;">` +
                `Can buy <span style="font-size:14px;font-weight:700;color:${col};">${affordable}</span>` +
                ` × ${cheapestItem.name}` +
            `</div>` +
            `<div style="font-size:8.5px;color:${C.textDim};margin-top:2px;">` +
                `Cheapest at ${fmtCash(cheapestItem.cost)} · Total: ` +
                `<span style="color:${col};font-weight:700;">${fmtCash(totalCost)}</span>` +
            `</div>`;

        budgetCard.appendChild(budgetBody);
        wrap.appendChild(budgetCard);
    }

    // ── Combined: Your Items (owned + YATA stock + profit) ──
    // Build owned count lookup from inventory
    const ownedMap = {};
    Object.entries(GROUPS).forEach(function(entry) {
        const g = entry[1];
        Object.entries(g.items).forEach(function(iEntry) {
            const iName = iEntry[0], iData = iEntry[1];
            if (iData.loc === country || iData.loc === locInfo.label) {
                ownedMap[iName] = invCache[iName] || 0;
            }
        });
    });
    Object.entries(SPECIAL_ITEMS).forEach(function(entry) {
        const iName = entry[0], iData = entry[1];
        if (iData.loc === country) ownedMap[iName] = invCache[iName] || 0;
    });

    // Auto-refresh YATA data if stale (>5 min) or missing
    const _yataAge = Date.now() - yataLastFetch;
    if ((!cityData || !cityData.stocks || !cityData.stocks.length || _yataAge > 300000) && !isLoading) {
        isLoading = true;
        renderStatusBar();
        fetchYataData().then(function(result) {
            if (result && result.map) { abroadCache = result.map; }
            if (result && result.sa)  { xanSACache  = result.sa;  }
            isLoading = false;
            if (panelEl && activeTab === 'abroad') renderPanel();
        }).catch(function() { isLoading = false; if (panelEl) renderPanel(); });
        if (!cityData || !cityData.stocks || !cityData.stocks.length) {
            wrap.appendChild(makeEmpty('◌', 'Loading YATA stock for ' + cityName + '…'));
            body.appendChild(wrap);
            return;
        }
    }

    if (!cityData || !cityData.stocks || !cityData.stocks.length) {
        wrap.appendChild(makeEmpty('📡', 'No YATA stock data yet. Try refreshing.'));
        body.appendChild(wrap);
        return;
    }

    // ── Unified item table ──
    const itemsSec = document.createElement('div');
    itemsSec.style.cssText = `background:${C.bg2};border:1px solid ${C.border};border-radius:8px;overflow:hidden;`;

    // Column header: img | name+price | owned | stock | profit
    const colHdr = document.createElement('div');
    colHdr.style.cssText = `display:grid;grid-template-columns:34px 1fr 44px 52px 60px;gap:4px;padding:4px 10px;background:${C.goldGlow};border-bottom:1px solid ${C.border};`;
    ['', 'Item', 'Owned', 'Stock', 'Profit'].forEach(function(txt) {
        const s = document.createElement('span');
        s.textContent = txt;
        s.style.cssText = `font:600 8px Consolas,monospace;color:${C.goldDim};text-align:${(txt==='Owned'||txt==='Stock'||txt==='Profit')?'center':'left'};text-transform:uppercase;letter-spacing:.4px;`;
        colHdr.appendChild(s);
    });
    itemsSec.appendChild(colHdr);

    const rows = cityData.stocks.map(function(stock) {
        const sellPrice = marketValueCache[stock.name] || 0;
        const profit    = sellPrice > stock.cost && stock.cost > 0 ? sellPrice - stock.cost : 0;
        return Object.assign({}, stock, { sellPrice: sellPrice, profit: profit });
    }).sort(function(a, b) { return b.profit - a.profit; });

    const fmtS = function(n){ return n>=1e6?'$'+(n/1e6).toFixed(1)+'M':n>=1e3?'$'+Math.round(n/1000)+'k':'$'+n; };

    rows.forEach(function(stock, rowIdx) {
        const isLast  = rowIdx === rows.length - 1;
        const owned   = ownedMap[stock.name] != null ? ownedMap[stock.name] : null;
        const itemWrap = document.createElement('div');
        itemWrap.style.cssText = `border-bottom:${isLast ? 'none' : '1px solid ' + C.border};`;

        const row = document.createElement('div');
        row.style.cssText = `display:grid;grid-template-columns:34px 1fr 44px 52px 60px;gap:4px;align-items:center;padding:6px 10px;min-height:40px;cursor:pointer;transition:background 0.15s;position:relative;padding-right:18px;`;
        row.addEventListener('mouseover', function(){ row.style.background = C.goldGlow; });
        row.addEventListener('mouseout',  function(){ row.style.background = 'transparent'; });

        // Image
        const imgWrap = document.createElement('div'); imgWrap.style.cssText = 'width:30px;height:30px;';
        const img = document.createElement('img'); img.src = itemImg(stock.id); img.alt = stock.name;
        img.style.cssText = `width:30px;height:30px;object-fit:contain;border-radius:3px;background:${C.card};border:1px solid ${C.cardBorder};display:block;`;
        img.addEventListener('error', function(){ img.style.display='none'; });
        imgWrap.appendChild(img);

        // Name + buy price
        const nameWrap = document.createElement('div'); nameWrap.style.cssText = 'min-width:0;';
        const nameEl = document.createElement('div'); nameEl.textContent = stock.name;
        nameEl.style.cssText = `font-size:9.5px;font-weight:700;color:${C.text};font-family:Arial,sans-serif;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;`;
        const priceEl = document.createElement('div');
        priceEl.style.cssText = `font-size:8px;color:${C.textDim};font-family:Consolas,monospace;margin-top:1px;`;
        priceEl.textContent = stock.cost > 0 ? 'Buy ' + fmtS(stock.cost) : '';
        nameWrap.appendChild(nameEl); nameWrap.appendChild(priceEl);

        // Owned count
        const ownedEl = document.createElement('div');
        ownedEl.style.cssText = 'text-align:center;';
        if (owned != null) {
            const ownCol = owned > 0 ? C.okay : C.textDim;
            ownedEl.innerHTML = `<div style="font-size:10px;font-weight:700;color:${ownCol};font-family:Consolas,monospace;">${owned}</div>`;
        } else {
            ownedEl.innerHTML = `<div style="font-size:9px;color:${C.textDim};font-family:Consolas,monospace;">—</div>`;
        }

        // Stock (YATA)
        const stockCol = stock.qty === 0 ? C.stockLo : stock.qty < 50 ? C.stockMid : C.stockHi;
        const stockEl = document.createElement('div');
        stockEl.style.cssText = `color:${stockCol};background:${stockCol}1a;font-weight:700;text-align:center;border:1px solid ${stockCol}44;font-family:Consolas,monospace;padding:2px 3px;border-radius:3px;font-size:10px;`;
        stockEl.textContent = stock.qty.toLocaleString();

        // Profit
        const profitEl = document.createElement('div'); profitEl.style.cssText = 'text-align:center;';
        profitEl.innerHTML = stock.profit > 0
            ? `<div style="font-size:10px;font-weight:700;color:${C.okay};font-family:Consolas,monospace;">${fmtS(stock.profit)}</div>`
            : `<div style="font-size:9px;color:${C.textDim};font-family:Consolas,monospace;">—</div>`;

        // Chevron
        const chev = document.createElement('span');
        chev.style.cssText = `position:absolute;right:5px;top:50%;transform:translateY(-50%) rotate(0deg);font-size:9px;color:${C.textDim};transition:transform 0.18s;pointer-events:none;`;
        chev.textContent = '▾';

        row.appendChild(imgWrap); row.appendChild(nameWrap);
        row.appendChild(ownedEl); row.appendChild(stockEl); row.appendChild(profitEl); row.appendChild(chev);

        // ── Expand detail ──
        var isOpen = false;
        const detail = document.createElement('div');
        detail.style.cssText = `overflow:hidden;max-height:0;transition:max-height 0.22s ease;background:${C.bg};`;
        var populated = false;

        row.addEventListener('click', function() {
            isOpen = !isOpen;
            detail.style.maxHeight = isOpen ? '320px' : '0';
            chev.style.transform = isOpen ? 'translateY(-50%) rotate(180deg)' : 'translateY(-50%) rotate(0deg)';
            if (isOpen && !populated) {
                populated = true;
                const inner = document.createElement('div'); inner.style.cssText = 'padding:8px 10px;display:flex;flex-direction:column;gap:6px;';
                const history  = cfg.getStockHistory(stock.name);
                const lastUpd  = history.length ? new Date(history[history.length-1].ts).toLocaleString([],{month:'short',day:'numeric',hour:'2-digit',minute:'2-digit'}) : '—';
                const ptsEquiv = stock.cost > 0 && pointsPrice > 0 ? (stock.cost/pointsPrice).toFixed(2)+' pts' : '—';
                const carry    = cfg.getXanCarry() || 1;

                // Stock trend: compare newest vs oldest history entry
                let trendLabel = '—', trendColor = C.textDim;
                if (history.length >= 2) {
                    const oldest = history[0].qty != null ? history[0].qty : (history[0].price || 0);
                    const newest = history[history.length-1].qty != null ? history[history.length-1].qty : (history[history.length-1].price || 0);
                    const diff = newest - oldest;
                    if (diff > 0)       { trendLabel = '↑ Restocking'; trendColor = C.okay; }
                    else if (diff < 0)  { trendLabel = '↓ Depleting';  trendColor = C.stockMid; }
                    else                { trendLabel = '→ Stable';      trendColor = C.textDim; }
                    // Check for out-of-stock events in history
                    const wentOOS = history.some(h => (h.qty != null ? h.qty : h.price) === 0);
                    if (wentOOS && newest === 0) trendLabel = '✕ OOS';
                    else if (wentOOS)             trendLabel = '↑ Restocked';
                }

                const grid = document.createElement('div'); grid.style.cssText = 'display:grid;grid-template-columns:repeat(3,1fr);gap:4px;';
                [
                    { label:'Item ID',    value:'#'+stock.id,                                           color:C.textDim },
                    { label:'Owned',      value:owned != null ? String(owned) : '—',                    color:owned > 0 ? C.okay : C.textDim },
                    { label:'YATA Stock', value:stock.qty.toLocaleString(),                             color:C.abroad  },
                    { label:'Buy',        value:stock.cost>0?fmtS(stock.cost):'—',                     color:C.stockLo },
                    { label:'Sell',       value:stock.sellPrice>0?fmtS(stock.sellPrice):'—',            color:C.okay    },
                    { label:'Profit',     value:stock.profit>0?fmtS(stock.profit):'—',                  color:stock.profit>0?C.okay:C.textDim },
                    { label:'Pts Equiv',  value:ptsEquiv,                                               color:C.goldDim },
                    { label:'Trip Profit',value:stock.profit>0?fmtS(stock.profit*carry):'—',            color:stock.profit>0?'#66dd66':C.textDim },
                    { label:'Stock Trend',value:trendLabel,                                             color:trendColor },
                ].forEach(function(cell) {
                    const cel = document.createElement('div');
                    cel.style.cssText = `background:${C.card};border:1px solid ${C.cardBorder};border-radius:4px;padding:5px 4px;text-align:center;`;
                    cel.innerHTML = `<div style="font-size:7.5px;color:${C.textDim};font-family:Consolas,monospace;letter-spacing:.4px;margin-bottom:2px;">${cell.label}</div>
                        <div style="font-size:10px;font-weight:700;color:${cell.color};font-family:Consolas,monospace;">${cell.value}</div>`;
                    grid.appendChild(cel);
                });
                inner.appendChild(grid);

                // Stock sparkline
                if (history.length >= 2) {
                    const vals = history.map(function(h){ return h.qty !== undefined ? h.qty : (h.price || 0); });
                    const minP = Math.min.apply(null,vals), maxP = Math.max.apply(null,vals), rng = maxP-minP||1;
                    const W=260, H=36, pad=4;
                    const svg = document.createElementNS('http://www.w3.org/2000/svg','svg');
                    svg.setAttribute('viewBox','0 0 '+W+' '+H); svg.setAttribute('width','100%'); svg.setAttribute('height',H);
                    const pts = vals.map(function(v,i){ return [pad+(i/(vals.length-1))*(W-pad*2), pad+(1-(v-minP)/rng)*(H-pad*2)]; });
                    const pd2  = 'M'+pts.map(function(xy){return xy[0].toFixed(1)+','+xy[1].toFixed(1);}).join('L');
                    const fill = document.createElementNS('http://www.w3.org/2000/svg','path');
                    fill.setAttribute('d', pd2+'L'+pts[pts.length-1][0].toFixed(1)+','+H+'L'+pts[0][0].toFixed(1)+','+H+'Z');
                    fill.setAttribute('fill','rgba(0,200,224,0.08)');
                    const line = document.createElementNS('http://www.w3.org/2000/svg','path');
                    line.setAttribute('d',pd2); line.setAttribute('fill','none'); line.setAttribute('stroke','rgba(0,200,224,0.7)'); line.setAttribute('stroke-width','1.5');
                    const dot = document.createElementNS('http://www.w3.org/2000/svg','circle');
                    dot.setAttribute('cx',pts[pts.length-1][0].toFixed(1)); dot.setAttribute('cy',pts[pts.length-1][1].toFixed(1)); dot.setAttribute('r','3'); dot.setAttribute('fill','#00c8e0');
                    svg.appendChild(fill); svg.appendChild(line); svg.appendChild(dot);
                    const sparkLbl = document.createElement('div');
                    sparkLbl.style.cssText = `font-size:8px;font-weight:700;color:${C.goldDim};font-family:Consolas,monospace;`;
                    sparkLbl.textContent = '📦 YATA Stock history (' + fmtHistorySpan(history) + ')';
                    inner.appendChild(sparkLbl); inner.appendChild(svg);
                }
                detail.appendChild(inner);
            }
        });

        itemWrap.appendChild(row); itemWrap.appendChild(detail);
        itemsSec.appendChild(itemWrap);
    });

    wrap.appendChild(itemsSec);
    body.appendChild(wrap);
}

function renderPanel() {
    if (!panelEl) return;
    syncTheme();



    // ── Dynamically update the first tab (Sets ↔ Dest) based on travel state ──
    const tabFirstEl = panelEl.querySelector('#lt-tab-first');
    const tabsEl     = panelEl.querySelector('#lt-tabs');
    if (tabFirstEl && tabsEl) {
        const isTraveling = !!(travelStatus && travelStatus.traveling && travelStatus.timeLeft > 0);
        const firstKey    = isTraveling ? 'dest' : 'sets';
        const firstLbl    = isTraveling ? '📍 Destination' : '🎒 Sets';

        // Update label and key
        tabFirstEl.textContent   = firstLbl;
        tabFirstEl.dataset.tab   = firstKey;

        // If activeTab got stranded on the wrong key, redirect it
        const isAbroad   = !!(abroadCountry && !isTraveling);

        const _xanPrioForRedirect = cfg.getXanPriority();
        if (isTraveling  && activeTab === 'sets')    activeTab = 'dest';
        if (isTraveling  && activeTab === 'abroad')  activeTab = 'dest';  // started return flight
        // Don't redirect away from xanax if priority is forcing it visible
        if (isTraveling  && activeTab === 'xanax' && !_xanPrioForRedirect) activeTab = 'dest';
        if (!isTraveling && activeTab === 'dest')    activeTab = isAbroad ? 'abroad' : 'sets';
        if (!isAbroad    && activeTab === 'abroad')  activeTab = 'sets';
        // If xanax was hidden and priority is now off, redirect away
        if (activeTab === 'xanax' && cfg.getSectionVis().xanax === false && !_xanPrioForRedirect) activeTab = isTraveling ? 'dest' : isAbroad ? 'abroad' : 'sets';

        // Tab visibility rules:
        // Traveling → only Dest shown  |  Abroad → only Abroad tab  |  Home → normal tabs
        // Exception: Xanax Priority ON → Xanax tab always visible regardless of state or hidden setting
        const _vis            = cfg.getSectionVis();
        const _xanHidden      = _vis.xanax === false;
        const _xanPrioActive  = cfg.getXanPriority();
        const _forceXanax     = _xanPrioActive; // priority overrides everything

        tabsEl.querySelectorAll('[data-tab]').forEach(t => {
            const key = t.dataset.tab;
            let hide = false;

            if (key === 'xanax') {
                // Priority active → always show, no matter what else is going on
                if (_forceXanax) { hide = false; }
                // Hidden in settings and priority off → always hide
                else if (_xanHidden) { hide = true; }
                // Otherwise normal travel/abroad rules apply
                else if (isTraveling) { hide = true; }
                else if (isAbroad)    { hide = true; }
            } else {
                if (isTraveling && (key === 'travel' || key === 'abroad')) hide = true;
                if (!isTraveling && !isAbroad && key === 'abroad') hide = true;
                // Abroad: show ONLY the Abroad tab (mirrors how Dest works when traveling)
                if (isAbroad && !isTraveling && (key === 'sets' || key === 'travel')) hide = true;
            }

            t.style.display = hide ? 'none' : '';
        });

        // Show ABROAD tab label with country flag when landed
        const abroadTabEl = tabsEl.querySelector('#lt-tab-abroad');
        if (abroadTabEl && isAbroad) {
            // Use flag image + text for the tab label (emoji shows as letters on Windows)
            abroadTabEl.innerHTML = '';
            abroadTabEl.style.display = 'flex';
            abroadTabEl.style.alignItems = 'center';
            abroadTabEl.style.justifyContent = 'center';
            abroadTabEl.style.gap = '4px';
            const _aFlagEl = makeFlagEl(abroadCountry, 14);
            const _aLblEl = document.createElement('span'); _aLblEl.textContent = 'Abroad';
            abroadTabEl.appendChild(_aFlagEl); abroadTabEl.appendChild(_aLblEl);
        }

        tabsEl.style.borderBottom = isTraveling ? 'none' : '';

        // Sync active highlight
        const tabDimCol = isLightMode() ? 'rgba(0,60,85,0.75)' : 'rgba(0,200,224,0.80)';
        tabsEl.querySelectorAll('[data-tab]').forEach(t => {
            const isActive = t.dataset.tab === activeTab;
            t.classList.toggle('lt-tab-active', isActive);
            t.style.color             = isActive ? C.gold : tabDimCol;
            t.style.borderBottomColor = isActive ? C.gold : 'transparent';
            t.style.background        = isActive ? C.goldGlow : 'transparent';
        });
    }

    renderStatusBar();
    if (activeTab === 'dest')   renderDestBody();
    if (activeTab === 'sets')   renderSetsBody();
    if (activeTab === 'xanax')  renderXanaxBody();
    if (activeTab === 'travel') renderTravelBody();
    if (activeTab === 'abroad') renderAbroadBody();
}
 
/* ─────────────────────────────────────────
   BUILD PANEL
───────────────────────────────────────── */

/* ─────────────────────────────────────────
   SHARED PANEL HEADER BUILDER
   All panels, popups, overlays use this.
   Background: #00283a  |  Text: #00b7cf
───────────────────────────────────────── */
function buildHdr(titleText, closeCallback, extraEl) {
    const hdr = document.createElement('div');
    hdr.style.cssText = 'display:flex;align-items:center;gap:8px;padding:10px 14px;background:#00283a;border-bottom:1px solid rgba(0,180,210,0.3);flex-shrink:0;';
    const ttl = document.createElement('div');
    ttl.style.cssText = 'font-size:11px;font-weight:700;letter-spacing:1.2px;text-transform:uppercase;color:#00b7cf;flex:1;font-family:Arial,sans-serif;';
    ttl.textContent = titleText;
    hdr.appendChild(ttl);
    if (extraEl) hdr.appendChild(extraEl);
    if (closeCallback) {
        const x = document.createElement('button');
        x.type = 'button'; x.textContent = '✕';
        x.style.cssText = 'background:none;border:none;color:rgba(0,180,210,0.5);font-size:14px;cursor:pointer;padding:0 2px;line-height:1;';
        x.addEventListener('mouseover', () => x.style.color = '#00b7cf');
        x.addEventListener('mouseout',  () => x.style.color = 'rgba(0,180,210,0.5)');
        x.addEventListener('click', closeCallback);
        hdr.appendChild(x);
    }
    return hdr;
}
function buildPanel() {
    const light = isLightMode();
    const panelBg = light
        ? 'linear-gradient(158deg,rgba(240,253,255,0.99),rgba(225,248,252,0.98))'
        : 'linear-gradient(158deg,rgba(0,14,18,0.98),rgba(0,8,12,0.97))';
    const tabDimCol = light ? 'rgba(0,60,85,0.75)' : 'rgba(0,200,224,0.80)';
 
    panelEl.setAttribute('style',
        'position:fixed !important;width:0 !important;opacity:0 !important;' +
        'max-height:84vh !important;' +
        `background:${panelBg} !important;` +
        `border:1px solid ${C.border} !important;` +
        'border-radius:11px !important;z-index:999989 !important;' +
        'display:flex !important;flex-direction:column !important;' +
        `box-shadow:0 10px 44px ${light ? 'rgba(0,0,0,0.25)' : 'rgba(0,0,0,0.9)'} !important;` +
        'backdrop-filter:blur(14px) !important;overflow:hidden !important;' +
        'transition:width 0.26s ease,opacity 0.2s ease !important;' +
        `font-family:Arial,sans-serif !important;color:${C.text} !important;`
    );
 
    // ── HEADER ──
    const hdr = document.createElement('div');
    hdr.style.cssText = 'padding:8px 12px;background:#00283a;border-bottom:1px solid rgba(0,180,210,0.3);display:flex;align-items:center;gap:7px;flex-shrink:0;';

    const titleEl = document.createElement('span');
    titleEl.id = 'lt-panel-title';
    titleEl.style.cssText = 'font-size:10.5px;font-weight:700;letter-spacing:1.5px;text-transform:uppercase;color:#00b7cf;flex:1;white-space:nowrap;font-family:Arial,sans-serif;';
    titleEl.innerHTML = '<span class="lt-float" style="display:inline-block;margin-right:4px;">✈</span> Sets Tracker <small style="font-size:7px;font-weight:400;opacity:0.5;letter-spacing:0.5px;vertical-align:middle;">v9.8.0</small>';
 
    function iconBtn(svg, title) {
        const b = document.createElement('span'); b.title = title; b.innerHTML = svg;
        b.setAttribute('style', 'width:20px;height:20px;cursor:pointer;flex-shrink:0;display:flex;align-items:center;justify-content:center;color:#00b7cf;transition:color 0.18s;opacity:0.85;');
        b.addEventListener('mouseover', () => { b.style.color = '#5de0f5'; b.style.opacity = '1'; });
        b.addEventListener('mouseout',  () => { b.style.color = '#00b7cf'; b.style.opacity = '0.85'; });
        return b;
    }
    const SVG_REFRESH = '<svg width="14" height="14" viewBox="0 0 16 16" fill="none"><path d="M13.5 8A5.5 5.5 0 1 1 8 2.5c1.8 0 3.4.87 4.4 2.2" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/><polyline points="11,1 13.5,3.5 11,6" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/></svg>';
    const SVG_GEAR    = '<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg"><path d="M12 15.5A3.5 3.5 0 0 1 8.5 12 3.5 3.5 0 0 1 12 8.5a3.5 3.5 0 0 1 3.5 3.5 3.5 3.5 0 0 1-3.5 3.5m7.43-2.92c.04-.34.07-.69.07-1.08s-.03-.73-.07-1.08l2.33-1.82c.21-.16.27-.45.13-.69l-2.2-3.81c-.14-.24-.42-.32-.67-.24l-2.75 1.1c-.57-.44-1.18-.8-1.85-1.08L14.14 2.1c-.05-.27-.27-.47-.55-.47H10.41c-.28 0-.5.2-.55.47L9.42 5.08C8.75 5.36 8.14 5.72 7.57 6.16l-2.75-1.1c-.25-.08-.53 0-.67.24l-2.2 3.81c-.14.24-.08.53.13.69l2.33 1.82C4.37 11.27 4.34 11.62 4.34 12s.03.73.07 1.08L2.08 14.9c-.21.16-.27.45-.13.69l2.2 3.81c.14.24.42.32.67.24l2.75-1.1c.57.44 1.18.8 1.85 1.08l.43 2.98c.05.27.27.47.55.47h4.18c.28 0 .5-.2.55-.47l.43-2.98c.67-.28 1.28-.64 1.85-1.08l2.75 1.1c.25.08.53 0 .67-.24l2.2-3.81c.14-.24.08-.53-.13-.69l-2.33-1.82z"/></svg>';
 
    const btnRefresh  = iconBtn(SVG_REFRESH, 'Refresh data');
    const btnSettings = iconBtn(SVG_GEAR, 'Settings');
 
    btnRefresh.addEventListener('click', () => {
        invCache = {}; abroadCache = {}; xanSACache = { qty:0, price:0 }; xanFacCache = null; xanPersonal = cfg.getXanCount(); yataPriceCache = {}; marketValueCache = {}; yataCityCache = {}; travelStatus = null; // xanPersonal preserved from last scrape
        renderPanel(); refreshAll(); toast('Refreshing…', 1500);
    });
    btnSettings.addEventListener('click', openSettings);
 
    hdr.appendChild(titleEl); hdr.appendChild(btnRefresh); hdr.appendChild(btnSettings);
 
    // ── TABS ──
    const tabs = document.createElement('div');
    tabs.id = 'lt-tabs';
    tabs.setAttribute('style', `display:flex;flex-shrink:0;border-bottom:1px solid ${C.border};`);

    function makeTab(label, key) {
        const t = document.createElement('div');
        t.textContent = label; t.dataset.tab = key;
        t.setAttribute('style', `flex:1;padding:6px 3px;font-size:9.5px;font-weight:700;letter-spacing:.6px;text-transform:uppercase;color:${tabDimCol};cursor:pointer;text-align:center;border-bottom:2px solid transparent;transition:all 0.2s;user-select:none;font-family:Arial,sans-serif;`);
        t.addEventListener('mouseover', () => { if (!t.classList.contains('lt-tab-active')) t.style.color = C.gold; });
        t.addEventListener('mouseout',  () => { if (!t.classList.contains('lt-tab-active')) t.style.color = tabDimCol; });
        t.addEventListener('click', () => {
            tabs.querySelectorAll('[data-tab]').forEach(x => {
                x.classList.remove('lt-tab-active');
                x.style.color = tabDimCol;
                x.style.borderBottomColor = 'transparent';
                x.style.background = 'transparent';
            });
            t.classList.add('lt-tab-active');
            t.style.color = C.gold;
            t.style.borderBottomColor = C.gold;
            t.style.background = C.goldGlow;
            activeTab = key;
            renderPanel();
        });
        return t;
    }

    // Build tabs with placeholders — renderPanel will update the first tab dynamically
    const tabFirst  = makeTab('🎒 Sets',  'sets');
    tabFirst.id = 'lt-tab-first';
    const tabXanax  = makeTab('Xanax', 'xanax');
    // Replace text with xanax item image + label
    tabXanax.innerHTML = '';
    tabXanax.style.display = 'flex';
    tabXanax.style.alignItems = 'center';
    tabXanax.style.justifyContent = 'center';
    tabXanax.style.gap = '4px';
    const _xanTabImg = document.createElement('img');
    _xanTabImg.src = itemImg(XANAX_ID);
    _xanTabImg.alt = 'Xanax';
    _xanTabImg.style.cssText = 'width:14px;height:14px;object-fit:contain;flex-shrink:0;';
    _xanTabImg.addEventListener('error', () => { _xanTabImg.replaceWith(Object.assign(document.createElement('span'), {textContent:'🧪'})); });
    const _xanTabLbl = document.createElement('span'); _xanTabLbl.textContent = 'Xanax';
    tabXanax.appendChild(_xanTabImg); tabXanax.appendChild(_xanTabLbl);
    const tabTravel = makeTab('✈ Travel', 'travel');

    const tabAbroad = makeTab('📍 Abroad', 'abroad');
    tabAbroad.id = 'lt-tab-abroad';
    tabAbroad.style.display = 'none'; // hidden until landed abroad

    tabFirst.classList.add('lt-tab-active');
    tabFirst.style.color = C.gold; tabFirst.style.borderBottomColor = C.gold; tabFirst.style.background = C.goldGlow;
    tabs.appendChild(tabFirst); tabs.appendChild(tabXanax); tabs.appendChild(tabTravel); tabs.appendChild(tabAbroad);
 
    // ── STATUS BAR ──
    const sbar = document.createElement('div');
    sbar.id = 'lt-sbar';
    sbar.setAttribute('style', `padding:3px 10px;font-size:9px;letter-spacing:.4px;color:${C.textDim};border-bottom:1px solid ${C.border};display:flex;gap:8px;align-items:center;flex-shrink:0;font-family:Consolas,monospace;`);
    sbar.textContent = 'Loading…';
 
    // ── BODY ──
    const bdy = document.createElement('div'); bdy.className = 'lt-body';
 
    panelEl.appendChild(hdr);
    panelEl.appendChild(tabs);
    panelEl.appendChild(sbar);
    panelEl.appendChild(bdy);
}
 
/* ─────────────────────────────────────────
   TOGGLE BUTTON
───────────────────────────────────────── */
function buildToggle() {
    const SVG = `<svg width="28" height="28" viewBox="0 0 44 44" fill="none">
        <path d="M6 22 L38 10 L32 22 L38 34 Z" fill="rgba(0,200,224,0.18)" stroke="rgba(0,200,224,0.8)" stroke-width="1.3" stroke-linejoin="round"/>
        <path d="M20 22 L32 22 L38 34 L20 28 Z" fill="rgba(0,200,224,0.12)" stroke="rgba(0,200,224,0.55)" stroke-width="1" stroke-linejoin="round"/>
        <path d="M18 16 L26 12 L28 18 L18 22 Z" fill="rgba(0,200,224,0.15)" stroke="rgba(0,190,220,0.6)" stroke-width="0.9" stroke-linejoin="round"/>
        <circle cx="14" cy="22" r="2" fill="rgba(0,200,224,0.7)"/>
    </svg>`;
 
    toggleEl.innerHTML = SVG;
    toggleEl.title = 'Loot Tracker · Right-click = Settings';
    const toggleBg = isLightMode()
        ? 'radial-gradient(circle at 38% 34%,#d0f8ff,#a8eef8)'
        : 'radial-gradient(circle at 38% 34%,#001a20,#000c10)';
    toggleEl.setAttribute('style',
        'position:fixed !important;width:46px !important;height:46px !important;' +
        `background:${toggleBg} !important;` +
        `border:2px solid ${C.border} !important;` +
        'border-radius:50% !important;' +
        'display:flex !important;align-items:center !important;justify-content:center !important;' +
        'cursor:grab !important;z-index:999990 !important;' +
        `box-shadow:0 0 16px ${C.goldGlow},inset 0 0 12px rgba(0,0,0,0.2) !important;` +
        'user-select:none !important;touch-action:none !important;overflow:visible !important;'
    );
    toggleEl.addEventListener('mouseover', () => {
        toggleEl.style.setProperty('box-shadow', `0 0 24px ${C.gold}aa,inset 0 0 12px rgba(0,0,0,0.2)`, 'important');
        toggleEl.style.setProperty('border-color', C.gold, 'important');
    });
    toggleEl.addEventListener('mouseout', () => {
        toggleEl.style.setProperty('box-shadow', `0 0 16px ${C.goldGlow},inset 0 0 12px rgba(0,0,0,0.2)`, 'important');
        toggleEl.style.setProperty('border-color', C.border, 'important');
    });
}
 
/* ─────────────────────────────────────────
   PANEL OPEN/CLOSE + POSITION
───────────────────────────────────────── */
function positionPanel() {
    const tr = toggleEl.getBoundingClientRect();
    panelEl.style.setProperty('top', Math.min(tr.top, window.innerHeight - 80) + 'px', 'important');
    panelEl.style.setProperty('bottom', 'auto', 'important');
    if (toggleEl._side === 'left') {
        panelEl.style.setProperty('left',  (tr.right + 7) + 'px', 'important');
        panelEl.style.setProperty('right', 'auto', 'important');
    } else {
        panelEl.style.setProperty('right', (window.innerWidth - tr.left + 7) + 'px', 'important');
        panelEl.style.setProperty('left',  'auto', 'important');
    }
}
 
function openPanel()  { panelOpen = true;  panelEl.style.setProperty('width','310px','important'); panelEl.style.setProperty('opacity','1','important'); positionPanel(); renderPanel(); }
function closePanel() { panelOpen = false; panelEl.style.setProperty('width','0','important');     panelEl.style.setProperty('opacity','0','important'); }
 
/* ─────────────────────────────────────────
   DRAG TO SNAP
───────────────────────────────────────── */
function setupDrag() {
    const SZ = 46, EDGE = 6;
 
    function snap(side, top) {
        toggleEl._side = side;
        const t = Math.max(EDGE, Math.min(top, window.innerHeight - SZ - EDGE));
        toggleEl.style.setProperty('top',    t + 'px', 'important');
        toggleEl.style.setProperty('bottom', 'auto', 'important');
        toggleEl.style.setProperty('left',   side === 'left'  ? EDGE + 'px' : 'auto', 'important');
        toggleEl.style.setProperty('right',  side === 'right' ? EDGE + 'px' : 'auto', 'important');
        if (panelOpen) positionPanel();
    }
 
    try {
        const saved = JSON.parse(store.get('lt_pos', '{}'));
        snap(saved.side || 'right', saved.top || 280);
    } catch(e) { snap('right', 280); }
 
    let dragging = false, moved = false, sX, sY, sL, sT;
 
    function start(cx, cy) { moved = false; dragging = true; sX = cx; sY = cy; const r = toggleEl.getBoundingClientRect(); sL = r.left; sT = r.top; toggleEl.style.setProperty('opacity','0.7','important'); toggleEl.style.setProperty('transform','scale(1.1)','important'); }
    function move(cx, cy) {
        if (!dragging) return;
        const dx = cx - sX, dy = cy - sY;
        if (!moved && Math.hypot(dx, dy) < 5) return;
        moved = true;
        toggleEl.style.setProperty('left',   Math.max(EDGE, Math.min(sL + dx, window.innerWidth  - SZ - EDGE)) + 'px', 'important');
        toggleEl.style.setProperty('right',  'auto', 'important');
        toggleEl.style.setProperty('top',    Math.max(EDGE, Math.min(sT + dy, window.innerHeight - SZ - EDGE)) + 'px', 'important');
        toggleEl.style.setProperty('bottom', 'auto', 'important');
        if (panelOpen) positionPanel();
    }
    function end() {
        if (!dragging) return; dragging = false;
        toggleEl.style.setProperty('opacity','1','important'); toggleEl.style.setProperty('transform','none','important');
        if (!moved) return;
        const r    = toggleEl.getBoundingClientRect();
        const side = (r.left + SZ / 2) < window.innerWidth / 2 ? 'left' : 'right';
        snap(side, r.top);
        store.set('lt_pos', JSON.stringify({ side, top: r.top }));
    }
 
    toggleEl.addEventListener('mousedown',  e => { if (e.button !== 0) return; start(e.clientX, e.clientY); });
    document.addEventListener('mousemove',  e => move(e.clientX, e.clientY));
    document.addEventListener('mouseup',    end);
    toggleEl.addEventListener('touchstart', e => { const t = e.touches[0]; start(t.clientX, t.clientY); }, { passive: true });
    toggleEl.addEventListener('touchmove',  e => { if (!dragging) return; e.preventDefault(); const t = e.touches[0]; move(t.clientX, t.clientY); }, { passive: false });
    toggleEl.addEventListener('touchend',   end);
 
    toggleEl.addEventListener('click',       () => { if (moved) return; if (panelOpen) closePanel(); else openPanel(); });
    toggleEl.addEventListener('contextmenu', e => { e.preventDefault(); openSettings(); });
 
    /* ── Carousel Tooltip ── */
    (function buildCarousel() {
        const slides = [
            { icon: '✈️', title: 'Sets Tracker',         body: 'Your overseas sets companion for Torn. Tracks Plushie, Flower, Prehistoric & Special sets plus Xanax supply runs — live stock, profit analysis, and travel planning in one panel.' },
            { icon: '🎒', title: 'Sets Tab',              body: 'Items sorted by bottleneck (lowest extra first). Own = your extras after completing full sets. Abroad = live YATA overseas stock. Tap any item row to expand buy price, sell value, profit, and price history sparkline.' },
            { icon: '🟢', title: 'Stock Colours',         body: 'Abroad column: Green = above threshold (Plushie ≥2000 / Flower ≥5000). Orange = below threshold. Red = zero stock. 🏪 BoB badge = live Bits n\' Bobs shop count for Sheep, Teddy & Kitten plushies.' },
            { icon: '🧪', title: 'Xanax Tab',             body: 'Personal count auto-reads when you visit your Items page. Shows personal + faction supply, selected country stock & price, carry limit, and trip cost (price × carry). Tap the amount to copy it for your vault.' },
            { icon: '🔑', title: 'Xanax Country',         body: 'Tap the country pill in the Xanax Runs header to change which country\'s live YATA stock and price is shown. Only countries confirmed to have Xanax in stock are listed.' },
            { icon: '✈',  title: 'Xanax Runs',            body: 'Start a contract run from the Xanax tab. Set client name, total qty, and your sell price. Log each trip with one tap. Tracks progress bar, profit vs buy price, payment status, and full trip history.' },
            { icon: '📊', title: 'Run Focus Mode',         body: 'When a run is active, the Sets tab collapses to Xanax-only and Travel pins your Xanax country at the top. Badge shows trips needed (remaining ÷ carry) with overage. Tap ✓ End to complete the run.' },
            { icon: '🎯', title: 'Xanax Priority',         body: 'Enable Priority in the Xanax tab to pin your Xanax country in Travel and collapse Sets to Xanax-only until your personal count hits your threshold. Set target count in the Stop above field.' },
            { icon: '💰', title: 'Pure Profit',            body: 'Tap ⚙ in the Travel tab Pure Profit header to open the profit calculator. Filter by item type, country range, and travel speed. Top 3 items ranked by $/hr — tap any to see full buy/sell/profit breakdown and stock sparkline.' },
            { icon: '✈',  title: 'Travel Tab',             body: 'Loot Run Planner ranks all 11 countries by items needed. Tap a row to navigate directly to the Torn travel page. Items per Country (collapsible) shows YATA stock, buy/sell prices, and per-trip profit for every item.' },
            { icon: '🏛️', title: 'Museum Day Bonus',       body: 'Within 7 days of Museum Day (or when active), a gold banner shows bonus sets, pts, and value if you wait. Enable Always Show in Settings → Museum Day to keep it pinned permanently.' },
            { icon: '⚙',  title: 'Settings',               body: 'Right-click the ✈ button (or tap ⚙ in the header). Configure API key, User ID, section visibility, carry limit, Museum Day pin, theme (Auto / Reverse / Light / Dark), and tooltip reset.' },
            { icon: '↕',  title: 'Reverse Theme',           body: 'New in v9.8.0. Under Appearance in Settings, the Reverse option flips the script\u2019s palette opposite to Torn\u2019s. Dark page → light script. Light page → dark script. Only applies when Torn\u2019s theme is auto-detected.' },
        ];
 
        let cur = 0;
        const tip = document.createElement('div');
        tip.id = 'lt-carousel';
        tip.setAttribute('style',
            'position:fixed !important;bottom:72px !important;right:12px !important;' +
            'width:234px !important;' +
            'background:linear-gradient(145deg,rgba(0,12,18,0.97),rgba(0,8,14,0.97)) !important;' +
            'border:1px solid rgba(0,180,210,0.45) !important;border-radius:10px !important;' +
            'z-index:999995 !important;padding:12px 14px 10px !important;' +
            'box-shadow:0 6px 28px rgba(0,0,0,0.85) !important;' +
            'font-family:Arial,sans-serif !important;color:#e8f0d0 !important;' +
            'opacity:0 !important;transition:opacity 0.35s ease !important;'
        );
 
        const iconEl   = document.createElement('div');
        const titleEl  = document.createElement('div');
        const bodyEl   = document.createElement('div');
        const dotsWrap = document.createElement('div');
        const navWrap  = document.createElement('div');
 
        iconEl.setAttribute('style',
            'font-size:24px !important;margin-bottom:5px !important;line-height:1 !important;');
        titleEl.setAttribute('style',
            'font-size:11px !important;font-weight:700 !important;color:#00c8e0 !important;' +
            'margin-bottom:5px !important;letter-spacing:.5px !important;text-transform:uppercase !important;');
        bodyEl.setAttribute('style',
            'font-size:10.5px !important;line-height:1.55 !important;' +
            'color:rgba(190,240,248,0.88) !important;min-height:48px !important;');
        dotsWrap.setAttribute('style',
            'display:flex !important;justify-content:center !important;gap:6px !important;' +
            'margin-top:2px !important;align-items:center !important;');
        navWrap.setAttribute('style',
            'display:flex !important;justify-content:space-between !important;' +
            'align-items:center !important;margin-top:9px !important;');
 
        function makeDot(i) {
            const d = document.createElement('div');
            d.setAttribute('style',
                'width:6px !important;height:6px !important;border-radius:50% !important;' +
                'cursor:pointer !important;flex-shrink:0 !important;transition:background 0.2s !important;' +
                'background:' + (i === cur ? 'rgba(0,200,224,0.9)' : 'rgba(120,120,120,0.3)') + ' !important;'
            );
            (function(idx) { d.addEventListener('click', function() { clearInterval(autoTimer); go(idx); }); })(i);
            return d;
        }
 
        function renderDots() {
            dotsWrap.innerHTML = '';
            for (let i = 0; i < slides.length; i++) dotsWrap.appendChild(makeDot(i));
        }
 
        function go(n) {
            cur = (n + slides.length) % slides.length;
            iconEl.textContent  = slides[cur].icon;
            titleEl.textContent = slides[cur].title;
            bodyEl.textContent  = slides[cur].body;
            renderDots();
        }
 
        const prevBtn = document.createElement('div');
        prevBtn.textContent = '◀';
        prevBtn.setAttribute('style',
            'cursor:pointer !important;font-size:12px !important;color:rgba(0,190,215,0.8) !important;' +
            'padding:3px 8px !important;user-select:none !important;border-radius:4px !important;' +
            'border:1px solid rgba(0,170,205,0.3) !important;'
        );
        prevBtn.addEventListener('click', function() { clearInterval(autoTimer); go(cur - 1); });
 
        const nextBtn = document.createElement('div');
        nextBtn.textContent = '▶';
        nextBtn.setAttribute('style',
            'cursor:pointer !important;font-size:12px !important;color:rgba(0,190,215,0.8) !important;' +
            'padding:3px 8px !important;user-select:none !important;border-radius:4px !important;' +
            'border:1px solid rgba(0,170,205,0.3) !important;'
        );
        nextBtn.addEventListener('click', function() { clearInterval(autoTimer); go(cur + 1); });
 
        const closeBtn = document.createElement('div');
        closeBtn.textContent = '✕';
        closeBtn.setAttribute('style',
            'position:absolute !important;top:8px !important;right:10px !important;' +
            'cursor:pointer !important;font-size:11px !important;' +
            'color:rgba(0,190,215,0.65) !important;line-height:1 !important;user-select:none !important;'
        );
        closeBtn.addEventListener('click', function() {
            clearInterval(autoTimer);
            tip.style.setProperty('opacity', '0', 'important');
            setTimeout(function() { if (tip.parentNode) tip.parentNode.removeChild(tip); }, 350);
        });
 
        navWrap.appendChild(prevBtn);
        navWrap.appendChild(dotsWrap);
        navWrap.appendChild(nextBtn);
 
        tip.appendChild(closeBtn);
        tip.appendChild(iconEl);
        tip.appendChild(titleEl);
        tip.appendChild(bodyEl);
        tip.appendChild(navWrap);
 
        go(0);
 
        let autoTimer = setInterval(function() { go(cur + 1); }, 10000);
 
        // Only show once — persisted in localStorage
        const SEEN_KEY = 'lt_tooltip_seen';
        let alreadySeen = false;
        try { alreadySeen = !!localStorage.getItem(SEEN_KEY); } catch(e) {}
        if (alreadySeen) return;
 
        document.body.appendChild(tip);
        setTimeout(function() { tip.style.setProperty('opacity', '1', 'important'); }, 1800);
 
        function markSeen() {
            try { localStorage.setItem(SEEN_KEY, '1'); } catch(e) {}
        }
        closeBtn.addEventListener('click', markSeen);
 
        // Also mark seen after cycling through all slides once
        let seenCount = 0;
        const origGo = go;
        go = function(n) {
            origGo(n);
            seenCount++;
            if (seenCount >= slides.length) markSeen();
        };
    })();
}
 
/* ─────────────────────────────────────────
   REFRESH
───────────────────────────────────────── */
/* ─────────────────────────────────────────
   LIVE COUNTDOWN — ticks every second while traveling
   Updates travelStatus.timeLeft and re-renders Dest body
   Works on any Torn page since it uses setInterval, not DOM
───────────────────────────────────────── */
function manageTravelCountdown() {
    const traveling = !!(travelStatus && travelStatus.traveling && travelStatus.timeLeft > 0);
    if (traveling && !countdownTimer) {
        // Start ticking
        countdownTimer = setInterval(() => {
            if (!travelStatus || !travelStatus.traveling) {
                clearInterval(countdownTimer); countdownTimer = null; return;
            }
            travelStatus.timeLeft = Math.max(0, (travelStatus.timeLeft || 0) - 1);
            // Update only the time-remaining cell — don't rebuild DOM (would close expanded rows)
            if (panelEl && panelOpen && activeTab === 'dest') {
                var _tEl = panelEl.querySelector('#lt-dest-time');
                if (_tEl) {
                    var _s = travelStatus.timeLeft;
                    var _h = Math.floor(_s/3600), _m = Math.floor((_s%3600)/60), _sec = _s%60;
                    _tEl.textContent = _h > 0 ? _h+'h '+_m+'m '+_sec+'s' : _m > 0 ? _m+'m '+_sec+'s' : _sec+'s';
                } else {
                    renderDestBody();
                }
            }
            // Landing detection — fire when we tick to zero
            if (travelStatus.timeLeft <= 0) {
                clearInterval(countdownTimer); countdownTimer = null;
                onLanded();
            }
        }, 1000);
    } else if (!traveling && countdownTimer) {
        clearInterval(countdownTimer); countdownTimer = null;
    }
}

function onLanded() {
    // Capture landing info before wiping travelStatus
    const wasReturn  = travelStatus && travelStatus.isReturn;
    const landedDest = travelStatus && travelStatus.destination; // country name for outbound
    const landedOrig = travelStatus && travelStatus.origin;     // city name for return scrape

    travelStatus = { traveling: false, destination: '', timeLeft: 0, departed: 0, isReturn: false, origin: '' };

    if (wasReturn) {
        // Landed back in Torn City
        abroadCountry = '';
        activeTab = 'sets';
        if (panelEl) renderPanel();
        toast('✅ Landed! Welcome back to Torn City.', 4000);
    } else {
        // Landed in a foreign country — toast only; ABROAD tab appears when API confirms state=Abroad
        const code     = landedDest ? TORN_DEST_TO_CODE[landedDest] : null;
        const cityName = TORN_CITIES[landedDest] || (code ? (YATA_CODE_TO_CITY[code] || landedDest) : (landedOrig || landedDest || 'your destination'));
        const ctryName = landedDest || '';
        const msg      = ctryName && cityName !== ctryName
            ? `✅ Landed! Welcome to ${cityName}, ${ctryName}.`
            : `✅ Landed! Welcome to ${cityName}.`;
        toast(msg, 5000);
    }
}

async function refreshAll() {
    isLoading = true;
    renderStatusBar();
 
    await Promise.allSettled([
        fetchInventory()
            .then(d  => { invCache = d; })
            .catch(() => {}),
        // xanPersonal is scraped from items page via watchItemsPage()
        fetchYataData()
            .then(result => {
                if (result && result.map)  abroadCache = result.map;
                if (result && result.sa)   xanSACache  = result.sa;
                console.log('[SetsTracker] xanSACache set:', JSON.stringify(xanSACache));
            })
            .catch(e => { console.warn('[SetsTracker] YATA assign failed:', e); }),
        fetchBobStock()
            .then(d  => { bobCache = d; })
            .catch(() => {}),
        fetchXanaxFaction()
            .then(d  => { if (d !== null) xanFacCache = d; })
            .catch(() => {}),
        fetchPointsPrice()
            .then(p  => { pointsPrice = p; })
            .catch(() => {}),
        fetchMarketValues()
            .then(d  => { marketValueCache = d; })
            .catch(() => {}),
        fetchTravelStatus()
            .then(s  => {
                travelStatus = s;
                if (s && s.abroad) {
                    // Player is abroad (landed, not flying) — show ABROAD tab
                    // destination may be empty when landed; keep last known if so
                    if (s.destination) abroadCountry = s.destination;
                    if (abroadCountry) {
                        activeTab = 'abroad';
                        abroadPlayerData = s; // store full abroad data for Abroad tab
                    }
                } else if (s && !s.abroad && !s.traveling) {
                    // Back home — clear abroad state
                    if (abroadCountry) { abroadCountry = ''; activeTab = 'sets'; }
                }
                scrapeTravelPage();
            })
            .catch(() => {}),
    ]);
 
    isLoading = false;
    if (panelEl) renderPanel();
    // Start/stop live countdown based on travel state
    manageTravelCountdown();
}
 
async function mainLoop() {
    await refreshAll();
    pollTimer = setTimeout(mainLoop, 45000);
}
 
/* ─────────────────────────────────────────
   INIT
───────────────────────────────────────── */
 
// Click a country row on the travel page — works in both TornPDA and Tampermonkey
function clickCountry(name) {
    var tLow = name.toLowerCase().trim();
    var doc  = (typeof unsafeWindow !== 'undefined' && unsafeWindow.document) ? unsafeWindow.document : document;

    // ── Pass 1: TornPDA — confirmed working via flagAndName divs ──
    var cells = doc.querySelectorAll('div[class*="flagAndName"]');
    for (var i = 0; i < cells.length; i++) {
        var txt = (cells[i].textContent || '').trim().toLowerCase();
        if (txt === tLow || txt.indexOf(tLow) === 0) {
            var p = cells[i].parentElement;
            if (p) { p.click(); return true; }
        }
    }

    // ── Pass 2: Desktop React — pins have empty innerHTML (React virtual DOM).
    // Match by style.left CSS position confirmed from live console inspection.
    // style.left values are stable per-country on the world map.
    // Each pin also has a sibling input[class*="destinationRadio___"] — click that.
    var DESKTOP_PIN_LEFT = {
        'Mexico':          '350px',
        'Cayman Islands':  '408px',
        'Canada':          '410px',
        'Hawaii':          '248px',
        'United Kingdom':  '576px',
        'UK':              '576px',
        'Argentina':       '454px',
        'Switzerland':     '597px',
        'Japan':           '109px',
        'China':            '61px',
        'UAE':             '694px',
        'South Africa':    '639px',
    };

    // Find the canonical country name key
    var pinLeft = null;
    for (var country in DESKTOP_PIN_LEFT) {
        if (country.toLowerCase() === tLow) { pinLeft = DESKTOP_PIN_LEFT[country]; break; }
    }

    if (pinLeft) {
        var pins = doc.querySelectorAll('div[class*="pin___"]');
        for (var j = 0; j < pins.length; j++) {
            if (pins[j].style.left === pinLeft) {
                // Try clicking the radio input inside, then the pin itself
                var radio = pins[j].querySelector('input[class*="destinationRadio___"]');
                if (radio) { radio.click(); return true; }
                pins[j].click(); return true;
            }
        }
    }

    // ── Pass 3: Fallback — any short element whose trimmed text exactly matches ──
    var all = doc.querySelectorAll('a, button, li, span, [role="button"], [role="option"]');
    for (var k = 0; k < all.length; k++) {
        var t = (all[k].textContent || '').trim().toLowerCase();
        if (t === tLow && t.length < 30) {
            all[k].click();
            return true;
        }
    }

    return false;
}
 
function init() {
    xanPersonal = cfg.getXanCount(); // restore last known count from previous scrape
    countryFlagCache = cfg.getFlagCache(); // restore scraped flag images
    // Auto-click via page-context script injection (bypasses GM sandbox)
    (function() {
        var target = null;
        try {
            var m = (location.hash || '').match(/lt_travel=([^&]+)/);
            if (m) target = decodeURIComponent(m[1]);
        } catch(e) {}
        if (!target || (location.pathname + location.search).indexOf('sid=travel') === -1) return;
 
 
        var tName = target;
        console.log('[SetsTracker] init: will auto-click', JSON.stringify(tName), 'on travel page');
        // Poll every 300ms for up to 15s — React travel page renders async
        var _attempts = 0;
        var _poll = setInterval(function() {
            _attempts++;
            if (_attempts > 50) {
                clearInterval(_poll);
                console.warn('[SetsTracker] gave up clicking', JSON.stringify(tName), 'after 15s');
                return;
            }
            if (clickCountry(tName)) { clearInterval(_poll); }
        }, 300);
        // Also try with MutationObserver in case React renders after 300ms intervals
        try {
            var _obs = new MutationObserver(function() {
                if (clickCountry(tName)) { _obs.disconnect(); clearInterval(_poll); }
            });
            _obs.observe(document.body || document.documentElement, { childList: true, subtree: true });
            setTimeout(function(){ _obs.disconnect(); }, 16000);
        } catch(e) {}
    })();
 
    if (document.getElementById('lt-toggle')) return;
 
    try { injectCSS(); } catch(e) { console.warn('[LT] CSS inject failed:', e); }
 
    // Sync theme before building — computed styles may not be ready at parse time
    syncTheme();
 
    toggleEl = document.createElement('div'); toggleEl.id = 'lt-toggle';
    panelEl  = document.createElement('div'); panelEl.id  = 'lt-panel';
 
    document.body.appendChild(panelEl);
    document.body.appendChild(toggleEl);
 
    buildToggle();
    buildPanel();
 
    // TornPDA sets its theme class asynchronously — recheck at 500ms, 1.5s, 3s.
    // Each check syncs the palette and, if the theme genuinely flipped,
    // rebuilds the panel chrome so colours are correct.
    [500, 1500, 3000].forEach(delay => {
        setTimeout(() => {
            const wasDark = !isLightMode();
            syncTheme(true);
            const isDark = !isLightMode();
            if (wasDark !== isDark) {
                if (toggleEl) buildToggle();
                if (panelEl)  {
                    const wasOpen = panelOpen;
                    panelEl.innerHTML = '';
                    buildPanel();
                    if (wasOpen) openPanel();
                }
            }
        }, delay);
    });
    setupDrag();
 
    if (!cfg.apiKey) {
        setTimeout(openSettings, 600);
    } else {
        mainLoop();
    }
}
 
if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', () => setTimeout(init, 400));
} else {
    setTimeout(init, 400);
}
 
})();