SB.SB-Enhance-Script

sb.sb 深度视觉、阅读与列表增强:完整主题系统、布局、过滤、图片预览、公开资料卡与阅读辅助。

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, Greasemonkey alebo Violentmonkey.

Na inštaláciu tohto skriptu budete musieť nainštalovať rozšírenie, ako je napríklad Tampermonkey alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey alebo Userscripts.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie, ako napríklad Tampermonkey.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie správcu používateľských skriptov.

(Už mám správcu používateľských skriptov, nechajte ma ho nainštalovať!)

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

(Už mám správcu používateľských štýlov, nechajte ma ho nainštalovať!)

// ==UserScript==
// @name         SB.SB-Enhance-Script
// @namespace    https://sb.sb/
// @version      2.4.24
// @description  sb.sb 深度视觉、阅读与列表增强:完整主题系统、布局、过滤、图片预览、公开资料卡与阅读辅助。
// @author       com.com.ee
// @match        https://sb.sb/*
// @grant        GM_addStyle
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_registerMenuCommand
// @run-at       document-start
// @license      MIT
// ==/UserScript==

(function () {
    'use strict';

    if (/^\/pages\/top(?:\/|$)/.test(window.location.pathname)) {
        return;
    }

    var STORAGE_KEY = 'sb-sb-enhance-settings-v2';
    var SETTINGS_VERSION = 8;
    var CHECKIN_STORAGE_PREFIX = 'sb-sb-enhance-checkin-v1-';
    var SAVE_DELAY = 180;
    var PROFILE_CACHE_TTL = 10 * 60 * 1000;

    var RANGE_DEFINITIONS = [
        { key: 'maxWidth', label: '页面最大宽度', min: 960, max: 2160, step: 20, unit: 'px' },
        { key: 'headerWidth', label: '顶部栏内容宽度', min: 720, max: 2160, step: 20, unit: 'px' },
        { key: 'headerHeight', label: '顶部栏高度', min: 48, max: 92, step: 2, unit: 'px' },
        { key: 'fontSize', label: '正文基准字号', min: 13, max: 21, step: 1, unit: 'px' },
        { key: 'radius', label: '全局圆角', min: 4, max: 18, step: 1, unit: 'px' },
        { key: 'sidebarWidth', label: '侧栏宽度', min: 220, max: 410, step: 10, unit: 'px' },
        { key: 'shellPadding', label: '内容内边距', min: 10, max: 52, step: 2, unit: 'px' },
        { key: 'columnGap', label: '正文与侧栏间距', min: 12, max: 56, step: 2, unit: 'px' },
        { key: 'avatarSize', label: '帖子列表头像大小', min: 20, max: 40, step: 1, unit: 'px' }
    ];

    /*
     * 每个主题都同时定义背景、面板、边线、语义色和交互色。
     * 暗色主题的 hover 仅在相邻层级之间提升明度,不使用纯白反转。
     */
    var THEMES = {
        original: { label: '跟随原站主题', vars: null },
        obsidian: {
            label: '曜石夜色',
            vars: {
                '--bg': '#0b0d12', '--panel': '#141821', '--panel-alt': '#1a202b', '--panel-hover': '#202735',
                '--line': '#2c3442', '--line-soft': '#202834', '--line-strong': '#414c60',
                '--text': '#e8ebf1', '--text-strong': '#f8f9fb', '--text-muted': '#b5bfce', '--text-subtle': '#8d98a9', '--text-disabled': '#687384',
                '--brand': '#aabaff', '--brand-hover': '#c7d1ff', '--brand-soft': '#212d55', '--brand-border': '#465886',
                '--success': '#52d6a0', '--success-soft': '#123426', '--success-soft-text': '#8eecc4',
                '--danger': '#ff9299', '--danger-soft': '#432127', '--danger-soft-text': '#ffb8bd',
                '--warning': '#ffd166', '--warning-soft': '#443719', '--warning-soft-text': '#ffe19a',
                '--info': '#aabaff', '--info-soft': '#212d55', '--info-soft-text': '#c7d1ff',
                '--mark': '#f5c451', '--mark-soft': '#3e3217', '--mark-soft-text': '#ffe09a', '--mark-text': '#fff9e8',
                '--action': '#e8ebf1', '--action-hover': '#ffffff', '--action-text': '#12151c', '--action-border': '#e8ebf1',
                '--inverse': '#e8ebf1', '--inverse-text': '#12151c', '--inverse-border': '#f6f7fa',
                '--focus-ring': '#aabaff', '--selection': '#3c4d7a', '--backdrop': 'rgb(3 6 12 / 0.78)',
                '--shadow-base': '0 2px 7px rgb(0 0 0 / 0.32)', '--shadow-medium': '0 20px 58px -14px rgb(0 0 0 / 0.70)'
            }
        },
        graphite: {
            label: '石墨中性',
            vars: {
                '--bg': '#121313', '--panel': '#1a1b1b', '--panel-alt': '#202121', '--panel-hover': '#292b2b',
                '--line': '#363838', '--line-soft': '#292b2b', '--line-strong': '#505353',
                '--text': '#eceeed', '--text-strong': '#ffffff', '--text-muted': '#c0c4c2', '--text-subtle': '#929795', '--text-disabled': '#6f7472',
                '--brand': '#9dd6c8', '--brand-hover': '#c1f1e5', '--brand-soft': '#1e3b35', '--brand-border': '#47756b',
                '--success': '#85d7ad', '--success-soft': '#173426', '--success-soft-text': '#a8e8c5',
                '--danger': '#f09a9a', '--danger-soft': '#422525', '--danger-soft-text': '#ffc0c0',
                '--warning': '#e7c275', '--warning-soft': '#40341e', '--warning-soft-text': '#f7d898',
                '--info': '#a9c3e8', '--info-soft': '#253449', '--info-soft-text': '#c6daf7',
                '--mark': '#e8c66e', '--mark-soft': '#40351f', '--mark-soft-text': '#f5dfa1', '--mark-text': '#fff8e5',
                '--action': '#eceeed', '--action-hover': '#ffffff', '--action-text': '#171818', '--action-border': '#eceeed',
                '--inverse': '#eceeed', '--inverse-text': '#171818', '--inverse-border': '#ffffff',
                '--focus-ring': '#9dd6c8', '--selection': '#36584f', '--backdrop': 'rgb(0 0 0 / 0.76)',
                '--shadow-base': '0 2px 7px rgb(0 0 0 / 0.28)', '--shadow-medium': '0 20px 58px -14px rgb(0 0 0 / 0.66)'
            }
        },
        paper: {
            label: '雾白纸感',
            vars: {
                '--bg': '#f4f5f7', '--panel': '#ffffff', '--panel-alt': '#fafbfc', '--panel-hover': '#edf1f6',
                '--line': '#d9dde5', '--line-soft': '#eaedf2', '--line-strong': '#c3cad6',
                '--text': '#242832', '--text-strong': '#11141b', '--text-muted': '#596273', '--text-subtle': '#737d8e', '--text-disabled': '#9aa2ae',
                '--brand': '#4965aa', '--brand-hover': '#314e96', '--brand-soft': '#e8edf9', '--brand-border': '#b8c6e8',
                '--success': '#138c5a', '--success-soft': '#e4f5ec', '--success-soft-text': '#08643e',
                '--danger': '#c5424d', '--danger-soft': '#fce8ea', '--danger-soft-text': '#8d2029',
                '--warning': '#9b6d00', '--warning-soft': '#fff3cf', '--warning-soft-text': '#6e4d00',
                '--info': '#4965aa', '--info-soft': '#e8edf9', '--info-soft-text': '#314e96',
                '--mark': '#e6b935', '--mark-soft': '#fff3c8', '--mark-soft-text': '#745b17', '--mark-text': '#2b2617',
                '--action': '#232934', '--action-hover': '#121720', '--action-text': '#ffffff', '--action-border': '#232934',
                '--inverse': '#232934', '--inverse-text': '#ffffff', '--inverse-border': '#323a48',
                '--focus-ring': '#4965aa', '--selection': '#cbd7f2', '--backdrop': 'rgb(16 23 38 / 0.48)',
                '--shadow-base': '0 2px 7px rgb(32 43 67 / 0.08)', '--shadow-medium': '0 20px 58px -14px rgb(32 43 67 / 0.22)'
            }
        }
    };

    var THEME_VARIABLES = Object.keys(THEMES.obsidian.vars);

    var DEFAULTS = {
        version: SETTINGS_VERSION,
        maxWidth: 1240,
        headerWidth: 1240,
        headerHeight: 62,
        fontSize: 15,
        radius: 10,
        sidebarWidth: 270,
        shellPadding: 20,
        columnGap: 24,
        avatarSize: 40,
        theme: 'original',
        density: 'comfortable',
        newWindowForTopics: false,
        stickyForumList: false,
        avatarProfileCard: false,
        blockNativeProfileCard: true,
        blockNativeReplyTree: false,
        imageLightbox: false,
        readingProgress: false,
        codeCopyButtons: false,
        autoCheckin: false,
        titleFilters: [],
        userFilters: []
    };

    var panel = null;
    var toggleButton = null;
    var statusElement = null;
    var saveTimer = 0;
    var filterTimer = 0;
    var refreshTimer = 0;
    var refreshFrame = 0;
    var progressFrame = 0;
    var lastProgressValue = '';
    var enhancementsRunning = false;
    var pendingEnhancementRoots = [];
    var pageObserver = null;
    var lightbox = null;
    var lightboxImage = null;
    var lightboxEventsBound = false;
    var searchModal = null;
    var searchModalForm = null;
    var searchModalInput = null;
    var profileCard = null;
    var profileHideTimer = 0;
    var nativeProfileCardBlockerBound = false;
    var profileCache = Object.create(null);
    var progressBar = null;
    var progressBound = false;
    var checkinPending = false;
    var settings = loadSettings();

    var BASE_CSS = [
        ':root { scrollbar-gutter: stable; }',
        'a, a:hover, a:focus-visible, a:active { text-decoration: none !important; }',
        ':root {',
        '  --sbe-max-width: 1240px; --sbe-header-width: 1240px; --sbe-header-height: 62px; --sbe-font-size: 15px; --sbe-radius: 10px;',
        '  --sbe-sidebar-width: 270px; --sbe-shell-padding: 20px; --sbe-column-gap: 24px; --sbe-avatar-size: 24px;',
        '}',
        '@media (min-width: 721px) {',
        '  :root { --font-size-sm: calc(var(--sbe-font-size) - 2px) !important; --font-size-md: var(--sbe-font-size) !important; --font-size-lg: calc(var(--sbe-font-size) + 1px) !important; }',
        '  body { font-size: var(--sbe-font-size) !important; }',
        '  .top .bar { width: min(var(--sbe-header-width), calc(100% - 44px)) !important; max-width: none !important; height: var(--sbe-header-height) !important; min-height: var(--sbe-header-height) !important; box-sizing: border-box; }',
        '  main.wrap, .footer { width: min(var(--sbe-max-width), calc(100% - 44px)) !important; max-width: none !important; }',
        '  main.wrap { padding-top: 5px !important; padding-bottom: 30px !important; }',
        '  .home-shell { padding: var(--sbe-shell-padding) !important; }',
        '  .forum-layout { display: grid !important; grid-template-columns: minmax(0, 1fr) minmax(220px, var(--sbe-sidebar-width)) !important; grid-template-areas: "main sidebar" !important; align-items: stretch !important; gap: var(--sbe-column-gap) !important; }',
        '  .forum-layout > .forum-main { grid-area: main !important; min-width: 0 !important; width: auto !important; }',
        '  .forum-layout > .sidebar { grid-area: sidebar !important; align-self: stretch !important; width: auto !important; min-width: 0 !important; }',
        '  .forum-layout > .sidebar > .sbe-forum-card { position: sticky !important; top: calc(var(--sbe-header-height) + 14px); z-index: 2; max-height: calc(100vh - var(--sbe-header-height) - 28px); overflow-y: auto; overscroll-behavior: contain; }',
        '  .top .bar { display: flex !important; align-items: center !important; gap: 10px !important; }',
        '  .top .brand { flex: 0 0 auto; white-space: nowrap; }',
        '  .top .forum-nav { display: flex !important; min-width: 0; min-height: 30px; flex: 1 1 auto; align-items: center; justify-content: flex-start; gap: 1px; overflow-x: auto; scrollbar-width: none; }',
        '  .top .forum-nav::-webkit-scrollbar { display: none; }',
        '  .top .forum-link { display: inline-flex !important; width: auto; height: 32px !important; min-height: 32px !important; box-sizing: border-box; align-items: center !important; justify-content: center !important; padding: 0 9px !important; border: 1px solid transparent !important; border-radius: 6px; font-size: 13px; line-height: 1 !important; text-align: center; vertical-align: middle; white-space: nowrap; }',
        '  .top .forum-link.active, .top .forum-link[aria-current="page"] { border-color: var(--brand-border) !important; }',
        '  .top .search-form { display: flex !important; width: clamp(190px, 18vw, 260px); min-width: 0; align-items: center; margin-left: 4px; padding: 2px 3px 2px 7px; border: 1px solid var(--line) !important; border-radius: 8px; background: var(--panel-alt); }',
        '  .top .search-field { width: 50px !important; min-width: 50px; padding: 4px 1px !important; border: 0 !important; border-radius: 0 !important; background: transparent !important; color: var(--text-subtle) !important; font-size: 12px; }',
        '  .top .search-input { min-width: 0; flex: 1; padding: 5px 6px !important; border: 0 !important; border-radius: 0 !important; background: transparent !important; font-size: 13px; }',
        '  .top .search-btn { display: grid !important; width: 28px; height: 28px; flex: 0 0 28px; place-items: center; padding: 0 !important; border: 0 !important; border-radius: 6px !important; background: transparent !important; color: var(--text-muted); }',
        '  .top .search-btn:hover, .top .search-btn:focus-visible { background: var(--brand-soft) !important; color: var(--brand-hover); outline: none; }',
        '  .top .script-toggle > button, .top .theme-toggle { display: grid !important; width: 30px; height: 30px; place-items: center; padding: 0 !important; border-radius: 6px !important; }',
        '}',
        '@media (min-width: 1080px) { .top .forum-nav { justify-content: center; } }',
        '@media (max-width: 720px) {',
        '  .sbe-layout-toggle { right: 16px !important; bottom: 16px !important; }',
        '  .sbe-layout-panel { right: 12px !important; bottom: 68px !important; width: min(392px, calc(100vw - 24px)) !important; max-height: min(78vh, 720px) !important; }',
        '  .sbe-forum-card { display: none !important; }',
        '}',
        ':root[data-sbe-density="compact"] .post-item { min-height: 55px !important; padding-top: 8px !important; padding-bottom: 8px !important; }',
        ':root[data-sbe-density="compact"] .post-content { line-height: 1.64 !important; }',
        ':root[data-sbe-density="relaxed"] .post-item { min-height: 72px !important; padding-top: 15px !important; padding-bottom: 15px !important; }',
        ':root[data-sbe-density="relaxed"] .post-content { line-height: 1.86 !important; }',
        ':root[data-sbe-theme] body { background: var(--bg) !important; color: var(--text) !important; }',
        ':root[data-sbe-theme] .top { background: var(--panel) !important; border-bottom-color: var(--line) !important; }',
        ':root[data-sbe-theme] .card, :root[data-sbe-theme] .sidebar-card, :root[data-sbe-theme] .topic-post-list.topic-opening, :root[data-sbe-theme] .topic-replies > .topic-post-list, :root[data-sbe-theme] .bio-card { background: var(--panel) !important; border-color: var(--line) !important; }',
        ':root[data-sbe-theme] .post-list, :root[data-sbe-theme] .main-panel { border-color: var(--line) !important; }',
        ':root[data-sbe-theme] .post-item { border-color: var(--line-soft) !important; color: var(--text) !important; }',
        '@media (hover: hover) { :root[data-sbe-theme] .post-item:not(.topic-pinned):hover { background: var(--panel-hover) !important; } }',
        ':root[data-sbe-theme] .post-item.topic-pinned { background: var(--mark-soft) !important; }',
        ':root[data-sbe-theme] .post-title, :root[data-sbe-theme] .post-topic-title, :root[data-sbe-theme] .post-content h1, :root[data-sbe-theme] .post-content h2, :root[data-sbe-theme] .post-content h3 { color: var(--text-strong) !important; }',
        ':root[data-sbe-theme] .post-title:hover, :root[data-sbe-theme] .post-title:focus-visible, :root[data-sbe-theme] .sidebar-topic-title:hover { color: var(--brand) !important; }',
        ':root[data-sbe-theme] .post-content, :root[data-sbe-theme] .post-content p, :root[data-sbe-theme] .reply-light { color: var(--text) !important; }',
        ':root[data-sbe-theme] .post-content a, :root[data-sbe-theme] .post-meta a:hover, :root[data-sbe-theme] .post-head a:hover, :root[data-sbe-theme] .footer a:hover { color: var(--brand-hover) !important; }',
        ':root[data-sbe-theme] .post-meta, :root[data-sbe-theme] .post-reply-no, :root[data-sbe-theme] .user-rank, :root[data-sbe-theme] .footer { color: var(--text-subtle) !important; }',
        ':root[data-sbe-theme] .post-forum-badge, :root[data-sbe-theme] .topic-badge { background: var(--panel-alt) !important; border-color: var(--line) !important; color: var(--text-muted) !important; }',
        ':root[data-sbe-theme] .topic-badge.pinned { background: var(--mark-soft) !important; border-color: var(--mark) !important; color: var(--mark-soft-text) !important; }',
        ':root[data-sbe-theme] .tab-bar .tab { color: var(--text-muted) !important; }',
        ':root[data-sbe-theme] .tab-bar .tab.active, :root[data-sbe-theme] .tab-bar .tab[aria-current="page"] { background: var(--brand) !important; border-color: var(--brand) !important; color: var(--inverse-text) !important; }',
        ':root[data-sbe-theme] .tab-bar .tab:not(.active):hover { background: var(--brand-soft) !important; color: var(--brand-hover) !important; }',
        ':root[data-sbe-theme] .search-input, :root[data-sbe-theme] .search-field, :root[data-sbe-theme] input, :root[data-sbe-theme] select, :root[data-sbe-theme] textarea { background: var(--panel) !important; border-color: var(--line-strong) !important; color: var(--text) !important; }',
        ':root[data-sbe-theme] input::placeholder, :root[data-sbe-theme] textarea::placeholder { color: var(--text-subtle) !important; opacity: 1 !important; }',
        ':root[data-sbe-theme] .post-content table { border-color: var(--line) !important; }',
        ':root[data-sbe-theme] .post-content th { background: var(--panel-alt) !important; color: var(--text-strong) !important; }',
        ':root[data-sbe-theme] .post-content td, :root[data-sbe-theme] .post-content th { border-color: var(--line) !important; }',
        ':root[data-sbe-theme] .post-content blockquote { border-left-color: var(--brand) !important; background: var(--brand-soft) !important; color: var(--text-muted) !important; }',
        ':root[data-sbe-theme] .post-content pre { background: var(--panel-alt) !important; border-color: var(--line) !important; }',
        ':root[data-sbe-theme] .post-content code:not(pre code) { background: var(--panel-alt) !important; color: var(--brand-hover) !important; }',
        ':root[data-sbe-theme] .pagination a, :root[data-sbe-theme] .profile-toolbar, :root[data-sbe-theme] .quick-card { border-color: var(--line) !important; }',
        ':root[data-sbe-theme] .pagination a:hover, :root[data-sbe-theme] .pagination a[aria-current="page"] { background: var(--brand-soft) !important; border-color: var(--brand-border) !important; color: var(--brand-hover) !important; }',
        ':root[data-sbe-theme] ::selection { background: var(--selection) !important; color: var(--text-strong) !important; }',
        ':root[data-sbe-theme] a:focus-visible, :root[data-sbe-theme] button:focus-visible, :root[data-sbe-theme] input:focus-visible, :root[data-sbe-theme] select:focus-visible, :root[data-sbe-theme] textarea:focus-visible { outline: 2px solid var(--focus-ring) !important; outline-offset: 2px !important; }',
        ':root[data-sbe-theme] .modal-panel, :root[data-sbe-theme] .toast { background: var(--panel) !important; border-color: var(--line) !important; color: var(--text) !important; }',
        ':root[data-sbe-theme] .to-top { background: var(--panel) !important; border-color: var(--line-strong) !important; color: var(--text) !important; }',
        '.sbe-forum-card { position: sticky; top: calc(var(--sbe-header-height) + 14px); z-index: 2; order: 99; margin: 14px 0 0 !important; padding: 14px; border: 1px solid var(--line); border-radius: var(--sbe-radius); background: var(--panel); box-shadow: var(--shadow-base); color: var(--text); }',
        '.forum-layout > .sidebar > .sbe-forum-card { margin-top: 14px !important; }',
        '.sbe-forum-card h2 { margin: 0 0 11px; color: var(--text-strong); font-size: 13px; letter-spacing: .01em; }',
        '.sbe-forum-list { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 6px; }',
        '.sbe-forum-list a { display: flex; min-width: 0; min-height: 27px; align-items: center; justify-content: center; overflow: hidden; padding: 0 4px; border: 1px solid var(--line-soft); border-radius: var(--r-pill); background: var(--panel-alt); color: var(--text-muted); font-size: 12px; line-height: 1; text-align: center; text-decoration: none; text-overflow: ellipsis; white-space: nowrap; transition: background-color .15s ease, border-color .15s ease, color .15s ease; }',
        '.sbe-forum-list a:hover, .sbe-forum-list a:focus-visible { border-color: var(--brand-border); background: var(--brand-soft); color: var(--brand-hover); outline: none; }',
        '.post-list .post-item > .post-avatar { width: var(--sbe-avatar-size) !important; height: var(--sbe-avatar-size) !important; flex: 0 0 var(--sbe-avatar-size) !important; }',
        '.post-list .post-item > .post-avatar .avatar-img { display: block; width: 100% !important; height: 100% !important; object-fit: cover; }',
        '.post-list .post-item > .post-body { min-width: 0 !important; flex: 1 1 auto !important; }',
        '.user-stats { grid-template-columns: repeat(4, minmax(0, 1fr)) !important; gap: 0 !important; }',
        '.user-stat { align-items: center; text-align: center; }',
        '.user-stat-label, .user-stat-value { width: 100%; text-align: center; }',
        '.user-stat-value { font-size: 13px !important; font-weight: 400 !important; line-height: 1.35; }',
        '.user-links { align-items: center; }',
        '.user-links a { display: flex !important; min-width: 0; align-items: center !important; justify-content: center; gap: 6px !important; line-height: 20px; }',
        '.user-links a > svg, .user-links a > .meta-icon { width: 14px; height: 14px; flex: 0 0 14px; }',
        '.quick-card .quick-forum-links { display: flex !important; flex-wrap: wrap !important; justify-content: flex-start; gap: 6px !important; margin: 12px 0 0 !important; padding: 0 !important; list-style: none !important; }',
        '.quick-card .quick-forum-links li { box-sizing: border-box; flex: 0 0 calc((100% - 18px) / 4); margin: 0 !important; }',
        '.quick-card .quick-forum-links a { display: inline-flex !important; width: 100%; min-height: 27px; box-sizing: border-box; align-items: center; justify-content: center; padding: 0 6px; border: 1px solid var(--line-soft); border-radius: var(--r-pill); background: var(--panel-alt); color: var(--text-muted); font-size: 12px; line-height: 1; white-space: nowrap; }',
        '.quick-card .quick-forum-links a:hover, .quick-card .quick-forum-links a:focus-visible { border-color: var(--brand-border); background: var(--brand-soft); color: var(--brand-hover); outline: none; }',
        '/* 站点将菜单链接收进 .nav-user-links 后,双列规则需作用于该容器;其余原样式保持不变。 */',
        '.top .nav-user { position: relative !important; flex: 0 0 auto !important; }',
        '.top .nav-user-menu:not([hidden]) { position: absolute !important; top: calc(100% + 8px) !important; right: 0 !important; left: auto !important; display: block !important; min-width: 0 !important; width: min(168px, calc(100vw - 20px)) !important; box-sizing: border-box !important; transform: none !important; }',
        '.top .nav-user-menu .nav-user-links { display: grid !important; grid-template-columns: repeat(2, minmax(0, 1fr)) !important; gap: 1px 2px !important; }',
        '.top .nav-user-menu .nav-user-item { display: flex; min-width: 0; min-height: 32px; align-items: center; justify-content: center; padding: 6px 8px; font-size: 13px; overflow: hidden; text-align: center; text-overflow: ellipsis; }',
        '.top .search-form { display: contents !important; width: auto !important; min-width: 0 !important; flex: 0 0 auto !important; margin-left: 4px !important; padding: 0 !important; border: 0 !important; background: transparent !important; }',
        '.top .search-form .search-input { display: none !important; }',
        '.top .search-form .search-btn { display: grid !important; width: 30px !important; height: 30px !important; flex: 0 0 30px !important; place-items: center; padding: 0 !important; border: 1px solid var(--line) !important; border-radius: 6px !important; background: var(--panel) !important; color: var(--text-muted); }',
        '.top .search-form .search-btn:hover, .top .search-form .search-btn:focus-visible { border-color: var(--brand-border) !important; background: var(--brand-soft) !important; color: var(--brand-hover) !important; outline: none; }',
        '#sbe-search-modal { position: fixed; z-index: 2147483646; inset: 0; display: grid; place-items: start center; padding: min(18vh, 140px) 16px 16px; background: var(--backdrop); }',
        '#sbe-search-modal[hidden] { display: none !important; }',
        '#sbe-search-modal-dialog { width: min(520px, 100%); padding: 14px; border: 1px solid var(--line); border-radius: var(--sbe-radius); background: var(--panel); box-shadow: var(--shadow-medium); animation: sbe-search-dialog-in .16s ease-out; }',
        '#sbe-search-modal-dialog form { display: flex; align-items: center; gap: 8px; }',
        '#sbe-search-modal-dialog .sbe-search-input { min-width: 0; flex: 1 1 auto; height: 38px; box-sizing: border-box; padding: 0 11px; border: 1px solid var(--line-strong); border-radius: 7px; background: var(--panel-alt); color: var(--text); font: inherit; }',
        '#sbe-search-modal-dialog .sbe-search-submit { display: inline-flex; width: 64px; min-width: 64px; height: 38px; flex: 0 0 64px; box-sizing: border-box; align-items: center; justify-content: center; padding: 0; border: 1px solid var(--brand-border); border-radius: 7px; background: var(--brand-soft); color: var(--brand-hover); font: inherit; line-height: 1; white-space: nowrap; cursor: pointer; }',
        '#sbe-search-modal-dialog .sbe-search-submit:hover, #sbe-search-modal-dialog .sbe-search-submit:focus-visible { background: var(--brand); color: var(--inverse-text); outline: none; }',
        '#sbe-search-modal-dialog .sbe-search-close { display: block; margin: 10px auto 0; padding: 0; border: 0; background: transparent; color: var(--text-subtle); font: 12px/1.4 var(--font-sans, system-ui); cursor: pointer; }',
        '#sbe-search-modal-dialog .sbe-search-close:hover, #sbe-search-modal-dialog .sbe-search-close:focus-visible { color: var(--brand-hover); outline: none; }',
        '.topic-replies.sbe-native-reply-tree-blocked .tree-toggle, .topic-replies.sbe-native-reply-tree-blocked .tree-status { display: none !important; }',
        '.post-head .post-author + .verify-badge { margin-left: 4px !important; vertical-align: middle; }',
        '.user-card .sbe-user-name-verify-row { display: flex !important; min-width: 0; align-items: center; gap: 4px; line-height: 1.2; }',
        '.user-card .sbe-user-name-verify-row .user-name { display: inline-block !important; min-width: 0; }',
        '.user-card .sbe-user-name-verify-row .user-verify { display: inline-flex !important; flex: 0 0 auto; margin: 0 !important; }',
        '.user-card .sbe-user-name-verify-row .verify-badge { margin: 0 !important; white-space: nowrap; }',
        '@keyframes sbe-search-dialog-in { from { opacity: 0; transform: translateY(-8px) scale(.98); } to { opacity: 1; transform: translateY(0) scale(1); } }',
        '.stats-card .stats-wrap { padding: 12px 14px 11px; }',
        '.stats-card .stats-title { margin-bottom: 6px; }',
        '.stats-card .stats-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); justify-content: normal; gap: 2px 0; margin: 0 0 10px; padding: 0 6px; }',
        '.stats-card .stats-grid > div:nth-child(1) { text-align: left; }',
        '.stats-card .stats-grid > div:nth-child(2) { text-align: center; }',
        '.stats-card .stats-grid > div:nth-child(3) { text-align: right; }',
        '.stats-card .stats-grid dd { font-size: 13px; font-weight: 400; line-height: 1.35; }',
        '.stats-card .new-users-title { margin: 0 0 8px; padding-top: 10px; border-top: 1px solid var(--line-soft); }',
        '.stats-card .new-users { gap: 8px 4px; }',
        '.stats-card .nu-item { position: relative; gap: 0; }',
        '.stats-card .nu-avatar-circle { width: var(--sbe-avatar-size) !important; height: var(--sbe-avatar-size) !important; }',
        '.stats-card .nu-avatar-circle .avatar-img { display: block; width: 100%; height: 100%; object-fit: cover; }',
        '.stats-card .nu-name { display: none !important; }',
        '.sbe-image-target, .sbe-image-target img { cursor: zoom-in !important; }',
        '#sbe-image-lightbox { position: fixed; z-index: 2147483644; inset: 0; display: grid; place-items: center; padding: 16px; background: var(--backdrop); cursor: zoom-out; }',
        '#sbe-image-lightbox[hidden] { display: none !important; }',
        '#sbe-image-lightbox img { display: block; max-width: 96vw; max-height: 92vh; border: 0; border-radius: 0; box-shadow: none; cursor: zoom-out; object-fit: contain; }',
        '#sbe-profile-card { position: fixed; z-index: 2147483645; width: 276px; padding: 13px; border: 1px solid var(--line); border-radius: var(--sbe-radius); background: var(--panel); box-shadow: var(--shadow-medium); color: var(--text); font-size: 13px; }',
        '#sbe-profile-card[hidden] { display: none !important; }',
        '.sbe-profile-head { min-width: 0; }',
        '.sbe-profile-name-row { display: flex; min-width: 0; align-items: baseline; gap: 10px; }',
        '.sbe-profile-name { overflow: hidden; min-width: 0; color: var(--text-strong); font-size: 14px; font-weight: 700; text-overflow: ellipsis; white-space: nowrap; }',
        '.sbe-profile-uid { margin-left: auto; color: var(--text-subtle); font-size: 12px; font-variant-numeric: tabular-nums; white-space: nowrap; }',
        '.sbe-profile-name { text-decoration: none !important; }',
        '.sbe-profile-name:hover, .sbe-profile-name:focus-visible { color: var(--brand-hover); text-decoration: none !important; outline: none; }',
        '.sbe-profile-rank { margin-top: 3px; color: var(--text-subtle); font-size: 12px; }',
        '.sbe-profile-stats { display: flex; align-items: center; gap: 8px; margin: 12px 0 0; }',
        '.sbe-profile-stat { display: flex; min-width: 0; flex: 1 1 0; align-items: baseline; justify-content: space-between; gap: 8px; padding: 8px 9px; border-radius: max(5px, calc(var(--sbe-radius) - 4px)); background: var(--panel-alt); }',
        '.sbe-profile-stat dt { overflow: hidden; color: var(--text-subtle); font-size: 11px; text-overflow: ellipsis; white-space: nowrap; }',
        '.sbe-profile-stat dd { margin: 0; color: var(--text-strong); font-weight: 700; font-variant-numeric: tabular-nums; white-space: nowrap; }',
        '.sbe-code-copy { position: absolute; top: 8px; right: 8px; z-index: 1; padding: 4px 8px; border: 1px solid var(--line-strong); border-radius: 6px; background: var(--panel); color: var(--text-muted); font: 12px/1.2 var(--font-sans, system-ui); cursor: pointer; opacity: .82; }',
        '.sbe-code-copy:hover, .sbe-code-copy:focus-visible { border-color: var(--brand-border); background: var(--brand-soft); color: var(--brand-hover); opacity: 1; outline: none; }',
        '.post-content pre[data-sbe-copyable] { position: relative; }',
        '#sbe-reading-progress { position: fixed; z-index: 2147483643; top: 0; left: 0; width: 100%; height: 3px; background: transparent; pointer-events: none; }',
        '#sbe-reading-progress::before { display: block; width: 100%; height: 100%; background: var(--brand); box-shadow: 0 0 12px var(--brand); content: ""; transform: scaleX(var(--sbe-progress, 0)); transform-origin: left center; }',
        '#sbe-reading-progress[hidden] { display: none !important; }',
        '.sbe-layout-toggle { position: fixed; z-index: 2147483640; right: 24px; bottom: 24px; display: grid; width: 44px; height: 44px; place-items: center; padding: 0; border: 1px solid var(--brand-border); border-radius: 50% !important; background: var(--panel); box-shadow: var(--shadow-medium); color: var(--brand); cursor: pointer; transition: transform .15s ease, background-color .15s ease; }',
        '.sbe-layout-toggle:hover { background: var(--brand-soft); transform: translateY(-2px); }',
        '.sbe-layout-toggle svg { width: 20px; height: 20px; }',
        '.sbe-layout-panel { position: fixed; z-index: 2147483641; right: 24px; bottom: 80px; width: 380px; max-height: min(78vh, 760px); overflow: auto; border: 1px solid var(--line); border-radius: calc(var(--sbe-radius) + 2px); background: var(--panel); box-shadow: var(--shadow-medium); color: var(--text); }',
        '.sbe-layout-panel[hidden] { display: none !important; }',
        '.sbe-panel-head { position: sticky; top: 0; z-index: 1; display: grid; min-height: 52px; grid-template-columns: minmax(0, 1fr) auto 30px; align-items: center; gap: 10px; padding: 10px 11px 10px 15px; border-bottom: 1px solid var(--line-soft); background: var(--panel); }',
        '.sbe-panel-title { margin: 0; min-width: 0; color: var(--text-strong); font-size: 15px; white-space: nowrap; }',
        '.sbe-panel-credit { justify-self: end; margin: 0; overflow: hidden; color: var(--text-subtle); font-size: 11px; text-align: right; text-overflow: ellipsis; white-space: nowrap; }',
        '.sbe-panel-credit a { color: inherit; text-decoration: none; }',
        '.sbe-panel-credit a:hover, .sbe-panel-credit a:focus-visible { color: var(--brand-hover); text-decoration: none; outline: none; }',
        '.sbe-icon-button, .sbe-button { border: 1px solid var(--line-strong); border-radius: max(5px, calc(var(--sbe-radius) - 3px)) !important; background: transparent; color: var(--text); cursor: pointer; }',
        '.sbe-icon-button { display: grid; width: 30px; height: 30px; flex: 0 0 30px; place-items: center; padding: 0; font-size: 21px; line-height: 1; }',
        '.sbe-panel-body { padding: 14px 15px 16px; }',
        '.sbe-section { margin: 0 0 14px; padding: 0 0 14px; border-bottom: 1px solid var(--line-soft); }',
        '.sbe-section-title { margin: 0 0 10px; color: var(--text-strong); font-size: 13px; }',
        '.sbe-range, .sbe-field, .sbe-check-line { display: grid; gap: 6px; margin: 9px 0; }',
        '.sbe-range-head, .sbe-check-line { display: flex; align-items: center; justify-content: space-between; gap: 10px; }',
        '.sbe-range-head span:first-child, .sbe-field > span { color: var(--text-muted); font-size: 12px; }',
        '.sbe-range output, .sbe-color-value { color: var(--text-subtle); font-size: 12px; font-variant-numeric: tabular-nums; }',
        '.sbe-range input[type="range"] { width: 100%; accent-color: var(--brand); }',
        '.sbe-check-line { justify-content: flex-start; color: var(--text); font-size: 13px; }',
        '.sbe-check-line input { margin: 0; accent-color: var(--brand); }',
        '.sbe-input, .sbe-select, .sbe-textarea { width: 100%; box-sizing: border-box; padding: 8px 9px; border: 1px solid var(--line-strong); border-radius: max(5px, calc(var(--sbe-radius) - 3px)) !important; background: var(--panel-alt); color: var(--text); font: inherit; font-size: 13px; }',
        '.sbe-textarea { min-height: 64px; resize: vertical; line-height: 1.45; }',
        '.sbe-color-line { display: flex; align-items: center; gap: 8px; }',
        '.sbe-color-input { width: 34px; height: 28px; padding: 2px; border: 1px solid var(--line-strong); border-radius: 5px !important; background: transparent; }',
        '.sbe-note { margin: 8px 0 0; color: var(--text-subtle); font-size: 11px; line-height: 1.55; }',
        '.sbe-actions { display: flex; justify-content: flex-end; gap: 8px; }',
        '.sbe-button { padding: 8px 11px; font-size: 12px; }',
        '.sbe-button:hover { border-color: var(--brand-border); background: var(--brand-soft); color: var(--brand-hover); }',
        '.sbe-button-primary { border-color: var(--brand); background: var(--brand); color: var(--inverse-text); }',
        '.sbe-button-primary:hover { border-color: var(--brand-hover); background: var(--brand-hover); color: var(--inverse-text); }',
        '.sbe-status { min-height: 16px; margin: 10px 0 0; color: var(--text-subtle); font-size: 11px; text-align: right; }',
        '@media (prefers-reduced-motion: reduce) { .sbe-layout-toggle, .sbe-forum-list a { transition: none !important; } }'
    ].join('\n');

    addStyle(buildStartupCss(settings) + '\n' + BASE_CSS);
    applySettings({ deferDynamic: true });
    /* 在页面解析阶段即开始监听;分类页局部更新时可在同一绘制帧补齐增强,避免 100ms 后的二次跳动。 */
    observePage();

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', initialize, { once: true });
    } else {
        initialize();
    }

    if (typeof GM_registerMenuCommand === 'function') {
        GM_registerMenuCommand('打开 SB.SB 增强设置', function () {
            ensureInterface();
            setPanelOpen(true);
        });
    }

    function initialize() {
        ensureInterface();
        applyDynamicEnhancements();
    }

    function addStyle(cssText) {
        if (typeof GM_addStyle === 'function') {
            GM_addStyle(cssText);
            return;
        }
        var style = document.createElement('style');
        style.id = 'sb-sb-enhance-style';
        style.textContent = cssText;
        (document.head || document.documentElement).appendChild(style);
    }

    function buildStartupCss(currentSettings) {
        var start = ':root:not([data-sbe-ready]){';
        start += '--sbe-max-width:' + currentSettings.maxWidth + 'px!important;';
        start += '--sbe-header-width:' + currentSettings.headerWidth + 'px!important;';
        start += '--sbe-header-height:' + currentSettings.headerHeight + 'px!important;';
        start += '--sbe-font-size:' + currentSettings.fontSize + 'px!important;';
        start += '--sbe-radius:' + currentSettings.radius + 'px!important;';
        start += '--sbe-sidebar-width:' + currentSettings.sidebarWidth + 'px!important;';
        start += '--sbe-shell-padding:' + currentSettings.shellPadding + 'px!important;';
        start += '--sbe-column-gap:' + currentSettings.columnGap + 'px!important;';
        start += '--sbe-avatar-size:' + currentSettings.avatarSize + 'px!important;';
        start += '}';
        return start;
    }

    function normalizeSettings(value) {
        var source = value && typeof value === 'object' ? value : {};
        var result = Object.assign({}, DEFAULTS, source);
        delete result['infinite' + 'HomeScroll'];
        if (Number(source.version || 0) < 7 && Number(source.avatarSize) === 24) {
            result.avatarSize = DEFAULTS.avatarSize;
        }
        RANGE_DEFINITIONS.forEach(function (definition) {
            var number = Number(result[definition.key]);
            result[definition.key] = Number.isFinite(number) ? Math.max(definition.min, Math.min(definition.max, number)) : DEFAULTS[definition.key];
        });
        if (!Object.prototype.hasOwnProperty.call(THEMES, result.theme)) {
            result.theme = DEFAULTS.theme;
        }
        if (['compact', 'comfortable', 'relaxed'].indexOf(result.density) < 0) {
            result.density = DEFAULTS.density;
        }
        ['newWindowForTopics', 'stickyForumList', 'avatarProfileCard', 'blockNativeProfileCard', 'blockNativeReplyTree', 'imageLightbox', 'readingProgress', 'codeCopyButtons', 'autoCheckin'].forEach(function (key) {
            result[key] = result[key] === true || result[key] === 'true';
        });
        result.titleFilters = normalizeFilterList(result.titleFilters);
        result.userFilters = normalizeFilterList(result.userFilters);
        result.version = SETTINGS_VERSION;
        return result;
    }

    function normalizeFilterList(value) {
        var list = Array.isArray(value) ? value : [];
        return list.map(function (item) { return String(item || '').trim().replace(/\s+/g, ' '); }).filter(Boolean).slice(0, 10).map(function (item) { return item.slice(0, 64); });
    }

    function loadSettings() {
        try {
            var stored = typeof GM_getValue === 'function' ? GM_getValue(STORAGE_KEY, null) : JSON.parse(localStorage.getItem(STORAGE_KEY) || 'null');
            return normalizeSettings(stored);
        } catch (error) {
            return normalizeSettings(null);
        }
    }

    function persistSettings() {
        try {
            if (typeof GM_setValue === 'function') {
                GM_setValue(STORAGE_KEY, settings);
            } else {
                localStorage.setItem(STORAGE_KEY, JSON.stringify(settings));
            }
            showStatus('设置已保存');
        } catch (error) {
            showStatus('设置已应用,但保存失败');
        }
    }

    function scheduleSave() {
        window.clearTimeout(saveTimer);
        saveTimer = window.setTimeout(persistSettings, SAVE_DELAY);
    }

    function setRootVariable(name, value) {
        if (document.documentElement) {
            document.documentElement.style.setProperty(name, value, 'important');
        }
    }

    function clearThemeVariables() {
        if (!document.documentElement) {
            return;
        }
        THEME_VARIABLES.forEach(function (name) { document.documentElement.style.removeProperty(name); });
    }

    function applySettings(options) {
        var configuration = options || {};
        if (!document.documentElement) {
            return;
        }
        setRootVariable('--sbe-max-width', settings.maxWidth + 'px');
        setRootVariable('--sbe-header-width', settings.headerWidth + 'px');
        setRootVariable('--sbe-header-height', settings.headerHeight + 'px');
        setRootVariable('--sbe-font-size', settings.fontSize + 'px');
        setRootVariable('--sbe-radius', settings.radius + 'px');
        setRootVariable('--sbe-sidebar-width', settings.sidebarWidth + 'px');
        setRootVariable('--sbe-shell-padding', settings.shellPadding + 'px');
        setRootVariable('--sbe-column-gap', settings.columnGap + 'px');
        setRootVariable('--sbe-avatar-size', settings.avatarSize + 'px');
        setRootVariable('--r', settings.radius + 'px');
        setRootVariable('--r-sm', Math.max(4, settings.radius - 3) + 'px');
        setRootVariable('--r-xs', Math.max(3, settings.radius - 5) + 'px');
        document.documentElement.setAttribute('data-sbe-density', settings.density);
        syncNativeProfileCardBlocker();
        applyTheme();
        document.documentElement.setAttribute('data-sbe-ready', '');
        if (!configuration.deferDynamic) {
            applyDynamicEnhancements();
        }
    }

    function applyTheme() {
        var theme = THEMES[settings.theme] || THEMES.original;
        clearThemeVariables();
        document.documentElement.removeAttribute('data-sbe-theme');
        if (!theme.vars) {
            return;
        }
        Object.keys(theme.vars).forEach(function (name) { setRootVariable(name, theme.vars[name]); });
        document.documentElement.setAttribute('data-sbe-theme', settings.theme);
    }

    function scheduleDynamicRefresh(root) {
        /* 分类切换时只处理新节点,并在本轮微任务完成,避免固定延迟和二次跳动。 */
        if (root && root.nodeType === 1) {
            var covered = pendingEnhancementRoots.some(function (item) { return item === root || item.contains(root); });
            if (!covered) {
                pendingEnhancementRoots = pendingEnhancementRoots.filter(function (item) { return !root.contains(item); });
                pendingEnhancementRoots.push(root);
            }
        }
        if (refreshFrame) { return; }
        refreshFrame = 1;
        var flush = function () {
            refreshFrame = 0;
            var roots = pendingEnhancementRoots.splice(0);
            applyDynamicEnhancements(roots.length ? roots : [document]);
        };
        if (typeof window.queueMicrotask === 'function') {
            window.queueMicrotask(flush);
        } else {
            Promise.resolve().then(flush);
        }
    }

    function observePage() {
        if (pageObserver || typeof MutationObserver !== 'function' || !document.documentElement) {
            return;
        }
        pageObserver = new MutationObserver(function (mutations) {
            if (enhancementsRunning) { return; }
            mutations.forEach(function (mutation) {
                if (mutation.type !== 'childList' || !mutation.addedNodes.length) { return; }
                var target = mutation.target && mutation.target.nodeType === 1 ? mutation.target : mutation.target.parentElement;
                if (target && target.closest && target.closest('#sbe-layout-panel, #sbe-image-lightbox, #sbe-profile-card, .sbe-forum-card')) { return; }
                Array.prototype.forEach.call(mutation.addedNodes, function (node) {
                    if (node.nodeType === 1) { scheduleDynamicRefresh(node); }
                });
            });
        });
        pageObserver.observe(document.documentElement, { childList: true, subtree: true });
    }

    function queryWithin(roots, selector) {
        var result = [];
        var seen = new Set();
        (roots && roots.length ? roots : [document]).forEach(function (root) {
            if (!root || !root.querySelectorAll) { return; }
            if (root.nodeType === 1 && root.matches(selector) && !seen.has(root)) {
                seen.add(root);
                result.push(root);
            }
            root.querySelectorAll(selector).forEach(function (node) {
                if (!seen.has(node)) {
                    seen.add(node);
                    result.push(node);
                }
            });
        });
        return result;
    }

    function applyDynamicEnhancements(roots) {
        if (enhancementsRunning) { return; }
        var scopes = roots && roots.length ? roots : [document];
        enhancementsRunning = true;
        /* 执行增强时暂停观察,杜绝脚本自己的 DOM 写入形成反馈循环。 */
        if (pageObserver) { pageObserver.disconnect(); }
        try {
            ensureForumShortcutCard();
            applyTopicNewWindow(scopes);
            removeLegacyTopicViews(scopes);
            removeLegacyTopicPostUids(scopes);
            applyFilters(scopes);
            syncLatestUserAvatars(scopes);
            alignVerificationBadges(scopes);
            syncNativeReplyTreeBlocker(scopes);
            bindSearchModal();
            updateImageLightboxTargets(scopes);
            bindProfileCardTargets(scopes);
            maybeRunAutoCheckin();
            scheduleReadingProgressUpdate();
            updateCodeCopyButtons(scopes);
        } finally {
            enhancementsRunning = false;
            if (pageObserver && document.documentElement) {
                pageObserver.observe(document.documentElement, { childList: true, subtree: true });
            }
        }
    }

    function getCheckinStorageKey() {
        var now = new Date();
        return CHECKIN_STORAGE_PREFIX + now.getFullYear() + '-' + String(now.getMonth() + 1).padStart(2, '0') + '-' + String(now.getDate()).padStart(2, '0');
    }

    function hasAutoCheckinRunToday() {
        var key = getCheckinStorageKey();
        try {
            return typeof GM_getValue === 'function' ? GM_getValue(key, false) === true : localStorage.getItem(key) === '1';
        } catch (error) {
            return false;
        }
    }

    function markAutoCheckinRunToday() {
        var key = getCheckinStorageKey();
        try {
            if (typeof GM_setValue === 'function') {
                GM_setValue(key, true);
            } else {
                localStorage.setItem(key, '1');
            }
        } catch (error) {
            /* 存储不可用时仍避免当前页面内重复触发。 */
        }
    }

    function requestSigninPage(method, csrfToken) {
        var options = { method: method, credentials: 'same-origin' };
        if (method === 'POST') {
            options.headers = { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' };
            options.body = '_csrf=' + encodeURIComponent(csrfToken || '');
        }
        return fetch('/signin/', options).then(function (response) { return response.text(); });
    }

    function parseSigninState(html) {
        var parsed = new DOMParser().parseFromString(String(html || ''), 'text/html');
        var text = ((parsed.body || {}).textContent || '').replace(/\s+/g, ' ').trim();
        var button = parsed.querySelector('.signin-hero-action .btn-post, form[action*="/signin" i] button[type="submit"], form[action*="/signin" i] input[type="submit"]');
        var label = button ? (button.textContent || button.value || '').replace(/\s+/g, ' ').trim() : '';
        var csrf = ((parsed.querySelector('input[name="_csrf"]') || {}).value || '').trim();
        var signed = /今日已签到/.test(label) || Boolean(parsed.querySelector('.signin-cal-day.done.today'));
        var loginRequired = Boolean(parsed.querySelector('input[name="username"], input[type="password"]')) && /登录/.test(text) && !/每日签到/.test(text);
        return { signed: signed, loginRequired: loginRequired, csrf: csrf, canSubmit: Boolean(button && /签到/.test(label) && !/已签到/.test(label)), label: label };
    }

    function maybeRunAutoCheckin() {
        if (!settings.autoCheckin || checkinPending || hasAutoCheckinRunToday()) {
            return;
        }
        checkinPending = true;
        requestSigninPage('GET').then(function (html) {
            var state = parseSigninState(html);
            if (state.loginRequired) {
                throw new Error('未登录');
            }
            if (state.signed) {
                markAutoCheckinRunToday();
                showStatus('今日已签到');
                return null;
            }
            if (!state.canSubmit || !state.csrf) {
                throw new Error('未找到可用签到入口');
            }
            return requestSigninPage('POST', state.csrf).then(function (resultHtml) {
                var result = parseSigninState(resultHtml);
                if (!result.loginRequired && result.signed) {
                    markAutoCheckinRunToday();
                    showStatus('已完成自动签到');
                    return;
                }
                throw new Error('自动签到未确认成功');
            });
        }).catch(function () {
            /* 登录失效、站点结构变化或网络波动时保持静默,避免打扰正常浏览。 */
        }).finally(function () {
            checkinPending = false;
        });
    }

    function ensureForumShortcutCard() {
        var existing = document.querySelector('.sbe-forum-card');
        var sidebar = document.querySelector('.forum-layout > .sidebar');
        if (!settings.stickyForumList || !sidebar || window.matchMedia('(max-width: 720px)').matches) {
            if (existing) { existing.remove(); }
            return;
        }
        if (existing && existing.parentElement === sidebar) {
            if (sidebar.lastElementChild !== existing) {
                sidebar.appendChild(existing);
            }
            return;
        }
        if (existing) { existing.remove(); }
        var sources = Array.from(document.querySelectorAll('.forum-more-panel a[href^="/go/"]'));
        if (!sources.length) {
            sources = Array.from(document.querySelectorAll('.forum-nav a[href^="/go/"]'));
        }
        if (!sources.length) {
            return;
        }
        var card = document.createElement('section');
        card.className = 'sbe-forum-card';
        card.setAttribute('aria-label', '版块快捷导航');
        var title = document.createElement('h2');
        title.textContent = '版块快捷导航';
        var list = document.createElement('nav');
        list.className = 'sbe-forum-list';
        sources.forEach(function (source) {
            var link = document.createElement('a');
            link.href = source.href;
            link.textContent = source.textContent.trim();
            if (source.classList.contains('active') || source.getAttribute('aria-current') === 'page') {
                link.setAttribute('aria-current', 'page');
            }
            list.appendChild(link);
        });
        card.appendChild(title);
        card.appendChild(list);
        /* 让快捷导航保持在原生侧栏卡片之后,避免抢占访客/用户信息的位置。 */
        sidebar.appendChild(card);
    }

    function removeLegacyTopicPostUids(roots) {
        queryWithin(roots, '.sbe-topic-post-uid').forEach(function (element) {
            element.remove();
        });
    }

    function removeLegacyTopicViews(roots) {
        queryWithin(roots, '.sbe-topic-views').forEach(function (element) {
            element.remove();
        });
    }

    function applyTopicNewWindow(roots) {
        queryWithin(roots, '.post-item .post-title[href^="/t/"], .sidebar-topic-title[href^="/t/"]').forEach(function (link) {
            if (settings.newWindowForTopics) {
                if (!link.hasAttribute('data-sbe-original-target')) {
                    link.setAttribute('data-sbe-original-target', link.getAttribute('target') || '__none__');
                    link.setAttribute('data-sbe-original-rel', link.getAttribute('rel') || '__none__');
                }
                link.target = '_blank';
                link.rel = 'noopener noreferrer';
            } else if (link.hasAttribute('data-sbe-original-target')) {
                restoreAttribute(link, 'target', link.getAttribute('data-sbe-original-target'));
                restoreAttribute(link, 'rel', link.getAttribute('data-sbe-original-rel'));
                link.removeAttribute('data-sbe-original-target');
                link.removeAttribute('data-sbe-original-rel');
            }
        });
    }

    function restoreAttribute(element, attribute, value) {
        if (value === '__none__') {
            element.removeAttribute(attribute);
        } else {
            element.setAttribute(attribute, value);
        }
    }

    function applyFilters(roots) {
        var titles = settings.titleFilters.map(function (item) { return item.toLowerCase(); });
        var users = settings.userFilters.map(function (item) { return item.toLowerCase(); });
        queryWithin(roots, '.post-item').forEach(function (item) {
            var title = ((item.querySelector('.post-title') || {}).textContent || '').trim().toLowerCase();
            var author = ((item.querySelector('.post-meta a[href^="/u/"]') || {}).textContent || '').trim().toLowerCase();
            var hidden = titles.some(function (itemTitle) { return title.indexOf(itemTitle) !== -1; }) || users.some(function (username) { return username === author; });
            if (hidden) {
                item.style.setProperty('display', 'none', 'important');
                item.setAttribute('data-sbe-filtered', '1');
            } else if (item.hasAttribute('data-sbe-filtered')) {
                item.style.removeProperty('display');
                item.removeAttribute('data-sbe-filtered');
            }
        });
    }

    function syncLatestUserAvatars(roots) {
        queryWithin(roots, '.stats-card .nu-item').forEach(function (item) {
            var name = ((item.querySelector('.nu-name') || {}).textContent || '').trim();
            if (!name) {
                var avatar = item.querySelector('.avatar-img');
                name = avatar ? (avatar.getAttribute('alt') || '').trim() : '';
            }
            if (name) {
                item.setAttribute('title', name);
                item.setAttribute('aria-label', name);
            }
        });
    }

    function alignVerificationBadges(roots) {
        queryWithin(roots, '.post-head .post-author').forEach(function (author) {
            var head = author.closest('.post-head');
            var badge = head ? head.querySelector('.verify-badge') : null;
            if (badge && badge.previousElementSibling !== author) {
                author.insertAdjacentElement('afterend', badge);
            }
        });
        queryWithin(roots, '.user-card .user-header-info').forEach(function (header) {
            var name = header.querySelector('.user-name');
            var verification = header.querySelector('.user-verify');
            var host = name && name.parentElement;
            if (!name || !verification || !host) {
                return;
            }
            var row = host.querySelector(':scope > .sbe-user-name-verify-row');
            if (!row) {
                row = document.createElement('div');
                row.className = 'sbe-user-name-verify-row';
                host.insertBefore(row, name);
            }
            if (name.parentElement !== row) {
                row.appendChild(name);
            }
            if (verification.parentElement !== row) {
                row.appendChild(verification);
            }
        });
    }

    function syncNativeReplyTreeBlocker(roots) {
        queryWithin(roots, '.topic-replies[data-reply-tree="1"]').forEach(function (container) {
            if (!settings.blockNativeReplyTree) {
                container.classList.remove('sbe-native-reply-tree-blocked');
                return;
            }
            container.classList.add('sbe-native-reply-tree-blocked');
            var flatButton = container.querySelector('[data-tree-mode="flat"]');
            if (flatButton && !flatButton.classList.contains('on')) {
                flatButton.click();
            }
        });
    }

    function ensureSearchModal(sourceForm) {
        if (searchModal || !document.body) {
            return;
        }
        searchModal = document.createElement('div');
        searchModal.id = 'sbe-search-modal';
        searchModal.hidden = true;
        searchModal.setAttribute('role', 'dialog');
        searchModal.setAttribute('aria-modal', 'true');
        searchModal.setAttribute('aria-label', '站内搜索');
        var dialog = document.createElement('div');
        dialog.id = 'sbe-search-modal-dialog';
        searchModalForm = document.createElement('form');
        searchModalForm.method = sourceForm.getAttribute('method') || 'post';
        searchModalForm.action = sourceForm.getAttribute('action') || '/search/';
        searchModalForm.setAttribute('data-no-ajax', '1');
        var sourceCsrf = sourceForm.querySelector('input[name="_csrf"]');
        if (sourceCsrf) {
            var csrf = document.createElement('input');
            csrf.type = 'hidden';
            csrf.name = '_csrf';
            csrf.value = sourceCsrf.value || '';
            searchModalForm.appendChild(csrf);
        }
        searchModalInput = document.createElement('input');
        searchModalInput.className = 'sbe-search-input';
        searchModalInput.type = 'search';
        searchModalInput.name = 'q';
        searchModalInput.placeholder = '搜索关键词';
        searchModalInput.minLength = 2;
        searchModalInput.required = true;
        var submit = document.createElement('button');
        submit.className = 'sbe-search-submit';
        submit.type = 'submit';
        submit.textContent = '搜索';
        searchModalForm.appendChild(searchModalInput);
        searchModalForm.appendChild(submit);
        var close = document.createElement('button');
        close.className = 'sbe-search-close';
        close.type = 'button';
//        close.textContent = '取消';
        close.addEventListener('click', closeSearchModal);
        dialog.appendChild(searchModalForm);
        dialog.appendChild(close);
        searchModal.appendChild(dialog);
        searchModal.addEventListener('click', function (event) {
            if (event.target === searchModal) {
                closeSearchModal();
            }
        });
        document.addEventListener('keydown', function (event) {
            if (event.key === 'Escape' && searchModal && !searchModal.hidden) {
                closeSearchModal();
            }
        });
        document.body.appendChild(searchModal);
    }

    function openSearchModal(sourceForm, value) {
        ensureSearchModal(sourceForm);
        if (!searchModal || !searchModalInput) {
            return;
        }
        searchModalInput.value = value || '';
        searchModal.hidden = false;
        window.setTimeout(function () { searchModalInput.focus(); }, 0);
    }

    function closeSearchModal() {
        if (searchModal) {
            searchModal.hidden = true;
        }
    }

    function bindSearchModal() {
        var form = document.querySelector('.top .search-form');
        if (!form || form.getAttribute('data-sbe-search-modal-bound') === '1') {
            return;
        }
        var input = form.querySelector('.search-input[type="search"]');
        var button = form.querySelector('.search-btn[type="submit"]');
        if (!input || !button) {
            return;
        }
        form.setAttribute('data-sbe-search-modal-bound', '1');
        var open = function (event) {
            event.preventDefault();
            openSearchModal(form, input.value);
        };
        button.addEventListener('click', open);
        form.addEventListener('submit', open);
    }

    function updateImageLightboxTargets(roots) {
        queryWithin(roots, '.post-content a.post-image-link').forEach(function (link) {
            var image = link.querySelector('img');
            if (settings.imageLightbox && image && (image.currentSrc || image.src)) {
                link.classList.add('sbe-image-target');
                link.setAttribute('data-sbe-lightbox', '1');
                link.setAttribute('aria-label', '打开图片预览:' + (image.alt || '图片'));
            } else {
                link.classList.remove('sbe-image-target');
                link.removeAttribute('data-sbe-lightbox');
                link.removeAttribute('aria-label');
            }
        });
        ensureImageLightboxEvents();
        if (!settings.imageLightbox) {
            closeImageLightbox();
        }
    }

    function ensureImageLightboxEvents() {
        if (lightboxEventsBound) {
            return;
        }
        lightboxEventsBound = true;
        document.addEventListener('click', function (event) {
            var link = event.target && event.target.closest ? event.target.closest('a.sbe-image-target[data-sbe-lightbox]') : null;
            if (!link || !settings.imageLightbox) {
                return;
            }
            var image = link.querySelector('img');
            if (!image) {
                return;
            }
            event.preventDefault();
            openImageLightbox(link.href || image.currentSrc || image.src, image.alt || '图片预览');
        }, true);
    }

    function ensureImageLightbox() {
        if (lightbox || !document.body) {
            return;
        }
        lightbox = document.createElement('div');
        lightbox.id = 'sbe-image-lightbox';
        lightbox.hidden = true;
        lightbox.setAttribute('role', 'dialog');
        lightbox.setAttribute('aria-modal', 'true');
        lightbox.setAttribute('aria-label', '图片预览');
        lightboxImage = document.createElement('img');
        lightbox.appendChild(lightboxImage);
        lightbox.addEventListener('click', function (event) {
            if (event.target === lightbox || event.target === lightboxImage) {
                closeImageLightbox();
            }
        });
        document.body.appendChild(lightbox);
    }

    function openImageLightbox(source, altText) {
        if (!settings.imageLightbox || !source) {
            return;
        }
        ensureImageLightbox();
        if (!lightbox || !lightboxImage) {
            return;
        }
        lightboxImage.src = source;
        lightboxImage.alt = altText || '';
        lightbox.hidden = false;
    }

    function closeImageLightbox() {
        if (!lightbox || lightbox.hidden) {
            return;
        }
        lightbox.hidden = true;
    }

    function getUserId(href) {
        var match = String(href || '').match(/\/u\/(\d+)(?:[/?#]|$)/);
        return match ? match[1] : '';
    }

    function syncNativeProfileCardBlocker() {
        if (nativeProfileCardBlockerBound || !document || !document.addEventListener) {
            return;
        }
        nativeProfileCardBlockerBound = true;
        ['pointerover', 'pointerenter', 'mouseover', 'mouseenter'].forEach(function (eventName) {
            document.addEventListener(eventName, blockNativeProfileCard, true);
        });
    }

    function blockNativeProfileCard(event) {
        if (!settings.avatarProfileCard || !settings.blockNativeProfileCard || !isTopicPage()) {
            return;
        }
        var source = event.target;
        if (!source || !source.closest) {
            return;
        }
        var target = source.closest('.post-item a[href^="/u/"], .topic-post-list .post-head a[href^="/u/"], .post-item .post-avatar img');
        if (!target) {
            return;
        }
        var profileHref = target.getAttribute('href');
        var username = target.textContent.trim();
        if (target.matches('img')) {
            var item = target.closest('.post-item');
            var author = item ? item.querySelector('.post-meta a[href^="/u/"]') : null;
            if (!author) {
                return;
            }
            profileHref = author.getAttribute('href');
            username = author.textContent.trim();
        }
        var uid = getUserId(profileHref);
        if (!uid) {
            return;
        }
        /* 在捕获阶段阻止站点为同一目标注册的悬停监听器;点击与链接导航不受影响。 */
        event.stopImmediatePropagation();
        if (event.type === 'mouseenter') {
            target.setAttribute('data-sbe-profile-url', profileHref);
            target.setAttribute('data-sbe-profile-name', username || ('UID ' + uid));
            showProfileCard(target, uid);
        }
    }

    function bindProfileCardTargets(roots) {
        if (!settings.avatarProfileCard) {
            hideProfileCard();
            return;
        }
        queryWithin(roots, '.post-item a[href^="/u/"], .topic-post-list .post-head a[href^="/u/"]').forEach(function (anchor) {
            bindProfileCardTarget(anchor, anchor.getAttribute('href'), anchor.textContent.trim());
        });
        queryWithin(roots, '.post-item .post-avatar img').forEach(function (avatar) {
            var item = avatar.closest('.post-item');
            var author = item ? item.querySelector('.post-meta a[href^="/u/"]') : null;
            if (author) {
                bindProfileCardTarget(avatar, author.getAttribute('href'), author.textContent.trim());
            }
        });
    }

    function bindProfileCardTarget(target, profileHref, username) {
        var uid = getUserId(profileHref);
        if (!target || !uid || target.getAttribute('data-sbe-profile-bound') === '1') {
            return;
        }
        target.setAttribute('data-sbe-profile-bound', '1');
        target.setAttribute('data-sbe-profile-url', profileHref);
        target.setAttribute('data-sbe-profile-name', username || ('UID ' + uid));
        target.addEventListener('mouseenter', function () { showProfileCard(target, uid); });
        target.addEventListener('mouseleave', scheduleHideProfileCard);
        if (target.matches('a')) {
            target.addEventListener('focus', function () { showProfileCard(target, uid); });
            target.addEventListener('blur', hideProfileCard);
        }
    }

    function getPublicProfile(target, uid) {
        var cached = profileCache[uid];
        if (cached && cached.data && cached.expiresAt > Date.now()) {
            return Promise.resolve(cached.data);
        }
        if (cached && cached.promise) {
            return cached.promise;
        }
        var fallbackName = target.getAttribute('data-sbe-profile-name') || target.textContent.trim() || ('UID ' + uid);
        var href = target.getAttribute('data-sbe-profile-url') || target.getAttribute('href');
        var url = new URL(href, window.location.origin).href;
        var request = fetch(url, { credentials: 'same-origin' }).then(function (response) {
            if (!response.ok) { throw new Error('资料请求失败'); }
            return response.text();
        }).then(function (html) {
            var data = parsePublicProfile(html, uid, fallbackName);
            profileCache[uid] = { data: data, expiresAt: Date.now() + PROFILE_CACHE_TTL };
            return data;
        }).catch(function () {
            var fallback = { uid: uid, username: fallbackName, rank: '暂未读取', avatarUrl: '', stats: {} };
            profileCache[uid] = { data: fallback, expiresAt: Date.now() + 60 * 1000 };
            return fallback;
        });
        profileCache[uid] = { promise: request };
        return request;
    }

    function parsePublicProfile(html, uid, fallbackName) {
        var parsed = new DOMParser().parseFromString(String(html || ''), 'text/html');
        var name = parsed.querySelector('.user-name');
        var rank = parsed.querySelector('.user-rank');
        var avatar = parsed.querySelector('.user-avatar-big img, .user-header img.avatar-img');
        var stats = {};
        parsed.querySelectorAll('.user-stat').forEach(function (stat) {
            var label = ((stat.querySelector('.user-stat-label') || {}).textContent || '').trim();
            var value = ((stat.querySelector('.user-stat-value') || {}).textContent || '').trim();
            /* 站点已将“积分”更名为“烧饼”;旧页面或缓存资料仍兼容归一到新键名。 */
            if (label === '积分') { label = '烧饼'; }
            if (label && value) { stats[label] = value; }
        });
        return {
            uid: uid,
            username: name ? name.textContent.trim() : fallbackName,
            rank: rank ? rank.textContent.replace(/\s+/g, ' ').trim() : '公开资料',
            avatarUrl: avatar ? avatar.currentSrc || avatar.src : '',
            stats: stats
        };
    }

    function ensureProfileCard() {
        if (profileCard || !document.body) {
            return;
        }
        profileCard = document.createElement('aside');
        profileCard.id = 'sbe-profile-card';
        profileCard.hidden = true;
        profileCard.setAttribute('role', 'status');
        profileCard.setAttribute('aria-live', 'polite');
        profileCard.addEventListener('mouseenter', function () { window.clearTimeout(profileHideTimer); });
        profileCard.addEventListener('mouseleave', scheduleHideProfileCard);
        document.body.appendChild(profileCard);
    }

    function renderProfileCard(data, loading) {
        if (!profileCard) {
            return;
        }
        profileCard.textContent = '';
        var head = document.createElement('div');
        head.className = 'sbe-profile-head';
        var identity = document.createElement('div');
        var nameRow = document.createElement('div');
        nameRow.className = 'sbe-profile-name-row';
        var name = document.createElement('a');
        name.className = 'sbe-profile-name';
        name.href = '/u/' + encodeURIComponent(String(data.uid)) + '/';
        name.textContent = data.username;
        var uid = document.createElement('div');
        uid.className = 'sbe-profile-uid';
        uid.textContent = 'UID ' + data.uid;
        var rank = document.createElement('div');
        rank.className = 'sbe-profile-rank';
        rank.textContent = loading ? '读取公开资料中…' : data.rank;
        nameRow.appendChild(name);
        nameRow.appendChild(uid);
        identity.appendChild(nameRow);
        identity.appendChild(rank);
        head.appendChild(identity);
        profileCard.appendChild(head);
        var grid = document.createElement('dl');
        grid.className = 'sbe-profile-stats';
        ['烧饼', '成长值'].forEach(function (label) {
            var item = document.createElement('div');
            item.className = 'sbe-profile-stat';
            var title = document.createElement('dt');
            title.textContent = label;
            var value = document.createElement('dd');
            value.textContent = loading ? '读取中' : (data.stats[label] || '未公开');
            item.appendChild(title);
            item.appendChild(value);
            grid.appendChild(item);
        });
        profileCard.appendChild(grid);
    }

    function positionProfileCard(target) {
        if (!profileCard || !target) {
            return;
        }
        var rect = target.getBoundingClientRect();
        var gap = 10;
        var maxLeft = Math.max(12, window.innerWidth - profileCard.offsetWidth - 12);
        var maxTop = Math.max(12, window.innerHeight - profileCard.offsetHeight - 12);
        var rightSide = rect.right + gap;
        var left = rightSide + profileCard.offsetWidth <= window.innerWidth - 12 ? rightSide : rect.left - profileCard.offsetWidth - gap;
        profileCard.style.left = Math.round(Math.max(12, Math.min(maxLeft, left))) + 'px';
        profileCard.style.top = Math.round(Math.max(12, Math.min(maxTop, rect.top))) + 'px';
    }

    function showProfileCard(target, uid) {
        if (!settings.avatarProfileCard || !target) {
            return;
        }
        window.clearTimeout(profileHideTimer);
        ensureProfileCard();
        if (!profileCard) { return; }
        var initial = { uid: uid, username: target.getAttribute('data-sbe-profile-name') || ('UID ' + uid), rank: '', avatarUrl: '', stats: {} };
        profileCard.dataset.uid = uid;
        renderProfileCard(initial, true);
        profileCard.hidden = false;
        positionProfileCard(target);
        getPublicProfile(target, uid).then(function (data) {
            if (!profileCard || profileCard.hidden || profileCard.dataset.uid !== uid) { return; }
            renderProfileCard(data, false);
            positionProfileCard(target);
        });
    }

    function scheduleHideProfileCard() {
        window.clearTimeout(profileHideTimer);
        profileHideTimer = window.setTimeout(hideProfileCard, 120);
    }

    function hideProfileCard() {
        window.clearTimeout(profileHideTimer);
        if (profileCard) { profileCard.hidden = true; }
    }

    function isTopicPage() {
        return /^\/t\/\d+/.test(window.location.pathname) && Boolean(document.querySelector('.topic-post-list, .topic-replies'));
    }

    function ensureReadingProgress() {
        if (progressBar || !document.body) {
            return;
        }
        progressBar = document.createElement('div');
        progressBar.id = 'sbe-reading-progress';
        progressBar.hidden = true;
        progressBar.setAttribute('aria-hidden', 'true');
        document.body.appendChild(progressBar);
        if (!progressBound) {
            progressBound = true;
            window.addEventListener('scroll', scheduleReadingProgressUpdate, { passive: true });
            window.addEventListener('resize', scheduleReadingProgressUpdate, { passive: true });
        }
    }

    function scheduleReadingProgressUpdate() {
        if (progressFrame) { return; }
        var run = function () {
            progressFrame = 0;
            updateReadingProgressNow();
        };
        if (typeof window.requestAnimationFrame === 'function') {
            progressFrame = window.requestAnimationFrame(run);
        } else {
            progressFrame = window.setTimeout(run, 16);
        }
    }

    function updateReadingProgressNow() {
        ensureReadingProgress();
        if (!progressBar) { return; }
        var enabled = settings.readingProgress && isTopicPage();
        if (progressBar.hidden === enabled) { progressBar.hidden = !enabled; }
        if (!enabled) {
            lastProgressValue = '';
            return;
        }
        var scrollable = Math.max(1, document.documentElement.scrollHeight - window.innerHeight);
        var value = Math.max(0, Math.min(1, window.scrollY / scrollable)).toFixed(4);
        if (value !== lastProgressValue) {
            lastProgressValue = value;
            progressBar.style.setProperty('--sbe-progress', value);
        }
    }

    function updateCodeCopyButtons(roots) {
        queryWithin(roots, '.post-content pre').forEach(function (block) {
            var existing = block.querySelector(':scope > .sbe-code-copy');
            if (!settings.codeCopyButtons) {
                if (existing) { existing.remove(); }
                block.removeAttribute('data-sbe-copyable');
                return;
            }
            block.setAttribute('data-sbe-copyable', '1');
            if (existing) { return; }
            var button = document.createElement('button');
            button.type = 'button';
            button.className = 'sbe-code-copy';
            button.textContent = '复制';
            button.setAttribute('aria-label', '复制代码');
            button.addEventListener('click', function () {
                copyText(block.innerText || block.textContent || '').then(function () {
                    button.textContent = '已复制';
                    window.setTimeout(function () { button.textContent = '复制'; }, 1200);
                }).catch(function () {
                    button.textContent = '失败';
                    window.setTimeout(function () { button.textContent = '复制'; }, 1200);
                });
            });
            block.appendChild(button);
        });
    }

    function copyText(text) {
        if (navigator.clipboard && navigator.clipboard.writeText) {
            return navigator.clipboard.writeText(text);
        }
        return new Promise(function (resolve, reject) {
            try {
                var input = document.createElement('textarea');
                input.value = text;
                input.style.position = 'fixed';
                input.style.opacity = '0';
                document.body.appendChild(input);
                input.select();
                var copied = document.execCommand('copy');
                input.remove();
                copied ? resolve() : reject(new Error('复制失败'));
            } catch (error) {
                reject(error);
            }
        });
    }

    function ensureInterface() {
        if (panel && document.body && document.body.contains(panel)) {
            return;
        }
        if (!document.body) {
            return;
        }
        toggleButton = document.createElement('button');
        toggleButton.className = 'sbe-layout-toggle';
        toggleButton.type = 'button';
        toggleButton.title = 'SB.SB 增强设置';
        toggleButton.setAttribute('aria-label', '打开 SB.SB 增强设置');
        toggleButton.setAttribute('aria-controls', 'sbe-layout-panel');
        toggleButton.setAttribute('aria-expanded', 'false');
        toggleButton.innerHTML = '<svg viewBox="0 0 24 24" fill="none" aria-hidden="true"><path d="M4 7h10M18 7h2M4 17h2M10 17h10M14 4v6M7 14v6" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg>';

        panel = document.createElement('section');
        panel.id = 'sbe-layout-panel';
        panel.className = 'sbe-layout-panel';
        panel.hidden = true;
        panel.setAttribute('role', 'dialog');
        panel.setAttribute('aria-modal', 'false');
        panel.setAttribute('aria-label', 'SB.SB 增强设置');
        panel.innerHTML = [
            '<div class="sbe-panel-head"><h2 class="sbe-panel-title">SB.SB 增强设置</h2><p class="sbe-panel-credit">Design by <a href="https://com.com.ee" target="_blank" rel="noopener noreferrer external">COM</a></p><button class="sbe-icon-button" type="button" data-sbe-close aria-label="关闭设置">×</button></div>',
            '<div class="sbe-panel-body">',
            '<section class="sbe-section"><h3 class="sbe-section-title">布局参数</h3><div data-sbe-ranges></div><label class="sbe-field"><span>列表密度</span><select class="sbe-select" data-sbe-density><option value="compact">紧凑</option><option value="comfortable">舒适</option><option value="relaxed">宽松</option></select></label></section>',
            '<section class="sbe-section"><h3 class="sbe-section-title">主题与配色</h3><label class="sbe-field"><span>配色方案</span><select class="sbe-select" data-sbe-theme></select></label><p class="sbe-note">深色主题的悬停状态仅使用相邻层级的面板色和品牌柔光,不会出现白底反转或白色闪烁。</p></section>',
            '<section class="sbe-section"><h3 class="sbe-section-title">阅读与列表</h3>',
            '<label class="sbe-check-line"><input type="checkbox" data-sbe-new-window><span>主题链接在新窗口打开</span></label>',
            '<label class="sbe-check-line"><input type="checkbox" data-sbe-sticky-forums><span>侧栏常驻版块快捷导航</span></label>',
            '<label class="sbe-check-line"><input type="checkbox" data-sbe-avatar-card><span>头像与用户名悬停资料卡</span></label>',
            '<label class="sbe-check-line"><input type="checkbox" data-sbe-block-native-card><span>屏蔽网站自带的悬停资料卡</span></label>',
            '<label class="sbe-check-line"><input type="checkbox" data-sbe-block-reply-tree><span>屏蔽网站回复树</span></label>',
            '<label class="sbe-check-line"><input type="checkbox" data-sbe-lightbox><span>帖子图片灯箱</span></label>',
            '<label class="sbe-check-line"><input type="checkbox" data-sbe-reading-progress><span>主题页阅读进度条</span></label>',
            '<label class="sbe-check-line"><input type="checkbox" data-sbe-code-copy><span>代码块一键复制</span></label>',
            '<label class="sbe-check-line"><input type="checkbox" data-sbe-auto-checkin><span>发现站内原生签到入口时,每日自动签到一次</span></label>',
            '<p class="sbe-note">仅在已登录页面检测到原生“签到”控件时才会触发;脚本不构造、猜测或转发签到接口。</p>',
            '</section>',
            '<section class="sbe-section"><h3 class="sbe-section-title">内容过滤</h3>',
            '<label class="sbe-field"><span>过滤标题关键字(每行一个,最多 10 项)</span><textarea class="sbe-textarea" data-sbe-title-filters rows="3" placeholder="输入要隐藏的标题关键字"></textarea></label>',
            '<label class="sbe-field"><span>过滤用户名(每行一个,最多 10 项)</span><textarea class="sbe-textarea" data-sbe-user-filters rows="3" placeholder="输入要隐藏的发帖用户名"></textarea></label>',
            '<p class="sbe-note">本站原生支持图片、附件和截图粘贴上传。本脚本不会使用第三方图床,也不会将图片或附件外传。</p>',
            '</section>',
            '<div class="sbe-actions"><button class="sbe-button" type="button" data-sbe-reset>恢复默认</button><button class="sbe-button sbe-button-primary" type="button" data-sbe-done>完成</button></div>',
            '<p class="sbe-status" data-sbe-status aria-live="polite"></p></div>'
        ].join('');

        document.body.appendChild(toggleButton);
        document.body.appendChild(panel);
        statusElement = panel.querySelector('[data-sbe-status]');
        buildRangeControls();
        buildThemeControls();
        bindInterfaceEvents();
        syncInterface();
    }

    function buildRangeControls() {
        var container = panel.querySelector('[data-sbe-ranges]');
        if (!container) { return; }
        container.textContent = '';
        RANGE_DEFINITIONS.forEach(function (definition) {
            var row = document.createElement('label');
            row.className = 'sbe-range';
            var head = document.createElement('span');
            head.className = 'sbe-range-head';
            var label = document.createElement('span');
            label.textContent = definition.label;
            var output = document.createElement('output');
            output.setAttribute('data-sbe-range-output', definition.key);
            head.appendChild(label);
            head.appendChild(output);
            var input = document.createElement('input');
            input.type = 'range';
            input.min = String(definition.min);
            input.max = String(definition.max);
            input.step = String(definition.step);
            input.setAttribute('data-sbe-range', definition.key);
            row.appendChild(head);
            row.appendChild(input);
            container.appendChild(row);
        });
    }

    function buildThemeControls() {
        var select = panel.querySelector('[data-sbe-theme]');
        if (!select) { return; }
        select.textContent = '';
        Object.keys(THEMES).forEach(function (key) {
            var option = document.createElement('option');
            option.value = key;
            option.textContent = THEMES[key].label;
            select.appendChild(option);
        });
    }

    function bindInterfaceEvents() {
        toggleButton.addEventListener('click', function () { setPanelOpen(panel.hidden); });
        panel.addEventListener('click', function (event) {
            if (event.target.closest('[data-sbe-close], [data-sbe-done]')) {
                setPanelOpen(false);
                return;
            }
            if (event.target.closest('[data-sbe-reset]')) {
                settings = normalizeSettings(null);
                applySettings();
                syncInterface();
                persistSettings();
            }
        });
        panel.addEventListener('input', function (event) {
            var target = event.target;
            var rangeKey = target.getAttribute('data-sbe-range');
            if (rangeKey) {
                settings[rangeKey] = Number(target.value);
                /* 拖动滑块只更新 CSS 变量,不触发列表、图片、资料卡等全页扫描。 */
                applySettings({ deferDynamic: true });
                syncRangeOutput(rangeKey);
                scheduleSave();
                return;
            }
            if (target.matches('[data-sbe-title-filters], [data-sbe-user-filters]')) {
                window.clearTimeout(filterTimer);
                filterTimer = window.setTimeout(function () {
                    settings.titleFilters = normalizeFilterList(panel.querySelector('[data-sbe-title-filters]').value.split(/\n/));
                    settings.userFilters = normalizeFilterList(panel.querySelector('[data-sbe-user-filters]').value.split(/\n/));
                    applyFilters();
                    scheduleSave();
                }, SAVE_DELAY);
            }
        });
        panel.addEventListener('change', function (event) {
            var target = event.target;
            var boolMap = [
                ['[data-sbe-new-window]', 'newWindowForTopics'], ['[data-sbe-sticky-forums]', 'stickyForumList'], ['[data-sbe-avatar-card]', 'avatarProfileCard'],
                ['[data-sbe-block-native-card]', 'blockNativeProfileCard'], ['[data-sbe-block-reply-tree]', 'blockNativeReplyTree'], ['[data-sbe-lightbox]', 'imageLightbox'], ['[data-sbe-reading-progress]', 'readingProgress'], ['[data-sbe-code-copy]', 'codeCopyButtons'], ['[data-sbe-auto-checkin]', 'autoCheckin']
            ];
            for (var index = 0; index < boolMap.length; index += 1) {
                if (target.matches(boolMap[index][0])) {
                    settings[boolMap[index][1]] = target.checked;
                    applySettings();
                    scheduleSave();
                    return;
                }
            }
            if (target.matches('[data-sbe-theme]')) {
                settings.theme = target.value;
                applySettings({ deferDynamic: true });
                scheduleSave();
                return;
            }
            if (target.matches('[data-sbe-density]')) {
                settings.density = target.value;
                applySettings({ deferDynamic: true });
                scheduleSave();
            }
        });
        document.addEventListener('keydown', function (event) {
            if (event.key === 'Escape' && panel && !panel.hidden) {
                setPanelOpen(false);
            }
        });
    }

    function syncInterface() {
        if (!panel) { return; }
        RANGE_DEFINITIONS.forEach(function (definition) {
            var input = panel.querySelector('[data-sbe-range="' + definition.key + '"]');
            if (input) { input.value = String(settings[definition.key]); }
            syncRangeOutput(definition.key);
        });
        var setChecked = function (selector, value) {
            var element = panel.querySelector(selector);
            if (element) { element.checked = value; }
        };
        panel.querySelector('[data-sbe-theme]').value = settings.theme;
        panel.querySelector('[data-sbe-density]').value = settings.density;
        setChecked('[data-sbe-new-window]', settings.newWindowForTopics);
        setChecked('[data-sbe-sticky-forums]', settings.stickyForumList);
        setChecked('[data-sbe-avatar-card]', settings.avatarProfileCard);
        setChecked('[data-sbe-block-native-card]', settings.blockNativeProfileCard);
        setChecked('[data-sbe-block-reply-tree]', settings.blockNativeReplyTree);
        setChecked('[data-sbe-lightbox]', settings.imageLightbox);
        setChecked('[data-sbe-reading-progress]', settings.readingProgress);
        setChecked('[data-sbe-code-copy]', settings.codeCopyButtons);
        setChecked('[data-sbe-auto-checkin]', settings.autoCheckin);
        panel.querySelector('[data-sbe-title-filters]').value = settings.titleFilters.join('\n');
        panel.querySelector('[data-sbe-user-filters]').value = settings.userFilters.join('\n');
    }

    function syncRangeOutput(key) {
        if (!panel) { return; }
        var definition = RANGE_DEFINITIONS.find(function (item) { return item.key === key; });
        var output = panel.querySelector('[data-sbe-range-output="' + key + '"]');
        if (definition && output) { output.textContent = settings[key] + definition.unit; }
    }

    function setPanelOpen(open) {
        ensureInterface();
        if (!panel || !toggleButton) { return; }
        panel.hidden = !open;
        toggleButton.setAttribute('aria-expanded', String(Boolean(open)));
        if (open) { syncInterface(); }
    }

    function showStatus(message) {
        if (!statusElement) { return; }
        statusElement.textContent = message;
        window.setTimeout(function () {
            if (statusElement && statusElement.textContent === message) { statusElement.textContent = ''; }
        }, 1800);
    }
})();