/*
   fonts.css — 冥想统计看板 demo · V2「册页」字体子集（X1 工卡·字体子集管线）
   CARD-F3（20260721）构建拆分改造：base64 内嵌 → 二进制文件引用（长缓存友好，
   对应 SPEC §7 服务器出口 3Mbps 门；字体 274KB 终身只下一次不再随每次访问重传）。
   三个 @font-face 的 url() 目标是占位符（QIJI/WENKAI/MONONUM 三个字体 key，
   各自套双花括号+_WOFF2_URL 后缀），由 web/build.js 按内容 sha256 前 8 位替换为
   dist/assets/ 下的真实哈希文件名；占位符名字不得改（build.js 靠 includes() 校验）。
   ⚠ 本注释故意不把占位符原文整串写出——它在下方 @font-face 里只应出现一次，
   写在文档里会被构建脚本的字符串替换误命中（CARD-F3 实测踩过的坑，见 build.js
   同款注释）。
   源二进制在 web/fonts/*.woff2（只读来源=demo/fonts/*-subset.woff2 原样复制，
   本文件不再是唯一的字体真源，改动字体需同时改 demo 侧子集再复制）。

   三层字体，family 名钉死，全 OFL 授权，子集化后独立 woff2 文件，零外部运行时依赖：
     display 巨字 QiJi   ← 齐伋体（明代刻本字形，LingDong-/qiji-font，OFL-1.1）
     body   正文 WenKai  ← 霞鹜文楷 LXGW WenKai Regular（lxgw/LxgwWenKai，OFL-1.1）
     num    数字 MonoNum ← JetBrains Mono Regular（JetBrains/JetBrainsMono，OFL-1.1）

   子集来源：src/gen-chars.js 扫描/显式清单生成 fonts/chars-*.txt →
     python -m fontTools.subset <字体> --text-file=<chars.txt> --flavor=woff2 --output-file=<out.woff2>
   子集体积（子集化后，V4.2 立轴首屏+度牒字段替换重子集 20260721，CARD-F3 实测校正）：
     WenKai 122.6 KB / QiJi 52.2 KB / MonoNum 29.3 KB，合计 204.2 KB
   （远低于 SPEC 3 规定的 700KB 预算上限；WenKai 是正文字体，计入首屏关键集 gzip
   预算，QiJi/MonoNum 走 font-display: swap 异步加载，不计首屏）

   回退栈（字体加载失败/子集缺字时页面仍可读，font-display: swap）：
     QiJi   → WenKai → "KaiTi", "STKaiti", serif
     WenKai → "KaiTi", "STKaiti", "Songti SC", serif
     MonoNum → ui-monospace, Consolas, monospace

   使用规则（SPEC 3 排印细则）：
     - 齐伋体（QiJi）禁做正文：字符集不全（仅显式清单 80 字），刻本字小号难读，只用于巨字标题/地支/产品名/印章。
     - 文楷（WenKai）正文最小 15px；巨字 letter-spacing −0.01em~0、行高 1.1；正文行高 1.75。
     - 一切统计数字/日期/时刻一律 MonoNum，tabular-nums，中文与数字混排时数字不切中文字体。
*/

@font-face {
  font-family: "QiJi";
  src: url(./qiji.39c06f20.woff2) format("woff2");
  font-weight: normal;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "WenKai";
  src: url(./wenkai.e5e5668f.woff2) format("woff2");
  font-weight: normal;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "MonoNum";
  src: url(./mononum.ba347bd0.woff2) format("woff2");
  font-weight: normal;
  font-style: normal;
  font-display: swap;
}

/* ============================================================================
   tokens.css — 冥想统计看板 demo 设计令牌（X2 工卡 · V2「册页」全案）
   ⚠ 本文件为 V2 推倒重做整体替换件，V1 双主题（极简 minimal / 水墨 ink）令牌
   契约作废；V1 成品已存档于 dist\冥想统计看板-demo-v1存档.html，不再维护。

   结构：
     第一层 primitive —— design-system-playbook/design-tokens.css 原样拷贝，
       不改一字（惯例：跨项目共享的机械尺度层：间距/控件高度/动效曲线/
       降级工具类）。V2 单主题不使用其中的通用灰阶/语义状态色/图表灰阶，
       保留只为遵循 playbook 拷贝纪律，不代表业务侧引用。
     第二层 semantic —— 本文件唯一改写区，V2「册页」单一主题
       （data-theme="ink" 沿用，同时写在 :root 上做零属性兜底）。
   使用纪律：全站业务代码（app.css 及 X3/X4 全部产物）颜色只能写 var(--*)，
   本文件是唯一允许出现裸色值（hex/rgba 字面量）的地方（gates.py no-raw-hex
   按文件名自动放行 tokens.css）。
   ========================================================================== */

/* ============================================================================
   第一层 · PRIMITIVE（原样拷贝自 design-system-playbook/design-tokens.css v1.1，
   不改一字；V2 语义层不引用其中的通用灰阶/语义色/图表灰阶/生成的 focus-ring，
   只借用间距/控件尺寸/动效时长-曲线/降级工具类这几组跨项目机械尺度）
   ========================================================================== */
:root {
  /* ---- 灰阶 · 中性系(默认;工具/精密感产品) ----------------------------------
     10 级"意图编码":每级一个固定职责,不是越深越重要 [官方:Vercel design.md] */
  --bg:        #FFFFFF;  /* 页面底 */
  --bg-2:      #FAFAFA;  /* 次级区块底/表头底 */
  --fill-1:    #F5F5F5;  /* 控件默认底 · 行 hover 底 */
  --fill-2:    #EBEBEB;  /* 控件 hover 底 */
  --fill-3:    #E0E0E0;  /* 控件 active 底 */
  --line:      #ECECEC;  /* 分隔线 —— 全站唯一 */
  --border:    #E2E2E4;  /* 控件边框 —— 全站唯一 */
  --border-hv: #CFCFD2;  /* 边框 hover(同笔更深一档,不换色) */
  --ink-ph:    #9A9A9A;  /* 仅 placeholder/禁用字。注意:白底 2.8:1 不过 WCAG AA
                            (placeholder 不在豁免清单!)——严格合规项目改 #767676
                            并靠字重/斜体区分占位与正文 [官方:WCAG 1.4.3] */
  --ink-2:     #666666;  /* 次级文字/图标(白底 5.7:1 过 AA) */
  --ink:       #111111;  /* 主文字/主按钮底 = 主色 */
  /* 次次级文字不再拍新灰值:color:var(--ink) + opacity .6/.3 [官方:Apple 同基色透明度阶梯] */

  /* ---- 半透明叠加(叠在色块/图表/图片上时用它,不用实色灰,防发脏) [官方:Vercel] */
  --alpha-1: rgba(0,0,0,.03);
  --alpha-2: rgba(0,0,0,.05);
  --alpha-3: rgba(0,0,0,.08);
  --alpha-4: rgba(0,0,0,.14);  /* 输入框默认边(alpha 式,随底色自适应) [官方:Geist] */

  /* ---- 语义色:只进状态点/pill/涨跌/告警/图表,永不进骨架 [实测:Attio 法则] ---- */
  --ok:   #16A34A;
  --warn: #D97706;
  --err:  #DC2626;
  --info: #2563EB;   /* 只许缩在圆点/pill 里,不做链接色不做焦点环 */

  /* ---- 图表灰阶(亮度必须单调:网格 > 轴线 > 轴标签,gates 可排序验证) -------- */
  --chart-s1:    #18181B;  /* 主系列(近黑非纯黑) */
  --chart-s2:    #71717A;  /* 第二系列 */
  --chart-s3:    #A1A1AA;  /* 第三系列;>3 系列才允许引入色相 */
  --chart-grid:  #F4F4F5;
  --chart-axis:  #E4E4E7;
  --chart-label: #71717A;

  /* ---- 圆角 [官方:Vercel 6/12/999 + 实测:Notion 4/8-12] ---- */
  --r-xs:   4px;    /* kbd 徽标/小按钮 */
  --r-ctl:  6px;    /* 按钮/输入框 */
  --r-card: 10px;   /* 卡片 */
  --r-pop:  12px;   /* 菜单/弹窗/抽屉 */
  --r-pill: 999px;

  /* ---- 阴影:默认态零阴影,只属于浮层 [官方:Vercel design.md 原值] ---- */
  --sh-raised: 0 2px 2px rgba(0,0,0,.04);
  --sh-pop: 0 1px 1px rgba(0,0,0,.02), 0 4px 8px -4px rgba(0,0,0,.04),
            0 16px 24px -8px rgba(0,0,0,.06);
  /* Notion 式更软替代(内容类产品):多层且每层 alpha≤.05 [实测] */

  /* ---- 焦点环:双层同心(第一层=与底同色的间隙,第二层才是环) [官方:Geist 结构] */
  --focus-ring: 0 0 0 2px #FFF, 0 0 0 4px #111;
  --focus-ring-err: 0 0 0 2px #FFF, 0 0 0 4px var(--err);

  /* ---- 动效 [官方:Vercel 150/200/300 + 一手:Emil Kowalski/Rauno] ----
     纪律: 只动 transform/opacity;禁 transition:all;退场≈进场×0.67;
     高频动作(命令面板/右键菜单)0ms;数据密集场景禁过冲(bezier 无 >1 分量) */
  --t-press: 120ms;   /* 按下反馈 */
  --t-fast:  160ms;   /* hover/tooltip/小浮层 */
  --t-pop:   200ms;   /* 菜单/下拉开合 */
  --t-modal: 240ms;   /* modal/drawer 进场 */
  --t-exit:  160ms;   /* 退场 ≈ 进场×0.67 [一手:Rauno;断言比例∈0.5-0.8] */
  --ease-out:  cubic-bezier(.23, 1, .32, 1);      /* 默认:进场与反馈 */
  --ease-exit: cubic-bezier(.4, 0, 1, 1);         /* 退场快收 [官方:M3 accelerate 系] */
  --ease-move: cubic-bezier(.77, 0, .175, 1);     /* 屏内 A→B 位移 */
  /* 禁纯 ease-in 做进场(慢启动=迟钝感) [一手:Emil];
     spatial(transform)时长 ≥ effects(opacity/color)时长 [官方:M3,可断言] */

  /* ---- 交互状态层: 同一色源(近黑)单调递增 [官方:M3 8/10/10/16% 结构,
       数值按白底下调——彩色 tonal 面的叠加量放白底会过重] ---- */
  --state-hover: rgba(0,0,0,.04);
  --state-focus: rgba(0,0,0,.06);
  --state-press: rgba(0,0,0,.06);
  --state-drag:  rgba(0,0,0,.10);

  /* ---- 排印 ----
     中文陷阱三条 [实测/官方:clreq+Inter 文档]:
     ① 字重只用 400/600(YaHei 无 500,浏览器伪加粗会糊;中等强调用颜色不用字重)
     ② 数字必须命中西文字体(CJK 字体无 tnum,tabular-nums 才能生效)——靠栈序实现
     ③ 中文禁负 letter-spacing;按钮文字垂直居中用 flex 不用 line-height 法 */
  --font-ui: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI",
             "PingFang SC", "Microsoft YaHei", "Noto Sans SC", sans-serif;
  --fs-aux:   12px;   /* 仅时间戳/版本号等强辅助,禁正文 */
  --fs-table: 13px;   /* 表格,行高 1.45 */
  --fs-body:  14px;   /* 中文正文下限 [官方:AntD 阅读距离依据],行高 1.6 */
  --fs-sub:   16px;
  --fs-h3:    20px;   /* 标题行高 1.2-1.35;层级=字号跳跃+字重跳跃,不做线性递增 */
  --fs-h1:    28px;
  --lh-body:  1.6;
  --lh-table: 1.45;
  --lh-tight: 1.3;

  /* ---- 间距(8pt 网格,半格 4 允许) [官方:Apple/Tailwind]
       两条定律 [书摘:Refactoring UI]: ①相邻档差 ≥25%(4/8/12/16/24/32/48/64 合规);
       ②组内间距 < 组间间距,比例 ≥2×(相关性=距离,可量测断言) ---- */
  --s1: 4px;  --s2: 8px;  --s3: 12px;  --s4: 16px;  --s5: 24px;  --s6: 32px;  --s7: 48px;

  /* ---- 控件尺寸 [官方:Geist] ---- */
  --h-ctl-s: 32px;  --h-ctl: 36px;  --h-ctl-l: 40px;
  --pad-ctl: 0 12px;
}

/* ---- 必备工具类(与 token 同源,业务侧直接用;沿用 primitive 惯例) ---- */
.tnum { font-variant-numeric: tabular-nums; }  /* 一切 KPI/金额/数字列必挂;
        验证法:渲染"1"和"0"量宽相等(CSS 不报错≠生效) */
kbd {
  font: 500 12px/1 var(--font-ui);
  padding: 2px 6px; border-radius: var(--r-xs);
  background: var(--fill-1); border: 1px solid var(--border); color: var(--ink-2);
}

@media (prefers-reduced-motion: reduce) {
  * { animation-duration: .01ms !important; transition-duration: .01ms !important; }
  /* 正确降级=去位移保留 opacity/color 感知,不是把"变化发生了"的信息一起删
     [官方:Apple HIG Motion] */
}


/* ============================================================================
   第二层 · SEMANTIC —— V2「册页」单一中式编辑排印主题
   （替换 V1 双主题 minimal/ink 契约；data-theme="ink" 沿用，:root 同值兜底，
   全站业务代码不再引用 --bg/--surface/--accent 等 V1 名字，一律改用下表）

   ---- 对比度实测记录（gates.py contrast，四色封顶纪律 SPEC §2）----
   fg          bg(--paper #f3ede0)   bg(--paper-2 #faf6ec)  bg(--paper-3 #eae2d0)
   --ink-2 #4a4438     8.28:1 OK            8.95:1 OK              7.49:1 OK
   --ink-3 见下条备注(已加深)
   --red-text/--line-red #b13b2e   5.09:1 OK   5.51:1 OK   4.61:1 OK(踩线达标)
   --indigo #425066    7.00:1 OK            7.57:1 OK              6.34:1 OK
   --ink   #1a1a1a     14.92:1 OK           16.13:1 OK             13.50:1 OK

   ⚠ --ink-3：SPEC §2 原值 #8a8171 实测对三种纸色均未达 4.5:1
     （3.30:1 / 3.56:1 / 2.98:1，其中对 --paper-3 甚至跌破 3.0:1 大字门槛），
     按 SPEC 本条注释「不达标则升 --ink-2」自行加深至 #675e50，
     加深后实测 5.47:1 / 5.91:1 / 4.95:1 三色全过 4.5:1，
     气质仍浅于 --ink-2（重墨），保留「淡墨」层级但不再是不可读的弱文本。
   ========================================================================== */
:root,
[data-theme="ink"] {
  /* ---- 四色封顶：纸(3阶)/墨(3阶)/朱(3用途)/黛(1) —— SPEC §2 令牌全表 ---- */
  --paper:      #f3ede0;  /* 宣纸 · 页面底（R7 验算基准，暖白非冷白） */
  --paper-2:    #faf6ec;  /* 票面 · 票据卡面/输入面/浮层底 */
  --paper-3:    #eae2d0;  /* 衬纸 · 本人行衬底/hover 衬底 */
  --paper-3-rgb: 234, 226, 208;  /* 衬纸 RGB 三元组（供 app.css 云絮渐变 rgba() 引用，避免裸色值，值与 --paper-3 保持同步） */
  --paper-2-rgb: 250, 246, 236;  /* 票面 RGB 三元组（R-032②：供成就清单浮层滚动渐变遮罩 rgba() 引用，与浮层卡底色 --paper-2 一致，避免裸色值，值与 --paper-2 保持同步） */
  --ink:        #1a1a1a;  /* 浓墨 · 主文（对纸 14.9:1） */
  --ink-2:      #4a4438;  /* 重墨 · 次级文 */
  --ink-3:      #675e50;  /* 淡墨 · 弱文/轴标/装饰刻线（已加深，见上方实测记录） */
  --line:       #c9bfa8;  /* 乌丝 · 默认界线/细线/界格 —— 全站唯一分隔灰 */
  --line-red:   #b13b2e;  /* 朱丝 · 强调界线（本人行/当前项/当前时辰弧） */
  --seal:       #ff461f;  /* 印朱 · 仅印章底色与色点，禁止做文字色 */
  --red-text:   #b13b2e;  /* 深朱 · 朱色文字唯一合法值（今日之数/榜首/校验错误） */
  --indigo:     #425066;  /* 黛蓝 · 辅强调文字（链接/次强调/英文体位） */

  /* 图表/热力/时辰盘：墨渍五阶（浓→淡），SPEC §2 固定值 */
  --chart-1: #1a1a1a;
  --chart-2: #4a4438;
  --chart-3: #7a7060;
  --chart-4: #ab9f88;
  --chart-5: #d6cdb8;

  /* ---- 字体三层（家族名钉死 QiJi/WenKai/MonoNum，由字体管线 @font-face 注册；
       本文件只声明栈序与回退，字体文件本身不属于 X2 工卡） ---- */
  --font-display: "QiJi", "WenKai", "KaiTi", "STKaiti", serif;          /* 齐伋体：巨字/地支/印章字，禁做正文；缺字回退内嵌文楷（V2 §3 规定栈） */
  --font-body:    "WenKai", "KaiTi", "STKaiti", "Songti SC", serif;     /* 霞鹜文楷：正文/按钮/标签/成员名 */
  --font-num:     "MonoNum", ui-monospace, Consolas, "WenKai", "KaiTi", "STKaiti", monospace; /* JetBrains Mono：数字/拉丁；混排汉字(时/分/天/笔/第/号/称号词)逐字回退内嵌文楷,杜绝系统黑体混入(G2 20260721 终审修) */

  /* ---- 字号（SPEC §4/§5 逐条落值） ---- */
  --fs-masthead:     11px;  /* 报头三段小字 */
  --fs-toc:          15px;  /* 目录条/纸签栏 */
  --fs-folio-d:      80px;  /* 巨字页标 · 桌面（SPEC 范围 64-88px） */
  --fs-folio-m:      50px;  /* 巨字页标 · 移动（SPEC 范围 44-56px） */
  --fs-folio-note:   12px;  /* 巨字页标小字注 */
  --fs-tagrule-label: 13px; /* 题签分隔左标题 */
  --fs-tagrule-meta:  12px; /* 题签分隔右端小字 */
  --fs-fieldlabel:   11px;  /* 字段表左列宽距小标签 */
  --fs-fieldvalue:   15px;  /* 字段表右列值 */
  --fs-ticketfoot:   11px;  /* 票据批次页脚 */
  --fs-body:         15px;  /* 正文下限（SPEC「文楷正文最小 15px」） */
  --fs-sub:          16px;  /* 面板标题/空态标题等次级标题字号 */
  --fs-body-sm:      13px;  /* 辅助/次级说明（非「正文」，不受 15px 下限约束） */
  --fs-caption:      12px;  /* 通用小字注/补录标记/页脚 */
  --fs-hero-num-d:   72px;  /* 核心数字横幅 · 桌面 */
  --fs-hero-num-m:   48px;  /* 核心数字横幅 · 移动 */
  --fs-hero-unit:    15px;  /* 「分钟」单位小字 */
  --fs-dizhi:        15px;  /* 时辰钟地支字 */
  --fs-dial-center:  32px;  /* 时辰钟盘心巨字 */
  --fs-dial-sub:     11px;  /* 时辰钟盘心小字注 */

  /* ---- V3 书名页专属字号（SPEC V3 §3，独立于站内 --fs-folio-*，
       避免登录/注册页的更高一级尺度牵动今日/我的/管理/排行榜等站内页标） ---- */
  --fs-book-title-d:   108px; /* 登录巨字「练功记录」· 桌面（SPEC 范围 96-120px） */
  --fs-book-title-m:   60px;  /* 登录巨字 · 移动（SPEC 范围 56-64px） */
  --fs-book-subtitle:  22px;  /* 注册页题签级标题「入册——新同修注册」 */

  /* ---- 行高 ---- */
  --lh-display: 1.1;   /* 巨字标题 */
  --lh-body:    1.75;  /* 正文（中文视觉密度高于西文 1.5 惯例） */
  --lh-tight:   1.3;   /* 字段表/票据内紧凑行高 */

  /* ---- 字距 ---- */
  --ls-wide:    0.18em;  /* 报头三段小字宽字距 */
  --ls-toc:     0.05em;  /* 目录条/纸签栏宽字距 */
  --ls-label:   0.08em;  /* 字段表标签/轴标宽字距 */
  --ls-display: -0.01em; /* 巨字标题（中文大字禁正向拉开，收紧近刻本观感） */

  /* ---- 线宽（乌丝栏/朱丝栏/虚线/文武线，SPEC §4.5 语汇） ---- */
  --lw-hair:  0.5px;  /* 乌丝内框/界格/时辰钟刻度环 */
  --lw-line:  1px;    /* 乌丝默认线/虚线分段/题签分隔线 */
  --lw-thick: 1.5px;  /* 票据卡外框（文武线粗线） */
  --lw-heavy: 2px;    /* 目录条当前项短朱线/巨字标题下划线 */

  /* ---- 圆角：全站硬顶 ≤4px，且仅限小控件（票据/巨字容器方角零圆角） ---- */
  --r-sm:  2px;  /* 墨块滑钮/输入格/月份小按钮 */
  --r-ctl: 3px;  /* 确认键/印章方章磨边 */

  /* ---- 动效：页面切换沿用既定单层透明度节奏，其余复用 primitive 层曲线 ---- */
  --t-page: 120ms;  /* 页面切换（SPEC §4.2「单层透明度 ≤140ms」，D13 定为 120ms） */

  /* ---- 阴影/遮罩：默认零阴影，仅浮层（录入票据/时辰钟明细浮层）使用暖褐调 ---- */
  --shadow-float: 0 6px 28px rgba(26, 20, 14, .16), 0 1px 2px rgba(26, 20, 14, .10);
  --scrim:        rgba(26, 26, 26, .35);
  /* V4.2（SPEC §3 R-014②）：登录/注册渐显浮层专用遮罩=墨色 20%（比 --scrim 更浅，
     立轴在虚焦中仍隐约可见），RGB 三元组拆出供 rgba() 引用（同 --paper-3-rgb 先例，
     token 文件本身是唯一允许裸色值的地方，业务层 app.css 只准 var()/rgba(var())）。 */
  --ink-rgb:      26, 26, 26;
  --scrim-book:   rgba(26, 26, 26, .2);
  /* V4.2（SPEC §2.2）：立轴 1280 两侧发丝竖线专用——乌丝色 30-40% 透明度 */
  --line-rgb:     201, 191, 168;

  /* ---- 宣纸纹理：构建期烘焙（feTurbulence 多层噪点合成 PNG，多重八度 + 横向轻拉伸
       还原纤维走向），data-URI 平铺，α 上限 10/255(≈3.9%)，满足「≤4% 强度」纪律，
       颜色恒定暖米（RGB 214,201,170），配 multiply 混合模式叠于 --paper 之上，
       不采用运行时 SVG filter（R7 §5.1 明确指出滚动/动画容器逐帧重算掉帧），
       静态背景一次性绘制，性能零风险 ---- */
  --texture-xuanzhi: url("data:image/png;base64,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");
}

/* ============================================================================
   app.css — 冥想统计看板 demo 全站唯一样式归属（X2 工卡 · V2「册页」全案）
   依赖：tokens.css 必须先于本文件加载（本文件只用 var(--*)，零裸色值）
   ⚠ 本文件为 V2 推倒重做整体替换件：V1 双主题（.shell/.nav/.tabbar/.kpi/.card/
   .rankrow/.wheel/.badge-shichen/.seal 等）契约作废，class 命名空间全新起造，
   V1 成品已存档 dist\冥想统计看板-demo-v1存档.html，不再兼容维护。

   世界观：这不是一个 App，是社区共用的一册修习手账——纸、墨、朱印、票据、放榜。
   五条裁决基准（SPEC V2 §1，冲突时以此为准）：
     1. 纸即界面：分区靠线与留白，禁底色块分区、禁投影卡片。
     2. 中文大字当主角：层级靠字号/字重/字体切换，不靠颜色/图形。
     3. 数据=票据/档案：等宽数字、虚线分段、字段表、批次页脚。
     4. 色彩四色封顶：--seal 只进印章底/色点，--red-text 只进「今日之数」类强调。
     5. 装饰只许来自印刷文书传统：细线/虚线/界格/鱼尾/刻度尺/印章/微旋转。
   本文件仅有的两处 radial-gradient（§14 宣纸云絮）是 SPEC §6 明令的纸纤维合成
   技法，属技术性纹理生成，非「渐变」类 UI 装饰效果，与禁止项 §7.1 不冲突——
   §7.1 针对的是按钮/卡片层面的色彩渐变、光泽、玻璃拟态。

   ============================================================================
   V2 全站 DOM 契约总纲（X3 组件层 / X4 页面层按此落地，class 名为唯一真理源）
   ----------------------------------------------------------------------------
   外壳（每页 <body> 下唯一根）：
     <div class="shell">
       <header class="masthead"> ... <div class="ruler" aria-hidden="true"></div></header>
       <nav class="toc">   <!-- 桌面 ≥768px 显示，移动隐藏 -->
         <a class="toc-item" aria-current="page">今日</a> ...
       </nav>
       <main class="page">
         <section class="folio"><h1 class="folio-title">今日</h1><p class="folio-note">…</p></section>
         ...页面内容，全部由 .tag-rule + .ticket 系组件拼装...
       </main>
       <nav class="tab-paper">  <!-- 移动 <768px 显示，桌面隐藏 -->
         <a class="tab-paper-item" aria-current="page">今日</a> ...
       </nav>
     </div>
     <div class="mask" data-state="open|closed"></div>  <!-- 录入面板遮罩，shell 外层同级 -->
     <div class="entry-ticket" data-state="open|closed" role="dialog">…</div>
     <!-- CARD-G3W11：原「全站唯一 seal-erosion 崩边滤镜」SVG 契约已随印章
          DOM 改造下架（旧 .seal-v2-face/.seal-v2-frame 手绘拼字方案专用，
          新版直接内联 owner 验收终版矢量 logo，不需要再做崩边失真），见
          .seal-v2 §14 印章一节新注释。 -->

   题签分隔（替代一切卡片标题，§4.4）：
     <div class="tag-rule">
       <span class="tag-rule-label">趋势</span>
       <span class="tag-rule-line" aria-hidden="true"></span>
       <span class="tag-rule-meta">近七日</span>
     </div>

   票据卡（唯一有边框的容器，§4.5）：
     <div class="ticket">
       <div class="ticket-body">…内容/字段表…</div>
       <div class="ticket-divider" aria-hidden="true"></div>  <!-- 可多个，虚线分段 -->
       <div class="fieldrow"><span class="fieldrow-label">本周</span><span class="fieldrow-value num">4 时 10 分</span></div>
       <div class="ticket-foot">录于 申时 · 第 026 笔</div>
     </div>
   轻量列表行变体（管理列表/记录卡片等重复项，不用双线重框）：
     <div class="ticket-row"> … 同 .ticket 内部语法，单线 1px 边框 … </div>

   其余组件契约见各自小节头部注释：.ticket-hero（§6）/.shichen-ledger（§7）/
   .gridcal（§8）/.ch-*（§9）/.bang（§10）/.entry-ticket .entry-mode-switch
   .numwheel .numfield .shichen-tag（§11）/.day-tickets（§12）/
   .empty（§13）/.seal-v2 .fishtail（§14）/.book-page .scroll .scroll-title
   .book-auth-overlay（§16，登录/注册立轴首屏+渐显浮层）/.dudie .timinglu
   .nianpu .me-settings（§17，我的页度牒·题名录·年谱三件套）/.colophon（§18，牌记）。
   目录：
     0. 基础重置与排印
     1. 报头 masthead + 刻度尺 ruler（V4：三段真居中网格，R-013⑧）
     2. 目录条 toc（桌面）/ 纸签栏 tab-paper（移动）/ 页面切换
     3. 巨字页标 folio
     4. 题签分隔 tag-rule
     5. 票据卡 ticket / ticket-row / fieldrow / ticket-divider / ticket-foot
     6. 核心数字横幅票据 ticket-hero
     7. 十二时辰账页 shichen-ledger（V4：周/月/年三范围切换，R-013⑥）
     8. 打卡历 gridcal「历注墨字格」（V4，R-013⑦第三版）
     9. 趋势「账簿墨条」ch-* 上色（V4，R-013⑤）
     10. 放榜 bang
     11. 录入面板 V3「细记/约记」：entry-ticket / entry-mode-switch / numwheel /
         numfield / shichen-tag
     12. 记录卡片 day-tickets
     13. 空态 empty / 通用按钮
     14. 纸纹背景 / 印章 seal-v2「压角朱文印」（V4，R-013②）/ 鱼尾 fishtail
     15. 响应式与 reduced-motion / 触控热区
     16. 登录/注册「立轴」首屏 + 渐显浮层 book-page（V4.2 §2/§3 R-014①② 推倒重做）
     17. 我的页「度牒 · 题名录 · 年谱」三件套（V4 整页重排，R-013③）
     18. 牌记 colophon（V3 新增，SPEC §13）
     19. CC 式滚动条 + .cc-scroll 静止隐/交互显（R-019③，W13a 新增；本节起
         同时落地 R-019④全局圆边卡片：.ticket/.ticket-row/.entry-ticket/
         .book-auth-card 圆角改动分散在各自原章节内，未新增独立小节，
         详见 §5/§11/§13/§16 各处 border-radius 增量注释）
   ========================================================================== */


/* ============================================================================
   0. 基础重置与排印
   ========================================================================== */
*, *::before, *::after { box-sizing: border-box; }
/* R-033①(CARD-G3W13，20260723 CDP 实测坐实)：切页(今日/排行榜/我的)时 masthead
   整条发生小幅位移，根因=各页内容高度不等，纵向滚动条随页面切换动态出现/
   消失(SPA hash 路由不重载整页，纯局部换 DOM)，document.documentElement.
   clientWidth 因此跟着抖动(实测 1280 视口 有/无滚动条差 10px，masthead 靠
   grid 居中，两侧各偏 5px)——不是 masthead 自身样式或某一页布局的缺陷，是
   "滚动条是否出现"这件事本身在跨页时不稳定。scrollbar-gutter:stable 让浏览
   器无论当前页内容是否溢出都恒定预留滚动条槽位，从根上抹平这个不稳定量，
   现代 Chrome/Firefox/Edge 均支持(不支持的旧浏览器退化为原生行为，即回到
   "抖动"而非报错，不影响功能)。 */
html { scrollbar-gutter: stable; }
html, body { height: 100%; }
body {
  margin: 0;
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
h1, h2, h3, h4, p, ul, ol, dl, dd { margin: 0; }
ul, ol { padding: 0; list-style: none; }
button, input, select, textarea { font: inherit; color: inherit; }
button {
  background: none; border: none; padding: 0; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center; /* 中文按钮禁 line-height 居中法 */
}
a { color: inherit; text-decoration: none; }
img, svg { display: block; max-width: 100%; }
::selection { background: var(--paper-3); color: var(--ink); }

/* 字重纪律：全站只用 400/600，中等强调一律靠颜色/字号，不靠 500（雅黑伪加粗） */
b, strong { font-weight: 600; }

/* 数字纪律：一切统计数字必须 MonoNum + tabular + 右对齐由各自容器决定，
   本工具类只负责字体与等宽特性 */
.num {
  font-family: var(--font-num);
  font-variant-numeric: tabular-nums;
  /* SPEC V3 §13：CJK 与数字/字母交界处留白（text-autospace 未获浏览器普遍实现，
     不能作为唯一方案——按 .num 包裹处统一加最小行内边距，近似达到「数字汉字
     间距」效果，零新增裸色值/裸时长） */
  margin-inline: 0.06em;
}

/* 焦点环：双层同心，用纸/墨色合成，禁裸色值 */
:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--paper), 0 0 0 4px var(--ink);
}


/* ============================================================================
   1. 报头 masthead + 刻度尺 ruler
   DOM 契约（V4 §9 三段真居中网格，叠加于 V3 §4 之上）：
   <header class="masthead">
     <div class="masthead-inner">
       <span class="masthead-brand">练功志<i class="masthead-brand-dot" aria-hidden="true"></i></span>
       <span class="masthead-slogan">── 争分夺秒 · 刻不容缓 ──</span>
       <span class="masthead-meta">
         <span class="masthead-meta-item">07·20</span>
         <span class="masthead-meta-item">戌时 <span class="num masthead-clock">19:42:07</span></span>
         <span class="masthead-meta-item masthead-member">沈知远</span>
         <button class="masthead-switch masthead-meta-item">退出登录</button>
       </span>
     </div>
     <div class="ruler" aria-hidden="true"></div>
   </header>
   ========================================================================== */
.masthead {
  padding: var(--s3) var(--s4) 0;
}
.masthead-inner {
  /* V4（SPEC §9 R-013⑧）：三段改真网格居中——两侧 1fr 等宽，口号列按内容
     宽度居中；此前 flex space-between 在右段 meta 缺席时会把口号推向右边，
     grid 两侧等宽轨道结构性杜绝这个问题（不依赖各段内容多寡）。 */
  display: grid;
  /* R-022 回归修:移动端两列(品牌|meta)——旧三列 1fr auto 1fr 迫使两侧轨道
     等宽,375 端 meta(日期+时辰钟+成员+退出登录 ≈368px)撑爆网格横向溢出 78px
     (R-019⑨ 解禁日期项时漏测的既有回归);桌面 ≥768 恢复三列真居中(见下方断点) */
  grid-template-columns: auto 1fr;
  align-items: center;
  gap: var(--s4);
  /* V3（SPEC §4）：整体加高一档，摆脱旧版「一条 11px 细线」的局促——
     min-height 撑出内容行高度，桌面断点见下方媒体查询 */
  min-height: 34px;
  padding-bottom: var(--s3);
  font-size: var(--fs-masthead);
  letter-spacing: var(--ls-wide);
  color: var(--ink-2);
}
.masthead-brand {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  justify-self: start;
  color: var(--ink);
  cursor: pointer; /* R-020⑥:品牌名可点回首屏 */
  /* V4（SPEC §9 R-013⑧）：品牌字改齐伋体 16px + 统一 letter-spacing 0.08em
     （改名「练功志」后无 glyph-blend 拆字 span，字距天然均匀，不必再借宽字距
     掩饰拼接缝）。 */
  font-family: var(--font-display);
  font-size: 16px;
  font-weight: 600;
  letter-spacing: 0.08em;
  flex-shrink: 0;
  /* G2 终审修(复核P0-①):375 端右段 meta 挤压 grid 1fr 轨道时,品牌中文默认
     逐字可断行,「练功志」被拆成三行破版——品牌字永不断行 */
  white-space: nowrap;
}
/* 全站唯一「色点」用途之一：--seal 仅限印章底色与色点，此处为品牌点睛 */
.masthead-brand-dot {
  width: 4px; height: 4px;
  border-radius: 50%;
  background: var(--seal);
  flex-shrink: 0;
}
.masthead-slogan {
  /* SPEC V3 §4：移动端隐藏中段口号，给右段新增的实时秒钟腾出宽度
     （375 宽实测：不隐藏会导致右段整体溢出视口触发横向滚动） */
  display: none;
  justify-self: center;
  text-align: center;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.masthead-meta {
  display: inline-flex;
  align-items: center;
  /* R-022 回归修:允许换行右对齐——375 端自然折成「日期干支 / 时辰钟·成员·
     退出登录」两行,全信息保留零溢出;桌面宽度充裕永不触发 */
  flex-wrap: wrap;
  justify-content: flex-end;
  row-gap: 2px;
  gap: 6px;
  justify-self: end;
}
/* G2 终审修(复核P0-①连带):meta 各项同样禁断行——375 端「沈知远」「切换身份」
   曾被逐字拆行;并在窄屏隐去首项日期(与今日页小字注重复,腾宽度) */
.masthead-meta-item { white-space: nowrap; }
/* R-019⑨:owner 令顶栏必显年月日+干支——旧「≤480px 整项隐藏」规则解除;
   窄屏防线=ui-pages 两级降级(≤480px 干支串降日柱)+R-022 的 meta 换行右对齐
   (当时「375 零溢出」断言实测有误,已由两列网格+换行根治) */
/* V3（SPEC §4）：段间改界行竖线（1px --line）分隔，替代旧版圆点堆砌；
   .masthead-switch 同时挂 .masthead-meta-item 以纳入同一分隔规则。
   移动端 gap/margin 收紧（375 宽实测：右段新增秒钟数字后按桌面间距会溢出
   视口触发横向滚动），桌面断点放宽回舒适间距。 */
.masthead-meta-item + .masthead-meta-item::before {
  content: "";
  display: inline-block;
  width: var(--lw-line);
  height: 12px;
  margin-right: 6px;
  background: var(--line);
  vertical-align: -2px;
}
.masthead-member { color: var(--ink-2); }
/* 时辰后接实时秒钟（MonoNum tabular，固定字宽防抖，§4） */
.masthead-clock { color: var(--ink-2); }
.masthead-switch {
  color: var(--ink-2);
  font-size: var(--fs-masthead);
  letter-spacing: var(--ls-wide);
  text-decoration: underline;
  text-decoration-style: dotted;
  text-underline-offset: 3px;
  transition: color var(--t-fast) var(--ease-out);
}
.masthead-switch:hover { color: var(--ink); }

/* 报头下压 1px 乌丝线 + 刻度尺装饰（静态细密刻线，R7 §5.3 语汇，硬边条纹非色彩渐变） */
.masthead::after {
  content: "";
  display: block;
  height: var(--lw-line);
  background: var(--line);
}
.ruler {
  height: 6px;
  background-image: repeating-linear-gradient(
    to right, var(--line) 0, var(--line) 1px, transparent 1px, transparent 6px
  );
  opacity: .8;
}
@media (min-width: 768px) {
  .masthead { padding: var(--s3) var(--s6) 0; }
  .masthead-inner {
    min-height: 46px; /* SPEC §4：桌面内容行 44-48px */
    grid-template-columns: 1fr auto 1fr; /* 桌面恢复三段真居中(口号列) */
  }
  .masthead-slogan { display: block; }
  .masthead-meta { gap: var(--s3); }
  .masthead-meta-item + .masthead-meta-item::before { margin-right: var(--s3); }
}


/* ============================================================================
   2. 目录条 toc（桌面水平） / 纸签栏 tab-paper（移动底部） / 页面切换
   DOM 契约：
   <nav class="toc">
     <a class="toc-item" aria-current="page">今日</a>
     <a class="toc-item">同修</a>
     <a class="toc-item">我的</a>
     <a class="toc-item">管理</a>
   </nav>
   <nav class="tab-paper">
     <a class="tab-paper-item" aria-current="page">今日</a> ...
   </nav>
   页面切换：.page-fade[data-state="enter|exit"] 由 X4 挂在 <main class="page"> 上，
   单层 opacity ≤140ms，不做位移/交叉淡入。
   ========================================================================== */
.toc {
  display: none;
}
.tab-paper {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 40;
  display: flex;
  background: var(--paper-2);
  border-top: var(--lw-line) solid var(--line);
  padding: 6px 0 max(6px, env(safe-area-inset-bottom));
}
.tab-paper-item {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  color: var(--ink-2);
  font-family: var(--font-body);
  font-size: var(--fs-toc);
  font-weight: 400;
  position: relative;
  transition: color var(--t-fast) var(--ease-out);
}
.tab-paper-item[aria-current="page"] {
  color: var(--ink);
  font-weight: 600;
}
/* 当前签：签顶短朱线（题签），无图标，纯楷字导航 */
.tab-paper-item[aria-current="page"]::before {
  content: "";
  position: absolute;
  top: 0; left: 50%;
  transform: translateX(-50%);
  width: 20px; height: var(--lw-heavy);
  background: var(--line-red);
}

@media (min-width: 768px) {
  .tab-paper { display: none; }
  .toc {
    display: flex;
    align-items: center;
    gap: var(--s6);
    padding: var(--s3) var(--s6);
    border-bottom: var(--lw-line) solid var(--line);
    background: var(--paper);
  }
  /* CARD-G3W11 附加项（G3W9 回执 §3 遗留裁决）：桌面顶部导航项命中区扩到
     ≥44×44——iPad 768 档命中本媒体查询分支但仍是触屏设备。配方同 G3W10
     （见上方 §13 .seg-item / 下方 §15 .masthead-brand 一族）：透明 padding+
     等量负 margin，margin-box 折算回原值不占用 .toc 的 gap（gap: var(--s6)=
     32px，本次单边只加 8px，两邻项相向合计吃 16px，剩 16px 净间隙，命中区
     不重叠）；.toc-item 自身零背景零边框，唯一可见装饰是当前项 ::after 底部
     2px 朱线，随 padding 增量用显式 inset 钉回原视觉位置（技巧同
     .seg-item::before 的 inset 补偿法）。 */
  .toc-item {
    position: relative;
    padding: 10px 8px 18px; /* 原仅 padding-bottom:8px；上/左右新增纯命中区，
      下方在原 8px 结构间距基础上再加 10px 命中区（8+10=18） */
    margin: -10px -8px -10px; /* 负 margin 只抵消「新增」的量：上 -10 对应
      padding-top 新增的 10；下 -10 对应 padding-bottom 新增的 10（不是原有
      的 8）；左右 -8 对应 padding-left/right 新增的 8 */
    font-family: var(--font-body);
    font-size: var(--fs-toc);
    letter-spacing: var(--ls-toc);
    color: var(--ink-2);
    font-weight: 400;
    transition: color var(--t-fast) var(--ease-out);
  }
  .toc-item:hover { color: var(--ink); }
  .toc-item[aria-current="page"] {
    color: var(--ink);
    font-weight: 600;
  }
  /* 当前项字底短朱线（题签），2px 朱丝；left/right:8px 抵消新增横向 padding，
     bottom:10px 抵消新增的 10px 纵向命中区（钉回原 8px 结构间距处的视觉基线，
     视觉位置与改前像素级一致） */
  .toc-item[aria-current="page"]::after {
    content: "";
    position: absolute;
    left: 8px; right: 8px; bottom: 10px;
    height: var(--lw-heavy);
    background: var(--line-red);
  }
}

.page {
  flex: 1;
  width: 100%;
  /* SPEC V3 §13「天头地脚」：页级上内边距为下内边距 1.5-2 倍，仅此一处调整
     （移动端下内边距被纸签栏让位撑大，比例天然合规；桌面端显式 1.5 倍）。 */
  padding: var(--s5) var(--s4) calc(64px + var(--s6)) var(--s4);
}
@media (min-width: 768px) {
  .page {
    max-width: 880px;
    margin: 0 auto;
    padding: calc(var(--s6) * 1.5) var(--s6) var(--s6);
  }
}
.shell {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

/* 页面切换：单层透明度，不做位移/交叉淡入（SPEC §4.2 沿用既定口径） */
.page-fade {
  transition: opacity var(--t-page) var(--ease-out);
}
.page-fade[data-state="exit"] { opacity: 0; }
.page-fade[data-state="enter"] { opacity: 1; }


/* ============================================================================
   3. 巨字页标 folio
   DOM 契约：
   <section class="folio">
     <h1 class="folio-title">今日</h1>
     <p class="folio-note">7月20日 · 星期一 · 戌时 19:42:07</p>
   </section>
   登录/注册页不用 .folio——改用 §16 的 .book-page/.scroll-title 独立语汇（开卷
   之前不算书内页，尺度也比站内页标更高一级）。
   ========================================================================== */
.folio {
  margin-bottom: var(--s6);
}
.folio-title {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--fs-folio-m);
  line-height: var(--lh-display);
  letter-spacing: var(--ls-display);
  color: var(--ink);
}
/* R-033②(CARD-G3W13，20260723 owner 令)：页首巨字下方日期时间行三页字号/间距
   不一致——「我的」/「管理」两页套 .page-scale-lg(R-028⑥ 正文+15%)，原样式靠
   --fs-folio-note 变量间接吃到这条覆写(12px→13.8px)，「今日」/「排行榜」两页
   没套 page-scale-lg，停在变量原值 12px，三页因此字号/行高不一致(CDP 实测
   12px/21px vs 13.8px/24.15px，与巨字页标间距 8px 本身四页一致，只有字号一项
   有差)。owner 令=今日/排行榜两页向「我的」页对齐——直接把该行字号定死为
   「我的」页的计算值，不再经 --fs-folio-note 变量间接吃 page-scale-lg 覆写
   (不依赖各页缩放开关的连带，见工单原话)，四页(含管理页)逐像素一致；
   line-height 走 body 继承的 --lh-body(1.75)不必单列，13.8×1.75=24.15px 与
   实测值吻合。旧 --fs-folio-note 变量与 page-scale-lg 覆写点已随之不再被
   .folio-note 消费(见下方 §22 覆写清单同步移除该行，避免留一条不再生效的
   死配置)。 */
.folio-note {
  margin-top: var(--s2);
  font-family: var(--font-body);
  font-size: 13.8px;
  color: var(--ink-3);
}
@media (min-width: 768px) {
  .folio-title { font-size: var(--fs-folio-d); }
}


/* ============================================================================
   4. 题签分隔 tag-rule（替代一切卡片标题）
   ========================================================================== */
.tag-rule {
  display: flex;
  align-items: baseline;
  gap: var(--s3);
  margin: var(--s6) 0 var(--s3);
}
.tag-rule-label {
  flex-shrink: 0;
  font-family: var(--font-body);
  font-size: var(--fs-tagrule-label);
  letter-spacing: var(--ls-label);
  font-weight: 600;
  color: var(--ink);
}
.tag-rule-line {
  flex: 1;
  height: var(--lw-line);
  background: var(--line);
  align-self: center;
}
.tag-rule-meta {
  flex-shrink: 0;
  font-family: var(--font-num);
  font-size: var(--fs-tagrule-meta);
  color: var(--ink-3);
}


/* ============================================================================
   5. 票据卡 ticket / ticket-row（轻量单线变体）/ fieldrow / 虚线分段 / 页脚
   ========================================================================== */
.ticket {
  position: relative;
  background: var(--paper-2);
  border: var(--lw-thick) solid var(--line);
  border-radius: var(--r-card); /* R-019④owner令:全局圆边卡片,覆盖旧「禁大圆角」纪律(W13a) */
  padding: var(--s5);
}
/* 内 0.5px 细线，距外框 4px，构成「文武线」双线框语法；内线圆角随外框收 2px 同步跟随 */
.ticket::before {
  content: "";
  position: absolute;
  inset: 4px;
  border: var(--lw-hair) solid var(--line);
  border-radius: calc(var(--r-card) - 2px);
  pointer-events: none;
}
.ticket-body { position: relative; }
.ticket-divider {
  border-top: var(--lw-line) dashed var(--line);
  margin: var(--s4) 0;
}
.ticket-foot {
  margin-top: var(--s4);
  padding-top: var(--s3);
  border-top: var(--lw-line) dashed var(--line);
  font-family: var(--font-num);
  font-size: var(--fs-ticketfoot);
  color: var(--ink-3);
  text-align: right;
}

/* 轻量列表行：管理列表/邀请码/记录卡片等重复项，单线不重框，避免密集列表过重 */
.ticket-row {
  background: var(--paper-2);
  border: var(--lw-line) solid var(--line);
  border-radius: var(--r-card); /* R-019④(W13a) */
  padding: var(--s3) var(--s4);
}
.ticket-row + .ticket-row { margin-top: var(--s2); }

.fieldrow {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s3);
  padding: 6px 0;
  line-height: var(--lh-tight);
}
.fieldrow + .fieldrow { border-top: var(--lw-hair) dashed var(--line); }
.fieldrow-label {
  font-family: var(--font-body);
  font-size: var(--fs-fieldlabel);
  letter-spacing: var(--ls-label);
  color: var(--ink-3);
  flex-shrink: 0;
}
.fieldrow-value {
  font-family: var(--font-num);
  font-size: var(--fs-fieldvalue);
  font-variant-numeric: tabular-nums;
  color: var(--ink);
  text-align: right;
}
/* 涨跌双编码：箭头+数值，色盲冗余（沿用 playbook 红线），色彩只用深朱/淡墨，不引第二语义色 */
.fieldrow-value[data-trend="up"] { color: var(--red-text); }
.fieldrow-value[data-trend="down"] { color: var(--ink-2); }
.fieldrow-value[data-trend="up"]::before { content: "↑ "; }
.fieldrow-value[data-trend="down"]::before { content: "↓ "; }


/* ============================================================================
   6. 核心数字横幅票据 ticket-hero（替代 KPI 卡阵，SPEC §5.1）
   DOM 契约：
   <div class="ticket ticket-hero">
     <div class="ticket-hero-main">
       <span class="ticket-hero-num num">128</span>
       <span class="ticket-hero-unit">分钟</span>
       <p class="ticket-hero-caption">今日</p>
     </div>
     <div class="ticket-hero-fields">
       <div class="fieldrow"><span class="fieldrow-label">本周</span><span class="fieldrow-value num" data-trend="up">4 时 10 分</span></div>
       <div class="fieldrow"><span class="fieldrow-label">连续</span><span class="fieldrow-value num">5 天</span></div>
       <div class="fieldrow"><span class="fieldrow-label">本周日均</span><span class="fieldrow-value num">36 分</span></div>
       <div class="fieldrow"><span class="fieldrow-label">本月</span><span class="fieldrow-value num" data-trend="down">18 时 20 分</span></div>
     </div>
   </div>
   ========================================================================== */
.ticket-hero {
  display: flex;
  flex-direction: column;
  gap: var(--s4);
}
.ticket-hero-numrow {
  display: flex;
  align-items: baseline;
  gap: var(--s2);
}
.ticket-hero-main {
  display: flex;
  align-items: baseline;
  gap: var(--s2);
  flex-wrap: wrap;
}
.ticket-hero-num {
  font-size: var(--fs-hero-num-m);
  font-weight: 600;
  line-height: 1;
  color: var(--red-text); /* 全站「今日之数」唯一朱色文字用途 */
}
.ticket-hero-unit {
  font-family: var(--font-body);
  font-size: var(--fs-hero-unit);
  color: var(--ink-2);
}
.ticket-hero-caption {
  width: 100%;
  font-family: var(--font-body);
  font-size: var(--fs-caption);
  color: var(--ink-3);
  order: -1;
}
.ticket-hero-fields {
  border-top: var(--lw-line) dashed var(--line);
  padding-top: var(--s2);
}
@media (min-width: 768px) {
  .ticket-hero {
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
    gap: var(--s6);
  }
  .ticket-hero-num { font-size: var(--fs-hero-num-d); }
  .ticket-hero-main { flex-direction: column; align-items: flex-start; gap: 0; }
  .ticket-hero-fields {
    border-top: none;
    border-left: var(--lw-line) dashed var(--line);
    padding-top: 0;
    padding-left: var(--s6);
    min-width: 260px;
  }
}


/* ============================================================================
   7. 十二时辰账页 shichen-ledger（SPEC V4 §6 R-013⑥，周/月/年三范围切换；
   账页本体沿用 V3 §9「替代 V2 单环日晷式 sundial」，sundial-* class 全套
   仍作废，未再被任何组件引用）
   DOM 契约（纯 flex/宽度百分比，零 SVG、零极坐标，文字零旋转）：
   <div class="shichen-ledger-wrap">
     <div class="seg shichen-ledger-seg">
       <button class="seg-item" aria-selected="true">周</button>
       <button class="seg-item" aria-selected="false">月</button>
       <button class="seg-item" aria-selected="false">年</button>
     </div>
     <div class="shichen-ledger">
       <div class="ledger-row" data-now="true|false" data-empty="false">
         <div class="ledger-dizhi">
           <span class="ledger-dizhi-name">戌时</span>
           <span class="ledger-dizhi-range num">19-21</span>
         </div>
         <span class="ledger-sep" aria-hidden="true"></span>
         <div class="ledger-bar-track"><span class="ledger-bar" style="width:64%"></span></div>
         <span class="ledger-value num">1 时 30 分 (2 次)</span>
       </div>
       ...固定 12 行，子→亥时序...
       <p class="ledger-footer">共录 18 笔 · 最常起于申时</p>
     </div>
   </div>
   ========================================================================== */
.shichen-ledger-wrap {
  display: flex;
  flex-direction: column;
}
.shichen-ledger-seg {
  align-self: flex-start;
  margin-bottom: var(--s4);
}
.shichen-ledger {
  display: flex;
  flex-direction: column;
}
.ledger-row {
  display: flex;
  align-items: center;
  gap: var(--s3);
  padding: var(--s2) 0;
  border-bottom: var(--lw-hair) dashed var(--line);
  cursor: pointer;
}
.ledger-row:last-of-type { border-bottom: none; }
/* 当前时辰行：衬纸底 + 上下朱丝细线 + 地支/数字转深朱（复用放榜本人行语法） */
.ledger-row[data-now="true"] {
  background: var(--paper-3);
  margin: 0 calc(var(--s3) * -1);
  padding: var(--s2) var(--s3);
  border-top: var(--lw-line) solid var(--line-red);
  border-bottom: var(--lw-line) solid var(--line-red);
}
.ledger-row[data-now="true"] .ledger-dizhi-name,
.ledger-row[data-now="true"] .ledger-value {
  color: var(--red-text);
  font-weight: 600;
}
.ledger-dizhi {
  flex-shrink: 0;
  width: 46px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  line-height: 1.3;
}
.ledger-dizhi-name {
  font-family: var(--font-body);
  font-size: 18px;
  color: var(--ink);
}
.ledger-dizhi-range {
  font-size: 11px;
  color: var(--ink-3);
}
.ledger-sep {
  align-self: stretch;
  width: var(--lw-line);
  background: var(--line);
  flex-shrink: 0;
}
.ledger-bar-track {
  flex: 1;
  min-width: 0;
  height: 8px;
  display: flex;
  align-items: flex-end;
  border-bottom: var(--lw-hair) dashed var(--line);
}
.ledger-bar {
  display: block;
  height: 6px;
  background: var(--chart-1);
}
.ledger-value {
  flex-shrink: 0;
  min-width: 7.5em;
  text-align: right;
  font-variant-numeric: tabular-nums;
  color: var(--ink-2);
}
.ledger-footer {
  margin-top: var(--s3);
  padding-top: var(--s3);
  border-top: var(--lw-hair) dashed var(--line);
  font-family: var(--font-body);
  font-size: var(--fs-caption);
  color: var(--ink-3);
}


/* ============================================================================
   8. 打卡历 gridcal「历注墨字格」（SPEC V4 §4，R-013⑦第三版，替代 V3「墨位
   量具」——彻底废除面积编码，格内只剩字/点/印三类离散符号，物理上不存在
   能连成片的色块）
   DOM 契约：
   <section class="gridcal">
     <div class="tag-rule">
       <span class="tag-rule-label">打卡历</span>
       <span class="tag-rule-line" aria-hidden="true"></span>
       <span class="gridcal-months">
         <button class="gridcal-month" aria-pressed="false">五月</button>
         <button class="gridcal-month" aria-pressed="true">六月</button>
         <button class="gridcal-month" aria-pressed="false">七月</button>
       </span>
     </div>
     <div class="gridcal-grid">
       <div class="gridcal-cell" data-today="false" data-future="false"><div class="gridcal-top"><span class="gridcal-daynum num">1</span></div></div>
       <div class="gridcal-cell" data-today="true" data-future="false">
         <div class="gridcal-top">
           <span class="gridcal-daynum num">2</span>
           <span class="gridcal-shichen" aria-hidden="true">申</span>  <!-- 当日主导时辰地支单字，全模糊录入日留空 -->
         </div>
         <span class="gridcal-dur num" data-tier="low|mid|high">3.6</span>  <!-- 当日时长(时)，一位小数，≥10 取整 -->
         <div class="gridcal-baseline"><span class="gridcal-dot" aria-hidden="true"></span></div>  <!-- 有记录=基线+实心墨点 -->
         <span class="gridcal-backfill-mark" aria-hidden="true"></span>  <!-- 补录日：墨点改为右下角空心方印，与 .gridcal-dot 互斥 -->
       </div>
       <div class="gridcal-cell" data-today="false" data-future="true"><div class="gridcal-top"><span class="gridcal-daynum num">31</span></div></div>  <!-- 未来日：仅界格+日期数字淡墨，不可点击，SPEC V4.2 §4.1 -->
       ...
     </div>
   </section>
   月历为完整自然月（1 日至月末，Engine.personalStats.heatmap[].days 逐日不
   再在「今天」处截断，SPEC V4.2 §4.1）；未来日（day.future===true）挂
   data-future="true"，仅界格+日期数字淡墨态，无墨点/时长/时辰注，不可点击。
   data-tier 语义（锚点=本人日均线 Engine.avgDailyMin，非当月最大）：
   low=淡墨（&lt;0.75×日均）/ mid=重墨（0.75-1.25×）/ high=浓墨（&gt;1.25×）；
   文字着色不是背景填色，永无连片。空日（当日无记录，含未来日）=仅界格+
   日期数字，不渲染 .gridcal-shichen/.gridcal-dur/.gridcal-baseline/.gridcal-backfill-mark。
   .gridcal-shichen 为装饰性单字，豁免正文最小字号。
   ========================================================================== */
/* 打卡历题签行：右端 meta 从 V4.2 起改今日干支全串（10 字）+「· 本月补录
   N%」，比旧版「本月补录 N%」长得多，窄屏（375）单行放不下会把月份切换按钮
   挤到换行、meta 本身溢出视口——允许本题签行整体换行，月份+干支小字整块
   落到第二行，避免横向溢出（SPEC V4.2 §4.3，只影响本题签行，不动全局
   .tag-rule 单行约定） */
.gridcal .tag-rule {
  flex-wrap: wrap;
  row-gap: var(--s2);
}
/* (已退役)旧多月按钮组 .gridcal-months/.gridcal-month——R-019⑧ 改组件内置
   箭头翻月后 DOM 已不再生成,规则保留仅为历史对照。 */
.gridcal-months {
  display: inline-flex;
  gap: var(--s2);
  flex-wrap: wrap;
}
/* R-019⑧ 组件内置翻月导航(W13b DOM 契约:.gridcal-nav > .cal-nav-prev +
   .gridcal-nav-label + .cal-nav-next):居中一行,箭头 44px 热区,边界禁用=淡墨 */
.gridcal-nav {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--s3);
  margin-bottom: var(--s3);
}
.cal-nav-prev,
.cal-nav-next {
  /* R-020④:owner 打回「按键太小」——热区补满 44px 见方,箭头字号 17→26,
     淡墨提为重墨,加细边框圈出可点感 */
  width: 44px;
  height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: var(--lw-hair) solid var(--line);
  border-radius: var(--r-ctl);
  color: var(--ink-2);
  font-size: 26px;
  line-height: 1;
  cursor: pointer;
  transition: background var(--t-fast) var(--ease-out), color var(--t-fast) var(--ease-out);
}
.cal-nav-prev:hover:not(:disabled),
.cal-nav-next:hover:not(:disabled) { background: var(--paper-3); color: var(--ink); }
.cal-nav-prev:disabled,
.cal-nav-next:disabled { color: var(--line); border-color: transparent; cursor: default; }
.gridcal-nav-label {
  font-family: var(--font-body);
  font-size: 15px;
  font-weight: 600;
  letter-spacing: .08em;
  color: var(--ink);
  min-width: 7.5em;
  text-align: center;
}
.gridcal-month {
  font-family: var(--font-body);
  font-size: var(--fs-tagrule-meta);
  color: var(--ink-3);
  padding: 2px 4px;
  border-radius: var(--r-sm);
  transition: color var(--t-fast) var(--ease-out), background var(--t-fast) var(--ease-out);
}
.gridcal-month[aria-pressed="true"] {
  color: var(--ink);
  font-weight: 600;
  background: var(--paper-3);
}
.gridcal-month:hover { color: var(--ink); }

.gridcal-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  border-top: var(--lw-hair) solid var(--line);
  border-left: var(--lw-hair) solid var(--line);
}
.gridcal-cell {
  position: relative;
  aspect-ratio: 1;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  border-right: var(--lw-hair) solid var(--line);
  border-bottom: var(--lw-hair) solid var(--line);
  padding: 3px 4px;
}
.gridcal-top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 2px;
}
.gridcal-daynum {
  /* SPEC V4.2 §4.2 日期字号加档：原 10px 级读不清，375→12-13px/1280→16-18px，
     层级须保持 日期 > 时长(.gridcal-dur) > 小注(.gridcal-shichen)，见下方两组 */
  font-size: 13px;
  color: var(--ink-3);
}
@media (min-width: 768px) {
  .gridcal-daynum { font-size: 17px; }
}
/* 未来日：仅界格+日期数字淡墨态，不可点击（SPEC V4.2 §4.1），日期数字本身
   叠加半透明再淡一档，与过去/今日的空日区分开 */
.gridcal-cell[data-future="true"] {
  cursor: default;
}
.gridcal-cell[data-future="true"] .gridcal-daynum {
  color: var(--ink-3);
  opacity: .55;
}
/* 顶行右侧随行时辰小注：紧跟日期，不再独占象限（SPEC V4 §4） */
.gridcal-shichen {
  font-family: var(--font-display);
  font-size: 8px;
  line-height: 1.2;
  color: var(--ink-2);
}
@media (min-width: 768px) {
  .gridcal-shichen { font-size: 10px; }
}
/* 中段主角：当日时长数字，三档文字色锚点=本人日均线（组件内计算） */
.gridcal-dur {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
  font-weight: 600;
}
@media (min-width: 768px) {
  .gridcal-dur { font-size: 16px; }
}
.gridcal-dur[data-tier="low"] { color: var(--ink-3); }
.gridcal-dur[data-tier="mid"] { color: var(--ink-2); }
.gridcal-dur[data-tier="high"] { color: var(--ink); }
/* 底部：极细乌丝基线，线上一枚实心墨点=当日有记录 */
.gridcal-baseline {
  position: relative;
  height: 6px;
  border-top: var(--lw-hair) solid var(--line);
  display: flex;
  justify-content: center;
}
.gridcal-dot {
  position: relative;
  top: -1.5px;
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: var(--ink);
}
/* 补录日：墨点改为右下角空心方印（细线方框，「事后钤记」意象），与
   .gridcal-dot 互斥——补录日的 .gridcal-baseline 不放 .gridcal-dot */
.gridcal-backfill-mark {
  position: absolute;
  right: 3px;
  bottom: 3px;
  width: 6px;
  height: 6px;
  border: var(--lw-hair) solid var(--ink-3);
}
/* 今日格：朱丝细框，用 box-shadow inset 避免干扰界格边框计算 */
.gridcal-cell[data-today="true"] {
  box-shadow: inset 0 0 0 var(--lw-line) var(--line-red);
}


/* ============================================================================
   9. 趋势「账簿墨条」ch-* 上色（SPEC V4 §5，R-013⑤）
   约定（沿用既有惯例，X3 产出 SVG 必须按此挂 class，禁 inline hex）：
   - 面状元素（墨条矩形/时辰盘扇区/热力格）：挂色阶类 .ch-s1..s5（fill 生效）。
   - 网格线/坐标轴：.ch-grid（网格）/ .ch-axis（轴线+刻度文字）。
   - 中文轴标（周内分布"一二三四五六日"/时辰标签）：额外挂 .ch-axis-cn 切正文字体。
   系列色阶惯例：本人/主系列=ch-s1（最深），社区/对比系列=ch-s3，第三系列=ch-s2。
   .ch-line / .ch-area / .ch-halftone-line / #ch-halftone-pattern（折线+圆点+
   斜线半调纹理面积）随 V4 账簿墨条改造整体下架——墨条本身即量感，不再有
   面积填充；本文件同步删除，避免死代码常驻（R-012 ㉕「代码简单」）。
   ========================================================================== */
.ch-grid { stroke: var(--line); fill: none; stroke-width: var(--lw-hair); opacity: .7; }
.ch-axis { stroke: none; fill: var(--ink-3); font-family: var(--font-num); font-size: 11px; font-variant-numeric: tabular-nums; }
.ch-axis-cn { font-family: var(--font-body); fill: var(--ink-2); }
.ch-s1 { fill: var(--chart-1); stroke: var(--chart-1); }
.ch-s2 { fill: var(--chart-2); stroke: var(--chart-2); }
.ch-s3 { fill: var(--chart-3); stroke: var(--chart-3); }
.ch-s4 { fill: var(--chart-4); stroke: var(--chart-4); }
.ch-s5 { fill: var(--chart-5); stroke: var(--chart-5); }
/* 远山淡墨(R-015):脊线=数据本体,全图最深最锐;回声远山=最淡墨装饰层,
   透明度显著弱于主山,一眼分得清哪层是数据哪层是意境 */
.ch-ridge { fill: none; stroke: var(--chart-1); stroke-width: 1.6; stroke-linecap: round; }
/* .ch-echo(回声远山)已随 R-019① 删除回声层而下架,零调用方,规则移除 */
/* .ch-accent（社区日均对比线专用色，SPEC V3 §7 R-012 ⑫已删除该对比线）已随之下架，
   零调用方——ch-s1..s5 是通用系列色阶（仍供 lineChart 多系列/heatmap 场景复用），保留 */

.chart-box { width: 100%; }
.chart-box svg { width: 100%; height: auto; display: block; }


/* ============================================================================
   10. 放榜 bang（SPEC §5.5 + V4.2 §6 R-014⑥ 数字定宽对齐增量）
   DOM 契约：
   <section class="bang">
     <div class="tag-rule"><span class="tag-rule-label">放榜</span><span class="tag-rule-line"></span><span class="tag-rule-meta">本周</span></div>
     <div class="seg bang-scope"> …周/月/年 子榜切换，复用既有 .seg 语法… </div>
     <div class="ticket bang-board">
       <div class="bang-row" data-top="1">
         <span class="bang-rank num">1</span><span class="bang-name">阿雅</span>
         <span class="bang-value num bang-value--duration"><span class="bang-value-n">12</span><span class="bang-value-u">时</span><span class="bang-value-n">30</span><span class="bang-value-u">分</span></span>
       </div>
       <div class="bang-row bang-row--me">
         <span class="bang-dot" aria-hidden="true"></span>
         <span class="bang-rank num">8</span><span class="bang-name">我</span>
         <span class="bang-value num bang-value--duration"><span class="bang-value-n">4</span><span class="bang-value-u">时</span><span class="bang-value-n">10</span><span class="bang-value-u">分</span></span>
       </div>
       <div class="bang-row">
         <span class="bang-rank num">—</span><span class="bang-name">陈墨</span>
         <span class="bang-value num bang-value--duration"><span class="bang-value-n">2</span><span class="bang-value-u">时</span><span class="bang-value-n">00</span><span class="bang-value-u">分</span></span>
       </div>
     </div>
     <p class="bang-footer">本周新上路：阿雅 · 阿雅</p>
   </section>
   全员必须可见，SPEC V3 §10.2（R-012 ㉑）删除匿名占位（原「一位低调的伙伴」/
   .bang-name--anon）逻辑与榜单可见性开关，本节不再有该变体。

   V4.2 数字定宽对齐（SPEC §6 R-014⑥，仅本节榜单专用格式，站内其余时长展示
   仍走 Engine.fmt.duration 自然宽不动）：时长列格式统一「H 时 MM 分」（分钟
   补零两位、整时显「00 分」），拆 .bang-value-n（数字，右对齐定宽）/
   .bang-value-u（单位字，上下成列）两类 span；进步幅度/连续天数等非时长值
   用 .bang-value--fixed 单层右对齐定宽，同理对齐。名次列 .bang-rank 固定
   2ch 右对齐（与 .num 等宽数字字体配合，天然按列对齐，无需 JS 测宽）。
   ========================================================================== */
.bang-board { padding: var(--s2) var(--s5); }
.bang-row {
  display: flex;
  align-items: center;
  gap: var(--s3);
  padding: var(--s3) 0;
  border-bottom: var(--lw-hair) solid var(--line);
}
.bang-row:last-child { border-bottom: none; }
.bang-rank {
  flex-shrink: 0;
  width: 2ch;
  text-align: right;
  color: var(--ink-2);
  font-weight: 600;
}
.bang-name {
  flex: 1;
  min-width: 4.5em;
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.bang-value {
  flex-shrink: 0;
  color: var(--ink);
  font-weight: 600;
}
/* 时长值：数字/单位各自成列。首个数字（时数）默认 2ch，百时以上由 JS 挂
   data-overhundred 扩到 3ch；分钟数字固定 2ch（已补零两位，恒 00-59）。 */
.bang-value-n {
  display: inline-block;
  min-width: 2ch;
  text-align: right;
}
.bang-value--duration[data-overhundred="1"] .bang-value-n:first-child {
  min-width: 3ch;
}
.bang-value-u {
  display: inline-block;
  min-width: 1.1em;
  text-align: left;
  color: var(--ink-2);
  font-weight: 400;
}
/* 进步幅度「+15%」/连续天数「45 天」：单层定宽右对齐（含符号最坏情形
   「+100%」5 字符，留够位不挤不抖）。 */
.bang-value--fixed {
  display: inline-block;
  min-width: 5ch;
  text-align: right;
}
/* 榜首：名次与姓名深朱（唯一「正文用深朱」的强调场景，SPEC 明示） */
.bang-row[data-top="1"] .bang-rank,
.bang-row[data-top="1"] .bang-name {
  color: var(--red-text);
}
/* 本人行：衬纸底 + 上下朱丝细线 + 行首小朱点（色点用途） */
.bang-row--me {
  background: var(--paper-3);
  margin: 0 calc(var(--s5) * -1);
  padding: var(--s3) var(--s5);
  border-top: var(--lw-line) solid var(--line-red);
  border-bottom: var(--lw-line) solid var(--line-red);
}
.bang-dot {
  width: 5px; height: 5px;
  border-radius: 50%;
  background: var(--seal);
  flex-shrink: 0;
}
.bang-footer {
  margin-top: var(--s3);
  padding-top: var(--s3);
  /* R-041(owner 20260723二次截图令,T5,截图6红框)："榜单卡内、'管理员账号不
     参与成员榜单'提示行上方的一道虚线,删"——border-top 已删除，padding-top
     保留只当纯间距用。.bang-footer 是单一组件位置在榜单卡尾部，三种文案
     （管理员不参与提示/本周期无记录提示/本周新上路名单）复用同一个 class、
     同一个视觉位置，非三张不同卡片，故一并去除，不做按角色/文案的按条件
     扣留（回执"一致性观察"列出，供 owner 复核）。 */
  font-family: var(--font-body);
  font-size: var(--fs-caption);
  color: var(--ink-3);
}


/* ============================================================================
   11. 录入面板「记一笔 · 时段记录/模糊录入」：entry-ticket / entry-mode-switch /
   entry-mode-body(.entry-mode-layer ×2) / numwheel / numfield / shichen-tag
   （V3 SPEC §6 推倒重做，R-011 引擎口径：单笔上限 18 时 59 分=1139 分钟；
   V4 SPEC §10 R-013⑨措辞表：细记→时段记录、约记→模糊录入、签条下小字
   「择一而记」删除、「坐了多久」→「大概练了」、「此笔不计入时辰分布」提示
   行删除——.entry-mode-hint/.entry-approx-hint 两个 class 随对应元素一并
   下架；V4.2 SPEC §5 R-014④：.entry-mode-body 改 grid 双层常驻叠放，两模式
   内容层同格重合渲染，非活动层 .entry-mode-layer[data-active="false"] 走
   visibility:hidden 仍占位撑高度，切换零跳动只剩 opacity 渐换，旧版
   data-state="switching" 整体清空重建机制随之下架）
   DOM 契约：
   <div class="mask" data-state="open|closed"></div>
   <div class="entry-ticket ticket" data-state="open|closed" role="dialog" aria-modal="true">
     <div class="entry-ticket-head">
       <h2 class="entry-ticket-title">记一笔 · 七月二十</h2>
       <span class="entry-ticket-now shichen-tag">
         <span class="shichen-tag-zi">戌</span><span class="shichen-tag-suffix">时</span>
       </span>
     </div>
     <div class="ticket-divider"></div>
     <div class="entry-mode-switch">
       <button class="entry-mode-tab" aria-selected="true">时段记录</button>
       <span class="entry-mode-sep" aria-hidden="true"></span>
       <button class="entry-mode-tab" aria-selected="false">模糊录入</button>
     </div>
     <div class="entry-mode-body"><!-- SPEC V4.2 §5：grid 双层常驻叠放，同格重合 -->
       <div class="entry-mode-layer" data-mode="precise" data-active="true" aria-hidden="false">
         <!-- 时段记录：起/止两组字段 + 止行下派生只读夹注 -->
         <div class="entry-field-group">
           <p class="entry-field-label">起</p>
           … 双滚轮/输入格（时 0-23，分 0-59）…
         </div>
         <div class="ticket-divider"></div>
         <div class="entry-field-group">
           <p class="entry-field-label">止</p>
           … 同款控件 …
           <p class="entry-derived">→ 3 时 30 分 · 起于戌时</p>
         </div>
       </div>
       <!-- 模糊录入层：与上面同格叠放常驻，非活动态 data-active="false"
            （visibility:hidden，仍占位撑高度）；单行「大概练了」-->
       <div class="entry-mode-layer" data-mode="approx" data-active="false" aria-hidden="true">
         <div class="entry-field-group">
           <p class="entry-field-label">大概练了</p>
           … 双滚轮/输入格（时 0-18，分 0-59）…
         </div>
       </div>
     </div>
     <p class="entry-error" data-visible="true">至少一分钟</p>
     <div class="ticket-divider"></div>
     <button class="btn entry-confirm" disabled>记下</button>
     <!-- 面板内「当日笔录」：复用 Components.dayTickets 填充，见 §12 -->
   </div>
   ========================================================================== */
.mask {
  position: fixed;
  inset: 0;
  z-index: 50;
  background: var(--scrim);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--t-modal) var(--ease-out);
}
.mask[data-state="open"] { opacity: 1; pointer-events: auto; }
/* R-019④「记一笔背景也要虚化」新契约类(W13a 新增,W13b 挂到记一笔的 .mask
   div 上，即 class="mask mask--blur")：.mask 全站共用(登录浮层遮罩另有专属
   .book-auth-mask 已带虚化，见 §16)，不直接改 .mask 基类以免波及未来其他
   用途，改用独立修饰类，强度与 .book-auth-mask 同款 blur(5px) */
.mask--blur {
  backdrop-filter: blur(5px);
  -webkit-backdrop-filter: blur(5px);
}

/* R-016③：移动端此前是从屏底升起的抽屉（translateY(100%) → 0），与桌面居中
   浮层是两套构图；owner 打回=录入要居中，移动/桌面同构——底部抽屉整套下架，
   改为全断点统一的居中浮层（位移+缩放+透明度进场，原桌面断点的进场手感直接
   收作全局默认，不再区分断点）。宽度 min(460px, 视口减两侧 --s5 留白)，最窄
   375 端得 375-48=327px，两侧各留 24px 呼吸；768px+ 走满 460px 定宽。 */
.entry-ticket {
  position: fixed;
  z-index: 51;
  left: 50%; top: 50%;
  width: min(460px, calc(100vw - var(--s5) * 2));
  max-height: 85vh;
  overflow-y: auto;
  padding: var(--s5) var(--s4) var(--s6);
  pointer-events: none;
  box-shadow: var(--shadow-float);
  border-radius: var(--r-pop); /* R-019④浮层级用 pop 令牌(大于卡片 r-card)(W13a) */
  transform: translate(-50%, -48%) scale(.98);
  opacity: 0;
  transition: transform var(--t-modal) var(--ease-out), opacity var(--t-modal) var(--ease-out);
}
.entry-ticket[data-state="open"] {
  pointer-events: auto;
  transform: translate(-50%, -50%) scale(1);
  opacity: 1;
}
/* .entry-ticket 同时挂 .ticket class(见 §5 .ticket::before 内线),浮层用
   r-pop 半径大于普通卡片 r-card，内线圆角需跟随重算(2 类复合选择器保证
   特异性稳赢 .ticket::before，不依赖源码顺序) */
.entry-ticket.ticket::before {
  border-radius: calc(var(--r-pop) - 2px);
}
.entry-ticket-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s3);
  margin-bottom: var(--s2);
}
.entry-ticket-title {
  font-family: var(--font-body);
  font-weight: 600;
  font-size: var(--fs-sub);
  color: var(--ink);
}
/* R-023:标题干支段换行——「{年柱}年 {月柱}月 {日柱}日」独立副行居首行下方,
   常规字重淡一档,与首行拉开主辅层级 */
.entry-ticket-title-ganzhi {
  display: block;
  margin-top: 2px;
  font-weight: 400;
  font-size: var(--fs-body-sm);
  color: var(--ink-2);
  letter-spacing: var(--ls-wide);
}
.entry-field-group { margin: var(--s4) 0; }
/* R-020②:细记 起/止 左右并排一行(止在右);窄屏(移动端双滚轮 4 列摆不开)
   经 flex-wrap 自动回退上下堆叠,不另设断点 */
.entry-precise-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0 var(--s5);
}
.entry-precise-row .entry-field-group {
  /* 基宽 150px:sheet 内容宽(桌面~420/移动~340)均可容双列一行;仅极窄端
     (<320px 级)才触发 wrap 回退堆叠 */
  flex: 1 1 150px;
  min-width: 0;
}
/* R-021②:模糊录入层整组居中——「大概练了」标签与 时:分 输入组(inline-flex,
   吃 text-align)同轴;细记层不受影响 */
.entry-mode-layer[data-mode="approx"] .entry-field-group {
  text-align: center;
}
.entry-field-label {
  font-family: var(--font-body);
  font-size: var(--fs-fieldlabel);
  letter-spacing: var(--ls-label);
  color: var(--ink-3);
  margin-bottom: var(--s3);
}
/* ---- 双签条：时段记录 / 模糊录入（SPEC §6.1 第 2 条，措辞 V4 §10 R-013⑨）---- */
.entry-mode-switch {
  display: flex;
  align-items: stretch;
  margin-top: var(--s3);
}
.entry-mode-tab {
  position: relative;
  flex: 1;
  height: 40px;
  font-family: var(--font-body);
  font-size: var(--fs-body);
  color: var(--ink-2); /* 未选中=重墨 */
  border-bottom: var(--lw-line) solid var(--line);
  transition: color var(--t-fast) var(--ease-out);
}
.entry-mode-tab[aria-selected="true"] {
  color: var(--ink); /* 选中=浓墨 */
  font-weight: 600;
  border-bottom-color: transparent; /* 与内容区之间不画底边线 */
}
/* 选中签：签底 2px 短朱线（非通栏），仿票号题签惯例 */
.entry-mode-tab[aria-selected="true"]::after {
  content: "";
  position: absolute;
  left: 50%; bottom: 0;
  transform: translateX(-50%);
  width: 28px;
  height: var(--lw-heavy);
  background: var(--line-red);
}
.entry-mode-sep {
  width: var(--lw-line);
  margin-bottom: var(--lw-line);
  background: var(--line);
  flex-shrink: 0;
}
/* 模式内容区：双层常驻叠放（SPEC V4.2 §5，R-014④）——时段记录/模糊录入
   两层同格叠放（grid-area 重合），容器高度=两层最大值，切换零跳动；非活动
   层 visibility:hidden 仍占位（贡献 grid 高度但不可见/不可交互），只有
   opacity 渐换 ≤140ms（复用 --t-page 页面切换节奏）。离场（失活）用
   visibility 延时切换，保证 opacity 淡出播完才真正消隐；入场（激活）visibility
   立即生效，随 opacity 淡入同步显现。 */
.entry-mode-body {
  display: grid;
}
.entry-mode-layer {
  grid-area: 1 / 1;
  min-width: 0;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity var(--t-page) var(--ease-out), visibility 0s linear var(--t-page);
}
/* 二轮盲审 P2:等高约束下模糊录入层内容少,顶排导致下方大片空白——
   内容垂直居中,留白上下分摊,观感从容 */
.entry-mode-layer[data-mode="approx"] {
  display: flex;
  flex-direction: column;
  justify-content: center;
}
.entry-mode-layer[data-active="true"] {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transition: opacity var(--t-page) var(--ease-out);
}
/* 时段记录：止行下派生只读夹注 */
.entry-derived {
  margin-top: var(--s2);
  font-family: var(--font-body);
  font-size: var(--fs-body-sm);
  color: var(--ink-3);
}
.entry-error {
  font-size: var(--fs-body-sm);
  font-weight: 600;
  color: var(--red-text);
  margin-top: var(--s2);
  display: none;
}
.entry-error[data-visible="true"] { display: block; }
/* R-016③排版优化：确认键此前只继承 .btn 的内容宽（inline-flex 无 width），
   窄条挤在票面左侧，观感像普通行按钮而非本面板的主操作——改满宽、与
   .book-submit 同级处理，视觉上真正立住"这是这张票的唯一确认动作"。 */
.entry-confirm {
  width: 100%;
  margin-top: var(--s1);
}
/* 「记下」确认键与「当日笔录」参考列表层级拉开（同一条要求②）：确认键是本
   面板唯一的主操作，当日笔录只是只读参考，视觉权重理应明显更低——分隔线
   之外再加一段留白（--s3，与分隔线自身 --s4 margin 叠加，总间距约等于
   --s4+--s3，读作与上方字段组的 --s4 节奏有意拉开一档，而非同级紧邻）。 */
.entry-daylist { margin-top: var(--s3); }
.entry-quick {
  font-family: var(--font-body);
  font-size: var(--fs-body-sm);
  color: var(--ink-2);
  text-decoration: underline;
  text-decoration-style: dotted;
  text-underline-offset: 3px;
  transition: color var(--t-fast) var(--ease-out);
}
.entry-quick:hover:not(:disabled) { color: var(--ink); }
/* 时长未填时置灰不可点：淡墨字 + 虚线换乌丝色，不引入新灰阶 */
.entry-quick:disabled {
  color: var(--ink-3);
  text-decoration-color: var(--line);
  cursor: not-allowed;
}

/* ---- 朱丝细框小签：面板当前时辰徽章（SPEC §6.2 修正版，R-012 ⑤）----
   根因修复：齐伋体做小字违 §7.3 排印纪律，改霞鹜文楷（font-body）+ 主辅两级
   （地支字浓墨 15px / 「时」字淡墨 11px 基线微降，仿票号大数小单位）；
   框仅保留朱丝边线（--line-red），文字改墨色（不再用红做正文字色）；
   固定内边距 + line-height:1 + flex 居中，min-width 取十二字实测最宽值
   （WenKai 全角字符等宽，两字内容天然不跳宽，min-width 兜底防极端字重差异）。 */
.shichen-tag {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 62px;
  padding: 0.35em 0.6em;
  border: var(--lw-line) solid var(--line-red);
  line-height: 1;
}
.shichen-tag-zi {
  font-family: var(--font-body);
  font-size: 15px;
  color: var(--ink);
}
.shichen-tag-suffix {
  font-family: var(--font-body);
  font-size: 11px;
  color: var(--ink-3);
  position: relative;
  top: 0.15em; /* 基线微降 */
  margin-left: 1px;
}
/* 当前时辰行（时辰账页 §9）与本组件共用「主辅分级」语汇，深朱强调走
   .ledger-row[data-now="true"] 自身规则，不叠加在 shichen-tag 上 */

/* ---- 移动端苹果计时器式双滚轮（无秒，时 0-18 / 分 0-59 逐分） ---- */
.numwheel {
  position: relative;
  display: flex;
  justify-content: center;
  align-items: center;
  gap: var(--s3);
  height: 176px;
}
.numwheel::before,
.numwheel::after {
  content: "";
  position: absolute;
  left: var(--s3); right: var(--s3);
  height: var(--lw-hair);
  background: var(--line);
  pointer-events: none;
  z-index: 1;
}
.numwheel::before { top: calc(50% - 18px); }
.numwheel::after { top: calc(50% + 18px); }

.numwheel-col {
  flex: 1;
  max-width: 72px;
  height: 100%;
  overflow-y: scroll;
  scroll-snap-type: y mandatory;
  scroll-padding: 50% 0;
  overscroll-behavior: contain;
  scrollbar-width: none;
  -ms-overflow-style: none;
  mask-image: linear-gradient(to bottom, transparent 0%, black 34%, black 66%, transparent 100%);
  -webkit-mask-image: linear-gradient(to bottom, transparent 0%, black 34%, black 66%, transparent 100%);
}
.numwheel-col::-webkit-scrollbar { display: none; }
.numwheel-pad { height: 72px; scroll-snap-align: none; }
.numwheel-item {
  height: 36px;
  display: flex;
  align-items: center;
  justify-content: center;
  scroll-snap-align: center;
  font-family: var(--font-num);
  font-variant-numeric: tabular-nums;
  font-size: 17px;
  color: var(--ink-3);
  opacity: .5;
  transition: color var(--t-fast) var(--ease-out), opacity var(--t-fast) var(--ease-out);
}
.numwheel-item[data-selected="true"] {
  opacity: 1;
  color: var(--ink);
  font-weight: 600;
}
.numwheel-unit {
  font-family: var(--font-body);
  font-size: var(--fs-body-sm);
  color: var(--ink-3);
}
/* 空态：仅隐去当前居中的"选中槽"（无默认值时它不代表真实选择），其余可拨动列项
   照常可见（沿用默认的淡墨/浓墨双态），居中叠加「—」占位刻线顶替被隐去的选中项数字。
   D1 修复：旧规则曾无差别隐去整列全部项（含未选中项），导致移动端滚轮列在触碰前
   数字滚轮"完全空白不可见"，只剩「时 — 分」单位行——此规则必须只命中
   [data-selected="true"] 这一项，其余项交给默认 .numwheel-item 样式正常显示。 */
.numwheel[data-empty="true"] .numwheel-item[data-selected="true"] { color: transparent; opacity: 0; }
.numwheel-empty-mark {
  display: none;
  position: absolute;
  left: 0; right: 0;
  top: 50%;
  transform: translateY(-50%);
  text-align: center;
  font-family: var(--font-num);
  font-size: 20px;
  color: var(--ink-3);
  pointer-events: none;
  z-index: 2;
}
.numwheel[data-empty="true"] .numwheel-empty-mark { display: block; }

/* ---- 桌面端就地数字输入格 ---- */
.numfield-group {
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
}
.numfield {
  width: 64px;
  height: 40px;
  text-align: center;
  border: var(--lw-line) solid var(--line);
  border-radius: var(--r-ctl); /* R-019④小控件统一用 --r-ctl(原 --r-sm)(W13a) */
  background: var(--paper-2);
  color: var(--ink);
  font-family: var(--font-num);
  font-variant-numeric: tabular-nums;
  font-size: var(--fs-sub);
  transition: border-color var(--t-fast) var(--ease-out);
}
.numfield::placeholder { color: var(--ink-3); font-family: var(--font-body); }
.numfield:focus { border-color: var(--ink); }
.numfield[data-invalid="true"] { border-color: var(--red-text); color: var(--red-text); }
.numfield-sep { color: var(--ink-3); }


/* ============================================================================
   12. 记录卡片 day-tickets（一日多笔，SPEC §5.6 结尾）
   DOM 契约：
   <section class="day-tickets">
     <div class="tag-rule"><span class="tag-rule-label">今日之录</span><span class="tag-rule-line"></span></div>
     <div class="ticket-row day-ticket">  <!-- R-022③ 账目式两行 -->
       <div class="day-ticket-head">
         <span class="day-ticket-index num">第 1 笔</span>
         <span class="day-ticket-shichen">申时</span>  <!-- 模糊录入笔=「模糊录入」 -->
         <span class="day-ticket-dur num">45 分钟</span>  <!-- margin-left:auto 右靠 -->
         <span class="day-ticket-flag day-ticket-flag--backfill">补录</span>
         <span class="day-ticket-actions">
           <button class="day-ticket-action">改</button>
           <button class="day-ticket-action">删</button>
         </span>
       </div>
       <div class="day-ticket-body">  <!-- 仅时段记录笔有 -->
         <span class="day-ticket-meta">(起止对分解见下方注释)</span>
       </div>
     </div>
     ...
   </section>
   ========================================================================== */
.day-ticket {
  /* R-022③:单行 flex 改「账目式两行」块结构(head 摘要行 + body 起止行),
     根治窄容器单行挤爆叠字 */
  display: block;
  transition: background var(--t-fast) var(--ease-out);
}
.day-ticket:hover { background: var(--paper-3); }
.day-ticket-head {
  display: flex;
  align-items: center;
  gap: var(--s3);
}
.day-ticket-body {
  margin-top: var(--s2);
}
.day-ticket-index {
  flex-shrink: 0;
  color: var(--ink-2);
  font-weight: 600;
}
.day-ticket-meta {
  display: inline-flex;
  align-items: baseline;
  gap: var(--s3);
  font-family: var(--font-body);
  color: var(--ink);
}
/* R-022③ 结构化三段(时辰签/起止对/时长),替代旧自由文本串:
   <span class="day-ticket-shichen">亥时</span>(模糊录入笔=「模糊录入」)
   <span class="day-ticket-meta">
     <span class="tk-time"><span class="tk-time-label">起</span><span class="tk-time-val num">22:39</span></span>
     <span class="tk-arrow">→</span>
     <span class="tk-time"><span class="tk-time-label">止</span>[<span class="tk-time-extra">次日</span>]<span class="tk-time-val num">23:50</span></span>
   </span>
   <span class="day-ticket-dur num">1 时 11 分</span> */
.day-ticket-shichen {
  flex-shrink: 0;
  font-size: var(--fs-caption);
  color: var(--ink-2);
  border: var(--lw-hair) solid var(--line);
  border-radius: var(--r-ctl);
  padding: 1px 6px;
  white-space: nowrap;
}
.tk-time {
  display: inline-flex;
  align-items: baseline;
  gap: 4px;
  white-space: nowrap;
}
.tk-time-label,
.tk-time-extra,
.tk-arrow {
  font-size: var(--fs-caption);
  color: var(--ink-3);
}
.day-ticket-dur {
  flex-shrink: 0;
  margin-left: auto;
  white-space: nowrap;
  font-weight: 600;
  color: var(--ink);
}
.day-ticket-flag {
  flex-shrink: 0;
  font-size: var(--fs-caption);
  color: var(--ink-2);
  border: var(--lw-hair) dashed var(--line);
  padding: 1px 6px;
}
/* 行内操作：静止不可见，hover/聚焦才显影（design-taste 黄金法则③） */
.day-ticket-actions {
  flex-shrink: 0;
  display: inline-flex;
  gap: var(--s3);
  opacity: 0;
  transition: opacity var(--t-fast) var(--ease-out);
}
.day-ticket:hover .day-ticket-actions,
.day-ticket:focus-within .day-ticket-actions { opacity: 1; }
.day-ticket-action {
  font-family: var(--font-body);
  font-size: var(--fs-body-sm);
  color: var(--ink-2);
  text-decoration: underline;
  text-decoration-style: dotted;
  text-underline-offset: 3px;
}
.day-ticket-action:hover { color: var(--ink); }


/* ============================================================================
   13. 空态 empty / 通用按钮
   ========================================================================== */
.empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--s2);
  padding: var(--s7) var(--s4);
  text-align: center;
}
.empty-title {
  font-family: var(--font-body);
  font-weight: 600;
  font-size: var(--fs-sub);
  color: var(--ink-2);
}
.empty-desc {
  font-family: var(--font-body);
  font-size: var(--fs-body-sm);
  color: var(--ink-3);
  max-width: 300px;
}

/* 通用按钮：骨架永远黑白灰，色彩只进数据（design-taste 黄金法则②），
   主按钮浓墨底+纸色字，禁彩色胶囊/渐变/投影 */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s2);
  height: 40px;
  padding: 0 var(--s4);
  border-radius: var(--r-ctl);
  font-family: var(--font-body);
  font-size: var(--fs-body);
  font-weight: 600;
  border: var(--lw-line) solid var(--ink);
  background: var(--ink);
  color: var(--paper-2);
  transition: background var(--t-fast) var(--ease-out), opacity var(--t-fast) var(--ease-out);
}
.btn:hover:not(:disabled) { opacity: .9; }
.btn:active:not(:disabled) { opacity: .8; }
/* D3：禁用态不许走"现代灰"（opacity 淡化整块底+字同时褪色，视觉上是灰盒子）；
   改用票据语汇——票面底 + 乌丝虚线框 + 淡墨字，与可用态"浓墨底纸色字"区分明确，
   同一套四色令牌内表达，不引入任何新灰阶 */
.btn:disabled {
  opacity: 1;
  cursor: not-allowed;
  background: var(--paper-2);
  border-style: dashed;
  border-color: var(--line);
  color: var(--ink-3);
}
.btn-ghost {
  background: transparent;
  border-color: var(--line);
  color: var(--ink);
}
.btn-ghost:hover { background: var(--paper-3); opacity: 1; }

/* 分段控件（周/月/年、子榜切换等，跨组件复用） */
.seg {
  display: inline-flex;
  border: var(--lw-line) solid var(--line);
  border-radius: var(--r-ctl); /* R-019④小控件统一用 --r-ctl(W13a) */
  padding: 2px;
  gap: 2px;
}
/* CARD-G3W10（F-P1-03/04：近七日/近三十日切换、榜签/子榜签命中区扩容）：
   视觉像素零变化铁律——.seg-item 选中态自带可见背景/描边，若直接放大自身
   padding 会连带放大那块可见胶囊，所以真正的可见外观（背景/边框）移到
   ::before 伪层，固定钉在原 5px/14px 的相对位置；宿主 .seg-item 自身只借
   透明 padding+等量负 margin 把「量出来的」命中盒子撑到 ≥44px，父级
   flex/grid 轨道按 margin-box 折算回原值，不占用旁边元素的空间、不移动
   布局流。横向只加 1px/边——.seg 容器自身 gap 仅 2px（三签一排的
   「周/月/年」），加满会让相邻签的命中盒真重叠；加 1px/边正好在 2px 缝隙
   中点相遇不重叠，刚好够把 F-P1-04 实测最窄 43px 一档顶到 ≥44。 */
.seg-item {
  padding: 11px 16px; /* 5px/14px 原值各 +6/+2；比命中区差额多算 1px 补偿
    「取消原占位边框」腾出的 2px(原 border 1px×2 边)，否则净增量会被吃掉
    (实测踩过一次坑：单纯 +5/+1 换算，边框归零后量出来只有 43px，达不到 44) */
  margin: -5px -1px;
  font-family: var(--font-body);
  font-size: var(--fs-body-sm);
  font-weight: 400;
  color: var(--ink-2);
  border: none;
  border-radius: calc(var(--r-ctl) - 2px); /* 内层选中态胶囊跟随外框圆角收 2px(W13a) */
  position: relative;
  z-index: 0; /* 显式建立层叠上下文，保证下方 ::before(z-index:-1) 只压在
                 自己文字下面，不牵动页面其它层叠顺序 */
  transition: color var(--t-fast) var(--ease-out);
}
.seg-item::before {
  content: "";
  position: absolute;
  top: 5px; right: 1px; bottom: 5px; left: 1px; /* 精确抵消上面加出来的
    padding，钉回原 5px/14px 视觉框位置——见上方说明 */
  border: var(--lw-line) solid transparent; /* 占位边框，防选中态描边致尺寸抖动 */
  border-radius: calc(var(--r-ctl) - 2px);
  z-index: -1;
  pointer-events: none;
  transition: background var(--t-fast) var(--ease-out), border-color var(--t-fast) var(--ease-out);
}
.seg-item[aria-selected="true"] {
  color: var(--ink);
  font-weight: 600;
}
.seg-item[aria-selected="true"]::before {
  background: var(--paper-3);
  border-color: var(--ink-2);
}


/* ============================================================================
   14. 纸纹背景 / 印章 seal-v2 / 鱼尾 fishtail
   ========================================================================== */
/* 宣纸质感：构建期烘焙 PNG data-URI（见 tokens.css --texture-xuanzhi 注释），
   两层暖色云絮 radial-gradient 还原纤维疏密 + multiply 纹理平铺，
   固定于视口不随滚动重算，任何文字/图表下不可感知颗粒（纹理压字门自查通过：
   α 上限 3.9% 远低于正文墨色 14.9:1 对比度量级，不构成阅读阻碍）。 */
body {
  background-color: var(--paper);
  background-image:
    radial-gradient(ellipse 60% 40% at 15% 8%, rgba(var(--paper-3-rgb), .55), transparent 60%),
    radial-gradient(ellipse 55% 55% at 88% 82%, rgba(var(--paper-3-rgb), .45), transparent 65%),
    var(--texture-xuanzhi);
  background-repeat: no-repeat, no-repeat, repeat;
  background-size: auto, auto, 220px 220px;
  background-attachment: fixed, fixed, fixed;
  background-blend-mode: normal, normal, multiply;
}

/* 印章「正统炎黄」logo（CARD-G3W11 R-031①，接线上站，替代 V4/V4.2「压角
   朱文印」DOM 拼字方案）：owner 验收终版矢量 SVG 内联注入（来源见
   ui-pages.js buildSealQuad 头注），登录/注册书名页与首屏立轴共用同一枚，
   微旋转仅此一处使用（SPEC V4 §3 R-013②，V4.2 §2 立轴构图沿用，本卡原样
   保留）。.seal-v2 容器宽高（占位尺寸）与旧方案完全一致，零布局位移；
   内部旧三层（.seal-v2-face 印面底+.seal-v2-frame 双线印边+.seal-v2-chars
   2×2 田字格四字）随本卡整体下架，改由 .seal-v2 svg 规则把内联 SVG 撑满
   容器。 */
.seal-v2 {
  position: relative;
  display: inline-block;
  width: 50px;
  height: 50px;
  transform: rotate(-.3deg); /* SPEC §4.5「微旋转 ±0.3deg 只允许印章使用」 */
}
.seal-v2 svg {
  display: block;
  width: 100%;
  height: 100%;
}
@media (min-width: 768px) {
  .seal-v2 { width: 76px; height: 76px; }
}

/* 压角位停靠语汇沿用：立轴构图中改「与题字栏同轴水平居中」，实际定位交由
   V4.2 新增 .scroll-seal-dock 覆盖（见 §16），本类只保留内层 flex 居中骨架。 */
.seal-v2-dock {
  display: flex;
  justify-content: center;
  width: 100%;
}

/* 鱼尾：页脚窄三角收尾装饰（R7 §5.3）。纯 CSS 三角——原「〆」字符系日文占位符
   且不受子集保证，G2 终审改为零字体依赖画法（古籍版心鱼尾的窄三角转译） */
.fishtail {
  display: flex;
  justify-content: center;
  margin: var(--s6) 0 var(--s4);
}
/* R-020③:页脚收尾装饰由窄三角改「旋转太极」——单元素纯 CSS 画法(零字体
   零图片依赖):底层 180° 线性分色(上墨下纸)+两枚半径 25% 的鱼眼圆(左墨含
   纸点/右纸含墨点)拼出 S 形回旋;慢速匀速自转,reduced-motion 下静止(§15) */
.fishtail::before {
  content: "";
  width: 18px;
  height: 18px;
  border-radius: 50%;
  border: var(--lw-hair) solid var(--line);
  /* R-022②:鱼向镜像修正——顺时针自转不变,但两鱼头(圆球端带鱼眼的大圆弧)
     必须领先于旋转方向:墨鱼身居上半,鱼头圆球改在右侧中线(左侧为其收尖鱼尾),
     顺时针时头在前尾在后「顺着圆球方向」游;纸鱼对偶。旧版头尾颠倒=倒着游 */
  background:
    radial-gradient(circle at 25% 50%, var(--ink-3) 0 2px, var(--paper-2) 2px 4.5px, transparent 4.5px),
    radial-gradient(circle at 75% 50%, var(--paper-2) 0 2px, var(--ink-3) 2px 4.5px, transparent 4.5px),
    linear-gradient(180deg, var(--ink-3) 0 50%, var(--paper-2) 50% 100%);
  animation: taiji-spin 14s linear infinite;
}
@keyframes taiji-spin {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}


/* ============================================================================
   15. 响应式与 reduced-motion / 触控热区
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  .mask, .entry-ticket, .numwheel-item,
  .btn, .toc-item, .tab-paper-item, .entry-mode-body, .entry-mode-layer,
  .day-ticket-actions, .page-fade {
    transition: none !important;
  }
  .fishtail::before { animation: none !important; } /* R-020③ 太极静止 */
}

/* 触控热区兜底：一切可点击控件最小 44px（可访问性红线） */
.btn, .toc-item, .tab-paper-item, .seg-item, .gridcal-month, .day-ticket-action,
.masthead-switch, .entry-quick, .entry-mode-tab {
  min-height: 44px;
}
.seg-item, .gridcal-month, .day-ticket-action, .masthead-switch, .entry-quick {
  min-height: auto; /* 纯文字型次要操作不强制点击热区，避免行内文字被撑成大按钮块 */
}

/* ============================================================================
   CARD-G3W10 移动触达热区扩容（F-P1-01/02/05/06/07/08，依据 G3W9 回执 §3）
   统一配方：本组元素均为「纯文字/图标+下划线」型次要操作，自身零背景零
   边框，可以直接对宿主本体加透明 padding + 等量负 margin——padding 计入
   元素自身 getBoundingClientRect（命中区审计据此判定），负 margin 在父级
   flex/grid 的 margin-box 折算里精确抵消掉新增的 padding，兄弟元素间距、
   自身可见文字位置、可见描边/背景（本组均无）三者都不移动，符合工单铁律
   「布局流不许动」。.seg-item（F-P1-03/04）例外——它自带可见选中态背景，
   拆到 §13 原位用 ::before 单独处理，见上方注释。
   各元素方向偏置均取自「往非交互空白处扩、往相邻可点元素方向少扩」的
   原则，避免命中区互相重叠误触相邻控件（尤其记一笔改/删两个高风险动作）。
   ========================================================================== */

/* F-P1-01 masthead 品牌名「练功志」52×28→需高度达标，宽度已够，纯上下扩 */
.masthead-brand {
  padding-top: 9px;
  padding-bottom: 9px;
  margin-top: -9px;
  margin-bottom: -9px;
}

/* F-P1-02 masthead 右上「退出登录」59×19→高度缺口大，且紧贴上方日期/时辰
   行(两行间 row-gap 仅 2px)——绝大部分扩容量往下方乌丝线/刻度尺方向要
   (纯装饰区零冲突)，往上只留 3px 最小缓冲 */
.masthead-switch {
  padding-top: 3px;
  padding-bottom: 23px;
  margin-top: -3px;
  margin-bottom: -23px;
}

/* F-P1-05（记一笔改/删，13×23，本卡重点）+ F-P1-06（管理操作行，同一
   .day-ticket-action 视觉语汇复用，26px 高文字链接）：先统一垂直方向
   （两个场景都要达标，取两者所需的较大值）+ 一份对管理行窄标签（"成就"
   "停用"30px 宽）也够用的通用水平缓冲；记一笔面板内改/删两钮横向差距太
   大（13→44 需要单边多补近 30px），再用 :first-child/:last-child 单独
   往各自「外侧」（远离另一个按钮的一侧）加码，内侧只留 5px，两内侧共
   10px < 该容器 12px 的 gap，命中区不重叠，不会连累点错「删」。 */
.day-ticket-action {
  padding: 11px 8px;
  margin: -11px -8px;
  position: relative;
}
.day-ticket-actions .day-ticket-action:first-child {
  padding-left: 27px;
  padding-right: 5px;
  margin-left: -27px;
  margin-right: -5px;
}
.day-ticket-actions .day-ticket-action:last-child {
  padding-left: 5px;
  padding-right: 27px;
  margin-left: -5px;
  margin-right: -27px;
}

/* F-P1-07 我的页「退出登录」/「管理后台」60×26（.me-settings 纵向排列，
   gap 12px）：两条各自往「远离另一条」的方向多扩，挨着对方的一侧只留
   2px，两侧共 4px < 12px gap，不重叠；仅剩「退出登录」一条时(非管理员，
   常见情形)同时命中 :first-child 与 :last-child，取源码序靠后的
   :last-child 那套值，向下多留、向上留 2px，同样安全达标。 */
.me-settings-link {
  position: relative;
}
.me-settings-link:first-child {
  padding-top: 16px;
  padding-bottom: 2px;
  margin-top: -16px;
  margin-bottom: -2px;
}
.me-settings-link:last-child {
  padding-top: 2px;
  padding-bottom: 16px;
  margin-top: -2px;
  margin-bottom: -16px;
}

/* F-P1-08a 登录/注册/强改密卡「显示/隐藏」密码切换 26×23：field-toggle
   与下方 .field-text 输入框只隔 2px(.book-fieldrow 的 column gap)，向下
   只留 2px 防止咬进输入框可点范围，缺口全部往上要（上方是同一行的字段
   标题文字，非交互，零冲突）。横向本以为两侧都是空白余量对称外扩即可，
   实测踩坑：.book-fieldrow-head 是 justify-content:space-between，
   field-toggle 本就贴着卡片右侧内边距零富余，右侧一旦外扩就会让
   .book-auth-layer 的 scrollWidth 真的量出超出 clientWidth（哪怕像素不可见，
   仍会被 §三 A 容器横滚断言判定为「可横滚」）——右侧起初改留 1px 仍未清零
   （space-between 的自由空间分配一旦算不平，残留的 1px 一样会露出来），
   最终改成右侧完全归零（padding-right/margin-right 均不动），横向缺口
   全部改从左侧要(左侧到字段标题之间有大片留白，绝对安全)。 */
.field-toggle {
  padding: 20px 0 3px 20px; /* top/right/bottom/left：右侧维持原值 0 完全不
    动(实测 space-between 布局对右侧哪怕仅 +1px 都会让 .book-auth-layer
    量出可横滚，干脆右侧零改动最保险)，横向缺口全部改从左侧要 */
  margin: -20px 0 -3px -20px;
}

/* F-P1-08b 登录/注册卡底「去注册/去登录」39×17：所在 .book-card-foot 是
   独立段落，行内 <a>，是这一行末尾唯一的收尾元素。实测踩坑：内联(inline)
   元素的负 margin 只影响「后面还有兄弟内容」时的相对位置——它是行尾最后
   一个元素，右侧没有后续内容可以被负 margin-right"拉回去"，padding-right
   加多少，自身盒子的右边缘就真的往右多出多少，量出来是 .book-auth-layer
   真横向溢出（首次按左右对称各 +3px 处理时，6 档全部命中 §三 A 容器横滚
   断言，实测 scrollWidth 比 clientWidth 多 3px，与「唯一的行尾元素+右侧
   padding 净移动」的量完全吻合）。改法＝右侧 padding/margin 都归零(不动)，
   横向缺口全部改从左侧要（原理与 F-P1-08a 同源，见上方 .field-toggle 注释）；
   上下则不受这条「行尾元素」限制——内联元素的纵向 padding 只会视觉溢出到
   行框外，不进入横向 scrollWidth 计算，也不影响行高，两侧对称扩没有风险。 */
.book-link {
  padding: 14px 0 14px 6px;
  margin: -14px 0 -14px -6px;
}


/* ============================================================================
   16. 登录/注册「立轴」首屏 + 渐显浮层（V4.2 §2/§3 R-014①② 推倒重做，替代
   V3/V4「书名页」三件套平起平坐版式；仅登录/注册两态使用，不挂 masthead/
   toc/tab-paper——「开卷之前不算书内页」）
   DOM 契约（立轴首屏，默认态，零表单）：
   <main class="page book-page">
     <div class="book-page-inner">
       <div class="scroll">
         <p class="scroll-slogan">争分夺秒 · 刻不容缓</p>  <!-- 竖排侧款，起笔低于「練」 -->
         <div class="scroll-title-col">
           <h1 class="scroll-title">练功志</h1>            <!-- 竖排单栏，齐伋体 -->
           <div class="seal-v2-dock scroll-seal-dock">
             <div class="seal-v2 seal-v2--quad" aria-hidden="true">…印随字尾，见 §14…</div>
           </div>
         </div>
       </div>
       <button class="btn scroll-enter">进入</button>
       <div class="fishtail"></div>
     </div>
   </main>
   <div class="mask book-auth-mask" data-state="closed"></div>
   <div class="book-auth-overlay" data-state="closed" role="dialog" aria-modal="true">
     <div class="ticket book-card book-auth-card">
       <div class="ticket-body">…登录卡（默认）或注册卡内容，二态互斥渐换…</div>
     </div>
   </div>
   点「进入」→ .book-auth-mask/.book-auth-overlay 打上 data-state="open"（单层
   opacity ≤140ms + 遮罩 backdrop-filter 虚化）；浮层内「去注册」/「去登录」
   原地渐换 .ticket-body 内容（不重新开合浮层外壳）；Esc/点遮罩关闭回首屏。
   .book-card-title/.book-fieldrow/.field-text/.field-toggle/.ticket-divider/
   .book-submit/.book-card-foot/.book-link 字段语汇不变（详见下方浮层区规则）。
   1280 断点：.book-page-inner 整组偏左约 42% 视口宽，.book-page::before/::after
   两根发丝竖线=卷轴绫边。印章结构详见 §14「压角朱文印」。
   ========================================================================== */
.book-page {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
  padding: 24px; /* SPEC §2.2「375：整屏即立轴画心…四边 24px 装裱留白」 */
  position: relative;
}
.book-page-inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  width: 100%; /* R-017 axisa:flex 下 inner 宽度塌缩会让台面百分比定位全坍中轴 */
}
/* ---- 首屏 V5「纵轴偏右」台面 axisa（R-017 方案 A 修订版,取代下方 .scroll
   立轴构图;.scroll* 规则组保留仅为历史对照,DOM 已不再引用）。台面居中于
   视口(呼应 owner 居中令),轴线在台面内偏右 73%;顶部留白 clamp 保底不出血。---- */
.axisa {
  position: relative;
  width: 100%;
  max-width: 430px;
  margin: 0 auto;
  height: 100dvh;
  min-height: 584px;
}
.axisa-title {
  position: absolute;
  left: 73%;
  top: clamp(30px, 6dvh, 56px);
  transform: translateX(-50%);
  font-family: var(--font-display);
  font-weight: 600;
  color: var(--ink);
  font-size: 100px;
}
.axisa-c { line-height: 1.02; }
.axisa-c1 { transform: translateX(-3px); }
.axisa-c2 { font-size: 1.12em; transform: translateX(1px); }
/* R-021①:「志」增大 20%(1→1.2em,现为三字最大);横挪回 2px 防大字外飘 */
.axisa-c3 { font-size: 1.2em; transform: translateX(2px); }
.axisa-slogan {
  position: absolute;
  writing-mode: vertical-rl;
  text-orientation: upright;
  white-space: nowrap;
  font-family: var(--font-body);
  font-size: 12px;
  letter-spacing: .42em;
  color: var(--ink-3);
}
.axisa-slogan--a { left: 13%; top: 31%; }
.axisa-slogan--b { left: calc(13% + 26px); top: 34.5%; }
.axisa-seal { position: absolute; left: 7%; bottom: 11%; }
.axisa-seal .seal-v2 { width: 44px; height: 44px; }
.axisa-enter {
  position: absolute;
  right: 8%;
  bottom: 7%;
  background: none;
  border: none;
  border-bottom: var(--lw-thick) solid var(--ink);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 16px;
  letter-spacing: .12em;
  padding: 10px 4px 10px;
  min-height: 44px;
  cursor: pointer;
}
.axisa-enter:hover { opacity: .78; }
@media (min-width: 768px) {
  .axisa { max-width: 860px; } /* 手卷式:宽屏台面展开,列锚右、左侧大留白(母本桌面口径) */
  .axisa-title { font-size: 122px; }
  .axisa-slogan { font-size: 13px; }
  .axisa-slogan--a { left: 16%; }
  .axisa-slogan--b { left: calc(16% + 28px); }
  .axisa-seal .seal-v2 { width: 54px; height: 54px; }
  .axisa-enter { font-size: 17px; }
}

/* ---- (已退役)立轴构图本体（旧 .scroll,DOM 已不引用,仅存对照） ---- */
.scroll {
  display: flex;
  align-items: flex-start;
  justify-content: center;
  /* G2 终审修(轴线):侧款改绝对定位脱流——在流内会把组合中心挤离題字轴,
     导致印/字轴线与「进入」钮错位 24px;脱流后題字栏=唯一居中主体,
     字、印、钮天然同轴 */
  position: relative;
}
.scroll-slogan {
  writing-mode: vertical-rl;
  text-orientation: upright;
  white-space: nowrap;
  font-family: var(--font-body);
  font-size: 15px;
  line-height: 1.6;
  letter-spacing: 0.14em;
  color: var(--ink-3);
  margin: 0;
  position: absolute;
  right: calc(100% + var(--s5));
  top: 2.7em; /* 起笔低于「練」约 15-20% 栏高（em 相对本行 15px 字号） */
}
@media (min-width: 768px) {
  .scroll-slogan { font-size: 16px; top: 3.6em; }
}
.scroll-title-col {
  display: flex;
  flex-direction: column;
  align-items: center;
  font-size: 78px; /* 题字单字尺寸主控点：.scroll-title 与 .scroll-seal-dock
                       的 em 间距均相对此值计算（SPEC 移动 72-84px 范围内） */
}
@media (min-width: 768px) {
  .scroll-title-col { font-size: 116px; } /* SPEC 桌面 104-120px 范围内 */
}
.scroll-title {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1em;
  line-height: .97;  /* 字挨字，SPEC 范围 0.95-1 */
  letter-spacing: 0;
  writing-mode: vertical-rl;
  text-orientation: upright;
  white-space: nowrap;
  color: var(--ink);
  margin: 0;
}
.scroll-seal-dock {
  width: auto;              /* 覆盖 §14 .seal-v2-dock 基类的 width:100%，
                                随印面本身尺寸收缩，靠父级 align-items 居中 */
  margin-top: .22em;        /* 志字正下方，垂直间距 0.2-0.25em（相对题字单字尺寸） */
}
.scroll-enter {
  width: 280px;
  max-width: calc(100vw - 48px);
  margin-top: 84px; /* 居构图重心正下方，SPEC 范围 72-96px */
}

/* 1280：R-017 owner 令构图居中(原 42vw 偏心经营位废除);两侧发丝竖线
   =卷轴绫边,随之改对称(左右各 24vw)。 */
@media (min-width: 1280px) {
  .book-page::before,
  .book-page::after {
    content: "";
    position: fixed;
    top: 80px;
    bottom: 80px;
    width: var(--lw-line);
    background: rgba(var(--line-rgb), .35);
    pointer-events: none;
  }
  .book-page::before { left: 24vw; }
  .book-page::after { right: 24vw; }
}

/* ---- 渐显浮层：登录/注册票据卡外壳（SPEC §3）。基础 fixed/z-index/opacity
   过渡骨架复用 §11 .mask 语法（data-state="open" 才可见可交互）；本节只覆写
   遮罩强度/虚化与浮层自身定位，浮层内卡片沿用既有 .book-card/.book-fieldrow
   字段语汇（见下方 .book-card 起）。 ---- */
.book-auth-mask {
  background: var(--scrim-book); /* 墨色 20%，比 --scrim 35% 更浅——立轴在虚焦
                                     中仍隐约可见（SPEC「背景虚化」不等于遮死） */
  backdrop-filter: blur(5px);
  -webkit-backdrop-filter: blur(5px);
}
.book-auth-overlay {
  position: fixed;
  inset: 0;
  z-index: 52; /* 高于 .book-auth-mask 的 z-index:50（继承自 .mask） */
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--s4); /* 原 s5，为固定正方形腾多 16px 视口余量（R-016②） */
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--t-page) var(--ease-out); /* 单层 opacity ≤140ms 铁律 */
}
.book-auth-overlay[data-state="open"] {
  opacity: 1;
  pointer-events: auto;
}
/* R-016②：登录卡/注册卡改同一固定尺寸正方形，切换零宽高变化（此前注册比
   登录多一个字段+密码提示行，卡片实测 320×503 vs 320×398，切换时明显跳一
   下）。做法＝复用 §11 entrySheet 双签「时段记录/模糊录入」的双层常驻叠放法
   （.entry-mode-layer 同款）：.book-auth-card 定宽定高的正方形外壳，
   .ticket-body 内改 grid 单格，登录/注册两层同格叠放常驻，只切 data-active
   做 opacity 渐换；容器尺寸由外壳固定值决定，与哪层内容更高无关。
   375 端 344×344，768px+ 420×420（SPEC 建议值 330/400 附近，字段收紧+浮层
   自身内边距收窄后仍无法让注册卡零滚动完整可见，故上调半档换更少滚动，
   两态仍严格同尺寸）；每层内容 overflow-y:auto 兜底——注册三字段+密码提示
   行天然比登录高，字段/间距已按 --s 系列收紧到贴边，仍放不下整卡时层内
   滚动，绝不撑破外壳固定尺寸；justify-content:safe center 保底顶对齐，
   保证滚动场景下标题与提交键两端都可达（实测 plain center 会两端各裁一半）。 */
/* .book-card.book-auth-card 复合选择器（非单独 .book-auth-card）：这两个
   class 同挂在同一元素上，特异性需赢过后面 .book-card 自身的 width:100%/
   max-width:320px（源码顺序在后，单类选择器特异性打平时会覆盖前面的规则）。 */
.book-card.book-auth-card {
  width: 344px;
  height: 448px; /* R-018:375 端正方形数学上装不下注册三字段的从容排布(硬塞=滚动条/裁切,
                    丑的根源)——移动端改同尺寸竖幅(1:1.3),两态零跳变铁律不变;桌面正方形保留 */
  max-width: calc(100vw - var(--s4) * 2); /* 随 .book-auth-overlay 的 s4 内边距同步 */
  padding: var(--s5) var(--s5) var(--s4); /* R-018:底线式输入省出的高度还给呼吸感 */
  border-radius: var(--r-pop); /* R-019④浮层级用 pop 令牌(大于卡片 r-card)(W13a) */
  text-align: left;
  display: flex;
  flex-direction: column;
}
/* 同 .entry-ticket 处理：.book-auth-card 同时挂 .ticket class（内线 ::before
   继承自 §5），浮层圆角覆写为 r-pop 时内线需跟随重算 */
.book-auth-card.ticket::before {
  border-radius: calc(var(--r-pop) - 2px);
}
.book-auth-card .ticket-body {
  flex: 1;
  min-height: 0;
  display: grid;
}
.book-auth-layer {
  grid-area: 1 / 1;
  min-width: 0;
  min-height: 0;
  overflow-y: auto;
  /* owner P5 试用意见(20260722)推翻 R-018 期"不露轨"注:真实新手在注册卡
     看不到滚动条就不知道下面还有字段,卡死在注册页。回归 §19① 全站默认
     "细滚动条常驻可见"基线(8px 胶囊 thumb,仅溢出时出现)——此处不再覆写
     scrollbar-width,继承全局 thin。 */
  /* R-041(owner 20260723二次截图令,T2)根因修复："卡内滚动条以悬浮错位形态
     叠在卡右缘"——本卡诊断：实测(headless Chrome + 真实取证脚本，未加
     --hide-scrollbars 参数以看清真实渲染)确认滚动条本身的盒模型位置是贴着
     .book-auth-layer 自己的右边界画的，问题出在"自己的右边界"与"卡片可视
     右边界"之间缺一道显式预留——多数操作系统/浏览器组合下滚动条是非叠加
     式（会挤占内容宽度），但启用了系统覆层滚动条(overlay scrollbar，Win/
     mac 触控板场景常见默认)的真实浏览器里，滚动条不挤占版面宽度，而是直接
     浮绘在内容右侧边缘之上——本容器此前没有为这种情形预留任何缓冲，滚动条
     轨道因此直接贴到内容乃至卡片内边距边界，读起来就是"悬浮、错位、叠在
     卡右缘"。通用化修法（不管注册卡本身是否还需要滚动，同款容器未来只要
     内容超高就会复现，故直接把两条加固规则写在 .book-auth-layer 基类上，
     不是给注册卡打局部补丁）：①scrollbar-gutter:stable——强制浏览器为滚动条
     恒定预留版面空间，即使当前内容不溢出也保留同样宽度，杜绝"有的浏览器
     挤内容、有的浏览器盖内容"的双重形态；②padding-right 给内容额外让出
     一点呼吸区，双重保险避免文字紧贴滚动条轨道。 */
  scrollbar-gutter: stable;
  padding-right: 6px;
  display: flex;
  flex-direction: column;
  /* safe center：内容够矮时真居中（登录卡的「内容纵向居中分布」）；内容比
     正方形更高时（注册卡三字段+密码提示行）自动回退顶对齐——纯 center 在溢出
     时会两端各裁一半，实测把标题「入册——新同修注册」和底部「去登录」都裁
     没了，两处都是这张卡必须看见的内容，safe 关键字正是为这种场景而生。 */
  justify-content: safe center;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity var(--t-page) var(--ease-out), visibility 0s linear var(--t-page);
}
.book-auth-layer[data-active="true"] {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transition: opacity var(--t-page) var(--ease-out);
}
@media (min-width: 768px) {
  /* R-041(owner 20260723二次截图令,T2)："注册页必须显示完整的页...保持
     正方形,所以你可能需要增加宽度。这样的话可能就不需要滑动块了"——原
     420×420 装不下注册卡四字段(实测需约 503px 内容高)，此前只能靠卡内滚动
     兜底。改法＝把"真实姓名/用户名"并成一行两列(见下方 .book-field-pair)
     省一整行竖向高度，同时加宽外壳给两列字段足够呼吸空间；两值一起调，
     使注册卡内容零溢出(实测见回执)且宽高比仍接近 1:1(590×560≈1.05:1)。
     登录/注册两态仍共用同一个固定尺寸盒子(R-016②零跳变架构不变)，登录卡
     内容改走 .book-login-narrow 把可视宽度钉回原基线，见下方规则。 */
  .book-card.book-auth-card { width: 590px; height: 560px; }
  /* 真实姓名/用户名并列一行两列，字段本身样式(.book-fieldrow)不变，只加
     栅格容器；窄屏(<768px)不进本 media query，两字段维持改版前逐字段纵向
     堆叠。 */
  .book-auth-card .book-field-pair {
    display: grid;
    grid-template-columns: 1fr 1fr;
    column-gap: var(--s5);
  }
  /* 登录卡内容视觉宽度钉回改版前 320px 基线并居中——外壳盒子客观上随注册卡
     加宽(两态共用同一固定尺寸盒子)，但登录卡本身只有账号/密码两个字段，
     不需要也不适合被拉伸铺满新宽度；.book-login-narrow 是 buildLoginCardBody
     的内容根节点(见 ui-pages.js)，只影响登录卡这一份内容，不影响注册卡。 */
  .book-auth-layer[data-mode="login"] { align-items: center; }
  .book-login-narrow { width: 100%; max-width: 320px; }
}

.book-card {
  width: 100%;
  max-width: 320px;
  text-align: left;
}
.book-card-title {
  font-family: var(--font-body);
  font-weight: 600;
  font-size: var(--fs-sub);
  color: var(--ink);
  margin-bottom: var(--s3); /* 原 s4，正方形定高后收紧一档（R-016③排版收紧） */
}
/* R-042(CARD-G3W22调研方案B"题签锚点版"，showConfirmOverlay 专用)：标题前置
   朱点，参数原样抄 .masthead-brand-dot(§15，见上方)，仅补一份 inline 排版
   下需要的间距/垂直微调(源规则用在 flex 行内靠 gap 控距，此处是 <p> 内文字
   前缀，flex 场景没有的 margin-right/position 由本规则自行补)。 */
.confirm-dot {
  display: inline-block;
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: var(--seal);
  margin-right: 6px;
  position: relative;
  top: -1px;
}
/* 标题下细线，参数抄 .tag-rule-line(§4，见上方) 的核心两行(height/background)；
   flex:1/align-self 是给三段式题签行(线两侧还有文字)用的，本处是独占一整行
   的 block div，不需要。 */
.dirb-rule {
  height: var(--lw-line);
  background: var(--line);
  margin: 0 0 var(--s3);
}
.book-fieldrow {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 2px;
  padding: var(--s2) 0;
}
/* R-018 重做:行间虚线删除——底线式输入本身即是线,叠虚线=线噪 */
.book-fieldrow-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
}
/* R-018 输入位重做:大白框(票面底+四边框)整体废除,改「账簿手写位」底线式——
   透明底+一道乌丝底线,聚焦底线转浓墨加重,与全站票据字段表同语汇 */
.field-text {
  width: 100%;
  height: 40px;
  padding: 2px 2px 6px;
  border: none;
  border-bottom: var(--lw-line) solid var(--line);
  border-radius: 0;
  background: transparent;
  font-family: var(--font-body);
  font-size: 16px;
  color: var(--ink);
  letter-spacing: .02em;
}
.field-text:focus-visible {
  outline: none;
  border-bottom-color: var(--ink);
  box-shadow: 0 1px 0 0 var(--ink); /* 聚焦=底线加重成 2px 墨线 */
}
.field-text[data-invalid="true"] {
  border-bottom-color: var(--red-text);
  box-shadow: 0 1px 0 0 var(--red-text);
}
/* 密码「显示」纯文字切换，禁图标 */
.field-toggle {
  align-self: flex-end;
  font-family: var(--font-body);
  font-size: var(--fs-body-sm);
  color: var(--indigo);
  text-decoration: underline;
  text-decoration-style: dotted;
  text-underline-offset: 2px;
}
.field-hint {
  font-family: var(--font-body);
  font-size: var(--fs-caption);
  color: var(--ink-3);
  margin-top: 2px;
}
.field-error {
  font-family: var(--font-body);
  font-size: var(--fs-caption);
  color: var(--red-text);
}
.book-card-foot {
  margin-top: var(--s1); /* R-018 收紧,给注册卡脚让出完整可见空间 */
  padding-top: var(--s1);
  border-top: var(--lw-line) dashed var(--line);
  text-align: right;
  font-family: var(--font-body);
  font-size: var(--fs-body-sm);
  color: var(--ink-3);
}
/* R-041(owner 20260723二次截图令,T4)：登录卡「进入」按钮下方一道虚线（owner
   截图3红框点名，"这两个虚线有点多余，删"）——只加在登录卡脚注一处
   （ui-pages.js buildLoginCardBody 的 foot），去掉分隔线，间距保留不变；
   注册/强改密两张卡脚注未点名，沿用 .book-card-foot 基类原样不动。 */
.book-card-foot--noline { border-top: none; }
.book-link { color: var(--indigo); text-decoration: underline; text-decoration-style: dotted; text-underline-offset: 2px; }
.book-submit { width: 100%; margin-top: var(--s3); } /* 原 s4 */
/* 登录/注册卡自身的分段虚线（.ticket-divider 用在起/止两组、确认键前等多处
   全站场景，不便改全局值）：正方形卡内收紧一档，s4→s3。 */
.book-card .ticket-divider { margin: var(--s2) 0; }


/* ============================================================================
   17. 我的页「度牒 · 题名录 · 年谱」三件套（V4 整页重排，SPEC §7 R-013③，
   替代 V3「功候」墙横条+进度条体系 gongho-* 整体下架；V4.2 §7 R-014⑦ 增量：
   度牒 编号/入册日/称号 三字段下架换新字段表，年谱收尾行改「今 · 入册第 X 日」）

   ① 度牒身份票 DOM 契约（唯一加宽票据卡，V4.2 新字段表全真数据）：
   <div class="ticket dudie">
     <div class="ticket-body">
       <p class="dudie-name">沈知远</p>
       <div class="dudie-fields">
         <div class="fieldrow"><span class="fieldrow-label">累计总时长</span><span class="fieldrow-value num">166 时 48 分</span></div>
         <div class="fieldrow"><span class="fieldrow-label">共录</span><span class="fieldrow-value num">312 笔</span></div>
         <div class="fieldrow"><span class="fieldrow-label">日均练功</span><span class="fieldrow-value num">1 时 12 分</span></div>
         <div class="fieldrow"><span class="fieldrow-label">最常练功时辰</span><span class="fieldrow-value">卯时</span></div>
         <div class="fieldrow"><span class="fieldrow-label">单笔最长</span><span class="fieldrow-value num">2 时 30 分</span></div>
       </div>
     </div>
   </div>

   ② 功候题名录 DOM 契约（字级放榜，两栏桌面并排/移动堆叠）：
   <div class="tag-rule"><span class="tag-rule-label">功候 —— 修习里程</span>...</div>
   <section class="timinglu">
     <div class="timinglu-col">
       <p class="fieldrow-label">累计功时</p>
       <div class="timinglu-row" data-reached="true">
         <span class="timinglu-name" data-tier="0">百时</span>
         <span class="timinglu-sub num">2026-05-01</span>
       </div>
       <div class="timinglu-row" data-reached="false">
         <span class="timinglu-name timinglu-name--hollow" data-tier="1">千时</span>
         <span class="timinglu-sub num">166 时 / 1000 时</span>
       </div>
     </div>
     <div class="timinglu-col">…连续功日，同构…</div>
   </section>

   ③ 年谱 DOM 契约（左乌丝竖线时间轴；V4.2 §7 R-014⑦：收尾行改「今 · 入册第
   X 日」——累计时长/最长连续/共录笔数/最常起于四项汇总数字已上移度牒身份票，
   此处不再重复）：
   <div class="tag-rule"><span class="tag-rule-label">历史纪录 —— 年谱</span>...</div>
   <ol class="nianpu-timeline">
     <li class="nianpu-row"><span class="nianpu-date num">2026-05-26</span><span class="nianpu-event">入册</span></li>
     <li class="nianpu-row nianpu-row--summary"><span class="nianpu-summary">今 · 入册第 57 日</span></li>
   </ol>

   ④ 设置降级 DOM 契约（去票据框纯文字行）：
   <div class="me-settings">
     <a class="me-settings-link" href="#/admin">管理后台</a>
     <button class="me-settings-link">退出登录</button>
   </div>
   ========================================================================== */

/* ---- ①度牒身份票 ---- */
.dudie-name {
  font-size: 19px; /* SPEC 范围 18-20px */
  font-weight: 600;
  color: var(--ink);
}
.dudie-fields {
  margin-top: var(--s3);
}

/* ---- ②功候题名录：字级放榜，已达成=实心深朱+下缀达成日期，未达成=同字号
   空心轮廓字+下缀「当前/目标」——字级两态本身即进度表达，删除墨位进度条体系 ---- */
.timinglu {
  display: flex;
  flex-direction: column;
  gap: var(--s5);
}
.timinglu-row {
  padding: var(--s3) 0;
}
.timinglu-row + .timinglu-row {
  border-top: var(--lw-hair) dashed var(--line);
}
.timinglu-name {
  display: block;
  font-family: var(--font-display);
  font-weight: 600;
  line-height: 1.1;
  color: var(--red-text);
}
/* 未达成=空心轮廓字：Blink/WebKit 走 -webkit-text-stroke 描边+透明填色，
   非 Blink 兜底=淡墨实色（不支持该属性时 color 保持不透明的 --ink-2）。
   二轮盲审 P2 复调:深色粗描边在齐伋体繁笔画上视觉≈实心灰——改「细+浅」
   (1px --ink-3)才有真「刻而未填」的镂空感;识别度靠字号与下缀进度小字兜底。 */
.timinglu-name--hollow {
  color: var(--ink-2);
  -webkit-text-stroke: 1px var(--ink-3);
}
@supports (-webkit-text-stroke: 1px black) {
  .timinglu-name--hollow { color: transparent; }
}
.timinglu-sub {
  display: block;
  margin-top: 4px;
  font-size: var(--fs-caption);
  color: var(--ink-3);
}
/* 字级递增梯：累计功时(5 档)/连续功日(4 档，共用同一把梯的前四级) 按
   data-tier 0-4 递增，移动端每级比桌面矮一档（SPEC「约 20→26→32→38→44px
   移动降档」） */
.timinglu-name[data-tier="0"] { font-size: 16px; }
.timinglu-name[data-tier="1"] { font-size: 20px; }
.timinglu-name[data-tier="2"] { font-size: 26px; }
.timinglu-name[data-tier="3"] { font-size: 32px; }
.timinglu-name[data-tier="4"] { font-size: 38px; }
@media (min-width: 768px) {
  .timinglu { flex-direction: row; gap: var(--s6); }
  .timinglu-col { flex: 1; min-width: 0; }
  .timinglu-name[data-tier="0"] { font-size: 20px; }
  .timinglu-name[data-tier="1"] { font-size: 26px; }
  .timinglu-name[data-tier="2"] { font-size: 32px; }
  .timinglu-name[data-tier="3"] { font-size: 38px; }
  .timinglu-name[data-tier="4"] { font-size: 44px; }
}

/* ---- ③年谱：左侧乌丝竖线时间轴，逐行 MonoNum 日期 + 文楷事件 ---- */
.nianpu-timeline {
  position: relative;
  margin: 0;
  padding-left: var(--s5);
  border-left: var(--lw-hair) solid var(--line);
  display: flex;
  flex-direction: column;
  gap: var(--s3);
}
.nianpu-row {
  position: relative;
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--s3);
}
.nianpu-row::before {
  content: "";
  position: absolute;
  left: calc(var(--s5) * -1 - 1px);
  top: .5em;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--ink-3);
}
.nianpu-date {
  flex-shrink: 0;
  color: var(--ink-2);
  font-size: var(--fs-fieldvalue);
}
.nianpu-event {
  color: var(--ink);
  font-family: var(--font-body);
}
.nianpu-row--summary {
  margin-top: var(--s2);
  padding-top: var(--s3);
  border-top: var(--lw-hair) dashed var(--line);
}
.nianpu-row--summary::before { display: none; }
.nianpu-summary {
  color: var(--ink-2);
  font-size: var(--fs-body-sm);
}

/* ---- ④设置降级：去票据框纯文字行，分量主动调低 ---- */
.me-settings {
  margin-top: var(--s6);
  padding-top: var(--s3);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--s3);
  border-top: var(--lw-hair) dashed var(--line);
}
.me-settings-link {
  font-family: var(--font-body);
  font-size: var(--fs-body-sm);
  color: var(--ink-3);
  text-decoration: underline;
  text-decoration-style: dotted;
  text-underline-offset: 3px;
  transition: color var(--t-fast) var(--ease-out);
}
.me-settings-link:hover { color: var(--ink-2); }


/* ============================================================================
   18. 牌记 colophon（SPEC V3 §13 第 6 条：管理页/我的页底部一行刊记体例小字，
   仅元信息区，数据页禁用）
   ========================================================================== */
.colophon {
  margin-top: var(--s6);
  padding-top: var(--s3);
  border-top: var(--lw-hair) dashed var(--line);
  text-align: center;
  font-family: var(--font-body);
  font-size: var(--fs-caption);
  letter-spacing: var(--ls-wide);
  color: var(--ink-3);
}


/* ============================================================================
   19. CC 式滚动条 + .cc-scroll 静止隐/交互显（R-019③，W13a 新增）
   配方来源：VS Code 官方设置页(10px 细滚动条) + Notion(10px)双印证，本站按
   8px 收紧一档（票据本位版式更密，8px 与 --lw-thick/--r-ctl 等小尺度更协调）。

   两层策略：
   ① 全站默认（未挂 .cc-scroll 的普通溢出容器，含 body/window 主滚动）：
      细滚动条常驻可见（8px，胶囊 thumb，track 透明），不做「静止隐藏」——
      这是「有滚动条但克制」的默认基线，不等于②。
   ② .cc-scroll 契约类（W13b/W13c 挂到需要"静止极净"的特定容器，如记一笔
      面板当日笔录列表/打卡历详情浮层等 Claude Code 设置页式局部滚动区）：
      静止态完全透明不可见，仅 hover 才显影滑块；同时叠顶部渐隐遮罩
      （内容滑入时淡出，呼应「下滑后上部虚化」，R-019④）。
      注：纯 CSS 无法感知"正在滚动中"这一状态（无对应伪类），显影只按
      hover 触发，滚动瞬间但未 hover 的边缘情形（如触屏滑动/触控板）不在
      本轮覆盖范围内，留作集成窗如需要再补 JS scroll 监听切 class。
   ========================================================================== */

/* ---- ① 全站默认：细滚动条常驻可见 ---- */
*::-webkit-scrollbar {
  width: 8px;
  height: 8px;
}
*::-webkit-scrollbar-track {
  background: transparent;
}
*::-webkit-scrollbar-thumb {
  background-color: rgba(var(--line-rgb), .55);
  border-radius: 4px;
  border: 2px solid transparent; /* 配 background-clip 造 4px 视觉细条，实际热区仍 8px */
  background-clip: padding-box;
}
*::-webkit-scrollbar-thumb:hover {
  background-color: rgba(var(--line-rgb), .8);
}
* {
  scrollbar-width: thin;
  scrollbar-color: rgba(var(--line-rgb), .55) transparent;
}

/* ---- ② .cc-scroll：静止极净，仅 hover 显影滑块 ---- */
.cc-scroll {
  scrollbar-color: transparent transparent; /* Firefox 侧静止态完全透明 */
  /* R-020①：本 .cc-scroll 语汇的上下渐隐 mask 遮罩已删——owner 裁「没必要模糊」，
     列表首末条曾被淡出裁切（此教训见下方 R-032②注：成就清单另起的滚动感知
     渐变遮罩系新增实现，非本条复活，不共用同一套常驻假设）。 */
}
.cc-scroll::-webkit-scrollbar-thumb {
  background-color: transparent; /* webkit 侧静止态默认透明 */
}
.cc-scroll:hover {
  scrollbar-color: rgba(var(--line-rgb), .55) transparent;
}
.cc-scroll:hover::-webkit-scrollbar-thumb {
  background-color: rgba(var(--line-rgb), .55);
}
.cc-scroll:hover::-webkit-scrollbar-thumb:hover {
  background-color: rgba(var(--line-rgb), .8);
}

/* ============================================================================
   20. 成就清单复选行（CARD-F5，R-026）：站内首次出现「多行可勾选列表」需求，
   既有 .fieldrow/.ticket-row 均是单行 label+value，无法复用；极小增量=零自制
   方块，原生 checkbox 只接 accent-color 令牌；条目文字走默认正文墨色（不用
   .field-hint 的弱文本档——20 条是要读的正文，非提示语）。声明行「以上属实」
   复用同一行骨架，页面层自行把文字 span 改挂既有 .field-hint class 降视觉
   一档，不新增 class。清单容器叠 .cc-scroll（静止极净滚动条语汇）+ 本节
   .achv-list 给出的定高，超出部分内滚，与记一笔面板「当日记录」同语汇。
   ========================================================================== */
/* R-032②(20260723,owner 截图三张明令)：list 滚动感知的顶/底渐变遮罩重开——
   吸取 R-020①「常驻遮罩把首末条永久裁切遮挡」的教训，本轮遮罩绝非常驻：
   .achv-fade-top 仅当 scrollTop>0（已下滑）才显示，回到顶隐藏；.achv-fade-bottom
   仅当"未滚到底"才显示，滚到底隐藏——两侧独立由 JS scroll 监听切 .achv-fade--visible
   class（见 web/src/ui-pages.js buildAchvListMount），CSS 只管渐变外观与
   opacity 过渡，不接手"何时显示"的判断。.achv-list-wrap 是新增的定位宿主
   （.achv-list 本身仍是滚动容器，宿主只负责让两片遮罩 position:absolute 贴在
   滚动容器的可视顶/底边缘，随内容一起裁在同一视口内，不随内容滚走）。
   渐变色用 --paper-2（浮层卡底色，票面色，R7 令牌表），不用白色——遮罩边缘
   与卡片底色融为一体，不留可见接缝。pointer-events:none 保证遮罩纯视觉，
   不挡底下清单行的点击/勾选。 */
.achv-list-wrap {
  position: relative;
}
.achv-fade {
  position: absolute;
  left: 0;
  right: 0;
  height: 28px;
  pointer-events: none;
  opacity: 0;
  transition: opacity var(--t-page) var(--ease-out);
}
.achv-fade-top {
  top: 0;
  background: linear-gradient(to bottom, rgba(var(--paper-2-rgb), 1), rgba(var(--paper-2-rgb), 0));
}
.achv-fade-bottom {
  bottom: 0;
  background: linear-gradient(to top, rgba(var(--paper-2-rgb), 1), rgba(var(--paper-2-rgb), 0));
}
.achv-fade--visible {
  opacity: 1;
}
.achv-list {
  max-height: 46vh;
  overflow-y: auto;
  margin-top: var(--s3);
}
.achv-row {
  display: flex;
  align-items: flex-start;
  gap: var(--s3);
  padding: var(--s2) 0;
}
.achv-row + .achv-row { border-top: var(--lw-hair) dashed var(--line); }
.achv-row input[type="checkbox"] {
  accent-color: var(--ink);
  width: 16px;
  height: 16px;
  flex: none;
  margin-top: 3px;
}
.achv-row-text {
  font-family: var(--font-body);
  font-size: var(--fs-body-sm);
  color: var(--ink);
  line-height: var(--lh-tight);
}
/* R-039(owner 20260723,管理页明细浮层改纯列表)：未达成条目弱化，与已达成
   （默认 --ink 正墨）/近 7 天达成（.achv-recent 朱红）拉开三档，让"直接列
   出来"的纯文字清单仍能一眼看出勾选状态，不依赖 checkbox 图形。 */
.achv-row-text--muted {
  color: var(--ink-3);
}
/* 管理页明细浮层练功类/生活类分段线（owner 原话"画一个横线"，不带文字标签）。
   grid-column:1/-1 只在 .achv-card 桌面双列网格（下方 §「成就清单浮层双列」）
   内生效——迫使分隔线独占一整行，同时把练功类最后一条(id 11，单数)自然挤到
   独占一行，视觉上恰好与生活类分组隔开；窄屏 .achv-list 是普通块级容器，该
   属性无宿主可依附，天然是普通一整行，不需要另写规则。线宽用 --lw-line
   （题签分隔线同一量级，比 .achv-row 之间的 --lw-hair 虚线更重，读作"分类
   边界"而非"逐条分隔"）。 */
.achv-list-divider {
  grid-column: 1 / -1;
  border-top: var(--lw-line) solid var(--line);
  margin: var(--s2) 0;
}

/* ---- 成就清单浮层双列(CARD-G3W15,R-037③,owner:"现在的清单是竖长的一列
   不好看,改成两列,也就是横向要加宽")：showAchievementsOverlay(本人可编辑)
   与 showAchievementsDetailOverlay(只读明细)共用同一个 buildAchvOverlayShell
   骨架+buildAchvListMount 清单，改动只加两个新 class，不碰其余三处复用
   buildAchvOverlayShell 的场景(showInfoOverlay/showConfirmOverlay 用各自的
   .book-auth-mask 骨架，不受影响；已确认站内仅这两个成就浮层调用
   buildAchvOverlayShell)。

   .achv-card：加在 shell 的 .book-card 上(第二个 class，不改 .book-card 本身
   的 320px 基线，避免连带影响同样裸用 .book-card 的 showInfoOverlay/
   showConfirmOverlay 两处一直很短、从未需要加宽的浮层)。桌面档(768px 断点，
   沿用本项目既有断点惯例)加宽到 640px——依据：实测 21 条现行文案(产品文档/
   成就清单/成就清单-现行21条v2-R028-20260723.txt)最长一条"熟练极光法，可7脉
   轮同时共振"13 字，其余多在 5-10 字；13 字在 --fs-body-sm(13px)下约 175px，
   加 checkbox+gap 约 28px，单列内容约 203px；640px 减卡内边距(--s5×2=48px)
   减列间距(--s6=32px)再对半，单列净宽约 280px，对最长文案留出约 77px 余量，
   不会贴边换行。窄屏(<768px)维持 .book-card 原有 320px 基线+单列，不新增
   断点表述。
   .achv-actions：仅 showAchievementsOverlay(有"确认"+"稍后再填"两个按钮)用到
   ——卡片加宽后两个按钮仍各自 100% 宽堆叠会显得与新宽度不协调(工单原话
   "不能列表变宽了但底部按钮还是窄modal的尺寸")，桌面档改并排；
   showAchievementsDetailOverlay 只有一个"知道了"按钮，单按钮撑满宽度是常见
   正常形态，不视为不协调，不额外处理。 ---- */
@media (min-width: 768px) {
  .achv-card { max-width: 640px; }
  .achv-card .achv-list {
    display: grid;
    grid-template-columns: 1fr 1fr;
    column-gap: var(--s6);
  }
  /* 双列后第一视觉行是第 1、2 两个 DOM 子项(grid 默认逐行填充，先填满第一行
     再换行)，原 .achv-row + .achv-row 的虚线顶边界(见上方)会连第 2 项也画上
     (它在 DOM 序里紧跟第 1 项之后)，但视觉上第 1、2 项同处第一行、上方均无
     更早内容——只有第 1 项因 :first-child 被上面的 + 选择器天然排除，第 2 项
     漏排除，会出现"同一行左边无线右边有线"的不对称。按"首视觉行两项都不画
     顶边界"显式补上。 */
  .achv-card .achv-row:nth-child(-n+2) { border-top: none; }
  /* R-041(owner 20260723二次截图令,T6,截图4红框)：分类横线(.achv-list-divider,
     grid-column:1/-1，见上方§)正下方、右列第一条上方出现一小段孤立虚线——
     根因与上面 nth-child(-n+2) 那条是同一类不对称，只是发生在"分隔线之后"
     而非"清单开头"：分隔线强制换行，紧跟它的第一项(左列)因上一个 DOM 兄弟
     是 .achv-list-divider 而非 .achv-row，天然不被 .achv-row + .achv-row 选中，
     没有顶边线；但第二项(右列)的上一个兄弟是"分隔线之后的第一项"(一个真
     .achv-row)，会被该选择器命中，画出一条只在右列出现的孤立虚线。
     修复：显式对"紧跟分隔线之后的第二个 .achv-row"去掉顶边线，用兄弟选择器
     定位（不依赖固定第 N 个 DOM 位置——divider 出现在哪，这条规则就近旁生效，
     天然适配"过滤后清单不定长"的动态序列，也天然支持未来出现多道分隔线的
     场景，每道分隔线各自的正下方两项都会被独立命中）。窄屏(<768px)不进本
     media query：单列布局下分隔线后第一项本就因同一 DOM 兄弟关系天然无顶边
     线，不需要、也不应该额外去掉第二项的顶边线（那是同一分组内正常的逐条
     分隔，若也去掉反而会漏画）。 */
  .achv-card .achv-list-divider + .achv-row + .achv-row { border-top: none; }
  .achv-card .achv-actions {
    display: flex;
    gap: var(--s3);
    margin-top: var(--s3);
  }
  .achv-card .achv-actions .book-submit {
    width: auto;
    flex: 1;
    margin-top: 0;
  }
}
/* R-042(owner 20260724三次截图令):"成就清单做成三列，这样就能完整展示"——
   在独立的更宽断点新增三列档，不直接把上面 768px 档改成三列，理由是 768px
   视口宽度本身容不下三列还留白（算法见下方），这里复用本项目 .disciple-grid
   (§20) 已验收的同款"两档断点分层加列"策略(768px 两列/1024px 三列)，不是
   另创一套新逻辑。
   .achv-card 宽度重算(同上方 640px 那条注释的口径，最长文案13字场景不变)：
   三列每列净宽目标 ≈230px(单列最短净宽 203px 之上留约13%余量，同一量级的
   留白比例)；800px 卡宽 - 卡内边距(--s5×2=48px) - 两道列间距(--s6×2=64px)
   =688px，三等分 ≈229px，达标。1024px 断点下 800px 卡宽 + 浮层左右内边距
   (--s4×2=32px)=832px，距 1024px 视口仍留 192px 余量，不会在断点临界值触发
   横向溢出（640px 档在 768px 断点做过同款验证，见上方注释）。
   下面两条边框抑制规则是 768px 档同款规则的三列版本，写法与上方 R-041 T6
   注释同一套推理："-n+2"→"-n+3"（首视觉行三项都不画顶边界）；分隔线后需要
   抑制的项从 1 个(右列)扩到 2 个(中列+右列)，故追加一条三层兄弟选择器命中
   分隔线后第三项。两档规则同时存在不冲突——768px 档的 -n+2/单层兄弟选择器
   命中的项是本档 -n+3/多层兄弟选择器命中范围的真子集，且都是同一个值
   (border-top:none)，非覆盖关系，是并集叠加。 */
@media (min-width: 1024px) {
  .achv-card { max-width: 800px; }
  .achv-card .achv-list { grid-template-columns: 1fr 1fr 1fr; }
  .achv-card .achv-row:nth-child(-n+3) { border-top: none; }
  .achv-card .achv-list-divider + .achv-row + .achv-row + .achv-row { border-top: none; }
}


/* ============================================================================
   21. R-028 弟子列表卡片（CARD-G3W6，取代原「成员列表」.ticket-row 单行体例）：
   每位弟子一张 .ticket 卡（.disciple-card 仅补卡间纵向节奏，卡片本体外观完全
   复用既有 .ticket），内部头行（姓名+状态元信息+操作按钮组）与既有 .fieldrow
   四件套/成就行之间用既有 .ticket-divider 分隔，零新增结构类语言。
   ========================================================================== */
.disciple-card + .disciple-card { margin-top: var(--s3); }

/* .disciple-achv-row/.disciple-achv-row .fieldrow-value（原内联成就文案清单
   字段行的换行支持）随 CARD-G3W15(R-037①)整体下架——该字段行本身已被移除，
   改图标+紧凑计数，见下方 §26；不留死 class。 */

/* 成就条目「近 7 天内首次勾选」高亮：唯一新增颜色类，值仍是全站「新达成」
   合法朱色令牌 --red-text（R-019 色彩四色封顶纪律未破例，只是多一个使用
   场景：弟子列表新达成条目，与「今日之数/榜首」同一朱色语义"新/当下"）。 */
.achv-recent {
  color: var(--red-text);
}


/* ============================================================================
   22. R-028⑥ 我的/管理两页正文与标题字号 +15%（页面范围限定，页首巨字页标
   --fs-folio-d/--fs-folio-m 两个变量刻意不在覆写清单内，故不受影响；masthead/
   toc/tab-paper 属于 shell 级共享结构，不在 .page 容器内，天然不受牵连）。
   实现=CSS 变量覆写（限定在 .page.page-scale-lg 作用域内），15% 后数值向
   0.05px 舍入；两处硬编码 px（.dudie-name 与 .timinglu-name 五档字号）同比例
   覆写，其余站内组件均通过 --fs-* 变量间接受益，零回归到其它页面。
   ========================================================================== */
.page.page-scale-lg {
  --fs-body:          16.1px;  /* 14px  *1.15 */
  --fs-sub:           18.4px;  /* 16px  *1.15 */
  --fs-body-sm:       14.95px; /* 13px  *1.15 */
  --fs-caption:       13.8px;  /* 12px  *1.15 */
  --fs-tagrule-label: 14.95px; /* 13px  *1.15 */
  --fs-tagrule-meta:  13.8px;  /* 12px  *1.15 */
  --fs-fieldlabel:    12.65px; /* 11px  *1.15 */
  --fs-fieldvalue:    17.25px; /* 15px  *1.15 */
  --fs-ticketfoot:    12.65px; /* 11px  *1.15 */
  /* --fs-folio-note 覆写行已随 R-033②(CARD-G3W13)移除：.folio-note 改为四页
     统一硬编码 13.8px(见 §3 .folio-note 注释)，不再经这条变量覆写间接生效，
     留在这里会变成一条不影响任何选择器的死配置，故删除，避免误导后来者。 */
}
.page.page-scale-lg .dudie-name { font-size: 21.85px; } /* 19px * 1.15 */
.page.page-scale-lg .timinglu-name[data-tier="0"] { font-size: 18.4px; } /* 16*1.15 */
.page.page-scale-lg .timinglu-name[data-tier="1"] { font-size: 23px; }   /* 20*1.15 */
.page.page-scale-lg .timinglu-name[data-tier="2"] { font-size: 29.9px; } /* 26*1.15 */
.page.page-scale-lg .timinglu-name[data-tier="3"] { font-size: 36.8px; } /* 32*1.15 */
.page.page-scale-lg .timinglu-name[data-tier="4"] { font-size: 43.7px; } /* 38*1.15 */
@media (min-width: 768px) {
  .page.page-scale-lg .timinglu-name[data-tier="0"] { font-size: 23px; }   /* 20*1.15 */
  .page.page-scale-lg .timinglu-name[data-tier="1"] { font-size: 29.9px; } /* 26*1.15 */
  .page.page-scale-lg .timinglu-name[data-tier="2"] { font-size: 36.8px; } /* 32*1.15 */
  .page.page-scale-lg .timinglu-name[data-tier="3"] { font-size: 43.7px; } /* 38*1.15 */
  .page.page-scale-lg .timinglu-name[data-tier="4"] { font-size: 50.6px; } /* 44*1.15 */
}


/* ============================================================================
   CARD-G3W10 移动端辅助字号 11px→12px（F-P2-01/02，依据 G3W9 回执 §3；
   F-P2-03 打卡历干支单字装饰 8px 已裁候豁免，本卡不动）。
   --fs-masthead 全站仅 masthead-inner/.masthead-switch 两处消费（报头日期/
   时辰/秒钟/成员名/退出登录文案）；--fs-fieldlabel 全站仅 .fieldrow-label/
   .entry-field-label 两处消费（登录注册字段标题/记一笔起止大概练了当日记录/
   今日页数据行本周连续本周日均本月等标题）——均为 F-P2 清单里点名的目标，
   变量改值零株连其它字号。桌面 ≥768px 不落这条媒体查询，原样保持 11px，
   与工单「仅移动档」要求一致；.page.page-scale-lg 的 +15% 覆写（见上方
   §22，12.65px）class 选择器特异性更高，两档媒体查询同时命中时该覆写
   仍会照常生效，互不冲突。 */
@media (max-width: 767px) {
  :root {
    --fs-masthead: 12px;
    --fs-fieldlabel: 12px;
  }
}

/* ============================================================================
   24. 成就浮层复选行移动档触达(对抗复审 VERDICT-RWD P1-2,G3 亲执,20260723)
   行=label 整行可点(真实触达目标),但 13px 文 + 8px 纵 padding 单行高约 33px,
   低于 44px 移动触达基线。移动档纵 padding 提至 14px(单行高约 45px)。
   内部 16px 复选框本身被 C 类扫描按元素口径命中属"伪命中"(点整行即可勾选,
   无需精确点中方框),已在专项报告口径节说明,不放大方框(视觉语言不动)。
   ========================================================================== */
@media (max-width: 767px) {
  .achv-row { padding: 14px 0; }
}

/* ============================================================================
   25. 弟子列表栅格(R-035②,20260723):管理视角=电脑浏览器,横向空间要吃满——
   桌面一行三卡;平板两卡;手机一卡(主编排默认裁定候翻案:窄屏塞三列会挤爆,
   自适应专项 96 格门不许回退)。卡内 fieldrow/成就行自适应列宽,不另调。

   align-items 由 start 改 stretch(CARD-G3W15,R-037①「确保每个弟子卡片尺寸
   一致」)：此前卡内还有内联成就文案清单(逗号分隔最多21条)这一巨大高度变量，
   同行三卡自然高度可能相差几倍，此时若用默认 stretch 会让内容少的卡片底部
   出现一大截突兀空白，start(各卡按自身内容高度顶对齐)反而是当时更不难看的
   选择。本卡①把该内联清单整体移除、换成右上角小图标+紧凑计数(见下方 §26)，
   卡内剩余内容(头行+四个定长 fieldrow)天然同构，不再有巨幅高度变量来源——
   改回 stretch 让同行卡片严格拉伸对齐(不足内容的卡片仅底部留少量呼吸空白，
   而非依赖"新内容恰好高度一致"这种未经断言的巧合)，直接达成 owner 原话
   "确保每个弟子卡片尺寸是一致的"，比停留在 start 更稳妥。实测截图见回执。 */
.disciple-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--s4);
  align-items: stretch;
}
@media (min-width: 768px) {
  .disciple-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 1024px) {
  .disciple-grid { grid-template-columns: repeat(3, 1fr); }
}
/* 栅格接管卡间距后,§20 的相邻卡 margin-top 在网格内必须归零(防 gap+margin 双重间距) */
.disciple-grid .disciple-card + .disciple-card { margin-top: 0; }

/* ============================================================================
   26. 弟子卡图标化操作(CARD-G3W15,R-037②，owner:"单独做一个成就的图标放到
   右上角,另外要把'重置密码 停用 设为/取消管理员'也做成一个图标入口放到右
   上角(点击图标后下拉选项)")。

   独立盲测面板 20260723 通报的真实 P0（记档供后来者理解本节为何这样设计，
   非本卡引入的缺陷——是本卡要连根清除的既有缺陷）：改版前"重置密码/成就/
   停用/设为管理员"四个 .day-ticket-action 文字链接横排，在 1024-1920px 桌面
   宽度下会 flex-wrap 换行；叠加 F-P1-06 给 .day-ticket-action 做触达热区扩容
   用的负 margin 技巧（首/尾项分别 margin-left:-27px/margin-right:-27px 撑出
   比可见文字大得多的不可见可点区域），换行后相邻行之间这些不可见区域会几何
   重叠，真机坐实"点'重置密码'实际命中'确认设为管理员'"。本节两个新控件的
   根治办法：①下拉菜单项(.disciple-actions-menu-item)改用真实 padding 撑满
   44px 触达高度，不再用负 margin 扩容技巧；②菜单项纵向堆叠单列布局，同一
   时刻只有一条可点目标位于任一 Y 坐标，结构上不可能出现同行多目标几何重叠
   ——不是给旧技巧打补丁，是整类问题随交互形态改变一并消失。真实坐标点击
   验证结果见回执"P0 命中验证"专节。
   ========================================================================== */
/* ---- 弟子卡头行两栏骨架(R-039,owner 20260723,截图三张点名卡片结构不一致)：
   header 本身不换行——identityWrap(姓名+meta)与 iconsWrap(R-041起只剩操作菜单
   一项，成就徽标已下架见下方 §)各自独占一个 flex 子项，图标组因而永远停在
   头行第一行最右侧，不随姓名长短或 meta 有无被挤到第二行(旧版三者同层
   flex-wrap 正是结构不一致的根因，见 ui-pages.js paintAdmin 头行构造处头注)。
   identityWrap 自己 flex-wrap，姓名超长只在自己范围内折行，不牵连 iconsWrap
   位置。 ---- */
.disciple-card-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s3);
}
.disciple-identity {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--s3);
  min-width: 0;
  flex: 1 1 auto;
}
.disciple-name {
  /* R-041(T8,owner二次截图令):"弟子列表的名字字体要和页标题一致(和顶部
     '管理'二字字体一致)"——页标题走 --font-display(齐伋体)，姓名同款换字体
     族；字号/字重不跟页标题(那是巨字页标，套进卡片行内会失衡)，"一致"按
     owner原话字面理解为字体本身，不是整套排版参数照搬。QiJi 子集已扩到
     "GB2312一级字库∩QiJi实有字形"交集(见 web/tools/gen-charset.js
     QIJI_COMMON_HANZI 头注)，缺字自动回退文楷(font-family 链第二项)，不会
     出现方块字。 */
  font-family: var(--font-display);
  overflow-wrap: break-word;
  min-width: 0;
  /* R-042(owner 20260724三次截图令):"弟子列表名字都太小，起码增大100%"——
     实测当前值继承自 --fs-body(15px)，未有显式声明；改显式 calc(2倍) 而非
     裸写 30px，语义上钉住"精确翻倍"这个 owner 原话要求，--fs-body 未来若调，
     本行仍保持二倍关系不必回头改字面数值。 */
  font-size: calc(var(--fs-body) * 2);
}
.disciple-card-icons {
  display: flex;
  align-items: center;
  gap: var(--s2);
  flex-shrink: 0;
}

/* R-041(owner 20260723二次截图令,T7)：成就徽标(.disciple-achv-badge/
   .disciple-achv-badge-text，"成就 N/21"紧凑计数按钮及其"未填写"文案态)
   整块下架——owner 原话"取消数字化显示成就(不要出现成就xx/21)，只保留一个
   成就入口也就是三个点那"。唯一入口收敛到下方操作下拉菜单里的"成就"项，
   openAchvDetail 逻辑与 showAchievementsDetailOverlay 明细浮层内部展示不受
   影响。原两条规则已整体删除，不留死 class。 */

/* ---- 操作下拉：重置密码/成就/设为管理员或取消管理员(R-041起"停用/启用"
   两项已随本节一并下架，见 ui-pages.js paintAdmin 内 R-041 注释)，原四个
   横排文字链接收进一个图标按钮触发的下拉面板。.disciple-actions-anchor 是
   定位宿主(position:relative)，面板 position:absolute 挂在它下面——不是整屏
   overlay(那是 buildAchvOverlayShell 一类"独立小窗"的形态，本卡工单原话
   "下拉面板通常是绝对定位在图标下方而非整屏overlay，选合适的实现方式"，
   点外部关闭沿用同一套 e.target 判断精神但改用 document 级捕获点击+命中
   anchor 之外即关闭，实现见 ui-pages.js openMenu/onDocClick)。 ---- */
.disciple-actions-anchor {
  position: relative;
  flex-shrink: 0;
}
.disciple-actions-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border: var(--lw-line) solid var(--line);
  border-radius: var(--r-ctl);
  background: transparent;
  color: var(--ink-2);
  transition: background var(--t-fast) var(--ease-out), color var(--t-fast) var(--ease-out);
}
.disciple-actions-toggle:hover,
.disciple-actions-toggle[aria-expanded="true"] {
  background: var(--paper-3);
  color: var(--ink);
}
.disciple-actions-toggle-icon {
  display: inline-flex;
  width: 16px;
  height: 16px;
}
.disciple-actions-toggle-icon svg { display: block; width: 100%; height: 100%; }
/* 面板：右对齐挂在图标下方(right:0 令面板右边界=图标右边界，向左侧展开)——
   桌面三栏栅格里卡片本身较窄时，向左展开比向右展开更不容易探出卡片右边界
   撞到相邻卡片(工单②不遮挡相邻卡片要求)；min-width 148px 在本页 +15%
   字号覆写(.page-scale-lg)下也够放下最长的"设为管理员"5 字项，不换行。
   浮层级语汇=--r-pop(菜单/弹窗/抽屉，tokens.css 原生定义此用途)+
   --shadow-float(唯一允许出现阴影的浮层场景，golden rule①"默认态零阴影,
   阴影只属于浮层")，不新造投影语言。z-index:5 保证面板画在同一行/下一行
   兄弟 .disciple-card 之上(卡片自身 .ticket 是 position:relative 但
   z-index:auto，不建立独立层叠上下文，本面板需要显式 z-index 才能稳定盖住
   兄弟卡片；数值远低于全站浮层层 50+，不会与 mask/overlay 抢层)。 */
.disciple-actions-menu {
  display: none;
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  z-index: 5;
  min-width: 148px;
  flex-direction: column;
  padding: var(--s1);
  background: var(--paper-2);
  border: var(--lw-thick) solid var(--line);
  border-radius: var(--r-pop);
  box-shadow: var(--shadow-float);
}
.disciple-actions-menu[data-open="true"] { display: flex; }
/* 触达热区=真实 padding(min-height 44px)撑出，非负 margin 扩容技巧——见本节
   头注"P0"说明，这是本节存在的核心理由。纵向堆叠单列，同一 Y 坐标同一时刻
   只有一个可点目标。 */
.disciple-actions-menu-item {
  display: flex;
  align-items: center;
  width: 100%;
  min-height: 44px;
  padding: var(--s2) var(--s3);
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--fs-body-sm);
  text-align: left;
  white-space: nowrap;
  transition: background var(--t-fast) var(--ease-out);
}
.disciple-actions-menu-item:hover { background: var(--paper-3); }

/* ============================================================================
   27. 「修改个人信息」/「修改密码」两小窗(CARD-G3W15，R-037③初版+R-038
   拆分)：复用 buildAchvOverlayShell 同一套独立小窗骨架(背景虚化+
   .book-auth-overlay+.book-card+点外部关闭)，不新起视觉语言，函数名沿用
   未改(见 ui-pages.js showEditProfileOverlay/showChangePasswordOverlay 两
   函数头注)。R-038 前是"一窗两区"(资料+密码共一个浮层)，R-038 后拆成两个
   各自独立的小窗，每个都更短(各自只有两字段+一按钮)，但仍共用这一个专属
   class——.book-card 基线无固定高度(不像 .book-auth-card 登录/注册那样
   定高+内层滚动)，拆分后单个小窗内容已经很短、正常视口下这条兜底基本不会
   触发，但矮视口(如横屏手机)仍有理论溢出风险，保留同一兜底比拆两个更细的
   class 简单，不为已经不太可能触发的场景过度拆分令牌。
   ========================================================================== */
.book-card.profile-edit-card {
  max-height: calc(100vh - var(--s4) * 2);
  overflow-y: auto;
}
