Kogama Plus

3 IN 1: Theme Editor + Font Editor + Player Count

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey, Greasemonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да инсталирате разширение, като например Tampermonkey .

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Userscripts.

За да инсталирате скрипта, трябва да инсталирате разширение като Tampermonkey.

За да инсталирате този скрипт, трябва да имате инсталиран скриптов мениджър.

(Вече имам скриптов мениджър, искам да го инсталирам!)

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

(Вече имам инсталиран мениджър на стиловете, искам да го инсталирам!)

// ==UserScript==
// @name         Kogama Plus
// @namespace    http://tampermonkey.net/
// @version      1.01
// @author       BlueEL
// @icon         https://www.kogama.com/favicon.ico
// @description  3 IN 1: Theme Editor + Font Editor + Player Count
// @match        https://www.kogama.com/*
// @grant        none
// @license Copyright (c) 2026 BlueEL - All rights reserved.
// ==/UserScript==

(() => {
    'use strict';

    const APP_CONFIG = {
        NAV_OPACITY: 0.1,
        PANEL_ID: 'kogama-theme-panel',
        PLAYER_COUNT_ID: 'kogama-player-count',
        ROOT_SELECTOR: '#root-page-mobile',
        CONTENT_CONTAINER_SELECTOR: '#content-container',
        GAME_CARD_SELECTOR: '.gmqKr',
        PLAYER_VALUE_SELECTOR: '._1ZdZA',
        ADS_LINK_SELECTOR: '.MuiCollapse-root a[href*="subscription"]',
        PANEL_TOP_OFFSET: 20,
        PANEL_RIGHT_OFFSET: 20,
        PANEL_WIDTH: 320,
        OBSERVER_DEBOUNCE_DELAY: 250,
        INITIAL_REFRESH_DELAY: 800,
        STATS_REFRESH_INTERVAL: 2000,
        FONT_RESULTS_LIMIT: 10,
        PANEL_SHORTCUT_KEY: 'S',
        DEFAULT_THEME: {
            background: '#0e0a1f',
            gradientStart: '#0e0a1f',
            gradientEnd: '#003138',
            textColor: '#ffffff',
            fontFamily: 'Inter'
        },
        DEFAULT_PANEL_STATE: {
            isOpen: true
        },
        FONT_FALLBACK: 'sans-serif',
FONT_OPTIONS: [
    'Inter',
    'Roboto',
    'Open Sans',
    'Lato',
    'Montserrat',
    'Poppins',
    'Raleway',
    'Ubuntu',
    'Oswald',
    'Merriweather',
    'Playfair Display',
    'Nunito',
    'Rubik',
    'Work Sans',
    'Fira Sans',
    'Quicksand',
    'Inconsolata',
    'DM Sans',
    'Manrope',
    'Space Grotesk',
    'Plus Jakarta Sans',
    'Source Sans 3',
    'Outfit',
    'Urbanist',
    'Mulish',

    'Bebas Neue',
    'Anton',
    'Archivo',
    'Barlow',
    'Barlow Condensed',
    'Barlow Semi Condensed',
    'Cabin',
    'Cairo',
    'Comfortaa',
    'Cormorant',
    'Cormorant Garamond',
    'Dancing Script',
    'Exo 2',
    'Hind',
    'Hind Madurai',
    'IBM Plex Sans',
    'IBM Plex Serif',
    'Josefin Sans',
    'Karla',
    'Libre Baskerville',
    'Libre Franklin',
    'M PLUS Rounded 1c',
    'Noto Sans',
    'Noto Serif',
    'Orbitron',
    'PT Sans',
    'PT Serif',
    'Rajdhani',
    'Red Hat Display',
    'Red Hat Text',
    'Sora',
    'Teko',
    'Titillium Web',
    'Varela Round',
    'Zilla Slab',
    'Asap',
    'Assistant',
    'Baloo 2',
    'Chivo',
    'Encode Sans',
    'Heebo',
    'Jost',
    'Kanit',
    'Lexend',
    'Maven Pro',
    'Public Sans',
    'Sen',
    'Syne',
    'Yantramanav'
],
        STORAGE_KEY: 'kogama-theme-editor-state',
        SELECTORS: {
            toolbar: '.MuiToolbar-root',
            stackHeader: '.MuiStack-root._2JO9f',
            sectionShell: '._3TORb',
            footer: 'footer',
            heroSurface: '._1q4mD',
            heroInnerSurface: '._1q4mD ._1sUGu ._1u05O',
            gameTitle: '.gmqKr ._1ZdZA',
            gameStatsItems: 'ul li'
        },
        ROUTES: {
            game: 'https://www.kogama.com/games/',
            build: 'https://www.kogama.com/build/'
        }
    };

    class DomCache {
        constructor() {
            this.documentHead = document.head;
            this.documentBody = document.body;
        }

        query(selector, root = document) {
            return root.querySelector(selector);
        }

        queryAll(selector, root = document) {
            return [...root.querySelectorAll(selector)];
        }

        getRootPage() {
            return this.query(APP_CONFIG.ROOT_SELECTOR);
        }

        getContentContainer() {
            return this.query(APP_CONFIG.CONTENT_CONTAINER_SELECTOR);
        }

        getGameCards() {
            return this.queryAll(APP_CONFIG.GAME_CARD_SELECTOR);
        }

        getPlayerValueElements() {
            return this.queryAll(APP_CONFIG.PLAYER_VALUE_SELECTOR);
        }

        getAdsLinks() {
            return this.queryAll(APP_CONFIG.ADS_LINK_SELECTOR);
        }
    }

    class StorageService {
        loadRawState() {
            try {
                const rawValue = localStorage.getItem(APP_CONFIG.STORAGE_KEY);
                if (!rawValue) {
                    return {};
                }

                const parsedValue = JSON.parse(rawValue);
                return parsedValue && typeof parsedValue === 'object' ? parsedValue : {};
            } catch {
                return {};
            }
        }

        saveRawState(nextState) {
            try {
                localStorage.setItem(APP_CONFIG.STORAGE_KEY, JSON.stringify(nextState));
            } catch {}
        }

        loadThemeState() {
            const storedState = this.loadRawState();
            return {
                ...APP_CONFIG.DEFAULT_THEME,
                background: typeof storedState.background === 'string' ? storedState.background : APP_CONFIG.DEFAULT_THEME.background,
                gradientStart: typeof storedState.gradientStart === 'string' ? storedState.gradientStart : APP_CONFIG.DEFAULT_THEME.gradientStart,
                gradientEnd: typeof storedState.gradientEnd === 'string' ? storedState.gradientEnd : APP_CONFIG.DEFAULT_THEME.gradientEnd,
                textColor: typeof storedState.textColor === 'string' ? storedState.textColor : APP_CONFIG.DEFAULT_THEME.textColor,
                fontFamily: typeof storedState.fontFamily === 'string' ? storedState.fontFamily : APP_CONFIG.DEFAULT_THEME.fontFamily
            };
        }

        saveThemeState(themeState) {
            const currentState = this.loadRawState();
            this.saveRawState({
                ...currentState,
                ...themeState
            });
        }

        loadPanelState() {
            const storedState = this.loadRawState();
            return {
                ...APP_CONFIG.DEFAULT_PANEL_STATE,
                isOpen: typeof storedState.isOpen === 'boolean' ? storedState.isOpen : APP_CONFIG.DEFAULT_PANEL_STATE.isOpen
            };
        }

        savePanelState(panelState) {
            const currentState = this.loadRawState();
            this.saveRawState({
                ...currentState,
                ...panelState
            });
        }
    }

    class StyleRegistry {
        constructor(domCache) {
            this.domCache = domCache;
            this.registeredStyles = new Map();
        }

        mountStyle(key, cssText) {
            if (this.registeredStyles.has(key)) {
                this.registeredStyles.get(key).textContent = cssText;
                return;
            }

            const styleElement = document.createElement('style');
            styleElement.textContent = cssText;
            this.domCache.documentHead.appendChild(styleElement);
            this.registeredStyles.set(key, styleElement);
        }
    }

    class FontCatalog {
        constructor() {
            this.loadedFonts = new Set();
        }

        searchFonts(queryText = '') {
            const normalizedQuery = queryText.trim().toLowerCase();

            return APP_CONFIG.FONT_OPTIONS
                .filter(fontName => fontName.toLowerCase().includes(normalizedQuery))
                .slice(0, APP_CONFIG.FONT_RESULTS_LIMIT);
        }

        loadFont(fontFamily) {
            if (!fontFamily || this.loadedFonts.has(fontFamily)) {
                return;
            }

            const fontLink = document.createElement('link');
            fontLink.rel = 'stylesheet';
            fontLink.href = `https://fonts.googleapis.com/css2?family=${encodeURIComponent(fontFamily).replace(/%20/g, '+')}:wght@400;500;600;700&display=swap`;

            document.head.appendChild(fontLink);
            this.loadedFonts.add(fontFamily);
        }
    }

    class ThemeManager {
        constructor(domCache, storageService, styleRegistry, fontCatalog) {
            this.domCache = domCache;
            this.storageService = storageService;
            this.styleRegistry = styleRegistry;
            this.fontCatalog = fontCatalog;
            this.themeState = this.storageService.loadThemeState();
        }

        initialize() {
            this.fontCatalog.loadFont(this.themeState.fontFamily);
            this.applyStaticShellStyles();
            this.applyTheme();
        }

        getThemeState() {
            return { ...this.themeState };
        }

        updateTheme(patch) {
            this.themeState = {
                ...this.themeState,
                ...patch
            };

            this.storageService.saveThemeState(this.themeState);
            this.fontCatalog.loadFont(this.themeState.fontFamily);
            this.applyTheme();
        }

        applyStaticShellStyles() {
            this.styleRegistry.mountStyle('kogama-shell-overrides', `
                ${APP_CONFIG.SELECTORS.toolbar},
                ${APP_CONFIG.SELECTORS.stackHeader},
                ${APP_CONFIG.SELECTORS.sectionShell},
                ${APP_CONFIG.SELECTORS.footer},
                ${APP_CONFIG.SELECTORS.heroSurface},
                ${APP_CONFIG.SELECTORS.heroInnerSurface} {
                    background: rgba(0, 0, 0, ${APP_CONFIG.NAV_OPACITY}) !important;
                    background-image: none !important;
                }

                ${APP_CONFIG.SELECTORS.gameTitle} {
                    color: #ffffff !important;
                }
            `);
        }

        applyTheme() {
            const rootPage = this.domCache.getRootPage();
            if (!rootPage) {
                return;
            }

            const { background, gradientStart, gradientEnd, textColor, fontFamily } = this.themeState;

            rootPage.style.backgroundColor = background;
            rootPage.style.backgroundImage = `linear-gradient(135deg, ${gradientStart}, ${gradientEnd})`;
            rootPage.style.color = textColor;
            document.body.style.fontFamily = `"${fontFamily}", ${APP_CONFIG.FONT_FALLBACK}`;
        }
    }

    class NumberFormatter {
        formatCompactValue(textValue) {
            const sanitizedText = String(textValue).trim();
            const compactPattern = /([\d.,]+)\s*([kKmM])/;
            const match = sanitizedText.match(compactPattern);

            if (!match) {
                return sanitizedText;
            }

            const numericValue = Number.parseFloat(match[1].replace(/\s/g, '').replace(',', '.'));
            if (Number.isNaN(numericValue)) {
                return sanitizedText;
            }

            const roundedValue = Math.round(numericValue * 10) / 10;
            const outputValue = roundedValue.toFixed(1).replace(/\.0$/, '');

            return `${outputValue}${match[2].toUpperCase()}`;
        }
    }

    class StatsManager {
        constructor(domCache, numberFormatter) {
            this.domCache = domCache;
            this.numberFormatter = numberFormatter;
        }

        refreshGameStats() {
            const gameCards = this.domCache.getGameCards();
            if (!gameCards.length) {
                return;
            }

            gameCards.forEach(card => this.refreshCardStats(card));
        }

        refreshCardStats(cardElement) {
            const statItems = this.domCache.queryAll(APP_CONFIG.SELECTORS.gameStatsItems, cardElement);
            statItems.forEach(statItem => this.refreshStatItem(statItem));
        }

        refreshStatItem(statItem) {
            const textNode = [...statItem.childNodes].reverse().find(node => node.nodeType === Node.TEXT_NODE);
            if (!textNode) {
                return;
            }

            const nextValue = this.numberFormatter.formatCompactValue(textNode.textContent);
            textNode.textContent = ` ${nextValue}`;
        }
    }

    class AdsManager {
        constructor(domCache) {
            this.domCache = domCache;
        }

        removeSubscriptionAds() {
            const adLinks = this.domCache.getAdsLinks();
            if (!adLinks.length) {
                return;
            }

            adLinks.forEach(link => {
                const collapsibleContainer = link.closest('.MuiCollapse-root');
                if (collapsibleContainer) {
                    collapsibleContainer.remove();
                }
            });
        }
    }

    class PlayerCountManager {
        constructor(domCache) {
            this.domCache = domCache;
        }

        refreshPlayerCount() {
            if (!this.isSupportedRoute()) {
                this.removePlayerCountBadge();
                return;
            }

            const totalPlayers = this.calculatePlayerCount();
            const badgeElement = this.getOrCreatePlayerCountBadge();

            if (badgeElement) {
                badgeElement.textContent = `Players Online · ${totalPlayers}`;
            }
        }

        isSupportedRoute() {
            const currentUrl = window.location.href;

            return (
                currentUrl.startsWith(APP_CONFIG.ROUTES.game) ||
                currentUrl.startsWith(APP_CONFIG.ROUTES.build) ||
                currentUrl === "https://www.kogama.com/" ||
                currentUrl === "https://www.kogama.com"
            );
        }

        calculatePlayerCount() {
            return this.domCache.getPlayerValueElements().reduce((total, element) => {
                const value = Number.parseInt(element.textContent.trim(), 10);
                return Number.isNaN(value) ? total : total + value;
            }, 0);
        }

        getOrCreatePlayerCountBadge() {
            const existingBadge = document.getElementById(APP_CONFIG.PLAYER_COUNT_ID);
            if (existingBadge) {
                return existingBadge;
            }

            const targetContainer = this.domCache.getContentContainer();
            if (!targetContainer) {
                return null;
            }

            const badgeElement = document.createElement('div');
            badgeElement.id = APP_CONFIG.PLAYER_COUNT_ID;
            badgeElement.textContent = 'Players Online · 0';
            targetContainer.prepend(badgeElement);

            return badgeElement;
        }

        removePlayerCountBadge() {
            const badgeElement = document.getElementById(APP_CONFIG.PLAYER_COUNT_ID);
            if (badgeElement) {
                badgeElement.remove();
            }
        }
    }

    class PanelView {
        constructor(themeManager, fontCatalog) {
            this.themeManager = themeManager;
            this.fontCatalog = fontCatalog;
            this.elements = {};
        }

        mount() {
            this.render();
            this.bindElements();
            this.populateForm();
            this.renderFontResults();
        }

        bindThemeChange(handler) {
            const colorInputs = [
                this.elements.gradientStartInput,
                this.elements.gradientEndInput
            ];

            colorInputs.forEach(input => {
                input.addEventListener('input', () => {
                    handler({
                        gradientStart: this.elements.gradientStartInput.value,
                        gradientEnd: this.elements.gradientEndInput.value
                    });
                });
            });

            this.elements.fontSearchInput.addEventListener('input', () => {
                this.renderFontResults(this.elements.fontSearchInput.value);
            });

            this.elements.fontResultsList.addEventListener('click', event => {
                const fontOption = event.target.closest('[data-font-family]');
                if (!fontOption) {
                    return;
                }

                const selectedFont = fontOption.dataset.fontFamily;
                this.setSelectedFont(selectedFont);
                handler({ fontFamily: selectedFont });
            });
        }

        bindPanelClose(handler) {
            this.elements.closeButton.addEventListener('click', handler);
        }

        render() {
            const panelElement = document.createElement('aside');
            panelElement.id = APP_CONFIG.PANEL_ID;
            panelElement.setAttribute('aria-label', 'Theme Editor');

            panelElement.innerHTML = `
                <div class="kte-panel__shell">
                    <div class="kte-panel__header">
                        <div class="kte-panel__eyebrow">Appearance</div>
                        <div class="kte-panel__title-row">
                            <h2 class="kte-panel__title">Theme Editor</h2>
                            <button
                                id="kte-panel-close"
                                class="kte-panel__close"
                                type="button"
                                aria-label="Hide theme editor"
                                title="Hide theme editor (Shift+S to show)"
                            >
                                <span class="kte-panel__close-icon" aria-hidden="true">✕</span>
                            </button>
                        </div>
                        <p class="kte-panel__subtitle">Made by BlueEL.</p>
                    </div>

                    <section class="kte-panel__section">
                        <div class="kte-panel__section-header">
                            <h3 class="kte-panel__section-title">Colors</h3>
                            <span class="kte-panel__section-caption">Gradient</span>
                        </div>

                        <div class="kte-color-grid">
                            <label class="kte-field kte-field--color">
                                <span class="kte-field__label">Gradient Start</span>
                                <input id="kte-gradient-start" class="kte-field__input kte-field__input--color" type="color">
                            </label>

                            <label class="kte-field kte-field--color">
                                <span class="kte-field__label">Gradient End</span>
                                <input id="kte-gradient-end" class="kte-field__input kte-field__input--color" type="color">
                            </label>
                        </div>
                    </section>

                    <section class="kte-panel__section">
                        <div class="kte-panel__section-header">
                            <h3 class="kte-panel__section-title">Typography</h3>
                            <span class="kte-panel__section-caption">Search and apply fonts</span>
                        </div>

                        <label class="kte-field">
                            <span class="kte-field__label">Font Family</span>
                            <input id="kte-font-search" class="kte-field__input" type="text" placeholder="Search fonts...">
                        </label>

                        <div id="kte-font-results" class="kte-font-results" role="listbox" aria-label="Font results"></div>
                    </section>
                </div>
            `;

            document.body.appendChild(panelElement);
        }

        bindElements() {
            this.elements.panel = document.getElementById(APP_CONFIG.PANEL_ID);
            this.elements.closeButton = document.getElementById('kte-panel-close');
            this.elements.gradientStartInput = document.getElementById('kte-gradient-start');
            this.elements.gradientEndInput = document.getElementById('kte-gradient-end');
            this.elements.fontSearchInput = document.getElementById('kte-font-search');
            this.elements.fontResultsList = document.getElementById('kte-font-results');
        }

        populateForm() {
            const themeState = this.themeManager.getThemeState();

            this.elements.gradientStartInput.value = themeState.gradientStart;
            this.elements.gradientEndInput.value = themeState.gradientEnd;
            this.elements.fontSearchInput.value = '';
        }

        renderFontResults(searchQuery = '') {
            const availableFonts = this.fontCatalog.searchFonts(searchQuery);
            const activeFont = this.themeManager.getThemeState().fontFamily;

            this.elements.fontResultsList.innerHTML = availableFonts.map(fontFamily => {
                const isActive = fontFamily === activeFont;
                return `
                    <button
                        type="button"
                        class="kte-font-option${isActive ? ' is-active' : ''}"
                        data-font-family="${fontFamily}"
                        style="font-family: '${fontFamily}', ${APP_CONFIG.FONT_FALLBACK};"
                    >
                        <span class="kte-font-option__name">${fontFamily}</span>
                        ${isActive ? '<span class="kte-font-option__badge">Active</span>' : ''}
                    </button>
                `;
            }).join('');
        }

        setSelectedFont() {
            this.renderFontResults(this.elements.fontSearchInput.value);
        }

        show() {
            this.elements.panel.classList.remove('is-hidden');
            this.elements.panel.setAttribute('aria-hidden', 'false');
        }

        hide() {
            this.elements.panel.classList.add('is-hidden');
            this.elements.panel.setAttribute('aria-hidden', 'true');
        }

        isVisible() {
            return !this.elements.panel.classList.contains('is-hidden');
        }
    }

    class PanelController {
        constructor(panelView, storageService) {
            this.panelView = panelView;
            this.storageService = storageService;
            this.handleKeydown = this.handleKeydown.bind(this);
            this.handleCloseButtonClick = this.handleCloseButtonClick.bind(this);
        }

        initialize() {
            this.panelView.bindPanelClose(this.handleCloseButtonClick);
            document.addEventListener('keydown', this.handleKeydown);

            if (!this.storageService.loadPanelState().isOpen) {
                this.panelView.hide();
            }
        }

        handleCloseButtonClick() {
            this.hidePanel();
        }

        handleKeydown(event) {
            const pressedKey = String(event.key || '').toUpperCase();
            if (!event.shiftKey || pressedKey !== APP_CONFIG.PANEL_SHORTCUT_KEY) {
                return;
            }

            this.showPanel();
        }

        showPanel() {
            if (this.panelView.isVisible()) {
                return;
            }

            this.panelView.show();
            this.storageService.savePanelState({ isOpen: true });
        }

        hidePanel() {
            if (!this.panelView.isVisible()) {
                return;
            }

            this.panelView.hide();
            this.storageService.savePanelState({ isOpen: false });
        }
    }

    class PanelStyles {
        constructor(styleRegistry) {
            this.styleRegistry = styleRegistry;
        }

        mount() {
            this.styleRegistry.mountStyle('kogama-theme-panel', `
                #${APP_CONFIG.PANEL_ID} {
                    position: fixed;
                    top: ${APP_CONFIG.PANEL_TOP_OFFSET}px;
                    right: ${APP_CONFIG.PANEL_RIGHT_OFFSET}px;
                    width: ${APP_CONFIG.PANEL_WIDTH}px;
                    z-index: 999999;
                    color: rgba(255, 255, 255, 0.96);
                    font-family: Inter, ${APP_CONFIG.FONT_FALLBACK};
                    transition:
                        opacity 180ms ease,
                        transform 220ms ease,
                        visibility 220ms ease;
                    transform-origin: top right;
                }

                #${APP_CONFIG.PANEL_ID}.is-hidden {
                    opacity: 0;
                    visibility: hidden;
                    pointer-events: none;
                    transform: translateY(-8px) scale(0.985);
                }

                #${APP_CONFIG.PANEL_ID} *,
                #${APP_CONFIG.PLAYER_COUNT_ID} * {
                    box-sizing: border-box;
                }

                #${APP_CONFIG.PANEL_ID} .kte-panel__shell {
                    background:
                        linear-gradient(180deg, rgba(255, 255, 255, 0.12), rgba(255, 255, 255, 0.06)),
                        rgba(12, 16, 28, 0.78);
                    border: 1px solid rgba(255, 255, 255, 0.14);
                    backdrop-filter: blur(20px);
                    -webkit-backdrop-filter: blur(20px);
                    border-radius: 22px;
                    padding: 18px;
                    box-shadow:
                        0 20px 60px rgba(0, 0, 0, 0.38),
                        inset 0 1px 0 rgba(255, 255, 255, 0.08);
                }

                #${APP_CONFIG.PANEL_ID} .kte-panel__header {
                    margin-bottom: 18px;
                }

                #${APP_CONFIG.PANEL_ID} .kte-panel__eyebrow {
                    font-size: 11px;
                    font-weight: 600;
                    letter-spacing: 0.12em;
                    text-transform: uppercase;
                    color: rgba(255, 255, 255, 0.52);
                    margin-bottom: 8px;
                }

                #${APP_CONFIG.PANEL_ID} .kte-panel__title-row {
                    display: flex;
                    align-items: flex-start;
                    justify-content: space-between;
                    gap: 10px;
                }

                #${APP_CONFIG.PANEL_ID} .kte-panel__title {
                    margin: 0;
                    font-size: 21px;
                    line-height: 1.1;
                    font-weight: 700;
                    letter-spacing: -0.03em;
                    padding-top: 2px;
                }

                #${APP_CONFIG.PANEL_ID} .kte-panel__close {
                    width: 32px;
                    height: 32px;
                    display: inline-flex;
                    align-items: center;
                    justify-content: center;
                    flex-shrink: 0;
                    margin: -2px -2px 0 0;
                    padding: 0;
                    border: 1px solid rgba(255, 255, 255, 0.12);
                    border-radius: 12px;
                    background:
                        linear-gradient(180deg, rgba(255, 255, 255, 0.12), rgba(255, 255, 255, 0.04)),
                        rgba(255, 255, 255, 0.04);
                    color: rgba(255, 255, 255, 0.86);
                    cursor: pointer;
                    outline: none;
                    box-shadow:
                        inset 0 1px 0 rgba(255, 255, 255, 0.06),
                        0 8px 20px rgba(0, 0, 0, 0.14);
                    transition:
                        transform 160ms ease,
                        background-color 160ms ease,
                        border-color 160ms ease,
                        color 160ms ease,
                        box-shadow 160ms ease;
                }

                #${APP_CONFIG.PANEL_ID} .kte-panel__close:hover {
                    transform: translateY(-1px);
                    color: rgba(255, 255, 255, 0.96);
                    border-color: rgba(255, 255, 255, 0.2);
                    background:
                        linear-gradient(180deg, rgba(255, 255, 255, 0.16), rgba(255, 255, 255, 0.06)),
                        rgba(255, 255, 255, 0.06);
                    box-shadow:
                        inset 0 1px 0 rgba(255, 255, 255, 0.08),
                        0 12px 24px rgba(0, 0, 0, 0.18);
                }

                #${APP_CONFIG.PANEL_ID} .kte-panel__close:focus-visible {
                    border-color: rgba(138, 180, 248, 0.72);
                    box-shadow:
                        0 0 0 4px rgba(138, 180, 248, 0.16),
                        inset 0 1px 0 rgba(255, 255, 255, 0.08);
                }

                #${APP_CONFIG.PANEL_ID} .kte-panel__close-icon {
                    font-size: 14px;
                    line-height: 1;
                    transform: translateY(-0.5px);
                }

                #${APP_CONFIG.PANEL_ID} .kte-panel__subtitle {
                    margin: 10px 0 0;
                    font-size: 13px;
                    line-height: 1.5;
                    color: rgba(255, 255, 255, 0.68);
                }

                #${APP_CONFIG.PANEL_ID} .kte-panel__section {
                    padding: 14px;
                    border-radius: 18px;
                    background: rgba(255, 255, 255, 0.04);
                    border: 1px solid rgba(255, 255, 255, 0.08);
                }

                #${APP_CONFIG.PANEL_ID} .kte-panel__section + .kte-panel__section {
                    margin-top: 12px;
                }

                #${APP_CONFIG.PANEL_ID} .kte-panel__section-header {
                    display: flex;
                    align-items: baseline;
                    justify-content: space-between;
                    gap: 10px;
                    margin-bottom: 12px;
                }

                #${APP_CONFIG.PANEL_ID} .kte-panel__section-title {
                    margin: 0;
                    font-size: 14px;
                    font-weight: 600;
                    color: rgba(255, 255, 255, 0.94);
                }

                #${APP_CONFIG.PANEL_ID} .kte-panel__section-caption {
                    font-size: 11px;
                    color: rgba(255, 255, 255, 0.48);
                }

                #${APP_CONFIG.PANEL_ID} .kte-color-grid {
                    display: grid;
                    grid-template-columns: repeat(2, minmax(0, 1fr));
                    gap: 10px;
                }

                #${APP_CONFIG.PANEL_ID} .kte-field {
                    display: flex;
                    flex-direction: column;
                    gap: 7px;
                }

                #${APP_CONFIG.PANEL_ID} .kte-field__label {
                    font-size: 12px;
                    font-weight: 500;
                    color: rgba(255, 255, 255, 0.72);
                }

                #${APP_CONFIG.PANEL_ID} .kte-field__input {
                    width: 100%;
                    border: 1px solid rgba(255, 255, 255, 0.1);
                    background: rgba(255, 255, 255, 0.06);
                    color: rgba(255, 255, 255, 0.96);
                    border-radius: 12px;
                    padding: 12px 14px;
                    outline: none;
                    transition:
                        border-color 160ms ease,
                        background-color 160ms ease,
                        transform 160ms ease,
                        box-shadow 160ms ease;
                }

                #${APP_CONFIG.PANEL_ID} .kte-field__input::placeholder {
                    color: rgba(255, 255, 255, 0.34);
                }

                #${APP_CONFIG.PANEL_ID} .kte-field__input:hover {
                    background: rgba(255, 255, 255, 0.08);
                    border-color: rgba(255, 255, 255, 0.16);
                }

                #${APP_CONFIG.PANEL_ID} .kte-field__input:focus {
                    border-color: rgba(138, 180, 248, 0.72);
                    background: rgba(255, 255, 255, 0.08);
                    box-shadow: 0 0 0 4px rgba(138, 180, 248, 0.16);
                }

                #${APP_CONFIG.PANEL_ID} .kte-field__input--color {
                    min-height: 48px;
                    padding: 6px;
                    cursor: pointer;
                }

                #${APP_CONFIG.PANEL_ID} .kte-field__input--color::-webkit-color-swatch-wrapper {
                    padding: 0;
                }

                #${APP_CONFIG.PANEL_ID} .kte-field__input--color::-webkit-color-swatch {
                    border: none;
                    border-radius: 9px;
                }

                #${APP_CONFIG.PANEL_ID} .kte-font-results {
                    display: flex;
                    flex-direction: column;
                    gap: 8px;
                    max-height: 260px;
                    overflow: auto;
                    padding-right: 2px;
                }

                #${APP_CONFIG.PANEL_ID} .kte-font-results::-webkit-scrollbar {
                    width: 8px;
                }

                #${APP_CONFIG.PANEL_ID} .kte-font-results::-webkit-scrollbar-thumb {
                    background: rgba(255, 255, 255, 0.16);
                    border-radius: 999px;
                }

                #${APP_CONFIG.PANEL_ID} .kte-font-option {
                    width: 100%;
                    display: flex;
                    align-items: center;
                    justify-content: space-between;
                    gap: 12px;
                    text-align: left;
                    padding: 12px 14px;
                    border-radius: 14px;
                    border: 1px solid rgba(255, 255, 255, 0.08);
                    background: rgba(255, 255, 255, 0.05);
                    color: rgba(255, 255, 255, 0.94);
                    cursor: pointer;
                    transition:
                        transform 160ms ease,
                        background-color 160ms ease,
                        border-color 160ms ease,
                        box-shadow 160ms ease;
                }

                #${APP_CONFIG.PANEL_ID} .kte-font-option:hover {
                    transform: translateY(-1px);
                    background: rgba(255, 255, 255, 0.08);
                    border-color: rgba(255, 255, 255, 0.14);
                    box-shadow: 0 10px 26px rgba(0, 0, 0, 0.18);
                }

                #${APP_CONFIG.PANEL_ID} .kte-font-option.is-active {
                    background: linear-gradient(180deg, rgba(138, 180, 248, 0.18), rgba(138, 180, 248, 0.1));
                    border-color: rgba(138, 180, 248, 0.45);
                    box-shadow: 0 10px 28px rgba(138, 180, 248, 0.14);
                }

                #${APP_CONFIG.PANEL_ID} .kte-font-option__name {
                    font-size: 14px;
                    font-weight: 500;
                    line-height: 1.3;
                }

                #${APP_CONFIG.PANEL_ID} .kte-font-option__badge {
                    flex-shrink: 0;
                    font-size: 11px;
                    font-weight: 600;
                    letter-spacing: 0.02em;
                    padding: 5px 8px;
                    border-radius: 999px;
                    color: rgba(255, 255, 255, 0.92);
                    background: rgba(138, 180, 248, 0.18);
                    border: 1px solid rgba(138, 180, 248, 0.28);
                }

                #${APP_CONFIG.PLAYER_COUNT_ID} {
                    display: inline-flex;
                    align-items: center;
                    gap: 8px;
                    margin: 14px 0 4px;
                    margin-left: 10px;
                    padding: 10px 14px;
                    border-radius: 999px;
                    background: rgba(15, 20, 32, 0.72);
                    color: rgba(255, 255, 255, 0.96);
                    border: 1px solid rgba(255, 255, 255, 0.1);
                    box-shadow:
                        0 10px 30px rgba(0, 0, 0, 0.2),
                        inset 0 1px 0 rgba(255, 255, 255, 0.05);
                    backdrop-filter: blur(12px);
                    -webkit-backdrop-filter: blur(12px);
                    font-size: 13px;
                    font-weight: 700;
                    letter-spacing: 0.01em;
                    width: fit-content;
                }

                @media (max-width: 980px) {
                    #${APP_CONFIG.PANEL_ID} {
                        top: 12px;
                        right: 12px;
                        width: min(calc(100vw - 24px), ${APP_CONFIG.PANEL_WIDTH}px);
                    }
                }

                @media (max-width: 640px) {
                    #${APP_CONFIG.PANEL_ID} .kte-panel__shell {
                        padding: 14px;
                        border-radius: 18px;
                    }
                }
            `);
        }
    }

    class MutationCoordinator {
        constructor(callback) {
            this.callback = callback;
            this.observer = null;
            this.debounceTimer = null;
        }

        start() {
            this.observer = new MutationObserver(() => this.schedule());
            this.observer.observe(document.body, {
                childList: true,
                subtree: true
            });
        }

        schedule() {
            window.clearTimeout(this.debounceTimer);
            this.debounceTimer = window.setTimeout(() => {
                this.callback();
            }, APP_CONFIG.OBSERVER_DEBOUNCE_DELAY);
        }
    }

    class KogamaCustomizerApp {
        constructor() {
            this.domCache = new DomCache();
            this.storageService = new StorageService();
            this.styleRegistry = new StyleRegistry(this.domCache);
            this.fontCatalog = new FontCatalog();
            this.themeManager = new ThemeManager(this.domCache, this.storageService, this.styleRegistry, this.fontCatalog);
            this.numberFormatter = new NumberFormatter();
            this.statsManager = new StatsManager(this.domCache, this.numberFormatter);
            this.adsManager = new AdsManager(this.domCache);
            this.playerCountManager = new PlayerCountManager(this.domCache);
            this.panelStyles = new PanelStyles(this.styleRegistry);
            this.panelView = new PanelView(this.themeManager, this.fontCatalog);
            this.panelController = new PanelController(this.panelView, this.storageService);
            this.mutationCoordinator = new MutationCoordinator(() => this.refreshDynamicUi());
            this.statsIntervalId = null;
        }

        start() {
            this.panelStyles.mount();
            this.themeManager.initialize();
            this.panelView.mount();
            this.panelView.bindThemeChange(themePatch => this.handleThemeChange(themePatch));
            this.panelController.initialize();
            this.mutationCoordinator.start();
            this.startStatsPolling();
            this.runInitialRefresh();
        }

        handleThemeChange(themePatch) {
            this.themeManager.updateTheme(themePatch);
        }

        refreshDynamicUi() {
            this.adsManager.removeSubscriptionAds();
            this.playerCountManager.refreshPlayerCount();
            this.themeManager.applyTheme();
        }

        startStatsPolling() {
            this.statsIntervalId = window.setInterval(() => {
                this.statsManager.refreshGameStats();
            }, APP_CONFIG.STATS_REFRESH_INTERVAL);
        }

        runInitialRefresh() {
            window.setTimeout(() => {
                this.statsManager.refreshGameStats();
                this.refreshDynamicUi();
            }, APP_CONFIG.INITIAL_REFRESH_DELAY);
        }
    }

    new KogamaCustomizerApp().start();
})();