/* AI 小课堂 —— 界面样式
 *
 * 视觉语言：知识信号台（产品逻辑改进与前端设计 §3）
 *   每节课是一段正在点亮的信号，每段代码是可观察的实验，每次排序是可操纵的数据表演。
 *
 * 三条结构性决定，改样式前先读：
 *
 * 1. **大面积保持明快，不做整站深色。** 冷白是底色，石墨黑只用于顶栏色带、
 *    标题与文字。整站深色会让长时间读题、写代码变累，而且和「科教馆互动装置」
 *    的明亮感相反。
 *
 * 2. **颜色各有含义，不按学段把整页涂成单一色。** 上一版用 body[data-stage]
 *    换全局强调色，结果是「同样的蓝按钮在四个学段含义不同」。现在学段差异
 *    走密度与图形语言（见文件末尾），不走色相。
 *
 * 3. **任何靠颜色区分的东西都必须同时有文字。** 排序条的「比较/交换/已归位」、
 *    判题结论的「通过/未通过」都印出字来 —— 色盲学生和投影仪都靠得住。
 */
:root {
  /* ---------- 原始色彩 ----------
     字面色值只出现在这一段。组件一律引用下面的语义变量，
     所以调整配色只需要改这里。 */
  --ink: #15191A;      /* 石墨黑 */
  --paper: #F7F9F8;    /* 冷白 */
  --signal: #D8FB43;   /* 电光黄绿 —— 只表示「当前行动」 */
  --alarm: #FF5A5F;    /* 珊瑚红 —— 错误与关键提醒 */
  --lake: #16B8C5;     /* 湖蓝 —— 解释与辅助信息 */

  /* ---------- 语义层 ----------
     组件只引用这一层，不直接写字面色值。 */
  --page: var(--paper);
  --band: #FFFFFF;             /* 抬起的面（工具栏、面板） */
  --band-2: #EFF3F2;           /* 代码/输出等次级面 */
  --on-page: var(--ink);
  --on-band: var(--ink);
  --muted: #5C6A6E;            /* 次要文字（对冷白 4.6:1，达 AA） */
  --faint: #7D8A8D;            /* 更弱的说明文字 */
  --line: #DCE3E2;             /* 分隔线 */
  --grid: #E8EEED;             /* 网格底纹 */

  /* 强调色作**文字**用时必须换成深色版本 ——
     #D8FB43 / #FF5A5F / #16B8C5 直接写在冷白上对比度只有 1.3~2.8:1，
     远低于 AA 的 4.5:1。这三条是实测出来的，不要"顺手"改回原色。 */
  --action: var(--signal);         /* 作填充 */
  --action-ink: var(--ink);        /* 黄绿上的字必须是石墨黑（15:1） */
  --danger: var(--alarm);          /* 作填充/描边 */
  --danger-ink: #C42B31;           /* 作文字（5.3:1） */
  --explain: var(--lake);          /* 作填充/描边 */
  --explain-ink: #0B7A84;          /* 作文字（4.8:1） */
  --warn-ink: #8A5A0B;

  /* 形状：色带 + 分隔线取代套娃卡片与满屏胶囊 */
  --r-sm: 4px;
  --r-md: 10px;
  --topbar-h: 64px;
  --rail-w: 208px;
  --rail-w-min: 64px;          /* 960~1199px 折叠为图标栏 */
}

* { box-sizing: border-box; }

/* ★ `hidden` 属性在浏览器默认样式表里是 `[hidden] { display: none }`，
   **优先级低于任何类选择器** —— 只要给同一个元素写了 `display: flex`，
   hidden 就悄悄失效了。

   这个坑已经踩过一次：方向按钮盘（`.pad-editor { display: flex }`）
   配上 `hidden` 之后，**高年级学生也看得见按钮盘**。
   在全局兜一条，比在每个带 display 的类上各补一条可靠。 */
[hidden] { display: none !important; }

body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Microsoft YaHei",
               "Segoe UI", Roboto, sans-serif;
  background: var(--page);
  color: var(--on-page);
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  /* 桌面优先（v2 §2.3）。不设下限时三栏工作区会被挤到互相重叠，
     而这一版不为窄屏做重新排版 —— 与其挤坏，不如横向滚动。 */
  min-width: 960px;
}

.hidden { display: none !important; }

h1, h2, h3 { font-weight: 800; letter-spacing: -.01em; }

/* 键盘焦点必须看得见 —— 全站统一，不给个别组件留例外 */
:focus-visible {
  outline: 3px solid var(--explain);
  outline-offset: 2px;
}

/* 等宽：代码、输出、报错、编号 */
.mono, .code-input, .step-code, .stdout, .error-out, .peek-code {
  font-family: "SF Mono", Consolas, Monaco, "Cascadia Code", monospace;
}

/* ============================================================
   骨架 —— 左侧栏 + 顶栏 + 主内容
   ============================================================ */

/* 外壳由 static/ui.js 注入（见 K12UI.shell）。这里只定义它长什么样。
   为什么是侧边栏而不是原来的「顶栏 + 左栏」：参照的是对话式应用的形态 ——
   历史对话需要一个纵向滚动的区域，横排顶栏放不下。 */
.app-body { display: flex; align-items: stretch; min-height: 100vh; }

/* ---------- 左侧栏 ---------- */
.sidebar {
  width: var(--rail-w); flex-shrink: 0;
  display: flex; flex-direction: column;
  background: var(--band); border-right: 1px solid var(--line);
  padding: 12px 10px; gap: 10px;
  transition: width .22s ease;
}

.side-head { display: flex; align-items: center; gap: 6px; padding: 2px 4px; }
.side-brand {
  display: flex; align-items: center; gap: 9px; flex: 1; min-width: 0;
  text-decoration: none; color: var(--on-page);
  font-weight: 800; font-size: 15px; white-space: nowrap;
}
.brand-mark { flex-shrink: 0; display: block; }
.brand-mark .track { stroke: var(--line); }
.brand-mark .node  { fill: var(--ink); }
/* 轨道上「当前节点」用黄绿点亮 —— 它对应真实的课程位置，不是装饰 */
.brand-mark .node.on { fill: var(--signal); stroke: var(--ink); stroke-width: 1; }

.side-collapse {
  flex-shrink: 0; width: 30px; height: 30px;
  display: flex; align-items: center; justify-content: center;
  border: none; border-radius: var(--r-sm); background: transparent;
  color: var(--faint); cursor: pointer; transition: background .18s, color .18s;
}
.side-collapse:hover { background: var(--band-2); color: var(--on-page); }

/* 「开启新对话」是本页唯一的主行动 —— 黄绿填充 + 石墨黑字 */
.new-chat {
  display: flex; align-items: center; gap: 9px;
  padding: 11px 13px; border-radius: var(--r-md);
  background: var(--action); color: var(--action-ink);
  text-decoration: none; font-size: 14px; font-weight: 800;
  transition: filter .18s ease;
}
.new-chat:hover { filter: brightness(1.06); }
.new-chat .ico { width: 16px; height: 16px; flex-shrink: 0; }

.side-nav { display: flex; flex-direction: column; gap: 2px; }
.side-link {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 12px; border-radius: var(--r-md);
  border-left: 3px solid transparent;
  text-decoration: none; font-size: 14px; font-weight: 600;
  color: var(--muted); white-space: nowrap;
  transition: background .18s ease, color .18s ease, border-color .18s ease;
}
.side-link:hover { background: var(--band-2); color: var(--on-page); }
/* 当前模式的标记是**左侧色条 + 字重**，不只是变色 */
.side-link.on {
  background: var(--band-2); color: var(--on-page); font-weight: 800;
  border-left-color: var(--signal);
}
.side-ico { width: 16px; height: 16px; flex-shrink: 0; }

/* ---------- 历史对话 ---------- */
.side-history {
  flex: 1; min-height: 0; overflow-y: auto;
  border-top: 1px solid var(--grid); padding-top: 10px; margin-top: 2px;
}
.hist-group { margin-bottom: 12px; }
.hist-label {
  font-size: 11px; font-weight: 800; letter-spacing: .06em;
  color: var(--faint); padding: 0 12px 5px;
}
.hist-item {
  display: flex; align-items: center; gap: 6px;
  padding: 8px 8px 8px 12px; border-radius: var(--r-sm);
  border-left: 3px solid transparent;
  text-decoration: none; color: var(--muted); font-size: 13px;
  transition: background .16s ease, color .16s ease;
}
.hist-item:hover { background: var(--band-2); color: var(--on-page); }
.hist-item.on {
  background: var(--band-2); color: var(--on-page); font-weight: 700;
  border-left-color: var(--ink);
}
.hist-title { flex: 1; min-width: 0; overflow: hidden;
              text-overflow: ellipsis; white-space: nowrap; }
/* 删除按钮平时不占视觉重量，悬停才出现 */
.hist-del {
  flex-shrink: 0; width: 24px; height: 24px; padding: 0;
  display: none; align-items: center; justify-content: center;
  border: none; border-radius: var(--r-sm); background: transparent;
  color: var(--faint); cursor: pointer;
}
.hist-item:hover .hist-del { display: flex; }
.hist-del:hover { color: var(--danger-ink); background: var(--page); }
.hist-del .ico { width: 14px; height: 14px; }
.side-empty { font-size: 12px; color: var(--faint); padding: 6px 12px; margin: 0; }

/* ---------- 底部：个人信息（固定在侧边栏底，不随历史滚动） ---------- */
.side-foot {
  flex-shrink: 0; display: flex; align-items: center; gap: 9px;
  margin-top: auto; padding: 10px 8px 4px;
  border-top: 1px solid var(--grid); min-width: 0;
}
.side-foot .avatar {
  width: 28px; height: 28px; border-radius: 50%; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  background: var(--signal); color: var(--ink);
  font-size: 13px; font-weight: 800;
}
.side-foot #who {
  flex: 1; min-width: 0; font-size: 13px; font-weight: 700; color: var(--on-page);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.foot-out {
  flex-shrink: 0; padding: 5px 10px; font-family: inherit; font-size: 12px;
  border: 1px solid var(--line); border-radius: var(--r-sm);
  background: transparent; color: var(--muted); cursor: pointer;
  transition: border-color .18s, color .18s;
}
.foot-out:hover { border-color: var(--alarm); color: var(--danger-ink); }

/* ---------- 按学段收掉的导航项 ----------
   小学低年级不涉及编程，对应的入口收掉。规则写在 CSS 里而不是 JS 里，
   是因为外壳必须**同步**渲染（页面脚本要立刻取到 DOM），而学段要等
   /api/auth/me 回来才知道。先渲染、再用这一条收掉，两边都满足。
   对应 ui.js 的 STAGE_HIDDEN_NAV —— 改那边要一起改这里。 */
/* 「闯关」**不在这里** —— 低年级有一个能玩的玩法（点亮地砖），
   它只点方向按钮，不写代码也不拖积木。见 ui.js 里 STAGE_HIDDEN_NAV 的说明。 */
body[data-stage="primary_low"] .side-link[data-nav="playground"],
body[data-stage="primary_low"] .side-link[data-nav="works"] { display: none; }

/* ---------- 折叠态 ---------- */
body.side-collapsed .sidebar { width: var(--rail-w-min); }
body.side-collapsed .side-text { display: none; }
body.side-collapsed .side-link,
body.side-collapsed .new-chat,
body.side-collapsed .side-brand { justify-content: center; padding-left: 8px; padding-right: 8px; }
body.side-collapsed .side-history { display: none; }
/* ★ 收缩后**不能**把折叠按钮藏掉 —— 它是唯一的回头路。
   原来写的是 `display: none`，于是手动折叠一次就再也打不开：
   状态还写进了 localStorage，刷新都没用。
   现在改成"它顶替 logo 的位置"：图标条里首页 logo 本来也不承载信息
   （回首页有「对话」这一项），把那个位置让给展开按钮更值。 */
body.side-collapsed .side-brand { display: none; }
body.side-collapsed .side-head { justify-content: center; }
/* 展开时图标翻个方向 —— 按钮此刻的语义是"展开"，不是"折叠" */
body.side-collapsed .side-collapse .ico { transform: rotate(180deg); }
body.side-collapsed .side-foot { justify-content: center; padding-left: 4px; padding-right: 4px; }
body.side-collapsed .foot-out { display: none; }

/* ---------- 右侧栏 ---------- */
.app-col { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.topbar {
  height: var(--topbar-h); flex-shrink: 0;
  display: flex; align-items: center; gap: 16px;
  padding: 0 20px;
  background: var(--ink); color: var(--paper);
  position: sticky; top: 0; z-index: 20;
}
/* 顶栏上的页面自带控件（进度条 / 沙箱状态 / 步骤计数）都塞进这一格 */
.topbar-extra { flex: 1; display: flex; align-items: center; gap: 14px; min-width: 0; }

/* 页面没有顶栏控件时，宽屏下整条顶栏不显示 —— 空色带会把内容往下推，
   而对话页的欢迎语是垂直居中的。窄屏要留着它放汉堡按钮（见媒体查询）。 */
@media (min-width: 960px) {
  body.no-page-tools .topbar { display: none; }
}

.app-content { flex: 1; min-height: 0; display: flex; flex-direction: column; }

/* 汉堡按钮只在窄屏出现（见文件末尾的媒体查询） */
.nav-toggle {
  display: none; width: 34px; height: 34px; padding: 0;
  align-items: center; justify-content: center;
  background: transparent; border: 1px solid #3A4345; border-radius: var(--r-sm);
  color: var(--paper); cursor: pointer;
}
.nav-toggle:hover { border-color: var(--signal); color: var(--signal); }
.nav-toggle .ico { width: 18px; height: 18px; }
.side-scrim { display: none; }

/* 页面主体。ui.js 把页面的 <main> 整个搬进 .app-content，
   所以这里只管内边距 —— 页面自己不需要知道外壳的存在。 */
.work { flex: 1; min-width: 0; padding: 24px 28px 64px; }
/* 编程/排序是固定高度的三栏/两栏工作区，不跟着页面滚 */
.work.work-fixed { padding: 16px 20px 20px; overflow: hidden; min-height: 0; }

/* 固定工作区：整页不滚，只有内部面板滚 */
body.page-fixed { height: 100vh; overflow: hidden; }
body.page-fixed .app-body { height: 100vh; min-height: 0; }
body.page-fixed .app-col { min-height: 0; }
body.page-fixed .app-content { overflow: hidden; }

/* ============================================================
   通用组件
   ============================================================ */

/* 主按钮 —— 电光黄绿是「当前行动」的专用色。
   一个屏幕里只该有一个。 */
.primary-btn {
  display: inline-block; padding: 12px 26px; border: none;
  border-radius: var(--r-md); background: var(--action); color: var(--action-ink);
  font-size: 15px; font-weight: 800; font-family: inherit; cursor: pointer;
  transition: filter .18s ease;
}
.primary-btn:hover:not(:disabled) { filter: brightness(1.08); }
.primary-btn:disabled {
  /* 置灰时也要能读出是「禁用」而不是「另一种可用状态」 */
  background: var(--line); color: var(--faint); cursor: not-allowed;
}
.primary-btn.wide { width: 100%; }
.primary-btn.small { padding: 8px 16px; font-size: 13px; }

.ghost-btn {
  display: inline-block; padding: 11px 20px;
  border: 1px solid var(--line); border-radius: var(--r-md);
  background: transparent; color: var(--muted);
  font-size: 14px; font-family: inherit; cursor: pointer;
  transition: border-color .18s ease, color .18s ease;
}
.ghost-btn:hover:not(:disabled) { border-color: var(--explain); color: var(--explain-ink); }
.ghost-btn:disabled { opacity: .45; cursor: not-allowed; }
.ghost-btn.small { padding: 8px 14px; font-size: 13px; }
.ghost-btn.wide { width: 100%; }

/* 色带 —— 取代原来的 .card。
   不用阴影和圆角套娃：一整块面 + 一条细分隔线，信息密度更高也更好扫读。 */
.band {
  background: var(--band); border: 1px solid var(--line);
  border-radius: var(--r-md); padding: 24px 26px; margin-bottom: 18px;
}
.band-flat { border: none; border-bottom: 1px solid var(--line); border-radius: 0; }

/* 面板 —— 工作区里的分栏 */
.pane {
  background: var(--band); border: 1px solid var(--line);
  border-radius: var(--r-md); padding: 16px;
  display: flex; flex-direction: column; overflow: hidden; min-height: 0;
}
.pane-title {
  margin: 0 0 12px; font-size: 12px; font-weight: 800;
  letter-spacing: .08em; color: var(--faint); text-transform: uppercase;
}

/* 标签 —— 描边小签，不用粉彩填充 */
.pill {
  display: inline-block; padding: 3px 10px; border-radius: var(--r-sm);
  border: 1px solid var(--explain); color: var(--explain-ink);
  font-size: 12px; font-weight: 800; white-space: nowrap;
  background: transparent;
}
.pill-quiz   { border-color: var(--warn-ink); color: var(--warn-ink); }
.pill-result { border-color: var(--line); color: var(--muted); }

.muted { color: var(--muted); font-size: 13px; }
.footnote { color: var(--faint); font-size: 13px; margin-top: 20px; text-align: center; }
.hint { font-size: 12px; color: var(--faint); margin: 12px 0 0; text-align: center; }

/* 输入 */
input[type="text"], input[type="password"], textarea {
  font-family: inherit; color: var(--on-page);
  background: var(--band); border: 1px solid var(--line);
  border-radius: var(--r-md); outline: none;
  transition: border-color .18s ease;
}
input[type="text"]:focus, input[type="password"]:focus, textarea:focus {
  border-color: var(--signal);
}
input[type="range"] { accent-color: var(--ink); }

/* 状态条 —— 顶栏右侧的「沙箱：就绪」「第 12 步」这类 */
.status-chip {
  font-size: 12px; color: #B9C4C3; white-space: nowrap;
  border: 1px solid #3A4345; border-radius: var(--r-sm); padding: 4px 10px;
}
.status-chip[data-kind="ok"]  { color: var(--signal); border-color: var(--signal); }
.status-chip[data-kind="bad"] { color: #FF8A8E; border-color: #7A2F33; }
.sandbox-status { font-size: 12px; color: #B9C4C3; white-space: nowrap; }
.sandbox-status[data-kind="ok"]  { color: var(--signal); }
.sandbox-status[data-kind="bad"] { color: #FF8A8E; }

/* 顶栏用户区 */
.user-box {
  display: flex; align-items: center; gap: 10px;
  font-size: 13px; color: #B9C4C3; white-space: nowrap;
}
.user-box #who { font-weight: 700; color: var(--paper); }
/* 头像取昵称首字 —— 不采集真实照片（v2 §4.1），所以没有真头像可用 */
.user-box .avatar {
  width: 28px; height: 28px; border-radius: 50%; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  background: var(--signal); color: var(--ink);
  font-size: 13px; font-weight: 800;
}
.user-box button {
  padding: 6px 12px; border: 1px solid #3A4345; border-radius: var(--r-sm);
  background: transparent; color: #B9C4C3; font-size: 12px;
  font-family: inherit; cursor: pointer; transition: border-color .18s, color .18s;
}
.user-box button:hover { border-color: var(--signal); color: var(--signal); }

/* 顶栏进度（学习页） */
.progress-wrap { flex: 1; display: flex; align-items: center; gap: 12px; min-width: 0; }
.progress-track { flex: 1; height: 4px; background: #3A4345; border-radius: 2px; overflow: hidden; }
.progress-fill { height: 100%; width: 0%; background: var(--signal); transition: width .32s ease; }
.progress-text { font-size: 12px; color: #B9C4C3; white-space: nowrap; }
.streak { font-size: 13px; font-weight: 700; color: #B9C4C3; white-space: nowrap; }
.streak b { color: var(--signal); font-size: 15px; }

/* ============================================================
   状态语义（产品文档 §2 P2：全站统一「加载中/可操作/保存中/失败可重试/成功」）
   ============================================================ */

/* 保存状态 —— 编程页的草稿自动保存 */
.save-status { font-size: 12px; color: var(--faint); white-space: nowrap; }
.save-status[data-state="saving"] { color: var(--muted); }
.save-status[data-state="saved"]  { color: var(--explain-ink); }
/* 保存失败**绝不能**看起来像成功 */
.save-status[data-state="failed"] { color: var(--danger-ink); font-weight: 700; }
.save-status button {
  margin-left: 6px; padding: 2px 8px; font-size: 12px; font-family: inherit;
  border: 1px solid var(--danger); border-radius: var(--r-sm);
  background: transparent; color: var(--danger-ink); cursor: pointer;
}

/* 空态 / 加载中 / 失败可重试 —— 三态共用一块，避免各页自造 */
.state-box {
  padding: 22px 18px; text-align: center;
  border: 1px dashed var(--line); border-radius: var(--r-md);
  color: var(--muted); font-size: 13px;
}
.state-box[data-kind="failed"] { border-color: var(--alarm); color: var(--danger-ink); }
.state-box button {
  margin-top: 10px; padding: 7px 16px; font-family: inherit; font-size: 13px;
  border: 1px solid var(--line); border-radius: var(--r-md);
  background: var(--band); color: var(--on-page); cursor: pointer;
}
.state-box button:hover { border-color: var(--explain); }

/* 结论条 —— 三态各自**印出文字**，不靠颜色单独表意 */
.verdict {
  padding: 12px 16px; border-radius: var(--r-md); font-size: 14px; line-height: 1.6;
  margin-bottom: 14px; border: 1px solid var(--line);
  border-left-width: 4px; background: var(--band-2); color: var(--on-page);
}
.verdict.ok   { border-left-color: var(--explain); }
.verdict.bad  { border-left-color: var(--alarm); }
.verdict.info { border-left-color: var(--muted); }
.verdict-tag {
  display: inline-block; margin-right: 8px; padding: 1px 8px;
  border-radius: var(--r-sm); font-size: 12px; font-weight: 800;
  border: 1px solid currentColor;
}
.verdict.ok   .verdict-tag { color: var(--explain-ink); }
.verdict.bad  .verdict-tag { color: var(--danger-ink); }
.verdict.info .verdict-tag { color: var(--muted); }

/* ============================================================
   登录 / 注册  —— 无顶栏无左栏的全屏页
   ============================================================ */
body.auth { min-height: 100vh; display: grid; grid-template-columns: 55fr 45fr; }

.auth-side {
  background: var(--ink); color: var(--paper);
  padding: 56px 52px; display: flex; flex-direction: column; justify-content: center;
}
.auth-side h1 { font-size: 40px; margin: 0 0 10px; letter-spacing: -.02em; }
.auth-side .subtitle { color: #B9C4C3; margin: 0 0 36px; font-size: 15px; }

/* 课程轨道 —— 展示真实的「概念→练习→作品」三个节点状态。
   它是识别图形，同时说明产品怎么运作；不是装饰点。 */
.track-demo { display: flex; flex-direction: column; gap: 0; }
.track-step { display: flex; align-items: flex-start; gap: 14px; }
.track-rail { display: flex; flex-direction: column; align-items: center; flex-shrink: 0; }
.track-dot {
  width: 14px; height: 14px; border-radius: 50%; margin-top: 4px;
  border: 2px solid #5A6466; background: transparent;
}
.track-step.on .track-dot { border-color: var(--signal); background: var(--signal); }
.track-step.done .track-dot { border-color: var(--signal); background: var(--signal); }
.track-line { width: 2px; height: 34px; background: #3A4345; }
.track-step.done .track-line { background: var(--signal); }
.track-body { padding-bottom: 18px; }
.track-label { font-size: 11px; letter-spacing: .1em; color: var(--faint); font-weight: 700; }
.track-step.on .track-label { color: var(--signal); }
.track-title { font-size: 15px; font-weight: 700; }
.track-note { font-size: 13px; color: #B9C4C3; }

.auth-main { display: flex; align-items: center; justify-content: center; padding: 40px 32px; }
.auth-card { width: 100%; max-width: 460px; }

/* 四个状态分开：学生登录 / 学生注册 / 教师登录 / 教师注册。
   用分组标题而不是四个并列页签 —— 页签放不下四个还不说清谁是谁。 */
.auth-group { margin-bottom: 18px; }
.auth-group-title {
  font-size: 11px; font-weight: 800; letter-spacing: .1em;
  color: var(--faint); margin-bottom: 8px;
}
.tabs { display: flex; gap: 4px; border: 1px solid var(--line); border-radius: var(--r-md); padding: 3px; }
.tab {
  flex: 1; padding: 10px 8px; border: none; border-radius: var(--r-sm);
  background: transparent; color: var(--muted); font-size: 14px;
  font-family: inherit; font-weight: 600; cursor: pointer;
  transition: background .18s, color .18s;
}
.tab:hover { color: var(--on-page); }
/* 选中态靠**背景 + 字重**，不靠色相 */
.tab.picked { background: var(--ink); color: var(--paper); font-weight: 800; }

.field { display: block; margin-bottom: 14px; }
.field > span { display: block; font-size: 12px; color: var(--muted); margin-bottom: 6px; font-weight: 600; }
.field input[type="text"], .field input[type="password"] {
  width: 100%; padding: 11px 14px; font-size: 15px;
}

.stage-grid { display: grid; gap: 10px; grid-template-columns: repeat(2, 1fr); }
.stage-card {
  display: flex; flex-direction: column; gap: 3px; align-items: flex-start;
  padding: 12px 14px; border: 1px solid var(--line); border-radius: var(--r-md);
  background: var(--band); cursor: pointer; text-align: left;
  font-family: inherit; color: var(--on-page);
  transition: border-color .18s ease, background .18s ease;
}
.stage-card:hover { border-color: var(--explain); }
.stage-card.picked { border-color: var(--ink); border-left: 4px solid var(--signal); }
.stage-card.picked .stage-label::after {
  content: "已选"; margin-left: 8px; font-size: 11px; font-weight: 700;
  color: var(--explain-ink);
}
.stage-label { font-size: 15px; font-weight: 700; }
.stage-desc  { font-size: 12px; color: var(--muted); }

.consent {
  display: flex; align-items: flex-start; gap: 9px; margin: 6px 0 16px;
  font-size: 13px; color: var(--muted); line-height: 1.5; cursor: pointer;
}
.consent input { margin-top: 2px; width: 16px; height: 16px; flex-shrink: 0;
                 accent-color: var(--ink); cursor: pointer; }

.err {
  margin: 14px 0 0; padding: 11px 14px; border-radius: var(--r-md);
  border: 1px solid var(--alarm); border-left-width: 4px;
  color: var(--danger-ink); font-size: 14px; background: var(--band);
}

/* ---- 演示账号（一键进入，不需要密码） ----
   这是免密入口，所以刻意做得**和正常登录长得不一样**：
   湖蓝边 + 明确的「演示模式」标注，不让人误以为这是常规登录方式。 */
.demo-box {
  border: 1px solid var(--explain); border-left-width: 4px;
  border-radius: var(--r-md); padding: 12px 14px; margin-bottom: 18px;
  background: var(--band);
}
.demo-head { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; }
.demo-tag {
  font-size: 11px; font-weight: 800; letter-spacing: .06em;
  color: var(--explain-ink); border: 1px solid currentColor;
  border-radius: var(--r-sm); padding: 1px 7px;
}
.demo-note { font-size: 12px; color: var(--muted); }

.demo-list { display: grid; grid-template-columns: repeat(2, 1fr); gap: 7px; }
/* 教师那一条横跨整行 —— 它不是"第五个学生"，身份不一样 */
.demo-item.demo-teacher { grid-column: 1 / -1; }

.demo-item {
  display: flex; flex-direction: column; gap: 1px; align-items: flex-start;
  padding: 9px 12px; text-align: left; font-family: inherit; cursor: pointer;
  border: 1px solid var(--line); border-radius: var(--r-md);
  background: var(--paper); color: var(--on-page);
  transition: border-color .18s ease, background .18s ease;
}
.demo-item:hover:not(:disabled) { border-color: var(--ink); background: var(--band); }
.demo-item:disabled { opacity: .55; cursor: not-allowed; }
.demo-teacher { background: var(--band-2); }
.demo-label { font-size: 13px; font-weight: 800; }
.demo-sub { font-size: 11px; color: var(--faint); }

.demo-class {
  grid-column: 1 / -1; margin: 4px 0 0; font-size: 12px; color: var(--muted);
}
.demo-class b {
  font-family: "SF Mono", Consolas, Monaco, monospace;
  letter-spacing: .12em; color: var(--ink); font-size: 13px;
}

/* ============================================================
   学习页
   ============================================================ */
.learn-stage { max-width: 1080px; margin: 0 auto; }
/* 左：讲解与题目主线（约 68%）；右：课程轨道与掌握概览（约 32%） */
.learn-cols { display: grid; grid-template-columns: 68fr 32fr; gap: 22px; align-items: start; }
.learn-main { min-width: 0; }
.learn-aside { position: sticky; top: 84px; }

/* 首屏的超大课程编号 —— 文档要求「第 07 课 / 当前概念」一眼可见 */
.lesson-no {
  font-size: 60px; font-weight: 800; line-height: 1; letter-spacing: -.03em;
  color: var(--ink); font-variant-numeric: tabular-nums;
}
.lesson-no small { font-size: 20px; font-weight: 700; color: var(--faint); margin-left: 8px; }
.lesson-title { font-size: 24px; font-weight: 800; margin: 10px 0 0; }

.card-head { display: flex; align-items: center; gap: 12px; margin-bottom: 16px; }
.card-head h2 { margin: 0; font-size: 19px; font-weight: 800; }

.teach-body { font-size: 16px; color: var(--on-page); }

/* 讲解的 Markdown 结构（static/md.js 渲染）。
   white-space: pre-wrap 去掉了 —— 排版交给标签，不靠源文本里的换行；
   留着的话 `<pre>` 之外的地方会多出一堆空行。 */
.teach-body > *:first-child { margin-top: 0; }
.teach-body > *:last-child { margin-bottom: 0; }
.teach-body h2, .teach-body h3, .teach-body h4 {
  margin: 22px 0 8px; font-weight: 800; letter-spacing: -.01em; line-height: 1.35;
}
.teach-body h2 { font-size: 19px; }
.teach-body h3 { font-size: 17px; }
.teach-body h4 { font-size: 16px; color: var(--muted); }
.teach-body p { margin: 0 0 12px; line-height: 1.85; }
.teach-body ul { margin: 0 0 12px; padding-left: 20px; }
.teach-body li { margin: 0 0 6px; line-height: 1.8; }
.teach-body strong { font-weight: 800; }
.teach-body code {
  font-family: "SF Mono", Consolas, Monaco, monospace; font-size: .92em;
  background: var(--band-2); border-radius: 3px; padding: 1px 5px;
}
/* 代码块：横着可以滚，不换行 —— 代码折行以后缩进全乱，比看不全更难读 */
.teach-body .md-code {
  margin: 0 0 14px; padding: 14px 16px; overflow-x: auto;
  background: #0F1416; border-radius: var(--r-md);
}
.teach-body .md-code code {
  background: none; padding: 0; font-size: 13px; line-height: 1.7;
  color: #E6EDEB; white-space: pre;
}

/* 教师补充材料 —— 必须和 AI 生成内容一眼分得开（M5） */
.teacher-material {
  border: 1px solid var(--explain); border-left: 4px solid var(--explain);
  border-radius: var(--r-md); padding: 14px 16px; margin: 0 0 14px;
  background: var(--band);
}
.teacher-material .tm-head {
  font-size: 12px; font-weight: 800; letter-spacing: .06em;
  color: var(--explain-ink); margin-bottom: 6px;
}
.teacher-material .tm-body { font-size: 14px; white-space: pre-wrap; color: var(--on-page); }

/* ---- 题目 ---- */
.q-stem { font-size: 17px; font-weight: 700; margin: 0 0 16px; }

.opts { display: flex; flex-direction: column; gap: 8px; }
.opt {
  display: flex; align-items: center; gap: 10px;
  padding: 13px 16px; border: 1px solid var(--line); border-radius: var(--r-md);
  background: var(--band); cursor: pointer; font-size: 15px; font-family: inherit;
  color: var(--on-page); text-align: left;
  transition: border-color .18s ease, background .18s ease;
}
.opt:hover { border-color: var(--explain); }
.opt.selected { border-color: var(--ink); border-left: 4px solid var(--signal); font-weight: 700; }
/* 对错同时给描边 + 文字标记 */
.opt.correct { border-color: var(--explain); border-left: 4px solid var(--explain); }
.opt.correct::after {
  content: "正确"; margin-left: auto; font-size: 12px; font-weight: 800;
  color: var(--explain-ink);
}
.opt.wrong { border-color: var(--alarm); border-left: 4px solid var(--alarm); }
.opt.wrong::after {
  content: "再想想"; margin-left: auto; font-size: 12px; font-weight: 800;
  color: var(--danger-ink);
}

/* 排序题 */
.order-pool { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 14px; }
.order-slot {
  display: flex; flex-direction: column; gap: 6px; padding: 12px;
  border: 1px dashed var(--line); border-radius: var(--r-md); min-height: 56px;
}
.order-slot-title { font-size: 12px; color: var(--faint); margin-bottom: 2px; font-weight: 700; }
.chip {
  padding: 9px 14px; border-radius: var(--r-md); border: 1px solid var(--line);
  background: var(--band); cursor: pointer; font-size: 14px; font-family: inherit;
  color: var(--on-page); transition: border-color .18s;
}
.chip:hover { border-color: var(--explain); }
.chip.placed { border-color: var(--ink); border-left: 4px solid var(--signal); font-weight: 700; }
.chip .idx { display: inline-block; width: 20px; font-weight: 800; color: var(--explain-ink); }

/* 连线题 */
.match-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.match-col { display: flex; flex-direction: column; gap: 8px; }
.match-col-title { font-size: 12px; color: var(--faint); font-weight: 700; }
.match-item {
  padding: 11px 14px; border: 1px solid var(--line); border-radius: var(--r-md);
  background: var(--band); cursor: pointer; font-size: 14px; font-family: inherit;
  color: var(--on-page); text-align: left; transition: border-color .18s;
}
.match-item:hover { border-color: var(--explain); }
.match-item.active { border-color: var(--ink); border-left: 4px solid var(--signal); }
.match-item.paired { border-color: var(--explain); }
.match-item .badge {
  display: inline-block; min-width: 20px; height: 20px; line-height: 20px; text-align: center;
  border-radius: var(--r-sm); background: var(--ink); color: var(--paper);
  font-size: 12px; font-weight: 800; margin-right: 8px;
}

/* 填空 / 代码题 */
.fill-input { width: 100%; padding: 13px 15px; font-size: 16px; }
.code-hint { font-size: 13px; color: var(--faint); margin-bottom: 8px; }

.quiz-actions { display: flex; justify-content: space-between; align-items: center; margin-top: 22px; }

/* ---- 结果 ---- */
.score-row { display: flex; align-items: center; gap: 22px; margin-bottom: 16px; }
.score-ring {
  width: 92px; height: 92px; border-radius: 50%; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  font-size: 28px; font-weight: 800;
  border: 6px solid var(--line); background: var(--band); color: var(--on-page);
}
/* 环的颜色+旁边的文字一起表意，环自己不说结论 */
.score-ring.good { border-color: var(--explain); color: var(--explain-ink); }
.score-ring.mid  { border-color: var(--signal); color: var(--warn-ink); }
.score-ring.low  { border-color: var(--alarm); color: var(--danger-ink); }
.score-detail { font-size: 15px; color: var(--muted); }
.score-band { font-size: 15px; font-weight: 800; }
.score-band.good { color: var(--explain-ink); }
.score-band.mid  { color: var(--warn-ink); }
.score-band.low  { color: var(--danger-ink); }
.feedback-body { font-size: 16px; white-space: pre-wrap; }

.detail-list { margin-top: 16px; display: flex; flex-direction: column; gap: 6px; }
.detail-row {
  display: flex; align-items: center; gap: 10px; font-size: 14px;
  padding: 9px 12px; border-radius: var(--r-sm); border: 1px solid var(--line);
}
.detail-row .mark { font-size: 12px; font-weight: 800; flex-shrink: 0; }
.detail-row .mark.ok  { color: var(--explain-ink); }
.detail-row .mark.bad { color: var(--danger-ink); }
.detail-row .stem { color: var(--muted); }

/* ---- 右侧课程轨道 ---- */
.lesson-track { display: flex; flex-direction: column; gap: 3px; }
.lt-item {
  display: flex; align-items: center; gap: 10px;
  font-size: 13px; color: var(--muted); padding: 5px 0;
}
.lt-dot {
  width: 9px; height: 9px; border-radius: 50%; flex-shrink: 0;
  border: 2px solid var(--line); background: transparent;
}
.lt-item.done .lt-dot { border-color: var(--ink); background: var(--ink); }
.lt-item.now  { color: var(--on-page); font-weight: 800; }
.lt-item.now .lt-dot { border-color: var(--ink); background: var(--signal); }
.lt-seq { font-variant-numeric: tabular-nums; color: var(--faint); width: 22px; flex-shrink: 0; }

/* ---- 课后练习 ----
   排序动画与编程题按知识点放出来，做成卡片网格。
   未解锁的与已解锁的**同一种版式**，只把灰条那组降调 ——
   看得到、点不动，这本身就是"还有东西在前面"的信号。 */
.practice-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 10px; margin-top: 4px;
}
.practice-grid + .practice-grid { margin-top: 12px; padding-top: 12px;
                                  border-top: 1px dashed var(--line); }
.practice-item {
  display: flex; flex-direction: column; gap: 2px;
  padding: 11px 13px; border-radius: var(--r-md);
  border: 1px solid var(--line); border-left: 4px solid var(--signal);
  background: var(--band); text-decoration: none; color: var(--on-page);
  transition: border-color .18s ease, background .18s ease;
}
.practice-item:hover { border-color: var(--ink); background: var(--band-2); }
.practice-kind {
  display: flex; align-items: center; gap: 6px;
  font-size: 11px; font-weight: 700;
  color: var(--explain-ink);
}
/* 「本课」标记 —— 让学生一眼看出这几道是刚学完那一课的 */
.practice-now {
  font-size: 10px; font-weight: 800; letter-spacing: .04em;
  color: var(--ink); background: var(--signal);
  border-radius: var(--r-sm); padding: 0 5px;
}
.practice-title { font-size: 14px; font-weight: 700; }
.practice-sub { font-size: 12px; color: var(--faint); }

/* 未解锁：降调 + 左侧换成灰条，且**不响应悬停**（它不可点） */
.practice-item.locked {
  border-left-color: var(--line); background: var(--band-2);
  cursor: not-allowed;
}
.practice-item.locked:hover { border-color: var(--line); background: var(--band-2); }
.practice-item.locked .practice-title { color: var(--muted); font-weight: 600; }

/* 学 → 编 → 排 的跳转出口（M5） */
.next-actions { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 16px; }
.next-actions a {
  text-decoration: none; padding: 10px 16px; border-radius: var(--r-md);
  border: 1px solid var(--line); color: var(--on-page); font-size: 14px; font-weight: 700;
  background: var(--band); transition: border-color .18s;
}
.next-actions a:hover { border-color: var(--explain); color: var(--explain-ink); }

/* 加入班级（M3.4） */
.class-join { display: flex; gap: 8px; align-items: center; }
.class-join input { flex: 1; padding: 9px 12px; font-size: 14px; text-transform: uppercase; }
.class-chip {
  font-size: 12px; color: var(--muted);
  border: 1px solid var(--line); border-radius: var(--r-sm); padding: 3px 9px;
}
.class-code {
  font-family: "SF Mono", Consolas, Monaco, monospace;
  font-size: 22px; font-weight: 800; letter-spacing: .16em;
  color: var(--ink); background: var(--band-2); border-radius: var(--r-sm);
  padding: 8px 14px; display: inline-block;
}

/* ============================================================
   编程模式
   ============================================================ */
.code-layout {
  flex: 1; display: grid; grid-template-columns: 260px minmax(0, 1fr) 340px;
  gap: 12px; min-height: 0;
}

/* ---- 案例列表 ---- */
.case-list { display: flex; flex-direction: column; gap: 5px; overflow-y: auto; flex-shrink: 0; }
.case-item {
  display: flex; flex-direction: column; gap: 2px; padding: 10px 12px;
  border: 1px solid var(--line); border-radius: var(--r-md);
  background: var(--band); cursor: pointer; text-align: left;
  font-family: inherit; color: var(--on-page); transition: border-color .18s;
}
.case-item:hover { border-color: var(--explain); }
.case-item.picked { border-color: var(--ink); border-left: 4px solid var(--signal); }
.case-name  { font-size: 14px; font-weight: 700; }
.case-point { font-size: 12px; color: var(--faint); }

.case-detail { margin-top: 14px; overflow-y: auto; }
.case-detail h3 { margin: 0 0 6px; font-size: 16px; }
.case-brief { font-size: 13px; color: var(--muted); line-height: 1.65; margin: 0 0 12px; }
.steps-title { font-size: 11px; font-weight: 800; letter-spacing: .08em;
               color: var(--faint); margin-bottom: 6px; }
.step-row { margin-bottom: 9px; }
/* 分步示范是**只读展示**，不做可点击的视觉暗示（见 code.js 的注释） */
.step-code {
  margin: 0; padding: 9px 11px; background: var(--band-2);
  border: 1px solid var(--line); border-left: 3px solid var(--muted);
  border-radius: var(--r-sm); font-size: 12px; line-height: 1.5;
  white-space: pre-wrap; color: var(--on-page);
}
/* 积木模式的示范是**大白话**，用正文字体 */
.step-text {
  padding: 9px 11px; background: var(--band-2);
  border: 1px solid var(--line); border-left: 3px solid var(--explain);
  border-radius: var(--r-sm); font-size: 13px; line-height: 1.7; color: var(--on-page);
}
.case-hint { font-size: 13px; color: var(--warn-ink); margin: 12px 0 0; line-height: 1.6; }

/* ---- 编辑器 ---- */
.editor-head {
  display: flex; align-items: center; gap: 10px;
  margin-bottom: 10px; flex-wrap: wrap;
}
.editor-title { font-size: 14px; font-weight: 800; margin: 0; }
.editor-actions { display: flex; gap: 6px; align-items: center; margin-left: auto; }

/* 入口切换：分段控件，选中靠底色与字重 */
.entry-tabs { display: flex; border: 1px solid var(--line); border-radius: var(--r-md); padding: 2px; }
.entry-tab {
  padding: 6px 14px; border: none; border-radius: var(--r-sm);
  background: transparent; color: var(--muted); font-size: 13px;
  font-family: inherit; font-weight: 600; cursor: pointer; transition: background .18s, color .18s;
}
.entry-tab:hover:not(:disabled) { color: var(--on-page); }
.entry-tab.picked { background: var(--ink); color: var(--paper); font-weight: 800; }
/* 这道题积木做不了（要用到列表）—— 置灰而不是隐藏，
   隐藏会让学生以为积木功能坏了 */
.entry-tab:disabled { opacity: .4; cursor: not-allowed; text-decoration: line-through; }

.code-input {
  flex: 1; width: 100%; min-height: 240px; padding: 14px 16px;
  border: 1px solid var(--line); border-radius: var(--r-md); outline: none; resize: none;
  font-size: 14px; line-height: 1.75; tab-size: 4;
  background: var(--band-2); color: var(--on-page); transition: border-color .18s;
}
.code-input:focus { border-color: var(--signal); }
.editor-note { font-size: 12px; color: var(--faint); margin: 8px 0 0; }

/* 积木 ↔ 代码 只读对照（M3.1）。
   以前这里直接把生成的代码写进文本编辑器，覆盖学生已经写的东西。 */
.peek-panel {
  border: 1px solid var(--explain); border-radius: var(--r-md);
  padding: 12px 14px; margin-bottom: 12px; background: var(--band);
}
.peek-head {
  display: flex; align-items: center; gap: 10px; margin-bottom: 8px;
  font-size: 12px; font-weight: 800; color: var(--explain-ink);
}
.peek-head .editor-actions { margin-left: auto; }
.peek-code {
  margin: 0; padding: 10px 12px; background: var(--band-2);
  border: 1px solid var(--line); border-radius: var(--r-sm);
  font-size: 12px; line-height: 1.6; white-space: pre-wrap;
  max-height: 220px; overflow: auto; color: var(--on-page);
}

/* Blockly 画布 —— 主题由 blockly-setup.js 的 k12 theme 提供，
   这里的边框与圆角要与之对齐，否则会看到"画布浮在框里" */
.blockly-div {
  flex: 1; width: 100%; min-height: 320px;
  border: 1px solid var(--line); border-radius: var(--r-md); overflow: hidden;
}

/* ---- 输出 ---- */
.pane-output { overflow-y: auto; }
.output-block { margin-bottom: 14px; }
.output-label {
  font-size: 11px; font-weight: 800; letter-spacing: .08em;
  color: var(--faint); margin-bottom: 5px;
}
.canvas-box {
  border: 1px dashed var(--line); border-radius: var(--r-md); min-height: 190px;
  display: flex; align-items: center; justify-content: center;
  background: var(--band-2); overflow: hidden;
}
.canvas-box svg { max-width: 100%; height: auto; }
.canvas-box .placeholder { color: var(--faint); font-size: 13px; }
.stdout, .error-out {
  margin: 0; padding: 11px 13px; border-radius: var(--r-sm); font-size: 13px;
  line-height: 1.6; white-space: pre-wrap; word-break: break-word;
}
.stdout { background: var(--band-2); border: 1px solid var(--line); }
.error-out { background: var(--band); border: 1px solid var(--alarm);
             border-left-width: 4px; color: var(--danger-ink); }

/* 老师的提示 —— 湖蓝。它是「有人在旁边说话」，不是对错判定，
   所以既不用判题的绿/红，也不用黄绿的"行动色" */
.tutor-text {
  padding: 12px 14px; border-radius: var(--r-md); font-size: 14px; line-height: 1.75;
  background: var(--band); border: 1px solid var(--explain);
  border-left-width: 4px; color: var(--on-page); white-space: pre-wrap;
}

/* AI 辅导入口：内联一行，不占新页面 */
.assist-row { display: flex; gap: 8px; align-items: center; margin-bottom: 10px; }
.assist-row input { flex: 1; padding: 9px 13px; font-size: 13px; }
.assist-row .primary-btn.small, .assist-row .ghost-btn.small { padding: 8px 14px; }

/* ============================================================
   排序动画
   ============================================================ */
.sort-layout {
  flex: 1; display: grid; grid-template-columns: 280px minmax(0, 1fr);
  gap: 12px; min-height: 0;
}

.algo-list { display: flex; flex-direction: column; gap: 5px; }
.algo-item {
  display: flex; flex-direction: column; gap: 2px; padding: 10px 12px;
  border: 1px solid var(--line); border-radius: var(--r-md);
  background: var(--band); cursor: pointer; text-align: left;
  font-family: inherit; color: var(--on-page); transition: border-color .18s;
}
.algo-item:hover { border-color: var(--explain); }
.algo-item.picked { border-color: var(--ink); border-left: 4px solid var(--signal); }
.algo-name { font-size: 14px; font-weight: 700; }
.algo-meta { font-size: 12px; color: var(--faint); }

.algo-detail { margin: 14px 0; padding: 12px 14px; background: var(--band-2);
               border-radius: var(--r-md); border-left: 3px solid var(--explain); }
.algo-intro { margin: 0 0 6px; font-size: 13px; line-height: 1.7; }
.algo-complexity { margin: 0; font-size: 12px; color: var(--faint); }

.sort-size { margin-bottom: 12px; font-size: 13px; color: var(--muted); }
.sort-size input[type="range"] { width: 100%; margin-top: 6px; }

/* ---- 动画区 ---- */
.sort-stage { display: flex; flex-direction: column; }

/* 大号算法名 + 当前步骤数 —— 演示时的视觉冲击力放在这里（文档 §3） */
.sort-head { display: flex; align-items: baseline; gap: 14px; margin-bottom: 10px; }
.sort-algo-name { font-size: 30px; font-weight: 800; letter-spacing: -.02em; margin: 0; }
.sort-step-no {
  font-size: 15px; font-weight: 700; color: var(--muted);
  font-variant-numeric: tabular-nums;
}

/* 图例 —— 颜色必须配文字（色盲 + 投影仪） */
.bar-legend { display: flex; gap: 16px; margin-bottom: 8px; font-size: 12px; color: var(--muted); }
.bar-legend span { display: flex; align-items: center; gap: 6px; }
.bar-legend i { width: 12px; height: 12px; border-radius: 3px; display: inline-block; }
.bar-legend .lg-compare i { background: var(--lake); }
.bar-legend .lg-swap    i { background: var(--alarm); }
.bar-legend .lg-settled i { background: var(--ink); }

.bars {
  flex: 1; min-height: 240px; display: flex; align-items: flex-end; gap: 5px;
  padding: 26px 14px 14px; background: var(--band-2);
  border: 1px solid var(--line); border-radius: var(--r-md);
}
.bar {
  flex: 1; position: relative; border-radius: 3px 3px 0 0;
  background: #A9B6B5;
  transition: height .18s ease, background .18s ease;
  display: flex; justify-content: center;
}
.bar > span {
  position: absolute; top: -22px; font-size: 12px; color: var(--on-page);
  font-weight: 700; font-variant-numeric: tabular-nums;
}
/* 三种状态：湖蓝=正在比较，珊瑚红=正在交换，石墨=已归位。
   每个活跃条上还要印出「比较」「交换」字样（.bar-tag），不让颜色单独表意。 */
.bar.comparing { background: var(--lake); }
.bar.swapping  { background: var(--alarm); }
.bar.settled   { background: var(--ink); }

/* 查找类算法（二分查找）的三个标记。
   **压暗而不是删掉**被排除的那一半 —— 学生要看得见"刚才那一半去哪了"，
   那正是二分查找省下来的东西。删掉就成了"数字凭空消失"。 */
.bar.cut { opacity: .34; }
.bar.target {
  background: var(--explain);
  box-shadow: inset 0 0 0 3px var(--paper);
}
.bar.found { background: var(--signal); }
.bar.found > span { color: var(--ink); }
.bar-legend .lg-target i { background: var(--explain); }
.bar-legend .lg-cut    i { background: #A9B6B5; opacity: .34; }
.bar-legend .lg-found  i { background: var(--signal); }

/* 「要找的是 N」—— 常驻在标题旁边，翻每一步时不用回头看解说 */
.sort-target {
  font-size: 13px; font-weight: 700; color: var(--explain-ink);
  background: rgba(22, 184, 197, .12); border-radius: 999px; padding: 3px 10px;
}
.bar-tag {
  position: absolute; bottom: 4px; left: 50%; transform: translateX(-50%);
  font-size: 10px; font-weight: 800; color: var(--paper);
  background: rgba(21, 25, 26, .72); border-radius: 3px; padding: 0 4px;
  white-space: nowrap; pointer-events: none;
}

/* 步骤解说 —— 紧贴动画，不埋到页面底部 */
.step-note {
  margin: 12px 0; padding: 12px 15px; border-radius: var(--r-md); font-size: 15px;
  line-height: 1.7; background: var(--band); color: var(--on-page);
  border: 1px solid var(--line); border-left: 4px solid var(--explain); min-height: 46px;
}

/* 播放控件固定在一条工具栏里，尺寸不随状态变化 */
.sort-controls { display: flex; gap: 8px; align-items: center; }
.sort-controls .ghost-btn, .sort-controls .primary-btn { margin: 0; padding: 10px 20px; }

.speed-row { display: flex; align-items: center; gap: 12px; margin-top: 12px;
             font-size: 13px; color: var(--muted); }
.speed-row input[type="range"] { flex: 1; }

.sort-hint { margin: 14px 0 0; font-size: 12px; color: var(--faint); line-height: 1.7; }

/* ============================================================
   自由编程（/playground）
   ============================================================ */
.play-layout {
  flex: 1; display: grid; grid-template-columns: 250px minmax(0, 1fr) 340px;
  gap: 12px; min-height: 0;
}

.snippet-list { display: flex; flex-direction: column; gap: 8px; overflow-y: auto; }
.snippet-item {
  display: flex; flex-direction: column; gap: 5px; align-items: stretch;
  padding: 10px 12px; text-align: left; font-family: inherit; cursor: pointer;
  border: 1px solid var(--line); border-radius: var(--r-md);
  background: var(--band); color: var(--on-page);
  transition: border-color .18s ease, background .18s ease;
}
.snippet-item:hover { border-color: var(--explain); background: var(--band-2); }
.snippet-name { font-size: 13px; font-weight: 700; }
/* 示例代码只露前三行 —— 整段摊开会把列表拉得很长 */
.snippet-code {
  font-family: "SF Mono", Consolas, Monaco, monospace;
  font-size: 11px; line-height: 1.5; color: var(--faint);
  white-space: pre; overflow: hidden; text-overflow: ellipsis; margin: 0;
}

/* ============================================================
   对话首页
   ============================================================ */
.chat-work { display: flex; flex-direction: column; padding: 0; min-height: 0; }

/* 消息区可滚，输入框固定在下 —— 与主流对话产品一致。
   长对话往下翻的时候，输入框不该跟着跑掉。 */
.chat-stage {
  flex: 1; min-height: 0; overflow-y: auto;
  display: flex; flex-direction: column;
  padding: 28px 24px 8px;
}

/* ---- 空状态 ---- */
.chat-welcome {
  flex: 1; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 14px;
  text-align: center; padding-bottom: 40px;
}
.welcome-mark { opacity: .9; }
.welcome-title { font-size: 30px; margin: 6px 0 0; letter-spacing: -.02em; }
.welcome-sub { color: var(--muted); font-size: 14px; margin: 0; }

/* ---- 消息 ---- */
.chat-thread { display: flex; flex-direction: column; gap: 22px; max-width: 780px;
               width: 100%; margin: 0 auto; }
.msg { display: flex; gap: 12px; align-items: flex-start; }
/* 学生的消息靠右，且不要头像 —— 自己的话不需要标是谁说的 */
.msg-user { flex-direction: row-reverse; }

.msg-avatar {
  flex-shrink: 0; width: 28px; height: 28px; border-radius: var(--r-sm);
  display: flex; align-items: center; justify-content: center;
  background: var(--band); border: 1px solid var(--line); color: var(--ink);
  overflow: hidden;   /* 图比框略大时不至于溢出圆角 */
}
/* 头像图。源图是 96px 的透明位图，缩到 28px 仍清楚；
   透明底让方框自己的底色透出来，不会出现一个白方块。 */
.avatar-img { width: 100%; height: 100%; object-fit: contain; display: block; }
/* 占位的思考态不给图标，免得和真回答的气泡混淆 */
#thinking-row .msg-avatar { border-style: dashed; }

.msg-body { min-width: 0; max-width: 88%; }
.msg-text {
  font-size: 15px; line-height: 1.75; white-space: pre-wrap; word-break: break-word;
}
/* 学生的话放在浅色气泡里，与 AI 的正文拉开层次 */
.msg-user .msg-text {
  background: var(--ink); color: var(--paper);
  border-radius: var(--r-md); padding: 10px 14px;
  font-size: 14px;
}
.msg-text.thinking { color: var(--faint); font-style: normal; }
/* 光标闪动 —— 只在这里用动效，而且 prefers-reduced-motion 会把它关掉 */
.msg-text.thinking::after {
  content: ""; display: inline-block; width: 2px; height: 1em;
  background: var(--faint); margin-left: 3px; vertical-align: -2px;
  animation: blink 1s steps(2, start) infinite;
}
@keyframes blink { to { visibility: hidden; } }

/* 超纲补充卡片 —— 湖蓝边，与正文明确分开。
   它讲的是"你还没学过但刚才提到了"的东西，不是回答本身。 */
.msg-supplement {
  margin-top: 10px; padding: 12px 14px;
  border: 1px solid var(--explain); border-left-width: 4px;
  border-radius: var(--r-md); background: var(--band);
  font-size: 13px; line-height: 1.75; white-space: pre-wrap;
  color: var(--on-page);
}

/* 绘本 —— 一次一页，可翻。
   和补充卡片一样嵌在消息里，但视觉上自成一个"东西"：
   它是学生要来的作品，不是回答的一部分，所以有自己的边框和标题。 */
.msg-book {
  margin-top: 12px; padding: 14px;
  border: 1px solid var(--line); border-radius: var(--r-md);
  background: var(--band); outline: none;
}
/* 键盘翻页时才画焦点圈 —— 鼠标点进来也画一圈会显得没被点中 */
.msg-book:focus-visible { border-color: var(--ink); }

.book-title {
  font-size: 16px; font-weight: 800; color: var(--ink);
  margin-bottom: 10px; letter-spacing: -.01em;
}
.book-stage { display: flex; flex-direction: column; gap: 10px; }

/* 限宽而不是铺满：一页绘本铺到整个聊天宽度会像横幅，
   而它是"一页纸"。图缺席时这块自然不占位（见 chat.js 的 paint）。 */
.book-img {
  width: 100%; max-width: 420px; height: auto;
  border-radius: var(--r-md); display: block;
  background: var(--band-2);
}
.book-text { font-size: 15px; line-height: 1.8; color: var(--on-page); }

.book-nav {
  display: flex; align-items: center; gap: 10px;
  margin-top: 12px; padding-top: 10px;
  border-top: 1px solid var(--line);
}
.book-pos {
  font-size: 13px; color: var(--muted);
  font-variant-numeric: tabular-nums;   /* 翻页时数字不跳宽 */
}

/* ---- 输入区 ---- */
.composer-wrap {
  flex-shrink: 0; padding: 8px 24px 16px;
  max-width: 828px; width: 100%; margin: 0 auto;
}
.composer {
  display: flex; align-items: flex-end; gap: 10px;
  padding: 10px 10px 10px 16px;
  background: var(--band); border: 1px solid var(--line);
  border-radius: 18px;
  /* 悬浮感靠阴影而不是嵌套卡片 —— 它是浮在内容之上的一层 */
  box-shadow: 0 6px 22px rgba(21, 25, 26, .07);
  transition: border-color .18s ease, box-shadow .18s ease;
}
.composer:focus-within {
  border-color: var(--signal);
  box-shadow: 0 8px 26px rgba(21, 25, 26, .11);
}
.composer textarea {
  flex: 1; min-width: 0; border: none; background: transparent;
  resize: none; outline: none; font-family: inherit;
  font-size: 15px; line-height: 1.6; color: var(--on-page);
  max-height: 160px; padding: 6px 0;
}
.composer-actions { display: flex; align-items: center; gap: 6px; flex-shrink: 0; }

.icon-btn {
  width: 34px; height: 34px; padding: 0;
  display: flex; align-items: center; justify-content: center;
  border: none; border-radius: 50%; background: transparent;
  color: var(--faint); cursor: pointer; transition: background .18s, color .18s;
}
.icon-btn svg { width: 17px; height: 17px; }
.icon-btn:hover:not(:disabled) { background: var(--band-2); color: var(--on-page); }
.icon-btn:disabled { opacity: .5; cursor: not-allowed; }

/* 发送是本页的主行动 —— 黄绿圆钮 + 石墨黑箭头。
   （参考稿里是蓝色，这里按项目配色换成黄绿。） */
.send-btn {
  width: 34px; height: 34px; padding: 0; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  border: none; border-radius: 50%;
  background: var(--action); color: var(--action-ink);
  cursor: pointer; transition: filter .18s ease, background .18s ease;
}
.send-btn svg { width: 17px; height: 17px; }
.send-btn:hover:not(:disabled) { filter: brightness(1.08); }
.send-btn:disabled { background: var(--line); color: var(--faint); cursor: not-allowed; }

.composer-hint { font-size: 11px; color: var(--faint); text-align: center; margin: 8px 0 0; }

/* ============================================================
   教师工作台
   ============================================================ */
.teach-tabs { display: flex; gap: 4px; border: 1px solid var(--line);
              border-radius: var(--r-md); padding: 3px; margin-bottom: 18px; }
.teach-tab {
  padding: 9px 18px; border: none; border-radius: var(--r-sm);
  background: transparent; color: var(--muted); font-size: 14px; font-weight: 600;
  font-family: inherit; cursor: pointer; transition: background .18s, color .18s;
}
.teach-tab.picked { background: var(--ink); color: var(--paper); font-weight: 800; }

.class-list { display: flex; flex-direction: column; gap: 10px; }
.class-row {
  display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
  padding: 14px 16px; border: 1px solid var(--line); border-radius: var(--r-md);
  background: var(--band);
}
.class-row .class-name { font-size: 15px; font-weight: 700; min-width: 120px; }
.class-row .class-count { font-size: 13px; color: var(--muted); }
.class-row .class-actions { margin-left: auto; display: flex; gap: 8px; }

/* 表格 —— 材料页用，密集信息比卡片好扫读 */
.data-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.data-table th {
  text-align: left; font-size: 11px; font-weight: 800; letter-spacing: .06em;
  color: var(--faint); padding: 8px 10px; border-bottom: 1px solid var(--line);
}
.data-table td { padding: 11px 10px; border-bottom: 1px solid var(--grid); vertical-align: top; }
.data-table tr:hover td { background: var(--band-2); }
.data-table .row-actions { display: flex; gap: 6px; }

/* 状态徽标 —— 解析状态/推荐状态 */
.badge-state {
  display: inline-block; padding: 2px 9px; border-radius: var(--r-sm);
  border: 1px solid currentColor; font-size: 12px; font-weight: 700; white-space: nowrap;
}
.badge-state.pending     { color: var(--muted); }
.badge-state.ready       { color: var(--explain-ink); }
.badge-state.recommended { color: var(--ink); border-color: var(--ink); }
.badge-state.failed      { color: var(--danger-ink); }

.material-form { display: grid; gap: 12px; max-width: 720px; }
.material-form textarea {
  width: 100%; min-height: 160px; padding: 12px 14px; font-size: 14px;
  line-height: 1.7; resize: vertical;
}
.material-form input[type="text"], .material-form select {
  width: 100%; padding: 10px 13px; font-size: 14px;
  font-family: inherit; color: var(--on-page); background: var(--band);
  border: 1px solid var(--line); border-radius: var(--r-md); outline: none;
}
.material-form select:focus, .material-form input[type="text"]:focus { border-color: var(--signal); }
.form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }

/* ============================================================
   学段差异（文档 §3「学段差异与状态」）
   —— 走**信息密度与图形语言**，不换整页色相
   ============================================================ */
/* 小学低年级：更大字号与点击区，单屏一件主任务 */
body[data-stage="primary_low"] .band { padding: 30px 32px; }
body[data-stage="primary_low"] .opt  { padding: 18px 20px; font-size: 17px; }
body[data-stage="primary_low"] .primary-btn { padding: 16px 34px; font-size: 17px; }
body[data-stage="primary_low"] .q-stem { font-size: 20px; }
body[data-stage="primary_low"] .chip { padding: 13px 18px; font-size: 16px; }

/* 小学高年级：分步示范与对应代码并排，每次只突出一个新概念 */
body[data-stage="primary_high"] .step-code { font-size: 13px; }

/* 初中：结果面板同时给文字、图形和判题依据 */
body[data-stage="junior"] .learn-stage { max-width: 1120px; }

/* 高中：类 IDE 密度，代码区优先，减少插画、增强黑白对比 */
body[data-stage="senior"] { --muted: #4A5759; }
body[data-stage="senior"] .band { padding: 20px 22px; }
body[data-stage="senior"] .code-input { font-size: 13.5px; line-height: 1.65; }
body[data-stage="senior"] .sort-algo-name { font-size: 24px; }

/* ============================================================
   窄屏
   ============================================================ */

/* 960~1199px：左侧栏收成图标条，三栏工作区相应让出宽度 */
@media (max-width: 1199px) {
  :root { --rail-w: var(--rail-w-min); }
  .side-text, .hist-title { display: none; }
  .side-link, .new-chat, .side-brand { justify-content: center; padding-left: 8px; padding-right: 8px; }
  .side-history { display: none; }
  .side-collapse { display: none; }
  .code-layout { grid-template-columns: 220px minmax(0, 1fr) 300px; }
  .learn-cols { grid-template-columns: 1fr; }
  .learn-aside { position: static; }
}

/* 小于 960px：左侧栏改为覆盖层，由顶栏的汉堡按钮唤出。
   注意 body 上有 min-width: 960px —— 这里**放开那个下限**，
   否则窄屏下页面会横向溢出、汉堡按钮根本够不着。 */
@media (max-width: 959px) {
  body { min-width: 0; }
  .sidebar {
    position: fixed; top: 0; left: 0; bottom: 0; z-index: 40;
    width: 260px; transform: translateX(-100%);
    transition: transform .22s ease;
    box-shadow: 0 0 24px rgba(21, 25, 26, .18);
  }
  body.side-open .sidebar { transform: none; }
  .side-text, .hist-title { display: inline; }
  .side-link, .new-chat, .side-brand { justify-content: flex-start; padding-left: 12px; }
  .side-history { display: block; }
  .side-collapse { display: none; }

  .side-scrim {
    display: block; position: fixed; inset: 0; z-index: 30;
    background: rgba(21, 25, 26, .35);
    opacity: 0; pointer-events: none; transition: opacity .22s ease;
  }
  body.side-open .side-scrim { opacity: 1; pointer-events: auto; }

  .nav-toggle { display: flex; }
  .app-content { overflow-x: hidden; }
  /* 固定工作区在窄屏挤不下三栏，改成纵向排布并允许滚动 */
  .code-layout, .sort-layout { grid-template-columns: 1fr; overflow-y: auto; }
  body.page-fixed { height: auto; overflow: auto; }
}

/* ============================================================
   减少动态效果
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ============================================================
   作业（学生端）
   ============================================================ */
.hw-item {
  padding: 14px 16px; margin-bottom: 12px;
  border: 1px solid var(--line); border-radius: var(--r-md);
  background: var(--band);
}
.hw-stem { font-size: 15px; font-weight: 700; margin-bottom: 10px; line-height: 1.7; }
.hw-kind {
  display: inline-block; margin-right: 8px; padding: 1px 8px;
  border: 1px solid var(--explain); border-radius: var(--r-sm);
  font-size: 11px; font-weight: 800; color: var(--explain-ink);
  vertical-align: 2px;
}
.hw-run-bar { display: flex; align-items: center; gap: 10px; margin-top: 8px; }
.hw-item .code-input { min-height: 150px; }

/* ============================================================
   作业编辑器（教师端）
   ============================================================ */
.hw-editor { display: flex; flex-direction: column; gap: 12px; }
.hw-edit-item {
  border: 1px solid var(--line); border-radius: var(--r-md);
  padding: 12px 14px; background: var(--band);
}
.hw-edit-head {
  display: flex; align-items: center; gap: 8px; margin-bottom: 10px;
  font-size: 12px; font-weight: 800; color: var(--faint);
}
.hw-edit-head .editor-actions { margin-left: auto; display: flex; gap: 6px; }
.hw-edit-item .field { margin-bottom: 10px; }
.hw-edit-item input[type="text"], .hw-edit-item select {
  width: 100%; padding: 9px 12px; font-size: 14px; font-family: inherit;
  color: var(--on-page); background: var(--paper);
  border: 1px solid var(--line); border-radius: var(--r-md); outline: none;
}
.hw-edit-item input[type="text"]:focus, .hw-edit-item select:focus {
  border-color: var(--signal);
}
.hw-opt-row { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
.hw-opt-row input[type="radio"] { accent-color: var(--ink); flex-shrink: 0; }
.hw-opt-row .opt-del {
  flex-shrink: 0; border: none; background: transparent; cursor: pointer;
  color: var(--faint); font-size: 12px;
}
.hw-opt-row .opt-del:hover { color: var(--danger-ink); }

/* ============================================================
   课程轨道的回顾
   ============================================================ */
/* 可点的课时：悬停给出反馈，光标暗示"这个能点" */
.lt-item.clickable {
  width: 100%; border: none; background: transparent; font-family: inherit;
  text-align: left; cursor: pointer; border-radius: var(--r-sm);
  padding-left: 6px; padding-right: 6px;
  transition: background .16s ease, color .16s ease;
}
.lt-item.clickable:hover { background: var(--band-2); color: var(--on-page); }
.lt-item.clickable:hover .lt-seq { color: var(--explain-ink); }
/* 正在看的那一课 —— 用左侧色条标出来，不只是变色 */
.lt-item.viewing {
  background: var(--band-2); font-weight: 800; color: var(--on-page);
  box-shadow: inset 3px 0 0 var(--signal);
}
/* 还没学到的：不给点的样子，但鼠标移上去有说明 */
.lt-item.locked { cursor: default; }

/* 回顾提示条 —— 学生点开旧课时，必须一眼看出"这不是当前进度" */
.review-banner {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 10px 14px; margin-bottom: 12px;
  border: 1px solid var(--explain); border-left-width: 4px;
  border-radius: var(--r-md); background: var(--band);
  font-size: 13px; color: var(--muted);
}
.review-banner .review-tag {
  font-size: 11px; font-weight: 800; letter-spacing: .06em;
  color: var(--explain-ink); border: 1px solid currentColor;
  border-radius: var(--r-sm); padding: 1px 7px; flex-shrink: 0;
}
.review-banner button { margin-left: auto; flex-shrink: 0; }

/* 轻提示 */
.toast {
  position: fixed; left: 50%; bottom: 32px; transform: translateX(-50%) translateY(12px);
  background: var(--ink); color: var(--paper); font-size: 13px;
  padding: 10px 18px; border-radius: var(--r-md);
  opacity: 0; pointer-events: none; transition: opacity .2s ease, transform .2s ease;
  z-index: 60; max-width: 80vw;
}
.toast.on { opacity: 1; transform: translateX(-50%) translateY(0); }

/* ---------- 回顾 / 切课时的等待态 ----------
   请求背后是两次模型调用，实测几秒。这几秒里界面必须有动静，
   否则用户只会觉得"按钮点不动" —— 即使请求正在正常进行。 */
body.review-busy .learn-main { opacity: .55; pointer-events: none; }
/* 提示条要**留着**（它说明在等什么），所以把它的变暗撤销掉 */
body.review-busy .review-banner { opacity: 1; pointer-events: auto; }

/* 课轨道上被点中的那一项 —— 立刻给一个"在处理"的样子 */
.lt-item.clickable:active { background: var(--line); }
.lt-item.clickable[aria-busy="true"] {
  background: var(--band-2); font-weight: 700;
  box-shadow: inset 3px 0 0 var(--explain);
}

/* ---------- 课程内容编辑器（教师端） ---------- */
.lec-ref { margin-bottom: 10px; font-size: 12px; }
.lec-ref summary { cursor: pointer; color: var(--explain-ink); font-weight: 700; }
.lec-ref p { margin: 8px 0 0; line-height: 1.8; }
.lec-editor {
  width: 100%; min-height: 200px; padding: 12px 14px;
  border: 1px solid var(--line); border-radius: var(--r-md);
  font-family: inherit; font-size: 14px; line-height: 1.8;
  color: var(--on-page); background: var(--paper); outline: none;
  resize: vertical;
}
.lec-editor:focus { border-color: var(--signal); }

/* ============================================================
   编程闯关（/game）—— v2 §8

   视觉语言跟全站一致（冷白 + 石墨黑 + 电光黄绿只表示"当前行动"）：
   机器人就是那个"当前行动"的东西，所以它用 --signal 填充，
   而**星星**用石墨黑 —— 星级是成绩，不是行动，用黄绿会让满屏都在发光。
   ============================================================ */
.game-layout {
  flex: 1; display: grid; grid-template-columns: 250px minmax(0, 1fr) 380px;
  gap: 12px; min-height: 0;
}

/* ---------- 左：进度与关卡列表 ---------- */
.game-progress {
  padding: 10px 12px; margin-bottom: 10px;
  border: 1px solid var(--line); border-radius: var(--r-md);
  background: var(--band-2);
}
.gp-main { font-size: 15px; font-weight: 700; color: var(--ink); }
.gp-sub { margin-top: 2px; font-size: 12px; color: var(--muted); }
.gp-recent { margin-top: 6px; font-size: 12px; color: var(--explain-ink); font-weight: 700; }

.level-list { display: flex; flex-direction: column; gap: 6px; overflow-y: auto; min-height: 0; }

.level-item {
  display: flex; align-items: center; gap: 10px;
  width: 100%; padding: 8px 10px; text-align: left; font-family: inherit;
  border: 1px solid var(--line); border-radius: var(--r-md);
  background: var(--band); color: var(--on-page); cursor: pointer;
  transition: border-color .18s ease, background .18s ease;
}
.level-item:hover { border-color: var(--ink); }
.level-item.on { border-color: var(--ink); box-shadow: inset 3px 0 0 var(--signal); }
/* 下一关：**只是高亮，不阻塞**。卡在某一关就再也看不到后面的，
   对课堂是灾难（见 maze.next_level 的说明）。 */
.level-item.next:not(.on) { border-color: var(--explain); }

.lv-no {
  flex: none; width: 26px; height: 26px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 13px; font-weight: 700;
  background: var(--band-2); color: var(--muted);
  border: 1px solid var(--line);
}
.level-item.done .lv-no { background: var(--ink); color: var(--paper); border-color: var(--ink); }
.level-item.locked .lv-no { opacity: .5; }
.lv-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.lv-title { font-size: 13px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.lv-stars { display: flex; gap: 2px; }
.lv-hint { font-size: 11px; color: var(--faint); }

.level-item.locked {
  cursor: not-allowed; background: var(--band-2); border-style: dashed;
}
.level-item.locked .lv-title { color: var(--faint); }

/* 星星：实心的用石墨黑，空心的只有描边 —— 色盲和投影仪都分得清 */
.star svg { width: 13px; height: 13px; display: block; }
.star path { fill: none; stroke: var(--line); }
.star.on path { fill: var(--ink); stroke: var(--ink); }

/* ---------- 中：舞台 ---------- */
.pane-stage { display: flex; flex-direction: column; gap: 10px; min-height: 0; }
.stage-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
.stage-heading { min-width: 0; }
.stage-title { margin: 0; font-size: 17px; font-weight: 700; color: var(--ink); }
.stage-head .muted { margin: 2px 0 0; }
.stage-stars { display: flex; gap: 4px; flex: none; padding-top: 4px; }
.stage-stars .star svg { width: 20px; height: 20px; }

.maze-stage {
  flex: 1; min-height: 0; display: flex; align-items: center; justify-content: center;
  padding: 12px; overflow: auto;
  background: var(--band-2); border: 1px solid var(--line); border-radius: var(--r-md);
  position: relative;
  isolation: isolate;
}
.maze-stage::before {
  content: "LIVE MAP";
  position: absolute; top: 10px; left: 12px; z-index: 2;
  padding: 3px 6px; border: 1px solid var(--line); border-radius: 3px;
  background: var(--band); color: var(--faint);
  font: 800 10px/1 ui-monospace, Consolas, monospace; letter-spacing: .08em;
}
.maze-stage::after {
  content: attr(data-frame);
  position: absolute; right: 12px; bottom: 10px; z-index: 2;
  min-width: 52px; padding: 4px 7px; text-align: center;
  border: 1px solid var(--line); border-radius: 3px;
  background: var(--band); color: var(--explain-ink);
  font: 700 11px/1 ui-monospace, Consolas, monospace;
}
.maze-stage:not([data-frame])::after { display: none; }
.maze-svg {
  max-width: 100%; max-height: 100%; height: auto;
  border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--paper);
  /* 网格是像素对齐的，缩放时不要糊 */
  shape-rendering: crispEdges;
}
.maze-svg circle, .maze-svg polygon { shape-rendering: auto; }

/* The game canvas keeps a stable box while snapshots and HUD values change. */
.scene-info { display: flex; justify-content: space-between; flex-wrap: wrap; gap: 6px 12px; font-size: 12px; color: var(--muted); }
.scene-info > :first-child { font-weight: 700; color: var(--ink); }
.maze-stage:has(.game-canvas) { padding: 0; min-height: 240px; overflow: hidden; border-radius: 8px; }
.maze-stage:has(.game-canvas)::before, .maze-stage:has(.game-canvas)::after { display: none; }
.game-canvas { display: block; position: absolute; inset: 0; width: 100%; height: 100%; }
.maze-stage[data-paused="true"]:has(.game-canvas)::after {
  display: block; content: "已暂停"; top: 12px; bottom: auto; right: 12px;
  padding: 8px 12px; background: var(--paper); color: var(--ink);
}
.playback-bar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; min-height: 44px; }
.playback-icon { width: 40px; height: 40px; padding: 0; flex: none; font-size: 19px; }
.playback-bar progress { flex: 1; min-width: 40px; width: 60px; height: 6px; accent-color: #159ca8; }
.playback-bar output { font: 12px ui-monospace, Consolas, monospace; min-width: 70px; text-align: right; }
.playback-bar select { min-height: 40px; border: 1px solid var(--line); border-radius: 4px; padding: 4px; background: var(--paper); color: var(--ink); }
.scene-result { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; padding: 10px 0; border-bottom: 1px solid var(--line); }
.scene-result strong { margin-right: auto; font-size: 16px; }
.scene-result[data-kind="won"] strong { color: #15746b; }
.scene-result[data-kind="retry"] strong { color: #b54f35; }
body[data-page="game"] .stage-title, body[data-page="game"] .brief-text { overflow-wrap: anywhere; }
body[data-page="game"] .stage-msg { max-height: 115px; overflow-y: auto; overflow-wrap: anywhere; }
body[data-page="game"] button:focus-visible, body[data-page="game"] select:focus-visible, .maze-stage:focus-visible { outline: 2px solid #159ca8; outline-offset: 3px; }
.game-progress progress { width: 100%; height: 5px; margin-top: 8px; accent-color: #159ca8; }
.pad-chip { min-height: 34px; font-family: inherit; }
@media (max-width: 1100px) {
  body[data-page="game"].page-fixed { height: auto; min-height: 100vh; overflow: auto; }
  body[data-page="game"] .work-fixed { height: auto; overflow: visible; }
  body[data-page="game"] .game-layout { grid-template-columns: minmax(0, 1fr) minmax(270px, .8fr); }
  body[data-page="game"] .game-layout > aside { grid-column: 1 / -1; }
  body[data-page="game"] .level-list { flex-direction: row; overflow-x: auto; max-height: 110px; }
  body[data-page="game"] .level-item { flex: 0 0 175px; }
  body[data-page="game"] .game-progress { margin: 0 0 8px; }
  body[data-page="game"] .maze-stage { min-height: 320px; }
}
@media (max-width: 700px) {
  body[data-page="game"] .game-layout { grid-template-columns: minmax(0, 1fr); }
  body[data-page="game"] .game-layout > .pane { min-width: 0; overflow: visible; }
  body[data-page="game"] .maze-stage { min-height: 290px; flex: none; height: 340px; }
  body[data-page="game"] .stage-foot { flex-wrap: wrap; }
  body[data-page="game"] .battle-bar { flex-wrap: wrap; gap: 8px; }
  body[data-page="game"] .code-input { min-height: 220px; }
  body[data-page="game"] .blockly-div { height: 360px; flex: none; }
  body[data-page="game"] .small { min-height: 40px; }
}
@keyframes stage-frame-in {
  from { opacity: .35; transform: scale(.985); }
  to { opacity: 1; transform: scale(1); }
}
.maze-stage[data-mode="light"] .maze-svg { box-shadow: 0 0 0 3px rgba(216,251,67,.18); }
.maze-stage[data-mode="battle"] .maze-svg { box-shadow: 0 0 0 3px rgba(255,90,95,.14); }

.stage-msg {
  margin: 0; min-height: 20px; font-size: 13px; line-height: 1.7;
  color: var(--muted); white-space: pre-line;
}
.stage-msg[data-kind="ok"] { color: var(--ink); font-weight: 700; }
.stage-msg[data-kind="bad"] { color: var(--danger-ink); }
.stage-msg[data-kind="ok"], .stage-msg[data-kind="bad"] {
  padding: 8px 10px; border-left: 3px solid currentColor; background: var(--band-2);
}

.stage-foot { display: flex; align-items: center; gap: 8px; }
.stage-foot-gap { flex: 1; }

/* ---------- 右：题目与编辑器 ---------- */
.brief-box {
  padding: 10px 12px; margin-bottom: 10px;
  border: 1px solid var(--line); border-left: 3px solid var(--signal);
  border-radius: var(--r-sm); background: var(--band-2);
}
.brief-title { margin: 0 0 4px; font-size: 14px; font-weight: 700; }
.brief-text { margin: 0; font-size: 13px; line-height: 1.8; color: var(--on-page); }
.brief-hint { margin: 8px 0 0; font-size: 12px; line-height: 1.7; color: var(--explain-ink); }

/* 调试关给的那段"跑不通的程序"：用等宽字排，跟编辑器里的看起来一样 */
.starter-box {
  padding: 8px 10px; margin-bottom: 8px;
  border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--band-2);
}
.starter-code {
  margin: 4px 0 0; font-family: ui-monospace, Consolas, monospace;
  font-size: 12px; line-height: 1.7; color: var(--muted);
  white-space: pre; overflow-x: auto;
}

/* 窄屏：舞台优先，关卡列表先让位 */
@media (max-width: 1199px) {
  .game-layout { grid-template-columns: 200px minmax(0, 1fr) 320px; }
}

/* ---------- 闯关：玩法切换条 ----------
   服务端只说"这个学段有哪几种玩法"，前端照着渲染 ——
   低年级因此只会看到「点亮地砖」一个。只有一个玩法时整条收掉：
   一个点了没反应的标签比没有标签更让人困惑。 */
.engine-tabs { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 10px; }
.engine-tab {
  flex: 1 1 auto; display: flex; align-items: center; gap: 6px;
  padding: 6px 10px; font-family: inherit; font-size: 13px; cursor: pointer;
  border: 1px solid var(--line); border-radius: var(--r-md);
  background: var(--band); color: var(--muted);
  transition: border-color .18s ease, color .18s ease;
}
.engine-tab:hover { border-color: var(--ink); color: var(--on-page); }
.engine-tab.picked {
  border-color: var(--ink); color: var(--on-page); font-weight: 700;
  box-shadow: inset 3px 0 0 var(--signal);
}
.et-count { margin-left: auto; font-size: 11px; color: var(--faint); }
.engine-tab.picked .et-count { color: var(--explain-ink); }

/* ---------- 闯关：方向按钮（低年级） ----------
   低年级**只有这一种编辑器** —— 没有代码框也没有积木。
   玩的是"给机器人下指令"，不是"写程序"。 */
.pad-editor { display: flex; flex-direction: column; gap: 10px; }
.pad-buttons { display: flex; flex-wrap: wrap; gap: 8px; }
.pad-btn {
  flex: 1 1 72px; min-height: 46px;
  font-family: inherit; font-size: 14px; font-weight: 700; cursor: pointer;
  border: 1px solid var(--ink); border-radius: var(--r-md);
  background: var(--band); color: var(--on-page);
  transition: background .15s ease, transform .06s ease;
}
.pad-btn:hover { background: var(--band-2); }
.pad-btn:active { transform: translateY(1px); background: var(--signal); }
.pad-btn:focus-visible { box-shadow: 0 0 0 3px rgba(22,184,197,.22); }

.pad-strip {
  display: flex; flex-wrap: wrap; gap: 6px; align-items: center;
  min-height: 40px; padding: 8px 10px;
  border: 1px dashed var(--line); border-radius: var(--r-md);
  background: var(--band-2);
}
.pad-empty { font-size: 12px; color: var(--faint); }
.pad-chip {
  padding: 3px 8px; font-size: 12px; cursor: pointer;
  border: 1px solid var(--line); border-radius: 999px;
  background: var(--band); color: var(--on-page);
}
.pad-chip:hover { border-color: var(--danger-ink); color: var(--danger-ink); }
.pad-actions { display: flex; gap: 8px; }
.pad-actions button:disabled { opacity: .45; cursor: not-allowed; }
.pad-code {
  margin: 0; padding: 8px 10px; overflow-x: auto;
  border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--band-2);
  font-family: ui-monospace, Consolas, monospace; font-size: 12px; line-height: 1.7;
  white-space: pre;
}

/* ---------- 闯关：画笔 ----------
   目标形状画成虚线底稿，学生画出来的叠在上面 ——
   一眼看得出"还差多远"，而不是画完了才被判对错。 */
.draw-layers { position: relative; display: inline-block; }
.draw-layers .ghost { position: absolute; inset: 0; opacity: .5; }
.draw-layers .ink { position: relative; }
.draw-layers .ink svg { display: block; }

/* ---------- 闯关：战斗闯关的战况条 ----------
   血量要**一眼看出还剩几下**，所以用一排小格而不是一根进度条：
   战斗闯关里每次挨打都是固定的一点，数格子比看长度直观。 */
.battle-bar {
  display: flex; align-items: center; gap: 18px;
  padding: 8px 12px; border: 1px solid var(--line); border-radius: var(--r-md);
  background: var(--band-2); font-size: 13px; color: var(--on-page);
}
.hud-hp { display: flex; align-items: center; gap: 6px; }
.hud-hp b { margin-left: 4px; font-size: 14px; }
.hp-pip {
  width: 12px; height: 12px; border-radius: 2px;
  border: 1px solid var(--line); background: var(--band);
}
.hp-pip.on { background: var(--alarm); border-color: var(--danger-ink); }
@keyframes hp-pulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(.86); } }
.hud-foes { margin-left: auto; color: var(--muted); }
.hud-foes b { color: var(--on-page); font-size: 14px; }

/* 战斗帧有明确的命中状态：整块舞台短促闪一下，学生能看懂伤害来自哪一回合。 */
@keyframes hit-surface {
  0% { box-shadow: inset 0 0 0 0 rgba(255,90,95,0); }
  45% { box-shadow: inset 0 0 0 5px rgba(255,90,95,.28); }
  100% { box-shadow: inset 0 0 0 0 rgba(255,90,95,0); }
}

@media (prefers-reduced-motion: reduce) {
  .maze-svg, .maze-stage[data-mode="battle"] { animation: none; }
  .hp-pip.on { animation: none; }
}

/* ============================================================
   Workbench refresh —— shared visual system
   ============================================================
   The application shell is intentionally quiet: a graphite navigation rail,
   a paper canvas, and compact tool surfaces. Page-specific illustrations and
   game effects keep their own semantic colors on top of this foundation.
*/
:root {
  --page: #f3f5f4;
  --band: #ffffff;
  --band-2: #edf1f0;
  --line: #d6dfdc;
  --grid: #e5ebe9;
  --muted: #526164;
  --faint: #788689;
  --ink: #172022;
  --rail: #202a2c;
  --rail-2: #293537;
  --rail-line: #3b484a;
  --topbar-h: 56px;
  --rail-w: 232px;
  --r-sm: 5px;
  --r-md: 8px;
}

body { background: var(--page); color: var(--ink); }

.sidebar {
  background: var(--rail);
  border-right: 1px solid #111819;
  padding: 14px 12px 12px;
  gap: 12px;
}
.side-head { padding: 2px 6px 10px; border-bottom: 1px solid var(--rail-line); }
.side-brand { color: #f2f7f5; letter-spacing: .01em; }
.brand-mark .track { stroke: #657476; }
.brand-mark .node { fill: #8e9b9c; }
.brand-mark .node.on { fill: var(--signal); stroke: #172022; }
.side-collapse { color: #a6b2b2; }
.side-collapse:hover { background: var(--rail-2); color: #fff; }
.new-chat {
  border-radius: 6px;
  padding: 10px 12px;
  box-shadow: 0 1px 0 rgba(0,0,0,.08);
}
.side-nav { gap: 3px; }
.side-link {
  color: #b5c0bf;
  border-left: 2px solid transparent;
  border-radius: 6px;
  padding: 9px 11px;
}
.side-link:hover { background: var(--rail-2); color: #f5faf8; }
.side-link.on {
  background: #344143;
  color: #fff;
  border-left-color: var(--signal);
}
.side-history { border-top-color: var(--rail-line); }
.hist-label { color: #839091; }
.hist-item { color: #aebbbb; }
.hist-item:hover { background: var(--rail-2); color: #fff; }
.hist-item.on { background: #344143; color: #fff; border-left-color: var(--signal); }
.side-empty { color: #839091; }
.side-foot { border-top-color: var(--rail-line); }
.side-foot #who { color: #f0f5f3; }
.side-foot .avatar { background: var(--signal); }
.foot-out { border-color: var(--rail-line); color: #aebbbb; }
.foot-out:hover { border-color: var(--alarm); color: #ff9a9d; }

.app-col { background: var(--page); }
.topbar {
  height: var(--topbar-h);
  padding: 0 24px;
  background: rgba(255,255,255,.94);
  color: var(--ink);
  border-bottom: 1px solid var(--line);
  box-shadow: 0 1px 7px rgba(23,32,34,.04);
}
.nav-toggle { border-color: var(--line); color: var(--muted); }
.nav-toggle:hover { border-color: var(--explain); color: var(--explain-ink); }
.topbar-extra { gap: 10px; }
.status-chip {
  color: var(--muted);
  border-color: var(--line);
  background: var(--band);
}
.status-chip[data-kind="ok"] { color: var(--explain-ink); border-color: #9bd6d9; background: #f1fbfb; }
.status-chip[data-kind="bad"] { color: var(--danger-ink); border-color: #efb2b4; background: #fff5f5; }
.sandbox-status { color: var(--muted); }
.sandbox-status[data-kind="ok"] { color: var(--explain-ink); }
.sandbox-status[data-kind="bad"] { color: var(--danger-ink); }
.progress-track { background: var(--line); }
.progress-text, .streak { color: var(--muted); }
.streak b { color: var(--explain-ink); }
.user-box { color: var(--muted); }
.user-box #who { color: var(--ink); }
.user-box button { border-color: var(--line); color: var(--muted); }
.user-box button:hover { border-color: var(--explain); color: var(--explain-ink); }

.work { padding: 28px 34px 64px; }
.work.work-fixed { padding: 18px 22px 22px; }
.band, .pane {
  border-color: var(--line);
  border-radius: 8px;
  box-shadow: 0 1px 2px rgba(23,32,34,.035);
}
.band { padding: 22px 24px; margin-bottom: 16px; }
.pane { padding: 15px; }
.pane-title { color: #647476; letter-spacing: .1em; }
.primary-btn, .ghost-btn { border-radius: 6px; }
.primary-btn { box-shadow: 0 1px 0 rgba(23,32,34,.12); }
.ghost-btn { background: var(--band); }
.ghost-btn:hover:not(:disabled) { background: #f6fbfa; }
.pill, .badge-state, .hw-kind { border-radius: 5px; }
input[type="text"], input[type="password"], textarea,
.material-form select { border-radius: 6px; background: #fbfcfc; }
input[type="text"]:focus, input[type="password"]:focus, textarea:focus,
.material-form select:focus, .material-form input[type="text"]:focus {
  border-color: var(--explain);
  box-shadow: 0 0 0 3px rgba(22,184,197,.12);
}
.state-box { border-radius: 6px; background: rgba(255,255,255,.5); }
.verdict { border-radius: 6px; }

/* Make the major work surfaces read like tools with a clear header/body split. */
.code-layout, .game-layout, .sort-layout { gap: 14px; }
.code-layout > .pane:first-child, .game-layout > .pane:first-child,
.sort-layout > .pane:first-child { background: #fbfcfc; }
.code-toolbar, .game-toolbar, .sort-toolbar { border-bottom-color: var(--line); }
.tab-bar, .entry-tabs, .teach-tabs { border-color: var(--line); background: #edf2f1; }
.tab-bar button, .entry-tab, .teach-tab { border-radius: 5px; }
.tab-bar button.on, .entry-tab.on, .teach-tab.picked { background: var(--ink); color: #fff; }

@media (max-width: 959px) {
  .topbar { padding: 0 14px; }
  .work { padding: 20px 16px 46px; }
  .work.work-fixed { padding: 12px; }
}

/* ---------- 闯关：能用的指令清单 ----------
   战斗闯关有个别的玩法没有的动作（攻击），而「攻击」两个字在代码里
   根本不出现 —— 不摆这份清单，学生只能靠猜。
   点一下就填进代码框，省掉"照着敲还打错一个字母"那一步。 */
.cmd-ref {
  margin-bottom: 8px; padding: 8px 10px;
  border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--band-2);
}
.cmd-head { margin-bottom: 6px; font-size: 12px; color: var(--muted); }
.cmd-list { display: flex; flex-direction: column; gap: 3px; max-height: 168px; overflow-y: auto; }
.cmd-item {
  display: flex; align-items: baseline; gap: 8px;
  padding: 3px 6px; text-align: left; font-family: inherit; cursor: pointer;
  border: none; border-radius: var(--r-sm); background: transparent;
  color: var(--on-page); transition: background .12s ease;
}
.cmd-item:hover { background: var(--band); }
.cmd-item code {
  flex: none; font-family: ui-monospace, Consolas, monospace;
  font-size: 12px; color: var(--explain-ink);
}
.cmd-item span { font-size: 12px; color: var(--muted); }

/* Constrain the desktop grid; narrow screens scroll as one document. */
body[data-page="game"] .work-fixed { display: flex; flex-direction: column; }
body[data-page="game"] .game-layout { grid-template-columns: 180px minmax(0, 1fr) 310px; grid-template-rows: minmax(0, 1fr); }
body[data-page="game"] .game-layout > .pane { min-width: 0; min-height: 0; }
body[data-page="game"] .game-layout > .pane:last-child { overflow-y: auto; }
body[data-page="game"] .code-input { min-height: 180px; }
body[data-page="game"] .pane-stage > :not(.maze-stage) { flex-shrink: 0; }
@media (max-width: 1100px) {
  body[data-page="game"] .app-body, body[data-page="game"] .app-col,
  body[data-page="game"] .app-content { height: auto; overflow: visible; }
  body[data-page="game"] .game-layout { grid-template-columns: minmax(0, 1fr) minmax(270px, .8fr); grid-template-rows: auto; }
}
@media (max-width: 700px) {
  body[data-page="game"] .game-layout { grid-template-columns: minmax(0, 1fr); }
  body[data-page="game"] .code-input { min-height: 220px; }
}
