/* ============================================================
 * Atlas 设计令牌（Design Tokens）
 * ------------------------------------------------------------
 * 单一来源：所有颜色都用 light-dark(浅, 深) 写一次，深浅色不再各维护一份。
 * 主题切换靠 :root 上的 color-scheme：
 *   [data-theme] 缺省      → light dark（跟随系统）
 *   [data-theme="light"]  → 强制浅色
 *   [data-theme="dark"]   → 强制深色
 * 顺带修掉一个老问题：声明了 color-scheme 之后，原生 checkbox / select /
 * 滚动条会跟着主题走，深色下不再出现刺眼的白色控件。
 * ============================================================ */
:root {
  color-scheme: light dark;

  /* ---------- 表面层级（越往下越"浮"） ----------
   * 取向参考 Linear：中性偏冷的灰，不带蓝味（原来的 #f4f6f9 / #14181f 蓝味偏重，
   * 导致整个界面像"浅蓝色的后台系统"）。深色侧压到接近纯黑，让内容浮起来。
   * 分层主要靠这四级表面 + border，几乎不靠阴影——见下方 shadow 的注释。 */
  /* 「星图 / 任务控制台」配色：深色侧是深空蓝黑（星点在里面发光），浅色侧是星图纸。
   * 四组合（冷/暖 × 浅/深）的对比度由 tests/contrast-tokens.spec.js 逐项验算。 */
  --bg:        light-dark(#f5f7fa, #06080d);   /* canvas：主区域底 / 星图底 */
  --bg-elev:   light-dark(#ffffff, #0b0e15);   /* 面板：侧栏 / 顶栏 / 弹窗 */
  --bg-elev-2: light-dark(#edf0f5, #121620);   /* 控件：输入框 / chip / ghost 按钮 */
  --bg-hover:  light-dark(#e5e9f0, #191e2b);
  --bg-active: light-dark(#e1e7fb, #1b2340);   /* 选中态填充（带一点 accent 味） */

  /* ---------- 描边 ----------
   * border 是这套设计的主要分层手段，所以要淡——"看得出有一条"就够，
   * 一旦重了，密集列表里几十条线会变成栅栏。 */
  --border:        light-dark(#e3e7ee, #171b26);  /* 分隔线（装饰） */
  --border-strong: light-dark(#c5cbd6, #2c3243);  /* 卡片 / 分组轮廓（装饰，非唯一辨识边界） */
  /* 文本输入框轮廓：它是输入框唯一的边界（WCAG 1.4.11），对 --bg-elev / --bg-elev-2 ≥3:1。
   * 输入框背景只能是 --bg-elev / --bg-elev-2，不得用 --bg-hover（对它余量太小） */
  --border-input:  light-dark(#7c8597, #636b7d);
  --hairline:      light-dark(rgba(13, 18, 32, .06), rgba(180, 200, 255, .06));

  /* ---------- 文字：三档 ----------
   * primary 承载内容，dim 承载次要信息（路径 / 时间），faint 用于分区标题 / HUD 标签。
   * 三档都 ≥4.5:1（README 的"小字满足 WCAG AA"）；层级差改由字号、字重、字距承担。 */
  --text:       light-dark(#0d1220, #eef1f7);
  --text-dim:   light-dark(#4d5767, #a3abbc);
  --text-faint: light-dark(#5a6474, #878fa0);

  /* ---------- 主色与语义色 ----------
   * accent 从纯蓝改成紫蓝：一是纯蓝是所有后台系统的默认色，没有辨识度；
   * 二是 brand-mark 的渐变本来就是 #5b9cff→#8b5bff，界面主色偏紫才和品牌自洽。
   * 浅色侧压深到能配白字（对比度 ≈ 6:1），深色侧提亮后必须换近黑文字（见 --on-accent）。 */
  --accent:       light-dark(#5151cf, #8d8df0);
  --accent-hover: light-dark(#4545bf, #a3a3f7);
  --accent-press: light-dark(#3b3ba8, #7a7ade);
  /* accent 的淡填充（标签 chip 选中态这类需要"底色也表态"的地方） */
  --accent-soft:  light-dark(rgba(81, 81, 207, .10), rgba(141, 141, 240, .16));
  --accent-ring:  light-dark(rgba(81, 81, 207, .30), rgba(141, 141, 240, .34));
  /* accent 实心填充上的文字色：浅色主题的蓝够深可以配白字，
   * 深色主题的蓝偏亮，白字只有 2.6:1，必须换成近黑 */
  --on-accent:    light-dark(#ffffff, #08101c);
  /* 未读 = 品牌强调色（新鲜未读满亮度 + 粗体，不只靠颜色）；红色改名 --danger，只表示错误 / 破坏性 */
  --unread: light-dark(#5151cf, #8d8df0);
  --ok:     light-dark(#116d35, #4ade80);
  --warn:   light-dark(#9a4604, #f59e0b);
  /* 错误 / 破坏性操作（删除确认、导出失败、回退、超预算告警）。即原来的红色未读 */
  --danger:        light-dark(#c01427, #ff5b5b);
  /* 危险按钮（实心红底）的 hover 态。浅色要更深、深色要更亮——两个方向都是
   * 为了把按钮上那层文字的对比度往上推，而不是像 brightness() 那样往下拉。 */
  --danger-strong: light-dark(#a01020, #ff7d7d);
  /* 收藏星标：中性色。收藏用填充形状表达，不再用金色（配色收敛：一个强调色 + 中性色） */
  --fav: var(--text-dim);
  /* 控制台"实时"信号色：刚变更的轻闪、在线状态。收敛为强调色 */
  --signal: var(--accent);

  /* 未读点的"陈旧"态：超过 24h 的变更降级用（见 .file.unread.stale）。
   * 和新鲜未读同一色相、更低明度：几百个满亮度点铺满侧栏等于没有信号。
   * 浅色取 #7677cd（比设计稿的 #7c7dd2 略深）：后者对 hover 行 / 选中行只有 2.90–2.98，
   * 这里让它对 bg / bg-elev / bg-hover / bg-active 四种表面冷暖都 ≥3.1 */
  --unread-stale: light-dark(#7677cd, #6668b8);
  /* 实心未读底（计数角标等）的 hover 态，跟随强调色 */
  --unread-strong: light-dark(#4545bf, #a3a3f7);

  /* 渐变文字专用（英雄区大数字）：比 --brand-1/2 压深 / 提亮，保证 34px 大字 ≥3:1。
   * --brand-1/-2/--brand-grad 只留给 logo 方块这类非文字的品牌面 */
  --brand-ink-1: light-dark(#3a6fe0, #5b9cff);
  --brand-ink-2: light-dark(#6b3ff0, #8b5bff);
  --brand-text-grad: linear-gradient(135deg, var(--brand-ink-1), var(--brand-ink-2));

  /* ---------- 星图专用令牌 ----------
   * 新鲜度 = 强调色色相上的明暗四档（浅色侧是墨色浓度，深色侧是亮度）。
   * 不只靠颜色：四档同时对应半径 --star-r-1..4，1–2 档带外晕。 */
  --star-1: light-dark(#2c2fb3, #eeeeff);   /* ≤1h */
  --star-2: light-dark(#3a55ad, #b3b3f6);   /* ≤24h */
  --star-3: light-dark(#5d6c8f, #7f82c4);   /* ≤7d */
  --star-4: light-dark(#7d879b, #5b5f8a);   /* 更早 */
  --star-halo: light-dark(rgba(81, 81, 207, .18), rgba(179, 179, 246, .22));
  --star-pulse: var(--unread);
  --star-twinkle: var(--signal);
  --star-r-1: 3.5px; --star-r-2: 2.75px; --star-r-3: 2px; --star-r-4: 1.5px;
  /* 引用关系连线（星图，FEAT-004 使用）：常态低透明度，悬停 / 选中实色 */
  --link-line:    color-mix(in srgb, var(--accent) 32%, transparent);
  --link-line-hl: var(--accent);
  /* 星云明度参数（非颜色令牌，走下方三处覆盖；浅色是默认值） */
  --nebula-s: 60%;
  --nebula-l: 55%;
  --nebula-a: .10;

  /* 半透明表面（毛玻璃面板 / 桌面版 vibrancy 侧栏）共用的着色比例。
   * 94% 下所有文字令牌、状态点、输入框轮廓叠在纯白 / 纯黑上都达标，无需令牌重映射 */
  --glass-tint: 94%;
  /* 桌面版侧栏叠在原生 vibrancy 材质上的着色比例（浅 / 深各一个，见「桌面版原生化」段）。
   * 材质本身不是纯白 / 纯黑，contrast-tokens.spec 按实测的材质亮度区间验算文字对比度 */
  --sidebar-tint-light: 60%;
  --sidebar-tint-dark: 68%;

  /* 控制台读数用的等宽字体：只用系统字体，不打包、不下载 */
  --font-mono: ui-monospace, "SF Mono", "Cascadia Mono", Menlo, Consolas, monospace;

  /* ---------- 阴影 ----------
   * 取向参考 Linear：分层交给 border 和表面色，阴影只在"真的浮在内容之上"时出现。
   * 所以 shadow-1 几乎为零（卡片不该有阴影），shadow-2 给悬浮小面板，
   * shadow-3 给 modal / ⌘K 这类盖住全屏的东西。 */
  --shadow-1: light-dark(0 1px 2px rgba(16, 24, 40, .04), none);
  --shadow-2: light-dark(0 2px 8px -2px rgba(16, 24, 40, .10), 0 2px 10px -2px rgba(0, 0, 0, .55));
  --shadow-3: light-dark(0 16px 44px -12px rgba(16, 24, 40, .22), 0 22px 60px -12px rgba(0, 0, 0, .72));

  /* ---------- 间距（4px 栅格） ---------- */
  --sp-1: 4px;  --sp-2: 8px;  --sp-3: 12px; --sp-4: 16px;
  --sp-5: 20px; --sp-6: 24px; --sp-8: 32px;

  /* ---------- 圆角：整体收紧一档 ----------
   * 原来 8/12/16 偏圆，配上 13px 小字会显得"软"。Linear 一路是 4/6/8，
   * 小圆角 + 淡 border 才是"工具"的观感而不是"消费级 App"。 */
  --r-xs: 3px; --r-sm: 5px; --r-md: 6px; --r-lg: 10px; --r-xl: 14px; --r-full: 999px;

  /* ---------- 控件尺寸 ---------- */
  --h-ctl-sm: 22px; --h-ctl: 28px; --h-ctl-lg: 32px;

  /* ---------- 字号阶梯 ----------
   * 原来全站 13px 一刀切，这是"层级扁平"的直接原因：KPI 数字、卡片标题、
   * 文件名、路径、时间全都一个重量，眼睛没有落点。这里定死 7 档，
   * 各区域只许从这里取值，不许再写裸数字。 */
  --fs-xs:   11px;   /* 分区标题 / 角标 / 键帽 */
  --fs-sm:   12px;   /* 次要信息：路径、时间、计数 */
  --fs-base: 13px;   /* 主体：文件名、正文控件 */
  --fs-md:   14px;   /* 强调行：卡片标题、品牌名 */
  --fs-lg:   16px;   /* 区块标题 */
  --fs-xl:   20px;   /* 页面标题 */
  --fs-2xl:  30px;   /* KPI 数字 */

  /* ---------- 字重 ----------
   * 用 500/560/620 这种非整档值：600 在 -apple-system 下会跳到偏粗的字面，
   * 密集界面里显得吵；560 能拉开层级又不抢戏。 */
  --fw-normal: 400; --fw-medium: 500; --fw-semi: 560; --fw-bold: 620;

  /* ---------- 行高 ---------- */
  --lh-tight: 1.25; --lh-base: 1.5; --lh-relaxed: 1.7;

  /* ---------- 密度：列表行高 ----------
   * 单行 = 只显示文件名的场合（收藏 / 最近）；两行 = 需要同时给出路径的场合
   * （目录树里的文件行，解决文件名被截断成 `01-user-segment-and...` 的问题）。 */
  --h-row: 28px; --h-row-2: 46px;

  /* ---------- 焦点环：全站唯一一种写法 ----------
   * 实色双环：内圈 --bg-elev 间隙让它压在任何控件色上都看得见，外圈实色 accent
   * 对 --bg-elev 四组合都 ≥6:1（原 30% 透明晕在白底上只有约 1.5:1，不满足 1.4.11） */
  --ring: 0 0 0 2px var(--bg-elev), 0 0 0 4px var(--accent);

  /* 用于 CSS mask 的图标（伪元素里没法插 <svg>，只能走 data-URI） */
  --ico-search: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.4' stroke-linecap='round'%3E%3Ccircle cx='11' cy='11' r='7'/%3E%3Cpath d='m21 21-4.35-4.35'/%3E%3C/svg%3E");

  /* 布局变量（JS 动态改 --sidebar-w） */
  --sidebar-w: 320px;
  --resizer-w: 5px;
  /* ---------- 动效令牌（全站唯一来源） ----------
   * 组件 CSS 不写裸 ms / 裸 cubic-bezier。旧名 --ease / --ease-out 保留为别名。
   * reduced-motion（系统或 html[data-motion="reduce"]）下全部 --dur-* 与 --stagger 归零。 */
  --ease-standard: cubic-bezier(.2, 0, 0, 1);      /* 一般状态变化（颜色、透明度） */
  --ease-enter:    cubic-bezier(.22, 1, .36, 1);   /* 进场 / 展开 */
  --ease-exit:     cubic-bezier(.4, 0, 1, 1);      /* 退场 / 收起：起步慢收尾快 */
  --ease-drawer:   cubic-bezier(.32, .72, 0, 1);   /* 侧栏、抽屉（iOS 抽屉曲线） */
  --ease: var(--ease-drawer);
  --ease-out: var(--ease-enter);
  --dur-instant: 80ms;    /* 按下反馈 */
  --dur-fast: 120ms;      /* hover、颜色 */
  --dur: 200ms;           /* 一般过渡、侧栏 */
  --dur-med: 280ms;       /* 浮窗、视图切换、View Transitions */
  --dur-ambient: 2400ms;  /* 呼吸（持续循环） */
  --dur-twinkle: 1600ms;  /* 刚变更的轻闪（单次，重复 3 次） */
  --stagger: 45ms;        /* 错峰步距 */

  /* ---------- UI 升级迭代新增令牌 ----------
   * 下面这几组是这次「有温度的编辑感」升级引入的：弹簧曲线让 hover / 卡片
   * 浮起带一点生命，品牌渐变从 brand-mark 抽成全站一处，封面色相体系让
   * 793 篇文档凭颜色成堆。都只作用在 chrome / 首页，正文阅读区一律不碰。 */

  /* 弹簧曲线：尾部轻微过冲，用于卡片 hover / 弹出。--dur-slow 给入场动效 */
  --ease-spring: cubic-bezier(.34, 1.56, .64, 1);
  --dur-slow: 420ms;

  /* 品牌渐变：brand-mark 一直是 #5b9cff→#8b5bff，抽成令牌统一来源 */
  --brand-1: #5b9cff;
  --brand-2: #8b5bff;
  --brand-grad: linear-gradient(135deg, var(--brand-1), var(--brand-2));
  /* 英雄区 / 侧栏头背后的极淡光晕，深浅色各一档 */
  --brand-glow: light-dark(rgba(123, 108, 255, .16), rgba(123, 108, 255, .24));

  /* 英雄区大标题字号：比页面标题再大一档，作为首页唯一的视觉落点 */
  --fs-hero: 34px;
}

/* 星云明度参数的深色档：百分比不能塞进 light-dark()（它只接受 <color>），
 * 所以走 prefers-color-scheme + [data-theme] 三处覆盖 */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) { --nebula-s: 55%; --nebula-l: 45%; --nebula-a: .16; }
}
:root[data-theme="dark"]  { --nebula-s: 55%; --nebula-l: 45%; --nebula-a: .16; }
:root[data-theme="light"] { --nebula-s: 60%; --nebula-l: 55%; --nebula-a: .10; }

/* 首页英雄区背后的静态星空（非颜色令牌：整组背景图，同样走三处覆盖）。
 * 浅色 = 星图纸坐标点阵，深色 = 散落星点 + 两团极淡星云。
 * 对比度约束：.home-hero::before 统一 opacity .5，所以这里每一个 rgba 的 alpha × .5
 * 都必须 ≤ .08（最亮星点 alpha ≤ .16），文字落在任何星点上 faint 仍 ≥4.5:1。
 * tests/contrast-tokens.spec.js 会解析这些声明逐个断言。 */
:root {
  --hero-stars:
    radial-gradient(circle, rgba(13, 18, 32, .13) 1px, transparent 1.2px) 0 0 / 22px 22px,
    radial-gradient(600px 260px at 85% -10%, rgba(107, 63, 240, .08), transparent 70%),
    radial-gradient(500px 240px at -5% 120%, rgba(58, 111, 224, .07), transparent 70%);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --hero-stars:
      radial-gradient(1px 1px at 12% 22%, rgba(238, 238, 255, .16), transparent 60%),
      radial-gradient(1px 1px at 28% 68%, rgba(238, 238, 255, .11), transparent 60%),
      radial-gradient(1.4px 1.4px at 41% 34%, rgba(238, 238, 255, .16), transparent 60%),
      radial-gradient(1px 1px at 56% 82%, rgba(179, 179, 246, .13), transparent 60%),
      radial-gradient(1px 1px at 63% 18%, rgba(238, 238, 255, .12), transparent 60%),
      radial-gradient(1.2px 1.2px at 74% 52%, rgba(179, 179, 246, .15), transparent 60%),
      radial-gradient(1px 1px at 86% 28%, rgba(238, 238, 255, .11), transparent 60%),
      radial-gradient(1px 1px at 92% 76%, rgba(238, 238, 255, .10), transparent 60%),
      radial-gradient(1px 1px at 6% 84%, rgba(238, 238, 255, .09), transparent 60%),
      radial-gradient(1px 1px at 35% 8%, rgba(238, 238, 255, .10), transparent 60%),
      radial-gradient(1px 1px at 48% 58%, rgba(238, 238, 255, .08), transparent 60%),
      radial-gradient(1px 1px at 19% 45%, rgba(179, 179, 246, .10), transparent 60%),
      radial-gradient(600px 260px at 85% -10%, rgba(139, 91, 255, .16), transparent 70%),
      radial-gradient(500px 240px at -5% 120%, rgba(91, 156, 255, .12), transparent 70%);
  }
}
:root[data-theme="dark"] {
  --hero-stars:
    radial-gradient(1px 1px at 12% 22%, rgba(238, 238, 255, .16), transparent 60%),
    radial-gradient(1px 1px at 28% 68%, rgba(238, 238, 255, .11), transparent 60%),
    radial-gradient(1.4px 1.4px at 41% 34%, rgba(238, 238, 255, .16), transparent 60%),
    radial-gradient(1px 1px at 56% 82%, rgba(179, 179, 246, .13), transparent 60%),
    radial-gradient(1px 1px at 63% 18%, rgba(238, 238, 255, .12), transparent 60%),
    radial-gradient(1.2px 1.2px at 74% 52%, rgba(179, 179, 246, .15), transparent 60%),
    radial-gradient(1px 1px at 86% 28%, rgba(238, 238, 255, .11), transparent 60%),
    radial-gradient(1px 1px at 92% 76%, rgba(238, 238, 255, .10), transparent 60%),
    radial-gradient(1px 1px at 6% 84%, rgba(238, 238, 255, .09), transparent 60%),
    radial-gradient(1px 1px at 35% 8%, rgba(238, 238, 255, .10), transparent 60%),
    radial-gradient(1px 1px at 48% 58%, rgba(238, 238, 255, .08), transparent 60%),
    radial-gradient(1px 1px at 19% 45%, rgba(179, 179, 246, .10), transparent 60%),
    radial-gradient(600px 260px at 85% -10%, rgba(139, 91, 255, .16), transparent 70%),
    radial-gradient(500px 240px at -5% 120%, rgba(91, 156, 255, .12), transparent 70%);
}
:root[data-theme="light"] {
  --hero-stars:
    radial-gradient(circle, rgba(13, 18, 32, .13) 1px, transparent 1.2px) 0 0 / 22px 22px,
    radial-gradient(600px 260px at 85% -10%, rgba(107, 63, 240, .08), transparent 70%),
    radial-gradient(500px 240px at -5% 120%, rgba(58, 111, 224, .07), transparent 70%);
}

:root[data-theme="light"] { color-scheme: light; }
:root[data-theme="dark"]  { color-scheme: dark; }

/* 弹簧曲线：支持 linear() 的浏览器用真正的弹簧采样，否则用上面的三次贝塞尔近似 */
@supports (transition-timing-function: linear(0, 1)) {
  :root {
    --ease-spring: linear(0, .009, .035 2.1%, .141, .281 6.7%, .723 12.9%, .938 16.7%, 1.017, 1.077, 1.121, 1.149 24.3%, 1.159, 1.163, 1.161, 1.154 29.9%, 1.129 32.8%, 1.051 39.6%, 1.017 43.1%, .991, .977 51%, .974 53.8%, .975 57.1%, .997 69.8%, 1.003 76.9%, 1.004 83.8%, 1);
  }
}

/* reduced-motion：系统偏好与应用内开关（html[data-motion="reduce"]）走同一组归零。
 * 新增动画只写在下方「运动需授权」块里，这里兜住所有引用时长令牌的旧过渡 */
@media (prefers-reduced-motion: reduce) {
  :root {
    --dur-instant: 0ms; --dur-fast: 0ms; --dur: 0ms; --dur-med: 0ms; --dur-slow: 0ms;
    --dur-ambient: 0ms; --dur-twinkle: 0ms; --stagger: 0ms;
  }
}
:root[data-motion="reduce"] {
  --dur-instant: 0ms; --dur-fast: 0ms; --dur: 0ms; --dur-med: 0ms; --dur-slow: 0ms;
  --dur-ambient: 0ms; --dur-twinkle: 0ms; --stagger: 0ms;
}

/* 全站统一焦点可见性：键盘导航一定看得见，鼠标点击不出现难看的框。
 * 各组件如需自定义（例如挤不出 3px 环的 18px 小按钮）再单独覆盖。 */
:where(button, a, input, select, textarea, [tabindex]):focus-visible {
  outline: none;
  box-shadow: var(--ring);
}

/* ---------- 图标基类 ---------- */
/* 全部图标都是 24×24 线性 sprite（见 index.html 顶部的 <symbol>）。
 * 描边宽度按 1.75/24 归一，所有尺寸下视觉重量一致。 */
.ico {
  display: block;
  flex: none;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.75;
  stroke-linecap: round;
  stroke-linejoin: round;
  /* 图标要么继承文字色，要么由父级 color 决定，绝不自带颜色 */
  color: inherit;
}
/* 图标容器：统一居中，不受 line-height 影响 */
.recent-icon, .fav-icon, .dir-icon, .toast-icon,
.folder-icon, .file-type-icon, .folder-toggle, .qo-icon, .quickopen-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  height: 100%;
  font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Helvetica Neue", Helvetica, Arial, sans-serif;
  font-size: var(--fs-base);
  background: var(--bg);
  color: var(--text);
  /* 密集小字界面必须开这两项：不开的话 13px 中英混排的字距会忽宽忽窄 */
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

body {
  height: 100vh;
  overflow: hidden;
}

/* sidebar：fixed + transform 滑出（GPU 加速） */
.sidebar {
  position: fixed;
  left: 0; top: 0; bottom: 0;
  width: var(--sidebar-w);
  display: flex;
  flex-direction: column;
  background: var(--bg-elev);
  border-right: 1px solid var(--border);
  z-index: 10;
  transform: translate3d(0, 0, 0);
  will-change: transform;
  /* 默认无 transition；DOM ready 后由 .tx-ready 启用 */
}
body.tx-ready .sidebar { transition: transform var(--dur) var(--ease); }
body.sidebar-collapsed .sidebar { transform: translate3d(-100%, 0, 0); }

/* resizer：fixed，用 left 跟随 sidebar 宽度 */
.resizer {
  position: fixed;
  top: 0; bottom: 0;
  left: var(--sidebar-w);
  width: var(--resizer-w);
  cursor: col-resize;
  z-index: 11;
  background: transparent;
}
body.tx-ready .resizer {
  transition: left var(--dur) var(--ease), opacity var(--dur-fast);
}
.resizer::before {
  content: '';
  position: absolute;
  inset: 0 2px;
  background: var(--border);
  opacity: 0;
  transition: opacity var(--dur-fast);
}
.resizer:hover::before,
.resizer.dragging::before { opacity: 1; }
body.sidebar-collapsed .resizer {
  left: 0;
  opacity: 0;
  pointer-events: none;
}
body.resizing { cursor: col-resize !important; user-select: none; }
body.resizing .sidebar,
body.resizing .resizer,
body.resizing .main { transition: none !important; }
/* 拖 resizer 时屏蔽 iframe 接收 mousemove，避免抢光标——但保留滚轮事件 */
body.resizing iframe { pointer-events: none; }
/* sidebar 切换动画期间不再屏蔽 iframe：滚动是用户最可能立即想做的事，
   屏蔽会被误判为"卡住"。动画 200ms 内即便用户点到 iframe 内链接也无伤大雅。 */

.sidebar-header {
  padding: 0 10px 0 12px;
  height: 46px;   /* 和 .main-header 对齐，两边顶栏在一条水平线上 */
  border-bottom: 1px solid var(--border);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}

/* 品牌区是个 <button>（回到首页），所以要先把浏览器默认按钮样式清干净。
 * 视觉上仍然只是 logo + 字，靠 hover 才透出"可点" */
.brand {
  display: flex; align-items: center; gap: 10px; min-width: 0;
  appearance: none;
  border: none;
  background: transparent;
  margin: 0;
  padding: 3px 6px 3px 3px;
  margin-left: -3px;          /* 抵掉 padding，logo 左缘回到原位 */
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
  border-radius: var(--r-md);
  transition: background var(--dur-fast);
}
/* hover 用半透明品牌色晕染，而不是不透明中性灰——侧栏头带一层紫调品牌渐变，
 * 灰块盖上去会发脏、突兀。半透明品牌色晕在深浅色 / 冷暖皮肤上都自然融入。 */
.brand:hover { background: color-mix(in srgb, var(--accent) 9%, transparent); }
.brand:hover .brand-title { color: var(--accent); }
/* logo 轻轻一顶：hover 时品牌方块微放大 + 侧倾，作为"点这里回首页"的俏皮暗示 */
.brand:hover .brand-mark { transform: scale(1.08) rotate(-3deg); }
.brand:focus-visible { outline: none; box-shadow: var(--ring); }
/* 侧栏拖窄后品牌区与操作组排不下：先收起「Atlas」字样只留图标（容器查询按侧栏头内容区宽度，
 * 浏览器 / 桌面红绿灯内边距 / 全屏同一套阈值）。图标 + 操作组的最窄宽度由 app.js sidebarMinWidth()
 * 兜住，拖不到更窄。阈值 = 品牌区全宽 80 + 间距 8 + 操作组 126；字体回退略宽时 overflow 兜底，只裁不压 */
.sidebar-header { container: sbhead / inline-size; }
.brand-text { min-width: 0; overflow: hidden; white-space: nowrap; }
@container sbhead (max-width: 213.98px) {
  .brand-text { display: none; }
}
.brand-mark {
  width: 28px; height: 28px; border-radius: 8px;
  background: linear-gradient(135deg, #5b9cff, #8b5bff);
  color: #fff; font-weight: 700;
  display: grid; place-items: center;
  font-size: 14px;
  flex: none;
}
/* 品牌小图标（堆叠文档 + 未读点），与 App 图标同款；渐变方块由 .brand-mark / .home-logo 提供 */
.brand-glyph { display: block; width: 100%; height: 100%; }
.brand-title {
  display: block;
  font-weight: 650; letter-spacing: 0.2px; font-size: 14px;
  transition: color var(--dur-fast);
}

.actions { display: flex; gap: 2px; flex: none; }

/* ---------- 工具条按钮（ghost） ----------
 * 侧栏头部与顶栏的图标按钮统一走 ghost：无边框、无填充，靠 hover 底色表态。
 * 原来每个按钮一圈边框，一行 10 个就是 10 个方块在互相打架——那是顶栏噪音的
 * 主要来源。真正需要"看起来像控件"的是弹窗里的文字按钮，它们保留边框。 */
.actions button,
.main-actions > button {
  height: var(--h-ctl);
  min-width: var(--h-ctl);
  padding: 0 7px;
  border: 1px solid transparent;
  background: transparent;
  color: var(--text-dim);
  border-radius: var(--r-sm);
  cursor: pointer;
  font-size: 13px;
  line-height: 1;
  transition: background var(--dur-fast), transform var(--dur-fast) var(--ease-spring),
              border-color var(--dur-fast), color var(--dur-fast);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 5px;
}
.actions button:hover:not(:disabled),
.main-actions > button:hover:not(:disabled) {
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  color: var(--accent);
  transform: scale(1.06);
}
/* ---------- 弹窗里的文字按钮：保留实体控件外观 ----------
 * 用 :not(:where(...)) 把三类"不是控件"的按钮排除掉：关闭 ✕、分区导航项、
 * 快速打开的结果行。它们本身是 ghost / 列表行，被这条规则套上边框和填充后
 * 会变成一格格的方块（快速打开列表里最明显：每一行都被画成一个按钮）。
 * 用 :where() 包住是为了不抬高整条选择器的优先级。 */
.modal button:not(:where(.modal-close, .settings-nav-item, .quickopen-item, .qo-section-act, .settings-changelog-btn, .settings-update-btn)) {
  height: var(--h-ctl);
  min-width: var(--h-ctl);
  padding: 0 10px;
  border: 1px solid var(--border-strong);
  background: var(--bg-elev-2);
  color: var(--text);
  border-radius: var(--r-sm);
  cursor: pointer;
  font-size: 12.5px;
  line-height: 1;
  transition: background var(--dur-fast), transform 80ms ease,
              border-color var(--dur-fast), color var(--dur-fast);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 5px;
}
.modal button:not(:where(.modal-close, .settings-nav-item, .quickopen-item, .qo-section-act, .settings-changelog-btn, .settings-update-btn)):hover:not(:disabled) {
  background: var(--bg-hover);
  border-color: var(--text-faint);
}
.actions button:active:not(:disabled),
.main-actions > button:active:not(:disabled),
.modal button:not(:where(.modal-close, .settings-nav-item, .quickopen-item, .qo-section-act, .settings-changelog-btn, .settings-update-btn)):active:not(:disabled),
.toggle-sidebar:active { transform: scale(0.95); }
.main-actions > button:disabled { opacity: 0.32; cursor: not-allowed; }
.actions button:disabled { opacity: 0.32; cursor: not-allowed; }

/* 工具条分组分隔线：1px 竖线，比 gap 更明确地表达"这几个是一类" */
.tb-sep {
  flex: none;
  width: 1px;
  height: 16px;
  margin: 0 5px;
  background: var(--border-strong);
  border-radius: 1px;
}
/* 首页态（没有打开任何文档）：整条文档工具栏直接收起，而不是把 10 个
 * 32% 透明的灰图标摆在那儿。一排"看起来坏掉"的按钮比没有按钮更糟——
 * 用户第一眼会以为功能失效，而它们其实只是没有作用对象。
 * 只隐藏 :disabled 的那些，match-badge / update-badge 这类状态指示不受影响。
 *
 * 判据是 body.no-active-doc，由 setActiveFile / goHome 维护。原来写的是
 * :has(#btn-edit:disabled)——"编辑按钮不可用"当时确实等价于"没打开文档"，
 * 但只读格式（csv / json / txt / svg）进来之后就不成立了：那些文档的编辑按钮
 * 本来就禁用，于是打开一篇 CSV 会让编辑按钮凭空消失，而不是变灰。 */
body.no-active-doc .main-actions > button:disabled,
body.no-active-doc .tb-sep { display: none; }

/* 快速打开胶囊（design §10.7）：首页 / 星图态的 ⌘K 可见入口；文档态隐藏，仍靠快捷键。
 * 选择器带 .main-actions > 才压得过上面 ghost 按钮的 padding / border / hover 缩放 */
.main-actions > button.qo-capsule {
  height: 30px;
  padding: 0 6px 0 10px;
  gap: 8px;
  border: 1px solid var(--border-strong);
  border-radius: 7px;
  color: var(--text-dim);
  font-size: 12px;
}
.main-actions > button.qo-capsule:hover:not(:disabled) { transform: none; }
.qo-capsule kbd { color: inherit; }
body:not(.no-active-doc) .main-actions > button.qo-capsule,
html.focus-mode .main-actions > button.qo-capsule { display: none; }

.actions button.scanning svg,
.main-actions button.spinning svg {
  animation: spin 0.9s linear infinite;
}

/* 排序与「仅未读」在树头 #btn-sort 的菜单里（见「侧栏瘦身」段） */
@keyframes spin {
  to { transform: rotate(360deg); }
}

.filter {
  padding: 10px 12px;
  border-bottom: 1px solid var(--border);
  display: flex;
  align-items: center;
  gap: 8px;
}
.filter input[type="search"] {
  flex: 1;
  background: var(--bg-elev-2);
  border: 1px solid var(--border-input);
  border-radius: var(--r-sm);
  color: var(--text);
  padding: 6px 10px;
  outline: none;
  font-size: 12px;
  min-width: 0;
  transition: border-color var(--dur-fast), box-shadow var(--dur-fast), background var(--dur-fast);
}
.filter input[type="search"]::placeholder { color: var(--text-faint); }
.filter input[type="search"]:hover { border-color: var(--text-faint); }
.filter input[type="search"]:focus {
  border-color: var(--accent);
  background: var(--bg-elev);
  box-shadow: var(--ring);
}

.seg {
  display: inline-flex;
  align-items: center;
  gap: 0;
  flex: none;
}
.seg-btn {
  position: relative;
  appearance: none;
  border: none;
  background: transparent;
  color: var(--text-dim);
  font-size: 12px;
  height: 24px;
  padding: 0 9px;
  cursor: pointer;
  font-family: inherit;
  border-radius: 4px;
  white-space: nowrap;
  transition: color var(--dur-fast), background var(--dur-fast);
}
.seg-btn:hover {
  color: var(--text);
  background: var(--bg-hover);
}
.seg-btn[aria-checked="true"] {
  color: var(--accent);
  font-weight: 600;
  background: transparent;
}
/* 选中态：靠底部 2px accent 下划线（短，仅在文字下方）+ accent 文字色——足够识别但不抢戏 */
.seg-btn[aria-checked="true"]::after {
  content: "";
  position: absolute;
  left: 9px; right: 9px; bottom: -1px;
  height: 2px;
  background: var(--accent);
  border-radius: 2px 2px 0 0;
}
.seg-btn:focus-visible {
  outline: none;
  box-shadow: var(--ring);
}


/* ---------- 分区标题 ----------
 * 收藏 / 最近 / 全部文档三段共用一套标题样式，视觉层级明显低于文件行，
 * 但比原来的 10px 全大写更容易定位。整行可点折叠（原来只有右侧 16px 的小箭头
 * 是热区，得瞄准才点得到）。 */
.section-head,
.tree-head {
  display: flex;
  align-items: center;
  gap: 6px;
  width: 100%;
  padding: 5px 8px 5px 6px;
  border: none;
  background: transparent;
  color: var(--text-faint);
  font-family: inherit;
  font-size: var(--fs-xs);
  font-weight: var(--fw-semi);
  /* 字距拉开是中文分区头唯一能用的"弱化但可识别"手段（没有大写可用）。
   * 配合最弱的文字色，分区头退到背景层，文件行才是主角。 */
  letter-spacing: 0.08em;
  text-align: left;
  user-select: none;
}
.section-head { cursor: pointer; border-radius: var(--r-sm); transition: background var(--dur-fast), color var(--dur-fast); }
.section-head:hover { background: var(--bg-hover); color: var(--text-dim); }
.section-title { flex: 1; min-width: 0; }
/* 计数徽章去掉底色：分区头是整个侧栏里最弱的一层，给它一个填充色的 pill
 * 反而让"计数"比"分区名"更抢眼。纯数字 + tabular-nums 就够了。 */
.section-count {
  flex: none;
  font-size: var(--fs-xs);
  font-weight: var(--fw-medium);
  font-variant-numeric: tabular-nums;
  color: var(--text-faint);
  padding: 0 2px;
}
.section-chev { transition: transform var(--dur-fast) var(--ease-out); }
.section-head[aria-expanded="false"] .section-chev { transform: rotate(-90deg); }
/* 树头：视图分段控件（左）+ 排序按钮 / 全部折叠 / 计数（右）。侧栏瘦身后它紧贴搜索框，
 * 排序菜单 #sort-menu 以它为定位基准向下展开。 */
.tree-head { position: relative; padding: 6px 8px 6px 8px; margin-top: 2px; gap: 4px; }

/* 全部折叠 / 全部展开：常驻但低调（分组多时是主要导航手段，藏在 hover 里
 * 反而找不到）。和分区标题同一行右侧，紧贴计数徽章。 */
.tree-head-btn {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  padding: 0;
  border: none;
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--text-faint);
  cursor: pointer;
  transition: background var(--dur-fast), color var(--dur-fast), opacity var(--dur-fast);
}
.tree-head-btn:hover { background: var(--bg-hover); color: var(--text); }
.tree-head-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.tree-head-btn:disabled { opacity: 0.35; cursor: default; background: transparent; color: var(--text-faint); }

/* 目录树视图切换（分组 / 目录）——「全部文档」标题行右侧的紧凑分段控件。
 * 比排序那组更小更低调：它是"换一种看树的方式"，不是常动的操作。 */
.view-seg { flex: none; }
.view-seg .seg-btn {
  height: 21px;
  padding: 0 7px;
  font-size: 11px;
  letter-spacing: normal;
  font-weight: var(--fw-normal);
}
.view-seg .seg-btn[aria-checked="true"] { font-weight: 600; }
.view-seg .seg-btn[aria-checked="true"]::after { left: 7px; right: 7px; }


/* tree */
.tree {
  flex: 1;
  overflow-y: auto;
  overflow-x: hidden;
  /* 顶部 padding 归零：否则顶层分组头 sticky 到 top:0 时，上方这段 padding 会
   * 露出下方正在滚动的内容（一丝上一行的残影）。顶部呼吸感由上面的
   * 「全部文档」分区头提供，这里不再重复留白。 */
  padding: 0 6px 12px;
}

.folder { margin: 1px 0; }
.folder.dragging-ghost { opacity: 0.5; }
/* 文件夹行前缀 = 折叠箭头 16 + 间距 4 + 图标 16 + 间距 4 = 40px（左内边距 2px）；
 * 文件行用同宽的 40px 固定列（见 .file / .unread-dot），于是同一层的文件名与文件夹名
 * 起点落在同一条竖线上（design §12.2，tests/sidebar-tree-alignment.spec.js 断言）。 */
.folder-header {
  display: flex; align-items: center; gap: 4px;
  min-height: 28px;
  padding: 0 6px 0 2px;
  cursor: pointer;
  border-radius: var(--r-sm);
  user-select: none;
  transition: background var(--dur-fast);
}
.folder-header:hover { background: var(--bg-hover); }
.folder-header.drop-target { background: var(--bg-active); }
/* 顶层分组头吸顶：深滚几百篇文档时，"我在哪个顶层分组"始终挂在树顶（VS Code 的
 * tree sticky scroll 同款）。只做顶层一级——嵌套多级吸顶会互相堆叠、拖拽排序时抖动。 */
.tree > .folder > .folder-header {
  position: sticky;
  top: 0;
  z-index: 2;
}
/* 吸顶时要一层实底遮住下方滚动内容。用 :where() 让这条背景规则特异性为 0，于是
 * :hover / .drop-target / .drag-hover 的背景仍照常覆盖；底色取侧栏表面色，静息
 * （未吸顶）时与背景同色"看不见"，只有真正吸顶盖住内容那一刻才起遮挡作用。 */
:where(.tree > .folder > .folder-header) { background: var(--bg-elev); }
/* 拖拽悬停 600ms 即将自动展开的视觉提示 */
.folder-header.drag-hover {
  background: var(--bg-active);
  box-shadow: inset 0 0 0 1px var(--accent);
}
/* 键盘焦点（↑↓ 导航时）— 与 active 视觉明显区分：active 是填充背景，kbd-focus 是边框 */
.file.kbd-focus {
  background: transparent;
  box-shadow: inset 0 0 0 1.5px var(--accent);
}
.file.kbd-focus.active {
  /* 同一个文件既是 active 又是 kbd-focus 时，叠加：背景 + 边框 */
  background: var(--bg-active);
}
.file:focus { outline: none; }

/* 全文搜索"仅内容匹配"标记：file-mtime 前面加一个小放大镜 */
.file.content-match .file-mtime::before {
  content: '';
  display: inline-block;
  width: 9px; height: 9px;
  margin-right: 3px;
  vertical-align: -1px;
  background: currentColor;
  -webkit-mask: var(--ico-search) center / contain no-repeat;
  mask: var(--ico-search) center / contain no-repeat;
}
.folder-toggle {
  width: 16px; height: 16px;
  color: var(--text-faint);
  transition: transform var(--dur-fast) var(--ease-out), color var(--dur-fast);
  flex: none;
}
.folder-header:hover .folder-toggle { color: var(--text-dim); }
.folder.collapsed > .folder-header .folder-toggle { transform: rotate(-90deg); }
/* 文件夹图标统一中性色、不填充、不随新鲜度变化（design §12.1）；展开 / 收起只换形状 */
.folder-icon {
  width: 16px; height: 16px; flex: none;
  color: var(--text-dim);
}
.folder-icon svg { fill: none; }
.folder-icon .fi-open { display: none; }
.folder:not(.collapsed) > .folder-header .folder-icon .fi-closed { display: none; }
.folder:not(.collapsed) > .folder-header .folder-icon .fi-open { display: inline; }
.folder-name {
  flex: 1;
  font-weight: 600;
  font-size: 12.5px;
  /* 分组名是这一层的主角，字距收紧一点点让它更"结实"，和文件名的常规字重拉开 */
  letter-spacing: -0.01em;
  color: var(--text);
  outline: none;
  border-radius: 3px;
  padding: 1px 4px;
  margin-left: -4px;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* 嵌套子分组的名字比顶层轻一档 */
.folder .folder .folder-name { font-weight: 560; }
/* 编辑态字号固定 12px（var(--fs-sm)）：静息态文件夹名是 12.5px，但输入框外观要稳定，
 * 也不能被粘贴进来的格式改掉（tests/inline-edit.spec.js 断言） */
.folder-name[contenteditable="true"],
.file-name[contenteditable="true"] {
  background: var(--bg);
  outline: 1px solid var(--accent);
  outline-offset: 1px;
  /* 编辑态解除 ellipsis，允许换行展示完整文本 */
  white-space: pre-wrap;
  overflow: visible;
  text-overflow: clip;
  word-break: break-word;
  font-weight: 600;
  font-style: normal;
  font-size: var(--fs-sm);
  line-height: 1.4;
  color: var(--text);
  /* 屏蔽粘贴进来的字号/颜色等格式 */
  font-family: inherit;
}
.folder-header:has(.folder-name[contenteditable="true"]),
.file:has(.file-name[contenteditable="true"]) {
  align-items: flex-start;
}
/* 与 .section-count 统一：去掉填充 pill。一屏十几个分组各挂一个灰色药丸，
 * 视觉上比分组名本身还显眼，而它只是个参考数字。 */
.folder-count {
  font-size: var(--fs-xs);
  color: var(--text-faint);
  padding: 0 2px;
  font-variant-numeric: tabular-nums;
  flex: none;
}
.folder-unread-dot {
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--unread);
  flex: none;
}
/* 分组内全是 >24h 的旧未读时降级成灰点，和文件行的 .stale 同一套语义 */
.folder-unread-dot.stale { background: var(--unread-stale); }
/* 同 file-actions：不 hover 时 width:0，让 count 贴最右；hover 时平滑展开 */
.folder-actions {
  display: flex; gap: 1px; flex: none;
  width: 0; overflow: hidden;
  opacity: 0;
  transition: width .12s, opacity .12s;
}
/* 3 个按钮 × 18px + 2 个 1px 间隙 = 56px */
.folder-header:hover .folder-actions { width: 56px; opacity: 1; }
/* 自动生成的分组多一个「在访达中显示」（虚拟分组没有磁盘目录，不给这个按钮），
 * 于是宽度也要多一档：4 × 18 + 3 × 1 = 75px */
.folder-header:hover .folder-actions:has([data-act="reveal-folder"]) { width: 75px; }
.folder-actions button {
  width: 18px; height: 18px;
  border: none;
  background: transparent;
  color: var(--text-faint);
  cursor: pointer;
  border-radius: 3px;
  font-size: 11px;
  padding: 0;
  min-width: 0;
}
.folder-actions button:hover { background: var(--bg-elev-2); color: var(--text); }
.folder-actions button { display: inline-flex; align-items: center; justify-content: center; }

/* 子级容器：每层缩进 10 + 8 + 1px 引导线 = 19px，竖线就是层级引导线（design §10.5b） */
.folder-children {
  min-height: 4px;
  margin-left: 10px;
  padding-left: 8px;
  border-left: 1px solid var(--border-strong);
  /* 注意：不在这里常驻 overflow:hidden——那会裁掉行内按钮的键盘焦点环。
   * 只有动画期间（.animating）才需要裁剪来实现高度收缩，见下方规则。 */
  transition: border-color var(--dur-fast);
}
/* 选中文件所在的整条层级链，缩进引导线混入 55% 强调色——一眼看清"当前文件在哪条支上"
 * （祖先链的 .on-active-path 由 markSidebarActive 维护）。 */
.folder.on-active-path > .folder-children {
  border-left-color: color-mix(in srgb, var(--accent) 55%, var(--border-strong));
}
/* 顶层分组之间留 4px 呼吸 */
.tree > .folder + .folder { margin-top: 4px; }

/* 折叠末态：display:none。三个理由缺一不可——
 *   ① 键盘上下导航 visibleFilesInOrder() 靠 .folder.collapsed 类过滤，class 一直在；
 *   ② 折叠后文件行必须"真的不可见"（overflow:hidden + height:0 时子行仍有自己的
 *      包围盒，会被判定为可见）；
 *   ③ collapse-all / 首屏渲染走整树重建，元素一出生即 .collapsed，直接 none、无动画，
 *      不会出现"满屏分组一起展开"。
 * 单个分组的展开 / 折叠高度动画由 JS 驱动（toggleFolder → animateFolderChildren），
 * 动画期间临时挂 .animating 覆盖 display / min-height / overflow。 */
.folder.collapsed > .folder-children { display: none; }
.folder-children.animating {
  display: block !important;
  min-height: 0;
  overflow: hidden;
  /* 动画期间把这棵子树的内部布局 / 绘制与外部隔离：逐帧改 height 时浏览器不必回头
   * 重排子树内部成百上千行，深目录树掉帧显著减轻。只在 .animating 期间生效，
   * 平时（无此类）不裁剪，键盘焦点环照常可见（overflow 也仅此期间裁剪）。 */
  contain: layout paint;
  /* 刻意不设 will-change: height —— height 不是可合成属性，动画每帧都要走布局，
   * will-change 帮不上忙，只会把这个容器提成 GPU 合成层、白占一份光栅内存（大子树上
   * 这份浪费不小）。JS 侧同样不再设置（见 animateFolderChildren）。 */
  transition: height var(--dur) var(--ease-out);
}
/* 目录视图（只读镜像、不挂 SortableJS）里给每个 .folder 加 layout containment：
 * 展开 / 折叠某个目录时，下方成百上千个兄弟节点只需整体平移，无需深度重排。
 * 分组视图刻意不加——拖拽重排依赖正常布局流，containment 会干扰命中判定。 */
.tree.tree-view-mode .folder { contain: layout; }
/* 卡顿根因是"把一大坨已建好的 DOM 一次性显示出来"——目录视图默认全展开、动辄几百上千行，
 * 展开一个大目录时浏览器要对整棵子树做一次性布局（实测同步阻塞 ~85ms、明显掉帧）。
 * content-visibility:auto 让浏览器只对视口内（及邻近）的行做布局 / 绘制，视口外的行用
 * contain-intrinsic-size 占位、跳过渲染——等于原生的列表虚拟化。实测展开大目录的同步
 * 阻塞从 ~85ms 降到 <1ms、0 掉帧，滚动也更轻。26px（= 文件行高）作为高度估值，
 * auto 关键字会在首次渲染后记住真实高度、自我校正，滚动条不跳。
 * 只作用于目录视图：分组视图要拖拽重排，content-visibility 会影响命中判定，故不加。
 *
 * 【实测记录】曾试过把 cv 也加到分组视图来治展开卡顿，结论是不可行，别再试：
 *   ① 没有收益 —— 400 子项分组、8 次配对采样，同步阻塞 2/8 占优、整体 -5%，在噪声内。
 *      因为展开的主导成本是 JS 侧建 DOM（约占 75~80%），不是 CSS 能省掉的布局/绘制。
 *   ② 有害 —— cv 让 .folder-children 的 scrollHeight 只报 contain-intrinsic-size 占位值
 *      （400 子项实测只报 40px），于是 animateFolderChildren 里"大子树走即时展开"的
 *      分流判断（estimate > ANIMATE_MAX_PX）被绕过，几百行的分组改去跑 height 过渡，
 *      而过渡起点又提交不了，只能等 400ms 兜底超时才跳开 —— 就是「点了之后卡一会儿
 *      才啪一下打开」。目录视图本来就默认全展开、极少走这条分流，所以只在那边成立。 */
.tree.tree-view-mode .folder-children { content-visibility: auto; contain-intrinsic-size: auto 40px; }
.tree.tree-view-mode .file { content-visibility: auto; contain-intrinsic-size: auto 26px; }   /* = 行高 26px，未渲染与渲染后一致，不再每行差 2px */
/* 展开时子项依次淡入并轻微上移，给折叠展开一点"空间连续性"。
 * 只 stagger 前 8 个，长分组尾部不再拖延迟；reduced-motion 下 --dur=0 自然失效。 */
@keyframes treeItemIn {
  from { opacity: 0; transform: translateY(-3px); }
  to   { opacity: 1; transform: none; }
}
.folder-children.is-opening > .file,
.folder-children.is-opening > .folder {
  animation: treeItemIn var(--dur) var(--ease-out) both;
}
.folder-children.is-opening > :nth-child(1) { animation-delay: 0ms; }
.folder-children.is-opening > :nth-child(2) { animation-delay: 16ms; }
.folder-children.is-opening > :nth-child(3) { animation-delay: 32ms; }
.folder-children.is-opening > :nth-child(4) { animation-delay: 48ms; }
.folder-children.is-opening > :nth-child(5) { animation-delay: 64ms; }
.folder-children.is-opening > :nth-child(6) { animation-delay: 80ms; }
.folder-children.is-opening > :nth-child(7) { animation-delay: 96ms; }
.folder-children.is-opening > :nth-child(n+8) { animation-delay: 112ms; }
/* 直接子项很多时，第 15 个起不再逐项入场：几百个子项同时跑 opacity/transform 会拖累
 * 合成器。前 14 个的错峰已足够传达"展开"的空间连续性，其余直接呈现。 */
.folder-children.is-opening > :nth-child(n+15) { animation: none; }

/* 文件行。
 * flex-wrap 是为了 .file-snippet：搜索命中正文时它 flex-basis:100% 强制换行，
 * 于是这一行自然变成"标题 + 内容摘要"的两行式（Bear 的列表行就是这个结构），
 * 没有命中时完全不占高度，密集树里仍是紧凑单行。 */
/* 行高固定（26px；带正文摘要的命中行 42px）、不换行：以前 flex-wrap 让窄侧栏下"静息态挤成两行、
 * 悬停后元素变少回到一行"，鼠标停在两行交界处时两行高度每帧来回翻（diagnosis §5）。
 * 现在悬停时行尾元素只是 visibility 隐身（占位不变），操作组绝对定位盖在行尾，悬停前后几何完全一致。
 * 摘要 .file-snippet 也改成绝对定位在第二行，不再参与流式换行 */
.file {
  position: relative;
  display: flex; align-items: center; gap: 4px;
  flex-wrap: nowrap;
  height: 26px;
  box-sizing: border-box;
  padding: 0 6px 0 2px;
  border-radius: var(--r-sm);
  cursor: pointer;
  font-size: var(--fs-sm);
  font-weight: var(--fw-normal);
  color: var(--text-dim);
  user-select: none;
  transition: background var(--dur-fast);
}
/* 键盘 Tab 进操作组时（没有悬停）与悬停同样上底色：操作组 background: inherit，
 * 行透明时图标会直接叠在文件名尾部的文字上（client-fix review #5）。只认 :focus-visible：鼠标点按钮也会给焦点，
 * 用 :focus-within 时点完「分享」等按钮移开鼠标，这一行会一直停在悬停态（第 2 轮评审 #2） */
.file:hover,
.file:has(.file-actions :focus-visible) { background: var(--bg-hover); color: var(--text); }
.file.unread, .file.active { color: var(--text); }
.file:has(.file-snippet) { height: 42px; padding-bottom: 16px; }
/* 分组视图的文件行同样按视口渲染（client-fix §2 / §6）：2,000 篇时侧栏常驻约 4 万个节点，
 * 每次切主题 / 首次光栅都要对它们全量重算样式。content-visibility:auto 让视口外的行跳过样式 / 布局 / 绘制，
 * 占位高度与真实行高完全一致（26px / 命中行 42px），所以 .folder-children 的 scrollHeight、
 * 展开动画的分流估算和滚动条都不受影响。只加在 .file 行上，不加在 .folder-children 上
 * （那样会让 scrollHeight 只报占位值，见上方目录视图段的实测记录） */
.tree .file { content-visibility: auto; contain-intrinsic-size: auto 26px; }
.tree .file:has(.file-snippet) { contain-intrinsic-size: auto 42px; }
.file.active {
  background: var(--bg-active);
  box-shadow: inset 2px 0 0 var(--accent);
}
.file.active .file-name { font-weight: var(--fw-semi); }
.file.dragging-ghost { opacity: 0.5; }
/* 文件行不再有左侧类型图标（design §11.B2）：DOM 保留、只是不显示，类型看右侧角标 */
.file .file-type-icon { display: none; }
/* 树里的类型角标：中性等宽小字（配色收敛，不再按类型换色相），悬停时让位给操作按钮 */
.tree .file .file-type-badge {
  font-family: var(--font-mono);
  font-weight: 600;
  font-size: 9.5px;
  color: var(--text-faint);
  background: transparent;
  border-color: var(--border-strong);
}
.tree .file .file-type-badge { flex: none; }
/* 文件名优先（diagnosis §11）：basis 取内容宽度、收缩权重 1；目录提示 / 标签 chip 收缩权重大得多，
 * 空间不够时先缩它们，文件名最后才被截。完整路径在悬停预览与行 title 里 */
.file-name {
  flex: 1 1 auto;
  min-width: 48px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  outline: none;
  border-radius: 3px;
  padding: 1px 4px;
  margin-left: -4px;
}
/* 两条 unread 字重规则都排除编辑态：contenteditable 时这里是个输入框，
 * 外观必须稳定（见下方 .file-name[contenteditable] 那条），不该再受
 * "这个文件未读吗"影响——否则同一个输入框会因为文件状态不同而粗细不一。 */
.file.unread .file-name:not([contenteditable="true"]) { font-weight: var(--fw-semi); }
/* 陈旧未读也恢复常规字重：和未读点降级是同一个道理——588 行全是粗体时，
 * 粗体不再表示"这行重要"，只是让整个侧栏看起来在喊。 */
.file.unread.stale .file-name:not([contenteditable="true"]) { font-weight: var(--fw-normal); }
.file.has-alias .file-name { font-style: italic; }
/* 深层文件的中间目录提示（分组视图）：靠右、次要色、可截断。跟在文件名之后，
 * 让平铺在同一分组里的多个同名文件（README / index …）一眼看出各自在哪个子目录。
 * 和时间戳一样在 hover 时让位给操作按钮，避免右侧拥挤。 */
.file-dir-hint {
  flex: 0 4 auto;       /* 收缩权重（× 内容宽）比文件名大得多：空间不够时先缩目录提示 */
  min-width: 32px;      /* 至少留出末级目录的尾巴（省略号在开头） */
  max-width: 30%;
  direction: rtl;       /* 省略号在开头，末级目录始终可见 */
  text-align: left;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--fs-xs);
  color: var(--text-faint);
  margin-left: 6px;
}
/* 侧栏较窄（树宽 < 280px，约侧栏 290px 以下）时整段不显示：同名文件靠悬停预览 / title 里的完整路径区分 */
.tree { container-type: inline-size; container-name: tree; }
@container tree (max-width: 279px) {
  .file-dir-hint { display: none; }
}
/* 悬停时行尾的次要信息只隐身、不出流（visibility），行内几何不变；操作组盖在上面 */
.file:hover :is(.file-dir-hint, .file-tags, .file-type-badge, .fav-mark, .share-badge),
.file:has(.file-actions :focus-visible) :is(.file-dir-hint, .file-tags, .file-type-badge, .fav-mark, .share-badge) { visibility: hidden; }
.file-mtime {
  font-size: var(--fs-xs);
  color: var(--text-faint);
  font-variant-numeric: tabular-nums;
  flex: none;
}
/* hover 时让位给 .file-actions。
 * 原来两者是叠加的：actions 展开 94px，把文件名从 163px 直接压到 97px——
 * 也就是"想操作某个文件时，恰恰看不清它叫什么"。时间戳在 hover 的那一刻
 * 是最不重要的信息，让它先走。 */
.file:hover .file-mtime,
.file:has(.file-actions :focus-visible) .file-mtime { visibility: hidden; }
/* 静息态默认不显示右侧时间戳。一列右对齐的 03-25 / 03-18 正是"上世纪办公软件
 * 详细信息列表"观感的主要来源；时间信息在行 title 与顶栏面包屑「更新于」里都有，
 * 加上按时间排序本身就表达了新旧，树里不必再挂一列日期。
 * 唯一例外：仅内容命中的行保留时间戳（连同前置放大镜标记）——它是"这行为什么
 * 会出现在结果里"的直接答案，属于有效信息而非噪音。 */
.file .file-mtime { display: none; }
.file.content-match .file-mtime { display: inline; }

/* 文件行的 40px 前缀列：左 25px + 6px 点 + 右 5px + 行内 gap 4px。点的中心正对同级文件夹
 * 图标的中心（2 + 16 + 4 + 8 = 30px），文件名从 42px 起、和同级文件夹名对齐。已读时点隐身但占位 */
.unread-dot {
  width: 6px; height: 6px;
  margin: 0 5px 0 25px;
  border-radius: 50%;
  background: var(--unread);
  flex: none;
  transition: background var(--dur-fast);
}
.file:not(.unread) .unread-dot { visibility: hidden; }
/* 陈旧未读（>24h）降级成灰点：见 app.js 里 isFresh 的注释。
 * 未读点必须稀缺才有意义——588 个满亮度点等于 0 个。 */
.file.unread.stale .unread-dot { background: var(--unread-stale); }

/* 搜索命中正文时的第二行摘要。
 * flex-basis:100% 让它独占一行；原来这段摘要只存在于 title tooltip 里，
 * 白白浪费——它恰恰是"为什么这个文件出现在结果里"的唯一答案。 */
.file-snippet {
  position: absolute;
  left: 42px; right: 6px; top: 24px;   /* 第二行，与上一行的文件名文字左缘对齐（40px 前缀列） */
  min-width: 0;
  font-size: var(--fs-xs);
  line-height: var(--lh-base);
  color: var(--text-dim);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.file-snippet mark {
  background: var(--accent-soft);
  color: var(--accent);
  border-radius: 2px;
  padding: 0 1px;
  font-weight: var(--fw-medium);
}
/* 默认 width:0 不占布局，让 mtime 贴最右；hover 时平滑展开（3 个按钮 = 57px） */
/* 绝对定位盖在行尾：展开 / 收起不参与行内布局（行高、文件名宽度都不变）。
 * 底色继承行的背景（悬停 / 选中色），盖住下面已隐身的角标位置；左缘一段渐隐接到文件名上 */
.file-actions {
  position: absolute; right: 4px; top: 4px; height: 18px;
  display: flex; gap: 1px;
  width: 0; overflow: hidden;
  opacity: 0;
  background: inherit;
  border-radius: 3px;
  transition: width .12s, opacity .12s;
}
/* 6 个按钮（收藏 / 分享 / 标签 / 重命名文件 / 备注名 / 访达）× 18px + 5 个 1px 间隙 = 113px。
 * 键盘 Tab 进到某个按钮时同样展开（只看按钮自己的 :focus-visible，点行本身、鼠标点按钮都不会撑住） */
.file:hover .file-actions,
.file-actions:has(:focus-visible) { width: 113px; opacity: 1; }
/* 分享态的 share button 用 accent 蓝标识（已分享中再点是再次打开 modal） */
.file.shared .file-actions [data-act="share"] { color: var(--accent); }

/* 已分享角标：常驻可见，绿色小链接 icon */
.share-badge {
  display: none;
  align-items: center; justify-content: center;
  width: 14px; height: 14px;
  color: var(--ok);
  flex: none;
}
.file.shared .share-badge { display: inline-flex; }

/* 顶栏分享按钮：当前文件已在分享中时 → accent 色 */
#btn-share.shared {
  color: var(--accent);
  background: var(--accent-soft);
}

/* ==================== 收藏与标签 ==================== */

/* 文件行上的星标：悬停操作组 .file-actions 的第一个按钮（design §12.2），尺寸与外观
 * 跟随 .file-actions button。已收藏 = 实心填充；静息态的收藏标记由名字后的 .fav-mark 负责。 */
.file.favorite .fav-btn { color: var(--fav); }
.file.favorite .fav-btn svg { fill: currentColor; }
/* 已收藏文件名后的 9px 实心星：不占左侧空间、不可点（点星走悬停组里的按钮） */
.fav-mark {
  flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--text-faint);
}
.fav-mark svg { width: 9px; height: 9px; fill: currentColor; }

/* 文件行上的标签：最多一个（design §10.5），宽度受限、优先让文件名占空间 */
.file-tags {
  display: flex; align-items: center; gap: 3px;
  flex: 0 4 auto;      /* 比文件名先缩（见 .file-name） */
  min-width: 0;
  overflow: hidden;
}
.file-tag {
  flex: none;
  /* 从 72px 收到 56px：一个 chip 省 16px，正是文件名被截断时缺的那几个字符。
   * 完整标签仍在行的 title 里，标签筛选区也照样能按它筛。 */
  max-width: 56px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: 9.5px;
  line-height: 1.5;
  padding: 0 6px;
  border-radius: var(--r-full);
  /* 去掉描边、底色再压淡：行内标签是次要信息，带边框的实心 chip 一多就把文件名
   * 挤成一排"药丸墙"。改成无边框的轻底色，安静地待在名字后面即可（完整标签在
   * title 里，筛选也照常）。 */
  background: var(--accent-soft);
  color: var(--text-dim);
  border: none;
}
/* 两层以下不显示（design §10.5）：三层文件夹内（及更深）的文件行只留文件名 + 类型角标；
 * 两层文件夹内的文件行仍显示。
 * 用嵌套深度判定而不是在 renderFile 里算，懒展开渲染的子树也天然一致 */
.folder-children .folder-children .folder-children .file .file-tags { display: none; }
/* hover 时把标签让给操作按钮：侧栏默认只有 320px，标签 chip 和展开后的
 * 5 个按钮同时占位会把文件名压成「留…」。标签是"平时用来认"的信息，
 * hover 是"现在要操作"，让位是正确的取舍（完整标签仍在 title 里） */
/* （悬停时的让位改为 visibility，见 .file-dir-hint 段） */

.tag-chip {
  display: inline-flex; align-items: center; gap: 5px;
  height: 22px;
  padding: 0 8px;
  border-radius: var(--r-full);
  border: 1px solid var(--border);
  background: var(--bg-elev-2);
  color: var(--text-dim);
  font-family: inherit;
  font-size: 11px;
  cursor: pointer;
  white-space: nowrap;
  max-width: 100%;
  transition: background var(--dur-fast), color var(--dur-fast),
              border-color var(--dur-fast), box-shadow var(--dur-fast);
}
.tag-chip:hover {
  background: var(--bg-hover);
  color: var(--text);
  border-color: var(--border-strong);
}
.tag-chip-name { overflow: hidden; text-overflow: ellipsis; }
.tag-chip-count {
  font-size: 9.5px;
  color: var(--text-faint);
  font-variant-numeric: tabular-nums;
}
/* 选中态：accent 描边 + 淡填充 + 一圈 inset 把 1px 边"加粗"到看得清。
 * 比纯文字色变化更容易在一堆 chip 里辨认，也不至于像实心填充那样在
 * 侧栏里糊成一片蓝 */
.tag-chip.on {
  border-color: var(--accent);
  color: var(--accent);
  background: var(--accent-soft);
  box-shadow: inset 0 0 0 1px var(--accent);
  font-weight: 500;
}
.tag-chip.on:hover { background: var(--accent-soft); color: var(--accent); }
.tag-chip.on .tag-chip-count { color: var(--accent); }
.tag-chip:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* 「清除」：只在有筛选时出现，跟在分区头右侧。用文字而不是 ✕ 图标——
 * 侧栏里已经有一排 ✕ 形状的按钮，再加一个含义完全不同的容易误点。
 * 「最近」分区的清除按钮共用这套外观（.recent-clear）——两者都是
 * "跟在分区头右边的次要文字动作"，长得不一样反而像两种不同的东西 */
.tag-bar-clear,
.recent-clear {
  flex: none;
  height: 22px;
  padding: 0 7px;
  border: none;
  background: transparent;
  color: var(--accent);
  font-family: inherit;
  font-size: 11px;
  cursor: pointer;
  border-radius: var(--r-xs);
  transition: background var(--dur-fast), color var(--dur-fast);
}
.tag-bar-clear:hover,
.recent-clear:hover { background: var(--accent-soft); }
.tag-bar-clear:focus-visible,
.recent-clear:focus-visible { outline: none; box-shadow: var(--ring); }
.tag-bar-clear.hidden,
.recent-clear.hidden { display: none; }


/* 顶栏收藏按钮：当前文件已收藏时 → 星星本身填成金色。
 * 刻意不像 #btn-share.shared 那样连边框一起变色：星形 fill 之后在 28px 按钮里
 * 几乎占满，再加一圈金边整个按钮会变成一块"金砖"，在一排线性图标里过分抢戏 */
#btn-favorite.on { color: var(--fav); }
#btn-favorite.on svg { fill: var(--fav); }

/* 顶栏信任盾牌。两态刻意都不刺眼：
 * 受保护（默认）是常态，不该像警告一样一直亮着；已信任是用户自己的选择，
 * 用一点绿色确认它生效了就够，不需要红色去"提醒风险"——那样每篇自己写的
 * 仪表盘都在挨骂。 */
#btn-trust.is-trusted { color: var(--ok, #3fb950); }
#btn-trust:disabled { opacity: .5; }

/* ---------- 分享 modal ---------- */
.share-panel {
  max-width: 520px;
  width: 92vw;
}
.share-body {
  padding: 18px 22px 22px;
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.share-meta { display: flex; flex-direction: column; gap: 4px; }
.share-filename {
  font-size: 14px;
  font-weight: 600;
  color: var(--text);
  word-break: break-all;
}
.share-hint { font-size: 12px; color: var(--text-dim); }

.share-qr-wrap {
  display: flex; flex-direction: column; align-items: center;
  gap: 8px;
  padding: 14px;
  background: #fff;     /* QR 必须白底（深色主题下也是） */
  border-radius: 8px;
  border: 1px solid var(--border);
}
.share-qr {
  width: 180px; height: 180px;
  display: flex; align-items: center; justify-content: center;
}
.share-qr canvas, .share-qr img { display: block; }
.share-qr-caption { font-size: 11px; color: #666; }

.share-urls { display: flex; flex-direction: column; gap: 6px; }
.share-url-row {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 6px 8px 6px 12px;
  background: var(--bg-elev-2);
  border: 1px solid var(--border);
  border-radius: 6px;
}
.share-url-row .share-url-label {
  font-size: 10.5px;
  color: var(--text-faint);
  flex: none;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  margin-right: 4px;
}
.share-url-row .share-url-text {
  flex: 1;
  font-family: var(--font-mono);
  font-size: 11.5px;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  user-select: all;
}
.share-url-row .share-url-copy {
  height: 22px;
  padding: 0 8px;
  font-size: 11px;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--text-dim);
  border-radius: 4px;
  cursor: pointer;
  flex: none;
  transition: border-color var(--dur-fast), color var(--dur-fast);
}
.share-url-row .share-url-copy:hover { border-color: var(--accent); color: var(--accent); }
.share-url-row .share-url-copy.copied { color: var(--ok); border-color: var(--ok); }

.share-actions {
  display: flex;
  gap: 8px;
  justify-content: flex-end;
}
.modal .share-btn-secondary {
  height: 30px;
  padding: 0 14px;
  border: 1px solid var(--border);
  background: var(--bg-elev);
  color: var(--text);
  border-radius: 6px;
  font-size: 12.5px;
  cursor: pointer;
  transition: border-color var(--dur-fast), background var(--dur-fast);
}
.modal .share-btn-secondary:hover { border-color: var(--accent); background: var(--bg-hover); }
.modal .share-btn-danger {
  height: 30px;
  padding: 0 14px;
  border: 1px solid var(--danger);
  background: transparent;
  color: var(--danger);
  border-radius: 6px;
  font-size: 12.5px;
  cursor: pointer;
  transition: background var(--dur-fast), color var(--dur-fast);
}
.modal .share-btn-danger:hover { background: var(--danger); color: light-dark(#fff, #1a0508); }
.file-actions button {
  width: 18px; height: 18px;
  border: none;
  background: transparent;
  color: var(--text-faint);
  cursor: pointer;
  border-radius: 3px;
  font-size: 10px;
  padding: 0;
  min-width: 0;
}
.file-actions button { display: inline-flex; align-items: center; justify-content: center; }
.file-actions button:hover { background: var(--bg-elev-2); color: var(--text); }
.file-actions button:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

.sidebar-footer {
  padding: 7px 12px;
  border-top: 1px solid var(--border);
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: 11px;
  color: var(--text-faint);
  flex: none;
}
#save-status { color: var(--ok); transition: color .2s; font-size: 8px; flex: none; }
#save-status.saving { color: var(--warn); }
#save-status.error { color: var(--danger); }
.footer-stats {
  flex: 1;
  min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.footer-stats .stat-unread { color: var(--unread); font-weight: 600; }
.footer-stats .stat-warn { color: var(--warn); font-weight: 600; cursor: help; }
.footer-kbd { flex: none; opacity: .8; }

/* main：margin-left 跟随 sidebar */
.main {
  margin-left: calc(var(--sidebar-w) + var(--resizer-w));
  height: 100vh;
  display: flex;
  flex-direction: column;
  background: var(--bg);
  overflow: hidden; /* iframe 冻结时超出部分裁掉 */
}
/* 主区跟着侧栏连续重排（margin-left 过渡）：动画期间主区宽度始终等于"侧栏右缘到窗口右缘"，
 * 居中 / 右对齐的内容不会横跳或滑出窗口。只排一次 + transform 平移的方案会在首尾帧横跳（见 app.js 注释）。
 * reduced-motion 下 --dur = 0，硬切 */
body.tx-ready .main { transition: margin-left var(--dur) var(--ease); }
body.sidebar-collapsed .main { margin-left: 0; }

.main-header {
  height: 46px;
  border-bottom: 1px solid var(--border);
  background: var(--bg-elev);
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 14px;
  gap: 12px;
}

.toggle-sidebar {
  height: var(--h-ctl);
  min-width: var(--h-ctl);
  padding: 0 6px;
  border: 1px solid transparent;
  background: transparent;
  color: var(--text-dim);
  border-radius: var(--r-sm);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  transition: background var(--dur-fast), border-color var(--dur-fast),
              color var(--dur-fast), transform var(--dur-fast) var(--ease-spring);
}
.toggle-sidebar:hover { background: color-mix(in srgb, var(--accent) 12%, transparent); color: var(--accent); transform: scale(1.06); }
.toggle-sidebar svg { display: block; transition: transform var(--dur) var(--ease); }
body.sidebar-collapsed .toggle-sidebar svg { transform: scaleX(-1); }

/* 首次加载若已收起，立即应用 transform，避免看到收起动画 */
html.init-sidebar-collapsed .sidebar { transform: translate3d(-100%, 0, 0); }
html.init-sidebar-collapsed .main { margin-left: 0; }
html.init-sidebar-collapsed .resizer { left: 0; opacity: 0; pointer-events: none; }
.crumbs {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 12px;
}
/* 面包屑第一段「首页」的两个形态：
 *   .placeholder —— 没打开文档（已经在首页了），不可点
 *   .crumb-home  —— 打开了文档，可点回首页
 * 图标 + 文字的排布刻意做成一样，只有 hover 底色和文字色的差别：它们是同一个
 * 位置的两种状态，位置一致才让人相信"点它就回到刚才那个页面"，
 * 也避免来回切换时文字横跳。 */
/* 两个形态用同一套 height / padding，靠这个对齐，而不是靠负 margin。
 * 原来 .crumb-home 是 `padding: 0 6px; margin-left: -6px`——但 .crumbs 是
 * overflow:hidden，负 margin 把按钮左移出容器后，左侧那 6px 的 padding 连同
 * hover 底色一起被裁掉了，于是 hover 背景看起来紧贴在 ⌂ 图标的左边缘上。 */
.crumbs .placeholder {
  display: inline-flex; align-items: center; gap: 5px;
  vertical-align: middle;
  color: var(--text-dim);
  height: 22px;
  padding: 0 8px;
}
.crumbs .placeholder .ico { color: var(--text-faint); }
.crumbs .crumb-home {
  display: inline-flex; align-items: center; gap: 5px;
  vertical-align: middle;
  appearance: none;
  border: none;
  background: transparent;
  color: var(--text-dim);
  font: inherit;
  font-size: 12px;
  height: 22px;
  padding: 0 8px;
  border-radius: var(--r-sm);
  cursor: pointer;
  transition: background var(--dur-fast), color var(--dur-fast);
}
.crumbs .crumb-home .ico { color: var(--text-faint); transition: color var(--dur-fast); }
.crumbs .crumb-home:hover { background: var(--bg-hover); color: var(--text); }
.crumbs .crumb-home:hover .ico { color: var(--accent); }
.crumbs .crumb-home:focus-visible { outline: none; box-shadow: var(--ring); }
.crumbs .crumb-project { color: var(--text-dim); margin-right: 6px; }
.crumbs .crumb-sep { color: var(--text-faint); margin: 0 5px; opacity: .7; }
.crumbs .crumb-name,
.crumbs .crumb-alias { color: var(--text); font-weight: 600; font-size: 12.5px; }
.crumbs .crumb-original { color: var(--text-faint); font-size: 11px; margin-left: 6px; }
/* 元信息（更新于 …）：纯文字最淡的一档。刻意不做成 pill——
 * pill 看起来像可点的标签，而它只是一句说明 */
.crumbs .crumb-meta {
  color: var(--text-faint);
  margin-left: 10px;
  font-size: 11px;
  white-space: nowrap;
}

.main-actions { display: flex; gap: 1px; align-items: center; flex: none; }

/* iframe 内搜索匹配数 + 上下跳转 */
.match-badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 4px;
  border-radius: 7px;
  background: var(--bg-elev-2);
  border: 1px solid var(--hairline, var(--border));
  margin-right: 6px;
  font-size: 11px;
  color: var(--text-mute);
  font-variant-numeric: tabular-nums;
}
.match-badge.hidden { display: none; }
.match-badge .match-text {
  padding: 0 4px;
  user-select: none;
}
.match-badge .match-nav {
  width: 20px; height: 20px;
  border: none;
  background: transparent;
  color: var(--text-faint);
  cursor: pointer;
  border-radius: 4px;
  font-size: 9px;
  padding: 0;
  display: grid; place-items: center;
  transition: background var(--dur-fast, 120ms), color var(--dur-fast, 120ms);
}
.match-badge .match-nav:hover {
  background: var(--bg-hover);
  color: var(--text);
}
/* ✕：就地清除文档内搜索高亮（不离开文档）。命中条本身没有出口是"搜索退不出"的直接原因 */
.match-badge .match-close {
  width: 20px; height: 20px;
  border: none;
  background: transparent;
  color: var(--text-faint);
  cursor: pointer;
  border-radius: 4px;
  padding: 0;
  display: grid; place-items: center;
  margin-left: 1px;
  transition: background var(--dur-fast, 120ms), color var(--dur-fast, 120ms);
}
.match-badge .match-close:hover { background: var(--bg-hover); color: var(--text); }
.match-badge .match-close svg { display: block; }

/* ==================== 文档内查找栏（⌘F） ==================== */
.find-bar {
  position: absolute;
  top: 10px;
  right: 16px;
  z-index: 6;   /* 浮在预览帧之上；编辑/对比态下不开启，故无需盖过它们 */
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 4px 4px 4px 8px;
  border-radius: var(--r-md);
  background: var(--bg-elev);
  /* 查找栏本身就是 #find-input 的可见边界（输入框无边框），用输入框轮廓色 */
  border: 1px solid var(--border-input);
  box-shadow: 0 6px 20px rgba(0, 0, 0, .18);
}
.find-bar.hidden { display: none; }
.find-bar .find-ico { color: var(--text-dim); flex: 0 0 auto; }
.find-bar input {
  width: 200px;
  border: 0;
  outline: none;
  background: transparent;
  color: var(--text);
  font: inherit;
  font-size: 12px;
  padding: 2px 2px;
}
.find-bar .find-count {
  font-size: 11px;
  color: var(--text-mute);
  font-variant-numeric: tabular-nums;
  padding: 0 4px;
  white-space: nowrap;
  min-width: 34px;
  text-align: center;
  user-select: none;
}
.find-bar .find-count.none { color: var(--danger); }
.find-bar .find-nav,
.find-bar .find-close {
  width: 22px; height: 22px;
  border: 0;
  background: transparent;
  color: var(--text-dim);
  cursor: pointer;
  border-radius: var(--r-sm);
  font-size: 10px;
  padding: 0;
  display: grid; place-items: center;
  transition: background var(--dur-fast, 120ms), color var(--dur-fast, 120ms);
}
.find-bar .find-nav:hover,
.find-bar .find-close:hover { background: var(--bg-hover); color: var(--text); }
.find-bar .find-close svg { display: block; }
/* 分隔关闭按钮 */
.find-bar .find-close { margin-left: 2px; box-shadow: -1px 0 0 var(--border); }

/* 升级提示标签 */
.update-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  margin-right: 6px;
  border-radius: 999px;
  background: var(--accent-soft);
  border: 1px solid var(--accent-ring);
  color: var(--accent);
  font-size: 11.5px;
  font-weight: 500;
  cursor: pointer;
  text-decoration: none;
  transition: background var(--dur-fast, 120ms), border-color var(--dur-fast, 120ms);
}
.update-badge:hover {
  background: color-mix(in srgb, var(--accent) 18%, transparent);
  border-color: var(--accent);
  text-decoration: none;
}
.update-badge.hidden { display: none; }
.update-badge .dot {
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--accent);
  animation: update-pulse 2s ease-out infinite;
}
@keyframes update-pulse {
  0%, 100% { box-shadow: 0 0 0 0 var(--accent-ring); }
  50%      { box-shadow: 0 0 0 5px transparent; }
}

/* ---------- 新版本提示 banner ---------- */
/* 风格：原生 IDE 风格通知条——左侧 accent stripe + 平铺底色，不喧哗。
 * 默认 36px 高，态切换到 busy 且日志展开时撑高到 ~220px。
 * 三种态：idle（默认）/ busy（升级中）/ error（失败）—— 由 .state-busy / .state-error 类切换
 */
.update-banner {
  position: relative;
  flex: none;
  display: flex;
  flex-direction: column;
  background: var(--bg-elev-2);
  border-bottom: 1px solid var(--border);
  font-size: 12px;
  color: var(--text-dim);
  overflow: hidden;
  transition: max-height var(--dur, 200ms) var(--ease, ease),
              opacity var(--dur-fast, 120ms),
              border-bottom-color var(--dur-fast, 120ms);
  max-height: 36px;
}
.update-banner.hidden {
  max-height: 0;
  opacity: 0;
  border-bottom-color: transparent;
  pointer-events: none;
}
.update-banner.log-open { max-height: 240px; }

.update-banner .ub-row {
  display: flex;
  align-items: center;
  gap: 10px;
  height: 36px;
  flex: none;
  padding: 0 8px 0 16px;
}

/* 态切换规则放在文件最后（见 banner 块尾部） */

/* 左侧 accent 竖条——3px 宽 */
.update-banner .ub-stripe {
  position: absolute;
  left: 0; top: 0; bottom: 0;
  width: 3px;
  background: var(--accent);
}

/* 脉动小圆点（unread 色）——表达 "有新东西" */
.update-banner .ub-pulse {
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--unread);
  animation: ub-pulse 1.8s ease-out infinite;
  flex: none;
}
@keyframes ub-pulse {
  0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--unread) 50%, transparent); }
  60%      { box-shadow: 0 0 0 5px transparent; }
}

/* 主文字 */
.update-banner .ub-text { display: inline-flex; align-items: baseline; gap: 6px; }
.update-banner .ub-text strong {
  color: var(--text);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.update-banner .ub-text .ub-sep { color: var(--text-faint); }

/* 升级命令——内联 code 块，整体可点击 */
.update-banner .ub-cmd {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  height: 24px;
  padding: 0 10px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 6px;
  cursor: pointer;
  color: var(--text-dim);
  font-family: var(--font-mono);
  font-size: 11.5px;
  transition: border-color var(--dur-fast, 120ms),
              background var(--dur-fast, 120ms),
              color var(--dur-fast, 120ms);
}
.update-banner .ub-cmd:hover {
  border-color: var(--accent);
  color: var(--text);
  background: var(--bg-hover);
}
.update-banner .ub-cmd:focus-visible {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 2px rgba(91, 156, 255, 0.25);
}
.update-banner .ub-cmd-prompt { color: var(--text-faint); user-select: none; }
.update-banner .ub-cmd-text { color: var(--text); }
.update-banner .ub-cmd-hint {
  color: var(--text-faint);
  font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", sans-serif;
  font-size: 10.5px;
  letter-spacing: 0.02em;
}
.update-banner .ub-cmd.copied {
  border-color: var(--ok);
  color: var(--ok);
}
.update-banner .ub-cmd.copied .ub-cmd-text,
.update-banner .ub-cmd.copied .ub-cmd-prompt,
.update-banner .ub-cmd.copied .ub-cmd-hint { color: var(--ok); }

/* 关闭按钮——极简 */
.update-banner .ub-close {
  width: 22px; height: 22px;
  display: inline-flex; align-items: center; justify-content: center;
  border: none;
  border-radius: 4px;
  background: transparent;
  color: var(--text-faint);
  cursor: pointer;
  padding: 0;
  flex: none;
  transition: background var(--dur-fast, 120ms), color var(--dur-fast, 120ms);
}
.update-banner .ub-close:hover {
  background: var(--bg-hover);
  color: var(--text);
}

/* "立即更新" 主按钮——accent fill，主推这个动作 */
.update-banner .ub-upgrade {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: 24px;
  padding: 0 12px;
  margin-left: auto;
  border: 1px solid var(--accent);
  border-radius: 6px;
  background: var(--accent);
  color: var(--on-accent);
  font-size: 11.5px;
  font-weight: 500;
  cursor: pointer;
  transition: filter var(--dur-fast, 120ms), transform var(--dur-fast, 120ms);
}
.update-banner .ub-upgrade:hover { filter: brightness(1.08); }
.update-banner .ub-upgrade:active { transform: translateY(1px); }
.update-banner .ub-upgrade:disabled { opacity: 0.5; cursor: default; }
.update-banner .ub-upgrade svg { display: block; }
/* idle 时主按钮居右靠拢，命令块紧随其后（margin-left: auto 已在 ub-upgrade 上） */
.update-banner .ub-upgrade + .ub-cmd { margin-left: 0; }
.update-banner.state-error .ub-cmd { margin-left: 0; }

/* busy 模式：阶段文字 */
.update-banner .ub-phase {
  color: var(--text);
  font-weight: 500;
}

/* indeterminate 进度条 */
.update-banner .ub-progress {
  flex: 1;
  margin-left: auto;
  max-width: 360px;
  height: 4px;
  background: var(--bg-hover);
  border-radius: 999px;
  overflow: hidden;
  position: relative;
}
.update-banner .ub-progress-bar {
  position: absolute;
  top: 0; left: 0; bottom: 0;
  width: 35%;
  background: var(--accent);
  border-radius: 999px;
  animation: ub-prog 1.6s cubic-bezier(.4,0,.2,1) infinite;
}
@keyframes ub-prog {
  0%   { left: -35%; width: 35%; }
  50%  { left: 30%;  width: 50%; }
  100% { left: 100%; width: 35%; }
}

/* "查看日志" 折叠按钮 */
.update-banner .ub-log-toggle {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  height: 24px;
  padding: 0 8px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: transparent;
  color: var(--text-dim);
  font-size: 11px;
  cursor: pointer;
  transition: border-color var(--dur-fast), background var(--dur-fast), color var(--dur-fast);
}
.update-banner .ub-log-toggle:hover {
  border-color: var(--accent);
  color: var(--text);
  background: var(--bg-hover);
}
.update-banner .ub-log-toggle .ub-chev {
  transition: transform var(--dur-fast);
}
.update-banner.log-open .ub-log-toggle .ub-chev {
  transform: rotate(180deg);
}

/* 实时日志区——折叠时高度 0 */
.update-banner .ub-log {
  flex: 1;
  margin: 0;
  padding: 0;
  overflow: auto;
  font-family: var(--font-mono);
  font-size: 11px;
  line-height: 1.55;
  color: var(--text-dim);
  background: var(--bg);
  border-top: 1px solid var(--border);
  white-space: pre-wrap;
  max-height: 0;
  transition: max-height var(--dur, 200ms) var(--ease);
}
.update-banner.log-open .ub-log {
  max-height: 200px;
  padding: 8px 16px;
}
.update-banner .ub-log .log-line { display: block; }
.update-banner .ub-log .log-line.stderr { color: var(--text); }

/* error 模式 */
.update-banner.state-error { border-bottom-color: var(--danger); }
.update-banner.state-error .ub-stripe { background: var(--danger); }
.update-banner .ub-error-text {
  color: var(--danger);
  font-weight: 500;
  margin-left: auto;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 380px;
}
.update-banner .ub-retry {
  display: inline-flex;
  align-items: center;
  height: 24px;
  padding: 0 10px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--bg-elev);
  color: var(--text);
  font-size: 11.5px;
  cursor: pointer;
  transition: border-color var(--dur-fast), background var(--dur-fast);
}
.update-banner .ub-retry:hover { border-color: var(--accent); background: var(--bg-hover); }

/* 态切换（必须放在所有按钮/元素具体样式之后，否则被覆盖）。
 * 默认隐藏 busy / error 元素；切到对应态时再显示。
 * .ub-progress 是 div（非 inline-flex），用 flex 显示。
 */
.update-banner .ub-when-busy,
.update-banner .ub-when-error { display: none !important; }
.update-banner.state-busy .ub-when-idle,
.update-banner.state-busy .ub-when-error { display: none !important; }
.update-banner.state-busy .ub-when-busy:not(.ub-progress) { display: inline-flex !important; }
.update-banner.state-busy .ub-progress { display: block !important; }
.update-banner.state-error .ub-when-idle,
.update-banner.state-error .ub-when-busy { display: none !important; }
.update-banner.state-error .ub-when-error { display: inline-flex !important; }
/* error 模式下 .ub-error-text 是 span，要求 inline 即可 */
.update-banner.state-error .ub-error-text { display: inline-block !important; }

.preview {
  flex: 1;
  position: relative;
  background: light-dark(#fff, #1a1a1a);
  /* 横向裁掉冻结锁宽时超出的部分；纵向必须可滚——因为很多 HTML
     会把自己整体撑开（document height 直接 = 内容高度），不在 iframe 内部滚动，
     此时必须由外层容器提供纵向滚动，否则用户完全无法向下查看。 */
  overflow-x: hidden;
  overflow-y: auto;
}
/* 每个 Tab 一个常驻 iframe。布局与旧的单例 #preview 完全一致：
   同一时刻只有活动 Tab 的那个不带 .hidden，其余 display:none 退出布局流，
   于是活动帧的表现和过去单 iframe 时期分毫不差（外层滚动那套逻辑照旧成立）。 */
#preview,
.preview-frame {
  width: 100%;
  height: 100%;
  border: 0;
  display: block;
  opacity: 1;
  transition: opacity var(--dur-fast) ease;
}
#preview.hidden,
.preview-frame.hidden { display: none; }
#preview.loading,
.preview-frame.loading { opacity: 0; }

/* ==================== 多 Tab 标签栏 ==================== */
/* 夹在 main-header 与预览区之间的一条水平标签条。无 Tab 时 .hidden 完全不占位，
   避免首页凭空多出一条空栏。标签多到放不下时整条横向滚动（细滚动条）。 */
.tab-bar {
  flex: 0 0 auto;
  position: relative;   /* 滑动指示层 .tab-indicator 的定位锚点 */
  display: flex;
  align-items: center;
  gap: 3px;
  height: 42px;
  padding: 0 8px;
  background: var(--bg-elev);
  border-bottom: 1px solid var(--border);
  overflow-x: auto;
  overflow-y: hidden;
  scrollbar-width: thin;
}
.tab-bar.hidden { display: none; }
.tab-bar::-webkit-scrollbar { height: 6px; }
.tab-bar::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }

/* 单枚标签：iOS / macOS 分段控件式的胶囊 chip，取代旧的"浏览器 tab 描边"。
 * 未选中 = 纯 ghost 文本；选中 = 一枚带 accent 淡填充的圆角胶囊——没有描边、
 * 没有单边 accent 线，选中感全靠"填充 + 悬停微浮起"，和系统分段控件同一套语言。 */
.tab {
  flex: 0 1 200px;
  min-width: 92px;
  max-width: 240px;
  height: 30px;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 0 4px 0 10px;
  border: none;
  border-radius: var(--r-lg);
  background: transparent;
  color: var(--text-dim);
  font-size: 12px;
  cursor: pointer;
  user-select: none;
  white-space: nowrap;
  position: relative;
  z-index: 1;   /* 浮在滑动指示层之上，文字 / 徽标 / 关闭键都可见可点 */
  transition: background var(--dur-fast) ease, color var(--dur-fast) ease,
              transform var(--dur-fast) var(--ease-spring);
}
/* 未选中的 hover：轻轻上浮一点点 + 半透明品牌色晕，和顶栏图标同一套 hover 语言 */
.tab:hover:not(.active) {
  background: color-mix(in srgb, var(--accent) 8%, transparent);
  color: var(--text);
  transform: translateY(-1px);
}
.tab.active {
  background: transparent;   /* 选中填充改由常驻的滑动指示层 .tab-indicator 提供 */
  color: var(--text);
  font-weight: var(--fw-semi);
}
.tab:focus-visible { outline: none; box-shadow: var(--ring); }

/* 滑动指示层：常驻一枚 accent 淡填充胶囊，切换标签时平滑滑到新标签的位置
 * （iOS 分段控件里那块会滑动的选中滑块）。left/width 由 JS positionTabIndicator 设置，
 * 首次出现不滑（无动画就位后淡入），之后每次切换都用弹簧曲线滑过去。 */
.tab-indicator {
  position: absolute;
  top: 6px; bottom: 6px; left: 0;
  width: 0;
  border-radius: var(--r-lg);
  background: var(--bg-active);
  opacity: 0;
  pointer-events: none;
  z-index: 0;
  transition: transform var(--dur) var(--ease-spring),
              width var(--dur) var(--ease-spring),
              opacity var(--dur-fast) ease;
}
.tab-indicator.show { opacity: 1; }
/* 复用全站类型徽标（.file-type-badge.type-*）的配色，标签里略微收窄留白 */
.tab .file-type-badge { flex: 0 0 auto; opacity: 0.8; }
.tab.active .file-type-badge { opacity: 1; }
.tab-name {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* 关闭按钮：常态低调，hover 标签或 hover 按钮本身时才醒目 */
.tab-close {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  padding: 0;
  border: 0;
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--text-dim);
  cursor: pointer;
  opacity: 0.55;
  transition: background var(--dur-fast) ease, opacity var(--dur-fast) ease;
}
.tab-close:hover { background: color-mix(in srgb, var(--accent) 15%, transparent); opacity: 1; color: var(--accent); }
.tab-close svg { display: block; }
/* ==================== 首页（无文档打开时） ==================== */
.empty {
  position: absolute; inset: 0;
  display: flex;
  align-items: flex-start; justify-content: center;
  overflow-y: auto;
  background: var(--bg);
  color: var(--text-dim);
  font-size: 13px;
}
.empty.hidden { display: none; }
/* 星图态（showHomeSurface 给 .home 加 hidden）必须真正移出布局：只靠 #starmap-view 实底盖住时，
 * 河流手柄（z-index:2）会透到星图上抢点击，被盖住的首页控件也还在 Tab 序列里。
 * 河流手柄 / 选区按百分比定位，回到封面墙时 renderHome() 重画即可，不依赖隐藏期间的尺寸 */
.home.hidden { display: none; }

/* max-width 从 880 提到 1120：1440 宽的屏幕上原来右侧空着一大块，
 * 而这一屏恰恰是信息最该铺开的地方（封面墙越宽，一屏能看到的越多）。 */
.home {
  width: 100%;
  max-width: 1120px;
  padding: 40px 32px 40px;
  display: flex;
  flex-direction: column;
  gap: var(--sp-6);
}

/* ---------- 「今日」英雄区 ----------
 * 取代四个 KPI 数字方块。这个 dashboard 真正要回答的是"AI 今天动了什么"——
 * 于是用一句大标题把它讲出来，数字（喷了品牌渐变）是句子的一部分而不是一格格
 * 仪表。背后垫一层极淡品牌光晕给首页一个焦点，正文阅读区一律不碰这层光。 */
/* 左栏（眉标 / 标题 / 副标题 / 迷你星图槽位）+ 右栏 208px 竖排读数（design §10.1）。
 * 视口 < 1100px 时右栏折到下方；< 700px 时右栏隐藏、改显示 chip 行。 */
.home-hero {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr) 208px;
  border: 1px solid var(--border);
  border-radius: var(--r-xl);
  background: var(--bg-elev);
  overflow: hidden;
  isolation: isolate;
}
.home-hero-body { padding: 24px 28px; }
.home-hero-eyebrow-row { display: flex; align-items: center; gap: 10px; }
.home-hero-eyebrow-sep { width: 18px; height: 1px; background: var(--border-strong); flex: none; }
.home-clock {
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  font-weight: var(--fw-semi);
  color: var(--text-faint);
}
.home-minimap.hidden { display: none; }
.home-readouts {
  display: flex; flex-direction: column;
  padding: 18px 18px;
  border-left: 1px solid var(--hairline);
}
.home-readout {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  min-height: 44px;
  border-bottom: 1px solid var(--hairline);
}
.home-readout:last-child { border-bottom: 0; }
.home-readout-k {
  display: inline-flex; align-items: center; gap: 6px; min-width: 0;
  font-size: var(--fs-sm); font-weight: var(--fw-medium); color: var(--text-dim);
}
.home-readout-tag {
  font-size: 10px; font-weight: var(--fw-normal); color: var(--text-faint);
  padding: 0 5px; border: 1px solid var(--border); border-radius: var(--r-full);
  white-space: nowrap;
}
.home-readout-v {
  font-family: var(--font-mono);
  font-size: 20px; font-weight: 600;
  color: var(--text);
}
.home-readout.is-signal .home-readout-v { color: var(--accent); }
/* ≥700px 读数栏在，chip 行就是重复信息；窄屏读数栏隐藏时 chip 回来 */
@media (min-width: 700px) { .home-hero .home-chips { display: none; } }
@media (max-width: 1099px) {
  .home-hero { grid-template-columns: minmax(0, 1fr); }
  .home-readouts {
    flex-direction: row; flex-wrap: wrap;
    padding: 4px 28px 12px;
    border-left: 0; border-top: 1px solid var(--hairline);
  }
  .home-readout { flex: 1 1 140px; border-bottom: 0; padding-right: 16px; }
}
@media (max-width: 699px) { .home-readouts { display: none; } }
.home-hero-glow {
  position: absolute;
  z-index: -1;
  top: -60%; right: -8%;
  width: 460px; height: 460px;
  background: radial-gradient(circle, var(--brand-glow), transparent 62%);
  pointer-events: none;
}
.home-hero-body { min-width: 0; display: flex; flex-direction: column; gap: 8px; }
.home-hero-eyebrow {
  font-size: var(--fs-xs); font-weight: var(--fw-semi);
  letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--text-faint);
}
.home-hero-line {
  margin: 0;
  font-size: var(--fs-hero);
  font-weight: var(--fw-bold);
  line-height: 1.1;
  letter-spacing: -.02em;
  color: var(--text);
  text-wrap: balance;
}
/* 数字喷品牌渐变——首页唯一一处彩色文字，作为视觉落点 */
.home-hero-line .n {
  font-variant-numeric: tabular-nums;
  /* 渐变文字专用令牌：浅色起点压深，34px 大字对底 ≥3:1（--brand-grad 只给非文字品牌面） */
  background: var(--brand-text-grad);
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent;
}
/* "全部看过了"那种数字不该喷渐变——0 篇待看不是喜事需要庆祝的对象 */
.home-hero-line .n.calm {
  background: none; -webkit-text-fill-color: currentColor; color: var(--text);
}
.home-hero-sub { margin: 0; font-size: var(--fs-md); color: var(--text-dim); }
.home-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 4px; }
.home-chip {
  display: inline-flex; align-items: baseline; gap: 5px;
  padding: 4px 10px;
  border-radius: var(--r-full);
  background: var(--bg-elev-2);
  border: 1px solid var(--border);
  font-size: var(--fs-sm); color: var(--text-dim);
}
.home-chip b { font-weight: var(--fw-bold); color: var(--text); font-variant-numeric: tabular-nums; }


/* ---------- 待看：封面图卡流 ----------
 * 原来 793 篇文档每一篇只是"图标 + 一行字"，这是最像上世纪文件管理器的地方。
 * 给每篇一张按路径哈希生成的渐变封面（同一项目同色系），列表就变成杂志式的
 * 图卡墙。封面不是截图（截 793 篇代价太大、还要后端管线），而是稳定的生成图——
 * 既有视觉密度又零成本，主题切换也是纯 CSS 跟着走。 */
.home-feed { display: flex; flex-direction: column; gap: var(--sp-3); }
.home-feed-head {
  display: flex; align-items: center; gap: 8px;
  color: var(--text);
  font-size: var(--fs-lg); font-weight: var(--fw-bold); letter-spacing: -.01em;
}
.home-feed-head .ico { color: var(--text-faint); }
.home-feed-count {
  font-size: var(--fs-sm); font-weight: var(--fw-medium);
  color: var(--text-faint); font-variant-numeric: tabular-nums;
}
.home-feed-hint {
  margin-left: auto;
  font-size: var(--fs-sm); font-weight: var(--fw-normal); color: var(--text-faint);
}
.home-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(212px, 1fr));
  gap: var(--sp-3);
}
.cover-card {
  display: flex; flex-direction: column;
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  background: var(--bg-elev);
  overflow: hidden;
  cursor: pointer;
  text-align: left;
  font-family: inherit;
  color: var(--text);
  padding: 0;
  transition: transform var(--dur) var(--ease-spring),
              box-shadow var(--dur), border-color var(--dur);
  will-change: transform;
}
.cover-card:hover {
  transform: translateY(-3px);
  box-shadow: var(--shadow-2);
  border-color: var(--border-strong);
}
.cover-card:focus-visible { outline: none; box-shadow: var(--ring); }
.cover-art {
  position: relative;
  height: 96px;
  display: flex; align-items: center; justify-content: center;
  overflow: hidden;
  /* 兜底封面单色（design §10.8）：不再按项目名哈希色相，全界面只留一个品牌强调色 */
  background: var(--bg-elev-2);
}
/* 大号类型字形水印：中性弱色，给封面一点"内容感" */
.cover-art .cover-glyph { color: var(--text-faint); }
/* 极淡斜纹，避免纯色块太"塑料" */
.cover-art::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: repeating-linear-gradient(45deg,
    color-mix(in srgb, var(--text) 3%, transparent) 0 8px, transparent 8px 16px);
}
.cover-badge {
  position: absolute; top: 8px; left: 8px;
  display: inline-flex; align-items: center;
  height: 18px; padding: 0 7px;
  border-radius: var(--r-full);
  /* 半透明着色层（--glass-tint，见令牌区）；叠在缩略图上，只着色不模糊 */
  background: color-mix(in srgb, var(--bg-elev) var(--glass-tint), transparent);
  color: var(--text); font-size: 10px; font-weight: var(--fw-semi); letter-spacing: .04em;
}
/* 状态点统一：6px 实心、无外环（新鲜 = --unread；刚变更的轻闪见动效段 .just-changed） */
.cover-fresh {
  position: absolute; top: 8px; right: 8px;
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--unread);
}
.cover-body { display: flex; flex-direction: column; gap: 4px; padding: 10px 12px 12px; }
.cover-name {
  font-size: var(--fs-base); font-weight: var(--fw-semi); line-height: 1.35;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.cover-meta {
  display: flex; align-items: center; gap: 6px;
  font-size: var(--fs-xs); color: var(--text-faint); font-variant-numeric: tabular-nums;
}
.cover-meta .cover-proj {
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text-dim);
}
.cover-meta .cover-dot { flex: none; width: 5px; height: 5px; border-radius: 50%; background: var(--text-faint); }

/* ---------- 最近 / 收藏 轨道 ----------
 * 降为次要信息：两列紧凑列表，复用 .home-row。封面墙是主角，这两条只是
 * "顺手回去"的入口，不再和待看抢版面。 */
.home-rails {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--sp-3);
  align-items: start;
}
@media (max-width: 860px) { .home-rails { grid-template-columns: minmax(0, 1fr); } }
.home-rail {
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  background: var(--bg-elev);
  overflow: hidden;
}
.home-rail.hidden { display: none; }
.home-rail-head {
  display: flex; align-items: center; gap: 6px;
  padding: 9px 14px;
  background: var(--bg-elev-2);
  border-bottom: 1px solid var(--border);
  color: var(--text-dim);
  font-size: var(--fs-xs); font-weight: var(--fw-semi);
  letter-spacing: 0.06em;
}
.home-rail-head .ico { color: var(--text-faint); }
.home-list { padding: 6px; display: flex; flex-direction: column; gap: 1px; }

.home-row {
  display: flex; align-items: center; gap: 8px;
  width: 100%;
  padding: 6px 8px;
  border: none;
  background: transparent;
  border-radius: var(--r-sm);
  color: var(--text);
  font-family: inherit; font-size: var(--fs-base);
  text-align: left;
  cursor: pointer;
  transition: background var(--dur-fast);
}
.home-row:hover { background: var(--bg-hover); }
.home-row-dot {
  flex: none; width: 6px; height: 6px; border-radius: 50%;
  background: var(--unread);
  visibility: hidden;
}
.home-row.unread .home-row-dot { visibility: visible; }
/* 和侧栏一致：陈旧未读（>24h）降级成灰点 + 常规字重。
 * 「待看」卡片是按 mtime 倒序的，于是未读点自然聚在顶部几行——
 * 这一列点从红渐灰，本身就成了"新鲜度"的刻度。 */
.home-row.unread .home-row-name { font-weight: var(--fw-semi); }
.home-row.unread.stale .home-row-dot { background: var(--unread-stale); }
.home-row.unread.stale .home-row-name { font-weight: var(--fw-normal); }
.home-row-ico { flex: none; color: var(--text-faint); display: inline-flex; }
.home-row-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.home-row-proj {
  flex: none; max-width: 34%;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: var(--fs-sm); color: var(--text-faint);
}
.home-row-time {
  flex: none; font-size: var(--fs-xs); color: var(--text-faint);
  font-variant-numeric: tabular-nums;
}
.home-more {
  padding: 8px 9px;
  font-size: var(--fs-sm);
  color: var(--text-faint);
}

/* 空状态三件套：图标 + 为什么空 + 下一步能做什么。
 * 竖向居中还有个副作用是好的——它把卡片撑到 ~130px，和左边那张有 7 行内容的
 * 「待看」卡片高度落差没那么突兀，不用强行 stretch 去拉一片空白出来。 */
.home-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 5px;
  padding: 26px 20px 24px;
}
.home-empty-ico {
  display: inline-flex;
  color: var(--text-faint);
  opacity: .7;
  margin-bottom: 2px;
}
.home-empty-title {
  font-size: var(--fs-base);
  font-weight: var(--fw-semi);
  color: var(--text-dim);
}
.home-empty-hint {
  font-size: var(--fs-sm);
  color: var(--text-faint);
  max-width: 34ch;
  line-height: var(--lh-base);
  /* 避免折行后末行只剩一个字（"…跨项目秒 / 回"）。
   * 不支持 pretty 的浏览器退化成默认换行，无副作用。 */
  text-wrap: pretty;
}
.home-empty-act {
  margin-top: 8px;
  display: inline-flex; align-items: center; gap: 6px;
  height: var(--h-ctl);
  padding: 0 10px;
  border: 1px solid var(--border-strong);
  border-radius: var(--r-sm);
  background: var(--bg-elev);
  color: var(--text-dim);
  font: inherit;
  font-size: var(--fs-sm);
  cursor: pointer;
  transition: background var(--dur-fast), border-color var(--dur-fast), color var(--dur-fast);
}
.home-empty-act:hover {
  background: var(--bg-hover);
  border-color: var(--text-faint);
  color: var(--text);
}

/* ---------- 快捷键提示 ---------- */
/* 原来是一条裸文字挂在 border-top 下面，看着像页脚残留：三组提示之间只有
 * 16px 空隙，键帽和说明文字混在一起，没有"这是一个整体"的边界。
 * 给它一个 elev-2 的底 + 圆角，它就成为一个明确的提示条区块（Raycast 面板
 * 底部那条 footer 也是这个思路），线也就不需要了。 */
.home-tips {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px 22px;
  padding: 10px 14px;
  background: var(--bg-elev-2);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  font-size: var(--fs-sm); color: var(--text-faint);
}
.home-tips span { display: inline-flex; align-items: center; gap: 6px; }
/* 「全部快捷键」按钮的样式在下方「首页提示行末尾的入口」一节，不在这里重复 */

@media (max-width: 720px) {
  .home { padding: 28px 18px; }
  .home-hero { flex-direction: column; align-items: stretch; gap: var(--sp-4); }
  .home-tips-more span { display: none; }
}
/* 键帽：全站统一一种。
 * 去掉原来 2px 的底边"凸起"——在 10px 字号下那条粗边比字本身还显眼，
 * 一排键帽会比它们要提示的文字更重。改成均匀 1px 淡边（Raycast 的键帽
 * 就是这种"轻到几乎只是个底色块"的处理）。 */
kbd {
  display: inline-block;
  min-width: 17px;
  text-align: center;
  background: var(--bg-elev-2);
  border: 1px solid var(--border);
  border-radius: var(--r-xs);
  padding: 1px 5px;
  color: var(--text-dim);
  font-family: var(--font-mono);
  font-size: 10px;
  line-height: 1.5;
}

/* modal */
.modal {
  position: fixed; inset: 0;
  display: flex; align-items: center; justify-content: center;
  z-index: 100;
}
.modal.hidden { display: none; }
.modal-backdrop {
  position: absolute; inset: 0;
  background: light-dark(rgba(23, 31, 46, .32), rgba(0, 0, 0, .58));
  -webkit-backdrop-filter: blur(3px) saturate(.9);
  backdrop-filter: blur(3px) saturate(.9);
}
.modal-panel {
  position: relative;
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  width: 540px;
  max-width: calc(100vw - 32px);
  max-height: calc(100vh - 60px);
  overflow-y: auto;
  box-shadow: var(--shadow-3);
}
.modal-header {
  display: flex; align-items: center; justify-content: space-between;
  padding: 12px 12px 12px 18px;
  border-bottom: 1px solid var(--border);
  flex: none;
}
.modal-header h2 { margin: 0; font-size: 14.5px; font-weight: 650; }
.modal .modal-close {
  display: inline-flex; align-items: center; justify-content: center;
  background: transparent;
  border: 1px solid transparent;
  color: var(--text-dim);
  cursor: pointer;
  width: var(--h-ctl); height: var(--h-ctl);
  border-radius: var(--r-sm);
  transition: background var(--dur-fast), color var(--dur-fast);
}
.modal .modal-close:hover { background: var(--bg-hover); color: var(--text); border-color: transparent; }
/* ---------- 设置弹窗：左导航 + 右内容 ---------- */
.settings-panel {
  width: 740px;
  display: flex;
  flex-direction: column;
  /* 面板本身不滚动，滚动交给右侧内容区，标题栏与导航才能固定住 */
  overflow: hidden;
  max-height: min(660px, calc(100vh - 80px));
}
.settings-body {
  display: grid;
  grid-template-columns: 168px minmax(0, 1fr);
  min-height: 0;
  flex: 1;
}
.settings-nav {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 10px 8px;
  border-right: 1px solid var(--border);
  background: var(--bg);
  overflow-y: auto;
}
.settings-nav .settings-nav-item {
  display: flex; align-items: center; justify-content: flex-start; gap: 8px;
  width: 100%;
  height: 30px;
  min-width: 0;
  padding: 0 9px;
  border: 1px solid transparent;
  background: transparent;
  color: var(--text-dim);
  font-family: inherit;
  font-size: 12.5px;
  text-align: left;
  border-radius: var(--r-sm);
  cursor: pointer;
  transition: background var(--dur-fast), color var(--dur-fast);
}
.settings-nav .settings-nav-item .ico { color: var(--text-faint); transition: color var(--dur-fast); }
.settings-nav .settings-nav-item:hover { background: var(--bg-hover); color: var(--text); border-color: transparent; }
.settings-nav .settings-nav-item.is-on {
  background: var(--accent-soft);
  color: var(--accent);
  border-color: transparent;
  font-weight: 600;
}
.settings-nav .settings-nav-item.is-on .ico { color: var(--accent); }
.settings-content { overflow-y: auto; min-height: 0; }
.settings-nav .settings-version {
  /* 左栏底部信息区起点：一条上分隔线把它和上面的分区项分开，版本号本身弱化。 */
  margin-top: auto;
  padding: 10px 10px 3px;
  border-top: 1px solid var(--border);
  font-size: 11px;
  line-height: 1.5;
  color: var(--text-faint);
  letter-spacing: 0.02em;
  word-break: break-word;
}
/* 版本号下方的低调操作：更新日志（所有版本）+ 检查更新（仅桌面版）。
   统一成"小图标 + 文字、hover 才有底色、无边框"的轻量入口，
   而不是两个描边方块按钮堆在一起（那样和版本 / 状态文字挤成一团很乱）。 */
.settings-nav .settings-changelog-btn,
.settings-nav .settings-update-btn {
  display: flex;
  align-items: center;
  gap: 6px;
  width: 100%;
  height: auto;
  min-width: 0;
  margin: 0;
  padding: 5px 10px;
  border: none;
  background: transparent;
  color: var(--text-dim);
  font-family: inherit;
  font-size: 12px;
  text-align: left;
  border-radius: var(--r-sm);
  cursor: pointer;
  transition: background var(--dur-fast), color var(--dur-fast);
}
.settings-nav .settings-changelog-btn:hover,
.settings-nav .settings-update-btn:hover:not(:disabled) {
  background: var(--bg-hover);
  color: var(--accent);
}
.settings-nav .settings-changelog-btn .ico { color: var(--accent); flex: none; }
/* 「检查更新」没有前置图标（它的文字会被状态更新用 textContent 重写，放不住图标），
   用左缩进让它的文字和「更新日志」（图标之后）的文字左对齐：10 基 + 13 图标 + 6 间隔。 */
.settings-nav .settings-update-btn { padding-left: 29px; }
.settings-nav .settings-changelog-btn:focus-visible,
.settings-nav .settings-update-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* 桌面版「检查更新」控件容器（紧贴更新日志下方） */
.settings-nav .settings-update {
  padding: 0 0 10px;
  display: flex;
  flex-direction: column;
  gap: 1px;
}
.settings-nav .settings-update.hidden { display: none; }
.settings-nav .settings-update-status {
  padding: 1px 10px 0;
  font-size: 11px;
  line-height: 1.5;
  color: var(--text-faint);
  word-break: break-word;
}
.settings-nav .settings-update-status.is-error { color: var(--danger); }

.modal-section {
  padding: 16px 18px;
  border-bottom: 1px solid var(--border);
}
.modal-section:last-child { border-bottom: none; }
/* 同一分区里最后一段不画分隔线（:last-child 判不出「最后一个可见的」） */
.modal-section[hidden] { display: none; }
.settings-content .modal-section:not([hidden]):has(+ .modal-section[hidden]) { border-bottom: none; }
@media (max-width: 700px) {
  .settings-panel { width: 100%; }
  .settings-body { grid-template-columns: minmax(0, 1fr); }
  .settings-nav {
    flex-direction: row;
    overflow-x: auto;
    border-right: none;
    border-bottom: 1px solid var(--border);
  }
    .settings-nav .settings-nav-item { width: auto; flex: none; }
}
.modal-section h3 { margin: 0 0 6px; font-size: 13px; color: var(--text); font-weight: 650; }
.modal-section .muted { margin: 4px 0 10px; color: var(--text-dim); font-size: 12px; line-height: 1.55; }
.modal-section input[type="text"] {
  width: 100%;
  background: var(--bg-elev-2);
  border: 1px solid var(--border-input);
  border-radius: var(--r-sm);
  color: var(--text);
  padding: 7px 10px;
  outline: none;
  font-size: 12px;
  font-family: inherit;
  transition: border-color var(--dur-fast), box-shadow var(--dur-fast);
}
.modal-section input[type="text"]:focus { border-color: var(--accent); box-shadow: var(--ring); }
.modal-section .row { display: flex; align-items: center; gap: 8px; cursor: pointer; user-select: none; }
.modal-section .row-end { display: flex; justify-content: flex-end; margin-top: 8px; }

.root-list { list-style: none; padding: 0; margin: 0 0 10px; }
.root-list li {
  display: flex; align-items: center; gap: 8px;
  padding: 7px 8px 7px 11px;
  background: var(--bg-elev-2);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  margin-bottom: 5px;
  font-family: var(--font-mono);
  font-size: 11px;
}
.root-list li .root-path { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.modal .root-list li button {
  height: 22px; min-width: 22px;
  padding: 0 6px;
  font-size: 11px;
}
.root-add { display: flex; gap: 6px; flex-wrap: wrap; }
.root-add input { flex: 1; min-width: 180px; }

/* 已归档分组列表 */
.archive-list { list-style: none; padding: 0; margin: 0; }
.archive-list li {
  display: flex; align-items: center; gap: 8px;
  padding: 6px 10px;
  background: var(--bg-elev-2);
  border-radius: 6px;
  margin-bottom: 4px;
  font-size: 12px;
}
.archive-list li.archive-empty {
  background: transparent;
  color: var(--text-faint);
  font-size: 11.5px;
  padding: 4px 0;
  font-style: italic;
}
.archive-list .archive-name {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--text);
}
.archive-list .archive-count {
  flex: none;
  color: var(--text-faint);
  font-size: 11px;
  font-variant-numeric: tabular-nums;
}
.archive-list .archive-restore {
  height: 24px;
  padding: 0 10px;
  font-size: 11.5px;
  border: 1px solid var(--border);
  background: var(--bg-elev);
  color: var(--text);
  border-radius: 5px;
  cursor: pointer;
  transition: border-color var(--dur-fast), background var(--dur-fast), color var(--dur-fast);
}
.archive-list .archive-restore:hover {
  border-color: var(--accent);
  color: var(--accent);
  background: var(--bg-hover);
}

/* 已分享文件列表 */
.modal-section .section-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 4px;
}
.modal-section .section-head h3 { margin: 0; }
.modal .share-stop-all-btn {
  height: 24px;
  padding: 0 10px;
  border: 1px solid var(--danger);
  background: transparent;
  color: var(--danger);
  border-radius: 5px;
  font-size: 11px;
  cursor: pointer;
  transition: background var(--dur-fast), color var(--dur-fast);
}
.modal .share-stop-all-btn:hover { background: var(--danger); color: light-dark(#fff, #1a0508); }
.modal .share-stop-all-btn:disabled { opacity: 0.4; cursor: default; }
.share-list { list-style: none; padding: 0; margin: 0; }
.share-list li {
  display: flex; align-items: center; gap: 8px;
  padding: 6px 10px;
  background: var(--bg-elev-2);
  border-radius: 6px;
  margin-bottom: 4px;
  font-size: 12px;
}
.share-list li.share-list-empty {
  background: transparent;
  color: var(--text-faint);
  font-size: 11.5px;
  padding: 4px 0;
  font-style: italic;
}
.share-list .share-list-name {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--text);
}
.share-list .share-list-url {
  flex: none;
  font-family: var(--font-mono);
  font-size: 10.5px;
  color: var(--text-faint);
  max-width: 180px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.share-list .share-list-stop {
  height: 22px;
  padding: 0 8px;
  font-size: 11px;
  border: 1px solid var(--border);
  background: var(--bg-elev);
  color: var(--text);
  border-radius: 5px;
  cursor: pointer;
  transition: border-color var(--dur-fast), color var(--dur-fast);
}
.share-list .share-list-stop:hover { border-color: var(--danger); color: var(--danger); }

/* 目录浏览器 */
.dir-picker {
  margin-top: 10px;
  background: var(--bg-elev-2);
  border: 1px solid var(--border);
  border-radius: 8px;
  overflow: hidden;
}
.dir-picker.hidden { display: none; }
.dir-picker-bar {
  display: flex; gap: 6px; align-items: center;
  padding: 8px 10px;
  background: var(--bg-elev);
  border-bottom: 1px solid var(--border);
}
.dir-picker-bar button {
  height: 26px; min-width: 26px;
  padding: 0 8px;
  border: 1px solid var(--border);
  background: var(--bg-elev-2);
  color: var(--text);
  border-radius: 5px;
  cursor: pointer;
  font-size: 12px;
}
.dir-picker-bar button:hover { background: var(--bg-hover); }
.dir-current {
  flex: 1;
  background: var(--bg-elev-2);
  border: 1px solid var(--border-input);
  border-radius: 5px;
  color: var(--text);
  padding: 4px 8px;
  outline: none;
  font-family: var(--font-mono);
  font-size: 11px;
}
.dir-current:focus { border-color: var(--accent); }
.dir-list {
  max-height: 260px;
  overflow-y: auto;
  padding: 4px 6px;
}
.dir-list .dir-item {
  display: flex; align-items: center; gap: 8px;
  padding: 5px 8px;
  border-radius: 5px;
  cursor: pointer;
  font-size: 12px;
  color: var(--text);
  user-select: none;
}
.dir-list .dir-item:hover { background: var(--bg-hover); }
.dir-list .dir-item.disabled { color: var(--text-faint); cursor: default; }
.dir-list .dir-item.disabled:hover { background: transparent; }
.dir-list .dir-item .dir-icon { font-size: 13px; flex: none; }
.dir-list .dir-empty {
  padding: 24px 8px;
  text-align: center;
  color: var(--text-faint);
  font-size: 12px;
}
.dir-picker-footer {
  display: flex; gap: 8px; justify-content: flex-end;
  padding: 8px 10px;
  background: var(--bg-elev);
  border-top: 1px solid var(--border);
}
.dir-picker-footer button {
  height: 28px;
  padding: 0 14px;
  border: 1px solid var(--border);
  background: var(--bg-elev-2);
  color: var(--text);
  border-radius: 6px;
  cursor: pointer;
  font-size: 12.5px;
}
.dir-picker-footer button:hover { background: var(--bg-hover); }
.dir-picker-footer button.primary {
  background: var(--accent);
  border-color: var(--accent);
  color: white;
  font-weight: 500;
}
.dir-picker-footer button.primary:hover {
  background: #6ba8ff;
}
.dir-picker-footer button.primary:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

/* Toast 通知 */
.toast-container {
  position: fixed;
  bottom: 24px;
  right: 24px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  z-index: 200;
  pointer-events: none;
}
.toast {
  position: relative;
  pointer-events: auto;
  display: flex;
  align-items: center;
  gap: 10px;
  background: var(--bg-elev);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-lg);
  padding: 11px 14px 11px 12px;
  font-size: 13px;
  color: var(--text);
  max-width: 380px;
  box-shadow: var(--shadow-2);
  animation: toast-in 220ms cubic-bezier(.32,.72,0,1);
}
.toast.fading {
  animation: toast-out 220ms cubic-bezier(.32,.72,0,1) forwards;
}
.toast .toast-icon {
  width: 20px; height: 20px;
  border-radius: 50%;
  display: grid; place-items: center;
  flex: none;
  font-size: 11px;
  font-weight: 700;
}
.toast .toast-icon .ico { stroke-width: 2; }
.toast.success .toast-icon { background: color-mix(in srgb, var(--ok) 16%, transparent); color: var(--ok); }
.toast.error .toast-icon { background: color-mix(in srgb, var(--danger) 16%, transparent); color: var(--danger); }
.toast.info .toast-icon { background: var(--accent-soft); color: var(--accent); }
.toast .toast-msg {
  flex: 1;
  line-height: 1.45;
  word-break: break-all;
}
.toast .toast-msg strong { font-weight: 600; color: var(--text); }
.toast .toast-msg .toast-secondary {
  display: block;
  margin-top: 3px;
  font-size: 11.5px;
  color: var(--text-faint);
}
.toast .toast-msg .toast-action {
  display: inline-block;
  margin-top: 6px;
  padding: 0;
  border: none;
  background: transparent;
  color: var(--accent);
  font-size: 11.5px;
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 2px;
}
.toast .toast-msg .toast-action:hover { filter: brightness(1.15); }

/* progress 模式的 toast：不自动消失，底部一条流动 indeterminate 条 */
.toast.toast-progress {
  padding-bottom: 14px;
}
.toast.toast-progress .toast-icon {
  animation: spin 1.4s linear infinite;
}
.toast.toast-progress .toast-progress-bar {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 3px;
  background: var(--bg-hover);
  overflow: hidden;
  border-radius: 0 0 var(--r-lg) var(--r-lg);
}
.toast.toast-progress .toast-progress-bar > div {
  position: absolute;
  top: 0; left: -35%; bottom: 0;
  width: 35%;
  background: var(--accent);
  border-radius: 999px;
  animation: ub-prog 1.6s cubic-bezier(.4,0,.2,1) infinite;
}
.toast .toast-close {
  display: inline-flex; align-items: center; justify-content: center;
  width: 20px; height: 20px;
  background: transparent;
  border: none;
  color: var(--text-faint);
  cursor: pointer;
  padding: 0;
  border-radius: var(--r-xs);
  flex: none;
  transition: background var(--dur-fast), color var(--dur-fast);
}
.toast .toast-close:hover { color: var(--text); background: var(--bg-hover); }

@keyframes toast-in {
  from { opacity: 0; transform: translate3d(0, 12px, 0) scale(.97); }
  to   { opacity: 1; transform: translate3d(0, 0, 0) scale(1); }
}
@keyframes toast-out {
  to { opacity: 0; transform: translate3d(0, 8px, 0) scale(.97); }
}

/* scrollbar */
.tree::-webkit-scrollbar,
.modal-panel::-webkit-scrollbar { width: 8px; }
.tree::-webkit-scrollbar-thumb,
.modal-panel::-webkit-scrollbar-thumb { background: var(--border); border-radius: 4px; }
.tree::-webkit-scrollbar-thumb:hover,
.modal-panel::-webkit-scrollbar-thumb:hover { background: var(--text-faint); }

/* ---------- 预览区轻量编辑 ---------- */
/* 保存/取消按钮默认隐藏：需覆盖 .main-actions button 的 display（优先级更高） */
.main-actions button.hidden { display: none; }
/* 编辑态：标题栏左侧一条 accent 竖条 + 编辑按钮高亮，提示当前可编辑 */
#btn-edit.active,
.editing-mode #btn-edit {
  color: var(--accent);
  background: var(--accent-soft);
}
.main-actions button.edit-save {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--on-accent);
  font-weight: 600;
}
.main-actions button.edit-save:hover:not(:disabled) {
  background: var(--accent-hover);
  border-color: var(--accent-hover);
}
.main-actions button.edit-save:disabled {
  opacity: 0.6;
}
.main-actions button.edit-cancel {
  color: var(--text-dim);
}
.editing-mode .main-header {
  box-shadow: inset 3px 0 0 0 var(--accent);
}
.editing-mode .preview {
  outline: 2px solid rgba(91, 156, 255, 0.35);
  outline-offset: -2px;
}

/* ==================== Markdown 编辑器（源码 + 实时预览分栏） ==================== */
.md-editor {
  position: absolute;
  inset: 0;
  display: flex;
  background: var(--bg);
  z-index: 2;
  /* 源码栏宽度占比，由分栏拖拽条改写并持久化 */
  --md-split: 50%;
}
.md-editor.hidden { display: none; }
.md-editor-pane {
  flex: 0 0 var(--md-split);
  min-width: 160px;
  display: flex;
  flex-direction: column;
}
.md-preview-pane-wrap {
  flex: 1 1 auto;
  min-width: 160px;
  display: flex;
  flex-direction: column;
}
/* 分栏拖拽条：视觉上是 1px 细线，命中区放宽到 9px 才好抓 */
.md-editor-split {
  flex: 0 0 9px;
  margin: 0 -4px;
  position: relative;
  z-index: 3;
  cursor: col-resize;
  background: transparent;
  border: 0;
  padding: 0;
}
.md-editor-split::before {
  content: '';
  position: absolute;
  top: 0; bottom: 0; left: 4px;
  width: 1px;
  background: var(--border);
  transition: background var(--dur-fast) ease;
}
.md-editor-split:hover::before,
.md-editor-split:focus-visible::before,
.md-editor-split.dragging::before {
  background: var(--accent);
  box-shadow: 0 0 0 1px var(--accent);
}
.md-editor-split:focus-visible { outline: none; }
body.md-splitting { cursor: col-resize; user-select: none; }
body.md-splitting .md-preview-pane { pointer-events: none; }
.md-pane-label {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  min-height: 33px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--text-dim);
  padding: 5px 10px 5px 16px;
  border-bottom: 1px solid var(--border);
  background: var(--bg-elev);
  user-select: none;
}
.md-pane-title { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* ---- 源码栏格式工具条 ---- */
.md-toolbar {
  display: flex;
  align-items: center;
  gap: 2px;
  flex: 0 0 auto;
}
.md-toolbar button {
  min-width: 24px;
  height: 22px;
  padding: 0 5px;
  border: 0;
  border-radius: 5px;
  background: transparent;
  color: var(--text-dim);
  font-size: 11px;
  font-family: inherit;
  font-weight: 500;
  cursor: pointer;
  transition: background var(--dur-fast) ease, color var(--dur-fast) ease;
}
.md-toolbar button:hover { background: var(--bg-hover); color: var(--text); }
.md-toolbar button:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.md-toolbar-sep {
  width: 1px;
  height: 13px;
  margin: 0 3px;
  background: var(--border);
}
.md-outline-toggle {
  flex: 0 0 auto;
  height: 22px;
  padding: 0 8px;
  border: 1px solid var(--border);
  border-radius: 5px;
  background: transparent;
  color: var(--text-dim);
  font: inherit;
  font-size: 11px;
  cursor: pointer;
  transition: background var(--dur-fast) ease, color var(--dur-fast) ease;
}
.md-outline-toggle:hover { background: var(--bg-hover); color: var(--text); }
.md-outline-toggle[aria-pressed="true"] {
  background: var(--bg-active);
  border-color: transparent;
  color: var(--text);
}
.md-outline-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* ---- 编辑器大纲（第三栏） ---- */
.md-outline {
  flex: 0 0 208px;
  min-width: 0;
  display: flex;
  flex-direction: column;
  border-left: 1px solid var(--border);
  background: var(--bg-elev);
  overflow: hidden;
}
.md-outline.hidden { display: none; }
.md-outline-title {
  flex: 0 0 auto;
  min-height: 33px;
  display: flex;
  align-items: center;
  padding: 5px 14px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--text-dim);
  border-bottom: 1px solid var(--border);
  user-select: none;
}
.md-outline-list {
  flex: 1 1 auto;
  overflow-y: auto;
  padding: 8px 6px 24px;
}
.md-outline-item {
  display: block;
  width: 100%;
  text-align: left;
  border: 0;
  background: transparent;
  color: var(--text-dim);
  font: inherit;
  font-size: 12px;
  line-height: 1.5;
  padding: 4px 8px;
  border-radius: 5px;
  cursor: pointer;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  transition: background var(--dur-fast) ease, color var(--dur-fast) ease;
}
.md-outline-item:hover { background: var(--bg-hover); color: var(--text); }
.md-outline-item.active { color: var(--accent); font-weight: 500; }
.md-outline-item:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.md-outline-item[data-level="1"] { padding-left: 8px; }
.md-outline-item[data-level="2"] { padding-left: 18px; }
.md-outline-item[data-level="3"] { padding-left: 28px; }
.md-outline-item[data-level="4"] { padding-left: 38px; }
.md-outline-item[data-level="5"] { padding-left: 46px; }
.md-outline-item[data-level="6"] { padding-left: 54px; }
.md-outline-empty {
  padding: 8px;
  font-size: 11px;
  color: var(--text-faint);
}
/* 源码区三层叠放：高亮带（底） / textarea（中，背景透明） / 测量镜像（不可见） */
.md-source-wrap {
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
  background: var(--bg-elev);
  overflow: hidden;
}
.md-source {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  box-sizing: border-box;
  border: 0;
  outline: none;
  resize: none;
  padding: 16px 18px;
  background: transparent;   /* 让下面的高亮带透出来 */
  color: var(--text);
  font: 13px/1.65 ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  tab-size: 2;
  overflow-wrap: break-word;
  z-index: 2;
}
.md-source::placeholder { color: var(--text-faint); }
.md-source-hl {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  overflow: hidden;
}
.md-source-hl-inner {
  position: absolute;
  top: 0; left: 0; right: 0;
  will-change: transform;
}
.md-source-band {
  position: absolute;
  left: 0; right: 0;
  background: var(--sync-selected-bg);
  border-left: 2px solid var(--sync-selected-bar);
}
.md-source-band.active {
  background: var(--sync-active-bg);
  border-left-color: var(--sync-active-bar);
}
/* 镜像层：盒模型必须和 textarea 完全一致，才能量出正确的软换行位置。
   用 visibility:hidden 而不是 display:none —— 后者量不到几何 */
.md-source-mirror {
  position: absolute;
  top: 0; left: 0;
  visibility: hidden;
  pointer-events: none;
  z-index: -1;
  box-sizing: border-box;
  padding: 16px 18px;
  font: 13px/1.65 ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  tab-size: 2;
  white-space: pre-wrap;
  overflow-wrap: break-word;
  word-break: normal;
}
/* 预览面板跟随系统主题，与只读 iframe 渲染效果一致。
   （原来硬编码 #fff：深色模式下编辑器会变成左半深色 + 右半纯白的割裂观感） */
.md-preview-pane-wrap { background: light-dark(#ffffff, #12141a); }
.md-preview-pane {
  flex: 1 1 auto;
  overflow: auto;
  padding: 24px 28px;
  background: light-dark(#ffffff, #12141a);
}
.editing-mode .md-editor { outline: 2px solid var(--accent-ring); outline-offset: -2px; }

/* ==================== 设置：主题三选一 ==================== */
.theme-choice { display: flex; gap: var(--sp-2); margin-top: 2px; }
.modal .theme-choice .theme-option {
  flex: 1;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px;
  height: auto;
  padding: 14px 8px;
  border: 1px solid var(--border-strong);
  border-radius: var(--r-md);
  background: var(--bg-elev-2);
  color: var(--text-dim);
  font-size: 12px;
  cursor: pointer;
  transition: border-color var(--dur-fast), background var(--dur-fast), color var(--dur-fast);
}
.modal .theme-choice .theme-option:hover { border-color: var(--text-faint); color: var(--text); }
.modal .theme-choice .theme-option.is-on {
  border-color: var(--accent);
  background: var(--accent-soft);
  color: var(--accent);
  font-weight: 600;
}

/* ==================== 设置：文档类型选择 ==================== */
.doctype-choice {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 4px;
}
.doctype-option {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: 8px;
  cursor: pointer;
  transition: border-color var(--dur-fast) ease, background var(--dur-fast) ease;
}
.doctype-option:hover { background: var(--bg-hover); }
.doctype-option input { margin-top: 3px; accent-color: var(--accent); }
.doctype-option:has(input:checked) {
  border-color: var(--accent);
  background: var(--bg-active);
}
.doctype-label { display: flex; flex-direction: column; gap: 2px; font-size: 13px; }
.doctype-label .muted { font-size: 12px; }
/* 次要格式（csv / json / txt / svg）两列排布：六项全部竖排会把这一屏拉得很长，
 * 而它们本来就是"需要时才勾一下"的东西 */
.doctype-choice-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
}
.doctype-more-hint { margin-top: 14px; }

/* ==================== 文档类型区分（HTML / Markdown）==================== */
.file-type-badge {
  flex: none;
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.04em;
  line-height: 1;
  padding: 2px 4px;
  border-radius: 4px;
  border: 1px solid transparent;
  text-transform: uppercase;
  user-select: none;
}
.file-type-badge.type-md {
  color: #2f9e6b;
  background: rgba(74, 222, 128, 0.14);
  border-color: rgba(74, 222, 128, 0.32);
}
.file-type-badge.type-html {
  color: var(--accent);
  background: var(--accent-soft);
  border-color: var(--accent-ring);
}
/* 其余格式（默认不扫，勾选后才出现）。四种各给一个色相，扫目录树时
 * 「哪些是正文、哪些是顺手导出的数据」一眼能分开。饱和度压得比 HTML/MD 低一档：
 * 它们是配角，不该比正文更抢眼。 */
.file-type-badge.type-csv {
  color: light-dark(#8a5a00, #d29922);
  background: light-dark(rgba(210, 153, 34, .12), rgba(210, 153, 34, .16));
  border-color: rgba(210, 153, 34, .3);
}
.file-type-badge.type-json {
  color: light-dark(#6f42c1, #a371f7);
  background: light-dark(rgba(163, 113, 247, .12), rgba(163, 113, 247, .16));
  border-color: rgba(163, 113, 247, .3);
}
.file-type-badge.type-txt {
  color: var(--text-dim);
  background: var(--bg-elev-2);
  border-color: var(--border);
}
.file-type-badge.type-svg {
  color: light-dark(#b02a37, #f85149);
  background: light-dark(rgba(248, 81, 73, .1), rgba(248, 81, 73, .16));
  border-color: rgba(248, 81, 73, .28);
}
/* 树里的角标：默认淡一点，hover/选中该行时更明显 */
.tree .file .file-type-badge { opacity: 0.75; transition: opacity var(--dur-fast) ease; }
.tree .file:hover .file-type-badge,
.tree .file.active .file-type-badge { opacity: 1; }
/* 面包屑里的角标略大，作为当前文档类型标识 */
.crumbs .file-type-badge { font-size: 10px; margin-right: 2px; }

/* 预览区可编辑态：文本光标 + 去掉默认 focus 描边（改用容器高亮） */
.md-preview-pane[contenteditable="true"] { cursor: text; outline: none; }
.md-preview-pane[contenteditable="true"]:focus { outline: none; }
.editing-mode .md-preview-pane-wrap:focus-within { box-shadow: inset 0 0 0 2px var(--accent-ring); }

/* ==================== 应用内确认 / 输入对话框 ==================== */
.atlas-dialog { z-index: 200; }
.atlas-dialog .dialog-panel {
  width: 440px;
  max-width: calc(100vw - 32px);
}
.atlas-dialog .modal-header { border-bottom: 1px solid var(--border); }
.dialog-body {
  padding: 16px 18px 4px;
}
.dialog-text {
  margin: 0;
  font-size: 13px;
  line-height: 1.65;
  color: var(--text-dim);
  white-space: pre-line;   /* 保留 body 里的换行 */
}
.dialog-input {
  width: 100%;
  margin-top: 12px;
  padding: 7px 10px;
  border: 1px solid var(--border-input);
  border-radius: 7px;
  background: var(--bg-elev-2);
  color: var(--text);
  font-size: 13px;
  font-family: inherit;
  outline: none;
}
.dialog-input:focus { border-color: var(--accent); }
.dialog-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  padding: 16px 18px 18px;
}
.dialog-actions button {
  padding: 7px 14px;
  border-radius: 7px;
  border: 1px solid var(--border);
  background: var(--bg-elev-2);
  color: var(--text);
  font-size: 12px;
  font-family: inherit;
  cursor: pointer;
  transition: background var(--dur-fast) ease, border-color var(--dur-fast) ease;
}
.dialog-actions button:hover { background: var(--bg-hover); }
.dialog-actions .dialog-confirm {
  border-color: transparent;
  background: var(--accent);
  color: var(--on-accent);
  font-weight: 500;
}
/* hover 不用 filter: brightness()。
 * 那个写法会把背景和文字一起提亮：实心按钮上的文字已经是白色（浅色主题的
 * --on-accent），提不亮了，而背景一亮，对比度就往下掉——「清除」这类红底白字
 * 的危险按钮 hover 时文案会明显发虚。改成明确指定更深一档的背景色。 */
.dialog-actions .dialog-confirm:hover { background: var(--accent-hover); }
.dialog-actions .dialog-confirm.danger { background: var(--danger); }
.dialog-actions .dialog-confirm.danger:hover { background: var(--danger-strong); }
.dialog-actions button:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* ==================== ⌘K 快速打开 ==================== */
#quickopen { align-items: flex-start; z-index: 150; }
.quickopen-panel {
  width: 620px;
  max-width: calc(100vw - 40px);
  margin-top: 12vh;
  overflow: visible;
  display: flex;
  flex-direction: column;
}
.quickopen-inputwrap {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 16px;
  /* #quickopen-input 无边框，这条底线是它的可见边界 */
  border-bottom: 1px solid var(--border-input);
}
.quickopen-icon { color: var(--text-faint); width: 15px; height: 15px; }
#quickopen-input {
  flex: 1 1 auto;
  min-width: 0;
  border: 0;
  outline: none;
  background: transparent;
  color: var(--text);
  font-family: inherit;
  font-size: 15px;
}
#quickopen-input::placeholder { color: var(--text-faint); }
/* 面板一打开焦点就在这里，整条顶栏即输入区，不需要再画焦点环 */
#quickopen-input:focus-visible { box-shadow: none; }
/* 结果行用 background 表达选中，焦点环会和 .active 的底色打架 */
.quickopen-item:focus-visible { box-shadow: none; background: var(--bg-active); }
.quickopen-hint {
  flex: 0 0 auto;
  background: var(--bg-elev-2);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 1px 5px;
  font-size: 10px;
  color: var(--text-faint);
  font-family: var(--font-mono);
}
.quickopen-list {
  max-height: 46vh;
  overflow-y: auto;
  padding: 6px;
}
.quickopen-item {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 7px 10px;
  border-radius: 7px;
  cursor: pointer;
  border: 0;
  width: 100%;
  text-align: left;
  background: transparent;
  font-family: inherit;
}
.quickopen-item.active { background: var(--bg-active); }
.quickopen-item:hover { background: var(--bg-hover); }
.quickopen-item.active:hover { background: var(--bg-active); }
.qo-icon { flex: 0 0 auto; width: 14px; height: 14px; color: var(--text-faint); }
.qo-main { flex: 1 1 auto; min-width: 0; }
.qo-name {
  display: block;
  font-size: var(--fs-base);
  font-weight: var(--fw-medium);
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* 选中行的标题再加一档字重：底色之外多一个焦点信号，
 * 快速连按 ↑↓ 时不用靠辨认淡底色来确认自己在哪 */
.quickopen-item.active .qo-name { font-weight: var(--fw-semi); }
.qo-name mark {
  background: transparent;
  color: var(--accent);
  font-weight: var(--fw-bold);
}
.qo-path {
  display: block;
  font-size: var(--fs-sm);
  color: var(--text-faint);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.qo-badge {
  flex: 0 0 auto;
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.03em;
  padding: 1px 5px;
  border-radius: 4px;
  background: var(--bg-elev-2);
  color: var(--text-dim);
}

/* ---------- 正文命中 ---------- */
/* 分组标题：把「文件名匹配」和「文件名没匹配上、但正文里有」分开。
 * 不做成可点的，纯分隔标签。 */
/* 分组头改用"上方留白 + 字距"代替分隔线：一条横线会把面板切成两个表格，
 * 而这里要表达的是"同一个列表的两段"。留白把两段推开，字距把标题压到
 * 比结果行更低的一层——和侧栏分区头是同一套手法。 */
.qo-group {
  display: flex; align-items: center; gap: 5px;
  padding: 6px 10px 4px;
  margin-top: 8px;
  color: var(--text-faint);
  font-size: var(--fs-xs);
  font-weight: var(--fw-semi);
  letter-spacing: 0.08em;
}
/* 第一段结果和分组头之间需要那段留白，但列表顶部不需要 */
.quickopen-list > .qo-group:first-child { margin-top: 0; }
.qo-group .ico { color: var(--text-faint); }
.qo-group-loading { padding-top: 6px; }
.qo-group-loading .qo-spin { animation: spin 1.1s linear infinite; }

/* 正文摘要：等宽 + 两行封顶。等宽是因为摘要常含代码/路径片段，
 * 比例字体下这些内容很难扫 */
.qo-snippet {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  font-family: var(--font-mono);
  font-size: 10.5px;
  line-height: 1.45;
  color: var(--text-faint);
  word-break: break-word;
}
.qo-snippet mark {
  background: var(--accent-soft);
  color: var(--accent);
  font-weight: 600;
  border-radius: 2px;
  padding: 0 1px;
}
.quickopen-item.active .qo-snippet { color: var(--text-dim); }

/* 命中处数：靠右，和 MD/HTML 角标并排 */
.qo-hits {
  flex: 0 0 auto;
  font-size: 9.5px;
  padding: 1px 5px;
  border-radius: var(--r-full);
  background: var(--accent-soft);
  color: var(--accent);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.qo-unread {
  flex: 0 0 auto;
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--unread);
}
/* 与侧栏、首页一致：>24h 的旧未读降级成灰点 */
.qo-unread.stale { background: var(--unread-stale); }

/* 选中行的动作提示。只在 .active 上出现，所以列表平时是干净的，
 * 而"焦点在哪一行"多了一个除底色之外的、语义明确的信号。 */
.qo-action {
  flex: 0 0 auto;
  display: none;
  align-items: center;
  gap: 5px;
  font-size: var(--fs-xs);
  color: var(--text-dim);
  white-space: nowrap;
}
.quickopen-item.active .qo-action { display: inline-flex; }
.qo-action kbd {
  font-size: 10px;
  min-width: 15px;
  padding: 0 3px;
  margin: 0;
}
.quickopen-empty {
  padding: 22px 12px;
  text-align: center;
  font-size: 12px;
  color: var(--text-faint);
}
.quickopen-footer {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 8px 16px;
  border-top: 1px solid var(--border);
  font-size: 11px;
  color: var(--text-faint);
}
.quickopen-footer kbd {
  font-size: 9px;
  padding: 1px 4px;
  margin-right: 2px;
}
.quickopen-count { margin-left: auto; }

/* ==================== 分享：有效期与范围 ==================== */
.share-options {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
  padding: 10px 0 2px;
}
.share-option {
  display: flex;
  align-items: center;
  gap: 6px;
}
.share-option-label {
  font-size: 11px;
  color: var(--text-dim);
  white-space: nowrap;
}
.share-options select {
  border: 1px solid var(--border-input);
  border-radius: 6px;
  background: var(--bg-elev-2);
  color: var(--text);
  font-family: inherit;
  font-size: 12px;
  padding: 4px 6px;
  cursor: pointer;
}
.share-options select:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.share-expiry {
  margin-left: auto;
  font-size: 11px;
  color: var(--text-faint);
  white-space: nowrap;
}
.share-expiry.expiring { color: var(--danger); }
.share-scope-hint {
  margin: 6px 0 0;
  font-size: 11px;
  line-height: 1.6;
  color: var(--text-faint);
}
.share-list-meta {
  font-size: 10px;
  color: var(--text-faint);
  white-space: nowrap;
}

/* ==================== Diff 面板：和上次已读版本对比 ==================== */
.diff-panel {
  position: absolute;
  inset: 0;
  z-index: 3;
  display: flex;
  flex-direction: column;
  background: var(--bg);
}
.diff-panel.hidden { display: none; }
.diff-header {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 8px 14px;
  border-bottom: 1px solid var(--border);
  background: var(--bg-elev);
}
.diff-title {
  display: flex;
  align-items: baseline;
  gap: 10px;
  min-width: 0;
}
.diff-title-main {
  font-size: 12px;
  font-weight: 600;
  color: var(--text);
  white-space: nowrap;
}
.diff-stats {
  font-size: 11px;
  color: var(--text-dim);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.diff-stats .add { color: var(--ok); font-weight: 600; }
.diff-stats .del { color: var(--danger); font-weight: 600; }
.diff-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 0 0 auto;
}
.diff-ctx {
  display: flex;
  align-items: center;
  gap: 5px;
  font-size: 11px;
  color: var(--text-dim);
}
.diff-ctx select {
  border: 1px solid var(--border);
  border-radius: 5px;
  background: var(--bg-elev-2);
  color: var(--text);
  font: inherit;
  font-size: 11px;
  padding: 2px 4px;
  cursor: pointer;
}
.diff-btn, .diff-btn-primary {
  border-radius: 6px;
  font-family: inherit;
  font-size: 11px;
  padding: 4px 10px;
  cursor: pointer;
  border: 1px solid var(--border);
  background: var(--bg-elev-2);
  color: var(--text);
  transition: background var(--dur-fast) ease;
}
.diff-btn:hover, .diff-btn-primary:hover { background: var(--bg-hover); }
.diff-btn-primary {
  border-color: transparent;
  background: var(--accent);
  color: var(--on-accent);
  font-weight: 500;
}
/* 同 .dialog-confirm：实心按钮的 hover 不用 filter: brightness()，
 * 那会把白字和背景一起提亮、反而压低对比度。用明确的深一档颜色。 */
.diff-btn-primary:hover { background: var(--accent-hover); }
.diff-actions button:focus-visible,
.diff-ctx select:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* 历史版本下拉：标题可能较长（"往前第 2 版 · 3 天前（与当前相同）"），
 * 给它比"上下文"那个下拉更宽的余量 */
.diff-ver select { max-width: 210px; }
.diff-ver.hidden, .diff-btn-revert.hidden { display: none; }
/* 回退会改写用户磁盘上的文件，用描边红标出它和其它按钮不是一类操作 */
.diff-btn-revert {
  border-color: color-mix(in srgb, var(--danger) 40%, var(--border));
  color: var(--danger);
}
.diff-btn-revert:hover:not(:disabled) {
  background: color-mix(in srgb, var(--danger) 10%, transparent);
  border-color: var(--danger);
}
.diff-btn-revert:disabled {
  opacity: .45;
  cursor: not-allowed;
  border-color: var(--border);
  color: var(--text-faint);
}

.diff-body {
  flex: 1 1 auto;
  overflow: auto;
  font: 12px/1.55 ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  padding-bottom: 30vh;
}
.diff-body:focus-visible { outline: none; }
.diff-hunk { border-bottom: 1px solid var(--border); }
.diff-hunk:last-child { border-bottom: 0; }
.diff-skip {
  padding: 4px 14px;
  background: var(--bg-elev);
  color: var(--text-faint);
  font-size: 10px;
  border-bottom: 1px solid var(--border);
  user-select: none;
}
.diff-line {
  display: flex;
  align-items: flex-start;
  white-space: pre-wrap;
  word-break: break-word;
}
.diff-gutter {
  flex: 0 0 auto;
  width: 82px;
  padding: 0 8px;
  text-align: right;
  color: var(--text-faint);
  background: var(--bg-elev);
  user-select: none;
  font-size: 10px;
  line-height: 1.55;
}
.diff-gutter span { display: inline-block; min-width: 30px; }
.diff-sign {
  flex: 0 0 auto;
  width: 16px;
  text-align: center;
  color: var(--text-faint);
  user-select: none;
}
.diff-text { flex: 1 1 auto; min-width: 0; padding-right: 14px; }
.diff-line.add { background: color-mix(in srgb, var(--ok) 12%, transparent); }
.diff-line.add .diff-sign { color: var(--ok); }
.diff-line.del { background: color-mix(in srgb, var(--danger) 12%, transparent); }
.diff-line.del .diff-sign { color: var(--danger); }
.diff-empty {
  padding: 40px 24px;
  text-align: center;
  color: var(--text-dim);
  font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", sans-serif;
  font-size: 13px;
  line-height: 1.7;
}
.diff-empty .diff-empty-art {
  display: flex; justify-content: center;
  color: var(--text-faint);
  margin-bottom: 12px;
}
.diff-empty .diff-empty-art.ok { color: var(--ok); }
.diff-empty .diff-empty-art.warn { color: var(--warn); }
.diff-empty .diff-empty-hint { font-size: 11px; color: var(--text-faint); margin-top: 8px; }
.diff-truncated {
  padding: 10px 14px;
  color: var(--text-faint);
  font-size: 11px;
  border-top: 1px solid var(--border);
}
/* 有底本且文件被改过时，工具栏按钮给个提示色 */
#btn-diff.has-changes { color: var(--accent); position: relative; }
#btn-diff.has-changes::after {
  content: '';
  position: absolute;
  top: 4px; right: 4px;
  width: 5px; height: 5px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 0 2px var(--bg-elev);
}

/* ==================== 源码 ↔ 预览 的对应区域高亮 ==================== */
/* 两种强度：active = 光标所在块（细），selected = 选区覆盖的块（明显） */
:root {
  --sync-active-bg:    light-dark(rgba(37, 99, 235, .07), rgba(91, 156, 255, .10));
  --sync-active-bar:   light-dark(rgba(37, 99, 235, .45), rgba(91, 156, 255, .55));
  --sync-selected-bg:  light-dark(rgba(37, 99, 235, .14), rgba(91, 156, 255, .20));
  --sync-selected-bar: var(--accent);
}

/* 预览侧：块级对应标识。用 box-shadow 画左侧竖条，不占布局、不引起重排 */
.md-preview-pane .md-sync-active,
.md-preview-pane .md-sync-selected {
  border-radius: 3px;
  transition: background-color var(--dur-fast) ease, box-shadow var(--dur-fast) ease;
}
.md-preview-pane .md-sync-active {
  background-color: var(--sync-active-bg);
  box-shadow: -8px 0 0 0 var(--sync-active-bg), inset 2px 0 0 0 var(--sync-active-bar);
}
.md-preview-pane .md-sync-selected {
  background-color: var(--sync-selected-bg);
  box-shadow: -8px 0 0 0 var(--sync-selected-bg), inset 2px 0 0 0 var(--sync-selected-bar);
}
@media (prefers-reduced-motion: reduce) {
  .md-preview-pane .md-sync-active,
  .md-preview-pane .md-sync-selected { transition: none; }
}

/* 正文命中的行有两行摘要，比名称行高一截：图标与右侧角标顶对齐更整齐 */
.quickopen-item.is-content { align-items: flex-start; }
.quickopen-item.is-content .qo-icon,
.quickopen-item.is-content .qo-hits,
.quickopen-item.is-content .qo-badge,
.quickopen-item.is-content .qo-unread { margin-top: 2px; }

/* ==================== 快捷键速查表 ==================== */
.shortcuts-panel {
  width: 760px;
  display: flex;
  flex-direction: column;
  overflow: hidden;                       /* 滚动交给 body，标题栏固定 */
  max-height: min(680px, calc(100vh - 80px));
}
.shortcuts-body {
  overflow-y: auto;
  min-height: 0;
  padding: 4px 18px 18px;
  /* 双列瀑布：分组数量会随功能增长，写死列数迟早要改 */
  columns: 2;
  column-gap: 26px;
}
.sc-group {
  break-inside: avoid;                    /* 一个分组不要被拆到两列 */
  /* 行分隔线去掉后，分组间距就是唯一的分段手段，要给足 */
  margin-top: 20px;
}
.sc-group h3 {
  margin: 0 0 8px;
  font-size: var(--fs-xs);
  font-weight: var(--fw-semi);
  color: var(--text-faint);
  /* 与侧栏分区头、⌘K 分组头同一套弱化手法 */
  letter-spacing: 0.08em;
}
.sc-group dl { margin: 0; }
/* 去掉每行的 border-bottom：一屏 20 多条横线会把这个面板变成一张财务报表。
 * 键帽列本身有底色块，已经足够把每一行分开——真正需要的分隔是"分组之间"，
 * 那个由 .sc-group 的 margin-top 负责。 */
.sc-row {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 5px 0;
}
/* 按键列定宽：各分组的说明文字左边缘对齐，整页才扫得快 */
.sc-row dt {
  flex: none;
  width: 108px;
  display: flex;
  align-items: center;
  gap: 2px;
  justify-content: flex-end;
}
.sc-row dd {
  flex: 1;
  min-width: 0;
  margin: 0;
  font-size: var(--fs-sm);
  line-height: var(--lh-base);
  color: var(--text-dim);
}
.sc-plus { color: var(--text-faint); font-size: 9px; margin: 0 1px; }

@media (max-width: 700px) {
  .shortcuts-panel { width: 100%; }
  .shortcuts-body { columns: 1; }
  .sc-row dt { width: 92px; }
}

/* ---------- 入口 ---------- */
/* 侧栏底部：原来是个静态的 ⌘K 提示，现在是真入口 */
.sidebar-footer .footer-kbd {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  flex: none;
  padding: 1px 4px 1px 5px;
  border: 1px solid transparent;
  background: transparent;
  color: var(--text-faint);
  border-radius: var(--r-sm);
  cursor: pointer;
  font-family: inherit;
  transition: background var(--dur-fast), color var(--dur-fast);
}
.sidebar-footer .footer-kbd:hover { background: var(--bg-hover); color: var(--text-dim); }

/* 首页提示行末尾的入口。
 * .home-tips 现在自带 elev-2 底色，所以这个按钮改成"平时无边框、hover 才浮起"：
 * 在一个已经有底色的条里再画一圈实边，会变成"框里的框"。 */
.home-tips-more {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-left: auto;
  height: var(--h-ctl-sm);
  padding: 0 8px;
  border: 1px solid transparent;
  background: transparent;
  color: var(--text-dim);
  border-radius: var(--r-sm);
  font-family: inherit;
  font-size: var(--fs-sm);
  cursor: pointer;
  transition: background var(--dur-fast), border-color var(--dur-fast), color var(--dur-fast);
}
.home-tips-more:hover {
  background: var(--bg-elev);
  border-color: var(--border);
  color: var(--text);
}

/* ============================================================
 * UI 升级迭代：动效系统 · 外壳品牌感 · 加载骨架 · 庆祝态
 * ------------------------------------------------------------
 * 原则：动效只作用在"一屏可数"的首页卡片与 chrome 上，密集列表（目录树几百行）
 * 不加入场动画——几百行一起动只会晕。所有动效都在 prefers-reduced-motion 下
 * 自动归零（:root 已把 --dur / --dur-fast 清零，另见下方各处的媒体查询）。
 * ============================================================ */

/* ---------- 入场：错峰浮起 ----------
 * 首页元素带 --i 序号，逐个淡入上移。45ms 的步距足够看出"依次出现"又不拖沓。 */
@keyframes atlas-rise {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}
.home-hero, .home-feed, .home-rails, .home-tips, .cover-card {
  animation: atlas-rise var(--dur-slow) var(--ease-out) both;
  animation-delay: calc(var(--i, 0) * var(--stagger));
}

/* ---------- 新鲜未读：呼吸（已停用） ----------
 * 状态点统一成 6px 实心静态点（侧栏 / 首页 / 封面 / ⌘K），侧栏与封面不再呼吸。
 * "AI 刚改过"改由 .just-changed 的单次轻闪（atlas-twinkle，见文件末尾动效段）表达。
 * keyframes 保留给星图画布同周期取值。 */
@keyframes atlas-breathe {
  0%, 100% { opacity: 1; }
  50%      { opacity: .45; }
}

/* View Transitions 的分场景规则（home / tab / view）见文件末尾「运动需授权」段 */

/* reduced-motion 兜底（旧代码）：系统偏好与应用内开关两路都覆盖。
 * 新增的动画一律写在「运动需授权」块里，天然静止，不用再往这里加 */
@media (prefers-reduced-motion: reduce) {
  .home-hero, .home-feed, .home-rails, .home-tips, .cover-card,
  .home-cheer-badge, .skeleton-card,
  .actions button.scanning svg, .main-actions button.spinning svg,
  .update-badge .dot, .update-banner .ub-pulse, .update-banner .ub-progress-bar,
  .toast.toast-progress .toast-icon, .toast.toast-progress .toast-progress-bar > div,
  .qo-group-loading .qo-spin, .tb-more-menu {
    animation: none !important;
  }
}
:root[data-motion="reduce"] :is(
  .home-hero, .home-feed, .home-rails, .home-tips, .cover-card,
  .home-cheer-badge, .skeleton-card,
  .actions button.scanning svg, .main-actions button.spinning svg,
  .update-badge .dot, .update-banner .ub-pulse, .update-banner .ub-progress-bar,
  .toast.toast-progress .toast-icon, .toast.toast-progress .toast-progress-bar > div,
  .qo-group-loading .qo-spin, .tb-more-menu) {
  animation: none !important;
}

/* ---------- 外壳品牌感 ----------
 * 侧栏头顶一层极淡品牌渐变，把 Atlas logo 所在的区域和下面的列表拉开。
 * 只在 chrome 上，绝不进正文，保证阅读区干净。 */
.sidebar-header {
  background:
    linear-gradient(180deg, var(--brand-glow), transparent 72%),
    var(--bg-elev);
}
/* brand-mark / home-logo 的渐变改走令牌，和首页数字、封面同一处来源 */
.brand-mark, .home-logo { background: var(--brand-grad); }
/* logo hover 微顶的弹簧过渡（配合 .brand:hover .brand-mark） */
.brand-mark { transition: transform 260ms var(--ease-spring); }

/* ---------- 加载骨架 ----------
 * 扫描还没回来时，首页 feed 先摆几张微光骨架卡，而不是空白或一行"扫描中…"。 */
@keyframes atlas-shimmer { to { background-position: -200% 0; } }
.skeleton-card {
  height: 152px;
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  background: linear-gradient(100deg,
    var(--bg-elev) 30%, var(--bg-hover) 50%, var(--bg-elev) 70%);
  background-size: 200% 100%;
  animation: atlas-shimmer 1.4s linear infinite;
}

/* ---------- 全部已读的小庆祝 ----------
 * 收件箱清空是这个工具里少数值得给一点正反馈的时刻。 */
.home-cheer {
  grid-column: 1 / -1;
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  padding: 40px 20px;
  text-align: center;
}
.home-cheer-badge {
  width: 52px; height: 52px; border-radius: var(--r-full);
  display: grid; place-items: center;
  color: #fff; background: var(--brand-grad);
  box-shadow: var(--shadow-2);
  animation: atlas-pop var(--dur-slow) var(--ease-spring) both;
}
@keyframes atlas-pop { from { transform: scale(.6); opacity: 0; } to { transform: none; opacity: 1; } }
.home-cheer-title { font-size: var(--fs-md); font-weight: var(--fw-semi); color: var(--text); }
.home-cheer-hint { font-size: var(--fs-sm); color: var(--text-faint); }

/* ============================================================
 * 暖中性皮肤（[data-skin="warm"]）
 * ------------------------------------------------------------
 * 只覆盖中性色（表面 / 描边 / 文字）——主色、品牌渐变、封面色、语义色一律不动。
 * 依旧用 light-dark() 写一遍，所以"冷/暖"与"浅/深"两个维度正交，四种组合各自自洽。
 * 只作用在 Atlas 外壳与首页；md / csv 等预览页由服务端渲染，保持中性纸白。
 * ============================================================ */
:root[data-skin="warm"] {
  /* 琥珀控制台（深）/ 羊皮纸（浅） */
  --bg:        light-dark(#faf8f4, #0b0907);
  --bg-elev:   light-dark(#fffdf9, #13100c);
  --bg-elev-2: light-dark(#f2eee6, #1c1812);
  --bg-hover:  light-dark(#eae4d9, #25201a);
  /* 选中态也要暖：原来沿用冷皮肤的蓝灰，在暖底上发冷 */
  --bg-active: light-dark(#e6e3f6, #232038);
  --border:        light-dark(#ebe4d8, #221d16);
  --border-strong: light-dark(#d4cab9, #383025);
  --border-input:  light-dark(#887d6c, #736a5b);
  --hairline:      light-dark(rgba(60, 45, 20, .05), rgba(255, 250, 240, .05));
  --text:       light-dark(#1b1712, #f6f2ea);
  --text-dim:   light-dark(#5c5448, #b0a798);
  --text-faint: light-dark(#6b6255, #938a7c);
}

/* ============================================================
 * 顶栏「更多」菜单
 * ------------------------------------------------------------
 * 把三个低频的"跳出 Atlas"动作收进一个下拉，顶栏从一排 ~10 个图标降到
 * 几个常用的 + 一个 ⋯。菜单项复用原按钮 id，事件与启用/禁用逻辑不动。
 * ============================================================ */
.tb-more { position: relative; display: inline-flex; }
/* 触发器视觉与其它 ghost 工具按钮一致（它不是 .main-actions 的直接子元素，
 * 拿不到那条规则，所以在这里重铺一遍） */
.tb-more-trigger {
  height: var(--h-ctl); min-width: var(--h-ctl); padding: 0 7px;
  border: 1px solid transparent; background: transparent; color: var(--text-dim);
  border-radius: var(--r-sm); cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  transition: background var(--dur-fast), color var(--dur-fast), transform var(--dur-fast) var(--ease-spring);
}
.tb-more-trigger:hover:not(:disabled) { background: color-mix(in srgb, var(--accent) 12%, transparent); color: var(--accent); transform: scale(1.06); }
.tb-more-trigger[aria-expanded="true"] { background: color-mix(in srgb, var(--accent) 14%, transparent); color: var(--accent); }
.tb-more-trigger:active:not(:disabled) { transform: scale(.95); }
.tb-more-trigger:disabled { opacity: .32; cursor: not-allowed; }
.tb-more-trigger:focus-visible { outline: none; box-shadow: var(--ring); }

.tb-more-menu {
  position: absolute; top: calc(100% + 6px); right: 0;
  min-width: 188px;
  padding: 5px;
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-3);
  z-index: 60;
  display: flex; flex-direction: column; gap: 2px;
  animation: atlas-menu-in var(--dur-fast) var(--ease-out);
}
.tb-more-menu.hidden { display: none; }
@keyframes atlas-menu-in { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }
.tb-more-menu button {
  display: flex; align-items: center; gap: 9px;
  width: 100%; height: 32px; padding: 0 9px;
  border: none; background: transparent; color: var(--text);
  border-radius: var(--r-sm); cursor: pointer;
  font: inherit; font-size: var(--fs-base); text-align: left;
  transition: background var(--dur-fast);
}
.tb-more-menu button:hover:not(:disabled) { background: var(--bg-hover); }
.tb-more-menu button:disabled { opacity: .4; cursor: not-allowed; }
.tb-more-menu button:focus-visible { outline: none; box-shadow: var(--ring); }
.tb-more-menu .ico { flex: none; color: var(--text-faint); }
.tb-more-menu button:hover:not(:disabled) .ico { color: var(--text-dim); }
.tb-more-label { flex: 1; }

/* 首页态：没有当前文档时整个「更多」入口收起（和其它文档工具按钮一致） */
body.no-active-doc .tb-more { display: none; }

/* ==================== 更新日志 / 新功能引导 ==================== */
/* 「更新日志」入口按钮的样式与「检查更新」统一定义在 .settings-nav 区（见上方
   .settings-changelog-btn / .settings-update-btn 合并规则），这里不再单独定义。 */

/* 弹窗 */
.changelog-panel { width: min(560px, 92vw); }
.changelog-body {
  padding: 2px 20px 6px;
  overflow-y: auto;
  max-height: min(64vh, 560px);
}
.changelog-foot {
  padding: 11px 20px;
  border-top: 1px solid var(--border);
  display: flex;
  justify-content: center;
}
/* .changelog-foot 的 display:flex 与全局 .hidden 同为单类选择器、且定义在后，
   会盖掉 .hidden 的 display:none —— 必须显式让 .hidden 赢，否则完整日志模式下
   底部那个「查看完整更新日志」按钮不会消失。 */
.changelog-foot.hidden { display: none; }
.changelog-all-btn {
  border: none;
  background: transparent;
  color: var(--accent);
  font-family: inherit;
  font-size: var(--fs-xs);
  cursor: pointer;
  padding: 4px 10px;
  border-radius: var(--r-sm);
  transition: background var(--dur-fast);
}
.changelog-all-btn:hover { background: var(--accent-soft); }

/* 版本分组 */
.cl-version { padding: 13px 0; border-bottom: 1px solid var(--border); }
.cl-version:last-child { border-bottom: none; }
.cl-version-head { display: flex; align-items: baseline; gap: 8px; margin-bottom: 6px; }
.cl-version-num { font-weight: var(--fw-semi); font-size: 13px; color: var(--text); }
.cl-version-date { font-size: var(--fs-xs); color: var(--text-faint); font-variant-numeric: tabular-nums; }

/* 单条改动 */
.cl-entry { display: flex; gap: 8px; padding: 7px 0; align-items: flex-start; }
.cl-tag {
  flex: none;
  margin-top: 2px;
  font-size: 10px;
  font-weight: var(--fw-medium);
  padding: 1px 7px;
  border-radius: 999px;
  line-height: 1.6;
  white-space: nowrap;
}
.cl-tag-feature { color: var(--accent); background: var(--accent-soft); }
.cl-tag-fix { color: var(--text-dim); background: var(--bg-hover); }
.cl-entry-body { flex: 1; min-width: 0; }
.cl-entry-title { font-size: 13px; font-weight: var(--fw-medium); color: var(--text); }
.cl-entry-desc { margin: 3px 0 0; font-size: var(--fs-xs); line-height: var(--lh-base); color: var(--text-dim); }
/* 新功能的使用引导：淡主色底块，和普通描述区分开——这是"教你怎么用"的重点 */
.cl-entry-guide {
  margin: 7px 0 0;
  font-size: var(--fs-xs);
  line-height: var(--lh-base);
  color: var(--text-dim);
  background: var(--accent-soft);
  border-radius: var(--r-sm);
  padding: 7px 9px;
}
.cl-guide-label {
  display: inline-block;
  margin-right: 6px;
  font-weight: var(--fw-semi);
  color: var(--accent);
}
.cl-empty { padding: 24px; text-align: center; color: var(--text-faint); font-size: var(--fs-xs); }

/* What's New 引导头部 */
.cl-whatsnew-head { display: flex; align-items: center; gap: 8px; padding: 12px 0 4px; }
.cl-whatsnew-badge {
  font-weight: var(--fw-semi);
  font-size: 12px;
  color: var(--accent);
  background: var(--accent-soft);
  padding: 2px 10px;
  border-radius: 999px;
}
.cl-whatsnew-sub { font-size: var(--fs-xs); color: var(--text-dim); }
.cl-entry.cl-whatsnew { padding: 10px 0; border-bottom: 1px solid var(--border); }
.cl-entry.cl-whatsnew:last-child { border-bottom: none; }

/* ==================== 预览区表格：拖行抓手 ==================== */
/* 只在 md 编辑分栏的可编辑预览里出现（setupMdPreviewTables 注入），
   是每行最左的一个窄格：图标提示"这行能拖"，从它按下才触发 Sortable 拖拽。 */
#md-preview table .md-row-grip {
  width: 22px;
  padding: 0 !important;
  text-align: center;
  vertical-align: middle;
  color: var(--text-faint);
  cursor: grab;
  user-select: none;
  background: var(--bg-elev-2);
}
#md-preview table .md-row-grip:hover { color: var(--accent); background: var(--accent-soft); }
#md-preview table .md-row-grip:active { cursor: grabbing; }
#md-preview table .md-row-grip svg { fill: currentColor; display: block; margin: 0 auto; }
/* 表头那一格只是占位对齐，不可拖、不高亮 */
#md-preview table .md-row-grip-head { cursor: default; }
#md-preview table .md-row-grip-head:hover { color: var(--text-faint); background: var(--bg-elev-2); }
/* 拖拽中的行残影 */
.md-row-ghost { opacity: 0.5; background: var(--accent-soft) !important; }

/* ============================================================
 * 「星图 / 任务控制台」外壳基建
 * ------------------------------------------------------------
 * 令牌定义在文件顶部 :root；这里是令牌落到组件上的那一层：
 * 半透明面板、视觉隐藏工具类、控制台读数排版、英雄区星空、状态点、
 * 「运动需授权」动效段（轻闪 / View Transitions）、桌面版原生化。
 * ============================================================ */

/* ---------- 半透明面板（--glass-tint，浮在内容之上的面板） ----------
 * 只有设计清单里的选择器可以用 --glass-tint，其它表面一律实底。
 * 面板内部有自己底色的控件（hover 行 / 选中行 / 输入框）仍是不透明的。 */
.modal-panel,
#match-badge,
#peek,
.starmap-view .sm-panel,
.home-minimap-cap {
  background: color-mix(in srgb, var(--bg-elev) var(--glass-tint), transparent);
  -webkit-backdrop-filter: blur(20px) saturate(1.4);
  backdrop-filter: blur(20px) saturate(1.4);
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .modal-panel, #match-badge, #peek, .cover-badge, .starmap-view .sm-panel, .home-minimap-cap { background: var(--bg-elev); }
}

/* ---------- 视觉隐藏（读屏可读） ---------- */
.sr-only {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ---------- 控制台式排版 ----------
 * HUD 分区标题：中文界面不做大写，只把字距收到 .02em（界面文案统一中文）。
 * 计数 / 时间 / n/m 命中数一律等宽数字，数字变化时不左右跳。 */
:is(.section-head, .home-hero-eyebrow, .home-rail-head, .qo-group, .qo-section,
    .sc-group h3, .sort-menu-h, .home-tag-strip-label, .share-url-label, .hud) {
  letter-spacing: .02em;
  text-transform: none;
}
:is(.section-count, #tree-count, .folder-count, .footer-stats, .file-mtime, .cover-meta,
    .home-chip, .home-row-time, .home-feed-count, .match-badge, .find-count, .qo-count,
    .live-status, .home-clock, .home-readout-v, .tag-strip-hits, .tag-chip-count,
    .hud-readout, .hud-scale, .starmap-filter-status, .starmap-legend, .sm-cluster, .sm-star,
    .home-river-meta, .river-days, .time-filter-chip) {
  font-variant-numeric: tabular-nums;
}

/* ---------- 首页英雄区：静态星空 ----------
 * 只在英雄区背后，不进正文、不进侧栏。不承载信息、不动。
 * 统一 50% 不透明度：--hero-stars 里最亮的 alpha（.16）× .5 = .08，
 * 四组合下文字落在星点上 faint 仍 ≥4.5:1（tests/contrast-tokens.spec.js 断言）。 */
.home-hero::before {
  content: "";
  position: absolute; inset: 0;
  z-index: -1;
  pointer-events: none;
  opacity: .5;
  background: var(--hero-stars);
}

/* ---------- 侧栏底栏：实时更新状态 ----------
 * data-state 由 connectSSE 维护：live（已连接）/ retry（重连中）/ off（页面在后台，已暂停）。
 * 点的颜色之外还有文字（实时 / 重连中 / 已暂停），不只靠颜色表达。 */
.live-status {
  flex: none;
  display: inline-flex; align-items: center; gap: 5px;
  color: var(--text-dim);
  font-size: 11px;
  white-space: nowrap;
}
.live-status > i { width: 6px; height: 6px; border-radius: 50%; background: var(--signal); flex: none; }
.live-status[data-state="retry"] > i { background: var(--warn); }
.live-status[data-state="off"] > i { background: var(--text-dim); }

/* ---------- 状态点：统一 6px 实心、无外环 ----------
 * 新鲜未读（24h 内）= --unread 满亮度（文件名另有粗体，不只靠颜色）；
 * 较早未读 = --unread-stale；已读不显示点。各组件里的 .stale 规则照旧生效。 */
.unread-dot, .folder-unread-dot, .cover-fresh, .home-row-dot, .qo-unread {
  width: 6px; height: 6px;
  border-radius: 50%;
  box-shadow: none;
}

/* ============================================================
 * 运动需授权
 * ------------------------------------------------------------
 * 新增的 animation / 非颜色 transition 只写在下面这个块里：系统没要求减少动态效果、
 * 且用户没在设置里选「减少」（html[data-motion="reduce"]）时才生效。
 * 新代码默认就是静止的，不会再出现"加了动画却忘了加进 reduce 列表"。
 * ============================================================ */

/* 刚变更（本会话经 SSE 收到、不是 Atlas 自己写的）：同一个点做 3 次透明度轻闪。
 * 不授权时点保持满亮度静止，信息不丢 */
@keyframes atlas-twinkle {
  0%, 100% { opacity: 1; }
  50%      { opacity: .35; }
}
/* View Transitions：tab 场景的方向性滑移 */
@keyframes atlas-vt-fade-out { to { opacity: 0; } }
@keyframes atlas-vt-in-fwd  { from { opacity: 0; transform: translateX(12px); } }
@keyframes atlas-vt-in-back { from { opacity: 0; transform: translateX(-12px); } }

/* 切主题期间（app.js switchTheme）关掉所有颜色类 transition：颜色一次到终值，由整页交叉淡入负责过渡，
 * 不再出现各元素 120ms 过渡被主线程卡住后"过一会儿又跳一下"。只在切换的这几帧生效 */
html.theme-switching *, html.theme-switching *::before, html.theme-switching *::after { transition: none !important; }

/* 参与命名的外壳容器：命名后它们从 root 快照里独立出来，切 Tab 时侧栏不会跟着闪 */
.preview     { view-transition-name: atlas-preview; }
#tab-bar     { view-transition-name: atlas-tabbar; }
.sidebar     { view-transition-name: atlas-sidebar; }
.main-header { view-transition-name: atlas-header; }
/* 切主题时整页一起淡入，不需要分组；而命名分组总是画在 root 快照之上，会把 root 里的设置窗、⌘K、
 * 悬停预览、对比面板这类浮层整个盖住（"设置窗消失一下再回来"）。theme 场景撤掉命名，
 * 全部留在 root 快照里，浮层按原本的层叠顺序跟着一起淡入 */
:root[data-vt="theme"] :is(.preview, #tab-bar, .sidebar, .main-header) { view-transition-name: none; }

@media (prefers-reduced-motion: no-preference) {
  :root:not([data-motion="reduce"]) :is(
    .file.just-changed .unread-dot,
    .cover-card.just-changed .cover-fresh) {
    animation: atlas-twinkle var(--dur-twinkle) var(--ease-standard) 3;
  }

  /* home：回首页 / 从首页进文档，整体交叉淡入 */
  :root:not([data-motion="reduce"])::view-transition-old(root),
  :root:not([data-motion="reduce"])::view-transition-new(root),
  :root:not([data-motion="reduce"])::view-transition-group(*) {
    animation-duration: var(--dur-med);
    animation-timing-function: var(--ease-enter);
  }

  /* theme：切主题时整页（含命名的侧栏 / 顶栏 / 预览）一起交叉淡入，240ms */
  :root[data-vt="theme"]:not([data-motion="reduce"])::view-transition-group(*),
  :root[data-vt="theme"]:not([data-motion="reduce"])::view-transition-old(*),
  :root[data-vt="theme"]:not([data-motion="reduce"])::view-transition-new(*) {
    animation-duration: 240ms;
    animation-timing-function: ease;
  }

  /* tab：只有 .preview 做方向性滑移（新 Tab 在右边就从右侧滑入 12px），其余硬切 */
  :root[data-vt="tab"]:not([data-motion="reduce"])::view-transition-old(root),
  :root[data-vt="tab"]:not([data-motion="reduce"])::view-transition-new(root),
  :root[data-vt="tab"]:not([data-motion="reduce"])::view-transition-group(atlas-tabbar),
  :root[data-vt="tab"]:not([data-motion="reduce"])::view-transition-old(atlas-tabbar),
  :root[data-vt="tab"]:not([data-motion="reduce"])::view-transition-new(atlas-tabbar),
  :root[data-vt="tab"]:not([data-motion="reduce"])::view-transition-group(atlas-sidebar),
  :root[data-vt="tab"]:not([data-motion="reduce"])::view-transition-old(atlas-sidebar),
  :root[data-vt="tab"]:not([data-motion="reduce"])::view-transition-new(atlas-sidebar),
  :root[data-vt="tab"]:not([data-motion="reduce"])::view-transition-group(atlas-header),
  :root[data-vt="tab"]:not([data-motion="reduce"])::view-transition-old(atlas-header),
  :root[data-vt="tab"]:not([data-motion="reduce"])::view-transition-new(atlas-header) {
    animation: none;
  }
  :root[data-vt="tab"]:not([data-motion="reduce"])::view-transition-old(atlas-preview) {
    animation: atlas-vt-fade-out var(--dur-med) var(--ease-exit) both;
  }
  :root[data-vt="tab"]:not([data-motion="reduce"])::view-transition-new(atlas-preview) {
    animation: atlas-vt-in-fwd var(--dur-med) var(--ease-enter) both;
  }
  :root[data-vt="tab"][data-vt-dir="back"]:not([data-motion="reduce"])::view-transition-new(atlas-preview) {
    animation-name: atlas-vt-in-back;
  }

  /* view：首页封面墙 ↔ 星图，只作用于主区；侧栏 / 顶栏 / 标签栏硬切 */
  :root[data-vt="view"]:not([data-motion="reduce"])::view-transition-group(atlas-sidebar),
  :root[data-vt="view"]:not([data-motion="reduce"])::view-transition-old(atlas-sidebar),
  :root[data-vt="view"]:not([data-motion="reduce"])::view-transition-new(atlas-sidebar),
  :root[data-vt="view"]:not([data-motion="reduce"])::view-transition-group(atlas-header),
  :root[data-vt="view"]:not([data-motion="reduce"])::view-transition-old(atlas-header),
  :root[data-vt="view"]:not([data-motion="reduce"])::view-transition-new(atlas-header),
  :root[data-vt="view"]:not([data-motion="reduce"])::view-transition-group(atlas-tabbar),
  :root[data-vt="view"]:not([data-motion="reduce"])::view-transition-old(atlas-tabbar),
  :root[data-vt="view"]:not([data-motion="reduce"])::view-transition-new(atlas-tabbar) {
    animation: none;
  }
}

/* ============================================================
 * 桌面版原生化（macOS）
 * ------------------------------------------------------------
 * 全部限定在 html[data-desktop="darwin"] 下：属性由 index.html 防闪脚本按
 * preload 的 atlasDesktop.platform 写入，浏览器版没有它，这里的规则天然不生效。
 * 专注模式的 #focus-drag / .focus-mode 规则随专注模式一起加。
 * ============================================================ */
/* 红绿灯避让宽度：灯组左缘 18 + 组宽 + 12px 留白。macOS 26 起灯组 60pt → 90px；
 * 主进程按实际系统版本算好经 preload 下发，index.html 防闪脚本首帧写进内联样式覆盖这里的默认值
 * （见 electron/titlebar.js）。旧值 76px 按 52pt 灯组估算，在 macOS 26 上和第三颗灯重叠 2pt */
html[data-desktop="darwin"] { --titlebar-inset: 90px; }
html[data-desktop="darwin"], html[data-desktop="darwin"] body { background: transparent; }

/* 侧栏：半透明着色层叠在原生 vibrancy 材质上；主阅读区保持不透明。
 * 着色比例单独成令牌（--sidebar-tint-light / -dark，见令牌区），不再借用浮层用的 94% --glass-tint：
 * 94% 只透出 6% 的材质，实机肉眼看不出毛玻璃（diagnosis §3）。
 * 侧栏里原本自带实底的分组头 / 搜索框同样改成透明或半透明，否则会在材质上盖出一块块补丁 */
html[data-desktop="darwin"] { --sidebar-tint: var(--sidebar-tint-light); }
html[data-desktop="darwin"][data-theme="dark"] { --sidebar-tint: var(--sidebar-tint-dark); }
@media (prefers-color-scheme: dark) {
  html[data-desktop="darwin"]:not([data-theme="light"]) { --sidebar-tint: var(--sidebar-tint-dark); }
}
html[data-desktop="darwin"] .sidebar {
  background: color-mix(in srgb, var(--bg-elev) var(--sidebar-tint), transparent);
  /* 半透明底上最弱的两档前景提一档，保证材质最亮 / 最暗时仍 ≥ 4.5 / 3（contrast-tokens.spec 读这里的映射） */
  --text-faint: var(--text-dim);
  --border-input: var(--text-dim);
}
html[data-desktop="darwin"] .sidebar-header { background: transparent; }
/* 顶层分组头静息时透明（:where 归零特异性，悬停 / 拖放的底色照常覆盖）；
 * 真正吸顶、盖在滚动内容上的那一个由 app.js 标 .is-stuck，换一层高比例着色 + 模糊遮住下方行 */
html[data-desktop="darwin"] :where(.tree > .folder > .folder-header) { background: transparent; }
html[data-desktop="darwin"] :where(.tree > .folder > .folder-header.is-stuck) {
  background: color-mix(in srgb, var(--bg-elev) 90%, transparent);
  -webkit-backdrop-filter: blur(12px);
  backdrop-filter: blur(12px);
}
html[data-desktop="darwin"].reduce-transparency :where(.tree > .folder > .folder-header) { background: var(--bg-elev); }
@media (prefers-reduced-transparency: reduce) {
  html[data-desktop="darwin"] :where(.tree > .folder > .folder-header) { background: var(--bg-elev); }
}
html[data-desktop="darwin"] #search {
  background: color-mix(in srgb, var(--bg-elev-2) 55%, transparent);
}
/* 系统「降低透明度」：侧栏改实底。两条链路任一生效即可——Chromium 上报的媒体查询，
 * 以及主进程从 nativeTheme 读出后推给前端的 html.reduce-transparency */
html[data-desktop="darwin"].reduce-transparency .sidebar { background: var(--bg-elev); }
html[data-desktop="darwin"].reduce-transparency #search { background: var(--bg-elev-2); }
@media (prefers-reduced-transparency: reduce) {
  html[data-desktop="darwin"] .sidebar { background: var(--bg-elev); }
  html[data-desktop="darwin"] #search { background: var(--bg-elev-2); }
}
/* 设置里「侧栏透明度」那一节只在桌面版出现 */
html:not([data-desktop="darwin"]) .desktop-only { display: none !important; }

/* 红绿灯避让：只加内边距，不平移 .sidebar / .main（sidebar.spec 断言其几何）。
 * 全屏时红绿灯消失 → :not(.is-fullscreen) 让两条顶栏回到原来的内边距 */
html[data-desktop="darwin"]:not(.is-fullscreen) .sidebar-header { padding-left: var(--titlebar-inset); }
html[data-desktop="darwin"]:not(.is-fullscreen) body.sidebar-collapsed .main-header,
html[data-desktop="darwin"].init-sidebar-collapsed:not(.is-fullscreen) .main-header { padding-left: var(--titlebar-inset); }
/* 收起 / 展开侧栏时主区整体平移（见 app.js toggleSidebar），顶栏左内边距跟着过渡，
 * 收起按钮从侧栏右侧平滑滑到红绿灯右边，不会先跳到灯下面。只重排顶栏这一小块 */
html[data-desktop="darwin"] body.tx-ready .main-header { transition: padding-left var(--dur) var(--ease); }

/* 拖动区：两条顶栏整体可拖，只有真正可交互的元素不可拖。
 * 面包屑容器 #crumbs 是 flex:1、几乎占满主顶栏，以前整块 no-drag，拖动 / 双击缩放只剩 13% 的宽度，
 * 窗口放大后同一个屏幕点落进面包屑，再双击没反应（"只能放大不能还原"，diagnosis §4）。
 * 现在面包屑里的纯文字也是拖动区，只有「回到首页」按钮等交互元素 no-drag；顶栏文字不可选中 */
html[data-desktop="darwin"] :is(.sidebar-header, .main-header) { -webkit-app-region: drag; user-select: none; -webkit-user-select: none; }
html[data-desktop="darwin"] :is(.sidebar-header, .main-header)
  :is(button, a, input, select, textarea, [contenteditable], [role="button"], [role="radio"],
      [tabindex], .brand, .tb-more, .match-badge) { -webkit-app-region: no-drag; }

/* ============================================================
 * 侧栏瘦身 + 首页顶部标签筛选条 + ⌘K 空状态分段（design §11.B1–B3、§12.3）
 * ------------------------------------------------------------
 * 侧栏只剩搜索、树头和目录树。排序与「仅未读」收进树头的 #btn-sort 菜单，
 * 标签 chip 在首页顶部 #home-tag-strip 与菜单里各渲染一份，读写同一个 state.tagFilter。
 * ============================================================ */

/* ---------- 树头排序按钮 + 菜单 ---------- */
.sort-wrap { position: static; margin-left: auto; display: inline-flex; }
.tree-head .sort-btn {
  width: auto;
  gap: 3px;
  padding: 0 5px;
}
.sort-btn-label {
  font-size: 10.5px;
  font-weight: var(--fw-medium);
  letter-spacing: normal;
  white-space: nowrap;
}
/* 有筛选（仅未读 / 标签）生效时按钮用强调色表态：菜单收着也能看出"树被筛过" */
.tree-head .sort-btn.filtering { color: var(--accent); }
.tree-head .sort-btn[aria-expanded="true"] { background: var(--bg-active); color: var(--text); }
.sort-menu {
  position: absolute; z-index: 30;
  top: calc(100% - 2px); left: 8px;
  width: 248px; max-width: calc(100% - 16px);
  padding: 4px;
  display: flex; flex-direction: column;
  border: 1px solid var(--border-strong);
  border-radius: var(--r-md);
  background: color-mix(in srgb, var(--bg-elev) var(--glass-tint), transparent);
  -webkit-backdrop-filter: blur(20px) saturate(1.4);
  backdrop-filter: blur(20px) saturate(1.4);
  box-shadow: var(--shadow-3);
  color: var(--text);
  font-size: var(--fs-sm);
  font-weight: var(--fw-normal);
  letter-spacing: normal;
  cursor: default;
}
.sort-menu.hidden { display: none; }
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .sort-menu { background: var(--bg-elev); }
}
.sort-menu-h {
  padding: 6px 8px 2px;
  font-size: var(--fs-xs);
  font-weight: var(--fw-semi);
  color: var(--text-faint);
}
.sort-menu-sep { height: 1px; margin: 4px 2px; background: var(--border); }
/* 排序三项：竖排单选，选中项前面打勾（原 .seg-btn 的下划线在竖排里不合适） */
.sort-menu-seg { display: flex; flex-direction: column; align-items: stretch; }
.sort-menu .seg-btn {
  display: flex; align-items: center; gap: 8px;
  height: 28px;
  padding: 0 8px 0 26px;
  border-radius: var(--r-sm);
  color: var(--text);
  font-size: 12.5px;
  text-align: left;
}
.sort-menu .seg-btn[aria-checked="true"] { color: var(--text); font-weight: var(--fw-semi); }
.sort-menu .seg-btn[aria-checked="true"]::after { content: none; }
.sort-menu .seg-btn[aria-checked="true"]::before {
  content: "";
  position: absolute; left: 9px; top: 50%;
  width: 9px; height: 5px;
  margin-top: -4px;
  border-left: 2px solid var(--accent);
  border-bottom: 2px solid var(--accent);
  transform: rotate(-45deg);
}
.sort-menu-note {
  margin-left: auto;
  font-size: 10.5px;
  font-weight: var(--fw-normal);
  color: var(--text-faint);
}
.sort-menu .only-unread {
  display: flex; align-items: center; gap: 8px;
  height: 28px;
  padding: 0 8px 0 8px;
  border-radius: var(--r-sm);
  font-size: 12.5px;
  color: var(--text);
  cursor: pointer;
}
.sort-menu .only-unread:hover { background: var(--bg-hover); }
.sort-menu .only-unread input { margin: 0 2px 0 1px; accent-color: var(--accent); cursor: pointer; }
.sort-menu-tags-sec.hidden { display: none; }
.sort-menu-tags {
  display: flex; flex-wrap: wrap; gap: 4px;
  padding: 2px 6px 6px;
}
/* 多于 12 个标签：这一段自己滚动，菜单不无限撑高 */
.sort-menu-tags.scrollable { max-height: 132px; overflow-y: auto; overscroll-behavior: contain; }

/* ---------- 树顶筛选提示 ---------- */
.tree-filter-note {
  flex: none;
  display: flex; align-items: center; gap: 6px;
  min-height: 26px;
  margin: 0 8px 6px;
  padding: 0 4px 0 8px;
  border-radius: var(--r-sm);
  background: var(--accent-soft);
  color: var(--text-dim);
  font-size: 11.5px;
}
.tree-filter-note.hidden { display: none; }
.tree-filter-note .ico { flex: none; color: var(--accent); }
.tree-filter-note-text { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tree-filter-note-text b { color: var(--text); font-weight: var(--fw-semi); }
.tree-filter-note-clear {
  flex: none;
  height: 20px; padding: 0 6px;
  border: none; border-radius: var(--r-xs);
  background: transparent;
  color: var(--accent);
  font-family: inherit; font-size: 11.5px;
  cursor: pointer;
}
.tree-filter-note-clear:hover { background: var(--bg-hover); }
.tree-filter-note-clear:focus-visible { outline: none; box-shadow: var(--ring); }

/* ---------- 首页顶部全局标签筛选条 ---------- */
.home-tag-strip {
  display: flex; align-items: center; gap: 10px;
  min-height: 40px;
  padding: 0 8px 0 14px;
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  background: var(--bg-elev);
}
.home-tag-strip.hidden { display: none; }
.home-tag-strip-label {
  flex: none;
  font-size: var(--fs-sm); font-weight: var(--fw-semi);
  color: var(--text-dim);
}
.tag-strip-chips {
  flex: 1; min-width: 0;
  display: flex; align-items: center; gap: 6px;
  padding: 6px 2px;
  overflow-x: auto;
  scrollbar-width: none;
  overscroll-behavior-x: contain;
}
.tag-strip-chips::-webkit-scrollbar { display: none; }
.tag-strip-chips.hidden { display: none; }
.tag-strip-chips .tag-chip { flex: none; }
/* 还有更多 chip 在右边：右侧渐隐提示（滚到最右就撤掉） */
.tag-strip-chips.scrollable:not(.at-end) {
  -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 28px), transparent);
          mask-image: linear-gradient(90deg, #000 calc(100% - 28px), transparent);
}
.tag-strip-hits {
  flex: none;
  font-size: 11.5px;
  color: var(--text-faint);
  white-space: nowrap;
}
.tag-strip-hits b { color: var(--unread); font-family: var(--font-mono); font-weight: 600; }
@media (max-width: 900px) { .tag-strip-hits { display: none; } }
.home-tag-strip .tag-bar-clear {
  height: 26px;
  padding: 0 10px;
  border: 1px solid var(--border-strong);
  border-radius: var(--r-sm);
}

/* ---------- 首页轨道头「清除」（最近打开） ---------- */
.home-rail-head .home-rail-clear { margin-left: auto; height: 20px; letter-spacing: normal; }

/* ---------- ⌘K 空状态分段 ---------- */
/* 段头（最近打开 / 收藏 / 全部文档）。和「正文命中」的 .qo-group 同一套视觉层级，
 * 但类名分开：quickopen-content-search.spec 断言空查询时没有 .qo-group */
.qo-section {
  display: flex; align-items: center; gap: 5px;
  min-height: 26px;
  padding: 6px 6px 2px 10px;
  margin-top: 6px;
  color: var(--text-faint);
  font-size: var(--fs-xs);
  font-weight: var(--fw-semi);
}
.quickopen-list > .qo-section:first-child { margin-top: 0; }
.qo-section .ico { color: var(--text-faint); }
.qo-section .qo-section-act { margin-left: auto; height: 20px; }
/* 收藏超过一屏：这一段自己滚动（约 8 行） */
.qo-section-scroll {
  display: flex; flex-direction: column; gap: inherit;
  max-height: 304px;
  overflow-y: auto;
  overscroll-behavior: contain;
}

/* ---------- 真实缩略图封面（design §3b） ----------
 * <img class="cover-thumb"> 插在 .cover-art 最底层：生成式底色与字形水印留作占位与降级，
 * 截图加载完成后（.loaded）淡入盖住它们；角标与新鲜点仍叠在截图之上。 */
.cover-art .cover-thumb {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover; object-position: top center;
  opacity: 0;
  transition: opacity var(--dur-med) var(--ease-enter);
  pointer-events: none;
}
.cover-art .cover-thumb.loaded { opacity: 1; }
.cover-art .cover-badge,
.cover-art .cover-fresh { z-index: 1; }
/* 截图盖住后不要斜纹；换成一条底部发丝线，白底页面的截图才不会和卡片正文糊成一片 */
.cover-art:has(.cover-thumb.loaded)::after {
  background: none;
  border-bottom: 1px solid var(--border);
  z-index: 1;
}

/* ---------- 快速查看（design §3c / §11.B5） ----------
 * 只"看"：整个浮窗 pointer-events:none，不拦鼠标、不抢焦点。位置由 peek.js 写 left / top。 */
.peek {
  position: fixed; left: 0; top: 0;
  z-index: 160;                 /* 高于 ⌘K（150）：⌘K 空状态里按 Space 也要看得见 */
  width: 380px;
  max-width: calc(100vw - 16px);
  pointer-events: none;
  border: 1px solid var(--border-strong);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-3);
  overflow: hidden;
}
.peek-head { display: grid; gap: 2px; padding: 10px 12px 8px; }
.peek-label { font-size: var(--fs-xs); color: var(--text-faint); }
.peek-title {
  font-size: var(--fs-base); font-weight: var(--fw-semi); color: var(--text);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.peek-meta { display: grid; gap: 1px; font-size: var(--fs-xs); color: var(--text-dim); }
.peek-meta-l2 { color: var(--text-faint); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-family: var(--font-mono); }
.peek-meta .peek-unread { color: var(--unread); font-weight: var(--fw-semi); }
.peek-body {
  position: relative;
  margin: 0 12px;
  aspect-ratio: 16 / 10;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  overflow: hidden;
  background: var(--bg-elev-2);
}
.peek-img { display: block; width: 100%; height: 100%; object-fit: cover; object-position: top center; }
/* 1280×800 的实时 iframe 用 transform:scale 缩进框里（缩放比由 peek.js 按框宽写） */
.peek-frame {
  position: absolute; left: 0; top: 0;
  border: 0;
  transform-origin: 0 0;
  background: #fff;
  pointer-events: none;
}
.peek-empty {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  color: var(--text-dim); font-size: var(--fs-sm);
}
.peek-foot { padding: 8px 12px 10px; font-size: var(--fs-xs); color: var(--text-faint); }
.peek.hidden,
.peek-img.hidden,
.peek-frame.hidden,
.peek-empty.hidden { display: none; }
.modal-section .peek-net-note { margin-top: 8px; }
@keyframes atlas-peek-in {
  from { opacity: 0; transform: scale(.96); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: no-preference) {
  :root:not([data-motion="reduce"]) .peek:not(.hidden) {
    animation: atlas-peek-in var(--dur-med) var(--ease-spring) both;
    transform-origin: left center;
  }
}

/* ============================================================
 * 星图视图（design §3a、§10.1–10.3、§11.A、§12.4）
 * ------------------------------------------------------------
 * 选中「星图」时 #empty-state 里的 .home 隐藏、#starmap-view 显示，占满主区：
 * 左侧舞台（主画布 + 动画层 + HUD 面板），右侧 300px 星表（画布的等价替代界面）。
 * 颜色全部来自令牌：星档 --star-1..4、未读 --unread / --unread-stale、连线 --link-line(-hl)。
 * ============================================================ */
.empty.is-map { overflow: hidden; }
.starmap-view {
  position: absolute; inset: 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr) 300px;
  background: var(--bg);
  color: var(--text);
}
.starmap-view.hidden { display: none; }
.starmap-view.catalog-collapsed { grid-template-columns: minmax(0, 1fr) 44px; }
.starmap-stage {
  position: relative;
  min-width: 0; min-height: 0;
  overflow: hidden;
  background: var(--bg);
  isolation: isolate;
}
/* 舞台背后的静态星空（同英雄区的 --hero-stars，不承载信息、不动） */
.starmap-stage::before {
  content: "";
  position: absolute; inset: 0;
  z-index: -1;
  pointer-events: none;
  opacity: .5;
  background: var(--hero-stars);
}
#starmap-canvas, #starmap-fx {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  display: block;
}
#starmap-canvas { cursor: grab; outline: none; }
#starmap-canvas.panning { cursor: grabbing; }
#starmap-canvas.on-star { cursor: pointer; }
#starmap-canvas:focus-visible { box-shadow: inset 0 0 0 2px var(--accent); }
#starmap-fx { pointer-events: none; }
.starmap-anchor { position: absolute; pointer-events: none; }

/* HUD 面板：半透明（--glass-tint，见上方毛玻璃清单） */
.starmap-view .sm-panel {
  position: absolute; z-index: 3;
  border: 1px solid var(--border-strong);
  border-radius: var(--r-lg);
}
.starmap-hud { left: 16px; top: 14px; padding: 6px 2px; }
.hud-readout {
  display: flex; align-items: center; gap: 14px;
  padding: 0 10px;
  font: 11.5px var(--font-mono);
  color: var(--text-dim);
  white-space: nowrap;
}
.hud-readout .k {
  margin-right: 5px;
  font-family: var(--font-ui);
  font-size: 10.5px; font-weight: var(--fw-semi);
  color: var(--text-faint);
}
.hud-readout b { color: var(--text); font-weight: var(--fw-semi); }
.hud-readout b.hot { color: var(--unread); }
.hud-readout .t { color: var(--text-faint); }
.hud-readout .hud-note { font-family: var(--font-ui); color: var(--warn); }
.starmap-filter-status {
  left: 16px; top: 58px;
  display: flex; align-items: center; gap: 2px;
  padding: 6px 12px;
  font-size: 12px;
  color: var(--text-dim);
  white-space: nowrap;
}
.starmap-filter-status.hidden { display: none; }
.starmap-filter-status .hud { color: var(--accent); font-weight: var(--fw-semi); }
.starmap-filter-status b { color: var(--text); font-family: var(--font-mono); font-weight: var(--fw-semi); }
.starmap-filter-status .sep, .starmap-filter-status .where { color: var(--text-faint); }
.starmap-lod {
  right: 16px; top: 14px;
  display: flex; gap: 10px;
  padding: 6px 10px;
  font-size: 11px; font-weight: var(--fw-semi);
  color: var(--text-faint);
}
.starmap-lod [aria-current="true"] { color: var(--accent); }
.starmap-legend {
  left: 16px; bottom: 16px;
  display: grid; gap: 6px;
  min-width: 200px; max-width: 250px;
  padding: 10px 12px;
  font-size: 11.5px;
  color: var(--text-dim);
}
.starmap-legend .row { display: flex; align-items: center; gap: 9px; }
.starmap-legend .lg-n { margin-left: auto; font: 10.5px var(--font-mono); color: var(--text-faint); }
.starmap-legend .sw { width: 16px; height: 12px; flex: none; display: grid; place-items: center; }
.starmap-legend .sw i { display: block; border-radius: 50%; }
.starmap-legend .sw .t1 { width: 7px; height: 7px; background: var(--star-1); box-shadow: 0 0 0 3px var(--star-halo); }
.starmap-legend .sw .t2 { width: 5.5px; height: 5.5px; background: var(--star-2); box-shadow: 0 0 0 2.5px var(--star-halo); }
.starmap-legend .sw .t3 { width: 4px; height: 4px; background: var(--star-3); }
.starmap-legend .sw .t4 { width: 3px; height: 3px; background: var(--star-4); }
.starmap-legend .sw .un { width: 6px; height: 6px; background: var(--unread); }
.starmap-legend .sw .jc { width: 6px; height: 6px; background: var(--star-1); outline: 1px solid var(--accent); outline-offset: 1px; }
.starmap-legend .sw .ln { width: 16px; height: 0; border-radius: 0; border-top: 1.5px solid var(--link-line-hl); opacity: .8; }
.starmap-legend .sw .arc {
  width: 16px; height: 8px;
  border: 1.5px solid var(--link-line-hl);
  border-color: var(--link-line-hl) transparent transparent;
  border-radius: 50% 50% 0 0;
  opacity: .7;
}
.starmap-legend .note { font-size: 11px; line-height: 1.45; color: var(--text-faint); }
.starmap-legend .lg-na { display: none; }
.starmap-legend.links-na .lg-link { display: none; }
.starmap-legend.links-na .lg-na { display: block; }
/* 视口太矮时图例让位，免得压住星团 */
@media (max-height: 640px) { .starmap-legend { display: none; } }
.starmap-zoom {
  right: 16px; bottom: 16px;
  display: flex; flex-direction: column; gap: 2px;
  padding: 3px;
}
.starmap-zoom button {
  width: 30px; height: 30px;
  display: grid; place-items: center;
  border: 0; border-radius: 7px;
  background: transparent;
  color: var(--text-dim);
  cursor: pointer;
}
.starmap-zoom button:hover { background: var(--bg-hover); color: var(--text); }
.starmap-zoom button:focus-visible { outline: none; box-shadow: var(--ring); }
.hud-scale {
  padding: 3px 0 2px;
  border-top: 1px solid var(--border);
  text-align: center;
  font: 10.5px var(--font-mono);
  color: var(--text-faint);
}
.starmap-tip {
  position: absolute; z-index: 6;
  padding: 7px 10px;
  border: 1px solid var(--border-strong);
  border-radius: var(--r-md);
  background: var(--bg-elev-2);
  box-shadow: var(--shadow-3);
  color: var(--text);
  font-size: 12px;
  white-space: nowrap;
  pointer-events: none;
  transform: translate(14px, -50%);
}
.starmap-tip.hidden { display: none; }
.starmap-tip .n { font-weight: var(--fw-semi); }
.starmap-tip .m { margin-top: 1px; font-size: 11.5px; color: var(--text-dim); }
.starmap-tip .u { color: var(--unread); }
.starmap-tip .mono { font-family: var(--font-mono); }
.starmap-empty {
  position: absolute; left: 50%; top: 50%; z-index: 4;
  transform: translate(-50%, -50%);
  padding: 10px 16px;
  border: 1px solid var(--border-strong);
  border-radius: var(--r-lg);
  background: var(--bg-elev);
  color: var(--text);
  font: inherit; font-size: 13px;
  cursor: pointer;
}
.starmap-empty:hover { border-color: var(--accent); }
.starmap-empty:focus-visible { outline: none; box-shadow: var(--ring); }
.starmap-empty.hidden, .starmap-nocanvas.hidden { display: none; }
.starmap-nocanvas { padding: 12px 16px; font-size: 12.5px; color: var(--text-dim); border-bottom: 1px solid var(--border); }
/* 画不了（getContext 为 null / 布局出错）：只留星表铺满 + 一行提示 */
.starmap-view.no-canvas,
.starmap-view.no-canvas.catalog-collapsed { grid-template-columns: minmax(0, 1fr); grid-template-rows: auto minmax(0, 1fr); }
.starmap-view.no-canvas .starmap-stage > :not(.starmap-nocanvas) { display: none; }
.starmap-view.no-canvas .starmap-stage::before { content: none; }

/* ---------- 星表 ---------- */
.starmap-catalog {
  min-width: 0; min-height: 0;
  display: flex; flex-direction: column;
  border-left: 1px solid var(--border);
  background: var(--bg-elev);
}
.starmap-catalog .cat-head {
  flex: none;
  display: flex; align-items: center;
  height: 44px;
  padding: 0 10px 0 10px;
  border-bottom: 1px solid var(--border);
}
#starmap-catalog-toggle {
  display: flex; align-items: center; gap: 8px;
  height: 30px; padding: 0 6px;
  border: 0; border-radius: var(--r-md);
  background: transparent;
  color: var(--text);
  font: inherit; font-size: 13px; font-weight: var(--fw-semi);
  cursor: pointer;
}
#starmap-catalog-toggle:hover { background: var(--bg-hover); }
#starmap-catalog-toggle:focus-visible { outline: none; box-shadow: var(--ring); }
.catalog-collapsed .starmap-catalog .cat-label,
.catalog-collapsed .starmap-catalog-body,
.catalog-collapsed .starmap-catalog .cat-tl { display: none; }
.catalog-collapsed .starmap-catalog .cat-head { justify-content: center; padding: 0; }
.starmap-catalog-body { flex: 1; min-height: 0; overflow: auto; padding: 6px; }
.starmap-catalog .cat-title {
  display: flex; align-items: center; gap: 8px;
  padding: 6px 8px 8px;
  font-size: 11.5px;
  color: var(--text-faint);
}
.starmap-catalog .cat-title b { font-size: 14px; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.starmap-catalog .cat-title .cat-sort,
.starmap-catalog .cat-title .cat-count { margin-left: auto; font-family: var(--font-mono); }
.starmap-catalog .cat-title .u { color: var(--unread); font-weight: var(--fw-semi); }
.starmap-catalog :is(.cat-back, .cat-more) {
  display: flex; width: 100%;
  padding: 7px 8px;
  border: 0; border-radius: var(--r-md);
  background: transparent;
  color: var(--accent);
  font: inherit; font-size: 12px; font-weight: var(--fw-semi);
  text-align: left;
  cursor: pointer;
}
.starmap-catalog .cat-more { justify-content: center; color: var(--text-dim); }
.starmap-catalog :is(.cat-back, .cat-more):hover { background: var(--bg-hover); }
.sm-cluster, .sm-star {
  position: relative;
  display: grid;
  width: 100%;
  gap: 2px;
  padding: 8px 10px;
  border: 0; border-radius: 8px;
  background: transparent;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.sm-cluster:hover, .sm-star:hover, .sm-cluster.hl { background: var(--bg-hover); }
:is(.sm-cluster, .sm-star, .starmap-catalog .cat-back, .starmap-catalog .cat-more):focus-visible { outline: none; box-shadow: var(--ring); }
.sm-cluster { grid-template-columns: minmax(0, 1fr) auto; column-gap: 8px; align-items: center; }
.sm-cluster .nm { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12.5px; font-weight: var(--fw-semi); color: var(--text); }
.sm-cluster.no-hit .nm { color: var(--text-dim); }
.sm-cluster .ct { font: 11px var(--font-mono); color: var(--text-faint); text-align: right; }
.sm-cluster .sub { grid-column: 1 / 3; display: flex; gap: 8px; font-size: 11.5px; color: var(--text-faint); }
.sm-cluster .sub .u { color: var(--unread); font-weight: var(--fw-semi); }
.sm-cluster .bar { grid-column: 1 / 3; height: 3px; margin-top: 4px; border-radius: 2px; background: var(--bg-elev-2); overflow: hidden; }
.sm-cluster .bar i { display: block; height: 100%; background: var(--star-3); }
.sm-cluster.has-fresh .bar i { background: var(--star-2); }
.sm-star { grid-template-columns: 14px minmax(0, 1fr) auto; column-gap: 8px; align-items: center; padding: 6px 10px; }
.sm-star .tdot { width: 14px; height: 14px; display: grid; place-items: center; }
.sm-star .tdot i { display: block; border-radius: 50%; }
.sm-star .tdot .t1 { width: 7px; height: 7px; background: var(--star-1); }
.sm-star .tdot .t2 { width: 5.5px; height: 5.5px; background: var(--star-2); }
.sm-star .tdot .t3 { width: 4px; height: 4px; background: var(--star-3); }
.sm-star .tdot .t4 { width: 3px; height: 3px; background: var(--star-4); }
.sm-star.unread .tdot i { width: 6px; height: 6px; background: var(--unread-stale); }
.sm-star.fresh .tdot i { background: var(--unread); }
.sm-star .nm { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12.5px; color: var(--text-dim); }
.sm-star.unread .nm { color: var(--text); }
.sm-star.fresh .nm { font-weight: var(--fw-semi); }
.sm-star .tm { font: 11px var(--font-mono); color: var(--text-faint); }
.sm-star[aria-current="true"] { background: var(--bg-active); }
.starmap-catalog .cat-tl {
  flex: none;
  display: flex; gap: 10px;
  padding: 8px 14px;
  border-top: 1px solid var(--border);
  font-size: 11px;
  color: var(--text-faint);
}

/* 高对比模式：画布无法保证可见性 → 隐藏两张画布与 HUD 面板，星表铺满作为主界面 */
@media (forced-colors: active) {
  #starmap-canvas, #starmap-fx, .starmap-view .sm-panel, .starmap-tip { display: none; }
  .starmap-view, .starmap-view.catalog-collapsed { grid-template-columns: 0 minmax(0, 1fr); }
  .catalog-collapsed .starmap-catalog-body, .catalog-collapsed .starmap-catalog .cat-tl { display: block; }
  .home-minimap { display: none; }
}

/* ---------- 首页迷你星图（§10.1）：英雄区左栏主体，高 214px ---------- */
.home-minimap {
  position: relative;
  display: block;
  width: 100%; height: 214px;
  margin-top: 10px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--bg);
  overflow: hidden;
  cursor: pointer;
  text-align: left;
}
.home-minimap:hover { border-color: color-mix(in srgb, var(--accent) 55%, var(--border)); }
.home-minimap:focus-visible { outline: none; box-shadow: var(--ring); }
/* object-fit: cover：位图尺寸和盒子一致时没有任何效果；侧栏动画 / 窗口缩放期间（重画被去抖推迟）
 * 位图等比放大裁边，而不是被横向拉成椭圆，尺寸稳定后再按新宽度重画一次 */
.home-minimap canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; object-fit: cover; }
.home-minimap-cap {
  position: absolute; left: 10px; top: 10px;
  padding: 3px 9px;
  border: 1px solid var(--border);
  border-radius: var(--r-full);
  font-size: 11px;
  color: var(--text-dim);
}
.home-minimap-go {
  position: absolute; right: 10px; bottom: 10px;
  padding: 5px 11px;
  border-radius: var(--r-full);
  background: var(--accent);
  color: var(--on-accent);
  font-size: 12px; font-weight: var(--fw-semi);
}

/* ---------- 顶栏：进星图自动收起侧栏的提示（§10.3） ---------- */
.auto-collapse-note {
  flex: none;
  margin: 0 8px;
  padding: 3px 10px;
  border-radius: var(--r-full);
  background: var(--accent-soft);
  color: var(--text-dim);
  font-size: 11.5px;
  white-space: nowrap;
}
.auto-collapse-note.hidden { display: none; }
.auto-collapse-note kbd { font: 10.5px var(--font-mono); padding: 0 4px; border: 1px solid var(--border-strong); border-radius: var(--r-xs); }
@media (max-width: 900px) { .auto-collapse-note { display: none; } }
/* ============================================================
 * 时间河流（design §3d）
 * ------------------------------------------------------------
 * 英雄区与待看之间的一张卡片：近 7 天 × 每小时 168 根柱（river.js 画进 svg.river-svg，
 * viewBox 0 0 168 100 拉伸铺满）。柱色按距今：24h 内 --star-2、更早 --star-3、
 * 只有修改时间可依的区段 --star-4 + 斜线底纹。选区 --accent-soft 填充 + --accent 实边。
 * 首次出现的升起动画写在本段末尾的「运动需授权」块里。
 * ============================================================ */
.home-river-card {
  border: 1px solid var(--border);
  border-radius: var(--r-xl);
  background: var(--bg-elev);
  padding: 16px 18px 12px;
}
.home-river-head {
  display: flex; align-items: baseline; flex-wrap: wrap; gap: 6px 12px;
  margin-bottom: 14px;
}
.home-river-title { margin: 0; font-size: var(--fs-lg); font-weight: var(--fw-bold); letter-spacing: -.01em; color: var(--text); }
.home-river-hud { font-size: var(--fs-xs); color: var(--text-faint); }
.home-river-meta { font-family: var(--font-mono); font-size: var(--fs-sm); color: var(--text-dim); }
.home-river-tag {
  font-size: 10px; color: var(--text-faint);
  padding: 0 5px; border: 1px solid var(--border); border-radius: var(--r-full);
  white-space: nowrap;
}
.home-river-tag.hidden, .home-river-note.hidden, .river-hatch-tag.hidden, .river-sel.hidden { display: none; }
.home-river-presets { margin-left: auto; display: flex; align-items: center; gap: 6px; }
.river-preset {
  height: 26px; padding: 0 10px;
  border: 1px solid var(--border-strong); border-radius: var(--r-md);
  background: transparent;
  color: var(--text-dim);
  font-family: inherit; font-size: var(--fs-sm);
  cursor: pointer;
}
.river-preset:hover:not(:disabled) { color: var(--text); background: var(--bg-hover); }
.river-preset:focus-visible { outline: none; box-shadow: var(--ring); }
.river-preset[aria-pressed="true"] { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }
.river-preset:disabled { opacity: .5; cursor: default; }
.home-river-note { margin: -6px 0 10px; font-size: var(--fs-sm); color: var(--text-faint); }
.home-river {
  position: relative;
  height: 112px;
  margin-top: 18px;   /* 留给「现在」刻线标签 */
  user-select: none; -webkit-user-select: none;
  touch-action: none;
  cursor: crosshair;
}
.river-svg { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; display: block; }
.river-svg .bar { fill: var(--star-3); transform-box: fill-box; transform-origin: bottom; }
.river-svg .bar.recent { fill: var(--star-2); }
.river-svg .bar.zero { fill: var(--border-strong); }
.river-svg .bar.dim { opacity: .28; }
.river-svg .river-dayline { stroke: var(--border-strong); stroke-width: 1; stroke-dasharray: 2 3; vector-effect: non-scaling-stroke; }
.river-svg .river-baseline { stroke: var(--border-strong); stroke-width: 1; vector-effect: non-scaling-stroke; }
.river-svg .river-nowline { stroke: var(--signal); stroke-width: 1.5; vector-effect: non-scaling-stroke; }
.river-svg .river-hatch { fill: url(#river-hatch); opacity: .32; }   /* 只做底纹，压低，不和柱子抢 */
.river-svg .river-hatch-line { fill: var(--border-strong); opacity: .7; }
.river-hatch-tag {
  position: absolute; top: 4px; left: 6px;
  padding: 0 4px;
  border-radius: var(--r-xs);
  background: var(--bg-elev);
  font-size: 10.5px; color: var(--text-faint);
  pointer-events: auto;
}
.river-now-tag {
  position: absolute; top: -18px;
  transform: translateX(-50%);
  font-size: 10.5px; font-weight: var(--fw-bold);
  color: var(--signal);
  pointer-events: none;
  white-space: nowrap;
}
.river-sel {
  position: absolute; top: 0; bottom: 0;
  background: var(--accent-soft);
  border-left: 2px solid var(--accent);
  border-right: 2px solid var(--accent);
  pointer-events: none;
}
.river-handle {
  position: absolute; top: 50%; z-index: 2;
  width: 12px; height: 34px;
  margin: -17px 0 0 -6px;
  border: 2px solid var(--accent);
  border-radius: 4px;
  background: var(--bg-elev);
  cursor: ew-resize;
}
.river-handle::before {
  content: ""; position: absolute;
  left: 3px; right: 3px; top: 11px; bottom: 11px;
  border-left: 1px solid var(--accent); border-right: 1px solid var(--accent);
}
.river-handle:focus-visible { outline: none; box-shadow: var(--ring); }
.river-days { position: relative; height: 20px; margin-top: 6px; }
.river-days span {
  position: absolute; top: 0;
  padding-left: 6px;
  font-size: 11px; color: var(--text-dim);
  white-space: nowrap;
}
.river-days span.today { color: var(--text); font-weight: var(--fw-semi); }
.river-foot {
  display: flex; align-items: center; flex-wrap: wrap; gap: 6px 14px;
  margin-top: 6px;
  font-size: 11.5px; color: var(--text-dim);
}
.river-lg { display: inline-flex; align-items: center; gap: 6px; }
.river-lg i { width: 10px; height: 10px; border-radius: 2px; display: inline-block; }
.river-lg .lg-recent { background: var(--star-2); }
.river-lg .lg-older { background: var(--star-3); }
.river-lg .lg-mtime { background: repeating-linear-gradient(125deg, var(--border-strong) 0 1px, transparent 1px 3px); border: 1px solid var(--border-strong); }
.river-lg .lg-now { width: 2px; background: var(--signal); }
.river-lg .lg-peak { background: var(--accent); }
.river-foot-hint { margin-left: auto; color: var(--text-faint); }
@media (max-width: 900px) { .river-foot-hint { display: none; } }
/* 首页封面墙的「该时段改动的文档」模式 */
.home-feed-all {
  margin-left: auto;
  height: 26px; padding: 0 10px;
  border: 1px solid var(--border-strong); border-radius: var(--r-md);
  background: transparent;
  color: var(--text-dim);
  font-family: inherit; font-size: var(--fs-sm); font-weight: var(--fw-normal); letter-spacing: normal;
  cursor: pointer;
}
.home-feed-all:hover { color: var(--text); background: var(--bg-hover); }
.home-feed-all:focus-visible { outline: none; box-shadow: var(--ring); }
.home-feed-all.hidden { display: none; }
.home-feed.is-time .home-feed-hint { display: none; }
/* 侧栏搜索框下方的时间段 chip */
.time-filter-chip {
  flex: none;
  align-self: flex-start;
  display: inline-flex; align-items: center; gap: 4px;
  max-width: calc(100% - 24px);
  height: 24px;
  margin: 8px 12px 0;
  padding: 0 9px;
  border: 1px solid color-mix(in srgb, var(--accent) 45%, var(--border));
  border-radius: var(--r-full);
  background: var(--accent-soft);
  color: var(--accent);
  font-family: inherit; font-size: 11.5px;
  white-space: nowrap; overflow: hidden;
  cursor: pointer;
}
.time-filter-chip:hover { border-color: var(--accent); }
.time-filter-chip:focus-visible { outline: none; box-shadow: var(--ring); }
.time-filter-chip.hidden { display: none; }
.time-filter-chip-text { overflow: hidden; text-overflow: ellipsis; }
@keyframes atlas-river-grow { from { transform: scaleY(0); } to { transform: scaleY(1); } }
@media (prefers-reduced-motion: no-preference) {
  :root:not([data-motion="reduce"]) .river-svg .bar.grow {
    animation: atlas-river-grow var(--dur-slow) var(--ease-enter) both;
    animation-delay: calc(var(--i, 0) * 4ms);
  }
}

/* ============================================================
 * 专注阅读模式（design §3f、§3g、§10.11、§11.D）
 * ------------------------------------------------------------
 * html.focus-mode 一个类控制全部表现。它是覆盖在原布局之上的一层：
 * 不碰 body.sidebar-collapsed / atlas:sidebarCollapsed / 侧栏宽度，
 * 退出后侧栏原来展开还是收起就恢复成什么。不持久化（刷新即正常布局）。
 * 过渡写在文件末尾的「运动需授权」块里。
 * ============================================================ */
.main-actions > #btn-focus[aria-pressed="true"] { color: var(--accent); }

/* 侧栏：复用收起的同一个 translate3d（过渡沿用 body.tx-ready .sidebar） */
html.focus-mode .sidebar { transform: translate3d(-100%, 0, 0); }
html.focus-mode .resizer { opacity: 0; pointer-events: none; }
html.focus-mode .main { margin-left: 0; }

/* 顶栏 / 标签栏 / 升级横幅：高度收到 0 并淡出；visibility:hidden 让它们退出 Tab 序列 */
html.focus-mode .main-header,
html.focus-mode #tab-bar,
html.focus-mode #update-banner {
  height: 0;
  min-height: 0;
  max-height: 0;
  padding-top: 0;
  padding-bottom: 0;
  border-bottom-width: 0;
  overflow: hidden;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}

/* 命中导航条属于「正在读的内容」：专注时由 app.js 挪到 .main 下，浮在正文顶部中央 */
html.focus-mode #match-badge {
  position: fixed;
  top: 12px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 45;
  margin: 0;
  padding: 3px 4px 3px 10px;
  border-radius: var(--r-full);
  border-color: var(--border-strong);
  box-shadow: var(--shadow-2);
}

/* 退出胶囊：静止 opacity 0；鼠标移动 / 聚焦时淡入（.awake），静止 2 秒后淡出 */
.focus-exit {
  position: fixed;
  top: 14px;
  right: 18px;
  z-index: 50;                     /* 高于桌面版拖动条 #focus-drag（40） */
  display: inline-flex;
  align-items: center;
  height: 32px;
  padding: 0 14px;
  border: 1px solid var(--border-strong);
  border-radius: var(--r-full);
  background: color-mix(in srgb, var(--bg-elev) var(--glass-tint), transparent);
  -webkit-backdrop-filter: blur(20px) saturate(1.4);
  backdrop-filter: blur(20px) saturate(1.4);
  box-shadow: var(--shadow-2);
  color: var(--text);
  font: inherit;
  font-size: 12px;
  white-space: nowrap;
  cursor: pointer;
  opacity: 0;
}
.focus-exit.hidden { display: none; }
.focus-exit.awake,
.focus-exit:hover,
.focus-exit:focus-visible { opacity: 1; }
.focus-exit:hover { border-color: var(--accent); }
.focus-exit:focus-visible { outline: none; box-shadow: var(--ring), var(--shadow-2); }
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .focus-exit { background: var(--bg-elev); }
}

/* 阅读进度：.preview 顶部 2px 强调色细条，transform: scaleX(p)（不改 width，不触发布局） */
.focus-progress {
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 2px;
  z-index: 7;
  background: var(--signal);
  transform: scaleX(0);
  transform-origin: 0 50%;
  pointer-events: none;
}
.focus-progress.hidden { display: none; }

/* 桌面版（§3g）：顶栏被隐藏后补一条透明拖动条；正文下移 28px，让拖动条压在空白上 */
#focus-drag { display: none; }
html[data-desktop="darwin"].focus-mode #focus-drag {
  display: block; position: fixed; inset: 0 0 auto 0; height: 28px;
  -webkit-app-region: drag; z-index: 40;              /* 低于 #focus-exit（z-index: 50） */
}
html[data-desktop="darwin"].focus-mode .preview { padding-top: 28px; }
html[data-desktop="darwin"].focus-mode #focus-exit,
html[data-desktop="darwin"].focus-mode #match-badge { -webkit-app-region: no-drag; }

@media (prefers-reduced-motion: no-preference) {
  /* visibility：进入专注时等收起动画走完再 hidden（延迟 --dur），
     退出时立刻 visible——退出后焦点要马上能回到顶栏的 #btn-focus */
  :root:not([data-motion="reduce"]) body.tx-ready :is(.main-header, #tab-bar) {
    transition: height var(--dur) var(--ease-drawer),
                padding-top var(--dur) var(--ease-drawer), padding-bottom var(--dur) var(--ease-drawer),
                padding-left var(--dur) var(--ease),   /* 桌面版收起侧栏时让出红绿灯（只在 data-desktop 下变化） */
                border-bottom-width var(--dur) var(--ease-drawer),
                opacity var(--dur) var(--ease-drawer), visibility 0s;
  }
  :root.focus-mode:not([data-motion="reduce"]) body.tx-ready :is(.main-header, #tab-bar) {
    transition: height var(--dur) var(--ease-drawer),
                padding-top var(--dur) var(--ease-drawer), padding-bottom var(--dur) var(--ease-drawer),
                padding-left var(--dur) var(--ease),   /* 桌面版收起侧栏时让出红绿灯（只在 data-desktop 下变化） */
                border-bottom-width var(--dur) var(--ease-drawer),
                opacity var(--dur) var(--ease-drawer), visibility 0s linear var(--dur);
  }
  :root:not([data-motion="reduce"]) body.tx-ready #update-banner {
    transition: max-height var(--dur) var(--ease-drawer), height var(--dur) var(--ease-drawer),
                opacity var(--dur) var(--ease-drawer), visibility 0s,
                border-bottom-color var(--dur-fast);
  }
  :root.focus-mode:not([data-motion="reduce"]) body.tx-ready #update-banner {
    transition: max-height var(--dur) var(--ease-drawer), height var(--dur) var(--ease-drawer),
                opacity var(--dur) var(--ease-drawer), visibility 0s linear var(--dur),
                border-bottom-color var(--dur-fast);
  }
  :root:not([data-motion="reduce"]) .focus-exit {
    transition: opacity var(--dur-med) var(--ease-standard), border-color var(--dur-fast);
  }
}

/* ============================================================
 * 设置弹窗：固定高度 + 星图视觉语言（client-fix §7）
 * ------------------------------------------------------------
 * 以前面板按内容取高、垂直居中，切到内容少的分区高度从 660 掉到 335、顶边上下跳 163px。
 * 现在面板高度固定为 min(640px, 视口 - 96px)，与当前分区无关；内容区内部滚动。
 * 外观：实底 --bg-elev（不再是 94% 玻璃压在模糊遮罩上），左栏 --bg，每一节是一张发丝描边的卡片，
 * 标题 / 说明文字收紧成控制台读数的层级。所有颜色都取令牌，深浅 × 冷暖四组合同一套规则，
 * 文字令牌的对比度由 contrast-tokens.spec 守护。所有选项、id 与事件绑定不变。
 * ============================================================ */
.modal-panel.settings-panel {
  height: min(640px, calc(100vh - 96px));
  max-height: none;
  background: var(--bg-elev);
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  border-color: var(--border-strong);
}
.settings-panel .modal-header { padding: 10px 10px 10px 18px; }
.settings-panel .modal-header h2 { font-size: 13.5px; font-weight: 650; letter-spacing: .01em; }
.settings-panel .settings-body { grid-template-columns: 176px minmax(0, 1fr); }
.settings-panel .settings-nav { padding: 12px 8px; gap: 1px; background: var(--bg); }
.settings-panel .settings-nav .settings-nav-item { height: 32px; font-size: 12.5px; }
.settings-panel .settings-nav .settings-nav-item.is-on {
  background: var(--bg-active);
  color: var(--text);
  box-shadow: inset 2px 0 0 var(--accent);
}
.settings-panel .settings-content {
  padding: 14px 16px 18px;
  display: flex; flex-direction: column; gap: 10px;
  scrollbar-gutter: stable;
}
.settings-panel .settings-content .modal-section {
  padding: 14px 16px;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--bg);
}
.settings-panel .settings-content .modal-section[hidden] { display: none; }
.settings-panel .settings-content .modal-section:not([hidden]):has(+ .modal-section[hidden]) { border-bottom: 1px solid var(--border); }
.settings-panel .modal-section h3 { margin: 0 0 4px; font-size: 12.5px; font-weight: 650; color: var(--text); }
.settings-panel .modal-section .muted { margin: 2px 0 10px; font-size: 11.5px; line-height: 1.55; color: var(--text-dim); max-width: 62ch; }
.settings-panel .modal-section .muted:last-child { margin-bottom: 0; }
.settings-panel .theme-choice { gap: 6px; }
.settings-panel.modal-panel .theme-choice .theme-option { padding: 10px 8px; gap: 5px; background: var(--bg-elev); border-color: var(--border); }
.settings-panel.modal-panel .theme-choice .theme-option:hover { border-color: var(--border-strong); }
.settings-panel.modal-panel .theme-choice .theme-option.is-on { border-color: var(--accent); background: var(--accent-soft); color: var(--text); }
.settings-panel.modal-panel .theme-choice .theme-option.is-on .ico { color: var(--accent); }
.settings-panel .settings-nav .settings-version { padding-top: 12px; font-family: var(--font-mono); font-size: 10.5px; }
/* 焦点环只给键盘：鼠标点开设置时自动聚焦的关闭按钮不再挂一圈粗强调色 */
.modal .modal-close:focus:not(:focus-visible) { outline: none; box-shadow: none; }
.modal .modal-close:focus-visible { outline: none; box-shadow: var(--ring); }

/* ============================================================
 * 封面缩略图：裁切 / 放大 / 角标 / 深色压暗（client-fix §12）
 * ------------------------------------------------------------
 * 截图现在是 800×500 CSS 视口的"首屏上部"（lib/thumbnail.js）。封面 212×96 里再放大 1.4 倍、
 * 从左上角裁：标题与第一块内容占满封面，不再是一整页缩成邮票。
 * 类型角标从左上（正是报告标题的位置）挪到右下，缩小并改实底。
 * 深色主题：HTML 文档自带白底，截图是白的，在深色卡片上刺眼 → 压暗并降一点对比度；
 * MD / 其它类型的渲染页本来就按深色主题截图，不处理。
 * ============================================================ */
.cover-art .cover-thumb {
  inset: 0 auto auto 0;
  width: 140%;
  height: auto;
  object-fit: initial;
}
.cover-art .cover-badge {
  top: auto; left: auto; right: 6px; bottom: 6px;
  height: 16px; padding: 0 6px;
  font-size: 9.5px;
  background: var(--bg-elev);
  border: 1px solid var(--border);
  color: var(--text-dim);
}
:root[data-theme="dark"] .cover-card[data-doctype="html"] .cover-thumb.loaded { filter: brightness(.72) contrast(.9); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .cover-card[data-doctype="html"] .cover-thumb.loaded { filter: brightness(.72) contrast(.9); }
}

/* 时间河流：截顶的尖峰柱在柱顶标注真实次数（river.js riverScale，client-fix §10） */
.river-peaks { position: absolute; inset: 0; pointer-events: none; }
.river-peak {
  position: absolute; top: -15px; transform: translateX(-50%);
  font-family: var(--font-mono); font-size: 10px; line-height: 13px;
  color: var(--text-dim); font-variant-numeric: tabular-nums;
  padding: 0 3px; border-radius: 3px; background: var(--bg-elev);
}
.river-svg .bar.peak { fill: var(--accent); }

/* ==================== iCloud 云盘（design §7） ====================
 * 只在 iCloud 里、没下载到本机的文件：照常列出，文件名灰显 + 一个云朵徽标。
 * 灰显只换文字令牌（--text-faint / 选中时 --text-dim），不用 opacity：这样对比度仍由
 * contrast-tokens.spec 守着的现有令牌保证，也不用新增令牌。 */
.file.is-cloud .file-name { color: var(--text-faint); }
.file.is-cloud.active .file-name { color: var(--text-dim); }
/* 徽标固定 14px 宽、不收缩：不改变 26px 行高、不换行（sidebar-row-stability 守着） */
.cloud-badge {
  flex: none; width: 14px; height: 14px;
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--text-faint);
}
.file:hover .cloud-badge,
.file:has(.file-actions :focus-visible) .cloud-badge { visibility: hidden; }
/* 多一个「下载到本机」按钮：7 × 18px + 6 个 1px 间隙 = 132px */
.file.can-cloud-dl:hover .file-actions,
.file.can-cloud-dl .file-actions:has(:focus-visible) { width: 132px; }
.file-actions button:disabled { opacity: .45; cursor: not-allowed; }
.file-actions button:disabled:hover { background: transparent; color: inherit; }

/* 首页封面卡：装饰层去色（不影响文字），正文里加一行「在 iCloud 中」 */
.cover-card.is-cloud .cover-art { filter: saturate(0); }
.cover-cloud {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: var(--fs-xs); color: var(--text-faint);
}
.cover-card.is-cloud .cover-name { color: var(--text-dim); }

/* ⌘K 结果行 */
.quickopen-item.is-cloud .qo-name { color: var(--text-dim); }
.quickopen-item .cloud-badge { margin-left: 2px; }

/* 占位面板：盖住预览区（和首页 .empty 一样 absolute inset:0），预览帧此时不导航 */
.cloud-panel {
  position: absolute; inset: 0; z-index: 2;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 8px; padding: 32px;
  background: var(--bg);
  color: var(--text-dim);
  text-align: center;
}
.cloud-panel.hidden { display: none; }
.cloud-panel > .ico { color: var(--text-faint); margin-bottom: 4px; }
.cloud-panel h2 { margin: 0; font-size: var(--fs-lg); font-weight: var(--fw-semi); color: var(--text); word-break: break-all; }
.cloud-panel p { margin: 0; font-size: var(--fs-sm); line-height: 1.55; max-width: 52ch; }
.cloud-panel .cloud-panel-meta { color: var(--text-faint); font-variant-numeric: tabular-nums; }
.cloud-panel .muted { color: var(--text-dim); }
.cloud-panel-actions { display: flex; gap: 8px; margin-top: 8px; }
.cloud-panel-actions button {
  height: 30px; padding: 0 14px;
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  background: var(--bg-elev-2);
  color: var(--text);
  font: inherit; font-size: var(--fs-sm);
  cursor: pointer;
  transition: background var(--dur-fast), border-color var(--dur-fast);
}
.cloud-panel-actions button:hover:not(:disabled) { background: var(--bg-hover); }
.cloud-panel-actions button:focus-visible { outline: none; box-shadow: var(--ring); }
.cloud-panel-actions button.primary { background: var(--accent); border-color: var(--accent); color: var(--on-accent); font-weight: var(--fw-medium); }
.cloud-panel-actions button.primary:hover:not(:disabled) { background: var(--accent); filter: brightness(1.08); }
.cloud-panel-actions button:disabled { opacity: .6; cursor: not-allowed; }
.cloud-panel-actions button[hidden] { display: none; }
#cloud-panel-status:empty { display: none; }

/* 设置：扫描根列表里的友好名 + iCloud 入口 */
.root-list li .root-label {
  flex: none;
  font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", sans-serif; font-size: 11.5px; font-weight: var(--fw-semi);
  color: var(--text);
}
.root-list li .root-warn {
  flex-basis: 100%;
  font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", sans-serif; font-size: 11.5px; line-height: 1.5;
  color: var(--danger);
}
.root-list li:has(.root-warn) { flex-wrap: wrap; }
.root-icloud { margin-top: 10px; }
.root-icloud[hidden] { display: none; }
.root-icloud button { display: inline-flex; align-items: center; gap: 6px; }
.root-icloud .muted { margin-top: 6px; }

/* 下载中：徽标轻轻呼吸。属于「运动需授权」：只在没有 reduced-motion 时跑，
 * 两路 reduce（系统偏好 / html[data-motion="reduce"]）下一律 animation:none */
@keyframes atlas-cloud-pulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: .4; }
}
@media (prefers-reduced-motion: no-preference) {
  :root:not([data-motion="reduce"]) .is-downloading .cloud-badge {
    animation: atlas-cloud-pulse var(--dur-ambient) var(--ease-standard) infinite;
  }
}
@media (prefers-reduced-motion: reduce) {
  .is-downloading .cloud-badge { animation: none; }
}
:root[data-motion="reduce"] .is-downloading .cloud-badge { animation: none; }
