SHAYA UI

All-in-One Faction HUD – v27.1: Travel Items Tab vollständig (Xanax/Plushies/Flowers via DroqsDB)

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         SHAYA UI
// @namespace    https://www.torn.com/
// @version      100.0
// @description  All-in-One Faction HUD – v27.1: Travel Items Tab vollständig (Xanax/Plushies/Flowers via DroqsDB)
// @author       xShaYaKaZ
// @match        https://www.torn.com/*
// @grant        GM_xmlhttpRequest
// @grant        GM_setValue
// @grant        GM_getValue
// @connect      api.torn.com
// @connect      ffscouter.com
// @connect      droqsdb.com
// @connect      yata.yt
// ==/UserScript==

(function () {
    'use strict';

    const VERSION = '27.1';
    const SCRIPT_TITLE = "SHAYA UI";
    const API_BASE = 'https://api.torn.com';

    const REFRESH = {
        PERSONAL:     15000,
        FACTION:      30000,
        CHAIN:        5000,
        TICK:         1000,
        NETWORTH:     60000,
        COMPANY:      60000,
        ENEMY:        30000,
        TRAVEL_ITEMS: 120000,
    };

    const TRAVEL_WARN_SEC   = 420;
    const TRAVEL_CRIT_SEC   = 120;
    const CHAIN_WARN_SEC    = 60;
    const CHAIN_START_HIT   = 10;
    const INACTIVE_LIMIT    = 3;
    const SHOPPING_TIME_SEC = 15;
    const HAPPY_JUMP_THRESHOLD = 151;

    const DESTINATIONS = {
        'Mexico':         { flag: '🇲🇽' },
        'Cayman Islands': { flag: '🇰🇾' },
        'Canada':         { flag: '🇨🇦' },
        'Hawaii':         { flag: '🇺🇸' },
        'United Kingdom': { flag: '🇬🇧' },
        'Argentina':      { flag: '🇦🇷' },
        'Switzerland':    { flag: '🇨🇭' },
        'Japan':          { flag: '🇯🇵' },
        'China':          { flag: '🇨🇳' },
        'UAE':            { flag: '🇦🇪' },
        'South Africa':   { flag: '🇿🇦' },
        'Torn':           { flag: '🏠'  },
        'Torn City':      { flag: '🏠'  },
    };

    const DEST_TOP_ITEMS = {
        'Mexico':         ['Jaguar Plushie', 'Dahlia'],
        'United Kingdom': ['Xanax', 'Red Fox Plushie'],
        'Japan':          ['Xanax', 'Cherry Blossom'],
        'South Africa':   ['Lion Plushie', 'African Violet'],
        'Cayman Islands': ['Beer', 'Flower'],
        'Canada':         ['Wine', 'Beer'],
        'Hawaii':         ['Flower', 'Cannabis'],
        'Argentina':      ['Cocaine', 'Cannabis'],
        'Switzerland':    ['Ecstasy', 'Wine'],
        'China':          ['Opium', 'Heroin'],
        'UAE':            ['Cannabis', 'Laptop'],
    };

    const THEMES = {
        obsidian: { name:'⬛ Obsidian', bg:'#0a0a0b', bgCard:'#111114', bgEl:'#18181c', border:'#2a2a30', accent:'#6366f1', accent2:'#818cf8', text:'#f1f5f9', textMuted:'#64748b', textDim:'#94a3b8', green:'#10b981', red:'#ef4444', amber:'#f59e0b', blue:'#3b82f6', chain:'#ef4444', travel:'#6366f1', online:'#10b981' },
        slate:    { name:'🔵 Slate',    bg:'#0f172a', bgCard:'#1e293b', bgEl:'#293548', border:'#334155', accent:'#38bdf8', accent2:'#7dd3fc', text:'#f8fafc', textMuted:'#64748b', textDim:'#94a3b8', green:'#22c55e', red:'#f87171', amber:'#fbbf24', blue:'#38bdf8', chain:'#f87171', travel:'#38bdf8', online:'#22c55e' },
        emerald:  { name:'🟢 Emerald',  bg:'#030a06', bgCard:'#071210', bgEl:'#0d1e17', border:'#1a3a28', accent:'#10b981', accent2:'#34d399', text:'#ecfdf5', textMuted:'#4b7a60', textDim:'#6ee7b7', green:'#10b981', red:'#ef4444', amber:'#f59e0b', blue:'#3b82f6', chain:'#ef4444', travel:'#10b981', online:'#10b981' },
        crimson:  { name:'🔴 Crimson',  bg:'#0a0305', bgCard:'#150509', bgEl:'#1f070c', border:'#3a0d14', accent:'#f43f5e', accent2:'#fb7185', text:'#fff1f2', textMuted:'#7a3040', textDim:'#fca5a5', green:'#22c55e', red:'#f43f5e', amber:'#fb923c', blue:'#60a5fa', chain:'#f43f5e', travel:'#fb923c', online:'#22c55e' },
    };

    // Travel items helpers
    const TRAVEL_ITEM_KEYWORDS = ['xanax', 'plushie', 'flower'];
    const isTravelTarget = (name) => {
        if (!name || typeof name !== 'string') return false;
        const l = name.toLowerCase();
        return TRAVEL_ITEM_KEYWORDS.some(k => l.includes(k));
    };
    const getTiCategory = (name) => {
        if (!name) return 'other';
        const l = name.toLowerCase();
        if (l.includes('xanax'))   return 'xanax';
        if (l.includes('plushie')) return 'plushie';
        if (l.includes('flower'))  return 'flower';
        return 'other';
    };
    const TI_ICONS       = { xanax:'💊', plushie:'🧸', flower:'🌸', other:'📦' };
    const TI_BADGE_CLASS = { xanax:'ti-cat-xanax', plushie:'ti-cat-plushie', flower:'ti-cat-flower' };
    const TI_BADGE_LABEL = { xanax:'Xanax', plushie:'Plüschi', flower:'Blume' };

    // ═══════════════════════════════════════════════════════════════
    // FF SCOUTER INTEGRATION
    // ═══════════════════════════════════════════════════════════════
    const FF_BASE_URL    = 'https://ffscouter.com';
    const FF_CACHE_EXPIRY = 60 * 60 * 1000;

    class FFCache {
        constructor() { this.db=null; this.DB_NAME='shaya-ff-cache'; this.DB_VERSION=1; this.STORE='cache'; }
        async open() {
            if (this.db) return this.db;
            return new Promise((resolve, reject) => {
                const req = window.indexedDB.open(this.DB_NAME, this.DB_VERSION);
                req.onerror = () => { this.db=null; reject(req.error); };
                req.onsuccess = () => { this.db=req.result; resolve(this.db); };
                req.onupgradeneeded = (e) => {
                    const db=e.target.result;
                    const store=db.createObjectStore(this.STORE, { keyPath:'player_id' });
                    store.createIndex('expiry', ['expiry'], { unique:false });
                };
            });
        }
        async get(player_ids) {
            try {
                await this.open();
                const tx=this.db.transaction(this.STORE,'readonly'), store=tx.objectStore(this.STORE);
                const results={}, now=Date.now();
                await Promise.all(player_ids.map(id=>new Promise((res)=>{
                    const req=store.get(Number(id));
                    req.onsuccess=()=>{ if(req.result&&req.result.expiry>now) results[Number(id)]=req.result; res(); };
                    req.onerror=()=>res();
                })));
                return results;
            } catch(e) { return {}; }
        }
        async set(entries) {
            try {
                await this.open();
                const tx=this.db.transaction(this.STORE,'readwrite'), store=tx.objectStore(this.STORE);
                entries.forEach(e=>store.put(e));
                return new Promise((res,rej)=>{ tx.oncomplete=()=>res(); tx.onerror=()=>rej(tx.error); });
            } catch(e) {}
        }
        async clear() {
            try { await this.open(); const tx=this.db.transaction(this.STORE,'readwrite'); tx.objectStore(this.STORE).clear(); } catch(e) {}
        }
    }

    const ffCache = new FFCache();

    function fetchFFStats(playerIds, ffKey, callback) {
        if (!ffKey||!playerIds||!playerIds.length) { callback({}); return; }
        const ids=[...new Set(playerIds)].join(',');
        const url=`${FF_BASE_URL}/api/v1/get-stats?key=${ffKey}&targets=${ids}`;
        GM_xmlhttpRequest({
            method:'GET', url,
            onload:(response)=>{
                if (!response||response.status!==200) { callback({}); return; }
                try {
                    const data=JSON.parse(response.responseText);
                    if (!Array.isArray(data)) { callback({}); return; }
                    const expiry=Date.now()+FF_CACHE_EXPIRY, results={}, toCache=[];
                    data.forEach(r=>{
                        if (!r||!r.player_id) return;
                        const entry={ player_id:Number(r.player_id), expiry, ff:r.fair_fight??null, last_updated:r.last_updated??null, bs_estimate:r.bs_estimate_human??null };
                        toCache.push(entry); results[Number(r.player_id)]=entry;
                    });
                    ffCache.set(toCache); callback(results);
                } catch(e) { callback({}); }
            },
            onerror:()=>callback({}),
        });
    }

    async function getFFData(playerIds, ffKey, callback) {
        if (!ffKey||!playerIds.length) { callback({}); return; }
        const ids=playerIds.map(Number), cached=await ffCache.get(ids);
        const missing=ids.filter(id=>!cached[id]);
        if (!missing.length) { callback(cached); return; }
        fetchFFStats(missing, ffKey, (fresh)=>callback({...cached,...fresh}));
    }

    function ffColor(ff) {
        if (!ff||ff<=0) return '#888';
        let r,g,b;
        if (ff<=1)      { r=0x28; g=0x28; b=0xc6; }
        else if (ff<=3) { const t=(ff-1)/2; r=0x28; g=Math.round(0x28+(0xc6-0x28)*t); b=Math.round(0xc6-(0xc6-0x28)*t); }
        else if (ff<=5) { const t=(ff-3)/2; r=Math.round(0x28+(0xc6-0x28)*t); g=Math.round(0xc6-(0xc6-0x28)*t); b=0x28; }
        else            { r=0xc6; g=0x28; b=0x28; }
        return '#'+[r,g,b].map(v=>v.toString(16).padStart(2,'0')).join('').toUpperCase();
    }

    function ffTextColor(hex) {
        const r=parseInt(hex.slice(1,3),16), g=parseInt(hex.slice(3,5),16), b=parseInt(hex.slice(5,7),16);
        return (r*0.299+g*0.587+b*0.114)>126?'#000':'#fff';
    }

    function ffBadge(entry) {
        if (!entry) return `<span class="ff-badge ff-badge-unknown" title="FF nicht geladen">FF ?</span>`;
        if (!entry.ff) return `<span class="ff-badge ff-badge-none" title="Keine FF Daten">FF —</span>`;
        const ff=entry.ff;
        const now=Math.floor(Date.now()/1000);
        const stale=entry.last_updated&&(now-entry.last_updated)>14*24*3600;
        const bg=ffColor(ff);
        const color=ffTextColor(bg);
        const label=ff.toFixed(2)+(stale?'*':'');
        let title=`FairFight: ${ff.toFixed(2)}`;
        if (stale) title+=' (Daten älter als 14 Tage)';
        if (entry.bs_estimate) title+=` · BS Schätzung: ${entry.bs_estimate}`;
        return `<span class="ff-badge" style="background:${bg};color:${color};border-color:${bg}88;" title="${title}">FF ${label}</span>`;
    }

    function ffValue(entry) { return entry?.ff??-1; }

    // ═══════════════════════════════════════════════════════════════
    // CSS
    // ═══════════════════════════════════════════════════════════════
    const buildCSS = () => `
@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&family=DM+Sans:wght@400;500;600;700&display=swap');
:root { --bg:#0a0a0b; --bg-card:#111114; --bg-el:#18181c; --border:#2a2a30; --accent:#6366f1; --accent2:#818cf8; --text:#f1f5f9; --muted:#64748b; --dim:#94a3b8; --green:#10b981; --red:#ef4444; --amber:#f59e0b; --blue:#3b82f6; --chain:#ef4444; --travel:#6366f1; --online:#10b981; --r:12px; --r-sm:8px; --r-lg:16px; --font:'DM Sans',sans-serif; --mono:'JetBrains Mono',monospace; }
* { box-sizing:border-box; margin:0; padding:0; }
#thud-overlay { position:fixed; z-index:99999; font-family:var(--font); font-size:13px; color:var(--text); background:var(--bg); border:1px solid var(--border); border-radius:20px; box-shadow:0 32px 80px rgba(0,0,0,.95),0 0 0 1px rgba(255,255,255,.03); display:flex; flex-direction:column; overflow:hidden; min-width:300px; min-height:200px; user-select:none; }
#thud-overlay.minimized { min-height:0 !important; height:auto !important; border-radius:12px; }
#thud-overlay.minimized .thud-tabs, #thud-overlay.minimized .thud-body, #thud-overlay.minimized .thud-resize-se, #thud-overlay.minimized .thud-resize-e, #thud-overlay.minimized .thud-resize-s { display:none !important; }
#thud-overlay.minimized .thud-mini { display:flex !important; }
#thud-overlay:not(.minimized) .thud-mini { display:none !important; }
.thud-header { display:flex; align-items:center; gap:8px; padding:10px 14px; background:linear-gradient(180deg,rgba(255,255,255,.025) 0%,transparent 100%); border-bottom:1px solid var(--border); cursor:grab; flex-shrink:0; }
.thud-header:active { cursor:grabbing; }
.thud-header-logo { width:32px; height:32px; flex-shrink:0; display:flex; align-items:center; justify-content:center; filter:drop-shadow(0 2px 8px rgba(239,68,68,.5)); }
.thud-header-logo svg { width:32px; height:32px; }
.thud-header-info { flex:1; min-width:0; }
.thud-header-title { font-size:13px; font-weight:800; color:var(--text); letter-spacing:1px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; text-transform:uppercase; }
.thud-header-sub { font-size:9px; color:var(--muted); font-family:var(--mono); margin-top:1px; }
.thud-header-actions { display:flex; gap:4px; flex-shrink:0; align-items:center; }
.thud-btn { background:var(--bg-el); border:1px solid var(--border); color:var(--dim); border-radius:7px; padding:5px 8px; cursor:pointer; font-size:11px; font-family:var(--font); font-weight:600; transition:all .15s; display:flex; align-items:center; gap:4px; white-space:nowrap; }
.thud-btn:hover { border-color:var(--accent); color:var(--text); background:rgba(99,102,241,.1); }
.thud-btn.active { border-color:var(--green); color:var(--green); background:rgba(16,185,129,.08); }
.thud-btn-close { background:rgba(239,68,68,.12); border:1px solid rgba(239,68,68,.25); color:var(--red); border-radius:7px; padding:5px 8px; cursor:pointer; font-size:12px; font-weight:700; transition:all .15s; display:flex; align-items:center; line-height:1; }
.thud-btn-close:hover { background:rgba(239,68,68,.3); border-color:var(--red); }
.pip { width:6px; height:6px; border-radius:50%; background:var(--green); box-shadow:0 0 6px var(--green); animation:pipPulse 2s ease-in-out infinite; }
@keyframes pipPulse { 0%,100%{opacity:1} 50%{opacity:.3} }
.thud-tabs { display:flex; background:var(--bg); border-bottom:1px solid var(--border); flex-shrink:0; overflow-x:auto; overflow-y:hidden; scrollbar-width:none; gap:2px; padding:5px 6px 0; cursor:default; }
.thud-tabs::-webkit-scrollbar { display:none; }
.thud-tab { display:flex; flex-direction:column; align-items:center; justify-content:center; gap:2px; padding:6px 8px 8px; cursor:pointer; color:var(--muted); border-bottom:2px solid transparent; border-radius:7px 7px 0 0; transition:all .15s; white-space:nowrap; min-width:40px; flex-shrink:0; }
.thud-tab .ti { font-size:13px; line-height:1; }
.thud-tab .tl { font-size:7.5px; font-weight:700; letter-spacing:.7px; text-transform:uppercase; }
.thud-tab:hover { color:var(--dim); background:rgba(255,255,255,.04); }
.thud-tab.active { color:var(--accent2); border-bottom-color:var(--accent); background:rgba(99,102,241,.06); }
.thud-mini { display:none; flex-direction:column; align-items:stretch; }
.thud-mini-bar { display:flex; align-items:center; gap:6px; padding:5px 10px; flex-wrap:nowrap; overflow:hidden; border-top:1px solid var(--border); }
.thud-mini-chip { display:flex; align-items:center; gap:3px; font-size:10px; font-weight:700; flex-shrink:0; background:var(--bg-el); border:1px solid var(--border); border-radius:6px; padding:2px 6px; }
.thud-mini-val { color:var(--text); font-family:var(--mono); font-size:10px; }
.thud-mini-lbl { color:var(--muted); font-size:9px; }
.thud-mini-tabs { display:flex; border-top:1px solid rgba(255,255,255,.04); width:100%; }
.thud-mini-tab { flex:1; padding:4px 2px; text-align:center; font-size:9px; font-weight:700; color:var(--muted); cursor:pointer; border-right:1px solid rgba(255,255,255,.04); letter-spacing:.3px; text-transform:uppercase; transition:all .15s; }
.thud-mini-tab:last-child { border-right:none; }
.thud-mini-tab:hover { color:var(--accent2); background:rgba(99,102,241,.06); }
.thud-body { flex:1 1 0; overflow-y:scroll !important; min-height:0; display:flex; flex-direction:column; gap:10px; padding:12px 14px 14px; pointer-events:auto; scrollbar-width:thin; scrollbar-color:var(--border) transparent; }
.thud-body::-webkit-scrollbar { width:4px; }
.thud-body::-webkit-scrollbar-thumb { background:var(--border); border-radius:2px; }
.card { background:var(--bg-card); border:1px solid var(--border); border-radius:var(--r); overflow:hidden; flex-shrink:0; }
.card-header { display:flex; align-items:center; justify-content:space-between; padding:10px 14px; border-bottom:1px solid rgba(255,255,255,.05); }
.card-title { font-size:10px; font-weight:700; letter-spacing:.8px; text-transform:uppercase; color:var(--dim); }
.card-title.accent { color:var(--accent2); } .card-title.green { color:var(--green); } .card-title.red { color:var(--red); } .card-title.amber { color:var(--amber); } .card-title.blue { color:var(--blue); }
.card-body { padding:12px 14px; display:flex; flex-direction:column; gap:8px; }
.card-body.p0 { padding:0; }
.badge { padding:2px 7px; border-radius:20px; font-size:9px; font-weight:700; letter-spacing:.5px; }
.badge-green { background:rgba(16,185,129,.12); color:var(--green); border:1px solid rgba(16,185,129,.2); }
.badge-red   { background:rgba(239,68,68,.12);  color:var(--red);   border:1px solid rgba(239,68,68,.2); }
.badge-amber { background:rgba(245,158,11,.12); color:var(--amber); border:1px solid rgba(245,158,11,.2); }
.badge-blue  { background:rgba(59,130,246,.12); color:var(--blue);  border:1px solid rgba(59,130,246,.2); }
.badge-muted { background:rgba(255,255,255,.06); color:var(--muted); border:1px solid rgba(255,255,255,.08); }
.row { display:flex; align-items:center; justify-content:space-between; padding:6px 0; border-bottom:1px solid rgba(255,255,255,.04); }
.row:last-child { border-bottom:none; }
.row-label { font-size:12px; color:var(--dim); font-weight:500; }
.row-value { font-size:12px; font-family:var(--mono); font-weight:600; color:var(--text); }
.bar { height:4px; background:rgba(255,255,255,.06); border-radius:3px; overflow:hidden; }
.bar-lg { height:6px; }
.bar-fill { height:100%; border-radius:3px; transition:width .4s; }
.bar-block { display:flex; flex-direction:column; gap:5px; }
.bar-block-head { display:flex; justify-content:space-between; align-items:center; }
.bar-block-lbl { font-size:12px; color:var(--muted); font-weight:600; }
.bar-block-val { font-size:13px; font-family:var(--mono); font-weight:700; }
.bar-block-sub { font-size:10px; color:var(--muted); }
.stat-grid { display:grid; gap:8px; }
.stat-grid-3 { grid-template-columns:repeat(3,1fr); }
.stat-grid-4 { grid-template-columns:repeat(4,1fr); }
.stat-card { background:var(--bg-el); border:1px solid var(--border); border-radius:var(--r-sm); padding:10px 6px; text-align:center; }
.sv { font-size:17px; font-weight:700; font-family:var(--mono); line-height:1; }
.sv-green { color:var(--green); } .sv-red { color:var(--red); } .sv-amber { color:var(--amber); } .sv-blue { color:var(--blue); }
.sl { font-size:9px; color:var(--muted); text-transform:uppercase; letter-spacing:.5px; margin-top:4px; }
.stat-chip { text-align:center; }
.sn { font-size:15px; font-weight:700; font-family:var(--mono); }
.status-summary { display:flex; gap:10px; flex-wrap:wrap; justify-content:center; padding:10px 0 4px; border-top:1px solid rgba(255,255,255,.05); margin-top:6px; }
.ql-grid { display:grid; grid-template-columns:repeat(4,1fr); gap:6px; padding:2px 0 4px; }
.ql-card { background:var(--bg-el); border:1px solid var(--border); border-radius:10px; padding:10px 4px 8px; cursor:pointer; text-align:center; display:flex; flex-direction:column; align-items:center; gap:4px; transition:all .18s; text-decoration:none; }
.ql-card:hover { border-color:var(--accent); background:rgba(99,102,241,.1); transform:translateY(-2px); box-shadow:0 6px 20px rgba(99,102,241,.15); }
.ql-icon { font-size:20px; line-height:1; }
.ql-name { font-size:9px; font-weight:800; color:var(--dim); text-align:center; line-height:1.2; }
.travel-card { border:1px solid rgba(99,102,241,.15); border-radius:var(--r); overflow:hidden; flex-shrink:0; }
.travel-card.home { border-color:rgba(16,185,129,.15); }
.travel-card.warn { border-color:rgba(245,158,11,.3); }
.travel-card.crit { border-color:rgba(239,68,68,.4); animation:travelCritPulse 1s ease-in-out infinite alternate; }
.travel-card.ok   { border-color:rgba(99,102,241,.3); }
@keyframes travelCritPulse { from{box-shadow:none} to{box-shadow:0 0 20px rgba(239,68,68,.3)} }
.travel-card-top { display:flex; align-items:center; justify-content:space-between; padding:8px 14px; font-size:10px; font-weight:700; letter-spacing:.8px; text-transform:uppercase; background:rgba(0,0,0,.15); border-bottom:1px solid rgba(255,255,255,.05); }
.travel-card-body { padding:12px 14px; }
.travel-dest { font-size:18px; font-weight:800; letter-spacing:-.3px; }
.travel-clock { font-size:28px; font-weight:800; font-family:var(--mono); line-height:1; }
.travel-prog { height:3px; background:rgba(255,255,255,.06); border-radius:2px; overflow:hidden; margin-top:10px; }
.travel-prog-fill { height:100%; border-radius:2px; background:var(--travel); transition:width .5s; }
.travel-prog-fill.warn { background:var(--amber); }
.travel-prog-fill.crit { background:var(--red); }
.smart-tipp { background:linear-gradient(135deg,rgba(99,102,241,.09),rgba(129,140,248,.03)); border:1px solid rgba(99,102,241,.25); border-left:3px solid var(--accent); border-radius:var(--r); padding:9px 12px; font-size:12px; color:var(--dim); line-height:1.6; flex-shrink:0; display:flex; align-items:center; gap:8px; }
.smart-tipp-icon { font-size:15px; flex-shrink:0; }
.smart-tipp-text { flex:1; }
.member-grid { display:grid; grid-template-columns:repeat(2,1fr); gap:2px 8px; }
.member-row { display:flex; align-items:center; gap:5px; padding:4px 0; border-bottom:1px solid rgba(255,255,255,.04); }
.member-row:last-child { border-bottom:none; }
.member-name { flex:1; font-size:11px; font-weight:600; color:var(--text); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.member-status { font-size:9px; color:var(--muted); font-weight:600; white-space:nowrap; }
.status-dot { width:6px; height:6px; border-radius:50%; flex-shrink:0; }
.sd-online { background:var(--online); box-shadow:0 0 5px var(--online); }
.sd-idle { background:var(--amber); }
.sd-traveling { background:var(--travel); }
.sd-hospital { background:var(--red); }
.sd-jail { background:#f97316; }
.sd-offline { background:rgba(255,255,255,.12); }
.offline-row { display:flex; align-items:center; gap:8px; padding:6px 14px; border-bottom:1px solid rgba(255,255,255,.04); }
.offline-row:last-child { border-bottom:none; }
.offline-name { flex:1; font-size:11px; color:var(--muted); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.offline-ago { font-size:10px; font-family:var(--mono); font-weight:700; }
.ago-m { color:var(--amber); } .ago-h { color:var(--red); } .ago-d { color:var(--muted); }
.ff-badge { display:inline-flex; align-items:center; justify-content:center; font-size:9px; font-weight:800; padding:2px 6px; border-radius:4px; border:1px solid transparent; letter-spacing:.4px; font-family:var(--mono); cursor:help; white-space:nowrap; line-height:1.4; box-shadow:0 1px 4px rgba(0,0,0,.4); }
.ff-badge-unknown { background:rgba(255,255,255,.06); color:var(--muted); border-color:rgba(255,255,255,.08); }
.ff-badge-none    { background:rgba(255,255,255,.06); color:var(--muted); border-color:rgba(255,255,255,.08); }
.bounty-row { display:flex; align-items:flex-start; gap:10px; padding:10px 14px; border-bottom:1px solid rgba(255,255,255,.04); }
.bounty-row:last-child { border-bottom:none; }
.bounty-row.is-hospital { background:rgba(239,68,68,.06); border-left:3px solid var(--red); }
.bounty-reward { font-family:var(--mono); font-size:12px; font-weight:700; color:var(--amber); white-space:nowrap; }
.bounty-link { display:inline-flex; align-items:center; padding:5px 10px; border-radius:6px; background:rgba(239,68,68,.15); border:1px solid rgba(239,68,68,.25); color:var(--red); font-size:11px; font-weight:700; text-decoration:none; white-space:nowrap; transition:all .15s; }
.bounty-link:hover { background:rgba(239,68,68,.3); }
.bounty-difficulty { padding:2px 6px; border-radius:4px; font-size:9px; font-weight:800; }
.diff-easy   { background:rgba(16,185,129,.15); color:var(--green); }
.diff-medium { background:rgba(245,158,11,.15);  color:var(--amber); }
.diff-hard   { background:rgba(239,68,68,.15);   color:var(--red); }
.sort-bar { display:flex; align-items:center; gap:5px; flex-wrap:wrap; }
.bounty-info-row { display:flex; gap:5px; flex-wrap:wrap; align-items:center; margin-top:4px; }
.bounty-tag { font-size:9px; font-weight:700; padding:1px 5px; border-radius:4px; white-space:nowrap; }
.tag-hosp { background:rgba(239,68,68,.25); color:var(--red); border:1px solid rgba(239,68,68,.5); font-size:10px; padding:2px 7px; border-radius:5px; animation:hospTagPulse 1.5s ease-in-out infinite; }
@keyframes hospTagPulse { 0%,100%{box-shadow:none} 50%{box-shadow:0 0 6px rgba(239,68,68,.5)} }
.tag-jail   { background:rgba(245,158,11,.15); color:var(--amber);  border:1px solid rgba(245,158,11,.2); }
.tag-travel { background:rgba(99,102,241,.15);  color:var(--accent2); border:1px solid rgba(99,102,241,.2); }
.tag-ok     { background:rgba(16,185,129,.10);  color:var(--green);  border:1px solid rgba(16,185,129,.15); }
.bounty-search { width:100%; background:var(--bg); border:1px solid var(--border); border-radius:var(--r-sm); padding:7px 11px; color:var(--text); font-family:var(--font); font-size:12px; outline:none; transition:border-color .15s; }
.bounty-search:focus { border-color:var(--accent); }
.level-filter-row { display:flex; align-items:center; gap:6px; flex-wrap:wrap; }
.level-input { background:var(--bg); border:1px solid var(--border); border-radius:6px; padding:4px 7px; color:var(--accent2); font-family:var(--mono); font-size:11px; width:55px; text-align:center; outline:none; }
.level-input:focus { border-color:var(--accent); }
.reward-input { background:var(--bg); border:1px solid var(--border); border-radius:6px; padding:4px 7px; color:var(--amber); font-family:var(--mono); font-size:11px; width:70px; text-align:center; outline:none; }
.ff-input { background:var(--bg); border:1px solid var(--border); border-radius:6px; padding:4px 7px; color:var(--accent2); font-family:var(--mono); font-size:11px; width:50px; text-align:center; outline:none; }
.ff-input:focus { border-color:var(--accent); }
.bounty-load-more-inline { display:inline-flex; align-items:center; gap:4px; padding:3px 9px; border-radius:6px; background:rgba(99,102,241,.15); border:1px solid rgba(99,102,241,.3); color:var(--accent2); font-size:10px; font-weight:700; cursor:pointer; font-family:var(--font); transition:all .15s; }
.bounty-load-more-inline:hover { background:rgba(99,102,241,.28); }
.company-kpi-grid { display:grid; grid-template-columns:repeat(2,1fr); gap:7px; margin-bottom:8px; }
.company-kpi { background:var(--bg-el); border:1px solid var(--border); border-radius:var(--r-sm); padding:9px 7px; text-align:center; }
.kv { font-size:13px; font-weight:700; font-family:var(--mono); line-height:1; }
.kl { font-size:9px; color:var(--muted); text-transform:uppercase; letter-spacing:.5px; margin-top:4px; }
.company-emp-row { display:flex; align-items:center; gap:8px; padding:7px 14px; border-bottom:1px solid rgba(255,255,255,.04); }
.company-emp-row:last-child { border-bottom:none; }
.company-emp-name { flex:1; font-size:12px; font-weight:600; color:var(--text); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.company-emp-pos { font-size:10px; color:var(--muted); white-space:nowrap; min-width:60px; }
.company-emp-eff { font-size:11px; font-family:var(--mono); font-weight:700; white-space:nowrap; text-align:right; min-width:36px; }
.nw-ticker { background:linear-gradient(135deg,rgba(245,158,11,.07),rgba(251,191,36,.02)); border:1px solid rgba(245,158,11,.15); border-radius:var(--r); padding:16px 18px; flex-shrink:0; }
.nw-main-val { font-size:26px; font-weight:800; font-family:var(--mono); color:var(--amber); }
.nw-change { font-size:11px; font-family:var(--mono); font-weight:700; }
.nw-change.pos { color:var(--green); } .nw-change.neg { color:var(--red); }
.nw-chart-wrap { position:relative; width:100%; }
.nw-chart-svg { width:100%; display:block; overflow:visible; }
.nw-chart-tooltip { position:absolute; background:var(--bg-card); border:1px solid var(--border); border-radius:6px; padding:5px 9px; font-size:10px; font-family:var(--mono); color:var(--text); pointer-events:none; white-space:nowrap; box-shadow:0 4px 16px rgba(0,0,0,.6); display:none; z-index:9; }
.nw-chart-grid-lbl { font-size:8.5px; fill:var(--muted); font-family:var(--mono); }
.settings-section { display:flex; flex-direction:column; gap:0; }
.settings-section-title { font-size:9px; font-weight:700; color:var(--muted); text-transform:uppercase; letter-spacing:.8px; padding:12px 14px 7px; border-top:1px solid rgba(255,255,255,.05); }
.settings-section:first-child .settings-section-title { border-top:none; padding-top:4px; }
.alarm-row-item { display:flex; align-items:center; gap:10px; padding:9px 14px; border-bottom:1px solid rgba(255,255,255,.04); }
.alarm-lbl { font-size:12px; font-weight:600; color:var(--text); }
.alarm-desc { font-size:10px; color:var(--muted); margin-top:1px; }
.toggle { position:relative; display:inline-block; width:34px; height:19px; flex-shrink:0; }
.toggle input { opacity:0; width:0; height:0; }
.toggle-slider { position:absolute; inset:0; background:rgba(255,255,255,.1); border-radius:19px; transition:.2s; }
.toggle-slider::before { content:''; position:absolute; height:13px; width:13px; left:3px; bottom:3px; background:var(--dim); border-radius:50%; transition:.2s; }
.toggle input:checked + .toggle-slider { background:var(--accent); }
.toggle input:checked + .toggle-slider::before { transform:translateX(15px); background:white; }
.theme-grid { display:grid; grid-template-columns:repeat(2,1fr); gap:5px; }
.theme-btn { padding:9px 5px; border-radius:var(--r-sm); border:1px solid var(--border); background:var(--bg-el); font-size:10px; font-weight:600; color:var(--muted); cursor:pointer; font-family:var(--font); transition:all .15s; text-align:center; }
.theme-btn.active,.theme-btn:hover { border-color:var(--accent); color:var(--accent2); background:rgba(99,102,241,.08); }
.vis-grid { display:flex; flex-direction:column; gap:0; }
.vis-row { display:flex; align-items:center; justify-content:space-between; padding:8px 10px; background:rgba(0,0,0,.15); border-radius:var(--r-sm); border:1px solid rgba(255,255,255,.04); }
.vis-lbl { font-size:11px; color:var(--dim); font-weight:500; }
.api-key-row { display:flex; align-items:center; gap:10px; padding:10px 12px; background:rgba(0,0,0,.2); border-radius:var(--r-sm); border:1px solid rgba(255,255,255,.04); }
.api-key-status { flex:1; font-size:12px; font-weight:600; }
.api-key-status.set { color:var(--green); } .api-key-status.unset { color:var(--red); }
.sound-profile-grid { display:grid; grid-template-columns:repeat(3,1fr); gap:5px; }
.sp-btn { padding:9px 5px; border-radius:var(--r-sm); border:1px solid var(--border); background:var(--bg-el); font-size:10px; font-weight:600; color:var(--muted); cursor:pointer; font-family:var(--font); transition:all .15s; text-align:center; }
.sp-btn:hover,.sp-btn.active { border-color:var(--accent); color:var(--accent2); background:rgba(99,102,241,.08); }
.section-header { font-size:9px; font-weight:700; color:var(--muted); text-transform:uppercase; letter-spacing:.8px; padding:3px 0 7px; display:flex; align-items:center; gap:7px; }
.section-header::after { content:''; flex:1; height:1px; background:rgba(255,255,255,.06); }
#thud-toasts { position:fixed; bottom:20px; left:20px; z-index:999998; display:flex; flex-direction:column-reverse; gap:10px; pointer-events:none; width:340px; }
.toast { display:flex; align-items:flex-start; gap:14px; background:var(--bg-card); border-radius:var(--r); padding:16px; pointer-events:all; border:1px solid var(--border); border-left:4px solid var(--green); box-shadow:0 16px 60px rgba(0,0,0,.95); animation:toastIn .25s cubic-bezier(.34,1.56,.64,1); }
.toast.warn   { border-left-color:var(--amber); }
.toast.crit   { border-left-color:var(--red); }
.toast.info   { border-left-color:var(--blue); }
.toast.travel { border-left-color:var(--travel); }
.toast.landed { border-left-color:var(--red); background:rgba(239,68,68,.08); }
.toast.enemy-alert { border-left-color:#ff4400; background:rgba(255,68,0,.12); box-shadow:0 16px 60px rgba(0,0,0,.95),0 0 50px rgba(255,68,0,.5),0 0 0 2px #ff4400; animation:toastIn .25s cubic-bezier(.34,1.56,.64,1), enemyPulse .6s ease-in-out infinite alternate; }
@keyframes enemyPulse { from{box-shadow:0 16px 60px rgba(0,0,0,.95),0 0 30px rgba(255,68,0,.4)} to{box-shadow:0 16px 60px rgba(0,0,0,.95),0 0 70px rgba(255,68,0,.8),0 0 0 2px #ff4400} }
@keyframes toastIn  { from{opacity:0;transform:translateX(-20px) scale(.95)} to{opacity:1;transform:translateX(0) scale(1)} }
@keyframes toastOut { from{opacity:1;transform:translateX(0) scale(1)} to{opacity:0;transform:translateX(-16px) scale(.95)} }
.toast.leaving { animation:toastOut .2s ease forwards; }
.toast-icon { font-size:22px; flex-shrink:0; margin-top:1px; }
.toast-body { flex:1; min-width:0; }
.toast-title { font-size:14px; font-weight:800; color:var(--text); letter-spacing:.3px; }
.toast-msg   { font-size:12px; color:var(--dim); margin-top:4px; line-height:1.5; }
.toast-x { background:transparent; border:1px solid var(--border); color:var(--muted); border-radius:5px; width:20px; height:20px; cursor:pointer; font-size:10px; display:flex; align-items:center; justify-content:center; flex-shrink:0; }
.thud-modal-bg { position:fixed; inset:0; background:rgba(0,0,0,.85); z-index:999999; display:flex; align-items:center; justify-content:center; }
.thud-modal { background:var(--bg-card); border:1px solid var(--border); border-radius:var(--r-lg); padding:26px; width:340px; box-shadow:0 32px 80px rgba(0,0,0,.9); }
.thud-modal h3 { font-size:15px; font-weight:700; color:var(--text); margin-bottom:7px; }
.thud-modal p  { font-size:12px; color:var(--muted); margin-bottom:16px; line-height:1.7; }
.thud-modal input { width:100%; background:var(--bg); border:1px solid var(--border); border-radius:var(--r-sm); padding:10px 12px; color:var(--text); font-family:var(--mono); font-size:12px; outline:none; margin-bottom:11px; }
.thud-modal input:focus { border-color:var(--accent); }
.thud-modal textarea { width:100%; background:var(--bg); border:1px solid var(--border); border-radius:var(--r-sm); padding:10px 12px; color:var(--text); font-family:var(--font); font-size:12px; outline:none; margin-bottom:11px; min-height:70px; resize:vertical; }
.modal-btns { display:flex; gap:8px; justify-content:flex-end; }
.modal-save   { background:var(--green); color:#000; border:none; border-radius:var(--r-sm); padding:9px 18px; font-size:12px; font-weight:700; cursor:pointer; font-family:var(--font); }
.modal-cancel { background:var(--bg-el); color:var(--muted); border:1px solid var(--border); border-radius:var(--r-sm); padding:9px 14px; font-size:12px; font-weight:600; cursor:pointer; font-family:var(--font); }
.thud-resize-se { position:absolute; right:0; bottom:0; width:18px; height:18px; cursor:se-resize; z-index:10; }
.thud-resize-se::after { content:''; position:absolute; right:4px; bottom:4px; width:7px; height:7px; border-right:2px solid var(--muted); border-bottom:2px solid var(--muted); opacity:.2; }
.thud-resize-e { position:absolute; right:-3px; top:20px; bottom:20px; width:8px; cursor:ew-resize; z-index:9; }
.thud-resize-s { position:absolute; left:20px; right:20px; bottom:-3px; height:8px; cursor:ns-resize; z-index:9; }
#thud-fab { position:fixed; z-index:99998; width:36px; height:36px; border-radius:10px; background:linear-gradient(135deg,#1a0505,#2a0a0a); border:1px solid rgba(239,68,68,.3); box-shadow:0 4px 16px rgba(0,0,0,.85); cursor:grab; display:flex; align-items:center; justify-content:center; transition:all .2s; overflow:hidden; }
#thud-fab:active { cursor:grabbing; transform:scale(.93); }
#thud-fab:hover { border-color:rgba(239,68,68,.7); box-shadow:0 4px 20px rgba(239,68,68,.4); }
#thud-fab.on { border-color:rgba(239,68,68,.5); box-shadow:0 4px 20px rgba(239,68,68,.35); }
#thud-fab svg { width:24px; height:24px; filter:drop-shadow(0 0 4px rgba(239,68,68,.8)); }
.chain-block { padding:8px 14px; border-bottom:1px solid var(--border); flex-shrink:0; }
.chain-idle { display:flex; align-items:center; }
.chain-active-row { display:flex; align-items:center; }
.chain-hits { font-family:var(--mono); font-size:18px; font-weight:800; color:var(--chain); }
.chain-hits-unit { font-size:9px; color:var(--muted); font-weight:600; text-transform:uppercase; align-self:flex-end; margin-bottom:2px; }
.chain-divider { width:1px; height:24px; background:rgba(255,255,255,.1); }
.chain-timer { font-family:var(--mono); font-size:16px; font-weight:800; color:var(--green); transition:color .2s; }
.chain-timer.warn { color:var(--chain); animation:chainWarn .5s ease-in-out infinite alternate; }
@keyframes chainWarn { from{opacity:1} to{opacity:.5} }
.chain-mult { font-family:var(--mono); font-size:15px; font-weight:800; color:var(--amber); }
.chain-prog { height:3px; background:rgba(255,255,255,.06); border-radius:2px; overflow:hidden; margin-top:6px; }
.chain-prog-fill { height:100%; border-radius:2px; background:var(--chain); transition:width .3s linear; }
.pulse-dot { width:7px; height:7px; border-radius:50%; background:var(--chain); box-shadow:0 0 8px var(--chain); animation:pipPulse 1s ease-in-out infinite; flex-shrink:0; }
.war-grid { display:grid; grid-template-columns:repeat(4,1fr); gap:7px; }
.war-cell { background:var(--bg-el); border:1px solid var(--border); border-radius:var(--r-sm); padding:12px 5px; text-align:center; }
.war-cell .wv { font-weight:700; font-size:20px; line-height:1; font-family:var(--mono); }
.war-cell .wl { font-size:9px; color:var(--muted); text-transform:uppercase; letter-spacing:.5px; margin-top:5px; }
.enemy-group { margin-bottom:4px; }
.enemy-group-header { display:flex; align-items:center; gap:7px; padding:8px 14px; background:rgba(0,0,0,.2); border-bottom:1px solid rgba(255,255,255,.05); cursor:pointer; user-select:none; }
.enemy-group-header:hover { background:rgba(255,255,255,.03); }
.enemy-group-name { font-size:10px; font-weight:700; letter-spacing:.7px; text-transform:uppercase; color:var(--dim); flex:1; }
.enemy-group-count { font-size:9px; font-family:var(--mono); color:var(--muted); background:var(--bg-el); border:1px solid var(--border); border-radius:10px; padding:1px 6px; }
.enemy-group-arrow { font-size:9px; color:var(--muted); transition:transform .15s; }
.enemy-group-arrow.open { transform:rotate(90deg); }
.enemy-group-body { display:flex; flex-direction:column; }
.enemy-group-add-btn { display:inline-flex; align-items:center; gap:4px; padding:4px 10px; border-radius:6px; background:rgba(99,102,241,.1); border:1px solid rgba(99,102,241,.2); color:var(--accent2); font-size:10px; font-weight:700; cursor:pointer; font-family:var(--font); transition:all .15s; }
.enemy-group-add-btn:hover { background:rgba(99,102,241,.2); }
.enemy-group-del-btn { display:inline-flex; align-items:center; gap:3px; padding:3px 8px; border-radius:5px; background:rgba(239,68,68,.08); border:1px solid rgba(239,68,68,.15); color:var(--red); font-size:9px; font-weight:700; cursor:pointer; font-family:var(--font); transition:all .15s; }
.enemy-group-del-btn:hover { background:rgba(239,68,68,.2); }
.enemy-group-color-dot { width:8px; height:8px; border-radius:50%; flex-shrink:0; }
.enemy-row { padding:12px 14px; border-bottom:1px solid rgba(255,255,255,.05); display:flex; flex-direction:column; gap:6px; }
.enemy-row:last-child { border-bottom:none; }
.enemy-row:hover { background:rgba(255,255,255,.02); }
.enemy-row.in-hospital { background:rgba(239,68,68,.06); border-left:3px solid var(--red); }
.enemy-row.traveling   { background:rgba(99,102,241,.05); border-left:3px solid var(--travel); }
.enemy-top { display:flex; align-items:center; gap:8px; flex-wrap:wrap; }
.enemy-name-link { font-size:13px; font-weight:800; color:var(--text); text-decoration:none; }
.enemy-name-link:hover { color:var(--red); }
.enemy-level { font-size:10px; font-family:var(--mono); font-weight:700; color:var(--amber); }
.enemy-meta { display:flex; gap:6px; flex-wrap:wrap; align-items:center; }
.enemy-tag { font-size:9px; font-weight:700; padding:2px 6px; border-radius:4px; white-space:nowrap; }
.etag-hosp   { background:rgba(239,68,68,.2);  color:var(--red);    border:1px solid rgba(239,68,68,.4); }
.etag-travel { background:rgba(99,102,241,.15); color:var(--accent2); border:1px solid rgba(99,102,241,.3); }
.etag-ok     { background:rgba(16,185,129,.1);  color:var(--green);  border:1px solid rgba(16,185,129,.2); }
.etag-jail   { background:rgba(245,158,11,.1);  color:var(--amber);  border:1px solid rgba(245,158,11,.2); }
.etag-job    { background:rgba(59,130,246,.08); color:var(--blue);   border:1px solid rgba(59,130,246,.15); }
.enemy-note { font-size:10px; color:var(--muted); font-style:italic; padding:4px 8px; background:rgba(0,0,0,.2); border-radius:4px; border-left:2px solid var(--amber); }
.enemy-actions { display:flex; gap:5px; flex-wrap:wrap; }
.enemy-action-btn { display:inline-flex; align-items:center; gap:3px; padding:4px 10px; border-radius:6px; font-size:10px; font-weight:700; text-decoration:none; cursor:pointer; border:none; font-family:var(--font); transition:all .15s; }
.eab-attack  { background:rgba(239,68,68,.15);  border:1px solid rgba(239,68,68,.25);  color:var(--red); }
.eab-attack:hover { background:rgba(239,68,68,.3); }
.eab-profile { background:rgba(255,255,255,.06); border:1px solid rgba(255,255,255,.1); color:var(--dim); }
.eab-profile:hover { border-color:var(--accent); color:var(--accent2); }
.eab-note    { background:rgba(245,158,11,.1);  border:1px solid rgba(245,158,11,.2);  color:var(--amber); }
.eab-note:hover { background:rgba(245,158,11,.2); }
.eab-remove  { background:rgba(255,255,255,.04); border:1px solid rgba(255,255,255,.08); color:var(--muted); }
.eab-remove:hover { background:rgba(239,68,68,.15); border-color:var(--red); color:var(--red); }
.eab-grp { background:rgba(99,102,241,.1); border:1px solid rgba(99,102,241,.2); color:var(--accent2); }
.eab-grp:hover { background:rgba(99,102,241,.25); }
.enemy-add-row { display:flex; gap:6px; align-items:center; }
.enemy-id-input { flex:1; background:var(--bg); border:1px solid var(--border); border-radius:var(--r-sm); padding:7px 11px; color:var(--text); font-family:var(--mono); font-size:12px; outline:none; }
.enemy-id-input:focus { border-color:var(--red); }
.group-color-picker { display:flex; gap:5px; flex-wrap:wrap; margin-top:4px; }
.group-color-swatch { width:18px; height:18px; border-radius:50%; cursor:pointer; border:2px solid transparent; transition:border-color .15s; }
.group-color-swatch.selected { border-color:var(--text); }
.loading-state { display:flex; align-items:center; justify-content:center; padding:40px; color:var(--muted); font-size:12px; letter-spacing:1px; animation:opPulse 1.5s ease-in-out infinite; }
@keyframes opPulse { 0%,100%{opacity:1} 50%{opacity:.3} }
.empty-state { display:flex; flex-direction:column; align-items:center; justify-content:center; padding:36px 18px; color:var(--muted); gap:10px; text-align:center; }
.empty-state .es-icon { font-size:32px; opacity:.2; }
.empty-state .es-text { font-size:12px; letter-spacing:.5px; font-weight:600; }
.empty-state .es-sub  { font-size:11px; opacity:.6; }
.toggle-more { display:inline-flex; align-items:center; gap:5px; padding:5px 11px; border-radius:20px; border:1px solid var(--border); color:var(--muted); cursor:pointer; font-size:10px; font-weight:600; transition:all .15s; margin-top:7px; }
.toggle-more:hover { border-color:var(--accent); color:var(--accent2); }
.ts { font-family:var(--mono); font-size:10px; color:var(--muted); }
.fullscreen-alert { position:fixed; inset:0; background:rgba(2,4,8,.97); display:none; flex-direction:column; align-items:center; justify-content:center; z-index:9999999; }
.fullscreen-alert.active { display:flex; animation:fadeIn .4s ease; }
@keyframes fadeIn { from{opacity:0} to{opacity:1} }
.fs-box { text-align:center; padding:48px; border-radius:20px; max-width:420px; background:rgba(99,102,241,.06); border:2px solid var(--accent); box-shadow:0 0 80px rgba(99,102,241,.3); }
.fs-title { font-size:20px; font-weight:700; letter-spacing:2px; text-transform:uppercase; margin:16px 0; }
.fs-msg   { font-size:15px; color:var(--dim); line-height:1.6; }
.fs-close { margin-top:24px; background:rgba(99,102,241,.2); border:1px solid var(--accent); color:var(--accent2); border-radius:var(--r-sm); padding:12px 28px; font-size:12px; font-weight:700; cursor:pointer; font-family:var(--font); }
.scan-btn { display:inline-flex; align-items:center; gap:5px; padding:7px 14px; border-radius:var(--r-sm); background:rgba(99,102,241,.15); border:1px solid rgba(99,102,241,.3); color:var(--accent2); font-size:11px; font-weight:700; cursor:pointer; font-family:var(--font); transition:all .15s; }
.scan-btn:hover { background:rgba(99,102,241,.3); }
.scan-btn:disabled { opacity:.4; cursor:not-allowed; }
.spinning { animation:spin 1s linear infinite; }
@keyframes spin { to { transform:rotate(360deg); } }
.sort-btn { padding:4px 9px; border-radius:6px; border:1px solid var(--border); background:var(--bg-el); color:var(--muted); font-size:10px; font-weight:700; cursor:pointer; font-family:var(--font); transition:all .15s; }
.sort-btn.active { border-color:var(--accent); color:var(--accent2); background:rgba(99,102,241,.1); }
.threshold-input { background:var(--bg); border:1px solid var(--border); border-radius:5px; padding:3px 7px; color:var(--accent2); font-family:var(--mono); font-size:11px; width:50px; text-align:center; outline:none; }
.threshold-input:focus { border-color:var(--accent); }
.fab-pin-row { display:flex; align-items:center; justify-content:space-between; padding:9px 14px; border-bottom:1px solid rgba(255,255,255,.04); }
.fab-pin-lbl { font-size:12px; font-weight:600; color:var(--text); }
.fab-pin-desc { font-size:10px; color:var(--muted); margin-top:1px; }
.ti-hero { background:linear-gradient(135deg,rgba(99,102,241,.12),rgba(99,102,241,.02)); border:1px solid rgba(99,102,241,.2); border-radius:var(--r); padding:14px 16px; display:flex; align-items:center; gap:12px; flex-shrink:0; }
.ti-hero-icon { font-size:28px; flex-shrink:0; }
.ti-hero-main { flex:1; min-width:0; }
.ti-hero-title { font-size:12px; font-weight:800; color:var(--accent2); letter-spacing:.8px; text-transform:uppercase; }
.ti-hero-sub { font-size:10px; color:var(--muted); margin-top:3px; }
.ti-hero-badge { font-size:9px; font-family:var(--mono); font-weight:700; color:var(--green); background:rgba(16,185,129,.1); border:1px solid rgba(16,185,129,.2); border-radius:4px; padding:2px 7px; flex-shrink:0; }
.ti-item-row { display:flex; align-items:center; gap:10px; padding:10px 14px; border-bottom:1px solid rgba(255,255,255,.04); transition:background .12s; }
.ti-item-row:last-child { border-bottom:none; }
.ti-item-row:hover { background:rgba(255,255,255,.025); }
.ti-item-row.xanax-row { background:rgba(16,185,129,.04); border-left:3px solid rgba(16,185,129,.4); }
.ti-item-row.xanax-row:hover { background:rgba(16,185,129,.07); }
.ti-item-icon { font-size:20px; flex-shrink:0; width:28px; text-align:center; }
.ti-item-info { flex:1; min-width:0; }
.ti-item-name { font-size:12px; font-weight:700; color:var(--text); }
.ti-item-country { font-size:10px; color:var(--muted); margin-top:2px; display:flex; align-items:center; gap:5px; flex-wrap:wrap; }
.ti-item-flag { font-size:14px; }
.ti-profit-block { text-align:right; flex-shrink:0; }
.ti-profit-val { font-family:var(--mono); font-size:13px; font-weight:800; }
.ti-profit-val.pos { color:var(--green); }
.ti-profit-val.neg { color:var(--red); }
.ti-profit-val.neu { color:var(--muted); }
.ti-profit-lbl { font-size:9px; color:var(--muted); margin-top:2px; text-transform:uppercase; letter-spacing:.5px; }
.ti-profit-min { font-size:9px; font-family:var(--mono); color:var(--dim); }
.ti-restock-row { display:flex; align-items:center; gap:10px; padding:9px 14px; border-bottom:1px solid rgba(255,255,255,.04); }
.ti-restock-row:last-child { border-bottom:none; }
.ti-restock-row:hover { background:rgba(255,255,255,.02); }
.ti-restock-name { flex:1; font-size:12px; font-weight:600; color:var(--text); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.ti-restock-time { font-family:var(--mono); font-size:11px; font-weight:700; color:var(--amber); white-space:nowrap; }
.ti-restock-time.soon { color:var(--red); animation:opPulse 1s ease-in-out infinite; }
.ti-category-badge { display:inline-flex; align-items:center; gap:3px; font-size:9px; font-weight:800; padding:2px 7px; border-radius:20px; letter-spacing:.4px; }
.ti-cat-xanax   { background:rgba(16,185,129,.15); color:var(--green);   border:1px solid rgba(16,185,129,.25); }
.ti-cat-plushie { background:rgba(129,140,248,.15); color:var(--accent2); border:1px solid rgba(129,140,248,.25); }
.ti-cat-flower  { background:rgba(251,191,36,.1);   color:var(--amber);  border:1px solid rgba(251,191,36,.2); }
.ti-filter-row { display:flex; gap:5px; flex-wrap:wrap; padding:8px 14px 10px; border-bottom:1px solid rgba(255,255,255,.05); }
.ti-stock-badge { font-family:var(--mono); font-size:9px; color:var(--muted); background:rgba(255,255,255,.05); border:1px solid rgba(255,255,255,.08); border-radius:4px; padding:1px 5px; }
.ti-empty { display:flex; flex-direction:column; align-items:center; justify-content:center; padding:28px; color:var(--muted); gap:7px; text-align:center; }
.ti-empty-icon { font-size:24px; opacity:.2; }
.ti-empty-txt { font-size:11px; font-weight:600; }
.ti-ts { font-family:var(--mono); font-size:9px; color:var(--muted); text-align:center; padding:7px; flex-shrink:0; }
.ti-err { background:rgba(239,68,68,.06); border:1px solid rgba(239,68,68,.15); border-radius:var(--r); padding:10px 14px; font-size:11px; color:var(--red); text-align:center; flex-shrink:0; }
`;

    const SHAYA_LOGO_SVG = `<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><defs><linearGradient id="sg" x1="0%" y1="0%" x2="100%" y2="100%"><stop offset="0%" style="stop-color:#ff2222"/><stop offset="50%" style="stop-color:#cc0000"/><stop offset="100%" style="stop-color:#880000"/></linearGradient></defs><polygon points="62,10 38,10 28,28 52,28 40,48 58,48 30,90 72,90 80,70 56,70 68,50 50,50" fill="url(#sg)"/><polygon points="68,8 74,14 66,18" fill="#ff4444" opacity="0.8"/><polygon points="30,8 24,16 34,14" fill="#dd2222" opacity="0.7"/><polygon points="26,88 20,82 30,80" fill="#ff3333" opacity="0.75"/><polygon points="74,92 80,84 70,86" fill="#cc1111" opacity="0.7"/></svg>`;
    const FAB_LOGO_SVG   = `<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><defs><linearGradient id="fg" x1="0%" y1="0%" x2="100%" y2="100%"><stop offset="0%" style="stop-color:#ff3333"/><stop offset="100%" style="stop-color:#aa0000"/></linearGradient></defs><polygon points="62,10 38,10 28,28 52,28 40,48 58,48 30,90 72,90 80,70 56,70 68,50 50,50" fill="url(#fg)"/><polygon points="68,8 74,14 66,18" fill="#ff5555" opacity="0.9"/><polygon points="30,8 24,16 34,14" fill="#ee2222" opacity="0.8"/><polygon points="26,88 20,82 30,80" fill="#ff4444" opacity="0.8"/><polygon points="74,92 80,84 70,86" fill="#cc1111" opacity="0.75"/></svg>`;

    // ═══════════════════════════════════════════════════════════════
    // UTILITY
    // ═══════════════════════════════════════════════════════════════
    class Utils {
        static fmtMoney(n) { if (!n&&n!==0) return '—'; const abs=Math.abs(n),sign=n<0?'-':''; if(abs>=1e9)return sign+'$'+(abs/1e9).toFixed(2)+'B'; if(abs>=1e6)return sign+'$'+(abs/1e6).toFixed(2)+'M'; if(abs>=1e3)return sign+'$'+(abs/1e3).toFixed(1)+'K'; return sign+'$'+abs.toLocaleString('de-DE'); }
        static fmtStat(n) { if (!n&&n!==0) return '—'; if(n>=1e9)return(n/1e9).toFixed(2)+'B'; if(n>=1e6)return(n/1e6).toFixed(2)+'M'; if(n>=1e3)return(n/1e3).toFixed(1)+'K'; return n.toLocaleString('de-DE'); }
        static fmtTravel(sec) { if(sec<=0)return'—'; const h=Math.floor(sec/3600),m=Math.ceil((sec%3600)/60); return h>0?`${h}h ${m}m`:(m<=0?'< 1m':`${m}m`); }
        static fmtHM(sec) { if(!sec||sec<=0)return'—'; const h=Math.floor(sec/3600),m=Math.floor((sec%3600)/60); if(h>0)return`${h}h ${m}m`; if(m>0)return`${m}m`; return'< 1m'; }
        static fmtDhm(sec) { if(!sec||sec<=0)return'Fertig'; const d=Math.floor(sec/86400),h=Math.floor((sec%86400)/3600),m=Math.floor((sec%3600)/60); if(d>0)return`${d}T ${h}h`; if(h>0)return`${h}h ${m}m`; return`${m}m`; }
        static fmtSec(sec) { if(!sec||sec<=0)return'0s'; const h=Math.floor(sec/3600),m=Math.floor((sec%3600)/60),s=sec%60; if(h>0)return`${h}h ${m}m`; if(m>0)return`${m}m ${s}s`; return`${s}s`; }
        static arrTime(secLeft) { if(!secLeft||secLeft<=0)return'—'; return new Date(Date.now()+secLeft*1000).toLocaleTimeString('de-DE',{hour:'2-digit',minute:'2-digit'}); }
        static tctTime() { return new Date().toLocaleTimeString('de-DE',{hour:'2-digit',minute:'2-digit',timeZone:'UTC'}); }
        static pctBar(pct,color,size='') { const w=Math.min(100,Math.max(0,pct||0)); return `<div class="bar ${size}"><div class="bar-fill" style="width:${w}%;background:${color};"></div></div>`; }
        static escHtml(s) { return String(s||'').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;'); }
        static inactiveAgo(ts) { if(!ts)return{text:'?',cls:'ago-d'}; const s=Math.floor(Date.now()/1000-ts),m=Math.floor(s/60),h=Math.floor(s/3600),d=Math.floor(s/86400); if(s<3600)return{text:m+'m',cls:'ago-m'}; if(h<24)return{text:h+'h',cls:'ago-h'}; return{text:d+'d',cls:'ago-d'}; }
        static now() { return Math.floor(Date.now()/1000); }
        static buildDonut(data,total) {
            const r=28,cx=32,cy=32,circ=2*Math.PI*r; let off=0;
            const segs=data.map(({count,color})=>{ if(!count||!total)return''; const dash=(count/total)*circ; const seg=`<circle cx="${cx}" cy="${cy}" r="${r}" fill="none" stroke="${color}" stroke-width="5" stroke-dasharray="${dash} ${circ-dash}" stroke-dashoffset="${-off}" stroke-linecap="butt"/>`; off+=dash; return seg; }).filter(Boolean);
            if(!segs.length)segs.push(`<circle cx="${cx}" cy="${cy}" r="${r}" fill="none" stroke="#2a2a30" stroke-width="5"/>`);
            return `<svg viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg" style="width:52px;height:52px;transform:rotate(-90deg)"><circle cx="${cx}" cy="${cy}" r="${r}" fill="none" stroke="#18181c" stroke-width="5"/>${segs.join('')}</svg>`;
        }
    }

    // ═══════════════════════════════════════════════════════════════
    // AUDIO
    // ═══════════════════════════════════════════════════════════════
    class AudioManager {
        constructor(store) { this.store=store; this._ctx=null; }
        get ctx() { if(!this._ctx){try{this._ctx=new(window.AudioContext||window.webkitAudioContext)();}catch(e){}}return this._ctx; }
        play(type) {
            if(!this.store.get('sound')&&type!=='enemy')return; if(!this.ctx)return;
            const c=this.ctx,now=c.currentTime,p=this.store.get('soundProfile')||'classic';
            const note=(freq,start,dur,vol,wave='sine')=>{const osc=c.createOscillator(),g=c.createGain();osc.connect(g);g.connect(c.destination);osc.type=wave;osc.frequency.value=freq;g.gain.setValueAtTime(0,now+start);g.gain.linearRampToValueAtTime(vol,now+start+0.03);g.gain.exponentialRampToValueAtTime(0.001,now+start+dur);osc.start(now+start);osc.stop(now+start+dur+0.05);};
            if(type==='enemy'){[0,.1,.2,.3].forEach(o=>note(880,o,.08,.25,'sawtooth'));note(440,.05,.3,.15,'square');return;}
            if(p==='subtle'){if(type==='travel_warn'){note(440,0,.2,.08);note(550,.25,.2,.08);}else if(type==='travel_crit'){[0,.15,.3].forEach(o=>note(660,o,.12,.1));}else if(type==='full'){note(330,0,.3,.08);}else note(400,0,.25,.06);return;}
            if(p==='military'){if(type==='travel_crit'){[0,.12,.24,.36].forEach(o=>note(1400,o,.07,.2,'sawtooth'));}else{note(1000,0,.05,.15,'square');note(800,.08,.05,.1,'square');}return;}
            if(type==='travel_warn'){[0,.22].forEach(o=>note(660,o,.2,.25));}
            else if(type==='travel_crit'){[0,.16,.32].forEach(o=>note(880,o,.13,.18,'square'));}
            else if(type==='landing'){[0,.15,.3].forEach(o=>note(520,o,.15,.2));}
            else if(type==='full'){const osc=c.createOscillator(),g=c.createGain();osc.connect(g);g.connect(c.destination);osc.type='sine';osc.frequency.setValueAtTime(440,now);osc.frequency.linearRampToValueAtTime(880,now+.4);g.gain.setValueAtTime(0,now);g.gain.linearRampToValueAtTime(.25,now+.08);g.gain.linearRampToValueAtTime(0,now+.5);osc.start(now);osc.stop(now+.55);}
            else{note(520,0,.4,.18);}
        }
    }

    // ═══════════════════════════════════════════════════════════════
    // STORE / TOAST / API
    // ═══════════════════════════════════════════════════════════════
    class Store {
        get(key,def) { const v=GM_getValue('thud_'+key,undefined); return v!==undefined?v:def; }
        set(key,val) { GM_setValue('thud_'+key,val); return val; }
    }

    class ToastManager {
        constructor() { this._fired={}; }
        show(icon,title,msg,style='',duration=10000) {
            let c=document.getElementById('thud-toasts');
            if(!c){c=document.createElement('div');c.id='thud-toasts';document.body.appendChild(c);}
            const t=document.createElement('div'); t.className=`toast ${style}`;
            t.innerHTML=`<div class="toast-icon">${icon}</div><div class="toast-body"><div class="toast-title">${title}</div><div class="toast-msg">${msg}</div></div><button class="toast-x">✕</button>`;
            t.querySelector('.toast-x').addEventListener('click',()=>{t.classList.add('leaving');setTimeout(()=>t.remove(),220);});
            c.appendChild(t);
            setTimeout(()=>{if(t.parentNode){t.classList.add('leaving');setTimeout(()=>t.remove(),220);}},duration);
        }
        showLandingTimer(destName) {
            let c=document.getElementById('thud-toasts');
            if(!c){c=document.createElement('div');c.id='thud-toasts';document.body.appendChild(c);}
            const t=document.createElement('div'); t.className='toast landed';
            const topItems=(DEST_TOP_ITEMS[destName]||[]).slice(0,2).join(' · ');
            t.innerHTML=`<div class="toast-icon">🛬</div><div class="toast-body"><div class="toast-title">GELANDET: ${Utils.escHtml(destName)}</div><div class="toast-msg">⏱ <span id="thud-land-timer">15</span>s Einkaufszeit${topItems?`<br>🎯 ${topItems}`:''}</div></div><button class="toast-x">✕</button>`;
            t.querySelector('.toast-x').addEventListener('click',()=>{t.classList.add('leaving');setTimeout(()=>t.remove(),220);});
            c.appendChild(t);
            let rem=SHOPPING_TIME_SEC;
            const iv=setInterval(()=>{rem--;const el=t.querySelector('#thud-land-timer');if(el)el.textContent=rem;if(rem<=0){clearInterval(iv);if(t.parentNode){t.classList.add('leaving');setTimeout(()=>t.remove(),220);}}},1000);
            hud.audio.play('landing');
        }
        fire(key,icon,title,msg,style,audio) { if(this._fired[key])return; this._fired[key]=true; hud.audio.play(audio||'notify'); this.show(icon,title,msg,style); }
        reset(key) { delete this._fired[key]; }
    }

    class TornAPI {
        constructor(store) { this.store=store; }
        get key() { return this.store.get('apiKey',''); }
        call(url,cb) {
            if(!this.key){cb(null,'NO_KEY');return;}
            GM_xmlhttpRequest({method:'GET',url, onload:r=>{try{const d=JSON.parse(r.responseText);if(d.error)cb(null,d.error.code);else cb(d,null);}catch(e){cb(null,'PARSE');}}, onerror:()=>cb(null,'NET')});
        }
        v2(path,cb) { this.call(`${API_BASE}/v2/${path}&key=${this.key}`,cb); }
        userBasicAndProfile(cb) { this.v2('user?selections=basic,profile',cb); }
        userTravel(cb)           { this.v2('user?selections=travel',cb); }
        userBars(cb)             { this.v2('user?selections=bars',cb); }
        userCooldowns(cb)        { this.v2('user?selections=cooldowns',cb); }
        userEducation(cb)        { this.v2('user?selections=education',cb); }
        userBattleStats(cb)      { this.v2('user?selections=battlestats',cb); }
        userWorkStats(cb)        { this.v2('user?selections=workstats',cb); }
        userNetworth(cb)         { this.v2('user?selections=networth',cb); }
        factionBasicMembers(cb)  { this.v2('faction?selections=basic,members,positions,timestamp',cb); }
        factionChain(cb)         { this.v2('faction?selections=chain',cb); }
        companyProfile(cb)       { this.v2('company?selections=profile',cb); }
        companyEmployees(cb)     { this.v2('company?selections=employees',cb); }
        tornBountiesPage(offset,cb) { this.v2(`torn?selections=bounties&offset=${offset}`,cb); }
        playerProfile(playerId,cb) { this.call(`${API_BASE}/v2/user/${playerId}?selections=basic,profile&key=${this.key}`,cb); }
        playerFactionInfo(playerId,cb) { this.call(`${API_BASE}/v2/user/${playerId}?selections=faction&key=${this.key}`,cb); }
        playerSearch(name,cb)     { this.v2(`user?selections=basic,profile&search=${encodeURIComponent(name)}`,cb); }
    }

    // ═══════════════════════════════════════════════════════════════
    // STATE
    // ═══════════════════════════════════════════════════════════════
    const DEFAULT_ENEMY_GROUPS = [
        { id:'default', name:'Allgemein',        color:'#ef4444', collapsed:false },
        { id:'level',   name:'Level Targets',    color:'#f59e0b', collapsed:false },
        { id:'ff',      name:'FairFight Targets', color:'#6366f1', collapsed:false },
        { id:'revenge', name:'Revanche',          color:'#10b981', collapsed:false },
    ];
    const GROUP_COLORS = ['#ef4444','#f59e0b','#6366f1','#10b981','#3b82f6','#f43f5e','#8b5cf6','#06b6d4','#84cc16','#ec4899'];

    class StateManager {
        constructor(store) {
            this.store=store;
            this.userBasic=null; this.userProfile=null; this.userTravel=null;
            this.bars=null; this.cooldowns=null; this.education=null;
            this.battleStats=null; this.workStats=null;
            this.faction={}; this.members={}; this.chain=null; this.chainEndTime=null;
            this.companyProfile=null; this.companyEmployees=[];
            this.networthData=null; this.networthHistory=store.get('nwHistory',[]);
            this.bountyData=null; this.bountyAllData=null; this.bountyLastFetch=0;
            this.bountySort=store.get('bountySort','reward');
            this.bountySearch='';
            this.bountyFilterHosp=store.get('bountyFilterHosp',false);
            this.bountyLevelMin=store.get('bountyLevelMin',1);
            this.bountyLevelMax=store.get('bountyLevelMax',100);
            this.bountyRewardMin=store.get('bountyRewardMin',0);
            this.bountyRewardMax=store.get('bountyRewardMax',0);
            this.bountyFFMin=store.get('bountyFFMin',0);
            this.bountyFFMax=store.get('bountyFFMax',0);
            this.bountyDisplayCount=80; this.bountyLoadedPages=0;
            this.bountyLoading=false; this.bountyScanningAll=false;
            this.searchQuery=''; this.searchResults=[]; this.searchLoading=false;
            this.searchSort=store.get('searchSort','name'); this.searchSortDir=store.get('searchSortDir','asc');
            this.searchFilterStatus=store.get('searchFilterStatus','all');
            this.searchFilterLvMin=store.get('searchFilterLvMin',1); this.searchFilterLvMax=store.get('searchFilterLvMax',100);
            this.recruitJoinCache={};
            this.enemies=store.get('enemies',[]);
            this.enemyGroups=store.get('enemyGroups', JSON.parse(JSON.stringify(DEFAULT_ENEMY_GROUPS)));
            this.enemyData={};
            this.enemyFetchRunning=false;
            this._enemyTravelAlerted=store.get('_enemyTravelAlerted',{});
            this.ffData={};
            this.lastUpdate=null; this.uptimeSec=0;
            this.showAllOnline=false; this.showAllOffline=false;
            this._fsAlertFired=false; this._landingFired=false;
            this._lastTravelSec=0; this._prevTravelDest='';
            this.energyThresholds=store.get('energyThresholds',[50,75,100]);
            this.nerveThresholds=store.get('nerveThresholds',[50,100]);
            this._energyAlerted=store.get('_energyAlerted',{});
            this._nerveAlerted=store.get('_nerveAlerted',{});
            // Travel Items
            this.travelItemsProfits=null;
            this.travelItemsRestocks=null;
            this.travelItemsLoading=false;
            this.travelItemsLastFetch=0;
            this.travelItemsError=null;
            this.travelItemsFilter=store.get('travelItemsFilter','all');
        }
        setBountySort(v) { this.bountySort=v; this.store.set('bountySort',v); }
        setBountyFilterHosp(v) { this.bountyFilterHosp=v; this.store.set('bountyFilterHosp',v); }
        setBountyLevelMin(v) { this.bountyLevelMin=Number(v)||1; this.store.set('bountyLevelMin',this.bountyLevelMin); }
        setBountyLevelMax(v) { this.bountyLevelMax=Number(v)||100; this.store.set('bountyLevelMax',this.bountyLevelMax); }
        setBountyRewardMin(v) { this.bountyRewardMin=Number(v)||0; this.store.set('bountyRewardMin',this.bountyRewardMin); }
        setBountyRewardMax(v) { this.bountyRewardMax=Number(v)||0; this.store.set('bountyRewardMax',this.bountyRewardMax); }
        setBountyFFMin(v) { this.bountyFFMin=Number(v)||0; this.store.set('bountyFFMin',this.bountyFFMin); }
        setBountyFFMax(v) { this.bountyFFMax=Number(v)||0; this.store.set('bountyFFMax',this.bountyFFMax); }
        setTravelItemsFilter(v) { this.travelItemsFilter=v; this.store.set('travelItemsFilter',v); }
        addEnemy(id,name,note,groupId) {
            if (this.enemies.find(e=>String(e.id)===String(id))) return false;
            this.enemies.push({id:String(id),name:name||`#${id}`,note:note||'',groupId:groupId||'default',addedAt:Utils.now()});
            this.store.set('enemies',this.enemies); return true;
        }
        removeEnemy(id) { this.enemies=this.enemies.filter(e=>String(e.id)!==String(id)); delete this.enemyData[id]; this.store.set('enemies',this.enemies); }
        updateEnemyNote(id,note) { const e=this.enemies.find(e=>String(e.id)===String(id)); if(e){e.note=note;this.store.set('enemies',this.enemies);} }
        updateEnemyGroup(id,groupId) { const e=this.enemies.find(e=>String(e.id)===String(id)); if(e){e.groupId=groupId;this.store.set('enemies',this.enemies);} }
        updateEnemyData(id,data) { this.enemyData[String(id)]=data; }
        addEnemyGroup(name,color) {
            const id='grp_'+Date.now();
            this.enemyGroups.push({id,name,color:color||'#6366f1',collapsed:false});
            this.store.set('enemyGroups',this.enemyGroups); return id;
        }
        removeEnemyGroup(id) {
            if (['default'].includes(id)) return;
            this.enemies.forEach(e=>{ if(e.groupId===id) e.groupId='default'; });
            this.enemyGroups=this.enemyGroups.filter(g=>g.id!==id);
            this.store.set('enemyGroups',this.enemyGroups);
            this.store.set('enemies',this.enemies);
        }
        toggleGroupCollapse(id) {
            const g=this.enemyGroups.find(g=>g.id===id); if(g){g.collapsed=!g.collapsed;this.store.set('enemyGroups',this.enemyGroups);}
        }
        get alarms() { return this.store.get('alarms',{}); }
        setAlarm(k,v) { const a=this.alarms; a[k]=v; this.store.set('alarms',a); }
        isAlarm(k) { const d=ALARM_DEFAULTS[k]; return this.alarms[k]!==undefined?this.alarms[k]:(d?d.enabled:false); }
        get visibility() { return this.store.get('visibility',{}); }
        isVisible(k) { const v=this.visibility; return v[k]!==undefined?v[k]:(VISIBILITY_DEFAULTS[k]?VISIBILITY_DEFAULTS[k].enabled:true); }
        setVisible(k,v) { const vis=this.visibility; vis[k]=v; this.store.set('visibility',vis); }
        get travelAlerts() { return this.store.get('travelAlerts',{}); }
        isTravelAlert(k) { const t=this.travelAlerts; return t[k]!==undefined?t[k]:true; }
        setTravelAlert(k,v) { const t=this.travelAlerts; t[k]=v; this.store.set('travelAlerts',t); }
        setEnergyThresholds(arr) { this.energyThresholds=arr; this.store.set('energyThresholds',arr); }
        setNerveThresholds(arr) { this.nerveThresholds=arr; this.store.set('nerveThresholds',arr); }
        _getEnemyBountyReward(id) {
            const data=this.bountyData||this.bountyAllData||[];
            for (const b of data) { const tid=String(b.target_id??b.id??''); if(tid===String(id))return Number(b.reward??b.amount??0); }
            return 0;
        }
    }

    const ALARM_DEFAULTS = {
        travel_warn:    { enabled:true,  label:'✈ Travel Warnung',      desc:'Alarm wenn Ankunft < 7 Min' },
        travel_crit:    { enabled:true,  label:'✈ Travel Kritisch',      desc:'Alarm wenn Ankunft < 2 Min' },
        landing_timer:  { enabled:true,  label:'🛬 Landing Timer',       desc:'15s Countdown bei Landung' },
        energy_full:    { enabled:true,  label:'⚡ Energie Voll',         desc:'Alarm wenn Energie 100%' },
        energy_thresh:  { enabled:true,  label:'⚡ Energie Schwellwert',  desc:'Alarm bei konfigurierten Werten' },
        energy_over151: { enabled:false, label:'⚡ Energie über 151',     desc:'Alarm wenn Energie > 151 (Happy Jump)' },
        nerve_full:     { enabled:true,  label:'🧠 Nerve Voll',           desc:'Alarm wenn Nerve 100%' },
        nerve_thresh:   { enabled:true,  label:'🧠 Nerve Schwellwert',    desc:'Alarm bei konfigurierten Werten' },
        hp_crit:        { enabled:true,  label:'❤ HP unter 50%',         desc:'Toast-Alarm' },
        hospital_free:  { enabled:true,  label:'🏥 Krankenhaus Frei',     desc:'Alarm letzte 60 Sek' },
        jail_free:      { enabled:true,  label:'🚔 Gefängnis Frei',       desc:'Alarm letzte 60 Sek' },
        drug_ready:     { enabled:false, label:'💊 Drogen Bereit',        desc:'Alarm wenn Drug-CD weg' },
        booster_ready:  { enabled:false, label:'🧪 Booster Bereit',       desc:'Alarm wenn Booster-CD weg' },
        medical_ready:  { enabled:false, label:'💉 Medical Bereit',       desc:'Alarm wenn Medical-CD weg' },
        chain_warn:     { enabled:true,  label:'⛓ Chain Warnung',        desc:'Alarm wenn Chain < 1 Min' },
        enemy_travel:   { enabled:true,  label:'🎯 Enemy reist ab',       desc:'Alarm wenn Enemy-Target abreist' },
    };

    const VISIBILITY_DEFAULTS = {
        show_happy:       { enabled:true,  label:'😊 Happy Bar' },
        show_jail:        { enabled:true,  label:'🚔 Gefängnis' },
        show_hospital:    { enabled:true,  label:'🏥 Krankenhaus' },
        show_drug_cd:     { enabled:true,  label:'💊 Drug CD' },
        show_booster_cd:  { enabled:true,  label:'🧪 Booster CD' },
        show_medical_cd:  { enabled:true,  label:'💉 Medical CD' },
        show_education:   { enabled:true,  label:'📚 Lehrgang' },
        show_tip:         { enabled:true,  label:'💡 Smart Tipp' },
        show_quick_links: { enabled:true,  label:'🔗 Quick Links' },
        show_tct_time:    { enabled:true,  label:'🕒 TCT Zeit' },
        show_battle_stats:{ enabled:true,  label:'⚔ Battle Stats (Ich)' },
    };

    const TABS_DEF = [
        { id:'personal',      icon:'👤', label:'Ich'      },
        { id:'faction',       icon:'⚔',  label:'Fraktion' },
        { id:'bounty',        icon:'🎯',  label:'Bounty'   },
        { id:'enemy',         icon:'💀',  label:'Enemy'    },
        { id:'company',       icon:'🏢',  label:'Firma'    },
        { id:'networth',      icon:'💰',  label:'Vermögen' },
        { id:'travel_items',  icon:'🌍',  label:'Items'    },
        { id:'notifications', icon:'🔔',  label:'Alarme'   },
        { id:'settings',      icon:'⚙',   label:'Settings' },
    ];

    // ═══════════════════════════════════════════════════════════════
    // ALARM CHECKER
    // ═══════════════════════════════════════════════════════════════
    class AlarmChecker {
        constructor(state,toast) { this.state=state; this.toast=toast; }
        run() {
            const s=this.state,now=Utils.now(),t=this.toast;
            if(!s.userBasic&&!s.userProfile)return;
            const travel=s.userTravel?.travel||{};
            const tSec=Math.max(0,Number(travel.time_left)||0),dest=travel.destination||'';
            if(s._lastTravelSec>5&&tSec<=5&&s._prevTravelDest&&s._prevTravelDest!=='Torn'&&s._prevTravelDest!=='Torn City'){
                if(!s._landingFired&&s.isAlarm('landing_timer')){s._landingFired=true;t.showLandingTimer(s._prevTravelDest);}
            }
            if(tSec>5){s._landingFired=false;if(dest)s._prevTravelDest=dest;}
            s._lastTravelSec=tSec;
            const flying=tSec>0;
            if(!flying){t.reset('travel_warn');t.reset('travel_crit');s._fsAlertFired=false;}
            else{
                if(tSec>TRAVEL_WARN_SEC)t.reset('travel_warn');
                if(tSec>TRAVEL_CRIT_SEC)t.reset('travel_crit');
                if(s.isAlarm('travel_warn')&&tSec<=TRAVEL_WARN_SEC&&tSec>TRAVEL_CRIT_SEC&&s.isTravelAlert('toast'))
                    t.fire('travel_warn','✈','ANKUNFT BALD',`${dest} — noch ${Utils.fmtHM(tSec)}`,'travel','travel_warn');
                if(s.isAlarm('travel_crit')&&tSec<=TRAVEL_CRIT_SEC){
                    if(s.isTravelAlert('toast'))t.fire('travel_crit','⚠','KRITISCH – KURZ VOR ANKUNFT',`${dest} landet in ${Utils.fmtTravel(tSec)}!`,'crit','travel_crit');
                    if(s.isTravelAlert('fullscreen')&&!s._fsAlertFired){
                        s._fsAlertFired=true;
                        const fs=document.getElementById('thud-fs-alert');
                        if(fs){document.getElementById('thud-fs-msg').innerHTML=`${dest} — Ankunft in ca. 1 Minute!`;fs.classList.add('active');setTimeout(()=>fs.classList.remove('active'),12000);}
                    }
                }
            }
            const energy=s.bars?.energy;
            if(energy){
                const pct=Math.round((energy.current/energy.maximum)*100);
                if(pct<100)t.reset('energy_full');
                if(s.isAlarm('energy_full')&&pct>=100&&energy.current<=HAPPY_JUMP_THRESHOLD)t.fire('energy_full','⚡','ENERGIE VOLL!',`${energy.current}/${energy.maximum} — Gym oder Chains!`,'warn','full');
                if(energy.current>HAPPY_JUMP_THRESHOLD){if(s.isAlarm('energy_over151'))t.fire('energy_over151','⚡',`ENERGIE ÜBER ${HAPPY_JUMP_THRESHOLD}!`,`${energy.current} Energie — Happy Jump Zone!`,'crit','notify');}
                else{t.reset('energy_over151');}
                if(s.isAlarm('energy_thresh')){
                    for(const thresh of(s.energyThresholds||[])){
                        if(thresh>=100)continue;
                        if(pct>=thresh&&!s._energyAlerted[thresh]){s._energyAlerted[thresh]=true;t.show('⚡',`ENERGIE ${thresh}%`,`${energy.current}/${energy.maximum} Energie erreicht!`,'warn',8000);hud.audio.play('notify');}
                        else if(pct<thresh-10){delete s._energyAlerted[thresh];}
                    }
                }
            }
            const nerve=s.bars?.nerve;
            if(nerve){
                const pct=Math.round((nerve.current/nerve.maximum)*100);
                if(pct<100)t.reset('nerve_full');
                if(s.isAlarm('nerve_full')&&pct>=100)t.fire('nerve_full','🧠','NERVE VOLL!',`${nerve.current}/${nerve.maximum} — Crimes abarbeiten!`,'warn','full');
                if(s.isAlarm('nerve_thresh')){
                    for(const thresh of(s.nerveThresholds||[])){
                        if(thresh>=100)continue;
                        if(pct>=thresh&&!s._nerveAlerted[thresh]){s._nerveAlerted[thresh]=true;t.show('🧠',`NERVE ${thresh}%`,`${nerve.current}/${nerve.maximum} Nerve erreicht!`,'info',8000);hud.audio.play('notify');}
                        else if(pct<thresh-10){delete s._nerveAlerted[thresh];}
                    }
                }
            }
            const life=s.bars?.life||s.userProfile?.life||{};
            if(life.maximum){const p=(life.current/life.maximum)*100;if(p>=50)t.reset('hp_crit');if(s.isAlarm('hp_crit')&&p<50)t.fire('hp_crit','❤','HP KRITISCH!',`Nur noch ${Math.round(p)}% HP! Medical nutzen!`,'crit','notify');}
            const statusObj=s.userProfile?.status||{};
            const hospState=(statusObj.state||'').toLowerCase();
            const hospUntil=Number(statusObj.until??0);
            const hospSec=hospState==='hospital'&&hospUntil>now?hospUntil-now:0;
            if(hospSec>60)t.reset('hospital_free');
            if(s.isAlarm('hospital_free')&&hospSec>0&&hospSec<=60)t.fire('hospital_free','🏥','KRANKENHAUS FREI','Du wirst gleich entlassen!','','notify');
            const jailUntil=(statusObj.state||'').toLowerCase()==='jail'?Number(statusObj.until??0):0;
            const jailSec=jailUntil>now?jailUntil-now:0;
            if(jailSec>60)t.reset('jail_free');
            if(s.isAlarm('jail_free')&&jailSec>0&&jailSec<=60)t.fire('jail_free','🚔','GEFÄNGNIS FREI','Du wirst gleich entlassen!','crit','notify');
            const cd=s.cooldowns?.cooldowns||{};
            const _cdVal=(v)=>Number(typeof v==='object'?(v?.cooldown_until??0):(v??0));
            const drugSec=Math.max(0,_cdVal(cd.drug)-now),boostSec=Math.max(0,_cdVal(cd.booster)-now),medSec=Math.max(0,_cdVal(cd.medical)-now);
            if(drugSec>120)t.reset('drug_ready'); if(boostSec>120)t.reset('booster_ready'); if(medSec>120)t.reset('medical_ready');
            if(s.isAlarm('drug_ready')&&_cdVal(cd.drug)>0&&drugSec<=0)t.fire('drug_ready','💊','DROGEN BEREIT','Drug-Cooldown abgelaufen!','info','notify');
            if(s.isAlarm('booster_ready')&&_cdVal(cd.booster)>0&&boostSec<=0)t.fire('booster_ready','🧪','BOOSTER BEREIT','Booster-Cooldown abgelaufen!','info','notify');
            if(s.isAlarm('medical_ready')&&_cdVal(cd.medical)>0&&medSec<=0)t.fire('medical_ready','💉','MEDICAL BEREIT','Cooldown abgelaufen!','info','notify');
            if(s.isAlarm('enemy_travel')){
                for(const enemy of s.enemies){
                    const ed=s.enemyData[enemy.id]; if(!ed)continue;
                    const eStat=(ed.status?.state||'').toLowerCase(),eDest=ed.status?.description||'';
                    const travelKey=`${enemy.id}_${eDest}`;
                    if(eStat==='traveling'&&!s._enemyTravelAlerted[travelKey]){
                        s._enemyTravelAlerted[travelKey]=true;hud.store.set('_enemyTravelAlerted',s._enemyTravelAlerted);
                        hud.audio.play('enemy');t.show('⚔',`⚠ ENEMY REIST AB!`,`${enemy.name} fliegt: ${eDest}`,'enemy-alert',20000);
                    }
                    if(eStat!=='traveling'){Object.keys(s._enemyTravelAlerted).forEach(k=>{if(k.startsWith(enemy.id+'_'))delete s._enemyTravelAlerted[k];});}
                }
            }
        }
    }

    // ═══════════════════════════════════════════════════════════════
    // CHAIN MANAGER
    // ═══════════════════════════════════════════════════════════════
    class ChainManager {
        constructor(state) { this.state=state; this._raf=null; this._lastSec=-1; }
        process(chain) {
            const s=this.state;
            if(chain&&chain.current>0){s.chain=chain;s.chainEndTime=Date.now()+Number(chain.timeout)*1000;this.startTick();}
            else{s.chain=null;s.chainEndTime=null;this.stopTick();}
            this.renderSection();
            if(hud.store.get('minimized',false))hud.renderer.renderMini();
        }
        startTick() { if(!this._raf){ const loop=()=>{ this.tick(); this._raf=requestAnimationFrame(loop); }; this._raf=requestAnimationFrame(loop); } }
        stopTick()  { if(this._raf){cancelAnimationFrame(this._raf);this._raf=null;this._lastSec=-1;} }
        tick() {
            const s=this.state; if(!s.chainEndTime){this.stopTick();return;}
            const remMs=Math.max(0,s.chainEndTime-Date.now());
            const remSec=Math.floor(remMs/1000);
            if(remSec===this._lastSec)return;
            this._lastSec=remSec;
            const m=Math.floor(remSec/60), sec=remSec%60;
            const warn=remMs<=CHAIN_WARN_SEC*1000;
            const el=document.getElementById('thud-chain-timer');
            if(el){
                el.textContent=remMs<=300000?`${m}:${String(sec).padStart(2,'0')}`:`${m}m`;
                warn?el.classList.add('warn'):el.classList.remove('warn');
            }
            const progEl=document.getElementById('thud-chain-prog-fill');
            if(progEl&&s.chain){
                const ms=[10,25,50,100,250,500,1000,2500,5000,10000,25000,50000,100000];
                const nM=ms.find(x=>x>s.chain.current)||s.chain.current,pM=ms[ms.indexOf(nM)-1]||0;
                const prog=((s.chain.current-pM)/(nM-pM))*100;
                progEl.style.width=Math.max(1,Math.min(100,prog))+'%';
            }
            const miniEl=document.querySelector('.thud-mini-bar .thud-mini-chip .thud-mini-lbl');
            if(miniEl&&s.chain?.current>0){
                miniEl.textContent=remMs<=300000?`${m}:${String(sec).padStart(2,'0')}`:`${m}m`;
            }
            if(warn&&s.isAlarm('chain_warn'))hud.toasts.fire('chain_warn','⛓','CHAIN LÄUFT AB!',`Nur noch ${m}m ${sec}s!`,'crit','travel_crit');
            if(!warn)hud.toasts.reset('chain_warn');
            if(remMs===0){s.chain=null;s.chainEndTime=null;this.stopTick();this.renderSection();}
        }
        renderSection() {
            const el=document.getElementById('thud-chain-block'); if(!el)return;
            const c=this.state.chain;
            if(!c||c.current===0){
                el.className='chain-block';
                el.innerHTML=`<div class="chain-idle"><div class="pulse-dot" style="opacity:.2;animation:none;background:var(--muted)"></div><span style="letter-spacing:.5px;font-size:10px;font-weight:600;margin-left:8px;">Keine aktive Chain</span></div>`;
                return;
            }
            const ms=[10,25,50,100,250,500,1000,2500,5000,10000,25000,50000,100000];
            const nM=ms.find(x=>x>c.current)||c.current,pM=ms[ms.indexOf(nM)-1]||0;
            const prog=((c.current-pM)/(nM-pM))*100;
            const mt=[[10,1],[25,1.5],[50,2],[100,2.5],[250,3],[500,3.5],[1000,4]];
            let mul=1; for(const[thresh,v]of mt)if(c.current>=thresh)mul=v;
            const rem=Math.max(0,this.state.chainEndTime-Date.now());
            const totalSec=Math.floor(rem/1000),m=Math.floor(totalSec/60),sec=totalSec%60;
            const warn=rem<=CHAIN_WARN_SEC*1000;
            const isWU=c.current<CHAIN_START_HIT,hits=isWU?c.current:c.current-CHAIN_START_HIT;
            const timerTxt=rem<=300000?`${m}:${String(sec).padStart(2,'0')}`:`${m}m`;
            el.className='chain-block active';
            el.innerHTML=`<div class="chain-active-row">
                <div class="pulse-dot"></div>
                <span style="font-size:9px;font-weight:700;letter-spacing:1px;color:var(--chain);margin-left:5px;">${isWU?'WARMUP':'CHAIN'}</span>
                <div class="chain-hits" style="margin-left:7px;">${hits.toLocaleString()}</div>
                <div class="chain-hits-unit" style="margin-left:2px;">hits</div>
                <div class="chain-divider" style="margin:0 10px;"></div>
                <div style="display:flex;flex-direction:column;align-items:center;">
                    <div class="chain-timer ${warn?'warn':''}" id="thud-chain-timer">${timerTxt}</div>
                    <div style="font-size:7px;color:var(--muted);text-transform:uppercase;margin-top:1px;">timeout</div>
                </div>
                <div style="display:flex;flex-direction:column;align-items:center;margin-left:10px;">
                    <div class="chain-mult">×${mul}</div>
                    <div style="font-size:7px;color:var(--muted);text-transform:uppercase;margin-top:1px;">mult</div>
                </div>
            </div>
            <div class="chain-prog"><div class="chain-prog-fill" id="thud-chain-prog-fill" style="width:${Math.max(1,Math.min(100,prog))}%"></div></div>`;
        }
    }

    // ═══════════════════════════════════════════════════════════════
    // FACTION HELPER
    // ═══════════════════════════════════════════════════════════════
    class FactionHelper {
        static getStatus(member) {
            const state=(typeof member.status==='object'?member.status?.state:member.status||'').toLowerCase();
            const lastStatus=(member.last_action?.status||'').toLowerCase();
            if(state.includes('hospital'))return'hospital';
            if(state.includes('travel')||state.includes('abroad'))return'traveling';
            if(state.includes('jail'))return'jail';
            if(lastStatus==='idle')return'idle';
            if(lastStatus==='online')return'online';
            const a=Date.now()/1000-(member.last_action?.timestamp||0);
            if(a<120)return'online'; if(a<600)return'idle'; return'offline';
        }
        static statusOrder(s) { return{online:0,traveling:1,idle:2,hospital:3,jail:4,offline:5}[s]??6; }
        static statusLabel(s) { return{online:'Online',idle:'AFK',traveling:'Travel',hospital:'Hosp.',jail:'Jail',offline:'Offline'}[s]||s; }
        static parseTravelRoute(member) {
            const s=typeof member.status==='object'?member.status:{};
            const desc=(s.description||s.state||'').trim();
            const toM=desc.match(/to\s+([A-Za-z\s]+?)(?:\s*(?:from|$)|\.|,)/i);
            const frM=desc.match(/from\s+([A-Za-z\s]+?)(?:\s*(?:to|$)|\.|,)/i);
            let to='Abroad',fr='Torn City';
            const norm=(str)=>{const l=str.trim().replace(/[.,;:!?]+$/,'');return l.toLowerCase().includes('torn')?'Torn City':l.length>2?l:'Abroad';};
            if(toM?.[1])to=norm(toM[1]);
            if(frM?.[1])fr=frM[1].trim().toLowerCase().includes('torn')?'Torn City':frM[1].trim();
            return{from:fr,to,dir:to==='Torn City'&&fr!=='Torn City'?'home':'abroad'};
        }
    }

    // ═══════════════════════════════════════════════════════════════
    // DATA FETCHER
    // ═══════════════════════════════════════════════════════════════
    class DataFetcher {
        constructor(api,state,toast,renderer) { this.api=api; this.state=state; this.toast=toast; this.renderer=renderer; }
        _opt(fetchFn,cb) { fetchFn((d,err)=>{ if(err===7||err===16)return; if(d)cb(d); }); }
        _afterPersonal() { setTimeout(()=>{ this.state.lastUpdate=new Date(); hud.alarmChecker.run(); if(hud.activeTab==='personal')this.renderer.render(); },600); }

        fetchPersonal() {
            if(!this.api.key){this.renderer.render();return;}
            this.api.userBasicAndProfile(d=>{if(d&&(d.profile||d.basic)){this.state.userBasic=d.profile||d.basic||d;this.state.userProfile=d.profile||d;this._afterPersonal();}});
            this.api.userTravel(d=>{if(d)this.state.userTravel=d;});
            this._opt(cb=>this.api.userBars(cb),d=>{this.state.bars=d?.bars||d||null;});
            this._opt(cb=>this.api.userCooldowns(cb),d=>{this.state.cooldowns=d||null;});
            this._opt(cb=>this.api.userEducation(cb),d=>{
                const edu=d?.education||d||null;
                if(edu&&typeof edu==='object'){const until=edu.current?.until??edu.until??edu.ends??0;this.state.education={until,raw:edu};}
                else this.state.education=edu;
            });
            this.api.userBattleStats((d,err)=>{
                if(err||!d)return;
                const _bv=(v)=>{if(!v&&v!==0)return 0;if(typeof v==='number')return v;if(typeof v==='object')return Number(v.modifier_value??v.value??v.current??0);return Number(v)||0;};
                let raw=null;
                if(d.battlestats&&typeof d.battlestats==='object')raw=d.battlestats;
                else if(d.battle_stats&&typeof d.battle_stats==='object')raw=d.battle_stats;
                else if('strength'in d||'defense'in d)raw=d;
                if(raw)this.state.battleStats={strength:_bv(raw.strength),defense:_bv(raw.defense),speed:_bv(raw.speed),dexterity:_bv(raw.dexterity)};
            });
            this._opt(cb=>this.api.userWorkStats(cb),d=>{
                if(d)this.state.workStats={manual:d.manual_labor??d.workstats?.manual_labor??0,intelligence:d.intelligence??d.workstats?.intelligence??0,endurance:d.endurance??d.workstats?.endurance??0};
            });
        }

        fetchFaction() {
            if(!this.api.key)return;
            this.api.factionBasicMembers(data=>{
                if(!data)return;
                const members=data.members||[];
                if(Array.isArray(members)){this.state.members={};members.forEach(m=>{this.state.members[m.id]=m;});}
                else this.state.members=members;
                this.state.faction={...this.state.faction,...(data.faction||data)};
                const el=document.getElementById('thud-faction-name');
                const n=this.state.faction.name||this.state.faction.faction?.name;
                if(el&&n)el.textContent=`${SCRIPT_TITLE} — ${n.toUpperCase()}`;
                this.fetchRecruitJoinTimestamps();
                if(hud.activeTab==='faction')this.renderer.render();
                if(hud.store.get('minimized',false))this.renderer.renderMini();
            });
        }
        fetchRecruitJoinTimestamps() {
            const recruits=Object.values(this.state.members).filter(m=>(m.position||m.rank||'').toLowerCase()==='recruit');
            if(!recruits.length)return;
            recruits.forEach(m=>{
                const cached=this.state.recruitJoinCache[m.id];
                if(cached&&cached.ts&&(Utils.now()-cached.fetchedAt)<3600)return;
                this.api.playerFactionInfo(m.id,(data)=>{
                    if(!data)return;
                    const fac=data.faction||data;
                    const joinTs=Number(fac.faction_join_date??fac.join_date??fac.joined??0);
                    const daysIn=Number(fac.days_in_faction??0);
                    const effectiveTs = joinTs > 0 ? joinTs : (daysIn > 0 ? (Utils.now() - daysIn*86400) : 0);
                    if(effectiveTs>0){
                        this.state.recruitJoinCache[m.id]={
                            ts: effectiveTs,
                            exact: joinTs > 0,
                            fetchedAt: Utils.now()
                        };
                        if(hud.activeTab==='faction'&&this.state._factionSubTab==='details')this.renderer.render();
                    }
                });
            });
        }
        fetchChain() { if(!this.api.key)return; this.api.factionChain(data=>{if(data)hud.chainManager.process(data.chain||null);}); }
        fetchCompany() {
            if(!this.api.key)return;
            this.api.companyProfile((data,err)=>{if(err===7||err===16)return;if(data)this.state.companyProfile=data.profile||data.company||data;});
            this.api.companyEmployees((data,err)=>{
                if(err===7||err===16)return;
                if(data){const empl=data.employees||[];this.state.companyEmployees=Array.isArray(empl)?empl:Object.values(empl);}
                if(hud.activeTab==='company')this.renderer.render();
            });
        }
        fetchNetworth() {
            if(!this.api.key)return;
            this.api.userNetworth((data,err)=>{
                if(err===7||err===16||!data)return;
                const nw=data.networth||null; if(!nw)return;
                const liquid=Number(nw.wallet??nw.cash??0)+Number(nw.stockmarket??nw.stocks??0);
                const now=Utils.now(),hist=this.state.networthHistory;
                hist.push({ts:now,val:liquid});if(hist.length>120)hist.shift();
                this.state.networthHistory=hist;hud.store.set('nwHistory',hist);
                this.state.networthData=nw;
                if(hud.activeTab==='networth')this.renderer.render();
            });
        }
        fetchBountiesPage(offset,onDone) { this.api.tornBountiesPage(offset,data=>{const list=data?.bounties;const arr=Array.isArray(list)?list:(list?Object.values(list):[]);onDone(arr);}); }
        fetchBountiesInitial(onDone) {
            const s=this.state; s.bountyLoading=true; s.bountyAllData=[]; s.bountyLoadedPages=0;
            this.fetchBountiesPage(0,arr=>{s.bountyAllData=this._dedup(arr);s.bountyLoadedPages=1;s.bountyData=s.bountyAllData;s.bountyLastFetch=Utils.now();s.bountyLoading=false;onDone(s.bountyAllData);});
        }
        fetchBountiesNextPage(onDone) {
            const s=this.state; if(s.bountyLoading)return; s.bountyLoading=true;
            this.fetchBountiesPage(s.bountyLoadedPages*100,arr=>{s.bountyLoading=false;if(!arr.length){onDone(false);return;}s.bountyAllData=this._dedup([...(s.bountyAllData||[]),...arr]);s.bountyLoadedPages++;s.bountyData=s.bountyAllData;onDone(true);});
        }
        fetchBountiesAllPages(onProgress,onDone) {
            const s=this.state; if(s.bountyScanningAll)return; s.bountyScanningAll=true; s.bountyLoading=true; s.bountyAllData=[]; s.bountyLoadedPages=0;
            const MAX=20;
            const go=(offset)=>{
                if(s.bountyLoadedPages>=MAX){s.bountyScanningAll=false;s.bountyLoading=false;s.bountyData=s.bountyAllData;onDone(s.bountyAllData);return;}
                this.fetchBountiesPage(offset,arr=>{if(!arr.length){s.bountyScanningAll=false;s.bountyLoading=false;s.bountyData=s.bountyAllData;onDone(s.bountyAllData);return;}s.bountyAllData=this._dedup([...s.bountyAllData,...arr]);s.bountyLoadedPages++;s.bountyLastFetch=Utils.now();if(onProgress)onProgress(s.bountyAllData.length,s.bountyLoadedPages);go(s.bountyLoadedPages*100);});
            };
            go(0);
        }
        _dedup(arr) { const seen=new Map(); for(const b of arr){const id=b.target_id??b.id??Math.random();if(!seen.has(id))seen.set(id,b);}return Array.from(seen.values()); }

        fetchEnemyData() {
            const s=this.state;
            if(s.enemyFetchRunning||!s.enemies.length||!this.api.key)return;
            s.enemyFetchRunning=true;
            let pending=s.enemies.length;
            const done=()=>{ pending--; if(pending<=0){s.enemyFetchRunning=false;if(hud.activeTab==='enemy')hud.renderer.render();} };
            s.enemies.forEach(enemy=>{
                this.api.playerProfile(enemy.id,(data,err)=>{
                    if(data){
                        const basic=data.basic||{},profile=data.profile||{};
                        const merged=Object.assign({},data,basic,profile);
                        if(!merged.age&&merged.registration_timestamp)merged.age=Math.floor((Utils.now()-merged.registration_timestamp)/86400);
                        if(!merged.age&&profile.age)merged.age=profile.age;
                        if(!merged.age&&basic.age)merged.age=basic.age;
                        const fac=merged.faction||profile.faction||basic.faction||null;
                        if(fac){const facId=Number(fac.faction_id??fac.id??0);merged._factionId=facId>0?facId:null;merged._factionName=facId>0?(fac.faction_name??fac.name??''):null;}
                        else{merged._factionId=null;merged._factionName=null;}
                        merged._bountyReward=s._getEnemyBountyReward(enemy.id);
                        s.updateEnemyData(enemy.id,merged);
                        const liveName=merged.name??merged.player_name;
                        if(liveName&&enemy.name!==liveName){enemy.name=liveName;hud.store.set('enemies',s.enemies);}
                    }
                    done();
                });
            });
            const ffKey=hud.store.get('ffKey','');
            if(ffKey&&s.enemies.length){
                const ids=s.enemies.map(e=>Number(e.id));
                getFFData(ids,ffKey,(ffResults)=>{
                    s.ffData={...s.ffData,...ffResults};
                    if(hud.activeTab==='enemy')hud.renderer.render();
                });
            }
        }

        // ─── Travel Items via YATA + Torn API ─────────────────────────
        fetchTravelItems(onDone) {
            const s=this.state;
            if(s.travelItemsLoading)return;
            s.travelItemsLoading=true;
            s.travelItemsError=null;
            s.travelItemsDebug='';
            if(hud.activeTab==='travel_items')hud.renderer.render();

            const tryFinish=()=>{
                s.travelItemsLoading=false;
                s.travelItemsLastFetch=Utils.now();
                if(hud.activeTab==='travel_items')hud.renderer.render();
                if(onDone)onDone();
            };

            // YATA export: {stocks:{mex:[{id,quantity,cost},...], ...}, timestamp:...}
            // Items with quantity>0 are in stock. Items missing = quantity 0.
            const COUNTRY_MAP={mex:'Mexico',cay:'Cayman Islands',can:'Canada',haw:'Hawaii',uni:'United Kingdom',arg:'Argentina',swi:'Switzerland',jap:'Japan',chi:'China',uae:'UAE',sou:'South Africa'};

            // Step 1: Fetch YATA stocks
            GM_xmlhttpRequest({
                method:'GET',
                url:'https://yata.yt/api/v1/travel/export/?key=',
                onload:(res)=>{
                    try{
                        if(!res||res.status!==200)throw new Error('YATA HTTP '+res.status);
                        const raw=JSON.parse(res.responseText);
                        const stocks=raw.stocks||{};

                        // Collect all items that have stock
                        const stockedItems=[]; // {id, quantity, cost, country}
                        const outItems=[];     // {id, country} - for restock
                        const now=Utils.now();

                        for(const [code, arr] of Object.entries(stocks)){
                            const country=COUNTRY_MAP[code]||code;
                            if(!Array.isArray(arr))continue;
                            for(const item of arr){
                                const id=String(item.id||'?');
                                const qty=Number(item.quantity||0);
                                const cost=Number(item.cost||0);
                                if(qty>0&&cost>0){
                                    stockedItems.push({id,qty,cost,country});
                                }
                            }
                        }

                        if(!stockedItems.length){
                            // Debug: show raw structure sample
                            const firstCountry=Object.keys(stocks)[0];
                            const firstArr=stocks[firstCountry];
                            s.travelItemsDebug='Alle qty=0 | sample ['+firstCountry+'][0]: '+JSON.stringify(Array.isArray(firstArr)?firstArr[0]:firstArr).substring(0,100);
                            s.travelItemsProfits=[];
                            s.travelItemsRestocks=[];
                            tryFinish();
                            return;
                        }

                        // Step 2: Get item names + market prices via Torn API
                        // Torn /v2/market?selections=itemmarket gives market values
                        // But simpler: use torn items endpoint for names
                        if(!this.api.key){
                            // No API key – show with IDs only
                            s.travelItemsProfits=stockedItems.map(i=>({
                                item_name:'Item #'+i.id,
                                country:i.country,
                                profit:0,
                                buy_price:i.cost,
                                sell_price:0,
                                stock:i.qty,
                            }));
                            s.travelItemsRestocks=[];
                            tryFinish();
                            return;
                        }

                        // Fetch torn item details for names & market values
                        GM_xmlhttpRequest({
                            method:'GET',
                            url:`https://api.torn.com/v2/torn?selections=items&key=${this.api.key}`,
                            onload:(r2)=>{
                                try{
                                    const d2=JSON.parse(r2.responseText);
                                    const tornItems=d2.items||d2.torn?.items||{};
                                    const profits=[];
                                    for(const si of stockedItems){
                                        const meta=tornItems[si.id]||tornItems[Number(si.id)]||{};
                                        const name=meta.name||'Item #'+si.id;
                                        const marketVal=Number(meta.market_value||meta.sell_price||0);
                                        const profit=marketVal>0?Math.round(marketVal*0.95-si.cost):0;
                                        profits.push({
                                            item_name:name,
                                            country:si.country,
                                            profit,
                                            buy_price:si.cost,
                                            sell_price:marketVal,
                                            stock:si.qty,
                                        });
                                    }
                                    profits.sort((a,b)=>b.profit-a.profit);
                                    s.travelItemsProfits=profits;
                                    s.travelItemsRestocks=[];
                                }catch(e){
                                    // Torn API failed – show without names
                                    s.travelItemsProfits=stockedItems.map(i=>({
                                        item_name:'Item #'+i.id,
                                        country:i.country,
                                        profit:0,
                                        buy_price:i.cost,
                                        sell_price:0,
                                        stock:i.qty,
                                    }));
                                    s.travelItemsRestocks=[];
                                    s.travelItemsDebug='Torn Items Fehler: '+e.message;
                                }
                                tryFinish();
                            },
                            onerror:()=>{
                                s.travelItemsProfits=stockedItems.map(i=>({item_name:'Item #'+i.id,country:i.country,profit:0,buy_price:i.cost,sell_price:0,stock:i.qty}));
                                s.travelItemsRestocks=[];
                                tryFinish();
                            }
                        });
                    }catch(e){
                        s.travelItemsDebug='YATA Fehler: '+e.message;
                        s.travelItemsProfits=[];
                        s.travelItemsRestocks=[];
                        tryFinish();
                    }
                },
                onerror:()=>{
                    s.travelItemsDebug='YATA Netzwerkfehler';
                    s.travelItemsProfits=[];
                    s.travelItemsRestocks=[];
                    tryFinish();
                }
            });
        }

        fetchAll() { this.fetchPersonal(); this.fetchFaction(); this.fetchChain(); this.fetchCompany(); this.fetchNetworth(); this.fetchEnemyData(); this.fetchTravelItems(()=>{}); }
    }

    // ═══════════════════════════════════════════════════════════════
    // RENDERER
    // ═══════════════════════════════════════════════════════════════
    class Renderer {
        constructor(state) { this.state=state; }
        get body() { return document.getElementById('thud-body'); }
        render() {
            switch(hud.activeTab){
                case 'personal':      return this.renderPersonal();
                case 'faction':       return this.renderFaction();
                case 'bounty':        return this.renderBounty();
                case 'enemy':         return this.renderEnemy();
                case 'company':       return this.renderCompany();
                case 'networth':      return this.renderNetworth();
                case 'travel_items':  return this.renderTravelItems();
                case 'notifications': return this.renderNotifications();
                case 'settings':      return this.renderSettings();
                default:              return this.renderPersonal();
            }
        }

        renderMini() {
            const b=document.getElementById('thud-mini'); if(!b)return;
            const m=Object.values(this.state.members);
            const online=m.filter(x=>FactionHelper.getStatus(x)!=='offline').length;
            const c=this.state.chain,energy=this.state.bars?.energy,nerve=this.state.bars?.nerve;
            const tSec=Math.max(0,Number(this.state.userTravel?.travel?.time_left)||0);
            const tDest=this.state.userTravel?.travel?.destination||'';
            let chips='';
            chips+=`<div class="thud-mini-chip"><span style="width:5px;height:5px;border-radius:50%;background:var(--online);box-shadow:0 0 4px var(--online);display:inline-block;"></span><span class="thud-mini-val">${online}</span><span class="thud-mini-lbl">/${m.length}</span></div>`;
            if(energy){const ep=Math.round((energy.current/energy.maximum)*100);const ec=ep>=100?'var(--red)':ep>=75?'var(--amber)':'var(--blue)';chips+=`<div class="thud-mini-chip"><span style="font-size:9px;">⚡</span><span class="thud-mini-val" style="color:${ec};">${energy.current}</span></div>`;}
            if(nerve){const np=Math.round((nerve.current/nerve.maximum)*100);const nc=np>=100?'var(--red)':'var(--dim)';chips+=`<div class="thud-mini-chip"><span style="font-size:9px;">🧠</span><span class="thud-mini-val" style="color:${nc};">${nerve.current}</span></div>`;}
            if(c&&c.current>0){
                const rem=Math.max(0,this.state.chainEndTime-Date.now());
                const ts=Math.floor(rem/1000),m2=Math.floor(ts/60),s2=ts%60;
                const warn=rem<=CHAIN_WARN_SEC*1000;
                const timerTxt=rem<=300000?`${m2}:${String(s2).padStart(2,'0')}`:` ${m2}m`;
                chips+=`<div class="thud-mini-chip"><span style="font-size:9px;">⛓</span><span class="thud-mini-val" style="color:${warn?'var(--red)':'var(--chain)'};">${c.current}</span><span class="thud-mini-lbl">${timerTxt}</span></div>`;
            }
            if(tSec>0){const dInfo=DESTINATIONS[tDest]||{flag:'✈'};const tw=tSec<=TRAVEL_WARN_SEC;chips+=`<div class="thud-mini-chip"><span style="font-size:11px;">${dInfo.flag}</span><span class="thud-mini-val" style="color:${tw?'var(--amber)':'var(--travel)'};">${Utils.fmtHM(tSec)}</span></div>`;}
            b.innerHTML=`<div class="thud-mini-bar">${chips}</div><div class="thud-mini-tabs"><div class="thud-mini-tab" data-mini-tab="personal">👤 Ich</div><div class="thud-mini-tab" data-mini-tab="faction">⚔ Frak</div><div class="thud-mini-tab" data-mini-tab="bounty">🎯 BNT</div><div class="thud-mini-tab" data-mini-tab="enemy">💀 ENM</div><div class="thud-mini-tab" data-mini-tab="networth">💰 NW</div><div class="thud-mini-tab" data-mini-tab="travel_items">🌍 ITM</div></div>`;
            b.querySelectorAll('[data-mini-tab]').forEach(btn=>btn.addEventListener('click',()=>{hud.store.set('minimized',false);document.getElementById('thud-overlay')?.classList.remove('minimized');hud.switchTab(btn.dataset.miniTab);}));
        }

        renderPersonal() {
            const body=this.body; if(!body)return;
            const s=this.state;
            if(!s.userBasic&&!s.userProfile){body.innerHTML=!hud.store.get('apiKey')?this._noKeyState():this._loadingState();return;}
            const now=Utils.now(),basic=s.userBasic||{},profile=s.userProfile||{};
            const travel=s.userTravel?.travel||{};
            const life=s.bars?.life||profile.life||basic.life||{};
            const hpC=Number(life.current??0),hpM=Number(life.maximum??0),hp=hpM>0?(hpC/hpM)*100:0;
            const statusObj=profile.status||basic.status||{};
            const statusState=typeof statusObj==='string'?statusObj:(statusObj.state||'Okay');
            const hospState=statusState.toLowerCase()==='hospital',jailState=statusState.toLowerCase()==='jail';
            const hospUntil=hospState?Number(statusObj.until??0):0,jailUntil=jailState?Number(statusObj.until??0):0;
            const hospSec=hospUntil>now?hospUntil-now:0,jailSec=jailUntil>now?jailUntil-now:0;
            const energy=s.bars?.energy,nerve=s.bars?.nerve,happy=s.bars?.happy;
            const ePct=energy?(energy.current/energy.maximum)*100:0;
            const nPct=nerve?(nerve.current/nerve.maximum)*100:0;
            const haPct=happy?(happy.current/happy.maximum)*100:0;
            const tSec=Math.max(0,Number(travel.time_left)||0),flying=tSec>0;
            const destInfo=DESTINATIONS[travel.destination]||{flag:'✈'};
            const tDest=travel.destination||'';
            let tvCls='home',tvBadge='🏠 TORN CITY – BEREIT',tvBadgeColor='var(--green)';
            if(flying){if(tSec<=TRAVEL_CRIT_SEC){tvCls='crit';tvBadge='⚠ KURZ VOR ANKUNFT';tvBadgeColor='var(--red)';}else if(tSec<=TRAVEL_WARN_SEC){tvCls='warn';tvBadge='✈ ANKUNFT BALD';tvBadgeColor='var(--amber)';}else{tvCls='ok';tvBadge='✈ UNTERWEGS';tvBadgeColor='var(--travel)';}}
            const tClockColor=tSec<=TRAVEL_CRIT_SEC?'var(--red)':tSec<=TRAVEL_WARN_SEC?'var(--amber)':'var(--travel)';
            const tProgCls=tSec<=TRAVEL_CRIT_SEC?'crit':tSec<=TRAVEL_WARN_SEC?'warn':'';
            const hpColor=hp<50?'var(--red)':hp<70?'var(--amber)':'var(--green)';
            const eColor=ePct>=100?'var(--red)':ePct>=75?'var(--amber)':'var(--travel)';
            const nColor=nPct>=100?'var(--red)':nPct>=75?'var(--amber)':'#9333ea';
            const eMin=energy?.fulltime?Math.max(0,Math.ceil((energy.fulltime-now)/60)):null;
            const nMin=nerve?.fulltime?Math.max(0,Math.ceil((nerve.fulltime-now)/60)):null;
            const cd=s.cooldowns?.cooldowns||{};
            const _cd=(v)=>Number(typeof v==='object'?(v?.cooldown_until??0):(v??0));
            const drugSec=Math.max(0,_cd(cd.drug)-now),medSec=Math.max(0,_cd(cd.medical)-now),boostSec=Math.max(0,_cd(cd.booster)-now);
            const edu=s.education,eduLeft=edu?Math.max(0,Number(edu.until??0)-now):0;
            const nwObj=s.networthData||{};
            const wallet=Number(nwObj.wallet??nwObj.cash??0),stocks=Number(nwObj.stockmarket??nwObj.stocks??0),totalMoney=wallet+stocks;
            const level=basic.level||profile.level||'?',name=basic.name||profile.name||'—';
            const tip=this._smartTip(hp,energy,nerve,tSec,hospSec,jailSec);
            const smartTippHtml=(tip&&s.isVisible('show_tip'))?`<div class="smart-tipp"><span class="smart-tipp-icon">${tip.icon}</span><span class="smart-tipp-text">${tip.text}</span></div>`:'';
            const DEFAULT_QL=[{label:'Stock\nMarket',icon:'📈',url:'https://www.torn.com/page.php?sid=stocks'},{label:'Casino',icon:'🎰',url:'https://www.torn.com/page.php?sid=slots'},{label:'Bazaar',icon:'🏪',url:'https://www.torn.com/bazaar.php?userId=0'},{label:'Forum',icon:'💬',url:'https://www.torn.com/forums.php'}];
            const quickLinks=hud.store.get('customQuickLinks',null)||DEFAULT_QL;
            const qlHtml=s.isVisible('show_quick_links')?`<div><div class="section-header">🔗 QUICK LINKS</div><div class="ql-grid">${quickLinks.map(({label,icon,url})=>`<a class="ql-card" href="${url}" target="_blank"><span class="ql-icon">${icon}</span><span class="ql-name">${label.replace('\n','<br>')}</span></a>`).join('')}</div></div>`:'';
            const bs=s.battleStats;
            const bsT=bs?(Number(bs.strength||0)+Number(bs.defense||0)+Number(bs.speed||0)+Number(bs.dexterity||0)):0;
            const bsHtml=(s.isVisible('show_battle_stats')&&bs)?`<div class="card"><div class="card-header"><span class="card-title red">⚔ BATTLE STATS</span><span class="badge badge-amber">${Utils.fmtStat(bsT)} total</span></div><div class="card-body"><div class="stat-grid stat-grid-4"><div class="stat-card"><div class="sv sv-red">${Utils.fmtStat(bs.strength||0)}</div><div class="sl">STR</div></div><div class="stat-card"><div class="sv sv-blue">${Utils.fmtStat(bs.defense||0)}</div><div class="sl">DEF</div></div><div class="stat-card"><div class="sv sv-green">${Utils.fmtStat(bs.speed||0)}</div><div class="sl">SPD</div></div><div class="stat-card"><div class="sv sv-amber">${Utils.fmtStat(bs.dexterity||0)}</div><div class="sl">DEX</div></div></div></div></div>`:'';
            const tctHtml=s.isVisible('show_tct_time')?`<span style="color:var(--accent2);font-family:var(--mono);">TCT ${Utils.tctTime()}</span>`:'';
            body.innerHTML=`
            ${smartTippHtml}
            <div style="display:flex;align-items:center;justify-content:space-between;padding:2px 2px 0;">
                <div><div style="font-size:14px;font-weight:700;color:var(--text);">${Utils.escHtml(name)}</div><div style="font-size:10px;color:var(--muted);margin-top:2px;">Level ${level} · ${Utils.escHtml(statusState)}</div></div>
                <div style="text-align:right;"><div style="font-family:var(--mono);font-size:15px;font-weight:700;color:var(--amber);">${Utils.fmtMoney(totalMoney)}</div><div style="font-size:9px;color:var(--muted);margin-top:1px;">💵 ${Utils.fmtMoney(wallet)}${stocks?` · 📈 ${Utils.fmtMoney(stocks)}`:''}</div><div style="font-size:8px;color:var(--muted);margin-top:1px;opacity:.6;">liquide</div></div>
            </div>
            <div class="travel-card ${tvCls}">
                <div class="travel-card-top" style="color:${tvBadgeColor};"><span>${tvBadge}</span>${flying?`<span style="font-size:18px;">${destInfo.flag}</span>`:''}</div>
                <div class="travel-card-body">
                ${!flying?`<div style="display:flex;align-items:center;gap:12px;"><span style="font-size:24px;opacity:.2;">🏠</span><div><div style="font-size:14px;font-weight:700;color:var(--green);">TORN CITY</div><div style="font-size:9px;color:var(--muted);text-transform:uppercase;letter-spacing:1.5px;margin-top:2px;">BEREIT ZUM ABFLUG</div></div></div>`
                :`<div style="display:flex;align-items:center;gap:10px;margin-bottom:10px;"><span style="font-size:22px;">${destInfo.flag}</span><div class="travel-dest" style="color:${tClockColor};">${Utils.escHtml(tDest||'Unbekannt')}</div></div><div style="display:flex;justify-content:space-between;align-items:flex-end;"><div class="travel-clock" style="color:${tClockColor};">${Utils.fmtHM(tSec)}</div><div style="text-align:right;"><div style="font-size:9px;color:var(--muted);text-transform:uppercase;letter-spacing:1px;">ANKUNFT</div><div style="font-family:var(--mono);font-size:15px;font-weight:700;color:var(--dim);margin-top:1px;">${Utils.arrTime(tSec)}</div></div></div><div class="travel-prog"><div class="travel-prog-fill ${tProgCls}" style="width:0%;"></div></div>`}
                </div>
            </div>
            <div class="card">
                <div class="card-header"><span class="card-title accent">◈ BARS &amp; STATUS</span><span class="badge badge-${statusState==='Okay'?'green':statusState==='Hospital'?'red':statusState==='Jail'?'amber':'muted'}">${Utils.escHtml(statusState)}</span></div>
                <div class="card-body">
                    <div class="stat-grid stat-grid-3" style="margin-bottom:12px;">
                        <div class="stat-card"><div class="sv ${hp>=70?'sv-green':hp>=50?'sv-amber':'sv-red'}">${hpM>0?Math.round(hp)+'%':'—'}</div><div class="sl">HP</div></div>
                        <div class="stat-card"><div class="sv ${ePct>=100?'sv-red':ePct>=75?'sv-amber':'sv-blue'}">${energy?energy.current:'—'}</div><div class="sl">Energie</div></div>
                        <div class="stat-card"><div class="sv ${nPct>=100?'sv-red':nPct>=75?'sv-amber':'sv-blue'}">${nerve?nerve.current:'—'}</div><div class="sl">Nerve</div></div>
                    </div>
                    <div class="bar-block"><div class="bar-block-head"><span class="bar-block-lbl">❤ HP</span><span class="bar-block-val" style="color:${hpColor}">${hpC} / ${hpM||'?'}</span></div>${Utils.pctBar(hp,hpColor,'bar-lg')}</div>
                    <div class="bar-block"><div class="bar-block-head"><span class="bar-block-lbl">⚡ Energie</span><span class="bar-block-val" style="color:${eColor}">${energy?`${energy.current} / ${energy.maximum}`:'—'}</span></div>${Utils.pctBar(ePct,eColor)}${eMin!==null&&eMin>0?`<div class="bar-block-sub">Voll in ${eMin} Min</div>`:''}</div>
                    <div class="bar-block"><div class="bar-block-head"><span class="bar-block-lbl">🧠 Nerve</span><span class="bar-block-val" style="color:${nColor}">${nerve?`${nerve.current} / ${nerve.maximum}`:'—'}</span></div>${Utils.pctBar(nPct,nColor)}${nMin!==null&&nMin>0?`<div class="bar-block-sub">Voll in ${nMin} Min</div>`:''}</div>
                    ${happy&&s.isVisible('show_happy')?`<div class="bar-block"><div class="bar-block-head"><span class="bar-block-lbl">😊 Happy</span><span class="bar-block-val" style="color:var(--amber)">${happy.current} / ${happy.maximum}</span></div>${Utils.pctBar(haPct,'var(--amber)')}</div>`:''}
                </div>
            </div>
            ${bsHtml}
            ${qlHtml}
            <div class="card">
                <div class="card-header"><span class="card-title">⏱ COOLDOWNS &amp; STATUS</span></div>
                <div class="card-body">
                    ${s.isVisible('show_hospital')?`<div class="row"><span class="row-label">🏥 Krankenhaus</span><span class="row-value" style="color:${hospSec>0?'var(--red)':'var(--green)'}">${hospSec>0?Utils.fmtSec(hospSec):'FREI'}</span></div>`:''}
                    ${s.isVisible('show_jail')?`<div class="row"><span class="row-label">🚔 Gefängnis</span><span class="row-value" style="color:${jailSec>0?'var(--amber)':'var(--green)'}">${jailSec>0?Utils.fmtSec(jailSec):'FREI'}</span></div>`:''}
                    ${s.isVisible('show_drug_cd')?`<div class="row"><span class="row-label">💊 Drogen CD</span><span class="row-value" style="color:${drugSec>0?'var(--amber)':'var(--green)'}">${drugSec>0?Utils.fmtSec(drugSec):'BEREIT'}</span></div>`:''}
                    ${s.isVisible('show_medical_cd')?`<div class="row"><span class="row-label">💉 Medical CD</span><span class="row-value" style="color:${medSec>0?'var(--amber)':'var(--green)'}">${medSec>0?Utils.fmtSec(medSec):'BEREIT'}</span></div>`:''}
                    ${s.isVisible('show_booster_cd')?`<div class="row"><span class="row-label">🧪 Booster CD</span><span class="row-value" style="color:${boostSec>0?'var(--amber)':'var(--green)'}">${boostSec>0?Utils.fmtSec(boostSec):'BEREIT'}</span></div>`:''}
                    ${s.isVisible('show_education')&&edu?(eduLeft>0?`<div class="row"><span class="row-label">📚 Kurs</span><span class="row-value">${Utils.fmtDhm(eduLeft)}</span></div>`:`<div class="row"><span class="row-label">📚 Lehrgang</span><span class="row-value" style="color:var(--green);">ABGESCHLOSSEN ✓</span></div>`):''}
                </div>
            </div>
            <div style="display:flex;justify-content:space-between;align-items:center;padding:2px 0;flex-shrink:0;" class="ts">
                <span style="display:flex;align-items:center;gap:5px;"><div class="pip"></div>${s.lastUpdate?.toLocaleTimeString('de-DE')||'—'}</span>
                ${tctHtml}
                <span>⬆ ${this._uptimeStr()}</span>
            </div>`;
        }

        _smartTip(hp,energy,nerve,tSec,hospSec,jailSec) {
            if(jailSec>0)return{icon:'🚔',text:`Du sitzt noch ${Utils.fmtSec(jailSec)} im Knast.`};
            if(hospSec>0&&hospSec<300)return{icon:'🏥',text:`Nur noch ${Utils.fmtSec(hospSec)} im Krankenhaus.`};
            if(hp<50&&hp>0)return{icon:'❤',text:`HP sehr niedrig (${Math.round(hp)}%). Medical Items nutzen!`};
            if(energy?.current>=energy?.maximum)return{icon:'⚡',text:'Energie voll! Chains oder Gym nutzen!'};
            if(nerve?.current>=nerve?.maximum)return{icon:'🧠',text:'Nerve voll! Crimes abarbeiten!'};
            if(tSec>0&&tSec<300)return{icon:'✈',text:`Fast da! Ankunft in ${Utils.fmtHM(tSec)}.`};
            if(energy&&(energy.current/energy.maximum)>0.85)return{icon:'💡',text:'Energie fast voll – nächste Chain planen?'};
            return null;
        }
        _uptimeStr() { const s=this.state.uptimeSec,h=Math.floor(s/3600),m=Math.floor((s%3600)/60); return h>0?`${h}h ${m}m`:`${m}m`; }

        renderFaction() {
            const body=this.body; if(!body)return;
            const s=this.state;
            const factionSubTab=s._factionSubTab||'overview';
            const subTabHtml=`<div style="display:flex;gap:5px;padding:8px 14px 0;flex-shrink:0;">
                <button class="sort-btn ${factionSubTab==='overview'?'active':''}" data-faction-sub="overview">⚔ Übersicht</button>
                <button class="sort-btn ${factionSubTab==='details'?'active':''}" data-faction-sub="details">📋 Mitglieder</button>
            </div>`;
            if(factionSubTab==='details'){
                this._renderFactionDetails(body,s,subTabHtml);
            } else {
                this._renderFactionOverview(body,s,subTabHtml);
            }
            body.querySelectorAll('[data-faction-sub]').forEach(btn=>btn.addEventListener('click',()=>{s._factionSubTab=btn.dataset.factionSub;this.renderFaction();}));
        }

        _renderFactionOverview(body,s,subTabHtml) {
            const m=Object.values(s.members),t=m.length;
            const cap=s.faction.max_members||s.faction.capacity||t||50;
            const groups={online:[],idle:[],traveling:[],hospital:[],jail:[],offline:[]};
            m.forEach(x=>groups[FactionHelper.getStatus(x)].push(x));
            const active=[...groups.online,...groups.traveling,...groups.idle,...groups.hospital,...groups.jail];
            const sa=active.sort((a,b)=>FactionHelper.statusOrder(FactionHelper.getStatus(a))-FactionHelper.statusOrder(FactionHelper.getStatus(b)));
            const so=[...groups.offline].sort((a,b)=>(a.last_action?.timestamp||0)-(b.last_action?.timestamp||0));
            const sl=20,ol=INACTIVE_LIMIT;
            const sOn=s.showAllOnline?sa:sa.slice(0,sl);
            const sOf=s.showAllOffline?so:so.slice(0,ol);
            const eOn=sa.length-sl,eOf=so.length-ol;
            const rd=groups.online.length+groups.idle.length,oc=active.length;
            const donutSVG=Utils.buildDonut([{count:groups.online.length,color:'var(--green)'},{count:groups.idle.length,color:'var(--amber)'},{count:groups.traveling.length,color:'var(--travel)'},{count:groups.hospital.length,color:'var(--red)'},{count:groups.jail.length,color:'#f97316'},{count:groups.offline.length,color:'var(--bg-el)'}],t);
            body.innerHTML=`
            ${subTabHtml}
            <div class="chain-block" id="thud-chain-block"><div class="chain-idle"><div class="pulse-dot" style="opacity:.2;animation:none;background:var(--muted);"></div><span style="font-size:10px;font-weight:600;margin-left:7px;letter-spacing:.5px;">Keine aktive Chain</span></div></div>
            <div class="card"><div class="card-header"><div style="display:flex;align-items:center;gap:8px;"><span class="card-title green">● ONLINE</span><span class="badge badge-green">${oc}</span></div><div style="position:relative;display:flex;align-items:center;justify-content:center;">${donutSVG}<div style="position:absolute;text-align:center;pointer-events:none;"><div style="font-size:13px;font-weight:700;color:var(--text);">${oc}</div><div style="font-size:8px;color:var(--muted);">/${cap}</div></div></div></div>
            <div class="card-body"><div class="member-grid">${sOn.map(x=>{const st=FactionHelper.getStatus(x);return`<div class="member-row"><span class="status-dot sd-${st}"></span><span class="member-name">${Utils.escHtml(x.name)}</span><span class="member-status">${FactionHelper.statusLabel(st)}</span></div>`;}).join('')}</div>${eOn>0&&!s.showAllOnline?`<span class="toggle-more" id="thud-more-online">▾ ${eOn} mehr</span>`:s.showAllOnline&&sa.length>sl?`<span class="toggle-more" id="thud-less-online">▴ Weniger</span>`:''}<div class="status-summary"><div class="stat-chip"><div class="sn" style="color:var(--green);">${groups.online.length}</div><div class="sl">Online</div></div><div class="stat-chip"><div class="sn" style="color:var(--amber);">${groups.idle.length}</div><div class="sl">AFK</div></div><div class="stat-chip"><div class="sn" style="color:var(--travel);">${groups.traveling.length}</div><div class="sl">Travel</div></div><div class="stat-chip"><div class="sn" style="color:var(--red);">${groups.hospital.length}</div><div class="sl">Hosp.</div></div><div class="stat-chip"><div class="sn" style="color:#f97316;">${groups.jail.length}</div><div class="sl">Jail</div></div><div class="stat-chip"><div class="sn" style="color:var(--muted);">${groups.offline.length}</div><div class="sl">Offline</div></div></div></div></div>
            <div class="card"><div class="card-header"><span class="card-title red">⚔ WAR READY</span></div><div class="card-body"><div class="war-grid"><div class="war-cell"><div class="wv" style="color:var(--green);">${rd}</div><div class="wl">Ready</div></div><div class="war-cell"><div class="wv" style="color:var(--red);">${groups.hospital.length}</div><div class="wl">Hosp.</div></div><div class="war-cell"><div class="wv" style="color:var(--travel);">${groups.traveling.length}</div><div class="wl">Travel</div></div><div class="war-cell"><div class="wv" style="color:var(--amber);">${t>0?Math.round(rd/t*100):0}%</div><div class="wl">Bereit</div></div></div></div></div>
            ${groups.traveling.length>0?`<div class="card"><div class="card-header"><span class="card-title blue">✈ UNTERWEGS (${groups.traveling.length})</span></div><div class="card-body p0">${groups.traveling.map(x=>{const{from,to,dir}=FactionHelper.parseTravelRoute(x);const fC=from.toLowerCase().includes('torn')?'var(--green)':'var(--travel)';const tC=to.toLowerCase().includes('torn')?'var(--green)':'var(--travel)';return`<div style="display:flex;align-items:center;gap:10px;padding:9px 14px;border-bottom:1px solid rgba(255,255,255,.04);"><span style="font-size:18px;">${dir==='home'?'🏠':'✈'}</span><div style="flex:1;min-width:0;"><div style="font-size:12px;font-weight:600;color:var(--text);">${Utils.escHtml(x.name)}</div><div style="font-size:10px;margin-top:2px;display:flex;align-items:center;gap:5px;"><span style="color:${fC};">${Utils.escHtml(from)}</span><span style="color:var(--muted);">→</span><span style="color:${tC};">${Utils.escHtml(to)}</span></div></div></div>`;}).join('')}</div></div>`:''}
            <div class="card"><div class="card-header"><span class="card-title">💤 OFFLINE (${groups.offline.length})</span></div><div class="card-body p0">${sOf.map(x=>{const a=Utils.inactiveAgo(x.last_action?.timestamp||0);return`<div class="offline-row"><span class="status-dot sd-offline"></span><span class="offline-name">${Utils.escHtml(x.name)}</span><span class="offline-ago ${a.cls}">${a.text}</span></div>`;}).join('')}${eOf>0&&!s.showAllOffline?`<span class="toggle-more" style="margin:7px 14px 12px;" id="thud-more-offline">▾ ${eOf} weitere</span>`:s.showAllOffline&&so.length>ol?`<span class="toggle-more" style="margin:7px 14px 12px;" id="thud-less-offline">▴ Weniger</span>`:''}</div></div>`;
            hud.chainManager.renderSection();
            if(s.chain?.current>0)hud.chainManager.startTick();
            document.getElementById('thud-more-online')?.addEventListener('click',()=>{s.showAllOnline=true;this.renderFaction();});
            document.getElementById('thud-less-online')?.addEventListener('click',()=>{s.showAllOnline=false;this.renderFaction();});
            document.getElementById('thud-more-offline')?.addEventListener('click',()=>{s.showAllOffline=true;this.renderFaction();});
            document.getElementById('thud-less-offline')?.addEventListener('click',()=>{s.showAllOffline=false;this.renderFaction();});
        }

        _renderFactionDetails(body,s,subTabHtml) {
            const now=Utils.now();
            const m=Object.values(s.members);
            const detSort=s._factionDetailSort||'name';
            const detSortDir=s._factionDetailSortDir||'asc';
            const sortOpts=[{k:'name',lbl:'Name'},{k:'rank',lbl:'Rang'},{k:'joined',lbl:'Beigetreten'},{k:'days',lbl:'Tage'},{k:'status',lbl:'Status'}];
            const sortBtnsHtml=sortOpts.map(o=>`<button class="sort-btn ${detSort===o.k?'active':''}" data-det-sort="${o.k}" style="font-size:10px;">${o.lbl}</button>`).join('');
            const dirBtnHtml=`<button class="sort-btn" id="thud-det-dir" style="padding:4px 10px;font-size:10px;">${detSortDir==='asc'?'▲':'▼'}</button>`;
            let sorted=m.slice();
            sorted.sort((a,b)=>{
                let va,vb;
                if(detSort==='name'){va=(a.name||'').toLowerCase();vb=(b.name||'').toLowerCase();return detSortDir==='asc'?va.localeCompare(vb):vb.localeCompare(va);}
                if(detSort==='rank'){va=(a.position||a.rank||'').toLowerCase();vb=(b.position||b.rank||'').toLowerCase();return detSortDir==='asc'?va.localeCompare(vb):vb.localeCompare(va);}
                if(detSort==='joined'){va=Number(a.days_in_faction??a.faction_joined??0);vb=Number(b.days_in_faction??b.faction_joined??0);}
                if(detSort==='days'){va=Number(a.days_in_faction??0);vb=Number(b.days_in_faction??0);}
                if(detSort==='status'){va=FactionHelper.statusOrder(FactionHelper.getStatus(a));vb=FactionHelper.statusOrder(FactionHelper.getStatus(b));}
                return detSortDir==='asc'?va-vb:vb-va;
            });
            const rows=sorted.map(x=>{
                const rank=x.position||x.rank||'Member';
                const isRecruit=rank.toLowerCase()==='recruit';
                const daysIn=Number(x.days_in_faction??0);
                const cachedJoin=s.recruitJoinCache[x.id]?.ts??0;
                const joinedTs=Number(cachedJoin||x.faction_joined||x.joined||0);
                let joinedStr='—';
                if(joinedTs>0){
                    joinedStr=new Date(joinedTs*1000).toLocaleDateString('de-DE',{day:'2-digit',month:'2-digit',year:'numeric'});
                } else if(daysIn>0){
                    const approxTs=(now-daysIn*86400)*1000;
                    joinedStr='~'+new Date(approxTs).toLocaleDateString('de-DE',{day:'2-digit',month:'2-digit',year:'numeric'});
                }
                let recruitTimerHtml='';
                if(isRecruit){
                    const RECRUIT_SEC=72*3600;
                    const cacheEntry=s.recruitJoinCache[x.id];
                    const cachedTs=Number(cacheEntry?.ts??0);
                    const isExact=cacheEntry?.exact===true;
                    let secsIn;
                    if(cachedTs>0){secsIn=now-cachedTs;}
                    else{secsIn=daysIn*86400;}
                    const remaining=Math.max(0,RECRUIT_SEC-secsIn);
                    if(remaining>0){
                        const hrs=Math.floor(remaining/3600);
                        const prefix=isExact?'':'~';
                        recruitTimerHtml=`<span style="font-size:9px;font-family:var(--mono);color:var(--amber);background:rgba(245,158,11,.1);border:1px solid rgba(245,158,11,.2);border-radius:4px;padding:1px 6px;">⏱ ${prefix}${hrs}h</span>`;
                    } else {
                        recruitTimerHtml=`<span style="font-size:9px;color:var(--green);background:rgba(16,185,129,.1);border:1px solid rgba(16,185,129,.2);border-radius:4px;padding:1px 6px;">✓ Beförderbar</span>`;
                    }
                }
                const st=FactionHelper.getStatus(x);
                const rankColor=isRecruit?'var(--amber)':rank.toLowerCase().includes('leader')||rank.toLowerCase().includes('co-leader')?'var(--red)':'var(--accent2)';
                return`<div style="display:flex;align-items:flex-start;gap:8px;padding:8px 14px;border-bottom:1px solid rgba(255,255,255,.04);">
                    <span class="status-dot sd-${st}" style="flex-shrink:0;margin-top:4px;"></span>
                    <div style="flex:1;min-width:0;">
                        <div style="display:flex;align-items:center;gap:6px;flex-wrap:wrap;">
                            <a href="https://www.torn.com/profiles.php?XID=${x.id}" target="_blank" style="color:var(--text);text-decoration:none;font-weight:700;font-size:12px;">${Utils.escHtml(x.name)}</a>
                            <span style="font-size:10px;font-weight:600;color:${rankColor};">⚔ ${Utils.escHtml(rank)}</span>
                            ${recruitTimerHtml}
                        </div>
                        <div style="display:flex;gap:8px;margin-top:3px;flex-wrap:wrap;">
                            <span style="font-size:10px;color:var(--muted);">📅 Beigetreten: <span style="color:var(--dim);">${joinedStr}</span></span>
                            ${daysIn>0?`<span style="font-size:10px;color:var(--muted);">· <span style="color:var(--dim);">${daysIn} Tage</span></span>`:''}
                        </div>
                    </div>
                    <span style="font-size:10px;font-family:var(--mono);color:var(--muted);flex-shrink:0;">${FactionHelper.statusLabel(st)}</span>
                </div>`;
            }).join('');

            body.innerHTML=`
            ${subTabHtml}
            <div class="card" style="flex-shrink:0;">
                <div class="card-header">
                    <span class="card-title accent">📋 MITGLIEDER DETAILS</span>
                    <span class="badge badge-accent">${m.length} Mitglieder</span>
                </div>
                <div class="card-body" style="padding:8px 14px;gap:5px;">
                    <div style="display:flex;gap:4px;align-items:center;flex-wrap:wrap;">
                        <span style="font-size:9px;color:var(--muted);font-weight:700;">SORT:</span>
                        ${sortBtnsHtml}${dirBtnHtml}
                    </div>
                </div>
            </div>
            ${m.length===0?`<div class="empty-state"><div class="es-icon">⚔</div><div class="es-text">Keine Mitglieder</div></div>`:
            `<div class="card" style="flex-shrink:0;"><div class="card-body p0">${rows}</div></div>`}`;

            body.querySelectorAll('[data-det-sort]').forEach(btn=>btn.addEventListener('click',()=>{s._factionDetailSort=btn.dataset.detSort;this.renderFaction();}));
            document.getElementById('thud-det-dir')?.addEventListener('click',()=>{s._factionDetailSortDir=s._factionDetailSortDir==='asc'?'desc':'asc';this.renderFaction();});
        }

        renderBounty() {
            const body=this.body; if(!body)return;
            const s=this.state;
            const now=Utils.now();
            const data=s.bountyData||s.bountyAllData||null;
            const sort=s.bountySort||'reward';

            const doScan=()=>{hud.fetcher.fetchBountiesInitial(()=>{s.bountySearch='';s.bountyDisplayCount=80;this.renderBounty();});};

            if(!data){
                body.innerHTML=`<div class="card" style="flex-shrink:0;"><div class="card-header"><span class="card-title red">🎯 BOUNTY RADAR</span></div></div><div class="empty-state"><div class="es-icon">🎯</div><div class="es-text">Keine Bounties geladen</div><div class="es-sub">Klick "Laden" um die Torn Bounty-Liste zu holen</div></div><button class="scan-btn" id="thud-bounty-scan" style="align-self:center;">🔍 Bounty-Liste laden</button>`;
                document.getElementById('thud-bounty-scan')?.addEventListener('click',doScan); return;
            }

            const lvMin=Number(s.bountyLevelMin)||1,lvMax=Number(s.bountyLevelMax)||100;
            const rwMin=Number(s.bountyRewardMin)||0,rwMax=Number(s.bountyRewardMax)||0;
            const ffMin=Number(s.bountyFFMin)||0,ffMax=Number(s.bountyFFMax)||0;
            const q=(s.bountySearch||'').toLowerCase().trim();
            let filtered=data.slice();
            if(q)filtered=filtered.filter(b=>{const name=(b.target_name??b.name??'').toLowerCase(),reason=(b.reason??'').toLowerCase();return name.includes(q)||reason.includes(q);});
            filtered=filtered.filter(b=>{const lvl=Number(b.target_level??b.level??0);return lvl>=lvMin&&lvl<=lvMax;});
            if(rwMin>0||rwMax>0){filtered=filtered.filter(b=>{const r=Number(b.reward??b.amount??0);if(rwMin>0&&r<rwMin)return false;if(rwMax>0&&r>rwMax)return false;return true;});}
            if(ffMin>0||ffMax>0){
                filtered=filtered.filter(b=>{
                    const tid=Number(b.target_id??b.id);
                    const ffEntry=s.ffData[tid];
                    const ff=ffEntry?.ff??null;
                    if(ff===null)return true;
                    if(ffMin>0&&ff<ffMin)return false;
                    if(ffMax>0&&ff>ffMax)return false;
                    return true;
                });
            }
            if(s.bountyFilterHosp)filtered=filtered.filter(b=>!this._isHospital(b));
            if(sort==='reward')filtered.sort((a,b)=>(b.reward??b.amount??0)-(a.reward??a.amount??0));
            if(sort==='level') filtered.sort((a,b)=>(a.target_level??a.level??0)-(b.target_level??b.level??0));
            if(sort==='time')  filtered.sort((a,b)=>(a.valid_until??a.expires??0)-(b.valid_until??b.expires??0));
            if(sort==='ff')    filtered.sort((a,b)=>{const fa=s.ffData[Number(a.target_id??a.id)]?.ff??-1;const fb=s.ffData[Number(b.target_id??b.id)]?.ff??-1;return fb-fa;});
            if(!s.bountyFilterHosp)filtered.sort((a,b)=>(this._isHospital(a)?0:1)-(this._isHospital(b)?0:1));
            const displayCount=s.bountyDisplayCount||80,visible=filtered.slice(0,displayCount),hasMoreVisible=filtered.length>displayCount;
            const hospCount=data.filter(b=>this._isHospital(b)).length,isFiltered=filtered.length!==data.length;
            const searchHadFocus=document.activeElement?.id==='thud-bounty-search';
            const searchCursorPos=searchHadFocus?(document.activeElement.selectionStart??0):0;
            const rwMinD=rwMin>0?(rwMin>=1e6?(rwMin/1e6).toFixed(1)+'M':rwMin>=1e3?(rwMin/1e3).toFixed(0)+'K':rwMin):'';
            const rwMaxD=rwMax>0?(rwMax>=1e6?(rwMax/1e6).toFixed(1)+'M':rwMax>=1e3?(rwMax/1e3).toFixed(0)+'K':rwMax):'';

            body.innerHTML=`
            <div class="card" style="flex-shrink:0;">
                <div class="card-header" style="flex-wrap:wrap;gap:6px;">
                    <span class="card-title red">🎯 BOUNTY RADAR</span>
                    <div style="display:flex;align-items:center;gap:5px;flex-wrap:wrap;">
                        <span class="badge badge-red">${data.length} total</span>
                        ${hospCount>0?`<span class="badge badge-red">🏥 ${hospCount}</span>`:''}
                        ${isFiltered?`<span class="badge badge-amber">${filtered.length} gefiltert</span>`:''}
                        ${!s.bountyLoading?`<button class="bounty-load-more-inline" id="thud-bounty-more">▾ +Mehr (S.${s.bountyLoadedPages})</button>`:`<span class="bounty-load-more-inline" style="opacity:.5;cursor:default;"><span class="spinning">↺</span>${s.bountyScanningAll?' Scan...':''}</span>`}
                        <button class="scan-btn" id="thud-bounty-scan-all" style="padding:4px 10px;font-size:10px;background:rgba(245,158,11,.15);border-color:rgba(245,158,11,.3);color:var(--amber);" ${s.bountyLoading?'disabled':''} title="Alle Seiten laden">⚡ Alle</button>
                        <button class="scan-btn" id="thud-bounty-scan" style="padding:4px 10px;font-size:10px;" ${s.bountyLoading?'disabled':''}>${s.bountyLoading?'<span class="spinning">↺</span>':'↺'}</button>
                    </div>
                </div>
                <div class="card-body" style="padding:9px 14px 10px;gap:7px;">
                    <input type="text" class="bounty-search" id="thud-bounty-search" placeholder="🔍 Name oder Grund..." value="${Utils.escHtml(s.bountySearch||'')}">
                    <div class="level-filter-row">
                        <span style="font-size:10px;color:var(--muted);font-weight:700;">LV:</span>
                        <input type="number" class="level-input" id="thud-lvl-min" min="1" max="100" value="${lvMin}">
                        <span style="color:var(--muted);font-size:10px;">–</span>
                        <input type="number" class="level-input" id="thud-lvl-max" min="1" max="100" value="${lvMax}">
                        <button class="sort-btn" id="thud-lvl-apply">✓</button>
                    </div>
                    <div class="level-filter-row">
                        <span style="font-size:10px;color:var(--amber);font-weight:700;">💰:</span>
                        <input type="text" class="reward-input" id="thud-rw-min" placeholder="Min" value="${rwMinD}">
                        <span style="color:var(--muted);font-size:10px;">–</span>
                        <input type="text" class="reward-input" id="thud-rw-max" placeholder="Max" value="${rwMaxD}">
                        <button class="sort-btn" id="thud-rw-apply" style="border-color:rgba(245,158,11,.3);color:var(--amber);">✓</button>
                        ${(rwMin>0||rwMax>0)?`<button class="sort-btn" id="thud-rw-clear" style="color:var(--muted);">✕</button>`:''}
                    </div>
                    <div class="level-filter-row">
                        <span style="font-size:10px;color:var(--accent2);font-weight:700;">FF:</span>
                        <input type="text" class="ff-input" id="thud-ff-min" placeholder="Min" value="${ffMin>0?ffMin:''}">
                        <span style="color:var(--muted);font-size:10px;">–</span>
                        <input type="text" class="ff-input" id="thud-ff-max" placeholder="Max" value="${ffMax>0?ffMax:''}">
                        <button class="sort-btn" id="thud-ff-apply" style="border-color:rgba(99,102,241,.3);color:var(--accent2);">✓</button>
                        ${(ffMin>0||ffMax>0)?`<button class="sort-btn" id="thud-ff-clear" style="color:var(--muted);">✕</button>`:''}
                    </div>
                    <div class="sort-bar">
                        <span style="font-size:9px;color:var(--muted);font-weight:700;">SORT:</span>
                        <button class="sort-btn ${sort==='reward'?'active':''}" data-sort="reward">💰 Reward</button>
                        <button class="sort-btn ${sort==='level'?'active':''}"  data-sort="level">⚔ Level</button>
                        <button class="sort-btn ${sort==='time'?'active':''}"   data-sort="time">⏱ Zeit</button>
                        <button class="sort-btn ${sort==='ff'?'active':''}"     data-sort="ff">📊 FF</button>
                    </div>
                    <div class="sort-bar">
                        <span style="font-size:9px;color:var(--muted);font-weight:700;">FILTER:</span>
                        <button class="sort-btn ${s.bountyFilterHosp?'active':''}" data-filter="hosp">🏥 ${s.bountyFilterHosp?'Hosp. ausgebl.':'Hosp. anzeigen'}</button>
                    </div>
                </div>
            </div>
            <div class="card" style="flex-shrink:0;"><div class="card-body p0" id="thud-bounty-list">${this._buildBountyRows(visible,now)}</div></div>
            <div class="ts" style="text-align:center;flex-shrink:0;">${data.length} Bounties · Seite ${s.bountyLoadedPages} · ${s.bountyLastFetch?new Date(s.bountyLastFetch*1000).toLocaleTimeString('de-DE'):'—'}</div>`;

            if(searchHadFocus){const inp=document.getElementById('thud-bounty-search');if(inp){inp.focus();try{inp.setSelectionRange(searchCursorPos,searchCursorPos);}catch(e){}}}
            document.getElementById('thud-bounty-scan')?.addEventListener('click',doScan);
            document.getElementById('thud-bounty-scan-all')?.addEventListener('click',()=>{if(s.bountyScanningAll||s.bountyLoading)return;hud.fetcher.fetchBountiesAllPages(()=>this.renderBounty(),(all)=>{s.bountySearch='';s.bountyDisplayCount=80;hud.toasts.show('✅','Alle Bounties geladen',`${all.length} Bounties`,'info',5000);this.renderBounty();});this.renderBounty();});
            document.getElementById('thud-bounty-search')?.addEventListener('input',e=>{s.bountySearch=e.target.value;s.bountyDisplayCount=80;this._renderBountyListOnly(s,now);});
            document.getElementById('thud-lvl-apply')?.addEventListener('click',()=>{s.setBountyLevelMin(Number(document.getElementById('thud-lvl-min')?.value)||1);s.setBountyLevelMax(Number(document.getElementById('thud-lvl-max')?.value)||100);s.bountyDisplayCount=80;this.renderBounty();});
            const parseR=(str)=>{if(!str||!str.trim())return 0;str=str.trim().toUpperCase().replace(/\s/g,'').replace(',','.');const m=str.match(/^([\d.]+)([KMB]?)$/);if(!m)return 0;return Math.round(parseFloat(m[1])*({K:1e3,M:1e6,B:1e9,'':1}[m[2]]||1));};
            document.getElementById('thud-rw-apply')?.addEventListener('click',()=>{s.setBountyRewardMin(parseR(document.getElementById('thud-rw-min')?.value));s.setBountyRewardMax(parseR(document.getElementById('thud-rw-max')?.value));s.bountyDisplayCount=80;this.renderBounty();});
            document.getElementById('thud-rw-clear')?.addEventListener('click',()=>{s.setBountyRewardMin(0);s.setBountyRewardMax(0);s.bountyDisplayCount=80;this.renderBounty();});
            document.getElementById('thud-ff-apply')?.addEventListener('click',()=>{s.setBountyFFMin(parseFloat(document.getElementById('thud-ff-min')?.value)||0);s.setBountyFFMax(parseFloat(document.getElementById('thud-ff-max')?.value)||0);s.bountyDisplayCount=80;this.renderBounty();});
            document.getElementById('thud-ff-clear')?.addEventListener('click',()=>{s.setBountyFFMin(0);s.setBountyFFMax(0);s.bountyDisplayCount=80;this.renderBounty();});
            body.querySelectorAll('[data-sort]').forEach(btn=>btn.addEventListener('click',()=>{s.setBountySort(btn.dataset.sort);s.bountyDisplayCount=80;this.renderBounty();}));
            body.querySelectorAll('[data-filter]').forEach(btn=>btn.addEventListener('click',()=>{if(btn.dataset.filter==='hosp')s.setBountyFilterHosp(!s.bountyFilterHosp);s.bountyDisplayCount=80;this.renderBounty();}));
            document.getElementById('thud-bounty-more')?.addEventListener('click',()=>{if(hasMoreVisible){s.bountyDisplayCount+=80;this._renderBountyListOnly(s,now);}else{hud.fetcher.fetchBountiesNextPage(has=>{if(!has)hud.toasts.show('ℹ','Alle geladen',`${s.bountyData.length} Bounties`,'info',4000);else s.bountyDisplayCount+=80;this.renderBounty();});}});
            body.querySelectorAll('[data-bounty-add-enemy]').forEach(btn=>btn.addEventListener('click',()=>{const id=btn.dataset.bountyAddEnemy,name=btn.dataset.bountyName||`#${id}`;if(!s.enemies.find(e=>String(e.id)===String(id))){s.addEnemy(String(id),name,'','default');hud.toasts.show('💀','Enemy hinzugefügt',`${name} aus Bounty-Liste`,'crit',3000);hud.fetcher.fetchEnemyData();}else{hud.toasts.show('⚠','Bereits in Enemy-Liste',name,'warn',2000);}}));
            const ffKey=hud.store.get('ffKey','');
            if(ffKey&&visible.length){
                const ids=visible.map(b=>Number(b.target_id??b.id)).filter(Boolean);
                getFFData(ids,ffKey,(ffResults)=>{
                    s.ffData={...s.ffData,...ffResults};
                    ids.forEach(id=>{const el=document.querySelector(`[data-ff-badge="${id}"]`);if(el)el.innerHTML=ffBadge(ffResults[id]);});
                });
            }
        }

        _isHospital(b) {
            const so=(typeof b.target?.status==='object'&&b.target.status)||(typeof b.status==='object'&&b.status)||{};
            const raw=so.state??so.description??b.target_state??b.state??'';
            return raw.toLowerCase().includes('hospital');
        }

        _buildBountyRows(visible,now) {
            if(!visible||!visible.length)return`<div class="empty-state"><div class="es-icon">🔍</div><div class="es-text">Keine Ergebnisse</div><div class="es-sub">Filter anpassen oder Bounties neu laden</div></div>`;
            const s=this.state;
            return visible.map(b=>{
                const tid=b.target_id??b.id,tname=b.target_name??b.name??`#${tid}`;
                const lvl=b.target_level??b.level??0,reward=b.reward??b.amount??0;
                const validUntil=b.valid_until??b.expires??0,timeLeft=validUntil>0?Math.max(0,validUntil-now):0;
                const reason=b.reason?Utils.escHtml(b.reason.substring(0,80)):'';
                const so=(typeof b.target?.status==='object'&&b.target.status)||(typeof b.status==='object'&&b.status)||{};
                const rawState=so.state??so.description??b.target_state??b.state??'';
                const ss=rawState.toLowerCase(),isH=ss.includes('hospital');
                const hospUntil=Number(so.until??so.hospital_timestamp??0);
                const hospLeft=isH&&hospUntil>now?hospUntil-now:0;
                let statusTag='';
                if(isH)statusTag=`<span class="bounty-tag tag-hosp">🏥 HOSPITAL${hospLeft>0?` · ${Utils.fmtSec(hospLeft)}`:''}</span>`;
                else if(ss.includes('travel')||ss.includes('abroad'))statusTag=`<span class="bounty-tag tag-travel">✈ TRAVEL</span>`;
                else if(ss.includes('jail'))statusTag=`<span class="bounty-tag tag-jail">🚔 JAIL</span>`;
                else if(ss&&ss!=='okay')statusTag=`<span class="bounty-tag tag-ok">✓ ${Utils.escHtml(rawState)}</span>`;
                let diff,dCls;
                if(lvl<=15){diff='EASY';dCls='diff-easy';}else if(lvl<=50){diff='MID';dCls='diff-medium';}else{diff='HARD';dCls='diff-hard';}
                const ffEntry=s.ffData[Number(tid)];
                const ffHtml=`<span data-ff-badge="${tid}">${ffBadge(ffEntry)}</span>`;
                return`<div class="bounty-row${isH?' is-hospital':''}">
                    <div style="flex:1;min-width:0;">
                        <div style="display:flex;align-items:center;gap:5px;flex-wrap:wrap;">
                            <a href="https://www.torn.com/profiles.php?XID=${tid}" target="_blank" style="color:var(--text);text-decoration:none;font-weight:700;font-size:13px;">${Utils.escHtml(tname)}</a>
                            <span class="${dCls} bounty-difficulty">${diff}</span>
                            <span style="font-size:10px;font-family:var(--mono);color:var(--muted);">Lv ${lvl}</span>
                            ${ffHtml}
                        </div>
                        <div class="bounty-info-row">${statusTag}</div>
                        ${reason?`<div style="font-size:10px;color:var(--muted);margin-top:2px;font-style:italic;">"${reason}"</div>`:''}
                        ${timeLeft>0?`<div style="font-size:9px;color:var(--amber);font-family:var(--mono);margin-top:1px;">⏱ ${Utils.fmtSec(timeLeft)}</div>`:''}
                    </div>
                    <div style="display:flex;flex-direction:column;align-items:flex-end;gap:4px;flex-shrink:0;">
                        <span class="bounty-reward">${Utils.fmtMoney(reward)}</span>
                        <a class="bounty-link" href="https://www.torn.com/page.php?sid=attack&user2ID=${tid}" target="_blank">⚔ Angriff</a>
                        <button class="bounty-link" style="background:rgba(239,68,68,.12);border:1px solid rgba(239,68,68,.25);color:var(--red);cursor:pointer;font-family:var(--font);" data-bounty-add-enemy="${tid}" data-bounty-name="${Utils.escHtml(tname)}">💀 Enemy</button>
                    </div>
                </div>`;
            }).join('');
        }

        _renderBountyListOnly(s,now) {
            const listEl=document.getElementById('thud-bounty-list'); if(!listEl){this.renderBounty();return;}
            const q=(s.bountySearch||'').toLowerCase().trim();
            const lvMin=Number(s.bountyLevelMin)||1,lvMax=Number(s.bountyLevelMax)||100;
            const rwMin=Number(s.bountyRewardMin)||0,rwMax=Number(s.bountyRewardMax)||0;
            const ffMin=Number(s.bountyFFMin)||0,ffMax=Number(s.bountyFFMax)||0;
            let filtered=(s.bountyData||[]).slice();
            if(q)filtered=filtered.filter(b=>{const n=(b.target_name??b.name??'').toLowerCase(),r=(b.reason??'').toLowerCase();return n.includes(q)||r.includes(q);});
            filtered=filtered.filter(b=>{const l=Number(b.target_level??b.level??0);return l>=lvMin&&l<=lvMax;});
            if(rwMin>0||rwMax>0)filtered=filtered.filter(b=>{const r=Number(b.reward??b.amount??0);if(rwMin>0&&r<rwMin)return false;if(rwMax>0&&r>rwMax)return false;return true;});
            if(ffMin>0||ffMax>0){filtered=filtered.filter(b=>{const tid=Number(b.target_id??b.id);const ff=s.ffData[tid]?.ff??null;if(ff===null)return true;if(ffMin>0&&ff<ffMin)return false;if(ffMax>0&&ff>ffMax)return false;return true;});}
            if(s.bountyFilterHosp)filtered=filtered.filter(b=>!this._isHospital(b));
            const sort=s.bountySort;
            if(sort==='reward')filtered.sort((a,b)=>(b.reward??b.amount??0)-(a.reward??a.amount??0));
            if(sort==='level') filtered.sort((a,b)=>(a.target_level??a.level??0)-(b.target_level??b.level??0));
            if(sort==='time')  filtered.sort((a,b)=>(a.valid_until??a.expires??0)-(b.valid_until??b.expires??0));
            if(sort==='ff')    filtered.sort((a,b)=>{const fa=s.ffData[Number(a.target_id??a.id)]?.ff??-1;const fb=s.ffData[Number(b.target_id??b.id)]?.ff??-1;return fb-fa;});
            if(!s.bountyFilterHosp)filtered.sort((a,b)=>(this._isHospital(a)?0:1)-(this._isHospital(b)?0:1));
            listEl.innerHTML=this._buildBountyRows(filtered.slice(0,s.bountyDisplayCount||80),now);
        }

        renderEnemy() {
            const body=this.body; if(!body)return;
            const s=this.state,now=Utils.now(),enemies=s.enemies||[];

            const addEnemy=(id,note,groupId)=>{
                if(!id||isNaN(parseInt(id))){hud.toasts.show('⚠','Ungültige ID','Bitte gültige Spieler-ID eingeben','warn',3000);return;}
                hud.fetcher.api.playerProfile(id,(data)=>{
                    const p=data?.profile??data;
                    const name=p?.name??p?.player_name??`#${id}`;
                    if(s.addEnemy(String(id),name,note,groupId||'default')){hud.toasts.show('💀',`Enemy hinzugefügt`,`${name} (ID: ${id})`,'crit',4000);hud.fetcher.fetchEnemyData();this.renderEnemy();}
                    else hud.toasts.show('⚠','Bereits vorhanden',`ID ${id} ist bereits in der Liste`,'warn',3000);
                });
            };

            const renderRow=(enemy)=>{
                const ed=s.enemyData[enemy.id]||{};
                const statusObj=ed.status||{},ss=(statusObj.state||'').toLowerCase();
                const isH=ss==='hospital',isTr=ss==='traveling'||ss==='abroad',isJ=ss==='jail';
                const hospUntil=isH?Number(statusObj.until??0):0,hospSec=hospUntil>now?hospUntil-now:0;
                const level=ed.level??'?',ageDays=Number(ed.age??0);
                const facId=ed._factionId??null,facName=ed._factionName??null;
                const jobCo=ed.job?.company_name??ed.job?.name??null,jobPos=ed.job?.position??null;
                const travelDesc=statusObj.description||'';
                const lastTs=ed.last_action?.timestamp??0;
                const isOnline=(ed.last_action?.status||'').toLowerCase()==='online'||(lastTs>0&&(now-lastTs)<120);
                const lastAgo=lastTs>0?Utils.inactiveAgo(lastTs):null;
                const bountyR=s._getEnemyBountyReward(enemy.id);
                const ffEntry=s.ffData[Number(enemy.id)];
                const ffHtml=`<span data-ff-badge="${enemy.id}">${ffBadge(ffEntry)}</span>`;
                let statusTag='',rowClass='enemy-row';
                if(isH){statusTag=`<span class="enemy-tag etag-hosp">🏥 HOSPITAL${hospSec>0?` · ${Utils.fmtSec(hospSec)}`:''}</span>`;rowClass+=' in-hospital';}
                else if(isTr){statusTag=`<span class="enemy-tag etag-travel">✈ ${Utils.escHtml(travelDesc)||'Reist'}</span>`;rowClass+=' traveling';}
                else if(isJ)statusTag=`<span class="enemy-tag etag-jail">🚔 JAIL</span>`;
                else if(ss)statusTag=`<span class="enemy-tag etag-ok">✓ OKAY</span>`;
                const lastHtml=lastAgo?`<span class="enemy-tag" style="background:rgba(255,255,255,.04);color:${isOnline?'var(--green)':'var(--muted)'};border:1px solid rgba(255,255,255,.08);">⏱ ${isOnline?'Online':lastAgo.text+' ago'}</span>`:'';
                const facTag=facId?`<span class="enemy-tag" style="background:rgba(255,255,255,.05);color:var(--muted);border:1px solid rgba(255,255,255,.08);">⚔ ${Utils.escHtml(facName||'Faction')}</span>`:'';
                const ageHtml=ageDays>0?`<span class="enemy-level" style="color:var(--green);">Age ${ageDays}</span>`:'';
                const bountyHtml=bountyR>0?`<span class="enemy-level" style="color:var(--amber);">🎯 ${Utils.fmtMoney(bountyR)}</span>`:'';
                return`<div class="${rowClass}" data-enemy-id="${enemy.id}">
                    <div class="enemy-top">
                        <a class="enemy-name-link" href="https://www.torn.com/profiles.php?XID=${enemy.id}" target="_blank">${Utils.escHtml(enemy.name)}</a>
                        <span class="enemy-level">Lv ${level}</span>
                        ${ageHtml}${bountyHtml}${ffHtml}
                    </div>
                    <div class="enemy-meta">
                        ${statusTag}${lastHtml}${facTag}
                        ${jobCo?`<span class="enemy-tag etag-job">🏢 ${Utils.escHtml(jobCo)}${jobPos?` · ${Utils.escHtml(jobPos)}`:''}</span>`:''}
                    </div>
                    ${enemy.note?`<div class="enemy-note">📝 ${Utils.escHtml(enemy.note)}</div>`:''}
                    <div class="enemy-actions">
                        <a class="enemy-action-btn eab-attack"  href="https://www.torn.com/page.php?sid=attack&user2ID=${enemy.id}" target="_blank">⚔ Angriff</a>
                        <a class="enemy-action-btn eab-profile" href="https://www.torn.com/profiles.php?XID=${enemy.id}" target="_blank">👤 Profil</a>
                        <button class="enemy-action-btn eab-note"   data-note-enemy="${enemy.id}">📝 Notiz</button>
                        <button class="enemy-action-btn eab-grp"    data-move-enemy="${enemy.id}">📁 Gruppe</button>
                        <button class="enemy-action-btn eab-remove" data-remove-enemy="${enemy.id}">✕</button>
                    </div>
                </div>`;
            };

            const ffKey=hud.store.get('ffKey','');
            const ffKeyHint=!ffKey?`<div style="background:rgba(245,158,11,.08);border:1px solid rgba(245,158,11,.2);border-radius:var(--r);padding:8px 12px;font-size:10px;color:var(--amber);flex-shrink:0;">⚠ Kein FF Scouter Key – FF-Werte nicht verfügbar. Key in Settings eingeben.</div>`:'';
            const groupSelOpts=s.enemyGroups.map(g=>`<option value="${g.id}">${g.name}</option>`).join('');

            let groupedHtml='';
            if(enemies.length>0){
                s.enemyGroups.forEach(grp=>{
                    const grpEnemies=enemies.filter(e=>(e.groupId||'default')===grp.id);
                    if(grpEnemies.length===0)return;
                    groupedHtml+=`<div class="enemy-group">
                        <div class="enemy-group-header" data-toggle-group="${grp.id}">
                            <span class="enemy-group-color-dot" style="background:${grp.color};box-shadow:0 0 5px ${grp.color}80;"></span>
                            <span class="enemy-group-name">${Utils.escHtml(grp.name)}</span>
                            <span class="enemy-group-count">${grpEnemies.length}</span>
                            <span class="enemy-group-arrow ${grp.collapsed?'':'open'}">▶</span>
                        </div>
                        ${grp.collapsed?'':`<div class="enemy-group-body">${grpEnemies.map(e=>renderRow(e)).join('')}</div>`}
                    </div>`;
                });
                const validGroupIds=s.enemyGroups.map(g=>g.id);
                const ungrouped=enemies.filter(e=>!validGroupIds.includes(e.groupId||'default'));
                if(ungrouped.length){
                    groupedHtml+=`<div class="enemy-group"><div class="enemy-group-header"><span class="enemy-group-color-dot" style="background:var(--muted);"></span><span class="enemy-group-name">Ohne Gruppe</span><span class="enemy-group-count">${ungrouped.length}</span></div><div class="enemy-group-body">${ungrouped.map(e=>renderRow(e)).join('')}</div></div>`;
                }
            }

            body.innerHTML=`
            ${ffKeyHint}
            <div class="card" style="flex-shrink:0;">
                <div class="card-header"><span class="card-title red">💀 ENEMY TRACKER</span><div style="display:flex;gap:5px;align-items:center;"><span class="badge badge-red">${enemies.length} Enemies</span><button class="scan-btn" id="thud-enemy-refresh" style="padding:4px 10px;font-size:10px;">${s.enemyFetchRunning?'<span class="spinning">↺</span>':'↺ Refresh'}</button></div></div>
                <div class="card-body" style="padding:10px 14px;gap:7px;">
                    <div style="font-size:10px;color:var(--muted);margin-bottom:2px;">Spieler-ID eingeben:</div>
                    <div class="enemy-add-row">
                        <input type="number" class="enemy-id-input" id="thud-enemy-id" placeholder="Spieler ID (z.B. 123456)" min="1">
                        <select id="thud-enemy-grp-sel" style="background:var(--bg);border:1px solid var(--border);border-radius:var(--r-sm);padding:7px 8px;color:var(--text);font-size:11px;outline:none;flex-shrink:0;">${groupSelOpts}</select>
                        <button class="scan-btn" id="thud-enemy-add" style="white-space:nowrap;">+ Add</button>
                    </div>
                </div>
            </div>
            <div class="card" style="flex-shrink:0;">
                <div class="card-header">
                    <span class="card-title amber">📁 GRUPPEN</span>
                    <button class="scan-btn" id="thud-enemy-new-group" style="padding:4px 10px;font-size:10px;">+ Neue Gruppe</button>
                </div>
                <div class="card-body" style="padding:8px 14px;gap:5px;flex-direction:row;flex-wrap:wrap;">
                    ${s.enemyGroups.map(g=>`<div style="display:flex;align-items:center;gap:5px;background:var(--bg-el);border:1px solid var(--border);border-left:3px solid ${g.color};border-radius:6px;padding:4px 8px;">
                        <span style="font-size:10px;font-weight:700;color:var(--text);">${Utils.escHtml(g.name)}</span>
                        <span style="font-size:9px;color:var(--muted);font-family:var(--mono);">${enemies.filter(e=>(e.groupId||'default')===g.id).length}</span>
                        ${g.id!=='default'?`<button class="enemy-group-del-btn" data-del-group="${g.id}" style="padding:1px 5px;font-size:9px;">✕</button>`:''}
                    </div>`).join('')}
                </div>
            </div>
            ${enemies.length===0?`<div class="empty-state"><div class="es-icon">💀</div><div class="es-text">Keine Enemies</div><div class="es-sub">Füge Spieler-IDs hinzu um sie zu tracken</div></div>`:`<div class="card" style="flex-shrink:0;"><div class="card-header"><span class="card-title amber">⚠ ENEMY LIST</span><span style="font-size:9px;color:var(--muted);">Auto-Refresh 30s</span></div><div class="card-body p0" id="thud-enemy-groups">${groupedHtml}</div></div>`}
            <div style="background:rgba(239,68,68,.06);border:1px solid rgba(239,68,68,.15);border-radius:var(--r);padding:9px 12px;font-size:10px;color:var(--muted);flex-shrink:0;">⚠ Enemy-Reise-Alarm → <b style="color:var(--text);">Alarme → Enemy reist ab</b></div>`;

            document.getElementById('thud-enemy-refresh')?.addEventListener('click',()=>{hud.fetcher.fetchEnemyData();this.renderEnemy();});
            document.getElementById('thud-enemy-add')?.addEventListener('click',()=>{
                const id=document.getElementById('thud-enemy-id')?.value?.trim();
                const grp=document.getElementById('thud-enemy-grp-sel')?.value||'default';
                addEnemy(id,'',grp);
            });
            document.getElementById('thud-enemy-id')?.addEventListener('keydown',e=>{if(e.key==='Enter'){const id=e.target.value?.trim(),grp=document.getElementById('thud-enemy-grp-sel')?.value||'default';addEnemy(id,'',grp);}});
            document.getElementById('thud-enemy-new-group')?.addEventListener('click',()=>{
                const modal=document.createElement('div'); modal.className='thud-modal-bg';
                const swatches=GROUP_COLORS.map((c,i)=>`<span class="group-color-swatch ${i===0?'selected':''}" style="background:${c};" data-color="${c}"></span>`).join('');
                modal.innerHTML=`<div class="thud-modal"><h3>📁 Neue Gruppe</h3><p>Name und Farbe für die neue Enemy-Gruppe:</p><input id="thud-grp-name" placeholder="z.B. Revanche, War Targets..." /><div class="group-color-picker" id="thud-grp-colors">${swatches}</div><div class="modal-btns" style="margin-top:12px;"><button class="modal-cancel" id="thud-grp-cancel">Abbrechen</button><button class="modal-save" id="thud-grp-save">Erstellen</button></div></div>`;
                document.body.appendChild(modal);
                let selectedColor=GROUP_COLORS[0];
                modal.querySelectorAll('.group-color-swatch').forEach(sw=>{
                    sw.addEventListener('click',()=>{modal.querySelectorAll('.group-color-swatch').forEach(s=>s.classList.remove('selected'));sw.classList.add('selected');selectedColor=sw.dataset.color;});
                });
                document.getElementById('thud-grp-save').onclick=()=>{
                    const name=document.getElementById('thud-grp-name')?.value?.trim();
                    if(!name){hud.toasts.show('⚠','Name fehlt','Bitte Gruppenname eingeben','warn',2000);return;}
                    s.addEnemyGroup(name,selectedColor);modal.remove();this.renderEnemy();
                };
                document.getElementById('thud-grp-cancel').onclick=()=>modal.remove();
                modal.addEventListener('click',e=>{if(e.target===modal)modal.remove();});
            });
            body.querySelectorAll('[data-del-group]').forEach(btn=>btn.addEventListener('click',()=>{
                const gid=btn.dataset.delGroup;
                const grp=s.enemyGroups.find(g=>g.id===gid);
                if(confirm(`Gruppe "${grp?.name}" löschen? Enemies werden zu "Allgemein" verschoben.`)){s.removeEnemyGroup(gid);this.renderEnemy();}
            }));
            body.querySelectorAll('[data-toggle-group]').forEach(btn=>btn.addEventListener('click',()=>{s.toggleGroupCollapse(btn.dataset.toggleGroup);this.renderEnemy();}));
            body.querySelectorAll('[data-remove-enemy]').forEach(btn=>btn.addEventListener('click',()=>{const id=btn.dataset.removeEnemy;const en=s.enemies.find(e=>String(e.id)===String(id));if(confirm(`Enemy "${en?.name||id}" wirklich entfernen?`)){s.removeEnemy(id);this.renderEnemy();}}));
            body.querySelectorAll('[data-move-enemy]').forEach(btn=>btn.addEventListener('click',()=>{
                const id=btn.dataset.moveEnemy,en=s.enemies.find(e=>String(e.id)===String(id));
                const modal=document.createElement('div');modal.className='thud-modal-bg';
                const opts=s.enemyGroups.map(g=>`<button class="sort-btn" data-assign-group="${g.id}" style="border-left:3px solid ${g.color};${(en?.groupId||'default')===g.id?'background:rgba(99,102,241,.15);border-color:var(--accent);color:var(--accent2);':''}margin-bottom:5px;text-align:left;padding:7px 10px;width:100%;">${g.name} (${s.enemies.filter(e=>(e.groupId||'default')===g.id).length})</button>`).join('');
                modal.innerHTML=`<div class="thud-modal"><h3>📁 Gruppe für ${Utils.escHtml(en?.name||id)}</h3><div style="display:flex;flex-direction:column;gap:3px;margin-bottom:12px;">${opts}</div><div class="modal-btns"><button class="modal-cancel" id="thud-grp-mv-cancel">Abbrechen</button></div></div>`;
                document.body.appendChild(modal);
                modal.querySelectorAll('[data-assign-group]').forEach(b=>b.addEventListener('click',()=>{s.updateEnemyGroup(id,b.dataset.assignGroup);modal.remove();this.renderEnemy();}));
                document.getElementById('thud-grp-mv-cancel').onclick=()=>modal.remove();
                modal.addEventListener('click',e=>{if(e.target===modal)modal.remove();});
            }));
            body.querySelectorAll('[data-note-enemy]').forEach(btn=>btn.addEventListener('click',()=>{const id=btn.dataset.noteEnemy;const en=s.enemies.find(e=>String(e.id)===String(id));const modal=document.createElement('div');modal.className='thud-modal-bg';modal.innerHTML=`<div class="thud-modal"><h3>📝 Notiz für ${Utils.escHtml(en?.name||id)}</h3><p>Füge eine persönliche Notiz hinzu:</p><textarea id="thud-note-input" placeholder="Deine Notiz...">${Utils.escHtml(en?.note||'')}</textarea><div class="modal-btns"><button class="modal-cancel" id="thud-note-cancel">Abbrechen</button><button class="modal-save" id="thud-note-save">Speichern</button></div></div>`;document.body.appendChild(modal);document.getElementById('thud-note-save').onclick=()=>{s.updateEnemyNote(id,document.getElementById('thud-note-input').value);modal.remove();this.renderEnemy();};document.getElementById('thud-note-cancel').onclick=()=>modal.remove();modal.addEventListener('click',e=>{if(e.target===modal)modal.remove();}); }));
            if(ffKey&&enemies.length){
                const ids=enemies.map(e=>Number(e.id));
                getFFData(ids,ffKey,(ffResults)=>{
                    s.ffData={...s.ffData,...ffResults};
                    ids.forEach(id=>{const el=document.querySelector(`[data-ff-badge="${id}"]`);if(el)el.innerHTML=ffBadge(ffResults[id]);});
                });
            }
        }

        renderCompany() {
            const body=this.body; if(!body)return;
            const s=this.state;
            const cp=s.companyProfile||null;
            const employees=s.companyEmployees||[];
            const ws=s.workStats||null;

            const wsHtml=ws?`<div class="card" style="flex-shrink:0;"><div class="card-header"><span class="card-title blue">💼 DEINE WORKING STATS</span></div><div class="card-body"><div class="stat-grid stat-grid-3"><div class="stat-card"><div class="sv sv-amber">${Utils.fmtStat(ws.manual)}</div><div class="sl">🛠 Manual</div></div><div class="stat-card"><div class="sv sv-blue">${Utils.fmtStat(ws.intelligence)}</div><div class="sl">🧠 Intel</div></div><div class="stat-card"><div class="sv sv-green">${Utils.fmtStat(ws.endurance)}</div><div class="sl">🏃 Endure</div></div></div></div></div>`:'';

            if(!cp){
                body.innerHTML=`${wsHtml}<div class="empty-state"><div class="es-icon">🏢</div><div class="es-text">Keine Firmendaten</div><div class="es-sub">Du musst Inhaber oder Direktor sein.</div></div>`;
                return;
            }

            const name=cp.name||'—',type=cp.type?.name||(typeof cp.type==='string'?cp.type:'')||cp.company_type||'—',rating=cp.rating??'—';
            const _s=(v)=>{if(v===null||v===undefined)return null;if(typeof v==='number')return v;if(typeof v==='object')return v.value??v.current??v.amount??null;return null;};
            const dailyIncome=Number(cp.income?.daily??cp.daily_income??cp.daily_revenue??0);
            const weeklyProfit=Number(cp.income?.weekly??cp.weekly_profit??0);
            const empHired=cp.employees?.hired??cp.employees_hired??employees.length??0;
            const empCap=cp.employees?.capacity??cp.employees_capacity??'—';
            const effs=employees.map(e=>Number(e.effectiveness?.working_stats?.total??e.effectiveness?.total??e.working_stats?.total??0)).filter(v=>v>0);
            const avgEff=effs.length?Math.round(effs.reduce((a,v)=>a+v,0)/effs.length):(Number(_s(cp.efficiency))||0);

            body.innerHTML=`${wsHtml}
            <div class="card" style="flex-shrink:0;"><div class="card-header"><span class="card-title amber">🏢 ${Utils.escHtml(name)}</span><span class="badge badge-amber">${Utils.escHtml(String(type))}</span></div>
            <div class="card-body"><div class="company-kpi-grid"><div class="company-kpi"><div class="kv" style="color:var(--green);">${Utils.fmtMoney(dailyIncome)}</div><div class="kl">Täglich</div></div><div class="company-kpi"><div class="kv" style="color:var(--amber);">${Utils.fmtMoney(weeklyProfit)}</div><div class="kl">Wöchentlich</div></div><div class="company-kpi"><div class="kv" style="color:var(--blue);">${empHired}/${empCap}</div><div class="kl">Mitarbeiter</div></div><div class="company-kpi"><div class="kv" style="color:${avgEff>=60?'var(--green)':avgEff>=30?'var(--amber)':'var(--muted)'};">${avgEff>0?avgEff+'%':'—'}</div><div class="kl">Effizienz Ø</div></div></div>${rating!=='—'?`<div class="row"><span class="row-label">⭐ Rating</span><span class="row-value" style="color:var(--amber);">${rating} / 10</span></div>`:''}</div></div>
            ${employees.length>0?`<div class="card" style="flex-shrink:0;"><div class="card-header"><span class="card-title green">👷 MITARBEITER</span><span class="badge badge-green">${avgEff>0?avgEff+'%':'—'} Ø</span></div><div class="card-body p0">${employees.slice(0,40).map(e=>{const eff=Number(e.effectiveness?.working_stats?.total??e.effectiveness?.total??e.working_stats?.total??0);const pos=(()=>{const p=e.position??e.role??null;if(!p)return'—';if(typeof p==='string')return p;if(typeof p==='object')return p.name??p.title??'—';return'—';})();const online=((Date.now()/1000-(e.last_action?.timestamp||0))<120);const ec=eff>=60?'var(--green)':eff>=30?'var(--amber)':eff>0?'var(--red)':'var(--muted)';return`<div class="company-emp-row"><span class="status-dot sd-${online?'online':'offline'}" style="flex-shrink:0;"></span><span class="company-emp-name">${Utils.escHtml(e.name||`#${e.id}`)}</span><span class="company-emp-pos">${Utils.escHtml(pos)}</span><span class="company-emp-eff" style="color:${ec};">${eff>0?eff+'%':'—'}</span></div>`;}).join('')}</div></div>`:''}`;
        }

        renderNetworth() {
            const body=this.body; if(!body)return;
            const nw=this.state.networthData,hist=this.state.networthHistory;
            if(!nw){body.innerHTML=`<div class="empty-state"><div class="es-icon">💰</div><div class="es-text">Lade Vermögensdaten...</div><div class="es-sub">Benötigt Full Access API Key</div></div>`;return;}
            const now2=Utils.now(),oh=hist.filter(h=>h.ts>=now2-3600);
            const change=oh.length>=2?hist[hist.length-1].val-oh[0].val:0;
            const wallet=Number(nw.wallet??nw.cash??0),bank=Number(nw.bank??0),stocks=Number(nw.stockmarket??nw.stocks??0);
            const totalLiquid=wallet+stocks;
            const bazaar=Number(nw.bazaar??0),props=Number(nw.properties??0),piggy=Number(nw.piggybank??0),fvault=Number(nw.factionvault??0);
            body.innerHTML=`
            <div class="nw-ticker">
                <div style="font-size:9px;color:var(--muted);text-transform:uppercase;letter-spacing:1px;margin-bottom:4px;">LIQUIDES VERMÖGEN (Cash + Aktien)</div>
                <div style="display:flex;align-items:baseline;gap:10px;">
                    <div class="nw-main-val">${Utils.fmtMoney(totalLiquid)}</div>
                    ${change!==0?`<div class="nw-change ${change>0?'pos':'neg'}">${change>0?'▲':'▼'} ${Utils.fmtMoney(Math.abs(change))} / 1h</div>`:'<div class="nw-change" style="color:var(--muted);">± —</div>'}
                </div>
            </div>
            <div class="card" style="flex-shrink:0;">
                <div class="card-header"><span class="card-title green">💵 LIQUIDITÄT</span></div>
                <div class="card-body">
                    <div class="row"><span class="row-label">💵 Cash</span><span class="row-value" style="color:var(--green);">${Utils.fmtMoney(wallet)}</span></div>
                    <div class="row"><span class="row-label">📈 Aktien</span><span class="row-value" style="color:var(--amber);">${Utils.fmtMoney(stocks)}</span></div>
                    <div class="row"><span class="row-label">🏦 Bank</span><span class="row-value" style="color:var(--muted);">${Utils.fmtMoney(bank)}</span></div>
                </div>
            </div>
            ${(bazaar||props||piggy||fvault)?`<div class="card" style="flex-shrink:0;"><div class="card-header"><span class="card-title">📦 WEITERE ASSETS</span></div><div class="card-body">${bazaar?`<div class="row"><span class="row-label">🏪 Bazaar</span><span class="row-value">${Utils.fmtMoney(bazaar)}</span></div>`:''}${props?`<div class="row"><span class="row-label">🏠 Immobilien</span><span class="row-value">${Utils.fmtMoney(props)}</span></div>`:''}${piggy?`<div class="row"><span class="row-label">🐷 Sparschwein</span><span class="row-value">${Utils.fmtMoney(piggy)}</span></div>`:''}${fvault?`<div class="row"><span class="row-label">🏛 Faction Vault</span><span class="row-value">${Utils.fmtMoney(fvault)}</span></div>`:''}</div></div>`:''} 
            ${hist.length>=2?`<div class="card" style="flex-shrink:0;"><div class="card-header"><span class="card-title green">📈 VERLAUF (Liquide)</span><span style="font-size:9px;color:var(--muted);">${hist.length} Datenpunkte</span></div><div class="card-body" style="padding:12px 14px 16px;">${this._richChart(hist)}</div></div>`:''}`;
            this._attachChartTooltip();
        }

        _richChart(hist) {
            if(hist.length<2)return'';
            const W=280,H=80,PAD_L=44,PAD_B=18,PAD_T=8,PAD_R=8;
            const innerW=W-PAD_L-PAD_R,innerH=H-PAD_T-PAD_B;
            const vals=hist.map(h=>h.val);
            const mn=Math.min(...vals),mx=Math.max(...vals),range=mx-mn||1;
            const rising=vals[vals.length-1]>=vals[0];
            const lineColor=rising?'var(--green)':'var(--red)';
            const fillColor=rising?'rgba(16,185,129,.12)':'rgba(239,68,68,.12)';
            let gridLines='',gridLabels='';
            for(let i=0;i<=3;i++){
                const frac=i/3;
                const yVal=mn+frac*range;
                const yPx=PAD_T+innerH-(frac*innerH);
                gridLines+=`<line x1="${PAD_L}" y1="${yPx.toFixed(1)}" x2="${W-PAD_R}" y2="${yPx.toFixed(1)}" stroke="rgba(255,255,255,.05)" stroke-width="1"/>`;
                gridLabels+=`<text x="${PAD_L-4}" y="${(yPx+3).toFixed(1)}" text-anchor="end" class="nw-chart-grid-lbl">${Utils.fmtMoney(yVal)}</text>`;
            }
            let timeLabels='';
            if(hist.length>1){
                const fmt=(ts)=>new Date(ts*1000).toLocaleTimeString('de-DE',{hour:'2-digit',minute:'2-digit'});
                const pts=[[0,PAD_L],[Math.floor(hist.length/2),PAD_L+innerW/2],[hist.length-1,W-PAD_R]];
                const anchors=['start','middle','end'];
                pts.forEach(([idx,x],i)=>{
                    timeLabels+=`<text x="${x.toFixed(1)}" y="${H-2}" text-anchor="${anchors[i]}" class="nw-chart-grid-lbl">${fmt(hist[idx].ts)}</text>`;
                });
            }
            const toX=(i)=>PAD_L+(i/(hist.length-1))*innerW;
            const toY=(v)=>PAD_T+innerH-((v-mn)/range)*innerH;
            const pts2=hist.map((h,i)=>`${toX(i).toFixed(1)},${toY(h.val).toFixed(1)}`).join(' ');
            const fillPath=`M ${toX(0).toFixed(1)},${toY(hist[0].val).toFixed(1)} `+
                hist.map((h,i)=>`L ${toX(i).toFixed(1)},${toY(h.val).toFixed(1)}`).join(' ')+
                ` L ${toX(hist.length-1).toFixed(1)},${(PAD_T+innerH).toFixed(1)} L ${toX(0).toFixed(1)},${(PAD_T+innerH).toFixed(1)} Z`;
            const hoverRects=hist.map((h,i)=>{
                const x=toX(i),y=toY(h.val);
                const hw=innerW/hist.length;
                return`<rect x="${(x-hw/2).toFixed(1)}" y="${PAD_T}" width="${hw.toFixed(1)}" height="${innerH}"
                    fill="transparent" class="nw-chart-hover"
                    data-val="${h.val}" data-ts="${h.ts}" data-x="${x.toFixed(1)}" data-y="${y.toFixed(1)}"/>`;
            }).join('');
            const lastX=toX(hist.length-1),lastY=toY(hist[hist.length-1].val);
            return`<div class="nw-chart-wrap" id="nw-chart-wrap">
                <svg viewBox="0 0 ${W} ${H}" class="nw-chart-svg" id="nw-chart-svg">
                    ${gridLines}${gridLabels}${timeLabels}
                    <path d="${fillPath}" fill="${fillColor}"/>
                    <polyline points="${pts2}" fill="none" stroke="${lineColor}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
                    <circle cx="${lastX.toFixed(1)}" cy="${lastY.toFixed(1)}" r="3.5" fill="${lineColor}" stroke="var(--bg-card)" stroke-width="1.5"/>
                    <line id="nw-chart-vline" x1="0" y1="${PAD_T}" x2="0" y2="${PAD_T+innerH}" stroke="rgba(255,255,255,.15)" stroke-width="1" stroke-dasharray="3,2" display="none"/>
                    <circle id="nw-chart-dot" cx="0" cy="0" r="4" fill="${lineColor}" stroke="var(--bg-card)" stroke-width="1.5" display="none"/>
                    ${hoverRects}
                </svg>
                <div class="nw-chart-tooltip" id="nw-chart-tooltip"></div>
            </div>`;
        }

        _attachChartTooltip() {
            setTimeout(()=>{
                const svg=document.getElementById('nw-chart-svg');
                const tooltip=document.getElementById('nw-chart-tooltip');
                const vline=document.getElementById('nw-chart-vline');
                const dot=document.getElementById('nw-chart-dot');
                if(!svg||!tooltip)return;
                svg.querySelectorAll('.nw-chart-hover').forEach(rect=>{
                    rect.addEventListener('mouseenter',(e)=>{
                        const val=Number(rect.dataset.val),ts=Number(rect.dataset.ts);
                        const x=rect.dataset.x,y=rect.dataset.y;
                        if(vline){vline.setAttribute('x1',x);vline.setAttribute('x2',x);vline.removeAttribute('display');}
                        if(dot){dot.setAttribute('cx',x);dot.setAttribute('cy',y);dot.removeAttribute('display');}
                        tooltip.style.display='block';
                        tooltip.innerHTML=`<div style="font-weight:700;color:var(--amber);">${Utils.fmtMoney(val)}</div><div style="color:var(--muted);font-size:9px;">${new Date(ts*1000).toLocaleTimeString('de-DE',{hour:'2-digit',minute:'2-digit'})}</div>`;
                        const svgW=svg.getBoundingClientRect().width||280;
                        const pxX=(Number(x)/280)*svgW;
                        tooltip.style.left=Math.min(pxX+6,svgW-80)+'px';
                        tooltip.style.top='4px';
                    });
                    rect.addEventListener('mouseleave',()=>{
                        tooltip.style.display='none';
                        if(vline)vline.setAttribute('display','none');
                        if(dot)dot.setAttribute('display','none');
                    });
                });
            },100);
        }

        renderNotifications() {
            const body=this.body; if(!body)return;
            const s=this.state;
            const ag=(title,keys)=>{const rows=keys.filter(k=>ALARM_DEFAULTS[k]).map(key=>{const def=ALARM_DEFAULTS[key],on=s.isAlarm(key);return`<div class="alarm-row-item"><div style="flex:1;min-width:0;"><div class="alarm-lbl">${def.label}</div><div class="alarm-desc">${def.desc}</div></div><label class="toggle"><input type="checkbox" data-alarm="${key}" ${on?'checked':''}><span class="toggle-slider"></span></label></div>`;}).join('');return`<div class="settings-section"><div class="settings-section-title">${title}</div>${rows}</div>`;};
            const eT=(s.energyThresholds||[50,75,100]).join(', '),nT=(s.nerveThresholds||[50,100]).join(', ');
            body.innerHTML=`
            <div class="card" style="flex-shrink:0;"><div class="card-body"><div style="display:flex;gap:6px;margin-bottom:12px;flex-wrap:wrap;"><button class="thud-btn ${s.store.get('sound',true)?'active':''}" id="thud-toggle-sound">${s.store.get('sound',true)?'🔊 Sound AN':'🔇 Sound AUS'}</button><button class="thud-btn ${s.store.get('notif',true)?'active':''}" id="thud-toggle-notif">${s.store.get('notif',true)?'🔔 Toasts AN':'🔕 Toasts AUS'}</button></div><div class="settings-section-title">SOUND-PROFIL</div><div class="sound-profile-grid">${['classic','military','subtle'].map(p=>`<button class="sp-btn ${s.store.get('soundProfile','classic')===p?'active':''}" data-sp="${p}">${{classic:'🎵 Classic',military:'🪖 Military',subtle:'🔉 Subtle'}[p]}</button>`).join('')}</div></div></div>
            <div class="card" style="flex-shrink:0;">${ag('✈ TRAVEL',['travel_warn','travel_crit','landing_timer'])}${ag('⚡ ENERGIE',['energy_full','energy_thresh','energy_over151'])}${ag('🧠 NERVE',['nerve_full','nerve_thresh'])}${ag('❤ HP',['hp_crit'])}${ag('⏱ COOLDOWNS',['hospital_free','jail_free','drug_ready','booster_ready','medical_ready'])}${ag('⛓ CHAIN',['chain_warn'])}${ag('💀 ENEMY',['enemy_travel'])}</div>
            <div class="card" style="flex-shrink:0;"><div class="card-body" style="padding:12px 14px;gap:10px;"><div class="settings-section-title" style="border:none;padding:0 0 8px;">⚡ ENERGIE SCHWELLWERTE</div><div style="font-size:11px;color:var(--muted);margin-bottom:4px;">Alarme bei diesen % Werten · kein Alarm über ${HAPPY_JUMP_THRESHOLD}</div><div style="display:flex;gap:8px;align-items:center;"><input type="text" class="threshold-input" id="thud-e-thresh" style="width:130px;" value="${eT}" placeholder="50,75,100"><button class="sort-btn" id="thud-e-thresh-save">✓ Speichern</button></div></div></div>
            <div class="card" style="flex-shrink:0;"><div class="card-body" style="padding:12px 14px;gap:10px;"><div class="settings-section-title" style="border:none;padding:0 0 8px;">🧠 NERVE SCHWELLWERTE</div><div style="display:flex;gap:8px;align-items:center;"><input type="text" class="threshold-input" id="thud-n-thresh" style="width:130px;" value="${nT}" placeholder="50,100"><button class="sort-btn" id="thud-n-thresh-save">✓ Speichern</button></div></div></div>
            <div class="card" style="flex-shrink:0;"><div class="card-body"><div class="settings-section-title">TRAVEL ALERT TYP</div>${[['toast','🔔 Toast'],['fullscreen','🖥 Vollbild']].map(([k,lbl])=>`<div class="alarm-row-item"><div class="alarm-lbl">${lbl}</div><label class="toggle"><input type="checkbox" data-ta="${k}" ${s.isTravelAlert(k)?'checked':''}><span class="toggle-slider"></span></label></div>`).join('')}</div></div>`;
            body.querySelectorAll('[data-alarm]').forEach(el=>el.addEventListener('change',e=>s.setAlarm(e.target.dataset.alarm,e.target.checked)));
            body.querySelectorAll('[data-ta]').forEach(el=>el.addEventListener('change',e=>s.setTravelAlert(e.target.dataset.ta,e.target.checked)));
            body.querySelectorAll('[data-sp]').forEach(btn=>btn.addEventListener('click',()=>{hud.store.set('soundProfile',btn.dataset.sp);hud.audio.play('notify');this.renderNotifications();}));
            document.getElementById('thud-toggle-sound')?.addEventListener('click',()=>{hud.store.set('sound',!hud.store.get('sound',true));this.renderNotifications();});
            document.getElementById('thud-toggle-notif')?.addEventListener('click',()=>{hud.store.set('notif',!hud.store.get('notif',true));this.renderNotifications();});
            document.getElementById('thud-e-thresh-save')?.addEventListener('click',()=>{const raw=document.getElementById('thud-e-thresh')?.value||'';const arr=raw.split(',').map(x=>parseInt(x.trim())).filter(x=>!isNaN(x)&&x>0&&x<=100);s.setEnergyThresholds([...new Set(arr)].sort((a,b)=>a-b));hud.toasts.show('✅','Energie-Schwellwerte gespeichert',arr.join(', ')+'%','info',3000);});
            document.getElementById('thud-n-thresh-save')?.addEventListener('click',()=>{const raw=document.getElementById('thud-n-thresh')?.value||'';const arr=raw.split(',').map(x=>parseInt(x.trim())).filter(x=>!isNaN(x)&&x>0&&x<=100);s.setNerveThresholds([...new Set(arr)].sort((a,b)=>a-b));hud.toasts.show('✅','Nerve-Schwellwerte gespeichert',arr.join(', ')+'%','info',3000);});
        }

        renderSettings() {
            const body=this.body; if(!body)return;
            const s=this.state,fabPinned=hud.store.get('fabPinned',false);
            const ffKey=hud.store.get('ffKey','');
            const visRows=Object.entries(VISIBILITY_DEFAULTS).map(([k,v])=>`<div class="vis-row" style="margin-bottom:3px;"><span class="vis-lbl">${v.label}</span><label class="toggle"><input type="checkbox" data-vis="${k}" ${s.isVisible(k)?'checked':''}><span class="toggle-slider"></span></label></div>`).join('');
            const curLinks=hud.store.get('customQuickLinks',null)||[{label:'Stock\nMarket',icon:'📈',url:'https://www.torn.com/page.php?sid=stocks'},{label:'Casino',icon:'🎰',url:'https://www.torn.com/page.php?sid=slots'},{label:'Bazaar',icon:'🏪',url:'https://www.torn.com/bazaar.php?userId=0'},{label:'Forum',icon:'💬',url:'https://www.torn.com/forums.php'}];
            body.innerHTML=`
            <div class="card" style="flex-shrink:0;"><div class="card-body"><div class="settings-section-title">THEME</div><div class="theme-grid">${Object.entries(THEMES).map(([k,t])=>`<button class="theme-btn ${hud.store.get('theme','obsidian')===k?'active':''}" data-theme="${k}">${t.name}</button>`).join('')}</div></div></div>
            <div class="card" style="flex-shrink:0;"><div class="card-header"><span class="card-title red">🔴 FAB BUTTON</span></div><div class="card-body" style="padding:4px 0 8px;gap:0;"><div class="fab-pin-row"><div><div class="fab-pin-lbl">📌 Position fixieren</div><div class="fab-pin-desc">Button lässt sich nicht mehr verschieben</div></div><label class="toggle"><input type="checkbox" id="thud-fab-pin" ${fabPinned?'checked':''}><span class="toggle-slider"></span></label></div><div style="padding:7px 14px;display:flex;gap:6px;align-items:center;"><button class="sort-btn" id="thud-fab-reset">↺ Position zurücksetzen</button></div></div></div>
            <div class="card" style="flex-shrink:0;"><div class="card-header"><span class="card-title accent">📊 FF SCOUTER</span><span class="badge badge-${ffKey?'green':'red'}">${ffKey?'Aktiv':'Kein Key'}</span></div><div class="card-body" style="padding:12px 14px;gap:8px;"><div style="font-size:11px;color:var(--muted);line-height:1.6;">Für FF-Werte (FairFight) in Enemy &amp; Bounty Tab. Key von <a href="https://ffscouter.com" target="_blank" style="color:var(--accent2);">ffscouter.com</a> — dort "FFScouter3" Key verwenden.</div><div style="display:flex;gap:8px;align-items:center;"><input type="text" id="thud-ff-key-input" placeholder="FF Scouter Limited API Key..." value="${Utils.escHtml(ffKey)}" style="flex:1;background:var(--bg);border:1px solid var(--border);border-radius:var(--r-sm);padding:7px 10px;color:var(--text);font-family:var(--mono);font-size:11px;outline:none;"><button class="sort-btn" id="thud-ff-key-save" style="border-color:rgba(16,185,129,.3);color:var(--green);">✓ Speichern</button></div>${ffKey?`<button class="sort-btn" id="thud-ff-cache-clear" style="color:var(--muted);font-size:10px;">🗑 FF Cache leeren</button>`:''}</div></div>
            <div class="card" style="flex-shrink:0;"><div class="card-body"><div class="settings-section-title">SICHTBARE ELEMENTE</div><div class="vis-grid">${visRows}</div></div></div>
            <div class="card" style="flex-shrink:0;"><div class="card-body" style="gap:8px;"><div class="settings-section-title">🔗 QUICK LINKS KONFIGURIEREN</div><div style="font-size:10px;color:var(--muted);margin-bottom:4px;">JSON-Array mit label, icon, url.</div><textarea id="thud-ql-json" style="width:100%;background:var(--bg);border:1px solid var(--border);border-radius:var(--r-sm);padding:8px;color:var(--text);font-family:var(--mono);font-size:10px;min-height:120px;resize:vertical;">${Utils.escHtml(JSON.stringify(curLinks,null,2))}</textarea><div style="display:flex;gap:6px;"><button class="sort-btn" id="thud-ql-save" style="border-color:rgba(16,185,129,.3);color:var(--green);">✓ Speichern</button><button class="sort-btn" id="thud-ql-reset" style="color:var(--muted);">↺ Standard</button></div></div></div>
            <div class="card" style="flex-shrink:0;"><div class="card-body"><div class="settings-section-title">TORN API KEY</div><div class="api-key-row"><span class="api-key-status ${hud.store.get('apiKey')?'set':'unset'}">${hud.store.get('apiKey')?'✓ API Key gesetzt':'⚠ Kein API Key'}</span><button class="thud-btn" id="thud-api-key-btn">🔑 Ändern</button></div></div></div>
            <div class="card" style="flex-shrink:0;"><div class="card-body"><div class="settings-section-title">TASTENKÜRZEL</div><div style="display:grid;grid-template-columns:1fr 1fr;gap:6px;font-size:11px;color:var(--dim);"><span><kbd style="background:var(--bg-el);border:1px solid var(--border);border-radius:3px;padding:1px 5px;font-family:var(--mono);font-size:9px;">M</kbd> Minimieren</span><span><kbd style="background:var(--bg-el);border:1px solid var(--border);border-radius:3px;padding:1px 5px;font-family:var(--mono);font-size:9px;">H</kbd> Ausblenden</span><span><kbd style="background:var(--bg-el);border:1px solid var(--border);border-radius:3px;padding:1px 5px;font-family:var(--mono);font-size:9px;">R</kbd> Refresh</span><span><kbd style="background:var(--bg-el);border:1px solid var(--border);border-radius:3px;padding:1px 5px;font-family:var(--mono);font-size:9px;">S</kbd> Settings</span></div></div></div>
            <div class="ts" style="text-align:center;flex-shrink:0;">${SCRIPT_TITLE} v${VERSION} · by xShaYaKaZ</div>`;

            document.getElementById('thud-ff-key-save')?.addEventListener('click',()=>{const val=document.getElementById('thud-ff-key-input')?.value?.trim()||'';hud.store.set('ffKey',val);hud.toasts.show('✅','FF Key gespeichert',val?'FF-Werte werden ab jetzt geladen.':'Key entfernt.','info',3000);this.renderSettings();});
            document.getElementById('thud-ff-cache-clear')?.addEventListener('click',()=>{ffCache.clear();hud.state.ffData={};hud.toasts.show('🗑','FF Cache geleert','Daten werden neu geladen','info',3000);});
            body.querySelectorAll('[data-theme]').forEach(btn=>btn.addEventListener('click',()=>{hud.store.set('theme',btn.dataset.theme);applyTheme(btn.dataset.theme);this.renderSettings();}));
            body.querySelectorAll('[data-vis]').forEach(el=>el.addEventListener('change',e=>s.setVisible(e.target.dataset.vis,e.target.checked)));
            document.getElementById('thud-api-key-btn')?.addEventListener('click',openApiModal);
            document.getElementById('thud-fab-pin')?.addEventListener('change',e=>hud.store.set('fabPinned',e.target.checked));
            document.getElementById('thud-fab-reset')?.addEventListener('click',()=>{const fab=document.getElementById('thud-fab');if(fab){fab.style.left='10px';fab.style.top='10px';}hud.store.set('fabPos',{l:10,t:10});hud.toasts.show('✅','FAB zurückgesetzt','Links oben','info',2000);});
            document.getElementById('thud-ql-save')?.addEventListener('click',()=>{try{const raw=document.getElementById('thud-ql-json')?.value||'[]';const parsed=JSON.parse(raw);if(!Array.isArray(parsed))throw new Error('Kein Array');hud.store.set('customQuickLinks',parsed);hud.toasts.show('✅','Quick Links gespeichert',`${parsed.length} Links`,'info',3000);this.renderSettings();}catch(e){hud.toasts.show('⚠','JSON Fehler',e.message,'warn',4000);}});
            document.getElementById('thud-ql-reset')?.addEventListener('click',()=>{hud.store.set('customQuickLinks',null);hud.toasts.show('✅','Quick Links zurückgesetzt','Standard wiederhergestellt','info',3000);this.renderSettings();});
        }

        renderTravelItems() {
            const body=this.body; if(!body)return;
            const s=this.state;
            const filter=s.travelItemsFilter||'all';

            // ── Loading state ──
            if(s.travelItemsLoading){
                body.innerHTML=`<div class="ti-hero"><div class="ti-hero-icon">🌍</div><div class="ti-hero-main"><div class="ti-hero-title">TRAVEL ITEMS</div><div class="ti-hero-sub">Lade Daten von DroqsDB…</div></div></div><div class="loading-state">LADE…</div>`;
                return;
            }

            // ── No data yet → trigger fetch ──
            if(s.travelItemsProfits===null&&s.travelItemsRestocks===null){
                body.innerHTML=`<div class="ti-hero"><div class="ti-hero-icon">🌍</div><div class="ti-hero-main"><div class="ti-hero-title">TRAVEL ITEMS</div><div class="ti-hero-sub">Daten werden geladen…</div></div></div><div class="loading-state">INITIALISIERE…</div>`;
                hud.fetcher.fetchTravelItems(()=>this.renderTravelItems());
                return;
            }

            const profits=s.travelItemsProfits||[];
            const restocks=s.travelItemsRestocks||[];
            const now=Utils.now();
            const ts=s.travelItemsLastFetch?new Date(s.travelItemsLastFetch*1000).toLocaleTimeString('de-DE'):'—';

            // ── Filter buttons ──
            const filterBtns=['all','xanax','plushie','flower'].map(f=>`<button class="sort-btn ${filter===f?'active':''}" data-ti-filter="${f}">${{all:'Alle',xanax:'💊 Xanax',plushie:'🧸 Plüschi',flower:'🌸 Blumen'}[f]}</button>`).join('');

            // ── Filter profits ──
            const filteredProfits=profits.filter(item=>{
                const name=item.item_name||item.name||'';
                if(filter==='all')return true;
                return getTiCategory(name)===filter;
            });

            // ── Build profit rows ──
            const profitRows=filteredProfits.length===0
                ?`<div class="ti-empty"><div class="ti-empty-icon">📭</div><div class="ti-empty-txt">Keine Items für diesen Filter</div></div>`
                :filteredProfits.map(item=>{
                    const name=item.item_name||item.name||'Unbekannt';
                    const cat=getTiCategory(name);
                    const icon=TI_ICONS[cat]||'📦';
                    const country=item.country||item.destination||'—';
                    const destInfo=DESTINATIONS[country]||{flag:'🌍'};
                    const profit=Number(item.profit??item.profit_per_item??item.net_profit??0);
                    const buy=Number(item.buy_price??item.foreign_price??0);
                    const sell=Number(item.sell_price??item.torn_price??0);
                    const stock=item.stock??item.quantity??null;
                    const profCls=profit>0?'pos':profit<0?'neg':'neu';
                    const badgeCls=TI_BADGE_CLASS[cat]||'';
                    const badgeLbl=TI_BADGE_LABEL[cat]||cat;
                    const isXanax=cat==='xanax';
                    return`<div class="ti-item-row${isXanax?' xanax-row':''}">
                        <div class="ti-item-icon">${icon}</div>
                        <div class="ti-item-info">
                            <div class="ti-item-name">${Utils.escHtml(name)}</div>
                            <div class="ti-item-country">
                                <span class="ti-item-flag">${destInfo.flag}</span>
                                <span>${Utils.escHtml(country)}</span>
                                ${badgeCls?`<span class="ti-category-badge ${badgeCls}">${badgeLbl}</span>`:''}
                                ${stock!==null?`<span class="ti-stock-badge">Lager: ${stock}</span>`:''}
                            </div>
                            ${buy>0?`<div style="font-size:9px;color:var(--muted);margin-top:2px;font-family:var(--mono);">Kauf: ${Utils.fmtMoney(buy)} · Verkauf: ${Utils.fmtMoney(sell)}</div>`:''}
                        </div>
                        <div class="ti-profit-block">
                            <div class="ti-profit-val ${profCls}">${Utils.fmtMoney(profit)}</div>
                            <div class="ti-profit-lbl">Profit</div>
                        </div>
                    </div>`;
                }).join('');

            // ── Build restock rows ──
            const filteredRestocks=restocks.filter(item=>{
                const name=item.item_name||item.name||'';
                if(filter==='all')return true;
                return getTiCategory(name)===filter;
            });

            const restockRows=filteredRestocks.length===0
                ?`<div class="ti-empty"><div class="ti-empty-icon">🕐</div><div class="ti-empty-txt">Kein Restock bald fällig</div></div>`
                :filteredRestocks.map(item=>{
                    const name=item.item_name||item.name||'Unbekannt';
                    const cat=getTiCategory(name);
                    const country=item.country||item.destination||'—';
                    const restockAt=Number(item.restock_at??item.next_restock??item.restock_time??0);
                    const secLeft=restockAt>0?Math.max(0,restockAt-now):0;
                    const soon=secLeft>0&&secLeft<600;
                    const destInfo=DESTINATIONS[country]||{flag:'🌍'};
                    const badgeCls=TI_BADGE_CLASS[cat]||'';
                    const badgeLbl=TI_BADGE_LABEL[cat]||cat;
                    return`<div class="ti-restock-row">
                        <span style="font-size:14px;">${destInfo.flag}</span>
                        <div class="ti-restock-name">${Utils.escHtml(name)} <span style="font-size:9px;color:var(--muted);">(${Utils.escHtml(country)})</span></div>
                        ${badgeCls?`<span class="ti-category-badge ${badgeCls}">${badgeLbl}</span>`:''}
                        <div class="ti-restock-time ${soon?'soon':''}">${secLeft>0?Utils.fmtSec(secLeft):'Jetzt!'}</div>
                    </div>`;
                }).join('');

            // ── Error banner ──
            const errHtml=(s.travelItemsError?`<div class="ti-err">⚠ ${Utils.escHtml(s.travelItemsError)}</div>`:'')
                +(s.travelItemsDebug?`<div class="ti-err" style="background:rgba(99,102,241,.08);border-color:rgba(99,102,241,.3);color:var(--accent2);font-size:9px;word-break:break-all;">🔍 DEBUG: ${Utils.escHtml(s.travelItemsDebug)}</div>`:'');

            body.innerHTML=`
            <div class="ti-hero" style="flex-shrink:0;">
                <div class="ti-hero-icon">🌍</div>
                <div class="ti-hero-main">
                    <div class="ti-hero-title">TRAVEL ITEMS</div>
                    <div class="ti-hero-sub">Auslandsmärkte · Profit-Ranking via YATA</div>
                </div>
                <div style="display:flex;flex-direction:column;align-items:flex-end;gap:5px;">
                    <button class="scan-btn" id="thud-ti-refresh" style="padding:5px 10px;font-size:10px;">↺ Refresh</button>
                    ${s.travelItemsLastFetch?`<div class="ti-hero-badge">✓ ${ts}</div>`:''}
                </div>
            </div>
            ${errHtml}
            <div class="ti-filter-row" style="flex-shrink:0;">${filterBtns}</div>
            <div class="card" style="flex-shrink:0;">
                <div class="card-header"><span class="card-title green">📈 TOP PROFITS</span><span class="badge badge-green">${filteredProfits.length} Items</span></div>
                <div class="card-body p0">${profitRows}</div>
            </div>
            <div class="card" style="flex-shrink:0;">
                <div class="card-header"><span class="card-title amber">⏱ RESTOCK BALD</span><span class="badge badge-amber">${filteredRestocks.length} Items</span></div>
                <div class="card-body p0">${restockRows}</div>
            </div>
            <div class="ti-ts" style="flex-shrink:0;">Daten: YATA (yata.yt) · Aktualisiert: ${ts}</div>`;

            // ── Events ──
            body.querySelectorAll('[data-ti-filter]').forEach(btn=>btn.addEventListener('click',()=>{s.setTravelItemsFilter(btn.dataset.tiFilter);this.renderTravelItems();}));
            document.getElementById('thud-ti-refresh')?.addEventListener('click',()=>{hud.fetcher.fetchTravelItems(()=>this.renderTravelItems());this.renderTravelItems();});
        }

        _noKeyState() { return`<div class="empty-state" style="padding:48px 24px;"><div class="es-icon">🔑</div><div class="es-text">KEIN API KEY</div><div class="es-sub">Klick um deinen Torn API Key einzugeben</div><button class="scan-btn" style="margin-top:14px;" onclick="document.getElementById('thud-api-key-btn-header').click()">🔑 API Key eingeben</button></div>`; }
        _loadingState() { return`<div class="loading-state">LADE DATEN…</div>`; }
    }

    // ═══════════════════════════════════════════════════════════════
    // THEME / MODAL
    // ═══════════════════════════════════════════════════════════════
    function applyTheme(key) {
        const t=THEMES[key]||THEMES.obsidian,r=document.documentElement.style;
        r.setProperty('--bg',t.bg);r.setProperty('--bg-card',t.bgCard);r.setProperty('--bg-el',t.bgEl);
        r.setProperty('--border',t.border);r.setProperty('--accent',t.accent);r.setProperty('--accent2',t.accent2);
        r.setProperty('--text',t.text);r.setProperty('--muted',t.textMuted);r.setProperty('--dim',t.textDim);
        r.setProperty('--green',t.green);r.setProperty('--red',t.red);r.setProperty('--amber',t.amber);
        r.setProperty('--blue',t.blue);r.setProperty('--chain',t.chain);r.setProperty('--travel',t.travel);r.setProperty('--online',t.online);
    }
    function openApiModal() {
        const m=document.createElement('div');m.className='thud-modal-bg';
        m.innerHTML=`<div class="thud-modal"><h3>⚙ API Key</h3><p>Torn → Settings → API Key</p><input id="thud-api-input" type="text" placeholder="dein_api_key" value="${hud.store.get('apiKey','')}"><div class="modal-btns"><button class="modal-cancel" id="thud-modal-cancel">Abbrechen</button><button class="modal-save" id="thud-modal-save">Speichern</button></div></div>`;
        document.body.appendChild(m);
        document.getElementById('thud-modal-save').onclick=()=>{const k=document.getElementById('thud-api-input').value.trim();if(k){hud.store.set('apiKey',k);m.remove();hud.fetcher.fetchAll();hud.renderer.render();}};
        document.getElementById('thud-modal-cancel').onclick=()=>m.remove();
        m.addEventListener('click',e=>{if(e.target===m)m.remove();});
    }

    // ═══════════════════════════════════════════════════════════════
          // HUD CONTROLLER
         // ═══════════════════════════════════════════════════════════════
    const hud = {
        store: new Store(),
        state:null,api:null,audio:null,toasts:null,
        chainManager:null,alarmChecker:null,fetcher:null,renderer:null,
        activeTab:null,

        init() {
            this.state        = new StateManager(this.store);
            this.api          = new TornAPI(this.store);
            this.audio        = new AudioManager(this.store);
            this.toasts       = new ToastManager();
            this.chainManager = new ChainManager(this.state);
            this.alarmChecker = new AlarmChecker(this.state,this.toasts);
            this.renderer     = new Renderer(this.state);
            this.activeTab    = this.store.get('activeTab','personal');
            this._injectCSS();this._buildUI();
            applyTheme(this.store.get('theme','obsidian'));
            this._setupIntervals();this._setupHotkeys();
            if(!this.api.key)setTimeout(openApiModal,600);
            else this.fetcher.fetchAll();
         },
        _injectCSS() { const st=document.createElement('style'); st.textContent=buildCSS(); document.head.appendChild(st); },
        _buildUI() {
            if(!document.getElementById('thud-toasts')){ const tc=document.createElement('div'); tc.id='thud-toasts'; document.body.appendChild(tc); }
            const fs=document.createElement('div'); fs.id='thud-fs-alert'; fs.className='fullscreen-alert';
            fs.innerHTML=`<div class="fs-box"><div style="font-size:52px;">✈️</div><div class="fs-title">ANKUNFT IN KÜRZE</div><div class="fs-msg" id="thud-fs-msg"></div><button class="fs-close">OK</button></div>`;
            document.body.appendChild(fs); fs.querySelector('.fs-close').addEventListener('click',()=>fs.classList.remove('active'));
            const overlay=document.createElement('div'); overlay.id='thud-overlay';
            const tabsHtml=TABS_DEF.map(t=>`<div class="thud-tab ${this.activeTab===t.id?'active':''}" data-tab="${t.id}"><span class="ti">${t.icon}</span><span class="tl">${t.label}</span></div>`).join('');
            overlay.innerHTML=`
                <div class="thud-header" id="thud-header">
                    <div class="thud-header-logo">${SHAYA_LOGO_SVG}</div>
                    <div class="thud-header-info"><div class="thud-header-title" id="thud-faction-name">${SCRIPT_TITLE}</div><div class="thud-header-sub">v${VERSION} · xShaYaKaZ</div></div>
                    <div class="thud-header-actions"><div class="pip"></div><button class="thud-btn" id="thud-btn-refresh" title="Refresh [R]">↺</button><button class="thud-btn" id="thud-api-key-btn-header" title="API Key">🔑</button><button class="thud-btn" id="thud-btn-minimize" title="[M]">—</button><button class="thud-btn-close" id="thud-btn-close" title="[H]">✕</button></div>
                </div>
                <div class="thud-tabs" id="thud-tabs">${tabsHtml}</div>
                <div class="thud-mini" id="thud-mini"></div>
                <div class="thud-body" id="thud-body"><div class="loading-state">LADE…</div></div>
                <div class="thud-resize-se" id="thud-rse"></div>
                <div class="thud-resize-e"  id="thud-re"></div>
                <div class="thud-resize-s"  id="thud-rs"></div>`;
            document.body.appendChild(overlay);
            const pos=this.store.get('pos',null);
            if(pos){overlay.style.left=pos.l+'px';overlay.style.top=pos.t+'px';}else{overlay.style.right='20px';overlay.style.top='60px';}
            overlay.style.width=this.store.get('width',400)+'px';
            overlay.style.height=(this.store.get('height',null)||560)+'px';
            if(!this.store.get('visible',true))overlay.style.display='none';
            if(this.store.get('minimized',false))overlay.classList.add('minimized');
            const fab=document.createElement('div'); fab.id='thud-fab'; fab.title=`${SCRIPT_TITLE} [H]`; fab.innerHTML=FAB_LOGO_SVG;
            document.body.appendChild(fab);
            const fPos=this.store.get('fabPos',null);
            if(fPos){fab.style.left=fPos.l+'px';fab.style.top=fPos.t+'px';}else{fab.style.left='10px';fab.style.top='10px';}
            if(this.store.get('visible',true))fab.classList.add('on');
            this._setupDrag(overlay); this._setupResize(overlay); this._setupFab(fab);
            this._setupTabWheelScroll(); this._setupScrollFix(overlay);
            document.getElementById('thud-tabs').addEventListener('click',e=>{const t=e.target.closest('.thud-tab');if(!t)return;this.switchTab(t.dataset.tab);});
            document.getElementById('thud-btn-refresh').addEventListener('click',()=>this.fetcher.fetchAll());
            document.getElementById('thud-btn-minimize').addEventListener('click',()=>this.toggleMinimize());
            document.getElementById('thud-btn-close').addEventListener('click',()=>this.toggleVisible());
            document.getElementById('thud-api-key-btn-header').addEventListener('click',openApiModal);
            this.fetcher=new DataFetcher(this.api,this.state,this.toasts,this.renderer);
            this.renderer.render();
        },
        _setupTabWheelScroll() { const tabs=document.getElementById('thud-tabs');if(!tabs)return;tabs.addEventListener('wheel',e=>{e.preventDefault();tabs.scrollLeft+=e.deltaY!==0?e.deltaY:e.deltaX;},{passive:false}); },
        _setupScrollFix(overlay) {
            const getBody=()=>document.getElementById('thud-body');
            overlay.addEventListener('wheel',e=>{const t=e.target.closest('#thud-tabs');if(t)return;const b=getBody();if(!b)return;b.scrollTop+=e.deltaY;e.preventDefault();e.stopPropagation();},{passive:false});
            let ty=0;
            overlay.addEventListener('touchstart',e=>{ty=e.touches[0].clientY;},{passive:true});
            overlay.addEventListener('touchmove',e=>{const b=getBody();if(!b)return;b.scrollTop+=ty-e.touches[0].clientY;ty=e.touches[0].clientY;e.preventDefault();},{passive:false});
        },
        switchTab(tabId) { this.activeTab=tabId; this.store.set('activeTab',tabId); document.querySelectorAll('.thud-tab').forEach(el=>el.classList.toggle('active',el.dataset.tab===tabId)); this.renderer.render(); setTimeout(()=>{const b=document.getElementById('thud-body');if(b)b.scrollTop=0;},50); },
        toggleMinimize() { const v=!this.store.get('minimized',false); this.store.set('minimized',v); const o=document.getElementById('thud-overlay');if(o)o.classList.toggle('minimized',v); if(v)this.renderer.renderMini(); },
        toggleVisible() { const v=!this.store.get('visible',true); this.store.set('visible',v); const o=document.getElementById('thud-overlay');if(o)o.style.display=v?'':'none'; const fab=document.getElementById('thud-fab');if(fab)v?fab.classList.add('on'):fab.classList.remove('on'); },
        _setupDrag(el) {
            let drag=false,ox=0,oy=0;
            document.getElementById('thud-header').addEventListener('mousedown',e=>{if(e.target.tagName==='BUTTON'||e.target.closest('button'))return;e.preventDefault();const r=el.getBoundingClientRect();ox=e.clientX-r.left;oy=e.clientY-r.top;drag=true;});
            document.addEventListener('mousemove',e=>{if(!drag)return;el.style.left=Math.max(0,Math.min(e.clientX-ox,window.innerWidth-el.offsetWidth))+'px';el.style.top=Math.max(0,Math.min(e.clientY-oy,window.innerHeight-40))+'px';el.style.right='auto';});
            document.addEventListener('mouseup',()=>{if(!drag)return;drag=false;this.store.set('pos',{l:parseInt(el.style.left),t:parseInt(el.style.top)});});
        },
        _setupResize(el) {
            let rSE=false,rE=false,rS=false,sx=0,sy=0,sw=0,sh=0;
            document.getElementById('thud-rse').addEventListener('mousedown',e=>{rSE=true;sx=e.clientX;sy=e.clientY;sw=el.offsetWidth;sh=el.offsetHeight;e.preventDefault();e.stopPropagation();});
            document.getElementById('thud-re').addEventListener('mousedown',e=>{rE=true;sx=e.clientX;sw=el.offsetWidth;e.preventDefault();e.stopPropagation();});
            document.getElementById('thud-rs').addEventListener('mousedown',e=>{rS=true;sy=e.clientY;sh=el.offsetHeight;e.preventDefault();e.stopPropagation();});
            document.addEventListener('mousemove',e=>{if(rSE){el.style.width=Math.max(300,Math.min(720,sw+(e.clientX-sx)))+'px';el.style.height=Math.max(250,sh+(e.clientY-sy))+'px';}else if(rE){el.style.width=Math.max(300,Math.min(720,sw+(e.clientX-sx)))+'px';}else if(rS){el.style.height=Math.max(250,sh+(e.clientY-sy))+'px';}});
            document.addEventListener('mouseup',()=>{if(rSE||rE||rS){this.store.set('width',parseInt(el.style.width));if(el.style.height)this.store.set('height',parseInt(el.style.height));}rSE=rE=rS=false;});
        },
        _setupFab(fab) {
            let drag=false,ox=0,oy=0,moved=false,sx=0,sy=0;
            fab.addEventListener('mousedown',e=>{e.preventDefault();drag=true;moved=false;const r=fab.getBoundingClientRect();ox=e.clientX-r.left;oy=e.clientY-r.top;sx=e.clientX;sy=e.clientY;});
            document.addEventListener('mousemove',e=>{if(!drag)return;if(hud.store.get('fabPinned',false)){drag=false;return;}if(Math.abs(e.clientX-sx)>4||Math.abs(e.clientY-sy)>4)moved=true;fab.style.left=Math.max(0,Math.min(window.innerWidth-40,e.clientX-ox))+'px';fab.style.top=Math.max(0,Math.min(window.innerHeight-40,e.clientY-oy))+'px';});
            document.addEventListener('mouseup',()=>{if(!drag)return;drag=false;if(!moved){this.toggleVisible();return;}this.store.set('fabPos',{l:parseInt(fab.style.left),t:parseInt(fab.style.top)});});
        },
        _setupIntervals() {
            setInterval(()=>hud.fetcher.fetchPersonal(),REFRESH.PERSONAL);
            setInterval(()=>hud.fetcher.fetchFaction(), REFRESH.FACTION);
            setInterval(()=>hud.fetcher.fetchChain(),   REFRESH.CHAIN);
            setInterval(()=>hud.fetcher.fetchNetworth(),REFRESH.NETWORTH);
            setInterval(()=>hud.fetcher.fetchCompany(), REFRESH.COMPANY);
            setInterval(()=>hud.fetcher.fetchEnemyData(),REFRESH.ENEMY);
            setInterval(()=>this._tick(),               REFRESH.TICK);
        },
        _setupHotkeys() {
            document.addEventListener('keydown',e=>{ if(e.target.tagName==='INPUT'||e.target.tagName==='TEXTAREA')return; if(e.key==='m'||e.key==='M')this.toggleMinimize(); if(e.key==='h'||e.key==='H')this.toggleVisible(); if(e.key==='r'||e.key==='R')this.fetcher.fetchAll(); if(e.key==='s'||e.key==='S')this.switchTab('settings'); });
        },
        _tick() {
            this.state.uptimeSec++;
            const td=this.state.userTravel?.travel;
            if(td?.time_left>0)td.time_left=Math.max(0,Number(td.time_left)-1);
            this.alarmChecker.run();
            const min=this.store.get('minimized',false);
            if(!min&&this.activeTab==='personal')this.renderer.renderPersonal();
            if(min)this.renderer.renderMini();
        },
    };

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

})();