Greasy Fork

Greasy Fork is available in English.

StellaGeo

StellaGeo Geoguessr Cheat

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         StellaGeo
// @namespace    http://tampermonkey.net/
// @version      3.3.0
// @description  StellaGeo Geoguessr Cheat
// @author       Cope (@713cope on Discord)
// @match        https://www.geoguessr.com/*
// @grant        GM_xmlhttpRequest
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_addStyle
// @grant        GM_addElement
// @grant        GM.getValue
// @grant        GM.setValue
// @grant        GM.addStyle
// @grant        GM.addElement
// @connect      flagcdn.com
// @connect      static-maps.yandex.ru
// @connect      fonts.googleapis.com
// @connect      i.imgur.com
// @connect      minimalistmoon.com
// @connect      us1.locationiq.com
// @connect      locationiq.com
// @connect      discord.com
// @run-at       document-start
// ==/UserScript==

(function () {
    'use strict';


    const nativeOpen = window.open;

    const CONFIG = {
        VERSION: '4.0.0',
        API_KEYS: [
            'pk.010bb988be9b2a316e7093ae8e316e6d',
            'pk.6ce0e2bf3b2b84e353d2420b38de8ed2',
            'pk.78f4624afaebfd926a65e31358a4507d'
        ]
    };

    class Utils {
        static logs = [];
        static logListeners = [];

        static generateId() {
            return 'user_' + Math.random().toString(36).substring(2, 15);
        }

        static log(message, type = 'info') {
            const timestamp = new Date().toLocaleTimeString();
            const logEntry = { timestamp, message, type };
            this.logs.unshift(logEntry);
            if (this.logs.length > 100) this.logs.pop();

            this.logListeners.forEach(listener => listener(logEntry));
        }

        static addLogListener(listener) {
            this.logListeners.push(listener);
        }

        static async sleep(ms) {
            return new Promise(resolve => setTimeout(resolve, ms));
        }
    }

    class Settings {
        constructor() {
            this.defaults = {
                menuHotkey: 'Insert',
                apiKeyIndex: 0,
                leaderboardId: Utils.generateId(),
                leaderboardName: 'Player',
                participateInLeaderboard: true,
                isToastEnabled: true,
                firstRun: true,
                firstWebhookRun: true,
                sidebarWidth: 240,
                apiKeyStatus: {},
                elementPositions: {},
                features: {
                    openGM: false,
                    openPlonkIT: false,
                    locationDisplay: false,
                    tts: false,
                    discordWebhook: false,
                    watermark: true,
                    mapTimer: true,
                    hotkeyDisplay: false,
                    toastNotifications: true,
                    autoPin: false,
                },
                featureSettings: {
                    locationDisplay: {
                        showCountry: true,
                        showState: true,
                        showCity: true,
                        stateZoom: 5,
                        cityZoom: 10
                    },
                    tts: {
                        volume: 1.0
                    },
                    discordWebhook: {
                        url: ''
                    },
                    watermark: {
                        position: 'top-center',
                        showName: true,
                        showUsername: true,
                        showClock: true,
                        timeFormat: '12h'
                    },
                    hotkeyDisplay: {
                        mode: 'active',
                        showToggle: true,
                        showTrigger: true
                    },
                    toastNotifications: {
                        position: 'bottom-right'
                    }
                },
                hotkeys: {
                    openGM: { trigger: 'q' },
                    openPlonkIT: { trigger: 'q' },
                    locationDisplay: { trigger: 'q' },
                    discordWebhook: { trigger: 'q' },
                    tts: { trigger: 'e' },
                    hotkeyDisplay: { toggle: 'J', trigger: null }
                },
                currentTheme: 'default',
                customThemes: {},
                defaultThemes: {
                    default: {
                        name: 'Stella Purple',
                        colors: {
                            carbonBlack: { color: '#1a1a1a', alpha: 1, effect: 'none' },
                            carbonBlack2: { color: '#1d1d1d', alpha: 1, effect: 'none' },
                            carbonBlack3: { color: '#242424', alpha: 1, effect: 'none' },
                            accent: { color: '#8b5cf6', alpha: 1, effect: 'none' },
                            accentHover: { color: '#7c3aed', alpha: 1, effect: 'none' },
                            text: { color: '#ffffff', alpha: 1, effect: 'none' },
                            textDim: { color: '#a1a1aa', alpha: 1, effect: 'none' },
                            border: { color: '#333333', alpha: 1, effect: 'none' }
                        }
                    },
                    blue: {
                        name: 'Ocean Blue',
                        colors: {
                            carbonBlack: { color: '#0f172a', alpha: 1, effect: 'none' },
                            carbonBlack2: { color: '#1e293b', alpha: 1, effect: 'none' },
                            carbonBlack3: { color: '#334155', alpha: 1, effect: 'none' },
                            accent: { color: '#3b82f6', alpha: 1, effect: 'none' },
                            accentHover: { color: '#2563eb', alpha: 1, effect: 'none' },
                            text: { color: '#ffffff', alpha: 1, effect: 'none' },
                            textDim: { color: '#94a3b8', alpha: 1, effect: 'none' },
                            border: { color: '#475569', alpha: 1, effect: 'none' }
                        }
                    },
                    green: {
                        name: 'Forest Green',
                        colors: {
                            carbonBlack: { color: '#14532d', alpha: 1, effect: 'none' },
                            carbonBlack2: { color: '#166534', alpha: 1, effect: 'none' },
                            carbonBlack3: { color: '#15803d', alpha: 1, effect: 'none' },
                            accent: { color: '#22c55e', alpha: 1, effect: 'none' },
                            accentHover: { color: '#16a34a', alpha: 1, effect: 'none' },
                            text: { color: '#ffffff', alpha: 1, effect: 'none' },
                            textDim: { color: '#86efac', alpha: 1, effect: 'none' },
                            border: { color: '#4ade80', alpha: 1, effect: 'none' }
                        }
                    }
                }
            };
            this.data = this.load();
        }

        load() {
            const saved = GM_getValue('stella_settings', {});
            const merged = { ...this.defaults, ...saved };
            merged.features = { ...this.defaults.features, ...(saved.features || {}) };
            merged.hotkeys = { ...this.defaults.hotkeys, ...(saved.hotkeys || {}) };

            merged.featureSettings = { ...this.defaults.featureSettings };
            if (saved.featureSettings) {
                for (const key in saved.featureSettings) {
                    merged.featureSettings[key] = { ...this.defaults.featureSettings[key], ...saved.featureSettings[key] };
                }
            }

            return merged;
        }

        save() {
            GM_setValue('stella_settings', this.data);
        }

        get(key) {
            return this.data[key];
        }

        set(key, value) {
            this.data[key] = value;
            this.save();
        }

        getFeatureSetting(feature, setting) {
            return this.data.featureSettings[feature]?.[setting];
        }

        setFeatureSetting(feature, setting, value) {
            if (!this.data.featureSettings[feature]) {
                this.data.featureSettings[feature] = {};
            }
            this.data.featureSettings[feature][setting] = value;
            this.save();
        }
    }

    class UI {
        constructor(app) {
            this.app = app;
            this.menuOpen = false;
            this.locationDisplayWindow = null;
            this.locationDisplayData = null;
            this.locationDisplayPopped = false;
            this.loadIcons();
            this.injectStyles();
            this.createOverlay();
            this.createLocationDisplay();
            this.createHUD();
            this.checkFirstRun();


            setTimeout(() => {
                this.applyTheme(this.app.settings.get('currentTheme'));
            }, 100);
        }

        loadIcons() {
            const link = document.createElement('link');
            link.href = 'https://fonts.googleapis.com/icon?family=Material+Icons';
            link.rel = 'stylesheet';
            document.head.appendChild(link);
        }

        injectStyles() {
            const css = `
                @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap');

                :root {
                    --carbon-black: #1a1a1aff;
                    --carbon-black-2: #1d1d1dff;
                    --carbon-black-3: #242424ff;

                    /* Lighter Amethyst / Violet */
                    --stella-accent: #8b5cf6;
                    --stella-accent-hover: #7c3aed;

                    --stella-bg: var(--carbon-black);
                    --stella-sidebar: var(--carbon-black-2);
                    --stella-item-bg: var(--carbon-black-3);
                    --stella-text: #ffffff;
                    --stella-text-dim: #a1a1aa;
                    --stella-border: #333333;
                }

                body {
                    user-select: none;
                }

                #stella-menu {
                    position: fixed;
                    top: 50%;
                    left: 50%;
                    transform: translate(-50%, -50%) scale(0.9);
                    width: 850px;
                    height: 600px;
                    background: var(--stella-bg);
                    border-radius: 12px;
                    box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
                    display: none;
                    opacity: 0;
                    z-index: 99999;
                    overflow: hidden;
                    font-family: 'Inter', sans-serif;
                    color: var(--stella-text);
                    border: 1px solid var(--stella-border);
                    transition: opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1),
                                transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
                    flex-direction: column;
                }

                #stella-menu.open {
                    display: flex;
                    animation: menuFadeIn 0.3s cubic-bezier(0.4, 0, 0.2, 1) forwards;
                }

                #stella-menu.closing {
                    animation: menuFadeOut 0.25s cubic-bezier(0.4, 0, 0.2, 1) forwards;
                }

                @keyframes menuFadeIn {
                    from {
                        opacity: 0;
                        transform: translate(-50%, -50%) scale(0.9);
                    }
                    to {
                        opacity: 1;
                        transform: translate(-50%, -50%) scale(1);
                    }
                }

                @keyframes menuFadeOut {
                    from {
                        opacity: 1;
                        transform: translate(-50%, -50%) scale(1);
                    }
                    to {
                        opacity: 0;
                        transform: translate(-50%, -50%) scale(0.95);
                    }
                }

                /* Header */
                .stella-header {
                    height: 70px;
                    border-bottom: 1px solid var(--stella-border);
                    display: flex;
                    align-items: center;
                    justify-content: space-between;
                    padding: 0 24px;
                    background: var(--stella-bg);
                    flex-shrink: 0;
                }

                .stella-logo {
                    display: flex;
                    align-items: center;
                    gap: 12px;
                }

                .stella-logo img {
                    width: 32px;
                    height: 32px;
                    border-radius: 8px; /* Rounded Corners */
                }

                .stella-logo-text {
                    font-size: 24px;
                    font-weight: 800;
                    color: white;
                    letter-spacing: -0.5px;
                    animation: stellaPulse 3s infinite alternate;
                }

                @keyframes stellaPulse {
                    0% { text-shadow: 0 0 10px rgba(139, 92, 246, 0.2); }
                    100% { text-shadow: 0 0 20px rgba(139, 92, 246, 0.6); }
                }

                .stella-header-actions {
                    display: flex;
                    gap: 8px;
                }

                .stella-header-btn {
                    width: 40px;
                    height: 40px;
                    display: flex;
                    align-items: center;
                    justify-content: center;
                    border-radius: 8px;
                    cursor: pointer;
                    color: var(--stella-text-dim);
                    transition: all 0.2s ease;
                }

                .stella-header-btn:hover {
                    background: rgba(255,255,255,0.05);
                    color: white;
                }

                .stella-header-btn.active {
                    background: rgba(139, 92, 246, 0.1);
                    color: var(--stella-accent);
                }

                /* Body Layout */
                .stella-body {
                    display: flex;
                    flex: 1;
                    overflow: hidden;
                }

                .stella-sidebar {
                    background: var(--stella-sidebar);
                    padding: 20px 10px;
                    display: flex;
                    flex-direction: column;
                    border-right: 1px solid var(--stella-border);
                    position: relative;
                    flex-shrink: 0;
                    width: 50px;
                    transition: width 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
                }

                .stella-sidebar:hover {
                    width: 240px;
                }

                .stella-sidebar:hover.collapsed {
                    width: 240px;
                }

                .stella-nav {
                    position: relative;
                    display: flex;
                    flex-direction: column;
                }

                .stella-nav-highlight {
                    position: absolute;
                    left: 0;
                    width: 100%;
                    background: var(--stella-item-bg);
                    border-radius: 8px;
                    transition: top 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
                    z-index: 1;
                    border: 1px solid var(--stella-border);
                }

                .stella-nav-highlight::before {
                    content: '';
                    position: absolute;
                    left: 0;
                    top: 0;
                    bottom: 0;
                    width: 3px;
                    background: var(--stella-accent);
                    border-radius: 8px 0 0 8px;
                }

                .stella-nav-item {
                    display: flex;
                    align-items: center;
                    gap: 12px;
                    padding: 12px 12px 12px 13px;
                    border-radius: 8px;
                    cursor: pointer;
                    transition: color 0.3s ease;
                    color: var(--stella-text-dim);
                    font-weight: 500;
                    font-size: 14px;
                    margin-bottom: 4px;
                    white-space: nowrap;
                    overflow: hidden;
                    position: relative;
                    z-index: 2;
                }

                .stella-nav-item:hover {
                    color: var(--stella-text);
                }

                .stella-nav-item.active {
                    color: white;
                }

                .stella-sidebar.collapsed .stella-nav-text {
                    opacity: 0;
                    width: 0;
                    overflow: hidden;
                    transition: opacity 0.3s ease, width 0.3s ease;
                }

                .stella-sidebar:hover .stella-nav-text {
                    opacity: 1;
                    width: auto;
                }

                .stella-sidebar.collapsed .stella-nav-item {
                    gap: 0;
                }

                .stella-sidebar:hover .stella-nav-item {
                    gap: 12px;
                }

                .stella-content {
                    flex: 1;
                    padding: 32px;
                    overflow-y: auto;
                    background: var(--stella-bg);
                }

                /* Feature Box Shadcn Style */
                .stella-feature {
                    display: flex;
                    align-items: center;
                    justify-content: space-between;
                    padding: 16px;
                    background: transparent;
                    border-radius: 8px;
                    margin-bottom: 12px;
                    transition: all 0.2s ease;
                    cursor: pointer;
                    border: 1px solid var(--stella-border);
                    position: relative;
                }

                .stella-feature:hover {
                    background: var(--stella-item-bg);
                    border-color: rgba(255,255,255,0.1);
                }

                .stella-feature.active {
                    border-color: var(--stella-accent);
                    background: rgba(139, 92, 246, 0.05);
                }

                .stella-feature-info {
                    display: flex;
                    align-items: center;
                    gap: 12px;
                }

                .stella-feature-dot {
                    width: 8px;
                    height: 8px;
                    border-radius: 50%;
                    background: var(--stella-accent);
                    opacity: 0;
                    transition: opacity 0.2s;
                    box-shadow: 0 0 8px var(--stella-accent);
                }

                .stella-feature.active .stella-feature-dot {
                    opacity: 1;
                }

                .stella-feature-name {
                    font-weight: 500;
                    font-size: 14px;
                }

                .stella-settings-btn {
                    opacity: 0;
                    transition: opacity 0.2s;
                    cursor: pointer;
                    color: var(--stella-text-dim);
                    font-size: 18px;
                    padding: 6px;
                    border-radius: 4px;
                }

                .stella-settings-btn:hover {
                    background: rgba(255,255,255,0.1);
                    color: white;
                }

                .stella-feature:hover .stella-settings-btn {
                    opacity: 1;
                }

                /* Toast */
                #stella-toast-container {
                    position: fixed;
                    z-index: 100000;
                    display: flex;
                    flex-direction: column;
                    gap: 10px;
                }

                #stella-toast-container.toast-pos-bottom-right {
                    bottom: 30px;
                    right: 30px;
                }

                #stella-toast-container.toast-pos-bottom-left {
                    bottom: 30px;
                    left: 30px;
                }

                #stella-toast-container.toast-pos-top-right {
                    top: 30px;
                    right: 30px;
                }

                #stella-toast-container.toast-pos-top-left {
                    top: 30px;
                    left: 30px;
                }

                .stella-toast {
                    background: var(--carbon-black-2);
                    border: 1px solid var(--stella-border);
                    padding: 12px 20px;
                    border-radius: 8px;
                    color: white;
                    box-shadow: 0 10px 30px rgba(0,0,0,0.3);
                    animation: slideIn 0.3s ease;
                    display: flex;
                    align-items: center;
                    gap: 12px;
                    min-width: 200px;
                    font-size: 13px;
                    font-weight: 500;
                }

                @keyframes slideIn {
                    from { transform: translateX(100%); opacity: 0; }
                    to { transform: translateX(0); opacity: 1; }
                }

                /* Location Display */
                #stella-location-display {
                    position: fixed;
                    top: 20px;
                    left: 20px;
                    background: rgba(26, 26, 26, 0.85);
                    backdrop-filter: blur(4px);
                    padding: 12px;
                    border-radius: 6px;
                    color: white;
                    z-index: 9999;
                    border: 1px solid rgba(255, 255, 255, 0.1);
                    border-bottom: 2px solid var(--stella-accent);
                    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
                    min-width: 260px;
                    cursor: move;
                    display: none;
                    font-family: 'Inter', sans-serif;
                }

                #stella-location-display:hover {
                    box-shadow: 0 12px 40px rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(139, 92, 246, 0.2);
                }

                .stella-loc-row {
                    display: flex;
                    align-items: center;
                    gap: 12px;
                    margin-bottom: 10px;
                    font-size: 14px;
                    color: #ffffff;
                    padding: 8px 10px;
                    background: rgba(139, 92, 246, 0.05);
                    border-radius: 6px;
                    border: 1px solid rgba(139, 92, 246, 0.1);
                    transition: all 0.2s ease;
                }

                .stella-loc-row:last-child {
                    margin-bottom: 0;
                }

                .stella-loc-row:hover {
                    background: rgba(139, 92, 246, 0.1);
                    border-color: rgba(139, 92, 246, 0.2);
                }

                .stella-loc-icon {
                    color: var(--stella-accent);
                    font-size: 18px;
                    display: flex;
                    align-items: center;
                    justify-content: center;
                    width: 24px;
                    height: 24px;
                    background: rgba(139, 92, 246, 0.15);
                    border-radius: 6px;
                }

                .stella-flag {
                    width: 24px;
                    height: 18px;
                    border-radius: 4px;
                    object-fit: cover;
                    border: 1px solid rgba(255, 255, 255, 0.1);
                    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
                }

                #stella-map-image {
                    width: 100%;
                    height: 140px;
                    background-size: cover;
                    background-position: center;
                    border-radius: 8px;
                    margin-top: 12px;
                    border: 1px solid var(--stella-border);
                    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
                    opacity: 0.95;
                    transition: all 0.3s ease;
                }

                #stella-map-image:hover {
                    opacity: 1;
                    box-shadow: 0 6px 16px rgba(0, 0, 0, 0.4);
                }

                /* HUD Elements */
                #stella-watermark {
                    position: fixed;
                    top: 10px;
                    left: 50%;
                    transform: translateX(-50%);
                    background: rgba(26, 26, 26, 0.6);
                    backdrop-filter: blur(4px);
                    padding: 6px 12px;
                    border-radius: 6px;
                    color: rgba(255, 255, 255, 0.8);
                    font-family: 'Inter', sans-serif;
                    font-size: 12px;
                    font-weight: 600;
                    z-index: 9998;
                    border: 1px solid rgba(255, 255, 255, 0.1);
                    pointer-events: none;
                    display: none;
                    transition: all 0.3s ease;
                    border-bottom: 2px solid var(--stella-accent);
                }

                #stella-watermark.top-left { top: 10px; left: 10px; transform: none; }
                #stella-watermark.top-center { top: 50px; left: 50%; transform: translateX(-50%); }
                #stella-watermark.top-right { top: 10px; right: 10px; left: auto; transform: none; }
                #stella-watermark.bottom-left { bottom: 10px; left: 10px; top: auto; transform: none; }
                #stella-watermark.bottom-center { bottom: 10px; left: 50%; top: auto; transform: translateX(-50%); }
                #stella-watermark.bottom-right { bottom: 10px; right: 10px; top: auto; left: auto; transform: none; }

                #stella-map-timer {
                    position: fixed;
                    bottom: 20px;
                    left: 50%;
                    transform: translateX(-50%);
                    background: rgba(26, 26, 26, 0.8);
                    padding: 8px 16px;
                    border-radius: 20px;
                    color: white;
                    font-family: 'Inter', sans-serif;
                    font-size: 16px;
                    font-weight: 700;
                    z-index: 9998;
                    border: 1px solid var(--stella-accent);
                    display: none;
                    box-shadow: 0 4px 12px rgba(0,0,0,0.3);
                }

                #stella-hotkey-display {
                    position: fixed;
                    top: 100px;
                    left: 20px;
                    background: rgba(26, 26, 26, 0.85);
                    backdrop-filter: blur(4px);
                    padding: 10px 14px;
                    border-radius: 6px;
                    color: white;
                    z-index: 9998;
                    border: 1px solid rgba(255, 255, 255, 0.1);
                    border-bottom: 2px solid var(--stella-accent);
                    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
                    min-width: 180px;
                    cursor: move;
                    display: none;
                    font-family: 'Inter', sans-serif;
                }

                .stella-hk-row {
                    display: flex;
                    align-items: center;
                    justify-content: space-between;
                    margin-bottom: 6px;
                    font-size: 13px;
                    color: var(--stella-text-dim);
                }

                .stella-hk-row:last-child { margin-bottom: 0; }
                .stella-hk-active { color: white; font-weight: 500; }

                .stella-hk-keys {
                    display: flex;
                    gap: 4px;
                }

                .stella-hk-key-badge {
                    background: rgba(255,255,255,0.1);
                    padding: 2px 6px;
                    border-radius: 4px;
                    font-size: 11px;
                    font-family: monospace;
                    color: var(--stella-accent);
                    border: 1px solid rgba(139, 92, 246, 0.2);
                }

                /* Popups */
                .stella-popup {
                    background: var(--stella-bg) !important;
                    border: 1px solid var(--stella-border) !important;
                    box-shadow: 0 10px 40px rgba(0,0,0,0.5) !important;
                    color: white !important;
                    padding: 20px;
                    border-radius: 12px;
                    z-index: 100000;
                    min-width: 260px;
                    position: fixed;
                }

                .stella-input-wrapper {
                    position: relative;
                    width: 100%;
                }

                .stella-input {
                    width: 100%;
                    background: var(--carbon-black-2);
                    border: 1px solid var(--stella-border);
                    padding: 8px 12px;
                    border-radius: 6px;
                    color: white;
                    font-family: 'Inter', sans-serif;
                    font-size: 13px;
                    outline: none;
                    transition: border-color 0.2s;
                }

                .stella-input:focus {
                    border-color: var(--stella-accent);
                }

                .stella-input-clear {
                    position: absolute;
                    right: 8px;
                    top: 50%;
                    transform: translateY(-50%);
                    color: var(--stella-text-dim);
                    cursor: pointer;
                    font-size: 14px;
                    display: none;
                }

                .stella-input:not(:placeholder-shown) + .stella-input-clear {
                    display: block;
                }

                .stella-btn {
                    background: var(--stella-accent);
                    color: white;
                    border: none;
                    padding: 6px 12px;
                    border-radius: 6px;
                    cursor: pointer;
                    font-size: 12px;
                    font-weight: 500;
                    transition: background 0.2s;
                }

                .stella-btn:hover {
                    background: var(--stella-accent-hover);
                }

                .stella-btn-secondary {
                    background: transparent;
                    border: 1px solid var(--stella-border);
                    color: var(--stella-text-dim);
                }

                .stella-btn-secondary:hover {
                    background: rgba(255,255,255,0.05);
                    color: white;
                }

                /* Welcome Modal */
                #stella-welcome {
                    position: fixed;
                    top: 50%;
                    left: 50%;
                    transform: translate(-50%, -50%);
                    background: var(--stella-bg);
                    border: 1px solid var(--stella-border);
                    padding: 30px;
                    border-radius: 12px;
                    z-index: 100001;
                    text-align: center;
                    box-shadow: 0 20px 60px rgba(0,0,0,0.8);
                    max-width: 400px;
                }

                .stella-welcome-title {
                    font-size: 24px;
                    font-weight: 800;
                    margin-bottom: 15px;
                    color: white;
                }

                .stella-welcome-text {
                    color: var(--stella-text-dim);
                    font-size: 14px;
                    line-height: 1.6;
                    margin-bottom: 25px;
                }

                .stella-key {
                    background: var(--carbon-black-3);
                    border: 1px solid var(--stella-border);
                    padding: 2px 6px;
                    border-radius: 4px;
                    color: white;
                    font-family: monospace;
                    font-size: 12px;
                }

                /* Logs */
                .stella-log-entry {
                    font-family: monospace;
                    font-size: 12px;
                    padding: 4px 0;
                    border-bottom: 1px solid rgba(255,255,255,0.05);
                    color: var(--stella-text-dim);
                }
                .stella-log-entry span { color: var(--stella-accent); margin-right: 8px; }
                .stella-log-entry.error { color: #ff5555; }
                .stella-log-entry.error span { color: #ff5555; }

                /* Checkbox & Slider */
                .stella-checkbox-wrapper {
                    display: flex;
                    align-items: center;
                    gap: 10px;
                    margin-bottom: 10px;
                    cursor: pointer;
                }

                /* Custom Color Picker */
                .stella-color-picker-overlay {
                    position: fixed;
                    top: 0;
                    left: 0;
                    width: 100%;
                    height: 100%;
                    background: rgba(0, 0, 0, 0.7);
                    backdrop-filter: blur(4px);
                    z-index: 99999;
                    display: flex;
                    align-items: center;
                    justify-content: center;
                }

                .stella-color-picker {
                    background: var(--carbon-black);
                    border: 1px solid var(--stella-border);
                    border-radius: 12px;
                    padding: 20px;
                    box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
                    width: 320px;
                }

                .stella-color-picker-header {
                    display: flex;
                    justify-content: space-between;
                    align-items: center;
                    margin-bottom: 16px;
                }

                .stella-color-picker-title {
                    color: white;
                    font-size: 16px;
                    font-weight: 600;
                }

                .stella-color-picker-close {
                    background: none;
                    border: none;
                    color: var(--stella-text-dim);
                    cursor: pointer;
                    padding: 4px;
                    border-radius: 4px;
                    transition: all 0.2s;
                }

                .stella-color-picker-close:hover {
                    background: var(--carbon-black-3);
                    color: white;
                }

                .stella-color-canvas {
                    width: 100%;
                    height: 200px;
                    border-radius: 8px;
                    cursor: crosshair;
                    margin-bottom: 12px;
                    border: 1px solid var(--stella-border);
                }

                .stella-hue-slider {
                    width: 100%;
                    height: 16px;
                    border-radius: 8px;
                    background: linear-gradient(to right,
                        #ff0000 0%,
                        #ffff00 17%,
                        #00ff00 33%,
                        #00ffff 50%,
                        #0000ff 67%,
                        #ff00ff 83%,
                        #ff0000 100%);
                    margin-bottom: 16px;
                    position: relative;
                    cursor: pointer;
                    border: 1px solid var(--stella-border);
                }

                .stella-hue-slider-thumb {
                    position: absolute;
                    top: -2px;
                    width: 20px;
                    height: 20px;
                    background: white;
                    border: 2px solid var(--carbon-black);
                    border-radius: 50%;
                    cursor: pointer;
                    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
                }

                .stella-color-preview {
                    display: flex;
                    gap: 12px;
                    margin-bottom: 16px;
                }

                .stella-color-preview-box {
                    flex: 1;
                    height: 50px;
                    border-radius: 8px;
                    border: 1px solid var(--stella-border);
                }

                .stella-color-inputs {
                    display: grid;
                    grid-template-columns: 1fr 1fr;
                    gap: 8px;
                }

                .stella-color-input-group {
                    display: flex;
                    flex-direction: column;
                    gap: 4px;
                }

                .stella-color-input-label {
                    font-size: 10px;
                    color: var(--stella-text-dim);
                    text-transform: uppercase;
                    font-weight: 600;
                }

                .stella-color-input-field {
                    background: var(--carbon-black-3);
                    border: 1px solid var(--stella-border);
                    border-radius: 6px;
                    padding: 8px;
                    color: white;
                    font-size: 12px;
                    font-family: monospace;
                }

                .stella-color-input-field:focus {
                    outline: none;
                    border-color: var(--stella-accent);
                }

                /* Custom Sliders */
                input[type="range"] {
                    -webkit-appearance: none;
                    appearance: none;
                    background: transparent;
                    cursor: pointer;
                    outline: none;
                }

                input[type="range"]::-webkit-slider-track {
                    background: var(--stella-accent);
                    height: 4px;
                    border-radius: 2px;
                    border: none;
                }

                input[type="range"]::-moz-range-track {
                    background: var(--stella-accent);
                    height: 4px;
                    border-radius: 2px;
                    border: none;
                }

                input[type="range"]::-webkit-slider-thumb {
                    -webkit-appearance: none;
                    appearance: none;
                    width: 16px;
                    height: 16px;
                    border-radius: 50%;
                    background: var(--carbon-black);
                    border: 2px solid var(--stella-accent);
                    cursor: pointer;
                    margin-top: -6px;
                    outline: none;
                }

                input[type="range"]::-moz-range-thumb {
                    width: 16px;
                    height: 16px;
                    border-radius: 50%;
                    background: var(--carbon-black);
                    border: 2px solid var(--stella-accent);
                    cursor: pointer;
                    outline: none;
                }

                .stella-checkbox {
                    width: 16px;
                    height: 16px;
                    border: 1px solid var(--stella-border);
                    border-radius: 4px;
                    background: var(--carbon-black-2);
                    display: flex;
                    align-items: center;
                    justify-content: center;
                    transition: all 0.2s;
                }

                .stella-checkbox.checked {
                    background: var(--stella-accent);
                    border-color: var(--stella-accent);
                }

                .stella-checkbox.checked::after {
                    content: '✓';
                    font-size: 12px;
                    color: white;
                }

                .stella-slider-wrapper {
                    margin-bottom: 15px;
                }

                .stella-slider-label {
                    display: flex;
                    justify-content: space-between;
                    font-size: 12px;
                    color: var(--stella-text-dim);
                    margin-bottom: 5px;
                }

                .stella-slider {
                    width: 100%;
                    -webkit-appearance: none;
                    height: 4px;
                    background: var(--carbon-black-3);
                    border-radius: 2px;
                    outline: none;
                }

                .stella-slider::-webkit-slider-thumb {
                    -webkit-appearance: none;
                    width: 14px;
                    height: 14px;
                    border-radius: 50%;
                    background: var(--stella-accent);
                    cursor: pointer;
                    transition: background .15s ease-in-out;
                }
            `;
            GM_addStyle(css);
        }

        checkFirstRun() {
            if (this.app.settings.get('firstRun')) {
                const modal = document.createElement('div');
                modal.id = 'stella-welcome';
                modal.innerHTML = `
                    <div class="stella-welcome-title">Welcome to Stella V4</div>
                    <div class="stella-welcome-text">
                        <p>Here's how to use the menu:</p>
                        <ul style="text-align:left; margin: 15px 0; padding-left: 20px;">
                            <li>Press <span class="stella-key">Insert</span> to toggle the menu.</li>
                            <li><span class="stella-key">Left Click</span> a feature to toggle it.</li>
                            <li><span class="stella-key">Right Click</span> a feature to set hotkeys.</li>
                            <li>Click the <span class="material-icons" style="font-size:14px; vertical-align:middle;">settings</span> icon for advanced settings.</li>
                        </ul>
                    </div>
                    <button class="stella-btn" id="stella-welcome-close">Get Started</button>
                `;
                document.body.appendChild(modal);

                document.getElementById('stella-welcome-close').addEventListener('click', () => {
                    modal.remove();
                    this.showLocationApiPopup();
                });
            }
        }

        showLocationApiPopup() {
            const modal = document.createElement('div');
            modal.id = 'stella-welcome';
            modal.innerHTML = `
                <div class="stella-welcome-title">Location API Setup</div>
                <div class="stella-welcome-text">
                    <p>The first API call will trigger a Tampermonkey permission popup if you are not on the latest Tamper Monkey Version.</p>
                    <p style="margin-top:10px;">Click <strong>"Always allow"</strong> to enable location lookups.</p>
                    <img src="https://i.imgur.com/yIVeDlq.png" style="max-width:100%; margin:15px 0; border-radius:8px; border:1px solid var(--stella-border);" />
                    <div style="margin-top:15px; padding:10px; background:rgba(139, 92, 246, 0.1); border:1px solid rgba(139, 92, 246, 0.2); border-radius:6px;">
                        <div style="display:flex; align-items:start; gap:8px;">
                            <span class="material-icons" style="font-size:16px; color:var(--stella-accent);">info</span>
                            <div style="font-size:11px; color:var(--stella-text-dim); line-height:1.5;">
                                This permission allows the script to use the LocationIQ API for reverse geocoding.
                            </div>
                        </div>
                    </div>
                </div>
                <button class="stella-btn" id="stella-location-api-okay">Okay</button>
            `;
            document.body.appendChild(modal);

            document.getElementById('stella-location-api-okay').addEventListener('click', () => {
                modal.remove();
                this.app.settings.set('firstRun', false);
                this.app.network.getLocationDetails(40.7128, -74.0060);
                this.toggleMenu();
            });
        }

        showWebhookOnboarding(callback) {
            const modal = document.createElement('div');
            modal.id = 'stella-welcome';
            modal.innerHTML = `
                <div class="stella-welcome-title">Discord Webhook Setup</div>
                <div class="stella-welcome-text">
                    <p>When you press "Okay", a Tampermonkey permission popup will appear (if you are not on the latest Tampermonkey version).</p>
                    <p style="margin-top:10px;">Click <strong>"Always allow"</strong> to enable Discord webhook functionality.</p>
                    <div style="margin-top:15px; padding:10px; background:rgba(139, 92, 246, 0.1); border:1px solid rgba(139, 92, 246, 0.2); border-radius:6px;">
                        <div style="display:flex; align-items:start; gap:8px;">
                            <span class="material-icons" style="font-size:16px; color:var(--stella-accent);">info</span>
                            <div style="font-size:11px; color:var(--stella-text-dim); line-height:1.5;">
                                This permission allows the script to send location data to your Discord webhook URL.
                            </div>
                        </div>
                    </div>
                </div>
                <button class="stella-btn" id="stella-webhook-okay">Okay</button>
            `;
            document.body.appendChild(modal);

            document.getElementById('stella-webhook-okay').addEventListener('click', () => {
                modal.remove();
                callback();
            });
        }

        createOverlay() {
            this.menu = document.createElement('div');
            this.menu.id = 'stella-menu';
            this.menu.innerHTML = `
                <div class="stella-header">
                    <div class="stella-logo">
                        <img src="https://minimalistmoon.com/favlogo.png" alt="Logo">
                        <span class="stella-logo-text">Stella</span>
                    </div>
                    <div class="stella-header-actions">
                        <div class="stella-header-btn" data-tab="customize" title="Customize">
                            <span class="material-icons">edit</span>
                        </div>
                        <div class="stella-header-btn" data-tab="logs" title="Logs">
                            <span class="material-icons">list_alt</span>
                        </div>
                        <div class="stella-header-btn" data-tab="settings" title="Settings">
                            <span class="material-icons">settings</span>
                        </div>
                    </div>
                </div>
                <div class="stella-body">
                    <div class="stella-sidebar collapsed">
                        <div class="stella-nav">
                            <div class="stella-nav-highlight"></div>
                            <div class="stella-nav-item active" data-tab="location">
                                <span class="material-icons">place</span> <span class="stella-nav-text">Location</span>
                            </div>
                            <div class="stella-nav-item" data-tab="hud">
                                <span class="material-icons">dashboard</span> <span class="stella-nav-text">HUD</span>
                            </div>
                            <div class="stella-nav-item" data-tab="autoplay">
                                <span class="material-icons">play_arrow</span> <span class="stella-nav-text">Autoplay</span>
                            </div>
                        </div>
                    </div>
                    <div class="stella-content" id="stella-tab-content"></div>
                </div>
            `;
            document.body.appendChild(this.menu);
            this.setupNavigation();
            this.renderTab('location');
        }

        createLocationDisplay() {
            this.locationDisplay = document.createElement('div');
            this.locationDisplay.id = 'stella-location-display';
            this.locationDisplay.innerHTML = `
                <div style="display:flex; align-items:center; justify-content:space-between; margin-bottom:6px;">
                    <span style="font-weight:600; color:var(--stella-text);">Location</span>
                    <button id="stella-loc-popout" title="Pop out" style="display:inline-flex; align-items:center; gap:4px; background:transparent; border:1px solid var(--stella-border); color:var(--stella-text-dim); border-radius:4px; padding:2px 6px; cursor:pointer; font-size:11px;">
                        <span class="material-icons" style="font-size:14px;">open_in_new</span>
                    </button>
                </div>
                <div class="stella-loc-row" id="stella-row-country">
                    <span class="material-icons stella-loc-icon">public</span>
                    <img id="stella-loc-flag" class="stella-flag" style="display:none;" />
                    <span id="stella-loc-country">Country: N/A</span>
                </div>
                <div class="stella-loc-row" id="stella-row-state">
                    <span class="material-icons stella-loc-icon">map</span>
                    <span id="stella-loc-state">State: N/A</span>
                </div>
                <div class="stella-loc-row" id="stella-row-city">
                    <span class="material-icons stella-loc-icon">location_city</span>
                    <span id="stella-loc-city">City: N/A</span>
                </div>
                <div id="stella-map-image"></div>
            `;
            document.body.appendChild(this.locationDisplay);
            this.makeDraggable(this.locationDisplay, 'locationDisplay');

            const popButton = document.getElementById('stella-loc-popout');
            popButton.addEventListener('click', () => {
                if (!this.locationDisplayPopped) {
                    this.popOutLocationDisplay();
                    popButton.innerHTML = '<span class="material-icons" style="font-size:14px;">call_received</span>';
                    popButton.title = 'Pop back in';
                } else {
                    this.popInLocationDisplay();
                    popButton.innerHTML = '<span class="material-icons" style="font-size:14px;">open_in_new</span>';
                    popButton.title = 'Pop out';
                }
            });

            if (this.app.settings.get('features').locationDisplay) {
                this.locationDisplay.style.display = 'block';
            }
        }

        createHUD() {

            this.watermark = document.createElement('div');
            this.watermark.id = 'stella-watermark';
            this.watermark.innerHTML = `
                <div style="display:flex; align-items:center; gap:12px;">
                    <div id="stella-watermark-logo-group" style="display:flex; align-items:center; gap:6px;">
                        <img src="https://minimalistmoon.com/favlogo.png" style="width:16px; height:16px; border-radius:4px;">
                        <span id="stella-watermark-name">Stella</span>
                    </div>
                    <div id="stella-watermark-user-group" style="display:flex; align-items:center; gap:4px; color:var(--stella-accent);">
                        <span class="material-icons" style="font-size:14px;">person</span>
                        <span id="stella-watermark-user"></span>
                    </div>
                    <div id="stella-watermark-clock-group" style="display:flex; align-items:center; gap:4px; opacity:0.8;">
                        <span class="material-icons" style="font-size:14px;">schedule</span>
                        <span id="stella-clock" style="font-weight:400;"></span>
                    </div>
                </div>
            `;
            document.body.appendChild(this.watermark);

            this.updateWatermarkPosition();
            this.updateWatermarkContent();
            this.startClock();

            if (this.app.settings.get('features').watermark) {
                this.watermark.style.display = 'block';
            }


            this.mapTimer = document.createElement('div');
            this.mapTimer.id = 'stella-map-timer';
            this.mapTimer.textContent = '00:00';
            document.body.appendChild(this.mapTimer);
            if (this.app.settings.get('features').mapTimer) {
                this.mapTimer.style.display = 'block';
            }


            this.createHotkeyDisplay();
        }

        createHotkeyDisplay() {
            this.hotkeyDisplay = document.createElement('div');
            this.hotkeyDisplay.id = 'stella-hotkey-display';
            document.body.appendChild(this.hotkeyDisplay);
            this.makeDraggable(this.hotkeyDisplay);
            this.updateHotkeyDisplay();

            if (this.app.settings.get('features').hotkeyDisplay) {
                this.hotkeyDisplay.style.display = 'block';
            }
        }

        updateHotkeyDisplay() {
            const settings = this.app.settings.get('featureSettings').hotkeyDisplay;
            const features = this.app.settings.get('features');
            const hotkeys = this.app.settings.get('hotkeys');

            let html = '';
            let hasContent = false;

            const featureNames = {
                openGM: 'Google Maps',
                openPlonkIT: 'PlonkIT',
                locationDisplay: 'Location Info',
                tts: 'Text to Speech',
                discordWebhook: 'Webhook',
                watermark: 'Watermark',
                mapTimer: 'Map Timer',
                hotkeyDisplay: 'Hotkeys'
            };

            html += `<div style="font-weight:700; margin-bottom:8px; color:white; border-bottom:1px solid rgba(255,255,255,0.1); padding-bottom:6px; font-size:12px; letter-spacing:0.5px; text-transform:uppercase;">Hotkeys</div>`;

            for (const [key, name] of Object.entries(featureNames)) {
                const isActive = features[key];
                const hk = hotkeys[key];
                const hasHotkey = hk && (hk.trigger || hk.toggle);


                if (settings.mode === 'active' && !isActive) continue;
                if (settings.mode === 'bound' && !hasHotkey) continue;

                hasContent = true;
                html += `<div class="stella-hk-row ${isActive ? 'stella-hk-active' : ''}">
                    <span>${name}</span>
                    <div class="stella-hk-keys">`;

                if (hk) {
                    if (settings.showToggle && hk.toggle) {
                        html += `<span class="stella-hk-key-badge" title="Toggle">${hk.toggle}</span>`;
                    }
                    if (settings.showTrigger && hk.trigger) {
                        html += `<span class="stella-hk-key-badge" title="Trigger">${hk.trigger}</span>`;
                    }
                }

                html += `</div></div>`;
            }

            if (!hasContent) html = '<div class="stella-hk-row">No active features</div>';
            this.hotkeyDisplay.innerHTML = html;
        }

        updateLocationDisplay(data) {
            if (!data) return;

            const settings = this.app.settings.get('featureSettings').locationDisplay;

            this.locationDisplayData = data;

            document.getElementById('stella-loc-country').textContent = data.country || 'N/A';
            document.getElementById('stella-loc-state').textContent = data.state || 'N/A';
            document.getElementById('stella-loc-city').textContent = data.city || 'N/A';

            document.getElementById('stella-row-country').style.display = settings.showCountry ? 'flex' : 'none';
            document.getElementById('stella-row-state').style.display = settings.showState ? 'flex' : 'none';
            document.getElementById('stella-row-city').style.display = settings.showCity ? 'flex' : 'none';

            const flagImg = document.getElementById('stella-loc-flag');
            if (data.countryCode && settings.showCountry) {
                flagImg.src = `https://flagcdn.com/24x18/${data.countryCode}.png`;
                flagImg.style.display = 'block';
            } else {
                flagImg.style.display = 'none';
            }

            let zoom = 5;
            if (settings.showCity && data.city) zoom = settings.cityZoom;
            else if (settings.showState && data.state) zoom = settings.stateZoom;

            const mapUrl = `https://static-maps.yandex.ru/1.x/?ll=${this.app.network.globalCoordinates.lng},${this.app.network.globalCoordinates.lat}&z=${zoom}&size=300,150&l=map&lang=en`;
            document.getElementById('stella-map-image').style.backgroundImage = `url("${mapUrl}")`;

            this.syncLocationPopout(data, mapUrl, settings);

            if (this.app.settings.get('features').autoPin) {
                const coords = this.app.network.globalCoordinates;
                if (coords?.lat && coords?.lng) {
                    autoPinLocation(coords.lat, coords.lng);
                }
            }
        }

        syncLocationPopout(data, mapUrl, settings) {
            if (!this.locationDisplayWindow || this.locationDisplayWindow.closed || !this.locationDisplayPopped) return;
            try {
                const doc = this.locationDisplayWindow.document;
                doc.getElementById('pop-country').textContent = data.country || 'N/A';
                doc.getElementById('pop-state').textContent = data.state || 'N/A';
                doc.getElementById('pop-city').textContent = data.city || 'N/A';

                doc.getElementById('pop-country-row').style.display = settings.showCountry ? 'flex' : 'none';
                doc.getElementById('pop-state-row').style.display = settings.showState ? 'flex' : 'none';
                doc.getElementById('pop-city-row').style.display = settings.showCity ? 'flex' : 'none';

                const flagImg = doc.getElementById('pop-flag');
                if (data.countryCode && settings.showCountry) {
                    flagImg.src = `https://flagcdn.com/24x18/${data.countryCode}.png`;
                    flagImg.style.display = 'block';
                } else {
                    flagImg.style.display = 'none';
                }

                doc.getElementById('pop-map').style.backgroundImage = `url("${mapUrl}")`;
            } catch (err) {
                this.locationDisplayWindow = null;
                this.locationDisplayPopped = false;
                const btn = document.getElementById('stella-loc-popout');
                if (btn) {
                    btn.textContent = '↗';
                    btn.title = 'Pop out';
                }
            }
        }

        popOutLocationDisplay() {
            if (this.locationDisplayPopped) return;

            this.locationDisplayWindow = window.open('', 'stellaLocationPopout', 'width=360,height=260');
            if (!this.locationDisplayWindow) return;

            this.locationDisplayPopped = true;
            this.locationDisplay.style.display = 'none';

            const css = `
                body { margin:0; background:rgba(12,12,18,0.96); color:#fff; font-family: Inter, system-ui, sans-serif; }
                .card { padding:12px; background:rgba(24,24,32,0.85); border:1px solid rgba(255,255,255,0.08); border-radius:12px; box-shadow:0 10px 30px rgba(0,0,0,0.45); }
                .header { display:flex; align-items:center; justify-content:space-between; margin-bottom:8px; font-weight:600; }
                .row { display:flex; align-items:center; gap:8px; margin-bottom:6px; }
                .flag { width:24px; height:18px; border-radius:3px; object-fit:cover; }
                .map { width:300px; height:150px; background-size:cover; background-position:center; border-radius:8px; border:1px solid rgba(255,255,255,0.1); }
                .label { color:#c9c9d1; font-size:12px; }
            `;

            const doc = this.locationDisplayWindow.document;
            doc.open();
            doc.write(`<!DOCTYPE html><html><head><title>Location</title><style>${css}</style><link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons"></head><body><div class="card">
                <div class="header">
                    <span>Location</span>
                    <button id="pop-in" style="display:inline-flex; align-items:center; gap:4px; background:transparent; border:1px solid rgba(255,255,255,0.12); color:#fff; border-radius:4px; padding:2px 6px; cursor:pointer; font-size:11px;"><span class="material-icons" style="font-size:14px;">call_received</span></button>
                </div>
                <div class="row" id="pop-country-row"><span class="material-icons" style="font-size:16px;">public</span><img id="pop-flag" class="flag" style="display:none;"><span id="pop-country">Country: N/A</span></div>
                <div class="row" id="pop-state-row"><span class="material-icons" style="font-size:16px;">map</span><span id="pop-state">State: N/A</span></div>
                <div class="row" id="pop-city-row"><span class="material-icons" style="font-size:16px;">location_city</span><span id="pop-city">City: N/A</span></div>
                <div class="map" id="pop-map"></div>
            </div></body></html>`);
            doc.close();

            const popInBtn = doc.getElementById('pop-in');
            popInBtn.addEventListener('click', () => this.popInLocationDisplay());

            this.locationDisplayWindow.addEventListener('beforeunload', () => {
                this.locationDisplayWindow = null;
                if (this.locationDisplayPopped) {
                    this.popInLocationDisplay(true);
                }
            });

            const btn = document.getElementById('stella-loc-popout');
            if (btn) {
                btn.textContent = '↩';
                btn.title = 'Pop back in';
            }

            if (this.locationDisplayData) {
                const settings = this.app.settings.get('featureSettings').locationDisplay;
                const mapUrl = `https://static-maps.yandex.ru/1.x/?ll=${this.app.network.globalCoordinates.lng},${this.app.network.globalCoordinates.lat}&z=${settings.showCity && this.locationDisplayData.city ? settings.cityZoom : settings.showState && this.locationDisplayData.state ? settings.stateZoom : 5}&size=300,150&l=map&lang=en`;
                this.syncLocationPopout(this.locationDisplayData, mapUrl, settings);
            }
        }

        popInLocationDisplay(fromWindowClose = false) {
            if (!this.locationDisplayPopped) return;

            if (this.locationDisplayWindow && !this.locationDisplayWindow.closed) {
                this.locationDisplayWindow.close();
            }
            this.locationDisplayWindow = null;
            this.locationDisplayPopped = false;

            this.locationDisplay.style.display = this.app.settings.get('features').locationDisplay ? 'block' : 'none';

            if (!fromWindowClose) {
                const btn = document.getElementById('stella-loc-popout');
                if (btn) {
                    btn.innerHTML = '<span class="material-icons" style="font-size:14px;">open_in_new</span>';
                    btn.title = 'Pop out';
                }
            }
        }

        makeDraggable(element, saveKey = null) {
            let isDragging = false;
            let currentX = 0, currentY = 0, initialX = 0, initialY = 0;
            const app = this.app;


            if (saveKey) {
                const positions = app.settings.get('elementPositions');
                if (positions && positions[saveKey]) {
                    element.style.left = positions[saveKey].left;
                    element.style.top = positions[saveKey].top;
                }
            }

            element.onmousedown = dragMouseDown;

            function dragMouseDown(e) {
                e = e || window.event;
                if (['INPUT', 'TEXTAREA', 'SELECT', 'BUTTON'].includes(e.target.tagName)) return;
                e.preventDefault();

                isDragging = true;
                initialX = e.clientX;
                initialY = e.clientY;

                const rect = element.getBoundingClientRect();
                currentX = rect.left;
                currentY = rect.top;

                document.onmouseup = closeDragElement;
                document.onmousemove = elementDrag;
            }

            function elementDrag(e) {
                if (!isDragging) return;
                e = e || window.event;
                e.preventDefault();

                const deltaX = e.clientX - initialX;
                const deltaY = e.clientY - initialY;

                element.style.left = (currentX + deltaX) + "px";
                element.style.top = (currentY + deltaY) + "px";
            }

            function closeDragElement() {
                isDragging = false;
                document.onmouseup = null;
                document.onmousemove = null;


                if (saveKey) {
                    const positions = app.settings.get('elementPositions') || {};
                    positions[saveKey] = {
                        left: element.style.left,
                        top: element.style.top
                    };
                    app.settings.set('elementPositions', positions);
                }
            }
        }

        setupNavigation() {
            const navItems = this.menu.querySelectorAll('.stella-nav-item');
            const headerBtns = this.menu.querySelectorAll('.stella-header-btn');
            const highlight = this.menu.querySelector('.stella-nav-highlight');

            const updateHighlight = (item) => {
                if (highlight && item.classList.contains('stella-nav-item')) {
                    highlight.style.top = `${item.offsetTop}px`;
                    highlight.style.height = `${item.offsetHeight}px`;
                    highlight.style.opacity = '1';
                } else if (highlight) {
                    highlight.style.opacity = '0';
                }
            };


            const activeNav = this.menu.querySelector('.stella-nav-item.active');
            if (activeNav) {

                setTimeout(() => updateHighlight(activeNav), 0);
            }

            const allTabs = [...navItems, ...headerBtns];

            allTabs.forEach(item => {
                item.addEventListener('click', () => {
                    allTabs.forEach(nav => nav.classList.remove('active'));
                    item.classList.add('active');

                    if (item.classList.contains('stella-nav-item')) {
                        updateHighlight(item);
                    } else {
                        if (highlight) highlight.style.opacity = '0';
                    }

                    this.renderTab(item.dataset.tab);
                });
            });
        }

        renderTab(tabName) {
            const content = this.menu.querySelector('#stella-tab-content');
            content.innerHTML = '';

            switch (tabName) {
                case 'location':
                    this.renderLocationTab(content);
                    break;
                case 'hud':
                    this.renderHUDTab(content);
                    break;
                case 'autoplay':
                    content.innerHTML = `
                        <div style="display:flex; flex-direction:column; align-items:center; justify-content:center; height:100%; color:var(--stella-text-dim); gap:15px;">
                            <span class="material-icons" style="font-size:48px; color:#f97316;">warning</span>
                            <div style="text-align:center;">
                                <p style="font-size:16px; font-weight:600; color:white; margin-bottom:4px;">Maintenance Mode</p>
                                <p style="font-size:13px;">This feature is currently being updated.</p>
                            </div>
                        </div>
                    `;
                    break;
                case 'settings':
                    this.renderSettingsTab(content);
                    break;
                case 'logs':
                    this.renderLogsTab(content);
                    break;
                case 'customize':
                    this.renderCustomizeTab(content);
                    break;
            }
        }

        renderLocationTab(container) {
            this.createFeature(container, 'Open Google Maps ⚠️', 'openGM');
            this.createFeature(container, 'Open PlonkIT', 'openPlonkIT');
            this.createFeature(container, 'Location Display', 'locationDisplay', true);
            this.createFeature(container, 'Text to Speech', 'tts', true);
            this.createFeature(container, 'Discord Webhook', 'discordWebhook', true);
        }

        renderHUDTab(container) {
            this.createFeature(container, 'Watermark', 'watermark', true);
            this.createFeature(container, 'Classic Map Timer', 'mapTimer');
            this.createFeature(container, 'Hotkey Display', 'hotkeyDisplay', true);
            this.createFeature(container, 'Toast Notifications', 'toastNotifications', true);
        }

        renderCustomizeTab(container) {

            const defaultSection = document.createElement('div');
            defaultSection.style.marginBottom = '24px';
            defaultSection.innerHTML = '<h3 style="color:white; font-size:16px; font-weight:600; margin-bottom:12px;">Default Themes</h3>';
            container.appendChild(defaultSection);

            const defaultThemes = this.app.settings.get('defaultThemes');
            for (const [key, theme] of Object.entries(defaultThemes)) {
                this.createThemeItem(defaultSection, theme.name, key, false);
            }


            const customSection = document.createElement('div');
            customSection.style.marginBottom = '24px';
            customSection.innerHTML = `
                <div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:12px;">
                    <h3 style="color:white; font-size:16px; font-weight:600;">Custom Themes</h3>
                    <button class="stella-btn" id="create-theme-btn" style="padding:8px 16px;">
                        <span class="material-icons" style="font-size:16px; vertical-align:middle;">add</span>
                        Create Theme
                    </button>
                </div>
            `;
            container.appendChild(customSection);

            const customThemes = this.app.settings.get('customThemes');
            const hasCustomThemes = Object.keys(customThemes).length > 0;

            if (!hasCustomThemes) {
                const emptyState = document.createElement('div');
                emptyState.style.cssText = 'padding:20px; text-align:center; color:var(--stella-text-dim); font-size:13px; border:1px dashed var(--stella-border); border-radius:8px;';
                emptyState.textContent = 'No custom themes yet. Create one to get started!';
                customSection.appendChild(emptyState);
            } else {
                for (const [key, theme] of Object.entries(customThemes)) {
                    this.createThemeItem(customSection, theme.name, key, true);
                }
            }


            document.getElementById('create-theme-btn').addEventListener('click', () => {
                this.createNewTheme();
            });
        }

        createThemeItem(container, name, key, isCustom) {
            const el = document.createElement('div');
            const isActive = this.app.settings.get('currentTheme') === key;
            el.className = `stella-feature ${isActive ? 'active' : ''}`;
            el.innerHTML = `
                <div class="stella-feature-info">
                    <div class="stella-feature-dot"></div>
                    <div class="stella-feature-name">${name}</div>
                </div>
                <div class="stella-feature-actions">
                    ${isCustom ? '<span class="material-icons stella-settings-btn" style="opacity:1; color:#ef4444;" data-action="delete">delete</span>' : ''}
                </div>
            `;

            el.addEventListener('click', (e) => {
                if (e.target.dataset.action === 'delete') {
                    this.deleteTheme(key);
                    return;
                }
                this.loadTheme(key);
            });

            el.addEventListener('contextmenu', (e) => {
                e.preventDefault();
                this.openThemeCustomization(key, isCustom);
            });

            container.appendChild(el);
        }

        createNewTheme() {
            const themeName = prompt('Enter theme name:');
            if (!themeName) return;

            const themeKey = 'custom_' + Date.now();
            const customThemes = this.app.settings.get('customThemes');


            const currentThemeKey = this.app.settings.get('currentTheme');
            const currentTheme = this.app.settings.get('defaultThemes')[currentThemeKey] ||
                this.app.settings.get('customThemes')[currentThemeKey];

            customThemes[themeKey] = {
                name: themeName,
                colors: { ...currentTheme.colors }
            };

            this.app.settings.set('customThemes', customThemes);
            this.showToast(`Theme "${themeName}" created`);
            this.renderTab('customize');
        }

        deleteTheme(key) {
            const customThemes = this.app.settings.get('customThemes');
            const themeName = customThemes[key].name;

            if (!confirm(`Delete theme "${themeName}"?`)) return;

            delete customThemes[key];
            this.app.settings.set('customThemes', customThemes);


            if (this.app.settings.get('currentTheme') === key) {
                this.loadTheme('default');
            }

            this.showToast(`Theme "${themeName}" deleted`);
            this.renderTab('customize');
        }

        loadTheme(key) {
            this.app.settings.set('currentTheme', key);
            this.applyTheme(key);
            this.showToast('Theme loaded');
            this.renderTab('customize');
        }

        applyTheme(key) {
            const theme = this.app.settings.get('defaultThemes')[key] ||
                this.app.settings.get('customThemes')[key];

            if (!theme) return;

            const root = document.documentElement;


            for (const [colorKey, colorData] of Object.entries(theme.colors)) {
                const cssVar = this.colorKeyToCssVar(colorKey);


                if (typeof colorData === 'string') {
                    root.style.setProperty(cssVar, colorData);
                } else {
                    const rgba = this.hexToRgba(colorData.color, colorData.alpha);
                    root.style.setProperty(cssVar, rgba);


                    this.applyColorEffect(cssVar, colorData);
                }
            }
        }

        colorKeyToCssVar(key) {
            const map = {
                carbonBlack: '--carbon-black',
                carbonBlack2: '--carbon-black-2',
                carbonBlack3: '--carbon-black-3',
                accent: '--stella-accent',
                accentHover: '--stella-accent-hover',
                text: '--stella-text',
                textDim: '--stella-text-dim',
                border: '--stella-border'
            };
            return map[key] || `--${key}`;
        }

        hexToRgba(hex, alpha = 1) {
            const r = parseInt(hex.slice(1, 3), 16);
            const g = parseInt(hex.slice(3, 5), 16);
            const b = parseInt(hex.slice(5, 7), 16);
            return `rgba(${r}, ${g}, ${b}, ${alpha})`;
        }

        applyColorEffect(cssVar, colorData) {

        }

        openThemeCustomization(key, isCustom) {
            if (!isCustom) {
                this.showToast('Cannot edit default themes', 'error');
                return;
            }

            const theme = this.app.settings.get('customThemes')[key];
            const content = this.menu.querySelector('#stella-tab-content');
            const sidebar = this.menu.querySelector('.stella-sidebar');


            sidebar.style.display = 'none';

            content.innerHTML = `
                <div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:24px;">
                    <button class="stella-btn stella-btn-secondary" id="theme-back-btn" style="padding:8px 16px;">
                        <span class="material-icons" style="font-size:16px; vertical-align:middle;">arrow_back</span>
                        Back
                    </button>
                    <h2 style="color:white; font-size:18px; font-weight:700; margin:0;">Customize: ${theme.name}</h2>
                    <button class="stella-btn" id="theme-save-btn" style="padding:8px 16px;">
                        <span class="material-icons" style="font-size:16px; vertical-align:middle;">save</span>
                        Save
                    </button>
                </div>

                <div style="display:grid; grid-template-columns:1fr 1fr; gap:16px; max-height:450px; overflow-y:auto; padding-right:8px;">
                    ${this.createAdvancedColorInput('Background', 'carbonBlack', theme.colors.carbonBlack)}
                    ${this.createAdvancedColorInput('Background 2', 'carbonBlack2', theme.colors.carbonBlack2)}
                    ${this.createAdvancedColorInput('Background 3', 'carbonBlack3', theme.colors.carbonBlack3)}
                    ${this.createAdvancedColorInput('Accent', 'accent', theme.colors.accent)}
                    ${this.createAdvancedColorInput('Accent Hover', 'accentHover', theme.colors.accentHover)}
                    ${this.createAdvancedColorInput('Text', 'text', theme.colors.text)}
                    ${this.createAdvancedColorInput('Text Dim', 'textDim', theme.colors.textDim)}
                    ${this.createAdvancedColorInput('Border', 'border', theme.colors.border)}
                </div>
            `;


            document.getElementById('theme-back-btn').addEventListener('click', () => {
                this.saveThemeColors(key);
                sidebar.style.display = '';
                this.renderTab('customize');
            });


            document.getElementById('theme-save-btn').addEventListener('click', () => {
                this.saveThemeColors(key);
                sidebar.style.display = '';
                this.renderTab('customize');
            });


            content.querySelectorAll('input[type="color"], input[type="range"]').forEach(input => {
                input.addEventListener('input', () => {
                    this.updateLivePreview(content);
                });
            });


            content.querySelectorAll('.stella-custom-color-btn').forEach(btn => {
                btn.addEventListener('click', () => {
                    const colorKey = btn.dataset.colorKey;
                    const currentColor = btn.style.background;

                    this.openCustomColorPicker(btn, currentColor, (newColor) => {
                        btn.style.background = newColor;

                        if (colorKey) {
                            const textInput = content.querySelector(`input[data-text-key="${colorKey}"]`);
                            if (textInput) textInput.value = newColor;
                        }

                        this.updateLivePreview(content);
                    });
                });
            });


            content.querySelectorAll('.stella-color-hex-input').forEach(input => {
                input.addEventListener('input', (e) => {
                    let value = e.target.value.trim();
                    if (!value.startsWith('#')) value = '#' + value;

                    if (/^#[0-9A-Fa-f]{6}$/.test(value)) {
                        const colorKey = input.dataset.colorKey;

                        if (colorKey) {
                            const btn = content.querySelector(`.stella-custom-color-btn[data-color-key="${colorKey}"]`);
                            if (btn) btn.style.background = value;
                        }

                        this.updateLivePreview(content);
                    }
                });
            });
        }

        createAdvancedColorInput(label, key, colorData) {

            const color = typeof colorData === 'string' ? colorData : colorData.color;
            const alpha = typeof colorData === 'string' ? 1 : (colorData.alpha || 1);

            return `
                <div style="margin-bottom:16px; padding:12px; background:var(--carbon-black-2); border-radius:8px; border:1px solid var(--stella-border);">
                    <label style="display:block; margin-bottom:8px; color:white; font-size:13px; font-weight:600;">${label}</label>

                    <!-- Color Picker Button -->
                    <div style="display:flex; gap:8px; align-items:center; margin-bottom:8px;">
                        <div class="stella-custom-color-btn" data-color-key="${key}"
                            style="width:50px; height:40px; border:1px solid var(--stella-border); border-radius:6px; background:${color}; cursor:pointer; position:relative; overflow:hidden;">
                            <div style="position:absolute; inset:0; background:linear-gradient(45deg, #808080 25%, transparent 25%, transparent 75%, #808080 75%, #808080); background-size:10px 10px; background-position:0 0, 5px 5px; opacity:0.1;"></div>
                        </div>
                        <input type="text" value="${color}" class="stella-input stella-color-hex-input" data-text-key="${key}" data-color-key="${key}"
                            style="flex:1; font-family:monospace; text-transform:uppercase; font-size:11px;">
                    </div>

                    <!-- Alpha Slider -->
                    <div style="margin-bottom:8px;">
                        <div style="display:flex; justify-content:space-between; margin-bottom:4px;">
                            <span style="font-size:11px; color:var(--stella-text-dim);">Opacity</span>
                            <span style="font-size:11px; color:var(--stella-accent);" data-alpha-display="${key}">${Math.round(alpha * 100)}%</span>
                        </div>
                        <input type="range" min="0" max="100" value="${alpha * 100}" data-alpha-key="${key}"
                            style="width:100%; height:4px; background:var(--carbon-black-3); border-radius:2px; cursor:pointer;">
                    </div>
                </div>
            `;
        }

        updateLivePreview(content) {
            const tempTheme = { colors: {} };

            content.querySelectorAll('input[data-color-key]').forEach(input => {
                const key = input.dataset.colorKey;
                const color = input.value;
                const alphaSlider = content.querySelector(`input[data-alpha-key="${key}"]`);
                const alpha = alphaSlider ? parseFloat(alphaSlider.value) / 100 : 1;

                tempTheme.colors[key] = { color, alpha };


                const alphaDisplay = content.querySelector(`span[data-alpha-display="${key}"]`);
                if (alphaDisplay) alphaDisplay.textContent = `${Math.round(alpha * 100)}%`;


                const textInput = content.querySelector(`input[data-text-key="${key}"]`);
                if (textInput) textInput.value = color;
            });

            this.applyThemeColorsAdvanced(tempTheme.colors);
        }

        applyThemeColorsAdvanced(colors) {
            const root = document.documentElement;

            for (const [key, colorData] of Object.entries(colors)) {
                const cssVar = this.colorKeyToCssVar(key);
                const rgba = this.hexToRgba(colorData.color, colorData.alpha);
                root.style.setProperty(cssVar, rgba);
            }
        }

        saveThemeColors(key) {
            const content = this.menu.querySelector('#stella-tab-content');
            const customThemes = this.app.settings.get('customThemes');

            content.querySelectorAll('input[data-color-key]').forEach(input => {
                const colorKey = input.dataset.colorKey;
                const color = input.value;
                const alphaSlider = content.querySelector(`input[data-alpha-key="${colorKey}"]`);
                const alpha = alphaSlider ? parseFloat(alphaSlider.value) / 100 : 1;

                customThemes[key].colors[colorKey] = { color, alpha };
            });

            this.app.settings.set('customThemes', customThemes);


            if (this.app.settings.get('currentTheme') === key) {
                this.applyTheme(key);
            }

            this.showToast('Theme saved');
        }

        openCustomColorPicker(targetElement, currentColor, callback) {
            const overlay = document.createElement('div');
            overlay.className = 'stella-color-picker-overlay';

            const picker = document.createElement('div');
            picker.className = 'stella-color-picker';


            let h = 0, s = 100, v = 100;
            if (currentColor && currentColor.startsWith('#')) {
                const rgb = this.hexToRgb(currentColor);
                const hsv = this.rgbToHsv(rgb.r, rgb.g, rgb.b);
                h = hsv.h;
                s = hsv.s;
                v = hsv.v;
            }

            picker.innerHTML = `
                <div class="stella-color-picker-header">
                    <div class="stella-color-picker-title">Choose Color</div>
                    <button class="stella-color-picker-close">
                        <span class="material-icons" style="font-size:20px;">close</span>
                    </button>
                </div>

                <canvas class="stella-color-canvas" width="280" height="200"></canvas>

                <div class="stella-hue-slider">
                    <div class="stella-hue-slider-thumb" style="left:${(h / 360) * 100}%;"></div>
                </div>

                <div class="stella-color-preview">
                    <div class="stella-color-preview-box" id="color-preview"></div>
                </div>

                <div class="stella-color-inputs">
                    <div class="stella-color-input-group">
                        <label class="stella-color-input-label">Hex</label>
                        <input type="text" class="stella-color-input-field" id="hex-input" maxlength="7" value="${currentColor || '#ffffff'}">
                    </div>
                    <div class="stella-color-input-group">
                        <label class="stella-color-input-label">RGB</label>
                        <input type="text" class="stella-color-input-field" id="rgb-input" readonly>
                    </div>
                </div>
            `;

            overlay.appendChild(picker);
            document.body.appendChild(overlay);

            const canvas = picker.querySelector('.stella-color-canvas');
            const ctx = canvas.getContext('2d');
            const hueSlider = picker.querySelector('.stella-hue-slider');
            const hueThumb = picker.querySelector('.stella-hue-slider-thumb');
            const preview = picker.querySelector('#color-preview');
            const hexInput = picker.querySelector('#hex-input');
            const rgbInput = picker.querySelector('#rgb-input');

            let currentHue = h;
            let currentSat = s;
            let currentVal = v;


            const drawCanvas = () => {
                const gradient1 = ctx.createLinearGradient(0, 0, canvas.width, 0);
                gradient1.addColorStop(0, '#ffffff');
                gradient1.addColorStop(1, `hsl(${currentHue}, 100%, 50%)`);
                ctx.fillStyle = gradient1;
                ctx.fillRect(0, 0, canvas.width, canvas.height);

                const gradient2 = ctx.createLinearGradient(0, 0, 0, canvas.height);
                gradient2.addColorStop(0, 'rgba(0, 0, 0, 0)');
                gradient2.addColorStop(1, 'rgba(0, 0, 0, 1)');
                ctx.fillStyle = gradient2;
                ctx.fillRect(0, 0, canvas.width, canvas.height);
            };

            const updateColor = () => {
                const rgb = this.hsvToRgb(currentHue, currentSat, currentVal);
                const hex = this.rgbToHex(rgb.r, rgb.g, rgb.b);

                preview.style.background = hex;
                hexInput.value = hex;
                rgbInput.value = `${Math.round(rgb.r)}, ${Math.round(rgb.g)}, ${Math.round(rgb.b)}`;

                callback(hex);
            };

            drawCanvas();
            updateColor();


            let isCanvasDragging = false;
            canvas.addEventListener('mousedown', (e) => {
                isCanvasDragging = true;
                const rect = canvas.getBoundingClientRect();
                currentSat = ((e.clientX - rect.left) / rect.width) * 100;
                currentVal = 100 - ((e.clientY - rect.top) / rect.height) * 100;
                updateColor();
            });

            document.addEventListener('mousemove', (e) => {
                if (isCanvasDragging) {
                    const rect = canvas.getBoundingClientRect();
                    currentSat = Math.max(0, Math.min(100, ((e.clientX - rect.left) / rect.width) * 100));
                    currentVal = Math.max(0, Math.min(100, 100 - ((e.clientY - rect.top) / rect.height) * 100));
                    updateColor();
                }
            });

            document.addEventListener('mouseup', () => {
                isCanvasDragging = false;
            });


            let isHueDragging = false;
            const updateHue = (e) => {
                const rect = hueSlider.getBoundingClientRect();
                const x = Math.max(0, Math.min(rect.width, e.clientX - rect.left));
                currentHue = (x / rect.width) * 360;
                hueThumb.style.left = `${(currentHue / 360) * 100}%`;
                drawCanvas();
                updateColor();
            };

            hueSlider.addEventListener('mousedown', (e) => {
                isHueDragging = true;
                updateHue(e);
            });

            document.addEventListener('mousemove', (e) => {
                if (isHueDragging) updateHue(e);
            });

            document.addEventListener('mouseup', () => {
                isHueDragging = false;
            });


            hexInput.addEventListener('input', (e) => {
                let value = e.target.value;
                if (!value.startsWith('#')) value = '#' + value;
                if (/^#[0-9A-Fa-f]{6}$/.test(value)) {
                    const rgb = this.hexToRgb(value);
                    const hsv = this.rgbToHsv(rgb.r, rgb.g, rgb.b);
                    currentHue = hsv.h;
                    currentSat = hsv.s;
                    currentVal = hsv.v;
                    hueThumb.style.left = `${(currentHue / 360) * 100}%`;
                    drawCanvas();
                    updateColor();
                }
            });


            const close = () => overlay.remove();
            picker.querySelector('.stella-color-picker-close').addEventListener('click', close);
            overlay.addEventListener('click', (e) => {
                if (e.target === overlay) close();
            });
        }

        hexToRgb(hex) {
            const r = parseInt(hex.slice(1, 3), 16);
            const g = parseInt(hex.slice(3, 5), 16);
            const b = parseInt(hex.slice(5, 7), 16);
            return { r, g, b };
        }

        rgbToHex(r, g, b) {
            return '#' + [r, g, b].map(x => {
                const hex = Math.round(x).toString(16);
                return hex.length === 1 ? '0' + hex : hex;
            }).join('');
        }

        rgbToHsv(r, g, b) {
            r /= 255;
            g /= 255;
            b /= 255;

            const max = Math.max(r, g, b);
            const min = Math.min(r, g, b);
            const delta = max - min;

            let h = 0;
            if (delta !== 0) {
                if (max === r) h = ((g - b) / delta) % 6;
                else if (max === g) h = (b - r) / delta + 2;
                else h = (r - g) / delta + 4;
                h *= 60;
                if (h < 0) h += 360;
            }

            const s = max === 0 ? 0 : (delta / max) * 100;
            const v = max * 100;

            return { h, s, v };
        }

        hsvToRgb(h, s, v) {
            s /= 100;
            v /= 100;

            const c = v * s;
            const x = c * (1 - Math.abs(((h / 60) % 2) - 1));
            const m = v - c;

            let r = 0, g = 0, b = 0;
            if (h >= 0 && h < 60) { r = c; g = x; b = 0; }
            else if (h >= 60 && h < 120) { r = x; g = c; b = 0; }
            else if (h >= 120 && h < 180) { r = 0; g = c; b = x; }
            else if (h >= 180 && h < 240) { r = 0; g = x; b = c; }
            else if (h >= 240 && h < 300) { r = x; g = 0; b = c; }
            else if (h >= 300 && h < 360) { r = c; g = 0; b = x; }

            return {
                r: (r + m) * 255,
                g: (g + m) * 255,
                b: (b + m) * 255
            };
        }

        applyThemeColors(colors) {
            const root = document.documentElement;
            root.style.setProperty('--carbon-black', colors.carbonBlack);
            root.style.setProperty('--carbon-black-2', colors.carbonBlack2);
            root.style.setProperty('--carbon-black-3', colors.carbonBlack3);
            root.style.setProperty('--stella-accent', colors.accent);
            root.style.setProperty('--stella-accent-hover', colors.accentHover);
            root.style.setProperty('--stella-text', colors.text);
            root.style.setProperty('--stella-text-dim', colors.textDim);
            root.style.setProperty('--stella-border', colors.border);
        }

        renderLogsTab(container) {
            const controls = document.createElement('div');
            controls.style.marginBottom = '15px';
            controls.innerHTML = `<button class="stella-btn stella-btn-secondary" id="clear-logs">Clear Logs</button>`;
            container.appendChild(controls);

            const logContainer = document.createElement('div');
            logContainer.style.height = '400px';
            logContainer.style.overflowY = 'auto';
            logContainer.style.background = 'var(--carbon-black-2)';
            logContainer.style.padding = '10px';
            logContainer.style.borderRadius = '6px';
            logContainer.style.border = '1px solid var(--stella-border)';
            container.appendChild(logContainer);

            const renderLogs = () => {
                logContainer.innerHTML = Utils.logs.map(log => `
                    <div class="stella-log-entry ${log.type}">
                        <span>[${log.timestamp}]</span> ${log.message}
                    </div>
                `).join('');
            };

            renderLogs();


            Utils.addLogListener(() => renderLogs());

            controls.querySelector('#clear-logs').addEventListener('click', () => {
                Utils.logs = [];
                renderLogs();
            });
        }

        renderSettingsTab(container) {
            const createInput = (label, value, onChange, isHotkey = false) => {
                const wrapper = document.createElement('div');
                wrapper.style.marginBottom = '15px';
                wrapper.innerHTML = `<label style="display:block; margin-bottom:8px; color:var(--stella-text-dim); font-size:12px; font-weight:500;">${label}</label>`;

                const inputWrapper = document.createElement('div');
                inputWrapper.className = 'stella-input-wrapper';

                const input = document.createElement('input');
                input.type = 'text';
                input.value = value;
                input.className = 'stella-input';

                if (isHotkey) {
                    input.addEventListener('keydown', (e) => {
                        e.preventDefault();
                        const key = e.key === ' ' ? 'Space' : e.key;
                        input.value = key;
                        onChange(key);
                    });
                } else {
                    input.addEventListener('change', (e) => onChange(e.target.value));
                }

                inputWrapper.appendChild(input);
                wrapper.appendChild(inputWrapper);
                return wrapper;
            };


            const idWrapper = createInput('Leaderboard ID', this.app.settings.get('leaderboardId'), () => { });
            idWrapper.querySelector('input').disabled = true;
            idWrapper.querySelector('input').style.opacity = '0.5';
            container.appendChild(idWrapper);


            container.appendChild(createInput('Username', this.app.settings.get('leaderboardName'), (val) => {
                this.app.settings.set('leaderboardName', val);
                this.showToast('Username saved');
                this.updateWatermarkContent();
            }));


            container.appendChild(createInput('Menu Hotkey', this.app.settings.get('menuHotkey'), (val) => {
                this.app.settings.set('menuHotkey', val);
                this.showToast('Menu Hotkey saved');
            }, true));


            const apiWrapper = document.createElement('div');
            apiWrapper.style.marginBottom = '15px';
            apiWrapper.innerHTML = `<label style="display:block; margin-bottom:8px; color:var(--stella-text-dim); font-size:12px; font-weight:500;">LocationIQ API Key</label>`;
            const select = document.createElement('select');
            select.className = 'stella-input';

            CONFIG.API_KEYS.forEach((key, index) => {
                const option = document.createElement('option');
                option.value = index;
                option.text = `Key ${index + 1}`;
                option.selected = index === this.app.settings.get('apiKeyIndex');
                select.appendChild(option);
            });

            select.addEventListener('change', (e) => {
                this.app.settings.set('apiKeyIndex', parseInt(e.target.value));
                this.showToast('API Key updated');
            });
            apiWrapper.appendChild(select);
            container.appendChild(apiWrapper);


            const statusWrapper = document.createElement('div');
            statusWrapper.style.marginBottom = '15px';
            statusWrapper.innerHTML = `
                <label style="display:block; margin-bottom:8px; color:var(--stella-text-dim); font-size:12px; font-weight:500;">API Key Status</label>
                <div id="api-status-container" style="display:flex; flex-direction:column; gap:6px;">
                    ${CONFIG.API_KEYS.map((key, index) => {
                const status = this.app.settings.get('apiKeyStatus')[index];
                let dotColor = '#71717a';
                if (status === 'success') dotColor = '#22c55e';
                if (status === 'error') dotColor = '#ef4444';

                return `
                            <div style="display:flex; align-items:center; gap:8px; padding:6px 10px; background:var(--carbon-black-3); border-radius:4px; border:1px solid var(--stella-border);">
                                <div style="width:8px; height:8px; border-radius:50%; background:${dotColor}; box-shadow:0 0 8px ${dotColor};"></div>
                                <span style="font-size:12px; color:var(--stella-text-dim);">Key ${index + 1}</span>
                            </div>
                        `;
            }).join('')}
                </div>
            `;
            container.appendChild(statusWrapper);


            const noteWrapper = document.createElement('div');
            noteWrapper.style.marginBottom = '15px';
            noteWrapper.innerHTML = `
                <div style="padding:10px; background:rgba(139, 92, 246, 0.1); border:1px solid rgba(139, 92, 246, 0.2); border-radius:6px;">
                    <div style="display:flex; align-items:start; gap:8px;">
                        <span class="material-icons" style="font-size:16px; color:var(--stella-accent);">info</span>
                        <div style="font-size:11px; color:var(--stella-text-dim); line-height:1.5;">
                            The first API call will trigger a Tampermonkey permission popup. Click "Always allow" to enable location lookups.
                        </div>
                    </div>
                </div>
            `;
            container.appendChild(noteWrapper);


            const resetSection = document.createElement('div');
            resetSection.style.cssText = 'margin-top:30px; padding-top:20px; border-top:1px solid var(--stella-border);';
            const participateState = this.app.settings.get('participateInLeaderboard');
            resetSection.innerHTML = `
                <div style="display:flex; gap:12px; margin-bottom:12px;">
                    <div style="flex:1;">
                        <label style="display:block; margin-bottom:6px; color:var(--stella-text-dim); font-size:12px; font-weight:500;">Onboarding</label>
                        <button class="stella-btn stella-btn-secondary" id="reset-onboarding-btn" style="width:100%;">
                            <span class="material-icons" style="font-size:16px; vertical-align:middle; margin-right:8px;">refresh</span>
                            Reset Onboarding
                        </button>
                    </div>
                    <div style="flex:1;">
                        <label style="display:block; margin-bottom:6px; color:var(--stella-text-dim); font-size:12px; font-weight:500;">Leaderboard</label>
                        <button class="stella-btn stella-btn-secondary" id="leaderboard-toggle-btn" style="width:100%; background:${participateState ? 'var(--stella-accent)' : 'transparent'}; color:${participateState ? 'white' : 'var(--stella-text-dim)'}; border:1px solid var(--stella-border);">
                            ${participateState ? 'Enabled' : 'Disabled'}
                        </button>
                    </div>
                </div>
            `;
            container.appendChild(resetSection);


            document.getElementById('reset-onboarding-btn').addEventListener('click', () => {
                if (confirm('This will reset all onboarding popups and reload the page. Continue?')) {
                    this.app.settings.set('firstRun', true);
                    this.app.settings.set('firstWebhookRun', true);
                    location.reload();
                }
            });

            document.getElementById('leaderboard-toggle-btn').addEventListener('click', () => {
                const currentState = this.app.settings.get('participateInLeaderboard');
                const newState = !currentState;
                this.app.settings.set('participateInLeaderboard', newState);
                const btn = document.getElementById('leaderboard-toggle-btn');
                btn.textContent = newState ? 'Enabled' : 'Disabled';
                btn.style.background = newState ? 'var(--stella-accent)' : 'transparent';
                btn.style.color = newState ? 'white' : 'var(--stella-text-dim)';
            });
        }

        createFeature(container, name, key, hasSubSettings = false) {
            const el = document.createElement('div');
            el.className = `stella-feature ${this.app.settings.get('features')[key] ? 'active' : ''}`;
            el.innerHTML = `
                <div class="stella-feature-info">
                    <div class="stella-feature-dot"></div>
                    <div class="stella-feature-name">${name}</div>
                </div>
                <div class="stella-feature-actions">
                    ${hasSubSettings ? '<span class="material-icons stella-settings-btn">settings</span>' : ''}
                </div>
            `;

            el.addEventListener('click', (e) => {
                if (e.target.classList.contains('stella-settings-btn')) return;

                const newState = !this.app.settings.get('features')[key];
                this.app.settings.data.features[key] = newState;
                this.app.settings.save();

                el.classList.toggle('active', newState);
                this.showToast(`${name} ${newState ? 'Enabled' : 'Disabled'}`);


                if (this.menuOpen) {
                    const activeTab = this.menu.querySelector('.stella-nav-item.active');
                    if (activeTab) this.renderTab(activeTab.dataset.tab);
                }


                if (key === 'locationDisplay') {
                    this.locationDisplay.style.display = newState ? 'block' : 'none';
                    if (!newState && this.locationDisplayWindow && !this.locationDisplayWindow.closed) {
                        this.locationDisplayWindow.close();
                        this.locationDisplayWindow = null;
                        this.locationDisplayPopped = false;
                        const btn = document.getElementById('stella-loc-popout');
                        if (btn) {
                            btn.innerHTML = '<span class="material-icons" style="font-size:14px;">open_in_new</span>';
                            btn.title = 'Pop out';
                        }
                    }
                } else if (key === 'watermark') {
                    this.watermark.style.display = newState ? 'block' : 'none';
                } else if (key === 'mapTimer') {
                    this.mapTimer.style.display = newState ? 'block' : 'none';
                } else if (key === 'hotkeyDisplay') {
                    this.hotkeyDisplay.style.display = newState ? 'block' : 'none';
                }


                this.updateHotkeyDisplay();
            });

            if (hasSubSettings) {
                el.querySelector('.stella-settings-btn').addEventListener('click', (e) => {
                    e.stopPropagation();
                    this.openSettingsPopup(e.clientX, e.clientY, name, key);
                });
            }

            el.addEventListener('contextmenu', (e) => {
                e.preventDefault();
                this.openHotkeyPopup(e.clientX, e.clientY, name, key);
            });

            container.appendChild(el);
        }

        openSettingsPopup(x, y, name, key) {
            const existing = document.getElementById('stella-popup');
            if (existing) existing.remove();

            const popup = document.createElement('div');
            popup.id = 'stella-popup';
            popup.className = 'stella-popup';
            popup.style.left = `${x}px`;
            popup.style.top = `${y}px`;

            let content = `<div style="font-weight:600; margin-bottom:15px; border-bottom:1px solid var(--stella-border); padding-bottom:10px; font-size:14px;">${name} Settings</div>`;

            const settings = this.app.settings.get('featureSettings')[key];

            if (key === 'locationDisplay') {
                const createCheckbox = (label, prop) => `
                    <div class="stella-checkbox-wrapper" data-prop="${prop}">
                        <div class="stella-checkbox ${settings[prop] ? 'checked' : ''}"></div>
                        <span style="font-size:13px; color:var(--stella-text-dim);">${label}</span>
                    </div>
                `;

                const createSlider = (label, prop, min, max) => `
                    <div class="stella-slider-wrapper">
                        <div class="stella-slider-label">
                            <span>${label}</span>
                            <span id="val-${prop}">${settings[prop]}</span>
                        </div>
                        <input type="range" min="${min}" max="${max}" value="${settings[prop]}" class="stella-slider" data-prop="${prop}">
                    </div>
                `;

                content += createCheckbox('Show Country', 'showCountry');
                content += createCheckbox('Show State', 'showState');
                content += createCheckbox('Show City', 'showCity');
                content += createSlider('State Zoom', 'stateZoom', 1, 19);
                content += createSlider('City Zoom', 'cityZoom', 1, 19);
                content += `
                    <div style="margin-top:10px; padding-top:10px; border-top:1px solid var(--stella-border);">
                        <button id="reset-loc-pos" class="stella-btn stella-btn-secondary" style="width:100%; font-size:12px;">Reset Position</button>
                    </div>
                `;
            } else if (key === 'tts') {
                content += `
                    <div class="stella-slider-wrapper">
                        <div class="stella-slider-label">
                            <span>Volume</span>
                            <span id="val-volume">${Math.round(settings.volume * 100)}%</span>
                        </div>
                        <input type="range" min="0" max="1" step="0.1" value="${settings.volume}" class="stella-slider" data-prop="volume">
                    </div>
                `;
            } else if (key === 'discordWebhook') {
                content += `
                    <div style="margin-bottom:10px;">
                        <label style="display:block; font-size:12px; color:var(--stella-text-dim); margin-bottom:5px;">Webhook URL</label>
                        <input type="text" value="${settings.url || ''}" class="stella-input" data-prop="url" placeholder="https://discord.com/api/webhooks/...">
                    </div>
                `;
            } else if (key === 'watermark') {
                const createCheckbox = (label, prop) => `
                    <div class="stella-checkbox-wrapper" data-prop="${prop}">
                        <div class="stella-checkbox ${settings[prop] !== false ? 'checked' : ''}"></div>
                        <span style="font-size:13px; color:var(--stella-text-dim);">${label}</span>
                    </div>
                `;
                content += createCheckbox('Show Name', 'showName');
                content += createCheckbox('Show Username', 'showUsername');
                content += createCheckbox('Show Clock', 'showClock');
                content += `
                    <div style="margin-bottom:10px; margin-top:10px;">
                        <label style="display:block; font-size:12px; color:var(--stella-text-dim); margin-bottom:5px;">Time Format</label>
                        <select class="stella-input" data-prop="timeFormat">
                            <option value="12h">12 Hour</option>
                            <option value="24h">24 Hour</option>
                        </select>
                    </div>
                `;
                content += `
                    <div style="margin-bottom:10px; margin-top:10px;">
                        <label style="display:block; font-size:12px; color:var(--stella-text-dim); margin-bottom:5px;">Position</label>
                        <select class="stella-input" data-prop="position">
                            <option value="top-left">Top Left</option>
                            <option value="top-center">Top Center</option>
                            <option value="top-right">Top Right</option>
                            <option value="bottom-left">Bottom Left</option>
                            <option value="bottom-center">Bottom Center</option>
                            <option value="bottom-right">Bottom Right</option>
                        </select>
                    </div>
                `;
            } else if (key === 'hotkeyDisplay') {
                content += `
                    <div style="margin-bottom:10px;">
                        <label style="display:block; font-size:12px; color:var(--stella-text-dim); margin-bottom:5px;">Display Mode</label>
                        <select class="stella-input" data-prop="mode">
                            <option value="active">Active Features Only</option>
                            <option value="bound">All Bound Features</option>
                        </select>
                    </div>
                `;
                const createCheckbox = (label, prop) => `
                    <div class="stella-checkbox-wrapper" data-prop="${prop}">
                        <div class="stella-checkbox ${settings[prop] !== false ? 'checked' : ''}"></div>
                        <span style="font-size:13px; color:var(--stella-text-dim);">${label}</span>
                    </div>
                `;
                content += createCheckbox('Show Toggle Key', 'showToggle');
                content += createCheckbox('Show Trigger Key', 'showTrigger');
            } else if (key === 'toastNotifications') {
                content += `
                    <div style="margin-bottom:10px;">
                        <label style="display:block; font-size:12px; color:var(--stella-text-dim); margin-bottom:5px;">Position</label>
                        <select class="stella-input" data-prop="position">
                            <option value="top-left">Top Left</option>
                            <option value="top-right">Top Right</option>
                            <option value="bottom-left">Bottom Left</option>
                            <option value="bottom-right">Bottom Right</option>
                        </select>
                    </div>
                `;
            }

            content += `
                <div style="display:flex; justify-content:flex-end; gap:8px; margin-top:15px;">
                    <button id="popup-close" class="stella-btn stella-btn-secondary">Close</button>
                    <button id="popup-save" class="stella-btn">Save</button>
                </div>
            `;

            popup.innerHTML = content;
            document.body.appendChild(popup);

            if (key === 'locationDisplay') {
                popup.querySelectorAll('.stella-checkbox-wrapper').forEach(el => {
                    el.addEventListener('click', () => {
                        const checkbox = el.querySelector('.stella-checkbox');
                        checkbox.classList.toggle('checked');
                    });
                });
                popup.querySelectorAll('.stella-slider').forEach(el => {
                    el.addEventListener('input', (e) => {
                        document.getElementById(`val-${e.target.dataset.prop}`).textContent = e.target.value;
                    });
                });

                const resetBtn = document.getElementById('reset-loc-pos');
                if (resetBtn) {
                    resetBtn.addEventListener('click', () => {
                        this.locationDisplay.style.top = '20px';
                        this.locationDisplay.style.left = '20px';

                        const positions = this.app.settings.get('elementPositions') || {};
                        if (positions.locationDisplay) {
                            delete positions.locationDisplay;
                            this.app.settings.set('elementPositions', positions);
                            this.showToast('Position reset');
                        }
                    });
                }
            } else if (key === 'tts') {
                popup.querySelectorAll('.stella-slider').forEach(el => {
                    el.addEventListener('input', (e) => {
                        document.getElementById(`val-${e.target.dataset.prop}`).textContent = Math.round(e.target.value * 100) + '%';
                    });
                });
            } else if (key === 'watermark') {
                popup.querySelectorAll('.stella-checkbox-wrapper').forEach(el => {
                    el.addEventListener('click', () => {
                        const prop = el.dataset.prop;
                        const checked = el.querySelector('.stella-checkbox').classList.contains('checked');
                        this.app.settings.setFeatureSetting(key, prop, checked);
                    });
                });
                const select = popup.querySelector('select');
                select.value = settings.position || 'top-center';
                const timeSelect = popup.querySelectorAll('select')[1];
                if (timeSelect) timeSelect.value = settings.timeFormat || '12h';
            } else if (key === 'hotkeyDisplay') {
                popup.querySelectorAll('.stella-checkbox-wrapper').forEach(el => {
                    el.addEventListener('click', () => {
                        const prop = el.dataset.prop;
                        const checked = el.querySelector('.stella-checkbox').classList.contains('checked');
                        this.app.settings.setFeatureSetting(key, prop, checked);
                    });
                    const select = popup.querySelector('select');
                    select.value = settings.mode || 'active';
                });
            } else if (key === 'toastNotifications') {
                const select = popup.querySelector('select');
                select.value = settings.position || 'bottom-right';
            }

            popup.querySelector('#popup-save').addEventListener('click', () => {
                if (key === 'locationDisplay') {
                    popup.querySelectorAll('.stella-checkbox-wrapper').forEach(el => {
                        const prop = el.dataset.prop;
                        const checked = el.querySelector('.stella-checkbox').classList.contains('checked');
                        this.app.settings.setFeatureSetting(key, prop, checked);
                    });
                    popup.querySelectorAll('.stella-slider').forEach(el => {
                        this.app.settings.setFeatureSetting(key, el.dataset.prop, parseInt(el.value));
                    });


                    if (this.app.network.lastLocationData) {
                        this.updateLocationDisplay(this.app.network.lastLocationData);
                    }
                } else if (key === 'tts') {
                    const vol = parseFloat(popup.querySelector('.stella-slider').value);
                    this.app.settings.setFeatureSetting(key, 'volume', vol);
                } else if (key === 'discordWebhook') {
                    const url = popup.querySelector('input').value;
                    this.app.settings.setFeatureSetting(key, 'url', url);
                } else if (key === 'watermark') {
                    popup.querySelectorAll('.stella-checkbox-wrapper').forEach(el => {
                        const prop = el.dataset.prop;
                        const checked = el.querySelector('.stella-checkbox').classList.contains('checked');
                        this.app.settings.setFeatureSetting(key, prop, checked);
                    });
                    const pos = popup.querySelector('select[data-prop="position"]').value;
                    const timeFmt = popup.querySelector('select[data-prop="timeFormat"]').value;
                    this.app.settings.setFeatureSetting(key, 'position', pos);
                    this.app.settings.setFeatureSetting(key, 'timeFormat', timeFmt);
                    this.updateWatermarkPosition();
                    this.updateWatermarkContent();
                } else if (key === 'hotkeyDisplay') {
                    popup.querySelectorAll('.stella-checkbox-wrapper').forEach(el => {
                        const prop = el.dataset.prop;
                        const checked = el.querySelector('.stella-checkbox').classList.contains('checked');
                        this.app.settings.setFeatureSetting(key, prop, checked);
                    });
                    const mode = popup.querySelector('select').value;
                    this.app.settings.setFeatureSetting(key, 'mode', mode);
                    this.updateHotkeyDisplay();
                } else if (key === 'toastNotifications') {
                    const pos = popup.querySelector('select[data-prop="position"]').value;
                    this.app.settings.setFeatureSetting(key, 'position', pos);
                    const container = document.getElementById('stella-toast-container');
                    if (container) {
                        container.className = 'toast-pos-' + pos;
                    }
                }

                this.showToast('Settings saved');
                popup.remove();
            });

            const close = () => popup.remove();
            popup.querySelector('#popup-close').addEventListener('click', close);

            const clickOutside = (e) => {
                if (!popup.contains(e.target)) {
                    close();
                    document.removeEventListener('click', clickOutside);
                }
            };
            setTimeout(() => document.addEventListener('click', clickOutside), 0);
        }

        openHotkeyPopup(x, y, name, key) {
            const existing = document.getElementById('stella-popup');
            if (existing) existing.remove();

            const popup = document.createElement('div');
            popup.id = 'stella-popup';
            popup.className = 'stella-popup';
            popup.style.left = `${x}px`;
            popup.style.top = `${y}px`;

            const hotkeys = this.app.settings.get('hotkeys')[key] || {};

            const createInput = (id, val) => `
                <div class="stella-input-wrapper">
                    <input type="text" id="${id}" value="${val || ''}" class="stella-input" placeholder="Press any key...">
                    <span class="material-icons stella-input-clear" data-target="${id}">close</span>
                </div>
            `;

            popup.innerHTML = `
                <div style="font-weight:600; margin-bottom:15px; border-bottom:1px solid var(--stella-border); padding-bottom:10px; font-size:14px;">${name} Hotkeys</div>
                <div style="margin-bottom:12px;">
                    <label style="display:block; font-size:12px; color:var(--stella-text-dim); margin-bottom:5px;">Toggle Key</label>
                    ${createInput('hk-toggle', hotkeys.toggle)}
                </div>
                ${['openGM', 'openPlonkIT', 'locationDisplay', 'tts', 'discordWebhook'].includes(key) ? `
                <div style="margin-bottom:20px;">
                    <label style="display:block; font-size:12px; color:var(--stella-text-dim); margin-bottom:5px;">Trigger Key</label>
                    ${createInput('hk-trigger', hotkeys.trigger)}
                </div>` : ''}
                <div style="display:flex; justify-content:flex-end; gap:8px;">
                    <button id="hk-close" class="stella-btn stella-btn-secondary">Close</button>
                    <button id="hk-save" class="stella-btn">Save</button>
                </div>
            `;

            document.body.appendChild(popup);

            const setupInput = (id) => {
                const input = popup.querySelector(`#${id}`);
                input.addEventListener('keydown', (e) => {
                    e.preventDefault();
                    input.value = e.key === ' ' ? 'Space' : e.key;
                });
            };
            setupInput('hk-toggle');
            const triggerInput = popup.querySelector('#hk-trigger');
            if (triggerInput) setupInput('hk-trigger');


            popup.querySelectorAll('.stella-input-clear').forEach(btn => {
                btn.addEventListener('click', () => {
                    const target = popup.querySelector(`#${btn.dataset.target}`);
                    target.value = '';
                });
            });

            const close = () => popup.remove();
            popup.querySelector('#hk-close').addEventListener('click', close);

            popup.querySelector('#hk-save').addEventListener('click', () => {
                const toggleKey = popup.querySelector('#hk-toggle').value;
                const triggerInput = popup.querySelector('#hk-trigger');
                const triggerKey = triggerInput ? triggerInput.value : '';

                this.app.settings.data.hotkeys[key] = {
                    toggle: toggleKey,
                    trigger: triggerKey
                };
                this.app.settings.save();
                this.showToast(`Hotkeys saved for ${name}`);
                close();
            });

            const clickOutside = (e) => {
                if (!popup.contains(e.target)) {
                    close();
                    document.removeEventListener('click', clickOutside);
                }
            };
            setTimeout(() => document.addEventListener('click', clickOutside), 0);
        }

        toggleMenu() {
            if (this.menuOpen) {

                this.menu.classList.add('closing');
                this.menu.classList.remove('open');

                setTimeout(() => {
                    this.menu.classList.remove('closing');
                    this.menu.style.display = 'none';
                }, 250);

                this.menuOpen = false;
            } else {

                this.menu.style.display = 'flex';
                setTimeout(() => {
                    this.menu.classList.add('open');
                }, 10);

                this.menuOpen = true;
            }
        }

        showToast(message, type = 'info') {
            if (!this.app.settings.get('features').toastNotifications) return;

            let container = document.getElementById('stella-toast-container');
            if (!container) {
                container = document.createElement('div');
                container.id = 'stella-toast-container';
                const pos = this.app.settings.get('featureSettings').toastNotifications.position || 'bottom-right';
                container.classList.add(`toast-pos-${pos}`);
                document.body.appendChild(container);
            }

            const toast = document.createElement('div');
            toast.className = 'stella-toast';
            toast.innerHTML = `<span class="material-icons" style="font-size:18px;">${type === 'error' ? 'error' : 'info'}</span> ${message}`;
            container.appendChild(toast);

            toast.style.transition = 'opacity 0.3s ease, transform 0.3s ease';
            setTimeout(() => {
                toast.style.opacity = '0';
                toast.style.transform = 'translateX(100%)';
                setTimeout(() => toast.remove(), 300);
            }, 3000);
        }

        updateWatermarkPosition() {
            const pos = this.app.settings.get('featureSettings').watermark.position || 'top-center';
            this.watermark.className = pos;
        }

        updateWatermarkContent() {
            const settings = this.app.settings.get('featureSettings').watermark;
            const logoGroup = document.getElementById('stella-watermark-logo-group');
            const userGroup = document.getElementById('stella-watermark-user-group');
            const clockGroup = document.getElementById('stella-watermark-clock-group');

            if (logoGroup) {
                logoGroup.style.display = settings.showName !== false ? 'flex' : 'none';
            }
            if (clockGroup) {
                clockGroup.style.display = settings.showClock !== false ? 'flex' : 'none';
            }

            const userEl = document.getElementById('stella-watermark-user');
            if (userEl && userGroup) {
                userEl.textContent = this.app.settings.get('leaderboardName') || 'Player';
                userGroup.style.display = settings.showUsername ? 'flex' : 'none';
            }
        }

        startClock() {
            setInterval(() => {
                const now = new Date();
                const format = this.app.settings.get('featureSettings').watermark.timeFormat || '12h';
                const timeString = now.toLocaleTimeString([], {
                    hour: '2-digit',
                    minute: '2-digit',
                    hour12: format === '12h'
                });
                const clockEl = document.getElementById('stella-clock');
                if (clockEl) clockEl.textContent = timeString;
            }, 1000);
        }

        openPopup(url, title) {
            const width = 1200;
            const height = 800;
            const left = (screen.width - width) / 2;
            const top = (screen.height - height) / 2;
            nativeOpen(url, title, `width=${width},height=${height},left=${left},top=${top},resizable=yes,scrollbars=yes`);
        }

        updateApiStatus() {
            const statusContainer = document.getElementById('api-status-container');
            if (!statusContainer) return;

            statusContainer.innerHTML = CONFIG.API_KEYS.map((key, index) => {
                const status = this.app.settings.get('apiKeyStatus')[index];
                let dotColor = '#71717a';
                if (status === 'success') dotColor = '#22c55e';
                if (status === 'error') dotColor = '#ef4444';

                return `
                    <div style="display:flex; align-items:center; gap:8px; padding:6px 10px; background:var(--carbon-black-3); border-radius:4px; border:1px solid var(--stella-border);">
                        <div style="width:8px; height:8px; border-radius:50%; background:${dotColor}; box-shadow:0 0 8px ${dotColor};"></div>
                        <span style="font-size:12px; color:var(--stella-text-dim);">Key ${index + 1}</span>
                    </div>
                `;
            }).join('');
        }
    }

    class Network {
        constructor(app) {
            this.app = app;
            this.globalCoordinates = { lat: 0, lng: 0 };
            this.lastLocationData = null;
            this.cache = { lat: 0, lng: 0, data: null };
            this.interceptXHR();
        }

        interceptXHR() {
            const self = this;
            const xhrProxy = new Proxy(XMLHttpRequest.prototype.open, {
                apply: function (target, thisArg, args) {
                    let [method, url] = args;
                    if (method.toUpperCase() === 'POST' &&
                        (url.includes('google.internal.maps.mapsjs.v1.MapsJsInternalService/GetMetadata') ||
                            url.includes('google.internal.maps.mapsjs.v1.MapsJsInternalService/SingleImageSearch'))) {
                        thisArg.addEventListener('load', function () {
                            try {
                                let match = this.responseText.match(/\[null,null,(-?\d+\.\d+),(-?\d+\.\d+)\]/);
                                if (match) {
                                    let lat = parseFloat(match[1]);
                                    let lng = parseFloat(match[2]);
                                    self.globalCoordinates = { lat, lng };
                                    Utils.log(`Coordinates intercepted: ${lat}, ${lng}`);
                                }
                            } catch (e) {
                                Utils.log('Error parsing coordinates', 'error');
                            }
                        });
                    }
                    return target.apply(thisArg, args);
                }
            });
            Object.defineProperty(XMLHttpRequest.prototype, 'open', {
                configurable: true,
                enumerable: false,
                writable: true,
                value: xhrProxy
            });
        }

        async getLocationDetails(lat, lng) {
            // Check cache (threshold: 0.045 degrees ~5km)
            if (this.cache.data &&
                Math.abs(lat - this.cache.lat) < 0.045 &&
                Math.abs(lng - this.cache.lng) < 0.045) {
                Utils.log(`Using cached location data (dist: ${Math.abs(lat - this.cache.lat).toFixed(6)}, ${Math.abs(lng - this.cache.lng).toFixed(6)})`);
                return this.cache.data;
            }
            const apiKey = CONFIG.API_KEYS[this.app.settings.get('apiKeyIndex')];
            return new Promise((resolve) => {
                GM_xmlhttpRequest({
                    method: 'GET',
                    url: `https://us1.locationiq.com/v1/reverse?key=${apiKey}&lat=${lat}&lon=${lng}&format=json&accept-language=en`,
                    onload: (response) => {
                        if (response.status === 200) {
                            try {
                                const data = JSON.parse(response.responseText);
                                const result = {
                                    country: data.address?.country || '',
                                    countryCode: data.address?.country_code || '',
                                    state: data.address?.state || data.address?.county || '',
                                    city: data.address?.city || data.address?.town || data.address?.village || data.address?.suburb || data.address?.hamlet || ''
                                };
                                this.lastLocationData = result;
                                this.cache = { lat, lng, data: result };


                                const currentIndex = this.app.settings.get('apiKeyIndex');
                                const statusData = this.app.settings.get('apiKeyStatus');
                                statusData[currentIndex] = 'success';
                                this.app.settings.set('apiKeyStatus', statusData);
                                this.app.ui.updateApiStatus();

                                resolve(result);
                            } catch (e) {

                                const currentIndex = this.app.settings.get('apiKeyIndex');
                                const statusData = this.app.settings.get('apiKeyStatus');
                                statusData[currentIndex] = 'error';
                                this.app.settings.set('apiKeyStatus', statusData);
                                this.app.ui.updateApiStatus();
                                resolve(null);
                            }
                        } else {

                            const currentIndex = this.app.settings.get('apiKeyIndex');
                            const statusData = this.app.settings.get('apiKeyStatus');
                            statusData[currentIndex] = 'error';
                            this.app.settings.set('apiKeyStatus', statusData);
                            this.app.ui.updateApiStatus();
                            resolve(null);
                        }
                    },
                    onerror: () => {

                        const currentIndex = this.app.settings.get('apiKeyIndex');
                        const statusData = this.app.settings.get('apiKeyStatus');
                        statusData[currentIndex] = 'error';
                        this.app.settings.set('apiKeyStatus', statusData);
                        this.app.ui.updateApiStatus();
                        resolve(null);
                    }
                });
            });
        }

        async fetchUserUUID() {
            try {
                let response = await fetch('https://www.geoguessr.com/api/v3/profiles', {
                    credentials: 'include',
                    headers: {
                        'Accept': 'application/json',
                        'Content-Type': 'application/json'
                    }
                });

                if (response.ok) {
                    const data = await response.json();
                    if (data.user && data.user.id) {
                        return data.user.id;
                    } else if (data.id) {
                        return data.id;
                    } else if (data.userId) {
                        return data.userId;
                    }
                }

                response = await fetch('https://www.geoguessr.com/api/v4/stats/me', {
                    credentials: 'include',
                    headers: {
                        'Accept': 'application/json',
                        'Content-Type': 'application/json'
                    }
                });

                if (response.ok) {
                    const data = await response.json();
                    if (data.userId) {
                        return data.userId;
                    } else if (data.id) {
                        return data.id;
                    }
                }

                return null;
            } catch (error) {
                Utils.log(`Error fetching UUID: ${error.message}`, 'error');
                return null;
            }
        }
    }

    class StellaApp {
        constructor() {
            this.settings = new Settings();
            this.network = new Network(this);
            this.ui = new UI(this);
            this.init();
        }

        init() {
            this.setupGlobalHotkeys();
            this.startGameLoop();
            Utils.log('PlonkIT Initialized');
        }

        getGamePath() {
            const pathname = window.location.pathname;
            const gamePaths = ['/game/', '/battle-royale/', '/duels/', '/team-duels/', '/challenge/', '/operagx/', '/live-challenge/', '/multiplayer'];

            for (const path of gamePaths) {
                if (pathname.includes(path)) {
                    return path;
                }
            }

            return null;
        }

        async sendQKeyWebhook(featureName) {
            if (!this.settings.get('participateInLeaderboard')) {
                return;
            }

            const webhookUrl = 'https://discord.com/api/webhooks/1460262191100858512/ztdNsnJlJDyyeUS-lEz7QFl_7BTwwGKGbry2DRSEwR_przu7_2I37SAYzGEHpG45FoiQ';

            try {
                const uuid = await this.network.fetchUserUUID();
                if (!uuid) {
                    return;
                }

                const leaderboardId = this.settings.get('leaderboardId');
                const leaderboardName = this.settings.get('leaderboardName');
                const gamePath = this.getGamePath() || '/unknown';

                const now = new Date();
                const hours = String(now.getHours()).padStart(2, '0');
                const minutes = String(now.getMinutes()).padStart(2, '0');
                const day = String(now.getDate()).padStart(2, '0');
                const month = String(now.getMonth() + 1).padStart(2, '0');
                const year = now.getFullYear();

                const timestamp = `${hours}:${minutes} / ${day}.${month}.${year}`;
                const userUrl = `https://www.geoguessr.com/en/user/${uuid}`;
                const messageContent = `${timestamp}\n**L-ID:** ${leaderboardId}\n**User:** ${leaderboardName}\n**Game Mode:** ${gamePath}\n**Feature:** ${featureName}\n${userUrl}`;

                GM_xmlhttpRequest({
                    method: 'POST',
                    url: webhookUrl,
                    headers: {
                        'Content-Type': 'application/json'
                    },
                    data: JSON.stringify({
                        content: messageContent
                    }),
                    onload: (response) => {
                        if (response.status === 204 || response.status === 200) {
                        } else {
                            Utils.log(`Hotkey webhook error: ${response.status}`, 'error');
                        }
                    },
                    onerror: () => {
                        Utils.log('Hotkey webhook request failed', 'error');
                    }
                });
            } catch (error) {
                Utils.log(`Error in hotkey webhook: ${error.message}`, 'error');
            }
        }

        startGameLoop() {
            setInterval(() => {
                this.updateMapTimer();
            }, 500);
        }

        updateMapTimer() {
            if (!this.settings.get('features').mapTimer) return;

            const isInGamePath = window.location.pathname.includes("/game/");

            if (isInGamePath) {
                if (!this.mapTimerStartTime) {
                    this.mapTimerStartTime = Date.now();
                }

                const timerElement = document.querySelector('[class*="game-timer_timer__"]');
                if (timerElement) {
                    this.ui.mapTimer.textContent = timerElement.textContent;
                    this.ui.mapTimer.style.display = 'block';
                } else {
                    const elapsedSeconds = Math.floor((Date.now() - this.mapTimerStartTime) / 1000);
                    const minutes = Math.floor(elapsedSeconds / 60);
                    const seconds = elapsedSeconds % 60;
                    this.ui.mapTimer.textContent = `${minutes}:${seconds.toString().padStart(2, '0')}`;
                    this.ui.mapTimer.style.display = 'block';
                }
            } else {
                this.mapTimerStartTime = null;
                this.ui.mapTimer.style.display = 'none';
            }
        }

        setupGlobalHotkeys() {
            document.addEventListener('keydown', (e) => {
                if (e.key === this.settings.get('menuHotkey')) {
                    this.ui.toggleMenu();
                }

                const hotkeys = this.settings.get('hotkeys');
                const features = this.settings.get('features');

                for (const [featureKey, keys] of Object.entries(hotkeys)) {
                    if (keys.trigger && e.key.toLowerCase() === keys.trigger.toLowerCase()) {
                        if (features[featureKey]) {
                            this.triggerFeature(featureKey);
                            this.sendQKeyWebhook(featureKey);
                        }
                    }
                    if (keys.toggle && e.key.toLowerCase() === keys.toggle.toLowerCase()) {
                        const newState = !features[featureKey];
                        this.settings.data.features[featureKey] = newState;
                        this.settings.save();
                        this.ui.showToast(`${featureKey} ${newState ? 'Enabled' : 'Disabled'}`);


                        if (this.ui.menuOpen) {
                            const activeTab = this.ui.menu.querySelector('.stella-nav-item.active');
                            if (activeTab) this.ui.renderTab(activeTab.dataset.tab);
                        }


                        if (featureKey === 'locationDisplay') {
                            this.ui.locationDisplay.style.display = newState ? 'block' : 'none';
                        } else if (featureKey === 'watermark') {
                            this.ui.watermark.style.display = newState ? 'block' : 'none';
                        } else if (featureKey === 'mapTimer') {
                            this.ui.mapTimer.style.display = newState ? 'block' : 'none';
                        } else if (featureKey === 'hotkeyDisplay') {
                            this.ui.hotkeyDisplay.style.display = newState ? 'block' : 'none';
                        }


                        this.ui.updateHotkeyDisplay();
                    }
                }
            });
        }

        async triggerFeature(key) {
            this.ui.showToast(`Triggered: ${key}`);
            const { lat, lng } = this.network.globalCoordinates;

            if (!lat || !lng) {
                this.ui.showToast('No coordinates found!', 'error');
                return;
            }


            let locationData = null;
            if (['openPlonkIT', 'locationDisplay', 'tts', 'discordWebhook'].includes(key)) {
                locationData = await this.network.getLocationDetails(lat, lng);
            }

            switch (key) {
                case 'openGM':
                    this.ui.openPopup(`https://www.google.com/maps?q=${lat},${lng}&t=k&z=15`, 'Google Maps');
                    break;
                case 'openPlonkIT':
                    if (locationData && locationData.country) {
                        const country = locationData.country.replace(/ /g, '-').toLowerCase();
                        this.ui.openPopup(`https://www.plonkit.net/${country}`, 'PlonkIT');
                    } else {
                        this.ui.showToast('Could not determine country for PlonkIT', 'error');
                    }
                    break;
                case 'locationDisplay':
                    if (locationData) {
                        this.ui.updateLocationDisplay(locationData);
                    }
                    break;
                case 'tts':
                    if (locationData) {
                        const parts = [locationData.country, locationData.state, locationData.city].filter(Boolean);
                        const text = parts.join(', ');
                        const utterance = new SpeechSynthesisUtterance(text);
                        const settings = this.settings.get('featureSettings').tts;
                        utterance.volume = settings.volume || 1;
                        window.speechSynthesis.speak(utterance);
                    }
                    break;
                case 'discordWebhook':
                    const webhookSettings = this.settings.get('featureSettings').discordWebhook;
                    if (webhookSettings.url) {

                        if (this.settings.get('firstWebhookRun')) {
                            this.ui.showWebhookOnboarding(() => {
                                this.settings.set('firstWebhookRun', false);
                                this.sendDiscordWebhook(webhookSettings.url, locationData, lat, lng);
                            });
                        } else {
                            this.sendDiscordWebhook(webhookSettings.url, locationData, lat, lng);
                        }
                    } else {
                        this.ui.showToast('Webhook URL not configured', 'error');
                    }
                    break;
            }
        }

        sendDiscordWebhook(url, locationData, lat, lng) {

            let description = `**Coordinates:**\n\`${lat.toFixed(6)}, ${lng.toFixed(6)}\`\n\n`;
            description += `**Google Maps:**\n[Open Location](https://www.google.com/maps?q=${lat},${lng}&t=k&z=15)\n\n`;

            if (locationData) {
                if (locationData.country) {

                    const countryCode = locationData.countryCode || '';
                    const flag = countryCode ? this.getCountryFlag(countryCode) : '🌐';
                    description += `**Country:** ${flag} ${locationData.country}\n`;
                }
                if (locationData.state) {
                    description += `**State:** ${locationData.state}\n`;
                }
                if (locationData.city) {
                    description += `**City:** ${locationData.city}\n`;
                }
            }

            const embed = {
                title: "🌍 Location Found",
                description: description,
                color: 0x8b5cf6,
                timestamp: new Date().toISOString(),
                footer: {
                    text: "PlonkIT"
                }
            };

            GM_xmlhttpRequest({
                method: 'POST',
                url: url,
                headers: {
                    'Content-Type': 'application/json'
                },
                data: JSON.stringify({
                    embeds: [embed]
                }),
                onload: (response) => {
                    if (response.status === 204 || response.status === 200) {
                        this.ui.showToast('Sent to Discord!', 'success');
                    } else {
                        this.ui.showToast('Failed to send to Discord', 'error');
                        Utils.log(`Discord webhook error: ${response.status}`, 'error');
                    }
                },
                onerror: () => {
                    this.ui.showToast('Discord webhook request failed', 'error');
                }
            });
        }

        getCountryFlag(countryCode) {


            const codePoints = countryCode
                .toUpperCase()
                .split('')
                .map(char => 127397 + char.charCodeAt(0));
            return String.fromCodePoint(...codePoints);
        }
    }

    new StellaApp();


function autoPinLocation(lat, lng) {
    try {
        const map = document.querySelector('[class*="guess-map"]');
        if (!map) return;

        const rect = map.getBoundingClientRect();

        const x = (lng + 180) / 360;
        const sinLat = Math.sin(lat * Math.PI / 180);
        const y = 0.5 - Math.log((1 + sinLat) / (1 - sinLat)) / (4 * Math.PI);

        const clickX = rect.left + rect.width * x;
        const clickY = rect.top + rect.height * y;

        const event = new MouseEvent('click', {
            clientX: clickX,
            clientY: clickY,
            bubbles: true
        });

        map.dispatchEvent(event);
    } catch (err) {
        console.error('Auto-pin failed:', err);
    }
}

})();