/* SENYU AI 基础规范 tokens：色彩、字体、间距与网格、动态，合为一个文件。
   由 03-foundations/tokens/build.py 拼接生成，不要手改；改各自的源头后重新生成，再运行本脚本。
   字体文件另用一个 <link> 并列引入：type/webfont/fonts.css（不要 @import）。
   <html lang="zh-CN"> 必须写；中西间距与拉丁字距需要 type/src/zh.js。 */

/* ==== color.css  （源头 03-foundations/color/src/tokens.py） ==== */
/* SENYU AI 色彩令牌 v1.2（2026-10-20）
   由 03-foundations/color/src/tokens.py 生成，数值来自 01-identity/senyu/src/spec.py 与
   03-foundations/color/src/palette.py。不要手改，改源头后重新生成。
   灰阶：L(i) = L墨 + (L纸 − L墨) × (i / 10)^1.0798，色相 91.5°、色度 0.0083（纸）。
   界面只引用 --color-* 与 --data-*；--senyu-* 是原色，只给令牌和品牌图形用。*/

:root {
  --senyu-ink: #0A0A0B;
  --senyu-paper: #F4F2EC;
  --senyu-light: #FFFFFF;
  --senyu-graphite: #6E6C66;
  --senyu-rule: #D9D6CE;
  --senyu-divider: #A3A09A;
  --senyu-divider-dark: #4A4946;

  --senyu-n0: #0A0A0B;  /* 墨 */
  --senyu-n1: #1A1915;
  --senyu-n2: #2C2B26;
  --senyu-n3: #413F3A;
  --senyu-n4: #565550;
  --senyu-n5: #6E6C66;  /* 石墨 */
  --senyu-n6: #86857F;
  --senyu-n7: #A3A09A;  /* 组合分隔线 */
  --senyu-n8: #BBB9B4;
  --senyu-n9: #D9D6CE;  /* 界线 */
  --senyu-n10: #F4F2EC;  /* 纸 */
  --senyu-n9-5: #E5E3DE;  /* 浅色主题 fill：界线与纸的正中 */

  /* 焦点环：两个主题相同的几何。outline 的间隙透出所在的底；用 box-shadow 画时间隙色取 --focus-ring-gap-color */
  --focus-ring-width: 2px;
  --focus-ring-gap: 2px;

  /* 垂类光色：只在母品牌产品目录（含官网产品页里的目录条目）与路线图的垂类条目里用，
     以及条目卡片内附带的数据。图表一律用 --v-ramp-*（第 6 档就是 sRGB 光色），不用 --v-light。 */
  --senyu-v-jade: #5DE78D;
  --senyu-v-jade-depth: linear-gradient(to bottom, #FFFFFF 0.00%, #F3FDF5 3.95%, #E3FBE8 9.74%, #D2F8DA 16.49%, #C1F6CD 23.97%, #B0F3C1 32.04%, #A1F1B6 40.61%, #93EFAD 49.62%, #86EDA4 59.03%, #7AEB9D 68.80%, #6FE997 78.90%, #66E891 89.30%, #5DE78D 100.00%);
  --senyu-v-jade-depth-paper: linear-gradient(to bottom, #F4F2EC 0.00%, #E9F1E4 3.95%, #DAF1D9 9.74%, #CBF0CE 16.49%, #BBEFC3 23.97%, #ACEDB9 32.04%, #9EECB0 40.61%, #91EBA8 49.62%, #84EAA1 59.03%, #79E99A 68.80%, #6FE895 78.90%, #65E891 89.30%, #5DE78D 100.00%);
  --senyu-v-jade-glow: linear-gradient(to bottom, rgb(93 231 141 / 0) 0.00%, rgb(93 231 141 / 0.0879) 3.95%, rgb(93 231 141 / 0.2046) 9.74%, rgb(93 231 141 / 0.325) 16.49%, rgb(93 231 141 / 0.4405) 23.97%, rgb(93 231 141 / 0.5472) 32.04%, rgb(93 231 141 / 0.6432) 40.61%, rgb(93 231 141 / 0.7278) 49.62%, rgb(93 231 141 / 0.8014) 59.03%, rgb(93 231 141 / 0.8644) 68.80%, rgb(93 231 141 / 0.9178) 78.90%, rgb(93 231 141 / 0.9627) 89.30%, rgb(93 231 141 / 1) 100.00%);
  --senyu-v-jade-ramp-1: #336844;
  --senyu-v-jade-ramp-2: #3A7E50;
  --senyu-v-jade-ramp-3: #42945E;
  --senyu-v-jade-ramp-4: #4BAE6C;
  --senyu-v-jade-ramp-5: #53C97C;
  --senyu-v-jade-ramp-6: #5DE78D;
}

/* 浅色主题（默认） */
:root, [data-theme="light"] {
  --color-bg: #F4F2EC;
  --color-surface: #F4F2EC;
  --color-fill: #E5E3DE;
  --color-fill-press: #D9D6CE;
  --color-text-1: #0A0A0B;
  --color-text-2: #6E6C66;
  --color-text-2-on-fill: #565550;
  --color-text-3: #86857F;
  --color-text-disabled: #A3A09A;
  --color-link: var(--color-text-1);
  --color-link-underline: #6E6C66;
  --color-link-hover: var(--color-text-1);
  --color-border: #D9D6CE;
  --color-border-control: #6E6C66;
  --color-focus: #0A0A0B;
  --color-selection: #BBB9B4;
  --color-inverse: #0A0A0B;
  --color-on-inverse: #F4F2EC;
  --color-error: #A5473F;
  --color-warning: #0A0A0B;
  --color-success: #0A0A0B;
  --color-info: #6E6C66;
  --data-mark: #86857F;
  --data-emphasis: #0A0A0B;
  --data-context: #BBB9B4;
  --data-track: #D9D6CE;
  --data-axis: #A3A09A;
  --data-grid: #D9D6CE;
  --data-label: #6E6C66;
  --data-value: #0A0A0B;
  --data-anomaly: #0A0A0B;
  --data-series-1: #0A0A0B;
  --data-series-2: #6E6C66;
  --data-series-3: #86857F;
  --focus-ring-gap-color: var(--color-bg);   /* 在主题块里声明，深浅各取自己的底 */
  color-scheme: light;
}

/* 深色主题：跟随系统，或用 data-theme 指定。不是反转，每个令牌重新按对比度取档 */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --color-bg: #0A0A0B;
    --color-surface: #1A1915;
    --color-fill: #2C2B26;
    --color-fill-press: #413F3A;
    --color-text-1: #F4F2EC;
    --color-text-2: #A3A09A;
    --color-text-2-on-fill: #BBB9B4;
    --color-text-3: #86857F;
    --color-text-disabled: #565550;
    --color-link: var(--color-text-1);
    --color-link-underline: #86857F;
    --color-link-hover: var(--color-text-1);
    --color-border: #413F3A;
    --color-border-control: #86857F;
    --color-focus: #FFFFFF;
    --color-selection: #413F3A;
    --color-inverse: #F4F2EC;
    --color-on-inverse: #0A0A0B;
    --color-error: #A9766F;
    --color-warning: #F4F2EC;
    --color-success: #F4F2EC;
    --color-info: #A3A09A;
    --data-mark: #6E6C66;
    --data-emphasis: #FFFFFF;
    --data-context: #413F3A;
    --data-track: #2C2B26;
    --data-axis: #565550;
    --data-grid: #2C2B26;
    --data-label: #A3A09A;
    --data-value: #F4F2EC;
    --data-anomaly: #F4F2EC;
    --data-series-1: #FFFFFF;
    --data-series-2: #86857F;
    --data-series-3: #6E6C66;
    --focus-ring-gap-color: var(--color-bg);   /* 在主题块里声明，深浅各取自己的底 */
    color-scheme: dark;
  }
}
[data-theme="dark"] {
  --color-bg: #0A0A0B;
  --color-surface: #1A1915;
  --color-fill: #2C2B26;
  --color-fill-press: #413F3A;
  --color-text-1: #F4F2EC;
  --color-text-2: #A3A09A;
  --color-text-2-on-fill: #BBB9B4;
  --color-text-3: #86857F;
  --color-text-disabled: #565550;
  --color-link: var(--color-text-1);
  --color-link-underline: #86857F;
  --color-link-hover: var(--color-text-1);
  --color-border: #413F3A;
  --color-border-control: #86857F;
  --color-focus: #FFFFFF;
  --color-selection: #413F3A;
  --color-inverse: #F4F2EC;
  --color-on-inverse: #0A0A0B;
  --color-error: #A9766F;
  --color-warning: #F4F2EC;
  --color-success: #F4F2EC;
  --color-info: #A3A09A;
  --data-mark: #6E6C66;
  --data-emphasis: #FFFFFF;
  --data-context: #413F3A;
  --data-track: #2C2B26;
  --data-axis: #565550;
  --data-grid: #2C2B26;
  --data-label: #A3A09A;
  --data-value: #F4F2EC;
  --data-anomaly: #F4F2EC;
  --data-series-1: #FFFFFF;
  --data-series-2: #86857F;
  --data-series-3: #6E6C66;
  --focus-ring-gap-color: var(--color-bg);   /* 在主题块里声明，深浅各取自己的底 */
  color-scheme: dark;
}

/* 由白入色在 OKLab 里插值，与规则的混色空间一致；不支持插值提示的浏览器用上面的 sRGB 停点 */
@supports (background: linear-gradient(in oklab, #000, #fff)) {
  :root {
    --senyu-v-jade-depth: linear-gradient(in oklab to bottom, #FFFFFF 0.00%, #F3FDF5 3.95%, #E3FBE8 9.74%, #D2F8DA 16.49%, #C1F6CD 23.97%, #B0F3C1 32.04%, #A1F1B6 40.61%, #93EFAD 49.62%, #86EDA4 59.03%, #7AEB9D 68.80%, #6FE997 78.90%, #66E891 89.30%, #5DE78D 100.00%);
    --senyu-v-jade-depth-paper: linear-gradient(in oklab to bottom, #F4F2EC 0.00%, #E9F1E4 3.95%, #DAF1D9 9.74%, #CBF0CE 16.49%, #BBEFC3 23.97%, #ACEDB9 32.04%, #9EECB0 40.61%, #91EBA8 49.62%, #84EAA1 59.03%, #79E99A 68.80%, #6FE895 78.90%, #65E891 89.30%, #5DE78D 100.00%);
    --senyu-v-jade-glow: linear-gradient(in oklab to bottom, rgb(93 231 141 / 0) 0.00%, rgb(93 231 141 / 0.0879) 3.95%, rgb(93 231 141 / 0.2046) 9.74%, rgb(93 231 141 / 0.325) 16.49%, rgb(93 231 141 / 0.4405) 23.97%, rgb(93 231 141 / 0.5472) 32.04%, rgb(93 231 141 / 0.6432) 40.61%, rgb(93 231 141 / 0.7278) 49.62%, rgb(93 231 141 / 0.8014) 59.03%, rgb(93 231 141 / 0.8644) 68.80%, rgb(93 231 141 / 0.9178) 78.90%, rgb(93 231 141 / 0.9627) 89.30%, rgb(93 231 141 / 1) 100.00%);
  }
}

/* Display P3：只换光色本身（平涂、由白入色、光晕），不放在 @supports 里，支持 P3 的浏览器都拿得到。
   P3 停点不写插值提示：CSS Color 4 规定含非传统颜色的渐变默认就在 OKLab 里插值。
   中性色、状态色与数据色（含 --v-ramp-6）不进 P3：图表、截图与打印一致。所以在 P3 屏上，
   同一张卡片里条目的光色比图表最亮的一档更饱和，这是允许的差别。 */
@media (color-gamut: p3) {
  :root {
    --senyu-v-jade: color(display-p3 0.3603 0.9306 0.4974);
    --senyu-v-jade-depth: linear-gradient(to bottom, color(display-p3 1.0000 1.0000 1.0000) 0.00%, color(display-p3 0.9522 0.9967 0.9568) 3.95%, color(display-p3 0.8878 0.9915 0.8994) 9.74%, color(display-p3 0.8199 0.9850 0.8401) 16.49%, color(display-p3 0.7529 0.9778 0.7831) 23.97%, color(display-p3 0.6887 0.9704 0.7301) 32.04%, color(display-p3 0.6286 0.9631 0.6820) 40.61%, color(display-p3 0.5729 0.9561 0.6393) 49.62%, color(display-p3 0.5217 0.9496 0.6017) 59.03%, color(display-p3 0.4751 0.9439 0.5692) 68.80%, color(display-p3 0.4328 0.9388 0.5412) 78.90%, color(display-p3 0.3946 0.9343 0.5174) 89.30%, color(display-p3 0.3603 0.9306 0.4974) 100.00%);
    --senyu-v-jade-depth-paper: linear-gradient(to bottom, color(display-p3 0.9555 0.9493 0.9278) 0.00%, color(display-p3 0.9122 0.9504 0.8912) 3.95%, color(display-p3 0.8536 0.9509 0.8424) 9.74%, color(display-p3 0.7915 0.9505 0.7920) 16.49%, color(display-p3 0.7298 0.9491 0.7432) 23.97%, color(display-p3 0.6704 0.9471 0.6979) 32.04%, color(display-p3 0.6143 0.9446 0.6566) 40.61%, color(display-p3 0.5622 0.9420 0.6199) 49.62%, color(display-p3 0.5139 0.9393 0.5875) 59.03%, color(display-p3 0.4697 0.9368 0.5594) 68.80%, color(display-p3 0.4295 0.9345 0.5352) 78.90%, color(display-p3 0.3930 0.9324 0.5147) 89.30%, color(display-p3 0.3603 0.9306 0.4974) 100.00%);
    --senyu-v-jade-glow: linear-gradient(to bottom, color(display-p3 0.3603 0.9306 0.4974 / 0) 0.00%, color(display-p3 0.3603 0.9306 0.4974 / 0.0879) 3.95%, color(display-p3 0.3603 0.9306 0.4974 / 0.2046) 9.74%, color(display-p3 0.3603 0.9306 0.4974 / 0.325) 16.49%, color(display-p3 0.3603 0.9306 0.4974 / 0.4405) 23.97%, color(display-p3 0.3603 0.9306 0.4974 / 0.5472) 32.04%, color(display-p3 0.3603 0.9306 0.4974 / 0.6432) 40.61%, color(display-p3 0.3603 0.9306 0.4974 / 0.7278) 49.62%, color(display-p3 0.3603 0.9306 0.4974 / 0.8014) 59.03%, color(display-p3 0.3603 0.9306 0.4974 / 0.8644) 68.80%, color(display-p3 0.3603 0.9306 0.4974 / 0.9178) 78.90%, color(display-p3 0.3603 0.9306 0.4974 / 0.9627) 89.30%, color(display-p3 0.3603 0.9306 0.4974 / 1) 100.00%);
  }
}

[data-vertical="jade"] {
  --v-light: var(--senyu-v-jade);
  --v-depth: var(--senyu-v-jade-depth);
  --v-depth-paper: var(--senyu-v-jade-depth-paper);
  --v-glow: var(--senyu-v-jade-glow);
  --v-ramp-1: var(--senyu-v-jade-ramp-1);
  --v-ramp-2: var(--senyu-v-jade-ramp-2);
  --v-ramp-3: var(--senyu-v-jade-ramp-3);
  --v-ramp-4: var(--senyu-v-jade-ramp-4);
  --v-ramp-5: var(--senyu-v-jade-ramp-5);
  --v-ramp-6: var(--senyu-v-jade-ramp-6);
}

/* ---- 垂类光色的 CSS 画法：只用于矩形色条（图例、进度、示意） --------------------------------
   目录与路线图里的垂类条目不用这些类：一律用 01-identity/senyu/verticals/catalog-{key}*.svg，
   或按色彩规范第 05 节的内联 SVG 写法（光晕要沿斜的折射段走，并裁在墨方块以内，CSS 盒子做不到）。
   渐变元素的上边必须正好是接触线，下边正好是落点。--v-a 是条目方块边长，光晕 σ = 0.025 A 与 0.09 A。
   光色只画光和数据，不排字：色条旁的数值与标签用纸色或 n7。 */
.v-flat           { background: var(--v-light); }
.v-depth          { background: var(--v-depth); }         /* 深色底：起点是白光 */
.v-depth-on-paper { background: var(--v-depth-paper); }   /* 纸上的墨方块里：起点是纸色 */

/* 光晕只在屏幕、只在墨上。父容器写 data-ground="ink"（底必须是墨 n0），光晕溢出的部分由它裁掉；
   别处（纸上、浅色主题、打印）.v-glow 只是一根 .v-depth，不发光。 */
.v-glow { position: relative; isolation: isolate; }
.v-glow::before, .v-glow::after {
  content: none; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background: var(--v-glow);
}
[data-ground="ink"]:not(html, body):has(.v-glow) { overflow: clip; }   /* 写在色块上，不写在 html、body 上 */
[data-ground="ink"] .v-glow::before, [data-ground="ink"] .v-glow::after { content: ""; }
[data-ground="ink"] :is([data-ground="paper"], [data-theme="light"]) .v-glow::before,
[data-ground="ink"] :is([data-ground="paper"], [data-theme="light"]) .v-glow::after { content: none; }
.v-glow::before { filter: blur(calc(var(--v-a, 240px) * 0.025)); opacity: 0.55; }
.v-glow::after  { filter: blur(calc(var(--v-a, 240px) * 0.09));  opacity: 0.35; }
@media print {
  .v-glow::before, .v-glow::after { content: none !important; }
  .v-depth, .v-depth-on-paper { background: var(--v-light); }
}

/* ---- 一屏一光 ---------------------------------------------------------------------------------
   白光在深色界面里只给两样：图表的唯一强调（--data-emphasis）与焦点环（--color-focus）。
   一屏多张深色图表时，只有最重要的一张用 --data-emphasis，其余用 --data-value（纸色）强调。
   焦点环是暂时状态，不计入；但焦点落在图表卡片（data-chart）里时改用纸色，白光只留给数据。 */
[data-chart] { --color-focus: var(--data-value); }   /* 与主题块同一优先级，写在其后 */

/* ---- 焦点环：所有可聚焦组件同一条 ---------------------------------------------------------- */
:where(a, button, input, select, textarea, [tabindex]):focus-visible {
  outline: var(--focus-ring-width) solid var(--color-focus);
  outline-offset: var(--focus-ring-gap);
}

/* ==== type.css  （源头 03-foundations/type/src/build_css.py） ==== */
/* SENYU AI 字体 tokens
   规范：03-foundations/type/SPEC.md        字样：03-foundations/type/index.html
   生成：03-foundations/type/src/build_css.py（数字来自 scale.json、metrics.json 与 webfont/manifest.json，勿手改）

   用法（SPEC 5.2）：<html lang="zh-CN">，然后并列两个 <link rel="stylesheet">，先 type/webfont/fonts.css，后本文件。
   lang 必须写：Noto Sans SC 只有在 zh-CN 下才把破折号换成整字宽（locl），标点也按大陆规范定位。
   拉丁字母不加字距、中西间距正好一个空格，需要再引入 type/src/zh.js（没有它时间距仍对，只是拉丁字母带字距）。 */

:root {
  /* 字体：中文为主。拉丁字母与数字在中文里来自 Inter（放大 8 %），空格与“”‘’…—来自 Noto。 */
  --font-sans: "SENYU Latin Mix", "SENYU Han", sans-serif;
  --font-latin: "SENYU Latin", "SENYU Han", sans-serif;  /* 纯英文段落，:lang(en) 自动切换；夹着的中文仍由 Noto 排 */
  --font-mono: "SENYU Mono", "SENYU Han", monospace;     /* 编号、色值、版本、代码、时间戳 */
  --font-serif: "SENYU Serif", serif;                    /* 只用于引语；不退到无衬线，一句话里不混两种字 */

  /* 字号：√2 阶梯，网页 390 → 1440 线性过渡。其余媒介的固定数值见 SPEC.md 第 3 节。
     演示、A4、界面样张用同一组类，只在容器上改 --fs-* 与 --line-unit。 */
  --fs-label: clamp(12px, 0.7268rem + 0.0952vw, 13px);   /* 标签 12→13 px，行距 22→22 */
  --fs-small: clamp(13px, 0.7661rem + 0.1905vw, 15px);   /* 小字 13→15 px，行距 22→26 */
  --fs-body: clamp(16px, 0.9536rem + 0.1905vw, 18px);   /* 正文 16→18 px，行距 28→32 */
  --fs-h3: clamp(23px, 1.3911rem + 0.1905vw, 25px);   /* 小标题 23→25 px，行距 36→38 */
  --fs-h2: clamp(32px, 1.9071rem + 0.3810vw, 36px);   /* 标题 32→36 px，行距 44→50 */
  --fs-h1: clamp(45px, 2.6732rem + 0.5714vw, 51px);   /* 大标题 45→51 px，行距 58→64 */
  --fs-display: clamp(54px, 2.6321rem + 3.0476vw, 86px);   /* 展示 54→86 px，行距 66→104 */

  /* 补充档（SPEC 3.6）：只在 1080 母版的固定画幅（演示、海报、社交大图）上用，单位是母版 px，不进网页的流式阶梯。
     演示大字是展示 96 的整档 √2，取到 8 px；巨大数字的四分之一档按需在页面里写，标明 k。 */
  --fs-note: 16px;   /* 注记档 k -1.5，行距 24；演示的第二层 */
  --fs-big-1: 136px;   /* k 4.5，行距 168 */
  --fs-big-2: 192px;   /* k 5.5，行距 240 */
  --fs-big-3: 272px;   /* k 6.5，行距 336 */
  --fs-big-4: 384px;   /* k 7.5，行距 468 */
  --fs-big-5: 544px;   /* k 8.5，行距 660 */

  /* 行距 = 字号 + 0.75 × min(字号, 正文字号)，取 --line-unit 的整数倍，且不低于 1.2 倍。 */
  --lead: 0.75;
  --lead-floor: 1.2;
  --line-unit: 2px;

  /* 字距只给中文（SPEC 3.3）。拉丁字母由 zh.js 包进 .t-la，字距归零；展示档同收。 */
  --track-label: 0.08em;
  --track-body: 0.02em;
  --track-heading: 0.0em;
  --track-display: -0.01em;
  --track-mono: 0.06em;

  /* 字重：三档。深色底各减 20（与 SENYU 字标深色版 600 → 580 同一做法），见 .t-on-dark。 */
  --w-text: 400;
  --w-ui: 500;
  --w-head: 700;

  /* 标点挤压：只看排法，不看字号。手工断行的文字（data-zh="manual"）行内，右侧空白 ≥ 0.5 em 的标点
     后一字左移 0.38 em（zh.js）；浏览器自动断行的段落保留全角。 */
  --squeeze: -0.38em;
}

.t-on-dark { --w-text: 380; --w-ui: 480; --w-head: 680; }

/* 基础：零优先级，任何页面样式都能覆盖。 */
:where(html) {
  font-family: var(--font-sans);
  font-size: 100%;
  font-kerning: normal;
  font-optical-sizing: auto;
  text-rendering: optimizeLegibility;
  -webkit-text-size-adjust: 100%;
}
:where(:lang(en)) { font-family: var(--font-latin); }

/* 行距公式，所有文字角色共用 */
:where([class^="t-"], [class*=" t-"]) {
  line-height: calc(1em + var(--lead) * min(1em, var(--fs-body)));   /* 不支持 round() 的浏览器 */
  line-height: max(
    round(nearest, calc(1em + var(--lead) * min(1em, var(--fs-body))), var(--line-unit)),
    round(up, calc(var(--lead-floor) * 1em), var(--line-unit))
  );
}

/* 段落规则：中文禁则、行首开引号去半格、末行不留孤字 */
.t-body, .t-small, .t-quote {
  line-break: strict;
  word-break: normal;
  overflow-wrap: break-word;
  text-spacing-trim: trim-start;
  text-autospace: normal;               /* 漏打空格时的保底（1/8 em）；正文应当打空格，见 SPEC 2.2 */
  text-wrap: pretty;
}

.t-display { --track: var(--track-display); --track-la: var(--track-display); font-size: var(--fs-display); font-weight: var(--w-head); text-spacing-trim: trim-both; text-wrap: balance; }
.t-h1      { --track: var(--track-heading); font-size: var(--fs-h1); font-weight: var(--w-head); text-spacing-trim: trim-both; text-wrap: balance; }
.t-h2      { --track: var(--track-heading); font-size: var(--fs-h2); font-weight: var(--w-head); text-spacing-trim: trim-both; text-wrap: balance; }
.t-h3      { --track: var(--track-heading); font-size: var(--fs-h3); font-weight: var(--w-head); text-spacing-trim: trim-both; }
.t-body    { --track: var(--track-body);    font-size: var(--fs-body);  font-weight: var(--w-text); }
.t-small   { --track: var(--track-body);    font-size: var(--fs-small); font-weight: var(--w-text); }
.t-label   { --track: var(--track-label);   font-size: var(--fs-label); font-weight: var(--w-ui); }

/* 字距与中西间距（SPEC 2.2、3.3）。字距加在每个字后面，空格两侧各多一份；word-spacing 把这两份扣回，
   所以“中文 + 空格 + 拉丁”之间正好是 Noto 的一个空格。.t-la 是 zh.js 包的拉丁字母：字距归零，
   末尾补回一份中文字距，让右侧间距也正好一个空格。各档实测见 SPEC 2.2。 */
:where(.t-display, .t-h1, .t-h2, .t-h3, .t-body, .t-small, .t-label, .t-quote) {
  letter-spacing: var(--track);
  word-spacing: calc(var(--space-fix, 0em) - 2 * var(--track));
}
.t-la {
  letter-spacing: var(--track-la, 0em);
  word-spacing: calc(var(--space-fix, 0em) - 2 * var(--track-la, 0em));
  margin-inline-end: calc(var(--track, 0em) - var(--track-la, 0em));
}
:is(.t-display, .t-h1, .t-h2, .t-h3, .t-body, .t-small, .t-label):lang(en) { --track: 0em; --track-la: 0em; }

/* 单行短语：标点半宽（SPEC 2.3）。.t-ui 是界面（再加字重 500），.t-halt 给表格的数据格与短标签。
   成句的说明（自动断行）不加，按正文排。Inter 的字形一律用默认：小写 l 与 Abel 标志一致，不开 cv05。 */
.t-ui      { font-weight: var(--w-ui); font-feature-settings: "halt" 1; }
.t-halt    { font-feature-settings: "halt" 1; }
:where(table) { font-variant-numeric: tabular-nums; }

/* 机器的字 */
.t-mono { font-family: var(--font-mono); font-weight: 400; letter-spacing: var(--track-mono); word-spacing: 0; }
.t-mono-caps { font-family: var(--font-mono); font-weight: 400; letter-spacing: var(--track-mono); word-spacing: 0; text-transform: uppercase; }
code, kbd, samp, pre { font-family: var(--font-mono); font-size: 0.9em; letter-spacing: 0; word-spacing: 0; text-spacing-trim: space-all; text-autospace: no-autospace; }   /* 代码原样排，不调标点 */

/* 人读的数：等宽数字，用于表格、对比与会跳动的数 */
.t-num { font-variant-numeric: tabular-nums; }

/* 引语：别人说的话。Noto Serif SC 的空格 0.255 em，比无衬线宽；--space-fix 把它收回到 0.225 em。 */
.t-quote { --track: var(--track-body); --space-fix: -0.03em; font-family: var(--font-serif); font-weight: 500; }

/* 中西混排的小工具 */
.t-sq { margin-inline-end: var(--squeeze); }   /* 挤压的标点（zh.js） */
.t-line { display: block; }                   /* 手工断行的一行 */
.t-name { white-space: nowrap; }              /* SENYU AI、璞玉 Jade 整体不断行 */
.t-keep { white-space: nowrap; }              /* 品牌主句的一个分句不跨行（SPEC 2.5） */

/* 竖排（SPEC 2.8）：只用于海报、封面、章节页，手工断行。引号用方案 B：横排的弯引号侧转 90°，
   每个引号占 0.7 字高、居中；冒号、分号直立，按字体的竖排位置落在字下偏右（待按 GB/T 15834 原文核对）。
   <div class="t-vert">他说<span class="t-vp">：</span><span class="t-vq o"><span>“</span></span>先去现场<span class="t-vq c"><span>”</span></span>。</div>
   两位以内的数字直立（<span class="t-vn">21</span>），三位以上与拉丁字母随 mixed 整体侧转。 */
.t-vert { writing-mode: vertical-rl; text-orientation: mixed; text-spacing-trim: normal; }
.t-vert .t-vp { text-orientation: upright; }
.t-vert .t-vn { text-combine-upright: all; }
.t-vert .t-vq { display: inline-flex; writing-mode: horizontal-tb; width: 1em; height: 0.7em; align-items: center; justify-content: center; line-height: 1; vertical-align: top; }
.t-vert .t-vq > span { display: block; transform: rotate(90deg) translate(var(--vq-x, 0em), 0.42em); }
.t-vert .t-vq.o { --vq-x: -0.18em; }
.t-vert .t-vq.c { --vq-x: 0.18em; }

/* ==== space.css  （源头 03-foundations/grid/src/tokens.py） ==== */
/* SENYU AI 间距与网格 tokens
   规范：03-foundations/grid/SPEC.md        示范：03-foundations/grid/index.html
   生成：03-foundations/grid/src/tokens.py（数字来自 src/canvases.py，勿手改）

   一个单位 U = 12 px。固定画幅 U = 短边 ÷ 90，屏幕画幅以短边 1080 为母版，所以也是 12。
   间距阶梯是 {1, 1.5} × 2^k 个 U：相邻两档交替 × 1.5、× 4/3，每两档 × 2，平均每档 √2，
   与字号阶梯（type/SPEC.md 3.1）、A 系列纸张是同一个比例。名字就是 U 的倍数：--space-6 = 6U = 72 px。 */

:root {
  --u: 12px;
  --space-hair: 2px;   /* U/6：焦点环与底色间隙、光学微调；也是字体规范的屏幕行距单位 */
  --space-0-5: 6px;
  --space-0-75: 9px;
  --space-1: 12px;
  --space-1-5: 18px;
  --space-2: 24px;
  --space-3: 36px;
  --space-4: 48px;
  --space-6: 72px;
  --space-8: 96px;
  --space-12: 144px;
  --space-16: 192px;
  --space-24: 288px;

  /* 网页网格：手机 4 栏（< 768） */
  --grid-cols: 4;
  --grid-margin: 24px;      /* 2U */
  --grid-gutter: 18px;      /* 1.5U；4 栏的栏宽在 390 上正好 72 = 6U */
  --grid-max: 1296px;      /* 版心上限：1440 − 2 × 72 */
  --nav-h: 48px;           /* 字标或 24 px 像素组合居中，上下各 12（= 组合保护区） */
  --section-gap: 72px;     /* 6U */
  --nav-cap: 14px;         /* 首屏有超级图形时导航只放字标 wordmark-senyu-ai-ink，大写高 14（= 24 px 像素组合里的大写高） */
  --lockup-nav: 24px;      /* 首屏离开视口后换 lockup-horizontal-24-ink.svg，所有宽度同一个 */
  --clear-square: calc(4 * var(--u));   /* 纸上文字离墨方块 ≥ 4U = 48 px，所有宽度相同（竖版首屏） */
}

/* 平板 768–1199：8 栏。断点只换栏数、边距、栏距、导航高与区块间距，阶梯不变 */
@media (min-width: 768px) {
  :root {
    --grid-cols: 8;
    --grid-margin: 48px;    /* 4U */
    --grid-gutter: 24px;    /* 2U */
    --nav-h: 72px;         /* 6U：字标或组合居中，上下各 24 以上 */
    --section-gap: 96px;   /* 8U */
  }
}

/* 桌面 ≥ 1200：12 栏；1440 上栏宽 86，1440 以上版心停在 1296，边距变宽 */
@media (min-width: 1200px) {
  :root {
    --grid-cols: 12;
    --grid-margin: 72px;    /* 6U，与所有固定画幅的边距同一个数 */
    --section-gap: 144px;  /* 12U */
  }
}

/* 首屏用横版规则：看首屏这一段的宽高，与固定画幅同一条规则。宽 ≥ 1200，或宽 ≥ 1024 且横屏（平板横放）。
   只有这里才有 --hero-*：竖版首屏的文字在纸上，用 --clear-square。 */
@media (min-width: 1200px), (min-width: 1024px) and (orientation: landscape) {
  :root {
    --hero-a: 720px;          /* 60U，方块贴版心右缘（1440 以上跟着版心，不贴视口） */
    --hero-clear: 60px;      /* 文字离光 C：w = 0.0697 × 720 = 50.16，取到 5U；只在这里成立 */
    --hero-text-top: 264px;  /* 标题行框顶 22U：接触线 295.34 以上 2U，取到 U */
  }
}

/* 固定画幅：每种画幅一组，作用域是 [data-canvas]（短边 1080 的母版，单位 px；A4、A3 的实际尺寸与 pt 字号见 grid.json）。
   不在 :root 上给默认值：16 : 9 的 36 / 108 拿到 1 : 1 上就是错的。 */
[data-canvas="poster-3x4"] {   /* 竖版海报 3 : 4 */
  --canvas-u: 12px;   /* 短边 ÷ 90 */
  --canvas-margin: 72px;   /* 6U */
  --canvas-gutter: 24px;   /* 2U */
  --canvas-cols: 6;
  --canvas-col: 136px;
  --canvas-lockup: 48px;   /* 页眉组合方块 4U（SPEC 1.5） */
  --canvas-header-baseline: 120px;   /* 安全区上缘 + 组合方块 */
  --canvas-footer-baseline: 1368px;   /* 安全区下缘以上最近的 U 线 */
  --canvas-safe-top: 72px;
  --canvas-safe-right: 72px;
  --canvas-safe-bottom: 72px;
  --canvas-safe-left: 72px;
  --canvas-clear: 72px;   /* 文字离光 C，6U（竖版规则） */
}
[data-canvas="a4"] {   /* A4 印刷 */
  --canvas-u: 12px;   /* 短边 ÷ 90 */
  --canvas-margin: 72px;   /* 6U */
  --canvas-gutter: 24px;   /* 2U */
  --canvas-cols: 6;
  --canvas-col: 136px;
  --canvas-lockup: 36px;   /* 页眉组合方块 3U（SPEC 1.5） */
  --canvas-header-baseline: 108px;   /* 安全区上缘 + 组合方块 */
  --canvas-footer-baseline: 1452px;   /* 安全区下缘以上最近的 U 线 */
  --canvas-safe-top: 72px;
  --canvas-safe-right: 72px;
  --canvas-safe-bottom: 72px;
  --canvas-safe-left: 72px;
  --canvas-clear: 72px;   /* 文字离光 C，6U（竖版规则） */
}
[data-canvas="a3"] {   /* A3 印刷 */
  --canvas-u: 12px;   /* 短边 ÷ 90 */
  --canvas-margin: 72px;   /* 6U */
  --canvas-gutter: 24px;   /* 2U */
  --canvas-cols: 6;
  --canvas-col: 136px;
  --canvas-lockup: 36px;   /* 页眉组合方块 3U（SPEC 1.5） */
  --canvas-header-baseline: 108px;   /* 安全区上缘 + 组合方块 */
  --canvas-footer-baseline: 1452px;   /* 安全区下缘以上最近的 U 线 */
  --canvas-safe-top: 72px;
  --canvas-safe-right: 72px;
  --canvas-safe-bottom: 72px;
  --canvas-safe-left: 72px;
  --canvas-clear: 72px;   /* 文字离光 C，6U（竖版规则） */
}
[data-canvas="slide-16x9"] {   /* 演示 16 : 9 */
  --canvas-u: 12px;   /* 短边 ÷ 90 */
  --canvas-margin: 72px;   /* 6U */
  --canvas-gutter: 24px;   /* 2U */
  --canvas-cols: 12;
  --canvas-col: 126px;
  --canvas-lockup: 36px;   /* 页眉组合方块 3U（SPEC 1.5） */
  --canvas-header-baseline: 108px;   /* 安全区上缘 + 组合方块 */
  --canvas-footer-baseline: 1008px;   /* 安全区下缘以上最近的 U 线 */
  --canvas-safe-top: 72px;
  --canvas-safe-right: 72px;
  --canvas-safe-bottom: 72px;
  --canvas-safe-left: 72px;
  --canvas-clear: 84px;   /* 文字离光 C，7U（横版规则） */
  --canvas-text-right: 792px;   /* 横版文字右界 = 方块左缘 − 4U */
}
[data-canvas="social-1x1"] {   /* 社交 1 : 1 */
  --canvas-u: 12px;   /* 短边 ÷ 90 */
  --canvas-margin: 72px;   /* 6U */
  --canvas-gutter: 24px;   /* 2U */
  --canvas-cols: 6;
  --canvas-col: 136px;
  --canvas-lockup: 48px;   /* 页眉组合方块 4U（SPEC 1.5） */
  --canvas-header-baseline: 120px;   /* 安全区上缘 + 组合方块 */
  --canvas-footer-baseline: 1008px;   /* 安全区下缘以上最近的 U 线 */
  --canvas-safe-top: 72px;
  --canvas-safe-right: 72px;
  --canvas-safe-bottom: 72px;
  --canvas-safe-left: 72px;
  --canvas-clear: 84px;   /* 文字离光 C，7U（横版规则） */
}
[data-canvas="story-9x16"] {   /* 竖屏 9 : 16 */
  --canvas-u: 12px;   /* 短边 ÷ 90 */
  --canvas-margin: 72px;   /* 6U */
  --canvas-gutter: 24px;   /* 2U */
  --canvas-cols: 6;
  --canvas-col: 136px;
  --canvas-lockup: 48px;   /* 页眉组合方块 4U（SPEC 1.5） */
  --canvas-header-baseline: 288px;   /* 安全区上缘 + 组合方块 */
  --canvas-footer-baseline: 1560px;   /* 安全区下缘以上最近的 U 线 */
  --canvas-safe-top: 240px;
  --canvas-safe-right: 72px;
  --canvas-safe-bottom: 360px;
  --canvas-safe-left: 72px;
  --canvas-clear: 72px;   /* 文字离光 C，6U（竖版规则） */
}
[data-canvas="banner-2x1"] {   /* 横幅 2 : 1 */
  --canvas-u: 12px;   /* 短边 ÷ 90 */
  --canvas-margin: 72px;   /* 6U */
  --canvas-gutter: 24px;   /* 2U */
  --canvas-cols: 12;
  --canvas-col: 146px;
  --canvas-lockup: 96px;   /* 页眉组合方块 8U（SPEC 1.5） */
  --canvas-header-baseline: 168px;   /* 安全区上缘 + 组合方块 */
  --canvas-footer-baseline: 1008px;   /* 安全区下缘以上最近的 U 线 */
  --canvas-safe-top: 72px;
  --canvas-safe-right: 120px;
  --canvas-safe-bottom: 72px;
  --canvas-safe-left: 120px;
  --canvas-clear: 84px;   /* 文字离光 C，7U（横版规则） */
  --canvas-text-right: 1032px;   /* 横版文字右界 = 方块左缘 − 4U */
}
[data-canvas="banner-3x1"] {   /* 横幅 3 : 1 */
  --canvas-u: 12px;   /* 短边 ÷ 90 */
  --canvas-margin: 72px;   /* 6U */
  --canvas-gutter: 24px;   /* 2U */
  --canvas-cols: 20;
  --canvas-col: 132px;
  --canvas-lockup: 144px;   /* 页眉组合方块 12U（SPEC 1.5） */
  --canvas-header-baseline: 360px;   /* 安全区上缘 + 组合方块 */
  --canvas-footer-baseline: 864px;   /* 安全区下缘以上最近的 U 线 */
  --canvas-safe-top: 216px;
  --canvas-safe-right: 72px;
  --canvas-safe-bottom: 216px;
  --canvas-safe-left: 72px;
  --canvas-clear: 84px;   /* 文字离光 C，7U（横版规则） */
  --canvas-text-right: 2112px;   /* 横版文字右界 = 方块左缘 − 4U */
}

/* 布局：零优先级，页面样式可以覆盖 */
:where(.l-wrap) {
  box-sizing: border-box;
  width: 100%;
  max-width: calc(var(--grid-max) + 2 * var(--grid-margin));
  margin-inline: auto;
  padding-inline: var(--grid-margin);
}
:where(.l-grid) {
  display: grid;
  grid-template-columns: repeat(var(--grid-cols), minmax(0, 1fr));
  column-gap: var(--grid-gutter);
}
:where(.l-full) { grid-column: 1 / -1; }
:where(.l-section) { margin-top: var(--section-gap); }
:where(.l-stack) > * + * { margin-top: var(--stack, var(--space-2)); }
:where(.l-nav) {
  height: var(--nav-h);
  display: flex;
  align-items: center;
  justify-content: space-between;
}

/* 网格叠层：任何 .l-wrap 加 data-grid 属性即显示栏与 12 px 横线（调试用，不上线）。纸上用构造标注色 #C8412B（spec.py RED）。 */
.l-wrap[data-grid] {
  background-image:
    linear-gradient(to right, rgb(200 65 43 / 0.08) 0 calc(100% - var(--grid-gutter)), transparent 0),
    repeating-linear-gradient(to bottom, transparent 0 calc(var(--u) - 1px), rgb(200 65 43 / 0.18) 0 var(--u));
  background-size: calc((100% + var(--grid-gutter)) / var(--grid-cols)) 100%, 100% var(--u);
  background-origin: content-box, border-box;
  background-clip: content-box, border-box;
}
/* 墨底上的叠层：不着色，只画 1 px 的栏边线（纸色 0.25）与基线（0.12），墨仍是墨 */
.l-wrap[data-grid].l-ink {
  background-image:
    linear-gradient(to right, rgb(244 242 236 / 0.25) 0 1px, transparent 1px calc(100% - var(--grid-gutter) - 1px),
                    rgb(244 242 236 / 0.25) 0 calc(100% - var(--grid-gutter)), transparent 0),
    repeating-linear-gradient(to bottom, transparent 0 calc(var(--u) - 1px), rgb(244 242 236 / 0.12) 0 var(--u));
}

/* ==== motion.css  （源头 03-foundations/motion/src/build_css.py） ==== */
/* SENYU AI 动态令牌 v1.1（2026-10-20）
   由 03-foundations/motion/src/build_css.py 生成，不要手改。
   规范：03-foundations/motion/SPEC.md        示范：03-foundations/motion/index.html、ui.html
   数值：03-foundations/motion/src/timing.py（timing.json）。改数值改源头，不要只改这里。

   只有两种运动：
     光    光在每一段里匀速，没有起步和刹车；只在接触线上换一次速度，变慢为 1/√3，转角和变慢
           来自同一个 n。曲线 linear。
     材料  对光的回答（透明度、颜色、光晕、线显出本色）：一阶回应，与颜色随深度增长是同一条曲线
           (1 − e^(−x ÷ 0.5)) ÷ (1 − e^(−2))。没有 ease-in，没有回弹，没有过冲，材料不移动。
   阶梯 100 × 2^n ms 约束速度与回答时长；光走多久 = 距离 ÷ 速度，不取整到阶梯。
   界面动作不超过 400 ms（Doherty 阈值）。

   配合 color.css（语义色）与 motion/src/motion.js（按深度给延迟、按距离给时长、加载的静默期）。 */

:root {
  /* 阶梯：100 × 2^n ms */
  --motion-100: 100ms;
  --motion-200: 200ms;
  --motion-400: 400ms;
  --motion-800: 800ms;

  /* 角色：界面 */
  --motion-instant: var(--motion-100);          /* 悬停进入、按下、开关、旧页退场：100 ms 以内被读成“我做的” */
  --motion-response: var(--motion-200);         /* 出现、消失、悬停退出、线显出本色：材料的回答 */
  --motion-travel: var(--motion-400);           /* 光走过一块材料（视口高、面板高）：界面上限 */

  /* 角色：品牌时刻（标志、开场、章节页） */
  --motion-hold: var(--motion-200);             /* 光到来前，材料先单独停留：约一次扫视潜伏期 */
  --motion-brand-travel: var(--motion-800);     /* 入射段走过一个方块边长 A */
  --motion-brand-response: var(--motion-400);   /* 字标、署名显现：品牌光程的一半 */
  --motion-brand-color: var(--motion-800);      /* T4：垂类颜色由白入色，一整个光程 */
  --motion-index: 1.7320508;                    /* n = √3：接触线两侧的速度比，与标志作图常数相同 */

  /* 加载 */
  --motion-quiet: var(--motion-400);            /* 少于 400 ms 的等待不显示任何加载 */
  --motion-loop: var(--motion-800);             /* 光走过一条轨道（轨道长 W）用 800 ms，也是一趟的周期 */

  /* 曲线 */
  --ease-light: linear;                         /* 光 */
  --ease-response: cubic-bezier(0.25, 0.58, 0.56, 0.86);
                                                /* 材料：拟合一阶回应，最大偏差 0.13% */
  --ease-response-exact: linear(0, 0.1359, 0.2558, 0.3617, 0.4551, 0.5375, 0.6102, 0.6744, 0.7311, 0.7811, 0.8252, 0.8641, 0.8985, 0.9288, 0.9555, 0.9792, 1);
                                                /* 同一条曲线的分段折线，最大偏差 0.21% */
}

/* ---------- 悬停与按下：材料回答，不移动 ---------------------------------------------------
   进入 100 ms，退出 200 ms：指针扫过一排条目时，退出得慢一点，整排不会闪。
   颜色取 color.css 的语义令牌（--color-fill 等），这里只管时间。 */
.m-hover {
  transition-property: background-color, color, border-color, text-decoration-color;
  transition-duration: var(--motion-response);
  transition-timing-function: var(--ease-response);
}
.m-hover:hover, .m-hover:active { transition-duration: var(--motion-instant); }

/* 焦点永远不做动画：键盘用户要在按键的同一帧看到光落在哪里。
   焦点环（outline）不加 transition；可悬停的元素被键盘聚焦时，底色也立即到位。 */
.m-hover:focus-visible { transition-duration: 0ms; }

/* ---------- 出现：按深度 -------------------------------------------------------------------
   光从上往下匀速走过视口（--motion-travel）。前沿经过一条线，线由界线色显出本色（--m-line，默认墨）；
   每块内容在前沿到达它的那一刻显现。motion.js 的 reveal() 写入 --m-delay（毫秒数，不带单位）。
   前沿本身看不见（界面前沿不是给眼睛追的），看得见的是次序：自上而下依次显出。
   页面给 .m-line 元素只写边框的宽度与样式（border-top-width、border-top-style），不写颜色，
   也不用 border-top 简写（简写会把颜色重置为 currentColor，盖掉这里的规则）。 */
.m-appear { transition: opacity var(--motion-response) var(--ease-response) calc(var(--m-delay, 0) * 1ms); }
.m-ready .m-appear { opacity: 0; }        /* 只在脚本接管的容器里先藏起来：没有 JS 时内容照常显示 */
.m-ready .m-appear.is-in { opacity: 1; }
.m-line {
  border-color: var(--m-line, var(--color-text-1));
  transition: border-color var(--motion-response) var(--ease-response) calc(var(--m-delay, 0) * 1ms);
}
.m-ready .m-line:not(.is-in) { border-color: var(--color-border); }
/* 换页：旧页 100 ms 退场（.m-leave），新页同时淡入（.m-enter），所以 0 ms 时旧页仍是它自己的静止设计，
   100 ms 时只剩新页的线（界线色：材料先在）。motion.js 的 swap() 依次调用。 */
.m-leave { opacity: 0; transition: opacity var(--motion-instant) var(--ease-response); }
.m-enter { opacity: 0; transition: opacity var(--motion-instant) var(--ease-response); }
.m-enter.is-in { opacity: 1; }

/* ---------- 展开：前沿匀速，内容按深度回答 --------------------------------------------------
   高度用 --ease-light；时长 = 高度 ÷ 光速，夹在 200–400 ms（motion.js 的 expand()）。
   下面被推开的行是版面的排布，不是材料的动作：它们没有自己的缓动与延迟，严格跟着前沿。 */
.m-expand {
  overflow: hidden;
  transition: height calc(var(--m-dur, 400) * 1ms) var(--ease-light);
}

/* ---------- 加载：一道光，不转圈 ------------------------------------------------------------
   画法与标志相同：轨道是材料（text-1：浅色主题墨，深色主题纸色），光是轨道上走动的一段镂空，
   透出底色。浅色、深色只有这一种画法，不占用白光。
   光走过轨道 W 用 --motion-loop（800 ms）；光段 L = 光速 × 100 ms = W/8（运动的光被看成约 100 ms
   的一道）。一趟镂空离开右缘的同一刻，下一趟从左缘外进入：镂空的间距正好是 W，周期 800 ms，
   轨道上任何时刻都有镂空，总长都是 L，所以墨轨道永远不会是满的一条（满条读成“完成”）。
   与确定进度不同：确定进度是界线色底上长出墨色（量），这里是墨色上走动的镂空（光）。
   静默：.is-on 由 motion.js 的 load() 在 400 ms 后加上。
   5 s 后加 .is-long：镂空停下，轨道转为界线色（材料还没回答），轨道下写“仍在加载”。所以任何加载
   动画最长 5 s（WCAG 2.2.2）。减弱动态从一开始就是这个静止样式，写“正在加载”。一屏只放一道加载光。 */
.m-load { display: grid; row-gap: 8px; color: var(--color-text-2); }
.m-load:not(.is-on) { visibility: hidden; }
.m-load-track {
  display: block; height: 2px;
  background: var(--color-text-1);
  /* 遮罩一格宽 2W，镂空在 [W − L, W) 与 [2W − L, 2W)，横向重复；位置 100% → 0% 时整格右移 W */
  -webkit-mask-image: linear-gradient(90deg, #000 43.75%, transparent 43.75% 50%, #000 50% 93.75%, transparent 93.75%);
          mask-image: linear-gradient(90deg, #000 43.75%, transparent 43.75% 50%, #000 50% 93.75%, transparent 93.75%);
  -webkit-mask-size: 200% 100%; mask-size: 200% 100%;
  -webkit-mask-repeat: repeat-x; mask-repeat: repeat-x;
  animation: m-pass var(--motion-loop) var(--ease-light) infinite;
}
@keyframes m-pass {
  from { -webkit-mask-position: 100% 0; mask-position: 100% 0; }
  to   { -webkit-mask-position: 0% 0;   mask-position: 0% 0; }
}
.m-load-note { visibility: hidden; }     /* 占住一行：出现时不推动下面的内容 */
.m-load.is-long .m-load-track { animation: none; -webkit-mask-image: none; mask-image: none; background: var(--color-border); }
.m-load.is-on.is-long .m-load-note { visibility: visible; }

/* 确定进度表达的是量，不是光：已完成为 text-1，轨道为界线色（装饰，另写数值），前沿匀速。
   数值 .m-progress-num 与条用同一个时长、同一条直线，按整数走（@property 整数插值），
   所以任何一帧里数字和条说的是同一个量。 */
.m-progress { position: relative; height: 2px; background: var(--color-border); }
.m-progress > i {
  position: absolute; inset: 0; transform-origin: 0 50%;
  background: var(--color-text-1);
  transform: scaleX(var(--m-value, 0));
  transition: transform calc(var(--m-dur, 400) * 1ms) var(--ease-light);
}
@property --m-pct { syntax: "<integer>"; inherits: true; initial-value: 0; }
.m-progress-num {
  counter-reset: m-pct var(--m-pct);
  transition: --m-pct calc(var(--m-dur, 400) * 1ms) var(--ease-light);
}
.m-progress-num::after { content: counter(m-pct) "%"; }

/* ---------- 减弱动态 ------------------------------------------------------------------------
   光不再走：前沿、展开、加载的光段全部停下；保留透明度与颜色的回答（不是运动），时长不变。
   加载不画停住的光段（会被读成某个位置），也不画满的墨条（会被读成完成）：轨道是界线色，
   轨道下写“正在加载”。数值直接跳到新值。 */
@media (prefers-reduced-motion: reduce) {
  :root {
    --motion-travel: 0ms;
    --motion-brand-travel: 0ms;
    --motion-brand-color: 0ms;
  }
  .m-appear, .m-line { transition-delay: 0ms; }
  .m-expand, .m-progress > i, .m-progress-num { transition: none; }
  .m-load-track { animation: none; -webkit-mask-image: none; mask-image: none; background: var(--color-border); }
  .m-load.is-on .m-load-note { visibility: visible; }
}
.m-reduce .m-appear, .m-reduce .m-line { transition-delay: 0ms; }
.m-reduce .m-expand, .m-reduce .m-progress > i, .m-reduce .m-progress-num { transition: none; }
.m-reduce .m-load-track { animation: none; -webkit-mask-image: none; mask-image: none; background: var(--color-border); }
.m-reduce .m-load.is-on .m-load-note { visibility: visible; }
