Bonk Enhanced

Bonk Enhanced — tracker, encrypted account manager, keybind overlay, stats, fullscreen and a recording option in the topbar of bonk itself

Tendrás que instalar una extensión para tu navegador como Tampermonkey, Greasemonkey o Violentmonkey si quieres utilizar este script.

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

Tendrás que instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Userscripts para instalar este script.

Tendrás que instalar una extensión como Tampermonkey antes de poder instalar este script.

Necesitarás instalar una extensión para administrar scripts de usuario si quieres instalar este script.

(Ya tengo un administrador de scripts de usuario, déjame instalarlo)

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

(Ya tengo un administrador de estilos de usuario, déjame instalarlo)

// ==UserScript==
// @name         Bonk Enhanced
// @namespace    Bonk Enhanced
// @version      3.5.1
// @description  Bonk Enhanced — tracker, encrypted account manager, keybind overlay, stats, fullscreen and a recording option in the topbar of bonk itself
// @match        https://bonk.io/*
// @match        https://bonk.io/gameframe-release.html*
// @grant        none
// @run-at       document-start
// ==/UserScript==

(function () {
    'use strict';

    /** Keep in sync with // @version in the userscript header. */
    const BE_SCRIPT_VERSION = "3.5.1";
    const BE_SCRIPT_AUTHOR = "Elysiun";

    const UI = {};
    const UI_SETTINGS_KEY = "be_ui_settings_v5";
    const UI_DEFAULT_SETTINGS = {
        theme: "dark",
        size: "medium",
        overlayHotkey: "Delete",
        widgetHotkey: "Home",
        keybindOverlayEnabled: false,
        keybindOverlayOpacity: 1,
        keybindOverlayScale: 1,
        keybindOverlayCompact: false,
        graphWeek: "",
        overviewSlots: {
            tiny: ["wins", "rate", "lastWin"],
            medium: ["xp", "wins", "rate", "lastWin"],
            large: ["xp", "wins", "rate", "lastWin", "session", "level"]
        },
        dashboardStats: {
            xp: true,
            wins: true,
            rate: true,
            lastWin: true
        },
        uiPresets: []
    };
    let uiSettings = loadUISettings();
    let isCapturingHotkey = false;
    const isGameFrame = window.location.href.includes("gameframe-release.html");
    const BE_CONFIG = {
        env: localStorage.getItem("be_env") || "test",
        debug: localStorage.getItem("be_debug") === "1",
        featureFlags: {
            enableOuterMods: true,
            enableTracker: true,
            safeModeGuards: true
        }
    };
    const BE_HEALTH = {
        scriptBoot: { ok: true, detail: "started", ts: Date.now() }
    };

    function markHealth(key, ok, detail) {
        BE_HEALTH[key] = { ok: !!ok, detail: detail || "", ts: Date.now() };
    }

    const BE_LOG = {
        info(...args) { console.info("[BonkEnhanced]", ...args); },
        warn(...args) { console.warn("[BonkEnhanced]", ...args); },
        error(...args) { console.error("[BonkEnhanced]", ...args); },
        debug(...args) {
            if (BE_CONFIG.debug || BE_CONFIG.env === "test") {
                console.debug("[BonkEnhanced:debug]", ...args);
            }
        }
    };

    function safeRun(key, fn) {
        try {
            const out = fn();
            markHealth(key, true, "ok");
            return out;
        } catch (err) {
            markHealth(key, false, err?.message || "failed");
            BE_LOG.error(`${key} failed`, err);
            return null;
        }
    }

    function beTabHidden() {
        try {
            return document.visibilityState === "hidden";
        } catch {
            return false;
        }
    }

    /** True when the custom games room list UI is visible (top window or #maingameframe iframe). */
    function beIsBonkRoomListVisible() {
        try {
            const vis = (el) => el && el.offsetParent !== null;
            if (vis(document.getElementById("roomListContainer"))) return true;
            const f = document.getElementById("maingameframe");
            const inner = f?.contentDocument?.getElementById("roomListContainer");
            return vis(inner);
        } catch {
            return false;
        }
    }

    function beBackoffMs(base, mult = 4, cap = 120000) {
        if (!beTabHidden()) return base;
        return Math.min(cap, Math.max(base, Math.floor(base * mult)));
    }

    /** Bump when Bonk DOM hook lists change (health / diagnostics). */
    const BE_SELECTOR_SCHEMA = "bonk_dom_v1";

    function loadUISettings() {
        try {
            const raw = localStorage.getItem(UI_SETTINGS_KEY);
            if (!raw) return { ...UI_DEFAULT_SETTINGS };
            const parsed = JSON.parse(raw);
            return {
                theme: parsed?.theme === "light" ? "light" : "dark",
                size: ["tiny", "medium", "large"].includes(parsed?.size) ? parsed.size : "medium",
                overlayHotkey: typeof parsed?.overlayHotkey === "string" && parsed.overlayHotkey.trim()
                    ? parsed.overlayHotkey
                    : "Delete",
                widgetHotkey: typeof parsed?.widgetHotkey === "string" && parsed.widgetHotkey.trim()
                    ? parsed.widgetHotkey
                    : "Home",
                keybindOverlayEnabled: parsed?.keybindOverlayEnabled === true,
                graphWeek: typeof parsed?.graphWeek === "string" ? parsed.graphWeek : "",
                overviewSlots: {
                    tiny: Array.isArray(parsed?.overviewSlots?.tiny) ? parsed.overviewSlots.tiny.slice(0, 3) : ["wins", "rate", "lastWin"],
                    medium: Array.isArray(parsed?.overviewSlots?.medium) ? parsed.overviewSlots.medium.slice(0, 4) : ["xp", "wins", "rate", "lastWin"],
                    large: Array.isArray(parsed?.overviewSlots?.large) ? parsed.overviewSlots.large.slice(0, 6) : ["xp", "wins", "rate", "lastWin", "session", "level"]
                },
                dashboardStats: {
                    xp: parsed?.dashboardStats?.xp !== false,
                    wins: parsed?.dashboardStats?.wins !== false,
                    rate: parsed?.dashboardStats?.rate !== false,
                    lastWin: parsed?.dashboardStats?.lastWin !== false
                },
                keybindOverlayOpacity: typeof parsed?.keybindOverlayOpacity === "number"
                    ? Math.min(1, Math.max(0.35, parsed.keybindOverlayOpacity))
                    : 1,
                keybindOverlayScale: typeof parsed?.keybindOverlayScale === "number"
                    ? Math.min(1.35, Math.max(0.65, parsed.keybindOverlayScale))
                    : 1,
                keybindOverlayCompact: parsed?.keybindOverlayCompact === true,
                uiPresets: Array.isArray(parsed?.uiPresets)
                    ? parsed.uiPresets.filter((p) => p && typeof p === "object" && p.name && p.data).slice(0, 8)
                    : []
            };
        } catch {
            return { ...UI_DEFAULT_SETTINGS };
        }
    }

    function saveUISettings() {
        localStorage.setItem(UI_SETTINGS_KEY, JSON.stringify(uiSettings));
    }

    function applyOverlaySettings() {
        if (!UI.root) return;
        UI.root.classList.toggle("be-theme-light", uiSettings.theme === "light");
        UI.root.classList.toggle("be-theme-dark", uiSettings.theme !== "light");
        UI.root.dataset.size = uiSettings.size;

        const hotkeyLabel = document.getElementById("beHotkeyCurrent");
        if (hotkeyLabel) hotkeyLabel.textContent = uiSettings.overlayHotkey;
        const widgetHotkeyLabel = document.getElementById("beWidgetHotkeyCurrent");
        if (widgetHotkeyLabel) widgetHotkeyLabel.textContent = uiSettings.widgetHotkey;
        if (UI.launcherLabel) UI.launcherLabel.textContent = uiSettings.overlayHotkey;
        const topLauncherHk = document.getElementById("pretty_top_be_launcher_hk");
        if (topLauncherHk) topLauncherHk.textContent = uiSettings.overlayHotkey;
        if (UI.widgetHubHint) UI.widgetHubHint.textContent = uiSettings.widgetHotkey;
        applyKeybindOverlayVisibility();
    }

    /** Physical key (event.code) → action slots currently held by that key (shared keys light multiple cells). */
    const BE_KB_PRESSED = new Map();
    let __beKeybindOverlayListenersBound = false;
    let __beKeybindPollTimer = null;

    /** Match Bonk’s “Key 1/2/3” display strings to the same style from a KeyboardEvent. */
    function beBonkStyleKeyLabel(ev) {
        if (!ev) return "";
        const k = ev.key;
        const c = ev.code || "";
        if (k === "ArrowUp") return "UP ARROW";
        if (k === "ArrowDown") return "DOWN ARROW";
        if (k === "ArrowLeft") return "LEFT ARROW";
        if (k === "ArrowRight") return "RIGHT ARROW";
        if (k === " " || c === "Space") return "SPACE";
        if (k === "Shift") return "SHIFT";
        if (k === "Control") return "CONTROL";
        if (k === "Alt") return "ALT";
        if (k === "Meta") return "META";
        if (k === "Enter") return "ENTER";
        if (k === "Tab") return "TAB";
        if (k === "Backspace") return "BACKSPACE";
        if (k === "Delete") return "DELETE";
        if (k === "Escape") return "ESCAPE";
        if (/^Numpad\d$/.test(c)) return "NUMPAD " + c.replace("Numpad", "");
        if (k && k.length === 1) return k.toUpperCase();
        return String(k || c || "").toUpperCase();
    }

    function beMatcherFromBonkLabel(label) {
        const raw = String(label || "")
            .replace(/\s+/g, " ")
            .trim();
        if (!raw) return null;
        const u = raw.toUpperCase();
        if (u === "LEFT ARROW") return (e) => e.key === "ArrowLeft";
        if (u === "RIGHT ARROW") return (e) => e.key === "ArrowRight";
        if (u === "UP ARROW") return (e) => e.key === "ArrowUp";
        if (u === "DOWN ARROW") return (e) => e.key === "ArrowDown";
        if (u === "SPACE" || u === "SPACEBAR") return (e) => e.code === "Space" || e.key === " ";
        if (u === "SHIFT") return (e) => e.key === "Shift";
        if (u === "CONTROL" || u === "CTRL") return (e) => e.key === "Control";
        if (u === "ALT") return (e) => e.key === "Alt";
        if (u === "META" || u === "WINDOWS" || u === "WIN") return (e) => e.key === "Meta";
        if (u === "ENTER" || u === "RETURN") return (e) => e.key === "Enter";
        if (u === "TAB") return (e) => e.key === "Tab";
        if (u === "BACKSPACE") return (e) => e.key === "Backspace";
        if (u === "DELETE" || u === "DEL") return (e) => e.key === "Delete";
        if (u === "ESCAPE" || u === "ESC") return (e) => e.key === "Escape";
        const np = u.match(/^NUMPAD\s*(\d)$/);
        if (np) {
            const code = "Numpad" + np[1];
            return (e) => e.code === code;
        }
        if (/^F([1-9]|1[0-2])$/i.test(raw)) return (e) => e.key.toUpperCase() === u;
        if (raw.length === 1) return (e) => e.key && e.key.length === 1 && e.key.toUpperCase() === u;
        return (e) => beBonkStyleKeyLabel(e) === u;
    }

    function beDefaultKeybindMatchers() {
        return {
            left: [(e) => e.key === "ArrowLeft"],
            right: [(e) => e.key === "ArrowRight"],
            up: [(e) => e.key === "ArrowUp"],
            down: [(e) => e.key === "ArrowDown"],
            heavy: [(e) => e.key === "x" || e.key === "X"],
            special: [(e) => {
                const x = e.key;
                return x === "z" || x === "Z" || x === "y" || x === "Y";
            }]
        };
    }

    let BE_KEYBIND_MATCHERS = beDefaultKeybindMatchers();

    function beGetBonkGameDocument() {
        try {
            const href = window.location.href || "";
            if (href.indexOf("gameframe-release.html") >= 0) return document;
            const f = document.getElementById("maingameframe");
            if (f && f.contentDocument) return f.contentDocument;
        } catch (e) {
            BE_LOG.debug("beGetBonkGameDocument", e);
        }
        return null;
    }

    function beFindRedefineControlsTable(doc) {
        if (!doc) return null;
        const ids = ["redefineControls_table", "bonk_redefineControls_table", "redefine_controls_table"];
        for (let i = 0; i < ids.length; i++) {
            const t = doc.getElementById(ids[i]);
            if (t) {
                markHealth("bonk.redefineTableNode", true, `${BE_SELECTOR_SCHEMA}:${ids[i]}`);
                return t;
            }
        }
        try {
            const q = doc.querySelector("[id*='edefineControl'][id*='table'], table[id*='controls_table']");
            if (q) {
                markHealth("bonk.redefineTableNode", true, `${BE_SELECTOR_SCHEMA}:fuzzy`);
                return q;
            }
        } catch (e) {
            BE_LOG.debug("beFindRedefineControlsTable fuzzy", e);
        }
        markHealth("bonk.redefineTableNode", false, `${BE_SELECTOR_SCHEMA}:not found`);
        return null;
    }

    function beParseRedefineControlsTable(doc) {
        const table = beFindRedefineControlsTable(doc);
        if (!table) return null;
        const slotByAction = {
            left: "left",
            right: "right",
            up: "up",
            down: "down",
            heavy: "heavy",
            special: "special"
        };
        const matchers = {
            left: [],
            right: [],
            up: [],
            down: [],
            heavy: [],
            special: []
        };
        let count = 0;
        table.querySelectorAll("tr").forEach((tr) => {
            const tds = tr.querySelectorAll("td");
            if (tds.length < 4) return;
            const actionText = (tds[0].textContent || "").trim().toLowerCase();
            const slot = slotByAction[actionText];
            if (!slot) return;
            for (let c = 1; c <= 3; c++) {
                const cellText = (tds[c].textContent || "").replace(/\s+/g, " ").trim();
                if (!cellText) continue;
                const m = beMatcherFromBonkLabel(cellText);
                if (m) {
                    matchers[slot].push(m);
                    count += 1;
                }
            }
        });
        if (count === 0) return null;
        return { matchers };
    }

    /** If Bonk’s table omits a row or a slot has no keys, keep default matchers for that slot so highlights still work. */
    function beCoalesceMatchersWithDefaults(parsedMatchers) {
        const d = beDefaultKeybindMatchers();
        const slots = ["left", "right", "up", "down", "heavy", "special"];
        const out = {};
        slots.forEach((slot) => {
            const c = parsedMatchers && parsedMatchers[slot] && parsedMatchers[slot].length ? parsedMatchers[slot] : null;
            out[slot] = c && c.length ? c.slice() : (d[slot] ? d[slot].slice() : []);
        });
        return out;
    }

    function beGetKeybindOverlayRoot() {
        try {
            const a = document.getElementById("beKeybindOverlay");
            if (a) return a;
            if (window.top && window.top !== window && window.top.document) {
                return window.top.document.getElementById("beKeybindOverlay");
            }
        } catch (e) {
            BE_LOG.debug("beGetKeybindOverlayRoot", e);
        }
        return null;
    }

    function beIsKeybindOverlayShown(root) {
        if (!root) return false;
        try {
            if (root.style && root.style.display === "none") return false;
            return window.getComputedStyle(root).display !== "none";
        } catch {
            return true;
        }
    }

    /** Overlay cells always show action names, not physical keys (matchers still come from Bonk’s table). */
    function beApplyKeybindOverlayActionLabels() {
        const root = beGetKeybindOverlayRoot();
        if (!root) return;
        const wide = { up: "UP", left: "LEFT", down: "DOWN", right: "RIGHT", heavy: "HEAVY", special: "SPECIAL" };
        const narrow = { up: "U", left: "L", down: "D", right: "R", heavy: "H", special: "S" };
        const cap = root.classList.contains("be-kb-compact") ? narrow : wide;
        Object.keys(wide).forEach((slot) => {
            root.querySelectorAll(`[data-be-kb="${slot}"] span`).forEach((el) => {
                el.textContent = cap[slot];
            });
        });
    }

    const BE_KB_OVERLAY_POS_KEY = "be_keybind_overlay_pos_v1";

    function beInitKeybindOverlayDrag(overlayEl) {
        if (!overlayEl || overlayEl.__beKbDragInit) return;
        overlayEl.__beKbDragInit = true;
        const handle = overlayEl.querySelector("#beKbDragHandle");
        if (!handle) return;

        try {
            const raw = localStorage.getItem(BE_KB_OVERLAY_POS_KEY);
            if (raw) {
                const p = JSON.parse(raw);
                if (typeof p.left === "number" && typeof p.top === "number") {
                    overlayEl.style.left = p.left + "px";
                    overlayEl.style.top = p.top + "px";
                    overlayEl.style.bottom = "auto";
                    overlayEl.style.right = "auto";
                }
            }
        } catch (e) {
            BE_LOG.debug("beInitKeybindOverlayDrag load pos", e);
        }

        let drag = null;
        handle.addEventListener("mousedown", (e) => {
            if (e.button !== 0) return;
            e.preventDefault();
            e.stopPropagation();
            const r = overlayEl.getBoundingClientRect();
            drag = {
                dx: e.clientX - r.left,
                dy: e.clientY - r.top,
                w: r.width,
                h: r.height
            };
            overlayEl.style.bottom = "auto";
            overlayEl.style.right = "auto";
            overlayEl.style.left = r.left + "px";
            overlayEl.style.top = r.top + "px";
            handle.classList.add("be-kb-dragging");
            try {
                document.body.style.userSelect = "none";
            } catch {}
        });

        const move = (e) => {
            if (!drag) return;
            let nl = e.clientX - drag.dx;
            let nt = e.clientY - drag.dy;
            const pad = 6;
            const maxL = Math.max(pad, window.innerWidth - drag.w - pad);
            const maxT = Math.max(pad, window.innerHeight - drag.h - pad);
            nl = Math.min(maxL, Math.max(pad, nl));
            nt = Math.min(maxT, Math.max(pad, nt));
            overlayEl.style.left = nl + "px";
            overlayEl.style.top = nt + "px";
        };

        const up = () => {
            if (!drag) return;
            drag = null;
            handle.classList.remove("be-kb-dragging");
            try {
                document.body.style.userSelect = "";
            } catch {}
            try {
                const l = parseFloat(overlayEl.style.left) || 0;
                const t = parseFloat(overlayEl.style.top) || 0;
                localStorage.setItem(BE_KB_OVERLAY_POS_KEY, JSON.stringify({ left: l, top: t }));
            } catch (err) {
                BE_LOG.debug("beInitKeybindOverlayDrag save pos", err);
            }
        };

        document.addEventListener("mousemove", move);
        document.addEventListener("mouseup", up);
    }

    function beAttachRedefineTableObserver(doc) {
        if (!doc || doc.__beKeybindTableHooked) return;
        const table = beFindRedefineControlsTable(doc);
        if (!table) return;
        doc.__beKeybindTableHooked = true;
        try {
            doc.__beKeybindTableMo = new MutationObserver(() => {
                if (uiSettings.keybindOverlayEnabled) refreshBonkKeybindOverlayFromGame();
            });
            doc.__beKeybindTableMo.observe(table, { childList: true, subtree: true, characterData: true });
        } catch (e) {
            BE_LOG.debug("beAttachRedefineTableObserver", e);
        }
    }

    function refreshBonkKeybindOverlayFromGame() {
        let parsed = null;
        try {
            const doc = beGetBonkGameDocument();
            if (doc) {
                beAttachRedefineTableObserver(doc);
                parsed = beParseRedefineControlsTable(doc);
            }
        } catch (e) {
            BE_LOG.debug("refreshBonkKeybindOverlayFromGame", e);
        }
        if (parsed && parsed.matchers) {
            BE_KEYBIND_MATCHERS = beCoalesceMatchersWithDefaults(parsed.matchers);
            markHealth("bonk.keybindParse", true, `${BE_SELECTOR_SCHEMA}:parsed`);
        } else {
            BE_KEYBIND_MATCHERS = beDefaultKeybindMatchers();
            markHealth("bonk.keybindParse", false, `${BE_SELECTOR_SCHEMA}:defaults`);
        }
        beApplyKeybindOverlayActionLabels();
        beHookKeybindToMaingameframe();
    }

    /** All action slots this key event maps to (one physical key may map to several). */
    function beKeybindSlotsForEvent(ev) {
        const order = ["left", "right", "up", "down", "heavy", "special"];
        const out = [];
        for (let i = 0; i < order.length; i++) {
            const slot = order[i];
            const fns = BE_KEYBIND_MATCHERS[slot];
            if (!fns || !fns.length) continue;
            let hit = false;
            for (let j = 0; j < fns.length; j++) {
                try {
                    if (fns[j](ev)) {
                        hit = true;
                        break;
                    }
                } catch {}
            }
            if (hit) out.push(slot);
        }
        return out;
    }

    function beKbActiveSlotsUnion() {
        const on = new Set();
        BE_KB_PRESSED.forEach((slots) => {
            (slots || []).forEach((s) => on.add(s));
        });
        return on;
    }

    function beSyncKeybindOverlayHighlights() {
        if (!uiSettings.keybindOverlayEnabled) return;
        const root = beGetKeybindOverlayRoot();
        if (!root || !beIsKeybindOverlayShown(root)) return;
        const on = beKbActiveSlotsUnion();
        root.querySelectorAll("[data-be-kb]").forEach((node) => {
            const s = node.getAttribute("data-be-kb");
            if (!s) return;
            node.classList.toggle("be-kb-active", on.has(s));
        });
    }

    function applyKeybindOverlayVisibility() {
        let el = beGetKeybindOverlayRoot();
        if (uiSettings.keybindOverlayEnabled && !el) {
            ensureBonkKeybindOverlayMounted();
            el = beGetKeybindOverlayRoot();
        }
        const toggle = document.getElementById("beKeybindOverlayToggle");
        const st = document.getElementById("beKeybindOverlayState");
        if (toggle) toggle.checked = uiSettings.keybindOverlayEnabled === true;
        if (st) st.textContent = uiSettings.keybindOverlayEnabled ? "On" : "Off";
        if (el) {
            el.style.display = uiSettings.keybindOverlayEnabled ? "block" : "none";
            if (!uiSettings.keybindOverlayEnabled) {
                BE_KB_PRESSED.clear();
                el.querySelectorAll(".be-kb-active").forEach((n) => n.classList.remove("be-kb-active"));
            } else {
                try {
                    refreshBonkKeybindOverlayFromGame();
                } catch (e) {
                    BE_LOG.debug("applyKeybindOverlayVisibility refresh", e);
                }
            }
        }
        applyKeybindOverlayStyle();
    }

    function applyKeybindOverlayStyle() {
        const el = beGetKeybindOverlayRoot();
        if (!el) return;
        const op = typeof uiSettings.keybindOverlayOpacity === "number" ? uiSettings.keybindOverlayOpacity : 1;
        const sc = typeof uiSettings.keybindOverlayScale === "number" ? uiSettings.keybindOverlayScale : 1;
        el.style.opacity = String(op);
        el.style.setProperty("--be-kb-scale", String(sc));
        el.classList.toggle("be-kb-compact", uiSettings.keybindOverlayCompact === true);
        beApplyKeybindOverlayActionLabels();
    }

    function beEnsureKeybindKeyboardHandlers() {
        if (window.__beKbKeyHandlers) return;
        const onDown = (e) => {
            if (!uiSettings.keybindOverlayEnabled) return;
            if (isCapturingHotkey) return;
            const tag = e.target?.tagName?.toLowerCase?.() || "";
            if (tag === "input" || tag === "textarea" || e.target?.isContentEditable) return;
            const slots = beKeybindSlotsForEvent(e);
            if (!slots.length || e.repeat) return;
            let kc = e.code || "";
            if (!kc && e.key && e.key.length === 1) kc = "Key" + e.key.toUpperCase();
            if (!kc) return;
            BE_KB_PRESSED.set(kc, slots);
            beSyncKeybindOverlayHighlights();
        };
        const onUp = (e) => {
            if (!uiSettings.keybindOverlayEnabled) return;
            let kc = e.code || "";
            if (!kc && e.key && e.key.length === 1) kc = "Key" + e.key.toUpperCase();
            if (kc) BE_KB_PRESSED.delete(kc);
            beSyncKeybindOverlayHighlights();
        };
        const onBlur = () => {
            BE_KB_PRESSED.clear();
            beSyncKeybindOverlayHighlights();
        };
        window.__beKbKeyHandlers = { onDown, onUp, onBlur };
        window.addEventListener("keydown", onDown, true);
        window.addEventListener("keyup", onUp, true);
        window.addEventListener("blur", onBlur);
        document.addEventListener("visibilitychange", () => {
            if (document.visibilityState === "hidden") onBlur();
        });
    }

    /** While the canvas has focus, key events go to the iframe window — not the top page. */
    function beHookKeybindToMaingameframe() {
        const h = window.__beKbKeyHandlers;
        if (!h) return;
        try {
            if ((window.location.href || "").indexOf("gameframe-release.html") >= 0) return;
            const f = document.getElementById("maingameframe");
            const fw = f && f.contentWindow;
            if (!fw || fw.__beKbGameFrameHooked) return;
            fw.__beKbGameFrameHooked = true;
            fw.addEventListener("keydown", h.onDown, true);
            fw.addEventListener("keyup", h.onUp, true);
            fw.addEventListener("blur", h.onBlur);
            try {
                fw.document.addEventListener("visibilitychange", () => {
                    try {
                        if (fw.document.visibilityState === "hidden") h.onBlur();
                    } catch {}
                });
            } catch {}
        } catch (e) {
            BE_LOG.debug("beHookKeybindToMaingameframe", e);
        }
    }

    function beEnsureKeybindPollInterval() {
        if (__beKeybindPollTimer) {
            try {
                clearInterval(__beKeybindPollTimer);
            } catch {}
            __beKeybindPollTimer = null;
        }
        const ms = beTabHidden() ? 3600 : 900;
        __beKeybindPollTimer = window.setInterval(() => {
            if (!uiSettings.keybindOverlayEnabled) return;
            refreshBonkKeybindOverlayFromGame();
            beHookKeybindToMaingameframe();
        }, ms);
    }

    function bindBonkKeybindOverlayListeners() {
        beEnsureKeybindKeyboardHandlers();
        if (!__beKeybindOverlayListenersBound) {
            __beKeybindOverlayListenersBound = true;
            if (!window.__beKbPollVisHooked) {
                window.__beKbPollVisHooked = true;
                document.addEventListener("visibilitychange", () => beEnsureKeybindPollInterval());
            }
        }
        beEnsureKeybindPollInterval();
        beHookKeybindToMaingameframe();
        try {
            const f = document.getElementById("maingameframe");
            if (f && !f.__beKbFrameLoadHooked) {
                f.__beKbFrameLoadHooked = true;
                f.addEventListener("load", () => {
                    try {
                        const w = f.contentWindow;
                        if (w) delete w.__beKbGameFrameHooked;
                    } catch {}
                    beHookKeybindToMaingameframe();
                });
            }
        } catch (e) {
            BE_LOG.debug("bindBonkKeybindOverlayListeners frame load", e);
        }
    }

    function ensureBonkKeybindOverlayMounted() {
        try {
            if (window.top !== window) {
                let topHasOverlay = false;
                try {
                    topHasOverlay = !!(window.top && window.top.document && window.top.document.getElementById("beKeybindOverlay"));
                } catch {
                    topHasOverlay = false;
                }
                if (topHasOverlay) return;
            }
        } catch {}
        const existingKb = document.getElementById("beKeybindOverlay");
        if (existingKb) {
            beInitKeybindOverlayDrag(existingKb);
            bindBonkKeybindOverlayListeners();
            applyKeybindOverlayVisibility();
            refreshBonkKeybindOverlayFromGame();
            return;
        }
        const wrap = document.createElement("div");
        wrap.id = "beKeybindOverlay";
        wrap.setAttribute("aria-hidden", "true");
        wrap.innerHTML = `
<div class="be-kb-panel">
  <div class="be-kb-draghead" id="beKbDragHandle" title="Drag to move">
    <span class="be-kb-drag-grip" aria-hidden="true"></span>
    <span class="be-kb-drag-title">Bonk controls</span>
  </div>
  <div class="be-kb-body">
  <div class="be-kb-grid">
    <div class="be-kb-spacer"></div>
    <div class="be-kb-key" data-be-kb="up" title="Up"><span id="be-kb-txt-up">UP</span></div>
    <div class="be-kb-spacer"></div>
    <div class="be-kb-key" data-be-kb="left" title="Left"><span id="be-kb-txt-left">LEFT</span></div>
    <div class="be-kb-key" data-be-kb="down" title="Down"><span id="be-kb-txt-down">DOWN</span></div>
    <div class="be-kb-key" data-be-kb="right" title="Right"><span id="be-kb-txt-right">RIGHT</span></div>
  </div>
  <div class="be-kb-strip" aria-hidden="true">
    <span class="be-kb-key be-kb-key-sm" data-be-kb="up" title="Up"><span>U</span></span>
    <span class="be-kb-key be-kb-key-sm" data-be-kb="left" title="Left"><span>L</span></span>
    <span class="be-kb-key be-kb-key-sm" data-be-kb="down" title="Down"><span>D</span></span>
    <span class="be-kb-key be-kb-key-sm" data-be-kb="right" title="Right"><span>R</span></span>
    <span class="be-kb-key be-kb-key-sm" data-be-kb="heavy" title="Heavy"><span>H</span></span>
    <span class="be-kb-key be-kb-key-sm" data-be-kb="special" title="Special"><span>S</span></span>
  </div>
  <div class="be-kb-actions">
    <div class="be-kb-action be-kb-action-wideonly">
      <span class="be-kb-key be-kb-key-wide" data-be-kb="heavy" title="Heavy"><span id="be-kb-txt-heavy">HEAVY</span></span>
    </div>
    <div class="be-kb-action be-kb-action-wideonly">
      <span class="be-kb-key be-kb-key-wide" data-be-kb="special" title="Special"><span id="be-kb-txt-special">SPECIAL</span></span>
    </div>
  </div>
  </div>
</div>`;
        document.body.appendChild(wrap);
        beInitKeybindOverlayDrag(wrap);
        bindBonkKeybindOverlayListeners();
        applyKeybindOverlayVisibility();
        refreshBonkKeybindOverlayFromGame();
    }

    function applySizePreset(size) {
        if (!UI.root || !UI.card) return;
        uiSettings.size = ["tiny", "medium", "large"].includes(size) ? size : "medium";
        UI.card.style.width = "";
        UI.card.style.height = "";
        UI.card.style.maxHeight = "";
        saveUISettings();
        applyOverlaySettings();
    }

    function beSnapshotUiPresetData() {
        let pos = null;
        try {
            pos = JSON.parse(localStorage.getItem("bonk_ui_pos") || "null");
        } catch {
            pos = null;
        }
        let kb = null;
        try {
            kb = JSON.parse(localStorage.getItem(BE_KB_OVERLAY_POS_KEY) || "null");
        } catch {
            kb = null;
        }
        const card = UI.card;
        return {
            v: 1,
            theme: uiSettings.theme,
            size: uiSettings.size,
            graphWeek: uiSettings.graphWeek,
            overviewSlots: JSON.parse(JSON.stringify(uiSettings.overviewSlots)),
            dashboardStats: JSON.parse(JSON.stringify(uiSettings.dashboardStats)),
            overlayHotkey: uiSettings.overlayHotkey,
            widgetHotkey: uiSettings.widgetHotkey,
            keybindOverlayEnabled: uiSettings.keybindOverlayEnabled,
            keybindOverlayOpacity: uiSettings.keybindOverlayOpacity,
            keybindOverlayScale: uiSettings.keybindOverlayScale,
            keybindOverlayCompact: uiSettings.keybindOverlayCompact,
            trackerPos: pos,
            cardW: card ? card.style.width || "" : "",
            cardH: card ? card.style.height || "" : "",
            keybindOverlayPos: kb
        };
    }

    function beApplyUiPresetData(data) {
        if (!data || data.v !== 1) return false;
        uiSettings.theme = data.theme === "light" ? "light" : "dark";
        uiSettings.size = ["tiny", "medium", "large"].includes(data.size) ? data.size : "medium";
        uiSettings.graphWeek = typeof data.graphWeek === "string" ? data.graphWeek : "";
        if (data.overviewSlots && typeof data.overviewSlots === "object") {
            uiSettings.overviewSlots = {
                tiny: Array.isArray(data.overviewSlots.tiny) ? data.overviewSlots.tiny.slice(0, 3) : uiSettings.overviewSlots.tiny,
                medium: Array.isArray(data.overviewSlots.medium) ? data.overviewSlots.medium.slice(0, 4) : uiSettings.overviewSlots.medium,
                large: Array.isArray(data.overviewSlots.large) ? data.overviewSlots.large.slice(0, 6) : uiSettings.overviewSlots.large
            };
        }
        if (data.dashboardStats && typeof data.dashboardStats === "object") {
            uiSettings.dashboardStats = {
                xp: data.dashboardStats.xp !== false,
                wins: data.dashboardStats.wins !== false,
                rate: data.dashboardStats.rate !== false,
                lastWin: data.dashboardStats.lastWin !== false
            };
        }
        if (typeof data.overlayHotkey === "string" && data.overlayHotkey) uiSettings.overlayHotkey = data.overlayHotkey;
        if (typeof data.widgetHotkey === "string" && data.widgetHotkey) uiSettings.widgetHotkey = data.widgetHotkey;
        uiSettings.keybindOverlayEnabled = data.keybindOverlayEnabled === true;
        if (typeof data.keybindOverlayOpacity === "number") {
            uiSettings.keybindOverlayOpacity = Math.min(1, Math.max(0.35, data.keybindOverlayOpacity));
        }
        if (typeof data.keybindOverlayScale === "number") {
            uiSettings.keybindOverlayScale = Math.min(1.35, Math.max(0.65, data.keybindOverlayScale));
        }
        uiSettings.keybindOverlayCompact = data.keybindOverlayCompact === true;
        saveUISettings();

        const ts = document.getElementById("beThemeSelect");
        const ss = document.getElementById("beSizeSelect");
        const gw = document.getElementById("beGraphWeekSelect");
        if (ts) ts.value = uiSettings.theme;
        if (ss) ss.value = uiSettings.size;
        if (gw) gw.value = uiSettings.graphWeek || "";

        const kbt = document.getElementById("beKeybindOverlayToggle");
        if (kbt) kbt.checked = uiSettings.keybindOverlayEnabled;
        const opR = document.getElementById("beKbOverlayOpacity");
        if (opR) opR.value = String(Math.round((uiSettings.keybindOverlayOpacity ?? 1) * 100));
        const scR = document.getElementById("beKbOverlayScale");
        if (scR) scR.value = String(Math.round((uiSettings.keybindOverlayScale ?? 1) * 100));
        const cpR = document.getElementById("beKbOverlayCompact");
        if (cpR) cpR.checked = uiSettings.keybindOverlayCompact === true;

        if (data.trackerPos && typeof data.trackerPos === "object" && UI.root) {
            const left = data.trackerPos.left;
            const top = data.trackerPos.top;
            if (typeof left === "string" && typeof top === "string") {
                UI.root.style.left = left;
                UI.root.style.top = top;
                UI.root.style.right = "auto";
                try {
                    localStorage.setItem("bonk_ui_pos", JSON.stringify({ left, top }));
                } catch {
                    // ignore
                }
            }
        }
        applyOverlaySettings();
        if (UI.root) UI.root.dataset.size = uiSettings.size;
        if (data.cardW && data.cardH && UI.card) {
            UI.card.style.width = data.cardW;
            UI.card.style.height = data.cardH;
        }
        if (data.keybindOverlayPos && typeof data.keybindOverlayPos === "object") {
            try {
                localStorage.setItem(BE_KB_OVERLAY_POS_KEY, JSON.stringify(data.keybindOverlayPos));
            } catch {
                // ignore
            }
        }
        applyDashboardStatVisibility();
        applyOverviewLayout();
        syncOverviewSlotControls();
        ensureBonkKeybindOverlayMounted();
        return true;
    }

    function beRefreshPresetSelect() {
        const sel = document.getElementById("bePresetSelect");
        if (!sel) return;
        const list = Array.isArray(uiSettings.uiPresets) ? uiSettings.uiPresets : [];
        sel.innerHTML = "";
        if (!list.length) {
            const o = document.createElement("option");
            o.value = "";
            o.textContent = "(no presets)";
            sel.appendChild(o);
            return;
        }
        list.forEach((p) => {
            const o = document.createElement("option");
            o.value = p.id || "";
            o.textContent = p.name || p.id || "Preset";
            sel.appendChild(o);
        });
    }

    function refreshScriptHealthPanel() {
        const out = document.getElementById("beScriptHealthOut");
        const lab = document.getElementById("beSelectorSchemaLabel");
        if (lab) lab.textContent = BE_SELECTOR_SCHEMA;
        if (!out) return;
        let lsOk = true;
        let lsDetail = "ok";
        try {
            localStorage.setItem("__be_ls_probe", "1");
            localStorage.removeItem("__be_ls_probe");
        } catch (e) {
            lsOk = false;
            lsDetail = e?.message || "blocked";
        }
        const frame = document.getElementById("maingameframe");
        let cdOk = false;
        let cdDetail = "null";
        try {
            const cd = beGetBonkGameDocument();
            cdOk = !!cd;
            cdDetail = cd ? "readable" : "null";
        } catch (e) {
            cdDetail = e?.message || "err";
        }
        const gdoc = beGetBonkGameDocument();
        const rt = beFindRedefineControlsTable(gdoc);
        const summary = {
            schema: BE_SELECTOR_SCHEMA,
            localStorage: { ok: lsOk, detail: lsDetail },
            maingameframe: { ok: !!frame },
            gameDocument: { ok: cdOk, detail: cdDetail },
            redefineControlsTable: { ok: !!rt, detail: rt ? "found" : "missing" },
            health: BE_HEALTH
        };
        try {
            out.textContent = JSON.stringify(summary, null, 2);
        } catch {
            out.textContent = "(could not stringify health)";
        }
    }

    function beExportStatsToCsv() {
        const cache = Storage.load();
        const data = cache?.data;
        if (!data || typeof data !== "object") {
            showDiagnosticsToast("No stats data to export.");
            return;
        }
        const dayKeys = Object.keys(data)
            .filter((k) => /^\d{4}-\d{2}-\d{2}$/.test(k))
            .sort();
        const rows = [["date", "xp", "wins", "synced"].join(",")];
        dayKeys.forEach((k) => {
            const d = data[k] || {};
            const line = [
                k,
                d.xp != null ? d.xp : "",
                d.wins != null ? d.wins : "",
                d._synced ? "1" : "0"
            ].map((cell) => `"${String(cell).replace(/"/g, '""')}"`);
            rows.push(line.join(","));
        });
        const sess = Array.isArray(data._sessions) ? data._sessions : [];
        if (sess.length) {
            rows.push("");
            rows.push(["session_at_iso", "gained_wins", "duration_sec"].join(","));
            sess.forEach((s) => {
                rows.push(
                    [s.at, s.gainedWins != null ? s.gainedWins : "", s.durationSec != null ? s.durationSec : ""]
                        .map((cell) => `"${String(cell).replace(/"/g, '""')}"`)
                        .join(",")
                );
            });
        }
        const blob = new Blob([rows.join("\r\n")], { type: "text/csv;charset=utf-8" });
        const a = document.createElement("a");
        a.href = URL.createObjectURL(blob);
        a.download = `bonk-stats-${(Storage.getPlayerKey() || "player").replace(/[^\w\-]+/g, "_")}-${new Date().toISOString().slice(0, 10)}.csv`;
        a.click();
        setTimeout(() => {
            try {
                URL.revokeObjectURL(a.href);
            } catch {}
        }, 4000);
    }

    function setOverlayVisibility(visible) {
        if (!UI.root) return;
        const topHint = document.getElementById("pretty_top_be_launcher");
        if (visible) {
            UI.root.style.display = "block";
            requestAnimationFrame(() => UI.root.classList.add("be-overlay-open"));
            if (UI.launcher) UI.launcher.style.display = "none";
            if (topHint) topHint.style.setProperty("display", "none", "important");
            return;
        }

        UI.root.classList.remove("be-overlay-open");
        setTimeout(() => {
            if (!UI.root.classList.contains("be-overlay-open")) {
                UI.root.style.display = "none";
                if (UI.launcher) UI.launcher.style.display = "none";
                if (topHint) topHint.style.setProperty("display", "flex", "important");
            }
        }, 170);
    }

    function toggleOverlayVisibility() {
        if (!UI.root) return;
        const isVisible = UI.root.style.display !== "none";
        setOverlayVisibility(!isVisible);
    }

    function setWidgetHubVisibility(visible) {
        if (!UI.widgetHub) return;
        UI.widgetHub.style.display = visible ? "flex" : "none";
        UI.widgetHub.style.pointerEvents = visible ? "auto" : "none";
    }

    function openWidgetHub() {
        setOverlayVisibility(false);
        setWidgetHubVisibility(true);
    }

    function toggleWidgetHub() {
        if (!UI.widgetHub) return;
        const open = UI.widgetHub.style.display === "flex";
        if (open) setWidgetHubVisibility(false);
        else openWidgetHub();
    }

    window.BonkEnhanced = window.BonkEnhanced || {};
    window.BonkEnhanced.diagnose = function () {
        const snapshot = {
            env: BE_CONFIG.env,
            debug: BE_CONFIG.debug,
            page: window.location.href,
            health: BE_HEALTH
        };
        const rows = Object.entries(BE_HEALTH).map(([k, v]) => ({
            check: k,
            ok: v.ok,
            detail: v.detail,
            at: new Date(v.ts).toLocaleTimeString()
        }));
        console.groupCollapsed(`[BonkEnhanced] Diagnostics (${rows.length} checks)`);
        console.table(rows);
        console.log("Snapshot:", snapshot);
        console.groupEnd();
        return snapshot;
    };
    window.BonkEnhanced.setDebug = function (enabled) {
        BE_CONFIG.debug = !!enabled;
        localStorage.setItem("be_debug", enabled ? "1" : "0");
        return BE_CONFIG.debug;
    };
    window.BonkEnhanced.setEnv = function (env) {
        const next = env === "prod" ? "prod" : "test";
        BE_CONFIG.env = next;
        localStorage.setItem("be_env", next);
        return BE_CONFIG.env;
    };
    window.BonkEnhanced.getConfig = function () {
        return JSON.parse(JSON.stringify(BE_CONFIG));
    };

    /** v9: lowercase friend names for in-game nameText tint (Pixi injector reads this). */
    window.BonkEnhanced.friendNamesLower = [];
    window.BonkEnhanced.syncFriendsFromStorage = function () {
        try {
            const raw = localStorage.getItem("be_friends_v9");
            const arr = raw ? JSON.parse(raw) : [];
            window.BonkEnhanced.friendNamesLower = Array.isArray(arr)
                ? arr.map((s) => String(s || "").trim().toLowerCase()).filter(Boolean)
                : [];
        } catch {
            window.BonkEnhanced.friendNamesLower = [];
        }
        return window.BonkEnhanced.friendNamesLower.slice();
    };
    window.BonkEnhanced.syncFriendsFromStorage();

    /** Main bonk.io tab: pending login fields (short TTL). Game frame queues; host page fills form. */
    const BE_AUTOFILL_PENDING_KEY = "be_autofill_login_v1";
    const BE_AUTOFILL_TTL_MS = 120000;

    function beMirrorAutofillPendingToBrowsingContexts(payload) {
        const seen = new Set();
        const write = (w) => {
            if (!w || seen.has(w)) return;
            seen.add(w);
            try {
                w.localStorage.setItem(BE_AUTOFILL_PENDING_KEY, payload);
            } catch (e) {
                BE_LOG.debug("beMirrorAutofillPendingToBrowsingContexts", e);
            }
        };
        write(window);
        try {
            write(window.top);
        } catch {}
        try {
            write(window.parent);
        } catch {}
    }

    function beReadAutofillPendingRaw() {
        const tryWin = (w) => {
            try {
                return w.localStorage.getItem(BE_AUTOFILL_PENDING_KEY);
            } catch {
                return null;
            }
        };
        let v = tryWin(window) || tryWin(window.top) || tryWin(window.parent);
        if (v) return v;
        try {
            if (window.opener) {
                v = tryWin(window.opener) || (window.opener.top && tryWin(window.opener.top));
                if (v) return v;
            }
        } catch {}
        try {
            const mem = window.top && window.top.__beAutofillPendingPayload;
            if (typeof mem === "string" && mem.length > 0) return mem;
        } catch {}
        try {
            const mem2 = window.__beAutofillPendingPayload;
            if (typeof mem2 === "string" && mem2.length > 0) return mem2;
        } catch {}
        return null;
    }

    function beClearAutofillPendingEverywhere() {
        const seen = new Set();
        const clear = (w) => {
            if (!w || seen.has(w)) return;
            seen.add(w);
            try {
                w.localStorage.removeItem(BE_AUTOFILL_PENDING_KEY);
            } catch {}
        };
        clear(window);
        try {
            clear(window.top);
        } catch {}
        try {
            clear(window.parent);
        } catch {}
        try {
            delete window.top.__beAutofillPendingPayload;
        } catch {}
        try {
            delete window.__beAutofillPendingPayload;
        } catch {}
    }

    function queueBonkLoginAutofill(username, password) {
        try {
            const payload = JSON.stringify({
                v: 1,
                username: String(username || ""),
                password: String(password || ""),
                exp: Date.now() + BE_AUTOFILL_TTL_MS
            });
            beMirrorAutofillPendingToBrowsingContexts(payload);
            try {
                window.top.__beAutofillPendingPayload = payload;
            } catch {}
            try {
                window.__beAutofillPendingPayload = payload;
            } catch {}
            let ok = false;
            try {
                ok = localStorage.getItem(BE_AUTOFILL_PENDING_KEY) === payload;
            } catch {}
            if (!ok) {
                try {
                    ok = window.top.localStorage.getItem(BE_AUTOFILL_PENDING_KEY) === payload;
                } catch {}
            }
            if (!ok) {
                BE_LOG.error("queueBonkLoginAutofill verify mismatch");
            } else {
                BE_LOG.debug("queueBonkLoginAutofill stored");
                try {
                    if (typeof showDiagnosticsToast === "function") {
                        showDiagnosticsToast(
                            "Autofill queued — bottom-right toast. On Guest/Account screen, script will click Login or Register next; “Bonk login filled” only after that form is visible."
                        );
                    }
                } catch (e) {
                    BE_LOG.debug("queue toast", e);
                }
            }
            const peekNow = beReadAutofillPendingRaw();
            return {
                stored: ok,
                note: "Queued toast = storage OK. “Bonk login filled” only when #loginwindow_* is visible and filled. On Guest/Account, consumer clicks LoR first — no fill toast until then.",
                peekNow
            };
        } catch (e) {
            BE_LOG.error("queueBonkLoginAutofill", e);
            return { stored: false, error: String(e && e.message ? e.message : e), peekNow: null };
        }
    }
    window.BonkEnhanced.queueBonkLoginAutofill = queueBonkLoginAutofill;
    window.BonkEnhanced.peekAutofillQueue = function () {
        return beReadAutofillPendingRaw();
    };
    /** Console: BonkEnhanced.probeAutofillStorage() — verify localStorage read/write works in this frame. */
    window.BonkEnhanced.probeAutofillStorage = function () {
        const key = "__be_storage_probe_v1";
        try {
            localStorage.setItem(key, "1");
            const ok = localStorage.getItem(key) === "1";
            localStorage.removeItem(key);
            return { localStorageOk: ok, topSame: window.top === window, href: window.location.href };
        } catch (e) {
            return { localStorageOk: false, error: String(e && e.message ? e.message : e), href: window.location.href };
        }
    };

    function beSetInputValue(el, value) {
        if (!el) return;
        try {
            const proto = el.constructor?.prototype || HTMLInputElement.prototype;
            const desc = Object.getOwnPropertyDescriptor(proto, "value");
            if (desc && desc.set) desc.set.call(el, value);
            else el.value = value;
        } catch {
            el.value = value;
        }
        el.dispatchEvent(new InputEvent("input", { bubbles: true, composed: true }));
        el.dispatchEvent(new Event("change", { bubbles: true }));
    }

    /**
     * Reduce fights with built-in autofill and extensions (LastPass, 1Password, Bitwarden, etc.).
     * Browsers may still ignore hints; user can turn off saved passwords for bonk.io in browser settings.
     */
    function beHardenBonkLoginFields(u, p) {
        if (!u || !p) return;
        try {
            u.setAttribute("autocomplete", "off");
            u.setAttribute("data-lpignore", "true");
            u.setAttribute("data-1p-ignore", "true");
            u.setAttribute("data-bwignore", "true");
            u.setAttribute("data-form-type", "other");
            p.setAttribute("autocomplete", "new-password");
            p.setAttribute("data-lpignore", "true");
            p.setAttribute("data-1p-ignore", "true");
            p.setAttribute("data-bwignore", "true");
            p.setAttribute("data-form-type", "other");
        } catch (err) {
            BE_LOG.debug("beHardenBonkLoginFields", err);
        }
    }

    /** Focus the iframe element in the parent page so inner clicks can register (main bonk.io tab). */
    function beFocusOwningIframe(el) {
        if (!el || !el.ownerDocument) return;
        const w = el.ownerDocument.defaultView;
        if (!w || w === window) return;
        try {
            const fe = w.frameElement;
            if (fe && typeof fe.focus === "function") fe.focus();
            if (typeof w.focus === "function") w.focus();
        } catch (e) {
            BE_LOG.debug("beFocusOwningIframe", e);
        }
    }

    /** Dispatch a more reliable click for Bonk’s div-based buttons (some builds ignore el.click()). */
    function beSyntheticClick(el) {
        if (!el) return;
        const view =
            el.ownerDocument && el.ownerDocument.defaultView ? el.ownerDocument.defaultView : window;
        try {
            el.scrollIntoView({ block: "center", inline: "nearest", behavior: "instant" });
        } catch {
            try {
                el.scrollIntoView(true);
            } catch {}
        }
        beFocusOwningIframe(el);
        try {
            el.click();
        } catch (e) {
            BE_LOG.warn("beSyntheticClick native", e);
        }
        try {
            el.click();
        } catch {}
        try {
            el.dispatchEvent(new PointerEvent("pointerdown", { bubbles: true, cancelable: true, view }));
        } catch {
            try {
                el.dispatchEvent(new MouseEvent("mousedown", { bubbles: true, cancelable: true, view }));
            } catch {}
        }
        try {
            el.dispatchEvent(new PointerEvent("pointerup", { bubbles: true, cancelable: true, view }));
        } catch {
            try {
                el.dispatchEvent(new MouseEvent("mouseup", { bubbles: true, cancelable: true, view }));
            } catch {}
        }
        try {
            el.dispatchEvent(new MouseEvent("click", { bubbles: true, cancelable: true, view }));
        } catch {}
        try {
            el.click();
        } catch (e) {
            BE_LOG.warn("beSyntheticClick", e);
        }
    }

    /** True if node looks painted and hittable; getComputedStyle uses the element’s document (iframe-safe). */
    function beIsLikelyVisibleClickTarget(el) {
        if (!el) return false;
        try {
            const dv = el.ownerDocument && el.ownerDocument.defaultView;
            const g = dv && dv.getComputedStyle ? dv.getComputedStyle(el) : window.getComputedStyle(el);
            if (!g || g.display === "none" || g.visibility === "hidden" || Number(g.opacity) === 0) return false;
            const r = el.getBoundingClientRect();
            return r.width >= 2 && r.height >= 2;
        } catch {
            return false;
        }
    }

    /**
     * “Login or Register” in #maingameframe — some builds ignore bare .click(); add coords, hit-test, and rAF settle.
     */
    function beClickLoRStrategies(btn, cd) {
        if (!btn || !cd) return;
        const view = cd.defaultView || window;
        try {
            btn.scrollIntoView({ block: "center", inline: "nearest", behavior: "instant" });
        } catch {
            try {
                btn.scrollIntoView(true);
            } catch {}
        }
        beFocusOwningIframe(btn);

        const fire = () => {
            const r = btn.getBoundingClientRect();
            const x = Math.round(r.left + r.width / 2);
            const y = Math.round(r.top + r.height / 2);
            if (r.width < 2 || r.height < 2) return;

            try {
                btn.click();
            } catch {}
            try {
                btn.dispatchEvent(
                    new MouseEvent("mousedown", {
                        bubbles: true,
                        cancelable: true,
                        view,
                        clientX: x,
                        clientY: y,
                        button: 0,
                        buttons: 1,
                    })
                );
                btn.dispatchEvent(
                    new MouseEvent("mouseup", {
                        bubbles: true,
                        cancelable: true,
                        view,
                        clientX: x,
                        clientY: y,
                        button: 0,
                        buttons: 0,
                    })
                );
                btn.dispatchEvent(
                    new MouseEvent("click", {
                        bubbles: true,
                        cancelable: true,
                        view,
                        clientX: x,
                        clientY: y,
                        button: 0,
                    })
                );
            } catch (e) {
                BE_LOG.debug("LoR coord MouseEvent", e);
            }
            try {
                const hit = cd.elementFromPoint(x, y);
                if (hit && typeof hit.click === "function") hit.click();
            } catch (e) {
                BE_LOG.debug("LoR elementFromPoint", e);
            }
            try {
                btn.dispatchEvent(
                    new PointerEvent("pointerdown", {
                        bubbles: true,
                        cancelable: true,
                        view,
                        clientX: x,
                        clientY: y,
                        pointerId: 1,
                        pointerType: "mouse",
                        isPrimary: true,
                    })
                );
                btn.dispatchEvent(
                    new PointerEvent("pointerup", {
                        bubbles: true,
                        cancelable: true,
                        view,
                        clientX: x,
                        clientY: y,
                        pointerId: 1,
                        pointerType: "mouse",
                        isPrimary: true,
                    })
                );
            } catch {}
            try {
                beSyntheticClick(btn);
            } catch {}
            try {
                btn.click();
            } catch {}
        };

        try {
            if (typeof view.requestAnimationFrame === "function") {
                view.requestAnimationFrame(() => {
                    view.requestAnimationFrame(fire);
                });
            } else {
                window.setTimeout(fire, 0);
            }
        } catch {
            fire();
        }
    }

    /** Depth-first walk of document / subtree including open shadow roots (Bonk may host menus in shadow DOM). */
    function beWalkDeep(entry, visitor) {
        const go = (node) => {
            if (!node) return;
            if (node.nodeType === 1) {
                visitor(node);
                const sr = node.shadowRoot;
                if (sr) {
                    const kids = sr.childNodes;
                    for (let i = 0; i < kids.length; i++) {
                        const c = kids[i];
                        if (c && c.nodeType === 1) go(c);
                    }
                }
                const ch = node.children;
                for (let j = 0; j < ch.length; j++) go(ch[j]);
            } else if (node.nodeType === 9) {
                if (node.documentElement) go(node.documentElement);
            }
        };
        try {
            if (entry && entry.nodeType === 9) go(entry);
            else go(entry);
        } catch (e) {
            BE_LOG.debug("beWalkDeep", e);
        }
    }

    function beDeepGetElementById(rootDoc, id) {
        if (!rootDoc || !id) return null;
        let found = null;
        try {
            if (rootDoc.getElementById) {
                const n = rootDoc.getElementById(id);
                if (n) return n;
            }
        } catch {}
        beWalkDeep(rootDoc, (el) => {
            if (found) return;
            try {
                if (el.id === id) found = el;
            } catch {}
        });
        return found;
    }

    function beDeepQuerySelectorAll(rootDoc, selector) {
        const out = [];
        const seen = new Set();
        beWalkDeep(rootDoc, (el) => {
            try {
                if (el.matches && el.matches(selector) && !seen.has(el)) {
                    seen.add(el);
                    out.push(el);
                }
            } catch {}
        });
        return out;
    }

    /** If the Account panel is closed, click the in-game main-menu brown “Account” button (not the start-screen green “Account” title). */
    function tryOpenBonkAccountPanel(root) {
        if (!root || !root.querySelector) return false;
        if (beDeepGetElementById(root, "loginwindow_username")) return false;
        const ac = beDeepGetElementById(root, "accountContainer");
        if (ac) {
            const st = window.getComputedStyle(ac);
            const visible = ac.offsetParent !== null && st.display !== "none" && st.visibility !== "hidden" && st.opacity !== "0";
            if (visible) return false;
        }
        const hits = beDeepQuerySelectorAll(root, ".brownButton, div[class*='brownButton'], div[class*='brown']");
        for (let i = 0; i < hits.length; i++) {
            const el = hits[i];
            const t = (el.textContent || "").replace(/\s+/g, " ").trim();
            if (/^account$/i.test(t)) {
                try {
                    beSyntheticClick(el);
                    return true;
                } catch (e) {
                    BE_LOG.warn("autofill Account click", e);
                }
            }
        }
        return false;
    }

    /**
     * Account screen shows “Login or Register” first; the Log In fields appear after click.
     * If #loginwindow_username is missing, find that control and click (same DOM position each build).
     */
    function tryClickBonkLoginOrRegister(root) {
        if (!root || !root.querySelector) return false;
        if (beDeepGetElementById(root, "loginwindow_username")) return false;

        const tryActivate = (el) => {
            if (!el) return false;
            try {
                beSyntheticClick(el);
                return true;
            } catch (e) {
                BE_LOG.warn("autofill Login or Register click", e);
                return false;
            }
        };

        const byStableId = beDeepGetElementById(root, "guestOrAccountContainer_accountButton");
        if (byStableId && beIsLikelyVisibleClickTarget(byStableId) && tryActivate(byStableId)) return true;

        const candidates = beDeepQuerySelectorAll(
            root,
            "div.brownButton, div[class*='brownButton'], div[class*='brown'], div[class*='Brown'], button, [role='button'], a"
        );
        for (let i = 0; i < candidates.length; i++) {
            const el = candidates[i];
            const t = (el.textContent || "").replace(/\s+/g, " ").trim();
            if (!t || t.length > 96) continue;
            if (/login\s*or\s*register/i.test(t)) {
                if (tryActivate(el)) return true;
            }
        }

        const exactHits = [];
        beWalkDeep(root, (el) => {
            try {
                const t = (el.innerText || el.textContent || "").replace(/\s+/g, " ").trim();
                if (!/^login\s*or\s*register$/i.test(t)) return;
                const r = el.getBoundingClientRect();
                if (r.width < 2 || r.height < 2) return;
                exactHits.push({ el, area: r.width * r.height });
            } catch {}
        });
        exactHits.sort((a, b) => a.area - b.area);
        for (let j = 0; j < exactHits.length; j++) {
            if (tryActivate(exactHits[j].el)) return true;
        }

        return false;
    }

    /** Bonk game UI (main menu) — IDs from DevTools. Submit is a div, not <button>. */
    function beIsLikelyVisibleInput(el) {
        if (!el) return false;
        try {
            if (el.disabled || el.getAttribute("aria-hidden") === "true") return false;
            const r = el.getBoundingClientRect();
            if (r.width < 3 || r.height < 3) return false;
            const st = window.getComputedStyle(el);
            if (st.display === "none" || st.visibility === "hidden" || Number(st.opacity) === 0) return false;
            return true;
        } catch {
            return false;
        }
    }

    function tryBonkLoginWindowFill(root, username, password) {
        if (!root || !root.querySelector) return false;
        const u = beDeepGetElementById(root, "loginwindow_username");
        const p = beDeepGetElementById(root, "loginwindow_password");
        const btn = beDeepGetElementById(root, "loginwindow_submitbutton");
        if (!u || !p) return false;
        if (!beIsLikelyVisibleInput(u) || !beIsLikelyVisibleInput(p)) return false;

        beHardenBonkLoginFields(u, p);
        beSetInputValue(u, username);
        beSetInputValue(p, password);

        if (btn) {
            window.setTimeout(() => {
                try {
                    btn.click();
                } catch (err) {
                    BE_LOG.warn("autofill loginwindow_submitbutton", err);
                }
            }, 180);
        }
        showDiagnosticsToast("Bonk login filled");
        return true;
    }

    function getBonkLoginRootDocuments() {
        const out = [];
        const seen = new Set();
        const addDoc = (d) => {
            if (!d || seen.has(d)) return;
            try {
                if (d.documentElement) {
                    seen.add(d);
                    out.push(d);
                }
            } catch {}
        };
        const collectIframes = (doc) => {
            if (!doc || !doc.querySelectorAll) return;
            let list;
            try {
                list = doc.querySelectorAll("iframe, frame");
            } catch {
                return;
            }
            for (let i = 0; i < list.length; i++) {
                try {
                    const cd = list[i].contentDocument;
                    if (cd) {
                        addDoc(cd);
                        collectIframes(cd);
                    }
                } catch {}
            }
        };
        try {
            if (window.location.href.includes("gameframe-release.html")) {
                addDoc(document);
                return out;
            }
            addDoc(document);
            collectIframes(document);
            const topDoc = document;
            const rest = out.filter((d) => d && d !== topDoc);
            if (rest.length) {
                return [...rest, topDoc];
            }
        } catch (e) {
            BE_LOG.debug("autofill: getBonkLoginRootDocuments", e);
        }
        return out;
    }

    /** Disabled: generic password heuristic matched stray/hidden inputs and cleared the queue. Bonk login uses #loginwindow_* via tryBonkLoginWindowFill only. */
    function fillBonkLoginFormHeuristic(root, username, password) {
        void root;
        void username;
        void password;
        return false;
    }

    function fillBonkLoginFormAnywhere(username, password) {
        if (!window.location.href.includes("gameframe-release.html") && window === window.top) {
            try {
                const f = document.getElementById("maingameframe");
                const cd = f && f.contentDocument;
                if (cd && !cd.getElementById("loginwindow_username")) {
                    const btn = cd.getElementById("guestOrAccountContainer_accountButton");
                    if (btn && beIsLikelyVisibleClickTarget(btn)) {
                        try {
                            f.contentWindow.focus();
                        } catch {}
                        try {
                            f.focus();
                        } catch {}
                        beClickLoRStrategies(btn, cd);
                        const retry = (delay) => {
                            window.setTimeout(() => {
                                try {
                                    if (cd.getElementById("loginwindow_username")) return;
                                    const b = cd.getElementById("guestOrAccountContainer_accountButton");
                                    if (b && beIsLikelyVisibleClickTarget(b)) beClickLoRStrategies(b, cd);
                                } catch {}
                            }, delay);
                        };
                        retry(130);
                        retry(420);
                        retry(1100);
                        return false;
                    }
                }
            } catch (e) {
                BE_LOG.debug("fillBonkLoginFormAnywhere top+maingameframe", e);
            }
        }

        const roots = getBonkLoginRootDocuments();
        let clickedOpener = false;
        for (let k = 0; k < roots.length; k++) {
            if (tryClickBonkLoginOrRegister(roots[k])) clickedOpener = true;
        }
        if (!clickedOpener) {
            for (let a = 0; a < roots.length; a++) {
                if (tryOpenBonkAccountPanel(roots[a])) clickedOpener = true;
            }
        }
        if (clickedOpener) return false;

        for (let i = 0; i < roots.length; i++) {
            if (tryBonkLoginWindowFill(roots[i], username, password)) return true;
        }
        return false;
    }

    function tickBonkMainLoginAutofill() {
        let raw = null;
        try {
            raw = beReadAutofillPendingRaw();
        } catch {
            return;
        }
        if (!raw) {
            window.__beAutofillBurst = 0;
            return;
        }
        let o;
        try {
            o = JSON.parse(raw);
        } catch {
            beClearAutofillPendingEverywhere();
            window.__beAutofillBurst = 0;
            return;
        }
        if (!o || o.v !== 1 || !o.username) {
            beClearAutofillPendingEverywhere();
            window.__beAutofillBurst = 0;
            return;
        }
        if (o.exp < Date.now()) {
            beClearAutofillPendingEverywhere();
            window.__beAutofillBurst = 0;
            return;
        }
        if (fillBonkLoginFormAnywhere(o.username, o.password)) {
            beClearAutofillPendingEverywhere();
            window.__beAutofillBurst = 0;
        } else {
            const b = (window.__beAutofillBurst = (window.__beAutofillBurst || 0) + 1);
            if (b < 80) {
                window.setTimeout(() => safeRun("autofill.burst", tickBonkMainLoginAutofill), 90);
            }
        }
    }

    function initBonkMainLoginAutofillConsumer() {
        if (window.__beAutofillMainPoll) return;
        const autofillMs = () => (beTabHidden() ? 1100 : 200);
        window.__beAutofillMainPoll = window.setInterval(() => {
            safeRun("autofill.tick", tickBonkMainLoginAutofill);
        }, autofillMs());
        if (!window.__beAutofillVisHooked) {
            window.__beAutofillVisHooked = true;
            document.addEventListener("visibilitychange", () => {
                if (!window.__beAutofillMainPoll) return;
                try {
                    clearInterval(window.__beAutofillMainPoll);
                } catch {}
                window.__beAutofillMainPoll = window.setInterval(() => {
                    safeRun("autofill.tick", tickBonkMainLoginAutofill);
                }, autofillMs());
            });
        }

        let moThrottleAt = 0;
        const moTick = () => {
            const now = Date.now();
            if (now - moThrottleAt < 140) return;
            moThrottleAt = now;
            safeRun("autofill.domMut", tickBonkMainLoginAutofill);
        };

        const attachDomObserver = (root) => {
            if (!root || root.__beAutofillDomObs) return;
            root.__beAutofillDomObs = true;
            try {
                const mo = new MutationObserver(moTick);
                mo.observe(root, { childList: true, subtree: true });
            } catch (e) {
                BE_LOG.debug("autofill dom observer", e);
            }
        };
        attachDomObserver(document.documentElement);

        const hookIframe = () => {
            const frame = document.getElementById("maingameframe");
            if (!frame || frame._beAutofillIframeHooked) return;
            frame._beAutofillIframeHooked = true;
            const onIframeReady = () => {
                safeRun("autofill.iframeLoad", tickBonkMainLoginAutofill);
                try {
                    const cd = frame.contentDocument;
                    if (cd && cd.documentElement) attachDomObserver(cd.documentElement);
                } catch (e) {
                    BE_LOG.debug("autofill iframe observe", e);
                }
            };
            frame.addEventListener("load", onIframeReady);
            try {
                if (frame.contentDocument && frame.contentDocument.readyState === "complete") {
                    window.setTimeout(() => safeRun("autofill.iframeAlreadyLoaded", onIframeReady), 0);
                }
            } catch (e) {
                BE_LOG.debug("autofill iframe ready check", e);
            }
        };
        hookIframe();
        if (document.readyState === "loading") {
            document.addEventListener("DOMContentLoaded", hookIframe, { once: true });
        }

        let hookScan = 0;
        const iframeScanMs = () => (beTabHidden() ? 2000 : 500);
        window.__beAutofillIframeScan = window.setInterval(() => {
            hookIframe();
            if (++hookScan > 60) {
                try {
                    clearInterval(window.__beAutofillIframeScan);
                } catch {}
                window.__beAutofillIframeScan = null;
            }
        }, iframeScanMs());
        document.addEventListener("visibilitychange", () => {
            if (!window.__beAutofillIframeScan || hookScan > 60) return;
            try {
                clearInterval(window.__beAutofillIframeScan);
            } catch {}
            window.__beAutofillIframeScan = window.setInterval(() => {
                hookIframe();
                if (++hookScan > 60) {
                    try {
                        clearInterval(window.__beAutofillIframeScan);
                    } catch {}
                    window.__beAutofillIframeScan = null;
                }
            }, iframeScanMs());
        });

        try {
            window.addEventListener("storage", (ev) => {
                if (ev.key === BE_AUTOFILL_PENDING_KEY) safeRun("autofill.storage", tickBonkMainLoginAutofill);
            });
        } catch (e) {
            BE_LOG.debug("autofill storage listener", e);
        }

        tickBonkMainLoginAutofill();
    }

    /** Start login autofill as soon as DOM is interactive — waiting only for `load` delays fill behind ads/assets. */
    function scheduleBonkMainLoginAutofillBootstrap(healthKey) {
        const go = () => safeRun(healthKey, initBonkMainLoginAutofillConsumer);
        if (document.readyState !== "loading") go();
        else document.addEventListener("DOMContentLoaded", go, { once: true });
        window.addEventListener("load", go);
    }

    window.BonkEnhanced.tickLoginAutofill = tickBonkMainLoginAutofill;
    window.BonkEnhanced.tickLoginAutoFill = tickBonkMainLoginAutofill;

    function showDiagnosticsToast(message) {
        const existing = document.getElementById("beDiagToast");
        if (existing) existing.remove();

        const toast = document.createElement("div");
        toast.id = "beDiagToast";
        toast.textContent = message;
        toast.style.cssText = [
            "position:fixed",
            "right:16px",
            "bottom:16px",
            "z-index:2147483647",
            "padding:8px 10px",
            "border-radius:8px",
            "font:12px 'Segoe UI',sans-serif",
            "background:rgba(15,22,36,0.92)",
            "color:#9cffb0",
            "border:1px solid rgba(76,175,80,0.5)",
            "box-shadow:0 6px 22px rgba(0,0,0,0.35)",
            "opacity:0",
            "transform:translateY(6px)",
            "transition:opacity 0.2s ease, transform 0.2s ease",
            "pointer-events:none"
        ].join(";");
        document.body.appendChild(toast);

        requestAnimationFrame(() => {
            toast.style.opacity = "1";
            toast.style.transform = "translateY(0)";
        });

        setTimeout(() => {
            toast.style.opacity = "0";
            toast.style.transform = "translateY(6px)";
            setTimeout(() => toast.remove(), 220);
        }, 1400);
    }

    function registerDiagnosticsHotkey() {
        if (window.__beDiagHotkeyBound) return;
        window.__beDiagHotkeyBound = true;
        let lastEndPressAt = 0;

        function buildCompactDiagnosticsReport(snapshot) {
            const health = snapshot?.health || {};
            const entries = Object.entries(health);
            const okCount = entries.filter(([, v]) => !!v.ok).length;
            const failEntries = entries.filter(([, v]) => !v.ok);
            const failList = failEntries.length
                ? failEntries.map(([k, v]) => `${k}=${v?.detail || "failed"}`).join(" | ")
                : "none";

            return [
                `BonkEnhanced Diagnostics`,
                `env=${snapshot?.env || "unknown"} debug=${snapshot?.debug ? "1" : "0"}`,
                `page=${snapshot?.page || window.location.href}`,
                `checks=${entries.length} ok=${okCount} failed=${entries.length - okCount}`,
                `failed_list=${failList}`
            ].join("\n");
        }

        async function copyDiagnosticsReport(snapshot) {
            const text = buildCompactDiagnosticsReport(snapshot);
            await navigator.clipboard.writeText(text);
            return text;
        }

        document.addEventListener("keydown", (ev) => {
            if (ev.key !== "End") return;
            if (ev.repeat) return;

            const target = ev.target;
            const tag = target?.tagName?.toLowerCase?.() || "";
            const isEditable = tag === "input" || tag === "textarea" || target?.isContentEditable;
            if (isEditable) return;

            ev.preventDefault();
            const report = window.BonkEnhanced.diagnose();
            const now = Date.now();
            const isDoublePress = now - lastEndPressAt <= 2000;
            lastEndPressAt = now;

            if (isDoublePress) {
                copyDiagnosticsReport(report)
                    .then(() => {
                        BE_LOG.info("Diagnostics report copied to clipboard");
                        showDiagnosticsToast("Diagnostics copied to clipboard");
                    })
                    .catch((err) => {
                        BE_LOG.warn("Clipboard copy failed", err);
                        showDiagnosticsToast("Copy failed (clipboard blocked)");
                    });
            } else {
                BE_LOG.info("Diagnostics snapshot:", report);
                showDiagnosticsToast("Diagnostics logged (press End again to copy)");
            }
        }, true);
    }
    registerDiagnosticsHotkey();

    function initOuterCustomMods() {
        const frame = document.getElementById("maingameframe");
        if (!frame) {
            markHealth("outer.frame", false, "maingameframe not found");
            return;
        }
        markHealth("outer.frame", true, "maingameframe found");

        const cw = frame.contentWindow;
        const cd = frame.contentDocument;
        if (!cw || !cd) {
            markHealth("outer.frameContext", false, "contentWindow/contentDocument missing");
            return;
        }
        markHealth("outer.frameContext", true, "frame context ready");

        // Keep browser shortcuts like Ctrl/Shift/F-keys working (keyboard only).
        // Applying this to all Event types broke mouse flows (e.g. room list) when Shift/Ctrl was held.
        const originalPreventDefault = cw.Event.prototype.preventDefault;
        const KB = cw.KeyboardEvent;
        cw.Event.prototype.preventDefault = function () {
            if (KB && this instanceof KB) {
                const ev = this;
                if (ev.ctrlKey || ev.shiftKey || (ev.key?.[0] === "F" && ev.key?.length > 1)) return;
            }
            originalPreventDefault.call(this);
        };
        markHealth("outer.preventDefaultPatch", true, "patched");

        const hostStyle = `
        <style>
            body { overflow: hidden; }
            #bonkioheader { display: none; }
            #adboxverticalleftCurse { display: none; }
            #adboxverticalCurse { display: none; }
            #descriptioncontainer { display: none; }
            #maingameframe { margin: 0 !important; }
        </style>`;
        document.head.insertAdjacentHTML("beforeend", hostStyle);
        markHealth("outer.hostStyle", true, "inserted");

        const setup = () => {
            if (!cd.getElementById("roomlisttopbar")) {
                markHealth("outer.roomTopbar", false, "roomlisttopbar not ready");
                return;
            }
            markHealth("outer.roomTopbar", true, "roomlisttopbar ready");

            try {
                if (cd._beRoomSearchDedupeObs) {
                    cd._beRoomSearchDedupeObs.disconnect();
                    cd._beRoomSearchDedupeObs = null;
                }
            } catch {
                // ignore
            }

            /* Re-run when room list is recreated (e.g. opening Custom Games). Skip if our bar already attached. */
            if (cd.getElementById("beRoomFilterBar")) {
                return;
            }

            if (!cd.getElementById("beRoomSearchPlaceholderHack")) {
                const placeholderStyler = `
                <style id="beRoomSearchPlaceholderHack">
                    [contenteditable=true]:empty:before {
                        content: attr(placeholder);
                        pointer-events: none;
                        display: block;
                        color: #757575;
                    }
                </style>`;
                cd.head.insertAdjacentHTML("beforeend", placeholderStyler);
                markHealth("outer.placeholderStyle", true, "inserted");
            }

            const $ = cw.$;
            if (!$) {
                markHealth("outer.jquery", false, "jQuery unavailable in frame");
                return;
            }
            markHealth("outer.jquery", true, "available");

            const ROOM_SETTINGS_KEY = "be_room_settings_v8";
            function loadRoomSettings() {
                try {
                    const raw = localStorage.getItem(ROOM_SETTINGS_KEY);
                    if (!raw) return {};
                    const o = JSON.parse(raw);
                    return {
                        hideSolo: !!o.hideSolo,
                        minPlayers: o.minPlayers === "" || o.minPlayers == null ? "" : String(o.minPlayers),
                        maxPlayers: o.maxPlayers === "" || o.maxPlayers == null ? "" : String(o.maxPlayers),
                        favoritesOnly: !!o.favoritesOnly,
                        favorites: Array.isArray(o.favorites) ? o.favorites.map(String) : [],
                        hidePassworded: !!o.hidePassworded
                    };
                } catch {
                    return {};
                }
            }
            let roomSettings = {
                hideSolo: false,
                minPlayers: "",
                maxPlayers: "",
                favoritesOnly: false,
                favorites: [],
                hidePassworded: false,
                ...loadRoomSettings()
            };
            function saveRoomSettings() {
                try {
                    localStorage.setItem(ROOM_SETTINGS_KEY, JSON.stringify(roomSettings));
                } catch {}
            }

            function getRoomNameFromRow(el) {
                return (el.children?.[0]?.textContent || "").trim();
            }

            /** First "cur / max" pair in row (Bonk room list player counts). */
            function getPlayerFractionFromRow(el) {
                const text = el.textContent || "";
                const m = /\b(\d+)\s*\/\s*(\d+)\b/.exec(text);
                if (!m) return null;
                return { cur: +m[1], max: +m[2] };
            }

            function isFavoriteName(name) {
                const n = (name || "").toLowerCase();
                return roomSettings.favorites.some((f) => (f || "").toLowerCase() === n);
            }

            function isRoomDataRow(el) {
                return el && el.closest && !el.closest("thead");
            }

            /** Bonk Enhanced uses #beRoomSearchInputBox; other userscripts (e.g. Bonk.io Custom Mods) use #roomSearchInputBox — never two boxes. */
            function getRoomSearchInputEl() {
                return cd.getElementById("beRoomSearchInputBox") || cd.getElementById("roomSearchInputBox");
            }

            function beRemoveDuplicateLegacyRoomSearch() {
                if (!cd.getElementById("beRoomSearchInputBox")) return;
                const bar = cd.getElementById("roomlisttopbar");
                if (!bar) return;
                try {
                    bar.querySelectorAll("#roomSearchInputBox").forEach((n) => {
                        n.remove();
                    });
                } catch {
                    // ignore
                }
            }

            function beAttachRoomSearchDedupeObserver() {
                if (cd._beRoomSearchDedupeObs) {
                    try {
                        cd._beRoomSearchDedupeObs.disconnect();
                    } catch {
                        // ignore
                    }
                    cd._beRoomSearchDedupeObs = null;
                }
                const bar = cd.getElementById("roomlisttopbar");
                if (!bar || !cd.getElementById("beRoomSearchInputBox")) return;
                cd._beRoomSearchDedupeObs = new MutationObserver(() => {
                    beRemoveDuplicateLegacyRoomSearch();
                });
                cd._beRoomSearchDedupeObs.observe(bar, { childList: true });
            }

            /** Bonk password column is 4th td (lock icon when passworded). */
            function isPasswordProtectedRow(el) {
                const pwCell = el.children?.[3];
                if (!pwCell) return false;
                return !!pwCell.querySelector("img[src*=\"lock\"], img[src*=\"Lock\"]");
            }

            function markFavoriteRows() {
                $("#roomlisttable tr").each((_, el) => {
                    if (!isRoomDataRow(el)) return;
                    const name = getRoomNameFromRow(el);
                    if (!name) return;
                    el.classList.toggle("be-room-fav", isFavoriteName(name));
                });
            }

            function applyAllFilters() {
                const qel = getRoomSearchInputEl();
                const s = ((qel?.textContent || "") + "").toLowerCase();
                const minP = roomSettings.minPlayers === "" ? null : Number(roomSettings.minPlayers);
                const maxP = roomSettings.maxPlayers === "" ? null : Number(roomSettings.maxPlayers);
                const hideSolo = !!roomSettings.hideSolo;
                const favOnly = !!roomSettings.favoritesOnly;
                const barPwEl = cd.getElementById("beHidePasswordedBar");
                const nativePwEl = cd.getElementById("roomlisthidepasswordedcheckbox");
                let hidePassworded = false;
                if (barPwEl) hidePassworded = !!barPwEl.checked;
                else if (nativePwEl) hidePassworded = !!nativePwEl.checked;
                else hidePassworded = !!roomSettings.hidePassworded;

                $("#roomlisttable tr").each((_, el) => {
                    if (!isRoomDataRow(el)) return;
                    const roomName = (el.children?.[0]?.textContent || "").toLowerCase();
                    if (!roomName.includes(s)) {
                        el.hidden = true;
                        return;
                    }
                    const frac = getPlayerFractionFromRow(el);
                    if (hideSolo && frac && frac.cur === 1 && frac.max === 1) {
                        el.hidden = true;
                        return;
                    }
                    if (minP != null && Number.isFinite(minP) && frac && frac.cur < minP) {
                        el.hidden = true;
                        return;
                    }
                    if (maxP != null && Number.isFinite(maxP) && frac && frac.cur > maxP) {
                        el.hidden = true;
                        return;
                    }
                    const rawName = getRoomNameFromRow(el);
                    if (favOnly && !isFavoriteName(rawName)) {
                        el.hidden = true;
                        return;
                    }
                    if (hidePassworded && isPasswordProtectedRow(el)) {
                        el.hidden = true;
                        return;
                    }
                    el.hidden = false;
                });
            }

            if (!cd.getElementById("beRoomSearchInputBox") && !cd.getElementById("roomSearchInputBox")) {
                const inputHtml = `<span contentEditable="true" id="beRoomSearchInputBox" placeholder="Search Rooms.." style="
                float:right;
                padding:2px 8px;
                margin:5px 20px;
                border:2px solid #006157;
                border-radius:5px;
                font:large futurept_b1;
                width:20%;
                background:white;
                color:#111;
                caret-color:#111;
                text-shadow:none;
            "></span>`;
                $("#roomlisttopbar").append(inputHtml);
                beRemoveDuplicateLegacyRoomSearch();
                beAttachRoomSearchDedupeObserver();
                markHealth("outer.roomSearch", true, "injected-beRoomSearchInputBox");
            } else if (cd.getElementById("beRoomSearchInputBox")) {
                beRemoveDuplicateLegacyRoomSearch();
                beAttachRoomSearchDedupeObserver();
                markHealth("outer.roomSearch", true, "already-beRoomSearchInputBox");
            } else {
                markHealth("outer.roomSearch", true, "compat-roomSearchInputBox");
            }

            const filterBarHtml = `
            <div id="beRoomFilterBar" style="clear:both;width:100%;padding:4px 6px;box-sizing:border-box;font:11px Arial,sans-serif;display:flex;flex-wrap:wrap;align-items:center;gap:6px 10px;background:rgba(0,20,30,0.35);border-bottom:1px solid rgba(0,97,87,0.35);color:#e8f4ff;">
                <label style="display:inline-flex;align-items:center;gap:4px;cursor:pointer;user-select:none;"><input type="checkbox" id="beHidePasswordedBar"/> Hide passworded</label>
                <label style="display:inline-flex;align-items:center;gap:4px;cursor:pointer;user-select:none;"><input type="checkbox" id="beHideSolo"/> Hide 1/1</label>
                <label style="display:inline-flex;align-items:center;gap:4px;">Min <input type="number" id="beMinPl" min="0" max="99" placeholder="—" style="width:38px;padding:1px 4px;border-radius:4px;border:1px solid #006157;"/></label>
                <label style="display:inline-flex;align-items:center;gap:4px;">Max <input type="number" id="beMaxPl" min="0" max="99" placeholder="—" style="width:38px;padding:1px 4px;border-radius:4px;border:1px solid #006157;"/></label>
                <label style="display:inline-flex;align-items:center;gap:4px;cursor:pointer;user-select:none;"><input type="checkbox" id="beFavOnly"/> Favorites only</label>
                <span style="opacity:0.75;font-size:10px;">Alt+click row: favorite</span>
            </div>`;
            if (!cd.getElementById("beRoomFilterBar")) {
                $("#roomlisttopbar").after(filterBarHtml);
            }

            const nativePwLabel = cd.getElementById("roomlisthidepasswordedcheckbox")?.closest("label.control-checkbox");
            if (nativePwLabel) {
                nativePwLabel.style.setProperty("display", "none", "important");
            }

            if (!cd.getElementById("beRoomFilterStyles")) {
                const roomFilterStyles = `
            <style id="beRoomFilterStyles">
                #roomlisttable tr.be-room-fav td { background: rgba(255, 180, 60, 0.12) !important; }
            </style>`;
                cd.head.insertAdjacentHTML("beforeend", roomFilterStyles);
            }

            const hidePwBarEl = cd.getElementById("beHidePasswordedBar");
            const nativePwForInit = cd.getElementById("roomlisthidepasswordedcheckbox");
            if (hidePwBarEl) {
                if (nativePwForInit) hidePwBarEl.checked = !!nativePwForInit.checked;
                else hidePwBarEl.checked = !!roomSettings.hidePassworded;
            }

            const hideSoloEl = cd.getElementById("beHideSolo");
            const minPlEl = cd.getElementById("beMinPl");
            const maxPlEl = cd.getElementById("beMaxPl");
            const favOnlyEl = cd.getElementById("beFavOnly");
            if (hideSoloEl) hideSoloEl.checked = roomSettings.hideSolo;
            if (minPlEl) minPlEl.value = roomSettings.minPlayers;
            if (maxPlEl) maxPlEl.value = roomSettings.maxPlayers;
            if (favOnlyEl) favOnlyEl.checked = roomSettings.favoritesOnly;

            function bindRoomFilterControls() {
                if (hideSoloEl) {
                    hideSoloEl.addEventListener("change", () => {
                        roomSettings.hideSolo = hideSoloEl.checked;
                        saveRoomSettings();
                        applyAllFilters();
                    });
                }
                const onNum = () => {
                    roomSettings.minPlayers = minPlEl?.value ?? "";
                    roomSettings.maxPlayers = maxPlEl?.value ?? "";
                    saveRoomSettings();
                    applyAllFilters();
                };
                if (minPlEl) minPlEl.addEventListener("input", onNum);
                if (maxPlEl) maxPlEl.addEventListener("input", onNum);
                if (favOnlyEl) {
                    favOnlyEl.addEventListener("change", () => {
                        roomSettings.favoritesOnly = favOnlyEl.checked;
                        saveRoomSettings();
                        applyAllFilters();
                    });
                }
            }
            bindRoomFilterControls();

            if (hidePwBarEl) {
                hidePwBarEl.addEventListener("change", () => {
                    const nat = cd.getElementById("roomlisthidepasswordedcheckbox");
                    if (nat) {
                        nat.checked = hidePwBarEl.checked;
                        const win = cd.defaultView || cw;
                        try {
                            nat.dispatchEvent(new win.Event("change", { bubbles: true }));
                        } catch {
                            const ev = cd.createEvent("HTMLEvents");
                            ev.initEvent("change", true, false);
                            nat.dispatchEvent(ev);
                        }
                    }
                    roomSettings.hidePassworded = hidePwBarEl.checked;
                    saveRoomSettings();
                    applyAllFilters();
                });
            }
            const nativeHidePw = cd.getElementById("roomlisthidepasswordedcheckbox");
            if (nativeHidePw && !nativeHidePw.dataset.beBarSync) {
                nativeHidePw.dataset.beBarSync = "1";
                nativeHidePw.addEventListener("change", () => {
                    const bar = cd.getElementById("beHidePasswordedBar");
                    if (bar) bar.checked = nativeHidePw.checked;
                    applyAllFilters();
                });
            }

            markHealth("outer.roomFilters", true, "injected");

            $(cd.body).off(".beRoomEnhanced");
            $(cd.body).on("click.beRoomEnhanced", "#roomlisttable tr", function (ev) {
                if (!ev.altKey) return;
                if (!isRoomDataRow(this)) return;
                ev.preventDefault();
                ev.stopPropagation();
                const name = getRoomNameFromRow(this);
                if (!name) return;
                const low = name.toLowerCase();
                const idx = roomSettings.favorites.findIndex((f) => (f || "").toLowerCase() === low);
                if (idx >= 0) roomSettings.favorites.splice(idx, 1);
                else roomSettings.favorites.push(name);
                saveRoomSettings();
                markFavoriteRows();
                applyAllFilters();
            });

            $(cd.body).on("click.beRoomEnhanced", "#roomlistrefreshbutton", () => {
                cw.setTimeout(() => {
                    markFavoriteRows();
                    applyAllFilters();
                }, 400);
            });

            function debounceWithRAF(fn) {
                let timeout;
                return function (...args) {
                    if (timeout) cw.cancelAnimationFrame(timeout);
                    timeout = cw.requestAnimationFrame(() => fn.apply(this, args));
                };
            }

            $(cd.body).on(
                "keyup.beRoomEnhanced",
                "#beRoomSearchInputBox, #roomSearchInputBox",
                debounceWithRAF(() => {
                    applyAllFilters();
                })
            );

            $(cd.body).on("keydown.beRoomEnhanced", debounceWithRAF((ev) => {
                if (ev.altKey) {
                    if (ev.key === "q" && $("#roomListContainer")[0]?.offsetParent === null) {
                        $("#pretty_top_exit").click();
                        $("#leaveconfirmwindow_okbutton").click();
                    } else if (ev.key === "r") {
                        $("#roomlistrefreshbutton").click();
                    }
                }
                if (ev.key === "/") {
                    const rs = getRoomSearchInputEl();
                    if (rs) $(rs).focus();
                }
            }));
            if (cd._beRoomListTableObs) {
                try {
                    cd._beRoomListTableObs.disconnect();
                } catch {}
                cd._beRoomListTableObs = null;
            }
            if (cd._beRoomFilterDebounceTimer) {
                try {
                    cw.clearTimeout(cd._beRoomFilterDebounceTimer);
                } catch {}
                cd._beRoomFilterDebounceTimer = null;
            }
            const roomTableEl = cd.getElementById("roomlisttable");
            if (roomTableEl) {
                const runRoomTableSync = () => {
                    markFavoriteRows();
                    applyAllFilters();
                };
                const scheduleRoomTableSync = () => {
                    if (cd._beRoomFilterDebounceTimer) cw.clearTimeout(cd._beRoomFilterDebounceTimer);
                    cd._beRoomFilterDebounceTimer = cw.setTimeout(() => {
                        cd._beRoomFilterDebounceTimer = null;
                        runRoomTableSync();
                    }, 48);
                };
                cd._beRoomListTableObs = new MutationObserver(() => {
                    scheduleRoomTableSync();
                });
                cd._beRoomListTableObs.observe(roomTableEl, { childList: true, subtree: true });
            }

            markFavoriteRows();
            applyAllFilters();
            markHealth("outer.bindings", true, "events attached");
        };

        let beRoomSetupTimer = null;
        const scheduleRoomSetup = () => {
            if (beRoomSetupTimer) cw.clearTimeout(beRoomSetupTimer);
            beRoomSetupTimer = cw.setTimeout(() => {
                beRoomSetupTimer = null;
                if (!cd.getElementById("roomlisttopbar")) return;
                if (cd.getElementById("beRoomFilterBar")) return;
                safeRun("outer.setupRetry", setup);
            }, 100);
        };

        const observer = new MutationObserver(() => scheduleRoomSetup());

        observer.observe(cd, { childList: true, subtree: true });
        markHealth("outer.observer", true, "active");
        safeRun("outer.setupInitial", setup);
        scheduleRoomSetup();
    }

    function beInjectDocStyle(doc, id, css) {
        try {
            if (!doc || doc.getElementById(id)) return;
            const s = doc.createElement("style");
            s.id = id;
            s.textContent = css;
            (doc.head || doc.documentElement).appendChild(s);
        } catch (e) {
            BE_LOG.debug("beInjectDocStyle", e);
        }
    }

    function beFullscreenGatherRootDocs(parentDoc) {
        const out = [];
        const seen = new Set();
        const push = (d) => {
            if (!d || !d.body || seen.has(d)) return;
            seen.add(d);
            out.push(d);
        };
        push(document);
        push(parentDoc);
        try {
            if (window.top && window.top.document) push(window.top.document);
        } catch (e) {
            void e;
        }
        return out;
    }

    /** Hide Bonk’s password-warning strip (copy can be split across nodes; banner may live on top window). */
    function beFullscreenHidePasswordBanners(doc) {
        const hidden = [];
        if (!doc || !doc.body) return hidden;

        /* Cheap probe: skip expensive XPath / querySelectorAll when the copy isn’t in this document. */
        try {
            const probe = (doc.body.textContent || "").slice(0, 120000).replace(/\s+/g, " ");
            if (!/easy\s+to\s+guess/i.test(probe)) return hidden;
            if (
                !/risk\s+losing\s+your\s+account/i.test(probe) &&
                !/change\s+it\s+in\s+settings/i.test(probe)
            ) {
                return hidden;
            }
        } catch (eQuick) {
            void eQuick;
        }

        function isPasswordStripText(t) {
            const z = String(t || "")
                .replace(/\s+/g, " ")
                .trim();
            if (z.length > 800) return false;
            const hasGuess = /easy\s+to\s+guess|password\s+easy/i.test(z);
            const hasFooter = /risk\s+losing\s+your\s+account|change\s+it\s+in\s+settings/i.test(z);
            return hasGuess && hasFooter;
        }

        function hideEl(el) {
            if (!el || el.nodeType !== 1 || el.dataset.beFsHidden === "1") return;
            el.dataset.beFsHidden = "1";
            el.dataset.bePasswordStripHidden = "1";
            el.style.setProperty("display", "none", "important");
            el.style.setProperty("visibility", "hidden", "important");
            el.style.setProperty("pointer-events", "none", "important");
            el.style.setProperty("max-height", "0", "important");
            el.style.setProperty("overflow", "hidden", "important");
            el.style.setProperty("margin", "0", "important");
            el.style.setProperty("padding", "0", "important");
            hidden.push(el);
        }

        try {
            const candidates = [];

            /* 1) XPath: subtree text can be split so string-value (.) matches even when no single text node does. */
            try {
                const xp =
                    "//*[contains(., 'password') and (contains(., 'guess') or contains(., 'Settings') or contains(., 'account'))]";
                const xr = doc.evaluate(xp, doc.body, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
                for (let i = 0; i < xr.snapshotLength; i++) {
                    const el = xr.snapshotItem(i);
                    if (el && el.nodeType === 1) candidates.push(el);
                }
            } catch (e1) {
                void e1;
            }

            /* 2) innerText pass (layout text; good when XPath isn’t available). */
            const tags = "div,section,footer,aside,p,header,nav,span,li,a,button,label";
            doc.querySelectorAll(tags).forEach((el) => {
                const t = (el.innerText || "").replace(/\s+/g, " ").trim();
                if (isPasswordStripText(t)) candidates.push(el);
            });

            const uniq = [];
            const seen = new Set();
            for (let u = 0; u < candidates.length; u++) {
                const el = candidates[u];
                if (!el || seen.has(el)) continue;
                seen.add(el);
                const t = (el.textContent || "").replace(/\s+/g, " ").trim();
                if (!isPasswordStripText(t)) continue;
                uniq.push(el);
            }

            /* Outermost nodes only (hide one wrapper, not every nested match). */
            const outer = uniq.filter(
                (a) => !uniq.some((b) => b !== a && b.contains(a))
            );
            for (let o = 0; o < outer.length; o++) hideEl(outer[o]);
        } catch (e) {
            BE_LOG.debug("beFullscreenHidePasswordBanners", e);
        }
        return hidden;
    }

    function beFullscreenRestoreHidden(els) {
        if (!els || !els.length) return;
        for (let i = 0; i < els.length; i++) {
            const el = els[i];
            try {
                if (el && el.dataset && el.dataset.beFsHidden === "1") {
                    if (el.dataset.bePasswordStripHidden === "1") continue;
                    el.style.removeProperty("display");
                    delete el.dataset.beFsHidden;
                }
            } catch (e) {
                void e;
            }
        }
    }

    /** Run on load and a few delayed passes so the strip stays hidden even when not in fullscreen. */
    function beScheduleGlobalPasswordBannerHiding() {
        let parentDoc = null;
        try {
            if (window.self !== window.top) parentDoc = window.parent.document;
        } catch (e) {
            parentDoc = null;
        }
        const sweep = () => {
            try {
                const docs = beFullscreenGatherRootDocs(parentDoc);
                for (let d = 0; d < docs.length; d++) {
                    beFullscreenHidePasswordBanners(docs[d]);
                }
            } catch (e) {
                BE_LOG.debug("beScheduleGlobalPasswordBannerHiding", e);
            }
        };
        sweep();
        [200, 1200, 4000, 8000].forEach((ms) => window.setTimeout(sweep, ms));
    }

    if (!isGameFrame) {
        scheduleBonkMainLoginAutofillBootstrap("outer.autofillInit");
        safeRun("outer.passwordBannerHide", () => {
            const run = () => beScheduleGlobalPasswordBannerHiding();
            if (document.body) run();
            else document.addEventListener("DOMContentLoaded", run, { once: true });
        });
        if (BE_CONFIG.featureFlags.enableOuterMods) {
            window.addEventListener("load", () => safeRun("outer.init", initOuterCustomMods));
        } else {
            markHealth("outer.init", false, "feature flag disabled");
        }
        return;
    }

    scheduleBonkMainLoginAutofillBootstrap("gameframe.autofillInit");
    safeRun("gameframe.passwordBannerHide", () => {
        const run = () => beScheduleGlobalPasswordBannerHiding();
        if (document.body) run();
        else document.addEventListener("DOMContentLoaded", run, { once: true });
    });

    const BE_FS_SCALE_CLASSES = [
        "be-fs-scale-contain",
        "be-fs-scale-contain-left",
        "be-fs-scale-contain-right",
        "be-fs-scale-wdb-left",
        "be-fs-scale-wdb-right",
        "be-fs-scale-cover",
        "be-fs-scale-fill"
    ];

    /** Stretch layout in fullscreen is required for scale modes (letterbox/cover/fill). The old checkbox is gone — always on so the settings dropdown always takes effect. */
    function beFullscreenStretchWanted() {
        return true;
    }

    /**
     * With stretch on: how the canvas maps into the fullscreen box.
     * contain = letterbox, full map, uniform scale, centered.
     * contain-left / contain-right = same as contain but one pillar (asymmetric letterbox).
     * wdb-left / wdb-right = contain + pin, scale(~1.03) + translateY(vh) so overflow clips less
     *   off the map top; not cover/fill.
     * cover = no letterboxing, uniform scale, may crop.
     * fill = no letterboxing, no crop, non-uniform scale (slight stretch) to match the monitor.
     */
    function beFullscreenScaleMode() {
        try {
            const m = localStorage.getItem("be_fullscreen_scale_mode");
            if (
                m === "contain" ||
                m === "contain-left" ||
                m === "contain-right" ||
                m === "wdb-left" ||
                m === "wdb-right" ||
                m === "cover" ||
                m === "fill"
            ) {
                return m;
            }
            return localStorage.getItem("be_fullscreen_fill_cover") === "1" ? "cover" : "contain";
        } catch {
            return "contain";
        }
    }

    function beApplyFullscreenScaleBodyClasses(bodyEl) {
        if (!bodyEl) return;
        for (let i = 0; i < BE_FS_SCALE_CLASSES.length; i++) {
            bodyEl.classList.remove(BE_FS_SCALE_CLASSES[i]);
        }
        bodyEl.classList.add(`be-fs-scale-${beFullscreenScaleMode()}`);
    }

    function beSyncFullscreenFitClasses() {
        try {
            if (!document.body.classList.contains("fullscreen")) return;
            document.body.classList.toggle("be-fs-stretch", beFullscreenStretchWanted());
            beApplyFullscreenScaleBodyClasses(document.body);
        } catch (e) {
            BE_LOG.debug("beSyncFullscreenFitClasses", e);
        }
    }

    /**
     * Excigma-style fullscreen: toggles body.fullscreen on the gameframe (and parent when embedded),
     * with the control on Bonk’s native top bar — left of the theme / paint button when detectable.
     */
    function beFindPrettyTopPaintAnchor(bar) {
        const byId = [
            "pretty_top_bonk_theme",
            "pretty_top_theme",
            "pretty_top_colormap",
            "pretty_top_colours",
            "pretty_top_colors"
        ];
        for (let i = 0; i < byId.length; i++) {
            const el = document.getElementById(byId[i]);
            if (el && bar.contains(el)) return el;
        }
        const btns = bar.querySelectorAll(":scope > .pretty_top_button.niceborderleft[id]");
        for (let j = 0; j < btns.length; j++) {
            const id = (btns[j].id || "").toLowerCase();
            if (/theme|colour|color|skin|paint|roller/.test(id)) return btns[j];
        }
        return null;
    }

    function beInjectPrettyTopClipRecordCss() {
        beInjectDocStyle(
            document,
            "be-pretty-top-clip-record",
            `
#pretty_top_clip_wrap {
    position: absolute;
    top: 0;
    width: 58px;
    height: 34px;
    z-index: 100002;
}
#pretty_top_clip_record {
    width: 58px;
    height: 34px;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 24 24'%3E%3Cpath d='M17 10.5V7c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h12c.55 0 1-.45 1-1v-3.5l4 4v-11l-4 4z'/%3E%3C/svg%3E");
    background-size: 24px 24px;
    background-repeat: no-repeat;
    background-position: center;
    position: relative;
    left: 0;
    top: 0;
    cursor: pointer;
}
#pretty_top_clip_record.be-recording {
    box-shadow: inset 0 0 0 2px rgba(255, 72, 72, 0.95);
}
/* Parent stays “held” while pointer is over the submenu (same wrap:hover). */
#pretty_top_clip_wrap:hover #pretty_top_clip_record:not(.be-recording) {
    filter: brightness(1.14);
}
#pretty_top_clip_wrap:hover #pretty_top_clip_record.be-recording {
    filter: none;
}
#pretty_top_clip_menu {
    display: none;
    position: absolute;
    left: 0;
    top: calc(100% - 1px);
    z-index: 100003;
    margin: 0;
    padding: 0;
    width: 58px;
    height: 34px;
    background: transparent;
    border: none;
    box-shadow: none;
    border-radius: 0;
    pointer-events: auto;
}
#pretty_top_clip_wrap:hover #pretty_top_clip_menu {
    display: block;
}
/* Same cell as native .pretty_top_button (58×34 including Bonk borders). */
#pretty_top_clip_last15.pretty_top_button {
    display: flex !important;
    align-items: center !important;
    justify-content: center !important;
    width: 58px !important;
    height: 34px !important;
    min-width: 58px !important;
    min-height: 34px !important;
    max-width: 58px !important;
    max-height: 34px !important;
    margin: 0 !important;
    padding: 0 !important;
    box-sizing: border-box !important;
    cursor: pointer;
    -webkit-appearance: none;
    appearance: none;
    font: inherit;
    color: #f5f5f5 !important;
    background-color: rgba(38, 36, 34, 0.98) !important;
    background-image: none !important;
}
#pretty_top_clip_last15.pretty_top_button:hover {
    background-color: rgba(50, 48, 45, 0.99) !important;
    filter: brightness(1.06);
}
#pretty_top_clip_last15 svg {
    display: block;
    pointer-events: none;
    width: 46px;
    height: 26px;
    flex-shrink: 0;
}
`
        );
    }

    const BE_CLIP_RING_MS = 15000;
    const beClipRing = {
        recorder: null,
        chunks: [],
        stream: null,
        started: false,
        ensureRetries: 0
    };

    function beTrimClipRingChunks() {
        const now = Date.now();
        while (beClipRing.chunks.length && now - beClipRing.chunks[0].t > BE_CLIP_RING_MS) {
            beClipRing.chunks.shift();
        }
    }

    function beStopClipRingBuffer() {
        try {
            if (beClipRing.recorder && beClipRing.recorder.state !== "inactive") {
                beClipRing.recorder.stop();
            }
        } catch (e) {
            void e;
        }
        beClipRing.recorder = null;
        try {
            if (beClipRing.stream) beClipRing.stream.getTracks().forEach((t) => t.stop());
        } catch (e2) {
            void e2;
        }
        beClipRing.stream = null;
        beClipRing.chunks = [];
        beClipRing.started = false;
    }

    function beStartClipRingBuffer() {
        if (beClipRing.started) return;
        const canvas = document.querySelector("#gamerenderer canvas");
        if (!canvas || !window.MediaRecorder) return;
        let stream;
        try {
            stream = canvas.captureStream(30);
        } catch (e) {
            return;
        }
        const rec = beCreateMediaRecorderPreferMp4(stream);
        if (!rec) {
            try {
                stream.getTracks().forEach((t) => t.stop());
            } catch (e2) {
                void e2;
            }
            return;
        }
        beClipRing.chunks = [];
        rec.ondataavailable = (ev) => {
            if (ev.data && ev.data.size) {
                beClipRing.chunks.push({ t: Date.now(), data: ev.data });
                beTrimClipRingChunks();
            }
        };
        try {
            rec.start(400);
            beClipRing.recorder = rec;
            beClipRing.stream = stream;
            beClipRing.started = true;
        } catch (e6) {
            BE_LOG.debug("clipRing.start", e6);
            try {
                stream.getTracks().forEach((t) => t.stop());
            } catch (e7) {
                void e7;
            }
        }
    }

    function beSaveClipLast15Seconds() {
        beTrimClipRingChunks();
        if (!beClipRing.chunks.length) {
            window.alert(
                "No buffered video yet — stay in a match for a few seconds so the rolling buffer can fill, then try again."
            );
            return;
        }
        const blobs = beClipRing.chunks.map((c) => c.data);
        const mime =
            (beClipRing.recorder && beClipRing.recorder.mimeType) ||
            (beClipRing.chunks[0] && beClipRing.chunks[0].data && beClipRing.chunks[0].data.type) ||
            "video/webm";
        try {
            const blob = new Blob(blobs, { type: mime });
            const ext = beClipFileExtensionForMime(mime);
            beDownloadBlobToPc(blob, `bonk-last15-${Date.now()}.${ext}`);
        } catch (e) {
            BE_LOG.debug("clipRing.save", e);
            window.alert("Could not build the last-15s clip.");
        }
    }

    function beEnsureClipRingBuffer() {
        if (beClipRing.started) {
            beClipRing.ensureRetries = 0;
            return;
        }
        beStartClipRingBuffer();
        if (!beClipRing.started && beClipRing.ensureRetries < 40) {
            beClipRing.ensureRetries++;
            window.setTimeout(beEnsureClipRingBuffer, 2000);
        }
    }

    function beBuildPrettyTopClipWrap(rightCss) {
        const wrap = document.createElement("div");
        wrap.id = "pretty_top_clip_wrap";
        wrap.style.position = "absolute";
        wrap.style.top = "0";
        wrap.style.right = rightCss;
        wrap.style.width = "58px";
        wrap.style.height = "34px";

        const clipBtn = document.createElement("div");
        clipBtn.id = "pretty_top_clip_record";
        clipBtn.title = "Record clip to PC (click to start) — hover for last 15s";
        clipBtn.classList.add("pretty_top_button", "niceborderleft");

        const menu = document.createElement("div");
        menu.id = "pretty_top_clip_menu";
        menu.setAttribute("role", "menu");

        const last15 = document.createElement("button");
        last15.type = "button";
        last15.id = "pretty_top_clip_last15";
        last15.classList.add("pretty_top_button", "niceborderleft");
        last15.setAttribute("aria-label", "Record last 15 seconds to PC");
        last15.title = "Save the last 15 seconds of gameplay (MP4 or WebM — depends on browser)";
        last15.innerHTML =
            '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 56 46" aria-hidden="true">' +
            '<path fill="none" stroke="#ffffff" stroke-width="2.1" stroke-linecap="round" d="M41 33.5A15 15 0 1 1 28 11"/>' +
            '<path fill="#ffffff" d="M29 8.5l7 2.5-7 2.5v-5z"/>' +
            '<text x="28" y="31" text-anchor="middle" fill="#ffffff" font-size="12" font-weight="700" font-family="Segoe UI,system-ui,sans-serif">15</text>' +
            '<text x="28" y="42" text-anchor="middle" fill="#ffffff" font-size="7.5" font-weight="700" font-family="Segoe UI,system-ui,sans-serif">SEC</text>' +
            "</svg>";

        menu.appendChild(last15);
        wrap.appendChild(clipBtn);
        wrap.appendChild(menu);
        return wrap;
    }

    /** Prefer MP4 (Chrome/Edge often support H.264); fall back to WebM. Returns null if nothing works. */
    function beCreateMediaRecorderPreferMp4(stream) {
        if (!window.MediaRecorder) return null;
        const candidates = [
            "video/mp4;codecs=avc1.42E01E",
            "video/mp4;codecs=avc1.4D401E",
            "video/mp4;codecs=avc1.640028",
            "video/mp4",
            "video/webm;codecs=vp9,opus",
            "video/webm;codecs=vp8,opus",
            "video/webm;codecs=vp9",
            "video/webm;codecs=vp8",
            "video/webm"
        ];
        for (let i = 0; i < candidates.length; i++) {
            try {
                if (!MediaRecorder.isTypeSupported(candidates[i])) continue;
                const r = new MediaRecorder(stream, { mimeType: candidates[i] });
                return r;
            } catch (e) {
                void e;
            }
        }
        try {
            return new MediaRecorder(stream);
        } catch (e2) {
            BE_LOG.debug("beCreateMediaRecorderPreferMp4.fallback", e2);
            return null;
        }
    }

    function beClipFileExtensionForMime(mime) {
        const m = (mime || "").toLowerCase();
        if (m.includes("mp4")) return "mp4";
        if (m.includes("webm")) return "webm";
        return "webm";
    }

    function beDownloadBlobToPc(blob, filename) {
        const url = URL.createObjectURL(blob);
        const a = document.createElement("a");
        a.href = url;
        a.download = filename;
        a.click();
        URL.revokeObjectURL(url);
    }

    function beUpgradeClipRecordToWrap(bar) {
        const old = document.getElementById("pretty_top_clip_record");
        if (!old || document.getElementById("pretty_top_clip_wrap")) return;
        let right = old.style.right;
        if (!right) {
            try {
                right = getComputedStyle(old).right;
            } catch (e) {
                return;
            }
        }
        old.remove();
        beInjectPrettyTopClipRecordCss();
        const wrap = beBuildPrettyTopClipWrap(right);
        bar.appendChild(wrap);
        beWirePrettyTopClipRecord(wrap.querySelector("#pretty_top_clip_record"));
    }

    function beWirePrettyTopClipRecord(clipBtn) {
        let recorder = null;
        let chunks = [];
        let recording = false;
        let stream = null;

        const last15 = document.getElementById("pretty_top_clip_last15");
        if (last15) {
            last15.addEventListener("mousedown", (e) => e.stopPropagation());
            last15.addEventListener("click", (e) => {
                e.preventDefault();
                e.stopPropagation();
                beSaveClipLast15Seconds();
            });
        }

        clipBtn.addEventListener("click", (e) => {
            e.stopPropagation();
            if (!window.MediaRecorder) {
                window.alert("Clip recording is not supported in this browser.");
                return;
            }
            const canvas = document.querySelector("#gamerenderer canvas");
            if (!canvas) {
                window.alert("Game canvas not found — enter a match and try again.");
                return;
            }
            if (recording) {
                try {
                    if (recorder && recorder.state !== "inactive") recorder.stop();
                } catch (e2) {
                    BE_LOG.debug("clipRecord.stop", e2);
                }
                recording = false;
                clipBtn.classList.remove("be-recording");
                clipBtn.title = "Record clip to PC (click to start) — hover for last 15s";
                return;
            }
            beStopClipRingBuffer();
            chunks = [];
            try {
                stream = canvas.captureStream(30);
            } catch (e3) {
                window.alert("Could not capture the canvas: " + (e3 && e3.message ? e3.message : String(e3)));
                return;
            }
            recorder = beCreateMediaRecorderPreferMp4(stream);
            if (!recorder) {
                try {
                    stream.getTracks().forEach((t) => t.stop());
                } catch (e4) {
                    void e4;
                }
                window.alert("No supported video recording codec (MP4/WebM) in this browser.");
                return;
            }
            recorder.ondataavailable = (ev) => {
                if (ev.data && ev.data.size) chunks.push(ev.data);
            };
            recorder.onstop = () => {
                recording = false;
                clipBtn.classList.remove("be-recording");
                clipBtn.title = "Record clip to PC (click to start) — hover for last 15s";
                try {
                    const mt = recorder.mimeType || "video/webm";
                    const blob = new Blob(chunks, { type: mt });
                    const ext = beClipFileExtensionForMime(mt);
                    beDownloadBlobToPc(blob, `bonk-clip-${Date.now()}.${ext}`);
                } catch (e8) {
                    BE_LOG.debug("clipRecord.blob", e8);
                    window.alert("Could not save the clip.");
                }
                chunks = [];
                recorder = null;
                try {
                    if (stream) stream.getTracks().forEach((t) => t.stop());
                } catch (e9) {
                    void e9;
                }
                stream = null;
                window.setTimeout(() => {
                    beClipRing.ensureRetries = 0;
                    beEnsureClipRingBuffer();
                }, 450);
            };
            try {
                recorder.start(250);
                recording = true;
                clipBtn.classList.add("be-recording");
                clipBtn.title = "Recording… click again to stop and save (MP4 or WebM)";
            } catch (e10) {
                BE_LOG.debug("clipRecord.start", e10);
                window.alert("Could not start recording.");
                try {
                    stream.getTracks().forEach((t) => t.stop());
                } catch (e11) {
                    void e11;
                }
                stream = null;
                recorder = null;
                window.setTimeout(() => {
                    beClipRing.ensureRetries = 0;
                    beEnsureClipRingBuffer();
                }, 450);
            }
        });

        window.setTimeout(() => {
            beClipRing.ensureRetries = 0;
            beEnsureClipRingBuffer();
        }, 500);
    }

    /**
     * Upgrade path: fullscreen already installed from an older build — add clip button + layout only.
     */
    function beInstallPrettyTopClipRecordOnly(bar) {
        beInjectPrettyTopClipRecordCss();
        const fs = document.getElementById("pretty_top_fullscreen");
        if (!fs) return;
        if (document.getElementById("pretty_top_clip_wrap")) return;
        if (document.getElementById("pretty_top_clip_record")) {
            beUpgradeClipRecordToWrap(bar);
            return;
        }
        const paint = beFindPrettyTopPaintAnchor(bar);
        let pr;
        try {
            const fsR = parseFloat(getComputedStyle(fs).right);
            const paintR = paint ? parseFloat(getComputedStyle(paint).right) : NaN;
            if (Number.isFinite(fsR) && Number.isFinite(paintR)) {
                if (paintR > fsR) {
                    pr = fsR;
                } else {
                    pr = paintR;
                    paint.style.right = `${pr + 116}px`;
                    fs.style.right = `${pr}px`;
                }
            } else if (Number.isFinite(fsR)) {
                pr = fsR - 58;
                if (paint) paint.style.right = `${pr + 116}px`;
                fs.style.right = `${pr}px`;
            }
        } catch (e) {
            BE_LOG.debug("beInstallPrettyTopClipRecordOnly.layout", e);
            return;
        }
        if (!Number.isFinite(pr)) return;

        const lc = document.getElementById("pretty_top_be_launcher");
        if (lc) lc.style.right = `${pr + 174}px`;

        const wrap = beBuildPrettyTopClipWrap(`${pr + 58}px`);
        bar.appendChild(wrap);
        beWirePrettyTopClipRecord(wrap.querySelector("#pretty_top_clip_record"));
    }

    function beInstallPrettyTopFullscreenOnce() {
        if (document.getElementById("pretty_top_fullscreen")) {
            const bar = document.getElementById("pretty_top_bar");
            if (bar && !document.getElementById("pretty_top_clip_wrap")) {
                if (document.getElementById("pretty_top_clip_record")) {
                    beUpgradeClipRecordToWrap(bar);
                } else {
                    beInstallPrettyTopClipRecordOnly(bar);
                }
            }
            return true;
        }
        const bar = document.getElementById("pretty_top_bar");
        if (!bar) return false;

        const inIframe = window.self !== window.top;
        let parentDoc = null;
        try {
            if (inIframe) parentDoc = window.parent.document;
        } catch (e) {
            parentDoc = null;
        }

        const FULLSCREEN_INNER_CSS = `
#pretty_top_fullscreen {
    width: 58px;
    height: 34px;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 24 24'%3E%3Cpath d='M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z'/%3E%3C/svg%3E");
    background-size: 24px 24px;
    background-repeat: no-repeat;
    background-position: center;
    position: absolute;
    top: 0;
}
.fullscreen #pretty_top_fullscreen {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 24 24'%3E%3Cpath d='M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z'/%3E%3C/svg%3E") !important;
}
.fullscreen #bonkiocontainer {
    height: 100vh !important;
    width: 100vw !important;
    border: none !important;
}
.fullscreen #ingamecountdown {
    opacity: 75%;
}
.fullscreen #gamerenderer {
    text-align: center !important;
}
.fullscreen #gamerenderer>canvas {
    display: inline-block !important;
}
.fullscreen #bgreplay {
    text-align: center !important;
}
.fullscreen #bgreplay>canvas {
    display: inline-block !important;
}
.fullscreen #xpbarcontainer {
    top: -3px !important;
}
html.fullscreen,
html.fullscreen body {
    overflow: hidden !important;
    height: 100% !important;
    max-height: 100vh !important;
}
body.fullscreen {
    margin: 0 !important;
}
/* Edge-to-edge canvas: reduces pillarboxing (optional via Settings / localStorage). */
.fullscreen.be-fs-stretch #bonkiocontainer {
    display: flex !important;
    flex-direction: column !important;
    align-items: stretch !important;
}
.fullscreen.be-fs-stretch #gamerenderer {
    flex: 1 1 auto !important;
    min-height: 0 !important;
    width: 100% !important;
    max-width: none !important;
    text-align: center !important;
    display: flex !important;
    align-items: center !important;
    justify-content: center !important;
}
.fullscreen.be-fs-stretch.be-fs-scale-contain #gamerenderer canvas,
.fullscreen.be-fs-stretch.be-fs-scale-contain-left #gamerenderer canvas,
.fullscreen.be-fs-stretch.be-fs-scale-contain-right #gamerenderer canvas {
    width: 100% !important;
    height: 100% !important;
    max-width: none !important;
    object-fit: contain !important;
}
/* Letterbox alignment: canvas is full-size; object-fit scales the map — use object-position to pin the fitted image (flex justify does nothing at 100% width). */
.fullscreen.be-fs-stretch.be-fs-scale-contain #gamerenderer canvas {
    object-position: center center !important;
}
/* Pillar left → map on the right */
.fullscreen.be-fs-stretch.be-fs-scale-contain-left #gamerenderer canvas {
    object-position: right center !important;
}
/* Pillar right → map on the left */
.fullscreen.be-fs-stretch.be-fs-scale-contain-right #gamerenderer canvas {
    object-position: left center !important;
}
/* WDB: contain + pin, then light scale + small translateY (after scale) so overflow clips less from the map top. */
.fullscreen.be-fs-stretch.be-fs-scale-wdb-left #gamerenderer,
.fullscreen.be-fs-stretch.be-fs-scale-wdb-right #gamerenderer {
    overflow: hidden !important;
}
.fullscreen.be-fs-stretch.be-fs-scale-wdb-left #gamerenderer canvas {
    width: 100% !important;
    height: 100% !important;
    max-width: none !important;
    object-fit: contain !important;
    object-position: right center !important;
    transform: scale(1.03) translateY(1.5vh) !important;
    transform-origin: right center !important;
}
.fullscreen.be-fs-stretch.be-fs-scale-wdb-right #gamerenderer canvas {
    width: 100% !important;
    height: 100% !important;
    max-width: none !important;
    object-fit: contain !important;
    object-position: left center !important;
    transform: scale(1.03) translateY(1.5vh) !important;
    transform-origin: left center !important;
}
.fullscreen.be-fs-stretch.be-fs-scale-cover #gamerenderer canvas {
    width: 100% !important;
    height: 100% !important;
    max-width: none !important;
    object-fit: cover !important;
}
.fullscreen.be-fs-stretch.be-fs-scale-fill #gamerenderer canvas {
    width: 100% !important;
    height: 100% !important;
    max-width: none !important;
    object-fit: fill !important;
}
.fullscreen.be-fs-stretch #bgreplay {
    width: 100% !important;
    text-align: center !important;
}
.fullscreen.be-fs-stretch.be-fs-scale-contain #bgreplay canvas,
.fullscreen.be-fs-stretch.be-fs-scale-contain-left #bgreplay canvas,
.fullscreen.be-fs-stretch.be-fs-scale-contain-right #bgreplay canvas {
    width: 100% !important;
    max-width: none !important;
    object-fit: contain !important;
}
.fullscreen.be-fs-stretch.be-fs-scale-contain #bgreplay canvas {
    object-position: center center !important;
}
.fullscreen.be-fs-stretch.be-fs-scale-contain-left #bgreplay canvas {
    object-position: right center !important;
}
.fullscreen.be-fs-stretch.be-fs-scale-contain-right #bgreplay canvas {
    object-position: left center !important;
}
.fullscreen.be-fs-stretch.be-fs-scale-wdb-left #bgreplay,
.fullscreen.be-fs-stretch.be-fs-scale-wdb-right #bgreplay {
    overflow: hidden !important;
}
.fullscreen.be-fs-stretch.be-fs-scale-wdb-left #bgreplay canvas {
    width: 100% !important;
    max-width: none !important;
    object-fit: contain !important;
    object-position: right center !important;
    transform: scale(1.03) translateY(1.5vh) !important;
    transform-origin: right center !important;
}
.fullscreen.be-fs-stretch.be-fs-scale-wdb-right #bgreplay canvas {
    width: 100% !important;
    max-width: none !important;
    object-fit: contain !important;
    object-position: left center !important;
    transform: scale(1.03) translateY(1.5vh) !important;
    transform-origin: left center !important;
}
.fullscreen.be-fs-stretch.be-fs-scale-cover #bgreplay canvas {
    width: 100% !important;
    max-width: none !important;
    object-fit: cover !important;
}
.fullscreen.be-fs-stretch.be-fs-scale-fill #bgreplay canvas {
    width: 100% !important;
    height: 100% !important;
    max-width: none !important;
    object-fit: fill !important;
}
`;
        const FULLSCREEN_PARENT_CSS = `
html.fullscreen,
html.fullscreen body {
    overflow: hidden !important;
    height: 100% !important;
    max-height: 100vh !important;
    margin: 0 !important;
}
body.fullscreen {
    overflow: hidden !important;
}
.fullscreen #maingameframe {
    position: fixed !important;
    margin-top: 0px !important;
    left: 0 !important;
    top: 0 !important;
    width: 100vw !important;
    height: 100vh !important;
    max-width: 100vw !important;
    max-height: 100vh !important;
    z-index: 99999 !important;
    border: none !important;
}
.fullscreen #bonkioheader {
    display: none !important;
}
.fullscreen #descriptioncontainer {
    display: none !important;
}
`;

        beInjectDocStyle(document, "be-fullscreen-inner", FULLSCREEN_INNER_CSS);
        if (parentDoc) beInjectDocStyle(parentDoc, "be-fullscreen-parent", FULLSCREEN_PARENT_CSS);
        beInjectDocStyle(
            document,
            "be-pretty-top-overlay-hint",
            `
/* Same cell size as #pretty_top_fullscreen / native .pretty_top_button (58×34). */
#pretty_top_be_launcher {
    display: none;
    width: 58px !important;
    height: 34px !important;
    min-width: 58px !important;
    min-height: 34px !important;
    box-sizing: border-box !important;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0;
    padding: 0 1px !important;
    margin: 0 !important;
    overflow: hidden;
    cursor: pointer;
    font: 700 10px/1 "Segoe UI", system-ui, sans-serif;
    color: rgba(255, 255, 255, 0.92);
    letter-spacing: 0.02em;
}
#pretty_top_be_launcher .be-top-launcher-row {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 4px;
    line-height: 1;
}
#pretty_top_be_launcher .be-top-launcher-dot {
    width: 6px;
    height: 6px;
    border-radius: 50%;
    /* Default matches tracker idle border; JS syncs to capped/farming/idle. */
    background: rgba(255, 170, 0, 0.9);
    flex-shrink: 0;
}
#pretty_top_be_launcher .be-top-launcher-hk {
    font-size: 8px;
    font-weight: 700;
    line-height: 1;
    opacity: 0.8;
    max-width: 54px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
`
        );

        const mapEditor = document.getElementById("mapeditor");
        const lobby = document.getElementById("newbonklobby");

        const limitSize = () => {
            if (!mapEditor || !lobby) return;
            const mapEditorStyle = mapEditor.style;
            const lobbyStyle = lobby.style;
            const vw = inIframe ? window.parent.innerWidth : window.innerWidth;
            const vh = inIframe ? window.parent.innerHeight : window.innerHeight;
            const max_width = `${Math.min(vw / 730, vh / 500) * 730 * 0.9}px`;
            mapEditorStyle.maxHeight = "calc(100vh - 70px)";
            lobbyStyle.maxHeight = "calc(100vh - 70px)";
            mapEditorStyle.maxWidth = max_width;
            lobbyStyle.maxWidth = max_width;
        };

        let fullscreenOn = false;
        let beFullscreenHiddenBannerEls = [];
        let beFsToggleGen = 0;

        beInjectPrettyTopClipRecordCss();

        const btn = document.createElement("div");
        btn.id = "pretty_top_fullscreen";
        btn.title = "Toggle fullscreen";
        btn.classList.add("pretty_top_button", "niceborderleft");

        const paint = beFindPrettyTopPaintAnchor(bar);
        let rightPx;
        if (paint) {
            try {
                const cs = getComputedStyle(paint);
                if (cs.position === "absolute") {
                    const pr0 = parseFloat(cs.right);
                    if (Number.isFinite(pr0)) {
                        rightPx = pr0;
                        paint.style.right = `${pr0 + 116}px`;
                    }
                }
            } catch (e2) {
                void e2;
            }
        }
        if (rightPx === undefined) {
            const n = bar.querySelectorAll(":scope > .pretty_top_button.niceborderleft").length;
            rightPx = n * 58 + 1;
        }

        const clipWrap = beBuildPrettyTopClipWrap(`${rightPx + 58}px`);

        btn.style.right = `${rightPx}px`;

        const launcherChip = document.createElement("div");
        launcherChip.id = "pretty_top_be_launcher";
        launcherChip.className = "pretty_top_button niceborderleft";
        launcherChip.title = "Open Bonk Enhanced overlay";
        launcherChip.innerHTML =
            '<div class="be-top-launcher-row"><span class="be-top-launcher-dot" aria-hidden="true"></span><span>BE</span></div><span id="pretty_top_be_launcher_hk" class="be-top-launcher-hk"></span>';
        launcherChip.style.position = "absolute";
        launcherChip.style.top = "0";
        launcherChip.style.right = `${rightPx + 174}px`;
        launcherChip.style.setProperty("display", "none", "important");
        launcherChip.addEventListener("click", (e) => {
            e.stopPropagation();
            setOverlayVisibility(true);
        });
        bar.appendChild(launcherChip);
        bar.appendChild(clipWrap);
        bar.appendChild(btn);
        beWirePrettyTopClipRecord(clipWrap.querySelector("#pretty_top_clip_record"));

        window.setTimeout(() => {
            const b = document.getElementById("pretty_top_fullscreen");
            const clip = document.getElementById("pretty_top_clip_wrap");
            const lc = document.getElementById("pretty_top_be_launcher");
            const br = document.getElementById("pretty_top_bar");
            if (!b || !br) return;
            const paintLate = beFindPrettyTopPaintAnchor(br);
            if (!paintLate) return;
            try {
                const cs = getComputedStyle(paintLate);
                if (cs.position !== "absolute") return;
                const paintR = parseFloat(cs.right);
                const fsR = parseFloat(getComputedStyle(b).right);
                if (!Number.isFinite(paintR) || !Number.isFinite(fsR)) return;
                const pr = paintR > fsR ? fsR : paintR;
                b.style.right = `${pr}px`;
                if (clip) clip.style.right = `${pr + 58}px`;
                paintLate.style.right = `${pr + 116}px`;
                if (lc) lc.style.right = `${pr + 174}px`;
            } catch (e4) {
                void e4;
            }
        }, 800);

        const applyFullscreenDom = (on) => {
            const rootBody = document.body;
            let outerBody = null;
            let outerHtml = null;
            try {
                if (inIframe && parentDoc) {
                    outerBody = parentDoc.body;
                    outerHtml = parentDoc.documentElement;
                }
            } catch (e3) {
                void e3;
            }
            const mergeBannerHidden = (add) => {
                for (let i = 0; i < add.length; i++) {
                    if (beFullscreenHiddenBannerEls.indexOf(add[i]) < 0) {
                        beFullscreenHiddenBannerEls.push(add[i]);
                    }
                }
            };

            const sweepPasswordBanners = () => {
                const docs = beFullscreenGatherRootDocs(parentDoc);
                for (let d = 0; d < docs.length; d++) {
                    mergeBannerHidden(beFullscreenHidePasswordBanners(docs[d]));
                }
            };

            if (on) {
                const g = ++beFsToggleGen;
                document.documentElement.classList.add("fullscreen");
                rootBody.classList.add("fullscreen");
                rootBody.classList.toggle("be-fs-stretch", beFullscreenStretchWanted());
                beApplyFullscreenScaleBodyClasses(rootBody);
                if (outerHtml) outerHtml.classList.add("fullscreen");
                if (outerBody) outerBody.classList.add("fullscreen");
                beFullscreenRestoreHidden(beFullscreenHiddenBannerEls);
                beFullscreenHiddenBannerEls = [];
                sweepPasswordBanners();
                /* A few delayed sweeps only (no MutationObserver — observing the whole game DOM caused severe lag). */
                [200, 1200, 4000].forEach((ms) => {
                    window.setTimeout(() => {
                        if (!fullscreenOn || g !== beFsToggleGen) return;
                        sweepPasswordBanners();
                    }, ms);
                });
                limitSize();
            } else {
                beFsToggleGen++;
                beFullscreenRestoreHidden(beFullscreenHiddenBannerEls);
                beFullscreenHiddenBannerEls = [];
                document.documentElement.classList.remove("fullscreen");
                rootBody.classList.remove("fullscreen", "be-fs-stretch", ...BE_FS_SCALE_CLASSES);
                if (outerHtml) outerHtml.classList.remove("fullscreen");
                if (outerBody) outerBody.classList.remove("fullscreen");
                if (mapEditor) {
                    mapEditor.style.maxHeight = "calc(100vh - 70px)";
                    mapEditor.style.maxWidth = "90vw";
                }
                if (lobby) {
                    lobby.style.maxHeight = "100%";
                    lobby.style.maxWidth = "100%";
                }
            }
        };

        btn.addEventListener("click", () => {
            fullscreenOn = !fullscreenOn;
            applyFullscreenDom(fullscreenOn);
        });

        window.addEventListener("resize", () => {
            if (fullscreenOn) window.setTimeout(limitSize, 50);
        });

        return true;
    }

    safeRun("gameframe.prettyTopFullscreen", () => {
        const tryInstall = () => {
            if (document.getElementById("pretty_top_fullscreen")) return true;
            if (!document.getElementById("pretty_top_bar")) return false;
            return beInstallPrettyTopFullscreenOnce();
        };
        if (tryInstall()) return;
        const mo = new MutationObserver(() => {
            if (tryInstall()) mo.disconnect();
        });
        mo.observe(document.documentElement, { childList: true, subtree: true });
        window.setTimeout(() => mo.disconnect(), 120000);
    });

    const Events = {
    events: {},

    on(name, fn) {
        if (!this.events[name]) this.events[name] = [];
        this.events[name].push(fn);
    },

    emit(name, data) {
        if (!this.events[name]) return;
        this.events[name].forEach(fn => fn(data));
    }
};
const Storage = {
    cache: null,

    /** Calendar day for stats buckets (UTC YYYY-MM-DD). Matches existing localStorage keys. */
    getTodayKey() {
        return new Date().toISOString().slice(0, 10);
    },

    getPlayerKey() {
        return document.getElementById("pretty_top_name")?.innerText || "Guest";
    },

    load() {
        const key = this.getTodayKey();
        const player = this.getPlayerKey();
        const storageKey = "bonk_stats_" + player;

        /* Must not reuse cache across midnight or account switch — otherwise wins/XP keep updating yesterday's key. */
        if (this.cache && this.cache.storageKey === storageKey && this.cache.key === key) {
            return this.cache;
        }

        const data = JSON.parse(localStorage.getItem(storageKey) || "{}");

if (!data[key]) {
    data[key] = {
        wins: 0,
        xp: 0,
        baselineXP: null,
        _synced: !!data._globalXP // 🔥 treat cache as synced if exists
    };
}
if (data[key]._synced === undefined) {
    data[key]._synced = !!data._globalXP;
}

        this.cache = { data, key, storageKey };
        return this.cache;
    },

    save(data, storageKey) {
        localStorage.setItem(storageKey, JSON.stringify(data));

        if (this.cache) {
            this.cache.data = data;
        }
    },

    invalidate() {
        this.cache = null;
    }
};

    const BE_ALTS_KEY = "be_alts_registry_v9";
    const BE_FRIENDS_KEY = "be_friends_v9";

    let beAltsFilterQuery = "";

    function loadAltsRegistry() {
        try {
            const raw = localStorage.getItem(BE_ALTS_KEY);
            const o = raw ? JSON.parse(raw) : {};
            const alts = Array.isArray(o.alts) ? o.alts : [];
            return alts
                .map((a) => ({
                    name: String(a?.name || "").trim(),
                    level: Math.max(0, Math.floor(Number(a?.level) || 0)),
                    xp: Math.max(0, Math.floor(Number(a?.xp) || 0)),
                    lastSeen: typeof a?.lastSeen === "number" ? a.lastSeen : 0,
                    tags: Array.isArray(a?.tags)
                        ? a.tags.map((t) => String(t || "").trim()).filter(Boolean).slice(0, 16)
                        : []
                }))
                .filter((a) => a.name && a.name !== "Guest");
        } catch {
            return [];
        }
    }

    function saveAltsRegistry(alts) {
        localStorage.setItem(BE_ALTS_KEY, JSON.stringify({ alts, updatedAt: Date.now() }));
    }

    let lastAltRecordAt = 0;
    function recordCurrentAltSnapshot(name, level, xp) {
        if (!name || name === "Guest" || !xp) return;
        const now = Date.now();
        if (now - lastAltRecordAt < 20000) return;
        lastAltRecordAt = now;

        const alts = loadAltsRegistry();
        const key = name.trim().toLowerCase();
        let idx = alts.findIndex((a) => a.name.trim().toLowerCase() === key);
        const row = {
            name: name.trim(),
            level,
            xp,
            lastSeen: now,
            tags: idx >= 0 ? (alts[idx].tags || []) : []
        };
        if (idx >= 0) alts[idx] = { ...alts[idx], ...row };
        else alts.push(row);
        alts.sort((a, b) => (b.lastSeen || 0) - (a.lastSeen || 0));
        saveAltsRegistry(alts.slice(0, 40));
    }

    function loadFriendsList() {
        try {
            const raw = localStorage.getItem(BE_FRIENDS_KEY);
            const arr = raw ? JSON.parse(raw) : [];
            return Array.isArray(arr) ? arr.map((s) => String(s || "").trim()).filter(Boolean) : [];
        } catch {
            return [];
        }
    }

    function saveFriendsList(names) {
        const clean = [...new Set(names.map((s) => String(s || "").trim()).filter(Boolean))].slice(0, 200);
        localStorage.setItem(BE_FRIENDS_KEY, JSON.stringify(clean));
        window.BonkEnhanced.syncFriendsFromStorage();
    }

    /* ----- Encrypted credential vault (master password) ----- */
    const BE_VAULT_STORAGE_KEY = "be_cred_vault_v1";
    const BE_VAULT_LOCK_MS = 2 * 60 * 60 * 1000;
    const BE_VAULT_PBKDF2_ITERS = 250000;
    /** Optional: store master password in plain text locally so the vault auto-unlocks (user opt-in). */
    const BE_VAULT_REMEMBER_KEY = "be_vault_remember_device_v1";
    const BE_VAULT_MASTER_PLAIN_KEY = "be_vault_master_plain_v1";

    function isVaultRememberDevice() {
        try {
            return localStorage.getItem(BE_VAULT_REMEMBER_KEY) === "1" && !!localStorage.getItem(BE_VAULT_MASTER_PLAIN_KEY);
        } catch {
            return false;
        }
    }

    function saveVaultRememberMaster(password) {
        try {
            localStorage.setItem(BE_VAULT_REMEMBER_KEY, "1");
            localStorage.setItem(BE_VAULT_MASTER_PLAIN_KEY, String(password || ""));
        } catch (e) {
            BE_LOG.error("saveVaultRememberMaster", e);
        }
    }

    function clearVaultRememberDevice() {
        try {
            localStorage.removeItem(BE_VAULT_REMEMBER_KEY);
            localStorage.removeItem(BE_VAULT_MASTER_PLAIN_KEY);
        } catch (e) {
            BE_LOG.error("clearVaultRememberDevice", e);
        }
    }

    const beVaultSession = {
        unlocked: false,
        /** @type {string|null} */
        password: null,
        /** @type {{ id: string, username: string, password: string, label?: string }[]} */
        entries: [],
        unlockUntil: 0,
        lockTimer: null
    };

    function beVaultScheduleLockRefresh() {
        if (isVaultRememberDevice()) {
            if (beVaultSession.lockTimer) {
                clearTimeout(beVaultSession.lockTimer);
                beVaultSession.lockTimer = null;
            }
            return;
        }
        if (beVaultSession.lockTimer) clearTimeout(beVaultSession.lockTimer);
        beVaultSession.lockTimer = setTimeout(() => {
            beVaultLock();
            renderSocialPanel();
        }, BE_VAULT_LOCK_MS);
    }

    function beVaultTouchActivity() {
        if (!beVaultSession.unlocked) return;
        beVaultSession.unlockUntil = Date.now() + BE_VAULT_LOCK_MS;
        beVaultScheduleLockRefresh();
    }

    function beVaultLock() {
        beVaultSession.unlocked = false;
        beVaultSession.password = null;
        beVaultSession.entries = [];
        beVaultSession.unlockUntil = 0;
        if (beVaultSession.lockTimer) {
            clearTimeout(beVaultSession.lockTimer);
            beVaultSession.lockTimer = null;
        }
    }

    function beVaultReadStored() {
        try {
            const raw = localStorage.getItem(BE_VAULT_STORAGE_KEY);
            if (!raw) return null;
            const o = JSON.parse(raw);
            if (!o || o.v !== 1 || typeof o.salt !== "string" || typeof o.iv !== "string" || typeof o.data !== "string") return null;
            return o;
        } catch {
            return null;
        }
    }

    function u8FromB64(b64) {
        const bin = atob(b64);
        const out = new Uint8Array(bin.length);
        for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);
        return out;
    }

    function u8ToB64(buf) {
        const bytes = buf instanceof Uint8Array ? buf : new Uint8Array(buf);
        let s = "";
        for (let i = 0; i < bytes.length; i++) s += String.fromCharCode(bytes[i]);
        return btoa(s);
    }

    async function beVaultDeriveAesKey(password, saltU8) {
        const enc = new TextEncoder();
        const keyMaterial = await crypto.subtle.importKey(
            "raw",
            enc.encode(password),
            { name: "PBKDF2" },
            false,
            ["deriveBits", "deriveKey"]
        );
        return crypto.subtle.deriveKey(
            { name: "PBKDF2", salt: saltU8, iterations: BE_VAULT_PBKDF2_ITERS, hash: "SHA-256" },
            keyMaterial,
            { name: "AES-GCM", length: 256 },
            false,
            ["encrypt", "decrypt"]
        );
    }

    async function beVaultEncrypt(password, payloadObj) {
        const salt = crypto.getRandomValues(new Uint8Array(16));
        const iv = crypto.getRandomValues(new Uint8Array(12));
        const key = await beVaultDeriveAesKey(password, salt);
        const enc = new TextEncoder();
        const ct = await crypto.subtle.encrypt({ name: "AES-GCM", iv }, key, enc.encode(JSON.stringify(payloadObj)));
        return {
            v: 1,
            salt: u8ToB64(salt),
            iv: u8ToB64(iv),
            data: u8ToB64(new Uint8Array(ct))
        };
    }

    async function beVaultDecrypt(password, stored) {
        const salt = u8FromB64(stored.salt);
        const iv = u8FromB64(stored.iv);
        const ct = u8FromB64(stored.data);
        const key = await beVaultDeriveAesKey(password, salt);
        const buf = await crypto.subtle.decrypt({ name: "AES-GCM", iv }, key, ct);
        const dec = new TextDecoder().decode(buf);
        return JSON.parse(dec);
    }

    async function beVaultPersist() {
        if (!beVaultSession.unlocked || !beVaultSession.password) throw new Error("Vault locked");
        const payload = { entries: beVaultSession.entries };
        const enc = await beVaultEncrypt(beVaultSession.password, payload);
        localStorage.setItem(BE_VAULT_STORAGE_KEY, JSON.stringify(enc));
    }

    async function beVaultUnlockWithPassword(pw) {
        const stored = beVaultReadStored();
        if (!stored) throw new Error("No vault");
        const plain = await beVaultDecrypt(pw, stored);
        const entries = Array.isArray(plain?.entries) ? plain.entries : [];
        beVaultSession.unlocked = true;
        beVaultSession.password = pw;
        beVaultSession.entries = entries
            .map((e) => ({
                id: String(e?.id || crypto.randomUUID?.() || `e_${Date.now()}_${Math.random()}`),
                username: String(e?.username || "").trim(),
                password: String(e?.password || ""),
                label: e?.label ? String(e.label).trim() : ""
            }))
            .filter((e) => e.username);
        beVaultTouchActivity();
        beVaultScheduleLockRefresh();
    }

    async function beVaultCreateVault(pw) {
        beVaultSession.entries = [];
        beVaultSession.unlocked = true;
        beVaultSession.password = pw;
        await beVaultPersist();
        beVaultTouchActivity();
        beVaultScheduleLockRefresh();
    }

    function beNewEntryId() {
        if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") return crypto.randomUUID();
        return `e_${Date.now()}_${Math.floor(Math.random() * 1e9)}`;
    }

    async function tryVaultAutoUnlockFromRemembered() {
        if (!beVaultReadStored()) return;
        if (beVaultSession.unlocked) return;
        if (!isVaultRememberDevice()) return;
        let pw = "";
        try {
            pw = localStorage.getItem(BE_VAULT_MASTER_PLAIN_KEY) || "";
        } catch {
            return;
        }
        if (!pw) return;
        try {
            await beVaultUnlockWithPassword(pw);
            if (beVaultSession.lockTimer) {
                clearTimeout(beVaultSession.lockTimer);
                beVaultSession.lockTimer = null;
            }
            renderSocialPanel();
        } catch {
            clearVaultRememberDevice();
            try {
                renderSocialPanel();
            } catch (e) {
                BE_LOG.debug("tryVaultAutoUnlockFromRemembered render after clear", e);
            }
        }
    }

/** One-off poll used by initial trySync / wake — not the autofill interval (see XP Farm addon). */
function beRequestOneXpPoll() {
    if (!bonkWSS || bonkWSS.readyState !== WebSocket.OPEN) return;
    if (beIsBonkRoomListVisible()) return;
    try {
        bonkWSS.send("42[38]");
    } catch {}
}

const XPEngine = {
    handleMessage(event) {
        try {
            const rawAny = event.data;
            let text;
            if (typeof rawAny === "string") {
                text = rawAny;
            } else if (typeof Blob !== "undefined" && rawAny instanceof Blob) {
                void rawAny.text().then((t) => {
                    XPEngine.applySocketPayload(t, event.target);
                });
                return;
            } else if (typeof ArrayBuffer !== "undefined" && rawAny instanceof ArrayBuffer) {
                text = new TextDecoder("utf-8").decode(rawAny);
            } else {
                return;
            }
            XPEngine.applySocketPayload(text, event.target);
        } catch {}
    },

    /**
     * @param {string} eventData
     * @param {EventTarget|null} sourceWs
     */
    applySocketPayload(eventData, sourceWs) {
        try {
            if (!eventData || !eventData.startsWith("42[")) return;

            const raw = eventData.slice(2);
            /* Avoid JSON.parse on the bulk of socket.io traffic (chat, game state, etc.). */
            if (!/^\[\s*46\s*,/.test(raw)) return;

            const msg = JSON.parse(raw);

            if (msg[0] !== 46) return;

            const xp = msg[1]?.newXP;
            if (typeof xp !== "number") return;

            /* This frame came from the real game socket — always pin polls to it (fixes wrong `bonkWSS` when multiple WebSockets exist, common on Brave). */
            try {
                if (sourceWs && sourceWs.readyState === WebSocket.OPEN) {
                    bonkWSS = /** @type {WebSocket} */ (sourceWs);
                }
            } catch {}

            const { data, key, storageKey } = Storage.load();

            const lastXP = data[key].xp || 0;
            const baselineWasNull = data[key].baselineXP == null;

            /* Today’s wins/rate use xpToday = xp - baseline. If the first server packet already
               includes several wins (200 XP) while lastXP was 0, old logic set baseline = xp and
               wins stayed 0 forever. Anchor at 0 when we had no prior XP for this UTC day; else
               resume from lastXP (minus seeded wins for legacy imports). */
            if (baselineWasNull) {
                const seededWins = Number(data[key].wins || 0);
                if (lastXP <= 0) {
                    data[key].baselineXP = 0;
                } else {
                    data[key].baselineXP = Math.max(0, lastXP - seededWins * 100);
                }
            }

            data[key].xp = xp;
            data._globalXP = xp;
            data[key]._synced = true;

            if (xp > lastXP) {
                const xpDelta = xp - lastXP;

                if (xpDelta > 100) {
                    console.debug("[Bonk Enhanced] XP batch detected:", xpDelta);
                }

                noXpCount = 0;
                lastXpChangeTime = Date.now();
            } else {
                const timeSinceLastXP = Date.now() - lastXpChangeTime;

                if (timeSinceLastXP > delay + 2000) {
                    noXpCount++;
                }
            }

            /* Same in-memory state as before; skip localStorage + UI when XP unchanged (reduces hitches). */
            const xpChanged = xp !== lastXP;
            if (xpChanged || baselineWasNull) {
                Storage.save(data, storageKey);
            }

            if (xpChanged) {
                Events.emit("xpUpdate");
            }
        } catch {}
    }
};
    function waitForPlayer(callback) {
    const el = document.getElementById("pretty_top_name");

    if (el && el.innerText.trim()) {
        callback();
        return;
    }

    const observer = new MutationObserver(() => {
        const el = document.getElementById("pretty_top_name");

        if (el && el.innerText.trim()) {
            observer.disconnect();
            callback();
        }
    });

    observer.observe(document.body, {
        childList: true,
        subtree: true
    });
}

function waitForSocket(callback) {
    const check = () => {
        if (bonkWSS && bonkWSS.readyState === 1) {
            callback();
        } else {
            setTimeout(check, 100);
        }
    };

    check();
}
    const UIEngine = {
    async init() {
        await createUI();
    },

    render() {
    updateUI();
},

    updateSession() {
        updateSessionOnly();
    }
};
    window.BonkVisuals = {
  players: {
    usernames: {
      visible: true,
      alpha: 1
    },
    skins: true,
    visible: true,
    alpha: 1
  },
  chat: {
    visible: true,
    alpha: 1
  }
};
    const DAILY_CAP = 180;
    let sessionStart = Date.now();
    let sessionStartXP = 0;
    let sessionStartWins = 0;
    let previousWins = 0;
    let currentPlayer = null;
    let hasInitializedPlayer = false;

    let bonkWSS = null;
    let xpEnabled = false;
    /** After farm has been turned on at least once (this page / account); IDLE label only shows when this is true. */
    let beFarmEverOnThisSession = false;
    let delay = 18500;

    /** Exposes XP farm hooks for the optional Bonk Enhanced — XP Farm addon userscript only. */
    function beRegisterFarmApi() {
        const BE = window.BonkEnhanced;
        if (!BE || BE._farmApiRegistered) return;
        BE._farmApiRegistered = true;
        BE._farmApiVersion = 1;
        BE.setFarmActive = (on) => {
            xpEnabled = !!on;
            if (on) beFarmEverOnThisSession = true;
            Events.emit("xpUpdate");
        };
        BE.getFarmActive = () => xpEnabled;
        BE.getBonkWSS = () => bonkWSS;
        BE.emitXpUpdate = () => Events.emit("xpUpdate");
        BE.beIsBonkRoomListVisible = beIsBonkRoomListVisible;
        BE.beBackoffMs = beBackoffMs;
        BE.getXpFarmDelay = () => delay;
    }

    let noXpCount = 0;
    let lastXpChangeTime = 0;
    let lastSkinSrc = null;
    let topBarEl = null;
    let statsCache = null;
    let lastRender = 0;
    const CIRCLE_RADIUS = 34;
const CIRCLE_CIRCUMFERENCE = 2 * Math.PI * CIRCLE_RADIUS;
    let hasRenderedOnce = false;
    let lastUIState = {
    xp: null,
    wins: null,
    level: null,
    player: null
};

    function getPlayerKey() {
        return document.getElementById("pretty_top_name")?.innerText || "Guest";
    }


    function getRealLevel() {
        try {
            const container = document.getElementById("pretty_top_name")?.parentElement;
            if (!container) return 0;

            const text = container.innerText;
            const match = text.match(/lv\s*(\d+)/i);

            if (match) return parseInt(match[1]);
        } catch {}

        return 0;
    }

    async function createUI() {
       if (document.getElementById("winTrackerBox")) return;

        const div = document.createElement("div");
        div.id = "winTrackerBox";
        const style = document.createElement("style");
style.innerHTML = `
#winTrackerBox {
    --be-space-xs: 4px;
    --be-space-sm: 8px;
    --be-space-md: 12px;
    --be-space-lg: 16px;
    --be-radius-md: 14px;
    --be-radius-sm: 8px;
    --be-font-sans: "Segoe UI", system-ui, sans-serif;
    --be-font-mono: ui-monospace, monospace;
}
.tracker-card {
    width: 240px;
    padding: var(--be-space-md);
    min-height: 160px;

    position: relative;
    border-radius: var(--be-radius-md);
    background: linear-gradient(180deg, #0f1a2b, #0a1220);
    border: 1px solid rgba(255, 170, 0, 0.55);
    transition: border-color 0.2s ease, box-shadow 0.2s ease;
    padding-top: 52px;
    overflow: hidden;
    /* Column flex so .tracker-view fills a user-resized height and scrolls reliably (not just max(72vh,560)). */
    display: flex;
    flex-direction: column;
    min-height: 0;
    /* Cap height (raised so resize-handle can grow the panel); content scrolls in .tracker-view */
    max-height: min(720px, 90vh) !important;

    color: white;
    font-family: monospace;
    transform: scale(0.98);
    opacity: 0;
    transition: transform 0.18s ease, opacity 0.18s ease;
}
#winTrackerBox.be-overlay-open .tracker-card {
    transform: scale(1);
    opacity: 1;
}
.be-overlay-launcher {
    position: fixed;
    right: 20px;
    bottom: 20px;
    z-index: 999999999;
    height: 36px;
    padding: 0 12px;
    border-radius: 999px;
    display: none !important;
    align-items: center;
    gap: 8px;
    font: 600 12px "Segoe UI", sans-serif;
    letter-spacing: 0.2px;
    color: #d8e7ff;
    background: linear-gradient(180deg, rgba(12, 25, 45, 0.95), rgba(7, 16, 30, 0.95));
    border: 1px solid rgba(89, 138, 221, 0.45);
    box-shadow: 0 10px 24px rgba(0, 0, 0, 0.35);
    cursor: pointer;
    user-select: none;
    pointer-events: none;
    visibility: hidden;
}
.be-overlay-launcher:hover {
    filter: brightness(1.08);
}
.be-overlay-launcher-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: #5ea2ff;
}
#beKeybindOverlay {
    position: fixed;
    left: 18px;
    bottom: 18px;
    top: auto;
    z-index: 999999990;
    pointer-events: none;
    display: none;
    font-family: "Segoe UI", system-ui, sans-serif;
    user-select: none;
    --be-kb-scale: 1;
    transform: scale(var(--be-kb-scale));
    transform-origin: left bottom;
}
.be-kb-strip {
    display: none;
    flex-wrap: wrap;
    gap: 4px;
    justify-content: center;
    align-items: center;
    padding-top: 4px;
    margin-top: 2px;
    border-top: 1px solid rgba(255, 255, 255, 0.06);
}
.be-kb-compact .be-kb-grid,
.be-kb-compact .be-kb-actions {
    display: none !important;
}
.be-kb-compact .be-kb-strip {
    display: flex !important;
    border-top: none;
    padding-top: 0;
    margin-top: 0;
}
.be-kb-key-sm {
    width: 34px;
    height: 26px;
    border-radius: 6px;
    font-size: 10px;
    font-weight: 800;
}
.be-kb-panel {
    pointer-events: auto;
    padding: 0;
    border-radius: 14px;
    background: linear-gradient(180deg, rgba(12, 22, 38, 0.96), rgba(8, 14, 26, 0.96));
    border: 1px solid rgba(255, 170, 0, 0.45);
    box-shadow: 0 10px 28px rgba(0, 0, 0, 0.38);
    min-width: 176px;
    overflow: hidden;
}
.be-kb-draghead {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 8px 10px;
    cursor: grab;
    background: linear-gradient(180deg, rgba(22, 36, 58, 0.98), rgba(14, 24, 42, 0.95));
    border-bottom: 1px solid rgba(255, 200, 120, 0.18);
    pointer-events: auto;
}
.be-kb-draghead:active,
.be-kb-draghead.be-kb-dragging {
    cursor: grabbing;
}
.be-kb-drag-grip {
    display: inline-block;
    width: 10px;
    height: 12px;
    opacity: 0.45;
    background: repeating-linear-gradient(
        180deg,
        rgba(255, 210, 160, 0.85) 0 2px,
        transparent 2px 4px
    );
    background-size: 3px 12px;
    background-repeat: no-repeat;
    background-position: 0 0;
    flex-shrink: 0;
}
.be-kb-drag-title {
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: rgba(255, 210, 140, 0.92);
}
.be-kb-body {
    padding: 10px 12px 11px;
}
.be-kb-grid {
    display: grid;
    grid-template-columns: repeat(3, 44px);
    grid-template-rows: auto auto;
    gap: 5px;
    justify-content: center;
    align-items: center;
}
.be-kb-spacer {
    min-height: 1px;
}
.be-kb-key {
    width: 44px;
    height: 36px;
    border-radius: 8px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 17px;
    font-weight: 700;
    color: #e8f0ff;
    background: linear-gradient(180deg, rgba(30, 48, 78, 0.95), rgba(18, 30, 52, 0.95));
    border: 1px solid rgba(100, 140, 200, 0.35);
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.12);
    transition: background 0.08s ease, border-color 0.08s ease, box-shadow 0.08s ease, color 0.08s ease, transform 0.06s ease;
}
.be-kb-key-wide {
    min-width: 72px;
    width: auto;
    padding: 0 10px;
    font-size: 13px;
    font-weight: 700;
}
.be-kb-key > span,
.be-kb-key-wide > span {
    display: inline-block;
    max-width: 112px;
    line-height: 1.2;
    text-align: center;
    white-space: normal;
    word-break: break-word;
    font-size: 12px;
    font-weight: 700;
}
.be-kb-grid .be-kb-key > span {
    font-size: 10px;
    font-weight: 800;
    letter-spacing: 0.02em;
    max-width: 44px;
    white-space: nowrap;
}
.be-kb-action-wideonly {
    justify-content: center;
}
.be-kb-key.be-kb-active {
    background: linear-gradient(180deg, rgba(255, 170, 60, 0.95), rgba(230, 120, 20, 0.92));
    border-color: rgba(255, 220, 160, 0.75);
    color: #1a0d00;
    box-shadow: 0 0 0 2px rgba(255, 200, 100, 0.35), inset 0 1px 0 rgba(255, 255, 255, 0.35);
    transform: translateY(1px);
}
.be-kb-actions {
    display: flex;
    gap: 10px;
    justify-content: center;
    margin-top: 10px;
    padding-top: 8px;
    border-top: 1px solid rgba(255, 255, 255, 0.08);
}
.be-kb-action {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 4px;
}
.be-kb-label {
    font-size: 9px;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: rgba(200, 215, 240, 0.65);
}
.be-widget-hub {
    position: fixed;
    inset: 0;
    z-index: 999999999;
    display: none;
    pointer-events: none;
    align-items: center;
    justify-content: center;
    background: rgba(5, 10, 18, 0.52);
    backdrop-filter: blur(8px);
}
.be-widget-hub-panel {
    width: min(620px, 92vw);
    border-radius: 18px;
    padding: 14px;
    background: linear-gradient(180deg, rgba(17, 29, 49, 0.96), rgba(11, 20, 36, 0.96));
    border: 1px solid rgba(95, 145, 230, 0.35);
    box-shadow: 0 14px 40px rgba(0, 0, 0, 0.42);
}
.be-widget-hub-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 10px;
    color: #dce9ff;
    font: 600 13px "Segoe UI", sans-serif;
}
.be-widget-hub-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 12px;
}
.be-widget-card.be-widget-hud {
    position: relative;
    overflow: hidden;
    border-radius: 14px;
    min-height: 92px;
    padding: 12px 14px 12px 12px;
    display: flex;
    flex-direction: row;
    align-items: center;
    gap: 12px;
    text-align: left;
    color: #fff;
    cursor: pointer;
    font: 600 12px "Segoe UI", sans-serif;
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, 0.12),
        0 4px 18px rgba(0, 0, 0, 0.35);
    border: 1px solid rgba(255, 255, 255, 0.12);
}
.be-widget-hud-glyph {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 50px;
    height: 50px;
    border-radius: 12px;
    background: rgba(0, 0, 0, 0.22);
    color: #fff;
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15);
}
.be-widget-hud-glyph svg {
    filter: drop-shadow(0 0 6px rgba(255, 255, 255, 0.25));
}
.be-widget-hud-copy {
    flex: 1;
    min-width: 0;
}
.be-widget-title {
    display: block;
    font-size: 12px;
    line-height: 1.2;
    font-weight: 800;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    text-shadow: 0 0 12px rgba(255, 255, 255, 0.25);
}
.be-widget-card.be-widget-hud small {
    display: block;
    margin-top: 5px;
    opacity: 0.88;
    font-weight: 500;
    font-size: 11px;
    letter-spacing: 0.02em;
    text-transform: none;
}
.be-widget-card.be-widget-hud:hover {
    transform: translateY(-2px);
    filter: brightness(1.05);
}
.be-widget-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    border-radius: 7px;
    margin-bottom: 8px;
    background: rgba(255, 255, 255, 0.22);
    font-size: 13px;
}
.be-widget-dashboard,
.be-widget-stats,
.be-widget-calculator,
.be-widget-social,
.be-widget-visuals,
.be-widget-settings {
    position: relative;
    overflow: hidden;
}
.be-widget-dashboard {
    background: linear-gradient(145deg, #1d4ed8, #2563eb 55%, #38bdf8);
}
.be-widget-dashboard::before {
    content: "";
    position: absolute;
    inset: 0;
    background:
        linear-gradient(90deg, rgba(255,255,255,0.08) 1px, transparent 1px) 0 0/18px 18px,
        linear-gradient(rgba(255,255,255,0.08) 1px, transparent 1px) 0 0/18px 18px;
    opacity: 0.35;
    pointer-events: none;
}
.be-widget-dashboard::after {
    content: "";
    position: absolute;
    right: -22px;
    top: -20px;
    width: 92px;
    height: 92px;
    border-radius: 50%;
    background: radial-gradient(circle at 30% 30%, rgba(255,255,255,0.35), rgba(255,255,255,0) 65%);
    pointer-events: none;
}
.be-widget-dashboard .be-widget-hud-glyph {
    background: linear-gradient(145deg, rgba(10, 31, 87, 0.75), rgba(11, 52, 126, 0.78));
    box-shadow: inset 0 1px 0 rgba(255,255,255,0.22);
}

.be-widget-stats {
    background: linear-gradient(145deg, #6d28d9, #9333ea 55%, #ec4899);
}
.be-widget-stats::before {
    content: "";
    position: absolute;
    inset: 0;
    background:
        linear-gradient(transparent 62%, rgba(255,255,255,0.15) 62% 66%, transparent 66%),
        linear-gradient(120deg, transparent 52%, rgba(255,255,255,0.18) 52% 56%, transparent 56%);
    opacity: 0.45;
    pointer-events: none;
}
.be-widget-stats::after {
    content: "";
    position: absolute;
    right: 10px;
    bottom: 8px;
    width: 56px;
    height: 26px;
    border-left: 2px solid rgba(255,255,255,0.45);
    border-bottom: 2px solid rgba(255,255,255,0.45);
    border-radius: 0 0 0 8px;
    pointer-events: none;
}
.be-widget-stats .be-widget-hud-glyph {
    background: linear-gradient(145deg, rgba(62, 18, 122, 0.8), rgba(112, 29, 150, 0.8));
    box-shadow: inset 0 1px 0 rgba(255,255,255,0.22);
}

.be-widget-calculator {
    background: linear-gradient(145deg, #b45309, #d97706 55%, #fbbf24);
}
.be-widget-calculator::before {
    content: "";
    position: absolute;
    inset: 0;
    background: repeating-linear-gradient(
        -12deg,
        transparent,
        transparent 14px,
        rgba(255, 255, 255, 0.04) 14px,
        rgba(255, 255, 255, 0.04) 15px
    );
    opacity: 0.4;
    pointer-events: none;
}
.be-widget-calculator .be-widget-hud-glyph {
    background: linear-gradient(145deg, rgba(80, 40, 8, 0.55), rgba(120, 60, 10, 0.65));
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2);
}

.be-widget-social {
    background: linear-gradient(145deg, #9d174d, #db2777 55%, #f472b6);
}
.be-widget-social::before {
    content: "";
    position: absolute;
    right: -20px;
    top: -24px;
    width: 100px;
    height: 100px;
    border-radius: 50%;
    border: 2px solid rgba(255, 255, 255, 0.12);
    pointer-events: none;
}
.be-widget-social .be-widget-hud-glyph {
    background: linear-gradient(145deg, rgba(90, 12, 50, 0.65), rgba(130, 20, 75, 0.72));
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.18);
}

.be-widget-visuals {
    background: linear-gradient(145deg, #0f766e, #0ea5a4 58%, #22c55e);
}
.be-widget-visuals::before {
    content: "";
    position: absolute;
    left: -18px;
    bottom: -18px;
    width: 90px;
    height: 90px;
    border-radius: 50%;
    border: 11px solid rgba(255,255,255,0.2);
    pointer-events: none;
}
.be-widget-visuals::after {
    content: "";
    position: absolute;
    right: 16px;
    top: 12px;
    width: 34px;
    height: 18px;
    border-radius: 999px;
    border: 2px solid rgba(255,255,255,0.42);
    pointer-events: none;
}
.be-widget-visuals .be-widget-hud-glyph {
    background: linear-gradient(145deg, rgba(9, 74, 72, 0.78), rgba(6, 121, 95, 0.8));
    box-shadow: inset 0 1px 0 rgba(255,255,255,0.22);
}
.be-widget-settings {
    position: relative;
    overflow: hidden;
    background: linear-gradient(145deg, #e5e7eb, #cbd5e1) !important;
    color: #1f2937;
    box-shadow: inset 0 1px 0 rgba(255,255,255,0.65), 0 5px 16px rgba(15, 23, 42, 0.2);
}
.be-widget-settings::after {
    content: "";
    position: absolute;
    right: -10px;
    bottom: -10px;
    width: 68px;
    height: 68px;
    border-radius: 50%;
    border: 10px solid rgba(71, 85, 105, 0.25);
    opacity: 0.9;
}
.be-widget-settings::before {
    content: "";
    position: absolute;
    left: -16px;
    top: -16px;
    width: 74px;
    height: 74px;
    border-radius: 50%;
    border: 12px solid rgba(75, 85, 99, 0.55);
    clip-path: polygon(0 0, 70% 0, 70% 35%, 100% 35%, 100% 100%, 0 100%);
    transform: rotate(-18deg);
    pointer-events: none;
}
.be-widget-settings .be-widget-hud-glyph {
    position: relative;
    background: linear-gradient(145deg, #6b7280, #374151);
    color: #e5e7eb;
    box-shadow: inset 0 1px 0 rgba(255,255,255,0.28), 0 4px 10px rgba(17, 24, 39, 0.35);
}
.be-widget-settings .be-widget-hud-glyph::before {
    content: "";
    position: absolute;
    inset: 5px;
    border-radius: 50%;
    border: 2px dashed rgba(229, 231, 235, 0.9);
}
.be-widget-settings small {
    color: rgba(31, 41, 55, 0.8);
}

/* sections */
.tracker-section {
    margin-bottom: 10px;
}
.tracker-name {
    font-size: 18px;
    font-weight: 700;
    color: #ffffff;

    letter-spacing: 0.5px;
    line-height: 1;
    font-family: 'Segoe UI', 'Inter', sans-serif;
    min-width: 0;
    max-width: 100%;
}
/* Long names: ~8 characters wide, slightly smaller type; full string scrolls inside */
.tracker-name.tracker-name--scroll {
    overflow: hidden;
    width: 8ch;
    max-width: 100%;
    box-sizing: border-box;
    flex-shrink: 0;
    font-size: 16px;
}
.tracker-name.tracker-name--scroll .tracker-name-text {
    display: inline-block;
    white-space: nowrap;
    will-change: transform;
    animation: be-tracker-name-marquee var(--be-name-marquee-duration, 10s) linear infinite alternate;
}
@keyframes be-tracker-name-marquee {
    from { transform: translateX(0); }
    to { transform: translateX(calc(-1 * var(--be-name-scroll, 0px))); }
}
.tracker-header {
    display: flex;
    flex-direction: column;
    gap: 2px;
    margin-bottom: 10px;
    margin-top: 14px;
    /* Containing block for .tracker-circle so it scrolls with this view (not fixed to .tracker-card) */
    position: relative;
}
.tracker-level {
    font-size: 14px;
    font-weight: 500;
    opacity: 0.7;

    margin: 0;
}

/* BAR */
.tracker-bar {
    position: relative;
    height: 12px;
    background: #1a2235;
    border-radius: 8px;
    overflow: hidden;
}

.tracker-bar-fill {
    height: 100%;
    width: 0%;
    background: linear-gradient(90deg, #4caf50, #7cff7c);
    transition: width 0.3s ease;
}

.tracker-bar-text {
    position: absolute;
    right: 6px;
    top: -18px;
    font-size: 11px;
    opacity: 0.8;
}

/* SUBTEXT */
.tracker-subtext {
    font-size: 11px;
    opacity: 0.7;
    margin-top: 4px;
}

/* STATS */
.tracker-stats div {
    display: flex;
    justify-content: space-between;
    font-size: 12px;
    margin: 2px 0;
}

.tracker-stats span:first-child {
    opacity: 0.6;
}
.tracker-stats {
    margin-top: 70px;
    box-sizing: border-box;
}
/* STATUS */
.tracker-status {
    display: flex;
    justify-content: space-between;
    font-size: 12px;
    margin-top: 6px;
    box-sizing: border-box;
}
/* CIRCLE PROGRESS */
/* top was 34px vs .tracker-card; header is now the containing block (scroll fix), so pull up to match old card-space alignment */
.tracker-circle {
    position: absolute;
    top: -10px;
    /* Flush with content edge: card padding is symmetric; extra right:12px made the right gap larger than the left */
    right: 0;

    width: 80px;
    height: 80px;
}

.tracker-circle-text {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);

    font-size: 16px;
    font-weight: 600;
    color: #fff;

    font-family: 'Segoe UI', 'Inter', sans-serif;
    text-shadow:
    0 0 4px rgba(76, 175, 80, 0.6),
    0 0 10px rgba(124, 255, 124, 0.4);
}

.tracker-xp-text {
    position: absolute;
    top: 88px;
    right: 0;
    left: auto;
    transform: none;
    text-align: right;

    font-size: 11px;
    opacity: 0.8;
    white-space: nowrap;
}

/* Daily wins bar: full content width (same horizontal inset as left via card padding) */
.be-dashboard-daily-bar {
    margin: 4px 0 6px 0;
    height: 4px;
    background: #1a2235;
    border-radius: 4px;
    overflow: hidden;
    box-sizing: border-box;
}

/* smooth animation */
#xpCircle {
    transition: stroke-dashoffset 0.4s ease;
}
/* SIDEBAR — compact HUD chips (pairs with widget hub tiles) */
.tracker-sidebar {
    position: absolute;
    top: 0;
    left: 0;

    width: 128px;
    height: 100%;

    background: rgba(6, 10, 18, 0.72);
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
    border-right: 1px solid rgba(255, 170, 0, 0.22);
    box-shadow: 4px 0 18px rgba(0, 0, 0, 0.45);

    transform: translateX(-100%);
    transition: transform 0.25s ease;

    z-index: 999999998;
}

.tracker-sidebar.open {
    transform: translateX(0);
}

.tracker-sidebar-content.be-side-grid {
    padding: 8px 8px 10px;
    font-family: "Segoe UI", system-ui, sans-serif;
    color: #e8f0ff;
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 6px;
    align-content: start;
}

.be-side-nav-title {
    grid-column: 1 / -1;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.28em;
    margin: 4px 2px 8px;
    opacity: 0.55;
    color: rgba(255, 210, 160, 0.95);
}

.sidebar-item.be-side-item {
    display: block;
    padding: 0;
    margin: 0;
    border-radius: 0;
    color: inherit;
}

.be-side-chip {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 3px;
    min-height: 52px;
    padding: 6px 4px 5px;
    border-radius: 10px;
    background: linear-gradient(165deg, rgba(18, 28, 48, 0.95), rgba(8, 12, 22, 0.98));
    border: 1px solid rgba(100, 140, 200, 0.22);
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, 0.06),
        0 0 0 1px rgba(0, 0, 0, 0.35);
    transition: border-color 0.15s ease, box-shadow 0.15s ease, transform 0.1s ease;
}

.be-side-glyph {
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--be-side-accent, #8ab4ff);
    filter: drop-shadow(0 0 5px color-mix(in srgb, var(--be-side-accent) 55%, transparent));
}

.be-side-abbr {
    font-size: 8px;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    opacity: 0.82;
    color: rgba(220, 232, 255, 0.88);
    line-height: 1;
}

.be-side--dashboard { --be-side-accent: #5ea2ff; }
.be-side--graph { --be-side-accent: #c084fc; }
.be-side--stats { --be-side-accent: #38bdf8; }
.be-side--calc { --be-side-accent: #fbbf24; }
.be-side--social { --be-side-accent: #f472b6; }
.be-side--visuals { --be-side-accent: #34d399; }
.be-side--settings { --be-side-accent: #94a3b8; }

.be-side-item.be-side--settings {
    grid-column: 1 / -1;
}
.be-side-item.be-side--settings .be-side-chip {
    flex-direction: row;
    justify-content: center;
    gap: 10px;
    min-height: 46px;
}
.be-side-item.be-side--settings .be-side-abbr {
    font-size: 9px;
}

.sidebar-item.be-side-item:hover .be-side-chip {
    border-color: color-mix(in srgb, var(--be-side-accent) 55%, rgba(255,255,255,0.2));
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, 0.1),
        0 0 12px color-mix(in srgb, var(--be-side-accent) 35%, transparent);
}

.sidebar-item.be-side-item.active .be-side-chip {
    border-color: var(--be-side-accent);
    box-shadow:
        inset 0 0 0 1px color-mix(in srgb, var(--be-side-accent) 65%, transparent),
        0 0 14px color-mix(in srgb, var(--be-side-accent) 45%, transparent),
        inset 0 1px 0 rgba(255, 255, 255, 0.12);
}

.sidebar-item.be-side-item.active .be-side-abbr {
    color: color-mix(in srgb, var(--be-side-accent) 92%, #fff);
    opacity: 1;
}

.sidebar-item.be-side-item:active .be-side-chip {
    transform: scale(0.97);
}

#winTrackerBox[data-size="tiny"] .be-side-abbr {
    display: none;
}
#winTrackerBox[data-size="tiny"] .be-side-chip {
    min-height: 44px;
    padding: 5px 2px;
}
#winTrackerBox[data-size="tiny"] .tracker-sidebar {
    width: 112px;
}
#winTrackerBox[data-size="large"] .tracker-sidebar {
    width: 144px;
}
#winTrackerBox[data-size="large"] .be-side-chip {
    min-height: 58px;
    gap: 5px;
}
#winTrackerBox[data-size="large"] .be-side-glyph svg {
    width: 22px;
    height: 22px;
}

.sidebar-title {
    font-size: 14px;
    font-weight: 600;
    margin-bottom: 12px;
    opacity: 0.8;
}

.sidebar-item:not(.be-side-item) {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 10px;
    border-radius: 8px;
    margin-bottom: 6px;
    cursor: pointer;
    transition: background 0.15s ease, transform 0.1s ease;
    color: rgba(255,255,255,0.85);
}

.sidebar-item:not(.be-side-item):hover {
    background: rgba(255, 255, 255, 0.08);
}

.sidebar-item .icon {
    width: 18px;
    text-align: center;
    opacity: 0.8;
}

.sidebar-item:not(.be-side-item).active {
    background: rgba(76, 175, 80, 0.15);
    color: #7cff7c;
}

.sidebar-item:not(.be-side-item).active .icon {
    opacity: 1;
}

.sidebar-item:not(.be-side-item):active {
    transform: scale(0.96);
}
.tracker-topbar {
    position: absolute;
    top: 0;
    left: 0;

    width: 100%;
    min-height: 44px;
    height: auto;
    box-sizing: border-box;

    display: flex;
    align-items: center;

    padding: 7px 0 7px 10px;

    border-bottom: 1px solid rgba(255,255,255,0.08);
    background: rgba(10, 18, 32, 0.6);
    backdrop-filter: blur(4px);

    cursor: grab;
    z-index: 2;
}
#beOverlayTitleBlock {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: flex-start;
    gap: 5px;
    padding-left: 2px;
    padding-right: 8px;
    font-family: "Segoe UI", system-ui, sans-serif;
}
#beOverlayTitleBlock .be-overlay-title-main {
    font-size: 12px;
    font-weight: 600;
    letter-spacing: 0.06em;
    color: rgba(255, 255, 255, 0.94);
    line-height: 1.22;
}
#beOverlayTitleBlock .be-overlay-title-meta {
    font-size: 9.5px;
    font-weight: 400;
    letter-spacing: 0.02em;
    color: rgba(255, 255, 255, 0.52);
    line-height: 1.4;
    white-space: nowrap;
}
#beOverlayTitleBlock .be-overlay-title-version {
    color: #d4b44a;
    font-weight: 500;
}

.tracker-menu-icon {
    width: 24px;
    height: 24px;

    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;

    gap: 3px;

    border-radius: 6px; /* 🔥 rounded square */
    transition: background 0.15s ease;

    cursor: pointer;

}
.tracker-menu-icon:hover {
    background: rgba(255, 255, 255, 0.08);
    }
.tracker-menu-icon:active {
    background: rgba(255, 255, 255, 0.14);
}
.tracker-menu-icon div {
    width: 14px;
    height: 2px;
    background: #ffffff;
    border-radius: 2px;
}
@keyframes fadeIn {
    from { opacity: 0; transform: translateX(6px); }
    to { opacity: 1; transform: translateX(0); }
}
/* 🔥 FADE ANIMATION */
.tracker-fade {
    opacity: 0;
    transition: opacity 0.25s ease;
}

.tracker-visible {
    opacity: 1;
    transition: opacity 0.25s ease;
}
/* 🔥 HEADER LAYOUT */
.tracker-header-row {
    display: flex;
    align-items: center;
    gap: 10px;
}

/* 🔥 NAME + LEVEL STACK */
.tracker-name-group {
    display: flex;
    flex-direction: column;
    justify-content: center;
    min-width: 0;
    flex: 1;
    /* Keep text out from under the absolute-positioned XP ring */
    padding-right: 76px;
}

/* 🔥 SKIN BOX */
.tracker-skin {
    width: 38px;
    height: 38px;

    border-radius: 50%; /* 🔥 make it circular like bonk */
    background: transparent;

    display: flex;
    align-items: center;
    justify-content: center;

    overflow: hidden;
}

/* 🔥 SKIN IMAGE */
.tracker-skin img {
    width: 100%;
    height: 100%;
    object-fit: contain;
}
.stat-row {
    display: flex;
    justify-content: space-between;
    align-items: center;
    font-size: 12px;
    margin: 2px 0;
}

.stat-label {
    display: flex;
    align-items: center;
    gap: 6px;
    opacity: 0.8;
}

.stat-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 14px;
    height: 14px;
    opacity: 0.7;
}

.stat-icon svg {
    width: 100%;
    height: 100%;
    stroke: currentColor;
}
/* 🔥 RESIZE HANDLE */
.tracker-resize {
    position: absolute;
    bottom: 6px;
    right: 6px;

    width: 18px;
    height: 18px;

    opacity: 0.25;
    cursor: nwse-resize;
}

.tracker-resize:hover {
    opacity: 0.8;
}
/* Calculator — same panels & inputs as Settings (settings-group) */
#winTrackerBox .tracker-view[data-view="calculator"] .be-calc-row {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 6px 0;
    font-size: 11px;
    flex-wrap: wrap;
}
#winTrackerBox .tracker-view[data-view="calculator"] .be-calc-row > span:first-child {
    min-width: 88px;
    opacity: 0.85;
}
#winTrackerBox .tracker-view[data-view="calculator"] .be-calc-pill {
    flex: 1;
    min-width: 0;
    box-sizing: border-box;
    border: 1px solid rgba(255, 255, 255, 0.14);
    border-radius: 8px;
    background: rgba(0, 0, 0, 0.22);
    color: inherit;
    padding: 6px 10px;
    font-size: 12px;
    outline: none;
    font-family: "Segoe UI", sans-serif;
}
#winTrackerBox .tracker-view[data-view="calculator"] .be-calc-pill:focus {
    border-color: rgba(120, 180, 255, 0.5);
    box-shadow: 0 0 0 1px rgba(120, 180, 255, 0.12);
}
#winTrackerBox .tracker-view[data-view="calculator"] .be-calc-pill::placeholder {
    color: rgba(200, 210, 230, 0.45);
}
#winTrackerBox .tracker-view[data-view="calculator"] .be-calc-out {
    font-size: 12px;
    font-weight: 600;
    color: rgba(160, 205, 255, 0.95);
    word-break: break-word;
}
#winTrackerBox .tracker-view[data-view="calculator"] .be-calc-split {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 8px;
    align-items: end;
}
#winTrackerBox .tracker-view[data-view="calculator"] .be-calc-split .be-calc-pill {
    width: 100%;
}
#winTrackerBox .tracker-view[data-view="calculator"] .be-calc-row-stack {
    flex-direction: column;
    align-items: stretch;
    gap: 6px;
}
#winTrackerBox .tracker-view[data-view="calculator"] .be-calc-row-stack > span:first-child {
    min-width: 0;
}
#winTrackerBox .tracker-view[data-view="calculator"] .be-calc-inline {
    display: flex;
    gap: 8px;
    align-items: center;
    flex-wrap: wrap;
}
#winTrackerBox .tracker-view[data-view="calculator"] .be-calc-inline .be-calc-pill {
    flex: 1;
    min-width: 100px;
    max-width: 220px;
}
#winTrackerBox .tracker-view[data-view="calculator"] .be-calc-goal-result {
    margin-top: 10px;
    font-size: 12px;
    font-weight: 600;
    line-height: 1.4;
    color: rgba(160, 205, 255, 0.92);
}
#winTrackerBox .tracker-view[data-view="calculator"] .be-calc-label-sm {
    font-size: 10px;
    opacity: 0.65;
    margin-bottom: 2px;
}
#winTrackerBox.be-theme-light .tracker-view[data-view="calculator"] .be-calc-pill {
    background: rgba(255, 255, 255, 0.92);
    color: #1a2740;
    border-color: rgba(77, 108, 159, 0.35);
}
#winTrackerBox.be-theme-light .tracker-view[data-view="calculator"] .be-calc-pill:focus {
    border-color: rgba(89, 138, 221, 0.55);
    box-shadow: 0 0 0 1px rgba(89, 138, 221, 0.2);
}
#winTrackerBox.be-theme-light .tracker-view[data-view="calculator"] .be-calc-out,
#winTrackerBox.be-theme-light .tracker-view[data-view="calculator"] .be-calc-goal-result {
    color: #1e5688;
}
.tracker-view {
    display: none;
    width: 100%;
    min-width: 0;
    box-sizing: border-box;
    flex: 1 1 0%;
    min-height: 0;
    overflow-y: auto;
    overflow-x: hidden;
    overscroll-behavior: contain;
    /* Invisible scrollbar but keep a real scroll lane (display:none / scrollbar-width:none breaks wheel scroll in some Chromium builds) */
    scrollbar-width: thin;
    scrollbar-color: transparent transparent;
    -ms-overflow-style: none;
    padding-right: 2px;
}

.tracker-view.active {
    display: flex;
    flex-direction: column;
}
.tracker-view::-webkit-scrollbar {
    width: 8px;
    height: 8px;
}
.tracker-view::-webkit-scrollbar-track {
    background: transparent;
}
.tracker-view::-webkit-scrollbar-thumb {
    background: transparent;
    border-radius: 5px;
}

/* Nested panels: same — transparent thumb, functional scroll */
.stats-history,
#beScriptHealthOut {
    scrollbar-width: thin;
    scrollbar-color: transparent transparent;
    -ms-overflow-style: none;
}
.stats-history::-webkit-scrollbar,
#beScriptHealthOut::-webkit-scrollbar {
    width: 8px;
    height: 8px;
}
.stats-history::-webkit-scrollbar-track,
#beScriptHealthOut::-webkit-scrollbar-track {
    background: transparent;
}
.stats-history::-webkit-scrollbar-thumb,
#beScriptHealthOut::-webkit-scrollbar-thumb {
    background: transparent;
    border-radius: 5px;
}

.tracker-view.fade-in {
    animation: trackerFadeIn 0.22s ease;
}

@keyframes trackerFadeIn {
    from {
        opacity: 0;
        transform: translateY(4px);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}
.tracker-topbar-btn {
    width: 22px;
    height: 22px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 4px;
    cursor: pointer;
    font-size: 12px;
    opacity: 0.7;
    transition: background 0.15s ease, opacity 0.15s ease;
    margin-right: 2px;
}
.tracker-topbar-btn:hover {
    background: rgba(255,255,255,0.1);
    opacity: 1;
}
.tracker-topbar-btn:active {
    background: rgba(255,255,255,0.18);
}
.settings-group {
    margin-bottom: 12px;
    padding: 10px;
    border-radius: 8px;
    background: rgba(255, 255, 255, 0.04);
    font-family: "Segoe UI", sans-serif;
}
.settings-title {
    font-size: 13px;
    font-weight: 600;
    opacity: 0.9;
    margin-bottom: 8px;
}
.settings-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    margin: 6px 0;
    font-size: 12px;
}
.settings-row select,
.settings-row button {
    border: 1px solid rgba(255, 255, 255, 0.15);
    background: rgba(13, 20, 33, 0.9);
    color: #fff;
    border-radius: 6px;
    padding: 4px 8px;
    font-size: 12px;
    cursor: pointer;
}
.settings-row button:hover {
    background: rgba(255, 255, 255, 0.1);
}
.settings-row input[type="range"] {
    flex: 1;
    min-width: 0;
    accent-color: rgba(120, 180, 255, 0.85);
}
/* UI presets: grid so preset name input and select share the same column width (match each other) */
.be-ui-presets-grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: center;
    column-gap: 8px;
    row-gap: 8px;
    min-width: 0;
    margin: 6px 0;
}
.be-ui-presets-grid #bePresetName {
    min-width: 0;
}
.be-ui-presets-grid #bePresetSelect {
    min-width: 0;
    width: 100%;
    max-width: 100%;
    box-sizing: border-box;
    border: 1px solid rgba(255, 255, 255, 0.15);
    background: rgba(13, 20, 33, 0.9);
    color: #fff;
    font-size: 12px;
    cursor: pointer;
    padding: 6px 28px 6px 8px;
    line-height: 1.25;
    border-radius: 8px;
}
.be-ui-presets-load-del {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}
/* Fullscreen scaling (top bar): single full-width select under the section title */
#winTrackerBox .settings-row.be-fs-scale-row {
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    margin-top: 2px;
}
#winTrackerBox #beFullscreenScaleModeSelect {
    width: 100%;
    max-width: 100%;
    min-width: 0;
    box-sizing: border-box;
    flex: none;
}
/* Visuals tab — single card, denser layout */
#winTrackerBox .be-visuals-panel {
    padding: 8px 10px 10px;
    opacity: 0.9;
    font-family: "Segoe UI", sans-serif;
}
#winTrackerBox .be-visuals-panel .settings-group {
    margin-bottom: 0;
    padding: 6px 8px;
}
#winTrackerBox .be-visuals-panel .settings-title {
    font-size: 12px;
    margin-bottom: 6px;
}
#winTrackerBox .be-visuals-panel .be-visuals-sub + .be-visuals-sub {
    margin-top: 8px;
    padding-top: 8px;
    border-top: 1px solid rgba(255, 255, 255, 0.1);
}
#winTrackerBox.be-theme-light .be-visuals-panel .be-visuals-sub + .be-visuals-sub {
    border-top-color: rgba(30, 50, 80, 0.12);
}
#winTrackerBox .be-visuals-panel .be-visuals-subtitle {
    font-size: 11px;
    font-weight: 600;
    opacity: 0.82;
    margin-bottom: 4px;
    letter-spacing: 0.02em;
}
#winTrackerBox .be-visuals-panel .settings-row {
    margin: 2px 0;
    gap: 6px;
    font-size: 11px;
}
#winTrackerBox .be-visuals-panel .be-visuals-slider-row {
    flex-direction: column;
    align-items: stretch;
    gap: 3px;
    margin-top: 1px;
}
#winTrackerBox .be-visuals-panel .be-visuals-slider-row > span {
    opacity: 0.82;
    font-size: 10px;
}
.dashboard-stat-hidden {
    display: none !important;
}
.be-gain-flash {
    animation: beGainFlash 0.45s ease;
}
@keyframes beGainFlash {
    0% { filter: brightness(1); transform: scaleY(1); }
    50% { filter: brightness(1.35); transform: scaleY(1.08); }
    100% { filter: brightness(1); transform: scaleY(1); }
}
.stats-wrap {
    padding: 14px;
    font-family: "Segoe UI", sans-serif;
}
.stats-controls {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 8px;
    gap: 8px;
}
.stats-controls select {
    border: 1px solid rgba(255,255,255,0.2);
    background: rgba(14, 22, 36, 0.9);
    color: #fff;
    border-radius: 6px;
    padding: 4px 8px;
    font-size: 12px;
}
.stats-controls input[type="week"] {
    border: 1px solid rgba(255,255,255,0.2);
    background: rgba(14, 22, 36, 0.9);
    color: #fff;
    border-radius: 6px;
    padding: 4px 8px;
    font-size: 12px;
}
.stats-chart {
    height: 110px;
    border-radius: 10px;
    border: 1px solid rgba(255,255,255,0.1);
    background: rgba(5, 12, 24, 0.5);
    display: flex;
    align-items: end;
    gap: 3px;
    padding: 8px;
    margin-bottom: 10px;
}
.stats-bar {
    flex: 1;
    min-width: 6px;
    border-radius: 4px 4px 2px 2px;
    background: linear-gradient(180deg, #72b1ff, #4e8fe8);
}
.stats-bar.wins {
    background: linear-gradient(180deg, #7cff7c, #44b464);
    opacity: 0.85;
}
.stats-legend {
    font-size: 11px;
    opacity: 0.8;
    margin-bottom: 10px;
}
.stats-history {
    font-size: 11px;
    max-height: 110px;
    overflow: auto;
    border-radius: 8px;
    border: 1px solid rgba(255,255,255,0.08);
    padding: 6px 8px;
    background: rgba(8, 16, 29, 0.5);
}
.stats-history-item {
    display: flex;
    justify-content: space-between;
    gap: 8px;
    padding: 3px 0;
}
/* Accounts tab list — avoid crushed one-letter lines when panel is narrow */
#beAltsTableWrap {
    container-type: inline-size;
    container-name: be-alts;
}
#beAltsTableWrap .stats-history-item.be-alt-row {
    flex-wrap: wrap;
    align-items: flex-start;
    justify-content: flex-start;
    gap: 8px 10px;
    padding: 8px 0;
    border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}
#beAltsTableWrap .stats-history-item.be-alt-row:last-child {
    border-bottom: none;
}
#beAltsTableWrap .be-alt-row-main {
    flex: 1 1 200px;
    min-width: min(100%, 11rem);
    max-width: 100%;
}
#beAltsTableWrap .be-alt-row-main b {
    word-break: normal;
    overflow-wrap: anywhere;
}
#beAltsTableWrap .be-alt-row-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    align-items: center;
    flex: 0 1 auto;
    margin-left: auto;
}
@container be-alts (max-width: 300px) {
    #beAltsTableWrap .be-alt-row-main {
        flex: 1 1 100%;
        min-width: 0;
        width: 100%;
    }
    #beAltsTableWrap .be-alt-row-actions {
        margin-left: 0;
        width: 100%;
        justify-content: flex-start;
    }
}
.pulse-dot {
    display: inline-block;
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: #4caf50;
    margin-right: 5px;
    vertical-align: middle;
    animation: pulse 1.5s infinite;
}

@keyframes pulse {
    0%   { box-shadow: 0 0 0 0 rgba(76, 175, 80, 0.6); }
    70%  { box-shadow: 0 0 0 5px rgba(76, 175, 80, 0); }
    100% { box-shadow: 0 0 0 0 rgba(76, 175, 80, 0); }
}

#winTrackerBox[data-size="tiny"] .tracker-card {
    width: 210px;
    min-height: 138px;
    padding-top: 50px;
    max-height: min(620px, 88vh) !important;
}
#winTrackerBox[data-size="tiny"] .tracker-circle {
    top: -7px;
    transform: scale(0.88);
    transform-origin: top right;
}
#winTrackerBox[data-size="tiny"] .tracker-header {
    margin-top: 10px;
    margin-bottom: 8px;
}
#winTrackerBox[data-size="tiny"] .tracker-name-group {
    padding-right: 64px;
}
#winTrackerBox[data-size="tiny"] .tracker-name.tracker-name--scroll {
    font-size: 14px;
}
#winTrackerBox[data-size="tiny"] .tracker-name {
    font-size: 15px;
}
#winTrackerBox[data-size="tiny"] .tracker-level,
#winTrackerBox[data-size="tiny"] .stat-row {
    font-size: 11px;
}
#winTrackerBox[data-size="tiny"] .tracker-stats {
    margin-top: 54px;
}
#winTrackerBox[data-size="large"] .tracker-card {
    width: 290px;
    min-height: 190px;
    max-height: min(820px, 92vh) !important;
}
#winTrackerBox[data-size="large"] .tracker-header {
    margin-top: 18px;
}
#winTrackerBox[data-size="large"] .tracker-circle {
    top: -14px;
}
#winTrackerBox[data-size="large"] .tracker-name-group {
    padding-right: 82px;
}
#winTrackerBox[data-size="large"] .tracker-name.tracker-name--scroll {
    font-size: 18px;
}
#winTrackerBox[data-size="large"] .tracker-name {
    font-size: 20px;
}
#winTrackerBox[data-size="large"] .tracker-level,
#winTrackerBox[data-size="large"] .stat-row {
    font-size: 13px;
}
#winTrackerBox[data-size="large"] .tracker-stats {
    margin-top: 12px;
}
#winTrackerBox.be-theme-light .tracker-card {
    background: linear-gradient(180deg, #e8eef9, #dce6f5 72%, #d8e2f3);
    color: #1b2a40;
    border-color: rgba(77, 104, 148, 0.45);
    box-shadow: 0 10px 24px rgba(33, 54, 92, 0.14), inset 0 1px 0 rgba(255, 255, 255, 0.35);
}
#winTrackerBox.be-theme-light .tracker-topbar {
    background: rgba(215, 226, 246, 0.92);
    border-bottom-color: rgba(70, 98, 142, 0.22);
}
#winTrackerBox.be-theme-light #beOverlayTitleBlock .be-overlay-title-main {
    color: rgba(22, 38, 62, 0.94);
}
#winTrackerBox.be-theme-light #beOverlayTitleBlock .be-overlay-title-meta {
    color: rgba(30, 50, 85, 0.52);
}
#winTrackerBox.be-theme-light #beOverlayTitleBlock .be-overlay-title-version {
    color: #7a6220;
}
#winTrackerBox.be-theme-light .tracker-menu-icon div {
    background: #304c78;
}
#winTrackerBox.be-theme-light .tracker-name,
#winTrackerBox.be-theme-light .tracker-circle-text,
#winTrackerBox.be-theme-light .tracker-level,
#winTrackerBox.be-theme-light .tracker-xp-text,
#winTrackerBox.be-theme-light .tracker-status {
    color: #223657;
    text-shadow: none;
}
#winTrackerBox.be-theme-light .tracker-sidebar {
    background: linear-gradient(180deg, rgba(230, 238, 252, 0.97), rgba(218, 228, 246, 0.98));
    color: #1a2f4d;
    border-right-color: rgba(200, 155, 80, 0.35);
}
#winTrackerBox.be-theme-light .be-side-chip {
    background: linear-gradient(165deg, rgba(255, 255, 255, 0.92), rgba(230, 238, 250, 0.95));
    border-color: rgba(77, 108, 159, 0.28);
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.85), 0 1px 3px rgba(30, 50, 90, 0.12);
}
#winTrackerBox.be-theme-light .be-side-abbr {
    color: rgba(30, 50, 85, 0.88);
}
#winTrackerBox.be-theme-light .be-side-nav-title {
    color: rgba(120, 90, 40, 0.75);
}
#winTrackerBox.be-theme-light .sidebar-item:not(.be-side-item) {
    color: rgba(29, 50, 80, 0.92);
}
#winTrackerBox.be-theme-light .sidebar-item:not(.be-side-item):hover,
#winTrackerBox.be-theme-light .tracker-topbar-btn:hover {
    background: rgba(81, 117, 173, 0.18);
}
#winTrackerBox.be-theme-light .sidebar-item:not(.be-side-item).active {
    background: rgba(73, 108, 164, 0.26);
    color: #173b70;
}
#winTrackerBox.be-theme-light .tracker-bar {
    background: rgba(126, 148, 186, 0.28);
}
#winTrackerBox.be-theme-light .settings-group {
    background: linear-gradient(180deg, rgba(123, 154, 205, 0.18), rgba(123, 154, 205, 0.12));
    border: 1px solid rgba(98, 127, 176, 0.22);
}
#winTrackerBox.be-theme-light .settings-row select,
#winTrackerBox.be-theme-light .settings-row button,
#winTrackerBox.be-theme-light .be-ui-presets-grid #bePresetSelect {
    background: rgba(245, 250, 255, 0.9);
    color: #1f3355;
    border-color: rgba(77, 108, 159, 0.35);
}
#winTrackerBox.be-theme-light .settings-row button:hover {
    background: rgba(232, 241, 255, 0.95);
}
#winTrackerBox.be-theme-light .tracker-stats span:first-child,
#winTrackerBox.be-theme-light .tracker-level,
#winTrackerBox.be-theme-light .tracker-subtext,
#winTrackerBox.be-theme-light .stat-label,
#winTrackerBox.be-theme-light .sidebar-title {
    color: rgba(33, 54, 85, 0.75);
}
#winTrackerBox.be-theme-light .tracker-topbar-btn,
#winTrackerBox.be-theme-light .tracker-topbar svg,
#winTrackerBox.be-theme-light #topbarClose {
    color: #24406a;
    fill: #24406a;
    opacity: 0.85;
}
#winTrackerBox.be-theme-light #xpCircleWrapper circle:first-child {
    stroke: rgba(109, 131, 168, 0.35);
}
#winTrackerBox.be-theme-light #dailyProgressBar {
    background: linear-gradient(90deg, #4e8fe8, #7ec3ff) !important;
}
#winTrackerBox.be-theme-light .be-dashboard-daily-bar {
    background: rgba(200, 212, 235, 0.55);
}
#winTrackerBox.be-theme-light .stats-controls select {
    background: rgba(245, 250, 255, 0.92);
    color: #1f3355;
    border-color: rgba(77, 108, 159, 0.35);
}
#winTrackerBox.be-theme-light .stats-controls input[type="week"] {
    background: rgba(245, 250, 255, 0.92);
    color: #1f3355;
    border-color: rgba(77, 108, 159, 0.35);
}
#winTrackerBox.be-theme-light .stats-chart,
#winTrackerBox.be-theme-light .stats-history {
    background: rgba(236, 244, 255, 0.72);
    border-color: rgba(84, 111, 154, 0.22);
}
#winTrackerBox.be-theme-light ~ .be-overlay-launcher {
    color: #193965;
    background: linear-gradient(180deg, rgba(234, 242, 255, 0.96), rgba(214, 228, 248, 0.96));
    border-color: rgba(79, 112, 167, 0.42);
    box-shadow: 0 8px 18px rgba(46, 67, 105, 0.2);
}
#winTrackerBox.be-theme-light ~ .be-widget-hub {
    background: rgba(234, 241, 253, 0.55);
}
#winTrackerBox.be-theme-light ~ .be-widget-hub .be-widget-hub-panel {
    background: linear-gradient(180deg, rgba(234, 242, 255, 0.98), rgba(221, 232, 250, 0.98));
    border-color: rgba(88, 118, 168, 0.36);
}
#winTrackerBox.be-theme-light ~ .be-widget-hub .be-widget-hub-head {
    color: #1f385c;
}

`;
document.head.appendChild(style);
div.style.position = "fixed";

// Default position; saved coords applied after mount (see beClampOverlayToViewport).
        div.style.top = "120px";
        div.style.right = "20px";
        div.style.color = "white";
        div.style.fontFamily = "monospace";
        div.style.zIndex = "999999999";

        div.innerHTML = `
        <div class="tracker-card">

        <div id="trackerSidebar" class="tracker-sidebar">
    <div class="tracker-sidebar-content be-side-grid">
        <div class="sidebar-title be-side-nav-title">NAV</div>

<div class="sidebar-item be-side-item be-side--dashboard active" data-tab="dashboard" title="Dashboard — main tracker panel">
    <div class="be-side-chip">
        <span class="be-side-glyph" aria-hidden="true"><svg viewBox="0 0 24 24" width="20" height="20" fill="none"><path d="M4 10.5L12 4l8 6.5V20a1 1 0 0 1-1 1h-5v-6H10v6H5a1 1 0 0 1-1-1v-9.5z" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/></svg></span>
        <span class="be-side-abbr">Main</span>
    </div>
</div>

<div class="sidebar-item be-side-item be-side--graph" data-tab="graph" title="Graph — weekly XP &amp; wins">
    <div class="be-side-chip">
        <span class="be-side-glyph" aria-hidden="true"><svg viewBox="0 0 24 24" width="20" height="20" fill="none"><path d="M5 19V5m4 14V9m4 10v-6m4 6v-3" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg></span>
        <span class="be-side-abbr">Graph</span>
    </div>
</div>

<div class="sidebar-item be-side-item be-side--stats" data-tab="statsOverview" title="Stats overview">
    <div class="be-side-chip">
        <span class="be-side-glyph" aria-hidden="true"><svg viewBox="0 0 24 24" width="20" height="20" fill="none"><path d="M8 6h12M8 12h12M8 18h8" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/><rect x="4" y="4" width="14" height="16" rx="2" stroke="currentColor" stroke-width="1.5"/></svg></span>
        <span class="be-side-abbr">Stats</span>
    </div>
</div>

<div class="sidebar-item be-side-item be-side--calc" data-tab="calculator" title="Calculator — XP / level / goals">
    <div class="be-side-chip">
        <span class="be-side-glyph" aria-hidden="true"><svg viewBox="0 0 24 24" width="20" height="20" fill="none"><rect x="5" y="3" width="14" height="18" rx="2" stroke="currentColor" stroke-width="1.5"/><path d="M8 8h8M9 12h1.5v1.5H9V15M13 12h2M8 17h8" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/></svg></span>
        <span class="be-side-abbr">Calc</span>
    </div>
</div>

<div class="sidebar-item be-side-item be-side--social" data-tab="social" title="Accounts — vault &amp; friends">
    <div class="be-side-chip">
        <span class="be-side-glyph" aria-hidden="true"><svg viewBox="0 0 24 24" width="20" height="20" fill="none"><circle cx="9" cy="8" r="2.5" stroke="currentColor" stroke-width="1.5"/><path d="M4 19v-1a4 4 0 0 1 4-4h2" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/><circle cx="17" cy="9" r="2" stroke="currentColor" stroke-width="1.5"/><path d="M21 19v0a4 4 0 0 0-3-3.87" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg></span>
        <span class="be-side-abbr">Acct</span>
    </div>
</div>

<div class="sidebar-item be-side-item be-side--visuals" data-tab="visuals" title="Visuals — players &amp; chat">
    <div class="be-side-chip">
        <span class="be-side-glyph" aria-hidden="true"><svg viewBox="0 0 24 24" width="20" height="20" fill="none"><circle cx="12" cy="12" r="3" stroke="currentColor" stroke-width="1.5"/><path d="M3 12h0a9 9 0 0 1 9-9h0a9 9 0 0 1 9 9h0M3 12a9 9 0 0 0 9 9M21 12a9 9 0 0 1-9 9" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/></svg></span>
        <span class="be-side-abbr">View</span>
    </div>
</div>

<div class="sidebar-item be-side-item be-side--settings" data-tab="settings" title="Settings">
    <div class="be-side-chip">
        <span class="be-side-glyph" aria-hidden="true"><svg viewBox="0 0 24 24" width="20" height="20" fill="none"><path d="M12 15.5a3.5 3.5 0 1 0 0-7 3.5 3.5 0 0 0 0 7z" stroke="currentColor" stroke-width="1.4"/><path d="M19.4 15a1.8 1.8 0 0 0 .36 1.99l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.8 1.8 0 0 0-1.99-.36 1.8 1.8 0 0 0-1 1.64V21a2 2 0 1 1-4 0v-.09a1.8 1.8 0 0 0-1-1.64 1.8 1.8 0 0 0-1.99.36l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.8 1.8 0 0 0 .36-1.99 1.8 1.8 0 0 0-1.64-1h-.09a2 2 0 1 1 0-4h.09a1.8 1.8 0 0 0 1.64-1 1.8 1.8 0 0 0-.36-1.99l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.8 1.8 0 0 0 1.99.36h.09a1.8 1.8 0 0 0 1-1.64V3a2 2 0 1 1 4 0v.09a1.8 1.8 0 0 0 1 1.64h.09a1.8 1.8 0 0 0 1.99-.36l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.8 1.8 0 0 0-.36 1.99v.09a1.8 1.8 0 0 0 1.64 1H21a2 2 0 1 1 0 4h-.09a1.8 1.8 0 0 0-1.64 1z" stroke="currentColor" stroke-width="1.1" stroke-linecap="round" opacity="0.9"/></svg></span>
        <span class="be-side-abbr">Set</span>
    </div>
</div>
    </div>
</div>

<div class="tracker-topbar" id="dragHandle">
    <div class="tracker-menu-icon">
        <div></div>
        <div></div>
        <div></div>
    </div>
    <span id="beOverlayTitleBlock">
        <span class="be-overlay-title-main">BONK ENHANCED</span>
        <span class="be-overlay-title-meta">By ${BE_SCRIPT_AUTHOR}<span aria-hidden="true"> · </span><span class="be-overlay-title-version">v${BE_SCRIPT_VERSION}</span></span>
    </span>
    <div id="topbarSettings" class="tracker-topbar-btn" title="Settings">
    <svg viewBox="0 0 24 24" width="13" height="13" fill="white" opacity="0.8">
        <path d="M12 15.5A3.5 3.5 0 0 1 8.5 12 3.5 3.5 0 0 1 12 8.5a3.5 3.5 0 0 1 3.5 3.5 3.5 3.5 0 0 1-3.5 3.5m7.43-2.92c.04-.34.07-.69.07-1.08s-.03-.74-.07-1.08l2.32-1.84c.21-.16.27-.45.13-.69l-2.2-3.84c-.14-.23-.42-.32-.66-.23l-2.74 1.1c-.57-.44-1.18-.81-1.86-1.08l-.42-2.93A.54.54 0 0 0 14 2h-4c-.27 0-.5.19-.54.46l-.42 2.93c-.68.27-1.29.64-1.86 1.08L4.44 5.37c-.24-.09-.52 0-.66.23L1.58 9.44c-.14.24-.08.53.13.69l2.32 1.84C4.03 12.26 4 12.61 4 13s.03.74.07 1.08L1.71 15.92c-.21.16-.27.45-.13.69l2.2 3.84c.14.23.42.32.66.23l2.74-1.1c.57.44 1.18.81 1.86 1.08l.42 2.93c.04.27.27.46.54.46h4c.27 0 .5-.19.54-.46l.42-2.93c.68-.27 1.29-.64 1.86-1.08l2.74 1.1c.24.09.52 0 .66-.23l2.2-3.84c.14-.24.08-.53-.13-.69l-2.32-1.84z"/>
    </svg>
</div>
<div id="topbarClose" class="tracker-topbar-btn" title="Hide overlay" style="margin-right:10px;">✕</div>
</div>
<!-- DASHBOARD -->
<div class="tracker-view active" data-view="dashboard">

    <div class="tracker-section tracker-header">
        <div class="tracker-header-row">
            <div id="playerSkin" class="tracker-skin"></div>

            <div class="tracker-name-group">
                <div id="playerName" class="tracker-name"><span class="tracker-name-text"></span></div>
                <div class="tracker-level" id="levelLine">Level 0</div>
            </div>
        </div>

        <div class="tracker-circle" id="xpCircleWrapper">
    <svg width="80" height="80">
        <defs>
            <linearGradient id="xpGradient" x1="0%" y1="100%" x2="100%" y2="0%">
                <stop offset="0%" stop-color="#2e7d32"/>
                <stop offset="100%" stop-color="#7cff7c"/>
            </linearGradient>
            <filter id="glow">
                <feGaussianBlur stdDeviation="2" result="coloredBlur"/>
                <feMerge>
                    <feMergeNode in="coloredBlur"/>
                    <feMergeNode in="SourceGraphic"/>
                </feMerge>
            </filter>
        </defs>

        <circle cx="40" cy="40" r="34" stroke="#1a2235" stroke-width="7" fill="none"/>
        <circle id="xpCircle"
            filter="url(#glow)"
            cx="40" cy="40" r="34"
            stroke="url(#xpGradient)"
            stroke-width="7"
            fill="none"
            stroke-linecap="round"
            transform="rotate(-90 40 40)"
            stroke-dasharray="213.6"
            stroke-dashoffset="213.6"
        />
    </svg>

    <div id="xpPercent" class="tracker-circle-text">0%</div>
    <div id="xpText" class="tracker-xp-text">0 / 0 XP</div>
</div>

<div class="tracker-section tracker-stats" id="dashboardStatsRows">

    <div class="stat-row" id="rowXp">
        <span class="stat-label">
            <span class="stat-icon">⚡</span>XP
        </span>
        <span id="xpGain">+0</span>
    </div>

    <div class="stat-row" id="rowWins">
        <span class="stat-label">
            <span class="stat-icon">🏆</span>Daily wins
        </span>
        <span id="winsLine">0</span>
    </div>

    <div class="stat-row" id="rowRate">
        <span class="stat-label">
            <span class="stat-icon">📈</span>Rate
        </span>
        <span id="rateLine">0</span>
    </div>

    <div class="stat-row" id="rowLastWin">
        <span class="stat-label">
            <span class="stat-icon">🕒</span>Last win
        </span>
        <span id="lastWinLine">--</span>
    </div>
    <div class="stat-row" id="rowSession">
        <span class="stat-label">
            <span class="stat-icon">⏱</span>Session
        </span>
        <span id="sessionStatLine">0:00</span>
    </div>
    <div class="stat-row" id="rowLevel">
        <span class="stat-label">
            <span class="stat-icon">⭐</span>Level
        </span>
        <span id="levelStatLine">0</span>
    </div>

</div>

<div class="be-dashboard-daily-bar">
    <div id="dailyProgressBar" style="height: 100%; width: 0%; background: linear-gradient(90deg, #4caf50, #7cff7c); border-radius: 4px; transition: width 0.4s ease;"></div>
</div>

        <div class="tracker-section tracker-status">
            <span id="sessionLine">⏱ 0:00</span>
            <span id="statusText" style="display:none;"></span>
        </div>
        </div>

</div> <!-- ✅ DASHBOARD CLOSED PROPERLY -->


<!-- GRAPH -->
<div class="tracker-view" data-view="graph">
    <div class="stats-wrap">
        <div class="stats-controls">
            <span>Week</span>
            <input id="beGraphWeekSelect" type="week">
            <button type="button" id="beExportStatsCsv" style="cursor:pointer;padding:4px 10px;border-radius:8px;border:1px solid rgba(120,180,255,0.45);background:rgba(25,45,80,0.55);color:#e8f0ff;font:600 11px 'Segoe UI',sans-serif;">Export CSV</button>
        </div>
        <div id="beStatsChart" class="stats-chart"></div>
        <div id="beStatsLegend" class="stats-legend">XP and wins trend</div>
        <div style="margin-bottom:6px;font-size:12px;opacity:0.85;">Last 10 sessions</div>
        <div id="beSessionHistory" class="stats-history"></div>
    </div>
</div>

<!-- STATS OVERVIEW -->
<div class="tracker-view" data-view="statsOverview">
    <div class="stats-wrap">
        <div id="beOverviewSizeHint" style="margin-bottom:8px;font-size:11px;opacity:0.8;">Current size: Medium (4 slots)</div>
        <div class="settings-group">
            <div class="settings-title">Dashboard Rows (slot layout)</div>
            <div id="beOverviewSlots">
                <div class="settings-row"><span>Slot 1</span><select data-slot-idx="0"><option value="">None</option><option value="xp">XP</option><option value="wins">Daily wins</option><option value="rate">Rate</option><option value="lastWin">Last win</option><option value="session">Session</option><option value="level">Level</option></select></div>
                <div class="settings-row"><span>Slot 2</span><select data-slot-idx="1"><option value="">None</option><option value="xp">XP</option><option value="wins">Daily wins</option><option value="rate">Rate</option><option value="lastWin">Last win</option><option value="session">Session</option><option value="level">Level</option></select></div>
                <div class="settings-row"><span>Slot 3</span><select data-slot-idx="2"><option value="">None</option><option value="xp">XP</option><option value="wins">Daily wins</option><option value="rate">Rate</option><option value="lastWin">Last win</option><option value="session">Session</option><option value="level">Level</option></select></div>
                <div class="settings-row"><span>Slot 4</span><select data-slot-idx="3"><option value="">None</option><option value="xp">XP</option><option value="wins">Daily wins</option><option value="rate">Rate</option><option value="lastWin">Last win</option><option value="session">Session</option><option value="level">Level</option></select></div>
                <div class="settings-row"><span>Slot 5</span><select data-slot-idx="4"><option value="">None</option><option value="xp">XP</option><option value="wins">Daily wins</option><option value="rate">Rate</option><option value="lastWin">Last win</option><option value="session">Session</option><option value="level">Level</option></select></div>
                <div class="settings-row"><span>Slot 6</span><select data-slot-idx="5"><option value="">None</option><option value="xp">XP</option><option value="wins">Daily wins</option><option value="rate">Rate</option><option value="lastWin">Last win</option><option value="session">Session</option><option value="level">Level</option></select></div>
            </div>
        </div>
        <div style="margin-bottom:8px;font-size:12px;opacity:0.85;">All tracked stats</div>
        <div id="beStatsOverview" class="stats-history"></div>
    </div>
</div>

<!-- CALCULATOR (matches Settings-style panels) -->
<div class="tracker-view" data-view="calculator">
    <div style="padding:16px;opacity:0.9;font-family:'Segoe UI',sans-serif;">
        <div class="settings-group">
            <div class="settings-title">XP / Level</div>
            <p style="font-size:11px;opacity:0.75;margin:0 0 8px 0;line-height:1.35;">Bonk uses total cumulative XP. Min XP to be at level L is (L−1)²×100.</p>
            <div class="be-calc-row be-calc-row-stack">
                <span>Level → XP:</span>
                <div class="be-calc-inline">
                    <input class="be-calc-pill" id="beCalcLv2XpIn" type="number" inputmode="numeric" min="1" placeholder="Level" />
                    <span class="be-calc-out" id="beCalcLv2XpOut">—</span>
                </div>
            </div>
            <div class="be-calc-row be-calc-row-stack">
                <span>XP → Level:</span>
                <div class="be-calc-inline">
                    <input class="be-calc-pill" id="beCalcXp2LvIn" type="number" inputmode="numeric" min="0" placeholder="XP" />
                    <span class="be-calc-out" id="beCalcXp2LvOut">—</span>
                </div>
            </div>
        </div>
        <div class="settings-group" style="margin-top:12px;">
            <div class="settings-title">Goal</div>
            <p style="font-size:11px;opacity:0.75;margin:0 0 8px 0;line-height:1.35;">Start defaults from your tracker when you open this tab. Enter a goal level or total XP (wins ≈ 100 XP each).</p>
            <div class="be-calc-row"><span>Start:</span></div>
            <div class="be-calc-split">
                <div>
                    <div class="be-calc-label-sm">Level</div>
                    <input class="be-calc-pill" id="beCalcStartLv" type="number" inputmode="numeric" min="1" placeholder="Level" />
                </div>
                <div>
                    <div class="be-calc-label-sm">Total XP</div>
                    <input class="be-calc-pill" id="beCalcStartXp" type="number" inputmode="numeric" min="0" placeholder="XP" />
                </div>
            </div>
            <div class="be-calc-row" style="margin-top:10px"><span>Goal:</span></div>
            <div class="be-calc-split">
                <div>
                    <div class="be-calc-label-sm">Level</div>
                    <input class="be-calc-pill" id="beCalcGoalLv" type="number" inputmode="numeric" min="1" placeholder="Level" />
                </div>
                <div>
                    <div class="be-calc-label-sm">Total XP</div>
                    <input class="be-calc-pill" id="beCalcGoalXp" type="number" inputmode="numeric" min="0" placeholder="XP" />
                </div>
            </div>
            <div class="be-calc-goal-result" id="beCalcGoalResult">Enter a goal level or total XP.</div>
        </div>
    </div>
</div>

<!-- ACCOUNTS (data-view social) -->
<div class="tracker-view" data-view="social">
    <div class="stats-wrap" style="padding:12px;">
        <div class="settings-group">
            <div class="settings-title">Saved logins (encrypted)</div>
            <div id="beVaultMount"></div>
        </div>
        <div class="settings-group" style="margin-top:12px;">
            <div class="settings-title">Seen in-game (stats)</div>
            <p style="font-size:11px;opacity:0.85;margin:0 0 8px 0;line-height:1.35;">From the tracker while you play (not secret). Open Bonk in another tab to switch accounts.</p>
            <input type="text" id="beAltsFilter" placeholder="Filter by name or tag…" style="width:100%;box-sizing:border-box;margin-bottom:8px;padding:7px 9px;border-radius:8px;border:1px solid rgba(255,255,255,0.14);background:rgba(0,0,0,0.22);color:inherit;font:12px var(--be-font-sans, 'Segoe UI',sans-serif);" />
            <div id="beAltsTableWrap"></div>
        </div>
        <div class="settings-group" style="margin-top:14px;">
            <div class="settings-title">Friends</div>
            <p style="font-size:11px;opacity:0.85;margin:0 0 8px 0;line-height:1.35;">One username per line, matching in-game spelling. Friends get a gold highlight on their name in the game.</p>
            <textarea id="beFriendsTextarea" rows="6" spellcheck="false" style="width:100%;box-sizing:border-box;font:12px ui-monospace,monospace;padding:8px;border-radius:8px;border:1px solid rgba(255,255,255,0.14);background:rgba(0,0,0,0.22);color:inherit;resize:vertical;"></textarea>
            <div style="display:flex;gap:10px;margin-top:10px;align-items:center;flex-wrap:wrap;">
                <button type="button" id="beFriendsSaveBtn" style="cursor:pointer;padding:6px 12px;border-radius:8px;border:1px solid rgba(120,180,255,0.45);background:rgba(25,45,80,0.6);color:#e8f0ff;font:600 12px 'Segoe UI',sans-serif;">Save friends</button>
                <span id="beFriendsSaveHint" style="font-size:11px;opacity:0.75;"></span>
            </div>
        </div>
    </div>
</div>

<!-- VISUALS — one card: players, usernames, chat -->
<div class="tracker-view" data-view="visuals">
    <div class="be-visuals-panel">
        <div class="settings-group">
            <div class="settings-title">Visuals</div>
            <div class="be-visuals-sub">
                <div class="be-visuals-subtitle">Players</div>
                <div class="settings-row"><span>Visible</span><input type="checkbox" checked id="togglePlayers" /></div>
                <div class="settings-row"><span>Skins</span><input type="checkbox" checked id="toggleSkins" /></div>
                <div class="settings-row be-visuals-slider-row">
                    <span>Opacity</span>
                    <input type="range" min="0" max="100" value="100" id="playersOpacity" style="width:100%;" />
                </div>
            </div>
            <div class="be-visuals-sub">
                <div class="be-visuals-subtitle">Usernames</div>
                <div class="settings-row"><span>Visible</span><input type="checkbox" checked id="toggleNames" /></div>
                <div class="settings-row be-visuals-slider-row">
                    <span>Opacity</span>
                    <input type="range" min="0" max="100" value="100" id="namesOpacity" style="width:100%;" />
                </div>
            </div>
            <div class="be-visuals-sub">
                <div class="be-visuals-subtitle">Chat</div>
                <div class="settings-row"><span>Visible</span><input type="checkbox" checked id="toggleChat" /></div>
                <div class="settings-row be-visuals-slider-row">
                    <span>Opacity</span>
                    <input type="range" min="0" max="100" value="100" id="chatOpacity" style="width:100%;" />
                </div>
            </div>
        </div>
    </div>
</div>

<!-- SETTINGS -->
<div class="tracker-view" data-view="settings">
    <div style="padding:16px; opacity:0.9;">
        <div class="settings-group">
            <div class="settings-title">Appearance</div>
            <div class="settings-row">
                <span>Theme</span>
                <select id="beThemeSelect">
                    <option value="dark">Dark</option>
                    <option value="light">Light</option>
                </select>
            </div>
            <div class="settings-row">
                <span>Size</span>
                <select id="beSizeSelect">
                    <option value="tiny">Tiny</option>
                    <option value="medium">Medium</option>
                    <option value="large">Large</option>
                </select>
            </div>
            <p style="font-size:10px;opacity:0.68;margin:8px 0 0;line-height:1.45;">While this overlay is open: <b>Alt+1–7</b> = Main · Graph · Stats · Calc · Accounts · Visuals · Settings. <b>Esc</b> closes the widget hub (if open), then the overlay. The <b>widget hub hotkey</b> (see below) <b>toggles</b> the hub open and closed. Other keys go to the game when the overlay is closed.</p>
        </div>
        <div class="settings-group">
            <div class="settings-title">Fullscreen scaling (top bar)</div>
            <div class="settings-row be-fs-scale-row">
                <select id="beFullscreenScaleModeSelect" title="WDB: pillar pin + ~3% scale and small vertical shift so the map top stays visible. Letterbox: no extra zoom. Cover: fill viewport crop. Stretch: non-uniform."
                    style="padding:6px 8px;border-radius:8px;border:1px solid rgba(255,255,255,0.14);background:rgba(0,0,0,0.25);color:inherit;">
                    <option value="contain">Letterbox — centered</option>
                    <option value="contain-left">Letterbox — pillar left (map right)</option>
                    <option value="contain-right">Letterbox — pillar right (map left)</option>
                    <option value="wdb-left">WDB — map right (+slight zoom)</option>
                    <option value="wdb-right">WDB — map left (+slight zoom)</option>
                    <option value="cover">Cover — crop edges</option>
                    <option value="fill">Stretch — fill screen</option>
                </select>
            </div>
        </div>
        <div class="settings-group">
            <div class="settings-title">Overlay Hotkey</div>
            <div class="settings-row">
                <span id="beHotkeyCurrent">Delete</span>
                <button id="beSetHotkeyBtn">Set hotkey</button>
            </div>
        </div>
        <div class="settings-group">
            <div class="settings-title">Widget Hub Hotkey</div>
            <div class="settings-row">
                <span id="beWidgetHotkeyCurrent">Home</span>
                <button id="beSetWidgetHotkeyBtn">Set hotkey</button>
            </div>
        </div>
        <div class="settings-group">
            <div class="settings-title">Keybind overlay</div>
            <div class="settings-row">
                <span>Show on-screen controls</span>
                <label style="display:inline-flex;align-items:center;gap:8px;cursor:pointer;">
                    <input type="checkbox" id="beKeybindOverlayToggle" />
                    <span id="beKeybindOverlayState">Off</span>
                </label>
            </div>
            <div style="font-size:11px;opacity:0.72;line-height:1.45;margin-top:8px;">Open Bonk’s in-game <b>Settings</b> (gear) at least <b>once</b> so the Change Controls table loads.</div>
            <div class="settings-row" style="margin-top:10px;flex-wrap:wrap;gap:10px;">
                <span>Opacity</span>
                <input type="range" id="beKbOverlayOpacity" min="35" max="100" value="100" style="width:120px;" />
            </div>
            <div class="settings-row" style="flex-wrap:wrap;gap:10px;">
                <span>Size</span>
                <input type="range" id="beKbOverlayScale" min="65" max="135" value="100" style="width:120px;" />
            </div>
            <div class="settings-row">
                <label style="display:inline-flex;align-items:center;gap:8px;cursor:pointer;">
                    <input type="checkbox" id="beKbOverlayCompact" />
                    <span>Compact single-row layout</span>
                </label>
            </div>
        </div>
        <div class="settings-group">
            <div class="settings-title">UI presets</div>
            <p style="font-size:11px;opacity:0.75;margin:0 0 8px 0;line-height:1.4;">Save theme, size, layout slots, tracker position, and keybind overlay options.</p>
            <div class="be-ui-presets-grid">
                <input type="text" id="bePresetName" placeholder="Preset name" style="padding:6px 8px;border-radius:8px;border:1px solid rgba(255,255,255,0.14);background:rgba(0,0,0,0.2);color:inherit;" />
                <button type="button" id="bePresetSaveBtn" style="cursor:pointer;padding:6px 10px;border-radius:8px;border:1px solid rgba(120,180,255,0.45);background:rgba(25,45,80,0.55);color:#e8f0ff;font:600 11px 'Segoe UI',sans-serif;">Save</button>
                <select id="bePresetSelect"></select>
                <div class="be-ui-presets-load-del">
                    <button type="button" id="bePresetLoadBtn" style="cursor:pointer;padding:6px 10px;border-radius:8px;border:1px solid rgba(120,200,160,0.45);background:rgba(20,55,40,0.45);color:#c8ffd8;font:600 11px 'Segoe UI',sans-serif;">Load</button>
                    <button type="button" id="bePresetDeleteBtn" style="cursor:pointer;padding:6px 10px;border-radius:8px;border:1px solid rgba(255,150,120,0.35);background:rgba(55,25,20,0.45);color:#ffd0c8;font:600 11px 'Segoe UI',sans-serif;">Delete</button>
                </div>
            </div>
        </div>
        <div class="settings-group">
            <div class="settings-title">Script health</div>
            <p style="font-size:11px;opacity:0.75;margin:0 0 8px 0;line-height:1.4;">Diagnostics for iframe hooks, storage, and Bonk UI selectors (<span id="beSelectorSchemaLabel"></span>).</p>
            <pre id="beScriptHealthOut" style="font-size:10px;line-height:1.35;max-height:180px;overflow:auto;padding:8px;border-radius:8px;border:1px solid rgba(255,255,255,0.1);background:rgba(0,0,0,0.25);white-space:pre-wrap;margin:0;"></pre>
            <button type="button" id="beScriptHealthRefresh" style="cursor:pointer;margin-top:8px;padding:6px 12px;border-radius:8px;border:1px solid rgba(120,180,255,0.45);background:rgba(25,45,80,0.55);color:#e8f0ff;font:600 11px 'Segoe UI',sans-serif;">Refresh</button>
        </div>
    </div>
</div>
<div id="resizeHandle" class="tracker-resize">
    <svg viewBox="0 0 24 24">
        <path d="M8 20L20 8M12 20L20 12M16 20L20 16"
              stroke="currentColor"
              stroke-width="1"
              stroke-linecap="round"
              fill="none"/>
    </svg>
</div>

</div>  <!-- tracker-card -->
`;

        document.body.appendChild(div);
        (function beApplyOverlayTitleBranding() {
            const el = document.getElementById("beOverlayTitleBlock");
            if (!el) return;
            el.innerHTML =
                '<span class="be-overlay-title-main">BONK ENHANCED</span>' +
                '<span class="be-overlay-title-meta">By ' +
                BE_SCRIPT_AUTHOR +
                '<span aria-hidden="true"> · </span><span class="be-overlay-title-version">v' +
                BE_SCRIPT_VERSION +
                "</span></span>";
        })();

        const BE_OVERLAY_DRAG_PAD = 4;
        /** How many pixels of the overlay must stay visible on an edge — allows dragging most of the panel past the bottom (or side) instead of forcing the whole box on-screen. */
        const BE_OVERLAY_EDGE_PEEK = 40;
        function beClampOverlayToViewport() {
            if (!div.parentNode) return;
            div.style.right = "auto";
            const w = div.offsetWidth;
            const h = div.offsetHeight;
            if (w < 8 || h < 8) return;
            const vw = window.innerWidth;
            const vh = window.innerHeight;
            const r = div.getBoundingClientRect();
            const parsedL = parseInt(div.style.left, 10);
            const parsedT = parseInt(div.style.top, 10);
            let left = Number.isFinite(parsedL) ? parsedL : r.left;
            let top = Number.isFinite(parsedT) ? parsedT : r.top;
            const pad = BE_OVERLAY_DRAG_PAD;
            const peek = BE_OVERLAY_EDGE_PEEK;
            const maxL = Math.max(pad, vw - peek);
            const maxT = Math.max(pad, vh - peek);
            left = Math.min(Math.max(pad, left), maxL);
            top = Math.min(Math.max(pad, top), maxT);
            div.style.left = Math.round(left) + "px";
            div.style.top = Math.round(top) + "px";
        }
        try {
            const rawPos = localStorage.getItem("bonk_ui_pos");
            const savedPos = rawPos ? JSON.parse(rawPos) : null;
            if (savedPos && typeof savedPos.left === "string" && typeof savedPos.top === "string") {
                div.style.left = savedPos.left;
                div.style.top = savedPos.top;
                div.style.right = "auto";
            }
        } catch {
            /* ignore bad storage */
        }
        requestAnimationFrame(() => {
            requestAnimationFrame(() => beClampOverlayToViewport());
        });

        const launcher = document.createElement("button");
        launcher.type = "button";
        launcher.className = "be-overlay-launcher";
        launcher.id = "beOverlayLauncher";
        launcher.innerHTML = `<span class="be-overlay-launcher-dot"></span><span>Open overlay</span><span id="beLauncherHotkey">Delete</span>`;
        launcher.addEventListener("click", () => setOverlayVisibility(true));
        document.body.appendChild(launcher);

        const widgetHub = document.createElement("div");
        widgetHub.className = "be-widget-hub";
        widgetHub.id = "beWidgetHub";
        widgetHub.innerHTML = `
            <div class="be-widget-hub-panel">
                <div class="be-widget-hub-head">
                    <span>Widgets</span>
                    <span>Hotkey: <b id="beWidgetHubHint">Home</b></span>
                </div>
                <div class="be-widget-hub-grid">
                    <button type="button" class="be-widget-card be-widget-hud be-widget-dashboard" data-open-tab="dashboard"><span class="be-widget-hud-glyph" aria-hidden="true"><svg viewBox="0 0 24 24" width="40" height="40" fill="none"><path d="M4 10.5L12 4l8 6.5V20a1 1 0 0 1-1 1h-5v-6H10v6H5a1 1 0 0 1-1-1v-9.5z" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/></svg></span><span class="be-widget-hud-copy"><span class="be-widget-title">Dashboard</span><small>Main tracker panel</small></span></button>
                    <button type="button" class="be-widget-card be-widget-hud be-widget-stats" data-open-tab="graph"><span class="be-widget-hud-glyph" aria-hidden="true"><svg viewBox="0 0 24 24" width="40" height="40" fill="none"><path d="M5 19V5m4 14V9m4 10v-6m4 6v-3" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg></span><span class="be-widget-hud-copy"><span class="be-widget-title">Graph</span><small>Weekly XP and wins</small></span></button>
                    <button type="button" class="be-widget-card be-widget-hud be-widget-stats" data-open-tab="statsOverview"><span class="be-widget-hud-glyph" aria-hidden="true"><svg viewBox="0 0 24 24" width="40" height="40" fill="none"><path d="M8 6h12M8 12h12M8 18h8" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/><rect x="4" y="4" width="14" height="16" rx="2" stroke="currentColor" stroke-width="1.5"/></svg></span><span class="be-widget-hud-copy"><span class="be-widget-title">Stats</span><small>All tracked stats overview</small></span></button>
                    <button type="button" class="be-widget-card be-widget-hud be-widget-calculator" data-open-tab="calculator"><span class="be-widget-hud-glyph" aria-hidden="true"><svg viewBox="0 0 24 24" width="40" height="40" fill="none"><rect x="5" y="3" width="14" height="18" rx="2" stroke="currentColor" stroke-width="1.5"/><path d="M8 8h8M9 12h1.5v1.5H9V15M13 12h2M8 17h8" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/></svg></span><span class="be-widget-hud-copy"><span class="be-widget-title">Calculator</span><small>XP, level, and goal wins</small></span></button>
                    <button type="button" class="be-widget-card be-widget-hud be-widget-social" data-open-tab="social"><span class="be-widget-hud-glyph" aria-hidden="true"><svg viewBox="0 0 24 24" width="40" height="40" fill="none"><circle cx="9" cy="8" r="2.5" stroke="currentColor" stroke-width="1.5"/><path d="M4 19v-1a4 4 0 0 1 4-4h2" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/><circle cx="17" cy="9" r="2" stroke="currentColor" stroke-width="1.5"/><path d="M21 19v0a4 4 0 0 0-3-3.87" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg></span><span class="be-widget-hud-copy"><span class="be-widget-title">Accounts</span><small>Vault, logins, friends</small></span></button>
                    <button type="button" class="be-widget-card be-widget-hud be-widget-visuals" data-open-tab="visuals"><span class="be-widget-hud-glyph" aria-hidden="true"><svg viewBox="0 0 24 24" width="40" height="40" fill="none"><circle cx="12" cy="12" r="3" stroke="currentColor" stroke-width="1.5"/><path d="M3 12h0a9 9 0 0 1 9-9h0a9 9 0 0 1 9 9h0M3 12a9 9 0 0 0 9 9M21 12a9 9 0 0 1-9 9" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/></svg></span><span class="be-widget-hud-copy"><span class="be-widget-title">Visuals</span><small>Player and chat controls</small></span></button>
                    <button type="button" class="be-widget-card be-widget-hud be-widget-settings" data-open-tab="settings"><span class="be-widget-hud-glyph" aria-hidden="true"><svg viewBox="0 0 24 24" width="40" height="40" fill="none"><path d="M12 15.5a3.5 3.5 0 1 0 0-7 3.5 3.5 0 0 0 0 7z" stroke="currentColor" stroke-width="1.4"/><path d="M19.4 15a1.8 1.8 0 0 0 .36 1.99l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.8 1.8 0 0 0-1.99-.36 1.8 1.8 0 0 0-1 1.64V21a2 2 0 1 1-4 0v-.09a1.8 1.8 0 0 0-1-1.64 1.8 1.8 0 0 0-1.99.36l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.8 1.8 0 0 0 .36-1.99 1.8 1.8 0 0 0-1.64-1h-.09a2 2 0 1 1 0-4h.09a1.8 1.8 0 0 0 1.64-1 1.8 1.8 0 0 0-.36-1.99l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.8 1.8 0 0 0 1.99.36h.09a1.8 1.8 0 0 0 1-1.64V3a2 2 0 1 1 4 0v.09a1.8 1.8 0 0 0 1 1.64h.09a1.8 1.8 0 0 0 1.99-.36l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.8 1.8 0 0 0-.36 1.99v.09a1.8 1.8 0 0 0 1.64 1H21a2 2 0 1 1 0 4h-.09a1.8 1.8 0 0 0-1.64 1z" stroke="currentColor" stroke-width="1.05" stroke-linecap="round" opacity="0.95"/></svg></span><span class="be-widget-hud-copy"><span class="be-widget-title">Settings</span><small>Theme, size, and hotkeys</small></span></button>
                </div>
            </div>
        `;
        widgetHub.addEventListener("click", (ev) => {
            if (ev.target === widgetHub) setWidgetHubVisibility(false);
        });
        document.body.appendChild(widgetHub);
        ensureBonkKeybindOverlayMounted();
        setTimeout(() => {
// 🔥 SAFE VISUAL TOGGLES (delayed + null-safe)

const v = window.BonkVisuals;

// PLAYERS
document.getElementById("togglePlayers").onchange = function () {
    v.players.visible = this.checked;
};

document.getElementById("toggleSkins").onchange = function () {
    v.players.skins = this.checked;
};

document.getElementById("playersOpacity").oninput = function () {
    v.players.alpha = this.value / 100;
};

// USERNAMES
document.getElementById("toggleNames").onchange = function () {
    v.players.usernames.visible = this.checked;
};

document.getElementById("namesOpacity").oninput = function () {
    v.players.usernames.alpha = this.value / 100;
};

// CHAT
document.getElementById("toggleChat").onchange = function () {
    v.chat.visible = this.checked;

    if (v.chatWindow) {
        v.chatWindow.style.opacity = this.checked ? v.chat.alpha : 0;
    }
};

document.getElementById("chatOpacity").oninput = function () {
    v.chat.alpha = this.value / 100;

    if (v.chatWindow) {
        v.chatWindow.style.opacity = v.chat.alpha;
    }
};
            }, 50);
UI.root = div;
UI.root.classList.add("tracker-visible");
UI.root.classList.add("be-overlay-open");
UI.launcher = launcher;
UI.launcherLabel = launcher.querySelector("#beLauncherHotkey");
UI.widgetHub = widgetHub;
UI.widgetHubHint = widgetHub.querySelector("#beWidgetHubHint");
applyOverlaySettings();
await tryVaultAutoUnlockFromRemembered();
const themeSelect = document.getElementById("beThemeSelect");
const sizeSelect = document.getElementById("beSizeSelect");
const setHotkeyBtn = document.getElementById("beSetHotkeyBtn");
const setWidgetHotkeyBtn = document.getElementById("beSetWidgetHotkeyBtn");
const graphWeekSelect = document.getElementById("beGraphWeekSelect");
if (themeSelect) themeSelect.value = uiSettings.theme;
if (sizeSelect) sizeSelect.value = uiSettings.size;
if (graphWeekSelect) graphWeekSelect.value = uiSettings.graphWeek || "";
if (themeSelect) {
    themeSelect.addEventListener("change", () => {
        uiSettings.theme = themeSelect.value === "light" ? "light" : "dark";
        saveUISettings();
        applyOverlaySettings();
    });
}
if (sizeSelect) {
    sizeSelect.addEventListener("change", () => {
        applySizePreset(sizeSelect.value);
        applyOverviewLayout();
        syncOverviewSlotControls();
    });
}
const beFullscreenScaleModeSelect = document.getElementById("beFullscreenScaleModeSelect");
function bePersistFullscreenScaleMode(mode) {
    try {
        localStorage.setItem("be_fullscreen_scale_mode", mode);
        const coverish = mode === "cover";
        localStorage.setItem("be_fullscreen_fill_cover", coverish ? "1" : "0");
    } catch (e) {
        void e;
    }
}
if (beFullscreenScaleModeSelect) {
    try {
        const m = beFullscreenScaleMode();
        const ok =
            m === "contain" ||
            m === "contain-left" ||
            m === "contain-right" ||
            m === "wdb-left" ||
            m === "wdb-right" ||
            m === "cover" ||
            m === "fill";
        beFullscreenScaleModeSelect.value = ok ? m : "contain";
    } catch (e) {
        beFullscreenScaleModeSelect.value = "contain";
    }
    beFullscreenScaleModeSelect.addEventListener("change", () => {
        const v = beFullscreenScaleModeSelect.value;
        if (
            v !== "contain" &&
            v !== "contain-left" &&
            v !== "contain-right" &&
            v !== "wdb-left" &&
            v !== "wdb-right" &&
            v !== "cover" &&
            v !== "fill"
        ) {
            return;
        }
        bePersistFullscreenScaleMode(v);
        beSyncFullscreenFitClasses();
    });
}
if (graphWeekSelect) {
    graphWeekSelect.addEventListener("change", () => {
        uiSettings.graphWeek = graphWeekSelect.value || "";
        saveUISettings();
        const cache = Storage.load();
        renderStatsTabFromData(cache?.data || {});
    });
}
if (setHotkeyBtn) {
    setHotkeyBtn.addEventListener("click", () => {
        if (isCapturingHotkey) return;
        isCapturingHotkey = true;
        setHotkeyBtn.textContent = "Press any key...";
        const onSetKey = (ev) => {
            ev.preventDefault();
            ev.stopPropagation();
            const nextKey = ev.key;
            if (!nextKey) return;
            uiSettings.overlayHotkey = nextKey;
            saveUISettings();
            applyOverlaySettings();
            setHotkeyBtn.textContent = "Set hotkey";
            isCapturingHotkey = false;
            document.removeEventListener("keydown", onSetKey, true);
        };
        document.addEventListener("keydown", onSetKey, true);
    });
}
if (setWidgetHotkeyBtn) {
    setWidgetHotkeyBtn.addEventListener("click", () => {
        if (isCapturingHotkey) return;
        isCapturingHotkey = true;
        setWidgetHotkeyBtn.textContent = "Press any key...";
        const onSetWidgetKey = (ev) => {
            ev.preventDefault();
            ev.stopPropagation();
            const nextKey = ev.key;
            if (!nextKey) return;
            uiSettings.widgetHotkey = nextKey;
            saveUISettings();
            applyOverlaySettings();
            setWidgetHotkeyBtn.textContent = "Set hotkey";
            isCapturingHotkey = false;
            document.removeEventListener("keydown", onSetWidgetKey, true);
        };
        document.addEventListener("keydown", onSetWidgetKey, true);
    });
}
const keybindOverlayToggle = document.getElementById("beKeybindOverlayToggle");
if (keybindOverlayToggle) {
    keybindOverlayToggle.checked = uiSettings.keybindOverlayEnabled === true;
    keybindOverlayToggle.addEventListener("change", () => {
        uiSettings.keybindOverlayEnabled = keybindOverlayToggle.checked;
        saveUISettings();
        applyKeybindOverlayVisibility();
    });
}
const kbOp = document.getElementById("beKbOverlayOpacity");
const kbSc = document.getElementById("beKbOverlayScale");
const kbCp = document.getElementById("beKbOverlayCompact");
if (kbOp) {
    kbOp.value = String(Math.round((uiSettings.keybindOverlayOpacity ?? 1) * 100));
    kbOp.addEventListener("input", () => {
        uiSettings.keybindOverlayOpacity = Number(kbOp.value) / 100;
        saveUISettings();
        applyKeybindOverlayStyle();
    });
}
if (kbSc) {
    kbSc.value = String(Math.round((uiSettings.keybindOverlayScale ?? 1) * 100));
    kbSc.addEventListener("input", () => {
        uiSettings.keybindOverlayScale = Number(kbSc.value) / 100;
        saveUISettings();
        applyKeybindOverlayStyle();
    });
}
if (kbCp) {
    kbCp.checked = uiSettings.keybindOverlayCompact === true;
    kbCp.addEventListener("change", () => {
        uiSettings.keybindOverlayCompact = kbCp.checked;
        saveUISettings();
        applyKeybindOverlayStyle();
    });
}
applyKeybindOverlayStyle();
const presetSave = document.getElementById("bePresetSaveBtn");
const presetLoad = document.getElementById("bePresetLoadBtn");
const presetDel = document.getElementById("bePresetDeleteBtn");
if (presetSave) {
    presetSave.addEventListener("click", () => {
        const nameInp = document.getElementById("bePresetName");
        const name = (nameInp?.value || "").trim() || `Preset ${(uiSettings.uiPresets?.length || 0) + 1}`;
        const id = typeof crypto !== "undefined" && typeof crypto.randomUUID === "function"
            ? crypto.randomUUID()
            : `p_${Date.now()}`;
        const data = beSnapshotUiPresetData();
        const list = Array.isArray(uiSettings.uiPresets) ? uiSettings.uiPresets.filter((p) => p.name !== name) : [];
        list.push({ id, name, data });
        uiSettings.uiPresets = list.slice(-8);
        saveUISettings();
        beRefreshPresetSelect();
    });
}
if (presetLoad) {
    presetLoad.addEventListener("click", () => {
        const sel = document.getElementById("bePresetSelect");
        const id = sel?.value;
        if (!id) return;
        const p = uiSettings.uiPresets.find((x) => x.id === id);
        if (p?.data) beApplyUiPresetData(p.data);
    });
}
if (presetDel) {
    presetDel.addEventListener("click", () => {
        const sel = document.getElementById("bePresetSelect");
        const id = sel?.value;
        if (!id) return;
        uiSettings.uiPresets = uiSettings.uiPresets.filter((x) => x.id !== id);
        saveUISettings();
        beRefreshPresetSelect();
    });
}
beRefreshPresetSelect();
const healthRef = document.getElementById("beScriptHealthRefresh");
if (healthRef) healthRef.addEventListener("click", () => refreshScriptHealthPanel());
const csvBtn = document.getElementById("beExportStatsCsv");
if (csvBtn) csvBtn.addEventListener("click", () => beExportStatsToCsv());
        // 🔥 cache DOM elements (DO THIS ONCE)
UI.xpGain = document.getElementById("xpGain");
UI.winsLine = document.getElementById("winsLine");
UI.rateLine = document.getElementById("rateLine");
UI.sessionLine = document.getElementById("sessionLine");
UI.statusText = document.getElementById("statusText");
UI.levelLine = document.getElementById("levelLine");
UI.playerName = document.getElementById("playerName");
UI.playerSkin = document.getElementById("playerSkin");
UI.xpPercent = document.getElementById("xpPercent");
UI.xpCircle = document.getElementById("xpCircle");
UI.xpText = document.getElementById("xpText");
        UI.xpCircleWrapper = document.getElementById("xpCircleWrapper");
        UI.lastWinLine = document.getElementById("lastWinLine");
UI.dailyProgressBar = document.getElementById("dailyProgressBar");
UI.card = div.querySelector(".tracker-card");
UI.sessionStatLine = document.getElementById("sessionStatLine");
UI.levelStatLine = document.getElementById("levelStatLine");
applyDashboardStatVisibility();
applyOverviewLayout();
syncOverviewSlotControls();
const overviewSlotWrap = document.getElementById("beOverviewSlots");
if (overviewSlotWrap) {
    overviewSlotWrap.querySelectorAll("select[data-slot-idx]").forEach((sel) => {
        sel.addEventListener("change", () => {
            const idx = Number(sel.getAttribute("data-slot-idx") || "0");
            const size = uiSettings.size === "tiny" || uiSettings.size === "large" ? uiSettings.size : "medium";
            const slots = getOverviewSlotsForCurrentSize();
            slots[idx] = sel.value;
            uiSettings.overviewSlots[size] = slots;
            saveUISettings();
            applyOverviewLayout();
            syncOverviewSlotControls();
        });
    });
}

        let isDragging = false, offsetX, offsetY;

        const dragHandle = div.querySelector("#dragHandle");
        dragHandle.addEventListener("contextmenu", (e) => {
            e.preventDefault();
        });
        const menuBtn = div.querySelector(".tracker-menu-icon");
const sidebar = document.getElementById("trackerSidebar");

menuBtn.addEventListener("click", (e) => {
    e.stopPropagation();
    sidebar.classList.toggle("open");
});
        document.getElementById("topbarSettings").addEventListener("click", (e) => {
    e.stopPropagation();
    switchTab("settings");
    sidebar.classList.remove("open");
});

document.getElementById("topbarClose").addEventListener("click", (e) => {
    e.stopPropagation();
    setOverlayVisibility(false);
});

function parseCalcField(el) {
    if (!el) return NaN;
    const v = String(el.value ?? "").trim();
    if (v === "") return NaN;
    const n = Number(v);
    return Number.isFinite(n) ? n : NaN;
}

function recalcBeCalculator() {
    const lv2xpIn = document.getElementById("beCalcLv2XpIn");
    const lv2xpOut = document.getElementById("beCalcLv2XpOut");
    if (lv2xpOut) {
        const lv = parseCalcField(lv2xpIn);
        if (lv >= 1) lv2xpOut.textContent = `${getXPForLevel(Math.floor(lv)).toLocaleString()} XP`;
        else lv2xpOut.textContent = "—";
    }
    const xp2lvIn = document.getElementById("beCalcXp2LvIn");
    const xp2lvOut = document.getElementById("beCalcXp2LvOut");
    if (xp2lvOut) {
        const x = parseCalcField(xp2lvIn);
        if (Number.isFinite(x) && x >= 0) xp2lvOut.textContent = `Level ${getLevelFromXP(Math.floor(x))}`;
        else xp2lvOut.textContent = "—";
    }
    const startLvEl = document.getElementById("beCalcStartLv");
    const startXpEl = document.getElementById("beCalcStartXp");
    const goalLvEl = document.getElementById("beCalcGoalLv");
    const goalXpEl = document.getElementById("beCalcGoalXp");
    const goalRes = document.getElementById("beCalcGoalResult");
    const rawStartXp = parseCalcField(startXpEl);
    const rawStartLv = parseCalcField(startLvEl);
    let startXp = 0;
    if (Number.isFinite(rawStartXp) && rawStartXp >= 0) {
        startXp = Math.floor(rawStartXp);
    } else if (rawStartLv >= 1) {
        startXp = getXPForLevel(Math.floor(rawStartLv));
    }
    let goalXpVal = NaN;
    const gx = parseCalcField(goalXpEl);
    const glv = parseCalcField(goalLvEl);
    if (Number.isFinite(gx) && gx >= 0) goalXpVal = Math.floor(gx);
    else if (glv >= 1) goalXpVal = getXPForLevel(Math.floor(glv));
    if (goalRes) {
        if (!Number.isFinite(goalXpVal)) {
            goalRes.textContent = "Enter a goal level or total XP.";
            goalRes.style.color = "";
        } else {
            const delta = goalXpVal - startXp;
            const wins = Math.ceil(Math.max(0, delta) / 100);
            if (delta <= 0) {
                goalRes.textContent = `Already at or past goal (${delta.toLocaleString()} XP Δ).`;
                goalRes.style.color = "#ffb86b";
            } else {
                goalRes.textContent = `Need ${delta.toLocaleString()} XP (~${wins} win${wins === 1 ? "" : "s"}).`;
                goalRes.style.color = "#9cffb0";
            }
        }
    }
}

function refreshBeCalculatorFromTracker() {
    const sl = document.getElementById("beCalcStartLv");
    const sx = document.getElementById("beCalcStartXp");
    if (!sl || !sx) return;
    const { data, key } = Storage.load();
    let xp = data[key]?.xp;
    if (xp === undefined || xp === null) xp = data?._globalXP;
    xp = Math.max(0, Math.floor(Number(xp) || 0));
    const level = getLevelFromXP(xp);
    sl.value = String(level);
    sx.value = String(xp);
    recalcBeCalculator();
}

function renderVaultMount() {
    const mount = document.getElementById("beVaultMount");
    if (!mount) return;

    const stored = beVaultReadStored();

    if (!stored) {
        mount.innerHTML = `
            <div style="display:flex;flex-direction:column;gap:8px;max-width:340px;">
                <span style="font-size:11px;opacity:0.9;">Create a strong master password. It encrypts your saved Bonk logins (not the stats list below).</span>
                <input type="password" id="beVaultNewMp1" autocomplete="off" placeholder="Master password" style="padding:8px;border-radius:8px;border:1px solid rgba(255,255,255,0.15);background:rgba(0,0,0,0.25);color:inherit;" />
                <input type="password" id="beVaultNewMp2" autocomplete="off" placeholder="Confirm master password" style="padding:8px;border-radius:8px;border:1px solid rgba(255,255,255,0.15);background:rgba(0,0,0,0.25);color:inherit;" />
                <label style="font-size:11px;opacity:0.9;display:flex;align-items:center;gap:6px;cursor:pointer;">
                    <input type="checkbox" id="beVaultRememberCb" checked />
                    Remember master password on this PC (plain text in localStorage — only if you trust this machine)
                </label>
                <button type="button" data-be-vault="create" style="cursor:pointer;padding:8px 12px;border-radius:8px;border:1px solid rgba(120,180,255,0.45);background:rgba(25,45,80,0.6);color:#e8f0ff;font:600 12px 'Segoe UI',sans-serif;">Create encrypted vault</button>
                <span id="beVaultErr" style="font-size:11px;color:#ff8e8e;min-height:14px;"></span>
            </div>`;
        return;
    }

    if (!beVaultSession.unlocked) {
        const rem = isVaultRememberDevice();
        mount.innerHTML = `
            <div style="display:flex;flex-direction:column;gap:8px;max-width:340px;">
                <input type="password" id="beVaultMp" autocomplete="off" placeholder="Master password" style="padding:8px;border-radius:8px;border:1px solid rgba(255,255,255,0.15);background:rgba(0,0,0,0.25);color:inherit;" />
                <label style="font-size:11px;opacity:0.9;display:flex;align-items:center;gap:6px;cursor:pointer;">
                    <input type="checkbox" id="beVaultRememberCb" ${rem ? "checked" : ""} />
                    Remember on this PC (plain text in localStorage)
                </label>
                <button type="button" data-be-vault="unlock" style="cursor:pointer;padding:8px 12px;border-radius:8px;border:1px solid rgba(120,180,255,0.45);background:rgba(25,45,80,0.6);color:#e8f0ff;font:600 12px 'Segoe UI',sans-serif;">Unlock</button>
                <span id="beVaultErr" style="font-size:11px;color:#ff8e8e;min-height:14px;"></span>
            </div>`;
        return;
    }

    const rows = beVaultSession.entries.length
        ? beVaultSession.entries.map((e) => {
            const lab = e.label ? `${escapeHtml(e.label)} · ` : "";
            return `<div class="stats-history-item" style="flex-wrap:wrap;gap:8px;align-items:flex-start;">
                <span style="min-width:0;"><b>${lab}${escapeHtml(e.username)}</b><br>
                <span style="font-size:10px;opacity:0.75;">Password hidden — use copy</span></span>
                <span style="display:flex;flex-wrap:wrap;gap:6px;">
                    <button type="button" data-be-vault="copy-user" data-entry-id="${escapeHtml(e.id)}" style="cursor:pointer;padding:4px 8px;border-radius:6px;border:1px solid rgba(120,180,255,0.4);background:rgba(20,40,70,0.5);color:#dbeaff;font:11px 'Segoe UI',sans-serif;">Copy user</button>
                    <button type="button" data-be-vault="copy-pass" data-entry-id="${escapeHtml(e.id)}" style="cursor:pointer;padding:4px 8px;border-radius:6px;border:1px solid rgba(120,180,255,0.4);background:rgba(20,40,70,0.5);color:#dbeaff;font:11px 'Segoe UI',sans-serif;">Copy password</button>
                    <button type="button" data-be-vault="autofill-login" data-entry-id="${escapeHtml(e.id)}" style="cursor:pointer;padding:4px 8px;border-radius:6px;border:1px solid rgba(120,255,180,0.45);background:rgba(15,55,40,0.55);color:#c8ffd8;font:11px 'Segoe UI',sans-serif;">Autofill login</button>
                    <button type="button" data-be-vault="open-bonk" style="cursor:pointer;padding:4px 8px;border-radius:6px;border:1px solid rgba(180,220,255,0.35);background:rgba(20,35,55,0.45);color:#cfe6ff;font:11px 'Segoe UI',sans-serif;">Open bonk.io</button>
                    <button type="button" data-be-vault="remove-entry" data-entry-id="${escapeHtml(e.id)}" style="cursor:pointer;padding:4px 8px;border-radius:6px;border:1px solid rgba(255,120,120,0.35);background:rgba(50,20,20,0.4);color:#ffc9c9;font:11px 'Segoe UI',sans-serif;">Remove</button>
                </span>
            </div>`;
        }).join("")
        : `<div class="stats-history-item"><span>No saved logins yet</span><span>—</span></div>`;

    const rememberOn = isVaultRememberDevice();
    const unlockHint = rememberOn
        ? "Unlocked — remembered on this PC (no auto-lock)"
        : "Unlocked — auto-locks after 2 hours idle";

    mount.innerHTML = `
        <div style="display:flex;flex-direction:column;gap:10px;">
            <div style="display:flex;flex-wrap:wrap;gap:8px;align-items:center;">
                <button type="button" data-be-vault="lock" style="cursor:pointer;padding:6px 12px;border-radius:8px;border:1px solid rgba(255,200,120,0.4);background:rgba(60,45,20,0.45);color:#ffe6b0;font:600 12px 'Segoe UI',sans-serif;">Lock vault</button>
                ${rememberOn ? `<button type="button" data-be-vault="forget-remember" style="cursor:pointer;padding:6px 10px;border-radius:8px;border:1px solid rgba(255,120,120,0.35);background:rgba(45,20,20,0.45);color:#ffc9c9;font:600 11px 'Segoe UI',sans-serif;">Forget saved master</button>` : ""}
                <span style="font-size:10px;opacity:0.75;">${unlockHint}</span>
            </div>
            <div style="display:flex;flex-wrap:wrap;gap:8px;align-items:center;">
                <button type="button" data-be-vault="export-file" style="cursor:pointer;padding:6px 10px;border-radius:8px;border:1px solid rgba(120,200,255,0.4);background:rgba(18,40,65,0.55);color:#d6ecff;font:600 11px 'Segoe UI',sans-serif;">Export vault backup file</button>
                <label style="font-size:11px;cursor:pointer;opacity:0.95;display:inline-flex;align-items:center;gap:6px;">
                    <input type="file" id="beVaultImportFile" accept="application/json,.json" style="display:none;" />
                    <span style="padding:6px 10px;border-radius:8px;border:1px solid rgba(200,200,255,0.35);background:rgba(30,30,55,0.45);">Import backup…</span>
                </label>
            </div>
            <p style="font-size:10px;opacity:0.72;margin:0;line-height:1.35;">Backup is the encrypted blob only — still protect the file. Import replaces the stored vault; unlock again afterward.</p>
            <span id="beVaultMsg" style="font-size:11px;color:#9cffb0;min-height:14px;"></span>
            <div style="font-size:11px;opacity:0.85;margin-bottom:4px;">Add login</div>
            <div style="display:grid;gap:6px;max-width:400px;">
                <input type="text" id="beVaultAddLabel" autocomplete="off" placeholder="Label (optional)" style="padding:7px;border-radius:8px;border:1px solid rgba(255,255,255,0.12);background:rgba(0,0,0,0.22);color:inherit;" />
                <input type="text" id="beVaultAddUser" autocomplete="off" placeholder="Bonk username" style="padding:7px;border-radius:8px;border:1px solid rgba(255,255,255,0.12);background:rgba(0,0,0,0.22);color:inherit;" />
                <input type="password" id="beVaultAddPass" autocomplete="off" placeholder="Password" style="padding:7px;border-radius:8px;border:1px solid rgba(255,255,255,0.12);background:rgba(0,0,0,0.22);color:inherit;" />
                <button type="button" data-be-vault="add-entry" style="cursor:pointer;padding:7px 12px;border-radius:8px;border:1px solid rgba(76,175,80,0.45);background:rgba(20,50,30,0.5);color:#c8ffc8;font:600 12px 'Segoe UI',sans-serif;width:fit-content;">Save login</button>
            </div>
            <div style="margin-top:8px;font-size:11px;opacity:0.85;">Saved accounts</div>
            <div>${rows}</div>
            <details style="margin-top:8px;font-size:11px;">
                <summary style="cursor:pointer;opacity:0.9;">Change master password</summary>
                <div style="display:grid;gap:6px;margin-top:8px;max-width:340px;">
                    <input type="password" id="beVaultOldMp" autocomplete="off" placeholder="Current master password" style="padding:7px;border-radius:8px;border:1px solid rgba(255,255,255,0.12);background:rgba(0,0,0,0.22);color:inherit;" />
                    <input type="password" id="beVaultNewMpCh1" autocomplete="off" placeholder="New master password" style="padding:7px;border-radius:8px;border:1px solid rgba(255,255,255,0.12);background:rgba(0,0,0,0.22);color:inherit;" />
                    <input type="password" id="beVaultNewMpCh2" autocomplete="off" placeholder="Confirm new" style="padding:7px;border-radius:8px;border:1px solid rgba(255,255,255,0.12);background:rgba(0,0,0,0.22);color:inherit;" />
                    <button type="button" data-be-vault="changemp" style="cursor:pointer;padding:7px 12px;border-radius:8px;border:1px solid rgba(120,180,255,0.45);background:rgba(25,45,80,0.6);color:#e8f0ff;font:600 12px 'Segoe UI',sans-serif;width:fit-content;">Update master password</button>
                </div>
            </details>
        </div>`;
}

function renderGameAltsTable() {
    const wrap = document.getElementById("beAltsTableWrap");
    if (!wrap) return;
    const filtEl = document.getElementById("beAltsFilter");
    if (filtEl) beAltsFilterQuery = filtEl.value || "";
    const q = (beAltsFilterQuery || "").trim().toLowerCase();
    const alts = loadAltsRegistry();
    const current = (getPlayerKey() || "").trim().toLowerCase();

    const filtered = !q
        ? alts
        : alts.filter((a) => {
              const nm = a.name.toLowerCase();
              if (nm.includes(q)) return true;
              return (a.tags || []).some((t) => t.toLowerCase().includes(q));
          });

    if (!alts.length) {
        wrap.innerHTML = `<div class="stats-history-item"><span>No alts recorded yet</span><span>—</span></div>`;
        return;
    }
    if (!filtered.length) {
        wrap.innerHTML = `<div class="stats-history-item"><span>No matches</span><span>—</span></div>`;
        return;
    }

    wrap.innerHTML = filtered.map((a) => {
        const isYou = a.name.trim().toLowerCase() === current;
        const when = a.lastSeen ? new Date(a.lastSeen).toLocaleString() : "—";
        const tag = isYou ? ` <span style="opacity:0.85;font-size:10px;">(this tab)</span>` : "";
        const enc = encodeURIComponent(a.name);
        const tagsStr = (a.tags || []).join(", ");
        return `<div class="stats-history-item be-alt-row">
            <div class="be-alt-row-main"><b>${escapeHtml(a.name)}</b>${tag}<br><span style="font-size:10px;opacity:0.8;">Lv ${a.level} · ${a.xp.toLocaleString()} XP · ${when}</span>
            <div style="margin-top:6px;font-size:10px;opacity:0.85;">Tags <input type="text" class="be-alt-tags" data-alt="${enc}" value="${escapeHtml(tagsStr)}" placeholder="comma separated" style="width:100%;max-width:100%;box-sizing:border-box;padding:4px 6px;border-radius:6px;border:1px solid rgba(255,255,255,0.12);background:rgba(0,0,0,0.2);color:inherit;" /></div></div>
            <div class="be-alt-row-actions">
                <button type="button" class="be-alt-open" data-alt="${enc}" style="cursor:pointer;padding:4px 8px;border-radius:6px;border:1px solid rgba(120,180,255,0.4);background:rgba(20,40,70,0.5);color:#dbeaff;font:11px 'Segoe UI',sans-serif;">Open login</button>
                <button type="button" class="be-alt-remove" data-alt="${enc}" style="cursor:pointer;padding:4px 8px;border-radius:6px;border:1px solid rgba(255,120,120,0.35);background:rgba(50,20,20,0.4);color:#ffc9c9;font:11px 'Segoe UI',sans-serif;">Remove</button>
            </div>
        </div>`;
    }).join("");

    if (filtEl && !filtEl.__beAltFilterHooked) {
        filtEl.__beAltFilterHooked = true;
        filtEl.addEventListener("input", () => {
            renderGameAltsTable();
        });
    }

    wrap.querySelectorAll("button.be-alt-open").forEach((btn) => {
        btn.addEventListener("click", () => {
            window.open("https://bonk.io/", "_blank");
        });
    });
    wrap.querySelectorAll("button.be-alt-remove").forEach((btn) => {
        btn.addEventListener("click", () => {
            let raw = "";
            try {
                raw = decodeURIComponent(btn.getAttribute("data-alt") || "");
            } catch {
                raw = "";
            }
            const next = loadAltsRegistry().filter((x) => x.name !== raw);
            saveAltsRegistry(next);
            renderSocialPanel();
        });
    });
    wrap.querySelectorAll("input.be-alt-tags").forEach((inp) => {
        inp.addEventListener("change", () => {
            let raw = "";
            try {
                raw = decodeURIComponent(inp.getAttribute("data-alt") || "");
            } catch {
                raw = "";
            }
            const tags = (inp.value || "")
                .split(",")
                .map((s) => s.trim())
                .filter(Boolean)
                .slice(0, 16);
            const all = loadAltsRegistry();
            const i = all.findIndex((x) => x.name === raw);
            if (i < 0) return;
            all[i] = { ...all[i], tags };
            saveAltsRegistry(all);
        });
    });
}

function renderSocialPanel() {
    const ta = document.getElementById("beFriendsTextarea");
    const hint = document.getElementById("beFriendsSaveHint");
    if (ta) ta.value = loadFriendsList().join("\n");
    if (hint) hint.textContent = "";

    renderVaultMount();
    renderGameAltsTable();
}

function escapeHtml(s) {
    return String(s)
        .replace(/&/g, "&amp;")
        .replace(/</g, "&lt;")
        .replace(/>/g, "&gt;")
        .replace(/"/g, "&quot;");
}

function switchTab(tab) {
    if (!tab) return;

    const views = div.querySelectorAll(".tracker-view");
    const items = div.querySelectorAll(".sidebar-item");

    if (!views.length) {
        console.warn("[UI] No views found");
        return;
    }

    // stable reset: hide everything first
    views.forEach(v => {
        v.classList.remove("active");
        v.classList.remove("fade-in");
        v.style.display = "none";
    });
    items.forEach(i => i.classList.remove("active"));

    // 🔥 activate sidebar item
    const activeItem = div.querySelector(`.sidebar-item[data-tab="${tab}"]`);
    if (activeItem) {
        activeItem.classList.add("active");
    } else {
        console.warn("[UI] Missing sidebar item for:", tab);
    }

    // 🔥 activate view
    const target = div.querySelector(`.tracker-view[data-view="${tab}"]`);

    if (!target) {
        console.warn("[UI] Missing view for:", tab);
        return;
    }

    // show + fade-in target only (no overlap)
    target.style.display = "block";
    target.classList.add("active");
    target.classList.add("fade-in");
    setTimeout(() => target.classList.remove("fade-in"), 240);
    if (tab === "graph") {
        const cache = Storage.load();
        renderStatsTabFromData(cache?.data || {});
    } else if (tab === "statsOverview") {
        const cache = Storage.load();
        renderStatsOverview(cache?.data || {});
    } else if (tab === "calculator") {
        refreshBeCalculatorFromTracker();
    } else if (tab === "social") {
        renderSocialPanel();
    } else if (tab === "settings") {
        refreshScriptHealthPanel();
        beRefreshPresetSelect();
    }

    // 🔥 DEBUG
    console.debug("[UI] Switched to:", tab);
}
UI.switchTab = switchTab;

(function attachBeCalculatorListenersOnce() {
    const cv = div.querySelector('.tracker-view[data-view="calculator"]');
    if (!cv || cv._beCalcBound) return;
    cv._beCalcBound = true;
    ["beCalcLv2XpIn", "beCalcXp2LvIn", "beCalcStartLv", "beCalcStartXp", "beCalcGoalLv", "beCalcGoalXp"].forEach((id) => {
        const el = document.getElementById(id);
        if (el) el.addEventListener("input", recalcBeCalculator);
    });
})();

(function attachSocialListenersOnce() {
    const sv = div.querySelector('.tracker-view[data-view="social"]');
    if (!sv || sv._beSocialBound) return;
    sv._beSocialBound = true;
    const btn = document.getElementById("beFriendsSaveBtn");
    if (btn) {
        btn.addEventListener("click", () => {
            const ta = document.getElementById("beFriendsTextarea");
            const hint = document.getElementById("beFriendsSaveHint");
            const lines = (ta?.value || "").split(/\r?\n/).map((s) => s.trim()).filter(Boolean);
            saveFriendsList(lines);
            if (hint) {
                hint.textContent = "Saved — gold name tint applies in matches.";
                setTimeout(() => {
                    if (hint) hint.textContent = "";
                }, 3200);
            }
        });
    }

    sv.addEventListener("click", (e) => {
        let t = e.target instanceof Element ? e.target.closest("[data-be-vault]") : null;
        if (!t && typeof e.composedPath === "function") {
            const path = e.composedPath();
            for (let i = 0; i < path.length; i++) {
                const node = path[i];
                if (node instanceof HTMLElement && node.hasAttribute("data-be-vault")) {
                    t = node;
                    break;
                }
            }
        }
        if (!(t instanceof HTMLElement)) return;
        const act = t.getAttribute("data-be-vault");
        if (!act) return;

        const run = async () => {
            const errEl = () => document.getElementById("beVaultErr");
            const msgEl = () => document.getElementById("beVaultMsg");

            if (act === "create") {
                const p1 = (document.getElementById("beVaultNewMp1")?.value || "").trim();
                const p2 = (document.getElementById("beVaultNewMp2")?.value || "").trim();
                const err = errEl();
                if (p1.length < 8) {
                    if (err) err.textContent = "Use at least 8 characters.";
                    return;
                }
                if (p1 !== p2) {
                    if (err) err.textContent = "Passwords do not match.";
                    return;
                }
                try {
                    await beVaultCreateVault(p1);
                    if (document.getElementById("beVaultRememberCb")?.checked) {
                        saveVaultRememberMaster(p1);
                        if (beVaultSession.lockTimer) {
                            clearTimeout(beVaultSession.lockTimer);
                            beVaultSession.lockTimer = null;
                        }
                    } else {
                        clearVaultRememberDevice();
                    }
                    if (err) err.textContent = "";
                    renderSocialPanel();
                } catch (ex) {
                    if (err) err.textContent = "Could not create vault.";
                    BE_LOG.error("vault create", ex);
                }
                return;
            }

            if (act === "unlock") {
                const pw = (document.getElementById("beVaultMp")?.value || "").trim();
                const err = errEl();
                if (!pw) {
                    if (err) err.textContent = "Enter master password.";
                    return;
                }
                try {
                    await beVaultUnlockWithPassword(pw);
                    if (document.getElementById("beVaultRememberCb")?.checked) {
                        saveVaultRememberMaster(pw);
                        if (beVaultSession.lockTimer) {
                            clearTimeout(beVaultSession.lockTimer);
                            beVaultSession.lockTimer = null;
                        }
                    } else {
                        clearVaultRememberDevice();
                    }
                    const inp = document.getElementById("beVaultMp");
                    if (inp) inp.value = "";
                    if (err) err.textContent = "";
                    renderSocialPanel();
                } catch {
                    if (err) err.textContent = "Wrong password or unreadable vault.";
                }
                return;
            }

            if (act === "forget-remember") {
                clearVaultRememberDevice();
                beVaultLock();
                renderSocialPanel();
                return;
            }

            if (act === "lock") {
                beVaultLock();
                renderSocialPanel();
                return;
            }

            if (act === "export-file") {
                const msg = msgEl();
                const raw = localStorage.getItem(BE_VAULT_STORAGE_KEY);
                if (!raw) {
                    if (msg) {
                        msg.style.color = "#ff8e8e";
                        msg.textContent = "No vault data to export.";
                    }
                    return;
                }
                try {
                    let payload;
                    try {
                        payload = JSON.parse(raw);
                    } catch {
                        payload = raw;
                    }
                    const blob = new Blob(
                        [JSON.stringify({ v: 1, beVaultExport: true, exportedAt: Date.now(), payload })],
                        { type: "application/json" }
                    );
                    const a = document.createElement("a");
                    a.href = URL.createObjectURL(blob);
                    a.download = `bonk-enhanced-vault-${new Date().toISOString().slice(0, 10)}.json`;
                    a.click();
                    setTimeout(() => {
                        try {
                            URL.revokeObjectURL(a.href);
                        } catch {}
                    }, 5000);
                    if (msg) {
                        msg.style.color = "#9cffb0";
                        msg.textContent = "Backup downloaded — keep the file private.";
                    }
                } catch (ex) {
                    BE_LOG.error("vault export file", ex);
                    if (msg) {
                        msg.style.color = "#ff8e8e";
                        msg.textContent = "Export failed.";
                    }
                }
                return;
            }

            if (act === "autofill-login") {
                const msg = msgEl();
                if (!beVaultSession.unlocked) {
                    if (msg) {
                        msg.style.color = "#ff8e8e";
                        msg.textContent = "Unlock the vault first (session not unlocked).";
                    }
                    BE_LOG.warn("autofill-login skipped: vault locked");
                    return;
                }
                beVaultTouchActivity();
                const eid = t.getAttribute("data-entry-id");
                const ent = eid ? beVaultSession.entries.find((x) => x.id === eid) : null;
                if (!ent || !ent.username) {
                    if (msg) {
                        msg.style.color = "#ff8e8e";
                        msg.textContent = "Entry not found.";
                    }
                    return;
                }
                queueBonkLoginAutofill(ent.username, ent.password);
                window.open("https://bonk.io/", "_blank");
                if (msg) {
                    msg.style.color = "#9cffb0";
                    msg.textContent = "Queued autofill — check the new tab (main bonk.io page).";
                    setTimeout(() => {
                        const m = msgEl();
                        if (m) m.textContent = "";
                    }, 4500);
                }
                return;
            }

            if (!beVaultSession.unlocked) return;

            beVaultTouchActivity();

            if (act === "open-bonk") {
                window.open("https://bonk.io/", "_blank");
                return;
            }

            if (act === "add-entry") {
                const label = (document.getElementById("beVaultAddLabel")?.value || "").trim();
                const username = (document.getElementById("beVaultAddUser")?.value || "").trim();
                const password = document.getElementById("beVaultAddPass")?.value || "";
                const msg = msgEl();
                if (!username) {
                    if (msg) {
                        msg.style.color = "#ff8e8e";
                        msg.textContent = "Enter a username.";
                    }
                    return;
                }
                if (beVaultSession.entries.length >= 40) {
                    if (msg) {
                        msg.style.color = "#ff8e8e";
                        msg.textContent = "Maximum 40 entries.";
                    }
                    return;
                }
                try {
                    beVaultSession.entries.push({
                        id: beNewEntryId(),
                        username,
                        password,
                        label
                    });
                    await beVaultPersist();
                    const u = document.getElementById("beVaultAddUser");
                    const p = document.getElementById("beVaultAddPass");
                    const l = document.getElementById("beVaultAddLabel");
                    if (u) u.value = "";
                    if (p) p.value = "";
                    if (l) l.value = "";
                    if (msg) {
                        msg.style.color = "#9cffb0";
                        msg.textContent = "Saved.";
                        setTimeout(() => {
                            const m = msgEl();
                            if (m) m.textContent = "";
                        }, 2400);
                    }
                    renderSocialPanel();
                } catch (ex) {
                    if (msg) {
                        msg.style.color = "#ff8e8e";
                        msg.textContent = "Could not save.";
                    }
                    BE_LOG.error("vault add", ex);
                }
                return;
            }

            const entryId = t.getAttribute("data-entry-id");
            const entry = entryId ? beVaultSession.entries.find((x) => x.id === entryId) : null;

            if (act === "copy-user" && entry) {
                try {
                    await navigator.clipboard.writeText(entry.username);
                    const msg = msgEl();
                    if (msg) {
                        msg.style.color = "#9cffb0";
                        msg.textContent = "Username copied.";
                        setTimeout(() => {
                            const m = msgEl();
                            if (m) m.textContent = "";
                        }, 2000);
                    }
                } catch {
                    BE_LOG.warn("clipboard user");
                }
                return;
            }

            if (act === "copy-pass" && entry) {
                try {
                    await navigator.clipboard.writeText(entry.password);
                    const msg = msgEl();
                    if (msg) {
                        msg.style.color = "#9cffb0";
                        msg.textContent = "Password copied.";
                        setTimeout(() => {
                            const m = msgEl();
                            if (m) m.textContent = "";
                        }, 2000);
                    }
                } catch {
                    BE_LOG.warn("clipboard pass");
                }
                return;
            }

            if (act === "remove-entry" && entryId) {
                try {
                    beVaultSession.entries = beVaultSession.entries.filter((x) => x.id !== entryId);
                    await beVaultPersist();
                    renderSocialPanel();
                } catch (ex) {
                    BE_LOG.error("vault remove", ex);
                }
                return;
            }

            if (act === "changemp") {
                const oldPw = (document.getElementById("beVaultOldMp")?.value || "").trim();
                const n1 = (document.getElementById("beVaultNewMpCh1")?.value || "").trim();
                const n2 = (document.getElementById("beVaultNewMpCh2")?.value || "").trim();
                const msg = msgEl();
                if (!oldPw || !n1) {
                    if (msg) {
                        msg.style.color = "#ff8e8e";
                        msg.textContent = "Fill current and new password.";
                    }
                    return;
                }
                if (n1.length < 8) {
                    if (msg) {
                        msg.style.color = "#ff8e8e";
                        msg.textContent = "New password: at least 8 characters.";
                    }
                    return;
                }
                if (n1 !== n2) {
                    if (msg) {
                        msg.style.color = "#ff8e8e";
                        msg.textContent = "New passwords do not match.";
                    }
                    return;
                }
                try {
                    const st = beVaultReadStored();
                    if (!st) throw new Error("no vault");
                    await beVaultDecrypt(oldPw, st);
                    beVaultSession.password = n1;
                    await beVaultPersist();
                    if (isVaultRememberDevice()) saveVaultRememberMaster(n1);
                    const o = document.getElementById("beVaultOldMp");
                    const a = document.getElementById("beVaultNewMpCh1");
                    const b = document.getElementById("beVaultNewMpCh2");
                    if (o) o.value = "";
                    if (a) a.value = "";
                    if (b) b.value = "";
                    if (msg) {
                        msg.style.color = "#9cffb0";
                        msg.textContent = "Master password updated.";
                        setTimeout(() => {
                            const m = msgEl();
                            if (m) m.textContent = "";
                        }, 3200);
                    }
                    beVaultTouchActivity();
                    renderSocialPanel();
                } catch {
                    if (msg) {
                        msg.style.color = "#ff8e8e";
                        msg.textContent = "Current password wrong or save failed.";
                    }
                }
            }
        };

        void run();
    });

    sv.addEventListener("change", (e) => {
        const inp = e.target;
        if (!(inp instanceof HTMLInputElement) || inp.id !== "beVaultImportFile") return;
        const f = inp.files?.[0];
        if (!f) return;
        const reader = new FileReader();
        reader.onload = () => {
            const msg = document.getElementById("beVaultMsg");
            try {
                const text = String(reader.result || "");
                const o = JSON.parse(text);
                const inner = o.payload !== undefined && o.payload !== null ? o.payload : o;
                if (typeof inner !== "object" || inner === null) throw new Error("bad payload");
                localStorage.setItem(BE_VAULT_STORAGE_KEY, JSON.stringify(inner));
                beVaultLock();
                if (msg) {
                    msg.style.color = "#9cffb0";
                    msg.textContent = "Imported — unlock with your master password.";
                }
                renderSocialPanel();
            } catch (ex) {
                BE_LOG.error("vault import", ex);
                const m2 = document.getElementById("beVaultMsg");
                if (m2) {
                    m2.style.color = "#ff8e8e";
                    m2.textContent = "Import failed — not a valid backup.";
                }
            }
        };
        reader.readAsText(f);
        inp.value = "";
    });
})();

// 🔥 bind safely AFTER UI exists
const items = div.querySelectorAll(".tracker-sidebar .sidebar-item");

items.forEach(item => {
    item.addEventListener("click", () => {
        const tab = item.dataset.tab;
        switchTab(tab);
    });
});
const widgetCards = widgetHub.querySelectorAll(".be-widget-card[data-open-tab]");
widgetCards.forEach(btn => {
    btn.addEventListener("click", () => {
        const tab = btn.getAttribute("data-open-tab");
        setWidgetHubVisibility(false);
        setOverlayVisibility(true);
        if (tab) switchTab(tab);
    });
});
        document.addEventListener("click", (e) => {
    if (!sidebar.contains(e.target) && !menuBtn.contains(e.target)) {
        sidebar.classList.remove("open");
    }
});

dragHandle.addEventListener("mousedown", (e) => {
    if (e.button === 2) {
        e.preventDefault();
        return;
    }
    if (e.button !== 0) return;

    isDragging = true;

    div.style.cursor = "grabbing";

    div.style.right = "auto";
    const r = div.getBoundingClientRect();
    offsetX = e.clientX - r.left;
    offsetY = e.clientY - r.top;
    document.body.style.userSelect = "none";
});

        document.addEventListener("mousemove", (e) => {
            if (!isDragging) return;
            div.style.right = "auto";
            let left = e.clientX - offsetX;
            let top = e.clientY - offsetY;
            const vw = window.innerWidth;
            const vh = window.innerHeight;
            const pad = BE_OVERLAY_DRAG_PAD;
            const peek = BE_OVERLAY_EDGE_PEEK;
            const maxL = Math.max(pad, vw - peek);
            const maxT = Math.max(pad, vh - peek);
            left = Math.min(Math.max(pad, left), maxL);
            top = Math.min(Math.max(pad, top), maxT);
            div.style.left = left + "px";
            div.style.top = top + "px";
        });

document.addEventListener("mouseup", (e) => {
    if (e.button !== 0) return;

    isDragging = false;
    div.style.cursor = "";
    document.body.style.userSelect = "";

    // 🔥 save position after drag
    if (div.style.left && div.style.top) {
        localStorage.setItem("bonk_ui_pos", JSON.stringify({
            left: div.style.left,
            top: div.style.top
        }));
    }
});
        // 🔥 ensure one valid active tab at start
setTimeout(() => {
    const active = div.querySelector(".sidebar-item.active");

    if (active) {
        switchTab(active.dataset.tab);
    } else {
        switchTab("dashboard");
    }
}, 0);
        // 🔥 RESIZE SYSTEM
const resizeHandle = div.querySelector("#resizeHandle");
const card = UI.card;

let isResizing = false;

resizeHandle.addEventListener("mousedown", (e) => {
    if (!card) return;
    e.stopPropagation(); // prevent drag conflict
    isResizing = true;
    document.body.style.userSelect = "none";
});

document.addEventListener("mousemove", (e) => {
    if (!isResizing || !card) return;

    const minWidth = 200;
    const minHeight = 140;

    const rect = div.getBoundingClientRect();

    const newWidth = Math.max(minWidth, e.clientX - rect.left);
    const newHeight = Math.max(minHeight, e.clientY - rect.top);

    card.style.width = newWidth + "px";
    card.style.height = newHeight + "px";
});

document.addEventListener("mouseup", () => {
    if (isResizing) {
        isResizing = false;
        document.body.style.userSelect = "";
        /* After shrinking the card, Chromium can leave scrollHeight/clientHeight stale — reflow + clamp scrollTop */
        requestAnimationFrame(() => {
            requestAnimationFrame(() => {
                const v = UI.root?.querySelector(".tracker-view.active");
                if (!v) return;
                void v.offsetHeight;
                const maxScroll = Math.max(0, v.scrollHeight - v.clientHeight);
                if (v.scrollTop > maxScroll) v.scrollTop = maxScroll;
            });
        });
    }
});
    }
    function applyDashboardStatVisibility() {
        const map = [
            ["xp", "rowXp"],
            ["wins", "rowWins"],
            ["rate", "rowRate"],
            ["lastWin", "rowLastWin"]
        ];
        map.forEach(([key, id]) => {
            const row = document.getElementById(id);
            if (!row) return;
            row.classList.remove("dashboard-stat-hidden");
        });
    }

    function getOverviewSlotCount() {
        if (uiSettings.size === "tiny") return 3;
        if (uiSettings.size === "large") return 6;
        return 4;
    }

    function getOverviewSlotsForCurrentSize() {
        const size = uiSettings.size === "tiny" || uiSettings.size === "large" ? uiSettings.size : "medium";
        const defaults = {
            tiny: ["wins", "rate", "lastWin"],
            medium: ["xp", "wins", "rate", "lastWin"],
            large: ["xp", "wins", "rate", "lastWin", "session", "level"]
        };
        const count = getOverviewSlotCount();
        const slots = Array.isArray(uiSettings.overviewSlots?.[size]) ? uiSettings.overviewSlots[size].slice(0, count) : defaults[size].slice(0, count);
        while (slots.length < count) slots.push(defaults[size][slots.length] || "wins");
        return slots;
    }

    function applyOverviewLayout() {
        const rowMap = {
            xp: document.getElementById("rowXp"),
            wins: document.getElementById("rowWins"),
            rate: document.getElementById("rowRate"),
            lastWin: document.getElementById("rowLastWin"),
            session: document.getElementById("rowSession"),
            level: document.getElementById("rowLevel")
        };
        const parent = document.getElementById("dashboardStatsRows");
        if (!parent) return;
        Object.values(rowMap).forEach((el) => {
            if (el) el.classList.add("dashboard-stat-hidden");
        });
        const slots = getOverviewSlotsForCurrentSize();
        slots.forEach((key) => {
            const row = rowMap[key];
            if (!row) return;
            row.classList.remove("dashboard-stat-hidden");
            parent.appendChild(row);
        });
    }

    function syncOverviewSlotControls() {
        const container = document.getElementById("beOverviewSlots");
        const sizeHint = document.getElementById("beOverviewSizeHint");
        if (!container) return;
        const selects = container.querySelectorAll("select[data-slot-idx]");
        const slotCount = getOverviewSlotCount();
        const sizeLabel = uiSettings.size === "tiny" ? "Tiny" : (uiSettings.size === "large" ? "Large" : "Medium");
        if (sizeHint) sizeHint.textContent = `Current size: ${sizeLabel} (${slotCount} slots)`;
        const activeSlots = getOverviewSlotsForCurrentSize();
        selects.forEach((sel) => {
            const idx = Number(sel.getAttribute("data-slot-idx") || "0");
            const row = sel.closest(".settings-row");
            if (row) row.style.display = idx < slotCount ? "flex" : "none";
            sel.value = activeSlots[idx] !== undefined ? activeSlots[idx] : "wins";
        });
    }

    function buildRecentDateKeys(days) {
        const out = [];
        for (let i = days - 1; i >= 0; i--) {
            const d = new Date();
            d.setDate(d.getDate() - i);
            out.push(d.toISOString().slice(0, 10));
        }
        return out;
    }

    function getISOWeekString(date) {
        const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));
        d.setUTCDate(d.getUTCDate() + 4 - (d.getUTCDay() || 7));
        const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
        const weekNo = Math.ceil((((d - yearStart) / 86400000) + 1) / 7);
        return `${d.getUTCFullYear()}-W${String(weekNo).padStart(2, "0")}`;
    }

    function parseISOWeekToMonday(weekValue) {
        const match = /^(\d{4})-W(\d{2})$/.exec(weekValue || "");
        if (!match) return null;
        const year = Number(match[1]);
        const week = Number(match[2]);
        const jan4 = new Date(Date.UTC(year, 0, 4));
        const jan4Day = jan4.getUTCDay() || 7;
        const monday = new Date(jan4);
        monday.setUTCDate(jan4.getUTCDate() - jan4Day + 1 + (week - 1) * 7);
        return new Date(monday.getUTCFullYear(), monday.getUTCMonth(), monday.getUTCDate());
    }

    function buildWeekDateKeys(weekValue) {
        const monday = parseISOWeekToMonday(weekValue) || (() => {
            const now = new Date();
            const day = now.getDay() || 7;
            const m = new Date(now);
            m.setDate(now.getDate() - day + 1);
            return m;
        })();
        const out = [];
        for (let i = 0; i < 7; i++) {
            const d = new Date(monday);
            d.setDate(monday.getDate() + i);
            out.push(d.toISOString().slice(0, 10));
        }
        return out;
    }

    function updateStatsStorage(data, key, xpToday, wins) {
        data._dailyHistory = data._dailyHistory || {};
        data._dailyHistory[key] = {
            xp: Math.max(0, Math.floor(xpToday || 0)),
            wins: Math.max(0, Math.floor(wins || 0))
        };
        data._sessions = Array.isArray(data._sessions) ? data._sessions : [];
    }

    function maybeSaveSession(data) {
        if (!data) return;
        data._sessions = Array.isArray(data._sessions) ? data._sessions : [];
        const now = Date.now();
        const durationSec = Math.floor((now - sessionStart) / 1000);
        if (durationSec < 20) return;

        const tKey = Storage.getTodayKey();
        const currXP = Number(data?.[tKey]?.xp || data?._globalXP || 0);
        const rawB = data?.[tKey]?.baselineXP;
        const baselineXP = rawB == null ? currXP : Number(rawB);
        const currWins = Math.max(0, Math.floor((currXP - baselineXP) / 100));
        const gainedXP = Math.max(0, currXP - sessionStartXP);
        const gainedWins = Math.max(0, currWins - sessionStartWins);
        if (gainedXP === 0 && gainedWins === 0) return;

        data._sessions.unshift({
            at: new Date(now).toISOString(),
            durationSec,
            gainedXP,
            gainedWins
        });
        data._sessions = data._sessions.slice(0, 10);
    }

    function renderStatsTabFromData(data) {
        const chart = document.getElementById("beStatsChart");
        const legend = document.getElementById("beStatsLegend");
        const history = document.getElementById("beSessionHistory");
        if (!chart || !legend || !history) return;

        const selectedWeek = uiSettings.graphWeek || getISOWeekString(new Date());
        if (uiSettings.graphWeek !== selectedWeek) {
            uiSettings.graphWeek = selectedWeek;
            saveUISettings();
        }
        const weekInput = document.getElementById("beGraphWeekSelect");
        if (weekInput && weekInput.value !== selectedWeek) weekInput.value = selectedWeek;

        const keys = buildWeekDateKeys(selectedWeek);
        const daily = data?._dailyHistory || {};
        const points = keys.map((k) => ({ key: k, xp: daily[k]?.xp || 0, wins: daily[k]?.wins || 0 }));
        const maxXP = Math.max(1, ...points.map((p) => p.xp));
        const maxWins = Math.max(1, ...points.map((p) => p.wins));

        chart.innerHTML = "";
        points.forEach((p) => {
            const xpBar = document.createElement("div");
            xpBar.className = "stats-bar";
            xpBar.style.height = `${Math.max(4, Math.round((p.xp / maxXP) * 100))}%`;
            xpBar.title = `${p.key} XP: ${p.xp}`;
            chart.appendChild(xpBar);

            const winsBar = document.createElement("div");
            winsBar.className = "stats-bar wins";
            winsBar.style.height = `${Math.max(3, Math.round((p.wins / maxWins) * 75))}%`;
            winsBar.title = `${p.key} Wins: ${p.wins}`;
            chart.appendChild(winsBar);
        });

        const totalXP = points.reduce((n, p) => n + p.xp, 0);
        const totalWins = points.reduce((n, p) => n + p.wins, 0);
        legend.textContent = `Week ${selectedWeek}: XP ${totalXP.toLocaleString()} | Wins ${totalWins}`;

        const sessions = Array.isArray(data?._sessions) ? data._sessions : [];
        if (!sessions.length) {
            history.innerHTML = `<div class="stats-history-item"><span>No sessions yet</span><span>--</span></div>`;
            return;
        }
        history.innerHTML = sessions.slice(0, 10).map((s) => {
            const date = new Date(s.at);
            const stamp = `${date.toLocaleDateString()} ${date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" })}`;
            const mins = Math.max(1, Math.round((s.durationSec || 0) / 60));
            return `<div class="stats-history-item"><span>${stamp}</span><span>+${s.gainedWins}w / ${mins}m</span></div>`;
        }).join("");
    }

    function renderStatsOverview(data) {
        const wrap = document.getElementById("beStatsOverview");
        if (!wrap) return;
        const todayKey = Storage.getTodayKey();
        const today = data?.[todayKey] || {};
        const xp = Number(today.xp || data?._globalXP || 0);
        const rawStatsB = today.baselineXP;
        const baselineXP = rawStatsB == null ? xp : Number(rawStatsB);
        const xpToday = Math.max(0, xp - baselineXP);
        const wins = Math.max(Number(today.wins || 0), Math.floor(xpToday / 100));
        const level = getLevelFromXP(xp);
        const sessionSecs = Math.floor((Date.now() - sessionStart) / 1000);
        const rate = sessionSecs > 30 ? ((wins / (sessionSecs / 60)) * 60).toFixed(1) : "0.0";
        const rows = [
            ["Total XP", xp.toLocaleString()],
            ["XP Today", xpToday.toLocaleString()],
            ["Wins Today", String(wins)],
            ["Level", String(level)],
            ["Session", formatTime(sessionSecs * 1000)],
            ["Rate", `${rate} w/h`],
            ["Last Win", lastXpChangeTime ? `${Math.floor((Date.now() - lastXpChangeTime) / 60000)}m ago` : "--"]
        ];
        wrap.innerHTML = rows.map(([k, v]) => `<div class="stats-history-item"><span>${k}</span><span>${v}</span></div>`).join("");
    }

    function formatTime(ms) {
        const s = Math.floor(ms / 1000);
        const m = Math.floor(s / 60);
        const sec = s % 60;
        return `${m}:${sec.toString().padStart(2, '0')}`;
    }

    /** Names longer than this use an 8ch-wide viewport + marquee */
    const TRACKER_NAME_MAX_CHARS = 8;

    function applyTrackerPlayerName(nameEl, player) {
        if (!nameEl) return;
        let inner = nameEl.querySelector(".tracker-name-text");
        if (!inner) {
            inner = document.createElement("span");
            inner.className = "tracker-name-text";
            nameEl.textContent = "";
            nameEl.appendChild(inner);
        }
        const text = String(player ?? "");
        inner.textContent = text;
        nameEl.title = text;

        const long = text.length > TRACKER_NAME_MAX_CHARS;
        if (long) {
            nameEl.classList.add("tracker-name--scroll");
        } else {
            nameEl.classList.remove("tracker-name--scroll");
            nameEl.style.removeProperty("--be-name-scroll");
            nameEl.style.removeProperty("--be-name-marquee-duration");
            return;
        }

        const measure = () => {
            if (!nameEl.classList.contains("tracker-name--scroll")) return;
            const w = nameEl.clientWidth;
            const sw = inner.scrollWidth;
            const dist = Math.max(0, sw - w);
            nameEl.style.setProperty("--be-name-scroll", `${dist}px`);
            const sec = Math.min(24, Math.max(6, 5 + dist / 25));
            nameEl.style.setProperty("--be-name-marquee-duration", `${sec}s`);
        };

        requestAnimationFrame(() => requestAnimationFrame(measure));

        if (!nameEl.dataset.beNameResizeObs) {
            nameEl.dataset.beNameResizeObs = "1";
            if (typeof ResizeObserver !== "undefined") {
                const ro = new ResizeObserver(() => requestAnimationFrame(measure));
                ro.observe(nameEl);
            }
        }
    }

    function updateUI() {

        lastRender = Date.now();

        const player = getPlayerKey();
const { data, key, storageKey } = Storage.load();

let xp = data[key].xp;

if (!xp && data._globalXP) {
    xp = data._globalXP; // 🔥 hard fallback
}
if (data[key].baselineXP == null) {
    const seededWins = Number(data[key].wins || 0);
    const priorXp = Math.max(0, Number(data[key].xp || 0));
    if (priorXp <= 0) {
        data[key].baselineXP = 0;
    } else {
        data[key].baselineXP = Math.max(0, priorXp - seededWins * 100);
    }
}
const xpToday = (() => {
    const b = data[key].baselineXP;
    return Math.max(0, xp - (b == null ? xp : b));
})();
const winsFromXP = Math.max(0, Math.floor(xpToday / 100));
const wins = Math.max(Number(data[key].wins || 0), winsFromXP);
if (wins !== Number(data[key].wins || 0)) {
    data[key].wins = wins;
}
const level = getLevelFromXP(xp);


// 🔥 detect login/logout change
if (player !== currentPlayer) {
    maybeSaveSession(data);
    Storage.save(data, storageKey);

    currentPlayer = player;

    // 🔥 reset storage cache
    Storage.invalidate();

    // 🔥 reset state
    lastUIState = {};
    hasRenderedOnce = false;

    sessionStart = Date.now();
    sessionStartXP = xp || 0;
    sessionStartWins = wins || 0;
    previousWins = wins || 0;
    noXpCount = 0;
    lastXpChangeTime = 0;
    beFarmEverOnThisSession = false;

    // 🔥 reset socket
    bonkWSS = null;

    // 🔥 UI refresh (with fade)
    UI.root.classList.remove("tracker-visible");
    UI.root.classList.add("tracker-fade");

    setTimeout(() => {
        UI.root.classList.remove("tracker-fade");
        UI.root.classList.add("tracker-visible");

        Events.emit("xpUpdate");
    }, 200);

    return;
}
    // optional: force fresh structure
if (!data._globalXP) {
    data._globalXP = 0;
    Storage.save(data, storageKey);
}
///
updateStatsStorage(data, key, xpToday, wins);
Storage.save(data, storageKey);
renderStatsTabFromData(data);
renderStatsOverview(data);
        UI.xpGain.innerText =
    "+" + xp.toLocaleString();

const isGuest = !xp; // 🔥 guest = no XP
        if (!isGuest && player && player !== "Guest") {
            recordCurrentAltSnapshot(player, level, xp);
        }
        UI.winsLine.innerText =
    `${wins} / ${DAILY_CAP}`;

        const sessionTime = Date.now() - sessionStart;
        UI.sessionLine.innerText =
    "⏱ " + formatTime(sessionTime);
if (UI.sessionStatLine) UI.sessionStatLine.innerText = formatTime(sessionTime);

       const minutes = sessionTime / 60000;

// 🔥 rate based on DAILY wins
let rate = minutes > 0.5 ? (wins / minutes) * 60 : 0;

UI.rateLine.innerText =
    rate.toFixed(1) + " w/h";

       const safeLevel = Math.max(1, level);

        const currentLevelXP = getXPForLevel(safeLevel);
        const nextLevelXP = getXPForLevel(safeLevel + 1);

        const xpNeeded = nextLevelXP - currentLevelXP;

const xpIntoLevel = Math.min(
    xpNeeded,
    Math.max(0, xp - currentLevelXP)
);
        lastUIState = { xp, wins, level, player };
hasRenderedOnce = true;

        const progress = xpNeeded > 0 ? (xpIntoLevel / xpNeeded) * 100 : 0;

        UI.xpPercent.innerText =
    Math.floor(progress) + "%";

        const nameEl = UI.playerName;
        // 🔥 SET PLAYER SKIN
if (UI.playerSkin) {
    if (!topBarEl) {
    topBarEl = document.getElementById("pretty_top_bar");
}
const topBar = topBarEl;

    if (topBar) {
        const skinElement = topBar.querySelector("img");

        if (skinElement) {
            const src = skinElement.src;

            // 🔥 ONLY update if changed
            if (src !== lastSkinSrc) {
                lastSkinSrc = src;

                const clone = skinElement.cloneNode(true);

                clone.style.width = "100%";
                clone.style.height = "100%";
                clone.style.display = "block";
                clone.style.margin = "0";

                UI.playerSkin.innerHTML = "";
                UI.playerSkin.appendChild(clone);
            }
        }
    }
}

if (nameEl) {
    applyTrackerPlayerName(nameEl, player);
}

if (isGuest) {
    UI.levelLine.innerText = ""; // 🔥 hide completely
} else {
    UI.levelLine.innerText = `Level ${level}`;
}
if (UI.levelStatLine) UI.levelStatLine.innerText = String(level);

        const circle = UI.xpCircle;

const offset = CIRCLE_CIRCUMFERENCE - (progress / 100) * CIRCLE_CIRCUMFERENCE;

circle.style.strokeDashoffset = offset;

        const xpTextEl = UI.xpText;
if (xpTextEl) {
    const winsIntoLevel = Math.floor(xpIntoLevel / 100);
    const winsNeeded = Math.floor(xpNeeded / 100);
    const winsLeft = winsNeeded - winsIntoLevel;

    xpTextEl.innerText = `${winsLeft} wins left`;
    xpTextEl.title = `${winsIntoLevel} / ${winsNeeded} wins to next level`;
}
        if (UI.xpCircleWrapper) {
    const winsIntoLevel = Math.floor(xpIntoLevel / 100);
    const winsNeeded = Math.floor(xpNeeded / 100);
    UI.xpCircleWrapper.title = `${winsIntoLevel} / ${winsNeeded} wins to next level`;
}
        // last win time
if (UI.lastWinLine) {
    if (lastXpChangeTime === 0) {
        UI.lastWinLine.innerText = "--";
    } else {
        const minAgo = Math.floor((Date.now() - lastXpChangeTime) / 60000);
        UI.lastWinLine.innerText = minAgo === 0 ? "just now" : `${minAgo}m ago`;
    }
}

// daily progress bar
if (UI.dailyProgressBar) {
    UI.dailyProgressBar.style.width = Math.min(100, (wins / DAILY_CAP) * 100) + "%";
    if (wins > previousWins) {
        UI.dailyProgressBar.classList.remove("be-gain-flash");
        void UI.dailyProgressBar.offsetWidth;
        UI.dailyProgressBar.classList.add("be-gain-flash");
    }
}
previousWins = wins;

const statusEl = UI.statusText;

// CAPPED border + label only after today’s wins reach the daily cap (see DAILY_CAP). Not tied to bonk_xpStopped.
const isCapped = wins >= DAILY_CAP;

if (isCapped) {
    if (statusEl) {
        statusEl.style.display = "";
        statusEl.innerHTML = "⛔ CAPPED";
        statusEl.style.color = "#ff4c4c";
    }
    setTrackerBorderByStatus("capped");
} else if (xpEnabled) {
    if (statusEl) {
        statusEl.style.display = "";
        statusEl.innerHTML = `<span class="pulse-dot"></span>FARMING`;
        statusEl.style.color = "#4caf50";
    }
    setTrackerBorderByStatus("farming");
} else {
    if (statusEl) {
        if (beFarmEverOnThisSession) {
            statusEl.style.display = "";
            statusEl.innerHTML = "⏸ IDLE";
            statusEl.style.color = "#aaa";
        } else {
            statusEl.innerHTML = "";
            statusEl.style.display = "none";
        }
    }
    setTrackerBorderByStatus("idle");
}
}

function updateSessionOnly() {
    const sessionTime = Date.now() - sessionStart;
    UI.sessionLine.innerText = "⏱ " + formatTime(sessionTime);
    if (UI.sessionStatLine) UI.sessionStatLine.innerText = formatTime(sessionTime);

    if (UI.lastWinLine && lastXpChangeTime > 0) {
        const minAgo = Math.floor((Date.now() - lastXpChangeTime) / 60000);
        UI.lastWinLine.innerText = minAgo === 0 ? "just now" : `${minAgo}m ago`;
    }
}

function setTrackerBorderByStatus(status) {
    if (!UI.root) return;
    const card = UI.root.querySelector(".tracker-card");
    if (!card) return;

    let border;
    let glow;
    if (status === "capped") {
        border = "rgba(255, 76, 76, 0.9)";
        glow = "0 0 14px rgba(255, 76, 76, 0.25)";
    } else if (status === "farming") {
        border = "rgba(76, 175, 80, 0.9)";
        glow = "0 0 14px rgba(76, 175, 80, 0.25)";
    } else {
        border = "rgba(255, 170, 0, 0.9)";
        glow = "0 0 14px rgba(255, 170, 0, 0.2)";
    }
    card.style.borderColor = border;
    card.style.boxShadow = glow;

    const dot = document.querySelector("#pretty_top_be_launcher .be-top-launcher-dot");
    if (dot) dot.style.background = border;
}

function getXPForLevel(n) {
    return (n - 1) * (n - 1) * 100;
}

/** Prefer Bonk / engine.io sockets over analytics or other extras (Brave often opens several). */
function beXpSocketUrlScore(urlStr) {
    const s = String(urlStr || "").toLowerCase();
    if (s.includes("peerjs")) return -1;
    if (s.includes("bonk") && (s.includes("socket") || s.includes("engine"))) return 100;
    if (s.includes("bonk.io")) return 90;
    if (s.includes("socket.io") || s.includes("engine.io")) return 50;
    return 5;
}

function beMaybePreferBonkWsForPoll(ws) {
    if (!ws) return;
    const u = typeof ws.url === "string" ? ws.url : "";
    if (beXpSocketUrlScore(u) < 0) return;
    const cur = bonkWSS;
    const curOpen = cur && cur.readyState === WebSocket.OPEN;
    const curSc = curOpen ? beXpSocketUrlScore(cur.url) : -100;
    const sc = beXpSocketUrlScore(u);
    if (!curOpen || sc >= curSc) {
        bonkWSS = ws;
    }
}

const originalSend = WebSocket.prototype.send;

WebSocket.prototype.send = function (...args) {
    const u = typeof this.url === "string" ? this.url : "";
    if (!u.includes("peerjs")) {
        if (!this._xpHooked) {
            this._xpHooked = true;
            this.addEventListener("message", (event) => {
                XPEngine.handleMessage(event);
            });
        }
        beMaybePreferBonkWsForPoll(this);
    }

    return originalSend.apply(this, args);
};
    function getLevelFromXP(xp) {
    return Math.floor(Math.sqrt(xp / 100)) + 1;
}
function init() {
   if (!BE_CONFIG.featureFlags.enableTracker) {
    markHealth("tracker.init", false, "feature flag disabled");
    return;
   }

   beRegisterFarmApi();

   safeRun("tracker.legacyThemeBridgeCleanup", () => {
        try {
            document.getElementById("be-bonk-excigma-theme-bridge")?.remove();
        } catch {}
        try {
            const fr = document.getElementById("maingameframe");
            const ocd = fr && fr.contentDocument;
            if (ocd) {
                ocd.getElementById("be-bonk-excigma-theme-bridge")?.remove();
                ocd.getElementById("be-bonk-excigma-theme-bridge-outer")?.remove();
            }
        } catch {}
    });

   void (async () => {
   try {
   await UIEngine.init();
   markHealth("tracker.uiInit", true, "createUI awaited");
   } catch (e) {
   markHealth("tracker.uiInit", false, e?.message || "failed");
   BE_LOG.error("tracker.uiInit", e);
   }

    waitForPlayer(() => {
    markHealth("tracker.playerReady", true, "player element ready");
    // 🔥 INSTANT render using cached data
const { data, key } = Storage.load();

// 🔥 FORCE instant XP from cache
if (data._globalXP && !data[key].xp) {
    data[key].xp = data._globalXP;
}
const initialXP = data[key].xp || data._globalXP || 0;
const _ib = data[key].baselineXP;
const initialBaseline = _ib == null ? initialXP : _ib;
sessionStartXP = initialXP;
sessionStartWins = Math.max(0, Math.floor((initialXP - initialBaseline) / 100));
previousWins = sessionStartWins;

lastUIState = {};
safeRun("tracker.firstRender", () => updateUI());
        // 🔥 force a second render after everything stabilizes
setTimeout(() => {
    lastUIState = {};
    safeRun("tracker.secondRenderEmit", () => Events.emit("xpUpdate"));
}, 300);

    waitForSocket(() => {
        markHealth("tracker.socketReady", true, "socket open");
        let attempts = 0;
        // 🔥 wake up server (minimal activity)
setTimeout(() => {
    try {
        if (bonkWSS && bonkWSS.readyState === WebSocket.OPEN && !beIsBonkRoomListVisible()) {
            bonkWSS.send("42[0]");
            bonkWSS.send("42[38]");
            setTimeout(() => {
                try {
                    if (bonkWSS && bonkWSS.readyState === WebSocket.OPEN && !beIsBonkRoomListVisible()) {
                        bonkWSS.send("42[38]");
                    }
                } catch {}
            }, 300);
        }
    } catch {}
}, 500);

    const trySync = () => {
    if (!bonkWSS || bonkWSS.readyState !== WebSocket.OPEN) {
        setTimeout(trySync, beBackoffMs(300, 4));
        return;
    }
        attempts++;

        beRequestOneXpPoll();

        const { data, key } = Storage.load();
const synced = data[key]?._synced;

if (synced) {
    lastUIState = {};
    safeRun("tracker.syncRenderEmit", () => Events.emit("xpUpdate"));
    return;
}

        setTimeout(trySync, beBackoffMs(1000, 3));
    };

    trySync();
});
});

    // lightweight timer — slower while tab is hidden
    let __beSessionTicker = null;
    function beRestartSessionTicker() {
        if (__beSessionTicker) {
            try {
                clearInterval(__beSessionTicker);
            } catch {}
            __beSessionTicker = null;
        }
        const ms = beTabHidden() ? 5000 : 1000;
        __beSessionTicker = setInterval(() => UIEngine.updateSession(), ms);
    }
    if (!window.__beSessionVisHooked) {
        window.__beSessionVisHooked = true;
        document.addEventListener("visibilitychange", () => beRestartSessionTicker());
    }
    beRestartSessionTicker();
    markHealth("tracker.sessionTicker", true, "active");

    // real updates when XP changes
    Events.on("xpUpdate", UIEngine.render);
    markHealth("tracker.xpEventBinding", true, "bound");
    // 🔥 observe login/name changes (replaces 2s polling)
let nameEl = document.getElementById("pretty_top_name");

if (!nameEl) {
    setTimeout(() => {
        nameEl = document.getElementById("pretty_top_name");
        if (!nameEl) return;

        const observer = new MutationObserver(() => {
            const newPlayer = getPlayerKey();
            if (newPlayer !== currentPlayer) Events.emit("xpUpdate");
        });

        observer.observe(nameEl, {
            childList: true,
            characterData: true,
            subtree: true
        });
    }, 2000);
} else {
    const observer = new MutationObserver(() => {
        const newPlayer = getPlayerKey();
        if (newPlayer !== currentPlayer) Events.emit("xpUpdate");
    });

    observer.observe(nameEl, {
        childList: true,
        characterData: true,
        subtree: true
    });
}

    safeRun("tracker.loginPwGuard", () => {
        function hardenIfPresent() {
            const u = document.getElementById("loginwindow_username");
            const p = document.getElementById("loginwindow_password");
            if (u && p) beHardenBonkLoginFields(u, p);
        }
        hardenIfPresent();
        if (document.body) {
            const mo = new MutationObserver(hardenIfPresent);
            mo.observe(document.body, { childList: true, subtree: true });
        }
    });
   })();
}

    window.addEventListener("load", () => {
        markHealth("tracker.windowLoad", true, "loaded");
        setTimeout(() => safeRun("tracker.init", init), 3000);
        // 🔥 ultra-light fallback (only if UI hasn't rendered in a long time)
        let __beFallbackTicker = null;
        function beRestartFallbackTicker() {
            if (__beFallbackTicker) {
                try {
                    clearInterval(__beFallbackTicker);
                } catch {}
                __beFallbackTicker = null;
            }
            const ms = beTabHidden() ? 40000 : 10000;
            __beFallbackTicker = setInterval(() => {
                if (Date.now() - lastRender > 20000) {
                    lastUIState = {};
                    safeRun("tracker.fallbackRenderEmit", () => Events.emit("xpUpdate"));
                }
            }, ms);
        }
        if (!window.__beFallbackVisHooked) {
            window.__beFallbackVisHooked = true;
            document.addEventListener("visibilitychange", () => beRestartFallbackTicker());
        }
        beRestartFallbackTicker();

    });
window.addEventListener("beforeunload", () => {
    try {
        const cache = Storage.load();
        if (!cache) return;
        maybeSaveSession(cache.data);
        Storage.save(cache.data, cache.storageKey);
    } catch {}
});
document.addEventListener("keydown", (e) => {
    if (!UI.root) return;
    if (isCapturingHotkey) return;
    if (e.repeat) return;

    const target = e.target;
    const tag = target?.tagName?.toLowerCase?.() || "";
    const isEditable = tag === "input" || tag === "textarea" || target?.isContentEditable;
    if (isEditable) return;

    if (
        e.altKey &&
        !e.ctrlKey &&
        !e.metaKey &&
        !e.shiftKey &&
        !e.repeat &&
        UI.root &&
        UI.root.style.display !== "none" &&
        UI.root.classList.contains("be-overlay-open") &&
        typeof UI.switchTab === "function"
    ) {
        const k = e.key;
        if (k >= "1" && k <= "7") {
            const tabs = [
                "dashboard",
                "graph",
                "statsOverview",
                "calculator",
                "social",
                "visuals",
                "settings"
            ];
            e.preventDefault();
            UI.switchTab(tabs[parseInt(k, 10) - 1]);
            return;
        }
    }

    if (e.ctrlKey || e.metaKey || e.altKey) return;

    if (e.key === "Escape") {
        if (UI.widgetHub && UI.widgetHub.style.display !== "none") {
            e.preventDefault();
            setWidgetHubVisibility(false);
            return;
        }
        if (UI.root.style.display !== "none" && UI.root.classList.contains("be-overlay-open")) {
            e.preventDefault();
            setOverlayVisibility(false);
            return;
        }
    }

    if (e.key === uiSettings.widgetHotkey) {
        e.preventDefault();
        toggleWidgetHub();
        return;
    }

    if (e.key === uiSettings.overlayHotkey) {
        e.preventDefault();
        toggleOverlayVisibility();
    }
}, true);
    if (!window.bonkCodeInjectors) window.bonkCodeInjectors = [];
window.bonkCodeInjectors.push(bonkCode => {
    let newSrc = bonkCode;

    let discID = newSrc.match(/this\.discGraphics\[([\w$]{2,4})\]=null;\}/)[1];
    newSrc = newSrc.replace(`this.discGraphics[${discID}]=null;}`, `this.discGraphics[${discID}]=null;} else {
        if(this.discGraphics[${discID}]){
            const _v = window.BonkVisuals;
            if(this.discGraphics[${discID}].sfwSkin){
                this.discGraphics[${discID}].playerGraphic.alpha = _v.players.skins ? 1 : 0;
                this.discGraphics[${discID}].sfwSkin.visible = !_v.players.skins;
            } else if(this.discGraphics[${discID}]?.avatar?.bc != undefined){
                this.discGraphics[${discID}].sfwSkin = new PIXI.Graphics;
                this.discGraphics[${discID}].sfwSkin.beginFill(this.discGraphics[${discID}].teamify(this.discGraphics[${discID}].avatar.bc, this.discGraphics[${discID}].team));
                this.discGraphics[${discID}].sfwSkin.drawCircle(0,0,this.discGraphics[${discID}].radius);
                this.discGraphics[${discID}].sfwSkin.endFill();
                this.discGraphics[${discID}].container.addChildAt(this.discGraphics[${discID}].sfwSkin, 3);
            }
            this.discGraphics[${discID}].nameText.alpha = _v.players.usernames.visible ? _v.players.usernames.alpha : 0;
            var _beFn=window.BonkEnhanced&&window.BonkEnhanced.friendNamesLower;
            if(_beFn&&_beFn.length&&this.discGraphics[${discID}].nameText){
                var _beT=this.discGraphics[${discID}].nameText;
                var _beN=(_beT.text!==undefined&&_beT.text!==null?String(_beT.text):"").trim().toLowerCase();
                if(_beN&&_beFn.indexOf(_beN)>=0){
                    _beT.tint=16766720;
                }
            }
            if(this.discGraphics[${discID}].playerID != this.localPlayerID){
                this.discGraphics[${discID}].container.visible = _v.players.visible;
                this.discGraphics[${discID}].container.alpha = _v.players.alpha;
            }
        }
    }`);

    let buildRendererFunction = newSrc.match(/(build\([\w$]{2,4},[\w$]{2,4}\)) \{.{30,150}=new [\w$]{2,4}\[[0-9]+\]\(/)[1];
    newSrc = newSrc.replace(`${buildRendererFunction} {`, `${buildRendererFunction} {
        window.BonkVisuals.chatWindow = document.querySelector('#ingamechatbox');
    `);

    return newSrc;
});
})();