Remanga

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

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

Advertisement:

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

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-класс",
        "Как в начале манхвы \"Поднятие уровня в одиночку\" называли Джин Ву?": "Слабейшее оружие человечества",
        "В манхве \"Лукизм\" какую особенность имеет главный герой Пак Хён Сок?": "У него два тела — одно красивое, другое обычное"
    };
})();