☰

Erome Ultimate Premium — By Insomnia

The ultimate all-in-one Erome enhancer: TikTok-style vertical

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Erome Ultimate Premium — By Insomnia
// @namespace    https://github.com// 
// @version      7.3.6-hover-length-filter
// @description  The ultimate all-in-one Erome enhancer: TikTok-style vertical  
// @icon         https://www.erome.com/favicon-32x32.png
// @match        https://*.erome.com/*
// @match        https://www.erome.com/*
// @run-at       document-end
// @grant        GM.xmlHttpRequest
// @grant        GM_xmlhttpRequest
// @grant        GM_addStyle
// @grant        GM_download
// @grant        GM_setClipboard
// @grant        GM_setValue
// @grant        GM_getValue
// @connect      erome.com
// @connect      *.erome.com
// @connect      *
// @require      https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js
// @license      MIT
// ==/UserScript==

/* globals $ JSZip saveAs */

(function () {
    'use strict';

    /* ============================================================
     *  CONFIGURATION & STATE
     * ============================================================ */
    const CONFIG = {
        brand: 'Erome Ultimate',
        version: '7.3.6-hover-length-filter',
        accent: '#8a5acc',
        accentSoft: '#b39ad6',
        dark: '#14151f',
        darker: '#0e0f17',
        surface: '#1d1e2a',
        success: '#4ade80',
        danger: '#ef4444',
        warning: '#fbbf24'
    };

    /* === Embedded Erome Logo (base64 PNG) === */
    const LOGO_SPLASH = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAZAAAACcCAMAAABm1A0xAAAB/lBMVEWcHy2hV1ceFCBUICHc2uCcKE3PV2+WatIQEB2tkY7Ms9kPkpPgJGHbqKqHSzqhjnJgM1jcX4yOcpq3nd49QD3rxK1tU5nCCSZd196o9fclYmUp0tXWmHRUPoB+R8fmPYV7cF+FPrwFBQoRERsUEiN2dnKsCCNWVlM2NTS0hnDGyshJSEksKSZoaGlMOStxWEbS1tDMlnjn7OOMZ1JWRTe1uLaPhXOuemrSmYiUlpTlp47WpInl59tyZFOHiYgAAAHqt5Tv9O6OdFqnqKjFiXJpSTeOemcDAwUBAQMtIxuvlXgCAgSKWUnHxrm9wsJvUj2SBhgUDyCsdVpTR2mwpY+9wrsqGhOmAhvd4desmod5eYMGBgwEBAinalZFOlVwOS90Z41bWWZkWnHNua1IKhp0CBK6vMHToX07OUN9gYOIVsyxaWUGBgs+QUHhnIvUtZSdoaLuSZLo2NBeYWKbnKHGeXFxKCrJi4htTUFuWpGqh9ldYFlvFxl9gXusNVDV/f0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAS69gxAAAAgHRSTlP//+n//////6z//////////////////////////////////v7+//////////////////////////////////8F/////////43P//+x////////////////////US7///////////////////9w/////////////////////////+lpkHUAABqCSURBVHja7d2LX9NY9gBwanmsDr7H2dn375cQkzS5Sds0SUNJVvKaaadS7SBYdpGnCjK+38/913/n3PSRloIocX9lN0cUFHTmky/nnHtuHh37e2LxzyHxj6HxWyd+6o/f/fQ7jF/gjQZ+FMWpfTFzhGAPC2Yk47ex/kPyS1/8PDQS/5847LDNHC9OnMhvY6Pz/xI7UEnoHAwxyiIjCMIklywHpsboivw0NmIew7Mj4ao1wiKjAxJlxjCPmWPHgSIMm4Icnh/st/AY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';
    const LOGO_ICON   = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAMAAADVRocKAAAB/lBMVEWXHiq1mNve4ttgIxyXbNPQcJp1WZuDTzyhUk2fjXHoIVrDp+Day7MQEBs/QDyvK0OtqZvEYGjgn34fIB45LEVfUjxjT2KHY7bABCPCpq4MCxUREBwUEiNVVlQ3NzRISEjGycezhnByWEfNl3mtCCMsKSZnaGmKZ1LR19CuemlLOStTRDd1dXLn7ONwZFWNhnTRmYe0t7Xlp4+HiIjYpIlmSjjk5tuVl5Tqt5OnqaeOdFqNe2fr8uqvlHbEiHEvIxuMWkoUDiDHxrptUTypARurdlu0pI2SBRi9wsJ5eIMJCREICBB0aYusmIcqGxNDOlBlWnKmali9wrnd4dhQQ2c9OkVxOjA+QUFeYWLNuLIQEB2doaLa4+Pm2s59gYS5vMLIioVdYFvinYtcV2R3BxNsFxl0KCWal6S0Z2W1jIaUaNWrFy2rJzbHe2nXtI99gHuwQ1O1WWiqh9dFKBdvWZKWISuUbGOCeomzOETJnKXQoX3Mu8jjxLgkHS5HLSdtTUIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+vwaQAAAAgHRSTlP/////////////////v/////////////////7+/v///////////////////////////////////////////////////////////7nR///////////////////T/////////////////////////////////////////////////zEiVIkAAArlSURBVHja7Zn3X9tIGocNJJtsL9elqMwISVZBxbIQkpHtuGHvYlpCCwRISAKk92w2u5t//d4ZGWODDVifu/vpvsTYOPb76C3zzowm83NX3377y507d34hupOIBd04T+wZMaeVSZ7OfvLGxWIHaQigAzlFGo2RfHkogD1l/8Zl1Yu4AJDKfh9iAKEPkMp8L4I5S+gBnDgwNIMD1Uc4k4bMafsM/cbpnF1EGO5CZmCAzi0/Vu5XP+G0C5mTEhpwQSBioiTLPV/5C39K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';


    const DEFAULT_SETTINGS = {
        defaultFeedLayout: 'desktop',      // 'phone' | 'desktop'
        defaultFeedType: 'all',           // 'videos' | 'photos' | 'all'
        performanceMode: 'normal',        // 'lite' | 'normal' | 'aggressive'
        autoplay: true,
        muteVideos: false,
        loopVideos: true,
        autoResume: true,
        fitMode: 'contain',               // 'contain' | 'cover' | 'natural'
        showSeenBadges: true,
        hideSeen: false,
        hideDownloaded: false,
        skipDownloadedInDownloads: false,
        zipFolders: true,
        retryDownloads: true,
        showDownloadButtons: true,
        hiddenSeconds: 0,              // legacy min-length value
        minVideoSeconds: 0,             // 0 = off; hide videos shorter than this
        maxVideoSeconds: 0,             // 0 = off; hide videos longer than this
        sortMode: 'views',
        sortAscending: false,
        albumSearch: '',
        lockFeedToSearch: true,          // listing/search feeds stay inside the current result set/pagination
        allowRelatedAlbumExpansion: true,  // album-page feeds preload next/related albums by default
        strictSearchTitleMatch: false,   // optional extra filter: album card/title must contain a search word
        feedControlsAutoHide: true,      // hide the feed tools/search/shuffle row after a moment
        feedControlsPinned: false,       // keep feed tools visible until manually hidden
        lastFeed: null
    };

    const SETTINGS = Object.assign({}, DEFAULT_SETTINGS, loadJSON('eu_settings_v73', {}));

    const TRACKING = {
        seenAlbums: new Set(loadJSON('eu_seen_albums_v1', [])),
        downloadedMedia: new Set(loadJSON('eu_downloaded_media_v1', [])),
        downloadedAlbums: new Set(loadJSON('eu_downloaded_albums_v1', [])),
        hiddenAlbums: new Set(loadJSON('eu_hidden_albums_v1', [])),
        favorites: loadJSON('eu_favorites_v1', [])
    };

    const DLX = {
        running: false,
        cancel: false,
        done: 0,
        total: 0,
        ok: 0,
        failed: []
    };

    const STATE = {
        showPhotos: true,
        showVideos: true,
        showDownloadButtons: SETTINGS.showDownloadButtons !== false,
        cinemaMode: false,
        nsfwBlur: loadBool('eu_nsfw', false),
        hiddenSeconds: Math.max(0, Number(SETTINGS.minVideoSeconds || SETTINGS.hiddenSeconds) || 0),
        maxVideoSeconds: Math.max(0, Number(SETTINGS.maxVideoSeconds) || 0),
        videoOnlyMode: loadBool('eu_video_only', false),
        sortAscending: !!SETTINGS.sortAscending,
        sortMode: SETTINGS.sortMode || 'views', // 'views' | 'videos' | 'photos'
        videoDurations: new Map(),
        downloadQueue: [],
        downloadInProgress: false,
        tiktokMode: false,
        tiktokLayout: SETTINGS.defaultFeedLayout === 'desktop' ? 'desktop' : 'phone', // 'phone' | 'desktop'
        tiktokType: SETTINGS.defaultFeedType || 'videos', // 'videos' | 'photos' | 'all'
        tiktokIndex: 0
    };

    function loadBool(key, def) {
        try { return localStorage.getItem(key) === 'true' ? true : (localStorage.getItem(key) === 'false' ? false : def); }
        catch (e) { return def; }
    }
    function saveBool(key, val) {
        try { localStorage.setItem(key, val ? 'true' : 'false'); } catch (e) {}
    }
    function loadJSON(key, def) {
        try {
            const raw = localStorage.getItem(key);
            return raw ? JSON.parse(raw) : def;
        } catch (e) { return def; }
    }
    function saveJSON(key, value) {
        try { localStorage.setItem(key, JSON.stringify(value)); } catch (e) {}
    }
    function saveSettings() {
        saveJSON('eu_settings_v73', SETTINGS);
    }
    function updateSetting(key, value) {
        SETTINGS[key] = value;
        saveSettings();
    }

    // 7.3.3 migration: 7.3.1 saved album-page feeds as "Current album only".
    // Restore the old behavior one time so album pages preload the next/related album again.
    (function migrateV733NextAlbumDefault() {
        try {
            const key = 'eu_migrated_v733_album_next_restored';
            if (localStorage.getItem(key) !== '1') {
                SETTINGS.allowRelatedAlbumExpansion = true;
                saveSettings();
                localStorage.setItem(key, '1');
            }
        } catch (e) {
            SETTINGS.allowRelatedAlbumExpansion = true;
        }
    })();
    function saveLimitedSet(key, set, limit = 2500) {
        const arr = Array.from(set).filter(Boolean);
        saveJSON(key, arr.slice(Math.max(0, arr.length - limit)));
    }

    const IS_ALBUM_PAGE = /^\/a\//.test(window.location.pathname);

    /* ============================================================
     *  REDIRECT & POPUP BLOCKER
     * ============================================================ */
    const BLOCKED_PATTERNS = [
        'brightadnetwork.com',
        'pemsrv.com',
        '/jump/next.php',
        'splash.php'
    ];

    function isBlockedUrl(url) {
        return url && typeof url === 'string' && BLOCKED_PATTERNS.some(p => url.includes(p));
    }

    (function installRedirectBlocker() {
        const origOpen = window.open;
        window.open = function (url, ...rest) {
            if (isBlockedUrl(url)) { console.log('[EU] Blocked popup:', url); return null; }
            return origOpen.apply(this, [url, ...rest]);
        };

        document.addEventListener('click', function (e) {
            let t = e.target;
            while (t && t.tagName !== 'A' && t.tagName !== 'FORM') t = t.parentElement;
            if (t && t.tagName === 'A' && isBlockedUrl(t.href)) {
                console.log('[EU] Blocked click redirect:', t.href);
                e.preventDefault(); e.stopPropagation(); e.stopImmediatePropagation();
            }
        }, { capture: true, passive: false });

        const observer = new MutationObserver(muts => {
            muts.forEach(m => m.addedNodes.forEach(node => {
                if (node.tagName === 'A' && isBlockedUrl(node.href)) { node.href = '#'; node.onclick = e => { e.preventDefault(); return false; }; }
                if ((node.tagName === 'SCRIPT' || node.tagName === 'IFRAME') && isBlockedUrl(node.src)) node.remove();
            }));
        });
        observer.observe(document.documentElement, { childList: true, subtree: true });
    })();

    /* ============================================================
     *  STYLES — Modern Twitter-like UI
     * ============================================================ */
    GM_addStyle(`
    /* ---------- Global Theme ---------- */
    body {
        background-color: ${CONFIG.surface} !important;
        color: #fff;
    }
    a { color: ${CONFIG.accent}; }

    /* ---------- Navbar ---------- */
    .navbar-inverse { background-color: ${CONFIG.dark} !important; }
    .nav.navbar-nav.navbar-right li a {
        color: ${CONFIG.accent};
        transition: all .2s ease-in-out;
    }
    .nav.navbar-nav.navbar-right li a:hover {
        color: #fff;
        text-shadow: 0 0 10px rgba(138,90,204,.9);
        transform: scale(1.05);
    }

    /* ---------- Buttons ---------- */
    .btn-pink {
        color: #fff;
        background-color: rgba(138,90,204,.35);
        border-radius: 6px;
        border: 1px solid transparent;
        transition: all .25s ease;
    }
    .btn-pink:hover {
        background-color: transparent;
        border: 1px solid ${CONFIG.accent} !important;
        box-shadow: 0 0 20px 2px rgba(138,90,204,.5);
        color: #fff;
    }
    .btn-grey {
        color: #fff;
        border-radius: 6px;
        filter: drop-shadow(0 0 5px rgba(138,90,204,.6));
    }
    .btn-grey:hover {
        background: transparent !important;
        border: 1px solid ${CONFIG.accent} !important;
        box-shadow: 0 0 15px rgba(138,90,204,.5);
    }

    /* ---------- Album Cards ---------- */
    .album-thumbnail-container {
        transition: all .25s ease-in-out;
        position: relative;
        border-radius: 8px;
        overflow: hidden;
    }
    .album-thumbnail-container:hover {
        transform: scale(.96);
        box-shadow: 0 0 25px 4px rgba(138,90,204,.55);
    }
    .album .album-thumbnail {
        object-fit: cover;
        width: 100%;
        height: 100%;
    }

    /* ---------- Pagination ---------- */
    .pagination > li > a, .pagination > li > span {
        background-color: ${CONFIG.dark};
        color: #fff;
        border: none;
        border-radius: 6px;
        margin: 0 4px;
        transition: background-color .3s ease;
    }
    .pagination > li > a:hover, .pagination > .active > a,
    .pagination > .active > span {
        background-color: ${CONFIG.accent};
        color: #fff;
        box-shadow: 0 0 10px rgba(138,90,204,.7);
    }

    /* ---------- NSFW Blur ---------- */
    .eu-blur {
        filter: grayscale(100%) blur(10px) invert(100%) hue-rotate(90deg) contrast(170%);
        transition: filter .3s ease;
    }

    /* ---------- Keyframes ---------- */
    @keyframes eu-spin { to { transform: rotate(360deg); } }
    @keyframes eu-pulse { 0%,100% { transform: scale(1); } 50% { transform: scale(1.08); } }
    @keyframes eu-fade-in {
        from { opacity: 0; transform: translateY(10px); }
        to   { opacity: 1; transform: translateY(0); }
    }
    @keyframes eu-toast-in {
        0%   { opacity: 0; transform: translateX(100px); }
        100% { opacity: 1; transform: translateX(0); }
    }

    /* ---------- Download Button on Media ---------- */
    .eu-dl-btn {
        position: absolute;
        top: 10px; left: 10px;
        width: 40px; height: 40px;
        border-radius: 50%;
        background: ${CONFIG.dark};
        border: none;
        display: flex;
        align-items: center;
        justify-content: center;
        box-shadow: 0 0 0 3px rgba(138,90,204,.4);
        cursor: pointer;
        transition: all .3s ease;
        overflow: hidden;
        color: #fff;
        font-size: 13px;
        font-weight: 600;
        z-index: 9999;
        white-space: nowrap;
    }
    .eu-dl-btn svg { width: 16px; height: 16px; transition: margin-right .2s ease; }
    .eu-dl-btn:hover {
        width: 110px;
        border-radius: 24px;
        background: ${CONFIG.accent};
    }
    .eu-dl-btn:hover .eu-dl-label { display: inline; margin-left: 6px; }
    .eu-dl-btn .eu-dl-label { display: none; }
    .eu-dl-btn.downloading { background: ${CONFIG.accent}; width: 110px; border-radius: 24px; pointer-events: none; }
    .eu-dl-btn.downloading .eu-dl-label { display: inline; margin-left: 6px; }

    /* ---------- Album Badges ---------- */
    .eu-duration-badge {
        position: absolute;
        top: 8px; right: 8px;
        background: rgba(0,0,0,.82);
        color: #fff;
        padding: 3px 7px;
        border-radius: 4px;
        font-size: 11px;
        font-weight: 700;
        z-index: 15;
        backdrop-filter: blur(4px);
        border: 1px solid rgba(255,255,255,.08);
    }
    .eu-count-badge {
        position: absolute;
        top: 8px; left: 8px;
        background: rgba(0,0,0,.82);
        color: #fff;
        padding: 4px 8px;
        border-radius: 4px;
        font-size: 11px;
        font-weight: 700;
        z-index: 15;
        display: flex;
        gap: 8px;
        backdrop-filter: blur(4px);
        border: 1px solid rgba(255,255,255,.08);
    }
    .eu-likes-badge {
        color: ${CONFIG.accent};
        filter: drop-shadow(0 0 4px rgba(138,90,204,.8));
        margin-left: 6px;
        font-weight: 600;
    }

    /* ---------- Toast Notifications ---------- */
    .eu-toast-wrap {
        position: fixed;
        bottom: 24px; right: 24px;
        display: flex;
        flex-direction: column-reverse;
        gap: 10px;
        z-index: 100000;
        pointer-events: none;
    }
    .eu-toast {
        display: flex;
        align-items: center;
        gap: 10px;
        min-width: 240px;
        max-width: 360px;
        padding: 12px 16px;
        border-radius: 12px;
        background: rgba(20,21,31,.95);
        color: ${CONFIG.accentSoft};
        font-size: 13.5px;
        font-family: 'Segoe UI', Roboto, sans-serif;
        box-shadow: 0 8px 24px rgba(0,0,0,.4), 0 0 0 1px rgba(138,90,204,.25);
        backdrop-filter: blur(10px);
        animation: eu-toast-in .35s cubic-bezier(.22,1,.36,1);
        border-left: 3px solid ${CONFIG.accent};
        pointer-events: auto;
    }
    .eu-toast.success { border-left-color: ${CONFIG.success}; color: #e0ffef; }
    .eu-toast.error   { border-left-color: ${CONFIG.danger};  color: #ffe0e0; }
    .eu-toast.warning { border-left-color: ${CONFIG.warning}; color: #fff4d6; }
    .eu-toast svg { flex-shrink: 0; }

    /* ---------- Hub Menu (Twitter-like popup cards) ---------- */
    .eu-hub {
        position: relative;
        display: inline-block;
        margin-left: 10px;
        vertical-align: middle;
        margin-top: 15px;
    }
    .eu-hub-btn {
        display: flex;
        align-items: center;
        justify-content: center;
        padding: 9px;
        background: ${CONFIG.surface};
        border-radius: 10px;
        color: ${CONFIG.accentSoft};
        transition: all .3s ease;
        cursor: pointer;
        border: 1px solid transparent;
    }
    .eu-hub-btn:hover {
        border-color: ${CONFIG.accent};
        box-shadow: 0 0 15px rgba(138,90,204,.4);
        transform: translateY(-1px);
    }
    .eu-hub-btn svg { width: 18px; height: 18px; fill: #fff; }
    .eu-hub-menu {
        position: absolute;
        top: calc(100% + 10px);
        right: 0;
        background: ${CONFIG.dark};
        border-radius: 12px;
        box-shadow: 0 12px 32px rgba(0,0,0,.6), 0 0 0 1px rgba(138,90,204,.2);
        padding: 8px;
        list-style: none;
        display: none;
        min-width: 220px;
        z-index: 10000;
        backdrop-filter: blur(12px);
        animation: eu-fade-in .2s ease;
    }
    .eu-hub.open .eu-hub-menu { display: block; }
    .eu-hub-item {
        display: flex;
        align-items: center;
        gap: 10px;
        padding: 10px 12px;
        cursor: pointer;
        color: #fff;
        border-radius: 8px;
        font-size: 13px;
        font-weight: 600;
        transition: all .2s ease;
        white-space: nowrap;
    }
    .eu-hub-item:hover {
        background: rgba(138,90,204,.15);
        transform: translateX(3px);
    }
    .eu-hub-item svg { width: 16px; height: 16px; fill: ${CONFIG.accentSoft}; flex-shrink: 0; }
    .eu-hub-item.inactive { opacity: .45; }
    .eu-hub-item .eu-kbd {
        margin-left: auto;
        background: rgba(255,255,255,.08);
        padding: 2px 6px;
        border-radius: 4px;
        font-size: 10px;
        color: ${CONFIG.accentSoft};
    }

    /* ---------- Bulk Download Modal ---------- */
    .eu-modal-overlay {
        position: fixed;
        inset: 0;
        background: rgba(0,0,0,.75);
        backdrop-filter: blur(6px);
        z-index: 99998;
        display: none;
        align-items: center;
        justify-content: center;
        animation: eu-fade-in .2s ease;
    }
    .eu-modal-overlay.open { display: flex; }
    .eu-modal {
        background: ${CONFIG.dark};
        border-radius: 16px;
        width: 440px;
        max-width: 92vw;
        max-height: 90vh;
        overflow: hidden;
        box-shadow: 0 24px 64px rgba(0,0,0,.7), 0 0 0 1px rgba(138,90,204,.2);
        animation: eu-fade-in .25s cubic-bezier(.22,1,.36,1);
    }
    .eu-modal-header {
        padding: 18px 22px;
        background: linear-gradient(135deg, ${CONFIG.accent}, #6b3fb0);
        display: flex;
        align-items: center;
        justify-content: space-between;
    }
    .eu-modal-header h3 {
        margin: 0;
        color: #fff;
        font-size: 17px;
        font-weight: 700;
        display: flex;
        align-items: center;
        gap: 10px;
    }
    .eu-modal-close {
        background: rgba(255,255,255,.2);
        border: none;
        color: #fff;
        width: 30px; height: 30px;
        border-radius: 50%;
        cursor: pointer;
        font-size: 18px;
        display: flex;
        align-items: center;
        justify-content: center;
        transition: background .2s ease;
    }
    .eu-modal-close:hover { background: rgba(255,255,255,.35); }
    .eu-modal-body { padding: 22px; }

    .eu-option-group { margin-bottom: 18px; }
    .eu-option-title {
        color: ${CONFIG.accentSoft};
        font-weight: 700;
        font-size: 12px;
        text-transform: uppercase;
        letter-spacing: .8px;
        margin-bottom: 10px;
    }
    .eu-option {
        display: flex;
        align-items: center;
        gap: 10px;
        padding: 8px 0;
        color: #e4e4e8;
        cursor: pointer;
        transition: color .2s ease;
        font-size: 14px;
    }
    .eu-option:hover { color: ${CONFIG.accentSoft}; }
    .eu-option input { display: none; }
    .eu-radio, .eu-check {
        width: 18px; height: 18px;
        border: 2px solid #555;
        border-radius: 50%;
        position: relative;
        transition: all .25s ease;
        flex-shrink: 0;
    }
    .eu-check { border-radius: 4px; }
    .eu-option input:checked + .eu-radio,
    .eu-option input:checked + .eu-check {
        border-color: ${CONFIG.accent};
        background: ${CONFIG.accent};
    }
    .eu-option input:checked + .eu-radio::after {
        content: ''; position: absolute;
        top: 4px; left: 4px;
        width: 6px; height: 6px;
        background: #fff; border-radius: 50%;
    }
    .eu-option input:checked + .eu-check::after {
        content: '✓'; position: absolute;
        top: -2px; left: 2px;
        color: #fff; font-size: 13px; font-weight: bold;
    }
    .eu-zip-preview {
        font-size: 11px;
        color: #888;
        margin-top: 6px;
        padding: 8px 10px;
        background: ${CONFIG.darker};
        border-radius: 6px;
        word-break: break-all;
        font-family: monospace;
    }
    .eu-btn-primary {
        width: 100%;
        padding: 13px;
        background: linear-gradient(135deg, ${CONFIG.accent}, #a77fd4);
        color: #fff;
        border: none;
        border-radius: 10px;
        font-size: 15px;
        font-weight: 700;
        cursor: pointer;
        transition: all .25s ease;
        display: flex;
        align-items: center;
        justify-content: center;
        gap: 8px;
    }
    .eu-btn-primary:hover:not(:disabled) {
        transform: translateY(-2px);
        box-shadow: 0 8px 20px rgba(138,90,204,.5);
    }
    .eu-btn-primary:disabled { opacity: .55; cursor: not-allowed; }

    .eu-progress-box {
        margin-top: 14px;
        padding: 14px;
        background: ${CONFIG.darker};
        border-radius: 10px;
    }
    .eu-progress-bar {
        width: 100%; height: 6px;
        background: rgba(255,255,255,.08);
        border-radius: 3px;
        overflow: hidden;
        margin-bottom: 8px;
    }
    .eu-progress-fill {
        height: 100%;
        background: linear-gradient(90deg, ${CONFIG.accent}, ${CONFIG.accentSoft});
        width: 0;
        transition: width .25s ease;
    }
    .eu-progress-text {
        color: ${CONFIG.accentSoft};
        font-size: 12px;
        text-align: center;
        font-weight: 600;
    }

    /* ---------- Floating Action Button (FAB) ---------- */
    .eu-fab {
        position: fixed;
        bottom: 24px; right: 24px;
        width: 56px; height: 56px;
        border-radius: 50%;
        background: linear-gradient(135deg, ${CONFIG.accent}, #6b3fb0);
        border: none;
        cursor: pointer;
        z-index: 9999;
        display: flex;
        align-items: center;
        justify-content: center;
        color: #fff;
        box-shadow: 0 6px 18px rgba(138,90,204,.55);
        transition: all .3s ease;
    }
    .eu-fab:hover { transform: scale(1.08); box-shadow: 0 8px 26px rgba(138,90,204,.75); }
    .eu-fab svg { width: 24px; height: 24px; fill: #fff; }
    .eu-fab-badge {
        position: absolute;
        top: -4px; right: -4px;
        min-width: 22px; height: 22px;
        padding: 0 6px;
        background: ${CONFIG.danger};
        color: #fff;
        border-radius: 11px;
        font-size: 11px;
        font-weight: 700;
        display: flex;
        align-items: center;
        justify-content: center;
        border: 2px solid ${CONFIG.dark};
    }

    /* ---------- Hidden Slider (bottom-left) ---------- */
    .eu-hidden-slider {
        position: fixed;
        bottom: 24px; left: 24px;
        width: 44px; height: 44px;
        border-radius: 50%;
        background: ${CONFIG.dark};
        border: none;
        display: none;
        align-items: center;
        justify-content: center;
        box-shadow: 0 0 0 3px rgba(138,90,204,.35);
        cursor: pointer;
        transition: all .3s ease;
        z-index: 9999;
        color: #fff;
        font-weight: 700;
        font-size: 12px;
        overflow: hidden;
    }
    .eu-hidden-slider.visible { display: flex; }
    .eu-hidden-slider:hover {
        width: 110px;
        border-radius: 22px;
        background: ${CONFIG.accent};
    }
    .eu-hidden-slider svg { width: 20px; height: 20px; fill: #fff; }

    /* ---------- Video Player Custom Controls ---------- */
    .video-js .vjs-play-progress {
        background: ${CONFIG.accent} !important;
        box-shadow: 0 0 18px 4px rgba(138,90,204,.5);
    }
    .vjs-control:hover { background: rgba(138,90,204,.3); }
    .eu-player-btn {
        background: ${CONFIG.accent};
        color: #fff;
        border: none;
        border-radius: 5px;
        padding: 6px 10px;
        margin: 0 3px;
        cursor: pointer;
        display: flex;
        align-items: center;
        justify-content: center;
        transition: background .2s ease;
        font-weight: 600;
        font-size: 12px;
    }
    .eu-player-btn:hover { background: #a77fd4; }
    .eu-player-btn svg { width: 16px; height: 16px; fill: #fff; }

    /* ---------- Cinema Overlay ---------- */
    .eu-cinema-overlay {
        position: fixed; inset: 0;
        background: rgba(0,0,0,.92);
        z-index: 998;
        pointer-events: none;
        display: none;
    }
    body.eu-cinema .eu-cinema-overlay { display: block; }
    body.eu-cinema .video-js,
    body.eu-cinema .media-group img { z-index: 9999; position: relative; }

    /* ---------- Utility ---------- */
    .eu-hidden-video { display: none !important; }

    /* ---------- Video-Only Filter (no gaps!) ---------- */
    .eu-filtered-out {
        display: none !important;
        visibility: hidden !important;
        width: 0 !important;
        height: 0 !important;
        margin: 0 !important;
        padding: 0 !important;
        overflow: hidden !important;
        position: absolute !important;
        pointer-events: none !important;
    }
    /* Compact grid auto-flow so remaining items reflow with no empty slots */
    .eu-grid-compact {
        display: grid !important;
        grid-auto-flow: row dense !important;
    }
    /* Some Erome templates use flex-wrap — ensure items reflow */
    .user-albums.eu-grid-compact,
    .albums.eu-grid-compact {
        display: flex !important;
        flex-wrap: wrap !important;
        gap: 12px !important;
    }
    .eu-spinner {
        width: 14px; height: 14px;
        border: 2px solid rgba(255,255,255,.3);
        border-top-color: #fff;
        border-radius: 50%;
        animation: eu-spin .8s linear infinite;
        display: inline-block;
    }
    `);

    /* ============================================================
     *  ICONS (SVG)
     * ============================================================ */
    const ICONS = {
        download:  '<svg viewBox="0 0 24 24"><path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/></svg>',
        upload:    '<svg viewBox="0 0 384 512"><path d="M214.6 41.4c-12.5-12.5-32.8-12.5-45.3 0l-160 160c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L160 141.2V448c0 17.7 14.3 32 32 32s32-14.3 32-32V141.2L329.4 246.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3l-160-160z"/></svg>',
        settings:  '<svg viewBox="0 0 24 24"><path d="M19.14 12.94c.04-.3.06-.61.06-.94 0-.32-.02-.64-.07-.94l2.03-1.58c.18-.14.23-.41.12-.61l-1.92-3.32c-.12-.22-.37-.29-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.36-2.54c-.04-.24-.24-.41-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L2.74 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.05.3-.09.63-.09.94s.02.64.07.94l-2.03 1.58c-.18.14-.23.41-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .44-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.47-.12-.61l-2.01-1.58zM12 15.6c-1.98 0-3.6-1.62-3.6-3.6s1.62-3.6 3.6-3.6 3.6 1.62 3.6 3.6-1.62 3.6-3.6 3.6z"/></svg>',
        eye:       '<svg viewBox="0 0 24 24"><path d="M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z"/></svg>',
        eyeOff:    '<svg viewBox="0 0 24 24"><path d="M12 7c2.76 0 5 2.24 5 5 0 .65-.13 1.26-.36 1.82l2.92 2.92c1.51-1.39 2.59-3.26 3.1-5.34-1.71-4.38-5.98-7.5-11-7.5-1.4 0-2.74.25-3.98.7l2.16 2.16C10.74 7.13 11.35 7 12 7zM2 4.27l2.28 2.28.46.46C3.08 8.3 1.78 10.02 1.14 12c1.71 4.38 5.98 7.5 11 7.5 1.55 0 3.03-.3 4.38-.84l.42.42L19.73 22 21 20.73 3.27 3 2 4.27z"/></svg>',
        photo:     '<svg viewBox="0 0 24 24"><path d="M22 16V4c0-1.1-.9-2-2-2H8c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2zm-11-4l2.03 2.71L16 11l4 5H8l3-4zM2 6v14c0 1.1.9 2 2 2h14v-2H4V6H2z"/></svg>',
        video:     '<svg viewBox="0 0 24 24"><path d="M17 10.5V7c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h12c.55 0 1-.45 1-1v-3.5l4 4v-11l-4 4z"/></svg>',
        cinema:    '<svg viewBox="0 0 24 24"><path d="M18 4l2 4h-3l-2-4h-2l2 4h-3l-2-4H8l2 4H7L5 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V4h-4z"/></svg>',
        heart:     '<svg viewBox="0 0 24 24"><path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/></svg>',
        copy:      '<svg viewBox="0 0 24 24"><path d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/></svg>',
        zip:       '<svg viewBox="0 0 24 24"><path d="M14 2H6c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V8l-6-6zm-4 17h-2v-2h2v2zm0-4h-2v-2h2v2zm0-4h-2V9h2v2zm0-4h-2V5h2v2zm3 12h-2v-2h2v2zm0-4h-2v-2h2v2z"/></svg>',
        flip:      '<svg viewBox="0 0 16 16"><path d="M7 16V0H9V16H7Z"/><path d="M15 12H14L10 8L14 4H15L15 12Z"/><path d="M2 12H1L1 4H2L6 8L2 12Z"/></svg>',
        pip:       '<svg viewBox="0 0 24 24"><path d="M19 7h-8v6h8V7zm2-4H3c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H3V5h18v14z"/></svg>',
        clock:     '<svg viewBox="0 0 24 24"><path d="M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10 10-4.5 10-10S17.5 2 12 2zm4.2 14.2L11 13V7h1.5v5.2l4.5 2.7-.8 1.3z"/></svg>',
        sort:      '<svg viewBox="0 0 24 24"><path d="M3 18h6v-2H3v2zM3 6v2h18V6H3zm0 7h12v-2H3v2z"/></svg>',
        bell:      '<svg viewBox="0 0 24 24"><path d="M12 22c1.1 0 2-.9 2-2h-4c0 1.1.89 2 2 2zm6-6v-5c0-3.07-1.63-5.64-4.5-6.32V4c0-.83-.67-1.5-1.5-1.5s-1.5.67-1.5 1.5v.68C7.64 5.36 6 7.92 6 11v5l-2 2v1h16v-1l-2-2z"/></svg>',
        close:     '<svg viewBox="0 0 24 24"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>',
        check:     '<svg viewBox="0 0 24 24"><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/></svg>',
        star:      '<svg viewBox="0 0 24 24"><path d="M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z"/></svg>',
        starFill:  '<svg viewBox="0 0 24 24"><path d="M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z"/></svg>',
        share:     '<svg viewBox="0 0 24 24"><path d="M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7s-.04-.47-.09-.7l7.05-4.11c.54.5 1.25.81 2.04.81 1.66 0 3-1.34 3-3s-1.34-3-3-3-3 1.34-3 3c0 .24.04.47.09.7L8.04 9.81C7.5 9.31 6.79 9 6 9c-1.66 0-3 1.34-3 3s1.34 3 3 3c.79 0 1.5-.31 2.04-.81l7.12 4.16c-.05.21-.08.43-.08.65 0 1.61 1.31 2.92 2.92 2.92s2.92-1.31 2.92-2.92-1.31-2.92-2.92-2.92z"/></svg>',
        flag:      '<svg viewBox="0 0 24 24"><path d="M14.4 6L14 4H5v17h2v-7h5.6l.4 2h7V6z"/></svg>',
        comment:   '<svg viewBox="0 0 24 24"><path d="M21.99 4c0-1.1-.89-2-1.99-2H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h14l4 4-.01-18zM18 14H6v-2h12v2zm0-3H6V9h12v2zm0-3H6V6h12v2z"/></svg>',
        user:      '<svg viewBox="0 0 24 24"><path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/></svg>',
        music:     '<svg viewBox="0 0 24 24"><path d="M12 3v10.55c-.59-.34-1.27-.55-2-.55-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4V7h4V3h-6z"/></svg>'
    };

    /* ============================================================
     *  TOAST SYSTEM
     * ============================================================ */
    const toastWrap = document.createElement('div');
    toastWrap.className = 'eu-toast-wrap';
    (document.body || document.documentElement).appendChild(toastWrap);

    function toast(message, type = 'info', duration = 2600) {
        const t = document.createElement('div');
        t.className = `eu-toast ${type}`;
        const iconMap = { success: ICONS.check, error: ICONS.close, warning: ICONS.bell, info: ICONS.bell };
        t.innerHTML = `${iconMap[type] || ICONS.bell}<span>${message}</span>`;
        toastWrap.appendChild(t);
        setTimeout(() => {
            t.style.opacity = '0';
            t.style.transform = 'translateX(100px)';
            t.style.transition = 'all .3s ease';
            setTimeout(() => t.remove(), 350);
        }, duration);
    }

    /* ============================================================
     *  UTILITIES
     * ============================================================ */
    function formatFileSize(bytes) {
        if (!bytes) return '0 B';
        const units = ['B', 'KB', 'MB', 'GB'];
        const i = Math.floor(Math.log(bytes) / Math.log(1024));
        return (bytes / Math.pow(1024, i)).toFixed(2) + ' ' + units[i];
    }
    function formatDuration(ms) {
        let s = Math.floor(ms / 1000);
        const h = Math.floor(s / 3600); s %= 3600;
        const m = Math.floor(s / 60); s %= 60;
        return h > 0
            ? `${h}:${String(m).padStart(2, '0')}:${String(s).padStart(2, '0')}`
            : `${m}:${String(s).padStart(2, '0')}`;
    }
    function getFilename(url) {
        try { return url.split('?')[0].split('/').pop(); }
        catch (e) { return 'file'; }
    }
    function sanitize(name) {
        return (name || '').replace(/[<>:"/\\|?*]/g, '_').replace(/\s+/g, '_').substring(0, 60);
    }

    function normalizeUrl(url, base = location.href) {
        if (!url) return '';
        try {
            const u = new URL(url, base);
            u.hash = '';
            return u.href;
        } catch (e) { return url || ''; }
    }
    function getAlbumUrlFromElement(album, base = location.href) {
        const link = album?.querySelector?.('a.album-link, a[href*="/a/"]');
        const href = link?.getAttribute('href') || link?.href || '';
        return href ? normalizeUrl(href, base) : '';
    }
    function currentAlbumUrl() {
        return IS_ALBUM_PAGE ? normalizeUrl(location.href) : '';
    }
    function markSeenAlbum(url) {
        const clean = normalizeUrl(url || currentAlbumUrl());
        if (!clean) return;
        TRACKING.seenAlbums.add(clean);
        saveLimitedSet('eu_seen_albums_v1', TRACKING.seenAlbums);
    }
    function markDownloadedMedia(url, albumUrl = '') {
        const media = normalizeUrl(url);
        if (media) TRACKING.downloadedMedia.add(media);
        const album = normalizeUrl(albumUrl || currentAlbumUrl());
        if (album) TRACKING.downloadedAlbums.add(album);
        saveLimitedSet('eu_downloaded_media_v1', TRACKING.downloadedMedia, 5000);
        saveLimitedSet('eu_downloaded_albums_v1', TRACKING.downloadedAlbums, 2500);
    }
    function saveFavoriteItem(item) {
        if (!item || !item.url) return;
        const clean = normalizeUrl(item.url);
        const exists = TRACKING.favorites.some(f => f.url === clean);
        if (!exists) {
            TRACKING.favorites.unshift({
                url: clean,
                kind: item.kind || 'media',
                albumUrl: normalizeUrl(item.albumUrl || ''),
                title: item.title || 'Media',
                username: item.username || 'Erome',
                savedAt: new Date().toISOString()
            });
            TRACKING.favorites = TRACKING.favorites.slice(0, 1000);
            saveJSON('eu_favorites_v1', TRACKING.favorites);
        }
    }
    function exportTextFile(name, text) {
        const blob = new Blob([text], { type: 'text/plain;charset=utf-8' });
        const u = URL.createObjectURL(blob);
        const a = document.createElement('a');
        a.href = u; a.download = name; a.style.display = 'none';
        document.body.appendChild(a); a.click(); a.remove();
        setTimeout(() => URL.revokeObjectURL(u), 5000);
    }

    function gmRequest(opts) {
        const fn = (typeof GM !== 'undefined' && GM.xmlHttpRequest) ? GM.xmlHttpRequest
                 : (typeof GM_xmlhttpRequest !== 'undefined' ? GM_xmlhttpRequest : null);
        if (!fn) throw new Error('GM.xmlHttpRequest not available');
        return fn(opts);
    }

    /* ============================================================
     *  DOWNLOAD ENGINE
     * ============================================================ */
    function downloadBlob(url) {
        return new Promise((resolve, reject) => {
            gmRequest({
                method: 'GET',
                url,
                responseType: 'blob',
                headers: {
                    'User-Agent': 'Mozilla/5.0',
                    'Referer': 'https://www.erome.com/',
                    'Accept': '*/*'
                },
                timeout: 60000,
                onload: r => r.status === 200 ? resolve(r.response) : reject(`HTTP ${r.status}`),
                onerror: e => reject(e?.error || 'Network error'),
                ontimeout: () => reject('Timeout')
            });
        });
    }

    async function downloadBlobWithRetry(url, attempts = 2) {
        let lastErr;
        for (let i = 0; i < Math.max(1, attempts); i++) {
            try { return await downloadBlob(url); }
            catch (e) {
                lastErr = e;
                await new Promise(r => setTimeout(r, 500 + (i * 350)));
            }
        }
        throw lastErr;
    }

    async function downloadSingle(url, filename, btn) {
        const name = filename || getFilename(url);
        const originalHTML = btn ? btn.innerHTML : null;

        if (btn) {
            btn.classList.add('downloading');
            btn.innerHTML = `<span class="eu-spinner"></span><span class="eu-dl-label">Downloading</span>`;
        }

        try {
            const blob = await downloadBlob(url);
            const tmp = URL.createObjectURL(blob);
            const a = document.createElement('a');
            a.href = tmp; a.download = name; a.style.display = 'none';
            document.body.appendChild(a); a.click(); a.remove();
            setTimeout(() => URL.revokeObjectURL(tmp), 5000);
            markDownloadedMedia(url, arguments[3] || currentAlbumUrl());
            decorateTrackingBadges();
            toast(`Downloaded: ${name}`, 'success');
        } catch (err) {
            console.error('[EU] Download failed:', err);
            toast(`Download failed: ${err}`, 'error');
        } finally {
            if (btn) {
                btn.classList.remove('downloading');
                btn.innerHTML = originalHTML;
            }
        }
    }

    /* ============================================================
     *  INLINE DOWNLOAD BUTTONS ON MEDIA
     * ============================================================ */
    function buildDownloadButton(url) {
        const btn = document.createElement('button');
        btn.className = 'eu-dl-btn';
        btn.innerHTML = `${ICONS.upload}<span class="eu-dl-label">Download</span>`;
        btn.addEventListener('click', (e) => {
            e.preventDefault(); e.stopPropagation();
            downloadSingle(url, getFilename(url), btn);
        });
        return btn;
    }

    function attachDownloadButtons() {
        if (!STATE.showDownloadButtons) return;

        document.querySelectorAll('.media-group video, .media-group img').forEach(media => {
            const parent = media.parentElement?.parentElement;
            if (!parent || parent.querySelector('.eu-dl-btn')) return;

            let src;
            if (media.tagName === 'IMG') {
                src = media.src || media.getAttribute('data-src');
            } else {
                src = media.querySelector('source')?.src || media.src || media.currentSrc;
            }
            if (!src) return;

            parent.style.position = parent.style.position || 'relative';
            parent.appendChild(buildDownloadButton(src));
        });

        // Also handle album thumbnails on listing pages for image detection
        document.querySelectorAll('.album-image img').forEach(img => {
            const src = img.src || img.getAttribute('data-src');
            if (!src) return;
            const host = img.closest('.album-image');
            if (!host || host.querySelector('.eu-dl-btn')) return;
            host.style.position = host.style.position || 'relative';
            host.appendChild(buildDownloadButton(src));
        });
    }

    /* ============================================================
     *  VIDEO DURATION DETECTION (from file header)
     * ============================================================ */
    function detectVideoDuration(url) {
        return new Promise(resolve => {
            if (!url) return resolve(0);
            if (STATE.videoDurations.has(url)) return resolve(STATE.videoDurations.get(url));

            const cacheKey = 'eu_vl_' + url;
            const cached = localStorage.getItem(cacheKey);
            if (cached && !isNaN(parseInt(cached))) {
                const d = parseInt(cached);
                STATE.videoDurations.set(url, d);
                return resolve(d);
            }

            try {
                let req = gmRequest({
                    method: 'GET',
                    url,
                    headers: {
                        'Accept': 'video/webm,video/ogg,video/*;q=0.9,*/*;q=0.5',
                        'Referer': 'https://www.erome.com/',
                        'Range': 'bytes=0-500'
                    },
                    responseType: 'blob',
                    onprogress: r => { if (Math.max(r.loaded || 0, r.total || 0) > 600) req.abort?.(); },
                    onload: r => {
                        try {
                            const text = r.responseText || '';
                            const m = text.match(/\x03.*\xe8/);
                            if (m) {
                                const i = text.indexOf(m[0]) + m[0].length;
                                const s = text.substring(i, i + 4);
                                const ms = Array.from(s)
                                    .map(c => c.charCodeAt(0))
                                    .map((v, idx, arr) => v * Math.pow(256, arr.length - idx - 1))
                                    .reduce((a, b) => a + b, 0);
                                localStorage.setItem(cacheKey, ms);
                                STATE.videoDurations.set(url, ms);
                                return resolve(ms);
                            }
                        } catch (e) {}
                        resolve(0);
                    },
                    onerror: () => resolve(0),
                    ontimeout: () => resolve(0)
                });
            } catch (e) { resolve(0); }
        });
    }

    function decorateVideoDurations() {
        document.querySelectorAll('.media-group video, .video-js video').forEach(video => {
            const container = video.closest('.media-group') || video.closest('.video') || video.parentElement;
            if (!container || container.querySelector('.eu-duration-badge')) return;

            const src = video.querySelector('source')?.src || video.src || video.currentSrc;
            if (!src) return;

            detectVideoDuration(src).then(ms => {
                if (ms > 0 && !container.querySelector('.eu-duration-badge')) {
                    const badge = document.createElement('span');
                    badge.className = 'eu-duration-badge';
                    badge.textContent = formatDuration(ms);
                    container.style.position = container.style.position || 'relative';
                    container.appendChild(badge);
                    video.dataset.length = Math.floor(ms / 1000);
                    applyHiddenFilter();
                }
            });
        });
    }

    /* ============================================================
     *  ALBUM COUNT BADGES (on listing page)
     * ============================================================ */
    function decorateAlbumCounts() {
        document.querySelectorAll('.album').forEach(album => {
            if (album.querySelector('.eu-count-badge')) return;

            const imgCount = parseInt(album.querySelector('.album-images')?.textContent?.match(/\d+/)?.[0] || '0');
            const vidCount = parseInt(album.querySelector('.album-videos')?.textContent?.match(/\d+/)?.[0] || '0');
            if (imgCount === 0 && vidCount === 0) return;

            const badge = document.createElement('div');
            badge.className = 'eu-count-badge';
            const parts = [];
            if (imgCount > 0) parts.push(`<span>🖼️ ${imgCount}</span>`);
            if (vidCount > 0) parts.push(`<span>🎥 ${vidCount}</span>`);
            badge.innerHTML = parts.join('');

            const thumb = album.querySelector('.album-thumbnail-container');
            if (thumb) {
                thumb.style.position = thumb.style.position || 'relative';
                thumb.appendChild(badge);
            }
        });
    }


    function decorateTrackingBadges() {
        if (IS_ALBUM_PAGE) markSeenAlbum(location.href);

        document.querySelectorAll('.album').forEach(album => {
            const albumUrl = getAlbumUrlFromElement(album);
            if (!albumUrl) return;

            const thumb = album.querySelector('.album-thumbnail-container') || album;
            thumb.style.position = thumb.style.position || 'relative';

            const isSeen = TRACKING.seenAlbums.has(albumUrl);
            const isDownloaded = TRACKING.downloadedAlbums.has(albumUrl);

            if (SETTINGS.showSeenBadges && isSeen && !thumb.querySelector('.eu-tracking-badge.eu-seen')) {
                const b = document.createElement('div');
                b.className = 'eu-tracking-badge eu-seen';
                b.textContent = 'Seen';
                thumb.appendChild(b);
            }
            if (SETTINGS.showSeenBadges && isDownloaded && !thumb.querySelector('.eu-tracking-badge.eu-downloaded')) {
                const b = document.createElement('div');
                b.className = 'eu-tracking-badge eu-downloaded';
                b.textContent = 'Downloaded';
                thumb.appendChild(b);
            }

            album.classList.toggle('eu-track-filtered-out',
                (SETTINGS.hideSeen && isSeen) ||
                (SETTINGS.hideDownloaded && isDownloaded) ||
                TRACKING.hiddenAlbums.has(albumUrl)
            );

            const link = album.querySelector('a.album-link, a[href*="/a/"]');
            if (link && !link.dataset.euTrackSeen) {
                link.dataset.euTrackSeen = '1';
                link.addEventListener('click', () => markSeenAlbum(albumUrl), { capture: true });
            }
        });
    }

    function applyAlbumSearch() {
        const q = (SETTINGS.albumSearch || '').trim().toLowerCase();
        document.querySelectorAll('.album').forEach(album => {
            if (!q) { album.classList.remove('eu-search-filtered-out'); return; }
            const text = album.textContent.toLowerCase();
            const href = getAlbumUrlFromElement(album).toLowerCase();
            album.classList.toggle('eu-search-filtered-out', !(text.includes(q) || href.includes(q)));
        });
    }

    function openAlbumSearch() {
        const q = prompt('Search loaded albums by title, creator, or URL. Leave blank to clear.', SETTINGS.albumSearch || '');
        if (q === null) return;
        updateSetting('albumSearch', q.trim());
        applyAlbumSearch();
        toast(q.trim() ? `Search filter: ${q.trim()}` : 'Search filter cleared', 'info');
    }

    /* ============================================================
     *  LIKE COUNTS ON ALBUMS (listing page)
     * ============================================================ */
    async function loadAlbumLikes() {
        if (IS_ALBUM_PAGE || SETTINGS.performanceMode === 'lite') return;

        // Unlimited: process every album currently loaded, not just the first 30.
        // dataset.euLiked keeps this cheap on repeat calls; small pauses keep the UI responsive.
        const albums = Array.from(document.querySelectorAll('.album-link'));
        for (const a of albums) {
            if (a.dataset.euLiked) continue;
            a.dataset.euLiked = '1';
            try {
                const res = await fetch(a.href, { credentials: 'include' });
                const text = await res.text();
                const doc = new DOMParser().parseFromString(text, 'text/html');
                const likeArea = doc.querySelector('#like_count') || doc.querySelector('.far.fa-heart.fa-lg');
                const count = likeArea?.nextElementSibling?.firstChild?.textContent?.trim() || 0;
                if (+count < 1) continue;
                const viewSec = a.parentElement.querySelector('.album-bottom-right .album-images');
                if (!viewSec || viewSec.querySelector('.eu-likes-badge')) continue;
                viewSec.insertAdjacentHTML('afterbegin',
                    `<span class="eu-likes-badge">${ICONS.heart} ${count}</span>`);
            } catch (e) { /* silent */ }

            // Avoid hammering the browser/site when many albums are loaded.
            const likeDelay = SETTINGS.performanceMode === 'aggressive' ? 25 : 100;
            await new Promise(r => setTimeout(r, likeDelay));
        }
    }

    /* ============================================================
     *  SORT ALBUMS BY VIEWS
     * ============================================================ */
    function parseViews(text) {
        if (!text) return 0;
        const hasK = /k/i.test(text);
        const num = parseFloat(text.replace(/[^\d.,]/g, '').replace(',', '.'));
        return (isNaN(num) ? 0 : num) * (hasK ? 1000 : 1);
    }
    function parseCount(text) {
        if (!text) return 0;
        const n = parseInt(text.replace(/[^\d]/g, ''));
        return isNaN(n) ? 0 : n;
    }
    function getAlbumMetric(album, mode) {
        switch (mode) {
            case 'videos': return parseCount(album.querySelector('.album-videos')?.textContent);
            case 'photos': return parseCount(album.querySelector('.album-images')?.textContent);
            case 'views':
            default: return parseViews(album.querySelector('.album-bottom-views')?.textContent);
        }
    }

    function sortAlbums(mode) {
        if (mode) STATE.sortMode = mode;
        updateSetting('sortMode', STATE.sortMode);
        updateSetting('sortAscending', STATE.sortAscending);
        const container = document.querySelector('#albums') || document.querySelector('.page-content');
        if (!container) return;
        const albums = Array.from(container.querySelectorAll('.album'));
        if (albums.length < 2) return;

        albums.sort((a, b) => {
            const va = getAlbumMetric(a, STATE.sortMode);
            const vb = getAlbumMetric(b, STATE.sortMode);
            return STATE.sortAscending ? va - vb : vb - va;
        });
        albums.forEach(a => container.appendChild(a));

        const labels = { views: 'views', videos: 'video count', photos: 'photo count' };
        toast(`Sorted by ${labels[STATE.sortMode]} (${STATE.sortAscending ? 'ascending' : 'descending'})`, 'info');
    }
    // Backward-compatible alias
    function sortAlbumsByViews() { sortAlbums('views'); }

    /* ============================================================
     *  TOGGLES: photos / videos / downloads / NSFW / cinema / video-only
     * ============================================================ */
    function togglePhotos() {
        STATE.showPhotos = !STATE.showPhotos;
        document.querySelectorAll('.media-group img').forEach(el => {
            el.style.display = STATE.showPhotos ? '' : 'none';
        });
        toast(STATE.showPhotos ? 'Photos visible' : 'Photos hidden', 'info');
    }
    function toggleVideos() {
        STATE.showVideos = !STATE.showVideos;
        document.querySelectorAll('.video-js, .media-group video').forEach(el => {
            el.style.display = STATE.showVideos ? '' : 'none';
        });
        toast(STATE.showVideos ? 'Videos visible' : 'Videos hidden', 'info');
    }
    function toggleDownloadButtons() {
        STATE.showDownloadButtons = !STATE.showDownloadButtons;
        document.querySelectorAll('.eu-dl-btn').forEach(btn => {
            btn.style.display = STATE.showDownloadButtons ? '' : 'none';
        });
        updateSetting('showDownloadButtons', STATE.showDownloadButtons);
        toast(STATE.showDownloadButtons ? 'Download buttons visible' : 'Download buttons hidden', 'info');
    }
    function toggleNSFW() {
        STATE.nsfwBlur = !STATE.nsfwBlur;
        saveBool('eu_nsfw', STATE.nsfwBlur);
        applyNSFW();
        toast(STATE.nsfwBlur ? 'NSFW blur enabled' : 'NSFW blur disabled', 'info');
    }
    function applyNSFW() {
        const targets = document.querySelectorAll(
            '.album-thumbnail-container, .media-group img, .media-group video, .vjs-poster'
        );
        targets.forEach(el => el.classList.toggle('eu-blur', STATE.nsfwBlur));
    }
    function toggleCinema() {
        STATE.cinemaMode = !STATE.cinemaMode;
        document.body.classList.toggle('eu-cinema', STATE.cinemaMode);
        if (STATE.cinemaMode && !document.querySelector('.eu-cinema-overlay')) {
            const o = document.createElement('div');
            o.className = 'eu-cinema-overlay';
            document.body.appendChild(o);
        }
        toast(STATE.cinemaMode ? 'Cinema mode on' : 'Cinema mode off', 'info');
    }
    function toggleVideoOnly() {
        STATE.videoOnlyMode = !STATE.videoOnlyMode;
        saveBool('eu_video_only', STATE.videoOnlyMode);
        applyVideoOnly();
        toast(STATE.videoOnlyMode ? 'Showing video albums only' : 'Showing all albums', 'info');
    }
    function applyVideoOnly() {
        // Use a CSS class so layout reflows cleanly (no orphan grid rows / gaps)
        document.querySelectorAll('.album').forEach(a => {
            const hasVideo = !!a.querySelector('.album-videos');
            if (STATE.videoOnlyMode && !hasVideo) {
                a.classList.add('eu-filtered-out');
            } else {
                a.classList.remove('eu-filtered-out');
            }
        });

        // Also hide empty placeholder slots that some Erome templates leave behind
        document.querySelectorAll('.album-placeholder, .album-empty, .albums .placeholder').forEach(el => {
            if (STATE.videoOnlyMode) el.classList.add('eu-filtered-out');
            else el.classList.remove('eu-filtered-out');
        });

        // Force the parent grid/flex container to reflow by toggling a class
        const containers = document.querySelectorAll('.user-albums, .albums, .user-content, #albums');
        containers.forEach(c => {
            c.classList.toggle('eu-grid-compact', STATE.videoOnlyMode);
        });
    }

    /* ============================================================
     *  VIDEO LENGTH FILTERS
     * ============================================================ */
    function getMinVideoSeconds() {
        return Math.max(0, Number(SETTINGS.minVideoSeconds || SETTINGS.hiddenSeconds || 0) || 0);
    }

    function getMaxVideoSeconds() {
        return Math.max(0, Number(SETTINGS.maxVideoSeconds || 0) || 0);
    }

    function videoLengthAllowed(seconds) {
        const s = Number(seconds) || 0;
        if (!s || !isFinite(s)) return true; // unknown length is allowed until metadata loads
        const min = getMinVideoSeconds();
        const max = getMaxVideoSeconds();
        if (min > 0 && s < min) return false;
        if (max > 0 && s > max) return false;
        return true;
    }

    function getLengthFilterLabel() {
        const min = getMinVideoSeconds();
        const max = getMaxVideoSeconds();
        if (min > 0 && max > 0) return `Length ${min}-${max}s`;
        if (min > 0) return `Length ${min}s+`;
        if (max > 0) return `Length ≤${max}s`;
        return 'Length Any';
    }

    function explainLengthFilter(seconds) {
        const s = Number(seconds) || 0;
        const min = getMinVideoSeconds();
        const max = getMaxVideoSeconds();
        if (min > 0 && s > 0 && s < min) return `under ${min}s`;
        if (max > 0 && s > 0 && s > max) return `over ${max}s`;
        return '';
    }

    function refreshFeedIndexes() {
        document.querySelectorAll('#eu-tiktok-feed .eu-tiktok-item').forEach((card, idx) => {
            card.dataset.idx = idx;
        });
    }

    function filterOutFeedVideoByLength(item, card, seconds) {
        if (!item || !card || videoLengthAllowed(seconds)) return false;
        const reason = explainLengthFilter(seconds);
        const oldIndex = Number(card.dataset.idx) || 0;
        TT.items = TT.items.filter(x => x.url !== item.url);
        card.remove();
        refreshFeedIndexes();
        STATE.tiktokIndex = Math.min(STATE.tiktokIndex, Math.max(0, TT.items.length - 1));
        updateTikTokCounter();
        updateFeedStatus();

        const cards = document.querySelectorAll('#eu-tiktok-feed .eu-tiktok-item');
        if (cards.length) {
            cards[Math.min(oldIndex, cards.length - 1)]?.scrollIntoView({ behavior: 'auto', block: 'start' });
        }
        if (TT.items.length < 5 || STATE.tiktokIndex >= TT.items.length - 3) {
            setTimeout(() => preloadNextAlbum(), 160);
        }
        if (reason) console.log(`[EU] Length-filtered feed video (${reason}):`, item.url);
        return true;
    }

    function pruneKnownFeedVideosByLength() {
        const before = TT.items.length;
        TT.items = TT.items.filter(item => item.kind !== 'video' || !item.duration || videoLengthAllowed(item.duration));
        if (TT.items.length !== before) {
            rebuildFeedFromItems(Math.min(STATE.tiktokIndex, Math.max(0, TT.items.length - 1)));
            if (TT.items.length < 5) setTimeout(() => preloadNextAlbum(), 160);
        }
    }

    function promptLengthFilter() {
        const minNow = getMinVideoSeconds();
        const maxNow = getMaxVideoSeconds();
        const minRaw = prompt('Minimum video length in seconds. Use 0 for no minimum.', String(minNow));
        if (minRaw === null) return;
        const maxRaw = prompt('Maximum video length in seconds. Use 0 for no maximum.', String(maxNow));
        if (maxRaw === null) return;
        const min = Math.max(0, Math.floor(Number(minRaw) || 0));
        const max = Math.max(0, Math.floor(Number(maxRaw) || 0));
        if (min > 0 && max > 0 && min > max) {
            toast('Min length cannot be higher than max length', 'error');
            return;
        }
        updateSetting('minVideoSeconds', min);
        updateSetting('hiddenSeconds', min); // keep the older bottom-left album filter in sync
        updateSetting('maxVideoSeconds', max);
        STATE.hiddenSeconds = min;
        STATE.maxVideoSeconds = max;
        applyHiddenFilter();
        pruneKnownFeedVideosByLength();
        updateFeedTools();
        toast(`${getLengthFilterLabel()} filter saved`, 'success');
    }

    /* ============================================================
     *  HIDDEN BY DURATION (bottom-left slider)
     * ============================================================ */
    function applyHiddenFilter() {
        const minSeconds = getMinVideoSeconds();
        const maxSeconds = getMaxVideoSeconds();
        STATE.hiddenSeconds = minSeconds;
        STATE.maxVideoSeconds = maxSeconds;
        document.querySelectorAll('.video').forEach(vc => {
            const video = vc.querySelector('.video-js video, video');
            if (!video || !video.dataset.length) { vc.classList.remove('eu-hidden-video'); return; }
            const len = parseInt(video.dataset.length, 10);
            if ((minSeconds > 0 && len < minSeconds) || (maxSeconds > 0 && len > maxSeconds)) {
                vc.classList.add('eu-hidden-video');
                vc.title = `Hidden by length filter: ${getLengthFilterLabel()}`;
            } else {
                vc.classList.remove('eu-hidden-video');
                if (vc.title && vc.title.startsWith('Hidden by length filter')) vc.title = '';
            }
        });
    }

    function createHiddenSlider() {
        if (!IS_ALBUM_PAGE) return;
        if (document.querySelector('.eu-hidden-slider')) return;

        const btn = document.createElement('button');
        btn.className = 'eu-hidden-slider';
        btn.dataset.count = String(getMinVideoSeconds() || 0);
        btn.innerHTML = ICONS.clock;
        btn.title = 'Min video length: hide videos shorter than this many seconds';

        btn.addEventListener('mouseenter', () => {
            const n = parseInt(btn.dataset.count);
            btn.innerHTML = n > 0 ? `${n}s` : 'OFF';
        });
        btn.addEventListener('mouseleave', () => { btn.innerHTML = ICONS.clock; });
        btn.addEventListener('click', () => {
            let n = parseInt(btn.dataset.count);
            n = (n + 5) % 105;
            if (n >= 100) n = 0;
            btn.dataset.count = n;
            STATE.hiddenSeconds = n;
            updateSetting('minVideoSeconds', n);
            updateSetting('hiddenSeconds', n);
            btn.innerHTML = n > 0 ? `${n}s` : 'OFF';
            applyHiddenFilter();
            toast(n > 0 ? `Minimum video length: ${n}s` : 'Minimum length filter off', 'info');
        });

        document.body.appendChild(btn);
    }

    /* ============================================================
     *  VIDEO.JS PLAYER CUSTOM CONTROLS
     * ============================================================ */
    const SPEEDS = [0.5, 1, 1.5, 2, 3, 5];

    function enhancePlayers() {
        if (typeof window.videojs === 'undefined') {
            // Wait for video.js to load
            setTimeout(enhancePlayers, 500);
            return;
        }

        document.querySelectorAll('.video-js').forEach(el => {
            if (el.dataset.euEnhanced) return;
            el.dataset.euEnhanced = '1';

            let player;
            try { player = window.videojs(el); }
            catch (e) { return; }

            player.ready(() => {
                const bar = player.controlBar?.el?.();
                if (!bar) return;

                // Speed
                let speedIdx = 1;
                const speedBtn = document.createElement('button');
                speedBtn.className = 'eu-player-btn';
                speedBtn.textContent = '1x';
                speedBtn.title = 'Playback speed';
                speedBtn.onclick = () => {
                    speedIdx = (speedIdx + 1) % SPEEDS.length;
                    player.playbackRate(SPEEDS[speedIdx]);
                    speedBtn.textContent = SPEEDS[speedIdx] + 'x';
                    toast(`Speed: ${SPEEDS[speedIdx]}x`, 'info', 1500);
                };

                // Download
                const dlBtn = document.createElement('button');
                dlBtn.className = 'eu-player-btn';
                dlBtn.innerHTML = ICONS.download;
                dlBtn.title = 'Download video';
                dlBtn.onclick = () => {
                    const src = player.currentSrc();
                    if (src) downloadSingle(src, getFilename(src));
                };

                // Flip
                let flipped = false;
                const flipBtn = document.createElement('button');
                flipBtn.className = 'eu-player-btn';
                flipBtn.innerHTML = ICONS.flip;
                flipBtn.title = 'Flip video';
                flipBtn.onclick = () => {
                    const v = el.querySelector('video');
                    flipped = !flipped;
                    if (v) v.style.transform = flipped ? 'scaleX(-1)' : '';
                    toast('Video flipped', 'info', 1500);
                };

                // Cinema
                const cineBtn = document.createElement('button');
                cineBtn.className = 'eu-player-btn';
                cineBtn.innerHTML = ICONS.cinema;
                cineBtn.title = 'Cinema mode';
                cineBtn.onclick = toggleCinema;

                // PiP
                const pipBtn = document.createElement('button');
                pipBtn.className = 'eu-player-btn';
                pipBtn.innerHTML = ICONS.pip;
                pipBtn.title = 'Picture-in-Picture';
                pipBtn.onclick = async () => {
                    try {
                        const v = el.querySelector('video');
                        if (document.pictureInPictureElement) { await document.exitPictureInPicture(); toast('PiP off', 'info', 1500); }
                        else if (v) { await v.requestPictureInPicture(); toast('PiP on', 'info', 1500); }
                    } catch (e) { toast('PiP not supported', 'error'); }
                };

                bar.appendChild(speedBtn);
                bar.appendChild(dlBtn);
                bar.appendChild(flipBtn);
                bar.appendChild(cineBtn);
                bar.appendChild(pipBtn);
            });
        });
    }

    /* ============================================================
     *  BULK DOWNLOAD MODAL
     * ============================================================ */
    function collectAllMediaUrls() {
        const urls = new Set();

        document.querySelectorAll('.media-group img, img.media, .album-image img').forEach(img => {
            const src = img.src || img.getAttribute('data-src') || img.getAttribute('data-original');
            if (src && !src.includes('thumb') && !src.includes('logo') && !src.includes('avatar')) {
                try { urls.add(new URL(src, location.href).href); } catch (e) {}
            }
        });

        document.querySelectorAll('.media-group video, .video-js video, video, video source').forEach(v => {
            const src = v.src || v.getAttribute('src') || v.currentSrc;
            if (src) { try { urls.add(new URL(src, location.href).href); } catch (e) {} }
        });

        return Array.from(urls);
    }

    function isImageUrl(u) { return /\.(jpe?g|png|gif|webp|bmp)(\?|$)/i.test(u); }
    function isVideoUrl(u) { return /\.(mp4|webm|mov|avi|m3u8)(\?|$)/i.test(u); }

    function getPageTitle() {
        const el = document.querySelector('h1, .album-title');
        let title = el?.textContent?.trim() || document.title || 'Erome_Album';
        return sanitize(title) || 'Erome_Album';
    }

    function generateZipName() {
        const stamp = new Date().toISOString().replace(/[:.]/g, '-').replace('T', '_').substring(0, 16);
        return `Erome_${stamp}_${getPageTitle()}.zip`;
    }

    function buildBulkModal() {
        if (document.getElementById('eu-bulk-modal')) return;

        const overlay = document.createElement('div');
        overlay.id = 'eu-bulk-modal';
        overlay.className = 'eu-modal-overlay';
        overlay.innerHTML = `
          <div class="eu-modal">
            <div class="eu-modal-header">
              <h3>${ICONS.download}<span>Bulk Download</span></h3>
              <button class="eu-modal-close">${ICONS.close}</button>
            </div>
            <div class="eu-modal-body">
              <div class="eu-option-group">
                <div class="eu-option-title">Media Type</div>
                <label class="eu-option"><input type="radio" name="eu-type" value="all" checked><span class="eu-radio"></span>All files</label>
                <label class="eu-option"><input type="radio" name="eu-type" value="images"><span class="eu-radio"></span>Images only</label>
                <label class="eu-option"><input type="radio" name="eu-type" value="videos"><span class="eu-radio"></span>Videos only</label>
              </div>
              <div class="eu-option-group">
                <div class="eu-option-title">Output</div>
                <label class="eu-option"><input type="checkbox" id="eu-zip" checked><span class="eu-check"></span>Package as ZIP</label>
                <label class="eu-option"><input type="checkbox" id="eu-zip-folders" ${SETTINGS.zipFolders ? 'checked' : ''}><span class="eu-check"></span>Folder ZIP by album/title</label>
                <label class="eu-option"><input type="checkbox" id="eu-skip-downloaded" ${SETTINGS.skipDownloadedInDownloads ? 'checked' : ''}><span class="eu-check"></span>Skip already downloaded media</label>
                <div class="eu-zip-preview" id="eu-zip-name"></div>
              </div>
              <div class="eu-option-group">
                <div class="eu-option-title">Stats</div>
                <div class="eu-zip-preview" id="eu-stats">Scanning…</div>
              </div>
              <button class="eu-btn-primary" id="eu-start-dl">${ICONS.download}<span>Start Download</span></button>
              <button class="eu-btn-secondary" id="eu-open-dlx" style="margin-top:10px;">Open Smart Download Manager</button>
              <div class="eu-progress-box">
                <div class="eu-progress-bar"><div class="eu-progress-fill" id="eu-progress-fill"></div></div>
                <div class="eu-progress-text" id="eu-progress-text">Ready</div>
              </div>
            </div>
          </div>`;
        document.body.appendChild(overlay);

        overlay.querySelector('.eu-modal-close').onclick = () => overlay.classList.remove('open');
        overlay.addEventListener('click', e => { if (e.target === overlay) overlay.classList.remove('open'); });

        const refreshPreview = () => {
            document.getElementById('eu-zip-name').textContent = generateZipName();
            const urls = collectAllMediaUrls();
            const imgs = urls.filter(isImageUrl).length;
            const vids = urls.filter(isVideoUrl).length;
            document.getElementById('eu-stats').textContent = `Found ${urls.length} items (${imgs} images, ${vids} videos)`;
        };
        overlay.querySelectorAll('input[name="eu-type"]').forEach(r => r.addEventListener('change', refreshPreview));
        document.getElementById('eu-zip').addEventListener('change', refreshPreview);

        document.getElementById('eu-start-dl').onclick = () => bulkDownload(overlay);
        document.getElementById('eu-open-dlx').onclick = () => { overlay.classList.remove('open'); openDownloadManager(); };
        document.getElementById('eu-zip-folders').addEventListener('change', e => updateSetting('zipFolders', e.target.checked));
        document.getElementById('eu-skip-downloaded').addEventListener('change', e => updateSetting('skipDownloadedInDownloads', e.target.checked));

        // Refresh preview whenever opened
        overlay.dataset.refresh = '1';
        overlay.addEventListener('transitionstart', refreshPreview);
        overlay._refreshPreview = refreshPreview;
    }

    function openBulkModal() {
        buildBulkModal();
        const m = document.getElementById('eu-bulk-modal');
        m.classList.add('open');
        m._refreshPreview?.();
    }

    async function bulkDownload(overlay) {
        if (STATE.downloadInProgress) { toast('A download is already running', 'warning'); return; }

        const type = overlay.querySelector('input[name="eu-type"]:checked').value;
        const asZip = overlay.querySelector('#eu-zip').checked;
        const zipFolders = overlay.querySelector('#eu-zip-folders')?.checked ?? SETTINGS.zipFolders;
        const skipDownloaded = overlay.querySelector('#eu-skip-downloaded')?.checked ?? SETTINGS.skipDownloadedInDownloads;
        updateSetting('zipFolders', zipFolders);
        updateSetting('skipDownloadedInDownloads', skipDownloaded);
        const btn = overlay.querySelector('#eu-start-dl');
        const fill = overlay.querySelector('#eu-progress-fill');
        const text = overlay.querySelector('#eu-progress-text');

        let urls = collectAllMediaUrls();
        if (type === 'images') urls = urls.filter(isImageUrl);
        if (type === 'videos') urls = urls.filter(isVideoUrl);
        if (skipDownloaded) urls = urls.filter(u => !TRACKING.downloadedMedia.has(normalizeUrl(u)));

        if (urls.length === 0) { toast('No media found', 'error'); return; }

        STATE.downloadInProgress = true;
        btn.disabled = true;
        btn.innerHTML = `<span class="eu-spinner"></span><span>Working…</span>`;
        fill.style.width = '0%';
        text.textContent = `0 / ${urls.length}`;

        try {
            if (asZip && typeof JSZip !== 'undefined') {
                const zip = new JSZip();
                let ok = 0;
                for (let i = 0; i < urls.length; i++) {
                    const url = urls[i];
                    const baseName = `${String(i + 1).padStart(3, '0')}_${getFilename(url)}`;
                    const name = zipFolders ? `${getPageTitle()}/${baseName}` : baseName;
                    text.textContent = `Downloading ${i + 1}/${urls.length}: ${baseName.substring(0, 30)}`;
                    fill.style.width = `${(i / urls.length) * 100}%`;
                    try {
                        const blob = await downloadBlobWithRetry(url, SETTINGS.retryDownloads ? 2 : 1);
                        zip.file(name, await blob.arrayBuffer());
                        markDownloadedMedia(url, currentAlbumUrl());
                        ok++;
                    } catch (e) {
                        zip.file(`ERROR_${i + 1}.txt`, `URL: ${url}\nError: ${e}`);
                    }
                }
                text.textContent = 'Generating ZIP…';
                const zipBlob = await zip.generateAsync(
                    { type: 'blob', compression: 'DEFLATE', compressionOptions: { level: 6 } },
                    meta => { text.textContent = `Compressing: ${Math.round(meta.percent)}%`; fill.style.width = `${meta.percent}%`; }
                );
                if (typeof saveAs === 'function') saveAs(zipBlob, generateZipName());
                else {
                    const a = document.createElement('a');
                    a.href = URL.createObjectURL(zipBlob);
                    a.download = generateZipName();
                    a.click();
                }
                fill.style.width = '100%';
                text.textContent = `Complete (${ok}/${urls.length})`;
                toast(`ZIP saved: ${ok}/${urls.length} files`, 'success');
            } else {
                let ok = 0;
                for (let i = 0; i < urls.length; i++) {
                    const url = urls[i];
                    const name = `${String(i + 1).padStart(3, '0')}_${getFilename(url)}`;
                    text.textContent = `Downloading ${i + 1}/${urls.length}`;
                    fill.style.width = `${(i / urls.length) * 100}%`;
                    try {
                        const blob = await downloadBlobWithRetry(url, SETTINGS.retryDownloads ? 2 : 1);
                        const u = URL.createObjectURL(blob);
                        const a = document.createElement('a');
                        a.href = u; a.download = name; a.style.display = 'none';
                        document.body.appendChild(a); a.click(); a.remove();
                        setTimeout(() => URL.revokeObjectURL(u), 5000);
                        markDownloadedMedia(url, currentAlbumUrl());
                        ok++;
                        await new Promise(r => setTimeout(r, SETTINGS.performanceMode === 'aggressive' ? 150 : 400));
                    } catch (e) { console.error(e); }
                }
                fill.style.width = '100%';
                text.textContent = `Complete (${ok}/${urls.length})`;
                decorateTrackingBadges();
                toast(`Downloaded ${ok}/${urls.length} files`, 'success');
            }
        } catch (err) {
            console.error(err);
            toast('Bulk download error', 'error');
        } finally {
            STATE.downloadInProgress = false;
            btn.disabled = false;
            btn.innerHTML = `${ICONS.download}<span>Start Download</span>`;
        }
    }

    /* ============================================================
     *  TIKTOK-STYLE VERTICAL SCROLL MODE — Phone-proportioned
     * ============================================================ */
    GM_addStyle(`
    .eu-tiktok-overlay {
        position: fixed; inset: 0;
        background: radial-gradient(circle at 50% 30%, #1a1a2e 0%, #000 70%);
        z-index: 99999;
        display: none;
        align-items: center;
        justify-content: center;
        animation: eu-fade-in .3s ease;
    }
    .eu-tiktok-overlay.open { display: flex; }

    /* TikTok layout - simple phone centered */

    /* Phone-shaped container (9:16 aspect ratio, like a real phone) */
    .eu-phone {
        position: relative;
        width: min(420px, 92vw);
        height: min(calc(420px * 16 / 9), 94vh);
        aspect-ratio: 9 / 16;
        background: #000;
        border-radius: 28px;
        overflow: hidden;
        box-shadow:
            0 0 0 8px #111,
            0 0 0 9px #2a2a2a,
            0 30px 80px rgba(138, 90, 204, .25),
            0 60px 120px rgba(0, 0, 0, .7);
        display: flex;
        flex-direction: column;
    }
    /* Phone notch */
    .eu-phone::before {
        content: '';
        position: absolute;
        top: 8px;
        left: 50%;
        transform: translateX(-50%);
        width: 110px;
        height: 22px;
        background: #0a0a0a;
        border-radius: 14px;
        z-index: 100;
    }
    @media (max-width: 520px) {
        .eu-phone {
            width: 100vw;
            height: 100vh;
            border-radius: 0;
            box-shadow: none;
            aspect-ratio: auto;
        }
        .eu-phone::before { display: none; }
    }

    /* Desktop Feed layout: same one-card scroll as mobile, but wide/fullscreen. */
    .eu-tiktok-overlay.eu-desktop-mode {
        align-items: stretch;
        justify-content: center;
        padding: 16px;
        background: radial-gradient(circle at 50% 20%, #24213a 0%, #090910 72%);
    }
    .eu-tiktok-overlay.eu-desktop-mode .eu-phone {
        width: min(1280px, calc(100vw - 32px));
        height: calc(100vh - 32px);
        aspect-ratio: auto;
        border-radius: 18px;
        box-shadow:
            0 0 0 1px rgba(138,90,204,.35),
            0 24px 90px rgba(0,0,0,.75),
            0 0 70px rgba(138,90,204,.22);
    }
    .eu-tiktok-overlay.eu-desktop-mode .eu-phone::before { display: none; }
    .eu-tiktok-overlay.eu-desktop-mode .eu-tiktok-topbar {
        height: 74px;
        padding-top: 18px;
        padding-left: 24px;
        padding-right: 24px;
    }
    .eu-tiktok-overlay.eu-desktop-mode .eu-tiktok-title { font-size: 16px; }
    .eu-tiktok-overlay.eu-desktop-mode .eu-tiktok-counter {
        top: 82px;
        left: 24px;
        font-size: 12px;
    }
    .eu-tiktok-overlay.eu-desktop-mode .eu-tiktok-item,
    .eu-tiktok-overlay.eu-desktop-mode .eu-tiktok-feed {
        height: 100%;
    }
    .eu-tiktok-overlay.eu-desktop-mode .eu-tiktok-media,
    .eu-tiktok-overlay.eu-desktop-mode .eu-tiktok-item video,
    .eu-tiktok-overlay.eu-desktop-mode .eu-tiktok-item img {
        object-fit: contain;
        background: #000;
    }
    .eu-tiktok-overlay.eu-desktop-mode .eu-tiktok-sidebar {
        right: 24px;
        bottom: 34px;
        gap: 12px;
    }
    .eu-tiktok-overlay.eu-desktop-mode .eu-tiktok-caption {
        left: 24px;
        right: 120px;
        bottom: 30px;
        margin-left: -24px;
        margin-right: -120px;
        padding-left: 24px;
        padding-right: 120px;
    }
    .eu-tiktok-overlay.eu-desktop-mode .eu-tiktok-caption .eu-tiktok-desc {
        font-size: 13px;
        max-width: 760px;
    }
    .eu-tiktok-overlay.eu-desktop-mode .eu-tiktok-duration-live {
        left: 24px;
        bottom: 96px;
    }
    .eu-tiktok-layout-toggle {
        background: rgba(138,90,204,.45);
        color: #fff;
        border: 1px solid rgba(255,255,255,.18);
        padding: 5px 10px;
        border-radius: 14px;
        font-size: 11px;
        font-weight: 700;
        cursor: pointer;
        backdrop-filter: blur(6px);
        transition: all .2s ease;
    }
    .eu-tiktok-layout-toggle:hover { background: rgba(138,90,204,.75); }

    .eu-tiktok-topbar {
        position: absolute;
        top: 0; left: 0; right: 0;
        padding-top: 36px;
        height: 86px;
        background: linear-gradient(180deg, rgba(0,0,0,.9), transparent);
        display: flex;
        align-items: flex-start;
        justify-content: space-between;
        padding-left: 14px;
        padding-right: 14px;
        z-index: 20;
        pointer-events: none;
    }
    .eu-tiktok-topbar > * { pointer-events: auto; }
    .eu-tiktok-title {
        color: #fff;
        font-weight: 700;
        font-size: 14px;
        display: flex;
        align-items: center;
        gap: 8px;
    }
    .eu-tiktok-title span {
        border-bottom: 2px solid ${CONFIG.accent};
        padding-bottom: 2px;
    }
    .eu-tiktok-title svg { width: 16px; height: 16px; fill: #fff; }
    .eu-tiktok-brand-logo {
        height: 22px; width: auto;
        filter: drop-shadow(0 0 6px rgba(138,90,204,0.7));
        border-radius: 4px;
    }

    /* === Splash screen (shown when TikTok mode opens) === */
    .eu-tiktok-splash {
        position: absolute; inset: 0;
        display: flex; flex-direction: column;
        align-items: center; justify-content: center;
        background: linear-gradient(135deg, #0e0f17 0%, #14151f 50%, #2a1f3d 100%);
        z-index: 100;
        gap: 18px;
        animation: eu-splash-fade 1.8s ease forwards;
        animation-delay: 0.8s;
        pointer-events: none;
    }
    .eu-tiktok-splash-logo {
        max-width: 76%; width: 360px; height: auto;
        filter: drop-shadow(0 0 40px rgba(138,90,204,0.7));
        animation: eu-splash-pop .7s cubic-bezier(.2,.9,.3,1.2);
    }
    .eu-tiktok-splash-tag {
        color: #b39ad6; font-size: 13px; font-weight: 700;
        letter-spacing: 4px; text-transform: uppercase;
        animation: eu-splash-pop .9s cubic-bezier(.2,.9,.3,1.2);
    }
    .eu-tiktok-splash-dots {
        display: flex; gap: 8px;
    }
    .eu-tiktok-splash-dots span {
        width: 8px; height: 8px; border-radius: 50%;
        background: #8a5acc;
        animation: eu-splash-dot 1.2s ease-in-out infinite;
    }
    .eu-tiktok-splash-dots span:nth-child(2) { animation-delay: .2s; }
    .eu-tiktok-splash-dots span:nth-child(3) { animation-delay: .4s; }
    @keyframes eu-splash-pop {
        0% { opacity: 0; transform: scale(.6); }
        100% { opacity: 1; transform: scale(1); }
    }
    @keyframes eu-splash-dot {
        0%, 100% { opacity: 0.3; transform: translateY(0); }
        50%      { opacity: 1;   transform: translateY(-6px); }
    }
    @keyframes eu-splash-fade {
        0%   { opacity: 1; }
        85%  { opacity: 1; }
        100% { opacity: 0; visibility: hidden; pointer-events: none; }
    }
    .eu-tiktok-controls {
        display: flex;
        gap: 6px;
        align-items: center;
        flex-wrap: wrap;
        justify-content: flex-end;
    }
    .eu-tiktok-tab {
        background: rgba(255,255,255,.12);
        color: #fff;
        border: none;
        padding: 5px 10px;
        border-radius: 14px;
        font-size: 11px;
        font-weight: 600;
        cursor: pointer;
        transition: all .2s ease;
        backdrop-filter: blur(6px);
    }
    .eu-tiktok-tab:hover { background: rgba(255,255,255,.22); }
    .eu-tiktok-tab.active {
        background: ${CONFIG.accent};
        box-shadow: 0 0 12px rgba(138,90,204,.6);
    }
    .eu-tiktok-close {
        background: rgba(0,0,0,.65);
        color: #fff;
        border: none;
        width: 32px; height: 32px;
        border-radius: 50%;
        cursor: pointer;
        display: flex;
        align-items: center;
        justify-content: center;
        transition: background .2s ease;
        backdrop-filter: blur(6px);
    }
    .eu-tiktok-close:hover { background: rgba(255,255,255,.2); }
    .eu-tiktok-close svg { width: 16px; height: 16px; fill: #fff; }

    .eu-tiktok-feed {
        flex: 1;
        overflow-y: scroll;
        scroll-snap-type: y mandatory;
        scrollbar-width: none;
        -ms-overflow-style: none;
        scroll-behavior: smooth;
        -webkit-overflow-scrolling: touch;
    }
    .eu-tiktok-feed::-webkit-scrollbar { display: none; }

    .eu-tiktok-item {
        position: relative;
        width: 100%;
        height: 100%;
        min-height: 100%;
        scroll-snap-align: start;
        scroll-snap-stop: always;
        display: flex;
        align-items: center;
        justify-content: center;
        background: #000;
        overflow: hidden;
    }
    .eu-tiktok-media,
    .eu-tiktok-item video,
    .eu-tiktok-item img {
        width: 100%;
        height: 100%;
        object-fit: cover;
        max-width: 100%;
        max-height: 100%;
        background: #000;
        display: block;
    }
    .eu-tiktok-item::after {
        content: '';
        position: absolute;
        inset: 0;
        pointer-events: none;
        box-shadow: inset 0 -140px 160px -40px rgba(0,0,0,.55), inset 0 70px 80px -40px rgba(0,0,0,.35);
    }

    /* Right sidebar — TikTok style */
    .eu-tiktok-sidebar {
        position: absolute;
        right: 10px;
        bottom: 24px;
        display: flex;
        flex-direction: column;
        align-items: center;
        gap: 10px;
        z-index: 5;
        max-height: calc(100% - 48px);
    }
    .eu-tiktok-user-link {
        color: #fff !important;
        text-decoration: none;
    }
    .eu-tiktok-action {
        display: flex;
        flex-direction: column;
        align-items: center;
        gap: 3px;
        color: #fff;
        cursor: pointer;
        transition: transform .15s ease;
    }
    .eu-tiktok-action:hover { transform: scale(1.12); }
    .eu-tiktok-action:active { transform: scale(.92); }
    .eu-tiktok-action .eu-tiktok-icon {
        width: 40px; height: 40px;
        border-radius: 50%;
        background: rgba(0,0,0,.55);
        backdrop-filter: blur(8px);
        display: flex;
        align-items: center;
        justify-content: center;
        border: 1px solid rgba(255,255,255,.12);
        transition: all .2s ease;
    }
    .eu-tiktok-action .eu-tiktok-icon svg { width: 20px; height: 20px; fill: #fff; }
    .eu-tiktok-action.liked .eu-tiktok-icon { background: #ef4444; animation: eu-pulse .35s ease; }
    .eu-tiktok-action .eu-tiktok-lbl {
        font-size: 10px;
        font-weight: 700;
        text-shadow: 0 0 6px rgba(0,0,0,.95);
    }

    /* Bottom caption */
    .eu-tiktok-caption {
        position: absolute;
        left: 14px;
        right: 80px;
        bottom: 22px;
        color: #fff;
        z-index: 4;
        padding-bottom: 12px;
        background: linear-gradient(to top, rgba(0,0,0,0.6) 0%, rgba(0,0,0,0) 100%);
        padding-top: 40px; padding-left: 0; padding-right: 0;
        margin-left: -14px; margin-right: -80px;
        padding-left: 14px; padding-right: 80px;
    }
    .eu-tiktok-caption .eu-tiktok-user {
        font-weight: 700;
        font-size: 14px;
        margin-bottom: 4px;
        text-shadow: 0 0 8px rgba(0,0,0,.95);
    }
    .eu-tiktok-caption .eu-tiktok-desc {
        font-size: 12px;
        line-height: 1.35;
        opacity: .92;
        text-shadow: 0 0 8px rgba(0,0,0,.95);
        max-height: 54px;
        overflow: hidden;
        display: -webkit-box;
        -webkit-line-clamp: 3;
        -webkit-box-orient: vertical;
    }
    .eu-tiktok-caption .eu-tiktok-album {
        display: inline-block;
        margin-top: 6px;
        font-size: 11px;
        padding: 3px 8px;
        background: rgba(138,90,204,.4);
        border-radius: 8px;
        backdrop-filter: blur(6px);
        color: #fff;
    }

    .eu-tiktok-counter {
        position: absolute;
        top: 95px;
        left: 14px;
        background: rgba(0,0,0,.55);
        color: #fff;
        padding: 4px 10px;
        border-radius: 10px;
        font-size: 11px;
        font-weight: 700;
        z-index: 5;
        backdrop-filter: blur(6px);
    }

    .eu-tiktok-play-hint {
        position: absolute;
        width: 64px; height: 64px;
        background: rgba(0,0,0,.65);
        border-radius: 50%;
        display: flex;
        align-items: center;
        justify-content: center;
        pointer-events: none;
        opacity: 0;
        transition: opacity .2s ease;
        top: 50%; left: 50%;
        transform: translate(-50%, -50%);
    }
    .eu-tiktok-play-hint.show { opacity: 1; }
    .eu-tiktok-play-hint svg { width: 32px; height: 32px; fill: #fff; }

    /* Video progress bar (at top of item) */
    .eu-tiktok-progress {
        position: absolute;
        bottom: 0; left: 0; right: 0;
        height: 3px;
        background: rgba(255,255,255,.12);
        z-index: 6;
    }
    .eu-tiktok-progress-fill {
        height: 100%;
        background: #fff;
        width: 0;
        transition: width .1s linear;
    }

    /* === Erome-native buttons === */
    .eu-tiktok-top {
        position: absolute; top: 14px; left: 14px; right: 70px;
        display: flex; align-items: center; gap: 8px;
        z-index: 10; pointer-events: none;
    }
    .eu-tiktok-views, .eu-tiktok-kind-pill {
        display: inline-flex; align-items: center; gap: 6px;
        background: rgba(0,0,0,0.55); backdrop-filter: blur(8px);
        color: #fff; font-size: 11px; font-weight: 600;
        padding: 5px 10px; border-radius: 20px;
        border: 1px solid rgba(255,255,255,0.1);
    }
    .eu-tiktok-views svg, .eu-tiktok-kind-pill svg { width: 12px; height: 12px; fill: #fff; }
    .eu-tiktok-duration-pill {
        display: inline-flex;
        align-items: center;
        gap: 4px;
        background: rgba(255, 0, 80, 0.85);
        color: #fff;
        font-size: 12px;
        font-weight: 700;
        padding: 5px 10px;
        border-radius: 14px;
        border: 1px solid rgba(255, 255, 255, 0.2);
        backdrop-filter: blur(6px);
        font-family: 'Courier New', monospace;
    }
    .eu-tiktok-duration-live {
        position: absolute;
        bottom: 80px;
        left: 14px;
        background: rgba(0, 0, 0, 0.85);
        color: #fff;
        padding: 5px 11px;
        border-radius: 8px;
        font-size: 13px;
        font-weight: 700;
        font-family: 'Courier New', monospace;
        z-index: 6;
        border: 1px solid rgba(255, 255, 255, 0.15);
        pointer-events: none;
    }

    /* Avatar button (profile + follow plus sign) */
    .eu-tiktok-avatar {
        position: relative; width: 52px; height: 52px;
        border-radius: 50%; overflow: visible;
        border: 2px solid #fff; margin-bottom: 6px;
        display: block; flex-shrink: 0;
        transition: transform .2s ease;
    }
    .eu-tiktok-avatar:hover { transform: scale(1.08); }
    .eu-tiktok-avatar img {
        width: 100%; height: 100%; border-radius: 50%;
        object-fit: cover; background: #8a5acc;
        display: block;
    }
    .eu-tiktok-avatar-plus {
        position: absolute; bottom: -7px; left: 50%;
        transform: translateX(-50%);
        width: 20px; height: 20px; border-radius: 50%;
        background: #ff0050; color: #fff;
        font-size: 14px; font-weight: 700;
        display: flex; align-items: center; justify-content: center;
        line-height: 1; border: 2px solid transparent;
    }

    /* Action button tweaks for Erome-native look */
    .eu-tiktok-action.faved .eu-tiktok-icon { background: #f59e0b; animation: eu-pulse .35s ease; }
    .eu-tiktok-action.faved .eu-tiktok-icon svg { fill: #fff; }
    .eu-tiktok-action-mini .eu-tiktok-icon {
        width: 36px !important; height: 36px !important;
        background: rgba(0,0,0,.55) !important;
    }
    .eu-tiktok-action-mini .eu-tiktok-icon svg { width: 16px !important; height: 16px !important; }
    .eu-tiktok-count {
        font-variant-numeric: tabular-nums;
        min-width: 20px; text-align: center;
    }

    /* Spinning music disc (TikTok-style) */
    .eu-tiktok-disc {
        width: 48px; height: 48px; border-radius: 50%;
        overflow: hidden; border: 6px solid #111;
        background: #000; flex-shrink: 0;
        animation: eu-spin 6s linear infinite;
        box-shadow: 0 2px 10px rgba(0,0,0,0.6);
        margin-top: 4px;
    }
    .eu-tiktok-disc img { width: 100%; height: 100%; object-fit: cover; }
    @keyframes eu-spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }

    /* Bottom-left scrolling ticker (music row) */
    .eu-tiktok-ticker {
        display: flex; align-items: center; gap: 6px;
        color: #fff; font-size: 12px; opacity: .85;
        margin-top: 6px;
        white-space: nowrap; overflow: hidden;
    }
    .eu-tiktok-ticker svg { width: 13px; height: 13px; fill: #fff; flex-shrink: 0; }
    .eu-tiktok-ticker span {
        display: inline-block; animation: eu-ticker 12s linear infinite;
        padding-left: 0;
    }
    @keyframes eu-ticker {
        0%   { transform: translateX(0); }
        50%  { transform: translateX(0); }
        100% { transform: translateX(-50%); }
    }

    /* "View Album on Erome" button — more prominent */
    .eu-tiktok-caption .eu-tiktok-album {
        display: inline-flex !important; align-items: center; gap: 6px;
        margin-top: 6px;
        background: rgba(138, 90, 204, 0.25);
        border: 1px solid rgba(138, 90, 204, 0.5);
        padding: 6px 12px; border-radius: 20px;
        font-size: 12px; font-weight: 600;
        text-decoration: none; color: #fff !important;
        transition: all .2s ease;
    }
    .eu-tiktok-caption .eu-tiktok-album:hover {
        background: rgba(138, 90, 204, 0.5);
        transform: translateY(-1px);
    }
    .eu-tiktok-caption .eu-tiktok-album svg { width: 12px; height: 12px; fill: #fff; }

    /* Loading spinner */
    .eu-tiktok-loading {
        color: #fff;
        text-align: center;
        padding: 24px;
        font-size: 13px;
        opacity: .7;
        display: flex;
        align-items: center;
        justify-content: center;
        gap: 10px;
    }
    .eu-tiktok-empty {
        color: #aaa;
        font-size: 13px;
        text-align: center;
        padding: 40px 20px;
        width: 100%;
        height: 100%;
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
    }

    /* Phone hint */
    .eu-tiktok-hint {
        position: absolute;
        bottom: 20px;
        left: 50%;
        transform: translateX(-50%);
        color: rgba(255,255,255,.5);
        font-size: 11px;
        text-align: center;
        z-index: 2;
        pointer-events: none;
        animation: eu-hint-bounce 2s ease infinite;
    }
    @keyframes eu-hint-bounce {
        0%, 100% { transform: translate(-50%, 0); opacity: .5; }
        50% { transform: translate(-50%, -6px); opacity: 1; }
    }

    /* Double-tap heart animation */
    .eu-tiktok-heart-pop {
        position: absolute;
        top: 50%; left: 50%;
        transform: translate(-50%, -50%) scale(0);
        font-size: 120px;
        pointer-events: none;
        opacity: 0;
        z-index: 50;
    }
    .eu-tiktok-heart-pop.animate {
        animation: eu-heart-pop .8s ease;
    }
    @keyframes eu-heart-pop {
        0% { transform: translate(-50%, -50%) scale(0); opacity: 0; }
        30% { transform: translate(-50%, -50%) scale(1.2); opacity: 1; }
        70% { transform: translate(-50%, -50%) scale(1); opacity: 1; }
    }
    `);


    GM_addStyle(`
    /* ---------- Smart Premium v7.3 additions ---------- */
    .eu-tracking-badge {
        position: absolute;
        right: 8px;
        bottom: 8px;
        padding: 4px 8px;
        border-radius: 999px;
        font-size: 10px;
        font-weight: 800;
        letter-spacing: .2px;
        background: rgba(0,0,0,.84);
        color: #fff;
        border: 1px solid rgba(255,255,255,.12);
        z-index: 18;
        backdrop-filter: blur(8px);
        pointer-events: none;
    }
    .eu-tracking-badge.eu-downloaded { bottom: 34px; color: #d1fae5; border-color: rgba(74,222,128,.45); }
    .eu-track-filtered-out, .eu-search-filtered-out { display: none !important; }
    .eu-smart-row { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-bottom: 12px; }
    .eu-smart-row.full { grid-template-columns: 1fr; }
    .eu-smart-field label { display: block; color: ${CONFIG.accentSoft}; font-size: 11px; font-weight: 800; text-transform: uppercase; letter-spacing: .6px; margin-bottom: 6px; }
    .eu-smart-field select, .eu-smart-field input[type="number"], .eu-smart-field input[type="text"] {
        width: 100%; padding: 10px; border-radius: 9px; border: 1px solid rgba(255,255,255,.1);
        background: ${CONFIG.darker}; color: #fff; outline: none;
    }
    .eu-smart-checks { display: grid; grid-template-columns: 1fr 1fr; gap: 8px 12px; margin-top: 6px; }
    .eu-smart-check { display: flex; align-items: center; gap: 8px; color: #eee; font-size: 13px; cursor: pointer; user-select: none; }
    .eu-smart-check input { accent-color: ${CONFIG.accent}; }
    .eu-smart-actions { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-top: 14px; }
    .eu-btn-secondary {
        width: 100%; padding: 12px; border-radius: 10px; border: 1px solid rgba(255,255,255,.12);
        background: ${CONFIG.darker}; color: #fff; cursor: pointer; font-weight: 800;
    }
    .eu-btn-secondary:hover { border-color: ${CONFIG.accent}; box-shadow: 0 0 15px rgba(138,90,204,.25); }
    .eu-status-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-bottom: 14px; }
    .eu-status-card { background: ${CONFIG.darker}; border-radius: 12px; padding: 12px; border: 1px solid rgba(255,255,255,.08); }
    .eu-status-card b { display: block; font-size: 20px; color: #fff; margin-bottom: 2px; }
    .eu-status-card span { color: ${CONFIG.accentSoft}; font-size: 11px; font-weight: 700; text-transform: uppercase; }
    .eu-feed-tools {
        position: absolute; top: 58px; left: 50%; transform: translateX(-50%);
        display: flex; gap: 7px; z-index: 35; flex-wrap: wrap; justify-content: center;
        width: min(760px, calc(100vw - 24px)); pointer-events: none;
        opacity: 1; visibility: visible;
        transition: opacity .22s ease, transform .22s ease, visibility .22s ease;
    }
    .eu-feed-tools button, .eu-feed-tools .eu-feed-chip {
        pointer-events: auto; border: 1px solid rgba(255,255,255,.12); color: #fff;
        background: rgba(20,21,31,.72); backdrop-filter: blur(10px); border-radius: 999px;
        font-size: 11px; font-weight: 800; padding: 7px 10px; cursor: pointer;
        box-shadow: 0 4px 16px rgba(0,0,0,.28);
    }
    .eu-feed-tools button:hover { border-color: ${CONFIG.accent}; background: rgba(138,90,204,.35); }
    .eu-feed-tools button.eu-active { border-color: ${CONFIG.accent}; background: rgba(138,90,204,.42); box-shadow: 0 0 18px rgba(138,90,204,.32); }
    .eu-feed-status {
        position: absolute; top: 98px; left: 50%; transform: translateX(-50%);
        color: ${CONFIG.accentSoft}; font-size: 11px; font-weight: 800; z-index: 34;
        background: rgba(0,0,0,.48); border: 1px solid rgba(255,255,255,.08); border-radius: 999px;
        padding: 5px 10px; backdrop-filter: blur(10px); pointer-events: none;
        opacity: 1; visibility: visible;
        transition: opacity .22s ease, transform .22s ease, visibility .22s ease;
    }
    .eu-tiktok-topbar,
    .eu-tiktok-tab,
    .eu-tiktok-layout-toggle,
    .eu-tiktok-title span,
    .eu-tiktok-close {
        transition: opacity .22s ease, transform .22s ease, background .22s ease, visibility .22s ease;
    }
    .eu-phone.eu-feed-controls-hidden .eu-feed-tools,
    .eu-phone.eu-feed-controls-hidden .eu-feed-status {
        opacity: 0; visibility: hidden; pointer-events: none;
        transform: translate(-50%, -12px);
    }
    .eu-phone.eu-feed-controls-hidden .eu-tiktok-tab,
    .eu-phone.eu-feed-controls-hidden .eu-tiktok-layout-toggle {
        opacity: 0; visibility: hidden; pointer-events: none;
        transform: translateY(-10px);
    }
    .eu-phone.eu-feed-controls-hidden .eu-tiktok-title span {
        opacity: 0; visibility: hidden; transform: translateY(-6px);
    }
    .eu-phone.eu-feed-controls-hidden .eu-tiktok-brand-logo {
        opacity: .95;
        box-shadow: 0 0 16px rgba(138,90,204,.45);
    }
    .eu-phone.eu-feed-controls-hidden .eu-tiktok-topbar {
        background: linear-gradient(180deg, rgba(0,0,0,.24), transparent);
        pointer-events: none;
    }
    .eu-phone.eu-feed-controls-hidden .eu-tiktok-close,
    .eu-phone.eu-feed-controls-hidden .eu-feed-tools-toggle {
        visibility: visible; pointer-events: auto;
    }
    .eu-phone.eu-feed-controls-hidden .eu-tiktok-close { opacity: .72; }
    .eu-phone.eu-feed-controls-hidden .eu-tiktok-close:hover { opacity: 1; }
    .eu-feed-tools-toggle {
        position: absolute; top: 104px; right: 14px; z-index: 36;
        display: inline-flex; align-items: center; gap: 7px;
        height: 34px; padding: 5px 10px 5px 6px;
        border-radius: 999px; border: 1px solid rgba(255,255,255,.14);
        color: #fff; background: rgba(20,21,31,.72); backdrop-filter: blur(12px);
        box-shadow: 0 8px 26px rgba(0,0,0,.32), 0 0 20px rgba(138,90,204,.18);
        font-size: 11px; font-weight: 900; cursor: pointer;
        opacity: .72; transition: opacity .2s ease, transform .2s ease, background .2s ease;
    }
    .eu-feed-tools-toggle:hover { opacity: 1; transform: scale(1.04); background: rgba(138,90,204,.36); }
    .eu-feed-tools-toggle img { width: 22px; height: 22px; border-radius: 50%; object-fit: cover; box-shadow: 0 0 10px rgba(138,90,204,.55); }
    .eu-feed-tools-toggle span { max-width: 48px; overflow: hidden; transition: max-width .18s ease, opacity .18s ease; }
    .eu-phone.eu-feed-controls-hidden .eu-feed-tools-toggle span { max-width: 0; opacity: 0; }
    .eu-phone.eu-feed-controls-hidden .eu-feed-tools-toggle:hover span { max-width: 48px; opacity: 1; }
    .eu-phone:not(.eu-feed-controls-hidden) .eu-feed-tools-toggle { opacity: .46; }
    .eu-desktop-mode .eu-feed-tools { top: 68px; }
    .eu-desktop-mode .eu-feed-status { top: 108px; }
    .eu-desktop-mode .eu-feed-tools-toggle { top: 118px; right: 24px; }
    .eu-fit-contain .eu-tiktok-media { object-fit: contain !important; }
    .eu-fit-cover .eu-tiktok-media { object-fit: cover !important; }
    .eu-fit-natural .eu-tiktok-media { object-fit: scale-down !important; max-width: 100%; max-height: 100%; }
    .eu-feed-badge {
        position: absolute; left: 18px; top: 88px; z-index: 12; border-radius: 999px;
        background: rgba(0,0,0,.58); color: #fff; border: 1px solid rgba(255,255,255,.12);
        padding: 5px 9px; font-size: 11px; font-weight: 900; backdrop-filter: blur(8px);
    }
    .eu-feed-badge.eu-feed-downloaded { top: 118px; color: #d1fae5; border-color: rgba(74,222,128,.45); }
    .eu-dlx-log {
        max-height: 160px; overflow: auto; margin-top: 12px; padding: 10px; background: ${CONFIG.darker};
        color: #ddd; border-radius: 10px; font: 11px/1.45 monospace; white-space: pre-wrap;
    }
    `);

    /* --- TikTok State --- */
    const TT = {
        items: [],          // All collected media items (flattened)
        seenUrls: new Set(), // Dedupe set
        albumQueue: [],     // Album URLs queued for preload
        albumSeen: new Set(), // Already preloaded album URLs
        loadingMore: false,
        listingNextPage: 2,  // Listing pages queued in the background for desktop feed
        listingLoading: false,
        listingStopped: false,
        listingEmptyHits: 0,
        listingSeenPages: new Set(),
        feedScopeMode: 'album',
        feedScopeLabel: '',
        feedScopeUrl: '',
        feedScopeTerms: [],
        io: null,           // IntersectionObserver
        currentAlbum: null
    };

    function extractMediaFromDoc(doc, type, albumMeta = {}) {
        const results = [];
        const add = (kind, url) => {
            if (!url || TT.seenUrls.has(url)) return;
            if (url.includes('thumb') || url.includes('avatar') || url.includes('logo')) return;
            TT.seenUrls.add(url);
            results.push({ kind, url, ...albumMeta });
        };

        if (type === 'videos' || type === 'all') {
            const addVideoSrc = (src) => {
                if (!src) return;
                src = String(src).trim().replace(/\\\//g, '/');
                if (src.startsWith('//')) src = 'https:' + src;
                if (/\.(mp4|webm|m3u8)(?:[?#]|$)/i.test(src)) add('video', src);
            };
            doc.querySelectorAll('.media-group video, .video-js video, video').forEach(v => {
                addVideoSrc(v.currentSrc || v.querySelector('source')?.src || v.src || v.getAttribute('src') || v.getAttribute('data-src') || v.dataset?.src);
            });
            doc.querySelectorAll('.media-group source, video source, source').forEach(s => {
                addVideoSrc(s.src || s.getAttribute('src') || s.getAttribute('data-src') || s.dataset?.src);
            });
            doc.querySelectorAll('a[href]').forEach(a => addVideoSrc(a.getAttribute('href')));
            const html = doc.documentElement?.innerHTML || '';
            html.match(/https?:\/\/[^"'<>\\\s]+?\.(?:mp4|webm|m3u8)(?:\?[^"'<>\\\s]*)?/gi)?.forEach(addVideoSrc);
        }

        if (type === 'photos' || type === 'all') {
            doc.querySelectorAll('.media-group img, .album-image img, img.img-front').forEach(img => {
                const src = img.src || img.getAttribute('data-src') || img.getAttribute('src');
                if (src) add('image', src.startsWith('//') ? 'https:' + src : src);
            });
        }

        return results;
    }

    function getAlbumMetaFromDoc(doc, fallbackUrl) {
        const userEl = doc.getElementById('user_name') || doc.querySelector('.username');
        const titleEl = doc.querySelector('.page-content h1, h1');

        // Try to pull like/favorite/view counts from Erome's native DOM
        const grabCount = (selector) => {
            const el = doc.querySelector(selector);
            if (!el) return '';
            const m = (el.textContent || '').match(/[\d,.]+[kKmM]?/);
            return m ? m[0] : '';
        };

        return {
            username: userEl?.textContent?.replace(/\s+/g, ' ').trim() || 'Erome',
            title: titleEl?.textContent?.trim() || 'Album',
            albumUrl: fallbackUrl || location.href,
            likeCount: grabCount('#like_count') || grabCount('.likes-count') || grabCount('.fa-heart + span') || '',
            favCount: grabCount('#favorites_count') || grabCount('.favorites-count') || grabCount('.fa-star + span') || '',
            viewCount: grabCount('#views_count') || grabCount('.views-count') || grabCount('.fa-eye + span') || ''
        };
    }

    function collectCurrentPageItems(type) {
        TT.seenUrls = new Set();
        // Listing/profile/search pages mostly contain thumbnails. For feed mode we fetch
        // the real album pages in the background instead of showing low-res thumbs.
        if (!IS_ALBUM_PAGE) {
            TT.currentAlbum = null;
            return [];
        }
        const meta = getAlbumMetaFromDoc(document, location.href);
        TT.currentAlbum = meta;
        return extractMediaFromDoc(document, type, meta);
    }

    function euCleanFeedUrl(href, baseUrl = location.href) {
        try {
            const u = new URL(href, baseUrl);
            u.hash = '';
            return u.href;
        } catch (e) { return ''; }
    }

    function normalizeFeedTerms(value) {
        return String(value || '')
            .toLowerCase()
            .replace(/https?:\/\/\S+/g, ' ')
            .replace(/[^a-z0-9]+/g, ' ')
            .split(/\s+/)
            .filter(w => w.length >= 3 && !['www', 'com', 'the', 'and', 'for', 'with', 'from', 'erome', 'page', 'search'].includes(w))
            .slice(0, 8);
    }

    function getFeedSearchPhrase() {
        try {
            const url = new URL(location.href);
            for (const key of ['q', 'query', 'search', 'term', 'keyword', 'keywords', 's']) {
                const val = url.searchParams.get(key);
                if (val && val.trim()) return val.trim();
            }
        } catch (e) {}

        const searchInput = document.querySelector('input[type="search"], input[name="q"], input[name="search"], input[name="query"]');
        if (searchInput?.value?.trim()) return searchInput.value.trim();

        const heading = document.querySelector('.page-content h1, h1, .search-title, .page-title');
        const h = heading?.textContent?.replace(/\s+/g, ' ').trim() || '';
        const m = h.match(/(?:search|results?)\s*(?:for)?\s*[:\-]?\s*(.+)$/i);
        return (m?.[1] || '').trim();
    }

    function resetFeedScope() {
        TT.feedScopeUrl = normalizeUrl(location.href);
        TT.feedScopeMode = IS_ALBUM_PAGE ? 'album' : 'listing';
        TT.feedScopeTerms = normalizeFeedTerms(getFeedSearchPhrase());
        if (IS_ALBUM_PAGE) {
            TT.feedScopeLabel = SETTINGS.allowRelatedAlbumExpansion !== false ? 'Album + related' : 'Current album only';
        } else if (TT.feedScopeTerms.length) {
            TT.feedScopeLabel = `Search lock: ${TT.feedScopeTerms.join(' ')}`;
        } else {
            TT.feedScopeLabel = SETTINGS.lockFeedToSearch !== false ? 'Search/listing lock' : 'Related expansion on';
        }
    }

    function albumCardMatchesFeedScope(album, href) {
        if (!album) return true;
        const text = `${album.textContent || ''} ${href || ''}`.toLowerCase();
        const q = (SETTINGS.albumSearch || '').trim().toLowerCase();
        if (q && !text.includes(q)) return false;
        if (SETTINGS.strictSearchTitleMatch && TT.feedScopeTerms?.length) {
            return TT.feedScopeTerms.some(term => text.includes(term));
        }
        if (album.classList?.contains('eu-search-filtered-out') || album.classList?.contains('eu-track-filtered-out')) return false;
        return true;
    }

    function collectAlbumLinksFromResultDoc(doc = document, baseUrl = location.href, source = 'listing') {
        const out = [];
        const localSeen = new Set();

        // For searches/listings, only trust actual album result cards. This prevents the
        // feed from drifting into related/suggested album links found inside fetched albums.
        let albumCards = Array.from(doc.querySelectorAll('#albums .album'));
        if (!albumCards.length) {
            albumCards = Array.from(doc.querySelectorAll('.page-content .album, .albums .album, .user-albums .album'));
        }

        if (albumCards.length) {
            albumCards.forEach(album => {
                const a = album.querySelector('a.album-link[href*="/a/"], a[href*="/a/"]');
                const href = a?.getAttribute('href');
                if (!href || !/\/a\//.test(href)) return;
                const abs = euCleanFeedUrl(href, baseUrl);
                if (!abs || localSeen.has(abs)) return;
                if (!albumCardMatchesFeedScope(album, abs)) return;
                localSeen.add(abs);
                out.push(abs);
            });
            // Listing/search pages should only use result cards. Album pages can still
            // fall back to older related-album links if the related section is not
            // built from .album cards.
            if (out.length || source !== 'album') return out;
        }

        // Album-page fallback: restore the old next-album behavior. This is never
        // used for locked search/listing feeds because buildNextAlbumQueue() blocks
        // source='album' there.
        if (source === 'album' && (SETTINGS.allowRelatedAlbumExpansion !== false || SETTINGS.lockFeedToSearch === false)) {
            doc.querySelectorAll('a.album-link[href*="/a/"], .album a[href*="/a/"], a[href*="/a/"]').forEach(a => {
                const abs = euCleanFeedUrl(a.getAttribute('href'), baseUrl);
                if (abs && !localSeen.has(abs)) { localSeen.add(abs); out.push(abs); }
            });
        }
        return out;
    }

    function buildNextAlbumQueue(doc = document, baseUrl = location.href, source = 'listing') {
        const lockedListing = !IS_ALBUM_PAGE && SETTINGS.lockFeedToSearch !== false;
        const albumRelatedDisabled = IS_ALBUM_PAGE && SETTINGS.allowRelatedAlbumExpansion === false;

        if (lockedListing && source === 'album') {
            console.log('[EU] Feed search lock: skipped related album links from fetched album');
            return 0;
        }
        if (albumRelatedDisabled && source === 'album') {
            console.log('[EU] Feed scope: album page related expansion is disabled');
            return 0;
        }

        const links = new Set(TT.albumQueue);
        const before = links.size;
        const currentAbs = euCleanFeedUrl(location.href, location.href);

        collectAlbumLinksFromResultDoc(doc, baseUrl, source).forEach(abs => {
            if (abs === currentAbs) return;
            if (TT.albumSeen.has(abs)) return;
            links.add(abs);
        });

        TT.albumQueue = Array.from(links);
        const added = Math.max(0, TT.albumQueue.length - before);
        const mode = lockedListing ? 'search/listing result' : 'related/expanded';
        console.log(`[EU] Feed queue: ${TT.albumQueue.length} ${mode} albums ready (${added} new)`);
        updateFeedStatus();
        return added;
    }

    function resetListingFeedPager() {
        const url = new URL(window.location.href);
        const currentPage = parseInt(url.searchParams.get('page'), 10) || 1;
        TT.listingNextPage = currentPage + 1;
        TT.listingLoading = false;
        TT.listingStopped = false;
        TT.listingEmptyHits = 0;
        TT.listingSeenPages = new Set();
    }

    async function loadNextListingPageIntoQueue() {
        if (IS_ALBUM_PAGE || TT.listingLoading || TT.listingStopped) return 0;

        const url = new URL(window.location.href);
        url.searchParams.set('page', TT.listingNextPage);
        const pageUrl = url.href;
        if (TT.listingSeenPages?.has(pageUrl)) {
            TT.listingNextPage++;
            return 0;
        }

        TT.listingLoading = true;
        TT.listingSeenPages?.add(pageUrl);

        try {
            const res = await fetch(pageUrl, { credentials: 'include' });
            if (!res.ok) throw new Error(`HTTP ${res.status}`);

            const html = await res.text();
            const doc = new DOMParser().parseFromString(html, 'text/html');
            const before = TT.albumQueue.length;
            buildNextAlbumQueue(doc, pageUrl, 'listing');
            TT.listingNextPage++;

            const added = Math.max(0, TT.albumQueue.length - before);
            if (added === 0) TT.listingEmptyHits++;
            else TT.listingEmptyHits = 0;

            if (TT.listingEmptyHits >= 2) TT.listingStopped = true;
            console.log(`[EU] Desktop feed queued ${added} albums from listing page ${TT.listingNextPage - 1}`);
            return added;
        } catch (e) {
            TT.listingStopped = true;
            console.warn('[EU] Desktop feed listing queue stopped:', e);
            return 0;
        } finally {
            TT.listingLoading = false;
        }
    }

    async function fetchAlbumPage(url) {
        try {
            const res = await fetch(url, { credentials: 'include' });
            const html = await res.text();
            const doc = new DOMParser().parseFromString(html, 'text/html');
            return doc;
        } catch (e) {
            console.warn('[EU] Failed to preload album:', url, e);
            return null;
        }
    }

    async function preloadNextAlbum() {
        if (TT.loadingMore) return 0;
        if (TT.albumQueue.length === 0) {
            buildNextAlbumQueue(document, location.href, IS_ALBUM_PAGE ? 'album' : 'listing');
            if (TT.albumQueue.length === 0 && !IS_ALBUM_PAGE) {
                await loadNextListingPageIntoQueue();
            }
            if (TT.albumQueue.length === 0) return 0;
        }

        TT.loadingMore = true;
        showLoadingCard(true);

        const url = TT.albumQueue.shift();
        TT.albumSeen.add(url);
        const doc = await fetchAlbumPage(url);
        let added = 0;

        if (doc) {
            // Search/listing feeds stay locked to the original result pages. We only
            // discover related albums from fetched albums when the user disables the lock.
            if (IS_ALBUM_PAGE || SETTINGS.lockFeedToSearch === false) {
                buildNextAlbumQueue(doc, url, 'album');
            }

            const meta = getAlbumMetaFromDoc(doc, url);
            const newItems = extractMediaFromDoc(doc, STATE.tiktokType, meta);
            added = newItems.length;
            if (added > 0) {
                TT.items.push(...newItems);
                newItems.forEach((item, i) => appendTikTokCard(item, TT.items.length - added + i));
                updateTikTokCounter();
                updateFeedStatus();
                console.log(`[EU] Preloaded ${added} items from: ${meta.title}`);
            }
        }

        showLoadingCard(false);
        TT.loadingMore = false;
        updateFeedStatus();

        // If this album produced nothing useful, keep moving through the queue.
        // On listing/profile/search pages, silently pull the next result page too.
        if (added === 0) {
            if (TT.albumQueue.length > 0) return preloadNextAlbum();
            if (!IS_ALBUM_PAGE && !TT.listingStopped) {
                const queued = await loadNextListingPageIntoQueue();
                if (queued > 0) return preloadNextAlbum();
            }
        }

        return added;
    }

    function showLoadingCard(show) {
        const feed = document.getElementById('eu-tiktok-feed');
        if (!feed) return;
        let card = feed.querySelector('.eu-tiktok-loading');
        if (show) {
            if (!card) {
                card = document.createElement('div');
                card.className = 'eu-tiktok-loading';
                const label = STATE.tiktokLayout === 'desktop'
                    ? 'Loading more albums without changing page…'
                    : 'Loading next album…';
                card.innerHTML = `<span class="eu-spinner"></span><span>${label}</span>`;
                feed.appendChild(card);
            }
        } else if (card) {
            card.remove();
        }
    }

    function updateTikTokCounter() {
        const el = document.getElementById('eu-tiktok-counter');
        if (!el) return;
        const idx = Math.min(STATE.tiktokIndex + 1, TT.items.length);
        el.textContent = `${idx} / ${TT.items.length}`;
        updateFeedStatus();
    }

    function appendTikTokCard(item, idx) {
        const feed = document.getElementById('eu-tiktok-feed');
        if (!feed) return;
        feed.querySelector('.eu-tiktok-empty')?.remove();

        const el = document.createElement('div');
        el.className = 'eu-tiktok-item';
        el.dataset.idx = idx;
        el._euData = item;

        let mediaHTML;
        if (item.kind === 'video') {
            mediaHTML = `<video class="eu-tiktok-media" src="${item.url}" playsinline preload="metadata" ${SETTINGS.loopVideos !== false ? 'loop' : ''} ${SETTINGS.muteVideos ? 'muted' : ''}></video>`;
        } else {
            mediaHTML = `<img class="eu-tiktok-media" src="${item.url}" alt="" loading="lazy">`;
        }

        const descText = (item.title || '').replace(/[<>]/g, '');
        const kindEmoji = item.kind === 'video' ? '🎥' : '🖼️';

        const username = (item.username || 'Erome').replace(/[<>]/g, '');
        const avatarSeed = encodeURIComponent(username);
        const likeCount = item.likeCount || '';
        const favCount = item.favCount || '';
        const viewCount = item.viewCount || '';
        const albumHref = item.albumUrl || '#';

        el.innerHTML = `
            ${mediaHTML}
            <div class="eu-tiktok-play-hint">${ICONS.video}</div>
            <div class="eu-tiktok-heart-pop">❤️</div>
            ${TRACKING.seenAlbums.has(normalizeUrl(item.albumUrl || '')) ? '<div class="eu-feed-badge">Seen</div>' : ''}
            ${TRACKING.downloadedMedia.has(normalizeUrl(item.url)) ? '<div class="eu-feed-badge eu-feed-downloaded">Downloaded</div>' : ''}

            <div class="eu-tiktok-top">
                ${viewCount ? `<span class="eu-tiktok-views">${ICONS.eye}<span>${viewCount}</span></span>` : ''}
                <span class="eu-tiktok-kind-pill">${kindEmoji} ${item.kind === 'video' ? 'Video' : 'Photo'}</span>
                ${item.kind === 'video' ? `<span class="eu-tiktok-duration-pill" data-duration>⏱ <span class="eu-tt-dur-val">--:--</span></span>` : ''}
            </div>

            <div class="eu-tiktok-caption">
                <a class="eu-tiktok-user" href="${albumHref}" target="_blank" rel="noopener">@${username}</a>
                <div class="eu-tiktok-desc">${descText || `${kindEmoji} Media`}</div>
                ${item.albumUrl ? `<a class="eu-tiktok-album" href="${item.albumUrl}" target="_blank" rel="noopener">${ICONS.eye}<span>View Album on Erome</span></a>` : ''}
                <div class="eu-tiktok-ticker">
                    ${ICONS.music}<span>Erome · @${username}</span>
                </div>
            </div>

            <div class="eu-tiktok-sidebar">
                <a class="eu-tiktok-avatar" href="${albumHref}" target="_blank" rel="noopener" title="Visit @${username}">
                    <img src="https://api.dicebear.com/7.x/initials/svg?seed=${avatarSeed}&backgroundColor=8a5acc" alt="${username}">
                    <span class="eu-tiktok-avatar-plus">+</span>
                </a>

                <div class="eu-tiktok-action" data-act="like" title="Like">
                    <div class="eu-tiktok-icon">${ICONS.heart}</div>
                    <div class="eu-tiktok-lbl eu-tiktok-count">${likeCount || 'Like'}</div>
                </div>

                <div class="eu-tiktok-action" data-act="favorite" title="Add to Favorites">
                    <div class="eu-tiktok-icon">${ICONS.star}</div>
                    <div class="eu-tiktok-lbl eu-tiktok-count">${favCount || 'Favorite'}</div>
                </div>

                <div class="eu-tiktok-action" data-act="comment" title="Comments">
                    <div class="eu-tiktok-icon">${ICONS.comment}</div>
                    <div class="eu-tiktok-lbl">Comments</div>
                </div>

                <div class="eu-tiktok-action" data-act="share" title="Share">
                    <div class="eu-tiktok-icon">${ICONS.share}</div>
                    <div class="eu-tiktok-lbl">Share</div>
                </div>

                <div class="eu-tiktok-action" data-act="download" title="Save">
                    <div class="eu-tiktok-icon">${ICONS.download}</div>
                    <div class="eu-tiktok-lbl">Save</div>
                </div>

                <div class="eu-tiktok-action" data-act="copy" title="Copy URL">
                    <div class="eu-tiktok-icon">${ICONS.copy}</div>
                    <div class="eu-tiktok-lbl">Copy</div>
                </div>

                <div class="eu-tiktok-action eu-tiktok-action-mini" data-act="report" title="Report">
                    <div class="eu-tiktok-icon">${ICONS.flag}</div>
                </div>

                <div class="eu-tiktok-disc">
                    <img src="https://api.dicebear.com/7.x/initials/svg?seed=${avatarSeed}&backgroundColor=14151f" alt="">
                </div>
            </div>

            ${item.kind === 'video' ? '<div class="eu-tiktok-progress"><div class="eu-tiktok-progress-fill"></div></div>' : ''}
        `;

        // Actions — Erome-native buttons
        el.querySelectorAll('.eu-tiktok-action').forEach(btn => {
            btn.addEventListener('click', (e) => {
                e.stopPropagation();
                const act = btn.dataset.act;
                if (act === 'download') downloadSingle(item.url, getFilename(item.url), null, item.albumUrl);
                if (act === 'copy') {
                    navigator.clipboard?.writeText(item.url)
                        .then(() => toast('URL copied!', 'success', 1400))
                        .catch(() => toast('Copy failed', 'error'));
                }
                if (act === 'like') {
                    btn.classList.toggle('liked');
                    if (btn.classList.contains('liked')) {
                        showHeartPop(el);
                        toast('Liked \u2665', 'success', 1200);
                    }
                }
                if (act === 'favorite') {
                    btn.classList.toggle('faved');
                    if (btn.classList.contains('faved')) saveFavoriteItem(item);
                    toast(btn.classList.contains('faved') ? 'Added to local favorites \u2b50' : 'Removed from favorites', 'success', 1400);
                }
                if (act === 'comment') {
                    if (item.albumUrl) window.open(item.albumUrl + '#comments', '_blank');
                    else toast('Comments available on the full album page', 'info');
                }
                if (act === 'share') {
                    const shareUrl = item.albumUrl || item.url;
                    if (navigator.share) {
                        navigator.share({ title: `Erome \u00b7 @${username}`, url: shareUrl }).catch(() => {});
                    } else {
                        navigator.clipboard?.writeText(shareUrl)
                            .then(() => toast('Share link copied \ud83d\udce4', 'success', 1400))
                            .catch(() => toast('Copy failed', 'error'));
                    }
                }
                if (act === 'report') {
                    if (confirm('Open the full album page to report this content?') && item.albumUrl) {
                        window.open(item.albumUrl, '_blank');
                    }
                }
            });
        });

        // Double-tap to like (mobile gesture)
        let lastTap = 0;
        el.addEventListener('click', (e) => {
            if (e.target.closest('.eu-tiktok-sidebar') || e.target.closest('.eu-tiktok-album')) return;

            const now = Date.now();
            if (now - lastTap < 350) {
                // Double tap
                const likeBtn = el.querySelector('.eu-tiktok-action[data-act="like"]');
                if (likeBtn && !likeBtn.classList.contains('liked')) {
                    likeBtn.classList.add('liked');
                }
                showHeartPop(el);
            } else {
                // Single tap = play/pause for videos
                if (item.kind === 'video') {
                    const video = el.querySelector('video');
                    const hint = el.querySelector('.eu-tiktok-play-hint');
                    if (video) {
                        if (video.paused) { video.play().catch(() => {}); hint.classList.remove('show'); }
                        else { video.pause(); hint.classList.add('show'); }
                    }
                }
            }
            lastTap = now;
        });

        // Observe for autoplay
        if (TT.io) TT.io.observe(el);

        // Insert before loading card if present
        const loadingCard = feed.querySelector('.eu-tiktok-loading');
        if (loadingCard) feed.insertBefore(el, loadingCard);
        else feed.appendChild(el);

        // Video progress bar + duration display
        if (item.kind === 'video') {
            const video = el.querySelector('video');
            applyVideoPrefs(video);
            const fill = el.querySelector('.eu-tiktok-progress-fill');
            const durPill = el.querySelector('.eu-tt-dur-val');

            // Live elapsed/total time badge (bottom-left)
            const liveTime = document.createElement('div');
            liveTime.className = 'eu-tiktok-duration-live';
            liveTime.textContent = '00:00 / --:--';
            el.appendChild(liveTime);

            const fmtTime = (s) => {
                if (!isFinite(s) || s < 0) return '--:--';
                const m = Math.floor(s / 60);
                const sec = Math.floor(s % 60).toString().padStart(2, '0');
                return `${m}:${sec}`;
            };

            video?.addEventListener('loadedmetadata', () => {
                if (video.duration && isFinite(video.duration)) {
                    const seconds = Math.floor(video.duration);
                    item.duration = seconds;
                    if (filterOutFeedVideoByLength(item, el, seconds)) return;
                    if (durPill) durPill.textContent = fmtTime(video.duration);
                    liveTime.textContent = `00:00 / ${fmtTime(video.duration)}`;
                }
            });

            video?.addEventListener('timeupdate', () => {
                if (video.duration) {
                    fill.style.width = `${(video.currentTime / video.duration) * 100}%`;
                    liveTime.textContent = `${fmtTime(video.currentTime)} / ${fmtTime(video.duration)}`;
                }
            });
        }
    }

    function showHeartPop(cardEl) {
        const pop = cardEl.querySelector('.eu-tiktok-heart-pop');
        if (!pop) return;
        pop.classList.remove('animate');
        void pop.offsetWidth;
        pop.classList.add('animate');
    }

    /* ---------- Right Panel (2-column grid) - REMOVED ---------- */

    function setTikTokLayout(layout) {
        STATE.tiktokLayout = layout === 'desktop' ? 'desktop' : 'phone';
        updateSetting('defaultFeedLayout', STATE.tiktokLayout);
        const overlay = document.getElementById('eu-tiktok');
        if (!overlay) return;

        const isDesktop = STATE.tiktokLayout === 'desktop';
        overlay.classList.toggle('eu-desktop-mode', isDesktop);

        const btn = overlay.querySelector('.eu-tiktok-layout-toggle');
        if (btn) {
            btn.textContent = isDesktop ? 'Phone' : 'Desktop';
            btn.title = isDesktop ? 'Switch to phone layout' : 'Switch to desktop layout';
        }

        const title = overlay.querySelector('.eu-tiktok-title span');
        if (title) title.textContent = isDesktop ? 'Desktop Feed' : 'For You';
        applyFeedFitMode();
    }


    function applyVideoPrefs(video) {
        if (!video) return;
        video.loop = SETTINGS.loopVideos !== false;
        video.muted = !!SETTINGS.muteVideos;
        video.setAttribute('playsinline', '');
    }

    function applyFeedFitMode() {
        const overlay = document.getElementById('eu-tiktok');
        if (!overlay) return;
        overlay.classList.remove('eu-fit-contain', 'eu-fit-cover', 'eu-fit-natural');
        overlay.classList.add('eu-fit-' + (SETTINGS.fitMode || 'contain'));
        document.querySelectorAll('#eu-tiktok video').forEach(applyVideoPrefs);
        updateFeedTools();
    }

    function markCurrentFeedPosition() {
        if (!SETTINGS.autoResume || !STATE.tiktokMode) return;
        SETTINGS.lastFeed = {
            page: normalizeUrl(location.href),
            type: STATE.tiktokType,
            layout: STATE.tiktokLayout,
            index: STATE.tiktokIndex,
            savedAt: new Date().toISOString()
        };
        saveSettings();
    }

    function resumeFeedPosition() {
        if (!SETTINGS.autoResume || !SETTINGS.lastFeed) return;
        const last = SETTINGS.lastFeed;
        if (last.page !== normalizeUrl(location.href)) return;
        if (last.type !== STATE.tiktokType || last.layout !== STATE.tiktokLayout) return;
        const feed = document.getElementById('eu-tiktok-feed');
        const cards = feed?.querySelectorAll('.eu-tiktok-item');
        if (!cards || !cards.length) return;
        const idx = Math.min(Math.max(0, Number(last.index) || 0), cards.length - 1);
        if (idx > 0) {
            STATE.tiktokIndex = idx;
            setTimeout(() => cards[idx]?.scrollIntoView({ behavior: 'auto', block: 'start' }), 150);
        }
    }

    function getFeedScopeButtonLabel() {
        if (IS_ALBUM_PAGE) return SETTINGS.allowRelatedAlbumExpansion !== false ? 'Related On' : 'Current Only';
        return SETTINGS.lockFeedToSearch !== false ? 'Search Lock' : 'Related On';
    }

    function updateFeedTools() {
        const panel = document.getElementById('eu-feed-tools');
        if (!panel) return;
        const auto = panel.querySelector('[data-act="autoplay"] span');
        const mute = panel.querySelector('[data-act="mute"] span');
        const fit = panel.querySelector('[data-act="fit"] span');
        const scope = panel.querySelector('[data-act="scope"] span');
        const length = panel.querySelector('[data-act="length"] span');
        const pin = panel.querySelector('[data-act="pin"] span');
        const pinBtn = panel.querySelector('[data-act="pin"]');
        if (auto) auto.textContent = SETTINGS.autoplay ? 'Auto On' : 'Auto Off';
        if (mute) mute.textContent = SETTINGS.muteVideos ? 'Muted' : 'Sound';
        if (fit) fit.textContent = `Fit ${SETTINGS.fitMode || 'contain'}`;
        if (scope) scope.textContent = getFeedScopeButtonLabel();
        if (length) {
            length.textContent = getLengthFilterLabel();
            length.closest('button')?.classList.toggle('eu-active', getMinVideoSeconds() > 0 || getMaxVideoSeconds() > 0);
        }
        if (pin) pin.textContent = SETTINGS.feedControlsPinned ? 'Pinned' : (SETTINGS.feedControlsAutoHide === false ? 'Always On' : 'Auto Hide');
        if (pinBtn) pinBtn.classList.toggle('eu-active', SETTINGS.feedControlsPinned === true || SETTINGS.feedControlsAutoHide === false);
        updateFeedStatus();
    }

    function updateFeedStatus() {
        const el = document.getElementById('eu-feed-status');
        if (!el) return;
        const scope = TT.feedScopeLabel || (SETTINGS.lockFeedToSearch !== false ? 'Search/listing lock' : 'Related expansion');
        el.textContent = `Items ${TT.items.length} · Queue ${TT.albumQueue.length} · Page ${TT.listingNextPage || '-'} · ${scope} · ${getLengthFilterLabel()} · ${SETTINGS.performanceMode}`;
    }

    function rebuildFeedFromItems(scrollIndex = 0) {
        const feed = document.getElementById('eu-tiktok-feed');
        if (!feed) return;
        if (TT.io) TT.io.disconnect();
        feed.innerHTML = '';
        TT.items.forEach((item, idx) => appendTikTokCard(item, idx));
        STATE.tiktokIndex = Math.min(scrollIndex, Math.max(0, TT.items.length - 1));
        updateTikTokCounter();
        applyFeedFitMode();
        updateFeedStatus();
        setTimeout(() => getCurrentCard()?.scrollIntoView({ behavior: 'auto', block: 'start' }), 80);
    }

    function skipCurrentAlbumInFeed() {
        const item = getCurrentItem();
        const album = item?.albumUrl;
        if (!album) { toast('No album to skip', 'warning'); return; }
        const before = TT.items.length;
        TT.items = TT.items.filter(x => x.albumUrl !== album);
        const removed = before - TT.items.length;
        rebuildFeedFromItems(Math.min(STATE.tiktokIndex, Math.max(0, TT.items.length - 1)));
        toast(`Skipped ${removed} item${removed === 1 ? '' : 's'} from album`, 'info');
        if (TT.items.length < 4) preloadNextAlbum();
    }

    function shuffleFeed() {
        for (let i = TT.items.length - 1; i > 0; i--) {
            const j = Math.floor(Math.random() * (i + 1));
            [TT.items[i], TT.items[j]] = [TT.items[j], TT.items[i]];
        }
        rebuildFeedFromItems(0);
        toast('Feed shuffled', 'success');
    }

    function cycleFitMode() {
        const modes = ['contain', 'cover', 'natural'];
        const next = modes[(modes.indexOf(SETTINGS.fitMode) + 1) % modes.length] || 'contain';
        updateSetting('fitMode', next);
        applyFeedFitMode();
        toast(`Fit mode: ${next}`, 'info');
    }

    let feedControlsTimer = null;

    function feedToolsShouldAutoHide() {
        return SETTINGS.feedControlsAutoHide !== false && SETTINGS.feedControlsPinned !== true;
    }

    function setFeedControlsVisible(visible, temporary = true, forceHide = false) {
        const overlay = document.getElementById('eu-tiktok');
        const phone = overlay?.querySelector('.eu-phone');
        if (!phone) return;
        const shouldHide = !visible && (forceHide || feedToolsShouldAutoHide());
        phone.classList.toggle('eu-feed-controls-hidden', shouldHide);
        const toggle = document.getElementById('eu-feed-tools-toggle');
        if (toggle) {
            toggle.title = shouldHide ? 'Show feed tools' : 'Hide feed tools';
            const label = toggle.querySelector('span');
            if (label) label.textContent = shouldHide ? 'Tools' : 'Hide';
        }
        if (visible && temporary && feedToolsShouldAutoHide()) scheduleFeedControlsHide();
    }

    function scheduleFeedControlsHide(delay = 2800) {
        clearTimeout(feedControlsTimer);
        const phone = document.querySelector('#eu-tiktok .eu-phone');
        if (!phone) return;
        if (!feedToolsShouldAutoHide()) {
            phone.classList.remove('eu-feed-controls-hidden');
            return;
        }
        feedControlsTimer = setTimeout(() => setFeedControlsVisible(false, false), delay);
    }

    function showFeedControlsTemp(delay = 2800) {
        setFeedControlsVisible(true, false);
        if (feedToolsShouldAutoHide()) scheduleFeedControlsHide(delay);
    }

    function installFeedControlsAutoHide(phone, panel) {
        if (!phone || phone.dataset.euFeedAutohide === '1') return;
        phone.dataset.euFeedAutohide = '1';

        panel?.addEventListener('mouseenter', () => clearTimeout(feedControlsTimer));
        panel?.addEventListener('mouseleave', () => scheduleFeedControlsHide(1400));
        panel?.addEventListener('click', () => scheduleFeedControlsHide(1800));

        const hideSoon = () => scheduleFeedControlsHide(650);
        const showFromTop = (e) => {
            const y = e.touches?.[0]?.clientY ?? e.clientY ?? 9999;
            const rect = phone.getBoundingClientRect();
            // Reveal only when the mouse rolls over the top control zone,
            // so the video stays clean during normal scrolling/watching.
            if (y - rect.top <= 155) showFeedControlsTemp(2400);
        };

        phone.addEventListener('wheel', hideSoon, { passive: true });
        phone.addEventListener('touchmove', hideSoon, { passive: true });
        phone.addEventListener('mousemove', showFromTop, { passive: true });

        const feed = phone.querySelector('#eu-tiktok-feed');
        feed?.addEventListener('scroll', () => scheduleFeedControlsHide(650), { passive: true });

        setTimeout(() => {
            if (feedToolsShouldAutoHide()) setFeedControlsVisible(false, false);
            else setFeedControlsVisible(true, false);
        }, 2600);
    }

    function enhanceFeedControls() {
        const overlay = document.getElementById('eu-tiktok');
        const phone = overlay?.querySelector('.eu-phone');
        if (!phone) return;
        if (!document.getElementById('eu-feed-tools')) {
            const panel = document.createElement('div');
            panel.id = 'eu-feed-tools';
            panel.className = 'eu-feed-tools';
            panel.innerHTML = `
                <button data-act="skip"><span>Skip Album</span></button>
                <button data-act="shuffle"><span>Shuffle</span></button>
                <button data-act="fit"><span>Fit ${SETTINGS.fitMode}</span></button>
                <button data-act="scope"><span>${getFeedScopeButtonLabel()}</span></button>
                <button data-act="length"><span>${getLengthFilterLabel()}</span></button>
                <button data-act="mute"><span>${SETTINGS.muteVideos ? 'Muted' : 'Sound'}</span></button>
                <button data-act="autoplay"><span>${SETTINGS.autoplay ? 'Auto On' : 'Auto Off'}</span></button>
                <button data-act="manager"><span>Downloads</span></button>
                <button data-act="status"><span>Status</span></button>
                <button data-act="pin"><span>${SETTINGS.feedControlsPinned ? 'Pinned' : (SETTINGS.feedControlsAutoHide === false ? 'Always On' : 'Auto Hide')}</span></button>
                <button data-act="hide"><span>Hide</span></button>`;
            phone.appendChild(panel);

            panel.addEventListener('click', e => {
                const btn = e.target.closest('button[data-act]');
                if (!btn) return;
                const act = btn.dataset.act;
                if (act === 'skip') skipCurrentAlbumInFeed();
                if (act === 'shuffle') shuffleFeed();
                if (act === 'fit') cycleFitMode();
                if (act === 'scope') {
                    if (IS_ALBUM_PAGE) {
                        updateSetting('allowRelatedAlbumExpansion', !(SETTINGS.allowRelatedAlbumExpansion !== false));
                        toast(SETTINGS.allowRelatedAlbumExpansion !== false ? 'Album feed will load next/related albums' : 'Album feed limited to current album', 'info');
                    } else {
                        updateSetting('lockFeedToSearch', !(SETTINGS.lockFeedToSearch !== false));
                        toast(SETTINGS.lockFeedToSearch !== false ? 'Feed locked to current search/listing' : 'Related album expansion enabled', 'info');
                    }
                    resetAndRenderFeed();
                }
                if (act === 'length') promptLengthFilter();
                if (act === 'mute') { updateSetting('muteVideos', !SETTINGS.muteVideos); document.querySelectorAll('#eu-tiktok video').forEach(applyVideoPrefs); updateFeedTools(); }
                if (act === 'autoplay') { updateSetting('autoplay', !SETTINGS.autoplay); updateFeedTools(); }
                if (act === 'manager') openDownloadManager();
                if (act === 'status') openStatusPanel();
                if (act === 'pin') {
                    updateSetting('feedControlsPinned', !SETTINGS.feedControlsPinned);
                    if (SETTINGS.feedControlsPinned) setFeedControlsVisible(true, false);
                    else scheduleFeedControlsHide(900);
                    updateFeedTools();
                    toast(SETTINGS.feedControlsPinned ? 'Feed tools pinned' : 'Feed tools will auto-hide', 'info');
                }
                if (act === 'hide') setFeedControlsVisible(false, false, true);
            });
        }
        if (!document.getElementById('eu-feed-tools-toggle')) {
            const toggle = document.createElement('button');
            toggle.id = 'eu-feed-tools-toggle';
            toggle.className = 'eu-feed-tools-toggle';
            toggle.innerHTML = `<img src="${LOGO_ICON}" alt=""><span>Tools</span>`;
            toggle.title = 'Show feed tools';
            toggle.addEventListener('click', e => {
                e.stopPropagation();
                const isHidden = phone.classList.contains('eu-feed-controls-hidden');
                if (isHidden) showFeedControlsTemp(4200);
                else setFeedControlsVisible(false, false, true);
            });
            phone.appendChild(toggle);
        }
        if (!document.getElementById('eu-feed-status')) {
            const status = document.createElement('div');
            status.id = 'eu-feed-status';
            status.className = 'eu-feed-status';
            phone.appendChild(status);
        }
        installFeedControlsAutoHide(phone, document.getElementById('eu-feed-tools'));
        applyFeedFitMode();
        updateFeedStatus();
        if (feedToolsShouldAutoHide()) scheduleFeedControlsHide(2600);
        else setFeedControlsVisible(true, false);
    }

    function buildTikTokOverlay() {
        if (document.getElementById('eu-tiktok')) return;

        const overlay = document.createElement('div');
        overlay.id = 'eu-tiktok';
        overlay.className = 'eu-tiktok-overlay';
        overlay.innerHTML = `
          <div class="eu-phone">
            <div class="eu-tiktok-splash" id="eu-tiktok-splash">
              <img src="${LOGO_SPLASH}" alt="Erome" class="eu-tiktok-splash-logo">
              <div class="eu-tiktok-splash-tag">Ultimate Premium</div>
              <div class="eu-tiktok-splash-dots"><span></span><span></span><span></span></div>
            </div>
            <div class="eu-tiktok-topbar">
              <div class="eu-tiktok-title">
                <img src="${LOGO_ICON}" alt="Erome" class="eu-tiktok-brand-logo">
                <span>For You</span>
              </div>
              <div class="eu-tiktok-controls">
                <button class="eu-tiktok-tab" data-type="videos">Videos</button>
                <button class="eu-tiktok-tab" data-type="photos">Photos</button>
                <button class="eu-tiktok-tab" data-type="all">All</button>
                <button class="eu-tiktok-layout-toggle" title="Switch to desktop layout">Desktop</button>
                <button class="eu-tiktok-close" title="Close">${ICONS.close}</button>
              </div>
            </div>
            <div class="eu-tiktok-counter" id="eu-tiktok-counter">0 / 0</div>
            <div class="eu-tiktok-feed" id="eu-tiktok-feed"></div>
          </div>`;

        document.body.appendChild(overlay);

        overlay.querySelector('.eu-tiktok-close').addEventListener('click', closeTikTok);
        overlay.addEventListener('click', (e) => {
            // Click outside phone closes
            if (e.target === overlay) closeTikTok();
        });

        overlay.querySelectorAll('.eu-tiktok-tab').forEach(tab => {
            tab.addEventListener('click', () => {
                STATE.tiktokType = tab.dataset.type;
                updateSetting('defaultFeedType', STATE.tiktokType);
                updateTikTokTabs();
                resetAndRenderFeed();
            });
        });

        overlay.querySelector('.eu-tiktok-layout-toggle')?.addEventListener('click', () => {
            setTikTokLayout(STATE.tiktokLayout === 'desktop' ? 'phone' : 'desktop');
        });

        // Setup IntersectionObserver once
        const feed = overlay.querySelector('#eu-tiktok-feed');
        TT.io = new IntersectionObserver((entries) => {
            entries.forEach(entry => {
                const card = entry.target;
                const videoEl = card.querySelector('video');
                const idx = parseInt(card.dataset.idx);

                if (entry.isIntersecting && entry.intersectionRatio > 0.65) {
                    STATE.tiktokIndex = idx;
                    updateTikTokCounter();
                    if (card._euData?.albumUrl) markSeenAlbum(card._euData.albumUrl);
                    if (videoEl) {
                        applyVideoPrefs(videoEl);
                        if (SETTINGS.autoplay !== false) {
                            videoEl.play().catch(() => {
                                // Autoplay with sound blocked — fall back to muted
                                videoEl.muted = true;
                                videoEl.play().catch(() => {});
                            });
                        } else {
                            videoEl.pause();
                        }
                    }
                    markCurrentFeedPosition();
                    decorateTrackingBadges();
                    // Preload next album when nearing the end
                    if (idx >= TT.items.length - 3) {
                        preloadNextAlbum();
                    }
                } else if (videoEl) {
                    videoEl.pause();
                }
            });
        }, { root: feed, threshold: [0, 0.65, 0.9] });

        // Scroll fallback for desktop wheel/trackpad: load ahead when nearing the end.
        let feedScrollDebounce;
        feed.addEventListener('scroll', () => {
            if (!STATE.tiktokMode) return;
            clearTimeout(feedScrollDebounce);
            feedScrollDebounce = setTimeout(() => {
                const nearEnd = feed.scrollTop + feed.clientHeight >= feed.scrollHeight - (feed.clientHeight * 2.2);
                if (nearEnd) preloadNextAlbum();
            }, 80);
        }, { passive: true });

        // Keyboard controls
        document.addEventListener('keydown', (e) => {
            if (!STATE.tiktokMode) return;
            if (e.key === 'Escape') closeTikTok();
            else if (e.key === 'ArrowDown' || e.key === 'j') { scrollTikTok(1); e.preventDefault(); }
            else if (e.key === 'ArrowUp' || e.key === 'k') { scrollTikTok(-1); e.preventDefault(); }
            else if (e.key === ' ') {
                const card = getCurrentCard();
                const video = card?.querySelector('video');
                if (video) {
                    if (video.paused) video.play(); else video.pause();
                    e.preventDefault();
                }
            }
            else if (e.key === 'd') {
                const item = getCurrentItem();
                if (item) downloadSingle(item.url, getFilename(item.url), null, item.albumUrl);
            }
            else if (e.key === 'l') {
                const card = getCurrentCard();
                card?.querySelector('.eu-tiktok-action[data-act="like"]')?.click();
            }
        });
    }

    function updateTikTokTabs() {
        document.querySelectorAll('.eu-tiktok-tab').forEach(t => {
            t.classList.toggle('active', t.dataset.type === STATE.tiktokType);
        });
    }

    function getCurrentCard() {
        const feed = document.getElementById('eu-tiktok-feed');
        if (!feed) return null;
        const cards = feed.querySelectorAll('.eu-tiktok-item');
        return cards[STATE.tiktokIndex] || cards[0] || null;
    }

    function getCurrentItem() {
        return getCurrentCard()?._euData || null;
    }

    function scrollTikTok(direction) {
        const feed = document.getElementById('eu-tiktok-feed');
        if (!feed) return;
        const cards = feed.querySelectorAll('.eu-tiktok-item');
        const next = Math.max(0, Math.min(cards.length - 1, STATE.tiktokIndex + direction));
        cards[next]?.scrollIntoView({ behavior: 'smooth', block: 'start' });
        if (next >= cards.length - 3) preloadNextAlbum();
    }

    async function resetAndRenderFeed() {
        const feed = document.getElementById('eu-tiktok-feed');
        if (!feed) return;

        // Reset state
        TT.items = [];
        TT.seenUrls = new Set();
        TT.albumQueue = [];
        TT.albumSeen = new Set();
        TT.loadingMore = false;
        resetListingFeedPager();
        resetFeedScope();
        if (IS_ALBUM_PAGE) TT.albumSeen.add(euCleanFeedUrl(location.href, location.href));
        if (TT.io) TT.io.disconnect();
        STATE.tiktokIndex = 0;

        feed.innerHTML = '';

        // Album pages can render their own media immediately. Listing pages are queued
        // from album links and fetched in the background so the browser URL never changes.
        const pageItems = collectCurrentPageItems(STATE.tiktokType);
        TT.items = pageItems;
        pageItems.forEach((item, idx) => appendTikTokCard(item, idx));

        buildNextAlbumQueue(document, location.href, IS_ALBUM_PAGE ? 'album' : 'listing');
        if (!IS_ALBUM_PAGE && TT.albumQueue.length === 0) {
            await loadNextListingPageIntoQueue();
        }

        // If the current page had no real media, pull albums until the first usable
        // card appears. This is what makes desktop feed work from profile/search grids.
        let attempts = 0;
        while (TT.items.length === 0 && attempts < 12) {
            if (TT.albumQueue.length === 0 && !IS_ALBUM_PAGE && !TT.listingStopped) {
                await loadNextListingPageIntoQueue();
            }
            if (TT.albumQueue.length === 0) break;
            const added = await preloadNextAlbum();
            attempts++;
            if (added > 0) break;
        }

        if (TT.items.length === 0) {
            feed.innerHTML = `<div class="eu-tiktok-empty">
                <div style="font-size:44px;margin-bottom:14px;">📭</div>
                <div style="font-weight:600;margin-bottom:4px;">No ${STATE.tiktokType} found</div>
                <div style="font-size:11px;opacity:.6;">Try another tab, or turn Related On for next-album loading.</div>
            </div>`;
            const c = document.getElementById('eu-tiktok-counter');
            if (c) c.textContent = '0 / 0';
            return;
        }

        updateTikTokCounter();
        feed.scrollTop = 0;
        applyFeedFitMode();
        updateFeedStatus();
        setTimeout(resumeFeedPosition, 250);

        // Preload ahead without changing the current page/URL.
        if (TT.albumQueue.length > 0 || (!IS_ALBUM_PAGE && !TT.listingStopped)) {
            setTimeout(() => preloadNextAlbum(), STATE.tiktokLayout === 'desktop' ? 700 : 1500);
        }
    }

    function openTikTok(type, layout = 'phone') {
        buildTikTokOverlay();
        if (type) STATE.tiktokType = type;
        updateSetting('defaultFeedType', STATE.tiktokType);
        setTikTokLayout(layout);
        enhanceFeedControls();
        updateTikTokTabs();
        STATE.tiktokMode = true;
        document.getElementById('eu-tiktok').classList.add('open');
        document.body.style.overflow = 'hidden';
        resetAndRenderFeed();
        const msg = STATE.tiktokLayout === 'desktop'
            ? (SETTINGS.lockFeedToSearch !== false && !IS_ALBUM_PAGE
                ? 'Desktop feed — locked to this search/listing and pulls next result pages'
                : 'Desktop feed — scroll up/down, loads next albums without changing page')
            : 'TikTok mode — swipe up/down, tap to pause, double-tap to like';
        toast(msg, 'info', 3500);
    }

    function openDesktopFeed(type = 'all') {
        openTikTok(type, 'desktop');
    }

    function closeTikTok() {
        markCurrentFeedPosition();
        STATE.tiktokMode = false;
        const o = document.getElementById('eu-tiktok');
        if (o) {
            o.classList.remove('open');
            // Pause all videos
            o.querySelectorAll('video').forEach(v => v.pause());
        }
        document.body.style.overflow = '';
    }

    /* ============================================================
     *  FLOATING ACTION BUTTON (FAB)
     * ============================================================ */
    function buildFAB() {
        if (!document.querySelector('.eu-fab-bulk')) {
            const fab = document.createElement('button');
            fab.className = 'eu-fab eu-fab-bulk';
            fab.title = 'Bulk Download';
            fab.innerHTML = `${ICONS.download}<span class="eu-fab-badge" id="eu-fab-badge">0</span>`;
            fab.addEventListener('click', openBulkModal);
            document.body.appendChild(fab);
        }

        // Feed FAB — phone layout on small screens, desktop layout on desktop.
        if (!document.querySelector('.eu-fab-tiktok')) {
            const tt = document.createElement('button');
            tt.className = 'eu-fab eu-fab-tiktok';
            tt.title = 'Feed Mode';
            tt.style.bottom = '92px';
            tt.style.background = 'linear-gradient(135deg, #ff0050 0%, #8a5acc 50%, #00f2ea 100%)';
            tt.innerHTML = `<img src="${LOGO_ICON}" alt="Erome" style="width:32px;height:32px;border-radius:50%;object-fit:cover;box-shadow:0 0 8px rgba(0,0,0,0.4);">`;
            tt.addEventListener('click', () => {
                const layout = SETTINGS.defaultFeedLayout || (window.matchMedia('(min-width: 760px)').matches ? 'desktop' : 'phone');
                const type = SETTINGS.defaultFeedType || (IS_ALBUM_PAGE ? STATE.tiktokType : 'all');
                openTikTok(type, layout);
            });
            document.body.appendChild(tt);
        }
    }
    function updateFabBadge() {
        const badge = document.getElementById('eu-fab-badge');
        if (!badge) return;
        const n = collectAllMediaUrls().length;
        badge.textContent = n;
        badge.style.display = n > 0 ? 'flex' : 'none';
    }

    /* ============================================================
     *  HUB MENU (Twitter-style popup cards in navbar)
     * ============================================================ */

    function buildSettingsModal() {
        if (document.getElementById('eu-settings-modal')) return;
        const overlay = document.createElement('div');
        overlay.id = 'eu-settings-modal';
        overlay.className = 'eu-modal-overlay';
        overlay.innerHTML = `
          <div class="eu-modal" style="width:620px;">
            <div class="eu-modal-header">
              <h3>${ICONS.settings}<span>Smart Premium Settings</span></h3>
              <button class="eu-modal-close">${ICONS.close}</button>
            </div>
            <div class="eu-modal-body">
              <div class="eu-smart-row">
                <div class="eu-smart-field"><label>Default Feed</label><select id="eu-set-feed-type">
                    <option value="all">All</option><option value="videos">Videos</option><option value="photos">Photos</option>
                </select></div>
                <div class="eu-smart-field"><label>Layout</label><select id="eu-set-feed-layout">
                    <option value="desktop">Desktop</option><option value="phone">Phone</option>
                </select></div>
              </div>
              <div class="eu-smart-row">
                <div class="eu-smart-field"><label>Performance</label><select id="eu-set-performance">
                    <option value="lite">Lite</option><option value="normal">Normal</option><option value="aggressive">Aggressive</option>
                </select></div>
                <div class="eu-smart-field"><label>Feed Fit</label><select id="eu-set-fit">
                    <option value="contain">Contain</option><option value="cover">Cover</option><option value="natural">Natural</option>
                </select></div>
              </div>
              <div class="eu-smart-row">
                <div class="eu-smart-field"><label>Min video length seconds</label><input id="eu-set-min-seconds" type="number" min="0" max="3600" step="5" placeholder="0 = off"></div>
                <div class="eu-smart-field"><label>Max video length seconds</label><input id="eu-set-max-seconds" type="number" min="0" max="3600" step="5" placeholder="0 = off"></div>
              </div>
              <div class="eu-smart-row">
                <div class="eu-smart-field"><label>Album search filter</label><input id="eu-set-album-search" type="text" placeholder="blank = off"></div>
                <div class="eu-smart-field"><label>Length filter</label><input disabled value="0 means off · applies to album page and feed videos"></div>
              </div>
              <div class="eu-option-group">
                <div class="eu-option-title">Toggles</div>
                <div class="eu-smart-checks">
                  <label class="eu-smart-check"><input id="eu-set-autoplay" type="checkbox"> Autoplay feed videos</label>
                  <label class="eu-smart-check"><input id="eu-set-mute" type="checkbox"> Start feed muted</label>
                  <label class="eu-smart-check"><input id="eu-set-loop" type="checkbox"> Loop videos</label>
                  <label class="eu-smart-check"><input id="eu-set-resume" type="checkbox"> Resume feed position</label>
                  <label class="eu-smart-check"><input id="eu-set-badges" type="checkbox"> Seen/download badges</label>
                  <label class="eu-smart-check"><input id="eu-set-hide-seen" type="checkbox"> Hide seen albums</label>
                  <label class="eu-smart-check"><input id="eu-set-hide-down" type="checkbox"> Hide downloaded albums</label>
                  <label class="eu-smart-check"><input id="eu-set-skip-down" type="checkbox"> Skip downloaded in downloads</label>
                  <label class="eu-smart-check"><input id="eu-set-folders" type="checkbox"> ZIP folders by album</label>
                  <label class="eu-smart-check"><input id="eu-set-retry" type="checkbox"> Retry failed downloads</label>
                  <label class="eu-smart-check"><input id="eu-set-dlbtns" type="checkbox"> Show media download buttons</label>
                  <label class="eu-smart-check"><input id="eu-set-lock-search" type="checkbox"> Lock feed to current search/listing</label>
                  <label class="eu-smart-check"><input id="eu-set-related" type="checkbox"> Allow related album expansion</label>
                  <label class="eu-smart-check"><input id="eu-set-strict-search" type="checkbox"> Strict search-word title filter</label>
                  <label class="eu-smart-check"><input id="eu-set-feed-auto-hide" type="checkbox"> Auto-hide feed tools row</label>
                  <label class="eu-smart-check"><input id="eu-set-feed-pinned" type="checkbox"> Keep feed tools pinned</label>
                </div>
              </div>
              <div class="eu-smart-actions">
                <button class="eu-btn-primary" id="eu-save-settings">${ICONS.check}<span>Save Settings</span></button>
                <button class="eu-btn-secondary" id="eu-reset-settings">Reset Defaults</button>
              </div>
            </div>
          </div>`;
        document.body.appendChild(overlay);
        overlay.querySelector('.eu-modal-close').onclick = () => overlay.classList.remove('open');
        overlay.addEventListener('click', e => { if (e.target === overlay) overlay.classList.remove('open'); });
        overlay.querySelector('#eu-save-settings').onclick = saveSettingsFromModal;
        overlay.querySelector('#eu-reset-settings').onclick = () => {
            if (!confirm('Reset Erome Ultimate settings to defaults? Tracking history is not deleted.')) return;
            Object.assign(SETTINGS, DEFAULT_SETTINGS);
            saveSettings();
            syncSettingsModal();
            applySettingsNow();
            toast('Settings reset', 'success');
        };
    }

    function syncSettingsModal() {
        const o = document.getElementById('eu-settings-modal');
        if (!o) return;
        o.querySelector('#eu-set-feed-type').value = SETTINGS.defaultFeedType || 'all';
        o.querySelector('#eu-set-feed-layout').value = SETTINGS.defaultFeedLayout || 'desktop';
        o.querySelector('#eu-set-performance').value = SETTINGS.performanceMode || 'normal';
        o.querySelector('#eu-set-fit').value = SETTINGS.fitMode || 'contain';
        o.querySelector('#eu-set-min-seconds').value = getMinVideoSeconds();
        o.querySelector('#eu-set-max-seconds').value = getMaxVideoSeconds();
        o.querySelector('#eu-set-album-search').value = SETTINGS.albumSearch || '';
        o.querySelector('#eu-set-autoplay').checked = SETTINGS.autoplay !== false;
        o.querySelector('#eu-set-mute').checked = !!SETTINGS.muteVideos;
        o.querySelector('#eu-set-loop').checked = SETTINGS.loopVideos !== false;
        o.querySelector('#eu-set-resume').checked = SETTINGS.autoResume !== false;
        o.querySelector('#eu-set-badges').checked = SETTINGS.showSeenBadges !== false;
        o.querySelector('#eu-set-hide-seen').checked = !!SETTINGS.hideSeen;
        o.querySelector('#eu-set-hide-down').checked = !!SETTINGS.hideDownloaded;
        o.querySelector('#eu-set-skip-down').checked = !!SETTINGS.skipDownloadedInDownloads;
        o.querySelector('#eu-set-folders').checked = SETTINGS.zipFolders !== false;
        o.querySelector('#eu-set-retry').checked = SETTINGS.retryDownloads !== false;
        o.querySelector('#eu-set-dlbtns').checked = SETTINGS.showDownloadButtons !== false;
        o.querySelector('#eu-set-lock-search').checked = SETTINGS.lockFeedToSearch !== false;
        o.querySelector('#eu-set-related').checked = SETTINGS.allowRelatedAlbumExpansion !== false;
        o.querySelector('#eu-set-strict-search').checked = SETTINGS.strictSearchTitleMatch === true;
        o.querySelector('#eu-set-feed-auto-hide').checked = SETTINGS.feedControlsAutoHide !== false;
        o.querySelector('#eu-set-feed-pinned').checked = SETTINGS.feedControlsPinned === true;
    }

    function saveSettingsFromModal() {
        const o = document.getElementById('eu-settings-modal');
        if (!o) return;
        updateSetting('defaultFeedType', o.querySelector('#eu-set-feed-type').value);
        updateSetting('defaultFeedLayout', o.querySelector('#eu-set-feed-layout').value);
        updateSetting('performanceMode', o.querySelector('#eu-set-performance').value);
        updateSetting('fitMode', o.querySelector('#eu-set-fit').value);
        const minSeconds = Math.max(0, Math.floor(Number(o.querySelector('#eu-set-min-seconds').value) || 0));
        const maxSeconds = Math.max(0, Math.floor(Number(o.querySelector('#eu-set-max-seconds').value) || 0));
        if (minSeconds > 0 && maxSeconds > 0 && minSeconds > maxSeconds) {
            toast('Min length cannot be higher than max length', 'error');
            return;
        }
        updateSetting('minVideoSeconds', minSeconds);
        updateSetting('hiddenSeconds', minSeconds);
        updateSetting('maxVideoSeconds', maxSeconds);
        updateSetting('albumSearch', o.querySelector('#eu-set-album-search').value.trim());
        updateSetting('autoplay', o.querySelector('#eu-set-autoplay').checked);
        updateSetting('muteVideos', o.querySelector('#eu-set-mute').checked);
        updateSetting('loopVideos', o.querySelector('#eu-set-loop').checked);
        updateSetting('autoResume', o.querySelector('#eu-set-resume').checked);
        updateSetting('showSeenBadges', o.querySelector('#eu-set-badges').checked);
        updateSetting('hideSeen', o.querySelector('#eu-set-hide-seen').checked);
        updateSetting('hideDownloaded', o.querySelector('#eu-set-hide-down').checked);
        updateSetting('skipDownloadedInDownloads', o.querySelector('#eu-set-skip-down').checked);
        updateSetting('zipFolders', o.querySelector('#eu-set-folders').checked);
        updateSetting('retryDownloads', o.querySelector('#eu-set-retry').checked);
        updateSetting('showDownloadButtons', o.querySelector('#eu-set-dlbtns').checked);
        updateSetting('lockFeedToSearch', o.querySelector('#eu-set-lock-search').checked);
        updateSetting('allowRelatedAlbumExpansion', o.querySelector('#eu-set-related').checked);
        updateSetting('strictSearchTitleMatch', o.querySelector('#eu-set-strict-search').checked);
        updateSetting('feedControlsAutoHide', o.querySelector('#eu-set-feed-auto-hide').checked);
        updateSetting('feedControlsPinned', o.querySelector('#eu-set-feed-pinned').checked);
        applySettingsNow();
        toast('Settings saved', 'success');
    }

    function applySettingsNow() {
        STATE.tiktokType = SETTINGS.defaultFeedType || STATE.tiktokType;
        STATE.tiktokLayout = SETTINGS.defaultFeedLayout || STATE.tiktokLayout;
        STATE.showDownloadButtons = SETTINGS.showDownloadButtons !== false;
        STATE.hiddenSeconds = getMinVideoSeconds();
        STATE.maxVideoSeconds = getMaxVideoSeconds();
        document.querySelectorAll('.eu-dl-btn').forEach(btn => { btn.style.display = STATE.showDownloadButtons ? '' : 'none'; });
        applyHiddenFilter();
        applyAlbumSearch();
        decorateTrackingBadges();
        applyFeedFitMode();
        updateFeedTools();
        if (document.getElementById('eu-tiktok')) {
            if (SETTINGS.feedControlsPinned || SETTINGS.feedControlsAutoHide === false) setFeedControlsVisible(true, false);
            else scheduleFeedControlsHide(900);
        }
    }

    function openSettingsModal() {
        buildSettingsModal();
        syncSettingsModal();
        document.getElementById('eu-settings-modal').classList.add('open');
    }

    function getStatusStats() {
        return {
            albums: document.querySelectorAll('.album').length,
            pageMedia: collectAllMediaUrls().length,
            feedItems: TT.items.length,
            queue: TT.albumQueue.length,
            seen: TRACKING.seenAlbums.size,
            downloaded: TRACKING.downloadedMedia.size,
            favorites: TRACKING.favorites.length,
            failures: DLX.failed.length,
            scope: TT.feedScopeLabel || (SETTINGS.lockFeedToSearch !== false ? 'Search/listing lock' : 'Related expansion')
        };
    }

    function buildStatusPanel() {
        if (document.getElementById('eu-status-modal')) return;
        const overlay = document.createElement('div');
        overlay.id = 'eu-status-modal';
        overlay.className = 'eu-modal-overlay';
        overlay.innerHTML = `
          <div class="eu-modal" style="width:620px;">
            <div class="eu-modal-header">
              <h3>${ICONS.bell}<span>Status / Debug</span></h3>
              <button class="eu-modal-close">${ICONS.close}</button>
            </div>
            <div class="eu-modal-body">
              <div class="eu-status-grid" id="eu-status-grid"></div>
              <div class="eu-zip-preview" id="eu-status-line"></div>
              <div class="eu-smart-actions">
                <button class="eu-btn-primary" id="eu-refresh-now">${ICONS.check}<span>Refresh UI</span></button>
                <button class="eu-btn-secondary" id="eu-export-tracking">Export Tracking</button>
                <button class="eu-btn-secondary" id="eu-clear-seen">Clear Seen</button>
                <button class="eu-btn-secondary" id="eu-clear-downloaded">Clear Downloaded</button>
              </div>
            </div>
          </div>`;
        document.body.appendChild(overlay);
        overlay.querySelector('.eu-modal-close').onclick = () => overlay.classList.remove('open');
        overlay.addEventListener('click', e => { if (e.target === overlay) overlay.classList.remove('open'); });
        overlay.querySelector('#eu-refresh-now').onclick = () => { refreshAll(); updateStatusPanel(); toast('UI refreshed', 'success'); };
        overlay.querySelector('#eu-export-tracking').onclick = exportTrackingData;
        overlay.querySelector('#eu-clear-seen').onclick = () => {
            if (!confirm('Clear seen album history?')) return;
            TRACKING.seenAlbums.clear(); saveLimitedSet('eu_seen_albums_v1', TRACKING.seenAlbums); decorateTrackingBadges(); updateStatusPanel();
        };
        overlay.querySelector('#eu-clear-downloaded').onclick = () => {
            if (!confirm('Clear downloaded media/album history?')) return;
            TRACKING.downloadedMedia.clear(); TRACKING.downloadedAlbums.clear();
            saveLimitedSet('eu_downloaded_media_v1', TRACKING.downloadedMedia); saveLimitedSet('eu_downloaded_albums_v1', TRACKING.downloadedAlbums);
            decorateTrackingBadges(); updateStatusPanel();
        };
    }

    function updateStatusPanel() {
        const overlay = document.getElementById('eu-status-modal');
        if (!overlay) return;
        const s = getStatusStats();
        const cards = [
            ['Albums', s.albums], ['Page media', s.pageMedia], ['Feed items', s.feedItems], ['Queue', s.queue],
            ['Seen albums', s.seen], ['Downloaded', s.downloaded], ['Favorites', s.favorites], ['Failures', s.failures]
        ];
        overlay.querySelector('#eu-status-grid').innerHTML = cards.map(([label, val]) => `<div class="eu-status-card"><b>${val}</b><span>${label}</span></div>`).join('');
        overlay.querySelector('#eu-status-line').textContent = `Mode: ${SETTINGS.performanceMode} · Feed: ${SETTINGS.defaultFeedLayout}/${SETTINGS.defaultFeedType} · Scope: ${s.scope} · ${getLengthFilterLabel()} · Page: ${normalizeUrl(location.href)}`;
    }

    function openStatusPanel() {
        buildStatusPanel();
        updateStatusPanel();
        document.getElementById('eu-status-modal').classList.add('open');
    }

    function exportTrackingData() {
        const data = {
            exportedAt: new Date().toISOString(),
            settings: SETTINGS,
            seenAlbums: Array.from(TRACKING.seenAlbums),
            downloadedMedia: Array.from(TRACKING.downloadedMedia),
            downloadedAlbums: Array.from(TRACKING.downloadedAlbums),
            favorites: TRACKING.favorites
        };
        exportTextFile(`Erome_Ultimate_tracking_${Date.now()}.json`, JSON.stringify(data, null, 2));
    }

    function collectDownloadItems(source = 'auto', type = 'all') {
        let items = [];
        const feedOpen = STATE.tiktokMode && TT.items.length > 0;
        if ((source === 'feed') || (source === 'auto' && feedOpen)) {
            items = TT.items.map((item, idx) => ({
                url: item.url,
                kind: item.kind,
                albumUrl: item.albumUrl || '',
                title: item.title || `Feed_${idx + 1}`,
                index: idx + 1
            }));
        } else {
            items = collectAllMediaUrls().map((url, idx) => ({
                url,
                kind: isImageUrl(url) ? 'image' : 'video',
                albumUrl: currentAlbumUrl(),
                title: getPageTitle(),
                index: idx + 1
            }));
        }
        if (type === 'images') items = items.filter(i => i.kind === 'image' || isImageUrl(i.url));
        if (type === 'videos') items = items.filter(i => i.kind === 'video' || isVideoUrl(i.url));
        const seen = new Set();
        items = items.filter(i => {
            const clean = normalizeUrl(i.url);
            if (!clean || seen.has(clean)) return false;
            seen.add(clean);
            if (SETTINGS.skipDownloadedInDownloads && TRACKING.downloadedMedia.has(clean)) return false;
            return true;
        });
        return items;
    }

    function buildDownloadManager() {
        if (document.getElementById('eu-dlx-modal')) return;
        const overlay = document.createElement('div');
        overlay.id = 'eu-dlx-modal';
        overlay.className = 'eu-modal-overlay';
        overlay.innerHTML = `
          <div class="eu-modal" style="width:620px;">
            <div class="eu-modal-header">
              <h3>${ICONS.download}<span>Smart Download Manager</span></h3>
              <button class="eu-modal-close">${ICONS.close}</button>
            </div>
            <div class="eu-modal-body">
              <div class="eu-smart-row">
                <div class="eu-smart-field"><label>Source</label><select id="eu-dlx-source"><option value="auto">Auto/current feed</option><option value="page">Current page</option><option value="feed">Loaded feed queue</option></select></div>
                <div class="eu-smart-field"><label>Media Type</label><select id="eu-dlx-type"><option value="all">All</option><option value="images">Images</option><option value="videos">Videos</option></select></div>
              </div>
              <div class="eu-option-group">
                <div class="eu-option-title">Options</div>
                <div class="eu-smart-checks">
                  <label class="eu-smart-check"><input id="eu-dlx-zip" type="checkbox" checked> Package as ZIP</label>
                  <label class="eu-smart-check"><input id="eu-dlx-folders" type="checkbox"> Folder by album/title</label>
                  <label class="eu-smart-check"><input id="eu-dlx-skip" type="checkbox"> Skip downloaded</label>
                  <label class="eu-smart-check"><input id="eu-dlx-retry" type="checkbox"> Retry failures</label>
                </div>
              </div>
              <div class="eu-zip-preview" id="eu-dlx-stats">Scanning…</div>
              <div class="eu-smart-actions">
                <button class="eu-btn-primary" id="eu-dlx-start">${ICONS.download}<span>Start</span></button>
                <button class="eu-btn-secondary" id="eu-dlx-cancel">Cancel</button>
              </div>
              <div class="eu-progress-box">
                <div class="eu-progress-bar"><div class="eu-progress-fill" id="eu-dlx-fill"></div></div>
                <div class="eu-progress-text" id="eu-dlx-text">Ready</div>
              </div>
              <div class="eu-dlx-log" id="eu-dlx-log">No downloads started.</div>
            </div>
          </div>`;
        document.body.appendChild(overlay);
        overlay.querySelector('.eu-modal-close').onclick = () => overlay.classList.remove('open');
        overlay.addEventListener('click', e => { if (e.target === overlay) overlay.classList.remove('open'); });
        ['#eu-dlx-source', '#eu-dlx-type', '#eu-dlx-skip'].forEach(sel => overlay.querySelector(sel).addEventListener('change', updateDownloadManagerStats));
        overlay.querySelector('#eu-dlx-folders').checked = SETTINGS.zipFolders !== false;
        overlay.querySelector('#eu-dlx-skip').checked = !!SETTINGS.skipDownloadedInDownloads;
        overlay.querySelector('#eu-dlx-retry').checked = SETTINGS.retryDownloads !== false;
        overlay.querySelector('#eu-dlx-start').onclick = runDownloadManager;
        overlay.querySelector('#eu-dlx-cancel').onclick = () => { DLX.cancel = true; toast('Download cancel requested', 'warning'); };
    }

    function updateDownloadManagerStats() {
        const overlay = document.getElementById('eu-dlx-modal');
        if (!overlay) return;
        const oldSkip = SETTINGS.skipDownloadedInDownloads;
        SETTINGS.skipDownloadedInDownloads = overlay.querySelector('#eu-dlx-skip').checked;
        const items = collectDownloadItems(overlay.querySelector('#eu-dlx-source').value, overlay.querySelector('#eu-dlx-type').value);
        SETTINGS.skipDownloadedInDownloads = oldSkip;
        const imgs = items.filter(i => i.kind === 'image' || isImageUrl(i.url)).length;
        const vids = items.filter(i => i.kind === 'video' || isVideoUrl(i.url)).length;
        overlay.querySelector('#eu-dlx-stats').textContent = `Ready: ${items.length} items (${imgs} images, ${vids} videos). Feed loaded: ${TT.items.length}. Queue: ${TT.albumQueue.length}.`;
    }

    function openDownloadManager() {
        buildDownloadManager();
        updateDownloadManagerStats();
        document.getElementById('eu-dlx-modal').classList.add('open');
    }

    async function runDownloadManager() {
        if (DLX.running || STATE.downloadInProgress) { toast('A download is already running', 'warning'); return; }
        const overlay = document.getElementById('eu-dlx-modal');
        const source = overlay.querySelector('#eu-dlx-source').value;
        const type = overlay.querySelector('#eu-dlx-type').value;
        const asZip = overlay.querySelector('#eu-dlx-zip').checked;
        updateSetting('zipFolders', overlay.querySelector('#eu-dlx-folders').checked);
        updateSetting('skipDownloadedInDownloads', overlay.querySelector('#eu-dlx-skip').checked);
        updateSetting('retryDownloads', overlay.querySelector('#eu-dlx-retry').checked);
        const items = collectDownloadItems(source, type);
        const fill = overlay.querySelector('#eu-dlx-fill');
        const text = overlay.querySelector('#eu-dlx-text');
        const log = overlay.querySelector('#eu-dlx-log');
        if (!items.length) { toast('No downloadable items found', 'error'); return; }

        DLX.running = true; DLX.cancel = false; DLX.done = 0; DLX.ok = 0; DLX.total = items.length; DLX.failed = [];
        STATE.downloadInProgress = true;
        fill.style.width = '0%'; log.textContent = '';

        const addLog = line => { log.textContent += line + '\n'; log.scrollTop = log.scrollHeight; };
        try {
            if (asZip && typeof JSZip !== 'undefined') {
                const zip = new JSZip();
                for (let i = 0; i < items.length; i++) {
                    if (DLX.cancel) break;
                    const item = items[i];
                    const base = `${String(i + 1).padStart(4, '0')}_${getFilename(item.url)}`;
                    const folder = SETTINGS.zipFolders ? sanitize(item.title || getPageTitle()) + '/' : '';
                    text.textContent = `Downloading ${i + 1}/${items.length}: ${base}`;
                    fill.style.width = `${(i / items.length) * 100}%`;
                    try {
                        const blob = await downloadBlobWithRetry(item.url, SETTINGS.retryDownloads ? 2 : 1);
                        zip.file(folder + base, await blob.arrayBuffer());
                        markDownloadedMedia(item.url, item.albumUrl);
                        DLX.ok++;
                        addLog(`OK  ${base}`);
                    } catch (e) {
                        DLX.failed.push({ url: item.url, error: String(e) });
                        addLog(`ERR ${base} — ${e}`);
                    }
                    DLX.done++;
                }
                if (DLX.failed.length) {
                    zip.file('FAILED_DOWNLOADS.txt', DLX.failed.map(f => `${f.url}\n${f.error}\n`).join('\n'));
                }
                text.textContent = 'Generating ZIP…';
                const zipBlob = await zip.generateAsync({ type: 'blob', compression: 'DEFLATE', compressionOptions: { level: 6 } }, meta => {
                    fill.style.width = `${meta.percent}%`;
                    text.textContent = `Compressing: ${Math.round(meta.percent)}%`;
                });
                if (!DLX.cancel) {
                    if (typeof saveAs === 'function') saveAs(zipBlob, generateZipName());
                    else {
                        const u = URL.createObjectURL(zipBlob);
                        const a = document.createElement('a');
                        a.href = u; a.download = generateZipName(); a.style.display = 'none';
                        document.body.appendChild(a); a.click(); a.remove();
                        setTimeout(() => URL.revokeObjectURL(u), 5000);
                    }
                }
            } else {
                for (let i = 0; i < items.length; i++) {
                    if (DLX.cancel) break;
                    const item = items[i];
                    const name = `${String(i + 1).padStart(4, '0')}_${getFilename(item.url)}`;
                    text.textContent = `Downloading ${i + 1}/${items.length}: ${name}`;
                    fill.style.width = `${(i / items.length) * 100}%`;
                    try {
                        const blob = await downloadBlobWithRetry(item.url, SETTINGS.retryDownloads ? 2 : 1);
                        const u = URL.createObjectURL(blob);
                        const a = document.createElement('a');
                        a.href = u; a.download = name; a.style.display = 'none'; document.body.appendChild(a); a.click(); a.remove();
                        setTimeout(() => URL.revokeObjectURL(u), 5000);
                        markDownloadedMedia(item.url, item.albumUrl);
                        DLX.ok++;
                        addLog(`OK  ${name}`);
                    } catch (e) {
                        DLX.failed.push({ url: item.url, error: String(e) });
                        addLog(`ERR ${name} — ${e}`);
                    }
                    DLX.done++;
                    await new Promise(r => setTimeout(r, SETTINGS.performanceMode === 'aggressive' ? 120 : 350));
                }
                if (DLX.failed.length) exportTextFile(`FAILED_DOWNLOADS_${Date.now()}.txt`, DLX.failed.map(f => `${f.url}\n${f.error}\n`).join('\n'));
            }
            fill.style.width = '100%';
            text.textContent = DLX.cancel ? `Canceled (${DLX.ok}/${items.length})` : `Complete (${DLX.ok}/${items.length})`;
            toast(text.textContent, DLX.cancel ? 'warning' : 'success');
        } catch (e) {
            console.error('[EU] Download manager failed:', e);
            toast('Download manager error', 'error');
        } finally {
            DLX.running = false; STATE.downloadInProgress = false;
            decorateTrackingBadges(); updateDownloadManagerStats(); updateStatusPanel();
        }
    }

    function buildHubMenu(id, iconKey, items, title = '') {
        if (document.getElementById(id)) return null;
        const hub = document.createElement('div');
        hub.id = id;
        hub.className = 'eu-hub';
        hub.innerHTML = `
          <div class="eu-hub-btn" title="${title}">${ICONS[iconKey] || ICONS.settings}</div>
          <ul class="eu-hub-menu"></ul>`;
        const menu = hub.querySelector('.eu-hub-menu');
        items.forEach(item => {
            const li = document.createElement('li');
            li.className = 'eu-hub-item';
            li.innerHTML = `${ICONS[item.icon] || ICONS.settings}<span>${item.label}</span>`;
            li.addEventListener('click', (e) => {
                e.stopPropagation();
                item.action?.();
                hub.classList.remove('open');
            });
            menu.appendChild(li);
        });

        hub.querySelector('.eu-hub-btn').addEventListener('click', (e) => {
            e.stopPropagation();
            document.querySelectorAll('.eu-hub.open').forEach(h => { if (h !== hub) h.classList.remove('open'); });
            hub.classList.toggle('open');
        });

        return hub;
    }

    function installHubs() {
        const navToggle = document.querySelector('.navbar-toggle');
        if (!navToggle) return;
        const target = navToggle.parentNode;
        const ref = navToggle.nextSibling;

        // Privacy hub
        const privacyItems = [
            { label: 'Settings', icon: 'settings', action: openSettingsModal },
            { label: 'Status / Debug', icon: 'bell', action: openStatusPanel },
            { label: 'Smart Download Manager', icon: 'download', action: openDownloadManager },
            { label: 'Toggle NSFW Blur', icon: 'eye', action: toggleNSFW },
            { label: 'Toggle Hidden Filter', icon: 'clock', action: () => {
                const s = document.querySelector('.eu-hidden-slider');
                if (s) s.classList.toggle('visible');
                toast('Hidden filter ' + (s?.classList.contains('visible') ? 'on' : 'off'), 'info');
            } }
        ];
        const privacyHub = buildHubMenu('eu-privacy-hub', 'eye', privacyItems, 'Privacy');
        if (privacyHub) target.insertBefore(privacyHub, ref);

        // View hub (listing pages)
        if (!IS_ALBUM_PAGE) {
            const viewItems = [
                { label: 'Desktop Feed — All', icon: 'cinema', action: () => openDesktopFeed('all') },
                { label: 'Desktop Feed — Videos', icon: 'video', action: () => openDesktopFeed('videos') },
                { label: 'Desktop Feed — Photos', icon: 'photo', action: () => openDesktopFeed('photos') },
                { label: 'Search Loaded Albums', icon: 'eye', action: openAlbumSearch },
                { label: SETTINGS.lockFeedToSearch !== false ? 'Feed Search Lock: On' : 'Feed Search Lock: Off', icon: 'eye', action: () => { updateSetting('lockFeedToSearch', !(SETTINGS.lockFeedToSearch !== false)); toast(SETTINGS.lockFeedToSearch !== false ? 'Feed locked to current search/listing' : 'Feed can expand into related albums', 'info'); } },
                { label: SETTINGS.hideSeen ? 'Show Seen Albums' : 'Hide Seen Albums', icon: 'eye', action: () => { updateSetting('hideSeen', !SETTINGS.hideSeen); decorateTrackingBadges(); toast(SETTINGS.hideSeen ? 'Seen albums hidden' : 'Seen albums shown', 'info'); } },
                { label: SETTINGS.hideDownloaded ? 'Show Downloaded Albums' : 'Hide Downloaded Albums', icon: 'download', action: () => { updateSetting('hideDownloaded', !SETTINGS.hideDownloaded); decorateTrackingBadges(); toast(SETTINGS.hideDownloaded ? 'Downloaded albums hidden' : 'Downloaded albums shown', 'info'); } },
                { label: 'Sort by Views', icon: 'sort', action: () => sortAlbums('views') },
                { label: 'Sort by Video Count', icon: 'video', action: () => sortAlbums('videos') },
                { label: 'Sort by Photo Count', icon: 'photo', action: () => sortAlbums('photos') },
                { label: 'Toggle Sort Direction', icon: 'sort', action: () => {
                    STATE.sortAscending = !STATE.sortAscending;
                    sortAlbums(STATE.sortMode);
                } },
                { label: 'Video-Only Albums', icon: 'video', action: toggleVideoOnly }
            ];
            const viewHub = buildHubMenu('eu-view-hub', 'sort', viewItems, 'View & Sort');
            if (viewHub) target.insertBefore(viewHub, ref);
        }

        // Album hub (album pages)
        if (IS_ALBUM_PAGE) {
            const albumItems = [
                { label: 'TikTok Mode — Videos', icon: 'video', action: () => openTikTok('videos', 'phone') },
                { label: 'TikTok Mode — Photos', icon: 'photo', action: () => openTikTok('photos', 'phone') },
                { label: 'TikTok Mode — All', icon: 'cinema', action: () => openTikTok('all', 'phone') },
                { label: 'Desktop Feed — All', icon: 'cinema', action: () => openDesktopFeed('all') },
                { label: 'Desktop Feed — Videos', icon: 'video', action: () => openDesktopFeed('videos') },
                { label: 'Desktop Feed — Photos', icon: 'photo', action: () => openDesktopFeed('photos') },
                { label: SETTINGS.allowRelatedAlbumExpansion !== false ? 'Album Next Loading: On' : 'Album Next Loading: Off', icon: 'eye', action: () => { updateSetting('allowRelatedAlbumExpansion', !(SETTINGS.allowRelatedAlbumExpansion !== false)); toast(SETTINGS.allowRelatedAlbumExpansion !== false ? 'Album feed will load next albums' : 'Album feed limited to current album', 'info'); } },
                { label: 'Bulk Download…', icon: 'download', action: openBulkModal },
                { label: 'Smart Download Manager', icon: 'download', action: openDownloadManager },
                { label: 'Toggle Download Buttons', icon: 'upload', action: toggleDownloadButtons },
                { label: 'Toggle Photos', icon: 'photo', action: togglePhotos },
                { label: 'Toggle Videos', icon: 'video', action: toggleVideos },
                { label: 'Cinema Mode', icon: 'cinema', action: toggleCinema }
            ];
            const albumHub = buildHubMenu('eu-album-hub', 'settings', albumItems, 'Album Tools');
            if (albumHub) target.insertBefore(albumHub, ref);
        }

        // Close hubs on outside click
        document.addEventListener('click', () => {
            document.querySelectorAll('.eu-hub.open').forEach(h => h.classList.remove('open'));
        });
    }

    /* ============================================================
     *  DISCLAIMER / ACCOUNT BYPASS
     * ============================================================ */
    function bypass() {
        const disc = document.getElementById('disclaimer');
        if (disc) {
            disc.remove();
            document.body.style.overflow = 'visible';
            try {
                if (typeof $ !== 'undefined') $.ajax({ type: 'POST', url: '/user/disclaimer', async: true });
                else fetch('/user/disclaimer', { method: 'POST' });
            } catch (e) {}
        }
        const needAcc = document.getElementById('needAccount');
        if (needAcc) needAcc.remove();
    }

    /* ============================================================
     *  INFINITE SCROLL (listing pages)
     * ============================================================ */
    function installInfiniteScroll() {
        if (IS_ALBUM_PAGE) return;
        if (!document.querySelector('.pagination')) return;

        const container = document.querySelector('#albums') ||
            document.querySelector('.user-albums') ||
            document.querySelector('.albums') ||
            document.querySelector('.page-content');
        if (!container) return;

        const url = new URL(window.location.href);
        const currentPage = parseInt(url.searchParams.get('page'), 10) || 1;
        let nextPage = currentPage + 1;
        let loading = false;
        let stopped = false;
        let emptyHits = 0;

        const albumKey = (album, baseUrl = location.href) => {
            const link = album.querySelector('a.album-link, a[href*="/a/"]');
            const href = link?.getAttribute('href') || link?.href;
            return href ? new URL(href, baseUrl).href : album.textContent.replace(/\s+/g, ' ').trim().slice(0, 200);
        };

        const seenAlbums = new Set(Array.from(document.querySelectorAll('.album')).map(a => albumKey(a)));

        const nextPageUrl = () => {
            url.searchParams.set('page', nextPage);
            return url.href;
        };

        async function loadNextPage() {
            if (loading || stopped) return;
            loading = true;

            const pageUrl = nextPageUrl();
            try {
                const res = await fetch(pageUrl, { credentials: 'include' });
                if (!res.ok) throw new Error(`HTTP ${res.status}`);

                const html = await res.text();
                const doc = new DOMParser().parseFromString(html, 'text/html');
                const albums = Array.from(doc.querySelectorAll('.album'));
                let added = 0;

                for (const album of albums) {
                    const key = albumKey(album, pageUrl);
                    if (!key || seenAlbums.has(key)) continue;
                    seenAlbums.add(key);
                    container.appendChild(document.importNode(album, true));
                    added++;
                }

                nextPage++;

                if (added === 0) emptyHits++;
                else emptyHits = 0;

                refreshAll();
                decorateTrackingBadges();
                applyAlbumSearch();
                if (SETTINGS.performanceMode !== 'lite') loadAlbumLikes();

                if (emptyHits >= 2) {
                    stopped = true;
                    window.removeEventListener('scroll', onScroll);
                    toast('Unlimited loading complete — no more new albums found', 'info', 3000);
                }
            } catch (e) {
                stopped = true;
                window.removeEventListener('scroll', onScroll);
                console.warn('[EU] Unlimited scroll stopped:', e);
                toast('Unlimited loading stopped — no more pages found', 'info', 3000);
            } finally {
                loading = false;
            }
        }

        function onScroll() {
            const docEl = document.documentElement;
            const nearBottom = window.innerHeight + window.scrollY >= docEl.scrollHeight - 1200;
            if (nearBottom) loadNextPage();
        }

        window.addEventListener('scroll', onScroll, { passive: true });
        onScroll();
    }

    /* ============================================================
     *  PERIODIC REFRESH — runs all decorators
     * ============================================================ */
    function refreshAll() {
        attachDownloadButtons();
        decorateAlbumCounts();
        decorateVideoDurations();
        applyNSFW();
        applyVideoOnly();
        applyHiddenFilter();
        decorateTrackingBadges();
        applyAlbumSearch();
        updateFabBadge();
        enhancePlayers();
    }

    /* ============================================================
     *  INITIALIZATION
     * ============================================================ */
    function init() {
        console.log(`[${CONFIG.brand}] v${CONFIG.version} initializing…`);

        bypass();
        installHubs();
        buildFAB();
        createHiddenSlider();
        if (IS_ALBUM_PAGE) markSeenAlbum(location.href);
        refreshAll();
        if (SETTINGS.performanceMode !== 'lite') loadAlbumLikes();
        installInfiniteScroll();
        applySettingsNow();

        // Periodic refresh for dynamic content; Lite mode scans less often.
        const refreshMs = SETTINGS.performanceMode === 'lite' ? 6500 : (SETTINGS.performanceMode === 'aggressive' ? 1200 : 3000);
        setInterval(() => { if (!document.hidden || SETTINGS.performanceMode !== 'lite') refreshAll(); }, refreshMs);

        // MutationObserver for real-time updates
        const mo = new MutationObserver(() => {
            clearTimeout(window._euDebounce);
            const delay = SETTINGS.performanceMode === 'lite' ? 900 : 300;
            window._euDebounce = setTimeout(refreshAll, delay);
        });
        mo.observe(document.body, { childList: true, subtree: true });

        // Welcome toast
        setTimeout(() => {
            const n = collectAllMediaUrls().length;
            toast(`${CONFIG.brand} ready — ${n} media items detected`, 'success', 3500);
        }, 1200);

        console.log(`[${CONFIG.brand}] ready`);
    }

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }

    window.addEventListener('load', () => { setTimeout(refreshAll, 1000); });

    // Expose API for debugging
    window.eromeUltimate = {
        state: STATE,
        config: CONFIG,
        collectAllMediaUrls,
        openBulkModal,
        openTikTok,
        openDesktopFeed,
        openSettingsModal,
        openStatusPanel,
        openDownloadManager,
        tracking: TRACKING,
        settings: SETTINGS,
        toast,
        refreshAll,
        resetFeedScope,
        buildNextAlbumQueue
    };
})();