/* ============================================================================
 * components.css —— 组件层
 * 严格按 docs/设计规范.md §3.2 逐组件实现。本期（第一棒）只实现被两个入口页
 * 用到的 4 个组件：C1 大按钮 / C2 次级按钮 / C15 底部导航 / C16 提示与空状态 /
 * C18 头像选择卡。其余组件（C3 选项按钮、C5 填空、C6 数字键盘、C19 计算题输入框…）
 * 属于后面的棒次，本文件不预先实现。
 *
 * 状态命名统一用 `is-` 前缀（规范 §7.3），便于全局搜样式。
 * ⛔ 不写死色值与字号，只引用 tokens.css 的变量（规范 §7.1）。
 *
 * 关于裸 px 字号：本文件里少数 `font-size: 22px/28px/32px/48px` 只用于**图标与装饰字符**
 * （箭头 › 、判断题的对错符号、头像 emoji、抽屉的 ×），不承载正文，故不占 §2.3 的字号阶梯；
 * 正文与按钮文字一律用 `--text-*` 令牌。设计规范 §7.1 的"不写死字号"针对的是文本类字号。
 * ========================================================================== */

/* ---------------------------------------------------------------- C16 提示 / 空状态块
   布局工具，被 C18 骨架屏与 no-JS 提示复用。 */
.stack {
  display: flex;
  flex-direction: column;
}

/* ================================================================ C1 大按钮 .btn-primary
   尺寸：宽撑满，高 60px（小学）/ 52px（初中），圆角 12px
   状态：默认 / 按下 / 禁用 / 加载 / 焦点（焦点走 base.css 的 :focus-visible）
   ⛔ 按下只变背景色，不做缩放动画（规范 §4.1） */
.btn-primary {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-xs);
  width: 100%;
  min-height: var(--touch-primary);
  padding: var(--space-sm) var(--space-md);
  border-radius: var(--radius-md);
  background: var(--color-primary);
  color: var(--color-on-primary);
  font-size: var(--text-btn);
  font-weight: var(--weight-medium);
  line-height: var(--leading-tight);
  text-align: center;
  transition: background-color 140ms ease-out;
}

.btn-primary:active {
  background: var(--color-primary-press);
}

.btn-primary:disabled,
.btn-primary.is-disabled {
  opacity: 0.45;
  pointer-events: none;
  cursor: not-allowed;
}

/* 加载态：文字换成"处理中…"，**按钮宽度不变**，防抖动（规范 §3.2 C2 同款约定） */
.btn-primary.is-loading {
  pointer-events: none;
}

/* 禁用原因说明：C1 要求"配一行 --text-caption 说明为什么禁用"。
   ⛔ 不用浅灰文字（会看不清），用 --color-text-hint（白底 5.43:1，达 AA）。 */
.btn-note {
  margin: 0;
  color: var(--color-text-hint);
  font-size: var(--text-caption);
  line-height: var(--leading-tight);
  text-align: center;
}

/* ================================================================ C2 次级按钮 .btn-secondary
   高 44px（不小于 44），透明底 + 1.5px 描边 */
.btn-secondary {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2xs);
  min-height: var(--touch-min);
  padding: var(--space-xs) var(--space-md);
  border: 1.5px solid var(--color-border);
  border-radius: var(--radius-md);
  background: transparent;
  color: var(--color-primary);
  font-size: var(--text-btn);
  font-weight: var(--weight-medium);
  line-height: var(--leading-tight);
  transition: background-color 140ms ease-out;
}

.btn-secondary:active {
  background: var(--color-surface-sunken);
}

.btn-secondary:disabled,
.btn-secondary.is-disabled {
  opacity: 0.45;
  pointer-events: none;
  cursor: not-allowed;
}

.btn-secondary.is-loading {
  pointer-events: none;
}

/* 纯文字按钮（口令页右下角的"家长入口"、空状态里的次要动作都用它）
   规范 §3.2 C2 的极简变体：无描边、无底色，只用文字。 */
.btn-text {
  min-height: var(--touch-min);
  padding: var(--space-xs);
  background: none;
  border: 0;
  color: var(--color-text-hint);
  font-size: var(--text-caption);
  line-height: var(--leading-tight);
}

.btn-text:active {
  color: var(--color-text-sub);
}

/* ================================================================ C18 头像选择卡 .avatar-card
   96×96px 圆角 24px，emoji 字号 48px，下方名字 --text-btn
   状态：默认 / 按下 / 选中（选中后约 200ms 跳转）
   ⛔ 卡片上绝不出现真实姓名、照片、学校、班级、位置（规范 §3.2 C18 / 红线 §8） */
.avatar-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-2xs);
  width: 96px;
  min-height: 96px;
  padding: var(--space-xs) var(--space-2xs);
  border: 1.5px solid var(--color-border);
  /* 规范 §3.2 C18 明确指定 24px（令牌里最接近的是 --radius-lg 16px，
     这里按组件规范取 24px，已在代码注释说明偏离）。 */
  border-radius: 24px;
  background: var(--color-surface);
  box-shadow: var(--shadow-card);
  transition: background-color 140ms ease-out, border-color 140ms ease-out;
}

.avatar-card:active {
  background: var(--color-surface-sunken);
}

.avatar-card.is-selected {
  border: 2px solid var(--color-primary);
  background: var(--color-primary-soft);
}

.avatar-card__emoji {
  font-size: 48px; /* 规范 §3.2 C18 明确指定 emoji 字号 48px（无对应字号令牌） */
  line-height: 1;
}

.avatar-card__name {
  color: var(--color-text-title);
  font-size: var(--text-btn);
  font-weight: var(--weight-medium);
  line-height: var(--leading-tight);
}

.avatar-grid {
  display: grid;
  grid-template-columns: repeat(2, 96px);
  justify-content: center;
  /* §2.2 卡片间距：小学 16px / 初中 12px —— 同一套组件，只靠 data-grade 切档 */
  gap: var(--space-md);
}

:root[data-grade="middle"] .avatar-grid {
  gap: var(--space-sm);
}

/* 已点中一个卡片、正在跳转：锁住整组，防止连点第二个 */
.avatar-grid.is-locked .avatar-card {
  pointer-events: none;
}

/* ================================================================ C15 底部导航
   固定底边，高 72px + 安全区，白底 + 上边 1px 分隔线 + --shadow-float
   孩子端只有三项：主页 / 错题本 / 我的（规范 §0.3：没有"家长"项）
   状态：默认 / 当前项（is-current）/ 隐藏（is-hidden，考试页与打印页用） */
.bottom-nav {
  position: fixed;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: var(--z-sticky);   /* A55：原字面量 10 ⇒ 令牌（吸底导航） */
  display: flex;
  align-items: stretch;
  justify-content: center;
  gap: var(--space-xs);
  min-height: var(--bottombar-h);
  padding-bottom: var(--safe-bottom);
  border-top: 1px solid var(--color-border);
  background: var(--color-surface);
  box-shadow: var(--shadow-float);
}

.bottom-nav.is-hidden {
  display: none;
}

.bottom-nav__item {
  display: flex;
  flex: 1 1 0;
  /* 平板（≥768px）上不把三项拉成超宽按钮：每项最多 --max-content 的 1/3 */
  max-width: calc(var(--max-content) / 3);
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-2xs);
  min-width: var(--touch-min);
  min-height: var(--touch-comfort);
  color: var(--color-text-sub);
  font-size: var(--text-caption);
  line-height: var(--leading-tight);
}

.bottom-nav__item.is-current {
  color: var(--color-primary);
  font-weight: var(--weight-medium);
}

.bottom-nav__item:active {
  background: var(--color-surface-sunken);
}

.bottom-nav__icon {
  width: 24px;
  height: 24px;
  /* 内联 SVG，无图标库、无外部请求（规范 §6.2：外部请求 0 个） */
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* ================================================================ C16 提示 / 空状态块
   居中：图标在上、一句友好文案在中、一个动作按钮在下。
   状态：空 / 加载（骨架屏，⛔ 不用 spinner）/ 错误（必须给"重试"按钮）。
   空状态必须是正向的，不写"暂无数据"（规范 §3.2 C16）。 */
.empty-state {
  display: flex;
  flex: 1 1 auto;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-md);
  padding: var(--space-2xl) var(--space-md);
  text-align: center;
}

.empty-state__icon {
  /* 用大字令牌而不是新造一个 56px：规范 §2.3 的 --text-score 就是"大数字强调" */
  font-size: var(--text-score);
  line-height: 1;
}

.empty-state__title {
  color: var(--color-text-title);
  font-size: var(--text-title);
  font-weight: var(--weight-medium);
  line-height: var(--leading-tight);
}

.empty-state__text {
  max-width: 30ch;
  color: var(--color-text-sub);
  font-size: var(--text-body);
  line-height: var(--leading-body);
}

.empty-state__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--space-xs);
  margin-top: var(--space-xs);
}

/* 加载态骨架屏：静态灰块（⛔ 禁止无限循环动效，规范 §4.2） */
.skeleton {
  border-radius: var(--radius-lg);
  background: var(--color-surface-sunken);
}

.skeleton-card {
  height: 96px;
}

/* 反馈条：错误与提示共用。文字色随语义，⛔ 不用"错"字训人。 */
.notice {
  padding: var(--space-xs) var(--space-sm);
  border-radius: var(--radius-sm);
  font-size: var(--text-caption);
  line-height: var(--leading-tight);
}

.notice.is-error {
  background: var(--color-wrong-soft);
  color: var(--color-wrong);
}

.notice.is-hint {
  background: var(--color-primary-soft);
  color: var(--color-primary-ink);
}

/* ================================================================ C8 章节卡片
   高 ≥72px，圆角 16px，白底；左 40×40 主色序号块 + 中 章节名/说明 + 右 › */
.chapter-card {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  width: 100%;
  min-height: 72px;
  padding: var(--space-sm) var(--space-md);
  border: 0;
  border-radius: var(--radius-lg);
  background: var(--color-surface);
  box-shadow: var(--shadow-card);
  text-align: left;
  transition: background-color 140ms ease-out;
}

.chapter-card:active {
  background: var(--color-surface-sunken);
}

.chapter-card__no {
  display: grid;
  flex: 0 0 auto;
  place-items: center;
  width: 40px;
  height: 40px;
  border-radius: var(--radius-md);
  background: var(--color-primary);
  color: var(--color-on-primary);
  font-size: var(--text-btn);
  font-weight: var(--weight-medium);
}

.chapter-card__main {
  display: flex;
  flex: 1 1 auto;
  flex-direction: column;
  gap: var(--space-2xs);
  min-width: 0;
}

.chapter-card__title {
  color: var(--color-text-title);
  font-size: var(--text-body);
  font-weight: var(--weight-bold);
  line-height: var(--leading-tight);
}

.chapter-card__meta {
  color: var(--color-text-hint);
  font-size: var(--text-caption);
  line-height: var(--leading-tight);
}

.chapter-card__arrow {
  flex: 0 0 auto;
  color: var(--color-text-hint);
  font-size: 22px;
  line-height: 1;
}

/* 禁用（无题目的章节）：不透明度 0.45，保留可读性（不用浅灰文字） */
.chapter-card.is-disabled,
.chapter-card:disabled {
  opacity: 0.45;
  pointer-events: none;
  cursor: not-allowed;
  box-shadow: none;
}

/* ================================================================ C9 进度条
   高 8px，圆角 full，轨道 --color-surface-sunken；**必须带文字**，不能只靠条 */
.progress-bar {
  height: 8px;
  border-radius: var(--radius-full);
  background: var(--color-surface-sunken);
  overflow: hidden;
}

.progress-bar--thin {
  height: 6px;   /* 练习页顶栏的细进度条（页面结构 §4 固定框架第 1 条） */
}

.progress-bar__fill {
  width: 0;
  height: 100%;
  border-radius: var(--radius-full);
  background: var(--color-primary);
  transition: width 180ms ease-out;
}

/* ================================================================ C15 底部操作条（主操作）
   固定底边，高 72px + safe-bottom，白底 + 上边 1px 分隔线 + 阴影
   ⚠️ bottom 用 --kb-inset（assets/viewport.js 写的系统键盘高度）：
      iOS Safari 弹键盘时不改布局视口，底栏会被键盘盖住 ⇒ 孩子看不见也点不到「提交」。
      抬到键盘之上，是这道题的提交键唯一能点到的方式。无键盘时该变量为 0px，与改动前完全一致。 */
.bottom-bar {
  position: fixed;
  right: 0;
  bottom: var(--kb-inset, 0px);
  left: 0;
  z-index: var(--z-sticky);   /* A55：原字面量 10 ⇒ 令牌（底部操作条） */
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2xs);
  padding: var(--space-xs) var(--space-md) calc(var(--space-sm) + var(--safe-bottom));
  border-top: 1px solid var(--color-border);
  background: var(--color-surface);
  box-shadow: var(--shadow-float);
  transition: bottom 120ms ease-out;
}

/* 键盘打开时：底栏已经贴着键盘，再留着 safe-area 的额外留白就显得很空（也多吃 34px 屏） */
body.kb-open .bottom-bar {
  padding-bottom: var(--space-sm);
  transition: none;   /* 键盘弹出过程中别跟着做动画，会抖 */
}

.bottom-bar > .btn-primary,
.bottom-bar > .btn-secondary,
.bottom-bar > .bottom-bar__row {
  width: 100%;
  max-width: var(--max-content);
}

.bottom-bar__row {
  display: flex;
  gap: var(--space-xs);
}

/* ================================================================ C17 弹层（底部抽屉）
   从底部上滑，圆角 16px（顶部两角），遮罩 rgba(17,24,39,.4)，最大高度 80vh */
.drawer-layer {
  position: fixed;
  inset: 0;
  z-index: var(--z-modal);   /* A55：原字面量 50 ⇒ 令牌（弹层本体，必须盖住遮罩） */
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
}

.drawer-layer__scrim {
  position: absolute;
  inset: 0;
  /* A55：`--color-scrim` 已进 tokens.css（§2.5b），这里改为取令牌。
     原来这里写的是"令牌里没有对应项：按规范原值写" —— 那句话现在不成立了。 */
  background: var(--color-scrim);
}

.drawer {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
  max-height: 80vh;
  padding: var(--space-md) var(--space-md) calc(var(--space-md) + var(--safe-bottom));
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  background: var(--color-surface);
  box-shadow: var(--shadow-modal);
  overflow-y: auto;
  animation: drawer-in 240ms ease-out;
}

@keyframes drawer-in {
  from { transform: translateY(16px); opacity: 0; }
  to { transform: translateY(0); opacity: 1; }
}

.drawer__head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-sm);
}

.drawer__title {
  color: var(--color-text-title);
  font-size: var(--text-title);
  font-weight: var(--weight-medium);
  line-height: var(--leading-tight);
}

.drawer__close {
  display: grid;
  place-items: center;
  min-width: 44px;
  min-height: 44px;
  border-radius: var(--radius-full);
  background: var(--color-surface-sunken);
  color: var(--color-text-sub);
  font-size: 22px;
  line-height: 1;
}

.drawer__body {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}

.drawer__label {
  color: var(--color-text-title);
  font-size: var(--text-body);
  font-weight: var(--weight-medium);
}

.drawer__hint,
.drawer__text {
  color: var(--color-text-hint);
  font-size: var(--text-caption);
  line-height: var(--leading-body);
}

.drawer__actions {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}

/* 题量选择：三个大按钮（默认 10 题） */
.count-options {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-xs);
}

.count-option {
  min-height: var(--touch-primary);
  border: 1.5px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface);
  color: var(--color-text-title);
  font-size: var(--text-btn);
  font-weight: var(--weight-medium);
  transition: background-color 140ms ease-out, border-color 140ms ease-out;
}

.count-option:active {
  background: var(--color-surface-sunken);
}

.count-option.is-selected {
  border: 2px solid var(--color-primary);
  background: var(--color-primary-soft);
  color: var(--color-primary-ink);
}

/* "我的"抽屉里的一行 */
.me-line {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
}

.me-line__avatar {
  font-size: 32px;
  line-height: 1;
}

.me-line__name {
  color: var(--color-text-title);
  font-size: var(--text-body);
  font-weight: var(--weight-medium);
}

/* ================================================================ 参考写法块
   说理题的「参考写法」（练习页与两个清单页共用同一套类名）。
   ⛔ 只展示参考写法，不做任何「你的答案 vs 参考答案」的并排比对。 */
/* 参考写法：「和孩子一起把这题说清楚」用的，值得给足空间 */
.reference {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  padding: var(--space-sm);
  border-radius: var(--radius-md);
  background: var(--color-surface-sunken);
}

.reference__label {
  color: var(--color-primary-ink);
  font-size: var(--text-caption);
  font-weight: var(--weight-medium);
}

.reference__text {
  color: var(--color-text);
  font-size: var(--text-body);
  line-height: var(--leading-body);
}

.reference__empty {
  color: var(--color-text-hint);
  font-size: var(--text-caption);
  line-height: var(--leading-body);
}

/* ================================================================ C21 常驻身份条
 * 每个孩子端页面顶部：现在是谁 ｜ 换人（规范 §3.2 C21）。
 *
 * 🔴 为什么**在文档流里**（sticky）而不是悬浮（fixed）：
 *    悬浮条要跟 11 个页面各自的顶栏、底栏、系统键盘、安全区逐个对错位 —— 对不完，
 *    而且对错位是"看不见的塌陷"，孩子只会觉得页面坏了。这里宁可多占 36px。
 * 🔴 只读身份，**一个数字都不显示**（成绩/错题数/待办数都不许出现）。
 * 🔴 考试作答中不给「换人」（答案会记到别人头上），所以 `__switch` 由页面决定挂不挂。
 */
.identity-bar {
  position: sticky;
  top: 0;
  z-index: var(--z-sticky);   /* A55：原字面量 20 ⇒ 令牌（吸顶身份条） */
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm);
  min-height: var(--identity-bar-h);
  padding: 0 var(--space-md);
  background: var(--color-surface-sunken);
  border-bottom: 1px solid var(--color-border);
}

.identity-bar__who {
  display: flex;
  align-items: center;
  gap: var(--space-2xs);
  margin: 0;
  min-width: 0;
  font-size: var(--text-caption);
  color: var(--color-text-sub);
}

.identity-bar__emoji { font-size: var(--text-body); line-height: 1; }
.identity-bar__name { font-weight: var(--weight-medium); color: var(--color-text); }
.identity-bar__grade { color: var(--color-text-hint); }

/* 进门落地态：前 5 秒说"以【X】的身份进入"（这就是选错身份的撤销窗口） */
.identity-bar.is-arriving { background: var(--color-primary-soft); }
.identity-bar__arrive { color: var(--color-primary-ink); font-weight: var(--weight-medium); }

.identity-bar__swap {
  flex: none;
  min-height: var(--touch-min);
  padding: 0 var(--space-xs);
  border: 0;
  background: none;
  color: var(--color-primary);
  font: inherit;
  font-size: var(--text-caption);
  font-weight: var(--weight-medium);
  cursor: pointer;
}
.identity-bar__swap:focus-visible { outline: 2px solid var(--color-focus); outline-offset: 2px; }

/* ============================================================================
 * 状态组件（K1.2 · 设计规范 §6.4）
 * ---------------------------------------------------------------------------
 * 一个壳 `.state-card`，状态用两个属性表达（与项目既有的 data-grade / data-subject 同风格，
 * 也为第三族「页面六态」留位）：
 *
 *   <span class="state-card" data-family="feedback" data-state="wrong">差一点点，我们看看这里</span>
 *
 *   data-family = feedback（作答反馈六态 · §6.3）/ mastery（掌握级六态 · §6.4）
 *   data-state  = 该族的态 key（反馈：correct/wrong/pending/timeout/skip/blank
 *                             掌握：new/learning/review/mastered/relearn/skip）
 *
 * ⛔ 本块**只准**出现 var(--…)：不许写死颜色 / 纹样 / 几何字符 / 文案 —— 写死了换皮就失效、
 *    色弱冗余也失效（K1.1 真浏览器抓到过一次"假填充"）。⛔ 本块**不出现任何文案**：
 *    文字标签由 DOM 提供，唯一来源是 `--label-*` / `--mstate-label-*`
 *    （CSS 伪元素里的字读屏读不到、也选不中，关键信息不能只活在 ::before 里）。
 *
 * 结构约定（两条，套件 `web/tests/state_components.mjs` 静态强制）：
 *   ① 视觉属性只写在下面这条 `.state-card` 基础规则里；
 *   ② 每个状态选择器**只许**赋 `--state-*` 六个通道（color/fill/edge/bar/glyph/label）。
 * ========================================================================== */
.state-card {
  /* 兜底：状态没写 / 写错时退化成中性块（写错的值由套件的跨层守卫拦，不会静默上线） */
  --state-color: var(--color-text);
  --state-fill: var(--color-surface);
  --state-edge: var(--edge-blank);
  --state-bar: 0;
  --state-glyph: none;
  --state-label: var(--label-blank);

  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  min-height: 28px;
  padding: var(--space-xs) var(--space-sm);
  border: var(--state-edge);
  border-radius: var(--radius-md);
  background: var(--state-fill);
  box-shadow: inset var(--state-bar) 0 0 var(--state-color);  /* 左侧竖条：0 宽时看不见 */
  color: var(--state-color);
  font-size: var(--text-caption);
  font-weight: 600;
  line-height: var(--leading-tight);
}

/* 几何字符：唯一来源 `--glyph-*` / `--mstate-glyph-*`（⛔ 不许在这里写字符） */
.state-card::before {
  content: var(--state-glyph);
  font-weight: 800;
}

/* --------------------------------------------------- 作答反馈六态（family=feedback）
 * 四层冗余（颜色 + 填充 + 几何字符 + 文字）在令牌层定义，见 tokens.css 末尾（K1.1 / §6.3）。
 * 「答对」不专门写一段：它的 --fill-correct 就是浅绿实色底，其余同族同构。 */
.state-card[data-family="feedback"][data-state="correct"] {
  --state-color: var(--color-correct);
  --state-fill: var(--fill-correct);
  --state-edge: var(--edge-correct);
  --state-bar: var(--bar-correct);
  --state-glyph: var(--glyph-correct);
  --state-label: var(--label-correct);
}

.state-card[data-family="feedback"][data-state="wrong"] {
  --state-color: var(--color-wrong);
  --state-fill: var(--fill-wrong);
  --state-edge: var(--edge-wrong);
  --state-bar: var(--bar-wrong);          /* 唯一有左侧竖条的一态 */
  --state-glyph: var(--glyph-wrong);
  --state-label: var(--label-wrong);
}

.state-card[data-family="feedback"][data-state="pending"] {
  --state-color: var(--color-pending);
  --state-fill: var(--fill-pending);      /* 点阵 */
  --state-edge: var(--edge-pending);
  --state-bar: var(--bar-pending);
  --state-glyph: var(--glyph-pending);
  --state-label: var(--label-pending);
}

.state-card[data-family="feedback"][data-state="timeout"] {
  --state-color: var(--color-timeout);
  --state-fill: var(--fill-timeout);      /* 45° 斜纹 */
  --state-edge: var(--edge-timeout);
  --state-bar: var(--bar-timeout);
  --state-glyph: var(--glyph-timeout);
  --state-label: var(--label-timeout);
}

.state-card[data-family="feedback"][data-state="skip"] {
  --state-color: var(--color-skip);
  --state-fill: var(--fill-skip);         /* 空心 */
  --state-edge: var(--edge-skip);         /* 2px 虚线 */
  --state-bar: var(--bar-skip);
  --state-glyph: var(--glyph-skip);
  --state-label: var(--label-skip);
}

.state-card[data-family="feedback"][data-state="blank"] {
  --state-color: var(--color-text-hint);
  --state-fill: var(--fill-blank);        /* 内凹底（不是页面底色，见 K1.1 的坑） */
  --state-edge: var(--edge-blank);        /* 1px 细虚线 */
  --state-bar: var(--bar-blank);
  --state-glyph: var(--glyph-blank);
  --state-label: var(--label-blank);
}

/* --------------------------------------------------- 掌握级六态（family=mastery）
 * 「这个知识点学得怎样」：新 / 在学 / 待复习 / 已掌握 / 需重学 / 已跳过。
 * 令牌在 tokens.css 末尾「掌握级六态」块（K1.2）；产品规格 §10.1，落地表 §6.4。
 * ⛔ 只做**展示**：这里不含任何排期 / 提醒逻辑（撞冻结清单 D1，调度维持手动 1/3/7/21）。 */
.state-card[data-family="mastery"][data-state="new"] {
  --state-color: var(--mstate-color-new);
  --state-fill: var(--mstate-fill-new);   /* 空心 = 还没开始 */
  --state-edge: var(--mstate-edge-new);
  --state-bar: var(--mstate-bar-new);
  --state-glyph: var(--mstate-glyph-new);
  --state-label: var(--mstate-label-new);
}

.state-card[data-family="mastery"][data-state="learning"] {
  --state-color: var(--mstate-color-learning);
  --state-fill: var(--mstate-fill-learning);
  --state-edge: var(--mstate-edge-learning);
  --state-bar: var(--mstate-bar-learning);
  --state-glyph: var(--mstate-glyph-learning);
  --state-label: var(--mstate-label-learning);
}

.state-card[data-family="mastery"][data-state="review"] {
  --state-color: var(--mstate-color-review);
  --state-fill: var(--mstate-fill-review);
  --state-edge: var(--mstate-edge-review);
  --state-bar: var(--mstate-bar-review);
  --state-glyph: var(--mstate-glyph-review);
  --state-label: var(--mstate-label-review);
}

.state-card[data-family="mastery"][data-state="mastered"] {
  --state-color: var(--mstate-color-mastered);
  --state-fill: var(--mstate-fill-mastered);
  --state-edge: var(--mstate-edge-mastered);
  --state-bar: var(--mstate-bar-mastered);
  --state-glyph: var(--mstate-glyph-mastered);
  --state-label: var(--mstate-label-mastered);
}

.state-card[data-family="mastery"][data-state="relearn"] {
  --state-color: var(--mstate-color-relearn);
  --state-fill: var(--mstate-fill-relearn);
  --state-edge: var(--mstate-edge-relearn);
  --state-bar: var(--mstate-bar-relearn);  /* 唯一有左侧竖条的一态 */
  --state-glyph: var(--mstate-glyph-relearn);
  --state-label: var(--mstate-label-relearn);
}

.state-card[data-family="mastery"][data-state="skip"] {
  --state-color: var(--mstate-color-skip);
  --state-fill: var(--mstate-fill-skip);
  --state-edge: var(--mstate-edge-skip);
  --state-bar: var(--mstate-bar-skip);
  --state-glyph: var(--mstate-glyph-skip);
  --state-label: var(--mstate-label-skip);
}
