✈️ Sets Tracker

Overseas sets companion — Plushies · Flowers · Prehistoric · Special · Xanax · Upgrade Planner — Torn

Від 15.03.2026. Дивіться остання версія.

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

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

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

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

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

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

(У мене вже є менеджер скриптів, дайте мені встановити його!)

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

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

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

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

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

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

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

// ==UserScript==
// @name         ✈️ Sets Tracker
// @namespace    https://osdevscape.com
// @version      9.1.9
// @author       Phillip_J_Fry [2184575] (OSMays8338) — OSDevscape
// @license      All Rights Reserved © 2026 OSDevscape
// @homepageURL  https://greatest.deepsurf.us/users/OSMays8338
// @description  Overseas sets companion — Plushies · Flowers · Prehistoric · Special · Xanax · Upgrade Planner — Torn
// @match        https://www.torn.com/*
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_xmlhttpRequest
// @grant        unsafeWindow
// @connect      yata.yt
// @connect      api.torn.com
// @run-at       document-end
// ==/UserScript==
 
// ╔══════════════════════════════════════════════════════════════╗
// ║              ✈️  SETS TRACKER  v9.1.9                          ║
// ║                                                              ║
// ║  Author  :  Phillip_J_Fry [2184575] (Torn) · OSMays8338     ║
// ║  Company :  OSDevscape                                       ║
// ║  License :  All Rights Reserved © 2026 OSDevscape           ║
// ║                                                              ║
// ║  Based on "Points Museum" by SuperNovae [2637223]            ║
// ║  Rebuilt under OSDevscape suite architecture v7              ║
// ╚══════════════════════════════════════════════════════════════╝
 
(function () {
'use strict';
 
/* ─────────────────────────────────────────
   STORAGE — dual layer: localStorage + GM
   Mirrors Target Tracker / Race Tracker
───────────────────────────────────────── */
const store = {
    get(k, d) {
        try { const v = localStorage.getItem('lt_' + k); if (v !== null) return v; } catch(e) {}
        try { return GM_getValue(k, d); } catch(e) {}
        return d;
    },
    set(k, v) {
        try { localStorage.setItem('lt_' + k, v); } catch(e) {}
        try { GM_setValue(k, v); } catch(e) {}
    },
    getJSON(k, d) {
        try { const v = localStorage.getItem('lt_' + k); if (v !== null) return JSON.parse(v); } catch(e) {}
        try { return JSON.parse(GM_getValue(k, JSON.stringify(d))); } catch(e) {}
        return d;
    },
    setJSON(k, v) {
        const s = JSON.stringify(v);
        try { localStorage.setItem('lt_' + k, s); } catch(e) {}
        try { GM_setValue(k, s); } catch(e) {}
    }
};
 
/* ─────────────────────────────────────────
   CONFIG
───────────────────────────────────────── */
const cfg = {
    get apiKey()  { return store.get('lt_api', ''); },
    set apiKey(v) { store.set('lt_api', v); },
    get userId()  { return store.get('lt_uid', ''); },
    set userId(v) { store.set('lt_uid', v); },
    getSectionVis() { return store.getJSON('lt_sections', { prehistoric: true, plushies: true, flowers: true, special: true, xanax: true }); },
    setSectionVis(v){ store.setJSON('lt_sections', v); },
    getXanCount()   { return store.getJSON('lt_xan_count', 0); },
    setXanCount(v)  { store.setJSON('lt_xan_count', v); },
    getXanCarry()       { return store.getJSON('lt_xan_carry', 0); },
    setXanCarry(v)      { store.setJSON('lt_xan_carry', v); },
    getXanPriority()    { return store.getJSON('lt_xan_priority', false); },
    setXanPriority(v)   { store.setJSON('lt_xan_priority', v); },
    getXanThreshold()   { return store.getJSON('lt_xan_threshold', 50); },
    setXanThreshold(v)  { store.setJSON('lt_xan_threshold', v); },
    getXanRuns()        { return store.getJSON('lt_xan_runs', []); },
    setXanRuns(v)       { store.setJSON('lt_xan_runs', v); },
    getXanCountry()     { return store.getJSON('lt_xan_country', 'South Africa'); },
    setXanCountry(v)    { store.setJSON('lt_xan_country', v); },
    getMuseumPin()      { return store.getJSON('lt_museum_pin', false); },
    setMuseumPin(v)     { store.setJSON('lt_museum_pin', v); },
    getTravelSpeed()    { return store.getJSON('lt_travel_speed', 0); },   // 0=Standard 1=Airstrip 2=PrivateJet 3=WindLines
    setTravelSpeed(v)   { store.setJSON('lt_travel_speed', v); },
    getProfitCarry()    { return store.getJSON('lt_profit_carry', 1); },
    setProfitCarry(v)   { store.setJSON('lt_profit_carry', v); },
    getProfitItemTypes(){ return store.getJSON('lt_profit_types', { plushies: true, flowers: true, prehistoric: true, special: true, drugs: false }); },
    setProfitItemTypes(v){ store.setJSON('lt_profit_types', v); },
    getProfitCountries(){ return store.getJSON('lt_profit_countries', { short: true, medium: true, long: true }); },
    setProfitCountries(v){ store.setJSON('lt_profit_countries', v); },
    getVaultDest()      { return store.getJSON('lt_vault_dest', 'faction'); }, // 'faction' | 'company' | 'property'
    setVaultDest(v)     { store.setJSON('lt_vault_dest', v); },
    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.
───────────────────────────────────────── */
// Torn/TornPDA confirmed: body has class 'dark-mode' in dark theme.
// Light mode = body does NOT have 'dark-mode' (or any other dark indicator).
const TORN_DARK_CLASSES = ['dark-mode', 'dark', 'night-mode', 'theme-dark'];

function isLightMode() {
    if (!document.body) return false;
    // Primary: known dark class on body or html → dark mode
    for (const cls of TORN_DARK_CLASSES) {
        if (document.body.classList.contains(cls)) return false;
        if (document.documentElement.classList.contains(cls)) return false;
    }
    // data-theme dark check
    const dt = (document.documentElement.getAttribute('data-theme') || document.body.getAttribute('data-theme') || '').toLowerCase();
    if (dt === 'dark' || dt === 'night') return false;
    if (dt === 'light' || dt === 'day')  return true;
    // Legacy light class check
    const LIGHT = ['light-mode','day-mode','theme-light','t-theme-day','light','daymode','l'];
    for (const cls of LIGHT) {
        if (document.body.classList.contains(cls)) return true;
        if (document.documentElement.classList.contains(cls)) return true;
    }
    // Brightness fallback — light body = rgb(204,204,204), dark = rgb(25,25,25)
    try {
        const bg = getComputedStyle(document.body).backgroundColor;
        const m  = bg.match(/rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/);
        if (m) {
            const lum = (parseInt(m[1])*299 + parseInt(m[2])*587 + parseInt(m[3])*114) / 1000;
            return lum > 140;
        }
    } catch(e) {}
    return false; // default dark
}

 
/* ─────────────────────────────────────────
   COLOUR PALETTES  (dark = default)
───────────────────────────────────────── */
const DARK_PALETTE = {
    bg:        '#001214',
    bg2:       '#001e24',
    border:    'rgba(0,180,210,0.35)',
    gold:      '#00c8e0',
    goldDim:   'rgba(0,200,224,0.65)',
    goldGlow:  'rgba(0,200,224,0.10)',
    green:     '#8BC34A',
    greenDim:  'rgba(139,195,74,0.7)',
    text:      '#d0f0f8',
    textDim:   'rgba(140,220,235,0.55)',
    abroad:    '#7fe0f0',
    stockHi:   '#00ff00',
    stockMid:  '#ffa500',
    stockLo:   '#ff0000',
    okay:      '#66dd66',
    card:      'rgba(0,16,26,0.65)',   // card background
    cardBorder:'rgba(0,200,224,0.28)', // card border
    mono:      '"Share Tech Mono",Consolas,monospace',
    sans:      'Rajdhani,"Segoe UI",Arial,sans-serif',
    // settings popup extras
    settBg:    '#000e12',
    settBorder:'rgba(0,180,210,0.55)',
    settNote:  'rgba(0,80,100,0.3)',
    settHdr:   'rgba(0,80,110,0.3)',
};
 
const LIGHT_PALETTE = {
    bg:        '#b8dff0',       // noticeably deeper blue-tint base
    bg2:       '#9dd0e6',       // deeper secondary surface
    border:    'rgba(0,70,100,0.55)',
    gold:      '#002a36',       // very deep teal heading colour
    goldDim:   'rgba(0,55,75,0.90)',
    goldGlow:  'rgba(0,75,105,0.18)',
    green:     '#163800',
    greenDim:  'rgba(20,55,4,0.90)',
    text:      '#000a10',
    textDim:   'rgba(0,32,46,0.78)',
    abroad:    '#002d40',
    stockHi:   '#0d3600',
    stockMid:  '#442500',
    stockLo:   '#680000',
    okay:      '#0d3600',
    card:      'rgba(0,40,60,0.18)',   // card background
    cardBorder:'rgba(0,65,95,0.40)',   // card border
    mono:      '"Share Tech Mono",Consolas,monospace',
    sans:      'Rajdhani,"Segoe UI",Arial,sans-serif',
    // settings popup extras
    settBg:    '#c2e8f8',
    settBorder:'rgba(0,90,120,0.65)',
    settNote:  'rgba(0,110,145,0.22)',
    settHdr:   'rgba(0,100,135,0.22)',
};
 
// C is a live proxy — always reflects current Torn theme
let C = isLightMode() ? { ...LIGHT_PALETTE } : { ...DARK_PALETTE };
 
function syncTheme() {
    const light = isLightMode();
    const src   = light ? LIGHT_PALETTE : DARK_PALETTE;
    Object.assign(C, src);
}
 
// 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);
        }
    });
    navObs.observe(document.body, { childList: true, subtree: true });
    // Also check on load in case we're already on items page
    watchItemsPage();
    // Check if we're already on the travel page
    setTimeout(scrapeTravelPage, 1000);
})();
 
// Watch <html> and <body> for class/attribute changes (user switches theme mid-session)
(function watchTheme() {
    let _lastLight = null;
    const observer = new MutationObserver(() => {
        const nowLight = isLightMode();
        if (_lastLight === nowLight) return; // no actual change
        _lastLight = nowLight;
        syncTheme();
        // Rebuild panel background + toggle — these are set in buildPanel/buildToggle
        // and don't update from renderPanel alone
        if (panelEl && toggleEl) {
            // Update panel background in-place — don't rebuild DOM, just change styles
            const light = isLightMode();
            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');
            // Update toggle background
            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');
            // Re-render body content with new colours
            if (panelOpen) renderPanel();
        }
    });
    const opts = { attributes: true, attributeFilter: ['class','data-theme','data-color-scheme'] };
    if (document.documentElement) observer.observe(document.documentElement, opts);
    if (document.body)            observer.observe(document.body, opts);
})();
 
/* ─────────────────────────────────────────
   POINTS & THRESHOLDS
───────────────────────────────────────── */
const PRE_PTS = 25, FLO_PTS = 10, PLU_PTS = 10, MET_PTS = 15, FOS_PTS = 20;
const PLU_THRESH = 2000, FLO_THRESH = 5000;
const XANAX_ID   = 206;
const POINTS_ENDPOINT = 'https://api.torn.com/v2/market/pointsmarket';
 
const POINTS_CACHE_DUR  = 300000;
const POINTS_HIST_SIZE  = 5;
let pointsPriceCache    = { time: 0, price: 0, history: [] };

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

// 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',
    '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
let isLoading = false;
let pollTimer = null;
 
// 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 travelStatus    = null; // null | { traveling: bool, destination: string, time_left: number }
 
/* ─────────────────────────────────────────
   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:#00121a !important;' +
        'border:1px solid rgba(0,200,224,0.7) !important;border-radius:8px !important;' +
        'padding:10px 20px !important;color:#00c8e0 !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:${C.settHdr};padding:13px 18px;font-size:12px;font-weight:700;letter-spacing:1.4px;color:${C.gold};border-bottom:1px solid ${C.border};`,
        body:   'padding:16px;',
        note:   `background:${C.settNote};border-left:3px solid ${C.border};padding:9px 12px;border-radius:4px;font-size:11px;color:${C.goldDim};margin-bottom:16px;line-height:1.5;`,
        secHdr: `font-size:9px;font-weight:700;letter-spacing:1.2px;text-transform:uppercase;color:${C.goldDim};padding-bottom:5px;margin:16px 0 8px;border-bottom:1px solid ${C.border};font-family:Consolas,monospace;`,
        lbl:    `display:block;margin-bottom:5px;font-size:10px;font-weight:700;letter-spacing:1px;text-transform:uppercase;color:${C.green};`,
        inp:    `display:block;width:100%;box-sizing:border-box;padding:9px 11px;background:${C.bg};border:1px solid ${C.border};border-radius:5px;color:${C.text};font-size:13px;font-family:Consolas,monospace;outline:none;`,
        hint:   `font-size:9px;color:${C.textDim};margin-top:4px;font-family:Consolas,monospace;`,
        btnRow: 'display:flex;gap:8px;margin-top:18px;',
        btn:    `flex:1;padding:10px 0;border-radius:7px;font-size:12px;font-weight:700;cursor:pointer;border:1px solid ${C.border};font-family:Arial,sans-serif;letter-spacing:0.5px;`,
    };
 
    const wrap = el('div'); wrap.id = 'lt-settings-wrap'; wrap.setAttribute('style', imp(S.wrap));
    const box  = el('div'); box.setAttribute('style', imp(S.box));
    const hdr  = el('div'); hdr.textContent = '⚙  LOOT TRACKER — Settings'; hdr.setAttribute('style', imp(S.hdr));
    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;
    }
 
    // ── 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:#00c8e0;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:rgba(0,200,224,0.04);border:1px solid rgba(0,140,170,0.18);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:rgba(0,200,224,0.04);border:1px solid rgba(0,140,170,0.18);cursor:pointer;'));
    const tooltipChk = document.createElement('input'); tooltipChk.type = 'checkbox'; tooltipChk.id = 'lt-pref-tooltip';
    let tooltipAlreadySeen = false;
    try { tooltipAlreadySeen = !!localStorage.getItem('lt_tooltip_seen'); } catch(e) {}
    tooltipChk.checked = !tooltipAlreadySeen;
    tooltipChk.setAttribute('style', imp('width:14px;height:14px;cursor:pointer;accent-color:#00c8e0;flex-shrink:0;'));
    const tooltipLbl = el('label'); tooltipLbl.textContent = '✈️ Show welcome tooltip on next load'; tooltipLbl.setAttribute('for', 'lt-pref-tooltip');
    tooltipLbl.setAttribute('style', imp(`font-size:10px;font-weight:600;color:${C.text};cursor:pointer;flex:1;`));
    tooltipRow.appendChild(tooltipChk); tooltipRow.appendChild(tooltipLbl);
    tooltipRow.addEventListener('click', e => { if (e.target !== tooltipChk) tooltipChk.checked = !tooltipChk.checked; });
 
    // ── 🧪 Xanax ──
    const xanSettHdr = el('div'); xanSettHdr.textContent = '🧪 Xanax'; xanSettHdr.setAttribute('style', imp(S.secHdr));
 
    // Adjust Count row
    const xanCarryGroup = el('div'); xanCarryGroup.setAttribute('style', imp('margin-bottom:10px;'));
    const xanCarryLbl = el('label'); xanCarryLbl.textContent = 'Carry Limit'; xanCarryLbl.setAttribute('for','lt-si-xan-carry'); xanCarryLbl.setAttribute('style', imp(S.lbl));
    const xanCarryRow = el('div'); xanCarryRow.setAttribute('style', imp('display:flex;align-items:center;gap:6px;'));
    const xanCarDecBtn = el('button'); xanCarDecBtn.textContent = '−'; xanCarDecBtn.setAttribute('type','button');
    xanCarDecBtn.setAttribute('style', imp('padding:6px 12px;border-radius:5px;border:1px solid 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;'));
    const xanCarryInp = document.createElement('input'); xanCarryInp.type = 'number'; xanCarryInp.min = '0';
    xanCarryInp.id = 'lt-si-xan-carry'; xanCarryInp.value = cfg.getXanCarry();
    xanCarryInp.setAttribute('style', imp(`font-family:Consolas,monospace;font-size:14px;font-weight:700;text-align:center;width:72px;padding:6px 4px;background:${C.bg};border:1px solid ${C.border};border-radius:5px;color:${C.text};outline:none;-moz-appearance:textfield;`));
    xanCarryInp.addEventListener('focus', () => xanCarryInp.style.setProperty('border-color','rgba(0,190,215,0.8)','important'));
    xanCarryInp.addEventListener('blur',  () => xanCarryInp.style.setProperty('border-color','rgba(0,140,170,0.4)','important'));
    const xanCarIncBtn = el('button'); xanCarIncBtn.textContent = '+'; xanCarIncBtn.setAttribute('type','button');
    xanCarIncBtn.setAttribute('style', imp('padding:6px 12px;border-radius:5px;border:1px solid rgba(0,200,224,0.35);background:rgba(0,40,60,0.5);color:#00c8e0;font-weight:800;font-size:15px;cursor:pointer;line-height:1;'));
    xanCarDecBtn.addEventListener('click', () => { xanCarryInp.value = Math.max(0,(parseInt(xanCarryInp.value)||0)-1); });
    xanCarIncBtn.addEventListener('click', () => { xanCarryInp.value = (parseInt(xanCarryInp.value)||0)+1; });
    xanCarryRow.appendChild(xanCarDecBtn); xanCarryRow.appendChild(xanCarryInp); xanCarryRow.appendChild(xanCarIncBtn);
    const xanCarryHint = el('div'); xanCarryHint.textContent = 'Max items you can carry per trip (also used by Pure Profit calculator)'; xanCarryHint.setAttribute('style', imp(S.hint));
    xanCarryGroup.appendChild(xanCarryLbl); xanCarryGroup.appendChild(xanCarryRow); xanCarryGroup.appendChild(xanCarryHint);
 
 
    // ── Buttons ──
    const btnRow    = el('div'); btnRow.setAttribute('style', imp(S.btnRow));
    const btnCancel = el('button'); btnCancel.textContent = 'Cancel';       btnCancel.setAttribute('type','button');
    const btnSave   = el('button'); btnSave.textContent   = 'Save & Refresh'; btnSave.setAttribute('type','button');
    btnCancel.setAttribute('style', imp(S.btn + `background:${C.bg};color:${C.green};`));
    btnSave.setAttribute('style',   imp(S.btn + `background:${C.settNote};color:${C.gold};`));
    btnRow.appendChild(btnCancel); btnRow.appendChild(btnSave);
 
    body.appendChild(note);
    body.appendChild(credHdr);
    body.appendChild(fAPI);
    body.appendChild(apiLink);
    body.appendChild(fUID);
    body.appendChild(secHdr);
    body.appendChild(visGrid);
    body.appendChild(xanSettHdr);
    body.appendChild(xanCarryGroup);
 
    // ── Museum Day toggle ──
    const museumSettHdr = el('div'); museumSettHdr.textContent = '🏛️ Museum Day'; museumSettHdr.setAttribute('style', imp(S.secHdr));
    const museumPinRow = el('div'); museumPinRow.setAttribute('style', imp('display:flex;align-items:center;gap:8px;padding:5px 8px;border-radius:5px;background:rgba(255,184,48,0.04);border:1px solid rgba(255,184,48,0.18);cursor:pointer;margin-bottom:8px;'));
    const museumPinChk = document.createElement('input'); museumPinChk.type = 'checkbox'; museumPinChk.id = 'lt-museum-pin';
    museumPinChk.checked = cfg.getMuseumPin();
    museumPinChk.setAttribute('style', imp('width:14px;height:14px;cursor:pointer;accent-color:#ffb830;flex-shrink:0;'));
    const museumPinLbl = el('label'); museumPinLbl.textContent = '🏛️ Always show Museum Day bonus'; museumPinLbl.setAttribute('for', 'lt-museum-pin');
    museumPinLbl.setAttribute('style', imp(`font-size:10px;font-weight:600;color:${C.text};cursor:pointer;flex:1;`));
    museumPinRow.appendChild(museumPinChk); museumPinRow.appendChild(museumPinLbl);
    museumPinRow.addEventListener('click', e => { if (e.target !== museumPinChk) museumPinChk.checked = !museumPinChk.checked; });
    body.appendChild(museumSettHdr);
    body.appendChild(museumPinRow);


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

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

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

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

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

        const isReturn = !TORN_DEST_TO_CODE[destination];

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

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

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() : '';

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

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


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

    const result = {};
    try {
        // Torn v1: /torn/[id1,id2,...]?selections=items
        // Returns { items: { "id": { name, market_value, ... } } }
        // market_value is Torn's own calculated market price — reliable single call
        const data = await gmFetch(
            `https://api.torn.com/torn/${ids.join(',')}?selections=items&key=${cfg.apiKey}`,
            20000
        );
        const items = data.items || {};
        Object.entries(items).forEach(([idStr, item]) => {
            const name = ID_TO_NAME[Number(idStr)];
            if (!name) return;
            const mv = Number(item.market_value || 0);
            if (mv > 0) result[name] = mv;
        });
        console.log('[SetsTracker] market values fetched:', Object.keys(result).length, 'items');
        // Record history for sparklines
        Object.entries(result).forEach(([name, price]) => { if (price > 0) cfg.addPriceHistory(name, price); });
    } catch(e) { console.warn('[SetsTracker] fetchMarketValues threw:', e); }
    return result;
}
function calcSet(inv, items) {
    const vals = Object.keys(items).map(k => inv[k] || 0);
    return vals.length ? Math.min(...vals) : 0;
}
 
function getSortedItems(inv, items, sets) {
    return Object.entries(items)
        .map(([name, data]) => ({ name, data, remaining: (inv[name] || 0) - sets }))
        .sort((a, b) => a.remaining - b.remaining);
}
 
function getBottleneck(inv, items, sets) {
    const sorted = getSortedItems(inv, items, sets).filter(i => i.remaining < 5);
    if (!sorted.length) return null;
    const parts = sorted.map(i => {
        const locLabel = LOCATIONS[i.data.loc]?.label || i.data.loc;
        return `${i.data.s} → ${locLabel}`;
    });
    return 'Need ' + parts.join(' & ');
}
 
function stockClass(name, qty) {
    if (qty === 0) return 'lt-da';
    if (GROUPS.Plushies.items[name]) return qty >= PLU_THRESH ? 'lt-hi' : 'lt-wa';
    if (GROUPS.Flowers.items[name])  return qty >= FLO_THRESH ? 'lt-hi' : 'lt-wa';
    return qty > 0 ? 'lt-hi' : 'lt-da';
}
 
function stockColor(name, qty) {
    if (qty === 0) return C.stockLo;
    if (GROUPS.Plushies.items[name]) return qty >= PLU_THRESH ? C.stockHi : C.stockMid;
    if (GROUPS.Flowers.items[name])  return qty >= FLO_THRESH ? C.stockHi : C.stockMid;
    return qty > 0 ? C.stockHi : C.stockLo;
}
 
/* ─────────────────────────────────────────
   RENDER HELPERS
───────────────────────────────────────── */
function makeEmpty(icon, msg) {
    const el = document.createElement('div');
    el.style.cssText = `padding:28px 16px;text-align:center;color:${C.textDim};font-size:11px;line-height:1.6;font-family:Arial,sans-serif;`;
    el.innerHTML = `<div style="font-size:26px;margin-bottom:8px;">${icon}</div>${msg}`;
    return el;
}
 
function makeSectionLabel(text, setCount, pts) {
    const el = document.createElement('div');
    el.style.cssText = `display:flex;justify-content:space-between;align-items:center;padding:4px 10px;font-size:9px;font-weight:700;letter-spacing:1px;text-transform:uppercase;color:${C.goldDim};background:rgba(0,60,80,0.15);border-top:1px solid rgba(0,140,180,0.2);border-bottom:1px solid rgba(0,140,180,0.15);font-family:Consolas,monospace;`;
    const left = document.createElement('span'); left.textContent = text;
    const right = document.createElement('span');
    right.style.cssText = `color:${C.gold};font-weight:700;font-size:8.5px;`;
    right.textContent = setCount !== undefined ? `${setCount} sets · ${setCount * pts} pts` : '';
    el.appendChild(left); el.appendChild(right);
    return el;
}
 
function makeAlertRow(msg) {
    const el = document.createElement('div');
    el.style.cssText = `margin:3px 8px;padding:5px 8px 5px 20px;position:relative;background:rgba(160,20,20,0.18);border-left:2px solid rgba(220,50,50,0.8);border-radius:3px;font-size:9.5px;font-weight:600;color:#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:#ff6666;">!</span>${msg}`;
    return el;
}
 
/* ─────────────────────────────────────────
   ITEM ROW BUILDER
───────────────────────────────────────── */
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:rgba(255,255,255,0.04);border:1px solid rgba(255,255,255,0.08);display:block;transition:transform 0.2s;';
    img.addEventListener('mouseover', () => img.style.transform = 'scale(1.15)');
    img.addEventListener('mouseout',  () => img.style.transform = 'scale(1)');
    const imgFallback = document.createElement('span');
    imgFallback.style.cssText = 'display:none;width:30px;height:30px;font-size:7px;font-weight:700;text-align:center;line-height:30px;border-radius:2px;border:1px solid rgba(255,255,255,0.1);background:rgba(255,255,255,0.05);color:#c0e0ff;font-family:Consolas,monospace;';
    imgFallback.textContent = data.s;
    img.addEventListener('error', () => { img.style.display = 'none'; imgFallback.style.display = 'block'; });
    imgWrap.appendChild(img); imgWrap.appendChild(imgFallback);
 
    // col 2 — own count (remaining after sets)
    const ownEl = document.createElement('div');
    ownEl.style.cssText = `color:${C.green};background:rgba(0,180,210,0.08);font-weight:700;text-align:center;border:1px solid rgba(0,180,210,0.15);font-family:Consolas,monospace;padding:2px 3px;border-radius:2px;font-size:10px;`;
    ownEl.title = `You have ${remaining} extra after completing sets`;
    ownEl.textContent = remaining;
 
    // col 3 — abroad / BoB button
    let abroadEl;
    if (isBob) {
        const bobQty = bobCache[name] !== undefined ? bobCache[name] : null;
        const bobCol = bobQty === null ? C.gold : bobQty > 0 ? C.okay : C.textDim;
        abroadEl = document.createElement('a');
        abroadEl.href = 'https://www.torn.com/shops.php?step=bitsnbobs';
        abroadEl.style.cssText = `display:flex;align-items:center;justify-content:center;font-size:9px;font-weight:700;color:${bobCol};background:${bobCol}1a;border:1px solid ${bobCol}44;border-radius:3px;padding:2px 4px;cursor:pointer;white-space:nowrap;text-decoration:none !important;transition:all 0.15s;font-family:Consolas,monospace;`;
        abroadEl.textContent = bobQty !== null ? '🏪 ' + bobQty : '🏪 BoB';
        abroadEl.title = bobQty !== null ? "Bits n' Bobs stock: " + bobQty + "\nClick to open shop" : "Open Bits n' Bobs shop";
        abroadEl.addEventListener('mouseover', () => { abroadEl.style.background = bobCol + '33'; abroadEl.style.borderColor = bobCol + '88'; });
        abroadEl.addEventListener('mouseout',  () => { abroadEl.style.background = bobCol + '1a'; abroadEl.style.borderColor = bobCol + '44'; });
    } else {
        const sc = stockClass(name, abroadQty);
        const col = stockColor(name, abroadQty);
        abroadEl = document.createElement('div');
        abroadEl.className = sc;
        abroadEl.style.cssText = `color:${col};background:${col}1a;font-weight:700;text-align:center;border:1px solid ${col}44;font-family:Consolas,monospace;padding:2px 3px;border-radius:2px;font-size:10px;transition:all 0.3s;text-shadow:0 0 4px ${col}66;`;
        abroadEl.title = `Overseas stock (Torn API): ${abroadQty}`;
        abroadEl.textContent = abroadQty;
    }
 
    // col 4 — flag / location
    const loc    = LOCATIONS[data.loc] || { flag: '❓', label: data.loc };
    const flagEl = document.createElement('div');
    flagEl.style.cssText = 'display:flex;align-items:center;justify-content:center;font-size:16px;line-height:1;';
    flagEl.title = loc.label;
    flagEl.textContent = loc.flag;
 
    row.appendChild(imgWrap);
    row.appendChild(ownEl);
    row.appendChild(abroadEl);
    row.appendChild(flagEl);
    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-country key if we know the location, else global fallback
        const itemCode = data && data.loc ? (Object.values(TORN_DEST_TO_CODE).find((c,i)=>Object.keys(TORN_DEST_TO_CODE)[i]===data.loc) || '') : '';
        const history   = cfg.getStockHistory(name + (itemCode ? '_'+itemCode : ''));
        const fmt = n => n >= 1e6 ? '$' + (n/1e6).toFixed(2) + 'M' : n >= 1e3 ? '$' + Math.round(n/1000) + 'k' : '$' + n;
        const lastUpdated = history.length ? new Date(history[history.length-1].ts).toLocaleString([], { month:'short', day:'numeric', hour:'2-digit', minute:'2-digit' }) : '—';

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

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

            const prices  = history.map(h => h.price);
            const minP    = Math.min(...prices);
            const maxP    = Math.max(...prices);
            const range   = maxP - minP || 1;
            const W = 260, H = 36, pad = 4;

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

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

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

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

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

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

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

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

    // Main start button
    const startBtn = document.createElement('button'); startBtn.type='button';
    startBtn.style.cssText = `width:100%;padding:10px 0;border-radius:0;font-size:11px;font-weight:700;cursor:pointer;font-family:Arial,sans-serif;background:transparent;border:none;border-bottom:1px solid rgba(0,200,224,0.18);color:#00c8e0;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 rgba(0,140,170,0.18)' : '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 rgba(0,140,170,0.18)' : 'none';
        // Defer renderPanel — calling it synchronously destroys the element firing this event
        if (panelEl) setTimeout(() => renderPanel(), 0);
    };

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

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

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

    const hasFaction = xanFacCache !== null;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    // ── Header ──
    const hdr = document.createElement('div');
    hdr.style.cssText = `display:flex;align-items:center;padding:12px 14px;background:${C.settHdr};border-bottom:1px solid ${C.border};`;
    const hdrTitle = document.createElement('div');
    hdrTitle.style.cssText = `font-size:11px;font-weight:700;letter-spacing:1.2px;text-transform:uppercase;color:${C.gold};flex:1;`;
    hdrTitle.textContent = '💰 Pure Profit';
    const closeBtn = document.createElement('button'); closeBtn.type='button'; closeBtn.textContent='✕';
    closeBtn.style.cssText = `background:none;border:none;color:${C.goldDim};font-size:14px;cursor:pointer;padding:0 2px;`;
    closeBtn.addEventListener('click', () => overlay.remove());
    hdr.appendChild(hdrTitle); hdr.appendChild(closeBtn);
    box.appendChild(hdr);

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

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

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

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

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

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

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

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

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

            const mid = document.createElement('div'); mid.style.cssText = 'flex:1;min-width:0;';
            const nameEl = document.createElement('div'); nameEl.textContent = item.name;
            nameEl.style.cssText = `font-size:10px;font-weight:700;color:${C.text};font-family:Arial,sans-serif;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;`;
            const subRow = document.createElement('div'); subRow.style.cssText = 'display:flex;align-items:center;gap:4px;margin-top:2px;';
            const flagEl = document.createElement('span'); flagEl.textContent = loc.flag; flagEl.style.cssText = 'font-size:11px;';
            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',  sub:'MX HI' },
        { key:'medium', label:'Medium', sub:'CA UK CH JP KY' },
        { key:'long',   label:'Long',   sub:'AR CN AE ZA' },
    ];
    countryOpts.forEach(opt => {
        const btn = document.createElement('button'); btn.type='button';
        const active = () => curCountries[opt.key];
        const setStyle = () => {
            btn.style.cssText = `flex:1;padding:6px 4px;border-radius:5px;cursor:pointer;text-align:center;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);'
                           : 'background:${C.card};border:1px solid ${C.border};');
        };
        btn.innerHTML = `<div style="font-size:9px;font-weight:700;color:${active() ? C.gold : C.text};font-family:Arial,sans-serif;">${opt.label}</div><div style="font-size:7px;color:${C.textDim};font-family:Consolas,monospace;margin-top:1px;">${opt.sub}</div>`;
        setStyle();
        btn.addEventListener('click', () => {
            curCountries[opt.key] = !curCountries[opt.key];
            // Re-render button innerHTML with updated colour
            btn.innerHTML = `<div style="font-size:9px;font-weight:700;color:${active() ? C.gold : C.text};font-family:Arial,sans-serif;">${opt.label}</div><div style="font-size:7px;color:${C.textDim};font-family:Consolas,monospace;margin-top:1px;">${opt.sub}</div>`;
            setStyle();
            cfg.setProfitCountries({ ...curCountries });
            renderResults();
        });
        countryGrid.appendChild(btn);
    });
    settDiv.appendChild(countryGrid);

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

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

    renderResults();
}

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

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

        const xanSecHdr = document.createElement('div');
        xanSecHdr.style.cssText = `display:flex;justify-content:space-between;align-items:center;padding:4px 10px;font-size:9px;font-weight:700;letter-spacing:1px;text-transform:uppercase;color:#66cc66;background:rgba(102,187,102,0.08);border-top:1px solid rgba(102,187,102,0.25);border-bottom:1px solid rgba(102,187,102,0.15);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:#66cc66;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.find(st => st.id === XANAX_ID);
                if (!s) return null; // show even 0-stock countries so user can plan
                const cKey = Object.keys(TORN_DEST_TO_CODE).find(k => TORN_DEST_TO_CODE[k] === code);
                const times = cKey ? TRAVEL_TIMES[cKey] : null;
                const tHr = times ? (times[speedTier2] || times[0]) : 99;
                const totalHr = tHr * 2 + (90/3600);
                const profit = xanSellPrice2 > 0 && xanSellPrice2 > s.cost ? xanSellPrice2 - s.cost : 0;
                // If no sell price set, use YATA buy price / r/t as efficiency metric
                const profPerHr = profit > 0 ? Math.round((profit * carry2) / totalHr) : 0;
                const hasRealProfit = profit > 0;
                const loc = cKey ? (LOCATIONS[cKey] || {flag:'✈️', label:cKey}) : {flag:'✈️', label:data.city};
                return { code, data, s, cKey, loc, tHr, profit, tripP: profit*carry2, tripCostV: s.cost*carry2, profPerHr, sellPrice: xanSellPrice2, hasRealProfit };
            })
            .filter(Boolean)
            .sort((a, b) => b.profPerHr - a.profPerHr);

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

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

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

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

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

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

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

        // ⚙ settings icon
        const gearBtn = document.createElement('span');
        gearBtn.title = 'Profit settings';
        gearBtn.innerHTML = '<svg width="13" height="13" viewBox="0 0 16 16" fill="none"><circle cx="8" cy="8" r="2.5" stroke="currentColor" stroke-width="1.4"/><path d="M8 1v2M8 13v2M1 8h2M13 8h2M2.93 2.93l1.41 1.41M11.66 11.66l1.41 1.41M2.93 13.07l1.41-1.41M11.66 4.34l1.41-1.41" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/></svg>';
        gearBtn.style.cssText = `cursor:pointer;display:flex;align-items:center;color:${C.goldDim};opacity:0.7;transition:opacity 0.15s;padding:2px;`;
        gearBtn.addEventListener('mouseover', () => { gearBtn.style.opacity = '1'; gearBtn.style.color = C.gold; });
        gearBtn.addEventListener('mouseout',  () => { gearBtn.style.opacity = '0.7'; gearBtn.style.color = C.goldDim; });
        gearBtn.addEventListener('click', e => { e.stopPropagation(); openProfitPopup(); });

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

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

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

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

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

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

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

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

            if (!xanCtries.length) {
                const empty2 = document.createElement('div');
                empty2.style.cssText = `font-size:9.5px;color:${C.textDim};font-family:Consolas,monospace;padding:8px 10px;text-align:center;`;
                empty2.textContent = 'No Xanax overseas stock found — try refreshing';
                content.appendChild(empty2);
            } else {
                const xHdr2 = document.createElement('div');
                xHdr2.style.cssText = `display:inline-flex;align-items:center;gap:4px;padding:2px 8px;border-radius:10px;background:rgba(102,187,102,0.08);border:1px solid rgba(102,187,102,0.3);font-size:8px;font-family:Consolas,monospace;color:#66cc66;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 rgba(102,187,102,0.35);background:rgba(102,187,102,0.07);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 = 'rgba(102,187,102,0.12)');
                    xCardHdr.addEventListener('mouseout',  () => xCardHdr.style.background = 'transparent');

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

                        pDetail.appendChild(pInner);
                    }
                });

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

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

    // ── Loot Run Planner ──
    const escSec = document.createElement('div'); escSec.appendChild(secTitle('💰 Loot Run Planner'));
 
    const lootRuns = [
        {
            flag: '🇲🇽', name: 'Mexico', col: '#ffb830', time: '~1.5h',
            loot: ['Dahlia', 'Jaguar Plushie', 'Obsidian Point'],
        },
        {
            flag: '🇦🇷', name: 'Argentina', col: '#74c9ff', time: '~14h',
            loot: ['Ceibo Flower', 'Monkey Plushie', 'Chalcedony Point', 'Meteorite Fragment', 'Patagonian Fossil'],
        },
        {
            flag: '🇬🇧', name: 'United Kingdom', col: '#cc88ff', time: '~10h',
            loot: ['Heather', 'Nessie Plushie', 'Red Fox Plushie', 'Chert Point', 'Xanax'],
        },
        {
            flag: '🇨🇦', name: 'Canada', col: '#ff7070', time: '~9h',
            loot: ['Crocus', 'Wolverine Plushie', 'Quartz Point', 'Xanax'],
        },
        {
            flag: '🇿🇦', name: 'South Africa', col: '#60cc60', time: '~16h',
            loot: ['African Violet', 'Lion Plushie', 'Quartzite Point', 'Xanax'],
        },
        {
            flag: '🇨🇭', name: 'Switzerland', col: '#ff9999', time: '~11h',
            loot: ['Edelweiss', 'Chamois Plushie'],
        },
        {
            flag: '🇯🇵', name: 'Japan', col: '#ffaacc', time: '~12h',
            loot: ['Cherry Blossom', 'Xanax'],
        },
        {
            flag: '🇨🇳', name: 'China', col: '#ff6040', time: '~14h',
            loot: ['Peony', 'Panda Plushie'],
        },
        {
            flag: '🏝️', name: 'Hawaii', col: '#ffe066', time: '~6h',
            loot: ['Orchid', 'Basalt Point'],
        },
        {
            flag: '🇦🇪', name: 'UAE', col: '#88ddaa', time: '~14h',
            loot: ['Tribulus Omanense', 'Camel Plushie'],
        },
        {
            flag: '🇰🇾', name: 'Cayman Islands', col: '#66ccff', time: '~10h',
            loot: ['Banana Orchid', 'Stingray Plushie'],
        },
    ];
 
    lootRuns.forEach(run => {
        let needed = 0, total = 0;
        run.loot.forEach(itemName => {
            // Determine which group (and thus which section key) this item belongs to
            let groupKey = null;
            for (const [gName, g] of Object.entries(GROUPS)) {
                if (g.items[itemName]) { groupKey = gName.toLowerCase(); break; }
            }
            if (itemName === 'Meteorite Fragment' || itemName === 'Patagonian Fossil') groupKey = 'special';
 
            // Skip items whose section is hidden — don't count them at all
            if (groupKey && vis[groupKey] === false) return;
 
            total++;
            let sets = 0;
            for (const g of Object.values(GROUPS)) {
                if (g.items[itemName]) { sets = calcSet(invCache, g.items); break; }
            }
            const have = (invCache[itemName] || 0) - sets;
            if (have < 5) needed++;
        });
        run.needed = needed; run.total = total;
    });
 
    // Focus mode: active run beats personal priority
    const _xanFocusCtryLoot = cfg.getXanCountry(); // declare early — used in banner innerHTML
    const runs          = cfg.getXanRuns();
    const activeRun     = runs.find(r => r.active);
    const xanPriority   = cfg.getXanPriority();
    const xanThreshold  = cfg.getXanThreshold();
    const xanBelowThresh = xanPriority && xanPersonal < xanThreshold;
    const focusMode      = !!(activeRun || xanBelowThresh);
 
    lootRuns.sort((a, b) => {
        if (focusMode) {
            const aIsSA = a.name === 'South Africa';
            const bIsSA = b.name === 'South Africa';
            if (aIsSA && !bIsSA) return -1;
            if (bIsSA && !aIsSA) return  1;
        }
        return b.needed - a.needed;
    });
 
    const runGrid = document.createElement('div'); runGrid.style.cssText = 'display:flex;flex-direction:column;gap:5px;';
 
    // Focus banner
    if (focusMode) {
        const bannerCol = activeRun ? '#00c8e0' : C.green;
        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 ? 'rgba(102,187,102,0.7)' : `${run.col}${urgency > 0.5 ? '66' : '28'}`;
        const bgCol     = isSAxanPinned ? 'rgba(102,187,102,0.1)' : `${run.col}${urgency > 0.5 ? '14' : '07'}`;
        const opacity   = (urgency === 0 && !isSAxanPinned) ? '0.35' : '1';
        a.style.cssText = `display:flex;align-items:center;gap:8px;padding:8px 10px;border-radius:7px;cursor:pointer;border:1px solid ${borderCol};background:${bgCol};transition:opacity 0.2s;opacity:${opacity};`;
        a.addEventListener('mouseover', () => { if (urgency > 0 || isSAxanPinned) a.style.opacity = '0.8'; });
        a.addEventListener('mouseout',  () => { a.style.opacity = opacity; });
        a.onclick = function() {
            var FULL_NAMES = {};
            var travelName = FULL_NAMES[run.name] || run.name;
            var onTravelPage = (location.pathname + location.search).indexOf('sid=travel') !== -1;
            if (onTravelPage) {
                clickCountry(travelName);
            } else {
                window.location.href = 'https://www.torn.com/page.php?sid=travel#lt_travel=' + encodeURIComponent(travelName);
            }
        };
 
        const flag = document.createElement('span'); flag.textContent = run.flag; flag.style.cssText = 'font-size:16px;flex-shrink:0;';
 
        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.green : run.col};font-family:Arial,sans-serif;`;
        const timeEl  = document.createElement('span'); timeEl.textContent = run.time; timeEl.style.cssText = `font-size:8.5px;color:${C.textDim};font-family:Consolas,monospace;`;
        nameRow.appendChild(nameEl); nameRow.appendChild(timeEl);
        if (isSAxanPinned) {
            const xanTag = document.createElement('span');
            xanTag.textContent = '🧪 Xanax';
            xanTag.style.cssText = `font-size:7.5px;font-family:Consolas,monospace;font-weight:700;color:${C.green};background:rgba(102,187,102,0.15);border:1px solid rgba(102,187,102,0.45);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:rgba(80,60,0,0.15);border:1px solid rgba(100,80,0,0.18);border-radius:3px;padding:1px 4px;opacity:0.5;`;
            tagWrap.appendChild(tag);
        });
        mid.appendChild(nameRow); mid.appendChild(tagWrap);
 
        const badge = document.createElement('div'); badge.style.cssText = 'flex-shrink:0;text-align:center;';
        if (isSAxanPinned) {
            // Focus mode: show trips needed
            const carry = cfg.getXanCarry() || 1;
            let xanNeeded = 0;
            if (activeRun) {
                const collected = activeRun.trips.reduce((s,t)=>s+t.bought,0);
                xanNeeded = Math.max(0, activeRun.contractQty - collected);
            } else {
                xanNeeded = Math.max(0, xanThreshold - xanPersonal);
            }
            const tripsExact  = xanNeeded / carry;
            const tripsNeeded = Math.ceil(tripsExact);
            const overage     = tripsNeeded > 0 ? (tripsNeeded * carry) - xanNeeded : 0;
            if (xanNeeded <= 0) {
                badge.innerHTML = `<div style="font-size:11px;color:${C.green};font-family:Consolas,monospace;">✓</div>`;
            } else {
                badge.innerHTML = `<div style="font-size:16px;font-weight:700;color:#00c8e0;font-family:Consolas,monospace;line-height:1;">${tripsNeeded}</div>
                    <div style="font-size:7px;color:${C.textDim};font-family:Consolas,monospace;">trips</div>
                    ${overage > 0 ? `<div style="font-size:7px;color:${C.goldDim};font-family:Consolas,monospace;margin-top:1px;">+${overage} over</div>` : ''}`;
            }
        } else if (run.needed > 0) {
            badge.innerHTML = `<div style="font-size:14px;font-weight:700;color:${run.col};font-family:Consolas,monospace;line-height:1;">${run.needed}</div><div style="font-size:7.5px;color:${C.textDim};font-family:Consolas,monospace;">needed</div>`;
        } else {
            badge.innerHTML = `<div style="font-size:11px;color:${C.green};font-family:Consolas,monospace;">✓</div>`;
        }
 
        a.appendChild(flag); a.appendChild(mid); a.appendChild(badge);
        runGrid.appendChild(a);
    });
 
    escSec.appendChild(runGrid);
 
    // ── Items per Country (collapsible) ──
    const carSec = document.createElement('div');

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    const dest      = travelStatus.destination || '';
    const isReturn  = travelStatus.isReturn || !TORN_DEST_TO_CODE[dest];
    const code      = TORN_DEST_TO_CODE[dest];
    const cityData  = code ? yataCityCache[code] : null;
    const secsLeft  = travelStatus.timeLeft || 0;

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

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

    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;
        fromCityName     = code2 ? (YATA_CODE_TO_CITY[code2] || originName) : originName || 'Abroad';
        fromDisplay      = resolved ? resolved.loc : { flag: '✈️', label: originName || 'Abroad' };
        toDisplay        = TORN_LOC;
        toCityName       = 'Torn City';
    } else {
        // Outbound: from Torn City → to foreign city
        fromDisplay    = TORN_LOC;
        fromCityName   = 'Torn City';
        const resolved = resolveLocation(dest);
        toDisplay      = resolved ? resolved.loc : { flag: '✈️', label: dest };
        toCityName     = 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` : 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;';

    const mkCity = (flag, city, label, align) => {
        const d = document.createElement('div');
        d.style.cssText = `flex:1;text-align:${align};`;
        d.innerHTML = `<div style="font-size:22px;line-height:1;margin-bottom:3px;">${flag}</div>
            <div style="font-size:11px;font-weight:700;color:${C.text};font-family:Arial,sans-serif;">${city}</div>
            <div style="font-size:8px;color:${C.textDim};font-family:Consolas,monospace;">${label}</div>`;
        return d;
    };

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        // If activeTab got stranded on the wrong key, redirect it
        if (isTraveling  && activeTab === 'sets') activeTab = 'dest';
        if (!isTraveling && activeTab === 'dest') activeTab = 'sets';

        // Hide Xanax and Travel tabs while traveling — only Dest is shown
        tabsEl.querySelectorAll('[data-tab]').forEach(t => {
            const key      = t.dataset.tab;
            const hide     = isTraveling && (key === 'xanax' || key === 'travel');
            t.style.display = hide ? 'none' : '';
        });

        // Hide the whole tab bar border if only one tab is visible
        tabsEl.style.borderBottom = isTraveling ? 'none' : '';

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

    renderStatusBar();
    if (activeTab === 'dest')   renderDestBody();
    if (activeTab === 'sets')   renderSetsBody();
    if (activeTab === 'xanax')  renderXanaxBody();
    if (activeTab === 'travel') renderTravelBody();
}
 
/* ─────────────────────────────────────────
   BUILD PANEL
───────────────────────────────────────── */
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,100,130,0.4)' : 'rgba(0,160,190,0.45)';
 
    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.setAttribute('style', `padding:8px 12px;background:${C.settHdr};border-bottom:1px solid ${C.border};display:flex;align-items:center;gap:7px;flex-shrink:0;`);
 
    const titleEl = document.createElement('span');
    titleEl.setAttribute('style', `font-size:10.5px;font-weight:700;letter-spacing:1.5px;text-transform:uppercase;color:${C.gold};flex:1;white-space:nowrap;font-family:Arial,sans-serif;`);
    titleEl.innerHTML = '<span class="lt-float" style="display:inline-block;margin-right:4px;">✈</span> Sets Tracker <small style="font-size:7px;font-weight:400;opacity:0.5;letter-spacing:0.5px;vertical-align:middle;">v9.1.9</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:${C.goldDim};transition:color 0.18s;`);
        b.addEventListener('mouseover', () => b.style.color = C.gold);
        b.addEventListener('mouseout',  () => b.style.color = C.goldDim);
        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 16 16" fill="none"><circle cx="8" cy="8" r="2.5" stroke="currentColor" stroke-width="1.4"/><path d="M8 1v2M8 13v2M1 8h2M13 8h2M2.93 2.93l1.41 1.41M11.66 11.66l1.41 1.41M2.93 13.07l1.41-1.41M11.66 4.34l1.41-1.41" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/></svg>';
 
    const btnRefresh  = iconBtn(SVG_REFRESH, 'Refresh data');
    const btnSettings = iconBtn(SVG_GEAR,    'Settings');
 
    btnRefresh.addEventListener('click', () => {
        invCache = {}; abroadCache = {}; xanSACache = { qty:0, price:0 }; xanFacCache = null; xanPersonal = cfg.getXanCount(); yataPriceCache = {}; marketValueCache = {}; yataCityCache = {}; travelStatus = null; // xanPersonal preserved from last scrape
        renderPanel(); refreshAll(); toast('Refreshing…', 1500);
    });
    btnSettings.addEventListener('click', openSettings);
 
    hdr.appendChild(titleEl); hdr.appendChild(btnRefresh); hdr.appendChild(btnSettings);
 
    // ── TABS ──
    const tabs = document.createElement('div');
    tabs.id = 'lt-tabs';
    tabs.setAttribute('style', `display:flex;flex-shrink:0;border-bottom:1px solid ${C.border};`);

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

    // Build tabs with placeholders — renderPanel will update the first tab dynamically
    const tabFirst  = makeTab('🎒 Sets',  'sets');
    tabFirst.id = 'lt-tab-first';
    const tabXanax  = makeTab('🧪 Xanax', 'xanax');
    const tabTravel = makeTab('✈ Travel', 'travel');

    tabFirst.classList.add('lt-tab-active');
    tabFirst.style.color = C.gold; tabFirst.style.borderBottomColor = C.gold; tabFirst.style.background = C.goldGlow;
    tabs.appendChild(tabFirst); tabs.appendChild(tabXanax); tabs.appendChild(tabTravel);
 
    // ── STATUS BAR ──
    const sbar = document.createElement('div');
    sbar.id = 'lt-sbar';
    sbar.setAttribute('style', `padding:3px 10px;font-size:9px;letter-spacing:.4px;color:${C.textDim};border-bottom:1px solid ${C.border};display:flex;gap:8px;align-items:center;flex-shrink:0;font-family:Consolas,monospace;`);
    sbar.textContent = 'Loading…';
 
    // ── BODY ──
    const bdy = document.createElement('div'); bdy.className = 'lt-body';
 
    panelEl.appendChild(hdr);
    panelEl.appendChild(tabs);
    panelEl.appendChild(sbar);
    panelEl.appendChild(bdy);
}
 
/* ─────────────────────────────────────────
   TOGGLE BUTTON
───────────────────────────────────────── */
function buildToggle() {
    const SVG = `<svg width="28" height="28" viewBox="0 0 44 44" fill="none">
        <path d="M6 22 L38 10 L32 22 L38 34 Z" fill="rgba(0,200,224,0.18)" stroke="rgba(0,200,224,0.8)" stroke-width="1.3" stroke-linejoin="round"/>
        <path d="M20 22 L32 22 L38 34 L20 28 Z" fill="rgba(0,200,224,0.12)" stroke="rgba(0,200,224,0.55)" stroke-width="1" stroke-linejoin="round"/>
        <path d="M18 16 L26 12 L28 18 L18 22 Z" fill="rgba(0,200,224,0.15)" stroke="rgba(0,190,220,0.6)" stroke-width="0.9" stroke-linejoin="round"/>
        <circle cx="14" cy="22" r="2" fill="rgba(0,200,224,0.7)"/>
    </svg>`;
 
    toggleEl.innerHTML = SVG;
    toggleEl.title = 'Loot Tracker · Right-click = Settings';
    const toggleBg = isLightMode()
        ? 'radial-gradient(circle at 38% 34%,#d0f8ff,#a8eef8)'
        : 'radial-gradient(circle at 38% 34%,#001a20,#000c10)';
    toggleEl.setAttribute('style',
        'position:fixed !important;width:46px !important;height:46px !important;' +
        `background:${toggleBg} !important;` +
        `border:2px solid ${C.border} !important;` +
        'border-radius:50% !important;' +
        'display:flex !important;align-items:center !important;justify-content:center !important;' +
        'cursor:grab !important;z-index:999990 !important;' +
        `box-shadow:0 0 16px ${C.goldGlow},inset 0 0 12px rgba(0,0,0,0.2) !important;` +
        'user-select:none !important;touch-action:none !important;overflow:visible !important;'
    );
    toggleEl.addEventListener('mouseover', () => {
        toggleEl.style.setProperty('box-shadow', `0 0 24px ${C.gold}aa,inset 0 0 12px rgba(0,0,0,0.2)`, 'important');
        toggleEl.style.setProperty('border-color', C.gold, 'important');
    });
    toggleEl.addEventListener('mouseout', () => {
        toggleEl.style.setProperty('box-shadow', `0 0 16px ${C.goldGlow},inset 0 0 12px rgba(0,0,0,0.2)`, 'important');
        toggleEl.style.setProperty('border-color', C.border, 'important');
    });
}
 
/* ─────────────────────────────────────────
   PANEL OPEN/CLOSE + POSITION
───────────────────────────────────────── */
function positionPanel() {
    const tr = toggleEl.getBoundingClientRect();
    panelEl.style.setProperty('top', Math.min(tr.top, window.innerHeight - 80) + 'px', 'important');
    panelEl.style.setProperty('bottom', 'auto', 'important');
    if (toggleEl._side === 'left') {
        panelEl.style.setProperty('left',  (tr.right + 7) + 'px', 'important');
        panelEl.style.setProperty('right', 'auto', 'important');
    } else {
        panelEl.style.setProperty('right', (window.innerWidth - tr.left + 7) + 'px', 'important');
        panelEl.style.setProperty('left',  'auto', 'important');
    }
}
 
function openPanel()  { panelOpen = true;  panelEl.style.setProperty('width','310px','important'); panelEl.style.setProperty('opacity','1','important'); positionPanel(); renderPanel(); }
function closePanel() { panelOpen = false; panelEl.style.setProperty('width','0','important');     panelEl.style.setProperty('opacity','0','important'); }
 
/* ─────────────────────────────────────────
   DRAG TO SNAP
───────────────────────────────────────── */
function setupDrag() {
    const SZ = 46, EDGE = 6;
 
    function snap(side, top) {
        toggleEl._side = side;
        const t = Math.max(EDGE, Math.min(top, window.innerHeight - SZ - EDGE));
        toggleEl.style.setProperty('top',    t + 'px', 'important');
        toggleEl.style.setProperty('bottom', 'auto', 'important');
        toggleEl.style.setProperty('left',   side === 'left'  ? EDGE + 'px' : 'auto', 'important');
        toggleEl.style.setProperty('right',  side === 'right' ? EDGE + 'px' : 'auto', 'important');
        if (panelOpen) positionPanel();
    }
 
    try {
        const saved = JSON.parse(store.get('lt_pos', '{}'));
        snap(saved.side || 'right', saved.top || 280);
    } catch(e) { snap('right', 280); }
 
    let dragging = false, moved = false, sX, sY, sL, sT;
 
    function start(cx, cy) { moved = false; dragging = true; sX = cx; sY = cy; const r = toggleEl.getBoundingClientRect(); sL = r.left; sT = r.top; toggleEl.style.setProperty('opacity','0.7','important'); toggleEl.style.setProperty('transform','scale(1.1)','important'); }
    function move(cx, cy) {
        if (!dragging) return;
        const dx = cx - sX, dy = cy - sY;
        if (!moved && Math.hypot(dx, dy) < 5) return;
        moved = true;
        toggleEl.style.setProperty('left',   Math.max(EDGE, Math.min(sL + dx, window.innerWidth  - SZ - EDGE)) + 'px', 'important');
        toggleEl.style.setProperty('right',  'auto', 'important');
        toggleEl.style.setProperty('top',    Math.max(EDGE, Math.min(sT + dy, window.innerHeight - SZ - EDGE)) + 'px', 'important');
        toggleEl.style.setProperty('bottom', 'auto', 'important');
        if (panelOpen) positionPanel();
    }
    function end() {
        if (!dragging) return; dragging = false;
        toggleEl.style.setProperty('opacity','1','important'); toggleEl.style.setProperty('transform','none','important');
        if (!moved) return;
        const r    = toggleEl.getBoundingClientRect();
        const side = (r.left + SZ / 2) < window.innerWidth / 2 ? 'left' : 'right';
        snap(side, r.top);
        store.set('lt_pos', JSON.stringify({ side, top: r.top }));
    }
 
    toggleEl.addEventListener('mousedown',  e => { if (e.button !== 0) return; start(e.clientX, e.clientY); });
    document.addEventListener('mousemove',  e => move(e.clientX, e.clientY));
    document.addEventListener('mouseup',    end);
    toggleEl.addEventListener('touchstart', e => { const t = e.touches[0]; start(t.clientX, t.clientY); }, { passive: true });
    toggleEl.addEventListener('touchmove',  e => { if (!dragging) return; e.preventDefault(); const t = e.touches[0]; move(t.clientX, t.clientY); }, { passive: false });
    toggleEl.addEventListener('touchend',   end);
 
    toggleEl.addEventListener('click',       () => { if (moved) return; if (panelOpen) closePanel(); else openPanel(); });
    toggleEl.addEventListener('contextmenu', e => { e.preventDefault(); openSettings(); });
 
    /* ── Carousel Tooltip ── */
    (function buildCarousel() {
        const slides = [
            { icon: '✈️', title: 'Sets Tracker',       body: 'Your overseas sets companion. Track Plushie, Flower, Prehistoric & Special sets, live Xanax data, and plan supply runs — all in one panel.' },
            { icon: '🎒', title: 'Sets Tab',            body: 'Shows every item sorted by bottleneck. Own = extras after completing sets. Abroad = live overseas stock from YATA. When a run or priority is active, all groups collapse to show only Xanax.' },
            { icon: '🟢', title: 'Stock Colours',       body: 'Green = stock above threshold (Plushie ≥2000 / Flower ≥5000). Orange = below threshold. Red = zero stock abroad. 🏪 = Bits n\' Bobs live stock count.' },
            { icon: '🧪', title: 'Xanax Tab',           body: 'Personal count is auto-read when you visit your Items page — no manual entry needed. Shows carry limit, SA live stock & price, and faction Xanax supply. Trip Cost shows SA Price × Carry Limit — click to copy the amount.' },
            { icon: '✈',  title: 'Xanax Runs',          body: 'Start a supply run contract from the Xanax tab. Set a client name, total qty, and sell price. Log each SA trip with one tap — the tracker counts progress, calculates profit vs SA buy price, and tracks payment.' },
            { icon: '📊', title: 'Run Focus Mode',      body: 'When a run is active, Sets and Travel tabs collapse to show only South Africa and Xanax. The SA card shows trips needed (contract ÷ carry limit) with overage. Ends automatically when you tap ✓ End.' },
            { icon: '🎯', title: 'Xanax Priority',      body: 'Enable Xanax Priority in Settings to pin South Africa to the top of Travel and collapse Sets to Xanax only — until your personal count hits your set threshold.' },
            { icon: '✈',  title: 'Travel Tab',          body: 'Loot Run Planner ranks all destinations by items needed. In focus mode only SA shows, with a trips-needed badge. Best Items section shows the top loot per country for quick reference.' },
            { icon: '🔑', title: 'API Key',             body: 'Requires a Full Access key from Torn for faction drug data, and a Limited Access (Display) key for inventory. Both stay on your device.' },
            { icon: '🔄', title: 'Manual Refresh',      body: 'Tap ↺ in the panel header to re-fetch inventory, YATA overseas stock, BoB shop stock, faction Xanax, and points price all at once.' },
            { icon: '📱', title: 'PDA Compatible',      body: 'Built for Torn PDA on mobile. Drag the ✈ button anywhere on screen — it snaps to the nearest edge. Works in browser too.' },
            { icon: '⚙',  title: 'Settings',            body: 'Right-click the ✈ button (or tap ⚙ in the header). Control API key, User ID, section visibility, carry limit, Xanax priority threshold, and tooltip.' },
        ];
 
        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); }, 5000);
 
        // 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 status bar countdown if on dest tab
            if (panelEl && panelOpen && activeTab === 'dest') {
                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 (panelEl) renderPanel();

    if (wasReturn) {
        // Landed back in Torn City
        toast('✅ Landed! Welcome back to Torn City.', 4000);
    } else {
        // Landed in a foreign country — build city + country label
        const code     = landedDest ? TORN_DEST_TO_CODE[landedDest] : null;
        const cityName = code ? (YATA_CODE_TO_CITY[code] || landedDest) : (landedOrig || landedDest || 'your destination');
        const ctryName = landedDest || '';
        const msg      = ctryName && cityName !== ctryName
            ? `✅ Landed! Welcome to ${cityName}, ${ctryName}.`
            : `✅ Landed! Welcome to ${cityName}.`;
        toast(msg, 5000);
    }
}

async function refreshAll() {
    isLoading = true;
    renderStatusBar();
 
    await Promise.allSettled([
        fetchInventory()
            .then(d  => { invCache = d; })
            .catch(() => {}),
        // xanPersonal is scraped from items page via watchItemsPage()
        fetchYataData()
            .then(result => {
                if (result && result.map)  abroadCache = result.map;
                if (result && result.sa)   xanSACache  = result.sa;
                console.log('[SetsTracker] xanSACache set:', JSON.stringify(xanSACache));
            })
            .catch(e => { console.warn('[SetsTracker] YATA assign failed:', e); }),
        fetchBobStock()
            .then(d  => { bobCache = d; })
            .catch(() => {}),
        fetchXanaxFaction()
            .then(d  => { if (d !== null) xanFacCache = d; })
            .catch(() => {}),
        fetchPointsPrice()
            .then(p  => { pointsPrice = p; })
            .catch(() => {}),
        fetchMarketValues()
            .then(d  => { marketValueCache = d; })
            .catch(() => {}),
        fetchTravelStatus()
            .then(s  => { travelStatus = s; scrapeTravelPage(); })
            .catch(() => {}),
    ]);
 
    isLoading = false;
    if (panelEl) renderPanel();
    // Start/stop live countdown based on travel state
    manageTravelCountdown();
}
 
async function mainLoop() {
    await refreshAll();
    pollTimer = setTimeout(mainLoop, 45000);
}
 
/* ─────────────────────────────────────────
   INIT
───────────────────────────────────────── */
 
// Click a country row on the travel page — works in both TornPDA and Tampermonkey
function clickCountry(name) {
    var tLow = name.toLowerCase();
    var doc = (typeof unsafeWindow !== 'undefined' && unsafeWindow.document) ? unsafeWindow.document : document;
    var cells = doc.querySelectorAll('div[class*="flagAndName"]');
    for (var i = 0; i < cells.length; i++) {
        var txt = (cells[i].textContent || '').trim().toLowerCase();
        if (txt.indexOf(tLow) !== -1) {
            var parent = cells[i].parentElement;
            if (parent) { parent.click(); return true; }
        }
    }
    return false;
}
 
function init() {
    xanPersonal = cfg.getXanCount(); // restore last known count from previous scrape
    // 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;
        // Try once after 3s with debug
        // Poll every 300ms for up to 15s
        var _attempts = 0;
        var _poll = setInterval(function() {
            _attempts++;
            if (_attempts > 50) { clearInterval(_poll); return; }
            if (clickCountry(tName)) { clearInterval(_poll); }
        }, 300);
    })();
 
    if (document.getElementById('lt-toggle')) return;
 
    try { injectCSS(); } catch(e) { console.warn('[LT] CSS inject failed:', e); }
 
    // Sync theme before building — computed styles may not be ready at parse time
    syncTheme();
 
    toggleEl = document.createElement('div'); toggleEl.id = 'lt-toggle';
    panelEl  = document.createElement('div'); panelEl.id  = 'lt-panel';
 
    document.body.appendChild(panelEl);
    document.body.appendChild(toggleEl);
 
    buildToggle();
    buildPanel();
 
    // Re-check theme 500ms later — TornPDA applies its theme class async;
    // getComputedStyle returns wrong values at script parse time.
    setTimeout(() => {
        const wasLight = isLightMode();
        syncTheme();
        if (wasLight !== isLightMode()) {
            // Theme actually changed — rebuild toggle and panel with correct palette
            if (toggleEl) buildToggle();
            if (panelEl)  { panelEl.innerHTML = ''; buildPanel(); }
            if (panelOpen) openPanel();
        }
    }, 500);
    setupDrag();
 
    if (!cfg.apiKey) {
        setTimeout(openSettings, 600);
    } else {
        mainLoop();
    }
}
 
if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', () => setTimeout(init, 400));
} else {
    setTimeout(init, 400);
}
 
})();