/* ============================================================================
 * pages.css —— 页面布局层（本期三页：家庭口令页 / 选头像页 / 主页骨架）
 * 结构依据 docs/页面结构.md §1、§2、§3 与 §0.3；尺寸与色值只引用 tokens.css。
 * ⛔ 不含组件内部样式（那只在 components.css / answer.css）。
 * 关于裸 px 字号：本文件里的 22px 只用于箭头符号 ›，不承载正文。
 * ========================================================================== */

/* ==========================================================================
 * 首屏可见性兜底（配合 index.html 头部的极小内联脚本）
 * 复访者（本机已通过口令）在探测会话的几十毫秒里不闪一下口令页；
 * 一旦脚本没跑起来（JS 失败 / 被禁用），这个类不会被加，页面正常可见。
 * ========================================================================== */
:root.is-probing .js-boot-hide {
  visibility: hidden;
}

/* ==========================================================================
 * 家庭口令页（页面结构.md §1）
 * 顶区占屏幕上方约 1/3；角落是低对比度的"家长入口"；底部一行说明。
 * ========================================================================== */
.gate-main {
  display: flex;
  flex: 1 1 auto;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  gap: var(--space-lg);
  padding-top: 12vh;   /* 顶区垂直居中偏上：占屏幕上方约 1/3 */
  text-align: center;
}

/* 应用图标：CSS 画的几何图形，不引图片、不加外部请求 */
.gate-icon {
  position: relative;
  width: 72px;
  height: 72px;
  border-radius: var(--radius-lg);
  background: var(--color-primary);
}

.gate-icon::before {
  content: "";
  position: absolute;
  top: 18px;
  left: 18px;
  width: 36px;
  height: 12px;
  border-radius: var(--radius-sm);
  background: var(--color-on-primary);
}

.gate-icon::after {
  content: "";
  position: absolute;
  bottom: 18px;
  left: 18px;
  width: 24px;
  height: 24px;
  border-radius: var(--radius-full);
  background: var(--color-on-primary);
}

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

.gate-form {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  width: 100%;
}

.gate-label {
  color: var(--color-text-sub);
  font-size: var(--text-body);
  line-height: var(--leading-tight);
}

/* 口令输入框：4–6 位数字，不显示明文（type="password" 的原生圆点占位），
   字号用 --text-input（孩子自己输入的字必须最大）。 */
.pin-input {
  width: 100%;
  min-height: 64px;
  padding: var(--space-sm) var(--space-md);
  border: 1.5px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface-sunken);
  color: var(--color-text);
  font-size: var(--text-input);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.5em;
  text-align: center;
  /* 用描边代替 outline，避免双框（规范 §3.2 C5 聚焦态） */
  outline: none;
  transition: background-color 140ms ease-out, border-color 140ms ease-out;
}

.pin-input:focus,
.pin-input:focus-visible {
  border: 2px solid var(--color-primary);
  background: var(--color-surface);
  outline: none;
}

/* 错误反馈区：条件显示、不占位；⛔ 不高亮红框、不抖动（页面结构.md §1 状态表） */
.gate-error {
  color: var(--color-wrong);
  font-size: var(--text-caption);
  line-height: var(--leading-tight);
  text-align: center;
}

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

.gate-footer {
  /* margin-top:auto 把它压到底部；再留出 --space-2xl 让开右下角的"家长入口"，
     两者不在同一行，窄屏（320px）也不会挤在一起 */
  margin-top: auto;
  padding-top: var(--space-lg);
  padding-bottom: var(--space-2xl);
  color: var(--color-text-hint);
  font-size: var(--text-caption);
  line-height: var(--leading-tight);
  text-align: center;
}

/* 角落区：距屏幕右下角 16px，低对比度纯文字按钮，不与其他任何元素对齐，
   ⛔ 绝不放在屏幕中央、绝不用主色、绝不加大（页面结构.md §1.4） */
.parent-entry {
  position: fixed;
  right: var(--space-md);
  bottom: calc(var(--space-md) + var(--safe-bottom));
  /* A55：5 是**有意**低于底栏（--z-sticky=10）—— 这个角标在左下角，不与底栏重叠；
     取最近的 --z-base=1 会把它压到卡片下面，改动行为。保留原值并写明理由。 */
  z-index: 5;
  color: var(--color-text-hint);
}

/* ==========================================================================
 * 选头像页（页面结构.md §2）
 * 两个 96×96 卡片并排、屏幕中部居中；底部无按钮，"点头像即进入"。
 * ========================================================================== */
.avatar-main {
  display: flex;
  flex: 1 1 auto;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-lg);
  text-align: center;
}

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

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

/* 拉取 /api/profiles 时的骨架屏（⛔ 不用 spinner） */
.avatar-skeleton {
  display: grid;
  grid-template-columns: repeat(2, 96px);
  justify-content: center;
  gap: var(--space-md);
}

/* ==========================================================================
 * 主页骨架（页面结构.md §3）
 * 本期只立住"顶栏 + 底部导航 + 空状态"的结构，章节列表由下一棒接上。
 * ========================================================================== */
.topbar {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  min-height: var(--header-h);
}

.topbar__avatar {
  display: grid;
  place-items: center;
  width: var(--touch-min);
  height: var(--touch-min);
  border: 1.5px solid var(--color-border);
  border-radius: var(--radius-full);
  background: var(--color-surface);
  font-size: var(--text-title); /* 随 data-grade 切档，不写死 */
  line-height: 1;
}

.topbar__ctx {
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
  color: var(--color-text-title);
  font-size: var(--text-body);
  font-weight: var(--weight-medium);
  line-height: var(--leading-tight);
}

.topbar__ctx-note {
  color: var(--color-text-hint);
  font-size: var(--text-caption);
  font-weight: var(--weight-regular);
}

/* 内容区：底部留白 = --bottombar-h + 16px，保证底部条不遮挡内容（规范 §2.6） */
.page__body {
  display: flex;
  flex: 1 1 auto;
  flex-direction: column;
  padding-bottom: calc(var(--bottombar-h) + var(--safe-bottom) + var(--space-md));
}

/* ==========================================================================
 * 小零件（多页共用）
 * ========================================================================== */

/* 顶栏左侧的图标按钮（返回）：顶部按钮额外加大到 48×48，防够不到时蹭偏 */
.icon-button {
  display: grid;
  flex: 0 0 auto;
  place-items: center;
  min-width: 48px;
  min-height: 48px;
  border-radius: var(--radius-full);
  background: var(--color-surface);
  color: var(--color-text-title);
}

.icon-button:active {
  background: var(--color-surface-sunken);
}

.icon-button__icon {
  width: 24px;
  height: 24px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.topbar__spacer {
  flex: 0 0 auto;
  width: 48px;
}

/* 小圆角标签（题号徽标 / 题型标签） */
.chip {
  display: inline-flex;
  align-items: center;
  padding: var(--space-2xs) var(--space-xs);
  border-radius: var(--radius-sm);
  background: var(--color-surface-sunken);
  color: var(--color-text-sub);
  font-size: var(--text-caption);
  line-height: var(--leading-tight);
}

.chip--type {
  background: var(--color-primary-soft);
  color: var(--color-primary-ink);
}

/* 难度分层（K7，计划书 slide 14）：只标题目本身的刻度。
   ⛔ 不用红/黄/绿那套 —— 那是"对 / 提示 / 错"的语义，用在难度上会变成评价孩子。
   三档共用一族样式，靠"描边实/虚 + 底"区分：挑战档稍微实一点，仍然很轻。 */
.chip--level {
  background: transparent;
  border: 1px dashed var(--color-border);
}
.chip--level[data-level="challenge"] {
  background: var(--color-surface-sunken);
  border-style: solid;
}

.section-title {
  margin: var(--space-lg) 0 var(--space-sm);
  color: var(--color-text-title);
  font-size: var(--text-title);
  font-weight: var(--weight-medium);
  line-height: var(--leading-tight);
}

/* ==========================================================================
 * 主页：选章节（页面结构.md §3）
 * ========================================================================== */
/* 科目说明与"题目准备中"：都在章节列表上方，语气是解释而不是警告
   （⛔ 不用 --color-wrong 那套红，那是"答错"的语义） */
.section-note {
  margin: calc(-1 * var(--space-xs)) 0 var(--space-xs);
  color: var(--color-text-hint);
  font-size: var(--text-caption);
  line-height: var(--leading-tight);
}

.prep-note {
  margin-bottom: var(--space-md);
  font-size: var(--text-body);
  line-height: var(--leading-body);
}

.chapter-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}

:root[data-grade="middle"] /* 科目说明与"题目准备中"：都在章节列表上方，语气是解释而不是警告
   （⛔ 不用 --color-wrong 那套红，那是"答错"的语义） */
.section-note {
  margin: calc(-1 * var(--space-xs)) 0 var(--space-xs);
  color: var(--color-text-hint);
  font-size: var(--text-caption);
  line-height: var(--leading-tight);
}

.prep-note {
  margin-bottom: var(--space-md);
  font-size: var(--text-body);
  line-height: var(--leading-body);
}

.chapter-list {
  gap: var(--space-sm);   /* §2.2 卡片间距：小学 16px / 初中 12px */
}

.skeleton-chapter {
  height: 72px;
}

/* 模拟考入口：次级样式横向条（⛔ 不用主色大按钮，不与"开始练习"抢注意力） */
.exam-entry {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  width: 100%;
  min-height: var(--touch-comfort);
  margin-top: var(--space-md);
  padding: var(--space-sm) var(--space-md);
  border: 1.5px solid var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--color-surface);
  text-align: left;
}

.exam-entry:active {
  background: var(--color-surface-sunken);
}

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

.exam-entry__note {
  flex: 1 1 auto;
  color: var(--color-text-hint);
  font-size: var(--text-caption);
}

.exam-entry__arrow {
  color: var(--color-text-hint);
  font-size: 22px;
  line-height: 1;
}

/* ==========================================================================
 * 练习作答页（页面结构.md §4）
 * 一页一题：顶栏（进度 + 退出）/ 题干区 / 作答区 / 反馈区 / 底部操作条
 * ========================================================================== */
.practice-body {
  display: flex;
  flex: 1 1 auto;
  flex-direction: column;
  /* 底部留白 = 底部操作条 + 安全区 + 16px，键盘/键盘区都不挡内容（规范 §2.6） */
  padding-bottom: calc(var(--bottombar-h) + var(--safe-bottom) + var(--space-md));
}

.practice-topbar {
  gap: var(--space-xs);
}

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

.practice-progress__text {
  color: var(--color-text-sub);
  font-size: var(--text-caption);
  line-height: var(--leading-tight);
}

/* 练习页顶部：恢复提示（"接着上次没做完的做"）+「重新开始一轮」的出口。
   ⛔ 只在恢复来的那一轮显示；新建的那一轮这一块是 hidden（HTML 里默认 hidden）。
   底色/字色沿用 .notice.is-hint（components.css），这里只补它自己的排布。 */
.resume-notice {
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
  margin-top: var(--space-md);
}

.resume-notice__text {
  color: var(--color-primary-ink);
  font-size: var(--text-caption);
  line-height: var(--leading-body);
}

.resume-notice__note {
  color: var(--color-primary-ink);
  font-size: var(--text-caption);
  line-height: var(--leading-tight);
}

/* 「重新开始一轮」：普通按钮（危险的那一侧不许是默认），但要有 48px 的触摸区 */
.resume-notice__restart {
  align-self: flex-start;
  min-height: var(--touch-min);
  color: var(--color-primary);
  font-size: var(--text-caption);
  text-align: left;
}

.stem {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  padding-top: var(--space-md);
}

.stem__meta {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
}

.stem__text {
  color: var(--color-text);
  font-size: var(--text-body);
  line-height: var(--leading-body);
  /* 算式轻微拉开，避免 1 和 l 混淆（规范 §2.3 排版硬要求 4） */
  letter-spacing: 0.02em;
  overflow-wrap: anywhere;
}

.answer-area {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  margin-top: var(--space-lg);
}

/* 计算题：题干区在被键盘占位时自动上滚，保证至少留 2 行题干可见 */
.compute {
  display: flex;
  flex-direction: column;
}

.blank-block {
  display: flex;
  flex-direction: column;
}

/* ==========================================================================
 * 本次小结页
 * ========================================================================== */
.summary-body {
  gap: var(--space-lg);
  padding-bottom: calc(var(--bottombar-h) + var(--safe-bottom) + var(--space-md));
}

.summary-head {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-xs);
  padding-top: var(--space-lg);
  text-align: center;
}

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

.summary-head__big {
  color: var(--color-primary);
  font-family: var(--font-mono);
  font-size: var(--text-score);
  font-variant-numeric: tabular-nums;
  font-weight: var(--weight-bold);
  line-height: 1.1;
}

.summary-head__unit {
  color: var(--color-text-sub);
  font-size: var(--text-body);
}

.summary-head__sub {
  color: var(--color-text-hint);
  font-size: var(--text-caption);
}

.summary-stats {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-xs);
}

.stat-cell {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2xs);
  padding: var(--space-sm);
  border-radius: var(--radius-lg);
  background: var(--color-surface);
  box-shadow: var(--shadow-card);
  text-align: center;
}

.stat-cell__value {
  color: var(--color-text-title);
  font-family: var(--font-mono);
  font-size: var(--text-title);
  font-variant-numeric: tabular-nums;
  font-weight: var(--weight-medium);
  line-height: var(--leading-tight);
}

/* ⛔ "错/再看看"这一格用中性灰，不用红（红色会放大挫败感） */
.stat-cell__label {
  color: var(--color-text-hint);
  font-size: var(--text-caption);
  line-height: var(--leading-tight);
}

/* 底部操作条里 <a> 与 <button> 混排时保持一致外观（打印入口是链接，便于长按/新标签打开） */
.bottom-bar__row > a.btn-secondary {
  flex: 1 1 0;
  text-decoration: none;
}

/* 同一条里的 <button> 也等分（K11③ 小结页："回主页" + "看看该复习的" 各占一半）。
   ⛔ 不加这条，两个按钮按文字宽度挤在中间、右边空一大块（.btn-secondary 是 inline-flex，没有宽度）。 */
.bottom-bar__row > button.btn-secondary {
  flex: 1 1 0;
}

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


/* ============================================================================
 * 页面六态组件（K1.3 · 设计规范 §6.5 · 产品规格 40-ux §6）
 * ---------------------------------------------------------------------------
 * 一个壳 `.state-view`，状态用两个属性表达（与 §6.4 的 `.state-card` 同一套纪律）：
 *
 *   <div class="state-view" data-family="view" data-state="error" data-layout="panel">
 *     <span class="state-view__glyph"></span>
 *     <p class="state-view__title">这次没加载出来</p>
 *     <p class="state-view__hint">是网络的问题，再来一次就好</p>
 *     <button class="state-view__action" type="button">再试一次</button>
 *   </div>
 *
 *   data-state  = skeleton / empty / error / blocked / offline / timeout（页面六态）
 *   data-layout = bars / panel / page / strip —— **结构**，由 state-view.js 按状态给出（§6.5 有对照表）
 *
 * 为什么 layout 单独一个属性：六态的**形态本来就不同**（骨架是灰条、离线是顶部细条、阻塞是整页），
 * 这不是"换个颜色"能表达的。所以：结构写在 4 条 `[data-layout=…]` 规则里；
 * 颜色 / 纹样 / 字符 / 文案一律仍是 `--state-*` 通道（6 条状态规则**只赋通道**，
 * ⛔ 不在状态规则里写视觉属性 —— 这样"换皮 / 加一态"只动令牌层）。
 *
 * ⛔ 本块只准出现 var(--…)：不许写死颜色 / 纹样 / 几何字符 / 文案（与 §6.4 同一条纪律，套件强制）。
 *    允许出现的是**结构尺寸**（px / % / z-index）——这些不是"可换皮的东西"，且能从令牌取的一律取令牌。
 * ⛔ 文字一个字都不在本块：文案唯一来源是 `--vstate-label-* / --vstate-hint-* / --vstate-action-*`，
 *    由 state-view.js 填进 DOM（::before 只承载几何字符，读屏读不到，关键信息不能只活在伪元素里）。
 * ========================================================================== */
.state-view {
  /* 兜底：状态没写 / 写错时退化成中性块（写错的值由套件与 JS 双双拦下，不会静默上线） */
  --state-color: var(--color-text);
  --state-fill: var(--color-surface);
  --state-edge: var(--edge-blank);
  --state-glyph: none;
  --state-label: "";
  --state-hint: "";
  --state-action: "";

  display: flex;
  color: var(--state-color);
  font-size: var(--text-body);
  line-height: var(--leading-body);
}

/* 几何字符：唯一来源 `--vstate-glyph-*`（⛔ 不许在这里写字符） */
.state-view__glyph {
  font-size: var(--text-title);
  font-weight: 800;
  line-height: 1;
}
.state-view__glyph::before { content: var(--state-glyph); }

.state-view__title {
  margin: 0;
  font-size: var(--text-body);
  font-weight: var(--weight-medium);
  color: var(--state-color);
}
.state-view__hint {
  margin: 0;
  font-size: var(--text-caption);
  color: var(--color-text-sub);
}

/* 出口按钮：每页最多一个阻塞态，且**唯一**出口（40-ux 规则 2）；高度 ≥ --touch-primary、落在拇指区 */
.state-view__action {
  min-height: var(--touch-primary);
  margin: var(--space-xs) 0 0;
  padding: 0 var(--space-lg);
  border: 0;
  border-radius: var(--radius-md);
  background: var(--state-color);
  color: var(--color-on-primary);
  font-family: inherit;
  font-size: var(--text-btn);
  font-weight: var(--weight-medium);
}

/* 骨架条（只有 data-layout="bars" 会渲染它们；宽度差是"与真实结构同形"的最低要求） */
.state-view__bars {
  display: flex;
  flex-direction: column;
  gap: var(--vstate-bar-gap);
  width: 100%;
}
.state-view__bar {
  display: block;
  height: var(--vstate-bar-h);
  border-radius: var(--radius-sm);
  background: var(--state-fill);
}
.state-view__bars > :nth-child(2) { width: 84%; }
.state-view__bars > :nth-child(3) { width: 62%; }

/* ------------------------------------------------- 布局变体（结构，不是状态）
 * bars  = 骨架：与真实结构同形的灰条，落在页面/区块原有的位置上，不遮内容
 * panel = 区块级：居中图标 + 标题 + 副文案 +（可选）唯一出口，只占它所在的那块
 * page  = 整页阻塞：铺满视口、压住一切，只给唯一出口（无权限 / 身份失效）
 * strip = 离线细条：48px 吸顶，不遮内容、不打断作答（40-ux 规则：断网仍可继续）
 * ---------------------------------------------------------------- */
.state-view[data-layout="bars"] {
  flex-direction: column;
  gap: var(--vstate-bar-gap);
  padding: var(--space-md);
  background: var(--state-fill);
  border: var(--state-edge);
  border-radius: var(--radius-lg);
}

.state-view[data-layout="panel"] {
  flex-direction: column;
  align-items: center;
  gap: var(--space-sm);
  padding: var(--space-2xl) var(--space-md);
  background: var(--state-fill);
  border: var(--state-edge);
  border-radius: var(--radius-lg);
  text-align: center;
}

.state-view[data-layout="page"] {
  position: fixed;
  inset: 0;
  z-index: var(--z-overlay);   /* A55：原字面量 40 ⇒ 令牌（整页状态层，配 --color-scrim 同级） */
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-sm);
  padding: var(--space-lg);
  background: var(--state-fill);
  border: var(--state-edge);
  text-align: center;
}

.state-view[data-layout="strip"] {
  position: sticky;
  top: 0;
  z-index: var(--z-drawer);   /* A55：原字面量 30 ⇒ 令牌（离线细条吸顶） */
  align-items: center;
  gap: var(--space-xs);
  height: var(--vstate-strip-h);
  padding: 0 var(--space-md);
  background: var(--state-fill);
  border-bottom: var(--state-edge);
}

/* ------------------------------------------------- 页面六态（family=view）
 * 视觉四层（颜色 + 填充 + 几何字符 + 标题文字）+ 文案三件套（标题 / 副文案 / 出口按钮）
 * 全部在令牌层定义，见 tokens.css 末尾「页面六态 · 令牌」（K1.3）。
 * ⛔ 本处**只赋通道**，不写任何视觉属性 —— 与 §6.4 的六通道约定同源（本族没有竖条层，见 §6.5）。 */
.state-view[data-family="view"][data-state="skeleton"] {
  --state-color: var(--vstate-color-skeleton);
  --state-fill: var(--vstate-fill-skeleton);
  --state-edge: var(--vstate-edge-skeleton);
  --state-glyph: var(--vstate-glyph-skeleton);
  --state-label: var(--vstate-label-skeleton);
  --state-hint: var(--vstate-hint-skeleton);
  --state-action: var(--vstate-action-skeleton);
}

.state-view[data-family="view"][data-state="empty"] {
  --state-color: var(--vstate-color-empty);
  --state-fill: var(--vstate-fill-empty);
  --state-edge: var(--vstate-edge-empty);
  --state-glyph: var(--vstate-glyph-empty);
  --state-label: var(--vstate-label-empty-zero);
  --state-hint: var(--vstate-hint-empty-zero);
  --state-action: var(--vstate-action-empty-zero);
}

.state-view[data-family="view"][data-state="error"] {
  --state-color: var(--vstate-color-error);
  --state-fill: var(--vstate-fill-error);
  --state-edge: var(--vstate-edge-error);
  --state-glyph: var(--vstate-glyph-error);
  --state-label: var(--vstate-label-error);
  --state-hint: var(--vstate-hint-error);
  --state-action: var(--vstate-action-error);
}

.state-view[data-family="view"][data-state="blocked"] {
  --state-color: var(--vstate-color-blocked);
  --state-fill: var(--vstate-fill-blocked);
  --state-edge: var(--vstate-edge-blocked);
  --state-glyph: var(--vstate-glyph-blocked);
  --state-label: var(--vstate-label-blocked);
  --state-hint: var(--vstate-hint-blocked);
  --state-action: var(--vstate-action-blocked);
}

.state-view[data-family="view"][data-state="offline"] {
  --state-color: var(--vstate-color-offline);
  --state-fill: var(--vstate-fill-offline);
  --state-edge: var(--vstate-edge-offline);
  --state-glyph: var(--vstate-glyph-offline);
  --state-label: var(--vstate-label-offline);
  --state-hint: var(--vstate-hint-offline);
  --state-action: var(--vstate-action-offline);
}

.state-view[data-family="view"][data-state="timeout"] {
  --state-color: var(--vstate-color-timeout);
  --state-fill: var(--vstate-fill-timeout);
  --state-edge: var(--vstate-edge-timeout);
  --state-glyph: var(--vstate-glyph-timeout);
  --state-label: var(--vstate-label-timeout);
  --state-hint: var(--vstate-hint-timeout);
  --state-action: var(--vstate-action-timeout);
}

/* ==========================================================================
 * A38 · 首页重做（2026-09-29，照正哥给的 `参考图-首页.png`）
 *   ① Hero 紫色渐变横幅 ② 七格挪进 Hero（半透明卡片）
 *   ③ 今日推荐（服务端按到期复习 + 薄弱点实时排） ④ 按 学科 × 年级 的分组卡片
 *
 * ⛔ 这一段**一个色值字面量都没有**：色标全部收在 tokens.css 的 `--hero-*` 里。
 *    为什么这么严：`web/tests/stats.mjs` 会从「七格容器」那条规则起往后扫 1600 个字符，
 *    里面出现十六进制色或 rgb 函数就红（那条断言就是为"组件层不许写死颜色"立的）。
 *    ⚠️ 它**不剥注释** —— 所以连注释里都不能出现那两个形状（本轮踩过一次，已改）。
 * ⛔ 家长口径：**零比较、零积分、不做题量上限、不做解锁**。
 *    进度条只"展示练到哪了"，练到 20% 也照样能点进去 —— 不许拿它拦路。
 * ========================================================================== */

/* ---------------------------------------------------------------- ① Hero */
.home-hero {
  /* .page 已经给了左右 16px，这里**不再加左右外边距**（否则会跟章节卡片对不齐） */
  margin: var(--space-xs) 0 var(--space-sm);
  padding: var(--space-lg) var(--space-md);
  border-radius: var(--radius-lg);
  background: linear-gradient(135deg, var(--hero-grad-a), var(--hero-grad-b));
}
.home-hero__title {
  margin: 0;
  color: var(--hero-ink);
  font-size: var(--text-title);
  font-weight: var(--weight-bold);
  line-height: var(--leading-tight);
}
.home-hero__sub {
  margin: var(--space-xs) 0 var(--space-md);
  color: var(--hero-ink-sub);
  font-size: var(--text-caption);
  line-height: var(--leading-body);
}

/* ------------------------------------- ② 七格：挪进 Hero，改成半透明卡片 */
/* ⚠️ 选择器写成 `.home-hero .stat-strip`（两级）而不是只加一个修饰类（一级）：
   基础规则 `.stat-strip`（一级）在**本段之后**，同级特异性下后者会赢 ——
   那样"挪进 Hero"就只剩位置变了、底色还是白卡。两级选择器稳赢，不依赖书写顺序。 */
.home-hero .stat-strip {
  margin: 0;
  padding: 0;
  border: 0;
  background: transparent;
}
/* 手机 4 列（7 格 = 4+3），宽屏 7 列一排 —— 参考图是 7 格一排 */
.home-hero .stat-strip__grid { grid-template-columns: repeat(4, 1fr); }
@media (min-width: 640px) {
  .home-hero .stat-strip__grid { grid-template-columns: repeat(7, 1fr); }
}
.home-hero .stat-cell {
  padding: var(--space-xs) var(--space-2xs);
  border: 1px solid var(--hero-cell-border);
  border-radius: var(--radius-md);
  background: var(--hero-cell-bg);
}
.home-hero .stat-cell__num {
  color: var(--hero-ink);
  font-size: 1.1rem;
}
.home-hero .stat-cell__label { color: var(--hero-ink-sub); }
.home-hero .stat-strip__note {
  color: var(--hero-note);
  border-top-color: var(--hero-cell-border);
}

/* ------------------------------------------------- ③ 今日推荐（一排卡片） */
.rec-block { margin-top: var(--space-md); }
.rec-block__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-xs);
}
.rec-block__title {
  margin: 0;
  color: var(--color-text-title);
  font-size: var(--text-title);
  font-weight: var(--weight-medium);
  line-height: var(--leading-tight);
}
.rec-block__note {
  margin: 0;
  color: var(--color-text-hint);
  font-size: var(--text-caption);
}
.rec-list {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-sm);
  margin-top: var(--space-sm);
}
@media (min-width: 560px) { .rec-list { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 900px) { .rec-list { grid-template-columns: repeat(4, 1fr); } }

.rec-card {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-2xs);
  min-height: var(--touch-comfort);
  padding: var(--space-md);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--color-surface);
  text-align: left;
}
.rec-card:active { background: var(--color-surface-sunken); }
.rec-card__badge {
  padding: 2px var(--space-xs);
  border-radius: var(--radius-full);
  background: var(--color-primary-soft);
  color: var(--color-primary-ink);
  font-size: 0.72rem;
  line-height: var(--leading-tight);
}
/* 三种来源各自的色：到期复习=待复习琥珀 · 薄弱点=引导紫 · 从这里开始=主色 */
.rec-card[data-kind="review"] .rec-card__badge {
  background: var(--color-review-soon-soft);
  color: var(--color-review-soon);
}
.rec-card[data-kind="weak"] .rec-card__badge {
  background: var(--color-guide-soft);
  color: var(--color-guide-ink);
}
.rec-card[data-kind="start"] .rec-card__badge {
  background: var(--color-primary-soft);
  color: var(--color-primary-ink);
}
.rec-card__title {
  color: var(--color-text-title);
  font-size: var(--text-body);
  font-weight: var(--weight-medium);
  line-height: var(--leading-tight);
}
.rec-card__sub { color: var(--color-text-hint); font-size: var(--text-caption); }
.rec-card__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-xs);
  width: 100%;
  margin-top: auto;
}
.rec-card__reason { color: var(--color-text-sub); font-size: var(--text-caption); }
.rec-card__go {
  color: var(--color-primary);
  font-size: var(--text-caption);
  white-space: nowrap;
}

/* ------------------------------------------- ④ 按 学科 × 年级 的分组卡片 */
.book-block { margin-top: var(--space-lg); }
.book-group + .book-group { margin-top: var(--space-lg); }
.book-group__head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2xs);
  margin: 0 0 var(--space-sm);
  color: var(--color-text-title);
  font-size: var(--text-body);
  font-weight: var(--weight-medium);
  line-height: var(--leading-tight);
}
.book-group__dot {
  width: 8px;
  height: 8px;
  border-radius: var(--radius-full);
  background: var(--color-primary);
}
.book-group__span {
  color: var(--color-text-hint);
  font-size: var(--text-caption);
  font-weight: var(--weight-regular);
}
.book-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-sm);
}
@media (min-width: 560px) { .book-grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 900px) { .book-grid { grid-template-columns: repeat(4, 1fr); } }

.book-card {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-2xs);
  min-height: var(--touch-comfort);
  padding: var(--space-md);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--color-surface);
  text-align: left;
}
.book-card:active { background: var(--color-surface-sunken); }
.book-card__grade {
  align-self: flex-end;
  padding: 2px var(--space-xs);
  border-radius: var(--radius-sm);
  background: var(--color-primary-soft);
  color: var(--color-primary-ink);
  font-size: 0.72rem;
  line-height: var(--leading-tight);
}
.book-card__title {
  color: var(--color-text-title);
  font-size: var(--text-body);
  font-weight: var(--weight-medium);
  line-height: var(--leading-tight);
}
.book-card__topics {
  color: var(--color-text-sub);
  font-size: var(--text-caption);
  line-height: var(--leading-tight);
}
/* 进度条：只展示"练到哪了"。分母是"这一册有多少个知识点"，分子是"练过的知识点"。 */
.book-card__bar {
  display: block;
  width: 100%;
  height: 6px;
  margin-top: var(--space-2xs);
  border-radius: var(--radius-full);
  background: var(--color-surface-sunken);
  overflow: hidden;
}
.book-card__bar-fill {
  display: block;
  width: 0;
  height: 100%;
  background: var(--color-primary);
}
.book-card__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-xs);
  width: 100%;
}
.book-card__status { color: var(--color-text-sub); font-size: var(--text-caption); }
.book-card__kps { color: var(--color-text-hint); font-size: var(--text-caption); }

/* ── B-016：首页七格（我的数字 + 全站口径分开写） ──────────────────────────
   布局：手机 3 列 × 2 行；宽屏 6 列 1 行。
   ⛔ 数字用 --color-text-title（最强的一级），标签用 --color-text-sub（弱一级）——
      不许把标签也做成大号，否则"7 个大数字"会盖过下面的"开始练习"主操作。 */
.stat-strip {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--space-md);
  margin-bottom: var(--space-md);
}
.stat-strip__title {
  margin: 0 0 var(--space-sm);
  font-size: 0.95rem;
  color: var(--color-text-title);
}
.stat-strip__grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-sm) var(--space-xs);
}
@media (min-width: 560px) {
  .stat-strip__grid { grid-template-columns: repeat(6, 1fr); }
}
.stat-cell {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-xs);
  padding: var(--space-xs) 0;
}
.stat-cell__num {
  font-size: 1.25rem;
  font-weight: 600;
  color: var(--color-text-title);
  font-variant-numeric: tabular-nums;
}
.stat-cell__label {
  font-size: 0.75rem;
  color: var(--color-text-sub);
  text-align: center;
}
.stat-strip__note {
  margin: var(--space-sm) 0 0;
  font-size: 0.75rem;
  color: var(--color-text-hint);
  border-top: 1px solid var(--color-border);
  padding-top: var(--space-sm);
}

/* ── B-017c：听写播放器 ──────────────────────────────────────────────
   ⛔ 播放键要够大好按（孩子的手），但**不能抢主操作的视觉重量** ——
      它用 btn-secondary，主提交键仍是 btn-primary。 */
.dictation-player {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  margin-bottom: var(--space-sm);
}
.dictation-player audio { display: none; }
.dictation-play { min-height: 44px; }

/* ── B-022：跟读（录音） ────────────────────────────────────────────
   ⛔ 全部复用既有令牌与 .btn-secondary/.btn-primary，不新造颜色。 */
.shadow-box {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  padding: var(--space-sm);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
.shadow-box__head { font-size: 0.95em; color: var(--text-muted); }
.shadow-box__row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-sm); }
.shadow-box__row audio { display: none; }
.shadow-box__status { font-size: 0.9em; color: var(--text-muted); }
.shadow-box__blocked { font-size: 0.9em; color: var(--text-muted); }

/* ── B-023：听写 / 跟读 两个独立模块页 ──────────────────────────────
   ⛔ 只复用既有令牌与既有按钮类（.exam-entry / .btn-secondary / .btn-primary），不新造颜色。 */
.shadow-item {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  margin-bottom: var(--space-md);
}
.shadow-item__label { font-size: 0.95em; color: var(--text-muted); margin: 0; }
/* ── B-024：跟读（学习模式，不是测试） ─────────────────────────────── */
.shadow-bar { display: flex; flex-wrap: wrap; gap: var(--space-sm); margin-bottom: var(--space-md); }
.learn-item {
  display: flex;
  align-items: flex-start;
  gap: var(--space-sm);
  padding: var(--space-sm) 0;
  border-bottom: 1px solid var(--border);
}
.learn-item__main { flex: 1 1 auto; min-width: 0; }
.learn-item__en { margin: 0; font-size: 1.15em; word-break: break-word; }
.learn-item__hint { margin: var(--space-xs) 0 0; font-size: 0.9em; color: var(--text-muted); }
.learn-item__play { flex: 0 0 auto; min-height: 44px; }
.learn-item audio { display: none; }

/* ==========================================================================
 * A41 · 护眼提醒（2026-09-29）
 *   计划书 Ch03 slide 12：「护眼 —— 控制单次时长，主动提醒休息」。
 *   与家长已裁的「不做题量上限」的张力，按对账表 §R-16 裁为 **只提醒、不阻拦**：
 *     · 这张卡落在**底部操作条上方**：顶栏「返回」与底部主操作都不被盖住，
 *       作答区照常点得动 —— 不点它也照样能做下一题；
 *     · 卡上只有一颗「继续练习」= **一键继续**；
 *     · ⛔ 不锁屏、不封顶、不强制中断、不写「今天必须完成」。
 *   色值一律走令牌（⛔ 本块一个写死的颜色都没有）；几何尺寸取不到令牌的才用 px。
 * ========================================================================== */
.eye-rest {
  position: fixed;
  right: var(--space-md);
  bottom: calc(var(--bottombar-h) + var(--safe-bottom) + var(--space-xs));
  left: var(--space-md);
  z-index: var(--z-toast);   /* A55：原字面量 20 ⇒ 令牌（护眼提醒要浮在底部操作条之上） */
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
  max-width: var(--max-content);
  margin: 0 auto;
  padding: var(--space-md);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--color-surface);
  box-shadow: var(--shadow-card);
}

.eye-rest__title {
  margin: 0;
  color: var(--color-text-title);
  font-size: var(--text-body);
  font-weight: var(--weight-medium);
  line-height: var(--leading-tight);
}

.eye-rest__text,
.eye-rest__note {
  margin: 0;
  color: var(--color-text-sub);
  font-size: var(--text-caption);
  line-height: var(--leading-body);
}

/* 一键继续：与底部主操作同一套按钮（.btn-primary），孩子不用找 */
.eye-rest__continue {
  margin-top: var(--space-2xs);
}

/* ── A46：成绩页逐题「再来一道同类题」 ─────────────────────────────── */
.result-item__actions { display: flex; justify-content: flex-end; margin-top: var(--space-sm); }
.result-item__again { min-height: 44px; }
