SHAYA UI

All-in-One Faction HUD – v25.0: Age in Tagen, Enemy Bounty, Bank raus, Bounty-Filter bereinigt, Code aufgeräumt

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

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

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

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

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

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

(Tôi đã có Trình quản lý tập lệnh người dùng, hãy cài đặt nó!)

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

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

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

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

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

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

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

// ==UserScript==
// @name         SHAYA UI
// @namespace    https://www.torn.com/
// @version      25.0
// @description  All-in-One Faction HUD – v25.0: Age in Tagen, Enemy Bounty, Bank raus, Bounty-Filter bereinigt, Code aufgeräumt
// @author       xShaYaKaZ
// @match        https://www.torn.com/*
// @grant        GM_xmlhttpRequest
// @grant        GM_setValue
// @grant        GM_getValue
// @connect      api.torn.com
// ==/UserScript==
 
(function () {
    'use strict';
 
    const VERSION = '25.0';
    const SCRIPT_TITLE = "SHAYA UI";
    const API_BASE = 'https://api.torn.com';
 
    const REFRESH = {
        PERSONAL:  15000,
        FACTION:   30000,
        CHAIN:     8000,
        TICK:      1000,
        NETWORTH:  60000,
        COMPANY:   60000,
        ENEMY:     30000,
    };
 
    const TRAVEL_WARN_SEC  = 420;
    const TRAVEL_CRIT_SEC  = 120;
    const CHAIN_WARN_SEC   = 60;
    const CHAIN_START_HIT  = 10;
    const INACTIVE_LIMIT   = 3;
    const SHOPPING_TIME_SEC = 15;
    const HAPPY_JUMP_THRESHOLD = 151;
 
    const DESTINATIONS = {
        'Mexico':         { flag: '🇲🇽' },
        'Cayman Islands': { flag: '🇰🇾' },
        'Canada':         { flag: '🇨🇦' },
        'Hawaii':         { flag: '🇺🇸' },
        'United Kingdom': { flag: '🇬🇧' },
        'Argentina':      { flag: '🇦🇷' },
        'Switzerland':    { flag: '🇨🇭' },
        'Japan':          { flag: '🇯🇵' },
        'China':          { flag: '🇨🇳' },
        'UAE':            { flag: '🇦🇪' },
        'South Africa':   { flag: '🇿🇦' },
        'Torn':           { flag: '🏠'  },
        'Torn City':      { flag: '🏠'  },
    };
 
    const DEST_TOP_ITEMS = {
        'Mexico':         ['Jaguar Plushie', 'Dahlia'],
        'United Kingdom': ['Xanax', 'Red Fox Plushie'],
        'Japan':          ['Xanax', 'Cherry Blossom'],
        'South Africa':   ['Lion Plushie', 'African Violet'],
        'Cayman Islands': ['Beer', 'Flower'],
        'Canada':         ['Wine', 'Beer'],
        'Hawaii':         ['Flower', 'Cannabis'],
        'Argentina':      ['Cocaine', 'Cannabis'],
        'Switzerland':    ['Ecstasy', 'Wine'],
        'China':          ['Opium', 'Heroin'],
        'UAE':            ['Cannabis', 'Laptop'],
    };
 
    const THEMES = {
        obsidian: { name:'⬛ Obsidian', bg:'#0a0a0b', bgCard:'#111114', bgEl:'#18181c', border:'#2a2a30', accent:'#6366f1', accent2:'#818cf8', text:'#f1f5f9', textMuted:'#64748b', textDim:'#94a3b8', green:'#10b981', red:'#ef4444', amber:'#f59e0b', blue:'#3b82f6', chain:'#ef4444', travel:'#6366f1', online:'#10b981' },
        slate:    { name:'🔵 Slate',    bg:'#0f172a', bgCard:'#1e293b', bgEl:'#293548', border:'#334155', accent:'#38bdf8', accent2:'#7dd3fc', text:'#f8fafc', textMuted:'#64748b', textDim:'#94a3b8', green:'#22c55e', red:'#f87171', amber:'#fbbf24', blue:'#38bdf8', chain:'#f87171', travel:'#38bdf8', online:'#22c55e' },
        emerald:  { name:'🟢 Emerald',  bg:'#030a06', bgCard:'#071210', bgEl:'#0d1e17', border:'#1a3a28', accent:'#10b981', accent2:'#34d399', text:'#ecfdf5', textMuted:'#4b7a60', textDim:'#6ee7b7', green:'#10b981', red:'#ef4444', amber:'#f59e0b', blue:'#3b82f6', chain:'#ef4444', travel:'#10b981', online:'#10b981' },
        crimson:  { name:'🔴 Crimson',  bg:'#0a0305', bgCard:'#150509', bgEl:'#1f070c', border:'#3a0d14', accent:'#f43f5e', accent2:'#fb7185', text:'#fff1f2', textMuted:'#7a3040', textDim:'#fca5a5', green:'#22c55e', red:'#f43f5e', amber:'#fb923c', blue:'#60a5fa', chain:'#f43f5e', travel:'#fb923c', online:'#22c55e' },
    };
 
    // ═══════════════════════════════════════════════════════════════
    // 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); }
.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; }
.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); }
.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); }
.chain-timer.warn { color:var(--chain); }
.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 .5s; }
.pulse-dot { width:7px; height:7px; border-radius:50%; background:var(--chain); box-shadow:0 0 8px var(--chain); animation:pipPulse 1s ease-in-out infinite; flex-shrink:0; }
.war-grid { display:grid; grid-template-columns:repeat(4,1fr); gap:7px; }
.war-cell { background:var(--bg-el); border:1px solid var(--border); border-radius:var(--r-sm); padding:12px 5px; text-align:center; }
.war-cell .wv { font-weight:700; font-size:20px; line-height:1; font-family:var(--mono); }
.war-cell .wl { font-size:9px; color:var(--muted); text-transform:uppercase; letter-spacing:.5px; margin-top:5px; }
.enemy-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); }
.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); }
.loading-state { display:flex; align-items:center; justify-content:center; padding:40px; color:var(--muted); font-size:12px; letter-spacing:1px; animation:opPulse 1.5s ease-in-out infinite; }
@keyframes opPulse { 0%,100%{opacity:1} 50%{opacity:.3} }
.empty-state { display:flex; flex-direction:column; align-items:center; justify-content:center; padding:36px 18px; color:var(--muted); gap:10px; text-align:center; }
.empty-state .es-icon { font-size:32px; opacity:.2; }
.empty-state .es-text { font-size:12px; letter-spacing:.5px; font-weight:600; }
.empty-state .es-sub  { font-size:11px; opacity:.6; }
.toggle-more { display:inline-flex; align-items:center; gap:5px; padding:5px 11px; border-radius:20px; border:1px solid var(--border); color:var(--muted); cursor:pointer; font-size:10px; font-weight:600; transition:all .15s; margin-top:7px; }
.toggle-more:hover { border-color:var(--accent); color:var(--accent2); }
.ts { font-family:var(--mono); font-size:10px; color:var(--muted); }
.fullscreen-alert { position:fixed; inset:0; background:rgba(2,4,8,.97); display:none; flex-direction:column; align-items:center; justify-content:center; z-index:9999999; }
.fullscreen-alert.active { display:flex; animation:fadeIn .4s ease; }
@keyframes fadeIn { from{opacity:0} to{opacity:1} }
.fs-box { text-align:center; padding:48px; border-radius:20px; max-width:420px; background:rgba(99,102,241,.06); border:2px solid var(--accent); box-shadow:0 0 80px rgba(99,102,241,.3); }
.fs-title { font-size:20px; font-weight:700; letter-spacing:2px; text-transform:uppercase; margin:16px 0; }
.fs-msg   { font-size:15px; color:var(--dim); line-height:1.6; }
.fs-close { margin-top:24px; background:rgba(99,102,241,.2); border:1px solid var(--accent); color:var(--accent2); border-radius:var(--r-sm); padding:12px 28px; font-size:12px; font-weight:700; cursor:pointer; font-family:var(--font); }
.scan-btn { display:inline-flex; align-items:center; gap:5px; padding:7px 14px; border-radius:var(--r-sm); background:rgba(99,102,241,.15); border:1px solid rgba(99,102,241,.3); color:var(--accent2); font-size:11px; font-weight:700; cursor:pointer; font-family:var(--font); transition:all .15s; }
.scan-btn:hover { background:rgba(99,102,241,.3); }
.scan-btn:disabled { opacity:.4; cursor:not-allowed; }
.spinning { animation:spin 1s linear infinite; }
@keyframes spin { to { transform:rotate(360deg); } }
.sort-btn { padding:4px 9px; border-radius:6px; border:1px solid var(--border); background:var(--bg-el); color:var(--muted); font-size:10px; font-weight:700; cursor:pointer; font-family:var(--font); transition:all .15s; }
.sort-btn.active { border-color:var(--accent); color:var(--accent2); background:rgba(99,102,241,.1); }
.threshold-input { background:var(--bg); border:1px solid var(--border); border-radius:5px; padding:3px 7px; color:var(--accent2); font-family:var(--mono); font-size:11px; width:50px; text-align:center; outline:none; }
.threshold-input:focus { border-color:var(--accent); }
.fab-pin-row { display:flex; align-items:center; justify-content:space-between; padding:9px 14px; border-bottom:1px solid rgba(255,255,255,.04); }
.fab-pin-lbl { font-size:12px; font-weight:600; color:var(--text); }
.fab-pin-desc { font-size:10px; color:var(--muted); margin-top:1px; }
`;
 
    const SHAYA_LOGO_SVG = `<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><defs><linearGradient id="sg" x1="0%" y1="0%" x2="100%" y2="100%"><stop offset="0%" style="stop-color:#ff2222"/><stop offset="50%" style="stop-color:#cc0000"/><stop offset="100%" style="stop-color:#880000"/></linearGradient></defs><polygon points="62,10 38,10 28,28 52,28 40,48 58,48 30,90 72,90 80,70 56,70 68,50 50,50" fill="url(#sg)"/><polygon points="68,8 74,14 66,18" fill="#ff4444" opacity="0.8"/><polygon points="30,8 24,16 34,14" fill="#dd2222" opacity="0.7"/><polygon points="26,88 20,82 30,80" fill="#ff3333" opacity="0.75"/><polygon points="74,92 80,84 70,86" fill="#cc1111" opacity="0.7"/></svg>`;
    const FAB_LOGO_SVG   = `<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><defs><linearGradient id="fg" x1="0%" y1="0%" x2="100%" y2="100%"><stop offset="0%" style="stop-color:#ff3333"/><stop offset="100%" style="stop-color:#aa0000"/></linearGradient></defs><polygon points="62,10 38,10 28,28 52,28 40,48 58,48 30,90 72,90 80,70 56,70 68,50 50,50" fill="url(#fg)"/><polygon points="68,8 74,14 66,18" fill="#ff5555" opacity="0.9"/><polygon points="30,8 24,16 34,14" fill="#ee2222" opacity="0.8"/><polygon points="26,88 20,82 30,80" fill="#ff4444" opacity="0.8"/><polygon points="74,92 80,84 70,86" fill="#cc1111" opacity="0.75"/></svg>`;
 
    // ═══════════════════════════════════════════════════════════════
    // UTILITY
    // ═══════════════════════════════════════════════════════════════
    class Utils {
        static fmtMoney(n) {
            if (!n && n !== 0) return '—';
            const abs = Math.abs(n), sign = n < 0 ? '-' : '';
            if (abs >= 1e9) return sign + '$' + (abs / 1e9).toFixed(2) + 'B';
            if (abs >= 1e6) return sign + '$' + (abs / 1e6).toFixed(2) + 'M';
            if (abs >= 1e3) return sign + '$' + (abs / 1e3).toFixed(1) + 'K';
            return sign + '$' + abs.toLocaleString('de-DE');
        }
        static fmtStat(n) {
            if (!n && n !== 0) return '—';
            if (n >= 1e9) return (n / 1e9).toFixed(2) + 'B';
            if (n >= 1e6) return (n / 1e6).toFixed(2) + 'M';
            if (n >= 1e3) return (n / 1e3).toFixed(1) + 'K';
            return n.toLocaleString('de-DE');
        }
        static fmtTravel(sec) { if (sec <= 0) return '—'; const h = Math.floor(sec/3600), m = Math.ceil((sec%3600)/60); return h > 0 ? `${h}h ${m}m` : (m <= 0 ? '< 1m' : `${m}m`); }
        static fmtHM(sec) { if (!sec || sec <= 0) return '—'; const h = Math.floor(sec/3600), m = Math.floor((sec%3600)/60); if (h > 0) return `${h}h ${m}m`; if (m > 0) return `${m}m`; return '< 1m'; }
        static fmtDhm(sec) { if (!sec || sec <= 0) return 'Fertig'; const d=Math.floor(sec/86400),h=Math.floor((sec%86400)/3600),m=Math.floor((sec%3600)/60); if(d>0)return`${d}T ${h}h`; if(h>0)return`${h}h ${m}m`; return`${m}m`; }
        static fmtSec(sec) { if (!sec || sec <= 0) return '0s'; const h=Math.floor(sec/3600),m=Math.floor((sec%3600)/60),s=sec%60; if(h>0)return`${h}h ${m}m`; if(m>0)return`${m}m ${s}s`; return`${s}s`; }
        static arrTime(secLeft) { if (!secLeft || secLeft <= 0) return '—'; return new Date(Date.now() + secLeft*1000).toLocaleTimeString('de-DE',{hour:'2-digit',minute:'2-digit'}); }
        static tctTime() { return new Date().toLocaleTimeString('de-DE',{hour:'2-digit',minute:'2-digit',timeZone:'UTC'}); }
        static pctBar(pct, color, size = '') { const w = Math.min(100,Math.max(0,pct||0)); return `<div class="bar ${size}"><div class="bar-fill" style="width:${w}%;background:${color};"></div></div>`; }
        static escHtml(s) { return String(s||'').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;'); }
        static inactiveAgo(ts) {
            if (!ts) return { text:'?', cls:'ago-d' };
            const s=Math.floor(Date.now()/1000-ts), m=Math.floor(s/60), h=Math.floor(s/3600), d=Math.floor(s/86400);
            if (s < 3600) return { text:m+'m', cls:'ago-m' };
            if (h < 24)   return { text:h+'h', cls:'ago-h' };
            return { text:d+'d', cls:'ago-d' };
        }
        static now() { return Math.floor(Date.now()/1000); }
        static buildDonut(data, total) {
            const r=28,cx=32,cy=32,circ=2*Math.PI*r; let off=0;
            const segs = data.map(({count,color}) => {
                if (!count||!total) return '';
                const dash=(count/total)*circ;
                const seg=`<circle cx="${cx}" cy="${cy}" r="${r}" fill="none" stroke="${color}" stroke-width="5" stroke-dasharray="${dash} ${circ-dash}" stroke-dashoffset="${-off}" stroke-linecap="butt"/>`;
                off+=dash; return seg;
            }).filter(Boolean);
            if (!segs.length) segs.push(`<circle cx="${cx}" cy="${cy}" r="${r}" fill="none" stroke="#2a2a30" stroke-width="5"/>`);
            return `<svg viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg" style="width:52px;height:52px;transform:rotate(-90deg)"><circle cx="${cx}" cy="${cy}" r="${r}" fill="none" stroke="#18181c" stroke-width="5"/>${segs.join('')}</svg>`;
        }
    }
 
    // ═══════════════════════════════════════════════════════════════
    // AUDIO
    // ═══════════════════════════════════════════════════════════════
    class AudioManager {
        constructor(store) { this.store = store; this._ctx = null; }
        get ctx() { if (!this._ctx) { try { this._ctx = new (window.AudioContext||window.webkitAudioContext)(); } catch(e){} } return this._ctx; }
        play(type) {
            if (!this.store.get('sound') && type !== 'enemy') return;
            if (!this.ctx) return;
            const c=this.ctx, now=c.currentTime, p=this.store.get('soundProfile')||'classic';
            const note=(freq,start,dur,vol,wave='sine')=>{const osc=c.createOscillator(),g=c.createGain();osc.connect(g);g.connect(c.destination);osc.type=wave;osc.frequency.value=freq;g.gain.setValueAtTime(0,now+start);g.gain.linearRampToValueAtTime(vol,now+start+0.03);g.gain.exponentialRampToValueAtTime(0.001,now+start+dur);osc.start(now+start);osc.stop(now+start+dur+0.05);};
            if (type==='enemy') { [0,.1,.2,.3].forEach(o=>note(880,o,.08,.25,'sawtooth')); note(440,.05,.3,.15,'square'); return; }
            if (p==='subtle')   { if(type==='travel_warn'){note(440,0,.2,.08);note(550,.25,.2,.08);}else if(type==='travel_crit'){[0,.15,.3].forEach(o=>note(660,o,.12,.1));}else if(type==='full'){note(330,0,.3,.08);}else note(400,0,.25,.06); return; }
            if (p==='military') { if(type==='travel_crit'){[0,.12,.24,.36].forEach(o=>note(1400,o,.07,.2,'sawtooth'));}else{note(1000,0,.05,.15,'square');note(800,.08,.05,.1,'square');} return; }
            if (type==='travel_warn')   { [0,.22].forEach(o=>note(660,o,.2,.25)); }
            else if (type==='travel_crit')  { [0,.16,.32].forEach(o=>note(880,o,.13,.18,'square')); }
            else if (type==='landing')  { [0,.15,.3].forEach(o=>note(520,o,.15,.2)); }
            else if (type==='full')     { const osc=c.createOscillator(),g=c.createGain();osc.connect(g);g.connect(c.destination);osc.type='sine';osc.frequency.setValueAtTime(440,now);osc.frequency.linearRampToValueAtTime(880,now+.4);g.gain.setValueAtTime(0,now);g.gain.linearRampToValueAtTime(.25,now+.08);g.gain.linearRampToValueAtTime(0,now+.5);osc.start(now);osc.stop(now+.55); }
            else { note(520,0,.4,.18); }
        }
    }
 
    // ═══════════════════════════════════════════════════════════════
    // STORE / TOAST / API
    // ═══════════════════════════════════════════════════════════════
    class Store {
        get(key, def) { const v = GM_getValue('thud_'+key, undefined); return v !== undefined ? v : def; }
        set(key, val) { GM_setValue('thud_'+key, val); return val; }
    }
 
    class ToastManager {
        constructor() { this._fired = {}; }
        show(icon, title, msg, style='', duration=10000) {
            let c = document.getElementById('thud-toasts');
            if (!c) { c = document.createElement('div'); c.id='thud-toasts'; document.body.appendChild(c); }
            const t = document.createElement('div');
            t.className = `toast ${style}`;
            t.innerHTML = `<div class="toast-icon">${icon}</div><div class="toast-body"><div class="toast-title">${title}</div><div class="toast-msg">${msg}</div></div><button class="toast-x">✕</button>`;
            t.querySelector('.toast-x').addEventListener('click', () => { t.classList.add('leaving'); setTimeout(()=>t.remove(),220); });
            c.appendChild(t);
            setTimeout(() => { if (t.parentNode) { t.classList.add('leaving'); setTimeout(()=>t.remove(),220); } }, duration);
        }
        showLandingTimer(destName) {
            let c = document.getElementById('thud-toasts');
            if (!c) { c = document.createElement('div'); c.id='thud-toasts'; document.body.appendChild(c); }
            const t = document.createElement('div'); t.className='toast landed';
            const topItems = (DEST_TOP_ITEMS[destName]||[]).slice(0,2).join(' · ');
            t.innerHTML = `<div class="toast-icon">🛬</div><div class="toast-body"><div class="toast-title">GELANDET: ${Utils.escHtml(destName)}</div><div class="toast-msg">⏱ <span id="thud-land-timer">15</span>s Einkaufszeit${topItems?`<br>🎯 ${topItems}`:''}</div></div><button class="toast-x">✕</button>`;
            t.querySelector('.toast-x').addEventListener('click', ()=>{ t.classList.add('leaving'); setTimeout(()=>t.remove(),220); });
            c.appendChild(t);
            let rem = SHOPPING_TIME_SEC;
            const iv = setInterval(()=>{ rem--; const el=t.querySelector('#thud-land-timer'); if(el)el.textContent=rem; if(rem<=0){clearInterval(iv);if(t.parentNode){t.classList.add('leaving');setTimeout(()=>t.remove(),220);}} },1000);
            hud.audio.play('landing');
        }
        fire(key, icon, title, msg, style, audio) { if(this._fired[key])return; this._fired[key]=true; hud.audio.play(audio||'notify'); this.show(icon,title,msg,style); }
        reset(key) { delete this._fired[key]; }
    }
 
    class TornAPI {
        constructor(store) { this.store = store; }
        get key() { return this.store.get('apiKey',''); }
        call(url, cb) {
            if (!this.key) { cb(null,'NO_KEY'); return; }
            GM_xmlhttpRequest({ method:'GET', url,
                onload: r => { try { const d=JSON.parse(r.responseText); if(d.error)cb(null,d.error.code); else cb(d,null); } catch(e){ cb(null,'PARSE'); } },
                onerror: () => cb(null,'NET'),
            });
        }
        v2(path, cb) { this.call(`${API_BASE}/v2/${path}&key=${this.key}`, cb); }
        userBasicAndProfile(cb) { this.v2('user?selections=basic,profile', cb); }
        userTravel(cb)           { this.v2('user?selections=travel', cb); }
        userBars(cb)             { this.v2('user?selections=bars', cb); }
        userCooldowns(cb)        { this.v2('user?selections=cooldowns', cb); }
        userEducation(cb)        { this.v2('user?selections=education', cb); }
        userBattleStats(cb)      { this.v2('user?selections=battlestats', cb); }
        userWorkStats(cb)        { this.v2('user?selections=workstats', cb); }
        userNetworth(cb)         { this.v2('user?selections=networth', cb); }
        factionBasicMembers(cb)  { this.v2('faction?selections=basic,members', cb); }
        factionChain(cb)         { this.v2('faction?selections=chain', cb); }
        companyProfile(cb)       { this.v2('company?selections=profile', cb); }
        companyEmployees(cb)     { this.v2('company?selections=employees', cb); }
        tornBountiesPage(offset, cb) { this.v2(`torn?selections=bounties&offset=${offset}`, cb); }
        playerProfile(playerId, cb) { this.call(`${API_BASE}/v2/user/${playerId}?selections=basic,profile&key=${this.key}`, cb); }
    }
 
    // ═══════════════════════════════════════════════════════════════
    // STATE
    // ═══════════════════════════════════════════════════════════════
    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',[]);
            // Bounty
            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.bountyDisplayCount=80; this.bountyLoadedPages=0;
            this.bountyLoading=false; this.bountyScanningAll=false;
            // Enemy
            this.enemies=store.get('enemies',[]);
            this.enemyData={};
            this.enemyFetchRunning=false;
            this._enemyTravelAlerted=store.get('_enemyTravelAlerted',{});
            // Misc
            this.lastUpdate=null; this.uptimeSec=0;
            this.showAllOnline=false; this.showAllOffline=false;
            this._fsAlertFired=false; this._landingFired=false;
            this._lastTravelSec=0; this._prevTravelDest='';
            this.energyThresholds=store.get('energyThresholds',[50,75,100]);
            this.nerveThresholds=store.get('nerveThresholds',[50,100]);
            this._energyAlerted=store.get('_energyAlerted',{});
            this._nerveAlerted=store.get('_nerveAlerted',{});
        }
        setBountySort(v)       { this.bountySort=v; this.store.set('bountySort',v); }
        setBountyFilterHosp(v) { this.bountyFilterHosp=v; this.store.set('bountyFilterHosp',v); }
        setBountyLevelMin(v)   { this.bountyLevelMin=Number(v)||1;   this.store.set('bountyLevelMin',this.bountyLevelMin); }
        setBountyLevelMax(v)   { this.bountyLevelMax=Number(v)||100; this.store.set('bountyLevelMax',this.bountyLevelMax); }
        setBountyRewardMin(v)  { this.bountyRewardMin=Number(v)||0;  this.store.set('bountyRewardMin',this.bountyRewardMin); }
        setBountyRewardMax(v)  { this.bountyRewardMax=Number(v)||0;  this.store.set('bountyRewardMax',this.bountyRewardMax); }
        addEnemy(id, name, note) { if(this.enemies.find(e=>String(e.id)===String(id)))return false; this.enemies.push({id:String(id),name:name||`#${id}`,note:note||'',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);} }
        updateEnemyData(id, data) { this.enemyData[String(id)]=data; }
        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); }
    }
 
    const ALARM_DEFAULTS = {
        travel_warn:    { enabled:true,  label:'✈ Travel Warnung',      desc:'Alarm wenn Ankunft < 7 Min' },
        travel_crit:    { enabled:true,  label:'✈ Travel Kritisch',      desc:'Alarm wenn Ankunft < 2 Min' },
        landing_timer:  { enabled:true,  label:'🛬 Landing Timer',       desc:'15s Countdown bei Landung' },
        energy_full:    { enabled:true,  label:'⚡ Energie Voll',         desc:'Alarm wenn Energie 100%' },
        energy_thresh:  { enabled:true,  label:'⚡ Energie Schwellwert',  desc:'Alarm bei konfigurierten Werten' },
        energy_over151: { enabled:false, label:'⚡ Energie über 151',     desc:'Alarm wenn Energie > 151 (Happy Jump)' },
        nerve_full:     { enabled:true,  label:'🧠 Nerve Voll',           desc:'Alarm wenn Nerve 100%' },
        nerve_thresh:   { enabled:true,  label:'🧠 Nerve Schwellwert',    desc:'Alarm bei konfigurierten Werten' },
        hp_crit:        { enabled:true,  label:'❤ HP unter 50%',         desc:'Toast-Alarm' },
        hospital_free:  { enabled:true,  label:'🏥 Krankenhaus Frei',     desc:'Alarm letzte 60 Sek' },
        jail_free:      { enabled:true,  label:'🚔 Gefängnis Frei',       desc:'Alarm letzte 60 Sek' },
        drug_ready:     { enabled:false, label:'💊 Drogen Bereit',        desc:'Alarm wenn Drug-CD weg' },
        booster_ready:  { enabled:false, label:'🧪 Booster Bereit',       desc:'Alarm wenn Booster-CD weg' },
        medical_ready:  { enabled:false, label:'💉 Medical Bereit',       desc:'Alarm wenn Medical-CD weg' },
        chain_warn:     { enabled:true,  label:'⛓ Chain Warnung',        desc:'Alarm wenn Chain < 1 Min' },
        enemy_travel:   { enabled:true,  label:'🎯 Enemy reist ab',       desc:'Alarm wenn Enemy-Target abreist' },
    };
 
    const VISIBILITY_DEFAULTS = {
        show_happy:       { enabled:true,  label:'😊 Happy Bar'          },
        show_jail:        { enabled:true,  label:'🚔 Gefängnis'          },
        show_hospital:    { enabled:true,  label:'🏥 Krankenhaus'        },
        show_drug_cd:     { enabled:true,  label:'💊 Drug CD'            },
        show_booster_cd:  { enabled:true,  label:'🧪 Booster CD'         },
        show_medical_cd:  { enabled:true,  label:'💉 Medical CD'         },
        show_education:   { enabled:true,  label:'📚 Lehrgang'           },
        show_tip:         { enabled:true,  label:'💡 Smart Tipp'         },
        show_quick_links: { enabled:true,  label:'🔗 Quick Links'        },
        show_tct_time:    { enabled:true,  label:'🕒 TCT Zeit'           },
        show_battle_stats:{ enabled:true,  label:'⚔ Battle Stats (Ich)' },
    };
 
    const TABS_DEF = [
        { id:'personal',      icon:'👤', label:'Ich'      },
        { id:'faction',       icon:'⚔',  label:'Fraktion' },
        { id:'bounty',        icon:'🎯',  label:'Bounty'   },
        { id:'enemy',         icon:'💀',  label:'Enemy'    },
        { id:'company',       icon:'🏢',  label:'Firma'    },
        { id:'networth',      icon:'💰',  label:'Vermögen' },
        { id:'notifications', icon:'🔔',  label:'Alarme'   },
        { id:'settings',      icon:'⚙',   label:'Settings' },
    ];
 
    // ═══════════════════════════════════════════════════════════════
    // ALARM CHECKER
    // ═══════════════════════════════════════════════════════════════
    class AlarmChecker {
        constructor(state, toast) { this.state=state; this.toast=toast; }
        run() {
            const s=this.state, now=Utils.now(), t=this.toast;
            if (!s.userBasic && !s.userProfile) return;
            const travel=s.userTravel?.travel||{};
            const tSec=Math.max(0,Number(travel.time_left)||0), dest=travel.destination||'';
            // Landing timer
            if (s._lastTravelSec>5 && tSec<=5 && s._prevTravelDest && s._prevTravelDest!=='Torn' && s._prevTravelDest!=='Torn City') {
                if (!s._landingFired && s.isAlarm('landing_timer')) { s._landingFired=true; t.showLandingTimer(s._prevTravelDest); }
            }
            if (tSec>5) { s._landingFired=false; if(dest) s._prevTravelDest=dest; }
            s._lastTravelSec=tSec;
            const flying=tSec>0;
            if (!flying) { t.reset('travel_warn'); t.reset('travel_crit'); s._fsAlertFired=false; }
            else {
                if (tSec>TRAVEL_WARN_SEC) t.reset('travel_warn');
                if (tSec>TRAVEL_CRIT_SEC) t.reset('travel_crit');
                if (s.isAlarm('travel_warn') && tSec<=TRAVEL_WARN_SEC && tSec>TRAVEL_CRIT_SEC && s.isTravelAlert('toast'))
                    t.fire('travel_warn','✈','ANKUNFT BALD',`${dest} — noch ${Utils.fmtHM(tSec)}`,'travel','travel_warn');
                if (s.isAlarm('travel_crit') && tSec<=TRAVEL_CRIT_SEC) {
                    if (s.isTravelAlert('toast')) t.fire('travel_crit','⚠','KRITISCH – KURZ VOR ANKUNFT',`${dest} landet in ${Utils.fmtTravel(tSec)}!`,'crit','travel_crit');
                    if (s.isTravelAlert('fullscreen') && !s._fsAlertFired) {
                        s._fsAlertFired=true;
                        const fs=document.getElementById('thud-fs-alert');
                        if (fs) { document.getElementById('thud-fs-msg').innerHTML=`${dest} — Ankunft in ca. 1 Minute!`; fs.classList.add('active'); setTimeout(()=>fs.classList.remove('active'),12000); }
                    }
                }
            }
            const energy=s.bars?.energy;
            if (energy) {
                const pct=Math.round((energy.current/energy.maximum)*100);
                if (pct<100) t.reset('energy_full');
                if (s.isAlarm('energy_full') && pct>=100 && energy.current<=HAPPY_JUMP_THRESHOLD) t.fire('energy_full','⚡','ENERGIE VOLL!',`${energy.current}/${energy.maximum} — Gym oder Chains!`,'warn','full');
                if (energy.current>HAPPY_JUMP_THRESHOLD) { if(s.isAlarm('energy_over151')) t.fire('energy_over151','⚡',`ENERGIE ÜBER ${HAPPY_JUMP_THRESHOLD}!`,`${energy.current} Energie — Happy Jump Zone!`,'crit','notify'); }
                else { t.reset('energy_over151'); }
                if (s.isAlarm('energy_thresh')) {
                    for (const thresh of (s.energyThresholds||[])) {
                        if (thresh>=100) continue;
                        if (pct>=thresh && !s._energyAlerted[thresh]) { s._energyAlerted[thresh]=true; t.show('⚡',`ENERGIE ${thresh}%`,`${energy.current}/${energy.maximum} Energie erreicht!`,'warn',8000); hud.audio.play('notify'); }
                        else if (pct<thresh-10) { delete s._energyAlerted[thresh]; }
                    }
                }
            }
            const nerve=s.bars?.nerve;
            if (nerve) {
                const pct=Math.round((nerve.current/nerve.maximum)*100);
                if (pct<100) t.reset('nerve_full');
                if (s.isAlarm('nerve_full') && pct>=100) t.fire('nerve_full','🧠','NERVE VOLL!',`${nerve.current}/${nerve.maximum} — Crimes abarbeiten!`,'warn','full');
                if (s.isAlarm('nerve_thresh')) {
                    for (const thresh of (s.nerveThresholds||[])) {
                        if (thresh>=100) continue;
                        if (pct>=thresh && !s._nerveAlerted[thresh]) { s._nerveAlerted[thresh]=true; t.show('🧠',`NERVE ${thresh}%`,`${nerve.current}/${nerve.maximum} Nerve erreicht!`,'info',8000); hud.audio.play('notify'); }
                        else if (pct<thresh-10) { delete s._nerveAlerted[thresh]; }
                    }
                }
            }
            const life=s.bars?.life||s.userProfile?.life||{};
            if (life.maximum) { const p=(life.current/life.maximum)*100; if(p>=50)t.reset('hp_crit'); if(s.isAlarm('hp_crit')&&p<50)t.fire('hp_crit','❤','HP KRITISCH!',`Nur noch ${Math.round(p)}% HP! Medical nutzen!`,'crit','notify'); }
            const statusObj=s.userProfile?.status||{};
            const hospState=(statusObj.state||'').toLowerCase();
            const hospUntil=Number(statusObj.until??0);
            const hospSec=hospState==='hospital'&&hospUntil>now?hospUntil-now:0;
            if (hospSec>60) t.reset('hospital_free');
            if (s.isAlarm('hospital_free')&&hospSec>0&&hospSec<=60) t.fire('hospital_free','🏥','KRANKENHAUS FREI','Du wirst gleich entlassen!','','notify');
            const jailUntil=(statusObj.state||'').toLowerCase()==='jail'?Number(statusObj.until??0):0;
            const jailSec=jailUntil>now?jailUntil-now:0;
            if (jailSec>60) t.reset('jail_free');
            if (s.isAlarm('jail_free')&&jailSec>0&&jailSec<=60) t.fire('jail_free','🚔','GEFÄNGNIS FREI','Du wirst gleich entlassen!','crit','notify');
            const cd=s.cooldowns?.cooldowns||{};
            const _cdVal=(v)=>Number(typeof v==='object'?(v?.cooldown_until??0):(v??0));
            const drugSec=Math.max(0,_cdVal(cd.drug)-now), boostSec=Math.max(0,_cdVal(cd.booster)-now), medSec=Math.max(0,_cdVal(cd.medical)-now);
            if (drugSec>120)  t.reset('drug_ready');
            if (boostSec>120) t.reset('booster_ready');
            if (medSec>120)   t.reset('medical_ready');
            if (s.isAlarm('drug_ready')    && _cdVal(cd.drug)>0    && drugSec<=0)  t.fire('drug_ready',   '💊','DROGEN BEREIT',  'Drug-Cooldown abgelaufen!',   'info','notify');
            if (s.isAlarm('booster_ready') && _cdVal(cd.booster)>0 && boostSec<=0) t.fire('booster_ready','🧪','BOOSTER BEREIT', 'Booster-Cooldown abgelaufen!','info','notify');
            if (s.isAlarm('medical_ready') && _cdVal(cd.medical)>0 && medSec<=0)   t.fire('medical_ready','💉','MEDICAL BEREIT', 'Cooldown abgelaufen!',        'info','notify');
            if (s.isAlarm('enemy_travel')) {
                for (const enemy of s.enemies) {
                    const ed=s.enemyData[enemy.id]; if(!ed) continue;
                    const eStat=(ed.status?.state||'').toLowerCase(), eDest=ed.status?.description||'';
                    const travelKey=`${enemy.id}_${eDest}`;
                    if (eStat==='traveling' && !s._enemyTravelAlerted[travelKey]) {
                        s._enemyTravelAlerted[travelKey]=true; hud.store.set('_enemyTravelAlerted',s._enemyTravelAlerted);
                        hud.audio.play('enemy'); t.show('⚔',`⚠ ENEMY REIST AB!`,`${enemy.name} fliegt: ${eDest}`,'enemy-alert',20000);
                    }
                    if (eStat!=='traveling') { Object.keys(s._enemyTravelAlerted).forEach(k=>{ if(k.startsWith(enemy.id+'_'))delete s._enemyTravelAlerted[k]; }); }
                }
            }
        }
    }
 
    // ═══════════════════════════════════════════════════════════════
    // CHAIN MANAGER
    // ═══════════════════════════════════════════════════════════════
    class ChainManager {
        constructor(state) { this.state=state; this._tick=null; }
        process(chain) {
            const s=this.state;
            if (chain&&chain.current>0) { s.chain=chain; s.chainEndTime=Date.now()+Number(chain.timeout)*1000; this.startTick(); }
            else { s.chain=null; s.chainEndTime=null; this.stopTick(); }
            this.renderSection();
            if (hud.store.get('minimized',false)) hud.renderer.renderMini();
        }
        startTick() { if(!this._tick) this._tick=setInterval(()=>this.tick(),1000); }
        stopTick()  { if(this._tick){clearInterval(this._tick);this._tick=null;} }
        tick() {
            const s=this.state; if(!s.chainEndTime){this.stopTick();return;}
            const rem=Math.max(0,s.chainEndTime-Date.now()), m=Math.floor(rem/60000), warn=rem<=CHAIN_WARN_SEC*1000;
            const el=document.getElementById('thud-chain-timer');
            if(el){el.textContent=`${m}m`;warn?el.classList.add('warn'):el.classList.remove('warn');}
            if(warn&&s.isAlarm('chain_warn'))hud.toasts.fire('chain_warn','⛓','CHAIN LÄUFT AB!',`Nur noch ${m}m!`,'crit','travel_crit');
            if(!warn)hud.toasts.reset('chain_warn');
            if(rem===0){s.chain=null;s.chainEndTime=null;this.stopTick();this.renderSection();}
        }
        renderSection() {
            const el=document.getElementById('thud-chain-block'); if(!el)return;
            const c=this.state.chain;
            if(!c||c.current===0){el.className='chain-block';el.innerHTML=`<div class="chain-idle"><div class="pulse-dot" style="opacity:.2;animation:none;background:var(--muted)"></div><span style="letter-spacing:.5px;font-size:10px;font-weight:600;margin-left:8px;">Keine aktive Chain</span></div>`;return;}
            const ms=[10,25,50,100,250,500,1000,2500,5000,10000,25000,50000,100000];
            const nM=ms.find(x=>x>c.current)||c.current, pM=ms[ms.indexOf(nM)-1]||0;
            const prog=((c.current-pM)/(nM-pM))*100;
            const mt=[[10,1],[25,1.5],[50,2],[100,2.5],[250,3],[500,3.5],[1000,4]];
            let mul=1; for(const[thresh,v]of mt)if(c.current>=thresh)mul=v;
            const rem=Math.max(0,this.state.chainEndTime-Date.now()), m=Math.floor(rem/60000), warn=rem<=CHAIN_WARN_SEC*1000;
            const isWU=c.current<CHAIN_START_HIT, hits=isWU?c.current:c.current-CHAIN_START_HIT;
            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">${m}m</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" style="width:${Math.max(1,Math.min(100,prog))}%"></div></div>`;
        }
    }
 
    // ═══════════════════════════════════════════════════════════════
    // FACTION HELPER
    // ═══════════════════════════════════════════════════════════════
    class FactionHelper {
        static getStatus(member) {
            const state=(typeof member.status==='object'?member.status?.state:member.status||'').toLowerCase();
            const lastStatus=(member.last_action?.status||'').toLowerCase();
            if(state.includes('hospital'))return'hospital';
            if(state.includes('travel')||state.includes('abroad'))return'traveling';
            if(state.includes('jail'))return'jail';
            if(lastStatus==='idle')return'idle';
            if(lastStatus==='online')return'online';
            const a=Date.now()/1000-(member.last_action?.timestamp||0);
            if(a<120)return'online'; if(a<600)return'idle'; return'offline';
        }
        static statusOrder(s) { return{online:0,traveling:1,idle:2,hospital:3,jail:4,offline:5}[s]??6; }
        static statusLabel(s) { return{online:'Online',idle:'AFK',traveling:'Travel',hospital:'Hosp.',jail:'Jail',offline:'Offline'}[s]||s; }
        static parseTravelRoute(member) {
            const s=typeof member.status==='object'?member.status:{};
            const desc=(s.description||s.state||'').trim();
            const toM=desc.match(/to\s+([A-Za-z\s]+?)(?:\s*(?:from|$)|\.|,)/i);
            const frM=desc.match(/from\s+([A-Za-z\s]+?)(?:\s*(?:to|$)|\.|,)/i);
            let to='Abroad',fr='Torn City';
            const norm=(str)=>{const l=str.trim().replace(/[.,;:!?]+$/,''); return l.toLowerCase().includes('torn')?'Torn City':l.length>2?l:'Abroad';};
            if(toM?.[1]) to=norm(toM[1]);
            if(frM?.[1]) fr=frM[1].trim().toLowerCase().includes('torn')?'Torn City':frM[1].trim();
            return{from:fr,to,dir:to==='Torn City'&&fr!=='Torn City'?'home':'abroad'};
        }
    }
 
    // ═══════════════════════════════════════════════════════════════
    // DATA FETCHER
    // ═══════════════════════════════════════════════════════════════
    class DataFetcher {
        constructor(api, state, toast, renderer) { this.api=api; this.state=state; this.toast=toast; this.renderer=renderer; }
        _opt(fetchFn, cb) { fetchFn((d,err)=>{ if(err===7||err===16)return; if(d)cb(d); }); }
        _afterPersonal() { setTimeout(()=>{ this.state.lastUpdate=new Date(); hud.alarmChecker.run(); if(hud.activeTab==='personal')this.renderer.render(); },600); }
 
        fetchPersonal() {
            if (!this.api.key) { this.renderer.render(); return; }
            this.api.userBasicAndProfile(d=>{ if(d&&(d.profile||d.basic)){this.state.userBasic=d.profile||d.basic||d;this.state.userProfile=d.profile||d;this._afterPersonal();} });
            this.api.userTravel(d=>{ if(d)this.state.userTravel=d; });
            this._opt(cb=>this.api.userBars(cb), d=>{ this.state.bars=d?.bars||d||null; });
            this._opt(cb=>this.api.userCooldowns(cb), d=>{ this.state.cooldowns=d||null; });
            this._opt(cb=>this.api.userEducation(cb), d=>{
                const edu=d?.education||d||null;
                if(edu&&typeof edu==='object'){const until=edu.current?.until??edu.until??edu.ends??0;this.state.education={until,raw:edu};}
                else this.state.education=edu;
            });
            this.api.userBattleStats((d,err)=>{
                if(err||!d)return;
                const _bv=(v)=>{if(!v&&v!==0)return 0;if(typeof v==='number')return v;if(typeof v==='object')return Number(v.modifier_value??v.value??v.current??0);return Number(v)||0;};
                let raw=null;
                if(d.battlestats&&typeof d.battlestats==='object')raw=d.battlestats;
                else if(d.battle_stats&&typeof d.battle_stats==='object')raw=d.battle_stats;
                else if('strength'in d||'defense'in d)raw=d;
                if(raw)this.state.battleStats={strength:_bv(raw.strength),defense:_bv(raw.defense),speed:_bv(raw.speed),dexterity:_bv(raw.dexterity)};
            });
            this._opt(cb=>this.api.userWorkStats(cb), d=>{
                if(d)this.state.workStats={manual:d.manual_labor??d.workstats?.manual_labor??0,intelligence:d.intelligence??d.workstats?.intelligence??0,endurance:d.endurance??d.workstats?.endurance??0};
            });
        }
 
        fetchFaction() {
            if(!this.api.key)return;
            this.api.factionBasicMembers(data=>{
                if(!data)return;
                const members=data.members||[];
                if(Array.isArray(members)){this.state.members={};members.forEach(m=>{this.state.members[m.id]=m;});}
                else this.state.members=members;
                this.state.faction={...this.state.faction,...(data.faction||data)};
                const el=document.getElementById('thud-faction-name');
                const n=this.state.faction.name||this.state.faction.faction?.name;
                if(el&&n)el.textContent=`${SCRIPT_TITLE} — ${n.toUpperCase()}`;
                if(hud.activeTab==='faction')this.renderer.render();
                if(hud.store.get('minimized',false))this.renderer.renderMini();
            });
        }
        fetchChain() { if(!this.api.key)return; this.api.factionChain(data=>{ if(data)hud.chainManager.process(data.chain||null); }); }
        fetchCompany() {
            if(!this.api.key)return;
            this.api.companyProfile((data,err)=>{ if(err===7||err===16)return; if(data)this.state.companyProfile=data.profile||data.company||data; });
            this.api.companyEmployees((data,err)=>{
                if(err===7||err===16)return;
                if(data){const empl=data.employees||[];this.state.companyEmployees=Array.isArray(empl)?empl:Object.values(empl);}
                if(hud.activeTab==='company')this.renderer.render();
            });
        }
        fetchNetworth() {
            if(!this.api.key)return;
            this.api.userNetworth((data,err)=>{
                if(err===7||err===16||!data)return;
                const nw=data.networth||null; if(!nw)return;
                const total=nw.total||0, now=Utils.now(), hist=this.state.networthHistory;
                hist.push({ts:now,val:total}); if(hist.length>120)hist.shift();
                this.state.networthHistory=hist; hud.store.set('nwHistory',hist);
                this.state.networthData=nw;
                if(hud.activeTab==='networth')this.renderer.render();
            });
        }
        fetchBountiesPage(offset, onDone) { this.api.tornBountiesPage(offset,data=>{ const list=data?.bounties; const arr=Array.isArray(list)?list:(list?Object.values(list):[]); onDone(arr); }); }
        fetchBountiesInitial(onDone) {
            const s=this.state; s.bountyLoading=true; s.bountyAllData=[]; s.bountyLoadedPages=0;
            this.fetchBountiesPage(0,arr=>{ s.bountyAllData=this._dedup(arr); s.bountyLoadedPages=1; s.bountyData=s.bountyAllData; s.bountyLastFetch=Utils.now(); s.bountyLoading=false; onDone(s.bountyAllData); });
        }
        fetchBountiesNextPage(onDone) {
            const s=this.state; if(s.bountyLoading)return; s.bountyLoading=true;
            this.fetchBountiesPage(s.bountyLoadedPages*100,arr=>{ s.bountyLoading=false; if(!arr.length){onDone(false);return;} s.bountyAllData=this._dedup([...(s.bountyAllData||[]),...arr]); s.bountyLoadedPages++; s.bountyData=s.bountyAllData; onDone(true); });
        }
        fetchBountiesAllPages(onProgress, onDone) {
            const s=this.state; if(s.bountyScanningAll)return; s.bountyScanningAll=true; s.bountyLoading=true; s.bountyAllData=[]; s.bountyLoadedPages=0;
            const MAX=20;
            const go=(offset)=>{
                if(s.bountyLoadedPages>=MAX){s.bountyScanningAll=false;s.bountyLoading=false;s.bountyData=s.bountyAllData;onDone(s.bountyAllData);return;}
                this.fetchBountiesPage(offset,arr=>{ if(!arr.length){s.bountyScanningAll=false;s.bountyLoading=false;s.bountyData=s.bountyAllData;onDone(s.bountyAllData);return;} s.bountyAllData=this._dedup([...s.bountyAllData,...arr]);s.bountyLoadedPages++;s.bountyLastFetch=Utils.now();if(onProgress)onProgress(s.bountyAllData.length,s.bountyLoadedPages);go(s.bountyLoadedPages*100); });
            };
            go(0);
        }
        _dedup(arr) { const seen=new Map(); for(const b of arr){const id=b.target_id??b.id??Math.random();if(!seen.has(id))seen.set(id,b);} return Array.from(seen.values()); }
 
        fetchEnemyData() {
            const s=this.state;
            if(s.enemyFetchRunning||!s.enemies.length||!this.api.key)return;
            s.enemyFetchRunning=true;
            let pending=s.enemies.length;
            const done=()=>{ pending--; if(pending<=0){s.enemyFetchRunning=false;if(hud.activeTab==='enemy')hud.renderer.render();} };
            s.enemies.forEach(enemy=>{
                this.api.playerProfile(enemy.id,(data,err)=>{
                    if(data){
                        const basic=data.basic||{}, profile=data.profile||{};
                        const merged=Object.assign({},data,basic,profile);
                        if(!merged.age&&merged.registration_timestamp) merged.age=Math.floor((Utils.now()-merged.registration_timestamp)/86400);
                        if(!merged.age&&profile.age) merged.age=profile.age;
                        if(!merged.age&&basic.age)   merged.age=basic.age;
                        const fac=merged.faction||profile.faction||basic.faction||null;
                        if(fac){const facId=Number(fac.faction_id??fac.id??0);merged._factionId=facId>0?facId:null;merged._factionName=facId>0?(fac.faction_name??fac.name??''):null;}
                        else{merged._factionId=null;merged._factionName=null;}
                        // Fetch open bounty on this enemy from bounty data
                        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();
                });
            });
        }
 
        fetchAll() { this.fetchPersonal(); this.fetchFaction(); this.fetchChain(); this.fetchCompany(); this.fetchNetworth(); this.fetchEnemyData(); }
    }
 
    // Helper: get bounty reward for enemy id from loaded bounty data
    StateManager.prototype._getEnemyBountyReward = function(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;
    };
 
    // ═══════════════════════════════════════════════════════════════
    // RENDERER
    // ═══════════════════════════════════════════════════════════════
    class Renderer {
        constructor(state) { this.state=state; }
        get body() { return document.getElementById('thud-body'); }
        render() {
            switch(hud.activeTab){
                case 'personal':      return this.renderPersonal();
                case 'faction':       return this.renderFaction();
                case 'bounty':        return this.renderBounty();
                case 'enemy':         return this.renderEnemy();
                case 'company':       return this.renderCompany();
                case 'networth':      return this.renderNetworth();
                case 'notifications': return this.renderNotifications();
                case 'settings':      return this.renderSettings();
                default:              return this.renderPersonal();
            }
        }
 
        renderMini() {
            const b=document.getElementById('thud-mini'); if(!b)return;
            const m=Object.values(this.state.members);
            const online=m.filter(x=>FactionHelper.getStatus(x)!=='offline').length;
            const c=this.state.chain, energy=this.state.bars?.energy, nerve=this.state.bars?.nerve;
            const tSec=Math.max(0,Number(this.state.userTravel?.travel?.time_left)||0);
            const tDest=this.state.userTravel?.travel?.destination||'';
            let chips='';
            chips+=`<div class="thud-mini-chip"><span style="width:5px;height:5px;border-radius:50%;background:var(--online);box-shadow:0 0 4px var(--online);display:inline-block;"></span><span class="thud-mini-val">${online}</span><span class="thud-mini-lbl">/${m.length}</span></div>`;
            if(energy){const ep=Math.round((energy.current/energy.maximum)*100);const ec=ep>=100?'var(--red)':ep>=75?'var(--amber)':'var(--blue)';chips+=`<div class="thud-mini-chip"><span style="font-size:9px;">⚡</span><span class="thud-mini-val" style="color:${ec};">${energy.current}</span></div>`;}
            if(nerve){const np=Math.round((nerve.current/nerve.maximum)*100);const nc=np>=100?'var(--red)':'var(--dim)';chips+=`<div class="thud-mini-chip"><span style="font-size:9px;">🧠</span><span class="thud-mini-val" style="color:${nc};">${nerve.current}</span></div>`;}
            if(c&&c.current>0){const rem=Math.max(0,this.state.chainEndTime-Date.now());const mc=Math.floor(rem/60000);const warn=rem<=CHAIN_WARN_SEC*1000;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">${mc}m</span></div>`;}
            if(tSec>0){const dInfo=DESTINATIONS[tDest]||{flag:'✈'};const tw=tSec<=TRAVEL_WARN_SEC;chips+=`<div class="thud-mini-chip"><span style="font-size:11px;">${dInfo.flag}</span><span class="thud-mini-val" style="color:${tw?'var(--amber)':'var(--travel)'};">${Utils.fmtHM(tSec)}</span></div>`;}
            b.innerHTML=`<div class="thud-mini-bar">${chips}</div><div class="thud-mini-tabs"><div class="thud-mini-tab" data-mini-tab="personal">👤 Ich</div><div class="thud-mini-tab" data-mini-tab="faction">⚔ Frak</div><div class="thud-mini-tab" data-mini-tab="bounty">🎯 BNT</div><div class="thud-mini-tab" data-mini-tab="enemy">💀 ENM</div><div class="thud-mini-tab" data-mini-tab="networth">💰 NW</div></div>`;
            b.querySelectorAll('[data-mini-tab]').forEach(btn=>btn.addEventListener('click',()=>{hud.store.set('minimized',false);document.getElementById('thud-overlay')?.classList.remove('minimized');hud.switchTab(btn.dataset.miniTab);}));
        }
 
        // ─── PERSONAL TAB ───
        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;
            // Hospital fix: v2 status.state + status.until
            const statusObj=profile.status||basic.status||{};
            const statusState=typeof statusObj==='string'?statusObj:(statusObj.state||'Okay');
            const hospState=statusState.toLowerCase()==='hospital', jailState=statusState.toLowerCase()==='jail';
            const hospUntil=hospState?Number(statusObj.until??0):0, jailUntil=jailState?Number(statusObj.until??0):0;
            const hospSec=hospUntil>now?hospUntil-now:0, jailSec=jailUntil>now?jailUntil-now:0;
            const energy=s.bars?.energy, nerve=s.bars?.nerve, happy=s.bars?.happy;
            const ePct=energy?(energy.current/energy.maximum)*100:0;
            const nPct=nerve?(nerve.current/nerve.maximum)*100:0;
            const haPct=happy?(happy.current/happy.maximum)*100:0;
            const tSec=Math.max(0,Number(travel.time_left)||0), flying=tSec>0;
            const destInfo=DESTINATIONS[travel.destination]||{flag:'✈'};
            const tDest=travel.destination||'';
            let tvCls='home',tvBadge='🏠 TORN CITY – BEREIT',tvBadgeColor='var(--green)';
            if(flying){if(tSec<=TRAVEL_CRIT_SEC){tvCls='crit';tvBadge='⚠ KURZ VOR ANKUNFT';tvBadgeColor='var(--red)';}else if(tSec<=TRAVEL_WARN_SEC){tvCls='warn';tvBadge='✈ ANKUNFT BALD';tvBadgeColor='var(--amber)';}else{tvCls='ok';tvBadge='✈ UNTERWEGS';tvBadgeColor='var(--travel)';}}
            const tClockColor=tSec<=TRAVEL_CRIT_SEC?'var(--red)':tSec<=TRAVEL_WARN_SEC?'var(--amber)':'var(--travel)';
            const tProgCls=tSec<=TRAVEL_CRIT_SEC?'crit':tSec<=TRAVEL_WARN_SEC?'warn':'';
            const hpColor=hp<50?'var(--red)':hp<70?'var(--amber)':'var(--green)';
            const eColor=ePct>=100?'var(--red)':ePct>=75?'var(--amber)':'var(--travel)';
            const nColor=nPct>=100?'var(--red)':nPct>=75?'var(--amber)':'#9333ea';
            const eMin=energy?.fulltime?Math.max(0,Math.ceil((energy.fulltime-now)/60)):null;
            const nMin=nerve?.fulltime?Math.max(0,Math.ceil((nerve.fulltime-now)/60)):null;
            const cd=s.cooldowns?.cooldowns||{};
            const _cd=(v)=>Number(typeof v==='object'?(v?.cooldown_until??0):(v??0));
            const drugSec=Math.max(0,_cd(cd.drug)-now), medSec=Math.max(0,_cd(cd.medical)-now), boostSec=Math.max(0,_cd(cd.booster)-now);
            const edu=s.education, eduLeft=edu?Math.max(0,Number(edu.until??0)-now):0;
            // Cash only (bank removed)
            const nwObj=s.networthData||{};
            const wallet=Number(nwObj.wallet??nwObj.cash??0);
            const stocks=Number(nwObj.stockmarket??nwObj.stocks??0);
            const totalMoney=wallet+stocks;
            const level=basic.level||profile.level||'?', name=basic.name||profile.name||'—';
            const tip=this._smartTip(hp,energy,nerve,tSec,hospSec,jailSec);
            const smartTippHtml=(tip&&s.isVisible('show_tip'))?`<div class="smart-tipp"><span class="smart-tipp-icon">${tip.icon}</span><span class="smart-tipp-text">${tip.text}</span></div>`:'';
            const DEFAULT_QL=[{label:'Stock\nMarket',icon:'📈',url:'https://www.torn.com/page.php?sid=stocks'},{label:'Casino',icon:'🎰',url:'https://www.torn.com/page.php?sid=slots'},{label:'Bazaar',icon:'🏪',url:'https://www.torn.com/bazaar.php?userId=0'},{label:'Forum',icon:'💬',url:'https://www.torn.com/forums.php'}];
            const quickLinks=hud.store.get('customQuickLinks',null)||DEFAULT_QL;
            const qlHtml=s.isVisible('show_quick_links')?`<div><div class="section-header">🔗 QUICK LINKS</div><div class="ql-grid">${quickLinks.map(({label,icon,url})=>`<a class="ql-card" href="${url}" target="_blank"><span class="ql-icon">${icon}</span><span class="ql-name">${label.replace('\n','<br>')}</span></a>`).join('')}</div></div>`:'';
            const bs=s.battleStats;
            const bsT=bs?(Number(bs.strength||0)+Number(bs.defense||0)+Number(bs.speed||0)+Number(bs.dexterity||0)):0;
            const bsHtml=(s.isVisible('show_battle_stats')&&bs)?`<div class="card"><div class="card-header"><span class="card-title red">⚔ BATTLE STATS</span><span class="badge badge-amber">${Utils.fmtStat(bsT)} total</span></div><div class="card-body"><div class="stat-grid stat-grid-4"><div class="stat-card"><div class="sv sv-red">${Utils.fmtStat(bs.strength||0)}</div><div class="sl">STR</div></div><div class="stat-card"><div class="sv sv-blue">${Utils.fmtStat(bs.defense||0)}</div><div class="sl">DEF</div></div><div class="stat-card"><div class="sv sv-green">${Utils.fmtStat(bs.speed||0)}</div><div class="sl">SPD</div></div><div class="stat-card"><div class="sv sv-amber">${Utils.fmtStat(bs.dexterity||0)}</div><div class="sl">DEX</div></div></div></div></div>`:'';
            const tctHtml=s.isVisible('show_tct_time')?`<span style="color:var(--accent2);font-family:var(--mono);">TCT ${Utils.tctTime()}</span>`:'';
            body.innerHTML=`
            ${smartTippHtml}
            <div style="display:flex;align-items:center;justify-content:space-between;padding:2px 2px 0;">
                <div><div style="font-size:14px;font-weight:700;color:var(--text);">${Utils.escHtml(name)}</div><div style="font-size:10px;color:var(--muted);margin-top:2px;">Level ${level} · ${Utils.escHtml(statusState)}</div></div>
                <div style="text-align:right;"><div style="font-family:var(--mono);font-size:15px;font-weight:700;color:var(--amber);">${Utils.fmtMoney(totalMoney)}</div><div style="font-size:9px;color:var(--muted);margin-top:1px;">💵 ${Utils.fmtMoney(wallet)}${stocks?` · 📈 ${Utils.fmtMoney(stocks)}`:''}</div></div>
            </div>
            <div class="travel-card ${tvCls}">
                <div class="travel-card-top" style="color:${tvBadgeColor};"><span>${tvBadge}</span>${flying?`<span style="font-size:18px;">${destInfo.flag}</span>`:''}</div>
                <div class="travel-card-body">
                ${!flying?`<div style="display:flex;align-items:center;gap:12px;"><span style="font-size:24px;opacity:.2;">🏠</span><div><div style="font-size:14px;font-weight:700;color:var(--green);">TORN CITY</div><div style="font-size:9px;color:var(--muted);text-transform:uppercase;letter-spacing:1.5px;margin-top:2px;">BEREIT ZUM ABFLUG</div></div></div>`
                :`<div style="display:flex;align-items:center;gap:10px;margin-bottom:10px;"><span style="font-size:22px;">${destInfo.flag}</span><div class="travel-dest" style="color:${tClockColor};">${Utils.escHtml(tDest||'Unbekannt')}</div></div><div style="display:flex;justify-content:space-between;align-items:flex-end;"><div class="travel-clock" style="color:${tClockColor};">${Utils.fmtHM(tSec)}</div><div style="text-align:right;"><div style="font-size:9px;color:var(--muted);text-transform:uppercase;letter-spacing:1px;">ANKUNFT</div><div style="font-family:var(--mono);font-size:15px;font-weight:700;color:var(--dim);margin-top:1px;">${Utils.arrTime(tSec)}</div></div></div><div class="travel-prog"><div class="travel-prog-fill ${tProgCls}" style="width:0%;"></div></div>`}
                </div>
            </div>
            <div class="card">
                <div class="card-header"><span class="card-title accent">◈ BARS &amp; STATUS</span><span class="badge badge-${statusState==='Okay'?'green':statusState==='Hospital'?'red':statusState==='Jail'?'amber':'muted'}">${Utils.escHtml(statusState)}</span></div>
                <div class="card-body">
                    <div class="stat-grid stat-grid-3" style="margin-bottom:12px;">
                        <div class="stat-card"><div class="sv ${hp>=70?'sv-green':hp>=50?'sv-amber':'sv-red'}">${hpM>0?Math.round(hp)+'%':'—'}</div><div class="sl">HP</div></div>
                        <div class="stat-card"><div class="sv ${ePct>=100?'sv-red':ePct>=75?'sv-amber':'sv-blue'}">${energy?energy.current:'—'}</div><div class="sl">Energie</div></div>
                        <div class="stat-card"><div class="sv ${nPct>=100?'sv-red':nPct>=75?'sv-amber':'sv-blue'}">${nerve?nerve.current:'—'}</div><div class="sl">Nerve</div></div>
                    </div>
                    <div class="bar-block"><div class="bar-block-head"><span class="bar-block-lbl">❤ HP</span><span class="bar-block-val" style="color:${hpColor}">${hpC} / ${hpM||'?'}</span></div>${Utils.pctBar(hp,hpColor,'bar-lg')}</div>
                    <div class="bar-block"><div class="bar-block-head"><span class="bar-block-lbl">⚡ Energie</span><span class="bar-block-val" style="color:${eColor}">${energy?`${energy.current} / ${energy.maximum}`:'—'}</span></div>${Utils.pctBar(ePct,eColor)}${eMin!==null&&eMin>0?`<div class="bar-block-sub">Voll in ${eMin} Min</div>`:''}</div>
                    <div class="bar-block"><div class="bar-block-head"><span class="bar-block-lbl">🧠 Nerve</span><span class="bar-block-val" style="color:${nColor}">${nerve?`${nerve.current} / ${nerve.maximum}`:'—'}</span></div>${Utils.pctBar(nPct,nColor)}${nMin!==null&&nMin>0?`<div class="bar-block-sub">Voll in ${nMin} Min</div>`:''}</div>
                    ${happy&&s.isVisible('show_happy')?`<div class="bar-block"><div class="bar-block-head"><span class="bar-block-lbl">😊 Happy</span><span class="bar-block-val" style="color:var(--amber)">${happy.current} / ${happy.maximum}</span></div>${Utils.pctBar(haPct,'var(--amber)')}</div>`:''}
                </div>
            </div>
            ${bsHtml}
            ${qlHtml}
            <div class="card">
                <div class="card-header"><span class="card-title">⏱ COOLDOWNS &amp; STATUS</span></div>
                <div class="card-body">
                    ${s.isVisible('show_hospital')?`<div class="row"><span class="row-label">🏥 Krankenhaus</span><span class="row-value" style="color:${hospSec>0?'var(--red)':'var(--green)'}">${hospSec>0?Utils.fmtSec(hospSec):'FREI'}</span></div>`:''}
                    ${s.isVisible('show_jail')?`<div class="row"><span class="row-label">🚔 Gefängnis</span><span class="row-value" style="color:${jailSec>0?'var(--amber)':'var(--green)'}">${jailSec>0?Utils.fmtSec(jailSec):'FREI'}</span></div>`:''}
                    ${s.isVisible('show_drug_cd')?`<div class="row"><span class="row-label">💊 Drogen CD</span><span class="row-value" style="color:${drugSec>0?'var(--amber)':'var(--green)'}">${drugSec>0?Utils.fmtSec(drugSec):'BEREIT'}</span></div>`:''}
                    ${s.isVisible('show_medical_cd')?`<div class="row"><span class="row-label">💉 Medical CD</span><span class="row-value" style="color:${medSec>0?'var(--amber)':'var(--green)'}">${medSec>0?Utils.fmtSec(medSec):'BEREIT'}</span></div>`:''}
                    ${s.isVisible('show_booster_cd')?`<div class="row"><span class="row-label">🧪 Booster CD</span><span class="row-value" style="color:${boostSec>0?'var(--amber)':'var(--green)'}">${boostSec>0?Utils.fmtSec(boostSec):'BEREIT'}</span></div>`:''}
                    ${s.isVisible('show_education')&&edu?(eduLeft>0?`<div class="row"><span class="row-label">📚 Kurs</span><span class="row-value">${Utils.fmtDhm(eduLeft)}</span></div>`:`<div class="row"><span class="row-label">📚 Lehrgang</span><span class="row-value" style="color:var(--green);">ABGESCHLOSSEN ✓</span></div>`):''}
                </div>
            </div>
            <div style="display:flex;justify-content:space-between;align-items:center;padding:2px 0;flex-shrink:0;" class="ts">
                <span style="display:flex;align-items:center;gap:5px;"><div class="pip"></div>${s.lastUpdate?.toLocaleTimeString('de-DE')||'—'}</span>
                ${tctHtml}
                <span>⬆ ${this._uptimeStr()}</span>
            </div>`;
        }
 
        _smartTip(hp, energy, nerve, tSec, hospSec, jailSec) {
            if(jailSec>0)return{icon:'🚔',text:`Du sitzt noch ${Utils.fmtSec(jailSec)} im Knast.`};
            if(hospSec>0&&hospSec<300)return{icon:'🏥',text:`Nur noch ${Utils.fmtSec(hospSec)} im Krankenhaus.`};
            if(hp<50&&hp>0)return{icon:'❤',text:`HP sehr niedrig (${Math.round(hp)}%). Medical Items nutzen!`};
            if(energy?.current>=energy?.maximum)return{icon:'⚡',text:'Energie voll! Chains oder Gym nutzen!'};
            if(nerve?.current>=nerve?.maximum)return{icon:'🧠',text:'Nerve voll! Crimes abarbeiten!'};
            if(tSec>0&&tSec<300)return{icon:'✈',text:`Fast da! Ankunft in ${Utils.fmtHM(tSec)}.`};
            if(energy&&(energy.current/energy.maximum)>0.85)return{icon:'💡',text:'Energie fast voll – nächste Chain planen?'};
            return null;
        }
        _uptimeStr() { const s=this.state.uptimeSec,h=Math.floor(s/3600),m=Math.floor((s%3600)/60); return h>0?`${h}h ${m}m`:`${m}m`; }
 
        // ─── FACTION TAB ───
        renderFaction() {
            const body=this.body; if(!body)return;
            const m=Object.values(this.state.members), t=m.length;
            const cap=this.state.faction.max_members||this.state.faction.capacity||t||50;
            const groups={online:[],idle:[],traveling:[],hospital:[],jail:[],offline:[]};
            m.forEach(x=>groups[FactionHelper.getStatus(x)].push(x));
            const active=[...groups.online,...groups.traveling,...groups.idle,...groups.hospital,...groups.jail];
            const sa=active.sort((a,b)=>FactionHelper.statusOrder(FactionHelper.getStatus(a))-FactionHelper.statusOrder(FactionHelper.getStatus(b)));
            const so=[...groups.offline].sort((a,b)=>(a.last_action?.timestamp||0)-(b.last_action?.timestamp||0));
            const sl=20,ol=INACTIVE_LIMIT;
            const sOn=this.state.showAllOnline?sa:sa.slice(0,sl);
            const sOf=this.state.showAllOffline?so:so.slice(0,ol);
            const eOn=sa.length-sl,eOf=so.length-ol;
            const rd=groups.online.length+groups.idle.length,oc=active.length;
            const donutSVG=Utils.buildDonut([{count:groups.online.length,color:'var(--green)'},{count:groups.idle.length,color:'var(--amber)'},{count:groups.traveling.length,color:'var(--travel)'},{count:groups.hospital.length,color:'var(--red)'},{count:groups.jail.length,color:'#f97316'},{count:groups.offline.length,color:'var(--bg-el)'}],t);
            body.innerHTML=`
            <div class="chain-block" id="thud-chain-block"><div class="chain-idle"><div class="pulse-dot" style="opacity:.2;animation:none;background:var(--muted);"></div><span style="font-size:10px;font-weight:600;margin-left:7px;letter-spacing:.5px;">Keine aktive Chain</span></div></div>
            <div class="card"><div class="card-header"><div style="display:flex;align-items:center;gap:8px;"><span class="card-title green">● ONLINE</span><span class="badge badge-green">${oc}</span></div><div style="position:relative;display:flex;align-items:center;justify-content:center;">${donutSVG}<div style="position:absolute;text-align:center;pointer-events:none;"><div style="font-size:13px;font-weight:700;color:var(--text);">${oc}</div><div style="font-size:8px;color:var(--muted);">/${cap}</div></div></div></div>
            <div class="card-body"><div class="member-grid">${sOn.map(x=>{const st=FactionHelper.getStatus(x);return`<div class="member-row"><span class="status-dot sd-${st}"></span><span class="member-name">${Utils.escHtml(x.name)}</span><span class="member-status">${FactionHelper.statusLabel(st)}</span></div>`;}).join('')}</div>${eOn>0&&!this.state.showAllOnline?`<span class="toggle-more" id="thud-more-online">▾ ${eOn} mehr</span>`:this.state.showAllOnline&&sa.length>sl?`<span class="toggle-more" id="thud-less-online">▴ Weniger</span>`:''}<div class="status-summary"><div class="stat-chip"><div class="sn" style="color:var(--green);">${groups.online.length}</div><div class="sl">Online</div></div><div class="stat-chip"><div class="sn" style="color:var(--amber);">${groups.idle.length}</div><div class="sl">AFK</div></div><div class="stat-chip"><div class="sn" style="color:var(--travel);">${groups.traveling.length}</div><div class="sl">Travel</div></div><div class="stat-chip"><div class="sn" style="color:var(--red);">${groups.hospital.length}</div><div class="sl">Hosp.</div></div><div class="stat-chip"><div class="sn" style="color:#f97316;">${groups.jail.length}</div><div class="sl">Jail</div></div><div class="stat-chip"><div class="sn" style="color:var(--muted);">${groups.offline.length}</div><div class="sl">Offline</div></div></div></div></div>
            <div class="card"><div class="card-header"><span class="card-title red">⚔ WAR READY</span></div><div class="card-body"><div class="war-grid"><div class="war-cell"><div class="wv" style="color:var(--green);">${rd}</div><div class="wl">Ready</div></div><div class="war-cell"><div class="wv" style="color:var(--red);">${groups.hospital.length}</div><div class="wl">Hosp.</div></div><div class="war-cell"><div class="wv" style="color:var(--travel);">${groups.traveling.length}</div><div class="wl">Travel</div></div><div class="war-cell"><div class="wv" style="color:var(--amber);">${t>0?Math.round(rd/t*100):0}%</div><div class="wl">Bereit</div></div></div></div></div>
            ${groups.traveling.length>0?`<div class="card"><div class="card-header"><span class="card-title blue">✈ UNTERWEGS (${groups.traveling.length})</span></div><div class="card-body p0">${groups.traveling.map(x=>{const{from,to,dir}=FactionHelper.parseTravelRoute(x);const fC=from.toLowerCase().includes('torn')?'var(--green)':'var(--travel)';const tC=to.toLowerCase().includes('torn')?'var(--green)':'var(--travel)';return`<div style="display:flex;align-items:center;gap:10px;padding:9px 14px;border-bottom:1px solid rgba(255,255,255,.04);"><span style="font-size:18px;">${dir==='home'?'🏠':'✈'}</span><div style="flex:1;min-width:0;"><div style="font-size:12px;font-weight:600;color:var(--text);">${Utils.escHtml(x.name)}</div><div style="font-size:10px;margin-top:2px;display:flex;align-items:center;gap:5px;"><span style="color:${fC};">${Utils.escHtml(from)}</span><span style="color:var(--muted);">→</span><span style="color:${tC};">${Utils.escHtml(to)}</span></div></div></div>`;}).join('')}</div></div>`:''}
            <div class="card"><div class="card-header"><span class="card-title">💤 OFFLINE (${groups.offline.length})</span></div><div class="card-body p0">${sOf.map(x=>{const a=Utils.inactiveAgo(x.last_action?.timestamp||0);return`<div class="offline-row"><span class="status-dot sd-offline"></span><span class="offline-name">${Utils.escHtml(x.name)}</span><span class="offline-ago ${a.cls}">${a.text}</span></div>`;}).join('')}${eOf>0&&!this.state.showAllOffline?`<span class="toggle-more" style="margin:7px 14px 12px;" id="thud-more-offline">▾ ${eOf} weitere</span>`:this.state.showAllOffline&&so.length>ol?`<span class="toggle-more" style="margin:7px 14px 12px;" id="thud-less-offline">▴ Weniger</span>`:''}</div></div>`;
            hud.chainManager.renderSection();
            if(this.state.chain?.current>0)hud.chainManager.startTick();
            document.getElementById('thud-more-online')?.addEventListener('click',()=>{this.state.showAllOnline=true;this.renderFaction();});
            document.getElementById('thud-less-online')?.addEventListener('click',()=>{this.state.showAllOnline=false;this.renderFaction();});
            document.getElementById('thud-more-offline')?.addEventListener('click',()=>{this.state.showAllOffline=true;this.renderFaction();});
            document.getElementById('thud-less-offline')?.addEventListener('click',()=>{this.state.showAllOffline=false;this.renderFaction();});
        }
 
        // ─── BOUNTY TAB ───
        renderBounty() {
            const body=this.body; if(!body)return;
            const s=this.state, data=s.bountyData, sort=s.bountySort, now=Utils.now();
            const doScan=()=>{ hud.fetcher.fetchBountiesInitial(()=>{s.bountySearch='';s.bountyDisplayCount=80;this.renderBounty();}); };
            if(!data){
                body.innerHTML=`<div class="card" style="flex-shrink:0;"><div class="card-header"><span class="card-title red">🎯 BOUNTY RADAR</span></div></div><div class="empty-state"><div class="es-icon">🎯</div><div class="es-text">Keine Bounties geladen</div><div class="es-sub">Klick "Laden" um die Torn Bounty-Liste zu holen</div></div><button class="scan-btn" id="thud-bounty-scan" style="align-self:center;">🔍 Bounty-Liste laden</button>`;
                document.getElementById('thud-bounty-scan')?.addEventListener('click',doScan); return;
            }
            const lvMin=Number(s.bountyLevelMin)||1, lvMax=Number(s.bountyLevelMax)||100;
            const rwMin=Number(s.bountyRewardMin)||0, rwMax=Number(s.bountyRewardMax)||0;
            const 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(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(!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="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>
                    </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();});
            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,'');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);}}));
        }
 
        _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>`;
            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';}
                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>
                        </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;
            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(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(!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);
        }
 
        // ─── ENEMY TAB ───
        renderEnemy() {
            const body=this.body; if(!body)return;
            const s=this.state, now=Utils.now(), enemies=s.enemies||[];
            const addEnemy=(id,note)=>{
                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)){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??'?';
                // Age in TAGEN (kein y/m Format)
                const 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;
                // Bounty reward on this enemy
                const bountyR=s._getEnemyBountyReward(enemy.id);
                let statusTag='',rowClass='enemy-row';
                if(isH){statusTag=`<span class="enemy-tag etag-hosp">🏥 HOSPITAL${hospSec>0?` · ${Utils.fmtSec(hospSec)}`:''}</span>`;rowClass+=' in-hospital';}
                else if(isTr){statusTag=`<span class="enemy-tag etag-travel">✈ ${Utils.escHtml(travelDesc)||'Reist'}</span>`;rowClass+=' traveling';}
                else if(isJ)statusTag=`<span class="enemy-tag etag-jail">🚔 JAIL</span>`;
                else if(ss)statusTag=`<span class="enemy-tag etag-ok">✓ OKAY</span>`;
                const lastHtml=lastAgo?`<span class="enemy-tag" style="background:rgba(255,255,255,.04);color:${isOnline?'var(--green)':'var(--muted)'};border:1px solid rgba(255,255,255,.08);">⏱ ${isOnline?'Online':lastAgo.text+' ago'}</span>`:'';
                const facTag=facId?`<span class="enemy-tag" style="background:rgba(255,255,255,.05);color:var(--muted);border:1px solid rgba(255,255,255,.08);">⚔ ${Utils.escHtml(facName||'Faction')}</span>`:
                                   `<span class="enemy-tag" style="background:rgba(16,185,129,.08);color:var(--green);border:1px solid rgba(16,185,129,.2);">✓ No Faction</span>`;
                // Age: "Age 1569" in grün, gleicher Style wie Lv
                const ageHtml=ageDays>0?`<span class="enemy-level" style="color:var(--green);">Age ${ageDays}</span>`:'';
                // Bounty: zeige wenn vorhanden
                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}
                    </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-remove" data-remove-enemy="${enemy.id}">✕ Entfernen</button>
                    </div>
                </div>`;
            };
            body.innerHTML=`
            <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"><button class="scan-btn" id="thud-enemy-add" style="white-space:nowrap;">+ Hinzufügen</button></div></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 (${enemies.length})</span><span style="font-size:9px;color:var(--muted);">Auto-Refresh 30s</span></div><div class="card-body p0">${enemies.map(e=>renderRow(e)).join('')}</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();addEnemy(id,'');});
            document.getElementById('thud-enemy-id')?.addEventListener('keydown',e=>{if(e.key==='Enter'){addEnemy(e.target.value?.trim(),'');}});
            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-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();});}));
        }
 
        // ─── COMPANY TAB ───
        renderCompany() {
            const body=this.body; if(!body)return;
            const cp=this.state.companyProfile, employees=this.state.companyEmployees||[], ws=this.state.workStats;
            const wsHtml=ws?`<div class="card" style="flex-shrink:0;"><div class="card-header"><span class="card-title blue">💼 DEINE WORKING STATS</span></div><div class="card-body"><div class="stat-grid stat-grid-3"><div class="stat-card"><div class="sv sv-amber">${Utils.fmtStat(ws.manual)}</div><div class="sl">🛠 Manual</div></div><div class="stat-card"><div class="sv sv-blue">${Utils.fmtStat(ws.intelligence)}</div><div class="sl">🧠 Intel</div></div><div class="stat-card"><div class="sv sv-green">${Utils.fmtStat(ws.endurance)}</div><div class="sl">🏃 Endure</div></div></div></div></div>`:'';
            if(!cp){body.innerHTML=`${wsHtml}<div class="empty-state"><div class="es-icon">🏢</div><div class="es-text">Keine Firmendaten</div><div class="es-sub">Du musst Inhaber oder Direktor sein.</div></div>`;return;}
            const name=cp.name||'—', type=cp.type?.name||(typeof cp.type==='string'?cp.type:'')||cp.company_type||'—', rating=cp.rating??'—';
            const _s=(v)=>{if(v===null||v===undefined)return null;if(typeof v==='number')return v;if(typeof v==='object')return v.value??v.current??v.amount??null;return null;};
            const dailyIncome=Number(cp.income?.daily??cp.daily_income??cp.daily_revenue??0);
            const weeklyProfit=Number(cp.income?.weekly??cp.weekly_profit??0);
            const empHired=cp.employees?.hired??cp.employees_hired??employees.length??0;
            const empCap=cp.employees?.capacity??cp.employees_capacity??'—';
            const effs=employees.map(e=>Number(e.effectiveness?.working_stats?.total??e.effectiveness?.total??e.working_stats?.total??0)).filter(v=>v>0);
            const avgEff=effs.length?Math.round(effs.reduce((a,v)=>a+v,0)/effs.length):(Number(_s(cp.efficiency))||0);
            body.innerHTML=`${wsHtml}
            <div class="card" style="flex-shrink:0;"><div class="card-header"><span class="card-title amber">🏢 ${Utils.escHtml(name)}</span><span class="badge badge-amber">${Utils.escHtml(String(type))}</span></div>
            <div class="card-body"><div class="company-kpi-grid"><div class="company-kpi"><div class="kv" style="color:var(--green);">${Utils.fmtMoney(dailyIncome)}</div><div class="kl">Täglich</div></div><div class="company-kpi"><div class="kv" style="color:var(--amber);">${Utils.fmtMoney(weeklyProfit)}</div><div class="kl">Wöchentlich</div></div><div class="company-kpi"><div class="kv" style="color:var(--blue);">${empHired}/${empCap}</div><div class="kl">Mitarbeiter</div></div><div class="company-kpi"><div class="kv" style="color:${avgEff>=60?'var(--green)':avgEff>=30?'var(--amber)':'var(--muted)'};">${avgEff>0?avgEff+'%':'—'}</div><div class="kl">Effizienz Ø</div></div></div>${rating!=='—'?`<div class="row"><span class="row-label">⭐ Rating</span><span class="row-value" style="color:var(--amber);">${rating} / 10</span></div>`:''}</div></div>
            ${employees.length>0?`<div class="card" style="flex-shrink:0;"><div class="card-header"><span class="card-title green">👷 MITARBEITER</span><span class="badge badge-green">${avgEff>0?avgEff+'%':'—'} Ø</span></div><div class="card-body p0">${employees.slice(0,40).map(e=>{const eff=Number(e.effectiveness?.working_stats?.total??e.effectiveness?.total??e.working_stats?.total??0);const pos=(()=>{const p=e.position??e.role??null;if(!p)return'—';if(typeof p==='string')return p;if(typeof p==='object')return p.name??p.title??'—';return'—';})();const online=((Date.now()/1000-(e.last_action?.timestamp||0))<120);const ec=eff>=60?'var(--green)':eff>=30?'var(--amber)':eff>0?'var(--red)':'var(--muted)';return`<div class="company-emp-row"><span class="status-dot sd-${online?'online':'offline'}" style="flex-shrink:0;"></span><span class="company-emp-name">${Utils.escHtml(e.name||`#${e.id}`)}</span><span class="company-emp-pos">${Utils.escHtml(pos)}</span><span class="company-emp-eff" style="color:${ec};">${eff>0?eff+'%':'—'}</span></div>`;}).join('')}</div></div>`:''}`;
        }
 
        // ─── NETWORTH TAB ───
        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 now=Utils.now(), oh=hist.filter(h=>h.ts>=now-3600);
            const change=oh.length>=2?hist[hist.length-1].val-oh[0].val:0;
            const wallet=Number(nw.wallet??nw.cash??0), bank=Number(nw.bank??0), stocks=Number(nw.stockmarket??nw.stocks??0);
            const totalLiquid=wallet+bank+stocks;
            const bazaar=Number(nw.bazaar??0), props=Number(nw.properties??0), piggy=Number(nw.piggybank??0), fvault=Number(nw.factionvault??0);
            body.innerHTML=`
            <div class="nw-ticker"><div style="font-size:9px;color:var(--muted);text-transform:uppercase;letter-spacing:1px;margin-bottom:4px;">LIQUIDES VERMÖGEN</div><div style="display:flex;align-items:baseline;gap:10px;"><div class="nw-main-val">${Utils.fmtMoney(totalLiquid)}</div>${change!==0?`<div class="nw-change ${change>0?'pos':'neg'}">${change>0?'▲':'▼'} ${Utils.fmtMoney(Math.abs(change))} / 1h</div>`:'<div class="nw-change" style="color:var(--muted);">± —</div>'}</div></div>
            <div class="card" style="flex-shrink:0;"><div class="card-header"><span class="card-title green">💵 LIQUIDITÄT</span></div><div class="card-body"><div class="row"><span class="row-label">💵 Cash</span><span class="row-value" style="color:var(--green);">${Utils.fmtMoney(wallet)}</span></div><div class="row"><span class="row-label">🏦 Bank</span><span class="row-value" style="color:var(--blue);">${Utils.fmtMoney(bank)}</span></div><div class="row"><span class="row-label">📈 Aktien</span><span class="row-value" style="color:var(--amber);">${Utils.fmtMoney(stocks)}</span></div></div></div>
            ${(bazaar||props||piggy||fvault)?`<div class="card" style="flex-shrink:0;"><div class="card-header"><span class="card-title">📦 WEITERE ASSETS</span></div><div class="card-body">${bazaar?`<div class="row"><span class="row-label">🏪 Bazaar</span><span class="row-value">${Utils.fmtMoney(bazaar)}</span></div>`:''}${props?`<div class="row"><span class="row-label">🏠 Immobilien</span><span class="row-value">${Utils.fmtMoney(props)}</span></div>`:''}${piggy?`<div class="row"><span class="row-label">🐷 Sparschwein</span><span class="row-value">${Utils.fmtMoney(piggy)}</span></div>`:''}${fvault?`<div class="row"><span class="row-label">🏛 Faction Vault</span><span class="row-value">${Utils.fmtMoney(fvault)}</span></div>`:''}</div></div>`:''} 
            ${hist.length>=2?`<div class="card" style="flex-shrink:0;"><div class="card-header"><span class="card-title green">📈 VERLAUF</span></div><div class="card-body">${this._simpleChart(hist)}</div></div>`:''}`;
        }
        _simpleChart(hist) {
            if(hist.length<2)return'';
            const vals=hist.map(h=>h.val), mn=Math.min(...vals), mx=Math.max(...vals), range=mx-mn||1;
            const W=280,H=60;
            const pts=hist.map((h,i)=>`${((i/(hist.length-1))*W).toFixed(1)},${(H-((h.val-mn)/range)*H).toFixed(1)}`).join(' ');
            return`<svg viewBox="0 0 ${W} ${H}" style="width:100%;height:56px;overflow:visible;"><polyline points="${pts}" fill="none" stroke="${vals[vals.length-1]>=vals[0]?'var(--green)':'var(--red)'}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>`;
        }
 
        // ─── NOTIFICATIONS TAB ───
        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);});
        }
 
        // ─── SETTINGS TAB ───
        renderSettings() {
            const body=this.body; if(!body)return;
            const s=this.state, fabPinned=hud.store.get('fabPinned',false);
            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">🔴 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-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">API KEY</div><div class="api-key-row"><span class="api-key-status ${hud.store.get('apiKey')?'set':'unset'}">${hud.store.get('apiKey')?'✓ API Key gesetzt':'⚠ Kein API Key'}</span><button class="thud-btn" id="thud-api-key-btn">🔑 Ändern</button></div></div></div>
            <div class="card" style="flex-shrink:0;"><div class="card-body"><div class="settings-section-title">TASTENKÜRZEL</div><div style="display:grid;grid-template-columns:1fr 1fr;gap:6px;font-size:11px;color:var(--dim);"><span><kbd style="background:var(--bg-el);border:1px solid var(--border);border-radius:3px;padding:1px 5px;font-family:var(--mono);font-size:9px;">M</kbd> Minimieren</span><span><kbd style="background:var(--bg-el);border:1px solid var(--border);border-radius:3px;padding:1px 5px;font-family:var(--mono);font-size:9px;">H</kbd> Ausblenden</span><span><kbd style="background:var(--bg-el);border:1px solid var(--border);border-radius:3px;padding:1px 5px;font-family:var(--mono);font-size:9px;">R</kbd> Refresh</span><span><kbd style="background:var(--bg-el);border:1px solid var(--border);border-radius:3px;padding:1px 5px;font-family:var(--mono);font-size:9px;">S</kbd> Settings</span></div></div></div>
            <div class="ts" style="text-align:center;flex-shrink:0;">${SCRIPT_TITLE} v${VERSION} · by xShaYaKaZ</div>`;
            body.querySelectorAll('[data-theme]').forEach(btn=>btn.addEventListener('click',()=>{hud.store.set('theme',btn.dataset.theme);applyTheme(btn.dataset.theme);this.renderSettings();}));
            body.querySelectorAll('[data-vis]').forEach(el=>el.addEventListener('change',e=>s.setVisible(e.target.dataset.vis,e.target.checked)));
            document.getElementById('thud-api-key-btn')?.addEventListener('click',openApiModal);
            document.getElementById('thud-fab-pin')?.addEventListener('change',e=>hud.store.set('fabPinned',e.target.checked));
            document.getElementById('thud-fab-reset')?.addEventListener('click',()=>{const fab=document.getElementById('thud-fab');if(fab){fab.style.left='10px';fab.style.top='10px';}hud.store.set('fabPos',{l:10,t:10});hud.toasts.show('✅','FAB zurückgesetzt','Links oben','info',2000);});
            document.getElementById('thud-ql-save')?.addEventListener('click',()=>{try{const raw=document.getElementById('thud-ql-json')?.value||'[]';const parsed=JSON.parse(raw);if(!Array.isArray(parsed))throw new Error('Kein Array');hud.store.set('customQuickLinks',parsed);hud.toasts.show('✅','Quick Links gespeichert',`${parsed.length} Links`,'info',3000);this.renderSettings();}catch(e){hud.toasts.show('⚠','JSON Fehler',e.message,'warn',4000);}});
            document.getElementById('thud-ql-reset')?.addEventListener('click',()=>{hud.store.set('customQuickLinks',null);hud.toasts.show('✅','Quick Links zurückgesetzt','Standard wiederhergestellt','info',3000);this.renderSettings();});
        }
 
        _noKeyState() { return`<div class="empty-state" style="padding:48px 24px;"><div class="es-icon">🔑</div><div class="es-text">KEIN API KEY</div><div class="es-sub">Klick um deinen Torn API Key einzugeben</div><button class="scan-btn" style="margin-top:14px;" onclick="document.getElementById('thud-api-key-btn-header').click()">🔑 API Key eingeben</button></div>`; }
        _loadingState() { return`<div class="loading-state">LADE DATEN…</div>`; }
    }
 
    // ═══════════════════════════════════════════════════════════════
    // THEME / MODAL
    // ═══════════════════════════════════════════════════════════════
    function applyTheme(key) {
        const t=THEMES[key]||THEMES.obsidian, r=document.documentElement.style;
        r.setProperty('--bg',t.bg); r.setProperty('--bg-card',t.bgCard); r.setProperty('--bg-el',t.bgEl);
        r.setProperty('--border',t.border); r.setProperty('--accent',t.accent); r.setProperty('--accent2',t.accent2);
        r.setProperty('--text',t.text); r.setProperty('--muted',t.textMuted); r.setProperty('--dim',t.textDim);
        r.setProperty('--green',t.green); r.setProperty('--red',t.red); r.setProperty('--amber',t.amber);
        r.setProperty('--blue',t.blue); r.setProperty('--chain',t.chain); r.setProperty('--travel',t.travel); r.setProperty('--online',t.online);
    }
    function openApiModal() {
        const m=document.createElement('div'); m.className='thud-modal-bg';
        m.innerHTML=`<div class="thud-modal"><h3>⚙ API Key</h3><p>Torn → Settings → API Key</p><input id="thud-api-input" type="text" placeholder="dein_api_key" value="${hud.store.get('apiKey','')}"><div class="modal-btns"><button class="modal-cancel" id="thud-modal-cancel">Abbrechen</button><button class="modal-save" id="thud-modal-save">Speichern</button></div></div>`;
        document.body.appendChild(m);
        document.getElementById('thud-modal-save').onclick=()=>{const k=document.getElementById('thud-api-input').value.trim();if(k){hud.store.set('apiKey',k);m.remove();hud.fetcher.fetchAll();hud.renderer.render();}};
        document.getElementById('thud-modal-cancel').onclick=()=>m.remove();
        m.addEventListener('click',e=>{if(e.target===m)m.remove();});
    }
 
    // ═══════════════════════════════════════════════════════════════
    // HUD CONTROLLER
    // ═══════════════════════════════════════════════════════════════
    const hud = {
        store: new Store(),
        state:null, api:null, audio:null, toasts:null,
        chainManager:null, alarmChecker:null, fetcher:null, renderer:null,
        activeTab:null,
 
        init() {
            this.state        = new StateManager(this.store);
            this.api          = new TornAPI(this.store);
            this.audio        = new AudioManager(this.store);
            this.toasts       = new ToastManager();
            this.chainManager = new ChainManager(this.state);
            this.alarmChecker = new AlarmChecker(this.state, this.toasts);
            this.renderer     = new Renderer(this.state);
            this.activeTab    = this.store.get('activeTab','personal');
            this._injectCSS(); this._buildUI();
            applyTheme(this.store.get('theme','obsidian'));
            this._setupIntervals(); this._setupHotkeys();
            if(!this.api.key) setTimeout(openApiModal,600);
            else this.fetcher.fetchAll();
        },
        _injectCSS() { const st=document.createElement('style'); st.textContent=buildCSS(); document.head.appendChild(st); },
        _buildUI() {
            if(!document.getElementById('thud-toasts')){ const tc=document.createElement('div'); tc.id='thud-toasts'; document.body.appendChild(tc); }
            const fs=document.createElement('div'); fs.id='thud-fs-alert'; fs.className='fullscreen-alert';
            fs.innerHTML=`<div class="fs-box"><div style="font-size:52px;">✈️</div><div class="fs-title">ANKUNFT IN KÜRZE</div><div class="fs-msg" id="thud-fs-msg"></div><button class="fs-close">OK</button></div>`;
            document.body.appendChild(fs); fs.querySelector('.fs-close').addEventListener('click',()=>fs.classList.remove('active'));
            const overlay=document.createElement('div'); overlay.id='thud-overlay';
            const tabsHtml=TABS_DEF.map(t=>`<div class="thud-tab ${this.activeTab===t.id?'active':''}" data-tab="${t.id}"><span class="ti">${t.icon}</span><span class="tl">${t.label}</span></div>`).join('');
            overlay.innerHTML=`
                <div class="thud-header" id="thud-header">
                    <div class="thud-header-logo">${SHAYA_LOGO_SVG}</div>
                    <div class="thud-header-info"><div class="thud-header-title" id="thud-faction-name">${SCRIPT_TITLE}</div><div class="thud-header-sub">v${VERSION} · xShaYaKaZ</div></div>
                    <div class="thud-header-actions"><div class="pip"></div><button class="thud-btn" id="thud-btn-refresh" title="Refresh [R]">↺</button><button class="thud-btn" id="thud-api-key-btn-header" title="API Key">🔑</button><button class="thud-btn" id="thud-btn-minimize" title="[M]">—</button><button class="thud-btn-close" id="thud-btn-close" title="[H]">✕</button></div>
                </div>
                <div class="thud-tabs" id="thud-tabs">${tabsHtml}</div>
                <div class="thud-mini" id="thud-mini"></div>
                <div class="thud-body" id="thud-body"><div class="loading-state">LADE…</div></div>
                <div class="thud-resize-se" id="thud-rse"></div>
                <div class="thud-resize-e"  id="thud-re"></div>
                <div class="thud-resize-s"  id="thud-rs"></div>`;
            document.body.appendChild(overlay);
            const pos=this.store.get('pos',null);
            if(pos){overlay.style.left=pos.l+'px';overlay.style.top=pos.t+'px';}else{overlay.style.right='20px';overlay.style.top='60px';}
            overlay.style.width=this.store.get('width',400)+'px';
            overlay.style.height=(this.store.get('height',null)||560)+'px';
            if(!this.store.get('visible',true))overlay.style.display='none';
            if(this.store.get('minimized',false))overlay.classList.add('minimized');
            const fab=document.createElement('div'); fab.id='thud-fab'; fab.title=`${SCRIPT_TITLE} [H]`; fab.innerHTML=FAB_LOGO_SVG;
            document.body.appendChild(fab);
            const fPos=this.store.get('fabPos',null);
            if(fPos){fab.style.left=fPos.l+'px';fab.style.top=fPos.t+'px';}else{fab.style.left='10px';fab.style.top='10px';}
            if(this.store.get('visible',true))fab.classList.add('on');
            this._setupDrag(overlay); this._setupResize(overlay); this._setupFab(fab);
            this._setupTabWheelScroll(); this._setupScrollFix(overlay);
            document.getElementById('thud-tabs').addEventListener('click',e=>{const t=e.target.closest('.thud-tab');if(!t)return;this.switchTab(t.dataset.tab);});
            document.getElementById('thud-btn-refresh').addEventListener('click',()=>this.fetcher.fetchAll());
            document.getElementById('thud-btn-minimize').addEventListener('click',()=>this.toggleMinimize());
            document.getElementById('thud-btn-close').addEventListener('click',()=>this.toggleVisible());
            document.getElementById('thud-api-key-btn-header').addEventListener('click',openApiModal);
            this.fetcher=new DataFetcher(this.api,this.state,this.toasts,this.renderer);
            this.renderer.render();
        },
        _setupTabWheelScroll() { const tabs=document.getElementById('thud-tabs');if(!tabs)return;tabs.addEventListener('wheel',e=>{e.preventDefault();tabs.scrollLeft+=e.deltaY!==0?e.deltaY:e.deltaX;},{passive:false}); },
        _setupScrollFix(overlay) {
            const getBody=()=>document.getElementById('thud-body');
            overlay.addEventListener('wheel',e=>{const t=e.target.closest('#thud-tabs');if(t)return;const b=getBody();if(!b)return;b.scrollTop+=e.deltaY;e.preventDefault();e.stopPropagation();},{passive:false});
            let ty=0;
            overlay.addEventListener('touchstart',e=>{ty=e.touches[0].clientY;},{passive:true});
            overlay.addEventListener('touchmove',e=>{const b=getBody();if(!b)return;b.scrollTop+=ty-e.touches[0].clientY;ty=e.touches[0].clientY;e.preventDefault();},{passive:false});
        },
        switchTab(tabId) { this.activeTab=tabId; this.store.set('activeTab',tabId); document.querySelectorAll('.thud-tab').forEach(el=>el.classList.toggle('active',el.dataset.tab===tabId)); this.renderer.render(); setTimeout(()=>{const b=document.getElementById('thud-body');if(b)b.scrollTop=0;},50); },
        toggleMinimize() { const v=!this.store.get('minimized',false); this.store.set('minimized',v); const o=document.getElementById('thud-overlay');if(o)o.classList.toggle('minimized',v); if(v)this.renderer.renderMini(); },
        toggleVisible() { const v=!this.store.get('visible',true); this.store.set('visible',v); const o=document.getElementById('thud-overlay');if(o)o.style.display=v?'':'none'; const fab=document.getElementById('thud-fab');if(fab)v?fab.classList.add('on'):fab.classList.remove('on'); },
        _setupDrag(el) {
            let drag=false,ox=0,oy=0;
            document.getElementById('thud-header').addEventListener('mousedown',e=>{if(e.target.tagName==='BUTTON'||e.target.closest('button'))return;e.preventDefault();const r=el.getBoundingClientRect();ox=e.clientX-r.left;oy=e.clientY-r.top;drag=true;});
            document.addEventListener('mousemove',e=>{if(!drag)return;el.style.left=Math.max(0,Math.min(e.clientX-ox,window.innerWidth-el.offsetWidth))+'px';el.style.top=Math.max(0,Math.min(e.clientY-oy,window.innerHeight-40))+'px';el.style.right='auto';});
            document.addEventListener('mouseup',()=>{if(!drag)return;drag=false;this.store.set('pos',{l:parseInt(el.style.left),t:parseInt(el.style.top)});});
        },
        _setupResize(el) {
            let rSE=false,rE=false,rS=false,sx=0,sy=0,sw=0,sh=0;
            document.getElementById('thud-rse').addEventListener('mousedown',e=>{rSE=true;sx=e.clientX;sy=e.clientY;sw=el.offsetWidth;sh=el.offsetHeight;e.preventDefault();e.stopPropagation();});
            document.getElementById('thud-re').addEventListener('mousedown',e=>{rE=true;sx=e.clientX;sw=el.offsetWidth;e.preventDefault();e.stopPropagation();});
            document.getElementById('thud-rs').addEventListener('mousedown',e=>{rS=true;sy=e.clientY;sh=el.offsetHeight;e.preventDefault();e.stopPropagation();});
            document.addEventListener('mousemove',e=>{if(rSE){el.style.width=Math.max(300,Math.min(720,sw+(e.clientX-sx)))+'px';el.style.height=Math.max(250,sh+(e.clientY-sy))+'px';}else if(rE){el.style.width=Math.max(300,Math.min(720,sw+(e.clientX-sx)))+'px';}else if(rS){el.style.height=Math.max(250,sh+(e.clientY-sy))+'px';}});
            document.addEventListener('mouseup',()=>{if(rSE||rE||rS){this.store.set('width',parseInt(el.style.width));if(el.style.height)this.store.set('height',parseInt(el.style.height));}rSE=rE=rS=false;});
        },
        _setupFab(fab) {
            let drag=false,ox=0,oy=0,moved=false,sx=0,sy=0;
            fab.addEventListener('mousedown',e=>{e.preventDefault();drag=true;moved=false;const r=fab.getBoundingClientRect();ox=e.clientX-r.left;oy=e.clientY-r.top;sx=e.clientX;sy=e.clientY;});
            document.addEventListener('mousemove',e=>{if(!drag)return;if(hud.store.get('fabPinned',false)){drag=false;return;}if(Math.abs(e.clientX-sx)>4||Math.abs(e.clientY-sy)>4)moved=true;fab.style.left=Math.max(0,Math.min(window.innerWidth-40,e.clientX-ox))+'px';fab.style.top=Math.max(0,Math.min(window.innerHeight-40,e.clientY-oy))+'px';});
            document.addEventListener('mouseup',()=>{if(!drag)return;drag=false;if(!moved){this.toggleVisible();return;}this.store.set('fabPos',{l:parseInt(fab.style.left),t:parseInt(fab.style.top)});});
        },
        _setupIntervals() {
            setInterval(()=>hud.fetcher.fetchPersonal(),REFRESH.PERSONAL);
            setInterval(()=>hud.fetcher.fetchFaction(), REFRESH.FACTION);
            setInterval(()=>hud.fetcher.fetchChain(),   REFRESH.CHAIN);
            setInterval(()=>hud.fetcher.fetchNetworth(),REFRESH.NETWORTH);
            setInterval(()=>hud.fetcher.fetchCompany(), REFRESH.COMPANY);
            setInterval(()=>hud.fetcher.fetchEnemyData(),REFRESH.ENEMY);
            setInterval(()=>this._tick(),               REFRESH.TICK);
        },
        _setupHotkeys() {
            document.addEventListener('keydown',e=>{ if(e.target.tagName==='INPUT'||e.target.tagName==='TEXTAREA')return; if(e.key==='m'||e.key==='M')this.toggleMinimize(); if(e.key==='h'||e.key==='H')this.toggleVisible(); if(e.key==='r'||e.key==='R')this.fetcher.fetchAll(); if(e.key==='s'||e.key==='S')this.switchTab('settings'); });
        },
        _tick() {
            this.state.uptimeSec++;
            const td=this.state.userTravel?.travel;
            if(td?.time_left>0)td.time_left=Math.max(0,Number(td.time_left)-1);
            this.alarmChecker.run();
            const min=this.store.get('minimized',false);
            if(!min&&this.activeTab==='personal')this.renderer.renderPersonal();
            if(min)this.renderer.renderMini();
        },
    };
 
    if(document.readyState==='loading'){ document.addEventListener('DOMContentLoaded',()=>hud.init()); }
    else { hud.init(); }
 
})();