/* ============================================================================
 * tokens.css —— 设计令牌（唯一真源）
 * 逐条落地自 docs/设计规范.md §2「设计令牌」。只放 CSS 自定义属性，
 * 不含任何组件样式（组件在 components.css / pages.css）。
 *
 * 落地约定（设计规范 §7.1 / §7.2）：
 *   - 其他 CSS 文件只引用变量，不写死色值与字号
 *   - :root 为默认档（小学档，规范 §2.3 代码块原样）；
 *     data-grade="middle" 下调一档，data-grade="primary" 显式写明，
 *     两个属性值都不会静默回落到错误的档
 *   - 换主色只改本文件
 * ========================================================================== */

:root {
  /* ---------------------------------------------------------------- §2.1 基础色值
     「只在这一层写死十六进制」。业务代码禁止直接用这一层。 */
  --blue-700: #12408F;   /* 主色深（按下态） */
  --blue-600: #1A56CC;   /* 主色 */
  --blue-100: #E8F0FE;   /* 主色浅底 */
  --blue-900: #0E3A6E;   /* 主色墨（浅底上的文字） */
  --green-700: #0F7B4A;  /* 答对 */
  --green-050: #E8F7EF;  /* 答对浅底 */
  --red-700: #B3261E;    /* 答错 */
  --red-050: #FDECEA;    /* 答错浅底 */
  --amber-800: #6B4E00;  /* 未答 / 待处理 */
  --amber-050: #FFF6E0;  /* 未答浅底 */
  --gray-900: #111827;   /* 标题文字 */
  --gray-800: #1F2937;   /* 正文文字 */
  --gray-600: #4B5563;   /* 次级文字 */
  --gray-500: #5F6B7A;   /* 提示文字（**不是 #8A93A0**，那个只有 3.11:1 不达 AA） */
  --gray-200: #EEF1F5;   /* 分隔线 / 内凹底 */
  --gray-050: #F7F8FA;   /* 页面底色 */
  --white: #FFFFFF;      /* 卡片底 */

  /* ---------------------------------------------------------------- §2.1 语义色 */
  /* ---- 主色 ---- */
  --color-primary: var(--blue-600);
  --color-primary-press: var(--blue-700);
  --color-primary-soft: var(--blue-100);
  --color-primary-ink: var(--blue-900);   /* 浅底上的主色文字 */
  --color-on-primary: #FFFFFF;

  /* ---- 作答状态色（三色一套，语义固定，不得复用） ---- */
  --color-correct: var(--green-700);      /* 答对 */
  --color-correct-soft: var(--green-050);
  --color-wrong: var(--red-700);          /* 答错（可再练） */
  --color-wrong-soft: var(--red-050);
  --color-pending: var(--amber-800);      /* 未答 / 待复习 */
  --color-pending-soft: var(--amber-050);

  /* ---- 文字层级 ---- */
  --color-text: var(--gray-800);          /* 正文 */
  --color-text-title: var(--gray-900);    /* 标题 */
  --color-text-sub: var(--gray-600);      /* 次级说明 */
  --color-text-hint: var(--gray-500);     /* 提示、计数、辅助 */

  /* ---- 背景层级（三级，靠明度区分，不靠边框） ---- */
  --color-bg: var(--gray-050);            /* 页面底 */
  --color-surface: #FFFFFF;               /* 卡片 */
  --color-surface-sunken: var(--gray-200);/* 内凹区：答题输入区、题目导航格 */
  --color-border: #E3E7ED;

  /* ---- 考试模式专用（全局降噪） ---- */
  --color-exam-bg: #FFFFFF;               /* 考试页底：纯白，不用灰底 */
  --color-exam-surface: #FFFFFF;
  --color-exam-text: var(--gray-900);
  --color-exam-line: #D5DAE2;

  /* ---- 焦点环（键盘/无障碍） ---- */
  --color-focus: var(--blue-600);

  /* ---------------------------------------------------------------- §2.3 字体与字号
     系统字体栈：不下载任何 Web 字体（中文字体动辄 3–8MB）。 */
  --font-sans: system-ui, -apple-system, "PingFang SC", "Noto Sans SC",
               "Microsoft YaHei", sans-serif;
  --font-mono: ui-monospace, "SFMono-Regular", Menlo, monospace; /* 仅倒计时/数字用 */

  --text-score: clamp(46px, 7vw + 12px, 64px);
  --text-title: 26px;
  --text-timer: 32px;
  --text-input: 22px;
  --text-btn: 20px;
  --text-body: 18px;
  --text-caption: 15px;

  --leading-tight: 1.3;   /* 标题、按钮 */
  --leading-body: 1.7;    /* 题干、解析（1.7 是实测最舒服的值） */
  --leading-math: 1.9;    /* 算式、竖式预留 */

  --weight-regular: 400;
  --weight-medium: 500;
  --weight-bold: 700;

  /* ---------------------------------------------------------------- §2.4 间距阶梯（8px 基准栅格） */
  --space-2xs: 4px;   /* 图标与文字间距、标签内边距 */
  --space-xs: 8px;    /* 紧凑元素之间、可点击元素的最小安全间隔 */
  --space-sm: 12px;   /* 卡片内部行距（初中） */
  --space-md: 16px;   /* 卡片内边距、卡片间距（小学）、页面左右边距 */
  --space-lg: 24px;   /* 区块之间 */
  --space-xl: 32px;   /* 大区块分隔、页面顶部留白 */
  --space-2xl: 48px;  /* 空状态居中留白、成绩页大数字周围 */

  --max-content: 640px;  /* 平板（≥768px）内容最大宽度，居中，不拉满 */

  /* ---------------------------------------------------------------- §2.5 圆角与阴影 */
  --radius-sm: 8px;      /* 标签、状态角标 */
  --radius-md: 12px;     /* 按钮、输入框、题目导航格 */
  --radius-lg: 16px;     /* 卡片、弹层 */
  --radius-full: 9999px; /* 进度条、头像、圆形图标按钮 */

  --shadow-card: 0 1px 3px rgba(17,24,39,.06);   /* 卡片（极淡） */
  --shadow-float: 0 6px 16px rgba(17,24,39,.12); /* 悬浮按钮、底部操作条 */
  --shadow-modal: 0 12px 32px rgba(17,24,39,.18);/* 弹层、底部抽屉 */

  /* A55：遮罩色。此前只有 components.css 里一处裸写 `rgba(17,24,39,0.4)` ——
     阴影进了令牌、遮罩没进，两者本该同一待遇（`--shadow-modal` 的基准色就是同一个 17,24,39）。
     与考试模式关阴影同理：将来若要"考试模式不压暗"，改这一处就够。 */
  --color-scrim: rgba(17,24,39,.4);

  /* ---------------------------------------------------------------- §2.5b 层级（A55 补）
   * 为什么要有这一段：此前 z-index 是**7 个裸数字**散在三个文件里
   *   components.css 10/10/20/50 · exam.css 10 · pages.css 5/20/30/40
   * —— pages.css 第 601 行的注释自己就写着"能从令牌取的一律取令牌"，但当时没有 z-index 令牌可取而作罢。
   * 后果是每加一个新浮层（A41 护眼卡、A54 底部条、底部抽屉）都得先翻遍全仓猜一个数。
   *
   * 档位按"谁该盖住谁"排，**不用 1 起步**：留出空间给将来的页内微调，
   * 也让"10 的倍数"这个约定一眼能看出层级关系。
   * ⛔ 新浮层一律取令牌，别再写字面数字（P4打磨 的静态检查盯着这一类）。
   * ------------------------------------------------------------------------- */
  --z-base: 1;        /* 页内普通相对定位（一般不写，写就用它） */
  --z-sticky: 10;     /* 吸顶/吸底：成绩页吸顶条、考试页进度条、drawer 内部件 */
  --z-dropdown: 20;   /* 下拉、浮出的小面板、底部操作条 */
  --z-drawer: 30;     /* 抽屉面板本体 */
  --z-overlay: 40;    /* 遮罩（配 --color-scrim） */
  --z-modal: 50;      /* 弹层本体：必须盖住遮罩 */
  --z-toast: 60;      /* 浮在最上的提醒：护眼提醒卡、离线提示 */

  /* ---------------------------------------------------------------- §2.6 触控与布局尺寸 */
  --touch-min: 44px;      /* 绝对下限，任何可点击元素不得小于此 */
  --touch-comfort: 52px;  /* 列表行、题目导航格 */
  --touch-primary: 60px;  /* 主操作按钮（小学 60 / 初中 52，见下方档位） */
  --touch-gap: 8px;       /* 相邻可点击元素最小间隔，防误触 */
  --thumb-zone: 96px;     /* 主操作按钮必须落在底部 96px 以内 */
  --header-h: 56px;       /* 顶栏高度（练习页） */
  --bottombar-h: 72px;    /* 底部操作条高度 */
  --safe-bottom: env(safe-area-inset-bottom); /* iPhone 全面屏底部安全区 */
  /* 系统键盘避让：由 assets/viewport.js 用 visualViewport 实时写入（无键盘时 0px）。
     ⛔ 别在别处改这个值；也⛔别在组件里直接算键盘高度 —— 只有 C15 底栏读它。 */
  --kb-inset: 0px;
  --identity-bar-h: 36px; /* 常驻身份条高度（C19）——比顶栏矮：它是"参照物"不是操作区 */
}

/* ============================================================================
 * §2.3 字号阶梯 / §2.2 年级差异
 * 小学（primary）：字号整体放大、间距放宽、主按钮更高
 * 初中（middle）：字号紧凑一档、一屏能多放内容
 * ⚠️ 机制：data-grade 变化后 CSS 自动重算，不需要刷新页面（设计规范 §7.2）
 *    —— 选完头像由 app.js 写入 <html data-grade>，本文件负责切档。
 * ========================================================================== */

:root[data-grade="primary"] {
  --grade-scale: 1.0;
  --text-title: 26px;
  --text-timer: 32px;
  --text-input: 22px;
  --text-btn: 20px;
  --text-body: 18px;
  --text-caption: 15px;
  --touch-primary: 60px;
}

:root[data-grade="middle"] {
  --grade-scale: 0.94;
  --text-title: 22px;
  --text-timer: 30px;
  --text-input: 19px;
  --text-btn: 18px;
  --text-body: 16px;
  --text-caption: 14px;
  --touch-primary: 52px;
}

/* ============================================================================
 * §2.5 考试模式：全部阴影关闭，只保留 1px 分隔线。安静、像卷子。
 * （规范 §2.5 原文只点名 --shadow-card: none；此处把三级阴影一并关掉，
 *   因为"考试模式下全部阴影关闭"是不留例外的表述。已在代码注释说明偏离。）
 * ========================================================================== */
:root[data-theme="exam"] {
  --shadow-card: none;
  --shadow-float: none;
  --shadow-modal: none;
}

/* ============================================================================
 * P0 升级 · 计划书新增维度（2026-09-26）
 * ① 学科专属色（一套学科 = 一个主题，按 data-subject 切换——计划书 §05 学科专属色）
 * ② 四档分学段主题（preschool 幼儿 → primary 小学 → middle 初中 → senior 高中，
 *    计划书 §08 学段分层交互，在原有 primary/middle 之上扩出两端）
 * ③ 人性化 / 学习科学机制 tokens（计划书 §09：答错=发现薄弱点、间隔重复状态、正向引导）
 * 追加块，不改动既有变量，向后兼容。
 * ========================================================================== */

/* ---------------------------------------------------------------
 * ① 学科专属色 —— 默认(未指定)回落通用主色(蓝)
 * 学科 key 与业务侧约定一致：math/chinese/english/geography/history/political/biology
 * 每科给 主色(primary) / 浅底(soft) / 墨字(ink) / 反白(on) 四个语义位，
 * 其余通用功能色(correct/wrong/pending)保持不动——只有主色随学科切换。
 * --------------------------------------------------------------- */
:root {
  /* 学科色原始值（业务代码禁止直接用这一层） */
  --subj-math-600: #1A56CC;  --subj-math-100: #E8F0FE;  --subj-math-900: #0E3A6E;
  --subj-chinese-600: #B5403C; --subj-chinese-100: #FAEAE8; --subj-chinese-900: #7A2A26; /* 丹砂红 */
  --subj-english-600: #0F766E; --subj-english-100: #E0F2EF; --subj-english-900: #0A5550; /* 青绿 */
  --subj-geography-600: #8B5E34; --subj-geography-100: #F3E9DD; --subj-geography-900: #5B3D1E; /* 赭石棕 */
  --subj-history-600: #A16207; --subj-history-100: #FBF1D9; --subj-history-900: #6B4E00; /* 鎏金 */
  --subj-political-600: #6D28D9; --subj-political-100: #EFEAFD; --subj-political-900: #472B8F; /* 紫棠 */
  --subj-biology-600: #0D9488; --subj-biology-100: #E0F2EF; --subj-biology-900: #0A5550; /* 青碧 */
}
/* 通用主色默认 = 数学蓝（保持现有主色不变） */
:root[data-subject="math"] {
  --color-primary: var(--subj-math-600);
  --color-primary-soft: var(--subj-math-100);
  --color-primary-ink: var(--subj-math-900);
}
:root[data-subject="chinese"] { --color-primary: var(--subj-chinese-600); --color-primary-soft: var(--subj-chinese-100); --color-primary-ink: var(--subj-chinese-900); }
:root[data-subject="english"] { --color-primary: var(--subj-english-600); --color-primary-soft: var(--subj-english-100); --color-primary-ink: var(--subj-english-900); }
:root[data-subject="geography"] { --color-primary: var(--subj-geography-600); --color-primary-soft: var(--subj-geography-100); --color-primary-ink: var(--subj-geography-900); }
:root[data-subject="history"] { --color-primary: var(--subj-history-600); --color-primary-soft: var(--subj-history-100); --color-primary-ink: var(--subj-history-900); }
:root[data-subject="political"] { --color-primary: var(--subj-political-600); --color-primary-soft: var(--subj-political-100); --color-primary-ink: var(--subj-political-900); }
:root[data-subject="biology"] { --color-primary: var(--subj-biology-600); --color-primary-soft: var(--subj-biology-100); --color-primary-ink: var(--subj-biology-900); }

/* ---------------------------------------------------------------
 * ② 四档分学段主题 —— 在 primary(小学)/middle(初中) 基础上扩出两端
 *    幼儿 preschool：专注 5-10 分钟 → 更大字、更大按钮、更大图标、间距更宽（认知负荷 ↓）
 *    高中 senior：抽象成熟、自主驱动 → 信息密度更高、字号更紧凑（一屏装更多）
 *    选择器优先级：preschool > primary > middle > senior（默认回落 middle 紧凑档之外的原值）
 * --------------------------------------------------------------- */
:root[data-grade="preschool"] {
  --grade-scale: 1.10;
  --text-title: 30px;
  --text-timer: 36px;
  --text-input: 26px;
  --text-btn: 23px;
  --text-body: 20px;
  --text-caption: 16px;
  --touch-primary: 68px;      /* 幼儿手指更粗、误触更常见，主键更大 */
  --space-md: 20px;           /* 间距放宽 */
}
:root[data-grade="senior"] {
  --grade-scale: 0.88;
  --text-title: 20px;
  --text-timer: 28px;
  --text-input: 17px;
  --text-btn: 16px;
  --text-body: 15px;
  --text-caption: 13px;
  --touch-primary: 48px;      /* 高中手指已成熟，可更紧凑 */
}

/* 家长档 parent（K2）—— 家长是成年人，不是"初中生"。
   原来家长端写 `data-grade="middle"`（借"常规字号档"），读代码的人得靠一行注释才知道
   那是借用；现在给它一个自己的档名，**值与 middle 逐令牌相同**。
   ⚠️ 同值是**刻意的**：改这里等于改家长端字号，那是另一件事、要走自己的验收
   （家长 30 秒看懂看板）。守"两边不许各自漂移"的套件：web/tests/grade_bands.mjs。 */
:root[data-grade="parent"] {
  --grade-scale: 0.94;
  --text-title: 22px;
  --text-timer: 30px;
  --text-input: 19px;
  --text-btn: 18px;
  --text-body: 16px;
  --text-caption: 14px;
  --touch-primary: 52px;
}

/* 默认(未写 data-grade)＝中档偏小学的通用值，保持既有行为不变 */

/* ---------------------------------------------------------------
 * ③ 人性化 / 学习科学机制 tokens（计划书 §09）
 *    —— 答错不是惩罚，是"发现薄弱点"；间隔重复不是压力，是科学节奏。
 *    颜色语义刻意与"三色作答状态"区分，不抢 correct/wrong/pending 的关注。
 * --------------------------------------------------------------- */
:root {
  /* 正向引导 / 发现薄弱点：答错后的"怎么办"引导，用温暖紫，不刺眼、不指责 */
  --color-guide: #7C3AED;        /* 引导箭头/提示图标 */
  --color-guide-soft: #F3EEFC;   /* 引导提示底 */
  --color-guide-ink: #472B8F;    /* 引导文字（浅底可读） */

  /* 间隔重复状态（错题本 / 复习计划） */
  --color-review-soon: var(--amber-800);   /* 待复习（到期） */
  --color-review-soon-soft: var(--amber-050);
  --color-review-mastered: var(--green-700);/* 已掌握（连续 3 次做对） */
  --color-review-mastered-soft: var(--green-050);
  --color-review-new: var(--blue-600);      /* 新收录 */

  /* 鼓励措辞（成就激励，非积分刺激） */
  --color-encourage: #B45309;    /* 暖橙，用于"完成了/有进步"的轻快语气 */
  --color-encourage-soft: #FDF1E3;

  /* 薄弱点分级（家长看板，越红越需干预，但刻意用"提醒"而非"警告"语义） */
  --color-weak-mild: #CA8A04;    /* 轻微薄弱：偶尔错 */
  --color-weak-major: var(--red-700); /* 明显薄弱：经常错 */
}

/* ============================================================================
 * 作答反馈六态 · 「三重冗余」令牌（2026-09-26 · K1.1）
 * ============================================================================
 * 依据：docs/设计规范.md §6.3（色盲可辨，令牌层强制）
 * 为什么必须有（硬数据，不是审美）：用 Viénot(1999) 矩阵模拟红色盲(protan)后，
 *   状态色全部塌成橄榄绿系（对 #565615 / 错 #505017 / 待复习 #444410 /
 *   超时 #42421B / 跳过 #55551A）—— 彼此几乎无法区分。
 *   ⇒ 只靠颜色，色弱孩子读不出状态。所以每一态必须**同时**具备四层：
 *     ① 颜色 --color-* + --color-*-soft（浅底）
 *     ② 填充 --fill-*（实心 / 实心+竖条 / 点阵 / 斜纹 / 空心 / 细虚线浅底，两两不同）
 *     ③ 几何字符 --glyph-*（纯字符，不用 emoji：各家手机渲染不一致）
 *     ④ 文字标签 --label-*（界面文案唯一来源；口径见设计规范 §5.1）
 *   另配 --bar-*（左侧竖条）与 --edge-*（描边）。
 * ⛔ 组件只准引用这些变量：不许自己写死色值 / 纹样 / 字符 / 文案。
 * ========================================================================== */

:root {
  /* ① 颜色：补齐计划书新增两态（既有 correct / wrong / pending 三色不动） */
  --color-timeout: #5B3FB5;          /* 超时（紫）—— 时间不够 ≠ 不会，语义独立 */
  --color-timeout-soft: #EEEAFB;
  --color-skip: #5F6B7A;             /* 跳过（灰） */
  --color-skip-soft: #F2F4F7;

  /* ② 填充样式：六种两两不同 —— 转成灰度也能靠纹理分辨 */
  --fill-correct: var(--color-correct-soft);   /* 实心浅绿底 */
  --fill-wrong: var(--color-wrong-soft);       /* 实心浅红底 */
  --fill-pending: radial-gradient(var(--color-pending) 1.5px, transparent 1.6px) 0 0 / 8px 8px; /* 点阵 */
  --fill-timeout: repeating-linear-gradient(45deg, var(--color-timeout) 0 2px, transparent 2px 8px); /* 45° 斜纹 */
  --fill-skip: transparent;                    /* 空心 */
  /* ⚠️ 「未作答」原来写 var(--color-bg)（页面底色）—— 真浏览器实测：在灰底页面上
     它和 transparent 视觉等价，"填充"这一层等于白给（靠字符/文案才分得开）。
     改成内凹底（白卡片上看得出凹陷），才真的多出一层冗余。 */
  --fill-blank: var(--color-surface-sunken);   /* 内凹底：视觉"缺席感" */

  /* 左侧竖条：只有"答错"有（比"答对"多一条竖边，灰度下也能分） */
  --bar-correct: 0;
  --bar-wrong: 4px;
  --bar-pending: 0;
  --bar-timeout: 0;
  --bar-skip: 0;
  --bar-blank: 0;

  /* ③ 几何字符（转义写法，避免源码编码问题）：✓ ↻ ◐ ⧗ — ○ */
  --glyph-correct: "\2713";
  --glyph-wrong: "\21BB";
  --glyph-pending: "\25D0";
  --glyph-timeout: "\29D7";   /* 几何沙漏 —— 刻意不用 emoji 的沙漏字符 */
  --glyph-skip: "\2014";
  --glyph-blank: "\25CB";

  /* ④ 描边（与填充配对） */
  --edge-correct: 2px solid var(--color-correct);
  --edge-wrong: 2px solid var(--color-wrong);
  --edge-pending: 2px solid var(--color-pending);
  --edge-timeout: 2px solid var(--color-timeout);
  --edge-skip: 2px dashed var(--color-skip);       /* 2px：与「未作答」的 1px 细虚线差两档 */
  --edge-blank: 1px dashed var(--color-border);

  /* ⑤ 文字标签（孩子端用词；不写"错误"、不写"正确答案为"，见规范 §5.1） */
  --label-correct: "答对了";
  --label-wrong: "差一点点，我们看看这里";
  --label-pending: "该再看一眼";
  --label-timeout: "时间到了，没来得及做";
  --label-skip: "跳过了";
  --label-blank: "还没做";
}

/* ============================================================================
 * 掌握级六态 · 令牌（2026-09-26 · K1.2 · 设计规范 §6.4，产品规格 §10.1）
 * ============================================================================
 * 与「作答反馈六态」（上一块）并列的第二族：
 *   反馈六态管「这一道题刚答得怎样」（每题即时，考完即过）；
 *   掌握级六态管「这个知识点/这道错题学得怎样」（长期，决定错题本与复习怎么展示）。
 *   ⛔ 两族不许互相顶替：不能用「答错」的红去表达「需重学」（§6.4 / §10.0）。
 *
 * 为什么另起命名空间 `--mstate-<组>-<态>`：两族**有同名 key**（两族都有「待复习」），
 *   共用 `--fill-*` 前缀会让"表 ↔ 令牌"双向核对分不清是哪一族，组件选择器也会撞。
 *   组 = color/fill/edge/bar/glyph/label；态 = new/learning/review/mastered/relearn/skip。
 *
 * 颜色语义源一律复用已有令牌（不重复定义同义色），只补 §10.1 点名的两个缺令牌：
 *   新 --color-state-new · 在学 --color-primary · 待复习 --color-review-soon
 *   已掌握 --color-review-mastered · 需重学 --color-guide · 已跳过 --color-state-skip
 *
 * ⚠️ 「新」的填充刻意用**空心**（transparent）而不是浅蓝底 —— §10.1 原建议 `--color-state-new-soft`，
 *   但它与「在学」的 `--color-primary-soft` 同值 ⇒ 「新」与「在学」在"颜色 + 填充"两层上完全一样，
 *   冗余层数不足（与 K1.1 真浏览器抓到的"假填充"同一类问题）。空心 = "还没开始"，语义也对。
 *   该令牌仍保留（供 chip 底色等场合），但**不作为**「新」的填充层。
 * ========================================================================== */

:root {
  /* 补齐 §10.1 点名的缺令牌（其余复用 §9 已有） */
  --color-state-new: var(--color-review-new);        /* 新收录（= §9 的 --color-review-new，别名不复制值） */
  --color-state-new-soft: var(--color-primary-soft); /* 浅蓝底：chip 底色用；**不是**「新」的填充层 */
  --color-state-skip: var(--color-text-hint);        /* 已跳过（灰） */
  --color-state-skip-soft: var(--color-surface-sunken);

  /* ① 主色（逐态一条，组件的 color 只引用它） */
  --mstate-color-new: var(--color-state-new);
  --mstate-color-learning: var(--color-primary);
  --mstate-color-review: var(--color-review-soon);
  --mstate-color-mastered: var(--color-review-mastered);
  --mstate-color-relearn: var(--color-guide);
  --mstate-color-skip: var(--color-state-skip);

  /* ② 填充：空心 / 实心浅底 / 点阵 / 实心浅底 / 实心浅底 / 灰底 —— 六种两两不同 */
  --mstate-fill-new: transparent;                       /* 空心：还没开始 */
  --mstate-fill-learning: var(--color-primary-soft);    /* 实心：学过一点 */
  --mstate-fill-review: var(--fill-pending);            /* 点阵：与反馈「待复习」共用同一套纹理语言 */
  --mstate-fill-mastered: var(--color-review-mastered-soft);
  --mstate-fill-relearn: var(--color-guide-soft);
  --mstate-fill-skip: var(--color-state-skip-soft);

  /* ③ 描边：未定/静止的两态（新 · 已跳过）用虚线 */
  --mstate-edge-new: 2px dashed var(--color-state-new);
  --mstate-edge-learning: 2px solid var(--color-primary);
  --mstate-edge-review: 2px solid var(--color-review-soon);
  --mstate-edge-mastered: 2px solid var(--color-review-mastered);
  --mstate-edge-relearn: 2px solid var(--color-guide);
  --mstate-edge-skip: 2px dashed var(--color-state-skip);

  /* ④ 左侧竖条：只有「需重学」有（会复发的那一态最需要被看见） */
  --mstate-bar-new: 0;
  --mstate-bar-learning: 0;
  --mstate-bar-review: 0;
  --mstate-bar-mastered: 0;
  --mstate-bar-relearn: 4px;
  --mstate-bar-skip: 0;

  /* ⑤ 几何字符（纯字符，不用 emoji）：○ ◐ ↻ ✓ ✎ – */
  --mstate-glyph-new: "\25CB";
  --mstate-glyph-learning: "\25D0";
  --mstate-glyph-review: "\21BB";
  --mstate-glyph-mastered: "\2713";
  --mstate-glyph-relearn: "\270E";
  --mstate-glyph-skip: "\2013";   /* – 短横：刻意与反馈「跳过」的 — 长横不同字 */

  /* ⑥ 文字标签（孩子话，措辞口径见规范 §5.1；错因四类文案见 §10.6） */
  --mstate-label-new: "新";
  --mstate-label-learning: "在学";
  --mstate-label-review: "待复习";
  --mstate-label-mastered: "已掌握";
  --mstate-label-relearn: "需重学";
  --mstate-label-skip: "已跳过";
}


/* ============================================================================
 * 页面六态 · 令牌（2026-09-26 · K1.3 · 设计规范 §6.5，产品规格 40-ux §6）
 * ============================================================================
 * 第三族，与「作答反馈六态」（§6.3）、「掌握级六态」（§6.4）并列：
 *   反馈六态   = 一道题刚答完怎样（每题即时，考完即过）；
 *   掌握级六态 = 一个知识点 / 一道错题学得怎样（长期，决定错题本与复习怎么展示）；
 *   页面六态   = **页面 / 区块自己**在加载与失败时怎样（单次请求内）。
 *   ⛔ 三族不许互相顶替：页面"没加载出来"不是"答错"，也不等于掌握级的"需重学"。
 *
 * 命名空间 `--vstate-<组>-<态>`（v = view）：
 *   态 = skeleton / empty / error / blocked / offline / timeout
 *   组 = color / fill / edge / glyph（视觉四层）+ label / hint / action（文案三件套）
 *   ⚠️ 本族**刻意不用**「左侧竖条」那一层（页面态不需要"哪一态最该被看见"的排序），
 *      所以通道数是 4 + 3，而不是另外两族的 6 组；差异只此一处，套件按 7 条逐态核对。
 *   另起前缀的理由同 §6.4：三族**有同名 key**（三族都有"超时"），共用前缀会让
 *   「文档表 ↔ 令牌」双向核对分不清是哪一族。
 *
 * 颜色一律复用已有语义令牌（不重复定义同义色）：
 *   骨架 = 提示灰 · 空 = 主色墨蓝 · 错误 = 答错红 · 阻塞 = 标题墨 · 离线 = 未答琥珀 · 超时 = 超时紫
 * ⚠️「错误」与「超时」刻意用**两个不同色 + 两种不同纹样**：40-ux §6 规则 7 要求
 *   「请求超时」与「考试时间到」视觉必须不同（后者根本不是错误，走成绩页的"晚交 N 题没算"）。
 *
 * 文案三件套都在**令牌层**：界面文案的唯一来源就是这里。JS 只许通过 state-view.js 的
 *   `text()` 读（`--vstate-label-*` / `--vstate-hint-*` / `--vstate-action-*`），
 *   不许在页面里另抄一份 —— 与 K1.1/K1.2 同一条纪律（§6.3「文案不在 CSS 里」的延伸：
 *   这里更进一步，文案**也不在 JS 里**）。
 * 低段（preschool / primary）另有一份覆盖块（本文件末尾）：不出现「网络 / 服务器 / 加载失败」技术词。
 * ========================================================================== */

:root {
  /* 结构性尺寸（离线细条 48px 是 40-ux §6 定死的数；骨架条与间距按现有网格取整） */
  --vstate-strip-h: 48px;   /* 离线细条高度 */
  --vstate-bar-h: 12px;     /* 骨架条高度 */
  --vstate-bar-gap: 8px;    /* 骨架条间距 */

  /* ① 主色（逐态一条，组件只引用它） */
  --vstate-color-skeleton: var(--color-text-hint);
  --vstate-color-empty: var(--color-primary-ink);
  --vstate-color-error: var(--color-wrong);
  --vstate-color-blocked: var(--color-text-title);
  --vstate-color-offline: var(--color-pending);
  --vstate-color-timeout: var(--color-timeout);

  /* ② 填充：骨架条纹 / 白卡 / 浅红 / 内凹灰 / 浅琥珀 / 45°斜纹 —— 六种两两不同，且都不是"假填充" */
  --vstate-fill-skeleton: repeating-linear-gradient(90deg, var(--color-surface-sunken) 0 24px, var(--color-border) 24px 48px);
  --vstate-fill-empty: var(--color-surface);
  --vstate-fill-error: var(--color-wrong-soft);
  --vstate-fill-blocked: var(--color-surface-sunken);
  --vstate-fill-offline: var(--color-pending-soft);
  --vstate-fill-timeout: repeating-linear-gradient(45deg, var(--color-timeout) 0 2px, transparent 2px 8px);

  /* ③ 描边 */
  --vstate-edge-skeleton: 1px dashed var(--color-border);
  --vstate-edge-empty: 1px solid var(--color-border);
  --vstate-edge-error: 1px solid var(--color-wrong);
  --vstate-edge-blocked: 2px solid var(--color-text-title);
  --vstate-edge-offline: 0;
  --vstate-edge-timeout: 1px solid var(--color-timeout);

  /* ④ 几何字符（纯几何字符，不用 emoji；与另外两族的字符**刻意不同字**） */
  --vstate-glyph-skeleton: none;      /* 骨架屏不出字符：它本身就是灰条 */
  --vstate-glyph-empty: "\25A1";      /* □ 空框 */
  --vstate-glyph-error: "\21BA";      /* ↺ 回退箭头 = 重试 */
  --vstate-glyph-blocked: "\2298";    /* ⊘ 禁止符 = 进不去 */
  --vstate-glyph-offline: "\2016";    /* ‖ 双竖线 = 连接断 */
  --vstate-glyph-timeout: "\29D6";    /* ⧖ 空心沙漏（几何字符，非 emoji 沙漏 ⏳） */

  /* ⑤ 标题：发生了什么（骨架 / 离线细条按 40-ux §6 表：骨架无文字） */
  --vstate-label-skeleton: "";
  --vstate-label-empty-none: "还没开始";
  --vstate-label-empty-zero: "暂时没有题目";
  --vstate-label-error: "这次没加载出来";
  --vstate-label-blocked: "这里进不去";
  --vstate-label-offline: "现在没网";
  --vstate-label-timeout: "等太久了";

  /* ⑥ 副文案：现在做什么（与标题合成 40-ux 规则 4 的"三段式"：发生了什么 + 谁的问题 + 现在做什么） */
  --vstate-hint-skeleton: "";
  --vstate-hint-empty-none: "这一科还在准备，先看看别的";
  --vstate-hint-empty-zero: "换一章，或者先去练几道";
  --vstate-hint-error: "是网络的问题，再来一次就好";
  --vstate-hint-blocked: "回到入口重新开始";
  --vstate-hint-offline: "答的题会先存着";
  --vstate-hint-timeout: "是网络慢，再来一次就好";

  /* ⑦ 出口按钮：每页最多一个阻塞态、且只给**唯一**出口（40-ux 规则 2）。
        空串 = 这一态不给按钮（骨架没有出口；离线是细条、不阻断，恢复后自动重试一次）。 */
  --vstate-action-skeleton: "";
  --vstate-action-empty-none: "去别的科目";
  --vstate-action-empty-zero: "去练几道";
  --vstate-action-error: "再试一次";
  --vstate-action-blocked: "回到入口";
  --vstate-action-offline: "";
  --vstate-action-timeout: "再试一次";
}

/* 低段文案覆盖（童蒙 preschool / 小学 primary）—— 40-ux §6 规则 9：
   低段不出现「网络 / 服务器 / 加载失败 / 错误码」这类技术词，改成「没连上 / 再来一次」。
   ⚠️ 只覆盖**文案**；颜色 / 填充 / 几何字符三态不变（换学段不换语义色）。 */
:root[data-grade="preschool"],
:root[data-grade="primary"] {
  --vstate-label-error: "没连上";
  --vstate-hint-error: "再来一次吧";
  --vstate-hint-timeout: "再来一次吧";
  --vstate-hint-offline: "做过的会先存着";
  --vstate-action-blocked: "回去";
}

/* ============================================================================
 * A38 · 首页 Hero（2026-09-29，照正哥给的 `参考图-首页.png` 那一块紫色渐变横幅重做）
 *
 * 为什么单开这一层：组件层（pages.css）**不许写死颜色** —— `web/tests/stats.mjs`
 * 会扫 `.stat-strip {` 之后 1600 个字符，里面出现 `#rrggbb` 或 `rgb()` 就红。
 * 而 Hero 是渐变 + 半透明卡片，本来就得有色标。所以色标收在令牌层，
 * 组件层一律 `var(...)`（「只在这一层写死十六进制」这条约定继续有效）。
 *
 * ⛔ 这是**产品自己的**配色，不是从参考图取色：浅端用与 `--color-guide`（温暖紫，
 *    「答错 = 发现薄弱点」）同一家族，深端沿用它本身 —— 不引入新的色系。
 * ⛔ 横幅上的文字全部是白字/半透明白字，**只用在这块渐变底上**；
 *    不许拿 `--hero-ink` 去浅色底上写字（那样对比度不达标）。
 * ========================================================================== */
:root {
  --hero-grad-a: #4C46D8;                  /* 渐变浅端（靛紫） */
  --hero-grad-b: var(--color-guide);       /* 渐变深端（紫，与"引导色"同源） */
  --hero-ink: #FFFFFF;                     /* 横幅上的大标题 */
  --hero-ink-sub: rgba(255,255,255,.88);   /* 横幅上的说明句 */
  --hero-cell-bg: rgba(255,255,255,.14);   /* 七格：半透明卡片底 */
  --hero-cell-border: rgba(255,255,255,.26);
  --hero-note: rgba(255,255,255,.74);      /* 横幅底部那行小字 */
  --hero-focus: #FFFFFF;                   /* 横幅内的焦点环（深底上的蓝环看不清） */
}

