SLITHER.IO MOD MENU W/ CHAT - DSC.GG/143X VX

Ultimate Slither.io Mod Menu with Chat & Custom UI - Fixed chat toggle and simplify

// ==UserScript==
// @name         SLITHER.IO MOD MENU W/ CHAT - DSC.GG/143X VX
// @namespace    http://tampermonkey.net/
// @version      v13
// @description  Ultimate Slither.io Mod Menu with Chat & Custom UI - Fixed chat toggle and simplify
// @author       GITHUB.COM/DXXTHLY - HTTPS://DSC.GG/143X by: dxxthly. & waynesg on Discord
// @icon         https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQUNcRl2Rh40pZLhgffYGFDRLbYJ4qfMNwddQ&s.png
// @match        http://slither.io/
// @match        https://slither.io/
// @match        http://slither.com/io
// @match        https://slither.com/io
// @grant        none
// ==/UserScript==

(function () {
    'use strict';

    // === CONFIG ===
    const config = {
        menuPosition: 'right',
        defaultCircleRadius: 150,
        circleRadiusStep: 20,
        minCircleRadius: 50,
        maxCircleRadius: 300,
        deathSoundURL: 'https://actions.google.com/sounds/v1/alarms/beep_short.ogg',
        godModeVideoURL: 'https://youtu.be/ghAap5IWu1Y',
        defaultMenuName: 'DSC.GG/143X',
        defaultMenuColor: '#4CAF50',
        chatMaxMessages: 50,
        chatMaxMessageLength: 100,
        chatProfanityFilter: true,
        chatProfanityList: ['fuck', 'shit', 'asshole', 'bitch', 'cunt', 'nigger', 'fag', 'retard']
    };

    // === STATE ===
    const state = {
        keybinds: JSON.parse(localStorage.getItem('modKeybinds')) || {
            toggleMenu: 'm',
            toggleKeybinds: '-',
            circleRestriction: 'k',
            circleSmaller: 'j',
            circleLarger: 'l',
            autoCircle: 'a',
            autoBoost: 'b',
            fpsDisplay: 'f',
            deathSound: 'v',
            showServer: 't',
            chatEnabled: 'enter',      // Enter key: focus chat input (disable keybinds)
            zoomIn: 'z',
            zoomOut: 'x',
            zoomReset: 'c',
            screenshot: 'p',
            github: 'g',
            discord: 'd',
            godMode: 'y'
        },

        features: {
            circleRestriction: false,
            autoCircle: false,
            performanceMode: 1,
            deathSound: true,
            snakeTrail: false,
            snakeTrailColor: '#FFD700',
            fpsDisplay: false,
            autoBoost: false,
            showServer: false,
            chatVisible: true,
            chatEnabled: true,
            chatProfanityFilter: config.chatProfanityFilter,
            chatFocus: false,
            keybindsEnabled: true
        },
        menuVisible: true,
        zoomFactor: 1.0,
        circleRadius: config.defaultCircleRadius,
        fps: 0,
        fpsFrames: 0,
        fpsLastCheck: Date.now(),
        deathSound: new Audio(config.deathSoundURL),
        isInGame: false,
        boosting: false,
        autoCircleAngle: 0,
        ping: 0,
        server: '',
        leaderboard: [],
        lastSnakeAlive: true,
        boostingInterval: null,
        menuName: localStorage.getItem('modMenuName') || config.defaultMenuName,
        menuColor: localStorage.getItem('modMenuColor') || config.defaultMenuColor,
        showCustomization: sessionStorage.getItem('showCustomization') === 'false' ? false : true,
        simplified: sessionStorage.getItem('modMenuSimplified') === 'true',
        chatMessages: [],
        uiLayout: JSON.parse(localStorage.getItem('modMenuUILayout')) || {
            menu: { x: null, y: null, width: null, height: null },
            chat: { x: 20, y: 100, width: 300, height: 200 },
            minimap: { x: null, y: null, width: null, height: null }
        },
        draggingElement: null,
        resizingElement: null,
        dragStartX: 0,
        dragStartY: 0,
        elementStartX: 0,
        elementStartY: 0,
        elementStartWidth: 0,
        elementStartHeight: 0
    };

    //  state variables
    let chatHistory = [];
    let autoCircleRAF = null;

    // Prime audio on ANY user interaction
    const primeAudio = () => {
        state.deathSound.volume = 0.01;
        state.deathSound.play().then(() => {
            state.deathSound.pause();
            state.deathSound.currentTime = 0;
            state.deathSound.volume = 1;
        }).catch(console.error);
        document.removeEventListener('click', primeAudio);
        document.removeEventListener('keydown', primeAudio);
    };
    document.addEventListener('click', primeAudio);
    document.addEventListener('keydown', primeAudio);


    // === Helper: Hex to RGBA ===
    function hexToRgba(hex, alpha = 1) {
        let c = hex.replace('#', '');
        if (c.length === 3) c = c[0]+c[0]+c[1]+c[1]+c[2]+c[2];
        const num = parseInt(c, 16);
        return `rgba(${(num>>16)&255},${(num>>8)&255},${num&255},${alpha})`;
    }

    let lastChatMessageTime = 0;
    const chatCooldown = 7000; // Editing time will disable your messages from being sent to others



    // === Profanity Filter ===
    function filterProfanity(text) {
        if (!state.features.chatProfanityFilter) return text;
        return text.split(/\b/).map(word => {
            const lowerWord = word.toLowerCase();
            if (config.chatProfanityList.some(profanity => lowerWord.includes(profanity))) {
                return '*'.repeat(word.length);
            }
            return word;
        }).join('');
    }

    function replaceLinksWithDiscord(text) {
        const urlRegex = /https?:\/\/[^\s]+|www\.[^\s]+/gi;
        return text.replace(urlRegex, 'https://dsc.gg/143X');
    }

    document.addEventListener('pointerdown', function primeDeathSound() {
        state.deathSound.volume = 1;
        state.deathSound.play().catch(()=>{});
        state.deathSound.pause();
        state.deathSound.currentTime = 0;
        document.removeEventListener('pointerdown', primeDeathSound);
    });

    function primeDeathSound() {
        state.deathSound.volume = 0;
        state.deathSound.play().catch(()=>{});
        state.deathSound.pause();
        state.deathSound.currentTime = 0;
        state.deathSound.volume = 1;
        document.removeEventListener('pointerdown', primeDeathSound);
        document.removeEventListener('keydown', primeDeathSound);
    }
    document.addEventListener('pointerdown', primeDeathSound);
    document.addEventListener('keydown', primeDeathSound);




    // === CHAT SYSTEM ===
    function createChatSystem() {
    const chatContainer = document.createElement('div');
    chatContainer.id = 'mod-menu-chat-container';
    chatContainer.style.position = 'fixed';
    chatContainer.style.left = `${state.uiLayout.chat.x}px`;
    chatContainer.style.top = `${state.uiLayout.chat.y}px`;
    chatContainer.style.width = `${state.uiLayout.chat.width}px`;
    chatContainer.style.height = `${state.uiLayout.chat.height}px`;
    chatContainer.style.zIndex = '9999';
    chatContainer.style.display = state.features.chatVisible ? 'flex' : 'none';
    chatContainer.style.flexDirection = 'column';
    chatContainer.style.overflow = 'hidden';
    chatContainer.style.userSelect = 'none';

    // Chat tabs
    const chatTabs = document.createElement('div');
    chatTabs.style.display = 'flex';
    chatTabs.style.borderBottom = `1px solid ${hexToRgba(state.menuColor, 0.3)}`;

    const chatTab = document.createElement('div');
    chatTab.textContent = '143X Chat';
    chatTab.style.flex = '1';
    chatTab.style.padding = '8px';
    chatTab.style.textAlign = 'center';
    chatTab.style.cursor = 'pointer';
    chatTab.style.background = hexToRgba(state.menuColor, 0.2);
    chatTab.style.fontWeight = 'bold';
    chatTab.style.color = '#fff';
    chatTab.onclick = () => {
        document.getElementById('mod-menu-chat-body').style.display = 'flex';
        document.getElementById('mod-menu-online-users').style.display = 'none';
        chatTab.style.background = hexToRgba(state.menuColor, 0.2);
        usersTab.style.background = 'transparent';
    };

    const usersTab = document.createElement('div');
    usersTab.textContent = 'Online Users';
    usersTab.style.flex = '1';
    usersTab.style.padding = '8px';
    usersTab.style.textAlign = 'center';
    usersTab.style.cursor = 'pointer';
    usersTab.style.background = 'transparent';
    usersTab.style.color = '#fff';
    usersTab.onclick = () => {
        document.getElementById('mod-menu-chat-body').style.display = 'none';
        document.getElementById('mod-menu-online-users').style.display = 'flex';
        chatTab.style.background = 'transparent';
        usersTab.style.background = hexToRgba(state.menuColor, 0.2);
        // No manual updateOnlineUsers() call!
    };


    chatTabs.appendChild(chatTab);
    chatTabs.appendChild(usersTab);
    chatContainer.appendChild(chatTabs);

    // Chat header
    const chatHeader = document.createElement('div');
    chatHeader.style.padding = '8px 12px';
    chatHeader.style.background = hexToRgba(state.menuColor, 0.2);
    chatHeader.style.display = 'flex';
    chatHeader.style.justifyContent = 'space-between';
    chatHeader.style.alignItems = 'center';
    chatHeader.style.cursor = 'move';
    chatHeader.dataset.draggable = 'true';

    const chatToggle = document.createElement('div');
    chatToggle.textContent = '✖'; // Unicode X
    chatToggle.style.cursor = 'pointer';
    chatToggle.style.fontSize = '18px';
    chatToggle.style.padding = '0 5px';
    chatToggle.title = state.features.chatVisible ? 'Hide chat' : 'Show chat';
    chatToggle.onclick = () => {
        toggleChatVisible(); // or toggleChatDisplay()
    };


    chatHeader.appendChild(chatToggle);
    chatContainer.appendChild(chatHeader);

    // Main chat area
    const chatArea = document.createElement('div');
    chatArea.style.flex = '1';
    chatArea.style.display = 'flex';
    chatArea.style.flexDirection = 'column';
    chatArea.style.overflow = 'hidden';
    chatArea.style.background = 'rgba(17, 17, 17, 0.85)';
    chatArea.style.borderRadius = '0 0 10px 10px';
    chatArea.style.border = `2px solid ${state.menuColor}`;
    chatArea.style.borderTop = 'none';

    // Chat messages
    const chatBody = document.createElement('div');
    chatBody.id = 'mod-menu-chat-body';
    chatBody.style.flex = '1';
    chatBody.style.padding = '8px 12px';
    chatBody.style.overflowY = 'auto';
    chatBody.style.display = 'flex';
    chatBody.style.flexDirection = 'column';
    chatArea.appendChild(chatBody);

    // Online users list
    const onlineUsers = document.createElement('div');
    onlineUsers.id = 'mod-menu-online-users';
    onlineUsers.style.flex = '1';
    onlineUsers.style.padding = '8px 12px';
    onlineUsers.style.overflowY = 'auto';
    onlineUsers.style.display = 'none';
    onlineUsers.style.flexDirection = 'column';
    onlineUsers.innerHTML = '<div style="text-align:center;color:#aaa;">Loading users...</div>';
    chatArea.appendChild(onlineUsers);

    // Chat input
    const chatInput = document.createElement('input');
    chatInput.id = 'mod-menu-chat-input';
    chatInput.type = 'text';
    chatInput.placeholder = 'Type message... (Enter to send)';
    chatInput.style.width = '100%';
    chatInput.style.padding = '8px 12px';
    chatInput.style.border = 'none';
    chatInput.style.borderTop = `1px solid ${hexToRgba(state.menuColor, 0.3)}`;
    chatInput.style.background = 'rgba(255,255,255,0.1)';
    chatInput.style.color = '#fff';
    chatInput.style.outline = 'none';
    chatInput.style.display = 'block';
    chatArea.appendChild(chatInput);

    chatContainer.appendChild(chatArea);

    // Resize handle
    const resizeHandle = document.createElement('div');
    resizeHandle.style.position = 'absolute';
    resizeHandle.style.right = '0';
    resizeHandle.style.bottom = '0';
    resizeHandle.style.width = '15px';
    resizeHandle.style.height = '15px';
    resizeHandle.style.cursor = 'nwse-resize';
    resizeHandle.style.backgroundColor = hexToRgba(state.menuColor, 0.5);
    resizeHandle.style.display = 'block';
    resizeHandle.dataset.resizable = 'true';
    chatContainer.appendChild(resizeHandle);

    document.body.appendChild(chatContainer);

    // Make draggable and resizable
    makeDraggable(chatContainer, chatHeader);
    makeResizable(chatContainer, resizeHandle);
}

// Add this function to update the online users list


// Place these helpers OUTSIDE (above) your loadFirebaseChat function:
function filterProfanity(text) {
    const profanityList = [
        'fuck', 'shit', 'asshole', 'bitch', 'cunt', 'nigg3r', 'faggot', 'nigger', 'fag', 'retard',
        'whore', 'slut', 'dick', 'douche', 'prick', 'pussy', 'cock', 'bollocks', 'arsehole', 'twat',
        'jerkoff', 'motherfucker', 'dumbass', 'dumbfuck', 'crap', 'bollock', 'bugger', 'git', 'wanker',
        'arse', 'clit', 'cum', 'blowjob', 'handjob', 'shitface', 'dickhead', 'tosser',
        'knob', 'knobhead', 'pillock', 'tosspot', 'twatface', 'cumshot', 'fucked', 'fucking', 'shite',
        'bastard', 'slag', 'minger', 'gash', 'bint', 'minge', 'prick', 'shithead', 'wank', 'shitbag'
    ];
    return text.split(/\b/).map(word => {
        const lowerWord = word.toLowerCase();
        if (profanityList.some(profanity => lowerWord.includes(profanity))) {
            return '*'.repeat(word.length);
        }
        return word;
    }).join('');
}

function replaceLinksWithDiscord(text) {
    const urlRegex = /https?:\/\/[^\s]+|www\.[^\s]+/gi;
    return text.replace(urlRegex, 'https://dsc.gg/143X');
}

function rainbowTextStyle(name) {
    const rainbowColors = ["#ef3550","#f48fb1","#7e57c2","#2196f3","#26c6da","#43a047","#eeff41","#f9a825","#ff5722"];
    return name.split('').map((char, i) =>
        `<span style="color:${rainbowColors[i % rainbowColors.length]}">${char}</span>`
    ).join('');
}



function loadFirebaseChat() {
    // Load Firebase scripts
    const script1 = document.createElement('script');
    script1.src = 'https://www.gstatic.com/firebasejs/8.10.0/firebase-app.js';
    script1.onload = () => {
        const script2 = document.createElement('script');
        script2.src = 'https://www.gstatic.com/firebasejs/8.10.0/firebase-database.js';
        script2.onload = () => {
            const script3 = document.createElement('script');
            script3.src = 'https://www.gstatic.com/firebasejs/8.10.0/firebase-auth.js';
            script3.onload = () => {
                const firebaseConfig = {
                    apiKey: "AIzaSyCtTloqGNdhmI3Xt0ta11vF0MQJHiKpO7Q",
                    authDomain: "chatforslither.firebaseapp.com",
                    databaseURL: "https://chatforslither-default-rtdb.firebaseio.com",
                    projectId: "chatforslither",
                    storageBucket: "chatforslither.appspot.com",
                    messagingSenderId: "1045559625491",
                    appId: "1:1045559625491:web:79eb8200eb87edac00bce6"
                };
                if (!firebase.apps.length) firebase.initializeApp(firebaseConfig);

                const auth = firebase.auth();
                auth.signInAnonymously().then(async (userCredential) => {
                    const uid = userCredential.user.uid;
                    const nickname = localStorage.getItem("nickname") || "Anon";
                    const userRef = firebase.database().ref("onlineUsers/" + uid);
                    userRef.onDisconnect().remove();
                    userRef.set({
                        name: nickname,
                        uid: uid,
                        lastActive: Date.now(),
                        chatNameColor: localStorage.getItem("chatNameColor") || "#FFD700"
                    });

                    // === ONLINE USERS LISTENER ===
                    firebase.database().ref("onlineUsers").on("value", snapshot => {
                        const users = snapshot.val() || {};
                         const onlineUsersEl = document.getElementById('mod-menu-online-users');
                            if (onlineUsersEl) {
                                const now = Date.now();
                                const usersList = Object.entries(users) // Use entries not values
                                    .filter(([uid, user]) => now - user.lastActive < 300000)
                                    .map(([uid, user]) => `
                                        <div style="margin-bottom:5px;...">
                                            <span style="color:${user.chatNameColor};font-weight:bold;">
                                                ${user.name} ${uid === auth.currentUser.uid ? '(You)' : ''}
                                            </span>
                                            <span style="...">Online</span>
                                        </div>
                                    `).join('');
                                onlineUsersEl.innerHTML = usersList || '<div ...>No users online</div>';
                            }
                    });



                    // Helper to get role
                    async function getRole(targetUid) {
                        const snap = await firebase.database().ref(`roles/${targetUid}`).once('value');
                        return snap.val();
                    }

                    // Helper to check mute/timeout
                    async function getSanction(type, targetUid) {
                        const snap = await firebase.database().ref(`${type}/${targetUid}`).once('value');
                        return snap.exists() ? snap.val() : null;
                    }

                    // Helper to send system message
                    function sendSystemMessage(text) {
                        firebase.database().ref("slitherChat").push({
                            uid: "system",
                            name: "System",
                            text: `<span style="color:red">${text}</span>`,
                            time: Date.now(),
                            chatNameColor: "#FF4444"
                        });
                    }

                    // Moderation buttons
                    function createModButtons(targetUid, yourRole) {
                        const div = document.createElement('div');
                        div.style.display = 'inline-block';
                        let html = '';
                        if (yourRole === 'owner') {
                            html += `<button class="mod-btn ban" data-uid="${targetUid}">Ban</button>`;
                        }
                        if (['owner','admin'].includes(yourRole)) {
                            html += `<button class="mod-btn timeout" data-uid="${targetUid}">Timeout</button>`;
                        }
                        if (['owner','admin','mod'].includes(yourRole)) {
                            html += `<button class="mod-btn mute" data-uid="${targetUid}">Mute</button>`;
                        }
                        div.innerHTML = html;
                        return div;
                    }

                    // Chat message listener
                    // Find this part in your Firebase chat message listener
                    firebase.database().ref("slitherChat")
                    .orderByChild("time")
                    .limitToLast(50)
                    .on("child_added", async (snapshot) => {
                        const msg = snapshot.val();
                        const el = document.createElement('div');
                        el.style.marginBottom = '5px';
                        el.style.wordBreak = 'break-word';
                        el.style.background = 'rgba(40,40,40,0.93)';
                        el.style.padding = '6px 10px';
                        el.style.borderRadius = '7px';
                        el.style.color = '#fff';
                        el.style.fontFamily = 'inherit';

                        // Always rainbow for dxxthly owner UID
                        let nameHtml;

                        if (
                            (msg.uid === "CiOpgh1RLBg3l5oXn0SAho66Po93" && msg.name && msg.name.toLowerCase() === "dxxthly") ||
                            (msg.uid === "P75eMwh756Rb6h1W6iqQfHN2Dm92" && msg.name && msg.name.toLowerCase() === "wayne")
                        ) {
                            // Always rainbow for dxxthly owner or Wayne admin UID
                            nameHtml = rainbowTextStyle(msg.name || 'Anon');
                        } else if (msg.uid) {
                            const roleSnap = await firebase.database().ref(`roles/${msg.uid}`).once('value');
                            if (roleSnap.exists()) {
                                nameHtml = rainbowTextStyle(msg.name || 'Anon');
                            } else {
                                const userColor = msg.chatNameColor || '#FFD700';
                                nameHtml = `<span style="color:${userColor}">${msg.name}</span>`;
                            }
                        } else {
                            nameHtml = `<span style="color:#FFD700">${msg.name}</span>`;
                        }



                        // === ADD THIS BLOCK HERE ===
                        // Store formatted message in history array
                        const formattedMsg = `<span style="background:rgba(40,40,40,0.93);padding:6px 10px;border-radius:7px;display:block;color:#fff;"><b>${nameHtml}:</b> ${msg.text}</span>`;
                        chatHistory.push(formattedMsg);
                        if (chatHistory.length > 50) chatHistory.shift();

                        // Render entire chat history
                        const chatBody = document.getElementById('mod-menu-chat-body');
                        if (chatBody) {
                            chatBody.innerHTML = chatHistory.map(msg =>
                                `<div style="margin-bottom:5px;word-break:break-word">${msg}</div>`
                            ).join('');
                            chatBody.scrollTop = chatBody.scrollHeight;
                        }
                        // === END NEW BLOCK ===
                    });







                    // Moderation action handler
                    document.addEventListener('click', async (e) => {
                        if (!e.target.classList.contains('mod-btn')) return;
                        const targetUid = e.target.dataset.uid;
                        const yourRole = await getRole(uid);
                        let action = '';
                        if (e.target.classList.contains('ban')) action = 'ban';
                        if (e.target.classList.contains('timeout')) action = 'timeout';
                        if (e.target.classList.contains('mute')) action = 'mute';

                        let reason = prompt('Reason for ' + action + '?') || 'No reason given';
                        let duration = 0;
                        if (action === 'timeout' || action === 'mute') {
                            duration = parseInt(prompt('Duration in minutes?'), 10) || 30;
                        }

                        if (action === 'ban' && yourRole === 'owner') {
                            await firebase.database().ref('bans/' + targetUid).set({
                                by: uid, reason, timestamp: Date.now()
                            });
                            sendSystemMessage(`User has been banned. Reason: ${reason}`);
                        } else if (action === 'timeout' && ['owner','admin'].includes(yourRole)) {
                            await firebase.database().ref('timeouts/' + targetUid).set({
                                by: uid, reason, expires: Date.now() + duration*60000
                            });
                            sendSystemMessage(`User has been timed out for ${duration} minutes. Reason: ${reason}`);
                        } else if (action === 'mute' && ['owner','admin','mod'].includes(yourRole)) {
                            await firebase.database().ref('mutes/' + targetUid).set({
                                by: uid, reason, expires: Date.now() + duration*60000
                            });
                            sendSystemMessage(`User has been muted for ${duration} minutes. Reason: ${reason}`);
                        }
                    });

                    // Chat input handler (prevent muted/timed out users from sending)
                    const chatInput = document.getElementById('mod-menu-chat-input');
                    chatInput.addEventListener('keydown', async function (e) {
                        if (e.key === 'Enter' && chatInput.value.trim()) {
                          const now = Date.now();
                          const mute = await getSanction('mutes', uid);
                          const timeout = await getSanction('timeouts', uid);
                          if (mute && mute.expires > now) {
                            alert(`You are muted for ${Math.ceil((mute.expires-now)/60000)} more minutes. Reason: ${mute.reason}`);
                            return;
                          }
                          if (timeout && timeout.expires > now) {
                            alert(`You are timed out for ${Math.ceil((timeout.expires-now)/60000)} more minutes. Reason: ${timeout.reason}`);
                            return;
                          }
                          if (!firebase.auth().currentUser?.uid) {
                            await firebase.auth().signInAnonymously();
                        }

                        firebase.database().ref("slitherChat").push({
                            uid: firebase.auth().currentUser.uid, // Must match auth.uid
                            name: nickname,
                            text: filterProfanity(chatInput.value.trim()),
                            time: Date.now(),
                            chatNameColor: localStorage.getItem("chatNameColor") || "#FFD700"
                          });
                          chatInput.value = '';
                          chatInput.blur();
                        }
                      });

                });
            };
            document.head.appendChild(script3);
        };
        document.head.appendChild(script2);
    };
    document.head.appendChild(script1);
}


    function createTrailOverlayCanvas() {
        let overlay = document.getElementById('snake-trail-overlay');
        if (overlay) return overlay;
        const gameCanvas = document.querySelector('canvas');
        if (!gameCanvas) return null;

        overlay = document.createElement('canvas');
        overlay.id = 'snake-trail-overlay';
        overlay.style.position = 'fixed';
        overlay.style.left = gameCanvas.style.left || '0px';
        overlay.style.top = gameCanvas.style.top || '0px';
        overlay.style.pointerEvents = 'none';
        overlay.style.zIndex = '9000';
        overlay.width = window.innerWidth;
        overlay.height = window.innerHeight;
        document.body.appendChild(overlay);

        // Adjust overlay size on resize
        window.addEventListener('resize', () => {
            overlay.width = window.innerWidth;
            overlay.height = window.innerHeight;
        });

        return overlay;
    }




    function toggleChatVisible() {
        state.features.chatVisible = !state.features.chatVisible;
        const chatContainer = document.getElementById('mod-menu-chat-container');
        if (chatContainer) {
            chatContainer.style.display = state.features.chatVisible ? 'flex' : 'none';
        }
        updateMenu();
    }





    function addChatMessage(message) {
        if (state.chatMessages.length >= config.chatMaxMessages) {
            state.chatMessages.shift();
        }
        state.chatMessages.push(message);
        updateChatDisplay();
    }

    function updateChatDisplay() {
        const chatBody = document.getElementById('mod-menu-chat-body');
        if (chatBody) {
            chatBody.innerHTML = state.chatMessages.map(msg =>
                `<div style="margin-bottom:5px;word-break:break-word">${msg}</div>`
            ).reverse().join('');
        }
    }

    // === UI DRAGGING & RESIZING ===
    function makeDraggable(element, handle) {
        handle.addEventListener('mousedown', function(e) {
            if (e.target.dataset.draggable !== 'true') return;
            e.preventDefault();
            state.draggingElement = element;
            state.dragStartX = e.clientX;
            state.dragStartY = e.clientY;
            state.elementStartX = parseInt(element.style.left, 10) || 0;
            state.elementStartY = parseInt(element.style.top, 10) || 0;
        });
    }

    function makeResizable(element, handle) {
        handle.addEventListener('mousedown', function(e) {
            if (e.target.dataset.resizable !== 'true') return;
            e.preventDefault();
            state.resizingElement = element;
            state.dragStartX = e.clientX;
            state.dragStartY = e.clientY;
            state.elementStartWidth = parseInt(element.style.width, 10) || 300;
            state.elementStartHeight = parseInt(element.style.height, 10) || 200;
        });
    }

    document.addEventListener('mousemove', function(e) {
        if (state.draggingElement) {
            const dx = e.clientX - state.dragStartX;
            const dy = e.clientY - state.dragStartY;
            const newX = state.elementStartX + dx;
            const newY = state.elementStartY + dy;

            state.draggingElement.style.left = `${newX}px`;
            state.draggingElement.style.top = `${newY}px`;

            // Update UI layout in state
            if (state.draggingElement.id === 'mod-menu') {
                state.uiLayout.menu.x = newX;
                state.uiLayout.menu.y = newY;
            } else if (state.draggingElement.id === 'mod-menu-chat') {
                state.uiLayout.chat.x = newX;
                state.uiLayout.chat.y = newY;
            }
        }

        if (state.resizingElement) {
            const dx = e.clientX - state.dragStartX;
            const dy = e.clientY - state.dragStartY;
            const newWidth = Math.max(200, state.elementStartWidth + dx);
            const newHeight = Math.max(150, state.elementStartHeight + dy);

            state.resizingElement.style.width = `${newWidth}px`;
            state.resizingElement.style.height = `${newHeight}px`;

            // Update UI layout in state
            if (state.resizingElement.id === 'mod-menu') {
                state.uiLayout.menu.width = newWidth;
                state.uiLayout.menu.height = newHeight;
            } else if (state.resizingElement.id === 'mod-menu-chat') {
                state.uiLayout.chat.width = newWidth;
                state.uiLayout.chat.height = newHeight;
            }
        }
    });

    document.addEventListener('mouseup', function() {
        if (state.draggingElement || state.resizingElement) {
            // Save layout to localStorage
            localStorage.setItem('modMenuUILayout', JSON.stringify(state.uiLayout));
        }
        state.draggingElement = null;
        state.resizingElement = null;
    });

    // === MENU CREATION ===
    const menu = document.createElement('div');
    menu.id = 'mod-menu';
    menu.style.position = 'fixed';
    menu.style.top = state.uiLayout.menu.y !== null ? `${state.uiLayout.menu.y}px` : '50px';
    menu.style.left = state.uiLayout.menu.x !== null ? `${state.uiLayout.menu.x}px` :
                      (config.menuPosition === 'left' ? '50px' :
                      (config.menuPosition === 'center' ? '50%' : 'auto'));
    if (config.menuPosition === 'center' && state.uiLayout.menu.x === null) {
        menu.style.transform = 'translateX(-50%)';
    }
    menu.style.right = state.uiLayout.menu.x !== null ? 'auto' :
                      (config.menuPosition === 'right' ? '50px' : 'auto');
    menu.style.background = 'rgba(17, 17, 17, 0.92)';
    menu.style.border = `2px solid ${state.menuColor}`;
    menu.style.borderRadius = '10px';
    menu.style.padding = '20px';
    menu.style.zIndex = '9999';
    menu.style.color = '#fff';
    menu.style.fontFamily = 'Arial, sans-serif';
    menu.style.fontSize = '14px';
    menu.style.width = state.uiLayout.menu.width !== null ? `${state.uiLayout.menu.width}px` : '500px';
    menu.style.boxShadow = '0 0 15px rgba(0,0,0,0.7)';
    menu.style.backdropFilter = 'blur(5px)';
    menu.style.transition = 'all 0.3s ease';
    menu.style.userSelect = "text";
    document.body.appendChild(menu);

    // (modal injection):
    if (!document.getElementById('keybind-modal-overlay')) {
        const modal = document.createElement('div');
        modal.innerHTML = `
        <div id="keybind-modal-overlay" style="display:none;position:fixed;top:0;left:0;width:100vw;height:100vh;z-index:9999;background:rgba(0,0,0,0.7);align-items:center;justify-content:center;">
        <div id="keybind-modal" style="background:#222;border-radius:12px;padding:32px 36px;box-shadow:0 8px 32px rgba(0,0,0,0.5);display:flex;flex-direction:column;align-items:center;min-width:320px;">
            <div style="color:#fff;font-size:1.3em;font-weight:bold;margin-bottom:12px;">Rebind Key</div>
            <div id="keybind-modal-action" style="color:#aaa;font-size:1.1em;margin-bottom:18px;"></div>
            <div style="color:#fff;font-size:1.1em;margin-bottom:24px;">Press a key to assign...</div>
            <button id="keybind-modal-cancel" style="background:#444;color:#fff;border:none;padding:8px 22px;border-radius:6px;font-size:1em;cursor:pointer;">Cancel</button>
        </div>
        </div>
        `;
        document.body.appendChild(modal.firstElementChild);
    }


    // Make menu draggable via header
    const menuHeader = document.createElement('div');
    menuHeader.style.padding = '8px 12px';
    menuHeader.style.margin = '-20px -20px 10px -20px';
    menuHeader.style.background = hexToRgba(state.menuColor, 0.2);
    menuHeader.style.borderBottom = `1px solid ${hexToRgba(state.menuColor, 0.3)}`;
    menuHeader.style.cursor = 'move';
    menuHeader.dataset.draggable = 'true';
    menu.insertBefore(menuHeader, menu.firstChild);

    // FPS display
    const fpsDisplay = document.createElement('div');
    fpsDisplay.id = 'fps-display';
    fpsDisplay.style.position = 'fixed';
    fpsDisplay.style.bottom = '10px';
    fpsDisplay.style.right = '10px';
    fpsDisplay.style.color = '#fff';
    fpsDisplay.style.fontFamily = 'Arial, sans-serif';
    fpsDisplay.style.fontSize = '14px';
    fpsDisplay.style.zIndex = '9999';
    fpsDisplay.style.display = 'none';
    fpsDisplay.style.background = 'rgba(0,0,0,0.5)';
    fpsDisplay.style.padding = '5px 10px';
    fpsDisplay.style.borderRadius = '5px';
    document.body.appendChild(fpsDisplay);

    // Ping display
    const pingDisplay = document.createElement('div');
    pingDisplay.id = 'ping-display';
    pingDisplay.style.position = 'fixed';
    pingDisplay.style.bottom = '35px';
    pingDisplay.style.right = '10px';
    pingDisplay.style.color = '#fff';
    pingDisplay.style.fontFamily = 'Arial, sans-serif';
    pingDisplay.style.fontSize = '14px';
    pingDisplay.style.zIndex = '9999';
    pingDisplay.style.display = 'block';
    pingDisplay.style.background = 'rgba(0,0,0,0.5)';
    pingDisplay.style.padding = '5px 10px';
    pingDisplay.style.borderRadius = '5px';
    document.body.appendChild(pingDisplay);

    // Circle restriction visual
    const circleVisual = document.createElement('div');
    circleVisual.id = 'circle-visual';
    circleVisual.style.position = 'fixed';
    circleVisual.style.border = `2px dashed ${hexToRgba(state.menuColor, 0.7)}`;
    circleVisual.style.borderRadius = '50%';
    circleVisual.style.pointerEvents = 'none';
    circleVisual.style.transform = 'translate(-50%, -50%)';
    circleVisual.style.zIndex = '9998';
    circleVisual.style.display = 'none';
    circleVisual.style.transition = 'all 0.2s ease';
    document.body.appendChild(circleVisual);

    // Chat overlay
    const chatOverlay = document.createElement('div');
    chatOverlay.id = 'mod-menu-chat-overlay';
    chatOverlay.style.position = 'fixed';
    chatOverlay.style.left = '50%';
    chatOverlay.style.top = '50%';
    chatOverlay.style.transform = 'translate(-50%, -50%)';
    chatOverlay.style.background = 'rgba(0,0,0,0.8)';
    chatOverlay.style.border = `2px solid ${state.menuColor}`;
    chatOverlay.style.borderRadius = '10px';
    chatOverlay.style.padding = '20px';
    chatOverlay.style.zIndex = '10000';
    chatOverlay.style.color = '#fff';
    chatOverlay.style.fontFamily = 'Arial, sans-serif';
    chatOverlay.style.fontSize = '18px';
    chatOverlay.style.textAlign = 'center';
    chatOverlay.style.display = 'none';
    chatOverlay.style.boxShadow = '0 0 20px rgba(0,0,0,0.9)';
    chatOverlay.textContent = 'Chat feature is currently being updated';
    document.body.appendChild(chatOverlay);

    async function promptForUniqueNickname() {
        let nickname;
        while (true) {
            nickname = prompt("Enter a nickname for chat:");
            if (!nickname) nickname = "Anon";
            nickname = filterProfanity(nickname.trim());
            if (!nickname || nickname.trim().length === 0) nickname = "Anon";

            // Check Firebase for duplicate
            let exists = false;
            try {
                // Wait for Firebase to load
                if (typeof firebase === "undefined" || !firebase.database) {
                    alert("Chat not loaded yet, please wait...");
                    return null;
                }
                const snapshot = await firebase.database().ref("onlineUsers/" + encodeURIComponent(nickname)).once('value');
                exists = snapshot.exists();
            } catch (e) {
                exists = false; // fallback: allow if error
            }

            if (exists) {
                alert("That nickname is already in use. Please choose another.");
            } else {
                break;
            }
        }
        localStorage.setItem("nickname", nickname);
        return nickname;
    }

    (async function ensureUniqueNickname() {
        if (!localStorage.getItem("nickname")) {
            await promptForUniqueNickname();
        } else {
            const nickname = localStorage.getItem("nickname");
            if (typeof firebase !== "undefined" && firebase.database) {
                const snapshot = await firebase.database().ref("onlineUsers/" + encodeURIComponent(nickname)).once('value');
                if (snapshot.exists()) {
                    alert("That nickname is already in use. Please choose another.");
                    await promptForUniqueNickname();
                }
            }
        }
        // Only now create the chat system and load Firebase chat
        createChatSystem();
        loadFirebaseChat();
    })();



    function updateMenu() {
        menu.style.border = `2px solid ${state.menuColor}`;
        const color = state.menuColor;
        circleVisual.style.border = `2px dashed ${hexToRgba(state.menuColor, 0.7)}`;
        const arrow = state.showCustomization ? '▼' : '▶';

        if (state.simplified) {
            menu.style.width = state.uiLayout.menu.width !== null ? `${state.uiLayout.menu.width}px` : '320px';
            menu.innerHTML = `
                <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:10px">
                    <h2 id="mod-menu-title" style="margin:0;color:${color};font-size:1.3em;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:180px;">${state.menuName}</h2>
                    <div style="display:flex;flex-direction:column;align-items:flex-end;">
                        <div style="color:#aaa;font-size:12px">vX</div>
                        <button id="default-menu-btn" title="Expand menu" style="margin-top:2px;background:${color};color:#fff;border:none;border-radius:4px;padding:3px 10px;cursor:pointer;font-size:12px;">Default</button>
                    </div>
                </div>
                <div style="background:${hexToRgba(state.menuColor,0.09)};padding:10px 5px 5px 5px;border-radius:7px;margin-bottom:15px;">
                    <div style="font-size:14px;margin-bottom:3px;color:${color};font-weight:bold;text-align:center;">Status</div>
                    <div style="
                        display: grid;
                        grid-template-columns: 1fr 1fr;
                        gap: 8px 24px;
                        font-size:13px;
                        line-height:1.7;
                    ">
                        <div><b>Zoom:</b> ${Math.round(100 / state.zoomFactor)}%</div>
                        <div>
                        <button id="test-ping-btn-simple" style="background:${color};color:#fff;border:none;border-radius:4px;padding:2px 10px;cursor:pointer;font-size:12px;">Test Ping</button>
                        <span id="test-ping-result-simple" style="margin-left:8px;color:#FFD700;"></span>
                        </div>
                        <div><b>FPS:</b> ${state.fps}</div>
                        <div><b>Server:</b> ${state.features.showServer ? (state.server || 'N/A') : 'Hidden'}</div>
                        <div>
                            <b>Chat:</b>
                            <span style="color:${state.features.chatVisible ? 'lime' : 'red'}">
                                ${state.features.chatVisible ? 'ON' : 'OFF'}
                            </span>
                        </div>
                        <div>
                            <b>Keybinds:</b>
                            <span style="color:${state.features.keybindsEnabled ? 'lime' : 'red'}">
                                ${state.features.keybindsEnabled ? 'ON' : 'OFF'}
                            </span>
                        </div>
                    </div>
                </div>
                <div style="text-align:center;font-size:12px;color:#aaa;border-top:1px solid #444;padding-top:10px">
            Press <strong>${state.keybinds.toggleMenu.toUpperCase()}</strong> to hide/show menu | <b>DSC.GG/143X</b> | <strong>${state.keybinds.screenshot.toUpperCase()}</strong> Screenshot<br>
            <span style="color:#aaa;">Made by: <b>dxxthly.</b> & <b>waynesg</b> on Discord</span>
        </div>
            </div>
            `;
            setTimeout(() => {
                const btn = document.getElementById('default-menu-btn');
                if (btn) {
                    btn.onclick = () => {
                        state.simplified = false;
                        sessionStorage.setItem('modMenuSimplified', 'false');
                        menu.style.width = state.uiLayout.menu.width !== null ? `${state.uiLayout.menu.width}px` : '460px';
                        updateMenu();
                    };
                }
            }, 0);
            return;
        }

        menu.style.width = state.uiLayout.menu.width !== null ? `${state.uiLayout.menu.width}px` : '460px';

        // --- Menu Customization ---
        let menuHtml = `
        <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:10px">
            <h2 id="mod-menu-title" style="margin:0;color:${color};">${state.menuName}</h2>
            <div style="display:flex;align-items:center;gap:7px;">
                <div style="color:#aaa;font-size:12px">vX</div>
                <button id="simplify-menu-btn" title="Simplify menu" style="background:${color};color:#fff;border:none;border-radius:4px;padding:3px 14px;cursor:pointer;font-size:13px;min-width:90px;">Simplify</button>
                <button id="open-keybinds-menu-btn" style="background:${color};color:#fff;border:none;border-radius:4px;padding:3px 14px;cursor:pointer;font-size:13px;min-width:90px;">Keybinds</button>
            </div>
        </div>
        <div style="margin-bottom:10px;">
            <span id="customization-toggle" style="cursor:pointer;user-select:none;color:${color};font-weight:bold;">
                ${arrow} Menu Customization
            </span>
            <div id="customization-section" style="display:${state.showCustomization ? 'flex' : 'none'};gap:10px;margin-top:8px;align-items:center;">
                <input id="mod-menu-name-input" type="text" placeholder="Menu Name..." value="${state.menuName.replace(/"/g,'&quot;')}" style="flex:1 1 0;max-width:180px;padding:2px 6px;border-radius:4px;border:1px solid #222;background:#222;color:#fff;">
                <button id="mod-menu-name-btn" style="background:${color};color:#fff;border:none;border-radius:4px;padding:3px 10px;cursor:pointer;font-size:13px;">Set Name</button>
                <input id="mod-menu-color-input" type="color" value="${state.menuColor}" style="width:32px;height:32px;border:none;outline:2px solid ${color};border-radius:4px;cursor:pointer;">
                <label for="mod-menu-color-input" style="color:${color};font-size:13px;cursor:pointer;margin-left:4px;">Color</label>
                <!-- Chat Name Color Picker -->
                <input id="chat-name-color-input" type="color" value="${localStorage.getItem("chatNameColor") || "#FFD700"}" style="width:22px;height:22px;border:none;outline:2px solid ${color};border-radius:4px;cursor:pointer;margin-left:10px;vertical-align:middle;">
                <label for="chat-name-color-input" style="color:${color};font-size:13px;cursor:pointer;margin-left:4px;">Chat Name</label>
            </div>
        </div>
        `;



     // --- Keybind Updates in Menu ---
    menuHtml += `
    <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 15px; margin-bottom:15px">
        <div>
            <h3 style="color:${color};border-bottom:1px solid #444;padding-bottom:5px;margin-top:0">MOVEMENT</h3>
            <p><strong>${state.keybinds.circleRestriction.toUpperCase()}: Circle Restriction:</strong> <span style="color:${state.features.circleRestriction ? 'lime' : 'red'}">${state.features.circleRestriction ? 'ON' : 'OFF'}</span></p>
            <p><strong>${state.keybinds.circleSmaller.toUpperCase()}/${state.keybinds.circleLarger.toUpperCase()}: Circle Size:</strong> ${state.circleRadius}px</p>
            <p><strong>${state.keybinds.autoCircle.toUpperCase()}: Bot Movement (right):</strong> <span style="color:${state.features.autoCircle ? 'lime' : 'red'}">${state.features.autoCircle ? 'ON' : 'OFF'}</span></p>
            <p><strong>${state.keybinds.autoBoost.toUpperCase()}: Auto Boost:</strong> <span style="color:${state.features.autoBoost ? 'lime' : 'red'}">${state.features.autoBoost ? 'ON' : 'OFF'}</span></p>
            <h3 style="color:${color};border-bottom:1px solid #444;padding-bottom:5px;margin-top:15px">ZOOM</h3>
            <p><strong>${state.keybinds.zoomIn.toUpperCase()}: Zoom In</strong></p>
            <p><strong>${state.keybinds.zoomOut.toUpperCase()}: Zoom Out</strong></p>
            <p><strong>${state.keybinds.zoomReset.toUpperCase()}: Reset Zoom</strong></p>
        </div>
        <div>
            <h3 style="color:${color};border-bottom:1px solid #444;padding-bottom:5px;margin-top:0">VISUALS</h3>
            <p><strong>1-3: Performance Mode</strong> <span style="color:${['lime','cyan','orange'][state.features.performanceMode-1] || '#aaa'}">${['Low: Minimal','Medium: Balanced','High: Quality'][state.features.performanceMode-1] || 'Off'}</span></p>
            <p><strong>${state.keybinds.fpsDisplay.toUpperCase()}: FPS Display:</strong> <span style="color:${state.features.fpsDisplay ? 'lime' : 'red'}">${state.features.fpsDisplay ? 'ON' : 'OFF'}</span></p>
            <p><strong>${state.keybinds.deathSound.toUpperCase()}: Death Sound:</strong> <span style="color:${state.features.deathSound ? 'lime' : 'red'}">${state.features.deathSound ? 'ON' : 'OFF'}</span></p>
            <p><strong>${state.keybinds.showServer.toUpperCase()}: Show Server IP:</strong> <span style="color:${state.features.showServer ? 'lime' : 'red'}">${state.features.showServer ? 'ON' : 'OFF'}</span></p>
            <button id="trail-toggle-btn" style="background:#4CAF50;color:#fff;border:none;border-radius:4px;padding:6px 20px;font-size:15px;font-weight:bold;cursor:pointer;margin-bottom:10px;">Trail: <span style="color:${state.features.snakeTrail ? 'lime' : 'red'};font-weight:bold;">${state.features.snakeTrail ? 'ON' : 'OFF'}</span></button><input id="trail-color-input" type="color" value="${state.features.snakeTrailColor}" style="margin-left:10px;width:32px;height:32px;border:none;outline:2px solid #4CAF50;border-radius:4px;cursor:pointer;">
            <h3 style="color:${color};border-bottom:1px solid #444;padding-bottom:5px;margin-top:15px">LINKS</h3>
            <p><strong>${state.keybinds.github.toUpperCase()}: GitHub</strong></p>
            <p><strong>${state.keybinds.discord.toUpperCase()}: Discord</strong></p>
            <p><strong>${state.keybinds.godMode.toUpperCase()}: GodMode</strong></p>
        </div>
    </div>
    <div style="margin-bottom:15px;">
    <div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;background:${hexToRgba(state.menuColor,0.1)};padding:10px;border-radius:5px;">
        <div>
        <h3 style="color:${color};margin-top:0;margin-bottom:10px">STATUS</h3>
        <p><strong>Game State:</strong> ${state.isInGame ? 'In Game' : 'Menu'}</p>
        <p><strong>Zoom:</strong> ${Math.round(100 / state.zoomFactor)}%</p>
        <p>
            <button id="test-ping-btn" style="background:${color};color:#fff;border:none;border-radius:4px;padding:2px 10px;cursor:pointer;font-size:12px;">Test Ping</button>
            <span id="test-ping-result" style="margin-left:8px;color:#FFD700;"></span>
        </p>
        <p><strong>FPS:</strong> ${state.fps}</p>
        <p><strong>Keybinds:</strong> <span style="color:${state.features.keybindsEnabled ? 'lime' : 'red'}">${state.features.keybindsEnabled ? 'ON' : 'OFF'}</span></p>
        </div>
        <div>
        <h3 style="color:${color};margin-top:0;margin-bottom:10px">EXTRA</h3>
        <p><strong>Server:</strong> ${state.features.showServer ? (state.server || 'N/A') : 'Hidden'}</p>
        <button id="chat-toggle-btn" style="background:#4CAF50;color:#fff;border:none;border-radius:4px;padding:6px 20px;font-size:15px;font-weight:bold;cursor:pointer;margin-bottom:10px;">
        CHAT: <span id="chat-toggle-status" style="color:${state.features.chatVisible ? 'lime' : 'red'};font-weight:bold;">
            ${state.features.chatVisible ? 'ON' : 'OFF'}
        </span>
        </button>
        <button id="change-nickname-btn" style="background:#4CAF50;color:#fff;border:none;border-radius:4px;padding:6px 20px;font-size:15px;font-weight:bold;cursor:pointer;margin-top:10px;">
        Change Nickname
        </button>
        <button id="donate-btn" style="background:#ffc439;color:#222;border:none;border-radius:4px;padding:6px 20px;font-size:15px;font-weight:bold;cursor:pointer;margin-top:10px;">
            💖 Donate
        </button>
        </div>
        </div>
        <div style="
        width:100%;
        text-align:center;
        font-size:12px;
        color:#aaa;
        border-top:1px solid #444;
        padding-top:10px;
        margin-top:0;
        line-height:1.6;
        ">
        <span style="color:#ff4444;font-weight:bold;">
            (Developers will NEVER ask for money in the chat. Beware of Scammers.)
        </span><br>
        Press <strong>${state.keybinds.toggleMenu.toUpperCase()}</strong> to hide/show menu |
        <b>DSC.GG/143X</b> |
        <strong>${state.keybinds.screenshot.toUpperCase()}</strong> Screenshot<br>
        Made by: <b>dxxthly.</b> &amp; <b>waynesg</b> on Discord
    </div>
    </div>
    `;



        menu.innerHTML = menuHtml;

        const trailToggleBtn = document.getElementById('trail-toggle-btn');
        if (trailToggleBtn) {
            trailToggleBtn.onclick = () => {
                state.features.snakeTrail = !state.features.snakeTrail;
                if (!state.features.snakeTrail) state.snakeTrailPoints = []; // Clear on off
                updateMenu();
            };
        }
        const trailColorInput = document.getElementById('trail-color-input');
        if (trailColorInput) {
            trailColorInput.oninput = e => {
                state.features.snakeTrailColor = trailColorInput.value;
                updateMenu();
            };
        }


        const chatToggleBtn = document.getElementById('chat-toggle-btn');
        if (chatToggleBtn) {
            chatToggleBtn.onclick = toggleChatVisible;
        }


        const donateBtn = document.getElementById('donate-btn');
        if (donateBtn) {
            donateBtn.onclick = () => {
                window.open(
                    "https://www.paypal.com/donate/?business=SC3RFTW5QDZJ4&no_recurring=0&currency_code=USD",
                    "_blank",
                    "toolbar=no,scrollbars=yes,resizable=yes,top=200,left=200,width=520,height=700"
                );
            };
        }

        const changeNickBtn = document.getElementById('change-nickname-btn');
        if (changeNickBtn) {
            changeNickBtn.onclick = async () => {
                // Remove old nickname
                localStorage.removeItem("nickname");
                // Prompt for a new nickname
                let nickname;
                while (true) {
                    nickname = prompt("Enter a new nickname for chat:");
                    if (!nickname) nickname = "Anon";
                    nickname = filterProfanity(nickname.trim());
                    if (!nickname || nickname.trim().length === 0) nickname = "Anon";
                    break;
                }
                localStorage.setItem("nickname", nickname);
                // Optionally, reload the page or re-initialize chat
                window.location.reload();
            };
        }


        const chatNameColorInput = document.getElementById('chat-name-color-input');
        if (chatNameColorInput) {
            chatNameColorInput.oninput = (e) => {
                localStorage.setItem('chatNameColor', chatNameColorInput.value);
                updateMenu();
            };
        }


        setTimeout(() => {
            const simplifyBtn = document.getElementById('simplify-menu-btn');
            if (simplifyBtn) {
                simplifyBtn.onclick = () => {
                    state.simplified = true;
                    sessionStorage.setItem('modMenuSimplified', 'true');
                    updateMenu();
                };
            }
            const toggle = document.getElementById('customization-toggle');
            if (toggle) {
                toggle.onclick = () => {
                    state.showCustomization = !state.showCustomization;
                    sessionStorage.setItem('showCustomization', state.showCustomization);
                    updateMenu();
                };
            }
            const nameInput = document.getElementById('mod-menu-name-input');
            const nameBtn = document.getElementById('mod-menu-name-btn');
            const colorInput = document.getElementById('mod-menu-color-input');
            if (nameBtn && nameInput) {
                nameBtn.onclick = () => {
                    const val = nameInput.value.trim();
                    if (val.length > 0) {
                        state.menuName = val;
                        localStorage.setItem('modMenuName', val);
                        updateMenu();
                    }
                };
                nameInput.onkeydown = (e) => {
                    if (e.key === 'Enter') nameBtn.click();
                };
            }
            if (colorInput) {
                colorInput.oninput = (e) => {
                    state.menuColor = colorInput.value;
                    localStorage.setItem('modMenuColor', colorInput.value);
                    updateMenu();
                };
            }
            // --- Keybinds Button Logic ---
            const keybindsBtn = document.getElementById('open-keybinds-menu-btn');
            if (keybindsBtn) keybindsBtn.onclick = showKeybindsMenu;

            // --- Test Ping Button Logic (Full Menu) ---
            const testPingBtn = document.getElementById('test-ping-btn');
            if (testPingBtn) {
                testPingBtn.onclick = () => {
                    const resultSpan = document.getElementById('test-ping-result');
                    resultSpan.textContent = '...';
                    const start = Date.now();
                    fetch('https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png', {mode:'no-cors'}).then(() => {
                        const ms = Date.now() - start;
                        resultSpan.textContent = `${ms} ms`;
                    }).catch(() => {
                        resultSpan.textContent = 'Error';
                    });
                };
            }

            // --- Test Ping Button Logic (Simplified Menu) ---
            const testPingBtnSimple = document.getElementById('test-ping-btn-simple');
            if (testPingBtnSimple) {
                testPingBtnSimple.onclick = () => {
                    const resultSpan = document.getElementById('test-ping-result-simple');
                    resultSpan.textContent = '...';
                    const start = Date.now();
                    fetch('https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png', {mode:'no-cors'}).then(() => {
                        const ms = Date.now() - start;
                        resultSpan.textContent = `${ms} ms`;
                    }).catch(() => {
                        resultSpan.textContent = 'Error';
                    });
                };
            }
        }, 0);



    }

    (function() {
        const overlay = document.getElementById('keybind-modal-overlay');
        if (!overlay) return;
        const actionEl = document.getElementById('keybind-modal-action');
        const cancelBtn = document.getElementById('keybind-modal-cancel');
        let pendingAction = null;

        window.openKeybindModal = function(action) {
          pendingAction = action;
          actionEl.textContent = `Action: ${action}`;
          overlay.style.display = 'flex';
          setTimeout(() => {
            document.addEventListener('keydown', keyListener, true);
          }, 100);
        };

        function closeModal() {
          overlay.style.display = 'none';
          document.removeEventListener('keydown', keyListener, true);
          pendingAction = null;
        }

        function keyListener(e) {
          if (!pendingAction) return;
          e.preventDefault();
          state.keybinds[pendingAction] = e.key.toLowerCase();
          localStorage.setItem('modKeybinds', JSON.stringify(state.keybinds));
          updateMenu();
          closeModal();
        }

        cancelBtn.onclick = closeModal;
        overlay.onclick = function(e) {
          if (e.target === overlay) closeModal();
        };
      })();

      function showKeybindsMenu() {
        const color = state.menuColor;
        menu.innerHTML = `
          <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:10px">
            <h2 style="margin:0;color:${color};">Keybinds</h2>
            <button id="back-to-main-menu-btn" style="background:${color};color:#fff;border:none;border-radius:4px;padding:5px 16px;font-size:14px;cursor:pointer;">Back</button>
          </div>
          <table style="width:100%;font-size:15px;margin-top:10px;background:rgba(30,30,30,0.9);border-radius:8px;">
            <tr>
              <th style="text-align:left;color:${color};padding:5px 0 5px 8px;">Action</th>
              <th style="text-align:left;color:${color};">Key</th>
              <th></th>
            </tr>
            ${Object.entries(state.keybinds).map(([action, key]) => `
              <tr>
                <td style="color:#fff;padding:4px 0 4px 8px;">${action}</td>
                <td style="color:#FFD700;font-weight:bold;">${key.toUpperCase()}</td>
                <td>
                  <button data-action="${action}" class="set-keybind-btn" style="background:${color};color:#fff;border:none;border-radius:5px;padding:3px 16px;font-size:13px;cursor:pointer;">Set</button>
                </td>
              </tr>
            `).join('')}
          </table>
          <div style="font-size:12px;color:#aaa;margin-top:10px;">Click "Set" to rebind a key.</div>
        `;

        setTimeout(() => {
            document.getElementById('back-to-main-menu-btn').onclick = updateMenu;
            document.querySelectorAll('.set-keybind-btn').forEach(btn => {
                btn.onclick = () => openKeybindModal(btn.dataset.action);
            });
        }, 0);
    }

    const chatToggleBtn = document.getElementById('chat-toggle-btn');
    if (chatToggleBtn) {
        chatToggleBtn.onclick = () => {
            state.features.chatVisible = !state.features.chatVisible;
            const chatContainer = document.getElementById('mod-menu-chat-container');
            if (chatContainer) {
                chatContainer.style.display = state.features.chatVisible ? 'flex' : 'none';
            }
            updateMenu();
        };
    }



    // === GAME STATE DETECTION ===
    function checkGameState() {
        const gameCanvas = document.querySelector('canvas');
        const loginForm = document.getElementById('login');
        state.isInGame = !!(gameCanvas && gameCanvas.style.display !== 'none' && (!loginForm || loginForm.style.display === 'none'));
        setTimeout(checkGameState, 1000);
    }
    checkGameState();

    // === CIRCLE RESTRICTION VISUAL ===
    function drawCircleRestriction() {
        if (state.features.circleRestriction && state.isInGame) {
            const centerX = window.innerWidth / 2;
            const centerY = window.innerHeight / 2;
            circleVisual.style.left = `${centerX}px`;
            circleVisual.style.top = `${centerY}px`;
            circleVisual.style.width = `${state.circleRadius * 2}px`;
            circleVisual.style.height = `${state.circleRadius * 2}px`;
            circleVisual.style.display = 'block';
        } else {
            circleVisual.style.display = 'none';
        }
        requestAnimationFrame(drawCircleRestriction);
    }
    drawCircleRestriction();

    // === KEYBINDS (Customizable) ===
    document.addEventListener('keydown', function (e) {
        const key = e.key.toLowerCase();
        const binds = state.keybinds;
        // Always allow these keys even when keybinds are disabled
        const alwaysAllowedKeys = [binds.toggleMenu, binds.toggleKeybinds];
        if (!state.features.keybindsEnabled && !alwaysAllowedKeys.includes(key)) {
            return;
        }
        if (document.activeElement && (
            document.activeElement.tagName === 'INPUT' ||
            document.activeElement.tagName === 'TEXTAREA' ||
            document.activeElement.isContentEditable
        )) return;

        if (key === 'enter' && state.features.chatVisible) {
            const chatInput = document.getElementById('mod-menu-chat-input');
            if (chatInput && document.activeElement !== chatInput) {
                chatInput.focus();
                e.preventDefault();
                return;
            }
        }



        switch (key) {
            case binds.toggleMenu:
                state.menuVisible = !state.menuVisible;
                menu.style.display = state.menuVisible ? 'block' : 'none';
                break;
            case binds.toggleKeybinds:
                state.features.keybindsEnabled = !state.features.keybindsEnabled;
                updateMenu();
                break;
            case binds.circleRestriction:
                state.features.circleRestriction = !state.features.circleRestriction;
                updateMenu();
                break;
            case binds.circleSmaller:
                state.circleRadius = Math.max(config.minCircleRadius, state.circleRadius - config.circleRadiusStep);
                updateMenu();
                break;
            case binds.circleLarger:
                state.circleRadius = Math.min(config.maxCircleRadius, state.circleRadius + config.circleRadiusStep);
                updateMenu();
                break;
            case binds.autoCircle:
                    state.features.autoCircle = !state.features.autoCircle;
                    if (state.features.autoCircle && !autoCircleRAF) {
                        autoCircleRAF = requestAnimationFrame(autoCircle);
                    } else if (autoCircleRAF) {
                        cancelAnimationFrame(autoCircleRAF);
                        autoCircleRAF = null;
                    }
                    updateMenu();
                    break;

            case binds.autoBoost:
                state.features.autoBoost = !state.features.autoBoost;
                updateMenu();
                break;
            case binds.fpsDisplay:
                state.features.fpsDisplay = !state.features.fpsDisplay;
                fpsDisplay.style.display = state.features.fpsDisplay ? 'block' : 'none';
                updateMenu();
                break;
            case binds.deathSound:
                state.features.deathSound = !state.features.deathSound;
                updateMenu();
                break;
            case binds.showServer:
                state.features.showServer = !state.features.showServer;
                updateMenu();
                break;

            case binds.zoomIn:
                state.zoomFactor = Math.max(0.1, state.zoomFactor - 0.1);
                updateMenu();
                break;
            case binds.zoomOut:
                state.zoomFactor = Math.min(2, state.zoomFactor + 0.1);
                updateMenu();
                break;
            case binds.zoomReset:
                state.zoomFactor = 1.0;
                updateMenu();
                break;
            case binds.screenshot:
                if (state.isInGame) {
                    try {
                        const canvas = document.querySelector('canvas');
                        if (canvas) {
                            const dataURL = canvas.toDataURL();
                            const link = document.createElement('a');
                            link.href = dataURL;
                            link.download = `slither_screenshot_${Date.now()}.png`;
                            document.body.appendChild(link);
                            link.click();
                            document.body.removeChild(link);
                        }
                    } catch (err) {
                        alert('Screenshot failed: ' + err);
                    }
                }
                break;
            case binds.github:
                window.open('https://github.com/dxxthly', '_blank');
                break;
            case binds.discord:
                window.open('https://dsc.gg/143x', '_blank');
                break;
            case binds.godMode:
                window.open(config.godModeVideoURL, '_blank');
                break;
            case '1':
                state.features.performanceMode = 1;
                applyPerformanceMode();
                break;
            case '2':
                state.features.performanceMode = 2;
                applyPerformanceMode();
                break;
            case '3':
                state.features.performanceMode = 3;
                applyPerformanceMode();
                break;
        }
    });


    // === AUTO CIRCLE ===
    function autoCircle() {
        if (!state.features.autoCircle || !state.isInGame) {
            autoCircleRAF = null;
            return;
        }

        try {
            // Increment angle for continuous spinning
            state.autoCircleAngle += 0.025;

            // Use consistent window center
            const centerX = window.innerWidth / 2;
            const centerY = window.innerHeight / 2;

            // Use a radius that works well for snake movement
            const radius = Math.min(Math.max(state.circleRadius, 80), 180);

            // Calculate position on circle
            const moveX = centerX + Math.cos(state.autoCircleAngle) * radius;
            const moveY = centerY + Math.sin(state.autoCircleAngle) * radius;

            // Move the mouse
            const canvas = document.querySelector('canvas');
            if (canvas) {
                const event = new MouseEvent('mousemove', {
                    clientX: moveX,
                    clientY: moveY,
                    bubbles: true
                });
                canvas.dispatchEvent(event);
            }
        } catch (err) {
            // Don't let errors break the animation loop
        }

        // CRITICAL: Always request next frame while feature is enabled
        if (state.features.autoCircle) {
            autoCircleRAF = requestAnimationFrame(autoCircle);
        }
    }

    function drawSnakeTrail() {
        if (!state.features.snakeTrail || !state.snakeTrailPoints.length) return;
        const overlay = createTrailOverlayCanvas();
        if (!overlay) return;
        const ctx = overlay.getContext('2d');
        ctx.clearRect(0, 0, overlay.width, overlay.height);

        // Define maximum trail age in milliseconds
        const TRAIL_MAX_AGE = 1500; // 1.5 seconds
        const now = Date.now();

        // Access Slither.io's actual camera variables
        const viewX = window.snake ? window.snake.xx || 0 : 0;
        const viewY = window.snake ? window.snake.yy || 0 : 0;
        const viewZoom = window.gsc || 1;
        const screenCenterX = window.innerWidth / 2;
        const screenCenterY = window.innerHeight / 2;

        ctx.save();
        ctx.lineJoin = 'round';
        ctx.lineCap = 'round';
        ctx.lineWidth = 8;
        ctx.shadowBlur = 12;
        ctx.shadowColor = state.features.snakeTrailColor;

        // Draw each segment separately with its own alpha
        for (let i = 1; i < state.snakeTrailPoints.length; i++) {
            const p1 = state.snakeTrailPoints[i-1];
            const p2 = state.snakeTrailPoints[i];

            // Calculate age of this segment (average of two points)
            const age = now - ((p1.time + p2.time) / 2);
            const alpha = Math.max(0, 1 - age / TRAIL_MAX_AGE); // 1 (new) → 0 (old)

            // Convert both points to screen coordinates
            const deltaX1 = p1.x - viewX;
            const deltaY1 = p1.y - viewY;
            const screenX1 = screenCenterX + deltaX1 * viewZoom;
            const screenY1 = screenCenterY + deltaY1 * viewZoom;

            const deltaX2 = p2.x - viewX;
            const deltaY2 = p2.y - viewY;
            const screenX2 = screenCenterX + deltaX2 * viewZoom;
            const screenY2 = screenCenterY + deltaY2 * viewZoom;

            // Set alpha for this segment
            ctx.strokeStyle = hexToRgba(state.features.snakeTrailColor, alpha * 0.7);

            // Draw this segment
            ctx.beginPath();
            ctx.moveTo(screenX1, screenY1);
            ctx.lineTo(screenX2, screenY2);
            ctx.stroke();
        }

        ctx.restore();
    }






    // === AUTO BOOST ===
    function autoBoost() {
        if (!state.features.autoBoost || !state.isInGame) {
            if (state.boosting) {
                state.boosting = false;
                if (typeof window.setAcceleration === 'function') window.setAcceleration(0);
                document.dispatchEvent(new KeyboardEvent('keyup', { key: ' ' }));
            }
            return;
        }
        if (!state.boosting) {
            state.boosting = true;
            if (typeof window.setAcceleration === 'function') window.setAcceleration(1);
            document.dispatchEvent(new KeyboardEvent('keydown', { key: ' ' }));
        }
    }
    function autoBoostLoop() {
        autoBoost();
        setTimeout(autoBoostLoop, 100);
    }
    autoBoostLoop();

    // === FPS COUNTER ===
    function fpsCounter() {
        state.fpsFrames++;
        const now = Date.now();
        if (now - state.fpsLastCheck >= 1000) {
            state.fps = state.fpsFrames;
            state.fpsFrames = 0;
            state.fpsLastCheck = now;
            if (state.features.fpsDisplay) {
                fpsDisplay.textContent = `FPS: ${state.fps}`;
            }
        }
        requestAnimationFrame(fpsCounter);
    }
    fpsCounter();

    // === DEATH SOUND ===
    function deathSoundObserver() {
        let lastAlive = true;
        setInterval(async () => {
            if (!state.features.deathSound) return;

            // Check death status using multiple methods
            const isDead =
                (window.snake && !window.snake.alive) ||
                (document.getElementById('died')?.style.display !== 'none');

            if (lastAlive && isDead) {
                try {
                    state.deathSound.currentTime = 0;
                    await state.deathSound.play();
                } catch (err) {
                    // Fallback: Create new audio instance
                    const fallbackAudio = new Audio(config.deathSoundURL);
                    fallbackAudio.play().catch(() => {
                        console.log('Audio playback blocked. Click the game first!');
                    });
                }
            }
            lastAlive = !isDead;
        }, 100);
    }


    state.deathSound.preload = 'auto';
    state.deathSound.load();
    state.deathSound.addEventListener('ended', () => {
        state.deathSound.currentTime = 0;
    });
    deathSoundObserver();

    // === PERFORMANCE MODES ===
    function applyPerformanceMode() {
        if (typeof window !== "undefined") {
            switch (state.features.performanceMode) {
                case 1:
                    window.want_quality = 0;
                    window.high_quality = false;
                    window.render_mode = 1;
                    break;
                case 2:
                    window.want_quality = 1;
                    window.high_quality = false;
                    window.render_mode = 2;
                    break;
                case 3:
                    window.want_quality = 2;
                    window.high_quality = true;
                    window.render_mode = 2;
                    break;
                default:
                    break;
            }
        }
        updateMenu();
    }
    applyPerformanceMode();

    // === ZOOM LOCK ===
    function zoomLockLoop() {
        if (typeof window.gsc !== 'undefined') {
            window.gsc = state.zoomFactor;
        }
        requestAnimationFrame(zoomLockLoop);
    }
    zoomLockLoop();

    // === PING DISPLAY ===
    function pingLoop() {
        let ping = 0;
        if (window.lagging && typeof window.lagging === "number") {
            ping = Math.round(window.lagging);
        } else if (window.lag && typeof window.lag === "number") {
            ping = Math.round(window.lag);
        }
        state.ping = ping;
        pingDisplay.textContent = `Ping: ${ping} ms`;
        const pingValue = document.getElementById("ping-value");
        if (pingValue) pingValue.textContent = `${ping} ms`;
        setTimeout(pingLoop, 500);
    }
    pingLoop();

    // === SCREENSHOT BUTTON (P) ===
    document.addEventListener('keydown', function (e) {
        if (e.key.toLowerCase() === 'p' && state.isInGame) {
            try {
                const canvas = document.querySelector('canvas');
                if (canvas) {
                    const dataURL = canvas.toDataURL();
                    const link = document.createElement('a');
                    link.href = dataURL;
                    link.download = `slither_screenshot_${Date.now()}.png`;
                    document.body.appendChild(link);
                    link.click();
                    document.body.removeChild(link);
                }
            } catch (err) {
                alert('Screenshot failed: ' + err);
            }
        }
    });

    function clearTrailOverlay() {
        const overlay = document.getElementById('snake-trail-overlay');
        if (overlay) {
            const ctx = overlay.getContext('2d');
            ctx.clearRect(0, 0, overlay.width, overlay.height);
        }
    }


    // === SERVER & LEADERBOARD UPDATES ===
    function updateServerAndLeaderboard() {
        if (window.bso && window.bso && window.bso.ip) {
            state.server = window.bso.ip;
        }
        if (window.lb && Array.isArray(window.lb)) {
            state.leaderboard = window.lb.map(x => x ? (x.nk || x.name || 'Unknown') : 'Unknown');
        }
        setTimeout(updateServerAndLeaderboard, 1000);
    }
    updateServerAndLeaderboard();

    // === INITIAL MENU VISIBILITY ===
    menu.style.display = state.menuVisible ? 'block' : 'none';

    // === INITIAL FPS DISPLAY ===
    fpsDisplay.style.display = state.features.fpsDisplay ? 'block' : 'none';

    // === INITIAL PING DISPLAY ===
    pingDisplay.style.display = 'block';

    // === INITIAL CIRCLE VISUAL COLOR ===
    circleVisual.style.border = `2px dashed ${hexToRgba(state.menuColor, 0.7)}`;

    function snakeTrailAnimationLoop() {
        requestAnimationFrame(snakeTrailAnimationLoop);
        drawSnakeTrail();
    }
    snakeTrailAnimationLoop();

    setInterval(() => {
        if (!state.features.snakeTrail) {
            state.snakeTrailPoints = [];
            return;
        }

        // Track actual mouse position (this always works!)
        const mouseX = window.xm !== undefined ? window.xm : window.mouseX || 0;
        const mouseY = window.ym !== undefined ? window.ym : window.mouseY || 0;

        // Only add points when the mouse moves
        if (state.snakeTrailPoints.length === 0 ||
            Math.abs(state.snakeTrailPoints[state.snakeTrailPoints.length-1].x - mouseX) > 5 ||
            Math.abs(state.snakeTrailPoints[state.snakeTrailPoints.length-1].y - mouseY) > 5) {

            state.snakeTrailPoints.push({
                x: mouseX || window.innerWidth/2,
                y: mouseY || window.innerHeight/2,
                time: Date.now()
            });


            // Limit trail length
            if (state.snakeTrailPoints.length > 100) state.snakeTrailPoints.shift();
        }
    }, 30);


})();