Greasy Fork

Greasy Fork is available in English.

BEST Cheat for Chess.com (Stockfish 18.0.5, 18.0.0, 17.1.0, No Anti-Ban)

An extremely advanced Chess.com cheat menu with three powerful Stockfish models, two online, one offline, and countless customization options.

当前为 2026-03-23 提交的版本,查看 最新版本

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name          BEST Cheat for Chess.com (Stockfish 18.0.5, 18.0.0, 17.1.0, No Anti-Ban)
// @namespace     http://tampermonkey.net/
// @version       9.0.3
// @description   An extremely advanced Chess.com cheat menu with three powerful Stockfish models, two online, one offline, and countless customization options.
// @author        Ech0
// @copyright     2025, Ech0
// @license       MIT
// @match         https://www.chess.com/play/*
// @match         https://www.chess.com/game/*
// @match         https://www.chess.com/analysis
// @match         https://www.chess.com/analysis/*
// @match         https://www.chess.com/puzzles/*
// @match         https://www.chess.com/daily
// @connect       chess-api.com
// @connect       stockfish.online
// @connect       unpkg.com
// @connect       *
// @grant         GM_getResourceText
// @grant         GM_getValue
// @grant         GM_setValue
// @grant         GM_xmlhttpRequest
// @resource      stockfish.js https://unpkg.com/[email protected]/bin/stockfish-18-single.js
// @run-at        document-idle
// ==/UserScript==
(function () {
    "use strict";
    // --- CONFIGURATION ---
    const CONFIG = {
        BOARD_SEL: "chess-board, wc-chess-board",
        LOOP_MS: 50,
        API: { MAX_DEPTH: 18, MAX_TIME: 2000 }
    };
    const PIECE_IMGS = {
        p: "https://upload.wikimedia.org/wikipedia/commons/c/c7/Chess_pdt45.svg",
        r: "https://upload.wikimedia.org/wikipedia/commons/f/ff/Chess_rdt45.svg",
        n: "https://upload.wikimedia.org/wikipedia/commons/e/ef/Chess_ndt45.svg",
        b: "https://upload.wikimedia.org/wikipedia/commons/9/98/Chess_bdt45.svg",
        q: "https://upload.wikimedia.org/wikipedia/commons/4/47/Chess_qdt45.svg",
        k: "https://upload.wikimedia.org/wikipedia/commons/f/f0/Chess_kdt45.svg",
        P: "https://upload.wikimedia.org/wikipedia/commons/4/45/Chess_plt45.svg",
        R: "https://upload.wikimedia.org/wikipedia/commons/7/72/Chess_rlt45.svg",
        N: "https://upload.wikimedia.org/wikipedia/commons/7/70/Chess_nlt45.svg",
        B: "https://upload.wikimedia.org/wikipedia/commons/b/b1/Chess_blt45.svg",
        Q: "https://upload.wikimedia.org/wikipedia/commons/1/15/Chess_qlt45.svg",
        K: "https://upload.wikimedia.org/wikipedia/commons/4/42/Chess_klt45.svg",
    };
    const STOCKFISH_ICON = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAACXBIWXMAAAsTAAALEwEAmpwYAAAEGklEQVR4nO2ZW2gcVRjH/9+Z3ewm22xMNtVGU9RIxNqmFxF8sC0iFhF8UF980QcFL1jwaRELXnwQBC94UfBBEQtK0Yqi1LwgaL0k0DQm2zapm2az2d1kd2bO8f/M7Gw22U12052lB34wzMzO+Z/vO+d85ztnlkQIIYQQQgghhBBCSKtQSt1BCHmOEDKplLqD53n7x8fH9xBCfC2U0r2EkNcIIY/xPG9rIR4F8CGl9EEA+wghG5s9+yGl9F0A+9sKEEJ8B+A5AMcIIb6W/v8B4BCl9AkA+1oK8Ty/m1L6LID9hJCNzb7ZhRL6IoD9bQcopc8SQp4ghExt9mw/pfR5APtbcwH1C68W/l8B3wO463+xAOu5gH2EkG2EENSX8F4A+wkhG7mA+l3gVwD3tBCAUvoYIeQpQkh/s2f7KaVPAthfFw/4HsA+QsjGZt/sJ5Q+01oArvN9Qkh/s2f7KaWPE0L2112Au8D3AO4jhGxs9u0+SulTAPbXFfA9gP2EkI3NvttPKX0KwP66Ar4HsJ8QsrHZd/sppU8C2F9XwPcA9hNCNjb7bj+l9CkA++sK+B7AfYSQjc2+208pfQrA/rYClNI9hJCnCCHTmz3bTyl9CsD+tgOU0mcIIU8RQqY3e7afUvo0gP1tBSilz1BKnwGwv60A/H8uQAh5DsB+QsjGZt98Qil9DsD+1gKU0ucIIc8QQqY2e7afUvo8gP2tBaij0N8A7iOEbGz23X5K6fMA9tcV8D2A+wkhG5t9t59S+iSA/XUFfA9gPyFkY7Pv9lNKTwLYX1fA9wDuI4RsbPbd/v8U4H/fA0II8Ty/mxDiA7C/Lh7wPID9hJCNzb7dTyl9EcD+ungA8Ty/mxDiA7C/pQCldC+l9EUA+1sK8Ty/hxDya0rpCwD2txTg/7kAIeR5APtbut8ghBBC2pZ/ALy683b5qZ2oAAAAAElFTkSuQmCC";

    const DEFAULT_WASM_URL = "https://unpkg.com/[email protected]/bin/stockfish-18-single.wasm";
    const WASM_PRESETS = [
        { label: "SF 18.0.5 Single-thread (unpkg)", url: "https://unpkg.com/[email protected]/bin/stockfish-18-single.wasm" },
        { label: "SF 18.0.5 Single-thread (jsDelivr)", url: "https://cdn.jsdelivr.net/npm/[email protected]/bin/stockfish-18-single.wasm" },
        { label: "Custom URL...", url: "custom" },
    ];

    // --- STATE MANAGEMENT ---
    const state = {
        board: null,
        isThinking: !1,
        ui: {},
        lastRawFEN: "N/A",
        lastSentFEN: "",
        lastSanitizedBoardFEN: "",
        lastMoveResult: "Waiting for analysis...",
        lastLiveResult: "Depth | Evaluation: Best move will appear here.",
        lastPayload: "N/A",
        lastResponse: "N/A",
        moveTargetTime: 0,
        calculatedDelay: 0,
        localEngine: null,
        engineLoadingInProgress: !1,
        engineStatus: "not_installed", // "not_installed" | "loading" | "ready" | "error"
        engineStatusMsg: "",
        currentCloudRequest: null,
        currentBestMove: null,
        currentPV: [],
        analysisStartTime: 0,
        h: 180, s: 100, l: 50,
        newGameObserver: null,
        queueTimeout: null,
        localEval: null,
        localMate: null,
        localPV: null,
        localDepth: null,
        history: [],
        hasSavedCurrentGameResult: !1,
        lastSeenFEN: "",
        playingAs: null,
        visualTab: "move",
        visuals: [],
    };
    const DEFAULT_SETTINGS = {
        engineMode: "cloud",
        depth: 18,
        maxThinkingTime: 0,
        searchMoves: "",
        autoRun: !0,
        autoMove: !0,
        autoQueue: !1,
        hideAfterMove: false,
        showPVArrows: !1,
        pvDepth: 5,
        pvShowNumbers: !1,
        pvCustomGradient: !1,
        pvStartColor: "#FFFF00",
        pvEndColor: "#FF0000",
        minDelay: 0,
        maxDelay: 0,
        highlightColor: "#00eeff",
        visualType: "outline",
        innerOpacity: 0.6,
        outerOpacity: 0.2,
        gradientBias: 0,
        arrowOpacity: 0.8,
        arrowWidth: 15,
        visualOutlineWidth: 5,
        visualOutlineOpacity: 0.5,
        visualOutlineGlow: !0,
        visualOutlineGlowRadius: 50,
        visualDuration: 0.6,
        visualFadeOut: !0,
        themeBg: "#222222",
        themeText: "#eeeeee",
        themeBorder: "#444444",
        themePrimary: "#81b64c",
        menuOpacity: 0.9,
        debugLogs: !1,
        enableHistory: !0,
        menuPosition: "top-right",
        localWasmUrl: DEFAULT_WASM_URL,
        localHashMB: 64,
        localMoveOverhead: 100,
        localSkillLevel: 20,
        localLimitStrength: false,
        localElo: 3190,
        localShowWDL: false,
        localMinThinkingTime: 20,
        localSlowMover: 100,
    };
    const settings = { ...DEFAULT_SETTINGS };
    // --- COLOR HELPERS ---
    const hexToRgb = (hex) => {
        const r = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
        return r ? { r: parseInt(r[1], 16), g: parseInt(r[2], 16), b: parseInt(r[3], 16) } : { r: 0, g: 0, b: 0 };
    };
    const rgbToHex = (r, g, b) => "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
    const rgbToHsl = (r, g, b) => {
        r /= 255; g /= 255; b /= 255;
        const max = Math.max(r, g, b), min = Math.min(r, g, b);
        let h, s, l = (max + min) / 2;
        if (max === min) h = s = 0;
        else {
            const d = max - min;
            s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
            switch (max) {
                case r: h = (g - b) / d + (g < b ? 6 : 0); break;
                case g: h = (b - r) / d + 2; break;
                case b: h = (r - g) / d + 4; break;
            }
            h /= 6;
        }
        return { h: h * 360, s: s * 100, l: l * 100 };
    };
    const hslToRgb = (h, s, l) => {
        let r, g, b;
        h /= 360; s /= 100; l /= 100;
        if (s === 0) r = g = b = l;
        else {
            const hue2rgb = (p, q, t) => {
                if (t < 0) t += 1;
                if (t > 1) t -= 1;
                if (t < 1 / 6) return p + (q - p) * 6 * t;
                if (t < 1 / 2) return q;
                if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
                return p;
            };
            const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
            const p = 2 * l - q;
            r = hue2rgb(p, q, h + 1 / 3);
            g = hue2rgb(p, q, h);
            b = hue2rgb(p, q, h - 1 / 3);
        }
        return { r: Math.round(r * 255), g: Math.round(g * 255), b: Math.round(b * 255) };
    };
    // --- SAVE/LOAD HELPERS ---
    function saveSetting(key, val) {
        settings[key] = val;
        GM_setValue(`bot_${key}`, val);
    }
    function loadSettings() {
        Object.keys(DEFAULT_SETTINGS).forEach((k) => {
            const saved = GM_getValue(`bot_${k}`);
            if (saved !== undefined) settings[k] = saved;
        });
        state.history = GM_getValue("bot_history", []);
    }
    // --- BOARD FEN LOGIC ---
    function getRawBoardFEN() {
        if (!state.board?.game) return null;
        try {
            if (typeof state.board.game.getFEN === "function") return state.board.game.getFEN();
            if (typeof state.board.game.fen === "string") return state.board.game.fen;
            if (state.board.game.getPosition) return state.board.game.getPosition();
        } catch (e) {}
        return null;
    }
    function sanitizeFEN(rawFEN) {
        if (!rawFEN) return "";
        let parts = rawFEN.replace(/\s+/g, " ").trim().split(" ");
        if (parts.length < 6) {
            const def = ["w", "-", "-", "0", "1"];
            for (let i = parts.length; i < 6; i++) parts.push(def[i - 1]);
        }
        if (parts[3] && parts[3] !== "-") parts[3] = parts[3].toLowerCase();
        return parts.join(" ");
    }
    // --- VISUAL MANAGER ---
    const Visuals = {
        add: (move, type) => {
            if (!move) return;
            if (type === 'history') {
                Visuals.removeByType('history');
                Visuals.removeByType('analysis');
            } else if (type === 'analysis') {
                Visuals.removeByType('analysis');
            }
            const id = `vis-${type}-${move}`;
            const existingIdx = state.visuals.findIndex(v => v.id === id);
            if (existingIdx !== -1) Visuals.remove(id);
            Visuals.draw(id, move);
            const interval = setInterval(() => {
                const vis = state.visuals.find(v => v.id === id);
                if (!vis || vis.isFading) { clearInterval(interval); return; }
                Visuals.draw(id, move);
            }, 50);
            state.visuals.push({ id, move, type, interval, isFading: false });
            if (type === 'history') {
                if (settings.visualDuration === -1) { Visuals.remove(id); return; }
                if (settings.visualDuration > 0) {
                    const ms = settings.visualDuration * 1000;
                    if (settings.visualFadeOut) setTimeout(() => Visuals.fadeOut(id), ms);
                    else setTimeout(() => Visuals.remove(id), ms);
                }
            }
        },
        draw: (id, move) => {
            state.board = document.querySelector(CONFIG.BOARD_SEL);
            if (!state.board) return;
            const existing = document.querySelector(`.${id}`);
            if (existing) {
                if (!state.board.contains(existing)) existing.remove();
                else return;
            }
            const { r, g, b } = hexToRgb(settings.highlightColor);
            const col = (a) => `rgba(${r}, ${g}, ${b}, ${a})`;
            const from = move.substring(0, 2);
            const to = move.substring(2, 4);
            const drawBox = () => {
                [from, to].forEach((alg) => {
                    const sqId = `${alg.charCodeAt(0) - 96}${alg.charAt(1)}`;
                    const div = document.createElement("div");
                    div.className = `square-${sqId} bot-highlight ${id}`;
                    let baseStyle = `position: absolute; pointer-events: none !important; z-index: 1000000 !important; width: 12.5%; height: 12.5%; box-sizing: border-box; transition: none !important; `;
                    if (settings.visualType === "outline") {
                        let glow = settings.visualOutlineGlow ? `box-shadow: 0 0 ${settings.visualOutlineGlowRadius}px ${col(1)}, inset 0 0 ${settings.visualOutlineGlowRadius/2}px ${col(0.5)} !important;` : "";
                        div.style.cssText = baseStyle + `border: ${settings.visualOutlineWidth}px solid ${col(settings.visualOutlineOpacity)} !important; ${glow}`;
                    } else {
                        const bias = settings.gradientBias + "%";
                        div.style.cssText = baseStyle + `background: radial-gradient(closest-side, ${col(settings.innerOpacity)} ${bias}, ${col(settings.outerOpacity)} 100%) !important;`;
                    }
                    state.board.appendChild(div);
                });
            };
            if (settings.visualType === "arrow") drawArrow(move, id);
            else drawBox();
        },
        fadeOut: (id) => {
            const vis = state.visuals.find(v => v.id === id);
            if (!vis) return;
            vis.isFading = true;
            clearInterval(vis.interval);
            const els = document.querySelectorAll(`.${id}`);
            els.forEach(el => {
                el.style.setProperty("transition", `opacity ${settings.visualDuration}s linear`, "important");
                el.style.setProperty("opacity", "0", "important");
            });
            setTimeout(() => Visuals.remove(id), settings.visualDuration * 1000);
        },
        remove: (id) => {
            const idx = state.visuals.findIndex(v => v.id === id);
            if (idx !== -1) { clearInterval(state.visuals[idx].interval); state.visuals.splice(idx, 1); }
            document.querySelectorAll(`.${id}`).forEach(el => el.remove());
        },
        removeByType: (type) => {
            const toRemove = state.visuals.filter(v => v.type === type);
            toRemove.forEach(v => Visuals.remove(v.id));
        }
    };
    // --- PV MANAGER ---
    const PV = {
        interval: null,
        lastMoves: [],
        update: (pvMoves) => {
            PV.lastMoves = pvMoves || [];
            if (!settings.showPVArrows) { PV.clear(); return; }
            PV.draw();
            if (!PV.interval) PV.interval = setInterval(PV.draw, 100);
        },
        clear: () => { document.querySelectorAll('.pv-arrow').forEach(el => el.remove()); },
        draw: () => {
            state.board = document.querySelector(CONFIG.BOARD_SEL);
            if (!state.board) return;
            if (!settings.showPVArrows || !PV.lastMoves.length) { PV.clear(); return; }
            const existing = document.querySelector('.pv-arrow');
            if (existing && !state.board.contains(existing)) PV.clear();
            const limit = Math.min(PV.lastMoves.length, settings.pvDepth);
            for (let i = 0; i < limit; i++) {
                const move = PV.lastMoves[i];
                const id = `pv-arrow-${i}`;
                const el = document.querySelector(`.${id}`);
                if (el && state.board.contains(el)) {
                    if (el.dataset.move === move) continue;
                    el.remove();
                } else if (el) el.remove();
                let color = settings.highlightColor;
                if (settings.pvCustomGradient) {
                    const start = hexToRgb(settings.pvStartColor);
                    const end = hexToRgb(settings.pvEndColor);
                    const factor = limit === 1 ? 0 : i / (limit - 1);
                    const r = Math.round(start.r + factor * (end.r - start.r));
                    const g = Math.round(start.g + factor * (end.g - start.g));
                    const b = Math.round(start.b + factor * (end.b - start.b));
                    color = `rgb(${r},${g},${b})`;
                }
                drawPVArrow(move, id, color, i + 1);
            }
            let i = limit;
            while (document.querySelector(`.pv-arrow-${i}`)) {
                document.querySelectorAll(`.pv-arrow-${i}`).forEach(e => e.remove());
                i++;
            }
        }
    };
    // --- EVALUATION BAR ---
    const EvalBar = {
        el: null, whiteFill: null, scoreWhiteEl: null, scoreBlackEl: null,
        _evalToTranslate: (ev) => 100 - (100 / (1 + Math.exp(-ev * 0.4))),
        create: () => {
            if (document.getElementById("bot-eval-bar")) {
                EvalBar.el = document.getElementById("bot-eval-bar");
                EvalBar.whiteFill = document.getElementById("bot-eval-white-fill");
                EvalBar.scoreWhiteEl = document.getElementById("bot-eval-score-white");
                EvalBar.scoreBlackEl = document.getElementById("bot-eval-score-black");
                return;
            }
            const bar = document.createElement("div");
            bar.id = "bot-eval-bar";
            bar.style.cssText = "position:fixed;width:16px;z-index:100;pointer-events:none;display:none;border-radius:3px;overflow:hidden;box-shadow:0 2px 8px rgba(0,0,0,0.6);";
            bar.innerHTML = `
                <div style="position:relative;width:100%;height:100%;background:#1a1a1a;border:1px solid #3a3a3a;box-sizing:border-box;border-radius:3px;overflow:hidden;">
                    <div id="bot-eval-white-fill" style="position:absolute;bottom:0;left:0;width:100%;height:100%;background:#ffffff;transform:translate3d(0,50%,0);transition:transform 0.35s cubic-bezier(0.4,0,0.2,1);will-change:transform;"></div>
                    <span id="bot-eval-score-black" style="position:absolute;top:4px;left:50%;transform:translateX(-50%);font-size:8.5px;font-weight:bold;font-family:monospace;z-index:3;color:#cccccc;white-space:nowrap;writing-mode:vertical-rl;text-orientation:mixed;line-height:1;text-shadow:0 1px 2px rgba(0,0,0,0.8);"></span>
                    <span id="bot-eval-score-white" style="position:absolute;bottom:4px;left:50%;transform:translateX(-50%);font-size:8.5px;font-weight:bold;font-family:monospace;z-index:3;color:#333333;white-space:nowrap;writing-mode:vertical-rl;text-orientation:mixed;line-height:1;text-shadow:0 1px 2px rgba(255,255,255,0.5);"></span>
                </div>`;
            document.body.appendChild(bar);
            EvalBar.el = bar;
            EvalBar.whiteFill = document.getElementById("bot-eval-white-fill");
            EvalBar.scoreWhiteEl = document.getElementById("bot-eval-score-white");
            EvalBar.scoreBlackEl = document.getElementById("bot-eval-score-black");
        },
        updatePosition: () => {
            if (!EvalBar.el) EvalBar.create();
            const board = document.querySelector(CONFIG.BOARD_SEL);
            if (!board) { if (EvalBar.el) EvalBar.el.style.display = "none"; return; }
            const rect = board.getBoundingClientRect();
            if (!rect.width) { EvalBar.el.style.display = "none"; return; }
            EvalBar.el.style.display = "block";
            EvalBar.el.style.top = rect.top + "px";
            EvalBar.el.style.left = (rect.left - 16 - 4) + "px";
            EvalBar.el.style.height = rect.height + "px";
        },
        update: (evalScore, mate) => {
            if (!EvalBar.el) return;
            const playingAsBlack = (state.playingAs === 2);
            const myFillColor = playingAsBlack ? '#202020' : '#ffffff';
            const oppBgColor = playingAsBlack ? '#f0f0f0' : '#1a1a1a';
            const myLabelColor = playingAsBlack ? '#cccccc' : '#333333';
            const oppLabelColor = playingAsBlack ? '#333333' : '#cccccc';
            const innerBar = EvalBar.el.querySelector('div');
            if (innerBar) innerBar.style.background = oppBgColor;
            if (EvalBar.whiteFill) EvalBar.whiteFill.style.background = myFillColor;
            let translateY = 50, scoreText = "0.0", iWin = false;
            if (mate !== null && mate !== undefined && mate !== 0) {
                const m = parseInt(mate);
                const whiteWinsMate = m > 0;
                iWin = playingAsBlack ? !whiteWinsMate : whiteWinsMate;
                translateY = iWin ? 0 : 100;
                scoreText = "M" + Math.abs(m);
            } else if (evalScore !== null && evalScore !== undefined) {
                const ev = parseFloat(evalScore);
                if (!isNaN(ev)) {
                    const raw = EvalBar._evalToTranslate(ev);
                    translateY = playingAsBlack ? (100 - raw) : raw;
                    const whiteWinsEval = ev >= 0;
                    iWin = playingAsBlack ? !whiteWinsEval : whiteWinsEval;
                    const absEv = Math.abs(ev);
                    const sign = ev > 0 ? "+" : ev < 0 ? "-" : "";
                    scoreText = absEv < 10 ? sign + absEv.toFixed(1) : sign + Math.round(absEv).toString();
                }
            }
            if (EvalBar.whiteFill) EvalBar.whiteFill.style.transform = `translate3d(0, ${translateY}%, 0)`;
            const absScore = Math.abs(parseFloat(scoreText.replace(/[^0-9.]/g, "")));
            const showScore = absScore > 0.1 || (mate !== null && mate !== undefined);
            if (EvalBar.scoreWhiteEl) { EvalBar.scoreWhiteEl.textContent = (showScore && iWin) ? scoreText : ""; EvalBar.scoreWhiteEl.style.color = myLabelColor; }
            if (EvalBar.scoreBlackEl) { EvalBar.scoreBlackEl.textContent = (showScore && !iWin) ? scoreText : ""; EvalBar.scoreBlackEl.style.color = oppLabelColor; }
        },
        reset: () => {
            if (!EvalBar.el) return;
            if (EvalBar.whiteFill) EvalBar.whiteFill.style.transform = "translate3d(0,50%,0)";
            if (EvalBar.scoreWhiteEl) EvalBar.scoreWhiteEl.textContent = "";
            if (EvalBar.scoreBlackEl) EvalBar.scoreBlackEl.textContent = "";
        }
    };
    function drawPVArrow(move, id, color, index) {
        if (!state.board) return;
        let isFlipped = state.board.classList.contains("flipped");
        if (!isFlipped && state.board.game && state.board.game.getPlayingAs && state.board.game.getPlayingAs() === "b") isFlipped = true;
        const from = move.substring(0, 2), to = move.substring(2, 4);
        const getCoords = (sq) => {
            const file = sq.charCodeAt(0) - 97, rank = parseInt(sq[1]) - 1;
            return isFlipped ? { x: (7-file)*12.5+6.25, y: rank*12.5+6.25 } : { x: file*12.5+6.25, y: (7-rank)*12.5+6.25 };
        };
        const start = getCoords(from), end = getCoords(to);
        const dx = end.x - start.x, dy = end.y - start.y;
        const len = Math.sqrt(dx*dx + dy*dy);
        if (len === 0) return;
        const scale = (settings.arrowWidth || 15) / 15;
        const headLen = 4*scale, headWidth = 3*scale, lineWidth = 1.0*scale;
        const ux = dx/len, uy = dy/len;
        const endLineX = end.x - ux*headLen, endLineY = end.y - uy*headLen;
        const px = -uy, py = ux;
        const ns = "http://www.w3.org/2000/svg";
        const svg = document.createElementNS(ns, "svg");
        svg.setAttribute("class", `pv-arrow ${id}`);
        svg.dataset.move = move;
        svg.style.cssText = "position:absolute; top:0; left:0; width:100%; height:100%; pointer-events:none; z-index:900;";
        svg.setAttribute("viewBox", "0 0 100 100");
        const line = document.createElementNS(ns, "line");
        line.setAttribute("x1", start.x); line.setAttribute("y1", start.y);
        line.setAttribute("x2", endLineX); line.setAttribute("y2", endLineY);
        line.setAttribute("stroke", color); line.setAttribute("stroke-width", lineWidth);
        line.setAttribute("stroke-opacity", settings.arrowOpacity || 0.8);
        line.setAttribute("stroke-linecap", "round");
        const poly = document.createElementNS(ns, "polygon");
        poly.setAttribute("points", `${end.x},${end.y} ${endLineX+px*(headWidth/2)},${endLineY+py*(headWidth/2)} ${endLineX-px*(headWidth/2)},${endLineY-py*(headWidth/2)}`);
        poly.setAttribute("fill", color); poly.setAttribute("fill-opacity", settings.arrowOpacity || 0.8);
        svg.appendChild(line); svg.appendChild(poly);
        if (settings.pvShowNumbers) {
            const text = document.createElementNS(ns, "text");
            text.setAttribute("x", (start.x+end.x)/2); text.setAttribute("y", (start.y+end.y)/2);
            text.setAttribute("dy", "0.3em"); text.setAttribute("text-anchor", "middle");
            text.setAttribute("fill", "#fff"); text.setAttribute("font-size", "2.5");
            text.setAttribute("font-weight", "bold"); text.setAttribute("stroke", "#000");
            text.setAttribute("stroke-width", "0.1"); text.textContent = index;
            svg.appendChild(text);
        }
        state.board.appendChild(svg);
    }
    function drawArrow(move, id) {
        const color = settings.highlightColor, opacity = settings.arrowOpacity, width = settings.arrowWidth;
        let isFlipped = !1;
        if (state.board.classList.contains("flipped")) isFlipped = !0;
        else if (state.board.game && state.board.game.getPlayingAs && state.board.game.getPlayingAs() === "b") isFlipped = !0;
        const from = move.substring(0, 2), to = move.substring(2, 4);
        const getCoords = (sq) => {
            const file = sq.charCodeAt(0) - 97, rank = parseInt(sq[1]) - 1;
            return isFlipped ? { x: (7-file)*12.5+6.25, y: rank*12.5+6.25 } : { x: file*12.5+6.25, y: (7-rank)*12.5+6.25 };
        };
        const start = getCoords(from), end = getCoords(to);
        const dx = end.x - start.x, dy = end.y - start.y;
        const len = Math.sqrt(dx*dx + dy*dy);
        if (len === 0) return;
        const scale = width/15, headLen = 4*scale, headWidth = 3*scale, lineWidth = 1.2*scale;
        const ux = dx/len, uy = dy/len;
        const endLineX = end.x - ux*headLen, endLineY = end.y - uy*headLen;
        const px = -uy, py = ux;
        const ns = "http://www.w3.org/2000/svg";
        const svg = document.createElementNS(ns, "svg");
        svg.setAttribute("class", `bot-highlight ${id}`);
        svg.style.cssText = "position:absolute; top:0; left:0; width:100%; height:100%; pointer-events:none; z-index:200;";
        svg.setAttribute("viewBox", "0 0 100 100");
        const line = document.createElementNS(ns, "line");
        line.setAttribute("x1", start.x); line.setAttribute("y1", start.y);
        line.setAttribute("x2", endLineX); line.setAttribute("y2", endLineY);
        line.setAttribute("stroke", color); line.setAttribute("stroke-width", lineWidth);
        line.setAttribute("stroke-opacity", opacity);
        const polygon = document.createElementNS(ns, "polygon");
        polygon.setAttribute("points", `${end.x},${end.y} ${endLineX+px*(headWidth/2)},${endLineY+py*(headWidth/2)} ${endLineX-px*(headWidth/2)},${endLineY-py*(headWidth/2)}`);
        polygon.setAttribute("fill", color); polygon.setAttribute("fill-opacity", opacity);
        svg.appendChild(line); svg.appendChild(polygon);
        state.board.appendChild(svg);
    }
    // --- EVAL STATUS LOGIC ---
    function getEvalStatusData(val, isMate) {
        const pa = state.playingAs || 1;
        let relativeScore = (pa === 2) ? -val : val;
        if (isMate) {
            if (relativeScore > 0) return { text: "Significant Advantage (Mate)", color: "#00ff00" };
            return { text: "Significant Disadvantage (Mate)", color: "#ff0000" };
        }
        if (relativeScore > 3) return { text: "Significant Advantage", color: "#00ff00" };
        if (relativeScore > 1.5) return { text: "Clear Advantage", color: "#55ff55" };
        if (relativeScore > 0.5) return { text: "Decisive Advantage", color: "#81b64c" };
        if (relativeScore > 0.25) return { text: "Slight Advantage", color: "#aaffaa" };
        if (relativeScore >= -0.25) return { text: "Equal", color: "#aaaaaa" };
        if (relativeScore >= -0.5) return { text: "Slight Disadvantage", color: "#ffaaaa" };
        if (relativeScore >= -1.5) return { text: "Decisive Disadvantage", color: "#ff7777" };
        if (relativeScore >= -3) return { text: "Clear Disadvantage", color: "#ff4444" };
        return { text: "Significant Disadvantage", color: "#ff0000" };
    }

    // --- SF18 ENGINE CORE ---
    function setEngineStatus(status, msg) {
        state.engineStatus = status;
        state.engineStatusMsg = msg || "";
        updateLocalSettingsUI();
    }

    function buildEngine(jsCode, wasmBytes, db) {
        try {
            let bin = "";
            const chunk = 8192;
            for (let i = 0; i < wasmBytes.length; i += chunk)
                bin += String.fromCharCode.apply(null, wasmBytes.subarray(i, i + chunk));
            const wasmB64 = btoa(bin);
            const patchCode = `
var _wasmB64 = "${wasmB64}";
var _wasmBytes = (function(){
    var b = atob(_wasmB64), a = new Uint8Array(b.length);
    for(var i=0;i<b.length;i++) a[i]=b.charCodeAt(i);
    return a;
})();
self.fetch = function(url, opts) {
    return Promise.resolve({ ok:true, arrayBuffer:function(){ return Promise.resolve(_wasmBytes.buffer); } });
};
`;
            const blob = new Blob([patchCode + jsCode], { type: "application/javascript" });
            state.localEngine = new Worker(URL.createObjectURL(blob));
            state.localEngine.onerror = (e) => {
                handleError("Local Engine Error", e);
                setEngineStatus("error", e.message || "Worker error");
                state.localEngine = null;
                state.engineLoadingInProgress = false;
            };
            state.localEngine.onmessage = handleLocalMessage;
            // SF18 single-file wires its own onmessage internally.
            // Send config commands; the internal queue handles ordering.
            ["ucinewgame",
             `setoption name Hash value ${settings.localHashMB}`,
             `setoption name Move Overhead value ${settings.localMoveOverhead}`,
             `setoption name Minimum Thinking Time value ${settings.localMinThinkingTime}`,
             `setoption name Slow Mover value ${settings.localSlowMover}`,
             `setoption name UCI_ShowWDL value ${settings.localShowWDL}`,
             `setoption name UCI_LimitStrength value ${settings.localLimitStrength}`,
             `setoption name UCI_Elo value ${settings.localElo}`,
             `setoption name Skill Level value ${settings.localSkillLevel}`,
             "setoption name MultiPV value 1"
            ].forEach(c => state.localEngine.postMessage(c));
            state.engineLoadingInProgress = false;
            setEngineStatus("ready", "");
            state.lastMoveResult = "✅ SF18 Ready.";
            console.log("Stockfish 18 loaded.");
            updateUI();
            // If we have a db and want to persist (already saved before calling buildEngine for reinstall)
        } catch (e) {
            handleError("Engine Build Fail", e);
            state.engineLoadingInProgress = false;
            setEngineStatus("error", e.message || "Build failed");
        }
    }

    function downloadAndInstall(jsCode, wasmUrl, db) {
        setEngineStatus("loading", "Downloading WASM...");
        state.lastMoveResult = "⏳ Downloading SF18 WASM...";
        updateUI();
        GM_xmlhttpRequest({
            method: "GET",
            url: wasmUrl,
            responseType: "arraybuffer",
            onload: (wasmRes) => {
                try {
                    const wasmBytes = new Uint8Array(wasmRes.response);
                    if (db) {
                        try {
                            const tx = db.transaction("wasm", "readwrite");
                            tx.objectStore("wasm").put(wasmBytes, "bytes");
                        } catch (e2) {}
                    }
                    buildEngine(jsCode, wasmBytes, db);
                } catch (e) {
                    handleError("Engine Load Fail", e);
                    state.engineLoadingInProgress = false;
                    setEngineStatus("error", e.message || "Load failed");
                }
            },
            onerror: (e) => {
                handleError("WASM Download Fail", e);
                state.engineLoadingInProgress = false;
                setEngineStatus("error", "Download failed — check URL");
            },
        });
    }

    function loadLocalEngine() {
        if (state.localEngine || state.engineLoadingInProgress) return;
        state.engineLoadingInProgress = true;
        state.isThinking = false;
        const jsCode = GM_getResourceText("stockfish.js");
        if (!jsCode) {
            handleError("Engine Load Fail", "SF18 JS resource missing");
            state.engineLoadingInProgress = false;
            setEngineStatus("error", "JS resource missing");
            return;
        }
        const wasmUrl = settings.localWasmUrl || DEFAULT_WASM_URL;
        setEngineStatus("loading", "Opening cache...");
        const dbReq = indexedDB.open("sf18cache", 1);
        dbReq.onupgradeneeded = (e) => e.target.result.createObjectStore("wasm");
        dbReq.onsuccess = (e) => {
            const db = e.target.result;
            const getReq = db.transaction("wasm", "readonly").objectStore("wasm").get("bytes");
            getReq.onsuccess = (e2) => {
                if (e2.target.result) {
                    setEngineStatus("loading", "Loading from cache...");
                    state.lastMoveResult = "⚡ SF18 loading from cache...";
                    updateUI();
                    buildEngine(jsCode, e2.target.result, db);
                } else {
                    downloadAndInstall(jsCode, wasmUrl, db);
                }
            };
            getReq.onerror = () => downloadAndInstall(jsCode, wasmUrl, null);
        };
        dbReq.onerror = () => downloadAndInstall(jsCode, wasmUrl, null);
    }

    function reinstallEngine() {
        if (state.localEngine) {
            try { state.localEngine.terminate(); } catch (e) {}
            state.localEngine = null;
        }
        state.engineLoadingInProgress = false;
        setEngineStatus("loading", "Clearing cache...");
        const jsCode = GM_getResourceText("stockfish.js");
        if (!jsCode) { setEngineStatus("error", "JS resource missing"); return; }
        const wasmUrl = settings.localWasmUrl || DEFAULT_WASM_URL;
        const dbReq = indexedDB.open("sf18cache", 1);
        dbReq.onupgradeneeded = (e) => e.target.result.createObjectStore("wasm");
        dbReq.onsuccess = (e) => {
            const db = e.target.result;
            try {
                const tx = db.transaction("wasm", "readwrite");
                const del = tx.objectStore("wasm").delete("bytes");
                del.onsuccess = () => {
                    state.engineLoadingInProgress = true;
                    downloadAndInstall(jsCode, wasmUrl, db);
                };
                del.onerror = () => {
                    state.engineLoadingInProgress = true;
                    downloadAndInstall(jsCode, wasmUrl, db);
                };
            } catch (e2) {
                state.engineLoadingInProgress = true;
                downloadAndInstall(jsCode, wasmUrl, null);
            }
        };
        dbReq.onerror = () => {
            state.engineLoadingInProgress = true;
            downloadAndInstall(jsCode, wasmUrl, null);
        };
    }

    function uninstallEngine() {
        if (state.localEngine) {
            try { state.localEngine.terminate(); } catch (e) {}
            state.localEngine = null;
        }
        state.engineLoadingInProgress = false;
        const dbReq = indexedDB.open("sf18cache", 1);
        dbReq.onupgradeneeded = (e) => e.target.result.createObjectStore("wasm");
        dbReq.onsuccess = (e) => {
            const db = e.target.result;
            try {
                const tx = db.transaction("wasm", "readwrite");
                tx.objectStore("wasm").delete("bytes");
                tx.oncomplete = () => setEngineStatus("not_installed", "");
            } catch (e2) { setEngineStatus("not_installed", ""); }
        };
        dbReq.onerror = () => setEngineStatus("not_installed", "");
        state.lastMoveResult = "Local engine uninstalled.";
        updateUI();
    }

    function triggerFallback() {
        if (settings.engineMode === 'local') return;
        console.warn(`API Error. Switching to Local SF18 at Depth ${settings.depth}.`);
        settings.engineMode = 'local';
        saveSetting('engineMode', 'local');
        if (state.ui.selMode) state.ui.selMode.value = 'local';
        state.lastMoveResult = `⚠️ API Error. Switched to Local SF18.`;
        loadLocalEngine();
        if (state.lastSanitizedBoardFEN) analyzeLocal(state.lastSanitizedBoardFEN, settings.depth);
        updateUI();
    }
    function analyze(depth = settings.depth, fenOverride = null, isRetry = !1) {
        if (state.isThinking && !fenOverride && !isRetry) return;
        let finalFEN = fenOverride || sanitizeFEN(getRawBoardFEN());
        if (!finalFEN) return;
        state.lastRawFEN = finalFEN;
        state.lastSentFEN = finalFEN;
        if (!fenOverride) state.lastSanitizedBoardFEN = finalFEN;
        state.isThinking = !0;
        state.analysisStartTime = performance.now();
        const minMs = settings.minDelay * 1000, maxMs = settings.maxDelay * 1000;
        const delay = Math.random() * (maxMs - minMs) + minMs;
        state.moveTargetTime = performance.now() + delay;
        state.calculatedDelay = (delay / 1000).toFixed(2);
        updateUI();
        if (settings.engineMode === "cloud") analyzeCloud(finalFEN, depth, isRetry);
        else if (settings.engineMode === "sfonline") analyzeSF16(finalFEN, depth);
        else analyzeLocal(finalFEN, depth);
    }
    function analyzeCloud(finalFEN, depth, isRetry) {
        const actualDepth = Math.min(depth, 18);
        const payload = {
            fen: finalFEN,
            depth: actualDepth,
            maxThinkingTime: Math.min(settings.maxThinkingTime, CONFIG.API.MAX_TIME),
            taskId: Math.random().toString(36).substring(7),
        };
        if (settings.searchMoves.trim()) payload.searchmoves = settings.searchMoves.trim();
        state.lastPayload = `POST https://chess-api.com/v1\n${JSON.stringify(payload, null, 2)}`;
        if (state.ui.liveOutput) state.ui.liveOutput.innerHTML = isRetry ? "♻️ Retrying Safe FEN..." : "☁️ SF18 Cloud Analysis...";
        updateUI();
        state.currentCloudRequest = GM_xmlhttpRequest({
            method: "POST", url: "https://chess-api.com/v1",
            headers: { "Content-Type": "application/json" },
            data: JSON.stringify(payload), timeout: 15000,
            onload: (res) => handleCloudResponse(res, finalFEN, actualDepth, isRetry),
            onerror: (err) => { handleError("Network Error", err); triggerFallback(); },
            ontimeout: () => { handleError("Timeout (15s)"); triggerFallback(); },
        });
    }
    function analyzeSF16(finalFEN, depth) {
        const actualDepth = Math.min(depth, 15);
        const url = `https://stockfish.online/api/s/v2.php?fen=${encodeURIComponent(finalFEN)}&depth=${actualDepth}&mode=bestmove`;
        state.lastPayload = `GET ${url}`;
        if (state.ui.liveOutput) state.ui.liveOutput.innerHTML = "☁️ SF17.1.0 Analysis...";
        updateUI();
        state.currentCloudRequest = GM_xmlhttpRequest({
            method: "GET", url, timeout: 20000,
            onload: (res) => handleSF16Response(res),
            onerror: (err) => { handleError("Network Error (SF16)", err); triggerFallback(); },
            ontimeout: () => { handleError("Timeout (SF16 20s)"); triggerFallback(); },
        });
    }
    function handleSF16Response(response) {
        state.isThinking = !1;
        state.lastResponse = response.responseText;
        try {
            if (response.status !== 200) throw new Error(`HTTP ${response.status}`);
            const data = JSON.parse(response.responseText);
            if (!data.success || !data.bestmove) { triggerFallback(); return; }
            const bestMove = data.bestmove.split(" ")[1] || data.bestmove;
            const duration = ((performance.now() - state.analysisStartTime) / 1000).toFixed(2);
            processBestMove(bestMove, data.evaluation, data.mate, data.continuation ? data.continuation.split(" ") : null, null, duration, true);
        } catch (e) { triggerFallback(); }
        updateUI();
    }
    function handleCloudResponse(response, sentFEN, depth, isRetry) {
        state.isThinking = !1;
        state.lastResponse = response.responseText;
        if (response.responseText.includes("HIGH_USAGE") || response.status === 429) { triggerFallback(); return; }
        try {
            if (response.status !== 200) throw new Error(`HTTP ${response.status}`);
            const rawData = JSON.parse(response.responseText);
            const result = Array.isArray(rawData) ? rawData[0] : rawData;
            if (!result || result.error || result.status === "error") {
                const errText = result?.error || result?.message || "Unknown Error";
                if (errText.includes("HIGH_USAGE")) { triggerFallback(); return; }
                if ((errText.includes("FEN") || errText.includes("VALIDATION")) && !isRetry) {
                    const parts = sentFEN.split(" ");
                    if (parts.length >= 4 && parts[3] !== "-") { parts[3] = "-"; analyze(depth, parts.join(" "), !0); return; }
                }
                triggerFallback(); return;
            }
            if (result.move || result.bestmove) {
                const duration = ((performance.now() - state.analysisStartTime) / 1000).toFixed(2);
                processBestMove(result.move || result.bestmove, result.eval, result.mate, result.continuationArr, result.winChance, duration, true);
            } else { triggerFallback(); }
        } catch (e) { triggerFallback(); }
        updateUI();
    }
    function analyzeLocal(fen, depth) {
        if (!state.localEngine) {
            loadLocalEngine();
            return; // engine loading async — mainLoop will retry once ready
        }
        state.localEval = null; state.localMate = null; state.localPV = null; state.localDepth = null;
        const actualDepth = Math.min(depth, 25);
        const cmds = [`position fen ${fen}`, `go depth ${actualDepth}`];
        state.lastPayload = `Worker CMDs:\n${cmds.join("\n")}`;
        state.ui.liveOutput.innerHTML = "⚡ Local SF18 Analysis...";
        updateUI();
        cmds.forEach((cmd) => state.localEngine.postMessage(cmd));
    }
    function handleLocalMessage(e) {
        const msg = typeof e.data === "string" ? e.data : (e.data?.toString ? e.data.toString() : null);
        if (!msg || typeof msg !== "string") return;
        state.lastResponse = (state.lastResponse.length > 500 ? "..." + state.lastResponse.slice(-500) : state.lastResponse) + "\n" + msg;
        if (state.ui.logRec) state.ui.logRec.innerText = state.lastResponse;
        if (msg.startsWith("info") && msg.includes("depth") && msg.includes("score")) {
            const depthMatch = msg.match(/depth (\d+)/);
            const scoreMatch = msg.match(/score (cp|mate) (-?\d+)/);
            const pvMatch = msg.match(/ pv (.*)/);
            if (depthMatch && scoreMatch) {
                const depth = depthMatch[1];
                let val = parseInt(scoreMatch[2]);
                const type = scoreMatch[1];
                const fenParts = state.lastSentFEN ? state.lastSentFEN.split(" ") : [];
                const sideToMove = fenParts.length > 1 ? fenParts[1] : "w";
                if (sideToMove === "b") val = -val;
                const pv = pvMatch ? pvMatch[1] : "";
                if (type === "mate") { state.localMate = val; state.localEval = null; }
                else { state.localMate = null; state.localEval = (val / 100).toFixed(2); }
                state.localPV = pv; state.localDepth = depth;
                if (pv) state.currentPV = pv.split(" ");
                EvalBar.update(type === "mate" ? null : parseFloat(state.localEval), type === "mate" ? val : null);
                let scoreTxt;
                if (type === "mate") { scoreTxt = "M" + Math.abs(val); if (val < 0) scoreTxt = "-" + scoreTxt; }
                else { scoreTxt = (val > 0 ? "+" : "") + (val / 100).toFixed(2); }
                const evalVal = type === "mate" ? val : parseFloat(state.localEval);
                const statusData = getEvalStatusData(evalVal, type === "mate");
                const duration = ((performance.now() - state.analysisStartTime) / 1000).toFixed(2);
                if (pv) {
                    const best = pv.split(" ")[0];
                    Visuals.add(best, 'analysis');
                    PV.update(state.currentPV);
                    state.lastMoveResult = `⏳ D${depth}: <span style="font-weight:bold; color:var(--bot-primary);">${best}</span>`;
                }
                state.lastLiveResult = `
                    <div style="display:flex; justify-content:space-between; align-items:center; font-weight:bold;">
                        <div style="display:flex; align-items:center; gap: 8px;">
                            <span style="color:var(--bot-primary); font-size:1.1em;">${scoreTxt}</span>
                            <span style="font-size:0.85em; color:${statusData.color}; font-weight:bold;">${statusData.text}</span>
                        </div>
                        <span style="font-size:0.7em; color:#aaa; font-weight:normal;">(${duration}s)</span>
                    </div>`;
                updateUI();
            }
        }
        if (msg.startsWith("bestmove")) {
            state.isThinking = !1;
            const parts = msg.split(" ");
            const bestMove = parts[1];
            if (bestMove && bestMove !== "(none)") {
                const duration = ((performance.now() - state.analysisStartTime) / 1000).toFixed(2);
                processBestMove(bestMove, state.localEval, state.localMate, state.localPV ? state.localPV.split(" ") : null, null, duration, state.localDepth, true);
            } else state.lastMoveResult = "⚠️ No move found";
            updateUI();
        }
    }
    function processBestMove(bestMove, evalScore, mate, continuationArr, winChance, duration, depth = null, isFinal = false) {
        state.currentBestMove = bestMove;
        state.currentPV = continuationArr || (bestMove ? [bestMove] : []);
        if (isFinal || !state.isThinking) { Visuals.add(bestMove, 'history'); PV.clear(); }
        else { Visuals.add(bestMove, 'analysis'); PV.update(state.currentPV); }
        const evalNum = (evalScore !== null && evalScore !== undefined) ? parseFloat(evalScore) : null;
        const mateNum = (mate !== null && mate !== undefined && mate !== 0) ? parseInt(mate) : null;
        EvalBar.update(evalNum, mateNum);
        let scoreTxt = "", pvStr = "N/A", numericValForStatus = 0, isMate = false;
        if (evalScore !== undefined || mate !== undefined) {
            if (mate) {
                isMate = true; numericValForStatus = mate;
                scoreTxt = `M${Math.abs(mate)}`; if (mate < 0) scoreTxt = "-" + scoreTxt;
            } else {
                const sc = parseFloat(evalScore); numericValForStatus = sc;
                scoreTxt = (sc > 0 ? "+" : "") + sc;
            }
            if (continuationArr) pvStr = continuationArr.join(" ");
        }
        const statusData = getEvalStatusData(numericValForStatus, isMate);
        const durHtml = duration ? `<span style="font-size:0.7em; color:#aaa; font-weight:normal;">(${duration}s)</span>` : "";
        state.lastMoveResult = `✅ Best: <span style="font-weight:bold; color:var(--bot-primary);">${bestMove}</span>`;
        let wcHtml = "";
        if (winChance) wcHtml = `<span style="color:#aaa; font-size:0.8em;">(${Math.round(winChance)}%)</span>`;
        else if (depth) wcHtml = `<span style="font-size:0.8em; color:#aaa;">(D${depth})</span>`;
        state.lastLiveResult = `
            <div style="display:flex; justify-content:space-between; align-items:center; font-weight:bold;">
                <div style="display:flex; align-items:center; gap: 8px;">
                    <span style="color:var(--bot-primary); font-size:1.1em;">${scoreTxt}</span>
                    <span style="font-size:0.85em; color:${statusData.color}; font-weight:bold;">${statusData.text}</span>
                </div>
                <div>${wcHtml} ${durHtml}</div>
            </div>
            <div style="margin-top:5px; font-size:0.85em; color:#bbb; width:100%; max-width:100%; box-sizing:border-box; word-wrap:break-word; overflow-wrap:anywhere; white-space:normal;">
                <span style="color:#888;">PV:</span> ${pvStr}
            </div>`;
        if (settings.autoMove) triggerAutoMove();
    }
    function triggerAutoMove() {
        if (!state.currentBestMove || !state.board?.game) return;
        const turn = state.board.game.getTurn();
        const playingAs = state.board.game.getPlayingAs();
        if (turn !== playingAs) return;
        const wait = Math.max(0, state.moveTargetTime - performance.now());
        setTimeout(() => playMove(state.currentBestMove), wait);
    }
    function handleError(type, err) {
        state.isThinking = !1;
        console.error(type, err);
        state.lastResponse = `${type}: ${err?.message || err}`;
        state.lastMoveResult = `❌ ${type}`;
        updateUI();
    }
    function playMove(move) {
        if (!state.board?.game) return;
        const from = move.substring(0, 2), to = move.substring(2, 4);
        const currentRaw = getRawBoardFEN();
        if (currentRaw && sanitizeFEN(currentRaw).split(" ")[0] !== state.lastSentFEN.split(" ")[0]) return;
        for (const m of state.board.game.getLegalMoves()) {
            if (m.from === from && m.to === to) {
                const promotion = move.length > 4 ? move.substring(4, 5) : "q";
                state.board.game.move({ ...m, promotion, animate: !0, userGenerated: !0 });
                return;
            }
        }
    }
    function toggleAutoQueue() {
        if (state.newGameObserver) { state.newGameObserver.disconnect(); state.newGameObserver = null; }
        if (state.queueTimeout) { clearTimeout(state.queueTimeout); state.queueTimeout = null; }
        if (settings.autoQueue) {
            state.newGameObserver = new MutationObserver(() => {
                const btns = Array.from(document.querySelectorAll("button"));
                const newGameBtn = btns.find((b) => {
                    const txt = b.innerText.toLowerCase();
                    return txt.includes("new") && !txt.includes("rematch") && b.offsetParent !== null;
                });
                if (newGameBtn && !state.queueTimeout) {
                    state.queueTimeout = setTimeout(() => { newGameBtn.click(); state.queueTimeout = null; }, 100);
                }
            });
            state.newGameObserver.observe(document.body, { childList: !0, subtree: !0 });
        }
    }
    function resetSettings() {
        const currentModel = settings.engineMode;
        Object.assign(settings, DEFAULT_SETTINGS);
        settings.engineMode = currentModel;
        Object.keys(DEFAULT_SETTINGS).forEach((k) => { if (k !== "engineMode") saveSetting(k, DEFAULT_SETTINGS[k]); });
        saveSetting("engineMode", currentModel);
        const hsl = rgbToHsl(...Object.values(hexToRgb(settings.highlightColor)));
        state.h = hsl.h; state.s = hsl.s; state.l = hsl.l;
        toggleAutoQueue();
        createUI();
        applyMenuPosition();
    }
    function syncColor() {
        const rgb = hslToRgb(state.h, state.s, state.l);
        const hex = rgbToHex(rgb.r, rgb.g, rgb.b);
        settings.highlightColor = hex;
        saveSetting("highlightColor", hex);
        if (state.ui.inpR) {
            state.ui.inpR.value = rgb.r; state.ui.inpG.value = rgb.g; state.ui.inpB.value = rgb.b;
            state.ui.inpHex.value = hex;
            state.ui.colorPreview.style.background = hex;
            state.ui.sliderH.value = state.h; state.ui.sliderS.value = state.s; state.ui.sliderL.value = state.l;
            if (state.ui.sliderHNum) state.ui.sliderHNum.value = Math.round(state.h);
            if (state.ui.sliderSNum) state.ui.sliderSNum.value = Math.round(state.s);
            if (state.ui.sliderLNum) state.ui.sliderLNum.value = Math.round(state.l);
        }
        Visuals.removeByType('history');
        if (state.currentBestMove) Visuals.add(state.currentBestMove, 'history');
    }
    function applyTheme() {
        const modals = [state.ui.panel, state.ui.modal, state.ui.histModal, state.ui.localModal];
        modals.forEach(m => {
            if (!m) return;
            m.style.setProperty("--bot-bg", settings.themeBg);
            m.style.setProperty("--bot-t", settings.themeText);
            m.style.setProperty("--bot-b", settings.themeBorder);
            m.style.setProperty("--bot-p", settings.themePrimary);
            m.style.color = settings.themeText;
            if (m === state.ui.panel) {
                m.style.opacity = settings.menuOpacity;
            } else {
                const overlayId = m.id === "modal" ? "modalOv" : m.id === "histModal" ? "histModalOv" : "localModalOv";
                const overlay = document.getElementById(overlayId);
                if (overlay) overlay.style.opacity = "1";
                m.style.opacity = settings.menuOpacity;
            }
        });
    }
    function applyMenuPosition() {
        const p = state.ui.panel;
        if (!p) return;
        const margin = "10px";
        p.style.transform = "none";
        p.style.top = ""; p.style.bottom = ""; p.style.left = ""; p.style.right = "";
        if (settings.menuPosition === "custom") {
            const savedX = GM_getValue("bot_pX", "auto");
            const savedY = GM_getValue("bot_pY", "0");
            if (savedX === "auto") { p.style.right = "0px"; p.style.left = "auto"; }
            else p.style.left = savedX + "px";
            p.style.top = savedY + "px";
            const rect = p.getBoundingClientRect();
            if (rect.left < 0) p.style.left = "0px";
            if (rect.top < 0) p.style.top = "0px";
            if (rect.right > window.innerWidth) p.style.left = (window.innerWidth - rect.width) + "px";
            if (rect.bottom > window.innerHeight) p.style.top = (window.innerHeight - rect.height) + "px";
        } else {
            switch (settings.menuPosition) {
                case "top-left": p.style.top = margin; p.style.left = margin; break;
                case "top-right": p.style.top = margin; p.style.right = margin; break;
                case "bottom-left": p.style.bottom = margin; p.style.left = margin; break;
                case "bottom-right": p.style.bottom = margin; p.style.right = margin; break;
            }
        }
    }

    function updateLocalSettingsUI() {
        const statusEl = document.getElementById("localEngineStatus");
        const statusMsgEl = document.getElementById("localEngineStatusMsg");
        const btnInstall = document.getElementById("btnLocalInstall");
        const btnReinstall = document.getElementById("btnLocalReinstall");
        const btnUninstall = document.getElementById("btnLocalUninstall");
        if (!statusEl) return;
        const statusMap = {
            not_installed: { text: "❌ Not Installed", color: "#ff5555" },
            loading:       { text: "⏳ Loading...",    color: "#ffaa00" },
            ready:         { text: "✅ Ready",          color: "#81b64c" },
            error:         { text: "⚠️ Error",          color: "#ff7777" },
        };
        const s = statusMap[state.engineStatus] || statusMap.not_installed;
        statusEl.textContent = s.text;
        statusEl.style.color = s.color;
        if (statusMsgEl) statusMsgEl.textContent = state.engineStatusMsg;
        const isLoading = state.engineStatus === "loading";
        const isReady   = state.engineStatus === "ready";
        if (btnInstall)   btnInstall.disabled   = isReady || isLoading;
        if (btnReinstall) btnReinstall.disabled  = isLoading;
        if (btnUninstall) btnUninstall.disabled  = !isReady && !isLoading;
    }

    function createUI() {
        if (document.getElementById("enginePanel")) document.getElementById("enginePanel").remove();
        if (document.getElementById("modalOv")) document.getElementById("modalOv").remove();
        if (document.getElementById("histModalOv")) document.getElementById("histModalOv").remove();
        if (document.getElementById("localModalOv")) document.getElementById("localModalOv").remove();
        if (document.getElementById("fenTooltip")) document.getElementById("fenTooltip").remove();
        loadSettings();
        const initHsl = rgbToHsl(...Object.values(hexToRgb(settings.highlightColor)));
        state.h = initHsl.h; state.s = initHsl.s; state.l = initHsl.l;
        const savedW = GM_getValue("bot_panelW", "25vw");
        const savedH = GM_getValue("bot_panelH", "50vh");
        const isMini = GM_getValue("bot_isMini", false);
        const S  = "#enginePanel";
        const SM = "#modal";
        const SH = "#histModal";
        const SL = "#localModal";
        const SO = "#modalOv, #histModalOv, #localModalOv";
        const style = `
            ${S} { --bot-bg:${settings.themeBg}; --bot-b:${settings.themeBorder}; --bot-p:${settings.themePrimary}; --bot-t:${settings.themeText}; }
            ${SM} { --bot-bg:${settings.themeBg}; --bot-b:${settings.themeBorder}; --bot-p:${settings.themePrimary}; --bot-t:${settings.themeText}; }
            ${SH} { --bot-bg:${settings.themeBg}; --bot-b:${settings.themeBorder}; --bot-p:${settings.themePrimary}; --bot-t:${settings.themeText}; }
            ${SL} { --bot-bg:${settings.themeBg}; --bot-b:${settings.themeBorder}; --bot-p:${settings.themePrimary}; --bot-t:${settings.themeText}; }
            ${S} * { box-sizing: border-box; } ${SM} * { box-sizing: border-box; }
            ${SH} * { box-sizing: border-box; } ${SL} * { box-sizing: border-box; }
            ${S} {
                position:fixed; width:${savedW}; height:${savedH};
                min-width:300px; min-height:300px;
                background:var(--bot-bg); border:1px solid var(--bot-b);
                color:var(--bot-t); z-index:9999; font-family:sans-serif;
                box-shadow:-4px 0 15px rgba(0,0,0,0.5); font-size:14px;
                display:flex; flex-direction:column; resize:both; overflow:hidden;
                opacity: ${settings.menuOpacity};
            }
            ${S}.minified {
                width: 34px !important; height: 34px !important;
                resize: none; min-height: 0 !important; min-width: 0 !important;
                overflow: hidden !important; border: 1px solid var(--bot-b);
                background: var(--bot-p); padding: 0; display: flex !important;
                align-items: center !important; justify-content: center !important;
                cursor: pointer; left: auto !important; top: 0 !important; right: 0 !important;
                border-radius: 4px;
            }
            ${S}.minified #panelContent,
            ${S}.minified #panelHeader > *:not(#minBtn) { display: none !important; }
            ${S}.minified #minBtn {
                width: 100% !important; height: 100% !important;
                display: flex !important; justify-content: center !important; align-items: center !important;
                padding: 0 !important; margin: 0 !important;
            }
            ${S}.minified #minBtn img { width: 28px !important; height: 28px !important; display: block; }
            #panelHeader {
                background:var(--bot-p); color:#000; padding:10px; font-weight:bold;
                display:flex; justify-content:space-between; align-items:center;
                cursor:move; flex:none; user-select:none; height:38px;
            }
            #panelContent { padding:15px; display:flex; flex-direction:column; gap:10px; overflow-y:auto; flex:1; min-height: 0; }
            ${S} .sect { border-top:1px solid #333; padding-top:10px; display:flex; flex-direction:column; gap:8px; }
            ${SM} .sect, ${SL} .sect { border-top:1px solid #333; padding-top:10px; display:flex; flex-direction:column; gap:8px; }
            ${S} .sect-title { font-size:0.85em; color:#aaa; font-weight:bold; text-transform:uppercase; margin-bottom:4px; }
            ${SM} .sect-title, ${SL} .sect-title { font-size:0.85em; color:#aaa; font-weight:bold; text-transform:uppercase; margin-bottom:4px; }
            ${S} .row { display:flex; justify-content:space-between; align-items:center; gap:10px; margin-bottom:6px; }
            ${SM} .row, ${SL} .row { display:flex; justify-content:space-between; align-items:center; gap:10px; margin-bottom:6px; }
            ${S} input, ${S} select,
            ${SM} input, ${SM} select,
            ${SH} input, ${SH} select,
            ${SL} input, ${SL} select {
                background:rgba(0,0,0,0.2); color:var(--bot-t);
                border:1px solid var(--bot-b); padding:4px; border-radius:4px;
            }
            ${S} input[type="number"], ${SM} input[type="number"], ${SH} input[type="number"], ${SL} input[type="number"] { width: 60px; }
            ${S} select, ${SM} select, ${SH} select, ${SL} select { width: 120px; }
            ${S} input[type="text"], ${SM} input[type="text"], ${SL} input[type="text"] { flex:1; }
            ${S} input[type=range], ${SM} input[type=range] {
                -webkit-appearance: none; width: 100%;
                background: transparent; padding: 0; margin: 0; border: none;
            }
            ${S} input[type=range]:focus, ${SM} input[type=range]:focus { outline: none; }
            ${S} input[type=range]::-webkit-slider-runnable-track,
            ${SM} input[type=range]::-webkit-slider-runnable-track {
                width: 100%; height: 6px; cursor: pointer;
                background: var(--bot-b); border-radius: 3px;
            }
            ${S} input[type=range]::-webkit-slider-thumb,
            ${SM} input[type=range]::-webkit-slider-thumb {
                height: 16px; width: 16px; border-radius: 50%;
                background: var(--bot-t); cursor: pointer;
                -webkit-appearance: none; margin-top: -5px;
                border: 1px solid rgba(0,0,0,0.3);
                box-shadow: 0 1px 3px rgba(0,0,0,0.3);
            }
            #sliderH { background: linear-gradient(to right, #f00, #ff0, #0f0, #0ff, #00f, #f0f, #f00) !important; }
            #sliderH::-webkit-slider-thumb { background: #fff !important; border: 1px solid #000 !important; }
            ${S} button, ${SM} button, ${SH} button, ${SL} button {
                background:var(--bot-p); border:none; padding:10px;
                color:#000; font-weight:bold; cursor:pointer; border-radius:4px;
            }
            ${S} button:disabled, ${SM} button:disabled, ${SL} button:disabled { opacity:0.6; cursor:not-allowed; }
            #custBtn  { background: #4fc3f7 !important; color: #000 !important; margin-top: 5px; }
            #histBtn  { background: #b39ddb !important; color: #000 !important; margin-top: 5px; }
            #localBtn { background: #ffcc80 !important; color: #000 !important; margin-top: 5px; }
            .log-box {
                background:rgba(0,0,0,0.5); padding:8px; font-family:monospace; font-size:0.75em; border-radius:4px;
                overflow-y:auto; word-break:break-all; white-space:pre-wrap; border:1px solid var(--bot-b); height:100px; resize:vertical;
                user-select: text !important; -webkit-user-select: text !important; cursor: text;
            }
            #statusBox { background:rgba(0,0,0,0.2); padding:8px; border:1px solid #00bcd4; border-radius:4px; font-size:0.9em; min-height:40px; width:100%; flex-shrink:0; display:flex; flex-direction:column; gap:5px; }
            ${SO} { position:fixed; top:0; left:0; width:100%; height:100%; background:rgba(0,0,0,0.8); z-index:10000; display:none; justify-content:center; align-items:center; }
            ${SM}, ${SH}, ${SL} { background:var(--bot-bg); padding:0; border-radius:8px; width:480px; border:1px solid var(--bot-b); display:flex; flex-direction:column; max-height:90vh; opacity: ${settings.menuOpacity}; }
            ${SH} { width: 600px; height: 600px; }
            ${SM} * { color:var(--bot-t); }
            ${SL} * { color:var(--bot-t); }
            ${SM} label, ${SH} label, ${SL} label { opacity:1 !important; font-weight:600; font-size:0.9em; }
            ${SM} input[type="color"], ${SH} input[type="color"] { height:24px; padding:0; width:40px; cursor:pointer; border:none; }
            ${SM} select, ${SH} select, ${SL} select { height:24px; padding:0 4px; font-size:0.9em; }
            ${S} .show-cloud { display: none; } ${S} .show-local { display: none; }
            body.mode-cloud ${S} .show-cloud { display: flex; }
            body.mode-local ${S} .show-local { display: flex; }
            ${SM} .rgb-inputs, ${S} .rgb-inputs { display:flex; gap:5px; flex:1; justify-content:flex-end; }
            ${SM} .rgb-inputs input, ${S} .rgb-inputs input { width:45px; text-align:center; }
            #histTableContainer { flex:1; overflow-y:auto; border:1px solid #444; border-radius:4px; margin-top:10px; }
            #histTable { width:100%; border-collapse:collapse; font-size:0.85em; }
            #histTable th { background:var(--bot-b); color:var(--bot-p); position:sticky; top:0; z-index:1; }
            #histTable th, #histTable td { border-bottom:1px solid var(--bot-b); padding:6px; text-align:left; color:var(--bot-t); }
            #histTable tr:hover { background:var(--bot-b); filter:brightness(1.2); }
            .hist-win { color:#81b64c; font-weight:bold; } .hist-loss { color:#ff5555; font-weight:bold; } .hist-draw { color:#aaaaaa; font-weight:bold; }
            .hist-fen { max-width:100px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; cursor:pointer; color:#888; text-decoration:underline dotted; }
            .btn-del { background:#ff5555 !important; color:white !important; padding:2px 6px; border-radius:3px; font-size:0.7em; cursor:pointer; border:none; }
            .hist-controls { display:flex; justify-content:space-between; align-items:center; margin-top:10px; }
            #histEmpty { padding:20px; text-align:center; color:#888; }
            #fenTooltip { position:fixed; border:3px solid #333; background:#222; z-index:10001; display:none; pointer-events:none; box-shadow:0 4px 15px rgba(0,0,0,0.5); }
            .fen-board { display:grid; grid-template-columns:repeat(8, 1fr); width:240px; height:240px; border:2px solid #555; }
            .fen-sq { width:30px; height:30px; display:flex; justify-content:center; align-items:center; background-size:100%; background-repeat:no-repeat; }
            .fen-sq.light { background-color:#eeeed2; } .fen-sq.dark { background-color:#769656; }
            .modal-header { display:flex; justify-content:space-between; align-items:center; padding:15px; border-bottom:1px solid var(--bot-b); }
            .modal-tabs { display:flex; border-bottom:1px solid var(--bot-b); }
            ${SM} .tab-btn { flex:1; background:transparent !important; border:none !important; padding:10px; color:var(--bot-t); cursor:pointer; opacity:0.7; border-bottom:2px solid transparent !important; }
            ${SM} .tab-btn.active { opacity:1; border-bottom:2px solid var(--bot-p) !important; font-weight:bold; }
            .modal-content { padding:15px; overflow-y:auto; flex:1; }
            ${S} .slider-group, ${SM} .slider-group { display:flex; align-items:center; gap:8px; flex:1; justify-content:flex-end; }
            ${S} .slider-group input[type=range], ${SM} .slider-group input[type=range] { flex:1; }
            ${S} .slider-group input[type=number], ${SM} .slider-group input[type=number] { width:45px; text-align:center; }
            .adv-toggle { cursor:pointer; font-size:0.8em; color:var(--bot-p); text-decoration:underline; margin-top:5px; display:inline-block; }
            ${SM} .modal-content .row { display:flex; align-items:center; margin-bottom:12px; }
            ${SM} .modal-content .row label { flex:0 0 120px; text-align:left; font-weight:600; }
            ${SM} .modal-content .row > input[type="text"],
            ${SM} .modal-content .row > input[type="color"],
            ${SM} .modal-content .row > select { flex:1; }
            .adv-sect { margin-top:10px; padding-left:10px; border-left:2px solid var(--bot-b); display:flex; flex-direction:column; gap:8px; }
            .theme-presets { display:flex; gap:10px; margin-bottom:10px; }
            ${SM} .theme-btn { flex:1; padding:5px; border:1px solid var(--bot-b) !important; cursor:pointer; background:rgba(0,0,0,0.2) !important; color:var(--bot-t) !important; }
            /* Local Settings modal specific */
            #localEngineStatus { font-weight:bold; font-size:1em; }
            #localEngineStatusMsg { font-size:0.8em; color:#aaa; margin-top:2px; min-height:14px; }
            .local-action-btn { padding:8px 14px !important; font-size:0.85em !important; }
            .local-btn-install   { background:#27ae60 !important; color:#fff !important; }
            .local-btn-reinstall { background:#2980b9 !important; color:#fff !important; }
            .local-btn-uninstall { background:#c0392b !important; color:#fff !important; }
            ${SL} .info-box { background:rgba(0,0,0,0.25); border:1px solid var(--bot-b); border-radius:4px; padding:8px 10px; font-size:0.82em; font-family:monospace; color:#bbb; word-break:break-all; }
            ${SL} input[type="text"] { width:100%; font-size:0.85em; }
            ${SL} select { width:100%; }
        `;
        const fullHTML = `<style>${style}</style>` + `
            <div id="enginePanel" class="${isMini ? "minified" : ""}">
                <div id="panelHeader">
                    <div style="display:flex; align-items:center; gap:5px;">
                        <span>Menu</span>
                        <span id="minBtn" style="cursor:pointer; display:flex; align-items:center; justify-content:center; width:100%; height:100%;">${isMini ? `<img src="${STOCKFISH_ICON}">` : "▼"}</span>
                    </div>
                    <button id="btnReset" style="padding:2px 8px; font-size:0.8em; background:#0002; color:#000; cursor:pointer;">Reset Defaults</button>
                </div>
                <div id="panelContent">
                    <div id="statusBox">${state.lastLiveResult}</div>
                    <div id="moveResult" style="background:rgba(0,0,0,0.2); padding:5px; border-radius:4px; text-align:center;">${state.lastMoveResult}</div>
                    <div class="sect">
                        <div class="sect-title">Engine Config</div>
                        <div class="row">
                            <label>Model</label>
                            <select id="selMode" style="width:240px;">
                                <option value="cloud">SF 18.0.0 (cloud 0.25-0.48s)</option>
                                <option value="sfonline">SF 17.1.0 (cloud 0.15-11.0s)</option>
                                <option value="local">SF 18.0.5 (local 0.0-28.0s)</option>
                            </select>
                        </div>
                        <div class="row"><label>Depth (Max <span id="lblMaxDepth">18</span>)</label><input type="number" id="inpDepth" min="1" max="18" value="${settings.depth}"></div>
                        <div class="row show-cloud"><label>Max Time (ms)</label><input type="number" id="inpTime" value="${settings.maxThinkingTime}"></div>
                        <div class="row show-cloud"><label>Search</label><input type="text" id="inpSearch" value="${settings.searchMoves}"></div>
                    </div>
                    <div class="sect">
                        <div class="sect-title" style="display:flex; justify-content:space-between; align-items:center;">
                            PV Display
                            <input type="checkbox" id="chkPV" ${settings.showPVArrows ? "checked" : ""}>
                        </div>
                        <div id="pvSettings" style="display:none;">
                            <div class="row"><label>Depth (1-45)</label><div class="slider-group"><input type="range" id="inpPVDepth" min="1" max="45" step="1" value="${settings.pvDepth}"><input type="number" id="inpPVDepthNum" min="1" max="45" value="${settings.pvDepth}"></div></div>
                            <div class="row" style="padding-top:3px;"><label>Show Numbers</label><input type="checkbox" id="chkPVNums" ${settings.pvShowNumbers ? "checked" : ""}></div>
                            <div class="row" style="padding-top:5px;"><label>Custom Gradient</label><input type="checkbox" id="chkPVGrad" ${settings.pvCustomGradient ? "checked" : ""}></div>
                            <div id="pvGradSettings" style="display:none; padding-left:10px; border-left:2px solid #333; margin-top:5px;">
                                <div class="row"><label>Start Color</label><input type="color" id="inpPVStart" value="${settings.pvStartColor}"></div>
                                <div class="row"><label>End Color</label><input type="color" id="inpPVEnd" value="${settings.pvEndColor}"></div>
                            </div>
                        </div>
                    </div>
                    <div class="sect">
                        <div class="sect-title">Automation</div>
                        <div class="row">
                            <label><input type="checkbox" id="chkRun" ${settings.autoRun ? "checked" : ""}> Auto-Analyze</label>
                            <label><input type="checkbox" id="chkMove" ${settings.autoMove ? "checked" : ""}> Auto-Move</label>
                            <label><input type="checkbox" id="chkQueue" ${settings.autoQueue ? "checked" : ""}> Auto-Queue</label>
                        </div>
                        <div class="row"><label>Randomized Delay (s)</label><div style="display:flex; gap:5px;"><input type="number" id="inpMin" style="width:50px" value="${settings.minDelay}"><span>-</span><input type="number" id="inpMax" style="width:50px" value="${settings.maxDelay}"></div></div>
                        <div style="font-size:0.7em; color:#888; text-align:right;" id="delayDisplay">Next: N/A</div>
                    </div>
                    <button id="btnAnalyze">Analyze</button>
                    <button id="custBtn">Visuals &amp; Theme</button>
                    <button id="histBtn">Game History</button>
                    <button id="localBtn">Local Settings</button>
                    <div class="sect">
                         <div class="row"><label style="cursor:pointer"><input type="checkbox" id="chkDebug" ${settings.debugLogs ? "checked" : ""}> Show Debug Logs</label></div>
                         <div id="debugArea" style="display:${settings.debugLogs ? "block" : "none"}">
                             <div class="log-box" id="sentCommandOutput"></div>
                             <div class="log-box" id="receivedMessageOutput"></div>
                         </div>
                    </div>
                </div>
            </div>

            <div id="modalOv">
                <div id="modal">
                    <div class="modal-header">
                        <h3 style="margin:0; color:var(--bot-p);">Settings</h3>
                        <button id="modalClose" style="padding:2px 8px; font-weight:bold; cursor:pointer;">×</button>
                    </div>
                    <div class="modal-tabs">
                        <button class="tab-btn active" id="tabMove">Move Display</button>
                        <button class="tab-btn" id="tabTheme">Menu Theme</button>
                    </div>
                    <div class="modal-content" id="tabContentMove">
                         <div class="sect" style="border:none; padding:0;">
                             <div class="row" style="margin-bottom: 20px;"><label>Visual Type</label><select id="visType" style="width:120px; height:24px;"><option value="boxes">Boxes</option><option value="arrow">Arrow</option><option value="outline">Outline</option></select></div>
                             <div class="row" style="margin-bottom: 12px;">
                                 <label>Display Duration</label>
                                 <div class="slider-group">
                                     <input type="range" id="visDuration" min="0" max="100" step="1" value="100">
                                     <span id="visDurationText" style="width:70px; text-align:right; font-size:0.9em; font-family:monospace;">Forever</span>
                                 </div>
                             </div>
                             <div class="row" id="rowFadeOut" style="display:none; margin-bottom: 12px;">
                                 <label>Fade Out</label>
                                 <div style="flex:1; display:flex; align-items:center;">
                                     <input type="checkbox" id="chkFadeOut" style="width: 18px; height: 18px;">
                                 </div>
                             </div>
                             <div class="row" style="margin-bottom: 12px;">
                                 <label>Hide After Move</label>
                                 <div style="flex:1; display:flex; align-items:center;">
                                     <input type="checkbox" id="chkHideAfterMove" style="width: 18px; height: 18px;" ${settings.hideAfterMove ? "checked" : ""}>
                                 </div>
                             </div>
                         </div>
                         <div class="sect">
                            <div class="sect-title">Basic Settings</div>
                            <div style="display:flex; flex-direction:column; gap:10px;">
                                <div class="row" style="width:100%; margin-bottom: 10px;">
                                    <div id="colorPreview" style="width:30px; height:30px; border-radius:50%; border:2px solid #555; background:${settings.highlightColor}; flex:0 0 30px;"></div>
                                    <div class="rgb-inputs">
                                       <input type="number" id="inpR" min="0" max="255" placeholder="R">
                                       <input type="number" id="inpG" min="0" max="255" placeholder="G">
                                       <input type="number" id="inpB" min="0" max="255" placeholder="B">
                                    </div>
                                </div>
                                <div class="row"><label>Hue</label><div class="slider-group"><input type="range" id="sliderH" min="0" max="360" value="${state.h}"><input type="number" id="sliderHNum" min="0" max="360" value="${Math.round(state.h)}"></div></div>
                                <div class="row"><label>Saturation</label><div class="slider-group"><input type="range" id="sliderS" min="0" max="100" value="${state.s}"><input type="number" id="sliderSNum" min="0" max="100" value="${Math.round(state.s)}"><span>%</span></div></div>
                                <div class="row"><label>Brightness</label><div class="slider-group"><input type="range" id="sliderL" min="0" max="100" value="${state.l}"><input type="number" id="sliderLNum" min="0" max="100" value="${Math.round(state.l)}"><span>%</span></div></div>
                                <div class="row" style="width:100%; margin-top:5px; margin-bottom: 20px;"><label>Hex</label><input type="text" id="inpHex" style="text-transform:uppercase; text-align:center;"></div>
                            </div>
                         </div>
                         <div class="sect">
                             <div class="adv-toggle" id="advToggle">▼ Advanced Visual Settings</div>
                             <div class="adv-sect" id="advSect" style="display:none;">
                                 <div id="visBoxSettings">
                                     <div class="row"><label>Inner Opacity</label><div class="slider-group"><input type="range" id="visInnerOp" min="0" max="1" step="0.01" value="${settings.innerOpacity}"><input type="number" id="visInnerOpNum" min="0" max="100" value="${Math.round(settings.innerOpacity*100)}"><span>%</span></div></div>
                                     <div class="row"><label>Outer Opacity</label><div class="slider-group"><input type="range" id="visOuterOp" min="0" max="1" step="0.01" value="${settings.outerOpacity}"><input type="number" id="visOuterOpNum" min="0" max="100" value="${Math.round(settings.outerOpacity*100)}"><span>%</span></div></div>
                                     <div class="row"><label>Gradient Bias</label><div class="slider-group"><input type="range" id="visBias" min="0" max="100" step="1" value="${settings.gradientBias}"><input type="number" id="visBiasNum" min="0" max="100" value="${settings.gradientBias}"><span>%</span></div></div>
                                 </div>
                                 <div id="visArrowSettings" style="display:none;">
                                     <div class="row"><label>Arrow Opacity</label><div class="slider-group"><input type="range" id="visArrowOp" min="0" max="1" step="0.01" value="${settings.arrowOpacity}"><input type="number" id="visArrowOpNum" min="0" max="100" value="${Math.round(settings.arrowOpacity*100)}"><span>%</span></div></div>
                                     <div class="row"><label>Arrow Width</label><div class="slider-group"><input type="range" id="visArrowWidth" min="5" max="50" step="1" value="${settings.arrowWidth}"><input type="number" id="visArrowWidthNum" min="5" max="50" value="${settings.arrowWidth}"><span>px</span></div></div>
                                 </div>
                                 <div id="visOutlineSettings" style="display:none;">
                                     <div class="row"><label>Line Opacity</label><div class="slider-group"><input type="range" id="visOutOp" min="0" max="1" step="0.01" value="${settings.visualOutlineOpacity}"><input type="number" id="visOutOpNum" min="0" max="100" value="${Math.round(settings.visualOutlineOpacity*100)}"><span>%</span></div></div>
                                     <div class="row"><label>Line Width</label><div class="slider-group"><input type="range" id="visOutWidth" min="1" max="10" step="1" value="${settings.visualOutlineWidth}"><input type="number" id="visOutWidthNum" min="1" max="10" value="${settings.visualOutlineWidth}"><span>px</span></div></div>
                                     <div class="row"><label>Glow Effect</label><input type="checkbox" id="visOutGlow" ${settings.visualOutlineGlow ? "checked" : ""}></div>
                                     <div class="row"><label>Glow Radius</label><div class="slider-group"><input type="range" id="visOutGlowRad" min="1" max="50" step="1" value="${settings.visualOutlineGlowRadius}"><input type="number" id="visOutGlowRadNum" min="1" max="50" value="${settings.visualOutlineGlowRadius}"><span>px</span></div></div>
                                 </div>
                             </div>
                         </div>
                    </div>
                    <div class="modal-content" id="tabContentTheme" style="display:none;">
                        <div class="theme-presets">
                            <button class="theme-btn" id="btnThemeDark">Dark Mode</button>
                            <button class="theme-btn" id="btnThemeLight">Light Mode</button>
                        </div>
                        <div class="sect">
                            <div class="sect-title">Menu Position</div>
                            <div class="row">
                                <label>Panel Position</label>
                                <select id="selMenuPos">
                                    <option value="custom">Custom (Drag)</option>
                                    <option value="top-left">Top Left</option>
                                    <option value="top-right">Top Right</option>
                                    <option value="bottom-left">Bottom Left</option>
                                    <option value="bottom-right">Bottom Right</option>
                                </select>
                            </div>
                        </div>
                        <div class="sect">
                            <div class="row" style="margin-bottom: 20px;"><label>Menu Opacity</label><div class="slider-group"><input type="range" id="inpMenuOp" min="0.1" max="1" step="0.01" value="${settings.menuOpacity}"><input type="number" id="inpMenuOpNum" min="10" max="100" value="${Math.round(settings.menuOpacity*100)}"><span>%</span></div></div>
                        </div>
                        <div class="sect">
                            <div class="sect-title">Custom Colors</div>
                            <div class="row"><label>Background</label><input type="color" id="colBg" value="${settings.themeBg}"></div>
                            <div class="row"><label>Text Color</label><input type="color" id="colTxt" value="${settings.themeText}"></div>
                            <div class="row"><label>Border Color</label><input type="color" id="colBorder" value="${settings.themeBorder}"></div>
                            <div class="row"><label>Primary/Accent</label><input type="color" id="colPrim" value="${settings.themePrimary}"></div>
                        </div>
                    </div>
                </div>
            </div>

            <div id="histModalOv">
                <div id="histModal">
                    <div class="modal-header">
                        <h3 style="margin:0; color:#8e44ad;">Game History</h3>
                        <button id="histModalClose" style="padding:2px 8px; font-weight:bold; cursor:pointer;">×</button>
                    </div>
                    <div id="histTableContainer" style="padding:0 15px;">
                        <table id="histTable">
                            <thead>
                                <tr><th>Date</th><th>Color</th><th>Result</th><th>Clock</th><th>FEN</th><th></th></tr>
                            </thead>
                            <tbody id="histBody"></tbody>
                        </table>
                    </div>
                    <div class="hist-controls" style="padding:15px;">
                        <label><input type="checkbox" id="chkHistory" ${settings.enableHistory ? "checked" : ""}> Recording Enabled</label>
                        <button id="btnClearHist" style="background:#ff5555 !important; padding:5px 10px; color:white !important; font-size:0.8em;">Delete All</button>
                    </div>
                </div>
            </div>

            <div id="localModalOv">
                <div id="localModal">
                    <div class="modal-header">
                        <h3 style="margin:0; color:#e67e22;">Local Engine Settings</h3>
                        <button id="localModalClose" style="padding:2px 8px; font-weight:bold; cursor:pointer;">×</button>
                    </div>
                    <div class="modal-content" style="display:flex; flex-direction:column; gap:0;">

                        <div class="sect" style="border:none; padding:10px 0 0 0;">
                            <div class="sect-title">Engine Status</div>
                            <div id="localEngineStatus" style="font-weight:bold; font-size:1em;">❌ Not Installed</div>
                            <div id="localEngineStatusMsg" style="font-size:0.8em; color:#aaa; margin-top:2px; min-height:14px;"></div>
                            <div style="display:flex; gap:8px; margin-top:10px; flex-wrap:wrap;">
                                <button id="btnLocalInstall" class="local-action-btn local-btn-install">Install / Load</button>
                                <button id="btnLocalReinstall" class="local-action-btn local-btn-reinstall">Reinstall (Force Re-download)</button>
                                <button id="btnLocalUninstall" class="local-action-btn local-btn-uninstall">Uninstall</button>
                            </div>
                        </div>

                        <div class="sect">
                            <div class="sect-title">WASM Source</div>
                            <div class="row" style="flex-direction:column; align-items:flex-start; gap:6px;">
                                <label style="font-size:0.85em;">Preset</label>
                                <select id="localWasmPreset" style="width:100%;">
                                    ${WASM_PRESETS.map(p => `<option value="${p.url}"${settings.localWasmUrl === p.url ? " selected" : ""}>${p.label}</option>`).join("")}
                                </select>
                            </div>
                            <div class="row" style="flex-direction:column; align-items:flex-start; gap:6px; margin-top:6px;" id="localCustomUrlRow">
                                <label style="font-size:0.85em;">Custom WASM URL</label>
                                <input type="text" id="localWasmUrl" value="${settings.localWasmUrl}" placeholder="https://...wasm" style="width:100%;font-size:0.82em;">
                            </div>
                            <div style="font-size:0.75em; color:#888; margin-top:4px;">WASM is cached in IndexedDB after first download. Use Reinstall to apply a new URL.</div>
                        </div>

                        <div class="sect">
                            <div class="sect-title">Engine Options</div>
                            <div class="row"><label>Hash Size (MB)</label><input type="number" id="localHashMB" min="1" max="2048" value="${settings.localHashMB}" style="width:70px;"></div>
                            <div class="row"><label>Move Overhead (ms)</label><input type="number" id="localMoveOverhead" min="0" max="5000" value="${settings.localMoveOverhead}" style="width:70px;"></div>
                            <div class="row" style="align-items:flex-start; flex-direction:column; gap:6px;">
                                <div style="display:flex; justify-content:space-between; align-items:center; width:100%;">
                                    <label>Skill Level (0–20)</label>
                                    <input type="number" id="localSkillLevel" min="0" max="20" value="${settings.localSkillLevel}" style="width:55px;">
                                </div>
                                <input type="range" id="localSkillLevelRange" min="0" max="20" step="1" value="${settings.localSkillLevel}" style="width:100%; margin:0;">
                                <div style="font-size:0.75em; color:#888;">20 = full strength. Below 20 intentionally weakens play.</div>
                            </div>
                            <div class="row" style="margin-top:8px;">
                                <label>Limit to Elo</label>
                                <input type="checkbox" id="localLimitStrength" ${settings.localLimitStrength ? "checked" : ""} style="width:18px;height:18px;">
                            </div>
                            <div class="row" id="localEloRow" style="${settings.localLimitStrength ? "" : "display:none;"}">
                                <label>Target Elo (1320–3190)</label>
                                <input type="number" id="localElo" min="1320" max="3190" value="${settings.localElo}" style="width:70px;">
                            </div>
                            <div style="font-size:0.75em; color:#888; margin-top:2px;">Elo limit overrides Skill Level when enabled.</div>
                        </div>

                        <div class="sect">
                            <div class="adv-toggle" id="localAdvToggle" style="cursor:pointer; font-size:0.85em; color:var(--bot-p); text-decoration:underline; display:inline-block; margin-bottom:6px;">▼ Advanced Options</div>
                            <div id="localAdvSect" style="display:none; padding-left:10px; border-left:2px solid var(--bot-b); display:none; flex-direction:column; gap:10px;">
                                <div class="row" style="margin-top:8px;">
                                    <label>Show WDL in output</label>
                                    <input type="checkbox" id="localShowWDL" ${settings.localShowWDL ? "checked" : ""} style="width:18px;height:18px;">
                                </div>
                                <div style="font-size:0.75em; color:#888; margin-bottom:4px;">Adds win/draw/loss % to each info line in debug logs.</div>
                                <div class="row">
                                    <label>Min Thinking Time (ms)</label>
                                    <input type="number" id="localMinThinkingTime" min="0" max="5000" value="${settings.localMinThinkingTime}" style="width:70px;">
                                </div>
                                <div style="font-size:0.75em; color:#888;">Minimum ms engine spends per move regardless of time control.</div>
                                <div class="row">
                                    <label>Slow Mover (10–1000)</label>
                                    <input type="number" id="localSlowMover" min="10" max="1000" value="${settings.localSlowMover}" style="width:70px;">
                                </div>
                                <div style="font-size:0.75em; color:#888; margin-bottom:4px;">Lower = faster moves, higher = engine uses more of its time budget. Default 100.</div>
                            </div>
                        </div>

                        <div class="sect">
                            <div class="sect-title">JS Source (from @resource)</div>
                            <div class="info-box" id="localJsSrc">https://unpkg.com/[email protected]/bin/stockfish-18-single.js</div>
                            <div style="font-size:0.75em; color:#888; margin-top:4px;">Fixed at install time. Change in @resource header to update.</div>
                        </div>

                    </div>
                </div>
            </div>

            <div id="fenTooltip"></div>
        `;
        document.body.insertAdjacentHTML("beforeend", fullHTML);
        const panel = document.getElementById("enginePanel");
        const computed = window.getComputedStyle(panel);
        panel.style.width = computed.width;
        if (!isMini) panel.style.height = computed.height;
        state.ui = {
            panel: panel,
            header: document.getElementById("panelHeader"),
            minBtn: document.getElementById("minBtn"),
            moveResult: document.getElementById("moveResult"),
            liveOutput: document.getElementById("statusBox"),
            logSent: document.getElementById("sentCommandOutput"),
            logRec: document.getElementById("receivedMessageOutput"),
            delayDisplay: document.getElementById("delayDisplay"),
            btnAnalyze: document.getElementById("btnAnalyze"),
            selMode: document.getElementById("selMode"),
            inpDepth: document.getElementById("inpDepth"),
            inpTime: document.getElementById("inpTime"),
            inpSearch: document.getElementById("inpSearch"),
            chkRun: document.getElementById("chkRun"),
            chkMove: document.getElementById("chkMove"),
            chkQueue: document.getElementById("chkQueue"),
            chkHideAfterMove: document.getElementById("chkHideAfterMove"),
            chkPV: document.getElementById("chkPV"),
            inpPVDepth: document.getElementById("inpPVDepth"),
            inpPVDepthNum: document.getElementById("inpPVDepthNum"),
            chkPVNums: document.getElementById("chkPVNums"),
            chkPVGrad: document.getElementById("chkPVGrad"),
            inpPVStart: document.getElementById("inpPVStart"),
            inpPVEnd: document.getElementById("inpPVEnd"),
            pvSettings: document.getElementById("pvSettings"),
            pvGradSettings: document.getElementById("pvGradSettings"),
            inpMin: document.getElementById("inpMin"),
            inpMax: document.getElementById("inpMax"),
            chkDebug: document.getElementById("chkDebug"),
            debugArea: document.getElementById("debugArea"),
            btnReset: document.getElementById("btnReset"),
            lblMaxDepth: document.getElementById("lblMaxDepth"),
            custBtn: document.getElementById("custBtn"),
            histBtn: document.getElementById("histBtn"),
            localBtn: document.getElementById("localBtn"),
            modal: document.getElementById("modalOv"),
            modalClose: document.getElementById("modalClose"),
            histModal: document.getElementById("histModalOv"),
            histModalClose: document.getElementById("histModalClose"),
            histBody: document.getElementById("histBody"),
            btnClearHist: document.getElementById("btnClearHist"),
            chkHistory: document.getElementById("chkHistory"),
            localModal: document.getElementById("localModalOv"),
            localModalClose: document.getElementById("localModalClose"),
            visType: document.getElementById("visType"),
            visBoxSettings: document.getElementById("visBoxSettings"),
            visArrowSettings: document.getElementById("visArrowSettings"),
            visOutlineSettings: document.getElementById("visOutlineSettings"),
            sliderH: document.getElementById("sliderH"),
            sliderHNum: document.getElementById("sliderHNum"),
            sliderS: document.getElementById("sliderS"),
            sliderSNum: document.getElementById("sliderSNum"),
            sliderL: document.getElementById("sliderL"),
            sliderLNum: document.getElementById("sliderLNum"),
            colorPreview: document.getElementById("colorPreview"),
            inpR: document.getElementById("inpR"),
            inpG: document.getElementById("inpG"),
            inpB: document.getElementById("inpB"),
            inpHex: document.getElementById("inpHex"),
            fenTooltip: document.getElementById("fenTooltip"),
            tabMove: document.getElementById("tabMove"),
            tabTheme: document.getElementById("tabTheme"),
            tabContentMove: document.getElementById("tabContentMove"),
            tabContentTheme: document.getElementById("tabContentTheme"),
            advToggle: document.getElementById("advToggle"),
            advSect: document.getElementById("advSect"),
            visInnerOp: document.getElementById("visInnerOp"),
            visInnerOpNum: document.getElementById("visInnerOpNum"),
            visOuterOp: document.getElementById("visOuterOp"),
            visOuterOpNum: document.getElementById("visOuterOpNum"),
            visBias: document.getElementById("visBias"),
            visBiasNum: document.getElementById("visBiasNum"),
            visArrowOp: document.getElementById("visArrowOp"),
            visArrowOpNum: document.getElementById("visArrowOpNum"),
            visArrowWidth: document.getElementById("visArrowWidth"),
            visArrowWidthNum: document.getElementById("visArrowWidthNum"),
            visOutOp: document.getElementById("visOutOp"),
            visOutOpNum: document.getElementById("visOutOpNum"),
            visOutWidth: document.getElementById("visOutWidth"),
            visOutWidthNum: document.getElementById("visOutWidthNum"),
            visOutGlow: document.getElementById("visOutGlow"),
            visOutGlowRad: document.getElementById("visOutGlowRad"),
            visOutGlowRadNum: document.getElementById("visOutGlowRadNum"),
            btnThemeDark: document.getElementById("btnThemeDark"),
            btnThemeLight: document.getElementById("btnThemeLight"),
            inpMenuOp: document.getElementById("inpMenuOp"),
            inpMenuOpNum: document.getElementById("inpMenuOpNum"),
            colBg: document.getElementById("colBg"),
            colTxt: document.getElementById("colTxt"),
            colBorder: document.getElementById("colBorder"),
            colPrim: document.getElementById("colPrim"),
            selMenuPos: document.getElementById("selMenuPos")
        };
        applyMenuPosition();
        // Sync initial engine status display
        if (state.localEngine) setEngineStatus("ready", "");
        else updateLocalSettingsUI();
        // Bindings
        state.ui.selMode.value = settings.engineMode;
        state.ui.selMenuPos.value = settings.menuPosition;
        state.ui.btnAnalyze.onclick = () => analyze();
        state.ui.btnReset.onclick = resetSettings;
        state.ui.custBtn.onclick = () => (state.ui.modal.style.display = "flex");
        state.ui.modalClose.onclick = () => (state.ui.modal.style.display = "none");
        state.ui.histBtn.onclick = () => { renderHistory(); state.ui.histModal.style.display = "flex"; };
        state.ui.histModalClose.onclick = () => (state.ui.histModal.style.display = "none");
        state.ui.btnClearHist.onclick = () => { if (confirm("Delete all history?")) { state.history = []; GM_setValue("bot_history", []); renderHistory(); } };
        state.ui.localBtn.onclick = () => { updateLocalSettingsUI(); state.ui.localModal.style.display = "flex"; };
        state.ui.localModalClose.onclick = () => (state.ui.localModal.style.display = "none");

        // Local Settings bindings
        document.getElementById("btnLocalInstall").onclick = () => { loadLocalEngine(); updateLocalSettingsUI(); };
        document.getElementById("btnLocalReinstall").onclick = () => reinstallEngine();
        document.getElementById("btnLocalUninstall").onclick = () => { if (confirm("Uninstall local engine and clear WASM cache?")) uninstallEngine(); };

        const wasmPresetSel = document.getElementById("localWasmPreset");
        const wasmUrlInp = document.getElementById("localWasmUrl");
        const customUrlRow = document.getElementById("localCustomUrlRow");

        const syncCustomUrlVisibility = () => {
            const isCustom = wasmPresetSel.value === "custom";
            customUrlRow.style.display = isCustom ? "block" : "none";
            if (!isCustom) {
                wasmUrlInp.value = wasmPresetSel.value;
                saveSetting("localWasmUrl", wasmPresetSel.value);
            }
        };
        wasmPresetSel.onchange = syncCustomUrlVisibility;
        wasmUrlInp.oninput = (e) => saveSetting("localWasmUrl", e.target.value.trim());
        syncCustomUrlVisibility();

        // Helper: send option live to running engine if available
        const sendOpt = (name, val) => { if (state.localEngine) state.localEngine.postMessage(`setoption name ${name} value ${val}`); };

        document.getElementById("localHashMB").oninput = (e) => {
            const v = parseInt(e.target.value) || 64;
            saveSetting("localHashMB", v);
            sendOpt("Hash", v);
        };
        document.getElementById("localMoveOverhead").oninput = (e) => {
            const v = parseInt(e.target.value) || 100;
            saveSetting("localMoveOverhead", v);
            sendOpt("Move Overhead", v);
        };

        // Skill Level — range + number synced
        const skillNum = document.getElementById("localSkillLevel");
        const skillRange = document.getElementById("localSkillLevelRange");
        const applySkill = (v) => {
            saveSetting("localSkillLevel", v);
            sendOpt("Skill Level", v);
        };
        skillNum.oninput = (e) => { const v = Math.min(20, Math.max(0, parseInt(e.target.value) || 0)); skillRange.value = v; applySkill(v); };
        skillRange.oninput = (e) => { skillNum.value = e.target.value; applySkill(parseInt(e.target.value)); };

        // Limit Strength + Elo
        const limitChk = document.getElementById("localLimitStrength");
        const eloRow = document.getElementById("localEloRow");
        const eloInp = document.getElementById("localElo");
        limitChk.onchange = (e) => {
            saveSetting("localLimitStrength", e.target.checked);
            eloRow.style.display = e.target.checked ? "flex" : "none";
            sendOpt("UCI_LimitStrength", e.target.checked);
        };
        eloInp.oninput = (e) => {
            const v = Math.min(3190, Math.max(1320, parseInt(e.target.value) || 1320));
            saveSetting("localElo", v);
            sendOpt("UCI_Elo", v);
        };

        // Advanced toggle
        const localAdvToggle = document.getElementById("localAdvToggle");
        const localAdvSect = document.getElementById("localAdvSect");
        localAdvToggle.onclick = () => {
            const open = localAdvSect.style.display === "none" || localAdvSect.style.display === "";
            localAdvSect.style.display = open ? "flex" : "none";
            localAdvToggle.innerText = open ? "▲ Advanced Options" : "▼ Advanced Options";
        };

        document.getElementById("localShowWDL").onchange = (e) => {
            saveSetting("localShowWDL", e.target.checked);
            sendOpt("UCI_ShowWDL", e.target.checked);
        };
        document.getElementById("localMinThinkingTime").oninput = (e) => {
            const v = parseInt(e.target.value) || 20;
            saveSetting("localMinThinkingTime", v);
            sendOpt("Minimum Thinking Time", v);
        };
        document.getElementById("localSlowMover").oninput = (e) => {
            const v = Math.min(1000, Math.max(10, parseInt(e.target.value) || 100));
            saveSetting("localSlowMover", v);
            sendOpt("Slow Mover", v);
        };

        const toggleMin = () => {
            const isMini = state.ui.panel.classList.toggle("minified");
            saveSetting("isMini", isMini);
            state.ui.minBtn.innerHTML = isMini ? `<img src="${STOCKFISH_ICON}">` : "▼";
        };
        state.ui.minBtn.onclick = (e) => { e.stopPropagation(); toggleMin(); };
        state.ui.panel.onclick = (e) => { if (state.ui.panel.classList.contains("minified")) toggleMin(); };
        if (isMini) state.ui.minBtn.innerHTML = `<img src="${STOCKFISH_ICON}">`;
        const bind = (el, key, type = "val") => {
            if (!el) return;
            el.addEventListener(type === "chk" ? "change" : "input", (e) => {
                const val = type === "chk" ? e.target.checked : type === "num" ? parseFloat(e.target.value) : e.target.value;
                saveSetting(key, val);
                if (key === "autoMove" && val === !0) triggerAutoMove();
                if (key === "autoQueue") toggleAutoQueue();
                if (key === "hideAfterMove" && val === !0) { Visuals.removeByType('history'); Visuals.removeByType('analysis'); PV.clear(); }
                if (["innerOpacity","outerOpacity","gradientBias","arrowOpacity","arrowWidth","visualOutlineWidth","visualOutlineOpacity","visualOutlineGlow","visualOutlineGlowRadius"].includes(key) && state.currentBestMove) {
                    Visuals.removeByType('history');
                    Visuals.add(state.currentBestMove, 'history');
                }
                if (["themeBg","themeText","themeBorder","themePrimary","menuOpacity"].includes(key)) applyTheme();
                updateUI();
            });
        };
        const bindSlider = (rangeEl, numEl, key, isPct = false) => {
            if (!rangeEl || !numEl) return;
            rangeEl.oninput = () => {
                let val = parseFloat(rangeEl.value);
                saveSetting(key, val);
                numEl.value = isPct ? Math.round(val * 100) : val;
                if (key === "menuOpacity") applyTheme();
                if (state.currentBestMove) { Visuals.removeByType('history'); Visuals.add(state.currentBestMove, 'history'); }
            };
            numEl.oninput = () => {
                let val = parseFloat(numEl.value);
                if (isPct) val /= 100;
                saveSetting(key, val);
                rangeEl.value = val;
                if (key === "menuOpacity") applyTheme();
                if (state.currentBestMove) { Visuals.removeByType('history'); Visuals.add(state.currentBestMove, 'history'); }
            };
        };
        state.ui.selMenuPos.onchange = (e) => { saveSetting("menuPosition", e.target.value); applyMenuPosition(); };
        state.ui.header.onmousedown = (e) => {
            if (e.target.id === "minBtn" || e.target.id === "btnReset") return;
            if (state.ui.panel.classList.contains("minified")) return;
            if (settings.menuPosition !== 'custom') { saveSetting("menuPosition", 'custom'); state.ui.selMenuPos.value = 'custom'; }
            e.preventDefault();
            const startX = e.clientX - state.ui.panel.offsetLeft;
            const startY = e.clientY - state.ui.panel.offsetTop;
            const onMove = (mv) => {
                let x = mv.clientX - startX, y = mv.clientY - startY;
                x = Math.max(0, Math.min(x, window.innerWidth - state.ui.panel.offsetWidth));
                y = Math.max(0, Math.min(y, window.innerHeight - state.ui.panel.offsetHeight));
                state.ui.panel.style.left = x + "px"; state.ui.panel.style.top = y + "px";
                state.ui.panel.style.right = "auto"; state.ui.panel.style.bottom = "auto";
                saveSetting("pX", x); saveSetting("pY", y);
            };
            document.addEventListener("mousemove", onMove);
            document.onmouseup = () => document.removeEventListener("mousemove", onMove);
        };
        new ResizeObserver(() => {
            if (!state.ui.panel.classList.contains("minified")) {
                saveSetting("panelW", state.ui.panel.style.width);
                saveSetting("panelH", state.ui.panel.style.height);
            }
        }).observe(state.ui.panel);
        state.ui.selMode.onchange = (e) => { saveSetting("engineMode", e.target.value); state.isThinking = !1; if (settings.engineMode === "local") loadLocalEngine(); updateUI(); };
        state.ui.chkDebug.onchange = (e) => { saveSetting("debugLogs", e.target.checked); updateUI(); };
        const durSlider = document.getElementById("visDuration");
        const durText = document.getElementById("visDurationText");
        const rowFade = document.getElementById("rowFadeOut");
        const chkFade = document.getElementById("chkFadeOut");
        const sliderToSeconds = (val) => { if (val <= 0) return -1; if (val >= 100) return 0; return Math.round((59.9 * Math.pow((val-1)/98,2)+0.1)*10)/10; };
        const secondsToSlider = (secs) => { if (secs === -1) return 0; if (secs === 0) return 100; return Math.round(Math.sqrt((secs-0.1)/59.9)*98)+1; };
        durSlider.value = secondsToSlider(settings.visualDuration);
        chkFade.checked = settings.visualFadeOut;
        const updateDurUI = () => {
            const val = parseInt(durSlider.value);
            if (val >= 100) { durText.innerText = "Forever"; rowFade.style.display = "none"; saveSetting("visualDuration", 0); }
            else if (val <= 0) { durText.innerText = "Disabled"; rowFade.style.display = "none"; saveSetting("visualDuration", -1); }
            else { const secs = sliderToSeconds(val); durText.innerText = secs.toFixed(1) + "s"; rowFade.style.display = "flex"; saveSetting("visualDuration", secs); }
        };
        durSlider.oninput = updateDurUI;
        chkFade.onchange = (e) => saveSetting("visualFadeOut", e.target.checked);
        updateDurUI();
        state.ui.visType.onchange = (e) => { saveSetting("visualType", e.target.value); toggleVisualInputs(); Visuals.removeByType('history'); if (state.currentBestMove) Visuals.add(state.currentBestMove, 'history'); };
        function toggleVisualInputs() {
            state.ui.visBoxSettings.style.display = "none";
            state.ui.visArrowSettings.style.display = "none";
            state.ui.visOutlineSettings.style.display = "none";
            if (settings.visualType === "arrow") state.ui.visArrowSettings.style.display = "block";
            else if (settings.visualType === "outline") state.ui.visOutlineSettings.style.display = "block";
            else state.ui.visBoxSettings.style.display = "block";
        }
        state.ui.visType.value = settings.visualType;
        toggleVisualInputs();
        state.ui.tabMove.onclick = () => { state.ui.tabMove.classList.add("active"); state.ui.tabTheme.classList.remove("active"); state.ui.tabContentMove.style.display = "block"; state.ui.tabContentTheme.style.display = "none"; };
        state.ui.tabTheme.onclick = () => { state.ui.tabTheme.classList.add("active"); state.ui.tabMove.classList.remove("active"); state.ui.tabContentTheme.style.display = "block"; state.ui.tabContentMove.style.display = "none"; };
        state.ui.advToggle.onclick = () => { const isH = state.ui.advSect.style.display==="none"; state.ui.advSect.style.display = isH?"block":"none"; state.ui.advToggle.innerText = isH?"▲ Advanced Visual Settings":"▼ Advanced Visual Settings"; };
        state.ui.btnThemeDark.onclick = () => {
            state.ui.colBg.value="#222222"; state.ui.colTxt.value="#eeeeee"; state.ui.colBorder.value="#444444"; state.ui.colPrim.value="#81b64c";
            ["themeBg","themeText","themeBorder","themePrimary"].forEach(k => saveSetting(k, k==="themeBg"?"#222222":k==="themeText"?"#eeeeee":k==="themeBorder"?"#444444":"#81b64c"));
            applyTheme();
        };
        state.ui.btnThemeLight.onclick = () => {
            state.ui.colBg.value="#f0f0f0"; state.ui.colTxt.value="#222222"; state.ui.colBorder.value="#cccccc"; state.ui.colPrim.value="#81b64c";
            ["themeBg","themeText","themeBorder","themePrimary"].forEach(k => saveSetting(k, k==="themeBg"?"#f0f0f0":k==="themeText"?"#222222":k==="themeBorder"?"#cccccc":"#81b64c"));
            applyTheme();
        };
        bind(state.ui.inpDepth, "depth", "num"); bind(state.ui.inpTime, "maxThinkingTime", "num");
        bind(state.ui.inpSearch, "searchMoves"); bind(state.ui.chkRun, "autoRun", "chk");
        bind(state.ui.chkMove, "autoMove", "chk"); bind(state.ui.chkQueue, "autoQueue", "chk");
        bind(state.ui.chkHideAfterMove, "hideAfterMove", "chk"); bind(state.ui.chkPV, "showPVArrows", "chk");
        bindSlider(state.ui.inpPVDepth, state.ui.inpPVDepthNum, "pvDepth", false);
        bind(state.ui.chkPVNums, "pvShowNumbers", "chk"); bind(state.ui.chkPVGrad, "pvCustomGradient", "chk");
        bind(state.ui.inpPVStart, "pvStartColor"); bind(state.ui.inpPVEnd, "pvEndColor");
        bind(state.ui.inpMin, "minDelay", "num"); bind(state.ui.inpMax, "maxDelay", "num");
        bindSlider(state.ui.visInnerOp, state.ui.visInnerOpNum, "innerOpacity", true);
        bindSlider(state.ui.visOuterOp, state.ui.visOuterOpNum, "outerOpacity", true);
        bindSlider(state.ui.visBias, state.ui.visBiasNum, "gradientBias", false);
        bindSlider(state.ui.visArrowOp, state.ui.visArrowOpNum, "arrowOpacity", true);
        bindSlider(state.ui.visArrowWidth, state.ui.visArrowWidthNum, "arrowWidth", false);
        bindSlider(state.ui.visOutOp, state.ui.visOutOpNum, "visualOutlineOpacity", true);
        bindSlider(state.ui.visOutWidth, state.ui.visOutWidthNum, "visualOutlineWidth", false);
        bind(state.ui.visOutGlow, "visualOutlineGlow", "chk");
        bindSlider(state.ui.visOutGlowRad, state.ui.visOutGlowRadNum, "visualOutlineGlowRadius", false);
        bindSlider(state.ui.inpMenuOp, state.ui.inpMenuOpNum, "menuOpacity", true);
        bind(state.ui.colBg, "themeBg"); bind(state.ui.colTxt, "themeText");
        bind(state.ui.colBorder, "themeBorder"); bind(state.ui.colPrim, "themePrimary");
        [state.ui.sliderH, state.ui.sliderS, state.ui.sliderL].forEach(el => { el.oninput = () => { state.h=parseFloat(state.ui.sliderH.value); state.s=parseFloat(state.ui.sliderS.value); state.l=parseFloat(state.ui.sliderL.value); syncColor(); }; });
        state.ui.inpHex.onchange = (e) => { if (/^#[0-9A-F]{6}$/i.test(e.target.value)) { const rgb=hexToRgb(e.target.value); const hsl=rgbToHsl(rgb.r,rgb.g,rgb.b); state.h=hsl.h; state.s=hsl.s; state.l=hsl.l; syncColor(); } };
    }
    function drawFenBoard(fen) {
        let rows = fen.split(" ")[0].split("/"), board = [];
        for (let r of rows) {
            let rowArr = [];
            for (let char of r) {
                if (!isNaN(char)) { for (let k = 0; k < parseInt(char); k++) rowArr.push(""); }
                else rowArr.push(char);
            }
            board.push(rowArr);
        }
        let html = '<div class="fen-board">';
        for (let r = 0; r < 8; r++) for (let c = 0; c < 8; c++) {
            const piece = board[r][c], isDark = (r+c)%2===1;
            const bg = piece ? `style="background-image: url('${PIECE_IMGS[piece]}');"` : "";
            html += `<div class="fen-sq ${isDark ? "dark" : "light"}" ${bg}></div>`;
        }
        return html + "</div>";
    }
    function renderHistory() {
        if (!state.ui.histBody) return;
        state.ui.histBody.innerHTML = "";
        if (state.history.length === 0) { state.ui.histBody.innerHTML = '<tr><td colspan="5" id="histEmpty">No history yet.</td></tr>'; return; }
        [...state.history].reverse().forEach((item, index) => {
            const tr = document.createElement("tr");
            let resClass = item.result === "Win" ? "hist-win" : item.result === "Loss" ? "hist-loss" : "hist-draw";
            tr.innerHTML = `
                <td>${item.date}</td>
                <td style="font-weight:bold; color:${item.color === "White" ? "#ffffff" : "#888888"};">${item.color || "N/A"}</td>
                <td class="${resClass}">${item.result}</td>
                <td>${item.myTime} / ${item.oppTime}</td>
                <td class="hist-fen" data-fen="${item.fen}">${item.fen}</td>
                <td><button class="btn-del" data-idx="${state.history.length - 1 - index}">Delete</button></td>`;
            state.ui.histBody.appendChild(tr);
        });
        document.querySelectorAll(".btn-del").forEach((btn) => {
            btn.onclick = (e) => { const idx = parseInt(e.target.dataset.idx); state.history.splice(idx, 1); GM_setValue("bot_history", state.history); renderHistory(); };
        });
        document.querySelectorAll(".hist-fen").forEach((el) => {
            el.onmouseenter = (e) => {
                const fen = e.target.getAttribute("data-fen");
                if (fen && state.ui.fenTooltip) {
                    state.ui.fenTooltip.innerHTML = drawFenBoard(fen);
                    state.ui.fenTooltip.style.display = "block";
                    const rect = e.target.getBoundingClientRect();
                    let left = rect.left + 20, top = rect.bottom + 5;
                    if (left + 250 > window.innerWidth) left = window.innerWidth - 260;
                    if (top + 250 > window.innerHeight) top = rect.top - 260;
                    state.ui.fenTooltip.style.left = left + "px"; state.ui.fenTooltip.style.top = top + "px";
                }
            };
            el.onmouseleave = () => { if (state.ui.fenTooltip) state.ui.fenTooltip.style.display = "none"; };
        });
    }
    function checkForGameOver() {
        if (!settings.enableHistory) return;
        const resultEl = document.querySelector(".game-result-component, .game-over-modal-content, .daily-game-footer-game-over");
        if (resultEl) {
            if (state.hasSavedCurrentGameResult) return;
            let fen = sanitizeFEN(getRawBoardFEN());
            let playingAsCode = state.playingAs;
            if (!playingAsCode && state.board?.game?.getPlayingAs) { try { playingAsCode = state.board.game.getPlayingAs(); } catch (e) {} }
            if (playingAsCode !== 1 && playingAsCode !== 2) playingAsCode = 0;
            const playerColor = playingAsCode === 2 ? "Black" : "White";
            if (playingAsCode === 2) {
                let parts = fen.split(" ");
                if (parts.length > 0) { parts[0] = parts[0].split("/").reverse().map((row) => row.split("").reverse().join("")).join("/"); fen = parts.join(" "); }
            }
            const clockBot = document.querySelector(".clock-bottom .clock-time-monospace, .clock-bottom");
            const clockTop = document.querySelector(".clock-top .clock-time-monospace, .clock-top");
            let myTime = clockBot ? clockBot.innerText : "N/A";
            let oppTime = clockTop ? clockTop.innerText : "N/A";
            let simpleRes = "Draw";
            const mainMsg = resultEl.querySelector(".game-result-main-message, .game-over-header-title");
            const subMsgEl = resultEl.querySelector(".game-result-sub-message, .game-over-header-subtitle");
            const fullText = ((mainMsg ? mainMsg.innerText : resultEl.innerText.split("\n")[0]) + " " + (subMsgEl ? subMsgEl.innerText : "")).toLowerCase();
            if (resultEl.classList.contains("game-result-win")) simpleRes = "Win";
            else if (resultEl.classList.contains("game-result-loss")) simpleRes = "Loss";
            else if (resultEl.classList.contains("game-result-draw")) simpleRes = "Draw";
            else if (fullText.includes("you won")) simpleRes = "Win";
            else if (fullText.includes("you lost")) simpleRes = "Loss";
            else if (playingAsCode === 1 && fullText.includes("white won")) simpleRes = "Win";
            else if (playingAsCode === 1 && fullText.includes("black won")) simpleRes = "Loss";
            else if (playingAsCode === 2 && fullText.includes("black won")) simpleRes = "Win";
            else if (playingAsCode === 2 && fullText.includes("white won")) simpleRes = "Loss";
            state.history.push({ date: new Date().toLocaleString(), color: playerColor, result: simpleRes, fen, myTime, oppTime, id: Date.now() });
            if (state.history.length > 200) state.history.shift();
            GM_setValue("bot_history", state.history);
            state.hasSavedCurrentGameResult = !0;
            if (state.ui.histModal && state.ui.histModal.style.display !== "none") renderHistory();
        } else {
            state.hasSavedCurrentGameResult = !1;
        }
    }
    function enforceBounds() {
        if (state.ui.panel) {
            const rect = state.ui.panel.getBoundingClientRect();
            if (rect.right > window.innerWidth) state.ui.panel.style.width = window.innerWidth - rect.left + "px";
            if (rect.bottom > window.innerHeight) state.ui.panel.style.height = window.innerHeight - rect.top + "px";
            if (rect.left < 0) state.ui.panel.style.left = "0px";
            if (rect.top < 0) state.ui.panel.style.top = "0px";
        }
        requestAnimationFrame(enforceBounds);
    }
    requestAnimationFrame(enforceBounds);
    function updateUI() {
        if (!state.ui.panel) return;
        document.body.classList.remove("mode-cloud", "mode-local", "mode-sfonline");
        document.body.classList.add(`mode-${settings.engineMode}`);
        if (state.ui.debugArea) state.ui.debugArea.style.display = settings.debugLogs ? "block" : "none";
        let maxD = 18;
        if (settings.engineMode === "local") maxD = 25;
        else if (settings.engineMode === "sfonline") maxD = 15;
        if (state.ui.lblMaxDepth) state.ui.lblMaxDepth.innerText = maxD;
        if (state.ui.inpDepth) state.ui.inpDepth.max = maxD;
        if (state.ui.inpPVDepth) state.ui.inpPVDepth.max = 45;
        if (state.ui.pvSettings) state.ui.pvSettings.style.display = settings.showPVArrows ? "block" : "none";
        if (state.ui.pvGradSettings) state.ui.pvGradSettings.style.display = settings.pvCustomGradient ? "block" : "none";
        if (state.ui.btnAnalyze) state.ui.btnAnalyze.disabled = state.isThinking;
        if (state.ui.moveResult) state.ui.moveResult.innerHTML = state.lastMoveResult;
        if (state.ui.liveOutput) state.ui.liveOutput.innerHTML = state.lastLiveResult;
        if (state.ui.delayDisplay) state.ui.delayDisplay.innerText = `Randomized Delay: ${state.calculatedDelay}s`;
        if (state.ui.logSent) state.ui.logSent.innerText = state.lastPayload;
        if (state.ui.logRec) state.ui.logRec.innerText = state.lastResponse;
        if (document.activeElement !== state.ui.inpDepth) state.ui.inpDepth.value = settings.depth;
    }
    function mainLoop() {
        state.board = document.querySelector(CONFIG.BOARD_SEL);
        EvalBar.create();
        EvalBar.updatePosition();
        if (state.board?.game && settings.autoRun) {
            const raw = getRawBoardFEN();
            if (raw) {
                const clean = sanitizeFEN(raw);
                if (settings.hideAfterMove && state.lastSeenFEN && clean !== state.lastSeenFEN) {
                    Visuals.removeByType('history'); Visuals.removeByType('analysis'); PV.clear();
                }
                state.lastSeenFEN = clean;
                const isTurn = state.board.game.getTurn() === state.board.game.getPlayingAs();
                if (isTurn && clean !== state.lastSanitizedBoardFEN) analyze(settings.depth);
            }
        }
        if (!state.ui.panel) createUI();
        if (state.board?.game?.getPlayingAs) {
            try { const pa = state.board.game.getPlayingAs(); if (pa === 1 || pa === 2) state.playingAs = pa; } catch (e) {}
        }
        if (state.board?.game && settings.autoRun) {
            const raw = getRawBoardFEN();
            if (raw) {
                const clean = sanitizeFEN(raw);
                const isTurn = state.board.game.getTurn() === state.board.game.getPlayingAs();
                if (isTurn && clean !== state.lastSanitizedBoardFEN) analyze(settings.depth);
            }
        }
        checkForGameOver();
        updateUI();
    }
    setInterval(mainLoop, CONFIG.LOOP_MS);
})();