Steam Currency Converter

Convert Steam prices between any currencies with commission support.

Bu betiği kurabilmeniz için Tampermonkey, Greasemonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği yüklemek için Tampermonkey gibi bir uzantı yüklemeniz gerekir.

Bu betiği kurabilmeniz için Tampermonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği kurabilmeniz için Tampermonkey ya da Userscripts gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği indirebilmeniz için ayrıca Tampermonkey gibi bir eklenti kurmanız gerekmektedir.

Bu komut dosyasını yüklemek için bir kullanıcı komut dosyası yöneticisi uzantısı yüklemeniz gerekecek.

(Zaten bir kullanıcı komut dosyası yöneticim var, kurmama izin verin!)

Bu stili yüklemek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için Stylus gibi bir uzantı kurmanız gerekir.

Bu stili yükleyebilmek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı kurmanız gerekir.

Bu stili yükleyebilmek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

(Zateb bir user-style yöneticim var, yükleyeyim!)

// ==UserScript==
// @name             Steam Currency Converter
// @name:ru          Конвертер валют в Steam
// @version          0.8.9.5
// @namespace        https://store.steampowered.com/
// @description      Convert Steam prices between any currencies with commission support.
// @description:ru   Конвертирует цены Steam между любой валютой с поддержкой комиссии.
// @author           NeTan
// @license          MIT
// @match            https://store.steampowered.com/*
// @match            https://steamcommunity.com/*
// @match            https://checkout.steampowered.com/*
// @icon             https://icons.duckduckgo.com/ip3/steampowered.com.ico
// @grant            GM_xmlhttpRequest
// @grant            GM_getValue
// @grant            GM_setValue
// @grant            GM_deleteValue
// @grant            GM_addStyle
// @grant            GM_registerMenuCommand
// @connect          cdn.jsdelivr.net
// ==/UserScript==

// --- DEBUG LOGGER ---
// (чтобы включить подробный лог, измените false на true)
const DEBUG_MODE = false;
const log = (message, ...args) => {
    if (DEBUG_MODE) {
        console.log("SCC DEBUG:", message, ...args);
    }
};
const warn = (message, ...args) => {
    if (DEBUG_MODE) {
        console.warn("SCC WARN:", message, ...args);
    }
};
const error = (message, ...args) => {
    console.error("SCC ERROR:", message, ...args);
};
// --- END LOGGER ---

// Currency symbol to abbreviation mapping
const CURRENCY_SYMBOLS = {
    "₸": "KZT",
    "TL": "TRY",
    "€": "EUR",
    "£": "GBP",
    "ARS$": "ARS",
    "₴": "UAH",
    "$": "USD", // Примечание: $ используется для USD, CAD, AUD, MXN и др. USD является запасным вариантом.
    "₽": "RUB",
    "Br": "BYN",
    "฿": "THB",
    "zł": "PLN",
    "R$": "BRL",
    "¥": "JPY", // Примечание: JPY и CNY используют ¥. JPY более вероятен без префикса.
    "CNY ¥": "CNY",
    "₩": "KRW",
    "A$": "AUD",
    "CDN$": "CAD",
    "CHF": "CHF",
    "kr": "NOK", // Также используется DKK, SEK. NOK - базовый вариант.
    "MXN$": "MXN",
    "₹": "INR",
    "AED": "AED",
    "SAR": "SAR",
    "R": "ZAR",
    "CLP$": "CLP",
    "S/.": "PEN",
    "COL$": "COP",
    "$U": "UYU",
    "₡": "CRC",
    "₪": "ILS",
    "KWD": "KWD",
    "QR": "QAR",
    "HK$": "HKD",
    "NT$": "TWD",
    "S$": "SGD",
    "Rp": "IDR",
    "RM": "MYR",
    "₱": "PHP",
    "₫": "VND"
};
// Abbreviation to symbol mapping
const ABBREVIATION_SYMBOLS = {};
Object.entries(CURRENCY_SYMBOLS).forEach(([symbol, abbr]) => {
    // Не перезаписываем $ -> USD, если уже есть
    if (!ABBREVIATION_SYMBOLS[abbr]) {
        ABBREVIATION_SYMBOLS[abbr] = symbol;
    }
});
// Добавляем $ вручную для валют, которые могут его использовать
ABBREVIATION_SYMBOLS["USD"] = "$";
ABBREVIATION_SYMBOLS["CAD"] = "CDN$";
ABBREVIATION_SYMBOLS["AUD"] = "A$";
ABBREVIATION_SYMBOLS["MXN"] = "MXN$";

// Pre-sorted symbols for currency detection (longest first)
const SORTED_CURRENCY_SYMBOLS = Object.entries(CURRENCY_SYMBOLS)
    .sort((a, b) => b[0].length - a[0].length);

// Supported Steam currencies
const SUPPORTED_CURRENCIES = [
    // Основные (из старого списка)
    { id: 1, code: "USD", sign: "$", description: "United States Dollars" },
    { id: 5, code: "RUB", sign: "₽", description: "Russian Rouble" },
    { id: 17, code: "TRY", sign: "TL", description: "Turkish Lira" },
    { id: 18, code: "UAH", sign: "₴", description: "Ukrainian Hryvnia" },
    { id: 29, code: "THB", sign: "฿", description: "Thai Baht" },
    { id: 34, code: "ARS", sign: "ARS$", description: "Argentine Peso" },
    { id: 37, code: "KZT", sign: "₸", description: "Kazakhstani Tenge" },
    { id: 42, code: "BYN", sign: "Br", description: "Belarusian Ruble" },

    // Добавленные
    { id: 2, code: "GBP", sign: "£", description: "British Pound" },
    { id: 3, code: "EUR", sign: "€", description: "European Union Euro" },
    { id: 6, code: "PLN", sign: "zł", description: "Polish Złoty" },
    { id: 7, code: "BRL", sign: "R$", description: "Brazilian Real" },
    { id: 8, code: "JPY", sign: "¥", description: "Japanese Yen" },
    { id: 9, code: "NOK", sign: "kr", description: "Norwegian Krone" },
    { id: 10, code: "CAD", sign: "CDN$", description: "Canadian Dollar" },
    { id: 11, code: "AUD", sign: "A$", description: "Australian Dollar" },
    { id: 12, code: "CHF", sign: "CHF", description: "Swiss Franc" },
    { id: 19, code: "MXN", sign: "MXN$", description: "Mexican Peso" },
    { id: 20, code: "INR", sign: "₹", description: "Indian Rupee" },
    { id: 21, code: "CLP", sign: "CLP$", description: "Chilean Peso" },
    { id: 22, code: "PEN", sign: "S/.", description: "Peruvian Sol" },
    { id: 23, code: "KRW", sign: "₩", description: "South Korean Won" },
    { id: 24, code: "COP", sign: "COL$", description: "Colombian Peso" },
    { id: 25, code: "CNY", sign: "CNY ¥", description: "Chinese Yuan" },
    { id: 26, code: "ZAR", sign: "R", description: "South African Rand" },
    { id: 27, code: "AED", sign: "AED", description: "United Arab Emirates Dirham" },
    { id: 28, code: "SAR", sign: "SAR", description: "Saudi Riyal" },
    { id: 30, code: "UYU", sign: "$U", description: "Uruguayan Peso" },
    { id: 31, code: "CRC", sign: "₡", description: "Costa Rican Colón" },
    { id: 32, code: "ILS", sign: "₪", description: "Israeli New Shekel" },
    { id: 33, code: "KWD", sign: "KWD", description: "Kuwaiti Dinar" },
    { id: 35, code: "QAR", sign: "QR", description: "Qatari Riyal" },
    { id: 36, code: "HKD", sign: "HK$", description: "Hong Kong Dollar" },
    { id: 38, code: "TWD", sign: "NT$", description: "New Taiwan Dollar" },
    { id: 39, code: "SGD", sign: "S$", description: "Singapore Dollar" },
    { id: 40, code: "IDR", sign: "Rp", description: "Indonesian Rupiah" },
    { id: 41, code: "MYR", sign: "RM", description: "Malaysian Ringgit" },
    { id: 43, code: "PHP", sign: "₱", description: "Philippine Peso" },
    { id: 44, code: "VND", sign: "₫", description: "Vietnamese Đồng" }
];
// Currencies using COMMA as decimal separator
const COMMA_DECIMAL_CURRENCIES = [
    "KZT", "TRY", "EUR", "ARS", "UAH", "RUB", "BYN", "PLN", "BRL",
    "NOK", "CHF", "CLP", "PEN", "COP", "UYU", "CRC", "IDR", "VND"
];
const COMMA_DECIMAL_SET = new Set(COMMA_DECIMAL_CURRENCIES);

// Lookup maps for O(1) currency access
const CURRENCY_BY_ID = new Map(SUPPORTED_CURRENCIES.map(c => [c.id, c]));
const CURRENCY_BY_CODE = new Map(SUPPORTED_CURRENCIES.map(c => [c.code, c]));

// Application state
let activeCurrencyCode = null;
let activeCurrencySign = null;
let exchangeData = null;
let baseRateFromRub = null;
let targetCurrencies = {}; // { code: commission }
let suppressOriginal = false;
let userLanguage = 'ru';
let languageMode = 'auto';
let panelThemeMode = 'auto';
let userAddedSelectors = [];
let finalPriceSelectorString = "";
let conversionLogicStarted = false; // Флаг, что логика конвертации уже запущена
let targetCurrencyOrder = []; // Порядок отображения валют
let panelCurrencyOrder = []; // Временный порядок при редактировании в панели

// Storage identifiers
const RATE_EXPIRY_ID = "rate_expiry_v2";
const RATE_DATA_ID = "rate_data_v2";
const TARGETS_ID = "targets";
const SUPPRESS_ID = "suppress_original";
const LANG_ID = "user_lang";
const LANG_MODE_ID = "user_lang_mode_v1";
const THEME_MODE_ID = "ui_theme_mode_v1";
const USER_SELECTORS_ID = "user_selectors_v1";
const DISABLED_BASE_SELECTORS_ID = "disabled_base_selectors_v1";
const CURRENCY_ORDER_ID = "currency_order_v1";

// Network utilities
const fetchData = url => new Promise((resolve, reject) => {
    GM_xmlhttpRequest({
        method: "GET",
        url,
        headers: { "Content-Type": "application/json" },
        onload: response => resolve(response.responseText),
        onerror: reject,
    });
});
const refreshRates = async () => {
    const cacheBuster = Math.random();
    const endpoint = `https://cdn.jsdelivr.net/npm/@fawazahmed0/currency-api@latest/v1/currencies/rub.json?${cacheBuster}`;
    log("Refreshing rates from:", endpoint);
    const response = await fetchData(endpoint);
    const parsedRates = JSON.parse(response);
    const expiry = Date.now() + 6 * 60 * 60 * 1000; // 6 hours
    GM_setValue(RATE_EXPIRY_ID, expiry);
    GM_setValue(RATE_DATA_ID, parsedRates);
    log("Rates refreshed and stored.");
    return parsedRates;
};
const loadRates = async () => {
    if (exchangeData) return;
    const expiry = GM_getValue(RATE_EXPIRY_ID, null);
    const storedRates = GM_getValue(RATE_DATA_ID, null);
    const rateTimestamp = storedRates ? Date.parse(storedRates.date) : Date.now();
    const needsRefresh = !storedRates || !expiry || expiry <= Date.now() ||
        (rateTimestamp + 24 * 60 * 60 * 1000) <= Date.now();
    if (needsRefresh) {
        log("Rate cache expired or missing, fetching new rates...");
        exchangeData = await refreshRates();
    } else {
        log("Loading rates from cache.");
        exchangeData = storedRates;
    }
};
// Value formatting utilities
const renderValue = (value, code) => {
    const lowValue = value < 100;
    const adjustedValue = lowValue ?
        Math.ceil(value * 10) / 10 : Math.ceil(value);
    const region = code === "RUB" ?
        "ru-RU" : "en-US";
    const formatOptions = lowValue ? { maximumFractionDigits: 1 } : {};
    return adjustedValue.toLocaleString(region, formatOptions);
};

// String/number helpers
const escapeRegex = value => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
const normalizePriceString = (raw, useCommaDecimal) => {
    let cleaned = raw.replace(/USD/gi, '');
    cleaned = cleaned.replace(/[\s\u00A0]/g, '');
    if (useCommaDecimal) {
        cleaned = cleaned.replace(/\./g, "").replace(/,/g, ".");
    } else {
        cleaned = cleaned.replace(/,/g, "");
    }
    return cleaned;
};

const cleanupDiscountedPrice = element => {
    const crossedOut = element.querySelector("span > strike");
    if (!crossedOut) return;
    crossedOut.remove();
    element.classList.remove("discounted");
    element.style.color = "#BEEE11";
};

// Regex cache for price parsing
const PRICE_TAG_REGEX_CACHE = new Map();
const getPriceTagRegex = sign => {
    const cached = PRICE_TAG_REGEX_CACHE.get(sign);
    if (cached) return cached;
    const escapedSign = escapeRegex(sign);
    const regex = new RegExp(
        `(<[a-z]+[^>]*>)?` +
        `(\\s*` +
            `(${escapedSign}\\s*[\\d.,]+(?:\\s*USD)?|[\\d.,\\s\u00A0]+\\s*${escapedSign})` +
        `\\s*)` +
        `(<\\/[a-z]+>)?`,
        'gi'
    );
    PRICE_TAG_REGEX_CACHE.set(sign, regex);
    return regex;
};

// *** ИСПРАВЛЕННАЯ ФУНКЦИЯ: Price modification routine (v1.0.15) ***
const applyConversion = element => {
    const classList = element.classList;

    // --- ИСПРАВЛЕНИЕ v0.8.15: Предотвращение бесконечного цикла React ---
    // Если React перезаписывает наш .done, проверяем, не содержит ли элемент уже нашу конвертацию.
    const elementText = element.textContent || "";
    if (classList.contains("done") || elementText.includes("≈")) {
        return;
    }
    // --- КОНЕЦ ИСПРАВЛЕНИЯ v0.8.15 ---

    const isWalletBalance = element.id === "header_wallet_balance";

    // Ранние проверки на выход
    if (!activeCurrencySign ||
        // Проверяем наличие символа валюты, только если это НЕ баланс кошелька
        (!isWalletBalance && !elementText.includes(activeCurrencySign)) ||
        classList.contains("es-regprice") || classList.contains("es-converted") ||
        classList.contains("discount_original_price")) {
        classList.add("done"); // Помечаем, чтобы не проверять снова
        return;
    }

    // Порядок валют: сначала по сохранённому порядку, затем остальные
    const orderedCodes = [];
    const seen = new Set();
    for (const code of targetCurrencyOrder) {
        if (code in targetCurrencies) { orderedCodes.push(code); seen.add(code); }
    }
    for (const code of Object.keys(targetCurrencies)) {
        if (!seen.has(code)) orderedCodes.push(code);
    }
    const targetEntries = orderedCodes.map(code => [code, targetCurrencies[code]]);
    if (targetEntries.length === 0 || !baseRateFromRub || !exchangeData) {
        if (DEBUG_MODE) {
            warn("Bailed applyConversion. Reason: missing targets, rate, or data.", {
                targets: targetEntries.length,
                baseRate: baseRateFromRub,
                data: !!exchangeData,
                element: element.cloneNode(true)
            });
        }
        return;
    }

    classList.add("done");
    if (DEBUG_MODE) {
        log("applyConversion running on element:", element.cloneNode(true));
    }

    // Предварительная чистка (для зачеркнутых цен)
    cleanupDiscountedPrice(element);

    let originalHTML = element.innerHTML;
    const escapedSign = escapeRegex(activeCurrencySign);
    const useCommaDecimal = COMMA_DECIMAL_SET.has(activeCurrencyCode);
    const priceTagPattern = getPriceTagRegex(activeCurrencySign);


    let replaced = false;

    const newHTML = originalHTML.replace(priceTagPattern, (match, openTag, priceTextWithSign, innerPrice, closeTag) => {
        log(`Match found: '${match}'`);

        const rawPriceString = priceTextWithSign.trim(); // e.g., "$1.79 USD" или "26 155,13₸"
        let cleanedText = rawPriceString.replace(new RegExp(escapedSign, 'g'), '');
        cleanedText = normalizePriceString(cleanedText, useCommaDecimal);

        const originalValue = parseFloat(cleanedText);

        log(`Parsed Value: ${originalValue} (from '${cleanedText}')`);

        if (isNaN(originalValue)) {
            warn("Parsing failed (NaN) for:", cleanedText, "from:", rawPriceString);
            return match; // Возвращаем оригинальное совпадение, если парсинг не удался
        }

        const rubEquivalent = originalValue / baseRateFromRub;
        log(`RUB Equivalent: ${rubEquivalent} (${originalValue} / ${baseRateFromRub})`);

        const currentConvertedDisplays = [];

        for (const [targetCode, fee] of targetEntries) {
            if (targetCode === activeCurrencyCode) {
                log(`Skipping conversion: Target (${targetCode}) is same as Active (${activeCurrencyCode})`);
                continue;
            }

            let rateToTarget = exchangeData.rub[targetCode.toLowerCase()];
            if (rateToTarget === undefined) {
                if (targetCode.toLowerCase() === 'rub') {
                    rateToTarget = 1;
                } else {
                    warn(`No rate found for target currency: ${targetCode}`);
                    continue;
                }
            }
            const convertedValue = rubEquivalent * rateToTarget * (1 + fee / 100);
            const displayedValue = renderValue(convertedValue, targetCode);
            const sign = getCurrencySign(targetCode);
            currentConvertedDisplays.push(`${displayedValue}${sign}`);
        }

        if (currentConvertedDisplays.length === 0) {
            warn("No converted displays generated (all targets might be same as active or no rates found).");
            if (!suppressOriginal) {
                 return match;
            }
        }

        replaced = true;
        const newPrice = currentConvertedDisplays.join(" ≈ ");
        log(`Converted Strings: ${newPrice}`);

        // Используем захваченные теги или пустую строку, чтобы сохранить форматирование
        const tagToUse = openTag ? openTag : '';
        const closingTagToUse = closeTag ? closeTag : '';

        let contentToInsert;

        if (suppressOriginal) {
            contentToInsert = (newPrice.length > 0) ? newPrice : "";
        } else {
            // Используем 'innerPrice' (Group 3), который содержит только цену (напр. $1.79 USD или 26 155,13₸)
            const displayPrice = innerPrice.trim();
            contentToInsert = (newPrice.length > 0) ? `${displayPrice} ≈ ${newPrice}` : displayPrice;
        }

        return `${tagToUse}${contentToInsert}${closingTagToUse}`;
    });

    if (replaced) {
        if (DEBUG_MODE) {
            log("Replacing HTML for element:", element);
        }
        element.innerHTML = newHTML;
    }
};

// *** ИСПРАВЛЕННАЯ ФУНКЦИЯ: Currency identification (v1.0.14 fix) ***
const identifyActiveCurrency = () => {
    if (activeCurrencyCode) return; // Не запускать дважды

    // 1. GStoreItemData (Надежно, если есть)
    try {
        const formattedSample = GStoreItemData.fnFormatCurrency(12345); // "$123.45 USD" or "123,45 ₸"

        if (formattedSample.includes('USD')) {
            activeCurrencyCode = "USD";
            activeCurrencySign = "$"; // Используем базовый символ $
            log(`Currency via GStoreItemData (USD Fix): ${activeCurrencyCode} (${activeCurrencySign})`);
            return;
        }

        const cleanedSample = formattedSample
            .replace("123,45", "").replace("123.45", "").trim(); // "₸"

        activeCurrencyCode = CURRENCY_SYMBOLS[cleanedSample];
        activeCurrencySign = cleanedSample;

        if (activeCurrencyCode && !activeCurrencySign) {
             activeCurrencySign = ABBREVIATION_SYMBOLS[activeCurrencyCode];
        }

        log(`Currency via GStoreItemData (Legacy): ${activeCurrencyCode} (${activeCurrencySign})`);
        return;
    } catch (err) {}

    // 2. Wallet Info (Надежно, если залогинен)
    try {
        const walletCode = getCurrencyById(g_rgWalletInfo?.wallet_currency);
        if (walletCode) {
            activeCurrencyCode = walletCode.code;
            activeCurrencySign = walletCode.sign;
            log(`Currency via Wallet Info: ${activeCurrencyCode} (${activeCurrencySign})`);
            return;
        }
    } catch (err) {}

    // 3. Meta tag (Надежный, но не всегда есть)
    const priceMeta = document.querySelector('meta[itemprop="priceCurrency"]');
    if (priceMeta?.content) {
        activeCurrencyCode = priceMeta.content;
        activeCurrencySign = ABBREVIATION_SYMBOLS[activeCurrencyCode];
        if (!activeCurrencySign) {
            warn(`Found currency code ${activeCurrencyCode} from meta, but no matching SIGN in ABBREVIATION_SYMBOLS.`);
            const curr = getCurrencyByCode(activeCurrencyCode);
            if (curr) activeCurrencySign = curr.sign;
        }
        log(`Currency via Meta Tag: ${activeCurrencyCode} (${activeCurrencySign})`);
        return;
    }


    // 4. Page Scrape (Резервный метод для динамических страниц)
    log("Using Page Scrape fallback to find currency...");
    const sortedSymbols = SORTED_CURRENCY_SYMBOLS;

    // Сканируем *только* селекторы цен, а не все div
    const candidateElements = document.querySelectorAll(BASE_PRICE_TARGETS.join(','));
    if (candidateElements.length === 0) {
        log("Page Scrape: No price elements found yet.");
        return; // Не найдено, выходим (повторный запуск будет из observe)
    }

    for (const [sign, code] of sortedSymbols) {
        for (const candidate of candidateElements) {
            if (sign === "$" && candidate.innerText.includes("USD")) {
                 activeCurrencySign = "$";
                 activeCurrencyCode = "USD";
                 log(`Currency via Page Scrape (USD Fix): ${activeCurrencyCode} (${activeCurrencySign})`);
                 return;
            }

            if (candidate.innerText.includes(sign)) {
                activeCurrencySign = sign;
                activeCurrencyCode = code;
                log(`Currency via Page Scrape (Legacy): ${activeCurrencyCode} (${activeCurrencySign})`);
                return;
            }
        }
    }

    warn("Could not identify active currency.");
};

// *** ИСПРАВЛЕННЫЙ СПИСОК: Price element selectors (v0.8.15) ***
const BASE_PRICE_TARGETS = [
    "#header_wallet_balance",
    "div[class*=StoreSalePriceBox]",
    "div[class*='StoreSalePriceWidget_']",
    "div[class*='StoreOriginalPrice_']",
    ".game_purchase_price",
    // --- Селекторы Списка желаемого ---
    "div._3j4dI1yA7cRfCvK8h406OB",
    "div.DOnsaVcV0Is-",
    "div._79DIT7RUQ5g-",
    // --- Селекторы Корзины (Новые) ---
    "._2WLaY5TxjBGVyuWe_6KS3N", // Итого (total)
    ".k2r-13oII_503_1b0Bf",     // Подытог (subtotal)
    "._38-m1g-YVkf-nCAcHJ1NbP", // Цена элемента (item price)
    // ---
    ".steamdb_prices_top",
    ".discount_final_price:not(:has(> .your_price_label))",
    ".discount_final_price > div:not([class])",
    ".search_price",
    ".price:not(.spotlight_body):not(.similar_grid_price)",
    ".match_subtitle",
    ".game_area_dlc_price:not(:has(> *))",
    ".savings.bundle_savings",
    ".wallet_column",
    ".wht_total",
    ".normal_price:not(.market_table_value)",
    ".sale_price",
    ".StoreSalePriceWidgetContainer:not(.Discounted) div",
    ".StoreSalePriceWidgetContainer.Discounted div:nth-child(2) > div:nth-child(2)",
    "#marketWalletBalanceAmount",
    ".market_commodity_order_summary > span:nth-child(2)",
    ".market_commodity_orders_table tr > td:first-child",
    ".market_listing_price_with_fee",
    ".market_activity_price",
    ".item_market_actions > div > div:nth-child(2)",
    ".wishlist_row .discount_final_price",
    ".wishlist_row .discount_original_price",
    ".wishlist_row .normal_price",
    ".wishlist_row .price",
    ".ws_row_bold .price",
    "[class*='wishlist'] .price",
    ".wishlist_row div",
];
// Price application handler
const processPrices = elements => {
    if (!elements || elements.length === 0) return;
    for (const elem of elements) {
        applyConversion(elem);
    }
};

// DOM mutation watcher
const initializeWatcher = () => {
    log("Initializing MutationObserver...");
    const pendingNodes = new Set();
    let scheduled = false;

    const scheduleProcess = () => {
        if (scheduled) return;
        scheduled = true;
        const run = () => {
            scheduled = false;
            if (pendingNodes.size === 0) return;
            const nodes = Array.from(pendingNodes);
            pendingNodes.clear();

            // Повторное определение, если валюта не найдена
            if (!activeCurrencyCode) {
                log("Watcher trying to find active currency...");
                identifyActiveCurrency(); // Повторный вызов
                if (activeCurrencyCode) {
                    // Валюта найдена, запускаем логику конвертации
                    startConversionLogic();
                } else {
                    return;
                }
            }

            const foundPriceNodes = new Set();
            for (const added of nodes) {
                if (added.nodeType !== Node.ELEMENT_NODE) continue;
                if (added.matches && added.matches(finalPriceSelectorString)) {
                    foundPriceNodes.add(added);
                }
                if (added.querySelectorAll) {
                    const matches = added.querySelectorAll(finalPriceSelectorString);
                    for (const match of matches) {
                        foundPriceNodes.add(match);
                    }
                }
            }
            if (foundPriceNodes.size > 0) {
                processPrices(Array.from(foundPriceNodes));
            }
        };
        if (typeof requestAnimationFrame === "function") {
            requestAnimationFrame(run);
        } else {
            setTimeout(run, 0);
        }
    };

    const watcher = new MutationObserver(mutations => {
        try {
            for (const change of mutations) {
                if (!change.addedNodes || change.addedNodes.length === 0) continue;
                for (const node of change.addedNodes) {
                    pendingNodes.add(node);
                }
            }
            if (pendingNodes.size > 0) {
                scheduleProcess();
            }
        } catch (err) {
            error("Error in MutationObserver:", err);
        }
    });
    watcher.observe(document.body, { childList: true, subtree: true });
};

// CSS selector generation
const generateSelectorForElement = (el) => {
    if ((!el.classList || el.classList.length === 0) && el.parentElement) {
        el = el.parentElement;
    }
    if (!el || el === document.body) return null;
    if (el.id) {
        const selector = `#${el.id}`;
        if (document.querySelectorAll(selector).length === 1) {
            return selector;
        }
    }
    const classes = Array.from(el.classList).filter(c => c !== 'done');
    if (classes.length > 0) {
        return `${el.tagName.toLowerCase()}.${classes.join('.')}`;
    }
    if (el.parentElement && el.parentElement !== document.body) {
        const parentClasses = Array.from(el.parentElement.classList).filter(c => c !== 'done');
        if (parentClasses.length > 0) {
            return `${el.parentElement.tagName.toLowerCase()}.${parentClasses.join('.')} > ${el.tagName.toLowerCase()}`;
        }
    }
    return null;
};
// Add selector by click
const findAndAddSelector = (callbacks = {}) => {
    const { onStatus, onDone } = callbacks;
    if (typeof onStatus === "function") {
        onStatus(getSettingsText("addMissingInstructions"));
    }
    const clickHandler = (e) => {
        e.preventDefault();
        e.stopPropagation();
        document.body.removeEventListener('click', clickHandler, true);
        const targetElement = e.target;
        const targetText = targetElement?.textContent || "";
        if (!activeCurrencySign || !targetText.includes(activeCurrencySign)) {
            error("Invalid click", { activeSign: activeCurrencySign, text: targetText });
            if (typeof onStatus === "function") {
                onStatus(getSettingsText("invalidPriceClick"));
            }
            if (typeof onDone === "function") onDone(false);
            return;
        }
        const newSelector = generateSelectorForElement(targetElement);
        if (newSelector) {
            let currentSelectors = GM_getValue(USER_SELECTORS_ID, []);
            if (currentSelectors.includes(newSelector)) {
                if (typeof onStatus === "function") {
                    onStatus(getSettingsText("selectorExists"));
                }
                if (typeof onDone === "function") onDone(false);
                return;
            }
            currentSelectors.push(newSelector);
            GM_setValue(USER_SELECTORS_ID, currentSelectors);
            userAddedSelectors = currentSelectors;
            buildFinalSelectorString();
            if (typeof onStatus === "function") {
                onStatus(getSettingsText("addedSelector", newSelector));
            }
            if (typeof onDone === "function") onDone(true);
        } else {
            if (typeof onStatus === "function") {
                onStatus(getSettingsText("priceNotFound"));
            }
            if (typeof onDone === "function") onDone(false);
        }
    };
    document.body.addEventListener('click', clickHandler, true);
};

const SETTINGS_PANEL_ID = "scc-settings-panel";
const SETTINGS_BACKDROP_ID = "scc-settings-backdrop";

const SETTINGS_I18N = {
    ru: {
        menuSettings: "Настройки",
        panelTitle: "Steam Currency Converter - Настройки",
        actions: "Действия",
        runNow: "Запустить вручную",
        addByClick: "Добавить селектор кликом",
        export: "Экспорт",
        import: "Импорт",
        configuration: "Конфигурация",
        language: "Язык",
        languageAuto: "Auto",
        themeToggleTitle: "Тема панели",
        themeToggleAuto: "Авто",
        themeToggleLight: "Светлая",
        themeToggleDark: "Темная",
        currencyColumn: "Валюта",
        feeColumn: "Комиссия",
        hideOriginal: "Скрывать оригинальную цену",
        targetCurrencies: "Целевые валюты",
        customSelectors: "Пользовательские селекторы",
        add: "Добавить",
        baseSelectorToggles: "Базовые селекторы (по умолчанию ВКЛ)",
        save: "Сохранить",
        remove: "Удалить",
        noSelectors: "Нет добавленных селекторов.",
        selectorPlaceholder: ".wishlist_row .price",
        removedSelector: selector => `Удален селектор: ${selector}`,
        runningManual: "Запускаю конвертацию вручную...",
        manualDone: "Ручной запуск завершен.",
        selectorExists: "Селектор уже существует.",
        addedSelector: selector => `Добавлен селектор: ${selector}`,
        exported: "Настройки экспортированы.",
        imported: "Настройки импортированы.",
        importFailed: err => `Ошибка импорта: ${err}`,
        savedReloading: "Сохранено. Перезагрузка страницы...",
        tipActions: "Быстрые действия: ручной запуск, добавление селектора и перенос настроек.",
        tipConfig: "Основные параметры отображения и конвертации цен.",
        tipTargets: "Включите нужные валюты, задайте комиссию в процентах.",
        tipSelectors: "Добавляйте селекторы, если на странице пропускаются цены.",
        tipSave: "Сохраняет изменения и перезагружает страницу.",
        displayOrder: "Порядок отображения",
        tipOrder: "Используйте стрелки для изменения порядка конвертированных цен.",
        orderEmpty: "Включите валюты выше.",
        addMissingInstructions: "Нажмите на неконвертированную цену. Страница перезагрузится.",
        invalidPriceClick: "Выбранный элемент не содержит цену с символом активной валюты.",
        priceNotFound: "Не удалось создать полезный селектор для этого элемента."
    },
    en: {
        menuSettings: "Settings",
        panelTitle: "Steam Currency Converter - Settings",
        actions: "Actions",
        runNow: "Run manually",
        addByClick: "Add selector by click",
        export: "Export",
        import: "Import",
        configuration: "Configuration",
        language: "Language",
        languageAuto: "Auto",
        themeToggleTitle: "Panel theme",
        themeToggleAuto: "Auto",
        themeToggleLight: "Light",
        themeToggleDark: "Dark",
        currencyColumn: "Currency",
        feeColumn: "Fee",
        hideOriginal: "Hide original price",
        targetCurrencies: "Target currencies",
        customSelectors: "Custom selectors",
        add: "Add",
        baseSelectorToggles: "Base selector toggles (default ON)",
        save: "Save",
        remove: "Remove",
        noSelectors: "No custom selectors added.",
        selectorPlaceholder: ".wishlist_row .price",
        removedSelector: selector => `Removed selector: ${selector}`,
        runningManual: "Running conversion manually...",
        manualDone: "Manual run finished.",
        selectorExists: "Selector already exists.",
        addedSelector: selector => `Added selector: ${selector}`,
        exported: "Settings exported.",
        imported: "Settings imported.",
        importFailed: err => `Import failed: ${err}`,
        savedReloading: "Saved. Reloading page...",
        tipActions: "Quick actions: manual run, selector capture, import/export settings.",
        tipConfig: "Main parameters for displaying and converting prices.",
        tipTargets: "Enable target currencies and set fee percent.",
        tipSelectors: "Add selectors when some prices are not detected.",
        tipSave: "Saves changes and reloads the page.",
        displayOrder: "Display order",
        tipOrder: "Use arrows to change display order of converted prices.",
        orderEmpty: "Enable currencies above.",
        addMissingInstructions: "Click on the unconverted price. The page will reload.",
        invalidPriceClick: "The selected element does not contain a price with the active currency symbol.",
        priceNotFound: "Could not generate a useful selector for this element."
    }
};

const detectBrowserLanguage = () => {
    const navLanguage = (navigator.language || navigator.userLanguage || "en").toLowerCase();
    return navLanguage.startsWith("ru") ? "ru" : "en";
};

const parseCssColorToRgb = (value) => {
    if (!value) return null;
    const normalized = value.trim().toLowerCase();
    if (normalized === "transparent" || normalized === "rgba(0, 0, 0, 0)") return null;

    const rgbMatch = normalized.match(/^rgba?\(([^)]+)\)$/);
    if (rgbMatch) {
        const parts = rgbMatch[1].split(",").map(part => parseFloat(part.trim()));
        if (parts.length >= 3 && parts.slice(0, 3).every(Number.isFinite)) {
            return { r: parts[0], g: parts[1], b: parts[2] };
        }
        return null;
    }

    const hexMatch = normalized.match(/^#([0-9a-f]{3}|[0-9a-f]{6})$/i);
    if (hexMatch) {
        const hex = hexMatch[1];
        if (hex.length === 3) {
            return {
                r: parseInt(hex[0] + hex[0], 16),
                g: parseInt(hex[1] + hex[1], 16),
                b: parseInt(hex[2] + hex[2], 16)
            };
        }
        return {
            r: parseInt(hex.slice(0, 2), 16),
            g: parseInt(hex.slice(2, 4), 16),
            b: parseInt(hex.slice(4, 6), 16)
        };
    }

    return null;
};

const getColorLuminance = (rgb) => {
    if (!rgb) return 1;
    return (0.2126 * rgb.r + 0.7152 * rgb.g + 0.0722 * rgb.b) / 255;
};

const isColorDark = (colorValue) => {
    const rgb = parseCssColorToRgb(colorValue);
    if (!rgb) return false;
    return getColorLuminance(rgb) < 0.45;
};

const detectPageDarkTheme = () => {
    const html = document.documentElement;
    const body = document.body;
    if (!html || !body) return false;

    const classMarkers = ["night-theme", "dark", "theme-dark", "dark-mode"];
    for (const marker of classMarkers) {
        if (html.classList.contains(marker) || body.classList.contains(marker)) {
            return true;
        }
    }

    const attrMarkers = ["data-theme", "theme", "color-theme"];
    for (const attr of attrMarkers) {
        const htmlTheme = (html.getAttribute(attr) || "").toLowerCase();
        const bodyTheme = (body.getAttribute(attr) || "").toLowerCase();
        if (htmlTheme.includes("dark") || bodyTheme.includes("dark")) {
            return true;
        }
    }

    const metaThemeColor = document.querySelector('meta[name="theme-color"]');
    if (metaThemeColor && isColorDark(metaThemeColor.getAttribute("content"))) {
        return true;
    }

    const probeSelectors = [
        "body",
        "html",
        "#responsive_page_frame",
        "#global_header",
        ".responsive_page_frame",
        ".responsive_page_content"
    ];
    let darkVotes = 0;
    let checked = 0;
    for (const selector of probeSelectors) {
        const node = selector === "body" ? body : (selector === "html" ? html : document.querySelector(selector));
        if (!node) continue;
        const bg = window.getComputedStyle(node).backgroundColor;
        const rgb = parseCssColorToRgb(bg);
        if (!rgb) continue;
        checked += 1;
        if (getColorLuminance(rgb) < 0.45) {
            darkVotes += 1;
        }
    }

    if (checked === 0) return false;
    return darkVotes >= Math.ceil(checked / 2);
};

const resolveLanguage = (mode) => {
    if (mode === "ru" || mode === "en") return mode;
    return detectBrowserLanguage();
};

const getSettingsText = (key, ...args) => {
    const dict = SETTINGS_I18N[userLanguage] || SETTINGS_I18N.en;
    const value = dict[key];
    if (typeof value === "function") return value(...args);
    return value || key;
};

const loadLanguagePreference = () => {
    const storedMode = GM_getValue(LANG_MODE_ID, null);
    if (storedMode === "auto" || storedMode === "ru" || storedMode === "en") {
        languageMode = storedMode;
    } else {
        const legacyLang = GM_getValue(LANG_ID, null);
        languageMode = legacyLang === "ru" || legacyLang === "en" ? legacyLang : "auto";
        GM_setValue(LANG_MODE_ID, languageMode);
    }
    userLanguage = resolveLanguage(languageMode);
    GM_setValue(LANG_ID, userLanguage);
};

const loadThemePreference = () => {
    const storedThemeMode = GM_getValue(THEME_MODE_ID, "auto");
    panelThemeMode = ["auto", "light", "dark"].includes(storedThemeMode) ? storedThemeMode : "auto";
};

const resolvePanelTheme = (mode) => {
    if (mode === "dark" || mode === "light") return mode;
    return detectPageDarkTheme() ? "dark" : "light";
};

const applyPanelTheme = (panel, mode) => {
    const resolvedTheme = resolvePanelTheme(mode);
    panel.classList.remove("scc-theme-light", "scc-theme-dark");
    panel.classList.add(`scc-theme-${resolvedTheme}`);
};

let settingsTooltipEl = null;
const ensureSettingsTooltip = () => {
    if (settingsTooltipEl && document.body.contains(settingsTooltipEl)) {
        return settingsTooltipEl;
    }
    settingsTooltipEl = document.createElement("div");
    settingsTooltipEl.id = "scc-help-tooltip";
    settingsTooltipEl.className = "scc-help-tooltip";
    document.body.append(settingsTooltipEl);
    return settingsTooltipEl;
};

const positionSettingsTooltip = (tooltip, anchor) => {
    const anchorRect = anchor.getBoundingClientRect();
    const gap = 8;
    let top = anchorRect.bottom + gap;
    let left = anchorRect.left;
    let preferAbove = false;

    const viewportWidth = window.innerWidth;
    const viewportHeight = window.innerHeight;
    const safeMaxWidth = Math.max(160, viewportWidth - (gap * 2));
    tooltip.style.maxWidth = `${safeMaxWidth}px`;
    tooltip.style.left = "0px";
    tooltip.style.top = "0px";
    const tooltipWidth = tooltip.offsetWidth;
    const tooltipHeight = tooltip.offsetHeight;

    if (left + tooltipWidth > viewportWidth - gap) {
        left = viewportWidth - tooltipWidth - gap;
    }
    if (left < gap) {
        left = gap;
    }

    if (top + tooltipHeight > viewportHeight - gap) {
        top = anchorRect.top - tooltipHeight - gap;
        preferAbove = true;
    }
    if (top < gap) {
        top = gap;
    }

    tooltip.classList.toggle("is-above", preferAbove);
    tooltip.style.left = `${left}px`;
    tooltip.style.top = `${top}px`;
};

const attachHelpTooltipHandlers = (panel) => {
    const tooltip = ensureSettingsTooltip();
    let activeHelp = null;
    let showTimer = null;

    const hideTooltip = () => {
        if (showTimer) {
            clearTimeout(showTimer);
            showTimer = null;
        }
        activeHelp = null;
        tooltip.classList.remove("is-visible");
        tooltip.classList.remove("is-above");
    };

    panel.addEventListener("mouseover", event => {
        const help = event.target.closest(".scc-help");
        if (!help || !panel.contains(help)) return;
        const text = help.getAttribute("data-tooltip") || "";
        if (!text) return;
        activeHelp = help;
        tooltip.textContent = text;
        if (showTimer) clearTimeout(showTimer);
        showTimer = setTimeout(() => {
            if (activeHelp !== help) return;
            tooltip.classList.add("is-visible");
            positionSettingsTooltip(tooltip, help);
        }, 180);
    });

    panel.addEventListener("mousemove", event => {
        if (!activeHelp || !tooltip.classList.contains("is-visible")) return;
        const help = event.target.closest(".scc-help");
        if (!help || help !== activeHelp) return;
        positionSettingsTooltip(tooltip, activeHelp);
    });

    panel.addEventListener("mouseout", event => {
        if (!activeHelp) return;
        const fromHelp = event.target.closest(".scc-help");
        if (!fromHelp) return;
        if (event.relatedTarget && fromHelp.contains(event.relatedTarget)) return;
        hideTooltip();
    });

    panel.addEventListener("mouseleave", hideTooltip);
    window.addEventListener("scroll", hideTooltip, true);
};

const getNextThemeMode = (current) => {
    if (current === "auto") return "light";
    if (current === "light") return "dark";
    return "auto";
};

const getThemeToggleMeta = (mode, langCode) => {
    const dict = SETTINGS_I18N[langCode] || SETTINGS_I18N.en;
    if (mode === "light") return { icon: "🔆", label: dict.themeToggleLight };
    if (mode === "dark") return { icon: "🌙", label: dict.themeToggleDark };
    return { icon: "🌓", label: dict.themeToggleAuto };
};

const updateThemeToggleButton = (panel, mode, langCode) => {
    const btn = panel.querySelector("#scc-theme-toggle-btn");
    if (!btn) return;
    const dict = SETTINGS_I18N[langCode] || SETTINGS_I18N.en;
    const meta = getThemeToggleMeta(mode, langCode);
    btn.textContent = meta.icon;
    btn.title = `${dict.themeToggleTitle}: ${meta.label}`;
    btn.setAttribute("aria-label", `${dict.themeToggleTitle}: ${meta.label}`);
};

const applyPanelLocalization = (panel, langCode) => {
    const dict = SETTINGS_I18N[langCode] || SETTINGS_I18N.en;
    const setText = (selector, value) => {
        const el = panel.querySelector(selector);
        if (el) el.textContent = value;
    };
    const setTip = (selector, value) => {
        const el = panel.querySelector(selector);
        if (el) {
            el.setAttribute("data-tooltip", value);
            el.removeAttribute("title");
            el.setAttribute("aria-label", value);
        }
    };

    setText("#scc-panel-title", dict.panelTitle);
    setText("#scc-actions-title", dict.actions);
    if (DEBUG_MODE) setText("#scc-run-now", dict.runNow);
    setText("#scc-add-selector-click", dict.addByClick);
    setText("#scc-export-settings", dict.export);
    setText("#scc-import-settings-btn", dict.import);
    setText("#scc-config-title", dict.configuration);
    setText("#scc-currency-col-label", dict.currencyColumn);
    setText("#scc-fee-col-label", dict.feeColumn);
    setText("#scc-hide-original-label", dict.hideOriginal);
    setText("#scc-target-currencies-title", dict.targetCurrencies);
    setText("#scc-currency-order-title", dict.displayOrder);
    setText("#scc-custom-selectors-title", dict.customSelectors);
    setText("#scc-add-selector-manual", dict.add);
    setText("#scc-base-selectors-title", dict.baseSelectorToggles);
    setText("#scc-save-settings", dict.save);

    const selectorInput = panel.querySelector("#scc-new-selector");
    if (selectorInput) selectorInput.placeholder = dict.selectorPlaceholder;

    const autoOption = panel.querySelector('#scc-lang-mode option[value="auto"]');
    if (autoOption) autoOption.textContent = dict.languageAuto;
    setTip("#scc-actions-help", dict.tipActions);
    setTip("#scc-config-help", dict.tipConfig);
    setTip("#scc-targets-help", dict.tipTargets);
    setTip("#scc-order-help", dict.tipOrder);
    setTip("#scc-selectors-help", dict.tipSelectors);
    setTip("#scc-save-help", dict.tipSave);
    updateThemeToggleButton(panel, panelThemeMode, langCode);
};

const buildFinalSelectorString = () => {
    const disabledBaseSelectors = new Set(GM_getValue(DISABLED_BASE_SELECTORS_ID, []));
    const enabledBaseSelectors = BASE_PRICE_TARGETS.filter(sel => !disabledBaseSelectors.has(sel));
    const uniqueSelectors = Array.from(new Set([...enabledBaseSelectors, ...userAddedSelectors]));
    finalPriceSelectorString = uniqueSelectors.map(sel => `${sel}:not(.done)`).join(", ");
    return finalPriceSelectorString;
};


const renderCurrencyList = (container, targetsSnapshot) => {
    container.innerHTML = "";
    for (const currency of SUPPORTED_CURRENCIES) {
        const row = document.createElement("div");
        row.className = "scc-list-row";

        const left = document.createElement("div");
        left.className = "scc-row-left";
        left.textContent = `${currency.code} (${currency.sign})`;

        const controls = document.createElement("div");
        controls.className = "scc-row-controls";

        const switchWrap = document.createElement("label");
        switchWrap.className = "scc-switch";
        const enabledInput = document.createElement("input");
        enabledInput.type = "checkbox";
        enabledInput.dataset.code = currency.code;
        enabledInput.className = "scc-currency-enabled";
        enabledInput.checked = Object.prototype.hasOwnProperty.call(targetsSnapshot, currency.code);
        const slider = document.createElement("span");
        slider.className = "scc-slider";
        switchWrap.append(enabledInput, slider);

        const feeInput = document.createElement("input");
        feeInput.type = "number";
        feeInput.min = "0";
        feeInput.step = "0.1";
        feeInput.className = "scc-input scc-fee-input";
        feeInput.dataset.code = currency.code;
        feeInput.value = (targetsSnapshot[currency.code] ?? 0).toString();
        feeInput.disabled = !enabledInput.checked;

        enabledInput.addEventListener("change", () => {
            feeInput.disabled = !enabledInput.checked;
        });

        controls.append(feeInput, switchWrap);
        row.append(left, controls);
        container.append(row);
    }
};

const renderCurrencyOrder = (container, order, langCode = userLanguage) => {
    const dict = SETTINGS_I18N[langCode] || SETTINGS_I18N.en;
    container.innerHTML = "";
    if (order.length === 0) {
        const empty = document.createElement("div");
        empty.className = "scc-empty";
        empty.textContent = dict.orderEmpty;
        container.append(empty);
        return;
    }
    order.forEach((code, index) => {
        const currency = CURRENCY_BY_CODE.get(code);
        if (!currency) return;
        const row = document.createElement("div");
        row.className = "scc-list-row scc-order-row";

        const label = document.createElement("span");
        label.className = "scc-row-left";
        label.textContent = `${currency.code} (${currency.sign})`;

        const controls = document.createElement("div");
        controls.className = "scc-row-controls";

        const upBtn = document.createElement("button");
        upBtn.type = "button";
        upBtn.className = "scc-btn scc-btn-secondary scc-order-btn";
        upBtn.textContent = "▲";
        upBtn.dataset.code = code;
        upBtn.dataset.dir = "up";
        upBtn.disabled = index === 0;

        const downBtn = document.createElement("button");
        downBtn.type = "button";
        downBtn.className = "scc-btn scc-btn-secondary scc-order-btn";
        downBtn.textContent = "▼";
        downBtn.dataset.code = code;
        downBtn.dataset.dir = "down";
        downBtn.disabled = index === order.length - 1;

        controls.append(upBtn, downBtn);
        row.append(label, controls);
        container.append(row);
    });
};

const renderSelectorList = (container, selectors, langCode = userLanguage) => {
    const dict = SETTINGS_I18N[langCode] || SETTINGS_I18N.en;
    container.innerHTML = "";
    if (!selectors.length) {
        const empty = document.createElement("div");
        empty.className = "scc-empty";
        empty.textContent = dict.noSelectors;
        container.append(empty);
        return;
    }
    selectors.forEach((selector, index) => {
        const row = document.createElement("div");
        row.className = "scc-list-row";

        const text = document.createElement("div");
        text.className = "scc-selector-text";
        text.textContent = `${index + 1}. ${selector}`;

        const removeBtn = document.createElement("button");
        removeBtn.type = "button";
        removeBtn.className = "scc-btn scc-btn-secondary";
        removeBtn.textContent = dict.remove;
        removeBtn.dataset.removeSelector = selector;
        row.append(text, removeBtn);
        container.append(row);
    });
};

const renderBaseSelectorToggleList = (container, disabledSelectors) => {
    container.innerHTML = "";
    const disabledSet = new Set(disabledSelectors);
    for (const selector of BASE_PRICE_TARGETS) {
        const row = document.createElement("div");
        row.className = "scc-list-row";

        const text = document.createElement("div");
        text.className = "scc-selector-text";
        text.textContent = selector;

        const switchWrap = document.createElement("label");
        switchWrap.className = "scc-switch";

        const toggle = document.createElement("input");
        toggle.type = "checkbox";
        toggle.className = "scc-base-selector-toggle";
        toggle.dataset.selector = selector;
        toggle.checked = !disabledSet.has(selector);
        const slider = document.createElement("span");
        slider.className = "scc-slider";
        switchWrap.append(toggle, slider);

        row.append(text, switchWrap);
        container.append(row);
    }
};

const loadSettingsToPanel = (panel) => {
    const languageInput = panel.querySelector("#scc-lang-mode");
    const suppressInput = panel.querySelector("#scc-suppress-original");
    const currencyList = panel.querySelector("#scc-currency-list");
    const selectorsList = panel.querySelector("#scc-custom-selectors-list");
    const baseSelectorsList = panel.querySelector("#scc-base-selectors-list");

    const storedTargets = GM_getValue(TARGETS_ID, {});
    const storedLangMode = GM_getValue(LANG_MODE_ID, languageMode);
    const storedThemeMode = GM_getValue(THEME_MODE_ID, panelThemeMode);
    const storedSuppress = GM_getValue(SUPPRESS_ID, false);
    const storedCustomSelectors = GM_getValue(USER_SELECTORS_ID, []);
    const disabledBaseSelectors = GM_getValue(DISABLED_BASE_SELECTORS_ID, []);

    languageInput.value = ["auto", "ru", "en"].includes(storedLangMode) ? storedLangMode : "auto";
    panelThemeMode = ["auto", "light", "dark"].includes(storedThemeMode) ? storedThemeMode : "auto";
    const panelLang = resolveLanguage(languageInput.value);
    applyPanelTheme(panel, panelThemeMode);
    updateThemeToggleButton(panel, panelThemeMode, panelLang);
    applyPanelLocalization(panel, panelLang);
    suppressInput.checked = storedSuppress;
    renderCurrencyList(currencyList, storedTargets);

    // Загружаем порядок валют и синхронизируем с включёнными
    const storedOrder = GM_getValue(CURRENCY_ORDER_ID, []);
    const enabledCodes = Object.keys(storedTargets);
    panelCurrencyOrder = storedOrder.filter(c => enabledCodes.includes(c));
    for (const code of enabledCodes) {
        if (!panelCurrencyOrder.includes(code)) panelCurrencyOrder.push(code);
    }
    const orderList = panel.querySelector("#scc-currency-order-list");
    if (orderList) renderCurrencyOrder(orderList, panelCurrencyOrder, panelLang);

    renderSelectorList(selectorsList, storedCustomSelectors, panelLang);
    if (baseSelectorsList) {
        renderBaseSelectorToggleList(baseSelectorsList, disabledBaseSelectors);
    }
};

const saveSettingsFromPanel = (panel) => {
    const selectedLanguageMode = panel.querySelector("#scc-lang-mode").value;
    const nextLanguageMode = ["auto", "ru", "en"].includes(selectedLanguageMode) ? selectedLanguageMode : "auto";
    const language = resolveLanguage(nextLanguageMode);
    const suppress = panel.querySelector("#scc-suppress-original").checked;

    const nextTargets = {};
    panel.querySelectorAll(".scc-currency-enabled").forEach(toggle => {
        const code = toggle.dataset.code;
        if (!toggle.checked) return;
        const feeInput = panel.querySelector(`.scc-fee-input[data-code="${code}"]`);
        const fee = parseFloat(feeInput?.value ?? "0");
        nextTargets[code] = Number.isFinite(fee) && fee >= 0 ? fee : 0;
    });

    let disabledBaseSelectors = GM_getValue(DISABLED_BASE_SELECTORS_ID, []);
    if (panel.querySelector("#scc-base-selectors-list")) {
        disabledBaseSelectors = [];
        panel.querySelectorAll(".scc-base-selector-toggle").forEach(toggle => {
            if (!toggle.checked) {
                disabledBaseSelectors.push(toggle.dataset.selector);
            }
        });
    }

    GM_setValue(LANG_MODE_ID, nextLanguageMode);
    GM_setValue(THEME_MODE_ID, panelThemeMode);
    GM_setValue(LANG_ID, language);
    GM_setValue(SUPPRESS_ID, suppress);
    GM_setValue(TARGETS_ID, nextTargets);
    GM_setValue(CURRENCY_ORDER_ID, panelCurrencyOrder);
    GM_setValue(DISABLED_BASE_SELECTORS_ID, disabledBaseSelectors);

    languageMode = nextLanguageMode;
    userLanguage = language;
    suppressOriginal = suppress;
    targetCurrencies = nextTargets;
    targetCurrencyOrder = [...panelCurrencyOrder];

    userAddedSelectors = GM_getValue(USER_SELECTORS_ID, []);
    buildFinalSelectorString();
};

const openSettingsPanel = () => {
    let backdrop = document.getElementById(SETTINGS_BACKDROP_ID);
    let panel = document.getElementById(SETTINGS_PANEL_ID);

    if (!backdrop || !panel) {
        backdrop = document.createElement("div");
        backdrop.id = SETTINGS_BACKDROP_ID;
        backdrop.className = "scc-settings-backdrop";

        const baseSelectorsSection = DEBUG_MODE ? `
                    <h4>
                        <span id="scc-base-selectors-title">Base selector toggles (default ON)</span>
                    </h4>
                    <div id="scc-base-selectors-list" class="scc-scroll-list"></div>
        ` : "";

        panel = document.createElement("aside");
        panel.id = SETTINGS_PANEL_ID;
        panel.className = "scc-settings-panel";
        panel.innerHTML = `
            <div class="scc-panel-header">
                <h2 class="scc-panel-title" id="scc-panel-title">Steam Currency Converter - Settings</h2>
                <div class="scc-header-actions">
                    <select id="scc-lang-mode" class="scc-input scc-lang-compact" aria-label="Language">
                        <option value="auto">Auto</option>
                        <option value="ru">RU</option>
                        <option value="en">EN</option>
                    </select>
                    <button type="button" class="scc-theme-toggle-btn" id="scc-theme-toggle-btn" title="Panel theme">A</button>
                    <button type="button" class="scc-close-btn" id="scc-close-panel">✖</button>
                </div>
            </div>
            <div class="scc-panel-content">
                <section class="scc-section">
                    <h3 class="scc-title-row">
                        <span id="scc-actions-title">Actions</span>
                        <span class="scc-help" id="scc-actions-help" data-tooltip="Actions help">?</span>
                    </h3>
                    <div class="scc-actions-grid">
                        ${DEBUG_MODE ? '<button type="button" class="scc-btn scc-btn-secondary" id="scc-run-now">Run manually</button>' : ''}
                        <button type="button" class="scc-btn scc-btn-secondary" id="scc-add-selector-click">Add selector by click</button>
                        <button type="button" class="scc-btn scc-btn-secondary" id="scc-export-settings">Export</button>
                        <button type="button" class="scc-btn scc-btn-secondary" id="scc-import-settings-btn">Import</button>
                        <input type="file" id="scc-import-file" accept="application/json" style="display:none">
                    </div>
                    <div id="scc-action-status" class="scc-status"></div>
                </section>

                <section class="scc-section">
                    <h3 class="scc-title-row">
                        <span id="scc-config-title">Configuration</span>
                        <span class="scc-help" id="scc-config-help" data-tooltip="Configuration help">?</span>
                    </h3>

                    <label class="scc-field scc-inline">
                        <span id="scc-hide-original-label">Hide original price</span>
                        <label class="scc-switch">
                            <input type="checkbox" id="scc-suppress-original" checked>
                            <span class="scc-slider"></span>
                        </label>
                    </label>

                    <h4 class="scc-title-row">
                        <span id="scc-target-currencies-title">Target currencies</span>
                        <span class="scc-help" id="scc-targets-help" data-tooltip="Target currencies help">?</span>
                    </h4>
                    <div class="scc-currency-head">
                        <span id="scc-currency-col-label">Currency</span>
                        <span id="scc-fee-col-label">Fee</span>
                        <span></span>
                    </div>
                    <div id="scc-currency-list" class="scc-scroll-list"></div>

                    <h4 class="scc-title-row">
                        <span id="scc-currency-order-title">Display order</span>
                        <span class="scc-help" id="scc-order-help" data-tooltip="Order help">?</span>
                    </h4>
                    <div id="scc-currency-order-list" class="scc-scroll-list scc-order-list"></div>

                    <h4 class="scc-title-row">
                        <span id="scc-custom-selectors-title">Custom selectors</span>
                        <span class="scc-help" id="scc-selectors-help" data-tooltip="Custom selectors help">?</span>
                    </h4>
                    <div class="scc-add-row">
                        <input type="text" id="scc-new-selector" class="scc-input" placeholder=".wishlist_row .price">
                        <button type="button" class="scc-btn scc-btn-secondary" id="scc-add-selector-manual">Add</button>
                    </div>
                    <div id="scc-custom-selectors-list" class="scc-scroll-list"></div>
                    ${baseSelectorsSection}
                </section>

                <section class="scc-save-area">
                    <span class="scc-help" id="scc-save-help" data-tooltip="Save help">?</span>
                    <button type="button" class="scc-btn scc-btn-primary" id="scc-save-settings">Save</button>
                    <div id="scc-save-status" class="scc-status"></div>
                </section>
            </div>
        `;

        document.body.append(backdrop, panel);

        const closePanel = () => {
            backdrop.classList.remove("is-open");
            panel.classList.remove("is-open");
        };

        backdrop.addEventListener("click", closePanel);
        panel.querySelector("#scc-close-panel").addEventListener("click", closePanel);

        const setActionStatus = (message) => {
            panel.querySelector("#scc-action-status").textContent = message;
        };
        const setSaveStatus = (message) => {
            panel.querySelector("#scc-save-status").textContent = message;
        };
        attachHelpTooltipHandlers(panel);

        const exportBtn = panel.querySelector("#scc-export-settings");
        const importBtn = panel.querySelector("#scc-import-settings-btn");
        const importFile = panel.querySelector("#scc-import-file");
        const langModeSelect = panel.querySelector("#scc-lang-mode");
        const themeToggleBtn = panel.querySelector("#scc-theme-toggle-btn");
        const addManualSelectorBtn = panel.querySelector("#scc-add-selector-manual");
        const runNowBtn = panel.querySelector("#scc-run-now");
        const addByClickBtn = panel.querySelector("#scc-add-selector-click");
        const saveBtn = panel.querySelector("#scc-save-settings");

        document.addEventListener("keydown", event => {
            if (event.key !== "Escape") return;
            if (!panel.classList.contains("is-open")) return;
            closePanel();
        });

        const syncThemeInAutoMode = () => {
            if (!panel.classList.contains("is-open")) return;
            if (panelThemeMode !== "auto") return;
            const panelLang = resolveLanguage(langModeSelect.value);
            applyPanelTheme(panel, panelThemeMode);
            updateThemeToggleButton(panel, panelThemeMode, panelLang);
        };
        const themeObserver = new MutationObserver(syncThemeInAutoMode);
        themeObserver.observe(document.documentElement, {
            attributes: true,
            attributeFilter: ["class", "style", "data-theme", "theme", "color-theme"]
        });
        if (document.body) {
            themeObserver.observe(document.body, {
                attributes: true,
                attributeFilter: ["class", "style", "data-theme", "theme", "color-theme"]
            });
        }

        langModeSelect.addEventListener("change", () => {
            const previewLang = resolveLanguage(langModeSelect.value);
            applyPanelLocalization(panel, previewLang);
            renderSelectorList(panel.querySelector("#scc-custom-selectors-list"), GM_getValue(USER_SELECTORS_ID, []), previewLang);
            const orderListEl = panel.querySelector("#scc-currency-order-list");
            if (orderListEl) renderCurrencyOrder(orderListEl, panelCurrencyOrder, previewLang);
        });
        themeToggleBtn.addEventListener("click", () => {
            panelThemeMode = getNextThemeMode(panelThemeMode);
            const panelLang = resolveLanguage(langModeSelect.value);
            applyPanelTheme(panel, panelThemeMode);
            updateThemeToggleButton(panel, panelThemeMode, panelLang);
        });

        // Синхронизация порядка при включении/выключении валют
        panel.addEventListener("change", (e) => {
            const toggle = e.target.closest(".scc-currency-enabled");
            if (!toggle) return;
            const code = toggle.dataset.code;
            if (toggle.checked) {
                if (!panelCurrencyOrder.includes(code)) panelCurrencyOrder.push(code);
            } else {
                panelCurrencyOrder = panelCurrencyOrder.filter(c => c !== code);
            }
            const orderListEl = panel.querySelector("#scc-currency-order-list");
            if (orderListEl) renderCurrencyOrder(orderListEl, panelCurrencyOrder, resolveLanguage(langModeSelect.value));
        });

        // Кнопки ▲/▼ для изменения порядка
        panel.addEventListener("click", (e) => {
            const orderBtn = e.target.closest(".scc-order-btn");
            if (orderBtn) {
                const code = orderBtn.dataset.code;
                const dir = orderBtn.dataset.dir;
                const idx = panelCurrencyOrder.indexOf(code);
                if (idx === -1) return;
                if (dir === "up" && idx > 0) {
                    [panelCurrencyOrder[idx - 1], panelCurrencyOrder[idx]] =
                        [panelCurrencyOrder[idx], panelCurrencyOrder[idx - 1]];
                } else if (dir === "down" && idx < panelCurrencyOrder.length - 1) {
                    [panelCurrencyOrder[idx + 1], panelCurrencyOrder[idx]] =
                        [panelCurrencyOrder[idx], panelCurrencyOrder[idx + 1]];
                }
                const orderListEl = panel.querySelector("#scc-currency-order-list");
                if (orderListEl) renderCurrencyOrder(orderListEl, panelCurrencyOrder, resolveLanguage(langModeSelect.value));
                return;
            }

            const removeSelectorBtn = e.target.closest("[data-remove-selector]");
            if (!removeSelectorBtn) return;
            const panelLang = resolveLanguage(langModeSelect.value);
            const selectorToRemove = removeSelectorBtn.dataset.removeSelector;
            const currentSelectors = GM_getValue(USER_SELECTORS_ID, []);
            const nextSelectors = currentSelectors.filter(sel => sel !== selectorToRemove);
            GM_setValue(USER_SELECTORS_ID, nextSelectors);
            userAddedSelectors = nextSelectors;
            renderSelectorList(panel.querySelector("#scc-custom-selectors-list"), nextSelectors, panelLang);
            setActionStatus(getSettingsText("removedSelector", selectorToRemove));
        });

        if (runNowBtn) {
            runNowBtn.addEventListener("click", async () => {
                setActionStatus(getSettingsText("runningManual"));
                conversionLogicStarted = false;
                await startConversionLogic();
                processPrices(document.querySelectorAll(finalPriceSelectorString));
                setActionStatus(getSettingsText("manualDone"));
            });
        }

        addByClickBtn.addEventListener("click", () => {
            findAndAddSelector({
                onStatus: setActionStatus,
                onDone: () => {
                    const panelLang = resolveLanguage(langModeSelect.value);
                    const selectors = GM_getValue(USER_SELECTORS_ID, []);
                    renderSelectorList(panel.querySelector("#scc-custom-selectors-list"), selectors, panelLang);
                }
            });
        });

        addManualSelectorBtn.addEventListener("click", () => {
            const input = panel.querySelector("#scc-new-selector");
            const value = (input.value || "").trim();
            if (!value) return;
            const currentSelectors = GM_getValue(USER_SELECTORS_ID, []);
            if (currentSelectors.includes(value)) {
                setActionStatus(getSettingsText("selectorExists"));
                return;
            }
            currentSelectors.push(value);
            GM_setValue(USER_SELECTORS_ID, currentSelectors);
            userAddedSelectors = currentSelectors;
            renderSelectorList(panel.querySelector("#scc-custom-selectors-list"), currentSelectors, resolveLanguage(langModeSelect.value));
            input.value = "";
            setActionStatus(getSettingsText("addedSelector", value));
        });

        exportBtn.addEventListener("click", () => {
            const data = {
                targets: GM_getValue(TARGETS_ID, {}),
                suppressOriginal: GM_getValue(SUPPRESS_ID, false),
                language: GM_getValue(LANG_ID, "ru"),
                languageMode: GM_getValue(LANG_MODE_ID, "auto"),
                themeMode: GM_getValue(THEME_MODE_ID, "auto"),
                userSelectors: GM_getValue(USER_SELECTORS_ID, []),
                disabledBaseSelectors: GM_getValue(DISABLED_BASE_SELECTORS_ID, []),
                currencyOrder: GM_getValue(CURRENCY_ORDER_ID, [])
            };
            const blob = new Blob([JSON.stringify(data, null, 2)], { type: "application/json" });
            const link = document.createElement("a");
            link.href = URL.createObjectURL(blob);
            link.download = "steam-currency-converter-settings.json";
            link.click();
            URL.revokeObjectURL(link.href);
            setActionStatus(getSettingsText("exported"));
        });

        importBtn.addEventListener("click", () => importFile.click());
        importFile.addEventListener("change", () => {
            const file = importFile.files?.[0];
            if (!file) return;
            const reader = new FileReader();
            reader.onload = () => {
                try {
                    const parsed = JSON.parse(String(reader.result || "{}"));
                    if (parsed.targets && typeof parsed.targets === "object") GM_setValue(TARGETS_ID, parsed.targets);
                    if (typeof parsed.suppressOriginal === "boolean") GM_setValue(SUPPRESS_ID, parsed.suppressOriginal);
                    if (parsed.languageMode === "auto" || parsed.languageMode === "ru" || parsed.languageMode === "en") {
                        GM_setValue(LANG_MODE_ID, parsed.languageMode);
                    } else if (parsed.language === "ru" || parsed.language === "en") {
                        GM_setValue(LANG_MODE_ID, parsed.language);
                    }
                    if (parsed.themeMode === "auto" || parsed.themeMode === "light" || parsed.themeMode === "dark") {
                        GM_setValue(THEME_MODE_ID, parsed.themeMode);
                    }
                    if (parsed.language === "ru" || parsed.language === "en") GM_setValue(LANG_ID, parsed.language);
                    if (Array.isArray(parsed.userSelectors)) GM_setValue(USER_SELECTORS_ID, parsed.userSelectors);
                    if (Array.isArray(parsed.disabledBaseSelectors)) GM_setValue(DISABLED_BASE_SELECTORS_ID, parsed.disabledBaseSelectors);
                    if (Array.isArray(parsed.currencyOrder)) GM_setValue(CURRENCY_ORDER_ID, parsed.currencyOrder);
                    loadLanguagePreference();
                    loadThemePreference();
                    loadSettingsToPanel(panel);
                    setActionStatus(getSettingsText("imported"));
                } catch (err) {
                    setActionStatus(getSettingsText("importFailed", String(err)));
                }
            };
            reader.readAsText(file);
        });

        saveBtn.addEventListener("click", () => {
            saveSettingsFromPanel(panel);
            setSaveStatus(getSettingsText("savedReloading"));
            setTimeout(() => location.reload(), 600);
        });
    }

    loadSettingsToPanel(panel);
    requestAnimationFrame(() => {
        requestAnimationFrame(() => {
            backdrop.classList.add("is-open");
            panel.classList.add("is-open");
        });
    });
};

const configureMenus = () => {
    GM_registerMenuCommand(getSettingsText("menuSettings"), openSettingsPanel);
};
const applyStyles = () => {
    const styles = `
    :root {
      --scc-panel-bg-light: #f8fafc;
      --scc-panel-text-light: #0f172a;
      --scc-panel-border-light: #cbd5e1;
      --scc-input-bg-light: #ffffff;
      --scc-input-text-light: #0f172a;
      --scc-input-border-light: #94a3b8;
      --scc-btn-primary-light: #1d4ed8;
      --scc-btn-primary-hover-light: #1e40af;
      --scc-btn-secondary-light: #334155;
      --scc-btn-secondary-hover-light: #1e293b;

      --scc-panel-bg-dark: #0f172a;
      --scc-panel-text-dark: #e2e8f0;
      --scc-panel-border-dark: #334155;
      --scc-input-bg-dark: #111827;
      --scc-input-text-dark: #f1f5f9;
      --scc-input-border-dark: #475569;
      --scc-btn-primary-dark: #2563eb;
      --scc-btn-primary-hover-dark: #1d4ed8;
      --scc-btn-secondary-dark: #475569;
      --scc-btn-secondary-hover-dark: #334155;
    }

    .scc-settings-backdrop {
      position: fixed;
      inset: 0;
      background: rgba(2, 6, 23, 0.45);
      opacity: 0;
      pointer-events: none;
      transition: opacity .2s ease;
      z-index: 99998;
    }
    .scc-settings-backdrop.is-open {
      opacity: 1;
      pointer-events: auto;
    }
    .scc-settings-panel {
      position: fixed;
      top: 12px;
      right: 0;
      transform: translateX(calc(100% + 24px));
      width: min(440px, 94vw);
      max-height: 90vh;
      overflow-y: auto;
      overflow-x: hidden;
      border-radius: 12px 0 0 12px;
      border: 1px solid var(--scc-panel-border);
      background: var(--scc-panel-bg);
      color: var(--scc-panel-text);
      box-shadow: var(--scc-panel-shadow, 0 10px 28px rgba(0,0,0,0.22));
      z-index: 99999;
      transition: transform .35s ease;
      padding: 16px;
      font-family: Arial, sans-serif;
      line-height: 1.35;
    }
    .scc-settings-panel.is-open { transform: translateX(0); }

    .scc-settings-panel,
    .scc-settings-panel.scc-theme-light {
      --scc-panel-bg: var(--scc-panel-bg-light);
      --scc-panel-text: var(--scc-panel-text-light);
      --scc-panel-border: var(--scc-panel-border-light);
      --scc-input-bg: var(--scc-input-bg-light);
      --scc-input-text: var(--scc-input-text-light);
      --scc-input-border: var(--scc-input-border-light);
      --scc-btn-primary: var(--scc-btn-primary-light);
      --scc-btn-primary-hover: var(--scc-btn-primary-hover-light);
      --scc-btn-secondary: var(--scc-btn-secondary-light);
      --scc-btn-secondary-hover: var(--scc-btn-secondary-hover-light);
      --scc-panel-shadow: 0 10px 28px rgba(0,0,0,0.22);
    }
    .scc-settings-panel.scc-theme-dark {
      --scc-panel-bg: var(--scc-panel-bg-dark);
      --scc-panel-text: var(--scc-panel-text-dark);
      --scc-panel-border: var(--scc-panel-border-dark);
      --scc-input-bg: var(--scc-input-bg-dark);
      --scc-input-text: var(--scc-input-text-dark);
      --scc-input-border: var(--scc-input-border-dark);
      --scc-btn-primary: var(--scc-btn-primary-dark);
      --scc-btn-primary-hover: var(--scc-btn-primary-hover-dark);
      --scc-btn-secondary: var(--scc-btn-secondary-dark);
      --scc-btn-secondary-hover: var(--scc-btn-secondary-hover-dark);
      --scc-panel-shadow: 0 10px 28px rgba(0,0,0,0.65);
    }

    .scc-panel-header {
      display: flex;
      justify-content: space-between;
      align-items: center;
      margin-bottom: 10px;
    }
    .scc-header-actions {
      display: flex;
      align-items: center;
      gap: 8px;
    }
    .scc-lang-compact {
      width: 74px;
      min-width: 74px;
      height: 34px;
      padding: 4px 8px;
      font-size: 12px;
    }
    .scc-panel-title {
      font-size: 18px;
      margin: 0;
      color: var(--scc-panel-text) !important;
      text-shadow: none !important;
      opacity: 1 !important;
    }
    .scc-theme-toggle-btn {
      border: 1px solid var(--scc-input-border);
      background: var(--scc-btn-secondary);
      color: #fff !important;
      border-radius: 10px;
      width: 34px;
      height: 34px;
      cursor: pointer;
      font-weight: 700;
      line-height: 1;
      display: inline-flex;
      align-items: center;
      justify-content: center;
    }
    .scc-close-btn {
      border: 1px solid var(--scc-input-border);
      background: var(--scc-input-bg);
      color: var(--scc-input-text);
      border-radius: 10px;
      width: 34px;
      height: 34px;
      cursor: pointer;
    }
    .scc-panel-content {
      display: flex;
      flex-direction: column;
      gap: 12px;
      padding-bottom: 72px;
    }
    .scc-section {
      border: 1px solid var(--scc-panel-border);
      border-radius: 12px;
      padding: 10px;
    }
    .scc-section h3, .scc-section h4 {
      margin: 0 0 8px 0;
      color: var(--scc-panel-text) !important;
      text-shadow: none !important;
      opacity: 1 !important;
    }
    .scc-title-row {
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: 8px;
    }
    .scc-help {
      display: inline-flex;
      align-items: center;
      justify-content: center;
      width: 16px;
      height: 16px;
      border-radius: 50%;
      border: 1px solid var(--scc-input-border);
      background: var(--scc-input-bg);
      color: var(--scc-input-text) !important;
      font-size: 11px;
      cursor: help;
      position: relative;
      flex: 0 0 auto;
    }
    .scc-help-tooltip {
      position: fixed;
      display: none;
      min-width: 160px;
      max-width: 260px;
      background: var(--scc-input-bg-light);
      color: var(--scc-input-text-light);
      border: 1px solid var(--scc-input-border-light);
      border-radius: 8px;
      padding: 6px 8px;
      font-size: 11px;
      line-height: 1.3;
      white-space: normal;
      z-index: 100001;
      box-shadow: 0 8px 20px rgba(0, 0, 0, .3);
      pointer-events: none;
    }
    .scc-help-tooltip.is-visible {
      display: block;
    }
    .scc-help-tooltip.is-above {
      transform-origin: bottom right;
    }
    @media (max-width: 560px) {
      .scc-help-tooltip {
        min-width: 140px;
        max-width: calc(100vw - 16px);
        font-size: 10px;
      }
    }
    body.night-theme .scc-help-tooltip,
    html.night-theme .scc-help-tooltip,
    .night-theme .scc-help-tooltip {
      background: var(--scc-input-bg-dark);
      color: var(--scc-input-text-dark);
      border-color: var(--scc-input-border-dark);
    }
    .scc-settings-panel .scc-field span,
    .scc-settings-panel .scc-row-left,
    .scc-settings-panel .scc-selector-text,
    .scc-settings-panel .scc-empty,
    .scc-settings-panel .scc-status {
      color: var(--scc-panel-text) !important;
      text-shadow: none !important;
      opacity: 1 !important;
    }
    .scc-actions-grid {
      display: grid;
      grid-template-columns: 1fr 1fr;
      gap: 8px;
    }
    .scc-field {
      display: flex;
      flex-direction: column;
      gap: 6px;
      margin-bottom: 8px;
    }
    .scc-field.scc-inline {
      flex-direction: row;
      align-items: center;
      justify-content: space-between;
    }
    .scc-input {
      width: 100%;
      box-sizing: border-box;
      border: 1px solid var(--scc-input-border) !important;
      background: var(--scc-input-bg) !important;
      color: var(--scc-input-text) !important;
      border-radius: 10px;
      padding: 8px 10px;
      font-size: 13px;
    }
    .scc-scroll-list {
      max-height: 180px;
      overflow-y: auto;
      overflow-x: hidden;
      border: 1px solid var(--scc-panel-border);
      border-radius: 10px;
      padding: 6px;
      margin-bottom: 8px;
    }
    .scc-currency-head {
      display: grid;
      grid-template-columns: 1fr 88px 44px;
      gap: 8px;
      padding: 0 6px 6px 6px;
      margin-bottom: 4px;
      border-bottom: 1px solid var(--scc-panel-border);
      color: var(--scc-panel-text) !important;
      font-size: 11px;
      font-weight: 700;
      letter-spacing: .2px;
    }
    .scc-currency-head span:nth-child(2) {
      text-align: center;
    }
    .scc-list-row {
      display: flex;
      justify-content: space-between;
      gap: 8px;
      align-items: center;
      padding: 6px 4px;
      border-bottom: 1px solid var(--scc-panel-border);
    }
    .scc-list-row:last-child { border-bottom: none; }
    .scc-row-left {
      font-size: 12px;
      white-space: nowrap;
    }
    .scc-row-controls {
      display: flex;
      align-items: center;
      gap: 8px;
      justify-content: flex-end;
    }
    .scc-fee-input {
      width: 80px;
      padding: 6px 8px;
    }
    .scc-selector-text {
      font-size: 12px;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
      max-width: 285px;
    }
    .scc-empty {
      font-size: 12px;
      opacity: .8;
      padding: 6px;
    }
    .scc-order-list {
      max-height: 120px;
    }
    .scc-order-btn {
      padding: 2px 8px !important;
      font-size: 10px !important;
      min-width: 28px;
      line-height: 1;
    }
    .scc-order-btn:disabled {
      opacity: .3;
      cursor: default;
    }
    .scc-add-row {
      display: grid;
      grid-template-columns: 1fr auto;
      gap: 8px;
      margin-bottom: 8px;
    }
    .scc-btn {
      border: none;
      color: #fff !important;
      border-radius: 10px;
      padding: 8px 10px;
      font-size: 12px;
      cursor: pointer;
      transition: background .15s ease;
    }
    .scc-btn-primary { background: var(--scc-btn-primary); }
    .scc-btn-primary:hover { background: var(--scc-btn-primary-hover); }
    .scc-btn-secondary { background: var(--scc-btn-secondary); }
    .scc-btn-secondary:hover { background: var(--scc-btn-secondary-hover); }
    .scc-theme-toggle-btn:hover { background: var(--scc-btn-secondary-hover); }
    .scc-status {
      margin-top: 8px;
      min-height: 18px;
      font-size: 12px;
      opacity: .95;
    }
    .scc-save-area {
      position: sticky;
      bottom: 8px;
      display: flex;
      align-items: center;
      gap: 8px;
      z-index: 3;
      background: transparent;
      border: none;
      padding: 0;
      margin-top: 2px;
    }

    .scc-switch {
      position: relative;
      display: inline-block;
      width: 44px;
      height: 24px;
    }
    .scc-switch input {
      opacity: 0;
      width: 0;
      height: 0;
    }
    .scc-slider {
      position: absolute;
      cursor: pointer;
      inset: 0;
      background: #64748b;
      transition: .2s;
      border-radius: 999px;
    }
    .scc-slider:before {
      position: absolute;
      content: "";
      height: 18px;
      width: 18px;
      left: 3px;
      bottom: 3px;
      background: #fff;
      transition: .2s;
      border-radius: 50%;
    }
    .scc-switch input:checked + .scc-slider {
      background: #22c55e;
    }
    .scc-switch input:checked + .scc-slider:before {
      transform: translateX(20px);
    }

    .tab_item_discount { width: 160px !important; }
    .tab_item_discount .discount_prices { width: 100% !important; }
    .tab_item_discount .discount_final_price { padding: 0 !important; }
    .home_marketing_message.small .discount_block { height: auto !important; }
    .discount_block_inline { white-space: nowrap !important; }
    .curator #RecommendationsRows .store_capsule.price_inline .discount_block { min-width: 200px !important; }
    .market_listing_their_price { min-width: 130px !important; }
  `;
    GM_addStyle(styles);
};
const getCurrencyById = id => CURRENCY_BY_ID.get(id);
const getCurrencyByCode = code => CURRENCY_BY_CODE.get(code);
const getCurrencySign = code => {
    const currency = CURRENCY_BY_CODE.get(code);
    return currency ? (currency.sign || code) : code;
};

// Вынесено в отдельную функцию
const startConversionLogic = async () => {
    if (conversionLogicStarted) {
        log("Conversion logic already started, skipping.");
        return;
    }
    conversionLogicStarted = true;
    log("Starting conversion logic...");

    if (Object.keys(targetCurrencies).length === 0) {
        warn("No target currencies set. Exiting conversion logic.");
        return;
    }

    await loadRates();
    log("Exchange rates loaded.");

    if (!activeCurrencyCode) {
         error("Conversion logic started but activeCurrencyCode is missing!");
         return;
    }

    baseRateFromRub = exchangeData.rub[activeCurrencyCode.toLowerCase()];
    if (activeCurrencyCode.toLowerCase() === 'rub') {
        baseRateFromRub = 1;
    }

    log(`Base Rate (1 RUB to ${activeCurrencyCode}): ${baseRateFromRub}`);

    if (!exchangeData || !baseRateFromRub) {
        error(`FAILED to find base rate for ${activeCurrencyCode}. Exiting.`);
        return;
    }

    // Первоначальный прогон
    processPrices(document.querySelectorAll(finalPriceSelectorString));

    // Дополнительные проверки для динамического контента
    for (const delay of [2000, 5000, 10000]) {
        setTimeout(() => {
            log(`Running delayed scan (${delay / 1000}s)...`);
            processPrices(document.querySelectorAll(finalPriceSelectorString));
        }, delay);
    }
}


// Core initialization
const bootstrap = async () => {
    "use strict";
    log("Bootstrapping script...");
    // Restore persistent state
    targetCurrencies = GM_getValue(TARGETS_ID, {});
    targetCurrencyOrder = GM_getValue(CURRENCY_ORDER_ID, []);
    suppressOriginal = GM_getValue(SUPPRESS_ID, false);
    loadLanguagePreference();
    loadThemePreference();
    userAddedSelectors = GM_getValue(USER_SELECTORS_ID, []);

    // Собираем финальную строку селекторов
    buildFinalSelectorString();
    log("Final selector string:", finalPriceSelectorString);

    // Определение валюты (Методы 1, 2, 3 и безопасный 4)
    identifyActiveCurrency();
    log(`Active Currency: ${activeCurrencyCode} (Sign: ${activeCurrencySign})`);

    // Настраиваем меню и стили в любом случае
    configureMenus();
    applyStyles();

    if (!activeCurrencyCode) {
        warn("No currency found on initial load. Initializing watcher for dynamic detection...");
        initializeWatcher(); // Запускаем наблюдатель, который будет *ждать* появления валюты
        return;
    }

    // Валюта найдена сразу, запускаем основную логику
    await startConversionLogic();
    initializeWatcher(); // Запускаем наблюдатель для отслеживания *будущих* изменений
};

// Start on page load
window.addEventListener("load", bootstrap);