/*
 * 中文小镇 · 四套视觉世界（CSS 变量驱动，data-theme 切换）
 *
 * 主题（在顶栏切换，localStorage 记忆）：
 *   night      夜巡观测台  墨色夜城，纸白文字，地图像夜间航拍的灯火
 *   bulletin   普查公报    米纸底宋体标题，像一份印刷的人口普查公报
 *   instrument 精密仪器    冷灰发丝线，Linear 式中性仪表
 *   classic    经典暗色    第一步的原版配色，保留
 *
 * 设计规则（全主题统一）：
 *   刻度：字号 9 档 / 间距 10 档（4px 基准）/ 圆角 5 档 + 三个组件别名
 *   单一强调色：仅用于印章、下划线强调、焦点环；地图自身的六色是数据墨水，不参与主题
 *   数据一律等宽字，标题按主题用衬线或无衬线展示体
 *   主题锁：整页同一主题，不做局部反色
 *
 * 文件结构（按顺序读，不要跳）：
 *   01 令牌        字体 / 字号 / 间距 / 圆角 / 布局 / 动效刻度 + 四套主题配色
 *   02 基础        重置、可访问性工具、页面底纹、焦点环
 *   03 顶栏        品牌、通道、分段控件（主题与地图视图共用）、设置弹窗
 *   04 首屏        标题、规模数字条、迷你小镇
 *   05 卡片        面板与分节标题
 *   06 表单        输入控件族、按钮族、进度、发帖框的可玩性件
 *   07 数据        统计、表格、胶囊、轨道条、地图、图例、档案
 *   08 报告        目录、快报、决策、地形、逐句承重、声音、骨架
 *   09 示例与流    示例卡、feed、页脚
 *   10 动效        入场与弹窗（reduced-motion 兜底）
 *   11 响应式      1080 / 860 / 760 / 720 / 560 五档
 *
 * 组件族用 :where() 包裹：族规则特异性为 0，组件自己的规则无论写在前面还是后面都能赢。
 * 这是 2026-10-28 第二轮重构的收获——上一轮并族时 .sample-chip 的颜色与内边距被族吞掉，
 * 靠人工 diff 才抓回来；:where() 让这类"并族丢样式"在结构上不可能发生。
 */

/* ============================================================
 * 01 令牌
 * ============================================================ */

:root {
  --font-body: -apple-system, "Segoe UI", "PingFang SC", "Microsoft YaHei", "Helvetica Neue", sans-serif;
  --font-mono: ui-monospace, "Cascadia Mono", "SF Mono", Consolas, "JetBrains Mono", monospace;

  /* 字号刻度：11 / 12 / 13 / 14 / 15 / 17 / 19 / 28 / 首屏 clamp。
     13 紧凑（控件、hint、图例），14 次级正文（表格、feed、声音卡），15 正文，
     17 引言与规模数字，19 分节标题，28 统计大数。
     上一轮还留着 12.5 这一档（六处半像素散值已并进 13）；本轮把 11.5 / 12.5 / 14.5 /
     16 / 17 / 18 这些孤值全部收回刻度，字号只从这九档里取。
     正文 15px 写在 body 的 font 简写里（theme.test.js 按 15px/1.7 断言），不在这里。 */
  --fs-2xs: 11px;
  --fs-xs: 12px;
  --fs-sm: 13px;
  --fs-ui: 14px;
  --fs-base: 15px;
  --fs-lede: 17px;
  --fs-lg: 19px;
  --fs-xl: 28px;
  --fs-hero: clamp(34px, 6vw, 56px);

  /* 间距刻度：4px 基准，全部是 4 的倍数。组件只取这几档。 */
  --s-1: 4px;
  --s-2: 8px;
  --s-3: 12px;
  --s-4: 16px;
  --s-5: 20px;
  --s-6: 24px;
  --s-7: 32px;
  --s-8: 40px;
  --s-9: 48px;
  --s-10: 72px;

  /* 圆角刻度（几何五档）+ 三个组件别名（代码与文档一直按"卡 / 控件 / 胶囊"说话）。
     上一轮 --r-xl 与 --r-card 同为 16px、--r-pill 与 --r-chip 同为 999px，是两套重复名字；
     本轮只留别名，几何档收敛到 xs..card 五档。尺寸令牌必须写 px 字面量——写成
     var(--r-lg) 会被 theme.test.js 的令牌解析器一路追到十六进制而断言失败。 */
  --r-xs: 4px;
  --r-sm: 6px;
  --r-md: 8px;
  --r-lg: 12px;
  --r-card: 16px;
  --r-ctrl: 10px;
  --r-pill: 999px;

  /* 控件几何：所有输入框与按钮同一个高度，表单才像一台仪器而不是一堆零件。 */
  --ctrl-h: 38px;
  --track-h: 6px;
  --track-h-lg: 14px;

  /* 动效：两条曲线、三档时长；reduced-motion 由文件末尾的全局规则压平到 0.01ms。 */
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
  --dur-1: 0.12s;
  --dur-2: 0.18s;
  --dur-3: 0.26s;

  /* 点阵底纹的缺省档（图例 / 监控小卡 / 标本卡共用一条 background-image 声明） */
  --dot-ink: 5%;
  --dot-size: 14px;

  /* 地图数据墨水（六色反应，来自 presets.js 的 LOOKS，不随主题变） */
  --map-blue: #6ea8fe;
  --map-green: #3ddc84;
  --map-yellow: #ffd84d;
  --map-red: #ff5c5c;

  /* 面版墨水（--face-*）：同七色语义，但压到能直接画在卡片/内槽表面上。
     地图底板是深版（--map-well），地图用 --map-*；条、堆叠段、图例点、SVG 图表这些
     直接落在 --card / --card-2 / --inset 上的地方用 --face-*。
     暗色两套主题的面就是底板本身（墨水本来就够亮），浅色两套必须换暗一档的墨。 */
  --face-dark: #141922;
  --face-scrolled: #39414f;
  --face-hollow: #5d6880;
  --face-stopped: var(--map-blue);
  --face-glad: var(--map-green);
  --face-spreads: var(--map-yellow);
  --face-sorry: var(--map-red);

  /* 浏览器面也带设计（R12）：滚动条跟主题令牌走。scrollbar-color 继承，设一次全页面生效；
     scrollbar-width 不继承，滚动容器各自声明（见 .monitor-wrap 与窄屏 .toc）。 */
  scrollbar-width: thin;
  scrollbar-color: var(--hairline-strong) transparent;
  /* 原生控件（复选框、日期、范围条）的强调色跟主题走，不再各写一遍 accent-color。 */
  accent-color: var(--accent);
}

/* 夜巡观测台（默认） */
:root,
:root[data-theme="night"] {
  color-scheme: dark;
  --bg: #0d1017;
  --glow-1: rgba(216, 130, 60, 0.05);
  --glow-2: rgba(80, 120, 200, 0.06);
  --card: #151b27;
  --card-2: #10151f;
  --inset: #0a0d13;
  --text: #e9e4d8;
  --muted: #9aa3b2;
  --hairline: rgba(233, 228, 216, 0.1);
  --hairline-strong: rgba(233, 228, 216, 0.2);
  /* #e0604a：原 #d8553c 在本主题卡片上只有 4.34:1，小字差一点到 4.5。抬到 #e0604a = 4.88:1。 */
  --accent: #e0604a;
  --accent-soft: rgba(216, 85, 60, 0.32);
  --btn-bg: #e9e4d8;
  --btn-ink: #14181f;
  --ok: #3ddc84;
  --bad: #ff6b6b;
  --map-well: #0a0d13;
  --shadow: 0 14px 44px rgba(0, 0, 0, 0.42), inset 0 1px 0 rgba(233, 228, 216, 0.04);
  --grain: 0.05;
  --font-display: Georgia, "Noto Serif SC", "Source Han Serif SC", "Noto Serif CJK SC", "Songti SC", "STZhongsong", "SimSun", serif;
}

/* 普查公报 */
:root[data-theme="bulletin"] {
  color-scheme: light;
  --bg: #f4efe4;
  --glow-1: rgba(180, 58, 43, 0.05);
  --glow-2: rgba(120, 110, 80, 0.07);
  --card: #fbf7ee;
  --card-2: #f2ecdd;
  --inset: #ece5d4;
  --text: #22201b;
  --muted: #6b665a;
  --hairline: rgba(34, 32, 27, 0.16);
  --hairline-strong: rgba(34, 32, 27, 0.32);
  --accent: #b43a2b;
  --accent-soft: rgba(180, 58, 43, 0.26);
  --btn-bg: #22201b;
  --btn-ink: #f4efe4;
  --ok: #1a7145;
  --bad: #b43a2b;
  /* 地图底板是"印刷墨版"式的暖近黑，不是米纸色：六色反应墨水在浅底板上几乎隐形
     （实测 spreads 黄 1.07:1、glad 绿 1.38、sorry 红 2.34，全线达不到非文字图形 3:1）。
     换成深版后 5/6 过线，最低 1.67（scrolled 本就该退到背景里去）。 */
  --map-well: #1e1b16;
  /* 面版墨水：这一套的表面是米纸，--map-* 的亮色在它上面全线隐形（蓝 1.92、绿 1.42、
     黄 1.10、红 2.41 —— 非文字图形要 3:1）。所以条/段/图例改用压暗一档的同色相墨水：
     三个信号色全部过 3:1（最低 4.36），三个背景色按"越不显眼越好"排在 1.4–3.6。 */
  --face-dark: #c6bca4;
  --face-scrolled: #9d957e;
  --face-hollow: #6f7887;
  --face-stopped: #2a67c4;
  --face-glad: #1a7145;
  --face-spreads: #8a5a00;
  --face-sorry: #bc2a2a;
  --shadow: 0 10px 30px rgba(84, 70, 40, 0.16), inset 0 1px 0 rgba(255, 255, 255, 0.7);
  --grain: 0.05;
  --font-display: Georgia, "Noto Serif SC", "Source Han Serif SC", "Noto Serif CJK SC", "Songti SC", "STZhongsong", "SimSun", serif;
}

/* 精密仪器 */
:root[data-theme="instrument"] {
  color-scheme: light;
  --bg: #e9edf2;
  --glow-1: rgba(45, 96, 225, 0.06);
  --glow-2: rgba(20, 40, 90, 0.05);
  --card: #fbfcfe;
  --card-2: #eff2f7;
  --inset: #e4e9f0;
  --text: #121722;
  --muted: #59636f;
  --hairline: rgba(18, 23, 34, 0.13);
  --hairline-strong: rgba(18, 23, 34, 0.28);
  --accent: #255bd6;
  --accent-soft: rgba(37, 91, 214, 0.22);
  /* 电蓝主按钮：这一版唯一的主行动色不再压成近黑，「Instrument」的信号感来自它。
     白字/蓝底 = 5.9:1，过 WCAG AA（4.5:1）；theme.test.js 也钉住这条，改色先看断言。 */
  --btn-bg: #255bd6;
  --btn-ink: #ffffff;
  --ok: #157347;
  --bad: #bc2a2a;
  /* 同公报：浅底板吃掉了亮墨水（spreads 1.09:1）。冷钢灰近黑，4/6 过线，最低 1.55。 */
  --map-well: #1d222a;
  /* 同公报：这一套表面是冷灰，亮墨水在它上面也读不出来（绿 1.43、黄 1.10、红 2.42）。
     面版墨水换成同色相的深墨，四个信号色全部过 3:1。 */
  --face-dark: #c6bca4;
  --face-scrolled: #9d957e;
  --face-hollow: #6f7887;
  --face-stopped: #2559ad;
  --face-glad: #17683e;
  --face-spreads: #7d5200;
  --face-sorry: #bc2a2a;
  --shadow: 0 10px 30px rgba(25, 35, 60, 0.1), inset 0 1px 0 rgba(255, 255, 255, 0.8);
  --grain: 0.03;
  --font-display: "Segoe UI", -apple-system, "PingFang SC", "Microsoft YaHei", sans-serif;
}

/* 经典暗色（第一版原样） */
:root[data-theme="classic"] {
  color-scheme: dark;
  --bg: #0f131a;
  --glow-1: rgba(110, 168, 254, 0.04);
  --glow-2: rgba(61, 220, 132, 0.03);
  --card: #171d27;
  --card-2: #10151d;
  --inset: #10151d;
  --text: #dfe5ee;
  --muted: #8b95a7;
  --hairline: rgba(223, 229, 238, 0.12);
  --hairline-strong: rgba(223, 229, 238, 0.22);
  --accent: #6ea8fe;
  --accent-soft: rgba(110, 168, 254, 0.3);
  --btn-bg: #6ea8fe;
  --btn-ink: #0b1220;
  --ok: #3ddc84;
  --bad: #ff5c5c;
  --map-well: #141922;
  --face-dark: var(--map-well);
  --face-scrolled: #39414f;
  --face-hollow: #5d6880;
  --face-stopped: var(--map-blue);
  --face-glad: var(--map-green);
  --face-spreads: var(--map-yellow);
  --face-sorry: var(--map-red);
  --shadow: 0 14px 44px rgba(0, 0, 0, 0.4);
  --grain: 0;
  --font-display: -apple-system, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
}

/* ============================================================
 * 02 基础
 * ============================================================ */

* { box-sizing: border-box; }

/* hidden 要赢过组件类：#resultActions 既有 hidden 又有 .row（display:flex），
   作者样式里的 display 会打败 UA 的 [hidden]，于是"改一版再发"在首屏就露了出来。 */
[hidden] { display: none !important; }

/* 屏幕阅读器专用：给只有占位符、没有可见标签的控件补可访问名 */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* 跳过导航：键盘用户不必逐个穿过品牌、通道、四个主题按钮才到发帖框。
   平时移出视口（transform 而不是 display:none——后者会让它从 Tab 序列里消失）。 */
.skip {
  position: fixed;
  top: var(--s-2);
  left: var(--s-2);
  z-index: 80;
  padding: 10px var(--s-4);
  border: 1px solid var(--accent);
  border-radius: var(--r-ctrl);
  background: var(--card);
  color: var(--text);
  font-size: var(--fs-sm);
  text-decoration: none;
  box-shadow: var(--shadow);
  transform: translateY(-200%);
  transition: transform var(--dur-2) var(--ease-out);
}

.skip:focus-visible { transform: translateY(0); }

html {
  height: 100%;
  /* 顶栏吸附：锚点跳转不被它盖住（--s-10 与页尾留白同一档） */
  scroll-padding-top: var(--s-10);
  /* 常驻滚动条槽位：报告由骨架换成正文时不横向抖一下 */
  scrollbar-gutter: stable;
}

@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}

body {
  margin: 0 auto;
  padding: 0 var(--s-5) var(--s-10);
  max-width: 1020px;
  min-height: 100%;
  /* 普查图纸：最上层铺一层 28px 间距的点阵（1px 点、取自文字色 6.5% 透明度），
     页面留白处变成一张淡淡的坐标纸——呼应 100×100 的小镇网格。两层氛围光垫底。 */
  background:
    radial-gradient(color-mix(in srgb, var(--text) 6.5%, transparent) 1px, transparent 1.4px),
    radial-gradient(60rem 34rem at 12% -6%, var(--glow-1), transparent 62%),
    radial-gradient(52rem 30rem at 88% 12%, var(--glow-2), transparent 60%),
    var(--bg);
  background-size: 28px 28px, auto, auto, auto;
  color: var(--text);
  font: 15px/1.7 var(--font-body);
  /* R33：等宽字体在主流平台数字恰好等宽，但 --font-mono 的兜底项 monospace 在某些
     Android/旧环境会落到比例数字字体，表格列就左右晃。这页有 17 处数据字体是等宽栈，
     所以把「数字对齐」从「碰字体运气」改成「由 CSS 兜底」。齐次要对齐，标题才用比例数字。
     全站只此一处声明——多写一处就是有人局部改了回去。 */
  font-variant-numeric: tabular-nums;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* 颗粒：固定层，绝不放在滚动容器上 */
.grain {
  position: fixed;
  inset: 0;
  z-index: 60;
  pointer-events: none;
  opacity: var(--grain);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
}

::selection { background: var(--accent-soft); }

textarea, input { caret-color: var(--accent); }

/* 焦点环：全站一条。不再写 border-radius——现代浏览器里 outline 会跟随元素自身的圆角，
   而一条 4px 的兜底会把卡片（16px）和输入框（10px）在聚焦瞬间压成直角。 */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

a { color: inherit; }

h1, h2, h3 { margin: 0; }

button { cursor: pointer; font-family: inherit; }

img, svg, canvas { max-width: 100%; }

/* 高对比偏好：发丝线是最先看不见的一层，这里只加粗它，不动配色 */
@media (prefers-contrast: more) {
  :root {
    --hairline: var(--hairline-strong);
  }
}

/* ============================================================
 * 03 顶栏
 * ============================================================ */

.top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-4);
  padding: var(--s-5) 2px var(--s-4);
  border-bottom: 1px solid var(--hairline);
  margin-bottom: var(--s-7);
}

/* 宽屏时顶栏吸附：滚动深了（报告很长）通道与主题切换始终在手边。
   半透明底 + 背景模糊让两层 glow 隐约透上来，不是一块死白/死黑。
   窄屏不吸附——换行后的两行高度会吃掉小屏的纵向空间。 */
@media (min-width: 721px) {
  .top {
    position: sticky;
    top: 0;
    z-index: 40;
    padding-top: var(--s-3);
    padding-bottom: var(--s-3);
    margin-bottom: var(--s-6);
    /* 上缘用背景色压住，下缘才渐隐：滚到一半的卡片不会在顶栏底下"穿出来"一道硬边 */
    background: color-mix(in srgb, var(--bg) 84%, transparent);
    -webkit-backdrop-filter: blur(14px) saturate(1.1);
    backdrop-filter: blur(14px) saturate(1.1);
  }
}

.brand {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  border-radius: var(--r-md);
  text-decoration: none;
}

/* 印章：40px 见方，字号是"格子里的一个字"而非排版刻度，所以这里保留字面值 */
.seal {
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  border-radius: var(--r-sm);
  background: linear-gradient(160deg, color-mix(in srgb, var(--accent) 84%, #fff), var(--accent));
  color: var(--bg);
  font-family: var(--font-display);
  font-size: 22px;
  font-weight: 700;
  line-height: 1;
  box-shadow: 0 4px 16px var(--accent-soft);
  transition: transform var(--dur-2) var(--ease-out);
}

.brand:hover .seal { transform: rotate(-4deg) scale(1.03); }

.brand-name {
  display: flex;
  flex-direction: column;
  line-height: 1.25;
}

.brand-name b {
  font-family: var(--font-display);
  font-size: var(--fs-lg);
  font-weight: 700;
  letter-spacing: 0.06em;
}

.brand-name small {
  color: var(--muted);
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  letter-spacing: 0.14em;
}

/* 顶栏右侧动作区 */
.top-actions { display: flex; align-items: center; gap: var(--s-2); flex-wrap: wrap; }
.mode-chip { font-size: var(--fs-2xs); color: var(--muted); }
.settings-btn { padding: var(--s-1) var(--s-3); font-size: var(--fs-sm); }

dialog#settings {
  background: var(--card);
  color: var(--text);
  border: 1px solid var(--hairline-strong);
  border-radius: var(--r-card);
  box-shadow: var(--shadow);
  padding: var(--s-6);
  max-width: 460px;
  width: calc(100vw - 40px);
  /* 小屏与横屏矮窗：内容比视口高时弹窗自己滚，而不是把"保存"顶出视口外 */
  max-height: min(88vh, 640px);
  overflow: auto;
}

dialog#settings::backdrop {
  background: rgba(0, 0, 0, 0.55);
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
}

dialog#settings h2 {
  font-family: var(--font-display);
  font-size: var(--fs-lg);
  margin-bottom: var(--s-2);
  letter-spacing: 0.01em;
}

dialog#settings .row { margin: var(--s-3) 0; }
dialog#settings .row.between { margin-top: var(--s-5); }

/* 分段控件：主题切换（顶栏）与地图三视图（报告里）共用一套规则。
   上一轮是四组并列选择器各写一遍（.themeswitch, .mapswitch / .theme-btn, .mapswitch button …），
   本轮改成容器挂一个 .segmented，规则只剩一条——新增第三处分段控件不用再改样式表。 */
.segmented {
  display: flex;
  gap: var(--s-1);
  padding: var(--s-1);
  border-radius: var(--r-pill);
  background: var(--card-2);
  border: 1px solid var(--hairline);
}

.segmented button {
  border: 0;
  background: transparent;
  color: var(--muted);
  border-radius: var(--r-pill);
  padding: var(--s-1) var(--s-3);
  font-size: var(--fs-sm);
  letter-spacing: 0.04em;
  transition: color var(--dur-2), background-color var(--dur-2);
}

.segmented button:hover { color: var(--text); }

.segmented button[aria-pressed="true"] {
  background: var(--btn-bg);
  color: var(--btn-ink);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12);
}

/* 分段控件的焦点环贴住胶囊轮廓即可，不需要额外圆角覆盖 */
.segmented button:focus-visible { outline-offset: 1px; }

/* ============================================================
 * 04 首屏
 * ============================================================ */

.hero {
  position: relative;
  padding: var(--s-7) 2px var(--s-6);
}

/* 宽屏时首屏右侧的一小块「迷你小镇」：10×10 的点阵代表一格一人，
   其中一格亮着强调色——整个产品的隐喻缩在一枚印章里。窄屏藏起，不与标题抢地。 */
.town-mark {
  display: none;
}

@media (min-width: 1000px) {
  .town-mark {
    display: block;
    position: absolute;
    top: 176px;
    right: var(--s-2);
    width: 122px;
    height: 122px;
    background-image: radial-gradient(color-mix(in srgb, var(--text) 20%, transparent) 1.1px, transparent 1.3px);
    background-size: 12.2px 12.2px;
    -webkit-mask-image: radial-gradient(closest-side, #000 62%, transparent);
    mask-image: radial-gradient(closest-side, #000 62%, transparent);
  }
  /* 那一格亮着的「窗」：Jev 判定到的第一个人 */
  .town-mark::after {
    content: "";
    position: absolute;
    top: 36.6px;   /* 3 格 × 12.2px */
    left: 61px;    /* 5 格 × 12.2px */
    width: 5px;
    height: 5px;
    border-radius: 1px;
    background: var(--accent);
    box-shadow: 0 0 10px var(--accent-soft);
  }
}

.driver {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  margin: 0 0 var(--s-4);
  color: var(--muted);
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  letter-spacing: 0.06em;
}

/* kicker 前的一小段强调色引线：公文页眉的那一笔（装饰件，尺寸不进刻度） */
.driver::before {
  content: "";
  width: 24px;
  height: 1px;
  background: var(--accent);
  opacity: 0.7;
  flex-shrink: 0;
}

.driver b { color: var(--accent); }

.hero h1 {
  font-family: var(--font-display);
  font-size: var(--fs-hero);
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: 0.005em;
  max-width: 21em;
  text-wrap: balance;
}

.hero .sub {
  margin: var(--s-4) 0 0;
  max-width: 36em;
  color: var(--muted);
  font-size: var(--fs-lede);
  line-height: 1.7;
  text-wrap: pretty;
}

/* 普查封面的规模数字条：dt 小字灰、dd 等宽数字，顶上一条发丝线——
   「这是一台仪器，这是它的量程」。事实全部来自引擎常量，不随版本漂。 */
.census {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-1) var(--s-8);
  margin: var(--s-6) 0 0;
  padding: var(--s-3) 0 var(--s-1);
  border-top: 1px solid var(--hairline);
  max-width: 42em;
}

.census-item dt {
  color: var(--muted);
  font-size: var(--fs-2xs);
  letter-spacing: 0.08em;
}

.census-item dd {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--fs-lede);
  font-weight: 600;
  letter-spacing: -0.01em;
  line-height: 1.45;
}

/* ============================================================
 * 05 卡片
 * ============================================================ */

/* 卡面带一层极浅的纵向渐变（上缘略沉、往下回到主卡色），面板不再是死平面；
   两色都来自主题令牌，四套主题自动跟随。130px 之后回归纯色，长报告的卡身不受影响。 */
.plate {
  background: linear-gradient(180deg, var(--card-2), var(--card) 130px);
  border: 1px solid var(--hairline);
  border-radius: var(--r-card);
  box-shadow: var(--shadow);
  padding: var(--s-6);
  margin-bottom: var(--s-5);
}

.plate > h2 {
  font-family: var(--font-display);
  font-size: var(--fs-lg);
  letter-spacing: 0.02em;
  line-height: 1.35;
  margin-bottom: var(--s-3);
  text-wrap: balance;
}

/* 分节标题：小标签 + 发丝线。左侧的强调色小方块给长报告一个稳定的视觉节拍
   （方块与引线是装饰件，尺寸不进刻度）。 */
h3 {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  margin: var(--s-6) 0 var(--s-3);
  color: var(--muted);
  font-family: var(--font-body);
  font-size: var(--fs-sm);
  font-weight: 600;
  letter-spacing: 0.03em;
  line-height: 1.4;
}

h3::before {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: 2px;
  background: var(--accent);
  opacity: 0.55;
  flex-shrink: 0;
}

h3::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--hairline);
}

/* ============================================================
 * 06 表单
 * ============================================================ */

.row { display: flex; align-items: center; gap: var(--s-3); margin: var(--s-3) 0; }
.row.between { justify-content: space-between; }
.row label { color: var(--muted); font-size: var(--fs-sm); }
.row > input { flex: 1; min-width: 0; }
/* 一行里放两个以上按钮时收成一组（弹窗底部的"关闭/清除/保存"），
   否则 .row.between 会把三个按钮均匀摊开，主次不分。 */
.row-actions { display: flex; gap: var(--s-2); flex-wrap: wrap; }

/* 控件族：select / textarea / 文本类 input 共用一套外观。
   曾经只有 select 与 textarea 有样式，发帖框里的「定价阶梯」输入框一路裸奔到 UA 默认——
   同一张表单里两种输入框长两个样。高度走 --ctrl-h，与按钮同一条基线。 */
select,
textarea,
input[type="text"],
input[type="password"],
input[type="number"],
input[type="search"] {
  background: var(--inset);
  color: var(--text);
  border: 1px solid var(--hairline);
  border-radius: var(--r-ctrl);
  padding: 9px var(--s-3);
  font: inherit;
  min-height: var(--ctrl-h);
  transition: border-color var(--dur-2), background-color var(--dur-2);
}

select:hover,
textarea:hover,
input[type="text"]:hover,
input[type="password"]:hover { border-color: var(--hairline-strong); }

select:focus-visible,
textarea:focus-visible,
input[type="text"]:focus-visible,
input[type="password"]:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
  border-color: transparent;
}

textarea {
  width: 100%;
  resize: vertical;
  line-height: 1.7;
}

textarea::placeholder,
input::placeholder { color: var(--muted); opacity: 0.75; }

/* 按钮族：几何与过渡一次说清（:where() 让族规则特异性为 0，变体永远能覆盖它），
   变体只声明自己的颜色与内边距。 */
:where(button.primary, button.ghost, #go) {
  border-radius: var(--r-ctrl);
  font-weight: 650;
  letter-spacing: 0.02em;
  transition:
    transform var(--dur-1),
    box-shadow var(--dur-2),
    filter var(--dur-2),
    border-color var(--dur-2),
    background-color var(--dur-2);
}

/* 主 CTA：印章式的实心块 + 脚下强调色软光 */
button.primary,
#go {
  background: var(--btn-bg);
  color: var(--btn-ink);
  border: 1px solid transparent;
  padding: var(--s-3) 26px;
  font-size: var(--fs-base);
  white-space: nowrap;
  box-shadow: 0 6px 22px var(--accent-soft);
}

button.primary:hover, #go:hover {
  filter: brightness(1.06);
  transform: translateY(-1px);
  box-shadow: 0 9px 28px var(--accent-soft);
}

button.primary:active, #go:active {
  transform: translateY(0) scale(0.985);
  box-shadow: 0 3px 12px var(--accent-soft);
}

/* 变暗只表示「此刻按不了」；光标形状要分清两种按不了：正在跑（wait，快回来）
   和你这下按不了（default，不会来）。原来这条无差别给 cursor: wait，
   于是「不是作者、逐句承重按钮不给点」也摆出一个等待光标——骗人。 */
button:disabled { opacity: 0.55; cursor: default; transform: none; }
#go:disabled { cursor: wait; }

.ghost {
  background: transparent;
  color: var(--text);
  border: 1px solid var(--hairline-strong);
  padding: var(--s-2) var(--s-4);
  font-size: var(--fs-ui);
}

.ghost:hover {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent-soft) 30%, transparent);
}

.ghost:active { transform: scale(0.98); }

.hint {
  color: var(--muted);
  font-size: var(--fs-sm);
  line-height: 1.6;
}

/* ---------- 进度 ---------- */

#status { margin-top: var(--s-4); }

.status-line {
  color: var(--muted);
  margin-bottom: var(--s-2);
  font-size: var(--fs-sm);
  font-family: var(--font-mono);
  line-height: 1.6;
}

/* 轨道条族：进度、分组条、漏斗条、双向条共用「内槽 + 圆角裁切」。
   高度各自由组件声明（--track-h 的比例条 / --track-h-lg 的漏斗条）。 */
:where(.bar, .seg .bar2, .frow .fbar, .atwin) {
  background: var(--inset);
  border-radius: var(--r-pill);
  overflow: hidden;
}

.bar { height: var(--track-h); }

/* 进度用 transform 缩放而不是动画 width（合成器友好）；
   fill 带一圈强调色软光，跑的时候进度条自己会「亮」，shadow 随 scaleX 拉伸正合适。 */
.bar-fill {
  height: 100%;
  width: 100%;
  background: var(--accent);
  border-radius: var(--r-pill);
  box-shadow: 0 0 12px var(--accent-soft);
  transform-origin: 0 50%;
  transform: scaleX(0);
  transition: transform var(--dur-3) var(--ease-out);
}

/* ---------- 发帖框里的可玩性件 ---------- */

/* 全城广播的行内复选框：整段标签都是可点区（触达目标别只给 13×13 的方框），
   hint 太长时允许换行，窄屏不溢出。 */
.inline-check {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  min-height: 28px;
  cursor: pointer;
  color: var(--text);
  font-size: var(--fs-sm);
}

.inline-check input { width: var(--s-4); height: var(--s-4); }
#broadcastRow { flex-wrap: wrap; row-gap: var(--s-1); }

/* 发帖框的可玩性行：「试一句」样例按钮 + 字数计。样例按钮与 .aud-tag 同气质，
   但它是动作不是标签：没有 ×、默认 muted、hover 才浮起来。字数计靠右，等宽数字不跳。 */
.compose-tools { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2); margin-top: var(--s-2); }
.compose-tools .text-count { margin-left: auto; font-family: var(--font-mono); font-size: var(--fs-2xs); }
.sample-chips { display: flex; flex-wrap: wrap; gap: var(--s-2); min-width: 0; }

/* 样例按钮是「动作」不是「标签」：没有 ×、默认 muted、hover 才浮起来。
   命中区从 26px 提到 28px——它是发帖框里最容易被点的一排小按钮。 */
.sample-chip {
  min-height: 28px;
  padding: var(--s-1) var(--s-3);
  font-size: var(--fs-xs);
  color: var(--muted);
  cursor: pointer;
}

.sample-chip:hover {
  border-color: var(--hairline-strong);
  color: var(--text);
  background: var(--inset);
}

/* ============================================================
 * 07 数据组件
 * ============================================================ */

/* 胶囊控件族：目录、样例、候选、已选标签、计数徽章共用一套几何（尺寸与状态），
   各组件只声明自己的颜色、字号与内边距。改一处圆角/过渡，五个组件一起跟上。
   :where() 让族规则特异性为 0——组件规则写在它前面也照样赢（上一轮 .sample-chip
   被族吞掉样式，根因就是族与组件同特异性、只看谁写在后面）。 */
:where(.toc a, .sample-chip, .aud-list button, .aud-tag, .chip, .badge) {
  display: inline-flex;
  align-items: center;
  gap: var(--s-1);
  border: 1px solid var(--hairline);
  border-radius: var(--r-pill);
  background: transparent;
  color: var(--text);
  font: inherit;
  line-height: 1.35;
  text-decoration: none;
  transition:
    color var(--dur-2),
    background-color var(--dur-2),
    border-color var(--dur-2),
    transform var(--dur-1);
}

/* 点阵底纹族：图例、监控小卡、标本卡是同一张坐标纸的三个用法。
   浓度与格距各组件覆盖 --dot-ink / --dot-size。 */
:where(.legend, .mini, .decision) {
  background-image: radial-gradient(
    color-mix(in srgb, var(--text) var(--dot-ink), transparent) 1px,
    transparent 1.3px
  );
  background-size: var(--dot-size) var(--dot-size);
}

/* 强调下划线（马克笔）：首屏标题、快报、地形结论共用一支笔 */
:where(.hero h1 em, .brief-line em, .terrain-say em) {
  font-style: normal;
  font-weight: 700;
  padding: 0 var(--s-1);
  background: linear-gradient(transparent 62%, var(--accent-soft) 0);
}

/* ---------- 统计数字 ---------- */

.stats { display: flex; flex-wrap: wrap; gap: var(--s-3) var(--s-7); }

.stat b {
  display: block;
  font-family: var(--font-mono);
  font-size: var(--fs-xl);
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1.2;
}

.stat span { color: var(--muted); font-size: var(--fs-xs); letter-spacing: 0.05em; }

/* ---------- 表格 ---------- */

.waves { width: 100%; border-collapse: collapse; margin-top: var(--s-1); }

.waves th {
  color: var(--muted);
  font-weight: 500;
  font-size: var(--fs-xs);
  text-align: left;
  padding: var(--s-2) var(--s-3);
  letter-spacing: 0.06em;
}

.waves td {
  padding: var(--s-2) var(--s-3);
  border-bottom: 1px solid var(--hairline);
  font-size: var(--fs-ui);
}

.waves tr:last-child td { border-bottom: 0; }
.waves tbody tr { transition: background-color var(--dur-2); }
.waves tbody tr:hover { background: var(--card-2); }
.waves td:nth-child(3) { font-family: var(--font-mono); font-size: var(--fs-sm); }

/* ---------- 徽章与计数 ---------- */

.badge {
  padding: var(--s-1) var(--s-3);
  font-size: var(--fs-xs);
  color: var(--muted);
  background: var(--card-2);
}

.badge.go { color: var(--ok); border-color: var(--ok); background: transparent; }
.badge.stop { color: var(--bad); border-color: var(--bad); background: transparent; }

.counts { display: flex; flex-wrap: wrap; gap: var(--s-2); }

.chip {
  padding: var(--s-1) var(--s-3);
  font-size: var(--fs-sm);
  border-color: var(--hairline-strong);
  background: var(--card-2);
}

.chip b { font-family: var(--font-mono); font-weight: 600; }

/* ---------- 地图 ---------- */

.map-wrap { display: flex; gap: var(--s-6); align-items: flex-start; flex-wrap: wrap; }

canvas.grid {
  image-rendering: pixelated;
  max-width: 100%;
  height: auto;
  border: 1px solid var(--hairline-strong);
  border-radius: var(--r-lg);
  cursor: crosshair;
  /* 装裱式画框：地图外先一圈卡面衬边、再一圈发丝线，像标本册里压裱的一块墨版 */
  box-shadow:
    0 0 0 5px var(--card-2),
    0 0 0 6px var(--hairline),
    var(--shadow);
}

.legend {
  font-size: var(--fs-xs);
  color: var(--muted);
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--s-2) var(--s-3);
  align-items: center;
  /* 图例与它右边的留白抢宽度时优先保住两列排版 */
  min-width: 150px;
  /* 图例是坐标纸小图版：点阵底 + 发丝框，与页面的普查图纸语言同源 */
  padding: var(--s-3) var(--s-4);
  border: 1px solid var(--hairline);
  border-radius: var(--r-ctrl);
  background-color: var(--card-2);
}

.legend .dot { display: inline-block; width: 10px; height: 10px; border-radius: var(--r-xs); }

/* 聚集地形的图例点是环，不是实心——与地图上描的环同色相。图例画在卡片上，
   所以取面版墨水（地图上的环在深底板上，仍用地图墨水）。 */
.legend .dot.ring {
  background: transparent;
  box-shadow: inset 0 0 0 2px currentColor;
  width: 12px;
  height: 12px;
}

.legend .dot.ring.hot { color: var(--face-glad); }
.legend .dot.ring.cold { color: var(--face-sorry); }

#tooltip {
  position: fixed;
  pointer-events: none;
  display: none;
  z-index: 50;
  background: color-mix(in srgb, var(--card) 96%, transparent);
  border: 1px solid var(--hairline-strong);
  border-radius: var(--r-ctrl);
  box-shadow: var(--shadow);
  padding: var(--s-3);
  font-size: var(--fs-xs);
  line-height: 1.65;
  max-width: 280px;
}

#tooltip b { font-family: var(--font-display); font-size: var(--fs-sm); }

/* ---------- 分组条与漏斗 ---------- */

.seg { display: flex; align-items: center; gap: var(--s-3); margin: var(--s-2) 0; font-size: var(--fs-sm); }
.seg .label { min-width: 150px; color: var(--text); }

.seg .bar2 {
  flex: 1;
  max-width: 340px;
  height: var(--track-h);
  align-self: center;
}

.seg .bar2 i { display: block; height: 100%; border-radius: var(--r-pill); }

/* 堆叠条（每波反应构成）：段并排铺满一根条 */
.seg .bar2.mix { display: flex; }
.seg .bar2.mix i { display: block; border-radius: 0; }
.seg .bar2.mix i:first-child { border-radius: var(--r-pill) 0 0 var(--r-pill); }
.seg .bar2.mix i:last-child { border-radius: 0 var(--r-pill) var(--r-pill) 0; }

.seg .num { color: var(--muted); min-width: 96px; font-family: var(--font-mono); font-size: var(--fs-xs); }

/* 漏斗与分阶段条（共用一行三段式） */
.funnel { margin-top: var(--s-2); }

.frow { display: flex; align-items: center; gap: var(--s-3); margin: var(--s-2) 0; }

.frow .flabel { min-width: 76px; color: var(--muted); font-size: var(--fs-sm); flex-shrink: 0; }

.frow .fbar {
  flex: 1;
  max-width: 400px;
  height: var(--track-h-lg);
}

.frow .fbar i { display: block; height: 100%; border-radius: var(--r-pill); background: var(--accent); }

/* 漏斗的情绪分层：够 glad=绿、持平=蓝、负面=红（面版墨水：条直接画在 --inset 上） */
.frow .fbar i.fgo { background: var(--face-glad); }
.frow .fbar i.fhold { background: var(--face-stopped); }
.frow .fbar i.fdrop { background: var(--face-sorry); }
/* 随机对照条（R29）：离基线远=绿（明显强于随机）、读不出=蓝（与随机无异）、比随机还冷=红 */
.frow .fbar i.ffar { background: var(--face-glad); }
.frow .fbar i.fnear { background: var(--face-stopped); }
.frow .fbar i.fbelow { background: var(--face-sorry); }

.frow .fnum { color: var(--muted); font-family: var(--font-mono); font-size: var(--fs-xs); }

/* ---------- 逐句承重（R32） ---------- */

/* 一行 = 一句话 + 一条双向条 + 一句人话。
   条从中线往两边长：绿向右=吃它的组，红色向左=掉头的组，两端各占条的一半，
   中线本身就是「删掉这句没差」的位置，所以不用画 0 刻度。 */
.away-bar { display: flex; align-items: center; gap: var(--s-3); flex-wrap: wrap; margin: 0 0 var(--s-3); }

.arow {
  display: grid;
  grid-template-columns: 76px minmax(0, 1fr) minmax(120px, 300px) minmax(0, 1.4fr) auto;
  align-items: center;
  gap: var(--s-3);
  margin: var(--s-2) 0;
}

.arow .flabel { color: var(--muted); font-size: var(--fs-sm); }

.arow .apart { color: var(--text); font-size: var(--fs-sm); line-height: 1.55; overflow-wrap: anywhere; }

.atwin { display: flex; height: var(--track-h-lg); }

/* 两端各自成段：右段靠右、左段靠左，中间留出的空当就是「没人被这句改变主意」。 */
.atwin i { display: block; height: 100%; }
.atwin i.aup { background: var(--face-glad); margin-left: auto; border-radius: var(--r-pill); }
.atwin i.adown { background: var(--face-sorry); margin-right: auto; border-radius: var(--r-pill); }

.arow .fsay { color: var(--muted); font-size: var(--fs-sm); line-height: 1.5; }
.arow.quiet .apart { color: var(--muted); }
.arow.quiet .fsay { font-style: italic; }
.arow .fnum { color: var(--muted); font-family: var(--font-mono); font-size: var(--fs-xs); white-space: nowrap; }

.report-meta { display: flex; align-items: center; gap: var(--s-3); margin: var(--s-1) 0 var(--s-3); }

/* ---------- 人格声音 ---------- */

/* 折叠：默认只露 24 张，按钮展开 */
.voices.collapsed .voice:nth-child(n + 25) { display: none; }

.voices { display: grid; grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); gap: var(--s-3); }

.voice {
  display: flex;
  gap: var(--s-3);
  align-items: flex-start;
  border: 1px solid var(--hairline);
  border-radius: var(--r-card);
  padding: var(--s-3) var(--s-4);
  font-size: var(--fs-sm);
  background: var(--card-2);
  transition: border-color var(--dur-2), transform var(--dur-1);
}

.voice .v-body { min-width: 0; }
.voice:hover { border-color: var(--hairline-strong); transform: translateY(-1px); }
.voice .who { font-family: var(--font-display); font-size: var(--fs-ui); font-weight: 700; }
.voice .what { margin-top: var(--s-1); color: var(--muted); }

.voice .rdot {
  display: inline-block;
  width: 10px;
  height: 10px;
  border-radius: var(--r-xs);
  margin-right: var(--s-2);
  vertical-align: -1px;
}

/* ---------- Jev 决策现场 ---------- */

.decisions { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: var(--s-3); }

.decision {
  border: 1px solid var(--hairline);
  border-radius: var(--r-lg);
  background-color: var(--card-2);
  /* 标本卡：Jev 决策现场的一格切片，铺一层比图例更淡的点阵 */
  --dot-ink: 4%;
  --dot-size: 16px;
  padding: var(--s-4);
}

.decision .dline {
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  line-height: 1.6;
  color: var(--text);
  border-bottom: 1px dashed var(--hairline);
  padding-bottom: var(--s-2);
  margin-bottom: var(--s-2);
}

.decision .dask {
  color: var(--muted);
  font-size: var(--fs-2xs);
  margin-bottom: var(--s-2);
}

.decision .dseg { margin: var(--s-1) 0; }
.decision .dseg .label { min-width: 76px; font-size: var(--fs-xs); color: var(--muted); }
.decision .dseg.drawn .label { color: var(--text); font-weight: 650; }
.decision .dseg.drawn .num { color: var(--accent); }

.decision .dverdict { margin-top: var(--s-3); font-size: var(--fs-sm); color: var(--muted); }
.decision .dverdict b { color: var(--text); font-family: var(--font-display); }

/* ---------- 核对结论（Jev 解读 / 地形 / 对账） ---------- */

.checks { display: flex; flex-wrap: wrap; gap: var(--s-2); }
.checks .chip.yes { color: var(--ok); border-color: var(--ok); }
.checks .chip.no { color: var(--muted); }
.checks .chip.unclear { color: var(--accent); border-color: var(--accent); }

.chip.clustered { color: var(--face-glad); border-color: var(--face-glad); }
.chip.scattered { color: var(--face-stopped); border-color: var(--face-stopped); }
.chip.unclear { color: var(--muted); }

/* 人群地形：一句人话 + 一个统计量 + 一行出处。
   两版之差借同一套排版（.terrain-say / .hint），所以这一节不再为 delta 另写一份。 */
.terrain-read { margin: var(--s-1) 0 var(--s-2); }
.terrain-say { margin: var(--s-3) 0 var(--s-2); font-size: var(--fs-ui); line-height: 1.65; }

/* 地图上方的开关条 */
.map-bar { display: flex; align-items: center; gap: var(--s-3); flex-wrap: wrap; margin: 0 0 var(--s-3); }
.map-bar .ghost { padding: var(--s-2) var(--s-4); font-size: var(--fs-sm); }
.map-bar .ghost[aria-pressed="true"] { border-color: var(--accent); color: var(--accent); }

/* 两版之差：图例点是实心——差分是实色量表，不是环 */
.delta-read { margin: var(--s-1) 0 var(--s-2); }
.delta-legend { min-width: 0; }
canvas.grid.diff { cursor: default; }

/* 两版头条数字对照（R24）：old → new 一行一个，差值按语义着色 */
.delta-kpis {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--s-2) var(--s-4);
  background: var(--inset);
  border-radius: var(--r-ctrl);
  padding: var(--s-3) var(--s-4);
  margin-bottom: var(--s-3);
}

.dkpi { display: flex; align-items: baseline; gap: var(--s-2); font-size: var(--fs-sm); }
.dkpi-label { color: var(--muted); min-width: 34px; }
.dkpi-nums { font-family: var(--font-mono); font-size: var(--fs-sm); }
.dkpi-diff { margin-left: auto; font-family: var(--font-mono); font-size: var(--fs-xs); color: var(--muted); }
.dkpi-diff.ok { color: var(--ok); }
.dkpi-diff.bad { color: var(--bad); }

/* ---------- 头像与档案 ---------- */

.avatar { flex-shrink: 0; display: block; }
#tooltip .avatar { display: inline-block; vertical-align: -9px; margin-right: var(--s-2); }

/* 文本预览：中性卡，不是拒发提示。
   原先误用 .blocked-note 的 --bad 红边，而它的内联 border-color: var(--line)
   又指向一个根本不存在的令牌（令牌叫 --hairline），红色就一直没被覆盖掉。 */
.source-text {
  border: 1px solid var(--hairline);
  border-radius: var(--r-ctrl);
  background: var(--card-2);
  padding: var(--s-3) var(--s-4);
  font-size: var(--fs-ui);
  line-height: 1.7;
}

.error { color: var(--bad); }

/* ============================================================
 * 08 报告
 * ============================================================ */

/* 报告目录：pill 一行，窄屏横向滚动。此前 .toc 一条规则都没有，锚点挤成一行连续文字 */
.toc {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2);
  margin: 0 0 var(--s-2);
}

.toc a {
  min-height: 28px;
  padding: var(--s-1) var(--s-3);
  color: var(--muted);
  font-size: var(--fs-sm);
  letter-spacing: 0.02em;
}

.toc a:hover {
  color: var(--text);
  border-color: var(--hairline-strong);
  background: var(--card-2);
}

/* 小镇快报（R23）：报告最前的 TL;DR 盒 */
.brief {
  position: relative;
  background: var(--inset);
  border-radius: var(--r-ctrl);
  padding: var(--s-3) var(--s-4);
  margin-bottom: var(--s-4);
}

.brief-copy {
  position: absolute;
  top: var(--s-2);
  right: var(--s-3);
  padding: var(--s-1) var(--s-3);
  font-size: var(--fs-xs);
}

.brief-line { margin: var(--s-1) 0; font-size: var(--fs-ui); line-height: 1.75; }

/* 骨架屏（R21）：报告打开期间的过渡态，形状对齐报告布局；扫光在 reduced-motion 下静止 */
.skeleton { padding: var(--s-1) 2px; }

.sk {
  height: var(--track-h-lg);
  margin: var(--s-3) 0;
  border-radius: var(--r-sm);
  background: linear-gradient(90deg, var(--card-2) 25%, var(--inset) 50%, var(--card-2) 75%);
  background-size: 200% 100%;
  animation: shimmer 1.3s linear infinite;
}

.sk-block { height: 240px; border-radius: var(--r-card); }

@keyframes shimmer {
  from { background-position: 200% 0; }
  to { background-position: -200% 0; }
}

/* ---------- 图谱 ---------- */

.charts-row { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-4); }

.mini {
  position: relative;
  border: 1px solid var(--hairline);
  border-radius: var(--r-lg);
  background-color: var(--card-2);
  /* 监控小卡也是图纸上的仪表：同款点阵，比图例更淡一档 */
  --dot-ink: 4%;
  --dot-size: 16px;
  padding: var(--s-3) var(--s-3) var(--s-2);
}

#chartShare { position: relative; }
.mini-head { color: var(--muted); font-size: var(--fs-xs); letter-spacing: 0.04em; margin-bottom: 2px; }
.chart.ekg .ekg-now { font-family: var(--font-mono); font-size: var(--fs-base); font-weight: 600; }
.chart-empty { color: var(--muted); font-size: var(--fs-xs); padding: var(--s-7) 0; text-align: center; }

/* 悬停读数（R18）：右上角小读数 + svg 内的竖参考线 */
.chart-read {
  position: absolute;
  top: var(--s-2);
  right: var(--s-3);
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  color: var(--accent);
  pointer-events: none;
  white-space: nowrap;
}

.hover-line { stroke: var(--accent); stroke-width: 1; opacity: 0.55; }

.chart { width: 100%; height: auto; display: block; margin-top: var(--s-3); }
.chart .chart-zero { stroke: var(--hairline-strong); stroke-width: 1; }
.chart .chart-axis-line { stroke: var(--hairline); stroke-width: 1; }
.chart .chart-num { fill: var(--muted); font-family: var(--font-mono); font-size: var(--fs-2xs); }
.chart .chart-axis { fill: var(--muted); font-family: var(--font-body); font-size: var(--fs-2xs); }

/* ---------- 实时监控表 ---------- */

.monitor-wrap {
  max-height: 300px;
  overflow: auto;
  border: 1px solid var(--hairline);
  border-radius: var(--r-ctrl);
  padding: var(--s-1);
  scrollbar-width: thin;
}

.monitor thead th { position: sticky; top: 0; background: var(--card-2); }
.monitor thead, .monitor tbody td:first-child { padding-left: var(--s-3); }
.monitor td { font-family: var(--font-mono); font-size: var(--fs-xs); }
.monitor td:nth-child(2) { font-family: var(--font-body); }
.monitor tr.wave-row td { color: var(--text); font-weight: 600; border-top: 1px solid var(--hairline-strong); }

/* ---------- 「这段话是给谁的」（R35） ---------- */

/* 焦点环沿用全局那条（--accent），圆角由控件自己的 border-radius 决定，这里不再改写。 */

/* 表单那一块：贴着提交按钮下面，视觉上属于「可选的补充」而不是必填项——所以不额外加框，
   只用一条 hairline 把它和主表单分开；填了内容它才占地方。 */
.aud-pick { margin-top: var(--s-4); padding-top: var(--s-3); border-top: 1px solid var(--hairline); }
.aud-pick > label { font-size: var(--fs-sm); color: var(--text); }
.aud-pick > label small { color: var(--muted); font-weight: 400; }

.aud-pick input {
  width: 100%;
  margin-top: var(--s-2);
  min-height: 36px;
  background: var(--card-2);
  border-color: var(--hairline-strong);
  padding: var(--s-2) var(--s-3);
}

.aud-pick input:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.aud-pick .hint { margin-top: var(--s-2); }

/* 一个字段 = 一行可见标签 + 一个控件。原来第一行标签被一个 div.row.between 包着，
   标签和输入框之间隔了一层容器，读屏软件按「标签→控件」相邻来找就断了；sr-only 的那条
   又把说明藏在 placeholder 里（placeholder 一打字就消失）。改成两个都是真正的可见标签：
   词表那一行的标签是这一节唯一的操作说明，不该靠 placeholder 撑。 */
.field { display: block; margin-top: var(--s-3); }
.field > label { display: block; margin-bottom: var(--s-1); }
/* 说明那两行允许折行：两条 hint 并排时各占一半宽，中文一折就成「半句话 + 半句话」，
   比换行更难读——换行后每句还是整句。 */
.aud-pick .row { flex-wrap: wrap; row-gap: var(--s-1); }

/* 过滤出来的候选：按钮而不是链接，点下去就是加一组。min-height 28px 是这个尺寸标签的
   命中区下限（padding 4+4 + 13px 行高 + 2px 边框 ≈ 26px）——一个 26px 的命中区在手机上
   很难点准，而这是这一节唯一的主交互。 */
.aud-list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2);
  margin-top: var(--s-3);
  max-height: 148px;
  overflow-y: auto;
  scrollbar-width: thin;
}

.aud-list button {
  font-size: var(--fs-sm);
  /* nowrap：标签是「年龄：25–34 岁 + 812」这种不长的固定串，折成两行会让人以为是两个组。
     真长到放不下时靠 flex-wrap 换行，不靠字内折行。 */
  white-space: nowrap;
  min-height: 28px;
  padding: var(--s-1) var(--s-3);
  border-color: var(--hairline-strong);
}

.aud-list button:hover { background: var(--inset); }
/* 已挑过的候选用 aria-pressed（不是 disabled，读屏才听得到），所以按下的样子得自己画：
   变淡 + 描边加重，和上面那条 :hover 的底色一起构成「已选 / 可选」两个状态。 */
.aud-list button[aria-pressed='true'] { opacity: 0.5; border-color: var(--face-glad); }
.aud-list button[aria-pressed='true']:hover { background: transparent; }
.aud-list .aud-n { font-family: var(--font-mono); font-size: var(--fs-xs); color: var(--muted); }

/* 已选：可点掉的一个个标签。作者挑的组是这一节的前提，挑错了要能一眼看见、一下改掉。 */
.aud-tags { display: flex; flex-wrap: wrap; gap: var(--s-2); margin-top: var(--s-3); }

.aud-tag {
  white-space: nowrap;
  min-height: 28px;
  font-size: var(--fs-sm);
  padding: var(--s-1) var(--s-2) var(--s-1) var(--s-3);
  border-color: var(--face-glad);
}

/* × 是 CSS 生成的装饰，不进可访问名（sr 读的是按钮自己的文字）；给键盘用户一个真图标位
   只是排版对齐，不额外读一遍。 */
.aud-tag::after { content: '×'; margin-left: var(--s-2); color: var(--muted); }
.aud-tag:hover { background: var(--inset); }

/* 报告里那一节：状态小标题。四个状态靠左边的色条区分，色就是「谁吃、谁掉头」的信号色。 */
.aud-state {
  margin: var(--s-4) 0 var(--s-1);
  padding-left: var(--s-2);
  border-left: 3px solid var(--hairline-strong);
  font-size: var(--fs-sm);
  color: var(--muted);
  letter-spacing: 0.04em;
}

.aud-state[data-state="hit"] { border-left-color: var(--face-glad); }
.aud-state[data-state="miss"] { border-left-color: var(--face-sorry); }
.aud-state[data-state="unsaid"] { border-left-color: var(--face-stopped); }
.aud-state[data-state="cold"] { border-left-color: var(--hairline-strong); }

/* 小镇在说 */
.said-row { margin: var(--s-2) 0; font-size: var(--fs-ui); line-height: 1.7; }
.said-row .what { color: var(--muted); }
.said-row .lead { color: var(--text); }

/* ============================================================
 * 09 示例与流
 * ============================================================ */

.examples { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-4); }

.examples .card {
  background: var(--card-2);
  border: 1px solid var(--hairline);
  border-radius: var(--r-lg);
  padding: var(--s-4);
  margin: 0;
  box-shadow: none;
}

/* 懒渲染占位（R22）：未渲染的示例卡不塌陷，滚到附近（rootMargin 300px）即填充 */
.examples .card:empty { min-height: 420px; }

#compare .source-text, #result .source-text { margin-bottom: var(--s-3); }
#resultActions { margin: calc(-1 * var(--s-2)) 2px var(--s-5); }

.feed { list-style: none; margin: 0; padding: 0; }

/* feed 行：整行是一块可悬停的面，摘要左、读数右；读数放不下时折到下一行（不硬撑单行） */
.feed li {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--s-1) var(--s-4);
  padding: var(--s-3);
  border-bottom: 1px solid var(--hairline);
  font-size: var(--fs-ui);
  border-radius: var(--r-md);
  transition: background-color var(--dur-2);
}

.feed li:hover { background: var(--card-2); }
.feed li:last-child { border-bottom: 0; }

.feed a {
  display: inline-block;
  padding: 2px 0;
  color: var(--text);
  text-decoration: none;
  transition: color var(--dur-2);
}

.feed li:hover a, .feed a:hover { color: var(--accent); }
.feed .meta { color: var(--muted); font-size: var(--fs-xs); }

/* ---------- 页脚 ---------- */

footer {
  margin-top: var(--s-7);
  padding-top: var(--s-5);
  border-top: 1px solid var(--hairline);
  color: var(--muted);
  font-size: var(--fs-sm);
  line-height: 1.75;
}

footer a {
  display: inline-block;
  padding: 2px 0;
  color: var(--muted);
  border-bottom: 1px solid var(--hairline-strong);
  text-decoration: none;
  transition: color var(--dur-2), border-color var(--dur-2);
}

footer a:hover { color: var(--text); border-bottom-color: var(--text); }

/* ============================================================
 * 10 动效（尊重 reduced-motion）
 * ============================================================ */

@media (prefers-reduced-motion: no-preference) {
  .top, .hero, .plate, #resultActions, footer {
    animation: rise 0.65s var(--ease-out) both;
  }
  .hero { animation-delay: 0.06s; }
  #compose { animation-delay: 0.12s; }
  #feedSection { animation-delay: 0.18s; }
  #ladder { animation-delay: 0.21s; }
  #showcase { animation-delay: 0.24s; }

  @keyframes rise {
    from { opacity: 0; transform: translateY(14px); }
    to { opacity: 1; transform: none; }
  }

  /* 设置弹窗入场：轻落而不是凭空闪现（backdrop 已有半透明压暗，这里只动卡片本身） */
  dialog#settings[open] {
    animation: dialog-in var(--dur-3) var(--ease-out);
  }

  @keyframes dialog-in {
    from { opacity: 0; transform: translateY(10px) scale(0.985); }
    to { opacity: 1; transform: none; }
  }
}

@media (prefers-reduced-motion: reduce) {
  * { transition-duration: 0.01ms !important; animation-duration: 0.01ms !important; }
  .sk { animation: none; }
}

/* ============================================================
 * 11 响应式
 * ============================================================ */

/* R33：761–1080px 之间这套五列会被挤坏——句子那一列 minmax(0,1fr) 先被压到 ~120px，
   一句 20 字的中文折成八行，而句子正是这一节的主体，不能被条挤碎。
   所以在够宽之前先把句子提成独占一行的左列：第 1 列标号、第 2 列句子（跨到行尾），
   下面那行才是条 + 人话 + 组数。两行读起来仍然一行一句，但句子拿回了整个宽度。 */
@media (max-width: 1080px) {
  .arow {
    grid-template-columns: 76px minmax(120px, 300px) minmax(0, 1fr);
    row-gap: var(--s-2);
  }
  .arow .flabel { grid-row: 1; grid-column: 1; }
  .arow .apart { grid-row: 1; grid-column: 2 / -1; }
  .arow .atwin { grid-row: 2; grid-column: 1 / 3; max-width: 300px; }
  .arow .fsay { grid-row: 2; grid-column: 3; }
  .arow .fnum { grid-row: 3; grid-column: 1 / -1; }
}

@media (max-width: 860px) {
  .examples { grid-template-columns: 1fr; }
}

/* 更窄时逐格落下来：标号与句子同一行（句子缩进让开标号），条、人话、组数各占一行。
   组数单独一行而不是并到条那行，是因为「N/83 组 · 均值 ±0.0234」有 18 个字符宽，
   在 300px 里和条抢位置只会把两边都挤短。 */
@media (max-width: 760px) {
  .arow { grid-template-columns: 52px minmax(0, 1fr); gap: var(--s-1) var(--s-2); }
  .arow .flabel { grid-row: 1; grid-column: 1; }
  .arow .apart { grid-row: 1; grid-column: 2; }
  .arow .atwin { grid-row: 2; grid-column: 1 / -1; max-width: none; }
  .arow .fsay { grid-row: 3; grid-column: 1 / -1; }
  .arow .fnum { grid-row: 4; grid-column: 1 / -1; }
}

@media (max-width: 720px) {
  .top { flex-wrap: wrap; }
  .stat b { font-size: var(--fs-lg); }
  .seg .label { min-width: 110px; }
  .plate { padding: var(--s-5) var(--s-4); }
  .charts-row { grid-template-columns: 1fr; }
}

/* 窄屏：标签独占一行，条与数字并排在下一行。
   否则 .label(110) + .num(96) 就把可用宽度吃光，比例条只剩一条细缝——等于没有。 */
@media (max-width: 560px) {
  /* kicker 的引线在窄屏会把一行字挤成三行怪断——恢复自然行流 */
  .driver { display: block; }
  .driver::before { display: none; }

  /* 规模数字条排两列：四项在窄屏折成 3+1 会读成一串散字 */
  .census {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--s-3) var(--s-4);
  }

  .seg { flex-wrap: wrap; row-gap: var(--s-1); }
  .seg .label { min-width: 0; flex: 1 1 100%; }
  .seg .bar2 { flex: 1 1 110px; max-width: none; }
  .seg .num { min-width: 0; }
  .decision .dseg .label { min-width: 0; }
  .frow { flex-wrap: wrap; row-gap: var(--s-1); }
  .frow .flabel { min-width: 0; flex: 1 1 100%; }
  .frow .fbar { flex: 1 1 100%; max-width: none; }
  .toc a { min-height: 30px; }

  /* 窄屏目录改单行横向滚动：换行会占掉近 250px，把正文整个推到首屏之外 */
  .toc {
    flex-wrap: nowrap;
    overflow-x: auto;
    padding-bottom: var(--s-1);
    scrollbar-width: thin;
    scrollbar-color: var(--hairline-strong) transparent;
    /* 右缘渐隐，提示还能往右滑 */
    mask-image: linear-gradient(to right, #000 0, #000 calc(100% - 28px), transparent 100%);
  }
  .toc a { flex: 0 0 auto; }
  .toc::-webkit-scrollbar { height: 4px; }
  .toc::-webkit-scrollbar-thumb { background: var(--hairline-strong); border-radius: 2px; }
  .toc::-webkit-scrollbar-track { background: transparent; }

  /* 总览 KPI 在窄屏排成两列，别让五个数字折成 3+2 的碎行 */
  .stats { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s-4) var(--s-4); }

  /* 窄屏地图与图例改为上下排：并排时 400px 的画布被挤成细条，图例也只剩一列窄字 */
  .map-wrap { flex-direction: column; gap: var(--s-3); }

  /* 示例卡是网格项：自动最小尺寸 = 内容 min-content，会把单列轨道撑到 600 多像素，
     整页跟着出现横向滚动条。目录此时已是 nowrap 滚动条（自己内部横滑），
     所以给网格项 min-width: 0，让轨道收缩到视口宽。与上面 .legend 同一套路。 */
  .examples .card { min-width: 0; }
  .legend { min-width: 0; }
}

/* ============================================================
 * 12 Instrument 现代层（C 方案）
 *
 * 这一层只做三件事：
 *   1. 首屏改成「叙事 + 输入控制台」双栏，输入的同时也看得见这台仪器在做什么；
 *   2. 可选玩法收进原生 details（渐进披露），主路径只剩类型、正文、主按钮；
 *   3. 实时区与报告改用"Instrument"的信息层级：先看结论，再允许下钻。
 * 全部是纯展示层改动：不改 DOM 契约里的数据来源，不改 JS 逻辑，动画只碰
 * transform / opacity（reduced-motion 由本文件末尾的全局兜底压平）。
 * ============================================================ */

/* ---------- 首屏双栏 ---------- */

.opening {
  display: grid;
  gap: var(--s-5);
}

@media (min-width: 860px) {
  .opening {
    grid-template-columns: minmax(0, 0.88fr) minmax(0, 1.12fr);
    align-items: center;
    gap: var(--s-7);
    margin-bottom: var(--s-2);
  }
  .opening .hero { padding: var(--s-6) 2px 0; }
  .opening #compose { margin-bottom: 0; }
}

:root[data-theme="instrument"] .hero h1 {
  font-weight: 750;
  letter-spacing: -0.045em;
}

/* 仪器主题的页面底纹换成 32px 网格：和 100×100 的人格网格同一个隐喻，
   不再是普查图纸的点阵。两张渐变各自一层 background-image，成本极低。 */
:root[data-theme="instrument"] body {
  background:
    linear-gradient(to right, color-mix(in srgb, var(--text) 5%, transparent) 1px, transparent 1px),
    linear-gradient(to bottom, color-mix(in srgb, var(--text) 5%, transparent) 1px, transparent 1px),
    radial-gradient(60rem 34rem at 12% -6%, var(--glow-1), transparent 62%),
    radial-gradient(52rem 30rem at 88% 12%, var(--glow-2), transparent 60%),
    var(--bg);
  background-size: 32px 32px, 32px 32px, auto, auto, auto;
  background-repeat: repeat, repeat, no-repeat, no-repeat, no-repeat;
}

/* 宽屏：人格网格缩到叙事区右下角，一枚亮格代表"正在被判定的那一个人"。 */
@media (min-width: 860px) {
  .hero .town-mark {
    display: block;
    position: absolute;
    top: 6px;
    right: 0;
    width: 104px;
    height: 104px;
    background-image:
      linear-gradient(to right, color-mix(in srgb, var(--text) 22%, transparent) 1px, transparent 1px),
      linear-gradient(to bottom, color-mix(in srgb, var(--text) 22%, transparent) 1px, transparent 1px);
    background-size: 10.4px 10.4px, 10.4px 10.4px;
    -webkit-mask-image: radial-gradient(closest-side, #000 62%, transparent);
    mask-image: radial-gradient(closest-side, #000 62%, transparent);
  }
  .hero .town-mark::after {
    content: "";
    position: absolute;
    left: 52%;
    top: 52%;
    width: 8px;
    height: 8px;
    background: var(--accent);
    transform: translate(-50%, -50%);
    box-shadow: 0 0 0 4px var(--accent-soft);
  }
}

/* ---------- 发帖控制台 ---------- */

#compose {
  position: relative;
  overflow: hidden;
}

/* 上缘一道信号条：面板不再是"又一张卡片"，而是仪器上的一块操作区。 */
#compose::before {
  content: "";
  position: absolute;
  inset: 0 0 auto;
  height: 3px;
  background: linear-gradient(90deg, var(--accent), color-mix(in srgb, var(--accent) 10%, transparent));
  opacity: 0.9;
}

.compose-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
  flex-wrap: wrap;
  margin-bottom: var(--s-3);
}

.compose-head .row {
  margin: 0;
  align-items: center;
  gap: var(--s-2);
}

.compose-head .row label {
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  letter-spacing: 0.12em;
  color: var(--muted);
}

.compose-note {
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  letter-spacing: 0.14em;
}

#compose textarea {
  min-height: 168px;
  padding: var(--s-4);
  border-radius: var(--r-lg);
  line-height: 1.75;
}

.compose-submit {
  align-items: center;
  gap: var(--s-4);
  margin-top: var(--s-4);
}

.compose-submit .hint {
  flex: 1 1 260px;
  min-width: 0;
}

#go {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  min-height: 44px;
  padding: var(--s-3) 22px;
}

.go-arrow { transition: transform var(--dur-2) var(--ease-out); }
#go:hover .go-arrow { transform: translateX(3px); }
#go:disabled .go-arrow { opacity: 0.6; }

/* 移动端输入字号 ≥ 16px：否则 iOS Safari 聚焦时会整页放大（既有防线，这里再兜一次） */
@media (max-width: 720px) {
  #compose textarea,
  #compose input[type="text"] { font-size: 16px; }
}

/* ---------- 渐进披露：可选玩法收进抽屉 ---------- */

.advanced-options,
.live-log {
  border-top: 1px solid var(--hairline);
}

.advanced-options {
  margin-top: var(--s-5);
  padding-top: var(--s-3);
}

.live-log {
  margin-top: var(--s-4);
  padding-top: var(--s-3);
}

.advanced-options > summary,
.live-log > summary,
.showcase-drawer > summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
  min-height: 40px;
  padding: var(--s-2) 0;
  cursor: pointer;
  list-style: none;
  font-size: var(--fs-ui);
  color: var(--muted);
  transition: color var(--dur-2);
}

.advanced-options > summary::-webkit-details-marker,
.live-log > summary::-webkit-details-marker,
.showcase-drawer > summary::-webkit-details-marker { display: none; }

.advanced-options > summary::after,
.live-log > summary::after,
.showcase-drawer > summary::after {
  content: "+";
  font-family: var(--font-mono);
  font-size: var(--fs-lg);
  line-height: 1;
  color: var(--accent);
}

.advanced-options[open] > summary::after,
.live-log[open] > summary::after,
.showcase-drawer[open] > summary::after { content: "−"; }

.advanced-options > summary:hover,
.live-log > summary:hover,
.showcase-drawer > summary:hover { color: var(--text); }

.advanced-hint { font-size: var(--fs-2xs); }

.advanced-body {
  padding-top: var(--s-4);
  animation: instrument-reveal var(--dur-3) var(--ease-out) both;
}

/* 抽屉里的旧「条形一带的分节」本来自带上边框，放进抽屉会多出一条割裂线。 */
.advanced-body .aud-pick {
  border-top: 1px solid var(--hairline);
  padding-top: var(--s-4);
  margin-top: var(--s-4);
}

.advanced-body > .row:first-child { margin-top: 0; }

@keyframes instrument-reveal {
  from { opacity: 0; transform: translateY(-4px); }
  to { opacity: 1; transform: none; }
}

/* ---------- 实时区：先看结论，再看下钻 ---------- */

.live-observation {
  display: grid;
  gap: var(--s-5);
  margin: var(--s-4) 0 var(--s-5);
}

@media (min-width: 900px) {
  .live-observation {
    grid-template-columns: minmax(0, 1.05fr) minmax(300px, 0.95fr);
    align-items: start;
  }
}

.live-panel > h3 {
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  letter-spacing: 0.1em;
  color: var(--muted);
  margin-bottom: var(--s-2);
}

.live-panel .map-wrap { margin: 0; }
.live-panel canvas.grid { width: 100%; }

.live-log[open] > summary { margin-bottom: var(--s-3); }

/* ---------- 报告：目录工具条 + 速览/完整 ---------- */

.report-toolbar {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--s-4);
  flex-wrap: wrap;
  margin: 0 0 var(--s-3);
}

.report-toolbar .toc {
  flex: 1 1 340px;
  margin: 0;
  align-items: center;
}

.report-mode { flex: 0 0 auto; }

@media (max-width: 720px) {
  .report-toolbar { flex-direction: column; gap: var(--s-3); }
  .report-mode { order: 1; }
  .report-toolbar .toc { order: 2; align-self: stretch; }
}

/* 速览模式下把次级小节连同正文一起收起：一次纯展示切换（数据仍在 DOM 里）。 */
[data-report-mode="quick"] [data-report-detail] { display: none !important; }

/* ---------- 存档示例抽屉：整块收进原生 details，默认收起 ---------- */

/* h2 放进 summary 保住文档大纲；.plate > h2 的直接子选择器够不到它，这里补齐同一套字。 */
.showcase-drawer > summary h2 {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--fs-lg);
  letter-spacing: 0.02em;
  line-height: 1.35;
  color: var(--text);
  text-wrap: balance;
}

.showcase-hint { font-size: var(--fs-2xs); }

.showcase-body {
  padding-top: var(--s-3);
  animation: instrument-reveal var(--dur-3) var(--ease-out) both;
}

/* ---------- 传播阶梯：同主题实测样本，条长 = 传到人数，格子 = 逐波情绪 ---------- */

.ladder { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s-3); }

.rung {
  border: 1px solid var(--hairline);
  border-radius: var(--r-md);
  padding: var(--s-3) var(--s-4);
  transition: background-color var(--dur-2);
}

.rung:hover { background: var(--card-2); }

/* 行 1：深度标签 · 条（宽度=reach，与漏斗同语） · 人数。条轨用发丝线，填充走 accent。 */
.rung-line {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  margin-bottom: var(--s-2);
}

.rung-depth { font-size: var(--fs-sm); color: var(--text); font-weight: 600; white-space: nowrap; }

.rung-bar {
  flex: 1 1 120px;
  min-width: 60px;
  height: 6px;
  border-radius: var(--r-pill);
  background: var(--hairline);
  overflow: hidden;
}

.rung-bar i { display: block; height: 100%; border-radius: var(--r-pill); background: var(--accent); }

.rung-reach { font-family: var(--font-mono); font-size: var(--fs-xs); color: var(--muted); }

.rung-body { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: var(--s-2) var(--s-4); align-items: start; }

.rung-name { font-size: var(--fs-sm); color: var(--muted); }
.rung-name small { color: var(--muted); font-weight: 400; }

.rung-text { margin: 0; font-size: var(--fs-ui); line-height: 1.7; color: var(--text); grid-column: 1; }

/* 逐波情绪格：颜色语义交给小圆点（主题令牌），文字保持主题安全对比度。 */
.rung-mood { display: flex; gap: var(--s-2); flex-wrap: wrap; align-items: center; grid-column: 2; grid-row: 2; }

.mood-cell {
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  color: var(--muted);
  border: 1px solid var(--hairline);
  border-radius: var(--r-pill);
  padding: 2px var(--s-2);
  white-space: nowrap;
}

.mood-cell::before {
  content: "";
  display: inline-block;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  margin-right: 6px;
  vertical-align: 1px;
}

.mood-cell.pass::before { background: var(--face-glad); }
.mood-cell.miss::before { background: var(--face-sorry); }

.rung-link {
  grid-column: 2;
  grid-row: 1;
  justify-self: end;
  font-size: var(--fs-sm);
  color: var(--accent);
  text-decoration: none;
  border-bottom: 1px solid transparent;
  transition: border-color var(--dur-2);
}

.rung-link:hover { border-bottom-color: var(--accent); }

/* 同档其他写法：一行小注，收住不铺开。 */
.rung-siblings {
  list-style: none;
  margin: var(--s-2) 0 0;
  padding: var(--s-2) 0 0;
  border-top: 1px solid var(--hairline);
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-1) var(--s-4);
  grid-column: 1 / -1;
}

.rung-siblings a { font-size: var(--fs-xs); color: var(--muted); text-decoration: none; }
.rung-siblings a:hover { color: var(--accent); }

.ladder-moral { margin: var(--s-4) 0 0; padding-top: var(--s-3); border-top: 1px solid var(--hairline); }
.ladder-moral b { color: var(--text); font-weight: 600; }

@media (max-width: 720px) {
  .rung-body { grid-template-columns: 1fr; }
  .rung-mood { grid-column: 1; grid-row: auto; }
  .rung-link { grid-column: 1; grid-row: auto; justify-self: start; }
}
