:root {
    --bg: #fafafa;
    --bg-card: #ffffff;
    --bg-hover: #f0f0f0;
    --text: #1a1a1a;
    --text-secondary: #666;
    --text-muted: #999;
    --border: #e5e5e5;
    --accent: #4a5568;
    --accent-hover: #2d3748;
    --radius: 8px;
    --shadow: 0 1px 3px rgba(0,0,0,0.06);
    --shadow-lg: 0 8px 24px rgba(0,0,0,0.1);
    --transition: 200ms ease;
}

/* 跟随系统：深色模式 */
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
        --bg: #0a0a0a;
        --bg-card: #161616;
        --bg-hover: #222;
        --text: #f0f0f0;
        --text-secondary: #a0a0a0;
        --text-muted: #666;
        --border: #2a2a2a;
        --accent: #a0aec0;
        --accent-hover: #cbd5e0;
        --shadow: 0 1px 3px rgba(0,0,0,0.3);
        --shadow-lg: 0 8px 24px rgba(0,0,0,0.5);
    }
}

/* 强制深色模式 */
:root[data-theme="dark"] {
    --bg: #0a0a0a;
    --bg-card: #161616;
    --bg-hover: #222;
    --text: #f0f0f0;
    --text-secondary: #a0a0a0;
    --text-muted: #666;
    --border: #2a2a2a;
    --accent: #a0aec0;
    --accent-hover: #cbd5e0;
    --shadow: 0 1px 3px rgba(0,0,0,0.3);
    --shadow-lg: 0 8px 24px rgba(0,0,0,0.5);
}

* { margin: 0; padding: 0; box-sizing: border-box; }

body {
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
    background: var(--bg);
    color: var(--text);
    line-height: 1.5;
    min-height: 100vh;
    /* dvh 跟随地址栏展开/收起：不支持 dvh 的浏览器沿用上一行的 100vh */
    min-height: 100dvh;
    -webkit-font-smoothing: antialiased;
}

.app {
    max-width: 1200px;
    margin: 0 auto;
    padding: 48px 24px;
    min-height: 100vh;
    min-height: 100dvh;
}

.header {
    text-align: center;
    margin-bottom: 48px;
}

.search-wrapper {
    position: relative;
    display: inline-block;
    width: 100%;
    max-width: 560px;
}

.search {
    display: flex;
    align-items: center;
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: 999px;
    padding: 6px;
    gap: 4px;
    width: 100%;
    box-shadow: var(--shadow);
    transition: box-shadow var(--transition), border-color var(--transition);
}

.search:focus-within {
    border-color: var(--accent);
    box-shadow: var(--shadow-lg);
}

.search-engine {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 10px 14px;
    background: var(--bg-hover);
    border: none;
    border-radius: 999px;
    color: var(--text-secondary);
    font-size: 13px;
    font-weight: 500;
    cursor: pointer;
    transition: all var(--transition);
    white-space: nowrap;
    flex-shrink: 0;
    height: 40px;
}

.search-engine:hover { background: var(--border); color: var(--text); }

.engine-name { max-width: 80px; overflow: hidden; text-overflow: ellipsis; }

/* .engine-arrow 的定义已统一到文件尾部的材质层（基础样式 + 展开态翻转）。
   这里原先还有一条 `.search-engine.active .engine-arrow { transform: rotate(180deg) }`，
   已删除：JS 在 app.js:314 同时设 `.active` 类和 aria-expanded，翻转只需一处驱动，
   统一改用语义化的 `[aria-expanded="true"]`，避免同一状态两条规则各自旋转。 */

.search-input {
    flex: 1;
    border: none;
    background: transparent;
    padding: 10px 12px;
    font-size: 16px;
    color: var(--text);
    outline: none;
    min-width: 0;
}

.search-input::placeholder { color: var(--text-muted); }

.search-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    background: var(--accent);
    border: none;
    border-radius: 50%;
    color: white;
    cursor: pointer;
    transition: all var(--transition);
    flex-shrink: 0;
}

.search-btn:hover { background: var(--accent-hover); transform: scale(1.05); }

.search-btn svg {
    width: 18px;
    height: 18px;
    fill: none;
    stroke: currentColor;
    stroke-width: 2.5;
    stroke-linecap: round;
    stroke-linejoin: round;
}

.engine-dropdown {
    position: absolute;
    /* 4px 是与仿真一致的间距；58px 把下拉对齐到引擎按钮下方：
       1px 表单边框 + 5px 内边距 + 48px 模式按钮 + 4px 间距 = 58px。
       模式按钮三个标签都是两字，min-width 48px 稳定生效，这个偏移不会漂。
       ≤370px 有独立偏移（模式按钮收窄到 43px、间距 2px）。 */
    top: calc(100% + 4px);
    left: 58px;
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-lg);
    min-width: 152px;
    padding: 5px;
    z-index: 100;
}

.engine-dropdown[hidden] { display: none; }

@keyframes dropIn {
    from { opacity: 0; transform: translateY(-8px); }
    to { opacity: 1; transform: translateY(0); }
}

.engine-option {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 12px;
    border-radius: 6px;
    color: var(--text);
    font-size: 14px;
    cursor: pointer;
    transition: background var(--transition);
}

.engine-option:hover { background: var(--bg-hover); }
.engine-option.active { background: var(--bg-hover); font-weight: 500; }

.engine-option::before {
    content: '';
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--accent);
    opacity: 0;
    transition: opacity var(--transition);
}

.engine-option.active::before { opacity: 1; }

.grid {
    display: flex;
    flex-direction: column;
    gap: 40px;
}

.category {
    animation: fadeIn 0.3s ease;
}

@keyframes fadeIn {
    from { opacity: 0; transform: translateY(10px); }
    to { opacity: 1; transform: translateY(0); }
}

.category-header {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-bottom: 16px;
    padding-bottom: 8px;
    border-bottom: 1px solid var(--border);
}

.category-title {
    font-size: 13px;
    font-weight: 500;
    color: var(--text-secondary);
    text-transform: uppercase;
    letter-spacing: 1px;
}

.bookmarks {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
    gap: 12px;
}

.bookmark {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
    padding: 20px 12px;
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    text-decoration: none;
    color: var(--text);
    transition: all var(--transition);
    cursor: pointer;
}

.bookmark:hover {
    transform: translateY(-2px);
    border-color: var(--accent);
    box-shadow: var(--shadow);
}

.bookmark:active, .btn:active, .search-btn:active {
    transform: translateY(1px);
}

.bookmark:focus-visible, .btn:focus-visible,
.search-btn:focus-visible, .search-engine:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 3px;
}

.bookmark-icon {
    width: 28px;
    height: 28px;
    border-radius: 6px;
    object-fit: contain;
    background: var(--bg-hover);
}

.bookmark-title {
    font-size: 13px;
    text-align: center;
    word-break: break-word;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.modal {
    position: fixed;
    inset: 0;
    z-index: 1000;
    display: flex;
    align-items: center;
    justify-content: center;
    animation: fadeIn 0.2s ease;
}

.modal[hidden] { display: none; }

.modal-backdrop {
    position: absolute;
    inset: 0;
    background: rgba(0,0,0,0.5);
    backdrop-filter: blur(4px);
}

.modal-content {
    position: relative;
    width: 90%;
    max-width: 900px;
    height: 80vh;
    background: var(--bg-card);
    border-radius: 12px;
    box-shadow: var(--shadow-lg);
    display: flex;
    flex-direction: column;
    animation: slideUp 0.3s ease;
}

@keyframes slideUp {
    from { opacity: 0; transform: translateY(20px); }
    to { opacity: 1; transform: translateY(0); }
}

.modal-header {
    padding: 16px 20px;
    border-bottom: 1px solid var(--border);
    display: flex;
    align-items: center;
    justify-content: space-between;
}

.modal-header h2 { font-size: 16px; font-weight: 500; }

.modal-actions { display: flex; align-items: center; gap: 8px; }

.config-save-status {
    min-width: 0;
    color: var(--text-secondary);
    font-size: 12px;
}

.config-save-status[data-state="error"] { color: #b93232; }

.toast {
    position: fixed;
    left: 50%;
    bottom: 28px;
    z-index: 1100;
    transform: translateX(-50%);
    padding: 10px 16px;
    border-radius: 8px;
    background: var(--text);
    color: var(--bg);
    box-shadow: var(--shadow-lg);
    font-size: 13px;
    pointer-events: none;
}

.toast[hidden] { display: none; }

.modal-body {
    flex: 1;
    overflow-y: auto;
    padding: 20px;
}

.btn {
    padding: 8px 16px;
    border-radius: 6px;
    font-size: 14px;
    font-weight: 500;
    cursor: pointer;
    border: 1px solid var(--border);
    background: transparent;
    color: var(--text-secondary);
    transition: all var(--transition);
}

.btn:hover { background: var(--bg-hover); }
.btn:disabled { opacity: .55; cursor: wait; }
.btn-primary { background: var(--accent); color: white; border-color: var(--accent); }
.btn-primary:hover { background: var(--accent-hover); }
.btn-danger { color: #e53e3e; border-color: #e53e3e; }
.btn-danger:hover { background: #e53e3e; color: white; }
.btn-sm { padding: 4px 10px; font-size: 12px; }

.section { margin-bottom: 32px; }
.section:last-child { margin-bottom: 0; }
.section-title {
    font-size: 12px;
    font-weight: 600;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 1px;
    margin-bottom: 12px;
}

.item {
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 12px;
    margin-bottom: 8px;
}

.item:last-child { margin-bottom: 0; }

.item-row {
    display: flex;
    gap: 8px;
    margin-bottom: 8px;
}

.item-row:last-child { margin-bottom: 0; }

.item-row input {
    flex: 1;
    padding: 8px 12px;
    border: 1px solid var(--border);
    border-radius: 4px;
    background: var(--bg-card);
    color: var(--text);
    font-size: 14px;
}

.item-row input:focus { outline: none; border-color: var(--accent); }

.item-header {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 12px;
}

.item-header input { flex: 1; font-weight: 500; }

.add-btn {
    width: 100%;
    padding: 10px;
    border: 1px dashed var(--border);
    border-radius: var(--radius);
    background: transparent;
    color: var(--text-muted);
    font-size: 13px;
    cursor: pointer;
    transition: all var(--transition);
    margin-top: 12px;
}

.add-btn:hover { border-color: var(--accent); color: var(--accent); }

@media (max-width: 768px) {
    .app { padding: 32px 16px; }
    .header { margin-bottom: 32px; position: relative; }
    .grid { gap: 32px; }
    .bookmarks { grid-template-columns: repeat(auto-fill, minmax(100px, 1fr)); gap: 8px; }
    .bookmark { padding: 16px 8px; }
    .modal-content { width: 95%; max-height: 90vh; border-radius: 8px; }
    .modal-header { padding: 12px 16px; }
    .modal-header h2 { font-size: 14px; }
    .modal-body { padding: 12px; }
    .modal-actions { gap: 6px; }
    .btn { padding: 6px 12px; font-size: 13px; }
    .btn-sm { padding: 4px 8px; font-size: 11px; }
    .section { margin-bottom: 20px; }
    .item { padding: 10px; }
    .item-row { flex-wrap: wrap; }
    .item-row input { flex: 1 1 calc(50% - 4px); min-width: 0; font-size: 13px; padding: 8px 10px; }
    .item-row .btn { flex: 0 0 100%; margin-top: 4px; }
    .item-header { flex-wrap: wrap; gap: 6px; margin-bottom: 8px; }
    .item-header input { flex: 1; min-width: 120px; font-size: 13px; padding: 6px 10px; }
    .add-btn { padding: 8px; font-size: 12px; }
    .search { max-width: 100%; }
    .search-engine { padding: 8px 12px; height: 36px; }
    .engine-name { max-width: 60px; font-size: 12px; }
    .search-input { padding: 10px 12px; font-size: 16px; }
    .search-btn { width: 36px; height: 36px; }
    .search-btn svg { width: 16px; height: 16px; }
    /* 这里原本把 left 重置为 0，把下拉拉回搜索框左缘、与引擎按钮脱开。
       窄屏下保持与基础规则同一条偏移（模式按钮 min-width 未变）。
       原先还带着一个 `right: auto` —— 基础规则从未设过 right，是死声明。 */
    .engine-dropdown { min-width: 140px; }
}

@media (max-width: 480px) {
    .app { padding: 24px 12px; }
    .bookmarks { grid-template-columns: repeat(3, 1fr); }
    .bookmark { padding: 12px 6px; }
    .bookmark-icon { width: 24px; height: 24px; }
    .bookmark-title { font-size: 12px; }
    .modal-content { width: 100%; max-height: 100vh; border-radius: 0; }
    .modal-header { position: sticky; top: 0; background: var(--bg-card); z-index: 10; }
    .item-row input { flex: 1 1 100%; }
    .item-header { gap: 4px; }
    .item-header input { min-width: 80px; }
    .search-engine { padding: 6px 10px; height: 32px; }
    .engine-name { max-width: 50px; }
    .search-input { padding: 8px 10px; font-size: 16px; }
    .search-btn { width: 32px; height: 32px; }

    /* 窄屏管理面板的调整放在 admin.css 的 ≤480px 块里：那个文件在
       styles.css 之后加载，且用的是 `.modal .fav-actions` 这类更高特异性的
       选择器，在这里写会被静默覆盖（本次实测踩过：头部仍停在 118.5px）。 */
}

[hidden] { display: none !important; }

/* 搜索栏的外观过渡。 */
.search { transition: min-height var(--transition), padding var(--transition), border-radius var(--transition), border-color 160ms, box-shadow 160ms, background 160ms; }

@media (max-width: 768px) {
}

/* 帮助弹窗 */
.help-overlay {
    position: fixed;
    inset: 0;
    background: rgba(0,0,0,0.5);
    backdrop-filter: blur(4px);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 1000;
    padding: 20px;
    animation: fadeIn 0.2s ease;
}

.help-content {
    position: relative;
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: 12px;
    padding: 24px;
    max-width: 400px;
    width: 100%;
    box-shadow: var(--shadow-lg);
}

.help-content h3 {
    font-size: 16px;
    margin-bottom: 16px;
    color: var(--text);
}

.help-close {
    position: absolute;
    top: 12px;
    right: 12px;
    width: 28px;
    height: 28px;
    border: none;
    background: var(--bg-hover);
    color: var(--text-muted);
    border-radius: 50%;
    font-size: 18px;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
}

.help-close:hover {
    background: var(--border);
    color: var(--text);
}

.help-section {
    margin-bottom: 16px;
    padding-bottom: 16px;
    border-bottom: 1px solid var(--border);
}

.help-section:last-child {
    margin-bottom: 0;
    padding-bottom: 0;
    border-bottom: none;
}

.help-section strong {
    display: block;
    font-size: 13px;
    color: var(--text);
    margin-bottom: 8px;
}

.help-section p {
    font-size: 12px;
    color: var(--text-secondary);
    margin: 4px 0;
    line-height: 1.5;
}

.help-section .help-tip {
    font-size: 11px;
    color: var(--text-muted);
    margin: 2px 0 2px 12px;
}

.help-section .help-tip::before {
    content: "•";
    margin-right: 6px;
}

.help-section code {
    background: var(--bg-hover);
    padding: 2px 6px;
    border-radius: 4px;
    font-family: 'SF Mono', 'Menlo', monospace;
    font-size: 11px;
}

.help-note {
    font-size: 11px;
    color: var(--text-muted);
    text-align: center;
    margin-top: 12px;
    padding-top: 12px;
    border-top: 1px solid var(--border);
}

/* 移动端适配 */
@media (max-width: 768px) {

    .help-content {
        padding: 20px;
    }
}

/* ========== Settings Row ========== */
.setting-row {
    display: flex;
    align-items: center;
    padding: 14px 0;
    border-bottom: 1px solid var(--border);
}

.setting-row:last-child {
    border-bottom: none;
}

.setting-row label {
    display: flex;
    justify-content: space-between;
    align-items: center;
    width: 100%;
    gap: 16px;
    font-size: 14px;
    color: var(--text);
}

.setting-row select {
    padding: 8px 12px;
    border: 1px solid var(--border);
    border-radius: 6px;
    background: var(--bg-card);
    color: var(--text);
    font-size: 14px;
    min-width: 140px;
    cursor: pointer;
    transition: border-color var(--transition);
}

.setting-row select:focus {
    outline: none;
    border-color: var(--accent);
}

/* ========== Toggle Switch ========== */
.toggle-switch {
    position: relative;
    display: inline-block;
    width: 44px;
    height: 24px;
}

.toggle-switch input {
    opacity: 0;
    width: 0;
    height: 0;
}

.toggle-slider {
    position: absolute;
    cursor: pointer;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background-color: #ccc;
    border-radius: 24px;
    transition: 0.3s;
}

.toggle-slider:before {
    content: "";
    position: absolute;
    height: 18px;
    width: 18px;
    left: 3px;
    bottom: 3px;
    background-color: white;
    border-radius: 50%;
    transition: 0.3s;
}

.toggle-switch input:checked + .toggle-slider {
    background-color: var(--accent);
}

.toggle-switch input:checked + .toggle-slider:before {
    transform: translateX(20px);
}

.btn-danger {
    background: #ef4444;
    color: white;
    border: none;
}

.btn-danger:hover {
    background: #dc2626;
}

/* ========== Text-Only Bookmark Mode ========== */
.bookmark-text-only {
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: center;
    padding: 14px 16px;
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    text-decoration: none;
    color: var(--text);
    transition: all var(--transition);
    cursor: pointer;
    min-height: auto;
}

.bookmark-text-only:hover {
    transform: translateY(-2px);
    border-color: var(--accent);
    box-shadow: var(--shadow);
    background: var(--bg-hover);
}

.bookmark-text-only:active {
    transform: translateY(0);
}

.bookmark-text-only .bookmark-title {
    font-size: 14px;
    font-weight: 500;
    text-align: center;
    margin: 0;
    display: block;
    -webkit-line-clamp: 1;
}

/* Mobile optimizations for text-only mode */
@media (max-width: 768px) {
    .bookmark-text-only {
        padding: 16px 14px;
        min-height: 52px;
    }

    .bookmark-text-only .bookmark-title {
        font-size: 15px;
    }

    .setting-row {
        padding: 12px 0;
    }

    .setting-row label {
        flex-direction: column;
        align-items: flex-start;
        gap: 8px;
    }

    .setting-row select {
        width: 100%;
    }
}

@media (max-width: 480px) {
    .bookmark-text-only {
        padding: 14px 12px;
        min-height: 48px;
    }

    .bookmark-text-only .bookmark-title {
        font-size: 14px;
    }
}

/* ========== 书签模糊检索 ========== */

/* 检索模式样式 */
.search.fav-search-mode {
    border-color: var(--accent);
}

/* 原本这里有一条 `.search.fav-search-mode .search-btn { background:#f59e0b }`：
   书签态的「打开」按钮是琥珀色。它是旧按钮的配色，材质层改成实心陶土后
   它以 (0,3,0) 的特异性压过 (0,1,0) 的 .search-btn，实心底被换成琥珀底。
   主操作在网页态与书签态应是同一种材质，删除而非覆盖。 */

/* 检索下拉 */
.fav-dropdown {
    position: absolute;
    top: calc(100% + 8px);
    left: 0;
    right: 0;
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-lg);
    max-height: 400px;
    overflow-y: auto;
    z-index: 100;
    animation: dropIn 0.15s ease;
}

.fav-dropdown[hidden] { display: none; }

.fav-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 12px;
    text-decoration: none;
    color: var(--text);
    border-bottom: 1px solid var(--border);
    transition: background var(--transition);
}

.fav-item:last-child { border-bottom: none; }
.fav-item:hover, .fav-item.selected { background: var(--bg-hover); }

.fav-icon {
    width: 18px;
    height: 18px;
    border-radius: 3px;
    object-fit: contain;
    background: var(--bg-hover);
    flex-shrink: 0;
}

.fav-info {
    flex: 1;
    min-width: 0;
}

.fav-title {
    font-size: 13px;
    font-weight: 500;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.fav-title mark {
    background: #fef08a;
    color: inherit;
    padding: 0 1px;
    border-radius: 2px;
}

@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .fav-title mark {
        background: #854d0e;
    }
}

:root[data-theme="dark"] .fav-title mark {
    background: #854d0e;
}

.fav-private-icon {
    fill: none;
    stroke: currentColor;
    stroke-width: 2;
    stroke-linecap: round;
    stroke-linejoin: round;
    opacity: 0.5;
    vertical-align: middle;
    margin-right: 2px;
    flex-shrink: 0;
}

.fav-meta {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-top: 2px;
    font-size: 11px;
    color: var(--text-muted);
}

.fav-category {
    background: var(--bg-hover);
    padding: 1px 5px;
    border-radius: 3px;
    color: var(--text-secondary);
    font-size: 10px;
}

.fav-host {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.fav-empty {
    padding: 24px;
    text-align: center;
    color: var(--text-muted);
    font-size: 14px;
}

/* 管理面板收藏夹区域 */
.fav-stats {
    font-size: 14px;
    color: var(--text-secondary);
    margin-bottom: 12px;
}

.fav-stats code {
    background: var(--bg-hover);
    padding: 2px 6px;
    border-radius: 4px;
    font-family: 'SF Mono', 'Menlo', monospace;
    font-size: 12px;
}

.fav-hint {
    font-size: 12px;
    color: var(--text-muted);
}

.fav-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}

/* 收藏管理器 */
.fav-manager-header {
    display: flex;
    gap: 8px;
    margin-bottom: 12px;
    flex-wrap: wrap;
}

.fav-manager-search {
    flex: 1;
    min-width: 150px;
    padding: 8px 12px;
    border: 1px solid var(--border);
    border-radius: 6px;
    background: var(--bg);
    color: var(--text);
    font-size: 14px;
}

.fav-manager-search:focus {
    outline: none;
    border-color: var(--accent);
}

.fav-category-filter {
    padding: 8px 12px;
    border: 1px solid var(--border);
    border-radius: 6px;
    background: var(--bg);
    color: var(--text);
    font-size: 13px;
    min-width: 120px;
}

.fav-manager-stats {
    font-size: 12px;
    color: var(--text-muted);
    margin-bottom: 8px;
    padding: 0 4px;
}

.fav-manager-list {
    flex: 1;
    overflow-y: auto;
    min-height: 200px;
}

.fav-manager-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 10px;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 6px;
    margin-bottom: 6px;
}

.fav-manager-icon {
    width: 16px;
    height: 16px;
    border-radius: 3px;
    flex-shrink: 0;
}

.fav-manager-info {
    flex: 1;
    min-width: 0;
}

.fav-manager-title {
    font-weight: 500;
    font-size: 13px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.fav-manager-title-link {
    text-decoration: none;
    color: inherit;
    display: block;
}

.fav-manager-title-link:hover .fav-manager-title {
    color: var(--accent);
    text-decoration: underline;
}

.fav-manager-url {
    font-size: 11px;
    color: var(--text-muted);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    margin-top: 2px;
}

.fav-manager-category {
    font-size: 10px;
    background: var(--bg-hover);
    padding: 2px 6px;
    border-radius: 3px;
    color: var(--text-secondary);
    white-space: nowrap;
    flex-shrink: 0;
}

.fav-manager-actions {
    display: flex;
    gap: 4px;
    flex-shrink: 0;
}

.fav-manager-footer {
    margin-top: 12px;
}

.fav-pagination {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 12px;
}

.fav-page-info {
    font-size: 13px;
    color: var(--text-secondary);
    min-width: 60px;
    text-align: center;
}

/* 添加/编辑对话框 */
.fav-dialog-overlay {
    position: fixed;
    inset: 0;
    background: rgba(0,0,0,0.5);
    backdrop-filter: blur(4px);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 1100;
    padding: 20px;
}

.fav-dialog {
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: 12px;
    padding: 24px;
    width: 100%;
    max-width: 400px;
    box-shadow: var(--shadow-lg);
}

.fav-dialog h3 {
    font-size: 16px;
    margin-bottom: 16px;
}

.fav-form {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.fav-form input,
.fav-form select {
    padding: 10px 14px;
    border: 1px solid var(--border);
    border-radius: 6px;
    background: var(--bg);
    color: var(--text);
    font-size: 14px;
}

.fav-form input:focus,
.fav-form select:focus {
    outline: none;
    border-color: var(--accent);
}

.fav-checkbox-row {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 4px 0;
    cursor: pointer;
    font-size: 14px;
}

.fav-checkbox-row input[type="checkbox"] {
    width: 16px;
    height: 16px;
    cursor: pointer;
}

.fav-category-row {
    display: flex;
    gap: 8px;
}

.fav-category-row select {
    flex: 1;
    min-width: 0;
}

.fav-category-row input {
    flex: 1;
    min-width: 0;
}

.fav-dialog-actions {
    display: flex;
    justify-content: flex-end;
    gap: 8px;
    margin-top: 20px;
}

/* 移动端适配 */
@media (max-width: 768px) {
    .fav-dropdown {
        max-height: min(60vh, 400px);
        max-height: min(60dvh, 400px);
        position: absolute;
        left: 0;
        right: 0;
        top: calc(100% + 8px);
        bottom: auto;
        border-radius: 11px;
        box-shadow: var(--shadow-lg);
        animation: dropIn 0.15s ease;
    }

    @keyframes slideUpMobile {
        from { transform: translateY(100%); }
        to { transform: translateY(0); }
    }

    .fav-item {
        padding: 12px 14px;
        gap: 12px;
        -webkit-tap-highlight-color: transparent;
    }

    .fav-item:active {
        background: var(--bg-hover);
    }

    .fav-icon {
        width: 22px;
        height: 22px;
    }

    .fav-title {
        font-size: 15px;
    }

    .fav-meta {
        font-size: 12px;
    }

    /* 管理界面优化 */
    .fav-manager-header {
        flex-direction: column;
        gap: 8px;
    }

    .fav-manager-header .btn {
        align-self: flex-start;
    }

    .fav-manager-search {
        width: 100%;
        font-size: 16px; /* 防止 iOS 自动缩放 */
        padding: 12px 14px;
    }

    .fav-category-filter {
        width: 100%;
        font-size: 16px;
        padding: 10px 12px;
    }

    .fav-manager-list {
        max-height: 50vh;
    }

    .fav-manager-item {
        flex-wrap: wrap;
        padding: 10px 12px;
        gap: 8px;
    }

    .fav-manager-icon {
        display: none;
    }

    .fav-manager-info {
        flex: 1 1 100%;
        order: 1;
    }

    .fav-manager-title {
        font-size: 15px;
    }

    .fav-manager-category {
        order: 2;
    }

    .fav-manager-actions {
        order: 3;
        margin-left: auto;
    }

    .fav-manager-actions .btn {
        padding: 8px 14px;
        font-size: 13px;
    }

    .fav-pagination {
        padding: 8px 0;
    }

    .fav-pagination .btn {
        padding: 10px 16px;
    }

    /* 对话框优化。弹窗保持居中：贴底的底部抽屉会被手机浏览器的地址栏与
       Home 指示条盖住，与 .ui-dialog 是同一类缺陷。内边距改为四边吃安全区，
       底部再减去软键盘遮挡高度（--kb-inset 由 app.js 写入，无键盘时为 0）。 */
    .fav-dialog-overlay {
        padding: calc(12px + env(safe-area-inset-top)) calc(12px + env(safe-area-inset-right)) calc(12px + var(--kb-inset, 0px) + env(safe-area-inset-bottom)) calc(12px + env(safe-area-inset-left));
        align-items: center;
    }

    .fav-dialog {
        max-width: 100%;
        border-radius: 14px;
        padding: 16px 14px;
        max-height: calc(88dvh - var(--kb-inset, 0px));
        overflow-y: auto;
    }

    .fav-form input,
    .fav-form select {
        font-size: 16px;
        padding: 12px 14px;
    }

    .fav-category-row {
        flex-direction: column;
    }

    .fav-dialog-actions {
        gap: 10px;
    }

    .fav-dialog-actions .btn {
        flex: 1;
        padding: 12px;
        font-size: 15px;
    }

    .fav-actions {
        flex-direction: column;
    }

    .fav-actions .btn {
        width: 100%;
        padding: 12px;
        font-size: 14px;
    }
}

/* 小屏手机适配 */
@media (max-width: 480px) {
    .fav-dropdown {
        max-height: min(55vh, 360px);
        max-height: min(55dvh, 360px);
        left: 0;
        right: 0;
        border-radius: 11px;
    }

    .fav-item {
        padding: 10px 12px;
    }

    .fav-manager-list {
        max-height: 45vh;
    }

    .fav-manager-item {
        padding: 8px 10px;
    }

    .fav-manager-title {
        font-size: 14px;
    }

    .fav-manager-url {
        font-size: 10px;
    }
}

/* iPad / 平板适配 */
@media (min-width: 769px) and (max-width: 1024px) {
    .fav-dropdown {
        max-height: 450px;
    }

    .fav-manager-header {
        flex-wrap: nowrap;
    }

    .fav-manager-list {
        max-height: 450px;
    }

    .fav-manager-item {
        padding: 10px 14px;
    }

    .fav-dialog {
        max-width: 500px;
    }
}

/* 横屏模式优化 */
@media (max-height: 500px) and (orientation: landscape) {
    .fav-dropdown {
        max-height: min(55vh, 300px);
        max-height: min(55dvh, 300px);
        position: absolute;
        top: calc(100% + 8px);
        bottom: auto;
        left: 0;
        right: 0;
        width: auto;
        transform: none;
        border-radius: var(--radius);
    }

    .fav-dialog-overlay {
        align-items: center;
    }

    .fav-dialog {
        border-radius: 12px;
        max-height: 90vh;
        overflow-y: auto;
    }

    .help-content {
        max-height: 88dvh;
        overflow-y: auto;
    }

    /* 引擎下拉原本没有 max-height：12 个引擎时高 442px，横屏 844×390 下
       超出视口 199px 且无法滚动，后 6 个引擎完全点不到。 */
    .engine-dropdown {
        max-height: min(60dvh, 300px);
        overflow-y: auto;
        overscroll-behavior: contain;
    }

    /* 搜索栏保留 44px 触摸高度（横屏也需可点），只把行高收到按钮加内边距的下限。 */
    .search { min-height: 54px; }
}

/* 触摸优化 */
@media (hover: none) and (pointer: coarse) {
    .fav-item,
    .fav-manager-item,
    .btn {
        -webkit-tap-highlight-color: transparent;
    }

    .fav-item:active,
    .fav-manager-item:active {
        background: var(--bg-hover);
        transition: none;
    }

    /* 增大触摸区域 */
    .fav-manager-actions .btn {
        min-height: 36px;
        min-width: 60px;
    }

    .fav-pagination .btn {
        min-height: 40px;
        min-width: 80px;
    }
}

/* ========== 版本更新提示 ========== */

/* ========== WebDAV 远程备份 ========== */

.webdav-status {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 12px;
    font-size: 13px;
    color: var(--text-secondary);
}

.webdav-status-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--text-muted);
}

.webdav-status-dot.active {
    background: #22c55e;
}

.webdav-last-backup {
    margin-left: auto;
    font-size: 12px;
    color: var(--text-muted);
}

.webdav-form {
    display: flex;
    flex-direction: column;
    gap: 10px;
    margin-bottom: 12px;
}

.webdav-row {
    display: flex;
    gap: 8px;
}

.webdav-row input[type="url"],
.webdav-row input[type="text"],
.webdav-row input[type="password"] {
    flex: 1;
    padding: 10px 12px;
    border: 1px solid var(--border);
    border-radius: 6px;
    background: var(--bg);
    color: var(--text);
    font-size: 14px;
}

.webdav-row input:focus {
    outline: none;
    border-color: var(--accent);
}

.webdav-row label {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 14px;
    color: var(--text);
    cursor: pointer;
}

.webdav-row input[type="checkbox"] {
    width: 16px;
    height: 16px;
    cursor: pointer;
}

.webdav-row-half {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 8px;
}

.webdav-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}

.webdav-message {
    margin-top: 10px;
    padding: 8px 12px;
    border-radius: 6px;
    font-size: 13px;
    min-height: 20px;
}

.webdav-message:empty {
    display: none;
}

.webdav-message.success {
    background: #dcfce7;
    color: #166534;
}

.webdav-message.error {
    background: #fee2e2;
    color: #991b1b;
}

@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .webdav-message.success {
        background: #14532d;
        color: #86efac;
    }
    :root:not([data-theme="light"]) .webdav-message.error {
        background: #450a0a;
        color: #fca5a5;
    }
}

:root[data-theme="dark"] .webdav-message.success {
    background: #14532d;
    color: #86efac;
}

:root[data-theme="dark"] .webdav-message.error {
    background: #450a0a;
    color: #fca5a5;
}

.webdav-loading {
    padding: 16px;
    text-align: center;
    color: var(--text-muted);
    font-size: 13px;
}

/* WebDAV 恢复对话框 */
.webdav-restore-dialog {
    max-width: 500px;
}

.webdav-backup-list {
    max-height: 300px;
    overflow-y: auto;
    margin-bottom: 16px;
}

.webdav-backup-item {
    padding: 12px;
    border: 1px solid var(--border);
    border-radius: 6px;
    margin-bottom: 8px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    transition: all var(--transition);
}

.webdav-backup-item:hover {
    border-color: var(--accent);
    background: var(--bg-hover);
}

.webdav-backup-item:last-child {
    margin-bottom: 0;
}

.webdav-backup-info {
    flex: 1;
    min-width: 0;
}

.webdav-backup-actions {
    display: flex;
    gap: 8px;
    flex-shrink: 0;
}

.webdav-backup-actions .btn-sm {
    padding: 4px 10px;
    font-size: 12px;
}

.webdav-backup-name {
    font-size: 13px;
    font-weight: 500;
    color: var(--text);
    word-break: break-all;
}

.webdav-backup-meta {
    font-size: 11px;
    color: var(--text-muted);
    margin-top: 4px;
}

/* 移动端适配 */
@media (max-width: 768px) {
    .webdav-row-half {
        grid-template-columns: 1fr;
    }

    .webdav-actions {
        flex-direction: column;
    }

    .webdav-actions .btn {
        width: 100%;
    }

    .webdav-status {
        flex-wrap: wrap;
    }

    .webdav-last-backup {
        flex-basis: 100%;
        margin-left: 16px;
        margin-top: 4px;
    }

    .webdav-restore-dialog {
        max-width: 100%;
    }

    .webdav-backup-list {
        max-height: 50vh;
    }
}

/* ========== 版本更新提示 (continued) ========== */

/* 帮助按钮更新徽章：「说明」有新版本时的提示点。
   原先挂在右下角圆形按钮的右上角（绝对定位），底栏改成纯文字后那个角落
   不再存在，改为文本行内的小红点：它跟着字号与行盒走，窄屏把链接撑到
   44px 高也不会跑偏。 */
.site-footer-link.has-update::after {
    content: '';
    display: inline-block;
    width: 6px;
    height: 6px;
    margin-left: 4px;
    vertical-align: 2px;
    background: #ef4444;
    border-radius: 50%;
    animation: badgePulse 2s ease-in-out infinite;
}

@keyframes badgePulse {
    0%, 100% { transform: scale(1); opacity: 1; }
    50% { transform: scale(1.2); opacity: 0.8; }
}

/* 新功能区块 */
.help-new-features {
    background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%);
    border: 1px solid #f59e0b;
    border-radius: 8px;
    padding: 12px 14px;
    margin-bottom: 16px;
}

@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .help-new-features {
        background: linear-gradient(135deg, #451a03 0%, #78350f 100%);
        border-color: #b45309;
    }
}

:root[data-theme="dark"] .help-new-features {
    background: linear-gradient(135deg, #451a03 0%, #78350f 100%);
    border-color: #b45309;
}

.help-new-features-header {
    font-size: 14px;
    font-weight: 600;
    color: #92400e;
    margin-bottom: 8px;
}

@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .help-new-features-header {
        color: #fbbf24;
    }
}

:root[data-theme="dark"] .help-new-features-header {
    color: #fbbf24;
}

.help-new-features-summary {
    margin: 0;
    font-size: 13px;
    color: #78350f;
    line-height: 1.6;
}

@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .help-new-features-summary {
        color: #fde68a;
    }
}

:root[data-theme="dark"] .help-new-features-summary {
    color: #fde68a;
}

/* ========== 可折叠区块 ========== */

.section-collapsible {
    margin-bottom: 32px;
}

.section-collapsible:last-child {
    margin-bottom: 0;
}

.section-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 10px 0;
    cursor: pointer;
    user-select: none;
    border-bottom: 1px solid var(--border);
    margin-bottom: 0;
}

.section-header:hover {
    color: var(--accent);
}

.section-header .section-title {
    margin-bottom: 0;
}

.section-toggle-icon {
    width: 16px;
    height: 16px;
    fill: none;
    stroke: currentColor;
    stroke-width: 2;
    transition: transform var(--transition);
}

.section-header.expanded .section-toggle-icon {
    transform: rotate(90deg);
}

.section-body {
    padding-top: 12px;
}

.section-body.collapsed {
    display: none;
}

/* ========== 恢复选项 ========== */

.restore-options {
    display: flex;
    flex-direction: column;
    gap: 12px;
    margin-bottom: 16px;
}

.restore-option {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 12px;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 8px;
    cursor: pointer;
    transition: all var(--transition);
}

.restore-option:hover {
    border-color: var(--accent);
    background: var(--bg-hover);
}

.restore-option input[type="radio"] {
    margin-top: 2px;
    flex-shrink: 0;
}

.restore-option span {
    font-size: 14px;
    color: var(--text);
    line-height: 1.4;
}

/* ========== 批量操作栏 ========== */

.fav-batch-bar {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 12px;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 8px;
    margin-bottom: 12px;
}

.fav-select-all {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 13px;
    color: var(--text-secondary);
    cursor: pointer;
}

.fav-select-all input {
    cursor: pointer;
}

.fav-selected-count {
    font-size: 13px;
    color: var(--accent);
    font-weight: 500;
}

/* ========== 分类拖放区 ========== */

.fav-category-zones {
    display: none; /* 已被树状分类替代 */
}

/* ========== 分割布局 ========== */

.fav-manager-split {
    display: flex;
    gap: 16px;
    height: 100%;
    min-height: 400px;
}

.fav-manager-sidebar {
    width: 220px;
    flex-shrink: 0;
    display: flex;
    flex-direction: column;
    border-right: 1px solid var(--border);
    padding-right: 12px;
    overflow: hidden;
}

.sidebar-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 8px 4px;
    margin-bottom: 8px;
    border-bottom: 1px solid var(--border);
}

.sidebar-title {
    font-weight: 600;
    font-size: 13px;
    color: var(--text);
}

.fav-manager-main {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
}

/* ========== 树状分类 ========== */

.category-tree {
    flex: 1;
    overflow-y: auto;
    padding: 4px 0;
}

.category-tree-item {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 6px 12px;
    border-radius: 6px;
    cursor: pointer;
    font-size: 13px;
    color: var(--text-secondary);
    transition: all var(--transition);
    user-select: none;
}

.category-tree-item:hover {
    background: var(--bg-hover);
    color: var(--text);
}

.category-tree-item.active {
    background: var(--accent);
    color: white;
}

.category-tree-item.drag-over {
    background: var(--accent);
    color: white;
    box-shadow: 0 0 0 2px var(--accent-hover);
}

.tree-toggle {
    width: 12px;
    font-size: 10px;
    color: var(--text-muted);
    cursor: pointer;
    flex-shrink: 0;
    transition: transform var(--transition);
}

.tree-toggle-placeholder {
    width: 12px;
    flex-shrink: 0;
}

.tree-item-icon {
    font-size: 14px;
    flex-shrink: 0;
}

.tree-item-name {
    flex: 1;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.tree-item-count {
    font-size: 11px;
    color: var(--text-muted);
    background: var(--bg);
    padding: 1px 6px;
    border-radius: 10px;
    flex-shrink: 0;
}

.category-tree-item.active .tree-item-count {
    background: rgba(255,255,255,0.2);
    color: white;
}

.category-tree-children {
    overflow: hidden;
    transition: max-height 0.2s ease;
}

.category-tree-children.collapsed {
    display: none;
}

.category-tree-new {
    border: 1px dashed var(--border);
    margin-top: 8px;
    color: var(--text-muted);
}

.category-tree-new:hover {
    border-color: var(--accent);
    color: var(--accent);
    background: transparent;
}

/* 分类编辑按钮 */
.tree-item-edit {
    display: none;
    background: none;
    border: none;
    color: var(--text-muted);
    cursor: pointer;
    padding: 2px 4px;
    font-size: 12px;
    flex-shrink: 0;
    margin-left: 4px;
}

.category-tree-item:hover .tree-item-edit {
    display: inline-block;
}

.tree-item-edit:hover {
    color: var(--accent);
}

.category-tree-item.active .tree-item-edit {
    color: rgba(255,255,255,0.7);
}

.category-tree-item.active .tree-item-edit:hover {
    color: white;
}

/* 分类编辑输入框 */
.tree-item-edit-input {
    flex: 1;
    min-width: 60px;
    padding: 2px 6px;
    border: 1px solid var(--accent);
    border-radius: 4px;
    background: var(--bg);
    color: var(--text);
    font-size: 13px;
    outline: none;
}

.tree-item-name.editing {
    display: none;
}

/* ========== 书签拖拽 ========== */

.fav-drag-handle {
    cursor: grab;
    color: var(--text-muted);
    font-size: 12px;
    padding: 4px;
    flex-shrink: 0;
}

.fav-drag-handle:active {
    cursor: grabbing;
}

.fav-manager-item.dragging {
    opacity: 0.5;
    background: var(--bg-hover);
}

.fav-manager-item[draggable="true"] {
    cursor: default;
}

.fav-checkbox {
    flex-shrink: 0;
    cursor: pointer;
    width: 16px;
    height: 16px;
}

/* 移动端适配 */
@media (max-width: 768px) {
    .fav-batch-bar {
        flex-wrap: wrap;
        gap: 8px;
    }

    .fav-batch-bar .btn {
        flex: 1;
        min-width: 100px;
    }

    /* 树状分类移动端适配 */
    .fav-manager-split {
        flex-direction: column;
    }

    .fav-manager-sidebar {
        width: 100%;
        border-right: none;
        border-bottom: 1px solid var(--border);
        padding-right: 0;
        padding-bottom: 12px;
        max-height: 180px;
    }

    .category-tree {
        display: flex;
        flex-wrap: wrap;
        gap: 6px;
        padding: 0;
    }

    .category-tree-item {
        padding: 6px 10px;
        font-size: 12px;
        border: 1px solid var(--border);
        border-radius: 16px;
    }

    .category-tree-children {
        display: none;
    }

    .tree-toggle,
    .tree-toggle-placeholder {
        display: none;
    }

    .restore-options {
        gap: 10px;
    }

    .restore-option {
        padding: 10px;
    }

    .restore-option span {
        font-size: 13px;
    }

    .fav-drag-handle {
        display: none;
    }
}

/* 帮助弹窗标题优化 */
.help-content h3 {
    font-size: 16px;
    margin-bottom: 16px;
    color: var(--text);
    display: flex;
    align-items: center;
    gap: 8px;
}

/* 移动端适配 */
@media (max-width: 768px) {
    .help-new-features {
        padding: 10px 12px;
        margin-bottom: 14px;
    }

    .help-new-features-header {
        font-size: 13px;
    }

    .help-new-features-summary {
        font-size: 12px;
    }
}

/* 2026 front-end material and interaction layer */
:root {
    color-scheme: light;
    --bg: #efede8;
    --bg-card: #faf8f4;
    --bg-hover: #f2ece5;
    --text: #302b27;
    --text-secondary: #655e57;
    --text-muted: #817970;
    --border: rgba(92,76,64,.18);
    --divider-rgb: 92,76,64;
    --bookmark-face: linear-gradient(180deg,rgba(255,255,255,.48),rgba(255,255,255,.08) 38%,rgba(121,105,91,.025)),linear-gradient(165deg,rgba(251,249,245,.86),rgba(238,235,230,.90));
    --bookmark-hover-face: linear-gradient(180deg,rgba(255,255,255,.62),rgba(255,255,255,.12) 40%,rgba(121,105,91,.035)),linear-gradient(165deg,rgba(253,251,247,.92),rgba(237,233,227,.94));
    --bookmark-pressed-face: linear-gradient(180deg,rgba(101,81,64,.17),rgba(255,255,255,.02) 54%,rgba(255,255,255,.11)),linear-gradient(165deg,#e8e3dc,#f5f1eb);
    --bookmark-border: rgba(116,100,85,.20);
    --bookmark-hover-border: rgba(148,94,74,.37);
    --bookmark-pressed-border: rgba(103,85,69,.34);
    /* 常态/悬停都带真实外投影——与深色同构。此前浅色只有内阴影，
       书签「浮起来」时身后没有影子跟着，平面感出不来；
       深色因为常态就带 --shadow、悬停带 --shadow-lg，手感才正确。 */
    --bookmark-idle-shadow: inset 0 1px rgba(255,255,255,.74),inset 0 -1px rgba(120,102,86,.11),inset 1px 0 rgba(255,255,255,.12),var(--shadow);
    --bookmark-hover-shadow: inset 0 1px rgba(255,255,255,.87),inset 0 -1px rgba(120,102,86,.16),inset 1px 0 rgba(255,255,255,.18),var(--shadow-lg);
    --bookmark-press-shadow: inset 0 3px 6px rgba(91,72,56,.26),inset 0 0 0 1px rgba(121,98,77,.09),inset 0 -1px rgba(255,255,255,.48);
    --bookmark-glint: var(--glint);
    /* 悬停抬升与深色一致（-2px）：两套主题共用同一套「抬起 + 影子跟随」
       机制。此前浅色 -1px、深色 -2px，同样是机制不一致的一例。 */
    --bookmark-lift: -2px;
    --bookmark-press-depth: 1px;
    --bookmark-press-scale: .975;
    --bookmark-glow-opacity: .32;
    --bookmark-glow-motion: 85ms;
    --bookmark-motion: 90ms;
    --bookmark-press-motion: 35ms;
    --accent: #945e4a;
    --accent-hover: #79503f;
    --focus: rgba(148,94,74,.58);
    --glass: rgba(252,250,246,.66);
    --glass-strong: rgba(252,250,246,.91);
    --card-bottom: #f0ede7;
    --control-top: #fcfaf6;
    --control-bottom: #eee9e1;
    --control-hover-top: #fffdf9;
    --control-hover-bottom: #f4eee6;
    --control-pressed-top: #e9e2d9;
    --control-pressed-bottom: #f1ece5;
    --glint: rgba(179,111,73,.34);
    --search-glint: rgba(179,111,73,.20);
    /* 悬浮光感的高光层：与仿真一致，书签与搜索框的 ::before/::after 共用。
       缺这一层时两层光晕叠在一起显得灰、糊，正是「说不上哪里不精致」的来源。 */
    --glint-white: rgba(255,255,255,.90);
    /* 浅色的投影比旧值（.06/.1）强、但比深色（.3/.5）弱。
       旧值在米色底上几乎不可见，导致浅色下按钮「抬起来却看不到影子」——
       深色之所以手感正确，正是因为它有真实外投影在跟。
       这组值取中间强度：米色底比深色亮，需要更强的黑才读得出，
       但不宜到深色那档，否则浅色整体会显得发脏。 */
    --shadow: 0 1px 3px rgba(60,45,32,.11),0 4px 10px rgba(60,45,32,.07);
    --shadow-lg: 0 4px 10px rgba(60,45,32,.10),0 14px 30px rgba(60,45,32,.15);
    --press-shadow: inset 0 2px 5px rgba(91,70,55,.20),inset 0 -1px rgba(255,255,255,.55);
    --radius: 10px;
    /* 搜索栏三按钮的色相锚点：同族拟物，仅色相微偏以区分功能。
       深浅两套必须成对改，见下方两个深色块。 */
    --mode-hue: 106,124,132;      /* 网页/书签：青灰 */
    --engine-hue: 168,128,78;     /* 引擎：琥珀 */
    --submit-hue: 148,94,74;      /* 搜索：陶土 */
    --ctl-h: 44px;                /* 触摸目标下限，不可下调 */
    --ctl-press-shadow: inset 0 4px 9px rgba(88,66,50,.32),inset 0 0 0 1px rgba(121,98,77,.12),inset 0 -1px rgba(255,255,255,.42);
    --ctl-press-motion: 28ms;
}
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
        color-scheme: dark;
        --bg: #211f1c;
        --bg-card: #3a3530;
        --bg-hover: #4a4139;
        --text: #f3eee8;
        --text-secondary: #c9beb3;
        --text-muted: #a89c90;
        --border: rgba(222,203,185,.18);
        --divider-rgb: 222,203,185;
        --bookmark-face: linear-gradient(165deg,var(--bg-card),var(--card-bottom));
        --bookmark-hover-face: linear-gradient(165deg,var(--bg-card),var(--card-bottom));
        --bookmark-pressed-face: linear-gradient(180deg,var(--control-pressed-top),var(--control-pressed-bottom));
        --bookmark-border: var(--border);
        --bookmark-hover-border: var(--focus);
        --bookmark-pressed-border: rgba(222,203,185,.30);
        --bookmark-idle-shadow: inset 0 1px rgba(255,255,255,.75),inset 0 -1px rgba(108,87,69,.08),var(--shadow);
        --bookmark-hover-shadow: inset 0 1px rgba(255,255,255,.88),var(--shadow-lg);
        --bookmark-press-shadow: inset 0 4px 8px rgba(0,0,0,.48),inset 0 0 0 1px rgba(0,0,0,.12),inset 0 -1px rgba(255,255,255,.08);
        --bookmark-glint: var(--glint);
        --bookmark-lift: -2px;
        --bookmark-press-depth: 1px;
        --bookmark-press-scale: .975;
        --bookmark-glow-opacity: .8;
        --bookmark-glow-motion: 160ms;
        --bookmark-motion: 160ms;
        --bookmark-press-motion: 55ms;
        --accent: #c28d70;
        --accent-hover: #d4a181;
        --focus: rgba(223,169,140,.68);
        --glass: rgba(65,58,51,.70);
        --glass-strong: rgba(57,51,46,.93);
        --card-bottom: #2c2925;
        --control-top: #625951;
        --control-bottom: #3c3630;
        --control-hover-top: #71655b;
        --control-hover-bottom: #4a4139;
        --control-pressed-top: #292521;
        --control-pressed-bottom: #39312b;
        --glint: rgba(243,180,135,.28);
        --search-glint: rgba(243,180,135,.18);
        --glint-white: rgba(255,244,226,.18);
        --shadow: 0 2px 4px rgba(0,0,0,.12),0 10px 28px rgba(0,0,0,.20);
        --shadow-lg: 0 5px 10px rgba(0,0,0,.18),0 18px 34px rgba(0,0,0,.35);
        --press-shadow: inset 0 3px 7px rgba(0,0,0,.45),inset 0 -1px rgba(255,255,255,.08);
        /* 与 :root 的三色锚点成对定义，缺一份则手动深色与系统深色表现分叉 */
        --mode-hue: 138,160,168;
        --engine-hue: 208,162,96;
        --submit-hue: 223,169,140;
        --ctl-h: 44px;
        --ctl-press-shadow: inset 0 5px 11px rgba(0,0,0,.55),inset 0 0 0 1px rgba(0,0,0,.16),inset 0 -1px rgba(255,255,255,.07);
        --ctl-press-motion: 28ms;
    }
}
:root[data-theme="dark"] {
    color-scheme: dark;
    --bg: #211f1c;
    --bg-card: #3a3530;
    --bg-hover: #4a4139;
    --text: #f3eee8;
    --text-secondary: #c9beb3;
    --text-muted: #a89c90;
    --border: rgba(222,203,185,.18);
    --divider-rgb: 222,203,185;
    --bookmark-face: linear-gradient(165deg,var(--bg-card),var(--card-bottom));
    --bookmark-hover-face: linear-gradient(165deg,var(--bg-card),var(--card-bottom));
    --bookmark-pressed-face: linear-gradient(180deg,var(--control-pressed-top),var(--control-pressed-bottom));
    --bookmark-border: var(--border);
    --bookmark-hover-border: var(--focus);
    --bookmark-pressed-border: rgba(222,203,185,.30);
    --bookmark-idle-shadow: inset 0 1px rgba(255,255,255,.75),inset 0 -1px rgba(108,87,69,.08),var(--shadow);
    --bookmark-hover-shadow: inset 0 1px rgba(255,255,255,.88),var(--shadow-lg);
    --bookmark-press-shadow: inset 0 4px 8px rgba(0,0,0,.48),inset 0 0 0 1px rgba(0,0,0,.12),inset 0 -1px rgba(255,255,255,.08);
    --bookmark-glint: var(--glint);
    --bookmark-lift: -2px;
    --bookmark-press-depth: 1px;
    --bookmark-press-scale: .975;
    --bookmark-glow-opacity: .8;
    --bookmark-glow-motion: 160ms;
    --bookmark-motion: 160ms;
    --bookmark-press-motion: 55ms;
    --accent: #c28d70;
    --accent-hover: #d4a181;
    --focus: rgba(223,169,140,.68);
    --glass: rgba(65,58,51,.70);
    --glass-strong: rgba(57,51,46,.93);
    --card-bottom: #2c2925;
    --control-top: #625951;
    --control-bottom: #3c3630;
    --control-hover-top: #71655b;
    --control-hover-bottom: #4a4139;
    --control-pressed-top: #292521;
    --control-pressed-bottom: #39312b;
    --glint: rgba(243,180,135,.28);
    --search-glint: rgba(243,180,135,.18);
    --glint-white: rgba(255,244,226,.18);
    --shadow: 0 2px 4px rgba(0,0,0,.12),0 10px 28px rgba(0,0,0,.20);
    --shadow-lg: 0 5px 10px rgba(0,0,0,.18),0 18px 34px rgba(0,0,0,.35);
    --press-shadow: inset 0 3px 7px rgba(0,0,0,.45),inset 0 -1px rgba(255,255,255,.08);
    /* 与上面 prefers-color-scheme 块逐字一致，两处必须同步 */
    --mode-hue: 138,160,168;
    --engine-hue: 208,162,96;
    --submit-hue: 223,169,140;
    --ctl-h: 44px;
    --ctl-press-shadow: inset 0 5px 11px rgba(0,0,0,.55),inset 0 0 0 1px rgba(0,0,0,.16),inset 0 -1px rgba(255,255,255,.07);
    --ctl-press-motion: 28ms;
}
body { font-family: -apple-system,BlinkMacSystemFont,"SF Pro Display","PingFang SC",sans-serif; }
/* 1040 → 1440：给背板外侧的两侧模块留位置（936 背板 + 2×220 模块 + 间隙）。
   背板本身仍是 936px 居中，宽度未变。 */
.app { max-width: 1440px; padding: 35px 22px 56px; }
.header { margin-bottom: 0; }
.search-wrapper { max-width: 560px; }
.search {
    --glow-x: 50%; --glow-y: 0%; position: relative; min-height: 56px;
    padding: 5px; border-color: rgba(132,109,92,.24); border-radius: 13px;
    background: linear-gradient(165deg,var(--glass-strong),var(--glass));
    box-shadow: inset 0 1px rgba(255,255,255,.76),inset 0 -1px rgba(105,81,63,.08),0 3px 8px rgba(67,51,39,.045),0 12px 27px rgba(67,51,39,.065);
    backdrop-filter: blur(18px) saturate(125%); -webkit-backdrop-filter: blur(18px) saturate(125%);

    transition: min-height var(--transition), padding var(--transition), border-radius var(--transition), border-color 160ms, box-shadow 160ms, background 160ms;
}
/* 顶边 1px 受光高光。::after 已被光感占用，所以这条用 ::before。
   仿真 .search-shell::before 同样存在，此前移植时整块漏掉——
   背板加了 padding-top 后，搜索框顶与背板顶拉开 20px，
   框自身若没有这条高光，会在这道缝里显得缺一道亮边。 */
.search::before { content: ''; position: absolute; left: 11px; right: 11px; top: 0; height: 1px; pointer-events: none; background: linear-gradient(90deg,transparent,rgba(255,255,255,.95) 30%,rgba(255,255,255,.95) 70%,transparent); }
/* 光感与 .bookmark::before 同构：中性白高光 + 色相光晕。
   单层时两层光晕叠在一起偏灰；仿真就是双层，这里对齐。 */
.search::after { content: ''; position: absolute; inset: 0; border-radius: inherit; pointer-events: none; background: radial-gradient(ellipse 77px 51px at var(--glow-x) var(--glow-y),var(--glint-white),transparent 68%),radial-gradient(ellipse 105px 52px at var(--glow-x) var(--glow-y),var(--search-glint),transparent 82%); opacity: 0; transition: opacity 85ms; }
.search:hover { border-color: rgba(148,94,74,.33); box-shadow: inset 0 1px rgba(255,255,255,.82),0 4px 10px rgba(67,51,39,.055),0 13px 27px rgba(67,51,39,.07); }
.search:hover::after { opacity: .40; }
.search:focus-within { border-color: var(--focus); box-shadow: inset 0 2px 5px rgba(91,70,55,.10),inset 0 0 0 1px rgba(255,255,255,.52),0 4px 12px rgba(67,51,39,.07),0 0 0 3px rgba(148,94,74,.065); }
:root[data-theme="dark"] .search:focus-within { background: rgba(44,39,35,.83); box-shadow: inset 0 3px 8px rgba(0,0,0,.34),inset 0 0 0 1px rgba(255,255,255,.06),0 0 0 3px rgba(223,169,140,.08); }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .search:focus-within { background: rgba(44,39,35,.83); box-shadow: inset 0 3px 8px rgba(0,0,0,.34),inset 0 0 0 1px rgba(255,255,255,.06),0 0 0 3px rgba(223,169,140,.08); } }
/* 搜索栏三按钮：同族拟物 + 三色相偏移。
   各自的 --HUE 是唯一的色相来源，光感与按压阴影都从它派生，
   三个按钮因此共享同一套结构、只在色相与明度上分层。 */
.search-mode,.search-engine,.search-btn {
    --glow-x: 50%; --glow-y: 0%; position: relative; z-index: 1; flex: 0 0 auto;
    height: var(--ctl-h); min-height: var(--ctl-h); padding: 0 10px; overflow: hidden;
    border: 1px solid rgba(var(--HUE),.26); border-radius: 9px; color: var(--text);
    font-size: 12px; font-weight: 600; cursor: pointer;
    background: linear-gradient(172deg,rgba(var(--HUE),.085),rgba(var(--HUE),.032) 62%,rgba(var(--HUE),.055));
    box-shadow: inset 0 1px rgba(255,255,255,.80),inset 0 -1px rgba(105,81,63,.09),var(--shadow);
    transition: transform 110ms,border-color 140ms,box-shadow 140ms,background 140ms,color 140ms;
}
/* 光感与 .bookmark::before 同构：中性白高光 + 各自色相光晕 */
.search-mode::before,.search-engine::before,.search-btn::before { content: ''; position: absolute; inset: 0; border-radius: inherit; pointer-events: none; background: radial-gradient(ellipse 62px 40px at var(--glow-x) var(--glow-y),rgba(255,255,255,.86),transparent 66%),radial-gradient(ellipse 66px 43px at var(--glow-x) var(--glow-y),rgba(var(--HUE),.40),transparent 82%); opacity: 0; transition: opacity 85ms; }
.search-mode:hover::before,.search-engine:hover::before,.search-btn:hover::before { opacity: .52; }
.search-mode:hover,.search-engine:hover,.search-btn:hover { transform: translateY(-1px); border-color: rgba(var(--HUE),.46); background: linear-gradient(172deg,rgba(var(--HUE),.14),rgba(var(--HUE),.055) 62%,rgba(var(--HUE),.09)); box-shadow: inset 0 1px rgba(255,255,255,.90),inset 0 -1px rgba(105,81,63,.11),var(--shadow-lg); }
/* 按下：做出「按键行程」而不只是内阴影。
   之前只 translateY(1px)，按钮几乎没离开原平面，看着像贴纸在变暗；
   2px 位移 + .985 缩放才有琴键被按下去的实感。
   缩放幅度刻意压在 .985：44px 按钮缩后仍有 43.3px，可点区域不被牺牲。
   过渡 28ms 比 hover 的 110ms 短，制造干脆的「按下去」而非「淡入」。
   常态与悬停那层外投影在这里被 --ctl-press-shadow 整条替换：
   按钮按下时影子必须收掉，否则抬升与下压两股力会互相抵消，
   「按下去」的读数被悬停的浮起感污染。 */
.search-mode:active,.search-engine:active,.search-btn:active { transform: translateY(2px) scale(.985); border-color: rgba(var(--HUE),.38); background: linear-gradient(180deg,rgba(var(--HUE),.18),rgba(var(--HUE),.07)); box-shadow: var(--ctl-press-shadow); transition-duration: var(--ctl-press-motion); }
.search-mode:active::before,.search-engine:active::before,.search-btn:active::before { opacity: 0; transition-duration: 20ms; }
.search-mode { --HUE: var(--mode-hue); min-width: 48px; color: var(--text-secondary); }
/* 选中态做成「点亮」而非「凹陷」：inset 阴影留给 :active，
   两种语义共用 inset 会让用户分不清「已切换」和「正在按」 */
.search-mode[aria-pressed="true"] { border-color: rgba(var(--mode-hue),.60); background: linear-gradient(172deg,rgba(var(--mode-hue),.30),rgba(var(--mode-hue),.17) 60%,rgba(var(--mode-hue),.24)); color: rgb(var(--mode-hue)); box-shadow: inset 0 1px rgba(255,255,255,.62),0 2px 5px rgba(67,51,39,.10); }
.search-engine { --HUE: var(--engine-hue); display: flex; align-items: center; justify-content: center; gap: 6px; min-width: 68px; }
/* 下拉指示箭头：展开时翻转 180°。
   JS 在 app.js:314 把 `.active` 与 aria-expanded 一起设，这里统一只认
   aria-expanded 一处，避免同一状态有两条规则各自驱动。 */
.engine-arrow { width: 11px; height: 11px; flex: 0 0 auto; fill: none; stroke: currentColor; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; opacity: .55; transition: transform 160ms, opacity 140ms; }
.search-engine:hover .engine-arrow { opacity: .85; }
.search-engine[aria-expanded="true"] .engine-arrow { transform: rotate(180deg); opacity: .85; }
.search-engine[aria-expanded="true"] { border-color: rgba(var(--engine-hue),.52); color: rgb(var(--engine-hue)); box-shadow: inset 0 2px 5px rgba(91,70,55,.14),inset 0 0 0 1px rgba(255,255,255,.30); }
/* 搜索是主操作：实心陶土，凹陷靠加深填充而非 inset 才读得出 */
.search-btn {
    --HUE: var(--submit-hue); width: auto; min-width: 49px; padding: 0 11px; font-weight: 700; color: #fffaf5;
    border-color: rgba(var(--submit-hue),.55);
    background: linear-gradient(172deg,#85604e,#71513f 58%,#66483a);
    box-shadow: inset 0 1px rgba(255,255,255,.24),inset 0 -1px rgba(0,0,0,.14),0 2px 5px rgba(67,51,39,.17),0 5px 12px rgba(67,51,39,.10);
}
.search-btn::before { background: radial-gradient(ellipse 62px 40px at var(--glow-x) var(--glow-y),rgba(255,255,255,.42),transparent 66%),radial-gradient(ellipse 66px 43px at var(--glow-x) var(--glow-y),rgba(255,214,180,.30),transparent 82%); }
.search-btn:hover { border-color: rgba(214,161,129,.70); background: linear-gradient(172deg,#96705c,#825d47 58%,#70503f); box-shadow: inset 0 1px rgba(255,255,255,.30),inset 0 -1px rgba(0,0,0,.14),0 6px 14px rgba(67,51,39,.22); }
/* 按下态必须显式给 border-color：否则指针仍停在按钮上时 :hover 的浅色描边
   会保留下来，深色实心底配一条亮边，凹陷读不出来 */
.search-btn:active { border-color: rgba(var(--submit-hue),.62); background: linear-gradient(180deg,#5c4034,#4e352b); color: rgba(255,250,245,.92); box-shadow: inset 0 5px 11px rgba(46,28,20,.52),inset 0 0 0 1px rgba(40,24,16,.28),inset 0 -1px rgba(255,255,255,.16); }
:root[data-theme="dark"] .search-btn { background: linear-gradient(172deg,#e0b293,#d4a181 58%,#c08e70); border-color: rgba(223,169,140,.42); color: #271e18; }
:root[data-theme="dark"] .search-btn:hover { background: linear-gradient(172deg,#eec3a6,#e0b293 58%,#d4a181); }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .search-btn { background: linear-gradient(172deg,#e0b293,#d4a181 58%,#c08e70); border-color: rgba(223,169,140,.42); color: #271e18; } }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .search-btn:hover { background: linear-gradient(172deg,#eec3a6,#e0b293 58%,#d4a181); } }
/* font-size 不能低于 16px：iOS Safari 焦点时小于该值会自动缩放整页。
   583/603 行的移动端 16px 规则被这里覆盖过，改成 15px 会让点击输入框时页面跳动。 */
.search-input { position: relative; z-index: 1; min-height: 44px; padding: 0 10px; font-size: 16px; font-family: inherit; line-height: 44px; border: 0; background: transparent; color: var(--text); resize: none; overflow-y: auto; }
.search-input:focus-visible { outline: none; }
.search.fav-search-mode { border-color: var(--focus); }
/* 书签态下按钮文字换成「打开」但材质不变：这里原本会把实心陶土压回
   浅色渐变 + 陶土文字，那是旧按钮的配色，套在实心底上会看不清。
   主操作在两种模式下都应是同一个视觉重量。 */
/* 引擎下拉与书签下拉在仿真里本就是两套材质（引擎 10px 圆角 + 轻阴影，
   书签是 13px + --shadow-hover），不再共用一条规则。
   原先合写时改一处会连带改动另一处。 */
.fav-dropdown { background: var(--glass-strong); border-color: var(--border); border-radius: 13px; box-shadow: 0 4px 8px rgba(67,51,39,.055),0 16px 30px rgba(67,51,39,.12); backdrop-filter: blur(25px) saturate(150%); -webkit-backdrop-filter: blur(25px) saturate(150%); }
.engine-dropdown { background: var(--glass-strong); border-color: var(--border); border-radius: 10px; box-shadow: 0 12px 28px rgba(67,51,39,.13),inset 0 1px rgba(255,255,255,.66); backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px); }
.engine-option { width: 100%; border: 0; background: transparent; text-align: left; font-family: inherit; padding: 6px 10px; min-height: 31px; font-size: 12px; }
.engine-option::before { display: none; }
/* 选中项用主色文字 + 加粗表示，不铺底色：底色留给 :hover，
   两者若都用底色就分不清「当前引擎」和「鼠标停在这项」。
   background 必须显式写 transparent——旧样式 203 行的
   `.engine-option.active { background: var(--bg-hover) }` 同特异性，
   不覆盖就会留一层米色底，把「加粗变色」这个信号淹掉。 */
.engine-option.active { background: transparent; color: var(--accent); font-weight: 700; }
/* 选项也有按下反馈（仿真 .engine-choice:active） */
.engine-option:active { box-shadow: var(--press-shadow); }
.engine-option:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }
.section-header { width: 100%; border: 0; background: transparent; color: var(--text); text-align: left; font: inherit; }
.section-toggle-label { color: var(--text-muted); font-size: 12px; }
.section-header:hover .section-toggle-label { color: var(--accent); }
.section-header:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.help-close { width: auto; min-width: 42px; padding: 0 8px; border: 1px solid var(--border); border-radius: 7px; background: var(--glass); color: var(--text-secondary); font-size: 12px; }
.help-close:hover { border-color: var(--focus); background: var(--bg-hover); color: var(--text); }
.help-close:active { background: var(--control-pressed-top); box-shadow: var(--press-shadow); }
.help-close:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
/* 引擎选项的选中态不再铺底色（见上方 .engine-option.active），
   所以这里只保留 hover 与书签项的两态。 */
.engine-option:hover,.fav-item:hover,.fav-item.selected { background: rgba(148,94,74,.11); }
.fav-item { border-bottom-color: var(--border); }
.fav-private-label { padding: 1px 4px; border: 1px solid var(--border); border-radius: 4px; color: var(--accent); font-size: 10px; }
.grid { gap: 26px; }
.category { animation-duration: 180ms; }
.category-header { margin-bottom: 10px; padding-bottom: 7px; border-bottom: 0; background: linear-gradient(90deg,transparent 0%,rgba(var(--divider-rgb),.08) 18%,rgba(var(--divider-rgb),.18) 50%,rgba(var(--divider-rgb),.08) 82%,transparent 100%) center bottom / 100% 1px no-repeat; }
.category-title { color: var(--text-secondary); font-size: 11px; font-weight: 600; letter-spacing: .07em; }
.bookmarks { grid-template-columns: repeat(auto-fill,96px); gap: 8px; }
.bookmark,.bookmark-text-only { --glow-x: 50%; --glow-y: 0%; position: relative; display: grid; place-items: center; min-height: 42px; padding: 6px 8px; overflow: hidden; border: 1px solid var(--bookmark-border); border-radius: 7px; background: var(--bookmark-face); box-shadow: var(--bookmark-idle-shadow); transition: transform var(--bookmark-motion) cubic-bezier(.2,.8,.25,1),border-color var(--bookmark-motion),box-shadow var(--bookmark-motion),background var(--bookmark-motion); }
.bookmark::before { content: ''; position: absolute; inset: 0; pointer-events: none; background: radial-gradient(ellipse 77px 51px at var(--glow-x) var(--glow-y),var(--glint-white),transparent 68%),radial-gradient(ellipse 103px 69px at var(--glow-x) var(--glow-y),var(--bookmark-glint),transparent 82%); opacity: .17; transition: opacity var(--bookmark-glow-motion); }
/* 顶边 1px 高光：让卡片在浅色背景上有「被从上方照亮」的边缘，
   与搜索框外壳、按钮的顶部 inset 高光同一套语言。
   左右各内缩 12px，与仿真一致——缩得太贴角会显得像描边而非受光。 */
.bookmark::after { content: ''; position: absolute; left: 12px; right: 12px; top: 0; height: 1px; pointer-events: none; background: linear-gradient(90deg,transparent,rgba(255,255,255,.96) 30%,rgba(255,255,255,.96) 70%,transparent); }
.bookmark:hover,.bookmark-text-only:hover { z-index: 1; transform: translateY(var(--bookmark-lift)); border-color: var(--bookmark-hover-border); background: var(--bookmark-hover-face); box-shadow: var(--bookmark-hover-shadow); }
.bookmark:hover::before,.bookmark:focus-visible::before { opacity: var(--bookmark-glow-opacity); }
.bookmark:active,.bookmark-text-only:active,.bookmark.is-pressed { transform: translateY(var(--bookmark-press-depth)) scale(var(--bookmark-press-scale)); border-color: var(--bookmark-pressed-border); background: var(--bookmark-pressed-face); box-shadow: var(--bookmark-press-shadow); transition-duration: var(--bookmark-press-motion); }
.bookmark:active::before,.bookmark.is-pressed::before { opacity: 0; transition-duration: 20ms; }
.bookmark-title,.bookmark-text-only .bookmark-title { position: relative; width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12px; font-weight: 600; letter-spacing: .015em; }
/* 页面级操作栏：背板底部的纯文字一行，取代原先右下角固定的 .utility-dock 药丸。
   药丸跟着视口走，窄屏会压住内容，且「编辑」在两种状态里换文案时宽度会跳。
   这里改为页面流内的一行居中弱化文字，悬停才提到正文色。
   编辑态的「取消 / 保存配置」是同一行里的两条入口，由 JS 控制显隐。 */
.site-footer { margin-top: 36px; padding-top: 18px; border-top: 1px solid var(--border); }
.site-footer-links { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 2px 10px; }
.site-footer-link { appearance: none; -webkit-appearance: none; position: relative; border: 0; background: none; padding: 6px 4px; color: var(--text-muted); font: inherit; font-size: 12px; line-height: 1.4; border-radius: 4px; cursor: pointer; transition: color 110ms; }
.site-footer-link:hover { color: var(--text); text-decoration: underline; text-underline-offset: 3px; }
.site-footer-link:disabled { cursor: default; color: var(--text-muted); text-decoration: none; opacity: .6; }
/* 保存是这一行里的主操作，其余入口都是弱化文字。 */
.site-footer-link.is-save { color: var(--accent); font-weight: 600; }
.site-footer-sep { color: var(--text-muted); font-size: 12px; opacity: .55; user-select: none; }
.modal-backdrop,.help-overlay { background: rgba(22,32,40,.39); backdrop-filter: blur(5px); -webkit-backdrop-filter: blur(5px); }
.modal-content,.help-content,.fav-dialog { border: 1px solid var(--border); background: var(--glass-strong); box-shadow: inset 0 1px rgba(255,255,255,.57),0 20px 48px rgba(35,27,21,.21); backdrop-filter: blur(23px) saturate(118%); -webkit-backdrop-filter: blur(23px) saturate(118%); }
.btn { border-color: var(--border); border-radius: 8px; background: linear-gradient(170deg,var(--control-top),var(--control-bottom)); color: var(--text); box-shadow: inset 0 1px rgba(255,255,255,.48),0 1px 3px rgba(67,51,39,.06); transition: transform 110ms,border-color 140ms,box-shadow 140ms,background 140ms,color 140ms; }
.btn:hover:not(:disabled) { transform: translateY(-1px); border-color: var(--focus); background: linear-gradient(170deg,var(--control-hover-top),var(--control-hover-bottom)); color: var(--text); box-shadow: inset 0 1px rgba(255,255,255,.68),0 3px 7px rgba(67,51,39,.1); }
.btn:active:not(:disabled) { transform: translateY(1px); background: var(--control-pressed-top); box-shadow: var(--press-shadow); }
.btn-primary { border-color: var(--accent); background: var(--accent); color: #fffaf5; }
.btn-primary:hover:not(:disabled) { border-color: var(--accent-hover); background: var(--accent-hover); color: #fffaf5; }
:root[data-theme="dark"] .btn-primary,:root[data-theme="dark"] .btn-primary:hover:not(:disabled) { color: #271e18; }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .btn-primary,:root:not([data-theme="light"]) .btn-primary:hover:not(:disabled) { color: #271e18; } }
.btn-danger { border: 1px solid rgba(157,74,68,.36); background: transparent; color: #a44c46; }
.btn-danger:hover:not(:disabled) { border-color: #a44c46; background: rgba(164,76,70,.11); color: #8a3833; }
:root[data-theme="dark"] .btn-danger,:root[data-theme="dark"] .config-save-status[data-state="error"] { color: #e9a69d; }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .btn-danger,:root:not([data-theme="light"]) .config-save-status[data-state="error"] { color: #e9a69d; } }
.bookmark:focus-visible,.btn:focus-visible,.site-footer-link:focus-visible,.search-mode:focus-visible,.search-btn:focus-visible,.search-engine:focus-visible,.fav-item:focus-visible { outline: 2px solid var(--focus); outline-offset: 3px; }
.item-row input:focus,.setting-row select:focus,.fav-form input:focus,.fav-form select:focus { border-color: var(--focus); outline: none; box-shadow: 0 0 0 3px rgba(148,94,74,.09); }
.toast { border: 1px solid var(--border); background: var(--glass-strong); color: var(--text); backdrop-filter: blur(18px); -webkit-backdrop-filter: blur(18px); }
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) { .search,.engine-dropdown,.fav-dropdown,.modal-content,.help-content,.fav-dialog,.toast,.backboard { background: var(--bg-card); } }
@media (prefers-reduced-transparency: reduce) { .search,.engine-dropdown,.fav-dropdown,.modal-content,.help-content,.fav-dialog,.toast,.backboard { backdrop-filter: none; -webkit-backdrop-filter: none; background: var(--bg-card); } }
@media (prefers-reduced-motion: reduce) { *,*::before,*::after { animation-duration: .01ms !important; transition-duration: .01ms !important; } .bookmark:hover,.btn:hover,.search-engine:hover,.search-btn:hover { transform: none; } }
@media (hover: none) { .search:hover::after,.bookmark:hover::before,.search-mode:hover::before,.search-engine:hover::before,.search-btn:hover::before { opacity: 0; } .bookmark:hover,.bookmark-text-only:hover,.btn:hover:not(:disabled),.search-engine:hover,.search-btn:hover { transform: none; } }
/* 触屏点按时关掉 UA 默认高亮。
   iOS Safari / 移动 Chrome 的 -webkit-tap-highlight-color 默认值是
   rgba(51,181,229,.4)，那抹蓝会盖在按钮的拟物材质上，与整套暖灰调冲突。
   键盘可达性不受影响：:focus-visible 的轮廓（2853 行）仍照常出现，
   它只在键盘导航时出现，正是该给提示的时候。 */
@media (hover: none) and (pointer: coarse) { .search-mode,.search-engine,.search-btn,.bookmark,.bookmark-text-only,.engine-option,.site-footer-link { -webkit-tap-highlight-color: transparent; } }
/* 快捷键说明行：只桌面显示。
   移动端的 display:none 唯一写在下面那条 ≤600px 块里。
   说明行贴着搜索框、与下方书签区拉开：间距由它自己给，
   所以 .header 的 margin-bottom 归零（≤600px 该行隐藏，那里再把间距还给 .header）。
   两个 span 的间距 20px——只有两项且都很短，14px 显得挤。 */
.search-caption { display: flex; justify-content: center; gap: 20px; margin: 10px 0 26px; color: var(--text-muted); font-size: 11px; }
.search-caption kbd { padding: 1px 4px; margin: 0 1px; border: 1px solid var(--border); border-radius: 4px; background: var(--glass); font: inherit; }
@media (max-width: 600px) { .app { padding: 25px 12px 48px; } .header { margin-bottom: 30px; } .search-caption { display: none; } .bookmarks { grid-template-columns: repeat(auto-fill,96px); gap: 8px; } .bookmark,.bookmark-text-only { min-height: 44px; padding: 6px 8px; } /* 底栏在窄屏就是手指在点：相邻两条入口必须各自撑到 44px 触控下限。 */ .site-footer-link { display: inline-flex; align-items: center; min-height: 44px; padding: 0 8px; } .toast { bottom: calc(28px + env(safe-area-inset-bottom)); max-width: calc(100% - 24px); } }
/* 三列铺满可用宽度：原先的 max-width:304px 在 390px 宽的机型上右侧留出大片空白，
   且使上一条 ≤600px 规则里的 auto-fill 96px 变成死代码（同为 ≤480px 且同特异性，后者在后）。 */
@media (max-width: 480px) { .bookmarks { max-width: 100%; grid-template-columns: repeat(3,minmax(0,1fr)); } }
@media (max-width: 370px) { .search { gap: 2px; } .search-mode { min-width: 43px; padding: 0 6px; } .search-engine { min-width: 55px; padding: 0 5px; } .search-input { padding: 0 5px; } .search-btn { min-width: 43px; padding: 0 6px; } .engine-dropdown { left: 51px; } }

/* ========== 宽屏背板与快捷键说明行 ========== */

/* 收束视觉：比 .app 窄一档，让宽屏下背景不再一览无余。
   padding-top 48px = 仿真的 24px 背板内边距 + 4px topbar 占位 + 20px 下边距。
   仿真保留那个空占位元素（本项目已随站点标识一并删除），
   两段留白叠起来比一段更透气；这里用单值等效还原，搜索框顶落在 83px。
   margin-left 不写 `margin: 0 auto`：auto 不可插值，首现那次让位动画
   （#app.is-railing，见下方宽栏段）要把它从窄态的居中量平滑推到
   宽态的 `--side-w + 20px`。数值居中量在 ≥1024 下恒 ≥22px（内容盒 ≥980px），
   与 auto 视觉等价；max-width 也显式写死 936——`none` 与长度值同样不可插值。 */
.backboard { position: relative; width: min(100%,936px); max-width: 936px; margin-left: calc((100% - 936px) / 2); margin-right: auto; padding: 48px 30px 30px; border-radius: 18px;
    background: linear-gradient(168deg,var(--glass-strong),var(--glass));
    box-shadow: inset 0 1px rgba(255,255,255,.72),inset 0 -1px rgba(105,81,63,.07),0 2px 6px rgba(67,51,39,.035),0 20px 48px rgba(67,51,39,.07);
    backdrop-filter: blur(20px) saturate(122%); -webkit-backdrop-filter: blur(20px) saturate(122%); }
/* 顶边受光高光 */
.backboard::before { content: ''; position: absolute; left: 30px; right: 30px; top: 0; height: 1px; pointer-events: none;
    background: linear-gradient(90deg,transparent,rgba(255,255,255,.95) 22%,rgba(255,255,255,.95) 78%,transparent); }
/* 四边发丝线：与 .category-header 分割线同一套 --divider-rgb 渐变语法 */
.backboard::after { content: ''; position: absolute; inset: 0; border-radius: inherit; pointer-events: none; padding: 1px;
    background: linear-gradient(90deg,transparent 0%,rgba(var(--divider-rgb),.10) 14%,rgba(var(--divider-rgb),.21) 50%,rgba(var(--divider-rgb),.10) 86%,transparent 100%);
    -webkit-mask: linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0); -webkit-mask-composite: xor;
    mask: linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0); mask-composite: exclude; }

/* 窄屏完全退回现状：透明、无描边、无模糊、无圆角、无内边距。
   margin 与 max-width 都必须显式归零：上面基础规则里的 margin-left 是个数值 calc，
   窄屏内容盒不足 936 时会算成负数把背板推出左边界；max-width 则是本轮新增的
   （为了让让位过渡可插值），不清掉的话 981~1023px 这段视口下 `width: auto` 会被它
   压到 936 且左对齐——实测 1000px 视口背板 956 → 936，比改前窄 20px。 */
@media (max-width: 1023px) { .backboard { width: auto; max-width: none; margin: 0; padding: 0; border-radius: 0; background: none; box-shadow: none; backdrop-filter: none; -webkit-backdrop-filter: none; }
    .backboard::before,.backboard::after { display: none; } }

/* ========== 模块平台 ==========
   登录后的 widget 区位于分类网格之后，首屏重点保留搜索与书签。
   复用既有 token，避免重复声明既有选择器；窄屏覆盖应排在基础规则之后。 */

/* `.app` 必须建立包含块（position: relative），否则 .module-zone 的
   `left/right: 22px` 会相对**视口**而不是 .app 的内容盒。
   两者在 .app 有居中留白时必然错位：实测 1760 宽的窗口下 .app 左边缘在
   160px，而模块区从视口的 22px 开始——卡片被甩到窗口左侧，离背板极远。
   sideDockAvailable() 又是用 .app 宽度算余量的，两个坐标系对不上。 */
.app { position: relative; }

/* 两侧布局由 JS 分配导航与模块宽度，模块至少 280px，导航至少 600px。
   余量不足时回到下方网格，保存的左右归属与排序继续保留。
   top 取 --module-top：那是 JS 实测出来的**搜索框顶端**（见 app.js 的
   alignModuleTop）。模块卡原先钉死在 48px，也就是背板顶边之上、页面最上沿，
   三栏没有共同的首行，读起来像两张卡挤在页角。 */
.module-zone { position: absolute; top: var(--module-top, 48px); left: 22px; right: 22px; margin-top: 0; pointer-events: none; }
.module-zone[hidden] { display: none; }
.module-zone-inner { display: contents; }
/* 纵向偏移由 JS 实测高度后写入 --stack-top，不是固定步进——
   卡片高度随内容变化（远端在线带「最后更新」那一行 174px / 本机 152px / 离线 98px），
   写死 166px 会让带提示的卡片压住下一张（实测重叠 8px）。 */
.module-widget[data-side="right"] { position: absolute; top: 0; right: 0; width: var(--side-w, 280px); margin-top: var(--stack-top, 0px); }
.module-widget[data-side="left"] { position: absolute; top: 0; left: 0; width: var(--side-w, 280px); margin-top: var(--stack-top, 0px); }
/* 首次布局不做让位动画（见 app.js 的 renderModuleZone）。
   卡片是带着 margin-top: 0 出生的，此时若允许过渡，四张卡会先完全重叠
   再各自滑开——那是加载时最显眼的「卡顿感」。只在这一次布局里关掉。 */
.module-zone.is-laying-out .module-widget { transition: none; }
/* 模块区**首次出现**时的入场。只动 opacity / transform——两者都由
   合成器处理，不触发布局，与 --stack-top（margin-top）那套让位动画互不干扰。
   时长必须与 app.js 的 App.MODULE_ENTER_MS 一致（有断言钉住）。
   方向是**从上方落下**（`translateY(-10px)` → 0）：配合每张卡各自的
   animation-delay（app.js 按 --stack-top 次序写 inline，首张等背板收窄完），
   观感是「导航先收窄，模块再从上到下一个个落到两侧」。
   ⚠️ delay 不写在这条规则里——它逐张不同，由 JS 写 inline animation-delay。
   ⚠️ 摘类之前 `animation-fill-mode: both` 会把最后一帧的 `transform: none`
   一直压着，而拖拽正是靠 inline transform 跟手——所以 app.js 播完必须摘掉。 */
@keyframes moduleEnter { from { opacity: 0; transform: translateY(-10px); } to { opacity: 1; transform: none; } }
.module-zone.is-entering .module-widget { animation: moduleEnter 260ms cubic-bezier(.2,.8,.25,1) both; }
/* 减少动态效果时不能还排队：入场的 delay 是逐张写在 inline 上的，全局那条
   reduced-motion 规则只压时长，压不掉它——卡片会一张张延迟出现（没有动画却仍排队）。
   这里给入场卡片补一条归零；**作用域限定在入场卡片**，不在全局 `*` 上顺手改所有动画的
   延迟（那会波及无关动画，也被本轮审查点名）。`!important` 能压过 inline 的 delay。 */
@media (prefers-reduced-motion: reduce) {
    .module-zone.is-entering .module-widget { animation-delay: 0s !important; }
}
/* 模块区整体不接收指针事件，只有卡片本身可交互——否则它铺满全宽会挡住背板 */
.module-zone-inner > .module-widget { pointer-events: auto; }

/* 放不下时回到文档流，网格按卡片可读宽度自动决定列数。 */
.module-zone[data-dock="below"] { position: static; margin-top: 24px; padding-top: 20px; border-top: 1px solid var(--border); pointer-events: auto; }
/* 单卡封顶 400px，手机占满可用宽度；平板与窄桌面可并排显示。 */
.module-zone[data-dock="below"] .module-zone-inner { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 320px), 1fr)); gap: 14px; align-items: start; justify-items: center; max-width: 936px; margin-inline: auto; }
/* 卡片是 relative 而不是 static：绝对定位的子元素需要一个包含块，static 会让
   它们去用 .module-zone（below 停靠时它也是 static，最终落到视口），那些
   逐行累积的溢出会撑宽整个文档——实测 390 视口下多出 338px，页面能真的横向
   滚动（这条原本只给 special-line 单开了一条例外，见下）。relative 不改变布局
   （top/left 为 auto），只是把包含块收到卡片自己身上。
   棱镜主题的模块卡要靠这层包含块画光学薄边（见 themes.css）。 */
.module-zone[data-dock="below"] .module-widget { position: relative; width: min(100%, 400px); margin-top: 0; }

/* 让位动画。基准（--stack-top）一变，卡片就换到新位置；给一个短过渡，
   让它「滑过去」而不是瞬移。没有过渡时其余卡片是硬跳的——用户说「生硬」
   主要指这一段，而不是被拖的那张（被拖的由 transform 补偿，位置是连续的）。 */
.module-widget { padding: 13px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--glass); box-shadow: var(--shadow); transition: box-shadow var(--bookmark-motion), border-color var(--bookmark-motion), margin-top 180ms cubic-bezier(.2,.8,.25,1); }
/* 拖拽中的那张不参与纵向过渡：它的位置由 transform 连续控制，
   再叠一个 margin 过渡会和基准补偿打架，出现二次抖动。 */
.module-widget.is-dragging { transition: box-shadow var(--bookmark-motion), border-color var(--bookmark-motion); }
/* 离线卡片：保留形状但压低存在感，让「哪台挂了」一眼可见，
   而不是和在线卡片长得一样、只能靠读数字才发现 */
.module-card[data-online="0"] { opacity: .62; border-style: dashed; }
/* 「离线」不再由 title::after 追加 —— 卡片头现在有一个真正的状态位，
   两处都写会显示成「名字 · 离线离线」。状态位承担全部文案。 */
.module-card-status {
    display: inline-flex; align-items: center; gap: 4px;
    flex: 0 0 auto; padding: 0 6px;
    border: 1px solid currentColor; border-radius: 999px;
    font-size: 9px; font-weight: 600; letter-spacing: .02em;
    white-space: nowrap;
}
.module-card-status[data-kind="ready"] { color: var(--accent); }
.module-card-status[data-kind="pending"] { color: #a2762c; }
.module-card-status[data-kind="not_deployed"] { color: var(--text-muted); }
.module-card-status[data-kind="offline"] { color: var(--text-secondary); }
/* ⚠️ kind 用的是服务端给的状态名（cert_mismatch），不是配色档名（alert）。
   两者混用过一次：CSS 只写了 [data-kind="alert"]，而模块发的是
   cert_mismatch，于是**安全事件从未被染成警示色**——浏览器实测读到的是
   var(--text-secondary) 的普通灰，看起来只是一次普通离线。
   这里两种都写上，让「配色档」与「状态名」任一变化都不会漏掉警示色。 */
.module-card-status[data-kind="cert_mismatch"],
.module-card-status[data-kind="alert"] { color: #a44c46; }
/* 卡片只有 132–220px 宽，标题要能被省略号截断。
   ⚠️ text-overflow: ellipsis 只在 white-space: nowrap 下生效——
   缺了它，长名字（「老式 http 配置」）会折成两行，把卡片撑高且
   参差不齐（浏览器实测：height 33 = 两行，而相邻卡片是 16.5 = 一行）。
   min-width:0 也要有：flex 子项默认 min-width:auto，不加的话
   省略号永远不触发（内容把卡片撑宽）。 */
.module-widget-title {
    min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* 深色下警示色要提亮，否则 #a44c46 在近黑背景上几乎看不见。
   ⚠️ cert_mismatch 与 alert 都要写——只写 alert 时，深色下的安全事件
   落回默认色（浏览器实测读到 rgb(48,43,39)，比背景还暗）。 */
:root[data-theme="dark"] .module-card-status[data-kind="cert_mismatch"],
:root[data-theme="dark"] .module-card-status[data-kind="alert"] { color: #e9a69d; }
:root[data-theme="dark"] .module-card-status[data-kind="pending"] { color: #e0bd7a; }
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .module-card-status[data-kind="cert_mismatch"],
    :root:not([data-theme="light"]) .module-card-status[data-kind="alert"] { color: #e9a69d; }
    :root:not([data-theme="light"]) .module-card-status[data-kind="pending"] { color: #e0bd7a; }
}
.module-card-hint { margin-top: 2px; color: var(--text-muted); font-size: 10px; text-align: right; }
.module-panel-list { margin-top: 16px; padding-top: 13px; border-top: 1px solid var(--border); }
.module-panel-list-title { margin-bottom: 8px; color: var(--text-secondary); font-size: 11px; font-weight: 600; letter-spacing: .06em; }
.module-panel-list-row { display: flex; align-items: center; gap: 12px; padding: 5px 0; }
.module-panel-list-row[data-online="0"] .module-panel-list-value { color: var(--text-muted); }
.module-panel-list-name { flex: 0 0 40%; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text); font-size: 12px; }
/* 概览行里的地址。比名称更弱一等——它是消歧信息，不是主要内容。 */
.module-panel-list-host { color: var(--text-muted); font-size: 10px; font-weight: 400; }
/* 详情页顶部的地址条。放在指标之外，这样即使这台机器拉不到数据
   也仍然能看到「它在哪」——排障时这是第一个要问的问题。 */
.module-panel-address { display: flex; align-items: center; gap: 10px; padding-bottom: 13px; margin-bottom: 13px; border-bottom: 1px solid var(--border); }
.module-panel-address-host { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text); font-size: 13px; font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace); }
.module-panel-list-value { flex: 1 1 auto; min-width: 0; color: var(--text-secondary); font-size: 11px; font-variant-numeric: tabular-nums; text-align: right; }
.module-widget-head { display: flex; align-items: center; gap: 8px; margin-bottom: 9px; }
.module-widget-title { flex: 1; color: var(--text-secondary); font-size: 11px; font-weight: 600; letter-spacing: .07em; }
.module-widget-expand { min-height: 26px; padding: 0 8px; border: 0; border-radius: 5px; background: transparent; color: var(--text-muted); font-size: 11px; font-family: inherit; cursor: pointer; transition: color 110ms, background 110ms; }
.module-widget-expand:hover { background: var(--bg-hover); color: var(--accent); }

/* 拖拽把手：默认隐藏，编辑模式才出现。触摸目标不设 44px——它是鼠标/长按操作，
   窄屏的实际拖动落点在卡片本体上。 */
.module-drag-handle { display: none; min-width: 26px; min-height: 26px; padding: 0; border: 0; border-radius: 5px; background: transparent; color: var(--text-muted); font-size: 13px; line-height: 1; cursor: grab; touch-action: none; }
.module-drag-handle::before { content: '⠿'; }
.module-drag-handle:hover { background: var(--bg-hover); color: var(--accent); }
.module-zone.is-editing .module-drag-handle { display: block; }
.module-zone.is-editing .module-widget { cursor: grab; }
.module-zone.is-editing .module-widget-expand { display: none; }
.module-widget.is-dragging { z-index: 5; opacity: .92; cursor: grabbing; box-shadow: var(--shadow-lg); transition: none; }
.module-widget.drop-target { border-color: var(--accent); }
.module-zone[data-drop-side="left"] .module-widget[data-side="left"],
.module-zone[data-drop-side="right"] .module-widget[data-side="right"] { border-color: var(--accent); }

/* ========== 首页编辑模式（书签网格的增删改与拖拽） ==========
   显隐全部由 .grid.is-editing 控制，JS 不写 hidden 属性——与模块拖拽把手
   同一条纪律（见 api-boundary.test.js「拖拽把手不由模块自己写 hidden 属性」那条用例）：
   挂载时 editLayout 几乎总是
   false，而 hidden 属性压过任何 CSS，控件会实测 0×0、点不到。
   这组规则**紧跟模块区**，不要挪到文件末尾：同优先级的重复声明后者胜，
   仓库已因尾部覆盖栽过两次。 */
.category-action { display: none; min-width: 26px; min-height: 26px; padding: 0 4px; border: 0; border-radius: 5px; background: transparent; color: var(--text-muted); font-family: inherit; font-size: 12px; line-height: 1; cursor: pointer; transition: color 110ms, background 110ms; }
.grid.is-editing .category-action { display: inline-flex; align-items: center; justify-content: center; }
.category-action:hover { background: var(--bg-hover); color: var(--accent); }
/* 删除按钮的红色与既有 .btn-danger 取同一批色值（styles.css:2872-2875），
   不新造变量——本仓库的危险色是硬编码的，成对定义在浅色与深色两处。 */
.grid.is-editing .category-del:hover { background: rgba(164,76,70,.11); color: #a44c46; }
:root[data-theme="dark"] .grid.is-editing .category-del:hover { background: rgba(233,166,157,.13); color: #e9a69d; }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .grid.is-editing .category-del:hover { background: rgba(233,166,157,.13); color: #e9a69d; } }
.grid.is-editing .category-drag { cursor: grab; }
.grid.is-editing .category-drag:active { cursor: grabbing; }

/* ＋占位卡必须与真实书签卡同尺寸，否则网格会因尺寸不同而错位。
   box-shadow 也要清掉：.bookmark 带 --bookmark-idle-shadow（内含 --shadow），
   虚线占位卡带着实体投影会比真实卡更重，与「占位」的视觉约定相反。 */
.bookmark-add, .category-add { border-style: dashed; border-color: var(--border); background: transparent; box-shadow: none; color: var(--text-muted); font-family: inherit; cursor: pointer; }
.bookmark-add { display: grid; place-items: center; min-height: 42px; border-width: 1px; border-radius: 7px; font-size: 17px; line-height: 1; }
.bookmark-add:hover { border-color: var(--accent); color: var(--accent); background: var(--bookmark-hover-face); }
.category-add { min-height: 40px; padding: 9px 14px; border-width: 1px; border-radius: var(--radius); font-size: 13px; }
.category-add:hover { border-color: var(--accent); color: var(--accent); }

/* 编辑态下书签卡是拖拽宿主，取代链接跳转。关掉 hover 抬升与按下凹陷，
   否则拖拽时卡片会浮起来，落点判断跟着偏。
   ⚠️ 必须连 :active / .is-pressed 一起中和：bindBookmarkPress 在 pointerdown
   就加 .is-pressed 并保留 135ms，只压 :hover 的话松手后的那一小段里
   抬升+缩放照旧出现——正是这里要避免的「卡片浮起来」。

   触摸端的长按拖拽靠 JS（bindTouchGridDrag），它还需要两件桌面端不需要的东西：
   1. user-select: none —— 否则长按会先选中卡片里的文字，拖拽根本起不来。
      只在**编辑态**关：常态首页仍要能选中文字。
   2. -webkit-touch-callout: none —— 关掉 iOS 长按弹出的「拷贝/查找/共享」菜单，
      否则长按 400ms 激活拖拽时菜单同时弹出，两者互相打断。
   ⚠️ 不要写 touch-action: none：书签网格在首页常常需要滚动浏览，写了就滚不动。
   激活判据是「长按 400ms」，滚动交给浏览器——这与模块把手的
   touch-action: none（那里没有滚动需求）是有意的不一致。 */
.grid.is-editing .bookmark,
.grid.is-editing .category-header {
    cursor: grab;
    -webkit-user-select: none;
    user-select: none;
    -webkit-touch-callout: none;
}
.grid.is-editing .bookmark:hover,
.grid.is-editing .bookmark:active,
.grid.is-editing .bookmark.is-pressed,
.grid.is-editing .bookmark-add:hover { transform: none; }
.grid.is-editing .bookmark.is-dragging { opacity: .4; }
.category.is-dragging { opacity: .55; }

/* 长按满 400ms、尚未移动时给一点反馈：否则用户以为没反应。
   激活后 .is-dragging 接管，这层提示随之退场。
   ⚠️ 状态选择器是**独立**的一条，不并进上面那条：并进去就等于
   `.grid.is-editing .bookmark` 被声明两次，而本仓库的守卫要求
   每个选择器在 .grid.is-editing 下只声明一次（同优先级后者胜，
   尾部覆盖栽过两次）。 */
.grid.is-editing .bookmark.is-arming,
.grid.is-editing .category-header.is-arming { opacity: .7; }
/* 分类落点提示：与 .category.is-dragging 配对（拖到谁头上谁就压暗）。
   这条规则此前不存在——分类拖拽整个不可用，也就没人写过它。 */
.grid.is-editing .category.drop-target { opacity: .55; }

/* 书签删除按钮：压在卡右上角，常态隐藏，编辑态悬停才显形。
   不写 hidden 属性，交给 .grid.is-editing + :hover 控制。 */
.bookmark-del { display: none; position: absolute; top: 3px; right: 3px; min-width: 22px; min-height: 22px; padding: 0; border: 0; border-radius: 5px; background: var(--control-pressed-top); color: var(--text-muted); font-family: inherit; font-size: 11px; line-height: 1; cursor: pointer; }
.grid.is-editing .bookmark:hover .bookmark-del, .grid.is-editing .bookmark-del:focus-visible { display: inline-flex; align-items: center; justify-content: center; }
.grid.is-editing .bookmark-del:hover { background: rgba(164,76,70,.11); color: #a44c46; }
:root[data-theme="dark"] .grid.is-editing .bookmark-del:hover { background: rgba(233,166,157,.13); color: #e9a69d; }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .grid.is-editing .bookmark-del:hover { background: rgba(233,166,157,.13); color: #e9a69d; } }

/* 指标行：标签 / 数值 / 进度条三段。进度不可知时 --pct 缺失，回落到 0 宽。 */
.module-metric { display: grid; grid-template-columns: auto 1fr; align-items: baseline; gap: 4px 8px; margin-bottom: 7px; }
.module-metric:last-child { margin-bottom: 0; }
.module-metric-label { color: var(--text-muted); font-size: 11px; }
.module-metric-value { color: var(--text); font-size: 12px; font-weight: 600; text-align: right; font-variant-numeric: tabular-nums; }
.module-metric-bar { grid-column: 1 / -1; height: 3px; border-radius: 2px; background: var(--bg-hover); overflow: hidden; }
.module-metric-fill { display: block; height: 100%; width: var(--pct, 0%); border-radius: inherit; background: linear-gradient(90deg, var(--accent), var(--accent-hover)); transition: width 320ms cubic-bezier(.2,.8,.25,1); }

.module-widget-loading { padding: 8px 0; color: var(--text-muted); font-size: 11px; }
.module-widget-error { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; padding: 9px 0; }
.module-widget-error-title { color: var(--text-secondary); font-size: 11px; font-weight: 600; }
.module-widget-error-detail { color: var(--text-muted); font-size: 11px; }
.module-widget-retry { min-height: 26px; padding: 0 9px; border: 1px solid var(--border); border-radius: 6px; background: var(--control-top); color: var(--text-secondary); font-size: 11px; font-family: inherit; cursor: pointer; }
.module-widget-retry:hover:not(:disabled) { border-color: var(--accent); color: var(--accent); }
.module-widget-retry:disabled { opacity: .5; cursor: default; }
/* 模块区整体的错误条（配置没拿到时渲染的那一条）。
   ⚠️ 必须自己开 pointer-events：`.module-zone` 是 `pointer-events: none`，
   而只有 `.module-zone-inner > .module-widget` 与 `[data-dock="below"]` 两处
   重新打开——错误条是 `.module-zone` 的**直接子节点**，不在那两条规则里，
   于是里面那个「重试」按钮点不动（实测命中测试返回的是 app）。 */
.module-zone-error { pointer-events: auto; display: flex; align-items: center; gap: 10px; padding: 13px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--glass); color: var(--text-muted); font-size: 11px; }

/* 全屏模块面板 */
.module-overlay { position: fixed; inset: 0; z-index: 200; display: grid; place-items: center; padding: 20px; background: rgba(22,32,40,.39); backdrop-filter: blur(5px); -webkit-backdrop-filter: blur(5px); }
.module-panel { width: min(100%, 480px); max-height: min(84dvh, 640px); overflow-y: auto; overscroll-behavior: contain; border: 1px solid var(--border); border-radius: 16px; background: var(--glass-strong); box-shadow: inset 0 1px rgba(255,255,255,.57), 0 20px 48px rgba(35,27,21,.21); backdrop-filter: blur(23px) saturate(118%); -webkit-backdrop-filter: blur(23px) saturate(118%); }
.module-panel-head { display: flex; align-items: center; justify-content: space-between; padding: 15px 18px 12px; border-bottom: 1px solid var(--border); }
.module-panel-head h2 { color: var(--text); font-size: 15px; font-weight: 600; letter-spacing: .02em; }
.module-panel-close { min-width: 32px; min-height: 32px; border: 0; border-radius: 8px; background: transparent; color: var(--text-muted); font-size: 20px; line-height: 1; cursor: pointer; }
.module-panel-close:hover { background: var(--bg-hover); color: var(--text); }
.module-panel-body { padding: 16px 18px 20px; }
.module-panel-metrics { margin-bottom: 16px; }
.module-panel-detail { display: grid; grid-template-columns: auto 1fr; gap: 7px 14px; padding-top: 13px; border-top: 1px solid var(--border); }
.module-panel-detail dt { color: var(--text-muted); font-size: 11px; }
.module-panel-detail dd { color: var(--text); font-size: 12px; text-align: right; font-variant-numeric: tabular-nums; }

/* ===== 备忘录模块 ===== */
/* 备忘卡片：与书签/搜索框同源的材料层（渐变表面 + 内高光 + 发丝边 + 外投影），
   不再用通用模块的平面 --glass。容器查询按卡片自身宽度做窄卡密度。 */
.memo-card { position: relative; container-type: inline-size; border-color: var(--bookmark-border); background: var(--bookmark-face); box-shadow: inset 0 1px rgba(255,255,255,.72), inset 0 -1px rgba(105,81,63,.07), var(--shadow); }
.memo-card::before { content: ''; position: absolute; left: 12px; right: 12px; top: 0; height: 1px; pointer-events: none; background: linear-gradient(90deg,transparent,rgba(255,255,255,.9) 30%,rgba(255,255,255,.9) 70%,transparent); }
.memo-card .module-widget-head { justify-content: center; padding-bottom: 8px; margin-bottom: 8px; background: linear-gradient(90deg,transparent 0%,rgba(var(--divider-rgb),.08) 18%,rgba(var(--divider-rgb),.18) 50%,rgba(var(--divider-rgb),.08) 82%,transparent 100%) center bottom / 100% 1px no-repeat; }
.memo-card .module-widget-head .memo-tabs { flex: 0 1 160px; min-width: 0; margin: 0; }
.memo-card .module-widget-body { min-width: 0; min-height: 0; }
.memo-card .memo-rows { max-height: min(240px, 45dvh); min-height: 0; overflow-y: auto; overscroll-behavior: contain; scrollbar-width: thin; scrollbar-gutter: stable; }
.memo-card .memo-rows:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.memo-card .memo-row { flex-shrink: 0; }
.memo-card .memo-card-footer { display: flex; flex-direction: column; gap: 4px; margin-top: 6px; }
.memo-card .memo-card-tools { display: flex; align-items: center; justify-content: flex-end; flex-wrap: wrap; gap: 4px 6px; min-width: 0; }
.memo-card .memo-sync-part { display: inline-flex; align-items: center; justify-content: flex-end; flex-wrap: wrap; gap: 4px; }

.memo-card-manage { min-height: 24px; padding: 0 8px; border: 1px solid var(--border); border-radius: 6px; background: linear-gradient(170deg, var(--control-top), var(--control-bottom)); box-shadow: inset 0 1px rgba(255,255,255,.5), 0 1px 2px rgba(60,45,32,.08); color: var(--text-muted); font: inherit; font-size: 10px; cursor: pointer; transition: color 110ms, background 110ms, box-shadow 110ms, transform 110ms; }
.memo-card-manage:hover { color: var(--accent); background: linear-gradient(170deg, var(--control-hover-top), var(--control-hover-bottom)); transform: translateY(-1px); }
.memo-card-manage:active { transform: translateY(1px); box-shadow: var(--press-shadow); }
.memo-status-row { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.memo-status-time { font-size: 10px; color: var(--text-muted); font-variant-numeric: tabular-nums; }
.memo-retry { border: 0; padding: 0; background: none; color: var(--accent); font-size: 10px; text-decoration: underline; cursor: pointer; }
.memo-spinner { width: 9px; height: 9px; border-radius: 50%; border: 1.5px solid currentColor; border-top-color: transparent; animation: memo-spin 700ms linear infinite; }
@keyframes memo-spin { to { transform: rotate(360deg); } }

.memo-rows { display: flex; flex-direction: column; gap: 2px; }
.memo-row { position: relative; display: flex; align-items: center; gap: 6px; padding: 5px 6px; border: 1px solid transparent; border-radius: 7px; cursor: pointer; transition: transform var(--bookmark-motion) cubic-bezier(.2,.8,.25,1), box-shadow var(--bookmark-motion), background var(--bookmark-motion), border-color var(--bookmark-motion); }
.memo-row:hover { transform: translateY(var(--bookmark-lift)); border-color: var(--bookmark-hover-border); background: var(--bookmark-hover-face); box-shadow: inset 0 1px rgba(255,255,255,.6), var(--shadow); }
.memo-row:active { transform: translateY(var(--bookmark-press-depth)) scale(var(--bookmark-press-scale)); border-color: var(--bookmark-pressed-border); background: var(--bookmark-pressed-face); box-shadow: var(--bookmark-press-shadow); transition-duration: var(--bookmark-press-motion); }
.memo-row:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.memo-row svg { flex: 0 0 auto; color: var(--accent); }
.memo-row-title { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text); font-size: 12px; font-weight: 600; letter-spacing: .015em; }
.memo-row-time { flex: 0 0 auto; font-size: 10px; color: var(--text-muted); font-variant-numeric: tabular-nums; }
.memo-empty-card { padding: 8px 6px; color: var(--text-muted); font-size: 11px; text-align: center; }

/* ===== 微博热搜模块 ===== */
.weibo-card-status { display: inline-flex; align-items: center; gap: 4px; flex: 0 0 auto; font-size: 10px; color: var(--text-muted); font-variant-numeric: tabular-nums; }
.weibo-card-status[data-kind="failed"] { color: #a44c46; }
:root[data-theme="dark"] .weibo-card-status[data-kind="failed"] { color: #e9a69d; }
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .weibo-card-status[data-kind="failed"] { color: #e9a69d; }
}
.weibo-retry { border: 0; padding: 0; background: none; color: var(--accent); font-size: 10px; text-decoration: underline; cursor: pointer; }
.weibo-rows { display: flex; flex-direction: column; gap: 2px; }
/* 行本身不带 hover transform——FLIP 期间 transform 归 FLIP 管，两者会打架 */
.weibo-row { display: flex; align-items: center; gap: 6px; padding: 5px 6px; border-radius: 7px; text-decoration: none; color: inherit; transition: background 110ms; }
.weibo-row:hover { background: var(--bg-hover); }
.weibo-row:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.weibo-rank { flex: 0 0 16px; font-size: 11px; font-weight: 600; color: var(--text-muted); text-align: center; font-variant-numeric: tabular-nums; }
.weibo-row[data-top="1"] .weibo-rank { color: #a44c46; }
:root[data-theme="dark"] .weibo-row[data-top="1"] .weibo-rank { color: #e9a69d; }
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .weibo-row[data-top="1"] .weibo-rank { color: #e9a69d; }
}
/* 标题走常规字重：15 行连排时 600 会糊成一片视觉重块（用户反馈「粗、显眼、
   密」）。备忘录卡同为卡内列表但预览只露 3 行、可保 600；书签卡一卡一条同理。
   唯独这里是 15 行满屏文本，必须降档。 */
.weibo-title { flex: 1; min-width: 0; white-space: normal; overflow-wrap: anywhere; line-height: 1.6; color: var(--text); font-size: 12px; font-weight: 400; letter-spacing: .015em; }
.weibo-badge { flex: 0 0 auto; padding: 0 4px; border: 1px solid currentColor; border-radius: 4px; font-size: 9px; line-height: 15px; font-weight: 500; }
.weibo-badge[data-label="热"] { color: #a44c46; }
.weibo-badge[data-label="新"] { color: var(--accent); }
.weibo-badge[data-label="辟谣"] { color: #4a7f62; }
.weibo-badge[data-label=""] { display: none; }
:root[data-theme="dark"] .weibo-badge[data-label="热"] { color: #e9a69d; }
:root[data-theme="dark"] .weibo-badge[data-label="辟谣"] { color: #7fbf9d; }
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .weibo-badge[data-label="热"] { color: #e9a69d; }
    :root:not([data-theme="light"]) .weibo-badge[data-label="辟谣"] { color: #7fbf9d; }
}
.weibo-hot { flex: 0 0 auto; font-size: 10px; color: var(--text-muted); font-variant-numeric: tabular-nums; }
.weibo-empty { padding: 8px 6px; color: var(--text-muted); font-size: 11px; text-align: center; }
/* 侧挂窄卡：热度列藏起来，宽度还给标题（仿真样例实测 220px 卡标题只剩 94px） */
.weibo-card { container-type: inline-size; }
@container (max-width: 240px) { .weibo-hot { display: none; } }

/* 管理面板：弹性布局，列表区独立滚动，头尾固定 */
.memo-panel { display: flex; flex-direction: column; overflow: hidden; }
.memo-dialog-header { display: flex; align-items: center; gap: 10px; padding: 13px 18px 11px; border-bottom: 0; background: linear-gradient(90deg,transparent 0%,rgba(var(--divider-rgb),.08) 18%,rgba(var(--divider-rgb),.18) 50%,rgba(var(--divider-rgb),.08) 82%,transparent 100%) center bottom / 100% 1px no-repeat; flex-wrap: wrap; }
.memo-dialog-header h2 { margin-right: auto; font-size: 16px; font-weight: 600; letter-spacing: .01em; }
.memo-dialog-header .memo-status-row { margin: 0; }
/* 输入类控件照**仓库既有对话框**的配方，不是抬起的浅色板：
   `.ui-dialog` 的输入框是「下沉的暗槽」——底色取 --bg、外加 inset 阴影
   （实测：应用 rgb(42,37,33) + inset 0 1px 2px rgba(0,0,0,.24)）。
   早先这里用 --bg-card 铺了一块比面板还亮的板，在 480px 的小面板里
   读起来就是一整块「大输入框」——用户原话「搜索框太大」。
   高度另按更紧凑的档取（7px 11px），比 .ui-dialog 的 40px 矮一点。 */
.memo-search { padding: 10px 18px 6px; }
.memo-search input {
    width: 100%; padding: 7px 11px; font-size: 13px; font-family: inherit;
    color: var(--text); background: var(--bg); border: 1px solid var(--border);
    border-radius: 8px; outline: none;
    box-shadow: inset 0 1px 2px rgba(0,0,0,.24), inset 0 -1px rgba(255,255,255,.09);
}
.memo-search input:focus { border-color: var(--accent); box-shadow: inset 0 1px 2px rgba(0,0,0,.24), 0 0 0 3px var(--focus); }
.memo-search input::placeholder { color: var(--text-muted); }
.memo-list { flex: 1; overflow-y: auto; overscroll-behavior: contain; padding: 8px 18px 12px; }
.memo-section-title { margin: 12px 0 6px; color: var(--text-secondary); font-size: 11px; font-weight: 600; letter-spacing: .06em; }
.memo-row-lg { display: flex; align-items: center; gap: 10px; padding: 8px; border-radius: 8px; transition: background var(--bookmark-motion), box-shadow var(--bookmark-motion); }
.memo-row-lg:hover { background: var(--bookmark-hover-face); box-shadow: inset 0 1px rgba(255,255,255,.5), var(--shadow); }
.memo-row-lg:active { background: var(--bookmark-pressed-face); box-shadow: var(--bookmark-press-shadow); }
.memo-row-lg:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.memo-pin-btn { flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 28px; border: 0; border-radius: 7px; background: transparent; color: var(--text-muted); cursor: pointer; transition: color 110ms, background 110ms, transform 110ms; }
.memo-pin-btn:hover { background: var(--bg-hover); color: var(--accent); }
.memo-pin-btn[aria-pressed="true"] { color: var(--accent); }
.memo-pin-btn:active { transform: scale(.92); }
.memo-row-main { flex: 1; min-width: 0; cursor: pointer; }
.memo-row-lg .memo-row-title { font-size: 13px; }
.memo-row-snippet { margin-top: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 11px; color: var(--text-muted); }
.memo-row-lg .memo-row-time { font-size: 10px; }
.memo-pend-dot { display: inline-block; width: 6px; height: 6px; margin-left: 6px; border-radius: 50%; background: #a2762c; vertical-align: 2px; }
.memo-row-actions { flex: 0 0 auto; display: flex; gap: 6px; opacity: 0; transition: opacity 110ms; }
.memo-row-lg:hover .memo-row-actions, .memo-row-lg:focus-within .memo-row-actions { opacity: 1; }
/* 行内操作收进悬停浮层：常驻占位会把 220 宽卡片的标题挤到只剩一个字。
   与书签右上角的删除键同一思路——操作在需要时出现，不参与常态排版。 */
.memo-card .memo-row-actions { position: absolute; right: 4px; top: 50%; transform: translateY(-50%); padding: 2px; border-radius: 7px; background: var(--bookmark-hover-face); box-shadow: inset 0 1px rgba(255,255,255,.45), 0 2px 6px rgba(60,45,32,.14); }
.memo-card .memo-row:hover .memo-row-actions, .memo-card .memo-row:active .memo-row-actions, .memo-card .memo-row:focus-within .memo-row-actions { opacity: 1; }
.memo-card .memo-row-time { transition: opacity 110ms; }
.memo-card .memo-row:hover .memo-row-time, .memo-card .memo-row:active .memo-row-time { opacity: 0; }
.memo-del-confirm { flex: 1; display: flex; align-items: center; gap: 8px; font-size: 12px; color: #a44c46; flex-wrap: wrap; }
.memo-del-confirm span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.memo-none { padding: 19px 0; text-align: center; color: var(--text-muted); font-size: 12px; }
.memo-dialog-footer { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 10px 18px; border-top: 0; background: linear-gradient(90deg,transparent 0%,rgba(var(--divider-rgb),.08) 18%,rgba(var(--divider-rgb),.18) 50%,rgba(var(--divider-rgb),.08) 82%,transparent 100%) center top / 100% 1px no-repeat; }
.memo-count { font-size: 11px; color: var(--text-muted); font-variant-numeric: tabular-nums; }

/* 编辑表单 */
.memo-form { flex: 1; overflow-y: auto; overscroll-behavior: contain; padding: 12px 18px 14px; display: flex; flex-direction: column; gap: 6px; }
.memo-form label { display: flex; justify-content: space-between; font-size: 12px; font-weight: 600; color: var(--text-secondary); margin-top: 6px; }
.memo-form input, .memo-form textarea {
    width: 100%; padding: 7px 11px; font-size: 13px; font-family: inherit;
    color: var(--text); background: var(--bg); border: 1px solid var(--border);
    border-radius: 8px; outline: none; resize: vertical;
    box-shadow: inset 0 1px 2px rgba(0,0,0,.24), inset 0 -1px rgba(255,255,255,.09);
}
.memo-form input:focus, .memo-form textarea:focus { border-color: var(--accent); box-shadow: inset 0 1px 2px rgba(0,0,0,.24), 0 0 0 3px var(--focus); }
.memo-form-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 4px; }

/* 行内小按钮（编辑 / 删除），与 .module-panel-close 同族 */
.icon-btn { display: inline-flex; align-items: center; justify-content: center; min-width: 28px; min-height: 28px; padding: 0 6px; border: 1px solid var(--border); border-radius: 7px; background: linear-gradient(170deg, var(--control-top), var(--control-bottom)); color: var(--text); font-size: 12px; box-shadow: inset 0 1px rgba(255,255,255,.48), 0 1px 3px rgba(67,51,39,.06); transition: transform 110ms, background 140ms; }
.icon-btn:hover { transform: translateY(-1px); background: linear-gradient(170deg, var(--control-hover-top), var(--control-hover-bottom)); }
.icon-btn:active { transform: translateY(1px); background: var(--control-pressed-top); box-shadow: var(--press-shadow); }
.icon-btn.danger { color: #a44c46; }
.icon-btn.danger:hover { background: rgba(164,76,70,.11); }

/* 面板内的按钮与仓库既有对话框同级：12px 字 / 35px 高 / padding 6px 13px，
   与 `.ui-dialog .btn` 实测值一致。早先沿用全局 .btn 的 14px / 38px / 8px 16px，
   在 480px 面板里比周围文字（11–13px）明显重一档——用户原话「按钮不够精致」。 */
.memo-panel .btn { padding: 6px 13px; font-size: 12px; min-height: 35px; }
.memo-panel .module-panel-close { min-width: 30px; min-height: 30px; border: 1px solid var(--border); border-radius: 7px; background: linear-gradient(170deg, var(--control-top), var(--control-bottom)); color: var(--text-secondary); font-size: 18px; box-shadow: inset 0 1px rgba(255,255,255,.5), 0 1px 2px rgba(60,45,32,.08); transition: color 110ms, transform 110ms, box-shadow 110ms; }
.memo-panel .module-panel-close:hover { color: var(--accent); transform: translateY(-1px); }
.memo-panel .module-panel-close:active { transform: translateY(1px); box-shadow: var(--press-shadow); }

/* ========== 备忘 / 待办页签 ==========
   最窄卡片 132px、正文可用 106px，两个 11px
   中文页签各约 40px，加内边距与间隙刚好——别再加第三个页签。
   ⚠️ 这一组只写在 styles.css：admin.css 后加载，同名选择器会被它覆盖。 */
.memo-tabs { display: flex; gap: 2px; margin-bottom: 7px; padding: 3px; border: 1px solid var(--border); border-radius: 8px; background: var(--bg); box-shadow: inset 0 1px 2px rgba(0,0,0,.18), inset 0 -1px rgba(255,255,255,.08); }
.memo-tab { flex: 1 1 0; min-width: 0; min-height: 26px; padding: 0 4px; border: 0; border-radius: 6px; background: transparent; color: var(--text-secondary); font-family: inherit; font-size: 11px; line-height: 1; cursor: pointer; white-space: nowrap; transition: background 110ms, color 110ms, box-shadow 110ms, transform 110ms; }
.memo-tab:hover { color: var(--text); }
.memo-tab[aria-selected="true"] { background: linear-gradient(170deg, var(--control-top), var(--control-bottom)); color: var(--text); font-weight: 600; box-shadow: inset 0 1px rgba(255,255,255,.55), 0 1px 2px rgba(60,45,32,.16); }
.memo-tab:active { transform: translateY(1px); box-shadow: var(--press-shadow); }
.memo-tabs-lg { margin: 10px 18px 0; }
.memo-panel .memo-tab { min-height: 30px; font-size: 12px; }

/* ========== 待办 ========== */
.memo-todo-rows { gap: 0; }
.memo-todo-row { gap: 8px; }
/* 勾选框外面包一层 label：触摸目标的尺寸由这一层承担（input 本身只有 18px） */
.memo-todo-check-wrap { position: relative; flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center; width: 26px; height: 26px; cursor: pointer; }
.memo-todo-check { appearance: none; -webkit-appearance: none; width: 18px; height: 18px; margin: 0; border: 1px solid var(--bookmark-border); border-radius: 5px; background: linear-gradient(180deg, var(--control-top), var(--control-bottom)); box-shadow: inset 0 1px rgba(255,255,255,.6), 0 1px 2px rgba(60,45,32,.14); cursor: pointer; transition: background 110ms, border-color 110ms, box-shadow 110ms, transform 110ms; }
.memo-todo-check:checked { border-color: var(--accent-hover); background: linear-gradient(180deg, var(--accent), var(--accent-hover)); box-shadow: inset 0 1px rgba(255,255,255,.32), inset 0 -2px rgba(0,0,0,.12); }
.memo-todo-check:active { transform: scale(.92); box-shadow: var(--press-shadow); }
.memo-todo-check:focus-visible { outline: 2px solid var(--focus); outline-offset: 3px; }
/* 勾用手绘遮罩而不是背景图：遮罩颜色能跟主题走，深色下换成深勾；
   白勾在深色主题的浅陶土底上对比不足。 */
.memo-todo-check-wrap::after { content: ''; position: absolute; inset: 0; margin: auto; width: 12px; height: 12px; background: #fff; pointer-events: none; opacity: 0; transform: scale(.8); transition: opacity 110ms, transform 110ms; -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 8.4l3 3 6-6.4' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / contain no-repeat; mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 8.4l3 3 6-6.4' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / contain no-repeat; }
.memo-todo-check-wrap:has(.memo-todo-check:checked)::after { opacity: 1; transform: scale(1); }
/* 深色主题的 accent 偏浅：选中底压深，白勾才够对比；两处深色定义成对写 */
:root[data-theme="dark"] .memo-todo-check:checked { background: linear-gradient(180deg, color-mix(in srgb, var(--accent) 74%, #000), color-mix(in srgb, var(--accent) 58%, #000)); }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .memo-todo-check:checked { background: linear-gradient(180deg, color-mix(in srgb, var(--accent) 74%, #000), color-mix(in srgb, var(--accent) 58%, #000)); } }
.memo-todo-text { flex: 1 1 auto; min-width: 0; padding: 2px 0; border: 0; background: transparent; color: var(--text); font-family: inherit; font-size: 12px; line-height: 1.35; text-align: left; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; cursor: text; }
.memo-todo-text:hover { color: var(--accent); }
/* 完成态三件套：删除线 + 文字变灰 + 斜体。卡片与面板共用这一条规则，
   别在别处再写一份（两份声明会随主题/断点漂移）。 */
.memo-todo-text[data-done="1"] { text-decoration: line-through; color: var(--text-muted); font-style: italic; }
.memo-todo-text[data-done="1"]:hover { color: var(--text-muted); }
/* 面板里文字可以换行（200 字的长待办），卡片上仍单行省略 */
.memo-panel .memo-todo-text { font-size: 13px; white-space: normal; overflow-wrap: anywhere; }
.memo-todo-input { flex: 1 1 auto; min-width: 0; padding: 4px 8px; font-size: 12px; font-family: inherit; color: var(--text); background: var(--bg); border: 1px solid var(--accent); border-radius: 6px; outline: none; box-shadow: 0 0 0 3px var(--focus); }
.memo-todo-list { padding-top: 6px; }
.memo-todo-footer { flex-wrap: wrap; }
.memo-todo-add { display: flex; align-items: center; gap: 6px; flex: 1 1 220px; min-width: 0; }
.memo-todo-add input { flex: 1 1 auto; min-width: 0; padding: 7px 11px; font-size: 13px; font-family: inherit; color: var(--text); background: var(--bg); border: 1px solid var(--border); border-radius: 8px; outline: none; box-shadow: inset 0 1px 2px rgba(0,0,0,.24), inset 0 -1px rgba(255,255,255,.09); }
.memo-todo-add input:focus { border-color: var(--accent); box-shadow: inset 0 1px 2px rgba(0,0,0,.24), 0 0 0 3px var(--focus); }

/* 132px 侧栏：标题可读优先。时间与摘要在窄卡里只是噪声，隐藏后标题多出约一倍宽度；
   容器查询按卡片自身宽度判断，不跟视口走（与 weibo 卡片同一做法）。 */
@container (max-width: 170px) {
    .memo-card .memo-row-time, .memo-card .memo-row-snippet, .memo-card .memo-card-count { display: none; }
    .memo-card .memo-tab { font-size: 10px; }
}

/* 深色下警示色与「待同步」圆点要提亮（与 .module-card-status 同值） */
:root[data-theme="dark"] .memo-pend-dot { background: #e0bd7a; }
:root[data-theme="dark"] .memo-del-confirm { color: #e9a69d; }
:root[data-theme="dark"] .icon-btn.danger { color: #e9a69d; }
:root[data-theme="dark"] .icon-btn.danger:hover { background: rgba(233,166,157,.13); }
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .memo-pend-dot { background: #e0bd7a; }
    :root:not([data-theme="light"]) .memo-del-confirm { color: #e9a69d; }
    :root:not([data-theme="light"]) .icon-btn.danger { color: #e9a69d; }
    :root:not([data-theme="light"]) .icon-btn.danger:hover { background: rgba(233,166,157,.13); }
}

/* 窄屏：触控目标抬到 44px 下限（与仓库既有做法一致），行内操作常显。
   桌面端 ✎/✕ 靠 hover 显形，触摸设备没有 hover——不常显就永远看不见。 */
@media (max-width: 1023px) {
    .memo-pin-btn, .memo-panel .icon-btn { min-width: 44px; min-height: 44px; }
    .memo-panel .btn { min-height: 44px; }
    /* 页签与勾选是卡片上的主控件，触摸设备一手点得中；面板里的同名规则
       特异性更高（0,2），这里必须一起写，否则面板仍是 30px。 */
    .memo-tab, .memo-panel .memo-tab { min-height: 44px; }
    .memo-todo-check-wrap, .memo-panel .memo-todo-check-wrap { width: 44px; height: 44px; }
    .memo-todo-check, .memo-panel .memo-todo-check { width: 18px; height: 18px; }
    .memo-row-lg .memo-row-actions { opacity: 1; }
}
/* 宽视口下的触控设备（平板）：同一件事，按能力问而不是按宽度问。
   注意 headless 里 (hover: none) 恒为 false，这条本地验证不到。 */
@media (hover: none) {
    .memo-row-lg .memo-row-actions { opacity: 1; }
}

/* 降级：prefers-reduced-transparency 下模块面板退回不透明底色，
   与 .backboard 的同一处理。 */
@media (prefers-reduced-transparency: reduce) {
    .module-widget, .module-panel, .module-overlay { backdrop-filter: none; -webkit-backdrop-filter: none; background: var(--bg-card); }
}

/* 窄屏撤销侧栏定位，下方网格与 data-dock="below" 使用同一套宽度规则。 */
@media (max-width: 1023px) {
    /* 窄屏把绝对定位全部撤掉，模块区回到文档流、落在背板（此断点下已是透明容器）下方。
       这些 reset 必须与宽屏那条成对出现：只写宽屏定位而不写这里，
       绝对定位会一路带到手机上，模块区脱离文档流、页面高度塌掉。 */
    .app { max-width: 1200px; }
    .module-zone { position: static; margin-top: 24px; padding-top: 20px; border-top: 1px solid var(--border); pointer-events: auto; }
    .module-zone-inner { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 320px), 1fr)); gap: 14px; align-items: start; justify-items: center; max-width: 936px; margin-inline: auto; }
    .module-widget { width: min(100%, 400px); }
    .module-widget[data-side] { position: static; width: min(100%, 400px); margin-top: 0; right: auto; left: auto; top: auto; }
    .module-overlay { padding: 12px; }
    .module-panel { max-height: calc(100dvh - var(--kb-inset, 0px) - 24px); }
}

/* ==================== Special Line 时间线（模块） ====================
   形态与仿真样例（docs/mockup-special-line.html）一致：左侧日期轨道 + 贯穿节点线
   + 事件卡 + chips 筛选，**内联**显示在首页右侧那一列里（不是「摘要卡 + 弹窗」）。
   两套版式由**容器查询**按卡片的实际宽度切换：宽列用桌面版，窄列用仿真 ≤700px 那套。
   类名统一 special-line- 前缀，避免与既有模块的类撞名。 */

/* 告警 / 警示色。⚠️ 本仓库**没有** --danger / --warning / --success 这三个主题
   token（`grep -- '--danger:' public/styles.css` 只会命中注释里的引用）。颜色的
   既有写法是「hex + 深色块覆盖」，所以这里自带一对命名空间化的变量，值取自
   .btn-danger 与 .module-card-status[data-kind="alert"]。引用不存在的 token
   不会报错，只会静默丢掉那条声明。 */
:root {
    --sl-danger: #a44c46;
    --sl-warning: #a2762c;
}
:root[data-theme="dark"] {
    --sl-danger: #e9a69d;
    --sl-warning: #e0bd7a;
}
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
        --sl-danger: #e9a69d;
        --sl-warning: #e0bd7a;
    }
}

.sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

/* ---------- 宽栏模式（平台机制，由 #app[data-rail] 驱动） ----------
   声明了 `wideRail: true` 的模块（今天只有 Special Line）需要一条真宽度的列，
   否则仿真那套「日期轨道 + 事件卡」在 210px 里没法成立。做法：右侧固定留 420px，
   背板收成「剩下的宽度」，左侧那一列也拿一份。三个值一律由 app.js 的
   railLayoutFor() 算好后写成 CSS 变量——这里不再自己算宽度（自己算会做出阈值
   悬崖：窗口缩 1px 模块就从 440 掉到 400 以下）。放不下就不进这个模式，
   模块自己再按容器宽度降级成窄版。 */
/* 三个宽度由 app.js 的 railLayoutFor() 算好后写在 #app 的行内变量上
   （--rail-w / --side-w / --board-max）——三列是「一起挤出来」的连续值，
   不会出现「窗口缩 1px 模块从 420 掉到 132」那种阈值悬崖。
   ⚠️ 背板默认靠 `margin-left: calc((100% - 936px) / 2)` 居中（数值，不是 `auto`——
   首现那次的让位过渡要可插值），必须显式改成靠左（`margin-left` 取左侧那一列的
   宽度），否则右侧那条时间线列会压在背板上——实测压 124px，而只看宽度公式是看不
   出来的。左边的 margin 必须把**列间距**一起算进去（`--side-w + 20px`），只写
   `--side-w` 会让左列与背板贴在一起、左右间距不对称。 */
#app[data-rail="wide"] .backboard {
    max-width: var(--board-max, 936px);
    margin-left: calc(var(--side-w, 180px) + 20px);
    margin-right: auto;
    /* 宽栏停靠时两侧模块把背板压到 600px：原 30px 侧内边距加分类自身的
       24px，分类内宽只剩 490px（再扣 2px 边框），放 4 个 96px 书签后还余
       82px，差 22px 才够第 5 个。这里把横向留白收到 22px，配合下面棱镜
       分类的 20px，内宽变为 600 - 44 - 40 - 2 = 514px，正好容纳 5 列
       （5×96 + 4×8 = 512）。 */
    padding-left: 22px;
    padding-right: 22px;
}
/* 顶边受光高光按基础规则的 30px 内缩；背板内边距收到 22px 后要跟着对齐，
   否则经典主题的那条 1px 亮边会比内容左缘再缩进 8px。棱镜隐藏了背板伪元素。 */
#app[data-rail="wide"] .backboard::before { left: 22px; right: 22px; }
#app[data-rail="wide"] .module-widget[data-side="right"] { width: var(--rail-w, 420px); }
#app[data-rail="wide"] .module-widget[data-side="left"] { width: var(--side-w, 180px); }

/* 棱镜分类卡自身还有 24px 横向内边距；宽栏停靠时收到 20px，让背板那侧
   让出的 16px 与这里的 8px 一起凑成 516px 内宽。经典主题分类没有内边距，
   不需要这条。 */
:root[data-theme-style="prism"] #app[data-rail="wide"] .category {
    padding-left: 20px;
    padding-right: 20px;
}

/* 首现那次让位的过渡：登录后模块区第一次出现时，背板从「936 居中」滑到
   「--board-max + 左让位」。这是入场编排的**第一段**——卡片的首张
   animation-delay 等于本时长（app.js 的 renderModuleZone 里 baseDelay），
   所以观感是「导航先收窄，收完模块再从上到下一个个落下」。
   - **类门控**，不写在基础规则或 [data-rail] 规则上：只有 #app.is-railing 这
     一小段时间开着过渡，摘类后 resize / 登出 / 配置改版仍瞬时切换——拖窗口时
     背板跟着拖泥带水地走，比硬跳更难受（那几条路径刻意保持瞬时）。
   - 260ms 必须与 app.js 的 App.MODULE_ENTER_MS 同值、曲线同 moduleEnter
     （有断言钉住）：它既是背板收窄的时长，也是首张卡的起跑延迟——两者漂移的话
     卡片会在背板还在挪的时候就开始落。
   - 过渡的是 max-width 与 margin-left 两个**布局**属性，成本≈一次窗口缩放，
     一次性 260ms；类由 watchRailShift 用 transitionend + 定时器兜底摘掉。 */
#app.is-railing .backboard {
    transition: max-width 260ms cubic-bezier(.2,.8,.25,1), margin-left 260ms cubic-bezier(.2,.8,.25,1);
}

/* ---------- 卡片：纵向四段（头 / 筛选 / 列表 / 底） ---------- */
.special-line-card {
    display: flex;
    flex-direction: column;
    gap: 8px;
    /* 右侧那一列的高度不能无限长：列表超出就在卡片内滚动。
       默认展示长度调大（原 62vh/620px）：卡片更高、首屏看到的事件更多。 */
    max-height: min(74vh, 760px);
    /* 版式按**卡片自己的宽度**切，而不是按视口：同一份 DOM 在 210px 与 420px
       下各自成立（拖到左列、窗口变窄都不需要重渲染）。 */
    container-type: inline-size;
}

/* 时间线的左侧留白收紧一点：平台卡片的 13px 内边距 + 时间轨右对齐留下的空档
   叠起来，时间左侧实测有 32px 空白（用户原话「时间左侧还有一些空白」）。
   这里把左右内边距收到 9px，配合时间轨 48→44px，时间左侧降到 24px。 */
.module-widget[data-module-id="special-line"] { padding-inline: 9px; }

/* 列表自身可聚焦：底部不再有「加载更早事件」按钮之后，键盘用户靠聚焦列表
   （方向键 / PageDown）滚动，滚到底部同样触发自动加载。 */
.special-line-listwrap:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }

/* ⚠️ 卡片必须自成一个包含块：它是第一个在卡片里用 .sr-only（绝对定位）的模块。
   below 停靠下卡片曾经是 position: static，那些 1×1 的盒子会以 .module-zone
   （它本身是 position: absolute）为包含块，逐行累积的溢出逃出横滑条的裁剪，
   把**整个文档**撑宽——实测 390 视口下多出 338px，页面能真的横向滚动。
   现在平台那条 below 规则已把卡片改成 relative（全模块通用），这条特例不再需要；
   留在这里是提醒：**改回 static 会同时打回这个缺陷与棱镜主题的模块卡光学薄边**
   （薄边是 ::after，要靠卡片自己的包含块定位）。
   （container-type 的 layout containment 也提供这个包含块，但显式写上更稳。） */

/* ---------- 头：标题 + 副标题 + 状态 + 同步 / 保存 + 拖拽把手 ---------- */
/* ⚠️ 头 / 筛选 / 底三段都必须 `flex: 0 0 auto`。
   卡片是 flex 纵向且带 max-height：列表一变长（订阅之后必然发生），卡片触顶，
   这几段作为**默认可收缩**的 flex 项会被压扁。筛选行同时带 `overflow-x: auto`
   （另一轴按规范计算为 auto）且 `align-items: center`，被压扁时文字会被
   **上下各裁一半**——就是用户报的「全部来源那一行只剩半个文字」。
   实测（对照页）：不写这条时筛选行 13.5px、clientHeight 12 < scrollHeight 23；
   写上后 35px、clientHeight 34 = scrollHeight 34，不再裁切。 */
.special-line-head { flex: 0 0 auto; flex-wrap: wrap; margin-bottom: 0; }
.special-line-status { display: inline-flex; align-items: center; gap: 6px; margin-left: auto; }

.special-line-pill {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    color: var(--text-muted);
    font-size: 10px;
    white-space: nowrap;
}
.special-line-pill[data-kind="pending"] { color: #a2762c; }
.special-line-pill[data-kind="alert"] { color: #a44c46; }

:root[data-theme="dark"] .special-line-pill[data-kind="pending"] { color: #e0bd7a; }
:root[data-theme="dark"] .special-line-pill[data-kind="alert"] { color: #e9a69d; }
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .special-line-pill[data-kind="pending"] { color: #e0bd7a; }
    :root:not([data-theme="light"]) .special-line-pill[data-kind="alert"] { color: #e9a69d; }
}

.special-line-time-note { color: var(--text-muted); font-size: 10px; }

.special-line-spinner {
    width: 9px;
    height: 9px;
    border: 1.5px solid currentColor;
    border-top-color: transparent;
    border-radius: 50%;
    animation: specialLineSpin .8s linear infinite;
}
@keyframes specialLineSpin { to { transform: rotate(360deg); } }

.special-line-retry {
    padding: 0;
    border: 0;
    background: transparent;
    color: var(--accent);
    font-size: 10px;
    text-decoration: underline;
    cursor: pointer;
}

.special-line-iconbtn {
    flex: 0 0 auto;
    height: 26px;
    padding: 0 9px;
    border: 1px solid var(--border);
    border-radius: 7px;
    background: linear-gradient(180deg, var(--control-top), var(--control-bottom));
    box-shadow: var(--shadow);
    color: var(--text-secondary);
    font-size: 11px;
    line-height: 1;
    cursor: pointer;
    transition: transform 120ms, box-shadow 120ms, color 120ms;
}
.special-line-iconbtn:hover { color: var(--accent); transform: translateY(-1px); box-shadow: var(--shadow-lg); }
.special-line-iconbtn:active { transform: translateY(1px) scale(.985); }
.special-line-iconbtn:focus-visible { outline: 2px solid var(--focus); outline-offset: 1px; }
.special-line-save { border-color: transparent; background: var(--accent); color: #fffaf5; font-weight: 620; }
.special-line-save:hover { background: var(--accent-hover); color: #fffaf5; }

/* ---------- 筛选：按渠道分组（渠道按钮 + 子菜单），放不下就换行 ----------
   ⚠️ 这里**不能有 overflow**：渠道子菜单是绝对定位的下拉，一旦这一行变成
   滚动容器（`overflow-x: auto` 会把另一轴也算成 auto），菜单就会被裁掉。
   之前的扁平 chips 正是靠 `overflow-x: auto` + 隐藏滚动条，结果来源一多，
   最右边的「已归档」被推出可视区且鼠标够不着（实测 8 个来源超出 415px）。 */
.special-line-filters {
    position: relative;
    z-index: 5;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
    padding-bottom: 8px;
    border-bottom: 1px solid var(--border);
    /* 不许被卡片压缩——见上面 .special-line-head 的实测说明 */
    flex: 0 0 auto;
}
.special-line-views { display: flex; gap: 4px; margin-left: auto; }
.special-line-chip {
    display: inline-flex;
    flex: 0 0 auto;
    align-items: center;
    min-height: 26px;
    padding: 0 9px;
    border: 1px solid transparent;
    border-radius: 7px;
    background: transparent;
    color: var(--text-muted);
    font-size: 11px;
    white-space: nowrap;
    cursor: pointer;
}
.special-line-chip:hover { background: var(--bg-hover); color: var(--text); }
.special-line-chip[aria-pressed="true"] {
    border-color: var(--border);
    background: var(--glass-strong);
    color: var(--accent);
    font-weight: 650;
}
.special-line-dot { width: 5px; height: 5px; margin-right: 5px; border-radius: 50%; background: var(--accent); }
.special-line-caret { margin-left: 4px; color: var(--text-muted); font-size: 9px; }

.special-line-chan { position: relative; }
/* 子菜单：桌面端悬停即展开，点开也展开（触屏与键盘只能靠点）。
   菜单是按钮的**子节点**且与它相接（top:100%、无空隙），指针从按钮移到
   菜单上时 :hover 仍然成立，不会闪一下就关。 */
.special-line-menu {
    position: absolute;
    z-index: 6;
    top: 100%;
    left: 0;
    display: none;
    /* 按内容收窄：固定 min-width 会让只有一个短账号名的菜单空出一大截
       （用户原话「下拉框太宽了」）。上下限只兜住极端情况。 */
    width: max-content;
    min-width: 104px;
    max-width: min(240px, 70vw);
    max-height: min(46vh, 320px);
    overflow-y: auto;
    overflow-x: hidden;
    overscroll-behavior: contain;
    padding: 4px;
    border: 1px solid var(--border);
    border-radius: 9px;
    /* ⚠️ 用 `--bg-card` 这个**不透明**表面色，不要用 `--glass-strong`：后者是给
       「同材质卡面上的小菜单」准备的半透明层（深色下只有 6% 白），下拉浮在事件
       列表上时底下的正文会透上来、读起来发糊。`--bg-card` 在四个主题块里都是
       不透明色（#ffffff / #161616 / #faf8f4 / #3a3530），实测浅色与深色下
       计算值都不带 alpha。 */
    background: var(--bg-card);
    box-shadow: var(--shadow-lg);
}
.special-line-chan:hover .special-line-menu,
.special-line-chan[data-open="1"] .special-line-menu { display: block; }
.special-line-menu button {
    display: block;
    width: 100%;
    min-height: 30px;
    padding: 0 9px;
    border: 0;
    border-radius: 6px;
    background: transparent;
    color: var(--text-secondary);
    font-size: 11px;
    text-align: left;
    white-space: nowrap;
    /* 账号名过长时截断，而不是把菜单撑到 max-width 再溢出 */
    overflow: hidden;
    text-overflow: ellipsis;
    cursor: pointer;
}
.special-line-menu button:hover { background: var(--bg-hover); color: var(--text); }
.special-line-menu button[aria-checked="true"] { color: var(--accent); font-weight: 650; }
.special-line-menu button:focus-visible { outline: 2px solid var(--focus); outline-offset: 1px; }

/* ---------- 列表（自身滚动） ---------- */
.special-line-listwrap {
    position: relative;
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    overscroll-behavior: contain;
    scrollbar-width: thin;
}
.special-line-listwrap.is-filtering .special-line-list {
    opacity: .42;
    pointer-events: none;
    transition: opacity 120ms ease;
}
.special-line-filtering {
    position: absolute;
    z-index: 5;
    top: 8px;
    right: 8px;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 5px 8px;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: var(--glass-strong);
    box-shadow: var(--shadow);
    color: var(--text-muted);
    font-size: 10px;
    pointer-events: none;
}
@media (prefers-reduced-motion: reduce) {
    .special-line-listwrap.is-filtering .special-line-list { transition: none; }
}

.special-line-list {
    position: relative;
    margin: 0;
    padding: 4px 0 8px;
    list-style: none;
}
/* 贯穿节点线：画在列表上而不是每个 item 上——items 高度不一，逐段画会断开 */
.special-line-list::before {
    position: absolute;
    top: 12px;
    bottom: 18px;
    width: 1px;
    background: linear-gradient(var(--border), rgba(var(--divider-rgb), .27) 88%, transparent);
    content: "";
}

.special-line-item { position: relative; }
.special-line-item::before {
    position: absolute;
    z-index: 1;
    width: 9px;
    height: 9px;
    border: 2px solid var(--bg-card);
    border-radius: 50%;
    background: var(--accent);
    content: "";
}
.special-line-time { color: var(--text-muted); font-size: 10px; font-variant-numeric: tabular-nums; }
.special-line-time strong { color: var(--text-secondary); font-weight: 570; }

.special-line-event {
    min-width: 0;
    padding: 10px 11px 9px;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--glass-strong);
    box-shadow: var(--shadow);
    transition: transform 150ms, box-shadow 150ms;
}
.special-line-event:hover { transform: translateY(-1px); box-shadow: var(--shadow-lg); }

.special-line-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-bottom: 4px; color: var(--text-muted); font-size: 9px; }
.special-line-mark { display: inline-flex; align-items: center; gap: 4px; color: var(--text-secondary); font-weight: 630; }

/* ---------- 作者行（像推特一样）：头像 + 显示名 + @handle ---------- */
.special-line-author { min-width: 0; max-width: 100%; }
.special-line-avatar {
    position: relative;
    display: grid;
    flex: 0 0 auto;
    width: 20px;
    height: 20px;
    place-items: center;
    overflow: hidden;
    border: 1px solid var(--border);
    border-radius: 50%;
    background: var(--bg-card);
    color: var(--accent);
    font-size: 9px;
    font-weight: 700;
}
/* 头像是外站图片：加载失败时 `onerror` 把它摘掉，底下的首字母就露出来 */
.special-line-avatar img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.special-line-author-name { overflow: hidden; color: var(--text); font-size: 11px; text-overflow: ellipsis; white-space: nowrap; }
.special-line-handle { flex: 0 0 auto; color: var(--text-muted); font-size: 10px; font-weight: 400; }
.special-line-badge {
    display: inline-block;
    margin-left: 4px;
    padding: 0 4px;
    border: 1px solid var(--border);
    border-radius: 5px;
    color: var(--text-muted);
    font-size: 9px;
}
.special-line-icon {
    display: inline-grid;
    width: 17px;
    height: 17px;
    place-items: center;
    border: 1px solid var(--border);
    border-radius: 5px;
    background: var(--bg-card);
    color: var(--accent);
    font-size: 9px;
    font-weight: 700;
}
.special-line-type { color: var(--text-muted); font-size: 9px; }

/* 右上角：归档按钮（替代原来的「⋯」菜单，菜单那套机件已整条删除）。
   归档的语义不变：从默认视图与「收藏」里收起，只在「已归档」筛选可见；
   它**不**豁免 30 天清理——豁免的是收藏。 */
.special-line-archive {
    margin-left: auto;
    min-height: 22px;
    padding: 0 8px;
    border: 1px solid var(--border);
    border-radius: 6px;
    background: transparent;
    color: var(--text-muted);
    font-family: inherit;
    font-size: 9px;
    line-height: 1;
    cursor: pointer;
}
.special-line-archive:hover { background: var(--bg-hover); color: var(--text-secondary); }
.special-line-archive:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.special-line-archive[aria-pressed="true"] {
    color: var(--accent);
    border-color: color-mix(in srgb, var(--accent) 35%, transparent);
}

/* 卡片底部一行：左是「查看原文 / 展开全文」，右是收藏。
   ⚠️ 这一行**始终存在**（左侧没有按钮时也留着），否则收藏按钮没有落脚点。
   ⚠️ 名字不能叫 `.special-line-foot`——那是**卡片级页脚**（30 天提示）在用。 */
.special-line-item-foot { display: flex; align-items: center; gap: 6px; margin-top: 6px; }
.special-line-item-foot .special-line-row-actions { margin-top: 0; }

/* 右下角：收藏星标。**一个** SVG，实心/线框由 aria-pressed 驱动——
   不按状态重写按钮内容（那会把按钮上别的东西一起抹掉）。 */
.special-line-star {
    margin-left: auto;
    display: grid;
    place-items: center;
    width: 26px;
    height: 26px;
    padding: 0;
    border: 0;
    border-radius: 6px;
    background: transparent;
    color: var(--text-muted);
    cursor: pointer;
}
.special-line-star:hover { background: var(--bg-hover); color: var(--text-secondary); }
.special-line-star:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.special-line-star svg {
    width: 15px;
    height: 15px;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.7;
    stroke-linejoin: round;
}
.special-line-star[aria-pressed="true"] { color: var(--accent); }
.special-line-star[aria-pressed="true"] svg { fill: currentColor; stroke-width: 0; }

.special-line-event h3 { margin: 0 0 6px; font-size: 13px; line-height: 1.45; font-weight: 610; overflow-wrap: anywhere; }
.special-line-title { color: inherit; text-decoration: none; }
.special-line-title:hover { color: var(--accent); text-decoration: underline; text-underline-offset: 3px; }
.special-line-event p { margin: 0; color: var(--text-secondary); font-size: 11px; line-height: 1.65; overflow-wrap: anywhere; }
/* 长文折叠：默认 6 行，渲染后量过确实溢出才显示「展开全文」 */
.special-line-body.is-clamped {
    display: -webkit-box;
    -webkit-line-clamp: var(--clamp-lines, 6);
    -webkit-box-orient: vertical;
    overflow: hidden;
}
.special-line-row-actions { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; }
.special-line-ghost {
    min-height: 26px;
    padding: 0 8px;
    border: 1px solid var(--border);
    border-radius: 6px;
    background: transparent;
    color: var(--text-secondary);
    font-size: 10px;
    cursor: pointer;
}
.special-line-ghost:hover { color: var(--accent); }
.special-line-ghost:focus-visible { outline: 2px solid var(--focus); outline-offset: 1px; }
.special-line-event blockquote {
    margin: 8px 0 0;
    padding: 7px 10px;
    border-left: 2px solid color-mix(in srgb, var(--accent) 62%, transparent);
    border-radius: 0 6px 6px 0;
    background: color-mix(in srgb, var(--bg-hover) 72%, transparent);
    color: var(--text-secondary);
    font-size: 10px;
    line-height: 1.6;
}
.special-line-stamp { margin-top: 5px; color: var(--text-muted); font-size: 9px; font-variant-numeric: tabular-nums; }

/* ---------- 宽列（≥360px）：仿真里的桌面版——日期在左、事件卡在右 ----------
   ⚠️ 阈值要按**内容盒**算，不是卡片的外框：容器查询量的是内容盒，而卡片有
   2×13px 内边距 + 2×1px 边框，所以 RAIL_MIN=400 那一档实际只有 372px 参与匹配
   ——阈值写 400（甚至 390）都会让它误判成窄版（实测：1280 视口下卡片 400 却
   渲染成窄版）。360 之下是手机竖屏那种整行卡片（约 323px），正好落在窄版。 */
@container (min-width: 360px) {
    /* 时间轨 44px：实测最宽的日期「11月30日」在 10px 字号下需要 43px，
       再窄就会折行。节点线随之从 64 移到 60——圆点（9px 本体 + 2px 描边）
       两侧要同时留出与时间列、与事件卡的空档，所以只能跟着缩 4px，
       再窄就会贴上时间文字。 */
    .special-line-list::before { left: 60px; }
    .special-line-item { display: grid; grid-template-columns: 44px minmax(0, 1fr); gap: 32px; padding: 8px 0 14px; }
    .special-line-item::before { top: 24px; left: 60px; transform: translate(-50%, -50%); }
    .special-line-time { min-width: 0; padding-top: 16px; text-align: right; white-space: nowrap; }
    .special-line-time strong { display: block; margin-bottom: 3px; font-size: 10px; }
    .special-line-stamp { display: none; }   /* 桌面版时间在左轨道上，卡内不重复 */
}

/* ---------- 窄列（<360px）：仿真 ≤700px 那套——时间移到卡上方、竖线贴左、单列 ---------- */
@container (max-width: 359.98px) {
    .special-line-list::before { left: 10px; }
    .special-line-item { display: block; padding: 8px 0 14px; }
    .special-line-item::before { top: 30px; left: 5px; }
    /* <time> 默认是行内元素：不显式 block，它的 padding 盒会压到下面那张卡的上沿 */
    .special-line-time { display: block; padding: 6px 0 6px 24px; }
    .special-line-time strong { display: inline; margin-right: 6px; }
    .special-line-event { margin-left: 24px; }
    /* 窄列那一行留给状态与两个按钮 */
}

/* ---------- 提示 / 空态 / 骨架 / 底 ---------- */
.special-line-notice {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 6px;
    margin-bottom: 8px;
    padding: 8px 10px;
    border: 1px solid color-mix(in srgb, var(--sl-warning) 34%, var(--border));
    border-radius: 8px;
    background: color-mix(in srgb, var(--sl-warning) 8%, var(--glass));
    color: var(--text-secondary);
    font-size: 10px;
    line-height: 1.55;
}
.special-line-notice strong { color: var(--sl-warning); }

.special-line-empty { padding: 22px 12px; color: var(--text-muted); text-align: center; font-size: 11px; line-height: 1.7; }
.special-line-empty p { margin: 0 0 8px; }

.special-line-skeleton {
    height: 58px;
    margin-bottom: 8px;
    border-radius: 9px;
    background: linear-gradient(90deg, var(--glass), var(--bg-hover), var(--glass));
    background-size: 200% 100%;
    animation: specialLineShimmer 1.5s ease-in-out infinite;
}
@keyframes specialLineShimmer { to { background-position: -200% 0; } }

/* 底部：居中的「加载更早事件」按钮（键盘路径）+ 一行说明。
   没有更多时只留说明：「已到最早一条 · 仅保留最近 30 天（收藏过的除外）」。 */
.special-line-foot {
    display: flex;
    /* 同 head / filters：不许被卡片压缩 */
    flex: 0 0 auto;
    flex-direction: column;
    align-items: center;
    gap: 7px;
    padding-top: 9px;
    border-top: 1px solid var(--border);
}
.special-line-footnote { color: var(--text-muted); font-size: 10px; text-align: center; }
/* 懒加载哨兵：必须放在滚动容器**内部**，observer 的 root 才是它 */
.special-line-sentinel { height: 1px; }
/* 卡片里的实体按钮。仿真底部区那几种状态各有一个真按钮（正常「加载更早事件」、
   筛选无结果「清除筛选」、空状态实心的「＋ 保存文章」），早先都写成了下划线小链接。 */
.special-line-btn {
    min-height: 34px;
    padding: 0 16px;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: linear-gradient(180deg, var(--control-top), var(--control-bottom));
    box-shadow: var(--shadow);
    color: var(--text-secondary);
    font-size: 11px;
    cursor: pointer;
    transition: transform 120ms, box-shadow 120ms, color 120ms;
}
.special-line-btn:hover { transform: translateY(-1px); box-shadow: var(--shadow-lg); color: var(--text); }
.special-line-btn:active { transform: translateY(1px) scale(.985); }
.special-line-btn:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
/* 空状态那个「＋ 保存文章」在仿真里是实心主按钮 */
.special-line-btn--primary {
    border-color: transparent;
    background: linear-gradient(180deg, #8a5a3c, #6d4327);
    box-shadow: 0 1px 2px rgba(0,0,0,.18), inset 0 1px 0 rgba(255,255,255,.18);
    color: #fff;
    font-weight: 600;
}
.special-line-btn--primary:hover { color: #fff; }

/* ---------- 保存文章对话框：复用平台 .ui-dialog-overlay / .ui-dialog（见模块文件） ---------- */
.special-line-dialog-hint { margin: 0 0 14px; color: var(--text-muted); font-size: 11px; line-height: 1.65; }

.special-line-form { display: grid; gap: 12px; }
.special-line-form label { display: grid; gap: 6px; color: var(--text-secondary); font-size: 12px; }
.special-line-form input,
.special-line-form textarea,
.special-line-form select {
    width: 100%;
    min-height: 42px;
    padding: 9px 10px;
    border: 1px solid var(--border);
    border-radius: 7px;
    background: var(--bg-card);
    color: var(--text);
    /* 16px 是 iOS 的底线：小一号，聚焦时整个页面会被放大 */
    font-size: 16px;
}
.special-line-form textarea { min-height: 84px; max-height: 220px; resize: vertical; }
.special-line-form details summary { padding: 6px 0; color: var(--text-muted); font-size: 12px; cursor: pointer; }
.special-line-form-error { margin: 0; color: var(--sl-danger); font-size: 11px; }
.special-line-form-actions { display: flex; justify-content: flex-end; flex-wrap: wrap; gap: 8px; }
.special-line-form-actions .btn { min-height: 40px; }

@media (prefers-reduced-motion: reduce) {
    .special-line-spinner,
    .special-line-skeleton { animation: none; }
}

/* ---------- 窄屏（视口 ≤700px）：触控目标 ≥44px ---------- */
@media (max-width: 700px) {
    .special-line-card { max-height: min(84vh, 780px); }
    .special-line-head { gap: 6px; }
    /* 44px 是仿真样例定的触控下限（与 .filter-button / .timeline-action 一致）：
       窄屏上手指就是指针，低于它的按钮点了没反应，用户只会以为坏了。 */
    .special-line-chip { min-height: 44px; padding: 0 12px; }
    .special-line-menu button { min-height: 44px; font-size: 12px; }
    .special-line-iconbtn { height: 44px; padding: 0 11px; }
    /* 归档与收藏同属窄屏手指操作，也要到 44px 触控下限（26px 的星标点不中）。
       星标靠 min-height 撑高、宽度一并给到 44px；归档按钮加高加大字号。 */
    .special-line-archive { min-height: 44px; padding: 0 12px; font-size: 12px; }
    .special-line-star { width: 44px; height: 44px; }
    .special-line-star svg { width: 18px; height: 18px; }
    .special-line-event h3 { font-size: 14px; }
    .special-line-event p { font-size: 12px; }
    .special-line-btn { min-height: 44px; font-size: 12px; }
    /* 「查看原文 / 展开全文」同属窄屏手指操作，也必须到 44px */
    .special-line-ghost { min-height: 44px; font-size: 12px; }
    .special-line-form-actions .btn { min-height: 44px; flex: 1 1 auto; }
}

/* 首页编辑抽屉，展开与收起不改变草稿及保存语义。 */
.local-navigation-tools { margin-top: 24px; font-size: 13px; }
.local-settings-toggle { display: flex; align-items: center; gap: 10px; width: 100%; min-height: 44px; padding: 0; border: 0; background: transparent; color: var(--text-secondary); font: inherit; font-size: 12px; cursor: pointer; }
.local-settings-toggle:hover { color: var(--text); }
.local-settings-rule { flex: 1; height: 1px; background: var(--border); transition: background 160ms; }
.local-settings-toggle:hover .local-settings-rule { background: var(--text-muted); }
.local-settings-toggle svg, .local-select-button svg { flex: none; width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; transition: transform 220ms cubic-bezier(.16,1,.3,1); }
.local-navigation-tools[data-open="true"] .local-settings-toggle svg { transform: rotate(180deg); }
.local-settings-body { display: grid; grid-template-rows: 1fr; opacity: 1; transition: grid-template-rows 260ms cubic-bezier(.16,1,.3,1),opacity 180ms; }
.local-navigation-tools[data-open="false"] .local-settings-body { grid-template-rows: 0fr; opacity: 0; }
.local-settings-body > form { min-height: 0; overflow: clip; }
.local-settings-grid { display: grid; grid-template-columns: minmax(0,1.25fr) minmax(0,1fr); gap: 24px 32px; padding: 16px 4px 4px; }
.local-settings-group { min-width: 0; }
.local-settings-group > h2 { margin: 0 0 16px; color: var(--text); font-size: 15px; font-weight: 600; }
.local-settings-modules { padding-left: 32px; border-left: 1px solid var(--border); }
.local-settings-span { grid-column: 1 / -1; }
.local-settings-row { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 16px; padding: 5px 0; }
.local-settings-row > label { color: var(--text-secondary); font-size: 12px; }
.local-settings-inline { display: flex; align-items: center; gap: 7px; color: var(--text-secondary); font-size: 12px; min-height: 36px; }
.local-settings-hint { margin: 4px 0 12px; color: var(--text-secondary); font-size: 12px; line-height: 1.6; }
.local-navigation-tools select,
.local-navigation-tools input:not([type="checkbox"]) { width: auto; min-width: 64px; min-height: 36px; padding: 6px 9px; border: 1px solid var(--border); border-radius: 8px; background: var(--bg-card); color: var(--text); font: inherit; transition: border-color 140ms; }
.local-navigation-tools input:not([type="checkbox"]):hover { border-color: var(--focus); }
.local-navigation-tools input[type="checkbox"] { width: 16px; height: 16px; margin: 0; accent-color: var(--accent); }
.local-navigation-tools :is(input,button,summary):focus-visible { outline: 2px solid var(--focus); outline-offset: 3px; }
.local-settings-disclosure { border-top: 1px solid var(--border); padding-top: 10px; }
.local-settings-base > .local-settings-disclosure { margin-top: 16px; }
.local-settings-disclosure summary { display: flex; align-items: center; gap: 7px; min-height: 32px; cursor: pointer; color: var(--text-secondary); font-size: 12px; font-weight: 600; list-style: none; }
.local-settings-disclosure summary::-webkit-details-marker { display: none; }
.local-settings-disclosure summary::after { content: ''; width: 6px; height: 6px; margin-left: auto; margin-right: 4px; border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor; transform: rotate(45deg); transition: transform 160ms; }
.local-settings-disclosure[open] summary { color: var(--text); }
.local-settings-disclosure[open] summary::after { transform: rotate(225deg); }
.local-settings-count { color: var(--text-secondary); font-weight: 400; }
.local-modules-empty { padding-top: 12px; color: var(--text-secondary); font-size: 13px; line-height: 1.6; }
.local-modules-empty span { display: block; margin-top: 6px; font-size: 12px; }

/* 保留 select 作为配置源；自绘按钮与顶层选项面板统一主题、键盘和触控。 */
.local-select { min-width: 0; width: 156px; max-width: 100%; }
.local-select-button { display: flex; align-items: center; justify-content: space-between; gap: 12px; width: 100%; min-height: 36px; padding: 7px 11px; border: 1px solid var(--border); border-radius: 8px; background: linear-gradient(165deg,var(--control-top),var(--control-bottom)); color: var(--text); box-shadow: inset 0 1px var(--inner-light,transparent),0 2px 4px -2px var(--inner-dark,transparent); font: inherit; text-align: left; cursor: pointer; transition: border-color 150ms,background 150ms,box-shadow 150ms; }
.local-select-button > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.local-select-button:hover:not(:disabled),.local-select-button[aria-expanded="true"] { border-color: var(--focus); background: linear-gradient(165deg,var(--control-hover-top),var(--control-hover-bottom)); }
.local-select-button[aria-expanded="true"] svg { transform: rotate(180deg); }
.local-select-button:active:not(:disabled) { box-shadow: var(--press-shadow); }
.local-select-button:disabled { opacity: .5; cursor: not-allowed; }
.local-select-menu { position: fixed; inset: auto; margin: 0; padding: 5px; overflow-y: auto; overscroll-behavior: contain; border: 1px solid var(--border); border-radius: 12px; background: var(--glass-strong); color: var(--text); box-shadow: var(--shadow-lg); backdrop-filter: blur(24px) saturate(150%); -webkit-backdrop-filter: blur(24px) saturate(150%); font: inherit; font-size: 13px; scrollbar-width: thin; scrollbar-color: var(--text-muted) transparent; }
.local-select-menu:popover-open { animation: local-select-enter 150ms cubic-bezier(.16,1,.3,1); }
.local-select-menu [role="option"] { display: flex; align-items: center; justify-content: space-between; gap: 12px; width: 100%; min-height: 36px; padding: 8px 10px; border: 0; border-radius: 7px; background: transparent; color: inherit; font: inherit; text-align: left; cursor: pointer; }
.local-select-menu [role="option"] > span { min-width: 0; overflow-wrap: anywhere; }
.local-select-menu [role="option"] svg { flex: none; width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; visibility: hidden; }
.local-select-menu [aria-selected="true"] { font-weight: 600; }
.local-select-menu [aria-selected="true"] svg { visibility: visible; }
.local-select-menu [role="option"]:is(:hover,.is-active) { background: var(--bg-hover); }
.local-select-menu [role="option"]:disabled { opacity: .5; cursor: not-allowed; }
@keyframes local-select-enter { from { opacity: .6; transform: translateY(-4px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) {
    .local-settings-body,.local-settings-toggle svg,.local-settings-disclosure summary::after,.local-select-button svg { transition: none; }
    .local-select-menu:popover-open { animation: none; }
}

/* 引擎编辑位于导航基础列内；名称、地址与操作分行，长地址不挤压名称。 */
.local-engine-head,
.local-engine-item { display: grid; grid-template-columns: 20px minmax(0, 1fr); align-items: center; gap: 8px; }
.local-engine-head { display: none; padding: 0 0 4px; border-bottom: 1px solid var(--border); color: var(--text-muted); font-size: 11px; }
.local-engine-item { padding: 10px 0; border-bottom: 1px solid var(--border); }
.local-engine-item:last-child { border-bottom: 0; }
.local-engine-item input { width: 100%; min-height: 36px; min-width: 0; }
.local-engine-index { grid-area: 1 / 1; }
.local-engine-name { grid-area: 1 / 2; }
.local-engine-item input[data-field="url"] { grid-area: 2 / 2; }
.local-engine-item .local-engine-actions { grid-area: 3 / 2; }
.local-engine-index { color: var(--text-muted); font-size: 12px; font-variant-numeric: tabular-nums; }
.local-engine-name { display: flex; align-items: center; gap: 6px; min-width: 0; }
.local-engine-name input { flex: 1 1 auto; min-width: 0; }
.local-engine-badge { flex: none; padding: 0 5px; border-radius: 4px; background: var(--bg-hover); color: var(--text-muted); font-size: 11px; line-height: 1.6; }
/* 非默认行留的是空槽（切默认时只改文本，不重绘整表）；空槽不该占宽度。 */
.local-engine-badge:empty { display: none; }
.local-engine-actions { display: flex; gap: 4px; }
/* 图标按钮撑成方形：26×30 的窄条点起来没有形状感，也难和相邻按钮分开 */
.local-engine-actions .btn { min-width: 30px; min-height: 30px; padding: 0 6px; }
/* 数据操作是同一档的并列动作，等宽排满整行——按内容定宽会在右侧留下和刚才同一种死白 */
.local-data-actions { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 6px; }
#localAddEngineBtn { margin-top: 10px; }
.local-settings-error, .local-navigation-feedback [data-state="error"] { color: #b54a3d; font-size: 12px; margin-top: 10px; overflow-wrap: anywhere; }
.local-navigation-feedback { font-size: 12px; }
.local-navigation-feedback [role="status"]:not([hidden]) { margin-top: 10px; }
/* 普通访客的「模块」分组：一行一个公开模块（开关 + 它声明的偏好）。
   开关与偏好贴着排——一个是「用不用」，另一个是「怎么用」。 */
.local-module-list { display: flex; flex-direction: column; gap: 2px; }
.local-module-row { padding: 6px 0; border-bottom: 1px solid var(--border); }
.local-module-row:last-child { border-bottom: 0; }
.local-module-toggle { display: flex; align-items: center; gap: 8px; color: var(--text-secondary); font-size: 12px; cursor: pointer; }
.local-module-title { color: var(--text); font-weight: 600; }
.local-module-settings { display: flex; flex-wrap: wrap; gap: 4px 16px; margin-top: 6px; }
.local-module-setting { display: inline-flex; align-items: center; gap: 6px; color: var(--text-secondary); font-size: 12px; }
/* 上面那条 84px 的 min-width 是给下拉框的；条数是个两位数，收窄一点。 */
.local-module-setting input { min-width: 64px; max-width: 88px; }
#localAdoptBtn { margin-top: 8px; }
@media (max-width: 700px) {
    .local-settings-grid { grid-template-columns: minmax(0,1fr); gap: 22px; }
    .local-settings-modules { padding: 20px 0 0; border-left: 0; border-top: 1px solid var(--border); }
}
@media (max-width: 600px) {
    .local-navigation-tools { margin-top: 20px; }
    /* iOS 在 font-size 小于 16px 的输入控件上聚焦会自动放大整个页面，弹层会被顶出视口。
       本项目其它表单遵守同一条（见 .special-line-form textarea 的注释），这里补上。 */
    .local-select-button,
    .local-navigation-tools input:not([type="checkbox"]) { min-height: 40px; font-size: 16px; }
    .local-navigation-tools button, .local-settings-disclosure summary, .local-select-menu [role="option"] { min-height: 44px; }
    .local-select { width: 148px; }
    .local-select-menu { font-size: 16px; }
    /* 上面那两条把操作压到 30px 的规则权重更高，窄屏必须显式还原到触控下限。 */
    .local-engine-actions .btn { min-height: 44px; }
    .local-engine-head { display: none; }
    .local-engine-item { grid-template-columns: 20px minmax(0, 1fr); row-gap: 6px; padding: 8px 0; }
    .local-engine-item input { min-height: 40px; }
}

/* 首页首屏主题入口，使用同一套明暗控件材质。
   它**不占文档流**：绝对定位到背板右上角，落在背板自己的上内边距里。
   为什么不能放在搜索框上面那一行：背板的上内边距 48px 是被钉死的（见
   homepage-material.test.js「背板上边距还原仿真的纵向节奏」）。主题入口这一行
   有 40px 高 + 18px 下外边距，插在搜索框前面会把搜索框顶从 83px 顶到 141px。
   v2.2.5 引入这个入口时靠 `margin-top: -28px` 把它按回 113px——搜索框因此比
   仿真里的节奏低了 30px，而入口自己悬在搜索框右上角那条缝里，不与任何东西对齐。
   绝对定位同时避开这两件事：入口回到背板右上角，搜索框顶也回到 83px。
   窄屏背板没有上内边距（≤1023px 全部清零），绝对定位会压到搜索框，
   那时退回常规流里的一行。 */
.home-theme-toolbar { position: absolute; top: 4px; right: 30px; display: flex; justify-content: flex-end; margin: 0; }
.home-theme-switcher { display: inline-flex; gap: 3px; padding: 3px; border: 1px solid var(--border); border-radius: 999px; background: linear-gradient(165deg,var(--control-pressed-top),var(--control-pressed-bottom)); }
.home-theme-switcher button { min-width: 68px; min-height: 32px; padding: 5px 15px; border: 1px solid transparent; border-radius: 999px; background: transparent; color: var(--text-secondary); font: inherit; font-size: 12px; font-weight: 600; cursor: pointer; transition: background 160ms,color 160ms,border-color 160ms; }
.home-theme-switcher button:hover:not(:disabled) { color: var(--text); background: var(--bg-hover); }
.home-theme-switcher button[aria-pressed="true"] { color: var(--text); border-color: var(--border); background: linear-gradient(165deg,var(--control-top),var(--control-bottom)); box-shadow: inset 0 1px var(--inner-light,transparent),0 2px 4px -2px var(--inner-dark,transparent); }
.home-theme-switcher button:focus-visible { outline: 2px solid var(--focus); outline-offset: 3px; }
.home-theme-switcher button:disabled { opacity: .5; cursor: not-allowed; }
@media (max-width: 1023px) { .home-theme-toolbar { position: static; margin: 0 0 20px; } }
@media (max-width: 600px) { .home-theme-switcher button { min-height: 44px; min-width: 72px; } }
@media (prefers-reduced-motion: reduce) { .home-theme-switcher button { transition: none; } }
