☰

Erome Ultimate Premium — By Insomnia

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==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
    };
})();