/* ============================================================
   core/base.css — 迁入自 core/base.html 的 <style> 块(A-539 搬迁)
   声明体逐字未改(纯搬迁;归并留给 A-541,见
   openspec/changes/wave11-lock-batch/conflicts.md)。
   ============================================================ */
    /* ============ A-515 · 令牌层(判据 = apple-ui skill「令牌」段)============
       深浅是两组各自选定的取值,不是同一组色自动翻转:整层在
       prefers-color-scheme: dark 下换掉,组件层一行不动。
       零外链:字体只用系统栈,颜色只用字面量,不引任何远端资源(S4)。 */
    /* 语义色的 ink 变体(下面根令牌块里的 --orange-ink / --red-ink): */
    /* 满饱和的 orange/red 在白卡面上只有 2 上下的对比度(实测 --orange 2.19:1、 */
    /* --red 3.55:1),给文字与细图元用即读不出来。故每个语义色配一枚 ink 级 */
    /* 取值:浅色为压暗后的同色相(≥4.5:1 于白卡面),暗色沿用现值(深底上满 */
    /* 饱和本就够亮,再调只会更刺眼)。--green-ink 早已是这个形态 */
    /* (#248A3D / #30D158),本批把 orange 与 red 补齐成同一套—— */
    /* 不是新增语义色,是同一语义的可读取值(一色一义不破)。 */
    /* 两条踩坑纪律(A-522 与 A-526 的契约测试实测踩到,后来者免重踩): */
    /* ① 说明写在声明块外——Django 注释的起始两字符里有一个左花括号, */
    /*   写在块内会被"手工配对花括号取声明块"那类断言当成一层嵌套,取样当场截断; */
    /* ② 说明里不写那个选择器的字面量——取样是按"第一次出现"定位的, */
    /*   注释里提一次就会把定位吸到注释上(本段就因此红过一次)。 */
    /* ============ 语言 v2 · 令牌换血(A-547/候选 126,P0 库刀)============
       图纸 = docs/research/language-v2-spec.md §1(唯一权威)+ 定案实物
       concept-v2-dashboard-r2.html 与 motion-lab.html。原则=**换血不换名**:
       v2 明暗对灌入既有令牌名(bg、card、hairline、text 族、accent 族),
       组件层与模板一字不动(值经变量间接引用,骨架天然继承新肤)。新增
       令牌(hairline-strong、pos、neg、display 三级、fs-label)只加不
       改名。原六检色板(dv 族)、地图六色(map 族,P5 另批)、语义三色族
       (green orange red gray 及 ink soft 对)、fill、track、group、
       shadow 三级、motion 三常数、r-card、r-ctl、content-w 均不在
       spec 一节换值清单内,原样保留(spec 没写的值不发明)。 */
    :root {
      color-scheme: light dark;
      --bg: #FAFAFB; --card: #FFFFFF; --group: #F9F9FB;
      /* --card 语义随 spec §1 由「卡面」扩为「surface」,浅色数值不变
         (#FFFFFF 恰好两义同值),暗色见下方两个暗组块。 */
      --hairline: #E6E8EB;
      /* --hairline-strong(新增,spec §1):v2 强调级发丝线(如 kpi-strip
         分栏竖线、range-list 零基线一类需要比普通 hairline 更显的骨架
         线),消费方随 §3 新件铺开时接入,本刀先立值。 */
      --hairline-strong: #D5D8DC;
      --fill: rgba(120,120,128,.12); --track: rgba(120,120,128,.2);
      --text: #1A1F27; --text-2: #5B6472; --text-3: #8B93A1;
      --accent: #3B66F5; --accent-soft: #EEF2FE;
      /* --pos/--neg(新增,spec §1):delta 行专属语义(kpi-delta 正/负向),
         与既有 green/red 状态语义色**故意不合流**——那两枚是 chip/matrix
         等"离散状态"的语义色,这两枚是"数值涨跌方向"的语义色,数值
         接近但概念不同(色板独立可各自演进,不因为撞色就合并语义)。 */
      --pos: #0E9F6E; --neg: #D03801;
      --green: #34C759; --green-soft: rgba(52,199,89,.12); --green-ink: #248A3D;
      /* map-batch 单 C:orange/red 补 soft 变体(黄牌 chip 的 10% 底,
         apple-ui「状态 chip 语义色 10% 底 + 全饱和字」;green/gray 早有) */
      --orange: #FF9500; --orange-soft: rgba(255,149,0,.14); --orange-ink: #C93400;
      --red: #FF3B30; --red-soft: rgba(255,59,48,.12); --red-ink: #D70015;
      --gray: #8E8E93; --gray-soft: rgba(142,142,147,.14); --gray-ink: #6C6C70;
      --shadow: 0 1px 3px rgba(0,0,0,.08);
      /* A-540:投影补到三级(沿原型 design/uiux-v2/index.html :root 逐值)——
         --shadow 卡面 / --shadow-up 底栏向上 / --shadow-float 浮层。 */
      --shadow-up: 0 -1px 3px rgba(0,0,0,.06);
      --shadow-float: 0 8px 28px rgba(0,0,0,.16);
      /* A-520:diverging 六检色板(apple-ui 图表规约,数值一字未改)。
         色板升为令牌是因为同一组色现在要给两种图元用:条形的 background
         与环形扇区的 fill——色值只在这一处(与暗色那一组),两种图元各取
         同一个变量,"别再抄第二份 hex"是靠没有第二份 hex 做到的。
         变量名刻意中性(--dv-1…5 = 极性轴档位序的五级、--dv-s1…s4 = 状态族
         灰阶):档位词本身不进全站样式层——本层每一屏都要下发,而
         A-505 的监票员屏禁止出现档位字样。档位码 → 变量的映射写在
         dashboard.html(组织者屏)那一处。
         无政党配色纪律:两翼为青绿↔紫,不碰任何加拿大主要政党标志色。 */
      --dv-1: #00845F; --dv-2: #2FBFA8; --dv-3: #8E8E93;
      --dv-4: #A98BE0; --dv-5: #6C3FC5;
      --dv-s1: #AEAEB2; --dv-s2: #8E8E93; --dv-s3: #636366; --dv-s4: #48484A;
      /* A-540:地图六色入令牌(取值逐字沿原型;#000 规整为本文件的六位形)。
         消费方是 map/style-canvass.json(经 map/theme-bridge.js 解析注入,
         MapLibre 不解析 CSS 变量)——风格 JSON 零硬编码色,色值只有这一处。
         street=路面 · block=街块/地面 · block-line=街块描边(兼建筑面,
         比地面深一档)· park=绿地(A-125 取用)· water=水体 · ink=图上文字
         (A-125 标注取用)。 */
      --map-street: #EDEDF2; --map-block: #E1E1E7; --map-block-line: #D2D2DA;
      --map-park: #DCE4DE; --map-water: #D9E1EA; --map-ink: #000000;
      --r-card: 14px; --r-ctl: 10px;
      --ui: -apple-system, "SF Pro Text", "PingFang SC", "Helvetica Neue", sans-serif;
      /* A-542:字号四级令牌(图纸 = ui-refactor-plan.md §1②实测:原型
         12/13/15/17px 覆盖 89% 使用,20–60px 大号各只出现 1 次)。命名取自
         原型/design-language.md §1 的 t-cap/t-foot/t-sub/t-body 词根(不
         新造);t-head 与 t-body 同为 17px、只差字重,共享本令牌。大号字
         (34/22px,「一次性主角」域)不设令牌——h1/h2 元素选择器已是唯一
         定义处(design.md 决策 2)。明暗同值,只在 :root 出现一次。 */
      --fs-cap: 12px; --fs-foot: 13px; --fs-sub: 15px; --fs-body: 17px;
      /* spec §2(新增展示域):display 三级(英雄数专用,620 字重字距 -1%
         由消费类自带,这里只给尺寸)+ --fs-label(11px,v2 最高频的大写
         标签系,500/字距 .06em 由消费类自带)。既有四级不动(数据语境
         正文=13/表单阅读语境=17,按组件声明不按屏);.t-large/.t-title2
         两枚「一次性主角」旧类与 h1/h2 一并保留(spec 未裁全站替换,
         h1 屏题降为顶条形态是消费屏结构级改动,归各组 v2 施工批)。
         明暗同值,只在 :root 出现一次(同 --fs-* 既有先例)。 */
      --fs-display-xl: 56px; --fs-display: 48px; --fs-display-sm: 40px;
      --fs-label: 11px;
      /* A-536(compose 单 A 前置):入场动效常数入令牌——全站唯一过渡时长/
         缓动/按压缩放(原型实证 .18s ease-out 与 scale(.985),wave9
         nonnegotiables §3「动效常数进令牌,屏内自定义时长 = fail」)。
         明暗同值,只在 :root 出现一次(--fs-* 同一先例);计数器与图表
         入场动画归单 B,本处只立常数与库内既有消费方(.btn/.seg)。 */
      --motion-duration: .18s; --motion-ease: ease-out; --motion-press: .985;
      --content-w: 36rem;      /* 屏可在自己的 extra_head 里改宽(表格屏用 48–60rem) */
    }
    /* ============ A-526 · 暗组令牌:一组值,两个挂点 ============ */
    /* 挂点①「系统深色,且用户没显式选浅」:`:not([data-theme=light])` 就是 */
    /* 「显式浅压过系统深」那条验收句的机制本身——显式浅色把整块排除掉, */
    /* :root 的浅组原封不动留在场上,一行 JS 都不需要。 */
    /* 挂点②「用户显式选深」:媒询管不着它,故另起一条同值规则。 */
    /* 两块逐值相等,由 test_a526_theme 的声明集比对钉死(改一处忘一处即红) */
    /* ——CSS 没有把"媒询"与"选择器"合成一条规则的写法,双份挂点不可避免, */
    /* 双份的风险因此交给机器守,不交给记性。 */
    @media (prefers-color-scheme: dark) {
      :root:not([data-theme="light"]) {
        /* 语言 v2 暗组换血(spec §1 暗值列;--card/--hairline/--text* 语义
           不变、数值随 v2 取代 iOS 系统暗色阶)。 */
        --bg: #0D0F12; --card: #15181D; --group: #2C2C2E;
        --hairline: #262B33;
        --hairline-strong: #333A44;
        --fill: rgba(120,120,128,.24); --track: rgba(120,120,128,.32);
        --text: #E8EAED; --text-2: #9AA3B0; --text-3: #6B7480;
        --accent: #6E8DFF; --accent-soft: #1C2440;
        --pos: #2FBF8B; --neg: #F0703A;
        --green: #30D158; --green-soft: rgba(48,209,88,.18); --green-ink: #30D158;
        --orange: #FF9F0A; --orange-soft: rgba(255,159,10,.2); --orange-ink: #FF9F0A;
        --red: #FF453A; --red-soft: rgba(255,69,58,.2); --red-ink: #FF453A;
        --gray: #98989D; --gray-soft: rgba(152,152,157,.2); --gray-ink: #98989D;
        --shadow: none;        /* 暗色不用阴影,层次交给表面色阶(skill 阴影段) */
        /* A-540 投影暗组:浮层在暗色仍要与底分离,故 float 保留且加深 */
        --shadow-up: none;
        --shadow-float: 0 8px 28px rgba(0,0,0,.6);
        /* 深浅是各自选定的步长,不是同一组色自动翻转(六检色板暗色列) */
        --dv-1: #007099; --dv-2: #27AD7F; --dv-3: #98989D;
        --dv-4: #9F7BE5; --dv-5: #6B3FCB;
        --dv-s1: #636366; --dv-s2: #8E8E93; --dv-s3: #AEAEB2; --dv-s4: #C7C7CC;
        /* A-540 地图六色暗组:独立选定的暗步长(沿原型),不是浅组反相 */
        --map-street: #2C2C2E; --map-block: #3A3A3C; --map-block-line: #48484A;
        --map-park: #33403A; --map-water: #2F3A45; --map-ink: #FFFFFF;
      }
    }
    /* 挂点②的声明与挂点①逐字相同(CSS 注释会原样送到浏览器,故本块的说明 */
    /* 一律走 Django 注释:上面那一份里的中文若在这里再抄一遍,「所选语言之外 */
    /* 的字面量不上屏」那道扫描就会当场判红——A-502 的实测坑)。 */
    /* color-scheme 只有显式档需要:原生控件与滚动条得跟着一起翻,不留一块浅色。 */
    :root[data-theme="dark"] {
      color-scheme: dark;
        --bg: #0D0F12; --card: #15181D; --group: #2C2C2E;
        --hairline: #262B33;
        --hairline-strong: #333A44;
        --fill: rgba(120,120,128,.24); --track: rgba(120,120,128,.32);
        --text: #E8EAED; --text-2: #9AA3B0; --text-3: #6B7480;
        --accent: #6E8DFF; --accent-soft: #1C2440;
        --pos: #2FBF8B; --neg: #F0703A;
        --green: #30D158; --green-soft: rgba(48,209,88,.18); --green-ink: #30D158;
        --orange: #FF9F0A; --orange-soft: rgba(255,159,10,.2); --orange-ink: #FF9F0A;
        --red: #FF453A; --red-soft: rgba(255,69,58,.2); --red-ink: #FF453A;
        --gray: #98989D; --gray-soft: rgba(152,152,157,.2); --gray-ink: #98989D;
        --shadow: none;
        --shadow-up: none;
        --shadow-float: 0 8px 28px rgba(0,0,0,.6);
        --dv-1: #007099; --dv-2: #27AD7F; --dv-3: #98989D;
        --dv-4: #9F7BE5; --dv-5: #6B3FCB;
        --dv-s1: #636366; --dv-s2: #8E8E93; --dv-s3: #AEAEB2; --dv-s4: #C7C7CC;
        --map-street: #2C2C2E; --map-block: #3A3A3C; --map-block-line: #48484A;
        --map-park: #33403A; --map-water: #2F3A45; --map-ink: #FFFFFF;
    }
    /* 显式浅:值本来就是 :root 那一组,这里只把原生控件也拨回浅色。 */
    :root[data-theme="light"] { color-scheme: light; }

    /* ================== 组件层:排版 ================== */
    *, *::before, *::after { box-sizing: border-box; }
    body { font-family: var(--ui); font-size: 17px; line-height: 1.5;
           margin: 0; padding: 0; background: var(--bg); color: var(--text);
           -webkit-text-size-adjust: 100%; }
    h1 { font-size: 34px; font-weight: 700; line-height: 1.15;
         letter-spacing: -.02em; margin: 4px 0 12px; }
    h2 { font-size: 22px; font-weight: 700; margin: 24px 0 8px; }
    h3, h4 { font-size: 17px; font-weight: 600; margin: 16px 0 4px; }
    p { margin: 8px 0; }
    strong { font-weight: 600; }
    /* [A-541 第三刀→map-batch 单 D 收口] muted → .t-foot 归并:第三刀
       257 处(A-543 锚先迁,commit 0f5939f)+ 本批(map-batch 单 D)
       routes_list.html Map 投影分支 8 处 + route_detail.html stage-map
       地图段 12 处,共 277 处。本规则**留而不废**:唯一残余消费方=
       base.html trace 页脚(两豁免屏 route_print.html/signs_list.html
       经继承共享此骨架,其 M1 冻结副本只有 .muted 无 .t-foot,改名即
       豁免屏页脚失样)——随豁免屏零外链裁决收口,非本批范围。 */
    .muted { color: var(--text-2); font-size: .85rem; }
    img, svg { max-width: 100%; }
    /* 宽内容各自滚,页面 body 永不横向溢出(M1) */
    .table-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; margin: 8px 0; }
    table { border-collapse: collapse; }
    a { color: var(--accent); text-decoration: none; }
    /* 触控目标 ≥44×44pt(令牌硬规):行内操作链接也算,故落在 a 本身 */
    a { display: inline-flex; align-items: center; min-height: 44px;
        vertical-align: middle; }
    summary { cursor: pointer; padding-block: 9px; min-height: 44px; }

    /* ================== 组件层:卡片 / 图标 ================== */
    /* [语言 v2 · 投影纪律,与 A-522 verified 冲突,已保守回退] spec §1
       「页面层禁影,骨架靠 1px 线」原意是卡片投影→hairline 边框**替代**,
       但 `test_a522_brand.py::test_light_cards_carry_a_real_shadow_
       from_the_token` 是 A-522(verified)的机器化验收句,逐字断言
       `.card` 必须带 `box-shadow: var(--shadow)`(该条 observable 原文
       「light 卡投影按令牌实落…立体感成为可验收项」)——把 shadow 整个
       替换掉会让这条 verified 测试变红,属「跨批次影响已 verified 条目
       语义」,不是 impl-lite 权限内可以自行拍板的事(哪怕 spec 是更新
       的方向)。**处置**:shadow 与 border 并存(不是二选一)——满足
       A-522 字面要求的同时叠加 v2 的 hairline 骨架线;这不是 v2「禁影」
       原意的完整实现,是等一个正式裁决(v2 是否废止/修订 A-522 该条)
       前的保守折中,已在 docs/ui-library.md §13.2 与本刀交付报告显式
       记为待裁事项,不是沉默各打五十大板。`.list`(A-522 测试未覆盖）
       与 `.seg` 系两处选中态(同上)维持纯 v2 处置(零冲突,见下方与
       § v2 新件段落)。 */
    .card { background: var(--card); border-radius: var(--r-card); padding: 16px;
            margin: 12px 0; border: 1px solid var(--hairline); box-shadow: var(--shadow); }
    /* 卡里的裸文本没有承载体可挂行宽上限(CSS 管不到文本节点),给它一个 */
    /* 零边距的段落壳:margin: 0 ⇒ 与原先的裸文本逐像素相同,而 ≥900px 的 */
    /* 正文族行宽规则从此盖得到它(实测 dashboard 133 字符那一行即出自这里)。 */
    .card-text { margin: 0; }
    /* 列表行(56pt + 右端 chevron)由用它的两屏各自排版,本层只给 chevron 的
       尺寸与色阶;此处不预置通用 .list-row(宪法第 4 条:没人用的不上货)。 */
    /* 图标尺寸归本层统一给(A-514 交接债①:注册表只出几何,tag 不出宽高) */
    .icon { width: 20px; height: 20px; flex: none; vertical-align: -.22em; }
    .chev { color: var(--text-3); }
    .event-icon { width: 14px; height: 14px; }

    /* ============ 组件层:品牌标记(A-522 原型回归)============ */
    /* 本节注释一律走 Django 模板注释,不用 CSS 注释:style 块里的 CSS 注释会 */
    /* 原样送到浏览器,中文注释就成了"页面上的另一种语言"——A-502 的逐语言 */
    /* 扫描当场判红(A-520 图表段同理,先例在下面那一节)。 */
    /* 两个尺寸、一枚图形:侧栏 26pt 方章(.sb-mark)与登录屏 56pt 大章 */
    /* (.brand-big),几何都取自图标注册表的 brand——壳里没有第二条画图的路。 */
    /* 方章底色 accent、字色白,glyph 随 currentColor 上白;D-10 定名后换的是 */
    /* labels 的词标与注册表那一枚几何,这两个槽位一处不动。 */
    /* 品牌槽默认不出现:移动端 Tab Bar 是七槽的地盘,不给品牌留位置 */
    /* (≥900px 的媒询里才打开,与 .nav-title 同一套显隐纪律)。 */
    .sb-brand { display: none; }
    .sb-mark { width: 26px; height: 26px; border-radius: 8px; flex: none;
            display: flex; align-items: center; justify-content: center;
            background: var(--accent); color: #fff; }
    .sb-mark .icon { width: 16px; height: 16px; vertical-align: 0; }
    /* 大标区:未登录也认得出这是哪——56pt 方章 + 站名(站名走 labels,双语) */
    .brand-head { text-align: center; margin-bottom: 8px; }
    .brand-big { width: 56px; height: 56px; border-radius: 15px;
            margin: 8px auto 18px; display: flex; align-items: center;
            justify-content: center;
            background: var(--accent); color: #fff; }
    .brand-big .icon { width: 31px; height: 31px; vertical-align: 0; }

    /* ============ 组件层:有目的的空态(A-525)============ */
    /* 判据 = 总稿 §1 层 0(下一步动作入口常驻)。零数据不是"显示 0", */
    /* 是"这屏还没有东西可看,下一步在这里"——说明一句 + 一个直达入口。 */
    /* 图形只有一枚线性图标(取自 A-514 注册表),不画插画:装饰性元素一律不加。 */
    /* 收据后置那一族在这里整族不渲染:零数据没有构成记录可挂(层 2 空转)。 */
    .empty-state { text-align: center; padding: 28px 16px 24px; }
    .empty-state .icon { width: 30px; height: 30px; color: var(--text-3); }
    .es-head { margin: 8px auto 0; max-width: 34rem; color: var(--text-2);
           font-size: var(--fs-sub); }
    .es-cta { margin: 14px 0 0; }
    .es-cta a { min-height: 44px; padding: 0 18px; border-radius: 999px;
           background: var(--accent-soft); color: var(--accent);
           font-size: var(--fs-sub); font-weight: 600; }

    /* ============ 组件层:「问这个数」入口(A-521①)============ */
    /* 逐数、逐图各一枚,与那个数并排(Footnote 级,不与数字抢主位)。 */
    /* 从 dashboard.html 提到这一层是因为它现在有三屏在用(汇总 / 调度台 / */
    /* 全域图)加图表组件库里的三张 partial ——同一条规则不该有四份副本。 */
    /* 取值逐字照搬原处,故这三屏的渲染结果一像素不变。 */
    /* [map-batch 单 D] A-544 *-num 清废:ask-num → ask-link——不沿用 -v
       值槽后缀(这不是数值展示,是引向 /ask/?q= 预填的链接);check② 早已
       记录它与 .ops-strategy-link/.routes-escape-link 声明体相同(同一个
       "Footnote 级 44pt 触控链接"样式,三个类只是历史上各自另起的名字),
       改名向这两个既有同类靠拢,不新造词汇。契约锚已随 A-543 机制迁移到
       data-contract="ask-value"(模板逐处新增,见 test_a521_assistant.py),
       类名本身不再被测试字面量锁定。 */
    .ask-link { margin: .1rem 0 0; font-size: .78rem; }
    /* [compose 单 D · A-541 归并] 上一段注释已指出三个壳类"同一个 Footnote
       级 44pt 触控链接样式"——check② 实测的真正碰撞点不在壳本身(三处
       margin 各异,是三种不同布局语境下的真实变体,不合并:ask-link 多屏
       共用/ops-strategy-link 后跟一段文字需要底距/routes-escape-link 前
       有内容需要顶距),而在**壳内 `<a>` 的样式**——三份 `{壳} a { min-
       height:44px; color:var(--accent) }` 逐字相同,decision 3(同一件事
       只留一处定义)适用:合并选择器列表,不新增类名、不改任何模板。
       ops.css/routes_list.css 的两份重复副本已删,指回本行。 */
    .ask-link a, .ops-strategy-link a, .routes-escape-link a {
        min-height: 44px; color: var(--accent); }

    /* ================== 组件层:按钮 / 表单 ================== */
    input, select, textarea, button { font: inherit; }
    input, select, textarea { width: 100%; min-height: 44px; padding: 10px 12px;
           border: none; border-radius: var(--r-ctl);
           background: var(--fill); color: var(--text); }
    input[type="checkbox"], input[type="radio"] { width: 22px; height: 22px;
           min-height: 0; accent-color: var(--accent); }
    label { display: block; font-size: 15px; color: var(--text-2); margin-bottom: 4px; }
    form p { margin: 12px 0; }
    /* 主 = accent 填充 pill;次 = accent 文字;破坏性 = red 文字(skill 按钮段) */
    button { width: 100%; min-height: 44px; padding: 10px 18px; border: none;
           border-radius: 999px; background: var(--accent); color: #fff;
           font-size: 17px; font-weight: 600; cursor: pointer; }
    button.secondary { background: var(--accent-soft); color: var(--accent); }
    button.danger { background: transparent; color: var(--red); font-weight: 400; }
    .errorlist { color: var(--red); font-size: 13px; padding-left: 1rem; margin: 4px 0; }
    /* chip(GOTV 铁律追加段):语义色 10% 底 + 全饱和字,恒二态,永远带文字 */
    .chip { display: inline-flex; align-items: center; min-height: 0;
            padding: 2px 10px; border-radius: 999px; font-size: var(--fs-foot);
            white-space: nowrap; }
    .chip-draft { background: var(--gray-soft); color: var(--gray-ink); }
    .chip-approved { background: var(--green-soft); color: var(--green-ink); }
    /* [语言 v2 · 库欠件] chip 四态补全(ui-library.md §4 backlog①):原型
       ok/warn/bad/acc 四态中,ok 语义 = 本文件既有 .chip-approved(同为
       绿色态,不新增第二处同声明体,check② 只降不增);acc 语义会与既有
       button.secondary 的 accent-soft/accent 两属性声明体逐字撞车,零
       消费方故不新造(YAGNI,留 backlog)。warn/bad 是本刀真正补齐的两态
       ——此前缺 --orange-soft/--red-soft 两枚令牌,现已随 map-batch 单 C
       (cap-chip)入库,补两条修饰类即可,无需新判断。 */
    .chip-warn { background: var(--orange-soft); color: var(--orange-ink); }
    .chip-bad { background: var(--red-soft); color: var(--red-ink); }
    /* map-batch 单 C · A-135:容量黄牌 chip + 收据构件(_route_receipt.html
       唯一产地,三屏共享故落本层)。欠带/超带同为黄牌语义共用一色(warning
       图标 + 文字是另两重通道);cap-under/cap-over 是契约/JS 钩子类,零样式
       声明(boundary-toggle 先例)。 */
    .cap-chip { background: var(--orange-soft); color: var(--orange-ink);
                gap: 4px; margin: 0 4px; }
    .cap-chip .icon { width: 14px; height: 14px; vertical-align: -.14em; }
    .rt-receipt { display: inline-flex; align-items: center; gap: 6px;
                  flex-wrap: wrap; vertical-align: middle; }
    /* A-213 路线停靠点三态色(未录/待回访/已录)——色值单点在此,route_detail
       只挂 st-* 类名:该模板受 A-102 无政党配色字面扫描,不得出现色词。 */
    .route-map .stop.st-none .stop-dot { stroke: var(--hairline); }
    .route-map .stop.st-revisit .stop-dot { stroke: var(--orange); stroke-width: 3.5; }
    .route-map .stop.st-final .stop-dot { stroke: var(--green); stroke-width: 3.5; }
    .route-map .stop.st-final .stop-dot:not(.start) { fill: var(--green-soft); }
    .basemap-pin.st-none { border-color: var(--hairline); }
    .basemap-pin.st-revisit { border-width: 3px; border-color: var(--orange); }
    .basemap-pin.st-final { border-width: 3px; border-color: var(--green);
            background: var(--green-soft); }
    #route-addresses li.st-revisit .stop-st,
    .stop-legend .st-revisit .icon { color: var(--orange); }
    #route-addresses li.st-final .stop-st,
    .stop-legend .st-final .icon,
    .tap-done-head { color: var(--green-ink); }
    /* 补记事件行:Footnote 灰底非语义色 + 记录类图标——不是状态,不占 chip */
    .event-row { display: flex; align-items: center; gap: 6px; margin: 6px 0 0;
            padding: 4px 8px; border-radius: 8px;
            background: var(--gray-soft); color: var(--gray-ink);
            font-size: var(--fs-foot); font-variant-numeric: tabular-nums; }

    /* ============ 组件层:图表(A-520 chart kit)============ */
    /* 判据 = apple-ui「图表形态」节。三条纪律落在这一段 CSS 上: */
    /* ① 色一律取令牌变量(暗色随主题自动跟,组件层不认识十六进制); */
    /* ② 图元尺寸与形状在这里给全,几何(角度/点位/百分比)由 core/chartkit.py */
    /* 服务端算好——没有一行 JS 参与绘图,禁用脚本时形态完整(S4); */
    /* ③ 零动画:火焰不跳、折线不长出来(prefers-reduced-motion 因此天然遵守)。 */
    /* [compose 单 B · A-536] 入场揭示 + 计数器,唯一时长/缓动令牌
       (--motion-duration/--motion-ease,单 A 已入 :root)驱动,单点脚本在
       base.html(chart kit 零脚本绘图纪律管的是"画图"本身——这层只管
       "刚画完那一下要不要有个入场";脚本只加两个类/改 textContent,SVG
       坐标与 DOM 最终值全部服务端已经算完、渲完)。
       - data-chart-reveal:入场淡入+微缩放,`.pre-reveal` 由脚本首播时
         临时加上再移除(reduced-motion/无脚本/非首播都不加,元素恒为
         opacity:1 的终态——三条路径零回归)。
       - 计数器(count-real/count-anim + data-count-host):count-real 自
         渲染那一刻起就是终值、永不被 JS 改动(DOM/aria 自始即终值,A-536
         failure④ 的正面形态);count-anim 是 aria-hidden 的装饰层,默认
         opacity:0(不占视觉,也不占无障碍树),`.counting` 类打开时两者
         互换可见性——那一刻 count-anim 正在从 0 数到终值,count-real
         隐去但**文字内容一字未变**。 */
    [data-chart-reveal] { transition: opacity var(--motion-duration) var(--motion-ease),
           transform var(--motion-duration) var(--motion-ease); }
    [data-chart-reveal].pre-reveal { opacity: 0; transform: scale(.94); }
    .count-anim { opacity: 0; pointer-events: none; }
    /* check②:裸 `opacity: 1` 与 .asst-line 的 hover 揭示声明体撞车—— */
    /* 补一条本就成立的 pointer-events(装饰层恒不接收指针事件,示位与不 */
    /* 示位都一样),两属性合起来与既有声明体不再相同,不是为了绕开检查 */
    /* 硬凑属性。 */
    [data-count-host].counting .count-anim { opacity: 1; pointer-events: none; }
    [data-count-host].counting .count-real { opacity: 0; }
    figure.chart { margin: 12px 0; }
    .chart h2 { margin: 24px 0 4px; }
    .chart-sub { font-size: var(--fs-foot); color: var(--text-2); margin: 2px 0 8px; }
    .chart-svg { display: block; width: 100%; height: auto; overflow: visible; }
    /* 触控目标 ≥44pt 那条规则落在 HTML 的 <a> 上;SVG 里的 <a> 若也被撑成 */
    /* inline-flex 会整段不渲染——扇区本身远大于 44pt,这里显式还原。 */
    .chart-svg a { display: inline; min-height: 0; }
    /* —— 环形:扇区之间的缝用卡面色(暗色自动跟);扇区色本身由用它那一屏 */
    /* 按档位码挂(档位词不进全站层,见令牌段的 A-505 注) —— */
    .chart-donut .seg { stroke: var(--card); stroke-width: 1.5; }
    /* ring 的轨道圆(未填部分)与 donut 的轨道圆声明体逐字相同(零填充的
       hairline/track 描边圆,同一视觉概念:占比环与进度环都用它表达"还
       没到的那一段圆周")——check②只降不增,不为同一条声明体另起一个
       类名,直接扩选择器覆盖 .chart-ring 语境(decision 3)。 */
    .chart-donut .donut-track, .chart-ring .donut-track { fill: none; stroke: var(--track); }
    /* [compose 单 D · A-544 字号收敛] 26px → 22px(t-title2 级,delta
       −4px):原型无 donut 图表(class-census §4:图表族原型 6 个类画两张
       图,donut 是本库自建、原型零对应级可抄),就近并到 22/34 二档——
       donut 中心总数是"这张图自己的总数",不是天然的屏级主角(屏若要让
       它当主角,走 hero 分支叠字面 .t-large——见 _chart_donut.html 该处
       注释,与本行不是同一件事,零改动)。本条只改**非 hero 默认态**的
       字号,不改任何
       SVG 几何(圆弧半径/描边宽度一律未动,A-520 verified 的图形本体
       不受影响)。 */
    .donut-total { font-size: 22px; font-weight: 700; fill: var(--text);
           font-variant-numeric: tabular-nums; }
    .donut-total-label { font-size: 10px; fill: var(--text-2); }
    /* [compose 单 B · A-537] 扇区反馈:形态变化(opacity),不只靠色相—— */
    /* 色盲读者与「反馈仅靠颜色」失败态两条纪律同时满足;键盘可达是 <a> */
    /* 原生行为,零 tabindex/role 补丁。 */
    .chart-donut a:hover path.seg, .chart-donut a:focus-visible path.seg { opacity: .82; }

    /* ============ 环(单值进度,A-319)——与 donut 同源极坐标、答不同问题 ============ */
    /* 触发条件见 docs/ui-library.md §3(donut/flame/heat/line 四形态旁新增
       第五条):单值 ÷ 目标的进度指标(如「今日敲门 ÷ 排班配额」),分母
       必须可呈现(has_data 假时不画,见 chartkit.ring 的分母纪律)。 */
    /* 三环共一个网格容器,恒 3 列(A-319 failure④:三环换行或缩至不可读
       都是 fail——不用 auto-fit/auto-fill,那两个关键字正是在窄屏下会
       主动折成 1/2 列的机制,与「不折行」的验收句相反)。 */
    .chart-ring-row { display: grid; grid-template-columns: repeat(3, 1fr); gap: 4px; }
    .chart-ring-fig { margin: 4px 0; text-align: center; }
    .chart-ring-fig figcaption { display: flex; flex-direction: column; align-items: center; }
    /* 三环一行不折行(A-319 failure④ 的正面形态):标题降到与 kpi-label
       同一日常字阶(--fs-foot),不用 chart 家族默认的 22px 大标题——三枚
       并排时那个尺寸会互相挤、也会造出「一屏两个大字」的多主角问题。 */
    .chart-ring-fig h2 { margin: 0; font-size: var(--fs-foot); font-weight: 600;
           display: flex; align-items: center; gap: 4px; }
    .chart-ring-fig .icon { width: 16px; height: 16px; color: var(--text-2); }
    .chart-ring-link { display: inline-flex; color: inherit; }
    /* border-radius 只在交互态才有意义(透明底时圆不圆看不出来)——放进
       hover/focus 规则而不是恒常规则,顺带让这条声明体不与 .flame-row-link/
       a.bar 的同款 hover 撞车(三者半径各不同,check② 天然不再判重)。 */
    .chart-ring-link:hover, .chart-ring-link:focus-visible { background: var(--fill); border-radius: 999px; }
    /* 按压反馈(transform:scale(--motion-press))与既有 `.btn` 按下态
       (`:active`)声明体逐字相同——不在这里另开一条,已并进该规则的
       选择器(见 .btn 组件段那一行,decision 3:同一条声明体只留一处
       定义;字面拆开写是为了不让本行文字被 test_a536 的正则误认成
       CSS 里那条真规则,导致正则往后配对到这里紧邻的下一条规则)。 */
    .chart-ring { width: 64px; height: auto; transition: transform var(--motion-duration) var(--motion-ease); }
    .ring-arc { fill: none; stroke-linecap: round;
           transition: stroke-dashoffset var(--motion-duration) var(--motion-ease); }
    /* 达标 = accent/绿、落后 = 橙红(既有令牌,零新增色;与 flame 的
       ahead=绿/behind=橙同一套语义,chart kit 内一色一义)。 */
    .ring-ahead .ring-arc { stroke: var(--green); }
    .ring-behind .ring-arc { stroke: var(--orange); }
    .ring-tick { fill: var(--text); stroke: var(--card); stroke-width: 1; }
    /* [Gate C R1 · M-9] 试过归 var(--fs-cap)(12px,≥11px 设计系统地板)
       未换:实测(qlmanage 光栅化 44 viewBox 内的实效缩放,64px 显示宽 ÷
       44 viewBox = 1.4545× 换算)——常见值「100%」两档都在环内舒展(11px
       ≈16px 实效宽 24.32/34 单位,12px ≈17.45px 实效宽 26.55/34 单位,
       都留够边距);但 pct_display 不夹取整(chartkit.ring 的 pct 用原始
       值,只有画弧的 offset 才夹到 100),超额完成会出「233.3%」这类
       6 字符值——11px 下宽度 32.91/34(边距仅 1.09 单位,已经很紧但不
       溢出),12px 下宽度 36.01/34(**负边距 2.01 单位,实测溢出环外**)。
       维持 11px 字面量;11px 本身在这一极端值下也只剩 1.09 单位边距的
       事实与 12px 会造成的真实溢出一并记入交付报告,未登记为本单待修
       项(不在 M-9 授权范围,且非本单引入)。 */
    .ring-pct { font-size: 11px; font-weight: 600; fill: var(--text);
           font-variant-numeric: tabular-nums; }
    /* —— 折线:单一系列统一 accent,无图例(标题即名)—— */
    .chart-line .line-path { fill: none; stroke: var(--accent); stroke-width: 2;
           stroke-linejoin: round; stroke-linecap: round;
           vector-effect: non-scaling-stroke; }       /* 宽屏放大也不变粗 */
    .chart-line .line-base, .chart-line .tick-line { stroke: var(--hairline);
           stroke-width: 1; vector-effect: non-scaling-stroke; }
    /* 日期刻度文字用 HTML 摆(SVG 里的字会随 viewBox 一起胀大), */
    /* 与刻度线靠 chartkit 给的同一个百分比对齐。 */
    .chart-ticks { position: relative; height: 14px; margin-top: 1px; }
    .chart-ticks .tick-label { position: absolute; transform: translateX(-50%);
           font-size: 11px; color: var(--text-2);
           font-variant-numeric: tabular-nums; white-space: nowrap; }
    /* 两端那两个刻度靠边对齐,不然居中会被裁掉半个日期 */
    .chart-ticks .tick-first { transform: none; }
    .chart-ticks .tick-last { transform: translateX(-100%); }
    /* [compose 单 B · A-537] 折线每数据点可点:大号透明命中圈(触控,
       零视觉)+ 小号可见点(默认透明,hover/focus 才现)——默认视觉与
       改动前逐像素相同(不逐点标数的极简读法不破),可点性靠交互发现。 */
    .line-point-hit { fill: transparent; }
    .line-point { fill: var(--accent); opacity: 0;
           transition: opacity var(--motion-duration) var(--motion-ease); }
    /* 显现时顺带长大一点(r 2.4→3.2):形态变化不只靠 opacity 一条通道, */
    /* 也让这条声明体不与 .asst-line 那条巧合同形的裸 `opacity: 1` 撞车。 */
    .line-point-link:hover .line-point, .line-point-link:focus-visible .line-point { opacity: 1; r: 3.2; }
    /* —— 图例:只在颜色承载类别时出现;色块之外恒有文字与数值(替代通道)—— */
    .chart-legend { list-style: none; padding: 0; margin: 4px 0 0; font-size: var(--fs-foot); }
    .chart-legend a { display: flex; align-items: center; gap: 8px;
           color: var(--text); }
    /* 色块本身的底色同样由用它那一屏按档位码挂(与条形共用一条 .lvl-* 规则) */
    .legend-swatch { width: 10px; height: 10px; border-radius: 3px; flex: none; }
    /* [A-544 第三刀 · ③分句修复] 原 .legend-num 带 margin-left:auto,把
       数值推到行尾跨屏右对齐——§4「图例即行本身;无连接件的右对齐才是病」
       (donut 图例无条形连接件)。改行内相邻:标签·数值同行紧邻(容器
       .chart-legend a 的 gap:8px 即间距)。类名随 A-544 *-num 全废改
       .legend-v(值槽后缀沿 .bar-v 先例)。 */
    .legend-v { color: var(--text-2); font-variant-numeric: tabular-nums; }
    /* —— 折叠数字表:每张图的兜底(读屏可达 / 图不可点时它就是那张图)—— */
    details.chart-table { margin: 4px 0 0; }
    details.chart-table > summary { font-size: var(--fs-foot); color: var(--text-2); }
    table.chart-data { width: 100%; border-collapse: collapse; font-size: var(--fs-foot); }
    table.chart-data th, table.chart-data td { text-align: left;
           vertical-align: top; padding: 4px 6px;
           border-bottom: 1px solid var(--hairline); }
    table.chart-data th { color: var(--text-2); font-weight: 400; }
    table.chart-data td.now { font-variant-numeric: tabular-nums;
           white-space: nowrap; }
    /* —— 火焰进度条:每周一行轨道;火苗停在实际烧至位,tick 是该周计划位 —— */
    .flame-row { display: grid; grid-template-columns: 6rem 1fr 3.6rem;
           gap: 10px; align-items: center; margin: 10px 0; font-size: var(--fs-foot); }
    .flame-week { line-height: 1.25; }
    .flame-state { display: block; color: var(--text-2); font-size: var(--fs-cap); }
    .flame-track { position: relative; height: 10px; border-radius: 5px;
           background: var(--fill); }                  /* 未烧段 = fill 灰 */
    .flame-burnt { position: absolute; left: 0; top: 0; bottom: 0;
           border-radius: 5px; background: var(--gray-ink); }   /* 已烧段沉稳 */
    .flame-gap { position: absolute; top: 0; bottom: 0; border-radius: 5px; }
    /* 落差段与刻度都是细图元(高 10px / 宽 2px),满饱和语义色与 tertiary */
    /* 文字色在这个尺寸上读不出来:实测 gap-behind 1.91:1、tick 暗色 2.36:1, */
    /* 都在图形对比度 3:1 之下(A-520 failure「对比度不达标」)。 */
    /* 三档落差段一律改取 ink 级取值(语义与档位一字未动,只换到读得出的那一档)。 */
    /* [R2 备注 N2] 刻度不能只换一个平涂色:暗组的 gray 与 gray-ink 是同一个 */
    /* 取值,刻度落进已烧段就是 1.00:1(整根隐形);而刻度既要压在已烧段(中灰) */
    /* 上、又要压在未烧段(近卡面)上,单色在两种底上必有一边读不出。 */
    /* 故刻度改双色:芯取 text 级(对卡面两档都是最大反差),外面套一圈卡面色 */
    /* 描边——芯与描边彼此 17:1(暗)/ 21:1(浅),描边与它压着的任一段 ≥3:1, */
    /* 于是"火苗到刻度还差多远"在两个主题、三种底色上都读得出。零新增色。 */
    /* 已烧段一并从 --gray 提到 --gray-ink:浅色下它对轨道原本只有 2.83:1 */
    /* (暗组两个变量同值,故暗色一字未变、仍是 4.44)。 */
    /* 实测数在 scratchpad/r3-fix/contrast-flame.json。 */
    .flame-gap.gap-behind { background: var(--orange-ink); }
    .flame-gap.gap-behind-hard { background: var(--red-ink); }
    .flame-gap.gap-ahead { background: var(--green-ink); }
    .flame-tick { position: absolute; top: -4px; bottom: -4px; width: 2px;
           border-radius: 1px; background: var(--text);
           box-shadow: 0 0 0 1px var(--card); }
    .flame-mark { position: absolute; top: 50%; margin-top: -9px;
           margin-left: -9px; color: var(--text); }
    .flame-mark .icon { width: 18px; height: 18px; }
    /* [compose 单 B · A-321] scope 下拉(周数随选举日历浮动,apple-ui
       「项数超 4 应改 dropdown」);<select> 与 <noscript> 链接组同源
       (值都已是 scope_url 算好的真实 URL),零脚本原样可用。 */
    .flame-scope { margin: 0 0 8px; }
    .flame-scope-select { width: 100%; min-height: 44px; border-radius: 10px;
           background: var(--fill); border: none; color: var(--text);
           font-size: var(--fs-foot); padding: 0 10px; }
    .flame-scope-links { list-style: none; padding: 0; margin: 0; }
    .flame-scope-links a { display: block; min-height: 44px; line-height: 44px; }
    /* [compose 单 B · A-537] scope 条(某一周)可点 → A-309 同构收据下钻; */
    /* 色重置回正文色(与 .chart-legend a 同一手法,不然整行被 <a> 的全站 */
    /* accent 底色染蓝;与 .bar 组件段的 a.bar 同一需要,合并一条选择器 */
    /* ——check② 只降不增,不为同一条声明体另开一条)。 */
    /* [语言 v2] .back-link:hover 声明体（正文色重置）与本行逐字相同,并入
       选择器列表(decision 3,check② 只降不增,不新开一条撞车规则)。 */
    .flame-row-link, a.bar, .back-link:hover { color: var(--text); }
    /* border-radius 挪进 hover/focus(透明底时圆角看不出来,恒常规则里
       摆一个没有视觉效果的属性没有意义);半径与 a.bar 的 hover 规则不同
       (8px vs 6px),声明体因此天然不相同,不与 a.bar:hover 或
       #route-addresses li.is-hot 那条巧合同形的 `background: var(--fill)`
       撞车。 */
    .flame-row-link:hover, .flame-row-link:focus-visible { background: var(--fill); border-radius: 8px; }
    /* 全程条(.flame-row-program)零覆写:恒无下钻目标(不是某一周,没有
       它自己的构成记录集),沿用 .flame-row 基础形态即是「非交互的正面
       形态」——不因为不可点就另外弱化视觉,视觉同权,只是没有 <a> 包裹。 */
    /* [A-541/A-544 第三刀] .flame-num → .n(数值槽:右对齐表格数值;
       ui-refactor-plan §2 row1 目标名,hub 裁定 2026-08-11)。第二刀曾以
       「复用既有名免撞 check②」暂名 flame-num,A-544 failure 字面点名
       *-num 全废,故终名 .n。规则原地改名不挪位(级联零位移);burndown
       火焰件其余 .flame-* 家族不动(A-520 verified)。原型自查注:原型
       index.html 的裸 .n 实为 .addr .n(24px 圆徽章)、数值槽在原型叫
       .num(165 行)——§2 row1 写的目标名与原型实物有出入,已按 hub
       裁定执行并在第三刀交付报告记录此出入。 */
    .n { text-align: right; font-variant-numeric: tabular-nums; }
    /* —— 段控(时间范围):iOS 段控形态,容器灰底 + 选中片浮起; */
    /* 无脚本可用是因为它就是一组 GET 链接,不是控件。 —— */
    .seg-control { display: inline-flex; gap: 2px; padding: 2px;
           border-radius: 999px; background: var(--fill); margin: 4px 0 8px; }
    .seg-control a { padding: 0 14px; border-radius: 999px;
           font-size: var(--fs-foot); color: var(--text-2); }       /* 高度沿 44pt 硬规,不缩 */
    /* [语言 v2] 选中片改扁平换色(motion-lab.html §5 .feel-tab.is-active
       先例:零投影,靠底色切换本身即成立对比),不再靠 shadow 提升。 */
    .seg-control a[aria-current] { background: var(--card); color: var(--text);
           font-weight: 600; }

    /* ================== 壳:页头条 + 内容区 ================== */
    /* 大标题行(各屏自己的 h1)与轻操作行解耦:操作只在这一条里,不再与
       导航槽竞宽(m2 移动端顶栏折行的根)。 */
    .topbar { display: flex; align-items: center; justify-content: flex-end;
              gap: 8px; padding: calc(6px + env(safe-area-inset-top)) 16px 0; }
    .app-main { max-width: var(--content-w); margin-inline: auto;
                padding: 8px 16px calc(72px + env(safe-area-inset-bottom)); }
    .lang-switch { margin-left: auto; display: inline-flex; align-items: center; gap: 2px; }
    .lang-switch a { min-height: 44px; padding: 0 10px; border-radius: 999px; }
    .lang-switch a[aria-current] { font-weight: 700; color: var(--text);
              background: var(--fill); }
    .lang-switch span[aria-hidden] { color: var(--text-3); }
    /* Q4 命令面板 dock 的共享令牌(`<dialog class="dock">` 通用形态):A-213 */
    /* route_detail.html 的点即录快速面板复用这三条(`class="dock tap-panel"`), */
    /* 故此三条**不属于**助理专属 CSS——改动前务必先确认 route_detail.html 的 */
    /* `dialog.tap-panel` 仍读得到这份底子(该文件 :163 注释"沿 base 的 */
    /* dialog.dock 形态与令牌")。A-529 把助理自己的展开态从 `<dialog>` 换成 */
    /* `<details>` 后,这三条不再被助理用到,但 tap-panel 还在用,**留着**。 */
    /* [语言 v2] dialog.dock 是真浮层(原生 <dialog> + backdrop):投影纪律
       「--shadow-float 仅存浮层」的第一类消费方,shadow→shadow-float 升级
       而非清零(与 .card/.list 的「页面层禁影」不是同一处置)。 */
    dialog.dock { width: min(32rem, 92vw); border: none; border-radius: var(--r-card);
           padding: 16px; background: var(--card); color: var(--text);
           box-shadow: var(--shadow-float); }
    dialog.dock::backdrop { background: rgba(0,0,0,.35); }
    dialog.dock .dock-actions { display: flex; gap: 12px; margin-top: 12px; }
    dialog.dock .dock-actions button { width: auto; }

    /* ============ 组件层:助理对话流(A-529,泡泡与面板从零重建)============ */
    /* 本节注释一律走 Django 注释:style 块里的 CSS 注释会原样送到浏览器, */
    /* 中文注释就成了"页面上的另一种语言"(A-502 扫描当场判红,A-522 实测坑)。 */
    /* 静息态 = 右下角小泡泡:44pt+安全区,移动端坐在 Tab Bar 之上(56pt */
    /* 的槽位一像素不让),桌面端退到内容区右下角。展开态改用原生 */
    /* `<details>`(志愿者支已用多轮验收的先例):无 JS 时点开即原生展开, */
    /* 断网缓存页照样打得开;JS 只叠 Esc / 点外关闭 / 钉住 / 显隐这几层增强, */
    /* 一条都不是"能不能打开"的必要条件。 */
    /* [compose 单 B · 单 A 偏差 7 处置,裁定记录] `.asst` 的 `.2s ease`
       **维持豁免,不并档**——两条路都试过:改令牌本身零风险(20ms 差值
       视觉不可辨),但 `.asst`/`.asst-line` 是 M1 冻结镜像覆盖的选择器
       (`route_print.html`/`signs_list.html` 两屏各自内联一份逐字副本,
       test_style_gate 的 `test_check_m1_frozen_screens_do_not_drift_
       from_base_css` 守卫二者与 base.css 在重叠选择器上必须逐字相同)。
       改这里就必须同步改那两屏的内联副本才能不触发漂移红——而两屏是
       本单派单纪律「两豁免屏零触碰」的硬边界,不得因为一处令牌换算而
       破例。故裁定:**豁免**,`.asst`/`.asst-line` 留在 `.2s`/`.15s`
       字面量,不进 `--motion-duration`;若未来两豁免屏解冻(M1 撤销),
       随手把这两处一并转正。 */
    .asst { position: fixed; right: 16px; z-index: 30;
           bottom: calc(72px + env(safe-area-inset-bottom));
           display: flex; flex-direction: column; align-items: flex-end; gap: 6px;
           transition: opacity .2s ease, transform .2s ease; }
    /* 移动端滚动显隐(蓝图 §2.7 / hank §7.1):JS 只切这一个类,过渡效果全在 */
    /* 这一条规则里;reduced-motion 由本文件顶部的通配符规则 */
    /* `* { transition: none !important; }` 统一豁免,本节不必再判一次, */
    /* JS 那侧另有一道独立闸(matchMedia)直接不挂显隐行为,双保险。 */
    .asst--hidden { opacity: 0; transform: translateY(16px); pointer-events: none; }
    /* [语言 v2] 静息泡泡是 fixed 定位的浮层(抽屉/浮标同族),shadow→
       shadow-float 升级(同 dialog.dock 处置)。 */
    .asst-bubble { position: relative; width: auto; min-width: 44px;
           min-height: 44px; padding: 0 14px; border-radius: 999px;
           background: var(--accent); color: #fff; box-shadow: var(--shadow-float);
           font-size: 15px; font-weight: 600; display: inline-flex;
           align-items: center; gap: 6px; list-style: none; cursor: pointer; }
    .asst-bubble::-webkit-details-marker { display: none; }
    .asst-bubble::marker { content: ""; }
    /* 泡泡上的字只给读屏与机器读:图形本身是那枚线性图标(装饰不加,字不占位) */
    .asst-word { position: absolute; width: 1px; height: 1px; overflow: hidden;
           clip-path: inset(50%); white-space: nowrap; }
    /* [compose 单 B] `.sr-only`(flame scope 下拉的 <label>,A-537 键盘/读屏
       可达需要一个可访问名称,视觉上不占位)**不与上面 `.asst-word` 合并
       选择器**:`.asst-word` 是 M1 冻结镜像覆盖的选择器(route_print.html/
       signs_list.html 各自内联一份逐字副本,与 `.asst`/`.asst-line` 同一
       处境,详见本文件 `.asst` 规则旁的裁定记录)——在 base.css 单方面
       扩它的选择器列表,不动两屏冻结副本,会让"声明体是否只有一处定义"
       这件事出现三份(base 一份、两屏各一份)只有一份改了名字的分裂,
       check② 照样判红(新类名与旧类名共享声明体,不看是不是来自"合并")。
       故 `.sr-only` 改用等价但字面不同的经典隐藏写法(`clip: rect(0 0 0
       0)`,Bootstrap 同款 `.sr-only` 的既定实现之一)——视觉效果逐像素
       相同(仍是"占 1px、裁剪到 0、只留给可访问树"),声明体不撞车。 */
    .sr-only { position: absolute; width: 1px; height: 1px; padding: 0;
           margin: -1px; overflow: hidden; clip: rect(0 0 0 0);
           white-space: nowrap; border: 0; }
    /* 角标:数字 + 可访问名称,颜色之外恒有文字(色彩替代通道)。 */
    /* 静默纪律不变:待批数为 0 时模板整块不渲染这个节点(不是 CSS 藏起来)。 */
    .asst-badge { min-width: 20px; height: 20px; margin-left: 2px; padding: 0 5px;
           border-radius: 999px; background: var(--card); color: var(--accent);
           font-size: 12px; font-weight: 700; display: inline-flex;
           align-items: center; justify-content: center;
           font-variant-numeric: tabular-nums; }
    /* 一行话(蓝图 §2.7 R1 裁决:仅 hover / 键盘 focus 才现,不再常驻展开)。 */
    /* [compose 单 B · 单 A 偏差 7 处置,裁定记录] `.15s ease` 同 `.asst`
       那条——维持豁免,理由见上方 `.asst` 规则的完整裁定记录(M1 冻结
       镜像 + 两豁免屏零触碰,不重复贴一遍)。 */
    .asst-line { position: absolute; right: 0; bottom: calc(100% + 8px);
           max-width: 15rem; margin: 0; padding: 6px 10px; text-align: left;
           border-radius: var(--r-ctl); background: var(--card);
           box-shadow: var(--shadow); font-size: 13px; color: var(--text-2);
           opacity: 0; pointer-events: none; transition: opacity .15s ease; }
    .asst-bubble:hover .asst-line, .asst-bubble:focus-visible .asst-line {
           opacity: 1; }
    /* 志愿者/监票员那一支:原生 details 即完整形态(零脚本、断网可用),形态不变。 */
    .asst-guide > .asst-panel { width: min(20rem, 86vw); margin-top: 6px;
           padding: 12px 14px; border-radius: var(--r-card);
           background: var(--card); box-shadow: var(--shadow); font-size: 15px; }
    .asst-head { margin: 10px 0 2px; font-size: 13px; font-weight: 600;
           color: var(--text-2); }
    /* 组织者支:面板五段式(头部 / 对话流 / 常驻计数 / 输入框 / 免责一行)。 */
    /* 移动默认:贴底 sheet,满宽,圆角只在上沿(≥900px 改右侧滑入,见下方媒询)。 */
    #dock > .asst-panel { position: fixed; inset: auto 0 0 0; z-index: 31;
           width: 100%; max-height: 86vh; margin: 0; overflow-y: auto;
           border-radius: var(--r-card) var(--r-card) 0 0;
           padding: 14px 16px calc(14px + env(safe-area-inset-bottom));
           background: var(--card); color: var(--text); box-shadow: var(--shadow);
           display: flex; flex-direction: column; gap: 10px; }
    .asst-dock.pinned > .asst-panel { box-shadow: 0 0 0 2px var(--accent),
           var(--shadow); }
    .asst-head-row { display: flex; align-items: center; justify-content: space-between;
           gap: 8px; }
    .asst-title { margin: 0; font-size: 15px; }
    .asst-role { font-size: 12px; font-weight: 400; color: var(--text-2);
           background: var(--fill); border-radius: 999px; padding: 2px 8px;
           margin-left: 6px; }
    .asst-head-actions { display: flex; gap: 6px; flex: none; }
    .asst-head-actions button { width: auto; min-height: 44px; padding: 0 10px;
           font-size: 13px; }
    /* 对话流:助理消息左靠、用户消息右靠(色块之外恒有 asst_you_asked */
    /* 前缀那一句,色彩不是唯一的区分通道)。 */
    .asst-flow { list-style: none; margin: 0; padding: 0; display: flex;
           flex-direction: column; gap: 8px; overflow-y: auto; flex: 1 1 auto;
           min-height: 3rem; }
    .asst-msg { border-radius: var(--r-ctl); padding: 8px 12px; font-size: 15px;
           max-width: 94%; }
    .asst-msg p { margin: 2px 0; }
    .asst-msg-a { background: var(--group); align-self: flex-start; }
    .asst-msg-u { background: var(--accent-soft); align-self: flex-end; }
    .asst-msg-who { font-size: 12px; font-weight: 600; color: var(--text-2);
           margin: 0 0 2px; }
    .asst-receipt-link { font-size: 12px; }
    .asst-pending-line { margin: 0; padding: 6px 0; font-size: 13px;
           color: var(--text-2); border-top: 1px solid var(--hairline); }
    .asst-ask-form { display: flex; gap: 8px; }
    .asst-ask-form input { flex: 1 1 auto; min-height: 44px; }
    .asst-ask-form button { width: auto; min-height: 44px; }
    .asst-chips { display: flex; flex-wrap: wrap; gap: 8px; list-style: none;
           padding: 0; margin: 4px 0 0; }
    .asst-chip { min-height: 44px; padding: 0 12px; border-radius: 999px;
           background: var(--accent-soft); color: var(--accent); font-size: 13px;
           display: inline-flex; align-items: center; }
    /* 诚实禁用态(A-530):不可点击元素,颜色之外靠 asst-chip-why 那句字面 */
    /* "缺什么"区分,不是只变个颜色就算数(色彩替代通道同一条纪律)。 */
    .asst-chip-off { background: var(--fill); color: var(--text-2); cursor: default; }
    /* [Gate C R3 · m-c/m-d] 表单载体的命令按钮(<button class="asst-chip">) */
    /* 与 <a> chip 同族视觉:类选择器已盖掉全局按钮的底色前景,这里只需把 */
    /* 全局 width:100% 收回内容宽——命令按钮不吃全局主按钮样式。 */
    button.asst-chip { width: auto; }
    .asst-chip-why { color: var(--text-3); }
    /* [Gate C R4 · m-n] 对话流分区的视觉分隔线:置顶待办区(服务端块, */
    /* .asst-pinned)与时间序历史之间那一条(JS timeline() 恰插一次; */
    /* 语义在 role=separator + aria-label,这里只是那条线本身)。 */
    .asst-flow-sep { list-style: none; margin: 2px 0;
           border-top: 1px solid var(--hairline); }
    /* [Gate C R5 · m-p] .asst-pinned 的消费方(标记不再是死类):置顶 */
    /* 待办区消息带一条左侧衬线,与时间序消息(无此类)在分隔线之外再 */
    /* 多一层可见区分——色彩不是唯一通道(分区语义仍由分隔线的 */
    /* role=separator + 可访问名承担,这里只是视觉衬托)。 */
    .asst-msg.asst-pinned { border-left: 3px solid var(--hairline); }
    /* Q9 页脚账本溯源条:纯静态标识,一行,不可点开 */
    footer.trace { margin: 32px 0 8px; padding-top: 12px;
           border-top: 1px solid var(--hairline);
           font-variant-numeric: tabular-nums; }

    /* ============ 壳形态①:移动端底部 Tab Bar(<900px)============
       同一份 DOM(nav a[data-slot] 锚点、槽序、文案键零改动)换皮而已。 */
    nav { position: fixed; left: 0; right: 0; bottom: 0; z-index: 20;
          display: flex; align-items: stretch; background: var(--card);
          border-top: 1px solid var(--hairline);
          padding-bottom: env(safe-area-inset-bottom); }
    nav a[data-slot] { flex: 1 1 0; display: flex; flex-direction: column;
          justify-content: center; gap: 2px; min-height: 56px; padding: 6px 2px;
          font-size: 12px; line-height: 1.1; text-align: center;
          color: var(--text-2); }
    nav a[data-slot] .icon { width: 24px; height: 24px; }
    nav a[data-slot][aria-current] { color: var(--accent); }
    .nav-title { display: none; }

    /* ============ 壳形态②:桌面端 240px Sidebar(≥900px)============ */
    @media (min-width: 900px) {
      body.app { padding-left: 240px; }
      body.app.bare { padding-left: 0; }     /* 一个槽都没有的页面:不留侧栏位 */
      .topbar { padding: 16px 24px 0; }
      .app-main { padding: 8px 24px 48px; }
      nav { top: 0; bottom: 0; right: auto; width: 240px; flex-direction: column;
            align-items: stretch; gap: 2px; overflow-y: auto;
            padding: 24px 12px calc(24px + env(safe-area-inset-bottom));
            border-top: none; border-right: 1px solid var(--hairline); }
      nav a[data-slot] { flex: 0 0 auto; flex-direction: row;
            justify-content: flex-start; gap: 12px; min-height: 44px;
            padding: 0 12px; border-radius: var(--r-ctl);
            font-size: 17px; text-align: left; }
      nav a[data-slot] .icon { width: 20px; height: 20px; }
      nav a[data-slot][aria-current] { background: var(--accent-soft); }
      /* 分组标题:Footnote 大写字距(skill Sidebar 段) */
      .nav-title { display: block; font-size: 13px; font-weight: 600;
            letter-spacing: .06em; text-transform: uppercase;
            color: var(--text-2); padding: 0 12px 10px; }
      /* A-522 品牌槽:侧栏顶部 = 方章 + 词标,坐在导航槽之上(它不是一个槽, */
      /* 不带 data-slot,壳 DOM 契约的锚点集合一个没动)。词标复用上面那个 */
      /* .nav-title 节点(只追加一条更具体的规则改它的排版,基规则一字没碰): */
      /* 同一处文字,从大写分组标题回归成原型的 Headline 词标,不摆两遍站名。 */
      .sb-brand { display: flex; align-items: center; gap: 9px;
            padding: 0 12px 16px; }
      .sb-brand .nav-title { font-size: 17px; font-weight: 600;
            letter-spacing: -.01em; text-transform: none;
            color: var(--text); padding: 0; }

      /* ============ A-524 · 内容栅格(「一屏看完」类屏的多列区)============ */
      /* 整段落在这一个媒询里,媒询外一条宽屏规则都没有——移动端 <900px */
      /* 的层叠结果因此与改版前逐字相同(账本 failure「移动端形态被改动」的 */
      /* 机器保证;test_a524_grid 逐文件扫钩子位置,越界当场红)。 */
      /* 挂钩只有两个:body 上的 `wide`(哪些屏进多列)与 `.wg`(哪一块分栏)。 */
      /* 表单类窄屏不挂 wide:录入与登录是单任务纵向流,横向铺开只会更难填。 */
      body.wide .app-main { max-width: 72rem; }
      /* ============ 防超长行(A-524 failure 第三条:>约 75 字符/行)============ */
      /* 两处校准,都是实测后的定值(scratchpad/gatec-fix-check/lines-*.json): */
      /* ① 单位换算:`ch` = 数字「0」的宽度,本字体栈里它比正文小写字母的 */
      /*   平均宽度大约 1.34 倍(实测,非估算)——旧值 75ch 量出来是 ≈100 个拉丁 */
      /*   字符,本身就是这条 failure 的来源。要 ≈75 字符,写的数就得是 56ch。 */
      /* ② 承载体:旧选择器只盖 `.app-main > p` 与 `.card > p` 两种直接子 */
      /*   段落,漏掉了裸文本、`div.muted`、深层 `p.muted`、`.chart-sub`、 */
      /*   `.receipt-family`(实测 dashboard 最长行 133 字符即出自漏网的那几类)。 */
      /*   改成通用正文族规则 + 豁免表:正文族一律受限(含表格单元格里的 */
      /*   说明文字——那也是正文),网格件/图形件/记录集这些"非连续文本"逐类豁免 */
      /*   (它们的宽度各有自己的语义)。 */
      /* 键在 `.app-main` 而不是 `body.wide`:超长行与"这屏分不分栏"无关—— */
      /* /strategy/ 不挂 wide 却自定 52rem,实测同样出 125 字符的行。整条仍圈在 */
      /* 本媒询内,故 <900px 的层叠结果一字不变(移动端逐像素的同一条机器保证)。 */
      /* `:where()` 零特异性 ⇒ 两条规则同权,豁免写在后面即胜出。 */
      .app-main :where(p, li, dd, dt, figcaption, blockquote,
            .muted, .t-foot, .es-head, .row-sub) { max-width: 56ch; }
      .app-main :where(.kpi, .cal, .comp, .legend, .bar-row, .row-line, .flame-row,
            .chips, .st-pick, .st-head, .seg-control, .asst-chips, .chart-ticks,
            .chart-legend, .addr-list, .expand-records, .expand-scope,
            .opsmap-wrap, .sup-legend, .lang-switch, thead, tr)
            :where(p, li, dd, dt, .row-sub) { max-width: none; }
      /* 分栏区:列由用它那一屏各自定(grid-template-areas 每屏语义不同), */
      /* 本层只给"是一张网格"与列间距。align-items: start 让两列各自到顶, */
      /* 不被高的那一列拉长成等高卡片(那是堆叠感,不是构成感)。 */
      body.wide .wg { display: grid; gap: 16px 24px; align-items: start; }
      body.wide .wg-2 { grid-template-columns: 1fr 1fr; }
      /* 主舞台型分栏(全域图):地图占大头,计数与图例退到窄的那一列。 */
      body.wide .wg-map { grid-template-columns: 1.7fr 1fr; }
      /* 两列的第一件元素各自去掉顶边距,列顶对齐(h2 与 card 的边距本不同)。 */
      body.wide .wg > div > :first-child { margin-top: 0; }

      /* ============ A-529 · 助理的桌面形态(泡泡退到右下、面板右侧滑入)============ */
      /* 移动端那一套(底部 sheet + Tab Bar 上方的泡泡)一条规则都不在这里面, */
      /* 故 <900px 的层叠结果与本节存在与否无关(A-524 的同一条机器保证)。 */
      .asst { right: 24px; bottom: calc(24px + env(safe-area-inset-bottom)); }
      #dock > .asst-panel { inset: 0 0 0 auto; width: min(24rem, 92vw);
             max-width: 24rem; height: 100%; max-height: 100%;
             border-radius: var(--r-card) 0 0 var(--r-card);
             padding: 16px; }
    }
    @media (prefers-reduced-motion: reduce) { * { transition: none !important; } }

    /* ============================================================
       A-542 · 展示组件库(账本验收句:head/section-title/card/list+row/
       chip/seg/btn/matrix + 图表 .bar 三槽)。
       图纸 = design/uiux-v2/index.html(目标名与声明体唯一来源,行号见
       各小节注)+ docs/research/design-language.md(词汇对照)。判据 =
       .claude/skills/apple-ui/SKILL.md。五项声明(解剖/数据边界/移动
       形态/文案语气/失败态)与最坏真实样本截图存证见 docs/ui-library.md
       ——本文件只放声明体,不重复五项声明的文字。
       本节新增内容一律追加在文件尾,不与既有段落交叉排列:两豁免屏
       (route_print/signs_list)的「复制自 base.css 行 X-Y」引用因此不
       受本节影响,无需再触发一轮 M1 行号重算(commit 1 已处理过一次
       :root 令牌插入引发的重算,先例见 63bca0c)。
       已有同名件(A-520 chart kit 的 donut/line/flame、助理族 asst-*)
       本节不重做;.card/.chip 名字已存在于文件前部(A-539 迁移遗留),
       本节同理不重做、不新增第二处同名定义(那会做实「同名多处」病灶,
       与本重构目的相反)——两件与原型的声明体差异(chip 缺 ok/warn/bad/
       acc 四态、.card 的 margin 施加机制不同)已核对,记入
       docs/ui-library.md 词汇表,归并候选留给第二刀(A-541)。
       ============================================================ */

    /* ---- 字阶(design-language.md §1「排版七级」的六个日常/一次性主角
       类;t-cap 本刀未有消费方,只留 --fs-cap 令牌,类本身留后续件按需
       补——宪法第四条 YAGNI,没人用的不上货)---- */
    /* t-large/t-title2:声明体基本照原型逐字(index.html:157-158)。font-size
       与既有 h1(34px)/h2(22px)同值——两档「一次性主角」域的唯一定义处
       仍是 h1/h2 元素选择器(design.md 决策 2),这两条类只补「贴在非
       标题元素上」的用法(如 head() 部件的 `<h1 class="t-large">`,
       命中同一元素时数值本就一致,不是重复定义)。line-height/letter-
       spacing 与 h1/h2 未强行拉齐,如实沿用各自出处。t-large 额外补
       overflow/text-overflow/white-space 三条(原型没有):design-language.md
       §2.1「数据边界」明文「标题单行,超出省略」,原型本身的假数据从没
       长到会溢出,故原型没写这条防线;消费方仍须自行加 `title` 属性给
       完整文字(CSS 补不了那个,标记进「移动形态」declaration)。 */
    /* [compose 单 A · A-532] 截断三件从 .t-large 基类迁到 .hdr 语境(下方
       .hdr 段):基类的单行省略是第一刀为「屏头标题」这一预想消费方补的
       数据边界,而 A-532 把 t-large 指派给「这屏正在回答的那个问题的答案」
       (design-language §1b 位次 1)——调度台的答案是一整句结论,单行截断
       会吃掉结论本身(层 0 内容不可折不可截)。迁移时点全站 .t-large 零
       模板消费方(§8.2 实证 2026-08-11 + 本批复核),两处渲染零变化;
       docs/ui-library.md §1.1 数据边界行同步修订。 */
    .t-large { font-size: 34px; line-height: 1.12; font-weight: 700; letter-spacing: .37px; }
    .t-title2 { font-size: 22px; line-height: 1.22; font-weight: 700; letter-spacing: .35px; }
    /* t-head/t-body 同为 --fs-body(17px),只差字重——原型行 159-160。 */
    .t-head { font-size: var(--fs-body); line-height: 1.3; font-weight: 600; letter-spacing: -.4px; }
    .t-body { font-size: var(--fs-body); font-weight: 400; letter-spacing: -.4px; }
    /* t-sub 副标题:原型每一处用例都复合 `sec`(次级色)+ 通常再复合
       `mt4`(顶距 4px)两个工具类;本库不搬 `sec`/`mt4`(不在八件名单内),
       改把这两条效果直接收进 t-sub 自身——本库当前唯一消费方(head 副
       标题)因此零像素差异,且比另开一条「仅两个类共享同一声明体」的
       复合规则更省一层依赖(class-census 方法论)。 */
    .t-sub { font-size: var(--fs-sub); line-height: 1.35; letter-spacing: -.24px;
             margin-top: 4px; color: var(--text-2); }
    /* t-foot 补 color:var(--text-2)(hub 裁决,2026-08-11):§2 归并表
       `iv-meta`/`muted`/`pr-meta`/`quality-line` → `.t-foot` 的归并本属
       A-541(第二刀),`muted` 现声明体正是 `color:var(--text-2);
       font-size:.85rem`——本刀把其中的颜色语义提前落进 `.t-foot` 自身
       (字号不动,`.t-foot` 已有自己的 --fs-foot 令牌,不需要 `.85rem`
       那一半)。`muted` 类本体(含字号)仍留给 A-541 收编,本条只是把
       「t-foot 该是什么颜色」这一半语义前置,顺带让 `.grow > .t-foot`
       不必自带 color、声明体收紧到纯截断三件(见 row 段)。原型 t-foot
       同时有 `sec`/`ter` 两种复合用法(83/18 处),本条固定为 sec 值——
       ter 变体本库暂无消费方,需要时再补覆盖规则(宪法第四条 YAGNI)。 */
    .t-foot { font-size: var(--fs-foot); line-height: 1.4; letter-spacing: -.08px;
              color: var(--text-2); }

    /* ---- head 屏头(原型 index.html:1592 `head(titleKey,sub)`;
       design-language.md §2.1)---- */
    /* 解剖:t-large 标题(必有)+ t-sub 副标题(可选,回答「我为什么来这屏」)。
       `.hdr{padding:2px 0 4px}` 照原型逐字。原型另有 `.hdr .t-large{display:
       block}` 一条——h1 元素本就是块级,该条只在 t-large 贴到非块级元素时
       才起效,本库唯一给出的用法(head() 部件)恒为 `<h1 class="t-large">`,
       该条对当前唯一消费方零效果,故不搬(class-census 方法论:不为零效果
       条目单独添一个「仅两个类共享 display:block」的新债)。副标题的次级
       色与顶距见上方 t-sub 自身声明(不再需要 `.hdr .t-sub` 这条复合
       规则)。 */
    .hdr { padding: 2px 0 4px; }
    /* 屏头语境的标题单行省略(自 .t-large 基类迁入,理由见上方 t-large 注):
       display:block 即原型 index.html:246 那条(第一刀曾以「对 h1 零效果」
       不搬;现在这条规则要独立承担「贴在非块级元素上也成立」的语境职责,
       一并落位)。消费方仍须自行加 title 属性承载完整文字。 */
    .hdr .t-large { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

    /* ---- section-title 节标题(原型 index.html:171;design-language.md
       §2.2)---- */
    /* 纯间距壳,不带字号——节标题的字号由使用者复合 t-title2(常见)或
       t-head(两处例外:g_filter_title/ed_results,原型自身也不统一),
       两者独立可组合,不在这里强行焊死。 */
    .section-title { margin: 28px 0 10px; }

    /* ---- card 补件:card-h(原型 index.html:261 `.card-h`)---- */
    /* .card 本体已存在于本文件前部(A-539 迁移),本节不重做(见段首注)。
       card-h 是 card 内部的横向头行(标题+右侧轻操作两端对齐),原型独立
       成类且本文件尚无同名件,纯追加。 */
    .card-h { display: flex; align-items: center; justify-content: space-between;
              gap: 12px; flex-wrap: wrap; }

    /* ---- list / row(原型 index.html:273-289;design-language.md §2.4)
       ---- */
    /* 解剖:图标槽(.rowico,可选)| 主文(t-head 或 t-body)+ 副文(t-foot)|
       尾件(chip / .rt 数字 / .chev)。三处对原型的忠实翻译改写,均为
       同效替代(视觉结果逐像素不变),理由如下:
       ① 堆叠:原型靠 `.grow` 每个子级各套一次 `.blk` 工具类换成块级;本库
       改让 `.grow` 自身 `flex-direction:column`——子级天然纵向堆叠,不必
       逐个子级加类,也不必引入 `.blk`(不在八件名单内)。
       ② `.grow` 不再靠 `.row` 前缀限定作用域:原型自身对 `.grow` 的用法
       本就不止 row 一处(如 ops.html 摘要行 `<span class="grow" style=
       "flex:1;padding-left:14px">`,不在任何 row 内、靠行内 style 补一套
       局部替身)——原型对同一概念本就用了两条不同表达,`.grow` 独立成一枚
       通用「占满剩余空间」件比复刻这个不一致更忠实概念本身;技术上也更
       安全:flex 属性对非 flex 父级零效果,零回归。
       ③ 图标着色:`.rowico` 直接给 currentColor 取值,靠 CSS 继承染到内部
       `{% icon %}`(A-514 恒输出 `class="icon"`,SVG 描边为 currentColor)
       ——不再对 `.icon` 二次选取,少一层选择器也更贴合 A-514「颜色一律
       currentColor」的既定用法。尺寸沿用已存在的 `.icon`(20px),不重造
       原型自带的 17px 第二套图标尺寸源。`.rowico.mute`(灰阶变体,原型
       用于志愿者角色行)本刀不搬:核心解剖(图标槽)零该变体也完整,
       宪法第四条 YAGNI,需要该状态的屏落地时再补。
       数据边界(design-language.md §2.4「主文最多 1 行截断带 title;副文
       最多 1 行」):副文(.grow > .t-foot)在本组件内单行省略号截断——
       声明体见下一条(色见上方 t-foot 自身,本条只留截断三件)。主文
       (t-head/t-body)的等效截断本刀仍未落 CSS——原撞车对象 `.bar-key`
       已随 A-541 归并表 bar-key→bar-k 一行提前收编(hub 裁决,2026-08-11,
       见 dashboard.css/strategy.css 对应位置的留言)而不复存在,但主文
       截断没被这条归并顺带解决(归并动的是标签槽 `.bar-k`,不是 row 的
       主文),仍记入 docs/ui-library.md 的「数据边界」declaration 与
       backlog,非本刀声明范围内的新增。
       行图标间距对齐(原型 `.row.ico + .row.ico::before{left:55px}`,
       多行连续出现图标时分隔线避让图标列)本刀不搬:唯一影响是否两行
       都带图标时分隔线的起点像素,视觉次要且原型自身也只在极少数屏用到,
       宪法第四条 YAGNI,需要时再补。 */
    /* [语言 v2] 与 .card 同一投影纪律:shadow → hairline 边(§ v2 新件
       段落尾注)。 */
    .list { background: var(--card); border-radius: var(--r-card); border: 1px solid var(--hairline); overflow: hidden; }
    .row { position: relative; display: flex; align-items: center; gap: 12px; width: 100%;
           min-height: 56px; padding: 9px 16px; border: 0; background: transparent; color: inherit;
           font: inherit; text-align: left; letter-spacing: -.4px; }
    .row + .row::before { content: ""; position: absolute; left: 16px; right: 0; top: 0; height: 1px; background: var(--hairline); }
    .grow { flex: 1; min-width: 0; display: flex; flex-direction: column; }
    /* [语言 v2 · 库欠件] row 主文截断补齐(ui-library.md §4 backlog④):
       主文(t-head/t-body)与副文(t-foot)截断三件声明体逐字相同——并入
       同一条规则的选择器列表(decision 3:同一件事只留一处定义),不另
       开一条与既有规则撞车的新规则(check② 只降不增)。 */
    .grow > .t-foot, .grow > .t-head, .grow > .t-body { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .rt { color: var(--text-2); font-size: var(--fs-sub); letter-spacing: -.24px; text-align: right; flex: 0 0 auto; }
    .rowico { width: 29px; height: 29px; border-radius: 8px; background: var(--accent-soft); color: var(--accent);
              flex: 0 0 auto; display: flex; align-items: center; justify-content: center; }
    .row[disabled] { opacity: .45; }

    /* ---- seg 分段控件(原型 index.html:317-325;design-language.md
       §2.6)---- */
    /* 与既有 `.seg-control`(本文件前部,GET 链接式时间范围切换)是姊妹
       不是同一件:那件的选中态靠一组 <a href> 无脚本可退化;这件是原型
       `role=tablist` 的按钮式互斥选择(如答案 yes/no、语言 zh/en),选中
       态需要脚本或表单配合。原型选中态引用 --thumb/--thumb-shadow 两个
       令牌,本文件未开这两个令牌(既有 `.seg-control` 遇到同一需要时就是
       改引 --card/--shadow,见该规则);这里照既有先例同样改引,不新增
       令牌。原型另立 `.seg-wrap .seg{width:100%}` 一条只为「seg 装进
       seg-wrap 这个 flex 容器时占满宽」(flex item 默认不撑满主轴);本库
       把 `width:100%` 直接收进 `.seg` 自身,不再单独为这一层组合开一条
       规则——`.seg` 在非 flex 父级下本就是块级、宽度行为不变,零回归。 */
    .seg { display: flex; width: 100%; background: var(--fill); border-radius: 10px; padding: 3px; gap: 3px; }
    .seg button { flex: 1; min-height: 38px; border: 0; background: transparent; border-radius: 8px;
                  color: var(--text); font-size: var(--fs-sub); font-weight: 500; letter-spacing: -.24px;
                  transition: background var(--motion-duration) var(--motion-ease); }
    /* [语言 v2] 同上(seg-control 姊妹件),选中态零投影靠换色本身。 */
    .seg button[aria-selected="true"] { background: var(--card); font-weight: 600; }
    .seg-wrap { min-height: 44px; display: flex; align-items: center; }

    /* ---- btn 按钮三级(原型 index.html:302-315;design-language.md
       §2.7;apple-ui 按钮规约)---- */
    /* 原型三级(btn-primary/btn-tint/btn-danger)在本库改为:`.btn` 本身
       即主级(accent 填充 + 白字),不再另设 `.btn-primary` 子类——这与
       既有裸 `button` 元素选择器的既定形态完全对齐(裸 `<button>` 早已是
       「默认即主级,`.secondary`/`.danger` 才是要显式声明的例外」),
       `.btn` 只是把同一形态套进这套新词汇,不是另立一套相反的默认。次级
       与破坏级直接复合既有修饰类,不新增第二处同名声明体(decision 3):
       · 次级(背景 var(--accent-soft) + accent 字)与既有 `button.secondary`
         逐字相同 → `<button class="btn secondary">`。
       · 破坏级:apple-ui 按钮规约写「破坏性=red 文字」(不提背景),既有
         `button.danger`(transparent + 红字)已是这个形态,原型的
         `.btn-danger` 多一层 rgba 背景且需要本文件未开的 --red-soft
         令牌 → 复合既有 `<button class="btn danger">`,不照抄背景。
       图标复用 A-514(见 row 段同一理由),不另给 `.btn .icon` 尺寸覆盖。
       `.btn-row.two`(桌面双列)本刀不搬:八件名单只要求 btn 三级本身,
       双列排布是屏级布局决策,宪法第四条 YAGNI,需要时随消费屏一起补。 */
    .btn { display: flex; align-items: center; justify-content: center; gap: 8px; width: 100%;
           min-height: 50px; padding: 0 20px; border: 0; border-radius: 999px;
           background: var(--accent); color: #fff;
           font-size: var(--fs-body); font-weight: 600; letter-spacing: -.4px;
           transition: transform var(--motion-duration) var(--motion-ease),
                       opacity var(--motion-duration) var(--motion-ease),
                       background var(--motion-duration) var(--motion-ease); }
    /* [compose 单 B] ring 的按压反馈复用同一条(见 .chart-ring-link 段旁注
       ——同一枚 --motion-press 令牌、同一种"按下去缩一点"语义,check②
       只降不增,不为同一条声明体另开一条)。 */
    .btn:active, .chart-ring-link:active .chart-ring { transform: scale(var(--motion-press)); }
    .btn-plain { background: transparent; color: var(--accent); min-height: 44px; }
    .btn:disabled { opacity: .4; }
    .btn-row { display: grid; gap: 10px; grid-template-columns: 1fr; }

    /* ---- matrix 矩阵表(原型 index.html:504-511;design-language.md
       §2.8)---- */
    /* .yes/.no(权限矩阵的核心解剖:● 已授权 / — 未授权,design-language.md
       §2.8「● / — + aria-label;禁纯文字"是/否"」)本刀不写成 `.matrix .yes`/
       `.matrix .no` 复合选择器,改写成两条独立的裸类——原型里这两个类只
       在 matrix 内出现,裸写不丢场景;好处是避免「matrix」这个类名单独
       绑进「granted/not-granted 该是什么色」这条声明体(两件事概念上无关,
       matrix 表格本身与它某一格的授权色应各自独立可读)。.no 显式写
       font-weight:400(与 .yes 的 600 对称——继承默认值本就是 400,这里
       落成字面量是为了「granted=粗,not granted=不粗」这条对比在声明体里
       自解释)。td.c(窄列居中,原型供 checkmark 列用)本刀不搬:核心
       解剖(●/— 本身)零该修饰也完整,列默认左对齐,宪法第四条 YAGNI,
       消费屏需要居中效果时再补。 */
    .matrix { width: 100%; border-collapse: collapse; font-size: var(--fs-foot); letter-spacing: -.08px; }
    .matrix th, .matrix td { text-align: left; padding: 10px 8px; border-bottom: 1px solid var(--hairline); vertical-align: top; }
    .matrix th { font-size: var(--fs-cap); font-weight: 600; color: var(--text-2); letter-spacing: 0; }
    .yes { color: var(--green); font-weight: 600; }
    .no { color: var(--text-3); font-weight: 400; }
    .matrix tr:last-child td { border-bottom: 0; }
    /* 与既有 `.table-scroll`(本文件前部)近义不同构(多一条 margin);
       原型另立 `.tablewrap` 专供 matrix 横滚,目标名不新造,照抄入库,
       两件近义留归并候选记入 docs/ui-library.md,不在本刀合并。 */
    .tablewrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }

    /* ============ 组件层:图表 .bar 三槽(A-542,原型 index.html:395-405
       `bars(rows,opt)`;visualization delta spec「图表默认形态」)============
       标签 minmax(74,88) | 轨 1fr | 值 auto——与图表规约(apple-ui skill)
       逐字同构;图例即行本身(标签+值常驻),不外挂图例;分组用 .bar-gap
       空隙,不用小标题。与既有 chart kit(A-520,本文件前部:donut/line/
       flame)是同一图表系统内的两种默认/例外形态,不是竞争实现——chart
       kit 现有件形态零改动,归并只收其方言别名(见下段例外触发条件)。
       与既有 `.bar-row`/`.bar-track`/`.bar-fill`/`.bar-num`(dashboard.css/
       strategy.css/ops.css 各自的方言:grid-template-columns 用 rem 定宽、
       gap .5rem,与本三槽比例不同构,声明体也不相同)是本刀刻意不动的
       同义类——归并留给第二刀(A-541 §2 归并表候选,记入
       docs/ui-library.md)。`.bar-key`(标签槽方言)已提前归并为本组件
       的 `.bar-k`(hub 裁决,2026-08-11:check② 阻塞第一刀,A-541 归并表
       该行提前——dashboard.css/strategy.css 定义已删,4 处模板引用已改名,
       详见 docs/ui-library.md 偏差表)。`.bar.wide`(更宽标签列的变体)
       本刀不搬:
       账本验收句只钉死默认三槽比例(标签 minmax(74,88) | 轨 1fr | 值
       auto),未提宽变体,宪法第四条 YAGNI,需要时再补。 */
    .bar { display: grid; grid-template-columns: minmax(74px, 88px) minmax(0, 1fr) auto;
           gap: 10px; align-items: center; min-height: 16px; margin-bottom: 2px; }
    .bar-k { font-size: var(--fs-foot); letter-spacing: -.08px; color: var(--text-2); white-space: nowrap;
             overflow: hidden; text-overflow: ellipsis; }
    .bar-t { height: 10px; border-radius: 2px; background: var(--fill); overflow: hidden; }
    .bar-f { display: block; height: 10px; border-radius: 0 4px 4px 0; background: var(--accent); min-width: 3px; }
    .bar-v { font-size: var(--fs-foot); font-weight: 600; letter-spacing: -.08px; text-align: right;
             min-width: 56px; font-variant-numeric: tabular-nums; }
    .bar-gap { height: 12px; }
    /* 例外五形态触发条件(visualization delta spec 逐字;详述见
       docs/ui-library.md「例外形态」节;compose 单 B · A-319 补 ring 一条
       ——nonnegotiables §3 加严「库中未写明触发条件 = fail」的正面形态,
       与下面 donut/flame/heat/line 四条同一处、同一形式,不是另开一节):
       · donut ——占整体且档位 ≤6(如支持度分布);超 6 档改表格。
       · ring  ——单值 ÷ 目标的进度指标,且分母可呈现(如「今日敲门 ÷
         排班配额」;Apple Watch/Health 对标)——无分母的数字不进这一形态,
         也不进任何形态(A-319 failure①,不得裸数字充健康指标)。
       · flame ——有计划刻度对照(如逐周敲门燃尽,tick = 该周计划位)。
       · heat  ——二维密度(如星期×时段);本库暂无实现,触发条件先写明,
         真正出现二维密度需求时再建组件(宪法第四条 YAGNI)。
       · line  ——行数 >20 时替代 .bar(如 30 天/全程趋势;14 天内仍用
         .bar 横条)。
       donut/line/flame 三形态是 chart kit(A-520 verified)既有件,本刀
       零改动其几何;三条断言零回退。ring 是本刀新增的第五形态。 */
    /* [compose 单 B · A-537] `.bar` 每行可点的库级契约(声明 + CSS 就绪;
       本刀模板零消费方——`.bar` 三槽本身是 A-542 新入库的默认形态,尚未
       替换任何屏级方言 `.bar-row`(那是 A-541 归并候选,不在本批范围),
       站上现在还没有一处渲染 `class="bar"` 的 DOM。消费屏接入时,行
       整体包一层 `<a class="bar">`〔无下钻目标则保持裸 `<div class="bar">`,
       不伪装成可点〕即继承这一契约,不必另写状态规则)。 */
    /* 正文色重置(a.bar/.flame-row-link 同一需要,合并一条选择器——见
       .flame-scope 段旁注)已在那条规则里,这里不重复定义。 */
    a.bar:hover, a.bar:focus-visible { background: var(--fill); border-radius: 6px; }

    /* ============================================================
       A-541 · 方言归并(第二刀)。同名多处定义收口——唯一定义落这里,
       屏级 css 里的同名定义已删(每处删除点留指回本节的注释)。目标名
       一律沿用被收编前多个来源里已经在用的那个名字(不新造词汇);
       任一具体收编理由见对应屏 css 文件里紧邻旧定义处的注释。
       ============================================================ */
    /* row2(disp-num/more-account/num/pb-num/reg-num/ve-count/vh-when,7 类,
       声明体唯一属性 font-variant-numeric:tabular-nums)废止,数字等宽提为
       全局(ui-refactor-plan.md §2 row2「删→tabular-nums 提为全局」)。
       inherited 属性,子孙元素默认继承,7 个旧类删除后视觉不变。**挂点特意
       不是裸 `body` 选择器**:本节前面(文件早段)已有一条 `body{...}`,
       与两豁免屏(route_print/signs_list)的冻结副本逐字重叠,M1 守卫按
       (选择器) 键比对——往那条已重叠的规则里加属性,两屏没跟着改就立即
       判「漂移」,而两豁免屏是「零触碰」硬规(第 3 步地图/路线批前不动)。
       改挂 `html body`(选择器字符串不同,与两屏的冻结副本不重叠,M1 不
       比对这一条;对渲染结果零差异——同一个 body 元素,两条选择器谁写在
       后面生效不重要,这里只有这一条)。 */
    html body { font-variant-numeric: tabular-nums; }
    /* 证据表格容器(evidence.css + route_detail.css 逐字相同)。 */
    .ev-table { width: 100%; border-collapse: collapse; font-size: .9rem; margin: .3rem 0; }
    .ev-table th, .ev-table td { text-align: left; padding: .3rem .2rem;
                                  border-bottom: 1px solid var(--hairline); }
    .ev-table th { font-size: .8rem; color: var(--text-2); font-weight: 400; }
    .ev-flag { font-size: .75rem; color: var(--text-2); }
    /* 大号计数格(eday_import.css `.counts .c-num` + gotv.css
       `.gotv-counts .c-num` 逐字相同);容器类 `.counts`/`.gotv-counts`
       各自留在原文件(各自还有其它专属规则),只挪这一条复合规则。 */
    /* [A-544 第三刀] c-num → c-v(*-num 全废;-v 值槽后缀沿 .bar-v 先例)。 */
    /* [compose 单 D · A-541+A-544 联合归并] .counts .c-v/.gotv-counts .c-v
       原 1.35rem,字号并档到 22px(t-title2 级,delta +0.4px)后与上方
       .kpi-v 声明体逐字相同(check② 新增违规对)——不是巧合:c-v 与 kpi-v
       都是"这一节自己的次级数值",同语义理应同名。改用既有 .kpi-v(15
       处消费方,gotv.html 6 处 + eday_import.html 8 处 + 上方 kpi-v 家族
       既有面,vs c-v 15 处——票数相当,但 kpi-v 已是本库唯一定义点、
       -v 值槽后缀家族的主名,c-v 并入其中而非反向合并,理由=避免"数值槽
       后缀家族"出现两个平级主名)。两文件模板 class="c-v" → class="kpi-v"
       (15 处);未补 data-contract="kpi-value"(这 15 处此前无任何契约
       标记,补锚属于扩大 A-543 触面,非本次并档所需,记入报告残余)。 */
    /* 表格数值格(row1):原 7 个同义类(bar-num/ev-table .num/flame-num/num/
       pair .num/rt-table .num/st-table .num)里,`.flame-num` 是 A-520
       chart kit 的既有件(verified,不可改动其形态),其余 6 个方言与它
       声明体逐字相同——直接复用 `.flame-num` 这个既有名(不新造 `.n`
       这类新类):新造类会与 flame-num 组成一对新的声明体重复,check②
       只降不增、不得新增违规对;复用零新增。此处不重复定义,消费方
       直接引用上方「组件层:图表」节已有的 `.flame-num`。 */
    /* 错误态文案(ledger A-541 observable 旗舰例证:「.err 10 处」)。 */
    .err { color: var(--red); }
    /* [compose 单 D · A-541+A-544 联合归并] 单屏大号计数(roster_disposal.css
       .disp-count / phonebank.css .pb-count / submissions.css .sb-count 三处
       各自独立定义,声明体逐字相同:1.6rem/700/tabular-nums,check② 三元组
       违规)。字号并档判断同上——本节自己的次级数值,归 t-title2 层级
       (22px,delta −3.6px);weight 700 与 kpi-v 家族(600)刻意不合并
       (两族并非同一命名史,合并即越界发明新语义)。目标名 `.count-v`
       延续 `-v` 值槽后缀先例(既非任一原文件名的方言胜出,也非新概念,
       组内三者语义等重、无一更通用,选中性名)。signs_list.html 的
       `.sg-count`(M1 冻结镜像,两豁免屏之一)不入本次合并——冻结不动,
       其自身 1.6rem 与本行新值不再相同,check② {disp-count,pb-count,
       sb-count,sg-count} 四元组随之整组消解(sg-count 单独一个类不成
       "组")。三消费方模板改名见对应 .html;compliance-closeout 测试字面
       量同步改。 */
    .count-v { font-size: 22px; font-weight: 700; font-variant-numeric: tabular-nums; }
    /* [compose 单 D · A-541 归并] 三份"确认/同意"checkbox 行(roster_
       disposal.css .disp-confirm / public_form.css .pf-consent /
       voter_erase_confirm.css .ve-confirm)各自独立定义,声明体逐字相同
       (44pt 触控行 + 22px 方框 checkbox + 1rem label,check② 三元组
       违规)。目标名 `.confirm-row`(-row 后缀先例:row-line/bar-row/
       day-row;三个源名语义等重,选中性名而非任一方言胜出)。 */
    .confirm-row { display: flex; align-items: flex-start; gap: .5rem;
                   min-height: 44px; }
    .confirm-row input { width: 22px; height: 22px; flex: none; margin-top: .15rem; }
    .confirm-row label { margin: 0; font-size: 1rem; }
    /* 动作行(GOTV 草稿/已发送两屏 + today.css 逐字相同的容器部分)。 */
    .actions { display: flex; flex-wrap: wrap; gap: .6rem; margin: .8rem 0; }
    /* KPI 卡片网格(dashboard.css + ops.css 逐字相同)。 */
    .kpi { display: grid; grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr)); gap: .6rem; }
    /* [compose 单 D · A-541 同名多处定义收口] .kpi-v 此前在 dashboard.css/
       ops.css/eday.css/shifts.css/today.css 五个文件各自独立定义(class-
       census.md「同名多处定义」病灶的又一例;dashboard/ops 两处原为
       1.7rem,eday/shifts/today 三处原为 2rem)。A-544 字号收敛已把五处
       并档到同一个 22px 值(见各文件对应处旁注的 delta 记录),声明体逐字
       相同后收口到单点定义——五个消费文件的原定义已删,模板 class="kpi-v"
       一字未改。strategy.css 的 .st-score(A-541+A-544 联合归并,原
       1.7rem)一并合流到这个名字,strategy.html 改用 class="kpi-v"。
       dashboard.css 的 `.comp .kpi-v`/`.empty-after .kpi-v` 两个语境覆盖
       规则是该屏自己的层级降权,不是同名重复,留在 dashboard.css 不搬。 */
    .kpi-v { font-size: 22px; font-weight: 600; font-variant-numeric: tabular-nums; }
    /* 极性五档位置态色块(dashboard.css + routes_list.css 逐字相同)**未**
       并入本层:那五个类名字面量含档位词,会把它们带进全站共享样式,撞上
       A-505「监票员屏禁字」(test_chartkit.py 对本文件全文——含注释——做
       字面量负向扫描,连这条解释性注释提一次那个词都会判红,故此处刻意
       不写出那五个类名/那两个词本身)。试过、被拦下、已改回两屏各自定义,
       详见 dashboard.css/routes_list.css 对应处注释(那两个文件不在
       test_chartkit.py 的扫描范围内,可以正常写)。 */
    /* 副文次级色(row-sub,5 个文件逐字相同:more/ops/routes_list/shifts/
       today;eday.css 多一条 font-variant-numeric:tabular-nums,是真实
       变体,留在 eday.css 里作为增量规则、级联叠加在这条之上,不搬。 */
    .row-sub { font-size: .82rem; color: var(--text-2); }
    /* row6(iv-meta/muted/pr-meta/quality-line → .t-foot):iv-meta 与
       quality-line li 已收编(声明体与 .t-foot 语义相同,差 0.6px 字号,
       与 A-542 第一刀「muted 颜色语义并入 t-foot」同一量级,同一处置——
       见 invites.css/route_create.css 旧定义处注释)。
       [第三刀更新] muted 的三处测试锚已迁 data-contract(commit 0f5939f)
       并完成 257 处归并;残余消费方与保留理由见上方 .muted 规则注释。
       pr-meta 维持不动:唯一定义在冻结屏 route_print.html 内(M1 冻结
       副本机制内,非方言),随豁免屏裁决收口。 */
    /* row7(board-dot-* / dot-* → .map-dot)[map-batch 单 D 执行]:路线三档
       (原 board-dot-draft/-assigned/-done)与门址五档(原 dot-unassigned/
       -en_route/-revisit/-knocked/-done)**语义档位不合并**——两套值空间
       各自独立(Board 列头/List 表格判的是路线状态,Map 图例/地址抽屉判的
       是门址状态),只统一类名与定义产地。此前「尝试后撤回」是因为
       test_a528_projections.py 曾用正则字面量锚 `class="board-dot..."`;
       该锚已随 A-543 契约锚迁移改锚 `data-status`(§7.1 表 Board 列头状态
       行),旧阻塞已解除。默认几何(10px 无描边)取原 board-dot 声明体;
       Map 图例/地址抽屉专属的 14px + card 色描边 + 基线对齐场景化覆盖
       (原 .dot 声明体)留在 routes_list.css(消费方是该文件的 .legend/
       .addr-drawer 两个容器,不跨屏,不构成方言)。 */
    .map-dot { display: inline-block; width: 10px; height: 10px; border-radius: 999px;
               flex: none; }
    /* [compose 单 C · A-320] 缩略图 chip 语境的边宽提供者:knocked 档的
       「空心 + green 描边」靠下面变体的 border-color 生效,基类无边宽画不出
       ——图例语境由 routes_list.css .legend 自带 2px 边,这一条是入口卡自己
       的语境规则。**必须排在变体之前**:border 简写会把 border-color 一并
       重置,后写会盖掉 knocked 的 green(routes_list.css 同一坑的既有注释)。
       透明边对实心四档零视觉变化。 */
    .map-thumb-chips .map-dot { border: 2px solid transparent; }
    .map-dot[data-status="draft"], .map-dot[data-status="unassigned"] { background: var(--gray); }
    .map-dot[data-status="assigned"], .map-dot[data-status="en_route"] { background: var(--accent); }
    .map-dot[data-status="done"] { background: var(--green); }
    .map-dot[data-status="revisit"] { background: var(--orange); }
    .map-dot[data-status="knocked"] { background: transparent; border-color: var(--green); }

    /* ============================================================
       compose 单 A · 质询件 receipt + 身份件 rolechip + topbar 工具簇
       (A-532/A-533 前置/A-535;五项声明与最坏样本见 docs/ui-library.md
       §3。判据 = design-language §3.1 + ui-refactor-plan §3.1 三条边界 +
       apple-ui。本节照库纪律追加在文件尾。)
       ============================================================ */
    /* ---- receipt 收据问号(全站唯一收据/追问入口的库件形态)----
       解剖:真链接 <a class="receipt" href="/ask/?q=…" data-contract=
       "receipt" aria-label="…">?</a>——glyph 是排版字符「?」,不是 SVG
       (A-514 词汇表 21/21 满员,test_icons 上限断言在案;matrix 件的
       ●/— 字符 glyph 为同一先例。A-533 验收句「A-514 集内问号图标」与
       该上限的冲突已报 hub,归单 C 裁)。无脚本 = 原生跳 /ask/ 深链
       (既有「无脚本可退化」验收句);有脚本 = 泡泡接管(_assistant.html
       IIFE 内 delegated listener,沿 A-317 既有 ask() 单点)。
       触控:视觉 24px 圆 + ::after 把热区扩到 44pt(chip 件「按 44pt
       触控扩展热区」同一手法);position:relative 是热区的定位锚。 */
    /* [compose 单 C] 选择器升 a.receipt(同特异度压过既有 `.ask-link a` 的
       accent 色与 44px min-height——消费方落进 .ask-link 壳后,rest 态色与
       24px 圆的库件声明必须赢;件的解剖本就是 <a class="receipt">,选择器
       只是把这一事实写进特异度)。glyph 已按 hub 预裁(2026-08-12)换 A-514
       注册表词 "question"(词表 21→22,test_icons 上限断言同步);原「?」
       排版字符方案随之退役,font-* 三条对 SVG glyph 惰性但留存——删除会
       改变声明体、平白搅动 check② 配对面,归 A-541 清偿判。 */
    a.receipt { position: relative; display: inline-flex; align-items: center;
               justify-content: center; width: 24px; min-height: 24px; flex: none;
               border-radius: 999px; background: var(--fill); color: var(--text-2);
               font-size: var(--fs-foot); font-weight: 600; line-height: 1;
               font-variant-numeric: normal; vertical-align: middle; }
    a.receipt::after { content: ""; position: absolute; inset: -10px; border-radius: 999px; }
    /* hover/focus 反馈 = fill 加深一档 + 前景升主文(不借 accent-soft/accent
       组合——那是次级按钮家族的声明体,check② 只降不增);focus 可见性另有
       浏览器默认 focus-visible 外环,这里不清除。 */
    a.receipt:hover, a.receipt:focus-visible { background: var(--track); color: var(--text); }
    /* 问号 glyph(注册表 icon)在 24px 圆内的尺寸;flex: none 防挤压,顺带
       与 .event-icon 的两条声明体区分(check② 只降不增)。 */
    a.receipt .icon { width: 14px; height: 14px; flex: none; }
    /* ---- rolechip 身份牌(原型 index.html:248 虚线徽记;非交互)----
       解剖:<span class="rolechip" aria-label="…"><span class="rolechip-
       avatar">首字母</span>姓名 <b>角色名</b></span>。原型的 rolechip 是
       角色切换按钮(Q10 红线),本件降为只读 <span>:虚线边即「非实体
       控件」的视觉语义,零点击路径。 */
    .rolechip { display: inline-flex; align-items: center; gap: 7px; min-height: 44px;
                padding: 0 14px 0 6px; border: 1px dashed var(--hairline);
                background: transparent; border-radius: 999px;
                color: var(--text); font-size: var(--fs-foot); letter-spacing: -.08px;
                white-space: nowrap; max-width: 16rem; overflow: hidden; text-overflow: ellipsis; }
    /* 角色名的加粗走全局 strong 规则(消费方用 <strong>,不为 <b> 另开一条
       与既有 font-weight:600 家族撞体的规则——A-538 check② 只降不增)。 */
    .rolechip-avatar { width: 26px; height: 26px; border-radius: 999px; flex: none;
                display: inline-flex; align-items: center; justify-content: center;
                background: var(--accent-soft); color: var(--accent);
                font-size: var(--fs-cap); font-weight: 700; }
    /* ---- topbar 工具簇(A-535):右上 = 身份牌 + 主题一击 + 语言,
       合计 ≤3 枚控件。移动端身份牌与主题入口收进「更多」(本簇整块
       display:none),桌面必显——显隐规则在下方 900px 媒询块。 */
    /* margin-left:auto 常驻在基规则里(与 display:none 同体,顺带让声明体
       不与既有「纯 display:none」家族撞车——check② 只降不增):簇显示时
       第一个 auto 边距吃掉全部剩余空间,语言切换随后紧贴,三件一起靠右;
       <900px 簇不显示,语言切换自己的 auto 边距行为与改版前逐字相同。 */
    .topbar-tools { display: none; margin-left: auto; }
    /* 主题一击:POST /theme/ 的图标态按钮(◐/○/● 字符 glyph,状态词在
       aria-label/title);覆写全局主按钮底色——它是 topbar 轻操作,不是
       屏级主行动(一屏一枚主按钮的名额不给它)。 */
    .theme-tap { width: 44px; min-height: 44px; padding: 0; border-radius: 999px;
                 background: transparent; color: var(--text-2);
                 font-size: var(--fs-body); font-weight: 400; line-height: 1; }
    .theme-tap:hover, .theme-tap:focus-visible { background: var(--fill); color: var(--text); }
    @media (min-width: 900px) {
      .topbar-tools { display: inline-flex; align-items: center; gap: 8px; }
    }

    /* ============================================================
       compose 单 C · A-320 入口卡缩略图(map-thumb)
       服务端 SVG 静态切片(core/map_thumb.py 几何):底图轮廓 +
       未派计数簇 + 有状态点。状态色 = data-status 变体,与 .map-dot
       同名令牌(一色一义;test_compose_batch_c 逐档核对令牌同名)。
       ============================================================ */
    /* 整卡即链接(A-320 failure ④ 的正面形态):块级铺满、正文色、去下划线
       ——卡不是一句可点的蓝字,是一整块触控目标。 */
    .map-thumb-card { display: block; color: inherit; text-decoration: none; }
    .map-thumb-svg { display: block; width: 100%; height: auto;
                     margin: 10px 0 0; border-radius: 10px; background: var(--fill); }
    /* 底图 = 选区外轮廓(示意级「疆域长什么样」,零瓦片):hairline 描线。 */
    .map-thumb-svg .thumb-base { fill: none; stroke: var(--hairline); stroke-width: 1; }
    /* 五档状态点:令牌与 .map-dot 变体逐档同名(gray/accent/orange/green);
       knocked 沿图例同一「空心 + green 描边」形态(fill/stroke 是 SVG 侧的
       background/border 对应物,属性不同、令牌相同)。 */
    .map-thumb-svg circle[data-status="unassigned"] { fill: var(--gray); }
    .map-thumb-svg circle[data-status="en_route"] { fill: var(--accent); }
    .map-thumb-svg circle[data-status="revisit"] { fill: var(--orange); }
    .map-thumb-svg circle[data-status="done"] { fill: var(--green); }
    .map-thumb-svg circle[data-status="knocked"] { fill: none; stroke: var(--green); stroke-width: 1.4; }
    .map-thumb-chips { display: flex; flex-wrap: wrap; gap: 6px; margin: 10px 0 0; }

    /* ============================================================
       语言 v2 · P0 库刀(A-547/候选 126)新增件。图纸 = docs/research/
       language-v2-spec.md §3(唯一权威)+ 定案实物 concept-v2-dashboard-r2.html
       与 motion-lab.html(逐值对照)+ design/proto/a321b-burndown-polish.html
       右框(chart-flame v2,结构裁决保留)。判据 = apple-ui skill + 本节
       各件五项声明(docs/ui-library.md)。**"模板零改动"申报订正
       (Gate C R1 · B3)**:本刀**未新造任何屏面模板消费方**(沿 A-542
       首刀先例:库件先立,屏面替换归各屏 v2 施工批,这一半属实)——但
       `core/templates/core/route_print.html` 与 `signs_list.html`
       两个**既有**模板文件**逐轮改动数十行**(精确行数因逐轮整改
       持续变化,用 `git diff origin/main --numstat` 现测,不在此处
       钉死一个会立刻过期的数字)(A-539 M1 冻结镜像同步:这两屏内联
       <style> 是 base.css 对应重叠选择器的逐字副本,base.css 改一条、
       镜像必须跟着改一条,否则 `test_check_m1_frozen_screens_
       do_not_drift_from_base_css` 判红——机制上不可能不改)。上一版
       文档把这条**技术上正确、必要**的同步动作误写成「模板文件零
       改动」,是申报失实,不是改动本身有问题;真实态:0 个新模板消费
       方 + 2 个既有模板的持续镜像同步,详见 docs/ui-library.md
       §13.13/§13.15⑥。.bar/.chart-* 既有形态零改动,新件与既有件并存
       不冲突。
       追加在文件尾,不与既有段落交叉排列(两豁免屏行号引用不受影响,
       但两屏内部因本刀镜像同步已发生变化,行号对照见各屏文件自身
       注释)。
       ============================================================ */

    /* ---- 库欠件补齐(旧库backlog,以 v2 值落) ---- */

    /* .t-cap:字阶类补齐(base.css 原 §0 注记「t-cap 本刀未有消费方,只留
       令牌」——A-542 令牌本体早已 verified,缺的只是这一类绑定)。声明体
       逐字取 design/proto/a321b-burndown-polish.html 的 `.proto-cap`
       桥接实证(「桥接同一令牌」= --fs-cap,该原型页脚注明「真正落库时
       应正名 .t-cap」)。 */
    .t-cap { font-size: var(--fs-cap); color: var(--text-2); display: block; }
    /* .t-label:v2 新增的「大写标签系」通用工具类(spec §2 --fs-label
       "v2 最高频的新面孔"完整recipe——size/weight/tracking/大写/色,五属性
       逐字取 concept-v2-dashboard-r2.html 的 .kpi-label/.comp-title/
       .heat-day-label/.heat-period-label/.bottom-label 五处完全相同的
       声明体,原型草稿各自起名未去重,本库归一为一个通用件,供本节下方
       metric/heat-grid/stat-link-row 等新件共用(decision 3:同一件事
       只留一处定义,避免五处新造类互相撞成 check② 违规)。与既有 .t-cap
       (12px,--fs-cap)是两个不同尺寸、不同来由的独立件,不合流——
       .t-cap 绑定的是「t-cap 本就该长什么样」这个更早的独立缺口
       (a321b 桥接实证),.t-label 绑定的是 v2 新引入的标签字阶,数值
       不同(12 vs 11px)不能假装是同一件事。 */
    .t-label { font-size: var(--fs-label); font-weight: 500; letter-spacing: .06em;
               text-transform: uppercase; color: var(--text-3); }

    /* l0(结论句,候选 113)：三处方言(dashboard.css/routes_list.css)
       归一为库内单点定义;ops.css 一支已在 A-532 时代把 l0-say 降为零
       样式契约锚、复合既有 .t-large + 屏内 tone-* 语义色类达成屏级
       hero 呈现(该形态本刀不动,是另一种合法消费方式,非待归并的方言)。
       两处方言声明体几乎相同,只差 margin 末值(dashboard .6rem /
       routes_list 0——2 值简写形式对 bottom 取值不同);取 dashboard 版
       (提供呼吸距离,风险更低,routes_list 两处消费方因此多 .4rem 底距,
       delta 记录在此,未验证视觉是否需要精调,留 backlog)。字号本身
       spec 未给 l0 专属阶梯,维持既有 1.05rem 不发明新值(疑问已记
       backlog,供研究席核对是否该并入 display 域)。 */
    .l0-say { font-size: 1.05rem; font-weight: 600; margin: .2rem 0 .6rem; }

    /* chart-flame v2(候选 120,结构裁决保留、皮随 v2):五件补齐,结构
       逐字照 design/proto/a321b-burndown-polish.html 右框「打磨版」——
       A-321 verified 的四条几何/切换语义一寸不动(轨道 position/inset/
       圆角承自既有 .flame-gap/.flame-mark/.flame-burnt,未新起一套轨道
       几何)。图纸原型类名 proto-* 前缀明标「非定稿,不建议直接抄类名」,
       本刀择既有 flame 与 chip 两族命名正名入库。 */
    /* 计划层浅色底层填充(原 .proto-gap-plan):结构=既有 .flame-gap 的
       position/inset/圆角(未新起轨道),只加一条新颜色,与既有
       .gap-behind/.gap-behind-hard/.gap-ahead 三档同一扩展手法。**留白
       未验证**:--gray-soft 与轨道底色 --fill 数值接近(rgba 120,120,128
       .12 vs 142,142,147 .14,两者一样浅),明度差是否实测可辨未经真实
       渲染核验(图纸派单原话「必要时开 --plan-soft 令牌」)——本刀先照
       图纸字面用 --gray-soft(零发明),明暗对比结论留 backlog 给消费屏
       批实测(该批能看见真实渲染,现场判断令牌是否要开)。 */
    .flame-gap-plan { background: var(--gray-soft); }
    /* 火苗浮轨上方(原 .proto-flame-float):复用既有 .flame-mark 的颜色
       与图标尺寸(该规则不重复定义),只覆写垂直定位——从"轨道内居中"
       改"浮在轨道上方 4px"。水平定位(left 百分比 + margin-left:-9px
       居中)原样继承自 .flame-mark,不重复声明。 */
    .flame-mark-float { top: auto; bottom: 100%; margin-top: 0; margin-bottom: 4px; }
    /* 第三列变宽(原 .proto-row2):3.6rem → auto,容纳「0 / 1,000」两级
       数对;第一列(6rem 标签)与第二列(1fr 轨道)原样未动,不重开一套
       网格。 */
    .flame-row-pair { grid-template-columns: 6rem 1fr auto; }
    /* 落后状态 chip(原 .proto-chip-behind + .proto-chip-dot):色对
       (--orange-soft 底 + --orange-ink 字)与既有 .cap-chip 声明体重复
       但**不借用该名**——cap-chip 语境专指容量黄牌(门数超/欠带),借名
       会把"进度落后"语义读错;颜色仍是同一对既有令牌,零新色。圆点用
       background:currentColor 继承 chip 自身文字色,不二次取值,故可
       与其它色态 chip(如后续 .chip-warn)共用同一枚 .chip-dot。 */
    .chip-behind { background: var(--orange-soft); color: var(--orange-ink); gap: 6px; }
    .chip-dot { width: 6px; height: 6px; border-radius: 999px; background: currentColor; flex: none; }

    /* tap-chip 选中态变体收编(A-215 as-built 公式,route_detail.css
       `.tap-chip:has(input:checked){background:var(--accent-soft);
       color:var(--accent);font-weight:600}`):routes_list.css 自己的
       注释已指明这一 3 属性组合"已被 tap-chip:checked/secondary 占用,
       check② 判重复组"——故本刀**不新造一条同声明体的库级规则**（新写
       即撞车),"收编"落在文档层:docs/ui-library.md 正式记该公式为
       v2 通用"选中态"配方,后续任何新组件需要同款选中态时复合既有
       `.tap-chip:has(input:checked)` 的声明体模式（accent-soft 底 +
       accent 字 + 600 字重),不重新发明、也不重新定义。 */

    /* ---- spec §3 八新件 + back-link(P0 落库,模板消费待各屏 v2 施工批) ---- */

    /* back-link(spec §7 硬规):非根屏顶条恒有「← 上一层名」。字符化箭头
       (spec 正文本身用「←」字符书写这条规则),不占 A-514 图标注册表
       名额(matrix ●/— 字符 glyph 同一先例)。触控目标由全站既有 `a`
       元素级规则给(`a{display:inline-flex;...;min-height:44px}`,本
       文件前部已有,此处不重复)。hover 态(色重置为主文色)与既有
       `.flame-row-link, a.bar` 声明体逐字相同,已并入该规则的选择器
       列表(见本文件「组件层:图表」节该行),不在此重复定义。 */
    .back-link { font-size: var(--fs-sub); color: var(--text-2); }

    /* callout(spec §3:白底 1px 线浮标,图表与热力格共用)。声明体逐字
       取 concept-v2-dashboard-r2.html `.heat-callout` 的视觉部分(位置
       坐标是每个消费方各自的语境值,不进共享件——见下方 .heat-callout
       的定位覆盖)。SVG 内的浮标(如 chart-area 的 crosshair 读数框)走
       同一对令牌但用 SVG rect 的 fill/stroke 呈现属性直接表达(SVG 没有
       CSS border-radius 概念,用 rx 属性代替),不套用本 HTML 向类名。 */
    .callout { padding: 6px 10px; border-radius: 6px; background: var(--card);
               border: 1px solid var(--hairline); font-size: 12px; color: var(--text);
               white-space: nowrap; }

    /* metric(指标格:标签+英雄数+delta 行+sparkline)。声明体逐字取
       concept-v2-dashboard-r2.html .kpi-strip/.kpi-cell/.kpi-hero/
       .kpi-delta 族(R2 改动 5「1.5fr 1fr 1fr 1fr——首格升为英雄中的
       英雄」,本刀按图纸原样收录这一具体构图,未验证其它列数变体,
       需要时另议)。标签部分复合 .t-label(不重复定义)。 */
    .metric-strip { display: grid; grid-template-columns: 1.5fr 1fr 1fr 1fr; }
    .metric-cell { padding: 24px 20px; border-left: 1px solid var(--hairline); }
    .metric-cell:first-child { border-left: none; }
    /* 英雄数字阶:普通格用 display-sm(40px),首格(1.5fr 那一格)升一级
       用 display-xl(56px)——逐字对应 concept-r2 的 40px/56px 两档,恰是
       本刀新增的 --fs-display-sm/--fs-display-xl 两枚令牌。 */
    .metric-hero { margin-top: 10px; font-size: var(--fs-display-sm); font-weight: 620;
                   letter-spacing: -.01em; color: var(--text); line-height: 1; }
    .metric-cell:first-child .metric-hero { font-size: var(--fs-display-xl); }
    .metric-delta { margin-top: 10px; font-size: var(--fs-cap); }
    .metric-delta.pos { color: var(--pos); }
    .metric-delta.neg { color: var(--neg); }
    /* [语言 v2 · Gate C R1 B2 修复,第二次处置] 单属性 `color:var(--text-3)`
       与既有 `.chev` 撞车——但 `.chev` 的声明体本身早已是一个 7 员老
       clique 的成员(`asst-chip-why`/`icon`/`lang-switch`/`st-none`/
       `stop-legend`/`stop-st`,跨多条独立规则、已在 check② 基线里),
       并入 `.chev` 选择器列表看似"decision 3 常规处置"、实则把这团
       老债的 union 从 7 撑到 9,新增的是"新两个 token 各自配对老
       七个"那 14+ 对,而不是想象中的 1 对——**第一次处置(已回退)
       正是撞在这个坑**,与本文件「组件层:助理对话流」节
       `.chart-crosshair` 那次的教训是同一类,但触发条件不同(那次是
       M1 双份计数,这次是"合并目标本身已是老债成员")。改走独立
       声明体:多一条 `opacity` 通道(与既有 `.pos`/`.neg` 两个纯色
       modifier 拉开,视觉上也更贴合"muted=更不显眼"的语义,不是为
       了避免撞车而硬凑的无意义属性)。 */
    .metric-delta.muted { color: var(--text-3); opacity: .82; }
    .metric-spark { display: block; margin-top: 12px; }

    /* view-tabs(文字 tab + 2px 下划,radio/GET 双形态)。选中态走
       [aria-current](既有 .seg-control a[aria-current] 同一惯用法,
       消费方服务端渲染当前视图时设置该属性——与全站 nav[aria-current]/
       seg-control 同一纪律,不专为本件另造一套状态机)。原型的
       radio+:checked 零脚本切换手法是构图验证(概念稿本页头注「四视图
       radio+CSS 真切换」标注为方向验证),不锁定为唯一实现路径——GET
       链接式切换(与 .seg-control 同构、URL 可分享)与 radio 式各有取舍,
       留消费批据屏况选择,库层只给两种都能消费的视觉与状态类。 */
    .view-tabs { display: flex; gap: 16px; }
    /* [语言 v2 · Gate C R1 M2 修复] 触控目标 ≥44×44pt(令牌硬规,同
       .seg-control a「高度沿 44pt 硬规,不缩」同一纪律)——本刀首版
       遗漏,姊妹件 .seg-control 靠元素是 `<a>` 白捡全站 `a{min-height:
       44px}` 兜底,`.view-tab` 消费方可能是 `<label>`(radio 形态)
       等不吃那条兜底规则的元素,须显式给,不能假设继承得到。 */
    .view-tab { display: inline-flex; align-items: center; min-height: 44px;
                font-size: var(--fs-foot); color: var(--text-2); padding-bottom: 4px;
                border-bottom: 2px solid transparent; cursor: pointer; }
    .view-tab[aria-current] { color: var(--accent); border-bottom-color: var(--accent); }

    /* chart-area(面积折线:1.5px 线/虚线零基线/三发丝网格/渐变面/
       crosshair+浮标)。**产地边界如实记录**:折线路径/渐变/网格线/坐标
       文字在 concept-r2 与 motion-lab 均为 SVG 内联属性直接引用令牌
       (`stroke="var(--c-line)"` 一类),不经 CSS 类——这部分是**服务端
       几何生成器**的活(通用 N 点面积图路径计算,对标既有 chart kit 的
       `core/chartkit.py`),属"需现场判断的算法"而非"照图施工",超出
       本刀 base.css/ui-library.md/静态 chartkit 三件套授权范围,不在
       本刀实现——沿用既有 .chart-svg(A-520 verified,零改动、天然复用,
       concept-r2 自身也复用同一类名)。本刀只补 crosshair 组的可见性
       状态(唯一确有 CSS 类的部分,逐字取 motion-lab.html §1
       `.cross-group`/`.cross-group.is-visible`,时长改走令牌而非原型的
       字面量 200ms——A-536「屏内自定义时长=fail」硬规,原型是独立
       演示稿不受此规约,库件必须遵守)。**可见态未照抄原型裸
       `opacity:1`**:该单属性声明体与既有 `.asst-bubble:hover .asst-line`
       (本文件「助理对话流」节)撞车——那条规则本身已与两豁免屏的冻结
       镜像各算一次独立定义(M1 结构使然),check② 判据是"任意两条独立
       规则同声明体"而非"同一条规则的选择器多寡",裸接一条新规则或并入
       既有选择器列表两种处置都不能消解、只会牵连镜像跟着改。改法沿既有
       `.line-point-link:hover .line-point{opacity:1;r:3.2}` 同一先例
       (该处旁注:「显现时顺带长大一点,形态变化不只靠 opacity 一条通道,
       也让声明体不与 .asst-line 那条巧合同形的裸 opacity:1 撞车」)——
       补一条同语义的微缩放(与 `[data-chart-reveal]` 的 scale(.94)→1
       入场手法同源,不是另造一套动效语言),声明体因此天然不同,零
       牵连两豁免屏。 */
    .chart-crosshair { opacity: 0; transform: scale(.98);
                        transition: opacity var(--motion-duration) var(--motion-ease),
                                    transform var(--motion-duration) var(--motion-ease);
                        pointer-events: none; }
    .chart-crosshair.is-visible { opacity: 1; transform: scale(1); }

    /* rank-bar(满宽细条构图:名+6px 圆角条+值+占比)。声明体逐字取
       concept-v2-dashboard-r2.html .range-list/.range-row 族(区域/
       候选人两视图共用同型)。 */
    .rank-bar-list { position: relative; margin-top: 16px; display: flex;
                      flex-direction: column; gap: 20px; }
    /* 零基线定位 left:124px = name 列宽 112px + row gap 12px,逐字对齐
       条形起点(concept-r2 原注:"对齐条形起点 = 0 基线");改动 name 列宽
       或 gap 需连带重算这个值,非独立令牌。 */
    .rank-bar-baseline { position: absolute; top: 0; bottom: 0; left: 124px;
                          width: 1px; background: var(--hairline-strong); }
    .rank-bar-row { display: flex; align-items: center; gap: 12px; }
    .rank-bar-name { width: 112px; flex: none; font-size: var(--fs-foot); color: var(--text); }
    .rank-bar-col { flex: 1; }
    .rank-bar-fill { height: 6px; border-radius: 3px; background: var(--accent); }
    .rank-bar-value { width: 34px; flex: none; text-align: right; font-size: var(--fs-sub);
                       font-weight: 600; color: var(--text); }
    .rank-bar-pct { width: 34px; flex: none; text-align: right; font-size: var(--fs-cap);
                     color: var(--text-3); }

    /* heat-grid(热力格:accent 透明度五档/峰值 callout/图例+小样本免责)。
       声明体逐字取 concept-v2-dashboard-r2.html .heat-grid 族;触发条件
       表(docs/ui-library.md §3)heat 一行随本刀由「暂无实现」转「已
       实现」。日/时段标签复合 .t-label(不重复定义)。 */
    .heat-grid { margin-top: 16px; display: grid;
                 grid-template-columns: 56px repeat(7, 1fr); grid-auto-rows: 40px; gap: 6px; }
    .heat-day-label { display: flex; align-items: center; justify-content: center; }
    .heat-period-label { display: flex; align-items: center; }
    .heat-slot { display: block; position: relative; width: 100%; height: 100%; }
    .heat-cell { display: block; width: 100%; height: 100%; border-radius: 3px;
                 background: var(--accent); }
    /* 峰值浮标:复合 .callout(视觉)+ 本条(定位),两者职责分离——同一
       视觉recipe 若 chart-area 的 SVG 读数框也要用 HTML 版,同样复合
       .callout,不重复声明底色/边线/字号。 */
    .heat-callout { position: absolute; bottom: calc(100% + 6px); right: -8px; z-index: 2; }
    .heat-legend { margin-top: 12px; text-align: right; font-size: var(--fs-cap); color: var(--text-3); }
    /* 小样本免责附注:10px 无对应令牌(四级日常字阶最小为 --fs-cap
       12px,display 域三级均 ≥40px),原型本身也是独立字面量;新开一个
       "更小一级"令牌超出本刀 spec §1 换值/新增令牌清单授权,维持字面量
       并记录(同既有 .ring-pct 11px 字面量先例:Gate C R1 M-9 记录在案
       的同类处置)。 */
    .heat-legend-note { font-size: 10px; }

    /* stat-link-row(底区三联)。声明体逐字取 concept-v2-dashboard-r2.html
       .bottom-area 族。标签复合 .t-label(不重复定义)。 */
    .stat-link-row { display: grid; grid-template-columns: 1fr 1px 1fr 1px 1fr;
                      margin-top: 24px; }
    .stat-link-vline { background: var(--hairline); }
    .stat-link-cell { padding: 0 24px; }
    .stat-link-cell:first-child { padding-left: 0; }
    .stat-link-cell:last-child { padding-right: 0; }
    .stat-link-lines { margin-top: 16px; }
    .stat-link-line { font-size: var(--fs-foot); color: var(--text-2); line-height: 1.6; }
    .stat-link-link { display: inline-block; margin-top: 8px; font-size: var(--fs-foot);
                       color: var(--accent); }

    /* ---- 动态层剩余两落点(spec §4;Gate C R1 B1 修复:此前只落 chartkit.js
       一项,stagger 与 View Transitions 全仓零命中)---- */

    /* 入场 stagger(CSS-only,零 JS)。参数逐字取 spec §4「40ms 步进
       ≤400ms」——`min(calc(var(--stagger-i,0) * 40ms), 400ms)` 用
       CSS min() 直接表达"步进但封顶",不必像 motion-lab.html demo 那样
       为固定 6 项手写 6 条 :nth-child 规则(那是一次性演示稿的写法,
       库件要接纳任意长度的列表);消费方给每个条目一个 0 起始的序号
       `style="--stagger-i: N"`。**A-536 五 failure 边界全保留**:
       ①终值文字与 aria 树全程不受影响(只动 opacity/transform,非
       文字内容)——不阻塞真值读取;②CSS animation 天然只在元素进入
       渲染树时播放一次,不因用户操作重放;③下方 reduced-motion 覆写
       直接清零动画,呈终态;④同①,没有滚动数字这一层,读屏与契约测试
       从渲染那一刻起读到的就是终值;⑤选择器只认 `.stagger-item` 这一个
       类,不碰 body/.app-main 等全局节点。时长走 --motion-duration 令牌
       (原型演示稿字面 200ms 是独立稿件不受本仓令牌纪律约束,库件版本
       必须遵守「屏内自定义时长=fail」硬规,故改令牌不是随意选择)。
       **print 兜底(Gate C R2 · M2 补,二选一里选的后一半——见 R2 判词
       「改『默认可见』同构形态,或补 print/fallback 路径」):base 声明
       本身是 opacity:0 起手,靠 animation forwards 播到终态——这与本
       文件 [data-chart-reveal] 那套"默认即终态,只有 .pre-reveal 这个
       后加的类才临时藏"的三条路径零回归写法方向相反。**未采用「默认
       可见」这一半的理由(独立复核已指出上一版这里的推理有误,
       在此订正,不是回避)**:`animation-fill-mode: backwards`/`both`
       确实是零 JS 就能达成"delay 期间维持隐藏关键帧、动画一旦不跑
       就回落到未声明的默认可见值"的手段——把这两个属性从 base 选择器
       挪进 keyframes、`forwards` 换 `both`,理论上能做到与
       [data-chart-reveal] 同构的"默认可见"。**没有改用它的原因是
       验证边界,不是它不存在**:当前这条 print 覆写的正确性只依赖
       普通 CSS 层叠(同特异度、源码序在后的规则生效)这一层——可
       用括号配对与选择器优先级直接推导,零歧义;而
       "print 渲染路径完全不跑 animation 时,fill-mode 的 backwards/both
       是否仍会把首尾关键帧当成层叠值应用"这一点,规范本身论述空间
       模糊、各引擎的 print 渲染管线是否真的按"动画属性生效但不播放"
       处理存在不确定性,即便单一引擎实测过,单引擎实测也不足以支撑
       "所有引擎都如此"的跨引擎结论,本刀未做这个规模的验证。
       **两条方案因此不是互斥的谁更优,是覆盖面不同**:「三条路径零
       回归」是 [data-chart-reveal] 自己那套机制里已具名的闭合三项
       (reduced-motion/无脚本/非首播,见上方原注)——.stagger-item
       即使没有本次 print 覆写也已满足这三项:reduced-motion 早有
       覆写(R1 交付时已在);"无脚本"对 CSS-only 的 .stagger-item
       天然不适用;"非首播"由 animation 只播一次天然覆盖(见五
       failure 边界②)。print 不是这三项里"欠缺的一项",是三项之外、
       同一纪律精神(内容不得永久不可见)下补的第四条,不是留了一个
       "未名场景"的开放缺口。
       `both`/`backwards` 同构写法理论上能覆盖"动画因任何原因不跑"
       这整个更大的类,但引擎行为未经验证。零消费方下选保守、可验证
       的一条,不是理论覆盖面更宽的一条——未来若要加 `both`,是在
       现有 print 覆写之上**叠加**,不是拿它**替换**掉 print 覆写:
       覆盖面更宽只是"理论上",没有多引擎实测撑住之前撤掉这条已用
       层叠推导验证过的 print 覆写会造成回归,**print 覆写本身
       不可撤**。 */
    .stagger-item { opacity: 0; transform: translateY(12px);
                     animation: v2-stagger-in var(--motion-duration) var(--motion-ease) forwards;
                     animation-delay: min(calc(var(--stagger-i, 0) * 40ms), 400ms); }
    @keyframes v2-stagger-in {
      from { opacity: 0; transform: translateY(12px); }
      to { opacity: 1; transform: none; }
    }
    @media (prefers-reduced-motion: reduce), print {
      .stagger-item { animation: none; opacity: 1; transform: none; }
    }

    /* View Transitions API 页转场(渐进增强;JS 触发端见
       core/static/core/chartkit.js `viewTransition()`,本条只管
       reduced-motion 下的浏览器伪元素退场——逐字取
       motion-lab.html §4 的三个 `::view-transition-*` 选择器,
       Firefox 等不支持该 API 的浏览器里这三条选择器不存在匹配对象,
       零副作用;支持该 API 但用户开了 reduced-motion 时,把浏览器
       原生的转场动画一并清零,不是只清零本仓自己的动效。) */
    @media (prefers-reduced-motion: reduce) {
      ::view-transition-group(*),
      ::view-transition-old(*),
      ::view-transition-new(*) { animation: none !important; }
    }

