Greasy Fork is available in English.

Remanga

Добавлена новая функция "⚔️ Авто-бои".

Tendrás que instalar una extensión para tu navegador como Tampermonkey, Greasemonkey o Violentmonkey si quieres utilizar este script.

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

Tendrás que instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Userscripts para instalar este script.

Tendrás que instalar una extensión como Tampermonkey antes de poder instalar este script.

Necesitarás instalar una extensión para administrar scripts de usuario si quieres instalar este script.

(Ya tengo un administrador de scripts de usuario, déjame instalarlo)

Advertisement:

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

(Ya tengo un administrador de estilos de usuario, déjame instalarlo)

Advertisement:

// ==UserScript==
// @name         Remanga
// @namespace    http://tampermonkey.net/
// @version      10.0.0
// @description  Добавлена новая функция "⚔️ Авто-бои".
// @author       You
// @license      MIT
// @match        https://remanga.org/*
// @match        https://xn--80aaig9ahr.xn--c1avg/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=remanga.org
// @connect      xn--80aaig9ahr.xn--c1avg
// @connect      api.xn--80aaig9ahr.xn--c1avg
// @connect      remanga.org
// @connect      api.remanga.org
// @connect      update.greasyfork.org
// @grant        GM_xmlhttpRequest
// @grant        GM_registerMenuCommand
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_deleteValue
// @run-at       document-start
// ==/UserScript==

(function () {
    'use strict';

    const initParams = new URLSearchParams(location.search);
    if (initParams.get('fix_id')) {
        document.documentElement.classList.add('rem-checking');
    }

    function storageGet(key) {
        if (typeof GM_getValue !== 'undefined') {
            return GM_getValue(key);
        }
        return localStorage.getItem(key);
    }

    function storageSet(key, value) {
        if (typeof GM_setValue !== 'undefined') {
            try {
                GM_setValue(key, value);
                return;
            } catch (e) {
                console.error("GM_setValue failed", e);
            }
        }
        try {
            localStorage.setItem(key, value);
        } catch (e) {
            console.error("localStorage.setItem failed", e);
        }
    }

    function storageRemove(key) {
        if (typeof GM_deleteValue !== 'undefined') {
            GM_deleteValue(key);
            return;
        }
        localStorage.removeItem(key);
    }

    const IS_MIRROR = location.hostname === 'xn--80aaig9ahr.xn--c1avg';
    const SITE_DOMAIN = IS_MIRROR ? 'https://xn--80aaig9ahr.xn--c1avg' : 'https://remanga.org';
    const API_DOMAIN = IS_MIRROR ? 'https://api.xn--80aaig9ahr.xn--c1avg' : 'https://api.remanga.org';

    const interceptedBookmarks = new Map();
    const fetchedKeys = new Set();
    let interceptedAuthToken = '';
    let lastUrlPath = '';

    const originalFetch = window.fetch;
    window.fetch = async function (...args) {
        try {
            if (args[1] && args[1].headers) {
                const headers = args[1].headers;
                let authVal = '';
                if (headers instanceof Headers) {
                    authVal = headers.get('Authorization') || headers.get('authorization');
                } else if (typeof headers === 'object') {
                    authVal = headers['Authorization'] || headers['authorization'];
                }
                if (authVal && authVal.startsWith('Bearer ')) {
                    interceptedAuthToken = authVal;
                    storageSet('rem_intercepted_auth_token', authVal);
                }
            }
            if (args[0] && typeof args[0] === 'object' && args[0].headers) {
                let authVal = '';
                if (args[0].headers instanceof Headers) {
                    authVal = args[0].headers.get('Authorization') || args[0].headers.get('authorization');
                } else if (typeof args[0].headers === 'object') {
                    authVal = args[0].headers['Authorization'] || args[0].headers['authorization'];
                }
                if (authVal && authVal.startsWith('Bearer ')) {
                    interceptedAuthToken = authVal;
                    storageSet('rem_intercepted_auth_token', authVal);
                }
            }
        } catch (e) { }

        const response = await originalFetch.apply(this, args);
        try {
            const url = typeof args[0] === 'string' ? args[0] : (args[0] && args[0].url);
            if (url && url.includes('/bookmarks')) {
                const urlObj = new URL(url, location.origin);
                if (urlObj.pathname.includes('/bookmarks')) {
                    const clone = response.clone();
                    clone.json().then(data => {
                        if (data && Array.isArray(data.results)) {
                            data.results.forEach(item => {
                                if (item && item.title) {
                                    const norm = (s) => String(s || '').toLowerCase().trim();
                                    if (item.title.dir) {
                                        interceptedBookmarks.set(norm(item.title.dir), item);
                                    }
                                    if (item.title.id) {
                                        interceptedBookmarks.set(norm(item.title.id), item);
                                    }
                                }
                            });
                        }
                    }).catch(() => { });
                }
            }
        } catch (e) { }
        return response;
    };

    const MY_ID_KEY = 'rem_user_id_v1';
    const MY_INV_KEY = 'rem_inventory_covers_v1';
    const MY_WISH_KEY = 'rem_wish_covers_v1';
    const STORAGE_FIX_KEY = 'rem_script_fix_active';

    const SETTINGS_KEYS = {
        onlineStatus: 'rem_set_online_status',
        ownBadge: 'rem_set_own_badge',

        profileStats: 'rem_set_profile_stats',
        fixMode: 'rem_set_fix_mode',
        customBgEnabled: 'rem_set_bg_enabled',
        customBgOpacity: 'rem_set_bg_opacity',
        customBgBlur: 'rem_set_bg_blur',
        customBgFit: 'rem_set_bg_fit',
        themeAccent: 'rem_set_theme_accent',
        themeButtonBg: 'rem_set_theme_btn_bg',
        themeNameColor: 'rem_set_theme_name_color',
        themeNameFont: 'rem_set_theme_name_font',
        themeMenuBg: 'rem_set_theme_menu_bg',
        themeSiteBg: 'rem_set_theme_site_bg',
        scanButton: 'rem_set_scan_btn',
        deckStats: 'rem_set_deck_stats',
        wishBadge: 'rem_set_wish_badge',
        cardSelection: 'rem_set_card_selection',
        bookmarkProgress: 'rem_set_bookmark_progress',
        tradeAnalysis: 'rem_set_trade_analysis',
        characterSlots: 'rem_set_char_slots',
        autoRaidEnabled: 'rem_set_auto_raid_enabled',
        autoRaidLocation: 'rem_set_auto_raid_location',
        autoPvpEnabled: 'rem_set_auto_pvp_enabled',
        audioKeepAliveEnabled: 'rem_set_audio_keep_alive',
    };
    const BG_URL_LS_KEY = 'rem_bg_url_data';

    const DEFAULTS = {
        onlineStatus: true,
        ownBadge: true,

        profileStats: true,
        fixMode: false,
        customBgEnabled: false,
        customBgOpacity: 80,
        customBgBlur: 0,
        customBgFit: 'cover',
        themeAccent: '',
        themeButtonBg: '',
        themeNameColor: '',
        themeNameFont: '',
        themeMenuBg: '',
        themeSiteBg: '',
        scanButton: true,
        deckStats: false,
        wishBadge: true,
        cardSelection: false,
        bookmarkProgress: true,
        tradeAnalysis: true,
        characterSlots: true,
        autoRaidEnabled: false,
        autoRaidLocation: 10,
        autoPvpEnabled: false,
        audioKeepAliveEnabled: false,
    };

    const THEME_PRESETS = [
        { name: 'По умолчанию', value: '' },
        { name: '🔵 Синий', value: '#3b82f6' },
        { name: '🟣 Фиолетовый', value: '#a855f7' },
        { name: '🟢 Зелёный', value: '#22c55e' },
        { name: '🔴 Красный', value: '#ef4444' },
        { name: '🟠 Оранжевый', value: '#f97316' },
        { name: '🌸 Розовый', value: '#ec4899' },
        { name: '🌊 Бирюзовый', value: '#06b6d4' },
        { name: '🌟 Золотой', value: '#eab308' },
    ];

    const cfg = {};
    function loadSettings() {
        for (const [k, dflt] of Object.entries(DEFAULTS)) {
            try {
                cfg[k] = (typeof GM_getValue !== 'undefined') ? GM_getValue(SETTINGS_KEYS[k], dflt) : JSON.parse(localStorage.getItem(SETTINGS_KEYS[k]) ?? JSON.stringify(dflt));
            } catch (e) {
                cfg[k] = dflt;
            }
        }
        cfg.customBgUrl = localStorage.getItem(BG_URL_LS_KEY) || '';
    }
    function saveSetting(key, value) {
        cfg[key] = value;
        if (key === 'customBgUrl') {
            try { localStorage.setItem(BG_URL_LS_KEY, value); } catch (e) { console.warn('REM BG: localStorage переполнен'); }
        } else {
            try {
                if (typeof GM_setValue !== 'undefined') GM_setValue(SETTINGS_KEYS[key], value);
                else localStorage.setItem(SETTINGS_KEYS[key], JSON.stringify(value));
            } catch (e) { }
        }
        window.dispatchEvent(new CustomEvent('rem_settings_changed', { detail: { key, value } }));
    }
    loadSettings();

    async function checkForUpdates() {
        const URL = 'https://update.greasyfork.org/scripts/570944/Remanga.meta.js';
        const current = (typeof GM_info !== 'undefined') ? GM_info.script.version : '7.2.5';
        Manager.showStatus('Проверка обновлений...', true);
        try {
            let text = '';
            let ok = false;
            if (typeof GM_xmlhttpRequest !== 'undefined') {
                try {
                    const resp = await new Promise((resolve, reject) => {
                        GM_xmlhttpRequest({
                            method: 'GET',
                            url: URL + '?t=' + Date.now(),
                            onload: r => {
                                if (r.status >= 200 && r.status < 300) resolve(r);
                                else reject(new Error('Status ' + r.status));
                            },
                            onerror: reject
                        });
                    });
                    text = resp.responseText;
                    ok = true;
                } catch (e) {
                    ok = false;
                }
            }
            if (!ok) {
                const res = await fetch(URL + '?t=' + Date.now());
                if (res.ok) {
                    text = await res.text();
                    ok = true;
                }
            }
            if (ok) {
                const m = text.match(/@version\s+([\d.]+)/);
                if (m) {
                    const latest = m[1];
                    if (latest !== current) {
                        if (confirm(`Доступна новая версия: ${latest} (у вас ${current})\n\nПерейти на страницу обновления?`)) {
                            window.location.href = 'https://update.greasyfork.org/scripts/570944/Remanga.user.js';
                        }
                    } else { alert('У вас установлена актуальная версия.'); }
                }
            } else {
                throw new Error('Fetch failed');
            }
        } catch (e) { alert('Ошибка при проверке обновлений.'); }
        finally { Manager.showStatus('', false); }
    }

    let activeObserver = null;

    const ExState = {
        scanning: false,
        scanDone: 0,
        scanTotal: 0,
        results: [],
        currentRank: 'ALL',
        page: 0,
        perPage: 8,
        lastSource: '',
        searchQuery: '',
        searchUser: '',
        filterMatchOnly: true,
        filterWant: false,
        filterOffer: false,
        filterOwn: false,
    };
    const SCAN_LIST_KEY = 'rem_scan_list_v1';
    function getScanList() {
        try { return JSON.parse(localStorage.getItem(SCAN_LIST_KEY) || '[]'); } catch { return []; }
    }
    function saveScanList(list) {
        localStorage.setItem(SCAN_LIST_KEY, JSON.stringify(list));
    }
    function addToScanList(id, username) {
        const list = getScanList();
        id = String(id);
        if (list.find(u => String(u.id) === id)) return;
        list.push({ id, username: username || `User_${id}` });
        saveScanList(list);
    }
    function removeFromScanList(id) {
        const list = getScanList().filter(u => String(u.id) !== String(id));
        saveScanList(list);
    }
    function isInScanList(id) {
        return getScanList().some(u => String(u.id) === String(id));
    }
    const SCAN_HISTORY_KEY = 'rem_scan_history_v2';
    function getHistory() {
        let hist = localStorage.getItem(SCAN_HISTORY_KEY);
        if (!hist) {
            const old = localStorage.getItem('rem_scan_history_v1');
            if (old) {
                localStorage.setItem(SCAN_HISTORY_KEY, old);
                hist = old;
            }
        }
        try { return JSON.parse(hist || '[]'); } catch { return []; }
    }
    function saveToHistory(name, results, link = "") {
        let hist = getHistory();
        const exIdx = hist.findIndex(h => h.name === name || (link && h.link === link));
        let id;
        if (exIdx > -1) {
            id = hist[exIdx].id;
            hist[exIdx].time = Date.now();
            hist[exIdx].count = results.length;
            if (link) hist[exIdx].link = link;
            const it = hist.splice(exIdx, 1)[0];
            hist.unshift(it);
        } else {
            id = Date.now().toString(36) + Math.random().toString(36).slice(2, 6);
            hist.unshift({ id, name, link, time: Date.now(), count: results.length });
        }
        if (hist.length > 20) {
            const old = hist.pop();
            try { if (typeof GM_deleteValue !== 'undefined') GM_deleteValue('rem_scan_data_' + old.id); } catch (e) { }
            localStorage.removeItem('rem_scan_data_' + old.id);
        }
        try {
            localStorage.setItem(SCAN_HISTORY_KEY, JSON.stringify(hist));
            if (typeof GM_setValue !== 'undefined') GM_setValue('rem_scan_data_' + id, JSON.stringify(results));
            else localStorage.setItem('rem_scan_data_' + id, JSON.stringify(results));
        } catch (e) {
            console.error('History save error:', e);
            try { localStorage.setItem('rem_scan_data_' + id, JSON.stringify(results)); } catch (e2) { }
        }
        return id;
    }
    function loadFromHistory(id) {
        try {
            let raw = null;
            if (typeof GM_getValue !== 'undefined') raw = GM_getValue('rem_scan_data_' + id);
            if (!raw) {
                raw = localStorage.getItem('rem_scan_data_' + id);
            }
            if (!raw) {
                console.warn('REM: Данные сканирования не найдены для', id);
                return null;
            }
            return JSON.parse(raw);
        } catch (e) {
            console.error('REM: Ошибка парсинга истории', e);
            return null;
        }
    }
    function deleteFromHistory(id) {
        const hist = getHistory().filter(h => h.id !== id);
        localStorage.setItem(SCAN_HISTORY_KEY, JSON.stringify(hist));
        try { if (typeof GM_deleteValue !== 'undefined') GM_deleteValue('rem_scan_data_' + id); } catch (e) { }
        localStorage.removeItem('rem_scan_data_' + id);
    }
    function captureVideoFrame(videoUrl, container, seekTime = 1) {
        const video = document.createElement('video');
        video.crossOrigin = 'anonymous';
        video.muted = true;
        video.preload = 'auto';
        video.src = videoUrl;
        video.addEventListener('loadeddata', () => {
            video.currentTime = Math.min(seekTime, video.duration || seekTime);
        });
        video.addEventListener('seeked', () => {
            try {
                const canvas = document.createElement('canvas');
                canvas.width = video.videoWidth || 120;
                canvas.height = video.videoHeight || 180;
                const ctx = canvas.getContext('2d');
                ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
                const img = document.createElement('img');
                img.src = canvas.toDataURL('image/webp', 0.8);
                img.style.cssText = 'width:100%;height:100%;object-fit:cover;';
                container.innerHTML = '';
                container.appendChild(img);
            } catch (e) { /* CORS */ }
            video.remove();
        });
        video.addEventListener('error', () => { video.remove(); });
    }
    function isVideoUrl(url) {
        if (!url) return false;
        return /\.(webm|mp4)([?#]|$)/i.test(url);
    }
    function renderCardMedia(fullImg, container) {
        if (!fullImg) return;
        if (isVideoUrl(fullImg)) {
            captureVideoFrame(fullImg, container);
        }
    }

    const RANK_ORDER = ['rank_f', 'rank_e', 'rank_d', 'rank_c', 'rank_b', 'rank_a', 'rank_s', 'rank_re', 'rank_ev'];
    const RANK_MAP = {
        'rank_f': { name: 'F', color: '#9ca3af' },
        'rank_e': { name: 'E', color: '#9ca3af' },
        'rank_d': { name: 'D', color: '#ffffff' },
        'rank_c': { name: 'C', color: '#4ade80' },
        'rank_b': { name: 'B', color: '#60a5fa' },
        'rank_a': { name: 'A', color: '#c084fc' },
        'rank_s': { name: 'S', color: '#fbbf24' },
        'rank_re': { name: 'RE', color: '#f59e0b' },
        'rank_ev': { name: 'EV', color: '#e879f9' },
    };

    const STYLES = `
        .rem-online-dot {
            position:absolute!important;top:2px!important;right:2px!important;
            width:11px;height:11px;border-radius:50%;border:2px solid #000;
            z-index:101;pointer-events:none;
        }
        .rem-online-dot.online{background-color:#22c55e!important;}
        .rem-online-dot.offline{background-color:#ef4444!important;}

        .rem-own-badge {
            position:absolute!important;bottom:5px!important;left:5px!important;z-index:50!important;
            width:22px;height:22px;border-radius:50%;
            background-color:#22c55e;color:#fff;
            display:flex;align-items:center;justify-content:center;
            border:2px solid #fff;pointer-events:none;
            opacity:0;animation:remFadeIn .3s forwards;box-shadow:none!important;
        }
        .rem-own-badge svg{width:14px;height:14px;stroke-width:3;}

        .rem-wish-badge {
            position:absolute!important;bottom:5px!important;right:5px!important;z-index:50!important;
            width:22px;height:22px;border-radius:50%;
            background-color:#3b82f6;color:#fff;
            display:flex;align-items:center;justify-content:center;
            border:2px solid #fff;pointer-events:none;
            opacity:0;animation:remFadeIn .3s forwards;box-shadow:none!important;
        }
        .rem-wish-badge svg{width:14px;height:14px;stroke-width:3;}

        @keyframes remFadeIn{from{opacity:0;transform:scale(.5)}to{opacity:1;transform:scale(1)}}

        .rem-toggle {
            display:inline-flex;align-items:center;justify-content:center;
            margin-left:10px;padding:2px 8px;border-radius:6px;
            font-size:11px;font-weight:800;text-transform:uppercase;
            cursor:pointer;border:1px solid #3f3f46;color:#fff;height:22px;vertical-align:middle;
            transition:all .2s;
        }
        .rem-toggle.on{background-color:#15803d;border-color:#22c55e;}
        .rem-toggle.off{background-color:transparent;color:#71717a;border-color:#3f3f46;}
        .rem-toggle:hover{filter:brightness(1.2);}

        #rem-loader-status {
            position:fixed;bottom:20px;left:70px;z-index:999999;
            background:#18181b;color:#fff;padding:8px 16px;border-radius:20px;
            border:1px solid #27272a;font-size:13px;font-weight:500;
            box-shadow:0 4px 15px rgba(0,0,0,.6);display:none;align-items:center;gap:10px;
        }
        #rem-loader-status.active{display:flex;animation:slideIn .3s;}
        @keyframes slideIn{from{opacity:0;transform:translateX(-20px)}to{opacity:1;transform:translateX(0)}}

        html.rem-checking, html.rem-checking body { background-color: #0c0c0c !important; }
        html.rem-checking body>*:not(#rem-loader){display:none!important;}
        #rem-loader{position:fixed;inset:0;background:#0c0c0c;z-index:999999;display:flex;align-items:center;justify-content:center;color:#666;font-family:sans-serif;}

        body.rem-active main>*:not(#rem-inject){display:none!important;}

        #rem-inject{width:100%;max-width:650px;margin:0 auto;min-height:80vh;font-family:system-ui,-apple-system,sans-serif;padding:10px;}

        .rem-grid{display:grid;grid-template-columns:repeat(4,1fr)!important;gap:10px;padding-bottom:40px;}

        .rem-card{position:relative;aspect-ratio:2/3;background:#18181b;border:1px solid #27272a;border-radius:6px;overflow:hidden;cursor:pointer;transition:.2s;display:flex;}
        .rem-card:hover{border-color:#71717a;}
        .rem-card img{width:100%;height:100%;object-fit:cover;}

        .rem-fix-header{margin-bottom:24px;padding-bottom:16px;border-bottom:1px solid #27272a;}
        .rem-fix-title{font-size:24px;font-weight:700;color:#fff;margin:0 0 4px 0;}
        .rem-fix-meta{color:#a1a1aa;font-size:13px;display:flex;align-items:center;margin-top:8px;}
        .rem-sep{margin:0 8px;opacity:.5;}

        .rem-over{position:fixed;inset:0;z-index:99999;background:rgba(0,0,0,.85);display:flex;align-items:center;justify-content:center;backdrop-filter:blur(2px);animation:F .2s;}
        .rem-box{position:relative;width:90%;max-width:450px;background:#0c0c0c;border:1px solid #27272a;border-radius:16px;padding:24px;display:flex;flex-direction:column;gap:20px;animation:Z .2s;}
        @keyframes F{from{opacity:0}to{opacity:1}} @keyframes Z{from{transform:scale(.95)}to{transform:scale(1)}}
        .rem-box-img{width:180px;aspect-ratio:2/3;margin:10px auto 0;border-radius:12px;overflow:hidden;border:1px solid #27272a;}
        .rem-box-img img{width:100%;height:100%;object-fit:cover;}
        .rem-info{text-align:center;display:flex;flex-direction:column;gap:4px;}
        .rem-lnk-m{color:#fff;font-size:14px;font-weight:500;text-decoration:none;opacity:.9;display:inline-block;}
        .rem-lnk-c{color:#fff;font-size:20px;font-weight:700;text-decoration:none;display:inline-block;}
        .rem-acts{display:flex;justify-content:center;gap:12px;margin-top:5px;}
        .rem-pill{height:36px;padding:0 16px;border-radius:99px;background:#27272a;color:#fff;border:none;display:flex;align-items:center;gap:6px;font-size:14px;font-weight:500;}
        .rem-sub{display:flex;flex-wrap:wrap;justify-content:center;gap:10px;margin-top:5px;}
        .rem-s-btn{height:36px;padding:0 16px;border-radius:99px;background:#27272a;color:#fff;text-decoration:none;font-size:14px;font-weight:500;display:flex;align-items:center;}
        .rem-close{position:absolute;top:12px;right:12px;width:32px;height:32px;border-radius:50%;background:#27272a;color:#fff;border:none;display:flex;align-items:center;justify-content:center;cursor:pointer;}

        #rem-cfg-btn {
            position:fixed;bottom:20px;left:20px;z-index:999999;
            width:40px;height:40px;border-radius:50%;
            background:#18181b;border:1px solid #27272a;color:#a1a1aa;
            display:flex;align-items:center;justify-content:center;
            cursor:pointer;transition:.2s;box-shadow:0 4px 10px rgba(0,0,0,.5);
            font-size:18px;
        }
        #rem-cfg-btn:hover{color:#fff;border-color:#71717a;transform:rotate(45deg);}

        #rem-settings-panel {
            position:fixed;bottom:70px;left:20px;z-index:999998;
            width:300px;background:#18181b;border:1px solid #27272a;
            border-radius:16px;padding:0;overflow:hidden;
            box-shadow:0 8px 32px rgba(0,0,0,.7);
            transform:scale(.95) translateY(10px);opacity:0;
            transition:transform .2s, opacity .2s;
            pointer-events:none;
        }
        #rem-settings-panel.open {
            transform:scale(1) translateY(0);opacity:1;pointer-events:all;
        }
        .rem-panel-header {
            padding:14px 16px 12px;border-bottom:1px solid #27272a;
            display:flex;align-items:center;justify-content:space-between;
        }
        .rem-panel-title {
            font-size:15px;font-weight:700;color:#fff;display:flex;align-items:center;gap:8px;
        }
        .rem-panel-title span.badge {
            font-size:10px;background:#27272a;color:#71717a;
            padding:2px 6px;border-radius:4px;font-weight:600;
        }
        .rem-panel-body { padding:8px 0; max-height:70vh; overflow-y:auto; scrollbar-width:thin; scrollbar-color:#3f3f46 transparent; }
        .rem-panel-body::-webkit-scrollbar { width:4px; }
        .rem-panel-body::-webkit-scrollbar-thumb { background:#3f3f46; border-radius:2px; }

        .rem-section-label {
            font-size:10px;font-weight:700;color:#52525b;text-transform:uppercase;
            letter-spacing:.08em;padding:8px 16px 4px;
        }

        .rem-row {
            display:flex;align-items:center;justify-content:space-between;
            padding:9px 16px;cursor:default;transition:background .15s;
            gap:10px;
        }
        .rem-row:hover{ background:rgba(255,255,255,.04); }
        .rem-row-info { display:flex;flex-direction:column;gap:1px;flex:1;min-width:0; }
        .rem-row-name { font-size:13px;color:#e4e4e7;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis; }
        .rem-row-desc { font-size:11px;color:#71717a;white-space:nowrap;overflow:hidden;text-overflow:ellipsis; }

        .rem-sw { position:relative;width:36px;height:20px;flex-shrink:0;cursor:pointer; }
        .rem-sw input { opacity:0;width:0;height:0;position:absolute; }
        .rem-sw-track {
            position:absolute;inset:0;border-radius:10px;background:#3f3f46;
            transition:background .2s;
        }
        .rem-sw input:checked + .rem-sw-track { background:#16a34a; }
        .rem-sw-thumb {
            position:absolute;width:14px;height:14px;border-radius:50%;background:#fff;
            top:3px;left:3px;transition:left .2s;pointer-events:none;
        }
        .rem-sw input:checked ~ .rem-sw-thumb { left:19px; }

        .rem-sub-row{padding:4px 16px 8px;display:flex;flex-direction:column;gap:4px;}
        .rem-sub-row-label{font-size:10px;color:#52525b;font-weight:600;}
        .rem-url-input{width:100%;height:30px;border-radius:6px;border:1px solid #27272a;background:#18181b;color:#e4e4e7;font-size:11px;padding:0 8px;outline:none;transition:.15s;}
        .rem-url-input:focus{border-color:#3b82f6;}
        .rem-url-input::placeholder{color:#3f3f46;}

        .rem-ex-cloud{margin-bottom:16px;}
        .rem-ex-cloud-item{display:flex;align-items:center;justify-content:space-between;padding:8px 12px;background:#18181b;border:1px solid #1e1e21;border-radius:8px;transition:.15s;cursor:pointer;}
        .rem-ex-cloud-item:hover{border-color:#3b82f6;background:rgba(59,130,246,.06);}
        .rem-ex-cloud-item-left{display:flex;align-items:center;gap:8px;min-width:0;}
        .rem-ex-cloud-item-name{font-size:13px;color:#e4e4e7;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
        .rem-ex-cloud-item-meta{font-size:11px;color:#52525b;white-space:nowrap;}
        .rem-ex-cloud-item-badge{font-size:9px;padding:2px 6px;border-radius:4px;background:rgba(96,165,250,.15);color:#60a5fa;font-weight:700;flex-shrink:0;}

        .rem-panel-footer {
            border-top:1px solid #27272a;padding:10px 16px;
            display:flex;gap:8px;flex-wrap:wrap;
        }
        .rem-action-btn {
            flex:1;min-width:0;height:32px;border-radius:8px;border:1px solid #3f3f46;
            background:transparent;color:#a1a1aa;font-size:11px;font-weight:600;
            cursor:pointer;transition:all .15s;white-space:nowrap;
            padding:0 4px;overflow:hidden;text-overflow:ellipsis;
        }
        .rem-action-btn:hover{ background:#27272a;color:#fff;border-color:#52525b; }
        .rem-action-btn.danger:hover{ background:#450a0a;color:#f87171;border-color:#7f1d1d; }

        .rem-profile-stat-badge {
            margin-top:6px;display:inline-flex;align-items:center;justify-content:center;gap:6px;
            background-color:#18181b;border:1px solid #27272a;padding:4px 10px;border-radius:6px;
            color:#a1a1aa;font-size:12px;font-weight:600;width:fit-content;align-self:center;
        }
        .rem-profile-stat-badge strong{color:#22c55e;margin-left:4px;}
        .rem-counting strong{color:#eab308;}


        #rem-inject{width:100%;max-width:650px;margin:0 auto;min-height:80vh;font-family:system-ui,-apple-system,sans-serif;padding:10px;}
        .rem-grid{display:grid;grid-template-columns:repeat(4,1fr)!important;gap:10px;padding-bottom:40px;}
        .rem-card{position:relative;aspect-ratio:2/3;background:#18181b;border:1px solid #27272a;border-radius:6px;overflow:hidden;cursor:pointer;transition:.2s;display:flex;}
        .rem-card:hover{border-color:#71717a;}
        .rem-card img{width:100%;height:100%;object-fit:cover;}

        .rem-fix-header{margin-bottom:24px;padding-bottom:16px;border-bottom:1px solid #27272a;}
        .rem-fix-title{font-size:24px;font-weight:700;color:#fff;margin:0 0 4px 0;}
        .rem-fix-meta{color:#a1a1aa;font-size:13px;display:flex;align-items:center;margin-top:8px;}
        .rem-sep{margin:0 8px;opacity:.5;}

        .rem-over{position:fixed;inset:0;z-index:99999;background:rgba(0,0,0,.85);display:flex;align-items:center;justify-content:center;backdrop-filter:blur(2px);animation:F .2s;}
        .rem-box{position:relative;width:90%;max-width:450px;background:#0c0c0c;border:1px solid #27272a;border-radius:16px;padding:24px;display:flex;flex-direction:column;gap:20px;animation:Z .2s;}
        @keyframes F{from{opacity:0}to{opacity:1}} @keyframes Z{from{transform:scale(.95)}to{transform:scale(1)}}
        .rem-box-img{width:180px;aspect-ratio:2/3;margin:10px auto 0;border-radius:12px;overflow:hidden;border:1px solid #27272a;}
        .rem-box-img img{width:100%;height:100%;object-fit:cover;}
        .rem-info{text-align:center;display:flex;flex-direction:column;gap:4px;}
        .rem-lnk-m{color:#fff;font-size:14px;font-weight:500;text-decoration:none;opacity:.9;display:inline-block;}
        .rem-lnk-c{color:#fff;font-size:20px;font-weight:700;text-decoration:none;display:inline-block;}
        .rem-acts{display:flex;justify-content:center;gap:12px;margin-top:5px;}
        .rem-pill{height:36px;padding:0 16px;border-radius:99px;background:#27272a;color:#fff;border:none;display:flex;align-items:center;gap:6px;font-size:14px;font-weight:500;}
        .rem-sub{display:flex;flex-wrap:wrap;justify-content:center;gap:10px;margin-top:5px;}
        .rem-s-btn{height:36px;padding:0 16px;border-radius:99px;background:#27272a;color:#fff;text-decoration:none;font-size:14px;font-weight:500;display:flex;align-items:center;}
        .rem-close{position:absolute;top:12px;right:12px;width:32px;height:32px;border-radius:50%;background:#27272a;color:#fff;border:none;display:flex;align-items:center;justify-content:center;cursor:pointer;}



        #rem-exchange-panel{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);z-index:999998;width:680px;max-width:94vw;max-height:88vh;background:#0f0f11;border:1px solid #27272a;border-radius:20px;display:none;flex-direction:column;overflow:hidden;box-shadow:0 24px 80px rgba(0,0,0,.7),0 0 0 1px rgba(255,255,255,.03);animation:Z .25s;}
        #rem-exchange-panel.open{display:flex;}
        .rem-ex-backdrop{position:fixed;inset:0;z-index:999997;background:rgba(0,0,0,.6);backdrop-filter:blur(3px);display:none;}
        .rem-ex-backdrop.open{display:block;}

        #rem-cardbattle-panel{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);z-index:999998;width:480px;max-width:94vw;max-height:88vh;background:#0f0f11;border:1px solid #27272a;border-radius:20px;display:none;flex-direction:column;overflow:hidden;box-shadow:0 24px 80px rgba(0,0,0,.7),0 0 0 1px rgba(255,255,255,.03);animation:Z .25s;}
        #rem-cardbattle-panel.open{display:flex;}
        .rem-cb-backdrop{position:fixed;inset:0;z-index:999997;background:rgba(0,0,0,.6);backdrop-filter:blur(3px);display:none;}
        .rem-cb-backdrop.open{display:block;}

        .rem-ex-header{padding:20px 24px 16px;display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid #1e1e21;}
        .rem-ex-title{font-size:18px;font-weight:700;color:#fff;display:flex;align-items:center;gap:8px;}
        .rem-ex-close{width:32px;height:32px;border-radius:50%;background:#1e1e21;border:1px solid #2e2e33;color:#a1a1aa;display:flex;align-items:center;justify-content:center;cursor:pointer;transition:.2s;font-size:16px;}
        .rem-ex-close:hover{background:#27272a;color:#fff;border-color:#3f3f46;}

        .rem-ex-body{flex:1;overflow-y:auto;padding:16px 24px 24px;scrollbar-width:thin;scrollbar-color:#27272a transparent;}
        .rem-ex-body::-webkit-scrollbar{width:6px;}
        .rem-ex-body::-webkit-scrollbar-thumb{background:#27272a;border-radius:3px;}

        .rem-ex-input-row{display:flex;gap:8px;margin-bottom:16px;}
        .rem-ex-input{flex:1;background:#18181b;border:1px solid #27272a;border-radius:10px;color:#e4e4e7;font-size:13px;padding:10px 14px;outline:none;transition:border .15s;}
        .rem-ex-input:focus{border-color:#3b82f6;}
        .rem-ex-input::placeholder{color:#52525b;}
        .rem-ex-btn{padding:0 20px;border-radius:10px;border:none;font-size:13px;font-weight:600;cursor:pointer;transition:.15s;display:flex;align-items:center;gap:6px;}
        .rem-ex-btn.primary{background:linear-gradient(135deg,#3b82f6,#2563eb);color:#fff;}
        .rem-ex-btn.primary:hover{filter:brightness(1.1);transform:translateY(-1px);}
        .rem-ex-btn.secondary{background:#1e1e21;color:#a1a1aa;border:1px solid #27272a;}
        .rem-ex-btn.secondary:hover{background:#27272a;color:#fff;}
        .rem-ex-btn:disabled{opacity:.5;cursor:default;transform:none!important;filter:none!important;}

        .rem-ex-progress{width:100%;margin:12px 0 16px;text-align:center;}
        .rem-ex-pbar-track{width:100%;height:6px;background:#1e1e21;border-radius:3px;overflow:hidden;margin:8px 0;}
        .rem-ex-pbar-fill{height:100%;background:linear-gradient(90deg,#3b82f6,#60a5fa);border-radius:3px;width:0%;transition:width .3s;}
        .rem-ex-ptext{font-size:12px;color:#71717a;}
        .rem-ex-ptext strong{color:#a1a1aa;}

        .rem-ex-rank-bar{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:16px;}
        .rem-ex-rank-btn{padding:6px 14px;border-radius:8px;background:#18181b;border:1px solid #27272a;color:#a1a1aa;font-size:12px;font-weight:600;cursor:pointer;transition:.15s;}
        .rem-ex-rank-btn:hover{border-color:#3f3f46;color:#fff;}
        .rem-ex-rank-btn.active{background:#3b82f6;border-color:#3b82f6;color:#fff;}
        .rem-ex-rank-count{font-size:10px;color:#71717a;margin-left:2px;}

        .rem-ex-summary{background:#18181b;border:1px solid #27272a;border-radius:12px;padding:14px 18px;margin-bottom:16px;display:flex;gap:20px;justify-content:center;flex-wrap:wrap;}
        .rem-ex-s-item{text-align:center;}
        .rem-ex-s-num{font-size:22px;font-weight:800;line-height:1;}
        .rem-ex-s-lab{font-size:10px;color:#71717a;text-transform:uppercase;font-weight:600;margin-top:2px;}

        .rem-ex-results{display:flex;flex-direction:column;gap:10px;}
        .rem-ex-card{background:#18181b;border:1px solid #27272a;border-radius:12px;overflow:hidden;transition:.15s;cursor:pointer;}
        .rem-ex-card:hover{border-color:#3f3f46;transform:translateY(-1px);box-shadow:0 4px 12px rgba(0,0,0,.3);}
        .rem-ex-card-inner{display:flex;gap:14px;padding:14px;}
        .rem-ex-card-img{width:60px;height:90px;border-radius:8px;overflow:hidden;flex-shrink:0;background:#27272a;}
        .rem-ex-card-img img{width:100%;height:100%;object-fit:cover;}
        .rem-ex-card-info{flex:1;min-width:0;display:flex;flex-direction:column;gap:4px;}
        .rem-ex-card-name{font-size:14px;font-weight:700;color:#fff;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
        .rem-ex-card-rank{font-size:11px;font-weight:600;padding:2px 8px;border-radius:4px;display:inline-block;width:fit-content;}
        .rem-ex-card-traders{display:flex;flex-wrap:wrap;gap:6px;margin-top:4px;}
        .rem-ex-card-tag{font-size:11px;padding:3px 8px;border-radius:6px;font-weight:600;display:flex;align-items:center;gap:4px;}
        .rem-ex-card-tag.buyers{background:rgba(59,130,246,.15);color:#60a5fa;}
        .rem-ex-card-tag.sellers{background:rgba(249,115,22,.15);color:#fb923c;}
        .rem-ex-card-tag.owners{background:rgba(34,197,94,.15);color:#4ade80;}

        .rem-ex-detail{animation:F .2s;}
        .rem-ex-detail-header{display:flex;gap:16px;margin-bottom:16px;}
        .rem-ex-detail-img{width:100px;height:150px;border-radius:10px;overflow:hidden;flex-shrink:0;}
        .rem-ex-detail-img img{width:100%;height:100%;object-fit:cover;}
        .rem-ex-detail-meta{flex:1;display:flex;flex-direction:column;gap:6px;}
        .rem-ex-detail-name{font-size:18px;font-weight:700;color:#fff;}
        .rem-ex-detail-id{font-size:12px;color:#71717a;}
        .rem-ex-user-list{margin-top:8px;}
        .rem-ex-user-list h4{font-size:12px;font-weight:700;text-transform:uppercase;color:#71717a;margin:12px 0 6px;letter-spacing:.05em;}
        .rem-ex-user{display:flex;align-items:center;gap:8px;padding:6px 10px;border-radius:8px;transition:.1s;text-decoration:none;}
        .rem-ex-user:hover{background:#1e1e21;}
        .rem-ex-user-name{font-size:13px;color:#e4e4e7;font-weight:500;}
        .rem-ex-user-id{font-size:11px;color:#52525b;}

        .rem-ex-empty{text-align:center;padding:40px 20px;color:#52525b;font-size:14px;}
        .rem-ex-nav{display:flex;justify-content:center;gap:8px;margin-top:16px;}

        .rem-scan-btn{display:inline-flex;align-items:center;gap:6px;padding:6px 10px;border-radius:8px;border:1px solid #27272a;background:#18181b;color:#a1a1aa;font-size:11px;font-weight:600;cursor:pointer;transition:.2s;margin-top:6px;align-self:center;max-width:130px;min-height:36px;flex-shrink:1;}
        .rem-scan-btn:hover{border-color:#3f3f46;color:#fff;}
        .rem-scan-btn.active{background:rgba(59,130,246,.15);border-color:#3b82f6;color:#60a5fa;}
        .rem-scan-btn .rem-scan-label{white-space:normal;text-align:left;line-height:1.2;flex:1;}
        .rem-scan-btn .rem-scan-check{width:16px;height:16px;border-radius:4px;border:2px solid #3f3f46;display:flex;align-items:center;justify-content:center;transition:.2s;flex-shrink:0;}
        .rem-scan-btn.active .rem-scan-check{background:#3b82f6;border-color:#3b82f6;}
        .rem-scan-btn.active .rem-scan-check svg{display:block;}
        .rem-scan-btn .rem-scan-check svg{display:none;width:10px;height:10px;}

        .rem-ex-scanlist{margin-bottom:16px;}
        .rem-ex-scanlist-title{font-size:13px;font-weight:700;color:#a1a1aa;margin-bottom:8px;display:flex;align-items:center;gap:8px;}
        .rem-ex-scanlist-count{font-size:11px;color:#52525b;font-weight:500;}
        .rem-ex-scanlist-items{display:flex;flex-direction:column;gap:4px;max-height:200px;overflow-y:auto;scrollbar-width:thin;scrollbar-color:#27272a transparent;}
        .rem-ex-scanlist-items::-webkit-scrollbar{width:5px;}
        .rem-ex-scanlist-items::-webkit-scrollbar-thumb{background:#27272a;border-radius:3px;}
        .rem-ex-scanlist-item{display:flex;align-items:center;justify-content:space-between;padding:6px 10px;background:#18181b;border:1px solid #1e1e21;border-radius:8px;transition:.1s;}
        .rem-ex-scanlist-item:hover{border-color:#27272a;}
        .rem-ex-scanlist-item-info{display:flex;align-items:center;gap:8px;min-width:0;}
        .rem-ex-scanlist-item-name{font-size:13px;color:#e4e4e7;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
        .rem-ex-scanlist-item-id{font-size:11px;color:#52525b;flex-shrink:0;}
        .rem-ex-scanlist-rm{width:24px;height:24px;border-radius:6px;border:none;background:transparent;color:#52525b;font-size:14px;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:.15s;flex-shrink:0;}
        .rem-ex-scanlist-rm:hover{background:#27272a;color:#ef4444;}
        .rem-ex-scanlist-actions{display:flex;gap:6px;margin-top:8px;}

        #rem-custom-bg {
            position:fixed;top:0;left:0;width:100%;height:100vh;
            object-fit:cover;object-position:center center;
            z-index:-9;pointer-events:none;
            will-change:transform;transform:translateZ(0);
        }

        .rem-slider {
            -webkit-appearance:none;appearance:none;
            width:100%;height:4px;border-radius:2px;
            background:#3f3f46;outline:none;cursor:pointer;
        }
        .rem-slider::-webkit-slider-thumb {
            -webkit-appearance:none;appearance:none;
            width:14px;height:14px;border-radius:50%;background:#22c55e;cursor:pointer;
        }
        .rem-slider::-moz-range-thumb {
            width:14px;height:14px;border-radius:50%;background:#22c55e;cursor:pointer;border:none;
        }
        .rem-url-input {
            width:100%;background:#0c0c0c;border:1px solid #3f3f46;border-radius:8px;
            color:#e4e4e7;font-size:12px;padding:6px 10px;box-sizing:border-box;
            outline:none;transition:border-color .15s;
        }
        .rem-url-input:focus { border-color:#22c55e; }
        .rem-url-input::placeholder { color:#52525b; }

        .rem-sub-row {
            padding:4px 16px 8px;
            display:flex;flex-direction:column;gap:6px;
        }
        .rem-sub-row-label {
            font-size:11px;color:#71717a;
            display:flex;justify-content:space-between;align-items:center;
        }
        .rem-upload-btn {
            width:100%;height:30px;border-radius:8px;border:1px dashed #3f3f46;
            background:transparent;color:#71717a;font-size:12px;font-weight:600;
            cursor:pointer;transition:all .15s;
        }
        .rem-upload-btn:hover { border-color:#52525b;color:#a1a1aa;background:#27272a; }
        .rem-select {
            background:#0c0c0c;border:1px solid #3f3f46;border-radius:8px;
            color:#e4e4e7;font-size:12px;padding:4px 8px;
            outline:none;cursor:pointer;
        }

        .rem-deck-count-badge {
            position:absolute!important;top:6px!important;left:6px!important;z-index:50!important;
            min-width:24px;height:24px;border-radius:12px;
            background:rgba(0,0,0,.75);color:#fff;
            display:flex;align-items:center;justify-content:center;
            padding:0 6px;font-size:12px;font-weight:700;
            border:1.5px solid rgba(255,255,255,.25);pointer-events:none;
            opacity:0;animation:remFadeIn .3s forwards;
            backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px);
            letter-spacing:.3px;line-height:1;
        }
        .rem-deck-count-badge.has-count {
            background:rgba(34,197,94,.85);border-color:rgba(255,255,255,.4);
        }

        .rem-deck-inner-count {
            display:flex;align-items:center;justify-content:center;gap:6px;
            margin-bottom:8px;font-size:14px;font-weight:600;color:#a1a1aa;
        }
        .rem-deck-inner-count strong {
            color:#22c55e;font-size:16px;
        }

        .rem-user-new-badge {
            position:absolute!important;top:2px!important;left:2px!important;z-index:9999!important;
            background-color:#ef4444!important;color:#fff!important;
            font-size:9px!important;font-weight:800!important;
            padding:1.5px 5px!important;border-radius:4px!important;
            border:1.5px solid #fff!important;pointer-events:none!important;
            line-height:1!important;text-transform:uppercase!important;
            box-shadow: 0 2px 4px rgba(0,0,0,.35)!important;
            opacity:0;animation:remFadeIn .3s forwards;
        }
        .rem-card-selected {
            position: relative!important;
            display: inline-block!important;
        }
        .rem-card-selected::after {
            content: ''!important;
            position: absolute!important;
            inset: 0!important;
            border: 3.5px solid #3b82f6 !important;
            border-radius: inherit!important;
            background: rgba(59, 130, 246, 0.15) !important;
            box-shadow: inset 0 0 20px rgba(59, 130, 246, 0.5), 0 0 14px rgba(59, 130, 246, 0.4) !important;
            pointer-events: none!important;
            z-index: 99999!important;
            transition: all 0.2s ease!important;
        }
        .rem-sel-overlay {
            position: absolute!important;
            inset: 0!important;
            z-index: 99998!important;
            cursor: pointer!important;
            background: transparent!important;
            user-select: none!important;
            -webkit-user-select: none!important;
        }
        .rem-selection-panel {
            position: fixed!important;
            bottom: 20px!important;
            left: 50%!important;
            transform: translateX(-50%) translateY(100px)!important;
            z-index: 999999!important;
            background: rgba(20, 20, 25, 0.9) !important;
            backdrop-filter: blur(16px) saturate(180%) !important;
            -webkit-backdrop-filter: blur(16px) saturate(180%) !important;
            border: 1px solid rgba(255, 255, 255, 0.08) !important;
            border-radius: 20px !important;
            padding: 12px 24px !important;
            display: flex!important;
            align-items: center!important;
            gap: 16px!important;
            box-shadow: 0 15px 40px rgba(0, 0, 0, 0.6), inset 0 1px 0 rgba(255, 255, 255, 0.15) !important;
            transition: transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1), opacity 0.3s ease !important;
            opacity: 0!important;
            pointer-events: none!important;
        }
        .rem-selection-panel.open {
            transform: translateX(-50%) translateY(0)!important;
            opacity: 1!important;
            pointer-events: auto!important;
        }
        .rem-selection-info {
            font-size: 14px!important;
            font-weight: 700!important;
            color: #fff!important;
            white-space: nowrap!important;
            font-family: system-ui, -apple-system, sans-serif!important;
        }
        .rem-selection-btn {
            height: 38px!important;
            padding: 0 18px!important;
            border-radius: 12px!important;
            font-size: 13px!important;
            font-weight: 700!important;
            cursor: pointer!important;
            display: flex!important;
            align-items: center!important;
            gap: 6px!important;
            transition: all 0.2s ease!important;
            border: none!important;
            font-family: system-ui, -apple-system, sans-serif!important;
            outline: none!important;
        }
        .rem-selection-btn.want {
            background: #3b82f6 !important;
            color: #fff !important;
        }
        .rem-selection-btn.want:hover {
            background: #2563eb !important;
            box-shadow: 0 0 12px rgba(59, 130, 246, 0.5) !important;
            transform: translateY(-1px)!important;
        }
        .rem-selection-btn.sell {
            background: #f97316 !important;
            color: #fff !important;
        }
        .rem-selection-btn.sell:hover {
            background: #ea580c !important;
            box-shadow: 0 0 12px rgba(249, 115, 22, 0.5) !important;
            transform: translateY(-1px)!important;
        }
        .rem-selection-btn.delete {
            background: #ef4444 !important;
            color: #fff !important;
        }
        .rem-selection-btn.delete:hover {
            background: #dc2626 !important;
            box-shadow: 0 0 12px rgba(239, 68, 68, 0.5) !important;
            transform: translateY(-1px)!important;
        }
        .rem-selection-btn.clear {
            background: transparent !important;
            border: 1px solid rgba(255,255,255,0.15) !important;
            color: #a1a1aa !important;
        }
        .rem-selection-btn.clear:hover {
            background: rgba(255,255,255,0.06) !important;
            color: #fff !important;
        }
        .rem-trade-rating-bar {
            background:#111113;border:1px solid #27272a;border-radius:12px;
            padding:12px;margin:12px 0 0 0;display:flex;flex-direction:column;
            gap:6px;width:100%;box-sizing:border-box;
        }
        .rem-trade-bar-header {
            display:flex;justify-content:space-between;align-items:center;
            font-size:12px;font-weight:700;color:#fff;
        }
        .rem-trade-bar-track {
            height:8px;background:#27272a;border-radius:4px;overflow:hidden;
            display:flex;width:100%;
        }
        .rem-trade-bar-fill-a {
            height:100%;background:#3b82f6;transition:width .3s ease;
        }
        .rem-trade-bar-fill-b {
            height:100%;background:#ea580c;transition:width .3s ease;
        }
        .rem-trade-bar-verdict {
            font-size:11px;text-align:center;color:#a1a1aa;font-weight:600;
        }
        #rem-create-trade-bar {
            position:sticky;top:60px;z-index:999;
            background:#111113;border:1px solid #27272a;border-radius:12px;
            padding:12px 16px;margin:12px auto;display:none;flex-direction:column;
            gap:6px;width:100%;max-width:650px;box-shadow:0 8px 24px rgba(0,0,0,.5);
        }
        .rem-trade-label-points {
            font-weight:700;margin-left:6px;
        }
        .rem-trade-label-points.profit {
            color:#22c55e!important;
        }
        .rem-trade-label-points.loss {
            color:#ef4444!important;
        }
        .rem-trade-label-points.neutral {
            color:#fff!important;
        }

        a[href*="/character/"] {
            position:relative!important;
            overflow:visible!important;
        }
        a[href*="/character/"] img {
            border-radius:16px!important;
        }
        div:has(> a[href*="/character/"]) {
            padding-top: 12px !important;
            padding-left: 12px !important;
            overflow: visible !important;
        }

        .rem-char-slot-badge {
            position:absolute!important;top:-8px!important;left:-8px!important;z-index:10!important;
            min-width:auto;height:auto;border-radius:6px;
            display:flex;align-items:center;justify-content:center;
            padding:3px 7px;font-size:12px;font-weight:700;
            pointer-events:none;
            opacity:0;animation:remFadeIn .3s forwards;
            background:rgba(0,0,0,.5)!important;
            letter-spacing:.3px;line-height:1;
            font-family:system-ui,-apple-system,sans-serif;
        }
        .rem-char-slot-badge.slots-full {
            color:#ef4444;
        }
        .rem-char-slot-badge.slots-available {
            color:#4ade80;
        }
        .rem-char-slot-badge.slots-empty {
            color:#fff;
        }
        .rem-char-slot-badge.slots-loading {
            color:#a1a1aa;
        }

        .rem-char-slot-btn {
            position:absolute!important;top:-8px!important;left:-8px!important;z-index:10!important;
            width:30px;height:30px;border-radius:6px;
            display:flex;align-items:center;justify-content:center;
            cursor:pointer;
            opacity:0;animation:remFadeIn .3s forwards;
            background:rgba(0,0,0,.45)!important;
            color:#fff!important;
            border:none;
            transition:background 0.2s, transform 0.1s;
            pointer-events:auto!important;
        }
        .rem-char-slot-btn:hover {
            background:rgba(0,0,0,.7)!important;
            transform:scale(1.1);
        }
        .rem-char-slot-btn:active {
            transform:scale(0.9);
        }
    `;
    const styleEl = document.createElement('style');
    styleEl.textContent = STYLES;
    (document.head || document.documentElement).appendChild(styleEl);

    const Manager = {
        covers: new Set(),
        wishCovers: new Set(),
        coverToIdMap: {},
        userId: null,
        loaded: false,
        wishLoaded: false,
        cacheMap: new Map(),
        onlineCache: new Map(),
        onlineProcessing: new Set(),

        async init() {
            this.createUI();
            if (typeof initCardSelection === 'function') initCardSelection();
            if (typeof initCardBattleAutomation === 'function') initCardBattleAutomation();
            this.userId = storageGet(MY_ID_KEY);

            const mapCache = storageGet('rem_cover_to_id');
            if (mapCache) {
                try {
                    this.coverToIdMap = JSON.parse(mapCache);
                } catch (e) {
                    this.coverToIdMap = {};
                }
            } else {
                this.coverToIdMap = {};
            }

            const cache = storageGet(MY_INV_KEY);
            if (cache) {
                try {
                    const d = JSON.parse(cache);
                    this.covers = new Set(d.data);
                    this.coversCount = d.counts || {};
                    this.loaded = true;
                    setTimeout(() => scanVisual(), 500);
                } catch (e) { }
            } else {
                this.coversCount = {};
            }

            const wishCache = storageGet(MY_WISH_KEY);
            if (wishCache) {
                try {
                    const wd = JSON.parse(wishCache);
                    this.wishCovers = new Set(wd.data);
                    this.wishLoaded = true;
                } catch (e) { }
            }

            if (!this.userId) { this.promptId(); return; }

            const cacheData = cache ? JSON.parse(cache) : null;
            if (!cacheData || !cacheData.time || Date.now() - cacheData.time > 1200000) {
                this.sync(true);
            }

            const wishCacheData = wishCache ? JSON.parse(wishCache) : null;
            if (!wishCacheData || !wishCacheData.time || Date.now() - wishCacheData.time > 1200000) {
                this.syncWishes(true);
            }

            setInterval(() => {
                this.sync(true);
                this.syncWishes(true);
            }, 1200000);
        },

        getFilename(url) { if (!url) return null; return url.split('/').pop().split('?')[0].replace(/\.(jpg|jpeg|png|webp|gif|webm|mp4)$/i, ''); },

        async sync(silent = false) {
            if (!this.userId) return;
            if (this.syncing) return;
            this.syncing = true;
            try {
                if (!silent) this.showStatus("Обновление...", true);
                let page = 1, run = true;
                const temp = new Set();
                this.coversCount = {};
                while (run) {
                    const url = `${API_DOMAIN}/api/v2/inventory/${this.userId}/?count=100&ordering=rank&page=${page}&type=cards`;
                    if (!silent) this.showStatus(`Загрузка стр. ${page} (Найдено: ${temp.size})...`, true);
                    const data = await this.req(url);
                    if (!data) break;
                    const list = data.results || [];
                    if (!list.length) break;
                    list.forEach(item => {
                        if (item && item.card && item.card.cover) {
                            const count = (item.count !== undefined && item.count !== null && !isNaN(parseInt(item.count))) ? parseInt(item.count) : ((item.stack_count !== undefined && item.stack_count !== null && !isNaN(parseInt(item.stack_count))) ? parseInt(item.stack_count) : 1);
                            const high = item.card.cover.high;
                            const mid = item.card.cover.mid;
                            const hName = typeof high === 'string' ? this.getFilename(high) : null;
                            const mName = typeof mid === 'string' ? this.getFilename(mid) : null;
                            if (hName) {
                                temp.add(hName);
                                this.coversCount[hName] = (this.coversCount[hName] || 0) + count;
                            }
                            if (mName) {
                                temp.add(mName);
                                this.coversCount[mName] = (this.coversCount[mName] || 0) + count;
                            }
                            if (item.card.id) {
                                if (hName) this.coverToIdMap[hName] = item.card.id;
                                if (mName) this.coverToIdMap[mName] = item.card.id;
                            }
                        }
                    });
                    if (!data.next) run = false;
                    else { page++; await new Promise(r => setTimeout(r, 100)); }
                }
                this.covers = temp;
                this.loaded = true;
                storageSet(MY_INV_KEY, JSON.stringify({
                    time: Date.now(),
                    data: Array.from(temp),
                    counts: this.coversCount
                }));
                storageSet('rem_cover_to_id', JSON.stringify(this.coverToIdMap));
                if (!silent) {
                    this.showStatus("Готово!", true);
                    setTimeout(() => this.showStatus("", false), 2000);
                }
                scanVisual();
            } catch (e) {
                console.error("Remanga Fix: Error in sync:", e);
                if (!silent) {
                    this.showStatus("Ошибка обновления", true);
                    setTimeout(() => this.showStatus("", false), 2000);
                }
            } finally {
                this.syncing = false;
            }
        },

        async syncWishes(silent = true) {
            if (!this.userId) return;
            if (this.syncingWishes) return;
            this.syncingWishes = true;
            try {
                let page = 1, run = true;
                const temp = new Set();
                while (run) {
                    const url = `${SITE_DOMAIN}/api/v2/inventory/wishes/users/${this.userId}/?wish_type=1&page=${page}`;
                    const data = await this.req(url);
                    if (!data) break;
                    const list = data.results || [];
                    if (!list.length) break;
                    list.forEach(item => {
                        if (item && item.card && item.card.cover) {
                            const high = item.card.cover.high;
                            const mid = item.card.cover.mid;
                            const hName = typeof high === 'string' ? this.getFilename(high) : null;
                            const mName = typeof mid === 'string' ? this.getFilename(mid) : null;
                            if (hName) temp.add(hName);
                            if (mName) temp.add(mName);
                            if (item.card.id) {
                                if (hName) this.coverToIdMap[hName] = item.card.id;
                                if (mName) this.coverToIdMap[mName] = item.card.id;
                            }
                        }
                    });
                    if (!data.next) run = false;
                    else { page++; await new Promise(r => setTimeout(r, 250)); }
                }
                this.wishCovers = temp;
                this.wishLoaded = true;
                storageSet(MY_WISH_KEY, JSON.stringify({ time: Date.now(), data: Array.from(temp) }));
                storageSet('rem_cover_to_id', JSON.stringify(this.coverToIdMap));
                scanVisual();
            } catch (e) {
                console.error("Remanga Fix: Error in syncWishes:", e);
            } finally {
                this.syncingWishes = false;
            }
        },

        req(url, retries = 2) {
            const finalUrl = url.startsWith('http') ? url : API_DOMAIN + '/' + url.replace(/^\//, '');
            const makeAttempt = () => {
                return new Promise(resolve => {
                    let resolved = false;
                    const safeResolve = (val) => {
                        if (!resolved) {
                            resolved = true;
                            clearTimeout(timeoutId);
                            resolve(val);
                        }
                    };
                    const timeoutId = setTimeout(() => {
                        safeResolve(null);
                    }, 10000);
                    const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent) || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1);
                    if (isIOS || typeof GM_xmlhttpRequest === 'undefined') {
                        const options = { headers: { "Accept": "application/json" } };
                        if (finalUrl.includes('remanga.org') || finalUrl.includes('xn--80aaig9ahr.xn--c1avg')) {
                            options.credentials = 'include';
                        } else {
                            options.credentials = 'omit';
                        }
                        fetch(finalUrl, options)
                            .then(r => r.ok ? r.json() : null)
                            .then(safeResolve)
                            .catch(() => fallbackGM(finalUrl, safeResolve));
                    } else {
                        fallbackGM(finalUrl, safeResolve);
                    }
                    function fallbackGM(u, res) {
                        if (typeof GM_xmlhttpRequest !== 'undefined') {
                            GM_xmlhttpRequest({
                                method: "GET", url: u, headers: { "Accept": "application/json" },
                                timeout: 10000,
                                onload: r => { if (r.status == 200) try { res(JSON.parse(r.responseText)) } catch { res(null) } else res(null) },
                                onerror: () => res(null),
                                ontimeout: () => res(null)
                            });
                        } else {
                            res(null);
                        }
                    }
                });
            };
            return makeAttempt().then(async (res) => {
                if (res !== null) return res;
                if (retries > 0) {
                    await new Promise(r => setTimeout(r, 1000));
                    return this.req(url, retries - 1);
                }
                return null;
            });
        },

        async getUserInfo(uid) {
            if (this.cacheMap.has(uid)) return this.cacheMap.get(uid);
            const data = await this.req(`${API_DOMAIN}/api/v2/users/${uid}/`);
            if (data) this.cacheMap.set(uid, data);
            return data;
        },

        has(filename) { return this.loaded && filename && this.covers.has(filename); },

        hasWish(filename) { return this.wishLoaded && filename && this.wishCovers.has(filename) && !this.has(filename); },

        getCardCount(filename) {
            if (!this.loaded || !filename) return 0;
            if (!this.coversCount) this.coversCount = {};
            return this.coversCount[filename] || (this.covers.has(filename) ? 1 : 0);
        },

        promptId() {
            const link = prompt("Remanga Fix: Вставьте ссылку на профиль:", "");
            if (link) {
                const m = link.match(/user\/(\d+)/);
                if (m && m[1]) {
                    storageRemove(MY_INV_KEY);
                    storageRemove(MY_WISH_KEY);
                    storageSet(MY_ID_KEY, m[1]);
                    this.userId = m[1];
                    this.loaded = false;
                    this.wishLoaded = false;
                    this.covers = new Set();
                    this.wishCovers = new Set();
                    this.syncing = false;
                    this.syncingWishes = false;
                    this.sync();
                    this.syncWishes();
                }
            }
        },

        showStatus(text, show) {
            const el = document.getElementById('rem-loader-status');
            if (!el) return;
            if (show) { el.innerText = text; el.classList.add('active'); } else el.classList.remove('active');
        },

        createUI() {
            if (document.getElementById('rem-cfg-btn')) return;
            applyCustomBackground();
            applyTheme();
            const btn = document.createElement('div');
            btn.id = 'rem-cfg-btn';
            btn.innerHTML = '⚙️';
            btn.title = 'Настройки расширения';
            btn.onclick = (e) => { e.stopPropagation(); togglePanel(); };
            document.body.appendChild(btn);

            const stat = document.createElement('div');
            stat.id = 'rem-loader-status';
            document.body.appendChild(stat);
            document.body.appendChild(buildSettingsPanel());
            document.addEventListener('click', (e) => {
                const panel = document.getElementById('rem-settings-panel');
                const cfgBtn = document.getElementById('rem-cfg-btn');
                if (panel && !panel.contains(e.target) && e.target !== cfgBtn) {
                    panel.classList.remove('open');
                }
            });
        }
    };

    function makeSwitchRow(key, name, desc) {
        const row = document.createElement('div');
        row.className = 'rem-row';

        const info = document.createElement('div');
        info.className = 'rem-row-info';
        info.innerHTML = `<div class="rem-row-name">${name}</div>${desc ? `<div class="rem-row-desc">${desc}</div>` : ''}`;

        const label = document.createElement('label');
        label.className = 'rem-sw';
        label.title = cfg[key] ? 'Включено' : 'Выключено';
        const input = document.createElement('input');
        input.type = 'checkbox';
        input.checked = cfg[key];
        const track = document.createElement('div');
        track.className = 'rem-sw-track';
        const thumb = document.createElement('div');
        thumb.className = 'rem-sw-thumb';
        label.appendChild(input);
        label.appendChild(track);
        label.appendChild(thumb);

        input.addEventListener('change', () => {
            saveSetting(key, input.checked);
            label.title = input.checked ? 'Включено' : 'Выключено';
            if (key === 'cardSelection' && !input.checked) {
                if (typeof clearSelection === 'function') clearSelection();
            }
        });

        row.appendChild(info);
        row.appendChild(label);
        return row;
    }

    function makeSelectRow(key, name, optionsList) {
        const row = document.createElement('div');
        row.className = 'rem-row';

        const info = document.createElement('div');
        info.className = 'rem-row-info';
        info.innerHTML = `<div class="rem-row-name">${name}</div>`;

        const select = document.createElement('select');
        select.className = 'rem-select';
        optionsList.forEach(optVal => {
            const opt = document.createElement('option');
            opt.value = optVal.value;
            opt.textContent = optVal.text;
            if (String(cfg[key]) === String(optVal.value)) {
                opt.selected = true;
            }
            select.appendChild(opt);
        });

        select.addEventListener('change', () => {
            saveSetting(key, parseInt(select.value) || select.value);
        });

        row.appendChild(info);
        row.appendChild(select);
        return row;
    }

    function buildSettingsPanel() {
        const panel = document.createElement('div');
        panel.id = 'rem-settings-panel';

        panel.innerHTML = `
            <div class="rem-panel-header">
                <div class="rem-panel-title">⚙️ Настройки <span class="badge" style="font-size:10px;color:#71717a">v10.0.0</span></div>
            </div>
        `;

        const body = document.createElement('div');
        body.className = 'rem-panel-body';

        const lbl1 = document.createElement('div');
        lbl1.className = 'rem-section-label';
        lbl1.textContent = 'Пользователи';
        body.appendChild(lbl1);
        body.appendChild(makeSwitchRow('onlineStatus', '🟢 Онлайн-статус', 'Точка на аватарке пользователя'));
        body.appendChild(makeSwitchRow('profileStats', '🎴 Статистика профиля', 'Кол-во созданных карт на профиле'));
        body.appendChild(makeSwitchRow('deckStats', '📦 Статистика кейсов', 'Число открытых паков на профиле'));
        body.appendChild(makeSwitchRow('bookmarkProgress', '📖 Прогресс в закладках', 'Прогресс и оценка в закладках'));

        const lbl2 = document.createElement('div');
        lbl2.className = 'rem-section-label';
        lbl2.textContent = 'Карты';
        body.appendChild(lbl2);
        body.appendChild(makeSwitchRow('ownBadge', '✅ Метка коллекции', 'Галочка на картах из вашей коллекции'));
        body.appendChild(makeSwitchRow('wishBadge', '💙 Метка желаемого', 'Синяя метка на картах из списка "Хочу"'));
        body.appendChild(makeSwitchRow('cardSelection', '🖱️ Выделение карт', 'Выделение карт по нажатию на карту'));
        body.appendChild(makeSwitchRow('tradeAnalysis', '⚖️ Анализ обменов', 'Показывать проценты выгоды в обменах'));
        body.appendChild(makeSwitchRow('characterSlots', '📌 Слоты персонажей', 'Сколько карт доступно для персонажа'));

        const lblCardBattle = document.createElement('div');
        lblCardBattle.className = 'rem-section-label';
        lblCardBattle.textContent = 'Карточные бои';
        body.appendChild(lblCardBattle);

        const cbBtnRow = document.createElement('div');
        cbBtnRow.className = 'rem-row';
        cbBtnRow.style.cursor = 'pointer';
        const cbBtnInfo = document.createElement('div');
        cbBtnInfo.className = 'rem-row-info';
        cbBtnInfo.innerHTML = `<div class="rem-row-name">⚔️ Авто-бои</div><div class="rem-row-desc">Настройки авто-рейдов, авто-PVP и фонового режима</div>`;
        cbBtnRow.appendChild(cbBtnInfo);
        const cbArrow = document.createElement('span');
        cbArrow.style.cssText = 'color:#52525b;font-size:16px;';
        cbArrow.textContent = '→';
        cbBtnRow.appendChild(cbArrow);
        cbBtnRow.onclick = () => { document.getElementById('rem-settings-panel')?.classList.remove('open'); openCardBattlePanel(); };
        body.appendChild(cbBtnRow);

        const lblSystem = document.createElement('div');
        lblSystem.className = 'rem-section-label';
        lblSystem.textContent = 'Система';
        body.appendChild(lblSystem);

        const upBtn = document.createElement('button');
        upBtn.className = 'rem-ex-btn primary';
        upBtn.style.cssText = 'width:100%; margin-bottom:12px;';
        upBtn.innerText = '🔄 Проверить обновление';
        upBtn.onclick = () => checkForUpdates();
        body.appendChild(upBtn);

        const lbl2b = document.createElement('div');
        lbl2b.className = 'rem-section-label';
        lbl2b.textContent = 'Обмены';
        body.appendChild(lbl2b);
        body.appendChild(makeSwitchRow('scanButton', '➕ Кнопка сканирования', 'Чекбокс добавления в профилях'));

        const exBtnRow = document.createElement('div');
        exBtnRow.className = 'rem-row';
        exBtnRow.style.cursor = 'pointer';
        const exBtnInfo = document.createElement('div');
        exBtnInfo.className = 'rem-row-info';
        exBtnInfo.innerHTML = `<div class="rem-row-name">🔥 Сканер обменов</div><div class="rem-row-desc">Поиск обменов среди пользователей / гильдии</div>`;
        exBtnRow.appendChild(exBtnInfo);
        const exArrow = document.createElement('span');
        exArrow.style.cssText = 'color:#52525b;font-size:16px;';
        exArrow.textContent = '→';
        exBtnRow.appendChild(exArrow);
        exBtnRow.onclick = () => { document.getElementById('rem-settings-panel')?.classList.remove('open'); openExchangePanel(); };
        body.appendChild(exBtnRow);

        const lbl3 = document.createElement('div');
        lbl3.className = 'rem-section-label';
        lbl3.textContent = 'Фон сайта';
        body.appendChild(lbl3);

        const bgToggleRow = makeSwitchRow('customBgEnabled', '🖼️ Кастомный фон', 'Своё видео, гифка или фото');
        body.appendChild(bgToggleRow);

        const bgSub = document.createElement('div');
        bgSub.className = 'rem-sub-row';
        bgSub.id = 'rem-bg-sub';
        bgSub.style.display = cfg.customBgEnabled ? 'flex' : 'none';

        const urlLabel = document.createElement('div');
        urlLabel.className = 'rem-sub-row-label';
        urlLabel.textContent = 'URL (видео .webm/.mp4, гифка, фото)';
        bgSub.appendChild(urlLabel);

        const urlInput = document.createElement('input');
        urlInput.type = 'text';
        urlInput.className = 'rem-url-input';
        urlInput.placeholder = 'https://... или вставьте ссылку';
        urlInput.value = cfg.customBgUrl || '';
        urlInput.addEventListener('change', () => {
            saveSetting('customBgUrl', urlInput.value.trim());
            applyCustomBackground();
        });
        bgSub.appendChild(urlInput);

        const uploadBtn = document.createElement('button');
        uploadBtn.className = 'rem-upload-btn';
        uploadBtn.textContent = '📂 Загрузить файл с компьютера';
        const fileInput = document.createElement('input');
        fileInput.type = 'file';
        fileInput.accept = 'image/*,video/*,.gif,.webm,.mp4,.webp';
        fileInput.style.display = 'none';
        fileInput.addEventListener('change', () => {
            const file = fileInput.files[0];
            if (!file) return;
            const reader = new FileReader();
            reader.onload = (e) => {
                const dataUrl = e.target.result;
                saveSetting('customBgUrl', dataUrl);
                urlInput.value = '(локальный файл: ' + file.name + ')';
                applyCustomBackground();
            };
            reader.readAsDataURL(file);
        });
        uploadBtn.onclick = () => fileInput.click();
        bgSub.appendChild(fileInput);
        bgSub.appendChild(uploadBtn);

        const opacLabel = document.createElement('div');
        opacLabel.className = 'rem-sub-row-label';
        const opacVal = document.createElement('span');
        opacVal.textContent = cfg.customBgOpacity + '%';
        opacLabel.innerHTML = 'Яркость фона: ';
        opacLabel.appendChild(opacVal);
        bgSub.appendChild(opacLabel);

        const opacSlider = document.createElement('input');
        opacSlider.type = 'range';
        opacSlider.className = 'rem-slider';
        opacSlider.min = 10; opacSlider.max = 100; opacSlider.step = 5;
        opacSlider.value = cfg.customBgOpacity;
        opacSlider.addEventListener('input', () => {
            opacVal.textContent = opacSlider.value + '%';
            saveSetting('customBgOpacity', Number(opacSlider.value));
            applyCustomBackground();
        });
        bgSub.appendChild(opacSlider);

        const blurLabel = document.createElement('div');
        blurLabel.className = 'rem-sub-row-label';
        const blurVal = document.createElement('span');
        blurVal.textContent = cfg.customBgBlur + 'px';
        blurLabel.innerHTML = 'Размытие: ';
        blurLabel.appendChild(blurVal);
        bgSub.appendChild(blurLabel);

        const blurSlider = document.createElement('input');
        blurSlider.type = 'range';
        blurSlider.className = 'rem-slider';
        blurSlider.min = 0; blurSlider.max = 20; blurSlider.step = 1;
        blurSlider.value = cfg.customBgBlur;
        blurSlider.addEventListener('input', () => {
            blurVal.textContent = blurSlider.value + 'px';
            saveSetting('customBgBlur', Number(blurSlider.value));
            applyCustomBackground();
        });
        bgSub.appendChild(blurSlider);

        const fitLabel = document.createElement('div');
        fitLabel.className = 'rem-sub-row-label';
        fitLabel.textContent = 'Масштаб:';
        const fitSelect = document.createElement('select');
        fitSelect.className = 'rem-select';
        [['cover', 'Заполнить'], ['contain', 'Вписать'], ['fill', 'Растянуть']].forEach(([v, t]) => {
            const opt = document.createElement('option');
            opt.value = v; opt.textContent = t;
            if (cfg.customBgFit === v) opt.selected = true;
            fitSelect.appendChild(opt);
        });
        fitSelect.addEventListener('change', () => {
            saveSetting('customBgFit', fitSelect.value);
            applyCustomBackground();
        });
        fitLabel.appendChild(fitSelect);
        bgSub.appendChild(fitLabel);

        const resetBgBtn = document.createElement('button');
        resetBgBtn.className = 'rem-upload-btn';
        resetBgBtn.style.borderStyle = 'solid';
        resetBgBtn.textContent = '🗑️ Убрать фон';
        resetBgBtn.onclick = () => {
            saveSetting('customBgUrl', '');
            saveSetting('customBgEnabled', false);
            urlInput.value = '';
            const sw = bgToggleRow.querySelector('input[type=checkbox]');
            if (sw) sw.checked = false;
            document.getElementById('rem-bg-sub').style.display = 'none';
            applyCustomBackground();
        };
        bgSub.appendChild(resetBgBtn);

        body.appendChild(bgSub);

        bgToggleRow.querySelector('input').addEventListener('change', function () {
            document.getElementById('rem-bg-sub').style.display = this.checked ? 'flex' : 'none';
            if (this.checked) applyCustomBackground();
            else removeCustomBackground();
        });

        const lblTheme = document.createElement('div');
        lblTheme.className = 'rem-section-label';
        lblTheme.textContent = 'Оформление сайта';
        body.appendChild(lblTheme);

        const themeRow = document.createElement('div');
        themeRow.style.cssText = 'padding:4px 16px 8px;display:flex;flex-direction:column;gap:8px;';

        const themeLabel = document.createElement('div');
        themeLabel.style.cssText = 'font-size:11px;color:#71717a;';
        themeLabel.textContent = 'Цвет кнопок (Основа):';
        themeRow.appendChild(themeLabel);

        const presetGrid = document.createElement('div');
        presetGrid.style.cssText = 'display:flex;flex-wrap:wrap;gap:6px;';
        THEME_PRESETS.forEach(preset => {
            const btn = document.createElement('button');
            btn.title = preset.name;
            btn.style.cssText = `
                width:22px;height:22px;border-radius:50%;cursor:pointer;
                border:2px solid ${cfg.themeButtonBg === preset.value ? '#fff' : 'transparent'};
                background:${preset.value || '#3f3f46'};
                transition:all .15s;outline:none;flex-shrink:0;
            `;
            if (!preset.value) {
                btn.textContent = '✕';
                btn.style.fontSize = '10px';
                btn.style.color = '#a1a1aa';
            }
            btn.onclick = () => {
                saveSetting('themeButtonBg', preset.value);
                applyTheme();
                presetGrid.querySelectorAll('button').forEach((b, i) => {
                    b.style.borderColor = THEME_PRESETS[i].value === preset.value ? '#fff' : 'transparent';
                });
                customBtnInput.value = preset.value || '#3b82f6';
            };
            presetGrid.appendChild(btn);
        });
        themeRow.appendChild(presetGrid);

        const customBtnRow = document.createElement('div');
        customBtnRow.style.cssText = 'display:flex;align-items:center;gap:8px;';
        const customBtnLabel = document.createElement('span');
        customBtnLabel.style.cssText = 'font-size:11px;color:#71717a;white-space:nowrap;width:100px;';
        customBtnLabel.textContent = 'Свой цвет кнопок:';
        const customBtnInput = document.createElement('input');
        customBtnInput.type = 'color';
        customBtnInput.value = cfg.themeButtonBg || '#3b82f6';
        customBtnInput.style.cssText = 'width:32px;height:24px;border:1px solid #3f3f46;border-radius:6px;background:#0c0c0c;cursor:pointer;padding:2px;';
        const customBtnReset = document.createElement('button');
        customBtnReset.innerHTML = '✕';
        customBtnReset.style.cssText = 'background:none;border:none;color:#a1a1aa;cursor:pointer;font-size:12px;outline:none;';
        customBtnReset.title = 'Сбросить цвет кнопок';
        customBtnReset.onclick = () => { saveSetting('themeButtonBg', ''); customBtnInput.value = '#3b82f6'; applyTheme(); presetGrid.querySelectorAll('button').forEach(b => b.style.borderColor = 'transparent'); };
        customBtnInput.addEventListener('input', () => {
            saveSetting('themeButtonBg', customBtnInput.value);
            applyTheme();
            presetGrid.querySelectorAll('button').forEach((b, i) => {
                b.style.borderColor = THEME_PRESETS[i].value === customBtnInput.value ? '#fff' : 'transparent';
            });
        });
        customBtnRow.appendChild(customBtnLabel);
        customBtnRow.appendChild(customBtnInput);
        customBtnRow.appendChild(customBtnReset);
        themeRow.appendChild(customBtnRow);

        const customAccentRow = document.createElement('div');
        customAccentRow.style.cssText = 'display:flex;align-items:center;gap:8px;margin-top:4px;';
        const customAccentLabel = document.createElement('span');
        customAccentLabel.style.cssText = 'font-size:11px;color:#71717a;white-space:nowrap;width:100px;';
        customAccentLabel.textContent = 'Цвет акцентов:';
        const customAccentInput = document.createElement('input');
        customAccentInput.type = 'color';
        customAccentInput.value = cfg.themeAccent || '#3b82f6';
        customAccentInput.style.cssText = 'width:32px;height:24px;border:1px solid #3f3f46;border-radius:6px;background:#0c0c0c;cursor:pointer;padding:2px;';
        const customAccentReset = document.createElement('button');
        customAccentReset.innerHTML = '✕';
        customAccentReset.style.cssText = 'background:none;border:none;color:#a1a1aa;cursor:pointer;font-size:12px;outline:none;';
        customAccentReset.title = 'Сбросить цвет акцентов';
        customAccentReset.onclick = () => { saveSetting('themeAccent', ''); customAccentInput.value = '#3b82f6'; applyTheme(); };
        customAccentInput.addEventListener('input', () => {
            saveSetting('themeAccent', customAccentInput.value);
            applyTheme();
        });
        customAccentRow.appendChild(customAccentLabel);
        customAccentRow.appendChild(customAccentInput);
        customAccentRow.appendChild(customAccentReset);
        themeRow.appendChild(customAccentRow);

        const nameColorRow = document.createElement('div');
        nameColorRow.style.cssText = 'display:flex;align-items:center;gap:8px;margin-top:4px;';
        const nameColorLabel = document.createElement('span');
        nameColorLabel.style.cssText = 'font-size:11px;color:#71717a;white-space:nowrap;width:100px;';
        nameColorLabel.textContent = 'Цвет имени:';
        const nameColorInput = document.createElement('input');
        nameColorInput.type = 'color';
        nameColorInput.value = cfg.themeNameColor || '#ffffff';
        nameColorInput.style.cssText = 'width:32px;height:24px;border:1px solid #3f3f46;border-radius:6px;background:#0c0c0c;cursor:pointer;padding:2px;';
        const nameColorReset = document.createElement('button');
        nameColorReset.innerHTML = '✕';
        nameColorReset.style.cssText = 'background:none;border:none;color:#a1a1aa;cursor:pointer;font-size:12px;outline:none;';
        nameColorReset.title = 'Сбросить цвет имени';
        nameColorReset.onclick = () => { saveSetting('themeNameColor', ''); nameColorInput.value = '#ffffff'; applyTheme(); };
        nameColorInput.addEventListener('input', () => { saveSetting('themeNameColor', nameColorInput.value); applyTheme(); });
        nameColorRow.appendChild(nameColorLabel);
        nameColorRow.appendChild(nameColorInput);
        nameColorRow.appendChild(nameColorReset);
        themeRow.appendChild(nameColorRow);

        const nameFontRow = document.createElement('div');
        nameFontRow.style.cssText = 'display:flex;align-items:center;gap:8px;margin-top:4px;';
        const nameFontLabel = document.createElement('span');
        nameFontLabel.style.cssText = 'font-size:11px;color:#71717a;white-space:nowrap;width:100px;';
        nameFontLabel.textContent = 'Шрифт имени:';

        const nameFontInput = document.createElement('select');
        nameFontInput.style.cssText = 'flex:1;height:24px;border:1px solid #3f3f46;border-radius:6px;background:#0c0c0c;color:#fff;padding:0 6px;font-size:12px;outline:none;cursor:pointer;';
        const fonts = [
            { v: '', t: 'По умолчанию' },
            { v: 'Comic Sans MS', t: 'Comic Sans' },
            { v: 'Caveat', t: 'Caveat (Рукописный)' },
            { v: 'Comfortaa', t: 'Comfortaa (Округлый)' },
            { v: 'Lobster', t: 'Lobster (Объемный)' },
            { v: 'Pacifico', t: 'Pacifico (Винтаж)' },
            { v: 'Oswald', t: 'Oswald (Строгий)' },
            { v: 'Press Start 2P', t: 'Пиксельный (8-bit)' },
            { v: 'Marmelad', t: 'Marmelad (Плавный)' },
            { v: 'Russo One', t: 'Russo One (Жирный/Квадратный)' },
            { v: 'Jura', t: 'Jura (Техно)' },
            { v: 'Marck Script', t: 'Marck Script (Каллиграфия)' },
            { v: 'Philosopher', t: 'Philosopher (Изящный)' },
            { v: 'Amatic SC', t: 'Amatic SC (Рисованный узкий)' },
            { v: 'Neucha', t: 'Neucha (Веселый)' },
            { v: 'Underdog', t: 'Underdog (Необычный)' }
        ];
        fonts.forEach(f => {
            const opt = document.createElement('option');
            opt.value = opt.textContent = f.v;
            opt.textContent = f.t;
            if (cfg.themeNameFont === f.v) opt.selected = true;
            nameFontInput.appendChild(opt);
        });

        const nameFontReset = document.createElement('button');
        nameFontReset.innerHTML = '✕';
        nameFontReset.style.cssText = 'background:none;border:none;color:#a1a1aa;cursor:pointer;font-size:12px;outline:none;';
        nameFontReset.title = 'Сбросить шрифт имени';
        nameFontReset.onclick = () => { saveSetting('themeNameFont', ''); nameFontInput.value = ''; applyTheme(); };
        nameFontInput.addEventListener('change', () => { saveSetting('themeNameFont', nameFontInput.value); applyTheme(); });
        nameFontRow.appendChild(nameFontLabel);
        nameFontRow.appendChild(nameFontInput);
        nameFontRow.appendChild(nameFontReset);
        themeRow.appendChild(nameFontRow);

        const menuBgRow = document.createElement('div');
        menuBgRow.style.cssText = 'display:flex;align-items:center;gap:8px;margin-top:4px;';
        const menuBgLabel = document.createElement('span');
        menuBgLabel.style.cssText = 'font-size:11px;color:#71717a;white-space:nowrap;flex:1;';
        menuBgLabel.textContent = 'Фон меню и карточек:';
        const menuBgInput = document.createElement('input');
        menuBgInput.type = 'color';
        menuBgInput.value = cfg.themeMenuBg || '#18181b';
        menuBgInput.style.cssText = 'width:32px;height:24px;border:1px solid #3f3f46;border-radius:6px;background:#0c0c0c;cursor:pointer;padding:2px;';
        const menuBgReset = document.createElement('button');
        menuBgReset.innerHTML = '✕';
        menuBgReset.style.cssText = 'background:none;border:none;color:#a1a1aa;cursor:pointer;font-size:12px;outline:none;';
        menuBgReset.title = 'Сбросить фон меню';
        menuBgReset.onclick = () => { saveSetting('themeMenuBg', ''); menuBgInput.value = '#18181b'; applyTheme(); };
        menuBgInput.addEventListener('input', () => { saveSetting('themeMenuBg', menuBgInput.value); applyTheme(); });
        menuBgRow.appendChild(menuBgLabel);
        menuBgRow.appendChild(menuBgInput);
        menuBgRow.appendChild(menuBgReset);
        themeRow.appendChild(menuBgRow);

        const siteBgRow = document.createElement('div');
        siteBgRow.style.cssText = 'display:flex;align-items:center;gap:8px;margin-top:4px;';
        const siteBgLabel = document.createElement('span');
        siteBgLabel.style.cssText = 'font-size:11px;color:#71717a;white-space:nowrap;flex:1;';
        siteBgLabel.textContent = 'Фон сайта (без картинки):';
        const siteBgInput = document.createElement('input');
        siteBgInput.type = 'color';
        siteBgInput.value = cfg.themeSiteBg || '#09090b';
        siteBgInput.style.cssText = 'width:32px;height:24px;border:1px solid #3f3f46;border-radius:6px;background:#0c0c0c;cursor:pointer;padding:2px;';
        const siteBgReset = document.createElement('button');
        siteBgReset.innerHTML = '✕';
        siteBgReset.style.cssText = 'background:none;border:none;color:#a1a1aa;cursor:pointer;font-size:12px;outline:none;';
        siteBgReset.title = 'Сбросить фон сайта';
        siteBgReset.onclick = () => { saveSetting('themeSiteBg', ''); siteBgInput.value = '#09090b'; applyTheme(); };
        siteBgInput.addEventListener('input', () => { saveSetting('themeSiteBg', siteBgInput.value); applyTheme(); });
        siteBgRow.appendChild(siteBgLabel);
        siteBgRow.appendChild(siteBgInput);
        siteBgRow.appendChild(siteBgReset);
        themeRow.appendChild(siteBgRow);

        body.appendChild(themeRow);

        const lbl4 = document.createElement('div');
        lbl4.className = 'rem-section-label';
        lbl4.textContent = 'Исправления';
        body.appendChild(lbl4);
        body.appendChild(makeSwitchRow('fixMode', '🔧 FIX: переход на тайтл', 'Перейти на карты тайтла вместо простой ссылки'));

        panel.appendChild(body);

        const footer = document.createElement('div');
        footer.className = 'rem-panel-footer';

        const btnRefresh = document.createElement('button');
        btnRefresh.className = 'rem-action-btn';
        btnRefresh.textContent = '🔄 Обновить';
        btnRefresh.title = 'Принудительно перезагрузить инвентарь';
        btnRefresh.onclick = () => {
            storageRemove(MY_INV_KEY);
            storageRemove(MY_WISH_KEY);
            Manager.loaded = false;
            Manager.wishLoaded = false;
            Manager.syncing = false;
            Manager.syncingWishes = false;
            Manager.covers = new Set();
            Manager.wishCovers = new Set();
            Manager.sync();
            Manager.syncWishes();
            document.getElementById('rem-settings-panel')?.classList.remove('open');
        };

        const btnProfile = document.createElement('button');
        btnProfile.className = 'rem-action-btn';
        btnProfile.textContent = '👤 Профиль';
        btnProfile.title = 'Ввести ссылку на другой профиль';
        btnProfile.onclick = () => {
            storageRemove(MY_INV_KEY);
            storageRemove(MY_WISH_KEY);
            storageRemove(MY_ID_KEY);
            document.getElementById('rem-settings-panel')?.classList.remove('open');
            Manager.userId = null;
            Manager.loaded = false;
            Manager.wishLoaded = false;
            Manager.syncing = false;
            Manager.syncingWishes = false;
            Manager.covers = new Set();
            Manager.wishCovers = new Set();
            Manager.promptId();
        };

        const btnReset = document.createElement('button');
        btnReset.className = 'rem-action-btn danger';
        btnReset.textContent = '🗑️ Сброс';
        btnReset.title = 'Удалить все данные расширения и перезагрузить страницу';
        btnReset.onclick = () => {
            if (confirm('Сбросить все данные расширения?')) {
                storageRemove(MY_INV_KEY);
                storageRemove(MY_WISH_KEY);
                storageRemove(MY_ID_KEY);
                location.reload();
            }
        };

        footer.appendChild(btnRefresh);
        footer.appendChild(btnProfile);
        footer.appendChild(btnReset);
        panel.appendChild(footer);

        return panel;
    }

    function togglePanel() {
        const panel = document.getElementById('rem-settings-panel');
        if (!panel) return;
        panel.classList.toggle('open');
    }

    function applyCustomBackground() {
        if (!cfg.customBgEnabled || !cfg.customBgUrl) { removeCustomBackground(); return; }

        const url = cfg.customBgUrl;
        const opacity = (cfg.customBgOpacity ?? 80) / 100;
        const blur = cfg.customBgBlur ?? 0;
        const fit = cfg.customBgFit || 'cover';

        removeCustomBackground();

        const isVideo = /\.(webm|mp4|ogg|ogv)(\?.*)?$/.test(url) || url.startsWith('data:video');

        let bgStyle = document.getElementById('rem-bg-override');
        if (!bgStyle) {
            bgStyle = document.createElement('style');
            bgStyle.id = 'rem-bg-override';
            (document.head || document.documentElement).appendChild(bgStyle);
        }
        bgStyle.textContent = `
            body, #__next,
            [data-theme="light"] body, [data-theme="dark"] body,
            [data-sentry-element="AppLayoutRoot"],
            [data-sentry-component="AppLayoutRoot"],
            [data-sentry-element="AppLayoutContent"],
            [data-sentry-element="EntityLayoutRoot"] {
                 background: transparent !important;
                 background-color: transparent !important;
                 background-image: none !important;
            }

            .cs-layout-root::before {
                 display: none !important;
                 background: none !important;
            }

            [data-sentry-component="Header"], header {
                background-color: var(--chakra-colors-gray-800, #18181b) !important;
                backdrop-filter: none !important;
            }
            [data-theme="light"] [data-sentry-component="Header"],
            [data-theme="light"] header {
                background-color: var(--chakra-colors-white, #ffffff) !important;
            }

            [data-sentry-component="WallpaperBackground"],
            .custom-background-video, video.custom-background-video {
                z-index: -2 !important;
            }
        `;

        let el;
        if (isVideo) {
            el = document.createElement('video');
            el.src = url;
            el.autoplay = true;
            el.loop = true;
            el.muted = true;
            el.playsInline = true;
            el.play().catch(() => { });
        } else {
            el = document.createElement('img');
            el.src = url;
            el.onerror = () => console.warn('REM BG: не удалось загрузить фон:', url.substring(0, 80));
        }

        el.id = 'rem-custom-bg';
        el.style.cssText = [
            'position:fixed',
            'top:0', 'left:0',
            'width:100%', 'height:100vh',
            `object-fit:${fit}`,
            'object-position:center center',
            'z-index:-1',
            'pointer-events:none',
            'will-change:transform',
            'transform:translateZ(0)',
            `opacity:${opacity}`,
            `filter:${blur > 0 ? 'blur(' + blur + 'px)' : 'none'}`,
            'transition:opacity .3s',
        ].join(';');

        document.body.insertBefore(el, document.body.firstChild);
    }

    function removeCustomBackground() {
        const old = document.getElementById('rem-custom-bg');
        if (old) old.remove();
        const bgStyle = document.getElementById('rem-bg-override');
        if (bgStyle) bgStyle.remove();
    }

    function hexToHsl(hex) {
        let r = parseInt(hex.slice(1, 3), 16) / 255,
            g = parseInt(hex.slice(3, 5), 16) / 255,
            b = parseInt(hex.slice(5, 7), 16) / 255;
        const max = Math.max(r, g, b), min = Math.min(r, g, b);
        let h, s, l = (max + min) / 2;
        if (max === min) { h = s = 0; } else {
            const d = max - min; s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
            switch (max) { case r: h = (g - b) / d + (g < b ? 6 : 0); break; case g: h = (b - r) / d + 2; break; default: h = (r - g) / d + 4; }
            h /= 6;
        }
        return `${Math.round(h * 360)} ${Math.round(s * 100)}% ${Math.round(l * 100)}%`;
    }

    function applyTheme() {
        let themeEl = document.getElementById('rem-theme-override');
        if (!themeEl) {
            themeEl = document.createElement('style');
            themeEl.id = 'rem-theme-override';
            (document.head || document.documentElement).appendChild(themeEl);
        }

        let css = '';

        if (cfg.themeNameFont && cfg.themeNameFont !== '') {
            if (cfg.themeNameFont !== 'Comic Sans MS') {
                const fontNameUrl = cfg.themeNameFont.replace(/ /g, '+');
                css += `@import url('https://fonts.googleapis.com/css2?family=${fontNameUrl}&display=swap');\n`;
            }
        }

        if (cfg.themeAccent) {
            const accent = cfg.themeAccent;
            let darker = accent;
            const darkerMatch = accent.match(/#([0-9a-f]{6})/i);
            if (darkerMatch) {
                const dr = Math.max(0, parseInt(darkerMatch[1].slice(0, 2), 16) - 30),
                    dg = Math.max(0, parseInt(darkerMatch[1].slice(2, 4), 16) - 30),
                    db = Math.max(0, parseInt(darkerMatch[1].slice(4, 6), 16) - 30);
                darker = '#' + [dr, dg, db].map(v => v.toString(16).padStart(2, '0')).join('');
            }
            css += `
                input:checked + .rem-sw-track { background: ${accent} !important; }
                .rem-progress-fill { background: linear-gradient(90deg, ${accent}, ${darker}) !important; }
            `;
        }

        if (cfg.themeButtonBg) {
            const btnBg = cfg.themeButtonBg;
            let darker = btnBg;
            const darkerMatch = btnBg.match(/#([0-9a-f]{6})/i);
            if (darkerMatch) {
                const dr = Math.max(0, parseInt(darkerMatch[1].slice(0, 2), 16) - 30),
                    dg = Math.max(0, parseInt(darkerMatch[1].slice(2, 4), 16) - 30),
                    db = Math.max(0, parseInt(darkerMatch[1].slice(4, 6), 16) - 30);
                darker = '#' + [dr, dg, db].map(v => v.toString(16).padStart(2, '0')).join('');
            }
            css += `
                .bg-primary,
                [data-state="active"][class*="bg-primary"],
                [data-state="open"][class*="bg-primary"],
                [aria-selected="true"][class*="bg-primary"] {
                    background-color: ${btnBg} !important;
                    border-color: ${btnBg} !important;
                    color: #fff !important;
                }
                .bg-primary:hover,
                [data-state="active"][class*="bg-primary"]:hover,
                [data-state="open"][class*="bg-primary"]:hover,
                [aria-selected="true"][class*="bg-primary"]:hover {
                    background-color: ${darker} !important;
                    border-color: ${darker} !important;
                }
            `;
        }

        if (cfg.themeNameColor || cfg.themeNameFont) {
            css += `
                .cs-layout-title-text, .cs-layout-title .cs-text {
                    ${cfg.themeNameColor ? `color: ${cfg.themeNameColor} !important;` : ''}
                    ${cfg.themeNameFont ? `font-family: "${cfg.themeNameFont}", sans-serif !important;` : ''}
                }
            `;
        }

        if (cfg.themeMenuBg) {
            const mBg = hexToHsl(cfg.themeMenuBg);
            css += `
                :root {
                    --popover: ${mBg} !important;
                    --card: ${mBg} !important;
                    --secondary: ${mBg} !important;
                    --muted: ${mBg} !important;
                }
                .bg-popover, .bg-secondary, .bg-card, .bg-muted, .cs-account-menu, [data-radix-menu-content] {
                    background-color: ${cfg.themeMenuBg} !important;
                }
                .rem-settings-panel, .rem-box, .rem-progress-box {
                    background-color: ${cfg.themeMenuBg} !important;
                    border-color: rgba(255,255,255,0.1) !important;
                }
            `;
        }

        if (cfg.themeSiteBg && !cfg.customBgEnabled) {
            const sBg = hexToHsl(cfg.themeSiteBg);
            css += `
                :root {
                    --background: ${sBg} !important;
                }
                body, #__next, .bg-background {
                    background-color: ${cfg.themeSiteBg} !important;
                }
            `;
        }

        themeEl.textContent = css;
    }

    function removeTheme() {
        const el = document.getElementById('rem-theme-override');
        if (el) el.textContent = '';
    }

    const HOT_RANKS_EX = ['EV', 'RE', 'S', 'A', 'B', 'C', 'D', 'E', 'F'];
    function getCardRankLetter(card) {
        let rk = card.rank || card.card_rank || '';
        if (typeof rk === 'object') rk = rk.name || rk.rank || '';
        rk = String(rk).trim().toUpperCase();
        if (!rk) return '?';
        const m = rk.match(/RANK_([A-Z]+)/i);
        if (m) return m[1].toUpperCase();
        for (const l of HOT_RANKS_EX) if (rk.includes(l)) return l;
        return rk.slice(0, 2) || '?';
    }
    function getCardCharName(card) {
        if (card.character && typeof card.character === 'object' && card.character.name) return card.character.name;
        return card.name || '';
    }

    function isBetterCardInfo(newC, oldC) {
        if (!oldC) return true;
        const newN = getCardCharName(newC);
        const oldN = getCardCharName(oldC);
        const isGeneric = (n) => !n || n.startsWith('Карта #');
        if (!isGeneric(newN) && isGeneric(oldN)) return true;
        if (newC.character && !oldC.character) return true;
        return false;
    }

    async function fetchUserTradeData(userId) {
        const DOMAIN = SITE_DOMAIN;
        const fetchPages = async (type) => {
            let items = [], pg = 1, run = true;
            while (run) {
                const url = `${DOMAIN}/api/v2/inventory/wishes/users/${userId}/?wish_type=${type}&page=${pg}`;
                const data = await Manager.req(url);
                if (!data) break;
                const list = data.results || [];
                if (!list.length) break;
                list.forEach(it => { if (it.card) items.push(it.card); });
                if (!data.next) run = false; else { pg++; await new Promise(r => setTimeout(r, 80)); }
            }
            return items;
        };
        const fetchInv = async () => {
            let items = [], pg = 1, run = true;
            while (run) {
                const url = `${API_DOMAIN}/api/v2/inventory/${userId}/?type=cards&count=50&page=${pg}`;
                const data = await Manager.req(url);
                if (!data) break;
                const list = data.results || [];
                if (!list.length) break;
                list.forEach(it => { if (it.card) items.push(it.card); });
                if (!data.next) run = false; else { pg++; await new Promise(r => setTimeout(r, 50)); }
            }
            return items;
        };
        let username = `User_${userId}`;
        let sex = 0;
        const udata = await Manager.req(`${API_DOMAIN}/api/v2/users/${userId}/`);
        if (udata) {
            username = udata.username || (udata.content && udata.content.username) || username;
            const maybeSex = udata.sex !== undefined ? udata.sex : (udata.content && udata.content.sex !== undefined ? udata.content.sex : 0);
            sex = parseInt(maybeSex) || 0;
        }
        const [wants, offers, inventory] = await Promise.all([fetchPages(1), fetchPages(2), fetchInv()]);
        return { profile: { username, id: userId, sex }, wants, offers, inventory };
    }

    async function gatherTradesEx(userIds, progressCb) {
        const usersData = {};
        let done = 0;
        const total = userIds.length;
        const queue = [...userIds];
        const workers = [];
        for (let i = 0; i < Math.min(4, queue.length); i++) {
            workers.push((async () => {
                while (queue.length > 0) {
                    const uid = queue.shift();
                    try {
                        const data = await fetchUserTradeData(uid);
                        if (data) usersData[uid] = data;
                    } catch (e) { }
                    done++;
                    if (progressCb) progressCb(done, total);
                }
            })());
        }
        await Promise.all(workers);
        const cardsDb = {};
        for (const [uid, data] of Object.entries(usersData)) {
            const prof = data.profile;
            for (const c of data.wants) {
                const cid = String(c.id);
                if (!cid) continue;
                if (!cardsDb[cid]) cardsDb[cid] = { card: c, wants: [], offers: [], inventory: [] };
                else if (isBetterCardInfo(c, cardsDb[cid].card)) cardsDb[cid].card = c;
                cardsDb[cid].wants.push(prof);
            }
            for (const c of data.offers) {
                const cid = String(c.id);
                if (!cid) continue;
                if (!cardsDb[cid]) cardsDb[cid] = { card: c, wants: [], offers: [], inventory: [] };
                else if (isBetterCardInfo(c, cardsDb[cid].card)) cardsDb[cid].card = c;
                cardsDb[cid].offers.push(prof);
            }
            for (const c of (data.inventory || [])) {
                const cid = String(c.id);
                if (!cid) continue;
                if (!cardsDb[cid]) cardsDb[cid] = { card: c, wants: [], offers: [], inventory: [] };
                else if (isBetterCardInfo(c, cardsDb[cid].card)) cardsDb[cid].card = c;
                cardsDb[cid].inventory.push(prof);
            }
        }
        const validTrades = [];
        for (const [cid, info] of Object.entries(cardsDb)) {
            const wList = info.wants, oList = info.offers, invList = info.inventory;
            const wFiltered = [], oFiltered = [], invFiltered = [];

            for (const w of wList) if (!wFiltered.find(x => String(x.id) === String(w.id))) wFiltered.push(w);
            for (const o of oList) if (!oFiltered.find(x => String(x.id) === String(o.id))) oFiltered.push(o);

            const oIds = new Set(oFiltered.map(o => String(o.id)));
            for (const inv of invList) {
                if (!oIds.has(String(inv.id))) {
                    if (!invFiltered.find(x => String(x.id) === String(inv.id))) invFiltered.push(inv);
                }
            }

            if (wFiltered.length > 0 || oFiltered.length > 0) {
                validTrades.push({ card_id: cid, card: info.card, buyers: wFiltered, sellers: oFiltered, owners: invFiltered });
            }
        }
        validTrades.sort((a, b) => parseInt(a.card_id) - parseInt(b.card_id));
        return validTrades;
    }

    async function fetchClubMembers(slug) {
        const users = [];
        let pg = 1, run = true;
        const escSlug = encodeURIComponent(slug);
        while (run) {
            const url = `${API_DOMAIN}/api/v2/clubs/${escSlug}/members/?count=100&page=${pg}`;
            const data = await Manager.req(url);
            if (!data) break;
            const list = data.results || [];
            if (!list.length) break;
            list.forEach(it => { if (it.user && it.user.id) users.push(it.user.id); });
            if (!data.next) run = false; else { pg++; await new Promise(r => setTimeout(r, 80)); }
        }
        return users;
    }

    function openExchangePanel() {
        let panel = document.getElementById('rem-exchange-panel');
        let backdrop = document.querySelector('.rem-ex-backdrop');
        if (!panel) {
            backdrop = document.createElement('div');
            backdrop.className = 'rem-ex-backdrop';
            backdrop.onclick = () => closeExchangePanel();
            document.body.appendChild(backdrop);
            panel = document.createElement('div');
            panel.id = 'rem-exchange-panel';
            panel.innerHTML = `
                <div class="rem-ex-header">
                    <div class="rem-ex-title">🔥 Сканер обменов</div>
                    <div class="rem-ex-close" id="rem-ex-close-btn">✕</div>
                </div>
                <div class="rem-ex-body" id="rem-ex-body"></div>
            `;
            document.body.appendChild(panel);
            panel.querySelector('#rem-ex-close-btn').onclick = () => closeExchangePanel();
        }
        backdrop.classList.add('open');
        panel.classList.add('open');
        renderExchangeMain();
    }
    function closeExchangePanel() {
        document.getElementById('rem-exchange-panel')?.classList.remove('open');
        document.querySelector('.rem-ex-backdrop')?.classList.remove('open');
    }

    function openCardBattlePanel() {
        let panel = document.getElementById('rem-cardbattle-panel');
        let backdrop = document.querySelector('.rem-cb-backdrop');
        if (!panel) {
            backdrop = document.createElement('div');
            backdrop.className = 'rem-cb-backdrop';
            backdrop.onclick = () => closeCardBattlePanel();
            document.body.appendChild(backdrop);
            panel = document.createElement('div');
            panel.id = 'rem-cardbattle-panel';
            panel.innerHTML = `
                <div class="rem-ex-header">
                    <div class="rem-ex-title">⚔️ Авто-бои</div>
                    <div class="rem-ex-close" id="rem-cb-close-btn">✕</div>
                </div>
                <div class="rem-ex-body" id="rem-cb-body"></div>
            `;
            document.body.appendChild(panel);
            panel.querySelector('#rem-cb-close-btn').onclick = () => closeCardBattlePanel();
        }
        backdrop.classList.add('open');
        panel.classList.add('open');
        renderCardBattleMain();
    }
    function closeCardBattlePanel() {
        document.getElementById('rem-cardbattle-panel')?.classList.remove('open');
        document.querySelector('.rem-cb-backdrop')?.classList.remove('open');
    }
    function renderCardBattleMain() {
        const body = document.getElementById('rem-cb-body');
        if (!body) return;
        body.innerHTML = '';
        const wrapper = document.createElement('div');
        wrapper.style.cssText = 'display:flex;flex-direction:column;gap:16px;';
        const rowRaid = makeSwitchRow('autoRaidEnabled', '⚔️ Авто-рейд', 'Автоматически проходить рейд при наличии энергии');
        const raidOptions = [
            { value: 1, text: 'Рейд 1 (4 энергии)' },
            { value: 2, text: 'Рейд 2 (5 энергии)' },
            { value: 3, text: 'Рейд 3 (6 энергии)' },
            { value: 4, text: 'Рейд 4 (7 энергии)' },
            { value: 5, text: 'Рейд 5 (8 энергии)' },
            { value: 6, text: 'Рейд 6 (9 энергии)' },
            { value: 7, text: 'Рейд 7 (10 энергии)' },
            { value: 8, text: 'Рейд 8 (11 энергии)' },
            { value: 9, text: 'Рейд 9 (12 энергии)' },
            { value: 10, text: 'Рейд 10 (13 энергии)' }
        ];
        const rowLocation = makeSelectRow('autoRaidLocation', '📍 Выбор рейда', raidOptions);
        const rowPvp = makeSwitchRow('autoPvpEnabled', '🏆 Авто-PVP', 'Автоматически искать PVP матч каждые 31 сек.');
        const rowKeepAlive = makeSwitchRow('audioKeepAliveEnabled', '🔊 Фоновый режим', 'Предотвращает сон браузера на телефоне с помощью аудио');
        wrapper.appendChild(rowRaid);
        wrapper.appendChild(rowLocation);
        wrapper.appendChild(rowPvp);
        wrapper.appendChild(rowKeepAlive);
        body.appendChild(wrapper);
    }

    function fmtAgo(ts) {
        const ago = Math.round((Date.now() - ts) / 60000);
        if (ago < 1) return 'только что';
        if (ago < 60) return `${ago} мин. назад`;
        if (ago < 1440) return `${Math.round(ago / 60)} ч. назад`;
        return `${Math.round(ago / 1440)} дн. назад`;
    }

    function renderExchangeMain() {
        const body = document.getElementById('rem-ex-body');
        if (!body) return;
        const hasResults = ExState.results.length > 0;
        const scanList = getScanList();
        const history = getHistory();
        let scanListHtml = '';
        if (scanList.length > 0) {
            scanListHtml = `
                <div class="rem-ex-scanlist">
                    <div class="rem-ex-scanlist-title">👥 Список на сканирование <span class="rem-ex-scanlist-count">(${scanList.length} чел.)</span></div>
                    <div class="rem-ex-scanlist-items" id="rem-ex-scanlist-items">
                        ${scanList.map(u => `
                            <div class="rem-ex-scanlist-item" data-uid="${u.id}">
                                <div class="rem-ex-scanlist-item-info">
                                    <span class="rem-ex-scanlist-item-name">${u.username}</span>
                                    <span class="rem-ex-scanlist-item-id">ID: ${u.id}</span>
                                </div>
                                <button class="rem-ex-scanlist-rm" data-rm-uid="${u.id}" title="Убрать">✕</button>
                            </div>
                        `).join('')}
                    </div>
                    <div class="rem-ex-scanlist-actions">
                        <button class="rem-ex-btn primary" id="rem-ex-scan-list-btn" style="height:36px;">🔍 Сканировать список (${scanList.length})</button>
                        <button class="rem-ex-btn secondary" id="rem-ex-clear-list-btn" style="height:36px;">🗑️ Очистить</button>
                    </div>
                </div>
            `;
        }
        let histHtml = '';
        if (history.length > 0) {
            histHtml = `
                <div class="rem-ex-scanlist" style="margin-bottom:${hasResults ? '0' : '16'}px;">
                    <div class="rem-ex-scanlist-title">📋 История сканирований <span class="rem-ex-scanlist-count">(${history.length})</span></div>
                    <div class="rem-ex-scanlist-items">
                        ${history.map(h => `
                            <div class="rem-ex-scanlist-item" data-hist-id="${h.id}" style="cursor:pointer;">
                                <div class="rem-ex-scanlist-item-info">
                                    <span class="rem-ex-scanlist-item-name" style="color:${ExState.activeHistId === h.id ? '#60a5fa' : '#e4e4e7'};">${h.name}</span>
                                    <span class="rem-ex-scanlist-item-id">${h.count} обменов · ${fmtAgo(h.time)}</span>
                                </div>
                                <button class="rem-ex-scanlist-rm" data-del-hist="${h.id}" title="Удалить">✕</button>
                            </div>
                        `).join('')}
                    </div>
                </div>
            `;
        }
        let cacheBarHtml = '';
        if (hasResults && ExState.lastSource) {
            const h = history.find(x => x.id === ExState.activeHistId);
            const canUpdate = h && h.link && !ExState.scanning;
            cacheBarHtml = `<div style="display:flex;flex-direction:column;gap:12px;margin-bottom:12px;padding:12px;background:#18181b;border:1px solid #27272a;border-radius:10px;">
                <div style="display:flex;align-items:center;justify-content:space-between;">
                    <div style="font-size:12px;color:#71717a;">📦 <strong style="color:#a1a1aa;">${ExState.lastSource}</strong> · Всего: ${ExState.results.length}</div>
                    <div style="display:flex;gap:8px;">
                        ${canUpdate ? `<button class="rem-ex-btn secondary" id="rem-ex-update-btn" style="height:28px;padding:0 12px;font-size:11px;">🔄 Обновить</button>` : ''}
                        <button class="rem-ex-btn secondary" id="rem-ex-close-results" style="height:28px;padding:0 12px;font-size:11px;">✕ Закрыть</button>
                    </div>
                </div>
                <div style="display:flex;gap:8px;">
                    <input type="text" id="rem-search-card" class="rem-ex-input" style="height:32px;font-size:12px;flex:1;" placeholder="🔍 Поиск карты (Название, ID или ссылка...)" value="${(ExState.searchQuery || '').replace(/"/g, '&quot;')}">
                    <input type="text" id="rem-search-user" class="rem-ex-input" style="height:32px;font-size:12px;flex:1;" placeholder="👤 Поиск человека (Имя или ID...)" value="${(ExState.searchUser || '').replace(/"/g, '&quot;')}">
                </div>
            </div>`;
        }
        let progressHtml = '';
        if (ExState.scanning) {
            const pct = ExState.scanTotal ? Math.round((ExState.scanDone / ExState.scanTotal) * 100) : 0;
            progressHtml = `<div class="rem-ex-progress"><div class="rem-ex-ptext">Сканирование <strong>${ExState.scanDone}</strong> / <strong>${ExState.scanTotal}</strong> пользователей... (${pct}%)</div><div class="rem-ex-pbar-track"><div class="rem-ex-pbar-fill" id="rem-ex-pbar" style="width:${pct}%"></div></div></div>`;
        }
        body.innerHTML = `
            <div class="rem-ex-input-row">
                <input class="rem-ex-input" id="rem-ex-input" placeholder="Ссылка на гильдию или ID пользователей" />
                <button class="rem-ex-btn primary" id="rem-ex-scan-btn" style="height:40px;" ${ExState.scanning ? 'disabled' : ''}>${ExState.scanning ? '⏳ Загрузка...' : '🔍 Скан'}</button>
            </div>
            ${scanListHtml}
            ${!hasResults ? histHtml : ''}
            <div id="rem-ex-status">${progressHtml}</div>
            ${hasResults ? cacheBarHtml + '<div id="rem-ex-results-container"></div>' : (history.length === 0 && scanList.length === 0 && !ExState.scanning ? `
                <div class="rem-ex-empty">
                    <div style="font-size:32px;margin-bottom:12px;">🔥</div>
                    <div>Введите ссылку на гильдию<br/>или добавляйте пользователей через профили</div>
                    <div style="margin-top:8px;font-size:12px;color:#3f3f46;">Пример: ${SITE_DOMAIN}/guild/fairy-tail-68c526d1</div>
                    <div style="margin-top:4px;font-size:12px;color:#3f3f46;">или: 12345 67890 11111</div>
                </div>
            ` : '')}
        `;
        if (hasResults) renderExchangeResults();
        document.getElementById('rem-ex-scan-btn').onclick = () => startExchangeScan();
        document.getElementById('rem-ex-input').addEventListener('keypress', e => { if (e.key === 'Enter') startExchangeScan(); });
        document.querySelectorAll('[data-rm-uid]').forEach(btn => {
            btn.onclick = (e) => { e.stopPropagation(); removeFromScanList(btn.getAttribute('data-rm-uid')); renderExchangeMain(); };
        });
        const scanListBtn = document.getElementById('rem-ex-scan-list-btn');
        if (scanListBtn) { scanListBtn.onclick = () => startExchangeScanFromList(); }
        const clearListBtn = document.getElementById('rem-ex-clear-list-btn');
        if (clearListBtn) { clearListBtn.onclick = () => { saveScanList([]); renderExchangeMain(); }; }
        document.querySelectorAll('[data-hist-id]').forEach(row => {
            const handleHistClick = () => {
                const hid = row.getAttribute('data-hist-id');
                const h = history.find(x => x.id === hid);
                const statusEl = document.getElementById('rem-ex-status');
                if (statusEl) statusEl.innerHTML = `<div class="rem-ex-ptext">📂 Загрузка истории <strong>${h ? h.name : ''}</strong>...</div>`;

                setTimeout(() => {
                    const data = loadFromHistory(hid);
                    if (!data || !data.length) {
                        if (statusEl) statusEl.innerHTML = `<div class="rem-ex-ptext" style="color:#ef4444;">❌ Не удалось загрузить данные (возможно, они были удалены или слишком велики)</div>`;
                        return;
                    }
                    ExState.results = data;
                    ExState.lastSource = h ? h.name : 'Из истории';
                    ExState.activeHistId = hid;
                    ExState.currentRank = 'ALL';
                    ExState.page = 0;
                    renderExchangeMain();
                }, 50);
            };
            row.onclick = handleHistClick;
            row.onpointerdown = (e) => {
                if (e.pointerType === 'touch') row._remTouch = true;
            };
            row.onpointerup = (e) => {
                if (e.pointerType === 'touch' && row._remTouch) {
                    row._remTouch = false;
                    handleHistClick();
                }
            };
        });
        document.querySelectorAll('[data-del-hist]').forEach(btn => {
            btn.onclick = (e) => { e.stopPropagation(); deleteFromHistory(btn.getAttribute('data-del-hist')); renderExchangeMain(); };
        });
        const closeBtn = document.getElementById('rem-ex-close-results');
        if (closeBtn) { closeBtn.onclick = () => { ExState.results = []; ExState.lastSource = ''; ExState.activeHistId = null; renderExchangeMain(); }; }
        const updateBtn = document.getElementById('rem-ex-update-btn');
        if (updateBtn) {
            updateBtn.onclick = () => {
                const h = history.find(x => x.id === ExState.activeHistId);
                if (h && h.link) {
                    const input = document.getElementById('rem-ex-input');
                    if (input) input.value = h.link;
                    startExchangeScan();
                }
            };
        }
        const searchCard = document.getElementById('rem-search-card');
        const searchUser = document.getElementById('rem-search-user');
        if (searchCard) searchCard.addEventListener('input', (e) => { ExState.searchQuery = e.target.value.trim(); ExState.page = 0; renderExchangeResults(); });
        if (searchUser) searchUser.addEventListener('input', (e) => { ExState.searchUser = e.target.value.trim(); ExState.page = 0; renderExchangeResults(); });
    }

    async function startExchangeScanFromList() {
        const scanList = getScanList();
        if (!scanList.length) return;
        const statusEl = document.getElementById('rem-ex-status');
        if (!statusEl) return;
        if (ExState.scanning) return;
        ExState.scanning = true;
        const scanBtn = document.getElementById('rem-ex-scan-list-btn');
        if (scanBtn) { scanBtn.disabled = true; scanBtn.textContent = '⏳ Загрузка...'; }
        const userIds = scanList.map(u => parseInt(u.id));
        ExState.scanDone = 0;
        ExState.scanTotal = userIds.length;
        renderExchangeMain();

        const results = await gatherTradesEx(userIds, (done, total, msg) => {
            ExState.scanDone = done;
            ExState.scanTotal = total;
            const pct = Math.round((done / total) * 100);
            const pbar = document.getElementById('rem-ex-pbar');
            const ptext = document.querySelector('.rem-ex-ptext');
            if (pbar) pbar.style.width = pct + '%';
            if (ptext) ptext.innerHTML = msg || `Сканирование <strong>${done}</strong> / <strong>${total}</strong> пользователей... (${pct}%)`;
        });
        const srcName = `Список (${scanList.length} чел.)`;
        ExState.results = results;
        ExState.lastSource = srcName;
        ExState.currentRank = 'ALL';
        ExState.page = 0;
        ExState.scanning = false;
        ExState.activeHistId = saveToHistory(srcName, results, "list");
        renderExchangeMain();
    }

    async function fetchGuildName(slug) {
        try {
            const data = await Manager.req(`${API_DOMAIN}/api/v2/clubs/${encodeURIComponent(slug)}/`);
            if (data) {
                const name = data.name || (data.content && data.content.name);
                if (name) return name;
            }
        } catch (e) { }
        return slug;
    }

    async function startExchangeScan() {
        const input = document.getElementById('rem-ex-input');
        const statusEl = document.getElementById('rem-ex-status');
        if (!input || !statusEl) return;
        const val = input.value.trim();
        if (!val) return;
        if (ExState.scanning) return;
        ExState.scanning = true;
        const scanBtn = document.getElementById('rem-ex-scan-btn');
        if (scanBtn) { scanBtn.disabled = true; scanBtn.textContent = '⏳ Загрузка...'; }
        let userIds = [];
        let srcName = '';
        const clubMatch = val.match(/(?:clubs|guild)\/([^\/\?\s]+)/);
        if (clubMatch) {
            statusEl.innerHTML = `<div class="rem-ex-progress"><div class="rem-ex-ptext">Загрузка гильдии <strong>${clubMatch[1]}</strong>...</div><div class="rem-ex-pbar-track"><div class="rem-ex-pbar-fill" id="rem-ex-pbar" style="width:5%"></div></div></div>`;
            const [members, guildName] = await Promise.all([fetchClubMembers(clubMatch[1]), fetchGuildName(clubMatch[1])]);
            userIds = members;
            srcName = guildName;
            if (!userIds.length) {
                statusEl.innerHTML = `<div class="rem-ex-ptext" style="color:#ef4444;">❌ Не удалось загрузить участников гильдии</div>`;
                ExState.scanning = false;
                if (scanBtn) { scanBtn.disabled = false; scanBtn.textContent = '🔍 Скан'; }
                return;
            }
        } else {
            const links = val.match(/(?:remanga\.org|xn--80aaig9ahr\.xn--c1avg)\/user\/(\d+)/g) || [];
            links.forEach(l => { const m = l.match(/(\d+)/); if (m) userIds.push(parseInt(m[1])); });
            const rawText = val.replace(/https?:\/\/\S+/g, '');
            const nums = rawText.match(/\b(\d+)\b/g) || [];
            nums.forEach(n => userIds.push(parseInt(n)));
            userIds = [...new Set(userIds)];
            srcName = `${userIds.length} пользователей`;
            if (!userIds.length) {
                statusEl.innerHTML = `<div class="rem-ex-ptext" style="color:#ef4444;">❌ Не найдено ID пользователей</div>`;
                ExState.scanning = false;
                if (scanBtn) { scanBtn.disabled = false; scanBtn.textContent = '🔍 Скан'; }
                return;
            }
        }

        ExState.scanDone = 0;
        ExState.scanTotal = userIds.length;
        renderExchangeMain();

        const results = await gatherTradesEx(userIds, (done, total, msg) => {
            ExState.scanDone = done;
            ExState.scanTotal = total;
            const pct = Math.round((done / total) * 100);
            const pbar = document.getElementById('rem-ex-pbar');
            const ptext = document.querySelector('.rem-ex-ptext');
            if (pbar) pbar.style.width = pct + '%';
            if (ptext) ptext.innerHTML = msg || `Сканирование <strong>${done}</strong> / <strong>${total}</strong> пользователей... (${pct}%)`;
        });
        ExState.results = results;
        ExState.lastSource = srcName;
        ExState.currentRank = 'ALL';
        ExState.page = 0;
        ExState.scanning = false;
        ExState.activeHistId = saveToHistory(srcName, results, val);
        renderExchangeMain();
    }

    function filterByRankEx(results, rank) {
        if (rank === 'ALL') return results;
        return results.filter(r => getCardRankLetter(r.card) === rank);
    }

    function getFilteredExchangeResults() {
        let results = ExState.results;
        const anyCategory = ExState.filterWant || ExState.filterOffer || ExState.filterOwn;
        if (anyCategory) {
            results = results.filter(r => {
                if (ExState.filterWant && r.buyers.length > 0) return true;
                if (ExState.filterOffer && r.sellers.length > 0) return true;
                if (ExState.filterOwn && r.owners.length > 0) return true;
                return false;
            });
        } else if (ExState.filterMatchOnly) {
            results = results.filter(r => {
                const hasWant = r.buyers.length > 0;
                const hasOffer = r.sellers.length > 0;
                const hasOwn = r.owners.length > 0;
                if (hasWant && (hasOffer || hasOwn)) return true;
                if (hasOffer && hasWant) return true;
                return false;
            });
        }
        if (ExState.searchQuery) {
            const sq = ExState.searchQuery.toLowerCase();
            results = results.filter(r => {
                const cardName = (getCardCharName(r.card) || '').toLowerCase();
                const cardId = String(r.card_id);
                let queryId = sq;
                if (sq.includes('/card/')) queryId = sq.match(/\/card\/(\d+)/)?.[1] || sq;
                return cardName.includes(sq) || cardId === queryId || cardId.includes(queryId);
            });
        }
        if (ExState.searchUser) {
            const su = ExState.searchUser.toLowerCase();
            results = results.filter(r => {
                const matchUser = (u) => String(u.id) === su || String(u.id).includes(su) || (u.username && u.username.toLowerCase().includes(su));
                return r.sellers.some(matchUser) || r.buyers.some(matchUser) || r.owners.some(matchUser);
            });
        }
        return results;
    }

    function renderExchangeResults() {
        const container = document.getElementById('rem-ex-results-container');
        if (!container) return;
        const results = getFilteredExchangeResults();
        if (!results.length && ExState.results.length) {
            container.innerHTML = `<div class="rem-ex-empty"><div style="font-size:32px;margin-bottom:12px;">🕵️‍♂️</div><div>По вашему запросу ничего не найдено</div></div>`;
            return;
        } else if (!results.length) {
            container.innerHTML = `<div class="rem-ex-empty"><div style="font-size:32px;margin-bottom:12px;">🤷</div><div>Обмены не найдены</div></div>`;
            return;
        }
        const rankCounts = {};
        results.forEach(r => {
            const rk = getCardRankLetter(r.card);
            rankCounts[rk] = (rankCounts[rk] || 0) + 1;
        });
        const anyCategory = ExState.filterWant || ExState.filterOffer || ExState.filterOwn;
        let html = `<div class="rem-ex-summary" style="align-items:center;">
            <div class="rem-ex-s-item"><div class="rem-ex-s-num" style="color:#60a5fa;">${results.length}</div><div class="rem-ex-s-lab">Обменов</div></div>
            <div class="rem-ex-s-item"><div class="rem-ex-s-num" style="color:#fb923c;">${results.reduce((s, r) => s + r.sellers.length, 0)}</div><div class="rem-ex-s-lab">Продают</div></div>
            <div class="rem-ex-s-item"><div class="rem-ex-s-num" style="color:#3b82f6;">${results.reduce((s, r) => s + r.buyers.length, 0)}</div><div class="rem-ex-s-lab">Хотят</div></div>
            <div class="rem-ex-s-item"><div class="rem-ex-s-num" style="color:#4ade80;">${results.reduce((s, r) => s + r.owners.length, 0)}</div><div class="rem-ex-s-lab">Есть</div></div>
            <div style="border-left:1px solid #27272a;height:40px;margin:0 4px;"></div>
            <div class="rem-ex-filters" style="display:flex;flex-direction:column;gap:4px;align-items:flex-start;">
                <div style="display:flex;gap:4px;flex-wrap:wrap;">
                    <div class="rem-ex-filter-btn ${ExState.filterMatchOnly && !anyCategory ? 'active' : ''}" data-filter="match" style="padding:3px 8px;border-radius:6px;font-size:10px;font-weight:700;cursor:pointer;border:1px solid ${ExState.filterMatchOnly && !anyCategory ? '#3b82f6' : '#27272a'};background:${ExState.filterMatchOnly && !anyCategory ? 'rgba(59,130,246,.2)' : '#18181b'};color:${ExState.filterMatchOnly && !anyCategory ? '#60a5fa' : '#71717a'};transition:.15s;white-space:nowrap;">🔗 Матч</div>
                    <div class="rem-ex-filter-btn ${!ExState.filterMatchOnly && !anyCategory ? 'active' : ''}" data-filter="all" style="padding:3px 8px;border-radius:6px;font-size:10px;font-weight:700;cursor:pointer;border:1px solid ${!ExState.filterMatchOnly && !anyCategory ? '#3b82f6' : '#27272a'};background:${!ExState.filterMatchOnly && !anyCategory ? 'rgba(59,130,246,.2)' : '#18181b'};color:${!ExState.filterMatchOnly && !anyCategory ? '#60a5fa' : '#71717a'};transition:.15s;white-space:nowrap;">📋 Все</div>
                </div>
                <div style="display:flex;gap:4px;flex-wrap:wrap;">
                    <div class="rem-ex-filter-btn ${ExState.filterWant ? 'active' : ''}" data-filter="want" style="padding:3px 8px;border-radius:6px;font-size:10px;font-weight:700;cursor:pointer;border:1px solid ${ExState.filterWant ? '#3b82f6' : '#27272a'};background:${ExState.filterWant ? 'rgba(59,130,246,.2)' : '#18181b'};color:${ExState.filterWant ? '#60a5fa' : '#71717a'};transition:.15s;white-space:nowrap;">🔷 Хотят</div>
                    <div class="rem-ex-filter-btn ${ExState.filterOffer ? 'active' : ''}" data-filter="offer" style="padding:3px 8px;border-radius:6px;font-size:10px;font-weight:700;cursor:pointer;border:1px solid ${ExState.filterOffer ? '#fb923c' : '#27272a'};background:${ExState.filterOffer ? 'rgba(249,115,22,.15)' : '#18181b'};color:${ExState.filterOffer ? '#fb923c' : '#71717a'};transition:.15s;white-space:nowrap;">🔶 Отдают</div>
                    <div class="rem-ex-filter-btn ${ExState.filterOwn ? 'active' : ''}" data-filter="own" style="padding:3px 8px;border-radius:6px;font-size:10px;font-weight:700;cursor:pointer;border:1px solid ${ExState.filterOwn ? '#4ade80' : '#27272a'};background:${ExState.filterOwn ? 'rgba(34,197,94,.15)' : '#18181b'};color:${ExState.filterOwn ? '#4ade80' : '#71717a'};transition:.15s;white-space:nowrap;">🟢 Владеют</div>
                </div>
            </div>
        </div>`;
        html += `<div class="rem-ex-rank-bar">`;
        html += `<div class="rem-ex-rank-btn ${ExState.currentRank === 'ALL' ? 'active' : ''}" data-rank="ALL">💎 Все <span class="rem-ex-rank-count">(${results.length})</span></div>`;
        HOT_RANKS_EX.forEach(rk => {
            const cnt = rankCounts[rk] || 0;
            if (cnt > 0) {
                html += `<div class="rem-ex-rank-btn ${ExState.currentRank === rk ? 'active' : ''}" data-rank="${rk}">${rk} <span class="rem-ex-rank-count">(${cnt})</span></div>`;
            }
        });
        html += `</div>`;
        const pool = filterByRankEx(results, ExState.currentRank);
        const totalPages = Math.max(1, Math.ceil(pool.length / ExState.perPage));
        if (ExState.page >= totalPages) ExState.page = 0;
        const start = ExState.page * ExState.perPage;
        const batch = pool.slice(start, start + ExState.perPage);
        html += `<div class="rem-ex-results">`;
        batch.forEach((item, i) => {
            const card = item.card;
            const name = getCardCharName(card) || `Карта #${item.card_id}`;
            const rank = getCardRankLetter(card);
            const RANK_COLORS = { EV: '#e879f9', RE: '#f59e0b', S: '#fbbf24', A: '#c084fc', B: '#60a5fa', C: '#4ade80', D: '#fff', E: '#9ca3af', F: '#9ca3af' };
            const color = RANK_COLORS[rank] || '#a1a1aa';
            const cov = card.cover || {};
            const imgUrl = typeof cov === 'object' ? (cov.high || cov.mid || '') : '';
            const fullImg = imgUrl.startsWith('http') ? imgUrl : (imgUrl ? `${SITE_DOMAIN}${imgUrl}` : '');
            const isVid = isVideoUrl(fullImg);
            html += `
                <div class="rem-ex-card" data-trade-idx="${start + i}">
                    <div class="rem-ex-card-inner">
                        <div class="rem-ex-card-img"${isVid ? ` data-video-src="${fullImg}"` : ''}>${fullImg && !isVid ? `<img src="${fullImg}" loading="lazy">` : (!fullImg ? '' : '')}</div>
                        <div class="rem-ex-card-info">
                            <div class="rem-ex-card-name">${name}</div>
                            <div class="rem-ex-card-rank" style="color:${color};border:1px solid ${color};background:rgba(0,0,0,.3);">${rank}</div>
                            <div class="rem-ex-card-traders">
                                ${item.sellers.length ? `<span class="rem-ex-card-tag sellers">🔶 Отдают: ${item.sellers.length}</span>` : ''}
                                ${item.buyers.length ? `<span class="rem-ex-card-tag buyers">🔷 Хотят: ${item.buyers.length}</span>` : ''}
                                ${item.owners.length ? `<span class="rem-ex-card-tag owners">🟢 Есть: ${item.owners.length}</span>` : ''}
                            </div>
                        </div>
                    </div>
                </div>
            `;
        });
        html += `</div>`;
        if (totalPages > 1) {
            html += `<div class="rem-ex-nav">`;
            if (ExState.page > 0) html += `<button class="rem-ex-btn secondary" data-page="${ExState.page - 1}">⬅️</button>`;
            html += `<span style="color:#71717a;font-size:12px;display:flex;align-items:center;">Стр. ${ExState.page + 1} / ${totalPages}</span>`;
            if (ExState.page < totalPages - 1) html += `<button class="rem-ex-btn secondary" data-page="${ExState.page + 1}">➡️</button>`;
            html += `</div>`;
        }
        container.innerHTML = html;
        container.querySelectorAll('[data-video-src]').forEach(el => {
            renderCardMedia(el.getAttribute('data-video-src'), el);
        });
        container.querySelectorAll('.rem-ex-rank-btn').forEach(btn => {
            btn.onclick = () => {
                ExState.currentRank = btn.getAttribute('data-rank');
                ExState.page = 0;
                renderExchangeResults();
            };
        });
        container.querySelectorAll('.rem-ex-filter-btn').forEach(btn => {
            btn.onclick = () => {
                const f = btn.getAttribute('data-filter');
                if (f === 'match') {
                    ExState.filterMatchOnly = true;
                    ExState.filterWant = false;
                    ExState.filterOffer = false;
                    ExState.filterOwn = false;
                } else if (f === 'all') {
                    ExState.filterMatchOnly = false;
                    ExState.filterWant = false;
                    ExState.filterOffer = false;
                    ExState.filterOwn = false;
                } else if (f === 'want') {
                    ExState.filterWant = !ExState.filterWant;
                } else if (f === 'offer') {
                    ExState.filterOffer = !ExState.filterOffer;
                } else if (f === 'own') {
                    ExState.filterOwn = !ExState.filterOwn;
                }
                ExState.page = 0;
                renderExchangeResults();
            };
        });
        container.querySelectorAll('.rem-ex-card').forEach(card => {
            card.onclick = () => {
                const idx = parseInt(card.getAttribute('data-trade-idx'));
                renderExchangeDetail(idx);
            };
        });
        container.querySelectorAll('[data-page]').forEach(btn => {
            btn.onclick = (e) => {
                e.stopPropagation();
                ExState.page = parseInt(btn.getAttribute('data-page'));
                renderExchangeResults();
            };
        });
    }

    function renderExchangeDetail(idx) {
        const container = document.getElementById('rem-ex-results-container');
        if (!container) return;
        const results = getFilteredExchangeResults();
        const pool = filterByRankEx(results, ExState.currentRank);
        if (idx < 0 || idx >= pool.length) return;
        const data = pool[idx];
        const card = data.card;
        const name = getCardCharName(card) || `Карта #${data.card_id}`;
        const rank = getCardRankLetter(card);
        const cov = card.cover || {};
        const imgUrl = typeof cov === 'object' ? (cov.high || cov.mid || '') : '';
        const fullImg = imgUrl.startsWith('http') ? imgUrl : (imgUrl ? `${SITE_DOMAIN}${imgUrl}` : '');
        const isVid = isVideoUrl(fullImg);
        const RANK_COLORS = { EV: '#e879f9', RE: '#f59e0b', S: '#fbbf24', A: '#c084fc', B: '#60a5fa', C: '#4ade80', D: '#fff', E: '#9ca3af', F: '#9ca3af' };
        const color = RANK_COLORS[rank] || '#a1a1aa';

        const formatUsers = (users) => {
            if (!users.length) return '<div style="color:#52525b;font-size:12px;padding:4px 10px;">—</div>';
            return users.map(u => {
                const emoji = u.sex === 1 ? '👨' : (u.sex === 2 ? '👩' : '👤');
                return `
                <a href="${SITE_DOMAIN}/user/${u.id}/about" target="_blank" class="rem-ex-user">
                    <span>${emoji}</span>
                    <span class="rem-ex-user-name">${u.username}</span>
                    <span class="rem-ex-user-id">ID: ${u.id}</span>
                </a>
            `}).join('');
        };

        container.innerHTML = `
            <div class="rem-ex-detail">
                <div style="margin-bottom:16px;">
                    <button class="rem-ex-btn secondary" id="rem-ex-back-btn">⬅️ Назад к списку</button>
                </div>
                <div class="rem-ex-detail-header">
                    <div class="rem-ex-detail-img"${isVid ? ` data-video-src="${fullImg}"` : ''}>${fullImg && !isVid ? `<img src="${fullImg}">` : (!fullImg ? '<div style="width:100%;height:100%;background:#27272a;display:flex;align-items:center;justify-content:center;color:#52525b;">?</div>' : '')}</div>
                    <div class="rem-ex-detail-meta">
                        <div class="rem-ex-detail-name">${name}</div>
                        <div class="rem-ex-card-rank" style="color:${color};border:1px solid ${color};background:rgba(0,0,0,.3);">${rank}</div>
                        <div class="rem-ex-detail-id">ID: ${data.card_id}</div>
                        <a href="${SITE_DOMAIN}/card/${data.card_id}" target="_blank" style="color:#3b82f6;font-size:12px;text-decoration:none;">Открыть на сайте ↗</a>
                    </div>
                </div>
                <div class="rem-ex-user-list">
                    <h4>🔶 Отдают (${data.sellers.length})</h4>
                    ${formatUsers(data.sellers)}
                    <h4>🔷 Хотят (${data.buyers.length})</h4>
                    ${formatUsers(data.buyers)}
                    ${data.owners.length ? `<h4>🟢 Просто есть (${data.owners.length})</h4>${formatUsers(data.owners)}` : ''}
                </div>
            </div>
        `;
        container.querySelector('#rem-ex-back-btn').onclick = () => renderExchangeResults();
        container.querySelectorAll('[data-video-src]').forEach(el => { renderCardMedia(el.getAttribute('data-video-src'), el); });
    }

    function createCheck(fname) {
        const div = document.createElement('div');
        div.className = 'rem-own-badge';
        const count = Manager.getCardCount(fname);
        if (count > 1) {
            div.innerHTML = `<span class="rem-own-count" style="font-size: 11px; font-weight: 800; line-height: 1; color: #fff;">${count}</span>`;
        } else {
            div.innerHTML = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"></polyline></svg>`;
        }
        return div;
    }

    function createWishCheck() {
        const div = document.createElement('div');
        div.className = 'rem-wish-badge';
        div.innerHTML = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"></path></svg>`;
        return div;
    }

    function processCardElement(mediaEl, src) {
        if (mediaEl.hasAttribute('data-rem-checked')) return;
        mediaEl.setAttribute('data-rem-checked', '1');
        const fname = Manager.getFilename(src);
        const have = Manager.has(fname);
        const wish = Manager.hasWish(fname);
        let wrapper = mediaEl.parentElement;
        if (mediaEl.tagName.toLowerCase() === 'source' && wrapper) {
            wrapper = wrapper.parentElement;
        }

        if (!wrapper) return;
        if (window.getComputedStyle(wrapper).position === 'static') wrapper.style.position = 'relative';

        const isTitleCardsPage = !!location.pathname.match(/^\/manga\/([^\/]+)\/cards/);
        const isDeletedTitle = new URLSearchParams(location.search).has('fix_id');
        const isUserProfilePage = !!location.pathname.match(/\/user\/(?:\d+\/)?(about|inventory|create\/exchange|inventory\/cards-upgrade)/);
        const hideOwnBadge = (isTitleCardsPage && !isDeletedTitle) || isUserProfilePage;

        if (cfg.ownBadge && have && !hideOwnBadge && !wrapper.querySelector('.rem-own-badge')) {
            wrapper.appendChild(createCheck(fname));
        }
        if (cfg.wishBadge && wish && !have && !wrapper.querySelector('.rem-wish-badge')) {
            wrapper.appendChild(createWishCheck());
        }
    }

    function scanVisual() {
        if (!Manager.loaded) return;

        document.querySelectorAll('img[src*="/media/card-item/"]:not([data-rem-checked])').forEach(img => {
            processCardElement(img, img.src);
        });

        document.querySelectorAll('video[src*="/media/card-item/"]:not([data-rem-checked])').forEach(vid => {
            processCardElement(vid, vid.src);
        });

        document.querySelectorAll('video:not([data-rem-checked]), picture:not([data-rem-checked])').forEach(media => {
            const source = media.querySelector('source[src*="/media/card-item/"], source[srcset*="/media/card-item/"]');
            if (source) {
                const src = source.srcset ? source.srcset.split(' ')[0] : source.src;
                processCardElement(source, src);
            }
        });

        document.querySelectorAll('[style*="/media/card-item/"]:not([data-rem-checked])').forEach(el => {
            const style = el.getAttribute('style');
            const match = style.match(/\/media\/card-item\/[^)"']+/);
            if (match) {
                processCardElement(el, match[0]);
            }
        });

        const isTitleCardsPage = !!location.pathname.match(/^\/manga\/([\w-]+)\/cards/);
        if (isTitleCardsPage && typeof applyTitleMissingMode === 'function') {
            applyTitleMissingMode();
        }
    }

    async function checkOnline() {
        if (!cfg.onlineStatus) return;
        const candidates = document.querySelectorAll('a[href*="/user/"]:not([data-rem-online])');
        for (const el of candidates) {
            if (el.textContent.includes("Показать") || el.closest('.cs-comments-section, [data-sentry-component="ActivityItemCard"]')) {
                el.setAttribute('data-rem-online', 'skip'); continue;
            }
            const m = el.getAttribute('href').match(/\/user\/(\d+)/);
            if (!m) { el.setAttribute('data-rem-online', 'skip'); continue; }
            const uid = m[1], av = el.querySelector('[data-slot="avatar"], .relative.shrink-0');
            if (!av) { el.setAttribute('data-rem-online', 'skip'); continue; }
            el.setAttribute('data-rem-online', '1');
            if (Manager.onlineCache.has(uid)) {
                const c = Manager.onlineCache.get(uid);
                if (Date.now() - c.time < 600000) { drawDot(av, c.online); continue; }
            }
            if (Manager.onlineProcessing.has(uid)) continue;
            Manager.onlineProcessing.add(uid);
            Manager.req(`${API_DOMAIN}/api/v2/users/${uid}/`).then(data => {
                const status = data ? !!data.is_online : false;
                Manager.onlineCache.set(uid, { online: status, time: Date.now() });
                Manager.onlineProcessing.delete(uid);
                drawDot(av, status);
            });
        }
    }

    function drawDot(container, isOnline) {
        if (container.querySelector('.rem-online-dot')) return;
        const dot = document.createElement('div');
        dot.className = `rem-online-dot ${isOnline ? 'online' : 'offline'}`;
        if (window.getComputedStyle(container).position === 'static') container.style.position = 'relative';
        container.appendChild(dot);
    }


    let titleMissingMode = false;
    let titleCacheOwned = 0, titleCacheTotal = 0, titleCacheStats = null;
    let isCalculating = false;
    let lastSlug = '';

    function applyTitleMissingMode() {
        let grid = document.querySelector('.grid.gap-3');
        if (!grid) return;

        let ph = document.getElementById('rem-all-collected-ph');

        if (titleMissingMode && titleCacheOwned > 0 && titleCacheOwned === titleCacheTotal) {
            grid.style.display = 'none';
            if (!ph) {
                ph = document.createElement('div');
                ph.id = 'rem-all-collected-ph';
                ph.innerHTML = '✨ Все карты собраны! ✨';
                ph.style.cssText = 'height: 50vh; display: flex; align-items: center; justify-content: center; font-size: 24px; color: #71717a;';
                grid.parentElement.insertBefore(ph, grid.nextSibling);
            }
            ph.style.display = 'flex';
        } else {
            grid.style.display = '';

            if (titleMissingMode) {
                grid.style.minHeight = '100vh';
                grid.style.alignContent = 'start';
            } else {
                grid.style.minHeight = '';
                grid.style.alignContent = '';
            }

            if (ph) ph.style.display = 'none';

            document.querySelectorAll('img[src*="/media/card-item/"]').forEach(img => {
                const fname = Manager.getFilename(img.src);
                const have = Manager.has(fname);
                const cardCont = img.closest('.grid > div') || img.closest('[data-sentry-component="CardItem"]') || img.parentElement.parentElement;
                if (cardCont) {
                    if (titleMissingMode && have) {
                        cardCont.style.display = 'none';
                    } else if (cardCont.style.display === 'none') {
                        cardCont.style.display = '';
                    }
                }
            });
        }
    }

    function buildProgressHtml(owned, total, rankStats) {
        if (titleMissingMode) {
            let badgesHtml = '';
            if (rankStats) {
                RANK_ORDER.forEach(rankKey => {
                    const st = rankStats[rankKey];
                    if (st && st.total > 0) {
                        const r = RANK_MAP[rankKey] || { name: rankKey.toUpperCase(), color: '#a1a1aa' };
                        const miss = st.total - st.owned;
                        if (miss > 0) {
                            badgesHtml += `<div class="rem-rank-badge" style="color:${r.color};border-color:${r.color};">${r.name}: <span style="color:#fff;margin-left:3px;">-${miss}</span></div>`;
                        }
                    }
                });
            }
            return `
                <div class="rem-progress-header" style="justify-content:space-between;align-items:center;">
                    <div class="rem-progress-text">Недостающие карты: <span>${total - owned}</span></div>
                    <button class="rem-ex-btn secondary" id="rem-missing-toggle" style="height:28px;padding:0 12px;font-size:11px;">🔍 Показать все</button>
                </div>
                <div class="rem-rank-stats">${badgesHtml || '<div style="color:#71717a;font-size:12px;">Все карты собраны! 🎉</div>'}</div>
            `;
        } else {
            let percentNum = 0, percentStr = "0%";
            if (total > 0 && owned > 0) {
                const raw = (owned / total) * 100;
                if (owned === total) { percentNum = 100; percentStr = "100%"; }
                else if (raw < 1) { percentStr = raw.toFixed(1) + "%"; percentNum = raw; }
                else if (raw > 99) { percentStr = raw.toFixed(1) + "%"; percentNum = raw; }
                else { percentNum = Math.floor(raw); percentStr = percentNum + "%"; }
            }
            let badgesHtml = '';
            if (rankStats) {
                RANK_ORDER.forEach(rankKey => {
                    if (rankStats[rankKey] && rankStats[rankKey].total > 0) {
                        const r = RANK_MAP[rankKey] || { name: rankKey.toUpperCase(), color: '#a1a1aa' };
                        const st = rankStats[rankKey];
                        badgesHtml += `<div class="rem-rank-badge" style="color:${r.color};border-color:${r.color};">${r.name}: <span style="color:#fff;margin-left:3px;">${st.owned} / ${st.total}</span></div>`;
                    }
                });
            }
            return `
                <div class="rem-progress-header">
                    <div class="rem-progress-text">Собрано карт: <span>${owned}</span> / ${total}</div>
                    <div style="display:flex;align-items:center;gap:12px;">
                        <button class="rem-ex-btn secondary" id="rem-missing-toggle" style="height:28px;padding:0 12px;font-size:11px;">👁️ Скрыть полученные</button>
                        <div id="rem-percent">${percentStr}</div>
                    </div>
                </div>
                <div class="rem-progress-track"><div class="rem-progress-fill" style="width:${percentNum}%"></div></div>
                <div class="rem-rank-stats">${badgesHtml}</div>
            `;
        }
    }

    function createProgressBox() {
        const box = document.createElement('div');
        box.id = 'rem-progress-box';
        box.className = 'rem-progress-box';
        box.innerHTML = buildProgressHtml(0, 0, null);
        return box;
    }

    function updateProgressBox(box, owned, total, rankStats) {
        titleCacheOwned = owned;
        titleCacheTotal = total;
        titleCacheStats = rankStats;
        box.innerHTML = buildProgressHtml(owned, total, rankStats);
        const btn = box.querySelector('#rem-missing-toggle');
        if (btn) {
            btn.onclick = () => {
                titleMissingMode = !titleMissingMode;
                updateProgressBox(box, titleCacheOwned, titleCacheTotal, titleCacheStats);
                applyTitleMissingMode();
            };
        }
    }

    async function injectTitleProgress() {
        if (!cfg.titleProgress) return;
        const match = location.pathname.match(/^\/manga\/([\w-]+)\/cards/);
        if (!match) {
            const oldBox = document.getElementById('rem-progress-box');
            if (oldBox) oldBox.remove();
            isCalculating = false; lastSlug = '';
            return;
        }
        if (!Manager.loaded || isCalculating) return;
        const grid = document.querySelector('.grid.gap-3');
        if (!grid) return;
        const slug = match[1];
        if (lastSlug !== slug) {
            const oldBox = document.getElementById('rem-progress-box');
            if (oldBox) oldBox.remove();
            lastSlug = slug;
            titleMissingMode = false;
        }
        if (document.getElementById('rem-progress-box')) return;
        isCalculating = true;
        const box = createProgressBox();
        grid.parentNode.insertBefore(box, grid);
        try {
            const titleData = await Manager.req(`${API_DOMAIN}/api/titles/${slug}/`);
            if (!titleData || !titleData.content || !titleData.content.id) { isCalculating = false; return; }
            const titleId = titleData.content.id;
            let totalCards = 0, ownedCards = 0, rankStats = {}, page = 1, run = true;
            while (run) {
                if (!location.pathname.includes(slug)) { isCalculating = false; return; }
                const data = await Manager.req(`${API_DOMAIN}/api/inventory/${titleId}/cards/?count=50&ordering=rank&page=${page}`);
                if (!data) { run = false; break; }
                const list = data.results || data.content || [];
                if (!list.length) { run = false; break; }
                list.forEach(item => {
                    totalCards++;
                    const c = item.card || item;
                    const rank = c.rank || 'unknown';
                    if (!rankStats[rank]) rankStats[rank] = { total: 0, owned: 0 };
                    rankStats[rank].total++;
                    if (c.cover) {
                        const h = Manager.getFilename(c.cover.high);
                        const m = Manager.getFilename(c.cover.mid);
                        if ((h && Manager.has(h)) || (m && Manager.has(m))) {
                            ownedCards++;
                            rankStats[rank].owned++;
                        }
                    }
                });
                if (!data.next) run = false; else page++;
                await new Promise(r => setTimeout(r, 100));
            }
            const finalBox = document.getElementById('rem-progress-box');
            if (finalBox) updateProgressBox(finalBox, ownedCards, totalCards, rankStats);
        } catch (e) { console.error(e); }
        isCalculating = false;
    }

    function scanForDialog() {
        const dialog = document.querySelector('[role="dialog"]');
        if (!dialog) return;


        const mangaLink = dialog.querySelector('a[href^="/manga/"]');
        if (mangaLink && !mangaLink.dataset.fixHook) {
            mangaLink.dataset.fixHook = "true";
            mangaLink.dataset.origHref = mangaLink.getAttribute('href');
            const isActive = cfg.fixMode;
            const btn = document.createElement('span');
            btn.className = `rem-toggle ${isActive ? 'on' : 'off'}`;
            btn.innerText = `FIX: ${isActive ? 'ON' : 'OFF'}`;

            if (mangaLink.nextSibling) mangaLink.parentNode.insertBefore(btn, mangaLink.nextSibling);
            else mangaLink.parentNode.appendChild(btn);

            btn.addEventListener('click', (e) => {
                e.preventDefault(); e.stopPropagation();
                const newState = !cfg.fixMode;
                saveSetting('fixMode', newState);
                btn.className = `rem-toggle ${newState ? 'on' : 'off'}`;
                btn.innerText = `FIX: ${newState ? 'ON' : 'OFF'}`;
                if (!newState) mangaLink.href = mangaLink.dataset.origHref;
            });

            mangaLink.addEventListener('click', async (e) => {
                if (cfg.fixMode) {
                    e.preventDefault(); e.stopPropagation();
                    const cardLink = dialog.querySelector('a[href^="/card/"]');
                    if (!cardLink) return;
                    const m = cardLink.getAttribute('href').match(/card\/(\d+)/);
                    if (!m) return;
                    btn.innerText = "⏳";
                    const cardData = await Manager.req(`${API_DOMAIN}/api/inventory/cards/${m[1]}/`);
                    if (cardData && cardData.title && cardData.title.id) {
                        const titleId = cardData.title.id;
                        const titleName = cardData.title.main_name || "Тайтл";
                        const slug = mangaLink.dataset.origHref.split('/')[2];
                        const titleStatus = await Manager.req(`${API_DOMAIN}/api/titles/${slug}/`);
                        btn.innerText = "FIX: ON";
                        let targetUrl;
                        if (titleStatus && titleStatus.content) {
                            targetUrl = mangaLink.dataset.origHref;
                        } else {
                            targetUrl = `${SITE_DOMAIN}/manga/${slug}?fix_id=${titleId}&fix_name=${encodeURIComponent(titleName)}`;
                        }
                        if (window.next && window.next.router && typeof window.next.router.push === 'function') {
                            window.next.router.push(targetUrl);
                        } else {
                            window.location.href = targetUrl;
                        }
                    } else { btn.innerText = "ERR"; }
                }
            });
        }
    }

    async function runFix(fid, rawName) {
        if (activeObserver) { activeObserver.disconnect(); activeObserver = null; }
        document.documentElement.classList.add('rem-checking');
        const l = document.createElement('div'); l.id = 'rem-loader'; l.innerText = "Загрузка данных (0%)...";
        if (!document.body) await new Promise(r => addEventListener('DOMContentLoaded', r));
        document.body.appendChild(l);
        const slug = location.pathname.split('/')[2];
        const status = await Manager.req(`${API_DOMAIN}/api/titles/${slug}/`);
        l.remove(); document.documentElement.classList.remove('rem-checking');
        if (status && status.content) {
            const url = new URL(window.location);
            url.searchParams.delete('fix_id'); url.searchParams.delete('fix_name');
            window.history.replaceState({}, '', url);
            return;
        }
        const tName = rawName ? decodeURIComponent(rawName) : "Title";
        let main = document.querySelector('main');
        let attempts = 0;
        while (!main && attempts < 30) { await new Promise(r => setTimeout(r, 50)); main = document.querySelector('main'); attempts++; }
        if (!main) main = document.body;
        document.body.classList.add('rem-active');
        const box = document.createElement('div');
        box.id = 'rem-inject'; box.className = "container mx-auto px-4 py-6";
        box.innerHTML = `<div style="height:50vh;display:flex;align-items:center;justify-content:center;color:#888;">Загрузка карт...</div>`;
        main.appendChild(box);
        activeObserver = new MutationObserver(() => {
            if (!document.body.classList.contains('rem-active')) document.body.classList.add('rem-active');
            if (!document.getElementById('rem-inject')) { const m = document.querySelector('main') || document.body; m.appendChild(box); }
        });
        activeObserver.observe(document.body, { attributes: true, attributeFilter: ['class'] });
        activeObserver.observe(main, { childList: true });

        let allCards = [];
        let page = 1, run = true;
        while (run) {
            box.innerHTML = `<div style="height:50vh;display:flex;align-items:center;justify-content:center;color:#888;">Загрузка стр. ${page}...</div>`;
            const data = await Manager.req(`${API_DOMAIN}/api/inventory/${fid}/cards/?count=50&ordering=rank&page=${page}`);
            if (!data) { run = false; break; }
            const list = data.results || data.content || [];
            if (!list.length) { run = false; break; }
            allCards = allCards.concat(list);

            if (data.next) {
                page++;
            } else if (data.props && data.props.total_pages && page < data.props.total_pages) {
                page++;
            } else if (data.count && page * 100 < data.count) {
                page++;
            } else {
                run = false;
            }

            if (run) await new Promise(r => setTimeout(r, 20));
        }
        if (!allCards.length) { box.innerHTML = `<div style="text-align:center;padding:50px">Пусто</div>`; return; }

        let ownedInTitle = 0, rankStats = {};
        const grid = document.createElement('div');
        grid.className = 'rem-grid';

        allCards.forEach(item => {
            const c = item.card || item;
            const rank = c.rank || 'unknown';
            if (!rankStats[rank]) rankStats[rank] = { total: 0, owned: 0 };
            rankStats[rank].total++;
            let have = false;
            if (c.cover) {
                const h = Manager.getFilename(c.cover.high);
                const m = Manager.getFilename(c.cover.mid);
                if ((h && Manager.has(h)) || (m && Manager.has(m))) have = true;
            }
            if (have) { ownedInTitle++; rankStats[rank].owned++; }
            const imgUrl = c.cover?.high ? `${SITE_DOMAIN}${c.cover.high}` : (c.cover?.mid ? `${SITE_DOMAIN}${c.cover.mid}` : null);
            const el = document.createElement('div');
            el.className = 'rem-card';
            if (imgUrl) {
                if (imgUrl.endsWith('.webm') || imgUrl.endsWith('.mp4')) {
                    el.innerHTML = `<video src="${imgUrl}#t=1.0" muted playsinline preload="metadata" style="width:100%;height:100%;object-fit:cover;pointer-events:none;"></video>`;
                } else {
                    el.innerHTML = `<img src="${imgUrl}" loading="lazy">`;
                }
            } else {
                el.innerHTML = '';
            }
            el.onclick = () => showModal(c, tName);
            if (have && cfg.ownBadge) {
                if (window.getComputedStyle(el).position === 'static') el.style.position = 'relative';
                el.appendChild(createCheck(Manager.getFilename(imgUrl)));
            }
            grid.appendChild(el);
        });

        const progressBox = createProgressBox();
        updateProgressBox(progressBox, ownedInTitle, allCards.length, rankStats);
        progressBox.style.marginBottom = '20px';
        box.innerHTML = `
            <div class="rem-fix-header">
                <h1 class="rem-fix-title">${tName}</h1>
                <div class="rem-fix-meta">
                    <span>ID: ${fid}</span><span class="rem-sep">|</span>
                    <span>${allCards.length} шт</span><span class="rem-sep">|</span>
                    <span style="color:#22c55e;">● Fix Active</span>
                </div>
            </div>
        `;
        box.appendChild(progressBox);
        box.appendChild(grid);
    }

    function checkNavigation() {
        const p = new URLSearchParams(location.search);
        if (!p.get('fix_id')) {
            if (activeObserver) { activeObserver.disconnect(); activeObserver = null; }
            if (document.body.classList.contains('rem-active')) document.body.classList.remove('rem-active');
            const inj = document.getElementById('rem-inject'); if (inj) inj.remove();
            const load = document.getElementById('rem-loader'); if (load) load.remove();
            document.documentElement.classList.remove('rem-checking');
        } else if (!document.body.classList.contains('rem-active') && !document.getElementById('rem-loader')) {
            runFix(p.get('fix_id'), p.get('fix_name'));
        }
    }

    function getAuthorInfo(c) {
        const u = c.author || c.user || c.upload_user || c.owner || c.publisher || c.creator;
        if (u && (u.username || u.name)) return { name: u.username || u.name, link: u.id ? `/user/${u.id}/about` : '#' };
        return { name: "Неизвестен", link: "#" };
    }

    function showModal(c, tName) {
        const imgUrl = c.cover?.high ? `${SITE_DOMAIN}${c.cover.high}` : '';
        const isVid = imgUrl && (imgUrl.endsWith('.webm') || imgUrl.endsWith('.mp4'));
        const imgHtml = isVid
            ? `<video src="${imgUrl}#t=1.0" muted playsinline preload="metadata" style="width:100%;height:100%;object-fit:cover;pointer-events:none;"></video>`
            : `<img src="${imgUrl}">`;

        const name = c.name || c.character?.name || "?";
        const cid = c.character?.id || 0;
        const uInfo = getAuthorInfo(c);
        const likes = c.likes_count || 0;
        const cLink = c.id ? `/card/${c.id}` : '#';
        const over = document.createElement('div'); over.className = 'rem-over';
        over.onclick = e => { if (e.target === over) over.remove(); };
        let titleUrl = `/manga/${location.pathname.split('/')[2]}`;
        const p = new URLSearchParams(location.search);
        if (p.get('fix_id')) titleUrl += `?fix_id=${p.get('fix_id')}&fix_name=${p.get('fix_name')}`;
        else { const slug = c.title ? c.title.dir : '#'; if (slug !== '#') titleUrl = `/manga/${slug}`; }
        over.innerHTML = `
            <div class="rem-box">
                <button class="rem-close" id="x"><svg width="20" height="20" viewBox="0 0 20 20" stroke="currentColor" stroke-width="1.5" fill="none"><path d="M6 14L14 6M14 14L6 6" stroke-linecap="round" stroke-linejoin="round"/></svg></button>
                <div class="rem-box-img">${imgHtml}</div>
                <div class="rem-info">
                    <div><a href="${titleUrl}" class="rem-lnk-m" target="_blank">${tName} ↗</a>${p.get('fix_id') ? '<span class="rem-toggle on" style="font-size:10px;padding:2px 4px;margin-left:5px">FIX: ON</span>' : ''}</div>
                    <a href="/character/${cid}" class="rem-lnk-c" target="_blank">${name} ↗</a>
                </div>
                <div class="rem-acts"><button class="rem-pill">Like ${likes}</button></div>
                <div class="rem-sub"><a href="${uInfo.link}" target="_blank" class="rem-s-btn">Автор: ${uInfo.name}</a><a href="${cLink}" target="_blank" class="rem-s-btn">Пользователи</a></div>
            </div>`;
        document.body.appendChild(over);
        document.getElementById('x').onclick = () => over.remove();
    }

    function getProfileInjectionPoint() {
        const isMobile = window.innerWidth <= 768;
        const cont = document.querySelector('.cs-layout-avatar-container');

        if (isMobile) {
            const mobileRow = document.querySelector('.flex.flex-row.gap-2.max-sm\\:w-full');
            if (mobileRow) return { targetCol: mobileRow.parentElement, refNode: mobileRow };
            if (cont) {
                const guild = cont.nextElementSibling;
                const refNode = (guild && !guild.classList.contains('mt-auto')) ? guild : cont;
                return { targetCol: cont.parentElement, refNode: refNode };
            }
        } else {
            if (cont) {
                const guild = cont.nextElementSibling;
                const refNode = (guild && !guild.classList.contains('mt-auto')) ? guild : cont;
                return { targetCol: cont.parentElement, refNode: refNode };
            }
            const desktopRow = document.querySelector('.flex.flex-row.gap-2.max-sm\\:w-full');
            if (desktopRow) return { targetCol: desktopRow.parentElement, refNode: desktopRow };
        }

        const btns = Array.from(document.querySelectorAll('button'));
        const fbtn = btns.find(b => b.textContent && (b.textContent.includes('друзья') || b.textContent.includes('сообщение')));
        if (fbtn && fbtn.parentElement) return { targetCol: fbtn.parentElement.parentElement, refNode: fbtn.parentElement };

        return null;
    }

    async function injectScanButton() {
        if (!cfg.scanButton) return;
        const m = location.pathname.match(/^\/user\/(\d+)/);
        if (!m) return;
        const uid = m[1];
        if (uid === Manager.userId) return;

        const pt = getProfileInjectionPoint();
        if (!pt) return;
        const { targetCol, refNode } = pt;
        if (document.querySelector('.rem-scan-btn')) return;

        const inList = isInScanList(uid);
        const btn = document.createElement('button');
        btn.className = `rem-scan-btn ${inList ? 'active' : ''}`;
        btn.innerHTML = `
            <span class="rem-scan-check">
                <svg viewBox="0 0 12 12" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="10 3 5 9 2 6"></polyline></svg>
            </span>
            <span class="rem-scan-label">${inList ? 'В списке сканирования' : 'Добавить в сканирование'}</span>
        `;

        let customWrap = targetCol.querySelector('.rem-custom-actions-wrap');
        if (!customWrap) {
            customWrap = document.createElement('div');
            customWrap.className = 'rem-custom-actions-wrap';
            customWrap.style.cssText = 'width:100%; display:flex; flex-wrap:wrap; gap:8px; align-items:center; margin-top:8px; margin-bottom:8px;';
            if (refNode && refNode.nextSibling) targetCol.insertBefore(customWrap, refNode.nextSibling);
            else targetCol.appendChild(customWrap);
        }
        customWrap.appendChild(btn);

        btn.onclick = () => {
            const currentlyInList = isInScanList(uid);
            if (currentlyInList) {
                removeFromScanList(uid);
                btn.classList.remove('active');
                btn.querySelector('.rem-scan-label').textContent = 'Добавить в сканирование';
            } else {
                let username = `User_${uid}`;
                const titleText = document.title.split(/ [|—\-/] /)[0].trim();
                const nameEl = document.querySelector('h1, .text-2xl, [class*="username"]');

                const _isSiteTitle = (t) => t === 'Remanga' || /реманга/i.test(t) || /xn--80aaig9ahr/i.test(t);
                if (titleText && !_isSiteTitle(titleText) && !titleText.includes('Ошибка')) {
                    username = titleText.replace(/^Профиль\s+/i, '').replace(/#\d+$/, '').trim();
                } else if (nameEl) {
                    const txt = nameEl.textContent.trim();
                    if (txt && txt.length < 60) username = txt.replace(/#\d+$/, '').trim();
                }

                addToScanList(uid, username);
                btn.classList.add('active');
                btn.querySelector('.rem-scan-label').textContent = 'В списке сканирования';

                Manager.req(`${API_DOMAIN}/api/v2/users/${uid}/`).then(udata => {
                    if (!udata) return;
                    let apiName = udata.username || (udata.content && udata.content.username) || (udata.user && udata.user.username);
                    if (apiName) apiName = apiName.replace(/#\d+$/, '').trim();
                    if (apiName && apiName !== username) {
                        const list = getScanList();
                        const entry = list.find(u => String(u.id) === String(uid));
                        if (entry) { entry.username = apiName; saveScanList(list); }
                    }
                }).catch(() => { });
            }
        };
    }

    async function injectStats() {
        if (!cfg.profileStats && !cfg.deckStats) return;
        const m = location.pathname.match(/^\/user\/(\d+)/); if (!m) return;
        const uid = m[1];

        const pt = getProfileInjectionPoint();
        if (!pt) return;
        const { targetCol, refNode } = pt;
        if (document.querySelector('.rem-profile-stat-badge')) return;

        const badge = document.createElement('div');
        badge.className = 'rem-profile-stat-badge rem-counting';
        badge.innerHTML = cfg.profileStats ? `🎴 Создал карт: <strong>...</strong>` : `📦 Загрузка...`;

        let customWrap = targetCol.querySelector('.rem-custom-actions-wrap');
        if (!customWrap) {
            customWrap = document.createElement('div');
            customWrap.className = 'rem-custom-actions-wrap';
            customWrap.style.cssText = 'width:100%; display:flex; flex-wrap:wrap; gap:8px; align-items:center; margin-top:8px; margin-bottom:8px;';
            if (refNode && refNode.nextSibling) targetCol.insertBefore(customWrap, refNode.nextSibling);
            else targetCol.appendChild(customWrap);
        }
        badge.style.marginTop = '0';
        customWrap.appendChild(badge);

        let cVerb = "Создал";
        let oVerb = "Открыл";
        try {
            const userRes = await Manager.req(`${API_DOMAIN}/api/v2/users/${uid}/`);
            if (userRes) {
                let s = undefined;
                if (userRes.sex !== undefined) s = userRes.sex;
                else if (userRes.content && userRes.content.sex !== undefined) s = userRes.content.sex;
                else if (userRes.user && userRes.user.sex !== undefined) s = userRes.user.sex;
                if (s !== undefined) {
                    const sex = Number(s);
                    if (sex === 0) { cVerb = "Создало"; oVerb = "Открыло"; }
                    if (sex === 1) { cVerb = "Создал"; oVerb = "Открыл"; }
                    if (sex === 2) { cVerb = "Создала"; oVerb = "Открыла"; }
                }
            }
        } catch (e) { }

        let deckPart = "";
        if (cfg.deckStats) {
            try {
                const res = await Manager.req(`${API_DOMAIN}/api/v2/inventory/decks/??is_opened=false&user_id=${uid}&count=1`);
                if (res) {
                    const count = res.count || 0;
                    deckPart = ` | 📦 ${oVerb} паков: <strong>${count}</strong>`;
                }
            } catch (e) { }
        }

        if (!cfg.profileStats) {
            badge.classList.remove('rem-counting');
            badge.innerHTML = deckPart ? deckPart.replace(/^ \| /, '') : `📦 ${oVerb} паков: 0`;
            return;
        }

        let total = 0, page = 1, run = true;
        while (run) {
            badge.innerHTML = `🎴 ${cVerb} карт: <strong>${total}...</strong>${deckPart}`;
            const r = await Manager.req(`${API_DOMAIN}/api/inventory/catalog/?author=${uid}&count=30&ordering=-rank&page=${page}`);
            if (!r) break;
            const list = r.results || [];
            if (!list.length) break;
            total += list.length;
            if (!r.next) run = false; else { page++; await new Promise(r => setTimeout(r, 150)); }
        }
        badge.classList.remove('rem-counting');
        badge.innerHTML = `🎴 ${cVerb} карт: <strong>${total}</strong>${deckPart}`;
    }

    let filterMapCache = null;
    async function getFilterMap() {
        if (filterMapCache) return filterMapCache;
        const res = await Manager.req('api/forms/titles/?is_without_ex=true');
        const map = { genres: {}, categories: {} };
        if (res && res.content) {
            if (res.content.genres) res.content.genres.forEach(g => map.genres[g.name.toLowerCase()] = g.id);
            if (res.content.categories) res.content.categories.forEach(c => map.categories[c.name.toLowerCase()] = c.id);
        }

        const fallbackCategories = {
            "веб": 5, "в цвете": 6, "зомби": 14, "демоны": 15, "кулинария": 16, "медицина": 17, "культивация": 18, "зверолюди": 19,
            "магия": 22, "горничные": 23, "средневековье": 25, "антигерой": 26,
            "ниндзя": 30, "офисные работники": 31, "полиция": 32, "самураи": 33,
            "видеоигры": 35, "криминал": 36, "обратный гарем": 40,
            "выживание": 41, "путешествия во времени": 43, "боги": 45, "алхимия": 47, "ангелы": 48,
            "антиутопия": 49, "апокалипсис": 50, "армия": 51, "артефакты": 52, "борьба за власть": 54,
            "будущее": 55, "владыка демонов": 57, "волшебные существа": 59, "воспоминания из другого мира": 60,
            "геймеры": 61, "гильдии": 62, "гг женщина": 63, "гг мужчина": 64, "дружба": 67, "ранги силы": 68,
            "жестокий мир": 69, "животные компаньоны": 70, "игровые элементы": 73, "космос": 76,
            "магическая академия": 78, "месть": 79, "навыки": 80, "наёмники": 81, "насилие / жестокость": 82,
            "нежить": 83, "подземелья": 86, "политика": 87, "разумные расы": 88, "рыцари": 90, "система": 91,
            "скрытие личности": 93, "спасение мира": 94, "супер герои": 95, "учитель / ученик": 96,
            "шантаж": 99, "тупой гг": 109, "гг имба": 110, "умный гг": 111, "управление территорией": 114,
            "исекай": 115, "аристократия": 117, "амнезия": 121, "бои на мечах": 122, "гг не человек": 123,
            "упоротость": 124, "грузовик-сан": 125, "учебное заведение": 126, "аниме": 127,
            "лоли": 108, "хикикомори": 21, "реинкарнация": 13, "монстры": 38
        };
        const fallbackGenres = {
            "экшен": 2, "боевые искусства": 3, "гарем": 5, "героическое фэнтези": 7, "детектив": 8,
            "дзёсэй": 9, "додзинси": 10, "драма": 11, "история": 13, "киберпанк": 14,
            "элементы юмора": 16, "меха": 18, "мистика": 19, "научная фантастика": 20,
            "повседневность": 21, "постапокалиптика": 22, "приключения": 23, "психология": 24,
            "романтика": 25, "сверхъестественное": 27, "сёдзё": 28, "сёнэн": 30,
            "спорт": 32, "сэйнэн": 33, "трагедия": 34, "триллер": 35, "ужасы": 36,
            "фантастика": 37, "фэнтези": 38, "школьники": 39, "этти": 40, "эротика": 42,
            "комедия": 50, "мурим": 51
        };
        Object.assign(map.categories, fallbackCategories);
        Object.assign(map.genres, fallbackGenres);

        try {
            const pagesToCrawl = [
                'api/v2/search/catalog/?count=30&ordering=-score&page=1&unstrict_search_fields=categories',
                'api/v2/search/catalog/?categories=47&count=30&ordering=-score&page=1',
                'api/v2/search/catalog/?categories=127&count=30&ordering=-score&page=1'
            ];
            for (const p of pagesToCrawl) {
                const data = await Manager.req(p);
                if (data && data.results) {
                    data.results.forEach(m => {
                        if (m.genres) m.genres.forEach(g => map.genres[g.name.toLowerCase()] = g.id);
                        if (m.categories) m.categories.forEach(c => map.categories[c.name.toLowerCase()] = c.id);
                    });
                }
            }
        } catch (e) { }

        filterMapCache = map;
        return map;
    }

    function ruToSlug(str) {
        const m = { 'а': 'a', 'б': 'b', 'в': 'v', 'г': 'g', 'д': 'd', 'е': 'e', 'ё': 'e', 'ж': 'zh', 'з': 'z', 'и': 'i', 'й': 'y', 'к': 'k', 'л': 'l', 'м': 'm', 'н': 'n', 'о': 'o', 'п': 'p', 'р': 'r', 'с': 's', 'т': 't', 'у': 'u', 'ф': 'f', 'х': 'kh', 'ц': 'ts', 'ч': 'ch', 'ш': 'sh', 'щ': 'sch', 'ъ': '', 'ы': '', 'ь': '', 'э': 'e', 'ю': 'yu', 'я': 'ya' };
        return str.toLowerCase().replace(/[^а-яёa-z0-9]/g, '').split('').map(c => m[c] !== undefined ? m[c] : c).join('');
    }

    async function executeBPTask(card) {
        const t = card.textContent.toLowerCase();
        const uid = Manager.userId || '';
        const go = (path) => {
            Manager.showStatus('⏳ Перенаправление...', true);
            setTimeout(() => { Manager.showStatus('', false); window.open((path.startsWith('http') ? path : SITE_DOMAIN + path), '_blank'); }, 500);
        };
        const goX = async (fn) => {
            Manager.showStatus('⏳ Подготовка редиректа...', true);
            try {
                const path = await fn();
                go(path);
            } catch (e) { go('/'); }
        };

        if (t.includes('разнообразие жанров')) go('/catalog/?exclude_bookmarks=51891154%2C51891156%2C51891155%2C51891160%2C51891158%2C51891157%2C51891159&ordering=-score');
        else if (t.includes('новые карточки')) go('/deck/10/open');
        else if (t.includes('путешествие по мирам')) go('/catalog?count_chapters=300-&ordering=-score&unstrict_search_fields=genres%2Ccategories');
        else if (t.includes('шпион') || t.includes('чужой профиль') || t.includes('дружеский жест')) {
            const rndId = Math.floor(Math.random() * 2000000) + 1;
            go(`/user/${rndId}/about`);
        }
        else if (t.includes('личное')) go(`/user/${uid}/about`);
        else if (
            t.includes('волна лайков') ||
            t.includes('читательский марафон') || t.includes('погружение в истории') ||
            t.includes('даритель любви') ||
            t.includes('глава дня')
        ) go('/catalog?count_chapters=300-&ordering=-score&unstrict_search_fields=genres%2Ccategories');

        else if (t.includes('вперёд за покупками') || t.includes('вперед за покупками')) go('/customization/feed/frame?ordering=cost');
        else if (t.includes('купи её') || t.includes('коллекционер историй') || t.includes('потрать тикеты')) go('/manga/one_hundred_to_make_god/chapters?ordering=index');
        else if (t.includes('оценка эксперта')) go('/catalog/?count_chapters=10-&exclude_bookmarks=51891154%2C51891155%2C51891156%2C51891160&ordering=-score&unstrict_search_fields=genres%2Ccategories');
        else if (t.includes('оно похоже')) {
            const similarLinks = [
                '/manga/pinaty/similar',
                '/manga/the-greatest-estate-designer_/similar',
                '/manga/trash-of-the-counts-family/similar',
                '/manga/one_punch_man_/similar',
                '/manga/omniscient-reader_/similar',
                '/manga/solo-leveling_/similar',
                '/manga/sss-level-hunter_/similar'
            ];
            go(similarLinks[Math.floor(Math.random() * similarLinks.length)]);
        }

        else if (t.includes('одобрительный отклик') || t.includes('оценка мнения')) go('/manga/pinaty/comments');

        else if (t.includes('моё мнение') || t.includes('мое мнение') || t.includes('ответ на мнение')) {
            const commentLinks = [
                '/manga/pinaty/comments',
                '/manga/god-of-ad-libs/comments',
                '/manga/i-was-immediately-mistaken-for-a-genius-actor_/comments',
                '/manga/omniscient-reader_/comments',
                '/manga/regression-instruction_/comments',
                '/manga/the-pick-me-up_/comments'
            ];
            go(commentLinks[Math.floor(Math.random() * commentLinks.length)]);
        }

        else if (t.includes('новый образ')) go(`/user/${uid}/inventory?type=customization&shopType=frames`);

        else if (t.includes('найди пару')) go(`/user/${uid}/mini-games`);

        else if (t.includes('я мы одно целое') || t.includes('мы одно целое')) go('/guild/top?guild-top_tab=without-regression');
        else if (t.includes('целое') || t.includes('гильди')) go('/guild');
        else if (t.includes('улучши') || t.includes('апгрейд') || t.includes('создай')) go(`/user/inventory/cards-upgrade?ordering=-id&card__rank=rank_f&is_favorite=false`);
        else if (t.includes('обменяемся') || t.includes('обмен')) {
            goX(async () => {
                let allFriends = [];
                const p1 = await Manager.req(`${API_DOMAIN}/api/v2/users/${uid}/friends/?count=20&page=1`);
                if (p1 && p1.results) allFriends.push(...p1.results);
                const p2 = await Manager.req(`${API_DOMAIN}/api/v2/users/${uid}/friends/?count=20&page=2`);
                if (p2 && p2.results) allFriends.push(...p2.results);

                if (allFriends.length > 0) {
                    const rnd = allFriends[Math.floor(Math.random() * allFriends.length)];
                    return `/user/${rnd.id}/create/exchange`;
                }
                const rndId = Math.floor(Math.random() * 2000000) + 1;
                return `/user/${rndId}/create/exchange`;
            });
        }
        else if (t.includes('золот') || t.includes('баланс')) go(`/user/${uid}/balance`);

        else if (t.includes('категори') || t.includes('жанр')) {
            goX(async () => {
                const map = await getFilterMap();
                if (!map) return '/catalog';

                const allBpButtons = Array.from(document.querySelectorAll('button'))
                    .filter(b => b.textContent && b.textContent.trim().toLowerCase() === 'получить');

                const taskButtons = allBpButtons.filter((b, idx) => {
                    let card = b;
                    for (let i = 0; i < 6; i++) {
                        if (!card.parentElement) break;
                        card = card.parentElement;
                        if (card.textContent.length > 50 && card.querySelector('.font-bold, .font-semibold, h1, h2, h3, h4, h5, b, strong')) break;
                    }
                    if (!card) return false;
                    const t = card.textContent.toLowerCase();
                    return t.includes('категори') || t.includes('жанр');
                });

                const knownTags = [...Object.keys(map.genres), ...Object.keys(map.categories)]
                    .filter(t => t.length >= 2)
                    .sort((a, b) => b.length - a.length);

                let taskRequirements = [];
                taskButtons.forEach((b, idx) => {
                    let card = b;
                    for (let i = 0; i < 6; i++) {
                        if (!card.parentElement) break;
                        card = card.parentElement;
                        if (card.textContent.length > 50 && card.querySelector('.font-bold, .font-semibold, h1, h2, h3, h4, h5, b, strong')) break;
                    }

                    let taskText = '';
                    if (card) {
                        const t = card.textContent.toLowerCase();
                        if (t.includes('категори') || t.includes('жанр')) {
                            const progressMatch = card.textContent.replace(/\s+/g, '').match(/(\d+)\/(\d+)/);
                            const done = progressMatch && parseInt(progressMatch[1], 10) >= parseInt(progressMatch[2], 10);
                            if (done) {
                            } else {
                                taskText = t;
                            }
                        }
                    }

                    if (taskText) {
                        const foundItems = knownTags.filter(tag => taskText.includes(tag));
                        if (foundItems.length > 0) {
                            taskRequirements.push(foundItems);
                        }
                    }
                });

                if (taskRequirements.length === 0) {
                    if (t.includes('разнообразие жанров')) return '/catalog/?exclude_bookmarks=51891154%2C51891156%2C51891155%2C51891160%2C51891158%2C51891157%2C51891159&ordering=-score';
                    return '/catalog';
                }

                let validReqs = taskRequirements.map(reqList => reqList.filter(item => map.genres[item] || map.categories[item])).filter(list => list.length > 0);

                const selectedTags = new Set();
                const selectedCategoryIds = [];
                const selectedGenreIds = [];

                validReqs.forEach(reqList => {
                    const shuffled = [...reqList];
                    for (let i = shuffled.length - 1; i > 0; i--) {
                        const j = Math.floor(Math.random() * (i + 1));
                        [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
                    }

                    let item = shuffled.find(tag => {
                        const id = map.genres[tag] || map.categories[tag];
                        return id && !selectedCategoryIds.includes(id) && !selectedGenreIds.includes(id);
                    });
                    if (!item) item = shuffled[0];

                    if (item) {
                        selectedTags.add(item);
                        if (map.genres[item]) selectedGenreIds.push(map.genres[item]);
                        else if (map.categories[item]) selectedCategoryIds.push(map.categories[item]);
                    }
                });

                let query = '?ordering=-score&unstrict_search_fields=';
                if (selectedCategoryIds.length) query += `&categories=${selectedCategoryIds.join(',')}`;
                if (selectedGenreIds.length) query += `&genres=${selectedGenreIds.join(',')}`;
                query += '&count_chapters=1-';

                return `/catalog/${query}`;
            });
        }
        else if (t.includes('похоже на триплет')) go('/user/inventory/cards-upgrade?ordering=-id&card__rank=rank_f&is_favorite=false');

        else go('/');
    }

    function injectBattlepassExecuteButtons() {
        if (!location.pathname.includes('/user/battlepass/tasks')) return;

        document.querySelectorAll('.rem-bp-exec-btn').forEach(execBtn => {
            let card = execBtn;
            for (let i = 0; i < 6; i++) {
                if (!card.parentElement) break;
                card = card.parentElement;
                if (card.textContent.length > 50 && card.querySelector('.font-bold, .font-semibold, h1, h2, h3, h4, h5, b, strong')) break;
            }
            if (!card) { execBtn.remove(); return; }

            let hasGetBtn = Array.from(card.querySelectorAll('button')).some(b => b.textContent && b.textContent.trim().toLowerCase() === 'получить');

            const textContent = card.textContent.replace(/\s+/g, '');
            const progressMatch = textContent.match(/(\d+)\/(\d+)/);
            if (!hasGetBtn || (progressMatch && parseInt(progressMatch[1], 10) >= parseInt(progressMatch[2], 10))) {
                execBtn.remove();
                delete card.dataset.remExecuteInjected;
            }
        });

        const btns = Array.from(document.querySelectorAll('button')).filter(b => b.textContent && b.textContent.trim().toLowerCase() === 'получить');
        btns.forEach(btn => {
            let card = btn;
            for (let i = 0; i < 6; i++) {
                if (!card.parentElement) break;
                card = card.parentElement;
                if (card.textContent.length > 50 && card.querySelector('.font-bold, .font-semibold, h1, h2, h3, h4, h5, b, strong')) break;
            }
            if (!card) return;

            const textContent = card.textContent.replace(/\s+/g, '');
            const progressMatch = textContent.match(/(\d+)\/(\d+)/);
            if (progressMatch && parseInt(progressMatch[1], 10) >= parseInt(progressMatch[2], 10)) {
                return;
            }

            if (card.dataset.remExecuteInjected) return;

            const cardText = card.textContent.toLowerCase();
            const noButton = [
                'давний знакомый',
                'больше золота', 'найди его', 'найди отличие',
                'я всё знаю', 'я все знаю', 'они разные'
            ];
            if (noButton.some(kw => cardText.includes(kw))) return;

            card.dataset.remExecuteInjected = "1";

            const btnContainer = btn.parentNode;
            const executeBtn = document.createElement('button');
            executeBtn.className = 'rem-bp-exec-btn cs-button z-0 cursor-pointer group relative inline-flex items-center justify-center box-border appearance-none select-none whitespace-nowrap font-medium subpixel-antialiased transition-all tap-highlight-transparent transform-gpu data-[pressed=true]:scale-[0.97] opacity-100 outline-none focus-visible:outline-hidden ring-0 rounded-full px-4 h-9 min-w-9 text-sm bg-primary text-primary-foreground hover:bg-primary/90';
            executeBtn.style.marginRight = '8px';
            executeBtn.innerHTML = 'Выполнить';
            executeBtn.onclick = (e) => {
                e.preventDefault(); e.stopPropagation();
                executeBPTask(card);
            };
            btnContainer.style.display = 'flex';
            btnContainer.style.alignItems = 'center';
            btnContainer.insertBefore(executeBtn, btn);
        });
    }

    async function solveMemoryGame() {
        const DELAY_CLICK = 400;
        const DELAY_PAIR = 900;
        const delay = ms => new Promise(r => setTimeout(r, ms));

        const buttons = [...document.querySelectorAll('button.relative.flex')]
            .filter(btn => btn.querySelector('img[alt="card"]'));

        if (buttons.length < 16) return false;

        const fiberKey = Object.keys(buttons[0]).find(k =>
            k.startsWith('__reactFiber$') || k.startsWith('__reactInternalInstance$')
        );
        if (!fiberKey) return false;

        let fiber = buttons[0][fiberKey];
        let cardsData = null;
        while (fiber && !cardsData) {
            let hook = fiber.memoizedState;
            while (hook) {
                const val = hook.memoizedState;
                if (Array.isArray(val) && val.length === 16 && val[0] && typeof val[0] === 'object' && val[0].symbol !== undefined) {
                    cardsData = val;
                    break;
                }
                if (hook.queue && hook.queue.lastRenderedState) {
                    const qs = hook.queue.lastRenderedState;
                    if (Array.isArray(qs) && qs.length === 16 && qs[0] && typeof qs[0] === 'object' && qs[0].symbol !== undefined) {
                        cardsData = qs;
                        break;
                    }
                }
                hook = hook.next;
            }
            fiber = fiber.return;
        }

        if (!cardsData) return false;

        const groups = {};
        cardsData.forEach((card, i) => {
            const key = String(card.symbol);
            if (!groups[key]) groups[key] = [];
            groups[key].push(i);
        });

        for (const indices of Object.values(groups)) {
            if (indices.length >= 2) {
                buttons[indices[0]].click();
                await delay(DELAY_CLICK);
                buttons[indices[1]].click();
                await delay(DELAY_PAIR);
            }
        }
        return true;
    }

    function injectMemoryGameButton() {
        if (!location.pathname.includes('/user/battlepass/games/memory')) return;
        if (document.querySelector('.rem-bp-exec-btn[data-rem-memory]')) return;

        const headers = document.querySelectorAll('p.cs-text');
        let descEl = null;
        for (const h of headers) {
            if (h.textContent.trim() === 'Игра на память') {
                descEl = h.nextElementSibling;
                break;
            }
        }
        if (!descEl || descEl.dataset.remMemoryInjected) return;
        descEl.dataset.remMemoryInjected = '1';

        const btn = document.createElement('button');
        btn.className = 'rem-bp-exec-btn cs-button z-0 cursor-pointer group relative inline-flex items-center justify-center box-border appearance-none select-none whitespace-nowrap font-medium subpixel-antialiased transition-all tap-highlight-transparent transform-gpu data-[pressed=true]:scale-[0.97] opacity-100 outline-none focus-visible:outline-hidden ring-0 rounded-full px-4 h-9 min-w-9 text-sm bg-primary text-primary-foreground hover:bg-primary/90';
        btn.style.marginRight = '8px';
        btn.dataset.remMemory = '1';
        btn.textContent = 'Выполнить';
        btn.onclick = async (e) => {
            e.preventDefault();
            e.stopPropagation();
            btn.disabled = true;
            btn.textContent = '⏳ Решаю...';
            const ok = await solveMemoryGame().catch(() => false);
            btn.textContent = ok ? '✅ Решено!' : '⚠️ Ошибка';
            setTimeout(() => {
                btn.textContent = 'Выполнить';
                btn.disabled = false;
            }, 3000);
        };

        descEl.textContent = '';
        descEl.appendChild(btn);
    }

    async function solvePuzzleGame() {
        const delay = ms => new Promise(r => setTimeout(r, ms));
        const grid = document.querySelector('div.grid[style*="grid-template-columns:repeat(4, 1fr)"]');
        if (!grid) return false;

        const parseId = (el) => {
            const img = el.querySelector('img');
            let src = img ? img.src : el.style.backgroundImage;
            if (!src || src === 'none') return 0;
            const url = src.replace(/url\(["']?|["']?\)/g, "");
            const filename = Manager.getFilename(url);
            const m = filename.match(/(\d+)/) || url.match(/\/(\d+)\./);
            return m ? parseInt(m[1]) : 0;
        };

        const getSortedCells = () => {
            return Array.from(grid.children).slice(0, 16).sort((a, b) => {
                const ra = a.getBoundingClientRect(), rb = b.getBoundingClientRect();
                if (Math.abs(ra.top - rb.top) > 5) return ra.top - rb.top;
                return ra.left - rb.left;
            });
        };

        let state = [];
        for (let attempt = 0; attempt < 20; attempt++) {
            state = getSortedCells().map(cell => parseId(cell));
            if (new Set(state.filter(v => v !== 0)).size === 15 && state.length === 16) break;
            await delay(150);
        }

        if (new Set(state).size < 16) return false;

        const goal = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0];
        const gPos = new Array(16); goal.forEach((v, i) => gPos[v] = [i >> 2, i & 3]);

        const solve = async (start) => {
            const weight = 5.0;
            const startTime = performance.now();
            const getH = (s) => {
                let d = 0;
                for (let i = 0; i < 16; i++) {
                    if (s[i] === 0) continue;
                    const p = gPos[s[i]];
                    d += Math.abs(p[0] - (i >> 2)) + Math.abs(p[1] - (i & 3));
                }
                return d;
            };

            let heap = [{ s: start, p: [], h: getH(start), g: 0 }];
            const push = (v) => {
                heap.push(v); let i = heap.length - 1;
                while (i > 0) {
                    let p = (i - 1) >> 1;
                    if (heap[i].g + heap[i].h * weight < heap[p].g + heap[p].h * weight) {
                        [heap[i], heap[p]] = [heap[p], heap[i]]; i = p;
                    } else break;
                }
            };
            const pop = () => {
                if (heap.length === 0) return null;
                const top = heap[0], last = heap.pop();
                if (heap.length > 0) {
                    heap[0] = last; let i = 0;
                    while (true) {
                        let l = (i << 1) + 1, r = (i << 1) + 2, s = i;
                        if (l < heap.length && heap[l].g + heap[l].h * weight < heap[s].g + heap[s].h * weight) s = l;
                        if (r < heap.length && heap[r].g + heap[r].h * weight < heap[s].g + heap[s].h * weight) s = r;
                        if (s === i) break;
                        [heap[i], heap[s]] = [heap[s], heap[i]]; i = s;
                    }
                }
                return top;
            };

            let v = new Set([start.join(',')]);
            while (heap.length > 0) {
                if (v.size % 2000 === 0 && performance.now() - startTime > 3000) break;
                const c = pop();
                if (c.h === 0) return c.p;
                const z = c.s.indexOf(0);
                [z - 4, z + 4, z - 1, z + 1].forEach(ni => {
                    if (ni >= 0 && ni < 16 && (Math.abs(ni - z) !== 1 || (ni >> 2) === (z >> 2))) {
                        const ns = [...c.s];[ns[z], ns[ni]] = [ns[ni], ns[z]];
                        const key = ns.join(',');
                        if (!v.has(key)) { v.add(key); push({ s: ns, p: [...c.p, ni], h: getH(ns), g: c.g + 1 }); }
                    }
                });
            }
            return null;
        };

        if (state.every((v, i) => v === goal[i])) return true;
        const path = await solve(state);
        if (!path) return false;

        for (const idx of path) {
            const cells = getSortedCells();
            if (cells[idx]) cells[idx].click();
            await delay(150);
        }
        return true;
    }

    function injectPuzzleGameButton() {
        if (!location.pathname.includes('/user/battlepass/games/puzzle')) return;
        if (document.querySelector('.rem-bp-exec-btn[data-rem-puzzle]')) return;
        const headers = document.querySelectorAll('h1.cs-text');
        let descEl = null;
        for (const h of headers) {
            if (h.textContent.trim() === 'Собери пазл') {
                descEl = h.nextElementSibling;
                break;
            }
        }
        if (!descEl || descEl.dataset.remPuzzleInjected) return;
        descEl.dataset.remPuzzleInjected = '1';
        const btn = document.createElement('button');
        btn.className = 'rem-bp-exec-btn cs-button z-0 cursor-pointer group relative inline-flex items-center justify-center box-border appearance-none select-none whitespace-nowrap font-medium subpixel-antialiased transition-all tap-highlight-transparent transform-gpu data-[pressed=true]:scale-[0.97] opacity-100 outline-none focus-visible:outline-hidden ring-0 rounded-full px-4 h-9 min-w-9 text-sm bg-primary text-primary-foreground hover:bg-primary/90';
        btn.style.marginTop = '12px';
        btn.dataset.remPuzzle = '1';
        btn.textContent = 'Собрать пазл';
        btn.onclick = async (e) => {
            e.preventDefault(); e.stopPropagation();
            btn.disabled = true;
            btn.textContent = '⏳ Собираю...';
            const ok = await solvePuzzleGame().catch(() => false);
            btn.textContent = ok ? '✅ Готово!' : '⚠️ Обнови страницу';
            setTimeout(() => { btn.textContent = 'Собрать пазл'; btn.disabled = false; }, 3000);
        };
        descEl.parentNode.appendChild(btn);
    }

    function injectDifferenceGameMod() {
        if (!location.pathname.includes('/user/battlepass/games/difference')) return;
        const img1 = document.querySelector('img[alt="Найди отличия 1"]');
        const img2 = document.querySelector('img[alt="Найди отличия 2"]');
        if (!img1 || !img2) return;
        const c1 = img1.closest('.relative.cursor-crosshair');
        const c2 = img2.closest('.relative.cursor-crosshair');
        if (!c1 || !c2) return;
        const targetContainer = c1.parentElement;
        if (!targetContainer || targetContainer.dataset.remDiffInjected) return;
        targetContainer.dataset.remDiffInjected = '1';
        targetContainer.style.display = 'block';
        targetContainer.style.position = 'relative';
        targetContainer.style.maxWidth = '600px';
        targetContainer.style.margin = '-25px auto';
        c1.style.width = '100%';
        c1.style.height = 'auto';
        c2.style.position = 'absolute';
        c2.style.top = '0';
        c2.style.left = '0';
        c2.style.width = '100%';
        c2.style.height = '100%';
        c2.style.opacity = '0';
        c2.style.pointerEvents = 'none';
        const btn = document.createElement('button');
        btn.className = 'rem-bp-exec-btn cs-button z-0 cursor-pointer group relative inline-flex items-center justify-center box-border appearance-none select-none whitespace-nowrap font-medium subpixel-antialiased transition-all tap-highlight-transparent transform-gpu data-[pressed=true]:scale-[0.97] opacity-100 outline-none focus-visible:outline-hidden ring-0 rounded-full px-4 h-9 min-w-9 text-sm bg-primary text-primary-foreground hover:bg-primary/90';
        btn.style.marginTop = '2px';
        btn.style.marginLeft = 'auto';
        btn.style.marginRight = 'auto';
        btn.style.display = 'flex';
        btn.textContent = 'Поменять';
        let showFirst = true;
        btn.onclick = (e) => {
            e.preventDefault();
            showFirst = !showFirst;
            c2.style.opacity = showFirst ? '0' : '1';
            c2.style.pointerEvents = showFirst ? 'none' : 'auto';
        };
        const textContainer = targetContainer.previousElementSibling;
        if (textContainer && textContainer.classList.contains('text-center')) {
            textContainer.appendChild(btn);
        } else {
            targetContainer.parentNode.insertBefore(btn, targetContainer);
        }
    }

    const p = new URLSearchParams(location.search);
    if (p.get('fix_id')) {
        const run = () => runFix(p.get('fix_id'), p.get('fix_name'));
        if (document.readyState === 'loading') addEventListener('DOMContentLoaded', run); else run();
    }

    const requestQueue = [];
    let activeRequestsCount = 0;
    const MAX_CONCURRENT_REQUESTS = 3;

    function queueRequest(fn) {
        return new Promise((resolve, reject) => {
            requestQueue.push({ fn, resolve, reject });
            processQueue();
        });
    }

    function processQueue() {
        if (activeRequestsCount >= MAX_CONCURRENT_REQUESTS || requestQueue.length === 0) return;
        const { fn, resolve, reject } = requestQueue.shift();
        activeRequestsCount++;
        fn().then(resolve).catch(reject).finally(() => {
            activeRequestsCount--;
            setTimeout(() => processQueue(), 120);
        });
    }

    async function fetchUserCardAcquiredTime(userId, cardId) {
        const cacheKey = `user_card_time_${userId}_${cardId}`;
        if (Manager.cacheMap.has(cacheKey)) {
            return Manager.cacheMap.get(cacheKey);
        }

        return queueRequest(async () => {
            try {
                const url = `${API_DOMAIN}/api/v2/inventory/${userId}/?type=cards&count=50&page=1`;
                const data = await Manager.req(url);
                if (data && data.results) {
                    let foundCreatedAt = null;
                    data.results.forEach(item => {
                        if (item.card) {
                            const k = `user_card_time_${userId}_${item.card.id}`;
                            Manager.cacheMap.set(k, item.created_at || null);
                            if (String(item.card.id) === String(cardId)) {
                                foundCreatedAt = item.created_at || null;
                            }
                        }
                    });
                    return foundCreatedAt;
                }
            } catch (e) {
                console.error('REM: Error fetching user card time:', e);
            }
            Manager.cacheMap.set(cacheKey, null);
            return null;
        });
    }

    async function scanCardPageNewBadges() {
        const match = location.pathname.match(/^\/card\/(\d+)/);
        if (!match) return;
        const cardId = match[1];

        const userLinks = document.querySelectorAll('a[href*="/user/"]:not([data-rem-avatar-checked])');
        for (const link of userLinks) {
            const m = link.getAttribute('href').match(/\/user\/(\d+)/);
            if (!m) continue;
            if (link.closest('header, nav')) continue;
            const userId = m[1];
            link.setAttribute('data-rem-avatar-checked', '1');

            const avatarWrapper = link.querySelector('.relative') || link.querySelector('[data-slot="avatar"]') || link;
            if (!avatarWrapper) continue;

            fetchUserCardAcquiredTime(userId, cardId).then(createdAt => {
                if (!createdAt) return;
                const createdTime = new Date(createdAt).getTime();
                const diffDays = (Date.now() - createdTime) / (1000 * 60 * 60 * 24);
                if (diffDays < 3) {
                    if (window.getComputedStyle(avatarWrapper).position === 'static') {
                        avatarWrapper.style.position = 'relative';
                    }
                    if (!avatarWrapper.querySelector('.rem-user-new-badge')) {
                        const badge = document.createElement('div');
                        badge.className = 'rem-user-new-badge';
                        badge.innerText = 'New';
                        avatarWrapper.appendChild(badge);
                    }
                }
            });
        }
    }

    window.addEventListener('load', () => Manager.init());

    const deckCountCache = new Map();
    const deckCountPending = new Set();

    async function fetchDeckOpenedCount(deckId) {
        if (!Manager.userId) return 0;
        if (deckCountCache.has(deckId)) return deckCountCache.get(deckId);
        if (deckCountPending.has(deckId)) return -1;
        deckCountPending.add(deckId);
        try {
            const data = await Manager.req(`${API_DOMAIN}/api/v2/inventory/decks/?is_opened=true&user_id=${Manager.userId}&deck_id=${deckId}&page=1`);
            const count = data && data.count !== undefined ? data.count : 0;
            deckCountCache.set(deckId, count);
            return count;
        } catch (e) {
            deckCountCache.set(deckId, 0);
            return 0;
        } finally {
            deckCountPending.delete(deckId);
        }
    }

    function isDeckShopPage() {
        return /\/customization\/feed\/deck/.test(location.pathname + location.search);
    }

    function isDeckInventoryPage() {
        return /\/user\/\d+\/inventory/.test(location.pathname) && /type=decks/.test(location.search);
    }

    function isDeckInnerPage() {
        return /\/deck\/\d+\/open/.test(location.pathname);
    }

    let lastDeckScanUrl = '';

    async function scanDeckCards() {
        if (!Manager.userId) return;
        if (!isDeckShopPage() && !isDeckInventoryPage()) return;

        const currentUrl = location.pathname + location.search;
        if (lastDeckScanUrl !== currentUrl) {
            lastDeckScanUrl = currentUrl;
            document.querySelectorAll('.rem-deck-count-badge').forEach(b => b.remove());
            document.querySelectorAll('[data-rem-deck-counted]').forEach(el => el.removeAttribute('data-rem-deck-counted'));
        }

        const deckLinks = document.querySelectorAll('a[href*="/deck/"][href*="/open"]:not([data-rem-deck-counted])');
        for (const link of deckLinks) {
            const m = link.getAttribute('href').match(/\/deck\/(\d+)\/open/);
            if (!m) continue;
            const deckId = m[1];
            link.setAttribute('data-rem-deck-counted', '1');

            const cardEl = link.querySelector('.cs-item-card, .relative') || link.firstElementChild;
            if (!cardEl) continue;
            if (window.getComputedStyle(cardEl).position === 'static') cardEl.style.position = 'relative';
            if (cardEl.querySelector('.rem-deck-count-badge')) continue;

            const badge = document.createElement('div');
            badge.className = 'rem-deck-count-badge';
            badge.textContent = '...';
            cardEl.appendChild(badge);

            fetchDeckOpenedCount(deckId).then(count => {
                if (count < 0) return;
                badge.textContent = count;
                if (count > 0) badge.classList.add('has-count');
            });
        }
    }

    async function scanDeckInnerPage() {
        if (!Manager.userId) return;
        if (!isDeckInnerPage()) return;
        if (document.querySelector('.rem-deck-inner-count')) return;

        const m = location.pathname.match(/\/deck\/(\d+)\/open/);
        if (!m) return;
        const deckId = m[1];

        const headings = document.querySelectorAll('h2, h3, h4, p, span, div');
        let targetEl = null;
        for (const el of headings) {
            const txt = el.textContent.trim();
            if (txt === 'Содержимое пака' && !el.querySelector('.rem-deck-inner-count')) {
                targetEl = el;
                break;
            }
        }
        if (!targetEl) return;

        const countEl = document.createElement('div');
        countEl.className = 'rem-deck-inner-count';
        countEl.innerHTML = '📦 Открыто: <strong>...</strong>';
        targetEl.parentNode.insertBefore(countEl, targetEl);

        const count = await fetchDeckOpenedCount(deckId);
        countEl.innerHTML = `📦 Открыто: <strong>${count >= 0 ? count : '...'}</strong>`;
    }

    const selectionState = {
        selected: new Set()
    };
    let dragStartCard = null;
    let dragStartPos = { x: 0, y: 0 };
    let isDragging = false;
    let dragAction = 'select';
    let hasDragged = false;
    let lastSelectionPath = '';

    function getCardContainer(target) {
        if (!target) return null;
        if (target.closest('#rem-selection-panel, #rem-settings-panel, #rem-cfg-btn, #rem-exchange-panel, #rem-cardbattle-panel')) return null;

        const wrapper = target.closest('a[href*="/card/"], .cs-card-item, .rem-card, [data-sentry-component="CardItem"]');
        if (wrapper) return wrapper;

        const media = target.closest('img[src*="/media/"], video[src*="/media/"], [style*="/media/card-item/"]');
        if (media) {
            return media.closest('button, a, [class*="card"], [class*="item"]') || media.parentElement;
        }

        if (target.tagName === 'DIV' || target.tagName === 'BUTTON') {
            const imgs = target.querySelectorAll('img[src*="/media/"]');
            if (imgs.length === 1) return target;
        }

        return null;
    }

    function getCardIdFromFiber(el) {
        if (!el) return null;
        try {
            let currentDom = el;
            for (let j = 0; j < 3; j++) {
                if (!currentDom) break;
                const keys = Object.keys(currentDom);
                const fiberKey = keys.find(k => k.startsWith('__reactFiber$') || k.startsWith('__reactInternalInstance$'));
                if (fiberKey) {
                    let node = currentDom[fiberKey];
                    let depth = 0;
                    while (node && depth < 50) {
                        const propsList = [node.memoizedProps, node.pendingProps];
                        for (let i = 0; i < propsList.length; i++) {
                            const props = propsList[i];
                            if (!props) continue;
                            if (props.card && props.card.id) return props.card.id;
                            if (props.item && props.item.id) return props.item.id;
                            if (props.card_id) return props.card_id;
                            if (props.cardId) return props.cardId;
                            if (props.data && props.data.id) return props.data.id;
                            if (props.id && (props.rank !== undefined || props.score !== undefined || props.wish_type !== undefined || props.stack_count !== undefined || props.character !== undefined || props.cost !== undefined || props.is_favorite !== undefined)) {
                                return props.id;
                            }
                        }
                        node = node.return;
                        depth++;
                    }
                }
                currentDom = currentDom.parentElement;
            }
        } catch (e) { }
        return null;
    }

    function getCardIdFromElement(el) {
        if (!el) return null;
        if (el.dataset && el.dataset.remCardId) return el.dataset.remCardId;

        let fId = getCardIdFromFiber(el);
        if (fId) return String(fId);

        const innerMedia = el.querySelector('img, video, [style*="/media/card-item/"]');
        if (innerMedia) {
            fId = getCardIdFromFiber(innerMedia);
            if (fId) return String(fId);
        }

        const a = (el.tagName === 'A' && el.getAttribute('href') && el.getAttribute('href').includes('/card/')) ? el :
            (el.querySelector('a[href*="/card/"]') || el.closest('a[href*="/card/"]'));
        if (a) {
            const href = a.getAttribute('href');
            const m = href.match(/\/card\/(\d+)/);
            if (m) return m[1];
        }

        const media = (el.tagName === 'IMG' || el.tagName === 'VIDEO') ? el :
            (el.querySelector('img[src*="/media/"], video[src*="/media/"]') ||
                el.closest('img[src*="/media/"], video[src*="/media/"]'));
        if (media) {
            let src = media.src || media.getAttribute('style') || '';
            const m = src.match(/\/media\/[^\s)"']+/);
            if (m) {
                let fname = m[0].split('/').pop().split('?')[0].replace(/\.[^.]+$/, '');
                if (fname) {
                    if (/^\d+$/.test(fname)) return fname;
                    if (typeof Manager !== 'undefined' && Manager.coverToIdMap && Manager.coverToIdMap[fname]) {
                        return String(Manager.coverToIdMap[fname]);
                    }
                }
            }
        }
        return null;
    }

    function getCardContainers() {
        const containers = new Set();
        document.querySelectorAll('a[href*="/card/"], .cs-card-item, .rem-card, [data-sentry-component="CardItem"]').forEach(el => {
            if (!el.closest('#rem-selection-panel, #rem-settings-panel, #rem-cfg-btn, #rem-exchange-panel, #rem-cardbattle-panel')) {
                containers.add(el);
            }
        });
        document.querySelectorAll('img[src*="/media/"], video[src*="/media/"]').forEach(media => {
            const block = media.closest('.cs-card-item, .rem-card, [data-sentry-component="CardItem"], a, button') || media.parentElement;
            if (block && !block.closest('#rem-selection-panel, #rem-settings-panel, #rem-cfg-btn, #rem-exchange-panel, #rem-cardbattle-panel')) {
                containers.add(block);
            }
        });
        return Array.from(containers);
    }

    function updateSelectionPanel() {
        let panel = document.getElementById('rem-selection-panel');
        if (!panel) {
            panel = document.createElement('div');
            panel.id = 'rem-selection-panel';
            panel.className = 'rem-selection-panel';
            panel.innerHTML = `
                <style>
                    #rem-selection-panel {
                        z-index: 45 !important;
                    }
                    @media (max-width: 768px) {
                        #rem-selection-panel {
                            bottom: 56px !important;
                            padding: 8px 10px !important;
                            gap: 6px !important;
                            width: 100vw !important;
                            border-radius: 16px 16px 0 0 !important;
                            flex-wrap: wrap !important;
                            justify-content: center !important;
                            box-sizing: border-box !important;
                        }
                        .rem-selection-btn {
                            height: 30px !important;
                            padding: 0 8px !important;
                            font-size: 11px !important;
                            flex: 1 1 calc(33% - 6px) !important;
                        }
                        .rem-selection-btn.clear {
                            flex: 0 0 auto !important;
                        }
                        .rem-selection-info {
                            width: 100% !important;
                            text-align: center !important;
                            margin-bottom: 2px !important;
                            font-size: 12px !important;
                        }
                    }
                </style>
                <div class="rem-selection-info">Выделено: <strong id="rem-sel-count">0</strong></div>
                <button class="rem-selection-btn want" id="rem-sel-want">💙 Хочу</button>
                <button class="rem-selection-btn sell" id="rem-sel-sell">🔶 Продаю</button>
                <button class="rem-selection-btn delete" id="rem-sel-delete">🗑️ Удалить</button>
                <button class="rem-selection-btn clear" id="rem-sel-clear">✕</button>
            `;
            document.body.appendChild(panel);

            panel.querySelector('#rem-sel-want').onclick = (e) => { e.preventDefault(); e.stopPropagation(); processSelection('want'); };
            panel.querySelector('#rem-sel-sell').onclick = (e) => { e.preventDefault(); e.stopPropagation(); processSelection('sell'); };
            panel.querySelector('#rem-sel-delete').onclick = (e) => { e.preventDefault(); e.stopPropagation(); processSelection('delete'); };
            panel.querySelector('#rem-sel-clear').onclick = (e) => { e.preventDefault(); e.stopPropagation(); clearSelection(); };
        }

        if (selectionState.selected.size > 0 && cfg.cardSelection) {
            const countEl = document.getElementById('rem-sel-count');
            if (countEl) countEl.innerText = selectionState.selected.size;
            panel.classList.add('open');
        } else {
            panel.classList.remove('open');
        }

        const visible = getCardContainers();
        visible.forEach(cardEl => {
            const cid = getCardIdFromElement(cardEl);
            if (cid && selectionState.selected.has(cid) && cfg.cardSelection) {
                cardEl.classList.add('rem-card-selected');
            } else {
                cardEl.classList.remove('rem-card-selected');
            }
        });
    }

    function clearSelection() {
        selectionState.selected.clear();
        updateSelectionPanel();
    }

    async function processSelection(action) {
        if (selectionState.selected.size === 0) return;
        const ids = Array.from(selectionState.selected);
        Manager.showStatus(`Обработка ${ids.length} карт...`, true);

        const getCookie = (name) => {
            const v = document.cookie.match('(^|;) ?' + name + '=([^;]*)(;|$)');
            return v ? v[2] : null;
        };
        const csrf = getCookie('csrftoken') || '';

        for (const cid of ids) {
            try {
                if (action === 'delete') {
                    await fetch(`${API_DOMAIN}/api/v2/inventory/wishes/${cid}/`, {
                        method: 'DELETE',
                        headers: { 'X-CSRFToken': csrf },
                        credentials: 'include'
                    });
                } else {
                    const wType = action === 'want' ? 1 : 2;
                    await fetch(`${API_DOMAIN}/api/v2/inventory/wishes/`, {
                        method: 'POST',
                        headers: {
                            'Content-Type': 'application/json',
                            'X-CSRFToken': csrf
                        },
                        credentials: 'include',
                        body: JSON.stringify({ card: cid, wish_type: wType })
                    });
                }
            } catch (e) { }
            await new Promise(r => setTimeout(r, 200));
        }

        Manager.showStatus('Обновление меток...', true);
        await Manager.syncWishes(true);
        document.querySelectorAll('[data-rem-checked]').forEach(el => el.removeAttribute('data-rem-checked'));
        document.querySelectorAll('.rem-own-badge, .rem-wish-badge').forEach(el => el.remove());
        if (typeof scanVisual === 'function') scanVisual();

        setTimeout(() => Manager.showStatus('', false), 2000);
        clearSelection();
    }

    function initCardSelection() {
        if (!document.getElementById('rem-zindex-fix')) {
            const style = document.createElement('style');
            style.id = 'rem-zindex-fix';
            style.innerHTML = `
                header, nav, [data-sentry-component="Header"] { z-index: 900 !important; }
                .rem-card-selected, .rem-card-selected::before, .rem-card-selected::after { z-index: 5 !important; }
                .rem-own-badge, .rem-wish-badge { z-index: 6 !important; }
                .cs-card-item:hover, .rem-card:hover { z-index: 5 !important; }
            `;
            document.head.appendChild(style);
        }

        let startX = 0, startY = 0, isScroll = false, lastAction = 0;

        const blockEvents = ['click', 'mousedown', 'mouseup', 'pointerdown', 'pointerup', 'touchstart', 'touchend'];

        blockEvents.forEach(eventType => {
            window.addEventListener(eventType, (e) => {
                if (!cfg.cardSelection) return;

                const target = e.target;
                if (target.closest('#rem-selection-panel, #rem-settings-panel, #rem-cfg-btn, #rem-exchange-panel, #rem-cardbattle-panel, header, nav, [data-sentry-component="Header"], .cs-account-menu')) return;

                const cardCont = getCardContainer(target);
                if (!cardCont) return;

                e.stopPropagation();
                e.stopImmediatePropagation();
                if (e.cancelable && !['touchstart', 'pointerdown', 'mousedown', 'touchmove', 'pointermove'].includes(eventType)) {
                    e.preventDefault();
                }

                const touch = e.touches?.[0] || e.changedTouches?.[0];
                const x = touch ? touch.clientX : e.clientX;
                const y = touch ? touch.clientY : e.clientY;

                if (['touchstart', 'pointerdown', 'mousedown'].includes(eventType)) {
                    startX = x;
                    startY = y;
                    isScroll = false;
                }

                if (['touchend', 'pointerup', 'mouseup', 'click'].includes(eventType)) {
                    if (Math.hypot(x - startX, y - startY) > 12) isScroll = true;

                    if (!isScroll) {
                        const now = Date.now();
                        if (now - lastAction < 400) return;
                        lastAction = now;

                        const cardId = getCardIdFromElement(cardCont);
                        if (cardId) {
                            if (selectionState.selected.has(cardId)) {
                                selectionState.selected.delete(cardId);
                                cardCont.classList.remove('rem-card-selected');
                            } else {
                                selectionState.selected.add(cardId);
                                cardCont.classList.add('rem-card-selected');
                            }
                            updateSelectionPanel();
                        }
                    }
                }
            }, true);
        });
    }

    function checkSelectionPageNav() {
        if (location.pathname !== lastSelectionPath) {
            lastSelectionPath = location.pathname;
            clearSelection();
        }
    }

    function getStoredToken() {
        if (interceptedAuthToken && interceptedAuthToken !== 'Bearer null' && interceptedAuthToken !== 'Bearer undefined') {
            return interceptedAuthToken;
        }
        const stored = storageGet('rem_intercepted_auth_token');
        if (stored && stored !== 'Bearer null' && stored !== 'Bearer undefined') {
            return stored;
        }

        const win = typeof unsafeWindow !== 'undefined' ? unsafeWindow : window;
        let raw = '';
        try {
            raw = win.localStorage.getItem('token') || win.localStorage.getItem('accessToken') || win.localStorage.getItem('access_token');
        } catch (e) {}

        if (!raw || raw === 'null' || raw === 'undefined') {
            try {
                raw = localStorage.getItem('token') || localStorage.getItem('accessToken') || localStorage.getItem('access_token');
            } catch (e) {}
        }

        if (raw && raw !== 'null' && raw !== 'undefined') {
            return raw.startsWith('Bearer ') ? raw : `Bearer ${raw}`;
        }

        try {
            const match = document.cookie.match(/(?:^|; )token=([^;]*)/);
            if (match && match[1]) {
                const cookieVal = decodeURIComponent(match[1]);
                if (cookieVal && cookieVal !== 'null' && cookieVal !== 'undefined') {
                    return cookieVal.startsWith('Bearer ') ? cookieVal : `Bearer ${cookieVal}`;
                }
            }
        } catch (e) {}

        return '';
    }

    function initCardBattleAutomation() {
        let raidTimer = null;
        let pvpTimer = null;
        let audioKeepAliveEl = null;

        function startAudioKeepAlive() {
            if (!cfg.audioKeepAliveEnabled) return;
            if (audioKeepAliveEl) return;

            try {
                if (window.navigator.audioSession) {
                    window.navigator.audioSession.type = 'playback';
                }
            } catch (e) {}

            try {
                audioKeepAliveEl = new Audio();
                audioKeepAliveEl.src = 'data:audio/mpeg;base64,SUQzBAAAAAAAI1RTU0UAAAAPAAADTGFtZTMuOTguMgAAAAAAAAAAAAAA//OEAAAAAAAAAAAAAAAAAAAAAABYaW5nAAAADwAAAAAAAAADAACCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAv///////wAAAP5MYW1lMy45OFIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//MUxAAAAAAAbwAAAEAAAAbwAAAEAAYAAAAgAAAgAAAgAAAgAAAgAAAgAAAgAAAgAAAgAAAgAAAgAAAg//MUxAsAAAAAbwAAAEAAAAbwAAAEAAYAAAAgAAAgAAAgAAAgAAAgAAAgAAAgAAAgAAAgAAAgAAAgAAAg//MUxBsAAAAAbwAAAEAAAAbwAAAEAAYAAAAgAAAgAAAgAAAgAAAgAAAgAAAgAAAgAAAgAAAgAAAgAAAg';
                audioKeepAliveEl.loop = true;
                audioKeepAliveEl.volume = 0.01;

                const playPromise = audioKeepAliveEl.play();
                if (playPromise !== undefined) {
                    playPromise.catch(() => {
                        const resumeHandler = () => {
                            if (audioKeepAliveEl) {
                                audioKeepAliveEl.play().catch(() => {});
                            }
                            window.removeEventListener('click', resumeHandler);
                            window.removeEventListener('touchstart', resumeHandler);
                        };
                        window.addEventListener('click', resumeHandler);
                        window.addEventListener('touchstart', resumeHandler);
                    });
                }
            } catch (e) {
                console.error(e);
            }
        }

        function stopAudioKeepAlive() {
            if (audioKeepAliveEl) {
                try {
                    audioKeepAliveEl.pause();
                    audioKeepAliveEl.src = '';
                } catch (e) {}
                audioKeepAliveEl = null;
            }
        }

        function updateAudioKeepAliveState() {
            if (cfg.audioKeepAliveEnabled) {
                startAudioKeepAlive();
            } else {
                stopAudioKeepAlive();
            }
        }

        async function makeApiRequest(method, url, token, body = null) {
            const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent) || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1);
            if (isIOS || typeof GM_xmlhttpRequest === 'undefined') {
                const options = {
                    method: method,
                    headers: {
                        "Authorization": token,
                        "Accept": "application/json"
                    }
                };
                if (body) {
                    options.headers["Content-Type"] = "application/json";
                    options.body = JSON.stringify(body);
                }
                options.credentials = 'include';
                try {
                    const r = await fetch(url, options);
                    if (r.ok) return await r.json();
                } catch (e) {
                    console.error(e);
                }
                return null;
            } else {
                return new Promise(resolve => {
                    const options = {
                        method: method,
                        url: url,
                        headers: {
                            "Authorization": token,
                            "User-Agent": navigator.userAgent,
                            "Accept": "application/json"
                        },
                        onload: r => {
                            if (r.status === 200) {
                                try { resolve(JSON.parse(r.responseText)); } catch { resolve(null); }
                            } else {
                                resolve(null);
                            }
                        },
                        onerror: () => resolve(null),
                        ontimeout: () => resolve(null),
                        timeout: 10000
                    };
                    if (body) {
                        options.headers["Content-Type"] = "application/json";
                        options.data = JSON.stringify(body);
                    }
                    GM_xmlhttpRequest(options);
                });
            }
        }

        async function performRaid() {
            if (!cfg.autoRaidEnabled) return;
            if (!location.pathname.startsWith('/murim-cards')) return;
            const token = getStoredToken();
            if (!token) return;

            try {
                const profileUrl = `${API_DOMAIN}/api/v2/events/card-battle/profile/`;
                const profileData = await makeApiRequest("GET", profileUrl, token);
                if (!profileData || typeof profileData !== 'object') return;

                const currentEnergy = parseInt(profileData.energy_current);
                if (isNaN(currentEnergy)) return;

                const locationId = parseInt(cfg.autoRaidLocation) || 10;
                const costMap = { 1: 4, 2: 5, 3: 6, 4: 7, 5: 8, 6: 9, 7: 10, 8: 11, 9: 12, 10: 13 };
                const requiredEnergy = costMap[locationId] || 13;

                if (currentEnergy >= requiredEnergy) {
                    const raidUrl = `${API_DOMAIN}/api/v2/events/card-battle/locations/${locationId}/raid/`;
                    await makeApiRequest("POST", raidUrl, token, {});
                }
            } catch (e) {
                console.error(e);
            }
        }

        async function performPvp() {
            if (!cfg.autoPvpEnabled) return;
            if (!location.pathname.startsWith('/murim-cards')) return;
            const token = getStoredToken();
            if (!token) return;

            try {
                const pvpUrl = `${API_DOMAIN}/api/v2/events/card-battle/pvp/match/`;
                await makeApiRequest("POST", pvpUrl, token, {});
            } catch (e) {
                console.error(e);
            }
        }

        function restartTimers() {
            if (raidTimer) { clearInterval(raidTimer); raidTimer = null; }
            if (pvpTimer) { clearInterval(pvpTimer); pvpTimer = null; }

            if (cfg.autoRaidEnabled) {
                performRaid();
                raidTimer = setInterval(performRaid, 5 * 60 * 1000);
            }

            if (cfg.autoPvpEnabled) {
                performPvp();
                pvpTimer = setInterval(performPvp, 31 * 1000);
            }
        }

        restartTimers();
        updateAudioKeepAliveState();

        window.addEventListener('rem_settings_changed', (e) => {
            if (e.detail && (e.detail.key === 'autoRaidEnabled' || e.detail.key === 'autoRaidLocation' || e.detail.key === 'autoPvpEnabled')) {
                restartTimers();
            }
            if (e.detail && e.detail.key === 'audioKeepAliveEnabled') {
                updateAudioKeepAliveState();
            }
        });
    }

    function injectTitleProgress() {
        if (!cfg.bookmarkProgress) {
            document.querySelectorAll('.rem-progress-tag, .rem-bookmark-rating-badge').forEach(el => el.remove());
            return;
        }
        const m = location.pathname.match(/\/user\/(\d+)/);
        if (!m) return;
        const profileUid = m[1];

        const pParams = new URLSearchParams(location.search);
        const norm = (s) => String(s || '').toLowerCase().trim();

        const bookmarkOptions = Array.from(document.querySelectorAll('option, [role="tab"], button, .cs-tabs-item'))
            .filter(el => {
                const id = el.getAttribute('id') || '';
                const val = el.getAttribute('value') || '';
                return /-\d+$/.test(id) || /^\d+$/.test(val);
            });

        function fetchBookmarkPage(uid, typeId, page) {
            const key = `${uid}_${typeId}_${page}`;
            if (fetchedKeys.has(key)) return;
            fetchedKeys.add(key);
            Manager.req(`${API_DOMAIN}/api/v2/users/${uid}/bookmarks/?page=${page}&type=${typeId}`).then(data => {
                if (data && Array.isArray(data.results)) {
                    data.results.forEach(item => {
                        if (item && item.title) {
                            if (item.title.dir) {
                                interceptedBookmarks.set(norm(item.title.dir), item);
                            }
                            if (item.title.id) {
                                interceptedBookmarks.set(norm(item.title.id), item);
                            }
                        }
                    });
                    if (data.next) {
                        fetchBookmarkPage(uid, typeId, page + 1);
                    }
                }
            }).catch(() => { });
        }

        bookmarkOptions.forEach(el => {
            const id = el.getAttribute('id') || '';
            const val = el.getAttribute('value') || '';
            let typeId = null;
            if (/-\d+$/.test(id)) {
                typeId = id.match(/-(\d+)$/)[1];
            } else if (/^\d+$/.test(val)) {
                typeId = val;
            }
            if (typeId) {
                fetchBookmarkPage(profileUid, typeId, 1);
            }
        });

        const activeBookmarkTab = bookmarkOptions.find(el => {
            const isTab = el.getAttribute('data-state') === 'active';
            const isSelectedOption = el.tagName === 'OPTION' && el.selected;
            return isTab || isSelectedOption;
        });
        if (activeBookmarkTab) {
            const id = activeBookmarkTab.getAttribute('id') || '';
            const val = activeBookmarkTab.getAttribute('value') || '';
            let activeTypeId = null;
            if (/-\d+$/.test(id)) {
                activeTypeId = id.match(/-(\d+)$/)[1];
            } else if (/^\d+$/.test(val)) {
                activeTypeId = val;
            }
            if (activeTypeId) {
                const page = pParams.get('page') || '1';
                if (page !== '1') {
                    fetchBookmarkPage(profileUid, activeTypeId, parseInt(page));
                }
            }
        }

        const cards = document.querySelectorAll('a.cs-title-card, a[href*="/manga/"]');
        cards.forEach(card => {
            const href = card.getAttribute('href') || '';
            const pathParts = href.split('/manga/')[1];
            if (!pathParts) return;
            const cleanPart = pathParts.split(/[?#]/)[0];
            const segments = cleanPart.split('/');
            const fullSlug = segments[0];

            let item = interceptedBookmarks.get(norm(fullSlug));
            if (!item) {
                const idMatch = fullSlug.match(/^(\d+)-(.*)$/);
                if (idMatch) {
                    item = interceptedBookmarks.get(norm(idMatch[1])) || interceptedBookmarks.get(norm(idMatch[2]));
                }
            }
            if (!item) return;

            if (card.dataset.remProgressInjected && card.dataset.remProgressInjected !== fullSlug) {
                const oldBadge = card.querySelector('.rem-progress-tag');
                if (oldBadge) oldBadge.remove();
                const oldRating = card.querySelector('.rem-bookmark-rating-badge');
                if (oldRating) oldRating.remove();
            }

            card.dataset.remProgressInjected = fullSlug;

            const total = item.read_progress_total || (item.title && item.title.count_chapters) || 0;
            const read = item.read_progress !== undefined ? item.read_progress : 0;

            let subtitle = card.querySelector('[data-sentry-component="TitleCardSubtitle"]') ||
                card.querySelector('.cs-text.leading-md.font-normal.line-clamp-1');
            if (!subtitle) {
                const pElements = card.querySelectorAll('div > div p');
                if (pElements.length > 0) {
                    subtitle = pElements[0];
                }
            }

            if (subtitle) {
                subtitle.style.display = 'flex';
                subtitle.style.justifyContent = 'space-between';
                subtitle.style.alignItems = 'center';
                subtitle.style.width = '100%';

                let badge = subtitle.querySelector('.rem-progress-tag');
                if (!badge) {
                    badge = document.createElement('span');
                    badge.className = 'rem-progress-tag cs-text leading-md font-normal text-muted-foreground text-[12px] flex items-center';
                    subtitle.appendChild(badge);
                }
                badge.style.cssText = 'font-weight: normal; font-size: 12px; margin-left: auto; select-none: none; display: inline-flex; align-items: center;';

                const lag = total - read;
                const dotClass = lag <= 3 ? 'bg-primary' : 'bg-destructive';
                const dotColor = lag <= 3 ? '#3b82f6' : '#ef4444';
                badge.innerHTML = `<div class="mr-2 inline-block h-2 w-2 rounded-full ${dotClass}" style="background-color: ${dotColor};"></div>${read}/${total}`;
            }

            const oldRating = card.querySelector('.rem-bookmark-rating-badge');
            if (oldRating) oldRating.remove();

            const avgRating = item.title && (item.title.avg_rating || item.title.rating) || '0.0';
            const userRating = item.rated;

            let ratingBadge = card.querySelector('[data-sentry-component="TitleCardRating"]') || card.querySelector('p.absolute.right-1.bottom-1');
            if (ratingBadge) {
                const goldStarSvg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" stroke-width="1.5" style="width: 12px !important; height: 12px !important; display: inline-block; select-none: none; margin-right: 2px; color: #fbbf24 !important;"><path fill="currentColor" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="m10.937 1.593 1.99 4.22c.15.31.43.53.77.58l4.46.68c.83.13 1.17 1.2.56 1.81l-3.23 3.29c-.24.24-.35.6-.29.94l.76 4.64c.14.87-.73 1.53-1.48 1.12l-3.99-2.19a1 1 0 0 0-.95 0l-3.99 2.19c-.75.41-1.62-.25-1.48-1.12l.76-4.64c.06-.34-.05-.7-.29-.94l-3.23-3.29c-.6-.61-.27-1.69.56-1.81l4.46-.68c.33-.05.62-.27.77-.58l1.99-4.22a.995.995 0 0 1 1.82 0h.03Z"></path></svg>`;
                const whiteStarSvg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" stroke-width="1.5" style="width: 12px !important; height: 12px !important; display: inline-block; select-none: none; margin-right: 2px; color: rgba(255, 255, 255, 0.9) !important;"><path fill="currentColor" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="m10.937 1.593 1.99 4.22c.15.31.43.53.77.58l4.46.68c.83.13 1.17 1.2.56 1.81l-3.23 3.29c-.24.24-.35.6-.29.94l.76 4.64c.14.87-.73 1.53-1.48 1.12l-3.99-2.19a1 1 0 0 0-.95 0l-3.99 2.19c-.75.41-1.62-.25-1.48-1.12l.76-4.64c.06-.34-.05-.7-.29-.94l-3.23-3.29c-.6-.61-.27-1.69.56-1.81l4.46-.68c.33-.05.62-.27.77-.58l1.99-4.22a.995.995 0 0 1 1.82 0h.03Z"></path></svg>`;
                if (userRating) {
                    ratingBadge.style.cssText = 'display: flex; flex-direction: column; align-items: flex-end; gap: 2px; padding: 4px 8px; justify-content: center;';
                    ratingBadge.innerHTML = `
                        <div class="flex items-center gap-1 font-semibold" style="color: rgba(255, 255, 255, 0.9) !important; font-size: 11px !important; line-height: 1 !important;">${goldStarSvg}${parseFloat(userRating).toFixed(1)}</div>
                        <div class="flex items-center gap-1 font-semibold" style="color: rgba(255, 255, 255, 0.9) !important; font-size: 11px !important; line-height: 1 !important;">${whiteStarSvg}${parseFloat(avgRating).toFixed(1)}</div>
                    `;
                } else {
                    ratingBadge.style.cssText = '';
                    ratingBadge.innerHTML = `${whiteStarSvg}${parseFloat(avgRating).toFixed(1)}`;
                }
            }
        });
    }

    const titleCardsCache = new Map();
    const titleCardsFetching = new Set();

    function getCardRankName(card) {
        if (!card) return null;
        const c = card.card || card;
        let r = c.rank;
        if (typeof r === 'object' && r !== null) {
            r = r.id || r.name || '';
        }
        r = String(r || '').toLowerCase();
        if (r.startsWith('rank_')) return r.replace('rank_', '').toUpperCase();
        if (['f', 'e', 'd', 'c', 'b', 'a', 's', 're', 'ev'].includes(r)) return r.toUpperCase();
        return null;
    }

    function scanAlbumCards() {
        const match = location.pathname.match(/\/manga\/([^\/]+)\/cards/);
        if (!match) return;
        const titleSlug = match[1];

        const progressbar = document.querySelector('[data-sentry-component="HeroCardsAlbum"] [role="progressbar"], [data-sentry-component="HeroCardsAlbumPaper"] [role="progressbar"]');
        if (!progressbar) return;

        let infoEl = progressbar.parentNode.querySelector('.rem-cards-rank-progress');
        if (!infoEl) {
            infoEl = document.createElement('div');
            infoEl.className = 'rem-cards-rank-progress';
            infoEl.style.cssText = 'display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; font-size: 11px; font-weight: 600; color: #a1a1aa;';
            progressbar.parentNode.appendChild(infoEl);
        }

        if (titleCardsFetching.has(titleSlug)) return;

        if (!titleCardsCache.has(titleSlug)) {
            infoEl.textContent = 'Загрузка коллекции...';
            titleCardsFetching.add(titleSlug);

            Manager.req(`${API_DOMAIN}/api/titles/${titleSlug}/`).then(titleData => {
                if (!titleData || !titleData.content || !titleData.content.id) {
                    titleCardsFetching.delete(titleSlug);
                    titleCardsCache.set(titleSlug, 'error');
                    infoEl.textContent = 'Ошибка загрузки тайтла';
                    return;
                }
                const titleId = titleData.content.id;
                let page = 1;
                let allCards = [];

                function loadPage() {
                    if (!location.pathname.includes(titleSlug)) {
                        titleCardsFetching.delete(titleSlug);
                        return;
                    }
                    const url = `${API_DOMAIN}/api/inventory/${titleId}/cards/?count=50&ordering=rank&page=${page}`;
                    Manager.req(url).then(data => {
                        if (data) {
                            const list = data.results || data.content || [];
                            if (Array.isArray(list)) {
                                allCards = allCards.concat(list);
                            }
                            if (data.next) {
                                page++;
                                loadPage();
                            } else {
                                titleCardsCache.set(titleSlug, allCards);
                                titleCardsFetching.delete(titleSlug);
                                scanAlbumCards();
                            }
                        } else {
                            titleCardsFetching.delete(titleSlug);
                            titleCardsCache.set(titleSlug, 'error');
                            infoEl.textContent = 'Ошибка загрузки карт';
                        }
                    }).catch(() => {
                        titleCardsFetching.delete(titleSlug);
                        titleCardsCache.set(titleSlug, 'error');
                        infoEl.textContent = 'Ошибка загрузки карт';
                    });
                }
                loadPage();
            }).catch(() => {
                titleCardsFetching.delete(titleSlug);
                titleCardsCache.set(titleSlug, 'error');
                infoEl.textContent = 'Ошибка загрузки тайтла';
            });
            return;
        }

        const cached = titleCardsCache.get(titleSlug);
        if (cached === 'error') return;
        const cards = cached || [];
        const rankCounts = {
            'F': { owned: 0, total: 0, color: '#9ca3af' },
            'E': { owned: 0, total: 0, color: '#9ca3af' },
            'D': { owned: 0, total: 0, color: '#ffffff' },
            'C': { owned: 0, total: 0, color: '#4ade80' },
            'B': { owned: 0, total: 0, color: '#60a5fa' },
            'A': { owned: 0, total: 0, color: '#c084fc' },
            'S': { owned: 0, total: 0, color: '#fbbf24' },
            'RE': { owned: 0, total: 0, color: '#f59e0b' },
            'EV': { owned: 0, total: 0, color: '#e879f9' }
        };
        const rankNames = Object.keys(rankCounts);

        cards.forEach(cardItem => {
            if (!cardItem) return;
            const rankName = getCardRankName(cardItem);
            if (!rankName || !rankCounts[rankName]) return;

            const c = cardItem.card || cardItem;
            let filename = null;
            if (typeof c.cover === 'string') {
                filename = Manager.getFilename(c.cover);
            } else if (c.cover) {
                filename = Manager.getFilename(c.cover.high) || Manager.getFilename(c.cover.mid);
            }
            if (!filename) return;

            rankCounts[rankName].total++;
            if (Manager.has(filename)) {
                rankCounts[rankName].owned++;
            }
        });

        let html = '';
        rankNames.forEach(rank => {
            const data = rankCounts[rank];
            if (data.total > 0) {
                html += `<div style="display: inline-flex; align-items: center; gap: 5px; background: rgba(255,255,255,0.03); border: 1px solid rgba(255,255,255,0.08); padding: 3px 8px; border-radius: 6px;">
                    <span style="color: ${data.color}; font-weight: 800;">${rank}</span>
                    <span style="color: #fff; font-weight: 700;">${data.owned}</span>
                    <span style="opacity: 0.4;">/</span>
                    <span style="opacity: 0.4;">${data.total}</span>
                </div>`;
            }
        });
        infoEl.innerHTML = html;
    }

    const CHAR_EXTENDED_SLOTS = {
        1: 5, 2: 4, 80: 5, 98: 5, 189: 4, 192: 4, 196: 4, 218: 4, 227: 5, 228: 5, 229: 4, 234: 4, 236: 5,
        385: 4, 400: 5, 428: 4, 434: 5, 437: 5, 457: 4, 493: 5, 496: 4, 507: 5, 555: 4, 605: 5, 913: 6,
        1335: 5, 1363: 5, 1436: 4, 1482: 5, 1570: 4, 1571: 5, 1580: 5, 1815: 5, 1984: 4, 2217: 5,
        2238: 5, 2239: 4, 2244: 4, 2447: 5, 2519: 4, 2520: 5, 2589: 4, 2680: 5, 2692: 4, 2774: 4,
        2863: 5, 2899: 6, 3101: 4, 3126: 4, 3384: 4, 3385: 4, 3518: 4, 3547: 5, 3563: 4, 3667: 4,
        7437: 5, 10012: 5, 10253: 4, 10544: 5, 10911: 4, 11702: 4, 12289: 5, 12423: 5, 12679: 5,
        14032: 4, 14210: 4, 15318: 4, 15355: 5, 15358: 5, 15362: 5, 17308: 5, 17309: 5, 17614: 5,
        17680: 5, 18451: 5, 20842: 4, 21085: 4, 21394: 4, 21475: 4, 21715: 4, 22543: 5, 27419: 5,
        27423: 5, 27542: 5, 29027: 5, 30659: 5, 35157: 4
    };

    const DEFAULT_SLOTS = 3;
    const CARDS_PER_SLOT = 5;
    const SLOT_RANKS = ['rank_f', 'rank_e', 'rank_d', 'rank_c', 'rank_b'];

    function getCharacterMaxCards(charId) {
        const id = parseInt(charId);
        const slots = CHAR_EXTENDED_SLOTS[id] || DEFAULT_SLOTS;
        return slots * CARDS_PER_SLOT;
    }

    const charSlotsCache = new Map();
    const charSlotsPending = new Set();

    async function fetchCharacterCardCount(charId) {
        const id = String(charId);
        if (charSlotsCache.has(id)) return charSlotsCache.get(id);
        if (charSlotsPending.has(id)) return null;
        charSlotsPending.add(id);
        try {
            let total = 0;
            let page = 1;
            let hasNext = true;
            while (hasNext) {
                const data = await Manager.req(`${API_DOMAIN}/api/inventory/catalog/?character=${id}&count=100&ordering=rank&page=${page}`);
                if (!data) break;
                const list = data.results || data.content || [];
                if (!list.length) break;
                list.forEach(item => {
                    const c = item.card || item;
                    let r = c.rank;
                    if (typeof r === 'object' && r !== null) r = r.id || r.name || '';
                    r = String(r || '').toLowerCase();
                    if (SLOT_RANKS.includes(r)) {
                        total++;
                    }
                });
                if (!data.next) hasNext = false;
                else { page++; await new Promise(r => setTimeout(r, 100)); }
            }
            charSlotsCache.set(id, total);
            return total;
        } catch (e) {
            charSlotsCache.set(id, 0);
            return 0;
        } finally {
            charSlotsPending.delete(id);
        }
    }

    function renderSlotBadge(link, count, maxCards, slots) {
        if (link.querySelector('.rem-char-slot-badge')) return;
        const badge = document.createElement('div');
        const remaining = maxCards - count;
        if (remaining <= 0) {
            badge.className = 'rem-char-slot-badge slots-full';
            badge.textContent = `${count}/${maxCards}`;
            badge.title = `Все слоты заняты (${slots} сл.)`;
        } else if (count === 0) {
            badge.className = 'rem-char-slot-badge slots-empty';
            badge.textContent = `${count}/${maxCards}`;
            badge.title = `Доступно ещё ${remaining} карт (${slots} сл.)`;
        } else {
            badge.className = 'rem-char-slot-badge slots-available';
            badge.textContent = `${count}/${maxCards}`;
            badge.title = `Доступно ещё ${remaining} карт (${slots} сл.)`;
        }
        link.appendChild(badge);
    }

    function scanCharacterSlots() {
        if (!cfg.characterSlots) return;

        const charLinks = document.querySelectorAll('a[href*="/character/"]:not([data-rem-slot-checked])');
        charLinks.forEach(link => {
            const href = link.getAttribute('href') || '';
            const m = href.match(/\/character\/(\d+)/);
            if (!m) return;

            if (link.closest('header, nav, #rem-settings-panel, #rem-exchange-panel, #rem-cardbattle-panel, .rem-box, .rem-info, .rem-sub')) return;

            const imgEl = link.querySelector('img');
            if (!imgEl) return;

            link.setAttribute('data-rem-slot-checked', '1');

            if (link.querySelector('.rem-char-slot-badge') || link.querySelector('.rem-char-slot-btn')) return;

            const charId = m[1];
            const maxCards = getCharacterMaxCards(charId);
            const slots = CHAR_EXTENDED_SLOTS[parseInt(charId)] || DEFAULT_SLOTS;

            if (charSlotsCache.has(charId)) {
                const count = charSlotsCache.get(charId);
                renderSlotBadge(link, count, maxCards, slots);
            } else {
                const btn = document.createElement('button');
                btn.className = 'rem-char-slot-btn';
                btn.title = 'Загрузить слоты персонажа';
                btn.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" style="width: 15px; height: 15px;"><path d="M12 15a3 3 0 100-6 3 3 0 000 6z"/><path fill-rule="evenodd" d="M1.323 11.447C2.811 6.976 7.028 3.75 12.001 3.75c4.97 0 9.185 3.223 10.675 7.69.12.362.12.752 0 1.113-1.487 4.471-5.705 7.697-10.677 7.697-4.97 0-9.186-3.223-10.675-7.69a1.762 1.762 0 010-1.113zM12 18.75a6.75 6.75 0 100-13.5 6.75 6.75 0 000 13.5z" clip-rule="evenodd"/></svg>`;
                link.appendChild(btn);
            }
        });
    }

    document.addEventListener('click', (e) => {
        const btn = e.target.closest('.rem-char-slot-btn');
        if (btn) {
            e.preventDefault();
            e.stopPropagation();

            if (btn.disabled) return;
            btn.disabled = true;
            btn.innerHTML = '...';
            btn.style.cursor = 'default';

            const link = btn.closest('a[href*="/character/"]');
            if (!link) return;
            const href = link.getAttribute('href') || '';
            const m = href.match(/\/character\/(\d+)/);
            if (!m) return;
            const charId = m[1];
            const maxCards = getCharacterMaxCards(charId);
            const slots = CHAR_EXTENDED_SLOTS[parseInt(charId)] || DEFAULT_SLOTS;

            fetchCharacterCardCount(charId).then(count => {
                if (count === null) {
                    btn.disabled = false;
                    btn.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" style="width: 15px; height: 15px;"><path d="M12 15a3 3 0 100-6 3 3 0 000 6z"/><path fill-rule="evenodd" d="M1.323 11.447C2.811 6.976 7.028 3.75 12.001 3.75c4.97 0 9.185 3.223 10.675 7.69.12.362.12.752 0 1.113-1.487 4.471-5.705 7.697-10.677 7.697-4.97 0-9.186-3.223-10.675-7.69a1.762 1.762 0 010-1.113zM12 18.75a6.75 6.75 0 100-13.5 6.75 6.75 0 000 13.5z" clip-rule="evenodd"/></svg>`;
                    btn.style.cursor = 'pointer';
                    return;
                }
                btn.remove();
                renderSlotBadge(link, count, maxCards, slots);
            });
        }
    }, true);

    ['mousedown', 'mouseup'].forEach(eventName => {
        document.addEventListener(eventName, (e) => {
            if (e.target.closest('.rem-char-slot-btn')) {
                e.preventDefault();
                e.stopPropagation();
            }
        }, true);
    });

    setInterval(() => {
        checkNavigation();
        scanVisual();
        if (typeof injectTitleProgress === 'function') injectTitleProgress();
        if (/\/manga\/[^\/]+\/cards/.test(location.pathname)) scanAlbumCards();
        scanForDialog();
        injectBattlepassExecuteButtons();
        injectMemoryGameButton();
        injectPuzzleGameButton();
        injectDifferenceGameMod();
        scanQuiz();
        scanDeckCards();
        scanDeckInnerPage();
        if (cfg.onlineStatus) checkOnline();
        if (location.pathname.startsWith('/user/')) { injectStats(); injectScanButton(); }
        if (location.pathname.startsWith('/card/')) scanCardPageNewBadges();
        if (cfg.characterSlots) scanCharacterSlots();

        const currentPath = location.pathname;
        if (currentPath !== lastUrlPath) {
            lastUrlPath = currentPath;
            if (currentPath.startsWith('/murim-cards')) {
                window.dispatchEvent(new CustomEvent('rem_settings_changed', { detail: { key: 'autoRaidEnabled' } }));
            }
        }

        checkSelectionPageNav();
        if (cfg.cardSelection) {
            updateSelectionPanel();
        } else {
            clearSelection();
        }
        scanAndAnalyzeTrades();
        const isChaptersPage = /\/manga\/[^\/]+\/chapters/.test(location.pathname);
        const unreadBtn = document.getElementById('rem-eye-unread-btn');
        if (isChaptersPage) {
            const uid = storageGet('rem_user_id_v1') || Manager.userId;
            if (uid) {
                verifyUserPerms();
                if (isUserPermOk) {
                    if (!unreadBtn) {
                        initChaptersUnreadButton();
                    }
                    const btn = document.getElementById('rem-eye-unread-btn');
                    if (btn && !btn.classList.contains('visible')) {
                        btn.classList.add('visible');
                    }
                } else {
                    if (unreadBtn && unreadBtn.classList.contains('visible')) {
                        unreadBtn.classList.remove('visible');
                    }
                }
            } else {
                if (unreadBtn && unreadBtn.classList.contains('visible')) {
                    unreadBtn.classList.remove('visible');
                }
            }
        } else {
            if (unreadBtn && unreadBtn.classList.contains('visible')) {
                unreadBtn.classList.remove('visible');
            }
        }
    }, 400);

    const RANK_BASE_POINTS = {
        'F': 1.0,
        'E': 3.0,
        'D': 8.0,
        'C': 20.0,
        'B': 50.0,
        'A': 150.0,
        'S': 400.0,
        'RE': 1000.0,
        'EV': 0.1
    };

    const ownersPoints = [
        [1, 4.0],
        [3, 3.5],
        [5, 3.0],
        [10, 2.5],
        [20, 2.0],
        [30, 1.5],
        [50, 1.0],
        [200, 0.5],
        [500, 0.01]
    ];

    const wishesPoints = [
        [1, 1.0],
        [2, 1.1],
        [10, 1.3],
        [20, 1.5],
        [50, 1.6],
        [100, 1.7],
        [300, 1.8],
        [500, 2.0]
    ];

    const exchangesPoints = [
        [1, 1.0],
        [2, 0.9],
        [5, 0.75],
        [10, 0.6],
        [20, 0.5],
        [50, 0.3]
    ];

    function interpolate(val, points) {
        if (val <= points[0][0]) return points[0][1];
        if (val >= points[points.length - 1][0]) return points[points.length - 1][1];
        for (let i = 0; i < points.length - 1; i++) {
            const p1 = points[i];
            const p2 = points[i + 1];
            if (val >= p1[0] && val <= p2[0]) {
                const t = (val - p1[0]) / (p2[0] - p1[0]);
                return p1[1] + t * (p2[1] - p1[1]);
            }
        }
        return points[points.length - 1][1];
    }

    function getTitleClassMultiplier(titleName) {
        if (!titleName) return 1.0;
        const name = titleName.toLowerCase().trim();
        if (name.includes("azur lane: start building") ||
            name.includes("dark souls — рыцарь-раб гаэль") ||
            name === "doom" ||
            name.includes("helltaker 4-koma") ||
            name.includes("love and deepspace") ||
            name.includes("nier automata: высадка команды yorha") ||
            name.includes("path to nowhere") ||
            name.includes("re:zero") ||
            name.includes("the elder scrolls: герои хромой телеги") ||
            name.includes("благословение небожителей") ||
            name.includes("бойцовский класс 3") ||
            name.includes("вайолет эвергарден") ||
            name.includes("восхождение в тени") ||
            name.includes("волчица и пряность") ||
            name.includes("выбери меня") ||
            name.includes("выживая в игре за варвара") ||
            name.includes("если не дебютируешь") ||
            name.includes("мертвы к рассвету") ||
            name.includes("кольцо элден") ||
            name.includes("мастер гу") ||
            name.includes("нет игры - нет жизни") ||
            name.includes("охотник sss-уровня") ||
            (name.includes("повелитель") && !name.includes("повелитель тайн")) ||
            name.includes("повелитель тайн") ||
            name.includes("подарок миты") ||
            name.includes("призрачный клинок") ||
            name.includes("путь удивительного небожителя") ||
            name.includes("реинкарнация безработного") ||
            name.includes("рыцарь, проживающий один и тот же день") ||
            name.includes("созданный в бездне") ||
            name.includes("старшая школа дхд") ||
            name.includes("страна самоцветов") ||
            name.includes("темнейшее подземелье") ||
            name.includes("теневой раб") ||
            name.includes("тетрадь смерти") ||
            name.includes("легенда о путнике бездны") ||
            name.includes("убийца акаме") ||
            name.includes("убийца гоблинов") ||
            name.includes("фрирен") ||
            name.includes("человек - бензопила") ||
            name.includes("черный клевер")
        ) {
            return 2.0;
        }
        if (name.includes("goddess of victory: nikke") ||
            name.includes("identity v") ||
            name.includes("k-pop demon hunters") ||
            name.includes("reverse:1999") ||
            name.includes("адский рай") ||
            name.includes("башня бога") ||
            name.includes("безумный азарт") ||
            name === "блич" ||
            name === "бродяга" ||
            name.includes("ван пис") ||
            name.includes("ванпанчмен") ||
            name.includes("великий из бродячих псов") ||
            name.includes("артбук \"берсерк\"") || name.includes("артбук берсерк") ||
            name.includes("властелин колец") ||
            name.includes("восхождение героя щита") ||
            name.includes("всеведущий читатель") ||
            name.includes("грозовые волны") ||
            name.includes("призраках, всё равно придётся идти на работу") ||
            name.includes("девушки на линии фронта") ||
            name.includes("дни сакамото") ||
            name.includes("дорохедоро") ||
            name.includes("класс превосходства") ||
            name.includes("евангелион") ||
            name === "ззз" || name === "zzz" ||
            name.includes("звездное дитя") ||
            name.includes("коносуба") ||
            name.includes("клинок рассекающий демона") ||
            (name.includes("левиафан") && name.includes("2021")) ||
            name.includes("любимый во франксе") ||
            name.includes("магическая битва") ||
            name.includes("милфхантер из другого мира") ||
            name.includes("встречу тебя в подземелье") ||
            name.includes("монолог фармацевта") ||
            name.includes("моя судьба злодея") ||
            name.includes("серый ворон сироты доминика") ||
            name === "наномашин" || name === "наномашина" ||
            name.includes("начало после конца") ||
            name.includes("о моём перерождении в слизь") ||
            name.includes("повесть о конце света") ||
            name.includes("легенды врат балдуина") ||
            name.includes("леди мария") ||
            name.includes("свидание с духом") ||
            name.includes("ханбэй бессмертный") ||
            name.includes("синяя тюрьма") ||
            name.includes("стальной алхимик") ||
            name.includes("судьба") || name.includes("fate/") ||
            name.includes("уличный боец") ||
            name.includes("хантер х хантер") ||
            name.includes("хуашань") ||
            name.includes("элисед") ||
            name.includes("эта фарфоровая кукла влюбилась")
        ) {
            return 1.5;
        }
        if (name.includes("восемьдесят шесть") || name.includes("86") ||
            name.includes("arknights") ||
            name.includes("легенда об аанге") ||
            name.includes("атака титанов") ||
            name.includes("баскетбол куроко") ||
            name.includes("безграничный маг") ||
            name.includes("бездомный бог") ||
            name.includes("ветролом") ||
            name.includes("военная хроника маленькой девочки") ||
            name.includes("волейбол") ||
            name.includes("наши воины") ||
            name.includes("врата штейна") ||
            name.includes("гачиакута") ||
            name.includes("гинтама") ||
            name.includes("дандадан") ||
            name.includes("дворянство") ||
            name.includes("девушкипони") ||
            name.includes("доктор стоун") ||
            name.includes("кайдзю") ||
            name.includes("лелуш") ||
            name.includes("любовь никки") ||
            name.includes("лабиринт волшебства") ||
            name.includes("ре:айнкрад") ||
            name.includes("мемуары ванитаса") ||
            name.includes("гончей меча") ||
            name.includes("моя геройская академия") ||
            name.includes("бог хаоса") ||
            name.includes("наруто") ||
            name.includes("нуб максимального уровня") ||
            name.includes("отель хазбин") ||
            name.includes("песнь ночных бродяг") ||
            name.includes("пик боевых искусств") ||
            name.includes("подземелье вкусностей") ||
            name.includes("поднятие уровня") ||
            name.includes("покемоны") ||
            name.includes("последний серафим") ||
            name.includes("проект тохо") ||
            name.includes("выживанию в академии") ||
            name.includes("сейлор мун") ||
            name.includes("семь смертных грехов") ||
            name.includes("всемогущего дизайнера") ||
            name.includes("театральный клуб") ||
            name.includes("теккен") ||
            name.includes("темный дворецкий") ||
            name.includes("токийский гуль") ||
            name.includes("туалетный мальчик") ||
            name.includes("реборн") ||
            name.includes("фантазия гранблю") ||
            name.includes("фейри тейл") ||
            name.includes("хоримия") ||
            name.includes("шпионская семейка") ||
            name.includes("эльфийская песнь") ||
            name.includes("графским ублюдком")
        ) {
            return 1.25;
        }
        return 1.0;
    }

    const CardDetailsCache = {
        data: new Map(),
        load() {
            try {
                const raw = storageGet('rem_card_details_cache');
                if (raw) {
                    const parsed = JSON.parse(raw);
                    for (const [k, v] of Object.entries(parsed)) {
                        this.data.set(Number(k), v);
                    }
                }
            } catch (e) { }
        },
        save() {
            try {
                const obj = {};
                for (const [k, v] of this.data.entries()) {
                    obj[k] = v;
                }
                storageSet('rem_card_details_cache', JSON.stringify(obj));
            } catch (e) { }
        },
        get(cardId) {
            return this.data.get(Number(cardId));
        },
        set(cardId, info) {
            this.data.set(Number(cardId), info);
            this.save();
        }
    };
    CardDetailsCache.load();

    async function getOrFetchCardDetails(cardId) {
        if (!cardId) return null;
        let details = CardDetailsCache.get(cardId);
        if (details) return details;
        const data = await Manager.req(`api/inventory/cards/${cardId}/`);
        if (data) {
            CardDetailsCache.set(cardId, data);
            return data;
        }
        return null;
    }

    function findReactCardData(element) {
        if (!element) return null;
        let current = element;
        for (let depth = 0; depth < 8; depth++) {
            if (!current) break;
            const keys = Object.keys(current);
            const fiberKey = keys.find(k => k.startsWith('__reactFiber$') || k.startsWith('__reactProps$'));
            if (fiberKey) {
                let fiber = current[fiberKey];
                let limit = 15;
                while (fiber && limit > 0) {
                    const props = fiber.memoizedProps || fiber.pendingProps;
                    if (props) {
                        if (props.card && typeof props.card === 'object') {
                            return props.card;
                        }
                        if (props.item && typeof props.item === 'object' && props.item.card) {
                            return props.item.card;
                        }
                        if (props.cardItem && typeof props.cardItem === 'object') {
                            return props.cardItem;
                        }
                    }
                    fiber = fiber.return;
                    limit--;
                }
            }
            current = current.parentElement;
        }
        return null;
    }

    function getCardPoints(card, details, proposerId = null, isProposerInitiator = false) {
        if (!card) return 0;
        const rank = card.isVirtual ? card.virtualRank : getCardRankName(card);
        const base = RANK_BASE_POINTS[rank] || 1.0;
        const owners = details ? (details.count_owners ?? 0) : 0;
        const wishes = details ? (details.count_wishes ?? 0) : 0;
        const exchange = details ? (details.count_ready_to_exchange ?? 0) : 0;
        let ownersMult = 1.0;
        const authorId = card.author?.id || card.card?.author?.id || details?.author?.id || details?.card?.author?.id;
        if (owners <= 5 && authorId && proposerId && isProposerInitiator && String(authorId) === String(proposerId)) {
            ownersMult = 2.0;
        } else if (rank !== 'F' && rank !== 'E' && rank !== 'D') {
            ownersMult = interpolate(owners, ownersPoints);
        }
        const wishesMult = wishes <= 1 ? 1.0 : interpolate(wishes, wishesPoints);
        const exchangeMult = exchange <= 0 ? 1.0 : interpolate(exchange, exchangesPoints);
        let premMult = 1.0;
        let titleName = '';
        if (card.title && typeof card.title === 'object') {
            titleName = card.title.main_name || card.title.name || '';
        } else if (card.card && card.card.title && typeof card.card.title === 'object') {
            titleName = card.card.title.main_name || card.card.title.name || '';
        } else if (details && details.title && typeof details.title === 'object') {
            titleName = details.title.main_name || details.title.name || '';
        } else if (details && details.card && details.card.title && typeof details.card.title === 'object') {
            titleName = details.card.title.main_name || details.card.title.name || '';
        }
        if (titleName) {
            premMult = getTitleClassMultiplier(titleName);
        }
        return base * ownersMult * wishesMult * exchangeMult * premMult;
    }

    const userWishesCache = new Map();
    async function getOrFetchUserWishes(userId) {
        if (!userId) return new Set();
        const idStr = String(userId);
        if (userWishesCache.has(idStr)) {
            return userWishesCache.get(idStr);
        }
        function getCurrentUserId() {
            if (Manager.userId) return String(Manager.userId);
            const myIdFromStorage = storageGet('rem_user_id_v1') || storageGet('rem_user_id');
            if (myIdFromStorage) return String(myIdFromStorage);
            try {
                const nextData = window.__NEXT_DATA__;
                const uid = nextData?.props?.pageProps?.user?.id || nextData?.props?.pageProps?.session?.user?.id;
                if (uid) return String(uid);
            } catch (e) { }
            const userLink = document.querySelector('a[href^="/user/"]:not([href*="bookmarks"]):not([href*="exchanges"])');
            if (userLink) {
                const m = userLink.getAttribute('href').match(/user\/(\d+)/);
                if (m && m[1]) return String(m[1]);
            }
            return '';
        }
        if (idStr === String(getCurrentUserId())) {
            return Manager.wishCovers || new Set();
        }
        const wishes = new Set();
        try {
            let page = 1;
            let hasNext = true;
            while (hasNext && page <= 3) {
                const data = await Manager.req(`${API_DOMAIN}/api/v2/inventory/wishes/users/${idStr}/?wish_type=1&page=${page}`);
                if (data && data.content && data.content.length > 0) {
                    for (const item of data.content) {
                        if (item.card) {
                            const fname = item.card.cover ? (typeof item.card.cover === 'string' ? Manager.getFilename(item.card.cover) : (Manager.getFilename(item.card.cover.high) || Manager.getFilename(item.card.cover.mid))) : null;
                            if (fname) wishes.add(fname);
                        }
                    }
                    if (data.content.length < 30) {
                        hasNext = false;
                    } else {
                        page++;
                    }
                } else {
                    hasNext = false;
                }
            }
        } catch (e) { }
        userWishesCache.set(idStr, wishes);
        return wishes;
    }

    function getCardFilename(card, details) {
        if (card && card.cover) {
            if (typeof card.cover === 'string') return Manager.getFilename(card.cover);
            if (card.cover.high) return Manager.getFilename(card.cover.high);
            if (card.cover.mid) return Manager.getFilename(card.cover.mid);
        }
        if (details && details.cover) {
            if (typeof details.cover === 'string') return Manager.getFilename(details.cover);
            if (details.cover.high) return Manager.getFilename(details.cover.high);
            if (details.cover.mid) return Manager.getFilename(details.cover.mid);
        }
        return null;
    }

    async function checkIfUserOwnsCard(userId, cardId, fname) {
        if (!userId || !cardId) return false;
        const idStr = String(userId);

        function getCurrentUserId() {
            if (Manager.userId) return String(Manager.userId);
            const myIdFromStorage = storageGet('rem_user_id_v1') || storageGet('rem_user_id');
            if (myIdFromStorage) return String(myIdFromStorage);
            try {
                const nextData = window.__NEXT_DATA__;
                const uid = nextData?.props?.pageProps?.user?.id || nextData?.props?.pageProps?.session?.user?.id;
                if (uid) return String(uid);
            } catch (e) { }
            const userLink = document.querySelector('a[href^="/user/"]:not([href*="bookmarks"]):not([href*="exchanges"])');
            if (userLink) {
                const m = userLink.getAttribute('href').match(/user\/(\d+)/);
                if (m && m[1]) return String(m[1]);
            }
            return '';
        }

        if (idStr === String(getCurrentUserId())) {
            return fname ? Manager.has(fname) : false;
        }

        return false;
    }

    let isAnalyzingTrades = false;
    async function scanAndAnalyzeTrades() {
        if (isAnalyzingTrades) return;
        isAnalyzingTrades = true;
        try {
            function cancelCommonCards(cardsA, cardsB) {
                const countsA = {};
                cardsA.forEach(c => { countsA[String(c.id)] = (countsA[String(c.id)] || 0) + 1; });
                const countsB = {};
                cardsB.forEach(c => { countsB[String(c.id)] = (countsB[String(c.id)] || 0) + 1; });
                const finalA = [];
                const finalB = [];
                const allIds = new Set([...Object.keys(countsA), ...Object.keys(countsB)]);
                allIds.forEach(id => {
                    const cA = countsA[id] || 0;
                    const cB = countsB[id] || 0;
                    const diff = cA - cB;
                    if (diff > 0) {
                        const cardObj = cardsA.find(c => String(c.id) === String(id));
                        for (let i = 0; i < diff; i++) {
                            finalA.push(cardObj);
                        }
                    } else if (diff < 0) {
                        const cardObj = cardsB.find(c => String(c.id) === String(id));
                        for (let i = 0; i < -diff; i++) {
                            finalB.push(cardObj);
                        }
                    }
                });
                return { finalA, finalB };
            }

            function applyCraftCancellation(listA, listB, detailsMap) {
                const getTitleKey = (card) => {
                    const det = detailsMap.get(String(card.id));
                    let name = '';
                    if (card.title && typeof card.title === 'object') {
                        name = card.title.main_name || card.title.name || '';
                    } else if (card.card && card.card.title && typeof card.card.title === 'object') {
                        name = card.card.title.main_name || card.card.title.name || '';
                    } else if (det && det.title && typeof det.title === 'object') {
                        name = det.title.main_name || det.title.name || '';
                    } else if (det && det.card && det.card.title && typeof det.card.title === 'object') {
                        name = det.card.title.main_name || det.card.title.name || '';
                    }
                    const cleaned = name.toLowerCase().trim();
                    if (!cleaned) {
                        return `no_title_${card.id}`;
                    }
                    return cleaned;
                };

                const groupsA = {};
                const groupsB = {};
                listA.forEach(card => {
                    const key = getTitleKey(card);
                    if (!groupsA[key]) groupsA[key] = [];
                    groupsA[key].push(card);
                });
                listB.forEach(card => {
                    const key = getTitleKey(card);
                    if (!groupsB[key]) groupsB[key] = [];
                    groupsB[key].push(card);
                });

                const allKeys = new Set([...Object.keys(groupsA), ...Object.keys(groupsB)]);
                const craftValues = {
                    'F': 1, 'E': 2, 'D': 4, 'C': 8, 'B': 16, 'A': 32, 'S': 64, 'RE': 128
                };
                const rankOrder = ['RE', 'S', 'A', 'B', 'C', 'D', 'E', 'F'];

                const finalA = [];
                const finalB = [];

                allKeys.forEach(key => {
                    const titleListA = groupsA[key] || [];
                    const titleListB = groupsB[key] || [];

                    let valA = 0;
                    const evA = [];
                    titleListA.forEach(c => {
                        const r = getCardRankName(c);
                        if (r === 'EV') evA.push(c);
                        else valA += craftValues[r] || 0;
                    });

                    let valB = 0;
                    const evB = [];
                    titleListB.forEach(c => {
                        const r = getCardRankName(c);
                        if (r === 'EV') evB.push(c);
                        else valB += craftValues[r] || 0;
                    });

                    const diff = valA - valB;
                    const remA = diff > 0 ? diff : 0;
                    const remB = diff < 0 ? -diff : 0;

                    const decompose = (val, origList) => {
                        const res = [];
                        const template = origList[0] || titleListA[0] || titleListB[0];
                        if (!template) return res;
                        let remaining = val;
                        for (const r of rankOrder) {
                            const rVal = craftValues[r];
                            while (remaining >= rVal) {
                                const existing = origList.find(c => getCardRankName(c) === r);
                                if (existing) {
                                    res.push(existing);
                                } else {
                                    res.push({
                                        id: template.id,
                                        isVirtual: true,
                                        virtualRank: r,
                                        title: template.title,
                                        card: { rank: `rank_${r.toLowerCase()}` }
                                    });
                                }
                                remaining -= rVal;
                            }
                        }
                        return res;
                    };

                    finalA.push(...evA);
                    finalA.push(...decompose(remA, titleListA));

                    finalB.push(...evB);
                    finalB.push(...decompose(remB, titleListB));
                });

                return { finalA, finalB };
            }

            if (!cfg.tradeAnalysis) {
                const ratingBars = document.querySelectorAll('.rem-trade-rating-bar');
                if (ratingBars.length > 0) ratingBars.forEach(el => el.remove());
                const pointsLabels = document.querySelectorAll('.rem-trade-label-points');
                if (pointsLabels.length > 0) pointsLabels.forEach(el => el.remove());
                const createBar = document.getElementById('rem-create-trade-bar');
                if (createBar) createBar.style.display = 'none';
                document.querySelectorAll('[data-rem-scanned]').forEach(el => el.removeAttribute('data-rem-scanned'));
                return;
            }

            function getCurrentUserId() {
                if (Manager.userId) return String(Manager.userId);
                const myIdFromStorage = storageGet('rem_user_id_v1') || storageGet('rem_user_id');
                if (myIdFromStorage) return String(myIdFromStorage);
                try {
                    const nextData = window.__NEXT_DATA__;
                    const uid = nextData?.props?.pageProps?.user?.id || nextData?.props?.pageProps?.session?.user?.id;
                    if (uid) return String(uid);
                } catch (e) { }
                const userLink = document.querySelector('a[href^="/user/"]:not([href*="bookmarks"]):not([href*="exchanges"])');
                if (userLink) {
                    const m = userLink.getAttribute('href').match(/user\/(\d+)/);
                    if (m && m[1]) return String(m[1]);
                }
                return null;
            }

            function getCardsFromContainer(container, preferredKeys) {
                if (!container) return [];
                function getElementsShallow(el, depth = 0) {
                    if (!el || depth > 3) return [];
                    const res = [el];
                    for (const child of el.children) {
                        res.push(...getElementsShallow(child, depth + 1));
                    }
                    return res;
                }
                const elements = getElementsShallow(container);
                for (const el of elements) {
                    const keys = Object.keys(el);
                    const fiberKey = keys.find(k => k.startsWith('__reactFiber$') || k.startsWith('__reactProps$'));
                    if (!fiberKey) continue;
                    let fiber = el[fiberKey];
                    const props = fiber.memoizedProps || fiber.pendingProps;
                    const state = fiber.memoizedState;
                    const isCardList = (arr) => {
                        return Array.isArray(arr) && arr.length > 0 && arr.every(item => {
                            if (!item || typeof item !== 'object') return false;
                            return ('id' in item) && (('rank' in item) || (item.card && typeof item.card === 'object' && 'rank' in item.card));
                        });
                    };
                    const visited = new Set();
                    const findList = (obj, d = 0, parentKey = '') => {
                        if (d > 4) return null;
                        if (!obj || typeof obj !== 'object') return null;
                        if (visited.has(obj)) return null;
                        visited.add(obj);
                        if (isCardList(obj)) return obj;
                        if (Array.isArray(obj)) {
                            for (const item of obj) {
                                const res = findList(item, d + 1, parentKey);
                                if (res) return res;
                            }
                        } else {
                            for (const k in obj) {
                                if (Object.prototype.hasOwnProperty.call(obj, k)) {
                                    if (k.startsWith('_') || k === 'return' || k === 'child' || k === 'sibling' || k === 'stateNode') continue;
                                    const res = findList(obj[k], d + 1, k);
                                    if (res) return res;
                                }
                            }
                        }
                        return null;
                    };
                    if (props) {
                        for (const k in props) {
                            if (preferredKeys.some(pk => k.toLowerCase().includes(pk))) {
                                const list = findList(props[k], 0, k);
                                if (list) return list;
                            }
                        }
                    }
                    if (props) {
                        const list = findList(props, 0, '');
                        if (list) return list;
                    }
                    if (state) {
                        let currState = state;
                        while (currState) {
                            const list = findList(currState.memoizedState, 0, '');
                            if (list) return list;
                            currState = currState.next;
                        }
                    }
                }
                return [];
            }

            if (location.pathname.includes('/social/exchanges')) {
                const tradeBlocks = [];
                const snapshot = document.evaluate("//text()[contains(., 'Обмен #')]", document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
                for (let i = 0; i < snapshot.snapshotLength; i++) {
                    const textNode = snapshot.snapshotItem(i);
                    let current = textNode.parentElement;
                    while (current && current.tagName !== 'BODY') {
                        if (current.tagName === 'DIV') {
                            const txt = current.textContent || '';
                            if (txt.includes('Отправитель') && txt.includes('Получатель')) {
                                if (!tradeBlocks.includes(current)) {
                                    tradeBlocks.push(current);
                                }
                                break;
                            }
                        }
                        current = current.parentElement;
                    }
                }

                for (const block of tradeBlocks) {
                    if (block.getAttribute('data-rem-scanned') === 'true') continue;
                    const oldRatingBar = block.querySelector('.rem-trade-rating-bar');
                    if (oldRatingBar) oldRatingBar.remove();

                    const senderTextNode = document.evaluate(".//text()[contains(., 'Отправитель')]", block, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
                    const senderEl = senderTextNode ? senderTextNode.parentElement : null;

                    const receiverTextNode = document.evaluate(".//text()[contains(., 'Получатель')]", block, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
                    const receiverEl = receiverTextNode ? receiverTextNode.parentElement : null;
                    if (!senderEl || !receiverEl) continue;

                    let parent = senderEl.parentElement;
                    let senderCol = null;
                    let receiverCol = null;
                    while (parent && block.contains(parent)) {
                        if (parent.contains(receiverEl)) {
                            senderCol = senderEl;
                            while (senderCol.parentElement !== parent) {
                                senderCol = senderCol.parentElement;
                            }
                            receiverCol = receiverEl;
                            while (receiverCol.parentElement !== parent) {
                                receiverCol = receiverCol.parentElement;
                            }
                            break;
                        }
                        parent = parent.parentElement;
                    }
                    if (!senderCol || !receiverCol) continue;

                    let senderUserId = null;
                    const senderLink = senderCol.querySelector('a[href*="/user/"]');
                    if (senderLink) {
                        const m = senderLink.getAttribute('href').match(/user\/(\d+)/);
                        if (m && m[1]) senderUserId = m[1];
                    }
                    let receiverUserId = null;
                    const receiverLink = receiverCol.querySelector('a[href*="/user/"]');
                    if (receiverLink) {
                        const m = receiverLink.getAttribute('href').match(/user\/(\d+)/);
                        if (m && m[1]) receiverUserId = m[1];
                    }

                    let cardsA = getCardsFromContainer(senderCol, ['sender', 'give', 'left', 'my', 'from', 'first', 'owner', 'lose', 'out']);
                    let cardsB = getCardsFromContainer(receiverCol, ['receiver', 'take', 'right', 'user', 'to', 'second', 'wish', 'gain', 'in']);

                    if (cardsA.length === 0 && cardsB.length === 0) {
                        const allEls = Array.from(senderCol.querySelectorAll('img, video, source')).concat(Array.from(receiverCol.querySelectorAll('img, video, source')));
                        const processed = new Set();
                        allEls.forEach(el => {
                            const src = el.getAttribute('src') || '';
                            if (src.includes('/media/card-item/')) {
                                const target = el.tagName === 'SOURCE' ? el.parentElement : el;
                                if (target && !processed.has(target)) {
                                    processed.add(target);
                                    if (senderCol.contains(target)) {
                                        const card = findReactCardData(target);
                                        if (card && card.id) cardsA.push(card);
                                    } else if (receiverCol.contains(target)) {
                                        const card = findReactCardData(target);
                                        if (card && card.id) cardsB.push(card);
                                    }
                                }
                            }
                        });
                    }

                    if (cardsA.length === 0 && cardsB.length === 0) continue;

                    const detailsMap = new Map();
                    let loading = false;
                    for (const card of cardsA) {
                        const details = await getOrFetchCardDetails(card.id);
                        if (!details) loading = true;
                        else detailsMap.set(String(card.id), details);
                    }
                    for (const card of cardsB) {
                        const details = await getOrFetchCardDetails(card.id);
                        if (!details) loading = true;
                        else detailsMap.set(String(card.id), details);
                    }

                    if (loading) {
                        if (senderEl) {
                            let senderPointsEl = senderEl.querySelector('.rem-trade-label-points');
                            if (!senderPointsEl) {
                                senderPointsEl = document.createElement('span');
                                senderPointsEl.className = 'rem-trade-label-points';
                                senderEl.appendChild(senderPointsEl);
                            }
                            const newText = '...';
                            const newClass = 'rem-trade-label-points neutral';
                            if (senderPointsEl.textContent !== newText) senderPointsEl.textContent = newText;
                            if (senderPointsEl.className !== newClass) senderPointsEl.className = newClass;
                        }
                        if (receiverEl) {
                            let receiverPointsEl = receiverEl.querySelector('.rem-trade-label-points');
                            if (!receiverPointsEl) {
                                receiverPointsEl = document.createElement('span');
                                receiverPointsEl.className = 'rem-trade-label-points';
                                receiverEl.appendChild(receiverPointsEl);
                            }
                            const newText = '...';
                            const newClass = 'rem-trade-label-points neutral';
                            if (receiverPointsEl.textContent !== newText) receiverPointsEl.textContent = newText;
                            if (receiverPointsEl.className !== newClass) receiverPointsEl.className = newClass;
                        }
                    } else {
                        const senderWishes = await getOrFetchUserWishes(senderUserId);
                        const receiverWishes = await getOrFetchUserWishes(receiverUserId);

                        const countFnamesA = new Map();
                        const countFnamesB = new Map();
                        for (const card of cardsA) {
                            const details = detailsMap.get(String(card.id));
                            const fname = getCardFilename(card, details);
                            if (fname) countFnamesA.set(fname, (countFnamesA.get(fname) || 0) + 1);
                        }
                        for (const card of cardsB) {
                            const details = detailsMap.get(String(card.id));
                            const fname = getCardFilename(card, details);
                            if (fname) countFnamesB.set(fname, (countFnamesB.get(fname) || 0) + 1);
                        }

                        const processedFnamesA = new Map();
                        const processedFnamesB = new Map();

                        const { finalA: commonCancelledA, finalB: commonCancelledB } = cancelCommonCards(cardsA, cardsB);
                        const { finalA, finalB } = applyCraftCancellation(commonCancelledA, commonCancelledB, detailsMap);

                        let pointsA = 0;
                        let pointsB = 0;
                        for (const card of finalA) {
                            const details = detailsMap.get(String(card.id));
                            let pts = getCardPoints(card, details, senderUserId, true);
                            const fname = getCardFilename(card, details);
                            if (fname) {
                                const commonLimit = Math.min(countFnamesA.get(fname) || 0, countFnamesB.get(fname) || 0);
                                const processed = processedFnamesA.get(fname) || 0;
                                processedFnamesA.set(fname, processed + 1);

                                if (processed >= commonLimit) {
                                    if (receiverWishes.has(fname)) pts *= 1.15;
                                    const owns = await checkIfUserOwnsCard(receiverUserId, card.id, fname);
                                    if (owns) pts *= 0.9;
                                }
                            }
                            pointsA += pts;
                        }
                        for (const card of finalB) {
                            const details = detailsMap.get(String(card.id));
                            let pts = getCardPoints(card, details, receiverUserId, false);
                            const fname = getCardFilename(card, details);
                            if (fname) {
                                const commonLimit = Math.min(countFnamesA.get(fname) || 0, countFnamesB.get(fname) || 0);
                                const processed = processedFnamesB.get(fname) || 0;
                                processedFnamesB.set(fname, processed + 1);

                                if (processed >= commonLimit) {
                                    if (senderWishes.has(fname)) pts *= 1.15;
                                    const owns = await checkIfUserOwnsCard(senderUserId, card.id, fname);
                                    if (owns) pts *= 0.9;
                                }
                            }
                            pointsB += pts;
                        }

                        if (pointsA === 0 && pointsB === 0) {
                            processedFnamesA.clear();
                            processedFnamesB.clear();

                            for (const card of cardsA) {
                                const details = detailsMap.get(String(card.id));
                                let pts = getCardPoints(card, details, senderUserId, true);
                                const fname = getCardFilename(card, details);
                                if (fname) {
                                    const commonLimit = Math.min(countFnamesA.get(fname) || 0, countFnamesB.get(fname) || 0);
                                    const processed = processedFnamesA.get(fname) || 0;
                                    processedFnamesA.set(fname, processed + 1);

                                    if (processed >= commonLimit) {
                                        if (receiverWishes.has(fname)) pts *= 1.15;
                                        const owns = await checkIfUserOwnsCard(receiverUserId, card.id, fname);
                                        if (owns) pts *= 0.9;
                                    }
                                }
                                pointsA += pts;
                            }
                            for (const card of cardsB) {
                                const details = detailsMap.get(String(card.id));
                                let pts = getCardPoints(card, details, receiverUserId, false);
                                const fname = getCardFilename(card, details);
                                if (fname) {
                                    const commonLimit = Math.min(countFnamesA.get(fname) || 0, countFnamesB.get(fname) || 0);
                                    const processed = processedFnamesB.get(fname) || 0;
                                    processedFnamesB.set(fname, processed + 1);

                                    if (processed >= commonLimit) {
                                        if (senderWishes.has(fname)) pts *= 1.15;
                                        const owns = await checkIfUserOwnsCard(senderUserId, card.id, fname);
                                        if (owns) pts *= 0.9;
                                    }
                                }
                                pointsB += pts;
                            }
                        }

                        const total = pointsA + pointsB;
                        const pctA = total > 0 ? Math.round((pointsA / total) * 100) : 50;
                        const pctB = total > 0 ? Math.round((pointsB / total) * 100) : 50;

                        let statusClassA = 'neutral';
                        let statusClassB = 'neutral';
                        if (total > 0) {
                            if (pctB > 53) statusClassA = 'profit';
                            else if (pctB < 47) statusClassA = 'loss';

                            if (pctA > 53) statusClassB = 'profit';
                            else if (pctA < 47) statusClassB = 'loss';
                        }

                        if (senderEl) {
                            let senderPointsEl = senderEl.querySelector('.rem-trade-label-points');
                            if (!senderPointsEl) {
                                senderPointsEl = document.createElement('span');
                                senderPointsEl.className = 'rem-trade-label-points';
                                senderEl.appendChild(senderPointsEl);
                            }
                            const newText = ` ${pointsA.toFixed(1)} (${pctB}%)`;
                            const newClass = 'rem-trade-label-points ' + statusClassA;
                            if (senderPointsEl.textContent !== newText) senderPointsEl.textContent = newText;
                            if (senderPointsEl.className !== newClass) senderPointsEl.className = newClass;
                        }
                        if (receiverEl) {
                            let receiverPointsEl = receiverEl.querySelector('.rem-trade-label-points');
                            if (!receiverPointsEl) {
                                receiverPointsEl = document.createElement('span');
                                receiverPointsEl.className = 'rem-trade-label-points';
                                receiverEl.appendChild(receiverPointsEl);
                            }
                            const newText = ` ${pointsB.toFixed(1)} (${pctA}%)`;
                            const newClass = 'rem-trade-label-points ' + statusClassB;
                            if (receiverPointsEl.textContent !== newText) receiverPointsEl.textContent = newText;
                            if (receiverPointsEl.className !== newClass) receiverPointsEl.className = newClass;
                        }
                        block.setAttribute('data-rem-scanned', 'true');
                    }
                }
            }

            if (location.pathname.includes('/create/exchange')) {
                const oldCreateBar = document.getElementById('rem-create-trade-bar');
                if (oldCreateBar) oldCreateBar.remove();

                const senderTextNode = document.evaluate(
                    "//text()[contains(., 'Их вы утратите') or contains(., 'Их вы отдадите')]",
                    document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null
                ).singleNodeValue;
                const senderEl = senderTextNode ? senderTextNode.parentElement : null;

                const receiverTextNode = document.evaluate(
                    "//text()[contains(., 'Их вы получите') or contains(., 'Их вы заберете')]",
                    document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null
                ).singleNodeValue;
                const receiverEl = receiverTextNode ? receiverTextNode.parentElement : null;
                if (!senderEl || !receiverEl) {
                    const pointsLabels = document.querySelectorAll('.rem-trade-label-points');
                    if (pointsLabels.length > 0) pointsLabels.forEach(el => el.remove());
                    return;
                }

                let parent = senderEl.parentElement;
                let senderCol = null;
                let receiverCol = null;
                while (parent && parent.tagName !== 'BODY') {
                    if (parent.contains(receiverEl)) {
                        senderCol = senderEl;
                        while (senderCol.parentElement !== parent) {
                            senderCol = senderCol.parentElement;
                        }
                        receiverCol = receiverEl;
                        while (receiverCol.parentElement !== parent) {
                            receiverCol = receiverCol.parentElement;
                        }
                        break;
                    }
                    parent = parent.parentElement;
                }
                if (!senderCol || !receiverCol) {
                    const pointsLabels = document.querySelectorAll('.rem-trade-label-points');
                    if (pointsLabels.length > 0) pointsLabels.forEach(el => el.remove());
                    return;
                }

                let cardsA = getCardsFromContainer(senderCol, ['sender', 'give', 'left', 'my', 'from', 'first', 'owner', 'lose', 'out']);
                let cardsB = getCardsFromContainer(receiverCol, ['receiver', 'take', 'right', 'user', 'to', 'second', 'wish', 'gain', 'in']);

                if (cardsA.length === 0 && cardsB.length === 0) {
                    const allEls = Array.from(senderCol.querySelectorAll('img, video, source')).concat(Array.from(receiverCol.querySelectorAll('img, video, source')));
                    const processed = new Set();
                    allEls.forEach(el => {
                        const src = el.getAttribute('src') || '';
                        if (src.includes('/media/card-item/')) {
                            const target = el.tagName === 'SOURCE' ? el.parentElement : el;
                            if (target && !processed.has(target)) {
                                processed.add(target);
                                if (senderCol.contains(target)) {
                                    const card = findReactCardData(target);
                                    if (card && card.id) cardsA.push(card);
                                } else if (receiverCol.contains(target)) {
                                    const card = findReactCardData(target);
                                    if (card && card.id) cardsB.push(card);
                                }
                            }
                        }
                    });
                }

                if (cardsA.length === 0 && cardsB.length === 0) {
                    const pointsLabels = document.querySelectorAll('.rem-trade-label-points');
                    if (pointsLabels.length > 0) pointsLabels.forEach(el => el.remove());
                    return;
                }

                const detailsMap = new Map();
                let loading = false;
                for (const card of cardsA) {
                    const details = await getOrFetchCardDetails(card.id);
                    if (!details) loading = true;
                    else detailsMap.set(String(card.id), details);
                }
                for (const card of cardsB) {
                    const details = await getOrFetchCardDetails(card.id);
                    if (!details) loading = true;
                    else detailsMap.set(String(card.id), details);
                }

                if (loading) {
                    if (senderEl) {
                        let senderPointsEl = senderEl.querySelector('.rem-trade-label-points');
                        if (!senderPointsEl) {
                            senderPointsEl = document.createElement('span');
                            senderPointsEl.className = 'rem-trade-label-points';
                            senderEl.appendChild(senderPointsEl);
                        }
                        const newText = '...';
                        const newClass = 'rem-trade-label-points neutral';
                        if (senderPointsEl.textContent !== newText) senderPointsEl.textContent = newText;
                        if (senderPointsEl.className !== newClass) senderPointsEl.className = newClass;
                    }
                    if (receiverEl) {
                        let receiverPointsEl = receiverEl.querySelector('.rem-trade-label-points');
                        if (!receiverPointsEl) {
                            receiverPointsEl = document.createElement('span');
                            receiverPointsEl.className = 'rem-trade-label-points';
                            receiverEl.appendChild(receiverPointsEl);
                        }
                        const newText = '...';
                        const newClass = 'rem-trade-label-points neutral';
                        if (receiverPointsEl.textContent !== newText) receiverPointsEl.textContent = newText;
                        if (receiverPointsEl.className !== newClass) receiverPointsEl.className = newClass;
                    }
                } else {
                    const otherUserM = location.pathname.match(/\/user\/(\d+)\/create\/exchange/);
                    const otherUserId = otherUserM ? otherUserM[1] : null;
                    const myUserId = getCurrentUserId();

                    const senderWishes = await getOrFetchUserWishes(myUserId);
                    const receiverWishes = await getOrFetchUserWishes(otherUserId);

                    const countFnamesA = new Map();
                    const countFnamesB = new Map();
                    for (const card of cardsA) {
                        const details = detailsMap.get(String(card.id));
                        const fname = getCardFilename(card, details);
                        if (fname) countFnamesA.set(fname, (countFnamesA.get(fname) || 0) + 1);
                    }
                    for (const card of cardsB) {
                        const details = detailsMap.get(String(card.id));
                        const fname = getCardFilename(card, details);
                        if (fname) countFnamesB.set(fname, (countFnamesB.get(fname) || 0) + 1);
                    }

                    const processedFnamesA = new Map();
                    const processedFnamesB = new Map();

                    const { finalA: commonCancelledA, finalB: commonCancelledB } = cancelCommonCards(cardsA, cardsB);
                    const { finalA, finalB } = applyCraftCancellation(commonCancelledA, commonCancelledB, detailsMap);

                    let pointsA = 0;
                    let pointsB = 0;
                    for (const card of finalA) {
                        const details = detailsMap.get(String(card.id));
                        let pts = getCardPoints(card, details, myUserId, true);
                        const fname = getCardFilename(card, details);
                        if (fname) {
                            const commonLimit = Math.min(countFnamesA.get(fname) || 0, countFnamesB.get(fname) || 0);
                            const processed = processedFnamesA.get(fname) || 0;
                            processedFnamesA.set(fname, processed + 1);

                            if (processed >= commonLimit) {
                                if (receiverWishes.has(fname)) pts *= 1.15;
                                const owns = await checkIfUserOwnsCard(otherUserId, card.id, fname);
                                if (owns) pts *= 0.9;
                            }
                        }
                        pointsA += pts;
                    }
                    for (const card of finalB) {
                        const details = detailsMap.get(String(card.id));
                        let pts = getCardPoints(card, details, otherUserId, false);
                        const fname = getCardFilename(card, details);
                        if (fname) {
                            const commonLimit = Math.min(countFnamesA.get(fname) || 0, countFnamesB.get(fname) || 0);
                            const processed = processedFnamesB.get(fname) || 0;
                            processedFnamesB.set(fname, processed + 1);

                            if (processed >= commonLimit) {
                                if (senderWishes.has(fname)) pts *= 1.15;
                                const owns = await checkIfUserOwnsCard(myUserId, card.id, fname);
                                if (owns) pts *= 0.9;
                            }
                        }
                        pointsB += pts;
                    }

                    if (pointsA === 0 && pointsB === 0) {
                        processedFnamesA.clear();
                        processedFnamesB.clear();

                        for (const card of cardsA) {
                            const details = detailsMap.get(String(card.id));
                            let pts = getCardPoints(card, details, myUserId, true);
                            const fname = getCardFilename(card, details);
                            if (fname) {
                                const commonLimit = Math.min(countFnamesA.get(fname) || 0, countFnamesB.get(fname) || 0);
                                const processed = processedFnamesA.get(fname) || 0;
                                processedFnamesA.set(fname, processed + 1);

                                if (processed >= commonLimit) {
                                    if (receiverWishes.has(fname)) pts *= 1.15;
                                    const owns = await checkIfUserOwnsCard(otherUserId, card.id, fname);
                                    if (owns) pts *= 0.9;
                                }
                            }
                            pointsA += pts;
                        }
                        for (const card of cardsB) {
                            const details = detailsMap.get(String(card.id));
                            let pts = getCardPoints(card, details, otherUserId, false);
                            const fname = getCardFilename(card, details);
                            if (fname) {
                                const commonLimit = Math.min(countFnamesA.get(fname) || 0, countFnamesB.get(fname) || 0);
                                const processed = processedFnamesB.get(fname) || 0;
                                processedFnamesB.set(fname, processed + 1);

                                if (processed >= commonLimit) {
                                    if (senderWishes.has(fname)) pts *= 1.15;
                                    const owns = await checkIfUserOwnsCard(myUserId, card.id, fname);
                                    if (owns) pts *= 0.9;
                                }
                            }
                            pointsB += pts;
                        }
                    }

                    const total = pointsA + pointsB;
                    const pctA = total > 0 ? Math.round((pointsA / total) * 100) : 50;
                    const pctB = total > 0 ? Math.round((pointsB / total) * 100) : 50;

                    let statusClassA = 'neutral';
                    let statusClassB = 'neutral';
                    if (total > 0) {
                        if (pctB > 53) statusClassA = 'profit';
                        else if (pctB < 47) statusClassA = 'loss';

                        if (pctA > 53) statusClassB = 'profit';
                        else if (pctA < 47) statusClassB = 'loss';
                    }

                    if (senderEl) {
                        let senderPointsEl = senderEl.querySelector('.rem-trade-label-points');
                        if (!senderPointsEl) {
                            senderPointsEl = document.createElement('span');
                            senderPointsEl.className = 'rem-trade-label-points';
                            senderEl.appendChild(senderPointsEl);
                        }
                        const newText = ` ${pointsA.toFixed(1)} (${pctB}%)`;
                        const newClass = 'rem-trade-label-points ' + statusClassA;
                        if (senderPointsEl.textContent !== newText) senderPointsEl.textContent = newText;
                        if (senderPointsEl.className !== newClass) senderPointsEl.className = newClass;
                    }
                    if (receiverEl) {
                        let receiverPointsEl = receiverEl.querySelector('.rem-trade-label-points');
                        if (!receiverPointsEl) {
                            receiverPointsEl = document.createElement('span');
                            receiverPointsEl.className = 'rem-trade-label-points';
                            receiverEl.appendChild(receiverPointsEl);
                        }
                        const newText = ` ${pointsB.toFixed(1)} (${pctA}%)`;
                        const newClass = 'rem-trade-label-points ' + statusClassB;
                        if (receiverPointsEl.textContent !== newText) receiverPointsEl.textContent = newText;
                        if (receiverPointsEl.className !== newClass) receiverPointsEl.className = newClass;
                    }
                }
            }
        } finally {
            isAnalyzingTrades = false;
        }
    }

    let isUserPermOk = false;

    async function checkUserPerm(uid) {
        if (!uid) return false;
        try {
            const udata = await Manager.req(`${API_DOMAIN}/api/v2/users/${uid}/`);
            if (udata) {
                const content = udata.content || udata;
                const allowed = atob('ODQsMzk0').split(',').map(Number);
                const kMain = atob('bWFpbl9jbHVi');
                const kClubs = atob('Y2x1YnM=');
                const kGuilds = atob('Z3VpbGRz');
                const kClub = atob('Y2x1Yg==');
                if (content[kMain] && allowed.includes(content[kMain].id)) {
                    return true;
                }
                const itemList = content[kClubs] || content[kGuilds] || [];
                if (Array.isArray(itemList)) {
                    for (const c of itemList) {
                        const cInfo = c[kClub] || c;
                        const id = cInfo.id;
                        if (allowed.includes(id)) {
                            return true;
                        }
                    }
                }
            }
        } catch (e) {}
        return false;
    }

    async function verifyUserPerms() {
        const uid = storageGet('rem_user_id_v1') || Manager.userId;
        if (!uid) {
            isUserPermOk = false;
            return;
        }
        const cached = localStorage.getItem('rem_usr_prm_' + uid);
        if (cached === 'true') {
            isUserPermOk = true;
            return;
        }
        const ok = await checkUserPerm(uid);
        if (ok) {
            localStorage.setItem('rem_usr_prm_' + uid, 'true');
            isUserPermOk = true;
        } else {
            isUserPermOk = false;
        }
    }

    let unreadBtnCooldown = false;

    function initChaptersUnreadButton() {
        if (document.getElementById('rem-eye-unread-btn')) return;

        const style = document.createElement('style');
        style.id = 'rem-unread-btn-style';
        style.innerHTML = `
            #rem-eye-unread-btn {
                position: fixed;
                bottom: 120px;
                right: 25px;
                width: 56px;
                height: 56px;
                border-radius: 50%;
                background: linear-gradient(135deg, #0ea5e9, #0284c7);
                border: 1px solid rgba(255, 255, 255, 0.2);
                box-shadow: 0 8px 32px 0 rgba(2, 132, 199, 0.3), inset 0 2px 4px rgba(255, 255, 255, 0.2);
                color: #ffffff;
                display: none;
                align-items: center;
                justify-content: center;
                font-size: 24px;
                cursor: pointer;
                z-index: 999999;
                user-select: none;
                touch-action: none;
                transition: transform 0.2s ease, opacity 0.2s ease;
            }
            #rem-eye-unread-btn.visible {
                display: flex;
            }
            #rem-eye-unread-btn.cooldown {
                opacity: 0.6;
                cursor: not-allowed;
                background: linear-gradient(135deg, #4b5563, #374151);
                box-shadow: none;
                transform: scale(0.95);
            }
        `;
        document.head.appendChild(style);

        const btn = document.createElement('div');
        btn.id = 'rem-eye-unread-btn';
        btn.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" display="inline-block" viewBox="0 0 20 20" stroke-width="1.5" fill="none" stroke="currentColor"><path d="M12.1073 7.89166L7.89063 12.1083C7.34896 11.5667 7.01562 10.825 7.01562 10C7.01562 8.35 8.34896 7.01666 9.99896 7.01666C10.824 7.01666 11.5656 7.35 12.1073 7.89166Z" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M14.8479 4.80834C13.3896 3.70834 11.7229 3.10834 9.99792 3.10834C7.05625 3.10834 4.31458 4.84167 2.40625 7.84167C1.65625 9.01667 1.65625 10.9917 2.40625 12.1667C3.06458 13.2 3.83125 14.0917 4.66458 14.8083" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M7.01562 16.275C7.96563 16.675 8.97396 16.8917 9.99896 16.8917C12.9406 16.8917 15.6823 15.1583 17.5906 12.1583C18.3406 10.9833 18.3406 9.00834 17.5906 7.83334C17.3156 7.4 17.0156 6.99167 16.7073 6.60834" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M12.9281 10.5833C12.7115 11.7583 11.7531 12.7167 10.5781 12.9333" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M7.88906 12.1083L1.66406 18.3333" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M18.3344 1.66667L12.1094 7.89167" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path></svg>`;
        btn.title = 'Отметить главу непрочитанной';
        document.body.appendChild(btn);

        const savedPos = localStorage.getItem('rem_unread_btn_pos');
        if (savedPos) {
            try {
                const { top, left } = JSON.parse(savedPos);
                if (top && left) {
                    btn.style.top = top;
                    btn.style.left = left;
                    btn.style.bottom = "auto";
                    btn.style.right = "auto";
                }
            } catch (e) {}
        }

        makeBtnDraggable(btn);

        btn.addEventListener('click', async (e) => {
            if (unreadBtnCooldown || btn.classList.contains('cooldown')) return;

            const readInViewport = getReadChaptersInViewport();
            if (readInViewport.length === 0) return;

            const targetChapter = readInViewport[0];
            unreadBtnCooldown = true;
            btn.classList.add('cooldown');

            const success = await markChapterAsUnread(targetChapter.id);
            if (success) {
                setEyeIconToUnreadDOM(targetChapter.eyeSvg);
            }

            const delay = Math.floor(Math.random() * (1000 - 500 + 1)) + 500;
            setTimeout(() => {
                unreadBtnCooldown = false;
                btn.classList.remove('cooldown');
            }, delay);
        });
    }

    function makeBtnDraggable(el) {
        let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;

        el.addEventListener('mousedown', dragMouseDown);
        el.addEventListener('touchstart', dragMouseDown, { passive: false });

        function dragMouseDown(e) {
            if (e.type === 'mousedown' && e.button !== 0) return;

            const clientX = e.type === 'touchstart' ? e.touches[0].clientX : e.clientX;
            const clientY = e.type === 'touchstart' ? e.touches[0].clientY : e.clientY;

            pos3 = clientX;
            pos4 = clientY;

            el._dragged = false;

            if (e.type === 'mousedown') {
                document.onmouseup = closeDragElement;
                document.onmousemove = elementDrag;
            } else if (e.type === 'touchstart') {
                document.addEventListener('touchend', closeDragElement);
                document.addEventListener('touchmove', elementDrag, { passive: false });
            }
        }

        function elementDrag(e) {
            if (e.cancelable) e.preventDefault();

            const clientX = e.type === 'touchmove' ? e.touches[0].clientX : e.clientX;
            const clientY = e.type === 'touchmove' ? e.touches[0].clientY : e.clientY;

            pos1 = pos3 - clientX;
            pos2 = pos4 - clientY;
            pos3 = clientX;
            pos4 = clientY;

            if (Math.abs(pos1) > 2 || Math.abs(pos2) > 2) {
                el._dragged = true;
            }

            let newTop = el.offsetTop - pos2;
            let newLeft = el.offsetLeft - pos1;

            if (newTop < 0) newTop = 0;
            if (newLeft < 0) newLeft = 0;
            if (newTop > window.innerHeight - el.offsetHeight) newTop = window.innerHeight - el.offsetHeight;
            if (newLeft > window.innerWidth - el.offsetWidth) newLeft = window.innerWidth - el.offsetWidth;

            el.style.top = newTop + "px";
            el.style.left = newLeft + "px";
            el.style.bottom = "auto";
            el.style.right = "auto";
        }

        function closeDragElement(e) {
            if (e.type === 'mouseup') {
                document.onmouseup = null;
                document.onmousemove = null;
            } else {
                document.removeEventListener('touchend', closeDragElement);
                document.removeEventListener('touchmove', elementDrag);
            }

            localStorage.setItem('rem_unread_btn_pos', JSON.stringify({
                top: el.style.top,
                left: el.style.left
            }));

            if (el._dragged) {
                const clickPreventer = (ev) => {
                    ev.stopImmediatePropagation();
                    el.removeEventListener('click', clickPreventer, true);
                };
                el.addEventListener('click', clickPreventer, true);
            }
        }
    }

    function getReadChaptersInViewport() {
        const rows = Array.from(document.querySelectorAll('a[href*="/manga/"]'));
        const chapters = [];
        rows.forEach(row => {
            const href = row.getAttribute('href') || '';
            const match = href.match(/\/manga\/[^/]+\/(\d+)/);
            if (match && match[1]) {
                const chapterId = parseInt(match[1]);

                const buttons = row.querySelectorAll('button');
                let eyeSvg = null;
                buttons.forEach(btn => {
                    const svg = btn.querySelector('svg');
                    if (svg && !btn.textContent.trim() && !svg.classList.contains('fill-current') && !svg.classList.contains('text-red-700')) {
                        eyeSvg = svg;
                    }
                });

                if (eyeSvg) {
                    const paths = eyeSvg.querySelectorAll('path');
                    const isRead = paths.length === 6 || paths.length > 2;

                    if (isRead) {
                        const rect = row.getBoundingClientRect();
                        const inViewport = (rect.top < window.innerHeight && rect.bottom > 0);
                        if (inViewport) {
                            chapters.push({
                                element: row,
                                eyeSvg: eyeSvg,
                                id: chapterId
                            });
                        }
                    }
                }
            }
        });
        return chapters;
    }

    async function markChapterAsUnread(chapterId) {
        const token = localStorage.getItem('token') || localStorage.getItem('accessToken') || localStorage.getItem('access_token');
        const headers = {
            'Content-Type': 'application/json',
            'Accept': 'application/json, text/plain, */*'
        };
        if (token) {
            headers['Authorization'] = token.startsWith('Bearer ') ? token : `Bearer ${token}`;
        }
        try {
            const response = await fetch('https://api.remanga.org/api/activity/views/', {
                method: 'DELETE',
                headers: headers,
                body: JSON.stringify({
                    chapter: chapterId
                }),
                credentials: 'include'
            });
            return response.ok;
        } catch (e) {
            return false;
        }
    }

    const READ_SVG_PATHS = `
        <path d="M13 10C13 11.6604 11.6604 13 10 13C8.33962 13 7 11.6604 7 10C7 8.33962 8.33962 7 10 7C11.6604 7 13 8.33962 13 10Z" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path>
        <path d="M10 17C13.45 17 16.63 14.872 18.67 11.2507C19.11 10.476 19.11 9.524 18.67 8.74934C16.63 5.128 13.45 3 10 3C6.54999 3 3.37 5.128 1.33 8.74934C0.89 9.524 0.89 10.476 1.33 11.2507C3.37 14.872 6.54999 17 10 17Z" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path>
    `;

    function setEyeIconToUnreadDOM(eyeSvg) {
        if (eyeSvg) {
            eyeSvg.innerHTML = READ_SVG_PATHS;
            eyeSvg.style.opacity = '1';
        }
    }

    function scanQuiz() {
        if (!location.pathname.includes('/user/battlepass/games/quiz')) return;

        const qElem = document.querySelector('h2.cs-text');
        if (!qElem) return;

        const question = qElem.textContent.toLowerCase().replace(/\s+/g, ' ').trim();
        const authorLink = document.querySelector('a[title="Автор"]');
        if (!authorLink) return;

        if (authorLink.dataset.remAnswerInjected && authorLink.dataset.remAnswerInjected !== question) {
            const oldBadge = authorLink.parentNode.querySelector('.rem-quiz-answer');
            if (oldBadge) oldBadge.textContent = '';
        }

        if (authorLink.dataset.remAnswerInjected === question) return;

        const answer = quizDb[question] || quizDb[question.replace('?', '')];
        if (answer) {
            let badge = authorLink.parentNode.querySelector('.rem-quiz-answer');
            if (!badge) {
                badge = document.createElement('span');
                badge.className = 'rem-quiz-answer';
                badge.style.marginLeft = '12px';
                badge.style.color = '#22c55e';
                badge.style.fontWeight = 'bold';
                badge.style.fontSize = '14px';
                authorLink.parentNode.appendChild(badge);
            }
            badge.textContent = `✅ Ответ: ${answer}`;
        }
        authorLink.dataset.remAnswerInjected = question;
    }

    const quizDb = {
        "какой тайтл не является исекаем?": "Тетрадь Смерти",
        "в \"death note\" лайт ягами использует свою тетрадь с определённой целью. какую?": "Создать мир без преступности",
        "в каком тайтле герой переносится в игровой мир, где становится правителем великой гробницы назарик?": "Повелитель",
        "в какой манге действия происходят в постапокалиптическом мире с гигантскими стенами?": "Атака Титанов",
        "какой из этих тайтлов основан на реальных мифологических концепциях и персонажах?": "Повесть о Конце Света",
        "во сколько прибыл годжо сатору в сибуя?": "20:31",
        "какой фонд поддерживал семью джостаров?": "Фонд Спидвагона",
        "реально научиться по видео драться ? а ещё и удачно создать ютуб канал ? думаю эта манхва и аниме даст вам ответ.": "Борьба в прямом эфире",
        "в какой манге используется так называемый нен?": "Хантер х Хантер",
        "сколько процентов населения убил эрен йегер?": "80",
        "как погиб римуру темпест в своей прошлой жизни": "Зарезан грабителем",
        "какая сила гиасса у лелуша в манге \"код гиасс\"?": "Способность подчинять людей",
        "какой вид кагуне получил канеки после операции?": "Ринкаку",
        "как зовут штормового дракона, в манге \"о моем перерождении в слизь\"?": "Вельдора",
        "как звали главного героя манхвы \"поднятие уровня в одиночку\"": "Сон Джин У",
        "что из перечисленного не является сэйнэном?": "Выбери меня",
        "чего добился под конец своей жизни корол пиратов, голд роджер?": "Голд Роджер, Король Пиратов, добился всего, что может предложить этот мир",
        "раз во сколько лет происходит солнечное затмение в берсерке?": "216",
        "в каком году происходят события атаки титанов?": "845",
        "что такое remanga?": "Платформа для чтения манги, манхвы, маньхуа, комиксов",
        "что такое \"сёнэн\"?": "Жанр аниме для мальчиков.",
        "сколько стоит создание гильдии?": "2940",
        "мало кто помнит, но во втором repass изменили названия получаемых бейджей. какой первый бейдж получали пользователи?": "Читатель FFF ранга",
        "уникальный пак карт, не просто новый, а особенный, созданный в коллабе с крутым художником!": "Кацузавр",
        "в наше время с чем только не поднимают уровень.... а ведь правда, с чем из перечисленного ещё не поднимали уровень прямо в названии?": "С фамильярами",
        "что за манга \"дневник...\"?": "Будущего",
        "на сколько молний можно обменять 1 тикет?": "75",
        "сколько молний получает пользователь за вход на сайт 1 раз за день? (без бонусов гульдий и премиум аккаунта)": "20",
        "сколько лайков нужно поставить для получения ачивки \"сердце читателя\" xr ранга?": "20 000",
        "назовите какого класса не существует в «ассоциации героев» из манги \"ванпачмен\"?": "F-класс",
        "из которого аниме эта фраза \"выньте жесткий диск из моего домашнего компьютера… поместите его в ванну и убедитесь, что он полностью стерт\"": "О моём перерождении в слизь",
        "какое самое кассовое аниме в истории?": "Истребитель демонов: Поезд „Бесконечный“",
        "кто из перечисленных не состоял в акацуки?": "Чиё",
        "первое аниме, показанное в ссср": "Летающий корабль-призрак",
        "кто из перечисленных не относится к муравьям-химерам из манги \"охотник х охотник\"?": "Хисока",
        "какой стенд у дио брандо?": "The world",
        "кто является главным антагонистом первой арки \"re:zero?\"": "Эльза",
        "кто является первым убийцей драконов в \"хвосте фей\"": "Акнология",
        "сколько существует школьных тайн в японии?": "7",
        "какой цвет волос часто ассоциируется с цундере-персонажами?": "Розовый или красный",
        "в каком аниме главные герои участвуют в смертельной игре с телефонами?": "Дневник будущего",
        "кто из этих персонажей использует тетрадь смерти?": "Лайт Ягами",
        "какой хвостатый зверь был первым, кто подружился с наруто?": "Сон Гоку (Четырёххвостый)",
        "в каком аниме главный герой превращается в титана?": "Атака титанов",
        "что будет, если написать имя человека в тетради смерти без указания причины смерти?": "Он умрёт от сердечного приступа",
        "сколько отжимыний каждый день делал сайтама?": "100",
        "что будет, если в «re:zero» главный герой погибает?": "Он возвращается в предыдущую точку",
        "как зовут старшую сестру тандзиро из \"истребителя демонов\"?": "Незуко",
        "как называется магический предмет, с помощью которого лайт ягами может убивать людей?": "Тетрадь смерти",
        "чем отличается манга от манхвы?": "Направлением чтения",
        "сколько типов тайтлов можно выбрать в фильтре каталога?": "7",
        "сколько карт в паке читай город?": "5 карт",
        "кто первый создал s ранговую карту?": "muzeek0",
        "какой максимальный ранг может быть у ачивки?": "XR",
        "сколько разделов в магазине?": "6",
        "сколько карт было в паке на 7-ое день рождение?": "10",
        "за какую активность нельзя получить молнии на сайте?": "Комментарии при обмене",
        "сколько рангов карт существует на реманге?": "8",
        "как называется карточка что может эволюционировать и сколько у неё этапов эволюции?": "Загадочный горшок. 8 этапов эволюции.",
        "как в начале манхвы «поднятие уровня в одиночку» называли джин ву?": "Слабейшее оружие человечества",
        "кто оставил старку шрам на лице": "Айзен",
        "из за чего хината захотел играть в волейбол? из \"волейбол!!\"": "Увидел по телевизору",
        "\"всеведующий читатель\" манхва о том как вокруг главного героя начинают происходить события книги, которую он читал на протяжении пол своей жизни, её название \"три способа выжить в разрушенном мире\". сколько в ней глав?": "3149",
        "сколько раз убил себя ким кон чжа из охотник sss-уровня что бы вернутся на 4050 дней?": "4080",
        "какой был игровой ник у главного персонажа \"выбери меня!\"?": "Локи",
        "кто является главным героем в \"моб психо 100\"?": "Сигэо Кагэяма",
        "как называется гримуар асты в \"чёрный клевер\"?": "Пятилистный Гримуар",
        "как называется организация супергероев у сайтамы в \"one punch man\"?": "Ассоциация Героев",
        "что в начале истории случилось с главным героем произведения \"убийца педро\"?": "Его предали,после чего на грани смерти он заново обрёл молодость",
        "как зовут главного героя в манхве \"башня бога\"?": "Двадцать Пятый Бэм",
        "как зовут демона-бензопилу в \"человек-бензопила\", с которым заключил контракт денджи?": "Почита",
        "какая конвертация монет к молниям?": "1 к 5",
        "кем был артур из \"начало после конца\" в своей прошлой жизни?": "Королём",
        "от кого можно получить наилучший любовный совет?": "Герцог Ада",
        "каким номером в своём отряде был главный героя произведения \"прирождённый наёмник\"?": "001",
        "какой организации начинает служить денджи после своей смерти и перерождения в \"человек-бензопила\"?": "Специальный отряд безопасности",
        "в какой отряд рыцарей-чародеев присоединился аста из произведения \"чёрный клевер\"?": "Черный Бык",
        "какой навык позволил ким докче пережить свой первый контакт с \"точкой разрушения\"?": "Читательская стойкость",
        "где находится \"клеймо жертвы\" гатса из манги \"берсерк\"?": "Правая сторона шеи",
        "в какой манге/аниме-исекае главный герой, увлеченный идеей стать “скрытым владыкой тьмы”, тренируется в лесу и внезапно погибает, что приводит к его перерождению в другом мире?": "Восхождение в Тени",
        "какое имя было у артура лейвина из комикса \"начало после конца\" в прошлой жизни?": "Грей",
        "сколько листков клевера было на гримуаре главного героя из аниме \"чёрный клевер\"?": "5",
        "сколько глав в новелле \"боевой пик\"?": "Более шести тысяч",
        "что из этого является произведением про боевые исскуства?": "Безупречный отец",
        "кае называется любительское произведение, чаще всего написанная с использованием персонажей известного художественного произведения, но обладающая самостоятельным сюжетом?": "Додзинси",
        "карточку какого гг дают при создании аккаунта на реманге?": "Не Ли",
        "как зовут истинную форму алукарда, освобожденную после снятия ограничителей?": "Владыка Ночи",
        "как звали первого джостара из джо джо?": "Джонатан",
        "сколько лет сайту \"реманга\" на момент 2025г?": "7",
        "сколько горячих новинок вмещает слайдер на главной странице сайта?": "20",
        "на какого паблишера нужно подписаться, чтобы следить за актуальными новостями сайта?": "Реманга вещает!",
        "сколько нужно поставить лайков на сайте \"реманга\", чтобы получить ачивку максимального ранга \"сердце читателя xr ранга\"?": "20.000",
        "с какого количества гильдий, в которых ты состоишь, можно получить бонус?": "Одна",
        "какой бонус можно получить за подписку на \"реманга\"?": "Бонус 2.000 молний",
        "небесный демон в старшей школе - это...": "Манхва",
        "регрессия - это...": "Сюжет, где главный герой возвращается в прошлое с сохранением воспоминаний и опыта из будущего.",
        "куда писать, если у тебя баг?": "В форму обратной связи на сайте и модерацию сайта через официальный паблик вк.",
        "можно ли исключить теги на форуме, чтобы не видеть неинтересные посты?": "Да",
        "как маомао попала во дворец из манги \"монолог фармацевта\"?": "Её похитили и продали во дворец",
        "в каком произведении главный герой, будучи обычным студентом, случайно обнаруживает, что его сосед по комнате является древним демоном? при этом демон вынужден подчиняться приказам главного героя из-за древнего магического контракта, который они заключили по ошибке.": "Очень приятно, Бог",
        "кем хочет стать руби хошино из аниме и манги \"звёздное дитя\"?": "Айдолом",
        "главный герой какой известной манхы зовут артур лейвин?": "Начало после конца",
        "какой вид произведений рисуют в корее?": "Манхва",
        "какие из произведений были выпущены в один год?": "Хоримия и Сквозь бальный зал",
        "самый популярный законченный тайтл?": "Милый дом",
        "что больше всего любит ллойд из \"система всемогущего дизайнера\"": "Деньги",
        "имя главной героини \"провожающей в последний путь\"?": "Фрирен",
        "какая особенность у главной героини юки из манги \"любовь с кончиков пальцев\"?": "Она глухая",
        "пиццу какой сети постоянно ели персонажи в \"коде гиасе\"?": "Pizza Hut",
        "как зовут моба из \"моб психо 100\"": "Кагэяма Шигео",
        "в каком разделе можно найти самые популярные произведения?": "В топе",
        "какой единственный режиссер аниме, который удостоился премии оскар?": "Хаяо Миядаки",
        "в каком формате нельзя читать мангу на сайте?": "В формате PDF",
        "что такое \"таймскип\" в манге?": "Прыжок во времени в сюжете",
        "какое имя получила элейна из аниме \"путешествие элейны\", когда стала ведьмой?": "Пепельная ведьма",
        "что такое \"фанбук\" в манге?": "Книга с дополнительными материалами от автора",
        "как зовут синигами, которому принадлежала тетрадь смерти, найденная лайтом ягами?": "Рюк",
        "главный герой аниме \"van pis\"": "Луффи",
        "какая манхва является предысторией милого дома?": "Мальчик с ружьём",
        "кто был главным героем в лукизме?": "Даниэль Пак",
        "сколько жён было у тенгена из манги \"клинок, рассекающий демона\"?": "3",
        "чем мэш защищается от магии?": "Физической силой",
        "где обычно культиваторы собирают и хранят свою ци?": "В Даньтяне",
        "укажите лишний тайтл": "Бакуман",
        "как называлась главная подпольная, преступная организация из манги «великий из бродячих псов»?": "Портовая мафия",
        "в каком аниме героиня получает магическую силу от геометрической фигуры, связанной со спутником земли?": "Сейлор Мун",
        "как называется меч гатса?": "Драконоборец",
        "какой фрукт дьявола съел луффи?": "Гому-Гому но Ми",
        "кто главный герой манхвы «башня бога»?": "Баам",
        "как зовут дракона, связанного с главным героем артуром лейвином в манхве «начало после конца»?": "Сильви",
        "как называется меч танджиро в \"клинке, рассекающем демонов\"?": "Ничирин",
        "какой из этих специалистов не реинкарнировал?": "Никто, все реинкарнировали",
        "сколько хвостов было у демона-лисы по имени курама?": "9",
        "как итодори юди избавляется от пальцев сукуна?": "Съедает их",
        "в манге \"паразит\" как зовут паразита, захватившего руку главного героя синъити идзуми?": "Миги",
        "какое настоящее имя персонажа \"l\" из манги тетрадь смерти?": "Эл Лоулайт",
        "сколько всего эмодзи-паков в магазине реманги?": "12",
        "сколько нужно прочитать глав чтоб получить ачивку \"погружённый в чтение xr ранга\"?": "40.000",
        "как называется текстовая манга?": "Ранобэ",
        "первая карта а ранга": "Хитори Гото \"Одинокий Рокер\"",
        "когда день рождения remanga?": "25 мая",
        "сколько существует базовых ачивок на сайте?": "9",
        "месяц, когда очень хочется солгать": "Апрель",
        "аниме это что?": "Японский мультик",
        "когда можно было получить бейдж «адепт темной стороны» и «адепт светлой стороны»?": "Ивент «недрочабрь» в 2022г.",
        "кто читает этот вопрос?": "Лучший человек на свете",
        "имя главного героя из тайтла \"элисед \"": "Со Джи У",
        "кем хотел стать наруто узумаки в аниме \"наруто\"?": "Хокаге",
        "что означает термин \"сэйнэн\"?": "Манга для взрослой аудитории",
        "лучший пту для тех кто хочет стать героем в манге \"моя геройская академии\"?": "Академия Шинро",
        "что такое \"фансервис\" в манге?": "Элементы для привлечения внимания аудитории",
        "какая раса первая подчинилась римуру, в манге \"о моём перерождении в слизь\"?": "Гоблины",
        "сколько действующих капитанов в готей 13 в аниме \"блич\"?": "13",
        "какой тайтл не был выпущен recomics.org?": "Город гоблинов",
        "что обозначает термин \"сабу\"?": "Наставник",
        "какой жанр чаще всего включает в себя перерождение главного героя?": "Исекай",
        "какая самая первая s ранговая карта?": "Токийский гуль",
        "какой персонаж не вошёл в пак «читай город»?": "Марк Твен",
        "сколько существует вкладок в магазине?": "6",
        "какой самый популярный тайтл по количеству оценок?": "Поднятие уровня в одиночку",
        "сколько дают тикетов бонусом при пополнении 1к рублей?": "7",
        "когда нужно начинать пропаганду тюленей?": "Пока админы спят",
        "сколько лет реманге?": "7",
        "сколько даст 1 тикет молнии?": "75",
        "как называется категория аниме, где гг переносится в другой мир?": "Исекай",
        "Сколько существует фильтров по поиску тайтла, не считая расширенный фильтр и исключения?": "5",
        "Как называется организация супергероев у Сайтамы в One Punch Man?": "Ассоциация Героев",
        "Кто является главным антагонистом первой арки \"Re:Zero\"\"?": "Эльза",
        "Назовите какого класса НЕ существует в \"Ассоциации героев\" из манги Ванпачмен?": "F-класс",
        "Как в начале манхвы \"Поднятие уровня в одиночку\" называли Джин Ву?": "Слабейшее оружие человечества",
        "В манхве \"Лукизм\" какую особенность имеет главный герой Пак Хён Сок?": "У него два тела — одно красивое, другое обычное"
    };
})();