SHAYA UI

All-in-One Faction HUD – v25.2: FF Design, NW Chart, Enemy Groups, Chain Fix, Bounty FF Filter

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey, Greasemonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Userscripts.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een gebruikersscriptbeheerder nodig.

(Ik heb al een user script manager, laat me het downloaden!)

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

(Ik heb al een beheerder - laat me doorgaan met de installatie!)

// ==UserScript==
// @name         SHAYA UI
// @namespace    https://www.torn.com/
// @version      25.2
// @description  All-in-One Faction HUD – v25.2: FF Design, NW Chart, Enemy Groups, Chain Fix, Bounty FF Filter
// @author       xShaYaKaZ
// @match        https://www.torn.com/*
// @grant        GM_xmlhttpRequest
// @grant        GM_setValue
// @grant        GM_getValue
// @connect      api.torn.com
// @connect      ffscouter.com
// ==/UserScript==

(function () {
    'use strict';

    const VERSION = '25.2';
    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,
    };

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

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

    // FF color scale: blue(low) → green(medium) → red(high)
    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';
    }

    // PATCH 1: FF Badge redesigned to match HUD style (badge-style, consistent with other tags)
    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}`;
        // Use inline style only for the dynamic bg color, rest via class
        return `<span class="ff-badge" style="background:${bg};color:${color};border-color:${bg}88;" title="${title}">FF ${label}</span>`;
    }

    // FF value label for sorting display
    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); }

/* ── PATCH 1: FF Badge – passt ins HUD Design ── */
.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; }

/* ── PATCH 2: Networth Chart – übersichtlicher ── */
.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)); }

/* ── PATCH 4: Chain Block – schneller & präziser ── */
.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; }

/* ── PATCH 3: Enemy Groups ── */
.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; }
`;

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

    // ═══════════════════════════════════════════════════════════════
    // STATE
    // ═══════════════════════════════════════════════════════════════

    // PATCH 3: Enemy Groups – default groups
    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);
            // PATCH 5: FF filter for bounty
            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.enemies=store.get('enemies',[]);
            // PATCH 3: Enemy Groups
            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',{});
        }
        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); }
        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; }
        // Group management
        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; // protect default
            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:'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 – PATCH 4: schnellerer Tick (500ms), präzisere Anzeige
    // ═══════════════════════════════════════════════════════════════
    class ChainManager {
        constructor(state) { this.state=state; this._tick=null; }
        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._tick)this._tick=setInterval(()=>this.tick(),500); } // 500ms für mehr Präzision
        stopTick()  { if(this._tick){clearInterval(this._tick);this._tick=null;} }
        tick() {
            const s=this.state; if(!s.chainEndTime){this.stopTick();return;}
            const rem=Math.max(0,s.chainEndTime-Date.now());
            const totalSec=Math.floor(rem/1000);
            const m=Math.floor(totalSec/60), sec=totalSec%60;
            const warn=rem<=CHAIN_WARN_SEC*1000;
            // Update timer display with seconds precision
            const el=document.getElementById('thud-chain-timer');
            if(el){
                el.textContent=rem<=300000?`${m}:${String(sec).padStart(2,'0')}`:` ${m}m`;
                warn?el.classList.add('warn'):el.classList.remove('warn');
            }
            // Update progress bar
            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))+'%';
            }
            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(rem===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()}`;
                if(hud.activeTab==='faction')this.renderer.render();
                if(hud.store.get('minimized',false))this.renderer.renderMini();
            });
        }
        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 total=nw.total||0,now=Utils.now(),hist=this.state.networthHistory;
                hist.push({ts:now,val:total});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();
                });
            }
        }

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

    // ═══════════════════════════════════════════════════════════════
    // 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 '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>`;
            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>
            </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 m=Object.values(this.state.members),t=m.length;
            const cap=this.state.faction.max_members||this.state.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=this.state.showAllOnline?sa:sa.slice(0,sl);
            const sOf=this.state.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=`
            <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&&!this.state.showAllOnline?`<span class="toggle-more" id="thud-more-online">▾ ${eOn} mehr</span>`:this.state.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&&!this.state.showAllOffline?`<span class="toggle-more" style="margin:7px 14px 12px;" id="thud-more-offline">▾ ${eOf} weitere</span>`:this.state.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(this.state.chain?.current>0)hud.chainManager.startTick();
            document.getElementById('thud-more-online')?.addEventListener('click',()=>{this.state.showAllOnline=true;this.renderFaction();});
            document.getElementById('thud-less-online')?.addEventListener('click',()=>{this.state.showAllOnline=false;this.renderFaction();});
            document.getElementById('thud-more-offline')?.addEventListener('click',()=>{this.state.showAllOffline=true;this.renderFaction();});
            document.getElementById('thud-less-offline')?.addEventListener('click',()=>{this.state.showAllOffline=false;this.renderFaction();});
        }

        renderBounty() {
            const body=this.body; if(!body)return;
            const s=this.state,data=s.bountyData,sort=s.bountySort,now=Utils.now();
            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;});}
            // PATCH 5: FF Filter
            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; // show unknowns unless strict filter
                    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);}}));
            // Load FF data for visible bounty targets
            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);
        }

        // ─── PATCH 3: ENEMY TAB – with Groups ───
        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>`:`<span class="enemy-tag" style="background:rgba(16,185,129,.08);color:var(--green);border:1px solid rgba(16,185,129,.2);">✓ No 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>`:'';
                // Group select options
                const groupOpts=s.enemyGroups.map(g=>`<option value="${g.id}" ${enemy.groupId===g.id?'selected':''}>${g.name}</option>`).join('');
                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>`:'';

            // Build group selector HTML for add-enemy form
            const groupSelOpts=s.enemyGroups.map(g=>`<option value="${g.id}">${g.name}</option>`).join('');

            // Build grouped enemy list
            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>`;
                });
                // Enemies without a valid group → show in default
                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);}});

            // New Group button
            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();});
            });

            // Delete group buttons
            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();}
            }));

            // Toggle group collapse
            body.querySelectorAll('[data-toggle-group]').forEach(btn=>btn.addEventListener('click',()=>{s.toggleGroupCollapse(btn.dataset.toggleGroup);this.renderEnemy();}));

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

            // Move enemy to group
            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();});
            }));

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

            // Load FF data
            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 cp=this.state.companyProfile,employees=this.state.companyEmployees||[],ws=this.state.workStats;
            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>`:''}`;
        }

        // ─── PATCH 2: Networth – übersichtlicher Chart ───
        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+bank+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</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">🏦 Bank</span><span class="row-value" style="color:var(--blue);">${Utils.fmtMoney(bank)}</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>
            </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</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>`:''}`;
            // attach tooltip interaction
            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)';

            // Grid lines (4 horizontal)
            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>`;
            }

            // Time labels (start / mid / end)
            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>`;
                });
            }

            // Data points & path
            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`;

            // Invisible hover rects for tooltip
            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('');

            // Current value dot
            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>`;
                        // position tooltip
                        const wrap=document.getElementById('nw-chart-wrap');
                        const wRect=wrap?.getBoundingClientRect()||{left:0,width:300};
                        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();});
        }

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

})();