☰

EH Twitter Style

将 E-Hentai 列表页和画廊详情页改造成 Twitter/X 风格,并为阅读页加入黑底连续预载和移动端适配

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey, Greasemonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да инсталирате разширение, като например Tampermonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Userscripts.

За да инсталирате скрипта, трябва да инсталирате разширение като Tampermonkey.

За да инсталирате този скрипт, трябва да имате инсталиран скриптов мениджър.

(Вече имам скриптов мениджър, искам да го инсталирам!)

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

(Вече имам инсталиран мениджър на стиловете, искам да го инсталирам!)

// ==UserScript==
// @name         EH Twitter Style
// @namespace    https://e-hentai.org/
// @version      1.17.280
// @license      PolyForm-Noncommercial-1.0.0
// @description  将 E-Hentai 列表页和画廊详情页改造成 Twitter/X 风格,并为阅读页加入黑底连续预载和移动端适配
// @homepageURL  https://github.com/anohaca/eh-twitter-style
// @supportURL   https://github.com/anohaca/eh-twitter-style/issues
// @match        https://e-hentai.org/*
// @match        https://exhentai.org/*
// @match        https://upld.e-hentai.org/manage*
// @match        https://upld.e-hentai.org/bounty_post.php*
// @match        https://upld.exhentai.org/upld/*
// @grant        none
// @run-at       document-start
// ==/UserScript==

(function () {
    'use strict';

    const injectionGuardKey = '__EHTW_USER_SCRIPT_INJECTION_ONCE__';
    if (window[injectionGuardKey]) return;
    window[injectionGuardKey] = true;
    let spaRenderedHref = location.href;
    // 1.17.255: declared up front. On ExHentai an early scroll save reaches
    // findSuspendedSpaPageByHref before the SPA section below has run (TDZ).
    const suspendedSpaPages = new Map();

    const NAV_LOG_SWITCH_KEY = 'ehtw-navigation-log-enabled-v1';
    const NAV_LOG_SESSION_KEY = 'ehtw-navigation-log-session-v1';
    const SPA_SUSPEND_CACHE_SWITCH_KEY = 'ehtw-spa-suspend-cache-enabled-v1';
    const NAV_LOG_HEARTBEAT_KEY = 'ehtw-navigation-log-heartbeat-v1';
    const NAV_LOG_CLEAN_EXIT_KEY = 'ehtw-navigation-log-clean-exit-v1';
    const NAV_LOG_LIMIT = 500;
    const navLogApplicable = ['e-hentai.org', 'exhentai.org'].includes(String(location.hostname || '').toLowerCase());
    const navLogCommand = String(location.hash || '').slice(1).toLowerCase();
    const navLogPanelRequested = navLogCommand === 'ehtw-navlog';
    let spaSuspendCacheEnabled = true;
    let navLogEnabled = false;
    try {
        if (navLogCommand === 'ehtw-navlog-on') {
            navLogEnabled = true;
            window.localStorage?.setItem(NAV_LOG_SWITCH_KEY, '1');
        } else if (navLogCommand === 'ehtw-navlog-off') {
            window.localStorage?.setItem(NAV_LOG_SWITCH_KEY, '0');
        } else {
            navLogEnabled = window.localStorage?.getItem(NAV_LOG_SWITCH_KEY) === '1';
        }
    } catch (_) {}
    try {
        if (navLogCommand === 'ehtw-nocache-on') {
            window.localStorage?.setItem(SPA_SUSPEND_CACHE_SWITCH_KEY, '0');
            spaSuspendCacheEnabled = false;
        } else if (navLogCommand === 'ehtw-nocache-off') {
            window.localStorage?.setItem(SPA_SUSPEND_CACHE_SWITCH_KEY, '1');
            spaSuspendCacheEnabled = true;
        } else {
            spaSuspendCacheEnabled = window.localStorage?.getItem(SPA_SUSPEND_CACHE_SWITCH_KEY) !== '0';
        }
    } catch (_) {}
    let navLogEntries = [];
    let navLogTextarea = null;
    let navLogTitle = null;
    if (navLogEnabled || navLogPanelRequested) {
        try {
            const saved = JSON.parse(window.sessionStorage?.getItem(NAV_LOG_SESSION_KEY) || '[]');
            if (Array.isArray(saved)) navLogEntries = saved.slice(-NAV_LOG_LIMIT);
        } catch (_) {}
    }
    const summarizeNavLogState = (state) => {
        if (!state || typeof state !== 'object') return null;
        const keys = [
            'ehtwSpa', 'ehtwSpaRoute', 'ehtwEntryId', 'ehtwSpaDepth',
            'ehtwScrollY', 'ehtwSpaPreviousHref', 'ehtwSpaReturnBackTarget'
        ];
        return Object.fromEntries(keys.filter((key) => key in state).map((key) => [key, state[key]]));
    };
    const navLogText = () => navLogEntries.map((entry) => JSON.stringify(entry)).join('\n');
    const navLog = (event, details = {}, coordinatesOnly = false) => {
        if (!navLogEnabled) return;
        try {
            const entry = coordinatesOnly
                ? { timestamp: new Date().toISOString(), timeMs: Date.now(), event, ...details }
                : {
                    timestamp: new Date().toISOString(),
                    timeMs: Date.now(),
                    elapsedMs: Math.round(window.performance?.now?.() || 0),
                    event,
                    href: location.href,
                    spaRenderedHref: typeof spaRenderedHref === 'string' ? spaRenderedHref : null,
                    historyLength: window.history.length,
                    ...details
                };
            navLogEntries.push(entry);
            if (navLogEntries.length > NAV_LOG_LIMIT) navLogEntries.splice(0, navLogEntries.length - NAV_LOG_LIMIT);
            window.sessionStorage?.setItem(NAV_LOG_SESSION_KEY, JSON.stringify(navLogEntries));
            if (navLogTextarea) navLogTextarea.value = navLogText();
            if (navLogTitle) navLogTitle.textContent = `导航诊断日志(${navLogEntries.length}/${NAV_LOG_LIMIT})`;
        } catch (_) {}
    };
    if (navLogEnabled) {
        try {
            const previousHeartbeat = JSON.parse(window.sessionStorage?.getItem(NAV_LOG_HEARTBEAT_KEY) || 'null');
            const cleanExit = window.sessionStorage?.getItem(NAV_LOG_CLEAN_EXIT_KEY) === '1';
            window.sessionStorage?.removeItem(NAV_LOG_CLEAN_EXIT_KEY);
            if (previousHeartbeat && !cleanExit) {
                navLog('previous-document-no-clean-exit', {
                    previousHeartbeat,
                    intervalMs: Math.max(0, Date.now() - Number(previousHeartbeat.t || Date.now()))
                });
            }
        } catch (_) {}
    }
    const navLogStack = () => {
        try { return String(new Error().stack || '').slice(0, 800); } catch (_) { return ''; }
    };
    const MAIN_WORLD_REPORT_EVENT = '__ehtw_mainworld_report_v1';
    const MAIN_WORLD_MESSAGE_KEY = '__ehtw_mainworld_message_v1';
    const MAIN_WORLD_CONTROL_EVENT = '__ehtw_mainworld_control_v1';
    const MAIN_WORLD_NATIVE_API = '__EHTW_PAGE_WORLD_NATIVE_API_V1__';
    const addUserScriptListener = (target, type, listener, options) => {
        const nativeApi = window[MAIN_WORLD_NATIVE_API];
        if (nativeApi && (target === window || target === document)) return nativeApi.add(target, type, listener, options);
        return target?.addEventListener?.(type, listener, options);
    };
    const setUserScriptInterval = (...args) => {
        const nativeApi = window[MAIN_WORLD_NATIVE_API];
        return nativeApi ? nativeApi.setInterval(...args) : window.setInterval?.(...args);
    };
    const mainWorldScriptId = '__ehtw_mainworld_bridge_v1';
    let mainWorldInjectionAttempted = false;
    let mainWorldBridgeReady = false;
    const mainWorldPendingReports = [];
    const mainWorldSeenReportIds = new Set();
    const forwardMainWorldReport = (detail) => {
        if (!navLogEnabled || !detail || typeof detail !== 'object') return;
        if (detail.reportId != null) {
            if (mainWorldSeenReportIds.has(detail.reportId)) return;
            mainWorldSeenReportIds.add(detail.reportId);
            if (mainWorldSeenReportIds.size > 256) mainWorldSeenReportIds.delete(mainWorldSeenReportIds.values().next().value);
        }
        const { event: sourceEvent, ...report } = detail;
        navLog(`page-world-${String(sourceEvent || 'report')}`, { ...report, sourceEvent: String(sourceEvent || 'report') });
    };
    if (navLogEnabled) {
        window.addEventListener('message', (event) => {
            if (event.source !== window || event.data?.key !== MAIN_WORLD_MESSAGE_KEY) return;
            forwardMainWorldReport(event.data.detail);
        });
        document.addEventListener(MAIN_WORLD_REPORT_EVENT, (event) => forwardMainWorldReport(event.detail));
    }
    {
        const installMainWorldBridge = () => {
            if (mainWorldInjectionAttempted) return;
            if (!document.documentElement) {
                try {
                    const observer = new MutationObserver(() => {
                        if (!document.documentElement) return;
                        observer.disconnect();
                        installMainWorldBridge();
                    });
                    observer.observe(document, { childList: true, subtree: true });
                } catch (error) {
                    forwardMainWorldReport({ event: 'bridge-injection-failed', reason: `documentElement-unavailable:${String(error?.message || error).slice(0, 200)}` });
                }
                return;
            }
            mainWorldInjectionAttempted = true;
            try {
                if (navLogEnabled) navLog('page-world-install-attempt', { worldMarkerVisible: window.__EHTW_PAGE_WORLD_BRIDGE_V1__ === true, hasDocumentElement: true });
                const bridge = function (diagnosticsEnabled) {
                    if (window.__EHTW_PAGE_WORLD_BRIDGE_V1__) return;
                    try { delete window.__EHTW_PAGE_WORLD_PROBE_V1__; } catch (_) {}
                    const controlEvent = '__ehtw_mainworld_control_v1';
                    const messageKey = '__ehtw_mainworld_message_v1';
                    let reportSequence = 0;
                    const trackedTypes = new Set(['popstate', 'hashchange', 'pageshow', 'beforeunload']);
                    const tracked = new Map();
                    const trackedProperties = new Map();
                    const trackedIntervals = new Set();
                    let captureRouteScripts = true;
                    const readingHandlers = new Map();
                    let readerActive = /^\/s\//.test(location.pathname);
                    const sourceOf = (listener) => {
                        try { return String(typeof listener === 'function' ? listener : listener?.handleEvent || '').slice(0, 300); }
                        catch (_) { return '[source unavailable]'; }
                    };
                    const stackOf = () => {
                        try { return String(new Error().stack || '').slice(0, 500); }
                        catch (_) { return ''; }
                    };
                    const stateKeys = (state) => {
                        try { return state && typeof state === 'object' ? Object.keys(state).slice(0, 40) : []; }
                        catch (_) { return []; }
                    };
                    const send = (event, detail = {}) => {
                        if (!diagnosticsEnabled) return;
                        try {
                            const report = { reportId: ++reportSequence, event, ...detail };
                            window.postMessage({ key: messageKey, detail: report }, '*');
                            document.dispatchEvent(new CustomEvent('__ehtw_mainworld_report_v1', { detail: report }));
                        }
                        catch (_) {}
                    };
                    const snapshot = (reason) => {
                        const listeners = [];
                        for (const [type, records] of tracked) {
                            for (const record of records) listeners.push({ type, source: record.source, reader: record.reader });
                        }
                        send('listeners', {
                            reason,
                            counts: Object.fromEntries([...trackedTypes].map((type) => [type, tracked.get(`window:${type}`)?.length || 0])),
                            listeners: listeners.slice(0, 40)
                        });
                    };
                    const originalAdd = EventTarget.prototype.addEventListener;
                    const originalRemove = EventTarget.prototype.removeEventListener;
                    const originalSetInterval = window.setInterval;
                    Object.defineProperty(window, '__EHTW_PAGE_WORLD_NATIVE_API_V1__', {
                        configurable: true,
                        value: {
                            add(target, type, listener, options) { return Reflect.apply(originalAdd, target, [type, listener, options]); },
                            setInterval(...args) { return Reflect.apply(originalSetInterval, window, args); }
                        }
                    });
                    const removeFromTracking = (type, listener) => {
                        const key = `window:${type}`;
                        const list = tracked.get(key);
                        if (!list) return;
                        const index = list.findIndex((entry) => entry.original === listener);
                        if (index >= 0) list.splice(index, 1);
                        if (!list.length) tracked.delete(key);
                    };
                    EventTarget.prototype.addEventListener = function (type, listener, options) {
                        const target = this === window ? 'window' : this === document ? 'document' : '';
                        const isTrackedGlobal = !!target && !!listener && captureRouteScripts;
                        if (!isTrackedGlobal) return Reflect.apply(originalAdd, this, [type, listener, options]);
                        let actual = listener;
                        if (target === 'window' && readerActive && String(type) === 'popstate' ||
                            readerActive && /^(?:keydown|keyup|keypress)$/.test(String(type))) {
                            actual = function (event) {
                                if (readerActive) return;
                                if (typeof listener === 'function') return Reflect.apply(listener, this, [event]);
                                return listener.handleEvent.call(listener, event);
                            };
                            readingHandlers.set(listener, { wrapped: actual, options });
                        }
                        const key = `${target}:${String(type)}`;
                        const list = tracked.get(key) || [];
                        if (!list.some((entry) => entry.original === listener)) {
                            list.push({ target, type: String(type), original: listener, actual, options, source: sourceOf(listener), reader: !!readerActive });
                            tracked.set(key, list);
                        }
                        const result = Reflect.apply(originalAdd, this, [type, actual, options]);
                        if (String(type) === 'popstate') snapshot(`add:${String(type)}`);
                        return result;
                    };
                    EventTarget.prototype.removeEventListener = function (type, listener, options) {
                        const target = this === window ? 'window' : this === document ? 'document' : '';
                        const key = `${target}:${String(type)}`;
                        const list = tracked.get(key);
                        const entry = target && list?.find((record) => record.original === listener);
                        const result = Reflect.apply(originalRemove, this, [type, entry?.actual || listener, options]);
                        if (entry) {
                            const index = list.indexOf(entry);
                            if (index >= 0) list.splice(index, 1);
                            if (!list.length) tracked.delete(key);
                            if (String(type) === 'popstate') snapshot(`remove:${String(type)}`);
                        }
                        return result;
                    };
                    const propertyTargets = [['window', window], ['document', document]];
                    const propertyTypes = ['popstate', 'hashchange', 'keydown', 'keyup', 'keypress', 'scroll', 'resize', 'beforeunload', 'unload', 'pagehide', 'message', 'click', 'mousedown', 'touchstart'];
                    for (const [targetName, target] of propertyTargets) for (const type of propertyTypes) {
                        if (targetName === 'window' && type === 'popstate') continue;
                        const property = `on${type}`;
                        let descriptor = null;
                        for (let proto = target; proto && !descriptor; proto = Object.getPrototypeOf(proto)) descriptor = Object.getOwnPropertyDescriptor(proto, property) || null;
                        if (!descriptor?.configurable || !descriptor.set || !descriptor.get) continue;
                        const key = `${targetName}:${property}`;
                        let current = null;
                        try { current = Reflect.apply(descriptor.get, target, []); } catch (_) {}
                        Object.defineProperty(target, property, {
                            configurable: true, enumerable: descriptor.enumerable,
                            get() { return current; },
                            set(listener) {
                                current = listener;
                                if (listener == null || !captureRouteScripts) trackedProperties.delete(key);
                                else trackedProperties.set(key, { target, targetName, property, type, listener, descriptor });
                                const suppressReaderKey = readerActive && /^(?:onkeydown|onkeyup|onkeypress)$/.test(property) && typeof listener === 'function';
                                Reflect.apply(descriptor.set, target, [suppressReaderKey ? null : listener]);
                            }
                        });
                    }
                    try {
                        const originalClearInterval = window.clearInterval;
                        window.setInterval = function (...args) {
                            const id = Reflect.apply(originalSetInterval, this, args);
                            if (captureRouteScripts) trackedIntervals.add(id);
                            return id;
                        };
                        window.clearInterval = function (id) {
                            trackedIntervals.delete(id);
                            return Reflect.apply(originalClearInterval, this, [id]);
                        };
                    } catch (_) {}
                    let onpopDescriptor = null;
                    for (let proto = window; proto && !onpopDescriptor; proto = Object.getPrototypeOf(proto)) {
                        onpopDescriptor = Object.getOwnPropertyDescriptor(proto, 'onpopstate') || null;
                    }
                    let assignedOnpopstate = null;
                    if (onpopDescriptor?.configurable && onpopDescriptor.set) {
                        Object.defineProperty(window, 'onpopstate', {
                            configurable: true,
                            enumerable: onpopDescriptor.enumerable,
                            get() { return assignedOnpopstate; },
                            set(listener) {
                                assignedOnpopstate = listener;
                                const propertyKey = 'window:onpopstate';
                                if (listener == null || !captureRouteScripts) trackedProperties.delete(propertyKey);
                                else trackedProperties.set(propertyKey, { target: window, targetName: 'window', property: 'onpopstate', type: 'popstate', listener, descriptor: onpopDescriptor });
                                let actual = listener;
                                if (typeof listener === 'function' && readerActive) {
                                    actual = function (event) {
                                        if (readerActive) return;
                                        return Reflect.apply(listener, window, [event]);
                                    };
                                    readingHandlers.set(listener, { wrapped: actual, property: true });
                                }
                                Reflect.apply(onpopDescriptor.set, window, [actual]);
                                if (captureRouteScripts) send('onpopstate-set', { reader: !!readerActive, source: sourceOf(listener) });
                            }
                        });
                    }
                    const wrapLocationMethod = (key) => {
                        try {
                            const original = Location.prototype[key];
                            if (typeof original !== 'function' || original.__ehtwPageWorldWrapped) return;
                            const wrapped = function (...args) {
                                send(`location-${key}`, { targetPath: (() => { try { return new URL(args[0], location.href).pathname; } catch (_) { return ''; } })(), stack: stackOf() });
                                return Reflect.apply(original, this, args);
                            };
                            Object.defineProperty(wrapped, '__ehtwPageWorldWrapped', { value: true });
                            Location.prototype[key] = wrapped;
                        } catch (_) {}
                    };
                    if (diagnosticsEnabled) ['assign', 'replace', 'reload'].forEach(wrapLocationMethod);
                    if (diagnosticsEnabled) try {
                        const hrefDescriptor = Object.getOwnPropertyDescriptor(Location.prototype, 'href');
                        if (hrefDescriptor?.set && hrefDescriptor.configurable && !hrefDescriptor.set.__ehtwPageWorldWrapped) {
                            const setter = function (value) {
                                let targetPath = '';
                                try { targetPath = new URL(value, location.href).pathname; } catch (_) {}
                                send('location-href', { targetPath, stack: stackOf() });
                                return Reflect.apply(hrefDescriptor.set, this, [value]);
                            };
                            Object.defineProperty(setter, '__ehtwPageWorldWrapped', { value: true });
                            Object.defineProperty(Location.prototype, 'href', { ...hrefDescriptor, set: setter });
                        }
                    } catch (_) {}
                    const wrapHistoryMethod = (key) => {
                        try {
                            const original = History.prototype[key];
                            if (typeof original !== 'function' || original.__ehtwPageWorldWrapped) return;
                            const wrapped = function (state, ...args) {
                                send(`history-${key}`, { stateKeys: stateKeys(state), targetPath: (() => { try { return args[1] ? new URL(args[1], location.href).pathname : location.pathname; } catch (_) { return ''; } })(), stack: stackOf() });
                                return Reflect.apply(original, this, [state, ...args]);
                            };
                            Object.defineProperty(wrapped, '__ehtwPageWorldWrapped', { value: true });
                            History.prototype[key] = wrapped;
                        } catch (_) {}
                    };
                    if (diagnosticsEnabled) ['pushState', 'replaceState'].forEach(wrapHistoryMethod);
                    if (diagnosticsEnabled) Reflect.apply(originalAdd, window, ['popstate', (event) => {
                        send('popstate', { onpopstate: sourceOf(assignedOnpopstate), onpopstatePresent: typeof assignedOnpopstate === 'function', stateKeys: stateKeys(event?.state) });
                    }]);
                    const applyReaderControl = (action) => {
                        if (action === 'route-scripts-start') {
                            captureRouteScripts = true;
                            readerActive = /^\/s\//.test(location.pathname);
                            return;
                        }
                        if (action === 'route-scripts-end') { captureRouteScripts = false; return; }
                        if (action === 'route-leave') {
                            let removedListeners = 0;
                            const removedListenerSources = [];
                            for (const [key, records] of [...tracked]) {
                                for (const record of [...records]) {
                                    const target = record.target === 'window' ? window : document;
                                    try { Reflect.apply(originalRemove, target, [record.type, record.actual, record.options]); } catch (_) {}
                                    removedListenerSources.push(`${record.target}:${record.type}:${record.source}`);
                                    removedListeners++;
                                }
                                tracked.delete(key);
                            }
                            let removedProperties = 0;
                            for (const [key, record] of [...trackedProperties]) {
                                try { Reflect.apply(record.descriptor.set, record.target, [null]); } catch (_) {}
                                trackedProperties.delete(key);
                                removedProperties++;
                            }
                            let clearedIntervals = 0;
                            for (const id of trackedIntervals) { try { window.clearInterval(id); clearedIntervals++; } catch (_) {} }
                            trackedIntervals.clear();
                            send('route-cleanup', { removedWindowDocumentListeners: removedListeners, removedListenerSources, clearedOnHandlers: removedProperties, clearedIntervals });
                            snapshot('route-leave');
                            return;
                        }
                        if (action === 'route-snapshot') {
                            const counts = { window: {}, document: {} };
                            for (const records of tracked.values()) for (const record of records) {
                                counts[record.target][record.type] = (counts[record.target][record.type] || 0) + 1;
                            }
                            const onCounts = { window: {}, document: {} };
                            for (const record of trackedProperties.values()) onCounts[record.targetName][record.type] = (onCounts[record.targetName][record.type] || 0) + 1;
                            send('route-global-handlers', { listeners: counts, onHandlers: onCounts });
                            return;
                        }
                        if (action === 'reader-enter') {
                            if (readerActive) return;
                            readerActive = true;
                            send('reader-active', { active: true });
                            return;
                        }
                        if (action === 'reader-leave') {
                            if (!readerActive && !assignedOnpopstate) return;
                            readerActive = false;
                            try { Reflect.apply(originalRemove, window, ['popstate', assignedOnpopstate, false]); } catch (_) {}
                            assignedOnpopstate = null;
                            try { Reflect.apply(onpopDescriptor?.set, window, [null]); } catch (_) {}
                            const records = tracked.get('window:popstate') || [];
                    const removedReaderListeners = records.filter((record) => record.reader).length;
                            for (const record of [...records]) {
                                if (!record.reader) continue;
                                try { Reflect.apply(originalRemove, window, ['popstate', record.actual, record.options]); } catch (_) {}
                                removeFromTracking('popstate', record.original);
                            }
                            readingHandlers.clear();
                            send('reader-cleanup', { active: false, removedWindowPopstateListeners: removedReaderListeners, onpopstateCleared: true });
                            snapshot('reader-leave');
                        }
                    };
                    Reflect.apply(originalAdd, window, [controlEvent, (event) => applyReaderControl(event.detail?.action)]);
                    const readerRouteObserver = new MutationObserver(() => {
                        const active = document.documentElement.getAttribute('data-ehtw-reader-route') === 'active';
                        applyReaderControl(active ? 'reader-enter' : 'reader-leave');
                    });
                    readerRouteObserver.observe(document.documentElement, { attributes: true, attributeFilter: ['data-ehtw-reader-route'] });
                    Object.defineProperty(window, '__EHTW_PAGE_WORLD_CONTROL_V1__', { value: applyReaderControl, configurable: true });
                    Object.defineProperty(window, '__EHTW_PAGE_WORLD_BRIDGE_V1__', { value: true, configurable: true });
                    document.documentElement.setAttribute('data-ehtw-page-world-bridge', 'ready');
                    send('bridge-ready', { initialReader: readerActive, mode: 'page-world' });
                    snapshot('bridge-ready');
                };
                const probeNonce = `${Date.now()}-${Math.random().toString(36).slice(2)}`;
                const probe = document.createElement('script');
                probe.textContent = `window.__EHTW_PAGE_WORLD_PROBE_V1__=${JSON.stringify(probeNonce)};`;
                document.documentElement.appendChild(probe);
                probe.remove();
                const directWorld = window.__EHTW_PAGE_WORLD_PROBE_V1__ === probeNonce;
                if (directWorld) {
                    try { delete window.__EHTW_PAGE_WORLD_PROBE_V1__; } catch (_) {}
                    bridge(navLogEnabled);
                } else {
                    const script = document.createElement('script');
                    script.id = mainWorldScriptId;
                    script.textContent = `;(${bridge.toString()})(${navLogEnabled ? 'true' : 'false'});`;
                    script.onerror = () => forwardMainWorldReport({ event: 'bridge-injection-failed', reason: 'inline-script-error-event' });
                    document.documentElement.appendChild(script);
                    script.remove();
                }
                mainWorldBridgeReady = document.documentElement.getAttribute('data-ehtw-page-world-bridge') === 'ready';
                if (directWorld) mainWorldBridgeReady = window.__EHTW_PAGE_WORLD_BRIDGE_V1__ === true;
                if (!mainWorldBridgeReady) forwardMainWorldReport({ event: 'bridge-injection-failed', reason: 'main-world-marker-not-set-after-synchronous-script-injection' });
                forwardMainWorldReport({ event: 'bridge-injection', mode: directWorld ? 'page-world' : 'dom-script', ready: mainWorldBridgeReady });
            } catch (error) {
                forwardMainWorldReport({ event: 'bridge-injection-failed', reason: String(error?.message || error).slice(0, 300) });
            }
        };
        installMainWorldBridge();
    }
    const controlMainWorldReader = (action) => {
        if (!mainWorldInjectionAttempted) return;
        try {
            document.documentElement?.setAttribute('data-ehtw-reader-route', action === 'reader-enter' ? 'active' : 'inactive');
            const script = document.createElement('script');
            script.textContent = `window.__EHTW_PAGE_WORLD_CONTROL_V1__?.(${JSON.stringify(action)});`;
            (document.documentElement || document.head || document).appendChild(script);
            script.remove();
        }
        catch (error) { navLog('page-world-control-failed', { action, reason: String(error?.message || error).slice(0, 300) }); }
    };
    const navLogPath = (value) => {
        try { return new URL(value, location.href).pathname; } catch (_) { return ''; }
    };
    const navLogActiveElement = () => {
        const active = document.activeElement;
        return active ? { tagName: String(active.tagName || '').toLowerCase(), id: active.id || '' } : null;
    };
    if (navLogEnabled) {
        const wrapMethod = (object, key, label, getDetails = () => ({})) => {
            try {
                const original = object?.[key];
                if (typeof original !== 'function' || original.__ehtwNavLogWrapped) return;
                const wrapped = function (...args) {
                    navLog(`api-${label}`, { ...getDetails.call(this, args), stack: navLogStack() });
                    return Reflect.apply(original, this, args);
                };
                Object.defineProperty(wrapped, '__ehtwNavLogWrapped', { value: true });
                object[key] = wrapped;
            } catch (_) {}
        };
        wrapMethod(Location.prototype, 'assign', 'location-assign', ([href]) => ({ targetPath: navLogPath(href) }));
        wrapMethod(Location.prototype, 'replace', 'location-replace', ([href]) => ({ targetPath: navLogPath(href) }));
        wrapMethod(Location.prototype, 'reload', 'location-reload');
        wrapMethod(History.prototype, 'back', 'history-back');
        wrapMethod(History.prototype, 'go', 'history-go', ([delta]) => ({ delta: Number(delta) || 0 }));
        wrapMethod(History.prototype, 'forward', 'history-forward');
        wrapMethod(HTMLFormElement.prototype, 'submit', 'form-submit', function (args) { return { targetPath: navLogPath(this.action) }; });
        wrapMethod(HTMLFormElement.prototype, 'requestSubmit', 'form-request-submit', function (args) { return { targetPath: navLogPath(this.action) }; });
        wrapMethod(HTMLAnchorElement.prototype, 'click', 'anchor-click', function () { return { targetPath: navLogPath(this.href) }; });
        try {
            const descriptor = Object.getOwnPropertyDescriptor(Location.prototype, 'href');
            if (descriptor?.set && descriptor.configurable) {
                Object.defineProperty(Location.prototype, 'href', { ...descriptor, set(value) {
                    navLog('api-location-href', { targetPath: navLogPath(value), stack: navLogStack() });
                    return Reflect.apply(descriptor.set, this, [value]);
                } });
            }
        } catch (_) {}
        addUserScriptListener(document, 'click', (event) => {
            const anchor = (event.target?.nodeType === 1 ? event.target : event.target?.parentElement)?.closest?.('a[href]');
            if (!anchor) return;
            const stack = navLogStack();
            window.setTimeout(() => {
                if (!event.defaultPrevented) navLog('default-anchor-navigation', {
                    targetPath: navLogPath(anchor.href), stack
                });
            }, 0);
        }, true);
        addUserScriptListener(document, 'submit', (event) => {
            const form = event.target;
            const stack = navLogStack();
            window.setTimeout(() => {
                if (!event.defaultPrevented) navLog('default-form-navigation', {
                    targetPath: navLogPath(form?.action), stack
                });
            }, 0);
        }, true);
        for (const eventName of ['beforeunload', 'unload', 'pagehide']) {
            addUserScriptListener(window, eventName, (event) => {
                if (eventName === 'pagehide') {
                    try { window.sessionStorage?.setItem(NAV_LOG_CLEAN_EXIT_KEY, '1'); } catch (_) {}
                }
                navLog(eventName, {
                ...(eventName === 'pagehide' ? { persisted: !!event?.persisted } : {}),
                activeElement: navLogActiveElement()
                });
            });
        }
        const logDocumentNavigationEntry = () => {
            const navigation = window.performance?.getEntriesByType?.('navigation')?.[0];
            navLog('document-navigation-entry', {
                type: navigation?.type || '', namePath: navLogPath(navigation?.name || location.href),
                referrerPath: document.referrer ? navLogPath(document.referrer) : ''
            });
        };
        if (document.readyState === 'loading') {
            addUserScriptListener(document, 'DOMContentLoaded', logDocumentNavigationEntry, { once: true });
        } else {
            logDocumentNavigationEntry();
        }
    }
    const safeReplaceHistoryState = (historyObject, state, title, href, source = 'unspecified') => {
        try {
            historyObject.replaceState(state, title, href);
            return true;
        } catch (error) {
            if (navLogEnabled) navLog('history-replace-state-error', {
                source,
                errorName: String(error?.name || 'Error'),
                errorMessage: String(error?.message || error)
            });
            return false;
        }
    };
    if (navLogCommand === 'ehtw-navlog-on' || navLogCommand === 'ehtw-navlog-off' ||
        navLogCommand === 'ehtw-nocache-on' || navLogCommand === 'ehtw-nocache-off' || navLogPanelRequested) {
        if (safeReplaceHistoryState(window.history, window.history.state, '', `${location.pathname}${location.search}`, 'navlog-command-consume')) {
            spaRenderedHref = location.href;
        }
    }

    // Track only active left-edge gestures. This reuses a single early
    // touchstart listener and installs touchend/cancel listeners temporarily.
    const EDGE_GESTURE_QUIET_MS = 350;
    const edgeGestureTouchIds = new Set();
    let edgeGestureQuietUntil = 0;
    let edgeGestureReleaseTimer = null;
    let flushDeferredScrollHistoryReplace = null;
    const isEdgeGestureProtected = () =>
        edgeGestureTouchIds.size > 0 || Date.now() < edgeGestureQuietUntil;
    const finishEdgeGestureTouches = (event) => {
        for (const touch of event.changedTouches || []) edgeGestureTouchIds.delete(touch.identifier);
        if (edgeGestureTouchIds.size) return;
        document.removeEventListener('touchend', finishEdgeGestureTouches, true);
        document.removeEventListener('touchcancel', finishEdgeGestureTouches, true);
        edgeGestureQuietUntil = Date.now() + EDGE_GESTURE_QUIET_MS;
        window.clearTimeout(edgeGestureReleaseTimer);
        edgeGestureReleaseTimer = window.setTimeout(() => {
            edgeGestureReleaseTimer = null;
            edgeGestureQuietUntil = 0;
            flushDeferredScrollHistoryReplace?.('edge-gesture-quiet-end');
        }, EDGE_GESTURE_QUIET_MS);
    };
    const trackLeftEdgeGestureStart = (event) => {
        if (!navLogApplicable) return;
        let foundEdgeTouch = false;
        for (const touch of event.changedTouches || []) {
            if (touch.clientX > 30) continue;
            edgeGestureTouchIds.add(touch.identifier);
            foundEdgeTouch = true;
        }
        if (!foundEdgeTouch) return;
        edgeGestureQuietUntil = 0;
        window.clearTimeout(edgeGestureReleaseTimer);
        edgeGestureReleaseTimer = null;
        addUserScriptListener(document, 'touchend', finishEdgeGestureTouches, { capture: true, passive: true });
        addUserScriptListener(document, 'touchcancel', finishEdgeGestureTouches, { capture: true, passive: true });
    };
    if (navLogEnabled) {
        window.__EHTW_NAV_LOG__ = {
            diagnosticListeners: navLogApplicable ? 4 : 0,
            diagnosticTimers: 0,
            clear() {
                navLogEntries = [];
                try { window.sessionStorage?.removeItem(NAV_LOG_SESSION_KEY); } catch (_) {}
                if (navLogTextarea) navLogTextarea.value = '';
                if (navLogTitle) navLogTitle.textContent = `导航诊断日志(0/${NAV_LOG_LIMIT})`;
            },
            exportText: navLogText,
            maxEntries: NAV_LOG_LIMIT
        };
        const edgeTouches = new Map();
        const coordinates = (touch) => ({ x: Math.round(touch.clientX), y: Math.round(touch.clientY) });
        addUserScriptListener(document, 'touchstart', (event) => {
            for (const touch of event.changedTouches || []) {
                if (touch.clientX <= 30) {
                    const point = coordinates(touch);
                    edgeTouches.set(touch.identifier, point);
                    navLog('edge-touch', { phase: 'touchstart', ...point }, true);
                }
            }
        }, { capture: true, passive: true });
        addUserScriptListener(document, 'touchend', (event) => {
            for (const touch of event.changedTouches || []) {
                const start = edgeTouches.get(touch.identifier);
                if (!start && touch.clientX > 30) continue;
                navLog('edge-touch', { phase: 'touchend', ...(start ? { startX: start.x, startY: start.y } : {}), ...coordinates(touch) }, true);
                edgeTouches.delete(touch.identifier);
            }
        }, { capture: true, passive: true });
        addUserScriptListener(document, 'touchcancel', (event) => {
            for (const touch of event.changedTouches || []) {
                const start = edgeTouches.get(touch.identifier);
                if (!start && touch.clientX > 30) continue;
                navLog('edge-touch', { phase: 'touchcancel', ...(start ? { startX: start.x, startY: start.y } : {}), ...coordinates(touch) }, true);
                edgeTouches.delete(touch.identifier);
            }
        }, { capture: true, passive: true });
    }

    const showNavLogPanel = () => {
        if (!navLogPanelRequested || !document.body || document.getElementById('ehtw-navlog-panel')) return;
        const panel = document.createElement('section');
        panel.id = 'ehtw-navlog-panel';
        panel.setAttribute('role', 'dialog');
        panel.setAttribute('aria-modal', 'true');
        panel.setAttribute('aria-label', '导航诊断日志');
        panel.style.cssText = 'position:fixed;inset:0;z-index:2147483647;display:flex;flex-direction:column;gap:8px;padding:12px;background:#101820;color:#fff;font:14px system-ui,sans-serif;box-sizing:border-box;';
        navLogTitle = document.createElement('div');
        navLogTitle.textContent = `导航诊断日志(${navLogEntries.length}/${NAV_LOG_LIMIT})`;
        const controls = document.createElement('div');
        controls.style.cssText = 'display:flex;gap:8px;flex-wrap:wrap;';
        const button = (id, label) => {
            const el = document.createElement('button');
            el.id = id;
            el.type = 'button';
            el.textContent = label;
            el.style.cssText = 'min-height:44px;min-width:72px;padding:8px 14px;font-size:16px;';
            return el;
        };
        const copyButton = button('ehtw-navlog-copy', '复制');
        const clearButton = button('ehtw-navlog-clear', '清空');
        const closeButton = button('ehtw-navlog-close', '关闭');
        const status = document.createElement('span');
        status.setAttribute('aria-live', 'polite');
        controls.append(copyButton, clearButton, closeButton, status);
        navLogTextarea = document.createElement('textarea');
        navLogTextarea.id = 'ehtw-navlog-text';
        navLogTextarea.readOnly = true;
        navLogTextarea.setAttribute('aria-label', '可复制的导航诊断日志');
        navLogTextarea.style.cssText = 'flex:1;min-height:0;width:100%;resize:none;box-sizing:border-box;font:12px ui-monospace,monospace;white-space:pre;';
        navLogTextarea.value = navLogText();
        copyButton.addEventListener('click', async () => {
            const text = navLogTextarea.value;
            try {
                if (navigator.clipboard?.writeText) {
                    await navigator.clipboard.writeText(text);
                    status.textContent = '已复制';
                    return;
                }
            } catch (_) {}
            navLogTextarea.focus();
            navLogTextarea.select();
            try { status.textContent = document.execCommand('copy') ? '已复制' : '已选中,请长按复制'; }
            catch (_) { status.textContent = '已选中,请长按复制'; }
        });
        clearButton.addEventListener('click', () => {
            navLogEntries = [];
            navLogTextarea.value = '';
            navLogTitle.textContent = `导航诊断日志(0/${NAV_LOG_LIMIT})`;
            try { window.sessionStorage?.removeItem(NAV_LOG_SESSION_KEY); } catch (_) {}
            status.textContent = '已清空';
        });
        closeButton.addEventListener('click', () => {
            navLogTextarea = null;
            navLogTitle = null;
            panel.remove();
        });
        panel.append(navLogTitle, controls, navLogTextarea);
        document.body.appendChild(panel);
        navLogTextarea.focus();
    };

    const EHTW_GALLERY_ORIGINS = new Set([
        'https://e-hentai.org',
        'https://exhentai.org'
    ]);
    const EHTW_UPLOAD_ORIGINS = new Set([
        'https://upld.e-hentai.org',
        'https://upld.exhentai.org'
    ]);
    function isGallerySiteOrigin(value = location.origin) {
        return EHTW_GALLERY_ORIGINS.has(String(value || ''));
    }
    function isUploadSiteOrigin(value = location.origin) {
        return EHTW_UPLOAD_ORIGINS.has(String(value || ''));
    }
    if (navLogEnabled && isGallerySiteOrigin(location.origin)) {
        addUserScriptListener(document, 'visibilitychange', () => navLog('visibilitychange', {
            visibilityState: document.visibilityState,
            hidden: !!document.hidden
        }), { passive: true });
    }

    const ehtwScriptBootTime = (() => {
        try {
            return window.performance?.now?.() ?? null;
        } catch (_) {
            return null;
        }
    })();

    const isReaderUrl = /^\/s\//i.test(location.pathname);
    const pageBootClass = isReaderUrl ? 'ehtw-reader-boot' : 'ehtw-page-boot';
    let pageBootResetTimer = null;
    let navigationResetTimer = null;
    let pageBootWasReleased = false;
    let pageBootObserver = null;
    let hasTouchInput = false;

    const releasePageBoot = () => {
        pageBootWasReleased = true;
        pageBootObserver?.disconnect();
        pageBootObserver = null;
        window.clearTimeout?.(pageBootResetTimer);
        window.clearTimeout?.(navigationResetTimer);
        pageBootResetTimer = null;
        navigationResetTimer = null;
        document.documentElement?.classList.remove(
            'ehtw-page-boot',
            'ehtw-reader-boot',
            'ehtw-navigation-pending'
        );
    };

    function getGalleryIdFromUrl(value, baseUrl = location.href) {
        try {
            return new URL(value, baseUrl).pathname.match(/^\/g\/(\d+)\//i)?.[1] || null;
        } catch (_) {
            return null;
        }
    }

    function getGalleryThumbPageIndex(value, baseUrl = location.href) {
        try {
            const raw = new URL(value, baseUrl).searchParams.get('p');
            const index = Number.parseInt(raw || '0', 10);
            return Number.isFinite(index) && index >= 0 ? index : 0;
        } catch (_) {
            return 0;
        }
    }

    // ---- Scroll position memory (history state + sessionStorage) ----
    window.__EHTW_SCROLL_SAVE_LOCK__ = false;
    let scrollRestoreProtectionUntil = 0;
    let scrollRestoreProtectionTimer = null;
    let saveCurrentScrollForSpa = () => false;
    const spaScrollStorageKey = (href) => `ehtw-scroll-v1:${href}`;
    const spaHistoryStateStorageKey = (href) => `ehtw-history-state-v1:${href}`;
    const spaScrollPageshowRestoreKey = (href) => `ehtw-scroll-pageshow-restore-v1:${href}`;
    const spaBackTargetStorageKey = (href) => `ehtw-back-target-v1:${href}`;
    const getSavedSpaBackTarget = (href) => {
        try {
            return window.sessionStorage?.getItem(spaBackTargetStorageKey(href)) || '';
        } catch (_) {
            return '';
        }
    };
    const resolveSpaBackTarget = (value, currentHref) => {
        try {
            let target = new URL(value, currentHref);
            if (target.origin !== location.origin || target.href === new URL(currentHref).href) return '/';
            const previousGalleryId = getGalleryIdFromUrl(target.href);
            if (previousGalleryId) {
                const savedListHref = window.sessionStorage?.getItem(
                    `ehtw-gallery-return-v1:${previousGalleryId}`
                );
                if (savedListHref) target = new URL(savedListHref, currentHref);
            }
            return target.origin === location.origin && target.href !== new URL(currentHref).href
                ? target.href
                : '/';
        } catch (_) {
            return '/';
        }
    };
    const getSavedSpaScroll = (href) => {
        try {
            return Math.max(0, Number(window.sessionStorage?.getItem(spaScrollStorageKey(href))) || 0);
        } catch (_) {
            return 0;
        }
    };
    const saveSpaScroll = (href, y) => {
        try {
            window.sessionStorage?.setItem(spaScrollStorageKey(href), String(Math.max(0, Math.round(Number(y) || 0))));
        } catch (_) {}
    };
    const copySpaHistoryState = (state) => {
        if (!state || typeof state !== 'object') return null;
        const keys = [
            'ehtwSpa', 'ehtwSpaRoute', 'ehtwEntryId', 'ehtwSpaDepth', 'ehtwScrollY',
            'ehtwSpaPreviousHref', 'ehtwSpaReturnBackTarget'
        ];
        const saved = Object.fromEntries(keys.filter((key) => key in state).map((key) => [key, state[key]]));
        return saved.ehtwSpa && saved.ehtwEntryId ? saved : null;
    };
    const rememberSpaHistoryState = (href, state) => {
        const saved = copySpaHistoryState(state);
        if (!saved) return false;
        try {
            const absoluteHref = new URL(href, location.href).href;
            window.sessionStorage?.setItem(spaHistoryStateStorageKey(absoluteHref), JSON.stringify(saved));
            return true;
        } catch (_) {
            return false;
        }
    };
    const readRememberedSpaHistoryState = (href) => {
        try {
            const absoluteHref = new URL(href, location.href).href;
            const value = JSON.parse(window.sessionStorage?.getItem(spaHistoryStateStorageKey(absoluteHref)) || 'null');
            return copySpaHistoryState(value);
        } catch (_) {
            return null;
        }
    };
    const forgetRememberedSpaHistoryState = (href) => {
        try {
            const absoluteHref = new URL(href, location.href).href;
            window.sessionStorage?.removeItem(spaHistoryStateStorageKey(absoluteHref));
        } catch (_) {}
    };
    function recoverSpaHistoryState(href, candidateState) {
        const candidate = candidateState && typeof candidateState === 'object' ? candidateState : null;
        const copiedCandidate = copySpaHistoryState(candidate);
        if (copiedCandidate) {
            rememberSpaHistoryState(href, copiedCandidate);
            return { ...(candidate || {}), ...copiedCandidate };
        }
        const remembered = readRememberedSpaHistoryState(href);
        if (remembered) return { ...(candidate || {}), ...remembered };
        const suspended = findSuspendedSpaPageByHref(href);
        const suspendedState = copySpaHistoryState(suspended?.historyState);
        if (suspendedState) return { ...(candidate || {}), ...suspendedState };
        return candidate || {};
    }

    // List-like pages are the ones that get suspended (kept alive) when a
    // gallery is opened from them, and are valid "return to list" targets.
    const isListLikePath = (pathname) =>
        !/^\/(g|s|mytags|uconfig)(\/|\.php|$)/i.test(String(pathname || ''));
    const newSpaEntryId = () =>
        `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
    const readFiniteNumber = (value) => {
        if (value === null || value === undefined || value === '') return null;
        const number = Number(value);
        return Number.isFinite(number) ? number : null;
    };
    function shouldRestoreSpaScrollOnPageShow(event, navigationType, explicitRestore = false, referrer = '', currentHref = '', historyState = null) {
        if (!!event?.persisted || navigationType === 'back_forward' || explicitRestore === true) return true;
        if (navigationType !== 'navigate' || !historyState?.ehtwSpa || !referrer || !currentHref) return false;
        try {
            const referrerUrl = new URL(referrer);
            const currentUrl = new URL(currentHref);
            referrerUrl.hash = '';
            currentUrl.hash = '';
            return referrerUrl.href === currentUrl.href;
        } catch (_) {
            return false;
        }
    }
    const galleryReturnDepthKey = (galleryId) => `ehtw-gallery-return-depth-v1:${galleryId}`;
    const galleryBackTraversalStorageKey = 'ehtw-gallery-back-traversal-v1';
    let galleryBackTraversalPending = false;

    if (isGallerySiteOrigin(location.origin)) {
        try {
            window.history.scrollRestoration = 'manual';
        } catch (_) {}
        const saveCurrentScroll = (source = 'scroll', forceHistoryReplace = false) => {
            if (window.__EHTW_SCROLL_SAVE_LOCK__ || Date.now() < scrollRestoreProtectionUntil) {
                if (navLogEnabled) navLog('scroll-save-skipped', {
                    source,
                    scrollY: window.scrollY || 0,
                    reason: window.__EHTW_SCROLL_SAVE_LOCK__ ? 'scroll-save-lock' : 'scroll-restore-quiet-window'
                });
                return;
            }
            if (
                document.documentElement?.classList.contains('ehtw-navigation-pending') &&
                location.href !== spaRenderedHref
            ) {
                if (navLogEnabled) navLog('scroll-save-skipped', { source, scrollY: window.scrollY || 0, reason: 'pending-destination-does-not-match-rendered-route' });
                return;
            }
            const y = window.scrollY || 0;
            if (navLogEnabled) navLog('scroll-save', { source, scrollY: y, historyState: summarizeNavLogState(window.history.state) });
            if (!/^\/s\//i.test(location.pathname)) saveSpaScroll(location.href, y);
            const state = recoverSpaHistoryState(location.href, window.history.state);
            if (!state?.ehtwSpa) return;
            queueScrollHistoryStateWrite(location.href, state, y, source, forceHistoryReplace);
            return true;
        };
        saveCurrentScrollForSpa = saveCurrentScroll;
        let scrollSaveScheduled = false;
        addUserScriptListener(window, 'scroll', () => {
            if (window.__EHTW_SCROLL_SAVE_LOCK__ || scrollSaveScheduled) return;
            scrollSaveScheduled = true;
            const schedule = window.requestAnimationFrame || ((callback) => window.setTimeout(callback, 0));
            schedule(() => {
                scrollSaveScheduled = false;
                saveCurrentScroll('scroll');
            });
        }, { passive: true });
        addUserScriptListener(window, 'pagehide', (event) => {
            if (navLogEnabled) navLog('pagehide', { persisted: !!event?.persisted, scrollY: window.scrollY || 0, activeElement: navLogActiveElement() });
            saveCurrentScroll('pagehide', true);
        });
    }

    // Restore scroll after the new page is rendered. The page grows while
    // images / async blocks load, so keep retrying until the target is
    // reachable (or ~3s passed, or the user starts scrolling themselves).
    // Scroll saving is locked meanwhile so clamped intermediate positions
    // never overwrite the saved value.
    let cancelActiveSpaScrollRestore = null;
    const restoreSpaScrollPosition = (value, source = 'unspecified') => {
        // A previous Back restore can keep retrying while images expand the
        // page. If the user navigates again, that stale task must not pull the
        // newly opened route back to the old list position.
        cancelActiveSpaScrollRestore?.();
        const target = Math.max(0, Number(value) || 0);
        if (navLogEnabled) navLog('scroll-restore-start', { source, targetY: target, startY: window.scrollY || 0 });
        window.clearTimeout(scrollHistoryStateWriteTimer);
        scrollHistoryStateWriteTimer = null;
        const raf = window.requestAnimationFrame || ((callback) => window.setTimeout(callback, 0));
        window.__EHTW_SCROLL_SAVE_LOCK__ = true;
        const startedAt = Date.now();
        let cancelled = false;
        let finished = false;
        let resizeObserver = null;
        // Ignore leftover input (wheel inertia, the tap that triggered "back")
        // for a moment; only deliberate scrolling afterwards cancels restore.
        const cancelEvents = ['wheel', 'touchmove', 'keydown'];
        const cancel = () => {
            if (Date.now() - startedAt > 400) cancelled = true;
        };
        cancelEvents.forEach((type) => addUserScriptListener(window, type, cancel, { passive: true }));
        const finish = () => {
            if (finished) return;
            finished = true;
            if (cancelActiveSpaScrollRestore === finish) cancelActiveSpaScrollRestore = null;
            cancelEvents.forEach((type) => window.removeEventListener?.(type, cancel));
            try { resizeObserver?.disconnect(); } catch (_) {}
            window.__EHTW_SCROLL_SAVE_LOCK__ = false;
            protectScrollStateAfterRestore();
            if (navLogEnabled) navLog('scroll-restore-finish', { source, targetY: target, reachedY: window.scrollY || 0, canceled: cancelled, elapsedMs: Date.now() - startedAt });
            try {
                console.debug('[EH Twitter Style] scroll restore', {
                    target,
                    reached: window.scrollY,
                    height: document.documentElement.scrollHeight
                });
            } catch (_) {}
        };
        cancelActiveSpaScrollRestore = finish;
        const apply = () => {
            if (finished) return true;
            if (cancelled) { finish(); return true; }
            const maxY = Math.max(0, document.documentElement.scrollHeight - window.innerHeight);
            window.scrollTo?.(0, Math.min(target, maxY));
            if (Math.abs((window.scrollY || 0) - target) <= 2) { finish(); return true; }
            if (Date.now() - startedAt > 4000) { finish(); return true; }
            return false;
        };
        const tick = () => {
            if (apply()) return;
            window.setTimeout(tick, 60);
        };
        // Re-apply whenever the page grows (images / async blocks settling).
        if (typeof window.ResizeObserver === 'function') {
            try {
                resizeObserver = new window.ResizeObserver(() => { apply(); });
                resizeObserver.observe(document.documentElement);
                if (document.body) resizeObserver.observe(document.body);
            } catch (_) {}
        }
        apply();
        raf(() => raf(tick));
    };

    function reorderGalleryToplistSections(root) {
        const heading = Array.from(root?.querySelectorAll?.('h2') || [])
            .find((item) => /gallery\s+toplists/i.test(item.textContent || ''));
        const group = heading?.closest?.('.dc') || heading?.parentElement;
        if (!group) return false;

        const sections = Array.from(group.children || [])
            .filter((item) => item.matches?.('.tdo'));
        const periodRank = (section) => {
            const label = String(section.querySelector?.('p')?.textContent || section.textContent || '');
            if (/yesterday/i.test(label)) return 0;
            if (/past\s+month|month/i.test(label)) return 1;
            if (/past\s+year|year/i.test(label)) return 2;
            if (/all[\s-]*time/i.test(label)) return 3;
            return 4;
        };
        if (sections.length < 2 || sections.filter((section) => periodRank(section) < 4).length < 2) {
            return false;
        }

        group.classList?.add('ehtw-gallery-toplists');
        sections
            .map((section, index) => ({ section, index, rank: periodRank(section) }))
            .sort((a, b) => a.rank - b.rank || a.index - b.index)
            .forEach(({ section }) => group.appendChild(section));
        return true;
    }

    function getToplistPageKey(value) {
        try {
            const url = new URL(value, location.href);
            return url.origin === location.origin ? `${url.pathname}${url.search}` : '';
        } catch (_) {
            return '';
        }
    }

    function publishToplistEntries(href, entries) {
        const key = getToplistPageKey(href);
        if (!key || !Array.isArray(entries)) return;
        const shared = window.__EHTW_TOPLIST_ENTRIES__ || {};
        shared[key] = entries;
        window.__EHTW_TOPLIST_ENTRIES__ = shared;
        try {
            window.dispatchEvent(new CustomEvent('ehtw-toplist-data', {
                detail: { href: key, entries }
            }));
        } catch (_) {}
    }

    function normalizeHiddenTag(value) {
        return String(value || '')
            .trim()
            .replace(/^#+/, '')
            .replace(/[_+]+/g, ' ')
            .replace(/\s+/g, ' ')
            .toLowerCase();
    }

    function getHiddenTagsFromMyTags(root) {
        const hidden = new Set();
        const tagSetEnabled = root?.querySelector?.('#tagset_enable');
        if (tagSetEnabled && !tagSetEnabled.checked) return hidden;
        const rows = root?.querySelectorAll?.('#usertags_outer > div[id^="usertag_"]') || [];
        Array.from(rows).forEach((row) => {
            if (!row.querySelector('input[id^="taghide_"]')?.checked) return;
            const preview = row.querySelector('[id^="tagpreview_"]');
            const tag = preview?.getAttribute('title') || preview?.textContent || '';
            const normalized = normalizeHiddenTag(tag);
            if (normalized) hidden.add(normalized);
        });
        return hidden;
    }

    function getToplistEntryTags(row) {
        return Array.from(row?.querySelectorAll?.('.gt') || [])
            .map((tag) => normalizeHiddenTag(tag.getAttribute('title') || tag.textContent || ''))
            .filter(Boolean);
    }

    function extractNativeToplistEntries(root, limit = 50, baseHref = location.href) {
        const maxEntries = Math.max(1, Math.floor(Number(limit) || 50));
        const source = root?.querySelector?.(
            'table.itg.gltc, table.itg.gltm, table.itg.glte, div.itg.gld, .itg'
        ) || root;
        const candidates = [
            ...Array.from(source?.querySelectorAll?.('.gl1t') || []),
            ...Array.from(source?.querySelectorAll?.('tr') || []).filter((row) =>
                Array.from(row.querySelectorAll?.('a[href*="/g/"]') || []).some((anchor) =>
                    (anchor.querySelector?.('.glink')?.textContent || anchor.textContent || '').trim()
                )
            )
        ];
        const seen = new Set();
        const entries = [];

        candidates.forEach((row) => {
            if (entries.length >= maxEntries) return;
            const link = Array.from(row.querySelectorAll?.('a[href*="/g/"]') || []).find((anchor) =>
                /\/g\/\d+\//i.test(anchor.getAttribute('href') || '') &&
                (anchor.querySelector?.('.glink')?.textContent || anchor.textContent || '').trim()
            );
            if (!link) return;
            try {
                const url = new URL(link.getAttribute('href'), baseHref);
                if (url.origin !== new URL(baseHref).origin || seen.has(url.pathname)) return;
                const title = (link.querySelector?.('.glink')?.textContent || link.textContent || '').trim();
                if (!title) return;
                const image = Array.from(row.querySelectorAll?.(
                    '.glthumb img, .gl3t img, .gl1e img, .gl1m img, img'
                ) || []).find((candidate) => {
                    const src = candidate.getAttribute('data-src') ||
                        candidate.getAttribute('data-original') || candidate.getAttribute('src') || '';
                    return src && !/^data:image\//i.test(src) && src !== '#';
                });
                let thumbnail = image?.getAttribute('data-src') ||
                    image?.getAttribute('data-original') || image?.getAttribute('src') || '';
                if (!thumbnail) {
                    const style = row.querySelector?.('.glthumb, .gl5t, [style*="background"]')?.getAttribute('style') || '';
                    thumbnail = style.match(/url\(["']?([^"')]+)["']?\)/i)?.[1] || '';
                }
                try {
                    const thumbUrl = thumbnail ? new URL(thumbnail, baseHref) : null;
                    thumbnail = thumbUrl && /^https?:$/.test(thumbUrl.protocol) ? thumbUrl.href : '';
                } catch (_) {
                    thumbnail = '';
                }
                seen.add(url.pathname);
                entries.push({ title, href: url.href, thumbnail, tags: getToplistEntryTags(row) });
            } catch (_) {}
        });
        return entries;
    }

    const isEhtwToplistBridgeFrame = window.self !== window.top &&
        location.hostname.toLowerCase() === 'e-hentai.org' &&
        /(?:\?|&)_ehtw_toplist_bridge=/.test(location.search || '');
    if (window.self !== window.top) {
        if (isEhtwToplistBridgeFrame) {
            const ehtwToplistBridgeParams = new URL(location.href).searchParams;
            const sendToplistBridgeData = () => {
                const period = ehtwToplistBridgeParams.get('tl') || '';
                const token = ehtwToplistBridgeParams.get('_ehtw_toplist_bridge') || '';
                const entries = extractNativeToplistEntries(document, 50, location.href);
                try {
                    window.parent.postMessage({
                        type: 'ehtw-toplist-bridge', token, period, entries
                    }, 'https://exhentai.org');
                } catch (_) {}
            };
            if (document.readyState === 'loading') {
                addUserScriptListener(document, 'DOMContentLoaded', sendToplistBridgeData, { once: true });
            } else {
                sendToplistBridgeData();
            }
        }
        return;
    }

    function filterToplistEntries(entries, hiddenTags) {
        const blockedTags = new Set(Array.from(hiddenTags || [], normalizeHiddenTag));
        return Array.from(entries || []).filter((entry) =>
            !(entry.tags || []).some((tag) => blockedTags.has(normalizeHiddenTag(tag)))
        );
    }

    function filterToplistResultRows(root, hiddenTags) {
        const blockedTags = new Set(Array.from(hiddenTags || [], normalizeHiddenTag));
        let hiddenCount = 0;
        const rows = Array.from(root?.querySelectorAll?.('.gl1t, table.itg tr') || []);
        rows.forEach((row) => {
            const blocked = getToplistEntryTags(row).some((tag) => blockedTags.has(tag));
            if (!blocked) return;
            row.hidden = true;
            row.classList?.add('ehtw-hidden-tag-gallery');
            hiddenCount += 1;
        });
        return hiddenCount;
    }

    function renderGalleryToplistCovers(group, entries, hiddenTags = new Set()) {
        const visibleEntries = filterToplistEntries(entries, hiddenTags);
        const rows = Array.from(group?.querySelectorAll?.('.tdo tr') || []);
        let rendered = 0;

        rows.forEach((row, index) => {
            const entry = visibleEntries[index];
            if (!entry) {
                row.hidden = true;
                row.classList?.add('ehtw-gallery-toplist-hidden');
                return;
            }

            row.hidden = false;
            row.classList?.remove('ehtw-gallery-toplist-hidden');
            const rank = row.querySelector?.('td.pso');
            if (rank) rank.textContent = `#${index + 1}`;
            const link = row.querySelector?.('a[href*="/g/"]');
            if (!link) return;
            link.href = entry.href;
            link.setAttribute('href', entry.href);

            let image = link.querySelector?.('.ehtw-gallery-toplist-cover');
            let imageUrl = '';
            try {
                const url = entry.thumbnail ? new URL(entry.thumbnail, location.href) : null;
                if (url && /^https?:$/.test(url.protocol)) imageUrl = url.href;
            } catch (_) {}
            if (imageUrl) {
                if (!image) {
                    image = document.createElement('img');
                    image.className = 'ehtw-gallery-toplist-cover';
                    image.alt = '';
                    image.loading = 'lazy';
                    image.decoding = 'async';
                    image.setAttribute('aria-hidden', 'true');
                }
                image.src = imageUrl;
                link.replaceChildren(image, document.createTextNode(entry.title || ''));
                rendered += 1;
            } else {
                link.replaceChildren(document.createTextNode(entry.title || ''));
            }
        });
        return rendered;
    }

    // Apply a theme-colored page shell before the first paint, then hide the
    // native page until its replacement layout is ready.
    if (
        isGallerySiteOrigin(location.origin) ||
        isUploadSiteOrigin(location.origin)
    ) {
        const installEarlyPageStyle = () => {
            const root = document.documentElement;
            if (root && !document.getElementById('ehtw-page-boot-style')) {
                const earlyStyle = document.createElement('style');
                earlyStyle.id = 'ehtw-page-boot-style';
                earlyStyle.textContent = `
                    html.ehtw-page-boot,
                    html.ehtw-page-boot body {
                        background: #fff !important;
                        color: #0f1419 !important;
                    }
                    @media (prefers-color-scheme: dark) {
                        html.ehtw-page-boot,
                        html.ehtw-page-boot body {
                            background: #000 !important;
                            color: #e7e9ea !important;
                        }
                    }
                    html.ehtw-reader-boot,
                    html.ehtw-reader-boot body {
                        background: #000 !important;
                        color: #fff !important;
                    }
                    html.ehtw-page-boot body > :not(#ehtw-shell),
                    html.ehtw-reader-boot body > :not(.ehtw-reader-root) {
                        opacity: 0 !important;
                    }
                    html.ehtw-page-boot body > #ehtw-shell,
                    html.ehtw-reader-boot body > .ehtw-reader-root {
                        opacity: 1 !important;
                    }
                    html.ehtw-navigation-pending body > :not(#ehtw-shell):not(.ehtw-reader-root) {
                        opacity: 0 !important;
                    }
                    html.ehtw-navigation-pending body > #ehtw-shell,
                    html.ehtw-navigation-pending body > .ehtw-reader-root {
                        opacity: 1 !important;
                    }
                    html.ehtw-navigation-pending,
                    html.ehtw-navigation-pending body {
                        background: var(--ehtw-bg, #fff) !important;
                        color: var(--ehtw-text, #0f1419) !important;
                    }
                    @media (prefers-color-scheme: dark) {
                        html.ehtw-navigation-pending:not(.ehtw-reader),
                        html.ehtw-navigation-pending:not(.ehtw-reader) body {
                            background: var(--ehtw-bg, #000) !important;
                            color: var(--ehtw-text, #e7e9ea) !important;
                        }
                    }
                `;
                (document.head || root).appendChild(earlyStyle);
            }
            return !!root;
        };

        const applyBootClass = () => {
            const root = document.documentElement;
            if (!root) return false;
            if (pageBootWasReleased) return true;
            root.classList.add(pageBootClass);
            installEarlyPageStyle();
            addUserScriptListener(document, 'touchstart', (event) => {
                hasTouchInput = true;
                root.classList.add('ehtw-touch-ui');
                trackLeftEdgeGestureStart(event);
            }, { capture: true, passive: true });
            return true;
        };
        if (!applyBootClass() && typeof MutationObserver === 'function') {
            pageBootObserver = new MutationObserver(() => {
                if (applyBootClass()) {
                    pageBootObserver?.disconnect();
                    pageBootObserver = null;
                }
            });
            pageBootObserver.observe(document, { childList: true, subtree: true });
        }
        pageBootResetTimer = window.setTimeout(releasePageBoot, 20000);

        // 1.17.261: on phones the sticky header and the bottom nav slide away
        // while scrolling down and come back when scrolling up (like the
        // reader). iOS Safari paints the status bar / its toolbar area with
        // the colour of a bar pinned to that edge; with the bars away the
        // page content shows through instead.
        {
            const CHROME_HIDDEN_CLASS = 'ehtw-chrome-scroll-hidden';
            let lastY = Number(window.scrollY) || 0;
            let travel = 0;
            let userInputAt = 0;
            let touching = false;
            const isPhoneLayout = () => {
                try {
                    return window.matchMedia?.('(max-width: 699px)')?.matches ?? (window.innerWidth <= 699);
                } catch (_) {
                    return false;
                }
            };
            const setHidden = (hidden) => {
                const root = document.documentElement;
                if (!root || root.classList.contains(CHROME_HIDDEN_CLASS) === hidden) return;
                root.classList.toggle(CHROME_HIDDEN_CLASS, hidden);
            };
            const markInput = () => { userInputAt = performance.now(); };
            addUserScriptListener(document, 'touchstart', (event) => {
                touching = Number(event.touches?.length) > 0;
                markInput();
            }, { capture: true, passive: true });
            addUserScriptListener(document, 'touchend', (event) => {
                touching = Number(event.touches?.length) > 0;
                markInput();
            }, { capture: true, passive: true });
            addUserScriptListener(document, 'touchcancel', () => { touching = false; }, { capture: true, passive: true });
            addUserScriptListener(window, 'wheel', markInput, { passive: true });
            addUserScriptListener(window, 'keydown', markInput, { passive: true });
            addUserScriptListener(window, 'scroll', () => {
                const root = document.documentElement;
                const y = Number(window.scrollY) || 0;
                const delta = y - lastY;
                lastY = y;
                if (!root || root.classList.contains('ehtw-reader') || !isPhoneLayout()) {
                    travel = 0;
                    setHidden(false);
                    return;
                }
                const now = performance.now();
                // Only finger, momentum, wheel or key scrolling counts; scroll
                // restoration after navigation never hides the bars.
                if (!(touching || now - userInputAt < 1200) || Math.abs(delta) > (window.innerHeight || 800)) {
                    travel = 0;
                    return;
                }
                userInputAt = now;
                if (y <= 8) {
                    travel = 0;
                    setHidden(false);
                    return;
                }
                if (!delta) return;
                if (Math.sign(delta) !== Math.sign(travel)) travel = 0;
                travel += delta;
                if (travel > 24) setHidden(true);
                else if (travel < -24) setHidden(false);
            }, { passive: true });
        }

        const markNavigationPending = () => {
            if (navLogEnabled) navLog('beforeunload-navigation-pending', { scrollY: window.scrollY || 0, historyState: summarizeNavLogState(window.history.state) });
            document.documentElement?.classList.add('ehtw-navigation-pending');
            window.clearTimeout?.(navigationResetTimer);
            navigationResetTimer = window.setTimeout(() => {
                document.documentElement?.classList.remove('ehtw-navigation-pending');
            }, 20000);
        };

        addUserScriptListener(document, 'click', (event) => {
            if (
                event.defaultPrevented ||
                event.button !== 0 ||
                event.metaKey || event.ctrlKey || event.shiftKey || event.altKey
            ) return;
            const anchor = event.target?.closest?.('a[href]');
            if (!anchor || anchor.target === '_blank' || anchor.matches('.ehtw-back-link')) return;
            // Let the site's inline popup handlers run before the browser's
            // default link action (for example, the torrent icon on a card).
            if (/\bpopUp\s*\(/i.test(anchor.getAttribute('onclick') || '')) return;
            const inlineLocationContainer = anchor.parentElement?.closest?.('[onclick]');
            const hasInlineLocationNavigation = !!inlineLocationContainer &&
                /(?:document|window)\.location(?:\.href)?\s*=/.test(
                    inlineLocationContainer.getAttribute('onclick') || ''
                );
            try {
                const targetUrl = new URL(anchor.getAttribute('href') || anchor.href, location.href);
                if (!isGallerySiteOrigin(targetUrl.origin) &&
                    !isUploadSiteOrigin(targetUrl.origin)) return;
                if (
                    targetUrl.pathname === location.pathname &&
                    targetUrl.search === location.search
                ) return;
                if (
                    isGallerySiteOrigin(location.origin) &&
                    targetUrl.origin === location.origin &&
                    window.__EHTW_SPA_READY__ &&
                    typeof window.__EHTW_SPA_NAVIGATE__ === 'function'
                ) {
                    const currentGalleryId = getGalleryIdFromUrl(location.href);
                    const isSameGalleryPagination = !!currentGalleryId &&
                        getGalleryIdFromUrl(targetUrl.href) === currentGalleryId;
                    // Gallery pagination cells also have a native inline
                    // document.location handler on their <td>. Stop that
                    // bubbling handler after the router takes over the link.
                    if (hasInlineLocationNavigation) event.stopPropagation();
                    event.preventDefault();
                    window.__EHTW_SPA_NAVIGATE__(targetUrl.href, {
                        // Keep one browser-history entry for the gallery so
                        // Safari's edge-swipe returns to the source list at once.
                        historyMode: isSameGalleryPagination ? 'replace' : 'push'
                    });
                    return;
                }
                markNavigationPending();
            } catch (_) {}
        }, true);
        addUserScriptListener(document, 'submit', (event) => {
            const form = event.target;
            try {
                const targetUrl = new URL(form?.action || location.href, location.href);
                if (isGallerySiteOrigin(targetUrl.origin) ||
                    isUploadSiteOrigin(targetUrl.origin)) {
                    if (
                        isGallerySiteOrigin(location.origin) &&
                        targetUrl.origin === location.origin &&
                        String(form?.method || 'get').toLowerCase() === 'get' &&
                        window.__EHTW_SPA_READY__ &&
                        typeof window.__EHTW_SPA_NAVIGATE__ === 'function'
                    ) {
                        event.preventDefault();
                        const formData = new window.FormData(form);
                        for (const [key, value] of formData.entries()) {
                            if (typeof value === 'string') targetUrl.searchParams.append(key, value);
                        }
                        window.__EHTW_SPA_NAVIGATE__(targetUrl.href, { historyMode: 'push' });
                    } else {
                        markNavigationPending();
                    }
                }
            } catch (_) {}
        }, true);
        addUserScriptListener(window, 'popstate', (event) => {
            const destinationState = recoverSpaHistoryState(location.href, event?.state);
            if (navLogEnabled) navLog('popstate', {
                eventState: summarizeNavLogState(event?.state),
                currentState: summarizeNavLogState(window.history.state),
                recoveredState: summarizeNavLogState(destinationState),
                stateRecovered: !!destinationState.ehtwSpa && !event?.state?.ehtwSpa
            });
            if (destinationState.ehtwSpa) {
                rememberSpaHistoryState(location.href, destinationState);
                if (!window.history.state?.ehtwSpa || !window.history.state?.ehtwEntryId) {
                    queueScrollHistoryStateWrite(location.href, destinationState, destinationState.ehtwScrollY, 'popstate-state-repair');
                }
            }
            let previousUrl;
            let currentUrl;
            try {
                previousUrl = new URL(spaRenderedHref);
                currentUrl = new URL(location.href);
            } catch (_) {}
            if (
                previousUrl && currentUrl &&
                previousUrl.origin === currentUrl.origin &&
                previousUrl.pathname === currentUrl.pathname &&
                previousUrl.search === currentUrl.search
            ) {
                // Hash history entries are in-page navigation, not an SPA
                // route. Keep the existing DOM and allow native anchor scroll.
                spaRenderedHref = location.href;
                return;
            }
            galleryBackTraversalPending = false;
            const previousGalleryId = getGalleryIdFromUrl(spaRenderedHref);
            const destinationGalleryId = getGalleryIdFromUrl(location.href);
            const previousThumbIndex = getGalleryThumbPageIndex(spaRenderedHref);
            const destinationThumbIndex = getGalleryThumbPageIndex(location.href);
            if (
                previousGalleryId &&
                previousGalleryId === destinationGalleryId &&
                spaRenderedHref !== location.href &&
                destinationThumbIndex < previousThumbIndex
            ) {
                // Older sessions may still contain one SPA entry per thumbnail.
                try {
                    window.sessionStorage?.setItem(
                        galleryBackTraversalStorageKey,
                        destinationGalleryId
                    );
                } catch (_) {}
            }
            if (
                isGallerySiteOrigin(location.origin) &&
                typeof window.__EHTW_SPA_NAVIGATE__ === 'function'
            ) {
                window.__EHTW_SPA_NAVIGATE__(location.href, {
                    historyMode: 'pop',
                    historyState: destinationState,
                    scrollY: Number(destinationState.ehtwScrollY) || getSavedSpaScroll(location.href)
                });
            }
        });
        addUserScriptListener(window, 'beforeunload', markNavigationPending);
        addUserScriptListener(window, 'pageshow', (event) => {
            if (event?.persisted) releasePageBoot();
            let explicitRestore = false;
            try {
                explicitRestore = window.sessionStorage?.getItem(
                    spaScrollPageshowRestoreKey(location.href)
                ) === '1';
                if (explicitRestore) {
                    window.sessionStorage?.removeItem(spaScrollPageshowRestoreKey(location.href));
                }
            } catch (_) {}
            let navigationType = '';
            try {
                navigationType = window.performance?.getEntriesByType?.('navigation')?.[0]?.type || '';
            } catch (_) {}
            if (navLogEnabled) navLog('pageshow', {
                persisted: !!event?.persisted,
                navigationType,
                explicitScrollRestore: explicitRestore,
                historyState: summarizeNavLogState(window.history.state)
            });
            if (!shouldRestoreSpaScrollOnPageShow(event, navigationType, explicitRestore, document.referrer, location.href, window.history.state)) return;
            const state = recoverSpaHistoryState(location.href, window.history.state);
            if (!state.ehtwSpa) return;
            const scrollY = Number(state.ehtwScrollY) || getSavedSpaScroll(location.href);
            if (scrollY <= 0) return;
            window.setTimeout(() => restoreSpaScrollPosition(scrollY, 'pageshow'), 0);
            if (!window.history.state?.ehtwSpa || !window.history.state?.ehtwEntryId) {
                queueScrollHistoryStateWrite(location.href, state, scrollY, 'pageshow-state-repair');
            }
        });
    }

    function runMain() {

    const PREFIX = 'ehtw';
    const RUN_KEY = '__EHTW_STYLE_050__';
    const READER_IMAGE_LOAD_TIMEOUT_MS = 30000;
    const READER_IMAGE_PREFETCH_CONCURRENCY = 5;
    const READER_IMAGE_AUTO_RETRY_LIMIT = 2;
    const READER_IMAGE_AUTO_RETRY_DELAYS_MS = [800, 1800];
    // 若在 E-Hentai 设置里改过时区,填写偏移分钟数(东八区 480、东九区 540);默认 UTC。
    const SITE_UTC_OFFSET_MIN = 0;
    const spaPageEventListeners = [];
    const addSpaPageEventListener = (target, type, listener, options) => {
        if (!target?.addEventListener) return;
        addUserScriptListener(target, type, listener, options);
        spaPageEventListeners.push({ target, type, listener, options });
    };
    const publishSpaPageCleanup = (cleanup) => {
        window.__EHTW_PAGE_CLEANUP__ = () => {
            spaPageEventListeners.splice(0).forEach(({ target, type, listener, options }) => {
                target.removeEventListener?.(type, listener, options);
            });
            if (/^\/mytags\/?$/i.test(location.pathname)) {
                hiddenMyTagsPromise = null;
                watchedMyTagsPagePromise = null;
                try { window.sessionStorage?.removeItem(SEARCH_HIDDEN_TAGS_CACHE_KEY); } catch (_) {}
            }
            window.__EHTW_SPA_READY__ = false;
            cleanup?.();
        };
        window.__EHTW_SPA_READY__ = true;
        window.__EHTW_PAGE_LISTENERS__ = spaPageEventListeners;
        if (isGallerySiteOrigin(location.origin)) {
            const actualState = window.history.state || {};
            const current = recoverSpaHistoryState(location.href, actualState);
            if (!current.ehtwSpa || !current.ehtwEntryId) {
                const referrerWasGallery = !!getGalleryIdFromUrl(document.referrer);
                const initialBackTarget = current.ehtwSpaPreviousHref ||
                    (isListLikePath(location.pathname)
                        ? resolveSpaBackTarget(document.referrer, location.href)
                        : '');
                const initialState = {
                    ...current,
                    ehtwSpa: true,
                    ehtwEntryId: current.ehtwEntryId || newSpaEntryId(),
                    ehtwSpaDepth: readFiniteNumber(current.ehtwSpaDepth) ?? 0,
                    ehtwScrollY: readFiniteNumber(current.ehtwScrollY) ?? getSavedSpaScroll(location.href),
                    ...(isListLikePath(location.pathname) ? {
                        ehtwSpaPreviousHref: initialBackTarget,
                        ehtwSpaReturnBackTarget: !current.ehtwSpaPreviousHref && referrerWasGallery
                    } : {})
                };
                if (historyStateWriteSuppressionReason(location.href)) {
                    queueScrollHistoryStateWrite(location.href, initialState, initialState.ehtwScrollY, 'initial-state-repair');
                } else if (safeReplaceHistoryState(window.history, initialState, '', location.href, 'initial-spa-state')) {
                    rememberSpaHistoryState(location.href, initialState);
                }
                if (isListLikePath(location.pathname)) {
                    try {
                        window.sessionStorage?.setItem(spaBackTargetStorageKey(location.href), initialBackTarget || '/');
                    } catch (_) {}
                }
            } else {
                rememberSpaHistoryState(location.href, current);
                if (!actualState.ehtwSpa || !actualState.ehtwEntryId) {
                    queueScrollHistoryStateWrite(location.href, current, current.ehtwScrollY, 'history-state-repair');
                }
            }
        }
        window.setTimeout(() => replayPendingSpaPopIfReady('spa-ready'), 0);
    };
    function normalizeUploaderName(value) {
        return String(value || '')
            .trim()
            .replace(/[_+]+/g, ' ')
            .replace(/\s+/g, ' ')
            .toLowerCase();
    }

    function normalizeCreatorFollowTag(value) {
        const match = String(value || '').trim().match(/^(artist|group):(.+)$/i);
        if (!match) return '';
        const namespace = match[1].toLowerCase();
        const name = match[2]
            .toLowerCase()
            .replace(/[_+]+/g, ' ')
            .replace(/\s+/g, ' ');
        return `${namespace}:${name}`;
    }

    function isCreatorTagValue(value) {
        let candidate = String(value || '').trim();
        try {
            candidate = decodeURIComponent(candidate);
        } catch (_) {}
        candidate = candidate.replace(/^ta_/i, '');
        return /^(?:artist|group):/i.test(candidate) ||
            /\/tag\/(?:artist|group):/i.test(candidate);
    }

    function getWatchedCreatorTagsFromMyTags(root) {
        const tags = new Set();
        const tagSetEnabled = root?.querySelector?.('#tagset_enable');
        if (tagSetEnabled && !tagSetEnabled.checked) return tags;
        const rows = root?.querySelectorAll?.(
            '#usertags_outer > div[id^="usertag_"]'
        ) || [];

        Array.from(rows).forEach((row) => {
            const watched = row.querySelector('input[id^="tagwatch_"]');
            if (!watched?.checked) return;

            const preview = row.querySelector('[id^="tagpreview_"]');
            const tagName = preview?.getAttribute('title') ||
                preview?.textContent || '';
            if (!/^(?:artist|group):/i.test(tagName.trim())) return;

            const normalized = normalizeCreatorFollowTag(tagName);
            if (normalized) tags.add(normalized);
        });

        return tags;
    }

let watchedMyTagsPagePromise = null;
let hiddenMyTagsPromise = null;
const SEARCH_HIDDEN_TAGS_CACHE_KEY = 'ehtw-search-hidden-tags-v1';
const SEARCH_HIDDEN_TAGS_CACHE_TTL = 60 * 1000;
function readCachedSearchHiddenTags() {
    try {
        const cached = JSON.parse(window.sessionStorage?.getItem(SEARCH_HIDDEN_TAGS_CACHE_KEY) || 'null');
        if (
            !cached ||
            !Number.isFinite(Number(cached.at)) ||
            Date.now() - Number(cached.at) >= SEARCH_HIDDEN_TAGS_CACHE_TTL ||
            !Array.isArray(cached.tags)
        ) return null;
        return new Set(cached.tags.map((tag) => String(tag || '').trim()).filter(Boolean));
    } catch (_) {
        return null;
    }
}

function requestWatchedMyTagsPage() {
    if (!watchedMyTagsPagePromise) {
        watchedMyTagsPagePromise = (async () => {
                const response = await window.fetch('/mytags', {
                    credentials: 'same-origin',
                    cache: 'no-store'
                });
                if (!response?.ok) throw new Error('Could not load My Tags');
                const pageText = await response.text();
            return new window.DOMParser().parseFromString(pageText, 'text/html');
            })();
        }
    return watchedMyTagsPagePromise;
}

function requestHiddenMyTags() {
    if (!hiddenMyTagsPromise) {
        hiddenMyTagsPromise = window.fetch('/mytags', {
            credentials: 'same-origin',
            cache: 'no-store'
        }).then(async (response) => {
            if (!response?.ok) throw new Error('Could not load My Tags');
            const page = new window.DOMParser().parseFromString(await response.text(), 'text/html');
            const tags = getHiddenTagsFromMyTags(page);
            try {
                window.sessionStorage?.setItem(SEARCH_HIDDEN_TAGS_CACHE_KEY, JSON.stringify({
                    at: Date.now(),
                    tags: Array.from(tags)
                }));
            } catch (_) {}
            return tags;
        }).catch((error) => {
            hiddenMyTagsPromise = null;
            throw error;
        });
    }
    return hiddenMyTagsPromise;
}

function requestWatchedCreatorTags() {
    return requestWatchedMyTagsPage().then(getWatchedCreatorTagsFromMyTags);
}

function getWatchedUploaderEntriesFromMyTags(root) {
    const entries = new Map();
    const tagSetEnabled = root?.querySelector?.('#tagset_enable');
    if (tagSetEnabled && !tagSetEnabled.checked) return [];
    const rows = root?.querySelectorAll?.(
        '#usertags_outer > div[id^="usertag_"]'
    ) || [];

    Array.from(rows).forEach((row) => {
        const watched = row.querySelector('input[id^="tagwatch_"]');
        if (!watched?.checked) return;
        const preview = row.querySelector('[id^="tagpreview_"]');
        const tag = String(
            preview?.getAttribute('title') || preview?.textContent || ''
        ).trim();
        const match = tag.match(/^uploader:(.+)$/i);
        if (!match) return;
        const name = match[1].trim();
        const key = normalizeUploaderName(name);
        if (!key || entries.has(key)) return;
        entries.set(key, {
            key,
            name,
            href: '/uploader/' + encodeURIComponent(name).replace(/%20/g, '+')
        });
    });

    return Array.from(entries.values());
}

const FOLLOW_UPLOADERS_FAVCAT = 9;
const FOLLOW_UPLOADERS_GALLERY_CACHE_KEY = 'ehtw.followUploaders.galleryNames.v1';
const followedUploaderEntriesPromiseState = { value: null };

    function navigatePage(value) {
        const targetUrl = new URL(value, location.href);
        if (
            targetUrl.origin === location.origin &&
            isGallerySiteOrigin(location.origin) &&
            typeof window.fetch === 'function' &&
            window.__EHTW_SPA_READY__ &&
            typeof window.__EHTW_SPA_NAVIGATE__ === 'function'
        ) {
            return window.__EHTW_SPA_NAVIGATE__(targetUrl.href, { historyMode: 'push' });
        }
        if (navLogEnabled) navLog('script-navigation-call', { method: 'location.assign', source: 'navigatePage', targetPath: targetUrl.pathname, stack: navLogStack() });
        location.assign(targetUrl.href);
        return false;
    }

function extractFavoriteGalleryPaths(root) {
    const paths = new Set();
    Array.from(root?.querySelectorAll?.('table.itg.gltc a[href], table.itg.gltm a[href], table.itg.glte a[href], div.itg.gld a[href]') || []).forEach((link) => {
        const href = link.getAttribute('href') || link.href;
        try {
            const url = new URL(href, window.location.origin);
            if (url.origin !== window.location.origin) return;
            if (!/^\/g\/\d+\/[0-9a-f]+\/?$/i.test(url.pathname)) return;
            paths.add(url.pathname);
        } catch (_) {}
    });
    return Array.from(paths);
}

function isNativeNavigationLoggedIn(root) {
    const links = Array.from(root?.querySelectorAll?.('a[href]') || []);
    const labels = links.map((link) => String(link.textContent || '').trim());
    const hrefs = links.map((link) => String(link.getAttribute?.('href') || link.href || ''));
    if (labels.some((label) => /^(?:log\s*in|login|sign\s*in)$/i.test(label)) ||
        hrefs.some((href) => /act=Login&CODE=00/i.test(href))) {
        return false;
    }
    return labels.some((label) => /^(?:my\s+tags|my\s+uploads?|my\s+home)$/i.test(label)) ||
        hrefs.some((href) => /(?:\/mytags|\/home\.php|\/upld\/manage)/i.test(href));
}

function getNativeAccountActionUrl(isLoggedIn) {
    const code = isLoggedIn ? '03' : '00';
    return `https://forums.e-hentai.org/index.php?act=Login&CODE=${code}`;
}

function findFollowUploaderFavoriteNextUrl(root, currentHref) {
    let currentUrl;
    try {
        currentUrl = new URL(currentHref, window.location.href);
    } catch (_) {
        return null;
    }
    const currentPage = Number.parseInt(currentUrl.searchParams.get('page') || '0', 10);
    const candidates = [];
    const directNext = root?.querySelector?.('#dnext[href], a[rel~="next"][href]');
    if (directNext) candidates.push(directNext);
    candidates.push(...Array.from(root?.querySelectorAll?.('.searchnav a[href]') || []));

    return candidates
        .map((link) => {
            try {
                return new URL(link.getAttribute('href') || link.href, currentUrl.href);
            } catch (_) {
                return null;
            }
        })
        .filter((url) =>
            url &&
            url.origin === currentUrl.origin &&
            url.pathname === currentUrl.pathname &&
            url.searchParams.get('favcat') === String(FOLLOW_UPLOADERS_FAVCAT) &&
            Number.parseInt(url.searchParams.get('page') || '0', 10) > currentPage
        )
        .sort((a, b) =>
            Number.parseInt(a.searchParams.get('page'), 10) -
            Number.parseInt(b.searchParams.get('page'), 10)
        )[0]?.href || null;
}

async function requestFollowUploadersFavoritePaths() {
    const paths = new Set();
    const seenPages = new Set();
    let pageUrl = new URL('/favorites.php', window.location.origin);
    pageUrl.searchParams.set('favcat', String(FOLLOW_UPLOADERS_FAVCAT));

    for (let pageCount = 0; pageUrl && pageCount < 100; pageCount += 1) {
        const requestUrl = pageUrl.href;
        if (seenPages.has(requestUrl)) break;
        seenPages.add(requestUrl);
        const response = await window.fetch(requestUrl, {
            credentials: 'same-origin',
            cache: 'no-store'
        });
        if (!response?.ok) {
            throw new Error(`收藏夹读取失败:${response?.status || '无响应'}(${requestUrl})`);
        }
        const page = new window.DOMParser().parseFromString(await response.text(), 'text/html');
        extractFavoriteGalleryPaths(page).forEach((path) => paths.add(path));
        const nextUrl = findFollowUploaderFavoriteNextUrl(page, requestUrl);
        pageUrl = nextUrl ? new URL(nextUrl) : null;
    }
    return Array.from(paths);
}

async function loadFollowedUploaderEntries(forceRefresh = false) {
    if (!forceRefresh && followedUploaderEntriesPromiseState.value) {
        return followedUploaderEntriesPromiseState.value;
    }
    const loadPromise = (async () => {
        const paths = await requestFollowUploadersFavoritePaths();
        const entries = new Map();
        let uploaderByPath = {};
        try {
            uploaderByPath = JSON.parse(
                window.localStorage.getItem(FOLLOW_UPLOADERS_GALLERY_CACHE_KEY) || '{}'
            ) || {};
        } catch (_) {}
        let cursor = 0;
        const worker = async () => {
            while (cursor < paths.length) {
                const path = paths[cursor++];
                try {
                    let name = String(uploaderByPath[path] || '').trim();
                    if (!name) {
                        const response = await window.fetch(path, {
                            credentials: 'same-origin',
                            cache: 'no-store'
                        });
                        if (!response?.ok) continue;
                        const detail = new window.DOMParser().parseFromString(
                            await response.text(), 'text/html'
                        );
                        const uploaderLink = detail.querySelector('#gdn a[href*="/uploader/"]');
                        name = String(uploaderLink?.textContent || '').trim();
                        if (name) uploaderByPath[path] = name;
                    }
                    const key = normalizeUploaderName(name);
                    if (!key) continue;
                    if (!entries.has(key)) {
                        entries.set(key, {
                            key,
                            name,
                            href: '/uploader/' + encodeURIComponent(name).replace(/%20/g, '+'),
                            galleryPaths: []
                        });
                    }
                    entries.get(key).galleryPaths.push(path);
                } catch (_) {}
            }
        };
        await Promise.all(Array.from({ length: Math.min(3, paths.length) }, worker));
        try {
            window.localStorage.setItem(
                FOLLOW_UPLOADERS_GALLERY_CACHE_KEY,
                JSON.stringify(uploaderByPath)
            );
        } catch (_) {}
        return Array.from(entries.values());
    })();
    followedUploaderEntriesPromiseState.value = loadPromise;
    try {
        return await loadPromise;
    } catch (error) {
        followedUploaderEntriesPromiseState.value = null;
        throw error;
    }
}

function requestWatchedUploaderNames() {
    return loadFollowedUploaderEntries();
}

async function submitFavoriteFormLikeBrowser(form, popupUrl, favoriteCategory, favoriteNote = '') {
    const formData = new window.FormData(form);
    formData.set('favcat', String(favoriteCategory));
    if (formData.has('favnote') || favoriteNote !== null) {
        formData.set('favnote', String(favoriteNote || ''));
    }
    const submitValue = form.querySelector('[name="apply"]')?.value || 'Add to Favorites';
    formData.set('apply', submitValue);

    const body = new window.URLSearchParams();
    for (const [name, value] of formData.entries()) {
        if (typeof value === 'string') body.append(name, value);
    }
    const action = new URL(form.getAttribute('action') || popupUrl.href, popupUrl.href);
    return window.fetch(action.href, {
        method: 'POST',
        credentials: 'same-origin',
        cache: 'no-store',
        referrer: popupUrl.href,
        referrerPolicy: 'same-origin',
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
        },
        body: body.toString()
    });
}

async function setGalleryInFollowUploadersFavorites(galleryHref, shouldFollow) {
    const galleryUrl = new URL(galleryHref, window.location.href);
    const match = galleryUrl.pathname.match(/^\/g\/(\d+)\/([0-9a-f]+)\/?$/i);
    if (galleryUrl.origin !== window.location.origin || !match) {
        throw new Error('无法识别这本漫画的收藏链接。');
    }
    const popupUrl = new URL('/gallerypopups.php', window.location.origin);
    popupUrl.searchParams.set('gid', match[1]);
    popupUrl.searchParams.set('t', match[2]);
    popupUrl.searchParams.set('act', 'addfav');
    const popupResponse = await window.fetch(popupUrl.href, {
        credentials: 'same-origin',
        cache: 'no-store'
    });
    if (!popupResponse?.ok) throw new Error('无法打开原站收藏表单。');
    const popup = new window.DOMParser().parseFromString(
        await popupResponse.text(), 'text/html'
    );
    const form = popup.querySelector('#galpop');
    if (!form) throw new Error('原站没有返回收藏表单,请稍后重试。');
    const result = await submitFavoriteFormLikeBrowser(
        form,
        popupUrl,
        shouldFollow ? String(FOLLOW_UPLOADERS_FAVCAT) : 'favdel',
        ''
    );
    if (!result?.ok) throw new Error(`收藏操作失败:${result?.status || '无响应'}`);
    followedUploaderEntriesPromiseState.value = null;
    await loadFollowedUploaderEntries(true);
}

function getWatchedMyTagsApiConfig(root) {
    const inlineScript = Array.from(root?.querySelectorAll?.('script') || [])
        .filter((script) => !script.src)
        .map((script) => script.textContent || '')
        .join('\n');
    const apiUrl = inlineScript.match(/\bapi_url\s*=\s*["']([^"']+)["']/)?.[1] || '';
    const apiUid = inlineScript.match(/\bapiuid\s*=\s*["']?(\d+)["']?/)?.[1] || '';
    const apiKey = inlineScript.match(/\bapikey\s*=\s*["']([^"']+)["']/)?.[1] || '';
    if (!apiUrl || !apiUid || !apiKey) return null;
    return { apiUrl, apiUid: Number(apiUid), apiKey };
}

function toggleWatchedTagInMyTags(tag, onComplete = () => {}) {
    const isUploader = /^uploader:/i.test(String(tag || ''));
    const normalizedTag = isUploader
        ? normalizeUploaderName(String(tag).replace(/^uploader:/i, ''))
        : normalizeCreatorFollowTag(tag);
    if (!normalizedTag) {
        onComplete(false, false);
        return;
    }

    const iframe = document.createElement('iframe');
    iframe.setAttribute('aria-hidden', 'true');
    iframe.tabIndex = -1;
    iframe.style.cssText = 'position:fixed;width:1px;height:1px;left:-10px;bottom:0;opacity:0;border:0;pointer-events:none';
    let phase = 'load';
    let srcdocAssigned = false;
    let finished = false;
    let verifyPromise = null;
    let timeoutId = null;

    const finish = (success, isWatched, reason = '') => {
        if (finished) return;
        finished = true;
        if (timeoutId !== null) window.clearTimeout(timeoutId);
        iframe.remove();
        watchedMyTagsPagePromise = null;
        onComplete(success, isWatched, reason);
    };

    const verifySavedState = () => {
        if (finished) return Promise.resolve();
        if (verifyPromise) return verifyPromise;
        verifyPromise = (async () => {
            let lastState = null;
            // Native My Tags saves asynchronously. Allow the server enough time to
            // commit before reporting failure, especially on mobile connections.
            for (let attempt = 0; attempt < 16; attempt += 1) {
                watchedMyTagsPagePromise = null;
                try {
                    const page = await requestWatchedMyTagsPage();
                    const watched = isUploader
                        ? getWatchedUploaderEntriesFromMyTags(page).some((entry) => entry.key === normalizedTag)
                        : getWatchedCreatorTagsFromMyTags(page).has(normalizedTag);
                    lastState = watched;
                    if (watched === phaseTargetState) {
                        finish(true, watched);
                        return;
                    }
                } catch (_) {}
                await new Promise((resolve) => window.setTimeout(resolve, 750));
            }
            finish(false, lastState, 'My Tags 暂未确认保存,请检查标签设置是否启用后重试。');
        })();
        return verifyPromise;
    };

    let phaseTargetState = null;
    iframe.addEventListener('load', () => {
        // Ignore the initial about:blank load; only the fetched srcdoc is
        // allowed to enter the native My Tags form handling path.
        if (!srcdocAssigned) return;
        if (phase !== 'saving' && timeoutId !== null) {
            window.clearTimeout(timeoutId);
            timeoutId = null;
        }
        if (phase === 'saving') {
            verifySavedState();
            return;
        }

        const page = iframe.contentDocument;
        const form = page?.querySelector('#usertag_form');
        if (!form) {
            finish(false, null, '无法打开 My Tags 保存表单,请确认账号仍处于登录状态。');
            return;
        }
        const frameWindow = page.defaultView;
        if (frameWindow) safeReplaceHistoryState(
            frameWindow.history,
            null,
            '',
            new URL('/mytags', window.location.origin).href,
            'mytags-iframe-route'
        );
        form.action = new URL('/mytags', window.location.origin).href;
        const tagSetEnabled = page.querySelector('#tagset_enable');
        if (tagSetEnabled && !tagSetEnabled.checked) {
            finish(false, null, 'My Tags 的标签关注已停用,请先启用标签设置。');
            return;
        }

        const rows = Array.from(page.querySelectorAll(
            '#usertags_outer > div[id^="usertag_"]'
        ));
        const row = rows.find((candidate) => {
            const preview = candidate.querySelector('[id^="tagpreview_"]');
            const name = preview?.getAttribute('title') || preview?.textContent || '';
            return isUploader
                ? normalizeUploaderName(String(name).trim().replace(/^uploader:/i, '')) === normalizedTag
                : normalizeCreatorFollowTag(name) === normalizedTag;
        });
        const watch = row
            ? row.querySelector('input[id^="tagwatch_"]')
            : page.querySelector('#tagwatch_0');
        if (!watch) {
            finish(false, null, 'My Tags 中找不到可保存的标签行。');
            return;
        }

        phaseTargetState = !row || !watch.checked;
        if (!row) {
            const nameInput = page.querySelector('#tagname_new');
            if (!nameInput) {
                finish(false, null, 'My Tags 中找不到新标签输入框。');
                return;
            }
            nameInput.value = isUploader
                ? `uploader:${String(tag).replace(/^uploader:/i, '').trim()}`
                : tag;
        }

        watch.checked = phaseTargetState;
        watch.dispatchEvent(new (page.defaultView || window).Event('change', { bubbles: true }));
        const rowId = row?.id.match(/_(\d+)$/)?.[1] || '0';
        const save = page.querySelector(`#tagsave_${rowId}`);
        if (!save || save.disabled) {
            finish(false, null, 'My Tags 保存按钮未启用,标签没有提交。');
            return;
        }

        phase = 'saving';
        save.click();
        timeoutId = window.setTimeout(() => {
            finish(false, null, 'My Tags 保存确认超时,请稍后重试。');
        }, 15000);
        window.setTimeout(verifySavedState, 500);
    });

    timeoutId = window.setTimeout(() => {
        finish(false, null, 'My Tags 页面加载超时,请稍后重试。');
    }, 20000);

    // The site may refuse a direct /mytags navigation inside an iframe. Fetch
    // the same-origin page first, then load its native editor as srcdoc so the
    // page's own save handlers can run without navigating the visible tab.
    (async () => {
        try {
            const response = await window.fetch('/mytags', {
                credentials: 'same-origin',
                cache: 'no-store'
            });
            if (!response?.ok) throw new Error('My Tags request failed');
            const pageText = await response.text();
            if (finished) return;

            const editor = new window.DOMParser().parseFromString(pageText, 'text/html');
            if (!editor.querySelector('#usertag_form')) {
                finish(false, null, 'My Tags 返回内容中没有保存表单,请确认已登录。');
                return;
            }

            const editorTagSetEnabled = editor.querySelector('#tagset_enable');
            if (editorTagSetEnabled && !editorTagSetEnabled.checked) {
                finish(false, null, 'My Tags 的标签关注已停用,请先启用标签设置。');
                return;
            }
            const editorRows = Array.from(editor.querySelectorAll(
                '#usertags_outer > div[id^="usertag_"]'
            ));
            const existingRow = editorRows.find((candidate) => {
                const preview = candidate.querySelector('[id^="tagpreview_"]');
                const name = preview?.getAttribute('title') || preview?.textContent || '';
                return isUploader
                    ? normalizeUploaderName(String(name).trim().replace(/^uploader:/i, '')) === normalizedTag
                    : normalizeCreatorFollowTag(name) === normalizedTag;
            });

            if (existingRow) {
                const watch = existingRow.querySelector('input[id^="tagwatch_"]');
                const rowId = existingRow.id.match(/_(\d+)$/)?.[1];
                const api = getWatchedMyTagsApiConfig(editor);
                if (!watch || !rowId || !api) {
                    finish(false, null, '无法读取 My Tags 的标签更新参数。');
                    return;
                }

                phaseTargetState = !watch.checked;
                const payload = {
                    method: 'setusertag',
                    apiuid: api.apiUid,
                    apikey: api.apiKey,
                    tagid: Number(rowId),
                    tagwatch: phaseTargetState ? 1 : 0,
                    taghide: phaseTargetState
                        ? 0
                        : (existingRow.querySelector(`#taghide_${rowId}`)?.checked ? 1 : 0),
                    tagcolor: existingRow.querySelector(`#tagcolor_${rowId}`)?.value || '',
                    tagweight: existingRow.querySelector(`#tagweight_${rowId}`)?.value || '10'
                };

                try {
                    if (timeoutId !== null) window.clearTimeout(timeoutId);
                    timeoutId = window.setTimeout(() => {
                        finish(false, null, 'My Tags API 保存超时,请稍后重试。');
                    }, 30000);
                    const result = await new Promise((resolve, reject) => {
                        const xhr = new window.XMLHttpRequest();
                        let settled = false;
                        const fail = (message) => {
                            if (settled) return;
                            settled = true;
                            reject(new Error(message));
                        };
                        xhr.open('POST', api.apiUrl, true);
                        xhr.withCredentials = true;
                        xhr.timeout = 20000;
                        xhr.setRequestHeader('Content-Type', 'application/json');
                        xhr.onreadystatechange = () => {
                            if (xhr.readyState !== 4 || settled) return;
                            settled = true;
                            if (xhr.status !== 200) {
                                reject(new Error(`Server communication failed: ${xhr.status}`));
                                return;
                            }
                            try {
                                resolve(JSON.parse(xhr.responseText));
                            } catch (_) {
                                reject(new Error('My Tags API returned invalid JSON'));
                            }
                        };
                        xhr.onerror = () => fail(`Server communication failed: ${xhr.status}`);
                        xhr.ontimeout = () => fail('My Tags API request timed out');
                        xhr.send(JSON.stringify(payload));
                    });
                    if (result?.login) {
                        throw new Error('Please sign in again');
                    }
                    if (result?.error) {
                        throw new Error(String(result.error));
                    }
                    watchedMyTagsPagePromise = null;
                    verifySavedState();
                } catch (error) {
                    finish(
                        false,
                        null,
                        `My Tags API 保存失败:${String(error?.message || error)}`
                    );
                }
                return;
            }

            if (isUploader) {
                finish(
                    false,
                    null,
                    'My Tags 返回“No matching tag found”:它不接受 uploader:<名称> 标签,无法在此保存上传者关注。'
                );
                return;
            }

            let base = editor.querySelector('base');
            if (!base) {
                base = editor.createElement('base');
                editor.head.prepend(base);
            }
            base.href = new URL('/mytags', window.location.origin).href;
            srcdocAssigned = true;
            iframe.srcdoc = `<!doctype html>${editor.documentElement.outerHTML}`;
            document.body.appendChild(iframe);
        } catch (_) {
            finish(false, null, '无法读取 My Tags 页面,请确认账号仍处于登录状态。');
        }
    })();
}

function followUploaderViaFavorites(name, galleryHref, onComplete = () => {}) {
    const key = normalizeUploaderName(name);
    if (!key) {
        onComplete(false, false, '上传者名称无效。');
        return;
    }
    (async () => {
        try {
            const entries = await loadFollowedUploaderEntries();
            const existing = entries.find((entry) => entry.key === key);
            const shouldFollow = !existing;
            const paths = shouldFollow
                ? [new URL(galleryHref, window.location.href).pathname]
                : existing.galleryPaths;
            if (!paths.length) throw new Error('收藏夹中没有可移除的漫画标记。');
            for (const path of paths) {
                await setGalleryInFollowUploadersFavorites(path, shouldFollow);
            }
            const updated = await loadFollowedUploaderEntries(true);
            const isFollowed = updated.some((entry) => entry.key === key);
            if (isFollowed !== shouldFollow) {
                throw new Error('收藏夹已提交,但上传者状态尚未确认,请刷新后检查。');
            }
            onComplete(true, isFollowed, '');
        } catch (error) {
            onComplete(false, false, String(error?.message || error));
        }
    })();
}

    function resolveGalleryReturnHref(storedHref, referrer, origin, galleryId) {
        for (const value of [storedHref, referrer]) {
            if (!value) continue;

            try {
                const url = new URL(value, origin);
                if (
                    url.origin === origin &&
                    getGalleryIdFromUrl(url.href) !== galleryId
                ) {
                    return url.href;
                }
            } catch (_) {}
        }

        return '/';
    }

function resolveSearchBackHref(previousHref, currentHref, origin, sessionStorage) {
    const previousGalleryId = getGalleryIdFromUrl(previousHref, currentHref);
    if (!previousGalleryId) return previousHref || '/';
    try {
        const storedListHref = sessionStorage?.getItem(`ehtw-gallery-return-v1:${previousGalleryId}`);
        if (storedListHref) {
            const targetHref = resolveGalleryReturnHref(storedListHref, '', origin, previousGalleryId);
            if (new URL(targetHref, currentHref).href !== new URL(currentHref).href) return targetHref;
        }
    } catch (_) {}
    // Never send a Search/list page back into a detail page just because the
    // document referrer survived a same-document return from that gallery.
    return '/';
}

function getReaderImagePriority(pageNumber, centerPageNumber, targetPageNumber, forwardCount = 5, fallbackHigh = false) {
        if (
            Number.isFinite(targetPageNumber) &&
            pageNumber === targetPageNumber
        ) {
            return 'high';
        }

        if (
            Number.isFinite(pageNumber) &&
            Number.isFinite(centerPageNumber)
        ) {
            const distance = Math.abs(pageNumber - centerPageNumber);
            if (distance === 0) return 'high';
            if (distance <= forwardCount) return distance === 1 ? 'high' : 'auto';
            return 'low';
        }

        return fallbackHigh ? 'high' : 'low';
    }

    function getReaderImagePreloadGroups(imageCount, centerIndex, preloadCount) {
        if (!Number.isFinite(imageCount) || !Number.isFinite(centerIndex)) return [];
        const count = Math.max(0, Math.floor(imageCount));
        if (count === 0) return [];
        const center = Math.max(0, Math.min(count - 1, Math.floor(centerIndex)));
        const radius = Math.max(0, Math.floor(preloadCount));
        const groups = [[center]];

        for (let distance = 1; distance <= radius; distance += 1) {
            const group = [];
            const nextIndex = center + distance;
            const previousIndex = center - distance;
            if (nextIndex < count) group.push(nextIndex);
            if (previousIndex >= 0) group.push(previousIndex);
            if (group.length) groups.push(group);
        }

        return groups;
    }

    const continueGalleryBackTraversal = () => {
        try {
            const routeState = recoverSpaHistoryState(location.href, window.history.state);
            const targetGalleryId = window.sessionStorage?.getItem(
                galleryBackTraversalStorageKey
            );
            if (!targetGalleryId) {
                galleryBackTraversalPending = false;
                return false;
            }

            if (navLogEnabled) navLog('gallery-back-traversal-check', {
                targetGalleryId,
                alreadyPending: galleryBackTraversalPending,
                historyState: summarizeNavLogState(routeState)
            });

            if (getGalleryIdFromUrl(location.href) !== targetGalleryId) {
                window.sessionStorage?.removeItem(galleryBackTraversalStorageKey);
                galleryBackTraversalPending = false;
                return false;
            }

            // runMain() and navigateSpa() can both reach this helper for the
            // same restored route. Schedule only one history step per route.
            if (galleryBackTraversalPending) return true;

            if (window.history.length <= 1) {
                window.sessionStorage?.removeItem(galleryBackTraversalStorageKey);
                galleryBackTraversalPending = false;
                return false;
            }

            galleryBackTraversalPending = true;
            if (navLogEnabled) navLog('gallery-back-traversal-trigger', {
                targetGalleryId,
                historyLength: window.history.length,
                historyState: summarizeNavLogState(routeState)
            });
            if (routeState?.ehtwSpaRoute) {
                window.setTimeout(() => window.history.back(), 0);
            } else {
                window.history.back();
            }
            return true;
        } catch (_) {
            galleryBackTraversalPending = false;
            return false;
        }
    };
    // The SPA router lives outside runMain(), while this traversal helper
    // closes over page-local gallery state. Expose the active closure so a
    // restored list can continue walking back past thumbnail-page entries.
    window.__EHTW_CONTINUE_GALLERY_BACK_TRAVERSAL__ = continueGalleryBackTraversal;

    if (continueGalleryBackTraversal() && !window.history.state?.ehtwSpaRoute) {
        releasePageBoot();
        return;
    }
    addSpaPageEventListener(window, 'pageshow', continueGalleryBackTraversal);

    // Expose the running version for local diagnostics without adding UI.
    document.documentElement.dataset.ehtwScriptVersion = '1.17.280';
    try {
        const paintEntries = window.performance?.getEntriesByType?.('paint') || [];
        const firstPaint = paintEntries.find((entry) => entry.name === 'first-paint') ||
            paintEntries.find((entry) => entry.name === 'first-contentful-paint');
        if (Number.isFinite(ehtwScriptBootTime) && firstPaint) {
            document.documentElement.dataset.ehtwBootAfterFirstPaint = String(
                ehtwScriptBootTime > firstPaint.startTime
            );
            document.documentElement.dataset.ehtwBootFirstPaintDeltaMs = String(
                Math.round(ehtwScriptBootTime - firstPaint.startTime)
            );
        }
    } catch (_) {}

    if (window[RUN_KEY]) {
        releasePageBoot();
        return;
    }

    const path = location.pathname;

    // 原站连续阅读器:默认纵向滚动,按需预载后续图片。
    if (/^\/s\//i.test(path)) {
        const readerImage =
            document.querySelector('#img') ||
            document.querySelector('#i3 img');

        // 不是实际图片阅读页时,保持原站页面不变。
        if (!readerImage) {
            releasePageBoot();
            return;
        }

        // The native image's inline onerror calls nl(), which navigates the
        // document to ?nl=... when an image fails. The custom reader owns image
        // failure and retry handling, so detach that native navigation hook.
        readerImage.removeAttribute('onerror');
        readerImage.onerror = null;

        // ehg_show.c.js binds its own resize handler to update_window_extents(),
        // which assumes the native #img and #i1 nodes remain in the page. The
        // custom reader replaces that image surface, so keep the native
        // callback from dereferencing the removed #img on viewport changes or
        // its already-scheduled 50ms callback. The reader installs its own
        // resize handler below.
        try { window.onresize = null; } catch (_) {}
        try {
            if (typeof window.update_window_extents === 'function') {
                window.update_window_extents = () => {};
            }
        } catch (_) {}

        window[RUN_KEY] = true;

        let readerStyle = null;
        let readerRoot = null;
        let readerControls = null;
        let readerPageTools = null;
        let readerPageTransition = null;
        let readerTransitionMessage = null;
        let updateBottomReaderStatus = () => {};
        const imageLoadWatchdogCallbacks = new WeakMap();
        let readerViewportMeta = null;
        let readerViewportCreated = false;
        let originalViewportContent = null;
        let originalParent = readerImage.parentNode;
        let originalNext = readerImage.nextSibling;
        const originalClass = readerImage.getAttribute('class');
        const originalImageSrc = readerImage.getAttribute('src');
        const originalImageSrcset = readerImage.getAttribute('srcset');
        const originalImageDataSrc = readerImage.getAttribute('data-src');
        const readerModeStorageKey = 'ehtw-reader-mode-v1';
        const readerZoomStorageKey = 'ehtw-reader-zoom-percent-v1';
        let resizeHandler = null;
        let readerInitializing = true;
        let lastReaderScrollY = window.scrollY;
        let suppressReaderScrollUntil = 0;
        let observer = null;
        let previousObserver = null;
        let pageObserver = null;
        let imageMemoryObserver = null;
        let pageSliderDragActive = false;
        let pageSliderReleaseHandler = null;
        let pageSliderCancelHandler = null;
        let updateReaderPageToolsViewport = () => {};
        let readerViewportUpdateRaf = 0;
        let wheelPagingHandler = null;
        let keyPagingHandler = null;
        let wheelUnlockTimer = null;
        let wheelGestureLocked = false;
        let scrollFallback = null;
        let readerChromeFadeTimer = null;
        // 1.17.255: a centre tap while natively zoomed asks for the chrome.
        // It stays hidden while the page moves and reappears once it settles.
        let readerZoomChromeRequested = false;
        let toggleReaderChromeWhileZoomed = () => false;
        // 1.17.261: Safari-style auto-hide in scroll mode. Scrolling down slides
        // the chrome away, scrolling up (or a centre tap) brings it back.
        let readerProgrammaticScrollAt = 0;
        let showReaderScrollHiddenChrome = () => false;
        let readerTouchActive = false;
        let readerTouchStartedOnChrome = false;
        let readerTouchStartViewport = null;
        let readerZoomResizeCount = 0;
        let readerZoomScrollRewriteCount = 0;
        let readerZoomUserScrollRewriteCount = 0;
        let readerZoomViewportEventCount = 0;
        let readerZoomNonUserScrollJumpCount = 0;
        let readerZoomLastScrollJump = 0;
        let readerZoomMaximumScale = 1;
        let readerZoomDiagnosticCount = 0;
        let readerZoomLastViewportScrollY = null;
        const readerLayoutWidth = () => document.documentElement.clientWidth || window.innerWidth || 0;
        const readerLayoutHeight = () => document.documentElement.clientHeight || window.innerHeight || 0;
        const isMobileReaderViewport = () => {
            try {
                return typeof window.matchMedia === 'function'
                    ? window.matchMedia('(max-width: 699px)').matches
                    : readerLayoutWidth() <= 699;
            } catch (_) {
                return readerLayoutWidth() <= 699;
            }
        };
        const isNativeReaderZoomed = () => Number(window.visualViewport?.scale) > 1.01;
        const recordReaderScrollRewrite = (userInitiated = false) => {
            readerProgrammaticScrollAt = performance.now();
            if (!isNativeReaderZoomed()) return;
            if (userInitiated) readerZoomUserScrollRewriteCount++;
            else readerZoomScrollRewriteCount++;
        };
        const imageListeners = [];
        const preloadStorageKey = 'ehtw-reader-preload-count-v2';
        const pageLinkPreloadStorageKey = 'ehtw-reader-page-link-preload-count-v1';

        try {
            readerViewportMeta = document.querySelector(
                'meta[name="viewport"]'
            );

            if (readerViewportMeta) {
                originalViewportContent =
                    readerViewportMeta.getAttribute('content');
                const normalizedViewport = /width\s*=\s*device-width/i.test(originalViewportContent || '')
                    ? String(originalViewportContent || 'width=device-width, initial-scale=1')
                    : 'width=device-width, initial-scale=1';
                const edgeToEdgeViewport = /viewport-fit\s*=/i.test(normalizedViewport)
                    ? normalizedViewport.replace(/viewport-fit\s*=\s*[^,]+/i, 'viewport-fit=cover')
                    : `${normalizedViewport.replace(/,\s*$/, '')}, viewport-fit=cover`;
                readerViewportMeta.setAttribute('content', edgeToEdgeViewport);
            } else {
                readerViewportMeta = document.createElement('meta');
                readerViewportMeta.setAttribute('name', 'viewport');
                readerViewportMeta.setAttribute(
                    'content',
                    'width=device-width, initial-scale=1, viewport-fit=cover'
                );
                document.head.appendChild(readerViewportMeta);
                readerViewportCreated = true;
            }

            const safePageUrl = (value, baseUrl) => {
                if (!value) return null;

                try {
                    const url = new URL(value, baseUrl);

                    if (
                        url.origin !== location.origin ||
                        !/^\/s\//i.test(url.pathname)
                    ) {
                        return null;
                    }

                    return url.href;
                } catch (_) {
                    return null;
                }
            };

            const findPageLink = (root, direction, baseUrl) => {
                const id = direction === 'previous' ? '#prev' : '#next';
                const rel = direction === 'previous' ? 'prev' : 'next';
                const direct =
                    root.querySelector(id) ||
                    root.querySelector(`a[rel="${rel}"]`);
                const directHref = direct?.getAttribute('href') || direct?.href;
                const safeDirectHref = safePageUrl(directHref, baseUrl);

                if (safeDirectHref) return safeDirectHref;

                const labels = direction === 'previous'
                    ? /\b(prev|previous|back)\b|上一页|前一页|<\s*prev/i
                    : /\b(next|forward)\b|下一页|后一页|next\s*>/i;

                return Array.from(
                    root.querySelectorAll('a[href]')
                ).find((link) => {
                    const href = safePageUrl(
                        link.getAttribute('href') || link.href,
                        baseUrl
                    );
                    const label = [
                        link.textContent,
                        link.getAttribute('title'),
                        link.getAttribute('aria-label')
                    ].filter(Boolean).join(' ');

                    return !!href && labels.test(label);
                }) || null;
            };

            const currentPageUrl = location.href;
            const readPagePosition = (root, pageUrl) => {
                const navigation = root.querySelector('#i2');
                const text = navigation?.innerText || navigation?.textContent || '';
                const textMatch = String(text).match(
                    /(?:^|\D)(\d+)\s*\/\s*(\d+)(?=\D|$)/
                );
                const dimensionsMatch = String(
                    navigation?.textContent || ''
                ).match(/(\d{2,5})\s*[x×]\s*(\d{2,5})/i);
                let pageMatch = null;
                let totalFromLinks = 0;

                try {
                    pageMatch = new URL(pageUrl, currentPageUrl)
                        .pathname.match(/-(\d+)\/?$/);
                } catch (_) {}

                Array.from(
                    navigation?.querySelectorAll('a[href]') || []
                ).forEach((link) => {
                    try {
                        const url = new URL(
                            link.getAttribute('href') || link.href,
                            pageUrl
                        );
                        const match = url.pathname.match(/-(\d+)\/?$/);

                        if (
                            url.origin === location.origin &&
                            /^\/s\//i.test(url.pathname) &&
                            match
                        ) {
                            totalFromLinks = Math.max(
                                totalFromLinks,
                                Number.parseInt(match[1], 10)
                            );
                        }
                    } catch (_) {}
                });

                return {
                    page: pageMatch
                        ? Number.parseInt(pageMatch[1], 10)
                        : textMatch
                            ? Number.parseInt(textMatch[1], 10)
                            : null,
                    total: totalFromLinks || (textMatch
                        ? Number.parseInt(textMatch[2], 10)
                        : null),
                    width: dimensionsMatch
                        ? Number.parseInt(dimensionsMatch[1], 10)
                        : null,
                    height: dimensionsMatch
                        ? Number.parseInt(dimensionsMatch[2], 10)
                        : null
                };
            };

            const initialPagePosition = readPagePosition(
                document,
                currentPageUrl
            );
            let totalPages = initialPagePosition.total;
            let galleryIndexUrl = null;
            try {
                const galleryLink = Array.from(
                    document.querySelectorAll('a[href]')
                ).find((link) => {
                    try {
                        const url = new URL(
                            link.getAttribute('href') || link.href,
                            currentPageUrl
                        );
                        return url.origin === location.origin && /^\/g\/\d+\//i.test(url.pathname);
                    } catch (_) {
                        return false;
                    }
                });
                if (galleryLink) {
                    const url = new URL(
                        galleryLink.getAttribute('href') || galleryLink.href,
                        currentPageUrl
                    );
                    url.search = '';
                    url.hash = '';
                    galleryIndexUrl = url.href.endsWith('/') ? url.href : `${url.href}/`;
                }
            } catch (_) {}
            const galleryIndexCache = new Map();
            let galleryIndexBlockedUntil = 0;
            const galleryIndexFetchPromises = new Map();
            const knownPageUrls = new Map();
            const galleryPageInfoCache = new Map();
            const galleryPageInfoFetchPromises = new Map();
            const galleryPageInfoFetchWaiters = [];
            let activeGalleryPageInfoFetches = 0;
            if (initialPagePosition.page) {
                knownPageUrls.set(initialPagePosition.page, currentPageUrl);
            }
            const currentPreviousUrl = findPageLink(
                document,
                'previous',
                currentPageUrl
            );
            let previousPageUrl =
                currentPreviousUrl === currentPageUrl
                    ? null
                    : currentPreviousUrl;
            let nextPageUrl = findPageLink(
                document,
                'next',
                currentPageUrl
            );

            if (nextPageUrl === currentPageUrl) {
                nextPageUrl = null;
            }

            readerStyle = document.createElement('style');
            readerStyle.textContent = `
                html.ehtw-reader,
                html.ehtw-reader body {
                    min-height: 100%;
                    background: #000 !important;
                    color: #fff !important;
                }

                html.ehtw-reader {
                    scroll-behavior: auto !important;
                    height: auto !important;
                    overflow-x: hidden !important;
                    overflow-y: auto !important;
                }

                html.ehtw-reader body {
                    margin: 0 !important;
                    padding: 0 !important;
                    position: static !important;
                    height: auto !important;
                    max-height: none !important;
                    min-height: 100vh !important;
                    overflow-x: hidden !important;
                    overflow-y: auto !important;
                }

                html.ehtw-reader:not(.ehtw-reader-click) body {
                    overflow: visible !important;
                }

                html.ehtw-reader body > :not(.ehtw-reader-root) {
                    display: none !important;
                }

                html.ehtw-reader .ehtw-reader-root {
                    display: flex !important;
                    flex-direction: column !important;
                    align-items: stretch !important;
                    min-height: 100vh !important;
                    width: 100% !important;
                    margin: 0 !important;
                    padding: 0 !important;
                    background: #000 !important;
                    color: #fff !important;
                }

                html.ehtw-reader .ehtw-reader-images {
                    display: flex !important;
                    flex-direction: column !important;
                    align-items: center !important;
                    gap: 0 !important;
                    width: 100% !important;
                    min-width: 0 !important;
                    margin: 0 !important;
                    padding: 56px 0 0 !important;
                    overflow-anchor: none !important;
                    background: #000 !important;
                }

                html.ehtw-reader .ehtw-reader-page-image {
                    position: absolute !important;
                    inset: 0 !important;
                    display: block !important;
                    width: 100% !important;
                    height: 100% !important;
                    min-width: 0 !important;
                    min-height: 0 !important;
                    max-width: 100% !important;
                    max-height: none !important;
                    margin: 0 !important;
                    object-fit: contain !important;
                    background: #000 !important;
                    -webkit-user-drag: none !important;
                    user-select: none !important;
                    -webkit-user-select: none !important;
                    touch-action: manipulation !important;
                }

                html.ehtw-reader .ehtw-reader-image-frame {
                    position: relative !important;
                    display: grid !important;
                    flex: 0 0 auto !important;
                    place-items: center !important;
                    max-width: 100vw !important;
                    margin: 0 auto !important;
                    background: #000 !important;
                }

                html.ehtw-reader .ehtw-reader-loading-spinner {
                    position: absolute !important;
                    inset: 0 !important;
                    z-index: 2 !important;
                    width: 40px !important;
                    height: 40px !important;
                    margin: auto !important;
                    box-sizing: border-box !important;
                    border: 3px solid #444 !important;
                    border-top-color: #fff !important;
                    border-radius: 50% !important;
                    animation: ehtw-reader-spin .8s linear infinite !important;
                    pointer-events: none !important;
                }

                html.ehtw-reader .ehtw-reader-page-transition {
                    position: fixed !important;
                    z-index: 2147482999 !important;
                    inset: 0 !important;
                    display: none !important;
                    align-content: center !important;
                    justify-items: center !important;
                    gap: 12px !important;
                    background: rgb(0 0 0 / 48%) !important;
                    color: #aaa !important;
                    font: 14px/1.3 -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif !important;
                    pointer-events: none !important;
                }

                html.ehtw-reader.ehtw-reader-page-pending .ehtw-reader-page-transition {
                    display: grid !important;
                }

                html.ehtw-reader.ehtw-reader-page-pending .ehtw-reader-images {
                    visibility: hidden !important;
                }

                html.ehtw-reader .ehtw-reader-page-transition .ehtw-reader-loading-spinner {
                    display: block !important;
                    grid-area: auto !important;
                    width: 48px !important;
                    height: 48px !important;
                }

                html.ehtw-reader .ehtw-reader-retry-button {
                    position: absolute !important;
                    inset: 0 !important;
                    z-index: 3 !important;
                    display: none !important;
                    width: 52px !important;
                    height: 52px !important;
                    margin: auto !important;
                    align-items: center !important;
                    justify-content: center !important;
                    padding: 0 !important;
                    border: 1px solid #555 !important;
                    border-radius: 50% !important;
                    background: rgb(0 0 0 / 70%) !important;
                    color: #fff !important;
                    cursor: pointer !important;
                }

                html.ehtw-reader .ehtw-reader-image-frame.ehtw-reader-image-failed .ehtw-reader-retry-button {
                    display: inline-flex !important;
                }

                html.ehtw-reader .ehtw-reader-retry-button svg {
                    width: 28px !important;
                    height: 28px !important;
                    fill: none !important;
                    stroke: currentColor !important;
                    stroke-width: 2 !important;
                    stroke-linecap: round !important;
                    stroke-linejoin: round !important;
                }

                html.ehtw-reader .ehtw-reader-image-frame:not(.ehtw-reader-image-loading) .ehtw-reader-loading-spinner {
                    display: none !important;
                }

                @keyframes ehtw-reader-spin {
                    to { transform: rotate(360deg); }
                }

                html.ehtw-reader .ehtw-reader-sentinel {
                    flex: 0 0 1px !important;
                    width: 100% !important;
                    height: 1px !important;
                    background: #000 !important;
                    pointer-events: none !important;
                }

                html.ehtw-reader .ehtw-reader-controls {
                    position: fixed !important;
                    transform-origin: 50% 0 !important;
                    z-index: 2147483000 !important;
                    pointer-events: auto !important;
                    top: 10px !important;
                    left: 50% !important;
                    display: flex !important;
                    flex-wrap: wrap !important;
                    justify-content: center !important;
                    max-width: calc(100vw - 20px) !important;
                    gap: 4px !important;
                    padding: 4px !important;
                    border: 1px solid #1a1a1a !important;
                    border-radius: 999px !important;
                    background: #000 !important;
                    overflow: visible !important;
                    transform: translateX(-50%) !important;
                    will-change: transform !important;
                }

                /* 1.17.263 (user request): the top controls also disappear at
                   once; a bar sliding near the edge keeps Safari's tint. */
                html.ehtw-reader .ehtw-reader-root.ehtw-reader-chrome-scroll-hidden .ehtw-reader-controls {
                    display: none !important;
                    pointer-events: none !important;
                }

                /* 1.17.262 (user request): the bottom tools disappear at once
                   instead of sliding (display:none also stops iOS Safari from
                   extending their colour under its toolbar). */
                html.ehtw-reader .ehtw-reader-root.ehtw-reader-chrome-scroll-hidden .ehtw-reader-page-tools {
                    display: none !important;
                }

                html.ehtw-reader .ehtw-reader-root.ehtw-reader-chrome-moving .ehtw-reader-controls,
                html.ehtw-reader .ehtw-reader-root.ehtw-reader-chrome-moving .ehtw-reader-page-tools {
                    opacity: 0 !important;
                    pointer-events: none !important;
                    transition: none !important;
                }

                html.ehtw-reader .ehtw-reader-page-count {
                    display: inline-flex !important;
                    align-items: center !important;
                    min-height: 32px !important;
                    padding: 0 8px !important;
                    color: #aaa !important;
                    font: 13px/1.2 -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif !important;
                    white-space: nowrap !important;
                }

                html.ehtw-reader .ehtw-reader-mode {
                    min-height: 32px !important;
                    padding: 0 12px !important;
                    border: 0 !important;
                    border-radius: 999px !important;
                    background: transparent !important;
                    color: #aaa !important;
                    font: 13px/1.2 -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif !important;
                    cursor: pointer !important;
                }

                html.ehtw-reader .ehtw-reader-mode[aria-pressed="true"] {
                    background: #1d9bf0 !important;
                    color: #fff !important;
                }

                html.ehtw-reader .ehtw-reader-page-tools {
                    position: fixed !important;
                    transform-origin: 50% 100% !important;
                    z-index: 2147483647 !important;
                    pointer-events: auto !important;
                    right: max(10px, env(safe-area-inset-right)) !important;
                    bottom: max(10px, env(safe-area-inset-bottom)) !important;
                    left: max(10px, env(safe-area-inset-left)) !important;
                    display: flex !important;
                    flex-direction: column !important;
                    gap: 6px !important;
                    width: min(620px, calc(100vw - 20px)) !important;
                    box-sizing: border-box !important;
                    margin: 0 auto !important;
                    padding: 8px 12px !important;
                    border: 1px solid #222 !important;
                    border-radius: 14px !important;
                    background: rgb(0 0 0 / 94%) !important;
                    color: #fff !important;
                    font: 13px/1.2 -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif !important;
                    transform: none !important;
                    will-change: transform !important;
                }

                html.ehtw-reader .ehtw-reader-zoom-controls,
                html.ehtw-reader .ehtw-reader-page-seek {
                    display: flex !important;
                    align-items: center !important;
                    justify-content: center !important;
                    gap: 8px !important;
                    min-width: 0 !important;
                }

                html.ehtw-reader .ehtw-reader-zoom-button,
                html.ehtw-reader .ehtw-reader-fit-button {
                    display: inline-flex !important;
                    flex: 0 0 auto !important;
                    align-items: center !important;
                    justify-content: center !important;
                    min-width: 42px !important;
                    min-height: 40px !important;
                    padding: 0 10px !important;
                    border: 1px solid #333 !important;
                    border-radius: 9px !important;
                    background: #111 !important;
                    color: #fff !important;
                    font: 18px/1 -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif !important;
                    cursor: pointer !important;
                }

                html.ehtw-reader .ehtw-reader-fit-button {
                    min-width: 52px !important;
                    font-size: 13px !important;
                }

                html.ehtw-reader .ehtw-reader-preload-details {
                    position: relative !important;
                    flex: 0 0 auto !important;
                }

                html.ehtw-reader .ehtw-reader-preload-details > summary {
                    display: inline-flex !important;
                    align-items: center !important;
                    justify-content: center !important;
                    width: 44px !important;
                    min-height: 40px !important;
                    box-sizing: border-box !important;
                    border: 1px solid #333 !important;
                    border-radius: 9px !important;
                    background: #111 !important;
                    color: #ddd !important;
                    font-size: 0 !important;
                    list-style: none !important;
                    cursor: pointer !important;
                }

                html.ehtw-reader .ehtw-reader-preload-details > summary svg {
                    display: block !important;
                    width: 20px !important;
                    height: 20px !important;
                    flex: 0 0 auto !important;
                    color: #fff !important;
                }

                html.ehtw-reader .ehtw-reader-preload-details > summary::-webkit-details-marker {
                    display: none !important;
                }

                html.ehtw-reader .ehtw-reader-preload-popover {
                    position: absolute !important;
                    right: 0 !important;
                    top: calc(100% + 8px) !important;
                    bottom: auto !important;
                    z-index: 2147483001 !important;
                    display: flex !important;
                    align-items: stretch !important;
                    flex-direction: column !important;
                    gap: 8px !important;
                    width: max-content !important;
                    max-width: calc(100vw - 24px) !important;
                    box-sizing: border-box !important;
                    padding: 10px !important;
                    border: 1px solid #333 !important;
                    border-radius: 10px !important;
                    background: #111 !important;
                    color: #ddd !important;
                    font: 13px/1.2 -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif !important;
                    white-space: nowrap !important;
                }

                html.ehtw-reader .ehtw-reader-preload-details:not([open]) > .ehtw-reader-preload-popover {
                    display: none !important;
                }

                html.ehtw-reader .ehtw-reader-preload-row {
                    display: flex !important;
                    align-items: center !important;
                    justify-content: space-between !important;
                    gap: 12px !important;
                }

                html.ehtw-reader .ehtw-reader-preload-count,
                html.ehtw-reader .ehtw-reader-page-link-preload-count {
                    width: 58px !important;
                    height: 36px !important;
                    box-sizing: border-box !important;
                    padding: 0 4px !important;
                    border: 1px solid #444 !important;
                    border-radius: 8px !important;
                    background: #000 !important;
                    color: #fff !important;
                    font: 15px/1.2 -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif !important;
                    text-align: center !important;
                }

                html.ehtw-reader .ehtw-reader-zoom-input {
                    width: 58px !important;
                    height: 36px !important;
                    box-sizing: border-box !important;
                    padding: 0 4px !important;
                    border: 1px solid #333 !important;
                    border-radius: 8px !important;
                    background: #000 !important;
                    color: #fff !important;
                    font: 15px/1.2 -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif !important;
                    text-align: center !important;
                }

                html.ehtw-reader .ehtw-reader-zoom-unit,
                html.ehtw-reader .ehtw-reader-seek-current,
                html.ehtw-reader .ehtw-reader-seek-total {
                    flex: 0 0 auto !important;
                    color: #aaa !important;
                    font-variant-numeric: tabular-nums !important;
                    white-space: nowrap !important;
                }

                html.ehtw-reader .ehtw-reader-page-slider {
                    flex: 1 1 auto !important;
                    width: 100% !important;
                    min-width: 40px !important;
                    height: 32px !important;
                    margin: 0 !important;
                    accent-color: #1d9bf0 !important;
                    cursor: pointer !important;
                    touch-action: pan-x !important;
                }

                html.ehtw-reader .ehtw-reader-seek-status {
                    display: block !important;
                    min-height: 14px !important;
                    visibility: hidden !important;
                    color: #aaa !important;
                    font-size: 12px !important;
                    text-align: center !important;
                }

                html.ehtw-reader .ehtw-reader-seek-status:not(:empty) {
                    visibility: visible !important;
                }

                html.ehtw-reader.ehtw-reader-click .ehtw-reader-page-image {
                    cursor: pointer !important;
                    pointer-events: auto !important;
                }

                html.ehtw-reader.ehtw-reader-click body {
                    height: 100vh !important;
                    height: 100dvh !important;
                    overflow: hidden !important;
                    overflow-anchor: none !important;
                }

                html.ehtw-reader.ehtw-reader-click .ehtw-reader-root,
                html.ehtw-reader.ehtw-reader-click .ehtw-reader-images {
                    height: 100vh !important;
                    min-height: 100vh !important;
                    height: 100dvh !important;
                    min-height: 100dvh !important;
                    overflow: hidden !important;
                    overflow-anchor: none !important;
                }

                html.ehtw-reader.ehtw-reader-click .ehtw-reader-images {
                    flex: 1 1 auto !important;
                    position: relative !important;
                    justify-content: center !important;
                    padding-top: 0 !important;
                }

                html.ehtw-reader.ehtw-reader-click .ehtw-reader-image-frame:not(.ehtw-reader-active) {
                    display: grid !important;
                    position: absolute !important;
                    top: 0 !important;
                    left: 0 !important;
                    visibility: hidden !important;
                    pointer-events: none !important;
                }

                html.ehtw-reader.ehtw-reader-zoomed .ehtw-reader-image-frame {
                    max-width: none !important;
                }

                html.ehtw-reader.ehtw-reader-zoomed .ehtw-reader-page-image {
                    max-width: none !important;
                }

                html.ehtw-reader.ehtw-reader-click.ehtw-reader-zoomed .ehtw-reader-images {
                    box-sizing: border-box !important;
                    align-items: center !important;
                    justify-content: flex-start !important;
                    overflow-x: hidden !important;
                    overflow-y: auto !important;
                    padding: 72px 0 124px !important;
                    touch-action: manipulation !important;
                }

                html.ehtw-reader.ehtw-reader-click .ehtw-reader-image-frame.ehtw-reader-active {
                    display: grid !important;
                    position: relative !important;
                    visibility: visible !important;
                    pointer-events: auto !important;
                }

                html.ehtw-reader .ehtw-reader-page-image[data-ehtw-load-failed="true"] {
                    cursor: pointer !important;
                }

                html.ehtw-reader.ehtw-reader-click .ehtw-reader-sentinel,
                html.ehtw-reader .ehtw-reader-settings-hidden .ehtw-reader-controls,
                html.ehtw-reader .ehtw-reader-settings-hidden .ehtw-reader-page-tools {
                    display: none !important;
                }

                html.ehtw-reader .ehtw-reader-preload-setting {
                    display: inline-flex !important;
                    align-items: center !important;
                    gap: 5px !important;
                    padding: 0 8px !important;
                    color: #aaa !important;
                    font: 12px/1.2 -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif !important;
                }

                html.ehtw-reader .ehtw-reader-preload-count {
                    width: 48px !important;
                    height: 28px !important;
                    box-sizing: border-box !important;
                    padding: 0 4px !important;
                    border: 1px solid #333 !important;
                    border-radius: 8px !important;
                    background: #000 !important;
                    color: #fff !important;
                    font: 13px/1.2 -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif !important;
                    text-align: center !important;
                }

                @media (max-width: 699px) {
                    html.ehtw-reader .ehtw-reader-controls {
                        top: calc(env(safe-area-inset-top, 0px) + 8px) !important;
                        width: max-content !important;
                        max-width: calc(100vw - 16px) !important;
                        flex-wrap: nowrap !important;
                        padding: 4px !important;
                        gap: 4px !important;
                    }

                    html.ehtw-reader .ehtw-reader-page-tools {
                        right: max(8px, env(safe-area-inset-right)) !important;
                        bottom: max(8px, env(safe-area-inset-bottom)) !important;
                        left: max(8px, env(safe-area-inset-left)) !important;
                        width: calc(100vw - 16px) !important;
                        gap: 4px !important;
                        padding: 6px 8px !important;
                        border-radius: 12px !important;
                    }

                    html.ehtw-reader .ehtw-reader-zoom-controls,
                    html.ehtw-reader .ehtw-reader-page-seek {
                        gap: 6px !important;
                    }

                    html.ehtw-reader .ehtw-reader-zoom-button,
                    html.ehtw-reader .ehtw-reader-fit-button {
                        min-height: 40px !important;
                    }

                    html.ehtw-reader .ehtw-reader-preload-details > summary {
                        width: 40px !important;
                        min-height: 40px !important;
                    }

                    html.ehtw-reader .ehtw-reader-mode {
                        min-height: 44px !important;
                        padding: 0 8px !important;
                        font-size: 15px !important;
                    }

                    html.ehtw-reader .ehtw-reader-page-count {
                        min-height: 44px !important;
                        padding: 0 4px !important;
                        font-size: 14px !important;
                    }

                    html.ehtw-reader .ehtw-reader-preload-setting {
                        min-height: 44px !important;
                        gap: 4px !important;
                        padding: 0 4px !important;
                        font-size: 14px !important;
                    }

                    html.ehtw-reader .ehtw-reader-preload-count {
                        width: 56px !important;
                        height: 40px !important;
                        font-size: 16px !important;
                    }

                    html.ehtw-reader .ehtw-reader-images {
                        padding-top: 0 !important;
                    }

                    html.ehtw-reader.ehtw-reader-click .ehtw-reader-images {
                        justify-content: center !important;
                        padding-top: 0 !important;
                    }
                }
            `;

            document.head.appendChild(readerStyle);
            document.documentElement.classList.add('ehtw-reader');

            readerRoot = document.createElement('main');
            readerRoot.className = 'ehtw-reader-root';

            readerControls = document.createElement('div');
            readerControls.className = 'ehtw-reader-controls';
            readerControls.setAttribute('role', 'group');
            readerControls.setAttribute('aria-label', '阅读方式');

            const readerImages = document.createElement('div');
            readerImages.className = 'ehtw-reader-images';

            let currentPageNumber = initialPagePosition.page;
            let pageSlider = null;
            let zoomInput = null;
            let pageSeekCurrent = null;
            let pageSeekTotal = null;
            let pageSeekStatus = null;
            let navigateToPageNumber = () => Promise.resolve();
            let historyTimer = null;
            const syncReaderHistory = (pageNumber) => {
                if (!Number.isFinite(pageNumber)) return;
                if (historyTimer) window.clearTimeout(historyTimer);
                historyTimer = window.setTimeout(() => {
                    historyTimer = null;
                    const pageUrl = knownPageUrls.get(pageNumber);
                    if (!pageUrl || pageUrl === location.href) return;
                    const suppressionReason = historyStateWriteSuppressionReason(location.href);
                    const readerRouteIsNoLongerCurrent = !/^\/s\//i.test(location.pathname) ||
                        !/^\/s\//i.test(new URL(spaRenderedHref, location.href).pathname) ||
                        location.href !== spaRenderedHref;
                    if (suppressionReason || readerRouteIsNoLongerCurrent) {
                        if (navLogEnabled) navLog('reader-history-replace-skipped', {
                            pageNumber,
                            targetHref: pageUrl,
                            reason: readerRouteIsNoLongerCurrent ? 'reader-route-is-no-longer-current' : suppressionReason,
                            currentHref: location.href,
                            renderedHref: spaRenderedHref
                        });
                        return;
                    }
                    const currentHref = location.href;
                    const readerState = recoverSpaHistoryState(currentHref, window.history.state);
                    if (navLogEnabled) navLog('reader-history-replace', {
                        pageNumber,
                        targetHref: pageUrl,
                        scrollY: window.scrollY || 0,
                        historyState: summarizeNavLogState(readerState)
                    });
                    if (safeReplaceHistoryState(window.history, readerState, '', pageUrl, 'reader-page-url-sync')) {
                        forgetRememberedSpaHistoryState(currentHref);
                        rememberSpaHistoryState(pageUrl, readerState);
                        // Reader page tracking is an intentional same-route URL
                        // change. Keep Safari's SPA reconciliation poll in sync
                        // so it does not treat each scroll page as a Back event.
                        spaRenderedHref = pageUrl;
                    }
                }, 500);
            };
            const pageCounter = document.createElement('span');
            pageCounter.className = 'ehtw-reader-page-count';
            pageCounter.setAttribute('aria-live', 'polite');
            let lastReaderCounterPageNumber = currentPageNumber;
            const readerVisitedPageNumbers = new Set(
                Number.isFinite(currentPageNumber) ? [currentPageNumber] : []
            );

            const updatePageCounter = (pageNumber) => {
                const pageChanged = Number.isFinite(pageNumber) &&
                    pageNumber !== lastReaderCounterPageNumber;
                const pageWasVisited = readerVisitedPageNumbers.has(pageNumber);
                if (Number.isFinite(pageNumber)) {
                    currentPageNumber = pageNumber;
                    lastReaderCounterPageNumber = pageNumber;
                    readerVisitedPageNumbers.add(pageNumber);
                }
                if (pageChanged && pageWasVisited) {
                    const returnedImage = readerPageImages?.find((image) =>
                        pageInfo.get(image)?.pageNumber === pageNumber &&
                        image.getAttribute('data-ehtw-load-failed') === 'true'
                    );
                    if (returnedImage) {
                        const listener = imageListeners.find((item) => item.image === returnedImage);
                        listener?.resetAutoRetryAttempts?.();
                        listener?.retryManually?.();
                    }
                }

                pageCounter.textContent =
                    `${currentPageNumber ?? '?'} / ${totalPages ?? '?'}`;
                if (pageSlider) {
                    pageSlider.max = String(Math.max(1, totalPages || currentPageNumber || 1));
                    pageSlider.value = String(Math.max(1, currentPageNumber || 1));
                }
                if (pageSeekCurrent) {
                    pageSeekCurrent.textContent = String(currentPageNumber ?? '?');
                }
                if (pageSeekTotal) {
                    pageSeekTotal.textContent = String(totalPages ?? '?');
                }
                syncReaderHistory(currentPageNumber);

            };

            updatePageCounter(currentPageNumber);
            readerControls.appendChild(pageCounter);

            let clickPaging = false;
            try {
                clickPaging = window.localStorage?.getItem(readerModeStorageKey) === 'click';
            } catch (_) {
                clickPaging = false;
            }
            let clickModeTouchGuardAttached = false;
            // 1.17.248: a single-finger gesture that starts at a screen edge belongs
            // to iOS swipe-back/forward; canceling its touchmove kills that gesture.
            const CLICK_MODE_EDGE_SWIPE_ZONE = 40;
            let clickModeTouchStartedAtEdge = false;
            const recordClickModeTouchStart = (event) => {
                const x = Number(event.touches?.[0]?.clientX);
                const width = document.documentElement.clientWidth || window.innerWidth || 0;
                clickModeTouchStartedAtEdge = event.touches?.length === 1 && Number.isFinite(x) &&
                    (x <= CLICK_MODE_EDGE_SWIPE_ZONE || (width > 0 && x >= width - CLICK_MODE_EDGE_SWIPE_ZONE));
            };
            const preventClickModePageScroll = (event) => {
                if (
                    !clickPaging ||
                    clickModeTouchStartedAtEdge ||
                    document.documentElement.classList.contains('ehtw-reader-zoomed') ||
                    (window.visualViewport && window.visualViewport.scale > 1.01) ||
                    event.touches?.length !== 1 ||
                    event.target?.closest?.('.ehtw-reader-controls, .ehtw-reader-page-tools')
                ) return;
                event.preventDefault();
            };
            const setClickModeTouchGuard = (enabled) => {
                if (enabled && !clickModeTouchGuardAttached) {
                    addSpaPageEventListener(document, 'touchstart', recordClickModeTouchStart, {
                        capture: true,
                        passive: true
                    });
                    addSpaPageEventListener(document, 'touchmove', preventClickModePageScroll, {
                        capture: true,
                        passive: false
                    });
                    clickModeTouchGuardAttached = true;
                } else if (!enabled && clickModeTouchGuardAttached) {
                    document.removeEventListener?.('touchmove', preventClickModePageScroll, true);
                    document.removeEventListener?.('touchstart', recordClickModeTouchStart, true);
                    clickModeTouchStartedAtEdge = false;
                    clickModeTouchGuardAttached = false;
                }
            };
            const modes = [];
            const pageInfo = new WeakMap();
            const pageFrames = new WeakMap();
            const readerPageImages = [readerImage];
            let targetedSeekImagePageNumber = null;
            let activeImageIndex = 0;
            let zoomPercent = 100;
            try {
                const savedZoomPercent = Number.parseInt(
                    window.localStorage?.getItem(readerZoomStorageKey),
                    10
                );
                if (Number.isFinite(savedZoomPercent)) {
                    zoomPercent = Math.max(25, Math.min(300, savedZoomPercent));
                }
            } catch (_) {
                // Keep the default zoom when browser storage is unavailable.
            }
            let preloadCount = 5;
            try {
                const savedPreloadCount = Number.parseInt(
                    window.localStorage?.getItem(preloadStorageKey),
                    10
                );

                if (Number.isFinite(savedPreloadCount)) {
                    preloadCount = Math.max(0, Math.min(50, savedPreloadCount));
                }
            } catch (_) {
                // Keep the default when browser storage is unavailable.
            }
            let pageLinkPreloadCount = 10;
            try {
                const savedPageLinkPreloadCount = Number.parseInt(
                    window.localStorage?.getItem(pageLinkPreloadStorageKey),
                    10
                );
                if (Number.isFinite(savedPageLinkPreloadCount)) {
                    pageLinkPreloadCount = Math.max(
                        0,
                        Math.min(50, savedPageLinkPreloadCount)
                    );
                }
            } catch (_) {
                // Keep the default page link preload count when storage is unavailable.
            }
            let activeBatchPromise = null;
            let activePreviousBatchPromise = null;
            let readerSequenceGeneration = 0;
            let activeNextAbortController = null;
            let activePreviousAbortController = null;
            let loadingStopped = false;
            let previousLoadingStopped = false;
            let loadNextBatch = () => Promise.resolve();
            let loadPreviousBatch = () => Promise.resolve();
            let pumpReaderImagePreloadQueue = () => {};
            let readerImageQueueCenterIndex = 0;
            const activeReaderImageQueueLoads = new Set();
            const seenPages = new Set([currentPageUrl]);
            const topSentinel = document.createElement('div');
            topSentinel.className = 'ehtw-reader-sentinel';
            topSentinel.setAttribute('aria-hidden', 'true');
            const bottomSentinel = document.createElement('div');
            bottomSentinel.className = 'ehtw-reader-sentinel';
            bottomSentinel.setAttribute('aria-hidden', 'true');

            const restoreImageResource = (image) => {
                if (image?.dataset.ehtwMemoryUnloaded !== 'true') return;

                const source = image.dataset.ehtwMemorySource;
                if (!source) return;

                const sourceSet = image.dataset.ehtwMemorySourceSet;
                delete image.dataset.ehtwMemoryUnloaded;
                delete image.dataset.ehtwMemorySource;
                delete image.dataset.ehtwMemorySourceSet;
                delete image.dataset.ehtwPreloadPaused;
                image.dataset.ehtwSeekDeferredSource = source;
                if (sourceSet) image.dataset.ehtwSeekDeferredSourceSet = sourceSet;
                image.loading = 'lazy';
                pageFrames.get(image)?.classList.add('ehtw-reader-image-loading');
                updateBottomReaderStatus();
            };

            const releaseReaderImagesOutsideWindow = (centerIndex) => {
                if (!Number.isFinite(centerIndex)) return;
                const keepRadius = Math.max(0, Math.floor(preloadCount));
                readerPageImages.forEach((image, imageIndex) => {
                    if (
                        image.dataset.ehtwMemoryUnloaded === 'true' ||
                        !image.complete ||
                        image.naturalWidth <= 0
                    ) return;

                    if (Math.abs(imageIndex - centerIndex) <= keepRadius) return;

                    const source = image.currentSrc || image.getAttribute('src');
                    if (!source) return;
                    const sourceSet = image.getAttribute('srcset');
                    image.dataset.ehtwMemorySource = source;
                    if (sourceSet) image.dataset.ehtwMemorySourceSet = sourceSet;
                    image.dataset.ehtwMemoryUnloaded = 'true';
                    image.dataset.ehtwPreloadPaused = 'true';
                    activeReaderImageQueueLoads.delete(image);
                    imageListeners.find((entry) => entry.image === image)
                        ?.cancelAutoRetry?.();
                    image.removeAttribute('srcset');
                    image.removeAttribute('src');
                    image.loading = 'lazy';
                });
            };

            const startDeferredImageResource = (image) => {
                if (!image) return;
                const imageIndex = readerPageImages.indexOf(image);
                if (imageIndex >= 0) readerImageQueueCenterIndex = imageIndex;
                restoreImageResource(image);
                pumpReaderImagePreloadQueue();
            };

            const pauseReaderImagesOutsideWindow = (centerIndex, shouldPause) => {
                if (!shouldPause) return;
                activeReaderImageQueueLoads.forEach((image) => {
                    const imageIndex = readerPageImages.indexOf(image);
                    if (imageIndex < 0) {
                        activeReaderImageQueueLoads.delete(image);
                        return;
                    }
                    if (Math.abs(imageIndex - centerIndex) <= preloadCount) return;
                    if (image.complete && image.naturalWidth > 0) {
                        activeReaderImageQueueLoads.delete(image);
                        return;
                    }

                    const source = image.getAttribute('src');
                    if (!source) {
                        activeReaderImageQueueLoads.delete(image);
                        return;
                    }
                    image.dataset.ehtwSeekDeferredSource = source;
                    const sourceSet = image.getAttribute('srcset');
                    if (sourceSet) image.dataset.ehtwSeekDeferredSourceSet = sourceSet;
                    image.dataset.ehtwPreloadPaused = 'true';
                    image.fetchPriority = 'low';
                    imageListeners.find((entry) => entry.image === image)
                        ?.cancelAutoRetry?.();
                    // Keep this slot until the browser reports the canceled
                    // request as an error; removing src is asynchronous.
                    image.removeAttribute('srcset');
                    image.removeAttribute('src');
                    image.loading = 'lazy';
                });
            };

            const startDeferredImagesAround = (centerIndex) => {
                if (
                    !Number.isFinite(centerIndex) ||
                    centerIndex < 0 ||
                    centerIndex >= readerPageImages.length
                ) return;

                readerImageQueueCenterIndex = Math.floor(centerIndex);

                updateReaderImagePriorities(
                    pageInfo.get(readerPageImages[centerIndex])?.pageNumber ||
                    currentPageNumber
                );
                pumpReaderImagePreloadQueue();
            };

            const updateReaderImagePriorities = (centerPageNumber) => {
                if (!Number.isFinite(centerPageNumber)) return;

                readerPageImages.forEach((image) => {
                    const pageNumber = pageInfo.get(image)?.pageNumber;
                    if (!Number.isFinite(pageNumber)) return;

                    const priority = getReaderImagePriority(
                        pageNumber,
                        centerPageNumber,
                        targetedSeekImagePageNumber,
                        preloadCount
                    );
                    if (image.fetchPriority !== priority) {
                        image.fetchPriority = priority;
                    }
                });
            };

            const showActiveImage = (index, userInitiated = false) => {
                if (!readerPageImages.length) return;

                activeImageIndex = Math.max(
                    0,
                    Math.min(index, readerPageImages.length - 1)
                );
                document.documentElement.classList.remove('ehtw-reader-page-pending');
                if (readerTransitionMessage) readerTransitionMessage.textContent = '';

                readerPageImages.forEach((image, imageIndex) => {
                    image.classList.toggle(
                        'ehtw-reader-active',
                        imageIndex === activeImageIndex
                    );
                    pageFrames.get(image)?.classList.toggle(
                        'ehtw-reader-active',
                        imageIndex === activeImageIndex
                    );
                    if (clickPaging) {
                        if (imageIndex === activeImageIndex) {
                            startDeferredImageResource(image);
                            restoreImageResource(image);
                        }
                    }
                });
                releaseReaderImagesOutsideWindow(activeImageIndex);
                if (clickPaging) startDeferredImagesAround(activeImageIndex);

                const activeInfo = pageInfo.get(
                    readerPageImages[activeImageIndex]
                );
                const activeImage = readerPageImages[activeImageIndex];
                sizeReaderFrame(
                    pageFrames.get(activeImage),
                    activeImage,
                    activeInfo,
                    clickPaging
                );

                if (activeInfo?.pageNumber) {
                    updatePageCounter(activeInfo.pageNumber);
                }
                updateBottomReaderStatus();

                const linkWindowEdge = Math.max(1, preloadCount);
                if (
                    activeImageIndex >= readerPageImages.length - linkWindowEdge - 1 &&
                    nextPageUrl &&
                    !loadingStopped
                ) {
                    loadNextBatch();
                }
                if (
                    activeImageIndex <= linkWindowEdge &&
                    previousPageUrl &&
                    !previousLoadingStopped
                ) {
                    loadPreviousBatch();
                }

                if (!isNativeReaderZoomed() || userInitiated) {
                    readerProgrammaticScrollAt = performance.now();
                    if (userInitiated) recordReaderScrollRewrite(true);
                    readerPageImages[activeImageIndex]?.scrollIntoView?.({
                        block: 'center'
                    });
                }
            };

            const scrollImageToViewportCenter = (image, userInitiated = false) => {
                if ((isNativeReaderZoomed() && !userInitiated) || !image || readerLayoutHeight() <= 0) return;

                const bounds = image.getBoundingClientRect();
                if (!Number.isFinite(bounds.top) || !Number.isFinite(bounds.height)) {
                    return;
                }

                const desiredTop = (readerLayoutHeight() - bounds.height) / 2;
                const shift = bounds.top - desiredTop;
                const scrollRoot = document.scrollingElement ||
                    document.documentElement;

                if (Math.abs(shift) > 1 && scrollRoot) {
                    recordReaderScrollRewrite(userInitiated);
                    scrollRoot.scrollTop += shift;
                    suppressReaderScrollUntil = performance.now() + 200;
                }
            };

            const findImageAtViewportCenter = () => {
                if (readerLayoutHeight() <= 0) {
                    return activeImageIndex;
                }

                const viewportCenter = readerLayoutHeight() / 2;
                let closestIndex = activeImageIndex;
                let closestDistance = Infinity;

                readerPageImages.forEach((image, index) => {
                    const bounds = image.getBoundingClientRect();
                    const imageCenter = (bounds.top + bounds.bottom) / 2;
                    const distance = Math.abs(imageCenter - viewportCenter);

                    if (Number.isFinite(distance) && distance < closestDistance) {
                        closestIndex = index;
                        closestDistance = distance;
                    }
                });

                return closestIndex;
            };

            if (typeof window.IntersectionObserver === 'function') {
                imageMemoryObserver = new window.IntersectionObserver(
                    (entries) => {
                        let restoredImage = false;
                        entries.forEach((entry) => {
                            if (entry.isIntersecting) {
                                if (
                                    document.documentElement.classList.contains('ehtw-reader-page-pending') &&
                                    pageInfo.get(entry.target)?.pageNumber !== targetedSeekImagePageNumber
                                ) {
                                    return;
                                }
                                restoredImage ||=
                                    entry.target.dataset.ehtwMemoryUnloaded === 'true';
                                restoreImageResource(entry.target);
                            }
                        });
                        if (restoredImage) {
                            startDeferredImagesAround(
                                clickPaging ? activeImageIndex : findImageAtViewportCenter()
                            );
                        }
                    },
                    { rootMargin: '200% 0px' }
                );
            }

            if (typeof window.IntersectionObserver === 'function') {
                pageObserver = new window.IntersectionObserver(
                    (entries) => {
                        if (
                            clickPaging ||
                            readerInitializing ||
                            isNativeReaderZoomed() ||
                            performance.now() < suppressReaderScrollUntil
                        ) {
                            return;
                        }

                        const viewportCenter = readerLayoutHeight() / 2;
                        let closestInfo = null;
                        let closestDistance = Infinity;

                        entries.forEach((entry) => {
                            if (!entry.isIntersecting) return;

                            const bounds = entry.target.getBoundingClientRect();
                            if (
                                bounds.bottom < 120 ||
                                bounds.top > readerLayoutHeight() - 120
                            ) {
                                return;
                            }

                            const imageCenter = (bounds.top + bounds.bottom) / 2;
                            const distance = Math.abs(imageCenter - viewportCenter);

                            if (Number.isFinite(distance) && distance < closestDistance) {
                                closestInfo = pageInfo.get(entry.target);
                                closestDistance = distance;
                            }
                        });

                        if (closestInfo?.pageNumber) {
                            updatePageCounter(closestInfo.pageNumber);
                            updateReaderImagePriorities(closestInfo.pageNumber);
                        }
                    },
                    { rootMargin: '-120px 0px -120px 0px' }
                );
            }

            const sizeReaderFrame = (frame, image, info, fitViewport = clickPaging) => {
                if (!frame) return;

                const sourceUrl =
                    image.currentSrc ||
                    image.getAttribute('src') ||
                    image.dataset.ehtwMemorySource ||
                    image.getAttribute('data-ehtw-seek-deferred-source') ||
                    '';
                const urlDimensions = sourceUrl.match(
                    /-(\d{2,5})-(\d{2,5})-wbp(?:\/|$)/i
                );
                const sourceWidth =
                    image.naturalWidth ||
                    Number.parseInt(urlDimensions?.[1] || '', 10) ||
                    info?.width ||
                    0;
                const sourceHeight =
                    image.naturalHeight ||
                    Number.parseInt(urlDimensions?.[2] || '', 10) ||
                    info?.height ||
                    0;

                if (sourceWidth > 0 && sourceHeight > 0) {
                    const viewportWidth = readerLayoutWidth() || sourceWidth;
                    const viewportHeight = readerLayoutHeight() || sourceHeight;
                    let availableWidth = viewportWidth;
                    let maxHeight = Infinity;
                    let fitOffsetY = 0;

                    if (fitViewport) {
                        // Reader controls overlay the page, so fit against the full viewport.
                        availableWidth = Math.max(1, viewportWidth - 4);
                        maxHeight = viewportHeight;
                    }

                    const fitScale = Math.min(
                        1,
                        availableWidth / sourceWidth,
                        maxHeight / sourceHeight
                    );
                    const scale = fitScale * (zoomPercent / 100);

                    frame.style.width = `${Math.max(1, Math.round(sourceWidth * scale))}px`;
                    frame.style.height = `${Math.max(1, Math.round(sourceHeight * scale))}px`;
                    frame.style.transform = fitViewport
                        ? `translateY(${Math.round(fitOffsetY)}px)`
                        : '';
                } else {
                    frame.style.width = 'min(100vw, 100%)';
                    frame.style.height = 'min(50vh, 360px)';
                }
            };

            const captureReaderScrollAnchor = () => {
                if (clickPaging || isNativeReaderZoomed() || typeof window.innerHeight !== 'number') return null;
                const viewportCenter = window.innerHeight / 2;
                const candidates = readerPageImages
                    .map((image) => pageFrames.get(image))
                    .filter((frame) => frame?.isConnected)
                    .map((frame) => {
                        const bounds = frame.getBoundingClientRect();
                        const center = (bounds.top + bounds.bottom) / 2;
                        return {
                            frame,
                            center,
                            distance: Math.abs(center - viewportCenter),
                            visible: bounds.bottom > 0 && bounds.top < readerLayoutHeight()
                        };
                    })
                    .filter(({ visible }) => visible)
                    .sort((a, b) => a.distance - b.distance);
                const anchor = candidates[0];
                return anchor
                    ? { frame: anchor.frame, center: anchor.center }
                    : null;
            };

            const restoreReaderScrollAnchor = (anchor) => {
                if (!anchor?.frame?.isConnected) return;
                const bounds = anchor.frame.getBoundingClientRect();
                const anchorAfterCenter = (bounds.top + bounds.bottom) / 2;
                const scrollDelta = anchorAfterCenter - anchor.center;
                const scrollRoot = document.scrollingElement || document.documentElement;
                if (
                    !isNativeReaderZoomed() &&
                    Number.isFinite(scrollDelta) &&
                    Math.abs(scrollDelta) > 1 &&
                    scrollRoot
                ) {
                    recordReaderScrollRewrite();
                    scrollRoot.scrollTop += scrollDelta;
                    suppressReaderScrollUntil = performance.now() + 250;
                }
            };

            const bindImage = (image, info) => {
                const frame = document.createElement('div');
                frame.className = 'ehtw-reader-image-frame';
                const originalAlt = image.getAttribute('alt') || '';

                image.classList.add('ehtw-reader-page-image');
                image.setAttribute('draggable', 'false');
                image.draggable = false;
                if (info?.pageUrl) image.dataset.pageUrl = info.pageUrl;
                pageInfo.set(image, info);
                if (
                    image === readerImage ||
                    (
                        Number.isFinite(info?.pageNumber) &&
                        info.pageNumber === currentPageNumber
                    )
                ) {
                    image.fetchPriority = 'high';
                }

                const sourceAlreadySet =
                    !!image.getAttribute('src') ||
                    !!image.getAttribute('data-src');
                let imageLoadWatchdogTimer = null;
                const clearImageLoadWatchdog = () => {
                    if (imageLoadWatchdogTimer) {
                        window.clearTimeout(imageLoadWatchdogTimer);
                        imageLoadWatchdogTimer = null;
                    }
                };
                const armImageLoadWatchdog = () => {
                    clearImageLoadWatchdog();
                    if (!image.getAttribute('src') || image.complete) return;
                    imageLoadWatchdogTimer = window.setTimeout(() => {
                        imageLoadWatchdogTimer = null;
                        if (image.complete || image.getAttribute('data-ehtw-load-failed') === 'true') {
                            return;
                        }
                        onImageError();
                    }, READER_IMAGE_LOAD_TIMEOUT_MS);
                };
                imageLoadWatchdogCallbacks.set(image, armImageLoadWatchdog);

                if (
                    sourceAlreadySet &&
                    image.complete &&
                    image.naturalWidth === 0
                ) {
                    image.setAttribute('data-ehtw-load-failed', 'true');
                    image.alt = '';
                    frame.classList.add('ehtw-reader-image-failed');
                }

                if (
                    image.getAttribute('data-ehtw-load-failed') !== 'true' &&
                    (!image.complete || image.naturalWidth === 0)
                ) {
                    frame.classList.add('ehtw-reader-image-loading');
                }

                sizeReaderFrame(frame, image, info, clickPaging);
                pageFrames.set(image, frame);
                frame.appendChild(image);

                const spinner = document.createElement('div');
                spinner.className = 'ehtw-reader-loading-spinner';
                spinner.setAttribute('aria-hidden', 'true');
                frame.appendChild(spinner);

                const retryButton = document.createElement('button');
                retryButton.type = 'button';
                retryButton.className = 'ehtw-reader-retry-button';
                retryButton.setAttribute('aria-label', '重新加载图片');
                const retryIcon = document.createElementNS(
                    'http://www.w3.org/2000/svg',
                    'svg'
                );
                retryIcon.setAttribute('viewBox', '0 0 24 24');
                retryIcon.setAttribute('aria-hidden', 'true');
                const retryPath = document.createElementNS(
                    'http://www.w3.org/2000/svg',
                    'path'
                );
                retryPath.setAttribute(
                    'd',
                    'M20 7v5h-5M4 17v-5h5M5.64 8.64A7 7 0 0 1 18 6l2 2M4 16l2 2a7 7 0 0 0 12.36-2.64'
                );
                retryIcon.appendChild(retryPath);
                retryButton.appendChild(retryIcon);
                frame.appendChild(retryButton);
                imageMemoryObserver?.observe(image);

                let retryingImage = false;
                let autoRetryAttempts = 0;
                let autoRetryTimer = null;
                const cancelAutoRetry = () => {
                    if (autoRetryTimer === null) return;
                    window.clearTimeout(autoRetryTimer);
                    autoRetryTimer = null;
                };
                const retryImage = async () => {
                    if (retryingImage) return;

                    let retrySource =
                        image.currentSrc ||
                        image.getAttribute('src') ||
                        image.getAttribute('data-src') ||
                        image.dataset.ehtwSeekDeferredSource ||
                        image.dataset.ehtwMemorySource;

                    if (!retrySource) return;

                    const retryPageNumber = pageInfo.get(image)?.pageNumber;
                    const retryIsPreload = Number.isFinite(retryPageNumber) &&
                        retryPageNumber !== currentPageNumber;
                    if (
                        retryIsPreload &&
                        !activeReaderImageQueueLoads.has(image) &&
                        activeReaderImageQueueLoads.size >= READER_IMAGE_PREFETCH_CONCURRENCY
                    ) {
                        autoRetryTimer = window.setTimeout(() => {
                            autoRetryTimer = null;
                            return retryImage();
                        }, 100);
                        return;
                    }
                    if (retryIsPreload) activeReaderImageQueueLoads.add(image);

                    retryingImage = true;
                    const sourceSet = image.getAttribute('srcset');
                    frame.classList.remove('ehtw-reader-image-failed');
                    frame.classList.add('ehtw-reader-image-loading');
                    image.removeAttribute('data-ehtw-load-failed');
                    image.alt = originalAlt;
                    updateBottomReaderStatus();

                    if (
                        info?.pageUrl &&
                        typeof window.fetch === 'function' &&
                        typeof window.DOMParser === 'function'
                    ) {
                        try {
                            const response = await window.fetch(info.pageUrl, {
                                credentials: 'same-origin',
                                cache: 'no-store'
                            });

                            if (response?.ok) {
                                const pageText = await response.text();
                                const parsedPage = new window.DOMParser()
                                    .parseFromString(pageText, 'text/html');
                                const refreshedImage =
                                    parsedPage.querySelector('#img') ||
                                    parsedPage.querySelector('#i3 img');
                                const refreshedSource = resolveImageUrl(
                                    getImageSource(refreshedImage),
                                    info.pageUrl
                                );

                                if (refreshedSource) {
                                    retrySource = refreshedSource;
                                    info.imageUrl = refreshedSource;
                                }
                            }
                        } catch (_) {
                            // Fall back to retrying the last known image URL.
                        }
                    }

                    image.removeAttribute('srcset');
                    image.removeAttribute('src');
                    if (sourceSet) image.setAttribute('srcset', sourceSet);
                    startPageImageSource(image, retrySource);
                    retryingImage = false;
                };
                const scheduleAutomaticRetry = () => {
                    if (
                        retryingImage ||
                        autoRetryAttempts >= READER_IMAGE_AUTO_RETRY_LIMIT
                    ) {
                        return false;
                    }

                    cancelAutoRetry();
                    const delay = READER_IMAGE_AUTO_RETRY_DELAYS_MS[
                        autoRetryAttempts
                    ] || READER_IMAGE_AUTO_RETRY_DELAYS_MS[
                        READER_IMAGE_AUTO_RETRY_DELAYS_MS.length - 1
                    ];
                    autoRetryAttempts++;
                    frame.classList.remove('ehtw-reader-image-failed');
                    frame.classList.add('ehtw-reader-image-loading');
                    image.removeAttribute('data-ehtw-load-failed');
                    image.alt = originalAlt;
                    updateBottomReaderStatus();
                    autoRetryTimer = window.setTimeout(() => {
                        autoRetryTimer = null;
                        return retryImage();
                    }, delay);
                    return true;
                };
                const retryManually = () => {
                    cancelAutoRetry();
                    autoRetryAttempts = 0;
                    return retryImage();
                };
                pageObserver?.observe(image);

                const onImageError = () => {
                    clearImageLoadWatchdog();
                    if (image.dataset.ehtwPreloadPaused === 'true') {
                        activeReaderImageQueueLoads.delete(image);
                        pumpReaderImagePreloadQueue();
                        return;
                    }
                    if (retryingImage) return;
                    if (autoRetryTimer !== null) return;
                    if (scheduleAutomaticRetry()) return;
                    activeReaderImageQueueLoads.delete(image);
                    frame.classList.remove('ehtw-reader-image-loading');
                    frame.classList.add('ehtw-reader-image-failed');
                    image.setAttribute('data-ehtw-load-failed', 'true');
                    image.alt = '';
                    updateBottomReaderStatus();
                    pumpReaderImagePreloadQueue();
                };

                const onImageLoad = () => {
                    const scrollAnchor = captureReaderScrollAnchor();
                    retryingImage = false;
                    cancelAutoRetry();
                    autoRetryAttempts = 0;
                    clearImageLoadWatchdog();
                    delete image.dataset.ehtwPreloadPaused;
                    activeReaderImageQueueLoads.delete(image);
                    frame.classList.remove('ehtw-reader-image-loading');
                    frame.classList.remove('ehtw-reader-image-failed');
                    image.removeAttribute('data-ehtw-load-failed');
                    image.alt = originalAlt;
                    sizeReaderFrame(frame, image, info, clickPaging);
                    restoreReaderScrollAnchor(scrollAnchor);
                    releaseReaderImagesOutsideWindow(
                        readerImageQueueCenterIndex
                    );
                    updateBottomReaderStatus();
                    pumpReaderImagePreloadQueue();
                };

                const onRetryClick = (event) => {
                    event.preventDefault();
                    event.stopPropagation();
                    return retryManually();
                };

                const onImageDragStart = (event) => {
                    event.preventDefault();
                    event.stopPropagation();
                };

                const onImageClick = async (event) => {
                    event.preventDefault();
                    event.stopPropagation();

                    const page = pageInfo.get(image);
                    const imageIndex = readerPageImages.indexOf(image);

                    const bounds = image.getBoundingClientRect();
                    // 1.17.255: when natively zoomed only part of the image is on
                    // screen, so measure the tap against the visible part.
                    let visibleLeft = bounds.left;
                    let visibleWidth = bounds.width;
                    const zoomViewport = window.visualViewport;
                    if (isNativeReaderZoomed() && zoomViewport) {
                        const viewLeft = Number(zoomViewport.offsetLeft) || 0;
                        const viewRight = viewLeft + (Number(zoomViewport.width) || 0);
                        const left = Math.max(bounds.left, viewLeft);
                        const right = Math.min(bounds.right, viewRight);
                        if (right - left > 0) {
                            visibleLeft = left;
                            visibleWidth = right - left;
                        }
                    }
                    const position = visibleWidth > 0
                        ? (event.clientX - visibleLeft) / visibleWidth
                        : 0.5;

                    // 图片中央区域用来隐藏或重新显示阅读设置。
                    if (position >= 0.4 && position <= 0.6) {
                        // Zoomed taps go to the zoom toggle; it clears auto-hide too.
                        const scrollHiddenShown = showReaderScrollHiddenChrome();
                        if (scrollHiddenShown && !isNativeReaderZoomed()) {
                            if (imageIndex >= 0) activeImageIndex = imageIndex;
                            return;
                        }
                        if (toggleReaderChromeWhileZoomed()) {
                            if (imageIndex >= 0) activeImageIndex = imageIndex;
                            return;
                        }
                        if (imageIndex >= 0) activeImageIndex = imageIndex;
                        readerRoot.classList.toggle(
                            'ehtw-reader-settings-hidden'
                        );
                        updateReaderPageToolsViewport();
                        sizeReaderFrame(
                            pageFrames.get(image),
                            image,
                            pageInfo.get(image),
                            clickPaging
                        );
                        return;
                    }

                    if (!clickPaging || !page) return;

                    if (position < 0.4) {
                        if (imageIndex > 0) {
                            showActiveImage(imageIndex - 1, true);
                        } else if (
                            previousPageUrl ||
                            (
                                page.previousUrl &&
                                page.previousUrl !== page.pageUrl
                            )
                        ) {
                            if (previousPageUrl) {
                                await loadPreviousBatch();

                                if (activeImageIndex > 0) {
                                    showActiveImage(activeImageIndex - 1, true);
                                } else if (
                                    page.previousUrl &&
                                    page.previousUrl !== page.pageUrl
                                ) {
                                    navigatePage(page.previousUrl);
                                }
                            } else {
                                navigatePage(page.previousUrl);
                            }
                        }
                        return;
                    }

                    if (imageIndex + 1 < readerPageImages.length) {
                        showActiveImage(imageIndex + 1, true);
                        return;
                    }

                    if (
                        page.nextUrl &&
                        page.nextUrl !== page.pageUrl &&
                        nextPageUrl
                    ) {
                        await loadNextBatch();

                        if (imageIndex + 1 < readerPageImages.length) {
                            showActiveImage(imageIndex + 1, true);
                        } else if (page.nextUrl !== page.pageUrl) {
                            navigatePage(page.nextUrl);
                        }
                    }
                };

                image.addEventListener('load', onImageLoad);
                image.addEventListener('error', onImageError);
                image.addEventListener('dragstart', onImageDragStart);
                image.addEventListener('click', onImageClick);
                retryButton.addEventListener('click', onRetryClick);
                if (frame.classList.contains('ehtw-reader-image-loading')) {
                    armImageLoadWatchdog();
                }
                if (image.getAttribute('data-ehtw-load-failed') === 'true') {
                    scheduleAutomaticRetry();
                }
                imageListeners.push({
                    image,
                    frame,
                    spinner,
                    retryButton,
                    onImageLoad,
                    onImageError,
                    onImageDragStart,
                    onImageClick,
                    onRetryClick,
                    clearImageLoadWatchdog,
                    retryManually,
                    resetAutoRetryAttempts: () => { autoRetryAttempts = 0; },
                    cancelAutoRetry
                });

                return frame;
            };

            const currentFrame = bindImage(readerImage, {
                pageUrl: currentPageUrl,
                previousUrl: currentPreviousUrl,
                nextUrl: nextPageUrl,
                pageNumber: initialPagePosition.page,
                totalPages: initialPagePosition.total
            });
            readerImages.appendChild(topSentinel);
            readerImages.appendChild(currentFrame);
            readerImages.appendChild(bottomSentinel);

            const updateMode = (enabled) => {
                if (enabled) {
                    activeImageIndex = findImageAtViewportCenter();
                }
                const imageToKeepOnScroll = !enabled
                    ? readerPageImages[activeImageIndex]
                    : null;

                clickPaging = enabled;
                readerRoot?.classList.remove('ehtw-reader-chrome-scroll-hidden');
                setClickModeTouchGuard(enabled);
                try {
                    window.localStorage?.setItem(
                        readerModeStorageKey,
                        enabled ? 'click' : 'scroll'
                    );
                } catch (_) {
                    // The current reader still changes mode when storage is unavailable.
                }
                document.documentElement.classList.toggle(
                    'ehtw-reader-click',
                    enabled
                );
                document.documentElement.classList.toggle(
                    'ehtw-reader-zoomed',
                    zoomPercent > 100
                );

                if (enabled) {
                    showActiveImage(activeImageIndex);
                } else {
                    readerPageImages.forEach((image) => {
                        image.classList.remove('ehtw-reader-active');
                        sizeReaderFrame(
                            pageFrames.get(image),
                            image,
                            pageInfo.get(image),
                            false
                        );
                    });
                    const activeInfo = imageToKeepOnScroll
                        ? pageInfo.get(imageToKeepOnScroll)
                        : null;
                    if (activeInfo?.pageNumber) {
                        currentPageNumber = activeInfo.pageNumber;
                        updatePageCounter(currentPageNumber);
                    }
                    startDeferredImagesAround(activeImageIndex);
                    scrollImageToViewportCenter(imageToKeepOnScroll);
                    lastReaderScrollY = window.scrollY;
                    updateBottomReaderStatus();
                }

                modes.forEach(({ button, mode }) => {
                    button.setAttribute(
                        'aria-pressed',
                        String((mode === 'click') === enabled)
                    );
                });
                updateReaderPageToolsViewport();
            };

            [
                ['scroll', '上下滚动'],
                ['click', '点击翻页']
            ].forEach(([mode, label]) => {
                const button = document.createElement('button');
                button.type = 'button';
                button.className = 'ehtw-reader-mode';
                button.setAttribute('data-mode', mode);
                button.textContent = label;
                button.addEventListener('click', (event) => {
                    event.preventDefault();
                    event.stopPropagation();
                    updateMode(mode === 'click');
                });
                modes.push({ button, mode });
                readerControls.appendChild(button);
            });

            const preloadDetails = document.createElement('details');
            preloadDetails.className = 'ehtw-reader-preload-details';
            const preloadSummary = document.createElement('summary');
            preloadSummary.innerHTML = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 7h9M17 7h3M4 17h3M11 17h9"/><circle cx="15" cy="7" r="2"/><circle cx="9" cy="17" r="2"/></svg>';
            preloadSummary.setAttribute('aria-label', '预载设置');
            preloadSummary.setAttribute('title', '预载设置');
            preloadDetails.appendChild(preloadSummary);

            const preloadPopover = document.createElement('div');
            preloadPopover.className = 'ehtw-reader-preload-popover';
            const imagePreloadRow = document.createElement('label');
            imagePreloadRow.className = 'ehtw-reader-preload-row';
            const preloadLabel = document.createElement('span');
            preloadLabel.textContent = '图片上下各预载';
            imagePreloadRow.appendChild(preloadLabel);

            const preloadInput = document.createElement('input');
            preloadInput.className = 'ehtw-reader-preload-count';
            preloadInput.type = 'number';
            preloadInput.min = '0';
            preloadInput.max = '50';
            preloadInput.step = '1';
            preloadInput.value = String(preloadCount);
            preloadInput.setAttribute('aria-label', '图片上下各预载数量');
            preloadInput.addEventListener('click', (event) => {
                event.stopPropagation();
            });
            preloadInput.addEventListener('change', () => {
                const requestedCount = Number.parseInt(
                    preloadInput.value,
                    10
                );

                if (!Number.isFinite(requestedCount)) {
                    preloadInput.value = String(preloadCount);
                    return;
                }

                preloadCount = Math.max(0, Math.min(50, requestedCount));
                preloadInput.value = String(preloadCount);
                try {
                    window.localStorage?.setItem(
                        preloadStorageKey,
                        String(preloadCount)
                    );
                } catch (_) {
                    // The current reader still uses the value when storage is unavailable.
                }
                startDeferredImagesAround(clickPaging ? activeImageIndex : findImageAtViewportCenter());
            });

            imagePreloadRow.appendChild(preloadInput);

            const preloadUnit = document.createElement('span');
            preloadUnit.textContent = '张(上/下)';
            imagePreloadRow.appendChild(preloadUnit);
            preloadPopover.appendChild(imagePreloadRow);

            const pageLinkPreloadRow = document.createElement('label');
            pageLinkPreloadRow.className = 'ehtw-reader-preload-row';
            const pageLinkPreloadLabel = document.createElement('span');
            pageLinkPreloadLabel.textContent = '页码链接上下各预载';
            pageLinkPreloadRow.appendChild(pageLinkPreloadLabel);
            const pageLinkPreloadInput = document.createElement('input');
            pageLinkPreloadInput.className = 'ehtw-reader-page-link-preload-count';
            pageLinkPreloadInput.type = 'number';
            pageLinkPreloadInput.min = '0';
            pageLinkPreloadInput.max = '50';
            pageLinkPreloadInput.step = '1';
            pageLinkPreloadInput.value = String(pageLinkPreloadCount);
            pageLinkPreloadInput.setAttribute('aria-label', '页码链接上下各预载数量');
            pageLinkPreloadInput.addEventListener('click', (event) => event.stopPropagation());
            pageLinkPreloadInput.addEventListener('change', () => {
                const requestedCount = Number.parseInt(pageLinkPreloadInput.value, 10);
                if (!Number.isFinite(requestedCount)) {
                    pageLinkPreloadInput.value = String(pageLinkPreloadCount);
                    return;
                }
                pageLinkPreloadCount = Math.max(0, Math.min(50, requestedCount));
                pageLinkPreloadInput.value = String(pageLinkPreloadCount);
                try {
                    window.localStorage?.setItem(pageLinkPreloadStorageKey, String(pageLinkPreloadCount));
                } catch (_) {
                    // Current session still uses the changed value.
                }
                loadNextBatch();
                loadPreviousBatch();
            });
            pageLinkPreloadRow.appendChild(pageLinkPreloadInput);
            const pageLinkPreloadUnit = document.createElement('span');
            pageLinkPreloadUnit.textContent = '页(上/下)';
            pageLinkPreloadRow.appendChild(pageLinkPreloadUnit);
            preloadPopover.appendChild(pageLinkPreloadRow);
            preloadDetails.appendChild(preloadPopover);
            readerControls.appendChild(preloadDetails);

            readerPageTools = document.createElement('div');
            readerPageTools.className = 'ehtw-reader-page-tools';
            readerPageTools.setAttribute('aria-label', '翻页和缩放');

            const zoomControls = document.createElement('div');
            zoomControls.className = 'ehtw-reader-zoom-controls';

            const createZoomButton = (text, label, action, className = 'ehtw-reader-zoom-button') => {
                const button = document.createElement('button');
                button.type = 'button';
                button.className = className;
                button.textContent = text;
                button.setAttribute('aria-label', label);
                button.addEventListener('click', (event) => {
                    event.preventDefault();
                    event.stopPropagation();
                    action();
                });
                return button;
            };

            const applyZoomPercent = (value) => {
                const parsed = Number.parseInt(value, 10);
                if (!Number.isFinite(parsed)) {
                    if (zoomInput) zoomInput.value = String(zoomPercent);
                    return;
                }

                zoomPercent = Math.max(25, Math.min(300, parsed));
                if (zoomInput) zoomInput.value = String(zoomPercent);
                try {
                    window.localStorage?.setItem(
                        readerZoomStorageKey,
                        String(zoomPercent)
                    );
                } catch (_) {
                    // The current reader still uses the value when storage is unavailable.
                }
                document.documentElement.classList.toggle(
                    'ehtw-reader-zoomed',
                    zoomPercent > 100
                );

                if (clickPaging) {
                    const activeImage = readerPageImages[activeImageIndex];
                    sizeReaderFrame(
                        pageFrames.get(activeImage),
                        activeImage,
                        pageInfo.get(activeImage),
                        true
                    );
                    if (!isNativeReaderZoomed()) activeImage?.scrollIntoView?.({ block: 'center' });
                } else {
                    readerPageImages.forEach((image) => {
                        sizeReaderFrame(
                            pageFrames.get(image),
                            image,
                            pageInfo.get(image),
                            false
                        );
                    });
                }
                updateReaderPageToolsViewport();
            };

            zoomControls.appendChild(createZoomButton(
                '−',
                '缩小图片',
                () => applyZoomPercent(zoomPercent - 10)
            ));

            zoomInput = document.createElement('input');
            zoomInput.className = 'ehtw-reader-zoom-input';
            zoomInput.type = 'number';
            zoomInput.min = '25';
            zoomInput.max = '300';
            zoomInput.step = '5';
            zoomInput.value = String(zoomPercent);
            zoomInput.setAttribute('aria-label', '缩放百分比');
            zoomInput.addEventListener('click', (event) => event.stopPropagation());
            zoomInput.addEventListener('change', () => applyZoomPercent(zoomInput.value));
            zoomControls.appendChild(zoomInput);

            const zoomUnit = document.createElement('span');
            zoomUnit.className = 'ehtw-reader-zoom-unit';
            zoomUnit.textContent = '%';
            zoomControls.appendChild(zoomUnit);
            zoomControls.appendChild(createZoomButton(
                '+',
                '放大图片',
                () => applyZoomPercent(zoomPercent + 10)
            ));
            zoomControls.appendChild(createZoomButton(
                '适屏',
                '适应页面',
                () => applyZoomPercent(100),
                'ehtw-reader-fit-button'
            ));

            readerPageTools.appendChild(zoomControls);

            const pageSeek = document.createElement('div');
            pageSeek.className = 'ehtw-reader-page-seek';
            pageSeekCurrent = document.createElement('span');
            pageSeekCurrent.className = 'ehtw-reader-seek-current';
            pageSeekCurrent.textContent = String(currentPageNumber ?? '?');
            pageSeek.appendChild(pageSeekCurrent);

            pageSlider = document.createElement('input');
            pageSlider.className = 'ehtw-reader-page-slider';
            pageSlider.type = 'range';
            pageSlider.min = '1';
            pageSlider.max = String(Math.max(1, totalPages || currentPageNumber || 1));
            pageSlider.step = '1';
            pageSlider.value = String(Math.max(1, currentPageNumber || 1));
            pageSlider.setAttribute('aria-label', '跳转到页码');
            pageSlider.addEventListener('click', (event) => event.stopPropagation());
            let lastSliderCommitValue = null;
            const previewPageSliderValue = () => {
                const requestedPage = Number.parseInt(pageSlider.value, 10);
                if (!Number.isFinite(requestedPage)) return;

                pageSeekCurrent.textContent = String(requestedPage);
                const loadedIndex = readerPageImages.findIndex((image) =>
                    pageInfo.get(image)?.pageNumber === requestedPage
                );

                if (loadedIndex >= 0) {
                    if (pageSeekPromise) {
                        pageSeekTargetNumber = requestedPage;
                        targetedSeekImagePageNumber = requestedPage;
                    } else {
                        targetedSeekImagePageNumber = null;
                    }
                    if (clickPaging) {
                        showActiveImage(loadedIndex, true);
                    } else {
                        const image = readerPageImages[loadedIndex];
                        startDeferredImagesAround(loadedIndex);
                        startDeferredImageResource(image);
                        restoreImageResource(image);
                        scrollImageToViewportCenter(image, true);
                        updatePageCounter(requestedPage);
                            updateBottomReaderStatus();
                    }
                    return;
                }

                navigateToPageNumber(requestedPage);
            };
            const commitPageSliderValue = () => {
                const requestedPage = Number.parseInt(pageSlider.value, 10);
                if (!Number.isFinite(requestedPage) || requestedPage === currentPageNumber) {
                    pageSlider.value = String(Math.max(1, currentPageNumber || 1));
                    pageSeekCurrent.textContent = String(currentPageNumber ?? '?');
                    return;
                }
                if (lastSliderCommitValue === requestedPage && pageSeekPromise) return;
                lastSliderCommitValue = requestedPage;
                navigateToPageNumber(requestedPage);
            };
            pageSlider.addEventListener('pointerdown', (event) => {
                pageSliderDragActive = true;
                event.stopPropagation();
            });
            pageSlider.addEventListener('touchstart', () => {
                pageSliderDragActive = true;
            }, { passive: true });
            pageSlider.addEventListener('input', () => {
                previewPageSliderValue();
            });
            pageSlider.addEventListener('change', () => {
                if (!pageSliderDragActive) commitPageSliderValue();
            });
            pageSliderReleaseHandler = () => {
                if (!pageSliderDragActive) return;
                pageSliderDragActive = false;
                commitPageSliderValue();
            };
            pageSliderCancelHandler = () => {
                if (!pageSliderDragActive) return;
                pageSliderDragActive = false;
                pageSlider.value = String(Math.max(1, currentPageNumber || 1));
                pageSeekCurrent.textContent = String(currentPageNumber ?? '?');
            };
            addSpaPageEventListener(window, 'pointerup', pageSliderReleaseHandler);
            addSpaPageEventListener(window, 'touchend', pageSliderReleaseHandler, { passive: true });
            addSpaPageEventListener(window, 'pointercancel', pageSliderCancelHandler);
            addSpaPageEventListener(window, 'touchcancel', pageSliderCancelHandler, { passive: true });
            pageSeek.appendChild(pageSlider);

            pageSeekTotal = document.createElement('span');
            pageSeekTotal.className = 'ehtw-reader-seek-total';
            pageSeekTotal.textContent = String(totalPages ?? '?');
            pageSeek.appendChild(pageSeekTotal);
            readerPageTools.appendChild(pageSeek);

            pageSeekStatus = document.createElement('span');
            pageSeekStatus.className = 'ehtw-reader-seek-status';
            pageSeekStatus.setAttribute('aria-live', 'polite');
            readerPageTools.appendChild(pageSeekStatus);
            updateBottomReaderStatus = () => {
                if (
                    !pageSeekStatus ||
                    readerPageTools.classList.contains('ehtw-reader-seeking')
                ) {
                    return;
                }

                const loadingPages = [];
                const failedPages = [];
                readerPageImages.forEach((image) => {
                    const info = pageInfo.get(image);
                    const frame = pageFrames.get(image);
                    if (!info || image.dataset.ehtwMemoryUnloaded === 'true') return;
                    if (
                        image.dataset.ehtwDeferredSource ||
                        image.dataset.ehtwSeekDeferredSource
                    ) {
                        return;
                    }
                    if (frame?.classList.contains('ehtw-reader-image-loading')) {
                        loadingPages.push(info.pageNumber);
                    } else if (image.getAttribute('data-ehtw-load-failed') === 'true') {
                        failedPages.push(info.pageNumber);
                    }
                });

                const formatPages = (pages) => pages
                    .filter(Number.isFinite)
                    .slice(0, 4)
                    .join('、');
                if (loadingPages.length) {
                    const visiblePages = formatPages(loadingPages);
                    const extraCount = Math.max(0, loadingPages.length - 4);
                    pageSeekStatus.textContent = visiblePages
                        ? `正在加载图片:第 ${visiblePages} 页${extraCount ? ` 等 ${extraCount} 页` : ''}`
                        : `正在加载图片(${loadingPages.length} 张)`;
                } else if (failedPages.length) {
                    const visiblePages = formatPages(failedPages);
                    pageSeekStatus.textContent = visiblePages
                        ? `第 ${visiblePages} 页图片加载失败,点击重试图标重试`
                        : `${failedPages.length} 张图片加载失败,点击重试图标重试`;
                } else {
                    pageSeekStatus.textContent = '';
                }
            };
            updateBottomReaderStatus();

            readerPageTransition = document.createElement('div');
            readerPageTransition.className = 'ehtw-reader-page-transition';
            readerPageTransition.setAttribute('role', 'status');
            readerPageTransition.setAttribute('aria-live', 'polite');
            const transitionSpinner = document.createElement('div');
            transitionSpinner.className = 'ehtw-reader-loading-spinner';
            transitionSpinner.setAttribute('aria-hidden', 'true');
            readerPageTransition.appendChild(transitionSpinner);
            readerTransitionMessage = document.createElement('span');
            readerPageTransition.appendChild(readerTransitionMessage);

            readerRoot.appendChild(readerControls);
            readerRoot.appendChild(readerImages);
            readerRoot.appendChild(readerPageTools);
            readerRoot.appendChild(readerPageTransition);
            document.body.appendChild(readerRoot);
            const controlsBaselineRect = readerControls.getBoundingClientRect();
            const toolsBaselineRect = readerPageTools.getBoundingClientRect();
            const readerStylePixel = (element, property, fallback) => {
                const value = Number.parseFloat(window.getComputedStyle?.(element)?.[property]);
                return Number.isFinite(value) ? value : fallback;
            };
            const controlsTopBaseline = readerStylePixel(readerControls, 'top', controlsBaselineRect.top);
            const baselineLayoutHeight = document.documentElement.clientHeight || readerLayoutHeight();
            const toolsBottomBaseline = readerStylePixel(readerPageTools, 'bottom', baselineLayoutHeight - toolsBaselineRect.bottom);
            let readerZoomBaselineCaptured = false;
            let readerViewportBaselineMargins = {
                controlsTop: Number.isFinite(controlsTopBaseline) ? controlsTopBaseline : controlsBaselineRect.top,
                controlsTopTarget: Number.isFinite(controlsTopBaseline) ? controlsTopBaseline : controlsBaselineRect.top,
                toolsBottom: Number.isFinite(toolsBottomBaseline) ? toolsBottomBaseline : 0,
                toolsBottomTarget: Number.isFinite(toolsBottomBaseline) ? toolsBottomBaseline : 0
            };
            // 1.17.261: the scroll auto-hide slides the toolbars with the CSS
            // `translate` property (also mid-transition). Measure positions
            // without it, or the viewport correction cancels the slide.
            const readerRectWithoutTranslate = (element) => {
                const rect = element.getBoundingClientRect();
                const translate = String(window.getComputedStyle?.(element)?.translate || 'none');
                if (translate === 'none') return rect;
                const [tx = 0, ty = 0] = translate.split(/\s+/).map((part) => Number.parseFloat(part) || 0);
                if (!tx && !ty) return rect;
                return {
                    left: rect.left - tx,
                    right: rect.right - tx,
                    top: rect.top - ty,
                    bottom: rect.bottom - ty,
                    width: rect.width,
                    height: rect.height
                };
            };
            updateReaderPageToolsViewport = () => {
                const viewport = window.visualViewport;
                if (!viewport || !readerPageTools?.isConnected) return;
                if (readerViewportUpdateRaf) return;
                const requestFrame = window.requestAnimationFrame || ((callback) => window.setTimeout(callback, 16));
                readerViewportUpdateRaf = requestFrame(() => {
                    readerViewportUpdateRaf = 0;
                    if (!readerPageTools?.isConnected) return;
                    const scale = Number.isFinite(viewport.scale) && viewport.scale > 0
                        ? viewport.scale
                        : 1;
                    const offsetX = Number(viewport.offsetLeft) || 0;
                    const offsetY = Number(viewport.offsetTop) || 0;
                    // Measure the fixed elements in their untransformed layout
                    // positions. Their offset metrics can be rounded or relative
                    // to a transformed containing block on mobile browsers.
                    readerControls.style.removeProperty('transform');
                    readerPageTools.style.removeProperty('transform');
                    const controlsLayoutRect = readerRectWithoutTranslate(readerControls);
                    const toolsLayoutRect = readerRectWithoutTranslate(readerPageTools);
                    const inverseScale = 1 / scale;
                    const visualCenterX = offsetX + viewport.width / 2;
                    const controlShiftX = visualCenterX - (controlsLayoutRect.left + controlsLayoutRect.width / 2);
                    const toolsShiftX = visualCenterX - (toolsLayoutRect.left + toolsLayoutRect.width / 2);
                    const controlShiftY = offsetY + readerViewportBaselineMargins.controlsTop * inverseScale - controlsLayoutRect.top;
                    const toolsShiftY = offsetY + viewport.height - readerViewportBaselineMargins.toolsBottom * inverseScale - toolsLayoutRect.bottom;
                    const needsVisualViewportCorrection = scale > 1.01 ||
                        Math.abs(controlShiftX) > 1 || Math.abs(controlShiftY) > 1 ||
                        Math.abs(toolsShiftX) > 1 || Math.abs(toolsShiftY) > 1;
                    if (!needsVisualViewportCorrection) return;
                    const controlsTransform = `translate3d(${controlShiftX}px, ${controlShiftY}px, 0) translateX(-50%) scale(${inverseScale})`;
                    const toolsTransform = `translate3d(${toolsShiftX}px, ${toolsShiftY}px, 0) scale(${inverseScale})`;
                    readerControls.style.setProperty('transform', controlsTransform, 'important');
                    readerPageTools.style.setProperty('transform', toolsTransform, 'important');
                    // Some mobile engines round fixed bottom offsets before
                    // applying transforms. Correct from the measured result so
                    // the visual viewport edges stay aligned to screen pixels.
                    const controlsTransformedRect = readerRectWithoutTranslate(readerControls);
                    const toolsTransformedRect = readerRectWithoutTranslate(readerPageTools);
                    const controlCorrectionX = (visualCenterX - (controlsTransformedRect.left + controlsTransformedRect.width / 2)) * scale;
                    const controlCorrectionY = (offsetY + readerViewportBaselineMargins.controlsTop * inverseScale - controlsTransformedRect.top) * scale;
                    const toolsCorrectionX = (visualCenterX - (toolsTransformedRect.left + toolsTransformedRect.width / 2)) * scale;
                    const toolsCorrectionY = (offsetY + viewport.height - readerViewportBaselineMargins.toolsBottom * inverseScale - toolsTransformedRect.bottom) * scale;
                    if (Math.abs(controlCorrectionX) > 0.01 || Math.abs(controlCorrectionY) > 0.01) {
                        readerControls.style.setProperty('transform', `translate3d(${controlShiftX + controlCorrectionX}px, ${controlShiftY + controlCorrectionY}px, 0) translateX(-50%) scale(${inverseScale})`, 'important');
                    }
                    if (Math.abs(toolsCorrectionX) > 0.01 || Math.abs(toolsCorrectionY) > 0.01) {
                        readerPageTools.style.setProperty('transform', `translate3d(${toolsShiftX + toolsCorrectionX}px, ${toolsShiftY + toolsCorrectionY}px, 0) scale(${inverseScale})`, 'important');
                    }
                });
            };
            const refreshReaderPageToolsBaseline = () => {
                const controlsTop = readerStylePixel(readerControls, 'top', readerViewportBaselineMargins.controlsTop);
                const toolsBottom = readerStylePixel(readerPageTools, 'bottom', readerViewportBaselineMargins.toolsBottom);
                if (Number.isFinite(controlsTop)) {
                    readerViewportBaselineMargins.controlsTop = controlsTop;
                    readerViewportBaselineMargins.controlsTopTarget = controlsTop;
                }
                if (Number.isFinite(toolsBottom)) {
                    readerViewportBaselineMargins.toolsBottom = toolsBottom;
                    readerViewportBaselineMargins.toolsBottomTarget = toolsBottom;
                }
            };
            const clearReaderChromeFadeTimer = () => {
                if (readerChromeFadeTimer !== null) {
                    window.clearTimeout?.(readerChromeFadeTimer);
                    readerChromeFadeTimer = null;
                }
            };
            const readerViewportSnapshot = () => {
                const viewport = window.visualViewport;
                if (!viewport) return null;
                return {
                    scale: Number(viewport.scale) || 1,
                    width: Number(viewport.width) || 0,
                    height: Number(viewport.height) || 0,
                    offsetLeft: Number(viewport.offsetLeft) || 0,
                    offsetTop: Number(viewport.offsetTop) || 0
                };
            };
            const viewportSnapshotChanged = (previous, next) => !!previous && !!next && (
                Math.abs(previous.scale - next.scale) > 0.01 ||
                Math.abs(previous.width - next.width) > 1 ||
                Math.abs(previous.height - next.height) > 1 ||
                Math.abs(previous.offsetLeft - next.offsetLeft) > 1 ||
                Math.abs(previous.offsetTop - next.offsetTop) > 1
            );
            let readerLastViewportSnapshot = readerViewportSnapshot();
            const logReaderNativeZoomDiagnostic = () => {
                if (!navLogEnabled || readerZoomDiagnosticCount >= 3 || !isNativeReaderZoomed()) return;
                const viewport = window.visualViewport;
                if (!viewport) return;
                const scrollY = Number(window.scrollY) || 0;
                const eventScrollDelta = readerZoomLastViewportScrollY === null
                    ? 0
                    : scrollY - readerZoomLastViewportScrollY;
                if (Math.abs(eventScrollDelta) > (Number(viewport.height) || readerLayoutHeight())) {
                    readerZoomNonUserScrollJumpCount++;
                    readerZoomLastScrollJump = eventScrollDelta;
                }
                readerZoomLastViewportScrollY = scrollY;
                readerZoomDiagnosticCount++;
                navLog('reader-native-zoom-diagnostic', {
                    scale: Number((Number(viewport.scale) || 1).toFixed(3)),
                    innerWidth: Number(window.innerWidth) || 0,
                    clientWidth: Number(document.documentElement.clientWidth) || 0,
                    visualViewportWidth: Number(viewport.width) || 0,
                    windowResizeCount: readerZoomResizeCount,
                    visualViewportEventCount: readerZoomViewportEventCount,
                    maximumScale: Number(readerZoomMaximumScale.toFixed(3)),
                    scrollRewriteCount: readerZoomScrollRewriteCount,
                    userScrollRewriteCount: readerZoomUserScrollRewriteCount,
                    nonUserScrollJumpCount: readerZoomNonUserScrollJumpCount,
                    lastNonUserScrollJump: Number(readerZoomLastScrollJump.toFixed(1))
                }, true);
            };
            // 1.17.255: iOS does not always deliver a final visualViewport event
            // once a pinch settles back at scale 1, and may settle a hair above 1.
            // A slightly tolerant threshold plus a watchdog that re-checks while
            // the chrome is hidden guarantees the toolbars come back.
            const READER_CHROME_ZOOM_THRESHOLD = 1.02;
            let readerChromeRestoreWatchdog = null;
            // 1.17.255: a two-finger pinch hides the chrome on the very first
            // touch, before iOS starts scaling, and the chrome stays hidden until
            // every finger is lifted. It never visibly moves with the zoom.
            let readerPinchGestureActive = false;
            let readerChromeWatchdogLastScale = null;
            const stopReaderChromeRestoreWatchdog = () => {
                if (readerChromeRestoreWatchdog !== null) {
                    window.clearInterval?.(readerChromeRestoreWatchdog);
                    readerChromeRestoreWatchdog = null;
                }
                readerChromeWatchdogLastScale = null;
            };
            const startReaderChromeRestoreWatchdog = () => {
                if (readerChromeRestoreWatchdog !== null) return;
                readerChromeWatchdogLastScale = null;
                readerChromeRestoreWatchdog = window.setInterval(() => {
                    if (!readerRoot?.isConnected || !readerRoot.classList.contains('ehtw-reader-chrome-moving')) {
                        stopReaderChromeRestoreWatchdog();
                        return;
                    }
                    updateReaderChromeMovement(false, 3);
                    if (readerChromeRestoreWatchdog !== null) readerChromeWatchdogLastScale = Number(window.visualViewport?.scale) || 1;
                }, 400);
            };
            const updateReaderChromeMovement = (hasMoved = false, restoreVia = 1) => {
                const viewport = window.visualViewport;
                if (!viewport || !readerRoot?.isConnected) {
                    stopReaderChromeRestoreWatchdog();
                    return;
                }
                const scale = Number(viewport.scale) || 1;
                readerZoomMaximumScale = Math.max(readerZoomMaximumScale, scale);
                // Below scale ~1 iOS is still rubber-banding back; wait until it
                // settles (or the watchdog sees the same scale twice in a row).
                const settledBelowOne = scale < 0.98 && restoreVia === 3 &&
                    readerChromeWatchdogLastScale !== null &&
                    Math.abs(readerChromeWatchdogLastScale - scale) < 0.001;
                const unzoomed = scale <= READER_CHROME_ZOOM_THRESHOLD && (scale >= 0.98 || settledBelowOne);
                if (unzoomed && readerPinchGestureActive && readerRoot.classList.contains('ehtw-reader-chrome-moving')) return;
                if (scale <= READER_CHROME_ZOOM_THRESHOLD && !unzoomed) {
                    readerRoot.classList.add('ehtw-reader-chrome-moving');
                    startReaderChromeRestoreWatchdog();
                    return;
                }
                if (unzoomed) {
                    readerZoomChromeRequested = false;
                    clearReaderChromeFadeTimer();
                    // 1.17.255: re-anchor the toolbars to their CSS offsets on every
                    // return to scale 1, even if no viewport event announced it.
                    if (readerRoot.classList.contains('ehtw-reader-chrome-moving')) {
                        readerZoomBaselineCaptured = false;
                        refreshReaderPageToolsBaseline();
                        updateReaderPageToolsViewport();
                    }
                    stopReaderChromeRestoreWatchdog();
                    if (readerRoot.classList.contains('ehtw-reader-chrome-moving')) {
                        readerRoot.classList.remove('ehtw-reader-chrome-moving');
                        navLog('reader-native-zoom-chrome-restored', {
                            scale: Number(scale.toFixed(3)),
                            via: restoreVia
                        }, true);
                    }
                    readerZoomDiagnosticCount = 0;
                    readerZoomLastViewportScrollY = null;
                    return;
                }
                // 1.17.247: while natively pinch-zoomed the reader chrome stays
                // hidden. iOS positions fixed elements unpredictably at zoom, so
                // the toolbars only come back once the page is at scale 1 again.
                const shownOnRequest = readerZoomChromeRequested &&
                    !readerRoot.classList.contains('ehtw-reader-chrome-moving');
                if (shownOnRequest && !hasMoved && !readerPinchGestureActive) return;
                readerRoot.classList.add('ehtw-reader-chrome-moving');
                startReaderChromeRestoreWatchdog();
                if (!hasMoved && !readerZoomChromeRequested) return;
                clearReaderChromeFadeTimer();
                readerChromeFadeTimer = window.setTimeout(() => {
                    readerChromeFadeTimer = null;
                    if (readerTouchActive || readerPinchGestureActive || isNativeReaderZoomed() === false || !readerRoot?.isConnected) return;
                    logReaderNativeZoomDiagnostic();
                    if (readerZoomChromeRequested) showReaderChromeAtZoom();
                }, 250);
            };
            const showReaderChromeAtZoom = () => {
                if (!readerRoot?.isConnected) return;
                readerRoot.classList.remove('ehtw-reader-chrome-moving');
                updateReaderPageToolsViewport();
            };
            toggleReaderChromeWhileZoomed = () => {
                if (!isNativeReaderZoomed() || !readerRoot?.isConnected) return false;
                const visible = !readerRoot.classList.contains('ehtw-reader-chrome-moving') &&
                    !readerRoot.classList.contains('ehtw-reader-settings-hidden');
                clearReaderChromeFadeTimer();
                if (visible) {
                    readerZoomChromeRequested = false;
                    readerRoot.classList.add('ehtw-reader-settings-hidden');
                } else {
                    readerZoomChromeRequested = true;
                    readerRoot.classList.remove('ehtw-reader-settings-hidden');
                    showReaderChromeAtZoom();
                }
                return true;
            };
            const onReaderViewportMovement = () => {
                const nextSnapshot = readerViewportSnapshot();
                if (isNativeReaderZoomed()) {
                    if (!readerZoomBaselineCaptured && (
                        readerControls.getBoundingClientRect().height <= 0 ||
                        readerPageTools.getBoundingClientRect().height <= 0
                    )) {
                        // 1.17.255: toolbars hidden (display:none) when the zoom
                        // started measure as 0x0 at the top; use their CSS offsets.
                        refreshReaderPageToolsBaseline();
                        readerZoomBaselineCaptured = true;
                    }
                    if (!readerZoomBaselineCaptured) {
                        const controlsRect = readerRectWithoutTranslate(readerControls);
                        const toolsRect = readerRectWithoutTranslate(readerPageTools);
                        readerViewportBaselineMargins.controlsTop = controlsRect.top;
                        readerViewportBaselineMargins.controlsTopTarget = controlsRect.top;
                        // iOS may already report a shrunken innerHeight on the first
                        // pinch event; never measure the baseline against less than
                        // the layout height.
                        const baselineBottomEdge = Math.max(Number(window.innerHeight) || 0, readerLayoutHeight());
                        readerViewportBaselineMargins.toolsBottom = baselineBottomEdge - toolsRect.bottom;
                        readerViewportBaselineMargins.toolsBottomTarget = baselineBottomEdge - toolsRect.bottom;
                        readerZoomBaselineCaptured = true;
                    }
                    readerZoomViewportEventCount++;
                    const scrollY = Number(window.scrollY) || 0;
                    if (readerZoomLastViewportScrollY !== null) {
                        const delta = scrollY - readerZoomLastViewportScrollY;
                        if (Math.abs(delta) > (Number(window.visualViewport?.height) || readerLayoutHeight())) {
                            readerZoomNonUserScrollJumpCount++;
                            readerZoomLastScrollJump = delta;
                        }
                    }
                    readerZoomLastViewportScrollY = scrollY;
                } else {
                    readerZoomDiagnosticCount = 0;
                    readerZoomLastViewportScrollY = null;
                    readerZoomBaselineCaptured = false;
                    refreshReaderPageToolsBaseline();
                    readerZoomMaximumScale = 1;
                    readerZoomResizeCount = 0;
                    readerZoomViewportEventCount = 0;
                    readerZoomScrollRewriteCount = 0;
                    readerZoomUserScrollRewriteCount = 0;
                    readerZoomNonUserScrollJumpCount = 0;
                    readerZoomLastScrollJump = 0;
                }
                const changed = viewportSnapshotChanged(readerLastViewportSnapshot, nextSnapshot);
                readerLastViewportSnapshot = nextSnapshot;
                const changedSinceTouchStart = viewportSnapshotChanged(readerTouchStartViewport, nextSnapshot);
                if (!readerTouchStartedOnChrome || (changed && changedSinceTouchStart)) {
                    updateReaderChromeMovement(changed);
                }
                updateReaderPageToolsViewport();
            };
            const onReaderTouchStart = (event) => {
                const touchCount = Number(event.touches?.length) || 0;
                readerTouchActive = touchCount > 0;
                if (!readerTouchActive) return;
                // A fresh single-finger touch means any earlier gesture is over,
                // even if its touchend/touchcancel was lost.
                if (touchCount === 1) readerPinchGestureActive = false;
                if (touchCount >= 2 && readerRoot?.isConnected) {
                    readerPinchGestureActive = true;
                    clearReaderChromeFadeTimer();
                    readerRoot.classList.add('ehtw-reader-chrome-moving');
                    startReaderChromeRestoreWatchdog();
                }
                if (!isNativeReaderZoomed()) {
                    const controlsTop = readerStylePixel(readerControls, 'top', readerViewportBaselineMargins.controlsTop);
                    const toolsBottom = readerStylePixel(readerPageTools, 'bottom', readerViewportBaselineMargins.toolsBottom);
                    if (Number.isFinite(controlsTop)) {
                        readerViewportBaselineMargins.controlsTop = controlsTop;
                        readerViewportBaselineMargins.controlsTopTarget = controlsTop;
                    }
                    if (Number.isFinite(toolsBottom)) {
                        readerViewportBaselineMargins.toolsBottom = toolsBottom;
                        readerViewportBaselineMargins.toolsBottomTarget = toolsBottom;
                    }
                    readerZoomBaselineCaptured = true;
                }
                const target = event.target;
                if (target?.closest?.('.ehtw-reader-controls, .ehtw-reader-page-tools')) {
                    readerTouchStartedOnChrome = true;
                    readerTouchStartViewport = readerViewportSnapshot();
                } else if (!readerTouchStartedOnChrome) {
                    readerTouchStartViewport = readerViewportSnapshot();
                }
            };
            const onReaderTouchMove = (event) => {
                readerTouchActive = Number(event.touches?.length) > 0;
                if (!window.visualViewport || !isNativeReaderZoomed()) return;
                if (!readerTouchStartedOnChrome) updateReaderChromeMovement(true);
                else if (viewportSnapshotChanged(readerTouchStartViewport, readerViewportSnapshot())) updateReaderChromeMovement(true);
            };
            const onReaderTouchEnd = (event) => {
                readerTouchActive = Boolean(event.touches?.length);
                if (!readerTouchActive) {
                    readerPinchGestureActive = false;
                    readerTouchStartedOnChrome = false;
                    readerTouchStartViewport = null;
                    if (readerRoot?.classList.contains('ehtw-reader-chrome-moving')) updateReaderChromeMovement(isNativeReaderZoomed(), 2);
                }
            };
            addSpaPageEventListener(readerRoot, 'focusin', updateReaderPageToolsViewport, true);
            addSpaPageEventListener(readerRoot, 'focusout', updateReaderPageToolsViewport, true);
            addSpaPageEventListener(window, 'resize', updateReaderPageToolsViewport);
            if (window.visualViewport) {
                addSpaPageEventListener(window.visualViewport, 'resize', onReaderViewportMovement);
                addSpaPageEventListener(window.visualViewport, 'scroll', onReaderViewportMovement);
            }
            let readerScrollChromeLastY = Number(window.scrollY) || 0;
            let readerScrollChromeTravel = 0;
            let readerUserScrollInputAt = 0;
            const markReaderUserScrollInput = () => {
                readerUserScrollInputAt = performance.now();
            };
            const setReaderScrollChromeHidden = (hidden) => {
                if (!readerRoot?.isConnected) return;
                if (readerRoot.classList.contains('ehtw-reader-chrome-scroll-hidden') !== hidden) {
                    navLog('reader-scroll-chrome', { hidden: hidden ? 1 : 0, scrollY: Math.round(Number(window.scrollY) || 0) }, true);
                }
                readerRoot.classList.toggle('ehtw-reader-chrome-scroll-hidden', hidden);
                if (!hidden) updateReaderPageToolsViewport();
            };
            // Nav-log only: why a noticeable scroll did not toggle the chrome.
            let readerScrollChromeSkipLoggedAt = 0;
            const logReaderScrollChromeSkip = (reason, delta) => {
                if (!navLogEnabled || Math.abs(delta) < 24) return;
                const now = performance.now();
                if (now - readerScrollChromeSkipLoggedAt < 1000) return;
                readerScrollChromeSkipLoggedAt = now;
                navLog('reader-scroll-chrome-skip', { reason, delta: Math.round(delta), touch: readerTouchActive ? 1 : 0 }, true);
            };
            showReaderScrollHiddenChrome = () => {
                if (!readerRoot?.classList.contains('ehtw-reader-chrome-scroll-hidden')) return false;
                readerScrollChromeTravel = 0;
                setReaderScrollChromeHidden(false);
                return true;
            };
            const onReaderScrollChrome = () => {
                const y = Number(window.scrollY) || 0;
                const delta = y - readerScrollChromeLastY;
                readerScrollChromeLastY = y;
                const now = performance.now();
                if (
                    !readerRoot?.isConnected ||
                    clickPaging ||
                    isNativeReaderZoomed() ||
                    readerTouchStartedOnChrome ||
                    pageSliderDragActive ||
                    now - readerProgrammaticScrollAt < 300
                ) {
                    logReaderScrollChromeSkip(
                        clickPaging ? 1 : isNativeReaderZoomed() ? 2 : readerTouchStartedOnChrome ? 3 : pageSliderDragActive ? 4 : 5,
                        delta
                    );
                    readerScrollChromeTravel = 0;
                    if (clickPaging) setReaderScrollChromeHidden(false);
                    return;
                }
                // Only finger, momentum, wheel or key scrolling counts; page loads
                // and scroll anchoring never toggle the chrome.
                const userDriven = readerTouchActive || now - readerUserScrollInputAt < 1200;
                if (!userDriven || Math.abs(delta) > readerLayoutHeight()) {
                    logReaderScrollChromeSkip(userDriven ? 7 : 6, delta);
                    readerScrollChromeTravel = 0;
                    return;
                }
                readerUserScrollInputAt = now;
                if (y <= 8) {
                    readerScrollChromeTravel = 0;
                    setReaderScrollChromeHidden(false);
                    return;
                }
                if (delta === 0) return;
                if (Math.sign(delta) !== Math.sign(readerScrollChromeTravel)) readerScrollChromeTravel = 0;
                readerScrollChromeTravel += delta;
                if (readerScrollChromeTravel > 24) setReaderScrollChromeHidden(true);
                else if (readerScrollChromeTravel < -24) setReaderScrollChromeHidden(false);
            };
            addSpaPageEventListener(window, 'scroll', onReaderScrollChrome, { passive: true });
            addSpaPageEventListener(document, 'touchstart', markReaderUserScrollInput, { passive: true });
            addSpaPageEventListener(document, 'touchend', markReaderUserScrollInput, { passive: true });
            addSpaPageEventListener(window, 'wheel', markReaderUserScrollInput, { passive: true });
            addSpaPageEventListener(window, 'keydown', markReaderUserScrollInput, { passive: true });
            addSpaPageEventListener(document, 'touchstart', onReaderTouchStart, { passive: true });
            addSpaPageEventListener(document, 'touchmove', onReaderTouchMove, { passive: true });
            addSpaPageEventListener(document, 'touchend', onReaderTouchEnd, { passive: true });
            addSpaPageEventListener(document, 'touchcancel', onReaderTouchEnd, { passive: true });
            updateReaderPageToolsViewport();
            // 1.17.255: numeric-only diagnostic for the iOS status bar area above
            // the reader (is the page drawn under it, and where the image starts).
            let readerStatusBarDiagnosticCount = 0;
            const logReaderStatusBarDiagnostic = (trigger) => {
                if (!navLogEnabled || readerStatusBarDiagnosticCount >= 6 || !readerRoot?.isConnected || isNativeReaderZoomed()) return;
                readerStatusBarDiagnosticCount++;
                try {
                    const probe = document.createElement('div');
                    probe.style.cssText = 'position:fixed;left:0;top:0;width:0;height:0;visibility:hidden;pointer-events:none;padding-top:env(safe-area-inset-top,0px);padding-bottom:env(safe-area-inset-bottom,0px)';
                    document.body.appendChild(probe);
                    const probeStyle = window.getComputedStyle(probe);
                    const safeTop = Number.parseFloat(probeStyle.paddingTop) || 0;
                    const safeBottom = Number.parseFloat(probeStyle.paddingBottom) || 0;
                    probe.remove();
                    const activeFrame = readerRoot.querySelector('.ehtw-reader-image-frame.ehtw-reader-active') ||
                        readerRoot.querySelector('.ehtw-reader-image-frame');
                    const frameRect = activeFrame?.getBoundingClientRect();
                    const viewport = window.visualViewport;
                    const round = (value) => Number((Number(value) || 0).toFixed(1));
                    const unitHeight = (value) => {
                        const unitProbe = document.createElement('div');
                        unitProbe.style.cssText = `position:absolute;left:0;top:0;width:0;visibility:hidden;pointer-events:none;height:${value}`;
                        document.body.appendChild(unitProbe);
                        const height = unitProbe.getBoundingClientRect().height;
                        unitProbe.remove();
                        return height;
                    };
                    const spaEnteredReader = performance.getEntriesByType?.('navigation')?.[0]?.name
                        ? !/\/s\//.test(new URL(performance.getEntriesByType('navigation')[0].name).pathname)
                        : false;
                    navLog('reader-status-bar-diagnostic', {
                        trigger: trigger === 'resize' ? 2 : 1,
                        clickMode: clickPaging ? 1 : 0,
                        standalone: window.matchMedia?.('(display-mode: standalone)')?.matches || window.navigator?.standalone ? 1 : 0,
                        safeAreaTop: round(safeTop),
                        safeAreaBottom: round(safeBottom),
                        innerHeight: Number(window.innerHeight) || 0,
                        outerHeight: Number(window.outerHeight) || 0,
                        clientHeight: Number(document.documentElement.clientHeight) || 0,
                        screenHeight: Number(window.screen?.height) || 0,
                        screenAvailHeight: Number(window.screen?.availHeight) || 0,
                        visualViewportHeight: round(viewport?.height),
                        visualViewportOffsetTop: round(viewport?.offsetTop),
                        scrollY: round(window.scrollY),
                        devicePixelRatio: Number(window.devicePixelRatio) || 0,
                        frameTop: round(frameRect?.top),
                        frameHeight: round(frameRect?.height),
                        // 1.17.261: toolbar edges and viewport-unit heights, to see
                        // what Safari's top/bottom bar areas overlap.
                        controlsTop: round(readerControls?.getBoundingClientRect().top),
                        toolsBottom: round(readerPageTools?.getBoundingClientRect().bottom),
                        svh: round(unitHeight('100svh')),
                        lvh: round(unitHeight('100lvh')),
                        dvh: round(unitHeight('100dvh')),
                        enteredInPage: spaEnteredReader ? 1 : 0
                    }, true);
                } catch (_) {}
            };
            if (navLogEnabled) {
                window.setTimeout(() => logReaderStatusBarDiagnostic('ready'), 600);
                addSpaPageEventListener(window, 'resize', () => logReaderStatusBarDiagnostic('resize'));
            }
            releasePageBoot();
            const beginPageTransition = (pageNumber) => {
                document.documentElement.classList.add('ehtw-reader-page-pending');
                readerTransitionMessage.textContent = Number.isFinite(pageNumber)
                    ? `正在加载第 ${pageNumber} 页…`
                    : '正在加载…';
            };
            const endPageTransition = () => {
                document.documentElement.classList.remove('ehtw-reader-page-pending');
                readerTransitionMessage.textContent = '';
            };
            let lastReaderLayoutWidth = readerLayoutWidth();
            let lastReaderLayoutHeight = readerLayoutHeight();
            resizeHandler = () => {
                if (isNativeReaderZoomed()) {
                    readerZoomResizeCount++;
                    readerZoomMaximumScale = Math.max(readerZoomMaximumScale, Number(window.visualViewport?.scale) || 1);
                    logReaderNativeZoomDiagnostic();
                }
                const layoutWidth = readerLayoutWidth();
                const layoutHeight = readerLayoutHeight();
                if (layoutWidth === lastReaderLayoutWidth && layoutHeight === lastReaderLayoutHeight) {
                    return;
                }
                if (!isNativeReaderZoomed()) refreshReaderPageToolsBaseline();
                lastReaderLayoutWidth = layoutWidth;
                lastReaderLayoutHeight = layoutHeight;
                const scrollAnchor = captureReaderScrollAnchor();
                if (clickPaging) {
                    const image = readerPageImages[activeImageIndex];
                    sizeReaderFrame(
                        pageFrames.get(image),
                        image,
                        pageInfo.get(image),
                        true
                    );
                } else {
                    readerPageImages.forEach((image) => {
                        sizeReaderFrame(
                            pageFrames.get(image),
                            image,
                            pageInfo.get(image),
                            false
                        );
                    });
                }
                restoreReaderScrollAnchor(scrollAnchor);
            };
            addSpaPageEventListener(window, 'resize', resizeHandler);
            updateMode(clickPaging);

            let autoFillBatches = 0;
            let scrollDuringBatch = false;
            let autoFillPreviousBatches = 0;
            let scrollDuringPreviousBatch = false;

            const getImageSource = (image) => {
                const dataSrc = image.getAttribute('data-src');
                const src = image.getAttribute('src');

                if (dataSrc && !/^data:image\//i.test(dataSrc)) {
                    return dataSrc;
                }

                if (src && !/^data:image\//i.test(src)) {
                    return src;
                }

                return dataSrc || src || '';
            };

            const resolveImageUrl = (rawUrl, baseUrl) => {
                if (!rawUrl) return null;

                try {
                    return new URL(rawUrl, baseUrl).href;
                } catch (_) {
                    return null;
                }
            };

            const fetchGalleryIndexPage = (index) => {
                if (galleryIndexCache.has(index)) {
                    return Promise.resolve(galleryIndexCache.get(index));
                }
                if (galleryIndexFetchPromises.has(index)) {
                    return galleryIndexFetchPromises.get(index);
                }
                if (
                    !galleryIndexUrl ||
                    Date.now() < galleryIndexBlockedUntil ||
                    typeof window.fetch !== 'function' ||
                    typeof window.DOMParser !== 'function'
                ) {
                    return Promise.resolve(null);
                }

                const request = (async () => {
                    const indexUrl = new URL(galleryIndexUrl);
                    indexUrl.searchParams.set('p', String(index));
                    const response = await window.fetch(indexUrl.href, {
                        credentials: 'same-origin'
                    });
                    if (response?.status === 429 || response?.status === 403) {
                        galleryIndexBlockedUntil = Date.now() + 30 * 60 * 1000;
                        return null;
                    }
                    if (!response?.ok) return null;

                    const pageText = await response.text();
                    if (
                        pageText.length < 3000 &&
                        !pageText.includes('id="gdt"') &&
                        /temporarily banned|too fast|excessive/i.test(pageText)
                    ) {
                        galleryIndexBlockedUntil = Date.now() + 30 * 60 * 1000;
                        return null;
                    }
                    const parsedPage = new window.DOMParser()
                        .parseFromString(pageText, 'text/html');
                    const galleryId = new URL(galleryIndexUrl).pathname
                        .match(/^\/g\/(\d+)\//i)?.[1];
                    const listing = parsedPage.querySelector('#gdt') || parsedPage;
                    const urlsByPage = new Map();

                    Array.from(listing.querySelectorAll('a[href]')).forEach((link) => {
                        try {
                            const url = new URL(
                                link.getAttribute('href') || link.href,
                                indexUrl.href
                            );
                            const match = url.pathname.match(/\/s\/[^/]+\/(\d+)-(\d+)\/?$/i);
                            if (
                                url.origin === location.origin &&
                                match &&
                                (!galleryId || match[1] === galleryId)
                            ) {
                                urlsByPage.set(Number.parseInt(match[2], 10), url.href);
                            }
                        } catch (_) {}
                    });

                    galleryIndexCache.set(index, urlsByPage);
                    urlsByPage.forEach((pageUrl, pageNumber) => {
                        knownPageUrls.set(pageNumber, pageUrl);
                    });
                    return urlsByPage;
                })().finally(() => {
                    galleryIndexFetchPromises.delete(index);
                });
                galleryIndexFetchPromises.set(index, request);
                return request;
            };

            const findGalleryPageUrl = async (pageNumber) => {
                if (knownPageUrls.has(pageNumber)) {
                    return knownPageUrls.get(pageNumber);
                }

                const firstIndex = await fetchGalleryIndexPage(0);
                if (!firstIndex?.size) return null;

                const pagesPerIndex = firstIndex.size;
                const index = Math.floor((pageNumber - 1) / pagesPerIndex);
                const pageIndex = index === 0
                    ? firstIndex
                    : await fetchGalleryIndexPage(index);
                return pageIndex?.get(pageNumber) || null;
            };

            const acquireGalleryPageInfoSlot = () => new Promise((resolve) => {
                if (activeGalleryPageInfoFetches < 5) {
                    activeGalleryPageInfoFetches += 1;
                    resolve();
                } else {
                    galleryPageInfoFetchWaiters.push(resolve);
                }
            });

            const releaseGalleryPageInfoSlot = () => {
                const next = galleryPageInfoFetchWaiters.shift();
                if (next) next();
                else activeGalleryPageInfoFetches = Math.max(0, activeGalleryPageInfoFetches - 1);
            };

            const fetchGalleryPageInfo = (pageUrl, requestedPageNumber, signal) => {
                const safeUrl = safePageUrl(pageUrl, currentPageUrl);
                if (!safeUrl) return Promise.resolve(null);
                if (galleryPageInfoCache.has(safeUrl)) {
                    return Promise.resolve(galleryPageInfoCache.get(safeUrl));
                }
                if (galleryPageInfoFetchPromises.has(safeUrl)) {
                    return galleryPageInfoFetchPromises.get(safeUrl);
                }

                const request = (async () => {
                    await acquireGalleryPageInfoSlot();
                    try {
                        if (signal?.aborted) return null;
                        const response = await window.fetch(safeUrl, {
                            credentials: 'same-origin',
                            cache: 'no-store',
                            ...(signal ? { signal } : {})
                        });
                        if (!response?.ok) return null;

                        const pageText = await response.text();
                        if (signal?.aborted) return null;
                        const parsedPage = new window.DOMParser()
                            .parseFromString(pageText, 'text/html');
                        const image = parsedPage.querySelector('#img') ||
                            parsedPage.querySelector('#i3 img');
                        const imageUrl = resolveImageUrl(getImageSource(image), safeUrl);
                        if (!imageUrl) return null;

                        const position = readPagePosition(parsedPage, safeUrl);
                        const pageNumber = position.page || requestedPageNumber;
                        const page = {
                            pageUrl: safeUrl,
                            imageUrl,
                            previousUrl: findPageLink(parsedPage, 'previous', safeUrl),
                            nextUrl: findPageLink(parsedPage, 'next', safeUrl),
                            pageNumber,
                            totalPages: position.total,
                            width: position.width,
                            height: position.height
                        };
                        if (page.pageNumber) {
                            knownPageUrls.set(page.pageNumber, page.pageUrl);
                            if (page.previousUrl) {
                                knownPageUrls.set(page.pageNumber - 1, page.previousUrl);
                            }
                            if (page.nextUrl) {
                                knownPageUrls.set(page.pageNumber + 1, page.nextUrl);
                            }
                        }
                        galleryPageInfoCache.set(safeUrl, page);
                        if (galleryPageInfoCache.size > 100) {
                            galleryPageInfoCache.delete(
                                galleryPageInfoCache.keys().next().value
                            );
                        }
                        return page;
                    } finally {
                        releaseGalleryPageInfoSlot();
                    }
                })().finally(() => {
                    galleryPageInfoFetchPromises.delete(safeUrl);
                });
                galleryPageInfoFetchPromises.set(safeUrl, request);
                return request;
            };

            const prefetchGalleryPageInfoRange = async (firstPage, lastPage, signal) => {
                if (
                    !galleryIndexUrl ||
                    !Number.isFinite(firstPage) ||
                    !Number.isFinite(lastPage) ||
                    lastPage < firstPage
                ) return;

                const pageNumbers = [];
                for (let pageNumber = firstPage; pageNumber <= lastPage; pageNumber += 1) {
                    pageNumbers.push(pageNumber);
                }
                const pageUrls = await Promise.all(
                    pageNumbers.map((pageNumber) => findGalleryPageUrl(pageNumber))
                );
                await Promise.all(pageUrls.map((pageUrl, index) =>
                    pageUrl
                        ? fetchGalleryPageInfo(pageUrl, pageNumbers[index], signal)
                        : Promise.resolve(null)
                ));
            };

            const startPageImageSource = (imageNode, source) => {
                imageNode.loading = 'eager';
                imageNode.src = source;
                imageLoadWatchdogCallbacks.get(imageNode)?.();
                updateBottomReaderStatus();
            };

            pumpReaderImagePreloadQueue = () => {
                if (!readerPageImages.length) return;
                const centerIndex = Math.max(
                    0,
                    Math.min(readerImageQueueCenterIndex, readerPageImages.length - 1)
                );
                const centerPageNumber = pageInfo.get(
                    readerPageImages[centerIndex]
                )?.pageNumber || currentPageNumber;
                updateReaderImagePriorities(centerPageNumber);

                const pageTransitionPending = document.documentElement.classList.contains(
                    'ehtw-reader-page-pending'
                );
                const groups = getReaderImagePreloadGroups(
                    readerPageImages.length,
                    centerIndex,
                    preloadCount
                );

                const windowImages = groups
                    .flatMap((group) => group.map((index) => readerPageImages[index]))
                    .filter((image) => !pageTransitionPending ||
                        pageInfo.get(image)?.pageNumber === targetedSeekImagePageNumber);
                const isImageSettled = (image) =>
                    image.dataset.ehtwMemoryUnloaded === 'true' ||
                    image.getAttribute('data-ehtw-load-failed') === 'true' ||
                    (image.complete && image.naturalWidth > 0) ||
                    (!image.getAttribute('src') &&
                        !image.dataset.ehtwSeekDeferredSource &&
                        !image.dataset.ehtwMemorySource);
                const windowComplete = !pageTransitionPending &&
                    windowImages.every(isImageSettled);
                pauseReaderImagesOutsideWindow(centerIndex, !windowComplete);

                const windowImageSet = new Set(windowImages);
                const waitingToStart = windowImages.filter((image) =>
                    image.dataset.ehtwMemoryUnloaded !== 'true' &&
                    image.getAttribute('data-ehtw-load-failed') !== 'true' &&
                    !(image.complete && image.naturalWidth > 0) &&
                    !image.getAttribute('src') &&
                    (image.dataset.ehtwSeekDeferredSource || image.dataset.ehtwMemorySource)
                );

                const capacity = Math.max(
                    0,
                    READER_IMAGE_PREFETCH_CONCURRENCY - activeReaderImageQueueLoads.size
                );
                waitingToStart.slice(0, capacity).forEach((image) => {
                    restoreImageResource(image);
                    const source = image.dataset.ehtwSeekDeferredSource ||
                        image.dataset.ehtwMemorySource;
                    if (!source) return;
                    const sourceSet = image.dataset.ehtwSeekDeferredSourceSet ||
                        image.dataset.ehtwMemorySourceSet;
                    delete image.dataset.ehtwSeekDeferredSource;
                    delete image.dataset.ehtwSeekDeferredSourceSet;
                    delete image.dataset.ehtwMemoryUnloaded;
                    delete image.dataset.ehtwMemorySource;
                    delete image.dataset.ehtwMemorySourceSet;
                    delete image.dataset.ehtwPreloadPaused;
                    if (sourceSet) image.setAttribute('srcset', sourceSet);
                    image.loading = 'eager';
                    pageFrames.get(image)?.classList.add('ehtw-reader-image-loading');
                    activeReaderImageQueueLoads.add(image);
                    startPageImageSource(image, source);
                });
            };

            const preparePageImage = (page, batchIndex = 0) => {
                const imageNode = document.createElement('img');
                imageNode.alt = '';
                imageNode.decoding = 'async';
                imageNode.loading = 'eager';
                imageNode.fetchPriority = getReaderImagePriority(
                    page.pageNumber,
                    currentPageNumber,
                    targetedSeekImagePageNumber,
                    preloadCount,
                    batchIndex === 0
                );
                imageNode.dataset.pageUrl = page.pageUrl;
                if (page.width > 0 && page.height > 0) {
                    imageNode.width = page.width;
                    imageNode.height = page.height;
                }

                const deferForTargetSeek =
                    Number.isFinite(targetedSeekImagePageNumber) &&
                    page.pageNumber !== targetedSeekImagePageNumber;
                const outsideImagePreloadWindow = Number.isFinite(page.pageNumber) &&
                    Number.isFinite(currentPageNumber) &&
                    Math.abs(page.pageNumber - currentPageNumber) > preloadCount;
                imageNode.dataset.ehtwSeekDeferredSource = page.imageUrl;
                if (deferForTargetSeek || outsideImagePreloadWindow) imageNode.loading = 'lazy';

                const frame = bindImage(imageNode, page);
                return { imageNode, frame };
            };

            loadNextBatch = () => {
                if (
                    activeBatchPromise ||
                    loadingStopped ||
                    !nextPageUrl ||
                    typeof window.fetch !== 'function' ||
                    typeof window.DOMParser !== 'function'
                ) {
                    return activeBatchPromise || Promise.resolve();
                }

                const batchLimit = pageLinkPreloadCount;
                const sequenceGeneration = readerSequenceGeneration;
                const abortController = typeof window.AbortController === 'function'
                    ? new window.AbortController()
                    : null;
                activeNextAbortController = abortController;
                scrollDuringBatch = false;
                const loadedPageNumbers = readerPageImages
                    .map((image) => pageInfo.get(image)?.pageNumber)
                    .filter(Number.isFinite);
                const nextMetadataStart = loadedPageNumbers.length
                    ? Math.max(...loadedPageNumbers) + 1
                    : currentPageNumber + 1;
                const nextMetadataEnd = Math.min(
                    totalPages || nextMetadataStart + batchLimit - 1,
                    nextMetadataStart + batchLimit - 1
                );
                const lastKnownPage = readerPageImages
                    .map((image) => pageInfo.get(image))
                    .filter((info) => Number.isFinite(info?.pageNumber))
                    .sort((a, b) => b.pageNumber - a.pageNumber)[0];
                if (lastKnownPage?.nextUrl) {
                    knownPageUrls.set(lastKnownPage.pageNumber + 1, lastKnownPage.nextUrl);
                }
                prefetchGalleryPageInfoRange(
                    nextMetadataStart,
                    nextMetadataEnd,
                    abortController?.signal
                ).catch(() => {});

                activeBatchPromise = (async () => {
                    try {
                        for (let i = 0; i < batchLimit && nextPageUrl; i++) {
                            const pageUrl = nextPageUrl;

                            if (seenPages.has(pageUrl)) {
                                nextPageUrl = null;
                                loadingStopped = true;
                                break;
                            }

                            seenPages.add(pageUrl);

                            const page = await fetchGalleryPageInfo(
                                pageUrl,
                                nextMetadataStart + i,
                                abortController?.signal
                            );
                            if (sequenceGeneration !== readerSequenceGeneration) return;
                            if (!page) {
                                nextPageUrl = null;
                                loadingStopped = true;
                                break;
                            }
                            if (page.totalPages && !totalPages) {
                                totalPages = page.totalPages;
                                updatePageCounter(currentPageNumber);
                            }
                            if (page.pageNumber) {
                                knownPageUrls.set(page.pageNumber, page.pageUrl);
                            }

                            const { imageNode, frame } = preparePageImage(page, i);
                            readerPageImages.push(imageNode);
                            readerImages.insertBefore(frame, bottomSentinel);
                            updateBottomReaderStatus();
                            startDeferredImagesAround(
                                clickPaging ? activeImageIndex : findImageAtViewportCenter()
                            );

                            nextPageUrl =
                                page.nextUrl &&
                                page.nextUrl !== pageUrl &&
                                !seenPages.has(page.nextUrl)
                                    ? page.nextUrl
                                    : null;

                            if (!nextPageUrl) {
                                loadingStopped = true;
                            }
                        }
                    } catch (error) {
                        if (sequenceGeneration === readerSequenceGeneration) {
                            loadingStopped = true;
                            console.warn(
                                '[EH Twitter Style] Image preload stopped',
                                error
                            );
                        }
                    }
                })().finally(() => {
                    if (sequenceGeneration !== readerSequenceGeneration) return;
                    activeBatchPromise = null;
                    activeNextAbortController = null;

                    if (
                        !readerInitializing &&
                        !clickPaging &&
                        nextPageUrl &&
                        !loadingStopped &&
                        readerLayoutHeight() > 0 &&
                        autoFillBatches < 5 &&
                        (
                            bottomSentinel.getBoundingClientRect().top < readerLayoutHeight() ||
                            (
                                scrollDuringBatch &&
                                bottomSentinel.getBoundingClientRect().top < readerLayoutHeight() + 100
                            )
                        )
                    ) {
                        autoFillBatches++;
                        const schedule =
                            window.requestAnimationFrame ||
                            ((callback) => window.setTimeout(callback, 0));
                        schedule(() => loadNextBatch());
                    }

                    scrollDuringBatch = false;
                });

                return activeBatchPromise;
            };

            loadPreviousBatch = () => {
                if (
                    activePreviousBatchPromise ||
                    previousLoadingStopped ||
                    !previousPageUrl ||
                    typeof window.fetch !== 'function' ||
                    typeof window.DOMParser !== 'function'
                ) {
                    return activePreviousBatchPromise || Promise.resolve();
                }

                const batchLimit = pageLinkPreloadCount;
                const sequenceGeneration = readerSequenceGeneration;
                const abortController = typeof window.AbortController === 'function'
                    ? new window.AbortController()
                    : null;
                activePreviousAbortController = abortController;
                scrollDuringPreviousBatch = false;
                const loadedPageNumbers = readerPageImages
                    .map((image) => pageInfo.get(image)?.pageNumber)
                    .filter(Number.isFinite);
                const previousMetadataEnd = loadedPageNumbers.length
                    ? Math.min(...loadedPageNumbers) - 1
                    : currentPageNumber - 1;
                const previousMetadataStart = Math.max(
                    1,
                    previousMetadataEnd - batchLimit + 1
                );
                const firstKnownPage = readerPageImages
                    .map((image) => pageInfo.get(image))
                    .filter((info) => Number.isFinite(info?.pageNumber))
                    .sort((a, b) => a.pageNumber - b.pageNumber)[0];
                if (firstKnownPage?.previousUrl) {
                    knownPageUrls.set(firstKnownPage.pageNumber - 1, firstKnownPage.previousUrl);
                }
                prefetchGalleryPageInfoRange(
                    previousMetadataStart,
                    previousMetadataEnd,
                    abortController?.signal
                ).catch(() => {});

                activePreviousBatchPromise = (async () => {
                    const anchorImage = clickPaging
                        ? readerPageImages[activeImageIndex]
                        : readerPageImages[findImageAtViewportCenter()];
                    let insertedCount = 0;

                    try {
                        for (let i = 0; i < batchLimit && previousPageUrl; i++) {
                            const pageUrl = previousPageUrl;

                            if (seenPages.has(pageUrl)) {
                                previousPageUrl = null;
                                previousLoadingStopped = true;
                                break;
                            }

                            seenPages.add(pageUrl);

                            const page = await fetchGalleryPageInfo(
                                pageUrl,
                                previousMetadataEnd - i,
                                abortController?.signal
                            );
                            if (sequenceGeneration !== readerSequenceGeneration) return;
                            if (!page) {
                                previousPageUrl = null;
                                previousLoadingStopped = true;
                                break;
                            }
                            if (page.totalPages && !totalPages) {
                                totalPages = page.totalPages;
                                updatePageCounter(currentPageNumber);
                            }
                            if (page.pageNumber) {
                                knownPageUrls.set(page.pageNumber, page.pageUrl);
                            }
                            const { imageNode, frame } = preparePageImage(page, i);
                            const insertionAnchor = !clickPaging && scrollDuringPreviousBatch
                                ? readerPageImages[findImageAtViewportCenter()]
                                : anchorImage;
                            const anchorTop = insertionAnchor?.getBoundingClientRect?.().top;
                            const insertionPoint =
                                pageFrames.get(readerPageImages[0]) || bottomSentinel;
                            readerImages.insertBefore(frame, insertionPoint);
                            readerPageImages.unshift(imageNode);
                            activeImageIndex++;
                            insertedCount++;
                            updateBottomReaderStatus();

                            if (
                                !clickPaging &&
                                !isNativeReaderZoomed() &&
                                insertionAnchor &&
                                !readerInitializing
                            ) {
                                const anchorAfterTop = insertionAnchor.getBoundingClientRect().top;
                                const scrollRoot = document.scrollingElement ||
                                    document.documentElement;
                                const scrollDelta = anchorAfterTop - anchorTop;
                                if (
                                    Number.isFinite(scrollDelta) &&
                                    Math.abs(scrollDelta) > 1 &&
                                    scrollRoot
                                ) {
                                    recordReaderScrollRewrite();
                                    scrollRoot.scrollTop += scrollDelta;
                                    suppressReaderScrollUntil = performance.now() + 250;
                                }
                            }

                            startDeferredImagesAround(
                                clickPaging ? activeImageIndex : findImageAtViewportCenter()
                            );

                            previousPageUrl =
                                page.previousUrl &&
                                page.previousUrl !== pageUrl &&
                                !seenPages.has(page.previousUrl)
                                    ? page.previousUrl
                                    : null;

                            if (!previousPageUrl) {
                                previousLoadingStopped = true;
                            }
                        }
                    } catch (error) {
                        if (sequenceGeneration === readerSequenceGeneration) {
                            previousLoadingStopped = true;
                            console.warn(
                                '[EH Twitter Style] Previous image preload stopped',
                                error
                            );
                        }
                    }

                    if (!insertedCount) return;

                    if (clickPaging) {
                        showActiveImage(activeImageIndex);
                    }
                })().finally(() => {
                    if (sequenceGeneration !== readerSequenceGeneration) return;
                    activePreviousBatchPromise = null;
                    activePreviousAbortController = null;

                    if (
                        !readerInitializing &&
                        !clickPaging &&
                        previousPageUrl &&
                        !previousLoadingStopped &&
                        readerLayoutHeight() > 0 &&
                        autoFillPreviousBatches < 5 &&
                        scrollDuringPreviousBatch &&
                        topSentinel.getBoundingClientRect().bottom > -100
                    ) {
                        autoFillPreviousBatches++;
                        const schedule =
                            window.requestAnimationFrame ||
                            ((callback) => window.setTimeout(callback, 0));
                        schedule(() => loadPreviousBatch());
                    }

                    scrollDuringPreviousBatch = false;
                });

                return activePreviousBatchPromise;
            };

            let pageSeekPromise = null;
            let pageSeekTargetNumber = null;
            const replaceReaderSequenceWithPage = async (page) => {
                readerSequenceGeneration++;
                activeNextAbortController?.abort();
                activePreviousAbortController?.abort();
                activeNextAbortController = null;
                activePreviousAbortController = null;
                activeBatchPromise = null;
                activePreviousBatchPromise = null;

                readerPageImages.forEach((image) => {
                    pageObserver?.unobserve(image);
                    imageMemoryObserver?.unobserve(image);
                    activeReaderImageQueueLoads.delete(image);
                    const listenerIndex = imageListeners.findIndex((item) => item.image === image);
                    if (listenerIndex >= 0) {
                        const listeners = imageListeners[listenerIndex];
                        image.removeEventListener('load', listeners.onImageLoad);
                        image.removeEventListener('error', listeners.onImageError);
                        listeners.cancelAutoRetry?.();
                        image.removeEventListener('dragstart', listeners.onImageDragStart);
                        image.removeEventListener('click', listeners.onImageClick);
                        listeners.retryButton.removeEventListener('click', listeners.onRetryClick);
                        imageListeners.splice(listenerIndex, 1);
                    }

                    pageFrames.get(image)?.remove();
                    image.removeAttribute('srcset');
                    image.removeAttribute('src');
                    delete image.dataset.ehtwMemorySource;
                    delete image.dataset.ehtwMemorySourceSet;
                    delete image.dataset.ehtwMemoryUnloaded;
                    delete image.dataset.ehtwSeekDeferredSource;
                    delete image.dataset.ehtwSeekDeferredSourceSet;
                    delete image.dataset.ehtwPreloadPaused;
                });
                readerPageImages.splice(0, readerPageImages.length);

                seenPages.clear();
                seenPages.add(page.pageUrl);
                knownPageUrls.set(page.pageNumber, page.pageUrl);
                previousPageUrl = page.previousUrl && page.previousUrl !== page.pageUrl
                    ? page.previousUrl
                    : null;
                nextPageUrl = page.nextUrl && page.nextUrl !== page.pageUrl
                    ? page.nextUrl
                    : null;
                previousLoadingStopped = !previousPageUrl;
                loadingStopped = !nextPageUrl;
                autoFillBatches = 0;
                autoFillPreviousBatches = 0;
                scrollDuringBatch = false;
                scrollDuringPreviousBatch = false;
                targetedSeekImagePageNumber = page.pageNumber;

                const { imageNode, frame } = preparePageImage(page, 0);
                readerPageImages.push(imageNode);
                readerImages.insertBefore(frame, bottomSentinel);
                updateBottomReaderStatus();
                activeImageIndex = 0;
                currentPageNumber = page.pageNumber || currentPageNumber;
                if (page.totalPages) totalPages = page.totalPages;
                updatePageCounter(currentPageNumber);

                if (clickPaging) {
                    showActiveImage(0, true);
                } else {
                    startDeferredImagesAround(0);
                    startDeferredImageResource(imageNode);
                    restoreImageResource(imageNode);
                    scrollImageToViewportCenter(imageNode, true);
                    updatePageCounter(currentPageNumber);
                    endPageTransition();
                }

                targetedSeekImagePageNumber = null;
                startDeferredImagesAround(0);
                const schedule = window.requestAnimationFrame ||
                    ((callback) => window.setTimeout(callback, 0));
                schedule(() => {
                    if (nextPageUrl) loadNextBatch();
                    if (previousPageUrl) loadPreviousBatch();
                });
            };

            navigateToPageNumber = (requestedPageNumber) => {
                if (!Number.isFinite(requestedPageNumber)) {
                    return Promise.resolve(false);
                }

                const maxPageNumber = Math.max(
                    1,
                    totalPages || requestedPageNumber
                );
                pageSeekTargetNumber = Math.max(
                    1,
                    Math.min(maxPageNumber, Math.round(requestedPageNumber))
                );
                targetedSeekImagePageNumber = pageSeekTargetNumber;

                const loadedTargetIndex = readerPageImages.findIndex((image) =>
                    pageInfo.get(image)?.pageNumber === pageSeekTargetNumber
                );
                if (loadedTargetIndex >= 0) {
                    if (!pageSeekPromise) targetedSeekImagePageNumber = null;
                    const targetImage = readerPageImages[loadedTargetIndex];
                    if (targetImage.getAttribute('data-ehtw-load-failed') === 'true') {
                        const listener = imageListeners.find((item) => item.image === targetImage);
                        listener?.resetAutoRetryAttempts?.();
                        listener?.retryManually?.();
                    }
                    if (clickPaging) {
                        showActiveImage(loadedTargetIndex, true);
                    } else {
                        startDeferredImagesAround(loadedTargetIndex);
                        startDeferredImageResource(targetImage);
                        restoreImageResource(targetImage);
                        scrollImageToViewportCenter(targetImage, true);
                        updatePageCounter(pageInfo.get(targetImage)?.pageNumber);
                        endPageTransition();
                        updateBottomReaderStatus();
                    }
                    return pageSeekPromise || Promise.resolve(true);
                }

                if (pageSeekPromise) {
                    pageSeekStatus.textContent = `正在定位第 ${pageSeekTargetNumber} 页…`;
                    beginPageTransition(pageSeekTargetNumber);
                    return pageSeekPromise;
                }

                readerPageTools.classList.add('ehtw-reader-seeking');
                pageSeekStatus.textContent = `正在定位第 ${pageSeekTargetNumber} 页…`;
                beginPageTransition(pageSeekTargetNumber);
                pageSeekPromise = (async () => {
                    let foundIndex = -1;
                    let iterations = 0;

                    while (iterations++ < Math.max(1, totalPages || 10000)) {
                        const target = pageSeekTargetNumber;
                        foundIndex = readerPageImages.findIndex((image) =>
                            pageInfo.get(image)?.pageNumber === target
                        );
                        if (foundIndex >= 0) break;

                        const knownPageNumbers = readerPageImages
                            .map((image) => pageInfo.get(image)?.pageNumber)
                            .filter(Number.isFinite);
                        if (!knownPageNumbers.length) break;

                        const firstKnownPage = Math.min(...knownPageNumbers);
                        const lastKnownPage = Math.max(...knownPageNumbers);
                        const countBeforeLoad = readerPageImages.length;

                        if (
                            galleryIndexUrl &&
                            Math.abs(target - currentPageNumber) > pageLinkPreloadCount
                        ) {
                            pageSeekStatus.textContent = `正在查找第 ${target} 页…`;
                            const directPageUrl = await findGalleryPageUrl(target);
                            if (target !== pageSeekTargetNumber) continue;
                            if (directPageUrl) {
                                pageSeekStatus.textContent = `正在获取第 ${target} 页…`;
                                const directPage = await fetchGalleryPageInfo(directPageUrl, target);
                                if (target !== pageSeekTargetNumber) continue;
                                if (directPage) {
                                    await replaceReaderSequenceWithPage(directPage);
                                    return true;
                                }
                            }
                        }

                        if (
                            target > lastKnownPage &&
                            nextPageUrl &&
                            !loadingStopped
                        ) {
                            pageSeekStatus.textContent = `正在读取第 ${lastKnownPage + 1} 页…`;
                            await loadNextBatch();
                        } else if (
                            target < firstKnownPage &&
                            previousPageUrl &&
                            !previousLoadingStopped
                        ) {
                            pageSeekStatus.textContent = `正在读取第 ${firstKnownPage - 1} 页…`;
                            await loadPreviousBatch();
                        } else {
                            break;
                        }

                        if (readerPageImages.length === countBeforeLoad) break;
                    }

                    foundIndex = readerPageImages.findIndex((image) =>
                        pageInfo.get(image)?.pageNumber === pageSeekTargetNumber
                    );
                    if (foundIndex < 0) return false;

                    if (clickPaging) {
                        showActiveImage(foundIndex);
                    } else {
                        const targetImage = readerPageImages[foundIndex];
                        startDeferredImagesAround(foundIndex);
                        startDeferredImageResource(targetImage);
                        restoreImageResource(targetImage);
                        scrollImageToViewportCenter(targetImage, true);
                        updatePageCounter(pageInfo.get(targetImage)?.pageNumber);
                        endPageTransition();
                        updateBottomReaderStatus();
                    }
                    return true;
                })().then((found) => {
                    if (!found) {
                        pageSeekStatus.textContent = '无法加载目标页';
                        return false;
                    }
                    pageSeekStatus.textContent = '';
                    return true;
                }).catch((error) => {
                    console.warn('[EH Twitter Style] Page seek failed', error);
                    pageSeekStatus.textContent = '无法加载目标页';
                    return false;
                }).finally(() => {
                    pageSeekPromise = null;
                    pageSeekTargetNumber = null;
                    targetedSeekImagePageNumber = null;
                    readerPageTools.classList.remove('ehtw-reader-seeking');
                    endPageTransition();
                    pageSlider.value = String(Math.max(1, currentPageNumber || 1));
                    updateBottomReaderStatus();
                });

                return pageSeekPromise;
            };

            let wheelPagePromise = null;
            const advanceClickPageByWheel = (direction) => {
                if (!clickPaging || wheelPagePromise) return wheelPagePromise;

                wheelPagePromise = (async () => {
                    if (direction > 0) {
                        if (activeImageIndex + 1 < readerPageImages.length) {
                            showActiveImage(activeImageIndex + 1, true);
                            return;
                        }

                        const currentInfo = pageInfo.get(
                            readerPageImages[activeImageIndex]
                        );
                        if (!currentInfo?.nextUrl || !nextPageUrl) return;

                        targetedSeekImagePageNumber = (currentInfo.pageNumber || currentPageNumber) + 1;
                        beginPageTransition(targetedSeekImagePageNumber);
                        const countBeforeLoad = readerPageImages.length;
                        await loadNextBatch();
                        if (
                            readerPageImages.length > countBeforeLoad &&
                            activeImageIndex + 1 < readerPageImages.length
                        ) {
                            showActiveImage(activeImageIndex + 1, true);
                        } else {
                            endPageTransition();
                        }
                        return;
                    }

                    if (activeImageIndex > 0) {
                        showActiveImage(activeImageIndex - 1, true);
                        return;
                    }

                    const currentInfo = pageInfo.get(readerPageImages[activeImageIndex]);
                    if (!currentInfo?.previousUrl || !previousPageUrl) return;

                    targetedSeekImagePageNumber = (currentInfo.pageNumber || currentPageNumber) - 1;
                    beginPageTransition(targetedSeekImagePageNumber);
                    const countBeforeLoad = readerPageImages.length;
                    await loadPreviousBatch();
                    if (
                        readerPageImages.length > countBeforeLoad &&
                        activeImageIndex > 0
                    ) {
                        showActiveImage(activeImageIndex - 1, true);
                    } else {
                        endPageTransition();
                    }
                })().finally(() => {
                    wheelPagePromise = null;
                    targetedSeekImagePageNumber = null;
                    endPageTransition();
                });

                return wheelPagePromise;
            };

            wheelPagingHandler = (event) => {
                if (!clickPaging || event.ctrlKey || event.metaKey) return;

                const target = event.target?.nodeType === 3
                    ? event.target.parentElement
                    : event.target;
                if (target?.closest?.('.ehtw-reader-controls, .ehtw-reader-page-tools')) {
                    return;
                }

                let deltaY = event.deltaY;
                if (event.deltaMode === 1) deltaY *= 16;
                else if (event.deltaMode === 2) deltaY *= readerLayoutHeight() || 800;
                if (!Number.isFinite(deltaY) || Math.abs(deltaY) < 12) return;

                if (zoomPercent > 100) {
                    const canScrollDown = readerImages.scrollTop +
                        readerImages.clientHeight < readerImages.scrollHeight - 1;
                    const canScrollUp = readerImages.scrollTop > 0;
                    if ((deltaY > 0 && canScrollDown) || (deltaY < 0 && canScrollUp)) {
                        return;
                    }
                }

                event.preventDefault();
                if (wheelUnlockTimer) window.clearTimeout?.(wheelUnlockTimer);
                wheelUnlockTimer = window.setTimeout?.(() => {
                    wheelGestureLocked = false;
                    wheelUnlockTimer = null;
                }, 220) || null;

                if (wheelGestureLocked) return;
                wheelGestureLocked = true;
                advanceClickPageByWheel(deltaY > 0 ? 1 : -1);
            };
            addSpaPageEventListener(window, 'wheel', wheelPagingHandler, { passive: false });

            keyPagingHandler = (event) => {
                if (
                    !clickPaging ||
                    event.defaultPrevented ||
                    event.ctrlKey || event.metaKey || event.altKey
                ) {
                    return;
                }
                if (event.key !== 'ArrowLeft' && event.key !== 'ArrowRight') return;
                if (event.target?.closest?.('input, textarea, select, [contenteditable="true"]')) return;

                event.stopImmediatePropagation();
                event.preventDefault();
                advanceClickPageByWheel(event.key === 'ArrowRight' ? 1 : -1);
            };
            addSpaPageEventListener(window, 'keydown', keyPagingHandler, true);

            if (nextPageUrl || previousPageUrl) {
                if (
                    nextPageUrl &&
                    typeof window.IntersectionObserver === 'function'
                ) {
                    observer = new window.IntersectionObserver(
                        (entries) => {
                            if (
                                !readerInitializing &&
                                entries.some(entry => entry.isIntersecting)
                            ) {
                                loadNextBatch();
                            }
                        },
                        { rootMargin: '1200px 0px' }
                    );

                    observer.observe(bottomSentinel);
                }

                if (
                    previousPageUrl &&
                    typeof window.IntersectionObserver === 'function'
                ) {
                    previousObserver = new window.IntersectionObserver(
                        (entries) => {
                            if (
                                !readerInitializing &&
                                entries.some(entry => entry.isIntersecting)
                            ) {
                                loadPreviousBatch();
                            }
                        },
                        { rootMargin: '1200px 0px' }
                    );

                    previousObserver.observe(topSentinel);
                }

                if (typeof window.addEventListener === 'function') {
                    scrollFallback = () => {
                        const currentScrollY = window.scrollY;
                        const scrollDelta = currentScrollY - lastReaderScrollY;
                        lastReaderScrollY = currentScrollY;

                        if (readerInitializing) return;
                        if (performance.now() < suppressReaderScrollUntil) return;

                        if (!clickPaging) {
                            const visibleIndex = findImageAtViewportCenter();
                            releaseReaderImagesOutsideWindow(visibleIndex);
                            startDeferredImagesAround(visibleIndex);
                            const visibleInfo = pageInfo.get(
                                readerPageImages[visibleIndex]
                            );

                            if (visibleInfo?.pageNumber) {
                                updatePageCounter(visibleInfo.pageNumber);
                            }
                            updateBottomReaderStatus();
                            const linkWindowEdge = Math.max(1, preloadCount);
                            if (
                                visibleIndex >= readerPageImages.length - linkWindowEdge - 1 &&
                                nextPageUrl &&
                                !loadingStopped
                            ) {
                                loadNextBatch();
                            }
                            if (
                                visibleIndex <= linkWindowEdge &&
                                previousPageUrl &&
                                !previousLoadingStopped
                            ) {
                                loadPreviousBatch();
                            }
                        }

                        if (clickPaging) return;

                        if (nextPageUrl && scrollDelta > 0) {
                            autoFillBatches = 0;

                            if (activeBatchPromise) {
                                scrollDuringBatch = true;
                            } else if (
                                bottomSentinel.getBoundingClientRect().top <
                                readerLayoutHeight() + 1200
                            ) {
                                loadNextBatch();
                            }
                        }

                        if (previousPageUrl && scrollDelta < 0) {
                            autoFillPreviousBatches = 0;

                            if (activePreviousBatchPromise) {
                                scrollDuringPreviousBatch = true;
                            } else if (
                                topSentinel.getBoundingClientRect().bottom >
                                -readerLayoutHeight() - 1200
                            ) {
                                loadPreviousBatch();
                            }
                        }
                    };

                    addSpaPageEventListener(
                        window,
                        'scroll',
                        scrollFallback,
                        { passive: true }
                    );
                }

                if (!clickPaging && !isMobileReaderViewport()) {
                    scrollImageToViewportCenter(readerImage);
                }
                readerRoot.setAttribute('data-ehtw-reader-ready', 'true');
                readerInitializing = false;
                lastReaderScrollY = window.scrollY;
                updatePageCounter(initialPagePosition.page);

                if (nextPageUrl) loadNextBatch();
                if (previousPageUrl) loadPreviousBatch();
            }

            publishSpaPageCleanup(() => {
                observer?.disconnect();
                previousObserver?.disconnect();
                pageObserver?.disconnect();
                imageMemoryObserver?.disconnect();
                if (wheelUnlockTimer) window.clearTimeout?.(wheelUnlockTimer);
                if (historyTimer) window.clearTimeout?.(historyTimer);
                clearReaderChromeFadeTimer();
                stopReaderChromeRestoreWatchdog();
                readerRoot?.classList.remove('ehtw-reader-chrome-moving');
                imageListeners.forEach(({ image, retryButton, onImageError, onImageDragStart, onImageClick, onRetryClick, clearImageLoadWatchdog, cancelAutoRetry }) => {
                    clearImageLoadWatchdog?.();
                    cancelAutoRetry?.();
                    image.removeEventListener('error', onImageError);
                    image.removeEventListener('dragstart', onImageDragStart);
                    image.removeEventListener('click', onImageClick);
                    retryButton.removeEventListener('click', onRetryClick);
                    image.removeAttribute('src');
                });
                readerRoot?.remove();
                readerStyle?.remove();
                document.documentElement.classList.remove(
                    'ehtw-reader',
                    'ehtw-reader-click',
                    'ehtw-reader-zoomed',
                    'ehtw-reader-settings-hidden',
                    'ehtw-reader-page-pending'
                );
                try {
                    delete window[RUN_KEY];
                } catch (_) {
                    window[RUN_KEY] = false;
                }
            });
        } catch (error) {
            observer?.disconnect();
            previousObserver?.disconnect();
            pageObserver?.disconnect();
            imageMemoryObserver?.disconnect();
            if (resizeHandler) {
                window.removeEventListener?.('resize', resizeHandler);
                resizeHandler = null;
            }
            if (pageSliderReleaseHandler) {
                window.removeEventListener?.('pointerup', pageSliderReleaseHandler);
                window.removeEventListener?.('touchend', pageSliderReleaseHandler);
            }
            if (pageSliderCancelHandler) {
                window.removeEventListener?.('pointercancel', pageSliderCancelHandler);
                window.removeEventListener?.('touchcancel', pageSliderCancelHandler);
            }
            if (wheelPagingHandler) {
                window.removeEventListener?.('wheel', wheelPagingHandler);
                wheelPagingHandler = null;
            }
            if (keyPagingHandler) {
                window.removeEventListener?.('keydown', keyPagingHandler, true);
                keyPagingHandler = null;
            }
            if (wheelUnlockTimer) {
                window.clearTimeout?.(wheelUnlockTimer);
                wheelUnlockTimer = null;
            }
            document.documentElement.classList.remove('ehtw-reader-page-pending');

            if (scrollFallback) {
                window.removeEventListener?.('scroll', scrollFallback);
            }

            imageListeners.forEach(({
                image,
                retryButton,
                onImageError,
                onImageDragStart,
                onImageClick,
                onRetryClick,
                clearImageLoadWatchdog,
                cancelAutoRetry
            }) => {
                clearImageLoadWatchdog?.();
                cancelAutoRetry?.();
                image.removeEventListener('error', onImageError);
                image.removeEventListener('dragstart', onImageDragStart);
                image.removeEventListener('click', onImageClick);
                retryButton.removeEventListener('click', onRetryClick);
            });

            if (originalParent) {
                if (originalNext?.parentNode === originalParent) {
                    originalParent.insertBefore(readerImage, originalNext);
                } else {
                    originalParent.appendChild(readerImage);
                }
            }

            if (originalClass === null) {
                readerImage.removeAttribute('class');
            } else {
                readerImage.setAttribute('class', originalClass);
            }
            if (originalImageSrc === null) readerImage.removeAttribute('src');
            else readerImage.setAttribute('src', originalImageSrc);
            if (originalImageSrcset === null) readerImage.removeAttribute('srcset');
            else readerImage.setAttribute('srcset', originalImageSrcset);
            if (originalImageDataSrc === null) readerImage.removeAttribute('data-src');
            else readerImage.setAttribute('data-src', originalImageDataSrc);

            readerRoot?.remove();
            readerStyle?.remove();

            if (readerViewportCreated) {
                readerViewportMeta?.remove();
            } else if (readerViewportMeta) {
                if (originalViewportContent === null) {
                    readerViewportMeta.removeAttribute('content');
                } else {
                    readerViewportMeta.setAttribute(
                        'content',
                        originalViewportContent
                    );
                }
            }

            document.documentElement.classList.remove(
                'ehtw-reader',
                'ehtw-reader-click',
                'ehtw-reader-boot'
            );

            try {
                delete window[RUN_KEY];
            } catch (_) {
                window[RUN_KEY] = false;
            }

            console.warn('[EH Twitter Style] Reader setup failed', error);
            releasePageBoot();
        }

        return;
    }
    const isDetail = /^\/g\/\d+\/[0-9a-f]+\/?$/i.test(path);
    const isBountyPage = /^\/bounty\.php\/?$/i.test(path);
    const isMyUploadsPage = isUploadSiteOrigin(location.origin);
    let table = document.querySelector(
        'table.itg.gltc, table.itg.gltm, table.itg.glte, div.itg.gld'
    );
    const isList = !!table;
    const followingFeedMode = path.replace(/\/+$/, '') === '/watched'
        ? new URLSearchParams(location.search).get('ehtw_following')
        : null;
    const isFollowingAuthorsPage = followingFeedMode === 'authors';
    const isFollowingUploadersPage = followingFeedMode === 'uploaders';
    const isFollowingFeedPage = isFollowingAuthorsPage || isFollowingUploadersPage;

    // 其它列表显示模式只应用主题与三栏布局。
    const isOtherList =
        !isList &&
        !isDetail &&
        !!document.querySelector('#searchbox form') &&
        !!document.querySelector(
            'table.itg, .itg, .searchnav'
        );

    // 其余带原站导航的页面用通用布局;登录页只换主题,不建三栏。
    const isGeneric =
        !isList &&
        !isDetail &&
        !isOtherList &&
        !!document.querySelector('#nb');

    const MY_HOME_PATH_PATTERN = /^\/(?:home\.php|mytags\/?|uconfig\.php|stats\.php|hentaiathome\.php|bitcoin\.php|hathperks\.php|exchange\.php|logs\.php)$/i;
    const isMyHomePage = MY_HOME_PATH_PATTERN.test(path);
    const isMyTagsPage = /^\/mytags\/?$/i.test(path);

    const isLogin =
        !isGeneric &&
        !!document.querySelector('form[name="ipb_login_form"]');

    if (!isList && !isDetail && !isOtherList && !isGeneric && !isLogin) {
        releasePageBoot();
        return;
    }

    // ============================================================
    // 0.5.0:事务状态全部放在 try 外层
    //
    // catch 必须能够直接访问 rollback 及其依赖的状态。
    // 不依赖严格模式下 try 块内部的函数声明。
    // ============================================================

    let styleElement = null;
    let shell = null;
    let committed = false;
    let rollbackStarted = false;
    let listInfiniteObserver = null;
    let listInfiniteScrollFallback = null;
    let startListInfiniteContinuation = null;
    let startGalleryStripContinuation = null;
    let startFollowingAuthorTimeline = null;
    let startFollowingAuthorContinuation = null;
    let registerAdditionalStatsTweets = null;

    const moves = [];
    const modifications = [];
    const mediaListeners = [];
    const domListeners = [];
    const createdNodes = [];
    let skipTransactionTracking = false;

    let rollback = null;

    // 保存节点初始位置。
    // nextSibling 是原始 DOM 中的后继节点,不是复制节点。
    function rememberMove(node) {
        if (!node) return;

        moves.push({
            node,
            parent: node.parentNode,
            next: node.nextSibling
        });
    }

    function registerCreatedNode(node) {
        if (node) createdNodes.push(node);
        return node;
    }

    function registerMediaListener(mediaQuery, callback) {
        if (!mediaQuery || !callback) return;

        mediaQuery.addEventListener('change', callback);

        mediaListeners.push({
            mediaQuery,
            callback
        });
    }

    // ============================================================
    // 0.5.0:回滚函数在 try 之前定义
    // ============================================================

        rollback = function () {
            if (rollbackStarted) return;

            rollbackStarted = true;
            committed = false;

            try {
                listInfiniteObserver?.disconnect();
                listInfiniteObserver = null;
                if (listInfiniteScrollFallback) {
                    window.removeEventListener('scroll', listInfiniteScrollFallback);
                    listInfiniteScrollFallback = null;
                }
            } catch (_) {}

            // 先停止响应式监听,避免回滚期间再次搬移节点。
            for (let i = mediaListeners.length - 1; i >= 0; i--) {
                const item = mediaListeners[i];

                try {
                    item.mediaQuery.removeEventListener(
                        'change',
                        item.callback
                    );
                } catch (error) {
                    console.warn(
                        '[EH Twitter Style] 监听器清理失败',
                        error
                    );
                }
            }

            for (let i = domListeners.length - 1; i >= 0; i--) {
                const item = domListeners[i];
                try {
                    item.target.removeEventListener(
                        item.type,
                        item.listener,
                        item.options
                    );
                    item.cleanup?.();
                } catch (_) {}
            }

            // 必须先恢复原节点,再移除任何新建容器。
            // 包括导航原文字、#f_search、分类控件和分页。
            for (let i = moves.length - 1; i >= 0; i--) {
                const record = moves[i];

                try {
                    const { node, parent, next } = record;

                    if (!node || !parent) continue;

                    if (next && next.parentNode === parent) {
                        parent.insertBefore(node, next);
                    } else {
                        parent.appendChild(node);
                    }
                } catch (error) {
                    console.warn(
                        '[EH Twitter Style] 节点恢复失败',
                        error
                    );
                }
            }

            // 恢复原站节点的 class、事件属性及内联样式。
            for (let i = modifications.length - 1; i >= 0; i--) {
                const item = modifications[i];

                try {
                    if (item.type === 'attribute') {
                        if (item.existed) {
                            item.element.setAttribute(
                                item.name,
                                item.oldValue
                            );
                        } else {
                            item.element.removeAttribute(item.name);
                        }
                    } else if (item.type === 'style') {
                        if (item.oldValue) {
                            item.element.style.setProperty(
                                item.property,
                                item.oldValue,
                                item.oldPriority
                            );
                        } else {
                            item.element.style.removeProperty(
                                item.property
                            );
                        }
                    }
                } catch (error) {
                    console.warn(
                        '[EH Twitter Style] 属性恢复失败',
                        error
                    );
                }
            }

            // 即使某个新节点嵌入了原站 DOM,也必须将其移除。
            for (let i = createdNodes.length - 1; i >= 0; i--) {
                try {
                    createdNodes[i]?.remove();
                } catch (error) {
                    console.warn(
                        '[EH Twitter Style] 新建节点清理失败',
                        error
                    );
                }
            }

            try {
                shell?.remove();
            } catch (_) {}

            try {
                styleElement?.remove();
            } catch (_) {}

            try {
                document.documentElement.classList.remove(
                    'ehtw-enabled',
                    'ehtw-exhentai',
                    'ehtw-list',
                    'ehtw-detail',
                    'ehtw-myhome',
                    'ehtw-mytags',
                    'ehtw-myuploads',
                    'ehtw-bounty',
                    'ehtw-other',
                    'ehtw-generic',
                    'ehtw-login'
                );
            } catch (_) {}

            try {
                delete window[RUN_KEY];
            } catch (_) {
                window[RUN_KEY] = false;
            }

            console.info(
                '[EH Twitter Style] Rollback completed'
            );
        };

    try {
        window[RUN_KEY] = true;

        // ============================================================
        // 1. 基础工具
        // ============================================================

        function qs(selector, root = document) {
            try {
                return root?.querySelector(selector) || null;
            } catch (_) {
                return null;
            }
        }

        function qsa(selector, root = document) {
            try {
                return Array.from(
                    root?.querySelectorAll(selector) || []
                );
            } catch (_) {
                return [];
            }
        }

        function create(tag, className, text) {
            const el = document.createElement(tag);

            if (className) {
                el.className = className;
            }

            if (text !== undefined && text !== null) {
                el.textContent = String(text);
            }

            return el;
        }

        function textOf(node, fallback = '') {
            const value = node?.textContent?.trim();
            return value || fallback;
        }

        function append(parent, child) {
            if (parent && child) {
                parent.appendChild(child);
            }

            return child;
        }

        function setAttributeSafe(element, name, value) {
            if (!element) return;

        if (!skipTransactionTracking) {
            modifications.push({
                type: 'attribute',
                element,
                name,
                existed: element.hasAttribute(name),
                oldValue: element.getAttribute(name)
            });
        }

            element.setAttribute(name, String(value));
        }

        function addClassSafe(element, className) {
            if (!element || !className) return;

            setAttributeSafe(
                element,
                'class',
                [
                    element.getAttribute('class') || '',
                    className
                ].filter(Boolean).join(' ')
            );
        }

        function setStyleSafe(element, property, value) {
            if (!element) return;

        if (!skipTransactionTracking) {
            modifications.push({
                type: 'style',
                element,
                property,
                oldValue:
                    element.style.getPropertyValue(property),
                oldPriority:
                    element.style.getPropertyPriority(property)
            });
        }

            element.style.setProperty(property, value);
        }

        // 搬移真实 DOM 节点,保留 ID、事件和全部子节点。
        function move(node, target) {
            if (!node || !target || node === target) {
                return null;
            }

            if (node.contains(target)) {
                return null;
            }

            if (!skipTransactionTracking) rememberMove(node);
            target.appendChild(node);

            return node;
        }

        function extractBackgroundImage(element) {
            if (!element) return '';

            const inline = element.style.backgroundImage;

            if (inline && inline !== 'none') {
                return inline;
            }

            try {
                const computed =
                    getComputedStyle(element).backgroundImage;

                return computed !== 'none' ? computed : '';
            } catch (_) {
                return '';
            }
        }

        // 兼容原生 src 和 data-src 懒加载。
        function prepareImage(img) {
            if (!img) return;

            const src = img.getAttribute('src') || '';
            const dataSrc = img.getAttribute('data-src') || '';

            const placeholder =
                !src ||
                src === '#' ||
                src.startsWith(
                    'data:image/gif;base64,R0lGOD'
                );

            if (dataSrc && placeholder) {
                setAttributeSafe(img, 'src', dataSrc);
            }

            setAttributeSafe(img, 'loading', 'lazy');
            setAttributeSafe(img, 'decoding', 'async');
        }

        function makeAvatar(categoryName, categoryClass) {
            const avatar = create('div', 'ehtw-avatar');

            const validClass = /^ct(?:[1-9]|a)$/.test(
                categoryClass || ''
            ) ? categoryClass : 'ct1';

            avatar.classList.add(validClass);

            const letter = Array.from(categoryName || '?')[0] || '?';

            append(
                avatar,
                create(
                    'span',
                    'ehtw-avatar-letter',
                    letter.toUpperCase()
                )
            );

            // 颜色延后读取,避免原站样式尚未计算完成。
            const source = document.querySelector(
                '.' + validClass + ':not(.ehtw-avatar)'
            );

            if (source) {
                const color = getComputedStyle(source).borderColor;

                if (color && color !== 'transparent') {
                    avatar.style.setProperty(
                        '--ehtw-avatar-solid',
                        color
                    );
                }
            }

            return avatar;
        }

        function makeSectionTitle(text) {
            return create(
                'h2',
                'ehtw-section-title',
                text
            );
        }

        function makeEmptyHint(text) {
            return create('div', 'ehtw-empty', text);
        }

        function makeIcon(kind) {
            const paths = {
                home: [
                    'M3 10.5 12 3l9 7.5',
                    'M5 9.5V21h5v-7h4v7h5V9.5'
                ],
                eye: [
                    'M2 12s3.7-7 10-7 10 7 10 7-3.7 7-10 7S2 12 2 12Z',
                    'M12 9a3 3 0 1 0 0 6 3 3 0 0 0 0-6Z'
                ],
                following: [
                    'M16 21v-2c0-2.5-1.8-4-4.5-4',
                    'M8 21v-2c0-3.5 2.2-5.5 5.5-5.5',
                    'M9 12a4 4 0 1 0 0-8 4 4 0 0 0 0 8Z',
                    'M17 8a3 3 0 1 1-2.5 4.7'
                ],
                flame: [
                    'M12 22c-4.5 0-8-3.3-8-7.5 0-3.4 2-5.4 4-7.7.1 2.4 1.2 3.2 2 3.7C10 6.2 12.6 3.4 14 2c-.1 4 2.1 5.8 3.7 7.8C19.1 11.5 20 13 20 15c0 4-3.5 7-8 7Z',
                    'M12 22c-2.2 0-4-1.6-4-3.7 0-1.8 1.1-3 2.5-4.6.1 1.3.8 1.8 1.4 2.2.5-1.5 1.5-2.4 2-3.2.2 1.8 2.1 3.3 2.1 5.5 0 2.2-1.7 3.8-4 3.8Z'
                ],
                download: [
                    'M12 3v12',
                    'm7 10 5 5 5-5',
                    'M4 18v3h16v-3'
                ],
                bookmark: [
                    'M6 3h12v18l-6-4-6 4V3Z'
                ],
                user: [
                    'M12 12a4.5 4.5 0 1 0 0-9 4.5 4.5 0 0 0 0 9Z',
                    'M3.5 21v-2c0-3.5 3.2-5.5 8.5-5.5s8.5 2 8.5 5.5v2'
                ],
                settings: [
                    'M12 8a4 4 0 1 0 0 8 4 4 0 0 0 0-8Z',
                    'M19.4 13.5a7.8 7.8 0 0 0 0-3l1.7-1.3-1.7-3-2 .7a7.8 7.8 0 0 0-2.6-1.5L14.5 3h-5l-.4 2.4a7.8 7.8 0 0 0-2.6 1.5l-2-.7-1.7 3 1.7 1.3a7.8 7.8 0 0 0 0 3l-1.7 1.3 1.7 3 2-.7a7.8 7.8 0 0 0 2.6 1.5l.4 2.4h5l.4-2.4a7.8 7.8 0 0 0 2.6-1.5l2 .7 1.7-3-1.8-1.3Z'
                ],
                tag: [
                    'M20.6 13.4 13.4 20.6a2 2 0 0 1-2.8 0L3 13V3h10l7.6 7.6a2 2 0 0 1 0 2.8Z',
                    'M7.5 7.5h.01'
                ],
                login: [
                    'M14 3h5a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-5',
                    'm10 17 5-5-5-5',
                    'M15 12H3'
                ],
                logout: [
                    'M10 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h5',
                    'm14 17-5-5 5-5',
                    'M9 12h12'
                ],
                upload: [
                    'M12 16V3',
                    'm7 8 5-5 5 5',
                    'M4 17v4h16v-4'
                ],
                list: [
                    'M8 6h13',
                    'M8 12h13',
                    'M8 18h13',
                    'M3 6h1',
                    'M3 12h1',
                    'M3 18h1'
                ],
                gift: [
                    'M3 10h18v11H3V10Z',
                    'M2 7h20v3H2V7Z',
                    'M12 7v14',
                    'M12 7C5 7 5 2 8 2c2 0 4 3 4 5Z',
                    'M12 7c7 0 7-5 4-5-2 0-4 3-4 5Z'
                ],
                news: [
                    'M4 3h16v18H4V3Z',
                    'M8 7h8',
                    'M8 11h8',
                    'M8 15h4',
                    'M15 15h2v3h-2z'
                ],
                chat: [
                    'M4 4h16v12H9l-5 4V4Z',
                    'M8 9h8',
                    'M8 12h5'
                ],
                book: [
                    'M12 6C9 3.5 5.5 3 2 4v15c3.5-1 7-.5 10 2',
                    'M12 6c3-2.5 6.5-3 10-2v15c-3.5-1-7-.5-10 2V6Z'
                ],
                globe: [
                    'M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20Z',
                    'M2 12h20',
                    'M12 2c-3 3-4.5 6.3-4.5 10S9 19 12 22',
                    'M12 2c3 3 4.5 6.3 4.5 10S15 19 12 22'
                ],
                more: [
                    'M5 10.25a1.75 1.75 0 1 0 0 3.5 1.75 1.75 0 0 0 0-3.5Z',
                    'M12 10.25a1.75 1.75 0 1 0 0 3.5 1.75 1.75 0 0 0 0-3.5Z',
                    'M19 10.25a1.75 1.75 0 1 0 0 3.5 1.75 1.75 0 0 0 0-3.5Z'
                ],
                moreCircle: [
                    'M12 2.5a9.5 9.5 0 1 0 0 19 9.5 9.5 0 0 0 0-19Z',
                    'M7.5 10.5a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3Z',
                    'M12 10.5a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3Z',
                    'M16.5 10.5a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3Z'
                ],
                search: [
                    'M10.8 3.3a7.5 7.5 0 1 0 0 15 7.5 7.5 0 0 0 0-15Z',
                    'm16.5 16.5 5 5'
                ],
                filter: [
                    'M3 6h18',
                    'M6 12h12',
                    'M9 18h6'
                ],
                reply: [
                    'M21 11.5c0 4.7-4 8.5-9 8.5-1.3 0-2.7-.3-3.8-.8L3 21l1.7-4.8A8.3 8.3 0 0 1 3 11.5C3 6.8 7 3 12 3s9 3.8 9 8.5Z'
                ],
                repost: [
                    'M4 9V6a3 3 0 0 1 3-3h10l3 3',
                    'm16 3 4 3-4 3',
                    'M20 15v3a3 3 0 0 1-3 3H7l-3-3',
                    'm8 21-4-3 4-3'
                ],
                heart: [
                    'M12 21 3.8 12.8a5.6 5.6 0 0 1 7.9-7.9l.3.4.3-.4a5.6 5.6 0 0 1 7.9 7.9L12 21Z'
                ],
                views: [
                    'M4 21V13',
                    'M9 21V9',
                    'M14 21V14',
                    'M19 21V4'
                ],
                share: [
                    'M12 16V3',
                    'm7 8 5-5 5 5',
                    'M4 14v7h16v-7'
                ],
                back: [
                    'M20 12H4',
                    'm11 5-7 7 7 7'
                ]
            };

            const ns = 'http://www.w3.org/2000/svg';
            const svg = document.createElementNS(ns, 'svg');

            svg.setAttribute('viewBox', '0 0 24 24');
            svg.setAttribute('fill', 'currentColor');
            svg.setAttribute('aria-hidden', 'true');
            svg.setAttribute('focusable', 'false');
            svg.setAttribute('class', 'ehtw-svg-icon');
            svg.setAttribute('width', '24');
            svg.setAttribute('height', '24');

            const active = kind.endsWith('-filled');
            const baseKind = active ? kind.slice(0, -7) : kind;

            if (active && baseKind === 'home') {
                const path = document.createElementNS(ns, 'path');

                path.setAttribute(
                    'd',
                    'M12 2.2 1.8 10.7l1.4 1.7L5 10.9V21h5.3v-7h3.4v7H19V10.9l1.8 1.5 1.4-1.7L12 2.2Z'
                );
                path.setAttribute('fill', 'currentColor');
                svg.appendChild(path);

                return svg;
            }

            for (const d of paths[baseKind] || paths.more) {
                const path = document.createElementNS(ns, 'path');

                path.setAttribute('d', d);

                if (baseKind === 'more' || baseKind === 'moreCircle') {
                    // 三个点为实心;moreCircle 的外圈保持描边。
                    if (baseKind === 'moreCircle' && d === paths.moreCircle[0]) {
                        path.setAttribute('fill', 'none');
                        path.setAttribute('stroke', 'currentColor');
                        path.setAttribute('stroke-width', active ? '2.6' : '1.8');
                    } else {
                        path.setAttribute('fill', 'currentColor');
                        path.setAttribute('stroke', 'none');
                    }
                } else {
                    path.setAttribute('fill', 'none');
                    path.setAttribute('stroke', 'currentColor');
                    path.setAttribute(
                        'stroke-width',
                        active ? '2.6' : '1.8'
                    );
                }

                path.setAttribute('stroke-linecap', 'round');
                path.setAttribute('stroke-linejoin', 'round');

                svg.appendChild(path);
            }

            return svg;
        }

        function makeAction(label, kind) {
            const item = create('div', 'ehtw-action');

            append(item, makeIcon(kind));

            if (label) {
                append(
                    item,
                    create(
                        'span',
                        'ehtw-action-label',
                        label
                    )
                );
            }

            return item;
        }

        // ============================================================
        // 2. CSS 主题
        // ============================================================

        const CSS = `
/* ============================================================
   EH Twitter Style 0.5.0
   ============================================================ */

html.ehtw-enabled {
    --ehtw-bg: #ffffff;
    --ehtw-text: #0f1419;
    --ehtw-muted: #536471;
    --ehtw-border: #eff3f4;
    --ehtw-accent: #1d9bf0;
    --ehtw-hover: rgba(15, 20, 25, 0.035);
    --ehtw-nav-hover: rgba(15, 20, 25, 0.09);
    --ehtw-card: #f7f9f9;
    --ehtw-input: #eff3f4;
    --ehtw-glass: rgba(255, 255, 255, 0.87);
    --ehtw-media: #f0f2f3;
    --ehtw-shadow: rgba(0, 0, 0, 0.08);

    color-scheme: light;
    background: var(--ehtw-bg) !important;
}

@media (prefers-color-scheme: dark) {
    html.ehtw-enabled {
        --ehtw-bg: #000000;
        --ehtw-text: #e7e9ea;
        --ehtw-muted: #71767b;
        --ehtw-border: #2f3336;
        --ehtw-accent: #1d9bf0;
        --ehtw-hover: rgba(231, 233, 234, 0.045);
        --ehtw-nav-hover: rgba(231, 233, 234, 0.11);
        --ehtw-card: #16181c;
        --ehtw-input: #202327;
        --ehtw-glass: rgba(0, 0, 0, 0.86);
        --ehtw-media: #16181c;
        --ehtw-shadow: rgba(255, 255, 255, 0.05);

        color-scheme: dark;
    }
}

html.ehtw-enabled,
html.ehtw-enabled body {
    margin: 0 !important;
    padding: 0 !important;
    width: 100% !important;
    max-width: 100% !important;
    min-width: 0 !important;
    background: var(--ehtw-bg) !important;
    color: var(--ehtw-text) !important;
    font-family:
        -apple-system,
        "Segoe UI",
        Roboto,
        "Helvetica Neue",
        sans-serif !important;
    font-size: 15px;
}

html.ehtw-enabled {
    overflow-x: clip !important;
}

html.ehtw-enabled body {
    overflow-x: clip !important;
}

html.ehtw-enabled * {
    box-sizing: border-box;
}

html.ehtw-enabled div.ido {
    min-width: 0 !important;
    max-width: 100% !important;
    width: auto !important;
}

html.ehtw-enabled #toppane {
    min-width: 0 !important;
    max-width: 100% !important;
    width: auto !important;
}

html.ehtw-enabled #cdiv {
    min-width: 0 !important;
    max-width: 100% !important;
}

html.ehtw-enabled .gm {
    min-width: 0 !important;
    max-width: 100% !important;
}

html.ehtw-enabled #gmid,
html.ehtw-enabled #gd2,
html.ehtw-enabled #gd3,
html.ehtw-enabled #gd4,
html.ehtw-enabled #gd5,
html.ehtw-enabled #gdt {
    min-width: 0 !important;
    max-width: 100% !important;
}

/* 只处理 body 下已经搬空的原站 .gm 空壳。 */

html.ehtw-detail body > .gm {
    display: block !important;
    position: static !important;
    float: none !important;

    width: 0 !important;
    min-width: 0 !important;
    max-width: 0 !important;

    height: 0 !important;
    min-height: 0 !important;
    max-height: 0 !important;

    margin: 0 !important;
    padding: 0 !important;

    border: 0 !important;
    border-width: 0 !important;
    border-style: none !important;
    border-color: transparent !important;

    background: transparent !important;
    background-image: none !important;

    box-shadow: none !important;
    outline: none !important;

    overflow: hidden !important;
    visibility: hidden !important;
}

html.ehtw-enabled a {
    color: inherit;
}

html.ehtw-enabled a:hover {
    color: var(--ehtw-accent);
}

html.ehtw-enabled input,
html.ehtw-enabled select,
html.ehtw-enabled textarea,
html.ehtw-enabled button {
    font-family: inherit;
}

html.ehtw-enabled ins[data-width][data-height],
html.ehtw-enabled .ehtw-hidden-ad {
    display: none !important;
}

/* ============================================================
   三栏布局
   ============================================================ */

#ehtw-shell {
    width: min(100%, 1225px);
    max-width: 100%;
    min-width: 0;
    min-height: 100vh;

    margin: 0 auto;

    display: grid;
    grid-template-columns: 275px minmax(0, 600px) 350px;
    align-items: start;

    background: var(--ehtw-bg);
}

#ehtw-left {
    width: 275px;
    min-width: 0;
    max-width: 100%;

    min-height: 0;
    max-height: 100vh;

    position: sticky;
    top: 0;
    align-self: start;

    padding: 16px 12px 24px 8px;

    overflow-y: auto;
    overflow-x: hidden;

    scrollbar-width: none;
    -ms-overflow-style: none;

    overscroll-behavior-y: contain;
}

#ehtw-left::-webkit-scrollbar {
    display: none;
    width: 0;
    height: 0;
}

#ehtw-center {
    min-width: 0;
    max-width: 100%;
    min-height: 100vh;

    border-left: 1px solid var(--ehtw-border);
    border-right: 1px solid var(--ehtw-border);
}

#ehtw-right {
    width: 350px;
    min-width: 0;
    max-width: 100%;

    padding: 8px 24px 32px 24px !important;

    position: sticky;
    top: 0;
    align-self: start;

    max-height: 100vh;

    overflow-y: auto;
    overflow-x: hidden;

    scrollbar-width: thin;
}

/* ============================================================
   左侧导航
   ============================================================ */

#ehtw-brand {
    display: flex;
    align-items: center;
    gap: 12px;

    height: 64px;
    padding: 0 14px;
    margin-bottom: 8px;

    font-size: 24px;
    font-weight: 800;
    letter-spacing: -1px;
}

.ehtw-brand-symbol {
    width: 42px;
    height: 42px;

    display: grid;
    place-items: center;

    border-radius: 50%;

    background: var(--ehtw-text);
    color: var(--ehtw-bg);

    font-weight: 900;
    font-size: 26px;
}

#ehtw-left #nb {
    position: static !important;
    float: none !important;

    width: 100% !important;
    min-width: 0 !important;
    max-width: none !important;

    height: auto !important;
    min-height: 0 !important;
    max-height: none !important;

    padding: 0 !important;
    margin: 0 !important;

    border: 0 !important;
    background: transparent !important;

    display: flex !important;
    flex-direction: column !important;
    flex-wrap: nowrap !important;
    align-items: flex-start;

    gap: 2px;

    white-space: normal !important;
    overflow: visible !important;
}

#ehtw-left #nb > div {
    display: block !important;

    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;

    height: auto !important;
    min-height: 0 !important;
    max-height: none !important;

    margin: 0 !important;
    padding: 0 !important;

    float: none !important;

    background: transparent !important;
    border: none !important;

    white-space: normal !important;
    overflow-wrap: anywhere;
}

#ehtw-left #nb a {
    position: relative;

    width: fit-content;
    max-width: 100%;
    min-height: 49px;

    display: flex !important;
    align-items: center;

    border-radius: 9999px;

    padding: 11px 17px !important;
    margin: 0 !important;

    text-decoration: none !important;
    font-size: 19px !important;
    font-weight: 500;
    line-height: 1.35;

    color: var(--ehtw-text) !important;
    background: transparent !important;
    border: none !important;

    white-space: normal !important;
    overflow-wrap: anywhere;

    transition: background 0.15s ease;
}

#ehtw-left #nb a:hover {
    background: var(--ehtw-nav-hover) !important;
    color: var(--ehtw-text) !important;
}

.ehtw-nav-hint {
    margin-top: 18px;
    padding: 12px 18px;

    color: var(--ehtw-muted);
    font-size: 12px;
}

/* ============================================================
   中栏标题
   ============================================================ */

#ehtw-header {
    position: sticky;
    top: 0;
    z-index: 100;

    min-height: 58px;
    min-width: 0;

    display: flex;
    align-items: center;

    padding: 8px 16px;

    background: var(--ehtw-glass);

    backdrop-filter: blur(14px);
    -webkit-backdrop-filter: blur(14px);

    border-bottom: 1px solid var(--ehtw-border);

    text-align: left !important;
}

#ehtw-header > div {
    min-width: 0;
    width: 100%;
    text-align: left !important;
}

#ehtw-header-title {
    display: block;

    width: 100%;
    min-width: 0;
    max-width: 100%;

    margin: 0;

    font-size: 20px !important;
    line-height: 1.3;
    font-weight: 800 !important;

    color: var(--ehtw-text);

    text-align: left !important;

    white-space: nowrap !important;
    overflow: hidden !important;
    text-overflow: ellipsis !important;
}

#ehtw-header-subtitle {
    margin-top: 3px;

    font-size: 12px;
    color: var(--ehtw-muted);

    text-align: left !important;
}

.ehtw-timeline {
    min-width: 0;
    max-width: 100%;
    width: 100%;
}

.ehtw-toplist-filter-pending {
    visibility: hidden !important;
}

/* ============================================================
   搜索框
   ============================================================ */

#ehtw-search-card {
    margin-bottom: 16px;
    min-width: 0;
    max-width: 100%;
}

.ehtw-search-toplists {
    display: grid;
    gap: 12px;
    margin-top: 12px;
    padding-top: 10px;
    border-top: 1px solid var(--ehtw-border);
}

.ehtw-search-toplists:empty {
    display: none !important;
}

.ehtw-search-toplist-section {
    min-width: 0;
}

.ehtw-search-toplist-heading {
    display: block;
    margin-bottom: 4px;
    color: var(--ehtw-accent) !important;
    font-size: 13px;
    font-weight: 700;
    line-height: 1.2;
    text-decoration: none !important;
}

.ehtw-search-toplist-status {
    color: var(--ehtw-muted);
    font-size: 11px;
}

.ehtw-search-toplist-status:empty {
    display: none;
}

.ehtw-search-toplist-list {
    margin: 0;
    padding: 0;
    list-style: none;
}

.ehtw-search-toplist-item {
    display: grid;
    grid-template-columns: 18px 44px minmax(0, 1fr);
    gap: 6px;
    align-items: center;
    min-height: 62px;
    padding: 4px 0;
}

.ehtw-search-toplist-more > summary {
    padding: 4px 0 2px 24px;
    color: var(--ehtw-muted);
    font-size: 11px;
    cursor: pointer;
    list-style-position: inside;
}

.ehtw-search-toplist-more {
    display: flex;
    flex-direction: column;
}

.ehtw-search-toplist-more > .ehtw-search-toplist-extra {
    order: 1;
}

.ehtw-search-toplist-more > summary {
    order: 2;
}

.ehtw-search-toplist-extra {
    margin-top: 2px !important;
}

.ehtw-search-toplist-rank {
    color: var(--ehtw-muted);
    font-size: 11px;
    text-align: right;
}

.ehtw-search-toplist-cover {
    display: block;
    width: 44px;
    height: 58px;
    overflow: hidden;
    border-radius: 4px;
    background: var(--ehtw-surface);
}

.ehtw-search-toplist-cover img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.ehtw-search-toplist-title {
    min-width: 0;
    color: var(--ehtw-text) !important;
    font-size: 11px;
    line-height: 1.3;
    text-decoration: none !important;
}

.ehtw-search-toplist-title:hover {
    color: var(--ehtw-accent) !important;
}

.ehtw-search-toplist-heading:hover {
    text-decoration: underline !important;
}

#ehtw-search-card #searchbox,
#ehtw-search-card #searchbox.idi,
#ehtw-mobile-search #searchbox,
#ehtw-mobile-search #searchbox.idi {
    position: static !important;

    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;

    height: auto !important;

    display: block !important;

    margin: 0 !important;
    padding: 0 !important;

    background: transparent !important;
    border: none !important;

    float: none !important;
    overflow: visible !important;

    text-align: left !important;
}

/* 分类筛选使用原始节点,允许自动换行。 */

#ehtw-search-card table.itc,
#ehtw-mobile-search table.itc {
    display: flex !important;

    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;

    height: auto !important;

    flex-direction: column;
    flex-wrap: wrap !important;

    margin: 0 !important;
    padding: 0 !important;

    background: transparent !important;
    border: none !important;

    text-align: left !important;
}

#ehtw-search-card table.itc > tbody,
#ehtw-mobile-search table.itc > tbody {
    display: flex !important;

    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;

    flex-direction: row !important;
    flex-wrap: wrap !important;

    gap: 4px;

    background: transparent !important;
}

#ehtw-search-card table.itc tr,
#ehtw-mobile-search table.itc tr {
    display: flex !important;

    width: auto !important;
    min-width: 0 !important;
    max-width: 100% !important;

    height: auto !important;

    flex-wrap: wrap !important;
    align-items: center;

    gap: 4px;

    margin: 0 !important;
    padding: 0 !important;

    background: transparent !important;
}

#ehtw-search-card table.itc td,
#ehtw-mobile-search table.itc td {
    display: flex !important;

    width: auto !important;
    min-width: 0 !important;
    max-width: 100% !important;

    height: auto !important;

    align-items: center;

    margin: 0 !important;
    padding: 2px !important;

    background: transparent !important;
    border: none !important;

    white-space: normal !important;
}

#ehtw-search-card table.itc .cs,
#ehtw-search-card table.itc .cn,
#ehtw-mobile-search table.itc .cs,
#ehtw-mobile-search table.itc .cn {
    position: static !important;
    float: none !important;

    width: auto !important;
    min-width: 0 !important;
    max-width: 100% !important;

    height: auto !important;

    margin: 0 !important;
    padding: 5px 8px !important;

    border-radius: 9999px;

    white-space: normal !important;
    overflow-wrap: anywhere;

    cursor: pointer;
}

#ehtw-search-card form,
#ehtw-mobile-search form {
    width: 100%;
    min-width: 0;
    max-width: 100%;

    display: flex;
    flex-wrap: wrap;
    align-items: center;

    gap: 6px;

    margin: 0;
    padding: 0;

    border: none !important;
    border-radius: 0;

    background: transparent !important;
    color: var(--ehtw-text);

    text-align: left !important;
}

/* 搜索输入胶囊。 */

#ehtw-search-card #f_search,
#ehtw-mobile-search #f_search {
    display: block !important;

    flex: 1 1 100%;

    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;

    height: 44px !important;
    min-height: 44px !important;

    padding: 0 16px !important;
    margin: 0 0 8px !important;

    border: 1px solid transparent !important;
    border-radius: 9999px !important;

    outline: none !important;
    box-shadow: none !important;

    background: var(--ehtw-input) !important;
    color: var(--ehtw-text) !important;

    font-size: 14px !important;
    line-height: normal !important;
    text-align: left !important;

    transition:
        border-color 0.15s ease,
        background-color 0.15s ease;
}

#ehtw-search-card #f_search::placeholder,
#ehtw-mobile-search #f_search::placeholder {
    color: var(--ehtw-muted) !important;
    opacity: 1;
}

#ehtw-search-card #f_search:focus,
#ehtw-mobile-search #f_search:focus {
    border: 1px solid var(--ehtw-accent) !important;

    background: var(--ehtw-bg) !important;

    outline: none !important;
    box-shadow: none !important;
}

#ehtw-search-card input:not(#f_search),
#ehtw-search-card select,
#ehtw-search-card button,
#ehtw-mobile-search input:not(#f_search),
#ehtw-mobile-search select,
#ehtw-mobile-search button {
    max-width: 100%;
    min-width: 0;

    color: var(--ehtw-text);
    background: var(--ehtw-bg);

    border: 1px solid var(--ehtw-border);
    border-radius: 12px;

    padding: 5px 8px;
    font-size: 12px;
}

#ehtw-search-card input[type="checkbox"],
#ehtw-search-card input[type="radio"],
#ehtw-mobile-search input[type="checkbox"],
#ehtw-mobile-search input[type="radio"] {
    width: auto;
    padding: 0;
}

#ehtw-search-card label,
#ehtw-mobile-search label {
    color: var(--ehtw-muted);
    font-size: 12px;
}

/* ============================================================
   右栏卡片
   ============================================================ */

.ehtw-side-card {
    overflow: hidden;

    width: 100%;
    min-width: 0;
    max-width: 100%;

    margin-bottom: 16px;
    padding: 14px 0;

    border-radius: 16px;

    background: var(--ehtw-card);
    color: var(--ehtw-text);

    text-align: left !important;
}

.ehtw-side-card * {
    text-align: left;
}

.ehtw-side-title {
    margin: 0 16px 12px;

    font-size: 20px;
    font-weight: 800;
    line-height: 1.3;

    text-align: left !important;
}

.ehtw-trend {
    display: block;
    padding: 8px 16px;

    text-decoration: none !important;
    color: var(--ehtw-text) !important;

    text-align: left !important;
    transition: background 0.15s ease;
}

.ehtw-trend:hover {
    background: var(--ehtw-nav-hover);
    color: var(--ehtw-text) !important;
}

.ehtw-trend-category {
    display: block;

    font-size: 11px;
    line-height: 1.25;

    color: var(--ehtw-muted);
}

.ehtw-trend-name {
    display: block;

    font-size: 15px;
    font-weight: 700;
    line-height: 1.3;

    overflow-wrap: anywhere;
    padding: 2px 0;
}

.ehtw-trend-count {
    font-size: 11px;
    line-height: 1.25;
    color: var(--ehtw-muted);
}

/* ============================================================
   列表分页
   ============================================================ */

.ehtw-pagination {
    width: 100%;
    min-width: 0;
    max-width: 100%;

    border-bottom: 1px solid var(--ehtw-border);
    border-top: 1px solid var(--ehtw-border);

    padding: 10px 12px;
}

html.ehtw-list .ehtw-list-pagination-hidden {
    display: none !important;
}

html.ehtw-list .ehtw-list-infinite-status {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 56px;
    padding: 12px 16px;
    color: var(--ehtw-muted);
    font-size: 13px;
    text-align: center;
}

html.ehtw-list .ehtw-list-infinite-status button {
    padding: 8px 14px;
    border: 1px solid var(--ehtw-border);
    border-radius: 999px;
    background: var(--ehtw-bg);
    color: var(--ehtw-text);
    cursor: pointer;
}

.ehtw-pagination .searchnav {
    position: static !important;
    float: none !important;

    display: flex !important;
    flex-wrap: wrap;

    justify-content: center;
    align-items: center;

    gap: 5px;

    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;

    height: auto !important;
    min-height: 36px;

    padding: 0 !important;
    margin: 0 !important;

    border: none !important;
    background: transparent !important;

    overflow: visible !important;
}

.ehtw-pagination .searchnav > div {
    position: static !important;
    float: none !important;

    width: auto !important;
    min-width: 0;
    max-width: 100%;

    height: auto !important;

    margin: 0 !important;
    padding: 0 !important;

    border: none !important;
    background: transparent !important;

    line-height: normal !important;
}

.ehtw-pagination .searchnav a,
.ehtw-pagination .searchnav span[id],
.ehtw-pagination .searchnav select {
    display: inline-flex;

    align-items: center;
    justify-content: center;

    min-height: 34px;
    padding: 6px 10px;

    border-radius: 9999px;
    border: 1px solid var(--ehtw-border);

    color: var(--ehtw-text) !important;
    background: var(--ehtw-bg) !important;

    font-size: 12px;
    text-decoration: none !important;
}

.ehtw-pagination .searchnav a:hover,
.ehtw-pagination .searchnav select:hover {
    background: var(--ehtw-nav-hover) !important;
}

.ehtw-pagination .searchnav select {
    cursor: pointer;
    max-width: 160px;
}

.ehtw-pagination .searchnav .jumpbox {
    position: relative;
}

.ehtw-pagination .searchnav span[id] {
    opacity: 0.45;
}

/* ============================================================
   推文列表
   ============================================================ */

.ehtw-tweet {
    position: relative;

    display: flex;
    gap: 12px;

    width: 100%;
    min-width: 0;
    max-width: 100%;

    padding: 14px 16px 16px;

    border-bottom: 1px solid var(--ehtw-border);

    background: var(--ehtw-bg);
    color: var(--ehtw-text);

    transition: background 0.12s ease;
}

.ehtw-tweet:hover {
    background: var(--ehtw-hover);
}

.ehtw-avatar {
    flex: 0 0 48px;

    width: 48px;
    height: 48px;

    border-radius: 50% !important;

    display: flex;
    align-items: center;
    justify-content: center;

    color: white !important;

    font-size: 22px;
    font-weight: 800;
    line-height: 1;

    overflow: hidden;
    user-select: none;

    border: none !important;
    box-shadow: none !important;
    outline: none !important;

    text-align: center !important;
}

/* 不覆盖原站 ct1~cta 的背景颜色。 */

.ehtw-avatar-letter {
    display: inline-block;

    color: white !important;
    font: inherit;
    line-height: 1;

    text-align: center !important;
}

.ehtw-tweet-content {
    min-width: 0;
    max-width: 100%;
    flex: 1;
}

.ehtw-tweet-author {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;

    gap: 5px;
    min-height: 23px;
    margin-bottom: 5px;

    line-height: 1.4;
}

.ehtw-author-name {
    min-width: 0;
    max-width: 100%;

    font-size: 15px;
    font-weight: 800;

    color: var(--ehtw-text);
    overflow-wrap: anywhere;
}

.ehtw-author-name a {
    color: var(--ehtw-text) !important;
    font-weight: 800;
    text-decoration: none !important;
}

.ehtw-author-name a:hover {
    text-decoration: underline !important;
}

.ehtw-handle,
.ehtw-time,
.ehtw-dot {
    font-size: 14px;
    color: var(--ehtw-muted) !important;
}

.ehtw-time [onclick],
.ehtw-time a {
    color: var(--ehtw-muted) !important;
    cursor: pointer;
}

.ehtw-tweet-title {
    margin: 3px 0 10px;

    font-size: 15px;
    line-height: 1.5;

    overflow-wrap: anywhere;
    white-space: normal;
}

.ehtw-tweet-title a {
    display: block;

    color: var(--ehtw-text) !important;
    text-decoration: none !important;
}

.ehtw-tweet-title a:hover {
    color: var(--ehtw-accent) !important;
}

.ehtw-tweet-title .glink {
    font-size: 15px !important;
    line-height: 1.5 !important;

    color: inherit !important;
    font-weight: 400 !important;

    text-align: left !important;
    white-space: normal !important;
}

.ehtw-tweet-title a > div:not(.glink):not(.gl4t):not(.gl4e):not(.glname) {
    display: none !important;
}

.ehtw-tweet-title a > .gl4e.glname {
    display: block !important;
    position: static !important;
    width: auto !important;
    min-width: 0 !important;
    max-width: 100% !important;
    height: auto !important;
    min-height: 0 !important;
    max-height: none !important;
    overflow: visible !important;
}

.ehtw-tweet-title a > .gl4e.glname > .glink {
    display: block !important;
    position: static !important;
    width: auto !important;
    height: auto !important;
    min-height: 0 !important;
    max-height: none !important;
    overflow: visible !important;
    white-space: normal !important;
}

.ehtw-tweet-title a > .gl4e.glname > div:not(.glink) {
    display: none !important;
}

/* 标签 */

.ehtw-tags {
    display: flex;
    flex-wrap: wrap;
    align-items: center;

    gap: 5px 9px;

    width: 100%;
    min-width: 0;

    margin: 8px 0 12px;
}

.ehtw-hashtag {
    display: inline-flex;
    align-items: center;

    max-width: 100%;

    border: none !important;
    background: transparent !important;

    color: var(--ehtw-accent) !important;

    font-size: 13px !important;
    font-weight: 500;
    line-height: 1.45;

    overflow-wrap: anywhere;
    cursor: pointer;
}

.ehtw-hashtag::before {
    content: "#";
    color: var(--ehtw-accent);
}

.ehtw-hashtag:hover {
    text-decoration: underline;
}

.ehtw-hashtag a {
    color: var(--ehtw-accent) !important;
    text-decoration: none !important;
}

.ehtw-hashtag a:hover {
    text-decoration: underline !important;
}

/* 大图媒体 */

.ehtw-media {
    width: 100%;
    max-width: 100%;
    min-width: 0;

    margin-top: 10px;
    overflow: hidden;

    border: 1px solid var(--ehtw-border);
    border-radius: 16px;

    background: var(--ehtw-media);
}

.ehtw-media-link {
    display: block;
    width: 100%;
    text-decoration: none;
}

.ehtw-media img {
    display: block !important;

    width: 100% !important;
    max-width: 100% !important;
    max-height: 510px !important;

    height: auto !important;
    min-height: 0 !important;

    margin: 0 auto !important;
    padding: 0 !important;

    border: none !important;
    border-radius: 0 !important;

    object-fit: cover;
    object-position: center;

    background: var(--ehtw-media);
}

/* 底部操作栏 */

.ehtw-actions {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;

    gap: 8px;
    margin-top: 12px;

    color: var(--ehtw-muted);
    font-size: 13px;
}

.ehtw-action {
    display: inline-flex;
    align-items: center;

    gap: 7px;
    min-width: 0;

    color: var(--ehtw-muted);
    font-size: 13px;
}

.ehtw-action-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;

    width: 25px;
    height: 25px;

    border-radius: 50%;
    font-size: 18px;

    color: var(--ehtw-muted);
}

.ehtw-action:hover .ehtw-action-icon {
    background: rgba(29, 155, 240, 0.10);
    color: var(--ehtw-accent);
}

.ehtw-action a {
    color: var(--ehtw-muted) !important;
}

.ehtw-action .ir {
    display: inline-block !important;
    flex: none;

    position: relative !important;
    float: none !important;

    margin: 0 !important;
    padding: 0 !important;

    transform: none !important;

    /* 保留原始 background-position。 */
}

.ehtw-action .gldown {
    display: inline-flex !important;

    position: static !important;
    float: none !important;

    width: auto !important;
    height: auto !important;

    margin: 0 !important;
    padding: 0 !important;

    background: transparent !important;
}

.ehtw-action .gldown img {
    max-width: 30px;
    max-height: 30px;
    cursor: pointer;
}

.ehtw-action .cn {
    display: inline-flex !important;

    width: auto !important;
    min-width: 0 !important;
    height: auto !important;
    max-width: 100%;

    padding: 4px 9px !important;
    margin: 0 !important;

    border-radius: 9999px;

    font-size: 11px !important;
    line-height: 1.3 !important;

    cursor: pointer;
}

html.ehtw-list .glthumb {
    display: none !important;
}

/* ============================================================
   详情页
   ============================================================ */

.ehtw-detail-main {
    min-width: 0;
    max-width: 100%;
    width: 100%;
}

.ehtw-detail-post {
    min-width: 0;
    max-width: 100%;

    padding: 16px;
    border-bottom: 1px solid var(--ehtw-border);
}

.ehtw-detail-author {
    display: flex;
    align-items: center;

    gap: 12px;
    margin-bottom: 14px;
}

.ehtw-detail-author-meta {
    flex: 1 1 auto;
    min-width: 0;

    display: flex;
    flex-direction: column;
    gap: 3px;
}

.ehtw-detail-author-meta a {
    color: var(--ehtw-text) !important;
    font-weight: 800;
    text-decoration: none !important;
}

.ehtw-detail-author-meta a:hover {
    text-decoration: underline !important;
}

.ehtw-detail-follow-actions {
    display: flex;
    flex-wrap: wrap;
    flex: 0 1 auto;
    justify-content: flex-end;
    align-content: center;
    align-items: center;
    max-width: 52%;
    margin: 0 0 0 auto;
    gap: 6px;
}

.ehtw-detail-follow-status {
    flex-basis: 100%;
    min-height: 0;
    color: var(--ehtw-muted);
    font-size: 12px;
    text-align: right;
}

.ehtw-detail-follow-status:empty {
    display: none;
}

.ehtw-detail-follow-status.is-error {
    color: #e57373;
}

html.ehtw-detail .ehtw-detail-follow-actions > .ehtw-detail-follow-button {
    display: inline-flex !important;
    flex: 0 0 auto;
    align-items: center !important;
    justify-content: center !important;
    min-width: 0 !important;
    min-height: 32px !important;
    margin: 0 !important;
    padding: 5px 12px !important;
    border: 1px solid var(--ehtw-border) !important;
    border-radius: 9999px !important;
    background: transparent !important;
    color: #1d9bf0 !important;
    font: inherit;
    font-size: 13px !important;
    font-weight: 700 !important;
    line-height: 1.2 !important;
    white-space: nowrap;
    cursor: pointer !important;
    transition: background-color 120ms ease, color 120ms ease, border-color 120ms ease;
}

html.ehtw-detail .ehtw-detail-follow-actions > .ehtw-detail-follow-button::before {
    content: '+';
    display: inline-block;
    margin-right: 5px;
    font-size: 15px;
    font-weight: 500;
    line-height: 1;
}

html.ehtw-detail .ehtw-detail-follow-actions > .ehtw-detail-follow-button[aria-pressed="true"]::before {
    content: '✓';
}

html.ehtw-detail .ehtw-detail-follow-actions > .ehtw-detail-follow-button:hover {
    border-color: rgba(29, 155, 240, .65) !important;
    background: rgba(29, 155, 240, .10) !important;
}

html.ehtw-detail .ehtw-detail-follow-actions > .ehtw-detail-follow-button[aria-pressed="true"] {
    border-color: #1d9bf0 !important;
    background: #1d9bf0 !important;
    color: #fff !important;
}

html.ehtw-detail .ehtw-detail-follow-actions > .ehtw-detail-follow-button[aria-pressed="true"]:hover {
    border-color: rgba(244, 33, 46, .65) !important;
    background: rgba(244, 33, 46, .10) !important;
    color: #f4212e !important;
}

html.ehtw-detail .ehtw-detail-follow-actions > .ehtw-detail-follow-button:disabled {
    opacity: .65;
    cursor: wait !important;
}

html.ehtw-detail .ehtw-detail-follow-actions > .ehtw-detail-follow-button:focus-visible {
    outline: 2px solid #1d9bf0 !important;
    outline-offset: 2px;
}

@media (max-width: 699px) {
    html.ehtw-detail .ehtw-detail-author {
        align-items: flex-start;
    }

    html.ehtw-detail .ehtw-detail-follow-actions {
        max-width: 56%;
        gap: 5px;
    }

    html.ehtw-detail .ehtw-detail-follow-actions > .ehtw-detail-follow-button {
        min-height: 30px !important;
        padding: 4px 9px !important;
        font-size: 12px !important;
    }
}

.ehtw-detail-post #gd2 {
    display: block !important;

    position: static !important;
    float: none !important;

    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;

    height: auto !important;

    margin: 0 0 12px !important;
    padding: 0 !important;

    text-align: left !important;

    background: transparent !important;
    border: none !important;
}

.ehtw-detail-post #gn {
    display: block !important;

    width: 100% !important;
    height: auto !important;

    margin: 0 0 8px !important;
    padding: 0 !important;

    color: var(--ehtw-text) !important;
    background: transparent !important;

    font-size: 19px !important;
    font-weight: 700 !important;
    line-height: 1.5 !important;

    text-align: left !important;
    overflow-wrap: anywhere;
}

.ehtw-detail-post #gj {
    display: block !important;

    width: 100% !important;
    height: auto !important;

    margin: 0 0 14px !important;
    padding: 0 !important;

    color: var(--ehtw-muted) !important;
    background: transparent !important;

    font-size: 14px !important;
    font-weight: 400 !important;
    line-height: 1.5 !important;

    text-align: left !important;
    overflow-wrap: anywhere;
}

.ehtw-detail-cover {
    width: 100%;
    max-width: 100%;
    min-width: 0;

    margin: 12px 0 16px;

    border-radius: 16px;
    overflow: hidden;

    border: 1px solid var(--ehtw-border);
    background: var(--ehtw-media);
}

.ehtw-detail-cover #gleft {
    display: block !important;

    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;

    height: auto !important;

    margin: 0 !important;
    padding: 0 !important;

    float: none !important;
    position: static !important;

    background: transparent !important;
    border: none !important;
}

.ehtw-detail-cover #gd1 {
    display: block !important;

    position: static !important;
    float: none !important;

    width: 100% !important;
    max-width: 100% !important;

    height: auto !important;
    min-height: 320px;

    margin: 0 !important;
    padding: 0 !important;

    background: transparent !important;
    border: none !important;
}

.ehtw-detail-cover #gd1 > div {
    display: block !important;

    position: relative !important;
    float: none !important;

    width: 100% !important;
    max-width: 100% !important;

    height: min(72vw, 510px) !important;
    min-height: 320px;

    margin: 0 auto !important;
    padding: 0 !important;

    border: 0 !important;

    background-repeat: no-repeat !important;
    background-position: center center !important;
    background-size: contain !important;
}

/* 详情元信息 */

.ehtw-detail-meta {
    margin-top: 14px;

    border-top: 1px solid var(--ehtw-border);
    border-bottom: 1px solid var(--ehtw-border);

    padding: 12px 0;
}

.ehtw-detail-meta #gmid {
    display: block !important;

    position: static !important;
    float: none !important;

    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;

    height: auto !important;

    margin: 0 !important;
    padding: 0 !important;

    border: none !important;
    background: transparent !important;
}

.ehtw-detail-meta #gd3 {
    position: static !important;
    float: none !important;

    display: flex !important;
    flex-direction: column;
    gap: 12px;

    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;

    height: auto !important;

    margin: 0 !important;
    padding: 0 !important;

    background: transparent !important;
    border: none !important;
}

.ehtw-detail-meta #gdc,
.ehtw-detail-meta #gdn,
.ehtw-detail-meta #gdd,
.ehtw-detail-meta #gdr,
.ehtw-detail-meta #gdf {
    position: static !important;
    float: none !important;

    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;

    height: auto !important;
    min-height: 0 !important;

    margin: 0 !important;
    padding: 0 !important;

    border: none !important;
    background-color: transparent !important;

    color: var(--ehtw-text) !important;
}

.ehtw-detail-meta #gdc .cs {
    display: inline-flex !important;

    position: static !important;
    float: none !important;

    width: auto !important;
    height: auto !important;

    padding: 5px 12px !important;

    border-radius: 9999px;
    cursor: pointer;
}

.ehtw-detail-meta #gdd table {
    display: table;

    width: 100%;
    max-width: 100%;

    border-collapse: collapse;
    background: transparent !important;
}

.ehtw-detail-meta #gdd tr {
    border-bottom: 1px solid var(--ehtw-border);
}

.ehtw-detail-meta #gdd td {
    padding: 8px 5px !important;

    border: none !important;
    background: transparent !important;

    color: var(--ehtw-text) !important;
    font-size: 13px;

    overflow-wrap: anywhere;
}

.ehtw-detail-meta #gdd .gdt1 {
    width: 35%;
    color: var(--ehtw-muted) !important;
}

.ehtw-detail-meta #gdr {
    overflow: visible !important;
}

.ehtw-detail-meta #gdr table {
    max-width: 100%;
}

.ehtw-detail-meta #gdr .ir,
.ehtw-detail-meta #gdr #rating_image {
    background-position: revert-layer;
}

.ehtw-detail-meta #gdf {
    display: block !important;
    cursor: pointer;
}

.ehtw-detail-meta #gdf a {
    color: var(--ehtw-accent) !important;
}

/* 命名空间标签 */

.ehtw-detail-tags {
    margin-top: 16px;
    padding-bottom: 4px;

    min-width: 0;
    max-width: 100%;
}

.ehtw-detail-tags #gd4,
.ehtw-detail-tags #taglist {
    position: static !important;
    float: none !important;

    display: block !important;

    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;

    height: auto !important;

    margin: 0 !important;
    padding: 0 !important;

    background: transparent !important;
    border: none !important;

    overflow: visible !important;
}

.ehtw-detail-tags #taglist table {
    width: 100%;
    max-width: 100%;

    border-collapse: separate;
    border-spacing: 0 9px;

    background: transparent !important;
}

.ehtw-detail-tags #taglist tr {
    background: transparent !important;
}

.ehtw-detail-tags #taglist td {
    height: auto !important;

    padding: 5px 8px !important;

    background: transparent !important;
    border: none !important;

    vertical-align: top;
    color: var(--ehtw-text) !important;
}

.ehtw-detail-tags #taglist td.tc {
    width: 115px;
    min-width: 0;

    padding-top: 8px !important;

    font-size: 12px;
    font-weight: 700;

    color: var(--ehtw-muted) !important;

    text-align: right;
    overflow-wrap: anywhere;
}

.ehtw-detail-tags #taglist td:not(.tc) {
    line-height: 1.8;
}

.ehtw-detail-tags #taglist .gt {
    display: inline-flex !important;

    position: static !important;
    float: none !important;

    max-width: 100%;
    width: auto !important;
    height: auto !important;

    min-height: 24px;

    margin: 2px 4px 2px 0 !important;
    padding: 3px 9px !important;

    border: 1px solid var(--ehtw-border) !important;
    border-radius: 9999px !important;

    background: transparent !important;
    color: var(--ehtw-accent) !important;

    font-size: 12px !important;
    line-height: 1.5 !important;

    white-space: normal !important;
    overflow-wrap: anywhere;
}

.ehtw-detail-tags #taglist .gt::before {
    content: "#";
    color: var(--ehtw-accent);
}

.ehtw-detail-tags #taglist .gt a {
    color: var(--ehtw-accent) !important;
    text-decoration: none !important;
}

.ehtw-detail-tags #taglist .gt:hover {
    background: rgba(29, 155, 240, 0.10) !important;
}

/* 详情操作链接 */

.ehtw-operation-card {
    padding-bottom: 10px;
    text-align: left !important;
}

.ehtw-operation-card #gd5 {
    display: block !important;

    position: static !important;
    float: none !important;

    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;

    height: auto !important;

    margin: 0 !important;
    padding: 0 12px 10px !important;

    border: none !important;
    background: transparent !important;

    color: var(--ehtw-text) !important;

    text-align: left !important;
    overflow-wrap: anywhere;
}

.ehtw-operation-card #gd5 a {
    color: var(--ehtw-accent) !important;
}

#ehtw-mobile-operations {
    display: none;
    min-width: 0;
    max-width: 100%;
}

#ehtw-mobile-operations .ehtw-operation-card {
    margin: 12px;

    width: calc(100% - 24px);
    max-width: calc(100% - 24px);

    text-align: left !important;
}

/* ============================================================
   画廊缩略图网格
   ============================================================ */

.ehtw-gallery-grid-section {
    width: 100%;
    min-width: 0;
    max-width: 100%;

    padding: 12px 12px 18px;

    border-bottom: 1px solid var(--ehtw-border);
}

.ehtw-gallery-grid-section #gdt {
    display: block !important;

    position: static !important;
    float: none !important;

    width: 100% !important;
    max-width: 100% !important;
    min-width: 0 !important;

    height: auto !important;

    margin: 0 !important;
    padding: 0 !important;

    background: transparent !important;
    border: none !important;

    overflow: visible !important;
    text-align: center;
}

.ehtw-gallery-grid-section #gdt a {
    display: inline-block;
    max-width: 100%;

    margin: 3px;
    vertical-align: top;
}

.ehtw-gallery-grid-section #gdt img {
    max-width: 100%;
    border-radius: 9px;
}

/* ============================================================
   0.5.0:画廊上下分页
   上分页 table.ptt
   下分页 table.ptb
   两者应用完全相同的胶囊样式。
   ============================================================ */

html.ehtw-detail #ehtw-center .ehtw-gallery-pagination {
    width: 100%;
    min-width: 0;
    max-width: 100%;

    padding: 12px 10px;

    border-bottom: 1px solid var(--ehtw-border);
    background: var(--ehtw-bg) !important;

    text-align: left !important;
}

html.ehtw-detail #ehtw-center .ehtw-gallery-pagination .gtb {
    display: flex !important;
    flex-direction: column;
    align-items: flex-start;

    gap: 8px;

    position: static !important;
    float: none !important;

    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;

    height: auto !important;
    min-height: 0 !important;

    margin: 0 !important;
    padding: 0 !important;

    border: none !important;
    background: transparent !important;

    color: var(--ehtw-text) !important;
    text-align: left !important;

    overflow: visible !important;
}

html.ehtw-detail #ehtw-center .ehtw-gallery-pagination .gtb p.gpc {
    position: static !important;
    float: none !important;

    display: block !important;

    width: auto !important;
    max-width: 100% !important;
    height: auto !important;

    margin: 0 !important;
    padding: 0 !important;

    background: transparent !important;
    background-image: none !important;

    border: none !important;
    box-shadow: none !important;

    color: var(--ehtw-muted) !important;

    font-size: 12px !important;
    line-height: 1.5 !important;
    font-weight: 400 !important;

    text-align: left !important;
    overflow-wrap: anywhere;
}

/* ptt 和 ptb:表格容器。 */

html.ehtw-detail #ehtw-center .ehtw-gallery-pagination .gtb table.ptt,
html.ehtw-detail #ehtw-center .ehtw-gallery-pagination .gtb table.ptb {
    display: block !important;

    position: static !important;
    float: none !important;

    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;

    height: auto !important;

    margin: 0 !important;
    padding: 0 !important;

    background: transparent !important;
    background-image: none !important;

    border: none !important;
    border-collapse: separate !important;
    border-spacing: 0 !important;

    overflow: visible !important;
}

/* tbody */

html.ehtw-detail #ehtw-center .ehtw-gallery-pagination table.ptt > tbody,
html.ehtw-detail #ehtw-center .ehtw-gallery-pagination table.ptb > tbody {
    display: flex !important;
    flex-wrap: wrap !important;

    align-items: center;
    justify-content: flex-start;

    gap: 6px;

    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;

    background: transparent !important;
}

/* tr */

html.ehtw-detail #ehtw-center .ehtw-gallery-pagination table.ptt tr,
html.ehtw-detail #ehtw-center .ehtw-gallery-pagination table.ptb tr {
    display: flex !important;
    align-items: center;
    flex-wrap: wrap !important;

    gap: 6px;

    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;

    height: auto !important;

    margin: 0 !important;
    padding: 0 !important;

    border: none !important;
    background: transparent !important;
}

/* 普通 td */

html.ehtw-detail #ehtw-center .ehtw-gallery-pagination table.ptt tr td,
html.ehtw-detail #ehtw-center .ehtw-gallery-pagination table.ptb tr td {
    display: inline-flex !important;
    align-items: center !important;
    justify-content: center !important;

    position: static !important;
    float: none !important;

    width: auto !important;
    min-width: 32px !important;
    max-width: 100% !important;

    height: 32px !important;
    min-height: 32px !important;
    max-height: none !important;

    margin: 0 !important;
    padding: 0 !important;

    border: 0 !important;
    border-width: 0 !important;
    border-style: none !important;

    border-radius: 9999px !important;

    background: transparent !important;
    background-image: none !important;

    box-shadow: none !important;
    outline: none !important;

    color: var(--ehtw-text) !important;

    font-size: 12px !important;
    line-height: 1.4 !important;

    text-align: center !important;
    vertical-align: middle !important;

    overflow: hidden !important;
}

/* 普通页链接 */

html.ehtw-detail #ehtw-center .ehtw-gallery-pagination table.ptt tr td a,
html.ehtw-detail #ehtw-center .ehtw-gallery-pagination table.ptb tr td a {
    display: inline-flex !important;
    align-items: center !important;
    justify-content: center !important;

    position: static !important;

    width: 100% !important;
    min-width: 32px !important;
    max-width: 100% !important;

    height: 32px !important;
    min-height: 32px !important;

    padding: 0 11px !important;
    margin: 0 !important;

    border: none !important;
    border-radius: 9999px !important;

    background: transparent !important;
    background-image: none !important;

    box-shadow: none !important;

    color: var(--ehtw-text) !important;

    font-size: 12px !important;
    font-weight: 500 !important;
    line-height: 32px !important;

    text-align: center !important;
    text-decoration: none !important;

    white-space: nowrap;
}

/* 悬停 */

html.ehtw-detail #ehtw-center .ehtw-gallery-pagination table.ptt tr td a:hover,
html.ehtw-detail #ehtw-center .ehtw-gallery-pagination table.ptb tr td a:hover {
    background: var(--ehtw-nav-hover) !important;
    color: var(--ehtw-accent) !important;
}

/* 当前页:ptds */

html.ehtw-detail #ehtw-center .ehtw-gallery-pagination table.ptt tr td.ptds,
html.ehtw-detail #ehtw-center .ehtw-gallery-pagination table.ptb tr td.ptds {
    background: var(--ehtw-accent) !important;
    color: #ffffff !important;
    font-weight: 800 !important;
}

html.ehtw-detail #ehtw-center .ehtw-gallery-pagination table.ptt tr td.ptds a,
html.ehtw-detail #ehtw-center .ehtw-gallery-pagination table.ptb tr td.ptds a {
    background: var(--ehtw-accent) !important;
    color: #ffffff !important;
    font-weight: 800 !important;
}

html.ehtw-detail #ehtw-center .ehtw-gallery-pagination table.ptt tr td.ptds a:hover,
html.ehtw-detail #ehtw-center .ehtw-gallery-pagination table.ptb tr td.ptds a:hover {
    background: var(--ehtw-accent) !important;
    color: #ffffff !important;
}

/* 禁用项:ptdd */

html.ehtw-detail #ehtw-center .ehtw-gallery-pagination table.ptt tr td.ptdd,
html.ehtw-detail #ehtw-center .ehtw-gallery-pagination table.ptb tr td.ptdd {
    background: transparent !important;
    color: var(--ehtw-muted) !important;
    font-weight: 400 !important;
}

html.ehtw-detail #ehtw-center .ehtw-gallery-pagination table.ptt tr td.ptdd a,
html.ehtw-detail #ehtw-center .ehtw-gallery-pagination table.ptb tr td.ptdd a {
    background: transparent !important;
    color: var(--ehtw-muted) !important;
    font-weight: 400 !important;
}

/* ============================================================
   页脚
   ============================================================ */

#ehtw-left .ehtw-footer {
    width: 100%;
    min-width: 0;
    max-width: 100%;

    margin-top: 22px;
    padding: 12px 16px;

    border-top: 1px solid var(--ehtw-border);

    color: var(--ehtw-muted);
    font-size: 11px;
    line-height: 1.6;

    text-align: left;
    overflow-wrap: anywhere;
}

#ehtw-left .ehtw-footer .dp {
    display: block !important;

    position: static !important;
    float: none !important;

    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;

    height: auto !important;

    margin: 0 !important;
    padding: 0 !important;

    border: none !important;
    background: transparent !important;

    color: var(--ehtw-muted) !important;
    font-size: 11px !important;
    line-height: 1.6 !important;

    text-align: left !important;

    white-space: normal !important;
    overflow-wrap: anywhere;
}

#ehtw-left .ehtw-footer .dp a {
    display: inline !important;

    width: auto !important;
    min-height: 0 !important;
    max-width: 100%;

    padding: 0 !important;
    margin: 0 4px 0 0 !important;

    border: none !important;
    border-radius: 0 !important;

    background: transparent !important;
    color: var(--ehtw-muted) !important;

    font-size: 11px !important;
    font-weight: 400 !important;
    line-height: inherit !important;

    text-decoration: none !important;
    white-space: normal !important;
}

#ehtw-left .ehtw-footer .dp a:hover {
    color: var(--ehtw-accent) !important;
    text-decoration: underline !important;
}

/* ============================================================
   评论与回复
   ============================================================ */

.ehtw-comments-section {
    min-width: 0;
    max-width: 100%;
    width: 100%;

    padding-top: 12px;
}

.ehtw-comments-section > .ehtw-section-title {
    padding: 0 16px 12px;
    text-align: left !important;
}

.ehtw-comments-section #cdiv {
    position: static !important;
    float: none !important;

    display: block !important;

    width: 100% !important;
    max-width: 100% !important;
    min-width: 0 !important;

    height: auto !important;

    margin: 0 !important;
    padding: 0 !important;

    border: none !important;
    background: transparent !important;
}

.ehtw-comments-section #cdiv .c1 {
    position: relative !important;
    float: none !important;

    display: block !important;

    width: 100% !important;
    max-width: 100% !important;
    min-width: 0 !important;

    height: auto !important;
    min-height: 70px;

    margin: 0 !important;
    padding: 16px 16px 16px 76px !important;

    border: none !important;
    border-bottom: 1px solid var(--ehtw-border) !important;

    border-radius: 0 !important;

    background: var(--ehtw-bg) !important;
    color: var(--ehtw-text) !important;

    overflow-wrap: anywhere;
    transition: background 0.15s ease;
}

.ehtw-comments-section #cdiv .c1:hover {
    background: var(--ehtw-hover) !important;
}

.ehtw-comments-section #cdiv .c1::before {
    content: "↩";

    position: absolute;
    left: 16px;
    top: 16px;

    display: flex;
    align-items: center;
    justify-content: center;

    width: 48px;
    height: 48px;

    border-radius: 50%;

    background: var(--ehtw-input);
    color: var(--ehtw-muted);

    font-size: 23px;
}

.ehtw-comments-section #cdiv .c1 .c2 {
    display: flex !important;
    flex-direction: row !important;
    flex-wrap: wrap !important;
    align-items: baseline !important;

    gap: 4px 12px;

    position: static !important;
    float: none !important;

    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;

    height: auto !important;
    min-height: 0 !important;

    margin: 0 0 9px !important;
    padding: 0 !important;

    background: transparent !important;
    background-image: none !important;

    border: none !important;
    border-radius: 0 !important;
    box-shadow: none !important;

    color: var(--ehtw-text) !important;
    text-align: left !important;

    overflow: visible !important;
}

.ehtw-comments-section #cdiv .c1 .c3,
.ehtw-comments-section #cdiv .c1 .c4 {
    display: inline-flex !important;
    align-items: baseline !important;
    flex-wrap: wrap !important;

    gap: 4px;

    position: static !important;
    float: none !important;

    width: auto !important;
    min-width: 0 !important;
    max-width: 100% !important;

    height: auto !important;
    min-height: 0 !important;

    margin: 0 !important;
    padding: 0 !important;

    background: transparent !important;
    background-image: none !important;

    border: none !important;
    border-radius: 0 !important;
    box-shadow: none !important;

    font-size: 13px !important;
    line-height: 1.5 !important;

    color: var(--ehtw-muted) !important;
    text-align: left !important;

    white-space: normal !important;
    overflow-wrap: anywhere;
}

.ehtw-comments-section #cdiv .c1 .c3 a {
    color: var(--ehtw-text) !important;

    font-size: 14px !important;
    font-weight: 800 !important;

    background: transparent !important;
    border: none !important;

    text-decoration: none !important;
}

.ehtw-comments-section #cdiv .c1 .c3 a:hover {
    color: var(--ehtw-accent) !important;
    text-decoration: underline !important;
}

.ehtw-comments-section #cdiv .c1 .c4,
.ehtw-comments-section #cdiv .c1 .c4 * {
    color: var(--ehtw-muted) !important;
    font-size: 13px !important;
}

.ehtw-comments-section #cdiv .c1 .c2 > .c {
    position: static !important;
    float: none !important;

    width: auto !important;
    min-width: 0 !important;
    max-width: 100% !important;

    height: auto !important;
    min-height: 0 !important;

    margin: 0 !important;
    padding: 0 !important;

    background: transparent !important;
    background-image: none !important;

    border: none !important;
    box-shadow: none !important;

    color: var(--ehtw-muted) !important;
}

.ehtw-comments-section #cdiv .c1 .c6 {
    display: block !important;

    position: static !important;
    float: none !important;

    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;

    height: auto !important;
    min-height: 0 !important;

    background: transparent !important;
    background-image: none !important;

    color: var(--ehtw-text) !important;

    font-size: 14px;
    line-height: 1.65;

    text-align: left !important;
    overflow-wrap: anywhere;
}

/* 评论投票控件保持原始 DOM 和事件。 */

.ehtw-comments-section #cdiv .c1 .c7 {
    max-width: 100% !important;
    min-width: 0 !important;

    background-color: transparent !important;

    color: var(--ehtw-muted) !important;
    text-align: left !important;
}

.ehtw-comments-section #cdiv .c1 a {
    color: var(--ehtw-accent) !important;
}

.ehtw-comments-section #cdiv .c1 .c3 a {
    color: var(--ehtw-text) !important;
}

.ehtw-comments-section #cdiv .c1,
.ehtw-comments-section #cdiv .c1 div,
.ehtw-comments-section #cdiv .c1 p {
    max-width: 100%;
    min-width: 0;
    overflow-wrap: anywhere;
}

.ehtw-comments-section #postnewcomment {
    display: block !important;

    position: static !important;
    float: none !important;

    width: 100% !important;
    max-width: 100% !important;
    min-width: 0 !important;

    height: auto !important;

    margin: 0 !important;
    padding: 16px !important;

    border: none !important;
    border-top: 1px solid var(--ehtw-border) !important;

    background: var(--ehtw-bg) !important;
    color: var(--ehtw-text) !important;
}

.ehtw-comments-section textarea {
    max-width: 100%;
    min-width: 0;

    color: var(--ehtw-text) !important;
    background: var(--ehtw-card) !important;

    border: 1px solid var(--ehtw-border) !important;
    border-radius: 12px;

    padding: 12px;
}

.ehtw-comments-section input,
.ehtw-comments-section button {
    max-width: 100%;
}

/* ============================================================
   其它列表显示模式
   ============================================================ */

.ehtw-other-content {
    padding: 12px;

    min-width: 0;
    max-width: 100%;
    width: 100%;
}

.ehtw-other-content .itg,
.ehtw-other-content table {
    max-width: 100%;
}

.ehtw-other-content img {
    max-width: 100%;
}

.ehtw-other-content .searchnav {
    max-width: 100%;
}

html.ehtw-other #ehtw-center {
    overflow-x: auto;
}

html.ehtw-other #ehtw-center a {
    color: var(--ehtw-accent);
}

/* Thumbnail (.itg.gld) */

html.ehtw-other .ehtw-other-content .itg.gld {
    display: grid !important;

    grid-template-columns:
        repeat(auto-fill, minmax(180px, 1fr)) !important;

    gap: 12px !important;

    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;

    height: auto !important;

    margin: 0 !important;
    padding: 0 !important;

    background: transparent !important;
    border: none !important;

    align-items: stretch !important;
    justify-content: stretch !important;
}

html.ehtw-other .ehtw-other-content .itg.gld .gl1t {
    display: block !important;

    position: relative !important;
    float: none !important;

    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;

    height: auto !important;

    margin: 0 !important;
    padding: 10px !important;

    border: 1px solid var(--ehtw-border) !important;
    border-radius: 12px !important;

    background: transparent !important;
    background-image: none !important;

    color: var(--ehtw-text) !important;

    overflow: hidden !important;
    overflow-wrap: anywhere;
}

html.ehtw-other .ehtw-other-content .itg.gld .gl1t:nth-child(odd),
html.ehtw-other .ehtw-other-content .itg.gld .gl1t:nth-child(even) {
    background: transparent !important;
    background-image: none !important;

    border-color: var(--ehtw-border) !important;
    color: var(--ehtw-text) !important;
}

html.ehtw-other .ehtw-other-content .itg.gld .gl1t > div {
    min-width: 0 !important;
    max-width: 100% !important;

    box-sizing: border-box;
}

html.ehtw-other .ehtw-other-content .itg.gld .gl1t img {
    max-width: 100% !important;
    height: auto;

    object-fit: contain;
}

html.ehtw-other .ehtw-other-content .itg.gld .gl1t a {
    max-width: 100%;

    color: var(--ehtw-text) !important;
    overflow-wrap: anywhere;
}

html.ehtw-other .ehtw-other-content .itg.gld .gl1t a:hover {
    color: var(--ehtw-accent) !important;
}

html.ehtw-other .ehtw-other-content .itg.gld .gl3t,
html.ehtw-other .ehtw-other-content .itg.gld .gl4t,
html.ehtw-other .ehtw-other-content .itg.gld .gl5t,
html.ehtw-other .ehtw-other-content .itg.gld .gl6t,
html.ehtw-other .ehtw-other-content .itg.gld .glthumb {
    width: auto !important;
    min-width: 0 !important;
    max-width: 100% !important;

    margin-left: auto !important;
    margin-right: auto !important;

    background-color: transparent !important;
    color: var(--ehtw-text) !important;
}

/* ============================================================
   0.5.0:修复 .gl5t 绝对定位残留
   ============================================================ */

/* 整个 .gl5t 由原站固定 46px 改成自动高度。 */

html.ehtw-other #ehtw-center .ehtw-other-content .itg.gld .gl1t .gl5t {
    display: flex !important;
    flex-direction: column !important;

    align-items: stretch !important;
    justify-content: flex-start !important;

    gap: 4px !important;

    position: static !important;

    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;

    height: auto !important;
    min-height: 0 !important;
    max-height: none !important;

    margin: 0 !important;
    padding: 0 !important;

    overflow: visible !important;
}

/* 第一层两个分组取消 110px / 80px 宽度限制。 */

html.ehtw-other #ehtw-center .ehtw-other-content .itg.gld .gl1t .gl5t > div {
    display: flex !important;
    flex-direction: row !important;

    align-items: center !important;
    justify-content: space-between !important;
    flex-wrap: wrap !important;

    gap: 4px !important;

    position: static !important;
    float: none !important;

    width: 100% !important;
    min-width: 0 !important;
    max-width: none !important;

    height: auto !important;
    min-height: 0 !important;
    max-height: none !important;

    margin: 0 !important;
    padding: 0 !important;

    background: transparent !important;
    border: none !important;

    overflow: visible !important;
}

/*
 * 核心修复:
 * 原站的 nth-child 规则把孙元素绝对定位。
 * 必须同时重置 position、top、left、right、bottom、
 * width、height,否则元素仍可能跑到 .gl1t 顶部。
 *
 * 后面的 .ir 和 .gldown 规则再恢复各自固定尺寸。
 */

html.ehtw-other #ehtw-center .ehtw-other-content .itg.gld .gl1t .gl5t > div > div {
    position: static !important;
    float: none !important;

    top: auto !important;
    left: auto !important;
    right: auto !important;
    bottom: auto !important;

    width: auto !important;
    min-width: 0 !important;
    max-width: 100% !important;

    height: auto !important;
    min-height: 0 !important;
    max-height: none !important;

    margin: 0 !important;
    padding: 0 !important;

    transform: none !important;

    background-color: transparent !important;
}

/* 分类胶囊不拉伸,保留原站类别颜色。 */

html.ehtw-other #ehtw-center .ehtw-other-content .itg.gld .gl5t .cs {
    display: inline-flex !important;

    align-items: center !important;
    justify-content: center !important;

    position: static !important;
    float: none !important;

    top: auto !important;
    left: auto !important;

    flex: 0 0 auto !important;

    width: auto !important;
    min-width: 0 !important;
    max-width: 100% !important;

    height: auto !important;

    margin: 0 !important;
    padding: 3px 8px !important;

    border-radius: 9999px !important;

    font-size: 11px !important;
    line-height: 1.4 !important;

    white-space: nowrap !important;
    cursor: pointer;
}

/* 星级:严格保留 80 × 16px 与背景精灵图。 */

html.ehtw-other #ehtw-center .ehtw-other-content .itg.gld .gl5t .ir {
    display: block !important;

    position: static !important;
    float: none !important;

    top: auto !important;
    left: auto !important;
    right: auto !important;
    bottom: auto !important;

    flex: 0 0 80px !important;
    flex-shrink: 0 !important;
    flex-grow: 0 !important;

    width: 80px !important;
    min-width: 80px !important;
    max-width: 80px !important;

    height: 16px !important;
    min-height: 16px !important;
    max-height: 16px !important;

    margin: 0 !important;
    padding: 0 !important;

    transform: none !important;
    scale: none !important;

    overflow: visible !important;

    /* 不设置 background-image 和 background-position。 */
}

/* 发布时间 */

html.ehtw-other #ehtw-center .ehtw-other-content .itg.gld .gl5t [id^="posted_"] {
    position: static !important;

    top: auto !important;
    left: auto !important;

    flex: 0 1 auto !important;

    width: auto !important;
    min-width: 0 !important;
    max-width: 100% !important;

    height: auto !important;

    color: var(--ehtw-muted) !important;

    font-size: 12px !important;
    line-height: 1.4 !important;

    white-space: normal !important;
    overflow-wrap: anywhere;
}

/* 页数 */

html.ehtw-other #ehtw-center .ehtw-other-content .itg.gld .gl5t > div:nth-child(2) > div:not(.ir):not(.gldown) {
    position: static !important;

    top: auto !important;
    left: auto !important;

    flex: 0 1 auto !important;

    width: auto !important;
    min-width: 0 !important;
    max-width: 100% !important;

    height: auto !important;

    color: var(--ehtw-muted) !important;

    font-size: 12px !important;
    line-height: 1.4 !important;

    white-space: normal !important;
}

/* 种子下载图标:15 × 15px。 */

html.ehtw-other #ehtw-center .ehtw-other-content .itg.gld .gl5t .gldown {
    display: inline-flex !important;

    position: static !important;
    float: none !important;

    top: auto !important;
    left: auto !important;
    right: auto !important;
    bottom: auto !important;

    flex: 0 0 15px !important;
    flex-shrink: 0 !important;
    flex-grow: 0 !important;

    width: 15px !important;
    min-width: 15px !important;
    max-width: 15px !important;

    height: 15px !important;
    min-height: 15px !important;
    max-height: 15px !important;

    margin: 0 !important;
    padding: 0 !important;

    align-items: center;
    justify-content: center;

    background: transparent !important;

    overflow: visible !important;
}

html.ehtw-other #ehtw-center .ehtw-other-content .itg.gld .gl5t .gldown img {
    display: block !important;

    width: 15px !important;
    max-width: 15px !important;

    height: 15px !important;
    max-height: 15px !important;

    object-fit: contain;
}

/* ============================================================
   Minimal / Minimal+ / Extended 通用表格
   ============================================================ */

html.ehtw-other .ehtw-other-content table.itg:not(.gld) {
    display: table !important;

    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;

    table-layout: fixed !important;
    border-collapse: collapse !important;

    margin: 0 !important;

    background: transparent !important;
    background-image: none !important;

    color: var(--ehtw-text) !important;

    border: 1px solid var(--ehtw-border) !important;
}

html.ehtw-other .ehtw-other-content table.itg:not(.gld) > tbody {
    display: table-row-group !important;
    width: 100% !important;

    background: transparent !important;
}

html.ehtw-other .ehtw-other-content table.itg:not(.gld) tr {
    display: table-row !important;

    width: auto !important;
    min-width: 0 !important;
    max-width: 100% !important;

    background: transparent !important;
    background-image: none !important;

    color: var(--ehtw-text) !important;
}

html.ehtw-other .ehtw-other-content table.itg:not(.gld) tr:nth-child(odd),
html.ehtw-other .ehtw-other-content table.itg:not(.gld) tr:nth-child(even) {
    background: transparent !important;
    background-image: none !important;
}

html.ehtw-other .ehtw-other-content table.itg:not(.gld) th,
html.ehtw-other .ehtw-other-content table.itg:not(.gld) td {
    min-width: 0 !important;
    max-width: 100% !important;

    padding: 8px 6px !important;

    border-color: var(--ehtw-border) !important;

    background: transparent !important;
    background-image: none !important;

    color: var(--ehtw-text) !important;

    white-space: normal !important;
    overflow-wrap: anywhere !important;
    word-break: break-word;
}

/*
 * 0.5.0:
 * 排除 .cn 与 .cs,避免分类颜色被通用主题覆盖。
 */

html.ehtw-other .ehtw-other-content table.itg:not(.gld) td div:not(.cn):not(.cs),
html.ehtw-other .ehtw-other-content table.itg:not(.gld) th div:not(.cn):not(.cs) {
    min-width: 0 !important;
    max-width: 100% !important;

    background-color: transparent !important;
    color: var(--ehtw-text) !important;

    overflow-wrap: anywhere;
}

html.ehtw-other .ehtw-other-content table.itg:not(.gld) a {
    color: var(--ehtw-text) !important;
    overflow-wrap: anywhere;
}

html.ehtw-other .ehtw-other-content table.itg:not(.gld) a:hover {
    color: var(--ehtw-accent) !important;
}

html.ehtw-other .ehtw-other-content table.itg:not(.gld) img {
    max-width: 100% !important;
    height: auto;
}

html.ehtw-other .ehtw-other-content .ir {
    background-color: transparent;
}

html.ehtw-other .ehtw-other-content .cn,
html.ehtw-other .ehtw-other-content .cs {
    cursor: pointer;
}

/*
 * 0.5.0:分类标签保持原站浅色文字。
 * 不覆盖分类背景,只修复被表格规则改变的文字。
 */

html.ehtw-other #ehtw-center .ehtw-other-content .cn,
html.ehtw-other #ehtw-center .ehtw-other-content .cs,
html.ehtw-other #ehtw-center .ehtw-other-content table.itg .cn,
html.ehtw-other #ehtw-center .ehtw-other-content table.itg .cs {
    color: #f1f1f1 !important;
}

/* ============================================================
   Minimal / Minimal+ 自适应列宽
   ============================================================ */

html.ehtw-other #ehtw-center .ehtw-other-content table.itg.gltm {
    display: table !important;

    table-layout: auto !important;

    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;

    border-collapse: collapse !important;
}

html.ehtw-other #ehtw-center .ehtw-other-content table.itg.gltm > tbody {
    display: table-row-group !important;
}

html.ehtw-other #ehtw-center .ehtw-other-content table.itg.gltm tr {
    display: table-row !important;

    width: auto !important;
    min-width: 0 !important;
    max-width: 100% !important;
}

html.ehtw-other #ehtw-center .ehtw-other-content table.itg.gltm td,
html.ehtw-other #ehtw-center .ehtw-other-content table.itg.gltm th {
    min-width: 0 !important;

    box-sizing: border-box;

    overflow: hidden !important;
    text-overflow: ellipsis;
}

/* 分类列 */

html.ehtw-other #ehtw-center .ehtw-other-content table.itg.gltm .gl1m {
    width: 1% !important;
    max-width: none !important;

    white-space: nowrap !important;
}

/* 时间列 */

html.ehtw-other #ehtw-center .ehtw-other-content table.itg.gltm .gl2m {
    width: 1% !important;
    max-width: none !important;

    white-space: nowrap !important;
}

/* 标题列 */

html.ehtw-other #ehtw-center .ehtw-other-content table.itg.gltm .gl3m {
    width: 100% !important;
    max-width: 0 !important;
    min-width: 0 !important;

    white-space: nowrap !important;

    overflow: hidden !important;
    text-overflow: ellipsis !important;
}

html.ehtw-other #ehtw-center .ehtw-other-content table.itg.gltm .gl3m a,
html.ehtw-other #ehtw-center .ehtw-other-content table.itg.gltm .gl3m .glink,
html.ehtw-other #ehtw-center .ehtw-other-content table.itg.gltm .gl3m > div {
    display: block;

    min-width: 0 !important;
    max-width: 100% !important;

    white-space: nowrap !important;

    overflow: hidden !important;
    text-overflow: ellipsis !important;
}

/* 评分列 */

html.ehtw-other #ehtw-center .ehtw-other-content table.itg.gltm .gl4m {
    width: 1% !important;
    max-width: none !important;

    white-space: nowrap !important;
}

html.ehtw-other #ehtw-center .ehtw-other-content table.itg.gltm .gl4m .ir {
    width: 80px !important;
    min-width: 80px !important;
    max-width: 80px !important;

    height: 16px !important;
    min-height: 16px !important;

    flex-shrink: 0 !important;
    transform: none !important;
}

/* 上传者列 */

html.ehtw-other #ehtw-center .ehtw-other-content table.itg.gltm .gl5m {
    width: 1% !important;
    min-width: 0 !important;
    max-width: 100px !important;

    white-space: nowrap !important;

    overflow: hidden !important;
    text-overflow: ellipsis !important;
}

html.ehtw-other #ehtw-center .ehtw-other-content table.itg.gltm .gl5m a,
html.ehtw-other #ehtw-center .ehtw-other-content table.itg.gltm .gl5m div {
    display: block;

    min-width: 0 !important;
    max-width: 100px !important;

    white-space: nowrap !important;

    overflow: hidden !important;
    text-overflow: ellipsis !important;
}

/* ============================================================
   通用样式
   ============================================================ */

.ehtw-section-title {
    margin: 0 0 12px;

    font-size: 19px;
    font-weight: 800;
    line-height: 1.35;

    color: var(--ehtw-text);

    text-align: left !important;
}

.ehtw-gallery-grid-section > .ehtw-section-title,
.ehtw-detail-tags > .ehtw-section-title,
.ehtw-comments-section > .ehtw-section-title {
    width: 100%;
    text-align: left !important;
}

.ehtw-empty {
    padding: 16px;

    font-size: 13px;
    color: var(--ehtw-muted);
}

.ehtw-muted {
    color: var(--ehtw-muted);
}

.ehtw-native-hidden {
    display: none !important;
}

/* ============================================================
   小于 1000px:两栏
   ============================================================ */

@media (max-width: 999px) {
    #ehtw-shell {
        width: min(100%, 875px);
        max-width: 100%;

        grid-template-columns: 275px minmax(0, 600px);
    }

    #ehtw-right {
        display: none !important;
    }

    #ehtw-center {
        border-right: 1px solid var(--ehtw-border);
    }

    #ehtw-left {
        width: 275px;
    }

    #ehtw-mobile-search {
        display: block !important;
    }

    #ehtw-mobile-operations {
        display: block !important;
    }
}

@media (min-width: 1000px) {
    #ehtw-mobile-search,
    #ehtw-mobile-operations {
        display: none !important;
    }
}

@media (min-width: 700px) {
    html.ehtw-exhentai #ehtw-left #nb > .ehtw-more-entry {
        display: none !important;
    }
}

/* ============================================================
   小于 700px:单栏 + 底部 Tab 导航
   ============================================================ */

@media (max-width: 699px) {
    html.ehtw-enabled .ehtw-search-toplists {
        display: none !important;
    }

    html.ehtw-enabled body {
        padding-bottom: 72px !important;
    }

    #ehtw-shell {
        display: block;

        width: 100%;
        max-width: 100%;
        min-width: 0;

        min-height: 100vh;
    }

    #ehtw-left {
        position: fixed !important;

        top: auto !important;
        bottom: 0 !important;
        left: 0 !important;
        right: 0 !important;

        z-index: 1000;

        width: 100% !important;
        max-width: 100vw !important;
        min-width: 0 !important;

        height: 64px !important;
        min-height: 64px !important;
        max-height: 64px !important;

        padding:
            4px
            6px
            max(4px, env(safe-area-inset-bottom)) !important;

        border-top: 1px solid var(--ehtw-border);

        background: var(--ehtw-glass);

        backdrop-filter: blur(14px);
        -webkit-backdrop-filter: blur(14px);

        overflow-x: auto !important;
        overflow-y: hidden !important;

        overscroll-behavior-x: contain;
        overscroll-behavior-y: auto;

        -webkit-overflow-scrolling: touch;

        scrollbar-width: none;
        box-sizing: border-box;
    }

    #ehtw-left::-webkit-scrollbar {
        display: none;
        width: 0;
        height: 0;
    }

    #ehtw-brand,
    .ehtw-nav-hint {
        display: none;
    }

    #ehtw-left #nb {
        display: flex !important;

        flex-direction: row !important;
        flex-wrap: nowrap !important;

        justify-content: flex-start;
        align-items: center;

        gap: 2px;

        width: max-content !important;
        min-width: 100% !important;
        max-width: none !important;

        height: 100% !important;
        min-height: 0 !important;
        max-height: none !important;

        overflow: visible !important;
        white-space: nowrap !important;
    }

    #ehtw-left #nb > div {
        display: block !important;

        width: auto !important;
        min-width: max-content !important;
        max-width: none !important;

        flex: 0 0 auto !important;

        height: auto !important;

        white-space: nowrap !important;
    }

    #ehtw-left #nb a {
        display: flex !important;

        align-items: center;
        justify-content: center;

        width: auto !important;
        min-width: max-content !important;
        max-width: none !important;

        min-height: 45px;
        height: 45px;

        padding: 8px 10px !important;

        font-size: 12px !important;
        font-weight: 600;

        white-space: nowrap !important;

        text-align: center;
        flex-shrink: 0;
    }

    #ehtw-center {
        width: 100%;
        max-width: 100%;
        min-width: 0;

        min-height: 100vh;

        border-left: none;
        border-right: none;
    }

    #ehtw-header {
        min-height: 54px;
        padding: 8px 14px;
    }

    #ehtw-header-title {
        font-size: 20px !important;
    }

    .ehtw-tweet {
        gap: 10px;
        padding: 12px 12px 14px;
    }

    .ehtw-avatar {
        width: 42px;
        height: 42px;

        flex-basis: 42px;
        font-size: 19px;
    }

    .ehtw-tweet-title {
        font-size: 14px;
    }

    .ehtw-media img {
        max-height: 430px !important;
    }

    .ehtw-actions {
        gap: 5px;
        font-size: 12px;
    }

    .ehtw-action {
        gap: 3px;
    }

    .ehtw-action-icon {
        width: 23px;
        height: 23px;
    }

    .ehtw-pagination {
        padding: 9px 5px;
    }

    .ehtw-pagination .searchnav {
        gap: 4px;
    }

    .ehtw-pagination .searchnav a,
    .ehtw-pagination .searchnav span[id],
    .ehtw-pagination .searchnav select {
        padding: 5px 7px;

        font-size: 11px;
        min-height: 30px;
    }

    .ehtw-detail-post {
        padding: 12px;
    }

    .ehtw-detail-post #gn {
        font-size: 18px !important;
    }

    .ehtw-detail-cover #gd1 > div {
        height: 420px !important;
        min-height: 260px;
    }

    .ehtw-detail-tags #taglist td.tc {
        width: 85px;
        font-size: 11px;
    }

    .ehtw-detail-tags #taglist td {
        padding: 4px !important;
    }

    .ehtw-comments-section #cdiv .c1 {
        padding-left: 62px !important;
    }

    .ehtw-comments-section #cdiv .c1::before {
        left: 12px;
        width: 38px;
        height: 38px;
    }

    .ehtw-comments-section #cdiv .c1 .c2 {
        gap: 3px 8px;
    }

    .ehtw-comments-section #cdiv .c1 .c3,
    .ehtw-comments-section #cdiv .c1 .c4 {
        max-width: 100% !important;
        white-space: normal !important;
    }

    #ehtw-mobile-search {
        padding: 12px;

        border-bottom: 1px solid var(--ehtw-border);
    }

    #ehtw-mobile-search #searchbox {
        width: 100% !important;
        min-width: 0 !important;
        max-width: 100% !important;

        height: auto !important;

        position: static !important;

        margin: 0 !important;
        padding: 0 !important;

        background: transparent !important;
        border: none !important;
    }

    #ehtw-mobile-search form {
        display: flex;
        flex-wrap: wrap;

        gap: 6px;

        width: 100%;
        min-width: 0;
        max-width: 100%;

        padding: 0;

        border-radius: 0;
        background: transparent !important;
    }

    #ehtw-mobile-operations {
        width: 100%;
        max-width: 100%;
        min-width: 0;
        padding: 0;
    }

    html.ehtw-other .ehtw-other-content .itg.gld {
        grid-template-columns:
            repeat(auto-fill, minmax(min(180px, 100%), 1fr)) !important;
    }

    html.ehtw-detail #ehtw-center .ehtw-gallery-pagination {
        padding: 10px 8px;
    }

    /* 上下分页均保留 32px 胶囊高度。 */

    html.ehtw-detail #ehtw-center .ehtw-gallery-pagination table.ptt tr td,
    html.ehtw-detail #ehtw-center .ehtw-gallery-pagination table.ptb tr td {
        min-width: 30px !important;
        height: 32px !important;
        min-height: 32px !important;
    }

    html.ehtw-detail #ehtw-center .ehtw-gallery-pagination table.ptt tr td a,
    html.ehtw-detail #ehtw-center .ehtw-gallery-pagination table.ptb tr td a {
        min-width: 30px !important;
        height: 32px !important;

        padding: 0 8px !important;
    }
}

/* ============================================================
   移动端 Minimal / Minimal+ 隐藏时间和上传者列
   ============================================================ */

@media (max-width: 699px) {
    html.ehtw-other #ehtw-center .ehtw-other-content table.itg.gltm .gl2m,
    html.ehtw-other #ehtw-center .ehtw-other-content table.itg.gltm .gl5m {
        display: none !important;

        width: 0 !important;
        min-width: 0 !important;
        max-width: 0 !important;

        padding: 0 !important;
        border: none !important;
    }

    html.ehtw-other #ehtw-center .ehtw-other-content table.itg.gltm tr > th:nth-child(2),
    html.ehtw-other #ehtw-center .ehtw-other-content table.itg.gltm tr > th:nth-child(5) {
        display: none !important;
    }

    html.ehtw-other #ehtw-center .ehtw-other-content table.itg.gltm .gl3m {
        width: 100% !important;
        max-width: 0 !important;
    }
}

/* ============================================================
   固定宽度兼容
   ============================================================ */

html.ehtw-enabled #ehtw-shell,
html.ehtw-enabled #ehtw-shell > *,
html.ehtw-enabled .ehtw-timeline,
html.ehtw-enabled .ehtw-tweet-content,
html.ehtw-enabled .ehtw-detail-post {
    min-width: 0;
}

html.ehtw-enabled #ehtw-center .ido,
html.ehtw-enabled #ehtw-right .ido,
html.ehtw-enabled #ehtw-mobile-search .ido {
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
}

html.ehtw-enabled #ehtw-search-card .idi,
html.ehtw-enabled #ehtw-mobile-search .idi {
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
}

html.ehtw-enabled #ehtw-center img,
html.ehtw-enabled #ehtw-right img {
    max-width: 100%;
}

html.ehtw-enabled #ehtw-center pre {
    max-width: 100%;
    overflow-x: auto;
}

html.ehtw-enabled #ehtw-center textarea,
html.ehtw-enabled #ehtw-center input,
html.ehtw-enabled #ehtw-center select {
    max-width: 100%;
}

html.ehtw-enabled #ehtw-center select {
    -webkit-appearance: none !important;
    appearance: none !important;
    border-radius: 12px !important;
}

@media (max-width: 699px) {
    html.ehtw-enabled #ehtw-shell,
    html.ehtw-enabled #ehtw-center,
    html.ehtw-enabled .ehtw-timeline {
        width: 100% !important;
        min-width: 0 !important;
        max-width: 100% !important;
    }

    html.ehtw-enabled #ehtw-center .gm,
    html.ehtw-enabled #ehtw-center .ido,
    html.ehtw-enabled #ehtw-center #cdiv {
        min-width: 0 !important;
        max-width: 100% !important;
    }
}

/* ============================================================
   移动端页脚
   ============================================================ */

#ehtw-mobile-footer {
    display: none;
}

@media (max-width: 699px) {
    #ehtw-mobile-footer {
        display: block;

        width: 100%;
        min-width: 0;
        max-width: 100%;

        margin-top: 12px;
        padding: 12px 16px 22px;

        border-top: 1px solid var(--ehtw-border);

        color: var(--ehtw-muted);

        font-size: 11px;
        text-align: left !important;
    }

    #ehtw-mobile-footer .ehtw-footer {
        width: 100%;
        min-width: 0;
        max-width: 100%;

        padding: 0;
        margin: 0;

        border: none;
    }

    #ehtw-mobile-footer .dp {
        display: block !important;

        position: static !important;
        float: none !important;

        width: 100% !important;
        min-width: 0 !important;
        max-width: 100% !important;

        height: auto !important;

        margin: 0 !important;
        padding: 0 !important;

        background: transparent !important;
        border: none !important;

        color: var(--ehtw-muted) !important;

        font-size: 11px !important;
        line-height: 1.6 !important;

        text-align: left !important;

        white-space: normal !important;
        overflow-wrap: anywhere;
    }

    #ehtw-mobile-footer .dp a {
        display: inline !important;

        color: var(--ehtw-muted) !important;
        background: transparent !important;

        font-size: 11px !important;
        text-decoration: none !important;
    }

    #ehtw-mobile-footer .dp a:hover {
        color: var(--ehtw-accent) !important;
        text-decoration: underline !important;
    }
}

/* 系统减少动画偏好 */

@media (prefers-reduced-motion: reduce) {
    #ehtw-shell *,
    #ehtw-shell *::before,
    #ehtw-shell *::after {
        transition-duration: 0.01ms !important;
        animation-duration: 0.01ms !important;
    }
}
/* 1. Thumbnail:恢复评分精灵图的固定尺寸。 */
html.ehtw-other #ehtw-center .ehtw-other-content .itg.gld .gl1t .gl5t > div > div.ir {
    position: static !important;
    width: 80px !important;
    min-width: 80px !important;
    max-width: 80px !important;
    height: 16px !important;
    min-height: 16px !important;
    max-height: 16px !important;
    flex: 0 0 80px !important;
    flex-shrink: 0 !important;
    padding: 0 !important;
    transform: none !important;
    overflow: visible !important;
}

/* 2. Extended:恢复两列响应式布局。 */
html.ehtw-other #ehtw-center .ehtw-other-content table.itg.glte {
    display: block !important;
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
    table-layout: auto !important;
    border-collapse: separate !important;
    background: transparent !important;
}

html.ehtw-other #ehtw-center .ehtw-other-content table.itg.glte > tbody {
    display: block !important;
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
}

html.ehtw-other #ehtw-center .ehtw-other-content table.itg.glte tr {
    display: grid !important;
    grid-template-columns: 150px minmax(0, 1fr) !important;
    gap: 12px !important;
    align-items: start !important;
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
    padding: 12px 0 !important;
    border: 0 !important;
    border-bottom: 1px solid var(--ehtw-border) !important;
    background: transparent !important;
}

html.ehtw-other #ehtw-center .ehtw-other-content table.itg.glte td.gl1e {
    display: block !important;
    width: 150px !important;
    min-width: 0 !important;
    max-width: 150px !important;
    height: auto !important;
    padding: 0 !important;
    overflow: hidden !important;
    background: transparent !important;
}

html.ehtw-other #ehtw-center .ehtw-other-content table.itg.glte td.gl1e > div {
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
    height: auto !important;
    background: transparent !important;
}

html.ehtw-other #ehtw-center .ehtw-other-content table.itg.glte td.gl1e img {
    display: block;
    max-width: 100% !important;
    height: auto !important;
    object-fit: contain;
}

html.ehtw-other #ehtw-center .ehtw-other-content table.itg.glte td.gl2e {
    display: block !important;
    width: auto !important;
    min-width: 0 !important;
    max-width: 100% !important;
    height: auto !important;
    padding: 0 !important;
    overflow: hidden !important;
    background: transparent !important;
}

html.ehtw-other #ehtw-center .ehtw-other-content table.itg.glte td.gl2e > div {
    display: block !important;
    position: static !important;
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
    height: auto !important;
    background: transparent !important;
}

html.ehtw-other #ehtw-center .ehtw-other-content table.itg.glte .gl3e {
    display: flex !important;
    position: static !important;
    float: none !important;
    flex-wrap: wrap !important;
    align-items: center !important;
    gap: 6px !important;
    width: auto !important;
    min-width: 0 !important;
    max-width: 100% !important;
    height: auto !important;
    min-height: 0 !important;
    max-height: none !important;
    margin: 0 0 8px !important;
    padding: 0 !important;
    background: transparent !important;
    overflow: visible !important;
}

html.ehtw-other #ehtw-center .ehtw-other-content table.itg.glte .gl3e > div {
    position: static !important;
    float: none !important;
    top: auto !important;
    left: auto !important;
    right: auto !important;
    bottom: auto !important;
    width: auto !important;
    min-width: 0 !important;
    max-width: 100% !important;
    height: auto !important;
    min-height: 0 !important;
    margin: 0 !important;
    padding: 0 !important;
    transform: none !important;
}

html.ehtw-other #ehtw-center .ehtw-other-content table.itg.glte .gl3e > .cn {
    display: inline-flex !important;
    align-items: center !important;
    flex: 0 0 auto !important;
    width: auto !important;
    padding: 3px 8px !important;
    border-radius: 9999px !important;
    color: #f1f1f1 !important;
}

html.ehtw-other #ehtw-center .ehtw-other-content table.itg.glte .gl3e > .ir {
    display: block !important;
    flex: 0 0 80px !important;
    width: 80px !important;
    min-width: 80px !important;
    max-width: 80px !important;
    height: 16px !important;
    min-height: 16px !important;
    max-height: 16px !important;
    overflow: visible !important;
}

html.ehtw-other #ehtw-center .ehtw-other-content table.itg.glte .gl3e > .gldown {
    display: inline-flex !important;
    flex: 0 0 15px !important;
    align-items: center !important;
    justify-content: center !important;
    width: 15px !important;
    min-width: 15px !important;
    max-width: 15px !important;
    height: 15px !important;
    min-height: 15px !important;
    max-height: 15px !important;
}

html.ehtw-other #ehtw-center .ehtw-other-content table.itg.glte .gl3e > .gldown img {
    width: 15px !important;
    max-width: 15px !important;
    height: 15px !important;
    max-height: 15px !important;
}

html.ehtw-other #ehtw-center .ehtw-other-content table.itg.glte .gl4e {
    display: block !important;
    position: static !important;
    width: auto !important;
    min-width: 0 !important;
    max-width: 100% !important;
    height: auto !important;
    min-height: 0 !important;
    max-height: none !important;
    margin: 0 !important;
    padding: 0 !important;
    overflow-wrap: anywhere;
    background: transparent !important;
}

@media (max-width: 699px) {
    html.ehtw-other #ehtw-center .ehtw-other-content table.itg.glte tr {
        grid-template-columns: 100px minmax(0, 1fr) !important;
    }

    html.ehtw-other #ehtw-center .ehtw-other-content table.itg.glte td.gl1e {
        width: 100px !important;
        max-width: 100px !important;
    }
}
/* ============================================================
   Twitter/X 视觉规格覆盖层 0.7.0
   不覆盖非推文列表四种显示模式的原有布局。
   ============================================================ */

html.ehtw-enabled,
html.ehtw-enabled body {
    font-family:
        TwitterChirp,
        -apple-system,
        BlinkMacSystemFont,
        "Segoe UI",
        Roboto,
        Helvetica,
        Arial,
        sans-serif !important;
}

html.ehtw-enabled {
    --ehtw-card: var(--ehtw-bg);
}

html.ehtw-enabled .ehtw-side-card {
    background: transparent !important;
    border: 1px solid var(--ehtw-border) !important;
    border-radius: 16px !important;
    box-shadow: none !important;
}

.ehtw-svg-icon {
    display: block;
    width: 24px;
    height: 24px;
    flex: 0 0 auto;
    fill: currentColor;
    color: inherit;
}

/* ============================================================
   左栏
   ============================================================ */

#ehtw-brand {
    display: none !important;
}

#ehtw-left .ehtw-brand-link {
    width: auto;
    min-width: 52px;
    max-width: 100%;
    height: 52px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 9999px;
    margin: 0 0 8px 8px;
    padding: 0 14px;
    color: var(--ehtw-text) !important;
    font-size: 15px;
    font-weight: 700;
    white-space: nowrap;
    text-decoration: none;
}

#ehtw-left .ehtw-brand-link:hover {
    background: var(--ehtw-nav-hover);
}

#ehtw-left .ehtw-nav-hint {
    display: none !important;
}

#ehtw-left #nb .ehtw-nav-entry {
    display: block !important;
    width: 100% !important;
    min-width: 0 !important;
}

#ehtw-left #nb .ehtw-nav-link,
#ehtw-left #nb .ehtw-more-button {
    display: inline-flex !important;
    align-items: center !important;
    justify-content: flex-start !important;
    gap: 20px !important;
    min-height: 50px !important;
    padding: 12px !important;
    border-radius: 9999px !important;
    color: var(--ehtw-text) !important;
    font-size: 20px !important;
    font-weight: 400 !important;
    white-space: nowrap !important;
    text-decoration: none !important;
    background: transparent !important;
    border: none !important;
    cursor: pointer;
}

#ehtw-left #nb .ehtw-nav-link .ehtw-svg-icon,
#ehtw-left #nb .ehtw-more-button .ehtw-svg-icon {
    width: 26px !important;
    height: 26px !important;
}

#ehtw-left #nb .ehtw-nav-current .ehtw-nav-link {
    font-weight: 700 !important;
}

#ehtw-left #nb .ehtw-nav-link:hover,
#ehtw-left #nb .ehtw-more-button:hover {
    background: var(--ehtw-nav-hover) !important;
}

#ehtw-left #nb .ehtw-nav-extra {
    display: none !important;
}

.ehtw-mobile-account-entry {
    display: none !important;
}

html.ehtw-enabled #ehtw-left #nb > .ehtw-mobile-account-entry {
    display: none !important;
}

#ehtw-left #nb.ehtw-nav-open .ehtw-nav-extra {
    display: block !important;
}

#ehtw-left #nb .ehtw-more-entry {
    display: block;
    width: 100%;
}

#ehtw-left #nb .ehtw-more-button {
    font-family: inherit;
}

#ehtw-left .ehtw-post-button {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 90%;
    height: 52px;
    margin: 18px auto 0;
    padding: 0 16px;
    border-radius: 9999px;
    background: var(--ehtw-text) !important;
    color: var(--ehtw-bg) !important;
    font-size: 17px;
    font-weight: 700;
    text-decoration: none;
}

#ehtw-left .ehtw-post-button:hover {
    opacity: 0.88;
    color: var(--ehtw-bg) !important;
}

/* ============================================================
   顶部 Tab
   ============================================================ */

#ehtw-header.ehtw-tabs-header {
    display: grid !important;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    height: 53px;
    min-height: 53px;
    padding: 0 !important;
}

#ehtw-header .ehtw-top-tab {
    display: flex;
    align-items: center;
    justify-content: center;
    position: relative;
    height: 53px;
    min-width: 0;
    color: var(--ehtw-muted) !important;
    text-decoration: none !important;
    font-size: 15px;
    font-weight: 500;
}

@media (hover: hover) and (pointer: fine) {
    #ehtw-header .ehtw-top-tab:hover {
        background: var(--ehtw-nav-hover);
    }
}

#ehtw-header .ehtw-top-tab.ehtw-tab-active {
    color: var(--ehtw-text) !important;
    font-weight: 700;
}

#ehtw-header .ehtw-top-tab.ehtw-tab-active .ehtw-top-tab-label::after {
    content: "";
    position: absolute;
    bottom: 0;
    left: 0;
    width: 100%;
    height: 4px;
    border-radius: 9999px;
    background: var(--ehtw-accent);
}

#ehtw-header .ehtw-top-tab-label {
    position: relative;
    display: flex;
    align-items: center;
    height: 100%;
    white-space: nowrap;
}

@media (hover: none), (pointer: coarse) {
    html.ehtw-enabled #ehtw-header .ehtw-top-tab:is(:hover, :focus, :active):not(.ehtw-tab-active),
    html.ehtw-enabled.ehtw-touch-ui #ehtw-header .ehtw-top-tab:is(:hover, :focus, :active):not(.ehtw-tab-active) {
        background: transparent !important;
        color: var(--ehtw-muted) !important;
    }
}

html.ehtw-enabled.ehtw-touch-ui #ehtw-header .ehtw-top-tab:is(:hover, :focus, :active):not(.ehtw-tab-active) {
    background: transparent !important;
    color: var(--ehtw-muted) !important;
}

/* 触屏下激活的 tab 也不要残留 hover/focus 背景(返回后手指位置恰好落在 For you 上) */
html.ehtw-enabled.ehtw-touch-ui #ehtw-header .ehtw-top-tab.ehtw-tab-active:is(:hover, :focus, :active) {
    background: transparent !important;
}

#ehtw-header .ehtw-back-link {
    width: 34px;
    height: 34px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    color: var(--ehtw-text) !important;
    margin-right: 24px;
}

#ehtw-header .ehtw-back-link:hover {
    background: var(--ehtw-nav-hover);
}

#ehtw-header .ehtw-back-link svg {
    width: 20px;
    height: 20px;
}

#ehtw-header .ehtw-detail-header-title {
    margin: 0;
    font-size: 20px;
    font-weight: 800;
    color: var(--ehtw-text);
}

/* 上分页改为低调工具栏。 */

.ehtw-pagination.ehtw-top-toolbar {
    min-height: 40px;
    padding: 4px 12px;
    border-top: none;
    border-bottom: 1px solid var(--ehtw-border);
}

.ehtw-pagination.ehtw-top-toolbar .searchnav {
    min-height: 32px;
    justify-content: flex-start;
    color: var(--ehtw-muted) !important;
    font-size: 13px;
}

.ehtw-pagination.ehtw-top-toolbar .searchnav a,
.ehtw-pagination.ehtw-top-toolbar .searchnav select,
.ehtw-pagination.ehtw-top-toolbar .searchnav span[id] {
    min-height: 28px;
    padding: 2px 6px;
    background: transparent !important;
    border: none !important;
    border-radius: 0 !important;
    color: var(--ehtw-muted) !important;
    font-size: 13px;
}

/* ============================================================
   搜索与 Filters
   ============================================================ */

#ehtw-search-card {
    position: sticky;
    top: 0;
    z-index: 110;
    padding-top: 2px;
    background: var(--ehtw-bg);
}

.ehtw-search-input-wrap {
    display: block;
    position: relative;
    width: 100%;
    min-width: 0;
    flex: 1 1 100%;
}

.ehtw-search-input-wrap .ehtw-search-icon {
    position: absolute;
    top: 50%;
    left: 16px;
    z-index: 1;
    width: 20px;
    height: 20px;
    color: var(--ehtw-muted);
    transform: translateY(-50%);
    pointer-events: none;
}

#ehtw-search-card #f_search,
#ehtw-mobile-search #f_search {
    height: 42px !important;
    min-height: 42px !important;
    margin: 0 !important;
    padding-left: 44px !important;
}

#ehtw-search-card #searchbox.ehtw-filters-collapsed form > :not(.ehtw-search-input-wrap),
#ehtw-mobile-search #searchbox.ehtw-filters-collapsed form > :not(.ehtw-search-input-wrap) {
    display: none !important;
}

#ehtw-search-card #searchbox.ehtw-filters-collapsed .ehtw-search-input-wrap,
#ehtw-mobile-search #searchbox.ehtw-filters-collapsed .ehtw-search-input-wrap {
    display: block !important;
}

#ehtw-search-card #searchbox.ehtw-filters-collapsed.ehtw-filters-open form > *,
#ehtw-mobile-search #searchbox.ehtw-filters-collapsed.ehtw-filters-open form > * {
    display: revert !important;
}

#ehtw-search-card #searchbox.ehtw-filters-open .ehtw-search-input-wrap,
#ehtw-mobile-search #searchbox.ehtw-filters-open .ehtw-search-input-wrap {
    display: block !important;
}

.ehtw-filters-toggle {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    height: 30px;
    margin-top: 6px;
    padding: 0 4px;
    border: none;
    background: transparent;
    color: var(--ehtw-accent);
    font-size: 13px;
    font-weight: 500;
    cursor: pointer;
}

.ehtw-filters-toggle svg {
    width: 16px;
    height: 16px;
}

#ehtw-search-card table.itc .cs,
#ehtw-search-card table.itc .cn,
#ehtw-mobile-search table.itc .cs,
#ehtw-mobile-search table.itc .cn {
    height: 28px !important;
    min-height: 28px !important;
    padding: 4px 8px !important;
    font-size: 13px !important;
    border-radius: 8px !important;
}

/* ============================================================
   Trending
   ============================================================ */

.ehtw-trending-card .ehtw-side-title {
    font-size: 20px;
    font-weight: 800;
    padding-top: 4px;
}

.ehtw-trending-card .ehtw-trend {
    padding: 12px 16px;
}

.ehtw-trending-card .ehtw-trend-category,
.ehtw-trending-card .ehtw-trend-count {
    font-size: 13px;
    color: var(--ehtw-muted);
}

.ehtw-trending-card .ehtw-trend-name {
    font-size: 15px;
    font-weight: 700;
    margin: 2px 0;
}

.ehtw-trending-card .ehtw-trend-extra {
    display: none;
}

.ehtw-trending-card.ehtw-trending-expanded .ehtw-trend-extra {
    display: block;
}

.ehtw-trending-more {
    display: block;
    width: 100%;
    min-height: 48px;
    padding: 12px 16px;
    text-align: left;
    border: none;
    border-top: 1px solid var(--ehtw-border);
    background: transparent;
    color: var(--ehtw-accent);
    font-size: 15px;
    cursor: pointer;
}

.ehtw-trending-more:hover {
    background: var(--ehtw-nav-hover);
}

/* 页脚转移到右栏。 */

#ehtw-right .ehtw-footer,
#ehtw-mobile-footer .ehtw-footer {
    margin: 12px 0 24px;
    padding: 8px 12px;
    border: none;
    background: transparent;
    color: var(--ehtw-muted);
    font-size: 13px;
    line-height: 1.6;
}

#ehtw-right .ehtw-footer .dp,
#ehtw-mobile-footer .ehtw-footer .dp {
    position: static !important;
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
    margin: 0 !important;
    padding: 0 !important;
    background: transparent !important;
    border: none !important;
    color: var(--ehtw-muted) !important;
    font-size: 13px !important;
    white-space: normal !important;
}

#ehtw-right .ehtw-footer .dp a,
#ehtw-mobile-footer .ehtw-footer .dp a {
    color: var(--ehtw-muted) !important;
    font-size: 13px !important;
}

/* ============================================================
   Twitter 推文
   ============================================================ */

html.ehtw-list .ehtw-tweet {
    display: grid !important;
    grid-template-columns: 40px minmax(0, 1fr) !important;
    gap: 12px !important;
    padding: 12px 16px !important;
    border: none !important;
    border-bottom: 1px solid var(--ehtw-border) !important;
    border-radius: 0 !important;
    background: transparent !important;
}

html.ehtw-list .ehtw-tweet:hover {
    background: var(--ehtw-hover) !important;
}

html.ehtw-list .ehtw-avatar,
html.ehtw-detail .ehtw-avatar {
    width: 40px !important;
    height: 40px !important;
    min-width: 40px !important;
    min-height: 40px !important;
    max-width: 40px !important;
    max-height: 40px !important;
    flex: 0 0 40px !important;
    border-radius: 50% !important;
    background-color: var(--ehtw-avatar-solid, #536471) !important;
    background-image: none !important;
    border: none !important;
    font-size: 15px !important;
    font-weight: 700 !important;
}

html.ehtw-list .ehtw-tweet-author {
    display: flex !important;
    align-items: center !important;
    flex-wrap: nowrap !important;
    gap: 4px !important;
    height: 20px;
    min-height: 20px;
    margin: 0 0 2px !important;
    line-height: 20px;
    overflow: hidden;
}

html.ehtw-list .ehtw-author-name {
    flex: 0 1 auto;
    min-width: 0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    font-size: 15px;
    font-weight: 700;
}

html.ehtw-list .ehtw-handle {
    flex: 1 1 auto;
    min-width: 0;
    max-width: 140px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    font-size: 15px;
}

html.ehtw-list .ehtw-dot,
html.ehtw-list .ehtw-time {
    flex: 0 0 auto;
    white-space: nowrap;
    font-size: 15px;
    color: var(--ehtw-muted) !important;
}

html.ehtw-list .ehtw-tweet-author .cn {
    display: inline !important;
    flex: 0 1 auto !important;
    width: auto !important;
    min-width: 0 !important;
    height: auto !important;
    margin: 0 !important;
    padding: 0 !important;
    border: none !important;
    border-radius: 0 !important;
    background: transparent !important;
    background-image: none !important;
    color: var(--ehtw-muted) !important;
    font-size: 15px !important;
    font-weight: 400 !important;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.ehtw-tweet-more {
    flex: 0 0 18px;
    width: 18px;
    height: 18px;
    margin-left: auto;
    color: var(--ehtw-muted);
}

.ehtw-tweet-more svg {
    width: 18px;
    height: 18px;
}

html.ehtw-list .ehtw-tweet-title {
    margin: 0 !important;
    font-size: 15px !important;
    line-height: 20px !important;
}

html.ehtw-list .ehtw-tweet-title .glink {
    font-size: 15px !important;
    line-height: 20px !important;
}

html.ehtw-list .ehtw-tags {
    display: block !important;
    width: 100%;
    margin: 0 !important;
    padding: 0 !important;
    line-height: 20px;
}

html.ehtw-list .ehtw-tags .ehtw-hashtag {
    display: inline !important;
    margin: 0 6px 0 0 !important;
    padding: 0 !important;
    border: none !important;
    border-radius: 0 !important;
    background: transparent !important;
    font-size: 15px !important;
    font-weight: 400 !important;
    line-height: 20px !important;
}

html.ehtw-list .ehtw-tags .ehtw-hashtag::before {
    content: none !important;
}

html.ehtw-list .ehtw-tags .ehtw-hashtag a {
    font-size: 15px !important;
    font-weight: 400 !important;
}

html.ehtw-list .ehtw-media {
    width: 100%;
    max-width: 100%;
    margin-top: 12px !important;
    border: 1px solid var(--ehtw-border) !important;
    border-radius: 16px !important;
    overflow: hidden;
}

html.ehtw-list .ehtw-media img {
    max-height: 510px !important;
}

/* ============================================================
   Twitter 操作栏
   ============================================================ */

.ehtw-twitter-actions {
    display: flex;
    align-items: center;
    justify-content: space-between;
    width: 100%;
    max-width: 425px;
    margin-top: 12px;
    gap: 4px;
}

.ehtw-twitter-action {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 4px;
    min-width: 0;
    height: 34px;
    padding: 0;
    color: var(--ehtw-muted) !important;
    text-decoration: none !important;
    font-size: 13px;
    cursor: pointer;
}

.ehtw-twitter-action-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    flex: 0 0 34px;
    border-radius: 50%;
    color: inherit;
    transition: background 0.15s ease;
}

.ehtw-twitter-action-icon .ehtw-svg-icon {
    width: 18.75px;
    height: 18.75px;
}

.ehtw-twitter-action .ehtw-action-number {
    color: var(--ehtw-muted);
    font-size: 13px;
    white-space: nowrap;
}

.ehtw-twitter-action:hover,
.ehtw-twitter-action-reply:hover,
.ehtw-twitter-action-views:hover,
.ehtw-twitter-action-share:hover {
    color: var(--ehtw-accent) !important;
}

.ehtw-twitter-action-reply:hover .ehtw-twitter-action-icon,
.ehtw-twitter-action-views:hover .ehtw-twitter-action-icon,
.ehtw-twitter-action-share:hover .ehtw-twitter-action-icon {
    background: rgba(29, 155, 240, 0.1);
}

.ehtw-twitter-action-repost:hover {
    color: #00ba7c !important;
}

.ehtw-twitter-action-repost:hover .ehtw-twitter-action-icon {
    background: rgba(0, 186, 124, 0.1);
}

.ehtw-twitter-action-heart:hover {
    color: #f91880 !important;
}

.ehtw-twitter-action-heart:hover .ehtw-twitter-action-icon {
    background: rgba(249, 24, 128, 0.1);
}

.ehtw-twitter-action-heart .ir,
.ehtw-twitter-action-views > div:not(.ehtw-twitter-action-icon) {
    display: none !important;
}

.ehtw-twitter-action-repost .ehtw-repost-native {
    position: absolute !important;
    inset: 0 !important;
    display: block !important;
    width: 100% !important;
    height: 100% !important;
    padding: 0 !important;
    margin: 0 !important;
    cursor: pointer;
}

.ehtw-twitter-action-repost .ehtw-repost-native img {
    position: absolute !important;
    inset: 0 !important;
    width: 100% !important;
    height: 100% !important;
    max-width: none !important;
    max-height: none !important;
    opacity: 0 !important;
    cursor: pointer;
}

/* ============================================================
   详情页
   ============================================================ */

html.ehtw-detail .ehtw-detail-post {
    padding: 12px 16px 16px;
}

html.ehtw-detail .ehtw-detail-author {
    display: flex;
    align-items: center;
    gap: 12px;
    min-height: 40px;
    margin-bottom: 14px;
}

html.ehtw-detail .ehtw-detail-author-meta {
    gap: 0;
    min-width: 0;
}

html.ehtw-detail .ehtw-detail-author-meta .ehtw-author-name {
    font-size: 15px;
    font-weight: 700;
    line-height: 20px;
}

html.ehtw-detail .ehtw-detail-author-meta .ehtw-handle {
    font-size: 15px;
    line-height: 20px;
}

.ehtw-detail-more {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    margin-left: auto;
    color: var(--ehtw-muted);
    border-radius: 50%;
}

.ehtw-detail-more svg {
    width: 20px;
    height: 20px;
}

.ehtw-detail-post #gn {
    margin: 0 0 4px !important;
    font-size: 17px !important;
    font-weight: 400 !important;
    line-height: 24px !important;
}

.ehtw-detail-post #gj {
    margin: 0 0 10px !important;
    font-size: 15px !important;
    font-weight: 400 !important;
    color: var(--ehtw-muted) !important;
    line-height: 20px !important;
}

/* 标签使用 inline hashtag,不再显示命名空间列。 */

html.ehtw-detail .ehtw-detail-tags {
    display: block !important;
    margin: 8px 0 12px !important;
    padding: 0 !important;
    line-height: 20px !important;
}

html.ehtw-detail .ehtw-detail-tags #gd4,
html.ehtw-detail .ehtw-detail-tags #taglist,
html.ehtw-detail .ehtw-detail-tags #taglist table,
html.ehtw-detail .ehtw-detail-tags #taglist tbody,
html.ehtw-detail .ehtw-detail-tags #taglist tr,
html.ehtw-detail .ehtw-detail-tags #taglist td {
    display: inline !important;
    width: auto !important;
    min-width: 0 !important;
    max-width: 100% !important;
    height: auto !important;
    margin: 0 !important;
    padding: 0 !important;
    border: none !important;
    background: transparent !important;
    vertical-align: baseline !important;
}

html.ehtw-detail .ehtw-detail-tags #taglist .tc {
    display: none !important;
}

html.ehtw-detail .ehtw-detail-tags #taglist .gt {
    display: inline !important;
    width: auto !important;
    height: auto !important;
    margin: 0 7px 0 0 !important;
    padding: 0 !important;
    border: none !important;
    border-radius: 0 !important;
    background: transparent !important;
    color: var(--ehtw-accent) !important;
    font-size: 15px !important;
    line-height: 20px !important;
}

html.ehtw-detail .ehtw-detail-tags #taglist .gt::before {
    content: none !important;
}

html.ehtw-detail .ehtw-detail-tags #taglist .gt a {
    display: inline !important;
    color: var(--ehtw-accent) !important;
    font-size: 15px !important;
    line-height: 20px !important;
}

html.ehtw-detail .ehtw-detail-cover {
    margin: 12px 0 16px;
    width: 100%;
    border-radius: 16px;
    border: 1px solid var(--ehtw-border);
}

html.ehtw-detail .ehtw-detail-timestamp {
    padding: 12px 0;
    color: var(--ehtw-muted);
    font-size: 15px;
    line-height: 20px;
    border-bottom: 1px solid var(--ehtw-border);
}

html.ehtw-detail .ehtw-detail-stats {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px 18px;
    padding: 12px 0;
    border-bottom: 1px solid var(--ehtw-border);
}

html.ehtw-detail .ehtw-detail-stat {
    display: inline-flex;
    align-items: baseline;
    gap: 5px;
    font-size: 15px;
}

html.ehtw-detail .ehtw-detail-stat strong {
    color: var(--ehtw-text);
    font-weight: 700;
}

html.ehtw-detail .ehtw-detail-stat span {
    color: var(--ehtw-muted);
}

html.ehtw-detail .ehtw-detail-native-meta {
    display: none !important;
}

html.ehtw-detail .ehtw-detail-meta {
    padding: 0 !important;
    margin-top: 10px;
    border-top: none !important;
    border-bottom: none !important;
}

html.ehtw-detail .ehtw-detail-actions {
    display: flex;
    justify-content: space-around;
    align-items: center;
    min-height: 50px;
    padding: 8px 0;
    border-top: 1px solid var(--ehtw-border);
    border-bottom: 1px solid var(--ehtw-border);
}

html.ehtw-detail .ehtw-detail-rating-action,
html.ehtw-detail .ehtw-detail-favorite-action {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    min-width: 0;
    color: var(--ehtw-muted);
}

html.ehtw-detail .ehtw-detail-rating-action > svg,
html.ehtw-detail .ehtw-detail-favorite-action > svg {
    width: 20px;
    height: 20px;
    flex: 0 0 20px;
}

html.ehtw-detail .ehtw-detail-rating-action #gdr,
html.ehtw-detail .ehtw-detail-favorite-action #gdf {
    display: block !important;
    width: auto !important;
    min-width: 0 !important;
    max-width: 100% !important;
    height: auto !important;
    background: transparent !important;
    cursor: pointer;
}

html.ehtw-detail .ehtw-about-card #gdd {
    display: block !important;
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
    padding: 0 12px 12px !important;
    background: transparent !important;
    font-size: 13px;
}

html.ehtw-detail .ehtw-operation-card #gd5 {
    padding: 0 16px 14px !important;
    font-size: 15px !important;
    line-height: 1.7 !important;
}

html.ehtw-detail .ehtw-gallery-grid-section #gdt {
    display: grid !important;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 2px !important;
    width: 100% !important;
    text-align: left !important;
}

html.ehtw-detail .ehtw-gallery-grid-section #gdt a {
    display: block !important;
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
    margin: 0 !important;
    padding: 0 !important;
    overflow: hidden;
    border-radius: 4px;
}

html.ehtw-detail .ehtw-gallery-grid-section #gdt img {
    display: block;
    width: 100% !important;
    max-width: 100% !important;
    height: auto !important;
    border-radius: 4px;
}

/* ============================================================
   回复
   ============================================================ */

html.ehtw-detail .ehtw-comments-section #cdiv .c1.ehtw-comment-tweet {
    display: grid !important;
    grid-template-columns: 40px minmax(0, 1fr);
    column-gap: 12px;
    padding: 12px 16px !important;
    border-bottom: 1px solid var(--ehtw-border) !important;
    background: transparent !important;
}

html.ehtw-detail .ehtw-comments-section #cdiv .c1.ehtw-comment-tweet::before {
    content: none !important;
}

.ehtw-comment-avatar {
    display: flex;
    grid-column: 1;
    grid-row: 1 / span 3;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    border-radius: 50%;
    background: #536471;
    color: #ffffff;
    font-size: 15px;
    font-weight: 700;
}

.ehtw-comment-heading {
    grid-column: 2;
    display: flex;
    flex-wrap: nowrap;
    align-items: center;
    gap: 4px;
    height: 20px;
    min-width: 0;
    overflow: hidden;
    white-space: nowrap;
    font-size: 15px;
}

.ehtw-comment-author {
    display: inline-block;
    min-width: 0;
    max-width: 45%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 15px;
    font-weight: 700;
}

.ehtw-comment-author a {
    color: var(--ehtw-text) !important;
    font-size: 15px !important;
    font-weight: 700 !important;
}

.ehtw-comment-handle {
    min-width: 0;
    flex: 0 1 auto;
    overflow: hidden;
    text-overflow: ellipsis;
    color: var(--ehtw-muted);
    font-size: 15px;
}

.ehtw-comment-time {
    flex: 0 0 auto;
    color: var(--ehtw-muted);
    font-size: 15px;
}

html.ehtw-detail .ehtw-comments-section #cdiv .c1.ehtw-comment-tweet .c2 {
    grid-column: 2;
    grid-row: 2;
    display: flex !important;
    align-items: center;
    justify-content: flex-end;
    margin: 0 !important;
    min-height: 0;
}

html.ehtw-detail .ehtw-comments-section #cdiv .c1.ehtw-comment-tweet .c3 {
    display: none !important;
}

html.ehtw-detail .ehtw-comments-section #cdiv .c1.ehtw-comment-tweet .c4 {
    color: var(--ehtw-muted) !important;
    font-size: 13px !important;
}

html.ehtw-detail .ehtw-comments-section #cdiv .c1.ehtw-comment-tweet .c6 {
    grid-column: 2;
    grid-row: 3;
    font-size: 15px;
    line-height: 20px;
}

html.ehtw-detail .ehtw-comments-section #cdiv .c1.ehtw-comment-tweet .c7 {
    grid-column: 2;
    grid-row: 4;
}

/* ============================================================
   手机底栏
   ============================================================ */

@media (max-width: 699px) {
    #ehtw-left {
        overflow-x: hidden !important;
        overflow-y: hidden !important;
    }

    #ehtw-left #nb {
        display: flex !important;
        flex-direction: row !important;
        flex-wrap: nowrap !important;
        align-items: center;
        justify-content: space-around;
        width: 100% !important;
        min-width: 0 !important;
        max-width: 100% !important;
        height: 100% !important;
    }

    #ehtw-left #nb .ehtw-nav-entry {
        width: auto !important;
        min-width: 0 !important;
        flex: 1 1 0;
    }

    #ehtw-left #nb .ehtw-nav-link {
        display: flex !important;
        align-items: center;
        justify-content: center !important;
        width: 100% !important;
        min-height: 48px !important;
        padding: 8px 4px !important;
        gap: 0 !important;
    }

    #ehtw-left #nb .ehtw-nav-label {
        display: none !important;
    }

    #ehtw-left #nb .ehtw-nav-link .ehtw-svg-icon {
        width: 26px !important;
        height: 26px !important;
    }

    #ehtw-left #nb .ehtw-nav-mobile-extra,
    #ehtw-left #nb .ehtw-nav-extra {
        display: none !important;
    }

    #ehtw-left #nb .ehtw-more-entry {
        display: flex !important;
        flex: 1 1 0;
        align-items: center;
        justify-content: center;
        width: auto !important;
    }

    #ehtw-left #nb .ehtw-more-button {
        display: flex !important;
        align-items: center;
        justify-content: center;
        width: 100%;
        min-height: 48px !important;
        padding: 8px 4px !important;
    }

    #ehtw-left #nb .ehtw-more-button .ehtw-svg-icon {
        width: 26px !important;
        height: 26px !important;
    }

    #ehtw-left .ehtw-brand-link,
    #ehtw-left .ehtw-post-button {
        display: none !important;
    }

    #ehtw-left:has(#nb.ehtw-nav-open) {
        height: auto !important;
        min-height: 64px !important;
        max-height: min(75vh, 560px) !important;
        overflow-y: auto !important;
    }

    #ehtw-left #nb.ehtw-nav-open {
        display: flex !important;
        flex-direction: row !important;
        flex-wrap: wrap !important;
        align-content: flex-start !important;
        min-height: 0 !important;
        height: auto !important;
        padding-top: 8px !important;
    }

    #ehtw-left #nb.ehtw-nav-open .ehtw-nav-mobile-extra,
    #ehtw-left #nb.ehtw-nav-open .ehtw-nav-extra {
        display: block !important;
        flex: 0 0 100%;
        width: 100% !important;
        order: -1;
    }

    #ehtw-left #nb.ehtw-nav-open .ehtw-nav-mobile-extra .ehtw-nav-link {
        justify-content: flex-start !important;
        gap: 16px !important;
        padding: 8px 16px !important;
    }

    #ehtw-left #nb.ehtw-nav-open .ehtw-nav-mobile-extra .ehtw-nav-label {
        display: inline !important;
        font-size: 16px;
    }

    html.ehtw-list .ehtw-tweet {
        grid-template-columns: 40px minmax(0, 1fr) !important;
        gap: 8px !important;
        padding: 12px !important;
    }

    html.ehtw-list .ehtw-tweet-author .ehtw-handle {
        max-width: 72px;
    }

    html.ehtw-list .ehtw-tweet-author .cn {
        max-width: 72px !important;
    }
}
/* ============================================================
   0.8.0 搜索框可见性修复
   ============================================================ */

/* 原站搜索输入框的父级是 form 的第二层 div。 */
#ehtw-search-card #searchbox.ehtw-filters-collapsed:not(.ehtw-filters-open) form > div:has(> .ehtw-search-input-wrap),
#ehtw-mobile-search #searchbox.ehtw-filters-collapsed:not(.ehtw-filters-open) form > div:has(> .ehtw-search-input-wrap) {
    display: block !important;
    position: relative !important;
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
    height: auto !important;
    margin: 0 !important;
    padding: 0 !important;
    background: transparent !important;
    border: 0 !important;
}

/* 折叠时只隐藏 Search、Clear 等非搜索输入控件。 */
#ehtw-search-card #searchbox.ehtw-filters-collapsed:not(.ehtw-filters-open) form input[type="submit"],
#ehtw-search-card #searchbox.ehtw-filters-collapsed:not(.ehtw-filters-open) form input[type="button"],
#ehtw-mobile-search #searchbox.ehtw-filters-collapsed:not(.ehtw-filters-open) form input[type="submit"],
#ehtw-mobile-search #searchbox.ehtw-filters-collapsed:not(.ehtw-filters-open) form input[type="button"] {
    display: none !important;
}

/* 展开时恢复操作按钮;保留 form 提交行为。 */
#ehtw-search-card #searchbox.ehtw-filters-open form input[type="submit"],
#ehtw-search-card #searchbox.ehtw-filters-open form input[type="button"],
#ehtw-mobile-search #searchbox.ehtw-filters-open form input[type="submit"],
#ehtw-mobile-search #searchbox.ehtw-filters-open form input[type="button"] {
    display: inline-block !important;
}

#ehtw-search-card #searchbox,
#ehtw-mobile-search #searchbox {
    display: flex !important;
    flex-direction: column !important;
    align-items: stretch !important;
    width: 100% !important;
    min-width: 0 !important;
}

#ehtw-search-card #searchbox form,
#ehtw-mobile-search #searchbox form {
    order: 0;
    width: 100% !important;
    min-width: 0 !important;
}

#ehtw-search-card #searchbox .ehtw-filters-toggle,
#ehtw-mobile-search #searchbox .ehtw-filters-toggle {
    order: 1;
    align-self: flex-start;
    display: inline-flex !important;
    margin-top: 6px !important;
    margin-bottom: 4px !important;
}

#ehtw-search-card .ehtw-search-input-wrap,
#ehtw-mobile-search .ehtw-search-input-wrap {
    display: block !important;
    position: relative !important;
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
}

#ehtw-search-card .ehtw-search-input-wrap #f_search,
#ehtw-mobile-search .ehtw-search-input-wrap #f_search {
    display: block !important;
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
    height: 42px !important;
    padding-left: 44px !important;
}

#ehtw-mobile-search {
    width: 100%;
    min-width: 0;
    max-width: 100%;
}

/* ============================================================
   0.8.0 左栏导航左对齐
   ============================================================ */

#ehtw-left #nb {
    text-align: left !important;
    align-items: flex-start !important;
}

#ehtw-left #nb .ehtw-nav-entry,
#ehtw-left #nb .ehtw-more-entry {
    text-align: left !important;
}

#ehtw-left #nb .ehtw-nav-link,
#ehtw-left #nb .ehtw-more-button {
    display: flex !important;
    width: fit-content !important;
    max-width: 100% !important;
    justify-content: flex-start !important;
    text-align: left !important;
    margin-left: 8px !important;
    padding: 12px !important;
}

#ehtw-left .ehtw-post-button {
    width: 90% !important;
    margin: 18px 0 0 8px !important;
    text-align: center !important;
}

/* ============================================================
   0.8.0 列表标签
   ============================================================ */

html.ehtw-list .ehtw-tags .ehtw-hashtag-overflow {
    display: none !important;
}

html.ehtw-list .ehtw-tags .ehtw-hashtag {
    display: inline !important;
    width: auto !important;
    max-width: 100%;
    margin-right: 6px !important;
    padding: 0 !important;
    background: transparent !important;
    border: 0 !important;
    border-radius: 0 !important;
    color: var(--ehtw-accent) !important;
    font-size: 15px !important;
    font-weight: 400 !important;
    line-height: 20px !important;
}

html.ehtw-list .ehtw-tags .ehtw-hashtag::before {
    content: none !important;
}

html.ehtw-list .ehtw-tags .ehtw-hashtag a {
    display: inline !important;
    color: var(--ehtw-accent) !important;
    font-weight: 400 !important;
    text-decoration: none !important;
}

html.ehtw-list .ehtw-tags .ehtw-hashtag a:hover {
    text-decoration: underline !important;
}

/* ============================================================
   0.8.0 详情标签:覆盖 gt / gtl / gtw
   ============================================================ */

html.ehtw-detail .ehtw-detail-tags,
html.ehtw-detail .ehtw-detail-meta {
    display: block !important;
    clear: both !important;
    float: none !important;
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
    height: auto !important;
}

html.ehtw-detail .ehtw-detail-tags {
    position: static !important;
    margin: 8px 0 12px !important;
    padding: 0 !important;
    border: 0 !important;
    background: transparent !important;
}

html.ehtw-detail .ehtw-detail-tags #gd4,
html.ehtw-detail .ehtw-detail-tags #taglist {
    display: block !important;
    position: static !important;
    float: none !important;
    clear: both !important;
    width: auto !important;
    min-width: 0 !important;
    max-width: 100% !important;
    height: auto !important;
    min-height: 0 !important;
    max-height: none !important;
    margin: 0 !important;
    padding: 0 !important;
    border: 0 !important;
    background: transparent !important;
    overflow: visible !important;
}

html.ehtw-detail .ehtw-detail-tags #taglist table,
html.ehtw-detail .ehtw-detail-tags #taglist tbody,
html.ehtw-detail .ehtw-detail-tags #taglist tr,
html.ehtw-detail .ehtw-detail-tags #taglist td:not(.tc) {
    display: inline !important;
    position: static !important;
    float: none !important;
    width: auto !important;
    min-width: 0 !important;
    max-width: 100% !important;
    height: auto !important;
    margin: 0 !important;
    padding: 0 !important;
    border: 0 !important;
    background: transparent !important;
    vertical-align: baseline !important;
}

html.ehtw-detail .ehtw-detail-tags #taglist td.tc {
    display: none !important;
}

html.ehtw-detail .ehtw-detail-tags #taglist .gt,
html.ehtw-detail .ehtw-detail-tags #taglist .gtl,
html.ehtw-detail .ehtw-detail-tags #taglist .gtw {
    display: inline !important;
    position: static !important;
    float: none !important;
    width: auto !important;
    min-width: 0 !important;
    max-width: 100% !important;
    height: auto !important;
    min-height: 0 !important;
    max-height: none !important;
    margin: 0 7px 0 0 !important;
    padding: 0 !important;
    border: 0 !important;
    border-radius: 0 !important;
    background: transparent !important;
    background-image: none !important;
    box-shadow: none !important;
    color: var(--ehtw-accent) !important;
    font-size: 15px !important;
    font-weight: 400 !important;
    line-height: 20px !important;
    white-space: normal !important;
}

html.ehtw-detail .ehtw-detail-tags #taglist .gt::before,
html.ehtw-detail .ehtw-detail-tags #taglist .gtl::before,
html.ehtw-detail .ehtw-detail-tags #taglist .gtw::before {
    content: none !important;
}

html.ehtw-detail .ehtw-detail-tags #taglist .gt a,
html.ehtw-detail .ehtw-detail-tags #taglist .gtl a,
html.ehtw-detail .ehtw-detail-tags #taglist .gtw a {
    display: inline !important;
    padding: 0 !important;
    margin: 0 !important;
    border: 0 !important;
    background: transparent !important;
    color: var(--ehtw-accent) !important;
    font-size: 15px !important;
    font-weight: 400 !important;
    line-height: 20px !important;
    text-decoration: none !important;
}

html.ehtw-detail .ehtw-detail-tags #taglist .gt a:hover,
html.ehtw-detail .ehtw-detail-tags #taglist .gtl a:hover,
html.ehtw-detail .ehtw-detail-tags #taglist .gtw a:hover {
    text-decoration: underline !important;
}

/* ============================================================
   0.8.0 详情标题原站红线与背景
   ============================================================ */

html.ehtw-detail .ehtw-detail-post #gd2,
html.ehtw-detail .ehtw-detail-post #gn,
html.ehtw-detail .ehtw-detail-post #gj,
html.ehtw-detail .ehtw-detail-meta #gmid,
html.ehtw-detail .ehtw-detail-meta #gd3,
html.ehtw-detail .ehtw-detail-meta #gdn,
html.ehtw-detail .ehtw-detail-meta #gdc,
html.ehtw-detail .ehtw-detail-meta #gdd,
html.ehtw-detail .ehtw-detail-meta #gdr,
html.ehtw-detail .ehtw-detail-meta #gdf {
    border: none !important;
    border-top: 0 !important;
    border-bottom: 0 !important;
    background: transparent !important;
    background-image: none !important;
    box-shadow: none !important;
}

html.ehtw-detail .ehtw-detail-post #gd2,
html.ehtw-detail .ehtw-detail-post #gn,
html.ehtw-detail .ehtw-detail-post #gj {
    text-align: left !important;
}

html.ehtw-detail .ehtw-detail-post #gn {
    font-size: 17px !important;
    line-height: 24px !important;
    color: var(--ehtw-text) !important;
}

html.ehtw-detail .ehtw-detail-post #gj {
    font-size: 15px !important;
    color: var(--ehtw-muted) !important;
}

/* ============================================================
   0.8.0 推文和评论头部间距
   ============================================================ */

html.ehtw-list .ehtw-tweet-author {
    display: flex !important;
    align-items: center !important;
    justify-content: flex-start !important;
    gap: 4px !important;
}

html.ehtw-list .ehtw-tweet-author .ehtw-handle {
    flex: 0 1 auto !important;
    width: auto !important;
    min-width: 0 !important;
    max-width: 140px !important;
}

html.ehtw-list .ehtw-tweet-author .ehtw-tweet-more {
    margin-left: auto !important;
    flex: 0 0 18px;
}

html.ehtw-detail .ehtw-comment-heading {
    display: flex !important;
    align-items: center !important;
    justify-content: flex-start !important;
    gap: 4px !important;
}

html.ehtw-detail .ehtw-comment-heading .ehtw-comment-handle {
    flex: 0 1 auto !important;
    width: auto !important;
    min-width: 0 !important;
}

/* ============================================================
   0.8.0 About 卡片与 Gallery actions
   ============================================================ */

html.ehtw-detail .ehtw-about-card,
html.ehtw-detail .ehtw-about-card * {
    text-align: left !important;
}

html.ehtw-detail .ehtw-about-card #gdd {
    display: block !important;
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
    margin: 0 !important;
    padding: 0 16px 14px !important;
    background: transparent !important;
    color: var(--ehtw-text) !important;
    font-size: 14px !important;
}

html.ehtw-detail .ehtw-about-card #gdd table {
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
    margin: 0 !important;
    padding: 0 !important;
    border-collapse: collapse !important;
    background: transparent !important;
    text-align: left !important;
}

html.ehtw-detail .ehtw-about-card #gdd tr,
html.ehtw-detail .ehtw-about-card #gdd td {
    background: transparent !important;
    border-color: var(--ehtw-border) !important;
    text-align: left !important;
}

html.ehtw-detail .ehtw-about-card #gdd td {
    width: auto !important;
    height: auto !important;
    padding: 7px 4px !important;
    font-size: 14px !important;
    line-height: 20px !important;
    vertical-align: top !important;
}

html.ehtw-detail .ehtw-about-card #gdd .gdt1,
html.ehtw-detail .ehtw-about-card #gdd td:first-child {
    width: 90px !important;
    min-width: 90px !important;
    max-width: 90px !important;
    color: var(--ehtw-muted) !important;
    font-size: 14px !important;
    font-weight: 400 !important;
}

html.ehtw-detail .ehtw-operation-card #gd5 {
    display: block !important;
    width: 100% !important;
    margin: 0 !important;
    padding: 0 16px 12px !important;
    text-align: left !important;
    background: transparent !important;
    color: var(--ehtw-text) !important;
    font-size: 15px !important;
    line-height: 1.5 !important;
    font-weight: 400 !important;
}

html.ehtw-detail .ehtw-operation-card #gd5 > div,
html.ehtw-detail .ehtw-operation-card #gd5 > p {
    display: block !important;
    position: static !important;
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
    height: auto !important;
    margin: 0 !important;
    padding: 8px 0 !important;
    border: 0 !important;
    background: transparent !important;
    background-image: none !important;
    text-align: left !important;
    font-size: 15px !important;
    font-weight: 400 !important;
}

html.ehtw-detail .ehtw-operation-card #gd5 a {
    display: inline !important;
    width: auto !important;
    height: auto !important;
    margin: 0 !important;
    padding: 0 !important;
    border: none !important;
    background: transparent !important;
    background-image: none !important;
    color: var(--ehtw-text) !important;
    font-size: 15px !important;
    font-weight: 400 !important;
    line-height: 1.5 !important;
    text-decoration: none !important;
}

html.ehtw-detail .ehtw-operation-card #gd5 a:hover {
    color: var(--ehtw-text) !important;
    text-decoration: underline !important;
}

/* ============================================================
   0.8.0 评论视觉排版
   ============================================================ */

html.ehtw-detail .ehtw-comments-section > .ehtw-section-title {
    display: none !important;
}

html.ehtw-detail .ehtw-comments-section #cdiv .c1.ehtw-comment-tweet {
    position: relative !important;
    display: grid !important;
    grid-template-columns: 40px minmax(0, 1fr);
    grid-template-rows: 20px auto auto;
    column-gap: 12px;
    row-gap: 4px;
    padding: 12px 16px !important;
}

html.ehtw-detail .ehtw-comments-section #cdiv .c1.ehtw-comment-tweet .ehtw-comment-heading {
    grid-column: 2 !important;
    grid-row: 1 !important;
    padding-right: 90px !important;
    min-width: 0 !important;
}

html.ehtw-detail .ehtw-comments-section #cdiv .c1.ehtw-comment-tweet .ehtw-comment-author {
    font-size: 15px !important;
    font-weight: 700 !important;
    color: var(--ehtw-text) !important;
    text-decoration: none !important;
}

html.ehtw-detail .ehtw-comments-section #cdiv .c1.ehtw-comment-tweet .ehtw-comment-author a {
    color: var(--ehtw-text) !important;
    font-size: 15px !important;
    font-weight: 700 !important;
    text-decoration: none !important;
}

html.ehtw-detail .ehtw-comments-section #cdiv .c1.ehtw-comment-tweet .ehtw-comment-author a:hover {
    color: var(--ehtw-text) !important;
    text-decoration: underline !important;
}

html.ehtw-detail .ehtw-comments-section #cdiv .c1.ehtw-comment-tweet .c2 {
    position: absolute !important;
    top: 12px !important;
    right: 16px !important;
    left: auto !important;
    display: flex !important;
    width: auto !important;
    max-width: min(45%, 180px) !important;
    height: 20px !important;
    margin: 0 !important;
    padding: 0 !important;
    align-items: center !important;
    justify-content: flex-end !important;
    background: transparent !important;
    border: none !important;
}

html.ehtw-detail .ehtw-comments-section #cdiv .c1.ehtw-comment-tweet .c2 .c3 {
    display: none !important;
}

html.ehtw-detail .ehtw-comments-section #cdiv .c1.ehtw-comment-tweet .c2 .c4 {
    display: inline !important;
    width: auto !important;
    max-width: 100% !important;
    min-width: 0 !important;
    height: auto !important;
    margin: 0 !important;
    padding: 0 !important;
    color: var(--ehtw-muted) !important;
    font-size: 13px !important;
    font-weight: 400 !important;
    line-height: 20px !important;
    text-align: right !important;
    white-space: nowrap !important;
    overflow: hidden !important;
    text-overflow: ellipsis !important;
}

html.ehtw-detail .ehtw-comments-section #cdiv .c1.ehtw-comment-tweet .c6 {
    grid-column: 2 !important;
    grid-row: 2 !important;
    width: 100% !important;
    min-width: 0 !important;
    margin: 0 !important;
    padding: 0 !important;
    text-indent: 0 !important;
    text-align: left !important;
    font-size: 15px !important;
    line-height: 20px !important;
    color: var(--ehtw-text) !important;
}

html.ehtw-detail .ehtw-comments-section #cdiv .c1.ehtw-comment-tweet .c7 {
    grid-column: 2 !important;
    grid-row: 3 !important;
    margin: 0 !important;
    text-indent: 0 !important;
}

/* 评论提交入口:保留原始 a、onclick 和 #formdiv。 */
html.ehtw-detail .ehtw-comments-section #postnewcomment {
    display: flex !important;
    align-items: center !important;
    justify-content: flex-end !important;
    width: 100% !important;
    padding: 12px 16px !important;
    background: transparent !important;
    border-top: 1px solid var(--ehtw-border) !important;
    text-align: right !important;
}

html.ehtw-detail .ehtw-comments-section #postnewcomment a,
html.ehtw-detail .ehtw-comments-section #postnewcomment [onclick] {
    display: inline-flex !important;
    position: relative !important;
    align-items: center !important;
    justify-content: center !important;
    width: auto !important;
    min-width: 150px !important;
    height: 36px !important;
    min-height: 36px !important;
    padding: 0 16px !important;
    margin: 0 !important;
    border: 0 !important;
    border-radius: 9999px !important;
    background: var(--ehtw-accent) !important;
    color: transparent !important;
    font-size: 0 !important;
    line-height: 36px !important;
    text-decoration: none !important;
    cursor: pointer;
    overflow: hidden !important;
}

html.ehtw-detail .ehtw-comments-section #postnewcomment a::after,
html.ehtw-detail .ehtw-comments-section #postnewcomment [onclick]::after {
    content: "Post your reply";
    display: block;
    position: absolute;
    inset: 0;
    color: #ffffff;
    font-size: 15px;
    font-weight: 700;
    line-height: 36px;
    text-align: center;
    white-space: nowrap;
    pointer-events: none;
}

html.ehtw-detail .ehtw-comments-section #postnewcomment a:hover,
html.ehtw-detail .ehtw-comments-section #postnewcomment [onclick]:hover {
    filter: brightness(0.92);
}

/* ============================================================
   移动端保留图标型底栏
   ============================================================ */

@media (max-width: 699px) {
    #ehtw-left #nb .ehtw-nav-link,
    #ehtw-left #nb .ehtw-more-button {
        width: 100% !important;
        margin-left: 0 !important;
        justify-content: center !important;
        text-align: center !important;
    }

    #ehtw-left #nb.ehtw-nav-open .ehtw-nav-mobile-extra .ehtw-nav-link {
        width: 100% !important;
        margin-left: 0 !important;
        justify-content: flex-start !important;
        text-align: left !important;
    }

    .ehtw-comments-section #cdiv .c1.ehtw-comment-tweet .ehtw-comment-heading {
        padding-right: 68px !important;
    }

    html.ehtw-detail .ehtw-comments-section #cdiv .c1.ehtw-comment-tweet .c2 {
        right: 12px !important;
        max-width: 110px !important;
    }
}
/* ============================================================
   0.9.0 详情标签换行和左对齐
   ============================================================ */

html.ehtw-detail #ehtw-center .ehtw-detail-tags,
html.ehtw-detail #ehtw-center .ehtw-detail-tags * {
    white-space: normal !important;
    max-width: 100% !important;
    overflow-wrap: anywhere !important;
    text-align: left !important;
}

html.ehtw-detail #ehtw-center .ehtw-detail-tags {
    display: block !important;
    position: static !important;
    clear: both !important;
    float: none !important;
    width: 100% !important;
    min-width: 0 !important;
    height: auto !important;
    margin: 8px 0 12px !important;
    padding: 0 !important;
    overflow: visible !important;
}

html.ehtw-detail #ehtw-center .ehtw-detail-tags #gd4,
html.ehtw-detail #ehtw-center .ehtw-detail-tags #taglist {
    display: block !important;
    position: static !important;
    float: none !important;
    clear: both !important;
    width: auto !important;
    min-width: 0 !important;
    max-width: 100% !important;
    height: auto !important;
    min-height: 0 !important;
    max-height: none !important;
    margin: 0 !important;
    padding: 0 !important;
    border: 0 !important;
    background: transparent !important;
    overflow: visible !important;
    text-align: left !important;
    white-space: normal !important;
}

html.ehtw-detail #ehtw-center .ehtw-detail-tags #taglist table,
html.ehtw-detail #ehtw-center .ehtw-detail-tags #taglist tbody,
html.ehtw-detail #ehtw-center .ehtw-detail-tags #taglist tr,
html.ehtw-detail #ehtw-center .ehtw-detail-tags #taglist td:not(.tc) {
    display: inline !important;
    position: static !important;
    float: none !important;
    width: auto !important;
    min-width: 0 !important;
    max-width: 100% !important;
    height: auto !important;
    margin: 0 !important;
    padding: 0 !important;
    border: none !important;
    background: transparent !important;
    text-align: left !important;
    white-space: normal !important;
    vertical-align: baseline !important;
}

html.ehtw-detail #ehtw-center .ehtw-detail-tags #taglist .gt,
html.ehtw-detail #ehtw-center .ehtw-detail-tags #taglist .gtl,
html.ehtw-detail #ehtw-center .ehtw-detail-tags #taglist .gtw {
    display: inline !important;
    position: static !important;
    float: none !important;
    width: auto !important;
    height: auto !important;
    margin: 0 6px 0 0 !important;
    padding: 0 !important;
    border: none !important;
    border-radius: 0 !important;
    background: transparent !important;
    background-image: none !important;
    color: var(--ehtw-accent) !important;
    font-size: 15px !important;
    font-weight: 400 !important;
    line-height: 20px !important;
    white-space: normal !important;
    overflow-wrap: anywhere !important;
    text-align: left !important;
}

/* ============================================================
   0.9.0 时间行与新标签输入框
   ============================================================ */

html.ehtw-detail #ehtw-center .ehtw-detail-meta {
    display: block !important;
    position: static !important;
    clear: both !important;
    float: none !important;
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
    height: auto !important;
    text-align: left !important;
}

@media (max-width: 699px) {
    html.ehtw-detail #ehtw-center .ehtw-detail-meta,
    html.ehtw-detail #ehtw-center .ehtw-mobile-about {
        display: none !important;
    }
}

html.ehtw-detail #ehtw-center .ehtw-detail-timestamp {
    display: block !important;
    width: 100% !important;
    margin: 0 !important;
    padding: 12px 0 !important;
    color: var(--ehtw-muted) !important;
    font-size: 15px !important;
    line-height: 20px !important;
    text-align: left !important;
}

html.ehtw-detail #ehtw-center .ehtw-detail-tags #newtagfield {
    display: inline-block !important;
    position: static !important;
    float: none !important;
    width: min(100%, 280px) !important;
    min-width: 0 !important;
    max-width: 100% !important;
    height: 36px !important;
    min-height: 36px !important;
    margin: 8px 6px 4px 0 !important;
    padding: 0 14px !important;
    border: 1px solid var(--ehtw-border) !important;
    border-radius: 9999px !important;
    outline: none !important;
    background: transparent !important;
    color: var(--ehtw-text) !important;
    font-size: 14px !important;
    line-height: 34px !important;
    text-align: left !important;
    vertical-align: middle !important;
}

html.ehtw-detail #ehtw-center .ehtw-detail-tags #newtagfield:focus {
    border-color: var(--ehtw-accent) !important;
}

html.ehtw-detail #ehtw-center .ehtw-detail-tags #newtagbutton {
    display: inline-flex !important;
    position: static !important;
    float: none !important;
    align-items: center !important;
    justify-content: center !important;
    width: auto !important;
    min-width: 72px !important;
    max-width: 100% !important;
    height: 36px !important;
    min-height: 36px !important;
    margin: 8px 0 4px !important;
    padding: 0 14px !important;
    border: 1px solid var(--ehtw-border) !important;
    border-radius: 9999px !important;
    background: transparent !important;
    color: var(--ehtw-text) !important;
    font-size: 14px !important;
    line-height: 34px !important;
    text-align: center !important;
    vertical-align: middle !important;
    cursor: pointer;
}

html.ehtw-detail #ehtw-center .ehtw-detail-tags #newtagbutton:hover {
    background: var(--ehtw-nav-hover) !important;
}

/* ============================================================
   0.9.0 评论头部与正文顺序
   ============================================================ */

html.ehtw-detail .ehtw-comments-section #cdiv .c1.ehtw-comment-tweet {
    display: grid !important;
    grid-template-columns: 40px minmax(0, 1fr) !important;
    grid-template-rows: auto auto auto !important;
    column-gap: 12px !important;
    row-gap: 4px !important;
    position: relative !important;
    padding: 12px 16px !important;
}

html.ehtw-detail .ehtw-comments-section #cdiv .c1.ehtw-comment-tweet .ehtw-comment-heading {
    grid-column: 2 !important;
    grid-row: 1 !important;
    display: flex !important;
    align-items: center !important;
    justify-content: flex-start !important;
    gap: 4px !important;
    width: 100% !important;
    min-width: 0 !important;
    height: auto !important;
    min-height: 20px !important;
    padding: 0 115px 0 0 !important;
    margin: 0 !important;
}

html.ehtw-detail .ehtw-comments-section #cdiv .c1.ehtw-comment-tweet .c2 {
    grid-column: 2 !important;
    grid-row: 1 !important;
    position: static !important;
    float: none !important;
    top: auto !important;
    left: auto !important;
    right: auto !important;
    bottom: auto !important;
    display: flex !important;
    align-items: center !important;
    justify-content: flex-end !important;
    gap: 4px !important;
    justify-self: end !important;
    align-self: start !important;
    width: auto !important;
    min-width: 0 !important;
    max-width: 115px !important;
    height: auto !important;
    min-height: 20px !important;
    margin: 0 !important;
    padding: 0 !important;
    background: transparent !important;
    border: none !important;
    z-index: 1;
}

html.ehtw-detail .ehtw-comments-section #cdiv .c1.ehtw-comment-tweet .c2 .c3 {
    display: none !important;
    flex: 1 1 auto !important;
    min-width: 0 !important;
}

html.ehtw-detail .ehtw-comments-section #cdiv .c1.ehtw-comment-tweet .c2 .c4 {
    display: inline-flex !important;
    position: static !important;
    float: none !important;
    top: auto !important;
    left: auto !important;
    right: auto !important;
    bottom: auto !important;
    flex: 0 0 auto !important;
    align-items: center !important;
    justify-content: flex-end !important;
    margin: 0 0 0 auto !important;
    padding: 0 !important;
    width: auto !important;
    min-width: 0 !important;
    max-width: 100% !important;
    height: auto !important;
    min-height: 0 !important;
    color: var(--ehtw-muted) !important;
    font-size: 13px !important;
    line-height: 20px !important;
    font-weight: 400 !important;
    text-align: right !important;
    background: transparent !important;
    border: none !important;
    white-space: nowrap !important;
    overflow: hidden !important;
    text-overflow: ellipsis !important;
}

html.ehtw-detail .ehtw-comments-section #cdiv .c1.ehtw-comment-tweet .c2 > .c {
    display: none !important;
}

html.ehtw-detail .ehtw-comments-section #cdiv .c1.ehtw-comment-tweet .c6 {
    grid-column: 2 !important;
    grid-row: 2 !important;
    position: static !important;
    float: none !important;
    display: block !important;
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
    height: auto !important;
    margin: 0 !important;
    padding: 0 !important;
    text-indent: 0 !important;
    text-align: left !important;
    font-size: 15px !important;
    line-height: 20px !important;
    clear: both !important;
}

html.ehtw-detail .ehtw-comments-section #cdiv .c1.ehtw-comment-tweet .c7 {
    grid-column: 2 !important;
    grid-row: 3 !important;
    position: static !important;
    float: none !important;
    margin: 0 !important;
    padding: 0 !important;
}

/* ============================================================
   0.9.0 隐藏 Post New Comment 两侧的文本方括号
   ============================================================ */

html.ehtw-detail .ehtw-comments-section #postnewcomment {
    display: flex !important;
    align-items: center !important;
    justify-content: flex-end !important;
    width: 100% !important;
    font-size: 0 !important;
    line-height: 0 !important;
    color: transparent !important;
    text-align: right !important;
}

html.ehtw-detail .ehtw-comments-section #postnewcomment a,
html.ehtw-detail .ehtw-comments-section #postnewcomment [onclick] {
    font-size: 15px !important;
    font-weight: 700 !important;
    line-height: 36px !important;
    color: #ffffff !important;
    background: var(--ehtw-accent) !important;
    height: 36px !important;
    min-height: 36px !important;
    border-radius: 9999px !important;
    text-align: center !important;
    white-space: nowrap !important;
}

/* 保持原始链接文字不可见,由伪元素提供按钮文案。 */
html.ehtw-detail .ehtw-comments-section #postnewcomment a,
html.ehtw-detail .ehtw-comments-section #postnewcomment [onclick] {
    font-size: 0 !important;
}

html.ehtw-detail .ehtw-comments-section #postnewcomment a::after,
html.ehtw-detail .ehtw-comments-section #postnewcomment [onclick]::after {
    content: "Post your reply";
    color: #ffffff !important;
    font-size: 15px !important;
    font-weight: 700 !important;
    line-height: 36px !important;
    pointer-events: none;
}

/* 1.0.0 收尾:标签不在单词中间断行,列表标签最多 6 个,评论标注不被裁切。 */
.ehtw-detail-tags,
.ehtw-detail-tags * {
    overflow-wrap: break-word !important;
}

html.ehtw-list .ehtw-tags .ehtw-hashtag:nth-child(n+7) {
    display: none !important;
}

html.ehtw-list .ehtw-media {
    margin-top: 12px !important;
}

#cdiv .c2 .c4 {
    flex: none !important;
    min-width: max-content !important;
    overflow: visible !important;
    padding: 0 0 0 8px !important;
    text-indent: 0 !important;
}
/* ============================================================
   1.1.0 X 实测尺寸:1440px 视口下三栏 267 / 600 / 380
   ============================================================ */

html.ehtw-enabled {
    -webkit-font-smoothing: antialiased;
}

html.ehtw-enabled body {
    -webkit-font-smoothing: antialiased;
}

html.ehtw-enabled #ehtw-shell {
    width: 1247px !important;
    max-width: 100% !important;
    grid-template-columns:
        267px minmax(0, 600px) 380px !important;
}

html.ehtw-enabled #ehtw-left {
    width: 267px !important;
    min-width: 0 !important;
    max-width: 267px !important;
    padding: 0 !important;
    margin: 0 !important;
}

html.ehtw-enabled #ehtw-center {
    width: 600px !important;
    min-width: 0 !important;
    max-width: 100% !important;
    border-left: 1px solid var(--ehtw-border) !important;
    border-right: 1px solid var(--ehtw-border) !important;
}

html.ehtw-enabled #ehtw-right {
    width: 380px !important;
    min-width: 0 !important;
    max-width: 380px !important;
    padding: 0 0 32px 30px !important;
    margin: 0 !important;
}

/* ============================================================
   左栏:Logo、58px 导航行、Post、账号
   ============================================================ */

html.ehtw-enabled #ehtw-left {
    display: flex !important;
    flex-direction: column !important;
    align-items: flex-start !important;
    min-height: 0 !important;
    max-height: 100vh !important;
}

#ehtw-left .ehtw-brand-link {
    display: flex !important;
    align-items: center !important;
    justify-content: center !important;
    flex: 0 0 52px !important;
    width: 52px !important;
    height: 52px !important;
    min-height: 52px !important;
    max-height: 52px !important;
    margin: 2px 0 4px !important;
    padding: 0 14px !important;
    border-radius: 9999px !important;
    width: auto !important;
    min-width: 52px !important;
    max-width: 100% !important;
    font-size: 15px !important;
    font-weight: 700 !important;
    white-space: nowrap !important;
}

#ehtw-left #nb {
    flex: 0 0 auto !important;
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
    margin: 0 !important;
    padding: 0 !important;
    gap: 0 !important;
    align-items: flex-start !important;
    text-align: left !important;
}

#ehtw-left #nb .ehtw-nav-entry,
#ehtw-left #nb .ehtw-more-entry {
    display: block !important;
    flex: 0 0 auto !important;
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
    height: 58px !important;
    min-height: 58px !important;
    max-height: 58px !important;
    margin: 0 !important;
    padding: 4px 0 !important;
    text-align: left !important;
    background: transparent !important;
}

#ehtw-left #nb .ehtw-nav-link,
#ehtw-left #nb .ehtw-more-button {
    display: flex !important;
    align-items: center !important;
    justify-content: flex-start !important;
    gap: 20px !important;
    width: fit-content !important;
    min-width: 0 !important;
    max-width: 100% !important;
    height: 50px !important;
    min-height: 50px !important;
    max-height: 50px !important;
    margin: 0 !important;
    padding: 12px !important;
    border: none !important;
    border-radius: 9999px !important;
    text-align: left !important;
    line-height: 24px !important;
    font-size: 20px !important;
    font-weight: 400 !important;
    background: transparent !important;
    color: var(--ehtw-text) !important;
}

#ehtw-left #nb .ehtw-nav-link .ehtw-svg-icon,
#ehtw-left #nb .ehtw-more-button .ehtw-svg-icon {
    width: 26px !important;
    height: 26px !important;
    min-width: 26px !important;
    min-height: 26px !important;
    flex: 0 0 26px !important;
}

#ehtw-left #nb .ehtw-nav-label {
    display: inline !important;
    font-size: 20px !important;
    line-height: 24px !important;
    font-weight: inherit !important;
    white-space: nowrap !important;
}

#ehtw-left #nb .ehtw-nav-current .ehtw-nav-link {
    font-weight: 700 !important;
}

#ehtw-left #nb .ehtw-nav-extra {
    display: none !important;
}

#ehtw-left #nb.ehtw-nav-open .ehtw-nav-extra {
    display: block !important;
}

#ehtw-left .ehtw-post-button {
    flex: 0 0 52px !important;
    display: flex !important;
    align-items: center !important;
    justify-content: center !important;
    width: 233px !important;
    min-width: 233px !important;
    max-width: 233px !important;
    height: 52px !important;
    min-height: 52px !important;
    max-height: 52px !important;
    margin: 20px 0 0 !important;
    padding: 0 32px !important;
    border: none !important;
    border-radius: 9999px !important;
    background: #0f1419 !important;
    color: #ffffff !important;
    font-size: 17px !important;
    font-weight: 700 !important;
    line-height: 20px !important;
    text-align: center !important;
    text-decoration: none !important;
}

#ehtw-left .ehtw-account-card {
    position: relative;
    display: grid;
    grid-template-columns: 40px minmax(0, 1fr);
    align-items: center;
    gap: 10px;
    flex: 0 0 65px;
    width: 259px;
    min-width: 0;
    max-width: 259px;
    height: 65px;
    min-height: 65px;
    max-height: 65px;
    margin: auto 0 12px;
    padding: 12px;
    border-radius: 9999px;
    background: transparent;
    color: var(--ehtw-text) !important;
    text-decoration: none !important;
    cursor: pointer;
}

#ehtw-left .ehtw-account-card:hover {
    background: var(--ehtw-nav-hover);
    color: var(--ehtw-text) !important;
}

.ehtw-account-avatar,
.ehtw-composer-avatar,
.ehtw-who-avatar {
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 40px;
    width: 40px;
    height: 40px;
    min-width: 40px;
    min-height: 40px;
    border-radius: 50%;
    background: #536471;
    color: #ffffff;
    font-size: 15px;
    font-weight: 700;
    line-height: 1;
}

.ehtw-account-text {
    display: flex;
    flex-direction: column;
    gap: 0;
    min-width: 0;
    overflow: hidden;
}

.ehtw-account-name,
.ehtw-account-handle {
    display: block;
    min-width: 0;
    max-width: 100%;
    font-size: 15px;
    line-height: 20px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.ehtw-account-name {
    color: var(--ehtw-text);
    font-weight: 700;
}

.ehtw-account-handle {
    color: var(--ehtw-muted);
    font-weight: 400;
}

.ehtw-account-menu {
    position: absolute;
    z-index: 1200;
    left: 0;
    bottom: calc(100% + 8px);
    width: 259px;
    max-width: calc(100vw - 24px);
    padding: 8px;
    border: 1px solid var(--ehtw-border);
    border-radius: 14px;
    background: var(--ehtw-card);
    box-shadow: 0 8px 28px rgba(0, 0, 0, .28);
}

.ehtw-account-menu[hidden] {
    display: none !important;
}

.ehtw-account-menu-action {
    display: block;
    padding: 12px 14px;
    border-radius: 10px;
    color: var(--ehtw-text) !important;
    font-size: 15px;
    font-weight: 700;
    text-decoration: none !important;
}

.ehtw-account-menu-action:hover,
.ehtw-account-menu-action:focus-visible {
    background: var(--ehtw-nav-hover);
    outline: none;
}

.ehtw-account-more {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 20px;
    height: 20px;
    color: var(--ehtw-text);
}

.ehtw-account-more svg {
    width: 20px;
    height: 20px;
}

/* ============================================================
   中栏 Tab 与发帖框
   ============================================================ */

#ehtw-header.ehtw-tabs-header {
    height: 53px !important;
    min-height: 53px !important;
    max-height: 53px !important;
}

#ehtw-header .ehtw-top-tab {
    height: 53px !important;
    font-size: 15px !important;
    font-weight: 500 !important;
    color: #71767b !important;
}

#ehtw-header .ehtw-top-tab.ehtw-tab-active {
    color: var(--ehtw-text) !important;
    font-weight: 700 !important;
}

#ehtw-header .ehtw-top-tab.ehtw-tab-active .ehtw-top-tab-label::after {
    height: 4px !important;
    border-radius: 9999px !important;
}

html.ehtw-list .ehtw-composer {
    display: grid;
    grid-template-columns: 40px minmax(0, 1fr);
    gap: 8px;
    width: 100%;
    min-width: 0;
    max-width: 100%;
    padding: 12px 16px;
    border-bottom: 1px solid var(--ehtw-border);
    background: transparent;
    color: var(--ehtw-text) !important;
    text-decoration: none !important;
}

html.ehtw-list .ehtw-composer:hover {
    color: var(--ehtw-text) !important;
}

.ehtw-composer-body {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.ehtw-composer-placeholder {
    display: flex;
    align-items: center;
    width: 100%;
    min-height: 56px;
    color: var(--ehtw-muted);
    font-size: 20px;
    font-weight: 400;
    line-height: 24px;
}

.ehtw-composer-footer {
    display: flex;
    justify-content: flex-end;
    align-items: center;
    width: 100%;
}

.ehtw-composer-button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: auto;
    height: 36px;
    min-height: 36px;
    padding: 0 16px;
    border-radius: 9999px;
    background: #0f1419;
    color: #ffffff;
    font-size: 15px;
    line-height: 20px;
    font-weight: 700;
}

/* 上方搜索工具栏:保持在原搜索框内,随 Filters 展开。 */

#ehtw-search-card #searchbox.ehtw-filters-collapsed:not(.ehtw-filters-open) .ehtw-search-mode-toolbar,
#ehtw-mobile-search #searchbox.ehtw-filters-collapsed:not(.ehtw-filters-open) .ehtw-search-mode-toolbar {
    display: none !important;
}

#ehtw-search-card #searchbox.ehtw-filters-open .ehtw-search-mode-toolbar,
#ehtw-mobile-search #searchbox.ehtw-filters-open .ehtw-search-mode-toolbar {
    display: flex !important;
    position: static !important;
    float: none !important;
    flex-wrap: wrap !important;
    align-items: center !important;
    justify-content: flex-start !important;
    gap: 5px !important;
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
    height: auto !important;
    min-height: 40px !important;
    margin: 8px 0 0 !important;
    padding: 5px 0 !important;
    border: none !important;
    background: transparent !important;
    color: var(--ehtw-muted) !important;
    font-size: 13px !important;
}

#ehtw-search-card .ehtw-search-mode-toolbar a,
#ehtw-search-card .ehtw-search-mode-toolbar select,
#ehtw-mobile-search .ehtw-search-mode-toolbar a,
#ehtw-mobile-search .ehtw-search-mode-toolbar select {
    background: transparent !important;
    color: var(--ehtw-muted) !important;
    border: none !important;
    border-radius: 0 !important;
    font-size: 13px !important;
}

/* ============================================================
   列表推文:精确间距与原图比例
   ============================================================ */

html.ehtw-list .ehtw-tweet {
    display: grid !important;
    grid-template-columns: 40px minmax(0, 1fr) !important;
    column-gap: 8px !important;
    row-gap: 0 !important;
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
    padding: 12px 16px 12px !important;
    border-left: none !important;
    border-right: none !important;
    border-top: none !important;
    border-bottom: 1px solid var(--ehtw-border) !important;
}

html.ehtw-list .ehtw-tweet-content {
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
}

html.ehtw-list .ehtw-tweet-author {
    display: flex !important;
    align-items: center !important;
    justify-content: flex-start !important;
    flex-wrap: nowrap !important;
    gap: 4px !important;
    width: 100% !important;
    height: 20px !important;
    min-height: 20px !important;
    margin: 0 0 2px !important;
    padding: 0 !important;
    line-height: 20px !important;
}

html.ehtw-list .ehtw-author-name,
html.ehtw-list .ehtw-author-name a {
    font-size: 15px !important;
    font-weight: 700 !important;
    line-height: 20px !important;
    color: var(--ehtw-text) !important;
}

html.ehtw-list .ehtw-handle,
html.ehtw-list .ehtw-time,
html.ehtw-list .ehtw-dot {
    color: #71767b !important;
    font-size: 15px !important;
    font-weight: 400 !important;
    line-height: 20px !important;
}

html.ehtw-list .ehtw-tweet-more {
    flex: 0 0 19px !important;
    width: 19px !important;
    height: 20px !important;
    margin-left: auto !important;
    color: #71767b !important;
}

html.ehtw-list .ehtw-tweet-more svg {
    width: 19px !important;
    height: 20px !important;
}

html.ehtw-list .ehtw-tweet-title {
    margin: 0 !important;
    font-size: 15px !important;
    line-height: 20px !important;
}

html.ehtw-list .ehtw-media {
    display: block !important;
    width: min(100%, calc(510px * var(--ehtw-ar, 1))) !important;
    max-width: 100% !important;
    max-height: 510px !important;
    height: auto !important;
    aspect-ratio: var(--ehtw-ar, 1) !important;
    margin: 12px 0 0 !important;
    padding: 0 !important;
    border: 1px solid var(--ehtw-border) !important;
    border-radius: 16px !important;
    overflow: hidden !important;
    background: var(--ehtw-media) !important;
}

html.ehtw-list .ehtw-media-link {
    display: block !important;
    width: 100% !important;
    height: 100% !important;
    margin: 0 !important;
    padding: 0 !important;
}

html.ehtw-list .ehtw-media img {
    display: block !important;
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
    height: 100% !important;
    min-height: 0 !important;
    max-height: none !important;
    margin: 0 !important;
    padding: 0 !important;
    object-fit: cover !important;
    object-position: center center !important;
}

/* ============================================================
   推文操作栏:四项均分 + Bookmark / Share
   ============================================================ */

html.ehtw-list .ehtw-twitter-actions {
    display: grid !important;
    grid-template-columns:
        repeat(4, minmax(0, 1fr))
        19px
        19px !important;
    align-items: center !important;
    justify-content: stretch !important;
    column-gap: 4px !important;
    row-gap: 0 !important;
    width: 100% !important;
    max-width: none !important;
    min-width: 0 !important;
    height: 20px !important;
    min-height: 20px !important;
    margin: 12px 0 0 !important;
    padding: 0 !important;
}

html.ehtw-list .ehtw-twitter-actions .ehtw-twitter-action {
    display: inline-flex !important;
    justify-content: flex-start !important;
    align-items: center !important;
    gap: 0 !important;
    width: 100% !important;
    min-width: 0 !important;
    height: 20px !important;
    min-height: 20px !important;
    margin: 0 !important;
    padding: 0 !important;
    color: #71767b !important;
    font-size: 13px !important;
    line-height: 16px !important;
}

html.ehtw-list .ehtw-twitter-actions .ehtw-twitter-action-icon {
    display: flex !important;
    align-items: center !important;
    justify-content: center !important;
    flex: 0 0 19px !important;
    width: 19px !important;
    height: 19px !important;
    border-radius: 50% !important;
}

html.ehtw-list .ehtw-twitter-actions .ehtw-twitter-action-icon svg {
    width: 18.75px !important;
    height: 18.75px !important;
    color: #71767b;
}

html.ehtw-list .ehtw-twitter-actions .ehtw-action-number {
    display: inline !important;
    padding-left: 4px !important;
    color: #71767b !important;
    font-size: 13px !important;
    font-weight: 400 !important;
    line-height: 16px !important;
}

html.ehtw-list .ehtw-twitter-actions .ehtw-twitter-action-bookmark,
html.ehtw-list .ehtw-twitter-actions .ehtw-twitter-action-share {
    width: 19px !important;
    justify-self: end !important;
    padding: 0 !important;
}

html.ehtw-list .ehtw-twitter-actions .ehtw-twitter-action-bookmark:hover {
    color: var(--ehtw-accent) !important;
}

html.ehtw-list .ehtw-twitter-actions .ehtw-twitter-action-bookmark:hover .ehtw-twitter-action-icon {
    background: rgba(29, 155, 240, 0.1) !important;
}

html.ehtw-list .ehtw-twitter-actions .ehtw-twitter-action-reply:hover,
html.ehtw-list .ehtw-twitter-actions .ehtw-twitter-action-views:hover,
html.ehtw-list .ehtw-twitter-actions .ehtw-twitter-action-share:hover {
    color: var(--ehtw-accent) !important;
}

html.ehtw-list .ehtw-twitter-actions .ehtw-twitter-action-repost:hover {
    color: #00ba7c !important;
}

html.ehtw-list .ehtw-twitter-actions .ehtw-twitter-action-heart:hover {
    color: #f91880 !important;
}

html.ehtw-list .ehtw-twitter-actions .ehtw-twitter-action:hover .ehtw-action-number,
html.ehtw-list .ehtw-twitter-actions .ehtw-twitter-action:hover svg {
    color: inherit !important;
}

html.ehtw-list .ehtw-twitter-actions .ehtw-twitter-action:hover .ehtw-twitter-action-icon {
    width: 34px !important;
    height: 34px !important;
    margin: 0 -7.5px !important;
}

/* ============================================================
   右栏搜索框:350 × 44,吸顶
   ============================================================ */

html.ehtw-enabled #ehtw-right #ehtw-search-card {
    position: sticky !important;
    top: 0 !important;
    z-index: 110 !important;
    width: 350px !important;
    min-width: 0 !important;
    max-width: 350px !important;
    min-height: 52px !important;
    margin: 0 !important;
    padding: 7px 0 0 !important;
    background: var(--ehtw-bg) !important;
}

#ehtw-search-card .ehtw-search-input-wrap {
    position: relative !important;
    width: 350px !important;
    max-width: 100% !important;
    min-width: 0 !important;
}

#ehtw-search-card #f_search,
#ehtw-mobile-search #f_search {
    height: 44px !important;
    min-height: 44px !important;
    max-height: 44px !important;
    padding: 0 12px 0 36px !important;
    margin: 0 !important;
    border: 1px solid #cfd9de !important;
    border-radius: 9999px !important;
    background: transparent !important;
    color: var(--ehtw-text) !important;
    font-size: 15px !important;
    line-height: normal !important;
    box-shadow: none !important;
}

#ehtw-search-card #f_search::placeholder,
#ehtw-mobile-search #f_search::placeholder {
    color: #71767b !important;
    opacity: 1 !important;
}

#ehtw-search-card #f_search:focus,
#ehtw-mobile-search #f_search:focus {
    border: 1px solid var(--ehtw-accent) !important;
    outline: none !important;
    box-shadow: none !important;
}

#ehtw-search-card .ehtw-search-input-wrap .ehtw-search-icon,
#ehtw-mobile-search .ehtw-search-input-wrap .ehtw-search-icon {
    position: absolute !important;
    top: 50% !important;
    left: 14px !important;
    width: 16px !important;
    height: 16px !important;
    transform: translateY(-50%) !important;
    color: #71767b !important;
}

#ehtw-search-card #searchbox .ehtw-filters-toggle,
#ehtw-mobile-search #searchbox .ehtw-filters-toggle {
    display: inline-flex !important;
    align-self: flex-end !important;
    justify-content: flex-end !important;
    gap: 6px !important;
    width: fit-content !important;
    height: 26px !important;
    margin: 3px 0 0 auto !important;
    padding: 0 !important;
    border: none !important;
    background: transparent !important;
    color: var(--ehtw-accent) !important;
    font-size: 13px !important;
    font-weight: 400 !important;
}

/* ============================================================
   趋势卡片:两行、每行约 62px
   ============================================================ */

html.ehtw-enabled #ehtw-right .ehtw-trending-card {
    order: 1;
    width: 350px !important;
    min-width: 0 !important;
    max-width: 350px !important;
    margin: 16px 0 0 !important;
    padding: 0 !important;
    border: 1px solid var(--ehtw-border) !important;
    border-radius: 16px !important;
    background: transparent !important;
}

html.ehtw-enabled #ehtw-right .ehtw-trending-card .ehtw-side-title {
    margin: 0 !important;
    padding: 12px 16px !important;
    font-size: 20px !important;
    font-weight: 800 !important;
    line-height: 24px !important;
}

.ehtw-trending-card .ehtw-trend {
    display: flex !important;
    align-items: flex-start !important;
    justify-content: space-between !important;
    gap: 8px !important;
    width: 100% !important;
    min-height: 62px !important;
    padding: 12px 16px !important;
    color: var(--ehtw-text) !important;
}

.ehtw-trending-card .ehtw-trend:hover {
    background: var(--ehtw-nav-hover) !important;
}

.ehtw-trend-text {
    display: flex;
    flex-direction: column;
    gap: 2px;
    flex: 1 1 auto;
    min-width: 0;
}

.ehtw-trending-card .ehtw-trend-category {
    display: block;
    width: 100%;
    min-width: 0;
    color: #71767b !important;
    font-size: 13px !important;
    font-weight: 400 !important;
    line-height: 16px !important;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.ehtw-trending-card .ehtw-trend-name {
    display: block;
    width: 100%;
    min-width: 0;
    margin: 0 !important;
    padding: 0 !important;
    color: var(--ehtw-text) !important;
    font-size: 15px !important;
    font-weight: 700 !important;
    line-height: 20px !important;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.ehtw-trend-more {
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 18px;
    width: 18px;
    height: 18px;
    color: #71767b !important;
}

.ehtw-trend-more svg {
    width: 18px;
    height: 18px;
}

.ehtw-trending-card .ehtw-trend-count {
    display: none !important;
}

.ehtw-trending-card .ehtw-trending-more {
    display: block;
    width: 100%;
    min-height: 50px;
    margin: 0 !important;
    padding: 16px !important;
    border: none !important;
    border-radius: 0 0 16px 16px;
    background: transparent !important;
    color: var(--ehtw-accent) !important;
    font-size: 15px !important;
    font-weight: 400 !important;
    line-height: 18px !important;
    text-align: left !important;
}

/* ============================================================
   Who to follow
   ============================================================ */

html.ehtw-enabled #ehtw-right {
    display: flex !important;
    flex-direction: column !important;
    align-items: stretch !important;
    gap: 0 !important;
}

html.ehtw-enabled #ehtw-right #ehtw-search-card {
    order: 0;
}

html.ehtw-enabled #ehtw-right .ehtw-who-card {
    order: 2;
    width: 350px !important;
    min-width: 0 !important;
    max-width: 350px !important;
    margin: 16px 0 0 !important;
    padding: 0 !important;
    border: 1px solid var(--ehtw-border) !important;
    border-radius: 16px !important;
    background: transparent !important;
}

.ehtw-who-card .ehtw-side-title {
    margin: 0 !important;
    padding: 12px 16px !important;
    font-size: 20px !important;
    font-weight: 800 !important;
    line-height: 24px !important;
}

.ehtw-who-row {
    display: grid;
    grid-template-columns: 40px minmax(0, 1fr) auto;
    align-items: center;
    gap: 8px;
    width: 100%;
    min-width: 0;
    min-height: 65px;
    padding: 12px 16px;
    background: transparent;
}

.ehtw-who-row:hover {
    background: var(--ehtw-nav-hover);
}

.ehtw-who-info {
    display: flex;
    flex-direction: column;
    min-width: 0;
    overflow: hidden;
}

.ehtw-who-name {
    display: block;
    min-width: 0;
    max-width: 100%;
    color: var(--ehtw-text) !important;
    font-size: 15px;
    font-weight: 700;
    line-height: 20px;
    text-decoration: none !important;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.ehtw-who-name:hover {
    text-decoration: underline !important;
}

.ehtw-who-handle {
    display: block;
    min-width: 0;
    max-width: 100%;
    color: #71767b;
    font-size: 15px;
    font-weight: 400;
    line-height: 20px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.ehtw-who-view {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    height: 32px;
    min-height: 32px;
    padding: 0 16px;
    border-radius: 9999px;
    background: #0f1419;
    color: #ffffff !important;
    font-size: 14px;
    font-weight: 700;
    line-height: 18px;
    text-decoration: none !important;
}

.ehtw-who-view:hover {
    opacity: 0.88;
    color: #ffffff !important;
}

/* ============================================================
   页脚:X 实测左对齐间距
   ============================================================ */

html.ehtw-enabled #ehtw-right .ehtw-footer {
    order: 99;
    width: 350px !important;
    min-width: 0 !important;
    max-width: 350px !important;
    margin: 16px 0 24px !important;
    padding: 0 16px !important;
    border: none !important;
    background: transparent !important;
    color: #71767b !important;
    text-align: left !important;
    font-size: 13px !important;
    line-height: 16px !important;
}

html.ehtw-enabled #ehtw-right .ehtw-footer .dp,
html.ehtw-enabled #ehtw-right .ehtw-footer .dp a {
    margin: 0 !important;
    padding: 0 !important;
    border: none !important;
    background: transparent !important;
    color: #71767b !important;
    text-align: left !important;
    font-size: 13px !important;
    line-height: 16px !important;
}

/* ============================================================
   详情页右栏内容与标题边缘一致
   ============================================================ */

html.ehtw-detail #ehtw-right .ehtw-operation-card #gd5,
html.ehtw-detail #ehtw-right .ehtw-about-card #gdd {
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
    margin: 0 !important;
    padding: 12px 16px !important;
    text-align: left !important;
}

html.ehtw-detail #ehtw-right .ehtw-operation-card #gd5 > div,
html.ehtw-detail #ehtw-right .ehtw-operation-card #gd5 > p {
    margin: 0 !important;
    padding: 8px 0 !important;
    text-align: left !important;
}

/* ============================================================
   深色主题按钮
   ============================================================ */

@media (prefers-color-scheme: dark) {
    #ehtw-left .ehtw-post-button,
    .ehtw-composer-button,
    .ehtw-who-view {
        background: #eff3f4 !important;
        color: #0f1419 !important;
    }

    #ehtw-search-card #f_search,
    #ehtw-mobile-search #f_search {
        border-color: #2f3336 !important;
    }

    #ehtw-search-card #f_search:focus,
    #ehtw-mobile-search #f_search:focus {
        border-color: var(--ehtw-accent) !important;
    }
}

/* ============================================================
   响应式:保留已验证的手机底栏逻辑
   ============================================================ */

@media (max-width: 999px) {
    html.ehtw-enabled #ehtw-shell {
        width: min(100%, 867px) !important;
        grid-template-columns:
            267px minmax(0, 600px) !important;
    }

    html.ehtw-enabled #ehtw-right {
        display: none !important;
    }

    html.ehtw-enabled #ehtw-center {
        width: 100% !important;
    }
}

@media (max-width: 699px) {
    html.ehtw-enabled #ehtw-shell {
        display: block !important;
        width: 100% !important;
        max-width: 100% !important;
    }

    html.ehtw-enabled #ehtw-left {
        position: fixed !important;
        top: auto !important;
        bottom: 0 !important;
        left: 0 !important;
        right: 0 !important;
        z-index: 1000 !important;
        display: block !important;
        width: 100% !important;
        min-width: 0 !important;
        max-width: 100vw !important;
        height: 64px !important;
        min-height: 64px !important;
        max-height: 64px !important;
        padding: 4px 6px !important;
        overflow-x: hidden !important;
        overflow-y: hidden !important;
    }

    #ehtw-left .ehtw-account-card {
        display: none !important;
    }

    #ehtw-left .ehtw-brand-link,
    #ehtw-left .ehtw-post-button {
        display: none !important;
    }

    #ehtw-left #nb {
        display: flex !important;
        flex-direction: row !important;
        flex-wrap: nowrap !important;
        align-items: center !important;
        justify-content: space-around !important;
        width: 100% !important;
        min-width: 0 !important;
        max-width: 100% !important;
        height: 100% !important;
    }

    #ehtw-left #nb .ehtw-nav-entry,
    #ehtw-left #nb .ehtw-more-entry {
        display: block !important;
        flex: 1 1 0 !important;
        width: auto !important;
        min-width: 0 !important;
        max-width: none !important;
        height: auto !important;
        min-height: 0 !important;
        max-height: none !important;
        padding: 0 !important;
    }

    #ehtw-left #nb .ehtw-nav-link,
    #ehtw-left #nb .ehtw-more-button {
        display: flex !important;
        align-items: center !important;
        justify-content: center !important;
        width: 100% !important;
        min-width: 0 !important;
        max-width: 100% !important;
        height: 48px !important;
        min-height: 48px !important;
        max-height: 48px !important;
        margin: 0 !important;
        padding: 8px 4px !important;
        gap: 0 !important;
    }

    #ehtw-left #nb .ehtw-nav-label {
        display: none !important;
    }

    #ehtw-left #nb .ehtw-nav-mobile-extra,
    #ehtw-left #nb .ehtw-nav-extra {
        display: none !important;
    }

    #ehtw-left:has(#nb.ehtw-nav-open) {
        height: auto !important;
        min-height: 64px !important;
        max-height: min(75vh, 560px) !important;
        overflow-y: auto !important;
    }

    #ehtw-left #nb.ehtw-nav-open {
        display: flex !important;
        flex-wrap: wrap !important;
        align-content: flex-start !important;
        height: auto !important;
        min-height: 0 !important;
        padding-top: 8px !important;
    }

    #ehtw-left #nb.ehtw-nav-open .ehtw-nav-mobile-extra,
    #ehtw-left #nb.ehtw-nav-open .ehtw-nav-extra {
        display: block !important;
        flex: 0 0 100% !important;
        width: 100% !important;
        height: auto !important;
        min-height: 0 !important;
        max-height: none !important;
        order: -1 !important;
    }

    #ehtw-left #nb.ehtw-nav-open .ehtw-nav-mobile-extra .ehtw-nav-link {
        justify-content: flex-start !important;
        gap: 16px !important;
        padding: 8px 16px !important;
    }

    #ehtw-left #nb.ehtw-nav-open .ehtw-nav-mobile-extra .ehtw-nav-label {
        display: inline !important;
        font-size: 16px !important;
    }

    html.ehtw-enabled #ehtw-center {
        width: 100% !important;
        min-width: 0 !important;
        max-width: 100% !important;
        border-left: none !important;
        border-right: none !important;
    }

    html.ehtw-list .ehtw-tweet {
        grid-template-columns:
            40px minmax(0, 1fr) !important;
        gap: 8px !important;
        padding: 12px !important;
    }
}
/* ============================================================
   1.2.0 手机 More 菜单:展开后主导航保持原宽度
   ============================================================ */

@media (max-width: 699px) {
    html.ehtw-enabled #ehtw-left #nb.ehtw-nav-open {
        display: flex !important;
        flex-direction: row !important;
        flex-wrap: wrap !important;
        align-content: flex-start !important;
        align-items: center !important;
        justify-content: space-around !important;
        width: 100% !important;
        min-width: 0 !important;
        max-width: 100% !important;
        height: auto !important;
        min-height: 0 !important;
        padding-top: 8px !important;
        gap: 0 !important;
    }

    html.ehtw-enabled #ehtw-left #nb.ehtw-nav-open
    > .ehtw-nav-entry:not(.ehtw-nav-mobile-extra):not(.ehtw-nav-extra),
    html.ehtw-enabled #ehtw-left #nb.ehtw-nav-open
    > .ehtw-more-entry {
        display: flex !important;
        flex: 1 1 0 !important;
        min-width: 0 !important;
        max-width: none !important;
        width: auto !important;
        height: 48px !important;
        min-height: 48px !important;
        max-height: 48px !important;
        margin: 0 !important;
        padding: 0 !important;
        order: 0 !important;
        align-items: center !important;
        justify-content: center !important;
    }

    html.ehtw-enabled #ehtw-left #nb.ehtw-nav-open
    > .ehtw-nav-mobile-extra,
    html.ehtw-enabled #ehtw-left #nb.ehtw-nav-open
    > .ehtw-nav-extra {
        display: block !important;
        flex: 0 0 100% !important;
        width: 100% !important;
        min-width: 0 !important;
        max-width: 100% !important;
        height: auto !important;
        min-height: 0 !important;
        max-height: none !important;
        margin: 0 !important;
        order: -1 !important;
    }

    html.ehtw-enabled #ehtw-left #nb.ehtw-nav-open
    > .ehtw-nav-entry:not(.ehtw-nav-mobile-extra):not(.ehtw-nav-extra)
    .ehtw-nav-link,
    html.ehtw-enabled #ehtw-left #nb.ehtw-nav-open
    > .ehtw-more-entry .ehtw-more-button {
        display: flex !important;
        width: 100% !important;
        min-width: 0 !important;
        max-width: 100% !important;
        height: 48px !important;
        min-height: 48px !important;
        max-height: 48px !important;
        margin: 0 !important;
        padding: 8px 4px !important;
        justify-content: center !important;
        align-items: center !important;
        gap: 0 !important;
    }

    html.ehtw-enabled #ehtw-left #nb.ehtw-nav-open
    > .ehtw-nav-entry:not(.ehtw-nav-mobile-extra):not(.ehtw-nav-extra)
    .ehtw-nav-label,
    html.ehtw-enabled #ehtw-left #nb.ehtw-nav-open
    > .ehtw-more-entry .ehtw-nav-label {
        display: none !important;
    }

    html.ehtw-enabled #ehtw-left #nb.ehtw-nav-open
    > .ehtw-nav-entry:not(.ehtw-nav-mobile-extra):not(.ehtw-nav-extra)
    .ehtw-svg-icon,
    html.ehtw-enabled #ehtw-left #nb.ehtw-nav-open
    > .ehtw-more-entry .ehtw-svg-icon {
        width: 26px !important;
        height: 26px !important;
        min-width: 26px !important;
        min-height: 26px !important;
        flex: 0 0 26px !important;
    }
}

/* ============================================================
   1.2.0 右栏:卡片不参与 flex 收缩,独立纵向滚动
   ============================================================ */

@media (min-width: 1000px) {
    html.ehtw-enabled #ehtw-right {
        position: sticky !important;
        top: 0 !important;
        align-self: start !important;
        max-height: 100vh !important;
        overflow-y: auto !important;
        overflow-x: hidden !important;
        scrollbar-width: none !important;
        -ms-overflow-style: none !important;
    }

    html.ehtw-enabled #ehtw-right::-webkit-scrollbar {
        display: none !important;
        width: 0 !important;
        height: 0 !important;
    }

    html.ehtw-enabled #ehtw-right > * {
        flex: none !important;
        flex-grow: 0 !important;
        flex-shrink: 0 !important;
    }
}

/* ============================================================
   1.2.0 Trending:只显示前五项,按钮在其后
   ============================================================ */

html.ehtw-enabled #ehtw-right
.ehtw-trending-card:not(.ehtw-trending-expanded)
.ehtw-trend-extra {
    display: none !important;
}

html.ehtw-enabled #ehtw-right
.ehtw-trending-card.ehtw-trending-expanded
.ehtw-trend-extra {
    display: flex !important;
}

html.ehtw-enabled #ehtw-right
.ehtw-trending-card .ehtw-trending-more {
    display: block !important;
    margin: 0 !important;
    padding: 16px !important;
    border-top: 0 !important;
    background: transparent !important;
}

/* ============================================================
   1.2.0 左栏账号卡片:贴底且较矮窗口可滚动
   ============================================================ */

@media (min-width: 700px) {
    html.ehtw-enabled #ehtw-left {
        display: flex !important;
        flex-direction: column !important;
        align-items: flex-start !important;
        width: 267px !important;
        height: 100vh !important;
        min-height: 0 !important;
        max-height: 100vh !important;
        overflow-y: auto !important;
        overflow-x: hidden !important;
        scrollbar-width: none !important;
    }

    html.ehtw-enabled #ehtw-left > #nb,
    html.ehtw-enabled #ehtw-left > .ehtw-post-button {
        flex: none !important;
        flex-grow: 0 !important;
        flex-shrink: 0 !important;
    }

    html.ehtw-enabled #ehtw-left > .ehtw-account-card {
        display: grid !important;
        grid-template-columns: 40px minmax(0, 1fr) !important;
        align-items: center !important;
        gap: 10px !important;
        flex: none !important;
        flex-grow: 0 !important;
        flex-shrink: 0 !important;
        width: 259px !important;
        min-width: 0 !important;
        max-width: 259px !important;
        height: 65px !important;
        min-height: 65px !important;
        max-height: 65px !important;
        margin: auto 0 12px !important;
        padding: 12px !important;
        border-radius: 9999px !important;
        text-align: left !important;
    }

    html.ehtw-enabled #ehtw-left
    .ehtw-account-text {
        display: flex !important;
        flex: 1 1 auto !important;
        flex-direction: column !important;
        align-items: flex-start !important;
        justify-content: center !important;
        min-width: 0 !important;
        max-width: 100% !important;
        text-align: left !important;
    }

    html.ehtw-enabled #ehtw-left
    .ehtw-account-name,
    html.ehtw-enabled #ehtw-left
    .ehtw-account-handle {
        width: 100% !important;
        min-width: 0 !important;
        max-width: 100% !important;
        text-align: left !important;
    }
}

/* ============================================================
   1.2.0 搜索占位符:覆盖原站全局规则
   ============================================================ */

html.ehtw-enabled #f_search::placeholder {
    color: #536471 !important;
    opacity: 1 !important;
}

@media (prefers-color-scheme: dark) {
    html.ehtw-enabled #f_search::placeholder {
        color: #71767b !important;
        opacity: 1 !important;
    }
}

/* ============================================================
   1.2.0 详情标签:禁止单词内部断行
   ============================================================ */

html.ehtw-detail .ehtw-detail-tags #taglist,
html.ehtw-detail .ehtw-detail-tags #taglist * {
    overflow-wrap: normal !important;
    word-break: normal !important;
}

html.ehtw-detail .ehtw-detail-tags #taglist .gt,
html.ehtw-detail .ehtw-detail-tags #taglist .gtl,
html.ehtw-detail .ehtw-detail-tags #taglist .gtw {
    display: inline-block !important;
    position: static !important;
    float: none !important;
    width: auto !important;
    min-width: 0 !important;
    max-width: 100% !important;
    height: auto !important;
    margin: 0 6px 0 0 !important;
    padding: 0 !important;
    white-space: nowrap !important;
    overflow-wrap: normal !important;
    word-break: normal !important;
    vertical-align: baseline !important;
}

html.ehtw-detail .ehtw-detail-tags #taglist .gt a,
html.ehtw-detail .ehtw-detail-tags #taglist .gtl a,
html.ehtw-detail .ehtw-detail-tags #taglist .gtw a {
    display: inline !important;
    max-width: 100% !important;
    white-space: nowrap !important;
    overflow-wrap: normal !important;
    word-break: normal !important;
}

/* ============================================================
   1.2.0 Gallery actions / About:统一左侧内边距
   ============================================================ */

html.ehtw-detail .ehtw-operation-card #gd5,
html.ehtw-detail .ehtw-about-card #gdd {
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
    margin: 0 !important;
    padding: 12px 16px !important;
    text-align: left !important;
}

html.ehtw-detail .ehtw-operation-card #gd5 p,
html.ehtw-detail .ehtw-operation-card #gd5 div {
    position: static !important;
    float: none !important;
    width: auto !important;
    max-width: 100% !important;
    height: auto !important;
    margin: 0 !important;
    padding: 8px 0 !important;
    background: none !important;
    background-image: none !important;
    border: none !important;
    text-indent: 0 !important;
    text-align: left !important;
}

html.ehtw-detail .ehtw-operation-card #gd5 a {
    margin: 0 !important;
    padding: 0 !important;
    background: none !important;
    background-image: none !important;
    text-indent: 0 !important;
    text-align: left !important;
}

/* ============================================================
   1.2.0 Filters 与首张卡片间距
   ============================================================ */

html.ehtw-enabled #ehtw-right #ehtw-search-card {
    padding-bottom: 8px !important;
}

html.ehtw-enabled #ehtw-right.ehtw-toplist-only-sidebar
> :not(#ehtw-search-card):not(.ehtw-right-search) {
    display: none !important;
}

html.ehtw-enabled #ehtw-right
#ehtw-search-card + .ehtw-side-card,
html.ehtw-enabled #ehtw-right
.ehtw-trending-card {
    margin-top: 0 !important;
}

/* 1.2.1 收尾:手机 More 用网格,操作链接去箭头,右栏卡片间距,占位文字颜色。 */
@media (max-width: 699px) {
    html.ehtw-enabled #ehtw-left #nb.ehtw-nav-open {
        display: grid !important;
        grid-template-columns: repeat(6, minmax(0, 1fr)) !important;
        align-items: center !important;
    }

    html.ehtw-enabled #ehtw-left #nb.ehtw-nav-open > .ehtw-nav-mobile-extra,
    html.ehtw-enabled #ehtw-left #nb.ehtw-nav-open > .ehtw-nav-extra {
        grid-column: 1 / -1 !important;
        order: -1 !important;
        width: 100% !important;
    }

    html.ehtw-enabled #ehtw-left #nb.ehtw-nav-open > .ehtw-nav-entry:not(.ehtw-nav-mobile-extra):not(.ehtw-nav-extra),
    html.ehtw-enabled #ehtw-left #nb.ehtw-nav-open > .ehtw-more-entry {
        grid-column: auto !important;
        order: 0 !important;
        min-width: 0 !important;
        width: 100% !important;
    }

}

html.ehtw-detail .ehtw-operation-card #gd5 p > img {
    display: none !important;
}

html.ehtw-enabled #ehtw-right #ehtw-search-card ~ .ehtw-side-card.ehtw-who-card {
    margin-top: 16px !important;
}

html.ehtw-enabled #ehtw-search-card #f_search::placeholder,
html.ehtw-enabled #ehtw-mobile-search #f_search::placeholder {
    color: #71767b !important;
    -webkit-text-fill-color: #71767b !important;
    opacity: 1 !important;
}
/* ============================================================
   1.3.0 全局设计令牌
   ============================================================ */

html.ehtw-enabled,
html.ehtw-login {
    --ehtw-surface: transparent;
    --ehtw-card-border: var(--ehtw-border, #eff3f4);
    --ehtw-secondary: #536471;
    --ehtw-primary-button-bg: #0f1419;
    --ehtw-primary-button-text: #ffffff;
    --ehtw-secondary-button-border: #536471;
    --ehtw-category-text: #ffffff;
    -webkit-font-smoothing: antialiased;
}

@media (prefers-color-scheme: dark) {
    html.ehtw-enabled,
    html.ehtw-login {
        --ehtw-bg: #000000;
        --ehtw-text: #e7e9ea;
        --ehtw-muted: #71767b;
        --ehtw-border: #2f3336;
        --ehtw-accent: #1d9bf0;
        --ehtw-input: #202327;
        --ehtw-nav-hover: rgba(231, 233, 234, 0.11);
        --ehtw-primary-button-bg: #eff3f4;
        --ehtw-primary-button-text: #0f1419;
        --ehtw-secondary-button-border: #536471;
        --ehtw-secondary: #71767b;
        color-scheme: dark;
    }
}

/* ============================================================
   C1. 统一按钮
   ============================================================ */

html.ehtw-enabled .ehtw-post-button,
html.ehtw-enabled .ehtw-composer-button,
html.ehtw-enabled .ehtw-who-view,
html.ehtw-enabled .ehtw-comments-section #postnewcomment a,
html.ehtw-enabled .ehtw-comments-section #postnewcomment [onclick],
html.ehtw-generic .ehtw-generic-content input[type="submit"],
html.ehtw-generic .ehtw-generic-content button:not(.ehtw-text-button),
html.ehtw-login input[type="submit"] {
    display: inline-flex !important;
    align-items: center !important;
    justify-content: center !important;
    min-height: 36px !important;
    border: none !important;
    border-radius: 9999px !important;
    background: var(--ehtw-primary-button-bg) !important;
    color: var(--ehtw-primary-button-text) !important;
    font-family: inherit !important;
    font-size: 15px !important;
    font-weight: 700 !important;
    text-decoration: none !important;
    cursor: pointer;
}

html.ehtw-enabled .ehtw-post-button {
    height: 52px !important;
    min-height: 52px !important;
    padding: 0 32px !important;
    font-size: 17px !important;
}

html.ehtw-enabled .ehtw-composer-button,
html.ehtw-enabled .ehtw-who-view,
html.ehtw-enabled .ehtw-comments-section #postnewcomment a,
html.ehtw-enabled .ehtw-comments-section #postnewcomment [onclick],
html.ehtw-generic .ehtw-generic-content input[type="submit"],
html.ehtw-generic .ehtw-generic-content button:not(.ehtw-text-button),
html.ehtw-login input[type="submit"] {
    height: 36px !important;
    padding: 0 16px !important;
}

html.ehtw-enabled .ehtw-who-view {
    height: 32px !important;
    min-height: 32px !important;
    font-size: 14px !important;
}

html.ehtw-generic .ehtw-generic-content input[type="button"],
html.ehtw-login input[type="button"],
html.ehtw-enabled input[type="button"][value="Clear"],
html.ehtw-enabled button.ehtw-secondary-button {
    display: inline-flex !important;
    align-items: center !important;
    justify-content: center !important;
    height: 36px !important;
    min-height: 36px !important;
    padding: 0 16px !important;
    border: 1px solid var(--ehtw-secondary-button-border) !important;
    border-radius: 9999px !important;
    background: transparent !important;
    color: var(--ehtw-text) !important;
    font-size: 15px !important;
    font-weight: 700 !important;
    cursor: pointer;
}

html.ehtw-enabled .ehtw-filters-toggle,
html.ehtw-enabled .ehtw-trending-more {
    border: 0 !important;
    background: transparent !important;
    color: var(--ehtw-accent) !important;
    font-weight: 400 !important;
}

html.ehtw-enabled .ehtw-comments-section
#postnewcomment a::after,
html.ehtw-enabled .ehtw-comments-section
#postnewcomment [onclick]::after {
    color: var(--ehtw-primary-button-text) !important;
}

/* ============================================================
   C2. 统一卡片
   ============================================================ */

html.ehtw-enabled #ehtw-right .ehtw-side-card,
html.ehtw-generic .ehtw-generic-content .tdo,
html.ehtw-login body > .d {
    border: 1px solid var(--ehtw-border) !important;
    border-radius: 16px !important;
    background: transparent !important;
    background-image: none !important;
    box-shadow: none !important;
    overflow: hidden;
}

html.ehtw-enabled .ehtw-side-card .ehtw-side-title {
    margin: 0 !important;
    padding: 12px 16px !important;
    font-size: 20px !important;
    font-weight: 800 !important;
    line-height: 24px !important;
    text-align: left !important;
}

html.ehtw-enabled #ehtw-right .ehtw-side-card
.ehtw-trend,
html.ehtw-enabled #ehtw-right .ehtw-side-card
.ehtw-who-row {
    padding: 12px 16px !important;
}

html.ehtw-detail .ehtw-about-card #gdd,
html.ehtw-detail .ehtw-operation-card #gd5 {
    display: block !important;
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
    margin: 0 !important;
    padding: 0 16px 12px !important;
    background: transparent !important;
    text-align: left !important;
}

html.ehtw-detail .ehtw-about-card #gdd table {
    margin-top: 0 !important;
    border-spacing: 0 !important;
    text-align: left !important;
}

html.ehtw-detail .ehtw-about-card #gdd table tr:first-child td {
    padding-top: 0 !important;
}

html.ehtw-detail .ehtw-operation-card #gd5 p,
html.ehtw-detail .ehtw-operation-card #gd5 div {
    margin: 0 !important;
    padding: 8px 0 !important;
    text-align: left !important;
    text-indent: 0 !important;
    background: transparent !important;
    background-image: none !important;
}

/* ============================================================
   C3. 分类色:扁平背景
   ============================================================ */

html.ehtw-enabled {
    --ehtw-ct1: #707070;
    --ehtw-ct2: #fc4e4e;
    --ehtw-ct3: #e78c1a;
    --ehtw-ct4: #c7bf07;
    --ehtw-ct5: #1a9317;
    --ehtw-ct6: #2756aa;
    --ehtw-ct7: #8800c3;
    --ehtw-ct8: #b452a5;
    --ehtw-ct9: #0f9ebd;
    --ehtw-cta: #5dc13b;
}

html.ehtw-enabled .cn.ct1,
html.ehtw-enabled .cs.ct1,
html.ehtw-enabled .ehtw-avatar.ct1 {
    --ehtw-flat-category: var(--ehtw-ct1);
}

html.ehtw-enabled .cn.ct2,
html.ehtw-enabled .cs.ct2,
html.ehtw-enabled .ehtw-avatar.ct2 {
    --ehtw-flat-category: var(--ehtw-ct2);
}

html.ehtw-enabled .cn.ct3,
html.ehtw-enabled .cs.ct3,
html.ehtw-enabled .ehtw-avatar.ct3 {
    --ehtw-flat-category: var(--ehtw-ct3);
}

html.ehtw-enabled .cn.ct4,
html.ehtw-enabled .cs.ct4,
html.ehtw-enabled .ehtw-avatar.ct4 {
    --ehtw-flat-category: var(--ehtw-ct4);
}

html.ehtw-enabled .cn.ct5,
html.ehtw-enabled .cs.ct5,
html.ehtw-enabled .ehtw-avatar.ct5 {
    --ehtw-flat-category: var(--ehtw-ct5);
}

html.ehtw-enabled .cn.ct6,
html.ehtw-enabled .cs.ct6,
html.ehtw-enabled .ehtw-avatar.ct6 {
    --ehtw-flat-category: var(--ehtw-ct6);
}

html.ehtw-enabled .cn.ct7,
html.ehtw-enabled .cs.ct7,
html.ehtw-enabled .ehtw-avatar.ct7 {
    --ehtw-flat-category: var(--ehtw-ct7);
}

html.ehtw-enabled .cn.ct8,
html.ehtw-enabled .cs.ct8,
html.ehtw-enabled .ehtw-avatar.ct8 {
    --ehtw-flat-category: var(--ehtw-ct8);
}

html.ehtw-enabled .cn.ct9,
html.ehtw-enabled .cs.ct9,
html.ehtw-enabled .ehtw-avatar.ct9 {
    --ehtw-flat-category: var(--ehtw-ct9);
}

html.ehtw-enabled .cn.cta,
html.ehtw-enabled .cs.cta,
html.ehtw-enabled .ehtw-avatar.cta {
    --ehtw-flat-category: var(--ehtw-cta);
}

html.ehtw-enabled .cn[class*="ct"],
html.ehtw-enabled .cs[class*="ct"],
html.ehtw-enabled .ehtw-avatar[class*="ct"] {
    background-color: var(--ehtw-flat-category, #707070) !important;
    background-image: none !important;
    border: 0 !important;
    box-shadow: none !important;
    color: var(--ehtw-category-text) !important;
}

html.ehtw-enabled .ehtw-avatar[class*="ct"] {
    border-radius: 50% !important;
}

html.ehtw-list .ehtw-tweet-author
.cn[class*="ct"] {
    background: transparent !important;
    background-image: none !important;
    border: none !important;
    box-shadow: none !important;
    color: var(--ehtw-muted) !important;
}

html.ehtw-enabled #ehtw-search-card .cn[class*="ct"],
html.ehtw-enabled #ehtw-search-card .cs[class*="ct"],
html.ehtw-enabled #ehtw-mobile-search .cn[class*="ct"],
html.ehtw-enabled #ehtw-mobile-search .cs[class*="ct"] {
    height: 28px !important;
    min-height: 28px !important;
    max-height: 28px !important;
    padding: 4px 9px !important;
    border-radius: 9999px !important;
    color: #ffffff !important;
    font-size: 13px !important;
    line-height: 20px !important;
    box-shadow: none !important;
}

html.ehtw-enabled .cn[data-disabled],
html.ehtw-enabled .cs[data-disabled],
html.ehtw-enabled .cn[data-disabled="1"],
html.ehtw-enabled .cs[data-disabled="1"],
html.ehtw-enabled .cn[data-disabled="true"],
html.ehtw-enabled .cs[data-disabled="true"] {
    opacity: 0.4 !important;
}

/* ============================================================
   A1. 搜索结果数量
   ============================================================ */

html.ehtw-list #ehtw-center .ehtw-results-count {
    display: block !important;
    min-width: 0 !important;
    width: 100% !important;
    padding: 10px 16px !important;
    border-bottom: 1px solid var(--ehtw-border) !important;
    color: var(--ehtw-muted) !important;
    font-size: 13px !important;
    text-align: left !important;
}

html.ehtw-list #ehtw-center .ehtw-results-count .searchtext {
    display: block !important;
    position: static !important;
    width: auto !important;
    height: auto !important;
    min-width: 0 !important;
    margin: 0 !important;
    padding: 0 !important;
    background: transparent !important;
    border: none !important;
    color: var(--ehtw-muted) !important;
    font-size: 13px !important;
    text-align: left !important;
}

/* ============================================================
   A2 / C7. 统一上下文头部
   ============================================================ */

html.ehtw-enabled #ehtw-header {
    display: flex;
    align-items: center;
    min-height: 53px !important;
    height: 53px !important;
    max-height: 53px !important;
    border-bottom: 1px solid var(--ehtw-border) !important;
    background: var(--ehtw-glass) !important;
    backdrop-filter: blur(14px);
    -webkit-backdrop-filter: blur(14px);
}

html.ehtw-enabled #ehtw-header.ehtw-tabs-header {
    display: grid !important;
    padding: 0 !important;
}

/* 1.17.261: phone header / bottom nav hide while scrolling down.
   1.17.263: the header is hidden at once (no slide) so iOS Safari stops
   tinting the status bar with it; visibility keeps its layout space so the
   page does not jump. */
@media (max-width: 699px) {
    /* 1.17.264: also drop out of sticky, so the (scrolled-away) header no
       longer sits at the top edge where Safari samples its tint colour.
       Sticky and relative take the same flow space, so nothing jumps. */
    html.ehtw-enabled.ehtw-chrome-scroll-hidden:not(.ehtw-reader) #ehtw-header {
        position: relative !important;
        visibility: hidden !important;
        pointer-events: none !important;
    }

    /* 1.17.262: the bottom nav disappears at once instead of sliding. */
    html.ehtw-enabled.ehtw-chrome-scroll-hidden:not(.ehtw-reader) #ehtw-left {
        display: none !important;
    }
}

html.ehtw-enabled #ehtw-header.ehtw-context-header {
    display: flex !important;
    gap: 20px !important;
    padding: 0 16px !important;
}

html.ehtw-enabled #ehtw-header .ehtw-back-link {
    display: flex !important;
    flex: 0 0 34px;
    align-items: center;
    justify-content: center;
    width: 34px !important;
    height: 34px !important;
    min-width: 34px !important;
    min-height: 34px !important;
    margin: 0 !important;
    padding: 0 !important;
    border-radius: 50% !important;
    background: transparent !important;
    color: var(--ehtw-text) !important;
}

html.ehtw-enabled #ehtw-header .ehtw-back-link:hover {
    background: var(--ehtw-nav-hover) !important;
}

html.ehtw-enabled #ehtw-header .ehtw-context-heading {
    display: flex;
    flex-direction: column;
    justify-content: center;
    min-width: 0;
    max-width: 100%;
    height: 100%;
    text-align: left;
}

html.ehtw-enabled #ehtw-header .ehtw-context-title {
    display: block !important;
    width: 100%;
    min-width: 0;
    margin: 0 !important;
    padding: 0 !important;
    color: var(--ehtw-text) !important;
    font-size: 20px !important;
    font-weight: 700 !important;
    line-height: 24px !important;
    text-align: left !important;
    white-space: nowrap !important;
    overflow: hidden;
    text-overflow: ellipsis;
}

html.ehtw-enabled #ehtw-header .ehtw-context-subtitle {
    display: block;
    color: var(--ehtw-muted) !important;
    font-size: 13px !important;
    line-height: 16px !important;
    text-align: left !important;
}

html.ehtw-list #ehtw-center:has(.ehtw-context-header)
.ehtw-composer {
    display: none !important;
}

/* ============================================================
   A3. 转发状态
   ============================================================ */

html.ehtw-list .ehtw-twitter-action-repost
.ehtw-repost-native {
    cursor: pointer;
}

html.ehtw-list .ehtw-twitter-action-repost
.ehtw-repost-native a {
    position: absolute !important;
    inset: 0 !important;
    display: block !important;
    width: 100% !important;
    height: 100% !important;
    z-index: 2;
}

html.ehtw-list .ehtw-twitter-action-repost
.ehtw-repost-native a img {
    position: absolute !important;
    inset: 0 !important;
    display: block !important;
    width: 100% !important;
    height: 100% !important;
    opacity: 0 !important;
    cursor: pointer;
}

html.ehtw-list .ehtw-repost-available:hover,
html.ehtw-list .ehtw-repost-available:hover svg {
    color: #00ba7c !important;
}

html.ehtw-list .ehtw-repost-available:hover
.ehtw-twitter-action-icon {
    background: rgba(0, 186, 124, 0.1) !important;
}

html.ehtw-list .ehtw-repost-unavailable,
html.ehtw-list .ehtw-repost-unavailable:hover,
html.ehtw-list .ehtw-repost-unavailable svg,
html.ehtw-list .ehtw-repost-unavailable:hover svg {
    color: var(--ehtw-muted) !important;
    cursor: default !important;
}

html.ehtw-list .ehtw-repost-unavailable
.ehtw-twitter-action-icon {
    opacity: 0.35 !important;
}

html.ehtw-list .ehtw-repost-unavailable:hover
.ehtw-twitter-action-icon {
    background: transparent !important;
}

/* ============================================================
   A5. 详情页统计及回复数
   ============================================================ */

html.ehtw-detail .ehtw-detail-stats {
    display: flex !important;
    flex-wrap: wrap !important;
    gap: 8px 16px !important;
    padding: 12px 0 !important;
}

html.ehtw-detail .ehtw-detail-stat {
    display: inline-flex !important;
    align-items: baseline !important;
    gap: 4px !important;
    font-size: 15px !important;
    white-space: nowrap;
}

html.ehtw-detail .ehtw-detail-reply-count {
    display: inline-flex !important;
    align-items: center !important;
    gap: 8px !important;
    margin: 10px 0 !important;
    padding: 4px 8px 4px 0 !important;
    color: var(--ehtw-muted) !important;
    font-size: 13px !important;
    text-decoration: none !important;
}

html.ehtw-detail .ehtw-detail-reply-count svg {
    width: 19px !important;
    height: 19px !important;
}

html.ehtw-detail .ehtw-detail-reply-count:hover {
    color: var(--ehtw-accent) !important;
}

/* ============================================================
   B1. Generic 页面基础
   ============================================================ */

html.ehtw-generic body {
    background: var(--ehtw-bg) !important;
    color: var(--ehtw-text) !important;
}

html.ehtw-generic #ehtw-center {
    min-width: 0 !important;
    max-width: 100% !important;
}

html.ehtw-generic .ehtw-generic-content {
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
    padding: 0 16px !important;
    color: var(--ehtw-text) !important;
    overflow-wrap: anywhere;
}

html.ehtw-generic .ehtw-generic-content
:is(div.ido, .dc, #newsouter, #newsinner, #nt, .d) {
    min-width: 0 !important;
    width: auto !important;
    max-width: 100% !important;
    height: auto !important;
    background: none !important;
    background-image: none !important;
    border: 0 !important;
    margin: 0 !important;
}

html.ehtw-generic .ehtw-generic-content
:is(table, tr, td, th) {
    max-width: 100% !important;
    background: transparent !important;
    background-image: none !important;
    border-color: var(--ehtw-border) !important;
    color: var(--ehtw-text) !important;
}

html.ehtw-generic .ehtw-generic-content a {
    color: var(--ehtw-accent) !important;
}

html.ehtw-generic .ehtw-generic-content h1 {
    display: none !important;
}

html.ehtw-generic .ehtw-generic-content h2 {
    font-size: 20px !important;
    font-weight: 800 !important;
    line-height: 24px !important;
    padding: 12px 16px !important;
    margin: 0 !important;
    color: var(--ehtw-text) !important;
}

html.ehtw-generic .ehtw-generic-content
:is(
    input[type="text"],
    input[type="password"],
    input[type="number"],
    textarea,
    select
) {
    box-sizing: border-box !important;
    max-width: 100% !important;
    min-height: 40px !important;
    height: 40px !important;
    padding: 0 16px !important;
    margin: 4px 0 !important;
    border: 1px solid var(--ehtw-border) !important;
    border-radius: 9999px !important;
    background: transparent !important;
    color: var(--ehtw-text) !important;
    font-size: 15px !important;
    outline: none !important;
    box-shadow: none !important;
}

html.ehtw-generic .ehtw-generic-content textarea {
    min-height: 110px !important;
    height: auto !important;
    padding: 12px 16px !important;
    border-radius: 16px !important;
}

html.ehtw-generic .ehtw-generic-content
:is(
    input[type="text"],
    input[type="password"],
    input[type="number"],
    textarea,
    select
):focus {
    border-color: var(--ehtw-accent) !important;
}

html.ehtw-generic .ehtw-generic-content
input::placeholder,
html.ehtw-generic .ehtw-generic-content
textarea::placeholder {
    color: var(--ehtw-muted) !important;
    opacity: 1 !important;
}

/* ============================================================
   B2. Torrents
   ============================================================ */

html.ehtw-generic .ehtw-generic-content #torrentform {
    display: block !important;
    width: 100% !important;
    min-width: 0 !important;
    padding: 12px 0 !important;
    border: none !important;
    background: transparent !important;
}

html.ehtw-generic #torrentform p a {
    display: inline-flex !important;
    align-items: center !important;
    min-height: 28px !important;
    padding: 4px 10px !important;
    margin: 2px 4px 2px 0 !important;
    border: 1px solid var(--ehtw-border) !important;
    border-radius: 9999px !important;
    background: transparent !important;
    font-size: 13px !important;
    color: var(--ehtw-text) !important;
    text-decoration: none !important;
}

html.ehtw-generic #torrentform p a:hover {
    background: var(--ehtw-nav-hover) !important;
}

html.ehtw-generic #torrentform #focusme {
    width: 100% !important;
    max-width: 100% !important;
}

html.ehtw-generic .ehtw-generic-content
table.itg,
html.ehtw-generic .ehtw-generic-content
table.itg > tbody {
    display: block !important;
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
    table-layout: auto !important;
    border: 0 !important;
    border-collapse: collapse !important;
    background: transparent !important;
}

html.ehtw-generic .ehtw-generic-content
table.itg tr:has(> td.itd) {
    display: flex !important;
    flex-wrap: wrap !important;
    align-items: baseline !important;
    gap: 4px 12px !important;
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
    margin: 0 !important;
    padding: 12px 16px !important;
    border: 0 !important;
    border-bottom: 1px solid var(--ehtw-border) !important;
    background: transparent !important;
}

html.ehtw-generic .ehtw-generic-content
table.itg tr:has(> td.itd):hover {
    background: var(--ehtw-hover) !important;
}

html.ehtw-generic .ehtw-generic-content
table.itg td.itd {
    display: block !important;
    position: static !important;
    float: none !important;
    flex: 0 1 auto !important;
    width: auto !important;
    min-width: 0 !important;
    max-width: 100% !important;
    height: auto !important;
    margin: 0 !important;
    padding: 0 !important;
    border: none !important;
    background: transparent !important;
    color: var(--ehtw-muted) !important;
    font-size: 13px !important;
    line-height: 18px !important;
    text-align: left !important;
}

html.ehtw-generic .ehtw-generic-content
table.itg tr:has(> td.itd) > td:nth-child(2) {
    flex: 0 0 100% !important;
    order: -1 !important;
    overflow: hidden !important;
    white-space: nowrap !important;
    text-overflow: ellipsis !important;
    font-size: 15px !important;
    font-weight: 700 !important;
    line-height: 20px !important;
}

html.ehtw-generic .ehtw-generic-content
table.itg tr:has(> td.itd) > td:nth-child(2) a {
    display: block !important;
    min-width: 0 !important;
    max-width: 100% !important;
    overflow: hidden !important;
    white-space: nowrap !important;
    text-overflow: ellipsis !important;
    color: var(--ehtw-text) !important;
    font-size: 15px !important;
    font-weight: 700 !important;
}

html.ehtw-generic .ehtw-generic-content
table.itg tr:has(> td.itd) > td:nth-child(3) a {
    color: var(--ehtw-accent) !important;
}

html.ehtw-generic .ehtw-generic-content
table.itg tr:has(> td.itd) > td:nth-child(4)::before {
    content: "Size ";
}

html.ehtw-generic .ehtw-generic-content
table.itg tr:has(> td.itd) > td:nth-child(5)::before {
    content: "Seeds ";
}

html.ehtw-generic .ehtw-generic-content
table.itg tr:has(> td.itd) > td:nth-child(6)::before {
    content: "Peers ";
}

html.ehtw-generic .ehtw-generic-content
table.itg tr:has(> td.itd) > td:nth-child(7)::before {
    content: "DLs ";
}

html.ehtw-generic .ehtw-generic-content
table.itg tr:has(> td.itd) > td:nth-child(8) {
    margin-left: auto !important;
}

html.ehtw-generic .ehtw-generic-content
table.itg tr:has(> th) {
    display: flex !important;
    align-items: center !important;
    flex-wrap: wrap !important;
    gap: 12px !important;
    width: 100% !important;
    padding: 8px 16px !important;
    border-bottom: 1px solid var(--ehtw-border) !important;
    background: transparent !important;
}

html.ehtw-generic .ehtw-generic-content
table.itg tr:has(> th) th {
    display: block !important;
    width: auto !important;
    min-width: 0 !important;
    max-width: 100% !important;
    margin: 0 !important;
    padding: 0 !important;
    border: none !important;
    background: transparent !important;
    color: var(--ehtw-muted) !important;
    font-size: 13px !important;
    font-weight: 400 !important;
    line-height: 18px !important;
}

/* ============================================================
   B3. Toplists
   ============================================================ */

html.ehtw-generic .ehtw-generic-content .dc:has(.tdo) {
    display: grid !important;
    grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
    align-items: start !important;
    gap: 16px !important;
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
    padding: 16px !important;
    background: transparent !important;
}

html.ehtw-generic .ehtw-generic-content .dc:has(.tdo) > h2 {
    grid-column: 1 / -1 !important;
    padding: 0 !important;
    margin: 0 !important;
}

html.ehtw-generic .ehtw-generic-content .tdo {
    display: block !important;
    position: static !important;
    float: none !important;
    width: auto !important;
    min-width: 0 !important;
    max-width: 100% !important;
    height: auto !important;
    margin: 0 !important;
    padding: 0 !important;
    border: 1px solid var(--ehtw-border) !important;
    border-radius: 16px !important;
    background: transparent !important;
    box-shadow: none !important;
    overflow: hidden !important;
}

html.ehtw-generic .ehtw-generic-content .tdo > p {
    margin: 0 !important;
    padding: 12px 16px !important;
    font-size: 15px !important;
    font-weight: 700 !important;
    line-height: 20px !important;
}

html.ehtw-generic .ehtw-generic-content .tdo img.mr {
    display: none !important;
}

html.ehtw-generic .ehtw-generic-content .tdo table.ttl,
html.ehtw-generic .ehtw-generic-content .tdo table.ttl > tbody {
    display: block !important;
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
    border: 0 !important;
    background: transparent !important;
}

html.ehtw-generic .ehtw-generic-content .tdo table.ttl tr {
    display: flex !important;
    align-items: center !important;
    gap: 8px !important;
    width: 100% !important;
    min-width: 0 !important;
    padding: 8px 16px !important;
    border: none !important;
    background: transparent !important;
}

html.ehtw-generic .ehtw-generic-content .tdo table.ttl tr:hover {
    background: var(--ehtw-nav-hover) !important;
}

html.ehtw-generic .ehtw-generic-content .tdo table.ttl td {
    display: block !important;
    min-width: 0 !important;
    width: auto !important;
    max-width: 100% !important;
    margin: 0 !important;
    padding: 0 !important;
    border: none !important;
}

html.ehtw-generic .ehtw-generic-content .tdo table.ttl td.pso {
    flex: 0 0 28px !important;
    width: 28px !important;
    max-width: 28px !important;
    color: var(--ehtw-muted) !important;
    font-size: 13px !important;
}

html.ehtw-generic .ehtw-generic-content .tdo .tun {
    display: block !important;
    min-width: 0 !important;
    max-width: 100% !important;
    white-space: nowrap !important;
    overflow: hidden !important;
    text-overflow: ellipsis !important;
}

/* ============================================================
   B4. News
   ============================================================ */

html.ehtw-generic #newsouter .baredge,
html.ehtw-generic #newsouter #botm,
html.ehtw-generic #newsouter .bartop {
    display: none !important;
}

html.ehtw-generic #newsouter,
html.ehtw-generic #newsinner,
html.ehtw-generic #nt,
html.ehtw-generic #nt > tbody,
html.ehtw-generic #nt tr,
html.ehtw-generic #nt td {
    display: block !important;
    position: static !important;
    float: none !important;
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
    height: auto !important;
    margin: 0 !important;
    padding: 0 !important;
    border: none !important;
    background: transparent !important;
}

html.ehtw-generic #newsouter .nwo {
    display: block !important;
    width: 100% !important;
    min-width: 0 !important;
    padding: 12px 16px !important;
    margin: 0 !important;
    border: none !important;
    border-bottom: 1px solid var(--ehtw-border) !important;
    border-radius: 0 !important;
    background: transparent !important;
    box-shadow: none !important;
}

html.ehtw-generic #newsouter .nwo h2 {
    padding: 0 0 8px !important;
    margin: 0 !important;
    font-size: 17px !important;
    font-weight: 700 !important;
    line-height: 24px !important;
}

html.ehtw-generic #newsouter .nwo h2 a {
    color: var(--ehtw-text) !important;
    text-decoration: none !important;
}

html.ehtw-generic #newsouter .nwo h2 a:hover {
    text-decoration: underline !important;
}

html.ehtw-generic #newsouter .nwi {
    display: block !important;
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
    padding: 0 !important;
    margin: 0 !important;
    background: transparent !important;
    border: none !important;
    color: var(--ehtw-text) !important;
    font-size: 15px !important;
    line-height: 20px !important;
}

/* 不覆盖 .nwi 内部带内联颜色的文字。 */

/* ============================================================
   B5. Watched 空状态
   ============================================================ */

html.ehtw-generic .ehtw-generic-content.ehtw-generic-empty {
    display: flex !important;
    flex-direction: column !important;
    align-items: center !important;
    justify-content: center !important;
    min-height: 200px !important;
    padding: 32px 16px !important;
    text-align: center !important;
}

html.ehtw-generic .ehtw-generic-empty
:is(h2, strong) {
    display: block !important;
    color: var(--ehtw-text) !important;
    font-size: 20px !important;
    font-weight: 700 !important;
    line-height: 26px !important;
    text-align: center !important;
}

html.ehtw-generic .ehtw-generic-empty
:is(p, div:not(.ido), span) {
    color: var(--ehtw-muted) !important;
    font-size: 15px !important;
    line-height: 22px !important;
    text-align: center !important;
}

/* ============================================================
   B6. 登录页:仅 CSS,不访问表单数据
   ============================================================ */

html.ehtw-login {
    --ehtw-bg: #ffffff;
    --ehtw-text: #0f1419;
    --ehtw-muted: #536471;
    --ehtw-border: #eff3f4;
    --ehtw-accent: #1d9bf0;
    background: var(--ehtw-bg) !important;
    color: var(--ehtw-text) !important;
    font-family:
        TwitterChirp,
        -apple-system,
        BlinkMacSystemFont,
        "Segoe UI",
        Roboto,
        Helvetica,
        Arial,
        sans-serif !important;
}

html.ehtw-login body {
    display: flex !important;
    flex-direction: column !important;
    align-items: center !important;
    justify-content: center !important;
    min-height: 100vh !important;
    width: 100% !important;
    margin: 0 !important;
    padding: 16px 0 !important;
    background: var(--ehtw-bg) !important;
    background-image: none !important;
    color: var(--ehtw-text) !important;
}

html.ehtw-login body > .d {
    display: block !important;
    position: static !important;
    float: none !important;
    width: 400px !important;
    min-width: 0 !important;
    max-width: calc(100% - 32px) !important;
    height: auto !important;
    margin: 0 auto !important;
    padding: 32px !important;
    border: 1px solid var(--ehtw-border) !important;
    border-radius: 16px !important;
    background: transparent !important;
    background-image: none !important;
    box-shadow: none !important;
    text-align: left !important;
}

html.ehtw-login body > .d p {
    color: var(--ehtw-text) !important;
    font-size: 15px !important;
    line-height: 22px !important;
    text-align: left !important;
}

html.ehtw-login form[name="ipb_login_form"],
html.ehtw-login form[name="ipb_login_form"] table,
html.ehtw-login form[name="ipb_login_form"] tbody,
html.ehtw-login form[name="ipb_login_form"] tr,
html.ehtw-login form[name="ipb_login_form"] td {
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
    height: auto !important;
    background: transparent !important;
    border: none !important;
    color: var(--ehtw-text) !important;
    text-align: left !important;
}

html.ehtw-login form[name="ipb_login_form"] table,
html.ehtw-login form[name="ipb_login_form"] tbody,
html.ehtw-login form[name="ipb_login_form"] tr,
html.ehtw-login form[name="ipb_login_form"] td {
    display: block !important;
    margin: 0 !important;
    padding: 0 !important;
}

html.ehtw-login
:is(input[type="text"], input[type="password"]) {
    display: block !important;
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
    height: 40px !important;
    min-height: 40px !important;
    margin: 6px 0 14px !important;
    padding: 0 16px !important;
    border: 1px solid var(--ehtw-border) !important;
    border-radius: 9999px !important;
    background: transparent !important;
    color: var(--ehtw-text) !important;
    font-size: 15px !important;
    outline: none !important;
    box-shadow: none !important;
}

html.ehtw-login
:is(input[type="text"], input[type="password"]):focus {
    border-color: var(--ehtw-accent) !important;
}

html.ehtw-login input::placeholder {
    color: var(--ehtw-muted) !important;
    opacity: 1 !important;
}

html.ehtw-login input[type="submit"] {
    width: 100% !important;
    margin: 8px 0 0 !important;
}

html.ehtw-login a {
    color: var(--ehtw-accent) !important;
}

/* ============================================================
   C4. 非推文模式视觉统一
   ============================================================ */

html.ehtw-other .ehtw-other-content .itg.gld .gl1t {
    border: 1px solid var(--ehtw-border) !important;
    border-radius: 0 !important;
    background: transparent !important;
    background-image: none !important;
}

html.ehtw-other .ehtw-other-content .itg.gld .gl3t {
    border: 1px solid var(--ehtw-border) !important;
    border-radius: 16px !important;
    background: transparent !important;
    background-image: none !important;
    box-shadow: none !important;
}

html.ehtw-other .ehtw-other-content
:is(.gl3t, .gl3m, .gl4e) .glink,
html.ehtw-other .ehtw-other-content
.glink {
    color: var(--ehtw-text) !important;
    font-size: 15px !important;
    line-height: 20px !important;
}

html.ehtw-other .ehtw-other-content
:is(table.itg.glte, table.itg.gltm)
:is(.gt, .gtl, .gtw) {
    display: inline !important;
    position: static !important;
    float: none !important;
    width: auto !important;
    max-width: 100% !important;
    height: auto !important;
    margin: 0 6px 0 0 !important;
    padding: 0 !important;
    border: none !important;
    border-radius: 0 !important;
    background: transparent !important;
    background-image: none !important;
    box-shadow: none !important;
    color: var(--ehtw-accent) !important;
    font-size: 13px !important;
    font-weight: 400 !important;
    line-height: 20px !important;
    white-space: normal !important;
}

html.ehtw-other .ehtw-other-content
:is(table.itg.glte, table.itg.gltm)
:is(.gt, .gtl, .gtw)::before {
    content: "#" !important;
}

html.ehtw-other .ehtw-other-content
:is(table.itg.glte, table.itg.gltm)
:is(.gt, .gtl, .gtw) a {
    color: var(--ehtw-accent) !important;
    font-weight: 400 !important;
    text-decoration: none !important;
}

html.ehtw-other .ehtw-other-content
:is(table.itg.glte, table.itg.gltm)
:is(.gt, .gtl, .gtw) a:hover {
    text-decoration: underline !important;
}

html.ehtw-other .ehtw-other-content
:is(table.itg.glte, table.itg.gltm) .tc {
    display: none !important;
}

html.ehtw-other .ehtw-other-content
table.itg.glte .gl3e > .cn {
    display: inline-flex !important;
    align-items: center !important;
    justify-content: center !important;
    width: auto !important;
    height: 20px !important;
    min-height: 20px !important;
    max-height: 20px !important;
    padding: 0 8px !important;
    margin: 0 !important;
    border: none !important;
    border-radius: 9999px !important;
    font-size: 12px !important;
    line-height: 20px !important;
}

html.ehtw-other .ehtw-other-content
table.itg.gltm th {
    background: transparent !important;
    background-image: none !important;
    color: var(--ehtw-muted) !important;
    font-size: 13px !important;
    font-weight: 400 !important;
}

html.ehtw-other .ehtw-other-content
table.itg.gltm tr:hover,
html.ehtw-other .ehtw-other-content
table.itg.glte tr:hover {
    background: var(--ehtw-hover) !important;
}

html.ehtw-other .ehtw-other-content
:is(.gt, .gtl, .gtw)[style*="background"] {
    background: transparent !important;
    background-image: none !important;
}

/* ============================================================
   C5. 全站 ptt / ptb 胶囊分页
   ============================================================ */

html.ehtw-enabled #ehtw-center
:is(table.ptt, table.ptb),
html.ehtw-generic .ehtw-generic-content
:is(table.ptt, table.ptb) {
    display: block !important;
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
    height: auto !important;
    margin: 8px 0 !important;
    padding: 0 !important;
    border: none !important;
    border-collapse: separate !important;
    border-spacing: 0 !important;
    background: transparent !important;
    background-image: none !important;
}

html.ehtw-enabled #ehtw-center
:is(table.ptt, table.ptb) > tbody,
html.ehtw-enabled #ehtw-center
:is(table.ptt, table.ptb) tr {
    display: flex !important;
    flex-wrap: wrap !important;
    align-items: center !important;
    justify-content: flex-start !important;
    gap: 6px !important;
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
    height: auto !important;
    background: transparent !important;
}

html.ehtw-enabled #ehtw-center
:is(table.ptt, table.ptb) td {
    display: inline-flex !important;
    align-items: center !important;
    justify-content: center !important;
    position: static !important;
    float: none !important;
    width: auto !important;
    min-width: 32px !important;
    max-width: 100% !important;
    height: 32px !important;
    min-height: 32px !important;
    margin: 0 !important;
    padding: 0 !important;
    border: 0 !important;
    border-radius: 9999px !important;
    background: transparent !important;
    background-image: none !important;
    color: var(--ehtw-text) !important;
    font-size: 12px !important;
    text-align: center !important;
}

html.ehtw-enabled #ehtw-center
:is(table.ptt, table.ptb) td a {
    display: inline-flex !important;
    align-items: center !important;
    justify-content: center !important;
    min-width: 32px !important;
    min-height: 32px !important;
    padding: 0 11px !important;
    border: 0 !important;
    border-radius: 9999px !important;
    background: transparent !important;
    color: var(--ehtw-text) !important;
    font-size: 12px !important;
    text-decoration: none !important;
}

html.ehtw-enabled #ehtw-center
:is(table.ptt, table.ptb) td a:hover {
    background: var(--ehtw-nav-hover) !important;
    color: var(--ehtw-accent) !important;
}

html.ehtw-enabled #ehtw-center
:is(table.ptt, table.ptb) td.ptds {
    background: var(--ehtw-accent) !important;
    color: #ffffff !important;
    font-weight: 700 !important;
}

html.ehtw-enabled #ehtw-center
:is(table.ptt, table.ptb) td.ptds a {
    background: var(--ehtw-accent) !important;
    color: #ffffff !important;
}

html.ehtw-enabled #ehtw-center
:is(table.ptt, table.ptb) td.ptdd,
html.ehtw-enabled #ehtw-center
:is(table.ptt, table.ptb) td.ptdd a {
    background: transparent !important;
    color: var(--ehtw-muted) !important;
}

html.ehtw-enabled #ehtw-center
.ehtw-pagination:not(.ehtw-top-toolbar) .searchnav a,
html.ehtw-enabled #ehtw-center
.ehtw-pagination:not(.ehtw-top-toolbar) .searchnav select {
    display: inline-flex !important;
    align-items: center !important;
    justify-content: center !important;
    min-height: 32px !important;
    padding: 0 11px !important;
    border: 1px solid var(--ehtw-border) !important;
    border-radius: 9999px !important;
    background: transparent !important;
    color: var(--ehtw-text) !important;
    font-size: 12px !important;
}

html.ehtw-enabled #ehtw-center
.ehtw-pagination:not(.ehtw-top-toolbar) .searchnav a:hover {
    background: var(--ehtw-nav-hover) !important;
}

/* ============================================================
   C6. 评论分数 / Uploader Comment
   ============================================================ */

html.ehtw-detail .ehtw-comments-section #cdiv
.c1.ehtw-comment-tweet .c2 {
    overflow: visible !important;
    background: transparent !important;
    border: none !important;
}

html.ehtw-detail .ehtw-comments-section #cdiv
.c1.ehtw-comment-tweet .c3 {
    flex: 1 1 auto !important;
    min-width: 0 !important;
    background: none !important;
    background-image: none !important;
    overflow: visible !important;
}

html.ehtw-detail .ehtw-comments-section #cdiv
.c1.ehtw-comment-tweet .c4 {
    position: static !important;
    float: none !important;
    flex: 0 0 auto !important;
    width: auto !important;
    max-width: none !important;
    margin: 0 0 0 auto !important;
    padding: 0 !important;
    overflow: visible !important;
    text-align: right !important;
    color: var(--ehtw-muted) !important;
    font-size: 13px !important;
    font-weight: 400 !important;
    line-height: 20px !important;
}

html.ehtw-detail .ehtw-comments-section #cdiv
.c1.ehtw-comment-tweet .c4 *,
html.ehtw-detail .ehtw-comments-section #cdiv
.c1.ehtw-comment-tweet .c3 .c {
    color: var(--ehtw-muted) !important;
    font-size: 13px !important;
    font-weight: 400 !important;
}

/* ============================================================
   通用页面窄屏
   ============================================================ */

@media (max-width: 699px) {
    html.ehtw-generic .ehtw-generic-content {
        padding: 0 12px !important;
    }

    html.ehtw-generic .ehtw-generic-content
    .dc:has(.tdo) {
        grid-template-columns: minmax(0, 1fr) !important;
        padding: 12px !important;
    }

    html.ehtw-generic .ehtw-generic-content
    .dc:has(.tdo) > h2 {
        grid-column: 1 !important;
    }

    html.ehtw-generic .ehtw-generic-content
    table.itg tr:has(> td.itd) {
        padding: 12px 8px !important;
    }

    html.ehtw-enabled #ehtw-header.ehtw-context-header {
        padding: 0 12px !important;
        gap: 12px !important;
    }
}
/* ============================================================
   1.4.0 功能回归:body > .ido 空壳
   ============================================================ */

html.ehtw-enabled body > .ido {
    display: block !important;
    position: static !important;
    float: none !important;
    width: auto !important;
    min-width: 0 !important;
    max-width: 100% !important;
    height: 0 !important;
    min-height: 0 !important;
    max-height: 0 !important;
    margin: 0 !important;
    padding: 0 !important;
    border: 0 !important;
    background: transparent !important;
    background-image: none !important;
    box-shadow: none !important;
    overflow: hidden !important;
}

/* ============================================================
   1.4.0 Filters:文件搜索面板和日期范围条
   ============================================================ */

html.ehtw-enabled #searchbox.ehtw-filters-collapsed:not(.ehtw-filters-open)
:is(#fsdiv, #rangebar) {
    display: none !important;
}

html.ehtw-enabled #searchbox.ehtw-filters-open #fsdiv {
    display: block !important;
    position: static !important;
    float: none !important;
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
    height: auto !important;
    min-height: 0 !important;
    margin: 8px 0 !important;
    padding: 12px !important;
    border: 1px solid var(--ehtw-border) !important;
    border-radius: 16px !important;
    background: transparent !important;
    background-image: none !important;
    color: var(--ehtw-text) !important;
    overflow: visible !important;
    text-align: left !important;
}

html.ehtw-enabled #searchbox #fsdiv
:is(input[type="text"], input[type="number"], select) {
    max-width: 100% !important;
    min-height: 36px !important;
    padding: 0 12px !important;
    border: 1px solid var(--ehtw-border) !important;
    border-radius: 9999px !important;
    background: transparent !important;
    color: var(--ehtw-text) !important;
    font-size: 13px !important;
}

html.ehtw-enabled #searchbox #fsdiv
:is(input[type="text"], input[type="number"], select):focus {
    border-color: var(--ehtw-accent) !important;
    outline: none !important;
}

html.ehtw-enabled #searchbox.ehtw-filters-open #rangebar {
    display: flex !important;
    position: static !important;
    float: none !important;
    flex-direction: row !important;
    align-items: center !important;
    flex-wrap: nowrap !important;
    gap: 2px !important;
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
    height: 16px !important;
    min-height: 16px !important;
    margin: 8px 0 !important;
    padding: 4px 0 !important;
    border: 0 !important;
    background: transparent !important;
    overflow: hidden !important;
}

html.ehtw-enabled #searchbox #rangebar > a {
    display: block !important;
    position: static !important;
    float: none !important;
    flex: 1 1 0 !important;
    width: auto !important;
    min-width: 0 !important;
    max-width: none !important;
    height: 8px !important;
    min-height: 8px !important;
    margin: 0 !important;
    padding: 0 !important;
    border: none !important;
    text-decoration: none !important;
}

html.ehtw-enabled #searchbox #rangebar > a > div {
    display: block !important;
    position: static !important;
    width: 100% !important;
    height: 8px !important;
    min-height: 8px !important;
    margin: 0 !important;
    padding: 0 !important;
    border: none !important;
    border-radius: 2px !important;
    background: var(--ehtw-border) !important;
    box-shadow: none !important;
}

html.ehtw-enabled #searchbox #rangebar
[data-inrange]:not([data-inrange="0"]):not([data-inrange="false"]) {
    background: var(--ehtw-accent) !important;
}

html.ehtw-enabled #searchbox #rangebar > a:hover > div {
    filter: brightness(1.2);
}

/* ============================================================
   1.4.0 无原站搜索框页面:右栏 GET 搜索
   ============================================================ */

html.ehtw-enabled #ehtw-right .ehtw-right-search {
    order: 0 !important;
    display: block !important;
    position: sticky !important;
    top: 0 !important;
    z-index: 110 !important;
    flex: none !important;
    width: 350px !important;
    min-width: 0 !important;
    max-width: 350px !important;
    min-height: 52px !important;
    margin: 0 !important;
    padding: 7px 0 8px !important;
    border: none !important;
    background: var(--ehtw-bg) !important;
}

html.ehtw-enabled #ehtw-right .ehtw-right-search-form,
html.ehtw-enabled #ehtw-right .ehtw-right-search-wrap {
    display: block !important;
    position: relative !important;
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
    height: auto !important;
    margin: 0 !important;
    padding: 0 !important;
    border: none !important;
    background: transparent !important;
}

html.ehtw-enabled #ehtw-right .ehtw-right-search-input {
    display: block !important;
    box-sizing: border-box !important;
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
    height: 44px !important;
    min-height: 44px !important;
    max-height: 44px !important;
    margin: 0 !important;
    padding: 0 12px 0 36px !important;
    border: 1px solid var(--ehtw-border) !important;
    border-radius: 9999px !important;
    background: transparent !important;
    background-image: none !important;
    color: var(--ehtw-text) !important;
    font-size: 15px !important;
    line-height: normal !important;
    box-shadow: none !important;
    outline: none !important;
    appearance: none !important;
}

html.ehtw-enabled #ehtw-right .ehtw-right-search-input:focus {
    border-color: var(--ehtw-accent) !important;
}

html.ehtw-enabled #ehtw-right .ehtw-right-search-input::placeholder {
    color: var(--ehtw-muted) !important;
    opacity: 1 !important;
}

html.ehtw-enabled #ehtw-right .ehtw-right-search-icon {
    position: absolute !important;
    top: 50% !important;
    left: 14px !important;
    z-index: 1 !important;
    width: 16px !important;
    height: 16px !important;
    transform: translateY(-50%) !important;
    color: var(--ehtw-muted) !important;
    pointer-events: none !important;
}

html.ehtw-enabled #ehtw-right .ehtw-right-search
+ .ehtw-side-card {
    margin-top: 8px !important;
}

/* ============================================================
   1.4.0 /popular:底部模式切换工具栏
   ============================================================ */

html.ehtw-list #ehtw-center
.ehtw-timeline > .ehtw-list-bottom-toolbar {
    display: flex !important;
    position: static !important;
    float: none !important;
    flex-wrap: wrap !important;
    align-items: center !important;
    justify-content: flex-start !important;
    gap: 6px !important;
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
    height: auto !important;
    min-height: 40px !important;
    margin: 8px 0 !important;
    padding: 8px 16px !important;
    border: none !important;
    background: transparent !important;
    color: var(--ehtw-muted) !important;
    font-size: 13px !important;
    text-align: left !important;
}

html.ehtw-list #ehtw-center
.ehtw-list-bottom-toolbar select {
    display: inline-block !important;
    width: auto !important;
    height: 32px !important;
    min-height: 32px !important;
    margin: 0 !important;
    padding: 0 10px !important;
    border: 1px solid var(--ehtw-border) !important;
    border-radius: 9999px !important;
    background: transparent !important;
    color: var(--ehtw-text) !important;
    font-size: 13px !important;
}

/* ============================================================
   1.4.0 标签浮动归零,媒体不得环绕标签
   ============================================================ */

html.ehtw-list #ehtw-center .ehtw-tweet .ehtw-tags {
    display: block !important;
    position: static !important;
    float: none !important;
    clear: both !important;
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
    height: auto !important;
    min-height: 20px !important;
    margin: 0 !important;
    padding: 0 !important;
    overflow: visible !important;
    line-height: 20px !important;
}

html.ehtw-list #ehtw-center .ehtw-tweet .ehtw-tags
:is(.gt, .gtl, .gtw) {
    display: inline !important;
    position: static !important;
    float: none !important;
    clear: none !important;
    width: auto !important;
    height: auto !important;
    margin: 0 6px 0 0 !important;
    padding: 0 !important;
    border: none !important;
    border-radius: 0 !important;
    background: transparent !important;
    background-image: none !important;
    color: var(--ehtw-accent) !important;
    font-size: 15px !important;
    font-weight: 400 !important;
    line-height: 20px !important;
    vertical-align: baseline !important;
}

html.ehtw-list #ehtw-center .ehtw-tweet .ehtw-tags
:is(.gt, .gtl, .gtw) a {
    display: inline !important;
    float: none !important;
    color: var(--ehtw-accent) !important;
    font-size: 15px !important;
    line-height: 20px !important;
}

html.ehtw-list #ehtw-center .ehtw-tweet .ehtw-media {
    display: block !important;
    position: relative !important;
    float: none !important;
    clear: both !important;
    margin-top: 12px !important;
    margin-left: 0 !important;
    margin-right: 0 !important;
}

/* ============================================================
   1.4.0 登录页深色变量:覆盖先前浅色固定值
   ============================================================ */

@media (prefers-color-scheme: dark) {
    html.ehtw-login {
        --ehtw-bg: #000000;
        --ehtw-text: #e7e9ea;
        --ehtw-muted: #71767b;
        --ehtw-border: #2f3336;
        --ehtw-accent: #1d9bf0;
        --ehtw-primary-button-bg: #eff3f4;
        --ehtw-primary-button-text: #0f1419;
        color-scheme: dark;
    }

    html.ehtw-login body,
    html.ehtw-login body > .d,
    html.ehtw-login form[name="ipb_login_form"],
    html.ehtw-login form[name="ipb_login_form"] table,
    html.ehtw-login form[name="ipb_login_form"] td {
        background: var(--ehtw-bg) !important;
        background-image: none !important;
        color: var(--ehtw-text) !important;
    }

    html.ehtw-login body > .d {
        border-color: var(--ehtw-border) !important;
    }

    html.ehtw-login label,
    html.ehtw-login form[name="ipb_login_form"] td,
    html.ehtw-login form[name="ipb_login_form"] p {
        color: var(--ehtw-text) !important;
    }

    html.ehtw-login
    :is(input[type="text"], input[type="password"]) {
        background: transparent !important;
        border-color: var(--ehtw-border) !important;
        color: var(--ehtw-text) !important;
    }

    html.ehtw-login
    :is(input[type="text"], input[type="password"]):focus {
        border-color: var(--ehtw-accent) !important;
    }

    html.ehtw-login input::placeholder {
        color: var(--ehtw-muted) !important;
    }
}

/* ============================================================
   1.4.0 Torrents:标题、元数据、左对齐
   ============================================================ */

html.ehtw-generic .ehtw-generic-content
table.itg tr:has(> td.itd) > td:nth-child(2) {
    flex: 0 0 100% !important;
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
    min-height: 20px !important;
    height: auto !important;
    line-height: 20px !important;
    overflow: hidden !important;
    text-overflow: ellipsis !important;
    white-space: nowrap !important;
}

html.ehtw-generic .ehtw-generic-content
table.itg tr:has(> td.itd) > td:nth-child(2) div {
    display: inline !important;
    position: static !important;
    float: none !important;
    width: auto !important;
    height: auto !important;
    min-height: 20px !important;
    max-height: none !important;
    margin: 0 !important;
    padding: 0 !important;
    overflow: visible !important;
    line-height: 20px !important;
}

html.ehtw-generic .ehtw-generic-content
table.itg tr:has(> td.itd) > td:nth-child(2) a {
    display: inline !important;
    height: auto !important;
    min-height: 20px !important;
    max-height: none !important;
    line-height: 20px !important;
    vertical-align: baseline !important;
    color: var(--ehtw-text) !important;
}

html.ehtw-generic .ehtw-generic-content
table.itg tr:has(> td.itd) > td:nth-child(8) {
    margin-left: 0 !important;
}

html.ehtw-generic .ehtw-generic-content #torrentform,
html.ehtw-generic .ehtw-generic-content #torrentform * {
    text-align: left !important;
}

html.ehtw-generic .ehtw-generic-content #torrentform {
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
    margin: 0 !important;
    padding: 12px 16px !important;
}

html.ehtw-generic .ehtw-generic-content
#torrentform p {
    margin: 8px 0 !important;
    padding: 0 !important;
    text-align: left !important;
}

html.ehtw-generic .ehtw-generic-content
#torrentform p a,
html.ehtw-generic .ehtw-generic-content
#torrentform p span {
    display: inline-flex !important;
    align-items: center !important;
    min-height: 28px !important;
    margin: 2px 4px 2px 0 !important;
    padding: 4px 10px !important;
    border: 1px solid var(--ehtw-border) !important;
    border-radius: 9999px !important;
    background: transparent !important;
    color: var(--ehtw-text) !important;
    font-size: 13px !important;
    line-height: 18px !important;
    text-decoration: none !important;
}

html.ehtw-generic .ehtw-generic-content
#torrentform p span {
    font-weight: 700 !important;
}

html.ehtw-generic .ehtw-generic-content
#torrentform input[type="button"]:not([value="Clear"]),
html.ehtw-generic .ehtw-generic-content
#torrentform input[type="submit"] {
    display: inline-flex !important;
    align-items: center !important;
    justify-content: center !important;
    height: 36px !important;
    min-height: 36px !important;
    padding: 0 16px !important;
    border: 0 !important;
    border-radius: 9999px !important;
    background: var(--ehtw-primary-button-bg) !important;
    color: var(--ehtw-primary-button-text) !important;
    font-size: 15px !important;
    font-weight: 700 !important;
}

html.ehtw-generic .ehtw-generic-content
#torrentform input[type="button"][value="Clear"] {
    border: 1px solid var(--ehtw-secondary-button-border) !important;
    background: transparent !important;
    color: var(--ehtw-text) !important;
}

html.ehtw-generic .ehtw-generic-content
#torrentform input::placeholder {
    color: var(--ehtw-muted) !important;
    opacity: 1 !important;
}

html.ehtw-generic .ehtw-generic-content p.ip {
    margin: 8px 0 !important;
    padding: 0 16px !important;
    text-align: left !important;
    color: var(--ehtw-muted) !important;
    font-size: 13px !important;
    line-height: 18px !important;
}

html.ehtw-generic .ehtw-generic-content
table.itg tr:has(> th) th a {
    color: var(--ehtw-muted) !important;
    font-size: 13px !important;
    font-weight: 400 !important;
    text-decoration: none !important;
}

html.ehtw-generic .ehtw-generic-content
table.itg tr:has(> th) th a:hover,
html.ehtw-generic .ehtw-generic-content
table.itg tr:has(> th) th a[aria-current],
html.ehtw-generic .ehtw-generic-content
table.itg tr:has(> th) th a[style*="font-weight: bold"],
html.ehtw-generic .ehtw-generic-content
table.itg tr:has(> th) th a[style*="font-weight:bold"] {
    color: var(--ehtw-text) !important;
}

/* ============================================================
   1.4.0 Toplists:600px 中栏单列卡片
   ============================================================ */

html.ehtw-generic .ehtw-generic-content
.dc:has(.tdo) {
    display: grid !important;
    grid-template-columns: minmax(0, 1fr) !important;
    align-items: start !important;
    gap: 16px !important;
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
    padding: 16px !important;
}

html.ehtw-generic .ehtw-generic-content
.dc:has(.tdo) > h2 {
    grid-column: 1 !important;
    margin: 0 !important;
    padding: 0 0 12px !important;
    color: var(--ehtw-text) !important;
    font-size: 20px !important;
    font-weight: 800 !important;
    line-height: 24px !important;
}

html.ehtw-generic .ehtw-generic-content .tdo {
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
    margin: 0 !important;
    padding: 0 0 12px !important;
    border: 1px solid var(--ehtw-border) !important;
    border-radius: 16px !important;
    background: transparent !important;
}

html.ehtw-generic .ehtw-generic-content
.tdo > p {
    margin: 0 !important;
    padding: 12px 16px !important;
    color: var(--ehtw-text) !important;
    font-size: 15px !important;
    font-weight: 700 !important;
    line-height: 20px !important;
}

html.ehtw-generic .ehtw-generic-content
.tdo > p a {
    color: var(--ehtw-text) !important;
    font-size: 15px !important;
    font-weight: 700 !important;
    text-decoration: none !important;
}

html.ehtw-generic .ehtw-generic-content
.tdo > p a:hover {
    text-decoration: underline !important;
}

html.ehtw-generic .ehtw-generic-content
.tdo table.ttl tr {
    display: flex !important;
    align-items: center !important;
    gap: 8px !important;
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
    margin: 0 !important;
    padding: 4px 16px !important;
}

html.ehtw-generic .ehtw-generic-content
.tdo table.ttl td.pso {
    display: block !important;
    flex: 0 0 36px !important;
    width: 36px !important;
    min-width: 36px !important;
    max-width: 36px !important;
    margin: 0 !important;
    padding: 0 8px 0 0 !important;
    white-space: nowrap !important;
    overflow: visible !important;
    text-align: left !important;
    color: var(--ehtw-muted) !important;
    font-size: 13px !important;
}

html.ehtw-generic .ehtw-generic-content
.tdo table.ttl tr > td:nth-child(2) {
    display: block !important;
    flex: 1 1 auto !important;
    width: auto !important;
    min-width: 0 !important;
    max-width: 100% !important;
    margin: 0 !important;
    padding: 0 !important;
    overflow: hidden !important;
}

html.ehtw-generic .ehtw-generic-content
.tdo table.ttl .tun {
    display: block !important;
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
    margin: 0 !important;
    padding: 0 !important;
    overflow: hidden !important;
    white-space: nowrap !important;
    text-overflow: ellipsis !important;
}

html.ehtw-generic .ehtw-generic-content
.tdo table.ttl a {
    color: var(--ehtw-text) !important;
    font-weight: 400 !important;
    text-decoration: none !important;
}

html.ehtw-generic .ehtw-generic-content
.tdo table.ttl a:hover {
    text-decoration: underline !important;
}

html.ehtw-generic .ehtw-gallery-toplists .tdo img.ehtw-gallery-toplist-cover {
    display: block !important;
    flex: 0 0 44px !important;
    width: 44px !important;
    height: 60px !important;
    max-width: 44px !important;
    object-fit: cover !important;
    border-radius: 4px !important;
    background: var(--ehtw-surface) !important;
}

html.ehtw-generic .ehtw-gallery-toplists .tdo .tun {
    display: block !important;
    width: 100% !important;
    height: auto !important;
    min-height: 60px !important;
    overflow: visible !important;
    white-space: normal !important;
    text-overflow: clip !important;
}

html.ehtw-generic .ehtw-gallery-toplists .tdo .tun > a {
    display: grid !important;
    grid-template-columns: 44px minmax(0, 1fr) !important;
    align-items: center !important;
    gap: 10px !important;
    min-width: 0 !important;
    width: 100% !important;
    min-height: 60px !important;
    white-space: normal !important;
    overflow: visible !important;
    line-height: 18px !important;
    box-sizing: border-box !important;
}

html.ehtw-generic .ehtw-gallery-toplists .tdo tr.ehtw-gallery-toplist-hidden {
    display: none !important;
}

html.ehtw-generic .ehtw-gallery-toplists .tdo table tr {
    display: flex !important;
    align-items: center !important;
    gap: 8px !important;
    width: 100% !important;
    min-height: 60px !important;
    padding: 4px 16px !important;
}

html.ehtw-generic .ehtw-gallery-toplists .tdo table tr > td {
    display: block !important;
    min-width: 0 !important;
    margin: 0 !important;
    padding: 0 !important;
    border: 0 !important;
}

html.ehtw-generic .ehtw-gallery-toplists .tdo table tr > td.pso {
    flex: 0 0 36px !important;
    width: 36px !important;
    color: var(--ehtw-muted) !important;
    font-size: 13px !important;
    text-align: left !important;
}

html.ehtw-generic .ehtw-gallery-toplists .tdo table tr > td:not(.pso) {
    flex: 1 1 auto !important;
    width: auto !important;
}

html.ehtw-generic #ehtw-center .ehtw-gallery-toplists .tdo .pso {
    display: block !important;
    flex: 0 0 36px !important;
    width: 36px !important;
    min-width: 36px !important;
    margin: 0 !important;
    padding: 0 8px 0 0 !important;
    white-space: nowrap !important;
    text-align: left !important;
}

html.ehtw-other .ehtw-other-content .gl3c.ehtw-hidden-tag-gallery {
    display: none !important;
}

/* 列表内容使用主文字色,强调色保留给操作入口和标签。 */

html.ehtw-generic .ehtw-generic-content
:is(.tun, .nwo, .nwi, .tdo, table.itg) a:not(.ehtw-hashtag) {
    color: var(--ehtw-text) !important;
}

html.ehtw-generic .ehtw-generic-content
:is(.tun, .nwo, .nwi, .tdo, table.itg) a:not(.ehtw-hashtag):hover {
    text-decoration: underline !important;
}

/* ============================================================
   1.4.0 About / Gallery actions:标题紧接内容
   ============================================================ */

html.ehtw-detail #ehtw-right
:is(.ehtw-about-card, .ehtw-operation-card)
.ehtw-side-title {
    margin: 0 !important;
    padding: 12px 16px !important;
    border: 0 !important;
    font-size: 20px !important;
    font-weight: 800 !important;
    line-height: 24px !important;
}

html.ehtw-detail #ehtw-right
.ehtw-about-card #gdd,
html.ehtw-detail #ehtw-right
.ehtw-operation-card #gd5 {
    margin: 0 !important;
    padding: 0 16px 12px !important;
    border: 0 !important;
    background: transparent !important;
}

html.ehtw-detail #ehtw-right
.ehtw-about-card #gdd > :first-child,
html.ehtw-detail #ehtw-right
.ehtw-operation-card #gd5 > :first-child {
    margin-top: 0 !important;
}

html.ehtw-detail #ehtw-right
.ehtw-about-card #gdd tr:first-child td {
    padding-top: 0 !important;
}

/* ============================================================
   1.4.0 Watched 空状态
   ============================================================ */

html.ehtw-generic .ehtw-generic-empty,
html.ehtw-generic .ehtw-generic-empty
:is(p, em, i, span) {
    font-style: normal !important;
}

html.ehtw-generic .ehtw-generic-empty a {
    color: var(--ehtw-accent) !important;
    font-style: normal !important;
    text-decoration: none !important;
}

html.ehtw-generic .ehtw-generic-empty a:hover {
    text-decoration: underline !important;
}

/* ============================================================
   1.4.0 手机端新增搜索表单适配
   ============================================================ */

@media (max-width: 999px) {
    html.ehtw-enabled #ehtw-right
    .ehtw-right-search {
        display: none !important;
    }
}

@media (max-width: 699px) {
    html.ehtw-generic .ehtw-generic-content
    .dc:has(.tdo) {
        grid-template-columns: minmax(0, 1fr) !important;
        padding: 12px !important;
    }

    html.ehtw-generic .ehtw-generic-content
    .tdo table.ttl tr {
        padding: 4px 12px !important;
    }

    html.ehtw-list .ehtw-tweet .ehtw-media {
        clear: both !important;
        margin-left: 0 !important;
        max-width: 100% !important;
    }
}

/* 1.4.2 收尾:标签排版、Extended、缩略图标题、Toplists 宽度、占位文字。 */
html.ehtw-list #ehtw-center .ehtw-tweet .ehtw-tags {
    display: block !important;
    max-width: 100% !important;
    overflow: hidden !important;
    white-space: normal !important;
    text-align: left !important;
    overflow-wrap: normal !important;
    word-break: normal !important;
}

html.ehtw-list #ehtw-center .ehtw-tweet .ehtw-tags > .gt,
html.ehtw-list #ehtw-center .ehtw-tweet .ehtw-tags > .ehtw-hashtag {
    display: inline-block !important;
    max-width: 100% !important;
    overflow: hidden !important;
    text-overflow: ellipsis !important;
    white-space: nowrap !important;
    vertical-align: top !important;
    margin: 0 6px 0 0 !important;
}

html.ehtw-list #ehtw-center .ehtw-tweet .ehtw-tags > .gt:nth-child(n+7),
html.ehtw-list #ehtw-center .ehtw-tweet .ehtw-tags > .ehtw-hashtag:nth-child(n+7) {
    display: none !important;
}

html.ehtw-other #ehtw-center table.itg.glte > tbody > tr > td,
html.ehtw-other #ehtw-center table.itg.glte > tbody > tr > td > div,
html.ehtw-other #ehtw-center table.itg.glte .gl4e {
    border: 0 !important;
}

html.ehtw-other #ehtw-center table.itg.glte {
    border: 0 !important;
}

html.ehtw-other #ehtw-center table.itg.glte > tbody > tr {
    border-bottom: 1px solid var(--ehtw-border) !important;
}

html.ehtw-other #ehtw-center table.itg.glte .gl4e table,
html.ehtw-other #ehtw-center table.itg.glte .gl4e tbody,
html.ehtw-other #ehtw-center table.itg.glte .gl4e tr,
html.ehtw-other #ehtw-center table.itg.glte .gl4e td {
    display: inline !important;
    width: auto !important;
    margin: 0 !important;
    padding: 0 !important;
    border: 0 !important;
    background: none !important;
}

html.ehtw-other #ehtw-center table.itg.glte .gl4e td.tc {
    display: none !important;
}

html.ehtw-other #ehtw-center table.itg.glte .gl4e .gt,
html.ehtw-other #ehtw-center table.itg.glte .gl4e .gtl,
html.ehtw-other #ehtw-center table.itg.glte .gl4e .gtw {
    display: inline !important;
    float: none !important;
    margin: 0 6px 0 0 !important;
    padding: 0 !important;
    border: 0 !important;
    background: none !important;
    color: var(--ehtw-accent) !important;
    font-weight: 400 !important;
    white-space: nowrap !important;
}

html.ehtw-other #ehtw-center table.itg.glte td.gl1e > div {
    overflow: hidden !important;
    border: 1px solid var(--ehtw-border) !important;
    border-radius: 16px !important;
}

html.ehtw-other #ehtw-center .itg.gld .gl4t {
    height: auto !important;
    max-height: 40px !important;
    line-height: 20px !important;
    overflow: hidden !important;
    margin-bottom: 8px !important;
}

html.ehtw-other #ehtw-center .itg.gld .gl1t {
    height: auto !important;
}

html.ehtw-generic table.ttl {
    display: block !important;
    width: 100% !important;
}

html.ehtw-generic table.ttl tbody {
    display: block !important;
}

html.ehtw-generic table.ttl tr {
    width: 100% !important;
}

html.ehtw-generic table.ttl .tun,
html.ehtw-generic table.ttl .tun a {
    width: auto !important;
    max-width: 100% !important;
}

html.ehtw-enabled input::placeholder,
html.ehtw-enabled textarea::placeholder {
    color: var(--ehtw-muted) !important;
    -webkit-text-fill-color: var(--ehtw-muted) !important;
    opacity: 1 !important;
}

/* 1.5.0:评论数和收藏数;评分移到推文头部;详情页爱心是收藏数。 */
html.ehtw-list .ehtw-tweet-rating {
    flex: none;
    color: var(--ehtw-muted);
    font-size: 15px;
    line-height: 20px;
    white-space: nowrap;
}

html.ehtw-detail .ehtw-detail-favorite-action {
    display: inline-flex !important;
    align-items: center !important;
    gap: 6px !important;
}

html.ehtw-detail .ehtw-detail-favorite-count {
    color: var(--ehtw-muted);
    font-size: 13px;
    line-height: 16px;
}

html.ehtw-detail .ehtw-detail-favorite-action:hover .ehtw-detail-favorite-count {
    color: #f91880;
}
html.ehtw-detail .ehtw-detail-actions {
    display: flex !important;
    align-items: center !important;
    justify-content: flex-start !important;
    gap: 32px !important;
    padding: 8px 0 !important;
    text-align: left !important;
}

html.ehtw-detail .ehtw-detail-actions .ehtw-detail-reply-count {
    display: inline-flex !important;
    align-items: center !important;
    gap: 6px !important;
    order: 1;
    color: var(--ehtw-muted) !important;
    font-size: 13px;
    text-decoration: none !important;
}

html.ehtw-detail .ehtw-detail-actions .ehtw-detail-favorite-action {
    order: 2;
}

html.ehtw-detail .ehtw-detail-actions .ehtw-detail-rating-action {
    order: 3;
    margin-left: auto !important;
}

html.ehtw-detail .ehtw-detail-actions #gdr,
html.ehtw-detail .ehtw-detail-actions #gdr table {
    margin: 0 !important;
}
/* 1.5.1:手机 Safari。底栏避开安全区,内容不被底栏遮住,不自动放大文字。 */
html.ehtw-enabled {
    -webkit-text-size-adjust: 100%;
    text-size-adjust: 100%;
}

@media (max-width: 699px) {
    html.ehtw-enabled #ehtw-left {
        padding-bottom: env(safe-area-inset-bottom, 0px) !important;
        height: auto !important;
        min-height: 0 !important;
    }

    html.ehtw-enabled #ehtw-center {
        padding-bottom: calc(72px + env(safe-area-inset-bottom, 0px)) !important;
    }
    html.ehtw-other #ehtw-center .ehtw-other-content .itg.gld,
    html.ehtw-other #ehtw-center .itg.gld {
        grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
    }

    html.ehtw-other #ehtw-center .itg.gld .gl3t {
        width: 100% !important;
        height: auto !important;
    }

    html.ehtw-other #ehtw-center .itg.gld .gl3t img {
        width: 100% !important;
        height: auto !important;
    }

    html.ehtw-other #ehtw-center .itg.gld .gl5t > div {
        gap: 2px 4px !important;
    }
}
/* 1.6.0:Thumbnail 模式也是推文。分类用 .cs,加载完成前名字先显示分类。 */
html.ehtw-list .ehtw-tweet-author .cs {
    display: inline !important;
    flex: 0 1 auto !important;
    width: auto !important;
    min-width: 0 !important;
    height: auto !important;
    margin: 0 !important;
    padding: 0 !important;
    border: none !important;
    border-radius: 0 !important;
    background: transparent !important;
    background-image: none !important;
    box-shadow: none !important;
    color: var(--ehtw-muted) !important;
    font-size: 15px !important;
    font-weight: 400 !important;
    line-height: 20px !important;
    text-shadow: none !important;
    white-space: nowrap;
}

html.ehtw-list .ehtw-tweet-pending .ehtw-handle,
html.ehtw-list .ehtw-tweet-pending .ehtw-category-dot,
html.ehtw-list .ehtw-tweet-pending .ehtw-tweet-author .cs {
    display: none !important;
}

html.ehtw-list .ehtw-thumb-tweet .ehtw-tags:empty {
    display: none !important;
}

html.ehtw-list .ehtw-thumb-tweet .ehtw-tags a {
    text-decoration: none !important;
}

html.ehtw-list .ehtw-thumb-tweet .ehtw-tweet-title {
    text-align: left !important;
}

html.ehtw-list #ehtw-center .ehtw-thumb-tweet .ehtw-tweet-title .gl4t {
    display: block !important;
    width: auto !important;
    height: auto !important;
    max-height: none !important;
    margin: 0 !important;
    padding: 0 !important;
    overflow: visible !important;
    visibility: visible !important;
    text-align: left !important;
    line-height: 20px !important;
}

/* 头部放不下时先压缩 @handle,分类和评分保持完整。 */
html.ehtw-list #ehtw-center .ehtw-tweet-author .ehtw-author-name {
    flex: 0 1 auto !important;
    min-width: 0 !important;
    max-width: 45% !important;
    overflow: hidden !important;
    text-overflow: ellipsis !important;
    white-space: nowrap !important;
}

html.ehtw-list #ehtw-center .ehtw-tweet-author .ehtw-handle {
    flex: 0 100 auto !important;
    min-width: 0 !important;
    overflow: hidden !important;
    text-overflow: ellipsis !important;
    white-space: nowrap !important;
}

html.ehtw-list #ehtw-center .ehtw-tweet-author .cn,
html.ehtw-list #ehtw-center .ehtw-tweet-author .cs,
html.ehtw-list #ehtw-center .ehtw-tweet-author .ehtw-dot,
html.ehtw-list #ehtw-center .ehtw-tweet-author .ehtw-time,
html.ehtw-list #ehtw-center .ehtw-tweet-author .ehtw-tweet-rating {
    flex: none !important;
    max-width: none !important;
    overflow: visible !important;
}
/* 1.6.1:非推文显示模式也显示评论数和收藏数。 */
html.ehtw-other .ehtw-row-stats {
    display: inline-flex !important;
    position: static !important;
    align-items: center;
    gap: 10px;
    width: auto !important;
    height: auto !important;
    margin: 0 !important;
    color: var(--ehtw-muted);
    font-size: 13px;
    line-height: 16px;
    white-space: nowrap;
}

html.ehtw-other .ehtw-row-stat {
    display: inline-flex;
    align-items: center;
    gap: 3px;
}

html.ehtw-other .ehtw-row-stat svg {
    width: 14px;
    height: 14px;
    flex: none;
}

html.ehtw-other #ehtw-center table.itg .gl4m {
    width: 84px !important;
    min-width: 84px !important;
    max-width: 84px !important;
    white-space: normal !important;
}

html.ehtw-other #ehtw-center table.itg .gl4m .ehtw-row-stats {
    display: flex !important;
    margin-top: 2px !important;
    gap: 8px;
}

html.ehtw-other #ehtw-center table.itg .gl2m {
    width: 1% !important;
    white-space: nowrap !important;
}
/* 1.6.2:回复和推文同一规格。 */
html.ehtw-detail .ehtw-comments-section > .ehtw-section-title {
    display: none !important;
}

html.ehtw-detail .ehtw-reply-box {
    display: grid !important;
    grid-template-columns: 40px minmax(0, 1fr) auto;
    align-items: center;
    column-gap: 8px;
    padding: 12px 16px !important;
    border-bottom: 1px solid var(--ehtw-border) !important;
    scroll-margin-top: 60px;
}

html.ehtw-detail .ehtw-reply-avatar {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    border-radius: 50%;
    background: var(--ehtw-avatar, #536471);
    color: #fff;
    font-size: 15px;
    font-weight: 700;
}

html.ehtw-detail .ehtw-reply-placeholder {
    justify-self: start;
    text-align: left;
    color: var(--ehtw-muted);
    font-size: 20px;
    line-height: 24px;
    padding-left: 4px;
}

html.ehtw-detail #ehtw-center .ehtw-comments-section #postnewcomment {
    display: block;
    margin: 0 !important;
    padding: 0 !important;
    border: 0 !important;
    background: none !important;
    color: transparent !important;
    font-size: 0 !important;
    line-height: 0 !important;
    text-align: right !important;
}

html.ehtw-detail #ehtw-center .ehtw-comments-section #postnewcomment a {
    display: inline-flex !important;
    align-items: center;
    justify-content: center;
    height: 36px !important;
    min-width: 0 !important;
    margin: 0 !important;
    padding: 0 16px !important;
    border: 0 !important;
    border-radius: 9999px !important;
    background: var(--ehtw-text) !important;
    color: transparent !important;
    font-size: 0 !important;
    line-height: 20px !important;
    text-decoration: none !important;
}

html.ehtw-detail #ehtw-center .ehtw-comments-section #postnewcomment a::before,
html.ehtw-detail #ehtw-center .ehtw-comments-section #postnewcomment a::after {
    content: none !important;
}

html.ehtw-detail #ehtw-center .ehtw-comments-section #postnewcomment a::after {
    content: 'Reply' !important;
    position: static !important;
    color: var(--ehtw-bg) !important;
    font-size: 15px !important;
    font-weight: 700 !important;
}

html.ehtw-detail .ehtw-comments-section #formdiv {
    padding: 12px 16px !important;
    border-bottom: 1px solid var(--ehtw-border) !important;
    max-width: 100% !important;
}

html.ehtw-detail #ehtw-center .ehtw-comments-section #cdiv .c1.ehtw-comment-tweet {
    display: grid !important;
    grid-template-columns: 40px minmax(0, 1fr) !important;
    column-gap: 8px !important;
    row-gap: 0 !important;
    width: auto !important;
    margin: 0 !important;
    padding: 12px 16px !important;
    border: 0 !important;
    border-bottom: 1px solid var(--ehtw-border) !important;
    border-radius: 0 !important;
    background: transparent !important;
}

html.ehtw-detail #ehtw-center .ehtw-comments-section #cdiv .c1.ehtw-comment-tweet::before {
    content: none !important;
    display: none !important;
}

html.ehtw-detail #ehtw-center .ehtw-comments-section #cdiv .c1.ehtw-comment-tweet:hover {
    background: var(--ehtw-hover) !important;
}

html.ehtw-detail #ehtw-center #cdiv .ehtw-comment-avatar {
    grid-column: 1 !important;
    grid-row: 1 / span 6 !important;
    align-self: start !important;
    width: 40px !important;
    height: 40px !important;
    margin: 0 !important;
}

html.ehtw-detail #ehtw-center #cdiv .ehtw-comment-heading {
    grid-column: 2 !important;
    display: flex !important;
    align-items: center !important;
    gap: 4px !important;
    width: auto !important;
    min-width: 0 !important;
    height: 20px !important;
    margin: 0 !important;
    padding: 0 !important;
    line-height: 20px !important;
    white-space: nowrap;
}

html.ehtw-detail #ehtw-center #cdiv .ehtw-comment-author,
html.ehtw-detail #ehtw-center #cdiv .ehtw-comment-author a {
    flex: 0 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    color: var(--ehtw-text) !important;
    font-size: 15px !important;
    font-weight: 700 !important;
    text-decoration: none !important;
}

html.ehtw-detail #ehtw-center #cdiv .ehtw-comment-author a:hover {
    text-decoration: underline !important;
}

html.ehtw-detail #ehtw-center #cdiv .ehtw-comment-handle {
    flex: 0 100 auto !important;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
}

html.ehtw-detail #ehtw-center #cdiv .ehtw-comment-handle,
html.ehtw-detail #ehtw-center #cdiv .ehtw-comment-time,
html.ehtw-detail #ehtw-center #cdiv .ehtw-comment-heading .ehtw-dot {
    color: var(--ehtw-muted) !important;
    font-size: 15px !important;
    font-weight: 400 !important;
}

html.ehtw-detail #ehtw-center #cdiv .ehtw-comment-time,
html.ehtw-detail #ehtw-center #cdiv .ehtw-comment-heading .ehtw-dot {
    flex: none;
}

html.ehtw-detail #ehtw-center #cdiv .ehtw-comment-heading .ehtw-comment-badge {
    position: static !important;
    flex: none !important;
    float: none !important;
    width: auto !important;
    min-width: 0 !important;
    height: 18px !important;
    margin: 0 0 0 4px !important;
    padding: 0 6px !important;
    overflow: visible !important;
    border: 1px solid var(--ehtw-border) !important;
    border-radius: 4px !important;
    background: none !important;
    color: var(--ehtw-muted) !important;
    font-size: 12px !important;
    font-weight: 400 !important;
    line-height: 16px !important;
    text-indent: 0 !important;
}

html.ehtw-detail #ehtw-center #cdiv .ehtw-comment-more {
    display: inline-flex;
    flex: none;
    align-items: center;
    margin-left: auto;
    color: var(--ehtw-muted) !important;
}

html.ehtw-detail #ehtw-center #cdiv .ehtw-comment-more svg {
    width: 19px;
    height: 19px;
}

html.ehtw-detail #ehtw-center #cdiv .c1 .c2 {
    display: none !important;
}

html.ehtw-detail #ehtw-center #cdiv .c1 .c6 {
    grid-column: 2 !important;
    margin: 2px 0 0 !important;
    padding: 0 !important;
    border: 0 !important;
    color: var(--ehtw-text) !important;
    font-size: 15px !important;
    line-height: 20px !important;
    overflow-wrap: anywhere;
}

html.ehtw-detail #ehtw-center #cdiv .c1 .c6 a {
    color: var(--ehtw-accent) !important;
    text-decoration: none !important;
}

html.ehtw-detail #ehtw-center #cdiv .c1 .c7 {
    grid-column: 2 !important;
    margin: 4px 0 0 !important;
    padding: 0 !important;
    border: 0 !important;
    background: none !important;
    color: var(--ehtw-muted) !important;
    font-size: 13px !important;
    line-height: 16px !important;
}

html.ehtw-detail #ehtw-center #cdiv .ehtw-comment-actions {
    grid-column: 2 !important;
    display: flex;
    align-items: center;
    gap: 96px;
    height: 20px;
    margin-top: 12px;
}

html.ehtw-detail #ehtw-center #cdiv .ehtw-comment-action {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    color: var(--ehtw-muted) !important;
    font-size: 13px;
    line-height: 16px;
    text-decoration: none !important;
}

html.ehtw-detail #ehtw-center #cdiv .ehtw-comment-action svg {
    width: 18.75px;
    height: 18.75px;
    flex: none;
}

html.ehtw-detail #ehtw-center #cdiv a.ehtw-comment-action:hover {
    color: var(--ehtw-accent) !important;
}

html.ehtw-detail #ehtw-center #cdiv .ehtw-comment-like:hover {
    color: #f91880 !important;
}

html.ehtw-detail #ehtw-center #cdiv .ehtw-comment-share {
    margin-left: auto;
}

html.ehtw-detail #ehtw-center #cdiv .ehtw-comment-like .c5 {
    position: static !important;
    float: none !important;
    width: auto !important;
    margin: 0 !important;
    padding: 0 !important;
    color: inherit !important;
    font-size: 0 !important;
    font-weight: 400 !important;
    cursor: pointer;
}

html.ehtw-detail #ehtw-center #cdiv .ehtw-comment-like .c5 span {
    color: inherit !important;
    font-size: 13px !important;
    line-height: 16px;
    opacity: 1 !important;
}

html.ehtw-detail #ehtw-center #cdiv .ehtw-comment-votes {
    position: static !important;
    float: none !important;
    width: auto !important;
    height: auto !important;
    margin: 0 !important;
    padding: 0 !important;
    color: var(--ehtw-muted) !important;
    font-size: 13px !important;
    font-weight: 400 !important;
}

html.ehtw-detail #ehtw-center #cdiv .ehtw-comment-votes a {
    color: var(--ehtw-accent) !important;
    text-decoration: none !important;
}

@media (max-width: 699px) {
    html.ehtw-detail #ehtw-center #cdiv .ehtw-comment-actions {
        gap: 56px;
    }

    html.ehtw-detail #ehtw-center #cdiv .ehtw-comment-heading .ehtw-comment-badge {
        display: none !important;
    }
}
/* 1.6.3:操作栏只留有动作或有数据的项;标签可点击。 */
html.ehtw-list .ehtw-twitter-actions {
    grid-template-columns: repeat(4, minmax(0, 1fr)) !important;
}

html.ehtw-list .ehtw-twitter-actions div.ehtw-twitter-action-views,
html.ehtw-list .ehtw-twitter-actions div.ehtw-twitter-action-views:hover {
    cursor: default !important;
    color: var(--ehtw-muted) !important;
}

html.ehtw-list .ehtw-twitter-actions div.ehtw-twitter-action-views:hover svg,
html.ehtw-list .ehtw-twitter-actions div.ehtw-twitter-action-views:hover .ehtw-action-number {
    color: var(--ehtw-muted) !important;
}

html.ehtw-list .ehtw-twitter-actions div.ehtw-twitter-action-views:hover .ehtw-twitter-action-icon,
html.ehtw-list .ehtw-twitter-actions div.ehtw-twitter-action-views .ehtw-twitter-action-icon::before,
html.ehtw-list .ehtw-twitter-actions div.ehtw-twitter-action-views .ehtw-twitter-action-icon::after {
    background: none !important;
}

html.ehtw-list .ehtw-twitter-actions .ehtw-repost-unavailable {
    visibility: hidden !important;
}

html.ehtw-list #ehtw-center .ehtw-tweet .ehtw-tags > .ehtw-tag-link {
    display: inline-block !important;
    max-width: 100% !important;
    overflow: hidden !important;
    text-overflow: ellipsis !important;
    white-space: nowrap !important;
    vertical-align: top !important;
    margin: 0 6px 0 0 !important;
    color: var(--ehtw-accent) !important;
    text-decoration: none !important;
}

html.ehtw-list #ehtw-center .ehtw-tweet .ehtw-tags > .ehtw-tag-link:hover {
    text-decoration: underline !important;
}

html.ehtw-list #ehtw-center .ehtw-tweet .ehtw-tags > .ehtw-tag-link > .gt,
html.ehtw-list #ehtw-center .ehtw-tweet .ehtw-tags > .ehtw-tag-link > .gtl,
html.ehtw-list #ehtw-center .ehtw-tweet .ehtw-tags > .ehtw-tag-link > .gtw {
    display: inline !important;
    margin: 0 !important;
    padding: 0 !important;
    color: inherit !important;
}

html.ehtw-list #ehtw-center .ehtw-tweet .ehtw-tags > .ehtw-tag-link:nth-child(n+7) {
    display: none !important;
}

html.ehtw-detail #ehtw-center #cdiv .ehtw-comment-like,
html.ehtw-detail #ehtw-center #cdiv .ehtw-comment-like .c5 {
    cursor: default !important;
}

html.ehtw-detail #ehtw-center #cdiv .ehtw-comment-like:hover {
    color: var(--ehtw-muted) !important;
}

html.ehtw-list .ehtw-twitter-actions .ehtw-repost-available,
html.ehtw-list .ehtw-twitter-actions .ehtw-repost-available .ehtw-twitter-action-icon,
html.ehtw-list .ehtw-twitter-actions .ehtw-repost-available svg,
html.ehtw-list .ehtw-twitter-actions .ehtw-repost-available .gldown {
    cursor: default !important;
}

html.ehtw-list .ehtw-twitter-actions .ehtw-repost-available .gldown a,
html.ehtw-list .ehtw-twitter-actions .ehtw-repost-available .gldown img {
    cursor: pointer !important;
}
/* 1.7.1:详情页图片放进卡片,横向滑动;不再有下方的 Media 区。 */
html.ehtw-detail {
    --ehtw-strip: 240;
}

html.ehtw-detail #ehtw-center .ehtw-gallery-grid-section {
    width: 100% !important;
    margin: 12px 0 0 !important;
    padding: 0 !important;
    border: 0 !important;
}

html.ehtw-detail #ehtw-center .ehtw-detail-section-header {
    display: flex !important;
    align-items: center !important;
    justify-content: space-between !important;
    gap: 12px !important;
    width: 100% !important;
    min-width: 0 !important;
    margin: 0 0 10px !important;
}

html.ehtw-detail #ehtw-center .ehtw-detail-section-title {
    margin: 0 !important;
    color: var(--ehtw-text) !important;
    font-size: 17px !important;
    font-weight: 800 !important;
    line-height: 1.3 !important;
}

html.ehtw-detail #ehtw-center .ehtw-detail-expand-button {
    flex: 0 0 auto !important;
    min-height: 34px !important;
    padding: 6px 12px !important;
    border: 1px solid var(--ehtw-border) !important;
    border-radius: 999px !important;
    background: var(--ehtw-bg) !important;
    color: var(--ehtw-accent) !important;
    font-size: 12px !important;
    cursor: pointer !important;
}

html.ehtw-detail #ehtw-center .ehtw-gallery-grid-section #gdt {
    display: flex !important;
    align-items: stretch;
    gap: 4px;
    width: 100% !important;
    max-width: 100% !important;
    min-width: 0 !important;
    height: calc(var(--ehtw-strip) * 1px) !important;
    margin: 0 !important;
    padding: 0 !important;
    overflow-x: auto !important;
    overflow-y: hidden !important;
    border: 0 !important;
    background: none !important;
    scrollbar-width: thin;
    -webkit-overflow-scrolling: touch;
}

html.ehtw-detail #ehtw-center .ehtw-gallery-grid-section #gdt a {
    position: relative !important;
    display: block !important;
    flex: none !important;
    width: calc(var(--ehtw-strip) * 1px * var(--ehtw-w, 200) / var(--ehtw-h, 280)) !important;
    max-width: none !important;
    height: 100% !important;
    margin: 0 !important;
    padding: 0 !important;
    overflow: hidden !important;
    border: 1px solid var(--ehtw-border) !important;
    border-radius: 16px !important;
    background: var(--ehtw-media) !important;
}

html.ehtw-detail #ehtw-center .ehtw-gallery-grid-section #gdt a > div {
    position: absolute !important;
    top: 0 !important;
    left: 0 !important;
    margin: 0 !important;
    transform: scale(calc(var(--ehtw-strip) / var(--ehtw-h, 280)));
    transform-origin: 0 0;
}

html.ehtw-detail #ehtw-center .ehtw-gallery-grid-section #gdt img {
    width: 100% !important;
    height: 100% !important;
    object-fit: cover !important;
}

html.ehtw-detail #ehtw-center .ehtw-gallery-grid-section.ehtw-gallery-grid-expanded #gdt {
    --ehtw-strip: 150;
    height: auto !important;
    min-height: 0 !important;
    flex-wrap: wrap !important;
    align-content: flex-start !important;
    overflow-x: hidden !important;
    overflow-y: visible !important;
    padding-bottom: 4px !important;
}

html.ehtw-detail #ehtw-center .ehtw-gallery-grid-section.ehtw-gallery-grid-expanded #gdt a {
    height: 150px !important;
    margin-bottom: 8px !important;
}

html.ehtw-detail #ehtw-center .ehtw-gallery-grid-section .ehtw-gallery-continuation {
    display: flex !important;
    flex: 0 0 112px !important;
    align-items: center !important;
    justify-content: center !important;
    width: 112px !important;
    height: 100% !important;
    color: var(--ehtw-muted) !important;
    font-size: 12px !important;
    text-align: center !important;
    white-space: normal !important;
}

html.ehtw-detail #ehtw-center .ehtw-gallery-grid-section .ehtw-gallery-continuation button {
    padding: 8px 10px !important;
    border: 1px solid var(--ehtw-border) !important;
    border-radius: 999px !important;
    background: var(--ehtw-bg) !important;
    color: var(--ehtw-text) !important;
    cursor: pointer !important;
}

html.ehtw-detail #ehtw-center .ehtw-gallery-grid-section.ehtw-gallery-grid-expanded .ehtw-gallery-continuation {
    flex: 1 0 100% !important;
    width: 100% !important;
    height: 48px !important;
    display: none !important;
}

html.ehtw-detail #ehtw-center .ehtw-detail-tags-collapsed #taglist {
    display: block !important;
    max-height: 112px !important;
    overflow: hidden !important;
}

html.ehtw-detail #ehtw-center .ehtw-detail-tags-expanded #taglist {
    max-height: none !important;
    overflow: visible !important;
}

html.ehtw-detail #ehtw-center .ehtw-gallery-grid-section .ehtw-gallery-pagination {
    margin: 8px 0 0 !important;
    padding: 0 !important;
    border: 0 !important;
    display: none !important;
}

html.ehtw-detail #ehtw-center .ehtw-gallery-grid-section.ehtw-gallery-grid-expanded .ehtw-gallery-pagination:not(.ehtw-gallery-pagination-secondary) {
    display: block !important;
}

html.ehtw-detail #ehtw-center .ehtw-gallery-grid-section .ehtw-gallery-pagination-secondary {
    display: none !important;
}

@media (max-width: 699px) {
    html.ehtw-detail {
        --ehtw-strip: 200;
    }

    html.ehtw-detail #ehtw-center .ehtw-detail-expand-button {
        min-height: 44px !important;
        padding: 8px 14px !important;
        touch-action: manipulation !important;
        pointer-events: auto !important;
    }

    html.ehtw-detail #ehtw-center .ehtw-gallery-grid-section #gdt {
        scrollbar-width: none;
    }

    html.ehtw-detail #ehtw-center .ehtw-gallery-grid-section.ehtw-gallery-grid-expanded #gdt {
        --ehtw-strip: 120;
    }

    html.ehtw-detail #ehtw-center .ehtw-gallery-grid-section.ehtw-gallery-grid-expanded #gdt a {
        height: 120px !important;
    }

    html.ehtw-detail #ehtw-center .ehtw-gallery-grid-section #gdt::-webkit-scrollbar {
        display: none;
    }
}
/* 1.7.2:详情页窄屏。新标签表单撑开容器,操作区换行,分页不折行。 */
html.ehtw-detail #ehtw-center #gd4 {
    display: flow-root !important;
    height: auto !important;
}

html.ehtw-detail #ehtw-center #tagmenu_new,
html.ehtw-detail #ehtw-center #tagmenu_act {
    float: none !important;
    width: auto !important;
    height: auto !important;
    margin: 8px 0 0 !important;
    padding: 0 !important;
}

html.ehtw-detail #ehtw-center #tagmenu_new form {
    display: flex !important;
    align-items: center;
    gap: 8px;
    width: 100%;
}

html.ehtw-detail #ehtw-center #newtagfield {
    flex: 1 1 auto !important;
    width: auto !important;
    min-width: 0 !important;
}

html.ehtw-detail #ehtw-center #newtagbutton {
    flex: none !important;
    margin: 0 !important;
}

html.ehtw-detail #ehtw-center .ehtw-detail-favorite-action #gdf,
html.ehtw-detail #ehtw-center .ehtw-detail-favorite-action #favoritelink {
    white-space: nowrap !important;
}

html.ehtw-detail #ehtw-center .ehtw-gallery-pagination table.ptt,
html.ehtw-detail #ehtw-center .ehtw-gallery-pagination table.ptt tbody {
    display: block !important;
    width: 100% !important;
    max-width: 100% !important;
}

html.ehtw-detail #ehtw-center .ehtw-gallery-pagination table.ptt tr {
    display: flex !important;
    flex-wrap: nowrap !important;
    max-width: 100% !important;
    overflow-x: auto !important;
    scrollbar-width: none;
}

html.ehtw-detail #ehtw-center .ehtw-gallery-pagination table.ptt tr::-webkit-scrollbar {
    display: none;
}

html.ehtw-detail #ehtw-center .ehtw-gallery-pagination table.ptt td {
    flex: none !important;
}

@media (max-width: 699px) {
    html.ehtw-detail #ehtw-center .ehtw-detail-actions {
        flex-wrap: wrap !important;
        gap: 8px 24px !important;
    }

    html.ehtw-detail #ehtw-center .ehtw-detail-actions .ehtw-detail-rating-action {
        flex: 0 0 100% !important;
        order: 3;
        margin-left: 0 !important;
        max-width: 100%;
    }

    html.ehtw-detail #ehtw-center .ehtw-detail-actions #gdr {
        width: auto !important;
        max-width: 100% !important;
        margin: 0 !important;
        text-align: left !important;
    }
}
/* 1.8.0:三个点菜单;评论爱心是投票,卡片爱心是收藏。 */
.ehtw-menu-wrap {
    position: relative;
    display: inline-flex;
    flex: none !important;
    margin-left: auto !important;
}

.ehtw-menu-button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    margin: -4px -6px -4px 0;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: none;
    color: var(--ehtw-muted);
    cursor: pointer;
}

.ehtw-menu-button:hover {
    background: rgba(29, 155, 240, 0.1);
    color: var(--ehtw-accent);
}

.ehtw-menu-button svg {
    width: 19px;
    height: 19px;
}

.ehtw-menu {
    position: fixed;
    z-index: 1000;
    display: none;
    min-width: 220px;
    max-width: min(300px, calc(100vw - 16px));
    padding: 4px 0;
    overflow: hidden;
    border: 1px solid var(--ehtw-border);
    border-radius: 12px;
    background: var(--ehtw-bg);
    box-shadow: 0 0 15px rgba(101, 119, 134, 0.25);
    text-align: left;
    white-space: normal;
}

.ehtw-menu-open > .ehtw-menu {
    display: block;
}

.ehtw-menu-item,
html.ehtw-detail .ehtw-menu #gd5 p a {
    display: block !important;
    width: 100%;
    margin: 0 !important;
    padding: 10px 16px !important;
    border: 0 !important;
    background: none !important;
    color: var(--ehtw-text) !important;
    font-family: inherit;
    font-size: 15px !important;
    font-weight: 700 !important;
    line-height: 20px !important;
    text-align: left;
    text-decoration: none !important;
    cursor: pointer;
    box-sizing: border-box;
}

.ehtw-menu-item:hover,
html.ehtw-detail .ehtw-menu #gd5 p a:hover {
    background: var(--ehtw-hover) !important;
}

html.ehtw-detail .ehtw-menu #gd5,
html.ehtw-detail .ehtw-menu #gd5 p {
    float: none !important;
    width: auto !important;
    height: auto !important;
    margin: 0 !important;
    padding: 0 !important;
    border: 0 !important;
    background: none !important;
    text-align: left !important;
}

html.ehtw-detail .ehtw-menu #gd5 p > img {
    display: none !important;
}

html.ehtw-detail .ehtw-detail-author {
    display: flex !important;
    align-items: center;
}

html.ehtw-detail .ehtw-detail-favorite-action {
    padding: 0 !important;
    border: 0 !important;
    background: none !important;
    color: var(--ehtw-muted) !important;
    cursor: pointer;
}

html.ehtw-detail .ehtw-detail-favorite-action svg {
    width: 18.75px;
    height: 18.75px;
}

html.ehtw-detail .ehtw-detail-favorite-action #gdf {
    display: none !important;
}

html.ehtw-detail .ehtw-detail-favorite-action:hover,
html.ehtw-detail .ehtw-detail-favorite-action:hover .ehtw-detail-favorite-count {
    color: #f91880 !important;
}

html.ehtw-detail #ehtw-center #cdiv button.ehtw-comment-like {
    padding: 0;
    border: 0;
    background: none;
    font: inherit;
    cursor: pointer;
}

html.ehtw-detail #ehtw-center #cdiv button.ehtw-comment-like .c5 {
    cursor: pointer;
}

html.ehtw-detail #ehtw-center #cdiv button.ehtw-comment-like:hover {
    color: #f91880 !important;
}

.ehtw-liked,
html.ehtw-detail #ehtw-center #cdiv .ehtw-comment-like.ehtw-liked,
html.ehtw-detail .ehtw-detail-favorite-action.ehtw-liked {
    color: #f91880 !important;
}

.ehtw-liked svg path {
    fill: currentColor;
}

html.ehtw-detail #ehtw-center #cdiv .c1 .ehtw-comment-votes-native {
    display: none !important;
}

/* The native vote breakdown is revealed on score hover. Keep it out of the
   comment grid so its appearance cannot move the score from under the pointer. */
html.ehtw-detail .ehtw-comments-section #cdiv .c1.ehtw-comment-tweet .c7 {
    position: absolute !important;
    inset: 32px 12px auto 52px !important;
    z-index: 5 !important;
    width: auto !important;
    max-width: none !important;
    pointer-events: none !important;
}
/* 1.9.0:名字是作者,@ 是上传者。 */
.ehtw-handle a {
    color: inherit !important;
    font: inherit !important;
    text-decoration: none !important;
}

.ehtw-handle a:hover {
    text-decoration: underline !important;
}

.ehtw-author-name a {
    display: inline;
    max-width: 100%;
}

/* My Home 子页面使用原站表单,但需要在中栏宽度内重排。 */
@media (min-width: 700px) {
    html.ehtw-myhome #ehtw-shell,
    html.ehtw-myuploads #ehtw-shell,
    html.ehtw-bounty #ehtw-shell {
        width: min(100%, 1225px) !important;
        max-width: 100% !important;
        grid-template-columns: 275px minmax(0, 1fr) !important;
    }

    html.ehtw-myhome #ehtw-right,
    html.ehtw-myuploads #ehtw-right,
    html.ehtw-bounty #ehtw-right {
        display: none !important;
    }

    html.ehtw-myhome #ehtw-center,
    html.ehtw-myuploads #ehtw-center,
    html.ehtw-bounty #ehtw-center {
        width: 100% !important;
        max-width: none !important;
    }
}

html.ehtw-myhome #ehtw-center > .lb,
html.ehtw-myuploads #ehtw-center > .lb,
html.ehtw-bounty #ehtw-center > .lb {
    display: flex !important;
    flex-wrap: wrap !important;
    align-items: center !important;
    gap: 6px 8px !important;
    width: 100% !important;
    min-width: 0 !important;
    margin: 0 !important;
    padding: 12px 16px !important;
    border-bottom: 1px solid var(--ehtw-border) !important;
    background: var(--ehtw-bg) !important;
    color: var(--ehtw-muted) !important;
}

html.ehtw-myhome #ehtw-center > .lb > div,
html.ehtw-myuploads #ehtw-center > .lb > div,
html.ehtw-bounty #ehtw-center > .lb > div {
    display: inline-flex !important;
    align-items: center !important;
    width: auto !important;
    min-width: 0 !important;
    margin: 0 !important;
    color: var(--ehtw-muted) !important;
    white-space: nowrap !important;
}

html.ehtw-myhome #ehtw-center > .lb a,
html.ehtw-myuploads #ehtw-center > .lb a,
html.ehtw-bounty #ehtw-center > .lb a {
    display: inline-flex !important;
    align-items: center !important;
    min-height: 34px !important;
    padding: 0 10px !important;
    border: 1px solid var(--ehtw-border) !important;
    border-radius: 999px !important;
    background: var(--ehtw-card) !important;
    color: var(--ehtw-text) !important;
    text-decoration: none !important;
}

html.ehtw-myhome #ehtw-center > .lb a:hover,
html.ehtw-myuploads #ehtw-center > .lb a:hover,
html.ehtw-bounty #ehtw-center > .lb a:hover {
    border-color: var(--ehtw-accent) !important;
    color: var(--ehtw-accent) !important;
}

html.ehtw-myhome #ehtw-center .ehtw-generic-content,
html.ehtw-myuploads #ehtw-center .ehtw-generic-content,
html.ehtw-bounty #ehtw-center .ehtw-generic-content {
    padding: 12px 16px 24px !important;
}

html.ehtw-myhome #ehtw-center .ehtw-generic-content .stuffbox,
html.ehtw-myhome #ehtw-center .ehtw-generic-content .logbox,
html.ehtw-myhome #ehtw-center .ehtw-generic-content #outer,
html.ehtw-myuploads #ehtw-center .ehtw-generic-content .stuffbox,
html.ehtw-myuploads #ehtw-center .ehtw-generic-content .logbox,
html.ehtw-myuploads #ehtw-center .ehtw-generic-content #outer,
html.ehtw-bounty #ehtw-center .ehtw-generic-content .stuffbox,
html.ehtw-bounty #ehtw-center .ehtw-generic-content .logbox,
html.ehtw-bounty #ehtw-center .ehtw-generic-content #outer {
    box-sizing: border-box !important;
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
    height: auto !important;
    margin: 0 0 12px !important;
    padding: 14px !important;
    overflow-x: auto !important;
    overflow-y: visible !important;
    -webkit-overflow-scrolling: touch;
    border: 1px solid var(--ehtw-border) !important;
    border-radius: 14px !important;
    background: var(--ehtw-card) !important;
    color: var(--ehtw-text) !important;
}

html.ehtw-myhome #ehtw-center .ehtw-generic-content :is(.stuffbox, .logbox, #outer) h2,
html.ehtw-myuploads #ehtw-center .ehtw-generic-content :is(.stuffbox, .logbox, #outer) h2,
html.ehtw-bounty #ehtw-center .ehtw-generic-content :is(.stuffbox, .logbox, #outer) h2 {
    padding: 8px 0 12px !important;
    border-bottom: 1px solid var(--ehtw-border) !important;
    color: var(--ehtw-text) !important;
}

html.ehtw-myhome #ehtw-center .ehtw-generic-content :is(.stuffbox, .logbox, #outer) p,
html.ehtw-myhome #ehtw-center .ehtw-generic-content :is(.stuffbox, .logbox, #outer) label,
html.ehtw-myhome #ehtw-center .ehtw-generic-content :is(.stuffbox, .logbox, #outer) td,
html.ehtw-myhome #ehtw-center .ehtw-generic-content :is(.stuffbox, .logbox, #outer) th,
html.ehtw-myhome #ehtw-center .ehtw-generic-content :is(.stuffbox, .logbox, #outer) strong,
html.ehtw-myuploads #ehtw-center .ehtw-generic-content :is(.stuffbox, .logbox, #outer) p,
html.ehtw-myuploads #ehtw-center .ehtw-generic-content :is(.stuffbox, .logbox, #outer) label,
html.ehtw-myuploads #ehtw-center .ehtw-generic-content :is(.stuffbox, .logbox, #outer) td,
html.ehtw-myuploads #ehtw-center .ehtw-generic-content :is(.stuffbox, .logbox, #outer) th,
html.ehtw-myuploads #ehtw-center .ehtw-generic-content :is(.stuffbox, .logbox, #outer) strong,
html.ehtw-bounty #ehtw-center .ehtw-generic-content :is(.stuffbox, .logbox, #outer) p,
html.ehtw-bounty #ehtw-center .ehtw-generic-content :is(.stuffbox, .logbox, #outer) label,
html.ehtw-bounty #ehtw-center .ehtw-generic-content :is(.stuffbox, .logbox, #outer) td,
html.ehtw-bounty #ehtw-center .ehtw-generic-content :is(.stuffbox, .logbox, #outer) th,
html.ehtw-bounty #ehtw-center .ehtw-generic-content :is(.stuffbox, .logbox, #outer) strong {
    color: var(--ehtw-text) !important;
}

html.ehtw-myhome #ehtw-center .ehtw-generic-content :is(.stuffbox, .logbox, #outer) a {
    color: var(--ehtw-accent) !important;
}

html.ehtw-myhome #ehtw-center .ehtw-generic-content :is(input[type="text"], input[type="password"], input[type="number"], input[type="email"], select, textarea) {
    box-sizing: border-box !important;
    max-width: 100% !important;
    border: 1px solid var(--ehtw-border) !important;
    border-radius: 12px !important;
    background: var(--ehtw-card) !important;
    color: var(--ehtw-text) !important;
}

html.ehtw-myuploads #ehtw-center .ehtw-generic-content :is(.stuffbox, .logbox, #outer) a,
html.ehtw-bounty #ehtw-center .ehtw-generic-content :is(.stuffbox, .logbox, #outer) a {
    color: var(--ehtw-accent) !important;
}

html.ehtw-myhome #ehtw-center .ehtw-generic-content :is(.stuffbox, .logbox, #outer) form {
    box-sizing: border-box !important;
    width: auto !important;
    min-width: 0 !important;
    max-width: 100% !important;
}

html.ehtw-myuploads #ehtw-center .ehtw-generic-content :is(.stuffbox, .logbox, #outer) form,
html.ehtw-bounty #ehtw-center .ehtw-generic-content :is(.stuffbox, .logbox, #outer) form {
    box-sizing: border-box !important;
    width: auto !important;
    min-width: 0 !important;
    max-width: 100% !important;
}

html.ehtw-myhome #ehtw-center .ehtw-generic-content :is(.stuffbox, .logbox, #outer) table {
    max-width: 100% !important;
    border-color: var(--ehtw-border) !important;
    color: var(--ehtw-text) !important;
}

html.ehtw-myuploads #ehtw-center .ehtw-generic-content #m {
    box-sizing: border-box !important;
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
}

/* Bounty search and results use a compact filter panel and cards. */
html.ehtw-bounty #ehtw-center .ehtw-generic-content div#s.stuffbox {
    display: block !important;
}

html.ehtw-bounty #ehtw-center .ehtw-generic-content #searchform {
    display: flex !important;
    flex-direction: column !important;
    align-items: stretch !important;
    gap: 8px !important;
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
}

html.ehtw-bounty #ehtw-center .ehtw-generic-content #searchform > div {
    display: flex !important;
    flex-wrap: wrap !important;
    align-items: center !important;
    gap: 8px !important;
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
}

html.ehtw-bounty #ehtw-center .ehtw-generic-content #searchform input#s {
    flex: 1 1 220px !important;
    width: auto !important;
    min-width: 0 !important;
}

html.ehtw-bounty #ehtw-center .ehtw-generic-content #searchform select {
    flex: 1 1 180px !important;
    width: auto !important;
    min-width: 0 !important;
}

html.ehtw-bounty #ehtw-center .ehtw-generic-content #searchform input[type="button"] {
    flex: 0 0 auto !important;
    width: auto !important;
    min-width: 64px !important;
}

html.ehtw-bounty #ehtw-center .ehtw-generic-content table.itg {
    display: block !important;
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
    border: 0 !important;
    background: transparent !important;
}

html.ehtw-bounty #ehtw-center .ehtw-generic-content table.itg > tbody {
    display: block !important;
    width: 100% !important;
    min-width: 0 !important;
}

html.ehtw-bounty #ehtw-center .ehtw-generic-content table.itg > tbody > tr:first-child {
    display: none !important;
}

html.ehtw-bounty #ehtw-center .ehtw-generic-content table.itg > tbody > tr:not(:first-child) {
    display: grid !important;
    grid-template-columns: minmax(0, 1fr) auto !important;
    grid-template-rows: auto auto auto auto !important;
    align-items: start !important;
    gap: 4px 12px !important;
    box-sizing: border-box !important;
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
    height: auto !important;
    margin: 0 0 8px !important;
    padding: 10px 12px !important;
    border: 1px solid var(--ehtw-border) !important;
    border-radius: 12px !important;
    background: var(--ehtw-card) !important;
}

html.ehtw-bounty #ehtw-center .ehtw-generic-content table.itg > tbody > tr:not(:first-child) > td {
    display: flex !important;
    align-items: baseline !important;
    gap: 6px !important;
    box-sizing: border-box !important;
    width: auto !important;
    min-width: 0 !important;
    max-width: 100% !important;
    height: auto !important;
    padding: 2px 0 !important;
    border: 0 !important;
    background: transparent !important;
    color: var(--ehtw-text) !important;
    white-space: normal !important;
    overflow-wrap: anywhere !important;
}

html.ehtw-bounty #ehtw-center .ehtw-generic-content table.itg > tbody > tr:not(:first-child) > td:nth-child(1) {
    grid-column: 1;
    grid-row: 2;
}

html.ehtw-bounty #ehtw-center .ehtw-generic-content table.itg > tbody > tr:not(:first-child) > td:nth-child(2) {
    grid-column: 1 / -1;
    grid-row: 1;
    font-size: 15px !important;
    font-weight: 700 !important;
}

html.ehtw-bounty #ehtw-center .ehtw-generic-content table.itg > tbody > tr:not(:first-child) > td:nth-child(3) {
    grid-column: 2;
    grid-row: 2;
    justify-content: flex-end !important;
}

html.ehtw-bounty #ehtw-center .ehtw-generic-content table.itg > tbody > tr:not(:first-child) > td:nth-child(4) {
    grid-column: 2;
    grid-row: 3;
    justify-content: flex-end !important;
}

html.ehtw-bounty #ehtw-center .ehtw-generic-content table.itg > tbody > tr:not(:first-child) > td:nth-child(5) {
    grid-column: 1;
    grid-row: 3;
    font-weight: 600 !important;
}

html.ehtw-bounty #ehtw-center .ehtw-generic-content table.itg > tbody > tr:not(:first-child) > td:nth-child(6) {
    grid-column: 1 / -1;
    grid-row: 4;
    color: var(--ehtw-muted) !important;
    font-size: 12px !important;
}

html.ehtw-bounty #ehtw-center .ehtw-generic-content table.itg > tbody > tr:not(:first-child) > td:nth-child(1)::before { content: 'Updated'; }
html.ehtw-bounty #ehtw-center .ehtw-generic-content table.itg > tbody > tr:not(:first-child) > td:nth-child(3)::before { content: 'Type'; }
html.ehtw-bounty #ehtw-center .ehtw-generic-content table.itg > tbody > tr:not(:first-child) > td:nth-child(4)::before { content: 'Status'; }
html.ehtw-bounty #ehtw-center .ehtw-generic-content table.itg > tbody > tr:not(:first-child) > td:nth-child(5)::before { content: 'Bounty'; }
html.ehtw-bounty #ehtw-center .ehtw-generic-content table.itg > tbody > tr:not(:first-child) > td:nth-child(6)::before { content: 'Posted by'; }

html.ehtw-bounty #ehtw-center .ehtw-generic-content table.ptt,
html.ehtw-bounty #ehtw-center .ehtw-generic-content table.ptb {
    display: block !important;
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
    overflow-x: auto !important;
}

/* Most Wanted pages contain a 340px image, 482px details column and a
   second 340px image. Fit all three columns into the narrower center pane. */
html.ehtw-bounty #ehtw-center .ehtw-generic-content #t > .d1 {
    display: grid !important;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.25fr) minmax(0, 1fr) !important;
    align-items: stretch !important;
    gap: 8px !important;
    box-sizing: border-box !important;
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
    height: auto !important;
    margin: 10px 0 !important;
    padding: 0 !important;
    top: auto !important;
    overflow: visible !important;
}

html.ehtw-bounty #ehtw-center .ehtw-generic-content #t > .d1 > .d2,
html.ehtw-bounty #ehtw-center .ehtw-generic-content #t > .d1 > .d3,
html.ehtw-bounty #ehtw-center .ehtw-generic-content #t > .d1 > .d6 {
    box-sizing: border-box !important;
    float: none !important;
    width: auto !important;
    min-width: 0 !important;
    max-width: 100% !important;
    height: auto !important;
    margin: 0 !important;
    padding: 0 !important;
    overflow: visible !important;
}

html.ehtw-bounty #ehtw-center .ehtw-generic-content #t > .d1 > .d2,
html.ehtw-bounty #ehtw-center .ehtw-generic-content #t > .d1 > .d6 {
    display: flex !important;
    align-items: flex-start !important;
    justify-content: center !important;
}

html.ehtw-bounty #ehtw-center .ehtw-generic-content #t > .d1 > .d2 img,
html.ehtw-bounty #ehtw-center .ehtw-generic-content #t > .d1 > .d6 img {
    display: block !important;
    width: 100% !important;
    height: auto !important;
    max-width: 100% !important;
    max-height: 400px !important;
    object-fit: contain !important;
}

html.ehtw-bounty #ehtw-center .ehtw-generic-content #t > .d1 > .d3 {
    display: flex !important;
    flex-direction: column !important;
    justify-content: space-between !important;
    gap: 8px !important;
}

html.ehtw-bounty #ehtw-center .ehtw-generic-content #t > .d1 > .d3 > .d4,
html.ehtw-bounty #ehtw-center .ehtw-generic-content #t > .d1 > .d3 > .d5 {
    box-sizing: border-box !important;
    width: 100% !important;
    min-width: 0 !important;
    height: auto !important;
    min-height: 0 !important;
    padding: 8px !important;
    border: 1px solid var(--ehtw-border) !important;
    border-radius: 10px !important;
    background: var(--ehtw-bg) !important;
    color: var(--ehtw-text) !important;
    overflow-wrap: anywhere !important;
}

html.ehtw-bounty #ehtw-center .ehtw-generic-content #t > .d1 .d4 table,
html.ehtw-bounty #ehtw-center .ehtw-generic-content #t > .d1 .d5 table {
    width: 100% !important;
    min-width: 0 !important;
    table-layout: fixed !important;
}

html.ehtw-bounty #ehtw-center .ehtw-generic-content #t > .d1 .d4 td,
html.ehtw-bounty #ehtw-center .ehtw-generic-content #t > .d1 .d5 td {
    min-width: 0 !important;
    padding: 3px 4px !important;
    overflow-wrap: anywhere !important;
}

html.ehtw-myuploads #ehtw-center .ehtw-generic-content :is(.stuffbox, .logbox, #outer) table,
html.ehtw-bounty #ehtw-center .ehtw-generic-content :is(.stuffbox, .logbox, #outer) table {
    max-width: 100% !important;
    border-color: var(--ehtw-border) !important;
    color: var(--ehtw-text) !important;
}

html.ehtw-mytags #ehtw-center .ehtw-generic-content #outer.stuffbox {
    padding: 14px !important;
}

html.ehtw-mytags #ehtw-center #tagset_outer {
    display: flex !important;
    flex-wrap: wrap !important;
    align-items: center !important;
    gap: 8px !important;
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
    height: auto !important;
    margin: 0 0 12px !important;
    padding: 0 0 12px !important;
    border-bottom: 1px solid var(--ehtw-border) !important;
    background: transparent !important;
}

html.ehtw-mytags #ehtw-center #tagset_outer > * {
    float: none !important;
    width: auto !important;
    min-width: 0 !important;
    max-width: 100% !important;
    height: auto !important;
    margin: 0 !important;
}

html.ehtw-mytags #ehtw-center #tagset_outer #tagcolor {
    width: 112px !important;
    min-width: 72px !important;
}

html.ehtw-mytags #ehtw-center #usertag_form,
html.ehtw-mytags #ehtw-center #usertags_outer {
    box-sizing: border-box !important;
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
    height: auto !important;
    margin: 0 !important;
}

html.ehtw-mytags #ehtw-center #usertags_outer {
    display: flex !important;
    flex-direction: column !important;
    gap: 6px !important;
}

html.ehtw-mytags #ehtw-center #usertags_outer > div {
    display: grid !important;
    grid-template-columns: minmax(88px, 1fr) 66px 66px 26px minmax(58px, 70px) 34px 48px !important;
    align-items: center !important;
    gap: 4px !important;
    box-sizing: border-box !important;
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
    height: auto !important;
    min-height: 42px !important;
    margin: 0 !important;
    padding: 5px 6px !important;
    border: 1px solid var(--ehtw-border) !important;
    border-radius: 10px !important;
    background: var(--ehtw-bg) !important;
}

html.ehtw-mytags #ehtw-center #usertags_outer > div > div {
    box-sizing: border-box !important;
    float: none !important;
    width: auto !important;
    min-width: 0 !important;
    max-width: 100% !important;
    height: auto !important;
    margin: 0 !important;
}

html.ehtw-mytags #ehtw-center #usertags_outer > div > div:nth-child(2),
html.ehtw-mytags #ehtw-center #usertags_outer > div > div:nth-child(3) {
    display: flex !important;
    align-items: center !important;
    justify-content: center !important;
    gap: 4px !important;
    color: var(--ehtw-muted) !important;
    font-size: 12px !important;
    white-space: nowrap !important;
}

html.ehtw-mytags #ehtw-center #usertags_outer > div > div:first-child {
    display: flex !important;
    align-items: center !important;
    justify-content: flex-start !important;
    text-align: left !important;
    overflow: hidden !important;
}

html.ehtw-mytags #ehtw-center #usertags_outer input[type="text"],
html.ehtw-mytags #ehtw-center #usertags_outer input[type="number"] {
    box-sizing: border-box !important;
    width: 100% !important;
    min-width: 0 !important;
    min-height: 32px !important;
    height: 32px !important;
    margin: 0 !important;
    padding: 0 7px !important;
    border-radius: 8px !important;
    font-size: 13px !important;
}

html.ehtw-mytags #ehtw-center #usertags_outer input[type="checkbox"] {
    width: 16px !important;
    min-width: 16px !important;
    height: 16px !important;
    min-height: 16px !important;
    margin: 0 !important;
}

html.ehtw-mytags #ehtw-center #usertags_outer button[id^="colorsetter"] {
    width: 24px !important;
    min-width: 24px !important;
    height: 24px !important;
    min-height: 24px !important;
    padding: 0 !important;
}

html.ehtw-mytags #ehtw-center #usertags_outer button[id^="tagsave"] {
    width: 48px !important;
    min-width: 48px !important;
    padding: 0 5px !important;
    font-size: 11px !important;
}

@media (max-width: 799px) {
    html.ehtw-mytags #ehtw-center #usertags_outer > div {
        grid-template-columns: minmax(0, 1fr) 72px 72px 58px !important;
        grid-template-rows: auto auto auto !important;
        gap: 5px !important;
    }

    html.ehtw-mytags #ehtw-center #usertags_outer > div > div:first-child {
        grid-column: 1 / -1 !important;
    }

    html.ehtw-mytags #ehtw-center #usertags_outer > div > div:nth-child(2),
    html.ehtw-mytags #ehtw-center #usertags_outer > div > div:nth-child(3) {
        justify-content: center !important;
        font-size: 11px !important;
    }

    html.ehtw-mytags #ehtw-center #usertags_outer button[id^="tagsave"] {
        width: 58px !important;
        min-width: 58px !important;
        padding: 0 4px !important;
        font-size: 11px !important;
    }

    html.ehtw-mytags #ehtw-center #usertags_outer > div > div:nth-child(5) {
        grid-column: 1 / span 2 !important;
    }

    html.ehtw-mytags #ehtw-center #usertags_outer > div > div:nth-child(6) {
        grid-column: 3 !important;
    }

    html.ehtw-mytags #ehtw-center #usertags_outer > div > div:nth-child(7) {
        grid-column: 4 !important;
    }
}

.ehtw-following-checking {
    visibility: hidden !important;
}

.ehtw-follow-mode-tabs {
    display: flex;
    gap: 4px;
    padding: 8px 12px;
    border-bottom: 1px solid var(--ehtw-border);
}

.ehtw-follow-mode-tab {
    flex: 1 1 0;
    padding: 9px 12px;
    border-radius: 999px;
    color: var(--ehtw-muted) !important;
    font-size: 13px;
    font-weight: 600;
    line-height: 18px;
    text-align: center;
    text-decoration: none !important;
}

.ehtw-follow-mode-tab:hover {
    background: var(--ehtw-nav-hover);
}

.ehtw-follow-mode-active {
    background: var(--ehtw-card);
    color: var(--ehtw-text) !important;
}

.ehtw-following-hidden {
    display: none !important;
}

html.ehtw-following-authors #toppane {
    display: none !important;
}

html.ehtw-list #ehtw-center .ehtw-tweet.ehtw-following-hidden {
    display: none !important;
}

.ehtw-card-follow-button {
    flex: 0 0 auto;
    min-height: 30px;
    padding: 4px 15px;
    border: 0;
    border-radius: 999px;
    background: #1d9bf0;
    color: #fff;
    font: inherit;
    font-size: 13px;
    font-weight: 700;
    line-height: 1.2;
    cursor: pointer;
}

.ehtw-card-follow-button:hover {
    background: #1a8cd8;
}

.ehtw-card-follow-button:disabled {
    cursor: default;
    background: transparent;
    color: var(--ehtw-muted);
    border: 1px solid var(--ehtw-border);
}

html.ehtw-list .ehtw-tweet-author .ehtw-card-follow-button {
    margin-left: auto;
}

html.ehtw-list .ehtw-tweet-author:has(.ehtw-card-follow-button) {
    height: auto !important;
    min-height: 32px !important;
    overflow: visible !important;
    margin-bottom: 4px !important;
}

html.ehtw-list .ehtw-tweet-author:has(> .ehtw-card-follow-button) .ehtw-card-more-menu {
    margin-left: 4px !important;
}

html.ehtw-list .ehtw-tweet-author .ehtw-follow-choice-wrap {
    margin-left: auto !important;
}

html.ehtw-list .ehtw-tweet-author:has(.ehtw-follow-choice-wrap) .ehtw-card-more-menu {
    margin-left: 4px !important;
}

.ehtw-follow-choice-wrap .ehtw-follow-choice-button {
    width: auto !important;
    min-width: 76px;
    height: 30px !important;
    margin: 0 !important;
    padding: 4px 12px !important;
    border-radius: 999px !important;
    font: inherit !important;
    font-size: 13px !important;
    font-weight: 700 !important;
    line-height: 1.2 !important;
}

.ehtw-native-setting-message {
    margin: 12px 0 !important;
    padding: 10px 14px !important;
    border: 1px solid var(--ehtw-border) !important;
    border-radius: 12px !important;
    background: var(--ehtw-card) !important;
    color: var(--ehtw-text) !important;
    font-size: 13px !important;
    line-height: 1.45 !important;
}

@media (max-width: 699px) {
    html.ehtw-detail #ehtw-center .ehtw-detail-tags #taglist,
    html.ehtw-detail #ehtw-center .ehtw-detail-tags #taglist * {
        box-sizing: border-box !important;
        min-width: 0 !important;
        max-width: 100% !important;
        white-space: normal !important;
        overflow-wrap: anywhere !important;
        word-break: normal !important;
    }

    html.ehtw-detail #ehtw-center .ehtw-detail-tags #taglist .gt,
    html.ehtw-detail #ehtw-center .ehtw-detail-tags #taglist .gtl,
    html.ehtw-detail #ehtw-center .ehtw-detail-tags #taglist .gtw,
    html.ehtw-detail #ehtw-center .ehtw-detail-tags #taglist .gt a,
    html.ehtw-detail #ehtw-center .ehtw-detail-tags #taglist .gtl a,
    html.ehtw-detail #ehtw-center .ehtw-detail-tags #taglist .gtw a {
        max-width: 100% !important;
        white-space: normal !important;
        overflow-wrap: anywhere !important;
        word-break: normal !important;
    }

    html.ehtw-bounty #ehtw-center .ehtw-generic-content #t > .d1 {
        grid-template-columns: minmax(0, 0.38fr) minmax(0, 0.62fr) !important;
        grid-template-rows: auto auto !important;
    }

    html.ehtw-bounty #ehtw-center .ehtw-generic-content #t > .d1 > .d2 {
        grid-column: 1 !important;
        grid-row: 1 !important;
    }

    html.ehtw-bounty #ehtw-center .ehtw-generic-content #t > .d1 > .d3 {
        display: contents !important;
    }

    html.ehtw-bounty #ehtw-center .ehtw-generic-content #t > .d1 > .d6 {
        grid-column: 1 !important;
        grid-row: 2 !important;
    }

    html.ehtw-bounty #ehtw-center .ehtw-generic-content #t > .d1 > .d3 > .d4,
    html.ehtw-bounty #ehtw-center .ehtw-generic-content #t > .d1 > .d4 {
        grid-column: 2 !important;
        grid-row: 1 !important;
        align-self: stretch !important;
    }

    html.ehtw-bounty #ehtw-center .ehtw-generic-content #t > .d1 > .d3 > .d5,
    html.ehtw-bounty #ehtw-center .ehtw-generic-content #t > .d1 > .d5 {
        grid-column: 2 !important;
        grid-row: 2 !important;
        align-self: stretch !important;
    }

    html.ehtw-bounty #ehtw-center .ehtw-generic-content #t > .d1 > .d2 img,
    html.ehtw-bounty #ehtw-center .ehtw-generic-content #t > .d1 > .d6 img {
        max-height: 320px !important;
    }

    html.ehtw-myhome #ehtw-center .ehtw-generic-content {
        padding: 8px 10px 20px !important;
    }
}
`;

        // ============================================================
        // 3. 注入样式与外壳
        // ============================================================

        styleElement = create('style');
        styleElement.id = 'ehtw-style';
        styleElement.textContent = CSS;

        append(
            document.head || document.documentElement,
            styleElement
        );

        document.documentElement.classList.add(
            'ehtw-enabled'
        );
        if (location.hostname.toLowerCase() === 'exhentai.org') {
            document.documentElement.classList.add('ehtw-exhentai');
        }

        document.documentElement.classList.add(
            isDetail
                ? 'ehtw-detail'
                : isList
                    ? 'ehtw-list'
                    : isGeneric
                        ? 'ehtw-generic'
                        : isLogin
                            ? 'ehtw-login'
                            : 'ehtw-other'
        );
        if (isFollowingFeedPage) {
            document.documentElement.classList.add('ehtw-following-authors');
        }
        if (isMyHomePage) {
            document.documentElement.classList.add('ehtw-myhome');
        }
        if (isMyTagsPage) {
            document.documentElement.classList.add('ehtw-mytags');
        }
        if (isMyUploadsPage) {
            document.documentElement.classList.add('ehtw-myuploads');
        }
        if (isBountyPage || path === '/bounty_post.php') {
            document.documentElement.classList.add('ehtw-bounty');
        }

        // 原站在手机上写死 width=750,手机断点永远不会生效。
        const viewportContent =
            'width=device-width, initial-scale=1, viewport-fit=cover';

        const viewportMeta = document.querySelector(
            'meta[name="viewport"]'
        );

        if (viewportMeta) {
            setAttributeSafe(
                viewportMeta,
                'content',
                viewportContent
            );
        } else {
            const meta = create('meta');

            meta.setAttribute('name', 'viewport');
            meta.setAttribute('content', viewportContent);
            document.head.appendChild(meta);
            registerCreatedNode(meta);
        }

        if (isLogin) {
            committed = true;
            publishSpaPageCleanup(() => rollback?.());
            releasePageBoot();
            return;
        }

        shell = create('div');
        shell.id = 'ehtw-shell';

        const left = create('aside');
        left.id = 'ehtw-left';

        const center = create('main');
        center.id = 'ehtw-center';

        const right = create('aside');
        right.id = 'ehtw-right';

        append(shell, left);
        append(shell, center);
        append(shell, right);

        document.body.insertBefore(
            shell,
            document.body.firstChild
        );

        // ============================================================
        // 4. 左侧导航
        // ============================================================

        function buildNavigation() {
            const brand = create('a', 'ehtw-brand-link');
            brand.href = '/';
            const siteBrand = location.hostname.toLowerCase() === 'exhentai.org'
                ? 'ExHentai'
                : 'E-Hentai';
            brand.setAttribute('aria-label', `${siteBrand} Home`);
            brand.textContent = siteBrand;
            append(left, brand);

            const nav = qs('#nb');
            if (!nav) return;

            move(nav, left);

            const entries = Array.from(nav.children);
            const accountIsLoggedIn = isNativeNavigationLoggedIn(nav);

            const mappings = [
                [/^front/i, 'home'],
                [/^watched/i, 'eye'],
                [/^popular/i, 'flame'],
                [/^torrents?/i, 'download'],
                [/^(favorites?|favs?)/i, 'bookmark'],
                [/^(my\s*home|home)/i, 'user'],
                [/^settings?/i, 'settings'],
                [/^my\s*tags?/i, 'tag'],
                [/^(my\s*uploads?|uploads?)/i, 'upload'],
                [/^top\s*lists?/i, 'list'],
                [/^bounties/i, 'gift'],
                [/^news/i, 'news'],
                [/^forums?/i, 'chat'],
                [/^wiki/i, 'book'],
                [/^hentai\s*verse/i, 'globe']
            ];

            function normalizePath(url) {
                try {
                    const value = new URL(url, location.href);
                    return value.pathname.replace(/\/+$/, '') || '/';
                } catch (_) {
                    return '';
                }
            }

            const currentPath = normalizePath(location.href);

            let uploadHref = '/';
            let homeHref = '/';
            let watchedEntry = null;

            entries.forEach((entry, index) => {
                const link = entry.matches('a[href]')
                    ? entry
                    : entry.querySelector('a[href]');

                if (!link) return;

                addClassSafe(entry, 'ehtw-nav-entry');
                addClassSafe(link, 'ehtw-nav-link');

                const label = textOf(link).trim();

                const mapping = mappings.find(([pattern]) =>
                    pattern.test(label)
                );

                const kind = mapping ? mapping[1] : 'more';

                if (kind === 'eye') watchedEntry = entry;

                const isCurrent =
                    new URL(link.href, location.href).origin === location.origin &&
                    normalizePath(link.href) === currentPath &&
                    !(isFollowingFeedPage && kind === 'eye');

                const originalText = registerCreatedNode(
                    create('span', 'ehtw-nav-label')
                );

                // 原文字节点必须记录搬移,才能在 rollback 时归位。
                while (link.firstChild) {
                    move(link.firstChild, originalText);
                }

                const icon = registerCreatedNode(
                    makeIcon(
                        isCurrent ? kind + '-filled' : kind
                    )
                );

                link.appendChild(icon);
                link.appendChild(originalText);

                if (kind === 'upload') {
                    uploadHref = link.href;
                }

                if (kind === 'user') {
                    homeHref = link.href;
                }

                if (isCurrent) {
                    addClassSafe(entry, 'ehtw-nav-current');
                    setAttributeSafe(
                        link,
                        'aria-current',
                        'page'
                    );
                }

                if (index >= 10) {
                    addClassSafe(entry, 'ehtw-nav-extra');
                }

                if (index >= 5) {
                    addClassSafe(
                        entry,
                        'ehtw-nav-mobile-extra'
                    );
                }
            });

            const hasNativeToplists = entries.some((entry) =>
                /\btop\s*lists?\b/i.test(textOf(entry))
            );
            if (location.hostname.toLowerCase() === 'exhentai.org' && !hasNativeToplists) {
                const toplistsEntry = registerCreatedNode(
                    create('div', 'ehtw-nav-entry ehtw-toplists-link ehtw-nav-mobile-extra')
                );
                const toplistsLink = registerCreatedNode(create('a', 'ehtw-nav-link'));
                toplistsLink.href = 'https://e-hentai.org/toplist.php';
                toplistsLink.title = 'Opens E-Hentai Toplists';
                toplistsLink.appendChild(makeIcon('list'));
                append(toplistsLink, create('span', 'ehtw-nav-label', 'Toplists'));
                append(toplistsEntry, toplistsLink);

                const uploadsEntry = entries.find((entry) =>
                    /\/upld\/manage|\/manage(?:$|[/?#])/i.test(
                        entry.querySelector?.('a[href]')?.getAttribute('href') || ''
                    )
                );
                if (uploadsEntry?.parentNode === nav) {
                    nav.insertBefore(toplistsEntry, uploadsEntry.nextSibling);
                } else {
                    nav.appendChild(toplistsEntry);
                }
            }

            const followingEntry = registerCreatedNode(
                create('div', 'ehtw-nav-entry ehtw-nav-following-entry')
            );
            const followingLink = registerCreatedNode(
                create('a', 'ehtw-nav-link')
            );
            followingLink.href = '/watched?ehtw_following=authors';
            followingLink.appendChild(makeIcon(isFollowingFeedPage ? 'following-filled' : 'following'));
            append(
                followingLink,
                create('span', 'ehtw-nav-label', 'Following')
            );
            append(followingEntry, followingLink);
            if (isFollowingFeedPage) {
                addClassSafe(followingEntry, 'ehtw-nav-current');
                setAttributeSafe(followingLink, 'aria-current', 'page');
            }
            addClassSafe(followingEntry, 'ehtw-nav-mobile-extra');
            if (watchedEntry?.parentNode === nav) {
                nav.insertBefore(followingEntry, watchedEntry.nextSibling);
            } else {
                append(nav, followingEntry);
            }

            const mobileAccountEntry = registerCreatedNode(
                create('div', 'ehtw-nav-entry ehtw-nav-mobile-extra ehtw-mobile-account-entry')
            );
            const mobileAccountLink = registerCreatedNode(
                create('a', 'ehtw-nav-link')
            );
            mobileAccountLink.href = getNativeAccountActionUrl(accountIsLoggedIn);
            mobileAccountLink.appendChild(makeIcon(accountIsLoggedIn ? 'logout' : 'login'));
            append(
                mobileAccountLink,
                create('span', 'ehtw-nav-label', accountIsLoggedIn ? '退出登录' : '登录')
            );
            append(mobileAccountEntry, mobileAccountLink);
            append(nav, mobileAccountEntry);

            const moreEntry = registerCreatedNode(
                create('div', 'ehtw-more-entry')
            );

            const moreButton = registerCreatedNode(
                create('button', 'ehtw-more-button')
            );

            moreButton.type = 'button';
            moreButton.setAttribute('aria-expanded', 'false');

            moreButton.appendChild(makeIcon('moreCircle'));

            append(
                moreButton,
                create('span', 'ehtw-nav-label', 'More')
            );

            append(moreEntry, moreButton);
            append(nav, moreEntry);

            moreButton.addEventListener('click', () => {
                const expanded = nav.classList.contains(
                    'ehtw-nav-open'
                );

                if (expanded) {
                    nav.classList.remove('ehtw-nav-open');
                } else {
                    addClassSafe(nav, 'ehtw-nav-open');
                }

                moreButton.setAttribute(
                    'aria-expanded',
                    String(!expanded)
                );
            });

            const postLink = create(
                'a',
                'ehtw-post-button',
                'Post'
            );

            postLink.href = uploadHref;
            append(left, postLink);

            // 账号入口位于左栏可滚动内容的末尾,
            // CSS 使用 flex + margin-top:auto 将其推到视口底部。
            const account = create('div', 'ehtw-account-card');
            account.setAttribute('data-ehtw-account-menu', '');
            account.setAttribute('role', 'button');
            account.setAttribute('tabindex', '0');
            account.setAttribute('aria-label', '账户菜单');
            account.setAttribute('aria-haspopup', 'menu');
            account.setAttribute('aria-expanded', 'false');

            const avatar = create('span', 'ehtw-account-avatar', 'E');

            const accountText = create('span', 'ehtw-account-text');

            append(
                accountText,
                create(
                    'span',
                    'ehtw-account-name',
                    'My Home'
                )
            );

            append(
                accountText,
                create(
                    'span',
                    'ehtw-account-handle',
                    '@e_hentai'
                )
            );

            append(account, avatar);
            append(account, accountText);

            const accountMenu = create('div', 'ehtw-account-menu');
            accountMenu.hidden = true;
            accountMenu.setAttribute('role', 'menu');
            const homeAction = create('a', 'ehtw-account-menu-action', 'My Home');
            homeAction.href = homeHref;
            homeAction.setAttribute('role', 'menuitem');
            append(accountMenu, homeAction);
            const accountAction = create(
                'a',
                'ehtw-account-menu-action',
                accountIsLoggedIn ? '退出登录' : '登录'
            );
            accountAction.href = getNativeAccountActionUrl(accountIsLoggedIn);
            accountAction.setAttribute('role', 'menuitem');
            append(accountMenu, accountAction);
            append(account, accountMenu);

            const toggleAccountMenu = (event) => {
                if (accountMenu.contains(event.target)) return;
                event.preventDefault();
                accountMenu.hidden = !accountMenu.hidden;
                account.setAttribute('aria-expanded', String(!accountMenu.hidden));
            };
            account.addEventListener('click', toggleAccountMenu);
            account.addEventListener('keydown', (event) => {
                if (event.target !== account || !['Enter', ' '].includes(event.key)) return;
                event.preventDefault();
                toggleAccountMenu(event);
            });
            addSpaPageEventListener(document, 'click', (event) => {
                if (accountMenu.hidden || account.contains(event.target)) return;
                accountMenu.hidden = true;
                account.setAttribute('aria-expanded', 'false');
            });
            addSpaPageEventListener(document, 'keydown', (event) => {
                if (event.key !== 'Escape' || accountMenu.hidden) return;
                accountMenu.hidden = true;
                account.setAttribute('aria-expanded', 'false');
                account.focus();
            });

            append(left, account);
        }

        function shouldUseHistoryBack(event, referrer, origin, historyLength, hasSpaHistoryParent = false) {
            if (
                !event ||
                event.defaultPrevented ||
                event.button !== 0 ||
                event.metaKey ||
                event.ctrlKey ||
                event.shiftKey ||
                event.altKey ||
                historyLength <= 1
            ) {
                return false;
            }

            if (hasSpaHistoryParent) return true;

            try {
                return new URL(referrer).origin === origin;
            } catch (_) {
                return false;
            }
        }

        function getGalleryGridExpandedStorageKey(pathname) {
            const galleryId = String(pathname || '')
                .match(/^\/g\/(\d+)\//i)?.[1];
            return galleryId
                ? `ehtw-gallery-grid-expanded-v1:${galleryId}`
                : null;
        }

        // ============================================================
        // 5. 中栏固定标题
        // ============================================================

        function buildHeader() {
            const header = create('header');
            header.id = 'ehtw-header';

            const pathname =
                location.pathname.replace(/\/+$/, '') || '/';

            const searchParams = new URLSearchParams(
                location.search
            );

            const searchTerm =
                searchParams.get('f_search') || '';

            const isHomeTabs =
                !isDetail &&
                !isGeneric &&
                !searchTerm &&
                ['/', '/watched', '/popular'].includes(pathname);

            const galleryId = isDetail
                ? getGalleryIdFromUrl(location.href)
                : null;
            const galleryReturnHrefStorageKey = galleryId
                ? `ehtw-gallery-return-v1:${galleryId}`
                : null;
            const spaPreviousHref = window.history.state?.ehtwSpaPreviousHref || '';
            const isExplicitReturnEntry = window.history.state?.ehtwSpaReturnBackTarget === true;
            const previousPageHref = spaPreviousHref || (isExplicitReturnEntry ? '' : document.referrer);
            const hasSpaHistoryParent = !!window.history.state?.ehtwSpaRoute;
            const previousPageGalleryId = !galleryId
                ? getGalleryIdFromUrl(previousPageHref)
                : null;
            const searchBackHref = previousPageGalleryId
                ? resolveSearchBackHref(previousPageHref, location.href, location.origin, window.sessionStorage)
                : '';
            let storedGalleryReturnHref = null;
            if (galleryReturnHrefStorageKey) {
                try {
                    if (previousPageHref) {
                        const previous = new URL(previousPageHref, location.href);
                        if (
                            previous.origin === location.origin &&
                            getGalleryIdFromUrl(previous.href) !== galleryId &&
                            isListLikePath(previous.pathname)
                        ) {
                            window.sessionStorage?.setItem(
                                galleryReturnHrefStorageKey,
                                previous.href
                            );
                            const currentDepth = readFiniteNumber(window.history.state?.ehtwSpaDepth);
                            if (window.history.state?.ehtwSpaRoute && currentDepth !== null && currentDepth > 0) {
                                window.sessionStorage?.setItem(
                                    galleryReturnDepthKey(galleryId),
                                    String(currentDepth - 1)
                                );
                            } else {
                                window.sessionStorage?.removeItem(galleryReturnDepthKey(galleryId));
                            }
                        }
                    }
                    storedGalleryReturnHref = window.sessionStorage?.getItem(
                        galleryReturnHrefStorageKey
                    ) || null;
                } catch (_) {}
            }

            function getBackHref() {
                if (galleryId) {
                    return resolveGalleryReturnHref(
                        storedGalleryReturnHref,
                        previousPageHref,
                        location.origin,
                        galleryId
                    );
                }

                if (previousPageGalleryId) return searchBackHref || '/';

                if (previousPageHref) {
                    try {
                        const previous = new URL(previousPageHref, location.href);

                        if (previous.origin === location.origin) {
                            return previous.href;
                        }
                    } catch (_) {}
                }

                if (isMyUploadsPage) {
                    const galleryOrigin = location.hostname.toLowerCase() === 'upld.exhentai.org'
                        ? 'https://exhentai.org'
                        : 'https://e-hentai.org';
                    return `${isGallerySiteOrigin(location.origin) ? location.origin : galleryOrigin}/home.php`;
                }

                return '/';
            }

            function addBackButton() {
                const back = create('a', 'ehtw-back-link');
                back.href = getBackHref();
                back.setAttribute('aria-label', 'Back');
                back.addEventListener('click', (event) => {
                    const isOrdinaryLeftClick =
                        event.button === 0 &&
                        !event.metaKey &&
                        !event.ctrlKey &&
                        !event.shiftKey &&
                        !event.altKey;

                    if (!galleryId && previousPageGalleryId && isOrdinaryLeftClick) {
                        const targetHref = searchBackHref || '/';
                        event.preventDefault();
                        if (
                            window.__EHTW_SPA_READY__ &&
                            typeof window.__EHTW_SPA_NAVIGATE__ === 'function'
                        ) {
                            window.__EHTW_SPA_NAVIGATE__(targetHref, {
                                historyMode: 'replace',
                                restoreScrollY: getSavedSpaScroll(targetHref),
                                returnBackHref: getSavedSpaBackTarget(targetHref) || '/',
                                returnBackTarget: true
                            });
                        } else {
                            location.replace(targetHref);
                        }
                        return;
                    }

                    if (!galleryId && isExplicitReturnEntry && isOrdinaryLeftClick) {
                        const targetHref = previousPageHref || '/';
                        if (new URL(targetHref, location.href).href === location.href) {
                            event.preventDefault();
                            return;
                        }
                        event.preventDefault();
                        if (
                            window.__EHTW_SPA_READY__ &&
                            typeof window.__EHTW_SPA_NAVIGATE__ === 'function'
                        ) {
                            window.__EHTW_SPA_NAVIGATE__(targetHref, {
                                historyMode: 'replace',
                                restoreScrollY: getSavedSpaScroll(targetHref),
                                returnBackHref: getSavedSpaBackTarget(targetHref) || '/',
                                returnBackTarget: true
                            });
                        } else {
                            location.replace(targetHref);
                        }
                        return;
                    }

                    if (galleryId && isOrdinaryLeftClick) {
                        // Jump straight back to the list from a direct SPA chain.
                        // Thumbnail pagination steps back through same-gallery entries;
                        // SPA depth can diverge from browser history indexes.
                        const isThumbnailPaginationPage = new URL(location.href).searchParams.has('p');
                        if (isThumbnailPaginationPage && storedGalleryReturnHref) {
                            event.preventDefault();
                            if (
                                window.__EHTW_SPA_READY__ &&
                                typeof window.__EHTW_SPA_NAVIGATE__ === 'function'
                            ) {
                                window.__EHTW_SPA_NAVIGATE__(storedGalleryReturnHref, {
                                    historyMode: 'replace',
                                    restoreScrollY: getSavedSpaScroll(storedGalleryReturnHref),
                                    returnBackHref: getSavedSpaBackTarget(storedGalleryReturnHref) || '/',
                                    returnBackTarget: true
                                });
                            } else {
                                location.replace(storedGalleryReturnHref);
                            }
                            return;
                        }
                        const returnDepth = readFiniteNumber(
                            window.sessionStorage?.getItem(galleryReturnDepthKey(galleryId))
                        );
                        const currentDepth = readFiniteNumber(window.history.state?.ehtwSpaDepth);
                        if (
                            !isThumbnailPaginationPage &&
                            hasSpaHistoryParent &&
                            returnDepth !== null &&
                            currentDepth !== null &&
                            currentDepth > returnDepth &&
                            currentDepth - returnDepth < window.history.length
                        ) {
                            event.preventDefault();
                            window.history.go(-(currentDepth - returnDepth));
                            return;
                        }
                        if (
                            previousPageHref &&
                            getGalleryIdFromUrl(previousPageHref) === galleryId
                        ) {
                            event.preventDefault();
                            if (window.history.length > 1) {
                                try {
                                    window.sessionStorage?.setItem(
                                        galleryBackTraversalStorageKey,
                                        galleryId
                                    );
                                    window.history.back();
                                } catch (_) {
                                    navigatePage('/');
                                }
                            } else {
                                navigatePage(storedGalleryReturnHref || getBackHref());
                            }
                            return;
                        }

                        if (
                            storedGalleryReturnHref &&
                            shouldUseHistoryBack(
                                event,
                                previousPageHref,
                                location.origin,
                                window.history.length,
                                hasSpaHistoryParent
                            )
                        ) {
                            event.preventDefault();
                            window.history.back();
                            return;
                        }
                    }

                    if (!shouldUseHistoryBack(
                        event,
                        previousPageHref,
                        location.origin,
                        window.history.length,
                        hasSpaHistoryParent
                    )) {
                        // Cannot pop history (fresh tab / reload etc.): still
                        // go to the stored page via SPA and restore its scroll.
                        if (
                            isOrdinaryLeftClick &&
                            window.__EHTW_SPA_READY__ &&
                            typeof window.__EHTW_SPA_NAVIGATE__ === 'function'
                        ) {
                            const targetHref = getBackHref();
                            event.preventDefault();
                            window.__EHTW_SPA_NAVIGATE__(targetHref, {
                                historyMode: 'push',
                                restoreScrollY: getSavedSpaScroll(targetHref)
                            });
                        }
                        return;
                    }

                    event.preventDefault();
                    window.history.back();
                });
                append(back, makeIcon('back'));
                append(header, back);

            }

            function addContextHeading(title, subtitle) {
                addClassSafe(header, 'ehtw-context-header');

                addBackButton();

                const text = create(
                    'div',
                    'ehtw-context-heading'
                );

                append(
                    text,
                    create(
                        'h1',
                        'ehtw-context-title',
                        title
                    )
                );

                if (subtitle) {
                    append(
                        text,
                        create(
                            'div',
                            'ehtw-context-subtitle',
                            subtitle
                        )
                    );
                }

                append(header, text);
            }

            function readResultCount() {
                const source = textOf(qs('.searchtext'));

                if (!source) return '';

                return source
                    .replace(/^Found(?:\s+about)?\s*/i, '')
                    .split('.')[0]
                    .trim();
            }

            function decodeSegment(value) {
                try {
                    return decodeURIComponent(value);
                } catch (_) {
                    return value;
                }
            }

            function getListTitle() {
                if (searchTerm) {
                    return searchTerm;
                }

                const uploaderMatch = pathname.match(
                    /^\/uploader\/(.+)$/i
                );

                if (uploaderMatch) {
                    return decodeSegment(uploaderMatch[1])
                        .replace(/\+/g, ' ');
                }

                const tagMatch = pathname.match(
                    /^\/tag\/(.+)$/i
                );

                if (tagMatch) {
                    const decoded = decodeSegment(
                        tagMatch[1]
                    ).replace(/\+/g, ' ');

                    const bare = decoded.replace(
                        /^[^:]+:/,
                        ''
                    );

                    return '#' + bare
                        .trim()
                        .replace(/\s+/g, '_');
                }

                return 'Search';
            }

            function getGenericTitle() {
                const titles = {
                    '/torrents.php': 'Torrents',
                    '/toplist.php': 'Toplists',
                    '/bounty.php': 'Bounties',
                    '/news.php': 'News',
                    '/favorites.php': 'Favorites',
                    '/home.php': 'My Home',
                    '/uconfig.php': 'Settings',
                    '/mytags': 'My Tags',
                    '/stats.php': 'My Stats',
                    '/hentaiathome.php': 'Hentai@Home',
                    '/bitcoin.php': 'Donations',
                    '/hathperks.php': 'Hath Perks',
                    '/manage': 'My Uploads',
                    '/managefolders': 'Manage Folders',
                    '/managegallery': 'Create New Gallery',
                    '/bounty_post.php': 'Post New Bounty'
                };

                if (titles[pathname]) {
                    return titles[pathname];
                }

                if (pathname === '/exchange.php') {
                    return searchParams.get('t') === 'hath'
                        ? 'Hath Exchange'
                        : 'GP Exchange';
                }

                if (pathname === '/logs.php') {
                    return searchParams.get('t') === 'credits'
                        ? 'Credit Log'
                        : 'Karma Log';
                }

                const fallback = (
                    document.title || ''
                ).split(' - ')[0].trim();

                return fallback || 'Page';
            }

            if (isHomeTabs) {
                addClassSafe(
                    header,
                    'ehtw-tabs-header'
                );

                const tabs = [
                    ['For you', '/', '/', pathname === '/' && !isFollowingFeedPage],
                    ['Watched', '/watched', '/watched', pathname === '/watched']
                ];

                tabs.forEach(([label, href, path, active]) => {
                    const link = create(
                        'a',
                        'ehtw-top-tab'
                    );

                    link.href = href;

                    append(
                        link,
                        create(
                            'span',
                            'ehtw-top-tab-label',
                            label
                        )
                    );

                    if (active) {
                        addClassSafe(
                            link,
                            'ehtw-tab-active'
                        );

                        link.setAttribute(
                            'aria-current',
                            'page'
                        );
                    }

                    append(header, link);
                });
            } else if (isDetail) {
                const commentCount = readCommentCount(document);

                addContextHeading(
                    'Post',
                    commentCount +
                        (commentCount === 1
                            ? ' comment'
                            : ' comments')
                );
            } else if (isGeneric) {
                if (
                    pathname === '/watched' &&
                    !searchTerm
                ) {
                    addClassSafe(
                        header,
                        'ehtw-tabs-header'
                    );

                    const tabs = [
                        ['For you', '/', '/'],
                        ['Watched', '/watched', '/watched']
                    ];

                    tabs.forEach(([label, href, path]) => {
                        const link = create(
                            'a',
                            'ehtw-top-tab'
                        );

                        link.href = href;

                        append(
                            link,
                            create(
                                'span',
                                'ehtw-top-tab-label',
                                label
                            )
                        );

                        if (pathname === path) {
                            addClassSafe(
                                link,
                                'ehtw-tab-active'
                            );

                            link.setAttribute(
                                'aria-current',
                                'page'
                            );
                        }

                        append(header, link);
                    });
                } else {
                    addContextHeading(
                        getGenericTitle(),
                        ''
                    );
                }
            } else {
                addContextHeading(
                    getListTitle(),
                    readResultCount()
                );
            }

            append(center, header);
        }

        // ============================================================
        // 6. 搜索框
        // ============================================================

        function makeSearchToplistPanel() {
            const panel = create('section', 'ehtw-search-toplists');
            const isEhentaiToplistHost = location.origin === 'https://e-hentai.org';
            const isExhentaiToplistHost = location.origin === 'https://exhentai.org';
            if (!isEhentaiToplistHost && !isExhentaiToplistHost) return panel;
            const toplists = [
                ['Yesterday Toplist', '/toplist.php?tl=15'],
                ['Month Toplist', '/toplist.php?tl=13']
            ];
            const sections = toplists.map(([label, href]) => {
                const section = create('section', 'ehtw-search-toplist-section');
                const heading = create('a', 'ehtw-search-toplist-heading', label);
                heading.href = isExhentaiToplistHost
                    ? new URL(href, 'https://e-hentai.org').href
                    : href;
                const status = create('div', 'ehtw-search-toplist-status', 'Loading…');
                const list = create('ol', 'ehtw-search-toplist-list');
                const more = create('details', 'ehtw-search-toplist-more');
                const moreSummary = create('summary');
                const moreList = create('ol', 'ehtw-search-toplist-list ehtw-search-toplist-extra');
                append(more, moreSummary);
                append(more, moreList);
                append(section, heading);
                append(section, status);
                append(section, list);
                append(section, more);
                append(panel, section);
                return { href, status, list, more, moreSummary, moreList };
            });

            const CACHE_KEY = 'ehtw-search-toplist-data-v5';
            const CACHE_TTL = 60 * 60 * 1000;
            const toplistEntryLimit = 50;
            // Retain the last tag filter briefly so a cached Toplist can paint
            // immediately while the fresh My Tags request updates it in the background.
            const cachedHiddenTags = readCachedSearchHiddenTags();
            const hiddenTagsPromise = requestHiddenMyTags()
                .then((tags) => ({ tags, error: null }))
                .catch((error) => ({ tags: null, error }));
            let cache = {};
            try {
                const saved = JSON.parse(localStorage.getItem(CACHE_KEY) || '{}');
                if (saved && typeof saved === 'object') cache = saved;
            } catch (_) {}

            function extractToplistEntries(root, limit = 10) {
                return extractNativeToplistEntries(root, limit, location.href);
            }
            window.__EHTW_EXTRACT_TOPLIST_ENTRIES__ = extractToplistEntries;

            function loadToplistThroughBridge(href) {
                const period = new URL(href, 'https://e-hentai.org').searchParams.get('tl') || '';
                const token = `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
                return new Promise((resolve, reject) => {
                    const frame = document.createElement('iframe');
                    let finished = false;
                    const cleanup = () => {
                        if (finished) return;
                        finished = true;
                        window.clearTimeout(timeout);
                        window.removeEventListener('message', onMessage);
                        frame.remove();
                    };
                    const timeout = window.setTimeout(() => {
                        cleanup();
                        reject(new Error('Toplist bridge timed out'));
                    }, 12000);
                    const onMessage = (event) => {
                        const payload = event.data;
                        if (
                            event.origin !== 'https://e-hentai.org' ||
                            event.source !== frame.contentWindow ||
                            payload?.type !== 'ehtw-toplist-bridge' ||
                            payload.token !== token ||
                            payload.period !== period
                        ) return;
                        cleanup();
                        resolve(Array.isArray(payload.entries) ? payload.entries : []);
                    };
                    addUserScriptListener(window, 'message', onMessage);
                    frame.setAttribute('aria-hidden', 'true');
                    frame.tabIndex = -1;
                    frame.style.cssText = 'position:fixed;width:1px;height:1px;left:-2px;top:-2px;opacity:0;pointer-events:none;border:0;';
                    const bridgeUrl = new URL(href, 'https://e-hentai.org');
                    bridgeUrl.searchParams.set('_ehtw_toplist_bridge', token);
                    frame.src = bridgeUrl.href;
                    document.body.appendChild(frame);
                });
            }

            function areTagSetsEqual(left, right) {
                if (!left || !right || left.size !== right.size) return false;
                for (const tag of left) if (!right.has(tag)) return false;
                return true;
            }

            function renderSection(section, entries, hiddenTags) {
                const { status, list, more, moreSummary, moreList } = section;
                const visibleEntries = filterToplistEntries(entries, hiddenTags);
                const displayEntries = visibleEntries.slice(0, 10);
                status.textContent = visibleEntries.length ? '' : 'No entries found.';
                list.replaceChildren();
                moreList.replaceChildren();
                more.hidden = displayEntries.length <= 5;
                moreSummary.textContent = displayEntries.length > 5
                    ? `Show ${displayEntries.length - 5} more`
                    : '';
                displayEntries.forEach((entry, entryIndex) => {
                    const item = create('li', 'ehtw-search-toplist-item');
                    append(item, create('span', 'ehtw-search-toplist-rank', String(entryIndex + 1)));
                    const cover = create('span', 'ehtw-search-toplist-cover');
                    if (entry.thumbnail) {
                        const image = create('img');
                        image.src = entry.thumbnail;
                        image.alt = '';
                        image.loading = 'lazy';
                        image.decoding = 'async';
                        append(cover, image);
                    }
                    append(item, cover);
                    const link = create('a', 'ehtw-search-toplist-title', entry.title);
                    link.href = isExhentaiToplistHost
                        ? entry.href.replace(/^https:\/\/e-hentai\.org(?=\/)/i, location.origin)
                        : entry.href;
                    append(item, link);
                    append(entryIndex < 5 ? list : moreList, item);
                });
            }

            async function loadSection(section) {
                    const { href, status } = section;
                    const cached = cache[href];
                    const cachedTagsAreComplete = Array.isArray(cached?.entries) &&
                        cached.entries.every((entry) => Array.isArray(entry?.tags));
                    let entries = cached && Date.now() - cached.at < CACHE_TTL &&
                        cachedTagsAreComplete &&
                        (toplistEntryLimit <= 10 || Number(cached.limit || cached.entries?.length || 0) >= toplistEntryLimit)
                        ? cached.entries
                        : null;

                    if (!Array.isArray(entries)) {
                        try {
                            if (isExhentaiToplistHost) {
                                entries = await loadToplistThroughBridge(href);
                            } else {
                                const response = await window.fetch(href, {
                                    credentials: 'same-origin',
                                    cache: 'no-store'
                                });
                                if (!response?.ok) throw new Error('Toplist request failed');
                                const pageText = await response.text();
                                const page = new window.DOMParser()
                                    .parseFromString(pageText, 'text/html');
                                entries = extractToplistEntries(page, toplistEntryLimit);
                            }
                            // 空结果通常意味着站点改了结构或返回了验证页,
                            // 不缓存它,避免榜单一直空白一小时。
                            if (entries.length) {
                                cache[href] = { at: Date.now(), limit: toplistEntryLimit, entries };
                                try {
                                    localStorage.setItem(CACHE_KEY, JSON.stringify(cache));
                                } catch (_) {}
                            }
                        } catch (_) {
                            status.textContent = 'Could not load this list.';
                            return;
                        }
                    }

                    publishToplistEntries(href, entries);
                    if (cachedHiddenTags) {
                        renderSection(section, entries, cachedHiddenTags);
                        hiddenTagsPromise.then((fresh) => {
                            if (!fresh.tags || areTagSetsEqual(cachedHiddenTags, fresh.tags)) return;
                            renderSection(section, entries, fresh.tags);
                        });
                        return;
                    }

                    const hiddenTagState = await hiddenTagsPromise;
                    if (!hiddenTagState.tags) {
                        status.textContent = 'Could not apply hidden tag filters.';
                        return;
                    }
                    renderSection(section, entries, hiddenTagState.tags);
            }

            function load() {
                // Yesterday and Month are independent; let both cache reads or
                // bridge requests run together instead of serializing the sidebar.
                sections.forEach((section) => { loadSection(section); });
            }

            load();
            return panel;
        }

        function buildSearch() {
            const searchbox = qs('#searchbox');
            if (!searchbox) return null;

            right.classList.add('ehtw-toplist-only-sidebar');

            const card = create('section');
            card.id = 'ehtw-search-card';

            const mobile = create('section');
            mobile.id = 'ehtw-mobile-search';

            append(right, card);
            const toplistShortcuts = makeSearchToplistPanel();

            center.insertBefore(
                mobile,
                qs('#ehtw-header')?.nextSibling || null
            );

            move(searchbox, card);
            append(card, toplistShortcuts);

            const form = qs('form', searchbox);
            const input = qs('#f_search', searchbox);

            if (form && input) {
                const wrapper = registerCreatedNode(
                    create('div', 'ehtw-search-input-wrap')
                );

                input.parentNode.insertBefore(wrapper, input);
                move(input, wrapper);

                const icon = registerCreatedNode(
                    makeIcon('search')
                );

                addClassSafe(icon, 'ehtw-search-icon');
                wrapper.insertBefore(icon, input);
            }

            const filters = registerCreatedNode(
                create('button', 'ehtw-filters-toggle')
            );

            filters.type = 'button';

            filters.setAttribute(
                'aria-expanded',
                'false'
            );

            filters.appendChild(makeIcon('filter'));

            append(
                filters,
                create('span', '', 'Filters')
            );

            append(searchbox, filters);

            addClassSafe(
                searchbox,
                'ehtw-filters-collapsed'
            );

            filters.addEventListener('click', () => {
                const expanded = searchbox.classList.contains(
                    'ehtw-filters-open'
                );

                if (expanded) {
                    searchbox.classList.remove(
                        'ehtw-filters-open'
                    );
                } else {
                    addClassSafe(
                        searchbox,
                        'ehtw-filters-open'
                    );
                }

                filters.setAttribute(
                    'aria-expanded',
                    String(!expanded)
                );
            });

            // 原站文件搜索面板仍依赖原有节点和事件。
            // 只搬移,不重建,也不修改其 onclick。
            const fileSearch = qs('#fsdiv');

            if (fileSearch && !searchbox.contains(fileSearch)) {
                addClassSafe(
                    fileSearch,
                    'ehtw-search-extra-panel'
                );

                move(fileSearch, searchbox);
            }

            // 日期范围条原本留在 body > .ido。
            const rangebar = qs('#rangebar');

            if (rangebar && !searchbox.contains(rangebar)) {
                addClassSafe(
                    rangebar,
                    'ehtw-search-extra-panel'
                );

                move(rangebar, searchbox);
            }

            // 搜索页的上方模式切换栏归入 Filters。
            // 原 select、分页链接和原站事件全部保留。
            if (typeof isList !== 'undefined' && isList) {
                const topNav = qsa('.searchnav').find(
                    node =>
                        !node.closest('#ehtw-shell') &&
                        !searchbox.contains(node)
                );

                if (topNav) {
                    addClassSafe(
                        topNav,
                        'ehtw-search-mode-toolbar'
                    );

                    move(topNav, searchbox);
                }
            }

            let currentHost = card;

            function updateSearchPlacement() {
                if (!committed) return;

                const target = window.matchMedia(
                    '(max-width: 999px)'
                ).matches ? mobile : card;

                if (target !== currentHost) {
                    target.appendChild(searchbox);
                    target.appendChild(toplistShortcuts);
                    currentHost = target;
                }
            }

            registerMediaListener(
                window.matchMedia('(max-width: 999px)'),
                updateSearchPlacement
            );

            return updateSearchPlacement;
        }

        // ============================================================
        // 7. Trending 标签统计
        // ============================================================

        function normalizeTag(value) {
            return String(value || '')
                .trim()
                .replace(/^#+/, '')
                .replace(/\s+/g, ' ');
        }

        function collectTags(root = document) {
            const counts = new Map();

            qsa('.gt', root).forEach((tag) => {
                const tagTitle = tag.getAttribute('title') || '';
                if (isFollowingAuthorsPage && !isCreatorTagValue(tagTitle)) {
                    return;
                }

                const raw =
                    textOf(qs('a', tag)) ||
                    textOf(tag) ||
                    tagTitle ||
                    '';

                const name = normalizeTag(raw);

                if (!name) return;

                counts.set(
                    name,
                    (counts.get(name) || 0) + 1
                );
            });

            return Array.from(counts.entries())
                .sort((a, b) => {
                    if (b[1] !== a[1]) {
                        return b[1] - a[1];
                    }

                    return a[0].localeCompare(b[0]);
                })
                .slice(0, 10);
        }

        function buildTrending(tagCounts) {
            if (right?.classList.contains('ehtw-toplist-only-sidebar')) return;
            if (!tagCounts || !tagCounts.length) return;

            const card = create(
                'section',
                'ehtw-side-card ehtw-trending-card'
            );

            append(
                card,
                create(
                    'h2',
                    'ehtw-side-title',
                    "What's happening"
                )
            );

            tagCounts.forEach(([raw, count], index) => {
                const parsed = parseTwitterTag(raw);

                const link = create(
                    'a',
                    'ehtw-trend'
                );

                link.href =
                    '/?f_search=' + encodeURIComponent(raw);

                link.title =
                    count +
                    (count === 1 ? ' gallery' : ' galleries');

                if (index >= 5) {
                    addClassSafe(
                        link,
                        'ehtw-trend-extra'
                    );
                }

                const textColumn = create(
                    'span',
                    'ehtw-trend-text'
                );

                const category =
                    parsed.category === 'Trending in tags'
                        ? 'Trending'
                        : parsed.category + ' · Trending';

                append(
                    textColumn,
                    create(
                        'span',
                        'ehtw-trend-category',
                        category
                    )
                );

                append(
                    textColumn,
                    create(
                        'span',
                        'ehtw-trend-name',
                        '#' + parsed.display
                    )
                );

                append(link, textColumn);

                append(card, link);
            });

            if (tagCounts.length > 5) {
                const moreButton = create(
                    'button',
                    'ehtw-trending-more',
                    'Show more'
                );

                moreButton.type = 'button';

                moreButton.addEventListener(
                    'click',
                    () => {
                        const expanded =
                            card.classList.contains(
                                'ehtw-trending-expanded'
                            );

                        if (expanded) {
                            card.classList.remove(
                                'ehtw-trending-expanded'
                            );

                            moreButton.textContent = 'Show more';
                        } else {
                            addClassSafe(
                                card,
                                'ehtw-trending-expanded'
                            );

                            moreButton.textContent = 'Show less';
                        }
                    }
                );

                append(card, moreButton);
            }

            append(right, card);
        }

        // ============================================================
        // 8. 原生列表分页
        // ============================================================

        function buildPagination(source, target) {
            if (!source || !target) return;

            const wrapper = create(
                'section',
                'ehtw-pagination'
            );

            move(source, wrapper);
            append(target, wrapper);

            return wrapper;
        }

        function findNextListPageUrl(root, baseHref = location.href) {
            let baseUrl;
            try {
                baseUrl = new URL(baseHref, location.href);
            } catch (_) {
                return null;
            }

            const candidates = [];
            const directNext = qs('#dnext, a[rel~="next"]', root);
            if (directNext) candidates.push(directNext);
            candidates.push(...qsa('.searchnav a[href]', root));

            const currentPage = Number.parseInt(
                baseUrl.searchParams.get('page') || '0',
                10
            );
            const numbered = [];
            const labelled = [];
            const seen = new Set();

            candidates.forEach((link) => {
                const href = link.getAttribute('href') || link.href;
                if (!href) return;

                try {
                    const url = new URL(href, baseUrl.href);
                    if (
                        url.origin !== location.origin ||
                        url.href === baseUrl.href ||
                        seen.has(url.href)
                    ) {
                        return;
                    }
                    seen.add(url.href);

                    const page = Number.parseInt(
                        url.searchParams.get('page') || '',
                        10
                    );
                    if (Number.isFinite(page) && page > currentPage) {
                        numbered.push({ page, href: url.href });
                    }

                    const label = [
                        link.getAttribute('aria-label'),
                        link.getAttribute('title'),
                        textOf(link)
                    ].filter(Boolean).join(' ').trim();
                    if (
                        link === directNext ||
                        /\bnext\b|下一页|后一页|^\s*(?:>|»|›|→)\s*$/i.test(label)
                    ) {
                        labelled.push(url.href);
                    }
                } catch (_) {}
            });

            if (labelled.length) return labelled[0];
            numbered.sort((a, b) => a.page - b.page);
            if (numbered.length) return numbered[0].href;
            return null;
        }

        // ============================================================
        // 8.1 画廊分页
        // ============================================================

        function buildGalleryPagination(source, target) {
            if (!source || !target) return null;

            const wrapper = create(
                'section',
                'ehtw-gallery-pagination'
            );

            move(source, wrapper);
            append(target, wrapper);

            return wrapper;
        }

        function findNextGalleryThumbPageUrl(pager, baseHref = location.href) {
            let baseUrl;
            try {
                baseUrl = new URL(baseHref, location.href);
            } catch (_) {
                return null;
            }

            const currentIndex = Number.parseInt(
                baseUrl.searchParams.get('p') || '0',
                10
            );
            const numbered = [];
            const labelled = [];
            qsa('a[href]', pager).forEach((link) => {
                try {
                    const url = new URL(
                        link.getAttribute('href') || link.href,
                        baseUrl.href
                    );
                    if (url.origin !== location.origin || url.href === baseUrl.href) return;

                    const pageIndex = Number.parseInt(
                        url.searchParams.get('p') || '',
                        10
                    );
                    if (Number.isFinite(pageIndex) && pageIndex > currentIndex) {
                        numbered.push({ pageIndex, href: url.href });
                    }

                    const label = [
                        link.getAttribute('aria-label'),
                        link.getAttribute('title'),
                        textOf(link)
                    ].filter(Boolean).join(' ').trim();
                    if (/\bnext\b|下一页|^\s*(?:>|»|›|→)\s*$/i.test(label)) {
                        labelled.push(url.href);
                    }
                } catch (_) {}
            });

            if (labelled.length) return labelled[0];
            numbered.sort((a, b) => a.pageIndex - b.pageIndex);
            return numbered[0]?.href || null;
        }

        function makeHorizontalStripWheelHandler(strip, scope = null) {
            return (event) => {
                let deltaY = Number(event.deltaY) || 0;
                if (!deltaY) {
                    deltaY = -(
                        Number(event.wheelDeltaY) ||
                        Number(event.wheelDelta) ||
                        0
                    );
                }
                if (
                    !strip ||
                    (scope && event.target &&
                        !scope.contains(event.target) &&
                        !event.composedPath?.().includes(scope)) ||
                    event.ctrlKey ||
                    !deltaY ||
                    Math.abs(event.deltaX) > Math.abs(deltaY) * 2
                ) {
                    return false;
                }

                const maxScrollLeft = Math.max(
                    0,
                    strip.scrollWidth - strip.clientWidth
                );
                if (!maxScrollLeft) return false;

                const deltaScale = event.deltaMode === 1
                    ? 40
                    : event.deltaMode === 2
                        ? Math.max(1, strip.clientWidth)
                        : 1;
                const horizontalDelta = deltaY * deltaScale;
                const currentLeft = strip.scrollLeft;
                const atStart = currentLeft <= 0;
                const atEnd = currentLeft >= maxScrollLeft - 1;
                if (
                    (horizontalDelta < 0 && atStart) ||
                    (horizontalDelta > 0 && atEnd)
                ) {
                    return false;
                }

                event.preventDefault();
                strip.scrollLeft = Math.max(
                    0,
                    Math.min(maxScrollLeft, currentLeft + horizontalDelta)
                );
                return true;
            };
        }

        // ============================================================
        // 8.2 页脚
        // ============================================================

        function buildFooter() {
            if (right?.classList.contains('ehtw-toplist-only-sidebar')) return null;
            const footers = qsa('div.dp').filter(
                node => !node.closest('#ehtw-shell')
            );

            if (!footers.length) return null;

            const wrapper = create(
                'div',
                'ehtw-footer'
            );

            footers.forEach(node => {
                move(node, wrapper);
            });

            append(right, wrapper);

            const mobile = create('div');
            mobile.id = 'ehtw-mobile-footer';

            append(center, mobile);

            let currentHost = right;

            function updateFooterPlacement() {
                if (!committed) return;

                const target = window.matchMedia(
                    '(max-width: 999px)'
                ).matches ? mobile : right;

                if (target !== currentHost) {
                    target.appendChild(wrapper);
                    currentHost = target;
                }
            }

            registerMediaListener(
                window.matchMedia('(max-width: 999px)'),
                updateFooterPlacement
            );

            return updateFooterPlacement;
        }

        // ============================================================
        // 9. 列表条目转换
        // ============================================================

        function findGalleryListLink(row) {
            const links = qsa('a[href*="/g/"]', row);
            return links.find((link) => textOf(qs('.glink', link) || link).trim()) || links[0] || null;
        }

        function isGalleryListRow(row) {
            return !!row && (
                row.matches?.('.gl1t') ||
                !!findGalleryListLink(row)
            );
        }

        function getCategoryClass(node) {
            if (!node) return 'ct1';

            const match = Array.from(node.classList)
                .find(
                    name => /^ct(?:[1-9]|a)$/.test(name)
                );

            return match || 'ct1';
        }

        function findUploader(row) {
            const link = qsa('a[href*="/uploader/"]', row)[0] || null;
            const uploaderCell = link?.closest?.('td, .gl4c, .gl5m, .gl4e') || link?.parentElement || null;

            return {
                cell: uploaderCell,
                link,
                name: textOf(
                    link,
                    'Unknown'
                )
            };
        }

        function findPostedNode(row) {
            const candidates = qsa(
                '[id^="posted_"]',
                row
            );

            return candidates.find(
                node => !node.id.startsWith('postedpop_')
            ) || null;
        }

        function findRowTimestamp(row) {
            return textOf(row).match(/\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}/)?.[0] || '';
        }

        function findRating(row) {
            const ratings = qsa('.ir', row);
            return ratings.find((rating) =>
                !rating.closest('.glthumb, .gl1e')
            ) || ratings[0] || null;
        }

        function findDownload(row) {
            return qs('.gldown', row);
        }

        function findPageCount(row) {
            return qsa('td, div, span', row).find((node) =>
                !qs('a', node) && /^\s*\d[\d,]*\s*(?:pages?|页)\s*$/i.test(textOf(node))
            ) || null;
        }

        function extractListTags(link, tagsContainer, creatorTagsOnly = false) {
            if (!link || !tagsContainer) return;

            const tags = qsa(
                '.gt, .gtl, .gtw',
                link
            );

            tags.forEach((tag, index) => {
                const raw = tag.getAttribute('title') || '';
                if (
                    creatorTagsOnly &&
                    !isCreatorTagValue(raw)
                ) {
                    tag.remove();
                    return;
                }

                const anchor = qs('a', tag);
                const sourceText = textOf(anchor || tag);
                const parsed = parseTwitterTag(sourceText);

                addClassSafe(tag, 'ehtw-hashtag');

                if (index >= 6) {
                    addClassSafe(
                        tag,
                        'ehtw-hashtag-overflow'
                    );
                }

                if (anchor) {
                    setTextSafe(
                        anchor,
                        '#' + parsed.display
                    );
                } else {
                    // 未找到 a 时使用可回滚的文本替换。
                    setTextSafe(
                        tag,
                        '#' + parsed.display
                    );
                }

                // 标签原本在画廊链接里,搬出来后需要自己的链接。
                const tagLink = registerCreatedNode(
                    create('a', 'ehtw-tag-link')
                );

                tagLink.href = raw
                    ? '/tag/' + encodeTagPath(raw)
                    : '/?f_search=' +
                        encodeURIComponent(sourceText);

                move(tag, tagLink);
                append(tagsContainer, tagLink);
            });
        }

        function neutralizeHoverHandlers(row) {
            if (!row) return;

            const cell = row.querySelector('td.gl3c, td.gl3m, td.gl3e');
            if (!cell) return;

            for (const name of ['onmouseover', 'onmouseout']) {
                if (!cell.hasAttribute(name)) continue;

                if (!skipTransactionTracking) {
                    modifications.push({
                        type: 'attribute',
                        element: cell,
                        name: name,
                        existed: true,
                        oldValue: cell.getAttribute(name)
                    });
                }

                cell.removeAttribute(name);
            }
        }

        function encodeTagPath(raw) {
            return encodeURI(String(raw).replace(/ /g, '+'))
                .replace(/[?#]/g, (character) => encodeURIComponent(character));
        }

        function parseTwitterTag(raw) {
            const value = String(raw || '')
                .trim()
                .replace(/^#/, '');

            const match = value.match(
                /^([a-z]+):(.+)$/i
            );

            let category = 'Trending in tags';
            let display = value;

            if (match) {
                const namespace = match[1].toLowerCase();
                display = match[2];

                if (namespace === 'f') {
                    category = 'female';
                } else if (namespace === 'm') {
                    category = 'male';
                } else if (namespace === 'x') {
                    category = 'mixed';
                }
            }

            display = display
                .trim()
                .replace(/\s+/g, '_');

            return {
                category,
                display,
                original: value
            };
        }

        function setTextSafe(node, value) {
            if (!node) return;

            // 保存全部原子节点及位置,包括原文本节点。
            // 新文本节点单独登记,以便 rollback 清除。
            const oldChildren = Array.from(node.childNodes);
            const parking = document.createDocumentFragment();

            for (const child of oldChildren) {
                rememberMove(child);
                parking.appendChild(child);
            }

            const textNode = document.createTextNode('');
            textNode.textContent = String(value);

            registerCreatedNode(textNode);
            node.appendChild(textNode);

            // parking 由旧子节点引用维持;rollback 按 moves 逆序
            // 恢复旧子节点,并由 createdNodes 删除新文本节点。
        }

        function formatTwitterTime(value) {
            const match = String(value || '').match(
                /(\d{4})-(\d{2})-(\d{2})\s+(\d{2}):(\d{2})/
            );

            if (!match) return String(value || '');

            const date = new Date(Date.UTC(
                Number(match[1]),
                Number(match[2]) - 1,
                Number(match[3]),
                Number(match[4]),
                Number(match[5])
            ));

            const elapsed = Date.now() -
                (date.getTime() - SITE_UTC_OFFSET_MIN * 60000);

            if (elapsed >= 0 && elapsed < 60000) {
                return 'now';
            }

            if (elapsed >= 0 && elapsed < 3600000) {
                return Math.floor(elapsed / 60000) + 'm';
            }

            if (elapsed >= 0 && elapsed < 86400000) {
                return Math.floor(elapsed / 3600000) + 'h';
            }

            const month = date.toLocaleString(
                'en-US',
                {
                    month: 'short',
                    timeZone: 'UTC'
                }
            );

            const day = date.getUTCDate();

            if (
                date.getUTCFullYear() ===
                new Date().getUTCFullYear()
            ) {
                return month + ' ' + day;
            }

            return month + ' ' + day + ', ' +
                date.getUTCFullYear();
        }

        function formatTwitterFullDate(value) {
            const match = String(value || '').match(
                /(\d{4})-(\d{2})-(\d{2})\s+(\d{2}):(\d{2})/
            );

            if (!match) return String(value || '');

            const date = new Date(Date.UTC(
                Number(match[1]),
                Number(match[2]) - 1,
                Number(match[3]),
                Number(match[4]),
                Number(match[5])
            ));

            return date.toLocaleString(
                'en-US',
                {
                    hour: 'numeric',
                    minute: '2-digit',
                    hour12: true,
                    month: 'short',
                    day: 'numeric',
                    year: 'numeric',
                    timeZone: 'UTC'
                }
            ).replace(',', ' ·');
        }

        function findDetailTimestamp(root) {
            const text = textOf(root);

            const match = text.match(
                /\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}/
            );

            return match ? match[0] : '';
        }

        function readSpriteRating(node) {
            if (!node) return '';

            const background =
                node.style.backgroundPosition ||
                getComputedStyle(node).backgroundPosition ||
                '';

            const values = background.match(
                /(-?\d+(?:\.\d+)?)px\s+(-?\d+(?:\.\d+)?)px/
            );

            if (!values) return '';

            const x = Number(values[1]);
            const y = Number(values[2]);

            const scores = {
                0: 5,
                '-16': 4,
                '-32': 3,
                '-48': 2,
                '-64': 1
            };

            let score = scores[String(x)];

            if (score === undefined) return '';

            if (y === -21) {
                score -= 0.5;
            }

            return String(score);
        }

        function extractPageNumber(node) {
            const text = textOf(node);

            const match = text.match(/[\d,]+/);

            return match ? match[0] : '';
        }

        addSpaPageEventListener(document, 'click', (event) => {
            // Keep the popup open when its trigger or one of its controls is
            // clicked. Menu items close themselves before running actions.
            if (event.target?.closest?.('.ehtw-menu-wrap')) return;
            closeActionMenus();
        });
        addSpaPageEventListener(document, 'keydown', (event) => {
            if (event.key === 'Escape') closeActionMenus();
        });
        addSpaPageEventListener(window, 'wheel', closeActionMenus, {
            passive: true
        });
        addSpaPageEventListener(window, 'touchmove', closeActionMenus, {
            passive: true
        });

        function makeTweetAction(kind, href) {
            const node = href
                ? create('a', 'ehtw-twitter-action')
                : create('div', 'ehtw-twitter-action');

            if (href) {
                node.href = href;
            }

            node.classList.add(
                'ehtw-twitter-action-' + kind
            );

            const icon = create(
                'span',
                'ehtw-twitter-action-icon'
            );

            icon.appendChild(makeIcon(kind));
            append(node, icon);

            return node;
        }

        async function saveGalleryFavoriteCategory(galleryHref, category) {
            try {
                const galleryUrl = new URL(galleryHref, location.href);
                const match = galleryUrl.pathname.match(/^\/g\/(\d+)\/([0-9a-f]+)\/?$/i);
                if (galleryUrl.origin !== location.origin || !match) throw new Error('Invalid gallery URL');
                const popupUrl = new URL('/gallerypopups.php', location.origin);
                popupUrl.searchParams.set('gid', match[1]);
                popupUrl.searchParams.set('t', match[2]);
                popupUrl.searchParams.set('act', 'addfav');
                const popupResponse = await window.fetch(popupUrl.href, {
                    credentials: 'same-origin',
                    cache: 'no-store'
                });
                if (!popupResponse?.ok) throw new Error(`Unable to load favorites (${popupResponse?.status || 'no response'})`);
                const popup = new window.DOMParser().parseFromString(await popupResponse.text(), 'text/html');
                const form = popup.querySelector('#galpop');
                if (!form) throw new Error('The site did not return the favorites form. Please sign in.');
                const result = await submitFavoriteFormLikeBrowser(form, popupUrl, category, '');
                if (!result?.ok) throw new Error(`Unable to save favorite (${result?.status || 'no response'})`);
                const text = await result.text();
                if (/login|sign in|please log in/i.test(text) && !/favorites updated/i.test(text)) {
                    throw new Error('Please sign in before saving a favorite.');
                }
                if (String(category) === '9') followedUploaderEntriesPromiseState.value = null;
                return true;
            } catch (error) {
                throw error;
            }
        }

        function showFavoritePickerError(error) {
            qs('.ehtw-browser-favorite-error')?.remove();
            const message = String(error?.message || 'Unable to save favorite. Please try again.');
            const toast = registerCreatedNode(create('div', 'ehtw-browser-favorite-error', message));
            toast.setAttribute('role', 'alert');
            toast.setAttribute('aria-live', 'assertive');
            setStyleSafe(toast, 'position', 'fixed');
            setStyleSafe(toast, 'top', 'max(12px, env(safe-area-inset-top))');
            setStyleSafe(toast, 'left', '50%');
            setStyleSafe(toast, 'transform', 'translateX(-50%)');
            setStyleSafe(toast, 'z-index', '2147483647');
            setStyleSafe(toast, 'max-width', 'calc(100vw - 24px)');
            setStyleSafe(toast, 'box-sizing', 'border-box');
            setStyleSafe(toast, 'padding', '10px 14px');
            setStyleSafe(toast, 'border', '1px solid rgba(255,255,255,.18)');
            setStyleSafe(toast, 'border-radius', '12px');
            setStyleSafe(toast, 'background', '#b42318');
            setStyleSafe(toast, 'color', '#fff');
            setStyleSafe(toast, 'font', '600 14px/1.4 system-ui, sans-serif');
            setStyleSafe(toast, 'box-shadow', '0 6px 24px rgba(0, 0, 0, .3)');
            document.body.appendChild(toast);
            window.setTimeout(() => toast.remove(), 5000);
        }

        function toggleBrowserFavorite(galleryHref, control) {
            if (!control || control.dataset.ehtwFavoriteToggleSaving === 'true') return;
            if (!control.classList.contains('ehtw-liked')) {
                openBrowserFavoritePicker(galleryHref, control, () => {
                    control.classList.add('ehtw-liked');
                    control.title = 'Added to Favorites';
                });
                return;
            }

            control.dataset.ehtwFavoriteToggleSaving = 'true';
            control.setAttribute('aria-busy', 'true');
            saveGalleryFavoriteCategory(galleryHref, 'favdel')
                .then(() => {
                    control.classList.remove('ehtw-liked');
                    control.title = 'Add to Favorites';
                })
                .catch(showFavoritePickerError)
                .finally(() => {
                    delete control.dataset.ehtwFavoriteToggleSaving;
                    control.removeAttribute('aria-busy');
                });
        }

        function openBrowserFavoritePicker(galleryHref, anchor, onSaved = () => {}) {
            const galleryUrl = new URL(galleryHref, location.href);
            const match = galleryUrl.pathname.match(/^\/g\/(\d+)\/([0-9a-f]+)\/?$/i);
            if (galleryUrl.origin !== location.origin || !match) return;

            const existing = qs('.ehtw-browser-favorite-picker');
            if (existing) {
                existing.focus();
                try { existing.showPicker?.(); } catch (_) { existing.click(); }
                return;
            }

            const select = registerCreatedNode(create('select', 'ehtw-browser-favorite-picker'));
            select.setAttribute('aria-label', 'Choose a favorites folder');
            select.setAttribute('title', 'Choose a favorites folder');
            const placeholder = create('option', '', 'Choose a favorites folder…');
            placeholder.value = '';
            placeholder.selected = true;
            placeholder.disabled = true;
            select.appendChild(placeholder);
            for (let category = 0; category < 10; category += 1) {
                const option = create('option', '', `Favorites ${category}`);
                option.value = String(category);
                select.appendChild(option);
            }

            const bounds = anchor?.getBoundingClientRect?.();
            const left = Math.max(0, Math.min(Number(bounds?.left) || 0, window.innerWidth - 1));
            const top = Math.max(0, Math.min(Number(bounds?.top) || 0, window.innerHeight - 1));
            setStyleSafe(select, 'position', 'fixed');
            setStyleSafe(select, 'left', left + 'px');
            setStyleSafe(select, 'top', top + 'px');
            setStyleSafe(select, 'z-index', '2147483647');
            setStyleSafe(select, 'width', '1px');
            setStyleSafe(select, 'height', '1px');
            setStyleSafe(select, 'min-height', '1px');
            setStyleSafe(select, 'padding', '0');
            setStyleSafe(select, 'border', '0');
            setStyleSafe(select, 'opacity', '0');
            setStyleSafe(select, 'background', 'transparent');
            setStyleSafe(select, 'color', 'transparent');
            setStyleSafe(select, 'box-shadow', 'none');
            document.body.appendChild(select);

            let saving = false;
            let outsideDismissTimer = null;
            const removeOutsideHandler = () => document.removeEventListener('pointerdown', onOutsidePointerDown, true);
            const close = () => {
                if (outsideDismissTimer !== null) window.clearTimeout(outsideDismissTimer);
                removeOutsideHandler();
                select.remove();
            };
            const onOutsidePointerDown = (event) => {
                if (select.contains(event.target)) return;
                const nextFavoriteTrigger = event.target?.closest?.('.ehtw-twitter-action-heart, .ehtw-detail-favorite-action');
                // Mobile browsers may retarget a native picker selection to the
                // document before dispatching <select>'s change event. Keep the
                // focused select alive through that pointer sequence.
                if (document.activeElement === select && !nextFavoriteTrigger) return;
                if (outsideDismissTimer !== null) window.clearTimeout(outsideDismissTimer);
                outsideDismissTimer = window.setTimeout(() => {
                    outsideDismissTimer = null;
                    if (!saving && !select.value) close();
                }, 450);
            };
            addSpaPageEventListener(document, 'pointerdown', onOutsidePointerDown, true);
            select.addEventListener('keydown', (event) => {
                if (event.key === 'Escape') close();
            });
            select.addEventListener('change', async () => {
                const category = select.value;
                if (!category || saving) return;
                if (outsideDismissTimer !== null) {
                    window.clearTimeout(outsideDismissTimer);
                    outsideDismissTimer = null;
                }
                saving = true;
                select.disabled = true;
                select.title = 'Saving favorite…';
                try {
                    await saveGalleryFavoriteCategory(galleryUrl.href, category);
                    onSaved({ folder: category, galleryHref: galleryUrl.href });
                    close();
                } catch (error) {
                    saving = false;
                    close();
                    showFavoritePickerError(error);
                }
            });
            select.focus();
            try { select.showPicker?.(); } catch (_) { select.click(); }
        }

        // 本轮无新增辅助函数。

        // 构建小尺寸灰色头像,不访问外部资源。
        function makeTwitterPlainAvatar(letter, className) {
            const avatar = create(
                'span',
                className,
                String(letter || 'E').charAt(0).toUpperCase()
            );

            return avatar;
        }

        function mountGalleryToplistCovers(root) {
            const group = root?.querySelector?.('.ehtw-gallery-toplists');
            if (!group) return;

            const sections = Array.from(group.children || [])
                .filter((section) => section.matches?.('.tdo'));
            const sectionHref = (section) => {
                const link = section.querySelector?.('p a[href]');
                return getToplistPageKey(link?.getAttribute('href') || link?.href || '');
            };
            const entriesByPeriod = new Map();
            let hiddenTags = null;
            const applyEntries = (href, entries) => {
                entriesByPeriod.set(href, entries);
                if (hiddenTags) {
                    const matching = sections.find((section) => sectionHref(section) === href);
                    if (matching) renderGalleryToplistCovers(matching, entries, hiddenTags);
                }
            };
            const onToplistData = (event) => {
                const detail = event?.detail;
                if (detail?.href && Array.isArray(detail.entries)) {
                    applyEntries(detail.href, detail.entries);
                }
            };
            addSpaPageEventListener(window, 'ehtw-toplist-data', onToplistData);

            const hiddenTagsPromise = requestHiddenMyTags().catch(() => new Set());
            hiddenTagsPromise.then((tags) => {
                hiddenTags = tags;
                entriesByPeriod.forEach((entries, href) => applyEntries(href, entries));
            });

            const shared = window.__EHTW_TOPLIST_ENTRIES__ || {};
            Object.entries(shared).forEach(([href, entries]) => applyEntries(href, entries));

            const CACHE_KEY = 'ehtw-search-toplist-data-v5';
            const CACHE_TTL = 60 * 60 * 1000;
            const requiresFullCandidates = (location.pathname.replace(/\/+$/, '') || '/') === '/toplist.php' && !location.search;
            const hasEnoughCandidates = (href) => {
                const entries = window.__EHTW_TOPLIST_ENTRIES__?.[href] || shared[href];
                return Array.isArray(entries) &&
                    entries.every((entry) => Array.isArray(entry?.tags)) &&
                    (!requiresFullCandidates || entries.length >= 50);
            };
            let saved = {};
            try {
                const parsed = JSON.parse(localStorage.getItem(CACHE_KEY) || '{}');
                if (parsed && typeof parsed === 'object') saved = parsed;
            } catch (_) {}
            Object.entries(saved).forEach(([href, value]) => {
                const key = getToplistPageKey(href);
                if (
                    key &&
                    value &&
                    Date.now() - Number(value.at || 0) < CACHE_TTL &&
                    Array.isArray(value.entries) &&
                    value.entries.every((entry) => Array.isArray(entry?.tags)) &&
                    (!requiresFullCandidates || Number(value.limit || value.entries.length) >= 50)
                ) {
                    publishToplistEntries(key, value.entries);
                }
            });

            const missingLongPeriodSections = sections.filter((section) => {
                const title = String(section.querySelector?.('p')?.textContent || '');
                return /past\s+year|all[\s-]*time/i.test(title) &&
                    !hasEnoughCandidates(sectionHref(section));
            });
            const extractEntries = window.__EHTW_EXTRACT_TOPLIST_ENTRIES__;
            if (typeof extractEntries !== 'function' || !missingLongPeriodSections.length) return;

            (async () => {
                for (let index = 0; index < missingLongPeriodSections.length; index += 1) {
                    const href = sectionHref(missingLongPeriodSections[index]);
                    if (!href || hasEnoughCandidates(href)) continue;
                    try {
                        const response = await window.fetch(href, {
                            credentials: 'same-origin',
                            cache: 'default'
                        });
                        if (!response?.ok) continue;
                        const page = new window.DOMParser().parseFromString(
                            await response.text(),
                            'text/html'
                        );
                        const entries = extractEntries(page, 50);
                        if (!entries.length) continue;
                        saved[href] = { at: Date.now(), limit: 50, entries };
                        try {
                            localStorage.setItem(CACHE_KEY, JSON.stringify(saved));
                        } catch (_) {}
                        publishToplistEntries(href, entries);
                    } catch (_) {}
                    if (index < missingLongPeriodSections.length - 1) {
                        await new Promise((resolve) => window.setTimeout(resolve, 400));
                    }
                }
            })();
        }

        function transformGenericPage() {
            if (typeof isLogin !== 'undefined' && isLogin) {
                return;
            }

            const content = create(
                'div',
                'ehtw-generic-content'
            );

            append(center, content);

            const pathname =
                location.pathname.replace(/\/+$/, '') || '/';

            function canMove(node) {
                if (
                    !node ||
                    node.nodeType !== Node.ELEMENT_NODE
                ) {
                    return false;
                }

                if (node.matches(
                    'script, style, link, ins, ' +
                    '#ehtw-shell, #nb, .dp, template'
                )) {
                    return false;
                }

                if (
                    node === shell ||
                    node.contains(shell) ||
                    node.closest('#ehtw-shell') ||
                    node.closest('#nb')
                ) {
                    return false;
                }

                try {
                    const computed = getComputedStyle(node);

                    if (
                        computed.display === 'none' ||
                        computed.visibility === 'hidden'
                    ) {
                        return false;
                    }
                } catch (_) {}

                return true;
            }

            const originals = Array.from(
                document.body.children
            );

            originals.forEach(node => {
                if (!canMove(node)) return;

                move(node, content);
            });

            if (pathname === '/toplist.php') {
                reorderGalleryToplistSections(content);
            }

            if (pathname === '/watched') {
                const rawText = textOf(content);

                if (
                    /You do not have any watched tags|No hits found/i
                        .test(rawText)
                ) {
                    addClassSafe(
                        content,
                        'ehtw-generic-empty'
                    );
                }
            }

            // 通用页面没有原站搜索框时,右栏使用独立 GET 表单。
            // 不访问或修改任何登录表单。
            if (!qs('#searchbox')) {
                ensureRightSearchForm();
            }

            if (pathname === '/toplist.php') {
                mountGalleryToplistCovers(content);
            }
        }

        function ensureRightSearchForm() {
            if (!right) return null;

            right.classList.add('ehtw-toplist-only-sidebar');

            const existing = qs(
                '.ehtw-right-search-form',
                right
            );

            if (existing) return existing;

            // 新建表单不使用 #f_search,避免与原站搜索控件冲突。
            const host = registerCreatedNode(
                create('section', 'ehtw-right-search')
            );

            const form = registerCreatedNode(
                create('form', 'ehtw-right-search-form')
            );

            form.method = 'GET';
            form.action = '/';
            form.setAttribute('role', 'search');

            const wrapper = create(
                'div',
                'ehtw-right-search-wrap'
            );

            const icon = makeIcon('search');
            addClassSafe(icon, 'ehtw-right-search-icon');

            const input = create('input');

            addClassSafe(
                input,
                'ehtw-right-search-input'
            );

            input.type = 'search';
            input.name = 'f_search';
            input.placeholder = 'Search';
            input.setAttribute(
                'aria-label',
                'Search galleries'
            );

            append(wrapper, icon);
            append(wrapper, input);
            append(form, wrapper);
            append(host, form);
            append(host, makeSearchToplistPanel());

            // 放在右栏第一个元素之前,不影响 Trending、
            // Who to follow 和页脚的原始节点。
            right.insertBefore(
                host,
                right.firstChild
            );

            return form;
        }

        // 作者优先取 artist 标签,其次 group 标签,最后取标题开头的方括号。
        // 来源、语言这类方括号不是作者;“X - 标题”常是角色名,不猜。
        function parseTitleAuthor(title) {
            const match = String(title || '')
                .trim()
                .match(/^(?:\([^)]*\)\s*)?\[([^\]]+)\]/);

            if (!match) return '';

            const inner = match[1].trim();
            const nested = inner.match(/\(([^()]+)\)\s*$/);
            const name = (nested ? nested[1] : inner).trim();

            return /^(patreon|pixiv|fanbox|fantia|twitter|x|skeb|gumroad|subscribestar|ko-?fi|dlsite|fanza|request|commission|english|chinese|japanese|korean|ai generated|decensored|digital)$/i
                .test(name)
                ? ''
                : name;
        }

        function pickAuthor(rawTags, title) {
            const fromTitle = parseTitleAuthor(title);

            const raw =
                rawTags.find((tag) => /^artist:/i.test(tag)) ||
                rawTags.find((tag) => /^group:/i.test(tag));

            if (raw) {
                const name = raw.replace(/^[a-z]+:/i, '');

                return {
                    // 标题里的写法和标签相同时,用标题的大小写。
                    name: fromTitle.toLowerCase() === name.toLowerCase()
                        ? fromTitle
                        : name,
                    href: '/tag/' + encodeTagPath(raw)
                };
            }

            return fromTitle
                ? {
                    name: fromTitle,
                    href: '/?f_search=' + encodeURIComponent(
                        '"' + fromTitle + '"'
                    )
                }
                : null;
        }

        function readPageAuthorTags(root) {
            return Array.from(
                root.querySelectorAll(
                    'a[id^="ta_artist:"], a[id^="ta_group:"]'
                )
            ).map((link) =>
                link.id.replace(/^ta_/, '').replace(/_/g, ' ')
            );
        }

        function showAuthorName(node, author) {
            const link = create('a', '', author.name);

            link.href = author.href;
            node.textContent = '';
            append(node, link);
        }

        // 头像字母跟随作者名;颜色仍是分类色。
        function syncAvatarLetter(root) {
            const letter = qs('.ehtw-avatar-letter', root);
            const name = qs('.ehtw-author-name a', root);

            if (letter && name) {
                letter.textContent = (
                    Array.from(name.textContent.trim())[0] || '?'
                ).toUpperCase();
            }
        }

        function formatHandle(name) {
            return '@' + name.replace(/\s+/g, '_');
        }

        // 右上角三个点的菜单。用 fixed 定位,避免被卡片裁切。
        function closeActionMenus() {
            const openMenus = document.getElementsByClassName('ehtw-menu-open');
            if (!openMenus.length) return;

            Array.from(openMenus).forEach((node) => {
                node.classList.remove('ehtw-menu-open');
            });
        }

        function makeActionMenu() {
            const wrap = create('span', 'ehtw-menu-wrap');
            const button = create('button', 'ehtw-menu-button');
            const panel = create('div', 'ehtw-menu');

            button.type = 'button';
            button.title = 'More';
            button.setAttribute('aria-label', 'More');
            button.appendChild(makeIcon('more'));

            button.addEventListener('click', (event) => {
                event.preventDefault();
                event.stopPropagation();

                const wasOpen = wrap.classList.contains('ehtw-menu-open');

                closeActionMenus();

                if (wasOpen) return;

                wrap.classList.add('ehtw-menu-open');

                const box = button.getBoundingClientRect();
                const height = panel.getBoundingClientRect().height;
                const below = box.bottom + 4 + height <
                    window.innerHeight - 8;

                panel.style.right =
                    Math.max(
                        8,
                        document.documentElement.clientWidth - box.right
                    ) + 'px';
                panel.style.top = below
                    ? box.bottom + 4 + 'px'
                    : Math.max(8, box.top - height - 4) + 'px';
            });

            append(wrap, button);
            append(wrap, panel);

            return { wrap, panel };
        }

        function addMenuItem(panel, label, action) {
            const item = create('button', 'ehtw-menu-item', label);

            item.type = 'button';
            item.addEventListener('click', () => {
                closeActionMenus();
                action();
            });

            append(panel, item);
        }

        // 收藏数和评论数只存在于画廊页,官方 API 也不提供。
        function readFavoriteCount(text) {
            const source = String(text || '');
            const digits = source.match(/\d[\d,]*/);

            if (digits) {
                return Number(digits[0].replace(/,/g, ''));
            }

            if (/\bonce\b/i.test(source)) return 1;
            if (/\bnever\b/i.test(source)) return 0;

            return null;
        }

        function readCommentCount(root) {
            const shown = root.querySelectorAll('.c1').length;
            const hidden = (
                root.querySelector('#chd')?.textContent || ''
            ).match(/there (?:is|are) (\d+) more comments?/i);

            return shown + (hidden ? Number(hidden[1]) : 0);
        }

        function formatCount(value) {
            if (value < 1000) return String(value);

            const units = value >= 1000000
                ? [1000000, 'M']
                : [1000, 'K'];

            return (value / units[0])
                .toFixed(1)
                .replace(/\.0$/, '') + units[1];
        }

        // 列表卡片的菜单取画廊页的操作链接;只留原站同域、能直接打开的。
        function readGalleryActions(root) {
            return Array.from(root.querySelectorAll('#gd5 a'))
                .map((link) => {
                    const popup = (
                        link.getAttribute('onclick') || ''
                    ).match(/popUp\('([^']+)'/);
                    const href = link.getAttribute('href') || '';
                    const url = popup ? popup[1] : href;

                    return /^https:\/\/e-hentai\.org\//.test(url)
                        ? [link.textContent.trim(), url]
                        : null;
                })
                .filter(Boolean);
        }

        // 一次只请求一页,只请求接近可视区的推文,结果缓存到本地。
        // 一旦遇到限流提示就停止,并在一段时间内不再请求。
        function startGalleryStatsLoader() {
            const CACHE_KEY = isFollowingAuthorsPage
                ? 'ehtw-gallery-following-author-stats-v1'
                : 'ehtw-gallery-stats-v6';
            const CACHE_TTL = 6 * 60 * 60 * 1000;
            const CACHE_LIMIT = 800;
            const REQUEST_GAP = 1500;
            const BLOCK_TIME = 30 * 60 * 1000;

            const tweets = qsa('[data-ehtw-href]');
            const followedAuthorTags = new Set();
            const followedUploaderNames = new Set();
            let followedAuthorTagsLoadFailed = false;
            const followedUploaderNamesPromise = requestWatchedUploaderNames()
                .then((entries) => entries.forEach((entry) => followedUploaderNames.add(entry.key)))
                .catch(() => {});

            function updateCardModeration(tweet, stats) {
                buildCardModerationActions(
                    tweet,
                    Array.isArray(stats.authorTags) && stats.authorTags.length
                        ? stats.authorTags
                        : tweet._ehtwListArtistTags,
                    stats.author || tweet._ehtwListAuthor,
                    stats.uploader || tweet._ehtwListUploader,
                    followedAuthorTags,
                    followedUploaderNames,
                    stats.uploaderHref || tweet._ehtwListUploaderHref || ''
                );
            }

            function setFollowingCardVisibility(tweet, visible) {
                tweet.hidden = !visible;
                tweet.classList.toggle('ehtw-following-hidden', !visible);
                if (visible) {
                    tweet.style.removeProperty('display');
                } else {
                    tweet.style.setProperty('display', 'none', 'important');
                }
            }

            function getCardCreatorTags(tweet, stats) {
                const sourceTag = normalizeCreatorFollowTag(
                    tweet._ehtwFollowingSourceTag || ''
                );
                if (isFollowingAuthorsPage && sourceTag) return [sourceTag];

                const tags = qsa('.ehtw-tags a[href]', tweet)
                    .map((link) => {
                        try {
                            const url = new URL(
                                link.getAttribute('href') || link.href,
                                location.href
                            );
                            const match = url.pathname.match(
                                /^\/tag\/((?:artist|group):[^/]+)$/i
                            );
                            return match ? decodeURIComponent(match[1]) : '';
                        } catch (_) {
                            return '';
                        }
                    })
                    .filter(Boolean);

                const sourceTags = tags.length
                    ? tags
                    : Array.isArray(stats.authorTags)
                        ? stats.authorTags
                        : [];

                return Array.from(new Set(
                    sourceTags
                        .filter((tag) => /^(?:artist|group):/i.test(tag))
                        .map(normalizeCreatorFollowTag)
                        .filter(Boolean)
                ));
            }

            const followedAuthorTagsPromise = (async () => {
                try {
                    (await requestWatchedCreatorTags()).forEach((tag) => {
                        followedAuthorTags.add(tag);
                    });
                } catch (_) {
                    followedAuthorTagsLoadFailed = true;
                }

                if (isFollowingAuthorsPage) {
                    const status = qs('#ehtw-following-status');
                    if (status) {
                        status.textContent = followedAuthorTagsLoadFailed
                            ? 'Could not read followed author tags. Open My Tags and try again.'
                            : followedAuthorTags.size
                                ? `Showing galleries from ${followedAuthorTags.size} artist/group tags watched in My Tags.`
                                : 'No artist or group tags are marked Watched. Use “关注” on a gallery card to add one in My Tags.';
                    }

                    if (!followedAuthorTags.size) {
                        tweets.forEach((tweet) => {
                            setFollowingCardVisibility(tweet, false);
                            tweet.classList.remove('ehtw-following-checking');
                        });
                    }
                }
            })();

            let cache = { blockedUntil: 0, items: {} };

            try {
                const saved = JSON.parse(
                    localStorage.getItem(CACHE_KEY) || 'null'
                );

                if (saved && saved.items) cache = saved;
            } catch (_) {}

            function saveCache() {
                try {
                    const keys = Object.keys(cache.items);

                    if (keys.length > CACHE_LIMIT) {
                        keys
                            .sort((a, b) =>
                                cache.items[a].at - cache.items[b].at
                            )
                            .slice(0, keys.length - CACHE_LIMIT)
                            .forEach((key) => {
                                delete cache.items[key];
                            });
                    }

                    localStorage.setItem(
                        CACHE_KEY,
                        JSON.stringify(cache)
                    );
                } catch (_) {}
            }

            Promise.all([followedAuthorTagsPromise, followedUploaderNamesPromise]).then(() => {
                tweets.forEach((tweet) => {
                    const href = tweet.getAttribute('data-ehtw-href');
                    const saved = href ? cache.items[href] : null;
                    updateCardModeration(tweet, saved || {
                        authorTags: tweet._ehtwListArtistTags || [],
                        author: tweet._ehtwListAuthor,
                        uploader: tweet._ehtwListUploader
                    });
                });
            });

            function showThumbnailMeta(tweet, stats) {
                const name = qs('.ehtw-author-name', tweet);

                if (name && stats.author) {
                    showAuthorName(name, stats.author);
                    syncAvatarLetter(tweet);
                }

                if (!tweet.classList.contains('ehtw-tweet-pending')) {
                    return;
                }

                tweet.classList.remove('ehtw-tweet-pending');

                const handle = qs('.ehtw-handle', tweet);

                if (stats.uploader) {
                    const uploader = {
                        name: stats.uploader,
                        href: stats.uploaderHref
                    };

                    if (name && !qs('a', name)) {
                        showAuthorName(name, uploader);
                        syncAvatarLetter(tweet);
                    }

                    if (handle) {
                        showAuthorName(handle, {
                            name: formatHandle(stats.uploader),
                            href: stats.uploaderHref
                        });
                    }
                }

                const tags = qs('.ehtw-tags', tweet);

                if (!tags || tags.children.length) return;

                const visibleTags = isFollowingAuthorsPage
                    ? stats.tags.filter(([label, href]) =>
                        isCreatorTagValue(label) || isCreatorTagValue(href)
                    )
                    : stats.tags;
                visibleTags.forEach(([label, href]) => {
                    const tag = create(
                        'a',
                        'gt ehtw-hashtag',
                        '#' + parseTwitterTag(label).display
                    );

                    tag.href = href;
                    append(tags, tag);
                });
            }

            function showTweetMenu(tweet, stats) {
                const author = qs('.ehtw-tweet-author', tweet);
                if (!author) return;

                let menuWrap = qs('.ehtw-card-more-menu', author);
                if (!menuWrap && (!stats.actions || !stats.actions.length)) return;

                let menuPanel = qs('.ehtw-menu', menuWrap);
                if (!menuWrap) {
                    const menu = makeActionMenu();
                    menuWrap = menu.wrap;
                    menuWrap.classList.add('ehtw-card-more-menu');
                    menuPanel = menu.panel;
                    append(author, menuWrap);
                }

                if (menuPanel.dataset.ehtwGalleryActionsLoaded) return;
                (stats.actions || []).forEach(([label, url]) => {
                    const item = create('a', 'ehtw-menu-item', label);
                    item.href = url;
                    item.target = '_blank';
                    item.rel = 'noopener noreferrer';
                    append(menuPanel, item);
                });
                menuPanel.dataset.ehtwGalleryActionsLoaded = 'true';
            }

            function showStats(tweet, stats) {
                showThumbnailMeta(tweet, stats);
                showTweetMenu(tweet, stats);
                updateCardModeration(tweet, stats);

                if (isFollowingAuthorsPage) {
                    const cardCreatorTags = getCardCreatorTags(tweet, stats);
                    const matchesFollowedAuthor =
                        !followedAuthorTagsLoadFailed &&
                        cardCreatorTags.some((tag) =>
                            followedAuthorTags.has(tag)
                        );
                    setFollowingCardVisibility(tweet, matchesFollowedAuthor);
                    tweet.classList.remove('ehtw-following-checking');
                }

                const comments = qs('.ehtw-stat-comments', tweet);
                const favorites = qs('.ehtw-stat-favorites', tweet);

                if (comments) {
                    comments.textContent = formatCount(stats.comments);
                    comments.title =
                        stats.comments.toLocaleString() + ' comments';
                }

                if (favorites && stats.favorites !== null) {
                    favorites.textContent = formatCount(stats.favorites);
                    favorites.title =
                        stats.favorites.toLocaleString() + ' favorites';
                }
            }

            // 只保留接近可视区的推文,每次取离屏幕中心最近的一条。
            const near = new Set();
            const failures = new Map();
            let running = false;
            let stopped = false;
            let observer = null;

            function block() {
                stopped = true;
                near.clear();
                cache.blockedUntil = Date.now() + BLOCK_TIME;
                saveCache();
                console.warn(
                    '[EH Twitter Style] Stats paused: rate limited'
                );
            }

            function isRateLimited(response, text) {
                if (
                    response.status === 429 ||
                    response.status === 403
                ) {
                    return true;
                }

                // 限流页很短,也没有画廊结构;标题里的词不算。
                return (
                    text.length < 3000 &&
                    !text.includes('id="favcount"') &&
                    /temporarily banned|too fast|excessive/i.test(text)
                );
            }

            async function fetchPage(url) {
                const response = await fetch(url, {
                    credentials: 'same-origin'
                });

                return {
                    response,
                    text: await response.text()
                };
            }

            function wait(ms) {
                return new Promise((resolve) => {
                    setTimeout(resolve, ms);
                });
            }

            function finish(tweet) {
                failures.delete(tweet);

                if (observer) observer.unobserve(tweet);
            }

            async function loadOne(tweet) {
                if (isFollowingAuthorsPage) {
                    await followedAuthorTagsPromise;
                    if (!followedAuthorTags.size) {
                        setFollowingCardVisibility(tweet, false);
                        finish(tweet);
                        return;
                    }
                }

                const href = tweet.getAttribute('data-ehtw-href');

                const { response, text } = await fetchPage(href);

                if (isRateLimited(response, text)) {
                    block();
                    return;
                }

                // 内容警告页不代替用户点击 View Gallery,跳过该条统计。
                if (
                    !text.includes('id="favcount"') &&
                    /Content Warning/i.test(text)
                ) {
                    finish(tweet);
                    return;
                }

                // DOMParser 的文档是惰性的,不执行脚本,也不加载图片。
                const page = new DOMParser().parseFromString(
                    text,
                    'text/html'
                );

                const favcount = page.querySelector('#favcount');

                if (!favcount) {
                    finish(tweet);
                    return;
                }

                const uploader = page.querySelector('#gdn a');

                const stats = {
                    comments: readCommentCount(page),
                    favorites: readFavoriteCount(favcount.textContent),
                    uploader: uploader
                        ? uploader.textContent.trim()
                        : '',
                    uploaderHref: uploader
                        ? uploader.getAttribute('href')
                        : '',
                    tags: Array.from(
                        page.querySelectorAll('#taglist a')
                    )
                        .filter((tag) => !isFollowingAuthorsPage ||
                            isCreatorTagValue(tag.getAttribute('title')) ||
                            isCreatorTagValue(tag.id) ||
                            isCreatorTagValue(tag.getAttribute('href'))
                        )
                        .slice(0, 6)
                        .map((tag) => [
                            tag.textContent.trim(),
                            tag.getAttribute('href')
                        ]),
                    actions: readGalleryActions(page),
                    author: pickAuthor(
                        readPageAuthorTags(page),
                        page.querySelector('#gj')?.textContent ||
                            page.querySelector('#gn')?.textContent
                    ),
                    authorTags: readPageAuthorTags(page).filter((tag) =>
                        /^(?:artist|group):/i.test(tag)
                    ),
                    at: Date.now()
                };

                cache.items[href] = stats;
                saveCache();
                showStats(tweet, stats);
                finish(tweet);
            }

            function nextTweet() {
                const middle = window.innerHeight / 2;
                let best = null;
                let bestDistance = Infinity;

                near.forEach((tweet) => {
                    const box = tweet.getBoundingClientRect();
                    const distance = Math.abs(
                        box.top + box.height / 2 - middle
                    );

                    if (distance < bestDistance) {
                        best = tweet;
                        bestDistance = distance;
                    }
                });

                return best;
            }

            async function pump() {
                if (running) return;

                running = true;

                while (!stopped) {
                    if (document.hidden) {
                        await new Promise((resolve) => {
                            addSpaPageEventListener(
                                document,
                                'visibilitychange',
                                resolve,
                                { once: true }
                            );
                        });
                        continue;
                    }

                    const tweet = nextTweet();

                    if (!tweet) break;

                    near.delete(tweet);

                    let delay = REQUEST_GAP;

                    try {
                        await loadOne(tweet);
                    } catch (_) {
                        // 切后台、断网都会到这里,只重试这一条,不算限流。
                        const count = (failures.get(tweet) || 0) + 1;

                        failures.set(tweet, count);
                        delay = REQUEST_GAP * (count + 1);

                        if (count < 3) {
                            near.add(tweet);
                        } else {
                            finish(tweet);
                        }
                    }

                    await wait(delay);
                }

                running = false;
            }

            const pending = [];

            tweets.forEach((tweet) => {
                const saved = cache.items[
                    tweet.getAttribute('data-ehtw-href')
                ];

                if (
                    saved &&
                    Date.now() - saved.at < CACHE_TTL &&
                    (!isFollowingAuthorsPage || Array.isArray(saved.authorTags))
                ) {
                    if (isFollowingAuthorsPage) {
                        followedAuthorTagsPromise.then(() => showStats(tweet, saved));
                    } else {
                        showStats(tweet, saved);
                    }
                } else {
                    pending.push(tweet);
                }
            });

            const ensureObserver = () => {
                if (
                    observer ||
                    stopped ||
                    Date.now() < cache.blockedUntil ||
                    typeof IntersectionObserver !== 'function'
                ) {
                    return;
                }

                observer = new IntersectionObserver((entries) => {
                    entries.forEach((entry) => {
                        if (entry.isIntersecting) {
                            near.add(entry.target);
                        } else {
                            near.delete(entry.target);
                        }
                    });

                    pump();
                }, { rootMargin: '300px 0px' });
            };

            ensureObserver();
            pending.forEach((tweet) => observer?.observe(tweet));

            return (additionalTweets) => {
                if (!Array.isArray(additionalTweets) || stopped) return;
                ensureObserver();
                additionalTweets.forEach((tweet) => {
                    const href = tweet.getAttribute('data-ehtw-href');
                    const saved = href ? cache.items[href] : null;
                    if (saved && Date.now() - saved.at < CACHE_TTL) {
                        showStats(tweet, saved);
                    } else {
                        observer?.observe(tweet);
                    }
                });
            };
        }

        function buildCardModerationActions(
            tweet,
            rawCreatorTags,
            author,
            uploaderName,
            followedAuthorTags = new Set(),
            followedUploaderNames = new Set(),
            uploaderHref = ''
        ) {
            const authorRow = qs('.ehtw-tweet-author', tweet);
            if (!authorRow) return;

            const creatorTags = Array.from(new Set(
                (Array.isArray(rawCreatorTags) ? rawCreatorTags : [])
                    .filter((tag) => /^(?:artist|group):/i.test(tag))
                    .map((tag) => String(tag).trim())
                    .filter(Boolean)
            ));

            // 仅把 artist/group 视为可关注作者标签,不会从语言、类别等标签推断。
            if (!creatorTags.length && author?.name) {
                const authorHref = String(author.href || '');
                creatorTags.push(
                    /\/tag\/group:/i.test(authorHref)
                        ? `group:${author.name}`
                        : `artist:${author.name}`
                );
            }

            qs('.ehtw-follow-choice-wrap', authorRow)?.remove();
            qsa('.ehtw-card-follow-button:not(.ehtw-menu-button)', authorRow)
                .forEach((button) => button.remove());

            const uniqueTags = Array.from(new Set(creatorTags));
            const followOne = (tag, onUpdated = () => {}) => {
                toggleWatchedTagInMyTags(tag, (success, isWatched) => {
                    const normalized = normalizeCreatorFollowTag(tag);
                    if (success && isWatched) followedAuthorTags.add(normalized);
                    else if (success) followedAuthorTags.delete(normalized);
                    onUpdated(success, isWatched);
                });
            };

            const moreMenu = () => {
                let wrap = qs('.ehtw-card-more-menu', authorRow);
                if (!wrap) {
                    const menu = makeActionMenu();
                    wrap = menu.wrap;
                    wrap.classList.add('ehtw-card-more-menu');
                    authorRow.appendChild(wrap);
                }
                return {
                    wrap,
                    panel: qs('.ehtw-menu', wrap)
                };
            };

            if (uniqueTags.length === 1) {
                const tag = uniqueTags[0];
                const normalized = normalizeCreatorFollowTag(tag);
                const alreadyFollowed = followedAuthorTags.has(normalized);
                const button = create(
                    'button',
                    'ehtw-card-follow-button',
                    alreadyFollowed ? '取消关注' : '关注'
                );
                button.type = 'button';
                button.setAttribute(
                    'aria-label',
                    `${alreadyFollowed ? '取消关注' : '关注'}${tag.startsWith('group:') ? ' Group' : ' Artist'} ${tag.replace(/^(?:artist|group):/i, '')}`
                );
                button.setAttribute('aria-pressed', String(alreadyFollowed));
                if (!(rawCreatorTags || []).some((item) => /^(?:artist|group):/i.test(item))) {
                    button.title = `按标题识别的作者名创建 Watched 标签:${tag}`;
                }
                button.addEventListener('click', (event) => {
                    event.preventDefault();
                    event.stopPropagation();
                    button.disabled = true;
                    button.textContent = '保存中…';
                    followOne(tag, (success, isWatched) => {
                        if (!success) {
                            button.disabled = false;
                            button.textContent = followedAuthorTags.has(normalized) ? '取消关注' : '关注';
                            return;
                        }
                        button.disabled = false;
                        button.textContent = isWatched ? '取消关注' : '关注';
                        button.setAttribute('aria-pressed', String(isWatched));
                    });
                });

                const existingMore = qs('.ehtw-card-more-menu', authorRow);
                if (existingMore) authorRow.insertBefore(button, existingMore);
                else authorRow.appendChild(button);
            } else if (uniqueTags.length > 1) {
                const menu = makeActionMenu();
                menu.wrap.classList.add('ehtw-follow-choice-wrap');
                const choiceButton = qs('.ehtw-menu-button', menu.wrap);
                const updateChoiceLabel = () => {
                    choiceButton.textContent = uniqueTags.some((tag) =>
                        followedAuthorTags.has(normalizeCreatorFollowTag(tag))
                    ) ? '已关注 ▾' : '关注 ▾';
                };
                updateChoiceLabel();
                choiceButton.classList.add(
                    'ehtw-card-follow-button',
                    'ehtw-follow-choice-button'
                );
                choiceButton.title = '选择关注 Artist 或 Group';
                choiceButton.setAttribute('aria-label', '选择关注 Artist 或 Group');

                uniqueTags.forEach((tag) => {
                    const type = tag.startsWith('group:') ? 'Group' : 'Artist';
                    const normalized = normalizeCreatorFollowTag(tag);
                    const alreadyFollowed = followedAuthorTags.has(normalized);
                    addMenuItem(
                        menu.panel,
                        `${alreadyFollowed ? '取消关注' : '关注'} ${type} · ${tag.replace(/^(?:artist|group):/i, '')}`,
                        () => followOne(tag, () => updateChoiceLabel())
                    );
                });

                const existingMore = qs('.ehtw-card-more-menu', authorRow);
                if (existingMore) authorRow.insertBefore(menu.wrap, existingMore);
                else authorRow.appendChild(menu.wrap);
            }

            const uploader = String(uploaderName || '').trim();
            if (uploader && uploader.toLowerCase() !== 'unknown') {
                const { panel: menuPanel } = moreMenu();

                const uploaderKey = normalizeUploaderName(uploader);
                let followUploaderItem = qs('.ehtw-menu-follow-uploader', menuPanel);
                if (!followUploaderItem) {
                    followUploaderItem = create(
                        'button',
                        'ehtw-menu-item ehtw-menu-follow-uploader'
                    );
                    followUploaderItem.type = 'button';
                    append(menuPanel, followUploaderItem);
                }
                const updateUploaderFollowLabel = () => {
                    const isFollowed = followedUploaderNames.has(uploaderKey);
                    followUploaderItem.textContent = isFollowed
                        ? `取消关注上传者 ${uploader}`
                        : `关注上传者 ${uploader}`;
                    followUploaderItem.setAttribute('aria-pressed', String(isFollowed));
                };
                updateUploaderFollowLabel();
                followUploaderItem.onclick = (event) => {
                    event.preventDefault();
                    event.stopPropagation();
                    followUploaderItem.disabled = true;
                    followUploaderItem.textContent = '保存中…';
                    followUploaderViaFavorites(
                        uploader,
                        tweet.getAttribute('data-ehtw-href'),
                        (success, isWatched, reason) => {
                        if (success && isWatched) followedUploaderNames.add(uploaderKey);
                        else if (success) followedUploaderNames.delete(uploaderKey);
                        followUploaderItem.disabled = false;
                        updateUploaderFollowLabel();
                        if (!success && reason) {
                            followUploaderItem.title = reason;
                        }
                    });
                };

                let blockItem = qs('.ehtw-menu-block-uploader', menuPanel);
                if (!blockItem) {
                    blockItem = create(
                        'button',
                        'ehtw-menu-item ehtw-menu-block-uploader'
                    );
                    blockItem.type = 'button';
                    append(menuPanel, blockItem);
                }
                blockItem.textContent = `屏蔽上传者 ${uploader}`;
                blockItem.setAttribute(
                    'aria-label',
                    `在 E-Hentai 原生设置中屏蔽上传者 ${uploader}`
                );
                blockItem.onclick = () => {
                    closeActionMenus();
                    const target = new URL('/uconfig.php', location.origin);
                    target.hash = `ehtw_block_uploader=${encodeURIComponent(uploader)}`;
                    navigatePage(target.href);
                };
            }
        }

        function createTweet(row) {
            // Thumbnail 模式的单元格没有上传者和标签,
            // 这两项由 startGalleryStatsLoader 从画廊页补齐。
            const isThumbnail = !!row && row.matches('.gl1t');
            const galleryLink = findGalleryListLink(row);
            if (!row || !galleryLink) return null;

            const categoryNode = qs(
                '.gl1c .cn, .gl5t .cs, .gl1m .cs, .gl1e .cs, .gl3e .cn',
                row
            );

            const categoryName = textOf(
                categoryNode,
                'Gallery'
            );

            const categoryClass =
                getCategoryClass(categoryNode);

            neutralizeHoverHandlers(row);

            const galleryHref =
                galleryLink.getAttribute('href') || '';

            const uploader = findUploader(row);
            const postedNode = findPostedNode(row);
            const ratingNode = findRating(row);
            const downloadNode = findDownload(row);
            const pageNode = findPageCount(row);

            const torrentLink = downloadNode
                ? qs('a[href]', downloadNode)
                : null;

            const torrentTitle = downloadNode
                ? (
                    qs('img[title]', downloadNode)
                        ?.getAttribute('title') ||
                    ''
                )
                : '';

            const image = qs(
                '.glthumb img[data-src], .glthumb img[src], ' +
                '.gl3t img[data-src], .gl3t img[src], ' +
                '.gl1e img[data-src], .gl1e img[src], ' +
                '.gl1m img[data-src], .gl1m img[src]',
                row
            );

            const tweet = create(
                'article',
                'ehtw-tweet'
            );

            if (isFollowingFeedPage) {
                tweet.classList.add('ehtw-following-checking');
            }

            tweet.setAttribute('data-ehtw-href', galleryHref);

            if (isThumbnail) {
                tweet.classList.add(
                    'ehtw-thumb-tweet',
                    'ehtw-tweet-pending'
                );
            }

            const avatar = makeAvatar(
                categoryName,
                categoryClass
            );

            const content = create(
                'div',
                'ehtw-tweet-content'
            );

            const author = create(
                'div',
                'ehtw-tweet-author'
            );

            const authorName = create(
                'span',
                'ehtw-author-name'
            );

            const titleText = textOf(
                qs('.glink', galleryLink) || galleryLink
            );

            const listAuthor = pickAuthor(
                qsa('.gt[title], .gtl[title], .gtw[title]', row)
                    .map((tag) => tag.getAttribute('title')),
                titleText
            );
            const listCreatorTags = qsa(
                '.gt[title], .gtl[title], .gtw[title]',
                row
            )
                .map((tag) => tag.getAttribute('title'))
                .filter((tag) => /^(?:artist|group):/i.test(tag || ''));

            tweet._ehtwListAuthor = listAuthor;
            tweet._ehtwListUploader = uploader.name;
            tweet._ehtwListUploaderHref = uploader.link?.getAttribute('href') || '';

            const handleNode = create('span', 'ehtw-handle');

            if (listAuthor) {
                showAuthorName(authorName, listAuthor);
            } else if (uploader.link) {
                showAuthorName(authorName, {
                    name: uploader.name,
                    href: uploader.link.getAttribute('href')
                });
            } else {
                authorName.textContent = isThumbnail
                    ? categoryName
                    : uploader.name;
            }

            if (uploader.link) {
                setTextSafe(uploader.link, formatHandle(uploader.name));
                move(uploader.link, handleNode);
            }

            append(author, authorName);

            append(author, handleNode);

            const fullTime = textOf(postedNode) || findRowTimestamp(row);
            if (fullTime) {

                const time = create(
                    'span',
                    'ehtw-time',
                    formatTwitterTime(fullTime)
                );

                time.title = fullTime;

                append(
                    author,
                    create(
                        'span',
                        'ehtw-dot',
                        '·'
                    )
                );

                append(author, time);

                if (postedNode) {
                    move(postedNode, content);

                    setStyleSafe(
                        postedNode,
                        'display',
                        'none'
                    );
                }
            }

            if (categoryNode) {
                append(
                    author,
                    create(
                        'span',
                        'ehtw-dot ehtw-category-dot',
                        '·'
                    )
                );

                move(categoryNode, author);
            }

            append(content, author);

            const title = create(
                'div',
                'ehtw-tweet-title'
            );

            const tags = create(
                'div',
                'ehtw-tags'
            );

            extractListTags(row, tags, isFollowingAuthorsPage);

            move(galleryLink, title);
            append(content, title);

            if (tags.children.length || isThumbnail) {
                append(content, tags);
            }

            tweet._ehtwListArtistTags = listCreatorTags;
            buildCardModerationActions(
                tweet,
                listCreatorTags,
                listAuthor,
                uploader.name,
                new Set(),
                new Set(),
                tweet._ehtwListUploaderHref
            );

            if (image) {
                const styleText =
                    image.getAttribute('style') || '';

                const widthMatch = styleText.match(
                    /(?:^|;)\s*width\s*:\s*([\d.]+)px/i
                );

                const heightMatch = styleText.match(
                    /(?:^|;)\s*height\s*:\s*([\d.]+)px/i
                );

                const width = widthMatch
                    ? Number(widthMatch[1])
                    : parseFloat(image.style.width);

                const height = heightMatch
                    ? Number(heightMatch[1])
                    : parseFloat(image.style.height);

                const ratio =
                    Number.isFinite(width) &&
                    Number.isFinite(height) &&
                    width > 0 &&
                    height > 0
                        ? width / height
                        : 1;

                const media = create(
                    'div',
                    'ehtw-media'
                );

                setStyleSafe(
                    media,
                    '--ehtw-ar',
                    String(ratio)
                );

                const link = create(
                    'a',
                    'ehtw-media-link'
                );

                link.href = galleryHref;

                move(image, link);
                prepareImage(image);

                append(media, link);
                append(content, media);
            }

            const actions = create(
                'div',
                'ehtw-twitter-actions'
            );

            const reply = makeTweetAction(
                'reply',
                galleryHref + '#cdiv'
            );

            append(
                reply,
                create(
                    'span',
                    'ehtw-action-number ehtw-stat-comments'
                )
            );

            append(actions, reply);

            // 种子数据只取原站 .gldown。
            // 存在原生 a 才允许触发下载弹窗。
            const repost = makeTweetAction('repost');

            if (downloadNode) {
                move(downloadNode, repost);

                addClassSafe(
                    downloadNode,
                    'ehtw-repost-native'
                );
            }

            if (torrentLink) {
                addClassSafe(
                    repost,
                    'ehtw-repost-available'
                );

                repost.title =
                    torrentTitle || 'Show torrents';
            } else {
                addClassSafe(
                    repost,
                    'ehtw-repost-unavailable'
                );

                repost.title =
                    torrentTitle || 'No torrents available';
            }

            append(actions, repost);

            let favoriteHref = '';

            try {
                const url = new URL(
                    galleryHref,
                    location.href
                );

                const match = url.pathname.match(
                    /^\/g\/(\d+)\/([0-9a-f]+)\/?$/i
                );

                if (
                    url.origin === location.origin &&
                    match
                ) {
                    favoriteHref =
                        '/gallerypopups.php?gid=' +
                        encodeURIComponent(match[1]) +
                        '&t=' +
                        encodeURIComponent(match[2]) +
                        '&act=addfav';
                }
            } catch (_) {}

            // 爱心对应收藏:打开原站的收藏表单。
            const heart = makeTweetAction('heart', favoriteHref);

            if (favoriteHref) {
                heart.target = '_blank';
                heart.rel = 'noopener noreferrer';
                heart.title = 'Add to Favorites';
                heart.setAttribute('aria-label', 'Add to Favorites');
                heart.addEventListener('click', (event) => {
                    event.preventDefault();
                    event.stopPropagation();
                    toggleBrowserFavorite(galleryHref, heart);
                });
            }

            append(
                heart,
                create(
                    'span',
                    'ehtw-action-number ehtw-stat-favorites'
                )
            );

            const ratingText = readSpriteRating(ratingNode);

            if (ratingText) {
                const rating = create(
                    'span',
                    'ehtw-tweet-rating',
                    '★ ' + ratingText
                );

                rating.title = 'Rating';

                append(author, create('span', 'ehtw-dot', '·'));
                append(author, rating);
            }

            if (ratingNode) {
                move(ratingNode, heart);

                setStyleSafe(
                    ratingNode,
                    'display',
                    'none'
                );
            }

            append(actions, heart);

            const views = makeTweetAction('views');

            append(
                views,
                create(
                    'span',
                    'ehtw-action-number',
                    extractPageNumber(pageNode)
                )
            );

            if (pageNode) {
                move(pageNode, views);

                setStyleSafe(
                    pageNode,
                    'display',
                    'none'
                );
            }

            append(actions, views);

            append(content, actions);
            append(tweet, avatar);
            append(tweet, content);

            syncAvatarLetter(tweet);

            return tweet;
        }

        function transformList() {
            const pathname =
                location.pathname.replace(/\/+$/, '') || '/';
            const isToplistResultPage = pathname === '/toplist.php' &&
                new URLSearchParams(location.search).has('tl');
            const blockedToplistTagsState = { value: null };

            let nextListPageUrl = findNextListPageUrl(document);
            const seenGalleryHrefs = new Set();

            const searchTerm = new URLSearchParams(
                location.search
            ).get('f_search') || '';

            const hasOriginalSearch = !!qs('#searchbox');

            const isUploaderPage =
                /^\/uploader\//i.test(pathname);

            const currentUploader = isUploaderPage
                ? (() => {
                    const raw = pathname.replace(
                        /^\/uploader\//i,
                        ''
                    );

                    try {
                        return decodeURIComponent(raw)
                            .replace(/\+/g, ' ')
                            .trim()
                            .toLowerCase();
                    } catch (_) {
                        return raw.replace(/\+/g, ' ')
                            .trim()
                            .toLowerCase();
                    }
                })()
                : '';

            const uploaderCounts = new Map();

            // 必须在 createTweet 搬走上传者链接之前读取。
            qsa('tr', table).forEach(row => {
                if (!isGalleryListRow(row)) return;

                const link = findUploader(row).link;

                if (!link) return;

                const name = textOf(link).trim();
                const href = link.getAttribute('href');

                if (!name || !href) return;

                if (
                    currentUploader &&
                    name.toLowerCase() === currentUploader
                ) {
                    return;
                }

                try {
                    const url = new URL(href, location.href);

                    if (url.origin !== location.origin) {
                        return;
                    }

                    const previous = uploaderCounts.get(url.href);

                    if (previous) {
                        previous.count++;
                    } else {
                        uploaderCounts.set(url.href, {
                            name,
                            href: url.href,
                            count: 1
                        });
                    }
                } catch (_) {}
            });

            const topUploaders = Array.from(
                uploaderCounts.values()
            )
                .sort((a, b) =>
                    b.count - a.count ||
                    a.name.localeCompare(b.name)
                )
                .slice(0, 3);

            const timeline = create(
                'div',
                'ehtw-timeline'
            );
            const isPopularTimelinePage = pathname === '/popular';
            const popularTimelineItems = [];
            let popularTimelineSequence = 0;
            const getListTimelineTimestamp = (row) => {
                const posted = findPostedNode(row);
                const raw = [
                    posted?.getAttribute('title'),
                    posted?.getAttribute('aria-label'),
                    textOf(posted),
                    findRowTimestamp(row)
                ].filter(Boolean).join(' ');
                const fullDate = raw.match(/(\d{4})-(\d{2})-(\d{2})\s+(\d{2}):(\d{2})/);
                if (fullDate) {
                    return Date.UTC(
                        Number(fullDate[1]),
                        Number(fullDate[2]) - 1,
                        Number(fullDate[3]),
                        Number(fullDate[4]),
                        Number(fullDate[5])
                    );
                }
                const relative = raw.match(/\b(\d+)\s*(m|h|d|w)\b/i);
                if (relative) {
                    const units = { m: 60000, h: 3600000, d: 86400000, w: 604800000 };
                    return Date.now() - Number(relative[1]) * units[relative[2].toLowerCase()];
                }
                const parsed = Date.parse(raw);
                return Number.isFinite(parsed) ? parsed : 0;
            };
            const renderPopularTimeline = () => {
                if (!isPopularTimelinePage || !popularTimelineItems.length) return;
                popularTimelineItems.sort((a, b) =>
                    b.timestamp - a.timestamp || a.sequence - b.sequence
                );
                const existingCards = qsa('.ehtw-tweet', timeline);
                const firstCard = existingCards[0];
                const previousNode = firstCard?.previousSibling || null;
                const anchorCard = existingCards.find((card) =>
                    card.getBoundingClientRect().bottom > 0
                ) || null;
                const anchorTop = anchorCard?.getBoundingClientRect().top ?? 0;
                existingCards.forEach((card) => card.remove());
                const fragment = document.createDocumentFragment();
                popularTimelineItems.forEach(({ tweet }) => fragment.appendChild(tweet));
                const reference = previousNode?.parentNode === timeline
                    ? previousNode.nextSibling
                    : timeline.firstChild;
                timeline.insertBefore(fragment, reference || null);
                if (anchorCard?.isConnected) {
                    const shift = anchorCard.getBoundingClientRect().top - anchorTop;
                    if (Math.abs(shift) > 1) {
                        window.scrollTo?.(window.scrollX || 0, (window.scrollY || 0) + shift);
                    }
                }
            };
        if (isToplistResultPage) {
            addClassSafe(timeline, 'ehtw-toplist-filter-pending');
        }

            append(center, timeline);

            if (isFollowingFeedPage) {
                const modeTabs = create('nav', 'ehtw-follow-mode-tabs');
                [
                    ['authors', '作者'],
                    ['uploaders', '上传者']
                ].forEach(([mode, label]) => {
                    const link = create(
                        'a',
                        'ehtw-follow-mode-tab',
                        label
                    );
                    link.href = `/watched?ehtw_following=${mode}`;
                    if (followingFeedMode === mode) addClassSafe(link, 'ehtw-follow-mode-active');
                    append(modeTabs, link);
                });
                append(timeline, modeTabs);
            }

            // .searchtext 只用作头部副标题的数据源。
            // 不再生成重复的 .ehtw-results-count。
            const oldResultsCount = qs(
                '.ehtw-results-count',
                center
            );

            if (oldResultsCount) {
                setStyleSafe(
                    oldResultsCount,
                    'display',
                    'none'
                );
            }

            const appendListRows = (sourceTable, cloneRows = false) => {
                const appendedTweets = [];
                const previousTrackingState = skipTransactionTracking;
                if (cloneRows) skipTransactionTracking = true;
                const addRow = (row) => {
                    const galleryLink = findGalleryListLink(row);
                    const galleryHref = galleryLink?.getAttribute('href');
                    if (!galleryHref || seenGalleryHrefs.has(galleryHref)) return;
                    const entryTags = getToplistEntryTags(row);
                    if (
                        isToplistResultPage &&
                        blockedToplistTagsState.value &&
                        entryTags.some((tag) => blockedToplistTagsState.value.has(tag))
                    ) {
                        seenGalleryHrefs.add(galleryHref);
                        return;
                    }

                    const sourceRow = cloneRows ? row.cloneNode(true) : row;
                    const timestamp = isPopularTimelinePage
                        ? getListTimelineTimestamp(sourceRow)
                        : 0;
                    const tweet = createTweet(sourceRow);
                    if (!tweet) return;

                    seenGalleryHrefs.add(galleryHref);
                    if (isToplistResultPage) tweet._ehtwToplistTags = entryTags;
                    if (isPopularTimelinePage) {
                        popularTimelineItems.push({
                            tweet,
                            timestamp,
                            sequence: popularTimelineSequence++
                        });
                    } else {
                        append(timeline, tweet);
                    }
                    appendedTweets.push(tweet);
                };

                try {
                    qsa('.gl1t', sourceTable).forEach(addRow);
                    qsa('tr', sourceTable).forEach((row) => {
                        if (qs('th', row)) return;
                        if (!isGalleryListRow(row)) {
                            if (!cloneRows) addClassSafe(row, 'ehtw-hidden-ad');
                            return;
                        }
                        addRow(row);
                    });

                    renderPopularTimeline();

                    return appendedTweets;
                } finally {
                    skipTransactionTracking = previousTrackingState;
                }
            };

            const followingTimelineItems = [];
            const followingSeenGalleryPaths = new Set();
            const followingNextPageUrls = new Map();
            let followingCreatorTags = [];
            const followingFeedSourceInfo = new Map();
            let followingTimelineSequence = 0;
            let followingPageBatchPromise = null;
            let followingRateLimited = false;
            let maybeLoadFollowingMore = () => {};
            const FOLLOWING_CREATOR_FETCH_CONCURRENCY = 3;

            const getFollowingTimestamp = (row) => {
                const posted = findPostedNode(row);
                const raw = [
                    posted?.getAttribute('title'),
                    posted?.getAttribute('aria-label'),
                    textOf(posted)
                ].filter(Boolean).join(' ');
                const fullDate = raw.match(
                    /(\d{4})-(\d{2})-(\d{2})\s+(\d{2}):(\d{2})/
                );
                if (fullDate) {
                    return Date.UTC(
                        Number(fullDate[1]),
                        Number(fullDate[2]) - 1,
                        Number(fullDate[3]),
                        Number(fullDate[4]),
                        Number(fullDate[5])
                    );
                }

                const relative = raw.match(/\b(\d+)\s*(m|h|d|w)\b/i);
                if (relative) {
                    const units = {
                        m: 60000,
                        h: 3600000,
                        d: 86400000,
                        w: 604800000
                    };
                    return Date.now() -
                        Number(relative[1]) * units[relative[2].toLowerCase()];
                }

                const parsed = Date.parse(raw);
                return Number.isFinite(parsed) ? parsed : 0;
            };

            const collectFollowingPage = (
                page,
                baseHref,
                sourceCreatorTag = '',
                sourceUploaderName = ''
            ) => {
                const sourceList = page.querySelector(
                    'table.itg.gltc, table.itg.gltm, table.itg.glte, div.itg.gld, .itg'
                );
                if (!sourceList) return [];
                const appended = [];
                const rows = [
                    ...Array.from(sourceList.querySelectorAll('.gl1t')),
                    ...Array.from(sourceList.querySelectorAll('tr'))
                        .filter((row) => isGalleryListRow(row))
                ];

                rows.forEach((row) => {
                    const galleryLink = findGalleryListLink(row);
                    const href = galleryLink?.getAttribute('href');
                    if (!href) return;

                    try {
                        const url = new URL(href, baseHref);
                        const key = url.pathname;
                        if (url.origin !== location.origin || followingSeenGalleryPaths.has(key)) return;

                        // Import parsed nodes into the live page document before
                        // createTweet moves their children into the timeline.
                        const liveRow = document.importNode(row, true);
                        const tweet = createTweet(liveRow);
                        if (!tweet) {
                            console.warn('[EH Twitter Style] Follow row produced no card', key);
                            return;
                        }
                        followingSeenGalleryPaths.add(key);
                        const normalizedSourceTag = normalizeCreatorFollowTag(sourceCreatorTag);
                        const normalizedSourceUploader = normalizeUploaderName(sourceUploaderName);
                        if (normalizedSourceTag) {
                            tweet._ehtwFollowingSourceTag = normalizedSourceTag;
                        }
                        if (normalizedSourceUploader) {
                            tweet._ehtwFollowingSourceUploader = normalizedSourceUploader;
                        }
                        if (normalizedSourceTag || normalizedSourceUploader) {
                            // This card came from an already-followed source; its optional stats can load after display.
                            tweet.classList.remove('ehtw-following-checking');
                        }
                        const timestamp = getFollowingTimestamp(row);
                        const item = {
                            tweet,
                            // Keep undated entries after dated galleries. A synthetic
                            // "now" timestamp made stale/unknown dates jump to the top.
                            timestamp: timestamp || 0,
                            sequence: followingTimelineSequence++
                        };
                        followingTimelineItems.push(item);
                        appended.push(tweet);
                    } catch (error) {
                        console.warn('[EH Twitter Style] Follow row conversion failed', href, error);
                    }
                });
                return appended;
            };

            const renderFollowingTimeline = (newTweets = []) => {
                followingTimelineItems.sort((a, b) =>
                    b.timestamp - a.timestamp || a.sequence - b.sequence
                );
                qs('.ehtw-empty', timeline)?.remove();
                if (infiniteStatus.parentNode !== timeline) append(timeline, infiniteStatus);
                const fragment = document.createDocumentFragment();
                followingTimelineItems.forEach(({ tweet }) => {
                    fragment.appendChild(tweet);
                });
                timeline.insertBefore(fragment, infiniteStatus);
                if (newTweets.length && typeof registerAdditionalStatsTweets === 'function') {
                    registerAdditionalStatsTweets(newTweets);
                }
            };

            const loadFollowingAuthorTimeline = async () => {
                if (!isFollowingFeedPage) return;

                try {
                    let feedSources = [];
                    if (isFollowingAuthorsPage) {
                        const followedTags = await requestWatchedCreatorTags();
                        feedSources = Array.from(followedTags).map((creatorTag) => ({
                                key: creatorTag,
                                url: '/tag/' + encodeTagPath(creatorTag),
                                creatorTag
                        }));
                    } else if (isFollowingUploadersPage) {
                        feedSources = (await requestWatchedUploaderNames()).map((uploader) => ({
                                key: `uploader:${uploader.key}`,
                            url: uploader.href,
                                uploaderName: uploader.name
                        }));
                    }
                    if (!feedSources.length) {
                        setFollowingStatus(
                            isFollowingAuthorsPage
                                ? 'No artist or group tags are marked Watched. Follow a creator from a gallery card to start this feed.'
                                : 'No uploaders followed yet. Follow an uploader from a card or gallery page to start this feed.',
                            'done'
                        );
                        append(timeline, makeEmptyHint('No followed creators yet.'));
                        append(timeline, infiniteStatus);
                        return;
                    }
                    followingCreatorTags = feedSources.map((source) => source.key);
                    feedSources.forEach((source) => followingFeedSourceInfo.set(source.key, source));

                    for (let offset = 0; offset < feedSources.length; offset += FOLLOWING_CREATOR_FETCH_CONCURRENCY) {
                        const sourceBatch = feedSources.slice(
                            offset,
                            offset + FOLLOWING_CREATOR_FETCH_CONCURRENCY
                        );
                        setFollowingStatus(
                            `Searching ${offset + 1}–${offset + sourceBatch.length}/${feedSources.length}`,
                            'loading'
                        );

                        const results = await Promise.all(sourceBatch.map(async (source) => {
                            const requestUrl = new URL(source.url, location.href).href;
                            try {
                                const fetchUrl = new URL(requestUrl, location.href);
                                fetchUrl.searchParams.set(
                                    '_ehtw_refresh',
                                    `${Date.now()}-${source.key}`
                                );
                                const response = await window.fetch(fetchUrl.href, {
                                    credentials: 'same-origin',
                                    cache: 'reload'
                                });
                                if (response.status === 429 || response.status === 403) {
                                    return { source, requestUrl, rateLimited: true };
                                }
                                if (!response.ok) return { source, requestUrl, failed: true };
                                const page = new window.DOMParser().parseFromString(
                                    await response.text(),
                                    'text/html'
                                );
                                return { source, requestUrl, page };
                            } catch (_) {
                                return { source, requestUrl, failed: true };
                            }
                        }));

                        const newTweets = [];
                        results.forEach(({ source, requestUrl, page, rateLimited }) => {
                            if (rateLimited) {
                                followingRateLimited = true;
                                return;
                            }
                            if (!page) return;
                            const nextUrl = findNextListPageUrl(page, requestUrl);
                            if (nextUrl) followingNextPageUrls.set(source.key, nextUrl);
                            newTweets.push(...collectFollowingPage(
                                page,
                                requestUrl,
                                source.creatorTag || '',
                                source.uploaderName || ''
                            ));
                        });
                        if (newTweets.length) renderFollowingTimeline(newTweets);
                        if (followingRateLimited) break;

                        if (offset + sourceBatch.length < feedSources.length) {
                            await new Promise((resolve) => setTimeout(resolve, 1500));
                        }
                    }

                    if (!followingTimelineItems.length) {
                        append(timeline, makeEmptyHint('No galleries found for followed creators.'));
                    }

                    if (infiniteStatus.parentNode !== timeline) append(timeline, infiniteStatus);
                    if (followingRateLimited) {
                        setFollowingStatus('Creator search was rate-limited. Reload later to continue.', 'error');
                    } else {
                        setInfiniteStatus('', 'idle');
                        startFollowingAuthorContinuation();
                    }
                } catch (error) {
                    console.warn('[EH Twitter Style] Following feed aggregation failed', error);
                    setFollowingStatus('Could not build the author timeline. Check My Tags and reload.', 'error');
                }
            };

            const loadNextFollowingBatch = () => {
                if (followingPageBatchPromise || !followingNextPageUrls.size) {
                    return followingPageBatchPromise || Promise.resolve();
                }

                listInfiniteObserver?.unobserve(infiniteStatus);
                setFollowingStatus('Loading more…', 'loading');
                followingPageBatchPromise = (async () => {
                    let batchFailed = false;
                    const requests = followingCreatorTags
                        .map((key) => [key, followingNextPageUrls.get(key), followingFeedSourceInfo.get(key)])
                        .filter(([, url]) => !!url);

                    for (let offset = 0; offset < requests.length; offset += FOLLOWING_CREATOR_FETCH_CONCURRENCY) {
                        const requestBatch = requests.slice(
                            offset,
                            offset + FOLLOWING_CREATOR_FETCH_CONCURRENCY
                        ).filter(([key, requestedUrl]) =>
                            followingNextPageUrls.get(key) === requestedUrl
                        );
                        if (!requestBatch.length) continue;

                        const results = await Promise.all(requestBatch.map(async ([sourceKey, requestedUrl, source]) => {
                            try {
                                const fetchUrl = new URL(requestedUrl, location.href);
                                fetchUrl.searchParams.set(
                                    '_ehtw_refresh',
                                    `${Date.now()}-${sourceKey}`
                                );
                                const response = await window.fetch(fetchUrl.href, {
                                    credentials: 'same-origin',
                                    cache: 'reload'
                                });
                                if (response.status === 429 || response.status === 403) {
                                    return { sourceKey, requestedUrl, source, rateLimited: true };
                                }
                                if (!response.ok) return { sourceKey, requestedUrl, source, failed: true };
                                const page = new window.DOMParser().parseFromString(
                                    await response.text(),
                                    'text/html'
                                );
                                return { sourceKey, requestedUrl, source, page };
                            } catch (_) {
                                return { sourceKey, requestedUrl, source, failed: true };
                            }
                        }));

                        const newTweets = [];
                        results.forEach(({ sourceKey, requestedUrl, source, page, rateLimited, failed }) => {
                            if (rateLimited) {
                                followingRateLimited = true;
                                batchFailed = true;
                                return;
                            }
                            if (failed || !page) {
                                batchFailed = true;
                                return;
                            }
                            const nextUrl = findNextListPageUrl(page, requestedUrl);
                            if (nextUrl && nextUrl !== requestedUrl) {
                                followingNextPageUrls.set(sourceKey, nextUrl);
                            } else {
                                followingNextPageUrls.delete(sourceKey);
                            }
                            newTweets.push(...collectFollowingPage(
                                page,
                                requestedUrl,
                                source?.creatorTag || '',
                                source?.uploaderName || ''
                            ));
                        });
                        if (newTweets.length) renderFollowingTimeline(newTweets);
                        if (followingRateLimited) break;

                        if (offset + requestBatch.length < requests.length) {
                            await new Promise((resolve) => setTimeout(resolve, 1500));
                        }
                    }

                    if (followingRateLimited || batchFailed) {
                        setFollowingStatus(
                            followingRateLimited
                                ? 'Creator feed was rate-limited. Retry later.'
                                : 'Could not load more creators. Retry to continue.',
                            'error'
                        );
                        const retry = create('button', '', 'Load more');
                        retry.type = 'button';
                        retry.addEventListener('click', () => {
                            followingRateLimited = false;
                            loadNextFollowingBatch();
                        });
                        infiniteStatus.appendChild(retry);
                        return;
                    }

                    if (!followingNextPageUrls.size) {
                        setInfiniteStatus('', 'done');
                        listInfiniteObserver?.disconnect();
                        listInfiniteObserver = null;
                        return;
                    }

                    setInfiniteStatus('', 'idle');
                    if (listInfiniteObserver) {
                        window.requestAnimationFrame?.(() => {
                            if (followingNextPageUrls.size) {
                                listInfiniteObserver?.observe(infiniteStatus);
                                maybeLoadFollowingMore();
                            }
                        });
                    }
                })().finally(() => {
                    followingPageBatchPromise = null;
                });

                return followingPageBatchPromise;
            };

            startFollowingAuthorContinuation = () => {
            if (!isFollowingFeedPage || !followingNextPageUrls.size) return;

                maybeLoadFollowingMore = () => {
                    if (
                        !followingNextPageUrls.size ||
                        followingPageBatchPromise ||
                        infiniteStatus.dataset.state === 'error'
                    ) return;
                    if (
                        infiniteStatus.getBoundingClientRect().top <
                        (window.innerHeight || 0) + 6000
                    ) {
                        loadNextFollowingBatch();
                    }
                };

                if (typeof window.IntersectionObserver === 'function') {
                    listInfiniteObserver = new window.IntersectionObserver(
                        (entries) => {
                            if (entries.some((entry) => entry.isIntersecting)) {
                                maybeLoadFollowingMore();
                            }
                        },
                        { rootMargin: '6000px 0px' }
                    );
                    listInfiniteObserver.observe(infiniteStatus);
                }

                listInfiniteScrollFallback = () => {
                    maybeLoadFollowingMore();
                };
                addSpaPageEventListener(window, 'scroll', listInfiniteScrollFallback, { passive: true });
                listInfiniteScrollFallback();
            };

            if (isFollowingFeedPage) {
                startFollowingAuthorTimeline = loadFollowingAuthorTimeline;
            }

            const converted = isFollowingFeedPage
                ? 0
                : appendListRows(table).length;

            if (isToplistResultPage) {
                requestHiddenMyTags().then((hiddenTags) => {
                    blockedToplistTagsState.value = hiddenTags;
                    qsa('.ehtw-tweet', timeline).forEach((tweet) => {
                        if (!(tweet._ehtwToplistTags || []).some((tag) => hiddenTags.has(tag))) return;
                        const href = tweet.getAttribute('data-ehtw-href');
                        if (href) seenGalleryHrefs.delete(href);
                        tweet.remove();
                    });
                    timeline.classList.remove('ehtw-toplist-filter-pending');
                }).catch(() => {
                    timeline.replaceChildren(create(
                        'div',
                        'ehtw-empty',
                        'Could not load My Tags filters. Reload to retry.'
                    ));
                    timeline.classList.remove('ehtw-toplist-filter-pending');
                });
            }

            if (!converted && !isFollowingFeedPage) {
                append(
                    timeline,
                    makeEmptyHint('No galleries found.')
                );
            }

            // 此时 buildSearch 已处理有搜索框页面的上方工具栏。
            // /popular 等无 #searchbox 的页面,必须保留上下两栏。
            const remainingNavs = qsa('.searchnav').filter(
                node =>
                    !node.closest('#ehtw-search-card') &&
                    !node.closest('#ehtw-mobile-search') &&
                    !node.closest('#ehtw-shell')
            );

            qsa('.searchnav').filter(
                node => !node.closest('#ehtw-shell')
            ).forEach((nav) => {
                qsa('a, span[id]', nav).forEach((control) => {
                    setStyleSafe(control, 'display', 'none');
                });
            });
            qsa('#dnext, #unext').forEach((control) => {
                setStyleSafe(control, 'display', 'none');
            });

            if (!hasOriginalSearch && remainingNavs.length > 1) {
                const upper = remainingNavs[0];

                addClassSafe(
                    upper,
                    'ehtw-list-bottom-toolbar'
                );

                move(upper, timeline);
            }

            if (remainingNavs.length) {
                const lower = remainingNavs[
                    remainingNavs.length - 1
                ];

                if (!hasOriginalSearch && remainingNavs.length === 1) {
                    addClassSafe(
                        lower,
                        'ehtw-list-bottom-toolbar'
                    );

                    move(lower, timeline);
                } else {
                    const pager = buildPagination(lower, timeline);
                    if (pager) {
                        addClassSafe(pager, 'ehtw-list-pagination-hidden');
                    }
                }
            }

            // 缺少原站搜索框时,在右栏补充独立 GET 搜索表单。
            if (!hasOriginalSearch) {
                ensureRightSearchForm();
            }

            // /uploader/ 页面不显示推荐;
            // 不同上传者不足两个也不显示。
            if (
                !right.classList.contains('ehtw-toplist-only-sidebar') &&
                !isUploaderPage &&
                topUploaders.length >= 2
            ) {
                const card = create(
                    'section',
                    'ehtw-side-card ehtw-who-card'
                );

                append(
                    card,
                    create(
                        'h2',
                        'ehtw-side-title',
                        'Who to follow'
                    )
                );

                topUploaders.forEach(item => {
                    const itemRow = create(
                        'div',
                        'ehtw-who-row'
                    );

                    append(
                        itemRow,
                        create(
                            'span',
                            'ehtw-who-avatar',
                            Array.from(item.name)[0]
                                ?.toUpperCase() || '?'
                        )
                    );

                    const info = create(
                        'div',
                        'ehtw-who-info'
                    );

                    const name = create(
                        'a',
                        'ehtw-who-name',
                        item.name
                    );

                    name.href = item.href;
                    append(info, name);

                    append(
                        info,
                        create(
                            'span',
                            'ehtw-who-handle',
                            '@' + item.name.replace(/\s+/g, '_')
                        )
                    );

                    const view = create(
                        'a',
                        'ehtw-who-view',
                        'View'
                    );

                    view.href = item.href;

                    append(itemRow, info);
                    append(itemRow, view);
                    append(card, itemRow);
                });

                append(right, card);
            }

            const infiniteStatus = create('div', 'ehtw-list-infinite-status');
            infiniteStatus.setAttribute('role', 'status');
            infiniteStatus.setAttribute('aria-live', 'polite');
            if (!isFollowingFeedPage) append(timeline, infiniteStatus);

            const seenListPageUrls = new Set([
                new URL(location.href).href
            ]);
            const setInfiniteStatus = (message, state) => {
                infiniteStatus.dataset.state = state;
                infiniteStatus.replaceChildren();
                infiniteStatus.textContent = message;
            };
            const setFollowingStatus = (message, state = 'loading') => {
                setInfiniteStatus(message, state);
            };
            let listPageLoadInProgress = false;
            let listPagesExhausted = !nextListPageUrl;

            const loadNextListPage = async () => {
                if (listPageLoadInProgress || listPagesExhausted || !nextListPageUrl) return;

                const requestedUrl = nextListPageUrl;
                if (seenListPageUrls.has(requestedUrl)) {
                    listPagesExhausted = true;
                    setInfiniteStatus('No more galleries', 'done');
                    listInfiniteObserver?.unobserve(infiniteStatus);
                    return;
                }

                listPageLoadInProgress = true;
                seenListPageUrls.add(requestedUrl);
                setInfiniteStatus('Loading more…', 'loading');
                listInfiniteObserver?.unobserve(infiniteStatus);

                try {
                    const response = await window.fetch(requestedUrl, {
                        credentials: 'same-origin'
                    });
                    if (!response?.ok) {
                        throw new Error(`List page request failed: ${response?.status || 'no response'}`);
                    }

                    const pageText = await response.text();
                    const parsedPage = new window.DOMParser()
                        .parseFromString(pageText, 'text/html');
                    const nextTable = parsedPage.querySelector(
                        'table.itg.gltc, table.itg.gltm, table.itg.glte, div.itg.gld'
                    );
                    if (!nextTable) {
                        throw new Error('The next page did not contain a gallery list');
                    }

                    const pageNextUrl = findNextListPageUrl(parsedPage, requestedUrl);
                    const appendedTweets = appendListRows(nextTable, true);
                    if (!appendedTweets.length) {
                        listPagesExhausted = true;
                        nextListPageUrl = null;
                        setInfiniteStatus('No more galleries', 'done');
                        listInfiniteObserver?.disconnect();
                        return;
                    }

                    qs('.ehtw-empty', timeline)?.remove();
                    registerAdditionalStatsTweets?.(appendedTweets);
                    nextListPageUrl = pageNextUrl && !seenListPageUrls.has(pageNextUrl)
                        ? pageNextUrl
                        : null;
                    listPagesExhausted = !nextListPageUrl;

                    if (listPagesExhausted) {
                        setInfiniteStatus('No more galleries', 'done');
                        listInfiniteObserver?.disconnect();
                    } else {
                        setInfiniteStatus('', 'idle');
                        if (listInfiniteObserver) {
                            window.requestAnimationFrame?.(() => {
                                if (!listPagesExhausted) {
                                    listInfiniteObserver?.observe(infiniteStatus);
                                }
                            });
                        }
                    }
                } catch (error) {
                    console.warn('[EH Twitter Style] Infinite list load failed', error);
                    seenListPageUrls.delete(requestedUrl);
                    setInfiniteStatus('', 'error');
                    const retry = create('button', '', 'Load more');
                    retry.type = 'button';
                    retry.addEventListener('click', () => loadNextListPage());
                    infiniteStatus.appendChild(retry);
                } finally {
                    listPageLoadInProgress = false;
                }
            };

            startListInfiniteContinuation = () => {
                if (isFollowingFeedPage) return;
                if (listPagesExhausted) {
                    setInfiniteStatus('No more galleries', 'done');
                    return;
                }

                if (typeof window.IntersectionObserver === 'function') {
                    listInfiniteObserver = new window.IntersectionObserver(
                        (entries) => {
                            if (entries.some(entry => entry.isIntersecting)) {
                                loadNextListPage();
                            }
                        },
                        { rootMargin: '1000px 0px' }
                    );
                    listInfiniteObserver.observe(infiniteStatus);
                    return;
                }

                listInfiniteScrollFallback = () => {
                    if (
                        !listPagesExhausted &&
                        infiniteStatus.getBoundingClientRect().top <
                            (window.innerHeight || 0) + 1000
                    ) {
                        loadNextListPage();
                    }
                };
                addSpaPageEventListener(window, 'scroll', listInfiniteScrollFallback, { passive: true });
                listInfiniteScrollFallback();
            };

            setStyleSafe(
                table,
                'display',
                'none'
            );
        }

        // ============================================================
        // 10. 详情页转换
        // ============================================================

        function buildDetailAuthor(
            post,
            categoryName,
            categoryClass
        ) {
            const author = create(
                'div',
                'ehtw-detail-author'
            );

            const avatar = makeAvatar(
                categoryName,
                categoryClass
            );

            const meta = create(
                'div',
                'ehtw-detail-author-meta'
            );

            const uploaderLink = qs('#gdn a');

            const uploaderName = textOf(
                uploaderLink,
                'Unknown'
            );

            const name = create(
                'div',
                'ehtw-author-name'
            );

            const detailCreatorTags = readPageAuthorTags(document)
                .filter((tag) => /^(?:artist|group):/i.test(tag));
            const detailAuthor = pickAuthor(
                detailCreatorTags,
                textOf(qs('#gj')) || textOf(qs('#gn'))
            ) || pickAuthor([], textOf(qs('#gn')));

            if (detailAuthor) {
                showAuthorName(name, detailAuthor);
            } else if (uploaderLink) {
                showAuthorName(name, {
                    name: uploaderName,
                    href: uploaderLink.getAttribute('href')
                });
            } else {
                name.textContent = uploaderName;
            }

            append(meta, name);

            const handle = create('div', 'ehtw-handle');

            if (uploaderLink) {
                setTextSafe(uploaderLink, formatHandle(uploaderName));
                move(uploaderLink, handle);
            } else {
                handle.textContent = formatHandle(uploaderName);
            }

            append(meta, handle);

            const followActions = create('div', 'ehtw-detail-follow-actions');
            const followStatus = create('span', 'ehtw-detail-follow-status');
            followStatus.setAttribute('role', 'status');
            followStatus.setAttribute('aria-live', 'polite');
            const setFollowStatus = (message, isError = false) => {
                followStatus.textContent = message || '';
                followStatus.classList.toggle('is-error', !!isError);
            };
            const creatorTags = detailCreatorTags.length
                ? detailCreatorTags
                : detailAuthor?.name
                    ? [
                        /\/tag\/group:/i.test(String(detailAuthor.href || ''))
                            ? `group:${detailAuthor.name}`
                            : `artist:${detailAuthor.name}`
                    ]
                    : [];
            Array.from(new Set(creatorTags)).forEach((tag) => {
                const normalized = normalizeCreatorFollowTag(tag);
                if (!normalized) return;
                const type = normalized.startsWith('group:') ? 'Group' : 'Artist';
                const label = create('button', 'ehtw-detail-follow-button', type);
                label.type = 'button';
                label.title = `关注 ${type} · ${tag.replace(/^(?:artist|group):/i, '')}`;
                let isFollowed = false;
                const updateLabel = () => {
                    label.textContent = type;
                    label.setAttribute('aria-pressed', String(isFollowed));
                    label.setAttribute('aria-label', `${isFollowed ? '取消关注' : '关注'} ${type}`);
                };
                label.addEventListener('click', () => {
                    const wasFollowed = isFollowed;
                    label.disabled = true;
                    isFollowed = !wasFollowed;
                    updateLabel();
                    setFollowStatus('正在保存到 My Tags…');
                    toggleWatchedTagInMyTags(tag, (success, nowFollowed, reason) => {
                        isFollowed = success ? !!nowFollowed : wasFollowed;
                        label.disabled = false;
                        updateLabel();
                        setFollowStatus(
                            success
                                ? `${isFollowed ? '已关注' : '已取消关注'} ${type}`
                                : (reason || '保存失败,请稍后重试。'),
                            !success
                        );
                    });
                });
                append(followActions, label);
                requestWatchedCreatorTags().then((followed) => {
                    isFollowed = followed.has(normalized);
                    updateLabel();
                }).catch(() => {});
            });

            if (uploaderName && uploaderName.toLowerCase() !== 'unknown') {
                const uploaderKey = normalizeUploaderName(uploaderName);
                const uploaderButton = create('button', 'ehtw-detail-follow-button');
                uploaderButton.type = 'button';
                let uploaderFollowed = false;
                const updateUploaderButton = () => {
                    uploaderButton.textContent = '上传者';
                    uploaderButton.setAttribute('aria-pressed', String(uploaderFollowed));
                    uploaderButton.setAttribute('aria-label', `${uploaderFollowed ? '取消关注' : '关注'}上传者`);
                };
                updateUploaderButton();
                uploaderButton.title = `关注上传者 · ${uploaderName}`;
                uploaderButton.addEventListener('click', () => {
                    const wasFollowed = uploaderFollowed;
                    uploaderButton.disabled = true;
                    uploaderFollowed = !wasFollowed;
                    updateUploaderButton();
                    setFollowStatus('正在保存到上传者收藏夹…');
                    followUploaderViaFavorites(uploaderName, location.pathname, (success, nowFollowed, reason) => {
                        uploaderFollowed = success ? !!nowFollowed : wasFollowed;
                        uploaderButton.disabled = false;
                        updateUploaderButton();
                        setFollowStatus(
                            success
                                ? `${uploaderFollowed ? '已关注' : '已取消关注'}上传者`
                                : (reason || '保存失败,请稍后重试。'),
                            !success
                        );
                    });
                });
                requestWatchedUploaderNames().then((entries) => {
                    uploaderFollowed = entries.some((entry) => entry.key === uploaderKey);
                    updateUploaderButton();
                }).catch(() => {});
                append(followActions, uploaderButton);
            }
            if (followActions.children.length) append(followActions, followStatus);

            append(author, avatar);
            append(author, meta);
            if (followActions.children.length) append(author, followActions);

            syncAvatarLetter(author);

            append(post, author);
        }

        function buildDetailCover(post) {
            const original = qs('#gleft');

            if (!original) return;

            const cover = create(
                'div',
                'ehtw-detail-cover'
            );

            move(original, cover);

            const background = qs(
                '#gd1 > div',
                cover
            );

            if (background) {
                const originalBackground =
                    extractBackgroundImage(background);

                if (
                    originalBackground &&
                    originalBackground !== 'none'
                ) {
                    setStyleSafe(
                        background,
                        'background-image',
                        originalBackground
                    );
                }
            }

            append(post, cover);
        }

        function buildDetailMeta(post) {
            const mid = qs('#gmid');
            if (!mid) return;

            const info = qs('#gdd');
            const rating = qs('#gdr');
            const favorites = qs('#gdf');

            const originalInfo = textOf(info);

            const pagesMatch = originalInfo.match(
                /(\d[\d,]*)\s+pages?/i
            );

            const pages = pagesMatch
                ? pagesMatch[1]
                : '—';

            const ratingText = textOf(
                qs('#rating_label')
            );

            const ratingMatch = ratingText.match(
                /(\d+(?:\.\d+)?)/
            );

            const ratingValue = ratingMatch
                ? ratingMatch[1]
                : '—';

            const favoriteNode = qs('#favcount');

            const favoriteSource = favoriteNode
                ? textOf(favoriteNode)
                : originalInfo;

            const favoriteMatch = favoriteSource.match(
                /(\d[\d,]*)/
            );

            let favoriteValue = '—';

            if (favoriteMatch) {
                favoriteValue = favoriteMatch[1];
            } else if (/\bonce\b/i.test(favoriteSource)) {
                favoriteValue = '1';
            } else if (/\bnever\b/i.test(favoriteSource)) {
                favoriteValue = '0';
            } else {
                const fallback = originalInfo.match(
                    /favorited:\s*(\d[\d,]*)\s*times?/i
                );

                if (fallback) {
                    favoriteValue = fallback[1];
                }
            }

            const ratingCountSource = textOf(
                qs('#rating_count')
            );

            const ratingCountMatch = ratingCountSource.match(
                /(\d[\d,]*)/
            );

            const ratingCount = ratingCountMatch
                ? ratingCountMatch[1]
                : '0';

            const commentCount = readCommentCount(document);

            const section = create(
                'section',
                'ehtw-detail-meta'
            );

            const time = findDetailTimestamp(mid);

            if (time) {
                append(
                    section,
                    create(
                        'div',
                        'ehtw-detail-timestamp',
                        formatTwitterFullDate(time)
                    )
                );
            }

            const stats = create(
                'div',
                'ehtw-detail-stats'
            );

            [
                [pages, 'Pages'],
                [ratingValue, 'Rating'],
                [ratingCount, Number(ratingCount.replace(/,/g, '')) === 1
                    ? 'Rating vote'
                    : 'Ratings'],
                [favoriteValue, 'Favorites'],
                [
                    String(commentCount),
                    commentCount === 1
                        ? 'Comment'
                        : 'Comments'
                ]
            ].forEach(([value, label]) => {
                const item = create(
                    'span',
                    'ehtw-detail-stat'
                );

                append(
                    item,
                    create('strong', '', value)
                );

                append(
                    item,
                    create('span', '', label)
                );

                append(stats, item);
            });

            append(section, stats);

            const reply = create(
                'a',
                'ehtw-detail-reply-count'
            );

            reply.href = '#cdiv';

            append(reply, makeIcon('reply'));

            append(
                reply,
                create(
                    'span',
                    '',
                    String(commentCount)
                )
            );

            append(section, reply);

            const native = create(
                'div',
                'ehtw-detail-native-meta'
            );

            move(mid, native);
            append(section, native);
            append(post, section);

            if (info) {
                const about = create(
                    'section',
                    'ehtw-side-card ehtw-about-card'
                );

                append(
                    about,
                    create(
                        'h2',
                        'ehtw-side-title',
                        'About'
                    )
                );

                move(info, about);
                append(right, about);

                const mobile = create(
                    'div',
                    'ehtw-mobile-about'
                );

                append(post, mobile);

                let host = right;

                function updateAboutPlacement() {
                    const target = window.matchMedia(
                        '(max-width: 999px)'
                    ).matches
                        ? mobile
                        : right;

                    if (host !== target) {
                        target.appendChild(about);
                        host = target;
                    }
                }

                registerMediaListener(
                    window.matchMedia('(max-width: 999px)'),
                    updateAboutPlacement
                );

                updateAboutPlacement();
            }

            void rating;
            void favorites;
        }

        function buildDetailTags(post) {
            const gd4 = qs('#gd4');
            if (!gd4) return;

            const section = create(
                'section',
                'ehtw-detail-tags'
            );
            const tagCount = qsa(
                '#taglist .gt, #taglist .gtl, #taglist .gtw',
                gd4
            ).length;

            const header = create('div', 'ehtw-detail-section-header');
            append(header, create('h2', 'ehtw-detail-section-title', 'Tags'));

            const expandButton = create(
                'button',
                'ehtw-detail-expand-button',
                'Expand'
            );
            expandButton.type = 'button';
            expandButton.setAttribute('aria-expanded', 'false');
            expandButton.addEventListener('click', (event) => {
                event.preventDefault();
                event.stopPropagation();
                const expanded = section.classList.toggle(
                    'ehtw-detail-tags-expanded'
                );
                section.classList.toggle(
                    'ehtw-detail-tags-collapsed',
                    !expanded
                );
                expandButton.textContent = expanded ? 'Collapse' : 'Expand';
                expandButton.setAttribute('aria-expanded', String(expanded));
            });

            append(section, header);

            move(gd4, section);

            qsa(
                '#taglist .gt, #taglist .gtl, #taglist .gtw',
                section
            ).forEach(tag => {
                addClassSafe(
                    tag,
                    'ehtw-detail-hashtag'
                );

                const anchor = qs('a', tag);
                if (!anchor) return;

                const parsed = parseTwitterTag(
                    textOf(anchor)
                );

                // 原 a、href、onclick 都保留。
                setTextSafe(
                    anchor,
                    '#' + parsed.display
                );
            });

            append(post, section);

            const tagList = qs('#taglist', section);
            if (tagCount && tagList) {
                section.classList.add('ehtw-detail-tags-collapsed');
                expandButton.hidden = true;
                append(header, expandButton);

                const updateTagExpandVisibility = () => {
                    const hasOverflow = tagList.scrollHeight > 112;
                    const expanded =
                        section.classList.contains('ehtw-detail-tags-expanded');

                    if (hasOverflow) {
                        section.classList.toggle(
                            'ehtw-detail-tags-collapsed',
                            !expanded
                        );
                        expandButton.hidden = false;
                        expandButton.textContent = expanded ? 'Collapse' : 'Expand';
                        expandButton.setAttribute(
                            'aria-expanded',
                            String(expanded)
                        );
                    } else {
                        section.classList.remove(
                            'ehtw-detail-tags-collapsed',
                            'ehtw-detail-tags-expanded'
                        );
                        expandButton.hidden = true;
                        expandButton.textContent = 'Expand';
                        expandButton.setAttribute('aria-expanded', 'true');
                    }
                };
                const scheduleTagExpandVisibilityUpdate = () => {
                    const schedule = window.requestAnimationFrame ||
                        ((callback) => window.setTimeout(callback, 0));
                    schedule(updateTagExpandVisibility);
                };

                scheduleTagExpandVisibilityUpdate();
                addSpaPageEventListener(
                    window,
                    'resize',
                    scheduleTagExpandVisibilityUpdate,
                    { passive: true }
                );
            }
        }

        // ============================================================
        // 10.1 详情操作链接
        // ============================================================

        function buildDetailOperations() {
            const detailGalleryHref = `${location.origin}${location.pathname}`;
            const gd5 = qs('#gd5');
            const gdr = qs('#gdr');
            const gdf = qs('#gdf');

            const timeline = qs(
                '.ehtw-detail-main',
                center
            );

            if (!timeline) return null;

            const detailPost = qs(
                '.ehtw-detail-post',
                timeline
            );

            if (detailPost && (gdr || gdf)) {
                const actions = create(
                    'div',
                    'ehtw-detail-actions'
                );

                const replyCount = qs('.ehtw-detail-reply-count');

                if (replyCount) {
                    actions.appendChild(replyCount);
                }

                if (gdr) {
                    const ratingAction = create(
                        'div',
                        'ehtw-detail-rating-action'
                    );

                    move(gdr, ratingAction);
                    append(actions, ratingAction);
                }

                if (gdf) {
                    const favoriteAction = create(
                        'button',
                        'ehtw-detail-favorite-action'
                    );

                    favoriteAction.type = 'button';
                    favoriteAction.title = textOf(
                        qs('#favoritelink'),
                        'Add to Favorites'
                    ).trim();

                    if (
                        !/add to favorites/i.test(favoriteAction.title)
                    ) {
                        favoriteAction.classList.add('ehtw-liked');
                    }

                    // 爱心就是收藏,走原站 #gdf 的 pop_fav()。
                    favoriteAction.addEventListener('click', (event) => {
                        event.preventDefault();
                        event.stopPropagation();
                        toggleBrowserFavorite(detailGalleryHref, favoriteAction);
                    }, true);

                    favoriteAction.appendChild(
                        makeIcon('heart')
                    );

                    const favoriteCount = readFavoriteCount(
                        textOf(qs('#favcount'))
                    );

                    if (favoriteCount !== null) {
                        const number = create(
                            'span',
                            'ehtw-detail-favorite-count',
                            formatCount(favoriteCount)
                        );

                        number.title =
                            favoriteCount.toLocaleString() +
                            ' favorites';

                        append(favoriteAction, number);
                    }

                    move(gdf, favoriteAction);
                    append(actions, favoriteAction);
                }

                append(detailPost, actions);
            }

            // Actions 收进卡片右上角,#gd5 原节点搬动,onclick 不变。
            const authorRow = detailPost
                ? qs('.ehtw-detail-author', detailPost)
                : null;

            if (gd5 && authorRow) {
                const menu = makeActionMenu();

                addClassSafe(gd5, 'ehtw-menu-native');
                move(gd5, menu.panel);
                append(authorRow, menu.wrap);

                menu.panel.addEventListener('click', (event) => {
                    event.stopPropagation();

                    if (event.target.closest('a')) {
                        closeActionMenus();
                    }
                });
            }

            return null;
        }

        // ============================================================
        // 10.2 详情缩略图和上下分页
        // ============================================================

        function buildDetailGrid(post) {
            const gdt = qs('#gdt');

            if (!gdt) return;

            let upperPagination = null;
            let lowerPagination = null;

            let previous = gdt.previousElementSibling;

            while (previous) {
                if (previous.classList?.contains('gtb')) {
                    upperPagination = previous;
                    break;
                }

                previous = previous.previousElementSibling;
            }

            let next = gdt.nextElementSibling;

            while (next) {
                if (next.classList?.contains('gtb')) {
                    lowerPagination = next;
                    break;
                }

                next = next.nextElementSibling;
            }

            if (!upperPagination || !lowerPagination) {
                const candidates = qsa('.gtb').filter(
                    node => !node.closest('#ehtw-shell')
                );

                if (!upperPagination) {
                    upperPagination = candidates.find(
                        node => !!(
                            node.compareDocumentPosition(gdt) &
                            Node.DOCUMENT_POSITION_FOLLOWING
                        )
                    ) || null;
                }

                if (!lowerPagination) {
                    lowerPagination = candidates.find(
                        node => !!(
                            node.compareDocumentPosition(gdt) &
                            Node.DOCUMENT_POSITION_PRECEDING
                        )
                    ) || null;
                }
            }

            let nextGalleryThumbPageUrl = (upperPagination || lowerPagination)
                ? findNextGalleryThumbPageUrl(upperPagination || lowerPagination)
                : null;
            let checkGalleryStripEdge = () => {};
            const seenGalleryThumbUrls = new Set();

            const section = create(
                'section',
                'ehtw-gallery-grid-section'
            );
            const expandedStorageKey = getGalleryGridExpandedStorageKey(
                location.pathname
            );
            let imagesInitiallyExpanded = false;
            try {
                imagesInitiallyExpanded = !!expandedStorageKey &&
                    window.sessionStorage?.getItem(expandedStorageKey) === 'true';
            } catch (_) {}

            const imageHeader = create('div', 'ehtw-detail-section-header');
            append(imageHeader, create('h2', 'ehtw-detail-section-title', 'Images'));
            const expandImagesButton = create(
                'button',
                'ehtw-detail-expand-button',
                'Expand'
            );
            expandImagesButton.type = 'button';
            if (imagesInitiallyExpanded) {
                section.classList.add('ehtw-gallery-grid-expanded');
                expandImagesButton.textContent = 'Collapse';
            }
            expandImagesButton.setAttribute(
                'aria-expanded',
                String(imagesInitiallyExpanded)
            );
            expandImagesButton.addEventListener('click', () => {
                const expanded = section.classList.toggle(
                    'ehtw-gallery-grid-expanded'
                );
                expandImagesButton.textContent = expanded ? 'Collapse' : 'Expand';
                expandImagesButton.setAttribute('aria-expanded', String(expanded));
                try {
                    if (expandedStorageKey) {
                        window.sessionStorage?.setItem(
                            expandedStorageKey,
                            String(expanded)
                        );
                    }
                } catch (_) {}
                checkGalleryStripEdge();
            });
            append(imageHeader, expandImagesButton);
            append(section, imageHeader);

            move(gdt, section);

            const horizontalWheelHandler = makeHorizontalStripWheelHandler(
                gdt,
                section
            );
            const horizontalWheelOptions = {
                capture: true,
                passive: false
            };
            addSpaPageEventListener(
                window,
                'wheel',
                horizontalWheelHandler,
                horizontalWheelOptions
            );
            domListeners.push({
                target: window,
                type: 'wheel',
                listener: horizontalWheelHandler,
                options: horizontalWheelOptions
            });

            // 封面就是第 1 页,图片条里不再重复。
            const cover = qs('.ehtw-detail-cover', post);

            if (cover) {
                setStyleSafe(cover, 'display', 'none');
            }

            const prepareGalleryThumb = (link, prepareImageNow = false) => {
                const href = link.getAttribute('href') || link.href;
                if (!href) return false;

                let canonicalHref = href;
                try {
                    canonicalHref = new URL(href, location.href).href;
                } catch (_) {}
                if (seenGalleryThumbUrls.has(canonicalHref)) return false;

                const thumb = qs('div', link);
                if (!thumb) return false;

                setStyleSafe(
                    link,
                    '--ehtw-w',
                    String(parseFloat(thumb.style.width) || 200)
                );
                setStyleSafe(
                    link,
                    '--ehtw-h',
                    String(parseFloat(thumb.style.height) || 280)
                );
                if (prepareImageNow) prepareImage(qs('img', link));
                seenGalleryThumbUrls.add(canonicalHref);
                return true;
            };

            qsa('a', gdt).forEach((link) => {
                prepareGalleryThumb(link);
            });

            // 保留原分页节点供回滚,但页面不再要求手动翻页。
            const pager = upperPagination
                ? buildGalleryPagination(upperPagination, section)
                : null;

            const hiddenPager =
                lowerPagination &&
                lowerPagination !== upperPagination
                    ? buildGalleryPagination(lowerPagination, section)
                    : null;

            if (hiddenPager) {
                addClassSafe(hiddenPager, 'ehtw-gallery-pagination-secondary');
                setStyleSafe(hiddenPager, 'display', 'none');
            }
            if (pager) setStyleSafe(pager, 'display', 'none');

            append(post, section);

            qsa('img', gdt).forEach(prepareImage);

            if (nextGalleryThumbPageUrl) {
                const continuationStatus = create('div', 'ehtw-gallery-continuation');
                continuationStatus.setAttribute('role', 'status');
                continuationStatus.setAttribute('aria-live', 'polite');
                continuationStatus.textContent = '';
                gdt.appendChild(continuationStatus);

                const seenGalleryPageUrls = new Set([
                    new URL(location.href).href
                ]);
                let loadingGalleryPage = false;
                let galleryPagesExhausted = false;
                let galleryPageLoadFailed = false;

                const setContinuationText = (text, state) => {
                    continuationStatus.dataset.state = state;
                    continuationStatus.replaceChildren();
                    continuationStatus.textContent = text;
                };

                const loadNextGalleryThumbPage = async () => {
                    if (
                        loadingGalleryPage ||
                        galleryPagesExhausted ||
                        !nextGalleryThumbPageUrl
                    ) {
                        return;
                    }

                    const requestedUrl = nextGalleryThumbPageUrl;
                    if (seenGalleryPageUrls.has(requestedUrl)) {
                        galleryPagesExhausted = true;
                        setContinuationText('End of gallery', 'done');
                        return;
                    }

                    loadingGalleryPage = true;
                    galleryPageLoadFailed = false;
                    seenGalleryPageUrls.add(requestedUrl);
                    setContinuationText('Loading…', 'loading');

                    try {
                        const response = await window.fetch(requestedUrl, {
                            credentials: 'same-origin'
                        });
                        if (!response?.ok) {
                            throw new Error(`Gallery page request failed: ${response?.status || 'no response'}`);
                        }

                        const pageText = await response.text();
                        const parsedPage = new window.DOMParser()
                            .parseFromString(pageText, 'text/html');
                        const pageGrid = qs('#gdt', parsedPage);
                        if (!pageGrid) {
                            throw new Error('The next page did not contain thumbnails');
                        }

                        const pagePager = qs('.gtb', parsedPage) || parsedPage;
                        const pageNextUrl = findNextGalleryThumbPageUrl(
                            pagePager,
                            requestedUrl
                        );
                        const previousTrackingState = skipTransactionTracking;
                        let appendedCount = 0;
                        skipTransactionTracking = true;
                        try {
                            qsa('a[href]', pageGrid).forEach((sourceLink) => {
                                const link = sourceLink.cloneNode(true);
                                if (!prepareGalleryThumb(link, true)) return;
                                gdt.insertBefore(link, continuationStatus);
                                appendedCount++;
                            });
                        } finally {
                            skipTransactionTracking = previousTrackingState;
                        }

                        if (!appendedCount) {
                            galleryPagesExhausted = true;
                            nextGalleryThumbPageUrl = null;
                            setContinuationText('End of gallery', 'done');
                            return;
                        }

                        nextGalleryThumbPageUrl = pageNextUrl &&
                            !seenGalleryPageUrls.has(pageNextUrl)
                                ? pageNextUrl
                                : null;
                        galleryPagesExhausted = !nextGalleryThumbPageUrl;
                        setContinuationText(
                            galleryPagesExhausted ? 'End of gallery' : '',
                            galleryPagesExhausted ? 'done' : 'idle'
                        );
                    } catch (error) {
                        console.warn('[EH Twitter Style] Gallery strip continuation failed', error);
                        seenGalleryPageUrls.delete(requestedUrl);
                        galleryPageLoadFailed = true;
                        setContinuationText('', 'error');
                        const retry = create('button', '', 'Retry');
                        retry.type = 'button';
                        retry.addEventListener('click', () => {
                            loadNextGalleryThumbPage();
                        });
                        continuationStatus.appendChild(retry);
                    } finally {
                        loadingGalleryPage = false;
                        if (!galleryPagesExhausted && !galleryPageLoadFailed) {
                            window.requestAnimationFrame?.(checkGalleryStripEdge);
                        }
                    }
                };

                checkGalleryStripEdge = () => {
                    if (galleryPagesExhausted || galleryPageLoadFailed) return;
                    if (section.classList.contains('ehtw-gallery-grid-expanded')) return;
                    const remaining = gdt.scrollWidth -
                        (gdt.scrollLeft + gdt.clientWidth);
                    const threshold = Math.max(600, gdt.clientWidth * 1.25);
                    if (remaining <= threshold) loadNextGalleryThumbPage();
                };

                const galleryStripScrollHandler = () => checkGalleryStripEdge();
                gdt.addEventListener('scroll', galleryStripScrollHandler, {
                    passive: true
                });
                domListeners.push({
                    target: gdt,
                    type: 'scroll',
                    listener: galleryStripScrollHandler,
                    options: { passive: true }
                });

                startGalleryStripContinuation = () => {
                    checkGalleryStripEdge();
                };
            }
        }

        // ============================================================
        // 10.3 评论
        // ============================================================

        // 回复的操作栏只放真实存在的功能:回复、分数、原站投票链接、分享。
        function buildCommentActions(comment, heading, c3) {
            const c4 = qs('.c4', comment);
            const c5 = qs('.c5', comment);
            const contact = qs('a[href*="showuser"]', c3);

            if (c4 && qs('a[name="ulcomment"]', c4)) {
                addClassSafe(c4, 'ehtw-comment-badge');
                move(c4, heading);
            }

            const voteUp = qs('[id^="comment_vote_up_"]', comment);
            const voteDown = qs('[id^="comment_vote_down_"]', comment);
            const editLink = c4
                ? qsa('a[href]', c4).find(
                    (link) =>
                        link !== voteUp &&
                        link !== voteDown &&
                        link.getAttribute('name') === null
                )
                : null;

            const menu = makeActionMenu();

            if (voteDown) {
                addMenuItem(menu.panel, 'Vote down', () => {
                    voteDown.click();
                });
            }

            if (editLink) {
                addMenuItem(
                    menu.panel,
                    textOf(editLink, 'Edit').replace(/[\[\]]/g, '').trim(),
                    () => {
                        editLink.click();
                    }
                );
            }

            if (contact) {
                addMenuItem(menu.panel, 'Contact Poster', () => {
                    window.open(contact.href, '_blank', 'noopener');
                });
            }

            if (menu.panel.children.length) {
                registerCreatedNode(menu.wrap);
                append(heading, menu.wrap);
            }

            const actions = registerCreatedNode(
                create('div', 'ehtw-comment-actions')
            );

            const reply = create('a', 'ehtw-comment-action');

            reply.href = '#ehtw-reply-box';
            reply.title = 'Reply';
            reply.appendChild(makeIcon('reply'));
            append(actions, reply);

            if (c5) {
                const like = create(
                    voteUp ? 'button' : 'span',
                    'ehtw-comment-action ehtw-comment-like'
                );

                like.appendChild(makeIcon('heart'));
                move(c5, like);

                // 爱心就是投票:走原站的 Vote+,未登录时没有这个链接。
                if (voteUp) {
                    const syncVoted = () => {
                        like.classList.toggle(
                            'ehtw-liked',
                            voteUp.style.color === 'blue' ||
                                voteUp.style.color === 'red'
                        );
                    };

                    like.type = 'button';
                    like.title = 'Vote';
                    like.addEventListener('click', () => {
                        voteUp.click();
                    });

                    new MutationObserver(syncVoted).observe(voteUp, {
                        attributes: true,
                        attributeFilter: ['style']
                    });

                    syncVoted();
                }

                append(actions, like);
            }

            if (c4 && comment.contains(c4) && !heading.contains(c4)) {
                addClassSafe(c4, 'ehtw-comment-votes-native');
                move(c4, actions);
            }

            comment.appendChild(actions);
        }

        function buildDetailComments(timeline) {
            const cdiv = qs('#cdiv');
            const postNew = qs('#postnewcomment');

            if (!cdiv && !postNew) return;

            const section = create(
                'section',
                'ehtw-comments-section'
            );

            append(
                section,
                makeSectionTitle('Replies')
            );

            if (cdiv) {
                move(cdiv, section);

                qsa('.c1', cdiv).forEach(comment => {
                    const c3 = qs('.c3', comment);
                    const author = qs('a', c3);

                    const authorName = textOf(
                        author,
                        'User'
                    );

                    const originalHeader = textOf(c3);

                    // 原站评论时间:
                    // Posted on 01 November 2026, 02:16 by:
                    // 按 UTC 解析,不依赖浏览器本地时区。
                    const utcMatch = originalHeader.match(
                        /Posted\s+on\s+(\d{1,2})\s+([A-Za-z]+)\s+(\d{4}),?\s+(\d{1,2}):(\d{2})\s+by:/i
                    );

                    let parsedDate = null;
                    let originalTime = '';

                    if (utcMatch) {
                        const monthNames = [
                            'january', 'february', 'march',
                            'april', 'may', 'june',
                            'july', 'august', 'september',
                            'october', 'november', 'december'
                        ];

                        const monthIndex = monthNames.indexOf(
                            utcMatch[2].toLowerCase()
                        );

                        if (monthIndex >= 0) {
                            parsedDate = new Date(Date.UTC(
                                Number(utcMatch[3]),
                                monthIndex,
                                Number(utcMatch[1]),
                                Number(utcMatch[4]),
                                Number(utcMatch[5])
                            ));

                            originalTime = utcMatch[0];
                        }
                    }

                    const avatar = registerCreatedNode(
                        create(
                            'div',
                            'ehtw-comment-avatar',
                            Array.from(authorName)[0]
                                ?.toUpperCase() || '?'
                        )
                    );

                    const heading = registerCreatedNode(
                        create(
                            'div',
                            'ehtw-comment-heading'
                        )
                    );

                    if (author) {
                        const nameWrap = registerCreatedNode(
                            create(
                                'span',
                                'ehtw-comment-author'
                            )
                        );

                        move(author, nameWrap);
                        append(heading, nameWrap);
                    } else {
                        append(
                            heading,
                            create(
                                'span',
                                'ehtw-comment-author',
                                authorName
                            )
                        );
                    }

                    append(
                        heading,
                        create(
                            'span',
                            'ehtw-comment-handle',
                            '@' + authorName.replace(/\s+/g, '_')
                        )
                    );

                    if (parsedDate && !Number.isNaN(parsedDate.getTime())) {
                        const marker = registerCreatedNode(
                            create(
                                'span',
                                'ehtw-dot',
                                '·'
                            )
                        );

                        append(heading, marker);

                        const elapsed = Date.now() -
                            (parsedDate.getTime() - SITE_UTC_OFFSET_MIN * 60000);

                        let relative = '';

                        if (elapsed >= 0 && elapsed < 60000) {
                            relative = 'now';
                        } else if (
                            elapsed >= 0 &&
                            elapsed < 3600000
                        ) {
                            relative =
                                Math.floor(elapsed / 60000) + 'm';
                        } else if (
                            elapsed >= 0 &&
                            elapsed < 86400000
                        ) {
                            relative =
                                Math.floor(elapsed / 3600000) + 'h';
                        } else {
                            const month = parsedDate.toLocaleString(
                                'en-US',
                                {
                                    month: 'short',
                                    timeZone: 'UTC'
                                }
                            );

                            relative =
                                month + ' ' + parsedDate.getUTCDate();

                            if (
                                parsedDate.getUTCFullYear() !==
                                new Date().getUTCFullYear()
                            ) {
                                relative += ', ' +
                                    parsedDate.getUTCFullYear();
                            }
                        }

                        const timeNode = registerCreatedNode(
                            create(
                                'span',
                                'ehtw-comment-time',
                                relative
                            )
                        );

                        timeNode.title = originalTime;

                        append(heading, timeNode);
                    }

                    const c2 = qs('.c2', comment);

                    if (c2) {
                        comment.insertBefore(avatar, c2);
                        comment.insertBefore(heading, c2);

                        addClassSafe(
                            comment,
                            'ehtw-comment-tweet'
                        );

                        buildCommentActions(comment, heading, c3);
                    }
                });
            }

            // X 的回复框在回复列表上方;表单节点原样搬动,onclick 不变。
            if (postNew) {
                const replyBox = create('div', 'ehtw-reply-box');

                replyBox.id = 'ehtw-reply-box';

                append(
                    replyBox,
                    create('span', 'ehtw-reply-avatar', 'E')
                );

                append(
                    replyBox,
                    create(
                        'span',
                        'ehtw-reply-placeholder',
                        'Post your reply'
                    )
                );

                move(postNew, replyBox);

                section.insertBefore(
                    replyBox,
                    cdiv || null
                );

                const form = qs('#formdiv');

                if (form) {
                    section.insertBefore(form, cdiv || null);
                }
            }

            append(timeline, section);
        }

        function transformDetail() {
            const timeline = create(
                'div',
                'ehtw-timeline ehtw-detail-main'
            );

            append(center, timeline);

            const post = create(
                'article',
                'ehtw-detail-post'
            );

            const category = qs('#gdc .cs');

            const categoryName = textOf(
                category,
                'Gallery'
            );

            const categoryClass =
                getCategoryClass(category);

            buildDetailAuthor(
                post,
                categoryName,
                categoryClass
            );

            const gd2 = qs('#gd2');

            if (gd2) {
                move(gd2, post);
            }

            buildDetailCover(post);
            buildDetailGrid(post);

            // 保持原始节点,改变其视觉呈现。
            buildDetailTags(post);

            buildDetailMeta(post);

            append(timeline, post);

            const updateOperationPlacement =
                buildDetailOperations();

            buildDetailComments(timeline);

            return updateOperationPlacement;
        }

        // ============================================================
        // 11. 其它显示模式
        // ============================================================

        function transformOtherList() {
            const content = create(
                'div',
                'ehtw-other-content'
            );

            append(center, content);

            const originalList = qs(
                'table.itg, .itg'
            );

            const navs = qsa('.searchnav');

            if (navs[0]) {
                buildPagination(navs[0], content);
            }

            if (originalList) {
                move(originalList, content);
            }

            if (
                (location.pathname.replace(/\/+$/, '') || '/') === '/toplist.php' &&
                new URLSearchParams(location.search).has('tl')
            ) {
                requestHiddenMyTags().then((hiddenTags) => {
                    if (!hiddenTags.size || !content.isConnected) return;
                    filterToplistResultRows(content, hiddenTags);
                }).catch(() => {});
            }

            if (navs[1]) {
                buildPagination(navs[1], content);
            }

            // 不转换原站列表条目,只补上评论数和收藏数。
            qsa('table.itg tr', content).forEach((row) => {
                const link = qs('a[href*="/g/"]', row);
                const target = qs('.gl3e, .gl4m', row);

                if (!link || !target) return;

                setAttributeSafe(
                    row,
                    'data-ehtw-href',
                    link.getAttribute('href')
                );

                const stats = create('span', 'ehtw-row-stats');

                [
                    ['reply', 'ehtw-stat-comments'],
                    ['heart', 'ehtw-stat-favorites']
                ].forEach(([icon, className]) => {
                    const item = create('span', 'ehtw-row-stat');

                    item.appendChild(makeIcon(icon));
                    append(item, create('span', className));
                    append(stats, item);
                });

                append(target, stats);
                registerCreatedNode(stats);
            });
        }

        // ============================================================
        // 12. 隐藏广告位
        // ============================================================

        function hideAdvertisements() {
            qsa('ins[data-width][data-height]').forEach(
                node => {
                    setStyleSafe(
                        node,
                        'display',
                        'none'
                    );
                }
            );

            if (table) {
                qsa('tr', table).forEach(row => {
                    if (
                        !isGalleryListRow(row) &&
                        !qs('th', row)
                    ) {
                        setStyleSafe(
                            row,
                            'display',
                            'none'
                        );
                    }
                });
            }
        }

        // ============================================================
        // 13. 保留原站隐藏容器
        // ============================================================

        function preserveOriginalStructures() {
            if (isList || isOtherList) {
                const toppane = qs('#toppane');

                if (toppane) {
                    if (isFollowingFeedPage) {
                        setStyleSafe(toppane, 'display', 'none');
                    }

                    const heading = qs(
                        'h1.ih',
                        toppane
                    );

                    if (heading) {
                        setStyleSafe(
                            heading,
                            'display',
                            'none'
                        );
                    }
                }
            }

            // 不删除原始事件节点。
        }

        // ============================================================
        // 14. 初始化
        // ============================================================

        const trending = collectTags(document);

        buildNavigation();
        buildHeader();

        const updateSearchPlacement = buildSearch();

        let updateOperationPlacement = null;

        if (isList) {
            transformList();
        } else if (isDetail) {
            updateOperationPlacement = transformDetail();
        } else if (isGeneric) {
            transformGenericPage();
        } else {
            transformOtherList();
        }

        buildTrending(trending);

        hideAdvertisements();
        preserveOriginalStructures();

        const updateFooterPlacement = buildFooter();

        if (
            !document.getElementById('ehtw-shell') ||
            !document.getElementById('ehtw-center')
        ) {
            throw new Error(
                'Layout initialization failed'
            );
        }

        committed = true;
        publishSpaPageCleanup(() => rollback?.());
        releasePageBoot();

        function handleNativeCardSettingAction() {
            const action = new URLSearchParams(
                location.hash.replace(/^#/, '')
            );
            const followTag = action.get('ehtw_follow_creator') ||
                action.get('ehtw_follow_artist');
            const followUploader = action.get('ehtw_follow_uploader');
            const blockedUploader = action.get('ehtw_block_uploader');
            if (!followTag && !followUploader && !blockedUploader) return;

            const actionState = recoverSpaHistoryState(location.href, window.history.state);
            if (safeReplaceHistoryState(
                window.history,
                actionState,
                '',
                location.pathname + location.search,
                'native-card-setting-action'
            )) rememberSpaHistoryState(location.href, actionState);
            // The action hash is not part of the rendered route. Keep the URL
            // observer aligned after consuming this instruction.
            spaRenderedHref = location.href;

            const message = create('div', 'ehtw-native-setting-message');
            append(center, message);
            const announce = (value) => {
                message.textContent = value;
                message.scrollIntoView({ block: 'nearest' });
            };

            if (followTag || followUploader) {
                const watchedTag = followTag || `uploader:${followUploader}`;
                const isUploaderFollow = !!followUploader;
                if (
                    !isMyTagsPage ||
                    (isUploaderFollow
                        ? !followUploader.trim()
                        : !/^(?:artist|group):/i.test(followTag))
                ) {
                    announce(isUploaderFollow
                        ? '无法识别上传者,请从画廊卡片或详情页重新操作。'
                        : '无法识别作者标签,请从画廊卡片重新操作。');
                    return;
                }

                const tagSetEnabled = qs('#tagset_enable');
                if (tagSetEnabled && !tagSetEnabled.checked) {
                    announce('My Tags 当前已停用标签关注。请先在原生页面启用标签设置。');
                    return;
                }

                const rows = qsa(
                    '#usertags_outer > div[id^="usertag_"]'
                );
                const row = rows.find((candidate) => {
                    const preview = qs('[id^="tagpreview_"]', candidate);
                    const name = preview?.getAttribute('title') ||
                        preview?.textContent || '';
                    if (isUploaderFollow) {
                        const match = String(name).trim().match(/^uploader:(.+)$/i);
                        return normalizeUploaderName(match?.[1]) ===
                            normalizeUploaderName(followUploader);
                    }
                    return normalizeCreatorFollowTag(name) === normalizeCreatorFollowTag(followTag);
                });

                let watch = row
                    ? qs('input[id^="tagwatch_"]', row)
                    : qs('#tagwatch_0');
                if (!watch) {
                    announce('My Tags 没有可用的作者标签输入行。');
                    return;
                }

                if (row && watch.checked) {
                    announce(isUploaderFollow
                        ? `上传者已在 My Tags 中设为 Watched:${followUploader}`
                        : `作者已在原生 My Tags 中设为 Watched:${followTag.replace(/^(?:artist|group):/i, '')}`);
                    return;
                }

                if (!row) {
                    const nameInput = qs('#tagname_new');
                    if (!nameInput) {
                        announce('找不到 My Tags 的新标签输入框。');
                        return;
                    }
                    nameInput.value = watchedTag;
                    nameInput.dispatchEvent(new Event('input', { bubbles: true }));
                    nameInput.dispatchEvent(new Event('change', { bubbles: true }));
                }

                watch.checked = true;
                watch.dispatchEvent(new Event('change', { bubbles: true }));
                const rowId = row?.id.match(/_(\d+)$/)?.[1] || '0';
                const save = qs(`#tagsave_${rowId}`);
                if (!save || save.disabled) {
                    announce('关注标签已填入,但原生保存按钮未启用;请检查 My Tags 页面后手动保存。');
                    return;
                }

                announce(isUploaderFollow
                    ? `正在将上传者 ${followUploader} 加入 My Tags / Watched…`
                    : `正在将 ${followTag.replace(/^(?:artist|group):/i, '')} 加入 My Tags / Watched…`);
                save.click();
                return;
            }

            if (!/^\/uconfig\.php$/i.test(location.pathname)) {
                announce('无法识别上传者设置页面,请从画廊卡片重新操作。');
                return;
            }

            const textarea = qs('#xu');
            if (!textarea || !blockedUploader.trim()) {
                announce('找不到 Excluded Uploaders 输入框。');
                return;
            }

            const lines = textarea.value.split(/\r?\n/)
                .map((line) => line.trim())
                .filter(Boolean);
            if (lines.some((line) => line.toLowerCase() === blockedUploader.toLowerCase())) {
                announce(`${blockedUploader} 已在原生 Excluded Uploaders 列表中。`);
                return;
            }

            if (lines.length >= 100) {
                announce('Excluded Uploaders 已达到 100 个条目的上限,请先在原生设置中移除一项。');
                return;
            }

            textarea.value = [...lines, blockedUploader].join('\n');
            textarea.dispatchEvent(new Event('input', { bubbles: true }));
            textarea.dispatchEvent(new Event('change', { bubbles: true }));
            const apply = qsa('button, input[type="submit"]')
                .find((button) => (button.textContent || button.value || '')
                    .trim().toLowerCase() === 'apply');
            if (!apply || apply.disabled) {
                announce(`${blockedUploader} 已填入 Excluded Uploaders,但找不到可用的 Apply 按钮;请在原生页面手动应用。`);
                return;
            }

            announce(`正在将 ${blockedUploader} 加入原生 Excluded Uploaders…`);
            apply.click();
        }

        try {
            handleNativeCardSettingAction();
        } catch (error) {
            console.warn('[EH Twitter Style] Native card setting action failed', error);
        }

        if (
            typeof updateSearchPlacement === 'function'
        ) {
            updateSearchPlacement();
        }

        if (
            typeof updateOperationPlacement === 'function'
        ) {
            updateOperationPlacement();
        }

        if (
            typeof updateFooterPlacement === 'function'
        ) {
            updateFooterPlacement();
        }

        if (isList || isOtherList) {
            // 统计是附加功能,失败时不应连带回滚整个界面。
            try {
                registerAdditionalStatsTweets = startGalleryStatsLoader();
            } catch (error) {
                console.warn('[EH Twitter Style] Stats loader failed', error);
            }
        }

        if (isList && typeof startListInfiniteContinuation === 'function') {
            try {
                startListInfiniteContinuation();
            } catch (error) {
                console.warn('[EH Twitter Style] Infinite list setup failed', error);
            }
        }

        if (
            isFollowingFeedPage &&
            typeof startFollowingAuthorTimeline === 'function'
        ) {
            startFollowingAuthorTimeline();
        }

        if (isDetail && typeof startGalleryStripContinuation === 'function') {
            try {
                startGalleryStripContinuation();
            } catch (error) {
                console.warn('[EH Twitter Style] Gallery strip continuation setup failed', error);
            }
        }

        console.info(
            '[EH Twitter Style] Applied:',
            isDetail
                ? 'detail'
                : isList
                    ? 'list'
                    : 'other list mode'
        );

        // 转换成功后,原始列表表格已不再参与显示;移除它可避免把原表和新卡片
        // 同时留在 DOM。事务日志只用于初始化失败回滚,成功后释放其节点引用。
        if (isList && table?.isConnected) {
            table.remove();
            table = null;
        }
        skipTransactionTracking = true;

    } catch (error) {
        releasePageBoot();
        // ========================================================
        // 0.5.0:catch 不依赖 try 内部声明
        // ========================================================

        console.error(
            '[EH Twitter Style] Initialization failed:',
            error
        );

        try {
            rollback();
        } catch (rollbackError) {
            console.error(
                '[EH Twitter Style] Rollback failed:',
                rollbackError
            );

            // 最后兜底:这些变量均在 try 外层声明。
            try {
                if (styleElement) {
                    styleElement.remove();
                }
            } catch (_) {}

            try {
                document.documentElement.classList.remove(
                    'ehtw-enabled',
                    'ehtw-exhentai',
                    'ehtw-list',
                    'ehtw-detail',
                    'ehtw-myhome',
                    'ehtw-mytags',
                    'ehtw-myuploads',
                    'ehtw-bounty',
                    'ehtw-other',
                    'ehtw-generic',
                    'ehtw-login'
                );
            } catch (_) {}

            try {
                delete window[RUN_KEY];
            } catch (_) {
                window[RUN_KEY] = false;
            }
        }
    }
    }

    let spaNavigationInProgress = false;
    let spaNavigationHistoryMode = null;
    let pendingSpaPop = null;
    const HISTORY_STATE_SCROLL_DEBOUNCE_MS = 200;
    const SCROLL_RESTORE_QUIET_MS = 650;
    let pendingScrollHistoryStateWrite = null;
    let scrollHistoryStateWriteTimer = null;

    function historyStateWriteSuppressionReason(href = location.href) {
        if (spaNavigationInProgress) return 'navigation-in-progress';
        if (window.__EHTW_SCROLL_SAVE_LOCK__) return 'scroll-restore-active';
        if (Date.now() < scrollRestoreProtectionUntil) return 'scroll-restore-quiet-window';
        if (isEdgeGestureProtected()) return 'left-edge-gesture-active';
        if (
            document.documentElement?.classList.contains('ehtw-navigation-pending') &&
            href !== spaRenderedHref
        ) return 'pending-destination-does-not-match-rendered-route';
        return '';
    }

    function flushPendingScrollHistoryStateWrite(source = 'scroll-debounce') {
        const pending = pendingScrollHistoryStateWrite;
        if (!pending) return false;
        const suppressedBy = historyStateWriteSuppressionReason(pending.href);
        if (suppressedBy) {
            if (navLogEnabled) navLog('scroll-history-state-write-skipped', {
                source: pending.source,
                requestedBy: source,
                scrollY: pending.scrollY,
                reason: suppressedBy
            });
            return false;
        }
        if (location.href !== pending.href || spaRenderedHref !== pending.href) {
            pendingScrollHistoryStateWrite = null;
            if (navLogEnabled) navLog('scroll-history-state-write-discarded', {
                source: pending.source,
                requestedBy: source,
                targetHref: pending.href,
                currentHref: location.href,
                renderedHref: spaRenderedHref
            });
            return false;
        }
        const current = recoverSpaHistoryState(pending.href, window.history.state);
        const nextState = {
            ...(pending.state || {}),
            ...(current || {}),
            ehtwSpa: true,
            ehtwScrollY: pending.scrollY
        };
        const replaced = safeReplaceHistoryState(window.history, nextState, '', pending.href, `scroll-${source}`);
        if (!replaced) return false;
        rememberSpaHistoryState(pending.href, nextState);
        pendingScrollHistoryStateWrite = null;
        if (navLogEnabled) navLog('scroll-history-state-write', {
            source: pending.source,
            requestedBy: source,
            scrollY: pending.scrollY,
            historyState: summarizeNavLogState(nextState)
        });
        return true;
    }

    function schedulePendingScrollHistoryStateWrite(delay = HISTORY_STATE_SCROLL_DEBOUNCE_MS) {
        if (!pendingScrollHistoryStateWrite) return;
        window.clearTimeout(scrollHistoryStateWriteTimer);
        scrollHistoryStateWriteTimer = window.setTimeout(() => {
            scrollHistoryStateWriteTimer = null;
            flushPendingScrollHistoryStateWrite('scroll-debounce');
        }, Math.max(0, Number(delay) || 0));
    }

    function queueScrollHistoryStateWrite(href, state, scrollY, source, force = false) {
        pendingScrollHistoryStateWrite = {
            href,
            state: { ...(state || {}) },
            scrollY: Math.max(0, Math.round(Number(scrollY) || 0)),
            source
        };
        if (force) {
            window.clearTimeout(scrollHistoryStateWriteTimer);
            scrollHistoryStateWriteTimer = null;
            flushPendingScrollHistoryStateWrite(source || 'forced');
        } else if (!historyStateWriteSuppressionReason(href)) {
            schedulePendingScrollHistoryStateWrite();
        } else {
            window.clearTimeout(scrollHistoryStateWriteTimer);
            scrollHistoryStateWriteTimer = null;
        }
    }

    function protectScrollStateAfterRestore() {
        scrollRestoreProtectionUntil = Math.max(scrollRestoreProtectionUntil, Date.now() + SCROLL_RESTORE_QUIET_MS);
        if (pendingScrollHistoryStateWrite) {
            schedulePendingScrollHistoryStateWrite(scrollRestoreProtectionUntil - Date.now());
        }
    }
    flushDeferredScrollHistoryReplace = (source) => flushPendingScrollHistoryStateWrite(source || 'edge-gesture-quiet-end');

    // Suspended list and detail pages: the live transformed DOM and listeners
    // are parked in a shadow root and restored when Back returns to that entry.
    const MAX_SUSPENDED_SPA_PAGES = 3;
    const SPA_STATE_CLASSES = ['ehtw-navigation-pending', 'ehtw-page-boot', 'ehtw-reader-boot', 'ehtw-chrome-scroll-hidden'];
    let spaInlineScriptSequence = 0;

    function findSuspendedSpaPageByHref(href) {
        let absoluteHref = '';
        try { absoluteHref = new URL(href, location.href).href; } catch (_) { return null; }
        for (const record of suspendedSpaPages.values()) {
            if (record.href === absoluteHref) return record;
        }
        return null;
    }

    function collectInlineLexicalNames(scriptText) {
        const tokens = [];
        const source = String(scriptText || '');
        let braceDepth = 0;
        let parenDepth = 0;
        let bracketDepth = 0;
        for (let index = 0; index < source.length;) {
            const character = source[index];
            if (character === '/' && source[index + 1] === '/') {
                index += 2;
                while (index < source.length && source[index] !== '\n') index += 1;
                continue;
            }
            if (character === '/' && source[index + 1] === '*') {
                index += 2;
                while (index < source.length && !(source[index] === '*' && source[index + 1] === '/')) index += 1;
                index = Math.min(source.length, index + 2);
                continue;
            }
            if (character === '"' || character === "'" || character === '`') {
                const quote = character;
                index += 1;
                while (index < source.length) {
                    if (source[index] === '\\') index += 2;
                    else if (source[index++] === quote) break;
                }
                continue;
            }
            if (/[A-Za-z_$]/.test(character)) {
                const start = index++;
                while (index < source.length && /[\w$]/.test(source[index])) index += 1;
                tokens.push({
                    value: source.slice(start, index),
                    topLevel: braceDepth === 0 && parenDepth === 0 && bracketDepth === 0
                });
                continue;
            }
            if (character === '{') braceDepth += 1;
            else if (character === '}') braceDepth = Math.max(0, braceDepth - 1);
            else if (character === '(') parenDepth += 1;
            else if (character === ')') parenDepth = Math.max(0, parenDepth - 1);
            else if (character === '[') bracketDepth += 1;
            else if (character === ']') bracketDepth = Math.max(0, bracketDepth - 1);
            index += 1;
        }

        const names = new Set();
        for (let index = 0; index < tokens.length - 1; index += 1) {
            if (
                tokens[index].topLevel &&
                (tokens[index].value === 'let' || tokens[index].value === 'const') &&
                tokens[index + 1].topLevel
            ) {
                names.add(tokens[index + 1].value);
            }
        }
        return names;
    }

    function isExecutableInlineClassicScript(script) {
        if (!script || script.src) return false;
        const type = String(script.getAttribute?.('type') || '').trim().toLowerCase();
        if (!type || type === 'text/javascript' || type === 'application/javascript' || type === 'text/ecmascript' || type === 'application/ecmascript') {
            return true;
        }
        return false;
    }

    function getSpaScriptSourceKey(value, base = location.href) {
        try {
            const url = new URL(value, base);
            return `${url.origin}${url.pathname}`;
        } catch (_) {
            return String(value || '');
        }
    }

    const spaExecutedScriptSources = new Set();
    function rememberSpaExternalScriptSources(root = document) {
        Array.from(root?.querySelectorAll?.('script[src]') || []).forEach((script) => {
            const source = script.getAttribute?.('src') || script.src;
            if (source) {
                spaExecutedScriptSources.add(getSpaScriptSourceKey(source, root?.baseURI || location.href));
            }
        });
    }

    function rememberSpaInlineLexicalBindings(root = document) {
        Array.from(root?.querySelectorAll?.('script') || []).forEach((script) => {
            if (!isExecutableInlineClassicScript(script)) return;
            collectInlineLexicalNames(script.textContent || '').forEach((name) => spaInlineLexicalBindings.add(name));
        });
    }

    function rewriteInlineLexicalNames(scriptText, aliases) {
        if (!aliases?.size) return String(scriptText || '');
        const source = String(scriptText || '');
        let output = '';
        let index = 0;
        let previousSignificant = '';
        while (index < source.length) {
            const character = source[index];
            if (character === '/' && source[index + 1] === '/') {
                const end = source.indexOf('\n', index + 2);
                const next = end < 0 ? source.length : end;
                output += source.slice(index, next);
                index = next;
                continue;
            }
            if (character === '/' && source[index + 1] === '*') {
                const close = source.indexOf('*/', index + 2);
                const next = close < 0 ? source.length : close + 2;
                output += source.slice(index, next);
                index = next;
                continue;
            }
            if (character === '"' || character === "'" || character === '`') {
                const quote = character;
                const start = index++;
                while (index < source.length) {
                    if (source[index] === '\\') index += 2;
                    else if (source[index++] === quote) break;
                }
                output += source.slice(start, index);
                previousSignificant = 'literal';
                continue;
            }
            if (/[A-Za-z_$]/.test(character)) {
                const start = index++;
                while (index < source.length && /[\w$]/.test(source[index])) index += 1;
                const identifier = source.slice(start, index);
                let next = index;
                while (/\s/.test(source[next] || '')) next += 1;
                const isProperty = previousSignificant === '.' || source[next] === ':';
                output += !isProperty && aliases.has(identifier) ? aliases.get(identifier) : identifier;
                previousSignificant = identifier;
                continue;
            }
            output += character;
            if (!/\s/.test(character)) previousSignificant = character;
            index += 1;
        }
        return output;
    }

    const spaInlineLexicalBindings = new Set();
    rememberSpaInlineLexicalBindings();

    function evictSuspendedSpaPage(record) {
        try { record.cleanup?.(); } catch (_) {}
        try { record.host?.remove(); } catch (_) {}
    }

    function suspendCurrentSpaPage(entryId, cleanup, href, scrollY, historyState = {}) {
        try {
            if (!spaSuspendCacheEnabled) return false;
            if (!entryId || typeof cleanup !== 'function' || !document.body) return false;
            const root = document.documentElement;
            const leavingPath = new URL(href, location.href).pathname;
            // Never park detail markup under a listing URL. If a previous SPA
            // transition left URL and DOM out of sync, rebuild the destination.
            if (
                isListLikePath(leavingPath) &&
                (root.classList.contains('ehtw-detail') || document.querySelector('#gmid'))
            ) {
                return false;
            }
            const earlyStyle = document.getElementById('ehtw-page-boot-style');
            const headNodes = Array.from(
                document.head.querySelectorAll('link[rel~="stylesheet"], style')
            ).filter((node) => node !== earlyStyle);
            const listeners = (window.__EHTW_PAGE_LISTENERS__ || []).slice();
            const viewport = document.querySelector('meta[name="viewport"]');
            const record = {
                href,
                scrollY,
                historyState: { ...(historyState || {}) },
                cleanup,
                listeners,
                headNodes,
                title: document.title,
                lang: root.lang,
                viewportContent: viewport?.getAttribute('content') || '',
                rootClass: Array.from(root.classList)
                    .filter((name) => !SPA_STATE_CLASSES.includes(name))
                    .join(' '),
                bodyAttrs: Array.from(document.body.attributes).map((a) => [a.name, a.value]),
                host: null,
                shadow: null
            };
            // 1.17.250: moving nodes into the shadow host drops the scroll
            // offsets of inner scrollers (e.g. the #gdt thumbnail strip).
            record.innerScrollPositions = [];
            document.body.querySelectorAll('*').forEach((element) => {
                const left = element.scrollLeft || 0;
                const top = element.scrollTop || 0;
                if (left > 0 || top > 0) record.innerScrollPositions.push({ element, left, top });
            });
            const host = document.createElement('div');
            host.setAttribute('aria-hidden', 'true');
            host.style.cssText = 'position:fixed;top:1000000px;left:0;width:100%;height:0;' +
                'overflow:hidden;visibility:hidden;pointer-events:none;';
            const shadow = host.attachShadow({ mode: 'open' });
            // Page-level listeners must not fire while another page is shown.
            listeners.forEach(({ target, type, listener, options }) => {
                try { target.removeEventListener?.(type, listener, options); } catch (_) {}
            });
            headNodes.forEach((node) => node.remove());
            shadow.append(...Array.from(document.body.childNodes));
            root.appendChild(host);
            record.host = host;
            record.shadow = shadow;
            suspendedSpaPages.delete(entryId);
            suspendedSpaPages.set(entryId, record);
            while (suspendedSpaPages.size > MAX_SUSPENDED_SPA_PAGES) {
                const [oldId, oldRecord] = suspendedSpaPages.entries().next().value;
                suspendedSpaPages.delete(oldId);
                evictSuspendedSpaPage(oldRecord);
            }
            return true;
        } catch (error) {
            console.warn('[EH Twitter Style] suspend failed', error);
            return false;
        }
    }

    async function resumeSuspendedSpaPage(entryId, targetUrl) {
        if (!spaSuspendCacheEnabled) return false;
        const record = suspendedSpaPages.get(entryId);
        if (!isSuspendedSpaPageCompatible(record, targetUrl)) {
            if (record) {
                suspendedSpaPages.delete(entryId);
                evictSuspendedSpaPage(record);
                console.warn('[EH Twitter Style] Discarded mismatched suspended page', {
                    target: targetUrl.href,
                    saved: record.href,
                    savedClass: record.rootClass
                });
            }
            return false;
        }
        suspendedSpaPages.delete(entryId);

        const cleanup = window.__EHTW_PAGE_CLEANUP__;
        window.__EHTW_SPA_READY__ = false;
        window.__EHTW_PAGE_CLEANUP__ = null;
        try { cleanup?.(); } catch (_) {}

        const root = document.documentElement;
        const earlyStyle = document.getElementById('ehtw-page-boot-style');
        Array.from(document.head.querySelectorAll('link[rel~="stylesheet"], style'))
            .filter((node) => node !== earlyStyle)
            .forEach((node) => node.remove());
        record.headNodes.forEach((node) => document.head.appendChild(node));
        if (earlyStyle) document.head.appendChild(earlyStyle);
        await Promise.all(record.headNodes
            .filter((node) => node.tagName === 'LINK')
            .map((link) => new Promise((resolve) => {
                if (link.sheet) return resolve();
                let timer = null;
                const finish = () => {
                    link.removeEventListener('load', finish);
                    link.removeEventListener('error', finish);
                    if (timer !== null) window.clearTimeout(timer);
                    resolve();
                };
                link.addEventListener('load', finish, { once: true });
                link.addEventListener('error', finish, { once: true });
                timer = window.setTimeout(finish, 800);
            })));

        root.className = record.rootClass;
        root.lang = record.lang || root.lang;
        document.title = record.title || document.title;
        const viewport = document.querySelector('meta[name="viewport"]');
        if (viewport && record.viewportContent) viewport.setAttribute('content', record.viewportContent);
        Array.from(document.body.attributes).forEach((a) => document.body.removeAttribute(a.name));
        record.bodyAttrs.forEach(([name, value]) => document.body.setAttribute(name, value));
        if (navLogEnabled) {
            const nodes = Array.from(record.shadow.querySelectorAll('iframe, meta[http-equiv], script, form'));
            const iframes = nodes.filter((node) => node.tagName === 'IFRAME');
            const metas = nodes.filter((node) => node.tagName === 'META');
            const scripts = nodes.filter((node) => node.tagName === 'SCRIPT');
            const forms = nodes.filter((node) => node.tagName === 'FORM');
            navLog('suspended-nodes-reinsert', {
                counts: { iframe: iframes.length, metaHttpEquiv: metas.length, script: scripts.length, form: forms.length },
                iframeSrcPaths: iframes.map((node) => navLogPath(node.getAttribute('src') || '')),
                metaHttpEquiv: metas.map((node) => String(node.getAttribute('http-equiv') || '').slice(0, 40)),
                metaRefreshPaths: metas.filter((node) => String(node.getAttribute('http-equiv') || '').toLowerCase() === 'refresh')
                    .map((node) => node.getAttribute('content')?.match(/url\s*=\s*['"]?([^;'"\s]+)/i)?.[1])
                    .filter(Boolean).map(navLogPath),
                scriptSrcPaths: scripts.filter((node) => node.getAttribute('src')).map((node) => navLogPath(node.getAttribute('src'))),
                formActionPaths: forms.map((node) => navLogPath(node.getAttribute('action') || location.href))
            });
        }
        document.body.replaceChildren(...Array.from(record.shadow.childNodes));
        record.host.remove();
        const restoreInnerScrollPositions = () => {
            (record.innerScrollPositions || []).forEach(({ element, left, top }) => {
                if (!element.isConnected) return;
                try {
                    if (left) element.scrollLeft = left;
                    if (top) element.scrollTop = top;
                } catch (_) {}
            });
        };
        restoreInnerScrollPositions();
        window.requestAnimationFrame?.(restoreInnerScrollPositions);

        record.listeners.forEach(({ target, type, listener, options }) => {
            try { target.addEventListener?.(type, listener, options); } catch (_) {}
        });
        window.__EHTW_PAGE_LISTENERS__ = record.listeners;
        window.__EHTW_PAGE_CLEANUP__ = record.cleanup;
        window['__EHTW_STYLE_050__'] = true;
        window.__EHTW_SPA_READY__ = true;
        return record;
    }

    function isSuspendedSpaPageCompatible(record, targetUrl) {
        if (!record || record.href !== targetUrl.href) return false;
        const classes = new Set(String(record.rootClass || '').split(/\s+/).filter(Boolean));
        const savedIsGalleryDetail = classes.has('ehtw-detail') ||
            !!record.shadow?.querySelector?.('#gmid');
        const targetIsGallery = /^\/g\/\d+\//i.test(targetUrl.pathname);
        if (isListLikePath(targetUrl.pathname) && savedIsGalleryDetail) return false;
        if (targetIsGallery && !savedIsGalleryDetail) return false;
        return true;
    }

    async function waitForSpaFetchRetry(targetHref, retryDelayMs, retryReason) {
        if (location.href !== targetHref) {
            if (navLogEnabled) navLog('navigate-fetch-retry-abandoned', {
                target: targetHref,
                reason: retryReason,
                activeHref: location.href,
                check: 'before-backoff'
            });
            const error = new Error('SPA fetch retry cancelled because the active history URL changed');
            error.name = 'AbortError';
            throw error;
        }
        if (navLogEnabled) navLog('navigate-fetch-retry-scheduled', {
            target: targetHref,
            retryDelayMs,
            reason: retryReason
        });
        await new Promise((resolve) => window.setTimeout(resolve, retryDelayMs));
        if (location.href !== targetHref) {
            if (navLogEnabled) navLog('navigate-fetch-retry-abandoned', {
                target: targetHref,
                reason: retryReason,
                activeHref: location.href,
                check: 'after-backoff'
            });
            const error = new Error('SPA fetch retry cancelled because the active history URL changed');
            error.name = 'AbortError';
            throw error;
        }
        await new Promise((resolve) => {
            const raf = window.requestAnimationFrame || ((callback) => window.setTimeout(callback, 0));
            raf(resolve);
        });
        if (location.href !== targetHref) {
            if (navLogEnabled) navLog('navigate-fetch-retry-abandoned', {
                target: targetHref,
                reason: retryReason,
                activeHref: location.href,
                check: 'after-animation-frame'
            });
            const error = new Error('SPA fetch retry cancelled after frame because the active history URL changed');
            error.name = 'AbortError';
            throw error;
        }
        if (navLogEnabled) navLog('navigate-fetch-retry-ready', { target: targetHref, reason: retryReason });
    }

    async function fetchSpaRouteResponse(targetHref) {
        const requestOptions = {
            credentials: 'same-origin',
            cache: 'default',
            redirect: 'manual'
        };
        for (let attempt = 0; attempt < 2; attempt += 1) {
            try {
                const response = await window.fetch(targetHref, requestOptions);
                if (
                    attempt === 0 &&
                    [502, 503, 504].includes(Number(response?.status))
                ) {
                    await waitForSpaFetchRetry(targetHref, 700, `http-${response.status}`);
                    continue;
                }
                return response;
            } catch (error) {
                if (attempt > 0 || error?.name !== 'TypeError') throw error;
                // Safari may abort a fetch during the edge-swipe snapshot. Give
                // the gesture/visibility transition a longer settling window,
                // then retry only if this history destination is still active.
                await waitForSpaFetchRetry(targetHref, 700, 'typeerror');
            }
        }
        return null;
    }

    async function navigateSpa(targetValue, options = {}) {
        let targetUrl;
        try {
            targetUrl = new URL(targetValue, location.href);
        } catch (_) {
            return false;
        }
        if (
            targetUrl.origin !== location.origin ||
            !isGallerySiteOrigin(location.origin)
        ) return false;
        if (spaNavigationInProgress) {
            const queuedMode = options.historyMode || 'push';
            if (navLogEnabled) navLog('navigate-queued', {
                target: targetUrl.href,
                mode: queuedMode,
                replacingPending: pendingSpaPop ? { ...pendingSpaPop } : null
            });
            if (queuedMode === 'pop') {
                if (navLogEnabled && pendingSpaPop) navLog('pendingSpaPop-superseded', {
                    previous: { href: pendingSpaPop.href, mode: pendingSpaPop.historyMode || null },
                    by: targetUrl.href,
                    input: 'pop'
                });
                pendingSpaPop = {
                    href: targetUrl.href,
                    scrollY: options.scrollY,
                    historyState: recoverSpaHistoryState(targetUrl.href, options.historyState || window.history.state),
                    historyMode: 'pop'
                };
                if (navLogEnabled) navLog('pendingSpaPop-queued', {
                    input: 'pop', pending: { href: pendingSpaPop.href, scrollY: pendingSpaPop.scrollY ?? null, mode: pendingSpaPop.historyMode }
                });
            } else if (queuedMode === 'push') {
                // Keep the in-flight push entry as the sole entry for a rapid
                // click burst. If a traversal happened first, pushing naturally
                // truncates the abandoned forward entry.
                const currentState = recoverSpaHistoryState(location.href, window.history.state);
                const afterTraversal = pendingSpaPop?.historyMode === 'pop' ||
                    spaNavigationHistoryMode === 'pop' ||
                    currentState.ehtwSpaRoute !== true;
                if (navLogEnabled && pendingSpaPop) navLog('pendingSpaPop-superseded', {
                    previous: { href: pendingSpaPop.href, mode: pendingSpaPop.historyMode || null },
                    by: targetUrl.href,
                    input: 'click'
                });
                const nextState = afterTraversal
                    ? {
                        ...currentState,
                        ehtwSpa: true,
                        ehtwSpaRoute: true,
                        ehtwSpaPreviousHref: location.href,
                        ehtwSpaReturnBackTarget: false,
                        ehtwEntryId: newSpaEntryId(),
                        ehtwSpaDepth: (readFiniteNumber(currentState.ehtwSpaDepth) ?? 0) + 1,
                        ehtwScrollY: 0
                    }
                    : {
                        ...currentState,
                        ehtwSpa: true,
                        ehtwSpaRoute: true,
                        ehtwSpaPreviousHref: currentState.ehtwSpaPreviousHref || spaRenderedHref,
                        ehtwEntryId: currentState.ehtwEntryId || newSpaEntryId(),
                        ehtwScrollY: 0
                    };
                if (afterTraversal) window.history.pushState(nextState, '', targetUrl.href);
                else safeReplaceHistoryState(window.history, nextState, '', targetUrl.href, 'queued-click-supersede');
                rememberSpaHistoryState(targetUrl.href, nextState);
                pendingSpaPop = {
                    href: targetUrl.href,
                    scrollY: 0,
                    historyState: nextState,
                    historyMode: 'push'
                };
                if (navLogEnabled) navLog('pendingSpaPop-queued', {
                    input: 'click', afterTraversal, pending: { href: pendingSpaPop.href, scrollY: 0, mode: pendingSpaPop.historyMode }
                });
            }
            return false;
        }
        if (!window.__EHTW_SPA_READY__) {
            if (options.historyMode === 'pop' && location.href !== spaRenderedHref) {
                pendingSpaPop = {
                    href: location.href,
                    scrollY: Number(options.scrollY) || getSavedSpaScroll(location.href),
                    historyState: recoverSpaHistoryState(location.href, options.historyState || window.history.state),
                    historyMode: 'pop'
                };
                if (navLogEnabled) navLog('pendingSpaPop-queued', {
                    input: 'pop-not-ready', pending: { href: pendingSpaPop.href, scrollY: pendingSpaPop.scrollY, mode: 'pop' }
                });
            }
            if (navLogEnabled) navLog('navigate-abandoned-not-ready', { target: targetUrl.href, mode: options.historyMode || 'push' });
            return false;
        }

        // document-start userscripts may be initialized before native body
        // scripts exist. Capture their bindings right before the first swap.
        rememberSpaInlineLexicalBindings();
        rememberSpaExternalScriptSources();
        const historyMode = options.historyMode || 'push';
        let renderedPathname = location.pathname;
        try { renderedPathname = new URL(spaRenderedHref, location.href).pathname; } catch (_) {}
        const leavingReaderRoute = /^\/s\//.test(renderedPathname);
        const enteringReaderRoute = /^\/s\//.test(targetUrl.pathname);
        if (leavingReaderRoute && !enteringReaderRoute) controlMainWorldReader('reader-leave');
        else if (!leavingReaderRoute && enteringReaderRoute) controlMainWorldReader('reader-enter');
        if (typeof controlMainWorldReader === 'function') controlMainWorldReader('route-leave');
        // Persist the current route's latest scroll before navigation begins.
        // This runs before the navigation lock, and the helper itself refuses
        // to call replaceState during edge gestures or restore quiet windows.
        cancelActiveSpaScrollRestore?.();
        if (historyMode !== 'pop') saveCurrentScrollForSpa('navigate-before', true);
        const leavingHref = location.href;
        const leavingState = recoverSpaHistoryState(leavingHref, window.history.state);
        const scrollAtStart = window.scrollY || 0;
        const leavingPathname = location.pathname;
        let leavingEntryIdUsed = leavingState.ehtwEntryId || null;
        let historyPrepared = false;
        let navigationOutcome = 'in-progress';
        const popHistoryState = historyMode === 'pop'
            ? recoverSpaHistoryState(targetUrl.href, options.historyState || window.history.state)
            : null;
        if (navLogEnabled) navLog('navigate-start', {
            target: targetUrl.href,
            mode: historyMode,
            options: { scrollY: options.scrollY ?? null, restoreScrollY: options.restoreScrollY ?? null },
            historyState: summarizeNavLogState(leavingState)
        });
        spaNavigationInProgress = true;
        spaNavigationHistoryMode = historyMode;
        window.__EHTW_SCROLL_SAVE_LOCK__ = true;
        let scrollRestoreOwnsLock = false;
        document.documentElement.classList.add('ehtw-navigation-pending');
        const queueActiveAddressIfStale = () => {
            if (location.href === targetUrl.href) return false;
            if (navLogEnabled && pendingSpaPop) navLog('pendingSpaPop-superseded', {
                previous: { href: pendingSpaPop.href, mode: pendingSpaPop.historyMode || null },
                by: location.href,
                input: 'stale-active-address'
            });
            if (navLogEnabled) navLog('navigate-abandoned-stale-address', {
                target: targetUrl.href,
                activeHref: location.href,
                activeState: summarizeNavLogState(window.history.state)
            });
            pendingSpaPop = {
                href: location.href,
                scrollY: Number(recoverSpaHistoryState(location.href, window.history.state).ehtwScrollY) || getSavedSpaScroll(location.href),
                historyState: recoverSpaHistoryState(location.href, window.history.state),
                historyMode: 'pop'
            };
            if (navLogEnabled) navLog('pendingSpaPop-queued', {
                input: 'stale-active-address', pending: { href: pendingSpaPop.href, scrollY: pendingSpaPop.scrollY, mode: pendingSpaPop.historyMode }
            });
            document.documentElement.classList.remove('ehtw-navigation-pending');
            window.clearTimeout?.(navigationResetTimer);
            navigationResetTimer = null;
            return true;
        };
        try {
            if (historyMode === 'pop') {
                const suspendedMatch = spaSuspendCacheEnabled ? Array.from(suspendedSpaPages.entries())
                    .find(([, record]) => record.href === targetUrl.href) : null;
                const entryId = spaSuspendCacheEnabled ? (popHistoryState.ehtwEntryId || suspendedMatch?.[0]) : null;
                if (entryId && suspendedSpaPages.has(entryId)) {
                    const resumed = await resumeSuspendedSpaPage(entryId, targetUrl);
                    if (resumed) {
                        spaRenderedHref = targetUrl.href;
                        const resumedHistoryState = recoverSpaHistoryState(targetUrl.href, resumed.historyState || popHistoryState);
                        const resumedScrollY = Number(resumed.scrollY) || Number(popHistoryState.ehtwScrollY) ||
                            Number(options.scrollY) || getSavedSpaScroll(targetUrl.href);
                        queueScrollHistoryStateWrite(targetUrl.href, resumedHistoryState, resumedScrollY, 'suspended-pop-state-repair');
                        scrollRestoreOwnsLock = true;
                        restoreSpaScrollPosition(
                            resumedScrollY,
                            'suspended-pop'
                        );
                        navigationOutcome = 'resumed-suspended-page';
                        window.__EHTW_CONTINUE_GALLERY_BACK_TRAVERSAL__?.();
                        return true;
                    }
                }
            }
            if (historyMode === 'replace') {
                const suspendedEntry = spaSuspendCacheEnabled ? Array.from(suspendedSpaPages.entries())
                    .find(([, record]) => record.href === targetUrl.href) : null;
                if (suspendedEntry) {
                    const [entryId] = suspendedEntry;
                    const resumed = await resumeSuspendedSpaPage(entryId, targetUrl);
                    if (resumed) {
                        const sourceState = resumed.historyState || {};
                        const resumedState = {
                            ...sourceState,
                            ehtwSpa: true,
                            ehtwSpaRoute: false,
                            ehtwSpaPreviousHref: options.returnBackHref || sourceState.ehtwSpaPreviousHref || '',
                            ehtwSpaReturnBackTarget: options.returnBackTarget === true,
                            ehtwEntryId: entryId,
                            ehtwSpaDepth: readFiniteNumber(sourceState.ehtwSpaDepth) ?? 0,
                            ehtwScrollY: Number(resumed.scrollY) || Number(options.restoreScrollY) || 0
                        };
                        if (safeReplaceHistoryState(window.history, resumedState, '', targetUrl.href, 'suspended-replace')) {
                            rememberSpaHistoryState(targetUrl.href, resumedState);
                        }
                        try {
                            window.sessionStorage?.removeItem(galleryBackTraversalStorageKey);
                        } catch (_) {}
                        scrollRestoreOwnsLock = true;
                        restoreSpaScrollPosition(
                            Number(resumed.scrollY) || Number(options.restoreScrollY) || 0,
                            'suspended-replace'
                        );
                        spaRenderedHref = targetUrl.href;
                        navigationOutcome = 'resumed-suspended-page';
                        return true;
                    }
                }
            }
            const previousScrollY = scrollAtStart || window.scrollY || 0;
            if (historyMode === 'push') {
                // Add the entry while the initiating click still has transient
                // user activation. Safari can skip async-created pushState items
                // during its native Back gesture.
                if (!/^\/s\//i.test(location.pathname)) saveSpaScroll(location.href, previousScrollY);
                const previousGalleryId = getGalleryIdFromUrl(location.href);
                const targetGalleryId = getGalleryIdFromUrl(targetUrl.href);
                const leavingDepth = readFiniteNumber(leavingState.ehtwSpaDepth) ?? 0;
                const leavingEntryId = leavingState.ehtwEntryId || newSpaEntryId();
                leavingEntryIdUsed = leavingEntryId;
                if (
                    targetGalleryId &&
                    targetGalleryId !== previousGalleryId &&
                    isListLikePath(leavingPathname)
                ) {
                    try {
                        window.sessionStorage?.setItem(
                            `ehtw-gallery-return-v1:${targetGalleryId}`,
                            leavingHref
                        );
                        window.sessionStorage?.setItem(
                            galleryReturnDepthKey(targetGalleryId),
                            String(leavingDepth)
                        );
                    } catch (_) {}
                }
                const state = {
                    ...leavingState,
                    ehtwSpa: true,
                    ehtwSpaRoute: true,
                    ehtwSpaPreviousHref: leavingHref,
                    ehtwSpaReturnBackTarget: false,
                    ehtwEntryId: newSpaEntryId(),
                    ehtwSpaDepth: leavingDepth + 1,
                    ehtwScrollY: 0
                };
                const preparedLeavingState = {
                    ...leavingState,
                    ehtwSpa: true,
                    ehtwEntryId: leavingEntryId,
                    ehtwSpaDepth: leavingDepth,
                    ehtwScrollY: previousScrollY
                };
                rememberSpaHistoryState(leavingHref, preparedLeavingState);
                window.history.pushState(state, '', targetUrl.href);
                rememberSpaHistoryState(targetUrl.href, state);
                historyPrepared = true;
                if (isListLikePath(targetUrl.pathname)) {
                    try {
                        window.sessionStorage?.setItem(
                            spaBackTargetStorageKey(targetUrl.href),
                            leavingHref
                        );
                    } catch (_) {}
                }
            }
            const response = await fetchSpaRouteResponse(targetUrl.href);
            if (!response?.ok) throw new Error(`SPA route request failed: ${response?.status || 'no response'}`);
            const pageText = await response.text();
            if ((historyPrepared || historyMode === 'pop') && queueActiveAddressIfStale()) {
                navigationOutcome = 'abandoned-stale-address';
                return false;
            }
            const nextDocument = new window.DOMParser().parseFromString(pageText, 'text/html');
            const nextPath = targetUrl.pathname;
            const isReaderTarget = /^\/s\//i.test(nextPath);
            const targetSupported = isReaderTarget
                ? !!(nextDocument.querySelector('#img') || nextDocument.querySelector('#i3 img'))
                : !!(
                    nextDocument.querySelector('#nb') ||
                    nextDocument.querySelector('table.itg.gltc, table.itg.gltm, table.itg.glte, div.itg.gld, #searchbox form') ||
                    nextDocument.querySelector('form[name="ipb_login_form"]') ||
                    /^\/g\/\d+\/[0-9a-f]+\/?$/i.test(nextPath)
                );
            if (!targetSupported) throw new Error('SPA target page is not supported');

            // E-Hentai can return a valid site shell for the wrong page type.
            // Never install gallery metadata into a list URL just because #nb exists.
            const responseIsGalleryDetail = !!nextDocument.querySelector('#gmid');
            const targetIsGalleryDetail = /^\/g\/\d+\//i.test(nextPath);
            if (
                (isListLikePath(nextPath) && responseIsGalleryDetail) ||
                (targetIsGalleryDetail && !responseIsGalleryDetail)
            ) {
                throw new Error('SPA response content does not match the target route');
            }

            // From here on the current DOM is being replaced by targetUrl.
            // Keep this marker pinned to the route whose content is in flight,
            // so a queued Back is replayed even if it returns to the old URL.
            spaRenderedHref = targetUrl.href;

            if (historyMode === 'replace') {
                const replacementState = {
                    ...(window.history.state || {}),
                    ehtwSpa: true,
                    ehtwSpaRoute: false,
                    ehtwSpaPreviousHref: options.returnBackHref || '',
                    ehtwSpaReturnBackTarget: options.returnBackTarget === true,
                    ehtwEntryId: newSpaEntryId(),
                    ehtwSpaDepth: 0,
                    ehtwScrollY: Number(options.restoreScrollY) || 0
                };
                if (safeReplaceHistoryState(window.history, replacementState, '', targetUrl.href, 'spa-route-replace')) {
                    rememberSpaHistoryState(targetUrl.href, replacementState);
                }
                historyPrepared = true;
            }

            const cleanup = window.__EHTW_PAGE_CLEANUP__;
            window.__EHTW_SPA_READY__ = false;
            window.__EHTW_PAGE_CLEANUP__ = null;
            const leavingIsListOrGalleryDetail = isListLikePath(leavingPathname) ||
                /^\/g\/\d+\//i.test(leavingPathname);
            const suspended = historyMode === 'push' &&
                leavingIsListOrGalleryDetail &&
                suspendCurrentSpaPage(leavingEntryIdUsed, cleanup, leavingHref, previousScrollY, leavingState);
            if (!suspended) cleanup?.();
            try {
                const traversalGalleryId = window.sessionStorage?.getItem('ehtw-gallery-back-traversal-v1');
                if (traversalGalleryId && getGalleryIdFromUrl(targetUrl.href) !== traversalGalleryId) {
                    window.sessionStorage?.removeItem('ehtw-gallery-back-traversal-v1');
                }
            } catch (_) {}

            const root = document.documentElement;
            const preserveTouchUi = hasTouchInput || root.classList.contains('ehtw-touch-ui');
            root.className = nextDocument.documentElement.className || '';
            if (preserveTouchUi) root.classList.add('ehtw-touch-ui');
            root.classList.add(isReaderTarget ? 'ehtw-reader-boot' : 'ehtw-page-boot');
            root.lang = nextDocument.documentElement.lang || root.lang || 'en';
            document.title = nextDocument.title || document.title;

            const earlyStyle = document.getElementById('ehtw-page-boot-style');
            Array.from(document.head.querySelectorAll('link[rel~="stylesheet"], style'))
                .filter((node) => node !== earlyStyle)
                .forEach((node) => node.remove());
            Array.from(nextDocument.head.querySelectorAll('link[rel~="stylesheet"], style'))
                .forEach((node) => document.head.appendChild(document.importNode(node, true)));
            const targetViewport = nextDocument.querySelector('meta[name="viewport"]');
            let viewport = document.querySelector('meta[name="viewport"]');
            if (targetViewport) {
                if (!viewport) {
                    viewport = document.createElement('meta');
                    viewport.name = 'viewport';
                    document.head.appendChild(viewport);
                }
                viewport.setAttribute('content', targetViewport.getAttribute('content') || 'width=device-width, initial-scale=1');
            }
            if (earlyStyle) document.head.appendChild(earlyStyle);

            const nextBody = nextDocument.body;
            const targetHeadScripts = Array.from(
                nextDocument.head.querySelectorAll('script[src]')
            );
            const targetBodyScripts = Array.from(nextBody.querySelectorAll('script'));
            // Scripts can leave document.scripts when a list is parked in its
            // shadow root. Keep a route-independent registry so native
            // top-level bindings (for example ehg_index.c.js's `matchyear`)
            // are not executed again in this Window.
            rememberSpaExternalScriptSources();
            const existingScriptSources = new Set(
                spaExecutedScriptSources
            );
            const inlineLexicalAliases = new Map();
            targetBodyScripts.forEach((script) => {
                if (!isExecutableInlineClassicScript(script)) return;
                collectInlineLexicalNames(script.textContent || '').forEach((name) => {
                    if (spaInlineLexicalBindings.has(name)) {
                        if (!inlineLexicalAliases.has(name)) {
                            spaInlineScriptSequence += 1;
                            inlineLexicalAliases.set(name, `${name}__ehtwSpa${spaInlineScriptSequence}`);
                        }
                    } else {
                        spaInlineLexicalBindings.add(name);
                    }
                });
            });
            Array.from(document.body.attributes).forEach((attribute) => {
                document.body.removeAttribute(attribute.name);
            });
            Array.from(nextBody.attributes).forEach((attribute) => {
                document.body.setAttribute(attribute.name, attribute.value);
            });
            const children = Array.from(nextBody.childNodes).map((node) =>
                document.importNode(node, true)
            );
            document.body.replaceChildren(...children);

            // Native inline body scripts may call helpers provided by scripts
            // in the target page's head. Load missing same-origin dependencies
            // before replaying those body scripts in this same-document route.
            if (typeof controlMainWorldReader === 'function') controlMainWorldReader('route-scripts-start');
            for (const inertScript of targetHeadScripts) {
                const sourceScriptUrl = new URL(
                    inertScript.getAttribute('src'),
                    targetUrl.href
                ).href;
                const sourceScriptKey = getSpaScriptSourceKey(sourceScriptUrl, targetUrl.href);
                if (
                    new URL(sourceScriptUrl).origin !== location.origin ||
                    existingScriptSources.has(sourceScriptKey)
                ) continue;

                const liveScript = document.createElement('script');
                Array.from(inertScript.attributes).forEach((attribute) =>
                    liveScript.setAttribute(attribute.name, attribute.value)
                );
                liveScript.async = false;
                liveScript.removeAttribute('defer');
                liveScript.src = sourceScriptUrl;
                existingScriptSources.add(sourceScriptKey);
                spaExecutedScriptSources.add(sourceScriptKey);
                await new Promise((resolve) => {
                    let finished = false;
                    let timeout = null;
                    const finish = () => {
                        if (finished) return;
                        finished = true;
                        if (timeout !== null) window.clearTimeout(timeout);
                        resolve();
                    };
                    liveScript.addEventListener('load', finish, { once: true });
                    liveScript.addEventListener('error', finish, { once: true });
                    timeout = window.setTimeout(finish, 2000);
                    document.head.appendChild(liveScript);
                });
            }

            // Recreate page-provided scripts so native inline behavior is present
            // before the userscript transforms the fetched page.
            const importedBodyScripts = Array.from(document.body.querySelectorAll('script'));
            for (let scriptIndex = 0; scriptIndex < targetBodyScripts.length; scriptIndex += 1) {
                const inertScript = targetBodyScripts[scriptIndex];
                const inertTarget = importedBodyScripts[scriptIndex];
                if (!inertTarget) continue;
                const sourceScriptUrl = inertScript.getAttribute('src')
                    ? new URL(inertScript.getAttribute('src'), targetUrl.href).href
                    : '';
                const sourceScriptKey = sourceScriptUrl
                    ? getSpaScriptSourceKey(sourceScriptUrl, targetUrl.href)
                    : '';
                if (sourceScriptUrl && existingScriptSources.has(sourceScriptKey)) {
                    inertTarget.remove();
                    continue;
                }
                const liveScript = document.createElement('script');
                Array.from(inertScript.attributes).forEach((attribute) =>
                    liveScript.setAttribute(attribute.name, attribute.value)
                );
                if (sourceScriptUrl) {
                    liveScript.removeAttribute('defer');
                    liveScript.async = false;
                    const loaded = new Promise((resolve) => {
                        let finished = false;
                        let timeout = null;
                        const finish = () => {
                            if (finished) return;
                            finished = true;
                            if (timeout !== null) window.clearTimeout(timeout);
                            resolve();
                        };
                        liveScript.addEventListener('load', finish, { once: true });
                        liveScript.addEventListener('error', finish, { once: true });
                        timeout = window.setTimeout(finish, 2000);
                    });
                    liveScript.src = sourceScriptUrl;
                    existingScriptSources.add(sourceScriptKey);
                    spaExecutedScriptSources.add(sourceScriptKey);
                    inertTarget.replaceWith(liveScript);
                    await loaded;
                    continue;
                } else {
                    liveScript.textContent = rewriteInlineLexicalNames(
                        inertScript.textContent || '',
                        inlineLexicalAliases
                    );
                }
                inertTarget.replaceWith(liveScript);
            }

            if (typeof controlMainWorldReader === 'function') controlMainWorldReader('route-scripts-end');

            if ((historyPrepared || historyMode === 'pop') && queueActiveAddressIfStale()) return false;
            document.documentElement.dataset.ehtwScriptVersion = '1.17.280';
            window[ '__EHTW_STYLE_050__' ] = false;
            window.__EHTW_SPA_READY__ = false;
            runMain();
            if (typeof controlMainWorldReader === 'function') controlMainWorldReader('route-snapshot');
            if (!window.__EHTW_SPA_READY__) {
                throw new Error('SPA route initialization did not finish');
            }
            if (historyMode === 'pop') {
                spaRenderedHref = location.href;
                const destinationHistoryState = recoverSpaHistoryState(
                    targetUrl.href,
                    window.history.state || popHistoryState
                );
                const destinationScrollY = Number(options.scrollY) ||
                    Number(destinationHistoryState.ehtwScrollY) ||
                    getSavedSpaScroll(targetUrl.href);
                rememberSpaHistoryState(targetUrl.href, destinationHistoryState);
                if (!window.history.state?.ehtwSpa || !window.history.state?.ehtwEntryId) {
                    queueScrollHistoryStateWrite(targetUrl.href, destinationHistoryState, destinationScrollY, 'pop-state-repair');
                }
                scrollRestoreOwnsLock = true;
                restoreSpaScrollPosition(destinationScrollY, 'popstate-or-reconcile');
                // SPA popstate does not emit pageshow. Continue the multi-entry
                // gallery-back traversal so Back reaches the source list.
                window.__EHTW_CONTINUE_GALLERY_BACK_TRAVERSAL__?.();
            } else if (Number(options.restoreScrollY) > 0) {
                scrollRestoreOwnsLock = true;
                restoreSpaScrollPosition(options.restoreScrollY, 'push-restore');
            } else {
                window.scrollTo?.(0, 0);
                if (historyMode === 'push') saveSpaScroll(location.href, 0);
            }
            spaRenderedHref = location.href;
            navigationOutcome = 'rendered';
            return true;
        } catch (error) {
            if ((historyPrepared || historyMode === 'pop') && location.href !== targetUrl.href) {
                queueActiveAddressIfStale();
                navigationOutcome = 'abandoned-after-error';
                return false;
            }
            navigationOutcome = 'document-fallback';
            if (navLogEnabled) navLog('navigate-fallback', {
                target: targetUrl.href,
                mode: historyMode,
                reason: String(error?.message || error),
                historyPrepared,
                fallbackScrollY: Number(options.scrollY) || Number(options.restoreScrollY) || Number(window.history.state?.ehtwScrollY) || getSavedSpaScroll(targetUrl.href)
            });
            console.warn('[EH Twitter Style] SPA navigation failed; falling back to document navigation', error);
            window.__EHTW_SPA_READY__ = false;
            const fallbackScrollY = Number(options.scrollY) ||
                Number(options.restoreScrollY) ||
                Number(window.history.state?.ehtwScrollY) ||
                getSavedSpaScroll(targetUrl.href);
            if (
                (historyMode === 'pop' || historyMode === 'replace') &&
                isListLikePath(targetUrl.pathname) &&
                fallbackScrollY > 0
            ) {
                try {
                    window.sessionStorage?.setItem(
                        spaScrollPageshowRestoreKey(targetUrl.href),
                        '1'
                    );
                } catch (_) {}
            }
            if (historyMode === 'replace' || historyMode === 'pop' || historyPrepared) {
                if (navLogEnabled) navLog('script-navigation-call', { method: 'location.replace', source: 'navigateSpa-fallback', targetPath: targetUrl.pathname, stack: navLogStack() });
                location.replace(targetUrl.href);
            } else {
                if (navLogEnabled) navLog('script-navigation-call', { method: 'location.assign', source: 'navigateSpa-fallback', targetPath: targetUrl.pathname, stack: navLogStack() });
                location.assign(targetUrl.href);
            }
            return false;
        } finally {
            if (navLogEnabled) navLog('navigate-finish', {
                target: targetUrl.href,
                mode: historyMode,
                outcome: navigationOutcome,
                activeHref: location.href,
                renderedHref: spaRenderedHref,
                historyPrepared
            });
            if (navLogEnabled) {
                try {
                    window.sessionStorage?.setItem(NAV_LOG_HEARTBEAT_KEY, JSON.stringify({
                        t: Date.now(),
                        href: location.href,
                        suspendedCount: suspendedSpaPages.size,
                        approxImageCount: document.images?.length || 0
                    }));
                } catch (_) {}
            }
            spaNavigationInProgress = false;
            spaNavigationHistoryMode = null;
            if (!scrollRestoreOwnsLock) window.__EHTW_SCROLL_SAVE_LOCK__ = false;
            if (pendingScrollHistoryStateWrite) flushPendingScrollHistoryStateWrite('navigation-finish');
            const pending = pendingSpaPop;
            pendingSpaPop = null;
            if (pending && location.href !== spaRenderedHref) {
                if (navLogEnabled) navLog('pending-pop-replay-scheduled', {
                    pending: { href: pending.href, scrollY: pending.scrollY ?? null, mode: pending.historyMode || null },
                    activeHref: location.href,
                    renderedHref: spaRenderedHref
                });
                window.setTimeout(() => {
                    const destinationHref = location.href;
                    if (navLogEnabled) navLog('pending-pop-replay', {
                        pending: { href: pending.href, scrollY: pending.scrollY ?? null, mode: pending.historyMode || null },
                        destinationHref,
                        historyState: summarizeNavLogState(window.history.state)
                    });
                    navigateSpa(destinationHref, {
                        historyMode: 'pop',
                        historyState: recoverSpaHistoryState(destinationHref, window.history.state || pending.historyState),
                        scrollY: Number(recoverSpaHistoryState(destinationHref, window.history.state || pending.historyState).ehtwScrollY) ||
                            (pending.href === destinationHref ? Number(pending.scrollY) : 0) ||
                            getSavedSpaScroll(destinationHref)
                    });
                }, 0);
            } else if (pending && navLogEnabled) {
                navLog('pending-pop-discarded-already-rendered', {
                    pending: { href: pending.href, mode: pending.historyMode || null },
                    renderedHref: spaRenderedHref
                });
            }
        }
    }

    function replayPendingSpaPopIfReady(source = 'ready') {
        if (!pendingSpaPop || pendingSpaPop.historyMode !== 'pop' ||
            spaNavigationInProgress || !window.__EHTW_SPA_READY__) return false;
        const pending = pendingSpaPop;
        const destinationHref = location.href;
        if (destinationHref === spaRenderedHref) {
            pendingSpaPop = null;
            if (navLogEnabled) navLog('pending-pop-discarded-already-rendered', {
                source,
                pending: { href: pending.href, mode: pending.historyMode },
                renderedHref: spaRenderedHref
            });
            return false;
        }
        pendingSpaPop = null;
        if (navLogEnabled) navLog('pending-pop-replay', {
            source,
            pending: { href: pending.href, scrollY: pending.scrollY ?? null, mode: pending.historyMode },
            destinationHref,
            historyState: summarizeNavLogState(window.history.state)
        });
        window.setTimeout(() => {
            const activeHref = location.href;
            navigateSpa(activeHref, {
                historyMode: 'pop',
                historyState: recoverSpaHistoryState(activeHref, window.history.state || pending.historyState),
                scrollY: Number(recoverSpaHistoryState(activeHref, window.history.state || pending.historyState).ehtwScrollY) ||
                    (pending.href === activeHref ? Number(pending.scrollY) : 0) ||
                    getSavedSpaScroll(activeHref)
            });
        }, 0);
        return true;
    }

    window.__EHTW_SPA_NAVIGATE__ = navigateSpa;

    // Safari can change the active same-document history URL during its edge
    // Back gesture without delivering popstate for some script-created entries.
    // Reconcile only when the router is idle and the URL differs from the DOM.
    setUserScriptInterval(() => {
        if (
            !isGallerySiteOrigin(location.origin) ||
            !window.__EHTW_SPA_READY__ ||
            location.href === spaRenderedHref
        ) return;
        if (spaNavigationInProgress) {
            if (navLogEnabled) navLog('reconcile-skipped', {
                reason: 'navigation-in-progress',
                currentState: summarizeNavLogState(window.history.state)
            });
            return;
        }
        if (navLogEnabled) navLog('reconcile-trigger', {
            reason: 'idle-url-differs-from-rendered-route',
            currentState: summarizeNavLogState(window.history.state)
        });
        navigateSpa(location.href, {
            historyMode: 'pop',
            scrollY: Number(window.history.state?.ehtwScrollY) ||
                getSavedSpaScroll(location.href)
        });
    }, 250);

    if (document.readyState === 'loading') {
        addUserScriptListener(document, 'DOMContentLoaded', () => {
            controlMainWorldReader('route-scripts-end');
            runMain();
        }, { once: true });
        if (navLogEnabled) addUserScriptListener(document, 'DOMContentLoaded', () => navLog('navigation-log-enabled', { version: document.documentElement.dataset.ehtwScriptVersion, suspendCache: spaSuspendCacheEnabled ? 'on' : 'off' }), { once: true });
        if (navLogPanelRequested) addUserScriptListener(document, 'DOMContentLoaded', showNavLogPanel, { once: true });
    } else {
        controlMainWorldReader('route-scripts-end');
        runMain();
        if (navLogEnabled) navLog('navigation-log-enabled', { version: document.documentElement.dataset.ehtwScriptVersion, suspendCache: spaSuspendCacheEnabled ? 'on' : 'off' });
        if (navLogPanelRequested) showNavLogPanel();
    }
})();