✈️ Sets Tracker

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

As of 10.03.2026. See апошняя версія.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         ✈️ Sets Tracker
// @namespace    https://osdevscape.com
// @version      6.1.0
// @author       Phillip_J_Fry (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  v6.1.0.0                       ║
// ║                                                              ║
// ║  Author  :  Phillip_J_Fry  (Torn) · OSMays8338 (Greasyfork) ║
// ║  Company :  OSDevscape                                       ║
// ║  License :  All Rights Reserved © 2026 OSDevscape           ║
// ║                                                              ║
// ║  Based on "Points Museum" by SuperNovae [2637223]            ║
// ║  Rebuilt under OSDevscape suite architecture v5              ║
// ╚══════════════════════════════════════════════════════════════╝

(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); },
};

/* ─────────────────────────────────────────
   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.
───────────────────────────────────────── */
const TORN_LIGHT_CLASSES = [
    // Add / remove entries once you've confirmed via DevTools:
    'day-mode',         // most common Torn convention
    't-theme-day',
    'theme-light',
    'light-mode',
    'light',
    'daymode',
];

function isLightMode() {
    const targets = [document.documentElement, document.body];
    for (const el of targets) {
        if (!el) continue;
        for (const cls of TORN_LIGHT_CLASSES) {
            if (el.classList.contains(cls)) return true;
        }
        // data-theme / data-color-scheme attribute fallback
        const dt = el.getAttribute('data-theme') || el.getAttribute('data-color-scheme') || '';
        if (dt === 'light' || dt === 'day') return true;
    }
    return false;
}

/* ─────────────────────────────────────────
   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',
    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:        '#f0fdff',
    bg2:       '#d8f6fc',
    border:    'rgba(0,140,170,0.3)',
    gold:      '#007a8f',       // darkened so it reads on white
    goldDim:   'rgba(0,140,170,0.75)',
    goldGlow:  'rgba(0,170,200,0.10)',
    green:     '#4a7c10',
    greenDim:  'rgba(60,110,15,0.75)',
    text:      '#001a22',
    textDim:   'rgba(0,80,100,0.55)',
    abroad:    '#007a9a',
    stockHi:   '#1a7a00',
    stockMid:  '#8a5500',
    stockLo:   '#bb0000',
    okay:      '#1a7a00',
    mono:      '"Share Tech Mono",Consolas,monospace',
    sans:      'Rajdhani,"Segoe UI",Arial,sans-serif',
    // settings popup extras
    settBg:    '#f0fdff',
    settBorder:'rgba(0,140,170,0.45)',
    settNote:  'rgba(0,170,200,0.15)',
    settHdr:   'rgba(0,160,190,0.15)',
};

// 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 <html> and <body> for class/attribute changes (user switches theme mid-session)
(function watchTheme() {
    const observer = new MutationObserver(() => {
        syncTheme();
        if (panelEl) renderPanel(); // re-render immediately on theme switch
    });
    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: [] };

/* ─────────────────────────────────────────
   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' },
    '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();

/* ─────────────────────────────────────────
   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 = {};   // YATA abroad stock { name: qty }
let xanSACache  = { qty: 0, price: 0 };
let xanFacCache = null;
let pointsPrice = 0;

/* ─────────────────────────────────────────
   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:#e8f0d0;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:#e8f0d0;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 xanCountGroup = el('div'); xanCountGroup.setAttribute('style', imp('margin-bottom:10px;'));
    const xanCountLbl = el('label'); xanCountLbl.textContent = 'Personal Count'; xanCountLbl.setAttribute('for','lt-si-xan-count'); xanCountLbl.setAttribute('style', imp(S.lbl));
    const xanCountRow = el('div'); xanCountRow.setAttribute('style', imp('display:flex;align-items:center;gap:6px;'));
    const xanDecBtn = el('button'); xanDecBtn.textContent = '−'; xanDecBtn.setAttribute('type','button');
    xanDecBtn.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 xanCountInp = document.createElement('input'); xanCountInp.type = 'number'; xanCountInp.min = '0';
    xanCountInp.id = 'lt-si-xan-count'; xanCountInp.value = cfg.getXanCount();
    xanCountInp.setAttribute('style', imp('font-family:Consolas,monospace;font-size:14px;font-weight:700;text-align:center;width:72px;padding:6px 4px;background:#000e14;border:1px solid rgba(0,140,170,0.4);border-radius:5px;color:#e8f0d0;outline:none;-moz-appearance:textfield;'));
    xanCountInp.addEventListener('focus', () => xanCountInp.style.setProperty('border-color','rgba(0,190,215,0.8)','important'));
    xanCountInp.addEventListener('blur',  () => xanCountInp.style.setProperty('border-color','rgba(0,140,170,0.4)','important'));
    const xanIncBtn = el('button'); xanIncBtn.textContent = '+'; xanIncBtn.setAttribute('type','button');
    xanIncBtn.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;'));
    xanDecBtn.addEventListener('click', () => { xanCountInp.value = Math.max(0,(parseInt(xanCountInp.value)||0)-1); });
    xanIncBtn.addEventListener('click', () => { xanCountInp.value = (parseInt(xanCountInp.value)||0)+1; });
    xanCountRow.appendChild(xanDecBtn); xanCountRow.appendChild(xanCountInp); xanCountRow.appendChild(xanIncBtn);
    const xanCountHint = el('div'); xanCountHint.textContent = 'Your current personal Xanax stockpile'; xanCountHint.setAttribute('style', imp(S.hint));
    xanCountGroup.appendChild(xanCountLbl); xanCountGroup.appendChild(xanCountRow); xanCountGroup.appendChild(xanCountHint);

    // Carry Limit 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:#000e14;border:1px solid rgba(0,140,170,0.4);border-radius:5px;color:#e8f0d0;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; });
    const addCarrySettBtn = el('button'); addCarrySettBtn.textContent = '+ Add Carry to Count'; addCarrySettBtn.setAttribute('type','button');
    addCarrySettBtn.setAttribute('style', imp('padding:6px 10px;border-radius:5px;background:rgba(30,50,10,0.5);border:1px solid rgba(139,195,74,0.4);color:#8BC34A;font-weight:700;font-size:10px;cursor:pointer;font-family:Arial,sans-serif;white-space:nowrap;'));
    addCarrySettBtn.addEventListener('click', () => {
        const carry = parseInt(xanCarryInp.value)||0;
        xanCountInp.value = (parseInt(xanCountInp.value)||0) + carry;
        xanCountInp.style.setProperty('border-color','#8BC34A','important');
        setTimeout(() => xanCountInp.style.setProperty('border-color','rgba(0,140,170,0.4)','important'), 600);
    });
    xanCarryRow.appendChild(xanCarDecBtn); xanCarryRow.appendChild(xanCarryInp); xanCarryRow.appendChild(xanCarIncBtn); xanCarryRow.appendChild(addCarrySettBtn);
    const xanCarryHint = el('div'); xanCarryHint.textContent = 'Max Xanax you can carry per trip'; xanCarryHint.setAttribute('style', imp(S.hint));
    xanCarryGroup.appendChild(xanCarryLbl); xanCarryGroup.appendChild(xanCarryRow); xanCarryGroup.appendChild(xanCarryHint);

    // Xanax Priority section
    const xanPriRowWrap = el('div'); xanPriRowWrap.setAttribute('style', imp('margin-bottom:8px;'));
    const xanPriRow = el('div'); xanPriRow.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 xanPriChk = document.createElement('input'); xanPriChk.type = 'checkbox'; xanPriChk.id = 'lt-si-xan-priority'; xanPriChk.checked = cfg.getXanPriority();
    xanPriChk.setAttribute('style', imp('width:14px;height:14px;cursor:pointer;accent-color:#00c8e0;flex-shrink:0;'));
    const xanPriLbl = el('label'); xanPriLbl.textContent = '🚨 Prioritise Xanax run in Travel planner'; xanPriLbl.setAttribute('for','lt-si-xan-priority');
    xanPriLbl.setAttribute('style', imp('font-size:10px;font-weight:600;color:#e8f0d0;cursor:pointer;flex:1;'));
    xanPriRow.appendChild(xanPriChk); xanPriRow.appendChild(xanPriLbl);
    xanPriRow.addEventListener('click', e => { if (e.target !== xanPriChk) xanPriChk.checked = !xanPriChk.checked; updateThresholdVis(); });
    xanPriChk.addEventListener('change', updateThresholdVis);
    xanPriRowWrap.appendChild(xanPriRow);

    // Threshold field — only visible when priority is on
    const xanThreshGroup = el('div'); xanThreshGroup.id = 'lt-xan-thresh-group';
    xanThreshGroup.setAttribute('style', imp('margin-top:6px;padding:8px 10px;background:rgba(0,200,224,0.03);border:1px solid rgba(0,140,170,0.2);border-radius:5px;' + (cfg.getXanPriority() ? '' : 'display:none;')));
    const xanThreshLbl = el('label'); xanThreshLbl.textContent = 'Stop prioritising above'; xanThreshLbl.setAttribute('for','lt-si-xan-thresh'); xanThreshLbl.setAttribute('style', imp(S.lbl));
    const xanThreshRow = el('div'); xanThreshRow.setAttribute('style', imp('display:flex;align-items:center;gap:6px;'));
    const xanThreshInp = document.createElement('input'); xanThreshInp.type = 'number'; xanThreshInp.min = '0';
    xanThreshInp.id = 'lt-si-xan-thresh'; xanThreshInp.value = cfg.getXanThreshold();
    xanThreshInp.setAttribute('style', imp('font-family:Consolas,monospace;font-size:14px;font-weight:700;text-align:center;width:72px;padding:6px 4px;background:#000e14;border:1px solid rgba(0,140,170,0.4);border-radius:5px;color:#e8f0d0;outline:none;-moz-appearance:textfield;'));
    xanThreshInp.addEventListener('focus', () => xanThreshInp.style.setProperty('border-color','rgba(0,190,215,0.8)','important'));
    xanThreshInp.addEventListener('blur',  () => xanThreshInp.style.setProperty('border-color','rgba(0,140,170,0.4)','important'));
    const xanThreshUnit = el('div'); xanThreshUnit.textContent = 'Xanax'; xanThreshUnit.setAttribute('style', imp('font-size:10px;color:rgba(200,220,160,0.55);font-family:Consolas,monospace;'));
    xanThreshRow.appendChild(xanThreshInp); xanThreshRow.appendChild(xanThreshUnit);
    const xanThreshHint = el('div'); xanThreshHint.textContent = 'South Africa stays top-ranked until personal count exceeds this'; xanThreshHint.setAttribute('style', imp(S.hint));
    xanThreshGroup.appendChild(xanThreshLbl); xanThreshGroup.appendChild(xanThreshRow); xanThreshGroup.appendChild(xanThreshHint);

    function updateThresholdVis() {
        const on = document.getElementById('lt-si-xan-priority') && document.getElementById('lt-si-xan-priority').checked;
        xanThreshGroup.style.setProperty('display', on ? 'block' : 'none', 'important');
    }

    // ── 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(prefHdr);
    body.appendChild(tooltipRow);
    body.appendChild(xanSettHdr);
    body.appendChild(xanCountGroup);
    body.appendChild(xanCarryGroup);
    body.appendChild(xanPriRowWrap);
    body.appendChild(xanThreshGroup);
    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 count, carry, priority, threshold
        cfg.setXanCount(Math.max(0, parseInt(document.getElementById('lt-si-xan-count').value)||0));
        cfg.setXanCarry(Math.max(0, parseInt(document.getElementById('lt-si-xan-carry').value)||0));
        cfg.setXanPriority(document.getElementById('lt-si-xan-priority').checked);
        cfg.setXanThreshold(Math.max(0, parseInt(document.getElementById('lt-si-xan-thresh').value)||0));
        // Retrigger tooltip: clear seen key so carousel shows again on next load
        const showTooltip = document.getElementById('lt-pref-tooltip').checked;
        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;
        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 fetchAbroad() {
    const data = await gmFetch('https://yata.yt/api/v1/travel/export/');
    const map = {};
    if (!data) return map;

    let entries = [];
    if (Array.isArray(data))                        entries = data;
    else if (data.stocks && typeof data.stocks === 'object') entries = Object.values(data.stocks);
    else entries = Object.values(data).filter(v => v && typeof v === 'object' && v.items);

    entries.forEach(country => {
        const items = Array.isArray(country?.items)  ? country.items
                    : Array.isArray(country?.stocks) ? country.stocks
                    : [];
        items.forEach(item => {
            const name = ID_TO_NAME[Number(item.id)];
            if (name) map[name] = (map[name] || 0) + Number(item.quantity || 0);
        });
    });
    return map;
}

async function fetchXanaxSA() {
    try {
        const data = await gmFetch('https://yata.yt/api/v1/travel/export/');
        const SA_KEYS = ['sou','saf','zaf','za','south_africa','South Africa'];
        const entries = Array.isArray(data) ? data
            : Array.isArray(data?.exports) ? data.exports
            : Object.entries(data?.stocks || {}).map(([k,v]) => ({ ...v, _key: k }));
        for (const country of entries) {
            const key = country._key || country.country || '';
            if (!SA_KEYS.some(k => key.toLowerCase().includes(k.toLowerCase()))) continue;
            const items = country?.items || country?.stocks || [];
            const hit = items.find(i => Number(i.id) === XANAX_ID);
            if (hit) return { qty: hit.quantity || 0, price: hit.cost || hit.price || 0 };
        }
    } catch(e) {}
    return { qty: 0, price: 0 };
}

async function fetchXanaxFaction() {
    if (!cfg.apiKey) return null;
    try {
        const d = await gmFetch(`https://api.torn.com/v2/faction/items?key=${cfg.apiKey}`);
        if (!d.error) {
            const items = d.items || {};
            const direct = items[String(XANAX_ID)];
            if (direct !== undefined) return direct.quantity || 0;
            let total = 0;
            Object.values(items).forEach(i => { if (i.name === 'Xanax' || Number(i.id) === XANAX_ID) total += (i.quantity || 0); });
            return total;
        }
    } catch(e) {}
    return null;
}

async function fetchPointsPrice() {
    const now = Date.now();
    if (pointsPriceCache.time && now - pointsPriceCache.time < POINTS_CACHE_DUR) return pointsPriceCache.price;
    if (!cfg.apiKey) return 0;
    try {
        const data = await gmFetch(`${POINTS_ENDPOINT}?key=${cfg.apiKey}`);
        if (data.pointsmarket) {
            const listings = Object.values(data.pointsmarket).filter(l => l.quantity > 0).map(l => l.cost).sort((a,b) => a-b);
            if (listings.length) {
                const top = listings.slice(0, Math.min(5, listings.length));
                const avg = Math.round(top.reduce((s,p) => s+p, 0) / top.length);
                pointsPriceCache.history.push(avg);
                if (pointsPriceCache.history.length > POINTS_HIST_SIZE) pointsPriceCache.history.shift();
                const stable = Math.round(pointsPriceCache.history.reduce((s,p) => s+p, 0) / pointsPriceCache.history.length);
                pointsPriceCache = { time: now, price: stable, history: pointsPriceCache.history };
                pointsPrice = stable;
                return stable;
            }
        }
    } catch(e) {}
    return pointsPrice || 0;
}

/* ─────────────────────────────────────────
   CALCULATION HELPERS
───────────────────────────────────────── */
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) {
        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:8px;font-weight:700;color:${C.gold};background:rgba(0,200,224,0.1);border:1px solid rgba(0,200,224,0.35);border-radius:3px;padding:2px 3px;cursor:pointer;white-space:nowrap;text-decoration:none !important;transition:all 0.15s;`;
        abroadEl.textContent = '🏪 BoB';
        abroadEl.title = "Open Bits n' Bobs shop";
        abroadEl.addEventListener('mouseover', () => { abroadEl.style.background = 'rgba(0,200,224,0.22)'; abroadEl.style.borderColor = 'rgba(0,200,224,0.7)'; });
        abroadEl.addEventListener('mouseout',  () => { abroadEl.style.background = 'rgba(0,200,224,0.1)';  abroadEl.style.borderColor = 'rgba(0,200,224,0.35)'; });
    } 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 (YATA): ${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;
}

/* ─────────────────────────────────────────
   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 YATA.\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();

    body.appendChild(makeColHeader());

    // ── GROUPS ──
    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));

        getSortedItems(invCache, g.items, sets).forEach(({ name, data, remaining }) => {
            const isBob = BOB_IDS.has(data.id);
            const abroad = abroadCache[name] || 0;
            body.appendChild(makeItemRow(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));
        Object.entries(SPECIAL_ITEMS).forEach(([name, data]) => {
            const own    = invCache[name] || 0;
            const abroad = abroadCache[name] || 0;
            body.appendChild(makeItemRow(name, data, own, abroad, false));
        });
    }
}

/* ─────────────────────────────────────────
   TAB: XANAX
───────────────────────────────────────── */
function renderXanaxBody() {
    const body = panelEl.querySelector('.lt-body');
    body.innerHTML = '';

    const xanCount = cfg.getXanCount();
    const xanCarry = cfg.getXanCarry();
    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('🧪 Personal Count'));
    const card = document.createElement('div');
    card.style.cssText = `background:rgba(0,16,22,0.6);border:1px solid rgba(0,140,170,0.3);border-radius:8px;padding:12px;display:flex;align-items:center;gap:12px;`;

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

    const countRight = document.createElement('div'); countRight.style.cssText = 'flex:1;';
    const countVal = document.createElement('div');
    countVal.style.cssText = `font-size:36px;font-weight:700;color:${C.gold};font-family:Consolas,monospace;line-height:1;`;
    countVal.textContent = xanCount;
    const countSub = document.createElement('div');
    countSub.style.cssText = `font-size:9px;color:${C.textDim};font-family:Consolas,monospace;margin-top:2px;`;
    countSub.textContent = `Carry limit: ${xanCarry || '—'}`;
    countRight.appendChild(countVal); countRight.appendChild(countSub);

    // Edit shortcut button
    const editBtn = document.createElement('button'); editBtn.type = 'button'; editBtn.textContent = '⚙ Edit';
    editBtn.style.cssText = `padding:5px 10px;border-radius:5px;border:1px solid rgba(0,200,224,0.3);background:rgba(50,40,0,0.5);color:${C.goldDim};font-size:9px;font-weight:700;cursor:pointer;font-family:Arial,sans-serif;flex-shrink:0;`;
    editBtn.title = 'Adjust count & carry in Settings';
    editBtn.addEventListener('click', openSettings);

    card.appendChild(xImg); card.appendChild(countRight); card.appendChild(editBtn);
    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;';
    [
        { label: 'SA Stock',  value: xanSACache.qty,                                                 color: xanSACache.qty > 0 ? C.okay : C.textDim },
        { label: 'SA Price',  value: xanSACache.price > 0 ? '$' + xanSACache.price.toLocaleString() : '—', color: C.gold },
        { label: 'Faction',   value: xanFacCache !== null ? xanFacCache.toLocaleString() : '—',      color: C.green },
        { label: 'Carry Lmt', value: xanCarry || '—',                                                color: C.goldDim },
    ].forEach(item => {
        const c = document.createElement('div');
        c.style.cssText = `background:rgba(0,16,22,0.5);border:1px solid rgba(0,140,170,0.22);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>`;
        infGrid.appendChild(c);
    });
    infSec.appendChild(infGrid);

    wrap.appendChild(cardSec); wrap.appendChild(infSec);
    body.appendChild(wrap);
}

/* ─────────────────────────────────────────
   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;
    }

    // ── 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: 'UK', col: '#cc88ff', time: '~10h',
            loot: ['Heather', 'Nessie Plushie', 'Red Fox Plushie', 'Chert Point'],
        },
        {
            flag: '🇨🇦', name: 'Canada', col: '#ff7070', time: '~9h',
            loot: ['Crocus', 'Wolverine Plushie', 'Quartz Point'],
        },
        {
            flag: '🇿🇦', name: 'South Africa', col: '#60cc60', time: '~16h',
            loot: ['African Violet', 'Lion Plushie', 'Quartzite Point'],
        },
        {
            flag: '🇨🇭', name: 'Switzerland', col: '#ff9999', time: '~11h',
            loot: ['Edelweiss', 'Chamois Plushie'],
        },
        {
            flag: '🇯🇵', name: 'Japan', col: '#ffaacc', time: '~12h',
            loot: ['Cherry Blossom'],
        },
        {
            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;
    });

    // Xanax priority: pin South Africa to top if personal count is below threshold
    const xanPriority   = cfg.getXanPriority();
    const xanThreshold  = cfg.getXanThreshold();
    const xanBelowThresh = xanPriority && cfg.getXanCount() < xanThreshold;

    lootRuns.sort((a, b) => {
        // If SA priority active, force SA to top regardless of needed count
        if (xanBelowThresh) {
            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;';

    // Xanax priority banner
    if (xanBelowThresh) {
        const xanBanner = document.createElement('div');
        xanBanner.style.cssText = `display:flex;align-items:center;gap:8px;padding:7px 10px;border-radius:6px;background:rgba(102,187,102,0.1);border:1px solid rgba(102,187,102,0.4);margin-bottom:2px;`;
        xanBanner.innerHTML = `<span style="font-size:14px;">🧪</span><div style="flex:1;"><div style="font-size:9.5px;font-weight:700;color:${C.green};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;">South Africa pinned — personal count ${cfg.getXanCount()} / ${cfg.getXanThreshold()} threshold</div></div>`;
        runGrid.appendChild(xanBanner);
    }

    lootRuns.forEach(run => {
        if (run.total === 0) return; // all items in this country are from hidden sections — skip
        const urgency = run.needed / run.total;
        const a = document.createElement('div');
        a.style.cssText = `display:flex;align-items:center;gap:8px;padding:8px 10px;border-radius:7px;cursor:pointer;border:1px solid ${run.col}${urgency > 0.5 ? '66' : '28'};background:${run.col}${urgency > 0.5 ? '14' : '07'};transition:opacity 0.2s;opacity:${urgency === 0 ? '0.35' : '1'};`;
        a.addEventListener('mouseover', () => { if (urgency > 0) a.style.opacity = '0.8'; });
        a.addEventListener('mouseout',  () => { if (urgency > 0) a.style.opacity = '1'; });
        a.onclick = function() {
            var FULL_NAMES = { 'UK': 'United Kingdom' };
            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:${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);

        const tagWrap = document.createElement('div'); tagWrap.style.cssText = 'display:flex;flex-wrap:wrap;gap:3px;margin-top:3px;';
        run.loot.forEach(itemName => {
            // 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;
            const isNeeded = 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 (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);

    // ── Best items to carry per country ──
    const carSec = document.createElement('div'); carSec.appendChild(secTitle('🎯 Best Items per Country'));
    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'] },
    ];

    countryGuide.forEach(({ flag, name, items: itemNames }) => {
        // Filter out items from hidden sections
        const visibleItems = itemNames.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; // entire country hidden — skip row

        const row = document.createElement('div');
        row.style.cssText = `display:flex;gap:8px;padding:5px 0;border-bottom:1px solid rgba(0,80,100,0.2);align-items:flex-start;`;

        const flagEl = document.createElement('div');
        flagEl.textContent = flag; flagEl.style.cssText = 'font-size:16px;flex-shrink:0;padding-top:1px;';

        const nameEl = document.createElement('div');
        nameEl.style.cssText = `font-size:10px;font-weight:700;color:${C.text};font-family:Arial,sans-serif;flex-shrink:0;min-width:80px;`;
        nameEl.textContent = name;

        const tagsWrap = document.createElement('div'); tagsWrap.style.cssText = 'display:flex;flex-wrap:wrap;gap:3px;flex:1;';
        visibleItems.forEach(iName => {
            const tag = document.createElement('span');
            tag.style.cssText = `font-size:8.5px;font-family:Consolas,monospace;color:${C.goldDim};background:rgba(120,90,0,0.18);border:1px solid rgba(140,110,0,0.25);border-radius:3px;padding:1px 5px;`;
            tag.textContent = iName;
            tagsWrap.appendChild(tag);
        });

        row.appendChild(flagEl); row.appendChild(nameEl); row.appendChild(tagsWrap);
        carSec.appendChild(row);
    });

    wrap.appendChild(escSec); wrap.appendChild(carSec);
    body.appendChild(wrap);
}

/* ─────────────────────────────────────────
   STATUS BAR
───────────────────────────────────────── */
function renderStatusBar() {
    const bar = panelEl ? panelEl.querySelector('#lt-sbar') : null;
    if (!bar) return;
    bar.innerHTML = '';

    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;`;
        span.textContent = 'Personal: ' + cfg.getXanCount() + ' · Faction: ' + (xanFacCache !== null ? xanFacCache : '—');
        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();
    renderStatusBar();
    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';

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

    const tabSets   = makeTab('🎒 Sets',   'sets');
    const tabXanax  = makeTab('🧪 Xanax',  'xanax');
    const tabTravel = makeTab('✈ Travel',  'travel');

    tabSets.classList.add('lt-tab-active');
    tabSets.style.color = C.gold; tabSets.style.borderBottomColor = C.gold; tabSets.style.background = C.goldGlow;
    tabs.appendChild(tabSets); 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, and Special item sets — plus Xanax and loot run planning.' },
            { icon: '🎒', title: 'Sets Tab',            body: 'Shows every item sorted by bottleneck. "Own" = extras after completing sets. "Abroad" = live YATA overseas stock.' },
            { icon: '🟢', title: 'Stock Colours',       body: 'Green = stock above threshold (Plushie ≥2000 / Flower ≥5000). Orange = below threshold. Red = zero stock abroad.' },
            { icon: '🧪', title: 'Xanax Tab',           body: 'Track your personal Xanax count and carry limit. Use the stepper controls and "+ Add Carry Limit" to update after a run.' },
            { icon: '✈',  title: 'Travel Tab',          body: 'Loot Run Planner ranks destinations by how many items you still need. Highlighted tags = items below 5 surplus.' },
            { icon: '🔑', title: 'API Key',             body: 'Requires a Limited Access (Display) key from Torn. Only display data is read — your key stays on your device.' },
            { icon: '🔄', title: 'Manual Refresh',      body: 'Tap ↺ in the panel header to instantly re-fetch inventory, YATA abroad stock, faction Xanax, and points price.' },
            { icon: '📱', title: 'PDA Compatible',      body: 'Built for Torn PDA on mobile. Drag the ✈ button anywhere on screen — it snaps to the nearest edge automatically.' },
            { icon: '⚙',  title: 'Settings',            body: 'Right-click the ✈ button (or tap ⚙ in the header) to open Settings. Control API key, User ID, and section visibility.' },
        ];

        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
───────────────────────────────────────── */
async function refreshAll() {
    isLoading = true;
    renderStatusBar();

    await Promise.allSettled([
        fetchInventory()
            .then(d  => { invCache = d; })
            .catch(() => {}),
        fetchAbroad()
            .then(d  => { abroadCache = d; })
            .catch(() => {}),
        fetchXanaxSA()
            .then(d  => { xanSACache = d; })
            .catch(() => {}),
        fetchXanaxFaction()
            .then(d  => { if (d !== null) xanFacCache = d; })
            .catch(() => {}),
        fetchPointsPrice()
            .then(p  => { pointsPrice = p; })
            .catch(() => {}),
    ]);

    isLoading = false;
    if (panelEl) renderPanel();
}

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() {
    // 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); }

    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();
    setupDrag();

    if (!cfg.apiKey) {
        setTimeout(openSettings, 600);
    } else {
        mainLoop();
    }
}

if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', () => setTimeout(init, 400));
} else {
    setTimeout(init, 400);
}

})();