/* ==========================================================================
   设计令牌 · Design Tokens
   三套主题（light / dark / midnight）+ 可换主色。

   色彩策略：中性色偏暖（纸/墨），不用蓝黑；主色只在「主要动作、当前项、
   焦点环、状态点」四处出现，其余一律走中性色。这样界面不会满屏高亮色，
   也不会随主色切换而整体变色。
   ========================================================================== */

:root {
  /*
   * 顶部安全区。
   *
   * index.html 里状态栏样式是 default（不是 black-translucent），
   * 所以系统会给状态栏自己的背景、网页从它下面开始 —— 两者永不重叠，
   * 这里就是 0，顶栏贴在网页顶端即可。
   *
   * 别再给它加"下限"或让 JS 按机型猜一个值：
   * 那些都是给 black-translucent 打的补丁。meta 改对之后，
   * 补丁反而会凭空多出一截空白（iPhone 上会多 59px）。
   */
  --safe-top: env(safe-area-inset-top, 0px);

  /* ---- 主色：boot.js 只写 --primary-raw，其余按主题在下面派生 ---- */
  --primary-raw: #0f766e;

  --primary: var(--primary-raw);
  --primary-foreground: #ffffff;
  --primary-soft: color-mix(in srgb, var(--primary) 10%, transparent);
  --primary-line: color-mix(in srgb, var(--primary) 36%, var(--border));
  --ring: var(--primary);

  /* ---- 排版 ---- */
  --font-sans: "PingFang SC", "HarmonyOS Sans SC", "Microsoft YaHei", "Hiragino Sans GB",
    -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  /*
   * 标题字：**黑体，不是宋体**。
   *
   * 这里原来是一支衬线（"Songti SC", "Noto Serif SC", Georgia, serif），
   * 用在全站的大标题、昵称署名、年刊标题上。用户明确否掉了它：
   * **「宋体是个写字的字体，它不是太适合用来制作这个应用字体」** —— 这个判断是对的：
   *   1. 宋体的横细竖粗是为纸面阅读设计的，在 13–15px 的界面字号下笔画会糊；
   *   2. 这一页里它只出现在标题上、正文全是黑体，两种字体混排像两套东西拼的；
   *   3. 中文界面本来就不靠衬线建立层级，靠的是**字号、字重、颜色**。
   *
   * 所以令牌名保留（21 处引用不用动），**值换成和正文同一支黑体**。
   * 层级改由字重和字号承担：标题 600、正文 400/500，这一页的 11px 等宽标注继续
   * 走 --font-mono 做"另一层"的对比（等宽 vs 比例，而不是衬线 vs 黑体）。
   *
   * 英文（拉丁）单独选一支字的事**还没做** —— 用户说「英文再说」。
   * 现在拉丁字符会落到这个栈里的 system-ui / Segoe UI / Roboto，
   * 和中文的苹方是同一档气质，先保持一致。
   */
  --font-display: "PingFang SC", "HarmonyOS Sans SC", "Microsoft YaHei", "Hiragino Sans GB",
    -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  /*
   * 等宽那一层：**也统一成黑体**。
   *
   * 这里原来是一支等宽字（ui-monospace / SF Mono / Menlo…），前几轮靠它承担
   * 全站的"标注腔调"（片号、小节标题、字段名、状态摘要、存储读数、快捷键键帽）。
   * 用户要求**全站只留一种字体、统一成黑体** —— 包括这一层。
   *
   * 令牌名保留（15 处引用不必改动，且 `code` / `kbd` 这类标签仍需要"走这一支"的语义），
   * 值换成和正文同一支黑体。代价与补偿：
   *   · 丢掉的是**等宽的字身比例**（等宽字看起来像"机器读数"）；
   *   · 但**数字对齐不能丢** —— 片号 01…08 要左对齐成一条线、存储读数要竖着对齐。
   *     这靠 `font-variant-numeric: tabular-nums`，它让比例字体里的数字等宽。
   *     凡是原来靠等宽来对齐数字的地方，都必须有这一条（见 pages.css）。
   *   · 层级改由**字号 + 字距 + 颜色**承担，不再靠换字形。
   */
  /*
   * 全站**唯一**的一处等宽例外：给"要一个字一个字抄下来"的串用（恢复码 / 邀请码）。
   *
   * 上面三个令牌已经按用户要求统一成黑体了，这里不是反悔 —— 是这两种字的用途不同：
   * 正文看的是"读起来顺不顺"，而一串码看的是"这一个是 0 还是 O、是 1 还是 l"。
   * 比例字体里 0/O、1/l/I 的骨架几乎一样，抄错一位整个码就废了，
   * 而恢复码**只显示一次**、错了就得重新生成。
   *
   * 只列系统自带（这个应用零依赖、完全离线，不许联网取字体），
   * 覆盖 macOS / Windows / Android，最后退回通用 monospace。
   */
  --font-code: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
               "Cascadia Mono", "Roboto Mono", "Noto Sans Mono CJK SC", monospace;
  --font-mono: "PingFang SC", "HarmonyOS Sans SC", "Microsoft YaHei", "Hiragino Sans GB",
    -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;

  --text-2xs: 0.6875rem;
  --text-xs: 0.75rem;
  --text-sm: 0.8125rem;
  --text-base: 0.9375rem;
  --text-lg: 1.0625rem;
  --text-xl: 1.25rem;
  --text-2xl: 1.5rem;
  --text-3xl: 1.875rem;
  --text-4xl: 2.5rem;

  --leading-tight: 1.15;
  --leading-snug: 1.35;
  --leading-normal: 1.62;

  /* ---- 间距刻度 ---- */
  --space-0: 0;
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-5: 1.25rem;
  --space-6: 1.5rem;
  --space-8: 2rem;
  --space-10: 2.5rem;
  --space-12: 3rem;
  --space-16: 4rem;

  /* ---- 圆角：整体收紧，避免「每个块都是大圆角气泡」 ---- */
  --radius-xs: 3px;
  --radius-sm: 4px;
  --radius-md: 6px;
  --radius-lg: 8px;
  --radius-xl: 10px;
  --radius-2xl: 14px;
  --radius-full: 9999px;

  /* ---- 阴影：只用来说明层级，不用来当装饰 ---- */
  --shadow-xs: 0 1px 1px rgb(0 0 0 / 0.03);
  --shadow-sm: 0 1px 2px rgb(0 0 0 / 0.05);
  --shadow-md: 0 2px 10px rgb(0 0 0 / 0.07);
  --shadow-lg: 0 10px 30px rgb(0 0 0 / 0.12);
  --shadow-xl: 0 24px 56px rgb(0 0 0 / 0.20);
  --shadow-glow: 0 0 0 1px var(--primary-line);

  --z-base: 1;
  --z-sticky: 20;
  --z-nav: 40;
  --z-overlay: 60;
  --z-modal: 70;
  --z-toast: 90;
  --z-cmdk: 100;

  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
  --ease-spring: cubic-bezier(0.34, 1.4, 0.64, 1);
  --dur-fast: 110ms;
  --dur-normal: 180ms;
  --dur-slow: 300ms;
  /*
   * 比 --dur-slow 更长的一档，目前只有年刊"月度足迹"的柱子入场在用。
   *
   * 为什么要单独一档：12 根柱子要一根根立起来（错帧 67ms），
   * 单根动画压在 300ms 里会让整组显得急促，撑到 900ms 才有"一年慢慢攒起来"的感觉。
   *
   * 加这一档时**必须**同时改下面 prefers-reduced-motion 那一块。
   * 那块是逐条覆写（白名单式）的，新令牌不会自动被压平 ——
   * 漏了它，"减少动态效果"的用户照样要等 900ms。
   * 静态自检抓不到这种漏（check.mjs 不校验这个块），只能靠人记。
   */
  --dur-reveal: 900ms;
  /*
   * 「文本展开」这一档（档案馆页「概览」面板）：比 --dur-slow 更长、曲线更匀。
   *
   * 为什么不能直接用 --dur-slow + --ease-out：那不是"稍微快一点"，是**根本看不见**。
   * --ease-out（0.16, 1, 0.3, 1）前 3 帧就走掉 60%，450px 的面板实测单帧最多跳 138px
   * —— 占总高的 31%。眼睛读到的是"啪一下多出一块"，而这一段动效的**全部意义**
   * 就是让用户看见空间在打开（同一个内容被打开，而不是页面里凭空长出一个新区域）。
   * 400ms + --ease-in-out：单帧最多约 8%，每一帧都看得出在长，整段也不到半秒。
   *
   * 加这一档时**必须**同时改下面 prefers-reduced-motion 那一块 —— 理由同 --dur-reveal。
   */
  --dur-disclose: 400ms;
  /*
   * 依次入场的错帧步长：每格错开 67ms ≈ 60fps 的 4 帧。
   * 12 格 × 67ms = 737ms，加上单根 900ms，整组约 1.6s 收完。
   *
   * 单独做一个令牌而不是把它写死在 animationDelay 里，
   * 是为了让 prefers-reduced-motion 下也能一起归零 —— 否则柱子虽然
   * 单根是 1ms，却还要一根一根等 67ms 才出现，等于"减少动效"只减了一半。
   */
  --stagger-reveal: 67ms;
  /*
   * 主题切换（换成深色/午夜从触点往外扩、换回浅色把深色收进触点）的时长与曲线。
   *
   * 为什么单独一档，不套 --dur-slow（300ms）：
   * 这一下要扫过**整个屏幕** —— 圆心到最远那个角可能一千多像素，
   * 300ms 走完看着是"刷"地盖住，读不出"扩散"这件事；
   * 而 --dur-reveal（900ms）是年刊慢慢攒一年的节拍，搬到这里会显得拖。
   *
   * 760ms 是"看得见过程"和"不浪费时间"之间的那个点：460ms 那版被否过 ——
   * 快到只看见结果，没有"慢慢扩出来/被吸进去"的感觉；
   * 再往 1s 上去，就成了每次换主题都要等它演完。
   *
   * 曲线不用 --ease-out（0.16, 1, 0.3, 1）：那一条是"起手极快、尾巴很长"，
   * 圆会在前 100ms 就吃掉约 87% 的半径，然后慢慢爬进四个角 ——
   * 观感是"早就完了却还在动"。扩散要的是先聚一下再推出去，
   * 所以用标准的减速曲线（起步略缓、中段最快、收尾停稳）。
   *
   * 加这一档同样**必须**改下面 prefers-reduced-motion 那一块（见 --dur-reveal 的说明）。
   */
  --dur-wipe: 760ms;
  --ease-wipe: cubic-bezier(0.4, 0, 0.2, 1);

  --layout-max: 1180px;
  --header-h: 3.25rem;
  --tabbar-h: 3.5rem;
  --sidebar-w: 12.5rem;
}

/* ---------------------------------------------------------------- 浅色 · 纸 */
:root,
.light {
  --background: #f6f4ef;
  --background-elevated: #ffffff;
  --foreground: #1b1a17;
  --card: #ffffff;
  --card-foreground: #1b1a17;
  --popover: #ffffff;
  --popover-foreground: #1b1a17;

  --secondary: #eae6dd;
  --secondary-foreground: #1b1a17;
  --muted: #eae6dd;
  --muted-foreground: #5f5a54;
  --faint-foreground: #6f6861;
  --accent: #eae6dd;
  --accent-foreground: #1b1a17;

  --info: #2b6ca3;
  --info-foreground: #f7fbff;
  --destructive: #ac3529;
  --destructive-foreground: #ffffff;
  --success: #2f7d4f;
  --success-foreground: #f6fdf8;
  --warning: #97700f;
  --warning-foreground: #fffdf5;

  --border: #e1dcd1;
  --border-strong: #cbc4b6;
  --input: #d9d3c6;
  --overlay: rgb(27 26 23 / 0.42);

  --chart-1: #0f766e;
  --chart-2: #2b6ca3;
  --chart-3: #b0651d;
  --chart-4: #86263a;
  --chart-5: #34633a;

  --sidebar: #efebe3;
  --sidebar-foreground: #1b1a17;
  --sidebar-primary: var(--primary);
  --sidebar-primary-foreground: #ffffff;
  --sidebar-accent: #e3ded3;
  --sidebar-accent-foreground: #1b1a17;
  --sidebar-border: #e1dcd1;

  --skeleton: linear-gradient(90deg, #eae6dd 25%, #f3f0e9 37%, #eae6dd 63%);
  --scrim-from: rgb(0 0 0 / 0.86);
  --scrim-to: rgb(0 0 0 / 0.62);
  /*
   * 照片抽屉（图片灯箱）的全屏底。
   * 单独一档是因为它原来硬编码 rgb(0 0 0 / 0.92)，而 --scrim-from 是 0.86 ——
   * 看大图时背景要更沉一点，但深色主题下再往 0.92 压就整块糊死了。
   * 逐主题给值，别再退回硬编码（浅色 0.92 / 深色 0.9 / 午夜 0.94）。
   */
  --photo-backdrop: rgb(0 0 0 / 0.92);
  /*
   * 设置页那条页内索引被顶到顶栏下面时的玻璃底。
   * 逐主题给值（和 --photo-backdrop 一个道理）：它由 --popover 派生，
   * 而 --popover 在三套主题里差得远，写死一次会在深色下变成一块灰。
   * 手法和关系图的工具条一致（见 pages.css 的 .graph-toolbar）。
   */
  --settings-glass: color-mix(in srgb, var(--popover) 90%, transparent);
}

/* ---------------------------------------------------------------- 深色 · 墨 */
.dark {
  --primary: color-mix(in oklab, var(--primary-raw) 74%, white);
  --primary-foreground: #0b0b0a;
  --primary-soft: color-mix(in srgb, var(--primary) 14%, transparent);
  --primary-line: color-mix(in srgb, var(--primary) 34%, var(--border));

  --background: #131311;
  --background-elevated: #1c1c19;
  --foreground: #f1eee8;
  --card: #1a1a17;
  --card-foreground: #f1eee8;
  --popover: #1e1e1b;
  --popover-foreground: #f1eee8;

  --secondary: #27261f;
  --secondary-foreground: #f1eee8;
  --muted: #27261f;
  --muted-foreground: #9c958c;
  --faint-foreground: #8a837b;
  --accent: #27261f;
  --accent-foreground: #f1eee8;

  --info: #6ba6d6;
  --info-foreground: #06121c;
  --destructive: #d9645a;
  --destructive-foreground: #1a0b09;
  --success: #62b483;
  --success-foreground: #05130a;
  --warning: #d7a441;
  --warning-foreground: #1a1204;

  --border: #2b2a25;
  --border-strong: #3d3b34;
  --input: #2b2a25;
  --overlay: rgb(0 0 0 / 0.66);

  --chart-1: #4fb3a3;
  --chart-2: #6ba6d6;
  --chart-3: #d7a441;
  --chart-4: #d9645a;
  --chart-5: #7cb87f;

  --sidebar: #0f0f0e;
  --sidebar-foreground: #f1eee8;
  --sidebar-primary: var(--primary);
  --sidebar-primary-foreground: #0b0b0a;
  --sidebar-accent: #22221e;
  --sidebar-accent-foreground: var(--primary);
  --sidebar-border: #232320;

  --skeleton: linear-gradient(90deg, #1e1e1b 25%, #27261f 37%, #1e1e1b 63%);
  --scrim-from: rgb(0 0 0 / 0.88);
  --scrim-to: rgb(0 0 0 / 0.66);
  --photo-backdrop: rgb(0 0 0 / 0.9);
  --settings-glass: color-mix(in srgb, var(--popover) 90%, transparent);
  color-scheme: dark;
}

/* ---------------------------------------------------------------- 午夜 · 纯黑 */
.midnight {
  --primary: color-mix(in oklab, var(--primary-raw) 78%, white);
  --primary-foreground: #000000;
  --primary-soft: color-mix(in srgb, var(--primary) 14%, transparent);
  --primary-line: color-mix(in srgb, var(--primary) 34%, var(--border));

  --background: #000000;
  --background-elevated: #0d0d0c;
  --foreground: #eeece6;
  --card: #0c0c0b;
  --card-foreground: #eeece6;
  --popover: #101010;
  --popover-foreground: #eeece6;

  --secondary: #171715;
  --secondary-foreground: #eeece6;
  --muted: #171715;
  --muted-foreground: #948d84;
  --faint-foreground: #7f7970;
  --accent: #1b1b18;
  --accent-foreground: #eeece6;

  --info: #6ba6d6;
  --info-foreground: #06121c;
  --destructive: #d9645a;
  --destructive-foreground: #1a0b09;
  --success: #62b483;
  --success-foreground: #05130a;
  --warning: #d7a441;
  --warning-foreground: #1a1204;

  --border: #1e1e1c;
  --border-strong: #2e2e2a;
  --input: #1e1e1c;
  --overlay: rgb(0 0 0 / 0.76);

  --chart-1: #4fb3a3;
  --chart-2: #6ba6d6;
  --chart-3: #d7a441;
  --chart-4: #d9645a;
  --chart-5: #7cb87f;

  --sidebar: #000000;
  --sidebar-foreground: #eeece6;
  --sidebar-primary: var(--primary);
  --sidebar-primary-foreground: #000000;
  --sidebar-accent: #161614;
  --sidebar-accent-foreground: var(--primary);
  --sidebar-border: #171715;

  --skeleton: linear-gradient(90deg, #131312 25%, #1b1b19 37%, #131312 63%);
  --scrim-from: rgb(0 0 0 / 0.9);
  --scrim-to: rgb(0 0 0 / 0.7);
  --photo-backdrop: rgb(0 0 0 / 0.94);
  --settings-glass: color-mix(in srgb, var(--popover) 90%, transparent);
  color-scheme: dark;
}

/* 无障碍：尊重系统「减少动态效果」 */
@media (prefers-reduced-motion: reduce) {
  :root {
    --dur-fast: 1ms;
    --dur-normal: 1ms;
    --dur-slow: 1ms;
    --dur-reveal: 1ms;
    --stagger-reveal: 0ms;
    /* 新增的时长令牌必须在这里补一条：这块是白名单，不是"自动继承"。
       漏了这一条，「减少动态效果」的用户点开「概览」还得等 400ms 才看见内容。 */
    --dur-disclose: 1ms;
    --ease-out: linear;
    --ease-in-out: linear;
    --ease-spring: linear;
    /*
     * 主题扩散这一档其实是双保险：js/ui/theme-wipe.js 在"减少动态效果"下
     * **根本不发起**转场（直接换，那才是这个动效的终止态）。
     * 压平放在这里是为了守同一条规矩：凡是时长令牌，都必须在白名单里有一条 ——
     * 万一哪天有别的路径真的播到了这段关键帧，它也不会真的动起来。
     */
    --dur-wipe: 1ms;
    --ease-wipe: linear;
  }
}
