✈️ Expedition Tracker

OSDevscape presents The Grand & Spectacular Overseas Expedition Tracker — Sets · Xanax Runs · Travel Planner · Pure Profit · Abroad HQ · Museum Day · Live YATA Stock — Torn

Tendrás que instalar una extensión para tu navegador como Tampermonkey, Greasemonkey o Violentmonkey si quieres utilizar este script.

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

Tendrás que instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Userscripts para instalar este script.

Tendrás que instalar una extensión como Tampermonkey antes de poder instalar este script.

Necesitarás instalar una extensión para administrar scripts de usuario si quieres instalar este script.

(Ya tengo un administrador de scripts de usuario, déjame instalarlo)

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

(Ya tengo un administrador de estilos de usuario, déjame instalarlo)

// ==UserScript==
// @name         ✈️ Expedition Tracker
// @namespace    https://osdevscape.com
// @version      10.5.0
// @author       Phillip_J_Fry [2184575] (OSMays8338) — OSDevscape
// @license      All Rights Reserved © 2026 OSDevscape
// @homepageURL  https://greasyfork.org/users/OSMays8338
// @description  OSDevscape presents The Grand & Spectacular Overseas Expedition Tracker — Sets · Xanax Runs · Travel Planner · Pure Profit · Abroad HQ · Museum Day · Live YATA Stock — Torn
// @match        https://www.torn.com/*
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_xmlhttpRequest
// @grant        GM_download
// @grant        unsafeWindow
// @connect      yata.yt
// @connect      api.torn.com
// @connect      flagcdn.com
// @connect      torn.com
// @connect      www.torn.com
// @run-at       document-end
// @inject-into  auto
// ==/UserScript==
 
// ╔══════════════════════════════════════════════════════════════════════════════════════════════╗
// ║                                                                                              ║
// ║                     ✦ ✦ ✦   O S D E V S C A P E   P R E S E N T S   ✦ ✦ ✦                 ║
// ║                                                                                              ║
// ║            ████████╗██╗  ██╗███████╗     ██████╗ ██████╗  █████╗ ███╗  ██╗██████╗            ║
// ║            ╚══██╔══╝██║  ██║██╔════╝    ██╔════╝ ██╔══██╗██╔══██╗████╗ ██║██╔══██╗           ║
// ║               ██║   ███████║█████╗      ██║  ███╗██████╔╝███████║██╔██╗██║██║  ██║           ║
// ║               ██║   ██╔══██║██╔══╝      ██║   ██║██╔══██╗██╔══██║██║╚████║██║  ██║           ║
// ║               ██║   ██║  ██║███████╗    ╚██████╔╝██║  ██║██║  ██║██║ ╚███║██████╔╝           ║
// ║               ╚═╝   ╚═╝  ╚═╝╚══════╝     ╚═════╝ ╚═╝  ╚═╝╚═╝  ╚═╝╚═╝  ╚══╝╚═════╝            ║
// ║                                                                                              ║
// ║                                    █████╗ ███╗  ██╗██████╗                                   ║
// ║                                   ██╔══██╗████╗ ██║██╔══██╗                                  ║
// ║                                   ███████║██╔██╗██║██║  ██║                                  ║
// ║                                   ██╔══██║██║╚████║██║  ██║                                  ║
// ║                                   ██║  ██║██║ ╚███║██████╔╝                                  ║
// ║                                    ╚═╝  ╚═╝╚═╝  ╚══╝╚═════╝                                  ║
// ║                                                                                              ║
// ║   ███████╗██████╗ ███████╗ ██████╗████████╗ █████╗  ██████╗██╗   ██╗██╗      █████╗ ██████╗  ║
// ║   ██╔════╝██╔══██╗██╔════╝██╔════╝╚══██╔══╝██╔══██╗██╔════╝██║   ██║██║     ██╔══██╗██╔══██╗ ║
// ║   ███████╗██████╔╝█████╗  ██║        ██║   ███████║██║     ██║   ██║██║     ███████║██████╔╝ ║
// ║   ╚════██║██╔═══╝ ██╔══╝  ██║        ██║   ██╔══██║██║     ██║   ██║██║     ██╔══██║██╔══██╗ ║
// ║   ███████║██║     ███████╗╚██████╗   ██║   ██║  ██║╚██████╗╚██████╔╝███████╗██║  ██║██║  ██║ ║
// ║   ╚══════╝╚═╝     ╚══════╝ ╚═════╝   ╚═╝   ╚═╝  ╚═╝ ╚═════╝ ╚═════╝╚══════╝╚═╝  ╚═╝╚═╝  ╚═╝  ║
// ║                                                                                              ║
// ║                      ✈  Overseas  ·  Expedition  ·  Tracker   —   v10.5.0                   ║
// ║                                                                                              ║
// ║  ──────────────────────────────────────────────────────────────────────────────────────────  ║
// ║  Author  :  Phillip_J_Fry [2184575] (Torn) · OSMays8338                                      ║
// ║  Studio  :  OSDevscape                                                                       ║
// ║  License :  All Rights Reserved © 2026 OSDevscape                                            ║
// ║  Origin  :  Forked from "Points Museum" by SuperNovae [2637223]                              ║
// ║  Built   :  OSDevscape suite architecture v7                                                 ║
// ╚══════════════════════════════════════════════════════════════════════════════════════════════╝
 
(function () {
'use strict';

// ── Suppress Torn's own unhandled chat/WebSocket promise rejections ──
// Torn's vendor bundle rejects promises with undefined — not our code.
// evt.preventDefault() stops TornPDA logging a spurious [ERR] each cycle.
window.addEventListener('unhandledrejection', function(evt) {
    if (evt.reason === undefined || evt.reason === null) evt.preventDefault();
});
 
/* ─────────────────────────────────────────
   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=WLT 3=BusinessClass
    setTravelSpeed(v)   { store.setJSON('lt_travel_speed', v); },
    getHasBook()        { return store.getJSON('lt_has_book', false); },
    setHasBook(v)       { store.setJSON('lt_has_book', v); },
    getTourismPin()     { return store.getJSON('lt_tourism_pin', false); },
    setTourismPin(v)    { store.setJSON('lt_tourism_pin', 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); },
    getSetsOnly()       { return store.getJSON('lt_sets_only', true); },
    setSetsOnly(v)      { store.setJSON('lt_sets_only', v); },
    getRehabWarn()      { return store.getJSON('lt_rehab_warn', 5); },   // company addiction warning threshold
    setRehabWarn(v)     { store.setJSON('lt_rehab_warn', Number(v)); },
    getRehabEmerg()     { return store.getJSON('lt_rehab_emerg', 10); },  // company addiction emergency threshold
    setRehabEmerg(v)    { store.setJSON('lt_rehab_emerg', Number(v)); },
    getRehabBanner()    { return store.getJSON('lt_rehab_banner', true); }, // show rehab warning banner under tabs
    setRehabBanner(v)   { store.setJSON('lt_rehab_banner', v); },
    getDisplayCaseMode(){ return store.getJSON('lt_dc_mode', 0); },        // 0=off 1=sets only 2=all items
    setDisplayCaseMode(v){ store.setJSON('lt_dc_mode', Number(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:        '#001e28',
    bg2:       '#002d3a',
    border:    'rgba(0,180,210,0.35)',
    teal:      '#00c8e0',
    tealDim:   'rgba(0,200,224,0.85)',
    tealGlow:  '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,45,62,0.85)',   // card background
    cardBorder:'rgba(0,200,224,0.45)', // 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)',
    xanPersonalCol: '#00c8e0',   // teal — personal Xanax count
    xanFactionCol:  '#8BC34A',   // green — faction Xanax count
};
 
const LIGHT_PALETTE = {
    bg:        '#a8d4e8',       // noticeably deeper blue-tint base
    bg2:       '#8ec4d8',       // deeper secondary surface
    border:    'rgba(0,70,100,0.55)',
    teal:      '#002a36',       // very deep teal heading colour
    tealDim:   'rgba(0,55,75,0.90)',
    tealGlow:  '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,60,90,0.28)',   // card background
    cardBorder:'rgba(0,90,130,0.60)',  // 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)',
    xanPersonalCol: '#006080',   // deep teal — readable on light bg
    xanFactionCol:  '#3a6e00',   // deep green — readable on light bg
};
 
// 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) {}
            // Auto-fill display case amount inputs
            if (href.includes('displaycase')) {
                setTimeout(fillDisplayCaseQty, 1500);
                setTimeout(fillDisplayCaseQty, 3500);
                setTimeout(fillDisplayCaseQty, 6000);
            }

        }
    });
    navObs.observe(document.body, { childList: true, subtree: true });
    // Also check on load in case we're already on items page
    watchItemsPage();
    if (document.location.href.includes('displaycase')) {
        setTimeout(fillDisplayCaseQty, 2000);
        setTimeout(fillDisplayCaseQty, 4000);
        setTimeout(fillDisplayCaseQty, 7000);
    }

    // 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);
    }
    // Display case observer — rows load dynamically, debounced to avoid re-entry
    setTimeout(function() {
        try {
            if (!document.location.href.includes('displaycase')) return;
            var _dcTimer = null;
            var _dcObs = new MutationObserver(function() {
                try {
                    clearTimeout(_dcTimer);
                    _dcTimer = setTimeout(function() { fillDisplayCaseQty(); }, 400);
                } catch(e) {}
            });
            _dcObs.observe(document.body, { childList: true, subtree: true });
        } catch(e) {}
    }, 1000);

    // 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();
                } 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() {
    // Track the raw page theme (not the forced/reversed result)
    // so Auto and Reverse modes respond correctly when Torn's class changes
    function pageIsLight() {
        const b = document.body, h = document.documentElement;
        if (!b) return true;
        return !b.classList.contains('dark-mode') && !h.classList.contains('dark-mode');
    }
    let _lastPageLight = pageIsLight();

    function checkAndSync() {
        const nowPageLight = pageIsLight();
        if (_lastPageLight === nowPageLight) return;
        _lastPageLight = nowPageLight;
        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_NO_BOOK = {
    // country → [Standard, Airstrip, WLT, BusinessClass] hours one-way — WITHOUT Travel Book
    'Mexico':         [0.4333,  0.3,  0.2167,  0.1333],
    'Cayman Islands': [0.5833,  0.4167,  0.3,  0.1833],
    'Canada':         [0.6833,  0.4833,  0.3333,  0.2],
    'Hawaii':         [2.2333, 1.5667,  1.1167,  0.6667],
    'UK':             [2.65, 1.85, 1.3333,  0.8],
    'United Kingdom': [2.65, 1.85, 1.3333,  0.8],
    'Argentina':      [2.7833, 1.95, 1.3833,  0.8333],
    'Switzerland':    [2.9167, 2.05, 1.4667,  0.8833],
    'Japan':          [3.75, 2.6333, 1.8833, 1.1333],
    'China':          [4.0333, 2.8167, 2.0167, 1.2],
    'UAE':            [4.5167, 3.1667, 2.25, 1.35],
    'South Africa':   [4.95, 3.4667, 2.4833, 1.4833],
};

const TRAVEL_TIMES_WITH_BOOK = {
    // country → [Standard, Airstrip, WLT, BusinessClass] hours one-way — WITH Travel Book
    'Mexico':         [0.3167,  0.2333,  0.1667,  0.1],
    'Cayman Islands': [0.4333,  0.3,  0.2167,  0.1333],
    'Canada':         [0.5167,  0.3667,  0.25,  0.15],
    'Hawaii':         [1.6667, 1.1667,  0.8333,  0.5],
    'UK':             [1.9833, 1.4,  1.0,  0.6],
    'United Kingdom': [1.9833, 1.4,  1.0,  0.6],
    'Argentina':      [2.0833, 1.45,  1.0333,  0.6167],
    'Switzerland':    [2.2, 1.5333,  1.1,  0.65],
    'Japan':          [2.8167, 1.9667, 1.4,  0.85],
    'China':          [3.0167, 2.1167, 1.5167,  0.9],
    'UAE':            [3.3833, 2.3667, 1.7, 1.0167],
    'South Africa':   [3.7167, 2.6, 1.8667, 1.1167],
};

function fmtTravelTime(hrs) {
    // Format decimal hours as "26m" or "2h 47m"
    const totalMins = Math.round(hrs * 60);
    const h = Math.floor(totalMins / 60);
    const m = totalMins % 60;
    if (h === 0) return m + 'm';
    return h + 'h' + (m > 0 ? ' ' + m + 'm' : '');
}

function getTravelTimes() {
    return cfg.getHasBook() ? TRAVEL_TIMES_WITH_BOOK : TRAVEL_TIMES_NO_BOOK;
}
const TRAVEL_TIMES = TRAVEL_TIMES_NO_BOOK; // backwards-compat alias
 
/* ─────────────────────────────────────────
   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.teal} !important;border-radius:8px !important;` +
        `padding:10px 20px !important;color:${C.teal} !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.tealDim};margin-bottom:16px;line-height:1.5;`,
        secHdr: `font-size:9px;font-weight:700;letter-spacing:1.2px;text-transform:uppercase;color:${C.tealDim};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('⚙  EXPEDITION 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 ──
    // ── Rehab Thresholds + Banner toggle ──
    const rehabHdr = el('div'); rehabHdr.textContent = '💊 Rehab Thresholds'; rehabHdr.setAttribute('style', imp(S.secHdr));

    const rehabBannerRow = el('div'); rehabBannerRow.setAttribute('style', imp('display:flex;align-items:center;gap:8px;padding:5px 8px;border-radius:5px;background:rgba(0,200,224,0.04);border:1px solid rgba(0,200,224,0.15);cursor:pointer;margin-bottom:8px;'));
    const rehabBannerChk = document.createElement('input'); rehabBannerChk.type='checkbox'; rehabBannerChk.id='lt-rehab-banner';
    rehabBannerChk.checked = cfg.getRehabBanner();
    rehabBannerChk.setAttribute('style', imp('width:14px;height:14px;cursor:pointer;accent-color:#00c8e0;flex-shrink:0;'));
    const rehabBannerLbl = el('label'); rehabBannerLbl.textContent = '💊 Show rehab warning banner under tabs'; rehabBannerLbl.setAttribute('for','lt-rehab-banner');
    rehabBannerLbl.setAttribute('style', imp('font-size:10px;font-weight:600;color:'+C.text+';cursor:pointer;flex:1;'));
    rehabBannerRow.appendChild(rehabBannerChk); rehabBannerRow.appendChild(rehabBannerLbl);
    rehabBannerRow.addEventListener('click', function(e){ if(e.target!==rehabBannerChk) rehabBannerChk.checked=!rehabBannerChk.checked; });

    const _mkRehabField = (labelTxt, storageKey, hint) => {
        const g = el('div'); g.setAttribute('style', imp('margin-bottom:8px;'));
        const lbl = el('label'); lbl.textContent = labelTxt; lbl.setAttribute('style', imp(S.lbl));
        const inp = document.createElement('input');
        inp.type = 'number'; inp.min = '0'; inp.max = '999';
        inp.value = store.get(storageKey, storageKey === 'lt_rehab_warn' ? '5' : '10');
        inp.placeholder = storageKey === 'lt_rehab_warn' ? '5' : '10';
        inp.setAttribute('style', imp(S.inp + 'width:80px;'));
        inp.addEventListener('change', () => { store.setJSON(storageKey, parseInt(inp.value) || 0); });
        const h = el('div'); h.textContent = hint; h.setAttribute('style', imp(S.hint));
        g.appendChild(lbl); g.appendChild(inp); g.appendChild(h);
        return g;
    };
    const rehabWarnField  = _mkRehabField('⚠️ Warning Level (Company -EE)', 'lt_rehab_warn',  'Show orange warning above this company addiction penalty');
    const rehabEmergField = _mkRehabField('🚨 Emergency Level (Company -EE)', 'lt_rehab_emerg', 'Show red emergency alert above this company addiction penalty');

    // ── Sets-Only Filter ──
    const setsOnlyHdr = el('div'); setsOnlyHdr.textContent = '🔎 Item Filter'; setsOnlyHdr.setAttribute('style', imp(S.secHdr));
    const setsOnlyRow = el('div'); setsOnlyRow.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 curSetsOnly = cfg.getSetsOnly();
    [{ val: true, label: '🎒 Sets Items Only' }, { val: false, label: '🌐 All YATA Items' }].forEach(function(opt) {
        const isActive = curSetsOnly === opt.val;
        const btn = el('button'); btn.type = 'button'; btn.textContent = opt.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.teal : C.text) + ';transition:all 0.15s;'));
        btn.addEventListener('click', function() {
            cfg.setSetsOnly(opt.val);
            wrap.remove();
            openSettings();
        });
        setsOnlyRow.appendChild(btn);
    });
    setsOnlyRow.lastChild.style.setProperty('border-right', 'none', 'important');
    const setsOnlyHint = el('div'); setsOnlyHint.textContent = 'Sets Items Only shows only Plushies, Flowers, Prehistoric & Special. All YATA Items shows everything sold in overseas shops.';
    setsOnlyHint.setAttribute('style', imp('font-size:9px;color:' + C.textDim + ';margin-top:4px;margin-bottom:4px;font-family:Consolas,monospace;'));

    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.teal : 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.teal};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 ──
    // ── Travel Settings ──
    const travelSettHdr = el('div'); travelSettHdr.textContent = '✈️ Travel Settings'; travelSettHdr.setAttribute('style', imp(S.secHdr));

    const bookRow = el('div'); bookRow.setAttribute('style', imp('display:flex;align-items:center;gap:8px;padding:6px 8px;border-radius:5px;background:rgba(0,200,224,0.04);border:1px solid rgba(0,200,224,0.15);cursor:pointer;margin-bottom:8px;'));
    const bookChk = document.createElement('input'); bookChk.type='checkbox'; bookChk.id='lt-has-book';
    bookChk.checked = cfg.getHasBook();
    bookChk.setAttribute('style', imp('width:14px;height:14px;cursor:pointer;accent-color:#00c8e0;flex-shrink:0;'));
    const bookLbl = el('label'); bookLbl.textContent = '📖 I have the Travel Book (shorter flight times)'; bookLbl.setAttribute('for','lt-has-book');
    bookLbl.setAttribute('style', imp('font-size:10px;font-weight:600;color:'+C.text+';cursor:pointer;flex:1;'));
    bookRow.appendChild(bookChk); bookRow.appendChild(bookLbl);
    bookRow.addEventListener('click', function(e){ if(e.target!==bookChk) bookChk.checked=!bookChk.checked; });

    const speedLbl2 = el('div'); speedLbl2.textContent = 'Flight Speed Tier';
    speedLbl2.setAttribute('style', imp('font-size:9px;font-weight:700;letter-spacing:.8px;text-transform:uppercase;color:'+C.tealDim+';font-family:Consolas,monospace;margin-bottom:5px;'));
    const speedRow2 = el('div'); speedRow2.setAttribute('style', imp('display:flex;align-items:center;gap:0;background:'+C.bg+';border:1px solid '+C.border+';border-radius:8px;overflow:hidden;margin-bottom:8px;'));
    const curSpeed2 = cfg.getTravelSpeed();
    [{val:0,label:'Standard'},{val:1,label:'Airstrip'},{val:2,label:'WLT'},{val:3,label:'Bus. Class'}].forEach(function(opt){
        const isA=curSpeed2===opt.val;
        const btn=el('button'); btn.type='button'; btn.textContent=opt.label;
        btn.setAttribute('style',imp('flex:1;padding:7px 2px;border:none;border-right:1px solid '+C.border+';font-size:10px;font-weight:'+(isA?'700':'400')+';cursor:pointer;font-family:Arial,sans-serif;background:'+(isA?C.settNote:C.card)+';color:'+(isA?C.teal:C.text)+';'));
        btn.addEventListener('click',function(){ cfg.setTravelSpeed(opt.val); wrap.remove(); openSettings(); });
        speedRow2.appendChild(btn);
    });
    speedRow2.lastChild.style.setProperty('border-right','none','important');

    // ── Tourism Day ──
    const tourismSettHdr = el('div'); tourismSettHdr.textContent = '🌍 Tourism Day'; tourismSettHdr.setAttribute('style', imp(S.secHdr));
    const tourismPinRow = el('div'); tourismPinRow.setAttribute('style', imp('display:flex;align-items:center;gap:8px;padding:5px 8px;border-radius:5px;background:rgba(0,200,224,0.04);border:1px solid rgba(0,200,224,0.18);cursor:pointer;margin-bottom:8px;'));
    const tourismPinChk = document.createElement('input'); tourismPinChk.type='checkbox'; tourismPinChk.id='lt-tourism-pin';
    tourismPinChk.checked = cfg.getTourismPin();
    tourismPinChk.setAttribute('style', imp('width:14px;height:14px;cursor:pointer;accent-color:#00c8e0;flex-shrink:0;'));
    const tourismPinLbl = el('label'); tourismPinLbl.textContent = 'Always show Tourism Day bonus'; tourismPinLbl.setAttribute('for','lt-tourism-pin');
    tourismPinLbl.setAttribute('style', imp('font-size:10px;font-weight:600;color:'+C.text+';cursor:pointer;flex:1;'));
    tourismPinRow.appendChild(tourismPinChk); tourismPinRow.appendChild(tourismPinLbl);
    tourismPinRow.addEventListener('click', function(e){ if(e.target!==tourismPinChk) tourismPinChk.checked=!tourismPinChk.checked; });

    // ── Display Case ──
    const dcHdr = el('div'); dcHdr.textContent = '📦 Display Case'; dcHdr.setAttribute('style', imp(S.secHdr));
    const dcLbl = el('div'); dcLbl.textContent = 'Auto-fill Add Items quantities';
    dcLbl.setAttribute('style', imp('font-size:9px;font-weight:700;letter-spacing:.8px;text-transform:uppercase;color:'+C.tealDim+';font-family:Consolas,monospace;margin-bottom:5px;'));
    const dcRow = el('div'); dcRow.setAttribute('style', imp('display:flex;align-items:center;gap:0;background:'+C.bg+';border:1px solid '+C.border+';border-radius:8px;overflow:hidden;margin-bottom:8px;'));
    const curDC = cfg.getDisplayCaseMode();
    [{val:0,label:'Off'},{val:1,label:'Sets Only'},{val:2,label:'All Items'}].forEach(function(opt){
        const isA = curDC === opt.val;
        const btn = el('button'); btn.type='button'; btn.textContent=opt.label;
        btn.setAttribute('style',imp('flex:1;padding:7px 2px;border:none;border-right:1px solid '+C.border+';font-size:10px;font-weight:'+(isA?'700':'400')+';cursor:pointer;font-family:Arial,sans-serif;background:'+(isA?C.settNote:C.card)+';color:'+(isA?C.teal:C.text)+';'));
        btn.addEventListener('click',function(){ cfg.setDisplayCaseMode(opt.val); wrap.remove(); openSettings(); });
        dcRow.appendChild(btn);
    });
    dcRow.lastChild.style.setProperty('border-right','none','important');

    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.teal};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.teal};`));
    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(rehabHdr);
    body.appendChild(rehabWarnField);
    body.appendChild(rehabEmergField);
    body.appendChild(rehabBannerRow);
    body.appendChild(setsOnlyHdr);
    body.appendChild(setsOnlyRow);
    body.appendChild(setsOnlyHint);
    body.appendChild(secHdr);
    body.appendChild(visGrid);
    body.appendChild(travelSettHdr);
    body.appendChild(bookRow);
    body.appendChild(speedLbl2);
    body.appendChild(speedRow2);
    body.appendChild(dcHdr);
    body.appendChild(dcLbl);
    body.appendChild(dcRow);
    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);
    body.appendChild(tourismSettHdr);
    body.appendChild(tourismPinRow);

    // ── 💡 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);
            const rehabBanner2 = document.getElementById('lt-rehab-banner') ? document.getElementById('lt-rehab-banner').checked : cfg.getRehabBanner();
            cfg.setRehabBanner(rehabBanner2);
            const hasBook2   = document.getElementById('lt-has-book')    ? document.getElementById('lt-has-book').checked    : cfg.getHasBook();
            cfg.setHasBook(hasBook2);
            const tourismPin = document.getElementById('lt-tourism-pin') ? document.getElementById('lt-tourism-pin').checked : cfg.getTourismPin();
            cfg.setTourismPin(tourismPin);

        try {
            if (showTooltip) localStorage.removeItem('lt_tooltip_seen');
            else             localStorage.setItem('lt_tooltip_seen', '1');
        } catch(e) {}
        wrap.remove();
        toast('✓ Saved!');
        // Don't wipe caches before renderPanel — shows loading forever
        // refreshAll will refresh data and re-render when done
        if (panelEl) renderPanel();
        refreshAll();
        refreshRehabBanner();
    }
    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;
    return new Promise(function(resolve) {
        // Wrap in try/catch — if GM_xmlhttpRequest throws synchronously
        // (e.g. TornPDA bridge error), resolve with empty object rather than
        // leaving the Promise in a rejected/pending state
        try {
            var done = false;
            var timer = setTimeout(function() { if (!done) { done = true; resolve({}); } }, timeoutMs);
            GM_xmlhttpRequest({
                method: 'GET', url: url, timeout: timeoutMs,
                onload: function(r) {
                    if (done) return; done = true; clearTimeout(timer);
                    try { resolve(JSON.parse(r.responseText)); } catch(e) { resolve({}); }
                },
                onerror:   function() { if (!done) { done = true; clearTimeout(timer); resolve({}); } },
                ontimeout: function() { if (!done) { done = true; clearTimeout(timer); resolve({}); } },
            });
        } catch(e) {
            resolve({});
        }
    });
}
 
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;
}
 
// ── Auto-correct item IDs from YATA stock data ──
// YATA returns real Torn item IDs for every item in stock.
// Called after fetchYataData() so yataCityCache is populated.
// Builds a name→id map from YATA and patches GROUPS + SPECIAL_ITEMS.
function patchItemIdsFromYata() {
    const CACHE_KEY = 'lt_yata_ids_v1';
    const nameToId = {};

    // Build from live yataCityCache
    Object.values(yataCityCache).forEach(function(city) {
        (city.stocks || []).forEach(function(stock) {
            if (stock.name && stock.id) nameToId[stock.name] = stock.id;
        });
    });

    if (Object.keys(nameToId).length === 0) {
        // Fall back to cached IDs if YATA hasn't loaded yet
        const cached = store.getJSON(CACHE_KEY, null);
        if (cached) _applyItemIds(cached);
        return;
    }

    // Cache for next load
    store.setJSON(CACHE_KEY, nameToId);
    _applyItemIds(nameToId);
}

function _applyItemIds(nameToId) {
    let changed = false;
    // Patch all GROUPS items
    Object.values(GROUPS).forEach(function(g) {
        Object.entries(g.items).forEach(function([name, data]) {
            if (nameToId[name] && data.id !== nameToId[name]) {
                data.id = nameToId[name];
                changed = true;
            }
        });
    });
    // Patch special items
    Object.entries(SPECIAL_ITEMS).forEach(function([name, data]) {
        if (nameToId[name] && data.id !== nameToId[name]) {
            data.id = nameToId[name];
            changed = true;
        }
    });
    // Rebuild ID_TO_NAME lookup if anything changed
    if (changed) {
        const newMap = buildIdMap();
        Object.keys(ID_TO_NAME).forEach(function(k) { delete ID_TO_NAME[k]; });
        Object.assign(ID_TO_NAME, newMap);
        if (panelEl && panelOpen) renderPanel();
    }
}

// ── Fetch addiction / workstats for rehab card ──
// Returns { companyAddiction: number|null, brainPct: number|null }
// companyAddiction = negative effectiveness from addiction (as positive number, e.g. 12 = -12 EE)
// brainPct = battle stat debuff % (e.g. 3 = -3%)
let _addictionCache    = null;
let _addictionFetching = false;

async function fetchAddiction() {
    if (_addictionFetching) return _addictionCache;
    _addictionFetching = true;
    try {
        // personalstats has drug_addiction (total AP used), icons has the brain/drug icon
        const d = await gmFetch(`https://api.torn.com/user/?selections=personalstats,icons&key=${cfg.apiKey}`);
        // Company addiction: look for drug-related personalstats fields
        // Known fields: drug_addiction (AP used), or check icons for brain icon (icon32 = Drug Addiction)
        let companyAddiction = null;
        let brainPct = null;
        if (d && d.personalstats) {
            // drug_addiction_current is the AP level, not directly available
            // Use rehabs as a proxy indicator if available
        }
        if (d && d.icons) {
            // Scan icon values for addiction info
            // icon57 (and higher tiers) = "Minor/Moderate/Severe Drug Addiction - ... reduced (-X%)"
            // Company EE penalty appears in the same icon at higher tiers: "... -X company effectiveness"
            Object.values(d.icons).forEach(function(val) {
                if (typeof val !== 'string') return;
                // Battle stat debuff: "(-X%)" anywhere in the text
                const pctMatch = val.match(/\(-(\d+)%\)/);
                if (pctMatch) brainPct = parseInt(pctMatch[1]);
                // Company EE penalty: "-X company effectiveness" or "(-X EE)"
                const eeMatch = val.match(/-(\d+)\s*company effectiveness/i) ||
                                val.match(/\(-(\d+)\s*EE\)/i);
                if (eeMatch) companyAddiction = parseInt(eeMatch[1]);
            });
        }
        _addictionCache = { companyAddiction, brainPct };
    } catch(e) {
        _addictionCache = { companyAddiction: null, brainPct: null };
    }
    _addictionFetching = false;
    return _addictionCache;
}

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';
        }

        return { traveling: true, abroad: false, destination, timeLeft, departed, isReturn, origin, state };
    } catch(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.
───────────────────────────────────────── */

/* ─────────────────────────────────────────
   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 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) {
            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) {}
        });
    } catch(e) {  }
}

function updateRehabBanner(addiction) {
    if (!panelEl) return;
    const bannerEl = panelEl._rehabBannerEl || panelEl.querySelector('#lt-rehab-banner-bar');
    if (!bannerEl) return;
    if (!cfg.getRehabBanner()) { bannerEl.style.display = 'none'; return; }

    const warn  = cfg.getRehabWarn();
    const emerg = cfg.getRehabEmerg();

    const val   = addiction ? addiction.companyAddiction : null;
    const brain = addiction ? addiction.brainPct : null;

    const eeHit    = val   != null ? val   : 0;
    const brainHit = brain != null ? Math.abs(brain) : 0;  // brainPct is negative %
    const worst    = Math.max(eeHit, brainHit);

    if (worst === 0 && val == null) { bannerEl.style.display = 'none'; return; }

    let col, bg, icon, msg;
    if (worst >= emerg) {
        col = '#ff4444'; bg = 'rgba(255,68,68,0.12)';
        icon = '🚨'; msg = 'Emergency — Rehab Now!';
    } else if (worst >= warn) {
        col = '#ffaa00'; bg = 'rgba(255,170,0,0.10)';
        icon = '⚠️'; msg = 'Warning — Plan Rehab';
    } else {
        col = '#3aaa6e'; bg = 'rgba(58,170,110,0.08)';
        icon = '✅'; msg = 'No Addiction';
    }

    const parts = [];
    if (val   != null && val   > 0) parts.push('🏬 -' + val   + ' EE');
    if (brain != null && brain !== 0) parts.push('🧠 ' + brain + '% stats');
    const detail = parts.length ? '  ·  ' + parts.join('  ·  ') : '';

    bannerEl.style.cssText = `display:flex;align-items:center;gap:6px;padding:3px 10px;background:${bg};border-bottom:1px solid ${col}44;flex-shrink:0;font-family:Consolas,monospace;font-size:8.5px;`;
    bannerEl.innerHTML = '';
    const iconEl = document.createElement('span'); iconEl.textContent = icon; iconEl.style.cssText = 'font-size:11px;flex-shrink:0;';
    const textEl = document.createElement('span'); textEl.style.cssText = `color:${col};font-weight:700;letter-spacing:.4px;flex:1;`;
    textEl.textContent = '💊 REHAB  ' + msg + detail;
    bannerEl.appendChild(iconEl); bannerEl.appendChild(textEl);

    if (worst >= emerg) bannerEl.style.animation = 'lt-pulse 1.5s ease-in-out infinite';
    else bannerEl.style.animation = '';
}

function fillDisplayCaseQty() {
    // Fills Amount inputs on displaycase.php#add
    // Confirmed DOM: LI.clearfix.no-mods rows, input[name="amount"][placeholder="Amount"]
    // Row text format: "83x83 Cannabis x83Unavailable..." — qty is first number
    if (!window.location.href.includes('displaycase')) return;
    const dcMode = cfg.getDisplayCaseMode();
    if (dcMode === 0) return; // Off

    // Set items list for "Sets Only" mode
    const SET_ITEM_NAMES = new Set(
        Object.keys(GROUPS).reduce(function(acc, gKey) {
            return acc.concat(Object.keys(GROUPS[gKey].items));
        }, []).concat(Object.keys(SPECIAL_ITEMS))
    );

    try {
        const doc2 = (typeof unsafeWindow !== 'undefined' && unsafeWindow.document) ? unsafeWindow.document : document;
        const amountDivs = doc2.querySelectorAll('div.amount');
        if (!amountDivs.length) return;

        function setVal(inp, val) {
            try {
                const pd = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value');
                if (pd && pd.set) pd.set.call(inp, val);
                else inp.value = val;
            } catch(e) { inp.value = val; }
            inp.dispatchEvent(new Event('input',  { bubbles: true }));
            inp.dispatchEvent(new Event('change', { bubbles: true }));
        }

        amountDivs.forEach(function(div) {
            const inp = div.querySelector('input[name="amount"]');
            if (!inp) return;
            if (inp.value !== '' && inp.value !== '0') return;

            const row = inp.closest('li') || div.parentElement;
            if (!row) return;
            const text = row.textContent || '';

            // Format: "83x83 Cannabis x83Unavailable..." — qty is first number
            const match = text.trim().match(/(\d+)x\d+/);
            if (!match) return;
            const qty = match[1];
            if (!qty || qty === '0') return;

            // Sets Only mode — extract item name and check against set items
            if (dcMode === 1) {
                // Name sits between "NxN " and " xN" — e.g. "83x83 Cannabis x83"
                const nameMatch = text.trim().match(/\d+x\d+\s+(.+?)\s+x\d+/);
                const itemName = nameMatch ? nameMatch[1].trim() : '';
                if (!itemName || !SET_ITEM_NAMES.has(itemName)) return;
            }

            setVal(inp, qty);
        });
    } catch(e) {}
}
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);
            if (panelEl) renderPanel(); // refresh to show real images
        }
    } catch(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) {  }
}


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;
                    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;
                    if (panelEl) renderPanel();
                    return;
                }
            }
        }
    } catch(e) {  }
}
 
function watchItemsPage() {
    // Watch for DOM changes on items page to trigger scrape
    if (!window.location.href.includes('item')) return;
    // 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 knownName = ID_TO_NAME[Number(item.id)];
                // Use YATA's own item name for all items; fall back to knownName
                const name = item.name || knownName;
                const qty  = Number(item.quantity || 0);
                const cost = Number(item.cost || 0);
                if (!name) return;
                // Always store in yataCityCache (all items regardless of group)
                stocks.push({ id: Number(item.id), name, qty, cost });
                // Only update sets-tracking caches for known group items
                if (knownName) {
                    map[knownName] = (map[knownName] || 0) + qty;
                    if (cost > 0 && (!yataPriceCache[knownName] || cost > yataPriceCache[knownName].price)) {
                        yataPriceCache[knownName] = { price: cost, country: country.country_name || code };
                    }
                    if (qty >= 0) cfg.addStockHistory(knownName + '_' + code, qty);
                }
                if (isSA && Number(item.id) === XANAX_ID) {
                    sa = { qty, price: Number(item.cost || 0) };
                }
            });
            yataCityCache[code] = { city, code, stocks };
        });
        yataLastFetch = Date.now();
        // Patch item IDs using real IDs from YATA
        setTimeout(patchItemIdsFromYata, 0);
    } catch(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) { 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); });
    } catch(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) { 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) {
            return Number(xan.quantity || 0);
        }
        return 0;
    } catch(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;
        });
        // Record history for sparklines
        Object.entries(result).forEach(([name, price]) => { if (price > 0) cfg.addPriceHistory(name, price); });
    } catch(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.tealDim};background:${C.tealGlow};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.teal};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);
}

/* ─────────────────────────────────────────
   SPARKLINE BUILDER
   Plots stock history with only two marked events:
   🔴 Out of Stock (qty → 0)   🟢 Restocked (qty 0 → positive)
───────────────────────────────────────── */
function buildSparkline(history) {
    const vals = history.map(h => h.qty !== undefined ? h.qty : (h.price || 0));
    if (vals.length < 2) return null;

    const minP = Math.min(...vals), maxP = Math.max(...vals), range = maxP - minP || 1;
    const W = 260, H = 40, 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');

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

    // Mark only OOS (→0) and Restock (0→positive) events
    for (let i = 1; i < vals.length; i++) {
        const prev = vals[i - 1], cur = vals[i];
        let eventColor = null;
        if (cur === 0 && prev > 0)  eventColor = '#ff4444'; // went OOS
        if (cur > 0  && prev === 0) eventColor = '#44dd88'; // restocked
        if (eventColor) {
            const dot = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
            dot.setAttribute('cx', pts[i][0].toFixed(1));
            dot.setAttribute('cy', pts[i][1].toFixed(1));
            dot.setAttribute('r', '3.5');
            dot.setAttribute('fill', eventColor);
            svg.appendChild(dot);
        }
    }

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

    return svg;
}

// 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.teal : 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 — stock history with OOS/restock markers
        if (history.length >= 2) {
            const _spark = buildSparkline(history);
            if (_spark) {
                const sparkLbl = document.createElement('div');
                sparkLbl.style.cssText = `font-size:8px;font-weight:700;color:${C.tealDim};font-family:Consolas,monospace;margin-bottom:3px;`;
                sparkLbl.innerHTML = (isBob ? '🏪 Stock History' : '📦 Stock History') + ' <span style="color:' + C.textDim + '">(' + fmtHistorySpan(history) + ')</span>&nbsp;&nbsp;<span style="color:#ff4444">● OOS</span>&nbsp;<span style="color:#44dd88">● Restock</span>';
                inner.appendChild(sparkLbl);
                inner.appendChild(_spark);
            }
        } 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.tealGlow};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.tealDim};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.teal;
        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     = '#cc8800';
        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:${C.tealGlow};border-bottom:1px solid ${C.border};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);
    })();
 
    // ── Tourism Day Banner ──
    (function() {
        const now    = new Date();
        // Tourism Day: 16:00 UTC Sept 26 → 16:00 UTC Sept 29 each year
        const year   = now.getUTCFullYear();
        let tStart   = new Date(Date.UTC(year,   8, 26, 16, 0, 0)); // Sept 26 16:00 UTC
        let tEnd     = new Date(Date.UTC(year,   8, 29, 16, 0, 0)); // Sept 29 16:00 UTC
        if (now > tEnd) {
            tStart = new Date(Date.UTC(year+1, 8, 26, 16, 0, 0));
            tEnd   = new Date(Date.UTC(year+1, 8, 29, 16, 0, 0));
        }
        const msUntil   = tStart - now;
        const daysUntil = msUntil / 86400000;
        const isActive  = now >= tStart && now <= tEnd;
        const inWindow  = isActive || (msUntil > 0 && daysUntil <= 7);
        const pinned    = cfg.getTourismPin();
        if (!(inWindow || pinned)) return;

        const carry     = cfg.getXanCarry() || 1;
        const doubled   = carry * 2;

        // Show event window in user's local time
        const fmtLocal  = function(d) {
            return d.toLocaleString([], {month:'short',day:'numeric',hour:'2-digit',minute:'2-digit'});
        };
        const windowStr = fmtLocal(tStart) + ' – ' + fmtLocal(tEnd);

        let timeTag = '';
        if (isActive)            timeTag = ' · 🟢 ACTIVE';
        else if (daysUntil <= 1) timeTag = ' · tomorrow';
        else if (daysUntil <= 7) timeTag = ` · in ${Math.ceil(daysUntil)}d`;

        const T = '#00c8e0';
        const tourRow = document.createElement('div');
        tourRow.style.cssText = `display:flex;align-items:center;gap:6px;padding:4px 8px;background:${C.tealGlow};border-bottom:1px solid ${C.border};font-family:Consolas,monospace;font-size:8px;cursor:default;`;
        tourRow.title = 'Tourism Day doubles carry capacity for all flights departing during the event.\n' + windowStr;

        const icon = document.createElement('span'); icon.textContent = '🌍'; icon.style.cssText = 'font-size:10px;flex-shrink:0;';
        const _NAVY = '#4a6fa5';
        const label = document.createElement('span');
        label.style.cssText = `color:${_NAVY};font-weight:700;letter-spacing:.5px;text-transform:uppercase;flex:1;`;
        label.textContent = `Tourism Day: Carry ×2 — ${carry} → ${doubled} per trip${timeTag}`;

        tourRow.appendChild(icon); tourRow.appendChild(label);
        body.appendChild(tourRow);
    })();

    // ── 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.tealDim));
    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.tealDim};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.teal};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.teal : C.border};background:${isActive ? C.tealGlow : 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.teal:C.text};">${country}</span>${isActive?`<span style="margin-left:auto;font-size:9px;color:${C.teal};">✓ 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.tealDim};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.teal);
        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.teal};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.tealDim};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.teal};background:${C.tealGlow};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.teal};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.tealDim};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.teal : 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.tealDim};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.teal },
                { 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.teal));
                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.tealDim};`;
            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.teal};`;
            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.tealDim};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.tealDim));
            mid.appendChild(mkTag(total + ' 🧪 delivered', C.okay));
            if (r.manualPrice) mid.appendChild(mkTag('$' + r.manualPrice.toLocaleString() + '/xan', C.teal));
            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.tealDim};`;
            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.tealDim};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 — Expedition 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.xanPersonalCol};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.xanFactionCol};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.xanPersonalCol};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.teal, sub: ptsEquiv !== '—' ? ptsEquiv : null },
        { label: 'Carry Lmt', value: xanCarry || '—',                                                       color: C.tealDim, 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.tealDim};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.tealDim;
    vaultLink.addEventListener('mouseover', () => { vaultLink.querySelector('svg').style.opacity = '1'; vaultLink.style.color = C.teal; });
    vaultLink.addEventListener('mouseout',  () => { vaultLink.querySelector('svg').style.opacity = '0.55'; vaultLink.style.color = C.tealDim; });

    // 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.teal};font-family:Consolas,monospace;`;
    tripValue.textContent = tripCostFormatted;

    const copyIcon = document.createElement('span');
    copyIcon.style.cssText = `color:${C.tealDim};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.teal; }, 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.teal; }, 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', 'WLT', 'Bus. Class'];
    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     = getTravelTimes()[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.tealGlow};border:1px solid ${C.teal};font-size:8px;font-family:Consolas,monospace;color:${C.teal};margin-bottom:8px;`;
        pill.textContent = '✈ ' + SPEED_NAMES[curSpeed] + '  ·  carry ' + curCarry;
        resultsDiv.appendChild(pill);

        const medals = ['🥇','🥈','🥉'];
        const medalColors = ['#ffd700', isLightMode() ? '#4a5568' : '#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.teal : 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.teal : 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.teal : 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.tealDim};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.tealDim};background:${C.tealGlow};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.tealDim};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 ? getTravelTimes()[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.tealGlow};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.tealGlow);
                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} · ${fmtTravelTime(tHr)} r/t</span>
                    </div>`;
                const xRight = document.createElement('div'); xRight.style.cssText = 'text-align:right;flex-shrink:0;';
                const prColor = hasRealProfit ? C.okay : C.tealDim;
                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:fmtTravelTime(tHr)+' r/t',             color:C.textDim},
                            {label:'$/hr',       value:profPerHr>0?fmt2(profPerHr):'—',color:C.okay},
                            {label:'Carry',      value:carry2+' 🧪',           color:C.tealDim},
                        ].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 _spark=buildSparkline(stockH);
                            if (_spark) {
                                const sLbl=document.createElement('div'); sLbl.style.cssText=`font-size:8px;font-weight:700;color:${C.tealDim};font-family:Consolas,monospace;margin-bottom:3px;`;
                                sLbl.innerHTML='📦 Xanax Stock History <span style="color:'+C.textDim+'">(' + fmtHistorySpan(stockH) + ')</span>&nbsp;&nbsp;<span style="color:#ff4444">● OOS</span>&nbsp;<span style="color:#44dd88">● Restock</span>';
                                xInner.appendChild(sLbl); xInner.appendChild(_spark);
                            }
                        }
                        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.tealDim};background:${C.tealGlow};border-top:1px solid ${C.border};border-bottom:1px solid ${C.border};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.tealDim};opacity:0.7;transition:opacity 0.15s;padding:2px;`;
        gearBtn.addEventListener('mouseover', () => { gearBtn.style.opacity = '1'; gearBtn.style.color = C.teal; });
        gearBtn.addEventListener('mouseout',  () => { gearBtn.style.opacity = '0.7'; gearBtn.style.color = C.tealDim; });
        gearBtn.addEventListener('click', e => { e.stopPropagation(); openProfitPopup(); });

        // ▾ chevron
        const chevron = document.createElement('span');
        chevron.style.cssText = `color:${C.teal};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     = getTravelTimes()[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 ? getTravelTimes()[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.tealGlow};border:1px solid ${C.border};font-size:8px;font-family:Consolas,monospace;color:${C.tealDim};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.tealGlow};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.tealGlow);
                    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} · ${fmtTravelTime(tHr)} 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:fmtTravelTime(tHr)+' r/t',               color:C.textDim },
                                { label:'$/hr',        value:profPerHr>0?fmt2(profPerHr):'—', color:'#66cc66' },
                                { label:'Carry',       value:carry+' 🧪',              color:C.tealDim },
                            ].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 _spark2=buildSparkline(stockH2);
                                if (_spark2) {
                                    const sLbl2=document.createElement('div'); sLbl2.style.cssText=`font-size:8px;font-weight:700;color:${C.tealDim};font-family:Consolas,monospace;margin-bottom:3px;`;
                                    sLbl2.innerHTML='📦 Xanax Stock History <span style="color:'+C.textDim+'">(' + fmtHistorySpan(stockH2) + ')</span>&nbsp;&nbsp;<span style="color:#ff4444">● OOS</span>&nbsp;<span style="color:#44dd88">● Restock</span>';
                                    xInner.appendChild(sLbl2); xInner.appendChild(_spark2);
                                }
                            }
                            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.tealDim};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}77;background:${col}28;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+'38');
                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 = fmtTravelTime(item.travelHr); 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:${col}18;border-top:1px solid ${col}44;`;
                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:fmtTravelTime(item.travelHr)+' r/t', color:C.textDim },
                            { label:'Pts Equiv',    value:ptsEq||'—',            color:C.tealDim },
                        ].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 _sparkPP = buildSparkline(stockH);
                            if (_sparkPP) {
                                const sLbl = document.createElement('div'); sLbl.style.cssText = `font-size:8px;font-weight:700;color:${C.tealDim};font-family:Consolas,monospace;margin-bottom:3px;`;
                                sLbl.innerHTML = '📦 Stock History <span style="color:'+C.textDim+'">(' + fmtHistorySpan(stockH) + ')</span>&nbsp;&nbsp;<span style="color:#ff4444">● OOS</span>&nbsp;<span style="color:#44dd88">● Restock</span>';
                                pInner.appendChild(sLbl); pInner.appendChild(_sparkPP);
                            }
                        } 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',
            loot: ['Dahlia', 'Jaguar Plushie', 'Obsidian Point'],
        },
        {
            flag: '🇦🇷', name: 'Argentina', col: '#74c9ff',
            loot: ['Ceibo Flower', 'Monkey Plushie', 'Chalcedony Point', 'Meteorite Fragment', 'Patagonian Fossil'],
        },
        {
            flag: '🇬🇧', name: 'United Kingdom', col: '#cc88ff',
            loot: ['Heather', 'Nessie Plushie', 'Red Fox Plushie', 'Chert Point', 'Xanax'],
        },
        {
            flag: '🇨🇦', name: 'Canada', col: '#ff7070',
            loot: ['Crocus', 'Wolverine Plushie', 'Quartz Point', 'Xanax'],
        },
        {
            flag: '🇿🇦', name: 'South Africa', col: '#60cc60',
            loot: ['African Violet', 'Lion Plushie', 'Quartzite Point', 'Xanax'],
        },
        {
            flag: '🇨🇭', name: 'Switzerland', col: '#ff9999',
            loot: ['Edelweiss', 'Chamois Plushie'],
        },
        {
            flag: '🇯🇵', name: 'Japan', col: '#ffaacc',
            loot: ['Cherry Blossom', 'Xanax'],
        },
        {
            flag: '🇨🇳', name: 'China', col: '#ff6040',
            loot: ['Peony', 'Panda Plushie'],
        },
        {
            flag: '🇺🇸', name: 'Hawaii', col: '#ffe066',
            loot: ['Orchid', 'Basalt Point'],
        },
        {
            flag: '🇦🇪', name: 'UAE', col: '#88ddaa',
            loot: ['Tribulus Omanense', 'Camel Plushie'],
        },
        {
            flag: '🇰🇾', name: 'Cayman Islands', col: '#66ccff',
            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.teal : C.teal;
        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 ? 'aa' : '66'}`;
        const bgCol     = isSAxanPinned ? C.tealGlow : `${run.col}${urgency > 0.5 ? '30' : '1a'}`;
        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.teal : run.col};font-family:Arial,sans-serif;`;
        const timeEl  = document.createElement('span');
        (function(){
            const tt = getTravelTimes()[run.name] || getTravelTimes()['United Kingdom'];
            const sp = cfg.getTravelSpeed();
            const hrs = tt[sp] || tt[0];
            const totalMins = Math.round(hrs * 60 * 2);
            const h = Math.floor(totalMins/60), m = totalMins%60;
            timeEl.textContent = '~' + (h > 0 ? h+'h'+(m>0?' '+m+'m':'') : m+'m') + ' r/t';
        })();
        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.tealDim};background:${C.tealGlow};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.tealDim};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.tealDim};background:${C.tealGlow};border-top:1px solid ${C.border};border-bottom:1px solid ${C.border};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.teal};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 _travelSetsOnly = cfg.getSetsOnly();
    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);
    });
    // When showing all items, supplement with everything YATA reports per country
    if (!_travelSetsOnly) {
        Object.entries(TORN_DEST_TO_CODE).forEach(([countryName, code]) => {
            const cityCache = yataCityCache[code];
            if (!cityCache || !cityCache.stocks) return;
            cityCache.stocks.forEach(stock => {
                if (!stock.name) return;
                if (!countryItemMap[countryName]) countryItemMap[countryName] = [];
                if (!countryItemMap[countryName].includes(stock.name)) {
                    countryItemMap[countryName].push(stock.name);
                }
            });
        });
    }

    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';
            // Unknown items (not in any group): show only when setsOnly=false
            if (!groupKey) return !_travelSetsOnly;
            return !(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];
                    // For non-group items, get ID from YATA cache
                    const _yataCode = TORN_DEST_TO_CODE[name] || TORN_DEST_TO_CODE[lookupKey];
                    const _yataCity = _yataCode ? yataCityCache[_yataCode] : null;
                    const _yataStock = _yataCity ? _yataCity.stocks.find(s => s.name === iName) : null;
                    const iId    = (iData && iData.id) || (_yataStock && _yataStock.id) || null;
                    const abroad = abroadCache[iName] !== undefined ? abroadCache[iName] : (_yataStock ? _yataStock.qty : null);
                    const buyP   = yataPriceCache[iName] ? yataPriceCache[iName].price : (_yataStock ? _yataStock.cost : 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.tealDim : 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 renderRehabCard(body, addiction) {
    const warn  = cfg.getRehabWarn();
    const emerg = cfg.getRehabEmerg();
    const val   = addiction ? addiction.companyAddiction : null;
    const brain = addiction ? addiction.brainPct : null;

    const eeHit    = val   != null ? val   : 0;
    const brainHit = brain != null ? brain : 0;
    const worst    = Math.max(eeHit, brainHit);
    // addiction object present but both null = API returned, no drug icon found = clean
    const _loaded  = addiction !== null && addiction !== undefined;
    let statusCol  = '#3aaa6e', statusText = '✅ No Addiction', pulse = false;
    if (worst >= emerg)          { statusCol = '#ff4444'; statusText = '🚨 Emergency — Rehab Now!'; pulse = true; }
    else if (worst >= warn)      { statusCol = '#ffaa00'; statusText = '⚠️ Warning'; }
    else if (!_loaded)           { statusText = '◌ Loading…'; statusCol = C.textDim; }
    // else: loaded, both null → clean, green "No Addiction" ✅

    const sec = document.createElement('div');
    sec.style.cssText = `margin:0 10px 8px;background:${C.bg2};border:2px solid ${statusCol}55;border-radius:8px;overflow:hidden;`;
    if (pulse) sec.style.animation = 'lt-pulse 1.5s ease-in-out infinite';

    // Collapsible header
    let _open = true;
    const chev = document.createElement('span');
    chev.style.cssText = `font-size:10px;color:${statusCol};transition:transform 0.18s;display:inline-block;`;
    chev.textContent = '▾';

    const hdr = document.createElement('div');
    hdr.style.cssText = `padding:7px 12px;background:${statusCol}22;border-bottom:1px solid ${statusCol}44;display:flex;align-items:center;justify-content:space-between;cursor:pointer;user-select:none;`;
    const hdrLeft = document.createElement('span');
    hdrLeft.style.cssText = `font-size:9px;font-weight:700;letter-spacing:1px;text-transform:uppercase;color:${statusCol};font-family:Consolas,monospace;`;
    hdrLeft.textContent = '💊 Rehab Status';
    const hdrRight = document.createElement('div');
    hdrRight.style.cssText = 'display:flex;align-items:center;gap:6px;';
    const statusSpan = document.createElement('span');
    statusSpan.style.cssText = `font-size:8px;font-weight:700;color:${statusCol};font-family:Consolas,monospace;`;
    statusSpan.textContent = statusText;
    hdrRight.appendChild(statusSpan); hdrRight.appendChild(chev);
    hdr.appendChild(hdrLeft); hdr.appendChild(hdrRight);

    const bdy = document.createElement('div');
    bdy.style.cssText = 'overflow:hidden;transition:max-height 0.22s ease;max-height:500px;';

    hdr.addEventListener('click', function() {
        _open = !_open;
        bdy.style.maxHeight = _open ? '500px' : '0';
        chev.style.transform = _open ? 'rotate(0deg)' : 'rotate(-90deg)';
    });

    const grid = document.createElement('div');
    grid.style.cssText = 'display:grid;grid-template-columns:1fr 1fr;gap:1px;background:' + statusCol + '18;';

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

    const eeCol = val   == null ? C.textDim : val   >= emerg ? '#ff4444' : val   >= warn ? '#ffaa00' : '#3aaa6e';
    const bCol  = brain == null ? C.textDim : brain >= 6     ? '#ff4444' : brain >= 3    ? '#ffaa00' : '#3aaa6e';

    grid.appendChild(mkStat('🏬💊 Company Addiction', val   != null ? '-' + val   + ' EE' : 'None', eeCol));
    grid.appendChild(mkStat('🧠💊 Battle Stats',      brain != null ? '-' + brain + '%'   : 'None', bCol));
    bdy.appendChild(grid);

    const threshEl = document.createElement('div');
    threshEl.style.cssText = `padding:5px 12px;font-size:8px;color:${C.textDim};font-family:Consolas,monospace;border-top:1px solid ${statusCol}22;`;
    threshEl.textContent = `Thresholds: ⚠️ warn at -${warn} EE  ·  🚨 emergency at -${emerg} EE`;
    bdy.appendChild(threshEl);

    sec.appendChild(hdr); sec.appendChild(bdy);
    body.appendChild(sec);
}

function _renderBobSection(body) {
    const BOB_ITEMS = [
        { name: 'Sheep Plushie',      id: 186 },
        { name: 'Teddy Bear Plushie', id: 187 },
        { name: 'Kitten Plushie',     id: 215 },
    ];
    const sec = document.createElement('div');
    sec.style.cssText = `margin:0 10px 8px;background:${C.bg2};border:1px solid ${C.border};border-radius:8px;overflow:hidden;`;

    const hdr = document.createElement('div');
    hdr.style.cssText = `padding:7px 12px;background:${C.tealGlow};border-bottom:1px solid ${C.border};display:flex;align-items:center;justify-content:space-between;cursor:pointer;user-select:none;`;
    let open = true;
    const chev = document.createElement('span'); chev.style.cssText = `font-size:10px;color:${C.teal};transition:transform 0.18s;display:inline-block;`; chev.textContent = '▾';
    hdr.innerHTML = `<span style="font-size:9px;font-weight:700;letter-spacing:1px;text-transform:uppercase;color:${C.tealDim};font-family:Consolas,monospace;">🏪 Bits n' Bobs</span>`;
    hdr.appendChild(chev);

    const bdy = document.createElement('div');
    bdy.style.cssText = 'overflow:hidden;transition:max-height 0.22s ease;max-height:500px;';
    hdr.addEventListener('click', function() {
        open = !open;
        bdy.style.maxHeight = open ? '500px' : '0';
        chev.style.transform = open ? 'rotate(0deg)' : 'rotate(-90deg)';
    });

    BOB_ITEMS.forEach(function(item, i) {
        const qty = bobCache[item.name] !== undefined ? bobCache[item.name] : null;
        const col = qty === null ? C.textDim : qty > 0 ? C.stockHi : C.stockLo;
        const isLast = i === BOB_ITEMS.length - 1;
        const row = document.createElement('div');
        row.style.cssText = `display:flex;align-items:center;gap:8px;padding:7px 12px;${isLast?'':'border-bottom:1px solid '+C.border+';'}`;

        const img = document.createElement('img'); img.src = itemImg(item.id); img.alt = item.name;
        img.style.cssText = `width:28px;height:28px;object-fit:contain;border-radius:2px;flex-shrink:0;border:1px solid ${C.cardBorder};`;
        img.addEventListener('error', function(){ img.style.display='none'; });

        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:9.5px;font-weight:700;color:${C.text};font-family:Arial,sans-serif;`;
        const subEl = document.createElement('div'); subEl.textContent = "Bits n' Bobs · Torn City";
        subEl.style.cssText = `font-size:8px;color:${C.textDim};font-family:Consolas,monospace;margin-top:1px;`;
        mid.appendChild(nameEl); mid.appendChild(subEl);

        const qtyEl = document.createElement('div');
        qtyEl.style.cssText = `font-size:12px;font-weight:700;color:${col};font-family:Consolas,monospace;`;
        qtyEl.textContent = qty !== null ? qty.toLocaleString() : '—';

        const link = document.createElement('a');
        link.href = 'https://www.torn.com/shops.php?step=bitsnbobs';
        link.style.cssText = `font-size:8px;color:${C.teal};font-family:Consolas,monospace;text-decoration:none;flex-shrink:0;padding:2px 6px;border:1px solid ${C.border};border-radius:3px;`;
        link.textContent = '🏪';
        link.title = "Open Bits n' Bobs";

        row.appendChild(img); row.appendChild(mid); row.appendChild(qtyEl); row.appendChild(link);
        bdy.appendChild(row);
    });

    sec.appendChild(hdr); sec.appendChild(bdy);
    body.appendChild(sec);
}

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 || '';
    // Trust the API's isReturn flag strictly. If undefined, show loading rather than guess.
    const isReturn  = travelStatus.isReturn === true ? true
                    : travelStatus.isReturn === false ? false
                    : null; // unresolved — don't render yet
    if (isReturn === null || (!isReturn && !dest)) {
        body.appendChild(makeEmpty('◌', 'Resolving flight…'));
        return;
    }
    // On return flights, dest = 'Torn' — use origin country for cityData
    const _originCountry = isReturn ? (travelStatus.origin || travelStatus.destination || '') : dest;
    // Origin may be a country name or a city name — try both
    const _originCode = TORN_DEST_TO_CODE[_originCountry] || CITY_TO_CODE[_originCountry] || null;
    const code      = isReturn ? _originCode : 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', 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       = 'The City';
    } else {
        // Outbound: from Torn City → to foreign city
        fromDisplay    = TORN_LOC;
        fromCityName   = 'The 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.teal};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.tealDim};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.teal },
        { 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);

    // ── Rehab card — outbound to Switzerland only (return handled inside isReturn block) ──
    if (!isReturn && (dest === 'Switzerland' || dest === 'Zurich')) {
        const _rehabPlaceholder = document.createElement('div');
        _rehabPlaceholder.id = 'lt-rehab-card';
        body.appendChild(_rehabPlaceholder);
        if (cfg.apiKey && !_addictionFetching) {
            fetchAddiction().then(function(addiction) {
                const _ph = panelEl ? panelEl.querySelector('#lt-rehab-card') : null;
                if (_ph) { const _rc = document.createElement('div'); renderRehabCard(_rc, addiction); _ph.replaceWith(_rc.firstChild || _rc); }
            }).catch(function() {});
        }
    }

    // ── Return flight — show trip planner ──
    if (isReturn) {
        // ── Trip Planner: what to buy next trip to maximise sets ──
        const vis = cfg.getSectionVis();
        const carry = cfg.getXanCarry() || 1;
        const planSec = document.createElement('div');
        planSec.style.cssText = `margin:8px 10px 4px;background:${C.bg2};border:1px solid ${C.border};border-radius:8px;overflow:hidden;`;

        const planHdr = document.createElement('div');
        planHdr.style.cssText = `padding:7px 12px;background:${C.tealGlow};border-bottom:1px solid ${C.border};display:flex;align-items:center;justify-content:space-between;cursor:pointer;user-select:none;`;
        let _planOpen = true;
        const _planChev = document.createElement('span');
        _planChev.style.cssText = `font-size:10px;color:${C.teal};transition:transform 0.18s;display:inline-block;`;
        _planChev.textContent = '▾';
        planHdr.innerHTML = `<span style="font-size:9px;font-weight:700;letter-spacing:1px;text-transform:uppercase;color:${C.tealDim};font-family:Consolas,monospace;">🗺 Trip Planner</span>`;
        const _planMeta = document.createElement('div'); _planMeta.style.cssText = 'display:flex;align-items:center;gap:6px;';
        const _planCarry = document.createElement('span'); _planCarry.style.cssText = `font-size:8px;color:${C.textDim};font-family:Consolas,monospace;`; _planCarry.textContent = 'carry: ' + carry;
        _planMeta.appendChild(_planCarry); _planMeta.appendChild(_planChev);
        planHdr.appendChild(_planMeta);
        planSec.appendChild(planHdr);

        const planBody = document.createElement('div');
        planBody.style.cssText = 'padding:8px 10px;display:flex;flex-direction:column;gap:6px;overflow:hidden;transition:max-height 0.22s ease;max-height:2000px;';
        planHdr.addEventListener('click', function() {
            _planOpen = !_planOpen;
            planBody.style.maxHeight = _planOpen ? '2000px' : '0';
            planBody.style.padding   = _planOpen ? '8px 10px' : '0 10px';
            _planChev.style.transform = _planOpen ? 'rotate(0deg)' : 'rotate(-90deg)';
        });

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

        // ── Build a global ranked list of the most-needed items across ALL groups ──
        // Score = how many more of this item you need to reach the next set milestone
        // i.e. (curSets + 1) - owned[item]  — higher = more urgent
        const _needed = [];

        Object.entries(GROUPS).forEach(function([gKey, g]) {
            if (vis[gKey.toLowerCase()] === false) return;
            const curSets = calcSet(invCache, g.items);
            // Target = highest owned count in this group (the leader sets the bar)
            const maxOwned = Math.max.apply(null, Object.keys(g.items).map(function(k){ return invCache[k] || 0; }));
            const target = Math.max(curSets + 1, maxOwned); // at minimum aim for next set
            Object.entries(g.items).forEach(function([iName, iData]) {
                const owned = invCache[iName] || 0;
                const need  = target - owned;
                if (need > 0) {
                    _needed.push({ name: iName, data: iData, owned: owned, need: need,
                                   group: gKey, curSets: curSets });
                }
            });
        });

        // Special items: need 1 if not owned
        Object.entries(SPECIAL_ITEMS).forEach(function([iName, iData]) {
            if (vis['special'] === false) return;
            const owned = invCache[iName] || 0;
            if (owned < 1) _needed.push({ name: iName, data: iData, owned: owned, need: 1,
                                          group: 'Special', curSets: 0 });
        });

        // Sort: most needed first (highest shortage), then by owned count ascending
        _needed.sort(function(a, b) {
            if (b.need !== a.need) return b.need - a.need;
            return a.owned - b.owned;
        });

        // Show top 3
        const _top3 = _needed.slice(0, 3);
        let anyPlan = _top3.length > 0;

        _top3.forEach(function(entry, idx) {
            const iName = entry.name, iData = entry.data;
            const isLast = idx === _top3.length - 1;

            // Get buy price from yataPriceCache (global — not country-filtered)
            const buyPrice  = yataPriceCache[iName] ? yataPriceCache[iName].price : 0;
            const sellPrice = marketValueCache[iName] || 0;
            const profit    = sellPrice > buyPrice && buyPrice > 0 ? sellPrice - buyPrice : 0;

            const wrap = document.createElement('div');
            wrap.style.cssText = isLast ? '' : `border-bottom:1px solid ${C.border};`;

            const row = document.createElement('div');
            row.style.cssText = `display:flex;align-items:center;gap:8px;padding:7px 4px;cursor:pointer;transition:background 0.15s;position:relative;padding-right:18px;`;
            row.addEventListener('mouseover', function(){ row.style.background='rgba(0,200,224,0.05)'; });
            row.addEventListener('mouseout',  function(){ row.style.background='transparent'; });

            // Rank badge
            const rankEl = document.createElement('div');
            const _rankStyles = [
                { bg:'#ffffff', fg:'#000000', border:'none'                   },
                { bg:'#aaaaaa', fg:'#000000', border:'none'                   },
                { bg:'#555555', fg:'#ffffff', border:'none'                   },
            ];
            const _rs = _rankStyles[idx];
            rankEl.style.cssText = `width:18px;height:18px;border-radius:50%;background:${_rs.bg};border:${_rs.border};display:flex;align-items:center;justify-content:center;font-size:9px;font-weight:700;color:${_rs.fg};flex-shrink:0;font-family:Consolas,monospace;`;
            rankEl.textContent = idx + 1;

            const img = document.createElement('img'); img.src = itemImg(iData.id); img.alt = iName;
            img.style.cssText = `width:26px;height:26px;object-fit:contain;border-radius:2px;flex-shrink:0;border:1px solid ${C.cardBorder};`;
            img.addEventListener('error', function(){ img.style.display='none'; });

            const mid = document.createElement('div'); mid.style.cssText = 'flex:1;min-width:0;';
            const nameDiv = document.createElement('div');
            nameDiv.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;`;
            nameDiv.textContent = iName;
            const subDiv = document.createElement('div');
            subDiv.style.cssText = `font-size:7.5px;color:${C.textDim};font-family:Consolas,monospace;margin-top:1px;display:flex;align-items:center;gap:4px;`;
            // Flag for country where item is sold
            const locFlagEl = makeFlagEl(iData.loc, 10);
            const locSpan = document.createElement('span'); locSpan.textContent = iData.loc + ' · own ' + entry.owned;
            subDiv.appendChild(locFlagEl); subDiv.appendChild(locSpan);
            mid.appendChild(nameDiv); mid.appendChild(subDiv);

            // "Need X" badge
            const needEl = document.createElement('div');
            needEl.style.cssText = `font-size:11px;font-weight:700;color:${entry.need===1?C.okay:C.teal};font-family:Consolas,monospace;white-space:nowrap;flex-shrink:0;`;
            needEl.textContent = 'Need ' + entry.need;

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

            row.appendChild(rankEl); row.appendChild(img); row.appendChild(mid);
            row.appendChild(needEl); row.appendChild(chev);

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

            row.addEventListener('click', function() {
                const open = detail.style.maxHeight !== '0px' && detail.style.maxHeight !== '';
                detail.style.maxHeight = open ? '0' : '200px';
                chev.style.transform = open ? 'translateY(-50%) rotate(0deg)' : 'translateY(-50%) rotate(180deg)';
                if (!open && !populated) {
                    populated = true;
                    const inner = document.createElement('div'); inner.style.cssText = 'padding:6px 8px;';
                    const grid = document.createElement('div'); grid.style.cssText = 'display:grid;grid-template-columns:repeat(3,1fr);gap:3px;';
                    [
                        { label:'Owned',      value:String(entry.owned),              color:entry.owned>0?C.okay:C.textDim },
                        { label:'Need',       value:String(entry.need),               color:entry.need===1?C.okay:C.teal  },
                        { label:'Group',      value:entry.group,                       color:C.textDim                     },
                        { label:'Buy Price',  value:buyPrice>0?fmtM(buyPrice):'—',    color:C.stockLo                     },
                        { label:'Sell Value', value:sellPrice>0?fmtM(sellPrice):'—',  color:C.okay                        },
                        { label:'Profit/ea',  value:profit>0?fmtM(profit):'—',        color:profit>0?C.okay:C.textDim     },
                    ].forEach(function(cell) {
                        const cel = document.createElement('div');
                        cel.style.cssText = `background:${C.card};border:1px solid ${C.cardBorder};border-radius:3px;padding:4px 3px;text-align:center;`;
                        cel.innerHTML = `<div style="font-size:7px;color:${C.textDim};font-family:Consolas,monospace;margin-bottom:1px;">${cell.label}</div><div style="font-size:9px;font-weight:700;color:${cell.color};font-family:Consolas,monospace;">${cell.value}</div>`;
                        grid.appendChild(cel);
                    });
                    inner.appendChild(grid);
                    detail.appendChild(inner);
                }
            });

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

        if (!anyPlan) {
            const nothingEl = document.createElement('div');
            nothingEl.style.cssText = `padding:10px 4px;text-align:center;font-size:9px;color:${C.okay};font-family:Consolas,monospace;`;
            nothingEl.textContent = '✅ All sets complete!';
            planBody.appendChild(nothingEl);
        }

        // ── Rehab card — above trip planner when returning from Switzerland ──
        if (_originCountry === 'Switzerland' || _originCountry === 'Zurich') {
            const _rehabReturn = document.createElement('div');
            body.appendChild(_rehabReturn);
            if (cfg.apiKey) {
                fetchAddiction().then(function(addiction) {
                    // Always render — even with no addiction, show clear status
                    renderRehabCard(_rehabReturn, addiction || { companyAddiction: null, brainPct: null });
                }).catch(function() {
                    renderRehabCard(_rehabReturn, { companyAddiction: null, brainPct: null });
                });
            } else {
                renderRehabCard(_rehabReturn, { companyAddiction: null, brainPct: null });
            }
        }

        planSec.appendChild(planBody);
        body.appendChild(planSec);

        // If showing all items, fall through to render the full item list below
        if (cfg.getSetsOnly()) return;
    }

    if (!cityData || !cityData.stocks.length) {
        if (isReturn) {
            // On return with no foreign YATA data — show BoB plushie stock instead
            _renderBobSection(body);
        } else {
            body.appendChild(makeEmpty('📡', 'No YATA stock data for this destination.<br>Try refreshing.'));
        }
        return;
    }

    // ── Collapsible item list section ──
    const _itemListSec = document.createElement('div');
    _itemListSec.style.cssText = `margin:0 10px 8px;background:${C.bg2};border:1px solid ${C.border};border-radius:8px;overflow:hidden;`;
    const _itemListHdr = document.createElement('div');
    _itemListHdr.style.cssText = `padding:7px 12px;background:${C.tealGlow};border-bottom:1px solid ${C.border};display:flex;align-items:center;justify-content:space-between;cursor:pointer;user-select:none;`;
    let _itemListOpen = true;
    const _ilChev = document.createElement('span'); _ilChev.style.cssText = `font-size:10px;color:${C.teal};transition:transform 0.18s;display:inline-block;`; _ilChev.textContent = '▾';
    _itemListHdr.innerHTML = `<span style="font-size:9px;font-weight:700;letter-spacing:1px;text-transform:uppercase;color:${C.tealDim};font-family:Consolas,monospace;">${isReturn ? '📦 Last Destination Stock' : '📦 Available Items'}</span>`;
    _itemListHdr.appendChild(_ilChev);
    const _itemListBody = document.createElement('div');
    _itemListBody.style.cssText = 'overflow:hidden;transition:max-height 0.22s ease;max-height:2000px;';
    _itemListHdr.addEventListener('click', function() {
        _itemListOpen = !_itemListOpen;
        _itemListBody.style.maxHeight = _itemListOpen ? '2000px' : '0';
        _ilChev.style.transform = _itemListOpen ? 'rotate(0deg)' : 'rotate(-90deg)';
    });
    _itemListSec.appendChild(_itemListHdr);
    _itemListSec.appendChild(_itemListBody);
    body.appendChild(_itemListSec);

    // ── 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);
    });
    _itemListBody.appendChild(colHdr);

    // ── Item rows — sorted by profit desc ──
    const _setsOnly = cfg.getSetsOnly();
    const _knownNames = new Set(Object.keys(ID_TO_NAME).map(id => ID_TO_NAME[id]));
    const rows = cityData.stocks
        .filter(stock => !_setsOnly || _knownNames.has(stock.name) || stock.name === 'Xanax')
        .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 _spark = buildSparkline(history);
                    if (_spark) {
                        const sparkLbl = document.createElement('div');
                        sparkLbl.style.cssText = `font-size:8px;font-weight:700;color:${C.tealDim};font-family:Consolas,monospace;margin-bottom:3px;`;
                        sparkLbl.innerHTML = '📦 Stock History <span style="color:' + C.textDim + '">(' + fmtHistorySpan(history) + ')</span>&nbsp;&nbsp;<span style="color:#ff4444">● OOS</span>&nbsp;<span style="color:#44dd88">● Restock</span>';
                        destInner.appendChild(sparkLbl); destInner.appendChild(_spark);
                    }
                }
                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);
        _itemListBody.appendChild(destWrap);
    });

    // BoB section on return when we have YATA data too
    if (isReturn) _renderBobSection(body);
}

/* ─────────────────────────────────────────
   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.tealDim};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.teal};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.tealDim};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.tealDim};`;
        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.tealDim};`;
        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 && !renderAbroadBody._fetching) {
        renderAbroadBody._fetching = true;
        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] || {};
            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,
            };
            renderAbroadBody._fetching = false;
            try { if (panelEl && activeTab === 'abroad') renderPanel(); } catch(e) {}
        }).catch(function(e) { renderAbroadBody._fetching = false; });
    }
    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.tealGlow};border-bottom:1px solid ${C.border};font-size:9px;font-weight:700;letter-spacing:1px;text-transform:uppercase;color:${C.tealDim};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     = getTravelTimes()[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.teal : 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.teal : 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.teal : 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.teal : C.stockLo,
            lPct + '%'
        ));
    } else {
        sitGrid.appendChild(mkStat('❤️', 'Life', '—', C.textDim, null));
    }

    sitCard.appendChild(sitGrid);

    wrap.appendChild(sitCard);

    // ── Rehab card — shown right after situation report when in Switzerland ──
    if (country === 'Switzerland') {
        const _rcWrap = document.createElement('div');
        _rcWrap.id = 'lt-rehab-card-top';
        wrap.appendChild(_rcWrap);
        if (cfg.apiKey) {
            fetchAddiction().then(function(addiction) {
                const _ph = wrap.querySelector('#lt-rehab-card-top');
                if (_ph) renderRehabCard(_ph, addiction);
            }).catch(function() {});
        }
    }

    // ── 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.teal : 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.tealGlow};border-bottom:1px solid ${C.border};font-size:9px;font-weight:700;letter-spacing:1px;text-transform:uppercase;color:${C.tealDim};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 && !renderAbroadBody._yataFetching) {
        renderAbroadBody._yataFetching = true;
        isLoading = true;
        renderStatusBar();
        fetchYataData().then(function(result) {
            if (result && result.map) { abroadCache = result.map; }
            if (result && result.sa)  { xanSACache  = result.sa;  }
            isLoading = false;
            renderAbroadBody._yataFetching = false;
            if (panelEl && activeTab === 'abroad') renderPanel();
        }).catch(function() { isLoading = false; renderAbroadBody._yataFetching = 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.tealGlow};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.tealDim};text-align:${(txt==='Owned'||txt==='Stock'||txt==='Profit')?'center':'left'};text-transform:uppercase;letter-spacing:.4px;`;
        colHdr.appendChild(s);
    });
    itemsSec.appendChild(colHdr);

    const _setsOnlyA = cfg.getSetsOnly();
    const _knownNamesA = new Set(Object.keys(ID_TO_NAME).map(function(id){ return ID_TO_NAME[id]; }));
    const rows = cityData.stocks
        .filter(function(stock) { return !_setsOnlyA || _knownNamesA.has(stock.name) || stock.name === 'Xanax'; })
        .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.tealGlow; });
        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.tealDim },
                    { 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 _spark = buildSparkline(history);
                    if (_spark) {
                        const sparkLbl = document.createElement('div');
                        sparkLbl.style.cssText = `font-size:8px;font-weight:700;color:${C.tealDim};font-family:Consolas,monospace;margin-bottom:3px;`;
                        sparkLbl.innerHTML = '📦 Stock History <span style="color:' + C.textDim + '">(' + fmtHistorySpan(history) + ')</span>&nbsp;&nbsp;<span style="color:#ff4444">● OOS</span>&nbsp;<span style="color:#44dd88">● Restock</span>';
                        inner.appendChild(sparkLbl); inner.appendChild(_spark);
                    }
                }
                detail.appendChild(inner);
            }
        });

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

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

    // ── Rehab card — only when landed in Switzerland ──
    if (country === 'Switzerland') {
        const _rehabWrap = document.createElement('div');
        _rehabWrap.id = 'lt-rehab-card-abroad';
        _rehabWrap.style.cssText = 'padding:0 10px 10px;';
        body.appendChild(_rehabWrap);
        if (cfg.apiKey) {
            fetchAddiction().then(function(addiction) {
                const _ph = panelEl ? panelEl.querySelector('#lt-rehab-card-abroad') : null;
                if (_ph) renderRehabCard(_ph, addiction);
            }).catch(function() {});
        }
    }
}

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.teal : tabDimCol;
            t.style.borderBottomColor = isActive ? C.teal : 'transparent';
            t.style.background        = isActive ? C.tealGlow : '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> Expedition Tracker <small style="font-size:7px;font-weight:400;opacity:0.5;letter-spacing:0.5px;vertical-align:middle;">v10.5.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.teal; });
        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.teal;
            t.style.borderBottomColor = C.teal;
            t.style.background = C.tealGlow;
            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.teal; tabFirst.style.borderBottomColor = C.teal; tabFirst.style.background = C.tealGlow;
    tabs.appendChild(tabFirst); tabs.appendChild(tabXanax); tabs.appendChild(tabTravel); tabs.appendChild(tabAbroad);
 
    // ── REHAB WARNING BANNER (between tabs and status bar) ──
    const rehabBannerEl = document.createElement('div');
    rehabBannerEl.id = 'lt-rehab-banner-bar';
    rehabBannerEl.setAttribute('style', 'display:none;flex-shrink:0;');
    panelEl._rehabBannerEl = rehabBannerEl; // store ref for updates

    // ── 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(rehabBannerEl);
    panelEl.appendChild(sbar);
    panelEl.appendChild(bdy);
    // Show banner on panel build
    setTimeout(refreshRehabBanner, 500);
}
 
/* ─────────────────────────────────────────
   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 = 'Expedition 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.tealGlow},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.teal}aa,inset 0 0 12px rgba(0,0,0,0.2)`, 'important');
        toggleEl.style.setProperty('border-color', C.teal, 'important');
    });
    toggleEl.addEventListener('mouseout', () => {
        toggleEl.style.setProperty('box-shadow', `0 0 16px ${C.tealGlow},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: 'Expedition Tracker',   body: 'OSDevscape presents The Grand & Spectacular Overseas Expedition Tracker. Your complete Torn travel companion — sets, Xanax runs, profit analysis, trip planning, live YATA stock, rehab tracking, and a full Abroad HQ.' },
            { icon: '🎒', title: 'Sets Tab',              body: 'Items sorted by bottleneck. Own = surplus after completing sets. Abroad = live YATA stock. 🌍 Tourism Day banner when carry doubles. 🏛️ Museum Day bonus. Tap any row to expand price, profit, and stock sparkline.' },
            { icon: '🟢', title: 'Stock Colours',         body: 'Abroad column: Green = above threshold (Plushie ≥2000 / Flower ≥5000). Orange = below. Red = zero stock. 🏪 BoB badge = live Bits n\' Bobs count for Sheep, Teddy & Kitten plushies.' },
            { icon: '🔎', title: 'Item Filter',           body: 'Settings → Item Filter. Sets Items Only shows Plushies, Flowers, Prehistoric & Special. All YATA Items shows everything sold in overseas shops — applied across the Destination, Abroad, and Travel tabs.' },
            { 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. Tap the amount to copy it for your vault.' },
            { icon: '✈',  title: 'Xanax Runs',            body: 'Run multiple supply contracts simultaneously. Set client name, qty, and sell price. Log each trip with one tap. Tracks progress, profit vs buy price, and payment status. View full trip history in Past Runs.' },
            { icon: '📊', title: 'Focus Mode',             body: 'Active run or Priority mode → Sets and Travel collapse to your Xanax country only. Badge shows trips needed (remaining ÷ carry). Clears automatically when the run ends.' },
            { icon: '💰', title: 'Pure Profit',            body: 'Travel tab → top 3 items ranked by $/hr. Uses your exact flight times from Settings — speed tier (Standard / Airstrip / WLT / Bus. Class) and whether you have the Travel Book.' },
            { icon: '✈',  title: 'Travel Tab',             body: 'Loot Run Planner shows live round-trip times based on your speed and book settings. Items per Country shows YATA stock, buy/sell prices, and profit — respects the Sets Only / All Items filter.' },
            { icon: '📍', title: 'Destination Tab',        body: 'Replaces Sets while flying. Outbound: items sorted by profit. Returning: 💊 Rehab card (Switzerland), then Trip Planner showing your top 3 most-needed items globally to complete more sets.' },
            { icon: '🛬', title: 'Abroad Tab',             body: 'Appears when landed. Situation Report, budget card, unified item table. 💊 Rehab card when in Switzerland. YATA auto-refreshes if stale.' },
            { icon: '💊', title: 'Rehab Banner & Card',    body: 'Live rehab warning banner sits directly under the tabs — green / amber / red based on your 🧠 battle stat debuff and 🏬 company EE penalty. Toggle in Settings. Detailed card appears on Switzerland tabs.' },
            { icon: '📦', title: 'Display Case',           body: 'On displaycase.php → Add Items, quantities auto-fill with your owned count. Settings → Display Case: Off · Sets Only (Plushies, Flowers, Prehistoric & Special) · All Items.' },
            { icon: '🌍', title: 'Tourism & Museum Day',   body: 'Tourism Day (Sept 26-29): carry doubles — banner shows carry × 2. Museum Day (May 17-19): 10% point bonus — projects sets, pts, and value. Both pinnable in Settings.' },
            { icon: '⚙',  title: 'Settings',               body: 'Theme · API key · Rehab banner + thresholds · Item filter · Section visibility · Travel Book + Speed tier · Display Case autofill · Xanax carry · Museum Day · Tourism Day · Tooltip reset.' },
        ];
 
        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 refreshRehabBanner() {
    // Show banner immediately with cached/hardcoded data, then update when fetch resolves
    updateRehabBanner(null);
    if (cfg.apiKey) {
        fetchAddiction().then(function(addiction) {
            updateRehabBanner(addiction);
        }).catch(function() { updateRehabBanner(null); });
    }
}

async function refreshAll() {
    try {
    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;
                    })
            .catch(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();
    } catch(e) { isLoading = false;  }
}
 
async function mainLoop() {
    try {
        if (document.visibilityState !== 'visible') { pollTimer = setTimeout(mainLoop, 45000); return; }
        await refreshAll();
    } catch(e) { /* absorb — never let mainLoop reject */ }
    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;
        // 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);
                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) {  }
 
    // 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 {
        // Item IDs are patched after YATA loads — see patchItemIdsFromYata()
        mainLoop().catch(function() {});
    }
}
 
if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', () => setTimeout(init, 400));
} else {
    setTimeout(init, 400);
}
 
})();