Kogama Plus

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

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey, το Greasemonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

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

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Userscripts για να εγκαταστήσετε αυτόν τον κώδικα.

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

Θα χρειαστεί να εγκαταστήσετε μια επέκταση διαχείρισης κώδικα χρήστη για να εγκαταστήσετε αυτόν τον κώδικα.

(Έχω ήδη έναν διαχειριστή κώδικα χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(Έχω ήδη έναν διαχειριστή στυλ χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

// ==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();
})();