/* 键盘检测台 —— 跟随系统深浅色 */

/* ── 字体：IBM Plex Mono（自托管，拉丁子集）──────────────────────
   仪器面板的丝印与读数必须是等宽字：数字跳变时不抖动，编号对齐成列。
   只取拉丁 + 数字子集（29.6KB / 两个字重），中文仍走系统字体栈
   —— 中文子集动辄数 MB，不值当。
   unicode-range 限定拉丁区间：中文字符不会去请求这个字体。
   font-display: swap —— 字体没到先用系统等宽顶上，不阻塞首屏。 */
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('../fonts/ibm-plex-mono-latin-400-normal.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191,
    U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('../fonts/ibm-plex-mono-latin-500-normal.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191,
    U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
  --bg: #ffffff;
  --bg-sub: #f7f7f5;
  --bg-tertiary: #efefec;
  --border: rgba(0, 0, 0, .12);
  --border-strong: rgba(0, 0, 0, .22);
  --text: #1c1c1a;
  --text-secondary: #5f5e5a;
  --text-tertiary: #6e6c67;   /* 浅色：bg-sub 上 ≈4.9:1，达 WCAG AA 正文 4.5:1（问题 5）。暗色 #74737a 不动。 */

  /* 键位配色：未测/已测必须一眼可辨。
     之前用 #f1efe8 vs #e4e1d7 对比度仅 1.14:1，几乎看不出。
     现在未测是中性浅灰，已测是明显的绿色调，对比度 > 3:1。 */
  --key-bg: #d2cfc6;              /* 未测：中性灰，与页面底对比 1.35:1 */
  --key-border: rgba(0, 0, 0, .18);
  --key-text: #4a4a46;            /* 未测键文字，对比度 6.49:1（WCAG 正文达标） */
  --key-tested: #2a805c;          /* 已测：深绿，对比度 3.53:1（WCAG 非文字图形达标） */
  --key-tested-text: #ffffff;
  --key-tested-border: #1d6547;
  --key-active: #7f77dd;
  --key-active-text: #ffffff;
  --key-warn: #e0a020;
  --key-bad: #e57373;
  --key-next: #534ab7;
  --key-border-strong: rgba(0, 0, 0, .34);
  /* 坏键：红调+ 叉号。文字对比度 5.62:1（WCAG 正文达标） */
  --key-dead: #c0392b;
  --key-dead-text: #ffffff;
  --key-dead-border: #96271b;

  --accent: #534ab7;
  --ok: #0f6e56;
  --warn: #854f0b;
  --bad: #a32d2d;

  --chart-line: #7f77dd;
  --chart-grid: rgba(0, 0, 0, .08);
  --chart-bar: #1d9e75;

  --bg-elev: #ffffff;   /* 色带底：内容带整块压暖白，里面的卡片才浮得起来（对标站 #FDFCF8 同思路）。 */
  --bg-band: #f7f5ef;
  --bg-invert: #1c1917;          /* 反色锚点卡（对标站 rgb(28,25,23)） */
  --text-invert: #f5f4f1;
  --text-invert-dim: #b8b4ad;   /* 浮起表面（卡片/内容带）—— 比页面底更亮才有层次 */
  --radius: 12px;

  /* ── 设计 token（DESIGN-TOKENS.md，由 check-ui.mjs 守护）──────────
     字阶 10 档 / 间距 8px 刻度 / 行高 4 档 / 圆角 5 档 + 胶囊 / 阴影 3 档。
     字面量一律走 var()，新增样式不得直接写 px。 */
  --fs-2xs: 10px;   --fs-xs: 11px;   --fs-sm: 12px;
  --fs-base: 13.5px; --fs-md: 15px;  --fs-lg: 17px;
  --fs-xl: 20px;    --fs-2xl: 24px;  --fs-3xl: 30px;
  --fs-4xl: 40px;

  --sp-1: 4px;  --sp-2: 8px;  --sp-3: 12px; --sp-4: 16px;
  --sp-5: 20px; --sp-6: 24px; --sp-8: 32px; --sp-10: 40px;

  --lh-flat: 1;  --lh-tight: 1.2;  --lh-body: 1.5;  --lh-loose: 1.7;

  /* 字重 4 档（DESIGN-TOKENS 1b 节）。此前字重无规范，实测 5 档并存，
     其中 800 只出现 1 处且无依据 —— 系统字体 800 与 700 差异极小，
     拿它当层级手段等于制造伪层级。 */
  --fw-normal: 400;  --fw-med: 500;  --fw-semi: 600;  --fw-bold: 700;

  --r-sm: 4px;  --r-md: 8px;  --r-lg: 12px;  --r-xl: 16px;  --r-pill: 999px;
  --r-2xl: 22px;

  --sh-1: 0 1px 2px rgba(0, 0, 0, .06);
  --sh-2: 0 2px 0 rgba(0, 0, 0, .1);
  --sh-3: 0 8px 24px rgba(0, 0, 0, .12);

  /* ── 仪器面板色板（Instrument Panel）──────────────────────────
     墨色面板（状态条 / logo 底）承载实时读数，琥珀是唯一的功能强调色
     （交互激活、当前读数）；语义色（绿/黄/红）只用于判定，不用于装饰。
     两者职责分离：看到琥珀=可操作，看到绿黄红=测量结果。 */
  --font-mono: 'IBM Plex Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  --ink: #22231f;
  --ink-edge: rgba(255, 255, 255, .06);   /* 墨盒内描边：亮色下几乎不可见，暗色下是唯一的边界 */
  --ink-line: #3a3c35;
  --ink-text: #e8e6dc;
  --ink-dim: #8a8c82;
  --amber: #b45309;
  --amber-bright: #f5c775;
  --lamp-on: #1d9e75;
  --lamp-glow: rgba(74, 222, 128, .18);   /* LIVE 灯的光晕，跟着 --lamp-on 走 */
  --lamp-warn: #ef9f27;
  --lamp-bad: #e24b4a;
  --lamp-off: #b4b2a9;
  /* 反色卡内的图标色：浅色下是浅粉，暗色下换深赭（见暗色块的同名变量）。
     提到变量是因为它跟着反色卡一起翻转，写死会在暗色下对比不足。 */
  --doc-icon-invert: #e8b4a0;

  /* 正文阅读宽度：一行 40–55 个汉字最易读，超 80 字眼睛会漏行。
     键盘区有 1264px 宽，正文铺满会一行 90+ 字 —— 必须单独限宽。 */
  --doc-measure: 760px;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #17171a;
    --bg-sub: #1e1e22;
    --bg-tertiary: #26262b;
    --bg-elev: #1e1e22;      /* 暗色下卡片比页面底亮一档 */
    --border: rgba(255, 255, 255, .12);
    --border-strong: rgba(255, 255, 255, .24);
    --text: #ececee;
    --text-secondary: #a3a2a8;
    --text-tertiary: #74737a;

    --key-bg: #3a3a42;              /* 未测：中性灰，明显亮过背景 */
    --key-border: rgba(255, 255, 255, .16);
    --key-text: #b8b8c0;            /* 未测键文字，对比度 7.24:1 */
    --key-tested: #0d9d66;          /* 已测：亮绿，与未测键底对比 3.24:1 */
    --key-tested-text: #04140d;
    --key-tested-border: #3fce8f;
    --key-active: #7f77dd;
    --key-active-text: #ffffff;
    --key-warn: #b8860b;
    --key-border-strong: rgba(255, 255, 255, .34);
    /* 坏键：深红底 + 亮红边，文字对比度 6.12:1 */
    --key-dead: #7a1f16;
    --key-dead-text: #ffd9d4;
    --key-dead-border: #e05a4a;
    --key-bad: #a04a4a;
    --key-next: #a9a2ec;

--accent: #a9a2ec;
  --on-accent: #17171a;
  --ok: #5dcaa5;
    --warn: #ef9f27;
    --bad: #f09595;

    --chart-line: #a9a2ec;
    --chart-grid: rgba(255, 255, 255, .1);
    --chart-bar: #5dcaa5;

    /* ── 页面层（v1.17.6 补）────────────────────────────────────
       之前暗色只覆盖了键位与图表，页面层三组变量全部漏掉：
         --bg-band    内容带底 → 漏了，暗色页中间横插 1304px 米白长带（对比 13.81:1）
         --bg-invert  反色锚点卡 → 漏了，暗色下仍是 #1c1917，与卡片底 #1e1e22 几乎同色，
                                  「跟别人不一样」的语义直接归零
         --text-invert / --text-invert-dim → 跟着反色卡一起漏

       配值原则：暗色下「亮 = 浮起」，与亮色模式「亮 = 浮起」同构。
       所以内容带要比页面底亮一档、卡片比内容带再亮一档 ——
       不是把浅色值调暗，而是把层次关系翻译过来。               */
    --bg-band: #1b1b1f;          /* 内容带底：比页面底(#26262b)沉一档，卡片(#1e1e22)浮在它上面 */
    --bg-invert: #f0ede4;        /* 反色卡在暗色下反过来：亮色卡，成为整段里唯一的亮块 */
    --text-invert: #1c1c1a;
    --text-invert-dim: #55534c;  /* 与 --bg-invert 配对：暗字亮底 */
    /* 反色卡内的图标色：亮底上原 #e8b4a0(浅粉) 对比不足，改深一档的赭（6.74:1） */
    --doc-icon-invert: #8f3319;

    /* ── 仪器面板（读数窗 / logo 底 / LIVE 灯）──────────────────
       暗色下墨色面板必须比页面底更沉，否则「浮起的墨盒」会反过来
       变成页面上一块比周围亮的斑块，仪器语言就翻了。
       亮色：#22231f 墨盒浮在 #f7f7f5 页底上（暗浮于亮）
       暗色：#101012 墨盒沉在 #26262b 页底上（更暗浮于更暗，仍是独立一层） */
    --ink: #101012;
    --ink-edge: rgba(255, 255, 255, .10);
    --ink-line: #2e2f2c;         /* 读数窗内的分隔竖线 / LIVE 灯灭时的芯 */
    --ink-text: #e8e6dc;
    --ink-dim: #93958a;
    --amber: #e08a2e;           /* 琥珀在暗底上要提亮，否则滑块把手看不见 */
    --amber-bright: #f5c775;
    --lamp-on: #35c48a;          /* 亮底荧光会刺眼，暗底上要更亮才对 */
    --lamp-glow: rgba(53, 196, 138, .22);
    --lamp-warn: #ef9f27;
    --lamp-bad: #e24b4a;
    --lamp-off: #55564f;         /* 灯灭 = 沉进墨盒，不是灰 */
  }
}

* { box-sizing: border-box; }

body {
  margin: 0;
  /* 环境色斑（对标站实测：背景有 500–600px 的低饱和色斑）。
     抄色值不算学设计，但「底色有呼吸感」是结构之外最便宜的丰富度：
     卡片浮在有温度的底上，而不是贴在死灰墙上。
     background-image 画在 background-color 之上，卡片自有背景会盖掉它，
     —— 只在卡片缝隙与页面两侧透出来，这正是要的效果（不是全屏染色）。 */
  background-color: var(--bg-tertiary);
  background-image:
    radial-gradient(520px 520px at 6% -6%, rgba(83, 74, 183, .10), transparent 68%),
    radial-gradient(600px 600px at 96% 2%, rgba(15, 118, 110, .09), transparent 70%);
  background-repeat: no-repeat;
  background-attachment: fixed;
  color: var(--text);
  font: var(--fs-base)/var(--lh-body) system-ui, -apple-system, "Segoe UI", "PingFang SC",
        "Microsoft YaHei", sans-serif;
}

/* 暗色下的色斑：降饱和、提冷调。
   直接沿用亮色的深紫/深青会变成「脏斑」—— 暗底上高饱和色会发闷。 */
@media (prefers-color-scheme: dark) {
  body {
    background-image:
      radial-gradient(520px 520px at 6% -6%, rgba(127, 119, 221, .14), transparent 68%),
      radial-gradient(600px 600px at 96% 2%, rgba(93, 202, 165, .10), transparent 70%);
  }
}

/* ── 顶栏 ─────────────────────────────────────────── */
/* ── 顶栏 ──────────────────────────────────────────────────────────
   外层 .topbar 只负责**背景铺满 + 分隔线**；
   内层 .topbar-inner 承接与键盘区相同的宽度，让内容左右对齐。
   ─────────────────────────────────────────────────────────────────
   ⚠️ 必须用内层容器，不能直接给 flex 子项设width：
      `.brand` / `.toolbar` 是 flex item，它们的 width 会被
      flex-basis 覆盖，设了不生效（实测：内容仍铺到0~1920）。
   ───────────────────────────────────────────────────────────────── */
.topbar {
  padding: 0;
  background: var(--bg);
  border-bottom: 1px solid var(--border);
}
.topbar-inner {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--sp-4); flex-wrap: wrap;
  padding: var(--sp-5) var(--sp-6);
  /* 与白卡同宽并居中 —— --card-w 由 render.js 写到 body 上 */
  width: var(--card-w, 100%);
  margin-inline: auto;
}
.brand { display: flex; align-items: center; gap: 12px; }
/* 品牌图形（v1.9.2）：「键」字的楷体轮廓（SVG path）
   —— 不用图形 logo：四版图形全撞「挂锁」几何特征（弧形盖 + 方形底座）。
   —— 不用字体文件：华文楷体 12.7MB，转path 后 14KB，且跨设备渲染一致。

   容器给 padding：字形轮廓贴边会显得拥挤，
   留出内边距让「键」字有呼吸空间（30px 容器里字占 26px）。*/
/* 键帽单字标（logo.svg）自带纸色键面与硬阴影，容器不再铺墨底 / currentColor */
.logo {
  width: 36px; height: 36px;
  display: flex; align-items: center; justify-content: center;
  flex: none;
  background: transparent;
  border-radius: var(--r-md);
  overflow: hidden;
}
.logo img { display: block; width: 36px; height: 36px; }
/* 品牌区：标题与规格副标同字号/等宽，靠键帽 logo 扛品牌重量 */
.brand-text { display: flex; align-items: baseline; gap: 10px; min-width: 0; }
.topbar h1 {
  margin: 0;
  font-size: var(--fs-xs);
  font-weight: var(--fw-normal, 400);
  font-family: var(--font-mono);
  letter-spacing: .1em;
  color: var(--text-tertiary);
}
.sub {
  margin: 0; font-size: var(--fs-xs); color: var(--text-tertiary);
  font-family: var(--font-mono);
  letter-spacing: .1em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* ⚠️ 窄屏必须撤掉这行规格文字（回归：错误 75）。
   它是 nowrap 的，一旦放不下就会把 .brand 撑到 392px —— 375 视口下
   整页被顶出 53px 横向滚动条。加大 logo 与 H1 之后才暴露出来，
   说明**顶栏从没有窄屏兜底**，不是这次改大才有的隐患。
   撤掉不是丢信息：这条规格在内容带里以表格形式完整出现。 */
@media (max-width: 720px) {
  .sub { display: none; }
}

.toolbar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.field { display: flex; align-items: center; gap: 6px; font-size: var(--fs-sm); color: var(--text-secondary); }

/* ── 配列标识（只有 108 一套，纯展示）───────────────────────────── */
.layout-badge {
  font-size: var(--fs-xs);
  font-family: var(--font-mono);
  letter-spacing: .1em;
  color: var(--text-tertiary);
  white-space: nowrap;
}

/* ── 坏键标记按钮 ──────────────────────────────────────────
   悬停键帽时出现在右下角。浏览器判断不了坏键，只能让用户手动标。

   ⚠️ 这些按钮用 tabindex="-1" 移出了 Tab 序列（审查 B1）：
   opacity:0 只是视觉隐藏，元素仍可被键盘聚焦，而浏览器会把
   「空格/回车」翻译成对聚焦按钮的 click → 用户按空格测空格键，
   却凭空把别的键标成坏键。不用鼠标时改用 Shift+D（标记引导指向的键）。 */
.key-flag {
  position: absolute;
  right: 2px; bottom: 2px;
  width: 11px; height: 11px;
  padding: 0;
  border: 1px solid var(--key-border-strong);
  border-radius: var(--r-sm);
  background: var(--key-bg);
  cursor: pointer;
  opacity: 0;
  transition: opacity .12s;
}
.key:hover .key-flag { opacity: .55; }
.key-flag:hover { opacity: 1; }
.key-flag:focus-visible { opacity: 1; outline: 2px solid var(--accent); outline-offset: 1px; }

/* 坏键：红底 + 叉号，双通道表达（不只靠颜色） */
.key.dead {
  background: var(--key-dead);
  color: var(--key-dead-text);
  border-color: var(--key-dead-border);
}
.key.dead .key-flag { opacity: 1; background: var(--key-dead-border); border-color: transparent; }
.key.dead::after {
  content: "\2715";
  position: absolute;
  top: 1px; right: 3px;
  font-size: var(--fs-2xs);
  color: var(--key-dead-text);
}

/* 标记成功的瞬时反馈 */
.key.flash { animation: keyflash .4s ease-out; }
@keyframes keyflash {
  0%   { box-shadow: 0 0 0 3px var(--key-dead-border); }
  100% { box-shadow: 0 0 0 3px transparent; }
}

.btn {
  font: inherit; font-size: var(--fs-base);
  padding: 6px 12px;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--bg-sub);
  color: var(--text);
  cursor: pointer;
  transition: background .12s, border-color .12s;
}
.btn:hover { background: var(--bg-tertiary); border-color: var(--border-strong); }
/* 按下态：位移 1px + 底色收深，给「已按下去」的确认感（六态最低线） */
.btn:active { transform: translateY(1px); background: var(--bg-sub); }
.btn:disabled { opacity: .55; cursor: default; }
.btn.on { background: var(--ink); border-color: var(--ink); color: var(--ink-text); }
/* 状态灯：开=绿，关=灰。让「状态」不靠读文字，靠一眼扫过灯的颜色。 */
.toolbar .btn { display: inline-flex; align-items: center; gap: 6px; }
.toolbar .btn::before {
  content: "";
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--lamp-off);
  flex: none;
}
.toolbar .btn.on::before { background: var(--lamp-on); }
.toolbar .btn#reset-btn::before { display: none; }
.toolbar .btn.game-nav::before { display: none; }
/* 游戏入口组：与「引导/Tab」开关区分开 */
.toolbar .game-links {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
  margin-right: 4px;
}
.toolbar .btn.game-nav {
  border-color: var(--border-strong);
  color: var(--text);
  background: var(--bg);
}
.toolbar .btn.game-nav:hover {
  background: var(--bg-tertiary);
  border-color: var(--ink);
}
.toolbar .btn.game-nav.is-primary,
.toolbar .btn#play-nav {
  border-color: var(--ink);
  color: var(--ink-text);
  background: var(--ink);
}
.toolbar .btn.game-nav.is-primary:hover,
.toolbar .btn#play-nav:hover {
  filter: brightness(1.08);
}

/* ── 内联说明块（.note）─────────────────────────
   ⚠️ 这里的 .note-inner / .note.ok / .note.warn / .note + .note
   原是给已删除的 #win-row / #hint-detail 用的，现已无消费者（待下一轮清理）。 */
.note {
  margin: 0; padding: 0;
  font-size: var(--fs-sm); line-height: var(--lh-body);
  background: var(--bg-sub);
  border-bottom: 1px solid var(--border);
  color: var(--text-secondary);
}
/* 提示条内层：与顶栏、键盘区同宽（背景仍铺满） */
.note-inner {
  width: var(--card-w, 100%);
  margin-inline: auto;
  padding: 8px 20px;
}
.note.hidden { display: none; }
.note strong { color: var(--text); font-weight: var(--fw-med); }
.note.ok { color: var(--ok); }
.note.warn { color: var(--warn); }
.note + .note { border-top: none; }

/* ── 主布局 ──────────────────────────────────────────────
   单列：键盘区占满整行。**没有左右分栏，也没有下方的诊断带。**
   工具区的信息全部落在键帽（绿/灰/黄/红四态）与顶栏读数条上。

   演进史（每一步都有实测出处，别按字面改回去）：
   ① 最早的「键盘 | 侧栏」左右两栏：键盘是主功能却只拿 75% 宽
      —— 视口 1440 时键盘区 1067px / 侧栏 317px，1u 被压到 45px，
      标签可读性下降；键盘区下方还有 42% 高度空着。
   ② 2026-10-07 改成「键盘在上、诊断带在下」：键盘拿满宽度，
      但诊断带把工具区撑高，页面纵向节奏被四张卡拖着。
   ③ v1.18.0 把诊断带整块移除（boss 决策）：只剩键盘 + 读数条，
      主客关系回到「工具是主角、内容带是补充」。*/
.layout {
  display: grid;
  /* 整块内容（键盘白卡）按白卡实际宽度居中。
     --card-w 由 render.js 的 relayout() 写入（视口 → 1u → 白卡宽，单向传递）。
     没拿到该变量时回退到 100% 宽度，保证 JS 未执行时也不塌。 */
  grid-template-columns: minmax(0, 1fr);
  justify-content: center;
  gap: 12px;
  padding: 16px 20px;
  align-items: start;
}

/* 很窄（<720px）：收窄内边距 */
@media (max-width: 720px) {
  .layout { padding: 12px; }
}

/* ── 键位舞台 ──────────────────────────────────────────
   白色卡片**贴合键盘实际大小**，不铺满整行（2026-10-07，boss 建议）。

   实现要点：断开了「容器宽度 ↔ 1u」的循环依赖 ——
   ① 容器用fit-content + inline-block，宽度跟随 #stage（即键盘实际宽度）
   ② 1u 仍然按**视口可用宽度**算（见 app.js 的 stageWidth()），
      不再读容器宽度

   为什么要断开：若 1u 仍读容器宽度，就成了
   「容器依赖 1u → 1u 依赖容器」的循环。
   浏览器会把 fit-content 解析成 0（因为子元素宽度也依赖它），
   实测容器一度只剩 1280px 减去内容宽度的差值。*/
.stage-wrap {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: 12px;
  /* 1u 小于 MIN_UNIT 时允许横向滚动 —— 拖动看全键盘，
     好过把 104 个键压成看不清的一团。 */
  overflow-x: auto;
  overflow-y: hidden;
  /* 容器宽度跟随键盘，但不超过视口（键盘超宽时可横向滚动） */
  width: fit-content;
  max-width: 100%;
  margin: 0 auto;                /* 窄屏时居中，不贴左边 */
  box-sizing: border-box;
}
/* min-width:0 会让宽度塌成 0，必须去掉 ——
   它原本是给 grid 子项用的（避免溢出），
   在 fit-content 场景下反而会阻止内容撑开。 */
.stage-inner { width: fit-content; max-width: none; }
.stage { /* 尺寸由 fit() 按视口可用宽度计算，不使用 transform 缩放 */ }
.stage-hint { margin: 12px 0 0; font-size: var(--fs-sm); color: var(--text-tertiary); }

kbd {
  display: inline-block; padding: 1px 4px;
  border: 1px solid var(--border-strong); border-radius: var(--r-sm);
  background: var(--bg-sub); font-size: var(--fs-xs); font-family: inherit;
}

/* ── 焦点哨兵 ─────────────────────────────────────────────
     用于把 Tab 焦点锁在页面内，防止焦点跑进浏览器地址栏。
     表现：占 1px、完全透明、但可聚焦（不能用 display:none 或
     visibility:hidden，那会让它从 Tab 序列消失）。 */
#focus-sentinel-start, #focus-sentinel-end {
  width: 1px;
  height: 1px;
  overflow: hidden;
  opacity: 0;
  pointer-events: none;
  outline: none;
}

/* 焦点在页内移动时给出可见指示 ——
   Tab 键测试必须让用户看得见焦点落在哪，否则无法判断按键是否生效 */
.btn:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
     /* ── 键帽 ────────────────────────────────────────────────
     状态靠「颜色 + 角标」双通道表达，不只靠颜色 ——
     色弱用户看不出深浅差异，但能看出有无角标。 */
.key {
  position: absolute;
  display: flex; align-items: center; justify-content: center;
  background: var(--key-bg);
  border: 1px solid var(--key-border);
  color: var(--key-text);
  /* 字号跟随 1u（--u 由 fit() 按容器宽度写入）。
     用 clamp 兜底：即使 --u 缺失（首次渲染前）也能有个可读值，
     不会因为 inherit 到 0 而全部消失。 */
  font-size: clamp(8px, calc(var(--u, 54px) * 0.21), 12px);
  cursor: default;
  user-select: none;
  transition: background .08s, color .08s, border-color .08s;
}
.key-label {
  pointer-events: none;
  white-space: nowrap;
  /* 长标签自动缩小，避免截断成「Scroll...」 */
  font-size: var(--fs-2xs);
  line-height: var(--lh-flat);
}
/* 窄键（小键盘数字）用更小的字号 */
.key[data-code^="Numpad"] .key-label,
.key[data-code^="Num"] .key-label {
  font-size: var(--fs-2xs);
}
/* 空格键留白即可，不印字 */
.key[data-code="Space"] .key-label {
  opacity: .25;
}

/* 已测过的键：绿底 + 深色字，另加左下角标作为非颜色标识 */
.key.pressed {
  background: var(--key-tested);
  color: var(--key-tested-text);
  border-color: var(--key-tested-border);
}
.key.pressed::after {
  content: "";
  position: absolute;
  left: 4px; bottom: 4px;
  width: 5px; height: 5px;
  border-radius: 50%;
  background: currentColor;
  opacity: .55;
  pointer-events: none;
}

.key.active {
  background: var(--key-active);
  color: var(--key-active-text);
  border-color: var(--key-active);
}
.key.active::after { opacity: .9; }

.key.bounce { border-color: var(--key-warn); box-shadow: inset 0 -2px 0 var(--key-warn); }
.key.stutter { border-color: var(--key-warn); }
.key.bounce.active, .key.stutter.active { box-shadow: none; }
.key.lock-on { border-color: var(--ok); }
.key.untestable { opacity: .4; }
.key.untestable::after { display: none; }
.key.next-target {
  border-color: var(--key-next);
  animation: pulse 1.4s ease-in-out infinite;
}
@keyframes pulse {
  0%, 100% { box-shadow: 0 0 0 0 transparent; }
  50% { box-shadow: 0 0 0 3px color-mix(in srgb, var(--key-next) 30%, transparent); }
}
@media (prefers-reduced-motion: reduce) {
  .key.next-target { animation: none; box-shadow: 0 0 0 2px var(--key-next); }
}

/* ── 旧侧栏卡片样式已于 v1.18.0 整块移除 ─────────────────
   史注保留 —— 这两条教训对之后任何新增面板都成立：
   ⚠️ 这里曾有两处 `.side { flex-direction: … }`，文件末尾那处
      **静默覆盖**了前面那处：CSS 写着横向平铺，浏览器算出来是纵向堆叠
      （诊断卡竖着堆 5 行、页面溢出 668px）。
      → **同一选择器只能定义一次**（错误 4 的变种）。
   ⚠️ 另有一条 `.card + .card { margin-top: 12px }` 是纵向堆叠时代的遗留，
      改成横向 wrap 之后它**仍然生效**，作用变成「让第 2/3/4 张卡顶部低
      12px」—— 从截图上看只是「有点没对齐」（P1-1，2026-10-10 审查已删）。
      → **旧布局的遗留规则不会自己失效，它换个布局继续起作用。**
   面板层一律不投影：硬阴影只用于页面层内容带大卡（.kbdoc），
   见 DESIGN-STATEMENT §3.6「不投影」与 DESIGN-TOKENS §5 的 --sh-2。 */

/* 模块铭牌：用「编号 + 英文功能名 + 中文名」标记内容带的每一节，
   取代单纯加粗的标题 —— 编号让页面成体系（01→03 可循），
   小号等宽 + 字距是面板丝印的标准做法。 */
.card-tag {
  margin: 0 0 6px;
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  letter-spacing: .14em;
  color: var(--text-tertiary);
  display: flex; align-items: center; gap: 6px;
}
.card-tag::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--border);
}
/* ── 实时读数条 ─────────────────────────────────────────────
   仪器在工作时必须有「活着」的信号：会话时长、事件数、当前键、并发峰值。
   深墨条是面板上的读数窗 —— v1.18.0 起它是全页**唯一**的实时数据出口
   （原先散在统计卡里的读数，现在都收在这一条上）。 */
/* 读数条从「满幅墨带」改成「浮起的墨色读数窗」：
   满幅带与顶栏的白底直接相接，两条横带贴在一起分不出主次；
   内嵌圆角盒有了边界，才是面板上的一块仪表，而不是页面的一条分隔线。
   （外层只留上下间距，背景交给页面底 + 环境色斑透出来。） */
.readout-bar {
  padding: var(--sp-4) var(--sp-5) var(--sp-2);
  background: transparent;
  border-bottom: none;
}
.readout-inner {
  display: flex; flex-wrap: wrap; align-items: center; gap: 0;
  width: var(--card-w, 100%);
  margin-inline: auto;
  padding: 10px var(--sp-5);
  background: var(--ink);
  border-radius: var(--r-lg);
  /* 墨盒与页面底的明度差：亮色 13.7:1、暗色 1.26:1 —— 暗色下只靠色差已经
     分不出边界了（两边都是暗色）。补一道内描边，任何主题下都有一条确定的边，
     读数窗才始终是「面板上的一块仪表」而不是一片色块。 */
  box-shadow: inset 0 0 0 1px var(--ink-edge);
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  letter-spacing: .06em;
}
/* LIVE 灯：仪器必须能一眼看出「在不在采数」。
   未按键是暗的，有事件后常亮 —— 不闪烁，闪烁会被误读成告警。 */
.rd-live { margin-left: auto; display: inline-flex; align-items: center; gap: 6px; color: var(--ink-dim); }
.rd-live i {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--ink-line);
  flex: none;
  transition: background .15s;
}
.rd-live.on { color: var(--ink-text); }
.rd-live.on i { background: var(--lamp-on); box-shadow: 0 0 0 3px var(--lamp-glow); }
.readout-inner > span { padding-right: 16px; }
.readout-inner > span + span { padding-left: 16px; border-left: 1px solid var(--ink-line); }
/* 窄屏换行后，行首的 span 仍带着左竖线像「断行误伤」（问题 7）。
   堆叠时改为上下分隔，去掉左竖线。 */
@media (max-width: 720px) {
  .readout-inner { flex-direction: column; align-items: flex-start; gap: 6px; }
  .readout-inner > span { padding-right: 0; }
  .readout-inner > span + span { border-left: none; padding-left: 0; border-top: 1px solid var(--ink-line); padding-top: 6px; }
}
.readout-inner b { color: var(--ink-text); font-weight: var(--fw-med); font-variant-numeric: tabular-nums; }
.readout-inner .rd-last b { color: var(--amber-bright); }
/* 配列外的键：换个色 + 带 ? —— 「收到了，但它不在 108 全尺寸配列里」，
   与「没收到」是两回事，颜色必须区分开（错误 79） */
.readout-inner .rd-last b.unk { color: var(--text-tertiary); font-style: italic; }

.foot {
  padding: 16px 20px 24px;
  font-size: var(--fs-xs); color: var(--text-tertiary); text-align: center;
}

/* 页脚链接：继承父级颜色 —— 浏览器默认蓝既不在色板里，暗色下也压不住背景
   （错误 93 同族：颜色必须显式配，不能靠默认值）。 */
.foot a { color: inherit; text-underline-offset: 2px; }

/* 备案号（2026-10-31 备案通过当天新增）：合规信息，**不跟着页脚次要色变淡**。
   --text-tertiary 压在本页底色上暗色只有 ≈3.8:1，11px 小字过不了 4.5:1；
   --text-secondary（浅 #5f5e5a / 暗 #a3a2a8）两档都在 6:1 以上。
   首页单独一行（工信部要求「底部显著位置」）；游戏页纵向空间紧，留在同一行。 */
.foot .foot-legal { display: block; margin-top: 4px; color: var(--text-secondary); }
.play-foot .foot-legal { display: inline; margin-top: 0; }

/* ── 内容带 ────────────────────────────────────────────────
   宽度对齐白卡（--card-w 由 render.js 写入），否则会出现
   「键盘区左右边缘和下方内容对不齐」，一眼就能看出没对齐
   （错误33 同类：同类的东西要成组处理）。*/
/* v1.18.4 视觉减重 —— boss 拍板「两块内容都留，只做视觉减重」。
   0 处文案改动，压的全是「看不见的体型」：图标小一档、卡片内外间距各收一档、
   节间距收一档、表格行高收一档、外壳内边距收一档、内卡圆角 22→16px。
   ⚠️ 字号一个没动 —— 正文 13.5px 是 DESIGN-TOKENS 第 9.1 节的地板
      （12px 紧凑档用在整段正文上正是错误 92）。把字变小 = 重犯错误 92。
   ⚠️ 所有新值仍在间距刻度集合内（4/6/8/12/16/20）。
   ⚠️ 高度缩了就必须同步收紧 smoke-ui 的上限（错误 91：缩了内容不收紧上限
      = 把线放空），基线数字见 smoke-ui「内容带高度不增长」。 */

/* 内容带：原先裸在灰色页面底、跟上方白卡不连贯（一滚下去就「散了」）。
   现改为「暖色带容器 + 内部三张白卡」：.kbdoc 只作承载带（bg-band + 大圆角，
   无边框/硬阴影，避免与内部白卡形成框中框 + 双层 22px 圆角，问题 4），
   真正的卡片是 .kbdoc-sec。整页仍是「白卡 → 白卡 → 白卡」的节奏。*/
.kbdoc {
  width: var(--card-w, 100%);
  max-width: 100%;
  margin: 0 auto;
  padding: var(--sp-6) var(--sp-5);
  min-width: 0;
  background: var(--bg-band);
  /* 问题 4：去掉外框 + 硬阴影，内容带不再是「一张卡」，
     改作承载三张 .kbdoc-sec 白卡的暖色带容器，消除双层 22px 圆角嵌套。 */
  border-radius: var(--r-2xl);
}
.kbdoc-inner { display: block; }

/* 分区之间用间距分隔，不画线 ——
   画线会在纯文字流里切得太碎，间距 + 标题层级已经足够分节。*/
.kbdoc-sec {
  margin: 0 0 var(--sp-4);
  padding: var(--sp-5);
  background: var(--bg-elev);
  border: 1px solid var(--border);
  /* v1.18.4：圆角 22px(--r-2xl) → 16px(--r-xl)。外壳与内卡同为 22px 时
     两层圆角一样"胖"，卡片看着比里面的内容重。外壳 > 内卡 才是有层级的嵌套。 */
  border-radius: var(--r-xl);
}
.kbdoc-sec:last-child { margin-bottom: 0; }

.kbdoc h2 {
  margin: 0 0 var(--sp-2);
  font-size: var(--fs-3xl);
  /* 800 → 700（DESIGN-TOKENS 1b）：800 与 700 在系统字体下差异极小，
     而同层的顶栏品牌 / 卡片标题都是 700 —— 让内容带 H2 独自 800
     是制造一个读不出差的伪层级。层级由 30px vs 13.5px 的字号差承担。 */
  font-weight: var(--fw-bold);
  letter-spacing: -.01em;
  color: var(--text);
}
.kbdoc h3 {
  margin: var(--sp-5) 0 var(--sp-1);
  font-size: var(--fs-md);
  font-weight: var(--fw-semi);
  color: var(--text);
}

/* v1.18.7（boss：v1.18.6「hairline + H3 摞进一张卡」是偷懒，重新设计排版）——
   01 区改为双栏仪器面板：左栏三层边界改「三行清单」（一层一行，hairline 分隔），
   右栏 25ms 口径做反色仪器面板。深色块从「三卡里的锚点」升格为「仪器的出厂标定值」：
   左边定性（能信到哪），右边定量（拿什么数判）。 */
.kbdoc-body {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 300px;
  gap: var(--sp-4);
  align-items: stretch;
}
/* 左栏行：行间用 hairline 分隔、不画框 —— 行是「清单」，不是「卡」，
   画框就退回三张窄卡的老样子了。 */
.kbdoc-layer,
.kbdoc-key {
  display: grid;
  grid-template-columns: 5.6em 8em minmax(0, 1fr);
  column-gap: var(--sp-3);
  align-items: center;
  padding: var(--sp-2) 0;
}
.kbdoc-keys .kbdoc-key {
  grid-template-columns: 3.6em 8.5em minmax(0, 1fr);
}
.kbdoc-layer + .kbdoc-layer,
.kbdoc-key + .kbdoc-key { border-top: 1px solid var(--border); }
.kbdoc-layer:first-child { padding-top: 0; }
.kbdoc-keys .kbdoc-key:first-child { border-top: 1px solid var(--border); }
.kbdoc-layer .tier,
.kbdoc-key .tier { justify-self: start; }
/* ⚠️ 特指度必须写成 .kbdoc .kbdoc-layer h3 (0,2,1)：单写 .kbdoc-layer h3 是 (0,1,1)，
   与 `.kbdoc h3` 同分，胜负只由书写顺序决定。 */
.kbdoc .kbdoc-layer h3,
.kbdoc .kbdoc-key h3 {
  margin: 0;
  font-size: var(--fs-md);
  line-height: var(--lh-body);
}
.kbdoc .kbdoc-layer p,
.kbdoc .kbdoc-key p {
  margin: 0;
  max-width: none;
  font-size: var(--fs-base);
  line-height: var(--lh-body);
  color: var(--text-secondary);
}
/* 右栏仪器面板：复用反色卡的全部语言 —— .doc-card 提供外壳（内边距/圆角），
   .doc-card.is-invert 提供底色与正文文字色（错误 93 修的对比度规则原样生效，
   行内 code 的 color-mix 底也照旧）。 */
.kbdoc-left { min-width: 0; }
.kbdoc .kbdoc-legend {
  margin: 0 0 var(--sp-3);
  max-width: none;
  font-size: var(--fs-sm);
  color: var(--text-secondary);
}
/* 四键与三层同一套扫读行：结论灯 / 对象 / 一句后果。 */
.kbdoc-sub { margin: var(--sp-4) 0 var(--sp-2); }
.kbdoc-keys { display: block; }
.kbdoc .kbdoc-key h3 {
  font-family: ui-monospace, Consolas, monospace;
  font-size: var(--fs-sm);
  font-weight: var(--fw-bold);
  letter-spacing: .02em;
}
/* v1.18.8：H2 挪进左栏（boss：黑面板要和右栏差不多大）——
   面板从标题那一行起就并排、撑到清单底部，右上角不再留空。
   节总高不变：行高本来就由左栏（H2 + 三行清单）决定。 */
.kbdoc-panel {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
}
/* v1.18.2：`.kbdoc-lead` 原来的五条声明（font-size / color / line-height /
   max-width / margin）**一条都没生效过** —— 特指度 `.kbdoc p`(0,1,1) >
   `.kbdoc-lead`(0,1,0)，全部被下面那行盖掉。也就是说「导语比正文大一档(15px)」
   这个设计意图**从来没出现在屏幕上**，它一直渲染成 13.5px 的正文
   （浏览器 computed font-size 实测 13.5px）。留着这套无效声明只会让下一个人
   以为导语是 15px，所以删掉。
   ⚠️ 01 节那句导语已按 boss 要求删除；本类现在只剩 01 区右栏仪器面板里
   这一处（v1.18.7 双栏重排），宽度交给 `.kbdoc-body` 的栅格列，
   不再需要 flex 排布容器。
   这条与错误 92 同族：**声明写了、层叠盖住了、静态检查看不见**。 */
.kbdoc p { margin: 0 0 var(--sp-3); max-width: var(--doc-measure); font-size: var(--fs-base); line-height: var(--lh-loose); color: var(--text-secondary); }

/* v1.18.1：这一段的 .kbdoc-note（左框线 + 浅底色的「说明块」）已删除 ——
   界面上那两处说明文字按 boss 要求拿掉，样式随之作废，不留死代码。
   ⚠️ 留档的是它的真实作用：这类块**看着**像补充说明，实际是「能力边界要写在
   界面上」（错误 28）在页面上的**唯一落点**。删掉之后，按 PrtSc 弹系统截图、
   键位图一直灰着这件事，界面上只剩 03 / FAQ 里那句通用说明兜底了。 */
.kbdoc code {
  font-family: ui-monospace, Consolas, monospace; font-size: var(--fs-xs);
  background: var(--bg-tertiary); padding: 1px 4px; border-radius: var(--r-sm);
}


/* 三层边界的状态标签：用色块 + 文字，不靠颜色单独传达信息
   （色觉障碍用户看不到颜色差别）。
   ⚠️ 三档必须靠**明度**拉开，不能全用同一个 bg-tertiary ——
   实测那样三档在灰底上看不出差别，标签等于白贴。
   ⚠️ 文字色按各自底色算，不能一律白字：
      白字配琥珀底只有 2.28:1（实测算过），远低于 WCAG 的 4.5:1。
      浅底必须配深字。*/
.tier {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: var(--fs-xs); line-height: var(--lh-body);
  padding: 0 8px; border-radius: var(--r-sm);
  white-space: nowrap;
}
/* 状态灯：色块徽章改成「灯 + 文字」—— 仪器面板用灯表达状态，
   实底大色块是 SaaS 的做法，在这个页面里三档并列时太吵。
   灯用 currentColor，配色仍由下面的 tier-* 决定（对比度检查读的就是这两处）。 */
.tier::before {
  content: "";
  width: 6px; height: 6px; border-radius: 50%;
  background: currentColor;
  flex: none;
  box-shadow: 0 0 0 2px rgba(255, 255, 255, .5);
}
.tier-ok   { color: #fff;     background: var(--key-tested); }  /* 深绿底 → 白字 4.84:1 */
.tier-warn { color: #412402;  background: var(--key-warn); }   /* 琥珀底 → 深棕字 6.24:1 */
.tier-bad  { color: #fff;     background: var(--key-dead); }   /* 深红底 → 白字 5.44:1 */


/* v1.18.7：doc-grid 栅格家族删除 —— 01 区三卡改三行清单后
   全仓 HTML 已无落点（grep 验证过），死规则不留。删内容必须连样式
   一起退，否则下一个人以为还有栅格在用（「删功能没删检查」的 CSS 版）。 */

.doc-card {
  padding: var(--sp-4);
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: var(--r-xl);
}
.doc-icon {
  /* v1.18.4：26 → 20px。图标是卡片里唯一的实心图形，
     它的大小决定卡片的"视觉体重"。等比缩小后 1.7 的描边相对变粗，
     一起降到 1.6，线条粗细在屏幕上才和原来一致。 */
  width: 20px; height: 20px; flex: none;
  fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round;
}
.doc-card.is-invert {
  background: var(--bg-invert);
  border-color: var(--bg-invert);
  color: var(--text-invert);
}
/* v1.18.2：反色卡正文原本**没有任何文字色规则** —— 它落回下面 `.doc-card p` 的
   --text-secondary(#5f5e5a)，而这张卡的底是 --bg-invert(#1c1917)：
   深灰字压近黑底，实测对比度 **2.69:1**，WCAG 正文门槛是 4.5:1 —— 基本读不清。
   放大截图里标题雪白、正文糊在背景里；boss 说「这些小字影响设计、没什么用」，
   物理原因就是这个。修法不是「换个好看的颜色」，是**给它一个达标的色**（实测 8.5:1）。
   ⚠️ check-contrast 一直没覆盖到它：那个脚本只比 :root 里的变量两两组合，
   而这里的毛病不是「某个变量不合格」，是「这张卡压根没给正文配文字色」。
   真正的守卫改成实测 —— 见 smoke-ui 的「每张卡的正文对比度」。 */
.doc-card.is-invert p { color: var(--text-invert-dim); }
/* 行内 code 的底是 --bg-tertiary（浅色）。正文提亮后「浅底 + 浅字」会翻车
   （实测 1.79:1），所以反色卡里的 code 改用「当前文字色的 14% 叠加」——
   这张卡在浅/深两个主题下明暗是相反的，只有跟着 currentColor 走才两边都成立
   （浅色主题：亮字→亮片；深色主题：暗字→暗片）。实测 6.8:1 / 5.5:1。 */
.doc-card.is-invert code {
  background: color-mix(in srgb, currentColor 14%, transparent);
  color: inherit;
}
/* v1.18.2：字号从 --fs-sm(12px) 提到 --fs-base(13.5px)。
   ⚠️ 这不是「调好看一点」，是**修一个规范违例**：DESIGN-TOKENS 第 1 节明写
   「中文正文不低于 13px」，而 --fs-sm 那一档的用途栏标的是「紧凑 UI、表头」——
   卡片正文是**正文**，却用了紧凑档。12px 的中文段落读不动，读不动就会被
   感觉成「没用」（boss 的原话是「这些小字影响整个页面的设计」）。
   ⚠️ check-ui 抓不到这个：它只断言「字号必须出自字阶」，而 12px 确实在字阶里 ——
   「用错了档」需要另一条断言，见 check-ui 的「内容带正文段落字号」。 */
.doc-card p {
  margin: 0;
  font-size: var(--fs-base);
  line-height: var(--lh-body);
  color: var(--text-secondary);
}
/* v1.18.2：`.doc-cause` / `.doc-fix` 两条规则删除 —— grep 全仓 HTML，这两个类
   **0 处使用**（它们服务于早已删掉的「原因/修复」卡片形态）。
   同一根因修一个实例时记得成组看（模式十七）：这里连样子都没了，规则也该走。 */

/* ── 右栏仪器面板读数 ─────────────────────────────────────
   25ms 是出厂标定值，必须比旁边说明大一档（--fs-4xl）。
   ⚠️ 选择器必须挂在 .kbdoc-panel 上：v1.18.7 把读数搬进右栏后，
      样式还写在已无 HTML 落点的 .kbdoc-readout 下 —— 40px 大数从未上过屏，
      黑块里四行小字挤在一起（错误 92 同族：声明写了、选择器对不上）。
   特指度要压过 `.doc-card.is-invert p` 与 `.kbdoc p`。 */
.kbdoc .kbdoc-panel p.rd-cap {
  margin: 0;
  max-width: none;
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  font-weight: var(--fw-med);
  letter-spacing: .14em;
  line-height: var(--lh-flat);
  color: var(--text-invert-dim);
}
.kbdoc .kbdoc-panel p.rd-val {
  margin: 0;
  max-width: none;
  font-family: var(--font-mono);
  font-size: var(--fs-4xl);
  font-weight: var(--fw-med);
  line-height: var(--lh-tight);
  font-variant-numeric: tabular-nums;
  color: var(--text-invert);
}
.kbdoc .kbdoc-panel p.rd-val span {
  font-size: var(--fs-md);
  font-weight: var(--fw-normal);
  color: var(--text-invert-dim);
  margin-left: var(--sp-1);
}
.rd-specs {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--sp-3);
  margin: 0;
  padding: var(--sp-3) 0 0;
  border-top: 1px solid color-mix(in srgb, var(--text-invert) 16%, transparent);
  list-style: none;
}
.rd-specs li {
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
  min-width: 0;
}
.rd-specs span {
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  letter-spacing: .1em;
  color: var(--text-invert-dim);
}
.rd-specs b {
  font-size: var(--fs-md);
  font-weight: var(--fw-med);
  line-height: var(--lh-tight);
  color: var(--text-invert);
}
.kbdoc .kbdoc-panel p.rd-note {
  margin: auto 0 0;
  max-width: none;
  padding-top: var(--sp-3);
  border-top: 1px solid color-mix(in srgb, var(--text-invert) 16%, transparent);
  font-size: var(--fs-base);
  line-height: var(--lh-body);
  color: var(--text-invert-dim);
}
/* ── FAQ 折叠（02 节，v1.18.6 起原 03 前移）──────────────────
   v1.17.2：七条全展开是 857px，比键盘区还高 —— 工具站被自己的说明书淹没。
   标题常驻（可扫读、可被搜到），答案按需展开。
   折叠用自绘 ▸ 箭头 + 旋转（原生 <details> 的三角太弱，撑不起面板语言）。 */
.kbdoc-faq { border-top: 1px solid var(--border); }
.kbdoc-faq:last-of-type { border-bottom: 1px solid var(--border); }
.kbdoc-faq > summary {
  display: flex; align-items: baseline; gap: var(--sp-2);
  cursor: pointer; list-style: none;
  padding: var(--sp-2) 0;
}
.kbdoc-faq > summary::-webkit-details-marker { display: none; }
.kbdoc-faq > summary::before {
  content: '▸';
  flex: none; color: var(--text-tertiary); font-size: var(--fs-2xs);
  transition: transform .15s;
}
.kbdoc-faq[open] > summary::before { transform: rotate(90deg); }
.kbdoc-faq > summary h3 { margin: 0; font-size: var(--fs-md); font-weight: var(--fw-semi); }
.kbdoc-faq > summary:hover h3 { color: var(--accent); }
/* 与 .key-flag / .btn 同一套 focus 写法（DESIGN-TOKENS 第 6 节） */
.kbdoc-faq > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.kbdoc-faq > summary:active { transform: translateY(1px); }
/* FAQ 答案：短文一段，不再用彩条标签扫读行 */
.kbdoc-faq > p {
  margin: 0 0 var(--sp-3);
  padding: 0 0 0 var(--sp-4);
  max-width: var(--doc-measure);
  font-size: var(--fs-base);
  line-height: var(--lh-loose);
  color: var(--text-secondary);
}

/* 窄屏：内容带降为单列（v1.18.5：表格横滚规则随三档出处表一并删除）*/
@media (max-width: 720px) {
  .kbdoc { padding: var(--sp-4) var(--sp-4) var(--sp-3); }
  .kbdoc-body { grid-template-columns: 1fr; }
  .kbdoc-sec { padding: var(--sp-4); }
  .kbdoc-layer,
  .kbdoc-key { grid-template-columns: auto 1fr; }
  .kbdoc-layer p,
  .kbdoc-key p { grid-column: 1 / -1; }
}