Torn — Pickpocket HUD

HUD de pickpocket autónomo — aparece apenas na página de crimes/pickpocketing. Seleção inteligente de alvos, logs em tempo real, nerve display, anti-padrão.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, Greasemonkey alebo Violentmonkey.

Na inštaláciu tohto skriptu budete musieť nainštalovať rozšírenie, ako je napríklad Tampermonkey alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey alebo Userscripts.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie, ako napríklad Tampermonkey.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie správcu používateľských skriptov.

(Už mám správcu používateľských skriptov, nechajte ma ho nainštalovať!)

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

(Už mám správcu používateľských štýlov, nechajte ma ho nainštalovať!)

// ==UserScript==
// @name         Torn — Pickpocket HUD
// @namespace    https://torn.com/
// @version      1.0
// @description  HUD de pickpocket autónomo — aparece apenas na página de crimes/pickpocketing. Seleção inteligente de alvos, logs em tempo real, nerve display, anti-padrão.
// @author       TeuNome
// @match        https://www.torn.com/*
// @icon         https://www.torn.com/favicon.ico
// @grant        none
// @license MIT
// ==/UserScript==

(function () {
    'use strict';

    /* ══════════════════════════════════════════════════════
       SÓ ACTIVA NA PÁGINA DE PICKPOCKET
    ══════════════════════════════════════════════════════ */
    function isPickpocketPage() {
        return location.href.includes("sid=crimes") && location.hash.includes("pickpocketing");
    }
    function isCrimesPage() {
        return location.href.includes("sid=crimes");
    }

    // Aguarda hash change caso o utilizador navegue directamente para crimes
    if (!isPickpocketPage() && !isCrimesPage()) return;

    /* ══════════════════════════════════════════════════════
       CONFIGURAÇÃO
    ══════════════════════════════════════════════════════ */
    const API_KEY            = "merda"; // ← substitui pelo teu
    const PICKPOCKET_URL     = "https://www.torn.com/page.php?sid=crimes#/pickpocketing";
    const WAIT_POLL_MS       = 3000;
    const SCAN_LOG_INTERVAL  = 2;
    const REFRESH_MIN_SEC    = 3 * 60;
    const REFRESH_MAX_SEC    = 5 * 60;
    const STATE_TTL_MS       = 30000;

    /* ── localStorage keys ── */
    const LOGS_KEY          = "tph_logs";
    const STATS_KEY         = "tph_stats";
    const DROP_STATS_KEY    = "tph_dropStats";
    const PAUSED_KEY        = "tph_paused";
    const SESSION_MODE_KEY  = "tph_sessionMode";
    const NERVE_COUNTER_KEY = "tph_nerveCounter";
    const TARGET_FILTERS_KEY= "tph_targetFilters";
    const VICTIM_CFG_KEY    = "tph_victimCfg";
    const SCORE_MODE_KEY    = "tph_scoreMode";

    /* ══════════════════════════════════════════════════════
       BASE DE DADOS DE ALVOS
    ══════════════════════════════════════════════════════ */
const VICTIM_DB = [
    {
        id: "drunk_woman",
        label: "Drunk Woman",
        emoji: "🍹",
        priority: 1,
        difficulty: 1,
        cashMin: 274,
        cashMax: 3380,
        timerSec: 42,
        statuses: ["stumbling", "distracted"],
        goodStatus: ["stumbling", "distracted"],
        requireBuild: "heavyset",
        requireRazor: true,
        unique: "2x Bottle of Pumpkin Brew",
        uniqueVal: 800,
        riskHosp: true
    },
    {
        id: "drunk_man",
        label: "Drunk Man",
        emoji: "🍺",
        priority: 1,
        difficulty: 1,
        cashMin: 216,
        cashMax: 3769,
        timerSec: 42,
        statuses: ["stumbling", "distracted"],
        goodStatus: ["stumbling", "distracted"],
        requireRazor: true,
        unique: "6x Bottle of Beer",
        uniqueVal: 900
    },
    {
        id: "businessman",
        label: "Businessman",
        emoji: "💼",
        priority: 2,
        difficulty: 2,
        cashMin: 1380,
        cashMax: 9600,
        timerSec: 25,
        statuses: ["walking", "on phone"],
        goodStatus: ["on phone"],
        unique: "2x Speed",
        uniqueVal: 1200,
        riskJail: true
    },
    {
        id: "businesswoman",
        label: "Businesswoman",
        emoji: "👠",
        priority: 2,
        difficulty: 2,
        cashMin: 1100,
        cashMax: 6000,
        timerSec: 28,
        statuses: ["walking", "on phone"],
        goodStatus: ["on phone"],
        unique: "Glasses",
        uniqueVal: 500,
        riskJail: true
    },
    {
        id: "classy_lady",
        label: "Classy Lady",
        emoji: "💎",
        priority: 2,
        difficulty: 3,
        cashMin: 1020,
        cashMax: 79400,
        timerSec: 25,
        statuses: ["walking", "on phone"],
        goodStatus: ["on phone"],
        unique: "High Heels",
        uniqueVal: 2000,
        riskJail: true
    },
    {
        id: "cyclist",
        label: "Cyclist",
        emoji: "🚴",
        priority: 2,
        difficulty: 3,
        cashMin: 720,
        cashMax: 10400,
        timerSec: 10,
        statuses: ["cycling"],
        goodStatus: [],
        unique: "Mountain Bike",
        uniqueVal: 5000,
        riskHosp: true,
        riskJail: true
    },
    {
        id: "elderly_man",
        label: "Elderly Man",
        emoji: "👴",
        priority: 1,
        difficulty: 1,
        cashMin: 460,
        cashMax: 5000,
        timerSec: 55,
        statuses: ["walking"],
        goodStatus: [],
        unique: "Bag of Tootsie Rolls",
        uniqueVal: 300
    },
    {
        id: "elderly_woman",
        label: "Elderly Woman",
        emoji: "👵",
        priority: 1,
        difficulty: 1,
        cashMin: 460,
        cashMax: 4900,
        timerSec: 55,
        statuses: ["walking"],
        goodStatus: [],
        unique: "Opium",
        uniqueVal: 800,
        riskJail: true
    },
    {
        id: "gang_member",
        label: "Gang Member",
        emoji: "🔫",
        priority: 2,
        difficulty: 3,
        cashMin: 1660,
        cashMax: 7000,
        timerSec: 70,
        statuses: ["loitering"],
        goodStatus: ["loitering"],
        requireRazor: true,
        unique: "Spray Cans / Wire Cutters",
        uniqueVal: 1500,
        riskJail: true
    },
    {
        id: "homeless",
        label: "Homeless Person",
        emoji: "🧍",
        priority: 1,
        difficulty: 1,
        cashMin: 70,
        cashMax: 2921,
        timerSec: 120,
        statuses: ["loitering"],
        goodStatus: ["loitering"],
        unique: "4x Morphine",
        uniqueVal: 1000,
        riskJail: true
    },
    {
        id: "jogger",
        label: "Jogger",
        emoji: "🏃",
        priority: 2,
        difficulty: 2,
        cashMin: 560,
        cashMax: 4550,
        timerSec: 20,
        statuses: ["walking", "jogging"],
        goodStatus: ["walking"],
        requireRazor: true,
        unique: "Crocozade",
        uniqueVal: 400,
        riskJail: true
    },
    {
        id: "junkie",
        label: "Junkie",
        emoji: "💉",
        priority: 1,
        difficulty: 1,
        cashMin: 202,
        cashMax: 4800,
        timerSec: 60,
        statuses: ["stumbling", "loitering"],
        goodStatus: ["stumbling"],
        unique: "Drug bundle",
        uniqueVal: 1500,
        riskJail: true
    },
    {
        id: "laborer",
        label: "Laborer",
        emoji: "🪓",
        priority: 2,
        difficulty: 2,
        cashMin: 560,
        cashMax: 4700,
        timerSec: 40,
        statuses: ["walking", "distracted", "on phone"],
        goodStatus: ["distracted", "on phone"],
        unique: "Megaphone",
        uniqueVal: 700,
        riskJail: true
    },
    {
        id: "mobster",
        label: "Mobster",
        emoji: "🕴️",
        priority: 3,
        difficulty: 4,
        cashMin: 10000,
        cashMax: 700000,
        timerSec: 30,
        statuses: ["walking"],
        goodStatus: [],
        requireRazor: true,
        unique: "Huge Cash Drop",
        uniqueVal: 600000,
        riskJound: true
    },
    {
        id: "police",
        label: "Police Officer",
        emoji: "👮",
        priority: 3,
        difficulty: 4,
        cashMin: 0,
        cashMax: 2000,
        timerSec: 25,
        statuses: ["walking", "running"],
        goodStatus: ["walking"],
        unique: "Police Badge",
        uniqueVal: 2000,
        riskJail: true
    },
    {
        id: "postal_worker",
        label: "Postal Worker",
        emoji: "📦",
        priority: 2,
        difficulty: 2,
        cashMin: 746,
        cashMax: 5792,
        timerSec: 25,
        statuses: ["walking", "distracted"],
        goodStatus: ["distracted"],
        unique: "Lottery Voucher",
        uniqueVal: 1200,
        riskJail: true
    },
    {
        id: "rich_kid",
        label: "Rich Kid",
        emoji: "🧑‍🎧",
        priority: 2,
        difficulty: 3,
        cashMin: 1200,
        cashMax: 8000,
        timerSec: 32,
        statuses: ["walking", "on phone", "listening to music"],
        goodStatus: ["listening to music"],
        requireRazor: true,
        unique: "Six Pack Alcohol",
        uniqueVal: 900,
        riskJail: true
    },
    {
        id: "sex_worker",
        label: "Sex Worker",
        emoji: "💋",
        priority: 2,
        difficulty: 3,
        cashMin: 530,
        cashMax: 5600,
        timerSec: 80,
        statuses: ["distracted", "on phone", "soliciting"],
        goodStatus: ["soliciting"],
        unique: "Thong",
        uniqueVal: 400,
        riskJail: true
    },
    {
        id: "student",
        label: "Student",
        emoji: "🎓",
        priority: 2,
        difficulty: 2,
        cashMin: 312,
        cashMax: 3631,
        timerSec: 25,
        statuses: ["walking", "running", "on phone", "listening to music"],
        goodStatus: ["on phone", "listening to music"],
        requireRazor: true,
        unique: "Tech bundle",
        uniqueVal: 1500,
        riskJail: true
    },
    {
        id: "thug",
        label: "Thug",
        emoji: "👊",
        priority: 2,
        difficulty: 2,
        cashMin: 800,
        cashMax: 6000,
        timerSec: 20,
        statuses: ["walking", "running"],
        goodStatus: ["walking"],
        unique: "Weapon bundle",
        uniqueVal: 1200,
        riskJail: true
    },
    {
        id: "young_man",
        label: "Young Man",
        emoji: "🧑",
        priority: 1,
        difficulty: 1,
        cashMin: 550,
        cashMax: 3970,
        timerSec: 25,
        statuses: ["walking", "on phone", "listening to music"],
        goodStatus: ["on phone", "listening to music"],
        unique: "Ketamine",
        uniqueVal: 600,
        riskJail: true
    },
    {
        id: "young_woman",
        label: "Young Woman",
        emoji: "👩",
        priority: 1,
        difficulty: 1,
        cashMin: 830,
        cashMax: 4960,
        timerSec: 25,
        statuses: ["walking", "on phone", "listening to music", "distracted"],
        goodStatus: ["distracted", "on phone"],
        unique: "Ecstasy",
        uniqueVal: 700,
        riskJail: true
    }
];

    const ALL_STATUSES = [
        { id: "stumbling",           label: "Stumbling",           emoji: "🥴", mult: 1.5 },
        { id: "distracted",          label: "Distracted",          emoji: "😵", mult: 1.3 },
        { id: "on phone",            label: "On Phone",            emoji: "📱", mult: 1.2 },
        { id: "listening to music",  label: "Listening to Music",  emoji: "🎧", mult: 1.2 },
        { id: "talking",             label: "Talking",             emoji: "💬", mult: 1.2 },
        { id: "loitering",           label: "Loitering",           emoji: "🧍", mult: 1.2 },
        { id: "soliciting",          label: "Soliciting",          emoji: "🤑", mult: 1.3 },
        { id: "walking",             label: "Walking",             emoji: "🚶", mult: 1.0 },
        { id: "jogging",             label: "Jogging",             emoji: "🏃", mult: 0.7 },
        { id: "running",             label: "Running",             emoji: "💨", mult: 0.4 },
        { id: "cycling",             label: "Cycling",             emoji: "🚴", mult: 0.5 },
        { id: "patrolling",          label: "Patrolling",          emoji: "👮", mult: 0.9 },
    ];

    const ALL_BUILDS = [
        { id: "skinny",     label: "Skinny",     emoji: "🦴" },
        { id: "average",    label: "Average",    emoji: "🧍" },
        { id: "heavyset",   label: "Heavyset",   emoji: "🪨" },
        { id: "muscular",   label: "Muscular",   emoji: "💪" },
        { id: "overweight", label: "Overweight", emoji: "🫃" },
    ];

    const VICTIM_DROPS = {
    drunk_woman: [
        { id: "candy_kisses", name: "Bag of Candy Kisses", emoji: "🍬", heavysetOnly: true },
        { id: "tootsie_rolls", name: "Bag of Tootsie Rolls", emoji: "🍫", heavysetOnly: true },
        { id: "lollipop", name: "Lollipop", emoji: "🍭", heavysetOnly: true },
        { id: "pumpkin_brew", name: "Bottle of Pumpkin Brew", emoji: "🎃", unique: true },
        { id: "cash", name: "Cash", emoji: "💵" },
    ],

    drunk_man: [
        { id: "beer", name: "Bottle of Beer", emoji: "🍺" },
        { id: "cash", name: "Cash", emoji: "💵" },
    ],

    businessman: [
        { id: "speed", name: "Speed", emoji: "💊" },
        { id: "cash", name: "Cash", emoji: "💵" },
    ],

    businesswoman: [
        { id: "glasses", name: "Glasses", emoji: "👓" },
        { id: "cash", name: "Cash", emoji: "💵" },
    ],

    classy_lady: [
        { id: "heels", name: "High Heels", emoji: "👠" },
        { id: "cash", name: "Cash", emoji: "💵" },
    ],

    cyclist: [
        { id: "bike", name: "Mountain Bike", emoji: "🚴" },
        { id: "cash", name: "Cash", emoji: "💵" },
    ],

    homeless: [
        { id: "morphine", name: "Morphine", emoji: "💉" },
        { id: "cash", name: "Cash", emoji: "💵" },
    ],

    student: [
        { id: "tech", name: "Tech bundle", emoji: "📱" },
        { id: "cash", name: "Cash", emoji: "💵" },
    ],
};

    const STATUS_MULT = {
        "stumbling": 1.5, "distracted": 1.3, "soliciting": 1.3,
        "loitering": 1.2, "on phone": 1.2, "listening to music": 1.2, "talking": 1.2,
        "walking": 1.0, "patrolling": 0.9, "jogging": 0.7, "cycling": 0.5, "running": 0.4,
    };

    /* ══════════════════════════════════════════════════════
       HELPERS
    ══════════════════════════════════════════════════════ */
    function randBetween(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; }
    function formatTime(sec) {
        sec = Math.max(0, Math.round(sec));
        const h = Math.floor(sec / 3600), m = Math.floor((sec % 3600) / 60), s = sec % 60;
        return h > 0 ? `${h}:${pad(m)}:${pad(s)}` : `${m}:${pad(s)}`;
    }
    function pad(n) { return String(n).padStart(2, "0"); }
    function delay(ms) { return new Promise(r => setTimeout(r, ms)); }
    function waitFor(fn, maxMs = 12000) {
        return new Promise((res, rej) => {
            const t0 = Date.now();
            const iv = setInterval(() => {
                const el = fn();
                if (el) { clearInterval(iv); res(el); }
                else if (Date.now() - t0 >= maxMs) { clearInterval(iv); rej(`Timeout ${maxMs}ms`); }
            }, 300);
        });
    }

    /* ══════════════════════════════════════════════════════
       ESTADO: PAUSA, MODO SESSÃO, MODO SCORE
    ══════════════════════════════════════════════════════ */
    function isPaused()         { return localStorage.getItem(PAUSED_KEY) === "1"; }
    function setPaused(v)       { localStorage.setItem(PAUSED_KEY, v ? "1" : "0"); }
    function getSessionMode()   { return localStorage.getItem(SESSION_MODE_KEY) || "wait"; }
    function setSessionMode(m)  { localStorage.setItem(SESSION_MODE_KEY, m); }
    function getScoreMode()     { return localStorage.getItem(SCORE_MODE_KEY) || "auto"; }
    function setScoreMode(m)    { localStorage.setItem(SCORE_MODE_KEY, m); }

    /* ══════════════════════════════════════════════════════
       FILTROS DE ALVO (status + build)
    ══════════════════════════════════════════════════════ */
    function buildDefaultFilters() {
        const filters = {};
        VICTIM_DB.forEach(v => {
            const statuses = {};
            ALL_STATUSES.forEach(s => {
                statuses[s.id] = v.statuses.includes(s.id) && (v.goodStatus.length === 0 || v.goodStatus.includes(s.id));
            });
            const builds = {};
            ALL_BUILDS.forEach(b => { builds[b.id] = v.requireBuild ? b.id === v.requireBuild : true; });
            filters[v.id] = { statuses, builds };
        });
        return filters;
    }
    function getTargetFilters() {
        try {
            const s = JSON.parse(localStorage.getItem(TARGET_FILTERS_KEY));
            if (s && typeof s === "object") return s;
        } catch {}
        return buildDefaultFilters();
    }
    function saveTargetFilters(f) { localStorage.setItem(TARGET_FILTERS_KEY, JSON.stringify(f)); }
    function isStatusAllowed(victimId, status) {
        return getTargetFilters()[victimId]?.statuses?.[status.toLowerCase()] === true;
    }
    function isBuildAllowed(victimId, build) {
        return getTargetFilters()[victimId]?.builds?.[build.toLowerCase()] === true;
    }
    function toggleStatusFilter(victimId, statusId) {
        const f = getTargetFilters();
        if (!f[victimId]) f[victimId] = buildDefaultFilters()[victimId];
        f[victimId].statuses[statusId] = !f[victimId].statuses[statusId];
        saveTargetFilters(f);
    }
    function toggleBuildFilter(victimId, buildId) {
        const f = getTargetFilters();
        if (!f[victimId]) f[victimId] = buildDefaultFilters()[victimId];
        f[victimId].builds[buildId] = !f[victimId].builds[buildId];
        saveTargetFilters(f);
    }

    /* ══════════════════════════════════════════════════════
       LOGS
    ══════════════════════════════════════════════════════ */
    const CAT_CLR = {
        crime: "#5c2db0", scan: "#1a4a3a", drop: "#1a5a2a",
        system: "#2a2a4a", nerve: "#3a1a6a", warn: "#6a3a00"
    };
    function getLogs() { try { return JSON.parse(localStorage.getItem(LOGS_KEY)) || []; } catch { return []; } }
    function addLog(category, msg) {
        const logs = getLogs();
        const ts   = new Date().toLocaleTimeString("pt-PT", { hour:"2-digit", minute:"2-digit", second:"2-digit" });
        logs.unshift({ ts, category, msg });
        if (logs.length > 300) logs.length = 300;
        localStorage.setItem(LOGS_KEY, JSON.stringify(logs));
        renderLogs();
    }
    function clearLogs() { localStorage.removeItem(LOGS_KEY); renderLogs(); }

    /* ══════════════════════════════════════════════════════
       ESTATÍSTICAS
    ══════════════════════════════════════════════════════ */
    function getStats() {
        try { return JSON.parse(localStorage.getItem(STATS_KEY)) || defaultStats(); } catch { return defaultStats(); }
    }
    function defaultStats() {
        return { pickpockets: { count: 0, nerveSpent: 0, successCount: 0, byVictim: {} } };
    }
    function saveStats(s) { localStorage.setItem(STATS_KEY, JSON.stringify(s)); }
    function recordPickpocket(victimId, nerveSpent, success) {
        const s = getStats();
        s.pickpockets.count++;
        s.pickpockets.nerveSpent += nerveSpent;
        if (success) s.pickpockets.successCount++;
        if (!s.pickpockets.byVictim[victimId]) s.pickpockets.byVictim[victimId] = { count: 0, success: 0 };
        s.pickpockets.byVictim[victimId].count++;
        if (success) s.pickpockets.byVictim[victimId].success++;
        saveStats(s);
        if (s.pickpockets.count % 5 === 0) {
            addLog("crime", `🦹 #${s.pickpockets.count} crimes · ${s.pickpockets.nerveSpent} nerve gasto`);
        }
        updateStatsDisplay();
    }

    /* ══════════════════════════════════════════════════════
       DROPS
    ══════════════════════════════════════════════════════ */
    function getDropStats()    { try { return JSON.parse(localStorage.getItem(DROP_STATS_KEY)) || {}; } catch { return {}; } }
    function saveDropStats(d)  { localStorage.setItem(DROP_STATS_KEY, JSON.stringify(d)); }
    function recordDrop(victimId, dropId) {
    const d = getDropStats();

    if (!d[victimId]) d[victimId] = {};
    d[victimId][dropId] = (d[victimId][dropId] || 0) + 1;

    saveDropStats(d);

    const drop = VICTIM_DROPS[victimId]?.find(x => x.id === dropId);

    if (drop) {
        addLog("drop", `${drop.emoji} ${victimId} → ${drop.name} (x${d[victimId][dropId]})`);
    }
}
function parseOutcomeForDrop(modalEl, victimId) {
    if (!modalEl || !victimId) return null;

    const text = modalEl.textContent.toLowerCase();
    const drops = VICTIM_DROPS[victimId] || [];

    for (const drop of drops) {
        if (text.includes(drop.name.toLowerCase())) {
            return drop.id;
        }
    }

    if (/\$[\d,]+/.test(modalEl.textContent) && !text.includes("failed")) {
        return "cash";
    }

    return null;
}

    /* ══════════════════════════════════════════════════════
       NERVE — LEITURA DO DOM + API
    ══════════════════════════════════════════════════════ */
    let currentNerve    = 0;
    let currentNerveMax = 30;
    let apiLoaded       = false;

    function readNerveFromDOM() {
        const stats = document.querySelectorAll('[class*="bar-stats"]');
        for (const stat of stats) {
            const nameEl = stat.querySelector('[class*="bar-name"]');
            const valEl  = stat.querySelector('[class*="bar-value"]');
            if (nameEl && valEl && nameEl.textContent.trim().toLowerCase().startsWith("nerve")) {
                const m = valEl.textContent.trim().match(/(\d+)\/(\d+)/);
                if (m) { currentNerve = parseInt(m[1], 10); currentNerveMax = parseInt(m[2], 10); return true; }
            }
        }
        const names = document.querySelectorAll('[class*="bar-name"]');
        for (const nameEl of names) {
            if (nameEl.textContent.trim().toLowerCase().startsWith("nerve")) {
                const parent = nameEl.parentElement;
                const valEl  = parent && parent.querySelector('[class*="bar-value"]');
                if (valEl) {
                    const m = valEl.textContent.trim().match(/(\d+)\/(\d+)/);
                    if (m) { currentNerve = parseInt(m[1], 10); currentNerveMax = parseInt(m[2], 10); return true; }
                }
            }
        }
        return false;
    }

    async function fetchNerveFromAPI() {
        try {
            const res  = await fetch(`https://api.torn.com/user/?selections=bars&key=${API_KEY}`);
            const data = await res.json();
            if (data?.nerve) {
                currentNerve    = data.nerve.current;
                currentNerveMax = data.nerve.maximum;
                apiLoaded = true;
            }
        } catch (e) { console.error("[TPH] API:", e); }
    }

    /* ══════════════════════════════════════════════════════
       PARSE DE CARDS DE VÍTIMAS
    ══════════════════════════════════════════════════════ */
    function parseVictimCards() {
        const wrappers = document.querySelectorAll('[class*="crimeOptionWrapper"]');
        const parsed   = [];
        for (const wrap of wrappers) {
            const titleDiv = wrap.querySelector('[class*="titleAndProps"] [style*="color"]');
            const rawTitle = (titleDiv ? titleDiv.textContent.trim() : "").replace(/\s*\(.*?\)\s*$/, "").trim();

            const activityEl = wrap.querySelector('[class*="activity___"]');
            let rawStatus = "";
            if (activityEl) {
                for (const node of activityEl.childNodes) {
                    if (node.nodeType === Node.TEXT_NODE) { const t = node.textContent.trim(); if (t) { rawStatus = t; break; } }
                }
            }

            const buildEl  = wrap.querySelector('[class*="physicalProps___"] [aria-hidden="true"]');
            const rawBuild = buildEl ? buildEl.textContent.trim() : "";
            const clockEl  = wrap.querySelector('[class*="clock___"]');
            const clockSec = clockEl ? (parseInt(clockEl.textContent) || 99) : 99;
            const btn      = wrap.querySelector('button[class*="commitButton"]');
            if (!btn) continue;

            const matchedVictim =
                VICTIM_DB.find(v => rawTitle.toLowerCase() === v.label.toLowerCase()) ||
                VICTIM_DB.find(v => rawTitle.toLowerCase().includes(v.label.toLowerCase()) || v.label.toLowerCase().includes(rawTitle.toLowerCase()));

            parsed.push({ btn, rawTitle, rawStatus: rawStatus.toLowerCase(), rawBuild, clockSec, victimId: matchedVictim?.id || null, victim: matchedVictim || null });
        }
        return parsed;
    }

    function cardMeetsRequirements(card) {
        const v = card.victim;
        if (!v) return false;
        if (!isStatusAllowed(v.id, card.rawStatus)) return false;
        const buildWord = card.rawBuild.split(/\s+/)[0].toLowerCase();
        if (buildWord && !isBuildAllowed(v.id, buildWord)) return false;
        return true;
    }

    /* ══════════════════════════════════════════════════════
       SCAN LOG DE VÍTIMAS VISÍVEIS
    ══════════════════════════════════════════════════════ */
    function logVisibleVictims(cards) {
        if (!cards.length) { addLog("scan", "👁 Nenhum alvo visível no ecrã"); return; }
        cards.forEach(c => {
            const meetsReq = c.victim ? cardMeetsRequirements(c) : false;
            const icon     = c.victim ? c.victim.emoji : "❓";
            let reason;
            if (!c.victim)       reason = "⬜ fora da lista";
            else if (!meetsReq) {
                const needStatus = !isStatusAllowed(c.victimId, c.rawStatus);
                const buildWord  = c.rawBuild.split(/\s+/)[0].toLowerCase();
                const needBuild  = buildWord && !isBuildAllowed(c.victimId, buildWord);
                reason = needStatus ? `⏳ status "${c.rawStatus}" não permitido`
                       : needBuild  ? `⏳ build "${buildWord}" não permitido`
                       : `⏳ req. não cumprido`;
            } else reason = "✅ CANDIDATO";
            const mult     = STATUS_MULT[c.rawStatus] || 1.0;
            const multClr  = mult >= 1.2 ? "#00e676" : mult >= 1.0 ? "#ffd966" : "#ff6666";
            addLog("scan", `${icon} ${c.rawTitle} · <span style="color:${multClr};">${c.rawStatus || "?"}</span> · ${c.rawBuild.split(" ")[0] || "?"} · ⏱${c.clockSec}s → ${reason}`);
        });
    }

    /* ══════════════════════════════════════════════════════
       SELECÇÃO DE MELHOR ALVO
    ══════════════════════════════════════════════════════ */
    let nervePickCounter = parseInt(localStorage.getItem(NERVE_COUNTER_KEY) || "0", 10);
    function incNerveCounter()   { nervePickCounter++; localStorage.setItem(NERVE_COUNTER_KEY, nervePickCounter); return nervePickCounter; }
    function resetNerveCounter() { nervePickCounter = 0; localStorage.setItem(NERVE_COUNTER_KEY, "0"); }

    function selectBestVictim(doLogScan = false) {
        const cards = parseVictimCards();
        if (doLogScan) logVisibleVictims(cards);
        if (!cards.length) return null;

        // A cada 4 crimes → crime aleatório anti-padrão
        if (nervePickCounter > 0 && nervePickCounter % 4 === 0) {
            const pool = cards.filter(c => c.victim);
            if (!pool.length) return null;
            const pick = pool[Math.floor(Math.random() * pool.length)];
            addLog("crime", `🎲 Aleatório (turno ${nervePickCounter}) → ${pick.rawTitle} · ${pick.rawStatus}`);
            return pick;
        }

        // Selecção por prioridade + filtros
        const sorted = VICTIM_DB.slice().sort((a, b) => (a.priority || 99) - (b.priority || 99));
        for (const dbEntry of sorted) {
            const candidates = cards.filter(c => c.victimId === dbEntry.id && cardMeetsRequirements(c));
            if (candidates.length) {
                candidates.sort((a, b) => b.clockSec - a.clockSec);
                const pick = candidates[0];
                addLog("crime", `🎯 Atacar: ${pick.rawTitle} · ${pick.rawStatus} · ⏱${pick.clockSec}s`);
                return pick;
            }
        }
        return null;
    }

    /* ══════════════════════════════════════════════════════
       FECHAR MODAL DE RESULTADO + DETECTAR DROP
    ══════════════════════════════════════════════════════ */
    async function closeOutcomeModal(victimId) {
        const modalSel = '[class*="outcomeWrapper___"]';
        const t0 = Date.now();
        let modal = null;
        while (Date.now() - t0 < 4000) {
            const m = document.querySelector(modalSel);
            if (m && m.children.length > 0) { modal = m; break; }
            await delay(200);
        }
        await delay(300);
        if (modal) {
            const dropId = parseOutcomeForDrop(modal, victimId);
            if (dropId) recordDrop(victimId, dropId);
        }
        const closeSelectors = [
            '[class*="outcomeWrapper"] button', '[class*="outcome"] button',
            '[class*="modal"] button[class*="close"]', '[class*="closeButton"]',
            '[aria-label="Close"]', '[class*="continue"]',
        ];
        for (const sel of closeSelectors) {
            const btn = document.querySelector(sel);
            if (btn) { btn.click(); await delay(300); return; }
        }
        document.dispatchEvent(new KeyboardEvent("keydown", { key: "Escape", keyCode: 27, bubbles: true }));
        await delay(300);
        const overlay = document.querySelector('[class*="overlay"], [class*="Overlay"]');
        if (overlay) overlay.click();
        await delay(300);
    }

    /* ══════════════════════════════════════════════════════
       LOOP DE PICKPOCKET
    ══════════════════════════════════════════════════════ */
    let pickpocketRunning = false;

    async function runPickpocketLoop() {
        if (pickpocketRunning) return;
        if (!isPickpocketPage()) return;
        pickpocketRunning = true;

        const refreshAt    = Date.now() + randBetween(REFRESH_MIN_SEC, REFRESH_MAX_SEC) * 1000;
        const sessionStart = Date.now();
        let sessionCrimes  = 0;
        let scanIter       = 0;
        let consecutiveErr = 0;

        addLog("crime", `▶ Sessão iniciada — refresh em ~${Math.round((refreshAt - Date.now()) / 60000)}m · modo: ${getSessionMode()}`);
        updateStatusBadge("▶ A correr", "#00e676");

        try {
            await delay(2000);

            while (true) {
                /* ── Pausa ── */
                if (isPaused()) { updateStatusBadge("⏸ Pausa", "#ffd966"); await delay(1000); continue; }

                /* ── Refresh periódico ── */
                if (Date.now() >= refreshAt) {
                    const elapsed = Math.round((Date.now() - sessionStart) / 1000);
                    addLog("crime", `🔄 Refresh após ${formatTime(elapsed)} (${sessionCrimes} crimes) — a recarregar`);
                    pickpocketRunning = false;
                    location.href = PICKPOCKET_URL;
                    return;
                }

                /* ── Lê nerve ── */
                readNerveFromDOM();
                updateNerveDisplay();

                const hasNerve = currentNerve >= 5;

                /* ── Sem nerve ── */
                if (!hasNerve) {
                    if (getSessionMode() === "leave") {
                        addLog("crime", `⚡ Nerve ${currentNerve}/5 — modo "sair" — sessão terminada`);
                        updateStatusBadge("💤 Nerve baixo", "#ff6666");
                        break;
                    }
                    updateStatusBadge(`⏳ Nerve ${currentNerve}/${currentNerveMax}`, "#ffd966");
                    if (scanIter % SCAN_LOG_INTERVAL === 0) {
                        const cards = parseVictimCards();
                        logVisibleVictims(cards);
                    }
                    scanIter++;
                    await delay(WAIT_POLL_MS);
                    continue;
                }

                /* ── Selecciona alvo ── */
                const doLog = (scanIter % SCAN_LOG_INTERVAL === 0);
                scanIter++;
                const pick = selectBestVictim(doLog);

                if (!pick) {
                    if (doLog) addLog("scan", `⏳ Sem alvos prioritários (nerve: ${currentNerve})`);
                    updateStatusBadge("🔍 A procurar alvo…", "#6c5ce7");
                    await delay(WAIT_POLL_MS);
                    continue;
                }

                /* ── Timer mínimo ── */
                if (pick.clockSec < 3) {
                    addLog("scan", `⚠️ ${pick.rawTitle} a desaparecer (${pick.clockSec}s) — saltar`);
                    await delay(500);
                    continue;
                }

                /* ── Executa crime ── */
                updateStatusBadge(`🦹 ${pick.rawTitle}`, "#5c2db0");
                try {
                    incNerveCounter();
                    pick.btn.click();
                    sessionCrimes++;
                    consecutiveErr = 0;
                    recordPickpocket(pick.victimId || "unknown", 5, true);

                    await closeOutcomeModal(pick.victimId);

                    let waited = 0;
                    while (waited < 6000) {
                        await delay(500); waited += 500;
                        if (document.querySelector('button[class*="commitButton"]')) break;
                    }
                } catch (crimeErr) {
                    consecutiveErr++;
                    addLog("warn", `⚠️ Erro no crime #${sessionCrimes}: ${crimeErr}`);
                    if (consecutiveErr >= 5) {
                        addLog("warn", "❌ 5 erros consecutivos — a recarregar");
                        pickpocketRunning = false;
                        location.href = PICKPOCKET_URL;
                        return;
                    }
                    await delay(3000);
                }
            }
        } catch (err) {
            addLog("warn", `❌ Erro fatal: ${err}`);
        }

        addLog("crime", `✅ Sessão terminada: ${sessionCrimes} crimes · ${sessionCrimes * 5} nerve gasto`);
        updateStatusBadge("⏹ Parado", "#3a3a6a");
        pickpocketRunning = false;
        resetNerveCounter();
    }

    /* ══════════════════════════════════════════════════════
       CONSTRUÇÃO DO HUD
    ══════════════════════════════════════════════════════ */
    const box = document.createElement("div");
    box.id = "tph-box";
    box.style.cssText = [
        "position:fixed;top:0;right:0;bottom:0;width:360px",
        "background:rgba(6,6,12,0.98);color:#d0d0e8",
        "z-index:999999;font-family:'Segoe UI',Arial,sans-serif",
        "box-shadow:-6px 0 40px rgba(0,0,0,0.95)",
        "display:flex;flex-direction:column;overflow:hidden",
        "border-left:1px solid #1a1a2e;user-select:none",
        "transition:transform .3s ease",
    ].join(";");

    box.innerHTML = `
<!-- ── HEADER ── -->
<div style="background:#08081a;padding:8px 12px;display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid #1a1a2e;flex-shrink:0;">
    <div>
        <div style="font-size:11px;font-weight:800;letter-spacing:2.5px;color:#6c5ce7;">🦹 PICKPOCKET HUD</div>
        <div id="tph-status-badge" style="font-size:10px;color:#3a3a6a;margin-top:2px;">iniciando…</div>
    </div>
    <div style="display:flex;gap:5px;align-items:center;">
        <button id="tph-pause" style="background:#3a1a00;border:1px solid #7a3a00;color:#ffd966;padding:3px 10px;border-radius:4px;cursor:pointer;font-size:10px;font-weight:800;">⏸</button>
        <button id="tph-start" style="background:#0e3a0e;border:1px solid #1a7a1a;color:#00e676;padding:3px 10px;border-radius:4px;cursor:pointer;font-size:10px;font-weight:800;">▶ START</button>
        <button id="tph-collapse" style="background:none;border:1px solid #2a2a3e;color:#444;padding:2px 8px;border-radius:4px;cursor:pointer;font-size:10px;">◀</button>
    </div>
</div>

<!-- ── PAUSA BANNER ── -->
<div id="tph-pause-banner" style="display:none;background:#2a1000;border-bottom:2px solid #ffd966;padding:5px 12px;text-align:center;font-size:10px;font-weight:800;color:#ffd966;flex-shrink:0;">
    ⏸ EM PAUSA — podes navegar livremente
</div>

<!-- ── TABS ── -->
<div style="display:flex;background:#08081a;border-bottom:1px solid #1a1a2e;flex-shrink:0;">
    <button class="tph-tab active" data-tab="nerve"   style="flex:1;">NERVE</button>
    <button class="tph-tab"        data-tab="alvos"   style="flex:1;">ALVOS</button>
    <button class="tph-tab"        data-tab="drops"   style="flex:1;">DROPS</button>
    <button class="tph-tab"        data-tab="stats"   style="flex:1;">STATS</button>
    <button class="tph-tab"        data-tab="logs"    style="flex:1;">LOGS</button>
</div>

<!-- ── SCROLL AREA ── -->
<div id="tph-scroll" style="flex:1;overflow-y:auto;padding:12px;scrollbar-width:thin;scrollbar-color:#252538 #08081a;">

    <!-- NERVE TAB -->
    <div id="tph-tab-nerve">
        <div class="tph-card">
            <div class="tph-label">⚡ NERVE ATUAL</div>
            <div style="display:flex;align-items:baseline;gap:8px;margin-bottom:8px;">
                <div id="tph-nerve-val" style="font-size:36px;font-weight:800;letter-spacing:1px;color:#e0e0e0;line-height:1;">--/--</div>
                <div id="tph-nerve-phase" style="font-size:11px;color:#3a3a6a;"></div>
            </div>
            <div style="background:#101020;border-radius:5px;height:10px;overflow:hidden;margin-bottom:5px;">
                <div id="tph-nerve-bar" style="height:100%;width:0%;background:#5c2db0;border-radius:5px;transition:width .9s linear;"></div>
            </div>
            <div id="tph-nerve-sub" style="font-size:10px;color:#3a3a6a;"></div>
        </div>

        <div class="tph-card" style="margin-top:10px;">
            <div class="tph-label" style="margin-bottom:8px;">⚙ CONFIGURAÇÃO DA SESSÃO</div>
            <div style="font-size:10px;color:#3a3a5a;margin-bottom:6px;">Modo quando sem nerve:</div>
            <div style="display:flex;gap:6px;margin-bottom:10px;">
                <button id="tph-mode-wait"  class="tph-btn" style="flex:1;font-size:10px;background:#1a3a1a;">🟢 Aguardar</button>
                <button id="tph-mode-leave" class="tph-btn" style="flex:1;font-size:10px;background:#1a1a2e;">🔴 Sair</button>
            </div>
            <div id="tph-session-desc" style="font-size:10px;color:#2a2a4a;line-height:1.6;"></div>
        </div>

        <div class="tph-card" style="margin-top:10px;">
            <div class="tph-label" style="margin-bottom:6px;">🎲 ANTI-PADRÃO</div>
            <div style="font-size:10px;color:#2a2a4a;line-height:1.6;">
                A cada <strong style="color:#ffd966;">4 crimes</strong>, o 4.º é sempre <strong style="color:#ffd966;">completamente aleatório</strong>.<br>
                Refresh automático a cada <strong style="color:#6c5ce7;">3–5 min</strong> (URL sempre limpo).
            </div>
            <div style="margin-top:8px;font-size:10px;color:#3a3a5a;">Crimes até próximo aleatório:</div>
            <div id="tph-counter-display" style="font-size:20px;font-weight:800;color:#6c5ce7;line-height:1.2;">—</div>
        </div>
    </div>

    <!-- ALVOS TAB -->
    <div id="tph-tab-alvos" style="display:none;">
        <div id="tph-victims-wrap"></div>
    </div>

    <!-- DROPS TAB -->
    <div id="tph-tab-drops" style="display:none;">
        <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:8px;">
            <div class="tph-label" style="margin-bottom:0;">🍹 DROPS DRUNK WOMAN</div>
            <button id="tph-clear-drops" style="padding:2px 8px;background:#160808;border:1px solid #3a1010;color:#993333;border-radius:4px;cursor:pointer;font-size:9px;">Repor</button>
        </div>
        <div id="tph-drops-list"></div>
    </div>

    <!-- STATS TAB -->
    <div id="tph-tab-stats" style="display:none;">
        <div class="tph-card" style="margin-bottom:10px;">
            <div class="tph-label" style="margin-bottom:10px;">🦹 PICKPOCKETS</div>
            <div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:8px;">
                <div class="tph-stat-box"><div class="tph-stat-val" id="st-count">0</div><div class="tph-stat-lbl">crimes</div></div>
                <div class="tph-stat-box"><div class="tph-stat-val" id="st-nerve">0</div><div class="tph-stat-lbl">nerve gasto</div></div>
                <div class="tph-stat-box"><div class="tph-stat-val" id="st-succ">—</div><div class="tph-stat-lbl">sucesso%</div></div>
            </div>
        </div>
        <div class="tph-card" style="margin-bottom:10px;">
            <div class="tph-label" style="margin-bottom:10px;">👤 POR ALVO</div>
            <div id="st-by-victim" style="font-size:11px;display:flex;flex-direction:column;gap:5px;"></div>
        </div>
        <button id="tph-reset-stats" style="width:100%;padding:8px;background:#160808;border:1px solid #3a1010;color:#993333;border-radius:6px;cursor:pointer;font-size:11px;">⚠ Repor estatísticas</button>
    </div>

    <!-- LOGS TAB -->
    <div id="tph-tab-logs" style="display:none;">
        <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:8px;">
            <div style="font-size:10px;color:#2a2a4a;">Últimas 300 entradas</div>
            <button id="tph-clear-logs" style="padding:3px 10px;background:#160808;border:1px solid #3a1010;color:#993333;border-radius:4px;cursor:pointer;font-size:10px;">Limpar</button>
        </div>
        <div id="tph-logs-list" style="display:flex;flex-direction:column;gap:3px;font-size:11px;"></div>
    </div>

</div><!-- /scroll -->

<style>
#tph-box * { box-sizing:border-box; }
#tph-scroll::-webkit-scrollbar { width:4px; }
#tph-scroll::-webkit-scrollbar-track { background:#08081a; }
#tph-scroll::-webkit-scrollbar-thumb { background:#202030; border-radius:3px; }
.tph-card  { background:#0c0c1c;border:1px solid #1a1a2e;border-radius:8px;padding:12px; }
.tph-label { font-size:9px;letter-spacing:1.5px;color:#2a2a5a;text-transform:uppercase;margin-bottom:5px; }
.tph-btn   { width:100%;padding:8px 0;border:none;border-radius:6px;color:#d0d0e8;cursor:pointer;font-size:12px;font-weight:700;transition:filter .15s; }
.tph-btn:hover { filter:brightness(1.35); }
.tph-tab   { padding:7px 0;border:none;background:none;color:#2a2a4a;font-size:9px;font-weight:800;cursor:pointer;border-bottom:2px solid transparent;letter-spacing:.6px;text-transform:uppercase; }
.tph-tab.active { background:#0c0c1c !important;color:#6c5ce7 !important;border-bottom:2px solid #6c5ce7 !important; }
.tph-stat-box  { background:#08081a;border:1px solid #1a1a2e;border-radius:6px;padding:8px;text-align:center; }
.tph-stat-val  { font-size:20px;font-weight:800;color:#6c5ce7;line-height:1; }
.tph-stat-lbl  { font-size:9px;color:#2a2a4a;margin-top:3px;text-transform:uppercase;letter-spacing:.4px; }
.tph-log       { background:#0c0c1c;border-radius:4px;padding:4px 9px;border-left:2px solid #1a1a2e;line-height:1.5;font-size:10px; }
.tph-log-ts    { color:#1e1e3a;font-size:9px;margin-right:5px; }
.tph-tinput    { width:100%;background:#0c0c1a;border:1px solid #20203a;color:#d0d0e8;padding:5px;border-radius:5px;font-size:11px; }
</style>`;

    document.body.appendChild(box);

    /* ── Colapsar ── */
    let collapsed = false;
    document.getElementById("tph-collapse").onclick = () => {
        collapsed = !collapsed;
        box.style.transform = collapsed ? "translateX(360px)" : "translateX(0)";
        document.getElementById("tph-collapse").textContent = collapsed ? "▶" : "◀";
    };

    /* ── Pausa ── */
    function updatePauseUI() {
        const paused  = isPaused();
        const btn     = document.getElementById("tph-pause");
        const banner  = document.getElementById("tph-pause-banner");
        if (paused) {
            btn.textContent      = "▶";
            btn.style.background = "#0e3a0e"; btn.style.borderColor = "#1a7a1a"; btn.style.color = "#00e676";
            if (banner) banner.style.display = "block";
            addLog("crime", "⏸ Pausado");
        } else {
            btn.textContent      = "⏸";
            btn.style.background = "#3a1a00"; btn.style.borderColor = "#7a3a00"; btn.style.color = "#ffd966";
            if (banner) banner.style.display = "none";
            addLog("crime", "▶ Retomado");
        }
    }
    document.getElementById("tph-pause").onclick = () => { setPaused(!isPaused()); updatePauseUI(); };
    updatePauseUI();

    /* ── Start manual ── */
    document.getElementById("tph-start").onclick = () => {
        if (isPickpocketPage()) { runPickpocketLoop(); }
        else { addLog("warn", "⚠️ Navega para a página de pickpocket primeiro!"); }
    };

    /* ── Tabs ── */
    const TAB_NAMES = ["nerve","alvos","drops","stats","logs"];
    function switchTab(name) {
        TAB_NAMES.forEach(t => {
            document.getElementById(`tph-tab-${t}`).style.display = t === name ? "block" : "none";
        });
        document.querySelectorAll(".tph-tab").forEach(b => b.classList.toggle("active", b.dataset.tab === name));
    }
    document.querySelectorAll(".tph-tab").forEach(b => b.addEventListener("click", () => switchTab(b.dataset.tab)));
    switchTab("nerve");

    /* ── Modo sessão ── */
    function updateSessionModeUI() {
        const m = getSessionMode();
        const bw = document.getElementById("tph-mode-wait"), bl = document.getElementById("tph-mode-leave");
        if (bw) bw.style.background = m === "wait"  ? "#0e4a0e" : "#1a3a1a";
        if (bl) bl.style.background = m === "leave" ? "#4a0e0e" : "#1a1a2e";
        const desc = document.getElementById("tph-session-desc");
        if (desc) desc.textContent = m === "wait"
            ? "🟢 Fica na página mesmo sem nerve. Ataca assim que aparecer alvo prioritário com ≥5 nerve."
            : "🔴 Sai da sessão quando nerve < 5.";
    }
    document.getElementById("tph-mode-wait").onclick  = () => { setSessionMode("wait");  updateSessionModeUI(); };
    document.getElementById("tph-mode-leave").onclick = () => { setSessionMode("leave"); updateSessionModeUI(); };
    updateSessionModeUI();

    /* ── Botões ── */
    document.getElementById("tph-clear-logs").onclick  = () => clearLogs();
    document.getElementById("tph-clear-drops").onclick = () => { if (confirm("Repor drops?")) { localStorage.removeItem(DROP_STATS_KEY); renderDropStats(); } };
    document.getElementById("tph-reset-stats").onclick = () => { if (confirm("Repor estatísticas?")) { localStorage.removeItem(STATS_KEY); updateStatsDisplay(); } };

    /* ══════════════════════════════════════════════════════
       STATUS BADGE (header)
    ══════════════════════════════════════════════════════ */
    function updateStatusBadge(text, color = "#3a3a6a") {
        const el = document.getElementById("tph-status-badge");
        if (el) { el.textContent = text; el.style.color = color; }
    }

    /* ══════════════════════════════════════════════════════
       NERVE DISPLAY
    ══════════════════════════════════════════════════════ */
    function updateNerveDisplay() {
        const valEl   = document.getElementById("tph-nerve-val");
        const phaseEl = document.getElementById("tph-nerve-phase");
        const barEl   = document.getElementById("tph-nerve-bar");
        const subEl   = document.getElementById("tph-nerve-sub");
        const cntEl   = document.getElementById("tph-counter-display");
        if (!valEl) return;

        valEl.textContent = `${currentNerve}/${currentNerveMax}`;
        const pct = currentNerveMax > 0 ? (currentNerve / currentNerveMax) * 100 : 0;

        if (currentNerve >= currentNerveMax && currentNerveMax > 0) {
            valEl.style.color   = "#00e676";
            phaseEl.textContent = "nerve cheio!"; phaseEl.style.color = "#00e676";
            barEl.style.width   = "100%"; barEl.style.background = "#00e676";
            subEl.textContent   = "Pronto para atacar";
        } else if (currentNerve >= 5) {
            valEl.style.color   = "#d0d0e8";
            phaseEl.textContent = `${currentNerveMax - currentNerve} em falta`; phaseEl.style.color = "#3a3a6a";
            barEl.style.width   = pct + "%"; barEl.style.background = pct > 70 ? "#7c3dd6" : "#4a1a90";
            subEl.textContent   = `${Math.round(pct)}% — nerve suficiente para atacar`;
        } else {
            valEl.style.color   = "#ff6666";
            phaseEl.textContent = "nerve baixo"; phaseEl.style.color = "#ff4444";
            barEl.style.width   = pct + "%"; barEl.style.background = "#4a1a1a";
            subEl.textContent   = `Precisa de ≥5 nerve para atacar`;
        }

        // Contador anti-padrão
        if (cntEl) {
            const nextRandom = 4 - (nervePickCounter % 4);
            cntEl.textContent = nextRandom === 4 ? "4 (próximo é aleatório!)" : nextRandom;
            cntEl.style.color = nextRandom === 1 ? "#ffd966" : "#6c5ce7";
        }
    }

    /* ══════════════════════════════════════════════════════
       TAB ALVOS — FILTROS DE STATUS E BUILD
    ══════════════════════════════════════════════════════ */
    function renderVictimFilters() {
        const wrap = document.getElementById("tph-victims-wrap");
        if (!wrap) return;
        wrap.innerHTML = "";
        const filters = getTargetFilters();

        VICTIM_DB.forEach(v => {
            const vf = filters[v.id] || buildDefaultFilters()[v.id];

            const card = document.createElement("div");
            card.className = "tph-card";
            card.style.marginBottom = "10px";

            /* Header do alvo */
            card.innerHTML = `
                <div style="display:flex;align-items:center;gap:8px;margin-bottom:10px;">
                    <span style="font-size:24px;">${v.emoji}</span>
                    <div style="flex:1;">
                        <div style="font-weight:800;font-size:14px;color:#c0c0e0;">${v.label}</div>
                        <div style="font-size:9px;color:#3a3a6a;margin-top:2px;">$${v.cashMin.toLocaleString()}–$${v.cashMax.toLocaleString()} · ⏱~${v.timerSec}s · ${v.riskHosp ? "🏥 risco hosp" : ""}</div>
                    </div>
                </div>
                ${v.requireRazor ? `<div style="background:#0c0c1a;border-radius:5px;padding:6px 8px;margin-bottom:10px;border-left:2px solid #ffd966;font-size:10px;color:#ffd966;">⭐ ${v.unique} &nbsp;<span style="color:#6c5ce7;">✂️ Razor no inv.</span></div>` : ""}
                ${v.notes ? `<div style="font-size:9px;color:#2a2a4a;margin-bottom:8px;line-height:1.5;">${v.notes}</div>` : ""}`;

            /* Secção de status */
            const statusSec = document.createElement("div");
            statusSec.innerHTML = `<div class="tph-label" style="margin-bottom:6px;">🎭 Atacar quando status:</div>`;
            const statusGrid = document.createElement("div");
            statusGrid.style.cssText = "display:grid;grid-template-columns:1fr 1fr;gap:3px;margin-bottom:6px;";

            ALL_STATUSES.forEach(s => {
                const inVictim = v.statuses.includes(s.id);
                const checked  = vf.statuses?.[s.id] === true;
                const multColor = s.mult >= 1.2 ? "#00e676" : s.mult >= 1.0 ? "#ffd966" : "#ff6666";

                const label = document.createElement("label");
                label.style.cssText = `display:flex;align-items:center;gap:4px;padding:4px 6px;border-radius:4px;cursor:${inVictim ? "pointer" : "default"};background:${inVictim && checked ? "#0e1e0e" : "#080812"};border:1px solid ${inVictim ? (checked ? "#2a4a2a" : "#1a1a2e") : "#0e0e18"};opacity:${inVictim ? "1" : "0.3"};font-size:10px;`;
                label.innerHTML = `<input type="checkbox" ${checked ? "checked" : ""} ${!inVictim ? "disabled" : ""} data-vid="${v.id}" data-sid="${s.id}" class="tph-status-cb" style="cursor:${inVictim ? "pointer" : "default"};"><span>${s.emoji} ${s.label}</span><span style="margin-left:auto;font-size:9px;color:${multColor};">×${s.mult.toFixed(1)}</span>`;
                statusGrid.appendChild(label);
            });
            statusSec.appendChild(statusGrid);

            /* Botões atalho */
            const sBtns = document.createElement("div");
            sBtns.style.cssText = "display:flex;gap:4px;margin-bottom:10px;";
            sBtns.innerHTML = `
                <button class="tph-btn tph-s-all" data-vid="${v.id}"  style="flex:1;padding:4px;font-size:9px;background:#0e2a0e;border:1px solid #1a4a1a;">✓ Todos</button>
                <button class="tph-btn tph-s-none" data-vid="${v.id}" style="flex:1;padding:4px;font-size:9px;background:#2a0e0e;border:1px solid #4a1a1a;">✗ Nenhum</button>
                <button class="tph-btn tph-s-good" data-vid="${v.id}" style="flex:1;padding:4px;font-size:9px;background:#0e0e2e;border:1px solid #2a2a5a;">★ Bons</button>`;
            statusSec.appendChild(sBtns);
            card.appendChild(statusSec);

            /* Secção de build */
            const buildSec = document.createElement("div");
            buildSec.innerHTML = `<div class="tph-label" style="margin-bottom:6px;">🧍 Atacar quando físico:</div>`;
            const buildGrid = document.createElement("div");
            buildGrid.style.cssText = "display:flex;flex-wrap:wrap;gap:4px;margin-bottom:4px;";

            ALL_BUILDS.forEach(b => {
                const checked     = vf.builds?.[b.id] === true;
                const isExclusive = v.requireBuild === b.id;
                const label = document.createElement("label");
                label.style.cssText = `display:flex;align-items:center;gap:4px;padding:4px 8px;border-radius:4px;cursor:pointer;background:${checked ? "#0e1e0e" : "#080812"};border:1px solid ${checked ? (isExclusive ? "#6c5ce7" : "#2a4a2a") : "#1a1a2e"};font-size:10px;`;
                label.innerHTML = `<input type="checkbox" ${checked ? "checked" : ""} data-vid="${v.id}" data-bid="${b.id}" class="tph-build-cb" style="cursor:pointer;"><span>${b.emoji} ${b.label}${isExclusive ? ` <span style="color:#6c5ce7;font-size:9px;">★</span>` : ""}</span>`;
                buildGrid.appendChild(label);
            });
            buildSec.appendChild(buildGrid);
            buildSec.innerHTML += `<div style="font-size:9px;color:#1e1e3a;margin-bottom:6px;">★ Build com drops exclusivos</div>`;
            card.appendChild(buildSec);

            /* Resumo activo */
            const summary = document.createElement("div");
            summary.className = `tph-sum-${v.id}`;
            summary.style.cssText = "background:#060610;border-radius:4px;padding:5px 8px;font-size:9px;color:#3a3a6a;line-height:1.6;";
            card.appendChild(summary);

            wrap.appendChild(card);

            /* Eventos */
            card.querySelectorAll(".tph-status-cb").forEach(cb => {
                cb.addEventListener("change", () => { toggleStatusFilter(cb.dataset.vid, cb.dataset.sid); refreshVictimSummary(cb.dataset.vid); });
            });
            card.querySelectorAll(".tph-build-cb").forEach(cb => {
                cb.addEventListener("change", () => { toggleBuildFilter(cb.dataset.vid, cb.dataset.bid); refreshVictimSummary(cb.dataset.vid); });
            });
            card.querySelector(".tph-s-all").addEventListener("click", () => {
                const f = getTargetFilters();
                v.statuses.forEach(s => { f[v.id].statuses[s] = true; }); saveTargetFilters(f); renderVictimFilters();
            });
            card.querySelector(".tph-s-none").addEventListener("click", () => {
                const f = getTargetFilters();
                ALL_STATUSES.forEach(s => { f[v.id].statuses[s.id] = false; }); saveTargetFilters(f); renderVictimFilters();
            });
            card.querySelector(".tph-s-good").addEventListener("click", () => {
                const f = getTargetFilters();
                ALL_STATUSES.forEach(s => { f[v.id].statuses[s.id] = v.goodStatus.includes(s.id); }); saveTargetFilters(f); renderVictimFilters();
            });

            refreshVictimSummary(v.id);
        });
    }

    function refreshVictimSummary(victimId) {
        const v   = VICTIM_DB.find(x => x.id === victimId);
        const f   = getTargetFilters();
        const vf  = f[victimId] || buildDefaultFilters()[victimId];
        const el  = document.querySelector(`.tph-sum-${victimId}`);
        if (!v || !el) return;
        const activeS = ALL_STATUSES.filter(s => v.statuses.includes(s.id) && vf.statuses?.[s.id]);
        const activeB = ALL_BUILDS.filter(b => vf.builds?.[b.id]);
        el.innerHTML = (activeS.length && activeB.length)
            ? `🎯 Status aceites: <span style="color:#00e676;">${activeS.map(s => s.emoji + " " + s.label).join(", ")}</span><br>🧍 Builds aceites: <span style="color:#6c5ce7;">${activeB.map(b => b.emoji + " " + b.label).join(", ")}</span>`
            : `<span style="color:#ff4444;">⚠️ Nenhum estado/build seleccionado — não vai atacar!</span>`;
    }

    renderVictimFilters();

    /* ══════════════════════════════════════════════════════
       DROPS DISPLAY
    ══════════════════════════════════════════════════════ */
    function renderDropStats() {
    const wrap = document.getElementById("tph-drops-list");
    if (!wrap) return;

    const data = getDropStats();

    if (!Object.keys(data).length) {
        wrap.innerHTML = `<div style="color:#1e1e3a;text-align:center;padding:20px;">Sem drops ainda.</div>`;
        return;
    }

    wrap.innerHTML = Object.entries(data).map(([victimId, drops]) => {
        const victim = VICTIM_DB.find(v => v.id === victimId);
        const total = Object.values(drops).reduce((a,b)=>a+b,0);

        return `
        <div style="margin-bottom:10px;">
            <div style="font-weight:800;color:#6c5ce7;margin-bottom:4px;">
                ${victim ? victim.emoji + " " + victim.label : victimId}
                <span style="font-size:9px;color:#2a2a4a;">(${total})</span>
            </div>
            ${
                Object.entries(drops).map(([dropId,count])=>{
                    const drop = VICTIM_DROPS[victimId]?.find(d=>d.id===dropId);
                    if (!drop) return "";
                    return `<div style="font-size:11px;padding-left:10px;">
                        ${drop.emoji} ${drop.name}
                        <span style="color:#6c5ce7;">x${count}</span>
                    </div>`;
                }).join("")
            }
        </div>`;
    }).join("");
}

    /* ══════════════════════════════════════════════════════
       STATS DISPLAY
    ══════════════════════════════════════════════════════ */
    function updateStatsDisplay() {
        const s   = getStats();
        const pp  = s.pickpockets;
        const set = (id, v) => { const el = document.getElementById(id); if (el) el.textContent = v; };
        set("st-count", pp.count);
        set("st-nerve", pp.nerveSpent);
        set("st-succ",  pp.count > 0 ? Math.round((pp.successCount / pp.count) * 100) + "%" : "—");

        const bvEl = document.getElementById("st-by-victim");
        if (bvEl) {
            bvEl.innerHTML = Object.entries(pp.byVictim).map(([id, data]) => {
                const v = VICTIM_DB.find(x => x.id === id);
                const pct = data.count > 0 ? Math.round((data.success / data.count) * 100) : 0;
                return `<div style="display:flex;justify-content:space-between;background:#080812;padding:5px 8px;border-radius:4px;border-left:2px solid #1a1a2e;">
                    <span>${v ? v.emoji + " " + v.label : id}</span>
                    <span style="color:#6c5ce7;">${data.count}× <span style="color:#2a2a5a;font-size:9px;">${pct}%</span></span>
                </div>`;
            }).join("") || `<div style="color:#1e1e3a;text-align:center;padding:10px;">Sem dados ainda.</div>`;
        }
    }
    updateStatsDisplay();

    /* ══════════════════════════════════════════════════════
       LOGS DISPLAY
    ══════════════════════════════════════════════════════ */
    function renderLogs() {
        const c = document.getElementById("tph-logs-list");
        if (!c) return;
        const logs = getLogs();
        c.innerHTML = "";
        if (!logs.length) { c.innerHTML = `<div style="color:#1e1e3a;text-align:center;padding:20px;font-size:11px;">Sem registos.</div>`; return; }
        logs.forEach(e => {
            const el = document.createElement("div");
            el.className = "tph-log";
            el.style.borderLeftColor = CAT_CLR[e.category] || "#1a1a2e";
            el.innerHTML = `<span class="tph-log-ts">${e.ts}</span>${e.msg}`;
            c.appendChild(el);
        });
    }
    renderLogs();

    /* ══════════════════════════════════════════════════════
       TICK — ACTUALIZA NERVE DISPLAY 1×/s
    ══════════════════════════════════════════════════════ */
    setInterval(() => {
        readNerveFromDOM();
        updateNerveDisplay();
    }, 1000);

    /* Fetch nerve da API a cada 30s */
    setInterval(fetchNerveFromAPI, 30000);

    /* ══════════════════════════════════════════════════════
       ARRANQUE AUTOMÁTICO
       Se já estamos na página de pickpocket → começa sozinho
    ══════════════════════════════════════════════════════ */
    async function init() {
        await fetchNerveFromAPI();
        updateNerveDisplay();
        updateStatsDisplay();

        if (isPickpocketPage()) {
            addLog("system", "🟢 HUD iniciado — página de pickpocket detectada — a arrancar sessão");
            updateStatusBadge("🔄 A iniciar…", "#6c5ce7");
            await delay(1500);
            runPickpocketLoop();
        } else if (isCrimesPage()) {
            addLog("system", "⚠️ Página de crimes detectada — navega para #/pickpocketing para iniciar");
            updateStatusBadge("⚠️ Navega para pickpocket", "#ffd966");
            // Aguarda hash change para pickpocket
            window.addEventListener("hashchange", () => {
                if (isPickpocketPage() && !pickpocketRunning) {
                    addLog("system", "✅ Pickpocket detectado via hashchange — a arrancar");
                    runPickpocketLoop();
                }
            });
        }
    }

    init();
    addLog("system", "🟣 Pickpocket HUD v1.0 carregado — apenas crimes/pickpocketing");

})();