тШ░

ЁЯО▓ЁЯзК Chess.com 3D Board

ЁЯФДЁЯО▓ Transforme o tabuleiro do Chess.com em 3D! ЁЯОи Personalize o verso do Tabuleiro, d├к zoom, rota├з├гo e diversos efeitos visuais. ЁЯЪАЁЯМЯ (funciona melhor em firefox)

рд╣реА рд╕реНрдХреНрд░рд┐рдкреНрдЯ рдЗрдВрд╕реНрдЯреЙрд▓ рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рддреБрдореНрд╣рд╛рд▓рд╛ Tampermonkey, Greasemonkey рдХрд┐рдВрд╡рд╛ Violentmonkey рдпрд╛рд╕рд╛рд░рдЦреЗ рдПрдХреНрд╕реНрдЯреЗрдВрд╢рди рдЗрдВрд╕реНрдЯреЙрд▓ рдХрд░рд╛рд╡реЗ рд▓рд╛рдЧреЗрд▓.

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

рд╣реА рд╕реНрдХреНрд░рд┐рдкреНрдЯ рдЗрдВрд╕реНрдЯреЙрд▓ рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рддреБрдореНрд╣рд╛рд▓рд╛ Tampermonkey рдХрд┐рдВрд╡рд╛ Violentmonkey рдпрд╛рд╕рд╛рд░рдЦреЗ рдПрдХреНрд╕реНрдЯреЗрдВрд╢рди рдЗрдВрд╕реНрдЯреЙрд▓ рдХрд░рд╛рд╡реЗ рд▓рд╛рдЧреЗрд▓..

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

рд╣реА рд╕реНрдХреНрд░рд┐рдкреНрдЯ рдЗрдВрд╕реНрдЯреЙрд▓ рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рддреБрдореНрд╣рд╛рд▓рд╛ Tampermonkey рдпрд╛рд╕рд╛рд░рдЦреЗ рдПрдХреНрд╕реНрдЯреЗрдВрд╢рди рдЗрдВрд╕реНрдЯреЙрд▓ рдХрд░рд╛рд╡реЗ рд▓рд╛рдЧреЗрд▓..

рд╣реА рд╕реНрдХреНрд░рд┐рдкреНрдЯ рдЗрдВрд╕реНрдЯреЙрд▓ рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рддреБрдореНрд╣рд╛рд▓рд╛ рдПрдХ рдпреБрдЭрд░ рд╕реНрдХреНрд░рд┐рдкреНрдЯ рд╡реНрдпрд╡рд╕реНрдерд╛рдкрдХ рдПрдХреНрд╕реНрдЯреЗрдВрд╢рди рдЗрдВрд╕реНрдЯреЙрд▓ рдХрд░рд╛рд╡реЗ рд▓рд╛рдЧреЗрд▓.

(рдорд╛рдЭреНрдпрд╛рдХрдбреЗ рдЖрдзреАрдЪ рдпреБрдЭрд░ рд╕реНрдХреНрд░рд┐рдкреНрдЯ рд╡реНрдпрд╡рд╕реНрдерд╛рдкрдХ рдЖрд╣реЗ, рдорд▓рд╛ рдЗрдВрд╕реНрдЯреЙрд▓ рдХрд░реВ рджреНрдпрд╛!)

рд╣реА рд╕реНрдЯрд╛рдИрд▓ рдЗрдВрд╕реНрдЯреЙрд▓ рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рддреБрдореНрд╣рд╛рд▓рд╛ Stylus рд╕рд╛рд░рдЦреЗ рдПрдХреНрд╕реНрдЯреЗрдВрд╢рди рдЗрдВрд╕реНрдЯреЙрд▓ рдХрд░рд╛рд╡реЗ рд▓рд╛рдЧреЗрд▓.

рд╣реА рд╕реНрдЯрд╛рдИрд▓ рдЗрдВрд╕реНрдЯреЙрд▓ рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рддреБрдореНрд╣рд╛рд▓рд╛ Stylus рд╕рд╛рд░рдЦреЗ рдПрдХреНрд╕реНрдЯреЗрдВрд╢рди рдЗрдВрд╕реНрдЯреЙрд▓ рдХрд░рд╛рд╡реЗ рд▓рд╛рдЧреЗрд▓.

рд╣реА рд╕реНрдЯрд╛рдИрд▓ рдЗрдВрд╕реНрдЯреЙрд▓ рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рддреБрдореНрд╣рд╛рд▓рд╛ Stylus рд╕рд╛рд░рдЦреЗ рдПрдХреНрд╕реНрдЯреЗрдВрд╢рди рдЗрдВрд╕реНрдЯреЙрд▓ рдХрд░рд╛рд╡реЗ рд▓рд╛рдЧреЗрд▓.

рд╣реА рд╕реНрдЯрд╛рдИрд▓ рдЗрдВрд╕реНрдЯреЙрд▓ рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рддреБрдореНрд╣рд╛рд▓рд╛ рдПрдХ рдпреБрдЭрд░ рд╕реНрдЯрд╛рдИрд▓ рд╡реНрдпрд╡рд╕реНрдерд╛рдкрдХ рдЗрдВрд╕реНрдЯреЙрд▓ рдХрд░рд╛рд╡реЗ рд▓рд╛рдЧреЗрд▓.

рд╣реА рд╕реНрдЯрд╛рдИрд▓ рдЗрдВрд╕реНрдЯреЙрд▓ рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рддреБрдореНрд╣рд╛рд▓рд╛ рдПрдХ рдпреБрдЭрд░ рд╕реНрдЯрд╛рдИрд▓ рд╡реНрдпрд╡рд╕реНрдерд╛рдкрдХ рдЗрдВрд╕реНрдЯреЙрд▓ рдХрд░рд╛рд╡реЗ рд▓рд╛рдЧреЗрд▓.

рд╣реА рд╕реНрдЯрд╛рдИрд▓ рдЗрдВрд╕реНрдЯреЙрд▓ рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рддреБрдореНрд╣рд╛рд▓рд╛ рдПрдХ рдпреБрдЭрд░ рд╕реНрдЯрд╛рдИрд▓ рд╡реНрдпрд╡рд╕реНрдерд╛рдкрдХ рдЗрдВрд╕реНрдЯреЙрд▓ рдХрд░рд╛рд╡реЗ рд▓рд╛рдЧреЗрд▓.

(рдорд╛рдЭреНрдпрд╛рдХрдбреЗ рдЖрдзреАрдЪ рдпреБрдЭрд░ рд╕реНрдЯрд╛рдИрд▓ рд╡реНрдпрд╡рд╕реНрдерд╛рдкрдХ рдЖрд╣реЗ, рдорд▓рд╛ рдЗрдВрд╕реНрдЯреЙрд▓ рдХрд░реВ рджреНрдпрд╛!)

// ==UserScript==
// @name         ЁЯО▓ЁЯзК Chess.com 3D Board
// @namespace    http://tampermonkey.net/
// @version      13.6
// @description  ЁЯФДЁЯО▓ Transforme o tabuleiro do Chess.com em 3D! ЁЯОи Personalize o verso do Tabuleiro, d├к zoom, rota├з├гo e diversos efeitos visuais. ЁЯЪАЁЯМЯ  (funciona melhor em firefox) 
// @author       aerus15
// @match        *://*.chess.com/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    window.addEventListener('error', function(e) {
        if (e.message && (
            e.message.toLowerCase().includes('piece') ||
            e.message.toLowerCase().includes('board') ||
            e.message.toLowerCase().includes('bounds') ||
            e.message.toLowerCase().includes('node') ||
            e.message.toLowerCase().includes('style')
        )) {
            e.preventDefault();
            e.stopPropagation();
        }
    }, true);

    const loadSavedState = () => {
        try {
            const saved = localStorage.getItem('chess3dState_v13');
            return saved ? JSON.parse(saved) : {};
        } catch (e) {
            return {};
        }
    };

    const savedState = loadSavedState();

    let state = {
        bgMode: savedState.bgMode || 'solid',
        colorSolid: savedState.colorSolid || '#333333',
        g1: savedState.g1 || '#ff0055',
        g2: savedState.g2 || '#5500ff',
        g3: savedState.g3 || '#00aaff',
        imgBase64: savedState.imgBase64 || '',
        reflect: savedState.reflect !== undefined ? savedState.reflect : true,
        allowInteraction: savedState.allowInteraction !== undefined ? savedState.allowInteraction : false,
        centerBoard: savedState.centerBoard !== undefined ? savedState.centerBoard : false,
        scale: savedState.scale !== undefined ? savedState.scale : 1,
        thorEnabled: savedState.thorEnabled !== undefined ? savedState.thorEnabled : false,
        thorDuration: savedState.thorDuration || 3.0,
        thorDistance: savedState.thorDistance || 5000,
        thorSpin: savedState.thorSpin || 5,
        rx: 0,
        ry: 0,
        tx: 0,
        ty: 0,
        locked: false
    };

    let saveTimeout = null;
    const saveSettings = () => {
        if (saveTimeout) clearTimeout(saveTimeout);
        saveTimeout = setTimeout(() => {
            const stateToSave = {
                bgMode: state.bgMode,
                colorSolid: state.colorSolid,
                g1: state.g1,
                g2: state.g2,
                g3: state.g3,
                imgBase64: state.imgBase64,
                reflect: state.reflect,
                allowInteraction: state.allowInteraction,
                centerBoard: state.centerBoard,
                scale: state.scale,
                thorEnabled: state.thorEnabled,
                thorDuration: state.thorDuration,
                thorDistance: state.thorDistance,
                thorSpin: state.thorSpin
            };
            localStorage.setItem('chess3dState_v13', JSON.stringify(stateToSave));
        }, 300);
    };

    const style = document.createElement('style');
    style.innerHTML = `
        #chess-3d-toggle-btn {
            position: fixed; bottom: 20px; left: 20px; background: rgba(15, 15, 15, 0.7); border: 1px solid #444; color: #fff;
            width: 38px; height: 38px; border-radius: 50%; cursor: pointer; z-index: 9999999; font-size: 18px;
            display: flex; align-items: center; justify-content: center; box-shadow: 0 4px 10px rgba(0, 0, 0, 0.5);
            transition: transform 0.2s ease, background 0.2s ease; backdrop-filter: blur(5px); padding: 0;
            will-change: transform;
        }
        #chess-3d-toggle-btn:hover { background: rgba(45, 45, 45, 0.95); transform: scale(1.05); }
        #chess-3d-toggle-btn:active { transform: scale(0.95); }

        #chess-3d-gui {
            position: fixed; bottom: 65px; left: 20px; width: 260px; background: rgba(15, 15, 15, 0.95);
            border: 1px solid #333; border-radius: 10px; padding: 12px; box-shadow: 0 10px 40px rgba(0, 0, 0, 0.95);
            z-index: 9999999; color: #eaeaea; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
            backdrop-filter: blur(10px); user-select: none; display: none; flex-direction: column; gap: 10px;
            box-sizing: border-box; transition: opacity 0.2s ease; max-height: 80vh; overflow-y: auto;
            will-change: opacity, transform;
        }
        #chess-3d-gui.show { display: flex; animation: fadeIn 0.2s ease-out; }
        #chess-3d-gui::-webkit-scrollbar { width: 6px; }
        #chess-3d-gui::-webkit-scrollbar-thumb { background: #444; border-radius: 3px; }
        @keyframes fadeIn { from { opacity: 0; transform: translateY(5px); } to { opacity: 1; transform: translateY(0); } }

        .chess-3d-control { display: flex; flex-direction: column; gap: 6px; }
        .chess-3d-control label { font-size: 11px; color: #ccc; display: flex; justify-content: space-between; align-items: center; cursor: pointer; font-weight: 500; }
        .chess-3d-control select, .chess-3d-control input[type="file"], .chess-3d-control input[type="range"] {
            background: #1a1a1a; color: #ffffff; border: 1px solid #555; border-radius: 4px; padding: 4px 6px; font-size: 11px; width: 100%; margin-top: 3px; outline: none; box-sizing: border-box;
        }
        .color-row { display: flex; gap: 6px; margin-top: 3px; }
        .color-row input[type="color"] { flex: 1; height: 24px; border: 1px solid #444; background: #1a1a1a; cursor: pointer; padding: 1px; border-radius: 3px; }
        input[type=checkbox] { accent-color: #739b53; width: 14px; height: 14px; cursor: pointer; }
        .status-indicator { font-size: 10px; text-align: center; color: #ff4444; font-weight: bold; min-height: 12px; letter-spacing: 0.5px; margin-top: 4px; }

        .board-layout-bg, .layout-board-bg, .bg-shadow, div[class*="board-layout-bg"], #board-layout-main::before, #board-layout-main::after,
        .board-layout-main::before, .board-layout-main::after, body::before, body::after, .hover-square {
            display: none !important; background-image: none !important; background-color: transparent !important;
            opacity: 0 !important; visibility: hidden !important; box-shadow: none !important; backdrop-filter: none !important;
        }

        .coordinates, .coordinate, .ranks, .rank, .files, .file {
            visibility: visible !important;
            opacity: 1 !important;
            z-index: 99995 !important;
            pointer-events: none !important;
            background-color: transparent !important;
            background-image: none !important;
            box-shadow: none !important;
            border: none !important;
        }

        .piece, .highlight, .hover-square {
            -webkit-backface-visibility: hidden !important;
            backface-visibility: hidden !important;
            outline: none !important;
            box-shadow: none !important;
            border: none !important;
        }

        .square {
            -webkit-backface-visibility: hidden !important;
            backface-visibility: hidden !important;
            outline: none !important;
            box-shadow: none !important;
            border: none !important;
            pointer-events: none !important;
        }

        .board-layout-chessboard, #board-layout-chessboard, .board-layout-center, .layout-board, .board-layout-main {
            background-color: transparent !important; background-image: none !important; box-shadow: none !important; border: none !important;
        }
        #board-layout-chessboard::before, #board-layout-chessboard::after, .board-layout-chessboard::before, .board-layout-chessboard::after,
        chess-board::before, chess-board::after {
            display: none !important; background-image: none !important; content: none !important; opacity: 0 !important;
        }
        .board-background-image, .board-background, .board-bg, svg.board-background { display: none !important; opacity: 0 !important; }

        chess-board {
            pointer-events: auto !important; touch-action: none !important; overflow: visible !important;
            transform: translateZ(0) !important;
            -webkit-font-smoothing: subpixel-antialiased !important;
            outline: none !important;
        }

        .piece { pointer-events: auto !important; z-index: 100000 !important; }
        svg { pointer-events: none !important; }
        .chess-3d-overlay.blocking { pointer-events: none !important; display: none !important; }

        .chess-board-3d-wrapper {
            perspective: 2500px !important; transform-style: preserve-3d !important;
            background: transparent !important; box-shadow: none !important;
        }
        .chess-board-3d-target {
            transform-style: preserve-3d !important; will-change: transform;
            transform-origin: center center !important; position: relative !important; z-index: 100 !important; transition: none !important;
        }

        .chess-3d-back {
            position: absolute !important; top: 0 !important; transform: translateZ(-0.5px) rotateY(180deg) !important;
            pointer-events: none !important; background-size: cover !important; background-position: center !important;
            background-repeat: no-repeat !important; box-shadow: inset 0 0 60px rgba(0,0,0,0.85) !important;
            border-radius: inherit !important; margin: 0 !important; padding: 0 !important; z-index: -1 !important;
            backface-visibility: hidden !important; box-sizing: border-box !important; overflow: hidden !important;
            will-change: background;
        }
        .chess-3d-back::after {
            content: '' !important; position: absolute !important; top: 0 !important; left: 0 !important; width: 100% !important; height: 100% !important;
            background: radial-gradient(circle at var(--bg-x, 50%) var(--bg-y, 50%), rgba(255,255,255,0.9), transparent 50%),
                        conic-gradient(from var(--bg-angle, 0deg) at 50% 50%, #ff007f, #7f00ff, #00ffff, #00ff7f, #ffeb3b, #ff007f) !important;
            mix-blend-mode: color-dodge !important; opacity: var(--reflect-opacity, 0) !important; pointer-events: none !important;
            border-radius: inherit !important; transition: opacity 0.2s ease !important; box-sizing: border-box !important;
            will-change: opacity;
        }
        .chess-3d-overlay { position: absolute !important; inset: 0 !important; z-index: 1000 !important; pointer-events: none !important; }

        body.chess-3d-centered .analysis-tools, body.chess-3d-centered .layout-right, body.chess-3d-centered [data-cy="analysis-panel"],
        body.chess-3d-centered .sidebar, body.chess-3d-centered .game-overview, body.chess-3d-centered .vertical-move-list,
        body.chess-3d-centered #board-layout-sidebar {
            opacity: 0 !important; pointer-events: none !important; z-index: -999 !important; position: absolute !important;
            visibility: hidden !important; display: none !important; width: 0 !important; height: 0 !important;
        }

        /* CORRE├З├ГO DO SUB-PIXEL (TELA CHEIA): Removido o translate(-50%, -50%) e substitu├нdo por inset:0 + margin:auto */
        body.chess-3d-centered .board-layout-main {
            display: flex !important; flex-direction: column !important; justify-content: center !important; align-items: center !important;
            width: max-content !important; height: max-content !important; position: fixed !important;
            inset: 0 !important; margin: auto !important; /* Alinhamento nativo do navegador para for├зar pixels inteiros */
            z-index: 9998 !important; background: transparent !important; padding: 0 !important;
            will-change: transform;
        }
        body.chess-3d-centered .board-layout-player-top, body.chess-3d-centered .board-layout-player-bottom {
            width: 100% !important; max-width: 100% !important; display: flex !important; justify-content: space-between !important;
            align-items: center !important; box-sizing: border-box !important; margin: 0 !important; padding: 0 !important;
        }
        body.chess-3d-centered #board-layout-chessboard { margin: 0 !important; }
    `;
    document.head.appendChild(style);

    const dynamicStyle = document.createElement('style');
    dynamicStyle.id = 'chess-3d-dynamic-style';
    document.head.appendChild(dynamicStyle);

    const toggleBtn = document.createElement('button');
    toggleBtn.id = 'chess-3d-toggle-btn';
    toggleBtn.innerHTML = 'тЪЩя╕П';
    toggleBtn.title = "Configura├з├╡es 3D & Efeitos";
    document.body.appendChild(toggleBtn);

    const gui = document.createElement('div');
    gui.id = 'chess-3d-gui';
    gui.innerHTML = `
        <div class="chess-3d-control">
            <label>Tipo do Fundo (Verso)</label>
            <select id="bg-type">
                <option value="solid">Cor S├│lida</option>
                <option value="gradient">Gradiente (3 Cores)</option>
                <option value="image">Imagem Personalizada</option>
            </select>
        </div>
        <div class="chess-3d-control" id="ctrl-solid">
            <div class="color-row"><input type="color" id="color-solid"></div>
        </div>
        <div class="chess-3d-control" id="ctrl-gradient" style="display:none;">
            <div class="color-row"><input type="color" id="color-g1"><input type="color" id="color-g2"><input type="color" id="color-g3"></div>
        </div>
        <div class="chess-3d-control" id="ctrl-image" style="display:none;">
            <input type="file" id="img-upload" accept="image/*">
        </div>
        <div class="chess-3d-control" style="border-top: 1px solid #333; padding-top: 6px; margin-top: 3px;">
            <label><span>Reflexos RTX (Verso)</span><input type="checkbox" id="check-reflect"></label>
        </div>
        <div class="chess-3d-control">
            <label><span>Mover Pe├зas Girado</span><input type="checkbox" id="check-interact"></label>
        </div>
        <div class="chess-3d-control" style="border-top: 1px solid #333; padding-top: 6px; margin-top: 3px;">
            <label><span>Centralizar Tabuleiro</span><input type="checkbox" id="check-center"></label>
        </div>
        <div class="chess-3d-control" style="border-top: 1px solid #333; padding-top: 6px; margin-top: 3px;">
            <label><span style="color: #00ffaa; font-weight:bold;">Anima├з├гo Thor (In├нcio)</span><input type="checkbox" id="check-thor"></label>
            <div id="thor-settings" style="display:none; flex-direction:column; gap:8px; margin-top:6px; background:#111; padding:6px; border-radius:4px;">
                <label>Dura├з├гo (Segundos): <span id="val-dur" style="color:#aaa; font-size:9px;"></span></label>
                <input type="range" id="thor-duration" min="0.5" max="10" step="0.5">
                <label>Dist├вncia de Origem: <span id="val-dist" style="color:#aaa; font-size:9px;"></span></label>
                <input type="range" id="thor-distance" min="1000" max="25000" step="1000">
                <label>Intensidade do Giro: <span id="val-spin" style="color:#aaa; font-size:9px;"></span></label>
                <input type="range" id="thor-spin" min="1" max="15" step="1">
                <div style="font-size:10px; color:#888; text-align:center;">Dica: Esconde o tabuleiro ao buscar partida. Pressione 'O' para invocar.</div>
            </div>
        </div>
        <div style="font-size:10px; color:#aaa; text-align:center; margin-top:2px;">Scroll do mouse altera tamanho (Zoom)</div>
        <div class="status-indicator" id="lock-status"></div>
    `;
    document.body.appendChild(gui);

    const elements = {
        bgType: document.getElementById('bg-type'),
        ctrlSolid: document.getElementById('ctrl-solid'),
        ctrlGradient: document.getElementById('ctrl-gradient'),
        ctrlImage: document.getElementById('ctrl-image'),
        colorSolid: document.getElementById('color-solid'),
        colorG1: document.getElementById('color-g1'),
        colorG2: document.getElementById('color-g2'),
        colorG3: document.getElementById('color-g3'),
        imgUpload: document.getElementById('img-upload'),
        checkReflect: document.getElementById('check-reflect'),
        checkInteract: document.getElementById('check-interact'),
        checkCenter: document.getElementById('check-center'),
        checkThor: document.getElementById('check-thor'),
        thorSettings: document.getElementById('thor-settings'),
        thorDuration: document.getElementById('thor-duration'),
        thorDistance: document.getElementById('thor-distance'),
        thorSpin: document.getElementById('thor-spin'),
        lockStatus: document.getElementById('lock-status'),
        valDur: document.getElementById('val-dur'),
        valDist: document.getElementById('val-dist'),
        valSpin: document.getElementById('val-spin')
    };

    elements.bgType.value = state.bgMode;
    elements.colorSolid.value = state.colorSolid;
    elements.colorG1.value = state.g1;
    elements.colorG2.value = state.g2;
    elements.colorG3.value = state.g3;
    elements.checkReflect.checked = state.reflect;
    elements.checkInteract.checked = state.allowInteraction;
    elements.checkCenter.checked = state.centerBoard;
    elements.checkThor.checked = state.thorEnabled;

    elements.thorDuration.value = state.thorDuration;
    elements.thorDistance.value = state.thorDistance;
    elements.thorSpin.value = state.thorSpin;
    elements.valDur.innerText = state.thorDuration + 's';
    elements.valDist.innerText = state.thorDistance;
    elements.valSpin.innerText = state.thorSpin;

    if (state.centerBoard) document.body.classList.add('chess-3d-centered');

    let boardEl = null;
    let backEl = null;
    let overlayEl = null;
    let cachedMainLayout = null;
    let cachedBoardParent = null;

    let isRotating = false;
    let isPanning = false;
    let lastX = 0; let lastY = 0;
    let vx = 0; let vy = 0;
    let inertiaFrame = null;

    const rotSensitivity = 0.45;
    const panSensitivity = 1.2;
    const friction = 0.93;

    let isAnimatingThor = false;
    let thorFrameId = null;
    let lastPiecesCount = -1;
    let rAF_pending = false;

    const updateControlsVisibility = () => {
        elements.ctrlSolid.style.display = state.bgMode === 'solid' ? 'block' : 'none';
        elements.ctrlGradient.style.display = state.bgMode === 'gradient' ? 'block' : 'none';
        elements.ctrlImage.style.display = state.bgMode === 'image' ? 'block' : 'none';
        elements.thorSettings.style.display = state.thorEnabled ? 'flex' : 'none';
    };

    const updateBackBackground = () => {
        if (!backEl) return;
        if (state.bgMode === 'solid') {
            backEl.style.setProperty('background', state.colorSolid, 'important');
        } else if (state.bgMode === 'gradient') {
            backEl.style.setProperty('background', `linear-gradient(45deg, ${state.g1}, ${state.g2}, ${state.g3})`, 'important');
        } else if (state.bgMode === 'image') {
            backEl.style.setProperty('background', state.imgBase64 ? `url(${state.imgBase64}) center/cover no-repeat` : '#111', 'important');
        }
    };

    const syncBackSize = () => {
        if (boardEl && backEl) {
            const corteLateral = 20;
            const width = boardEl.offsetWidth - (corteLateral * 2);
            const height = boardEl.offsetHeight;
            if (width > 0 && height > 0) {
                backEl.style.setProperty('width', `${width}px`, 'important');
                backEl.style.setProperty('height', `${height}px`, 'important');
                backEl.style.setProperty('left', `${corteLateral}px`, 'important');
            }
        }
    };

    const killGhostBoards = () => {
        if (!boardEl || !cachedBoardParent) return;
        cachedBoardParent.style.setProperty('background-image', 'none', 'important');
        cachedBoardParent.style.setProperty('background-color', 'transparent', 'important');

        const children = cachedBoardParent.children;
        for (let i = 0; i < children.length; i++) {
            const child = children[i];
            if (child !== boardEl &&
                child.tagName !== 'STYLE' && child.tagName !== 'SCRIPT' && child.tagName !== 'LINK' &&
                !child.classList.contains('chess-3d-overlay') && !child.classList.contains('chess-3d-back') &&
                !child.classList.contains('board-layout-player-top') && !child.classList.contains('board-layout-player-bottom')) {

                const zIndex = window.getComputedStyle(child).zIndex;
                if (zIndex === '-1' || zIndex === '0' || child.tagName === 'IMG' || child.tagName === 'SVG') {
                    if (!child.classList.contains('coordinates') && !child.classList.contains('piece')) {
                        child.style.setProperty('display', 'none', 'important');
                        child.style.setProperty('opacity', '0', 'important');
                    }
                }
            }
        }
    };

    const applyStaticLayoutStyles = () => {
        const centerWrapperRules = state.centerBoard ? `z-index: 9999 !important; position: relative !important; margin: 0 auto !important;` : '';
        const forceTargetInteraction = state.centerBoard ? 'z-index: 10000 !important; position: relative !important; pointer-events: auto !important;' : '';
        dynamicStyle.innerHTML = `
            .chess-board-3d-wrapper { ${centerWrapperRules} }
            .chess-board-3d-target { ${forceTargetInteraction} }
        `;
    };

    const applyTransform = () => {
        if (!boardEl || isAnimatingThor) return;

        if (cachedMainLayout) {
            // Removido o translate(-50%, -50%) que causava o sub-pixel
            const wrapperTransform = `translate3d(${state.tx}px, ${state.ty}px, 0) scale(${state.scale})`;
            cachedMainLayout.style.setProperty('transform', wrapperTransform, 'important');
            cachedMainLayout.style.setProperty('transform-origin', 'center center', 'important');
        }

        const boardTransform = `translate3d(0px, 0px, 0px) rotateX(${state.rx}deg) rotateY(${state.ry}deg)`;
        boardEl.style.setProperty('transform', boardTransform, 'important');

        if (backEl) {
            boardEl.style.setProperty('--reflect-opacity', state.reflect ? '1' : '0');
            const bgAngle = (state.rx + state.ry) * 2.5;
            const bgX = 50 + (state.ry / 1.2);
            const bgY = 50 + (state.rx / 1.2);
            boardEl.style.setProperty('--bg-angle', `${bgAngle}deg`);
            boardEl.style.setProperty('--bg-x', `${bgX}%`);
            boardEl.style.setProperty('--bg-y', `${bgY}%`);
        }

        if (overlayEl) {
            const isShiftedFromOrigin = Math.abs(state.rx) > 0.5 || Math.abs(state.ry) > 0.5 || Math.abs(state.tx) > 0.5 || Math.abs(state.ty) > 0.5;
            const isBlocking = overlayEl.classList.contains('blocking');
            if (isShiftedFromOrigin && !state.allowInteraction) {
                if (!isBlocking) overlayEl.classList.add('blocking');
            } else {
                if (isBlocking) overlayEl.classList.remove('blocking');
            }
        }
    };

    const triggerThorAnimation = () => {
        if (!boardEl || isAnimatingThor) return;
        isAnimatingThor = true;

        if (inertiaFrame) cancelAnimationFrame(inertiaFrame);
        boardEl.style.setProperty('opacity', '1', 'important');

        let startTime = null;
        const durationMs = state.thorDuration * 1000;
        const spinForce = parseFloat(state.thorSpin);
        const startZ = -state.thorDistance;

        const startRx = (Math.random() > 0.5 ? 1 : -1) * (360 + Math.random() * 720) * spinForce;
        const startRy = (Math.random() > 0.5 ? 1 : -1) * (360 + Math.random() * 720) * spinForce;
        const targetScale = state.scale;

        const easeOut = t => 1 - Math.pow(1 - t, 3);

        const animate = (timestamp) => {
            if (!startTime) startTime = timestamp;
            let progress = (timestamp - startTime) / durationMs;
            if (progress >= 1) progress = 1;

            const e = easeOut(progress);
            const currentRx = startRx * (1 - e);
            const currentRy = startRy * (1 - e);
            const currentTx = state.tx * (1 - e);
            const currentTy = state.ty * (1 - e);
            const currentZ = startZ * (1 - e);

            if (cachedMainLayout) {
                 // Removido o translate(-50%, -50%)
                 cachedMainLayout.style.setProperty('transform', `translate3d(${currentTx}px, ${currentTy}px, 0) scale(${targetScale})`, 'important');
            }
            boardEl.style.setProperty('transform', `translate3d(0, 0, ${currentZ}px) rotateX(${currentRx}deg) rotateY(${currentRy}deg)`, 'important');

            if (progress < 1) {
                thorFrameId = requestAnimationFrame(animate);
            } else {
                isAnimatingThor = false;
                resetTransform();
            }
        };
        requestAnimationFrame(animate);
    };

    const resetTransform = () => {
        if (state.locked) return;
        state.rx = 0; state.ry = 0; state.tx = 0; state.ty = 0; state.scale = 1;
        vx = 0; vy = 0;
        if (inertiaFrame) cancelAnimationFrame(inertiaFrame);
        if (thorFrameId && !isAnimatingThor) cancelAnimationFrame(thorFrameId);
        applyTransform();
        saveSettings();
    };

    const startInertia = () => {
        if (inertiaFrame) cancelAnimationFrame(inertiaFrame);
        const loop = () => {
            if (state.locked || isRotating || isPanning || isAnimatingThor) return;
            if (Math.abs(vx) > 0.05 || Math.abs(vy) > 0.05) {
                state.rx -= vy;
                state.ry += vx;
                vx *= friction;
                vy *= friction;
                applyTransform();
                inertiaFrame = requestAnimationFrame(loop);
            } else { vx = 0; vy = 0; }
        };
        loop();
    };

    const findBoard = () => {
        if (!boardEl) {
            boardEl = document.querySelector('chess-board') || document.querySelector('#board-layout-chessboard') || document.querySelector('.board');
            if (boardEl) {
                cachedBoardParent = boardEl.parentElement;
                cachedMainLayout = document.querySelector('.board-layout-main') || boardEl.closest('.board-layout-main');

                boardEl.classList.add('chess-board-3d-target');
                if (cachedBoardParent) cachedBoardParent.classList.add('chess-board-3d-wrapper');

                if (!backEl) { backEl = document.createElement('div'); backEl.className = 'chess-3d-back'; boardEl.appendChild(backEl); }
                if (!overlayEl) { overlayEl = document.createElement('div'); overlayEl.className = 'chess-3d-overlay'; boardEl.appendChild(overlayEl); }

                if (!boardEl._antiResetObserver) {
                    boardEl._antiResetObserver = new MutationObserver(() => {
                        if (isAnimatingThor || isRotating || isPanning) return;
                        const expectedTransform = `translate3d(0px, 0px, 0px) rotateX(${state.rx}deg) rotateY(${state.ry}deg)`;
                        if (boardEl.style.getPropertyValue('transform') !== expectedTransform) {
                            boardEl.style.setProperty('transform', expectedTransform, 'important');
                        }
                    });
                    boardEl._antiResetObserver.observe(boardEl, { attributes: true, attributeFilter: ['style'] });
                }

                if (cachedMainLayout && !cachedMainLayout._antiResetObserver) {
                    cachedMainLayout._antiResetObserver = new MutationObserver(() => {
                        if (isAnimatingThor || isRotating || isPanning) return;
                        // Removido o translate(-50%, -50%)
                        const expectedTransform = `translate3d(${state.tx}px, ${state.ty}px, 0) scale(${state.scale})`;
                        if (cachedMainLayout.style.getPropertyValue('transform') !== expectedTransform) {
                            cachedMainLayout.style.setProperty('transform', expectedTransform, 'important');
                        }
                    });
                    cachedMainLayout._antiResetObserver.observe(cachedMainLayout, { attributes: true, attributeFilter: ['style'] });
                }
                updateControlsVisibility();
                updateBackBackground();
                applyStaticLayoutStyles();
                syncBackSize();
                killGhostBoards();
                applyTransform();
            }
        }
    };

    toggleBtn.addEventListener('click', () => { gui.classList.toggle('show'); });
    elements.bgType.addEventListener('change', (e) => { state.bgMode = e.target.value; updateControlsVisibility(); updateBackBackground(); saveSettings(); });
    elements.colorSolid.addEventListener('input', (e) => { state.colorSolid = e.target.value; updateBackBackground(); saveSettings(); });
    elements.colorG1.addEventListener('input', (e) => { state.g1 = e.target.value; updateBackBackground(); saveSettings(); });
    elements.colorG2.addEventListener('input', (e) => { state.g2 = e.target.value; updateBackBackground(); saveSettings(); });
    elements.colorG3.addEventListener('input', (e) => { state.g3 = e.target.value; updateBackBackground(); saveSettings(); });

    elements.imgUpload.addEventListener('change', (e) => {
        const file = e.target.files[0];
        if (file) {
            const reader = new FileReader();
            reader.onload = (event) => { state.imgBase64 = event.target.result; updateBackBackground(); saveSettings(); };
            reader.readAsDataURL(file);
        }
    });

    elements.checkReflect.addEventListener('change', (e) => { state.reflect = e.target.checked; applyTransform(); saveSettings(); });
    elements.checkInteract.addEventListener('change', (e) => { state.allowInteraction = e.target.checked; applyTransform(); saveSettings(); });
    elements.checkCenter.addEventListener('change', (e) => {
        state.centerBoard = e.target.checked;
        if (state.centerBoard) document.body.classList.add('chess-3d-centered');
        else document.body.classList.remove('chess-3d-centered');
        applyStaticLayoutStyles();
        applyTransform();
        saveSettings();
    });

    elements.checkThor.addEventListener('change', (e) => {
        state.thorEnabled = e.target.checked; updateControlsVisibility();
        if (!state.thorEnabled && boardEl && !isAnimatingThor) { boardEl.style.setProperty('opacity', '1', 'important'); }
        saveSettings();
    });

    elements.thorDuration.addEventListener('input', (e) => { state.thorDuration = parseFloat(e.target.value); elements.valDur.innerText = state.thorDuration + 's'; saveSettings(); });
    elements.thorDistance.addEventListener('input', (e) => { state.thorDistance = parseFloat(e.target.value); elements.valDist.innerText = state.thorDistance; saveSettings(); });
    elements.thorSpin.addEventListener('input', (e) => { state.thorSpin = parseFloat(e.target.value); elements.valSpin.innerText = state.thorSpin; saveSettings(); });

    window.addEventListener('keydown', (e) => {
        const key = e.key.toLowerCase();
        if (key === 'p') {
            state.locked = !state.locked;
            if (state.locked && inertiaFrame) { cancelAnimationFrame(inertiaFrame); vx = 0; vy = 0; }
            elements.lockStatus.textContent = state.locked ? '[ Posi├з├гo Est├бtica (P) ]' : '';
        }
        if (key === 'o' && !isAnimatingThor) { triggerThorAnimation(); }
    });

    window.addEventListener('wheel', (e) => {
        if (e.target.closest('#chess-3d-gui') || e.target.closest('.chess-3d-gui')) return;
        if (e.target.closest('.chess-board-3d-wrapper') || e.target.closest('chess-board') || e.target.closest('.chess-3d-overlay') || e.target.closest('.board-layout-main')) {
            e.preventDefault();
            const zoomSpeed = 0.001;
            state.scale -= e.deltaY * zoomSpeed;
            if (state.scale < 0.05) state.scale = 0.05;
            if (state.scale > 20) state.scale = 20;

            if (!rAF_pending) {
                rAF_pending = true;
                requestAnimationFrame(() => { applyTransform(); rAF_pending = false; });
            }
            saveSettings();
        } else if (e.button === 1) e.preventDefault();
    }, { passive: false });

    document.addEventListener('mousedown', (e) => {
        if (e.target.closest('#chess-3d-gui') || e.target.closest('#chess-3d-toggle-btn') || isAnimatingThor) return;
        if (e.button === 1) { e.preventDefault(); resetTransform(); return; }

        if (e.button === 0 && !state.locked) {
            if (inertiaFrame) cancelAnimationFrame(inertiaFrame);
            vx = 0; vy = 0;
            const isTargetingBoard = e.target.closest('.chess-board-3d-target') || e.target.closest('.chess-3d-overlay');
            const isDisplaced = Math.abs(state.rx) > 0.5 || Math.abs(state.ry) > 0.5 || Math.abs(state.tx) > 0.5 || Math.abs(state.ty) > 0.5;

            if (isTargetingBoard) {
                if (isDisplaced && !state.allowInteraction) { isPanning = true; lastX = e.clientX; lastY = e.clientY; }
            } else { isRotating = true; lastX = e.clientX; lastY = e.clientY; }
        }
    }, { passive: false });

    document.addEventListener('mousemove', (e) => {
        if (state.locked || isAnimatingThor) return;
        if (isRotating || isPanning) {
            const deltaX = e.clientX - lastX;
            const deltaY = e.clientY - lastY;
            lastX = e.clientX;
            lastY = e.clientY;

            if (isRotating) {
                vx = deltaX * rotSensitivity;
                vy = deltaY * rotSensitivity;
                state.rx -= vy;
                state.ry += vx;
            } else {
                state.tx += deltaX * panSensitivity;
                state.ty += deltaY * panSensitivity;
            }

            if (!rAF_pending) {
                rAF_pending = true;
                requestAnimationFrame(() => { applyTransform(); rAF_pending = false; });
            }
        }
    });

    document.addEventListener('mouseup', () => {
        if (isRotating) { isRotating = false; if (Math.abs(vx) > 1 || Math.abs(vy) > 1) startInertia(); }
        isPanning = false;
    });

    document.addEventListener('auxclick', (e) => { if (e.button === 1) e.preventDefault(); });

    window.addEventListener('resize', () => {
        if (!isRotating && !isPanning && !isAnimatingThor) {
            syncBackSize();
        }
    });

    const domObserver = new MutationObserver(() => {
        if (state.centerBoard && !document.body.classList.contains('chess-3d-centered')) document.body.classList.add('chess-3d-centered');
    });
    domObserver.observe(document.body, { childList: true, subtree: true });

    setInterval(() => {
        findBoard();

        if (boardEl && !isRotating && !isPanning && !isAnimatingThor) {
            syncBackSize();
            killGhostBoards();

            boardEl.style.setProperty('pointer-events', 'auto', 'important');
            boardEl.style.setProperty('touch-action', 'none', 'important');
            boardEl.style.setProperty('overflow', 'visible', 'important');

            const pieces = boardEl.getElementsByClassName('piece');
            for(let i=0; i<pieces.length; i++) {
                pieces[i].style.setProperty('pointer-events', 'auto', 'important');
                pieces[i].style.setProperty('z-index', '100000', 'important');
            }

            const squares = boardEl.getElementsByClassName('square');
            for(let i=0; i<squares.length; i++) {
                squares[i].style.setProperty('z-index', '90000', 'important');
            }

            const svgs = boardEl.getElementsByTagName('svg');
            for(let i=0; i<svgs.length; i++) {
                svgs[i].style.setProperty('pointer-events', 'none', 'important');
            }

            const currentPieces = pieces.length;
            if (state.thorEnabled) {
                if (lastPiecesCount === 0 && currentPieces > 0) triggerThorAnimation();
                else if (currentPieces === 0 && !isAnimatingThor) boardEl.style.setProperty('opacity', '0', 'important');
                else if (currentPieces > 0 && !isAnimatingThor) boardEl.style.setProperty('opacity', '1', 'important');
            }
            lastPiecesCount = currentPieces;
        }

        const ghostPanels = document.querySelectorAll('.analysis-tools, .layout-right, [data-cy="analysis-panel"], .sidebar, .game-overview, .vertical-move-list, #board-layout-sidebar');
        if (state.centerBoard) {
            if (!document.body.classList.contains('chess-3d-centered')) document.body.classList.add('chess-3d-centered');
            for(let i=0; i<ghostPanels.length; i++) {
                const p = ghostPanels[i];
                p.style.setProperty('opacity', '0', 'important');
                p.style.setProperty('position', 'absolute', 'important');
                p.style.setProperty('pointer-events', 'none', 'important');
                p.style.setProperty('visibility', 'hidden', 'important');
                p.style.setProperty('display', 'none', 'important');
            }
        } else {
            for(let i=0; i<ghostPanels.length; i++) {
                const p = ghostPanels[i];
                p.style.removeProperty('opacity');
                p.style.removeProperty('position');
                p.style.removeProperty('pointer-events');
                p.style.removeProperty('visibility');
                p.style.removeProperty('display');
            }
        }
    }, 500);

    updateControlsVisibility();

})();