Bullet Force - Mod Menu

Bullet Force - Mod Menu with red crosshair dot

// ==UserScript==
// @name         Bullet Force - Mod Menu
// @version      1
// @description  Bullet Force - Mod Menu with red crosshair dot
// @match        https://games.crazygames.com/en_US/bullet-force-multiplayer/*
// @match        https://www.multiplayerpiano.dev/*
// @match        http://localhost:48897/game
// @match        https://www.gamepix.com/play/bullet-force
// @match        https://www.miniplay.com/game/bullet-force-multiplayer
// @match        https://kbhgames.com/game/bullet-force
// @match        https://bullet-force.com/
// @match        https://www.jopi.com/game/game/bullet-force/
// @match        https://www.gogy.com/games/bullet-force
// @match        https://www.gameflare.com/online-game/bullet-force/
// @match        https://www.silvergames.com/en/bullet-force
// @match        https://kour-io.com/bullet-force
// @grant        none
// @run-at       document-idle
// @namespace https://greatest.deepsurf.us/users/1527535
// ==/UserScript==

(function () {
    'use strict';

    // ===========================
    //  SETTINGS (edit here)
    // ===========================
    const DEFAULT_OFFSET = -23; // Initial default vertical offset for crosshair (negative = move up)
    let DOT_OFFSET = DEFAULT_OFFSET;
    // Default dot color and size (will be overridden by saved settings if present)
    const DEFAULT_COLOR = '#ff2b2b';
    // bright red default
    const DEFAULT_SIZE = 3;
    // px diameter

    // localStorage keys
    const LS_PREFIX = 'bfv2_';
    const LS_CROSSHAIR_ON = LS_PREFIX + 'Crosshair';
    const LS_DOT_COLOR = LS_PREFIX + 'DotColor';
    const LS_DOT_SIZE = LS_PREFIX + 'DotSize';
    const LS_DOT_OFFSET = LS_PREFIX + 'DotOffset';
    const LS_LAUNCHER_LEFT = LS_PREFIX + 'LauncherLeft';
    const LS_LAUNCHER_TOP = LS_PREFIX + 'LauncherTop';
    const LS_MODAL_LEFT = LS_PREFIX + 'ModalLeft'; // New key for modal position
    const LS_MODAL_TOP = LS_PREFIX + 'ModalTop';
    // New key for modal position


    // ===========================
    //  STYLE INJECTION
    // ===========================
    const style = document.createElement('style');
    style.textContent = `
@keyframes rainbow-flow{0%{background-position:0% 50%}50%{background-position:100% 50%}100%{background-position:0% 50%}}
@keyframes fadeIn { from { opacity: 0; transform: scale(0.95); } to { opacity: 1;
transform: scale(1); } }

#bf-enhancer-ui-v2{backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);}
#bf-enhancer-launcher span{background-image:linear-gradient(to right,#e81919,#f7ff00,#40ff00,#00fff2,#0015ff,#c300ff,#e81919);background-size:400% 100%;color:transparent;-webkit-background-clip:text;-webkit-text-fill-color:transparent;animation:rainbow-flow 6s ease infinite;pointer-events:none;}
.social-button{padding:6px 10px;border-radius:6px;font-weight:700;font-size:12px;border:none;cursor:pointer;color:white;transition:background 0.15s ease, transform 0.1s;flex-grow:1;text-align:center;}
/* color-dot small inline icon */
.color-dot {
    width: 14px;
    height: 14px;
    border-radius: 50%;
    display: inline-block;
    margin-right: 8px;
    vertical-align: middle;
    box-shadow: 0 0 4px rgba(0,0,0,0.4);
    border: 1px solid rgba(255,255,255,0.15);
}

/* ------------------------------------- */
/* !!! NEW SLEEK MODAL STYLES !!! */
/* ------------------------------------- */

/* Modal overlay: Serves only as a transparent blocker and container.
Removed centering. */
#bf-dot-modal {
    position: fixed;
    inset: 0;
    z-index: 99999997; /* MODIFIED: Lowered z-index so it doesn't overlay the main menu (99999998) */
    display: none;
    /* Removed centering properties */
    background: transparent;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
}
/* Modal content: Now ABSOLUTELY positioned and draggable */
#bf-dot-modal .modal-card {
    position: absolute;
    /* Changed to absolute for dragging */
    top: 50%;
    /* Initial center position */
    left: 50%;
    /* Initial center position */
    transform: translate(-50%, -50%);
    /* Center it initially */
    cursor: default;
    /* Ensure the whole card doesn't grab */
    width: 210px;
    max-width: 95%;
    background: #18191d;
    padding: 16px;
    border-radius: 12px;
    /* Subtle glow border */
    border: 1px solid rgba(255,255,255,0.08);
    box-shadow: 0 10px 30px rgba(0,0,0,0.9), 0 0 20px #2befff30; /* Dark shadow + cyan accent glow */
    color: #e5e7eb;
    /* Light gray text */
    font-family: 'Inter', Arial, sans-serif;
    font-size: 13px;
    animation: fadeIn 0.3s ease-out;
}

/* Modal Drag Handle */
#bf-dot-modal .modal-card .drag-handle-modal {
    cursor: move;
    /* Make the title a clear drag handle */
    user-select: none;
    padding-bottom: 8px;
    margin-bottom: 8px;
    border-bottom: 1px solid #37415130;
    text-align: center;
    font-weight: 700;
    font-size: 14px;
    color: #2befff;
}

/* Modal Close Button (X) Styling */
#bf-dot-modal .close-btn {
    position: absolute;
    top: 8px;
    right: 8px;
    background: none;
    border: none;
    color: #e5e7eb;
    font-size: 18px;
    font-weight: bold;
    cursor: pointer;
    line-height: 1;
    padding: 4px;
    border-radius: 4px;
    transition: color 0.15s, background 0.15s;
    opacity: 0.7;
}

#bf-dot-modal .close-btn:hover {
    opacity: 1;
    color: #2befff;
    background: #374151;
}

/* Utility for row structure */
#bf-dot-modal .setting-row {
    margin-bottom: 15px;
    padding: 0;
    border-bottom: 1px solid #37415130;
    padding-bottom: 12px;
}
#bf-dot-modal .setting-row:last-of-type {
     border-bottom: none;
     padding-bottom: 0;
}
#bf-dot-modal label {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    opacity: 0.8;
}
/* Range/Input Styling */
#bf-dot-modal .range-row { display:flex; align-items:center; gap:12px; margin-top:10px; }
#bf-dot-modal input[type="range"] {
    width:100%;
    height: 6px;
    -webkit-appearance: none;
    background: #374151; /* Dark track */
    border-radius: 4px;
    outline: none;
    transition: opacity .15s ease-in-out;
}
#bf-dot-modal input[type="range"]::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    width: 14px;
    height: 14px;
    border-radius: 50%;
    background: #2befff;
    /* Cyan thumb */
    cursor: pointer;
    box-shadow: 0 0 4px #2befff;
}
/* Color Swatches Grid */
#bf-dot-modal .color-grid {
    display: grid;
    grid-template-columns: repeat(5, 1fr);
    /* 5 columns of swatches */
    gap: 6px;
    margin-top: 8px;
    max-height: 120px;
    /* Restrict height */
    overflow-y: auto;
}
#bf-dot-modal .swatch-item {
    width: 100%;
    aspect-ratio: 1 / 1; /* Make it a square */
    border-radius: 4px;
    cursor: pointer;
    transition: transform 0.1s, box-shadow 0.1s;
    border: 1px solid transparent;
    box-shadow: 0 1px 4px rgba(0,0,0,0.5);
}
#bf-dot-modal .swatch-item:hover {
    transform: scale(1.1);
    box-shadow: 0 0 8px rgba(255,255,255,0.3);
}
#bf-dot-modal .swatch-item.selected {
    border: 2px solid #2befff; /* Cyan selection ring */
    transform: scale(1.05);
    box-shadow: 0 0 10px #2befff, inset 0 0 5px rgba(255,255,255,0.5);
}
/* Preview Dot (Inside modal) */
#bf-dot-modal .preview-dot {
    width: 60px;
    /* Slightly larger preview */
    height: 60px;
    border-radius: 50%;
    margin: 8px auto 16px auto;
    box-shadow: 0 0 25px rgba(255,255,255,0.1);
    border: 2px solid #ffffff30;
}
/* Action Buttons */
/* Updated styling for centered Reset button */
#bf-dot-modal .modal-actions {
    display:flex;
    justify-content:center; /* Center the Reset button */
    gap:8px;
    margin-top:15px;
    /* Increase gap from content */
}
#bf-dot-modal .btn {
    padding:8px 16px;
    /* Slightly larger padding for prominence */
    border-radius:6px;
    border:none;
    cursor:pointer;
    font-weight:700;
    font-size:14px;
    /* Slightly larger font */
    transition: transform 0.1s, background 0.1s;
}
#bf-dot-modal .btn:hover { transform: translateY(-1px);
}
#bf-dot-modal .btn.primary { background:#2befff; color:#18191d; } /* Cyan primary */
#bf-dot-modal .btn.ghost { background: #374151; color:#e5e7eb;
} /* Dark gray ghost */
`;
    document.head.appendChild(style);

    // ===========================
    //  UTILS
    // ===========================
    function readLS(key, fallback) {
        const v = localStorage.getItem(key);
        return v === null ? fallback : v;
    }
    function writeLS(key, value) {
        localStorage.setItem(key, value);
    }

    // restore settings
    const savedDotColor = readLS(LS_DOT_COLOR, DEFAULT_COLOR);
    const savedDotSize = parseInt(readLS(LS_DOT_SIZE, String(DEFAULT_SIZE)), 10) || DEFAULT_SIZE;
    const savedDotOffset = parseInt(readLS(LS_DOT_OFFSET, String(DOT_OFFSET)), 10);
    if (!isNaN(savedDotOffset)) DOT_OFFSET = savedDotOffset;
    let crosshairOn = readLS(LS_CROSSHAIR_ON, '0') === '1';

    // ===========================
    //  CROSSHAIR (dot) functions
    // ===========================
    const CROSSHAIR_ID = 'bf-simple-crosshair';
    function getDotElement() {
        return document.getElementById(CROSSHAIR_ID);
    }
    function applyCrosshairStyle(el, color, size) {
        if (!el) return;
        el.style.width = size + 'px';
        el.style.height = size + 'px';
        el.style.background = color;
        el.style.borderRadius = '50%';
        el.style.zIndex = '999999998';
        el.style.pointerEvents = 'none';
        el.style.position = 'fixed';
        // position center with DOT_OFFSET (uses current global DOT_OFFSET value)
        el.style.top = '50%';
        el.style.left = '50%';
        el.style.transform = `translate(-50%, calc(-50% + ${DOT_OFFSET}px))`;
    }
    function createCrosshairIfNeeded() {
        if (!getDotElement()) {
            const c = document.createElement('div');
            c.id = CROSSHAIR_ID;
            document.body.appendChild(c);
            applyCrosshairStyle(c, savedDotColor, savedDotSize);
            return c;
        }
        return getDotElement();
    }
    function toggleCrosshair(on) {
        crosshairOn = !!on;
        writeLS(LS_CROSSHAIR_ON, on ? '1' : '0');
        const el = getDotElement();
        if (on) {
            const created = createCrosshairIfNeeded();
            applyCrosshairStyle(created, savedDotColor, savedDotSize);
        } else {
            if (el) el.remove();
        }
    }
    // if saved ON, create afterwards
    if (crosshairOn) {
        setTimeout(() => createCrosshairIfNeeded(), 500);
    }

    // ===========================
    //  COLORS: bright palette requested
    // ===========================
    const COLORS = [
        { name: 'Red', hex: '#ff2b2b' },
        { name: 'Orange', hex: '#ff7a2d' },
        { name: 'Yellow', hex: '#ffd400' },
        { name: 'Lime', hex: '#b7ff2f' },
        { name: 'Green', hex: '#2bff66' },
        {
        name: 'Cyan', hex: '#2fefff' },
        { name: 'Blue', hex: '#2b8bff' },
        { name: 'Indigo', hex: '#4b3bff' },
        { name: 'Violet', hex: '#a13bff' },
        { name: 'Pink', hex: '#ff49a1' },
        { name: 'Magenta', hex: '#ff2bff' },
        { name: 'Maroon', hex: '#a12b2b' },
        { name: 'Brown', hex: '#a86a36' },

        { name: 'Black', hex: '#0d0d0d' },
        { name: 'White', hex: '#ffffff' },
        { name: 'Gray', hex: '#9aa0a6' },
        { name: 'Teal', hex: '#00b3a6' },
        { name: 'Turquoise', hex: '#3fe0c8' },
        { name: 'Purple', hex: '#8e2bff' },
        { name: 'Turquoise Bright', hex: '#2be3d0' }
    ];
    // ===========================
    //  BUILD UI: launcher + menu + modal
    // ===========================
    function ready(cb) {
        if (document.body) return cb();
        new MutationObserver((_, obs) => {
            if (document.body) { obs.disconnect(); cb(); }
        }).observe(document.documentElement, { childList: true });
    }

    ready(() => {
        // remove existing if present
        const existing = document.getElementById('bf-enhancer-ui-v2');
        if (existing) existing.remove();
        const existingLauncher = document.getElementById('bf-enhancer-launcher');
        if (existingLauncher) existingLauncher.remove();
        const existingModal = document.getElementById('bf-dot-modal');
        if (existingModal) existingModal.remove();

        // --- LAUNCHER ---

        const launcher = document.createElement('button');
        launcher.id = 'bf-enhancer-launcher';
        const launcherText = document.createElement('span');
        launcherText.textContent = '420 MENU';
        launcher.appendChild(launcherText);
        Object.assign(launcher.style, {
            position: 'fixed', zIndex: 99999999,
            padding: '8px 14px', borderRadius: '8px', fontWeight: '900', fontSize: '16px',
            background:
            '#1a2333', border: '2px solid #ffffff33', boxShadow: '0 6px 20px rgba(0,0,0,0.7)',
            cursor: 'grab', transition: 'background 0.2s, transform 0.1s', textShadow: '1px 1px 2px rgba(0,0,0,0.5)'
        });
        launcher.onmouseover = () => launcher.style.boxShadow = '0 6px 20px #7289da70';
        launcher.onmouseout = () => launcher.style.boxShadow = '0 6px 20px rgba(0,0,0,0.7)';
        launcher.onmousedown = () => launcher.style.cursor = 'grabbing';
        launcher.onmouseup = () => launcher.style.cursor = 'grab';
        document.body.appendChild(launcher);
        // --- MENU CONTAINER ---
        const container = document.createElement('div');
        container.id = 'bf-enhancer-ui-v2';
        Object.assign(container.style, {
            position: 'fixed', zIndex: 99999998,
            width: '240px', background: 'rgba(0,0,0,0.4)', color: '#f3f4f6', padding: '12px',
            borderRadius: '12px', fontFamily: 'Inter, Arial, sans-serif', fontSize: '12px',
            boxShadow: '0 10px 30px rgba(0,0,0,0.8),0 0 0 1px rgba(255,255,255,0.1)',
            border: 'none', display: 'none', resize: 'both', overflow: 'auto'

        });

        // --- DRAG, POSITIONING, AND TOGGLE LOGIC (Unified) ---
        const defaultLeft = 20;
        // Default X offset
        const defaultTop = 20;
        // Default Y offset
        let launcherLeft = parseFloat(readLS(LS_LAUNCHER_LEFT, defaultLeft));
        let launcherTop = parseFloat(readLS(LS_LAUNCHER_TOP, defaultTop));

        // Apply saved position to launcher
        Object.assign(launcher.style, {
            left: launcherLeft + 'px',
            top: launcherTop + 'px',
        });
        // Function to position the menu relative to the launcher
        const updateContainerPosition = () => {
            // Check if launcher is visible (its offsetTop/Left will be accurate)
            const lTop = launcher.offsetTop;
            const lLeft = launcher.offsetLeft;
            container.style.left = lLeft + 'px';
            container.style.top = (lTop + launcher.offsetHeight + 10) + 'px';
            // 10px gap
        };
        // DRAG/CLICK LOGIC
        let isDragging = false;
        let hasMoved = false;
        // New flag to detect movement
        const DRAG_THRESHOLD = 5;
        // Pixels required to count as a drag

        let startX, startY, initialX, initialY;
        launcher.addEventListener('mousedown', (e) => {
            // Only start drag if not the launcher text span (which has pointer-events: none)
            if (e.button !== 0) return; // Only primary button
            isDragging = true;
            hasMoved = false; // Reset on mousedown
            startX = e.clientX;

            startY = e.clientY;
            initialX = launcher.offsetLeft;
            initialY = launcher.offsetTop;
            launcher.style.cursor = 'grabbing';
            e.preventDefault(); // Prevent text selection
        });
        document.addEventListener('mousemove', (e) => {
            if (!isDragging) return;

            const dx = e.clientX - startX;
            const dy = e.clientY - startY;

            // Check if movement exceeds threshold
            if (Math.abs(dx) > DRAG_THRESHOLD || Math.abs(dy) > DRAG_THRESHOLD) {

            hasMoved = true;
            }

            let newLeft = initialX + dx;
            let newTop = initialY + dy;

            // Boundary checks (keep launcher fully visible)
            newLeft = Math.max(0, Math.min(newLeft, window.innerWidth - launcher.offsetWidth));

            newTop = Math.max(0, Math.min(newTop, window.innerHeight - launcher.offsetHeight));

            launcher.style.left = newLeft + 'px';
            launcher.style.top = newTop + 'px';

            // Update container position only if it's open
            if (container.style.display === 'block') {
                updateContainerPosition();

            }
        });
        document.addEventListener('mouseup', () => {
            if (isDragging) {
                isDragging = false;
                launcher.style.cursor = 'grab';

                // Save new position regardless of whether it moved
                writeLS(LS_LAUNCHER_LEFT, launcher.offsetLeft);

                writeLS(LS_LAUNCHER_TOP, launcher.offsetTop);

                // TOGGLE LOGIC: Only toggle if NO significant movement occurred (pure click)
                if (!hasMoved) {
                     if (container.style.display === 'none') {

                         updateContainerPosition(); // Calculate position before opening
                        container.style.display = 'block';
                    } else {
                        container.style.display = 'none';

                    }
                }
                // Important: Reset hasMoved flag, though it will be reset on next mousedown too
                hasMoved = false;
            }
        });
        // --- END DRAG/POSITIONING/TOGGLE LOGIC ---


        // Title (Changed to smaller text)
        const title = document.createElement('div');
        title.className = 'drag-handle';
        title.textContent = "Bullet Force - Mod Menu";
        Object.assign(title.style, {
            fontSize: '16px', // Smaller font size
            fontWeight: '900', marginBottom: '12px', borderBottom: '1px solid #374151', paddingBottom: '6px', cursor: 'move',
            backgroundImage: 'linear-gradient(to right,#e81919,#f7ff00,#40ff00,#00fff2,#0015ff,#c300ff,#e81919)',
            backgroundSize: '400% 100%', color: 'transparent', WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent', animation: 'rainbow-flow 6s ease infinite'
        });
        container.appendChild(title);

        // --- CROSSHAIR ROW with color icon + label + ON/OFF button (but clicking label toggles) ---
        const crosshairRow = document.createElement('div');
        Object.assign(crosshairRow.style, {
            display: 'flex', alignItems: 'center', justifyContent: 'space-between',
            padding: '6px 0', borderBottom: '1px dotted #374151', margin: '0', cursor: 'pointer'
        });
        // Left: color icon (button)
        const colorIconBtn = document.createElement('button');
        colorIconBtn.type = 'button';
        colorIconBtn.title = 'Change crosshair color, size & offset';
        colorIconBtn.style.border = 'none';
        colorIconBtn.style.background = 'transparent';
        colorIconBtn.style.padding = '0';
        colorIconBtn.style.marginRight = '8px';
        colorIconBtn.style.cursor = 'pointer';
        // color dot element inside
        const colorIconDot = document.createElement('span');
        colorIconDot.className = 'color-dot';
        colorIconDot.style.background = savedDotColor;
        colorIconBtn.appendChild(colorIconDot);

        // Label area
        const label = document.createElement('div');
        label.textContent = 'Simple Crosshair Dot';
        label.style.flex = '1';
        label.style.fontWeight = '700';
        label.style.color = '#fff';
        label.style.userSelect = 'none';
        label.style.display = 'flex';
        label.style.alignItems = 'center';
        label.style.gap = '8px';

        // Place color icon (inline with label) to the left of the label text visually
        const labelWrapper = document.createElement('div');
        labelWrapper.style.display = 'flex';
        labelWrapper.style.alignItems = 'center';
        labelWrapper.appendChild(colorIconBtn);
        labelWrapper.appendChild(label);

        // ON/OFF button (kept for clarity but clicking label toggles)
        const btn = document.createElement('button');
        Object.assign(btn.style, {
            padding: '6px 10px',
            borderRadius: '6px',
            border: 'none',
            cursor: 'pointer', color: 'white', fontWeight: '700',
            minWidth: '60px', transition: 'all 0.15s ease'
        });
        function updateToggleButton(isOn) {
            btn.textContent = isOn ?
            'ON' : 'OFF';
            const bg = isOn ? '#22c55e' : '#ef4444';
            btn.style.background = bg;
            btn.style.boxShadow = isOn ?
            `0 0 8px ${bg}50` : 'none';
        }
        updateToggleButton(crosshairOn);
        // Click behavior: clicking labelWrapper toggles crosshair ON/OFF (main interface)
        labelWrapper.addEventListener('click', (e) => {
            e.stopPropagation();
            crosshairOn = !crosshairOn;
            updateToggleButton(crosshairOn);
            toggleCrosshair(crosshairOn);
        });
        // clicking the small ON/OFF does same (kept for user preference)
        btn.addEventListener('click', (e) => {
            e.stopPropagation();
            crosshairOn = !crosshairOn;
            updateToggleButton(crosshairOn);
            toggleCrosshair(crosshairOn);
        });
        crosshairRow.appendChild(labelWrapper);
        crosshairRow.appendChild(btn);
        container.appendChild(crosshairRow);

        // --- Social Buttons (YouTube + Discord) - restored per request ---
        const socialSection = document.createElement('div');
        Object.assign(socialSection.style, {
            display: 'flex',
            gap: '8px',
            padding: '10px 0 6px 0',
            borderBottom: '1px dotted #374151',
            marginBottom: '10px'
        });
        // YouTube
        const youtubeBtn = document.createElement('button');
        youtubeBtn.textContent = 'YouTube 🚀';
        youtubeBtn.className = 'social-button';
        Object.assign(youtubeBtn.style, { background: '#ff0000' });
        youtubeBtn.onclick = () => window.open('https://www.youtube.com/@ClearBlueSky420', '_blank');
        youtubeBtn.onmouseover = () => youtubeBtn.style.transform = 'scale(1.03)';
        youtubeBtn.onmouseout = () => youtubeBtn.style.transform = 'scale(1)';
        socialSection.appendChild(youtubeBtn);

        // Discord (copy)
        const discordBtn = document.createElement('button');
        discordBtn.textContent = 'Copy Discord 🫂';
        discordBtn.className = 'social-button';
        const discordName = 'japan1z';
        Object.assign(discordBtn.style, { background: '#5865f2' });
        discordBtn.onclick = () => {
            // Using document.execCommand('copy') as navigator.clipboard might fail in an iframe
            const tempInput = document.createElement('textarea');
            tempInput.value = discordName;
            document.body.appendChild(tempInput);
            tempInput.select();
            document.execCommand('copy');
            document.body.removeChild(tempInput);

            const originalText = discordBtn.textContent;
            discordBtn.textContent = 'COPIED!';
            discordBtn.style.background = '#22c55e';
            setTimeout(() => {
                discordBtn.textContent = originalText;
                discordBtn.style.background = '#5865f2';
            }, 1000);
        };
        discordBtn.onmouseover = () => discordBtn.style.transform = 'scale(1.03)';
        discordBtn.onmouseout = () => discordBtn.style.transform = 'scale(1)';
        socialSection.appendChild(discordBtn);

        container.appendChild(socialSection);
        // --- Footer with Hide & Close ---
        const footer = document.createElement('div');
        Object.assign(footer.style, {
            paddingTop: '0', display: 'flex', justifyContent: 'space-between', gap: '8px', flexDirection: 'column'
        });
        const message = document.createElement('p');
        message.textContent = 'ClearBlueSky420';
        Object.assign(message.style, {
            fontSize: '11px', color: '#FFD700', fontWeight: '600', textAlign: 'center', marginBottom: '8px', paddingBottom: '8px', borderBottom: '1px dotted #374151'
        });
        footer.appendChild(message);

        const info = document.createElement('small');
        info.textContent = 'Working as of 3:22 PM 10/16/2025';
        info.style.opacity = '0.7';

        const buttonGroup = document.createElement('div');
        Object.assign(buttonGroup.style, { display: 'flex', gap: '6px', justifyContent: 'flex-end' });

        // Hide (only hide the menu)
        const hideButton = document.createElement('button');
        hideButton.textContent = 'Hide';
        Object.assign(hideButton.style, {
            padding: '6px 12px', borderRadius: '6px', border: 'none', cursor: 'pointer', background: '#374151', color: 'white', fontWeight: '700', flexShrink: '0', fontSize: '12px'
        });
        hideButton.addEventListener('click', () => {
            container.style.display = 'none';
        });
        buttonGroup.appendChild(hideButton);

        // Close (hide both)
        const close = document.createElement('button');
        close.textContent = 'Close Menu';
        Object.assign(close.style, {
            padding: '6px 12px', borderRadius: '6px', border: 'none', cursor: 'pointer',
            background: '#ef4444', color: 'white', fontWeight: '700', flexShrink: '0', fontSize: '12px'
        });
        close.addEventListener('click', () => {
            container.style.display = 'none';
            launcher.style.display = 'none';
            // also hide modal if open
            const modal = document.getElementById('bf-dot-modal');
            // IMPORTANT: If modal is open, we need to save the current preview values before closing, as per new auto-apply logic.

            if (modal && modal.style.display === 'flex') {
                updatePreview(true); // Save current preview state to LS and global vars
            }
            if (modal) modal.style.display = 'none';
        });
        buttonGroup.appendChild(close);

        footer.appendChild(info);
        footer.appendChild(buttonGroup);
        container.appendChild(footer);

        // append to document
        document.body.appendChild(container);
        // ==========================
        //  DOT COLOR & SIZE MODAL (Auto-Apply)
        // ==========================
        const modalOverlay = document.createElement('div');
        modalOverlay.id = 'bf-dot-modal';
        // !!! UPDATED HTML STRUCTURE: Added ID to modal-card and added drag-handle-modal !!!
        modalOverlay.innerHTML = `
<div class="modal-card" role="dialog" aria-modal="true" id="bf-dot-modal-card">
  <button class="close-btn" id="bf-modal-close-x" aria-label="Close settings modal">X</button>
  <div class="drag-handle-modal" id="bf-modal-drag-handle">DOT SETTINGS</div>

  <div style="text-align:center;">
    <div class="preview-dot" id="bf-preview-dot"></div>
  </div>

  <div class="setting-row">
    <label for="bf-dot-size-range">DOT SIZE</label>
    <div class="range-row" style="margin-top:6px;">
      <input id="bf-dot-size-range" type="range" min="4" max="48" value="${savedDotSize}">
      <div id="bf-dot-size-value" style="width:30px; text-align:right; font-weight:700; color:#2befff;">${savedDotSize}</div>
    </div>
  </div>

  <div class="setting-row">
    <label for="bf-dot-offset-range">VERTICAL OFFSET</label>

    <div class="range-row" style="margin-top:6px;">
      <input id="bf-dot-offset-range" type="range" min="-100" max="100" value="${savedDotOffset}">
      <div id="bf-dot-offset-value" style="width:30px;
text-align:right; font-weight:700; color:#2befff;">${savedDotOffset}</div>
    </div>
  </div>

  <div style="margin-bottom: 5px;">
    <label style="display:block;">COLOR SWATCHES</label>
    <div class="color-grid" id="bf-color-list"></div>
  </div>

  <div class="modal-actions">
    <button class="btn ghost" id="bf-dot-reset" style="flex-grow:0;
min-width: 100px;">RESET</button>
  </div>
</div>
`;
        document.body.appendChild(modalOverlay);

        // populate color list (LOGIC CHANGED FOR SWATCH GRID)
        const colorListEl = modalOverlay.querySelector('#bf-color-list');
        COLORS.forEach(c => {
            const item = document.createElement('div');
            item.className = 'swatch-item'; // New class name for swatch
            item.dataset.hex = c.hex;

            item.title = c.name; // Tooltip on hover
            item.style.background = c.hex;
            item.style.borderColor = c.hex + '60'; // Subtle border based on color

            item.addEventListener('click', () => {
                // set preview color

                previewHex = c.hex;
                // highlight selection visually
                // remove existing highlight and add new one
                modalOverlay.querySelectorAll('.swatch-item').forEach(ci => ci.classList.remove('selected'));
                item.classList.add('selected');
                updatePreview(true); // AUTO-APPLY: Apply and save immediately
            });

            colorListEl.appendChild(item);
        });

        // modal controls and state
        let previewHex = savedDotColor;
        let previewSize = savedDotSize;
        let previewOffset = DOT_OFFSET; // Use the globally loaded DOT_OFFSET value

        const previewDot = modalOverlay.querySelector('#bf-preview-dot');
        const sizeRange = modalOverlay.querySelector('#bf-dot-size-range');
        const sizeValueLabel = modalOverlay.querySelector('#bf-dot-size-value');

        // New elements
        const offsetRange = modalOverlay.querySelector('#bf-dot-offset-range');
        const offsetValueLabel = modalOverlay.querySelector('#bf-dot-offset-value');
        const btnReset = modalOverlay.querySelector('#bf-dot-reset');
        const btnCloseX = modalOverlay.querySelector('#bf-modal-close-x');
        // Get the new X button
        const modalCard = modalOverlay.querySelector('#bf-dot-modal-card');
        // Get the modal card
        const modalDragHandle = modalOverlay.querySelector('#bf-modal-drag-handle');
        // Get the new drag handle


        function updatePreview(applyToCrosshair = false) {
            previewDot.style.background = previewHex;
            previewDot.style.width = previewSize + 'px';
            previewDot.style.height = previewSize + 'px';
            // Apply offset to preview dot (only visually inside modal)
            previewDot.style.transform = `translateY(${previewOffset}px)`;
            // Dynamic shadow for preview (makes it pop and reflect color)
            previewDot.style.boxShadow = `0 0 25px rgba(255,255,255,0.1), 0 0 10px ${previewHex}a0`;
            // update small icon in menu
            colorIconDot.style.background = previewHex;
            if (applyToCrosshair) {
                // Save new settings to global state and localStorage
                savedDotColor = previewHex;
                savedDotSize = previewSize;
                DOT_OFFSET = previewOffset;

                writeLS(LS_DOT_COLOR, savedDotColor);
                writeLS(LS_DOT_SIZE, String(savedDotSize));
                writeLS(LS_DOT_OFFSET, String(DOT_OFFSET));
                // Save new offset

                // Apply to the actual crosshair element
                const dotEl = getDotElement();
                if (dotEl) applyCrosshairStyle(dotEl, savedDotColor, savedDotSize);
            }
        }

        // initialize preview
        updatePreview();
        // size range handler: AUTO-APPLY on input
        sizeRange.addEventListener('input', (e) => {
            previewSize = parseInt(e.target.value, 10);
            sizeValueLabel.textContent = previewSize;
            updatePreview(true); // AUTO-APPLY
        });
        // offset range handler: AUTO-APPLY on input
        offsetRange.addEventListener('input', (e) => {
            previewOffset = parseInt(e.target.value, 10);
            offsetValueLabel.textContent = previewOffset;
            updatePreview(true); // AUTO-APPLY
        });
        // open modal handler
        function openModal() {
            // set preview values to current saved
            previewHex = savedDotColor;
            previewSize = savedDotSize;
            previewOffset = DOT_OFFSET; // Use currently active global offset

            sizeRange.value = previewSize;
            sizeValueLabel.textContent = previewSize;

            offsetRange.value = previewOffset;
            offsetValueLabel.textContent = previewOffset;

            // Load saved position or use the CSS default (centered by transform)
            let modalLeft = parseFloat(readLS(LS_MODAL_LEFT, '50'));
            let modalTop = parseFloat(readLS(LS_MODAL_TOP, '50'));

            if (modalLeft !== 50 || modalTop !== 50) {
                // Only apply if it has been saved before (to override CSS initial centering)
                modalCard.style.left = modalLeft + 'px';
                modalCard.style.top = modalTop + 'px';
                modalCard.style.transform = 'none'; // Disable initial centering transform
            } else {
                 // Use initial centering style if no saved position exists
                modalCard.style.left = '50%';
                modalCard.style.top = '50%';
                modalCard.style.transform = 'translate(-50%, -50%)';
            }


            // clear highlight then highlight current color if present (Updated for swatch grid)
            modalOverlay.querySelectorAll('.swatch-item').forEach(ci => {
                ci.classList.remove('selected');
                if (ci.dataset.hex.toLowerCase() === previewHex.toLowerCase()) ci.classList.add('selected');
            });
            updatePreview();
            modalOverlay.style.display = 'flex';
        }

        // closeModal now ALWAYS saves because changes were auto-applied during interaction
        function closeModal(saveChanges = true) {
            if (saveChanges) {
                 updatePreview(true);
            // If called via outer click/close, ensure final state is saved
            }
            modalOverlay.style.display = 'none';
        }

        // reset to default handler: AUTO-APPLY on click
        btnReset.addEventListener('click', () => {
            previewHex = DEFAULT_COLOR;
            previewSize = DEFAULT_SIZE;
            previewOffset = DEFAULT_OFFSET;

            sizeRange.value = previewSize;
            sizeValueLabel.textContent = previewSize;

            offsetRange.value = previewOffset;
            offsetValueLabel.textContent = previewOffset;

            // highlight default color
            modalOverlay.querySelectorAll('.swatch-item').forEach(ci => {
                ci.classList.remove('selected');
                if (ci.dataset.hex.toLowerCase() === DEFAULT_COLOR.toLowerCase()) ci.classList.add('selected');

            });

            updatePreview(true); // AUTO-APPLY: Apply and save immediately
        });
        // colorIconBtn opens modal
        colorIconBtn.addEventListener('click', (e) => {
            e.stopPropagation();
            openModal();
        });
        // Close modal using the 'X' button
        btnCloseX.addEventListener('click', () => {
            closeModal(true);
        });
        // close modal by clicking overlay (Now saves and closes)
        modalOverlay.addEventListener('click', (e) => {
            // Clicks on the transparent background will close the modal.
            if (e.target === modalOverlay) {
                closeModal(true); // Save current preview state to LS and close
            }

        });

        // ===========================
        //  MODAL DRAG LOGIC
        // ===========================
        let isModalDragging = false;
        let startXModal, startYModal, initialXModal, initialYModal;
        let hasMovedModal = false;

        modalDragHandle.addEventListener('mousedown', (e) => {
            if (e.button !== 0) return;
            isModalDragging = true;
            hasMovedModal = false;
            startXModal = e.clientX;
            startYModal = e.clientY;
            // Get position relative
            initialXModal = modalCard.offsetLeft;
            initialYModal = modalCard.offsetTop;

            // If the modal was in its initial centered state, we remove the transform before dragging
            modalCard.style.transform = 'none';
            e.preventDefault();
        });
        document.addEventListener('mousemove', (e) => {
            if (!isModalDragging) return;

            const dx = e.clientX - startXModal;
            const dy = e.clientY - startYModal;

            if (Math.abs(dx) > DRAG_THRESHOLD || Math.abs(dy) > DRAG_THRESHOLD) {
                hasMovedModal = true;

            }

            let newLeft = initialXModal + dx;
            let newTop = initialYModal + dy;

            // Basic boundary checks
            newLeft = Math.max(0, Math.min(newLeft, window.innerWidth - modalCard.offsetWidth));
            newTop = Math.max(0, Math.min(newTop, window.innerHeight - modalCard.offsetHeight));

            modalCard.style.left =
            newLeft + 'px';
            modalCard.style.top = newTop + 'px';
        });
        document.addEventListener('mouseup', () => {
            if (isModalDragging) {
                isModalDragging = false;

                // Save new position
                writeLS(LS_MODAL_LEFT, modalCard.offsetLeft);
                writeLS(LS_MODAL_TOP, modalCard.offsetTop);


            // Reset flag
                hasMovedModal = false;
            }
        });
        // ===========================
        //  END MODAL DRAG LOGIC
        // ===========================


        // ensure crosshair element uses saved settings if present
        if (crosshairOn) {
            const el = createCrosshairIfNeeded();
            applyCrosshairStyle(el, savedDotColor, savedDotSize);
        }

    }); // ready()

})();