SHAYA UI

All-in-One Faction HUD – v28.0: PlayerSearch, War-Tab, Networth-Fix, CD-Fix, FF-Design

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         SHAYA UI
// @namespace    https://www.torn.com/
// @version     1011
// @description  All-in-One Faction HUD – v28.0: PlayerSearch, War-Tab, Networth-Fix, CD-Fix, FF-Design
// @author       xShaYaKaZ
// @match        https://www.torn.com/*
// @grant        GM_xmlhttpRequest
// @grant        GM_setValue
// @grant        GM_getValue
// @connect      api.torn.com
// @connect      ffscouter.com
// @connect      droqsdb.com
// @connect      yata.yt
// ==/UserScript==
 
(function () {
    'use strict';
 
    const VERSION = '28.1';
    const SCRIPT_TITLE = "SHAYA UI";
    const API_BASE = 'https://api.torn.com';
 
    const REFRESH = {
        PERSONAL:     15000,
        FACTION:      30000,
        CHAIN:        5000,
        TICK:         1000,
        NETWORTH:     60000,
        COMPANY:      60000,
        ENEMY:        30000,
        TRAVEL_ITEMS: 120000,
        WAR:          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' },
    };
 
    // Travel items helpers
    const TRAVEL_ITEM_KEYWORDS = ['xanax', 'plushie', 'flower'];
    const isTravelTarget = (name) => {
        if (!name || typeof name !== 'string') return false;
        const l = name.toLowerCase();
        return TRAVEL_ITEM_KEYWORDS.some(k => l.includes(k));
    };
    const getTiCategory = (name) => {
        if (!name) return 'other';
        const l = name.toLowerCase();
        if (l.includes('xanax'))   return 'xanax';
        if (l.includes('plushie')) return 'plushie';
        if (l.includes('flower'))  return 'flower';
        return 'other';
    };
    const TI_ICONS       = { xanax:'💊', plushie:'🧸', flower:'🌸', other:'📦' };
    const TI_BADGE_CLASS = { xanax:'ti-cat-xanax', plushie:'ti-cat-plushie', flower:'ti-cat-flower' };
    const TI_BADGE_LABEL = { xanax:'Xanax', plushie:'Plüschi', flower:'Blume' };
 
    // ═══════════════════════════════════════════════════════════════
    // FF SCOUTER INTEGRATION
    // ═══════════════════════════════════════════════════════════════
    const FF_BASE_URL    = 'https://ffscouter.com';
    const FF_CACHE_EXPIRY = 60 * 60 * 1000;
 
    class FFCache {
        constructor() { this.db=null; this.DB_NAME='shaya-ff-cache'; this.DB_VERSION=1; this.STORE='cache'; }
        async open() {
            if (this.db) return this.db;
            return new Promise((resolve, reject) => {
                const req = window.indexedDB.open(this.DB_NAME, this.DB_VERSION);
                req.onerror = () => { this.db=null; reject(req.error); };
                req.onsuccess = () => { this.db=req.result; resolve(this.db); };
                req.onupgradeneeded = (e) => {
                    const db=e.target.result;
                    const store=db.createObjectStore(this.STORE, { keyPath:'player_id' });
                    store.createIndex('expiry', ['expiry'], { unique:false });
                };
            });
        }
        async get(player_ids) {
            try {
                await this.open();
                const tx=this.db.transaction(this.STORE,'readonly'), store=tx.objectStore(this.STORE);
                const results={}, now=Date.now();
                await Promise.all(player_ids.map(id=>new Promise((res)=>{
                    const req=store.get(Number(id));
                    req.onsuccess=()=>{ if(req.result&&req.result.expiry>now) results[Number(id)]=req.result; res(); };
                    req.onerror=()=>res();
                })));
                return results;
            } catch(e) { return {}; }
        }
        async set(entries) {
            try {
                await this.open();
                const tx=this.db.transaction(this.STORE,'readwrite'), store=tx.objectStore(this.STORE);
                entries.forEach(e=>store.put(e));
                return new Promise((res,rej)=>{ tx.oncomplete=()=>res(); tx.onerror=()=>rej(tx.error); });
            } catch(e) {}
        }
        async clear() {
            try { await this.open(); const tx=this.db.transaction(this.STORE,'readwrite'); tx.objectStore(this.STORE).clear(); } catch(e) {}
        }
    }
 
    const ffCache = new FFCache();
 
    function fetchFFStats(playerIds, ffKey, callback) {
        if (!ffKey||!playerIds||!playerIds.length) { callback({}); return; }
        const ids=[...new Set(playerIds)].join(',');
        const url=`${FF_BASE_URL}/api/v1/get-stats?key=${ffKey}&targets=${ids}`;
        GM_xmlhttpRequest({
            method:'GET', url,
            onload:(response)=>{
                if (!response||response.status!==200) { callback({}); return; }
                try {
                    const data=JSON.parse(response.responseText);
                    if (!Array.isArray(data)) { callback({}); return; }
                    const expiry=Date.now()+FF_CACHE_EXPIRY, results={}, toCache=[];
                    data.forEach(r=>{
                        if (!r||!r.player_id) return;
                        const entry={ player_id:Number(r.player_id), expiry, ff:r.fair_fight??null, last_updated:r.last_updated??null, bs_estimate:r.bs_estimate_human??null };
                        toCache.push(entry); results[Number(r.player_id)]=entry;
                    });
                    ffCache.set(toCache); callback(results);
                } catch(e) { callback({}); }
            },
            onerror:()=>callback({}),
        });
    }
 
    async function getFFData(playerIds, ffKey, callback) {
        if (!ffKey||!playerIds.length) { callback({}); return; }
        const ids=playerIds.map(Number), cached=await ffCache.get(ids);
        const missing=ids.filter(id=>!cached[id]);
        if (!missing.length) { callback(cached); return; }
        fetchFFStats(missing, ffKey, (fresh)=>callback({...cached,...fresh}));
    }
 
    function ffColor(ff) {
        if (!ff||ff<=0) return '#888';
        let r,g,b;
        if (ff<=1)      { r=0x28; g=0x28; b=0xc6; }
        else if (ff<=3) { const t=(ff-1)/2; r=0x28; g=Math.round(0x28+(0xc6-0x28)*t); b=Math.round(0xc6-(0xc6-0x28)*t); }
        else if (ff<=5) { const t=(ff-3)/2; r=Math.round(0x28+(0xc6-0x28)*t); g=Math.round(0xc6-(0xc6-0x28)*t); b=0x28; }
        else            { r=0xc6; g=0x28; b=0x28; }
        return '#'+[r,g,b].map(v=>v.toString(16).padStart(2,'0')).join('').toUpperCase();
    }
 
    function ffTextColor(hex) {
        const r=parseInt(hex.slice(1,3),16), g=parseInt(hex.slice(3,5),16), b=parseInt(hex.slice(5,7),16);
        return (r*0.299+g*0.587+b*0.114)>126?'#000':'#fff';
    }
 
    function ffBadge(entry) {
        if (!entry) return `<span class="ff-badge ff-badge-unknown" title="FF nicht geladen">FF ?</span>`;
        if (!entry.ff) return `<span class="ff-badge ff-badge-none" title="Keine FF Daten">FF —</span>`;
        const ff=entry.ff;
        const now=Math.floor(Date.now()/1000);
        const stale=entry.last_updated&&(now-entry.last_updated)>14*24*3600;
        const bg=ffColor(ff);
        const color=ffTextColor(bg);
        const label=ff.toFixed(2)+(stale?'*':'');
        let title=`FairFight: ${ff.toFixed(2)}`;
        if (stale) title+=' (Daten älter als 14 Tage)';
        if (entry.bs_estimate) title+=` · BS Schätzung: ${entry.bs_estimate}`;
        return `<span class="ff-badge" style="background:${bg};color:${color};border-color:${bg}88;" title="${title}">FF ${label}</span>`;
    }
 
    function ffValue(entry) { return entry?.ff??-1; }
 
    // ═══════════════════════════════════════════════════════════════
    // CSS
    // ═══════════════════════════════════════════════════════════════
    const buildCSS = () => `
@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&family=DM+Sans:wght@400;500;600;700&display=swap');
:root { --bg:#0a0a0b; --bg-card:#111114; --bg-el:#18181c; --border:#2a2a30; --accent:#6366f1; --accent2:#818cf8; --text:#f1f5f9; --muted:#64748b; --dim:#94a3b8; --green:#10b981; --red:#ef4444; --amber:#f59e0b; --blue:#3b82f6; --chain:#ef4444; --travel:#6366f1; --online:#10b981; --r:12px; --r-sm:8px; --r-lg:16px; --font:'DM Sans',sans-serif; --mono:'JetBrains Mono',monospace; }
* { box-sizing:border-box; margin:0; padding:0; }
#thud-overlay { position:fixed; z-index:99999; font-family:var(--font); font-size:13px; color:var(--text); background:var(--bg); border:1px solid var(--border); border-radius:20px; box-shadow:0 32px 80px rgba(0,0,0,.95),0 0 0 1px rgba(255,255,255,.03); display:flex; flex-direction:column; overflow:hidden; min-width:300px; min-height:200px; user-select:none; }
#thud-overlay.minimized { min-height:0 !important; height:auto !important; border-radius:12px; }
#thud-overlay.minimized .thud-tabs, #thud-overlay.minimized .thud-body, #thud-overlay.minimized .thud-resize-se, #thud-overlay.minimized .thud-resize-e, #thud-overlay.minimized .thud-resize-s { display:none !important; }
#thud-overlay.minimized .thud-mini { display:flex !important; }
#thud-overlay:not(.minimized) .thud-mini { display:none !important; }
.thud-header { display:flex; align-items:center; gap:8px; padding:10px 14px; background:linear-gradient(180deg,rgba(255,255,255,.025) 0%,transparent 100%); border-bottom:1px solid var(--border); cursor:grab; flex-shrink:0; }
.thud-header:active { cursor:grabbing; }
.thud-header-logo { width:32px; height:32px; flex-shrink:0; display:flex; align-items:center; justify-content:center; filter:drop-shadow(0 2px 8px rgba(239,68,68,.5)); }
.thud-header-logo svg { width:32px; height:32px; }
.thud-header-info { flex:1; min-width:0; }
.thud-header-title { font-size:13px; font-weight:800; color:var(--text); letter-spacing:1px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; text-transform:uppercase; }
.thud-header-sub { font-size:9px; color:var(--muted); font-family:var(--mono); margin-top:1px; }
.thud-header-actions { display:flex; gap:4px; flex-shrink:0; align-items:center; }
.thud-btn { background:var(--bg-el); border:1px solid var(--border); color:var(--dim); border-radius:7px; padding:5px 8px; cursor:pointer; font-size:11px; font-family:var(--font); font-weight:600; transition:all .15s; display:flex; align-items:center; gap:4px; white-space:nowrap; }
.thud-btn:hover { border-color:var(--accent); color:var(--text); background:rgba(99,102,241,.1); }
.thud-btn.active { border-color:var(--green); color:var(--green); background:rgba(16,185,129,.08); }
.thud-btn-close { background:rgba(239,68,68,.12); border:1px solid rgba(239,68,68,.25); color:var(--red); border-radius:7px; padding:5px 8px; cursor:pointer; font-size:12px; font-weight:700; transition:all .15s; display:flex; align-items:center; line-height:1; }
.thud-btn-close:hover { background:rgba(239,68,68,.3); border-color:var(--red); }
.pip { width:6px; height:6px; border-radius:50%; background:var(--green); box-shadow:0 0 6px var(--green); animation:pipPulse 2s ease-in-out infinite; }
@keyframes pipPulse { 0%,100%{opacity:1} 50%{opacity:.3} }
.thud-tabs { display:flex; background:var(--bg); border-bottom:1px solid var(--border); flex-shrink:0; overflow-x:auto; overflow-y:hidden; scrollbar-width:none; gap:2px; padding:5px 6px 0; cursor:default; }
.thud-tabs::-webkit-scrollbar { display:none; }
.thud-tab { display:flex; flex-direction:column; align-items:center; justify-content:center; gap:2px; padding:6px 8px 8px; cursor:pointer; color:var(--muted); border-bottom:2px solid transparent; border-radius:7px 7px 0 0; transition:all .15s; white-space:nowrap; min-width:40px; flex-shrink:0; }
.thud-tab .ti { font-size:13px; line-height:1; }
.thud-tab .tl { font-size:7.5px; font-weight:700; letter-spacing:.7px; text-transform:uppercase; }
.thud-tab:hover { color:var(--dim); background:rgba(255,255,255,.04); }
.thud-tab.active { color:var(--accent2); border-bottom-color:var(--accent); background:rgba(99,102,241,.06); }
.thud-mini { display:none; flex-direction:column; align-items:stretch; }
.thud-mini-bar { display:flex; align-items:center; gap:6px; padding:5px 10px; flex-wrap:nowrap; overflow:hidden; border-top:1px solid var(--border); }
.thud-mini-chip { display:flex; align-items:center; gap:3px; font-size:10px; font-weight:700; flex-shrink:0; background:var(--bg-el); border:1px solid var(--border); border-radius:6px; padding:2px 6px; }
.thud-mini-val { color:var(--text); font-family:var(--mono); font-size:10px; }
.thud-mini-lbl { color:var(--muted); font-size:9px; }
.thud-mini-tabs { display:flex; border-top:1px solid rgba(255,255,255,.04); width:100%; }
.thud-mini-tab { flex:1; padding:4px 2px; text-align:center; font-size:9px; font-weight:700; color:var(--muted); cursor:pointer; border-right:1px solid rgba(255,255,255,.04); letter-spacing:.3px; text-transform:uppercase; transition:all .15s; }
.thud-mini-tab:last-child { border-right:none; }
.thud-mini-tab:hover { color:var(--accent2); background:rgba(99,102,241,.06); }
.thud-body { flex:1 1 0; overflow-y:scroll !important; min-height:0; display:flex; flex-direction:column; gap:10px; padding:12px 14px 14px; pointer-events:auto; scrollbar-width:thin; scrollbar-color:var(--border) transparent; }
.thud-body::-webkit-scrollbar { width:4px; }
.thud-body::-webkit-scrollbar-thumb { background:var(--border); border-radius:2px; }
.card { background:var(--bg-card); border:1px solid var(--border); border-radius:var(--r); overflow:hidden; flex-shrink:0; }
.card-header { display:flex; align-items:center; justify-content:space-between; padding:10px 14px; border-bottom:1px solid rgba(255,255,255,.05); }
.card-title { font-size:10px; font-weight:700; letter-spacing:.8px; text-transform:uppercase; color:var(--dim); }
.card-title.accent { color:var(--accent2); } .card-title.green { color:var(--green); } .card-title.red { color:var(--red); } .card-title.amber { color:var(--amber); } .card-title.blue { color:var(--blue); }
.card-body { padding:12px 14px; display:flex; flex-direction:column; gap:8px; }
.card-body.p0 { padding:0; }
.badge { padding:2px 7px; border-radius:20px; font-size:9px; font-weight:700; letter-spacing:.5px; }
.badge-green { background:rgba(16,185,129,.12); color:var(--green); border:1px solid rgba(16,185,129,.2); }
.badge-red   { background:rgba(239,68,68,.12);  color:var(--red);   border:1px solid rgba(239,68,68,.2); }
.badge-amber { background:rgba(245,158,11,.12); color:var(--amber); border:1px solid rgba(245,158,11,.2); }
.badge-blue  { background:rgba(59,130,246,.12); color:var(--blue);  border:1px solid rgba(59,130,246,.2); }
.badge-muted { background:rgba(255,255,255,.06); color:var(--muted); border:1px solid rgba(255,255,255,.08); }
.row { display:flex; align-items:center; justify-content:space-between; padding:6px 0; border-bottom:1px solid rgba(255,255,255,.04); }
.row:last-child { border-bottom:none; }
.row-label { font-size:12px; color:var(--dim); font-weight:500; }
.row-value { font-size:12px; font-family:var(--mono); font-weight:600; color:var(--text); }
.bar { height:4px; background:rgba(255,255,255,.06); border-radius:3px; overflow:hidden; }
.bar-lg { height:6px; }
.bar-fill { height:100%; border-radius:3px; transition:width .4s; }
.bar-block { display:flex; flex-direction:column; gap:5px; }
.bar-block-head { display:flex; justify-content:space-between; align-items:center; }
.bar-block-lbl { font-size:12px; color:var(--muted); font-weight:600; }
.bar-block-val { font-size:13px; font-family:var(--mono); font-weight:700; }
.bar-block-sub { font-size:10px; color:var(--muted); }
.stat-grid { display:grid; gap:8px; }
.stat-grid-3 { grid-template-columns:repeat(3,1fr); }
.stat-grid-4 { grid-template-columns:repeat(4,1fr); }
.stat-card { background:var(--bg-el); border:1px solid var(--border); border-radius:var(--r-sm); padding:10px 6px; text-align:center; }
.sv { font-size:17px; font-weight:700; font-family:var(--mono); line-height:1; }
.sv-green { color:var(--green); } .sv-red { color:var(--red); } .sv-amber { color:var(--amber); } .sv-blue { color:var(--blue); }
.sl { font-size:9px; color:var(--muted); text-transform:uppercase; letter-spacing:.5px; margin-top:4px; }
.stat-chip { text-align:center; }
.sn { font-size:15px; font-weight:700; font-family:var(--mono); }
.status-summary { display:flex; gap:10px; flex-wrap:wrap; justify-content:center; padding:10px 0 4px; border-top:1px solid rgba(255,255,255,.05); margin-top:6px; }
.ql-grid { display:grid; grid-template-columns:repeat(4,1fr); gap:6px; padding:2px 0 4px; }
.ql-card { background:var(--bg-el); border:1px solid var(--border); border-radius:10px; padding:10px 4px 8px; cursor:pointer; text-align:center; display:flex; flex-direction:column; align-items:center; gap:4px; transition:all .18s; text-decoration:none; }
.ql-card:hover { border-color:var(--accent); background:rgba(99,102,241,.1); transform:translateY(-2px); box-shadow:0 6px 20px rgba(99,102,241,.15); }
.ql-icon { font-size:20px; line-height:1; }
.ql-name { font-size:9px; font-weight:800; color:var(--dim); text-align:center; line-height:1.2; }
.travel-card { border:1px solid rgba(99,102,241,.15); border-radius:var(--r); overflow:hidden; flex-shrink:0; }
.travel-card.home { border-color:rgba(16,185,129,.15); }
.travel-card.warn { border-color:rgba(245,158,11,.3); }
.travel-card.crit { border-color:rgba(239,68,68,.4); animation:travelCritPulse 1s ease-in-out infinite alternate; }
.travel-card.ok   { border-color:rgba(99,102,241,.3); }
@keyframes travelCritPulse { from{box-shadow:none} to{box-shadow:0 0 20px rgba(239,68,68,.3)} }
.travel-card-top { display:flex; align-items:center; justify-content:space-between; padding:8px 14px; font-size:10px; font-weight:700; letter-spacing:.8px; text-transform:uppercase; background:rgba(0,0,0,.15); border-bottom:1px solid rgba(255,255,255,.05); }
.travel-card-body { padding:12px 14px; }
.travel-dest { font-size:18px; font-weight:800; letter-spacing:-.3px; }
.travel-clock { font-size:28px; font-weight:800; font-family:var(--mono); line-height:1; }
.travel-prog { height:3px; background:rgba(255,255,255,.06); border-radius:2px; overflow:hidden; margin-top:10px; }
.travel-prog-fill { height:100%; border-radius:2px; background:var(--travel); transition:width .5s; }
.travel-prog-fill.warn { background:var(--amber); }
.travel-prog-fill.crit { background:var(--red); }
.smart-tipp { background:linear-gradient(135deg,rgba(99,102,241,.09),rgba(129,140,248,.03)); border:1px solid rgba(99,102,241,.25); border-left:3px solid var(--accent); border-radius:var(--r); padding:9px 12px; font-size:12px; color:var(--dim); line-height:1.6; flex-shrink:0; display:flex; align-items:center; gap:8px; }
.smart-tipp-icon { font-size:15px; flex-shrink:0; }
.smart-tipp-text { flex:1; }
.member-grid { display:grid; grid-template-columns:repeat(2,1fr); gap:2px 8px; }
.member-row { display:flex; align-items:center; gap:5px; padding:4px 0; border-bottom:1px solid rgba(255,255,255,.04); }
.member-row:last-child { border-bottom:none; }
.member-name { flex:1; font-size:11px; font-weight:600; color:var(--text); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.member-status { font-size:9px; color:var(--muted); font-weight:600; white-space:nowrap; }
.status-dot { width:6px; height:6px; border-radius:50%; flex-shrink:0; }
.sd-online { background:var(--online); box-shadow:0 0 5px var(--online); }
.sd-idle { background:var(--amber); }
.sd-traveling { background:var(--travel); }
.sd-hospital { background:var(--red); }
.sd-jail { background:#f97316; }
.sd-offline { background:rgba(255,255,255,.12); }
.offline-row { display:flex; align-items:center; gap:8px; padding:6px 14px; border-bottom:1px solid rgba(255,255,255,.04); }
.offline-row:last-child { border-bottom:none; }
.offline-name { flex:1; font-size:11px; color:var(--muted); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.offline-ago { font-size:10px; font-family:var(--mono); font-weight:700; }
.ago-m { color:var(--amber); } .ago-h { color:var(--red); } .ago-d { color:var(--muted); }
.ff-badge { display:inline-flex; align-items:center; justify-content:center; font-size:9px; font-weight:800; padding:2px 7px; border-radius:20px; border:1px solid transparent; letter-spacing:.5px; font-family:var(--mono); cursor:help; white-space:nowrap; line-height:1.4; box-shadow:0 1px 6px rgba(0,0,0,.5); }
.ff-badge-unknown { background:rgba(255,255,255,.06); color:var(--muted); border-color:rgba(255,255,255,.1); }
.ff-badge-none    { background:rgba(255,255,255,.06); color:var(--muted); border-color:rgba(255,255,255,.1); }
.bounty-row { display:flex; align-items:flex-start; gap:10px; padding:10px 14px; border-bottom:1px solid rgba(255,255,255,.04); }
.bounty-row:last-child { border-bottom:none; }
.bounty-row.is-hospital { background:rgba(239,68,68,.06); border-left:3px solid var(--red); }
.bounty-reward { font-family:var(--mono); font-size:12px; font-weight:700; color:var(--amber); white-space:nowrap; }
.bounty-link { display:inline-flex; align-items:center; padding:5px 10px; border-radius:6px; background:rgba(239,68,68,.15); border:1px solid rgba(239,68,68,.25); color:var(--red); font-size:11px; font-weight:700; text-decoration:none; white-space:nowrap; transition:all .15s; }
.bounty-link:hover { background:rgba(239,68,68,.3); }
.bounty-difficulty { padding:2px 6px; border-radius:4px; font-size:9px; font-weight:800; }
.diff-easy   { background:rgba(16,185,129,.15); color:var(--green); }
.diff-medium { background:rgba(245,158,11,.15);  color:var(--amber); }
.diff-hard   { background:rgba(239,68,68,.15);   color:var(--red); }
.sort-bar { display:flex; align-items:center; gap:5px; flex-wrap:wrap; }
.bounty-info-row { display:flex; gap:5px; flex-wrap:wrap; align-items:center; margin-top:4px; }
.bounty-tag { font-size:9px; font-weight:700; padding:1px 5px; border-radius:4px; white-space:nowrap; }
.tag-hosp { background:rgba(239,68,68,.25); color:var(--red); border:1px solid rgba(239,68,68,.5); font-size:10px; padding:2px 7px; border-radius:5px; animation:hospTagPulse 1.5s ease-in-out infinite; }
@keyframes hospTagPulse { 0%,100%{box-shadow:none} 50%{box-shadow:0 0 6px rgba(239,68,68,.5)} }
.tag-jail   { background:rgba(245,158,11,.15); color:var(--amber);  border:1px solid rgba(245,158,11,.2); }
.tag-travel { background:rgba(99,102,241,.15);  color:var(--accent2); border:1px solid rgba(99,102,241,.2); }
.tag-ok     { background:rgba(16,185,129,.10);  color:var(--green);  border:1px solid rgba(16,185,129,.15); }
.bounty-search { width:100%; background:var(--bg); border:1px solid var(--border); border-radius:var(--r-sm); padding:7px 11px; color:var(--text); font-family:var(--font); font-size:12px; outline:none; transition:border-color .15s; }
.bounty-search:focus { border-color:var(--accent); }
.level-filter-row { display:flex; align-items:center; gap:6px; flex-wrap:wrap; }
.level-input { background:var(--bg); border:1px solid var(--border); border-radius:6px; padding:4px 7px; color:var(--accent2); font-family:var(--mono); font-size:11px; width:55px; text-align:center; outline:none; }
.level-input:focus { border-color:var(--accent); }
.reward-input { background:var(--bg); border:1px solid var(--border); border-radius:6px; padding:4px 7px; color:var(--amber); font-family:var(--mono); font-size:11px; width:70px; text-align:center; outline:none; }
.ff-input { background:var(--bg); border:1px solid var(--border); border-radius:6px; padding:4px 7px; color:var(--accent2); font-family:var(--mono); font-size:11px; width:50px; text-align:center; outline:none; }
.ff-input:focus { border-color:var(--accent); }
.bounty-load-more-inline { display:inline-flex; align-items:center; gap:4px; padding:3px 9px; border-radius:6px; background:rgba(99,102,241,.15); border:1px solid rgba(99,102,241,.3); color:var(--accent2); font-size:10px; font-weight:700; cursor:pointer; font-family:var(--font); transition:all .15s; }
.bounty-load-more-inline:hover { background:rgba(99,102,241,.28); }
.company-kpi-grid { display:grid; grid-template-columns:repeat(2,1fr); gap:7px; margin-bottom:8px; }
.company-kpi { background:var(--bg-el); border:1px solid var(--border); border-radius:var(--r-sm); padding:9px 7px; text-align:center; }
.kv { font-size:13px; font-weight:700; font-family:var(--mono); line-height:1; }
.kl { font-size:9px; color:var(--muted); text-transform:uppercase; letter-spacing:.5px; margin-top:4px; }
.company-emp-row { display:flex; align-items:center; gap:8px; padding:7px 14px; border-bottom:1px solid rgba(255,255,255,.04); }
.company-emp-row:last-child { border-bottom:none; }
.company-emp-name { flex:1; font-size:12px; font-weight:600; color:var(--text); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.company-emp-pos { font-size:10px; color:var(--muted); white-space:nowrap; min-width:60px; }
.company-emp-eff { font-size:11px; font-family:var(--mono); font-weight:700; white-space:nowrap; text-align:right; min-width:36px; }
.nw-ticker { background:linear-gradient(135deg,rgba(245,158,11,.07),rgba(251,191,36,.02)); border:1px solid rgba(245,158,11,.15); border-radius:var(--r); padding:16px 18px; flex-shrink:0; }
.nw-main-val { font-size:26px; font-weight:800; font-family:var(--mono); color:var(--amber); }
.nw-change { font-size:11px; font-family:var(--mono); font-weight:700; }
.nw-change.pos { color:var(--green); } .nw-change.neg { color:var(--red); }
.nw-chart-wrap { position:relative; width:100%; }
.nw-chart-svg { width:100%; display:block; overflow:visible; }
.nw-chart-tooltip { position:absolute; background:var(--bg-card); border:1px solid var(--border); border-radius:6px; padding:5px 9px; font-size:10px; font-family:var(--mono); color:var(--text); pointer-events:none; white-space:nowrap; box-shadow:0 4px 16px rgba(0,0,0,.6); display:none; z-index:9; }
.nw-chart-grid-lbl { font-size:8.5px; fill:var(--muted); font-family:var(--mono); }
.settings-section { display:flex; flex-direction:column; gap:0; }
.settings-section-title { font-size:9px; font-weight:700; color:var(--muted); text-transform:uppercase; letter-spacing:.8px; padding:12px 14px 7px; border-top:1px solid rgba(255,255,255,.05); }
.settings-section:first-child .settings-section-title { border-top:none; padding-top:4px; }
.alarm-row-item { display:flex; align-items:center; gap:10px; padding:9px 14px; border-bottom:1px solid rgba(255,255,255,.04); }
.alarm-lbl { font-size:12px; font-weight:600; color:var(--text); }
.alarm-desc { font-size:10px; color:var(--muted); margin-top:1px; }
.toggle { position:relative; display:inline-block; width:34px; height:19px; flex-shrink:0; }
.toggle input { opacity:0; width:0; height:0; }
.toggle-slider { position:absolute; inset:0; background:rgba(255,255,255,.1); border-radius:19px; transition:.2s; }
.toggle-slider::before { content:''; position:absolute; height:13px; width:13px; left:3px; bottom:3px; background:var(--dim); border-radius:50%; transition:.2s; }
.toggle input:checked + .toggle-slider { background:var(--accent); }
.toggle input:checked + .toggle-slider::before { transform:translateX(15px); background:white; }
.theme-grid { display:grid; grid-template-columns:repeat(2,1fr); gap:5px; }
.theme-btn { padding:9px 5px; border-radius:var(--r-sm); border:1px solid var(--border); background:var(--bg-el); font-size:10px; font-weight:600; color:var(--muted); cursor:pointer; font-family:var(--font); transition:all .15s; text-align:center; }
.theme-btn.active,.theme-btn:hover { border-color:var(--accent); color:var(--accent2); background:rgba(99,102,241,.08); }
.vis-grid { display:flex; flex-direction:column; gap:0; }
.vis-row { display:flex; align-items:center; justify-content:space-between; padding:8px 10px; background:rgba(0,0,0,.15); border-radius:var(--r-sm); border:1px solid rgba(255,255,255,.04); }
.vis-lbl { font-size:11px; color:var(--dim); font-weight:500; }
.api-key-row { display:flex; align-items:center; gap:10px; padding:10px 12px; background:rgba(0,0,0,.2); border-radius:var(--r-sm); border:1px solid rgba(255,255,255,.04); }
.api-key-status { flex:1; font-size:12px; font-weight:600; }
.api-key-status.set { color:var(--green); } .api-key-status.unset { color:var(--red); }
.sound-profile-grid { display:grid; grid-template-columns:repeat(3,1fr); gap:5px; }
.sp-btn { padding:9px 5px; border-radius:var(--r-sm); border:1px solid var(--border); background:var(--bg-el); font-size:10px; font-weight:600; color:var(--muted); cursor:pointer; font-family:var(--font); transition:all .15s; text-align:center; }
.sp-btn:hover,.sp-btn.active { border-color:var(--accent); color:var(--accent2); background:rgba(99,102,241,.08); }
.section-header { font-size:9px; font-weight:700; color:var(--muted); text-transform:uppercase; letter-spacing:.8px; padding:3px 0 7px; display:flex; align-items:center; gap:7px; }
.section-header::after { content:''; flex:1; height:1px; background:rgba(255,255,255,.06); }
#thud-toasts { position:fixed; bottom:20px; left:20px; z-index:999998; display:flex; flex-direction:column-reverse; gap:10px; pointer-events:none; width:340px; }
.toast { display:flex; align-items:flex-start; gap:14px; background:var(--bg-card); border-radius:var(--r); padding:16px; pointer-events:all; border:1px solid var(--border); border-left:4px solid var(--green); box-shadow:0 16px 60px rgba(0,0,0,.95); animation:toastIn .25s cubic-bezier(.34,1.56,.64,1); }
.toast.warn   { border-left-color:var(--amber); }
.toast.crit   { border-left-color:var(--red); }
.toast.info   { border-left-color:var(--blue); }
.toast.travel { border-left-color:var(--travel); }
.toast.landed { border-left-color:var(--red); background:rgba(239,68,68,.08); }
.toast.enemy-alert { border-left-color:#ff4400; background:rgba(255,68,0,.12); box-shadow:0 16px 60px rgba(0,0,0,.95),0 0 50px rgba(255,68,0,.5),0 0 0 2px #ff4400; animation:toastIn .25s cubic-bezier(.34,1.56,.64,1), enemyPulse .6s ease-in-out infinite alternate; }
@keyframes enemyPulse { from{box-shadow:0 16px 60px rgba(0,0,0,.95),0 0 30px rgba(255,68,0,.4)} to{box-shadow:0 16px 60px rgba(0,0,0,.95),0 0 70px rgba(255,68,0,.8),0 0 0 2px #ff4400} }
@keyframes toastIn  { from{opacity:0;transform:translateX(-20px) scale(.95)} to{opacity:1;transform:translateX(0) scale(1)} }
@keyframes toastOut { from{opacity:1;transform:translateX(0) scale(1)} to{opacity:0;transform:translateX(-16px) scale(.95)} }
.toast.leaving { animation:toastOut .2s ease forwards; }
.toast-icon { font-size:22px; flex-shrink:0; margin-top:1px; }
.toast-body { flex:1; min-width:0; }
.toast-title { font-size:14px; font-weight:800; color:var(--text); letter-spacing:.3px; }
.toast-msg   { font-size:12px; color:var(--dim); margin-top:4px; line-height:1.5; }
.toast-x { background:transparent; border:1px solid var(--border); color:var(--muted); border-radius:5px; width:20px; height:20px; cursor:pointer; font-size:10px; display:flex; align-items:center; justify-content:center; flex-shrink:0; }
.thud-modal-bg { position:fixed; inset:0; background:rgba(0,0,0,.85); z-index:999999; display:flex; align-items:center; justify-content:center; }
.thud-modal { background:var(--bg-card); border:1px solid var(--border); border-radius:var(--r-lg); padding:26px; width:340px; box-shadow:0 32px 80px rgba(0,0,0,.9); }
.thud-modal h3 { font-size:15px; font-weight:700; color:var(--text); margin-bottom:7px; }
.thud-modal p  { font-size:12px; color:var(--muted); margin-bottom:16px; line-height:1.7; }
.thud-modal input { width:100%; background:var(--bg); border:1px solid var(--border); border-radius:var(--r-sm); padding:10px 12px; color:var(--text); font-family:var(--mono); font-size:12px; outline:none; margin-bottom:11px; }
.thud-modal input:focus { border-color:var(--accent); }
.thud-modal textarea { width:100%; background:var(--bg); border:1px solid var(--border); border-radius:var(--r-sm); padding:10px 12px; color:var(--text); font-family:var(--font); font-size:12px; outline:none; margin-bottom:11px; min-height:70px; resize:vertical; }
.modal-btns { display:flex; gap:8px; justify-content:flex-end; }
.modal-save   { background:var(--green); color:#000; border:none; border-radius:var(--r-sm); padding:9px 18px; font-size:12px; font-weight:700; cursor:pointer; font-family:var(--font); }
.modal-cancel { background:var(--bg-el); color:var(--muted); border:1px solid var(--border); border-radius:var(--r-sm); padding:9px 14px; font-size:12px; font-weight:600; cursor:pointer; font-family:var(--font); }
.thud-resize-se { position:absolute; right:0; bottom:0; width:18px; height:18px; cursor:se-resize; z-index:10; }
.thud-resize-se::after { content:''; position:absolute; right:4px; bottom:4px; width:7px; height:7px; border-right:2px solid var(--muted); border-bottom:2px solid var(--muted); opacity:.2; }
.thud-resize-e { position:absolute; right:-3px; top:20px; bottom:20px; width:8px; cursor:ew-resize; z-index:9; }
.thud-resize-s { position:absolute; left:20px; right:20px; bottom:-3px; height:8px; cursor:ns-resize; z-index:9; }
#thud-fab { position:fixed; z-index:99998; width:36px; height:36px; border-radius:10px; background:linear-gradient(135deg,#1a0505,#2a0a0a); border:1px solid rgba(239,68,68,.3); box-shadow:0 4px 16px rgba(0,0,0,.85); cursor:grab; display:flex; align-items:center; justify-content:center; transition:all .2s; overflow:hidden; }
#thud-fab:active { cursor:grabbing; transform:scale(.93); }
#thud-fab:hover { border-color:rgba(239,68,68,.7); box-shadow:0 4px 20px rgba(239,68,68,.4); }
#thud-fab.on { border-color:rgba(239,68,68,.5); box-shadow:0 4px 20px rgba(239,68,68,.35); }
#thud-fab svg { width:24px; height:24px; filter:drop-shadow(0 0 4px rgba(239,68,68,.8)); }
.chain-block { padding:8px 14px; border-bottom:1px solid var(--border); flex-shrink:0; }
.chain-idle { display:flex; align-items:center; }
.chain-active-row { display:flex; align-items:center; }
.chain-hits { font-family:var(--mono); font-size:18px; font-weight:800; color:var(--chain); }
.chain-hits-unit { font-size:9px; color:var(--muted); font-weight:600; text-transform:uppercase; align-self:flex-end; margin-bottom:2px; }
.chain-divider { width:1px; height:24px; background:rgba(255,255,255,.1); }
.chain-timer { font-family:var(--mono); font-size:16px; font-weight:800; color:var(--green); transition:color .2s; }
.chain-timer.warn { color:var(--chain); animation:chainWarn .5s ease-in-out infinite alternate; }
@keyframes chainWarn { from{opacity:1} to{opacity:.5} }
.chain-mult { font-family:var(--mono); font-size:15px; font-weight:800; color:var(--amber); }
.chain-prog { height:3px; background:rgba(255,255,255,.06); border-radius:2px; overflow:hidden; margin-top:6px; }
.chain-prog-fill { height:100%; border-radius:2px; background:var(--chain); transition:width .3s linear; }
.pulse-dot { width:7px; height:7px; border-radius:50%; background:var(--chain); box-shadow:0 0 8px var(--chain); animation:pipPulse 1s ease-in-out infinite; flex-shrink:0; }
.war-grid { display:grid; grid-template-columns:repeat(4,1fr); gap:7px; }
.war-header { background:linear-gradient(135deg,rgba(239,68,68,.12),rgba(239,68,68,.03)); border:1px solid rgba(239,68,68,.3); border-radius:var(--r); padding:12px 14px; flex-shrink:0; animation:warPulse 2s ease-in-out infinite; }
@keyframes warPulse { 0%,100%{border-color:rgba(239,68,68,.3)} 50%{border-color:rgba(239,68,68,.6);box-shadow:0 0 20px rgba(239,68,68,.15)} }
.war-score-bar { height:14px; background:rgba(255,255,255,.06); border-radius:8px; overflow:hidden; display:flex; margin:8px 0 4px; }
.war-score-fill-us { background:linear-gradient(90deg,var(--green),#22c55e); border-radius:8px 0 0 8px; transition:width .5s; display:flex;align-items:center;justify-content:flex-end;padding-right:5px; }
.war-score-fill-them { background:linear-gradient(90deg,#dc2626,var(--red)); border-radius:0 8px 8px 0; transition:width .5s; display:flex;align-items:center;padding-left:5px; }
.war-target-row { display:flex; align-items:center; gap:8px; padding:10px 14px; border-bottom:1px solid rgba(255,255,255,.04); transition:background .12s; }
.war-target-row:last-child { border-bottom:none; }
.war-target-row:hover { background:rgba(255,255,255,.02); }
.war-target-row.online-now { background:rgba(239,68,68,.06); border-left:3px solid rgba(239,68,68,.5); }
.war-cb-warn { background:linear-gradient(135deg,rgba(239,68,68,.2),rgba(239,68,68,.05)); border:1px solid rgba(239,68,68,.5); border-radius:var(--r); padding:10px 14px; display:flex; align-items:center; gap:10px; animation:cbPulse .8s ease-in-out infinite alternate; }
@keyframes cbPulse { from{box-shadow:none} to{box-shadow:0 0 25px rgba(239,68,68,.4)} }
.search-result-row { display:flex; align-items:center; gap:8px; padding:10px 14px; border-bottom:1px solid rgba(255,255,255,.04); transition:background .12s; }
.search-result-row:last-child { border-bottom:none; }
.search-result-row:hover { background:rgba(255,255,255,.025); }
.war-cell { background:var(--bg-el); border:1px solid var(--border); border-radius:var(--r-sm); padding:12px 5px; text-align:center; }
.war-cell .wv { font-weight:700; font-size:20px; line-height:1; font-family:var(--mono); }
.war-cell .wl { font-size:9px; color:var(--muted); text-transform:uppercase; letter-spacing:.5px; margin-top:5px; }
.enemy-group { margin-bottom:4px; }
.enemy-group-header { display:flex; align-items:center; gap:7px; padding:8px 14px; background:rgba(0,0,0,.2); border-bottom:1px solid rgba(255,255,255,.05); cursor:pointer; user-select:none; }
.enemy-group-header:hover { background:rgba(255,255,255,.03); }
.enemy-group-name { font-size:10px; font-weight:700; letter-spacing:.7px; text-transform:uppercase; color:var(--dim); flex:1; }
.enemy-group-count { font-size:9px; font-family:var(--mono); color:var(--muted); background:var(--bg-el); border:1px solid var(--border); border-radius:10px; padding:1px 6px; }
.enemy-group-arrow { font-size:9px; color:var(--muted); transition:transform .15s; }
.enemy-group-arrow.open { transform:rotate(90deg); }
.enemy-group-body { display:flex; flex-direction:column; }
.enemy-group-add-btn { display:inline-flex; align-items:center; gap:4px; padding:4px 10px; border-radius:6px; background:rgba(99,102,241,.1); border:1px solid rgba(99,102,241,.2); color:var(--accent2); font-size:10px; font-weight:700; cursor:pointer; font-family:var(--font); transition:all .15s; }
.enemy-group-add-btn:hover { background:rgba(99,102,241,.2); }
.enemy-group-del-btn { display:inline-flex; align-items:center; gap:3px; padding:3px 8px; border-radius:5px; background:rgba(239,68,68,.08); border:1px solid rgba(239,68,68,.15); color:var(--red); font-size:9px; font-weight:700; cursor:pointer; font-family:var(--font); transition:all .15s; }
.enemy-group-del-btn:hover { background:rgba(239,68,68,.2); }
.enemy-group-color-dot { width:8px; height:8px; border-radius:50%; flex-shrink:0; }
.enemy-row { padding:12px 14px; border-bottom:1px solid rgba(255,255,255,.05); display:flex; flex-direction:column; gap:6px; }
.enemy-row:last-child { border-bottom:none; }
.enemy-row:hover { background:rgba(255,255,255,.02); }
.enemy-row.in-hospital { background:rgba(239,68,68,.06); border-left:3px solid var(--red); }
.enemy-row.traveling   { background:rgba(99,102,241,.05); border-left:3px solid var(--travel); }
.enemy-top { display:flex; align-items:center; gap:8px; flex-wrap:wrap; }
.enemy-name-link { font-size:13px; font-weight:800; color:var(--text); text-decoration:none; }
.enemy-name-link:hover { color:var(--red); }
.enemy-level { font-size:10px; font-family:var(--mono); font-weight:700; color:var(--amber); }
.enemy-meta { display:flex; gap:6px; flex-wrap:wrap; align-items:center; }
.enemy-tag { font-size:9px; font-weight:700; padding:2px 6px; border-radius:4px; white-space:nowrap; }
.etag-hosp   { background:rgba(239,68,68,.2);  color:var(--red);    border:1px solid rgba(239,68,68,.4); }
.etag-travel { background:rgba(99,102,241,.15); color:var(--accent2); border:1px solid rgba(99,102,241,.3); }
.etag-ok     { background:rgba(16,185,129,.1);  color:var(--green);  border:1px solid rgba(16,185,129,.2); }
.etag-jail   { background:rgba(245,158,11,.1);  color:var(--amber);  border:1px solid rgba(245,158,11,.2); }
.etag-job    { background:rgba(59,130,246,.08); color:var(--blue);   border:1px solid rgba(59,130,246,.15); }
.enemy-note { font-size:10px; color:var(--muted); font-style:italic; padding:4px 8px; background:rgba(0,0,0,.2); border-radius:4px; border-left:2px solid var(--amber); }
.enemy-actions { display:flex; gap:5px; flex-wrap:wrap; }
.enemy-action-btn { display:inline-flex; align-items:center; gap:3px; padding:4px 10px; border-radius:6px; font-size:10px; font-weight:700; text-decoration:none; cursor:pointer; border:none; font-family:var(--font); transition:all .15s; }
.eab-attack  { background:rgba(239,68,68,.15);  border:1px solid rgba(239,68,68,.25);  color:var(--red); }
.eab-attack:hover { background:rgba(239,68,68,.3); }
.eab-profile { background:rgba(255,255,255,.06); border:1px solid rgba(255,255,255,.1); color:var(--dim); }
.eab-profile:hover { border-color:var(--accent); color:var(--accent2); }
.eab-note    { background:rgba(245,158,11,.1);  border:1px solid rgba(245,158,11,.2);  color:var(--amber); }
.eab-note:hover { background:rgba(245,158,11,.2); }
.eab-remove  { background:rgba(255,255,255,.04); border:1px solid rgba(255,255,255,.08); color:var(--muted); }
.eab-remove:hover { background:rgba(239,68,68,.15); border-color:var(--red); color:var(--red); }
.eab-grp { background:rgba(99,102,241,.1); border:1px solid rgba(99,102,241,.2); color:var(--accent2); }
.eab-grp:hover { background:rgba(99,102,241,.25); }
.enemy-add-row { display:flex; gap:6px; align-items:center; }
.enemy-id-input { flex:1; background:var(--bg); border:1px solid var(--border); border-radius:var(--r-sm); padding:7px 11px; color:var(--text); font-family:var(--mono); font-size:12px; outline:none; }
.enemy-id-input:focus { border-color:var(--red); }
.group-color-picker { display:flex; gap:5px; flex-wrap:wrap; margin-top:4px; }
.group-color-swatch { width:18px; height:18px; border-radius:50%; cursor:pointer; border:2px solid transparent; transition:border-color .15s; }
.group-color-swatch.selected { border-color:var(--text); }
.loading-state { display:flex; align-items:center; justify-content:center; padding:40px; color:var(--muted); font-size:12px; letter-spacing:1px; animation:opPulse 1.5s ease-in-out infinite; }
@keyframes opPulse { 0%,100%{opacity:1} 50%{opacity:.3} }
.empty-state { display:flex; flex-direction:column; align-items:center; justify-content:center; padding:36px 18px; color:var(--muted); gap:10px; text-align:center; }
.empty-state .es-icon { font-size:32px; opacity:.2; }
.empty-state .es-text { font-size:12px; letter-spacing:.5px; font-weight:600; }
.empty-state .es-sub  { font-size:11px; opacity:.6; }
.toggle-more { display:inline-flex; align-items:center; gap:5px; padding:5px 11px; border-radius:20px; border:1px solid var(--border); color:var(--muted); cursor:pointer; font-size:10px; font-weight:600; transition:all .15s; margin-top:7px; }
.toggle-more:hover { border-color:var(--accent); color:var(--accent2); }
.ts { font-family:var(--mono); font-size:10px; color:var(--muted); }
.fullscreen-alert { position:fixed; inset:0; background:rgba(2,4,8,.97); display:none; flex-direction:column; align-items:center; justify-content:center; z-index:9999999; }
.fullscreen-alert.active { display:flex; animation:fadeIn .4s ease; }
@keyframes fadeIn { from{opacity:0} to{opacity:1} }
.fs-box { text-align:center; padding:48px; border-radius:20px; max-width:420px; background:rgba(99,102,241,.06); border:2px solid var(--accent); box-shadow:0 0 80px rgba(99,102,241,.3); }
.fs-title { font-size:20px; font-weight:700; letter-spacing:2px; text-transform:uppercase; margin:16px 0; }
.fs-msg   { font-size:15px; color:var(--dim); line-height:1.6; }
.fs-close { margin-top:24px; background:rgba(99,102,241,.2); border:1px solid var(--accent); color:var(--accent2); border-radius:var(--r-sm); padding:12px 28px; font-size:12px; font-weight:700; cursor:pointer; font-family:var(--font); }
.scan-btn { display:inline-flex; align-items:center; gap:5px; padding:7px 14px; border-radius:var(--r-sm); background:rgba(99,102,241,.15); border:1px solid rgba(99,102,241,.3); color:var(--accent2); font-size:11px; font-weight:700; cursor:pointer; font-family:var(--font); transition:all .15s; }
.scan-btn:hover { background:rgba(99,102,241,.3); }
.scan-btn:disabled { opacity:.4; cursor:not-allowed; }
.spinning { animation:spin 1s linear infinite; }
@keyframes spin { to { transform:rotate(360deg); } }
.sort-btn { padding:4px 9px; border-radius:6px; border:1px solid var(--border); background:var(--bg-el); color:var(--muted); font-size:10px; font-weight:700; cursor:pointer; font-family:var(--font); transition:all .15s; }
.sort-btn.active { border-color:var(--accent); color:var(--accent2); background:rgba(99,102,241,.1); }
.threshold-input { background:var(--bg); border:1px solid var(--border); border-radius:5px; padding:3px 7px; color:var(--accent2); font-family:var(--mono); font-size:11px; width:50px; text-align:center; outline:none; }
.threshold-input:focus { border-color:var(--accent); }
.fab-pin-row { display:flex; align-items:center; justify-content:space-between; padding:9px 14px; border-bottom:1px solid rgba(255,255,255,.04); }
.fab-pin-lbl { font-size:12px; font-weight:600; color:var(--text); }
.fab-pin-desc { font-size:10px; color:var(--muted); margin-top:1px; }
.ti-hero { background:linear-gradient(135deg,rgba(99,102,241,.12),rgba(99,102,241,.02)); border:1px solid rgba(99,102,241,.2); border-radius:var(--r); padding:14px 16px; display:flex; align-items:center; gap:12px; flex-shrink:0; }
.ti-hero-icon { font-size:28px; flex-shrink:0; }
.ti-hero-main { flex:1; min-width:0; }
.ti-hero-title { font-size:12px; font-weight:800; color:var(--accent2); letter-spacing:.8px; text-transform:uppercase; }
.ti-hero-sub { font-size:10px; color:var(--muted); margin-top:3px; }
.ti-hero-badge { font-size:9px; font-family:var(--mono); font-weight:700; color:var(--green); background:rgba(16,185,129,.1); border:1px solid rgba(16,185,129,.2); border-radius:4px; padding:2px 7px; flex-shrink:0; }
.ti-item-row { display:flex; align-items:center; gap:10px; padding:10px 14px; border-bottom:1px solid rgba(255,255,255,.04); transition:background .12s; }
.ti-item-row:last-child { border-bottom:none; }
.ti-item-row:hover { background:rgba(255,255,255,.025); }
.ti-item-row.xanax-row { background:rgba(16,185,129,.04); border-left:3px solid rgba(16,185,129,.4); }
.ti-item-row.xanax-row:hover { background:rgba(16,185,129,.07); }
.ti-item-icon { font-size:20px; flex-shrink:0; width:28px; text-align:center; }
.ti-item-info { flex:1; min-width:0; }
.ti-item-name { font-size:12px; font-weight:700; color:var(--text); }
.ti-item-country { font-size:10px; color:var(--muted); margin-top:2px; display:flex; align-items:center; gap:5px; flex-wrap:wrap; }
.ti-item-flag { font-size:14px; }
.ti-profit-block { text-align:right; flex-shrink:0; }
.ti-profit-val { font-family:var(--mono); font-size:13px; font-weight:800; }
.ti-profit-val.pos { color:var(--green); }
.ti-profit-val.neg { color:var(--red); }
.ti-profit-val.neu { color:var(--muted); }
.ti-profit-lbl { font-size:9px; color:var(--muted); margin-top:2px; text-transform:uppercase; letter-spacing:.5px; }
.ti-profit-min { font-size:9px; font-family:var(--mono); color:var(--dim); }
.ti-restock-row { display:flex; align-items:center; gap:10px; padding:9px 14px; border-bottom:1px solid rgba(255,255,255,.04); }
.ti-restock-row:last-child { border-bottom:none; }
.ti-restock-row:hover { background:rgba(255,255,255,.02); }
.ti-restock-name { flex:1; font-size:12px; font-weight:600; color:var(--text); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.ti-restock-time { font-family:var(--mono); font-size:11px; font-weight:700; color:var(--amber); white-space:nowrap; }
.ti-restock-time.soon { color:var(--red); animation:opPulse 1s ease-in-out infinite; }
.ti-category-badge { display:inline-flex; align-items:center; gap:3px; font-size:9px; font-weight:800; padding:2px 7px; border-radius:20px; letter-spacing:.4px; }
.ti-cat-xanax   { background:rgba(16,185,129,.15); color:var(--green);   border:1px solid rgba(16,185,129,.25); }
.ti-cat-plushie { background:rgba(129,140,248,.15); color:var(--accent2); border:1px solid rgba(129,140,248,.25); }
.ti-cat-flower  { background:rgba(251,191,36,.1);   color:var(--amber);  border:1px solid rgba(251,191,36,.2); }
.ti-filter-row { display:flex; gap:5px; flex-wrap:wrap; padding:8px 14px 10px; border-bottom:1px solid rgba(255,255,255,.05); }
.ti-stock-badge { font-family:var(--mono); font-size:9px; color:var(--muted); background:rgba(255,255,255,.05); border:1px solid rgba(255,255,255,.08); border-radius:4px; padding:1px 5px; }
.ti-empty { display:flex; flex-direction:column; align-items:center; justify-content:center; padding:28px; color:var(--muted); gap:7px; text-align:center; }
.ti-empty-icon { font-size:24px; opacity:.2; }
.ti-empty-txt { font-size:11px; font-weight:600; }
.ti-ts { font-family:var(--mono); font-size:9px; color:var(--muted); text-align:center; padding:7px; flex-shrink:0; }
.ti-err { background:rgba(239,68,68,.06); border:1px solid rgba(239,68,68,.15); border-radius:var(--r); padding:10px 14px; font-size:11px; color:var(--red); text-align:center; flex-shrink:0; }
`;
 
    const SHAYA_LOGO_SVG = `<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><defs><linearGradient id="sg" x1="0%" y1="0%" x2="100%" y2="100%"><stop offset="0%" style="stop-color:#ff2222"/><stop offset="50%" style="stop-color:#cc0000"/><stop offset="100%" style="stop-color:#880000"/></linearGradient></defs><polygon points="62,10 38,10 28,28 52,28 40,48 58,48 30,90 72,90 80,70 56,70 68,50 50,50" fill="url(#sg)"/><polygon points="68,8 74,14 66,18" fill="#ff4444" opacity="0.8"/><polygon points="30,8 24,16 34,14" fill="#dd2222" opacity="0.7"/><polygon points="26,88 20,82 30,80" fill="#ff3333" opacity="0.75"/><polygon points="74,92 80,84 70,86" fill="#cc1111" opacity="0.7"/></svg>`;
    const FAB_LOGO_SVG   = `<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><defs><linearGradient id="fg" x1="0%" y1="0%" x2="100%" y2="100%"><stop offset="0%" style="stop-color:#ff3333"/><stop offset="100%" style="stop-color:#aa0000"/></linearGradient></defs><polygon points="62,10 38,10 28,28 52,28 40,48 58,48 30,90 72,90 80,70 56,70 68,50 50,50" fill="url(#fg)"/><polygon points="68,8 74,14 66,18" fill="#ff5555" opacity="0.9"/><polygon points="30,8 24,16 34,14" fill="#ee2222" opacity="0.8"/><polygon points="26,88 20,82 30,80" fill="#ff4444" opacity="0.8"/><polygon points="74,92 80,84 70,86" fill="#cc1111" opacity="0.75"/></svg>`;
 
    // ═══════════════════════════════════════════════════════════════
    // UTILITY
    // ═══════════════════════════════════════════════════════════════
    class Utils {
        static fmtMoney(n) { if (!n&&n!==0) return '—'; const abs=Math.abs(n),sign=n<0?'-':''; if(abs>=1e9)return sign+'$'+(abs/1e9).toFixed(2)+'B'; if(abs>=1e6)return sign+'$'+(abs/1e6).toFixed(2)+'M'; if(abs>=1e3)return sign+'$'+(abs/1e3).toFixed(1)+'K'; return sign+'$'+abs.toLocaleString('de-DE'); }
        static fmtStat(n) { if (!n&&n!==0) return '—'; if(n>=1e9)return(n/1e9).toFixed(2)+'B'; if(n>=1e6)return(n/1e6).toFixed(2)+'M'; if(n>=1e3)return(n/1e3).toFixed(1)+'K'; return n.toLocaleString('de-DE'); }
        static fmtTravel(sec) { if(sec<=0)return'—'; const h=Math.floor(sec/3600),m=Math.ceil((sec%3600)/60); return h>0?`${h}h ${m}m`:(m<=0?'< 1m':`${m}m`); }
        static fmtHM(sec) { if(!sec||sec<=0)return'—'; const h=Math.floor(sec/3600),m=Math.floor((sec%3600)/60); if(h>0)return`${h}h ${m}m`; if(m>0)return`${m}m`; return'< 1m'; }
        static fmtDhm(sec) { if(!sec||sec<=0)return'Fertig'; const d=Math.floor(sec/86400),h=Math.floor((sec%86400)/3600),m=Math.floor((sec%3600)/60); if(d>0)return`${d}T ${h}h`; if(h>0)return`${h}h ${m}m`; return`${m}m`; }
        static fmtSec(sec) { if(!sec||sec<=0)return'0s'; const h=Math.floor(sec/3600),m=Math.floor((sec%3600)/60),s=sec%60; if(h>0)return`${h}h ${m}m`; if(m>0)return`${m}m ${s}s`; return`${s}s`; }
        static arrTime(secLeft) { if(!secLeft||secLeft<=0)return'—'; return new Date(Date.now()+secLeft*1000).toLocaleTimeString('de-DE',{hour:'2-digit',minute:'2-digit'}); }
        static tctTime() { return new Date().toLocaleTimeString('de-DE',{hour:'2-digit',minute:'2-digit',timeZone:'UTC'}); }
        static pctBar(pct,color,size='') { const w=Math.min(100,Math.max(0,pct||0)); return `<div class="bar ${size}"><div class="bar-fill" style="width:${w}%;background:${color};"></div></div>`; }
        static escHtml(s) { return String(s||'').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;'); }
        static inactiveAgo(ts) { if(!ts)return{text:'?',cls:'ago-d'}; const s=Math.floor(Date.now()/1000-ts),m=Math.floor(s/60),h=Math.floor(s/3600),d=Math.floor(s/86400); if(s<3600)return{text:m+'m',cls:'ago-m'}; if(h<24)return{text:h+'h',cls:'ago-h'}; return{text:d+'d',cls:'ago-d'}; }
        static now() { return Math.floor(Date.now()/1000); }
        static buildDonut(data,total) {
            const r=28,cx=32,cy=32,circ=2*Math.PI*r; let off=0;
            const segs=data.map(({count,color})=>{ if(!count||!total)return''; const dash=(count/total)*circ; const seg=`<circle cx="${cx}" cy="${cy}" r="${r}" fill="none" stroke="${color}" stroke-width="5" stroke-dasharray="${dash} ${circ-dash}" stroke-dashoffset="${-off}" stroke-linecap="butt"/>`; off+=dash; return seg; }).filter(Boolean);
            if(!segs.length)segs.push(`<circle cx="${cx}" cy="${cy}" r="${r}" fill="none" stroke="#2a2a30" stroke-width="5"/>`);
            return `<svg viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg" style="width:52px;height:52px;transform:rotate(-90deg)"><circle cx="${cx}" cy="${cy}" r="${r}" fill="none" stroke="#18181c" stroke-width="5"/>${segs.join('')}</svg>`;
        }
    }
 
    // ═══════════════════════════════════════════════════════════════
    // AUDIO
    // ═══════════════════════════════════════════════════════════════
    class AudioManager {
        constructor(store) { this.store=store; this._ctx=null; }
        get ctx() { if(!this._ctx){try{this._ctx=new(window.AudioContext||window.webkitAudioContext)();}catch(e){}}return this._ctx; }
        play(type) {
            if(!this.store.get('sound')&&type!=='enemy')return; if(!this.ctx)return;
            const c=this.ctx,now=c.currentTime,p=this.store.get('soundProfile')||'classic';
            const note=(freq,start,dur,vol,wave='sine')=>{const osc=c.createOscillator(),g=c.createGain();osc.connect(g);g.connect(c.destination);osc.type=wave;osc.frequency.value=freq;g.gain.setValueAtTime(0,now+start);g.gain.linearRampToValueAtTime(vol,now+start+0.03);g.gain.exponentialRampToValueAtTime(0.001,now+start+dur);osc.start(now+start);osc.stop(now+start+dur+0.05);};
            if(type==='enemy'){[0,.1,.2,.3].forEach(o=>note(880,o,.08,.25,'sawtooth'));note(440,.05,.3,.15,'square');return;}
            if(p==='subtle'){if(type==='travel_warn'){note(440,0,.2,.08);note(550,.25,.2,.08);}else if(type==='travel_crit'){[0,.15,.3].forEach(o=>note(660,o,.12,.1));}else if(type==='full'){note(330,0,.3,.08);}else note(400,0,.25,.06);return;}
            if(p==='military'){if(type==='travel_crit'){[0,.12,.24,.36].forEach(o=>note(1400,o,.07,.2,'sawtooth'));}else{note(1000,0,.05,.15,'square');note(800,.08,.05,.1,'square');}return;}
            if(type==='travel_warn'){[0,.22].forEach(o=>note(660,o,.2,.25));}
            else if(type==='travel_crit'){[0,.16,.32].forEach(o=>note(880,o,.13,.18,'square'));}
            else if(type==='landing'){[0,.15,.3].forEach(o=>note(520,o,.15,.2));}
            else if(type==='full'){const osc=c.createOscillator(),g=c.createGain();osc.connect(g);g.connect(c.destination);osc.type='sine';osc.frequency.setValueAtTime(440,now);osc.frequency.linearRampToValueAtTime(880,now+.4);g.gain.setValueAtTime(0,now);g.gain.linearRampToValueAtTime(.25,now+.08);g.gain.linearRampToValueAtTime(0,now+.5);osc.start(now);osc.stop(now+.55);}
            else{note(520,0,.4,.18);}
        }
    }
 
    // ═══════════════════════════════════════════════════════════════
    // STORE / TOAST / API
    // ═══════════════════════════════════════════════════════════════
    class Store {
        get(key,def) { const v=GM_getValue('thud_'+key,undefined); return v!==undefined?v:def; }
        set(key,val) { GM_setValue('thud_'+key,val); return val; }
    }
 
    class ToastManager {
        constructor() { this._fired={}; }
        show(icon,title,msg,style='',duration=10000) {
            let c=document.getElementById('thud-toasts');
            if(!c){c=document.createElement('div');c.id='thud-toasts';document.body.appendChild(c);}
            const t=document.createElement('div'); t.className=`toast ${style}`;
            t.innerHTML=`<div class="toast-icon">${icon}</div><div class="toast-body"><div class="toast-title">${title}</div><div class="toast-msg">${msg}</div></div><button class="toast-x">✕</button>`;
            t.querySelector('.toast-x').addEventListener('click',()=>{t.classList.add('leaving');setTimeout(()=>t.remove(),220);});
            c.appendChild(t);
            setTimeout(()=>{if(t.parentNode){t.classList.add('leaving');setTimeout(()=>t.remove(),220);}},duration);
        }
        showLandingTimer(destName) {
            let c=document.getElementById('thud-toasts');
            if(!c){c=document.createElement('div');c.id='thud-toasts';document.body.appendChild(c);}
            const t=document.createElement('div'); t.className='toast landed';
            const topItems=(DEST_TOP_ITEMS[destName]||[]).slice(0,2).join(' · ');
            t.innerHTML=`<div class="toast-icon">🛬</div><div class="toast-body"><div class="toast-title">GELANDET: ${Utils.escHtml(destName)}</div><div class="toast-msg">⏱ <span id="thud-land-timer">15</span>s Einkaufszeit${topItems?`<br>🎯 ${topItems}`:''}</div></div><button class="toast-x">✕</button>`;
            t.querySelector('.toast-x').addEventListener('click',()=>{t.classList.add('leaving');setTimeout(()=>t.remove(),220);});
            c.appendChild(t);
            let rem=SHOPPING_TIME_SEC;
            const iv=setInterval(()=>{rem--;const el=t.querySelector('#thud-land-timer');if(el)el.textContent=rem;if(rem<=0){clearInterval(iv);if(t.parentNode){t.classList.add('leaving');setTimeout(()=>t.remove(),220);}}},1000);
            hud.audio.play('landing');
        }
        fire(key,icon,title,msg,style,audio) { if(this._fired[key])return; this._fired[key]=true; hud.audio.play(audio||'notify'); this.show(icon,title,msg,style); }
        reset(key) { delete this._fired[key]; }
    }
 
    class TornAPI {
        constructor(store) { this.store=store; }
        get key() { return this.store.get('apiKey',''); }
        call(url,cb) {
            if(!this.key){cb(null,'NO_KEY');return;}
            GM_xmlhttpRequest({method:'GET',url, onload:r=>{try{const d=JSON.parse(r.responseText);if(d.error)cb(null,d.error.code);else cb(d,null);}catch(e){cb(null,'PARSE');}}, onerror:()=>cb(null,'NET')});
        }
        v2(path,cb) { this.call(`${API_BASE}/v2/${path}&key=${this.key}`,cb); }
        userBasicAndProfile(cb) { this.v2('user?selections=basic,profile',cb); }
        userTravel(cb)           { this.v2('user?selections=travel',cb); }
        userBars(cb)             { this.v2('user?selections=bars',cb); }
        userCooldowns(cb)        { this.v2('user?selections=cooldowns',cb); }
        userEducation(cb)        { this.v2('user?selections=education',cb); }
        userBattleStats(cb)      { this.v2('user?selections=battlestats',cb); }
        userWorkStats(cb)        { this.v2('user?selections=workstats',cb); }
        userNetworth(cb)         { this.v2('user?selections=networth',cb); }
        factionBasicMembers(cb)  { this.v2('faction?selections=basic,members,positions,timestamp',cb); }
        factionBasicMembersById(facId,cb) { this.call(`${API_BASE}/v2/faction/${facId}?selections=basic,members,positions,timestamp&key=${this.key}`,cb); }
        factionChain(cb)         { this.v2('faction?selections=chain',cb); }
        factionChainById(facId,cb) { this.call(`${API_BASE}/v2/faction/${facId}?selections=chain&key=${this.key}`,cb); }
        companyProfile(cb)       { this.v2('company?selections=profile',cb); }
        companyEmployees(cb)     { this.v2('company?selections=employees',cb); }
        tornBountiesPage(offset,cb) { this.v2(`torn?selections=bounties&offset=${offset}`,cb); }
        playerProfile(playerId,cb) { this.call(`${API_BASE}/v2/user/${playerId}?selections=basic,profile&key=${this.key}`,cb); }
        playerFactionInfo(playerId,cb) { this.call(`${API_BASE}/v2/user/${playerId}?selections=faction&key=${this.key}`,cb); }
        playerSearch(name,cb)     { this.v2(`user?selections=basic,profile&search=${encodeURIComponent(name)}`,cb); }
        factionWars(cb)           { this.v2('faction?selections=basic,wars,chain,members',cb); }
        factionWarsById(facId,cb)  { this.call(`${API_BASE}/v2/faction/${facId}?selections=basic,wars,chain,members&key=${this.key}`,cb); }
        enemyFactionMembers(facId,cb) { this.call(`${API_BASE}/v2/faction/${facId}?selections=basic,members&key=${this.key}`,cb); }
    }
 
    // ═══════════════════════════════════════════════════════════════
    // STATE
    // ═══════════════════════════════════════════════════════════════
    const DEFAULT_ENEMY_GROUPS = [
        { id:'default', name:'Allgemein',        color:'#ef4444', collapsed:false },
        { id:'level',   name:'Level Targets',    color:'#f59e0b', collapsed:false },
        { id:'ff',      name:'FairFight Targets', color:'#6366f1', collapsed:false },
        { id:'revenge', name:'Revanche',          color:'#10b981', collapsed:false },
    ];
    const GROUP_COLORS = ['#ef4444','#f59e0b','#6366f1','#10b981','#3b82f6','#f43f5e','#8b5cf6','#06b6d4','#84cc16','#ec4899'];
 
    class StateManager {
        constructor(store) {
            this.store=store;
            this.userBasic=null; this.userProfile=null; this.userTravel=null;
            this.bars=null; this.cooldowns=null; this.education=null;
            this.battleStats=null; this.workStats=null;
            this.faction={}; this.members={}; this.chain=null; this.chainEndTime=null;
            this.companyProfile=null; this.companyEmployees=[];
            this.networthData=null; this.networthHistory=store.get('nwHistory',[]);
            this.bountyData=null; this.bountyAllData=null; this.bountyLastFetch=0;
            this.bountySort=store.get('bountySort','reward');
            this.bountySearch='';
            this.bountyFilterHosp=store.get('bountyFilterHosp',false);
            this.bountyLevelMin=store.get('bountyLevelMin',1);
            this.bountyLevelMax=store.get('bountyLevelMax',100);
            this.bountyRewardMin=store.get('bountyRewardMin',0);
            this.bountyRewardMax=store.get('bountyRewardMax',0);
            this.bountyFFMin=store.get('bountyFFMin',0);
            this.bountyFFMax=store.get('bountyFFMax',0);
            this.bountyDisplayCount=80; this.bountyLoadedPages=0;
            this.bountyLoading=false; this.bountyScanningAll=false;
            this.searchQuery=''; this.searchResults=[]; this.searchLoading=false;
            this.searchSort=store.get('searchSort','name'); this.searchSortDir=store.get('searchSortDir','asc');
            this.searchFilterStatus=store.get('searchFilterStatus','all');
            this.searchFilterLvMin=store.get('searchFilterLvMin',1); this.searchFilterLvMax=store.get('searchFilterLvMax',100);
            this.recruitJoinCache={};
            this.enemies=store.get('enemies',[]);
            this.enemyGroups=store.get('enemyGroups', JSON.parse(JSON.stringify(DEFAULT_ENEMY_GROUPS)));
            this.enemyData={};
            this.enemyFetchRunning=false;
            this._enemyTravelAlerted=store.get('_enemyTravelAlerted',{});
            this.ffData={};
            this.lastUpdate=null; this.uptimeSec=0;
            this.showAllOnline=false; this.showAllOffline=false;
            this._fsAlertFired=false; this._landingFired=false;
            this._lastTravelSec=0; this._prevTravelDest='';
            this.energyThresholds=store.get('energyThresholds',[50,75,100]);
            this.nerveThresholds=store.get('nerveThresholds',[50,100]);
            this._energyAlerted=store.get('_energyAlerted',{});
            this._nerveAlerted=store.get('_nerveAlerted',{});
            // Travel Items — aus Cache laden damit Tab sofort Daten zeigt
            this.travelItemsLoading=false;
            this.travelItemsLastFetch=0;
            this.travelItemsError=null;
            this.travelItemsFilter=store.get('travelItemsFilter','all');
            this.yataFavOnly=store.get('yataFavOnly',false);
            this.yataRestockAlarm=store.get('yataRestockAlarm',true);
            // War state
            this.warData=null;
            this.enemyFactionData=null;
            this.enemyFactionMembers={};
            this.warLastFetch=0;
            // Player Search state
            this.searchResults=[];
            this.searchLoading=false;
            this.searchQuery='';
            this.searchSortField=store.get('searchSortField','name');
            this.searchSortDir=store.get('searchSortDir','asc');
            this.searchFilterStatus=store.get('searchFilterStatus','all');
            this.searchLvMin=store.get('searchLvMin',1);
            this.searchLvMax=store.get('searchLvMax',100);
            this.searchFFMin=store.get('searchFFMin',0);
            this.searchFFMax=store.get('searchFFMax',0);
            // Cache-Init: gespeicherte Daten direkt laden
            try{
                const cachedHist=GM_getValue('restockHistory2',null);
                const cachedRaw=GM_getValue('yataRawStocksCache',null);
                const cachedTs=GM_getValue('yataLastFetch',0);
                if(cachedHist&&cachedRaw&&cachedTs>0){
                    const hist=JSON.parse(cachedHist);
                    const raw=JSON.parse(cachedRaw);
                    this.restockHistory=hist;
                    this.yataRawStocks=raw;
                    this.travelItemsLastFetch=Number(cachedTs);
                    // Profits aus Raw aufbauen
                    const CMAP={mex:'Mexico',cay:'Cayman Islands',can:'Canada',haw:'Hawaii',uni:'United Kingdom',arg:'Argentina',swi:'Switzerland',jap:'Japan',chi:'China',uae:'UAE',sou:'South Africa'};
                    const profits=[];
                    for(const[code,co] of Object.entries(raw)){
                        if(!co||typeof co!=='object')continue;
                        const country=CMAP[code]||code;
                        for(const item of (Array.isArray(co.stocks)?co.stocks:[])){
                            if(item&&Number(item.quantity||0)>0&&Number(item.cost||0)>0)
                                profits.push({item_name:item.name||'',country,stock:Number(item.quantity||0),buy_price:Number(item.cost||0)});
                        }
                    }
                    this.travelItemsProfits=profits;
                    // Restocks aus History aufbauen
                    const now2=Math.floor(Date.now()/1000);
                    const restocks=[];
                    for(const[,h] of Object.entries(hist)){
                        if(h.lastQty!==0)continue;
                        const lastEv=h.events.filter(e=>e.type==='restock').slice(-1)[0];
                        let rAt=h.emptyAt>0?h.emptyAt+7200:(lastEv?lastEv.ts+7200:0);
                        if(rAt>now2-300)restocks.push({item_name:h.name,country:h.country,restock_at:rAt});
                    }
                    restocks.sort((a,b)=>a.restock_at-b.restock_at);
                    this.travelItemsRestocks=restocks;
                } else {
                    this.travelItemsProfits=null;
                    this.travelItemsRestocks=null;
                }
            }catch(e){
                this.travelItemsProfits=null;
                this.travelItemsRestocks=null;
            }
        }
        setBountySort(v) { this.bountySort=v; this.store.set('bountySort',v); }
        setBountyFilterHosp(v) { this.bountyFilterHosp=v; this.store.set('bountyFilterHosp',v); }
        setBountyLevelMin(v) { this.bountyLevelMin=Number(v)||1; this.store.set('bountyLevelMin',this.bountyLevelMin); }
        setBountyLevelMax(v) { this.bountyLevelMax=Number(v)||100; this.store.set('bountyLevelMax',this.bountyLevelMax); }
        setBountyRewardMin(v) { this.bountyRewardMin=Number(v)||0; this.store.set('bountyRewardMin',this.bountyRewardMin); }
        setBountyRewardMax(v) { this.bountyRewardMax=Number(v)||0; this.store.set('bountyRewardMax',this.bountyRewardMax); }
        setBountyFFMin(v) { this.bountyFFMin=Number(v)||0; this.store.set('bountyFFMin',this.bountyFFMin); }
        setBountyFFMax(v) { this.bountyFFMax=Number(v)||0; this.store.set('bountyFFMax',this.bountyFFMax); }
        setTravelItemsFilter(v) { this.travelItemsFilter=v; this.store.set('travelItemsFilter',v); }
        setYataFavOnly(v) { this.yataFavOnly=v; this.store.set('yataFavOnly',v); }
        setYataRestockAlarm(v) { this.yataRestockAlarm=v; this.store.set('yataRestockAlarm',v); }
        addEnemy(id,name,note,groupId) {
            if (this.enemies.find(e=>String(e.id)===String(id))) return false;
            this.enemies.push({id:String(id),name:name||`#${id}`,note:note||'',groupId:groupId||'default',addedAt:Utils.now()});
            this.store.set('enemies',this.enemies); return true;
        }
        removeEnemy(id) { this.enemies=this.enemies.filter(e=>String(e.id)!==String(id)); delete this.enemyData[id]; this.store.set('enemies',this.enemies); }
        updateEnemyNote(id,note) { const e=this.enemies.find(e=>String(e.id)===String(id)); if(e){e.note=note;this.store.set('enemies',this.enemies);} }
        updateEnemyGroup(id,groupId) { const e=this.enemies.find(e=>String(e.id)===String(id)); if(e){e.groupId=groupId;this.store.set('enemies',this.enemies);} }
        updateEnemyData(id,data) { this.enemyData[String(id)]=data; }
        addEnemyGroup(name,color) {
            const id='grp_'+Date.now();
            this.enemyGroups.push({id,name,color:color||'#6366f1',collapsed:false});
            this.store.set('enemyGroups',this.enemyGroups); return id;
        }
        removeEnemyGroup(id) {
            if (['default'].includes(id)) return;
            this.enemies.forEach(e=>{ if(e.groupId===id) e.groupId='default'; });
            this.enemyGroups=this.enemyGroups.filter(g=>g.id!==id);
            this.store.set('enemyGroups',this.enemyGroups);
            this.store.set('enemies',this.enemies);
        }
        toggleGroupCollapse(id) {
            const g=this.enemyGroups.find(g=>g.id===id); if(g){g.collapsed=!g.collapsed;this.store.set('enemyGroups',this.enemyGroups);}
        }
        get alarms() { return this.store.get('alarms',{}); }
        setAlarm(k,v) { const a=this.alarms; a[k]=v; this.store.set('alarms',a); }
        isAlarm(k) { const d=ALARM_DEFAULTS[k]; return this.alarms[k]!==undefined?this.alarms[k]:(d?d.enabled:false); }
        get visibility() { return this.store.get('visibility',{}); }
        isVisible(k) { const v=this.visibility; return v[k]!==undefined?v[k]:(VISIBILITY_DEFAULTS[k]?VISIBILITY_DEFAULTS[k].enabled:true); }
        setVisible(k,v) { const vis=this.visibility; vis[k]=v; this.store.set('visibility',vis); }
        get travelAlerts() { return this.store.get('travelAlerts',{}); }
        isTravelAlert(k) { const t=this.travelAlerts; return t[k]!==undefined?t[k]:true; }
        setTravelAlert(k,v) { const t=this.travelAlerts; t[k]=v; this.store.set('travelAlerts',t); }
        setEnergyThresholds(arr) { this.energyThresholds=arr; this.store.set('energyThresholds',arr); }
        setNerveThresholds(arr) { this.nerveThresholds=arr; this.store.set('nerveThresholds',arr); }
        _getEnemyBountyReward(id) {
            const data=this.bountyData||this.bountyAllData||[];
            for (const b of data) { const tid=String(b.target_id??b.id??''); if(tid===String(id))return Number(b.reward??b.amount??0); }
            return 0;
        }
    }
 
    const ALARM_DEFAULTS = {
        travel_warn:    { enabled:true,  label:'✈ Travel Warnung',      desc:'Alarm wenn Ankunft < 7 Min' },
        travel_crit:    { enabled:true,  label:'✈ Travel Kritisch',      desc:'Alarm wenn Ankunft < 2 Min' },
        landing_timer:  { enabled:true,  label:'🛬 Landing Timer',       desc:'15s Countdown bei Landung' },
        energy_full:    { enabled:true,  label:'⚡ Energie Voll',         desc:'Alarm wenn Energie 100%' },
        energy_thresh:  { enabled:true,  label:'⚡ Energie Schwellwert',  desc:'Alarm bei konfigurierten Werten' },
        energy_over151: { enabled:false, label:'⚡ Energie über 151',     desc:'Alarm wenn Energie > 151 (Happy Jump)' },
        nerve_full:     { enabled:true,  label:'🧠 Nerve Voll',           desc:'Alarm wenn Nerve 100%' },
        nerve_thresh:   { enabled:true,  label:'🧠 Nerve Schwellwert',    desc:'Alarm bei konfigurierten Werten' },
        hp_crit:        { enabled:true,  label:'❤ HP unter 50%',         desc:'Toast-Alarm' },
        hospital_free:  { enabled:true,  label:'🏥 Krankenhaus Frei',     desc:'Alarm letzte 60 Sek' },
        jail_free:      { enabled:true,  label:'🚔 Gefängnis Frei',       desc:'Alarm letzte 60 Sek' },
        drug_ready:     { enabled:false, label:'💊 Drogen Bereit',        desc:'Alarm wenn Drug-CD weg' },
        booster_ready:  { enabled:false, label:'🧪 Booster Bereit',       desc:'Alarm wenn Booster-CD weg' },
        medical_ready:  { enabled:false, label:'💉 Medical Bereit',       desc:'Alarm wenn Medical-CD weg' },
        chain_warn:     { enabled:true,  label:'⛓ Chain Warnung',        desc:'Alarm wenn Chain < 1 Min' },
        enemy_travel:   { enabled:true,  label:'🎯 Enemy reist ab',       desc:'Alarm wenn Enemy-Target abreist' },
    };
 
    const VISIBILITY_DEFAULTS = {
        show_happy:       { enabled:true,  label:'😊 Happy Bar' },
        show_jail:        { enabled:true,  label:'🚔 Gefängnis' },
        show_hospital:    { enabled:true,  label:'🏥 Krankenhaus' },
        show_drug_cd:     { enabled:true,  label:'💊 Drug CD' },
        show_booster_cd:  { enabled:true,  label:'🧪 Booster CD' },
        show_medical_cd:  { enabled:true,  label:'💉 Medical CD' },
        show_education:   { enabled:true,  label:'📚 Lehrgang' },
        show_tip:         { enabled:true,  label:'💡 Smart Tipp' },
        show_quick_links: { enabled:true,  label:'🔗 Quick Links' },
        show_tct_time:    { enabled:true,  label:'🕒 TCT Zeit' },
        show_battle_stats:{ enabled:true,  label:'⚔ Battle Stats (Ich)' },
    };
 
    const TABS_DEF = [
        { id:'personal',      icon:'👤', label:'Ich'      },
        { id:'faction',       icon:'⚔',  label:'Fraktion' },
        { id:'war',           icon:'🔥',  label:'War'      },
        { id:'bounty',        icon:'🎯',  label:'Bounty'   },
        { id:'enemy',         icon:'💀',  label:'Enemy'    },
        { id:'search',        icon:'🔍',  label:'Suche'    },
        { id:'company',       icon:'🏢',  label:'Firma'    },
        { id:'networth',      icon:'💰',  label:'Vermögen' },
        { id:'yata_live',     icon:'🌍',  label:'Restock'  },
        { 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||{};
            // Torn API v2 liefert entweder cooldown_until (Timestamp) oder cooldown (Sekunden Restzeit)
            const _cdVal=(v)=>{
                if(!v&&v!==0)return 0;
                if(typeof v==='number'){
                    if(v>1700000000)return v; // Unix timestamp
                    if(v>0)return now+v; // seconds remaining
                    return 0;
                }
                if(typeof v==='object'){
                    // API v2: cooldown_until = Unix timestamp, cooldown = seconds left
                    const until=Number(v.cooldown_until??v.until??0);
                    const secs=Number(v.cooldown??v.time_left??0);
                    if(until>1700000000)return until;
                    if(secs>0)return now+secs;
                    return 0;
                }
                return 0;
            };
            const drugSec=Math.max(0,_cdVal(cd.drug)-now),boostSec=Math.max(0,_cdVal(cd.booster)-now),medSec=Math.max(0,_cdVal(cd.medical)-now);
            if(drugSec>120)t.reset('drug_ready'); if(boostSec>120)t.reset('booster_ready'); if(medSec>120)t.reset('medical_ready');
            if(s.isAlarm('drug_ready')&&_cdVal(cd.drug)>0&&drugSec<=0)t.fire('drug_ready','💊','DROGEN BEREIT','Drug-Cooldown abgelaufen!','info','notify');
            if(s.isAlarm('booster_ready')&&_cdVal(cd.booster)>0&&boostSec<=0)t.fire('booster_ready','🧪','BOOSTER BEREIT','Booster-Cooldown abgelaufen!','info','notify');
            if(s.isAlarm('medical_ready')&&_cdVal(cd.medical)>0&&medSec<=0)t.fire('medical_ready','💉','MEDICAL BEREIT','Cooldown abgelaufen!','info','notify');
            if(s.isAlarm('enemy_travel')){
                for(const enemy of s.enemies){
                    const ed=s.enemyData[enemy.id]; if(!ed)continue;
                    const eStat=(ed.status?.state||'').toLowerCase(),eDest=ed.status?.description||'';
                    const travelKey=`${enemy.id}_${eDest}`;
                    if(eStat==='traveling'&&!s._enemyTravelAlerted[travelKey]){
                        s._enemyTravelAlerted[travelKey]=true;hud.store.set('_enemyTravelAlerted',s._enemyTravelAlerted);
                        hud.audio.play('enemy');t.show('⚔',`⚠ ENEMY REIST AB!`,`${enemy.name} fliegt: ${eDest}`,'enemy-alert',20000);
                    }
                    if(eStat!=='traveling'){Object.keys(s._enemyTravelAlerted).forEach(k=>{if(k.startsWith(enemy.id+'_'))delete s._enemyTravelAlerted[k];});}
                }
            }
        }
    }
 
    // ═══════════════════════════════════════════════════════════════
    // CHAIN MANAGER
    // ═══════════════════════════════════════════════════════════════
    class ChainManager {
        constructor(state) { this.state=state; this._raf=null; this._lastSec=-1; }
        process(chain) {
            const s=this.state;
            if(chain&&chain.current>0){s.chain=chain;s.chainEndTime=Date.now()+Number(chain.timeout)*1000;this.startTick();}
            else{s.chain=null;s.chainEndTime=null;this.stopTick();}
            this.renderSection();
            if(hud.store.get('minimized',false))hud.renderer.renderMini();
        }
        startTick() { if(!this._raf){ const loop=()=>{ this.tick(); this._raf=requestAnimationFrame(loop); }; this._raf=requestAnimationFrame(loop); } }
        stopTick()  { if(this._raf){cancelAnimationFrame(this._raf);this._raf=null;this._lastSec=-1;} }
        tick() {
            const s=this.state; if(!s.chainEndTime){this.stopTick();return;}
            const remMs=Math.max(0,s.chainEndTime-Date.now());
            const remSec=Math.floor(remMs/1000);
            if(remSec===this._lastSec)return;
            this._lastSec=remSec;
            const m=Math.floor(remSec/60), sec=remSec%60;
            const warn=remMs<=CHAIN_WARN_SEC*1000;
            const el=document.getElementById('thud-chain-timer');
            if(el){
                el.textContent=remMs<=300000?`${m}:${String(sec).padStart(2,'0')}`:`${m}m`;
                warn?el.classList.add('warn'):el.classList.remove('warn');
            }
            const progEl=document.getElementById('thud-chain-prog-fill');
            if(progEl&&s.chain){
                const ms=[10,25,50,100,250,500,1000,2500,5000,10000,25000,50000,100000];
                const nM=ms.find(x=>x>s.chain.current)||s.chain.current,pM=ms[ms.indexOf(nM)-1]||0;
                const prog=((s.chain.current-pM)/(nM-pM))*100;
                progEl.style.width=Math.max(1,Math.min(100,prog))+'%';
            }
            const miniEl=document.querySelector('.thud-mini-bar .thud-mini-chip .thud-mini-lbl');
            if(miniEl&&s.chain?.current>0){
                miniEl.textContent=remMs<=300000?`${m}:${String(sec).padStart(2,'0')}`:`${m}m`;
            }
            if(warn&&s.isAlarm('chain_warn'))hud.toasts.fire('chain_warn','⛓','CHAIN LÄUFT AB!',`Nur noch ${m}m ${sec}s!`,'crit','travel_crit');
            if(!warn)hud.toasts.reset('chain_warn');
            if(remMs===0){s.chain=null;s.chainEndTime=null;this.stopTick();this.renderSection();}
        }
        renderSection() {
            const el=document.getElementById('thud-chain-block'); if(!el)return;
            const c=this.state.chain;
            if(!c||c.current===0){
                el.className='chain-block';
                el.innerHTML=`<div class="chain-idle"><div class="pulse-dot" style="opacity:.2;animation:none;background:var(--muted)"></div><span style="letter-spacing:.5px;font-size:10px;font-weight:600;margin-left:8px;">Keine aktive Chain</span></div>`;
                return;
            }
            const ms=[10,25,50,100,250,500,1000,2500,5000,10000,25000,50000,100000];
            const nM=ms.find(x=>x>c.current)||c.current,pM=ms[ms.indexOf(nM)-1]||0;
            const prog=((c.current-pM)/(nM-pM))*100;
            const mt=[[10,1],[25,1.5],[50,2],[100,2.5],[250,3],[500,3.5],[1000,4]];
            let mul=1; for(const[thresh,v]of mt)if(c.current>=thresh)mul=v;
            const rem=Math.max(0,this.state.chainEndTime-Date.now());
            const totalSec=Math.floor(rem/1000),m=Math.floor(totalSec/60),sec=totalSec%60;
            const warn=rem<=CHAIN_WARN_SEC*1000;
            const isWU=c.current<CHAIN_START_HIT,hits=isWU?c.current:c.current-CHAIN_START_HIT;
            const timerTxt=rem<=300000?`${m}:${String(sec).padStart(2,'0')}`:`${m}m`;
            el.className='chain-block active';
            el.innerHTML=`<div class="chain-active-row">
                <div class="pulse-dot"></div>
                <span style="font-size:9px;font-weight:700;letter-spacing:1px;color:var(--chain);margin-left:5px;">${isWU?'WARMUP':'CHAIN'}</span>
                <div class="chain-hits" style="margin-left:7px;">${hits.toLocaleString()}</div>
                <div class="chain-hits-unit" style="margin-left:2px;">hits</div>
                <div class="chain-divider" style="margin:0 10px;"></div>
                <div style="display:flex;flex-direction:column;align-items:center;">
                    <div class="chain-timer ${warn?'warn':''}" id="thud-chain-timer">${timerTxt}</div>
                    <div style="font-size:7px;color:var(--muted);text-transform:uppercase;margin-top:1px;">timeout</div>
                </div>
                <div style="display:flex;flex-direction:column;align-items:center;margin-left:10px;">
                    <div class="chain-mult">×${mul}</div>
                    <div style="font-size:7px;color:var(--muted);text-transform:uppercase;margin-top:1px;">mult</div>
                </div>
            </div>
            <div class="chain-prog"><div class="chain-prog-fill" id="thud-chain-prog-fill" style="width:${Math.max(1,Math.min(100,prog))}%"></div></div>`;
        }
    }
 
    // ═══════════════════════════════════════════════════════════════
    // FACTION HELPER
    // ═══════════════════════════════════════════════════════════════
    class FactionHelper {
        static getStatus(member) {
            // Torn API v2: status.state = "Okay"/"Hospital"/"Traveling"/"Jail"/"Federal"
            // last_action.status = "Online"/"Idle"/"Offline"
            const statusObj=typeof member.status==='object'?member.status:{state:member.status||''};
            const state=(statusObj.state||statusObj.description||'').toLowerCase();
            const lastStatus=(member.last_action?.status||'').toLowerCase();
            const ago=Date.now()/1000-(member.last_action?.timestamp||0);
            // Hard stati zuerst prüfen
            if(state.includes('hospital'))return'hospital';
            if(state.includes('federal'))return'jail';
            if(state.includes('jail'))return'jail';
            if(state.includes('travel')||state.includes('abroad'))return'traveling';
            // Für "Okay" / "" / unbekannt: online/idle via last_action
            if(lastStatus==='online')return'online';
            if(lastStatus==='idle')return'idle';
            if(lastStatus==='offline')return'offline';
            // Fallback über Timestamp
            if(ago<120)return'online';
            if(ago<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;
            const _processFactionData=(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 if(members&&typeof members==='object'){this.state.members=members;}
                this.state.faction={...this.state.faction,...(data.faction||data)};
                const el=document.getElementById('thud-faction-name');
                const n=this.state.faction.name||this.state.faction.faction?.name;
                if(el&&n)el.textContent=`${SCRIPT_TITLE} — ${n.toUpperCase()}`;
                this.fetchRecruitJoinTimestamps();
                if(hud.activeTab==='faction')this.renderer.render();
                if(hud.store.get('minimized',false))this.renderer.renderMini();
            };
            // Versuche /faction direkt (Owner-Key) - bei Fehler Faction-ID aus User-Profil holen
            this.api.factionBasicMembers((data,err)=>{
                if(!err&&data&&(data.members||data.faction)){
                    _processFactionData(data); return;
                }
                // Fallback: Faction-ID aus gecachtem User-Profil
                const up=this.state.userBasic||this.state.userProfile;
                const facId=Number(up?.faction?.faction_id??up?.faction?.id??this.state._knownFacId??0);
                if(!facId){
                    this.api.userBasicAndProfile((pd)=>{
                        if(!pd)return;
                        this.state.userBasic=pd.profile||pd.basic||pd;
                        this.state.userProfile=pd.profile||pd;
                        const fid=Number(pd.faction?.faction_id??pd.faction?.id??pd.profile?.faction?.faction_id??pd.profile?.faction?.id??0);
                        if(!fid)return;
                        this.state._knownFacId=fid;
                        this.api.factionBasicMembersById(fid,(d2)=>_processFactionData(d2));
                    });
                    return;
                }
                this.state._knownFacId=facId;
                this.api.factionBasicMembersById(facId,(d2)=>_processFactionData(d2));
            });
        }
        fetchRecruitJoinTimestamps() {
            const recruits=Object.values(this.state.members).filter(m=>(m.position||m.rank||'').toLowerCase()==='recruit');
            if(!recruits.length)return;
            recruits.forEach(m=>{
                const cached=this.state.recruitJoinCache[m.id];
                if(cached&&cached.ts&&(Utils.now()-cached.fetchedAt)<3600)return;
                this.api.playerFactionInfo(m.id,(data)=>{
                    if(!data)return;
                    const fac=data.faction||data;
                    const joinTs=Number(fac.faction_join_date??fac.join_date??fac.joined??0);
                    const daysIn=Number(fac.days_in_faction??0);
                    const effectiveTs = joinTs > 0 ? joinTs : (daysIn > 0 ? (Utils.now() - daysIn*86400) : 0);
                    if(effectiveTs>0){
                        this.state.recruitJoinCache[m.id]={
                            ts: effectiveTs,
                            exact: joinTs > 0,
                            fetchedAt: Utils.now()
                        };
                        if(hud.activeTab==='faction'&&this.state._factionSubTab==='details')this.renderer.render();
                    }
                });
            });
        }
        fetchChain() {
            if(!this.api.key)return;
            this.api.factionChain((data,err)=>{
                if(!err&&data){hud.chainManager.process(data.chain||null);return;}
                const facId=Number(this.state._knownFacId??this.state.faction?.ID??this.state.faction?.id??0);
                if(!facId)return;
                this.api.factionChainById(facId,(d2)=>{if(d2)hud.chainManager.process(d2.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 wallet2=Number(nw.wallet??nw.cash??0);
                const stocks2=Number(nw.stockmarket??nw.stocks??0);
                const bank2=Number(nw.bank??0);
                const bazaar2=Number(nw.bazaar??0);
                const liquid=wallet2+stocks2;
                // Track liquid separately for accurate chart
                const now=Utils.now(),hist=this.state.networthHistory;
                hist.push({ts:now,val:liquid,wallet:wallet2,stocks:stocks2,bank:bank2});
                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();
                });
            }
        }
 
        // ─── DroqsDB Travel Items ────────────────────────────────
        fetchTravelItems(onDone) {
            const s=this.state;
            if(s.travelItemsLoading)return;
            s.travelItemsLoading=true;
            s.travelItemsError=null;
            if(hud.activeTab==='yata_live')hud.renderer.render();
 
            let profitsDone=false, restocksDone=false;
 
            const tryFinish=()=>{
                if(!profitsDone||!restocksDone)return;
                s.travelItemsLoading=false;
                s.travelItemsLastFetch=Utils.now();
                if(hud.activeTab==='yata_live')hud.renderer.render();
                if(onDone)onDone();
            };
 
            GM_xmlhttpRequest({
                method:'GET',
                url:'https://droqsdb.com/api/public/v1/top-profits',
                onload:(res)=>{
                    try{
                        if(!res||res.status!==200)throw new Error('HTTP '+res.status);
                        const data=JSON.parse(res.responseText);
                        if(!Array.isArray(data))throw new Error('Kein Array');
                        s.travelItemsProfits=data.filter(item=>item&&isTravelTarget(item.item_name||item.name));
                    }catch(e){
                        s.travelItemsError='Profite: '+e.message;
                        s.travelItemsProfits=[];
                    }
                    profitsDone=true; tryFinish();
                },
                onerror:()=>{
                    s.travelItemsError='Netzwerkfehler (Profite)';
                    s.travelItemsProfits=[];
                    profitsDone=true; tryFinish();
                }
            });
 
            GM_xmlhttpRequest({
                method:'GET',
                url:'https://droqsdb.com/api/public/v1/restocking-soon',
                onload:(res)=>{
                    try{
                        if(!res||res.status!==200)throw new Error('HTTP '+res.status);
                        const data=JSON.parse(res.responseText);
                        if(!Array.isArray(data))throw new Error('Kein Array');
                        s.travelItemsRestocks=data.filter(item=>item&&isTravelTarget(item.item_name||item.name));
                    }catch(e){
                        s.travelItemsError=(s.travelItemsError?s.travelItemsError+' | ':'')+'Restocks: '+e.message;
                        s.travelItemsRestocks=[];
                    }
                    restocksDone=true; tryFinish();
                },
                onerror:()=>{
                    s.travelItemsError=(s.travelItemsError?s.travelItemsError+' | ':'')+'Netzwerkfehler (Restocks)';
                    s.travelItemsRestocks=[];
                    restocksDone=true; tryFinish();
                }
            });
        }
 
 
        // ─── Travel Items: YATA for stock + self-learning restock tracker ──
        fetchTravelItems(onDone) {
            const s=this.state;
            if(s.travelItemsLoading)return;
            s.travelItemsLoading=true;
            s.travelItemsError=null;
            s.travelItemsDebug='';
            if(hud.activeTab==='yata_live'||hud.activeTab==='yata_live')hud.renderer.render();
            const done=()=>{ s.travelItemsLoading=false; s.travelItemsLastFetch=Utils.now(); if(hud.activeTab==='yata_live'||hud.activeTab==='yata_live')hud.renderer.render(); if(onDone)onDone(); };
            const COUNTRY_MAP={mex:'Mexico',cay:'Cayman Islands',can:'Canada',haw:'Hawaii',uni:'United Kingdom',arg:'Argentina',swi:'Switzerland',jap:'Japan',chi:'China',uae:'UAE',sou:'South Africa'};
            // Load persisted history
            let hist={};
            try{const saved=GM_getValue('restockHistory2',null);if(saved)hist=JSON.parse(saved);}catch(e){}
            const now=Utils.now();
 
            GM_xmlhttpRequest({
                method:'GET',
                url:'https://yata.yt/api/v1/travel/export/?key=',
                onload:(res)=>{
                    try{
                        if(!res||res.status!==200)throw new Error('YATA HTTP '+res.status);
                        const raw=JSON.parse(res.responseText);
                        const stocksData=raw.stocks||{};
                        s.yataRawStocks=stocksData;
                        const profits=[];
                        // Which keys were seen with qty>0
                        const seenWithStock=new Set();
 
                        for(const [code,countryObj] of Object.entries(stocksData)){
                            if(!countryObj||typeof countryObj!=='object')continue;
                            const country=COUNTRY_MAP[code]||code;
                            const itemArr=Array.isArray(countryObj.stocks)?countryObj.stocks:[];
                            const updateTs=Number(countryObj.update||0);
 
                            for(const item of itemArr){
                                if(!item)continue;
                                const id=Number(item.id||0);
                                const qty=Number(item.quantity||0);
                                const cost=Number(item.cost||0);
                                const name=item.name||'Item #'+id;
                                const key=code+'_'+id;
 
                                if(qty>0) seenWithStock.add(key);
 
                                if(!hist[key])hist[key]={name,country,id,code,events:[],emptyAt:0,lastQty:-1,lastUpdateTs:0};
                                const h=hist[key];
                                h.name=name; h.country=country;
                                const prevQty=h.lastQty;
                                const prevUpdateTs=h.lastUpdateTs;
 
                                // Detect restock: updateTs changed AND qty>0
                                if(qty>0&&updateTs>0&&updateTs!==prevUpdateTs&&prevQty>=0){
                                    // New restock observed
                                    h.events.push({ts:updateTs,type:'restock',qty,obs:now});
                                    h.emptyAt=0; // reset empty timer
                                    if(h.events.length>50)h.events.shift();
                                } else if(qty>0&&prevQty===-1){
                                    // First ever observation with stock
                                    h.events.push({ts:updateTs||now,type:'restock',qty,obs:now});
                                    if(h.events.length>50)h.events.shift();
                                }
                                h.lastQty=qty;
                                if(updateTs>0)h.lastUpdateTs=updateTs;
 
                                if(qty>0&&cost>0){
                                    profits.push({item_name:name,country,stock:qty,buy_price:cost});
                                }
                            }
                        }
 
                        // Items NOT seen or seen with qty=0 → mark as empty
                        // For all tracked history items: if not in seenWithStock → empty
                        for(const [key,h] of Object.entries(hist)){
                            if(seenWithStock.has(key))continue;
                            const prevQty=h.lastQty;
                            if(prevQty>0){
                                // Just went empty
                                h.emptyAt=now;
                                h.events.push({ts:now,type:'empty',qty:0,obs:now});
                                if(h.events.length>50)h.events.shift();
                            }
                            if(h.lastQty!==-1)h.lastQty=0;
                        }
 
                        // Persist — yataRawStocks und Timestamp auch cachen für Script-Neustart
                        try{
                            GM_setValue('restockHistory2',JSON.stringify(hist));
                            GM_setValue('yataRawStocksCache',JSON.stringify(stocksData));
                            GM_setValue('yataLastFetch',Utils.now());
                        }catch(e){}
                        s.restockHistory=hist;
                        s.travelItemsProfits=profits;
 
                        // Build restock timers for restock tab
                        const restocks=[];
                        for(const [key,h] of Object.entries(hist)){
                            if(h.lastQty!==0)continue;
                            const lastRestockEv=h.events.filter(e=>e.type==='restock').slice(-1)[0];
                            let restockAt=0;
                            if(h.emptyAt>0){
                                restockAt=h.emptyAt+7200;
                            } else if(lastRestockEv){
                                restockAt=lastRestockEv.ts+7200;
                            }
                            if(restockAt>now-300){
                                restocks.push({item_name:h.name,country:h.country,restock_at:restockAt});
                            }
                        }
                        restocks.sort((a,b)=>a.restock_at-b.restock_at);
                        s.travelItemsRestocks=restocks;
                    }catch(e){
                        s.travelItemsDebug='YATA Fehler: '+e.message;
                        s.travelItemsProfits=s.travelItemsProfits||[];
                        s.travelItemsRestocks=s.travelItemsRestocks||[];
                    }
                    done();
                },
                onerror:()=>{s.travelItemsDebug='YATA Netzwerkfehler';s.travelItemsProfits=[];s.travelItemsRestocks=[];done();}
            });
        }
 
        // ─── Restock History Tracker ──────────────────────────────────
        updateRestockHistory(snapshot) {
            // snapshot: {ts, stocks:{key:{name,country,code,qty,updateTs}}}
            const MAX=50; // keep last 50 events per item
            let history=this.state.restockHistory||{};
            for(const [key,cur] of Object.entries(snapshot.stocks)){
                if(!history[key])history[key]={name:cur.name,country:cur.country,events:[]};
                const events=history[key].events;
                const last=events[events.length-1];
                // Detect restock: was 0, now >0 OR updateTs changed and qty>0
                const wasEmpty=last&&last.qty===0;
                const nowFull=cur.qty>0;
                const tsChanged=last&&cur.updateTs&&cur.updateTs!==last.updateTs&&cur.qty>0;
                if(!last||(wasEmpty&&nowFull)||tsChanged){
                    events.push({ts:snapshot.ts,qty:cur.qty,updateTs:cur.updateTs,type:wasEmpty&&nowFull?'restock':'update'});
                    if(events.length>MAX)events.shift();
                }
                // Update current qty
                history[key].qty=cur.qty;
                history[key].updateTs=cur.updateTs;
            }
            this.state.restockHistory=history;
            // Persist to GM storage
            try{ GM_setValue('restockHistory',JSON.stringify(history)); }catch(e){}
        }
 
        loadRestockHistory() {
            try{
                const saved=GM_getValue('restockHistory',null);
                if(saved)this.state.restockHistory=JSON.parse(saved);
            }catch(e){ this.state.restockHistory={}; }
        }
 
        fetchWar(onDone) {
            if(!this.api.key)return;
            const _processWarData=(data,err)=>{
                if(err||!data)return;
                const s=this.state;
                const wars=data.wars||data.faction?.wars||null;
                s.warData=wars;
                // Check for active war or pending (preparation) war
                let activeWar=null, pendingWar=null;
                if(wars&&typeof wars==='object'){
                    const warList=wars.ranked_wars??wars;
                    const _processW=(id,w)=>{
                        if(w.winner)return; // beendet
                        const nowTs=Math.floor(Date.now()/1000);
                        const startTs=Number(w.start??w.war_start??0);
                        if(startTs>0&&startTs>nowTs){
                            // Krieg noch nicht gestartet = Vorbereitungsphase
                            if(!pendingWar)pendingWar={...w,war_id:id};
                        } else {
                            if(!activeWar)activeWar={...w,war_id:id};
                        }
                    };
                    if(Array.isArray(warList)){warList.forEach((w,i)=>_processW(i,w));}
                    else if(typeof warList==='object'){Object.entries(warList).forEach(([id,w])=>_processW(id,w));}
                }
                s.activeWar=activeWar||null;
                s.pendingWar=pendingWar||null;
                // Chain of our faction from this endpoint
                if(data.chain)hud.chainManager.process(data.chain);
                // If active or pending war, fetch enemy faction members
                const warForMembers=activeWar||pendingWar;
                if(warForMembers){
                    const ourFacId=Number(data.ID??data.id??0);
                    const factions=warForMembers.factions||warForMembers;
                    let enemyId=0;
                    if(factions&&typeof factions==='object'){
                        for(const[fid,fd] of Object.entries(factions)){
                            if(Number(fid)!==ourFacId){enemyId=Number(fid);break;}
                        }
                    }
                    if(enemyId&&enemyId!==s._lastEnemyFacId){
                        s._lastEnemyFacId=enemyId;
                        this.api.enemyFactionMembers(enemyId,(fdata,ferr)=>{
                            if(!ferr&&fdata){
                                s.enemyFactionData=fdata;
                                const mems=fdata.members||[];
                                s.enemyFactionMembers={};
                                if(Array.isArray(mems))mems.forEach(m=>{s.enemyFactionMembers[m.id]=m;});
                                else Object.assign(s.enemyFactionMembers,mems);
                            }
                            if(hud.activeTab==='war')hud.renderer.render();
                            if(onDone)onDone();
                        });
                    } else {
                        if(hud.activeTab==='war')hud.renderer.render();
                        if(onDone)onDone();
                    }
                } else {
                    if(hud.activeTab==='war')hud.renderer.render();
                    if(onDone)onDone();
                }
                s.warLastFetch=Utils.now();
            };
            // Direkter Aufruf (Owner-Key) - bei Fehler Faction-ID als Fallback
            this.api.factionWars((data,err)=>{
                if(!err&&data&&(data.members||data.wars||data.faction)){
                    _processWarData(data,null);
                } else {
                    const facId=Number(this.state._knownFacId??this.state.faction?.ID??this.state.faction?.id??0);
                    if(!facId){if(hud.activeTab==='war')hud.renderer.render();if(onDone)onDone();return;}
                    this.api.factionWarsById(facId,(d2,e2)=>_processWarData(d2,e2));
                }
            });
        }
        fetchPlayerSearch(query,onDone) {
            if(!this.api.key||!query){onDone([]);return;}
            this.api.playerSearch(query,(data,err)=>{
                if(err||!data){onDone([]);return;}
                // v2 player search returns array or object
                let results=[];
                if(Array.isArray(data))results=data;
                else if(data.users)results=Array.isArray(data.users)?data.users:Object.values(data.users);
                else if(typeof data==='object'&&data.id)results=[data];
                else results=Object.values(data).filter(v=>v&&typeof v==='object'&&v.id);
                onDone(results);
            });
        }
        fetchAll() { this.fetchPersonal(); this.fetchFaction(); this.fetchChain(); this.fetchCompany(); this.fetchNetworth(); this.fetchEnemyData(); this.fetchWar(); }
    }
 
    // ═══════════════════════════════════════════════════════════════
    // 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 'war':           return this.renderWar();
                case 'search':        return this.renderSearch();
                case 'yata_live':  return this.renderYataLive();
                case 'notifications': return this.renderNotifications();
                case 'settings':      return this.renderSettings();
                default:              return this.renderPersonal();
            }
        }
 
        renderMini() {
            const b=document.getElementById('thud-mini'); if(!b)return;
            const m=Object.values(this.state.members);
            const online=m.filter(x=>FactionHelper.getStatus(x)!=='offline').length;
            const c=this.state.chain,energy=this.state.bars?.energy,nerve=this.state.bars?.nerve;
            const tSec=Math.max(0,Number(this.state.userTravel?.travel?.time_left)||0);
            const tDest=this.state.userTravel?.travel?.destination||'';
            let chips='';
            chips+=`<div class="thud-mini-chip"><span style="width:5px;height:5px;border-radius:50%;background:var(--online);box-shadow:0 0 4px var(--online);display:inline-block;"></span><span class="thud-mini-val">${online}</span><span class="thud-mini-lbl">/${m.length}</span></div>`;
            if(energy){const ep=Math.round((energy.current/energy.maximum)*100);const ec=ep>=100?'var(--red)':ep>=75?'var(--amber)':'var(--blue)';chips+=`<div class="thud-mini-chip"><span style="font-size:9px;">⚡</span><span class="thud-mini-val" style="color:${ec};">${energy.current}</span></div>`;}
            if(nerve){const np=Math.round((nerve.current/nerve.maximum)*100);const nc=np>=100?'var(--red)':'var(--dim)';chips+=`<div class="thud-mini-chip"><span style="font-size:9px;">🧠</span><span class="thud-mini-val" style="color:${nc};">${nerve.current}</span></div>`;}
            if(c&&c.current>0){
                const rem=Math.max(0,this.state.chainEndTime-Date.now());
                const ts=Math.floor(rem/1000),m2=Math.floor(ts/60),s2=ts%60;
                const warn=rem<=CHAIN_WARN_SEC*1000;
                const timerTxt=rem<=300000?`${m2}:${String(s2).padStart(2,'0')}`:` ${m2}m`;
                chips+=`<div class="thud-mini-chip"><span style="font-size:9px;">⛓</span><span class="thud-mini-val" style="color:${warn?'var(--red)':'var(--chain)'};">${c.current}</span><span class="thud-mini-lbl">${timerTxt}</span></div>`;
            }
            if(tSec>0){const dInfo=DESTINATIONS[tDest]||{flag:'✈'};const tw=tSec<=TRAVEL_WARN_SEC;chips+=`<div class="thud-mini-chip"><span style="font-size:11px;">${dInfo.flag}</span><span class="thud-mini-val" style="color:${tw?'var(--amber)':'var(--travel)'};">${Utils.fmtHM(tSec)}</span></div>`;}
            b.innerHTML=`<div class="thud-mini-bar">${chips}</div><div class="thud-mini-tabs"><div class="thud-mini-tab" data-mini-tab="personal">👤 Ich</div><div class="thud-mini-tab" data-mini-tab="faction">⚔ Frak</div><div class="thud-mini-tab" data-mini-tab="bounty">🎯 BNT</div><div class="thud-mini-tab" data-mini-tab="enemy">💀 ENM</div><div class="thud-mini-tab" data-mini-tab="networth">💰 NW</div><div class="thud-mini-tab" data-mini-tab="yata_live">⏱ RST</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||{};
            // Torn API v2 liefert entweder cooldown_until (Timestamp) oder cooldown (Sekunden Restzeit)
            const _cd=(v)=>{
                if(!v&&v!==0)return 0;
                if(typeof v==='number'){
                    if(v>1700000000)return v;
                    if(v>0)return now+v;
                    return 0;
                }
                if(typeof v==='object'){
                    const until=Number(v.cooldown_until??v.until??0);
                    const secs=Number(v.cooldown??v.time_left??0);
                    if(until>1700000000)return until;
                    if(secs>0)return now+secs;
                    return 0;
                }
                return 0;
            };
            const drugSec=Math.max(0,_cd(cd.drug)-now),medSec=Math.max(0,_cd(cd.medical)-now),boostSec=Math.max(0,_cd(cd.booster)-now);
            const edu=s.education,eduLeft=edu?Math.max(0,Number(edu.until??0)-now):0;
            const nwObj=s.networthData||{};
            const wallet=Number(nwObj.wallet??nwObj.cash??0),stocks=Number(nwObj.stockmarket??nwObj.stocks??0),totalMoney=wallet+stocks;
            const level=basic.level||profile.level||'?',name=basic.name||profile.name||'—';
            const tip=this._smartTip(hp,energy,nerve,tSec,hospSec,jailSec);
            const smartTippHtml=(tip&&s.isVisible('show_tip'))?`<div class="smart-tipp"><span class="smart-tipp-icon">${tip.icon}</span><span class="smart-tipp-text">${tip.text}</span></div>`:'';
            const DEFAULT_QL=[{label:'Stock\nMarket',icon:'📈',url:'https://www.torn.com/page.php?sid=stocks'},{label:'Casino',icon:'🎰',url:'https://www.torn.com/page.php?sid=slots'},{label:'Bazaar',icon:'🏪',url:'https://www.torn.com/bazaar.php?userId=0'},{label:'Forum',icon:'💬',url:'https://www.torn.com/forums.php'}];
            const quickLinks=hud.store.get('customQuickLinks',null)||DEFAULT_QL;
            const qlHtml=s.isVisible('show_quick_links')?`<div><div class="section-header">🔗 QUICK LINKS</div><div class="ql-grid">${quickLinks.map(({label,icon,url})=>`<a class="ql-card" href="${url}" target="_blank"><span class="ql-icon">${icon}</span><span class="ql-name">${label.replace('\n','<br>')}</span></a>`).join('')}</div></div>`:'';
            const bs=s.battleStats;
            const bsT=bs?(Number(bs.strength||0)+Number(bs.defense||0)+Number(bs.speed||0)+Number(bs.dexterity||0)):0;
            const bsHtml=(s.isVisible('show_battle_stats')&&bs)?`<div class="card"><div class="card-header"><span class="card-title red">⚔ BATTLE STATS</span><span class="badge badge-amber">${Utils.fmtStat(bsT)} total</span></div><div class="card-body"><div class="stat-grid stat-grid-4"><div class="stat-card"><div class="sv sv-red">${Utils.fmtStat(bs.strength||0)}</div><div class="sl">STR</div></div><div class="stat-card"><div class="sv sv-blue">${Utils.fmtStat(bs.defense||0)}</div><div class="sl">DEF</div></div><div class="stat-card"><div class="sv sv-green">${Utils.fmtStat(bs.speed||0)}</div><div class="sl">SPD</div></div><div class="stat-card"><div class="sv sv-amber">${Utils.fmtStat(bs.dexterity||0)}</div><div class="sl">DEX</div></div></div></div></div>`:'';
            const tctHtml=s.isVisible('show_tct_time')?`<span style="color:var(--accent2);font-family:var(--mono);">TCT ${Utils.tctTime()}</span>`:'';
            body.innerHTML=`
            ${smartTippHtml}
            <div style="display:flex;align-items:center;justify-content:space-between;padding:2px 2px 0;">
                <div><div style="font-size:14px;font-weight:700;color:var(--text);">${Utils.escHtml(name)}</div><div style="font-size:10px;color:var(--muted);margin-top:2px;">Level ${level} · ${Utils.escHtml(statusState)}</div></div>
                <div style="text-align:right;"><div style="font-family:var(--mono);font-size:15px;font-weight:700;color:var(--amber);">${Utils.fmtMoney(totalMoney)}</div><div style="font-size:9px;color:var(--muted);margin-top:1px;">💵 ${Utils.fmtMoney(wallet)}${stocks?` · 📈 ${Utils.fmtMoney(stocks)}`:''}</div><div style="font-size:8px;color:var(--muted);margin-top:1px;opacity:.6;">liquide</div></div>
            </div>
            <div class="travel-card ${tvCls}">
                <div class="travel-card-top" style="color:${tvBadgeColor};"><span>${tvBadge}</span>${flying?`<span style="font-size:18px;">${destInfo.flag}</span>`:''}</div>
                <div class="travel-card-body">
                ${!flying?`<div style="display:flex;align-items:center;gap:12px;"><span style="font-size:24px;opacity:.2;">🏠</span><div><div style="font-size:14px;font-weight:700;color:var(--green);">TORN CITY</div><div style="font-size:9px;color:var(--muted);text-transform:uppercase;letter-spacing:1.5px;margin-top:2px;">BEREIT ZUM ABFLUG</div></div></div>`
                :`<div style="display:flex;align-items:center;gap:10px;margin-bottom:10px;"><span style="font-size:22px;">${destInfo.flag}</span><div class="travel-dest" style="color:${tClockColor};">${Utils.escHtml(tDest||'Unbekannt')}</div></div><div style="display:flex;justify-content:space-between;align-items:flex-end;"><div class="travel-clock" style="color:${tClockColor};">${Utils.fmtHM(tSec)}</div><div style="text-align:right;"><div style="font-size:9px;color:var(--muted);text-transform:uppercase;letter-spacing:1px;">ANKUNFT</div><div style="font-family:var(--mono);font-size:15px;font-weight:700;color:var(--dim);margin-top:1px;">${Utils.arrTime(tSec)}</div></div></div><div class="travel-prog"><div class="travel-prog-fill ${tProgCls}" style="width:0%;"></div></div>`}
                </div>
            </div>
            <div class="card">
                <div class="card-header"><span class="card-title accent">◈ BARS &amp; STATUS</span><span class="badge badge-${statusState==='Okay'?'green':statusState==='Hospital'?'red':statusState==='Jail'?'amber':'muted'}">${Utils.escHtml(statusState)}</span></div>
                <div class="card-body">
                    <div class="stat-grid stat-grid-3" style="margin-bottom:12px;">
                        <div class="stat-card"><div class="sv ${hp>=70?'sv-green':hp>=50?'sv-amber':'sv-red'}">${hpM>0?Math.round(hp)+'%':'—'}</div><div class="sl">HP</div></div>
                        <div class="stat-card"><div class="sv ${ePct>=100?'sv-red':ePct>=75?'sv-amber':'sv-blue'}">${energy?energy.current:'—'}</div><div class="sl">Energie</div></div>
                        <div class="stat-card"><div class="sv ${nPct>=100?'sv-red':nPct>=75?'sv-amber':'sv-blue'}">${nerve?nerve.current:'—'}</div><div class="sl">Nerve</div></div>
                    </div>
                    <div class="bar-block"><div class="bar-block-head"><span class="bar-block-lbl">❤ HP</span><span class="bar-block-val" style="color:${hpColor}">${hpC} / ${hpM||'?'}</span></div>${Utils.pctBar(hp,hpColor,'bar-lg')}</div>
                    <div class="bar-block"><div class="bar-block-head"><span class="bar-block-lbl">⚡ Energie</span><span class="bar-block-val" style="color:${eColor}">${energy?`${energy.current} / ${energy.maximum}`:'—'}</span></div>${Utils.pctBar(ePct,eColor)}${eMin!==null&&eMin>0?`<div class="bar-block-sub">Voll in ${eMin} Min</div>`:''}</div>
                    <div class="bar-block"><div class="bar-block-head"><span class="bar-block-lbl">🧠 Nerve</span><span class="bar-block-val" style="color:${nColor}">${nerve?`${nerve.current} / ${nerve.maximum}`:'—'}</span></div>${Utils.pctBar(nPct,nColor)}${nMin!==null&&nMin>0?`<div class="bar-block-sub">Voll in ${nMin} Min</div>`:''}</div>
                    ${happy&&s.isVisible('show_happy')?`<div class="bar-block"><div class="bar-block-head"><span class="bar-block-lbl">😊 Happy</span><span class="bar-block-val" style="color:var(--amber)">${happy.current} / ${happy.maximum}</span></div>${Utils.pctBar(haPct,'var(--amber)')}</div>`:''}
                </div>
            </div>
            ${bsHtml}
            ${qlHtml}
            <div class="card">
                <div class="card-header"><span class="card-title">⏱ COOLDOWNS &amp; STATUS</span></div>
                <div class="card-body">
                    ${s.isVisible('show_hospital')?`<div class="row"><span class="row-label">🏥 Krankenhaus</span><span class="row-value" style="color:${hospSec>0?'var(--red)':'var(--green)'}">${hospSec>0?Utils.fmtSec(hospSec):'FREI'}</span></div>`:''}
                    ${s.isVisible('show_jail')?`<div class="row"><span class="row-label">🚔 Gefängnis</span><span class="row-value" style="color:${jailSec>0?'var(--amber)':'var(--green)'}">${jailSec>0?Utils.fmtSec(jailSec):'FREI'}</span></div>`:''}
                    ${s.isVisible('show_drug_cd')?`<div class="row"><span class="row-label">💊 Drogen CD</span><span class="row-value" style="color:${drugSec>0?'var(--amber)':'var(--green)'}">${drugSec>0?Utils.fmtSec(drugSec):'BEREIT'}</span></div>`:''}
                    ${s.isVisible('show_medical_cd')?`<div class="row"><span class="row-label">💉 Medical CD</span><span class="row-value" style="color:${medSec>0?'var(--amber)':'var(--green)'}">${medSec>0?Utils.fmtSec(medSec):'BEREIT'}</span></div>`:''}
                    ${s.isVisible('show_booster_cd')?`<div class="row"><span class="row-label">🧪 Booster CD</span><span class="row-value" style="color:${boostSec>0?'var(--amber)':'var(--green)'}">${boostSec>0?Utils.fmtSec(boostSec):'BEREIT'}</span></div>`:''}
                    ${s.isVisible('show_education')&&edu?(eduLeft>0?`<div class="row"><span class="row-label">📚 Kurs</span><span class="row-value">${Utils.fmtDhm(eduLeft)}</span></div>`:`<div class="row"><span class="row-label">📚 Lehrgang</span><span class="row-value" style="color:var(--green);">ABGESCHLOSSEN ✓</span></div>`):''}
                </div>
            </div>
            <div style="display:flex;justify-content:space-between;align-items:center;padding:2px 0;flex-shrink:0;" class="ts">
                <span style="display:flex;align-items:center;gap:5px;"><div class="pip"></div>${s.lastUpdate?.toLocaleTimeString('de-DE')||'—'}</span>
                ${tctHtml}
                <span>⬆ ${this._uptimeStr()}</span>
            </div>`;
        }
 
        _smartTip(hp,energy,nerve,tSec,hospSec,jailSec) {
            if(jailSec>0)return{icon:'🚔',text:`Du sitzt noch ${Utils.fmtSec(jailSec)} im Knast.`};
            if(hospSec>0&&hospSec<300)return{icon:'🏥',text:`Nur noch ${Utils.fmtSec(hospSec)} im Krankenhaus.`};
            if(hp<50&&hp>0)return{icon:'❤',text:`HP sehr niedrig (${Math.round(hp)}%). Medical Items nutzen!`};
            if(energy?.current>=energy?.maximum)return{icon:'⚡',text:'Energie voll! Chains oder Gym nutzen!'};
            if(nerve?.current>=nerve?.maximum)return{icon:'🧠',text:'Nerve voll! Crimes abarbeiten!'};
            if(tSec>0&&tSec<300)return{icon:'✈',text:`Fast da! Ankunft in ${Utils.fmtHM(tSec)}.`};
            if(energy&&(energy.current/energy.maximum)>0.85)return{icon:'💡',text:'Energie fast voll – nächste Chain planen?'};
            return null;
        }
        _uptimeStr() { const s=this.state.uptimeSec,h=Math.floor(s/3600),m=Math.floor((s%3600)/60); return h>0?`${h}h ${m}m`:`${m}m`; }
 
        renderFaction() {
            const body=this.body; if(!body)return;
            const s=this.state;
            const factionSubTab=s._factionSubTab||'overview';
            const subTabHtml=`<div style="display:flex;gap:5px;padding:8px 14px 0;flex-shrink:0;">
                <button class="sort-btn ${factionSubTab==='overview'?'active':''}" data-faction-sub="overview">⚔ Übersicht</button>
                <button class="sort-btn ${factionSubTab==='details'?'active':''}" data-faction-sub="details">📋 Mitglieder</button>
            </div>`;
            if(factionSubTab==='details'){
                this._renderFactionDetails(body,s,subTabHtml);
            } else {
                this._renderFactionOverview(body,s,subTabHtml);
            }
            body.querySelectorAll('[data-faction-sub]').forEach(btn=>btn.addEventListener('click',()=>{s._factionSubTab=btn.dataset.factionSub;this.renderFaction();}));
        }
 
        _renderFactionOverview(body,s,subTabHtml) {
            const m=Object.values(s.members),t=m.length;
            const cap=s.faction.max_members||s.faction.capacity||t||50;
            const groups={online:[],idle:[],traveling:[],hospital:[],jail:[],offline:[]};
            m.forEach(x=>groups[FactionHelper.getStatus(x)].push(x));
            const active=[...groups.online,...groups.traveling,...groups.idle,...groups.hospital,...groups.jail];
            const sa=active.sort((a,b)=>FactionHelper.statusOrder(FactionHelper.getStatus(a))-FactionHelper.statusOrder(FactionHelper.getStatus(b)));
            const so=[...groups.offline].sort((a,b)=>(a.last_action?.timestamp||0)-(b.last_action?.timestamp||0));
            const sl=20,ol=INACTIVE_LIMIT;
            const sOn=s.showAllOnline?sa:sa.slice(0,sl);
            const sOf=s.showAllOffline?so:so.slice(0,ol);
            const eOn=sa.length-sl,eOf=so.length-ol;
            const rd=groups.online.length+groups.idle.length,oc=active.length;
            const donutSVG=Utils.buildDonut([{count:groups.online.length,color:'var(--green)'},{count:groups.idle.length,color:'var(--amber)'},{count:groups.traveling.length,color:'var(--travel)'},{count:groups.hospital.length,color:'var(--red)'},{count:groups.jail.length,color:'#f97316'},{count:groups.offline.length,color:'var(--bg-el)'}],t);
            body.innerHTML=`
            ${subTabHtml}
            <div class="chain-block" id="thud-chain-block"><div class="chain-idle"><div class="pulse-dot" style="opacity:.2;animation:none;background:var(--muted);"></div><span style="font-size:10px;font-weight:600;margin-left:7px;letter-spacing:.5px;">Keine aktive Chain</span></div></div>
            <div class="card"><div class="card-header"><div style="display:flex;align-items:center;gap:8px;"><span class="card-title green">● ONLINE</span><span class="badge badge-green">${oc}</span></div><div style="position:relative;display:flex;align-items:center;justify-content:center;">${donutSVG}<div style="position:absolute;text-align:center;pointer-events:none;"><div style="font-size:13px;font-weight:700;color:var(--text);">${oc}</div><div style="font-size:8px;color:var(--muted);">/${cap}</div></div></div></div>
            <div class="card-body"><div class="member-grid">${sOn.map(x=>{const st=FactionHelper.getStatus(x);return`<div class="member-row"><span class="status-dot sd-${st}"></span><span class="member-name">${Utils.escHtml(x.name)}</span><span class="member-status">${FactionHelper.statusLabel(st)}</span></div>`;}).join('')}</div>${eOn>0&&!s.showAllOnline?`<span class="toggle-more" id="thud-more-online">▾ ${eOn} mehr</span>`:s.showAllOnline&&sa.length>sl?`<span class="toggle-more" id="thud-less-online">▴ Weniger</span>`:''}<div class="status-summary"><div class="stat-chip"><div class="sn" style="color:var(--green);">${groups.online.length}</div><div class="sl">Online</div></div><div class="stat-chip"><div class="sn" style="color:var(--amber);">${groups.idle.length}</div><div class="sl">AFK</div></div><div class="stat-chip"><div class="sn" style="color:var(--travel);">${groups.traveling.length}</div><div class="sl">Travel</div></div><div class="stat-chip"><div class="sn" style="color:var(--red);">${groups.hospital.length}</div><div class="sl">Hosp.</div></div><div class="stat-chip"><div class="sn" style="color:#f97316;">${groups.jail.length}</div><div class="sl">Jail</div></div><div class="stat-chip"><div class="sn" style="color:var(--muted);">${groups.offline.length}</div><div class="sl">Offline</div></div></div></div></div>
            <div class="card"><div class="card-header"><span class="card-title red">⚔ WAR READY</span></div><div class="card-body"><div class="war-grid"><div class="war-cell"><div class="wv" style="color:var(--green);">${rd}</div><div class="wl">Ready</div></div><div class="war-cell"><div class="wv" style="color:var(--red);">${groups.hospital.length}</div><div class="wl">Hosp.</div></div><div class="war-cell"><div class="wv" style="color:var(--travel);">${groups.traveling.length}</div><div class="wl">Travel</div></div><div class="war-cell"><div class="wv" style="color:var(--amber);">${t>0?Math.round(rd/t*100):0}%</div><div class="wl">Bereit</div></div></div></div></div>
            ${groups.traveling.length>0?`<div class="card"><div class="card-header"><span class="card-title blue">✈ UNTERWEGS (${groups.traveling.length})</span></div><div class="card-body p0">${groups.traveling.map(x=>{const{from,to,dir}=FactionHelper.parseTravelRoute(x);const fC=from.toLowerCase().includes('torn')?'var(--green)':'var(--travel)';const tC=to.toLowerCase().includes('torn')?'var(--green)':'var(--travel)';return`<div style="display:flex;align-items:center;gap:10px;padding:9px 14px;border-bottom:1px solid rgba(255,255,255,.04);"><span style="font-size:18px;">${dir==='home'?'🏠':'✈'}</span><div style="flex:1;min-width:0;"><div style="font-size:12px;font-weight:600;color:var(--text);">${Utils.escHtml(x.name)}</div><div style="font-size:10px;margin-top:2px;display:flex;align-items:center;gap:5px;"><span style="color:${fC};">${Utils.escHtml(from)}</span><span style="color:var(--muted);">→</span><span style="color:${tC};">${Utils.escHtml(to)}</span></div></div></div>`;}).join('')}</div></div>`:''}
            <div class="card"><div class="card-header"><span class="card-title">💤 OFFLINE (${groups.offline.length})</span></div><div class="card-body p0">${sOf.map(x=>{const a=Utils.inactiveAgo(x.last_action?.timestamp||0);return`<div class="offline-row"><span class="status-dot sd-offline"></span><span class="offline-name">${Utils.escHtml(x.name)}</span><span class="offline-ago ${a.cls}">${a.text}</span></div>`;}).join('')}${eOf>0&&!s.showAllOffline?`<span class="toggle-more" style="margin:7px 14px 12px;" id="thud-more-offline">▾ ${eOf} weitere</span>`:s.showAllOffline&&so.length>ol?`<span class="toggle-more" style="margin:7px 14px 12px;" id="thud-less-offline">▴ Weniger</span>`:''}</div></div>`;
            hud.chainManager.renderSection();
            if(s.chain?.current>0)hud.chainManager.startTick();
            document.getElementById('thud-more-online')?.addEventListener('click',()=>{s.showAllOnline=true;this.renderFaction();});
            document.getElementById('thud-less-online')?.addEventListener('click',()=>{s.showAllOnline=false;this.renderFaction();});
            document.getElementById('thud-more-offline')?.addEventListener('click',()=>{s.showAllOffline=true;this.renderFaction();});
            document.getElementById('thud-less-offline')?.addEventListener('click',()=>{s.showAllOffline=false;this.renderFaction();});
        }
 
        _renderFactionDetails(body,s,subTabHtml) {
            const now=Utils.now();
            const m=Object.values(s.members);
            const detSort=s._factionDetailSort||'name';
            const detSortDir=s._factionDetailSortDir||'asc';
            const sortOpts=[{k:'name',lbl:'Name'},{k:'rank',lbl:'Rang'},{k:'joined',lbl:'Beigetreten'},{k:'days',lbl:'Tage'},{k:'status',lbl:'Status'}];
            const sortBtnsHtml=sortOpts.map(o=>`<button class="sort-btn ${detSort===o.k?'active':''}" data-det-sort="${o.k}" style="font-size:10px;">${o.lbl}</button>`).join('');
            const dirBtnHtml=`<button class="sort-btn" id="thud-det-dir" style="padding:4px 10px;font-size:10px;">${detSortDir==='asc'?'▲':'▼'}</button>`;
            let sorted=m.slice();
            sorted.sort((a,b)=>{
                let va,vb;
                if(detSort==='name'){va=(a.name||'').toLowerCase();vb=(b.name||'').toLowerCase();return detSortDir==='asc'?va.localeCompare(vb):vb.localeCompare(va);}
                if(detSort==='rank'){va=(a.position||a.rank||'').toLowerCase();vb=(b.position||b.rank||'').toLowerCase();return detSortDir==='asc'?va.localeCompare(vb):vb.localeCompare(va);}
                if(detSort==='joined'){va=Number(a.days_in_faction??a.faction_joined??0);vb=Number(b.days_in_faction??b.faction_joined??0);}
                if(detSort==='days'){va=Number(a.days_in_faction??0);vb=Number(b.days_in_faction??0);}
                if(detSort==='status'){va=FactionHelper.statusOrder(FactionHelper.getStatus(a));vb=FactionHelper.statusOrder(FactionHelper.getStatus(b));}
                return detSortDir==='asc'?va-vb:vb-va;
            });
            const rows=sorted.map(x=>{
                const rank=x.position||x.rank||'Member';
                const isRecruit=rank.toLowerCase()==='recruit';
                const daysIn=Number(x.days_in_faction??0);
                const cachedJoin=s.recruitJoinCache[x.id]?.ts??0;
                const joinedTs=Number(cachedJoin||x.faction_joined||x.joined||0);
                let joinedStr='—';
                if(joinedTs>0){
                    joinedStr=new Date(joinedTs*1000).toLocaleDateString('de-DE',{day:'2-digit',month:'2-digit',year:'numeric'});
                } else if(daysIn>0){
                    const approxTs=(now-daysIn*86400)*1000;
                    joinedStr='~'+new Date(approxTs).toLocaleDateString('de-DE',{day:'2-digit',month:'2-digit',year:'numeric'});
                }
                let recruitTimerHtml='';
                if(isRecruit){
                    const RECRUIT_SEC=72*3600;
                    const cacheEntry=s.recruitJoinCache[x.id];
                    const cachedTs=Number(cacheEntry?.ts??0);
                    const isExact=cacheEntry?.exact===true;
                    let secsIn;
                    if(cachedTs>0){secsIn=now-cachedTs;}
                    else{secsIn=daysIn*86400;}
                    const remaining=Math.max(0,RECRUIT_SEC-secsIn);
                    if(remaining>0){
                        const hrs=Math.floor(remaining/3600);
                        const prefix=isExact?'':'~';
                        recruitTimerHtml=`<span style="font-size:9px;font-family:var(--mono);color:var(--amber);background:rgba(245,158,11,.1);border:1px solid rgba(245,158,11,.2);border-radius:4px;padding:1px 6px;">⏱ ${prefix}${hrs}h</span>`;
                    } else {
                        recruitTimerHtml=`<span style="font-size:9px;color:var(--green);background:rgba(16,185,129,.1);border:1px solid rgba(16,185,129,.2);border-radius:4px;padding:1px 6px;">✓ Beförderbar</span>`;
                    }
                }
                const st=FactionHelper.getStatus(x);
                const rankColor=isRecruit?'var(--amber)':rank.toLowerCase().includes('leader')||rank.toLowerCase().includes('co-leader')?'var(--red)':'var(--accent2)';
                return`<div style="display:flex;align-items:flex-start;gap:8px;padding:8px 14px;border-bottom:1px solid rgba(255,255,255,.04);">
                    <span class="status-dot sd-${st}" style="flex-shrink:0;margin-top:4px;"></span>
                    <div style="flex:1;min-width:0;">
                        <div style="display:flex;align-items:center;gap:6px;flex-wrap:wrap;">
                            <a href="https://www.torn.com/profiles.php?XID=${x.id}" target="_blank" style="color:var(--text);text-decoration:none;font-weight:700;font-size:12px;">${Utils.escHtml(x.name)}</a>
                            <span style="font-size:10px;font-weight:600;color:${rankColor};">⚔ ${Utils.escHtml(rank)}</span>
                            ${recruitTimerHtml}
                        </div>
                        <div style="display:flex;gap:8px;margin-top:3px;flex-wrap:wrap;">
                            <span style="font-size:10px;color:var(--muted);">📅 Beigetreten: <span style="color:var(--dim);">${joinedStr}</span></span>
                            ${daysIn>0?`<span style="font-size:10px;color:var(--muted);">· <span style="color:var(--dim);">${daysIn} Tage</span></span>`:''}
                        </div>
                    </div>
                    <span style="font-size:10px;font-family:var(--mono);color:var(--muted);flex-shrink:0;">${FactionHelper.statusLabel(st)}</span>
                </div>`;
            }).join('');
 
            body.innerHTML=`
            ${subTabHtml}
            <div class="card" style="flex-shrink:0;">
                <div class="card-header">
                    <span class="card-title accent">📋 MITGLIEDER DETAILS</span>
                    <span class="badge badge-accent">${m.length} Mitglieder</span>
                </div>
                <div class="card-body" style="padding:8px 14px;gap:5px;">
                    <div style="display:flex;gap:4px;align-items:center;flex-wrap:wrap;">
                        <span style="font-size:9px;color:var(--muted);font-weight:700;">SORT:</span>
                        ${sortBtnsHtml}${dirBtnHtml}
                    </div>
                </div>
            </div>
            ${m.length===0?`<div class="empty-state"><div class="es-icon">⚔</div><div class="es-text">Keine Mitglieder</div></div>`:
            `<div class="card" style="flex-shrink:0;"><div class="card-body p0">${rows}</div></div>`}`;
 
            body.querySelectorAll('[data-det-sort]').forEach(btn=>btn.addEventListener('click',()=>{s._factionDetailSort=btn.dataset.detSort;this.renderFaction();}));
            document.getElementById('thud-det-dir')?.addEventListener('click',()=>{s._factionDetailSortDir=s._factionDetailSortDir==='asc'?'desc':'asc';this.renderFaction();});
        }
 
        renderBounty() {
            const body=this.body; if(!body)return;
            const s=this.state;
            const now=Utils.now();
            const data=s.bountyData||s.bountyAllData||null;
            const sort=s.bountySort||'reward';
 
            const doScan=()=>{hud.fetcher.fetchBountiesInitial(()=>{s.bountySearch='';s.bountyDisplayCount=80;this.renderBounty();});};
 
            if(!data){
                body.innerHTML=`<div class="card" style="flex-shrink:0;"><div class="card-header"><span class="card-title red">🎯 BOUNTY RADAR</span></div></div><div class="empty-state"><div class="es-icon">🎯</div><div class="es-text">Keine Bounties geladen</div><div class="es-sub">Klick "Laden" um die Torn Bounty-Liste zu holen</div></div><button class="scan-btn" id="thud-bounty-scan" style="align-self:center;">🔍 Bounty-Liste laden</button>`;
                document.getElementById('thud-bounty-scan')?.addEventListener('click',doScan); return;
            }
 
            const lvMin=Number(s.bountyLevelMin)||1,lvMax=Number(s.bountyLevelMax)||100;
            const rwMin=Number(s.bountyRewardMin)||0,rwMax=Number(s.bountyRewardMax)||0;
            const ffMin=Number(s.bountyFFMin)||0,ffMax=Number(s.bountyFFMax)||0;
            const q=(s.bountySearch||'').toLowerCase().trim();
            let filtered=data.slice();
            if(q)filtered=filtered.filter(b=>{const name=(b.target_name??b.name??'').toLowerCase(),reason=(b.reason??'').toLowerCase();return name.includes(q)||reason.includes(q);});
            filtered=filtered.filter(b=>{const lvl=Number(b.target_level??b.level??0);return lvl>=lvMin&&lvl<=lvMax;});
            if(rwMin>0||rwMax>0){filtered=filtered.filter(b=>{const r=Number(b.reward??b.amount??0);if(rwMin>0&&r<rwMin)return false;if(rwMax>0&&r>rwMax)return false;return true;});}
            if(ffMin>0||ffMax>0){
                filtered=filtered.filter(b=>{
                    const tid=Number(b.target_id??b.id);
                    const ffEntry=s.ffData[tid];
                    const ff=ffEntry?.ff??null;
                    if(ff===null)return true;
                    if(ffMin>0&&ff<ffMin)return false;
                    if(ffMax>0&&ff>ffMax)return false;
                    return true;
                });
            }
            if(s.bountyFilterHosp)filtered=filtered.filter(b=>!this._isHospital(b));
            if(sort==='reward')filtered.sort((a,b)=>(b.reward??b.amount??0)-(a.reward??a.amount??0));
            if(sort==='level') filtered.sort((a,b)=>(a.target_level??a.level??0)-(b.target_level??b.level??0));
            if(sort==='time')  filtered.sort((a,b)=>(a.valid_until??a.expires??0)-(b.valid_until??b.expires??0));
            if(sort==='ff')    filtered.sort((a,b)=>{const fa=s.ffData[Number(a.target_id??a.id)]?.ff??-1;const fb=s.ffData[Number(b.target_id??b.id)]?.ff??-1;return fb-fa;});
            if(!s.bountyFilterHosp)filtered.sort((a,b)=>(this._isHospital(a)?0:1)-(this._isHospital(b)?0:1));
            const displayCount=s.bountyDisplayCount||80,visible=filtered.slice(0,displayCount),hasMoreVisible=filtered.length>displayCount;
            const hospCount=data.filter(b=>this._isHospital(b)).length,isFiltered=filtered.length!==data.length;
            const searchHadFocus=document.activeElement?.id==='thud-bounty-search';
            const searchCursorPos=searchHadFocus?(document.activeElement.selectionStart??0):0;
            const rwMinD=rwMin>0?(rwMin>=1e6?(rwMin/1e6).toFixed(1)+'M':rwMin>=1e3?(rwMin/1e3).toFixed(0)+'K':rwMin):'';
            const rwMaxD=rwMax>0?(rwMax>=1e6?(rwMax/1e6).toFixed(1)+'M':rwMax>=1e3?(rwMax/1e3).toFixed(0)+'K':rwMax):'';
 
            body.innerHTML=`
            <div class="card" style="flex-shrink:0;">
                <div class="card-header" style="flex-wrap:wrap;gap:6px;">
                    <span class="card-title red">🎯 BOUNTY RADAR</span>
                    <div style="display:flex;align-items:center;gap:5px;flex-wrap:wrap;">
                        <span class="badge badge-red">${data.length} total</span>
                        ${hospCount>0?`<span class="badge badge-red">🏥 ${hospCount}</span>`:''}
                        ${isFiltered?`<span class="badge badge-amber">${filtered.length} gefiltert</span>`:''}
                        ${!s.bountyLoading?`<button class="bounty-load-more-inline" id="thud-bounty-more">▾ +Mehr (S.${s.bountyLoadedPages})</button>`:`<span class="bounty-load-more-inline" style="opacity:.5;cursor:default;"><span class="spinning">↺</span>${s.bountyScanningAll?' Scan...':''}</span>`}
                        <button class="scan-btn" id="thud-bounty-scan-all" style="padding:4px 10px;font-size:10px;background:rgba(245,158,11,.15);border-color:rgba(245,158,11,.3);color:var(--amber);" ${s.bountyLoading?'disabled':''} title="Alle Seiten laden">⚡ Alle</button>
                        <button class="scan-btn" id="thud-bounty-scan" style="padding:4px 10px;font-size:10px;" ${s.bountyLoading?'disabled':''}>${s.bountyLoading?'<span class="spinning">↺</span>':'↺'}</button>
                    </div>
                </div>
                <div class="card-body" style="padding:9px 14px 10px;gap:7px;">
                    <input type="text" class="bounty-search" id="thud-bounty-search" placeholder="🔍 Name oder Grund..." value="${Utils.escHtml(s.bountySearch||'')}">
                    <div class="level-filter-row">
                        <span style="font-size:10px;color:var(--muted);font-weight:700;">LV:</span>
                        <input type="number" class="level-input" id="thud-lvl-min" min="1" max="100" value="${lvMin}">
                        <span style="color:var(--muted);font-size:10px;">–</span>
                        <input type="number" class="level-input" id="thud-lvl-max" min="1" max="100" value="${lvMax}">
                        <button class="sort-btn" id="thud-lvl-apply">✓</button>
                    </div>
                    <div class="level-filter-row">
                        <span style="font-size:10px;color:var(--amber);font-weight:700;">💰:</span>
                        <input type="text" class="reward-input" id="thud-rw-min" placeholder="Min" value="${rwMinD}">
                        <span style="color:var(--muted);font-size:10px;">–</span>
                        <input type="text" class="reward-input" id="thud-rw-max" placeholder="Max" value="${rwMaxD}">
                        <button class="sort-btn" id="thud-rw-apply" style="border-color:rgba(245,158,11,.3);color:var(--amber);">✓</button>
                        ${(rwMin>0||rwMax>0)?`<button class="sort-btn" id="thud-rw-clear" style="color:var(--muted);">✕</button>`:''}
                    </div>
                    <div class="level-filter-row">
                        <span style="font-size:10px;color:var(--accent2);font-weight:700;">FF:</span>
                        <input type="text" class="ff-input" id="thud-ff-min" placeholder="Min" value="${ffMin>0?ffMin:''}">
                        <span style="color:var(--muted);font-size:10px;">–</span>
                        <input type="text" class="ff-input" id="thud-ff-max" placeholder="Max" value="${ffMax>0?ffMax:''}">
                        <button class="sort-btn" id="thud-ff-apply" style="border-color:rgba(99,102,241,.3);color:var(--accent2);">✓</button>
                        ${(ffMin>0||ffMax>0)?`<button class="sort-btn" id="thud-ff-clear" style="color:var(--muted);">✕</button>`:''}
                    </div>
                    <div class="sort-bar">
                        <span style="font-size:9px;color:var(--muted);font-weight:700;">SORT:</span>
                        <button class="sort-btn ${sort==='reward'?'active':''}" data-sort="reward">💰 Reward</button>
                        <button class="sort-btn ${sort==='level'?'active':''}"  data-sort="level">⚔ Level</button>
                        <button class="sort-btn ${sort==='time'?'active':''}"   data-sort="time">⏱ Zeit</button>
                        <button class="sort-btn ${sort==='ff'?'active':''}"     data-sort="ff">📊 FF</button>
                    </div>
                    <div class="sort-bar">
                        <span style="font-size:9px;color:var(--muted);font-weight:700;">FILTER:</span>
                        <button class="sort-btn ${s.bountyFilterHosp?'active':''}" data-filter="hosp">🏥 ${s.bountyFilterHosp?'Hosp. ausgebl.':'Hosp. anzeigen'}</button>
                    </div>
                </div>
            </div>
            <div class="card" style="flex-shrink:0;"><div class="card-body p0" id="thud-bounty-list">${this._buildBountyRows(visible,now)}</div></div>
            <div class="ts" style="text-align:center;flex-shrink:0;">${data.length} Bounties · Seite ${s.bountyLoadedPages} · ${s.bountyLastFetch?new Date(s.bountyLastFetch*1000).toLocaleTimeString('de-DE'):'—'}</div>`;
 
            if(searchHadFocus){const inp=document.getElementById('thud-bounty-search');if(inp){inp.focus();try{inp.setSelectionRange(searchCursorPos,searchCursorPos);}catch(e){}}}
            document.getElementById('thud-bounty-scan')?.addEventListener('click',doScan);
            document.getElementById('thud-bounty-scan-all')?.addEventListener('click',()=>{if(s.bountyScanningAll||s.bountyLoading)return;hud.fetcher.fetchBountiesAllPages(()=>this.renderBounty(),(all)=>{s.bountySearch='';s.bountyDisplayCount=80;hud.toasts.show('✅','Alle Bounties geladen',`${all.length} Bounties`,'info',5000);this.renderBounty();});this.renderBounty();});
            document.getElementById('thud-bounty-search')?.addEventListener('input',e=>{s.bountySearch=e.target.value;s.bountyDisplayCount=80;this._renderBountyListOnly(s,now);});
            document.getElementById('thud-lvl-apply')?.addEventListener('click',()=>{s.setBountyLevelMin(Number(document.getElementById('thud-lvl-min')?.value)||1);s.setBountyLevelMax(Number(document.getElementById('thud-lvl-max')?.value)||100);s.bountyDisplayCount=80;this.renderBounty();});
            const parseR=(str)=>{if(!str||!str.trim())return 0;str=str.trim().toUpperCase().replace(/\s/g,'').replace(',','.');const m=str.match(/^([\d.]+)([KMB]?)$/);if(!m)return 0;return Math.round(parseFloat(m[1])*({K:1e3,M:1e6,B:1e9,'':1}[m[2]]||1));};
            document.getElementById('thud-rw-apply')?.addEventListener('click',()=>{s.setBountyRewardMin(parseR(document.getElementById('thud-rw-min')?.value));s.setBountyRewardMax(parseR(document.getElementById('thud-rw-max')?.value));s.bountyDisplayCount=80;this.renderBounty();});
            document.getElementById('thud-rw-clear')?.addEventListener('click',()=>{s.setBountyRewardMin(0);s.setBountyRewardMax(0);s.bountyDisplayCount=80;this.renderBounty();});
            document.getElementById('thud-ff-apply')?.addEventListener('click',()=>{s.setBountyFFMin(parseFloat(document.getElementById('thud-ff-min')?.value)||0);s.setBountyFFMax(parseFloat(document.getElementById('thud-ff-max')?.value)||0);s.bountyDisplayCount=80;this.renderBounty();});
            document.getElementById('thud-ff-clear')?.addEventListener('click',()=>{s.setBountyFFMin(0);s.setBountyFFMax(0);s.bountyDisplayCount=80;this.renderBounty();});
            body.querySelectorAll('[data-sort]').forEach(btn=>btn.addEventListener('click',()=>{s.setBountySort(btn.dataset.sort);s.bountyDisplayCount=80;this.renderBounty();}));
            body.querySelectorAll('[data-filter]').forEach(btn=>btn.addEventListener('click',()=>{if(btn.dataset.filter==='hosp')s.setBountyFilterHosp(!s.bountyFilterHosp);s.bountyDisplayCount=80;this.renderBounty();}));
            document.getElementById('thud-bounty-more')?.addEventListener('click',()=>{if(hasMoreVisible){s.bountyDisplayCount+=80;this._renderBountyListOnly(s,now);}else{hud.fetcher.fetchBountiesNextPage(has=>{if(!has)hud.toasts.show('ℹ','Alle geladen',`${s.bountyData.length} Bounties`,'info',4000);else s.bountyDisplayCount+=80;this.renderBounty();});}});
            body.querySelectorAll('[data-bounty-add-enemy]').forEach(btn=>btn.addEventListener('click',()=>{const id=btn.dataset.bountyAddEnemy,name=btn.dataset.bountyName||`#${id}`;if(!s.enemies.find(e=>String(e.id)===String(id))){s.addEnemy(String(id),name,'','default');hud.toasts.show('💀','Enemy hinzugefügt',`${name} aus Bounty-Liste`,'crit',3000);hud.fetcher.fetchEnemyData();}else{hud.toasts.show('⚠','Bereits in Enemy-Liste',name,'warn',2000);}}));
            const ffKey=hud.store.get('ffKey','');
            if(ffKey&&visible.length){
                const ids=visible.map(b=>Number(b.target_id??b.id)).filter(Boolean);
                getFFData(ids,ffKey,(ffResults)=>{
                    s.ffData={...s.ffData,...ffResults};
                    ids.forEach(id=>{const el=document.querySelector(`[data-ff-badge="${id}"]`);if(el)el.innerHTML=ffBadge(ffResults[id]);});
                });
            }
        }
 
        _isHospital(b) {
            const so=(typeof b.target?.status==='object'&&b.target.status)||(typeof b.status==='object'&&b.status)||{};
            const raw=so.state??so.description??b.target_state??b.state??'';
            return raw.toLowerCase().includes('hospital');
        }
 
        _buildBountyRows(visible,now) {
            if(!visible||!visible.length)return`<div class="empty-state"><div class="es-icon">🔍</div><div class="es-text">Keine Ergebnisse</div><div class="es-sub">Filter anpassen oder Bounties neu laden</div></div>`;
            const s=this.state;
            return visible.map(b=>{
                const tid=b.target_id??b.id,tname=b.target_name??b.name??`#${tid}`;
                const lvl=b.target_level??b.level??0,reward=b.reward??b.amount??0;
                const validUntil=b.valid_until??b.expires??0,timeLeft=validUntil>0?Math.max(0,validUntil-now):0;
                const reason=b.reason?Utils.escHtml(b.reason.substring(0,80)):'';
                const so=(typeof b.target?.status==='object'&&b.target.status)||(typeof b.status==='object'&&b.status)||{};
                const rawState=so.state??so.description??b.target_state??b.state??'';
                const ss=rawState.toLowerCase(),isH=ss.includes('hospital');
                const hospUntil=Number(so.until??so.hospital_timestamp??0);
                const hospLeft=isH&&hospUntil>now?hospUntil-now:0;
                let statusTag='';
                if(isH)statusTag=`<span class="bounty-tag tag-hosp">🏥 HOSPITAL${hospLeft>0?` · ${Utils.fmtSec(hospLeft)}`:''}</span>`;
                else if(ss.includes('travel')||ss.includes('abroad'))statusTag=`<span class="bounty-tag tag-travel">✈ TRAVEL</span>`;
                else if(ss.includes('jail'))statusTag=`<span class="bounty-tag tag-jail">🚔 JAIL</span>`;
                else if(ss&&ss!=='okay')statusTag=`<span class="bounty-tag tag-ok">✓ ${Utils.escHtml(rawState)}</span>`;
                let diff,dCls;
                if(lvl<=15){diff='EASY';dCls='diff-easy';}else if(lvl<=50){diff='MID';dCls='diff-medium';}else{diff='HARD';dCls='diff-hard';}
                const ffEntry=s.ffData[Number(tid)];
                const ffHtml=`<span data-ff-badge="${tid}">${ffBadge(ffEntry)}</span>`;
                return`<div class="bounty-row${isH?' is-hospital':''}">
                    <div style="flex:1;min-width:0;">
                        <div style="display:flex;align-items:center;gap:5px;flex-wrap:wrap;">
                            <a href="https://www.torn.com/profiles.php?XID=${tid}" target="_blank" style="color:var(--text);text-decoration:none;font-weight:700;font-size:13px;">${Utils.escHtml(tname)}</a>
                            <span class="${dCls} bounty-difficulty">${diff}</span>
                            <span style="font-size:10px;font-family:var(--mono);color:var(--muted);">Lv ${lvl}</span>
                            ${ffHtml}
                        </div>
                        <div class="bounty-info-row">${statusTag}</div>
                        ${reason?`<div style="font-size:10px;color:var(--muted);margin-top:2px;font-style:italic;">"${reason}"</div>`:''}
                        ${timeLeft>0?`<div style="font-size:9px;color:var(--amber);font-family:var(--mono);margin-top:1px;">⏱ ${Utils.fmtSec(timeLeft)}</div>`:''}
                    </div>
                    <div style="display:flex;flex-direction:column;align-items:flex-end;gap:4px;flex-shrink:0;">
                        <span class="bounty-reward">${Utils.fmtMoney(reward)}</span>
                        <a class="bounty-link" href="https://www.torn.com/page.php?sid=attack&user2ID=${tid}" target="_blank">⚔ Angriff</a>
                        <button class="bounty-link" style="background:rgba(239,68,68,.12);border:1px solid rgba(239,68,68,.25);color:var(--red);cursor:pointer;font-family:var(--font);" data-bounty-add-enemy="${tid}" data-bounty-name="${Utils.escHtml(tname)}">💀 Enemy</button>
                    </div>
                </div>`;
            }).join('');
        }
 
        _renderBountyListOnly(s,now) {
            const listEl=document.getElementById('thud-bounty-list'); if(!listEl){this.renderBounty();return;}
            const q=(s.bountySearch||'').toLowerCase().trim();
            const lvMin=Number(s.bountyLevelMin)||1,lvMax=Number(s.bountyLevelMax)||100;
            const rwMin=Number(s.bountyRewardMin)||0,rwMax=Number(s.bountyRewardMax)||0;
            const ffMin=Number(s.bountyFFMin)||0,ffMax=Number(s.bountyFFMax)||0;
            let filtered=(s.bountyData||[]).slice();
            if(q)filtered=filtered.filter(b=>{const n=(b.target_name??b.name??'').toLowerCase(),r=(b.reason??'').toLowerCase();return n.includes(q)||r.includes(q);});
            filtered=filtered.filter(b=>{const l=Number(b.target_level??b.level??0);return l>=lvMin&&l<=lvMax;});
            if(rwMin>0||rwMax>0)filtered=filtered.filter(b=>{const r=Number(b.reward??b.amount??0);if(rwMin>0&&r<rwMin)return false;if(rwMax>0&&r>rwMax)return false;return true;});
            if(ffMin>0||ffMax>0){filtered=filtered.filter(b=>{const tid=Number(b.target_id??b.id);const ff=s.ffData[tid]?.ff??null;if(ff===null)return true;if(ffMin>0&&ff<ffMin)return false;if(ffMax>0&&ff>ffMax)return false;return true;});}
            if(s.bountyFilterHosp)filtered=filtered.filter(b=>!this._isHospital(b));
            const sort=s.bountySort;
            if(sort==='reward')filtered.sort((a,b)=>(b.reward??b.amount??0)-(a.reward??a.amount??0));
            if(sort==='level') filtered.sort((a,b)=>(a.target_level??a.level??0)-(b.target_level??b.level??0));
            if(sort==='time')  filtered.sort((a,b)=>(a.valid_until??a.expires??0)-(b.valid_until??b.expires??0));
            if(sort==='ff')    filtered.sort((a,b)=>{const fa=s.ffData[Number(a.target_id??a.id)]?.ff??-1;const fb=s.ffData[Number(b.target_id??b.id)]?.ff??-1;return fb-fa;});
            if(!s.bountyFilterHosp)filtered.sort((a,b)=>(this._isHospital(a)?0:1)-(this._isHospital(b)?0:1));
            listEl.innerHTML=this._buildBountyRows(filtered.slice(0,s.bountyDisplayCount||80),now);
        }
 
        renderEnemy() {
            const body=this.body; if(!body)return;
            const s=this.state,now=Utils.now(),enemies=s.enemies||[];
 
            const addEnemy=(id,note,groupId)=>{
                if(!id||isNaN(parseInt(id))){hud.toasts.show('⚠','Ungültige ID','Bitte gültige Spieler-ID eingeben','warn',3000);return;}
                hud.fetcher.api.playerProfile(id,(data)=>{
                    const p=data?.profile??data;
                    const name=p?.name??p?.player_name??`#${id}`;
                    if(s.addEnemy(String(id),name,note,groupId||'default')){hud.toasts.show('💀',`Enemy hinzugefügt`,`${name} (ID: ${id})`,'crit',4000);hud.fetcher.fetchEnemyData();this.renderEnemy();}
                    else hud.toasts.show('⚠','Bereits vorhanden',`ID ${id} ist bereits in der Liste`,'warn',3000);
                });
            };
 
            const renderRow=(enemy)=>{
                const ed=s.enemyData[enemy.id]||{};
                const statusObj=ed.status||{},ss=(statusObj.state||'').toLowerCase();
                const isH=ss==='hospital',isTr=ss==='traveling'||ss==='abroad',isJ=ss==='jail';
                const hospUntil=isH?Number(statusObj.until??0):0,hospSec=hospUntil>now?hospUntil-now:0;
                const level=ed.level??'?',ageDays=Number(ed.age??0);
                const facId=ed._factionId??null,facName=ed._factionName??null;
                const jobCo=ed.job?.company_name??ed.job?.name??null,jobPos=ed.job?.position??null;
                const travelDesc=statusObj.description||'';
                const lastTs=ed.last_action?.timestamp??0;
                const isOnline=(ed.last_action?.status||'').toLowerCase()==='online'||(lastTs>0&&(now-lastTs)<120);
                const lastAgo=lastTs>0?Utils.inactiveAgo(lastTs):null;
                const bountyR=s._getEnemyBountyReward(enemy.id);
                const ffEntry=s.ffData[Number(enemy.id)];
                const ffHtml=`<span data-ff-badge="${enemy.id}">${ffBadge(ffEntry)}</span>`;
                let statusTag='',rowClass='enemy-row';
                if(isH){statusTag=`<span class="enemy-tag etag-hosp">🏥 HOSPITAL${hospSec>0?` · ${Utils.fmtSec(hospSec)}`:''}</span>`;rowClass+=' in-hospital';}
                else if(isTr){statusTag=`<span class="enemy-tag etag-travel">✈ ${Utils.escHtml(travelDesc)||'Reist'}</span>`;rowClass+=' traveling';}
                else if(isJ)statusTag=`<span class="enemy-tag etag-jail">🚔 JAIL</span>`;
                else if(ss)statusTag=`<span class="enemy-tag etag-ok">✓ OKAY</span>`;
                const lastHtml=lastAgo?`<span class="enemy-tag" style="background:rgba(255,255,255,.04);color:${isOnline?'var(--green)':'var(--muted)'};border:1px solid rgba(255,255,255,.08);">⏱ ${isOnline?'Online':lastAgo.text+' ago'}</span>`:'';
                const facTag=facId?`<span class="enemy-tag" style="background:rgba(255,255,255,.05);color:var(--muted);border:1px solid rgba(255,255,255,.08);">⚔ ${Utils.escHtml(facName||'Faction')}</span>`:'';
                const ageHtml=ageDays>0?`<span class="enemy-level" style="color:var(--green);">Age ${ageDays}</span>`:'';
                const bountyHtml=bountyR>0?`<span class="enemy-level" style="color:var(--amber);">🎯 ${Utils.fmtMoney(bountyR)}</span>`:'';
                return`<div class="${rowClass}" data-enemy-id="${enemy.id}">
                    <div class="enemy-top">
                        <a class="enemy-name-link" href="https://www.torn.com/profiles.php?XID=${enemy.id}" target="_blank">${Utils.escHtml(enemy.name)}</a>
                        <span class="enemy-level">Lv ${level}</span>
                        ${ageHtml}${bountyHtml}${ffHtml}
                    </div>
                    <div class="enemy-meta">
                        ${statusTag}${lastHtml}${facTag}
                        ${jobCo?`<span class="enemy-tag etag-job">🏢 ${Utils.escHtml(jobCo)}${jobPos?` · ${Utils.escHtml(jobPos)}`:''}</span>`:''}
                    </div>
                    ${enemy.note?`<div class="enemy-note">📝 ${Utils.escHtml(enemy.note)}</div>`:''}
                    <div class="enemy-actions">
                        <a class="enemy-action-btn eab-attack"  href="https://www.torn.com/page.php?sid=attack&user2ID=${enemy.id}" target="_blank">⚔ Angriff</a>
                        <a class="enemy-action-btn eab-profile" href="https://www.torn.com/profiles.php?XID=${enemy.id}" target="_blank">👤 Profil</a>
                        <button class="enemy-action-btn eab-note"   data-note-enemy="${enemy.id}">📝 Notiz</button>
                        <button class="enemy-action-btn eab-grp"    data-move-enemy="${enemy.id}">📁 Gruppe</button>
                        <button class="enemy-action-btn eab-remove" data-remove-enemy="${enemy.id}">✕</button>
                    </div>
                </div>`;
            };
 
            const ffKey=hud.store.get('ffKey','');
            const ffKeyHint=!ffKey?`<div style="background:rgba(245,158,11,.08);border:1px solid rgba(245,158,11,.2);border-radius:var(--r);padding:8px 12px;font-size:10px;color:var(--amber);flex-shrink:0;">⚠ Kein FF Scouter Key – FF-Werte nicht verfügbar. Key in Settings eingeben.</div>`:'';
            const groupSelOpts=s.enemyGroups.map(g=>`<option value="${g.id}">${g.name}</option>`).join('');
 
            let groupedHtml='';
            if(enemies.length>0){
                s.enemyGroups.forEach(grp=>{
                    const grpEnemies=enemies.filter(e=>(e.groupId||'default')===grp.id);
                    if(grpEnemies.length===0)return;
                    groupedHtml+=`<div class="enemy-group">
                        <div class="enemy-group-header" data-toggle-group="${grp.id}">
                            <span class="enemy-group-color-dot" style="background:${grp.color};box-shadow:0 0 5px ${grp.color}80;"></span>
                            <span class="enemy-group-name">${Utils.escHtml(grp.name)}</span>
                            <span class="enemy-group-count">${grpEnemies.length}</span>
                            <span class="enemy-group-arrow ${grp.collapsed?'':'open'}">▶</span>
                        </div>
                        ${grp.collapsed?'':`<div class="enemy-group-body">${grpEnemies.map(e=>renderRow(e)).join('')}</div>`}
                    </div>`;
                });
                const validGroupIds=s.enemyGroups.map(g=>g.id);
                const ungrouped=enemies.filter(e=>!validGroupIds.includes(e.groupId||'default'));
                if(ungrouped.length){
                    groupedHtml+=`<div class="enemy-group"><div class="enemy-group-header"><span class="enemy-group-color-dot" style="background:var(--muted);"></span><span class="enemy-group-name">Ohne Gruppe</span><span class="enemy-group-count">${ungrouped.length}</span></div><div class="enemy-group-body">${ungrouped.map(e=>renderRow(e)).join('')}</div></div>`;
                }
            }
 
            body.innerHTML=`
            ${ffKeyHint}
            <div class="card" style="flex-shrink:0;">
                <div class="card-header"><span class="card-title red">💀 ENEMY TRACKER</span><div style="display:flex;gap:5px;align-items:center;"><span class="badge badge-red">${enemies.length} Enemies</span><button class="scan-btn" id="thud-enemy-refresh" style="padding:4px 10px;font-size:10px;">${s.enemyFetchRunning?'<span class="spinning">↺</span>':'↺ Refresh'}</button></div></div>
                <div class="card-body" style="padding:10px 14px;gap:7px;">
                    <div style="font-size:10px;color:var(--muted);margin-bottom:2px;">Spieler-ID eingeben:</div>
                    <div class="enemy-add-row">
                        <input type="number" class="enemy-id-input" id="thud-enemy-id" placeholder="Spieler ID (z.B. 123456)" min="1">
                        <select id="thud-enemy-grp-sel" style="background:var(--bg);border:1px solid var(--border);border-radius:var(--r-sm);padding:7px 8px;color:var(--text);font-size:11px;outline:none;flex-shrink:0;">${groupSelOpts}</select>
                        <button class="scan-btn" id="thud-enemy-add" style="white-space:nowrap;">+ Add</button>
                    </div>
                </div>
            </div>
            <div class="card" style="flex-shrink:0;">
                <div class="card-header">
                    <span class="card-title amber">📁 GRUPPEN</span>
                    <button class="scan-btn" id="thud-enemy-new-group" style="padding:4px 10px;font-size:10px;">+ Neue Gruppe</button>
                </div>
                <div class="card-body" style="padding:8px 14px;gap:5px;flex-direction:row;flex-wrap:wrap;">
                    ${s.enemyGroups.map(g=>`<div style="display:flex;align-items:center;gap:5px;background:var(--bg-el);border:1px solid var(--border);border-left:3px solid ${g.color};border-radius:6px;padding:4px 8px;">
                        <span style="font-size:10px;font-weight:700;color:var(--text);">${Utils.escHtml(g.name)}</span>
                        <span style="font-size:9px;color:var(--muted);font-family:var(--mono);">${enemies.filter(e=>(e.groupId||'default')===g.id).length}</span>
                        ${g.id!=='default'?`<button class="enemy-group-del-btn" data-del-group="${g.id}" style="padding:1px 5px;font-size:9px;">✕</button>`:''}
                    </div>`).join('')}
                </div>
            </div>
            ${enemies.length===0?`<div class="empty-state"><div class="es-icon">💀</div><div class="es-text">Keine Enemies</div><div class="es-sub">Füge Spieler-IDs hinzu um sie zu tracken</div></div>`:`<div class="card" style="flex-shrink:0;"><div class="card-header"><span class="card-title amber">⚠ ENEMY LIST</span><span style="font-size:9px;color:var(--muted);">Auto-Refresh 30s</span></div><div class="card-body p0" id="thud-enemy-groups">${groupedHtml}</div></div>`}
            <div style="background:rgba(239,68,68,.06);border:1px solid rgba(239,68,68,.15);border-radius:var(--r);padding:9px 12px;font-size:10px;color:var(--muted);flex-shrink:0;">⚠ Enemy-Reise-Alarm → <b style="color:var(--text);">Alarme → Enemy reist ab</b></div>`;
 
            document.getElementById('thud-enemy-refresh')?.addEventListener('click',()=>{hud.fetcher.fetchEnemyData();this.renderEnemy();});
            document.getElementById('thud-enemy-add')?.addEventListener('click',()=>{
                const id=document.getElementById('thud-enemy-id')?.value?.trim();
                const grp=document.getElementById('thud-enemy-grp-sel')?.value||'default';
                addEnemy(id,'',grp);
            });
            document.getElementById('thud-enemy-id')?.addEventListener('keydown',e=>{if(e.key==='Enter'){const id=e.target.value?.trim(),grp=document.getElementById('thud-enemy-grp-sel')?.value||'default';addEnemy(id,'',grp);}});
            document.getElementById('thud-enemy-new-group')?.addEventListener('click',()=>{
                const modal=document.createElement('div'); modal.className='thud-modal-bg';
                const swatches=GROUP_COLORS.map((c,i)=>`<span class="group-color-swatch ${i===0?'selected':''}" style="background:${c};" data-color="${c}"></span>`).join('');
                modal.innerHTML=`<div class="thud-modal"><h3>📁 Neue Gruppe</h3><p>Name und Farbe für die neue Enemy-Gruppe:</p><input id="thud-grp-name" placeholder="z.B. Revanche, War Targets..." /><div class="group-color-picker" id="thud-grp-colors">${swatches}</div><div class="modal-btns" style="margin-top:12px;"><button class="modal-cancel" id="thud-grp-cancel">Abbrechen</button><button class="modal-save" id="thud-grp-save">Erstellen</button></div></div>`;
                document.body.appendChild(modal);
                let selectedColor=GROUP_COLORS[0];
                modal.querySelectorAll('.group-color-swatch').forEach(sw=>{
                    sw.addEventListener('click',()=>{modal.querySelectorAll('.group-color-swatch').forEach(s=>s.classList.remove('selected'));sw.classList.add('selected');selectedColor=sw.dataset.color;});
                });
                document.getElementById('thud-grp-save').onclick=()=>{
                    const name=document.getElementById('thud-grp-name')?.value?.trim();
                    if(!name){hud.toasts.show('⚠','Name fehlt','Bitte Gruppenname eingeben','warn',2000);return;}
                    s.addEnemyGroup(name,selectedColor);modal.remove();this.renderEnemy();
                };
                document.getElementById('thud-grp-cancel').onclick=()=>modal.remove();
                modal.addEventListener('click',e=>{if(e.target===modal)modal.remove();});
            });
            body.querySelectorAll('[data-del-group]').forEach(btn=>btn.addEventListener('click',()=>{
                const gid=btn.dataset.delGroup;
                const grp=s.enemyGroups.find(g=>g.id===gid);
                if(confirm(`Gruppe "${grp?.name}" löschen? Enemies werden zu "Allgemein" verschoben.`)){s.removeEnemyGroup(gid);this.renderEnemy();}
            }));
            body.querySelectorAll('[data-toggle-group]').forEach(btn=>btn.addEventListener('click',()=>{s.toggleGroupCollapse(btn.dataset.toggleGroup);this.renderEnemy();}));
            body.querySelectorAll('[data-remove-enemy]').forEach(btn=>btn.addEventListener('click',()=>{const id=btn.dataset.removeEnemy;const en=s.enemies.find(e=>String(e.id)===String(id));if(confirm(`Enemy "${en?.name||id}" wirklich entfernen?`)){s.removeEnemy(id);this.renderEnemy();}}));
            body.querySelectorAll('[data-move-enemy]').forEach(btn=>btn.addEventListener('click',()=>{
                const id=btn.dataset.moveEnemy,en=s.enemies.find(e=>String(e.id)===String(id));
                const modal=document.createElement('div');modal.className='thud-modal-bg';
                const opts=s.enemyGroups.map(g=>`<button class="sort-btn" data-assign-group="${g.id}" style="border-left:3px solid ${g.color};${(en?.groupId||'default')===g.id?'background:rgba(99,102,241,.15);border-color:var(--accent);color:var(--accent2);':''}margin-bottom:5px;text-align:left;padding:7px 10px;width:100%;">${g.name} (${s.enemies.filter(e=>(e.groupId||'default')===g.id).length})</button>`).join('');
                modal.innerHTML=`<div class="thud-modal"><h3>📁 Gruppe für ${Utils.escHtml(en?.name||id)}</h3><div style="display:flex;flex-direction:column;gap:3px;margin-bottom:12px;">${opts}</div><div class="modal-btns"><button class="modal-cancel" id="thud-grp-mv-cancel">Abbrechen</button></div></div>`;
                document.body.appendChild(modal);
                modal.querySelectorAll('[data-assign-group]').forEach(b=>b.addEventListener('click',()=>{s.updateEnemyGroup(id,b.dataset.assignGroup);modal.remove();this.renderEnemy();}));
                document.getElementById('thud-grp-mv-cancel').onclick=()=>modal.remove();
                modal.addEventListener('click',e=>{if(e.target===modal)modal.remove();});
            }));
            body.querySelectorAll('[data-note-enemy]').forEach(btn=>btn.addEventListener('click',()=>{const id=btn.dataset.noteEnemy;const en=s.enemies.find(e=>String(e.id)===String(id));const modal=document.createElement('div');modal.className='thud-modal-bg';modal.innerHTML=`<div class="thud-modal"><h3>📝 Notiz für ${Utils.escHtml(en?.name||id)}</h3><p>Füge eine persönliche Notiz hinzu:</p><textarea id="thud-note-input" placeholder="Deine Notiz...">${Utils.escHtml(en?.note||'')}</textarea><div class="modal-btns"><button class="modal-cancel" id="thud-note-cancel">Abbrechen</button><button class="modal-save" id="thud-note-save">Speichern</button></div></div>`;document.body.appendChild(modal);document.getElementById('thud-note-save').onclick=()=>{s.updateEnemyNote(id,document.getElementById('thud-note-input').value);modal.remove();this.renderEnemy();};document.getElementById('thud-note-cancel').onclick=()=>modal.remove();modal.addEventListener('click',e=>{if(e.target===modal)modal.remove();}); }));
            if(ffKey&&enemies.length){
                const ids=enemies.map(e=>Number(e.id));
                getFFData(ids,ffKey,(ffResults)=>{
                    s.ffData={...s.ffData,...ffResults};
                    ids.forEach(id=>{const el=document.querySelector(`[data-ff-badge="${id}"]`);if(el)el.innerHTML=ffBadge(ffResults[id]);});
                });
            }
        }
 
        renderCompany() {
            const body=this.body; if(!body)return;
            const s=this.state;
            const cp=s.companyProfile||null;
            const employees=s.companyEmployees||[];
            const ws=s.workStats||null;
 
            const wsHtml=ws?`<div class="card" style="flex-shrink:0;"><div class="card-header"><span class="card-title blue">💼 DEINE WORKING STATS</span></div><div class="card-body"><div class="stat-grid stat-grid-3"><div class="stat-card"><div class="sv sv-amber">${Utils.fmtStat(ws.manual)}</div><div class="sl">🛠 Manual</div></div><div class="stat-card"><div class="sv sv-blue">${Utils.fmtStat(ws.intelligence)}</div><div class="sl">🧠 Intel</div></div><div class="stat-card"><div class="sv sv-green">${Utils.fmtStat(ws.endurance)}</div><div class="sl">🏃 Endure</div></div></div></div></div>`:'';
 
            if(!cp){
                body.innerHTML=`${wsHtml}<div class="empty-state"><div class="es-icon">🏢</div><div class="es-text">Keine Firmendaten</div><div class="es-sub">Du musst Inhaber oder Direktor sein.</div></div>`;
                return;
            }
 
            const name=cp.name||'—',type=cp.type?.name||(typeof cp.type==='string'?cp.type:'')||cp.company_type||'—',rating=cp.rating??'—';
            const _s=(v)=>{if(v===null||v===undefined)return null;if(typeof v==='number')return v;if(typeof v==='object')return v.value??v.current??v.amount??null;return null;};
            const dailyIncome=Number(cp.income?.daily??cp.daily_income??cp.daily_revenue??0);
            const weeklyProfit=Number(cp.income?.weekly??cp.weekly_profit??0);
            const empHired=cp.employees?.hired??cp.employees_hired??employees.length??0;
            const empCap=cp.employees?.capacity??cp.employees_capacity??'—';
            const effs=employees.map(e=>Number(e.effectiveness?.working_stats?.total??e.effectiveness?.total??e.working_stats?.total??0)).filter(v=>v>0);
            const avgEff=effs.length?Math.round(effs.reduce((a,v)=>a+v,0)/effs.length):(Number(_s(cp.efficiency))||0);
 
            body.innerHTML=`${wsHtml}
            <div class="card" style="flex-shrink:0;"><div class="card-header"><span class="card-title amber">🏢 ${Utils.escHtml(name)}</span><span class="badge badge-amber">${Utils.escHtml(String(type))}</span></div>
            <div class="card-body"><div class="company-kpi-grid"><div class="company-kpi"><div class="kv" style="color:var(--green);">${Utils.fmtMoney(dailyIncome)}</div><div class="kl">Täglich</div></div><div class="company-kpi"><div class="kv" style="color:var(--amber);">${Utils.fmtMoney(weeklyProfit)}</div><div class="kl">Wöchentlich</div></div><div class="company-kpi"><div class="kv" style="color:var(--blue);">${empHired}/${empCap}</div><div class="kl">Mitarbeiter</div></div><div class="company-kpi"><div class="kv" style="color:${avgEff>=60?'var(--green)':avgEff>=30?'var(--amber)':'var(--muted)'};">${avgEff>0?avgEff+'%':'—'}</div><div class="kl">Effizienz Ø</div></div></div>${rating!=='—'?`<div class="row"><span class="row-label">⭐ Rating</span><span class="row-value" style="color:var(--amber);">${rating} / 10</span></div>`:''}</div></div>
            ${employees.length>0?`<div class="card" style="flex-shrink:0;"><div class="card-header"><span class="card-title green">👷 MITARBEITER</span><span class="badge badge-green">${avgEff>0?avgEff+'%':'—'} Ø</span></div><div class="card-body p0">${employees.slice(0,40).map(e=>{const eff=Number(e.effectiveness?.working_stats?.total??e.effectiveness?.total??e.working_stats?.total??0);const pos=(()=>{const p=e.position??e.role??null;if(!p)return'—';if(typeof p==='string')return p;if(typeof p==='object')return p.name??p.title??'—';return'—';})();const online=((Date.now()/1000-(e.last_action?.timestamp||0))<120);const ec=eff>=60?'var(--green)':eff>=30?'var(--amber)':eff>0?'var(--red)':'var(--muted)';return`<div class="company-emp-row"><span class="status-dot sd-${online?'online':'offline'}" style="flex-shrink:0;"></span><span class="company-emp-name">${Utils.escHtml(e.name||`#${e.id}`)}</span><span class="company-emp-pos">${Utils.escHtml(pos)}</span><span class="company-emp-eff" style="color:${ec};">${eff>0?eff+'%':'—'}</span></div>`;}).join('')}</div></div>`:''}`;
        }
 
        renderNetworth() {
            const body=this.body; if(!body)return;
            const nw=this.state.networthData,hist=this.state.networthHistory;
            if(!nw){body.innerHTML=`<div class="empty-state"><div class="es-icon">💰</div><div class="es-text">Lade Vermögensdaten...</div><div class="es-sub">Benötigt Full Access API Key</div></div>`;return;}
            const now2=Utils.now();
            // Filter only history entries that were actually fetched (not tick-duplicated)
            const validHist=hist.filter((h,i)=>i===0||h.ts!==hist[i-1]?.ts);
            const oh=validHist.filter(h=>h.ts>=now2-3600);
            const change=oh.length>=2?oh[oh.length-1].val-oh[0].val:0;
            const wallet=Number(nw.wallet??nw.cash??0),bank=Number(nw.bank??0),stocks=Number(nw.stockmarket??nw.stocks??0);
            const totalLiquid=wallet+stocks;
            const bazaar=Number(nw.bazaar??0),props=Number(nw.properties??0),piggy=Number(nw.piggybank??0),fvault=Number(nw.factionvault??0);
            body.innerHTML=`
            <div class="nw-ticker">
                <div style="font-size:9px;color:var(--muted);text-transform:uppercase;letter-spacing:1px;margin-bottom:4px;">LIQUIDES VERMÖGEN (Cash + Aktien)</div>
                <div style="display:flex;align-items:baseline;gap:10px;">
                    <div class="nw-main-val">${Utils.fmtMoney(totalLiquid)}</div>
                    ${change!==0?`<div class="nw-change ${change>0?'pos':'neg'}">${change>0?'▲':'▼'} ${Utils.fmtMoney(Math.abs(change))} / 1h</div>`:'<div class="nw-change" style="color:var(--muted);">± —</div>'}
                </div>
            </div>
            <div class="card" style="flex-shrink:0;">
                <div class="card-header"><span class="card-title green">💵 LIQUIDITÄT</span></div>
                <div class="card-body">
                    <div class="row"><span class="row-label">💵 Cash</span><span class="row-value" style="color:var(--green);">${Utils.fmtMoney(wallet)}</span></div>
                    <div class="row"><span class="row-label">📈 Aktien</span><span class="row-value" style="color:var(--amber);">${Utils.fmtMoney(stocks)}</span></div>
                    <div class="row"><span class="row-label">🏦 Bank</span><span class="row-value" style="color:var(--muted);">${Utils.fmtMoney(bank)}</span></div>
                </div>
            </div>
            ${(bazaar||props||piggy||fvault)?`<div class="card" style="flex-shrink:0;"><div class="card-header"><span class="card-title">📦 WEITERE ASSETS</span></div><div class="card-body">${bazaar?`<div class="row"><span class="row-label">🏪 Bazaar</span><span class="row-value">${Utils.fmtMoney(bazaar)}</span></div>`:''}${props?`<div class="row"><span class="row-label">🏠 Immobilien</span><span class="row-value">${Utils.fmtMoney(props)}</span></div>`:''}${piggy?`<div class="row"><span class="row-label">🐷 Sparschwein</span><span class="row-value">${Utils.fmtMoney(piggy)}</span></div>`:''}${fvault?`<div class="row"><span class="row-label">🏛 Faction Vault</span><span class="row-value">${Utils.fmtMoney(fvault)}</span></div>`:''}</div></div>`:''} 
            ${hist.length>=2?`<div class="card" style="flex-shrink:0;"><div class="card-header"><span class="card-title green">📈 VERLAUF (Liquide)</span><span style="font-size:9px;color:var(--muted);">${hist.length} Datenpunkte</span></div><div class="card-body" style="padding:12px 14px 16px;">${this._richChart(hist)}</div></div>`:''}`;
            this._attachChartTooltip();
        }
 
        _richChart(hist) {
            if(hist.length<2)return'';
            const W=280,H=80,PAD_L=44,PAD_B=18,PAD_T=8,PAD_R=8;
            const innerW=W-PAD_L-PAD_R,innerH=H-PAD_T-PAD_B;
            const vals=hist.map(h=>h.val);
            const mn=Math.min(...vals),mx=Math.max(...vals),range=mx-mn||1;
            const rising=vals[vals.length-1]>=vals[0];
            const lineColor=rising?'var(--green)':'var(--red)';
            const fillColor=rising?'rgba(16,185,129,.12)':'rgba(239,68,68,.12)';
            let gridLines='',gridLabels='';
            for(let i=0;i<=3;i++){
                const frac=i/3;
                const yVal=mn+frac*range;
                const yPx=PAD_T+innerH-(frac*innerH);
                gridLines+=`<line x1="${PAD_L}" y1="${yPx.toFixed(1)}" x2="${W-PAD_R}" y2="${yPx.toFixed(1)}" stroke="rgba(255,255,255,.05)" stroke-width="1"/>`;
                gridLabels+=`<text x="${PAD_L-4}" y="${(yPx+3).toFixed(1)}" text-anchor="end" class="nw-chart-grid-lbl">${Utils.fmtMoney(yVal)}</text>`;
            }
            let timeLabels='';
            if(hist.length>1){
                const fmt=(ts)=>new Date(ts*1000).toLocaleTimeString('de-DE',{hour:'2-digit',minute:'2-digit'});
                const pts=[[0,PAD_L],[Math.floor(hist.length/2),PAD_L+innerW/2],[hist.length-1,W-PAD_R]];
                const anchors=['start','middle','end'];
                pts.forEach(([idx,x],i)=>{
                    timeLabels+=`<text x="${x.toFixed(1)}" y="${H-2}" text-anchor="${anchors[i]}" class="nw-chart-grid-lbl">${fmt(hist[idx].ts)}</text>`;
                });
            }
            const toX=(i)=>PAD_L+(i/(hist.length-1))*innerW;
            const toY=(v)=>PAD_T+innerH-((v-mn)/range)*innerH;
            const pts2=hist.map((h,i)=>`${toX(i).toFixed(1)},${toY(h.val).toFixed(1)}`).join(' ');
            const fillPath=`M ${toX(0).toFixed(1)},${toY(hist[0].val).toFixed(1)} `+
                hist.map((h,i)=>`L ${toX(i).toFixed(1)},${toY(h.val).toFixed(1)}`).join(' ')+
                ` L ${toX(hist.length-1).toFixed(1)},${(PAD_T+innerH).toFixed(1)} L ${toX(0).toFixed(1)},${(PAD_T+innerH).toFixed(1)} Z`;
            const hoverRects=hist.map((h,i)=>{
                const x=toX(i),y=toY(h.val);
                const hw=innerW/hist.length;
                return`<rect x="${(x-hw/2).toFixed(1)}" y="${PAD_T}" width="${hw.toFixed(1)}" height="${innerH}"
                    fill="transparent" class="nw-chart-hover"
                    data-val="${h.val}" data-ts="${h.ts}" data-x="${x.toFixed(1)}" data-y="${y.toFixed(1)}"/>`;
            }).join('');
            const lastX=toX(hist.length-1),lastY=toY(hist[hist.length-1].val);
            return`<div class="nw-chart-wrap" id="nw-chart-wrap">
                <svg viewBox="0 0 ${W} ${H}" class="nw-chart-svg" id="nw-chart-svg">
                    ${gridLines}${gridLabels}${timeLabels}
                    <path d="${fillPath}" fill="${fillColor}"/>
                    <polyline points="${pts2}" fill="none" stroke="${lineColor}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
                    <circle cx="${lastX.toFixed(1)}" cy="${lastY.toFixed(1)}" r="3.5" fill="${lineColor}" stroke="var(--bg-card)" stroke-width="1.5"/>
                    <line id="nw-chart-vline" x1="0" y1="${PAD_T}" x2="0" y2="${PAD_T+innerH}" stroke="rgba(255,255,255,.15)" stroke-width="1" stroke-dasharray="3,2" display="none"/>
                    <circle id="nw-chart-dot" cx="0" cy="0" r="4" fill="${lineColor}" stroke="var(--bg-card)" stroke-width="1.5" display="none"/>
                    ${hoverRects}
                </svg>
                <div class="nw-chart-tooltip" id="nw-chart-tooltip"></div>
            </div>`;
        }
 
        _attachChartTooltip() {
            setTimeout(()=>{
                const svg=document.getElementById('nw-chart-svg');
                const tooltip=document.getElementById('nw-chart-tooltip');
                const vline=document.getElementById('nw-chart-vline');
                const dot=document.getElementById('nw-chart-dot');
                if(!svg||!tooltip)return;
                svg.querySelectorAll('.nw-chart-hover').forEach(rect=>{
                    rect.addEventListener('mouseenter',(e)=>{
                        const val=Number(rect.dataset.val),ts=Number(rect.dataset.ts);
                        const x=rect.dataset.x,y=rect.dataset.y;
                        if(vline){vline.setAttribute('x1',x);vline.setAttribute('x2',x);vline.removeAttribute('display');}
                        if(dot){dot.setAttribute('cx',x);dot.setAttribute('cy',y);dot.removeAttribute('display');}
                        tooltip.style.display='block';
                        tooltip.innerHTML=`<div style="font-weight:700;color:var(--amber);">${Utils.fmtMoney(val)}</div><div style="color:var(--muted);font-size:9px;">${new Date(ts*1000).toLocaleTimeString('de-DE',{hour:'2-digit',minute:'2-digit'})}</div>`;
                        const svgW=svg.getBoundingClientRect().width||280;
                        const pxX=(Number(x)/280)*svgW;
                        tooltip.style.left=Math.min(pxX+6,svgW-80)+'px';
                        tooltip.style.top='4px';
                    });
                    rect.addEventListener('mouseleave',()=>{
                        tooltip.style.display='none';
                        if(vline)vline.setAttribute('display','none');
                        if(dot)dot.setAttribute('display','none');
                    });
                });
            },100);
        }
 
        renderNotifications() {
            const body=this.body; if(!body)return;
            const s=this.state;
            const ag=(title,keys)=>{const rows=keys.filter(k=>ALARM_DEFAULTS[k]).map(key=>{const def=ALARM_DEFAULTS[key],on=s.isAlarm(key);return`<div class="alarm-row-item"><div style="flex:1;min-width:0;"><div class="alarm-lbl">${def.label}</div><div class="alarm-desc">${def.desc}</div></div><label class="toggle"><input type="checkbox" data-alarm="${key}" ${on?'checked':''}><span class="toggle-slider"></span></label></div>`;}).join('');return`<div class="settings-section"><div class="settings-section-title">${title}</div>${rows}</div>`;};
            const eT=(s.energyThresholds||[50,75,100]).join(', '),nT=(s.nerveThresholds||[50,100]).join(', ');
            body.innerHTML=`
            <div class="card" style="flex-shrink:0;"><div class="card-body"><div style="display:flex;gap:6px;margin-bottom:12px;flex-wrap:wrap;"><button class="thud-btn ${s.store.get('sound',true)?'active':''}" id="thud-toggle-sound">${s.store.get('sound',true)?'🔊 Sound AN':'🔇 Sound AUS'}</button><button class="thud-btn ${s.store.get('notif',true)?'active':''}" id="thud-toggle-notif">${s.store.get('notif',true)?'🔔 Toasts AN':'🔕 Toasts AUS'}</button></div><div class="settings-section-title">SOUND-PROFIL</div><div class="sound-profile-grid">${['classic','military','subtle'].map(p=>`<button class="sp-btn ${s.store.get('soundProfile','classic')===p?'active':''}" data-sp="${p}">${{classic:'🎵 Classic',military:'🪖 Military',subtle:'🔉 Subtle'}[p]}</button>`).join('')}</div></div></div>
            <div class="card" style="flex-shrink:0;">${ag('✈ TRAVEL',['travel_warn','travel_crit','landing_timer'])}${ag('⚡ ENERGIE',['energy_full','energy_thresh','energy_over151'])}${ag('🧠 NERVE',['nerve_full','nerve_thresh'])}${ag('❤ HP',['hp_crit'])}${ag('⏱ COOLDOWNS',['hospital_free','jail_free','drug_ready','booster_ready','medical_ready'])}${ag('⛓ CHAIN',['chain_warn'])}${ag('💀 ENEMY',['enemy_travel'])}</div>
            <div class="card" style="flex-shrink:0;"><div class="card-body" style="padding:12px 14px;gap:10px;"><div class="settings-section-title" style="border:none;padding:0 0 8px;">⚡ ENERGIE SCHWELLWERTE</div><div style="font-size:11px;color:var(--muted);margin-bottom:4px;">Alarme bei diesen % Werten · kein Alarm über ${HAPPY_JUMP_THRESHOLD}</div><div style="display:flex;gap:8px;align-items:center;"><input type="text" class="threshold-input" id="thud-e-thresh" style="width:130px;" value="${eT}" placeholder="50,75,100"><button class="sort-btn" id="thud-e-thresh-save">✓ Speichern</button></div></div></div>
            <div class="card" style="flex-shrink:0;"><div class="card-body" style="padding:12px 14px;gap:10px;"><div class="settings-section-title" style="border:none;padding:0 0 8px;">🧠 NERVE SCHWELLWERTE</div><div style="display:flex;gap:8px;align-items:center;"><input type="text" class="threshold-input" id="thud-n-thresh" style="width:130px;" value="${nT}" placeholder="50,100"><button class="sort-btn" id="thud-n-thresh-save">✓ Speichern</button></div></div></div>
            <div class="card" style="flex-shrink:0;"><div class="card-body"><div class="settings-section-title">TRAVEL ALERT TYP</div>${[['toast','🔔 Toast'],['fullscreen','🖥 Vollbild']].map(([k,lbl])=>`<div class="alarm-row-item"><div class="alarm-lbl">${lbl}</div><label class="toggle"><input type="checkbox" data-ta="${k}" ${s.isTravelAlert(k)?'checked':''}><span class="toggle-slider"></span></label></div>`).join('')}</div></div>`;
            body.querySelectorAll('[data-alarm]').forEach(el=>el.addEventListener('change',e=>s.setAlarm(e.target.dataset.alarm,e.target.checked)));
            body.querySelectorAll('[data-ta]').forEach(el=>el.addEventListener('change',e=>s.setTravelAlert(e.target.dataset.ta,e.target.checked)));
            body.querySelectorAll('[data-sp]').forEach(btn=>btn.addEventListener('click',()=>{hud.store.set('soundProfile',btn.dataset.sp);hud.audio.play('notify');this.renderNotifications();}));
            document.getElementById('thud-toggle-sound')?.addEventListener('click',()=>{hud.store.set('sound',!hud.store.get('sound',true));this.renderNotifications();});
            document.getElementById('thud-toggle-notif')?.addEventListener('click',()=>{hud.store.set('notif',!hud.store.get('notif',true));this.renderNotifications();});
            document.getElementById('thud-e-thresh-save')?.addEventListener('click',()=>{const raw=document.getElementById('thud-e-thresh')?.value||'';const arr=raw.split(',').map(x=>parseInt(x.trim())).filter(x=>!isNaN(x)&&x>0&&x<=100);s.setEnergyThresholds([...new Set(arr)].sort((a,b)=>a-b));hud.toasts.show('✅','Energie-Schwellwerte gespeichert',arr.join(', ')+'%','info',3000);});
            document.getElementById('thud-n-thresh-save')?.addEventListener('click',()=>{const raw=document.getElementById('thud-n-thresh')?.value||'';const arr=raw.split(',').map(x=>parseInt(x.trim())).filter(x=>!isNaN(x)&&x>0&&x<=100);s.setNerveThresholds([...new Set(arr)].sort((a,b)=>a-b));hud.toasts.show('✅','Nerve-Schwellwerte gespeichert',arr.join(', ')+'%','info',3000);});
        }
 
        renderSettings() {
            const body=this.body; if(!body)return;
            const s=this.state,fabPinned=hud.store.get('fabPinned',false);
            const ffKey=hud.store.get('ffKey','');
            const visRows=Object.entries(VISIBILITY_DEFAULTS).map(([k,v])=>`<div class="vis-row" style="margin-bottom:3px;"><span class="vis-lbl">${v.label}</span><label class="toggle"><input type="checkbox" data-vis="${k}" ${s.isVisible(k)?'checked':''}><span class="toggle-slider"></span></label></div>`).join('');
            const curLinks=hud.store.get('customQuickLinks',null)||[{label:'Stock\nMarket',icon:'📈',url:'https://www.torn.com/page.php?sid=stocks'},{label:'Casino',icon:'🎰',url:'https://www.torn.com/page.php?sid=slots'},{label:'Bazaar',icon:'🏪',url:'https://www.torn.com/bazaar.php?userId=0'},{label:'Forum',icon:'💬',url:'https://www.torn.com/forums.php'}];
            body.innerHTML=`
            <div class="card" style="flex-shrink:0;"><div class="card-body"><div class="settings-section-title">THEME</div><div class="theme-grid">${Object.entries(THEMES).map(([k,t])=>`<button class="theme-btn ${hud.store.get('theme','obsidian')===k?'active':''}" data-theme="${k}">${t.name}</button>`).join('')}</div></div></div>
            <div class="card" style="flex-shrink:0;"><div class="card-header"><span class="card-title red">🔴 FAB BUTTON</span></div><div class="card-body" style="padding:4px 0 8px;gap:0;"><div class="fab-pin-row"><div><div class="fab-pin-lbl">📌 Position fixieren</div><div class="fab-pin-desc">Button lässt sich nicht mehr verschieben</div></div><label class="toggle"><input type="checkbox" id="thud-fab-pin" ${fabPinned?'checked':''}><span class="toggle-slider"></span></label></div><div style="padding:7px 14px;display:flex;gap:6px;align-items:center;"><button class="sort-btn" id="thud-fab-reset">↺ Position zurücksetzen</button></div></div></div>
            <div class="card" style="flex-shrink:0;"><div class="card-header"><span class="card-title accent">📊 FF SCOUTER</span><span class="badge badge-${ffKey?'green':'red'}">${ffKey?'Aktiv':'Kein Key'}</span></div><div class="card-body" style="padding:12px 14px;gap:8px;"><div style="font-size:11px;color:var(--muted);line-height:1.6;">Für FF-Werte (FairFight) in Enemy &amp; Bounty Tab. Key von <a href="https://ffscouter.com" target="_blank" style="color:var(--accent2);">ffscouter.com</a> — dort "FFScouter3" Key verwenden.</div><div style="display:flex;gap:8px;align-items:center;"><input type="text" id="thud-ff-key-input" placeholder="FF Scouter Limited API Key..." value="${Utils.escHtml(ffKey)}" style="flex:1;background:var(--bg);border:1px solid var(--border);border-radius:var(--r-sm);padding:7px 10px;color:var(--text);font-family:var(--mono);font-size:11px;outline:none;"><button class="sort-btn" id="thud-ff-key-save" style="border-color:rgba(16,185,129,.3);color:var(--green);">✓ Speichern</button></div>${ffKey?`<button class="sort-btn" id="thud-ff-cache-clear" style="color:var(--muted);font-size:10px;">🗑 FF Cache leeren</button>`:''}</div></div>
            <div class="card" style="flex-shrink:0;"><div class="card-body"><div class="settings-section-title">SICHTBARE ELEMENTE</div><div class="vis-grid">${visRows}</div></div></div>
            <div class="card" style="flex-shrink:0;"><div class="card-body" style="gap:8px;"><div class="settings-section-title">🔗 QUICK LINKS KONFIGURIEREN</div><div style="font-size:10px;color:var(--muted);margin-bottom:4px;">JSON-Array mit label, icon, url.</div><textarea id="thud-ql-json" style="width:100%;background:var(--bg);border:1px solid var(--border);border-radius:var(--r-sm);padding:8px;color:var(--text);font-family:var(--mono);font-size:10px;min-height:120px;resize:vertical;">${Utils.escHtml(JSON.stringify(curLinks,null,2))}</textarea><div style="display:flex;gap:6px;"><button class="sort-btn" id="thud-ql-save" style="border-color:rgba(16,185,129,.3);color:var(--green);">✓ Speichern</button><button class="sort-btn" id="thud-ql-reset" style="color:var(--muted);">↺ Standard</button></div></div></div>
            <div class="card" style="flex-shrink:0;"><div class="card-body"><div class="settings-section-title">TORN API KEY</div><div class="api-key-row"><span class="api-key-status ${hud.store.get('apiKey')?'set':'unset'}">${hud.store.get('apiKey')?'✓ API Key gesetzt':'⚠ Kein API Key'}</span><button class="thud-btn" id="thud-api-key-btn">🔑 Ändern</button></div></div></div>
            <div class="card" style="flex-shrink:0;"><div class="card-body"><div class="settings-section-title">TASTENKÜRZEL</div><div style="display:grid;grid-template-columns:1fr 1fr;gap:6px;font-size:11px;color:var(--dim);"><span><kbd style="background:var(--bg-el);border:1px solid var(--border);border-radius:3px;padding:1px 5px;font-family:var(--mono);font-size:9px;">M</kbd> Minimieren</span><span><kbd style="background:var(--bg-el);border:1px solid var(--border);border-radius:3px;padding:1px 5px;font-family:var(--mono);font-size:9px;">H</kbd> Ausblenden</span><span><kbd style="background:var(--bg-el);border:1px solid var(--border);border-radius:3px;padding:1px 5px;font-family:var(--mono);font-size:9px;">R</kbd> Refresh</span><span><kbd style="background:var(--bg-el);border:1px solid var(--border);border-radius:3px;padding:1px 5px;font-family:var(--mono);font-size:9px;">S</kbd> Settings</span></div></div></div>
            <div class="card" style="flex-shrink:0;"><div class="card-body" style="gap:8px;"><div class="settings-section-title" style="color:var(--red);">⚠ DANGER ZONE</div><div style="font-size:11px;color:var(--muted);margin-bottom:6px;">Alle gespeicherten Daten, Einstellungen, Enemies und Caches vollständig löschen.</div><button class="scan-btn" id="thud-clear-all" style="background:rgba(239,68,68,.15);border-color:rgba(239,68,68,.3);color:var(--red);width:100%;justify-content:center;">🗑 Alles löschen (Reset)</button></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();});
            document.getElementById('thud-clear-all')?.addEventListener('click',()=>{
                if(!confirm('⚠ WIRKLICH ALLES LÖSCHEN?\n\nDas löscht:\n• API Keys\n• Alle Enemies\n• Bounty-Daten\n• Networth-Verlauf\n• YATA-Daten & Favoriten\n• FF-Cache\n• Alle Einstellungen\n\nDiese Aktion kann nicht rückgängig gemacht werden!'))return;
                // Clear all GM storage keys
                const keys=['apiKey','ffKey','enemies','enemyGroups','pos','fabPos','width','height','minimized','visible','theme','activeTab','alarms','visibility','travelAlerts','sound','soundProfile','notif','bountySort','bountyFilterHosp','bountyLevelMin','bountyLevelMax','bountyRewardMin','bountyRewardMax','bountyFFMin','bountyFFMax','energyThresholds','nerveThresholds','_energyAlerted','_nerveAlerted','_enemyTravelAlerted','customQuickLinks','fabPinned','nwHistory','restockHistory','restockHistory2','yataRawStocksCache','yataLastFetch','yataAlarmFired','yataFavs','searchSortField','searchSortDir','searchFilterStatus','searchLvMin','searchLvMax','searchFFMin','searchFFMax'];
                keys.forEach(k=>{try{GM_setValue('thud_'+k,undefined);}catch(e){}try{GM_setValue(k,undefined);}catch(e){}});
                try{ffCache.clear();}catch(e){}
                hud.toasts.show('🗑','Alles gelöscht','Seite wird neu geladen...','info',3000);
                setTimeout(()=>location.reload(),2000);
            });
        }
 
        renderYataLive() {
            const body=this.body; if(!body)return;
            const s=this.state;
 
            // ── Favoriten aus GM Storage ──
            let favs=new Set();
            try{const f=GM_getValue('yataFavs','[]');favs=new Set(JSON.parse(f));}catch(e){}
            const saveFavs=()=>{try{GM_setValue('yataFavs',JSON.stringify([...favs]));}catch(e){}};
 
            if(s.travelItemsProfits===null&&s.travelItemsRestocks===null){
                // Automatisch im Hintergrund laden (kein Ladescreen wenn möglich)
                body.innerHTML='<div class="loading-state">LADE DATEN…</div>';
                hud.fetcher.fetchTravelItems(()=>this.renderYataLive());
                return;
            }
            if(s.travelItemsLoading){ body.innerHTML=this._loadingState(); return; }
 
            const profits  = s.travelItemsProfits  || [];
            const restocks = s.travelItemsRestocks  || [];
            const fetchTs  = s.travelItemsLastFetch;
            const tsStr    = fetchTs?new Date(fetchTs*1000).toLocaleTimeString('de-DE'):'—';
            const now      = Utils.now();
 
            const COUNTRY_MAP={mex:'Mexico',cay:'Cayman Islands',can:'Canada',haw:'Hawaii',
                uni:'United Kingdom',arg:'Argentina',swi:'Switzerland',jap:'Japan',
                chi:'China',uae:'UAE',sou:'South Africa'};
 
            const FLAG_URL={
                'United Kingdom':'https://flagcdn.com/24x18/gb.png',
                'Japan':'https://flagcdn.com/24x18/jp.png',
                'Canada':'https://flagcdn.com/24x18/ca.png',
                'Mexico':'https://flagcdn.com/24x18/mx.png',
                'Cayman Islands':'https://flagcdn.com/24x18/ky.png',
                'China':'https://flagcdn.com/24x18/cn.png',
                'Hawaii':'https://flagcdn.com/24x18/us.png',
                'Argentina':'https://flagcdn.com/24x18/ar.png',
                'Switzerland':'https://flagcdn.com/24x18/ch.png',
                'UAE':'https://flagcdn.com/24x18/ae.png',
                'South Africa':'https://flagcdn.com/24x18/za.png'
            };
            const FLAG_EMOJI=FLAG_URL; // alias
            const CODE_LABEL={
                'United Kingdom':'GB','Japan':'JP','Canada':'CA','Mexico':'MX',
                'Cayman Islands':'KY','China':'CN','Hawaii':'HI','Argentina':'AR',
                'Switzerland':'CH','UAE':'AE','South Africa':'ZA'
            };
            const CODE_COLOR={
                'United Kingdom':'#3b82f6','Japan':'#ef4444','Canada':'#f97316',
                'Mexico':'#22c55e','Cayman Islands':'#06b6d4','China':'#dc2626',
                'Hawaii':'#8b5cf6','Argentina':'#60a5fa','Switzerland':'#f43f5e',
                'UAE':'#f59e0b','South Africa':'#10b981'
            };
 
            // Items aus Raw YATA
            let allItems=[];
            for(const [code,countryObj] of Object.entries(s.yataRawStocks||{})){
                if(!countryObj||typeof countryObj!=='object')continue;
                const country=COUNTRY_MAP[code]||code;
                const updateTs=Number(countryObj.update||0);
                for(const item of (Array.isArray(countryObj.stocks)?countryObj.stocks:[])){
                    if(!item)continue;
                    allItems.push({code,country,name:item.name||'Item #'+item.id,
                        qty:Number(item.quantity||0),cost:Number(item.cost||0),updateTs});
                }
            }
            if(allItems.length===0){
                for(const p of profits)
                    allItems.push({code:'',country:p.country||'',name:p.item_name||p.name||'',
                        qty:Number(p.stock||0),cost:Number(p.buy_price||p.cost||0),updateTs:0});
            }
 
            // Restock-Map
            const restockMap={};
            for(const r of restocks)
                restockMap[(r.country||'')+'|'+(r.item_name||r.name||'')]=Number(r.restock_at||0);
 
            // Aktiver Länder-Filter + Suche + FavOnly aus State
            const activeCountry=s.yataCountryFilter||'';
            const search=(s.travelItemsSearch||'').toLowerCase().trim();
            const favOnly=s.yataFavOnly||false;
            const restockAlarm=s.yataRestockAlarm!==false;
 
            // Alle Länder für Pills (vor Filter)
            const countries=[...new Set(allItems.map(i=>i.country))].sort();
 
            // favKey früh definiert damit Filter es nutzen kann
            const favKey=it=>it.country+'|'+it.name;
 
            // Filter (favOnly: nur gemerkte Items)
            let filtered=allItems.filter(it=>{
                if(favOnly&&!favs.has(favKey(it)))return false;
                if(activeCountry&&it.country!==activeCountry)return false;
                if(search&&!it.name.toLowerCase().includes(search)&&!it.country.toLowerCase().includes(search))return false;
                return true;
            });
 
            // Sort: Favoriten → Xanax UK → auf Lager qty desc → leer
            filtered.sort((a,b)=>{
                const af=favs.has(favKey(a))?0:1;
                const bf=favs.has(favKey(b))?0:1;
                if(af!==bf)return af-bf;
                const ax=(a.name.toLowerCase().includes('xanax')&&a.country==='United Kingdom')?0:1;
                const bx=(b.name.toLowerCase().includes('xanax')&&b.country==='United Kingdom')?0:1;
                if(ax!==bx)return ax-bx;
                if((a.qty>0)!==(b.qty>0))return a.qty>0?-1:1;
                return b.qty-a.qty;
            });
 
            const fmt=(ts2)=>{
                if(!ts2)return '—';
                const d=new Date(ts2*1000);
                return d.toLocaleDateString('de-DE',{day:'2-digit',month:'2-digit'})
                    +'. '+d.toLocaleTimeString('de-DE',{hour:'2-digit',minute:'2-digit'});
            };
 
            // ── Zeilen ──
            const rows=filtered.map((it,idx)=>{
                const rAt  =restockMap[it.country+'|'+it.name]||0;
                const inStock=it.qty>0;
                const isXU =(it.name.toLowerCase().includes('xanax')&&it.country==='United Kingdom');
                const fk   =favKey(it);
                const isFav=favs.has(fk);
                const codeLabel=CODE_LABEL[it.country]||it.country.slice(0,2).toUpperCase();
                const codeColor=CODE_COLOR[it.country]||'#6b7280';
                const flagUrl=FLAG_URL[it.country]||'';
                const flagEmoji=flagUrl?'<img src="'+flagUrl+'" style="width:20px;height:15px;object-fit:cover;border-radius:2px;display:block;" onerror="this.style.display=\'none\'">':'🌍';
 
                // Hintergrund
                let rowBg;
                if(isFav){
                    rowBg='background:linear-gradient(135deg,rgba(251,191,36,.13),rgba(245,158,11,.06));border:1px solid rgba(251,191,36,.3);';
                } else if(isXU){
                    rowBg='background:linear-gradient(135deg,rgba(99,102,241,.16),rgba(139,92,246,.06));border:1px solid rgba(99,102,241,.3);';
                } else if(inStock){
                    rowBg=idx%2===0
                        ?'background:rgba(34,197,94,.06);border:1px solid rgba(34,197,94,.1);'
                        :'background:rgba(34,197,94,.02);border:1px solid rgba(34,197,94,.06);';
                } else {
                    rowBg=idx%2===0
                        ?'background:rgba(255,255,255,.03);border:1px solid rgba(255,255,255,.05);'
                        :'background:rgba(0,0,0,.1);border:1px solid rgba(255,255,255,.02);';
                }
 
                // Restock-Zelle
                let restockCell;
                if(inStock){
                    restockCell='<div style="display:flex;align-items:center;justify-content:flex-end;gap:3px;">'
                        +'<span style="width:6px;height:6px;border-radius:50%;background:#22c55e;box-shadow:0 0 6px #22c55e;flex-shrink:0;"></span>'
                        +'<span style="font-size:10px;color:#22c55e;font-weight:700;">Lager</span>'
                        +'</div>';
                } else if(rAt>0){
                    const sec=Math.max(0,rAt-now);
                    const col=sec<600?'#22c55e':sec<1800?'#f59e0b':'var(--text)';
                    const pulse=sec<600?' class="ti-restock-time soon"':' class="ti-restock-time"';
                    restockCell='<span'+pulse+' data-restock-at="'+rAt+'" '
                        +'style="font-family:var(--mono);font-weight:800;font-size:12px;color:'+col+';letter-spacing:-.3px;">'
                        +(sec>0?Utils.fmtSec(sec):'Jetzt!')+'</span>';
                } else {
                    restockCell='<span style="color:rgba(255,255,255,.2);font-size:11px;">—</span>';
                }
 
                // Preis schön formatiert
                const priceHtml=it.cost>0
                    ?'<span style="font-size:10px;font-weight:700;color:'+(inStock?'#86efac':'rgba(255,255,255,.35)')+';font-family:var(--mono);background:rgba(0,0,0,.2);padding:1px 5px;border-radius:3px;">$'+it.cost.toLocaleString('de-DE')+'</span>'
                    :'';
 
                const favBtn='<button data-fav="'+fk.replace(/"/g,'&quot;')+'" style="'
                    +'background:'+(isFav?'rgba(251,191,36,.2)':'none')+';'
                    +'border:1px solid '+(isFav?'rgba(251,191,36,.5)':'rgba(255,255,255,.1)')+';'
                    +'border-radius:5px;cursor:pointer;padding:3px;width:22px;height:22px;'
                    +'display:flex;align-items:center;justify-content:center;'
                    +'font-size:13px;color:'+(isFav?'#fbbf24':'rgba(255,255,255,.3)')+';'
                    +'transition:all .15s;flex-shrink:0;" '
                    +'title="'+(isFav?'Favorit entfernen':'Als Favorit merken')+'">'
                    +(isFav?'★':'☆')+'</button>';
 
                const searchKey=(it.name+' '+it.country).toLowerCase();
                return '<div data-yata-row="1" data-yata-search="'+searchKey.replace(/"/g,'')+'" '
                    +'style="display:grid;grid-template-columns:26px 32px 1fr auto 88px;'
                    +'align-items:center;gap:7px;padding:7px 8px;border-radius:8px;margin-bottom:3px;'+rowBg+'">'
                    // Fav-Button
                    +favBtn
                    // Land-Badge mit Emoji
                    +'<div style="display:flex;flex-direction:column;align-items:center;gap:1px;flex-shrink:0;">'
                        +flagEmoji
                        +'<span style="font-size:7px;font-weight:800;color:'+codeColor+';letter-spacing:.3px;">'+codeLabel+'</span>'
                    +'</div>'
                    // Name + Land + Preis
                    +'<div style="min-width:0;">'
                        +'<div style="display:flex;align-items:center;gap:5px;flex-wrap:wrap;">'
                            +'<span style="font-size:12px;font-weight:'+(isXU||isFav?'800':'600')+';color:'+(isFav?'#fde68a':isXU?'#c7d2fe':'var(--text)')+';white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:120px;">'+it.name+'</span>'
                            +priceHtml
                        +'</div>'
                        +'<div style="font-size:9px;color:rgba(255,255,255,.35);margin-top:1px;">'
                            +it.country
                            +(it.updateTs?'<span style="margin-left:5px;opacity:.6;">'+fmt(it.updateTs)+'</span>':'')
                        +'</div>'
                    +'</div>'
                    // Bestand
                    +'<div style="text-align:right;flex-shrink:0;">'
                        +'<div style="font-size:13px;font-weight:800;font-family:var(--mono);color:'+(inStock?'#4ade80':'rgba(255,255,255,.25)')+';">'
                            +(inStock?it.qty.toLocaleString('de-DE'):'0')+' <span style="font-size:9px;font-weight:600;opacity:.7;">Stk</span>'
                        +'</div>'
                    +'</div>'
                    // Restock
                    +'<div style="text-align:right;flex-shrink:0;">'+restockCell+'</div>'
                    +'</div>';
            }).join('');
 
            const emptyMsg=filtered.length===0
                ?'<div style="padding:32px;text-align:center;color:rgba(255,255,255,.3);font-size:11px;">Nichts gefunden</div>':'';
 
            // ── Länder-Pills ──
            const favPill='<button data-favonly="1" title="Nur Favoriten" style="display:flex;flex-direction:column;align-items:center;justify-content:center;gap:1px;padding:5px 2px;border-radius:8px;border:1px solid '
                +(favOnly?'#fbbf24':'rgba(255,255,255,.08)')+';background:'
                +(favOnly?'rgba(251,191,36,.2)':'transparent')+';cursor:pointer;transition:all .15s;">'
                +'<span style="font-size:13px;line-height:1;">★</span>'
                +'<span style="font-size:7px;font-weight:800;color:'+(favOnly?'#fbbf24':'rgba(255,255,255,.35)')+';">FAV</span>'
                +'</button>';
            const alarmPill='<button data-alarmtoggle="1" title="Restock-Alarm '+(restockAlarm?'AN':'AUS')+'" style="display:flex;flex-direction:column;align-items:center;justify-content:center;gap:1px;padding:5px 2px;border-radius:8px;border:1px solid '
                +(restockAlarm?'rgba(16,185,129,.4)':'rgba(255,255,255,.08)')+';background:'
                +(restockAlarm?'rgba(16,185,129,.15)':'transparent')+';cursor:pointer;transition:all .15s;">'
                +'<span style="font-size:13px;line-height:1;">🔔</span>'
                +'<span style="font-size:7px;font-weight:800;color:'+(restockAlarm?'var(--green)':'rgba(255,255,255,.35)')+';">'+( restockAlarm?'AN':'AUS')+'</span>'
                +'</button>';
            const allPill='<button data-country="" title="Alle Länder" style="display:flex;flex-direction:column;align-items:center;justify-content:center;gap:1px;padding:5px 2px;border-radius:8px;border:1px solid '
                +(!activeCountry&&!favOnly?'rgba(255,255,255,.4)':'rgba(255,255,255,.1)')+';background:'
                +(!activeCountry&&!favOnly?'rgba(255,255,255,.12)':'transparent')+';cursor:pointer;transition:all .15s;">'
                +'<span style="font-size:15px;line-height:1;">🌍</span>'
                +'<span style="font-size:7px;font-weight:800;color:'+(!activeCountry&&!favOnly?'var(--text)':'rgba(255,255,255,.4)')+';">ALL</span>'
                +'</button>';
 
            const countryPills=countries.map(c=>{
                const active=activeCountry===c;
                const col=CODE_COLOR[c]||'#6b7280';
                const flagPillUrl=FLAG_URL[c]||'';
                const flag=flagPillUrl?'<img src="'+flagPillUrl+'" style="width:20px;height:15px;object-fit:cover;border-radius:2px;display:block;" onerror="this.style.display=\'none\'">':'🌍';
                const lbl=CODE_LABEL[c]||c.slice(0,2);
                return '<button data-country="'+c+'" title="'+c+'" style="display:flex;flex-direction:column;align-items:center;justify-content:center;gap:1px;padding:5px 2px;border-radius:8px;border:1px solid '
                    +(active?col:'rgba(255,255,255,.08)')+';background:'
                    +(active?col+'28':'transparent')+';cursor:pointer;transition:all .15s;">'
                    +'<span style="font-size:15px;line-height:1;">'+flag+'</span>'
                    +'<span style="font-size:7px;font-weight:800;color:'+(active?col:'rgba(255,255,255,.35)')+';">'+lbl+'</span>'
                    +'</button>';
            }).join('');
 
            const searchVal=(s.travelItemsSearch||'').replace(/"/g,'&quot;');
 
            // Stats-Line
            const inStockCount=filtered.filter(i=>i.qty>0).length;
            const emptyCount=filtered.filter(i=>i.qty===0).length;
 
            body.innerHTML=
                // ── Header ──
                '<div style="flex-shrink:0;display:flex;justify-content:space-between;align-items:center;padding:10px 12px 8px;">'
                    +'<div>'
                        +'<div style="font-size:13px;font-weight:800;letter-spacing:.5px;color:var(--text);">YATA LIVE STOCKS</div>'
                        +'<div style="font-size:9px;color:rgba(255,255,255,.35);margin-top:1px;">'
                            +'<span style="color:#4ade80;">&#9679; '+inStockCount+' Lager</span>'
                            +' &nbsp;·&nbsp; '
                            +'<span style="color:rgba(255,255,255,.3);">'+emptyCount+' leer</span>'
                            +(fetchTs?'&nbsp;·&nbsp; Stand '+tsStr:'')
                            +(favOnly?'&nbsp;·&nbsp;<span style="color:#fbbf24;">★ Nur Favoriten</span>':'')
                        +'</div>'
                    +'</div>'
                    +'<button id="yata-refresh-btn" style="display:flex;align-items:center;gap:4px;padding:5px 10px;'
                        +'background:rgba(255,255,255,.08);border:1px solid rgba(255,255,255,.12);border-radius:8px;'
                        +'color:var(--text);font-size:10px;font-weight:700;cursor:pointer;transition:background .15s;">'
                        +'&#8635; Refresh'
                    +'</button>'
                +'</div>'
                // ── Länder-Pills (2-zeiliges Emoji-Grid) ──
                +'<div id="yata-country-pills" style="flex-shrink:0;display:grid;grid-template-columns:repeat(auto-fill,minmax(38px,1fr));gap:4px;padding:0 10px 8px;">'
                    +favPill+alarmPill+allPill+countryPills
                +'</div>'
                // ── Suchfeld ──
                +'<div style="flex-shrink:0;padding:0 10px 8px;">'
                    +'<div style="position:relative;">'
                        +'<span style="position:absolute;left:10px;top:50%;transform:translateY(-50%);font-size:12px;opacity:.4;">&#128269;</span>'
                        +'<input id="yata-search" type="text" placeholder="Item oder Land suchen..." value="'+searchVal+'" autocomplete="off" '
                            +'style="width:100%;box-sizing:border-box;padding:8px 12px 8px 30px;'
                            +'background:rgba(255,255,255,.06);border:1px solid rgba(255,255,255,.1);'
                            +'border-radius:8px;color:var(--text);font-size:11px;outline:none;min-width:0;">'
                    +'</div>'
                +'</div>'
                // ── Spalten-Header ──
                +'<div style="flex-shrink:0;display:grid;grid-template-columns:26px 32px 1fr auto 88px;gap:7px;'
                    +'padding:2px 8px 5px;font-size:8px;color:rgba(255,255,255,.3);text-transform:uppercase;letter-spacing:.8px;font-weight:800;border-bottom:1px solid rgba(255,255,255,.06);margin-bottom:4px;">'
                    +'<span></span><span></span><span>Item / Land</span>'
                    +'<span style="text-align:right;">Bestand</span>'
                    +'<span style="text-align:right;">Restock</span>'
                +'</div>'
                // ── Liste — kein eigener Scroll, thud-body scrollt ──
                +'<div id="yata-list" style="padding:0 10px 8px;">'+rows+emptyMsg+'</div>';
 
            // ── Events ──
            document.getElementById('yata-refresh-btn')?.addEventListener('click',()=>{
                s.travelItemsProfits=null; s.travelItemsRestocks=null; s.yataRawStocks={};
                hud.fetcher.fetchTravelItems(()=>this.renderYataLive());
                this.renderYataLive();
            });
 
            // Länder-Pills
            document.getElementById('yata-country-pills')?.querySelectorAll('[data-country]').forEach(btn=>{
                btn.addEventListener('click',()=>{
                    s.yataCountryFilter=btn.dataset.country||'';
                    if(btn.dataset.country!==undefined)s.setYataFavOnly(false); // Länderwahl hebt favOnly auf
                    this.renderYataLive();
                });
            });
            // Fav-Only-Pill
            document.getElementById('yata-country-pills')?.querySelector('[data-favonly]')?.addEventListener('click',()=>{
                s.setYataFavOnly(!s.yataFavOnly);
                this.renderYataLive();
            });
            // Alarm-Toggle-Pill
            document.getElementById('yata-country-pills')?.querySelector('[data-alarmtoggle]')?.addEventListener('click',()=>{
                s.setYataRestockAlarm(!s.yataRestockAlarm);
                this.renderYataLive();
            });
 
            // Suche — nur DOM-Filter, kein re-render
            const si=document.getElementById('yata-search');
            const list=document.getElementById('yata-list');
            if(si&&list){
                si.addEventListener('input',()=>{
                    s.travelItemsSearch=si.value;
                    const q=si.value.toLowerCase().trim();
                    list.querySelectorAll('[data-yata-row]').forEach(row=>{
                        row.style.display=(!q||row.dataset.yataSearch.includes(q))?'':'none';
                    });
                });
                if(s.travelItemsSearch){si.focus();si.setSelectionRange(si.value.length,si.value.length);}
            }
 
            // Fav-Buttons — kein re-render, nur DOM-Update
            list?.querySelectorAll('[data-fav]').forEach(btn=>{
                btn.addEventListener('click',()=>{
                    const fk2=btn.dataset.fav;
                    if(favs.has(fk2)){favs.delete(fk2);btn.textContent='☆';btn.style.opacity='.25';}
                    else{favs.add(fk2);btn.textContent='★';btn.style.opacity='1';}
                    saveFavs();
                    // Zeile visuell updaten (kein full re-render)
                    const row=btn.closest('[data-yata-row]');
                    if(row){
                        if(favs.has(fk2)){
                            row.style.background='linear-gradient(135deg,rgba(251,191,36,.13),rgba(245,158,11,.06))';
                            row.style.border='1px solid rgba(251,191,36,.3)';
                        } else {
                            row.style.background='';row.style.border='';
                        }
                    }
                });
            });
 
            // Countdown — nur Timer-Texte updaten, kein Full-Re-Render (kein Flackern)
            // Anti-Spam: alarmFired persistent im GM_Storage damit Rerender es nicht löscht
            if(this._tiCountdownIv)clearInterval(this._tiCountdownIv);
            let _lastRefetch=0; // Throttle: max 1x alle 5 Minuten
            // Persistente fired-Map laden
            let _alarmFiredMap={};
            try{_alarmFiredMap=JSON.parse(GM_getValue('yataAlarmFired','{}'));}catch(e){}
            // Alte Einträge aufräumen (älter als 4h)
            const _nowClean=Math.floor(Date.now()/1000);
            for(const k of Object.keys(_alarmFiredMap)){
                if(_nowClean-_alarmFiredMap[k]>14400)delete _alarmFiredMap[k];
            }
            this._tiCountdownIv=setInterval(()=>{
                if(hud.activeTab!=='yata_live'){clearInterval(this._tiCountdownIv);this._tiCountdownIv=null;return;}
                const n2=Math.floor(Date.now()/1000);
                let needsRefetch=false;
                document.querySelectorAll('[data-restock-at]').forEach(el=>{
                    const at=Number(el.dataset.restockAt||0); if(!at)return;
                    const sec=Math.max(0,at-n2);
                    const newTxt=sec>0?Utils.fmtSec(sec):'Jetzt!';
                    if(el.textContent!==newTxt)el.textContent=newTxt;
                    // Restock erreicht: Alarm + Refetch, aber NUR EINMAL (persistent)
                    if(sec<=0){
                        const row=el.closest('[data-yata-row]');
                        const favBtn=row?.querySelector('[data-fav]');
                        const itemKey=favBtn?.dataset?.fav||el.dataset.restockAt;
                        // Alarm nur wenn: AN + Favorit + noch nicht gefeuert in letzten 4h
                        if(!_alarmFiredMap[itemKey]){
                            _alarmFiredMap[itemKey]=n2;
                            try{GM_setValue('yataAlarmFired',JSON.stringify(_alarmFiredMap));}catch(e){}
                            const isFavItem=favBtn&&favs.has(favBtn.dataset.fav);
                            if(restockAlarm&&isFavItem){
                                hud.audio.play('notify');
                                const rowSearch=row?.dataset?.yataSearch||'';
                                hud.toasts.show('🌍','RESTOCK JETZT!',rowSearch,'info',20000);
                            }
                            // Refetch anfragen, aber throttled
                            needsRefetch=true;
                        }
                    }
                });
                // Refetch max 1x alle 5 Minuten um Spam zu verhindern
                if(needsRefetch&&(n2-_lastRefetch)>300){
                    _lastRefetch=n2;
                    hud.fetcher.fetchTravelItems(()=>{
                        if(hud.activeTab==='yata_live')this.renderYataLive();
                    });
                }
            },1000);
        };
 
 
        renderWar() {
            const body=this.body; if(!body)return;
            const s=this.state;
            if(!hud.store.get('apiKey','')){body.innerHTML=this._noKeyState();return;}
            const now=Utils.now();
            const war=s.activeWar;
            const pending=s.pendingWar;

            // ── KEIN WAR ────────────────────────────────────────────
            if(!war&&!pending){
                body.innerHTML=`
                <div class="empty-state"><div class="es-icon">⚔</div><div class="es-text">Kein aktiver Krieg</div><div class="es-sub">Deine Faction ist gerade in keinem War</div></div>
                <button class="scan-btn" style="align-self:center;" id="thud-war-refresh">↺ Prüfen</button>`;
                document.getElementById('thud-war-refresh')?.addEventListener('click',()=>hud.fetcher.fetchWar(()=>this.renderWar()));
                return;
            }

            const ffKey=hud.store.get('ffKey','');
            const warData=war||pending;
            const factions=warData.factions||{};
            const ourFacId=Number(s.faction?.ID??s.faction?.id??s._knownFacId??0);
            let ourFac=null,themFac=null,ourId=0,themId=0;
            for(const[fid,fd] of Object.entries(factions)){
                if(Number(fid)===ourFacId){ourFac=fd;ourId=Number(fid);}
                else{themFac=fd;themId=Number(fid);}
            }
            const ourName=ourFac?.name??s.faction?.name??'Wir';
            const themName=themFac?.name??'Gegner';

            // ── VORBEREITUNGSPHASE ───────────────────────────────────
            if(!war&&pending){
                const startTs=Number(pending.start??pending.war_start??0);
                const timeToStart=startTs>0?Math.max(0,startTs-now):0;
                const enemyMems=Object.values(s.enemyFactionMembers||{});
                enemyMems.sort((a,b)=>Number(b.level??0)-Number(a.level??0));

                const _statusTag=(m)=>{
                    const st=(typeof m.status==='object'?m.status?.state:m.status||'').toLowerCase();
                    const la=(m.last_action?.status||'').toLowerCase();
                    const ago=now-(m.last_action?.timestamp||0);
                    if(st.includes('hospital'))return'<span class="enemy-tag etag-hosp">🏥</span>';
                    if(st.includes('travel'))return'<span class="enemy-tag etag-travel">✈</span>';
                    if(st.includes('jail'))return'<span class="enemy-tag etag-jail">🚔</span>';
                    if(la==='online'||ago<120)return'<span class="badge badge-green" style="font-size:8px;">Online</span>';
                    if(la==='idle'||ago<600)return'<span style="font-size:9px;color:var(--amber);">AFK</span>';
                    return'<span style="font-size:9px;color:var(--muted);">'+Utils.inactiveAgo(m.last_action?.timestamp||0).text+' ago</span>';
                };

                body.innerHTML=`
                <div style="background:linear-gradient(135deg,rgba(99,102,241,.15),rgba(99,102,241,.03));border:1px solid rgba(99,102,241,.4);border-radius:var(--r);padding:16px 18px;flex-shrink:0;animation:warPulse 2s ease-in-out infinite;">
                    <div style="display:flex;align-items:center;gap:10px;margin-bottom:12px;">
                        <span style="font-size:22px;">⚔</span>
                        <div style="flex:1;">
                            <div style="font-size:12px;font-weight:800;letter-spacing:1px;text-transform:uppercase;color:var(--accent2);">KRIEG BEGINNT IN</div>
                            <div style="font-size:10px;color:var(--muted);margin-top:2px;">${Utils.escHtml(ourName)} vs. ${Utils.escHtml(themName)}</div>
                        </div>
                        <button class="scan-btn" id="thud-war-ref" style="padding:4px 8px;font-size:10px;">↺</button>
                    </div>
                    <div style="font-size:36px;font-weight:800;font-family:var(--mono);color:var(--accent2);letter-spacing:-1px;" id="war-prep-timer">${timeToStart>0?Utils.fmtSec(timeToStart):'STARTET JETZT'}</div>
                    ${startTs>0?`<div style="font-size:10px;color:var(--muted);margin-top:4px;">Start: ${new Date(startTs*1000).toLocaleString('de-DE',{day:'2-digit',month:'2-digit',hour:'2-digit',minute:'2-digit'})}</div>`:''}
                </div>

                <div class="card" style="flex-shrink:0;">
                    <div class="card-header">
                        <span class="card-title accent">🏛 GEGNER FACTION</span>
                        <span class="badge badge-blue">${Utils.escHtml(themName)}</span>
                    </div>
                    <div class="card-body">
                        <div class="stat-grid stat-grid-3">
                            <div class="stat-card"><div class="sv sv-amber">${enemyMems.length}</div><div class="sl">Mitglieder</div></div>
                            <div class="stat-card"><div class="sv sv-green">${enemyMems.filter(m=>{const la=(m.last_action?.status||'').toLowerCase();const ago=now-(m.last_action?.timestamp||0);return la==='online'||ago<120;}).length}</div><div class="sl">Online</div></div>
                            <div class="stat-card"><div class="sv sv-red">${enemyMems.filter(m=>(typeof m.status==='object'?m.status?.state:m.status||'').toLowerCase().includes('hospital')).length}</div><div class="sl">Hospital</div></div>
                        </div>
                    </div>
                </div>

                <div class="card" style="flex-shrink:0;">
                    <div class="card-header">
                        <span class="card-title red">💀 ALLE GEGNER (${enemyMems.length})</span>
                        <span style="font-size:9px;color:var(--muted);">sortiert nach Level</span>
                    </div>
                    <div class="card-body p0">
                    ${enemyMems.length===0?'<div class="empty-state"><div class="es-icon">⏳</div><div class="es-text">Lade Gegner-Daten...</div></div>':
                    enemyMems.map(m=>{
                        const lvl=m.level??'?';
                        const ffEntry=s.ffData[Number(m.id)];
                        const ffHtml=`<span data-ff-badge="${m.id}">${ffBadge(ffEntry)}</span>`;
                        const facInfo=m.faction?.faction_name??m.faction?.name??null;
                        const statusHtml=_statusTag(m);
                        const lastTs=m.last_action?.timestamp||0;
                        const lastAgo=Utils.inactiveAgo(lastTs);
                        return`<div class="war-target-row">
                            <div style="flex:1;min-width:0;">
                                <div style="display:flex;align-items:center;gap:6px;flex-wrap:wrap;">
                                    <a href="https://www.torn.com/profiles.php?XID=${m.id}" target="_blank" style="color:var(--text);font-weight:700;font-size:12px;text-decoration:none;">${Utils.escHtml(m.name??'#'+m.id)}</a>
                                    <span style="font-size:10px;font-family:var(--mono);color:var(--amber);">Lv ${lvl}</span>
                                    ${ffHtml}
                                    ${statusHtml}
                                </div>
                                ${facInfo?`<div style="font-size:9px;color:var(--muted);margin-top:2px;">⚔ ${Utils.escHtml(facInfo)}</div>`:''}
                            </div>
                            <div style="display:flex;flex-direction:column;gap:3px;align-items:flex-end;flex-shrink:0;">
                                <a class="enemy-action-btn eab-profile" href="https://www.torn.com/profiles.php?XID=${m.id}" target="_blank" style="font-size:10px;">👤 Profil</a>
                                <a class="enemy-action-btn eab-attack"  href="https://www.torn.com/page.php?sid=attack&user2ID=${m.id}" target="_blank" style="font-size:10px;">⚔ Angriff</a>
                            </div>
                        </div>`;
                    }).join('')}
                    </div>
                </div>
                <div class="ts" style="text-align:center;flex-shrink:0;">Stand: ${s.warLastFetch?new Date(s.warLastFetch*1000).toLocaleTimeString('de-DE'):'—'}</div>`;

                document.getElementById('thud-war-ref')?.addEventListener('click',()=>hud.fetcher.fetchWar(()=>this.renderWar()));

                // Live-Countdown
                if(startTs>0&&timeToStart>0){
                    if(this._warPrepIv)clearInterval(this._warPrepIv);
                    this._warPrepIv=setInterval(()=>{
                        if(hud.activeTab!=='war'){clearInterval(this._warPrepIv);return;}
                        const el=document.getElementById('war-prep-timer');
                        if(!el){clearInterval(this._warPrepIv);return;}
                        const rem=Math.max(0,startTs-Utils.now());
                        el.textContent=rem>0?Utils.fmtSec(rem):'STARTET JETZT!';
                        if(rem===0){clearInterval(this._warPrepIv);setTimeout(()=>hud.fetcher.fetchWar(()=>this.renderWar()),2000);}
                    },1000);
                }

                // FF für alle Gegner laden
                if(ffKey&&enemyMems.length){
                    const ids=enemyMems.map(m=>Number(m.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]);});
                    });
                }
                return;
            }

            // ── AKTIVER WAR ──────────────────────────────────────────
            const ourScore=Number(ourFac?.score??0);
            const themScore=Number(themFac?.score??0);
            const total=ourScore+themScore||1;
            const ourPct=Math.round((ourScore/total)*100);
            const themPct=100-ourPct;
            const lead=ourScore-themScore;
            const leadStr=lead>=0?`+${lead.toLocaleString('de-DE')} Punkte Vorsprung`:`${lead.toLocaleString('de-DE')} Punkte Rückstand`;
            const leadColor=lead>=0?'var(--green)':'var(--red)';
            const warEnd=Number(war.end??war.war_end??0);
            const timeLeft=warEnd>0?Math.max(0,warEnd-now):0;

            // Enemy chain info
            const enemyChain=war.factions?.[themId]?.chain??0;
            const chainMilestones=[10,25,50,100,250,500,1000];
            const nextMile=chainMilestones.find(m=>m>enemyChain)||enemyChain;
            const chainWarn=chainMilestones.some(m=>enemyChain>=m-10&&enemyChain<m);

            // Enemy online targets
            const enemyMems2=Object.values(s.enemyFactionMembers||{});
            const targets=enemyMems2.filter(m=>{
                const st=(typeof m.status==='object'?m.status?.state:m.status||'').toLowerCase();
                return !st.includes('hospital')&&!st.includes('jail')&&!st.includes('travel');
            }).sort((a,b)=>{
                const aO=(a.last_action?.status||'').toLowerCase()==='online'||(now-(a.last_action?.timestamp||0))<120?0:1;
                const bO=(b.last_action?.status||'').toLowerCase()==='online'||(now-(b.last_action?.timestamp||0))<120?0:1;
                return aO-bO;
            }).slice(0,10);

            body.innerHTML=`
            <div class="war-header">
                <div style="display:flex;align-items:center;gap:10px;margin-bottom:8px;">
                    <span style="font-size:18px;animation:pipPulse 1s ease-in-out infinite;">🔥</span>
                    <div style="flex:1;">
                        <div style="font-size:11px;font-weight:800;letter-spacing:1px;text-transform:uppercase;color:var(--red);">KRIEG AKTIV</div>
                        <div style="font-size:10px;color:var(--muted);margin-top:1px;">${Utils.escHtml(ourName)} vs. ${Utils.escHtml(themName)}${timeLeft>0?' · noch '+Utils.fmtHM(timeLeft):''}</div>
                    </div>
                    <button class="scan-btn" id="thud-war-ref" style="padding:4px 8px;font-size:10px;">↺</button>
                </div>
                <div class="war-score-bar">
                    <div class="war-score-fill-us" style="width:${ourPct}%;"><span style="font-size:9px;font-weight:800;color:#fff;opacity:.9;">${ourScore>0?ourScore.toLocaleString('de-DE'):''}</span></div>
                    <div class="war-score-fill-them" style="width:${themPct}%;"><span style="font-size:9px;font-weight:800;color:#fff;opacity:.9;">${themScore>0?themScore.toLocaleString('de-DE'):''}</span></div>
                </div>
                <div style="display:flex;justify-content:space-between;font-size:9px;color:var(--muted);margin-top:2px;">
                    <span style="color:var(--green);font-weight:700;">${Utils.escHtml(ourName)}: ${ourScore}</span>
                    <span style="color:${leadColor};font-weight:800;">${leadStr}</span>
                    <span style="color:var(--red);font-weight:700;">${Utils.escHtml(themName)}: ${themScore}</span>
                </div>
            </div>

            ${chainWarn?`<div class="war-cb-warn"><span style="font-size:22px;">⛓</span><div><div style="font-size:12px;font-weight:800;color:var(--red);">CHAIN BREAKER!</div><div style="font-size:11px;color:var(--dim);margin-top:2px;">${Utils.escHtml(themName)} steht bei <b>${enemyChain}</b> Hits — kurz vor ${nextMile}! Jetzt angreifen!</div></div></div>`:''}

            ${enemyChain>0&&!chainWarn?`<div class="card" style="flex-shrink:0;"><div class="card-header"><span class="card-title red">⛓ ENEMY CHAIN</span><span class="badge badge-${enemyChain>=100?'red':'amber'}">${enemyChain} Hits</span></div><div class="card-body" style="padding:10px 14px;"><div style="display:flex;gap:12px;flex-wrap:wrap;align-items:center;"><span style="font-size:11px;color:var(--dim);">Nächster Milestone: <b style="color:var(--amber);">${nextMile} Hits</b></span><span style="font-size:10px;color:var(--muted);">Noch ${nextMile-enemyChain} Hits bis Bonus</span></div></div></div>`:''}

            <div class="card" style="flex-shrink:0;">
                <div class="card-header"><span class="card-title red">🎯 TOP TARGETS (Online / Verfügbar)</span><span class="badge badge-red">${targets.length}</span></div>
                <div class="card-body p0" id="war-targets">
                    ${targets.length===0?'<div class="empty-state"><div class="es-icon">😮</div><div class="es-text">Alle im Hospital oder Offline</div></div>':
                    targets.map(m=>{
                        const isOnline=(m.last_action?.status||'').toLowerCase()==='online'||(now-(m.last_action?.timestamp||0))<120;
                        const lvl=m.level??'?';
                        const ffEntry=s.ffData[Number(m.id)];
                        const ffHtml=`<span data-ff-badge="${m.id}">${ffBadge(ffEntry)}</span>`;
                        const lastAgo=Utils.inactiveAgo(m.last_action?.timestamp||0);
                        return`<div class="war-target-row ${isOnline?'online-now':''}">
                            <span class="status-dot ${isOnline?'sd-online':'sd-idle'}" style="flex-shrink:0;"></span>
                            <div style="flex:1;min-width:0;">
                                <div style="display:flex;align-items:center;gap:6px;flex-wrap:wrap;">
                                    <a href="https://www.torn.com/profiles.php?XID=${m.id}" target="_blank" style="color:var(--text);font-weight:700;font-size:12px;text-decoration:none;">${Utils.escHtml(m.name??'#'+m.id)}</a>
                                    <span style="font-size:10px;font-family:var(--mono);color:var(--amber);">Lv ${lvl}</span>
                                    ${ffHtml}
                                    ${isOnline?'<span class="badge badge-green" style="font-size:8px;">Online</span>':'<span style="font-size:9px;color:var(--muted);">'+lastAgo.text+' ago</span>'}
                                </div>
                            </div>
                            <a class="enemy-action-btn eab-attack" href="https://www.torn.com/page.php?sid=attack&user2ID=${m.id}" target="_blank" style="white-space:nowrap;">⚔ Angriff</a>
                        </div>`;
                    }).join('')}
                </div>
            </div>
            <div class="ts" style="text-align:center;flex-shrink:0;">Stand: ${s.warLastFetch?new Date(s.warLastFetch*1000).toLocaleTimeString('de-DE'):'—'}</div>`;

            document.getElementById('thud-war-ref')?.addEventListener('click',()=>hud.fetcher.fetchWar(()=>this.renderWar()));

            // Load FF for targets
            if(ffKey&&targets.length){
                const ids=targets.map(m=>Number(m.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]);});
                });
            }
        }

        renderSearch() {
            const body=this.body; if(!body)return;
            const s=this.state;
            if(!hud.store.get('apiKey','')){body.innerHTML=this._noKeyState();return;}

            const ffKey=hud.store.get('ffKey','');
            const results=s.searchResults||[];
            const q=s.searchQuery||'';
            const loading=s.searchLoading;

            // Sort & filter results
            const lvMin=Number(s.searchLvMin)||1, lvMax=Number(s.searchLvMax)||100;
            const ffMin=Number(s.searchFFMin)||0, ffMax=Number(s.searchFFMax)||0;
            const statusFilter=s.searchFilterStatus||'all';
            const sortField=s.searchSortField||'name';
            const sortDir=s.searchSortDir||'asc';

            let filtered=results.slice();
            filtered=filtered.filter(p=>{const l=Number(p.level??0);return l>=lvMin&&l<=lvMax;});
            if(statusFilter!=='all'){
                filtered=filtered.filter(p=>{
                    const st=(p.status?.state||p.status||'').toLowerCase();
                    if(statusFilter==='online')return(p.last_action?.status||'').toLowerCase()==='online'||(Utils.now()-(p.last_action?.timestamp||0))<120;
                    if(statusFilter==='hospital')return st.includes('hospital');
                    if(statusFilter==='traveling')return st.includes('travel')||st.includes('abroad');
                    return true;
                });
            }
            if(ffMin>0||ffMax>0){
                filtered=filtered.filter(p=>{
                    const ff=s.ffData[Number(p.id)]?.ff??null;
                    if(ff===null)return true;
                    if(ffMin>0&&ff<ffMin)return false;
                    if(ffMax>0&&ff>ffMax)return false;
                    return true;
                });
            }
            filtered.sort((a,b)=>{
                let va,vb;
                if(sortField==='name'){va=(a.name||'').toLowerCase();vb=(b.name||'').toLowerCase();return sortDir==='asc'?va.localeCompare(vb):vb.localeCompare(va);}
                if(sortField==='level'){va=Number(a.level??0);vb=Number(b.level??0);}
                if(sortField==='age'){va=Number(a.age??0);vb=Number(b.age??0);}
                if(sortField==='ff'){va=s.ffData[Number(a.id)]?.ff??-1;vb=s.ffData[Number(b.id)]?.ff??-1;}
                return sortDir==='asc'?va-vb:vb-va;
            });

            const searchHadFocus=document.activeElement?.id==='thud-psearch-input';
            const cursorPos=searchHadFocus?(document.activeElement.selectionStart??0):0;

            body.innerHTML=`
            <div class="card" style="flex-shrink:0;">
                <div class="card-header"><span class="card-title accent">🔍 SPIELER SUCHE</span>${results.length>0?`<span class="badge badge-blue">${filtered.length}/${results.length}</span>`:''}</div>
                <div class="card-body" style="padding:9px 14px 10px;gap:7px;">
                    <div style="display:flex;gap:6px;align-items:center;">
                        <input type="text" id="thud-psearch-input" class="bounty-search" placeholder="🔍 Spielername eingeben..." value="${Utils.escHtml(q)}" style="flex:1;">
                        <button class="scan-btn" id="thud-psearch-btn" ${loading?'disabled':''} style="white-space:nowrap;">${loading?'<span class="spinning">↺</span>':'🔍 Suchen'}</button>
                    </div>
                    <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-ps-lv-min" min="1" max="100" value="${lvMin}">
                        <span style="color:var(--muted);font-size:10px;">–</span>
                        <input type="number" class="level-input" id="thud-ps-lv-max" min="1" max="100" value="${lvMax}">
                        <button class="sort-btn" id="thud-ps-lv-apply">✓</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-ps-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-ps-ff-max" placeholder="Max" value="${ffMax>0?ffMax:''}">
                        <button class="sort-btn" id="thud-ps-ff-apply" style="border-color:rgba(99,102,241,.3);color:var(--accent2);">✓</button>
                        ${(ffMin>0||ffMax>0)?`<button class="sort-btn" id="thud-ps-ff-clear" style="color:var(--muted);">✕</button>`:''}
                    </div>
                    <div class="sort-bar">
                        <span style="font-size:9px;color:var(--muted);font-weight:700;">STATUS:</span>
                        ${['all','online','hospital','traveling'].map(f=>`<button class="sort-btn ${statusFilter===f?'active':''}" data-ps-status="${f}">${{all:'Alle',online:'🟢 Online',hospital:'🏥 Hosp.',traveling:'✈ Travel'}[f]}</button>`).join('')}
                    </div>
                    <div class="sort-bar">
                        <span style="font-size:9px;color:var(--muted);font-weight:700;">SORT:</span>
                        ${['name','level','age','ff'].map(f=>`<button class="sort-btn ${sortField===f?'active':''}" data-ps-sort="${f}">${{name:'Name',level:'⚔ Level',age:'📅 Age',ff:'📊 FF'}[f]}</button>`).join('')}
                        <button class="sort-btn" id="thud-ps-dir">${sortDir==='asc'?'▲':'▼'}</button>
                    </div>
                </div>
            </div>
            ${loading?`<div class="loading-state">SUCHE LÄUFT…</div>`:''}
            ${!loading&&results.length>0?`<div class="card" style="flex-shrink:0;"><div class="card-body p0" id="thud-ps-results">
                ${filtered.length===0?`<div class="empty-state"><div class="es-icon">🔍</div><div class="es-text">Keine Treffer</div><div class="es-sub">Filter anpassen</div></div>`:
                filtered.slice(0,50).map(p=>{
                    const now=Utils.now();
                    const level=p.level??'?';
                    const statusObj=p.status||{};
                    const ss=(typeof statusObj==='string'?statusObj:statusObj.state||'Okay').toLowerCase();
                    const isH=ss.includes('hospital');
                    const isTr=ss.includes('travel')||ss.includes('abroad');
                    const isOnline=(p.last_action?.status||'').toLowerCase()==='online'||(now-(p.last_action?.timestamp||0))<120;
                    const ageDays=Number(p.age??0);
                    const facName=p.faction?.faction_name??p.faction?.name??null;
                    const ffEntry=s.ffData[Number(p.id)];
                    let stDot='sd-offline',stTag='';
                    if(isH){stDot='sd-hospital';stTag=`<span class="bounty-tag tag-hosp">🏥</span>`;}
                    else if(isTr){stDot='sd-traveling';stTag=`<span class="bounty-tag tag-travel">✈</span>`;}
                    else if(isOnline){stDot='sd-online';}
                    else stDot='sd-idle';
                    const lastAgo=Utils.inactiveAgo(p.last_action?.timestamp||0);
                    return`<div class="search-result-row">
                        <span class="status-dot ${stDot}" style="flex-shrink:0;"></span>
                        <div style="flex:1;min-width:0;">
                            <div style="display:flex;align-items:center;gap:6px;flex-wrap:wrap;">
                                <a href="https://www.torn.com/profiles.php?XID=${p.id}" target="_blank" style="font-size:12px;font-weight:700;color:var(--text);text-decoration:none;">${Utils.escHtml(p.name??'#'+p.id)}</a>
                                <span style="font-size:10px;font-family:var(--mono);color:var(--amber);">Lv ${level}</span>
                                ${ageDays>0?`<span style="font-size:9px;color:var(--green);font-family:var(--mono);">${ageDays}d</span>`:''}
                                <span data-ff-badge="${p.id}">${ffBadge(ffEntry)}</span>
                                ${stTag}
                            </div>
                            <div style="display:flex;gap:8px;margin-top:2px;flex-wrap:wrap;">
                                ${facName?`<span style="font-size:9px;color:var(--muted);">⚔ ${Utils.escHtml(facName)}</span>`:''}
                                <span style="font-size:9px;color:var(--muted);">${isOnline?'<span style="color:var(--green);">Online</span>':lastAgo.text+' ago'}</span>
                            </div>
                        </div>
                        <div style="display:flex;flex-direction:column;gap:4px;flex-shrink:0;">
                            <a class="enemy-action-btn eab-attack" href="https://www.torn.com/page.php?sid=attack&user2ID=${p.id}" target="_blank">⚔ Angriff</a>
                            <button class="enemy-action-btn eab-grp" data-search-add-enemy="${p.id}" data-search-name="${Utils.escHtml(p.name??'')}">💀 Enemy</button>
                        </div>
                    </div>`;
                }).join('')}
            </div></div>`:''}
            ${!loading&&results.length===0&&q?`<div class="empty-state"><div class="es-icon">🔍</div><div class="es-text">Keine Ergebnisse</div><div class="es-sub">Prüfe den Namen und versuche es erneut</div></div>`:''}
            ${!loading&&!q?`<div class="empty-state"><div class="es-icon">🔍</div><div class="es-text">Spieler suchen</div><div class="es-sub">Namen eingeben und Enter drücken</div></div>`:''}
            `;

            if(searchHadFocus){const inp=document.getElementById('thud-psearch-input');if(inp){inp.focus();try{inp.setSelectionRange(cursorPos,cursorPos);}catch(e){}}}

            const doSearch=()=>{
                const qv=document.getElementById('thud-psearch-input')?.value?.trim()||'';
                if(!qv)return;
                s.searchQuery=qv;
                s.searchLoading=true;
                s.searchResults=[];
                this.renderSearch();
                hud.fetcher.fetchPlayerSearch(qv,(results)=>{
                    s.searchLoading=false;
                    s.searchResults=results||[];
                    this.renderSearch();
                    // Fetch FF for results
                    if(ffKey&&results.length){
                        const ids=results.map(p=>Number(p.id)).filter(Boolean);
                        getFFData(ids,ffKey,(ffR)=>{
                            s.ffData={...s.ffData,...ffR};
                            ids.forEach(id=>{const el=document.querySelector(`[data-ff-badge="${id}"]`);if(el)el.innerHTML=ffBadge(ffR[id]);});
                        });
                    }
                });
            };

            document.getElementById('thud-psearch-btn')?.addEventListener('click',doSearch);
            document.getElementById('thud-psearch-input')?.addEventListener('keydown',e=>{if(e.key==='Enter')doSearch();});
            document.getElementById('thud-ps-lv-apply')?.addEventListener('click',()=>{
                s.searchLvMin=Number(document.getElementById('thud-ps-lv-min')?.value)||1;
                s.searchLvMax=Number(document.getElementById('thud-ps-lv-max')?.value)||100;
                hud.store.set('searchLvMin',s.searchLvMin);hud.store.set('searchLvMax',s.searchLvMax);
                this.renderSearch();
            });
            document.getElementById('thud-ps-ff-apply')?.addEventListener('click',()=>{
                s.searchFFMin=parseFloat(document.getElementById('thud-ps-ff-min')?.value)||0;
                s.searchFFMax=parseFloat(document.getElementById('thud-ps-ff-max')?.value)||0;
                hud.store.set('searchFFMin',s.searchFFMin);hud.store.set('searchFFMax',s.searchFFMax);
                this.renderSearch();
            });
            document.getElementById('thud-ps-ff-clear')?.addEventListener('click',()=>{s.searchFFMin=0;s.searchFFMax=0;hud.store.set('searchFFMin',0);hud.store.set('searchFFMax',0);this.renderSearch();});
            document.getElementById('thud-ps-dir')?.addEventListener('click',()=>{s.searchSortDir=s.searchSortDir==='asc'?'desc':'asc';hud.store.set('searchSortDir',s.searchSortDir);this.renderSearch();});
            body.querySelectorAll('[data-ps-status]').forEach(btn=>btn.addEventListener('click',()=>{s.searchFilterStatus=btn.dataset.psStatus;hud.store.set('searchFilterStatus',s.searchFilterStatus);this.renderSearch();}));
            body.querySelectorAll('[data-ps-sort]').forEach(btn=>btn.addEventListener('click',()=>{s.searchSortField=btn.dataset.psSort;hud.store.set('searchSortField',s.searchSortField);this.renderSearch();}));
            body.querySelectorAll('[data-search-add-enemy]').forEach(btn=>btn.addEventListener('click',()=>{
                const id=btn.dataset.searchAddEnemy,name=btn.dataset.searchName||`#${id}`;
                if(!s.enemies.find(e=>String(e.id)===String(id))){s.addEnemy(String(id),name,'','default');hud.toasts.show('💀','Enemy hinzugefügt',name,'crit',3000);hud.fetcher.fetchEnemyData();}
                else hud.toasts.show('⚠','Bereits in Enemy-Liste',name,'warn',2000);
            }));
        }

        _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(()=>hud.fetcher.fetchWar(),     REFRESH.WAR);
            setInterval(()=>this._tick(),               REFRESH.TICK);
            // YATA Hintergrund-Tracking: Favoriten + Restocks auch ohne offenen Tab
            setInterval(()=>{
                hud.fetcher.fetchTravelItems(()=>{
                    // Nur re-render wenn Tab gerade offen
                    if(hud.activeTab==='yata_live')hud.renderer.renderYataLive();
                });
            },REFRESH.TRAVEL_ITEMS);
        },
        _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(); }

    // ═══════════════════════════════════════════════════════════════
    // PROFILE PAGE FF INJECTION
    // ═══════════════════════════════════════════════════════════════
    function injectProfileFF() {
        const m=window.location.href.match(/profiles\.php\?XID=(\d+)/);
        if(!m)return;
        const playerId=Number(m[1]);
        if(!playerId)return;
        const ffKey=hud.store.get('ffKey','');
        if(!ffKey)return;
        const st=document.createElement('style');
        st.textContent='.ff-badge{display:inline-flex;align-items:center;justify-content:center;font-size:10px;font-weight:800;padding:3px 9px;border-radius:20px;border:1px solid transparent;letter-spacing:.5px;font-family:monospace;cursor:help;white-space:nowrap;line-height:1.4;box-shadow:0 1px 6px rgba(0,0,0,.5);}.ff-badge-unknown,.ff-badge-none{background:rgba(255,255,255,.1);color:#94a3b8;border-color:rgba(255,255,255,.15);}';
        document.head.appendChild(st);
        const tryInject=()=>{
            const nameEl=document.querySelector('.profile-name-wrap h2, .profile-name, [class*="profileName"], .basic-information h4');
            if(!nameEl){setTimeout(tryInject,900);return;}
            if(document.getElementById('shaya-ff-inject'))return;
            getFFData([playerId],ffKey,(ffResults)=>{
                const entry=ffResults[playerId];
                if(!entry)return;
                const wrap=document.createElement('span');
                wrap.id='shaya-ff-inject';
                wrap.style.cssText='display:inline-flex;align-items:center;gap:6px;margin-left:10px;vertical-align:middle;';
                wrap.innerHTML=ffBadge(entry);
                nameEl.appendChild(wrap);
            });
        };
        setTimeout(tryInject,600);
    }
    injectProfileFF();

})();