Ultimate Steam Enhancer

Добавляет множество функций для улучшения взаимодействия с магазином и сообществом (Полный список на странице скрипта)

当前为 2025-05-02 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

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

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Ultimate Steam Enhancer
// @namespace    https://store.steampowered.com/
// @version      1.9.5
// @description  Добавляет множество функций для улучшения взаимодействия с магазином и сообществом (Полный список на странице скрипта)
// @author       0wn3df1x
// @license      MIT
// @require      https://code.jquery.com/jquery-3.6.0.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/noUiSlider/15.7.1/nouislider.min.js
// @match        https://store.steampowered.com/*
// @match        *://*steamcommunity.com/*
// @grant        GM_xmlhttpRequest
// @grant        GM_getResourceText
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_addStyle
// @grant        GM_deleteValue
// @connect      zoneofgames.ru
// @connect      raw.githubusercontent.com
// @connect      gist.githubusercontent.com
// @connect      store.steampowered.com
// @connect      api.steampowered.com
// @connect      steamcommunity.com
// @connect      shared.cloudflare.steamstatic.com
// @connect      umadb.ro
// @connect      api.github.com
// @connect      howlongtobeat.com
// @connect      vgtimes.ru
// @connect      api.digiseller.com
// @connect      plati.market
// @connect      digiseller.mycdn.ink
// ==/UserScript==


(function() {
    'use strict';

    const scriptsConfig = {
        // Основные скрипты
        gamePage: true, // Скрипт для страницы игры (индикаторы о наличии русского перевода; получение дополнительных обзоров) | https://store.steampowered.com/app/*
        hltbData: true, // Скрипт для страницы игры (HLTB; получение сведений о времени прохождения) | https://store.steampowered.com/app/*
        friendsPlaytime: true, // Скрипт для страницы игры (Время друзей & Достижения) | https://store.steampowered.com/app/*
        earlyaccdata: true, // Скрипт для страницы игры (Ранний доступ) | https://store.steampowered.com/app/*
        zogInfo: true, // Скрипт для страницы игры (ZOG; получение сведение о наличии русификаторов) | https://store.steampowered.com/app/*
        vgtSales: true, // Скрипт для страницы игры (VGT; отображения цен из агрегатора VGTimes) | https://store.steampowered.com/app/*
        platiSales: true, // Скрипт для страницы игры (Plati; отображение цен с Plati.Market) | https://store.steampowered.com/app/*
        catalogInfo: true, // Скрипт для получения дополнительной информации об игре при наведении на неё на странице поиска по каталогу | https://store.steampowered.com/search/
        catalogHider: false, // Скрипт скрытия игр на странице поиска по каталогу | https://store.steampowered.com/search/
        newsFilter: true, // Скрипт для скрытия новостей в новостном центре: | https://store.steampowered.com/news/
        Kaznachei: true, // Скрипт для показа годовых и исторических продаж предмета на торговой площадке Steam | https://steamcommunity.com/market/listings/*
        homeInfo: true, // Скрипт для получения дополнительной информации об игре при наведении на неё на странице вашей активности Steam | https://steamcommunity.com/my/
        wishlistTracker: true, // Скрипт для получения уведомлений об изменении дат выхода игр из вашего списка желаемого Steam и показа календаря с датами | https://steamcommunity.com/my/wishlist/
        // Дополнительные настройки
        autoExpandHltb: false, // Автоматически раскрывать спойлер HLTB
        autoLoadReviews: false, // Автоматически загружать дополнительные обзоры
        toggleEnglishLangInfo: false // Отображает данные об английском языке в дополнительной информации при поиске по каталогу и в активности (функция для переводчиков)
    };


    // Скрипт для страницы игры (индикаторы о наличии русского перевода; получение дополнительных обзоров) | https://store.steampowered.com/app/*
    if (scriptsConfig.gamePage && window.location.pathname.includes('/app/')) {
        (function() {
            'use strict';

            function createFruitIndicator(apple, hasSupport, orange) {
                const banana = document.createElement('div');
                banana.style.position = 'relative';
                banana.style.cursor = 'pointer';

                const grape = document.createElement('div');
                grape.style.width = '60px';
                grape.style.height = '60px';
                grape.style.borderRadius = '4px';
                grape.style.display = 'flex';
                grape.style.alignItems = 'center';
                grape.style.justifyContent = 'center';
                grape.style.background = hasSupport ? 'rgba(66, 135, 245, 0.2)' : 'rgba(0, 0, 0, 0.1)';
                grape.style.border = `1px solid ${hasSupport ? '#2A5891' : '#3c3c3c'}`;
                grape.style.opacity = '0.95';
                grape.style.transition = 'transform 0.3s ease, box-shadow 0.3s ease';
                grape.style.overflow = 'hidden';
                grape.style.position = 'relative';
                grape.style.transform = 'translateZ(0)';

                const kiwi = document.createElement('div');
                kiwi.innerHTML = apple;
                kiwi.style.width = '30px';
                kiwi.style.height = '30px';
                kiwi.style.display = 'block';
                kiwi.style.margin = '0 auto';
                kiwi.style.transition = 'fill 0.3s ease';

                grape.appendChild(kiwi);

                const svgElement = kiwi.querySelector('svg');

                function setColor(hasSupport) {
                    const borderColor = hasSupport ? '#2A5891' : '#3c3c3c';
                    const svgFill = hasSupport ? '#FFFFFF' : '#0E1C25';

                    grape.style.border = `1px solid ${borderColor}`;
                    svgElement.style.fill = svgFill;
                }

                setColor(hasSupport);

                const pineapple = document.createElement('div');
                const hasLabel = hasSupport ? orange : getGenitiveCase(orange);
                pineapple.textContent = hasSupport ? `Есть ${orange}` : `Нет ${hasLabel}`;
                pineapple.style.position = 'absolute';
                pineapple.style.top = '50%';
                pineapple.style.left = '100%';
                pineapple.style.transform = 'translateY(-50%) translateX(10px)';
                pineapple.style.background = 'rgba(0, 0, 0, 0.8)';
                pineapple.style.color = '#fff';
                pineapple.style.padding = '8px 12px';
                pineapple.style.borderRadius = '8px';
                pineapple.style.fontSize = '14px';
                pineapple.style.whiteSpace = 'nowrap';
                pineapple.style.opacity = '0';
                pineapple.style.transition = 'opacity 0.3s ease';
                pineapple.style.zIndex = '10000';
                pineapple.style.pointerEvents = 'none';
                banana.appendChild(pineapple);

                banana.addEventListener('mouseenter', () => {
                    grape.style.transform = 'scale(1.1) translateZ(0)';
                    pineapple.style.opacity = '1';
                });

                banana.addEventListener('mouseleave', () => {
                    grape.style.transform = 'scale(1) translateZ(0)';
                    pineapple.style.opacity = '0';
                });

                banana.appendChild(grape);
                return banana;
            }

            function getGenitiveCase(orange) {
                switch (orange) {
                    case 'интерфейс':
                        return 'интерфейса';
                    case 'озвучка':
                        return 'озвучки';
                    case 'субтитры':
                        return 'субтитров';
                    default:
                        return orange;
                }
            }

            function checkRussianSupport() {
                const mango = document.querySelector('#languageTable table.game_language_options');
                if (!mango) return {
                    interface: false,
                    voice: false,
                    subtitles: false
                };

                const strawberry = mango.querySelectorAll('tr');
                for (let blueberry of strawberry) {
                    const watermelon = blueberry.querySelector('td.ellipsis');
                    if (watermelon && /русский|Russian/i.test(watermelon.textContent.trim())) {
                        const cherry = blueberry.querySelector('td.checkcol:nth-child(2) span');
                        const raspberry = blueberry.querySelector('td.checkcol:nth-child(3) span');
                        const blackberry = blueberry.querySelector('td.checkcol:nth-child(4) span');

                        return {
                            interface: cherry !== null,
                            voice: raspberry !== null,
                            subtitles: blackberry !== null
                        };
                    }
                }
                return {
                    interface: false,
                    voice: false,
                    subtitles: false
                };
            }

            function addRussianIndicators() {
                const russianSupport = checkRussianSupport();
                if (!russianSupport) return;

                let lemon = document.querySelector('#gameHeaderImageCtn');
                if (!lemon) return;

                const lime = document.createElement('div');
                lime.style.position = 'absolute';
                lime.style.top = '-10px';
                lime.style.left = 'calc(100% + 10px)';
                lime.style.display = 'flex';
                lime.style.flexDirection = 'column';
                lime.style.gap = '15px';
                lime.style.alignItems = 'flex-start';
                lime.style.zIndex = '2';
                lime.style.marginTop = '10px';

                const peach = createFruitIndicator(`<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M12,0C5.38,0,0,5.38,0,12s5.38,12,12,12s12-5.38,12-12S18.62,0,12,0z M12,22C6.49,22,2,17.51,2,12S6.49,2,12,2	s10,4.49,10,10S17.51,22,12,22z M10.5,10h3v8h-3V10z M10.5,5h3v3h-3V5z" /></svg>`, russianSupport.interface, 'интерфейс');
                const plum = createFruitIndicator(`<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M15,21v-2c3.86,0,7-3.14,7-7s-3.14-7-7-7V3c4.96,0,9,4.04,9,9S19.96,21,15,21z M15,17v-2c1.65,0,3-1.35,3-3s-1.35-3-3-3V7 c2.76,0,5,2.24,5,5S17.76,17,15,17z M1,12v4h5l6,5V3L6,8H1V12" /></svg>`, russianSupport.voice, 'озвучка');
                const apricot = createFruitIndicator(`<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><g><path d="M11,24l-4.4-5H0V0h23v19h-7.6L11,24z M2,17h5.4l3.6,4l3.6-4H21V2H2V17z" /></g><g><rect x="5" y="8" width="3" height="3" /></g><g><rect x="10" y="8" width="3" height="3" /></g><g><rect x="15" y="8" width="3" height="3" /></g></svg>`, russianSupport.subtitles, 'субтитры');

                lime.appendChild(peach);
                lime.appendChild(plum);
                lime.appendChild(apricot);

                lemon.style.position = 'relative';
                lemon.appendChild(lime);

                const appName = document.querySelector('#appHubAppName.apphub_AppName');
                if (appName) {
                    appName.style.maxWidth = '530px';
                    appName.style.overflow = 'hidden';
                    appName.style.textOverflow = 'ellipsis';
                    appName.style.whiteSpace = 'nowrap';
                    appName.title = appName.textContent;
                }
            }

            const settings = {
                showTotalReviews: true,
                showNonChineseReviews: true,
                showRussianReviews: true
            };

            function fetchReviews(appid, language, callback) {
                let url = `https://store.steampowered.com/appreviews/${appid}?json=1&language=${language}&purchase_type=all`;
                GM_xmlhttpRequest({
                    method: "GET",
                    url: url,
                    onload: function(response) {
                        let data = JSON.parse(response.responseText);
                        callback(data);
                    }
                });
            }

            function fetchRussianReviewsHTML(appid, filter, callback) {
                let url = `https://store.steampowered.com/appreviews/${appid}?language=russian&purchase_type=all&filter=${filter}&day_range=365`;
                GM_xmlhttpRequest({
                    method: "GET",
                    url: url,
                    onload: function(response) {
                        let data = JSON.parse(response.responseText);
                        callback(data.html);
                    }
                });
            }

            function addStyles() {
                GM_addStyle(`
        .additional-reviews {
            margin-top: 10px;
        }
        .additional-reviews .user_reviews_summary_row {
            display: flex;
            line-height: 16px;
            cursor: pointer;
            margin-bottom: 5px;
        }
        .additional-reviews .subtitle {
            flex: 1;
            color: #556772;
            font-size: 12px;
        }
        .additional-reviews .summary {
            flex: 3;
            color: #c6d4df;
            font-size: 12px;
            overflow: hidden;
            white-space: nowrap;
            text-overflow: ellipsis;
        }
        .additional-reviews .game_review_summary {
            font-weight: normal;
        }
        .additional-reviews .positive {
            color: #66c0f4;
        }
        .additional-reviews .mixed {
            color: #B9A074;
        }
        .additional-reviews .negative {
            color: #a34c25;
        }
        .additional-reviews .no_reviews {
            color: #929396;
        }
        .additional-reviews .responsive_hidden {
            color: #556772;
            margin-left: 5px;
        }
        .ofxmodal {
            display: none;
            position: fixed;
            z-index: 1000;
            left: 0;
            top: 0;
            width: 100%;
            height: 100%;
            overflow: auto;
            background-color: rgba(0,0,0,0.8);
        }
        .ofxmodal-content {
            background-color: #1b2838;
            margin: 10% auto;
            padding: 20px;
            border: 1px solid #888;
            width: 80%;
            max-width: 800px;
            color: #c6d4df;
            position: relative;
            max-height: 80vh;
            overflow-y: auto;
        }
        .ofxclose {
            color: #aaa;
            position: sticky;
            top: 0;
            float: right;
            font-size: 28px;
            font-weight: bold;
            cursor: pointer;
            background: rgba(0,0,0,0.8);
            padding: 5px 10px;
            border-radius: 5px;
            transition: color 0.2s ease, background 0.2s ease, transform 0.2s ease;
            box-shadow: 0 2px 4px rgba(0,0,0,0.2);
        }
        .ofxclose:hover {
            color: #fff;
            background: #e64a4a;
            transform: scale(1.1);
        }
        .ofxclose:active {
            background: #c43c3c;
            transform: scale(0.95);
        }
        .refresh-button {
            position: left;
            top: 10px;
            right: 50px;
            background: #66c0f4;
            color: #1b2838;
            padding: 10px 20px;
            border: none;
            cursor: pointer;
            z-index: 1001;
            border-radius: 2px;
            transition: background 0.2s ease, color 0.2s ease;
        }
        .refresh-button:hover {
            background: #45b0e6;
            color: #fff;
        }
        .refresh-button:active {
            background: #329cd4;
            transform: translateY(1px);
        }
    `);
            }

            function formatNumber(number) {
                return number.toLocaleString('en-US');
            }

            function getReviewClass(percent, totalReviews) {
                if (totalReviews === 0) return 'no_reviews';
                if (percent >= 70) return 'positive';
                if (percent >= 40) return 'mixed';
                if (percent >= 1) return 'negative';
                return 'negative';
            }

            function addLoadButton() {
                let reviewsContainer = document.querySelector('.user_reviews');
                if (reviewsContainer) {
                    let additionalReviews = document.createElement('div');
                    additionalReviews.className = 'additional-reviews';

                    additionalReviews.innerHTML = `
            <div class="user_reviews_summary_row" id="load-reviews-button">
                <div class="subtitle column all">Доп. обзоры:</div>
                <div class="summary column">
                    <span class="game_review_summary no_reviews">Загрузить</span>
                </div>
            </div>
        `;

                    reviewsContainer.appendChild(additionalReviews);

                    document.getElementById('load-reviews-button').addEventListener('click', function() {
                        loadAdditionalReviews();
                    });

                    if (scriptsConfig.autoLoadReviews) {
                        loadAdditionalReviews();
                    }
                }
            }

            function loadAdditionalReviews() {
                let appid = window.location.pathname.match(/\/app\/(\d+)/)[1];
                let languages = [];
                let data = {};

                let loadButton = document.getElementById('load-reviews-button');
                if (loadButton) {
                    loadButton.querySelector('.game_review_summary').textContent = 'Загрузка...';
                }

                if (settings.showTotalReviews || settings.showNonChineseReviews) {
                    languages.push('all');
                }
                if (settings.showNonChineseReviews) {
                    languages.push('schinese');
                }
                if (settings.showRussianReviews) {
                    languages.push('russian');
                }

                languages.forEach(language => {
                    fetchReviews(appid, language, (response) => {
                        data[language] = response;
                        if (Object.keys(data).length === languages.length) {
                            displayAdditionalReviews(data['all'], data['schinese'], data['russian']);

                            if (loadButton) {
                                loadButton.querySelector('.game_review_summary').textContent = 'Загрузить';
                            }
                        }
                    });
                });
            }

            function displayAdditionalReviews(allData, schineseData, russianData) {
                let allReviews = allData ? allData.query_summary : null;
                let schineseReviews = schineseData ? schineseData.query_summary : null;
                let russianReviews = russianData ? russianData.query_summary : null;

                let additionalReviews = document.querySelector('.additional-reviews');
                if (additionalReviews) {
                    additionalReviews.innerHTML = '';

                    if (settings.showTotalReviews && allReviews) {
                        let allPercent = allReviews.total_reviews > 0 ? Math.round((allReviews.total_positive / allReviews.total_reviews) * 100) : 0;
                        let allClass = getReviewClass(allPercent, allReviews.total_reviews);
                        additionalReviews.innerHTML += `
                <div class="user_reviews_summary_row">
                    <div class="subtitle column all">Тотальные:</div>
                    <div class="summary column">
                        <span class="game_review_summary ${allClass}">${allPercent}% из ${formatNumber(allReviews.total_reviews)} положительные</span>
                    </div>
                </div>
            `;
                    }

                    if (settings.showNonChineseReviews && allReviews && schineseReviews) {
                        let schintotalrev = allReviews.total_reviews - schineseReviews.total_reviews;
                        let schintotapos = allReviews.total_positive - schineseReviews.total_positive;
                        let schinpercent = schintotalrev > 0 ? Math.round((schintotapos / schintotalrev) * 100) : 0;
                        let schinClass = getReviewClass(schinpercent, schintotalrev);
                        additionalReviews.innerHTML += `
                <div class="user_reviews_summary_row">
                    <div class="subtitle column all">Безкитайские:</div>
                    <div class="summary column">
                        <span class="game_review_summary ${schinClass}">${schinpercent}% из ${formatNumber(schintotalrev)} положительные</span>
                    </div>
                </div>
            `;
                    }

                    if (settings.showRussianReviews && russianReviews) {
                        let rustotalrev = russianReviews.total_reviews;
                        let ruspositive = russianReviews.total_positive;
                        let ruspercent = rustotalrev > 0 ? Math.round((ruspositive / rustotalrev) * 100) : 0;
                        let rusClass = getReviewClass(ruspercent, rustotalrev);
                        additionalReviews.innerHTML += `
                <div class="user_reviews_summary_row" id="russian-reviews-row">
                    <div class="subtitle column all">Русские:</div>
                    <div class="summary column">
                        <span class="game_review_summary ${rusClass}">${ruspercent}% из ${formatNumber(rustotalrev)} положительные</span>
                    </div>
                </div>
            `;

                        document.getElementById('russian-reviews-row').addEventListener('click', function() {
                            openModal();
                        });
                    }
                }
            }

            function openModal() {
                let ofxmodal = document.createElement('div');
                ofxmodal.className = 'ofxmodal';
                ofxmodal.innerHTML = `
        <div class="ofxmodal-content">
            <span class="ofxclose">×</span>
            <button class="refresh-button" id="refresh-reviews">Загрузить актуальные</button>
            <div id="reviews-container"></div>
        </div>
    `;
                document.body.appendChild(ofxmodal);

                ofxmodal.querySelector('.ofxclose').addEventListener('click', function() {
                    ofxmodal.style.display = 'none';
                });

                ofxmodal.querySelector('#refresh-reviews').addEventListener('click', function() {
                    refreshReviews(ofxmodal);
                });

                ofxmodal.style.display = 'block';

                loadReviews(ofxmodal, 'all');
            }

            function refreshReviews(ofxmodal) {
                ofxmodal.querySelector('#reviews-container').innerHTML = '';
                loadReviews(ofxmodal, 'recent');
            }

            function loadReviews(ofxmodal, filter) {
                fetchRussianReviewsHTML(window.location.pathname.match(/\/app\/(\d+)/)[1], filter, function(html) {
                    ofxmodal.querySelector('#reviews-container').innerHTML = html;
                    ofxmodal.querySelector('#LoadMoreReviewsall')?.remove();
                    ofxmodal.querySelector('#LoadMoreReviewsrecent')?.remove();
                });
            }

            function main() {
                addStyles();
                addRussianIndicators();
                addLoadButton();
            }

            main();
        })();
    }

    // Скрипт для страницы игры (HLTB; получение сведений о времени прохождения) | https://store.steampowered.com/app/*
    if (window.location.pathname.includes('/app/') && scriptsConfig.hltbData) {
        (async function() {
            let hltbBlock = document.createElement('div');
            hltbBlock.style.position = 'absolute';
            hltbBlock.style.top = '232px';
            hltbBlock.style.left = '334px';
            hltbBlock.style.width = '30px';
            hltbBlock.style.height = '30px';
            hltbBlock.style.background = 'rgba(27, 40, 56, 0.95)';
            hltbBlock.style.padding = '15px';
            hltbBlock.style.borderRadius = '4px';
            hltbBlock.style.border = '1px solid #3c3c3c';
            hltbBlock.style.boxShadow = '0 0 10px rgba(0, 0, 0, 0.5)';
            hltbBlock.style.zIndex = '2';
            hltbBlock.style.fontFamily = 'Arial, sans-serif';
            hltbBlock.style.overflow = 'hidden';
            hltbBlock.style.transition = 'all 0.3s ease';

            let triangle = document.createElement('div');
            triangle.className = 'triangle-down';
            triangle.style.position = 'absolute';
            triangle.style.bottom = '5px';
            triangle.style.left = '50%';
            triangle.style.transform = 'translateX(-50%)';
            triangle.style.width = '0';
            triangle.style.height = '0';
            triangle.style.borderLeft = '5px solid transparent';
            triangle.style.borderRight = '5px solid transparent';
            triangle.style.borderTop = '5px solid #67c1f5';
            triangle.style.cursor = 'pointer';
            hltbBlock.appendChild(triangle);

            let title = document.createElement('div');
            title.style.fontSize = '12px';
            title.style.fontWeight = 'bold';
            title.style.color = '#67c1f5';
            title.style.marginBottom = '10px';
            title.textContent = 'HLTB';
            title.style.cursor = 'pointer';
            hltbBlock.appendChild(title);

            let content = document.createElement('div');
            content.style.fontSize = '14px';
            content.style.color = '#c6d4df';
            content.style.display = 'none';
            content.style.whiteSpace = 'auto';
            content.style.padding = '0 0';
            hltbBlock.appendChild(content);

            const updateHltbPosition = () => {
                const russianIndicators = document.querySelector('#gameHeaderImageCtn > div[style*="position: absolute; top: -10px; left: calc(100% + 10px);"]');

                if (scriptsConfig.gamePage && russianIndicators) {
                    hltbBlock.style.top = `${russianIndicators.offsetTop + russianIndicators.offsetHeight + 16}px`;
                } else {
                    hltbBlock.style.top = '0px';
                }

                hltbBlock.style.left = '334px';
            };

            const initHltbObservers = () => {
                if (scriptsConfig.gamePage) {
                    const indicatorsObserver = new MutationObserver(() => {
                        updateHltbPosition();
                    });

                    const indicators = document.querySelector('#gameHeaderImageCtn > div[style*="position: absolute; top: -10px; left: calc(100% + 10px);"]');
                    if (indicators) {
                        indicatorsObserver.observe(indicators, {
                            attributes: true,
                            childList: true,
                            subtree: true
                        });
                    }
                }

                const generalObserver = new MutationObserver((mutations) => {
                    mutations.forEach(mutation => {
                        if (mutation.type === 'childList') {
                            updateHltbPosition();
                        }
                    });
                });

                generalObserver.observe(document.querySelector('#gameHeaderImageCtn'), {
                    childList: true,
                    subtree: true
                });
            };

            document.querySelector('#gameHeaderImageCtn').appendChild(hltbBlock);
            initHltbObservers();
            updateHltbPosition();


            const handleClick = async function() {
                if (content.style.display === 'none') {
                    hltbBlock.style.transition = 'width 0.3s ease, height 0.3s ease';

                    updateHltbPosition();
                    await new Promise(resolve => setTimeout(resolve, 50));

                    hltbBlock.style.width = '200px';
                    hltbBlock.style.height = '40px';
                    await new Promise(resolve => setTimeout(resolve, 300));

                    content.textContent = 'Ищем в базе...';
                    content.style.display = 'block';

                    triangle.classList.remove('triangle-down');
                    triangle.classList.add('triangle-up');
                    triangle.style.borderTop = 'none';
                    triangle.style.borderBottom = '5px solid #67c1f5';

                    let gameName = getGameName();
                    let gameNameNormalized = normalizeGameName(gameName);

                    let orangutanFetchUrl = 'https://umadb.ro/hltb/fetch.php';
                    let orangutanHltbUrl = "https://howlongtobeat.com";

                    try {
                        const response = await new Promise((resolve, reject) => {
                            GM_xmlhttpRequest({
                                method: "GET",
                                url: orangutanFetchUrl,
                                onload: resolve,
                                onerror: reject
                            });
                        });

                        if (response.status === 200) {
                            const key = response.responseText.trim();
                            orangutanHltbUrl = "https://howlongtobeat.com" + key;
                        } else {
                            throw new Error('Failed to fetch key. Status: ' + response.status);
                        }
                    } catch (error) {
                        content.textContent = 'Ошибка при получении ключа.';
                        return;
                    }

                    let chimpQuery = '{"searchType":"games","searchTerms":[' + gameNameNormalized + '],"searchPage":1,"size":20,"searchOptions":{"games":{"userId":0,"platform":"","sortCategory":"popular","rangeCategory":"main","rangeTime":{"min":null,"max":null},"gameplay":{"perspective":"","flow":"","genre":"","difficulty":""},"rangeYear":{"min":"","max":""},"modifier":""},"users":{"sortCategory":"postcount"},"lists":{"sortCategory":"follows"},"filter":"","sort":0,"randomizer":0},"useCache":true}';

                    GM_xmlhttpRequest({
                        method: "POST",
                        url: orangutanHltbUrl,
                        data: chimpQuery,
                        headers: {
                            "Content-Type": "application/json",
                            "origin": "https://howlongtobeat.com",
                            "referer": "https://howlongtobeat.com/"
                        },
                        onload: async function(response) {
                            let baboonData = {
                                count: 0,
                                data: []
                            };
                            if (!response.responseText.includes("<title>HowLongToBeat - 404</title>")) {
                                try {
                                    baboonData = JSON.parse(response.responseText);
                                } catch (e) {
                                    content.textContent = 'Ошибка при обработке данных.';
                                    return;
                                }
                            }

                            if (baboonData.count === 0 && /[а-яё]/i.test(gameName)) {
                                const appId = window.location.pathname.split('/')[2];
                                const steamApiUrl = `https://api.steampowered.com/IStoreBrowseService/GetItems/v1?input_json={"ids": [{"appid": ${appId}}], "context": {"language": "english", "country_code": "US", "steam_realm": 1}, "data_request": {"include_assets": true}}`;

                                try {
                                    const steamResponse = await new Promise((resolve, reject) => {
                                        GM_xmlhttpRequest({
                                            method: "GET",
                                            url: steamApiUrl,
                                            onload: resolve,
                                            onerror: reject
                                        });
                                    });

                                    if (steamResponse.status === 200) {
                                        const steamData = JSON.parse(steamResponse.responseText);
                                        const englishName = steamData.response.store_items[0].name;

                                        if (englishName) {
                                            gameName = englishName;
                                            gameNameNormalized = normalizeGameName(gameName);
                                            chimpQuery = '{"searchType":"games","searchTerms":[' + gameNameNormalized + '],"searchPage":1,"size":20,"searchOptions":{"games":{"userId":0,"platform":"","sortCategory":"popular","rangeCategory":"main","rangeTime":{"min":null,"max":null},"gameplay":{"perspective":"","flow":"","genre":"","difficulty":""},"rangeYear":{"min":"","max":""},"modifier":""},"users":{"sortCategory":"postcount"},"lists":{"sortCategory":"follows"},"filter":"","sort":0,"randomizer":0},"useCache":true}';

                                            const secondResponse = await new Promise((resolve, reject) => {
                                                GM_xmlhttpRequest({
                                                    method: "POST",
                                                    url: orangutanHltbUrl,
                                                    data: chimpQuery,
                                                    headers: {
                                                        "Content-Type": "application/json",
                                                        "origin": "https://howlongtobeat.com",
                                                        "referer": "https://howlongtobeat.com/"
                                                    },
                                                    onload: resolve,
                                                    onerror: reject
                                                });
                                            });

                                            if (secondResponse.status === 200) {
                                                baboonData = JSON.parse(secondResponse.responseText);
                                            }
                                        }
                                    }
                                } catch (error) {
                                    console.error('Ошибка при запросе к Steam API:', error);
                                }
                            }

                            if (baboonData.count > 0) {
                                const matches = findPossibleMatches(gameName, baboonData.data);
                                if (matches.length > 0) {
                                    renderPossibleMatches(matches);
                                    hltbBlock.style.height = `${content.scrollHeight + 30}px`;
                                    return;
                                }
                            }

                            renderContent(baboonData.data[0]);
                            hltbBlock.style.height = `${content.scrollHeight + 30}px`;
                        },
                        onerror: function(error) {
                            content.textContent = 'Ошибка при запросе к HLTB.';
                        },
                        ontimeout: function() {
                            content.textContent = 'Тайм-аут при запросе к HLTB.';
                        },
                        timeout: 10000
                    });
                } else {
                    content.style.display = 'none';
                    hltbBlock.style.height = '30px';
                    hltbBlock.style.width = '30px';

                    triangle.classList.remove('triangle-up');
                    triangle.classList.add('triangle-down');
                    triangle.style.borderBottom = 'none';
                    triangle.style.borderTop = '5px solid #67c1f5';
                }
            };

            title.onclick = handleClick;
            triangle.onclick = handleClick;

            window.addEventListener('resize', updateHltbPosition);

            function normalizeGameName(name) {
                return name
                    .normalize("NFD")
                    .replace(/[\u0300-\u036f]/g, "")
                    .replace(/[^a-zа-яё0-9 _'\-!]/gi, '')
                    .toLowerCase()
                    .split(/\s+/)
                    .map(word => `"${word}"`)
                    .join(",");
            }

            function findPossibleMatches(gameName, data) {
                const cleanGameName = gameName
                    .normalize("NFD")
                    .replace(/[\u0300-\u036f]/g, "")
                    .replace(/[^a-zа-яё0-9 _'\-!]/gi, '')
                    .toLowerCase();

                return data
                    .map(item => {
                        const cleanItemName = item.game_name
                            .normalize("NFD")
                            .replace(/[\u0300-\u036f]/g, "")
                            .replace(/[^a-zа-яё0-9 _'\-!]/gi, '')
                            .toLowerCase();

                        const similarity = calculateSimilarity(cleanGameName, cleanItemName);
                        const startsWith = cleanItemName.startsWith(cleanGameName);

                        return {
                            ...item,
                            percentage: similarity,
                            startsWith: startsWith
                        };
                    })
                    .filter(item => item.percentage > 50 || item.startsWith)
                    .sort((a, b) => {
                        if (a.startsWith && !b.startsWith) return -1;
                        if (!a.startsWith && b.startsWith) return 1;
                        return b.percentage - a.percentage;
                    })
                    .slice(0, 5);
            }

            function calculateSimilarity(str1, str2) {
                const len = Math.max(str1.length, str2.length);
                if (len === 0) return 100;
                const distance = levenshteinDistance(str1, str2);
                return Math.round(((len - distance) / len) * 100);
            }

            function levenshteinDistance(str1, str2) {
                const m = str1.length;
                const n = str2.length;
                const dp = Array.from({
                    length: m + 1
                }, () => Array(n + 1).fill(0));

                for (let i = 0; i <= m; i++) {
                    for (let j = 0; j <= n; j++) {
                        if (i === 0) {
                            dp[i][j] = j;
                        } else if (j === 0) {
                            dp[i][j] = i;
                        } else {
                            dp[i][j] = Math.min(
                                dp[i - 1][j - 1] + (str1[i - 1] === str2[j - 1] ? 0 : 1),
                                dp[i - 1][j] + 1,
                                dp[i][j - 1] + 1
                            );
                        }
                    }
                }

                return dp[m][n];
            }

            function getTextWidth(text, font) {
                const canvas = document.createElement('canvas');
                const context = canvas.getContext('2d');
                context.font = font;
                const metrics = context.measureText(text);
                return metrics.width;
            }

            function renderPossibleMatches(matches) {
                content.innerHTML = '';

                const title = document.createElement('div');
                title.textContent = 'Возможные совпадения:';
                title.style.color = '#67c1f5';
                title.style.marginBottom = '10px';
                content.appendChild(title);

                const list = document.createElement('ul');
                list.style.paddingLeft = '15px';
                list.style.marginTop = '5px';
                list.style.marginBottom = '0';

                matches.forEach(match => {
                    const li = document.createElement('li');
                    li.style.marginBottom = '8px';

                    const link = document.createElement('a');
                    link.href = '#';
                    link.textContent = `${match.game_name} (${match.percentage}%)`;
                    link.style.color = '#c6d4df';
                    link.style.wordBreak = 'break-word';
                    link.style.textDecoration = 'none';
                    link.onclick = () => {
                        renderContent(match);
                        hltbBlock.style.height = `${content.scrollHeight + 30}px`;
                        return false;
                    };

                    li.appendChild(link);
                    list.appendChild(li);
                });

                const noMatch = document.createElement('li');
                noMatch.style.marginBottom = '8px';

                const noMatchLink = document.createElement('a');
                noMatchLink.href = '#';
                noMatchLink.textContent = 'Ничего не подходит';
                noMatchLink.style.color = '#c6d4df';
                noMatchLink.style.wordBreak = 'break-word';
                noMatchLink.style.textDecoration = 'none';
                noMatchLink.onclick = () => {
                    renderContent(null);
                    hltbBlock.style.height = `${content.scrollHeight + 30}px`;
                    return false;
                };

                noMatch.appendChild(noMatchLink);
                list.appendChild(noMatch);

                content.appendChild(list);

                let maxWidth = 0;
                content.querySelectorAll('a').forEach(link => {
                    const text = link.textContent;
                    const font = window.getComputedStyle(link).font;
                    const width = getTextWidth(text, font);
                    if (width > maxWidth) maxWidth = width;
                });

                hltbBlock.style.width = `${Math.max(maxWidth + 40, 250)}px`;
            }

            function renderContent(entry) {
                content.innerHTML = '';

                if (!entry) {
                    content.textContent = 'Игра не найдена в базе HLTB';
                    return;
                }

                const titleLink = document.createElement('a');
                titleLink.href = `https://howlongtobeat.com/game/${entry.game_id}`;
                titleLink.target = '_blank';
                titleLink.textContent = entry.game_name || 'Без названия';
                titleLink.style.color = '#67c1f5';
                titleLink.style.wordBreak = 'break-word';
                content.appendChild(titleLink);

                const list = document.createElement('ul');
                list.style.paddingLeft = '15px';
                list.style.marginTop = '5px';
                list.style.marginBottom = '0';

                const times = [{
                        label: 'Только сюжет',
                        time: entry.comp_main,
                        count: entry.comp_main_count
                    },
                    {
                        label: 'Сюжет + доп.',
                        time: entry.comp_plus,
                        count: entry.comp_plus_count
                    },
                    {
                        label: 'Комплеционист',
                        time: entry.comp_100,
                        count: entry.comp_100_count
                    },
                    {
                        label: 'Все стили',
                        time: entry.comp_all,
                        count: entry.comp_all_count
                    }
                ];

                times.forEach(time => {
                    const li = document.createElement('li');
                    li.style.marginBottom = '8px';

                    const timeText = time.time ? formatTime(time.time) : "X";
                    li.innerHTML = `${time.label}: <span style="color: #fff;">${timeText}</span> (${time.count} чел.)`;

                    list.appendChild(li);
                });

                content.appendChild(list);

                let maxWidth = 0;
                content.querySelectorAll('li').forEach(child => {
                    const text = child.textContent;
                    const font = window.getComputedStyle(child).font;
                    const width = getTextWidth(text, font);
                    if (width > maxWidth) maxWidth = width;
                });

                hltbBlock.style.width = `${Math.max(maxWidth + 30, 200)}px`;
                hltbBlock.style.whiteSpace = 'nowrap';
            }

            function formatTime(seconds) {
                const hours = Math.floor(seconds / 3600);
                const minutes = Math.round((seconds % 3600) / 60);

                if (hours === 0) {
                    return `${minutes} м.`;
                } else if (hours + (minutes / 60) >= hours + 0.5) {
                    return `${hours + 1} ч.`;
                } else {
                    return `${hours} ч.`;
                }
            }

            function getGameName() {
                return document.querySelector('.apphub_AppName').textContent
                    .normalize("NFD")
                    .replace(/[\u0300-\u036f]/g, "")
                    .replace(/[’]/g, "'")
                    .replace(/[^a-zA-Zа-яёА-ЯЁ0-9 _'\-!]/g, '')
                    .trim()
                    .toLowerCase();
            }

            if (scriptsConfig.autoExpandHltb) {
                handleClick();
            }
        })();
    }

    // Скрипт для страницы игры (Время друзей & Достижения) | https://store.steampowered.com/app/*
    if (window.location.pathname.includes('/app/') && scriptsConfig.friendsPlaytime) {
        (async function() {
            const statsBlock = document.createElement('div');
            statsBlock.style.position = 'absolute';
            statsBlock.style.top = '0px';
            statsBlock.style.left = '406px';
            statsBlock.style.width = '30px';
            statsBlock.style.height = '30px';
            statsBlock.style.background = 'rgba(27, 40, 56, 0.95)';
            statsBlock.style.padding = '15px';
            statsBlock.style.borderRadius = '4px';
            statsBlock.style.border = '1px solid #3c3c3c';
            statsBlock.style.boxShadow = '0 0 10px rgba(0, 0, 0, 0.5)';
            statsBlock.style.zIndex = '1';
            statsBlock.style.fontFamily = 'Arial, sans-serif';
            statsBlock.style.overflow = 'hidden';
            statsBlock.style.transition = 'all 0.3s ease';

            const triangle = document.createElement('div');
            triangle.style.position = 'absolute';
            triangle.style.bottom = '5px';
            triangle.style.left = '50%';
            triangle.style.transform = 'translateX(-50%)';
            triangle.style.width = '0';
            triangle.style.height = '0';
            triangle.style.borderLeft = '5px solid transparent';
            triangle.style.borderRight = '5px solid transparent';
            triangle.style.borderTop = '5px solid #67c1f5';
            triangle.style.cursor = 'pointer';
            statsBlock.appendChild(triangle);

            const title = document.createElement('div');
            title.style.display = 'flex';
            title.style.alignItems = 'center';
            title.style.marginBottom = '7px';
            title.style.cursor = 'pointer';

            const combinedImg = document.createElement('div');
            combinedImg.style.width = '29px';
            combinedImg.style.height = '29px';
            combinedImg.style.backgroundImage = 'url(https://gist.githubusercontent.com/0wn3dg0d/9c259eebc40a1e97397ccf3da7ee7bd6/raw/SUEftach.png)';
            combinedImg.style.backgroundSize = 'contain';
            combinedImg.style.backgroundPosition = 'center';


            title.appendChild(combinedImg);
            statsBlock.appendChild(title);


            const content = document.createElement('div');
            content.style.fontSize = '14px';
            content.style.color = '#c6d4df';
            content.style.display = 'none';
            content.style.padding = '0';
            statsBlock.appendChild(content);

            const toggleBlock = async () => {
                if (content.style.display === 'none') {
                    statsBlock.style.width = '250px';
                    statsBlock.style.height = '60px';
                    content.style.display = 'block';
                    content.textContent = 'Загрузка...';
                    triangle.style.borderTop = 'none';
                    triangle.style.borderBottom = '5px solid #67c1f5';

                    try {
                        const friendsData = await loadFriendsData();
                        const achievementsData = await loadAchievementsData();
                        content.innerHTML = '';

                        const friendsTitle = document.createElement('div');
                        friendsTitle.style.fontSize = '12px';
                        friendsTitle.style.fontWeight = 'bold';
                        friendsTitle.style.color = '#67c1f5';
                        friendsTitle.style.marginBottom = '5px';
                        friendsTitle.textContent = 'ВРЕМЯ ДРУЗЕЙ';
                        content.appendChild(friendsTitle);

                        if (friendsData.length > 0) {
                            const maxHours = Math.max(...friendsData.map(f => f.hours));
                            const minHours = Math.min(...friendsData.map(f => f.hours));
                            const avgHours = friendsData.reduce((sum, f) => sum + f.hours, 0) / friendsData.length;
                            const maxPlayers = friendsData.filter(f => f.hours === maxHours);

                            const maxEl = document.createElement('div');
                            maxEl.style.marginBottom = '4px';
                            maxEl.innerHTML = `<span style="color: #67c1f5;">Макс:</span> ${maxHours.toFixed(1)} ч.`;

                            if (maxPlayers.length > 0) {
                                maxEl.innerHTML += ` (${maxPlayers.map(p =>
                            `<a href="${p.profile}" target="_blank" style="color: #c6d4df; text-decoration: none;">${p.name}</a>`
                        ).join(', ')})`;
                            }

                            const avgEl = document.createElement('div');
                            avgEl.style.marginBottom = '4px';
                            avgEl.innerHTML = `<span style="color: #67c1f5;">Среднее:</span> ${avgHours.toFixed(1)} ч. (${friendsData.length} чел.)`;

                            const minEl = document.createElement('div');
                            minEl.innerHTML = `<span style="color: #67c1f5;">Минимальное:</span> ${minHours.toFixed(1)} ч.`;

                            content.append(maxEl, avgEl, minEl);
                        } else {
                            const noData = document.createElement('div');
                            noData.textContent = 'Друзья не играли';
                            noData.style.marginBottom = '12px';
                            content.appendChild(noData);
                        }

                        const achTitle = document.createElement('div');
                        achTitle.style.fontSize = '12px';
                        achTitle.style.fontWeight = 'bold';
                        achTitle.style.color = '#67c1f5';
                        achTitle.style.margin = '16px 0 5px 0';
                        achTitle.textContent = 'ГЛОБАЛЬНЫЕ ДОСТИЖЕНИЯ';
                        content.appendChild(achTitle);

                        if (achievementsData.hasAchievements) {
                            const platinumEl = document.createElement('div');
                            platinumEl.style.marginBottom = '4px';
                            platinumEl.innerHTML = `<span style="color: #67c1f5;">Платина:</span> ${achievementsData.platinumPercent}%`;

                            const averageEl = document.createElement('div');
                            averageEl.innerHTML = `<span style="color: #67c1f5;">Средний прогресс:</span> ${achievementsData.averageAdjustedPercent}%`;

                            content.append(platinumEl, averageEl);
                        } else {
                            const noAch = document.createElement('div');
                            noAch.textContent = achievementsData.error === 'Нет достижений' ?
                                'Достижений нет' :
                                achievementsData.error;
                            noAch.style.marginBottom = '12px';
                            content.appendChild(noAch);
                        }

                        statsBlock.style.height = `${content.scrollHeight + 38}px`;

                    } catch (error) {
                        content.textContent = 'Ошибка загрузки';
                        statsBlock.style.height = '60px';
                    }
                } else {
                    content.style.display = 'none';
                    statsBlock.style.height = '30px';
                    statsBlock.style.width = '30px';
                    triangle.style.borderBottom = 'none';
                    triangle.style.borderTop = '5px solid #67c1f5';
                }
            };

            async function loadFriendsData() {
                try {
                    const friendsLink = document.querySelector('.recommendation_reasons a[href*="friendsthatplay"]');
                    if (!friendsLink) return [];

                    const response = await new Promise((resolve, reject) => {
                        GM_xmlhttpRequest({
                            method: "GET",
                            url: friendsLink.href,
                            onload: resolve,
                            onerror: reject,
                            timeout: 5000
                        });
                    });

                    const parser = new DOMParser();
                    const doc = parser.parseFromString(response.responseText, 'text/html');

                    return Array.from(doc.querySelectorAll('.friendBlockContent'))
                        .map(block => {
                            const timeText = block.querySelector('.friendSmallText')?.textContent;
                            const hoursMatch = timeText?.match(/(\d+[,.]?\d*)\s*ч/);
                            return {
                                name: block.firstChild.textContent.trim(),
                                hours: hoursMatch ? parseFloat(hoursMatch[1].replace(',', '.')) : 0,
                                profile: block.closest('.friendBlock').querySelector('a').href
                            };
                        })
                        .filter(f => f.hours > 0);

                } catch (error) {
                    return [];
                }
            }

            async function loadAchievementsData() {
                try {
                    const appIdMatch = window.location.pathname.match(/\/app\/(\d+)/);
                    if (!appIdMatch) return {
                        hasAchievements: false,
                        error: 'Не найден App ID'
                    };

                    const appId = appIdMatch[1];
                    const achievementsUrl = `https://steamcommunity.com/stats/${appId}/achievements/`;

                    const response = await new Promise((resolve, reject) => {
                        GM_xmlhttpRequest({
                            method: "GET",
                            url: achievementsUrl,
                            onload: resolve,
                            onerror: reject,
                            timeout: 8000
                        });
                    });

                    if (response.status !== 200) return {
                        hasAchievements: false,
                        error: 'Ошибка загрузки страницы'
                    };

                    const parser = new DOMParser();
                    const doc = parser.parseFromString(response.responseText, 'text/html');

                    if (doc.querySelector('.no_achievements_message')) {
                        return {
                            hasAchievements: false,
                            error: 'Достижения отсутствуют'
                        };
                    }

                    const percentElements = doc.querySelectorAll('.achievePercent');
                    if (percentElements.length === 0) return {
                        hasAchievements: false,
                        error: 'Достижения отсутствуют'
                    };

                    const percents = Array.from(percentElements)
                        .map(el => {
                            const text = el.textContent.trim();
                            return parseFloat(text.replace('%', '')) || 0;
                        })
                        .filter(p => p > 0);

                    if (percents.length === 0) return {
                        hasAchievements: false,
                        error: 'Нет данных'
                    };

                    const maxPercent = Math.max(...percents);
                    const minPercent = Math.min(...percents);
                    const adjustment = 100 - maxPercent;
                    const adjustedPercents = percents.map(p => p + adjustment);
                    const averageAdjusted = adjustedPercents.reduce((sum, p) => sum + p, 0) / adjustedPercents.length;

                    return {
                        hasAchievements: true,
                        platinumPercent: (minPercent + adjustment).toFixed(1),
                        averageAdjustedPercent: averageAdjusted.toFixed(1),
                    };

                } catch (error) {
                    return {
                        hasAchievements: false,
                        error: 'Ошибка соединения'
                    };
                }
            }

            title.addEventListener('click', toggleBlock);
            triangle.addEventListener('click', toggleBlock);

            document.querySelector('#gameHeaderImageCtn').appendChild(statsBlock);

            if (scriptsConfig.autoExpandFriends) {
                toggleBlock();
            }
        })();
    }

    // Скрипт для страницы игры (Ранний доступ) | https://store.steampowered.com/app/*
    if (window.location.pathname.includes('/app/') && scriptsConfig.earlyaccdata) {
        (function() {
            'use strict';

            const EAORDATE_STORAGE_KEY = 'USE_EarlyAccess_ordateData';
            const EAORDATE_URL = 'https://gist.githubusercontent.com/0wn3dg0d/58a8e35f3d34014ea749a22d02f7e203/raw/eaordate.json';
            const CACHE_DURATION = 180 * 24 * 60 * 60 * 1000; // 6 месяцев

            const getYearForm = (n) => {
                n = Math.abs(n) % 100;
                const n1 = n % 10;
                if (n > 10 && n < 20) return 'лет';
                if (n1 === 1) return 'год';
                if (n1 >= 2 && n1 <= 4) return 'года';
                return 'лет';
            };

            const getMonthForm = (n) => {
                n = Math.abs(n) % 100;
                const n1 = n % 10;
                if (n > 10 && n < 20) return 'месяцев';
                if (n1 === 1) return 'месяц';
                if (n1 >= 2 && n1 <= 4) return 'месяца';
                return 'месяцев';
            };

            const parseSteamDate = (dateStr) => {
                const numericParts = dateStr.split('.');
                if (numericParts.length === 3) {
                    const day = parseInt(numericParts[0], 10);
                    const month = parseInt(numericParts[1], 10) - 1;
                    const year = parseInt(numericParts[2], 10);
                    return new Date(year, month, day);
                }

                const monthsMap = {
                    'янв': 0,
                    'фев': 1,
                    'мар': 2,
                    'апр': 3,
                    'мая': 4,
                    'июн': 5,
                    'июл': 6,
                    'авг': 7,
                    'сен': 8,
                    'окт': 9,
                    'ноя': 10,
                    'дек': 11
                };

                const cleanedStr = dateStr.replace(/\./g, '');
                const [day, monthNameRaw, year] = cleanedStr.split(' ');
                const monthName = monthNameRaw.substring(0, 3);
                return new Date(parseInt(year), monthsMap[monthName], parseInt(day));
            };

            const fetchOrdateData = async () => {
                const cachedData = GM_getValue(EAORDATE_STORAGE_KEY, null);
                if (cachedData && Date.now() - cachedData.timestamp < CACHE_DURATION) {
                    return cachedData.data;
                }

                return new Promise(resolve => {
                    GM_xmlhttpRequest({
                        method: 'GET',
                        url: EAORDATE_URL,
                        onload: function(response) {
                            try {
                                const data = JSON.parse(response.responseText);
                                GM_setValue(EAORDATE_STORAGE_KEY, {
                                    timestamp: Date.now(),
                                    data: data
                                });
                                resolve(data);
                            } catch (e) {
                                console.error('Error parsing EAOrdate data:', e);
                                resolve(cachedData?.data || []);
                            }
                        },
                        onerror: () => resolve(cachedData?.data || [])
                    });
                });
            };

            const getAppId = () => {
                const match = window.location.pathname.match(/\/app\/(\d+)/);
                return match ? parseInt(match[1]) : null;
            };

            const createInfoBox = (duration, isReleased) => {
                const infoBox = document.createElement('div');
                Object.assign(infoBox.style, {
                    position: 'absolute',
                    top: '-46px',
                    left: '334px',
                    background: isReleased ? 'rgba(103, 193, 245, 0.15)' : 'rgba(245, 166, 35, 0.15)',
                    padding: '6.5px',
                    borderRadius: '3px',
                    border: `1px solid ${isReleased ? '#2A568E' : '#f5a623'}`,
                    fontSize: '12px',
                    color: '#c6d4df',
                    boxShadow: '0 2px 4px rgba(0, 0, 0, 0.2)',
                    fontFamily: '"Motiva Sans", Arial, sans-serif',
                    zIndex: 3,
                    display: 'inline-block',
                    whiteSpace: 'nowrap'
                });

                let message;
                if (isReleased) {
                    message = duration ?
                        `Вышла спустя ${duration} раннего доступа` :
                        'Игра вышла из раннего доступа (срок неизвестен)';
                } else {
                    message = `В раннем доступе уже ${duration}`;
                }

                infoBox.innerHTML = `
                <div style="display: flex; align-items: center; gap: 8px;">
                    <span style="color: ${isReleased ? '#67c1f5' : '#f5a623'}; font-weight: bold;">
                        ${isReleased ? '➡️' : '⏳'}
                    </span>
                    <span>${message}</span>
                </div>
            `;
                return infoBox;
            };

            const calculateDuration = (startDate, endDate) => {
                let diffMonths = (endDate.getFullYear() - startDate.getFullYear()) * 12 +
                    (endDate.getMonth() - startDate.getMonth());

                if (endDate.getDate() < startDate.getDate()) diffMonths--;

                const years = Math.floor(diffMonths / 12);
                const months = diffMonths % 12;

                const parts = [];
                if (years > 0) parts.push(`${years} ${getYearForm(years)}`);
                if (months > 0) parts.push(`${months} ${getMonthForm(months)}`);

                return parts.length > 0 ? parts.join(' и ') : 'менее месяца';
            };

            const main = async () => {
                const detailsBlock = document.querySelector('#genresAndManufacturer');
                const isStillEarlyAccess = !!document.querySelector('#earlyAccessHeader');
                if (!detailsBlock) return;

                const parseDates = () => {
                    const fullText = detailsBlock.textContent;
                    const dates = {
                        earlyDate: null,
                        releaseDate: null
                    };

                    const earlyMatch = fullText.match(/Дата выпуска в раннем доступе:\s*(\d+\s\S+\s\d{4})/);
                    const releaseMatch = fullText.match(/Дата выхода:\s*(\d+\s\S+\s\d{4})/);

                    if (isStillEarlyAccess && !earlyMatch && releaseMatch) {
                        dates.earlyDate = releaseMatch[1];
                    } else {
                        if (earlyMatch) dates.earlyDate = earlyMatch[1];
                        if (releaseMatch) dates.releaseDate = releaseMatch[1];
                    }
                    return dates;
                };

                const {
                    earlyDate: earlyDateStr,
                    releaseDate: releaseDateStr
                } = parseDates();
                const appid = getAppId();

                if (!earlyDateStr && appid) {
                    const ordateData = await fetchOrdateData();
                    const gameData = ordateData.find(item => item.appid === appid);

                    if (gameData) {
                        try {
                            const ordate = parseSteamDate(gameData.ordate);
                            const releaseDate = releaseDateStr ? parseSteamDate(releaseDateStr) : new Date();

                            if (ordate >= releaseDate) throw new Error('Invalid date order');

                            const duration = calculateDuration(ordate, releaseDate);
                            const infoBox = createInfoBox(duration, true);
                            document.querySelector('.game_header_image_ctn')?.appendChild(infoBox);
                        } catch (e) {
                            const infoBox = createInfoBox(null, true);
                            document.querySelector('.game_header_image_ctn')?.appendChild(infoBox);
                        }
                    }
                    return;
                }

                const earlyDate = earlyDateStr ? parseSteamDate(earlyDateStr) :
                    isStillEarlyAccess ? parseSteamDate(releaseDateStr) : null;
                const releaseDate = releaseDateStr ? parseSteamDate(releaseDateStr) : null;

                if (!earlyDate) return;

                const endDate = isStillEarlyAccess ? new Date() : releaseDate;
                if (!endDate) return;

                try {
                    const duration = calculateDuration(earlyDate, endDate);
                    const infoBox = createInfoBox(duration, !isStillEarlyAccess);
                    document.querySelector('.game_header_image_ctn')?.appendChild(infoBox);
                } catch (e) {
                    console.error('Early access date calculation error:', e);
                }
            };

            main();
        })();
    }

    // Скрипт для получения дополнительной информации об игре при наведении на неё на странице поиска по каталогу | https://store.steampowered.com/search/
    if (scriptsConfig.catalogInfo && window.location.pathname.includes('/search')) {
        (function() {
            'use strict';

            const ALEXANDER_API_URL = "https://api.steampowered.com/IStoreBrowseService/GetItems/v1";
            const HANNIBAL_WAIT_TIME = 2000;
            const CAESAR_VISIBLE_ELEMENTS_SELECTOR = "a.search_result_row[data-ds-appid]";
            const NAPOLEON_HOVER_ELEMENT_SELECTOR = "a.search_result_row";

            let GENghis_collectedAppIds = new Set();
            let ATTILA_tooltip = null;
            let SALADIN_hoverTimer = null;
            let TAMERLAN_hideTimer = null;

            let RUSSIAN_TRANSLATION_CHECKBOX = null;
            let RUSSIAN_VOICE_CHECKBOX = null;
            let NO_RUSSIAN_CHECKBOX = null;
            const STEAM_TAGS_CACHE_KEY = 'SteamEnhancer_TagsCache_v2';
            const STEAM_TAGS_URL = "https://gist.githubusercontent.com/0wn3dg0d/22a351ff4c65e50a9a8af6da360defad/raw/steamrutagsownd.json";
            const OWNED_APPS_CACHE_KEY = 'SteamEnhancer_OwnedApps';
            const USERDATA_URL = 'https://store.steampowered.com/dynamicstore/userdata/';
            const CACHE_DURATION = 24 * 60 * 60 * 1000; // 24 часа

            async function loadSteamTags() {
                const cached = GM_getValue(STEAM_TAGS_CACHE_KEY, {
                    data: null,
                    timestamp: 0
                });
                const now = Date.now();
                const CACHE_DURATION = 744 * 60 * 60 * 1000;

                if (cached.data && (now - cached.timestamp) < CACHE_DURATION) {
                    return cached.data;
                }

                try {
                    const response = await new Promise((resolve, reject) => {
                        GM_xmlhttpRequest({
                            method: "GET",
                            url: STEAM_TAGS_URL,
                            onload: resolve,
                            onerror: reject
                        });
                    });

                    if (response.status === 200) {
                        const data = JSON.parse(response.responseText);
                        GM_setValue(STEAM_TAGS_CACHE_KEY, {
                            data: data,
                            timestamp: now
                        });
                        return data;
                    }
                } catch (e) {
                    console.error('Ошибка загрузки тегов:', e);
                    return cached.data || {};
                }

                return {};
            }

            async function fetchOwnedApps() {
                const cached = GM_getValue(OWNED_APPS_CACHE_KEY, {
                    data: null,
                    timestamp: 0
                });
                const now = Date.now();

                if (cached.data && (now - cached.timestamp) < CACHE_DURATION) {
                    return cached.data;
                }

                try {
                    const response = await new Promise((resolve, reject) => {
                        GM_xmlhttpRequest({
                            method: "GET",
                            url: USERDATA_URL,
                            onload: resolve,
                            onerror: reject
                        });
                    });

                    if (response.status === 200) {
                        const data = JSON.parse(response.responseText);
                        const ownedApps = data.rgOwnedApps || [];
                        GM_setValue(OWNED_APPS_CACHE_KEY, {
                            data: ownedApps,
                            timestamp: now
                        });
                        return ownedApps;
                    }
                } catch (e) {
                    console.error('Ошибка загрузки списка игр:', e);
                    return cached.data || [];
                }

                return [];
            }

            function fetchGameData(appIds) {
                const inputJson = {
                    ids: Array.from(appIds).map(appid => ({
                        appid
                    })),
                    context: {
                        language: "russian",
                        country_code: "US",
                        steam_realm: 1
                    },
                    data_request: {
                        include_assets: true,
                        include_release: true,
                        include_platforms: true,
                        include_all_purchase_options: true,
                        include_screenshots: true,
                        include_trailers: true,
                        include_ratings: true,
                        include_tag_count: true,
                        include_reviews: true,
                        include_basic_info: true,
                        include_supported_languages: true,
                        include_full_description: true,
                        include_included_items: true,
                        included_item_data_request: {
                            include_assets: true,
                            include_release: true,
                            include_platforms: true,
                            include_all_purchase_options: true,
                            include_screenshots: true,
                            include_trailers: true,
                            include_ratings: true,
                            include_tag_count: true,
                            include_reviews: true,
                            include_basic_info: true,
                            include_supported_languages: true,
                            include_full_description: true,
                            include_included_items: true,
                            include_assets_without_overrides: true,
                            apply_user_filters: false,
                            include_links: true
                        },
                        include_assets_without_overrides: true,
                        apply_user_filters: false,
                        include_links: true
                    }
                };

                GM_xmlhttpRequest({
                    method: "GET",
                    url: `${ALEXANDER_API_URL}?input_json=${encodeURIComponent(JSON.stringify(inputJson))}`,
                    onload: function(response) {
                        const data = JSON.parse(response.responseText);
                        processGameData(data);
                    }
                });
            }

            async function processGameData(data) {
                const ownedApps = await fetchOwnedApps();
                const items = data.response.store_items;
                const dlcFilterActive = document.querySelector('[data-param="your_dlc"] .tab_filter_control')?.classList.contains('checked');

                items.forEach(item => {
                    const appId = item.id;
                    const gameElement = document.querySelector(`a.search_result_row[data-ds-appid="${appId}"]`);
                    if (gameElement) {
                        const gameData = {
                            is_early_access: item.is_early_access,
                            review_count: item.reviews?.summary_filtered?.review_count,
                            percent_positive: item.reviews?.summary_filtered?.percent_positive,
                            short_description: item.basic_info?.short_description,
                            publishers: item.basic_info?.publishers?.map(p => p.name).join(", "),
                            developers: item.basic_info?.developers?.map(d => d.name).join(", "),
                            franchises: item.basic_info?.franchises?.map(f => f.name).join(", "),
                            tagids: item.tagids || [],
                            language_support_russian: item.supported_languages?.find(lang => lang.elanguage === 8),
                            language_support_english: item.supported_languages?.find(lang => lang.elanguage === 0),
                            type: item.type,
                            parent_appid: item.related_items?.parent_appid
                        };

                        gameElement.dataset.gameInfo = JSON.stringify(gameData);
                        applyRussianLanguageFilter(gameElement);

                        if (item.type === 4 && item.related_items?.parent_appid && ownedApps.includes(item.related_items.parent_appid)) {
                            gameElement.classList.add('es_highlighted_dlcforya');
                        }

                        if (dlcFilterActive) {
                            applyDlcFilter(gameElement, true);
                        }
                    }
                });
            }

            function collectAndFetchAppIds() {
                const visibleElements = document.querySelectorAll(CAESAR_VISIBLE_ELEMENTS_SELECTOR);
                const newAppIds = new Set();

                visibleElements.forEach(element => {
                    const appId = element.dataset.dsAppid;
                    if (!GENghis_collectedAppIds.has(appId)) {
                        newAppIds.add(parseInt(appId, 10));
                        GENghis_collectedAppIds.add(appId);
                    }
                });

                if (newAppIds.size > 0) {
                    fetchGameData(newAppIds);
                }
            }

            function handleHover(event) {
                const gameElement = event.target.closest(NAPOLEON_HOVER_ELEMENT_SELECTOR);

                if (gameElement && gameElement.dataset.gameInfo) {
                    clearTimeout(SALADIN_hoverTimer);
                    clearTimeout(TAMERLAN_hideTimer);

                    SALADIN_hoverTimer = setTimeout(() => {
                        const gameData = JSON.parse(gameElement.dataset.gameInfo);
                        displayGameInfo(gameElement, gameData);
                    }, 300);
                } else {
                    clearTimeout(SALADIN_hoverTimer);
                    clearTimeout(TAMERLAN_hideTimer);
                    if (ATTILA_tooltip) {
                        ATTILA_tooltip.style.opacity = 0;
                        setTimeout(() => {
                            ATTILA_tooltip.style.display = 'none';
                        }, 300);
                    }
                }
            }

            function getReviewClassCatalog(percent, totalReviews) {
                if (totalReviews === 0) return 'catalog-no-reviews';
                if (percent >= 70) return 'catalog-positive';
                if (percent >= 40) return 'catalog-mixed';
                if (percent >= 1) return 'catalog-negative';
                return 'catalog-negative';
            }

            async function getTagNames(tagIds) {
                const tagsData = await loadSteamTags();
                return tagIds.slice(0, 5).map(tagId =>
                    tagsData[tagId] || `Тег #${tagId}`
                );
            }

            async function displayGameInfo(element, data) {
                if (!ATTILA_tooltip) {
                    ATTILA_tooltip = document.createElement('div');
                    ATTILA_tooltip.className = 'custom-tooltip';
                    ATTILA_tooltip.innerHTML = '<div class="tooltip-arrow"></div><div class="tooltip-content"></div>';
                    document.body.appendChild(ATTILA_tooltip);
                }

                const tooltipContent = ATTILA_tooltip.querySelector('.tooltip-content');

                let languageSupportRussianText = "Отсутствует";
                let languageSupportRussianClass = 'catalog-language-no';
                if (data.language_support_russian) {
                    languageSupportRussianText = "";
                    if (data.language_support_russian.supported) languageSupportRussianText += "<br>Интерфейс: ✔ ";
                    if (data.language_support_russian.full_audio) languageSupportRussianText += "<br>Озвучка: ✔ ";
                    if (data.language_support_russian.subtitles) languageSupportRussianText += "<br>Субтитры: ✔";
                    if (languageSupportRussianText === "") languageSupportRussianText = "Отсутствует";
                    else languageSupportRussianClass = 'catalog-language-yes';
                }

                let languageSupportEnglishText = "Отсутствует";
                let languageSupportEnglishClass = 'catalog-language-no';
                if (scriptsConfig.toggleEnglishLangInfo && data.language_support_english) {
                    languageSupportEnglishText = "";
                    if (data.language_support_english.supported) languageSupportEnglishText += "<br>Интерфейс: ✔ ";
                    if (data.language_support_english.full_audio) languageSupportEnglishText += "<br>Озвучка: ✔ ";
                    if (data.language_support_english.subtitles) languageSupportEnglishText += "<br>Субтитры: ✔";
                    if (languageSupportEnglishText === "") languageSupportEnglishText = "Отсутствует";
                    else languageSupportEnglishClass = 'catalog-language-yes';
                }

                const reviewClass = getReviewClassCatalog(data.percent_positive, data.review_count);
                const earlyAccessClass = data.is_early_access ? 'catalog-early-access-yes' : 'catalog-early-access-no';
                const tags = await getTagNames(data.tagids || []);
                const tagsHtml = tags.map(tag =>
                    `<div class="custom-tag">${tag}</div>`
                ).join('');

                tooltipContent.innerHTML = `
                    <div style="margin-bottom: 0px;"><strong>Издатели:</strong> <span class="${!data.publishers ? 'catalog-no-reviews' : ''}">${data.publishers || "Нет данных"}</span></div>
                    <div style="margin-bottom: 0px;"><strong>Разработчики:</strong> <span class="${!data.developers ? 'catalog-no-reviews' : ''}">${data.developers || "Нет данных"}</span></div>
                    <div style="margin-bottom: 10px;"><strong>Серия игр:</strong> <span class="${!data.franchises ? 'catalog-no-reviews' : ''}">${data.franchises || "Нет данных"}</span></div>
                    <div style="margin-bottom: 10px;"><strong>Отзывы: </strong><span id="reviewCount">${data.review_count || "0"} </span><span class="${reviewClass}">(${data.percent_positive || "0"}% положительных)</span></div>
                    <div style="margin-bottom: 10px;"><strong>Ранний доступ:</strong> <span class="${earlyAccessClass}">${data.is_early_access ? "Да" : "Нет"}</span></div>
                    <div style="margin-bottom: 10px;"><strong>Русский язык:</strong> <span class="${languageSupportRussianClass}">${languageSupportRussianText}</span></div>
                    ${scriptsConfig.toggleEnglishLangInfo ? `<div style="margin-bottom: 10px;"><strong>Английский язык:</strong> <span class="${languageSupportEnglishClass}">${languageSupportEnglishText}</span></div>` : ''}
                    <div style="margin-bottom: 10px;"><strong>Метки:</strong><br>
                    <div class="custom-tags-container">${tagsHtml}</div></div>
                    <div style="margin-bottom: 10px;"><strong>Описание:</strong> <span class="${!data.short_description ? 'catalog-no-reviews' : ''}">${data.short_description || "Нет данных"}</span></div>
                `;

                ATTILA_tooltip.style.display = 'block';

                const rect = element.getBoundingClientRect();
                const tooltipRect = ATTILA_tooltip.getBoundingClientRect();
                ATTILA_tooltip.style.left = `${rect.left + window.scrollX - tooltipRect.width - 4}px`;
                ATTILA_tooltip.style.top = `${rect.top + window.scrollY - 20}px`;

                ATTILA_tooltip.style.opacity = 0;
                ATTILA_tooltip.style.display = 'block';
                setTimeout(() => {
                    ATTILA_tooltip.style.opacity = 1;
                }, 10);

                element.addEventListener('mouseleave', () => {
                    clearTimeout(TAMERLAN_hideTimer);
                    TAMERLAN_hideTimer = setTimeout(() => {
                        ATTILA_tooltip.style.opacity = 0;
                        setTimeout(() => {
                            ATTILA_tooltip.style.display = 'none';
                        }, 300);
                    }, 200);
                }, {
                    once: true
                });

                element.addEventListener('mouseover', () => {
                    clearTimeout(TAMERLAN_hideTimer);
                });
            }

            function createRussianLanguageFilterBlock() {
                const filterBlock = document.createElement('div');
                filterBlock.className = 'block search_collapse_block';
                filterBlock.innerHTML = `
                    <div data-panel="{&quot;focusable&quot;:true,&quot;clickOnActivate&quot;:true}" class="block_header labs_block_header">
                        <div>Русский перевод</div>
                    </div>
                    <div class="block_content block_content_inner">
                        <div class="tab_filter_control_row" data-param="russian_translation" data-value="__toggle" data-loc="Только текст" data-clientside="0">
                            <span data-panel="{&quot;focusable&quot;:true,&quot;clickOnActivate&quot;:true}" class="tab_filter_control tab_filter_control_include" data-param="russian_translation" data-value="__toggle" data-loc="Только текст" data-clientside="0" data-gpfocus="item">
                                <span>
                                    <span class="tab_filter_control_checkbox"></span>
                                    <span class="tab_filter_control_label">Только текст</span>
                                    <span class="tab_filter_control_count" style="display: none;"></span>
                                </span>
                            </span>
                        </div>
                        <div class="tab_filter_control_row" data-param="russian_voice" data-value="__toggle" data-loc="Озвучка" data-clientside="0">
                            <span data-panel="{&quot;focusable&quot;:true,&quot;clickOnActivate&quot;:true}" class="tab_filter_control tab_filter_control_include" data-param="russian_voice" data-value="__toggle" data-loc="Озвучка" data-clientside="0" data-gpfocus="item">
                                <span>
                                    <span class="tab_filter_control_checkbox"></span>
                                    <span class="tab_filter_control_label">Озвучка</span>
                                    <span class="tab_filter_control_count" style="display: none;"></span>
                                </span>
                            </span>
                        </div>
                        <div class="tab_filter_control_row" data-param="no_russian" data-value="__toggle" data-loc="Без перевода" data-clientside="0">
                            <span data-panel="{&quot;focusable&quot;:true,&quot;clickOnActivate&quot;:true}" class="tab_filter_control tab_filter_control_include" data-param="no_russian" data-value="__toggle" data-loc="Без перевода" data-clientside="0" data-gpfocus="item">
                                <span>
                                    <span class="tab_filter_control_checkbox"></span>
                                    <span class="tab_filter_control_label">Без перевода</span>
                                    <span class="tab_filter_control_count" style="display: none;"></span>
                                </span>
                            </span>
                        </div>
                    </div>
                `;

                const dlcFilterBlock = document.createElement('div');
                dlcFilterBlock.className = 'block search_collapse_block';
                dlcFilterBlock.innerHTML = `
                    <div data-panel="{&quot;focusable&quot;:true,&quot;clickOnActivate&quot;:true}" class="block_header labs_block_header">
                        <div>DLC</div>
                    </div>
                    <div class="block_content block_content_inner">
                        <div class="tab_filter_control_row" data-param="your_dlc" data-value="__toggle" data-loc="Только ваши DLC" data-clientside="0">
                            <span data-panel="{&quot;focusable&quot;:true,&quot;clickOnActivate&quot;:true}" class="tab_filter_control tab_filter_control_include" data-param="your_dlc" data-value="__toggle" data-loc="Только ваши DLC" data-clientside="0" data-gpfocus="item">
                                <span>
                                    <span class="tab_filter_control_checkbox"></span>
                                    <span class="tab_filter_control_label">Только ваши DLC</span>
                                    <span class="tab_filter_control_count" style="display: none;"></span>
                                </span>
                            </span>
                        </div>
                    </div>
                `;

                const priceBlock = document.querySelector('.block.search_collapse_block[data-collapse-name="price"]');
                priceBlock.parentNode.insertBefore(filterBlock, priceBlock.nextSibling);
                priceBlock.parentNode.insertBefore(dlcFilterBlock, filterBlock.nextSibling);

                const translationRow = filterBlock.querySelector('[data-param="russian_translation"]');
                const voiceRow = filterBlock.querySelector('[data-param="russian_voice"]');
                const noRussianRow = filterBlock.querySelector('[data-param="no_russian"]');
                const dlcRow = dlcFilterBlock.querySelector('[data-param="your_dlc"]');

                [translationRow, voiceRow, noRussianRow].forEach(row => {
                    row.addEventListener('click', () => {
                        const control = row.querySelector('.tab_filter_control');
                        const wasChecked = control.classList.contains('checked');

                        [translationRow, voiceRow, noRussianRow].forEach(r => {
                            r.querySelector('.tab_filter_control').classList.remove('checked');
                            r.classList.remove('checked');
                        });

                        if (!wasChecked) {
                            control.classList.add('checked');
                            row.classList.add('checked');
                        }

                        document.querySelectorAll(CAESAR_VISIBLE_ELEMENTS_SELECTOR).forEach(gameElement => {
                            applyRussianLanguageFilter(gameElement);
                        });
                    });
                });

                dlcRow.addEventListener('click', () => {
                    const control = dlcRow.querySelector('.tab_filter_control');
                    const isChecked = !control.classList.contains('checked');

                    control.classList.toggle('checked');
                    dlcRow.classList.toggle('checked');

                    document.querySelectorAll(CAESAR_VISIBLE_ELEMENTS_SELECTOR).forEach(gameElement => {
                        applyDlcFilter(gameElement, isChecked);
                    });
                });
            }

            function applyDlcFilter(gameElement, showOnlyDlc) {
                if (!gameElement.dataset.gameInfo) return;

                const gameData = JSON.parse(gameElement.dataset.gameInfo);
                const isDlcForOwnedGame = gameElement.classList.contains('es_highlighted_dlcforya');

                if (showOnlyDlc) {
                    if (!isDlcForOwnedGame) {
                        animateDisappearance(gameElement);
                    } else {
                        animateAppearance(gameElement);
                    }
                } else {
                    animateAppearance(gameElement);
                }
            }

            function applyRussianLanguageFilter(gameElement) {
                if (!gameElement.dataset.gameInfo) return;

                const gameData = JSON.parse(gameElement.dataset.gameInfo);
                const hasRussianText = gameData.language_support_russian?.supported || gameData.language_support_russian?.subtitles;
                const hasRussianVoice = gameData.language_support_russian?.full_audio;
                const hasAnyRussian = hasRussianText || hasRussianVoice;

                const translationChecked = document.querySelector('[data-param="russian_translation"] .tab_filter_control').classList.contains('checked');
                const voiceChecked = document.querySelector('[data-param="russian_voice"] .tab_filter_control').classList.contains('checked');
                const noRussianChecked = document.querySelector('[data-param="no_russian"] .tab_filter_control').classList.contains('checked');
                const dlcFilterActive = document.querySelector('[data-param="your_dlc"] .tab_filter_control')?.classList.contains('checked');

                if (dlcFilterActive && !gameElement.classList.contains('es_highlighted_dlcforya')) {
                    animateDisappearance(gameElement);
                    return;
                }

                if (translationChecked) {
                    if (!hasRussianText || hasRussianVoice) animateDisappearance(gameElement);
                    else animateAppearance(gameElement);
                } else if (voiceChecked) {
                    if (!hasRussianVoice) animateDisappearance(gameElement);
                    else animateAppearance(gameElement);
                } else if (noRussianChecked) {
                    if (hasAnyRussian) animateDisappearance(gameElement);
                    else animateAppearance(gameElement);
                } else {
                    animateAppearance(gameElement);
                }
            }

            function animateDisappearance(element) {
                element.style.transition = 'opacity 0.5s ease-out, transform 0.5s ease-out';
                element.style.opacity = '0';
                element.style.transform = 'translateX(-100%)';

                setTimeout(() => {
                    element.style.display = 'none';
                }, 500);
            }

            function animateAppearance(element) {
                element.style.display = 'block';
                element.style.opacity = '0';
                element.style.transform = 'translateX(-100%)';
                element.style.transition = 'opacity 0.5s ease-in-out, transform 0.5s ease-in-out';

                setTimeout(() => {
                    element.style.opacity = '1';
                    element.style.transform = 'translateX(0)';
                }, 0);

                setTimeout(() => {
                    element.style.transition = '';
                }, 500);
            }

            function observeNewElements() {
                const observer = new MutationObserver((mutations) => {
                    mutations.forEach(mutation => {
                        if (mutation.type === 'childList') {
                            collectAndFetchAppIds();
                        }
                    });
                });

                observer.observe(document.body, {
                    childList: true,
                    subtree: true
                });
            }

            function initialize() {
                setTimeout(() => {
                    collectAndFetchAppIds();
                    observeNewElements();
                    document.addEventListener('mouseover', handleHover);
                    createRussianLanguageFilterBlock();
                }, HANNIBAL_WAIT_TIME);
            }

            initialize();

            const style = document.createElement('style');
            style.innerHTML = `
                .custom-tooltip {
                    position: absolute;
                    background: linear-gradient(to bottom, #e3eaef, #c7d5e0);
                    color: #30455a;
                    padding: 12px;
                    border-radius: 0px;
                    box-shadow: 0 0 12px #000;
                    font-size: 12px;
                    max-width: 300px;
                    display: none;
                    z-index: 1000;
                    opacity: 0;
                    transition: opacity 0.4s ease-in-out;
                }
                .tooltip-arrow {
                    position: absolute;
                    right: -9px;
                    top: 32px;
                    width: 0;
                    height: 0;
                    border-top: 10px solid transparent;
                    border-bottom: 10px solid transparent;
                    border-left: 10px solid #E1E8ED;
                }
                .catalog-positive {
                    color: #2B80E9;
                }
                .catalog-mixed {
                    color: #997a00;
                }
                .catalog-negative {
                    color: #E53E3E;
                }
                .catalog-no-reviews {
                    color: #929396;
                }
                .catalog-language-yes {
                    color: #2B80E9;
                }
                .catalog-language-no {
                    color: #E53E3E;
                }
                .catalog-early-access-yes {
                    color: #2B80E9;
                }
                .catalog-early-access-no {
                    color: #929396;
                }
                .search_result_row {
                    transition: opacity 0.5s ease-in-out, transform 0.5s ease-in-out;
                }
                .custom-tags-container {
                    display: flex;
                    flex-wrap: wrap;
                    gap: 3px;
                    margin-top: 6px;
                }
                .custom-tag {
                    background-color: #96a3ae;
                    color: #e3eaef;
                    padding: 0 4px;
                    border-radius: 2px;
                    font-size: 11px;
                    line-height: 19px;
                    white-space: nowrap;
                    overflow: hidden;
                    text-overflow: ellipsis;
                    max-width: 200px;
                    box-shadow: none;
                    margin-bottom: 3px;
                }
                .es_highlighted_dlcforya {
                    background: #822dbf linear-gradient(135deg, rgba(0, 0, 0, 0.70) 10%, rgba(0, 0, 0, 0) 100%) !important;
                }
            `;
            document.head.appendChild(style);
        })();
    }

    // Скрипт скрытия игр на странице поиска по каталогу | https://store.steampowered.com/search/
    if (scriptsConfig.catalogHider && window.location.pathname.includes('/search')) {
        (function() {
            "use strict";

            function addBeetles() {
                const scarabLinks = document.querySelectorAll("a.search_result_row:not(.ds_ignored):not(.ds_excluded_by_preferences):not(.ds_wishlist):not(.ds_owned)");
                scarabLinks.forEach(link => {
                    if (link.querySelector(".my-checkbox")) return;
                    const ladybug = document.createElement("input");
                    ladybug.type = "checkbox";
                    ladybug.className = "my-checkbox";
                    ladybug.dataset.aphid = link.dataset.dsAppid;
                    link.insertBefore(ladybug, link.firstChild);

                    ladybug.addEventListener("change", function() {
                        link.style.background = this.checked ? "linear-gradient(to bottom, #381616, #5d1414)" : "";
                    });
                });
            }

            function hideSelectedCrickets() {
                const checkedLadybugs = document.querySelectorAll(".my-checkbox:checked");
                checkedLadybugs.forEach(ladybug => {
                    const link = document.querySelector(`a[data-ds-appid="${ladybug.dataset.aphid}"]`);
                    if (link) {
                        link.classList.add("ds_ignored", "ds_flagged");
                        ladybug.remove();
                        jQuery.ajax({
                            url: "https://store.steampowered.com/recommended/ignorerecommendation/",
                            type: "POST",
                            data: {
                                sessionid: g_sessionID,
                                appid: ladybug.dataset.aphid,
                                remove: 0,
                                snr: "1_account_notinterested_",
                            },
                            success: () => {
                                console.log(`Game with appid ${ladybug.dataset.aphid} added to the ignore list`);
                                GDynamicStore.InvalidateCache();
                            },
                        });
                    }
                });
                updateAntCounter();
            }

            function removeIgnoredDragonflies() {
                const ignoredGames = document.querySelectorAll("a.search_result_row.ds_ignored, a.search_result_row.ds_excluded_by_preferences,a.search_result_row.ds_wishlist");
                ignoredGames.forEach(game => game.remove());
                updateAntCounter();
            }

            function updateAntCounter() {
                const scarabLinks = document.querySelectorAll("a.search_result_row:not(.ds_ignored):not(.ds_excluded_by_preferences):not(.ds_wishlist):not(.ds_owned)");
                const termiteElement = document.querySelector(".game-counter");
                if (termiteElement) {
                    termiteElement.textContent = `Игр осталось: ${scarabLinks.length}`;
                }
            }

            const grasshopperButton = document.createElement("button");
            grasshopperButton.textContent = "Скрыть выбранное";
            grasshopperButton.addEventListener("click", hideSelectedCrickets);
            grasshopperButton.classList.add("my-button", "floating-button");
            document.body.appendChild(grasshopperButton);

            const cockroach = document.createElement("div");
            cockroach.textContent = "Игр осталось: 0";
            cockroach.classList.add("game-counter", "floating-button");
            document.body.appendChild(cockroach);



            GM_addStyle(`
      input[type=checkbox] {
        -webkit-appearance: none;
        -moz-appearance: none;
        appearance: none;
        border: 6px inset rgba(255, 0, 0, 0.8);
        border-radius: 50%;
        width: 42px;
        height: 42px;
        outline: none;
        transition: .15s ease-in-out;
        vertical-align: middle;
        position: absolute;
        left: 0px;
        top: 50%;
        transform: translateY(-50%);
        background-color: rgba(0, 0, 0, 0.0);
        box-shadow: inset 0 0 0 0 rgba(255, 255, 255, 0.5);
        cursor: pointer;
        z-index: 9999;
      }
      input[type=checkbox]:checked {
        background-color: rgba(0, 0, 0, 0.5);
        border-color: #b71c1c;
        box-shadow: inset 0 0 0 12px rgba(255, 0, 0, 0.5);
      }
      input[type=checkbox]:after {
        content: "";
        display: block;
        position: absolute;
        left: 50%;
        top: 50%;
        transform: translate(-50%, -50%) scale(0);
        width: 25px;
        height: 25px;
        border-radius: 50%;
        background-color: rgba(0, 0, 0, 0.9);
        opacity: 0.9;
        box-shadow: 0 0 0 0 #b71c1c;
        transition: transform .15s ease-in-out, box-shadow .15s ease-in-out;
      }
      input[type=checkbox]:checked:after {
        transform: translate(-50%, -50%) scale(1);
        box-shadow: 0 0 0 4px #b71c1c;
      }

      .my-button {
        margin-right: 10px;
        padding: 10px 20px;
        border: none;
        border-radius: 50px;
        font-size: 16px;
        font-weight: 700;
        color: #fff;
        background: linear-gradient(to right, #16202D, #1B2838);
        box-shadow: 0px 2px 5px rgba(0, 0, 0, 0.2);
        cursor: pointer;
        font-family: "Roboto", sans-serif;
        margin-top: 245px;
      }
      .my-button:hover {
        background: linear-gradient(to right, #0072ff, #00c6ff);
        box-shadow: 0px 5px 10px rgba(0, 0, 0, 0.3);
      }
      .floating-button {
        position: fixed;
        top: -189px;
        left: 240px;
        z-index: 1000;
      }
      .game-counter {
        margin-right: 10px;
        padding: 10px 20px;
        border: none;
        border-radius: 50px;
        font-size: 16px;
        font-weight: 700;
        color: #fff;
        background: linear-gradient(to right, #16202D, #1B2838);
        box-shadow: 0px 2px 5px rgba(0, 0, 0, 0.2);
        font-family: "Roboto", sans-serif;
        margin-top: 195px;
      }
    `);

            const butterflyObserver = new MutationObserver(mutations => {
                mutations.forEach(mutation => {
                    if (mutation.type === "childList" && mutation.addedNodes.length) {
                        addBeetles();
                        removeIgnoredDragonflies();
                        updateAntCounter();
                    }
                });
            });

            butterflyObserver.observe(document.body, {
                childList: true,
                subtree: true
            });

            addBeetles();
            removeIgnoredDragonflies();
            updateAntCounter();
        })();
    }

    // Скрипт для скрытия новостей в новостном центре: | https://store.steampowered.com/news/
    if (scriptsConfig.newsFilter && window.location.pathname.includes('/news')) {
        (function() {
            'use strict';

            const stromboliStyle = `
      .etna-checkbox {
        position: absolute;
        top: 50%;
        right: 10px;
        width: 60px;
        height: 60px;
        border-radius: 50%;
        border: 2px solid #66c0f4;
        background-color: rgba(27, 40, 56, 0.7);
        cursor: pointer;
        z-index: 1000;
        transform: translateY(-50%);
        opacity: 0.5;
      }
      .etna-checkbox:checked {
        background-color: rgba(102, 192, 244, 0.8);
      }
      .vesuvius-hide-button {
        position: fixed;
        top: 20px;
        right: 20px;
        padding: 15px 30px;
        background-color: #66c0f4;
        color: #fff;
        border: none;
        border-radius: 5px;
        cursor: pointer;
        z-index: 1000;
        font-size: 18px;
        transition: background-color 0.3s, box-shadow 0.3s;
      }

      .vesuvius-hide-button:hover {
        background-color: #4a90e2;
        box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
      }
    `;

            const krakatoaStyleElement = document.createElement('style');
            krakatoaStyleElement.innerHTML = stromboliStyle;
            document.head.appendChild(krakatoaStyleElement);

            function addEtnaCheckboxes(newsItems) {
                newsItems.forEach(item => {
                    const fujiNewsLink = item.querySelector('a.Focusable[href^="/news/app/"]');
                    if (fujiNewsLink && !item.querySelector('.etna-checkbox')) {
                        const rainierCheckbox = document.createElement('input');
                        rainierCheckbox.type = 'checkbox';
                        rainierCheckbox.className = 'etna-checkbox';
                        rainierCheckbox.addEventListener('click', (event) => {
                            event.stopPropagation();
                        });
                        const kilimanjaroOverlayDiv = item.querySelector('._3HF9tOy_soo1B_odf1XArk');
                        if (kilimanjaroOverlayDiv) {
                            kilimanjaroOverlayDiv.style.position = 'relative';
                            kilimanjaroOverlayDiv.appendChild(rainierCheckbox);
                        }
                    }
                });
            }

            function addVesuviusHideButton() {
                const vesuviusHideButton = document.createElement('button');
                vesuviusHideButton.className = 'vesuvius-hide-button';
                vesuviusHideButton.textContent = 'Скрыть';
                vesuviusHideButton.onclick = hideSelectedNews;
                document.body.appendChild(vesuviusHideButton);
            }

            function hideSelectedNews() {
                const maunaLoaCheckboxes = document.querySelectorAll('.etna-checkbox:checked');
                maunaLoaCheckboxes.forEach(maunaLoaCheckbox => {
                    const newsItem = maunaLoaCheckbox.closest('._398u23KF15gxmeH741ZSyL');
                    const fujiNewsLink = newsItem.querySelector('a.Focusable[href^="/news/app/"]').getAttribute('href');
                    const shishaldinNewsTitle = newsItem.querySelector('._1M8-Pa3b3WboayCgd5VBJT').textContent;
                    const bakerNewsDate = new Date().toISOString();

                    const hiddenNews = JSON.parse(localStorage.getItem('hiddenNews') || '[]');
                    hiddenNews.push({
                        link: fujiNewsLink,
                        title: shishaldinNewsTitle,
                        date: bakerNewsDate
                    });
                    localStorage.setItem('hiddenNews', JSON.stringify(hiddenNews));

                    newsItem.remove();
                });
            }

            function removeHiddenNews() {
                const hiddenNews = JSON.parse(localStorage.getItem('hiddenNews') || '[]');
                hiddenNews.forEach(news => {
                    const newsItem = document.querySelector(`a[href="${news.link}"]`)?.closest('._398u23KF15gxmeH741ZSyL');
                    if (newsItem) {
                        newsItem.remove();
                    }
                });
            }

            function init() {
                removeHiddenNews();
                addEtnaCheckboxes(document.querySelectorAll('._398u23KF15gxmeH741ZSyL'));
                addVesuviusHideButton();
            }

            setTimeout(init, 1000);

            const erebusObserver = new MutationObserver((mutations) => {
                mutations.forEach(mutation => {
                    if (mutation.type === 'childList') {
                        const newNewsItems = document.querySelectorAll('._398u23KF15gxmeH741ZSyL');
                        addEtnaCheckboxes(newNewsItems);
                        removeHiddenNews();
                    }
                });
            });

            erebusObserver.observe(document.body, {
                childList: true,
                subtree: true
            });

        })();
    }

    // Скрипт для показа годовых и исторических продаж предмета на торговой площадке Steam | https://steamcommunity.com/market/listings/*
    if (scriptsConfig.Kaznachei && window.location.pathname.includes('/market/listings/')) {
        async function fetchSalesInfo() {
            const urlParts = window.location.pathname.split('/');
            const appId = urlParts[3];
            const marketHashName = decodeURIComponent(urlParts[4]);
            const apiUrl = `https://steamcommunity.com/market/pricehistory/?appid=${appId}&market_hash_name=${marketHashName}`;

            try {
                const response = await fetch(apiUrl);
                const data = await response.json();

                if (data.success) {
                    const salesData = data.prices;
                    const yearlySales = {};
                    let totalSales = 0;

                    salesData.forEach(sale => {
                        const date = sale[0];
                        const price = parseFloat(sale[1]);
                        const quantity = parseInt(sale[2]);
                        const year = date.split(' ')[2];

                        const totalForDay = price * quantity;

                        if (!yearlySales[year]) {
                            yearlySales[year] = {
                                total: 0,
                                commission: 0,
                                developerShare: 0,
                                valveShare: 0
                            };
                        }

                        yearlySales[year].total += totalForDay;
                        totalSales += totalForDay;
                    });

                    for (const year in yearlySales) {
                        const commission = yearlySales[year].total * 0.13;
                        const developerShare = commission * 0.6667;
                        const valveShare = commission * 0.3333;

                        yearlySales[year].commission = commission;
                        yearlySales[year].developerShare = developerShare;
                        yearlySales[year].valveShare = valveShare;
                    }

                    displaySalesInfo(yearlySales, totalSales);
                } else {
                    console.error('Не удалось получить информацию о продажах.');
                }
            } catch (error) {
                console.error('Ошибка при получении данных:', error);
            }
        }

        function displaySalesInfo(yearlySales, totalSales) {
            const salesInfoContainer = document.createElement('div');
            salesInfoContainer.style.marginTop = '20px';
            salesInfoContainer.style.padding = '10px';
            salesInfoContainer.style.border = '1px solid #4a4a4a';
            salesInfoContainer.style.backgroundColor = '#1b2838';
            salesInfoContainer.style.borderRadius = '4px';
            salesInfoContainer.style.boxShadow = '0 1px 3px rgba(0, 0, 0, 0.5)';
            salesInfoContainer.style.color = '#c7d5e0';

            const spoilerHeader = document.createElement('div');
            spoilerHeader.style.cursor = 'pointer';
            spoilerHeader.style.padding = '10px';
            spoilerHeader.style.backgroundColor = '#171a21';
            spoilerHeader.style.borderRadius = '4px 4px 0 0';
            spoilerHeader.style.color = '#c7d5e0';
            spoilerHeader.style.fontWeight = 'bold';
            spoilerHeader.style.fontFamily = '"Motiva Sans", sans-serif';
            spoilerHeader.style.fontSize = '16px';
            spoilerHeader.style.display = 'flex';
            spoilerHeader.style.alignItems = 'center';
            spoilerHeader.style.justifyContent = 'space-between';
            spoilerHeader.innerHTML = 'Информация о продажах <span style="font-size: 12px; transform: rotate(0deg); transition: transform 0.3s ease;">&#9660;</span>';

            spoilerHeader.addEventListener('click', () => {
                const content = spoilerHeader.nextElementSibling;
                content.style.display = content.style.display === 'none' ? 'block' : 'none';
                const arrow = spoilerHeader.querySelector('span');
                arrow.style.transform = content.style.display === 'none' ? 'rotate(0deg)' : 'rotate(180deg)';
            });

            const spoilerContent = document.createElement('div');
            spoilerContent.style.display = 'none';
            spoilerContent.style.padding = '10px';
            spoilerContent.style.borderTop = '1px solid #4a4a4a';

            const yearlySalesTable = document.createElement('table');
            yearlySalesTable.style.width = '100%';
            yearlySalesTable.style.borderCollapse = 'collapse';
            yearlySalesTable.style.marginBottom = '20px';
            yearlySalesTable.style.fontFamily = '"Motiva Sans", sans-serif';
            yearlySalesTable.style.fontSize = '14px';

            const yearlySalesHeader = document.createElement('tr');
            yearlySalesHeader.innerHTML = '<th style="padding: 8px; text-align: left; border-bottom: 2px solid #4a4a4a; background-color: #171a21; color: #c7d5e0;">Год</th><th style="padding: 8px; text-align: left; border-bottom: 2px solid #4a4a4a; background-color: #171a21; color: #c7d5e0;">Сумма продаж за год</th><th style="padding: 8px; text-align: left; border-bottom: 2px solid #4a4a4a; background-color: #171a21; color: #c7d5e0;">Ушло разработчику</th><th style="padding: 8px; text-align: left; border-bottom: 2px solid #4a4a4a; background-color: #171a21; color: #c7d5e0;">Ушло Valve</th>';
            yearlySalesTable.appendChild(yearlySalesHeader);

            for (const year in yearlySales) {
                const row = document.createElement('tr');
                row.innerHTML = `<td style="padding: 8px; border-bottom: 1px solid #4a4a4a; background-color: #1b2838; color: #c7d5e0;">${year}</td><td style="padding: 8px; border-bottom: 1px solid #4a4a4a; background-color: #1b2838; color: #c7d5e0;">${yearlySales[year].total.toLocaleString('ru-RU', { minimumFractionDigits: 2, maximumFractionDigits: 2 })} руб.</td><td style="padding: 8px; border-bottom: 1px solid #4a4a4a; background-color: #1b2838; color: #c7d5e0;">${yearlySales[year].developerShare.toLocaleString('ru-RU', { minimumFractionDigits: 2, maximumFractionDigits: 2 })} руб.</td><td style="padding: 8px; border-bottom: 1px solid #4a4a4a; background-color: #1b2838; color: #c7d5e0;">${yearlySales[year].valveShare.toLocaleString('ru-RU', { minimumFractionDigits: 2, maximumFractionDigits: 2 })} руб.</td>`;
                yearlySalesTable.appendChild(row);
            }

            const totalSalesParagraph = document.createElement('p');
            totalSalesParagraph.textContent = `Сумма продаж за всё время: ${totalSales.toLocaleString('ru-RU', { minimumFractionDigits: 2, maximumFractionDigits: 2 })} руб.`;
            totalSalesParagraph.style.fontWeight = 'bold';
            totalSalesParagraph.style.fontSize = '16px';
            totalSalesParagraph.style.color = '#c7d5e0';
            totalSalesParagraph.style.fontFamily = '"Motiva Sans", sans-serif';

            const commission = totalSales * 0.13;
            const developerShare = commission * 0.6667;
            const valveShare = commission * 0.3333;

            const developerShareParagraph = document.createElement('p');
            developerShareParagraph.textContent = `Ушло разработчику: ${developerShare.toLocaleString('ru-RU', { minimumFractionDigits: 2, maximumFractionDigits: 2 })} руб.`;
            developerShareParagraph.style.fontSize = '14px';
            developerShareParagraph.style.color = '#c7d5e0';
            developerShareParagraph.style.fontFamily = '"Motiva Sans", sans-serif';

            const valveShareParagraph = document.createElement('p');
            valveShareParagraph.textContent = `Ушло Valve: ${valveShare.toLocaleString('ru-RU', { minimumFractionDigits: 2, maximumFractionDigits: 2 })} руб.`;
            valveShareParagraph.style.fontSize = '14px';
            valveShareParagraph.style.color = '#c7d5e0';
            valveShareParagraph.style.fontFamily = '"Motiva Sans", sans-serif';

            spoilerContent.appendChild(yearlySalesTable);
            spoilerContent.appendChild(totalSalesParagraph);
            spoilerContent.appendChild(developerShareParagraph);
            spoilerContent.appendChild(valveShareParagraph);

            salesInfoContainer.appendChild(spoilerHeader);
            salesInfoContainer.appendChild(spoilerContent);

            const marketHeaderBg = document.querySelector('.market_header_bg');
            if (marketHeaderBg) {
                marketHeaderBg.parentNode.insertBefore(salesInfoContainer, marketHeaderBg.nextSibling);
            }
        }

        setTimeout(fetchSalesInfo, 100);
    }

    // Скрипт для получения дополнительной информации об игре при наведении на неё на странице вашей активности Steam
    if (scriptsConfig.homeInfo && window.location.href.includes('steamcommunity.com') && window.location.pathname.includes('/home')) {
        (function() {
            'use strict';

            const MOREL_API_URL = "https://api.steampowered.com/IStoreBrowseService/GetItems/v1";
            const CHANTERELLE_WAIT_TIME = 2000;
            const PORCINI_VISIBLE_ELEMENTS_SELECTOR = "a[href*='/app/'], a[data-appid]";
            const TRUFFLE_HOVER_ELEMENT_SELECTOR = "a[href*='/app/'], a[data-appid]";

            let SHIITAKE_collectedAppIds = new Set();
            let ENOKI_tooltip = null;
            let MAITAKE_hoverTimer = null;
            let HEN_OF_THE_WOODS_hideTimer = null;

            const MUSHROOM_GAME_DATA = {};

            const STEAM_TAGS_CACHE_KEY = 'SteamEnhancer_TagsCache_v2';
            const STEAM_TAGS_URL = "https://gist.githubusercontent.com/0wn3dg0d/22a351ff4c65e50a9a8af6da360defad/raw/steamrutagsownd.json";

            function fetchGameData(appIds) {
                const inputJson = {
                    ids: Array.from(appIds).map(appid => ({
                        appid
                    })),
                    context: {
                        language: "russian",
                        country_code: "US",
                        steam_realm: 1
                    },
                    data_request: {
                        include_assets: true,
                        include_release: true,
                        include_platforms: true,
                        include_all_purchase_options: true,
                        include_screenshots: true,
                        include_trailers: true,
                        include_ratings: true,
                        include_tag_count: true,
                        include_reviews: true,
                        include_basic_info: true,
                        include_supported_languages: true,
                        include_full_description: true,
                        include_included_items: true,
                        included_item_data_request: {
                            include_assets: true,
                            include_release: true,
                            include_platforms: true,
                            include_all_purchase_options: true,
                            include_screenshots: true,
                            include_trailers: true,
                            include_ratings: true,
                            include_tag_count: true,
                            include_reviews: true,
                            include_basic_info: true,
                            include_supported_languages: true,
                            include_full_description: true,
                            include_included_items: true,
                            include_assets_without_overrides: true,
                            apply_user_filters: false,
                            include_links: true
                        },
                        include_assets_without_overrides: true,
                        apply_user_filters: false,
                        include_links: true
                    }
                };

                GM_xmlhttpRequest({
                    method: "GET",
                    url: `${MOREL_API_URL}?input_json=${encodeURIComponent(JSON.stringify(inputJson))}`,
                    onload: function(response) {
                        const data = JSON.parse(response.responseText);
                        processGameData(data);
                    }
                });
            }

            function processGameData(data) {
                const items = data.response.store_items;
                items.forEach(item => {
                    const appId = item.id;
                    MUSHROOM_GAME_DATA[appId] = {
                        name: item.name,
                        is_early_access: item.is_early_access,
                        review_count: item.reviews?.summary_filtered?.review_count,
                        percent_positive: item.reviews?.summary_filtered?.percent_positive,
                        short_description: item.basic_info?.short_description,
                        publishers: item.basic_info?.publishers?.map(p => p.name).join(", "),
                        developers: item.basic_info?.developers?.map(d => d.name).join(", "),
                        franchises: item.basic_info?.franchises?.map(f => f.name).join(", "),
                        tagids: item.tagids || [],
                        language_support_russian: item.supported_languages?.find(lang => lang.elanguage === 8),
                        language_support_english: item.supported_languages?.find(lang => lang.elanguage === 0),
                        release_date: item.release?.steam_release_date ? new Date(item.release.steam_release_date * 1000).toLocaleDateString() : "Нет данных"
                    };
                });
            }

            function collectAndFetchAppIds() {
                const visibleElements = document.querySelectorAll(PORCINI_VISIBLE_ELEMENTS_SELECTOR);
                const newAppIds = new Set();

                visibleElements.forEach(element => {
                    const appId = element.dataset.appid || element.href.match(/app\/(\d+)/)?.[1];
                    if (appId && !SHIITAKE_collectedAppIds.has(appId)) {
                        newAppIds.add(parseInt(appId, 10));
                        SHIITAKE_collectedAppIds.add(appId);
                    }
                });

                if (newAppIds.size > 0) {
                    fetchGameData(newAppIds);
                }
            }

            function handleHover(event) {
                const gameElement = event.target.closest(TRUFFLE_HOVER_ELEMENT_SELECTOR);

                if (gameElement) {
                    const appId = gameElement.dataset.appid || gameElement.href.match(/app\/(\d+)/)?.[1];
                    if (appId && MUSHROOM_GAME_DATA[appId]) {
                        clearTimeout(MAITAKE_hoverTimer);
                        clearTimeout(HEN_OF_THE_WOODS_hideTimer);

                        MAITAKE_hoverTimer = setTimeout(() => {
                            displayGameInfo(gameElement, MUSHROOM_GAME_DATA[appId], appId);
                        }, 300);
                    } else {
                        clearTimeout(MAITAKE_hoverTimer);
                        clearTimeout(HEN_OF_THE_WOODS_hideTimer);
                        if (ENOKI_tooltip) {
                            ENOKI_tooltip.style.opacity = 0;
                            setTimeout(() => {
                                ENOKI_tooltip.style.display = 'none';
                            }, 300);
                        }
                    }
                }
            }

            function getReviewClassCatalog(percent, totalReviews) {
                if (totalReviews === 0) return 'mushroom-no-reviews';
                if (percent >= 70) return 'mushroom-positive';
                if (percent >= 40) return 'mushroom-mixed';
                if (percent >= 1) return 'mushroom-negative';
                return 'mushroom-negative';
            }


            async function loadSteamTags() {
                const cached = GM_getValue(STEAM_TAGS_CACHE_KEY, {
                    data: null,
                    timestamp: 0
                });
                const now = Date.now();
                const CACHE_DURATION = 744 * 60 * 60 * 1000;

                if (cached.data && (now - cached.timestamp) < CACHE_DURATION) {
                    return cached.data;
                }

                try {
                    const response = await new Promise((resolve, reject) => {
                        GM_xmlhttpRequest({
                            method: "GET",
                            url: STEAM_TAGS_URL,
                            onload: resolve,
                            onerror: reject
                        });
                    });

                    if (response.status === 200) {
                        const data = JSON.parse(response.responseText);
                        GM_setValue(STEAM_TAGS_CACHE_KEY, {
                            data: data,
                            timestamp: now
                        });
                        return data;
                    }
                } catch (e) {
                    console.error('Ошибка загрузки тегов:', e);
                    return cached.data || {};
                }

                return {};
            }

            async function displayGameInfo(element, data, appId) {
                if (!ENOKI_tooltip) {
                    ENOKI_tooltip = document.createElement('div');
                    ENOKI_tooltip.className = 'mushroom-tooltip';
                    ENOKI_tooltip.innerHTML = '<div class="tooltip-arrow"></div><div class="tooltip-content"></div>';
                    document.body.appendChild(ENOKI_tooltip);
                }

                const tooltipContent = ENOKI_tooltip.querySelector('.tooltip-content');

                let languageSupportRussianText = "Отсутствует";
                let languageSupportRussianClass = 'mushroom-language-no';
                if (data.language_support_russian) {
                    languageSupportRussianText = "";
                    if (data.language_support_russian.supported) languageSupportRussianText += "<br>Интерфейс: ✔ ";
                    if (data.language_support_russian.full_audio) languageSupportRussianText += "<br>Озвучка: ✔ ";
                    if (data.language_support_russian.subtitles) languageSupportRussianText += "<br>Субтитры: ✔";
                    if (languageSupportRussianText === "") languageSupportRussianText = "Отсутствует";
                    else languageSupportRussianClass = 'mushroom-language-yes';
                }

                let languageSupportEnglishText = "Отсутствует";
                let languageSupportEnglishClass = 'mushroom-language-no';
                if (scriptsConfig.toggleEnglishLangInfo && data.language_support_english) {
                    languageSupportEnglishText = "";
                    if (data.language_support_english.supported) languageSupportEnglishText += "<br>Интерфейс: ✔ ";
                    if (data.language_support_english.full_audio) languageSupportEnglishText += "<br>Озвучка: ✔ ";
                    if (data.language_support_english.subtitles) languageSupportEnglishText += "<br>Субтитры: ✔";
                    if (languageSupportEnglishText === "") languageSupportEnglishText = "Отсутствует";
                    else languageSupportEnglishClass = 'mushroom-language-yes';
                }

                const reviewClass = getReviewClassCatalog(data.percent_positive, data.review_count);
                const earlyAccessClass = data.is_early_access ? 'mushroom-early-access-yes' : 'mushroom-early-access-no';

                async function getTagNames(tagIds) {
                    const tagsData = await loadSteamTags();
                    return tagIds.slice(0, 5).map(tagId =>
                        tagsData[tagId] || `Тег #${tagId}`
                    );
                }

                const tags = await getTagNames(data.tagids || []);
                const tagsHtml = tags.map(tag =>
                    `<div class="mushroom-tag">${tag}</div>`
                ).join('');

                tooltipContent.innerHTML = `
                <div style="margin-bottom: 10px;"><strong>Название:</strong> ${data.name || "Нет данных"}</div>
                <div style="margin-bottom: 10px;"><img src="https://shared.cloudflare.steamstatic.com/store_item_assets/steam/apps/${appId}/header.jpg" alt="${data.name}" style="width: 50%; height: auto;"></div>
                <div style="margin-bottom: 10px;"><strong>Дата выхода:</strong> ${data.release_date}</div>
                <div style="margin-bottom: 0px;"><strong>Издатели:</strong> <span class="${!data.publishers ? 'mushroom-no-reviews' : ''}">${data.publishers || "Нет данных"}</span></div>
                <div style="margin-bottom: 0px;"><strong>Разработчики:</strong> <span class="${!data.developers ? 'mushroom-no-reviews' : ''}">${data.developers || "Нет данных"}</span></div>
                <div style="margin-bottom: 10px;"><strong>Серия игр:</strong> <span class="${!data.franchises ? 'mushroom-no-reviews' : ''}">${data.franchises || "Нет данных"}</span></div>
                <div style="margin-bottom: 10px;"><strong>Отзывы: </strong><span id="reviewCount">${data.review_count || "0"} </span><span class="${reviewClass}">(${data.percent_positive || "0"}% положительных)</span></div>
                <div style="margin-bottom: 10px;"><strong>Ранний доступ:</strong> <span class="${earlyAccessClass}">${data.is_early_access ? "Да" : "Нет"}</span></div>
                <div style="margin-bottom: 10px;"><strong>Русский язык:</strong> <span class="${languageSupportRussianClass}">${languageSupportRussianText}</span></div>
                ${scriptsConfig.toggleEnglishLangInfo ? `<div style="margin-bottom: 10px;"><strong>Английский язык:</strong> <span class="${languageSupportEnglishClass}">${languageSupportEnglishText}</span></div>` : ''}
                <div style="margin-bottom: 10px;"><strong>Метки:</strong><br>
                <div class="mushroom-tags-container">${tagsHtml}</div></div>
                <div style="margin-bottom: 10px;"><strong>Описание:</strong> <span class="${!data.short_description ? 'mushroom-no-reviews' : ''}">${data.short_description || "Нет данных"}</span></div>
            `;

                ENOKI_tooltip.style.display = 'block';

                const blotterDayElement = document.querySelector('.blotter_day');
                if (blotterDayElement) {
                    const blotterRect = blotterDayElement.getBoundingClientRect();
                    const tooltipRect = ENOKI_tooltip.getBoundingClientRect();

                    ENOKI_tooltip.style.left = `${blotterRect.left - tooltipRect.width - 5}px`;
                    ENOKI_tooltip.style.top = `${element.getBoundingClientRect().top + window.scrollY - 35}px`;
                }

                ENOKI_tooltip.style.opacity = 0;
                ENOKI_tooltip.style.display = 'block';
                setTimeout(() => {
                    ENOKI_tooltip.style.opacity = 1;
                }, 10);

                element.addEventListener('mouseleave', () => {
                    clearTimeout(HEN_OF_THE_WOODS_hideTimer);
                    HEN_OF_THE_WOODS_hideTimer = setTimeout(() => {
                        ENOKI_tooltip.style.opacity = 0;
                        setTimeout(() => {
                            ENOKI_tooltip.style.display = 'none';
                        }, 300);
                    }, 200);
                }, {
                    once: true
                });

                element.addEventListener('mouseover', () => {
                    clearTimeout(HEN_OF_THE_WOODS_hideTimer);
                });
            }

            function observeNewElements() {
                const observer = new MutationObserver((mutations) => {
                    mutations.forEach(mutation => {
                        if (mutation.type === 'childList') {
                            collectAndFetchAppIds();
                        }
                    });
                });

                observer.observe(document.body, {
                    childList: true,
                    subtree: true
                });
            }

            function initialize() {
                setTimeout(() => {
                    collectAndFetchAppIds();
                    observeNewElements();
                    document.addEventListener('mouseover', handleHover);
                }, CHANTERELLE_WAIT_TIME);
            }

            initialize();

            const style = document.createElement('style');
            style.innerHTML = `
            .mushroom-tooltip {
                position: absolute;
                background: linear-gradient(to bottom, #e3eaef, #c7d5e0);
                color: #30455a;
                padding: 12px;
                border-radius: 0px;
                box-shadow: 0 0 12px #000;
                font-size: 12px;
                max-width: 300px;
                display: none;
                z-index: 1000;
                opacity: 0;
                transition: opacity 0.4s ease-in-out;
            }
            .tooltip-arrow {
                position: absolute;
                right: -9px;
                top: 32px;
                width: 0;
                height: 0;
                border-top: 10px solid transparent;
                border-bottom: 10px solid transparent;
                border-left: 10px solid #E1E8ED;
            }
            .mushroom-positive {
                color: #2B80E9;
            }
            .mushroom-mixed {
                color: #997a00;
            }
            .mushroom-negative {
                color: #E53E3E;
            }
            .mushroom-no-reviews {
                color: #929396;
            }
            .mushroom-language-yes {
                color: #2B80E9;
            }
            .mushroom-language-no {
                color: #E53E3E;
            }
            .mushroom-early-access-yes {
                color: #2B80E9;
            }
            .mushroom-early-access-no {
                color: #929396;
            }
           .mushroom-tags-container {
               display: flex;
               flex-wrap: wrap;
               gap: 3px;
               margin-top: 6px;
           }
           .mushroom-tag {
               background-color: #96a3ae;
               color: #e3eaef;
               padding: 0 4px;
               border-radius: 2px;
               font-size: 11px;
               line-height: 19px;
               white-space: nowrap;
               overflow: hidden;
               text-overflow: ellipsis;
               max-width: 200px;
               box-shadow: none;
               margin-bottom: 3px;
           }
        `;
            document.head.appendChild(style);
        })();
    }

    // Скрипт для страницы игры (ZOG; получение сведений о наличии русификаторов) | https://store.steampowered.com/app/*
    if (window.location.pathname.includes('/app/') && scriptsConfig.zogInfo) {
        (async function() {
            const ZOG_CACHE_KEY = 'ZoGRusekiEdrit';
            const ZOG_DATA_URL = 'https://gist.githubusercontent.com/0wn3dg0d/7baa8d9f42b0304fe303e903d44d2ada/raw/zogrusbase.json';

            const zogBlock = document.createElement('div');
            Object.assign(zogBlock.style, {
                position: 'absolute',
                left: '334px',
                width: '30px',
                height: '30px',
                background: 'rgba(27, 40, 56, 0.95)',
                padding: '15px',
                borderRadius: '4px',
                border: '1px solid #3c3c3c',
                boxShadow: '0 0 10px rgba(0, 0, 0, 0.5)',
                zIndex: '2',
                fontFamily: 'Arial, sans-serif',
                overflow: 'hidden',
                transition: 'all 0.3s ease'
            });

            let hltbBlock = null;
            let hltbObserver = null;
            let zogMap = null;
            let zogNameMap = null;

            const updatePosition = () => {
                hltbBlock = document.querySelector('#gameHeaderImageCtn > div[style*="background: rgba(27, 40, 56, 0.95)"]');

                const russianIndicators = document.querySelector('#gameHeaderImageCtn > div[style*="position: absolute; top: -10px; left: calc(100% + 10px);"]');

                if (hltbBlock && scriptsConfig.hltbData) {
                    zogBlock.style.top = `${hltbBlock.offsetTop + hltbBlock.offsetHeight + 16}px`;
                } else if (russianIndicators && scriptsConfig.gamePage) {
                    zogBlock.style.top = `${russianIndicators.offsetTop + russianIndicators.offsetHeight + 16}px`;
                } else {
                    const headerImage = document.querySelector('#gameHeaderImageCtn');
                    if (headerImage) {
                        zogBlock.style.top = `${0}px`;
                    }
                }

                zogBlock.style.left = '334px';
                zogBlock.style.zIndex = '2';
            };

            const initObservers = () => {
                if (scriptsConfig.hltbData) {
                    hltbBlock = document.querySelector('#gameHeaderImageCtn > div[style*="background: rgba(27, 40, 56, 0.95)"]');
                    if (hltbBlock && !hltbObserver) {
                        hltbObserver = new ResizeObserver(updatePosition);
                        hltbObserver.observe(hltbBlock);
                        hltbBlock.addEventListener('transitionend', updatePosition);
                    }
                }

                if (scriptsConfig.gamePage) {
                    const russianObserver = new MutationObserver((mutations) => {
                        mutations.forEach(mutation => {
                            if (mutation.type === 'attributes' && mutation.attributeName === 'style') {
                                updatePosition();
                            }
                        });
                    });

                    const indicators = document.querySelector('#gameHeaderImageCtn > div[style*="position: absolute; top: -10px; left: calc(100% + 10px);"]');
                    if (indicators) {
                        russianObserver.observe(indicators, {
                            attributes: true,
                            attributeFilter: ['style']
                        });
                    }
                }

                const generalObserver = new MutationObserver((mutations) => {
                    mutations.forEach(mutation => {
                        if (mutation.type === 'childList') {
                            updatePosition();
                            initObservers();
                        }
                    });
                });

                generalObserver.observe(document.querySelector('#gameHeaderImageCtn'), {
                    childList: true,
                    subtree: true
                });
            };

            async function loadZogData() {
                const cached = GM_getValue(ZOG_CACHE_KEY);
                const lastUpdated = cached?.lastUpdated || '';

                try {
                    const metaResponse = await new Promise((resolve, reject) => {
                        GM_xmlhttpRequest({
                            method: 'GET',
                            url: 'https://api.github.com/gists/7baa8d9f42b0304fe303e903d44d2ada',
                            onload: resolve,
                            onerror: reject
                        });
                    });

                    const metaData = JSON.parse(metaResponse.responseText);

                    const newLastUpdated = metaData.updated_at;

                    if (newLastUpdated === lastUpdated) {
                        return cached.data;
                    }

                    const dataResponse = await new Promise((resolve, reject) => {
                        GM_xmlhttpRequest({
                            method: 'GET',
                            url: ZOG_DATA_URL,
                            onload: resolve,
                            onerror: reject
                        });
                    });

                    const newData = JSON.parse(dataResponse.responseText);

                    GM_setValue(ZOG_CACHE_KEY, {
                        lastUpdated: newLastUpdated,
                        data: newData,
                        timestamp: Date.now()
                    });

                    return newData;
                } catch (error) {
                    console.error('Ошибка загрузки данных ZOG:', error);
                    return cached?.data || [];
                }
            }

            async function initZogData() {
                try {
                    const data = await loadZogData();
                    zogMap = new Map(data.map(item => [item.app_id, item]));
                    zogNameMap = new Map(data.map(item => [
                        item.title
                        ?.normalize("NFD").replace(/[\u0300-\u036f]/g, "")
                        .replace(/[^a-zа-яё0-9 _'\-!]/gi, '')
                        .toLowerCase(),
                        item
                    ]));
                } catch (e) {
                    console.error('Ошибка инициализации данных ZOG:', e);
                    content.textContent = 'Ошибка загрузки базы';
                }
            }

            const title = document.createElement('div');
            Object.assign(title.style, {
                fontSize: '12px',
                fontWeight: 'bold',
                color: '#67c1f5',
                marginBottom: '10px',
                cursor: 'pointer'
            });
            title.textContent = 'ZOG';

            const content = document.createElement('div');
            Object.assign(content.style, {
                display: 'none',
                color: '#c6d4df',
                fontSize: '14px',
                maxWidth: '300px',
                overflowY: 'auto',
                whiteSpace: 'normal',
                lineHeight: '1.4',
                padding: '0 5px'
            });

            const arrow = createArrow();

            zogBlock.append(arrow, title, content);
            document.querySelector('#gameHeaderImageCtn').appendChild(zogBlock);

            initObservers();
            updatePosition();
            await initZogData();

            title.onclick = () => toggleBlock(arrow);
            arrow.onclick = () => toggleBlock(arrow);

            async function toggleBlock(arrowElement) {
                if (content.style.display === 'none') {
                    await expandBlock(arrowElement);
                } else {
                    collapseBlock(arrowElement);
                }
            }

            async function expandBlock(arrowElement) {
                if (!zogMap || !zogNameMap) {
                    console.error('Данные ZOG не инициализированы');
                    return;
                }

                zogBlock.style.transition = 'width 0.3s ease, height 0.3s ease';
                zogBlock.style.width = '300px';
                zogBlock.style.height = '40px';

                arrowElement.style.transform = 'translateX(-50%) rotate(180deg)';
                await new Promise(resolve => setTimeout(resolve, 300));

                content.style.display = 'block';
                content.textContent = 'Ищем в базе...';

                await new Promise(resolve => requestAnimationFrame(resolve));

                const appId = getAppId();
                let entry = zogMap.get(appId);

                if (!entry) {
                    const gameName = getGameName()
                        .normalize("NFD").replace(/[\u0300-\u036f]/g, "")
                        .replace(/[^a-zа-яё0-9 _'\-!]/gi, '')
                        .toLowerCase();

                    content.textContent = 'Ищем углубленно...';
                    await new Promise(resolve => requestAnimationFrame(resolve));

                    entry = zogNameMap.get(gameName);

                    if (!entry && /[а-яё]/i.test(gameName)) {
                        content.textContent = 'Запрашиваем англ. название...';
                        await new Promise(resolve => requestAnimationFrame(resolve));

                        const steamApiUrl = `https://api.steampowered.com/IStoreBrowseService/GetItems/v1?input_json={"ids": [{"appid": ${appId}}], "context": {"language": "english", "country_code": "US", "steam_realm": 1}, "data_request": {"include_assets": true}}`;

                        try {
                            const steamResponse = await new Promise((resolve, reject) => {
                                GM_xmlhttpRequest({
                                    method: "GET",
                                    url: steamApiUrl,
                                    onload: resolve,
                                    onerror: reject
                                });
                            });

                            if (steamResponse.status === 200) {
                                const steamData = JSON.parse(steamResponse.responseText);
                                const englishName = steamData.response.store_items[0]?.name;

                                if (englishName) {
                                    const cleanEnglishName = englishName
                                        .normalize("NFD").replace(/[\u0300-\u036f]/g, "")
                                        .replace(/[^a-zа-яё0-9 _'\-!]/gi, '')
                                        .toLowerCase();

                                    content.textContent = 'Проверяем англ. название...';
                                    await new Promise(resolve => requestAnimationFrame(resolve));

                                    entry = zogNameMap.get(cleanEnglishName);

                                    if (!entry) {
                                        content.textContent = 'Проверяем возможные совпадения...';
                                        await new Promise(resolve => requestAnimationFrame(resolve));

                                        const possibleMatches = findPossibleMatches(cleanEnglishName, Array.from(zogNameMap.values()));
                                        if (possibleMatches.length > 0) {
                                            renderPossibleMatches(possibleMatches);
                                            zogBlock.style.height = `${content.scrollHeight + 30}px`;
                                            updatePosition();
                                            return;
                                        }
                                    }
                                }
                            }
                        } catch (error) {
                            console.error('Ошибка при запросе к Steam API:', error);
                        }
                    }
                }

                if (!entry) {
                    content.textContent = 'Проверяем возможные совпадения...';
                    await new Promise(resolve => requestAnimationFrame(resolve));
                    const possibleMatches = findPossibleMatches(getGameName(), Array.from(zogNameMap.values()));
                    if (possibleMatches.length > 0) {
                        renderPossibleMatches(possibleMatches);
                        zogBlock.style.height = `${content.scrollHeight + 30}px`;
                        updatePosition();
                        return;
                    }
                }

                renderContent(entry);
                zogBlock.style.height = `${content.scrollHeight + 30}px`;
                updatePosition();
            }

            function nextFrame() {
                return new Promise(resolve => requestAnimationFrame(resolve));
            }

            function collapseBlock(arrowElement) {
                zogBlock.style.transition = 'width 0.3s ease, height 0.3s ease';
                zogBlock.style.width = '30px';
                zogBlock.style.height = '30px';

                arrowElement.style.transform = 'translateX(-50%) rotate(0deg)';

                content.style.display = 'none';
                updatePosition();
            }

            function renderContent(entry) {
                content.innerHTML = '';

                if (!entry) {
                    content.textContent = 'Игра не найдена в базе ZOG';
                    return;
                }

                const titleLink = document.createElement('a');
                titleLink.href = `https://www.zoneofgames.ru/games/${entry.id}.html`;
                titleLink.target = '_blank';
                titleLink.textContent = entry.title || 'Без названия';
                titleLink.style.color = '#67c1f5';
                titleLink.style.wordBreak = 'break-word';
                content.appendChild(titleLink);

                const list = document.createElement('ul');
                list.style.paddingLeft = '15px';
                list.style.marginTop = '5px';
                list.style.marginBottom = '0';

                if (entry.localizations?.length > 0) {
                    entry.localizations.forEach(loc => {
                        const li = document.createElement('li');
                        li.style.marginBottom = '8px';

                        const link = document.createElement('a');
                        link.href = loc.link;
                        link.target = '_blank';
                        link.textContent = `${loc.name} ${loc.size || ''}`;
                        link.style.color = '#c6d4df';
                        link.style.wordBreak = 'break-word';
                        link.style.textDecoration = 'none';

                        li.appendChild(link);
                        list.appendChild(li);
                    });
                } else {
                    list.textContent = 'Русификаторы отсутствуют';
                    list.style.color = '#999';
                }

                content.appendChild(list);
            }

            function renderPossibleMatches(matches) {
                content.innerHTML = '';

                const title = document.createElement('div');
                title.textContent = 'Возможные совпадения:';
                title.style.color = '#67c1f5';
                title.style.marginBottom = '10px';
                content.appendChild(title);

                const list = document.createElement('ul');
                list.style.paddingLeft = '15px';
                list.style.marginTop = '5px';
                list.style.marginBottom = '0';

                matches.forEach(match => {
                    const li = document.createElement('li');
                    li.style.marginBottom = '8px';

                    const link = document.createElement('a');
                    link.href = `https://www.zoneofgames.ru/games/${match.id}.html`;
                    link.target = '_blank';
                    link.textContent = `${match.title} (${match.percentage}%)`;
                    link.style.color = '#c6d4df';
                    link.style.wordBreak = 'break-word';
                    link.style.textDecoration = 'none';
                    link.onclick = () => {
                        renderContent(match);
                        zogBlock.style.height = `${content.scrollHeight + 30}px`;
                        updatePosition();
                        return false;
                    };

                    li.appendChild(link);
                    list.appendChild(li);
                });

                const noMatch = document.createElement('li');
                noMatch.style.marginBottom = '8px';

                const noMatchLink = document.createElement('a');
                noMatchLink.href = '#';
                noMatchLink.textContent = 'Ничего не подходит';
                noMatchLink.style.color = '#c6d4df';
                noMatchLink.style.wordBreak = 'break-word';
                noMatchLink.style.textDecoration = 'none';
                noMatchLink.onclick = () => {
                    renderContent(null);
                    zogBlock.style.height = `${content.scrollHeight + 30}px`;
                    updatePosition();
                    return false;
                };

                noMatch.appendChild(noMatchLink);
                list.appendChild(noMatch);

                content.appendChild(list);
            }

            function findPossibleMatches(gameName, data) {
                const cleanGameName = gameName
                    .normalize("NFD").replace(/[\u0300-\u036f]/g, "")
                    .replace(/[^a-zа-яё0-9 _'\-!]/gi, '')
                    .toLowerCase();

                return data
                    .map(item => {
                        const cleanItemName = item.title
                            .normalize("NFD").replace(/[\u0300-\u036f]/g, "")
                            .replace(/[^a-zа-яё0-9 _'\-!]/gi, '')
                            .toLowerCase();

                        const similarity = calculateSimilarity(cleanGameName, cleanItemName);
                        const startsWith = cleanItemName.startsWith(cleanGameName);

                        return {
                            ...item,
                            percentage: similarity,
                            startsWith: startsWith
                        };
                    })
                    .filter(item => item.percentage > 50 || item.startsWith)
                    .sort((a, b) => {
                        if (a.startsWith && !b.startsWith) return -1;
                        if (!a.startsWith && b.startsWith) return 1;
                        return b.percentage - a.percentage;
                    })
                    .slice(0, 5);
            }

            function calculateSimilarity(str1, str2) {
                const len = Math.max(str1.length, str2.length);
                if (len === 0) return 100;
                const distance = levenshteinDistance(str1, str2);
                return Math.round(((len - distance) / len) * 100);
            }

            function levenshteinDistance(str1, str2) {
                const m = str1.length;
                const n = str2.length;
                const dp = Array.from({
                    length: m + 1
                }, () => Array(n + 1).fill(0));

                for (let i = 0; i <= m; i++) {
                    for (let j = 0; j <= n; j++) {
                        if (i === 0) {
                            dp[i][j] = j;
                        } else if (j === 0) {
                            dp[i][j] = i;
                        } else {
                            dp[i][j] = Math.min(
                                dp[i - 1][j - 1] + (str1[i - 1] === str2[j - 1] ? 0 : 1),
                                dp[i - 1][j] + 1,
                                dp[i][j - 1] + 1
                            );
                        }
                    }
                }

                return dp[m][n];
            }

            function createArrow() {
                const arrow = document.createElement('div');
                Object.assign(arrow.style, {
                    position: 'absolute',
                    bottom: '5px',
                    left: '50%',
                    width: '0',
                    height: '0',
                    borderLeft: '5px solid transparent',
                    borderRight: '5px solid transparent',
                    borderTop: '5px solid #67c1f5',
                    cursor: 'pointer',
                    transition: 'transform 0.3s ease',
                    transform: 'translateX(-50%)'
                });
                return arrow;
            }

            function getAppId() {
                return window.location.pathname.split('/')[2];
            }

            function getGameName() {
                return document.querySelector('.apphub_AppName').textContent
                    .normalize("NFD")
                    .replace(/[\u0300-\u036f]/g, "")
                    .replace(/[’]/g, "'")
                    .replace(/[^a-zA-Zа-яёА-ЯЁ0-9 _'\-!]/g, '')
                    .trim()
                    .toLowerCase();
            }
        })();
    }

    // Скрипт для получения уведомлений об изменении дат выхода игр из вашего списка желаемого Steam и показа календаря с датами | https://steamcommunity.com/my/wishlist/
    if (scriptsConfig.wishlistTracker) {
        (function() {
            'use strict';

            const STORAGE_PREFIX = 'USE_Wishlist_';
            const STORAGE_KEYS = {
                NOTIFICATIONS: STORAGE_PREFIX + 'notifications',
                GAME_DATA: STORAGE_PREFIX + 'gameData',
                LAST_UPDATE: STORAGE_PREFIX + 'lastUpdate'
            };

            const calendarIcon = `<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
            <path d="M19 4h-1V2h-2v2H8V2H6v2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zM5 20V10h14v10H5zM9 14H7v-2h2v2zm4 0h-2v-2h2v2zm4 0h-2v-2h2v2zm-8 4H7v-2h2v2zm4 0h-2v-2h2v2zm4 0h-2v-2h2v2z"/>
            </svg>`;

            const BATCH_SIZE = 200;
            const MILLISECONDS_IN_HOUR = 60 * 60 * 1000;
            let notifications = GM_getValue(STORAGE_KEYS.NOTIFICATIONS, []);
            let isPanelOpen = false;

            GM_addStyle(`
            .wishlist-tracker-container {
				position: absolute;
				right: 180px;
				top: 6px;
				z-index: 999;
			}

			.wishlist-tracker-button {
				color: #c6d4df;
				background: rgba(103, 193, 245, 0.1);
				padding: 7px 12px;
				border-radius: 2px;
				cursor: pointer;
				font-size: 13px;
				display: flex;
                align-items: center;
                gap: 4px;
				align-items: center;
				transition: all 0.2s ease;
			}

			.wishlist-tracker-button:hover {
				background: rgba(103, 193, 245, 0.2);
			}

			.notification-badge {
				background: #67c1f5;
				color: #1b2838;
				border-radius: 3px;
				padding: 3px 6px;
				font-size: 14px;
				font-weight: bold;
				margin-left: 8px;
				min-width: 20px;
				text-align: center;
				box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
			}

			.status-indicator {
			    background: #4a5562;
			    color: #c6d4df;
			    border-radius: 3px;
			    padding: 3px 6px;
			    font-size: 12px;
			    font-weight: bold;
			    margin-left: 5px;
			    min-width: 30px;
			    text-align: center;
			    transition: all 0.3s ease;
			    cursor: help;
			}

			    .status-ok { background: #4a5562; }
			    .status-warning { background: #4a5562; }
			    .status-alert1 { background: #665c3a; color: #ffd700; }
			    .status-alert2 { background: #804d4d; color: #ffb3b3; }
			    .status-critical { background: #e60000; color: #fff; }
			    .status-unknown { background: #1b2838; color: #8f98a0; }

			.wishlist-tracker-panel {
				position: fixed;
				right: 132px;
				top: 50px;
				background: #1b2838;
				border: 1px solid #67c1f5;
				width: 500px;
				max-height: 500px;
                min-width: 460px;
				overflow-y: auto;
				z-index: 9999;
				box-shadow: 0 0 15px rgba(0, 0, 0, 0.5);
				display: none;
			}

			.wt-panel-header {
				padding: 15px;
				background: #171a21;
				display: flex;
				justify-content: space-between;
				align-items: center;
			}

			.panel-title {
				font-size: 17px;
				font-weight: 500;
				color: #67c1f5;
			}


            .panel-controls {
                display: flex;

            }

            .panel-controls button {
                background: rgba(30, 45, 60, 0.7);
                border: none;
                color: #c6d4df;
                padding: 8px 14px;
                cursor: pointer;
                margin-left: 5px;
                border-radius: 2px;
                font-weight: 400;
                text-transform: uppercase;
                letter-spacing: 0.5px;
                box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
                transition: background 0.2s ease, box-shadow 0.2s ease;
            }

            .panel-controls button:hover {
                background: rgba(40, 60, 80, 0.9);
                box-shadow: 0 3px 6px rgba(0, 0, 0, 0.4);
            }

            .panel-controls button:active {
                background: rgba(30, 45, 60, 0.6);
                box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
            }

			.calendar-btn {
			    padding: 8px 10px !important;
			    display: flex;
			    align-items: center;
			}

			.wt-notification-item {
				padding: 15px;
				border-bottom: 1px solid #2a475e;
				position: relative;
				transition: opacity 0.3s;
			}

			.notification-content {
				display: flex;
				gap: 15px;
			}

			.notification-image {
				width: 80px;
				height: 45px;
				object-fit: cover;
			}

			.notification-text {
				flex-grow: 1;
				padding-right: 25px;
			}

			.notification-game-title {
				color: #66c0f4;
				font-weight: bold;
				text-decoration: none;
				display: block;
				margin-bottom: 5px;
			}

			.notification-date {
				font-size: 12px;
				color: #8f98a0;
			}

			.notification-dates {
				color: #c6d4df;
				font-size: 13px;
			}

			.wtunread {
				background: rgba(102, 192, 244, 0.15);
			}

			.notification-controls {
				position: absolute;
				right: 10px;
				top: 10px;
				display: flex;
				gap: 8px;
			}

			.notification-control {
				cursor: pointer;
				width: 18px;
				height: 18px;
				opacity: 0.7;
				transition: opacity 0.2s;
			}

			.notification-control:hover {
				opacity: 1;
			}

			.delete-btn {
			  width: 20px;
			  height: 20px;
			  display: flex;
			  align-items: center;
			  justify-content: center;
			  color: #6C7781;
			  font-size: 16px;
			  font-weight: bold;
			  line-height: 1;
			  border: none;
			  cursor: pointer;
			  transition: color 0.2s ease, transform 0.1s ease;
			}

			.delete-btn:hover {
			  color: #8F98A0;
			}

			.delete-btn:active {
			  color: #800000;
			  transform: scale(0.9);
			}

			.loading-indicator {
				color: #67c1f5;
				text-align: center;
				padding: 10px;
			}

			.calendar-wtmodal.active {
			    display: flex;
			    flex-direction: column;
			}

			.calendar-wtmodal {
			    position: fixed;
			    top: 50%;
			    left: 50%;
			    transform: translate(-50%, -50%);
			    width: 80%;
			    height: 80vh;
			    background: #1b2838;
			    border: 1px solid #67c1f5;
			    box-shadow: 0 0 30px rgba(0,0,0,0.7);
			    z-index: 100000;
			    display: none;
			    padding: 20px;
			    overflow: hidden;
			}

			.calendar-header {
			    display: flex;
			    justify-content: space-between;
			    align-items: center;
			    padding-bottom: 15px;
			    border-bottom: 1px solid #2a475e;
			    margin-bottom: 15px;
			}

			.calendar-title {
			    color: #67c1f5;
			    font-size: 25px;
			}

			.calendar-close {
			    cursor: pointer;
			    color: #8f98a0;
			    font-size: 54px;
			    padding: 5px;
			}

			.calendar-close:hover {
			    color: #67c1f5;
			}

			.calendar-content {
			    flex-grow: 1;
			    overflow-y: auto;
			    padding-right: 10px;
			}

			.calendar-month {
			    margin-bottom: 30px;
			}

			.month-header {
			    color: #67c1f5;
			    font-size: 24px;
			    margin-bottom: 15px;
			}

			.calendar-grid {
			    display: grid;
			    grid-template-columns: repeat(7, 1fr);
			    gap: 2px;
			    font-size: 14px;
			    font-weight: 500;
			}

			.calendar-grid > div:not(.calendar-day) {
			    padding: 10px 0;
 			    background: #1b2838;
 			    color: #67c1f5;
  			    border-bottom: 2px solid #67c1f5;
 			    text-transform: uppercase;
			    text-align: center;
			}

			.calendar-day {
			    background: #2a475e;
			    min-height: 69px;
			    padding: 20px 0 16px 0;
			    position: relative;
			    display: flex;
			    flex-direction: column;
			    gap: 3px;
			}

			.day-number {
			    position: absolute;
			    top: 3px;
			    right: 5px;
			    color: #8f98a0;
			    font-size: 14px;
                z-index: 100003
			}

			.calendar-game {
			    display: flex;
                position: relative;
                padding-bottom: 8px;
			    align-items: center;
			    margin: 5px 0;
			    padding: 5px;
			    background: rgba(42,71,94,0.5);
			    border-radius: 3px;
			    transition: background 0.2s;
			    text-decoration: none !important;
			    color: inherit;
			}

			.calendar-game:not(:last-child)::after {
			    content: "";
			    position: absolute;
			    bottom: -7px;
			    left: 0;
			    right: 0;
			    height: 1px;
			    background: linear-gradient(90deg,
			        transparent 0%,
			        rgba(103, 193, 245, 0.3) 20%,
			        rgba(103, 193, 245, 0.4) 50%,
			        rgba(103, 193, 245, 0.3) 80%,
			        transparent 100%
			    );
			    margin-top: 8px;
			}

			.calendar-game-approximate .calendar-game-title {
			    color: #FFD580 !important;
 			   opacity: 0.9;
			}

			.calendar-game:hover {
			    background: rgba(67, 103, 133, 0.5);
			}

			.calendar-game-image {
			    width: 100px;
			    height: 45px;
			    object-fit: cover;
			    margin-right: 10px;
			}

			.calendar-game-title {
			    color: #c6d4df;
			    font-size: 13px;
			}


			.load-more-months {
			    text-align: center;
			    padding: 15px;
			}

			.load-more-btn {
			    background: rgba(103, 193, 245, 0.1);
			    color: #67c1f5;
			    border: none;
			    padding: 10px 20px;
			    cursor: pointer;
			    border-radius: 3px;
			}

			.load-more-btn:hover {
			    background: rgba(103, 193, 245, 0.2);
			}

			.wt-tooltip {
			    display: flex !important;
			    position: relative;
			}

			.wt-tooltip .wt-tooltiptext {
			    visibility: hidden;
			    width: 220px;
			    background-color: #171a21;
			    color: #c6d4df;
			    text-align: center;
			    border-radius: 3px;
			    padding: 12px;
			    position: absolute;
			    z-index: 1;
			    left: 100%;
			    margin-left: 2px;
			    opacity: 0;
			    transition: opacity 0.3s;
			    border: 1px solid #67c1f5;
			}

			.wt-tooltip:hover .wt-tooltiptext {
			    visibility: visible;
			    opacity: 1;
			}

			`);

            const envelopeIcons = {
                wtunread: `<svg width="20" height="16" viewBox="0 0 32 32" fill="#67c1f5" xmlns="http://www.w3.org/2000/svg">
        <path d="M16.015 18.861l-4.072-3.343-8.862 10.463h25.876l-8.863-10.567-4.079 3.447zM29.926 6.019h-27.815l13.908 11.698 13.907-11.698zM20.705 14.887l9.291 11.084v-18.952l-9.291 7.868zM2.004 7.019v18.952l9.291-11.084-9.291-7.868z"/>
    </svg>`,
                wtread: `<svg width="20" height="16" viewBox="0 0 32 32" fill="#8f98a0" xmlns="http://www.w3.org/2000/svg">
        <path d="M20.139 18.934l9.787-7.999-13.926-9.833-13.89 9.833 9.824 8.032 8.205-0.033zM12.36 19.936l-9.279 10.962h25.876l-9.363-10.9-7.234-0.062zM20.705 19.803l9.291 11.084v-18.952l-9.291 7.868zM2.004 11.935v18.952l9.291-11.084-9.291-7.868z"/>
    </svg>`
            };

            function createNotificationUI() {
                const container = $(`
    <div class="wishlist-tracker-container">
        <div class="wishlist-tracker-button">
            <span>Отслеживание вишлиста</span>
            <div class="status-indicator status-unknown">??</div>
            <div class="notification-badge">${getUnreadCount()}</div>
        </div>
                <div class="wishlist-tracker-panel">
                    <div class="wt-panel-header">
                        <div class="panel-title">Уведомлений: (${notifications.length})</div>
                        <div class="panel-controls">
                            <button class="refresh-btn">⟳ Обновить</button>
                            <button class="clear-btn">× Очистить</button>
                            <button class="calendar-btn">${calendarIcon}</button>
                        </div>
                    </div>
                </div>
            </div>
        `);

                const panel = container.find('.wishlist-tracker-panel');
                const button = container.find('.wishlist-tracker-button');

                button.click(function(e) {
                    e.stopPropagation();
                    togglePanel();
                });

                container.find('.refresh-btn').click((e) => {
                    e.stopPropagation();
                    updateData();
                });

                container.find('.clear-btn').click(() => {
                    notifications = [];
                    GM_setValue(STORAGE_KEYS.NOTIFICATIONS, notifications);
                    updateNotificationPanel();
                    updateBadge();
                });
                container.find('.calendar-btn').click((e) => {
                    e.stopPropagation();
                    showCalendarModal();
                });

                if (window.self === window.top) {
                    document.body.appendChild(container[0]);
                }
                updateNotificationPanel();

                $(document).click(() => {
                    if (isPanelOpen) {
                        panel.hide();
                        isPanelOpen = false;
                    }
                });
            }

            function showLoadingIndicator() {
                const panel = $('.wishlist-tracker-panel');
                panel.find('.loading-indicator').remove();
                const loading = $(`<div class="loading-indicator">Обновление данных...</div>`);
                panel.append(loading);
            }

            function togglePanel() {
                updateStatusIndicator();
                const panel = $('.wishlist-tracker-panel');
                panel.toggle();
                isPanelOpen = !isPanelOpen;
                if (isPanelOpen) {
                    panel.css('display', 'block');
                }
            }

            function updateNotificationPanel() {
                const panel = $('.wishlist-tracker-panel');
                panel.find('.wt-notification-item, .loading-indicator').remove();
                panel.find('.panel-title').text(`Уведомлений: (${notifications.length})`);

                notifications.slice(0, 5000).forEach((notification, index) => {
                    const item = $(`
            <div class="wt-notification-item ${notification.wtread ? '' : 'wtunread'}">
                <div class="notification-controls">
                    <div class="toggle-wtread-btn notification-control">
                        ${notification.wtread ? envelopeIcons.wtread : envelopeIcons.wtunread}
                    </div>
                    <div class="delete-btn notification-control">X</div>
                </div>
                <div class="notification-content">
                    <a href="https://store.steampowered.com/app/${notification.appid}" target="_blank">
                        <img src="https://shared.cloudflare.steamstatic.com/store_item_assets/steam/apps/${notification.appid}/header.jpg"
                             class="notification-image">
                    </a>
                    <div class="notification-text">
                        <a href="https://store.steampowered.com/app/${notification.appid}"
                           class="notification-game-title" target="_blank">
                            ${notification.name}
                        </a>
                        <div class="notification-dates">
                            Дата выхода изменилась:<br>
                            <span class="old-date">${formatDate(notification.oldDate)}</span> →
                            <span class="new-date">${formatDate(notification.newDate)}</span>
                        </div>
                        <div class="notification-date">
                            Обнаружено: ${new Date(notification.timestamp).toLocaleString()}
                        </div>
                    </div>
                </div>
            </div>
        `);

                    item.find('.delete-btn').click((e) => {
                        e.stopPropagation();
                        notifications = notifications.filter((_, i) => i !== index);
                        GM_setValue(STORAGE_KEYS.NOTIFICATIONS, notifications);
                        item.fadeOut(300, () => {
                            updateNotificationPanel();
                            updateBadge();
                        });
                    });

                    item.find('.toggle-wtread-btn').click((e) => {
                        e.stopPropagation();
                        notifications[index].wtread = !notifications[index].wtread;
                        GM_setValue(STORAGE_KEYS.NOTIFICATIONS, notifications);
                        item.toggleClass('wtunread', !notifications[index].wtread);
                        item.find('.toggle-wtread-btn').html(notifications[index].wtread ? envelopeIcons.wtread : envelopeIcons.wtunread);
                        updateBadge();
                    });

                    panel.append(item);
                });
            }

            function formatDate(dateInfo) {
                if (!dateInfo || dateInfo.value === 'Не указана') return 'Не указано';

                const value = dateInfo.value;
                const displayType = dateInfo.displayType;

                if (typeof value === 'string' && isNaN(value)) {
                    return value;
                }

                const ts = formatTimestamp(value);
                const date = new Date(ts * 1000);

                const monthNames = ["январь", "февраль", "март", "апрель", "май", "июнь",
                    "июль", "август", "сентябрь", "октябрь", "ноябрь", "декабрь"
                ];
                const quarter = Math.floor(date.getMonth() / 3) + 1;

                if (displayType) {
                    switch (displayType) {
                        case 'date_month':
                            return `${monthNames[date.getMonth()]} ${date.getFullYear()}`;
                        case 'date_quarter':
                            return `Q${quarter} ${date.getFullYear()}`;
                        case 'date_year':
                            return `${date.getFullYear()}`;
                        case 'date_full':
                        default:
                            return date.toLocaleDateString('ru-RU', {
                                day: 'numeric',
                                month: 'long',
                                year: 'numeric'
                            });
                    }
                }

                return date.toLocaleDateString('ru-RU', {
                    day: 'numeric',
                    month: 'long',
                    year: 'numeric'
                });
            }

            function updateStatusIndicator() {
                const lastUpdate = GM_getValue(STORAGE_KEYS.LAST_UPDATE, 0);
                const hoursPassed = (Date.now() - lastUpdate) / MILLISECONDS_IN_HOUR;
                const indicator = $('.status-indicator');
                const days = Math.floor(hoursPassed / 24);
                const hours = Math.floor(hoursPassed % 24);

                indicator.attr('title', `Данные не обновлялись: ${days} д. и ${hours} ч.`);

                if (!lastUpdate) {
                    indicator.text('-').removeClass().addClass('status-indicator status-unknown');
                    return;
                }

                if (hoursPassed < 12) {
                    indicator.text('OK').removeClass().addClass('status-indicator status-ok');
                } else if (hoursPassed < 24) {
                    indicator.text('OK?').removeClass().addClass('status-indicator status-warning');
                } else if (hoursPassed < 48) {
                    indicator.text('!').removeClass().addClass('status-indicator status-alert1');
                } else if (hoursPassed < 72) {
                    indicator.text('!!').removeClass().addClass('status-indicator status-alert2');
                } else if (hoursPassed < 96) {
                    indicator.text('!!!').removeClass().addClass('status-indicator status-critical');
                } else {
                    indicator.text('???').removeClass().addClass('status-indicator status-critical');
                }
            }

            function updateBadge() {
                $('.notification-badge').text(getUnreadCount());
            }

            function getUnreadCount() {
                return notifications.filter(n => !n.wtread).length;
            }


            async function fetchWishlistAppIds() {
                return new Promise(resolve => {
                    GM_xmlhttpRequest({
                        method: 'GET',
                        url: 'https://store.steampowered.com/dynamicstore/userdata/',
                        onload: function(response) {
                            const data = JSON.parse(response.responseText);
                            resolve(data.rgWishlist || []);
                        }
                    });
                });
            }

            async function fetchGameDetails(appIds) {
                const batches = [];
                for (let i = 0; i < appIds.length; i += BATCH_SIZE) {
                    batches.push(appIds.slice(i, i + BATCH_SIZE));
                }

                const allDetails = [];
                for (const batch of batches) {
                    const details = await fetchBatchDetails(batch);
                    allDetails.push(...details);
                    await new Promise(resolve => setTimeout(resolve, 1000));
                }

                return allDetails;
            }

            async function fetchBatchDetails(appIds) {
                const requestData = {
                    ids: appIds.map(appid => ({
                        appid
                    })),
                    context: {
                        language: 'russian',
                        country_code: 'RU',
                        steam_realm: 1
                    },
                    data_request: {
                        include_release: true,
                        include_basic_info: true
                    }
                };

                return new Promise(resolve => {
                    GM_xmlhttpRequest({
                        method: 'GET',
                        url: `https://api.steampowered.com/IStoreBrowseService/GetItems/v1?input_json=${encodeURIComponent(JSON.stringify(requestData))}`,
                        onload: function(response) {
                            try {
                                const data = JSON.parse(response.responseText);
                                resolve(data.response?.store_items || []);
                            } catch (e) {
                                console.error('Error parsing response:', e);
                                resolve([]);
                            }
                        }
                    });
                });
            }

            function checkForChanges(currentData) {
                const previousData = GM_getValue(STORAGE_KEYS.GAME_DATA, {});
                const changes = [];

                currentData.forEach(game => {
                    const prevGame = previousData[game.appid];
                    const currentRelease = getReleaseInfo(game.release);
                    const prevRelease = prevGame ? getReleaseInfo(prevGame.rawRelease) : null;

                    if (prevGame && (
                            currentRelease.date !== prevRelease?.date ||
                            currentRelease.type !== prevRelease?.type ||
                            currentRelease.displayType !== prevRelease?.displayType
                        )) {
                        changes.push({
                            appid: game.appid,
                            name: game.name,
                            oldDate: {
                                value: prevRelease?.date || 'Не указана',
                                displayType: prevRelease?.displayType
                            },
                            newDate: {
                                value: currentRelease.date,
                                displayType: currentRelease.displayType
                            },
                            timestamp: Date.now(),
                            wtread: false
                        });
                    }
                });

                const newGameData = currentData.reduce((acc, game) => {
                    acc[game.appid] = {
                        name: game.name,
                        rawRelease: game.release,
                        releaseInfo: getReleaseInfo(game.release)
                    };
                    return acc;
                }, {});

                GM_setValue(STORAGE_KEYS.GAME_DATA, {
                    ...previousData,
                    ...newGameData
                });

                if (changes.length > 0) {
                    notifications = [...changes, ...notifications];
                    GM_setValue(STORAGE_KEYS.NOTIFICATIONS, notifications);
                    updateNotificationPanel();
                    updateBadge();
                }

                $('.wishlist-tracker-panel .loading-indicator').remove();
            }

            function getReleaseInfo(releaseData) {
                if (!releaseData) return {
                    date: 'Не указана',
                    type: 'unknown',
                    displayType: null
                };

                const displayType = releaseData.coming_soon_display || null;

                if (releaseData.steam_release_date) {
                    return {
                        date: releaseData.steam_release_date,
                        type: 'date',
                        displayType: displayType
                    };
                }

                if (releaseData.custom_release_date_message) {
                    return {
                        date: releaseData.custom_release_date_message,
                        type: 'custom',
                        displayType: null
                    };
                }

                return {
                    date: 'Не указана',
                    type: 'unknown',
                    displayType: null
                };
            }

            function formatTimestamp(ts) {
                if (!ts) return ts;
                if (typeof ts === 'string') {
                    if (/^\d{4}-\d{2}-\d{2}$/.test(ts)) {
                        return Math.floor(new Date(ts).getTime() / 1000);
                    }
                    return ts;
                }
                return typeof ts === 'number' ? ts : parseInt(ts);
            }

            async function updateData() {
                try {
                    showLoadingIndicator();
                    const indicator = $('.status-indicator');
                    indicator.text('...').removeClass().addClass('status-indicator status-unknown');
                    const appIds = await fetchWishlistAppIds();
                    const gameDetails = await fetchGameDetails(appIds);
                    checkForChanges(gameDetails);
                    GM_setValue(STORAGE_KEYS.LAST_UPDATE, Date.now());
                    updateStatusIndicator();
                } catch (e) {
                    console.error('Update error:', e);
                    showErrorIndicator();
                    updateStatusIndicator();
                } finally {
                    $('.wishlist-tracker-panel .loading-indicator').remove();
                }
            }

            function showErrorIndicator() {
                const panel = $('.wishlist-tracker-panel');
                const error = $(`
            <div class="wt-notification-item" style="color: #ff4747;">
                Ошибка при обновлении данных
            </div>
        `);
                panel.prepend(error);
                setTimeout(() => error.remove(), 5000);
            }

            function showCalendarModal() {
                const gameData = GM_getValue(STORAGE_KEYS.GAME_DATA, {});
                const monthsData = getGamesByMonths(gameData);

                const wtmodal = $(`
        <div class="calendar-wtmodal">
            <div class="calendar-header">
                <div class="calendar-title">Календарь релизов (${monthsData.length} месяцев)</div>
                <div class="calendar-close">×</div>
            </div>
            <div class="calendar-content"></div>
        </div>
    `);

                const clickHandler = (e) => {
                    if (!$(e.target).closest('.calendar-wtmodal').length) {
                        e.preventDefault();
                        e.stopPropagation();
                        e.stopImmediatePropagation();

                        wtmodal.remove();
                        $(document).off('click', clickHandler);
                    }
                };

                wtmodal.find('.calendar-close').click((e) => {
                    e.preventDefault();
                    e.stopPropagation();
                    wtmodal.remove();
                    $(document).off('click', clickHandler);
                });

                wtmodal.click(e => e.stopPropagation());

                $(document).on('click', clickHandler);

                $('body').append(wtmodal);
                wtmodal.addClass('active');

                let visibleMonths = 3;
                const renderCalendar = () => {
                    const visibleData = monthsData.slice(0, visibleMonths);
                    const content = wtmodal.find('.calendar-content').empty();

                    visibleData.forEach(({
                        month,
                        year,
                        games
                    }) => {
                        const monthDate = new Date(year, month);
                        const monthName = monthDate.toLocaleString('ru-RU', {
                            month: 'long'
                        });
                        const daysInMonth = new Date(year, month + 1, 0).getDate();
                        const firstDay = new Date(year, month, 1).getDay();
                        const adjustedFirstDay = firstDay === 0 ? 6 : firstDay - 1;

                        const monthBlock = $(`
                <div class="calendar-month">
                    <div class="month-header">${monthName} ${year}</div>
                    <div class="calendar-grid"></div>
                </div>
            `);

                        const grid = monthBlock.find('.calendar-grid');
                        grid.append('<div>Пн</div><div>Вт</div><div>Ср</div><div>Чт</div><div>Пт</div><div>Сб</div><div>Вс</div>');

                        for (let i = 0; i < adjustedFirstDay; i++) {
                            grid.append('<div class="calendar-day"></div>');
                        }

                        for (let day = 1; day <= daysInMonth; day++) {
                            const dayGames = games.filter(g => {
                                const releaseDate = new Date(g.releaseInfo.date * 1000);
                                return releaseDate.getDate() === day &&
                                    releaseDate.getMonth() === month &&
                                    releaseDate.getFullYear() === year;
                            });

                            const dayElement = $(`
                    <div class="calendar-day">
                        <div class="day-number">${day}</div>
                    </div>
                `);

                            dayGames.sort((a, b) => a.name.localeCompare(b.name)).forEach(game => {
                                const isApproximate = ['date_month', 'date_quarter', 'date_year']
                                    .includes(game.releaseInfo.displayType);

                                const gameElement = $(`
    <a href="https://store.steampowered.com/app/${game.appid}"
       target="_blank"
       class="calendar-game ${isApproximate ? 'calendar-game-approximate wt-tooltip' : ''}">
        <img src="https://shared.cloudflare.steamstatic.com/store_item_assets/steam/apps/${game.appid}/header.jpg"
             class="calendar-game-image">
        <div class="calendar-game-title">${game.name}</div>
        ${isApproximate ?
            `<div class="wt-tooltiptext">Приблизительная дата: ${getApproximateDateText(game.releaseInfo)}</div>`
            : ''}
    </a>
`);



                                dayElement.append(gameElement);
                            });

                            grid.append(dayElement);
                        }

                        content.append(monthBlock);
                    });

                    if (visibleMonths < monthsData.length) {
                        content.append(`
                <div class="load-more-months">
                    <button class="load-more-btn">Показать ещё 3 месяца</button>
                </div>
            `);

                        content.find('.load-more-btn').click(() => {
                            visibleMonths += 3;
                            renderCalendar();
                        });
                    }
                };

                wtmodal.addClass('active');
                renderCalendar();
            }

            function getGamesByMonths(gameData) {
                const now = new Date();
                const currentYear = now.getFullYear();
                const currentMonth = now.getMonth();

                const games = Object.entries(gameData)
                    .map(([appid, game]) => ({
                        appid: parseInt(appid),
                        ...game,
                        releaseDate: game.releaseInfo.date && typeof game.releaseInfo.date === 'number' ?
                            new Date(game.releaseInfo.date * 1000) : null
                    }))
                    .filter(g => g.releaseDate)
                    .filter(g => {
                        const releaseYear = g.releaseDate.getFullYear();
                        const releaseMonth = g.releaseDate.getMonth();
                        return (releaseYear > currentYear) ||
                            (releaseYear === currentYear && releaseMonth >= currentMonth);
                    });

                const monthMap = games.reduce((acc, game) => {
                    const year = game.releaseDate.getFullYear();
                    const month = game.releaseDate.getMonth();
                    const key = `${year}-${month}`;

                    if (!acc[key]) {
                        acc[key] = {
                            year,
                            month,
                            games: []
                        };
                    }
                    acc[key].games.push(game);
                    return acc;
                }, {});

                return Object.values(monthMap)
                    .sort((a, b) => a.year === b.year ? a.month - b.month : a.year - b.year);
            }

            function getApproximateDateText(releaseInfo) {
                const date = new Date(releaseInfo.date * 1000);
                const quarter = Math.floor(date.getMonth() / 3) + 1;
                switch (releaseInfo.displayType) {
                    case 'date_month':
                        return date.toLocaleString('ru-RU', {
                            month: 'long',
                            year: 'numeric'
                        });
                    case 'date_quarter':
                        return `Q${quarter} ${date.getFullYear()}`;
                    case 'date_year':
                        return date.getFullYear().toString();
                    default:
                        return date.toLocaleDateString('ru-RU');
                }
            }

            function initialize() {
                createNotificationUI();
                updateStatusIndicator();
            }

            $(document).ready(initialize);
        })();
    }

    // Скрипт для страницы игры (Plati; отображение цен с Plati.Market) | https://store.steampowered.com/app/*
    if (scriptsConfig.platiSales && window.location.pathname.includes('/app/')) {
        (function() {
            'use strict';

            // --- Конфигурация PlatiSearch (PS) ---
            const PS_API_BASE_URL = 'https://api.digiseller.com/api/products/search2';
            const PS_SUGGEST_API_URL = 'https://plati.market/api/suggest.ashx';
            const PS_IMAGE_DOMAIN = 'digiseller.mycdn.ink';
            const PS_RESULTS_PER_PAGE_CHECK = 1;
            const PS_DEFAULT_SORT_MODE = 2; // Relevance
            const PS_SUGGEST_DEBOUNCE_MS = 300;
            const PS_FILTER_DEBOUNCE_MS = 500;
            const PS_FILTER_STORAGE_PREFIX = 'platiSalesFilter_v1_';
            const PS_EXCLUSION_STORAGE_KEY = 'platiSalesExclusions_v1_';
            const PS_LAST_SORT_STORAGE_KEY = 'platiSalesLastSort_v1_';
            const PS_CURRENCY_STORAGE_KEY = 'platiSalesCurrency_v1_';
            const PS_FILTER_PANEL_WIDTH = 230;
            const PS_EXCLUSION_PANEL_WIDTH = 250;
            const PS_SIDE_PANEL_HORIZONTAL_PADDING = 20;
            const PS_CONTENT_PADDING_BUFFER = 15;
            const PS_CONTENT_PADDING_LEFT = PS_FILTER_PANEL_WIDTH + PS_SIDE_PANEL_HORIZONTAL_PADDING + PS_CONTENT_PADDING_BUFFER;
            const PS_CONTENT_PADDING_RIGHT = PS_EXCLUSION_PANEL_WIDTH + PS_SIDE_PANEL_HORIZONTAL_PADDING + PS_CONTENT_PADDING_BUFFER;
            const PS_HEADER_APPROX_HEIGHT = 65;
            const PS_TOP_OFFSET_FOR_SIDE_PANELS = PS_HEADER_APPROX_HEIGHT + 25;
            const PS_BOTTOM_OFFSET_FOR_SIDE_PANELS = 20;
            const PS_ADV_SORT_CONTAINER_WIDTH = 230;
            const NEW_ITEM_THRESHOLD_DAYS = 7;

            // --- Глобальные переменные ---
            let ps_currentResults = [];
            let ps_currentSort = GM_getValue(PS_LAST_SORT_STORAGE_KEY, { field: 'relevance', direction: 'asc' });
            let ps_currentCurrency = GM_getValue(PS_CURRENCY_STORAGE_KEY, 'RUR');
            let ps_firstSortClick = {};
            ['price', 'sales', 'relevance', 'name', 'date_create', 'discount', 'seller_rating', 'review_ratio', 'good_reviews', 'bad_reviews', 'returns'].forEach(field => {
                ps_firstSortClick[field] = ps_currentSort.field !== field;
            });
            let ps_exclusionKeywords = GM_getValue(PS_EXCLUSION_STORAGE_KEY, []);
            let ps_currentFilters = ps_loadFilters();
            let ps_suggestDebounceTimeout;
            let ps_filterDebounceTimeout;
            let ps_advSortMenuTimeout;

            // --- DOM Элементы ---
            let ps_modal, ps_closeBtn, ps_searchInput, ps_searchBtn, ps_sortPriceBtn, ps_sortSalesBtn, ps_advSortBtnContainer, ps_advSortBtn, ps_advSortMenu, ps_currencySelect, ps_resetSortBtn;
            let ps_resultsContainer, ps_resultsDiv, ps_statusDiv, ps_excludeInput, ps_addExcludeBtn, ps_exclusionTagsDiv;
            let ps_suggestionsDiv;
            let ps_filtersPanel;
            let ps_filterPriceMin, ps_filterPriceMax, ps_filterSalesMin, ps_filterSalesMax, ps_filterRatingMin, ps_filterRatingMax;
            let ps_filterHideBadReviews, ps_filterHideReturns, ps_filterOnlyDiscount;
            let ps_filterDateSelect;
            let ps_resetAllFiltersBtn;
            let ps_exclusionTagsListDiv;

            // --- Описания сортировок ---
             const ps_advancedSorts = {
                 'price':        { name: 'По цене',          defaultDir: 'asc' },
                 'sales':        { name: 'По продажам',      defaultDir: 'desc'},
                 'relevance':    { name: 'По релевантности', defaultDir: 'asc' },
                 'name':         { name: 'По названию',      defaultDir: 'asc'  },
                 'date_create':  { name: 'По дате добавления', defaultDir: 'desc' },
                 'discount':     { name: 'По % в скид. системе', defaultDir: 'desc' },
                 'seller_rating':{ name: 'По рейтингу продавца', defaultDir: 'desc' },
                 'review_ratio': { name: 'По соотношению отзывов', defaultDir: 'desc' },
                 'good_reviews': { name: 'По кол-ву хор. отзывов', defaultDir: 'desc' },
                 'bad_reviews':  { name: 'По кол-ву плох. отзывов', defaultDir: 'asc'  },
                 'returns':      { name: 'По кол-ву возвратов', defaultDir: 'asc'  }
            };
            const ps_advSortOrder = ['name', 'date_create', 'discount', 'seller_rating', 'review_ratio', 'good_reviews', 'bad_reviews', 'returns'];
            const ps_dateFilterOptions = {
                 'all': 'За все время', '1d': 'За сутки', '2d': 'За 2 дня', '1w': 'За неделю', '1m': 'За месяц', '6m': 'За полгода', '1y': 'За год', '5y': 'За 5 лет', '10y': 'За 10 лет',
            };

            // --- Вспомогательные функции ---
            function formatPrice(priceStr) {
                if (!priceStr) return 0;
                return parseFloat(String(priceStr).replace(/[^\d,.]/g, '').replace(',', '.')) || 0;
            }
            function formatSales(salesStr) {
                if (!salesStr) return 0;
                return parseInt(String(salesStr).replace(/\D/g, ''), 10) || 0;
            }
             function parseSellerRating(ratingStr) {
                 if (!ratingStr) return 0;
                 return parseFloat(String(ratingStr).replace(',', '.')) || 0;
             }
             function calculateReviewRatio(item) {
                 const good = parseInt(item.cnt_good_responses || '0', 10);
                 const bad = parseInt(item.cnt_bad_responses || '0', 10);
                 const total = good + bad;
                 return total > 0 ? (good / total) : -1;
             }
             function parseDate(dateStr) {
                 if (!dateStr) return 0;
                 const parts = dateStr.split(' ');
                 if (parts.length !== 2) return 0;
                 const dateParts = parts[0].split('.');
                 const timeParts = parts[1].split(':');
                 if (dateParts.length !== 3 || timeParts.length !== 3) return 0;
                 try { return new Date(Date.UTC(dateParts[2], dateParts[1] - 1, dateParts[0], timeParts[0], timeParts[1], timeParts[2])).getTime(); }
                 catch (e) { return 0; }
             }
            function formatDateString(timestamp) {
                if (!timestamp || timestamp === 0) return 'N/A';
                try {
                    const date = new Date(timestamp);
                    const day = String(date.getUTCDate()).padStart(2, '0');
                    const month = String(date.getUTCMonth() + 1).padStart(2, '0');
                    const year = String(date.getUTCFullYear()).slice(-2);
                    return `${day}.${month}.${year}`;
                } catch (e) { return 'N/A'; }
            }
             function getPriceInSelectedCurrency(item, currency) {
                 let price = 0;
                 switch (currency) {
                     case 'USD': price = formatPrice(item.price_usd); break;
                     case 'EUR': price = formatPrice(item.price_eur); break;
                     case 'UAH': price = formatPrice(item.price_uah); break;
                     case 'RUR': default: price = formatPrice(item.price_rur); break;
                 }
                 if (price <= 0 && currency !== 'RUR') price = formatPrice(item.price_rur);
                 if (price <= 0 && currency !== 'USD') price = formatPrice(item.price_usd);
                 if (price <= 0 && currency !== 'EUR') price = formatPrice(item.price_eur);
                 return price > 0 ? price : Infinity;
             }
            function debounce(func, wait) {
                let timeout;
                return function executedFunction(...args) {
                    const later = () => { clearTimeout(timeout); func(...args); };
                    clearTimeout(timeout);
                    timeout = setTimeout(later, wait);
                };
            }
            function getSteamGameName() {
                 const appNameElement = document.querySelector('#appHubAppName');
                 return appNameElement ? appNameElement.textContent.trim() : '';
            }

            // --- Создание UI ---
            function createPlatiModal() {
                 const existingModal = document.querySelector('#platiSearchModal');
                 if (existingModal) existingModal.remove();

                 ps_modal = document.createElement('div');
                 ps_modal.id = 'platiSearchModal';

                 const container = document.createElement('div');
                 container.id = 'platiSearchContainer';

                 const header = document.createElement('div');
                 header.id = 'platiSearchHeader';

                 const searchInputContainer = document.createElement('div');
                 searchInputContainer.className = 'platiSearchInputContainer';
                 ps_searchInput = document.createElement('input');
                 ps_searchInput.id = 'platiSearchInput';
                 ps_searchInput.type = 'text';
                 ps_searchInput.placeholder = 'Введите название игры или товара...';
                 ps_searchInput.autocomplete = 'off';
                 ps_searchInput.onkeydown = (e) => { if (e.key === 'Enter') ps_triggerSearch(); };
                 ps_searchInput.oninput = () => {
                     clearTimeout(ps_suggestDebounceTimeout);
                     ps_suggestDebounceTimeout = setTimeout(() => ps_fetchSuggestions(ps_searchInput.value), PS_SUGGEST_DEBOUNCE_MS);
                 };
                 ps_searchInput.onblur = () => { setTimeout(() => { if (ps_suggestionsDiv) ps_suggestionsDiv.style.display = 'none'; }, 150); };

                 ps_suggestionsDiv = document.createElement('div');
                 ps_suggestionsDiv.id = 'platiSearchSuggestions';
                 searchInputContainer.appendChild(ps_searchInput);
                 searchInputContainer.appendChild(ps_suggestionsDiv);
                 header.appendChild(searchInputContainer);

                 ps_searchBtn = document.createElement('button');
                 ps_searchBtn.textContent = 'Найти';
                 ps_searchBtn.id = 'platiSearchGoBtn';
                 ps_searchBtn.className = 'platiSearchBtn';
                 ps_searchBtn.onclick = ps_triggerSearch;
                 header.appendChild(ps_searchBtn);

                 ps_resetSortBtn = document.createElement('button');
                 ps_resetSortBtn.id = 'platiResetSortBtn';
                 ps_resetSortBtn.className = 'platiSearchBtn';
                 ps_resetSortBtn.title = 'Сбросить сортировку (Релевантность)';
                 ps_resetSortBtn.innerHTML = `<svg viewBox="0 0 24 24"><path fill="currentColor" d="M12 4V1L8 5l4 4V6c3.31 0 6 2.69 6 6s-2.69 6-6 6s-6-2.69-6-6H4c0 4.42 3.58 8 8 8s8-3.58 8-8s-3.58-8-8-8Z"/></svg>`;
                 ps_resetSortBtn.onclick = () => ps_resetSort(true);
                 header.appendChild(ps_resetSortBtn);

                 ps_sortPriceBtn = document.createElement('button');
                 ps_sortPriceBtn.className = 'platiSearchBtn sortBtn';
                 ps_sortPriceBtn.dataset.sort = 'price';
                 ps_sortPriceBtn.onclick = () => ps_handleSort('price');
                 header.appendChild(ps_sortPriceBtn);

                 ps_sortSalesBtn = document.createElement('button');
                 ps_sortSalesBtn.className = 'platiSearchBtn sortBtn';
                 ps_sortSalesBtn.dataset.sort = 'sales';
                 ps_sortSalesBtn.onclick = () => ps_handleSort('sales');
                 header.appendChild(ps_sortSalesBtn);

                 ps_advSortBtnContainer = document.createElement('div');
                 ps_advSortBtnContainer.id = 'platiSearchAdvSortBtnContainer';
                 ps_advSortBtn = document.createElement('button');
                 ps_advSortBtn.id = 'platiSearchAdvSortBtn';
                 ps_advSortBtn.className = 'platiSearchBtn sortBtn';
                 ps_advSortBtnContainer.appendChild(ps_advSortBtn);
                 ps_advSortMenu = document.createElement('div');
                 ps_advSortMenu.id = 'platiSearchAdvSortMenu';
                 ps_advSortOrder.forEach(key => {
                     const sortInfo = ps_advancedSorts[key];
                     const menuItem = document.createElement('div');
                     menuItem.className = 'platiSearchSortMenuItem';
                     menuItem.dataset.sort = key;
                     menuItem.innerHTML = `${sortInfo.name} <span class="sortArrow"></span>`; // Стрелка добавится в update
                     menuItem.onclick = () => ps_handleSort(key);
                     ps_advSortMenu.appendChild(menuItem);
                 });
                 ps_advSortBtnContainer.appendChild(ps_advSortMenu);
                 header.appendChild(ps_advSortBtnContainer);

                 ps_currencySelect = document.createElement('select');
                 ps_currencySelect.id = 'platiSearchCurrencySelect';
                 ['RUR', 'USD', 'EUR', 'UAH'].forEach(curr => {
                     const option = document.createElement('option');
                     option.value = curr; option.textContent = curr;
                     if (curr === ps_currentCurrency) option.selected = true;
                     ps_currencySelect.appendChild(option);
                 });
                 ps_currencySelect.onchange = ps_handleCurrencyChange;
                 header.appendChild(ps_currencySelect);

                 container.appendChild(header);

                 ps_resultsContainer = document.createElement('div');
                 ps_resultsContainer.id = 'platiSearchResultsContainer';
                 ps_statusDiv = document.createElement('div');
                 ps_statusDiv.id = 'platiSearchResultsStatus';
                 ps_resultsDiv = document.createElement('div');
                 ps_resultsDiv.id = 'platiSearchResults';
                 ps_resultsContainer.appendChild(ps_statusDiv);
                 ps_resultsContainer.appendChild(ps_resultsDiv);
                 container.appendChild(ps_resultsContainer);
                 ps_modal.appendChild(container);

                 ps_filtersPanel = document.createElement('div');
                 ps_filtersPanel.id = 'platiSearchFiltersPanel';
                 ps_filtersPanel.innerHTML = `
                    <div class="filterGroup"> <h4>Цена (${ps_currentCurrency}) ${ps_createResetButtonHTML('price')}</h4> <div class="filterRangeInputs"> <input type="number" id="psFilterPriceMin" placeholder="от" min="0"> <input type="number" id="psFilterPriceMax" placeholder="до" min="0"> </div> </div>
                    <div class="filterGroup"> <h4>Продажи ${ps_createResetButtonHTML('sales')}</h4> <div class="filterRangeInputs"> <input type="number" id="psFilterSalesMin" placeholder="от" min="0"> <input type="number" id="psFilterSalesMax" placeholder="до" min="0"> </div> </div>
                    <div class="filterGroup"> <h4>Рейтинг продавца ${ps_createResetButtonHTML('rating')}</h4> <div class="filterRangeInputs"> <input type="number" id="psFilterRatingMin" placeholder="от" step="0.1" min="0"> <input type="number" id="psFilterRatingMax" placeholder="до" step="0.1" min="0"> </div> </div>
                    <div class="filterGroup"> <h4>Опции ${ps_createResetButtonHTML('options')}</h4> <div class="filterCheckbox"> <label><input type="checkbox" id="psFilterHideBadReviews"> Скрыть с плох. отзывами</label> </div> <div class="filterCheckbox"> <label><input type="checkbox" id="psFilterHideReturns"> Скрыть с возвратами</label> </div> <div class="filterCheckbox"> <label><input type="checkbox" id="psFilterOnlyDiscount"> Участие в скидках</label> </div> </div>
                    <div class="filterGroup"> <h4>Дата добавления ${ps_createResetButtonHTML('date')}</h4> <div class="filterSelect"> <select id="psFilterDateSelect"> ${Object.entries(ps_dateFilterOptions).map(([key, text]) => `<option value="${key}">${text}</option>`).join('')} </select> </div> </div>
                    <button id="psResetAllFiltersBtn" class="platiSearchBtn">Сбросить все фильтры</button>
                 `;
                 ps_modal.appendChild(ps_filtersPanel);

                 ps_exclusionTagsDiv = document.createElement('div');
                 ps_exclusionTagsDiv.id = 'platiSearchExclusionTags';
                 const exclusionInputGroup = document.createElement('div');
                 exclusionInputGroup.className = 'exclusionInputGroup';
                 ps_excludeInput = document.createElement('input');
                 ps_excludeInput.type = 'text';
                 ps_excludeInput.id = 'platiSearchExcludeInput';
                 ps_excludeInput.placeholder = 'Исключить слово';
                 ps_excludeInput.onkeydown = (e) => { if (e.key === 'Enter') ps_addFilterKeyword(); };
                 ps_addExcludeBtn = document.createElement('button');
                 ps_addExcludeBtn.id = 'platiSearchAddExcludeBtn';
                 ps_addExcludeBtn.innerHTML = `<svg viewBox="0 0 20 20"><path d="M10 2.5a.75.75 0 0 1 .75.75v6h6a.75.75 0 0 1 0 1.5h-6v6a.75.75 0 0 1-1.5 0v-6h-6a.75.75 0 0 1 0-1.5h6v-6a.75.75 0 0 1 .75-.75Z" /></svg>`;
                 ps_addExcludeBtn.onclick = ps_addFilterKeyword;
                 exclusionInputGroup.appendChild(ps_excludeInput);
                 exclusionInputGroup.appendChild(ps_addExcludeBtn);
                 ps_exclusionTagsDiv.appendChild(exclusionInputGroup);
                 ps_exclusionTagsListDiv = document.createElement('div');
                 ps_exclusionTagsListDiv.id = 'platiExclusionTagsList';
                 ps_exclusionTagsDiv.appendChild(ps_exclusionTagsListDiv);
                 ps_modal.appendChild(ps_exclusionTagsDiv);

                 ps_closeBtn = document.createElement('button');
                 ps_closeBtn.id = 'platiSearchCloseBtn';
                 ps_closeBtn.innerHTML = '&times;';
                 ps_closeBtn.onclick = hidePlatiModal;
                 ps_modal.appendChild(ps_closeBtn);

                 document.body.appendChild(ps_modal);

                 // Назначение переменных элементам UI
                 ps_filterPriceMin = document.getElementById('psFilterPriceMin');
                 ps_filterPriceMax = document.getElementById('psFilterPriceMax');
                 ps_filterSalesMin = document.getElementById('psFilterSalesMin');
                 ps_filterSalesMax = document.getElementById('psFilterSalesMax');
                 ps_filterRatingMin = document.getElementById('psFilterRatingMin');
                 ps_filterRatingMax = document.getElementById('psFilterRatingMax');
                 ps_filterHideBadReviews = document.getElementById('psFilterHideBadReviews');
                 ps_filterHideReturns = document.getElementById('psFilterHideReturns');
                 ps_filterOnlyDiscount = document.getElementById('psFilterOnlyDiscount');
                 ps_filterDateSelect = document.getElementById('psFilterDateSelect');
                 ps_resetAllFiltersBtn = document.getElementById('psResetAllFiltersBtn');

                 ps_addFilterEventListeners();
                 applyLoadedFiltersToUI();
                 ps_updateSortButtonsState(); // Устанавливаем начальное состояние кнопок сортировки

                 function handleEsc(event) { if (event.key === 'Escape') hidePlatiModal(); }
                 document.addEventListener('keydown', handleEsc);
                 ps_modal._escHandler = handleEsc;
            }

            function ps_createResetButtonHTML(filterKey) {
                 return `<button class="filterResetBtn" title="Сбросить фильтр" data-filter-key="${filterKey}"><svg viewBox="0 0 24 24"><path d="M13 3a9 9 0 0 0-9 9H1l3.89 3.89.07.14L9 12H6c0-3.87 3.13-7 7-7s7 3.13 7 7-3.13 7-7 7c-1.93 0-3.68-.79-4.94-2.06l-1.42 1.42A8.954 8.954 0 0 0 13 21a9 9 0 0 0 0-18zm-1 5v5l4.28 2.54.72-1.21-3.5-2.08V8H12z"></path></svg></button>`;
            }

            // --- Управление Модальным Окном ---
            function showPlatiModal() {
                 if (!ps_modal) createPlatiModal();
                 const gameName = getSteamGameName();
                 if (gameName && !ps_searchInput.value) { ps_searchInput.value = gameName; }

                 document.body.style.overflow = 'hidden';
                 ps_modal.style.display = 'block';
                 ps_modal.scrollTop = 0;

                 ps_renderExclusionTags();
                 applyLoadedFiltersToUI();
                 ps_updateFilterPlaceholders();
                 ps_updateSortButtonsState();

                 requestAnimationFrame(() => {
                    const header = document.getElementById('platiSearchHeader');
                    const headerRect = header ? header.getBoundingClientRect() : { bottom: PS_TOP_OFFSET_FOR_SIDE_PANELS };
                    const newTopOffset = headerRect.bottom + 5;
                    const availableHeight = `calc(100vh - ${newTopOffset}px - ${PS_BOTTOM_OFFSET_FOR_SIDE_PANELS}px)`;
                    if (ps_filtersPanel) { ps_filtersPanel.style.top = `${newTopOffset}px`; ps_filtersPanel.style.maxHeight = availableHeight;}
                    if (ps_exclusionTagsDiv) { ps_exclusionTagsDiv.style.top = `${newTopOffset}px`; ps_exclusionTagsDiv.style.maxHeight = availableHeight; }
                 });

                 if (ps_searchInput.value.trim()) { ps_triggerSearch(); }
                 else { ps_updateStatus('Введите запрос для поиска.'); }
            }
            function hidePlatiModal() {
                 if (ps_modal) {
                     ps_modal.style.display = 'none';
                     if (ps_suggestionsDiv) ps_suggestionsDiv.style.display = 'none';
                      if (ps_modal._escHandler) { document.removeEventListener('keydown', ps_modal._escHandler); delete ps_modal._escHandler; }
                 }
                 document.body.style.overflow = '';
            }

            // --- Обновление статуса ---
            function ps_updateStatus(message, isLoading = false) {
                if (ps_statusDiv) {
                    ps_statusDiv.innerHTML = message + (isLoading ? ' <span class="spinner"></span>' : '');
                    ps_statusDiv.style.display = 'block';
                    if(ps_currentResults.length === 0 && message && !isLoading) {
                        ps_resultsDiv.innerHTML = '';
                    }
                }
            }

            // --- Запуск поиска ---
            function ps_triggerSearch() {
                const query = ps_searchInput.value.trim();
                if (ps_suggestionsDiv) ps_suggestionsDiv.style.display = 'none';
                if (!query) {
                    ps_updateStatus('Пожалуйста, введите запрос.');
                    ps_currentResults = []; ps_renderResults(); return;
                }
                ps_currentResults = [];
                ps_resetSort(false); // Сброс на релевантность без рендера
                applyLoadedFiltersToUI();
                ps_renderResults();
                ps_updateStatus('Получение общего количества товаров...', true);
                ps_fetchTotalCount(query);
            }

            // --- Функции подсказок ---
            function ps_fetchSuggestions(query) {
                 const trimmedQuery = query.trim();
                 if (trimmedQuery.length < 2) {
                     if (ps_suggestionsDiv) { ps_suggestionsDiv.innerHTML = ''; ps_suggestionsDiv.style.display = 'none'; } return;
                 }
                 const params = new URLSearchParams({ q: trimmedQuery, v: 2 });
                 try { if (typeof plang !== 'undefined') params.append('lang', plang); if (typeof clientgeo !== 'undefined') params.append('geo', clientgeo); }
                 catch (e) { console.warn("PlatiSearch: Could not get plang/clientgeo for suggestions."); }

                 GM_xmlhttpRequest({
                     method: "GET", url: `${PS_SUGGEST_API_URL}?${params.toString()}`, timeout: 5000,
                     onload: function(response) {
                         try { ps_renderSuggestions(JSON.parse(response.responseText)); }
                         catch (e) { if (ps_suggestionsDiv) { ps_suggestionsDiv.innerHTML = ''; ps_suggestionsDiv.style.display = 'none'; } }
                     },
                     onerror: function(error) { if (ps_suggestionsDiv) { ps_suggestionsDiv.innerHTML = ''; ps_suggestionsDiv.style.display = 'none'; } },
                     ontimeout: function() { if (ps_suggestionsDiv) { ps_suggestionsDiv.innerHTML = ''; ps_suggestionsDiv.style.display = 'none'; } }
                 });
            }
            function ps_renderSuggestions(suggestions) {
                 if (!ps_suggestionsDiv) return;
                 if (!suggestions || !Array.isArray(suggestions) || suggestions.length === 0) {
                     ps_suggestionsDiv.innerHTML = ''; ps_suggestionsDiv.style.display = 'none'; return;
                 }
                 ps_suggestionsDiv.innerHTML = '';
                 let addedSuggestions = 0;
                 suggestions.forEach(suggestion => {
                     if (suggestion && suggestion.name && (suggestion.type === "Товары" || suggestion.type === "Search" || suggestion.type === "Игры")) {
                         const item = document.createElement('div');
                         item.className = 'suggestionItem';
                         item.textContent = suggestion.name;
                         item.onmousedown = (e) => {
                             e.preventDefault(); ps_searchInput.value = suggestion.name;
                             ps_suggestionsDiv.style.display = 'none'; ps_triggerSearch();
                         };
                         ps_suggestionsDiv.appendChild(item);
                         addedSuggestions++;
                     }
                 });
                 ps_suggestionsDiv.style.display = addedSuggestions > 0 ? 'block' : 'none';
            }

            // --- Запросы API ---
            function ps_fetchTotalCount(query) {
                 // При запросе количества всегда используем сортировку по релевантности (дефолт API)
                 const params = new URLSearchParams({
                     query: query, searchmode: 10, sortmode: PS_DEFAULT_SORT_MODE,
                     pagesize: PS_RESULTS_PER_PAGE_CHECK, pagenum: 1, owner: 1,
                     details: 1, checkhidesales: 1, host: 'plati.market'
                 });
                 GM_xmlhttpRequest({
                     method: "GET", url: `${PS_API_BASE_URL}?${params.toString()}`, timeout: 15000, responseType: 'json',
                     onload: function(response) {
                         if (response.status >= 200 && response.status < 400 && response.response) {
                             const data = response.response;
                             if (data?.result?.total > 0) {
                                 const total = data.result.total;
                                 ps_updateStatus(`Найдено ${total} товаров. Загрузка...`, true);
                                 // Загружаем все результаты с дефолтной сортировкой API (релевантность)
                                 ps_fetchAllResults(query, total, PS_DEFAULT_SORT_MODE);
                             } else {
                                 ps_updateStatus(`По запросу "${query}" ничего не найдено.`);
                                 ps_currentResults = []; ps_renderResults(); ps_updateFilterPlaceholders(); ps_applyFilters();
                             }
                         } else { ps_updateStatus(`Ошибка получения общего количества товаров (Статус: ${response.status})`); }
                     },
                     onerror: function(error) { ps_updateStatus('Ошибка сети при получении общего количества товаров.'); },
                     ontimeout: function() { ps_updateStatus('Время ожидания ответа от сервера (количество) истекло.'); }
                 });
            }
            function ps_fetchAllResults(query, total, sortMode) {
                 const MAX_PAGE_SIZE = 1000;
                 const effectivePageSize = Math.min(total, MAX_PAGE_SIZE);
                 if (total > MAX_PAGE_SIZE) ps_updateStatus(`Найдено ${total} товаров. Загрузка первых ${MAX_PAGE_SIZE}...`, true);

                 const params = new URLSearchParams({
                     query: query, searchmode: 10, sortmode: sortMode, pagesize: effectivePageSize,
                     pagenum: 1, owner: 1, details: 1, checkhidesales: 1, host: 'plati.market'
                 });
                 GM_xmlhttpRequest({
                     method: "GET", url: `${PS_API_BASE_URL}?${params.toString()}`, timeout: 90000, responseType: 'json',
                     onload: function(response) {
                          // Проверяем, открыто ли еще модальное окно
                          if (!document.body.contains(ps_modal)) return;

                         if (response.status >= 200 && response.status < 400 && response.response) {
                             const data = response.response;
                             if (data?.items?.item && Array.isArray(data.items.item)) {
                                 ps_currentResults = data.items.item.map((item, index) => ({ ...item, originalIndex: index }));
                                 const loadedCount = ps_currentResults.length;
                                 ps_updateStatus(`Загружено ${loadedCount}${total > loadedCount ? ` из ${total}` : ''} товаров.`);
                                 // После загрузки применяем ТЕКУЩУЮ выбранную пользователем сортировку
                                 ps_applySort(ps_currentSort.field, ps_currentSort.direction);
                                 ps_renderResults();
                                 ps_updateFilterPlaceholders();
                                 ps_applyFilters();
                             } else {
                                 ps_updateStatus(`Ошибка загрузки товаров: неверный формат ответа API.`);
                                 ps_currentResults = []; ps_renderResults(); ps_updateFilterPlaceholders(); ps_applyFilters();
                             }
                         } else { ps_updateStatus(`Ошибка загрузки товаров (Статус: ${response.status})`); }
                     },
                     onerror: function(error) { if (document.body.contains(ps_modal)) ps_updateStatus('Ошибка сети при загрузке товаров.'); },
                     ontimeout: function() { if (document.body.contains(ps_modal)) ps_updateStatus('Время ожидания ответа от сервера (товары) истекло.'); }
                 });
            }

            // --- Сортировка ---
             function ps_handleSort(field) {
                 let newDirection;
                 const sortInfo = ps_advancedSorts[field];
                 if (!sortInfo) return; // Неизвестное поле сортировки

                 // Определяем текущее направление из сохраненного состояния
                 let currentDir = (ps_currentSort.field === field) ? ps_currentSort.direction : sortInfo.defaultDir;

                 // Определяем новое направление
                 if (ps_firstSortClick[field] || ps_currentSort.field !== field) {
                     // Если это первый клик по этому полю или клик по новому полю, используем дефолтное направление
                     newDirection = sortInfo.defaultDir;
                 } else {
                     // Иначе инвертируем текущее направление
                     newDirection = currentDir === 'desc' ? 'asc' : 'desc';
                 }

                 // Обновляем флаги первого клика
                 Object.keys(ps_firstSortClick).forEach(key => {
                     ps_firstSortClick[key] = (key !== field);
                 });
                 ps_firstSortClick[field] = false; // Устанавливаем, что это уже не первый клик

                 // Сохраняем новое состояние
                 ps_currentSort.field = field;
                 ps_currentSort.direction = newDirection;
                 GM_setValue(PS_LAST_SORT_STORAGE_KEY, ps_currentSort);

                 // Применяем сортировку КЛИЕНТСКИ к уже загруженным данным
                 ps_applySort(field, newDirection);
                 ps_renderResults(); // Перерисовываем с новой сортировкой
                 ps_updateSortButtonsState(); // Обновляем UI кнопок
             }

             function ps_updateSortButtonsState() {
                 const activeField = ps_currentSort.field;
                 const activeDirection = ps_currentSort.direction;

                 // Основные кнопки (Цена, Продажи)
                  $(ps_sortPriceBtn).add(ps_sortSalesBtn).each(function() {
                     const $btn = $(this);
                     const btnField = $btn.data('sort');
                     const baseText = (btnField === 'price') ? 'Цена' : 'Продажи';
                     if (btnField === activeField) {
                         const arrow = activeDirection === 'asc' ? ' ▲' : ' ▼';
                         $btn.addClass('active').text(baseText + arrow).attr('data-dir', activeDirection);
                     } else {
                         const defaultDir = ps_advancedSorts[btnField].defaultDir;
                         const defaultArrow = defaultDir === 'asc' ? ' ▲' : ' ▼';
                         $btn.removeClass('active').text(baseText + defaultArrow).attr('data-dir', defaultDir);
                     }
                 });

                 // Кнопка и меню доп. сортировки
                 let advBtnText = 'Доп. сорт.';
                 const $advButton = $(ps_advSortBtn);
                 const isAdvSortActive = ps_advancedSorts[activeField] && activeField !== 'price' && activeField !== 'sales' && activeField !== 'relevance';

                 if (isAdvSortActive) {
                     $advButton.addClass('active');
                     const arrow = activeDirection === 'asc' ? ' ▲' : ' ▼';
                     advBtnText = `${ps_advancedSorts[activeField].name}${arrow}`;
                 } else {
                     $advButton.removeClass('active');
                 }
                 $advButton.text(advBtnText);

                 // Пункты меню
                 $('#platiSearchAdvSortMenu .platiSearchSortMenuItem').each(function() {
                     const $item = $(this);
                     const itemField = $item.data('sort');
                     const baseText = ps_advancedSorts[itemField].name;
                     if (itemField === activeField) {
                         const arrow = activeDirection === 'asc' ? ' ▲' : ' ▼';
                         $item.addClass('active').html(`${baseText} <span class="sortArrow">${arrow}</span>`).attr('data-dir', activeDirection);
                     } else {
                         const defaultDir = ps_advancedSorts[itemField].defaultDir;
                         const defaultArrow = defaultDir === 'asc' ? ' ▲' : ' ▼';
                         $item.removeClass('active').html(`${baseText} <span class="sortArrow">${defaultArrow}</span>`).attr('data-dir', defaultDir);
                     }
                 });

                 // Кнопка сброса показывает релевантность
                 if (activeField === 'relevance') {
                     $(ps_resetSortBtn).addClass('active');
                 } else {
                      $(ps_resetSortBtn).removeClass('active');
                 }
             }

            function ps_resetSort(render = true) {
                 ps_currentSort = { field: 'relevance', direction: 'asc' }; // Релевантность - это исходный порядок API
                 ps_firstSortClick = {
                     price: true, sales: true, relevance: false, name: true, date_create: true, discount: true,
                     seller_rating: true, review_ratio: true, good_reviews: true, bad_reviews: true, returns: true
                 };
                 GM_setValue(PS_LAST_SORT_STORAGE_KEY, ps_currentSort);
                 ps_updateSortButtonsState();

                 if (render) {
                     ps_applySort(ps_currentSort.field, ps_currentSort.direction);
                     ps_renderResults();
                 }
             }

            function ps_applySort(field, direction) {
                 const dirMultiplier = direction === 'asc' ? 1 : -1;
                 const selectedCurrency = ps_currencySelect ? ps_currencySelect.value.toUpperCase() : 'RUR';
                 ps_currentResults.sort((a, b) => {
                     let valA, valB;
                     const nameA = (a.name || '').toLowerCase();
                     const nameB = (b.name || '').toLowerCase();
                     const finalPriceA = getPriceInSelectedCurrency(a, selectedCurrency);
                     const finalPriceB = getPriceInSelectedCurrency(b, selectedCurrency);
                     let comparisonResult = 0;
                     switch (field) {
                         case 'price':         valA = finalPriceA; valB = finalPriceB; break;
                         case 'sales':         valA = formatSales(a.cnt_sell); valB = formatSales(b.cnt_sell); break;
                         case 'name':          comparisonResult = nameA.localeCompare(nameB) * dirMultiplier; break;
                         case 'date_create':   valA = parseDate(a.date_create); valB = parseDate(b.date_create); break;
                         case 'discount':      valA = parseInt(a.discount || '0', 10); valB = parseInt(b.discount || '0', 10); break;
                         case 'seller_rating': valA = parseSellerRating(a.seller_rating); valB = parseSellerRating(b.seller_rating); break;
                         case 'review_ratio':  valA = calculateReviewRatio(a); valB = calculateReviewRatio(b); break;
                         case 'good_reviews':  valA = parseInt(a.cnt_good_responses || '0', 10); valB = parseInt(b.cnt_good_responses || '0', 10); break;
                         case 'bad_reviews':   valA = parseInt(a.cnt_bad_responses || '0', 10); valB = parseInt(b.cnt_bad_responses || '0', 10); break;
                         case 'returns':       valA = parseInt(a.cnt_return || '0', 10); valB = parseInt(b.cnt_return || '0', 10); break;
                         case 'relevance':     valA = a.originalIndex; valB = b.originalIndex; break;
                         default: return 0;
                     }
                     if (field !== 'name') {
                         const fallbackAsc = Infinity; const fallbackDesc = -Infinity;
                         if (valA === null || valA === undefined || isNaN(valA) || valA === Infinity || valA === -Infinity) valA = direction === 'asc' ? fallbackAsc : fallbackDesc;
                         if (valB === null || valB === undefined || isNaN(valB) || valB === Infinity || valB === -Infinity) valB = direction === 'asc' ? fallbackAsc : fallbackDesc;
                         if (valA < valB) comparisonResult = -1; else if (valA > valB) comparisonResult = 1; else comparisonResult = 0;
                         comparisonResult *= dirMultiplier;
                     }
                      // Вторичная сортировка для стабильности
                     if (comparisonResult === 0) {
                         if (field !== 'name') { let nameCompare = nameA.localeCompare(nameB); if (nameCompare !== 0) return nameCompare; }
                         if (field !== 'price') { if (finalPriceA < finalPriceB) return -1; if (finalPriceA > finalPriceB) return 1; }
                         if (field !== 'relevance') { return a.originalIndex - b.originalIndex; }
                     }
                     return comparisonResult;
                 });
            }

            // --- Управление Фильтрами ---
            function ps_getFilterStorageKey(key) { return `${PS_FILTER_STORAGE_PREFIX}${key}`; }
            function ps_loadFilters() {
                 const defaults = { priceMin: '', priceMax: '', salesMin: '', salesMax: '', ratingMin: '', ratingMax: '', hideBadReviews: false, hideReturns: false, onlyDiscount: false, date: 'all' };
                 let loaded = {};
                 for (const key in defaults) { loaded[key] = GM_getValue(ps_getFilterStorageKey(key), defaults[key]); }
                 return loaded;
            }
            function ps_saveFilter(key, value) { ps_currentFilters[key] = value; GM_setValue(ps_getFilterStorageKey(key), value); }
            function applyLoadedFiltersToUI() {
                 if (!ps_filtersPanel) return;
                 ps_filterPriceMin.value = ps_currentFilters.priceMin; ps_filterPriceMax.value = ps_currentFilters.priceMax;
                 ps_filterSalesMin.value = ps_currentFilters.salesMin; ps_filterSalesMax.value = ps_currentFilters.salesMax;
                 ps_filterRatingMin.value = ps_currentFilters.ratingMin; ps_filterRatingMax.value = ps_currentFilters.ratingMax;
                 ps_filterHideBadReviews.checked = ps_currentFilters.hideBadReviews; ps_filterHideReturns.checked = ps_currentFilters.hideReturns;
                 ps_filterOnlyDiscount.checked = ps_currentFilters.onlyDiscount; ps_filterDateSelect.value = ps_currentFilters.date;
                 const priceHeader = ps_filtersPanel.querySelector('.filterGroup h4');
                 if (priceHeader && priceHeader.textContent.includes('Цена')) {
                     priceHeader.innerHTML = `Цена (${ps_currentCurrency}) ${ps_createResetButtonHTML('price')}`;
                     const resetButton = priceHeader.querySelector('.filterResetBtn');
                     if (resetButton) resetButton.onclick = ps_handleFilterReset;
                 }
            }
            function ps_addFilterEventListeners() {
                 if (!ps_filtersPanel) return;
                 const debouncedApply = debounce(ps_applyFilters, PS_FILTER_DEBOUNCE_MS);
                 ps_filterPriceMin.addEventListener('input', (e) => { ps_saveFilter('priceMin', e.target.value); debouncedApply(); });
                 ps_filterPriceMax.addEventListener('input', (e) => { ps_saveFilter('priceMax', e.target.value); debouncedApply(); });
                 ps_filterSalesMin.addEventListener('input', (e) => { ps_saveFilter('salesMin', e.target.value); debouncedApply(); });
                 ps_filterSalesMax.addEventListener('input', (e) => { ps_saveFilter('salesMax', e.target.value); debouncedApply(); });
                 ps_filterRatingMin.addEventListener('input', (e) => { ps_saveFilter('ratingMin', e.target.value); debouncedApply(); });
                 ps_filterRatingMax.addEventListener('input', (e) => { ps_saveFilter('ratingMax', e.target.value); debouncedApply(); });
                 ps_filterHideBadReviews.addEventListener('change', (e) => { ps_saveFilter('hideBadReviews', e.target.checked); ps_applyFilters(); });
                 ps_filterHideReturns.addEventListener('change', (e) => { ps_saveFilter('hideReturns', e.target.checked); ps_applyFilters(); });
                 ps_filterOnlyDiscount.addEventListener('change', (e) => { ps_saveFilter('onlyDiscount', e.target.checked); ps_applyFilters(); });
                 ps_filterDateSelect.addEventListener('change', (e) => { ps_saveFilter('date', e.target.value); ps_applyFilters(); });
                 ps_resetAllFiltersBtn.addEventListener('click', () => ps_resetAllFilters(true));
                 ps_filtersPanel.querySelectorAll('.filterResetBtn').forEach(btn => { btn.onclick = ps_handleFilterReset; });
            }
            function ps_handleFilterReset(event) { ps_resetFilterByKey(event.currentTarget.dataset.filterKey, true); }
            function ps_resetFilterByKey(key, apply = true) {
                 switch (key) {
                     case 'price': ps_saveFilter('priceMin', ''); if (ps_filterPriceMin) ps_filterPriceMin.value = ''; ps_saveFilter('priceMax', ''); if (ps_filterPriceMax) ps_filterPriceMax.value = ''; break;
                     case 'sales': ps_saveFilter('salesMin', ''); if (ps_filterSalesMin) ps_filterSalesMin.value = ''; ps_saveFilter('salesMax', ''); if (ps_filterSalesMax) ps_filterSalesMax.value = ''; break;
                     case 'rating': ps_saveFilter('ratingMin', ''); if (ps_filterRatingMin) ps_filterRatingMin.value = ''; ps_saveFilter('ratingMax', ''); if (ps_filterRatingMax) ps_filterRatingMax.value = ''; break;
                     case 'options': ps_saveFilter('hideBadReviews', false); if (ps_filterHideBadReviews) ps_filterHideBadReviews.checked = false; ps_saveFilter('hideReturns', false); if (ps_filterHideReturns) ps_filterHideReturns.checked = false; ps_saveFilter('onlyDiscount', false); if (ps_filterOnlyDiscount) ps_filterOnlyDiscount.checked = false; break;
                     case 'date': ps_saveFilter('date', 'all'); if (ps_filterDateSelect) ps_filterDateSelect.value = 'all'; break;
                 }
                 if (apply) ps_applyFilters();
            }
            function ps_resetAllFilters(apply = true) {
                 const filterKeys = ['price', 'sales', 'rating', 'options', 'date'];
                 filterKeys.forEach(key => ps_resetFilterByKey(key, false));
                 if (apply) ps_applyFilters();
            }
            function ps_updateFilterPlaceholders() {
                 if (!ps_filtersPanel || !ps_currentResults || ps_currentResults.length === 0) {
                      $('#psFilterPriceMin, #psFilterPriceMax, #psFilterSalesMin, #psFilterSalesMax, #psFilterRatingMin, #psFilterRatingMax').attr('placeholder', '-'); return;
                 }
                 let minPrice = Infinity, maxPrice = -Infinity, minSales = Infinity, maxSales = -Infinity, minRating = Infinity, maxRating = -Infinity;
                 const selectedCurrency = ps_currencySelect ? ps_currencySelect.value.toUpperCase() : 'RUR';
                 ps_currentResults.forEach(item => {
                     const price = getPriceInSelectedCurrency(item, selectedCurrency); const sales = formatSales(item.cnt_sell); const rating = parseSellerRating(item.seller_rating);
                     if (price !== Infinity && price < minPrice) minPrice = price; if (price !== Infinity && price > maxPrice) maxPrice = price;
                     if (sales < minSales) minSales = sales; if (sales > maxSales) maxSales = sales;
                     if (rating > 0 && rating < minRating) minRating = rating; if (rating > maxRating) maxRating = rating;
                 });
                 if (minRating === Infinity) minRating = 0;
                 if (ps_filterPriceMin) ps_filterPriceMin.placeholder = minPrice === Infinity ? '-' : `от ${Math.floor(minPrice)}`; if (ps_filterPriceMax) ps_filterPriceMax.placeholder = maxPrice === -Infinity ? '-' : `до ${Math.ceil(maxPrice)}`;
                 if (ps_filterSalesMin) ps_filterSalesMin.placeholder = minSales === Infinity ? '-' : `от ${minSales}`; if (ps_filterSalesMax) ps_filterSalesMax.placeholder = maxSales === -Infinity ? '-' : `до ${maxSales}`;
                 if (ps_filterRatingMin) ps_filterRatingMin.placeholder = minRating === Infinity ? '-' : `от ${minRating.toFixed(1)}`; if (ps_filterRatingMax) ps_filterRatingMax.placeholder = maxRating === -Infinity ? '-' : `до ${maxRating.toFixed(1)}`;
            }
            function ps_getDateThreshold(periodKey) {
                const now = Date.now(); let threshold = 0; const dayMs = 86400000;
                switch (periodKey) {
                    case '1d': threshold = now - 1 * dayMs; break; case '2d': threshold = now - 2 * dayMs; break; case '1w': threshold = now - 7 * dayMs; break;
                    case '1m': threshold = now - 30 * dayMs; break; case '6m': threshold = now - 182 * dayMs; break; case '1y': threshold = now - 365 * dayMs; break;
                    case '5y': threshold = now - 5 * 365 * dayMs; break; case '10y': threshold = now - 10 * 365 * dayMs; break; default: threshold = 0; break;
                } return threshold;
            }
            function ps_applyFilters() {
                 if (!ps_resultsDiv || !ps_currentResults) return;
                 const keywords = ps_exclusionKeywords.map(k => k.toLowerCase());
                 const pMin = parseFloat(ps_currentFilters.priceMin) || 0; const pMax = parseFloat(ps_currentFilters.priceMax) || Infinity;
                 const sMin = parseInt(ps_currentFilters.salesMin, 10) || 0; const sMax = parseInt(ps_currentFilters.salesMax, 10) || Infinity;
                 const rMin = parseFloat(ps_currentFilters.ratingMin) || 0; const rMax = parseFloat(ps_currentFilters.ratingMax) || Infinity;
                 const hideBad = ps_currentFilters.hideBadReviews; const hideRet = ps_currentFilters.hideReturns; const onlyDisc = ps_currentFilters.onlyDiscount;
                 const datePeriod = ps_currentFilters.date; const dateThreshold = ps_getDateThreshold(datePeriod);
                 const selectedCurrency = ps_currencySelect ? ps_currencySelect.value.toUpperCase() : 'RUR';
                 let visibleCount = 0;
                 const items = ps_resultsDiv.querySelectorAll('.platiSearchItem');
                 items.forEach(itemElement => {
                     const itemId = itemElement.dataset.id; const itemData = ps_currentResults.find(r => r.id === itemId);
                     if (!itemData) { itemElement.classList.add('hidden-by-filter'); return; }
                     let shouldHide = false;
                     if (!shouldHide && keywords.length > 0) { const title = (itemData.name || '').toLowerCase(); const seller = (itemData.seller_name || '').toLowerCase(); if (keywords.some(keyword => (title + ' ' + seller).includes(keyword))) { shouldHide = true; } }
                     if (!shouldHide) { const price = getPriceInSelectedCurrency(itemData, selectedCurrency); if (price < pMin || price > pMax) { shouldHide = true; } }
                     if (!shouldHide) { const sales = formatSales(itemData.cnt_sell); if (sales < sMin || sales > sMax) { shouldHide = true; } }
                     if (!shouldHide) { const rating = parseSellerRating(itemData.seller_rating); if ((rating === 0 && (rMin > 0 || rMax < Infinity)) || rating < rMin || rating > rMax) { shouldHide = true; } }
                     if (!shouldHide && hideBad) { if (parseInt(itemData.cnt_bad_responses || '0', 10) > 0) { shouldHide = true; } }
                     if (!shouldHide && hideRet) { if (parseInt(itemData.cnt_return || '0', 10) > 0) { shouldHide = true; } }
                     if (!shouldHide && onlyDisc) { if (parseInt(itemData.discount || '0', 10) <= 0) { shouldHide = true; } }
                     if (!shouldHide && dateThreshold > 0) { const itemDate = parseDate(itemData.date_create); if (!itemDate || itemDate < dateThreshold) { shouldHide = true; } }
                     if (shouldHide) { itemElement.classList.add('hidden-by-filter'); } else { itemElement.classList.remove('hidden-by-filter'); visibleCount++; }
                 });
                 const totalLoadedCount = ps_currentResults.length;
                 const anyFilterActive = pMin > 0 || pMax < Infinity || sMin > 0 || sMax < Infinity || rMin > 0 || rMax < Infinity || hideBad || hideRet || onlyDisc || datePeriod !== 'all' || keywords.length > 0;
                 if (totalLoadedCount > 0) {
                     if (anyFilterActive) { ps_updateStatus(`Показано ${visibleCount} из ${totalLoadedCount} товаров (фильтры/исключения применены).`); }
                     else { ps_updateStatus(`Загружено ${totalLoadedCount} товаров.`); }
                 } else if (ps_searchInput && ps_searchInput.value.trim()){ /* Статус уже должен быть установлен */ }
                 else { ps_updateStatus(`Введите запрос для поиска.`); }
                 if (visibleCount === 0 && totalLoadedCount > 0 && anyFilterActive) { ps_statusDiv.textContent += ' Нет товаров, соответствующих критериям.'; ps_statusDiv.style.display = 'block'; }
                 else if (totalLoadedCount === 0 && ps_searchInput && ps_searchInput.value.trim()) { ps_statusDiv.style.display = 'block'; }
            }

            // --- Фильтрация Исключений ---
            function ps_addFilterKeyword() {
                const keyword = ps_excludeInput.value.trim().toLowerCase();
                if (keyword && !ps_exclusionKeywords.includes(keyword)) {
                    ps_exclusionKeywords.push(keyword); GM_setValue(PS_EXCLUSION_STORAGE_KEY, ps_exclusionKeywords);
                    ps_excludeInput.value = ''; ps_renderExclusionTags(); ps_applyFilters();
                }
            }
            function ps_removeFilterKeyword(keywordToRemove) {
                ps_exclusionKeywords = ps_exclusionKeywords.filter(k => k !== keywordToRemove);
                GM_setValue(PS_EXCLUSION_STORAGE_KEY, ps_exclusionKeywords); ps_renderExclusionTags(); ps_applyFilters();
            }
            function ps_renderExclusionTags() {
                if (!ps_exclusionTagsListDiv) return;
                ps_exclusionTagsListDiv.innerHTML = '';
                ps_exclusionKeywords.forEach(keyword => {
                    const tag = document.createElement('span'); tag.className = 'exclusionTag';
                    tag.textContent = keyword; tag.title = `Удалить "${keyword}"`;
                    tag.onclick = () => ps_removeFilterKeyword(keyword); ps_exclusionTagsListDiv.appendChild(tag);
                });
            }

            // --- Рендеринг Результатов ---
             function ps_renderResults() {
                 if (!ps_resultsDiv) return;
                 ps_resultsDiv.innerHTML = '';
                 if (ps_currentResults.length === 0) { ps_applyFilters(); return; }

                 const fragment = document.createDocumentFragment();
                 const now = Date.now();
                 const thresholdTime = now - NEW_ITEM_THRESHOLD_DAYS * 24 * 60 * 60 * 1000;
                 const selectedCurrency = ps_currencySelect ? ps_currencySelect.value.toUpperCase() : 'RUR';

                 ps_currentResults.forEach(item => {
                     const itemDiv = document.createElement('div'); itemDiv.className = 'platiSearchItem'; itemDiv.dataset.id = item.id;
                     const link = document.createElement('a'); link.href = item.url || `https://plati.market/itm/${item.id}`; link.target = '_blank'; link.rel = 'noopener noreferrer nofollow';
                     const imageWrapper = document.createElement('div'); imageWrapper.className = 'card-image-wrapper';
                     const img = document.createElement('img'); const imgSrc = `https://${PS_IMAGE_DOMAIN}/imgwebp.ashx?id_d=${item.id}&w=164&h=164&dc=${item.ticks_last_change || Date.now()}`;
                     img.src = imgSrc; img.alt = item.name || 'Изображение товара'; img.loading = 'lazy';
                     img.onerror = function() { this.onerror = null; this.src = 'https://plati.market/images/logo-plati.png'; this.style.objectFit = 'contain'; };
                     imageWrapper.appendChild(img);
                      const itemDate = parseDate(item.date_create);
                      if (itemDate && itemDate > thresholdTime) { const newBadge = document.createElement('span'); newBadge.className = 'newItemBadge'; newBadge.textContent = 'New'; imageWrapper.appendChild(newBadge); }
                      link.appendChild(imageWrapper);
                     const priceDiv = document.createElement('div'); priceDiv.className = 'price'; let displayPrice = getPriceInSelectedCurrency(item, selectedCurrency); let currencySymbol;
                     switch (selectedCurrency) { case 'USD': currencySymbol = '$'; break; case 'EUR': currencySymbol = '€'; break; case 'UAH': currencySymbol = '₴'; break; default: currencySymbol = '₽'; break; }
                      priceDiv.textContent = displayPrice !== Infinity ? `${displayPrice.toLocaleString('ru-RU', {minimumFractionDigits: 0, maximumFractionDigits: 2})} ${currencySymbol}` : 'Нет цены'; priceDiv.title = `Цена в ${selectedCurrency}`; link.appendChild(priceDiv);
                     const titleDiv = document.createElement('div'); titleDiv.className = 'title'; titleDiv.textContent = item.name || 'Без названия'; titleDiv.title = item.name || 'Без названия'; link.appendChild(titleDiv);
                     const infoContainer = document.createElement('div'); infoContainer.className = 'cardInfoContainer';
                     const infoRow1 = document.createElement('div'); infoRow1.className = 'cardInfoRow1'; const infoRow2 = document.createElement('div'); infoRow2.className = 'cardInfoRow2';
                     const ratingVal = parseSellerRating(item.seller_rating); const goodRev = parseInt(item.cnt_good_responses || '0'); const badRev = parseInt(item.cnt_bad_responses || '0'); const returns = parseInt(item.cnt_return || '0'); let salesCount = formatSales(item.cnt_sell);
                      infoRow1.innerHTML = `<span title="Рейтинг продавца">Рейт: ${ratingVal > 0 ? ratingVal.toLocaleString('ru-RU', {maximumFractionDigits: 0}) : 'N/A'}</span><span title="Отзывы (Хорошие/Плохие)">Отз: <span class="reviewsGood">${goodRev}</span>${badRev > 0 ? '/<span class="reviewsBad">' + badRev + '</span>' : ''}</span><span title="Возвраты">Возв: ${returns}</span>`;
                      infoRow2.innerHTML = `<span class="sales" title="Продажи">Прод: ${salesCount > 0 ? salesCount.toLocaleString('ru-RU') : '0'}</span><span class="dateAdded" title="Дата добавления">Доб: ${formatDateString(itemDate)}</span>`;
                      infoContainer.appendChild(infoRow1); infoContainer.appendChild(infoRow2);
                     const sellerLink = document.createElement('a'); sellerLink.className = 'sellerLink'; sellerLink.textContent = `Продавец: ${item.seller_name || 'N/A'}`; sellerLink.title = `Перейти к продавцу: ${item.seller_name || 'N/A'}`;
                     if (item.seller_id && item.seller_name) { const safeSellerName = encodeURIComponent(item.seller_name.replace(/[^a-zA-Z0-9_\-.~]/g, '-')).replace(/%2F/g, '/'); sellerLink.href = `https://plati.market/seller/${safeSellerName}/${item.seller_id}/`; sellerLink.target = '_blank'; sellerLink.rel = 'noopener noreferrer nofollow'; sellerLink.onclick = (e) => { e.stopPropagation(); }; }
                     else { sellerLink.style.pointerEvents = 'none'; }
                     infoContainer.appendChild(sellerLink); link.appendChild(infoContainer);
                     const buyButtonDiv = document.createElement('div'); buyButtonDiv.className = 'buyButton'; buyButtonDiv.textContent = 'Перейти'; link.appendChild(buyButtonDiv);
                     itemDiv.appendChild(link); fragment.appendChild(itemDiv);
                 });
                 ps_resultsDiv.appendChild(fragment);
                 ps_applyFilters();
             }

            // --- Обработчики UI ---
            function ps_handleCurrencyChange() {
                 ps_currentCurrency = ps_currencySelect.value.toUpperCase(); GM_setValue(PS_CURRENCY_STORAGE_KEY, ps_currentCurrency);
                 applyLoadedFiltersToUI(); ps_updateFilterPlaceholders();
                 if (ps_currentSort.field === 'price') { ps_applySort(ps_currentSort.field, ps_currentSort.direction); }
                 ps_renderResults();
            }

            // --- Добавление кнопки Plati ---
            function addPlatiButton() {
                const actionsContainer = document.querySelector('#queueActionsCtn');
                const ignoreButtonContainer = actionsContainer?.querySelector('#ignoreBtn');
                if (!actionsContainer || !ignoreButtonContainer || actionsContainer.querySelector('.plati_price_button')) return;

                const platiContainer = document.createElement('div');
                platiContainer.className = 'plati_price_button queue_control_button';
                platiContainer.style.marginLeft = '3px';
                platiContainer.innerHTML = `<div class="btnv6_blue_hoverfade btn_medium" style="height: 32px;" title="Найти на Plati.Market"><span>Plati</span></div>`;
                platiContainer.querySelector('div').addEventListener('click', (e) => { e.preventDefault(); e.stopPropagation(); showPlatiModal(); });
                ignoreButtonContainer.insertAdjacentElement('afterend', platiContainer);
            }

            // --- Стили ---
             function addPlatiStyles() {
                 GM_addStyle(`
                    /* Стили Спиннера */
                    @keyframes platiSpin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
                    .spinner { border: 3px solid rgba(255, 255, 255, 0.3); border-radius: 50%; border-top-color: #fff; width: 1em; height: 1em; animation: platiSpin 1s linear infinite; display: inline-block; vertical-align: middle; margin-left: 5px; }
                    .platiSearchBtn .spinner { width: 0.8em; height: 0.8em; border-width: 2px; }
                    /* Стили Модального окна */
                    #platiSearchModal { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(20, 20, 25, 0.98); z-index: 9999; display: none; color: #eee; font-family: "Motiva Sans", Sans-serif, Arial; overflow-y: auto; scrollbar-color: #67c1f5 #17202d; scrollbar-width: thin; }
                    #platiSearchModal::-webkit-scrollbar { width: 8px; } #platiSearchModal::-webkit-scrollbar-track { background: #17202d; border-radius: 4px; } #platiSearchModal::-webkit-scrollbar-thumb { background-color: #4b6f9c; border-radius: 4px; border: 2px solid #17202d; } #platiSearchModal::-webkit-scrollbar-thumb:hover { background-color: #67c1f5; }
                    #platiSearchModal * { box-sizing: border-box; }
                    #platiSearchContainer { max-width: 1350px; margin: 0 auto; padding: 15px ${PS_SIDE_PANEL_HORIZONTAL_PADDING}px; position: relative; min-height: 100%; }
                    #platiSearchCloseBtn { position: fixed; top: 15px; right: 20px; font-size: 35px; color: #aaa; background: none; border: none; cursor: pointer; line-height: 1; z-index: 10002; padding: 5px; transition: color 0.2s, transform 0.2s; } #platiSearchCloseBtn:hover { color: #fff; transform: scale(1.1); }
                    /* Шапка */
                    #platiSearchHeader { display: flex; align-items: center; gap: 10px; margin-bottom: 15px; flex-wrap: wrap; position: relative; z-index: 5; border-bottom: 1px solid #444; padding-bottom: 15px; padding-left: ${PS_CONTENT_PADDING_LEFT}px; padding-right: ${PS_CONTENT_PADDING_RIGHT}px; margin-left: -${PS_CONTENT_PADDING_LEFT}px; margin-right: -${PS_CONTENT_PADDING_RIGHT}px; flex-shrink: 0; }
                    .platiSearchInputContainer { position: relative; flex-grow: 0.7; min-width: 200px; flex-basis: 350px; }
                    #platiSearchInput { width: 100%; padding: 10px 15px; font-size: 16px; background-color: #333; border: 1px solid #555; color: #eee; border-radius: 4px; height: 40px; outline: none; } #platiSearchInput:focus { border-color: #67c1f5; }
                    #platiSearchSuggestions { position: absolute; top: 100%; left: 0; right: 0; background-color: #3a3a40; border: 1px solid #555; border-top: none; border-radius: 0 0 4px 4px; max-height: 300px; overflow-y: auto; z-index: 10000; display: none; }
                    .suggestionItem { padding: 8px 15px; cursor: pointer; color: #eee; font-size: 14px; border-bottom: 1px solid #4a4a50; } .suggestionItem:last-child { border-bottom: none; } .suggestionItem:hover { background-color: #4a4a55; }
                    /* Кнопки в шапке */
                    .platiSearchBtn { padding: 10px 15px; font-size: 14px; color: white; border: none; border-radius: 4px; cursor: pointer; white-space: nowrap; height: 40px; display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; background-color: #555; transition: background-color 0.2s; } .platiSearchBtn:hover:not(:disabled) { background-color: #666; } .platiSearchBtn:disabled { opacity: 0.6; cursor: default; }
                    #platiSearchGoBtn { background-color: #4D88FF; } #platiSearchGoBtn:hover { background-color: #3366CC; }
                    .platiSearchBtn.sortBtn.active { background-color: #007bff; } .platiSearchBtn.sortBtn.active:hover { background-color: #0056b3; }
                    #platiResetSortBtn { background-color: #777; margin-right: 5px; padding: 0 10px; } #platiResetSortBtn:hover { background-color: #888; } #platiResetSortBtn svg { width: 16px; height: 16px; fill: currentColor; } #platiResetSortBtn.active { background-color: #007bff; }
                    #platiSearchAdvSortBtnContainer { position: relative; flex-shrink: 0; width: ${PS_ADV_SORT_CONTAINER_WIDTH}px; display: flex; justify-content: center; }
                    #platiSearchAdvSortBtn { width: 100%; justify-content: center; overflow: hidden; text-overflow: ellipsis; }
                    #platiSearchCurrencySelect { margin-left: 10px; background-color: #333; color: #eee; border: 1px solid #555; border-radius: 4px; height: 40px; padding: 0 8px; font-size: 14px; cursor: pointer; flex-shrink: 0; outline: none; } #platiSearchCurrencySelect:focus { border-color: #67c1f5; }
                    /* Меню доп сортировки */
                    #platiSearchAdvSortMenu { display: none; position: absolute; top: 100%; left: 0; background-color: #3a3a40; border: 1px solid #555; border-radius: 4px; min-width: 100%; z-index: 10001; padding: 5px 0; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3); }
                    #platiSearchAdvSortBtnContainer:hover #platiSearchAdvSortMenu { display: block; }
                    .platiSearchSortMenuItem { display: block; padding: 8px 15px; color: #eee; font-size: 14px; cursor: pointer; white-space: nowrap; transition: background-color 0.1s; } .platiSearchSortMenuItem:hover { background-color: #4a4a55; } .platiSearchSortMenuItem.active { background-color: #007bff; color: white; } .platiSearchSortMenuItem .sortArrow { display: inline-block; margin-left: 5px; font-size: 12px; }
                    /* Боковые панели */
                    #platiSearchFiltersPanel, #platiSearchExclusionTags { position: fixed; top: ${PS_TOP_OFFSET_FOR_SIDE_PANELS}px; max-height: calc(100vh - ${PS_TOP_OFFSET_FOR_SIDE_PANELS}px - ${PS_BOTTOM_OFFSET_FOR_SIDE_PANELS}px); overflow-y: auto; z-index: 1000; padding: 10px; padding-right: 15px; scrollbar-width: thin; scrollbar-color: #555 #2a2a30; background-color: transparent; transition: top 0.2s ease-in-out; }
                    #platiSearchFiltersPanel::-webkit-scrollbar, #platiSearchExclusionTags::-webkit-scrollbar { width: 5px; } #platiSearchFiltersPanel::-webkit-scrollbar-track, #platiSearchExclusionTags::-webkit-scrollbar-track { background: rgba(42, 42, 48, 0.5); border-radius: 3px; } #platiSearchFiltersPanel::-webkit-scrollbar-thumb, #platiSearchExclusionTags::-webkit-scrollbar-thumb { background-color: rgba(85, 85, 85, 0.7); border-radius: 3px; }
                    #platiSearchFiltersPanel { left: ${PS_SIDE_PANEL_HORIZONTAL_PADDING}px; width: ${PS_FILTER_PANEL_WIDTH}px; }
                    #platiSearchExclusionTags { right: ${PS_SIDE_PANEL_HORIZONTAL_PADDING}px; width: ${PS_EXCLUSION_PANEL_WIDTH}px; display: flex; flex-direction: column; gap: 10px; }
                    /* Фильтры */
                    .filterGroup { margin-bottom: 18px; } .filterGroup h4 { font-size: 15px; color: #ddd; margin-bottom: 8px; padding-bottom: 4px; display: flex; justify-content: space-between; align-items: center; text-shadow: 1px 1px 2px rgba(0,0,0,0.7); font-weight: 500; }
                    .filterResetBtn { font-size: 12px; color: #aaa; background: none; border: none; cursor: pointer; padding: 0 3px; line-height: 1; } .filterResetBtn:hover { color: #fff; } .filterResetBtn svg { width: 14px; height: 14px; vertical-align: middle; fill: currentColor; }
                    .filterRangeInputs { display: flex; gap: 8px; align-items: center; } .filterRangeInputs input[type="number"] { width: calc(50% - 4px); padding: 6px 8px; font-size: 13px; background-color: rgba(51,51,51,0.85); border: 1px solid #666; color: #eee; border-radius: 3px; height: 30px; text-align: center; -moz-appearance: textfield; box-shadow: inset 0 1px 3px rgba(0,0,0,0.3); outline: none; } .filterRangeInputs input[type="number"]:focus { border-color: #67c1f5; } .filterRangeInputs input[type="number"]::-webkit-outer-spin-button, .filterRangeInputs input[type="number"]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; } .filterRangeInputs input[type="number"]::placeholder { color: #999; font-size: 11px; text-align: center; }
                    .filterCheckbox { margin-bottom: 8px; } .filterCheckbox label { display: flex; align-items: center; font-size: 14px; cursor: pointer; color: #ccc; text-shadow: 1px 1px 2px rgba(0,0,0,0.7); } .filterCheckbox input[type="checkbox"] { margin-right: 8px; width: 16px; height: 16px; accent-color: #007bff; cursor: pointer; flex-shrink: 0; }
                    .filterSelect select { width: 100%; padding: 6px 8px; font-size: 13px; background-color: rgba(51,51,51,0.85); border: 1px solid #666; color: #eee; border-radius: 3px; height: 30px; box-shadow: inset 0 1px 3px rgba(0,0,0,0.3); outline: none; } .filterSelect select:focus { border-color: #67c1f5; }
                    #psResetAllFiltersBtn { width: 100%; margin-top: 10px; padding: 8px 10px; height: auto; background-color: rgba(108, 117, 125, 0.8); border: 1px solid #888; text-shadow: 1px 1px 1px rgba(0,0,0,0.4); } #psResetAllFiltersBtn:hover { background-color: rgba(90, 98, 104, 0.9); }
                    /* Исключения */
                    .exclusionInputGroup { display: flex; align-items: stretch; border: 1px solid #555; border-radius: 4px; background-color: rgba(51,51,51,0.85); overflow: hidden; height: 34px; flex-shrink: 0; box-shadow: inset 0 1px 3px rgba(0,0,0,0.3); }
                    .exclusionInputGroup #platiSearchExcludeInput { padding: 6px 10px; font-size: 13px; background-color: transparent; border: none; color: #eee; outline: none; border-radius: 0; flex-grow: 1; width: auto; height: auto; } .exclusionInputGroup #platiSearchExcludeInput:focus { box-shadow: none; }
                    .exclusionInputGroup #platiSearchAddExcludeBtn { display: flex; align-items: center; justify-content: center; padding: 0 10px; background-color: #555; border: none; border-left: 1px solid #555; cursor: pointer; border-radius: 0; color: #eee; height: auto; } .exclusionInputGroup #platiSearchAddExcludeBtn:hover { background-color: #666; } .exclusionInputGroup #platiSearchAddExcludeBtn svg { width: 16px; height: 16px; fill: currentColor; }
                    #platiExclusionTagsList { display: flex; flex-direction: row; flex-wrap: wrap; align-content: flex-start; gap: 8px; overflow-y: auto; flex-grow: 1; }
                    .exclusionTag { display: inline-block; background-color: rgba(70,70,80,0.9); color: #ddd; padding: 5px 10px; border-radius: 15px; font-size: 13px; cursor: pointer; transition: background-color 0.2s; border: 1px solid rgba(100,100,110,0.9); white-space: nowrap; text-shadow: 1px 1px 1px rgba(0,0,0,0.5); } .exclusionTag:hover { background-color: rgba(220,53,69,0.9); border-color: rgba(200,40,50,0.95); color: #fff; } .exclusionTag::after { content: ' ×'; font-weight: bold; margin-left: 4px; }
                    /* Результаты */
                    #platiSearchResultsContainer { position: relative; padding-left: ${PS_CONTENT_PADDING_LEFT}px; padding-right: ${PS_CONTENT_PADDING_RIGHT}px; margin-left: -${PS_CONTENT_PADDING_LEFT}px; margin-right: -${PS_CONTENT_PADDING_RIGHT}px; }
                    #platiSearchResultsStatus { width: 100%; text-align: center; font-size: 18px; color: #aaa; padding: 50px 0; display: none; min-height: 100px; display: flex; align-items: center; justify-content: center; flex-direction: column;}
                    #platiSearchResults { display: flex; flex-wrap: wrap; gap: 15px; justify-content: flex-start; padding-top: 10px; }
                    /* Карточка товара */
                    .platiSearchItem { background-color: #2a2a30; border-radius: 8px; padding: 10px; width: calc(20% - 12px); min-width: 170px; display: flex; flex-direction: column; transition: transform 0.2s ease, box-shadow 0.2s ease; box-shadow: 0 2px 5px rgba(0,0,0,0.2); position: relative; color: #ccc; font-size: 13px; min-height: 340px; border: 1px solid transparent; }
                    .platiSearchItem:hover { transform: translateY(-3px); box-shadow: 0 4px 10px rgba(0,0,0,0.4); border-color: #4b6f9c; }
                    .platiSearchItem.hidden-by-filter { display: none !important; }
                    .platiSearchItem a { text-decoration: none; color: inherit; display: flex; flex-direction: column; height: 100%; }
                    .platiSearchItem .card-image-wrapper { position: relative; width: 100%; aspect-ratio: 1 / 1; margin-bottom: 8px; background-color: #444; border-radius: 6px; overflow: hidden; }
                    .platiSearchItem img { position: absolute; top: 0; left: 0; width: 100%; height: 100%; object-fit: cover; border-radius: 6px; }
                    .newItemBadge { position: absolute; top: 4px; left: 4px; background-color: #f54848; color: white; padding: 1px 5px; font-size: 10px; border-radius: 3px; font-weight: bold; z-index: 1; text-shadow: 1px 1px 1px rgba(0,0,0,0.3); }
                    .platiSearchItem .price { font-size: 16px; font-weight: 700; color: #a4d007; margin-bottom: 5px; }
                    .platiSearchItem .title { font-size: 13px; font-weight: 500; line-height: 1.3; height: 3.9em; overflow: hidden; text-overflow: ellipsis; margin-bottom: 6px; color: #eee; display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; }
                    .cardInfoContainer { margin-top: auto; padding-top: 6px; }
                    .cardInfoRow1, .cardInfoRow2 { display: flex; justify-content: space-between; flex-wrap: nowrap; gap: 8px; font-size: 12px; color: #bbb; margin-bottom: 4px; }
                    .cardInfoRow1 span, .cardInfoRow2 span { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; flex-shrink: 1; }
                    .cardInfoRow1 span:first-child, .cardInfoRow2 span:first-child { flex-shrink: 0; margin-right: auto; }
                    .reviewsGood { color: #6cff5c; font-weight: bold; } .reviewsBad { color: #f54848; margin-left: 2px; font-weight: bold;} .sales { font-weight: bold; color: #eee; }
                    .sellerLink { display: block; font-size: 12px; color: #bbb; text-decoration: none; margin-bottom: 4px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; transition: color 0.2s; } .sellerLink:hover { color: #ddd; text-decoration: underline; }
                    .platiSearchItem .buyButton { display: block; text-align: center; padding: 8px; margin-top: 8px; background-color: #007bff; color: white; border-radius: 4px; font-size: 13px; font-weight: 600; transition: background-color 0.2s; } .platiSearchItem .buyButton:hover { background-color: #0056b3; }
                    /* Адаптивность */
                    @media (max-width: 1650px) { .platiSearchItem { width: calc(20% - 12px); } }
                    @media (max-width: 1400px) { .platiSearchItem { width: calc(25% - 12px); } }
                    @media (max-width: 1100px) { .platiSearchItem { width: calc(33.33% - 10px); } }
                    @media (max-width: 850px) { #platiSearchFiltersPanel, #platiSearchExclusionTags { display: none; } #platiSearchHeader, #platiSearchResultsContainer { padding-left: 15px; padding-right: 15px; margin-left: 0; margin-right: 0; } .platiSearchItem { width: calc(50% - 8px); } #platiSearchHeader { justify-content: center; } }
                    @media (max-width: 600px) { .platiSearchItem { width: 100%; min-height: auto; } #platiSearchHeader { gap: 5px; } .platiSearchInputContainer { flex-basis: 100%; order: -1; } .platiSearchBtn, #platiSearchCurrencySelect, #platiSearchAdvSortBtnContainer { width: calc(33.3% - 4px); font-size: 13px; padding: 8px 5px; height: 36px; } #platiSearchAdvSortBtnContainer { width: calc(33.3% - 4px); } #platiSearchAdvSortBtn { width: 100%; } #platiSearchAdvSortMenu { min-width: 200px; left: 50%; transform: translateX(-50%); } #platiResetSortBtn { width: auto; padding: 0 8px; } }
                    /* Стили для кнопки Plati на странице Steam */
                    .plati_price_button .btnv6_blue_hoverfade { margin: 0; padding: 0 15px; font-size: 15px; display: flex; align-items: center; transition: filter 0.2s; }
                    .plati_price_button .btnv6_blue_hoverfade:hover { filter: brightness(1.1); }
                 `);
             }

            // --- Инициализация модуля ---
            addPlatiStyles();
            const steamAppIdCheck = window.location.pathname.match(/\/app\/(\d+)/);
            if (steamAppIdCheck && steamAppIdCheck[1]) {
                addPlatiButton();
            }

        })();
    }

    // Скрипт для страницы игры (VGT; отображения цен из агрегатора VGTimes) | https://store.steampowered.com/app/*
    if (scriptsConfig.vgtSales && window.location.pathname.includes('/app/')) {
        (function() {
            'use strict';

            const VGT_DATA_URL = 'https://gist.githubusercontent.com/0wn3dg0d/2644d328cca76b74c57804c7303b8606/raw/vgtstulex.json';
            const VGT_API_URL = 'https://vgtimes.ru/engine/modules/games/shops_table.php';
            const ITEMS_PER_PAGE = 40;
            let vgtDataMap = new Map();
            let vgtSteamMap = new Map();
            let vgtNameMap = new Map();

            function addVGTButton() {
                const actionsContainer = document.querySelector('#queueActionsCtn');
                // Находим элемент кнопки "Скрыть" (Ignore) по его ID
                const ignoreButtonContainer = actionsContainer?.querySelector('#ignoreBtn');

                // Проверяем, что оба элемента найдены
                if (!actionsContainer || !ignoreButtonContainer) {
                    console.warn('VGT Button: Could not find actions container or ignore button container.');
                    return; // Выходим, если не нашли нужные элементы
                }

                const vgtContainer = document.createElement('div');
                // Добавляем класс для стилизации и идентификации
                vgtContainer.className = 'vgt_price_button queue_control_button'; // Добавляем queue_control_button для выравнивания
                // Убираем правый отступ и добавляем левый отступ в 3px
                vgtContainer.style.marginLeft = '3px';
                // vgtContainer.style.marginRight = '4px'; // Убираем этот стиль

                vgtContainer.innerHTML = `
                    <div class="btnv6_blue_hoverfade btn_medium" style="height: 32px;"> <span>Цены (VGT)</span>
                    </div>
                `;

                vgtContainer.querySelector('div').addEventListener('click', (e) => {
                    e.preventDefault();
                    e.stopPropagation();
                    openVGTModal();
                });

                // Вставляем кнопку VGT *после* контейнера кнопки "Скрыть"
                ignoreButtonContainer.insertAdjacentElement('afterend', vgtContainer);
            }

            async function openVGTModal() {
                const vgtModal = createModal();
                document.body.appendChild(vgtModal);
                showLoading(vgtModal);
                setupModalEvents(vgtModal);

                try {
                    await loadVGTData();
                    const appId = getAppId();
                    const gameData = await findGameData(appId);

                    if (!gameData) {
                        showError(vgtModal, 'Игра не найдена в базе VGTimes');
                        return;
                    }

                    initModalContent(vgtModal, gameData);
                    await loadPrices(gameData['data-id'], vgtModal, 0);
                } catch (error) {
                    showError(vgtModal, 'Ошибка загрузки данных');
                    console.error('VGT Error:', error);
                }
            }

            async function loadVGTData() {
                const data = await fetchJson(VGT_DATA_URL);
                vgtDataMap = new Map(Object.entries(data));

                vgtSteamMap = new Map();
                vgtNameMap = new Map();
                for (const [key, value] of Object.entries(data)) {
                    if (value.steam !== null) {
                        vgtSteamMap.set(String(value.steam), value);
                    }
                    const normalized = normalizeName(value.title);
                    vgtNameMap.set(normalized, value);
                }
            }

            async function findGameData(appId) {
                if (vgtSteamMap.has(appId)) {
                    return vgtSteamMap.get(appId);
                }

                const gameName = getGameName();
                const normalized = normalizeName(gameName);

                if (vgtNameMap.has(normalized)) return vgtNameMap.get(normalized);

                const possibleMatches = findPossibleMatches(gameName, Array.from(vgtDataMap.values()));
                if (possibleMatches.length === 0) return null;

                const selectedGame = await showGameSelection(possibleMatches);
                return selectedGame;
            }

            function normalizeName(name) {
                return name
                    .normalize("NFD").replace(/[\u0300-\u036f]/g, "")
                    .replace(/[’]/g, "'")
                    .replace(/[^a-zA-Zа-яёА-ЯЁ0-9 _'\-!]/g, '')
                    .trim()
                    .toLowerCase();
            }

            function findPossibleMatches(gameName, games) {
                const cleanGameName = normalizeName(gameName);

                return games
                    .map(game => {
                        const cleanTitle = normalizeName(game.title);
                        const similarity = calculateSimilarity(cleanGameName, cleanTitle);
                        return {
                            ...game,
                            similarity
                        };
                    })
                    .filter(game => game.similarity > 50)
                    .sort((a, b) => b.similarity - a.similarity)
                    .slice(0, 5);
            }

            function calculateSimilarity(a, b) {
                const maxLen = Math.max(a.length, b.length);
                if (maxLen === 0) return 0;
                const distance = levenshteinDistance(a, b);
                return Math.round((1 - distance / maxLen) * 100);
            }

            function levenshteinDistance(a, b) {
                const matrix = Array.from({
                        length: a.length + 1
                    }, (_, i) =>
                    Array.from({
                            length: b.length + 1
                        }, (_, j) =>
                        i === 0 ? j : j === 0 ? i : 0));

                for (let i = 1; i <= a.length; i++) {
                    for (let j = 1; j <= b.length; j++) {
                        const cost = a[i - 1] === b[j - 1] ? 0 : 1;
                        matrix[i][j] = Math.min(
                            matrix[i - 1][j] + 1,
                            matrix[i][j - 1] + 1,
                            matrix[i - 1][j - 1] + cost
                        );
                    }
                }
                return matrix[a.length][b.length];
            }

            async function showGameSelection(games) {
                return new Promise(resolve => {
                    const modal = document.querySelector('.vgt_modal');
                    const content = modal.querySelector('.vgt_content');

                    content.innerHTML = `
            <div class="vgt_selection">
                <h3>Выберите игру:</h3>
                <div class="vgt_games_list">
                    ${games.map(game => `
                        <div class="vgt_game_item" data-id="${game['data-id']}">
                            <div class="vgt_game_title">${game.title}</div>
                            <div class="vgt_game_similar">Совпадение: ${game.similarity}%</div>
                        </div>
                    `).join('')}
                </div>
                <div class="vgt_selection_buttons">
                    <button class="vgt_cancel_btn">Ничего не подходит</button>
                </div>
            </div>
        `;

                    modal.querySelectorAll('.vgt_game_item').forEach(item => {
                        item.addEventListener('click', () => {
                            const selectedId = item.dataset.id;
                            const selectedGame = vgtDataMap.get(selectedId);
                            content.innerHTML = '';
                            resolve(selectedGame);
                        });
                    });

                    modal.querySelector('.vgt_cancel_btn').addEventListener('click', () => {
                        content.innerHTML = '';
                        resolve(null);
                    });
                });
            }

            function createModal() {
                const modal = document.createElement('div');
                modal.className = 'vgt_modal';
                modal.innerHTML = `
                <div class="vgt_modal-overlay"></div>
                <div class="vgt_modal-content">
                    <span class="vgt_close">&times;</span>
                    <div class="vgt_header"></div>
                    <div class="vgt_content"></div>
                </div>
            `;
                return modal;
            }

            function initModalContent(modal, gameData) {
                const header = modal.querySelector('.vgt_header');
                header.innerHTML = `<h2><a href="${gameData.url}" target="_blank">${gameData.title}</a></h2>`;
            }

            function setupModalEvents(modal) {
                modal.querySelector('.vgt_modal-overlay').addEventListener('click', () => modal.remove());

                const closeBtn = modal.querySelector('.vgt_close');
                closeBtn.addEventListener('mouseenter', () => closeBtn.style.color = '#67c1f5');
                closeBtn.addEventListener('mouseleave', () => closeBtn.style.color = '#aaa');
                closeBtn.onclick = () => modal.remove();
            }


            async function loadPrices(dataId, modal, skip) {
                const content = modal.querySelector('.vgt_content');
                content.innerHTML = '<div class="vgt_loading">Загрузка цен...</div>';

                try {
                    const params = new URLSearchParams({
                        skin: 'vgtimes',
                        id: dataId,
                        skip: skip,
                        sort: 'rele',
                        shop: 'all',
                        payment_method: 'all',
                        platform: 'all',
                        custom_filter: ''
                    });

                    const response = await postRequest(VGT_API_URL, params.toString());
                    const data = JSON.parse(response.responseText);

                    if (data.offercount === 0 || data.result.includes('notf gp_lb')) {
                        content.innerHTML = '<div class="vgt_error">Информация о ценах отсутствует в базе VGTimes.</div>';
                        return;
                    }

                    const tempDiv = document.createElement('div');
                    tempDiv.innerHTML = data.result;

                    if (tempDiv.querySelector('.notf.gp_lb')) {
                        content.innerHTML = '<div class="vgt_error">Информация о ценах отсутствует в базе VGTimes.</div>';
                        return;
                    }

                    tempDiv.querySelectorAll('.int, .s_rating, .s_reviews, .boosted, .s_promocodes, .promocode').forEach(el => el.remove());

                    processElements(tempDiv);

                    const shops = groupByShops(tempDiv);
                    const sortedShops = sortShops(shops);

                    content.innerHTML = generateShopColumns(sortedShops);

                    addExpandHandlers(content);

                    if (data.offercount > skip + ITEMS_PER_PAGE) {
                        const loadMoreBtn = document.createElement('button');
                        loadMoreBtn.className = 'vgt_load_more';
                        loadMoreBtn.textContent = 'Загрузить ещё';
                        loadMoreBtn.onclick = () => loadPrices(dataId, modal, skip + ITEMS_PER_PAGE);
                        content.appendChild(loadMoreBtn);
                    }

                } catch (error) {
                    content.innerHTML = '<div class="vgt_error">Ошибка загрузки цен</div>';
                }
            }

            function processElements(container) {
                container.querySelectorAll('img').forEach(img => img.remove());

                container.querySelectorAll('a').forEach(link => {
                    let href = link.getAttribute('href');

                    try {
                        if (href.startsWith('/')) {
                            href = 'https://vgtimes.ru' + href;
                        }

                        if (href.includes('/shop_redirect')) {
                            const urlObj = new URL(href);
                            const realUrl = urlObj.searchParams.get('url');
                            if (realUrl) {
                                href = decodeURIComponent(realUrl)
                                    .replace(/(https?:\/\/)?store\.steampowered\.com\/?/i, '')
                                    .replace(/^\/+/g, '');

                                const cleanUrl = href.split('?')[0];
                                href = cleanUrl.startsWith('http') ? cleanUrl : `https://${cleanUrl}`;
                            }
                        }

                        if (link.classList.contains('shopm')) {
                            const shopPath = new URL(href).pathname;
                            href = `https://vgtimes.ru${shopPath}`;
                        }

                        link.href = href;

                    } catch (e) {
                        console.error('URL processing error:', e);
                        link.href = '#';
                    }

                    link.removeAttribute('style');
                });
            }

            function groupByShops(container) {
                const shopsMap = new Map();

                container.querySelectorAll('.products_search_par').forEach(item => {
                    const shopElement = item.querySelector('.shopm');
                    if (!shopElement) return;

                    const shopName = shopElement.textContent.trim();
                    if (!shopsMap.has(shopName)) {
                        shopsMap.set(shopName, {
                            name: shopName,
                            items: [],
                            minPrice: Infinity,
                            url: shopElement.href
                        });
                    }

                    const priceElement = item.querySelector('.aprice');
                    let price = parsePrice(priceElement.textContent);

                    const shopData = shopsMap.get(shopName);
                    shopData.items.push(item);
                    if (price < shopData.minPrice) shopData.minPrice = price;
                });

                return Array.from(shopsMap.values());
            }

            function parsePrice(priceText) {
                if (priceText.toLowerCase() === 'бесплатно') return 0;
                const number = priceText.replace(/[^0-9,]/g, '').replace(',', '.');
                return parseFloat(number) || Infinity;
            }

            function sortShops(shops) {
                return shops.sort((a, b) => a.minPrice - b.minPrice);
            }

            function generateShopColumns(shops) {
                return `
                <div class="vgt_columns">
                    ${shops.map(shop => `
                        <div class="vgt_shop_column">
                            <div class="vgt_shop_header">
                                <a href="${shop.url}" target="_blank">${shop.name}</a>
                            </div>
                            <div class="vgt_shop_items">
                                ${shop.items.slice(0, 3).map(item => generateItemHtml(item)).join('')}
                            </div>
                            ${shop.items.length > 3 ? `
                                <div class="vgt_shop_expand" data-shop="${shop.name}" data-expanded="false">
                                    <div class="vgt_expand_content" style="display: none;">
                                        ${shop.items.slice(3).map(item => generateItemHtml(item)).join('')}
                                    </div>
                                    <div class="vgt_expand_toggle">...</div>
                                </div>
                            ` : ''}
                        </div>
                    `).join('')}
                </div>
            `;
            }

            function generateItemHtml(item) {
                const title = item.querySelector('.title').textContent.trim();
                const priceElement = item.querySelector('.aprice');
                const oldPriceElement = item.querySelector('.oldprice');
                const discountElement = item.querySelector('.percent');
                const link = item.querySelector('a.f_click').href;

                return `
                <div class="vgt_shop_item">
                    <a href="${link}" target="_blank" class="vgt_item_link">
                        <div class="vgt_item_title">${title}</div>
                        <div class="vgt_prices">
                            ${oldPriceElement ? `<span class="vgt_oldprice">${oldPriceElement.textContent}</span>` : ''}
                            <span class="vgt_aprice">${priceElement.textContent}</span>
                            ${discountElement ? `<span class="vgt_percent">${discountElement.textContent}</span>` : ''}
                        </div>
                    </a>
                </div>
            `;
            }

            function addExpandHandlers(content) {
                content.querySelectorAll('.vgt_shop_expand').forEach(expand => {
                    expand.querySelector('.vgt_expand_toggle').addEventListener('click', () => {
                        const isExpanded = expand.dataset.expanded === 'true';
                        expand.dataset.expanded = !isExpanded;
                        expand.querySelector('.vgt_expand_content').style.display = isExpanded ? 'none' : 'block';
                        expand.querySelector('.vgt_expand_toggle').textContent = isExpanded ? '...' : '▲';
                    });
                });
            }

            function getAppId() {
                return window.location.pathname.split('/')[2];
            }

            function getGameName() {
                return document.querySelector('.apphub_AppName')?.textContent || '';
            }

            function fetchJson(url) {
                return new Promise((resolve, reject) => {
                    GM_xmlhttpRequest({
                        method: 'GET',
                        url: url,
                        onload: (r) => resolve(JSON.parse(r.responseText)),
                        onerror: reject
                    });
                });
            }

            function postRequest(url, body) {
                return new Promise((resolve, reject) => {
                    GM_xmlhttpRequest({
                        method: 'POST',
                        url: url,
                        headers: {
                            'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
                            'X-Requested-With': 'XMLHttpRequest'
                        },
                        data: body,
                        onload: resolve,
                        onerror: reject
                    });
                });
            }

            function showLoading(modal) {
                modal.querySelector('.vgt_content').innerHTML = '<div class="vgt_loading">Загрузка...</div>';
            }

            function showError(modal, message) {
                modal.querySelector('.vgt_content').innerHTML = `<div class="vgt_error">${message}</div>`;
            }

            GM_addStyle(`
            .vgt_modal {
                display: block;
                position: fixed;
                z-index: 10000;
                left: 0;
                top: 0;
                width: 100%;
                height: 100%;
            }

            .vgt_modal-overlay {
                position: fixed;
                top: 0;
                left: 0;
                width: 100%;
                height: 100%;
                background: rgba(0,0,0,0.8);
            }

            .vgt_modal-content {
                background: #1b2838;
                margin: 5% auto;
                padding: 20px;
                border: 1px solid #67c1f5;
                width: 90%;
                max-width: 1200px;
                color: #c6d4df;
                position: relative;
                max-height: 80vh;
                overflow-y: auto;
                z-index: 10001;
            }

            .vgt_columns {
                display: grid;
                grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
                gap: 20px;
                margin-top: 20px;
            }

            .vgt_shop_column {
                background: #16202d;
                border-radius: 4px;
                padding: 15px;
                border: 1px solid #2a475e;
            }

            .vgt_shop_header {
                font-weight: bold;
                font-size: 16px;
                margin-bottom: 15px;
                padding-bottom: 10px;
                border-bottom: 1px solid #67c1f5;
            }

            .vgt_shop_header a {
                color: #67c1f5 !important;
                text-decoration: none;
            }

            .vgt_shop_item {
                margin: 10px 0;
                padding: 10px;
                background: rgba(27, 40, 56, 0.7);
                border-radius: 3px;
            }

            .vgt_item_title {
                font-size: 14px;
                margin-bottom: 5px;
                line-height: 1.3;
            }

            .vgt_prices {
                display: flex;
                align-items: center;
                gap: 8px;
                flex-wrap: wrap;
            }

            .vgt_aprice {
                color: #5ba32b;
                font-weight: bold;
                font-size: 15px;
            }

            .vgt_oldprice {
                text-decoration: line-through;
                opacity: 0.6;
                font-size: 13px;
            }

            .vgt_percent {
                color: #67c1f5;
                font-size: 13px;
            }

            .vgt_shop_expand {
                text-align: center;
                margin-top: 10px;
                cursor: pointer;
            }

            .vgt_expand_toggle {
                color: #67c1f5;
                font-weight: bold;
                padding: 5px;
                transition: opacity 0.2s;
            }

            .vgt_expand_toggle:hover {
                opacity: 0.8;
            }

            .vgt_load_more {
                background: #67c1f5;
                color: #1b2838;
                border: none;
                padding: 10px 20px;
                margin: 20px auto 0;
                display: block;
                cursor: pointer;
                border-radius: 3px;
                transition: background 0.2s;
            }

            .vgt_load_more:hover {
                background: #4fa0d1;
            }

            .vgt_close {
                color: #aaa;
                position: absolute;
                right: 20px;
                top: 10px;
                font-size: 28px;
                cursor: pointer;
                transition: color 0.2s;
            }

            .vgt_close:hover {
                color: #67c1f5;
            }

            .vgt_loading, .vgt_error {
                text-align: center;
                padding: 20px;
                font-size: 16px;
            }


            .vgt_price_button .btnv6_blue_hoverfade {
                /* Убери 'height: 32px;' отсюда, если добавил его инлайн в HTML */
                margin: 0;
                padding: 0 15px;
                font-size: 15px;
                /* height: 32px; */ /* Можно оставить тут или задать инлайн */
                display: flex; /* Для выравнивания текста внутри */
                align-items: center; /* Для выравнивания текста внутри */
            }

            .vgt_selection {
                padding: 20px;
                text-align: center;
            }

            .vgt_games_list {
                display: grid;
                gap: 10px;
                margin-top: 15px;
            }

            .vgt_game_item {
                padding: 15px;
                background: #16202d;
                border-radius: 4px;
                cursor: pointer;
                transition: background 0.2s;
            }

            .vgt_game_item:hover {
                background: #1b2838;
            }

            .vgt_game_title {
                color: #67c1f5;
                font-weight: bold;
                margin-bottom: 5px;
            }

            .vgt_game_similar {
                color: #8f98a0;
                font-size: 12px;
            }

            .vgt_selection_buttons {
                margin-top: 20px;
                text-align: center;
            }

            .vgt_cancel_btn {
                background: #a34d4d;
                color: #fff;
                border: none;
                padding: 8px 20px;
                cursor: pointer;
                border-radius: 3px;
                transition: background 0.2s;
            }

            .vgt_cancel_btn:hover {
                background: #c25555;
            }
        `);

            addVGTButton();
        })();
    }

})();