/* ===== 学习工作台 全局样式（儿童友好风格） ===== */
body {
  background:
    radial-gradient(1200px 500px at 90% -10%, #ffe9f3 0%, transparent 60%),
    radial-gradient(1000px 480px at -10% 0%, #e3edff 0%, transparent 55%),
    var(--bg);
}
:root {
  --bg: #f0f6ff;
  --card: #ffffff;
  --primary: #4f7cff;
  --primary-light: #e8efff;
  --green: #34c77b;
  --orange: #ff9f43;
  --red: #ff6b6b;
  --purple: #a06bff;
  --yellow: #ffd93d;
  --text: #2d3348;
  --text-light: #8a90a6;
  --radius: 16px;
  --shadow: 0 4px 16px rgba(60, 90, 180, .10);
}
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body { height: 100%; }
body {
  font-family: "PingFang SC", "Microsoft YaHei", "Segoe UI", sans-serif;
  background: var(--bg);
  color: var(--text);
  overflow: hidden;
}
.hidden { display: none !important; }
button { font-family: inherit; cursor: pointer; border: none; }

/* ===== 档案选择页 ===== */
#profile-gate {
  height: 100vh; display: flex; align-items: center; justify-content: center;
  background: linear-gradient(135deg, #a8c8ff 0%, #d8b8ff 50%, #ffd8e8 100%);
  overflow: auto; padding: 16px;
}
.gate-card {
  background: rgba(255,255,255,.92); border-radius: 24px; padding: 48px 56px;
  text-align: center; box-shadow: 0 12px 48px rgba(80,80,160,.25);
  max-width: 640px; width: min(640px, calc(100vw - 32px));
  max-height: calc(100vh - 32px); overflow-y: auto;
}
.gate-card h1 { font-size: 32px; margin-bottom: 8px; }
.gate-sub { color: var(--text-light); margin-bottom: 24px; }
.gate-profiles { display: flex; gap: 20px; justify-content: center; margin-bottom: 24px; flex-wrap: wrap; }
.gate-profile {
  width: 150px; padding: 20px 12px; border-radius: 20px; background: var(--primary-light);
  border: 3px solid transparent; transition: .15s; cursor: pointer;
}
.gate-profile:hover { transform: translateY(-4px); border-color: var(--primary); }
.gate-profile .gate-av { display: block; margin: 0 auto 10px; width: 58px; }
.gate-profile .pname { font-size: 18px; font-weight: 700; }
.gate-profile .pinfo { font-size: 12px; color: var(--text-light); margin-top: 4px; }

/* ===== 布局 ===== */
#main-ui { display: flex; height: 100vh; }
#sidebar {
  width: 200px; min-width: 200px; background: #fff; border-right: 1px solid #e4e9f5;
  display: flex; flex-direction: column; padding: 16px 12px;
}
.logo {
  font-size: 15px; font-weight: 800; line-height: 1.2; padding: 4px 2px 14px;
  white-space: nowrap; overflow: hidden;
  background: linear-gradient(90deg, #4f7cff, #a06bff);
  -webkit-background-clip: text; background-clip: text; color: transparent;
}
#nav-menu { display: flex; flex-direction: column; gap: 4px; overflow-y: auto; flex: 1; }
.nav-item {
  display: flex; align-items: center; gap: 10px; padding: 10px 12px; border-radius: 12px;
  background: transparent; font-size: 15px; color: var(--text); text-align: left; transition: .12s;
}
.nav-item:hover { background: var(--primary-light); }
.nav-item.active { background: var(--primary); color: #fff; font-weight: 700; }
.nav-divider { height: 1px; background: #e4e9f5; margin: 8px 4px; }

#content-wrap { flex: 1; display: flex; flex-direction: column; min-width: 0; }
#topbar {
  height: 60px; background: #fff; border-bottom: 1px solid #e4e9f5;
  display: flex; align-items: center; justify-content: space-between; padding: 0 24px;
}
#topbar-child { display: flex; align-items: center; gap: 10px; font-weight: 700; font-size: 16px; }
.avatar-sm { font-size: 28px; }
#topbar-right { display: flex; align-items: center; gap: 14px; }
.streak-badge { background: #fff1e6; color: #e8590c; padding: 6px 14px; border-radius: 999px; font-weight: 700; font-size: 14px; }
.points-badge { background: #fff9db; color: #b08900; padding: 6px 14px; border-radius: 999px; font-weight: 700; font-size: 14px; }
.pet-mini { font-size: 26px; cursor: pointer; display: inline-block; animation: miniBob 3s ease-in-out infinite; }
@keyframes miniBob { 0%,100% { transform: translateY(0) rotate(0); } 50% { transform: translateY(-3px) rotate(-4deg); } }
.store-badge { font-size: 12px; font-weight: 700; padding: 4px 10px; border-radius: 999px; background: #f2f5fb; cursor: help; }
/* 答题计时（2026-10-01）：答题页头部的实时用时，数值由 app.js 的 QuizTimer 每秒刷新 */
.quiz-timer {
  display: inline-block; padding: 3px 9px; border-radius: 999px;
  background: #eef4ff; color: var(--primary); font-weight: 800; font-size: 12px; white-space: nowrap;
  font-variant-numeric: tabular-nums;   /* 数字等宽，秒跳时不会左右晃 */
}

/* scrollbar-gutter: stable（2026-10-17 第十二轮）——内容超不超屏都**永远预留**滚动条那 10px。
 * 不加这条的话，"单词"这种长页有滚动条（内容窄 10px）、"阅读"地图页刚好不滚（没滚动条），
 * 吸顶的英语页头就会在两个 tab 之间左右跳 10px（用户在手机上截图指出）。
 * Chromium 94+ / Firefox 97+ 支持；老 WebView 不认就是原样，不会更坏。 */
#view-root { flex: 1; overflow-y: auto; padding: 24px; scrollbar-gutter: stable; }

/* ===== 通用组件 ===== */
.card {
  background: var(--card); border-radius: var(--radius); box-shadow: var(--shadow);
  padding: 20px; margin-bottom: 16px; border: 1px solid #edf1fb;
}
.card h3 { margin-bottom: 12px; font-size: 17px; }
.btn {
  padding: 10px 22px; border-radius: 12px; font-size: 15px; font-weight: 600;
  background: #eef1f8; color: var(--text); transition: .12s;
}
.btn:hover { filter: brightness(.96); }
.btn:disabled { opacity: .45; cursor: not-allowed; }
.btn-primary { background: var(--primary); color: #fff; }
.btn-green { background: var(--green); color: #fff; }
.btn-orange { background: var(--orange); color: #fff; }
.btn-red { background: var(--red); color: #fff; }
.btn-mini { padding: 4px 12px; font-size: 13px; border-radius: 8px; background: var(--primary-light); color: var(--primary); }
.btn-big { padding: 14px 36px; font-size: 18px; border-radius: 16px; }

/* 状态标签一律不许折行（2026-10-17 全站收口）：`.tag` 之前没有 white-space，
 * 一行里标题一长，「已完成」「正确 8/10（80%）」这类状态就会被压成两行（首页/数学/语文/阅读都出现过）。
 * 加在这里而不是逐个页面补：状态标签"整块显示"是全站一致的语义。
 * ⚠ 副作用是超长 tag 会**溢出**而不是折行，所以长文案必须改短（见 chinese-flow 的 rangeLabel、
 *   math-flow 的卷头说明），别把整句话塞进 tag。 */
.tag { display: inline-block; padding: 2px 10px; border-radius: 999px; font-size: 12px; font-weight: 600; white-space: nowrap; }
.tag-blue { background: var(--primary-light); color: var(--primary); }
.tag-green { background: #e2f8ee; color: #1d9e5f; }
.tag-orange { background: #fff1e6; color: #e8590c; }
.tag-red { background: #ffe9e9; color: #e03131; }
.tag-purple { background: #f1e9ff; color: #7c4ddb; }
.tag-gray { background: #eef1f8; color: var(--text-light); }

.grid { display: grid; gap: 16px; }
.grid-2 { grid-template-columns: repeat(2, 1fr); }
.grid-3 { grid-template-columns: repeat(3, 1fr); }
.grid-4 { grid-template-columns: repeat(4, 1fr); }
.row { display: flex; align-items: center; gap: 12px; }
.spread { justify-content: space-between; }
.muted { color: var(--text-light); font-size: 13px; }
.center { text-align: center; }
.mt8 { margin-top: 8px; } .mt16 { margin-top: 16px; } .mb8 { margin-bottom: 8px; }

input[type=text], input[type=number], select, textarea {
  /* 字号必须 ≥16px：iOS Safari 对 <16px 的输入框会在聚焦时把整个页面放大 */
  padding: 10px 14px; border: 2px solid #dde3f0; border-radius: 10px; font-size: 16px;
  font-family: inherit; outline: none; background: #fff; color: var(--text);
}
input:focus, select:focus, textarea:focus { border-color: var(--primary); }

.progress-bar { height: 10px; background: #e8ecf6; border-radius: 999px; overflow: hidden; }
.progress-fill { height: 100%; background: linear-gradient(90deg, var(--primary), var(--purple)); border-radius: 999px; transition: width .3s; }

/* ===== 弹窗（2026-09-28 统一框架：头部固定 + 正文独立滚动 + 底部按钮条固定）=====
 * 为什么要改：原来整个 #modal-box 一起滚，长弹窗（每日学习量/宠物规则/词库管理）
 * 要滚到底才看得见"保存"，家长经常找不到按钮。现在只有正文滚。 */
#modal-mask {
  position: fixed; inset: 0; background: rgba(30,40,80,.45); z-index: 100;
  display: flex; align-items: center; justify-content: center;
}
#modal-box {
  background: #fff; border-radius: 20px; width: 480px; max-width: 92vw;
  max-height: 86vh; box-shadow: 0 20px 60px rgba(0,0,30,.3);
  display: flex; flex-direction: column; overflow: hidden;   /* 滚动交给 #modal-body */
}
/* 家长中心的学习量设置、重领养等宽内容，480px 太窄，单独放宽 */
#modal-box.wide { width: 720px; }
/* openModal 支持第 4 个参数传 'wide'：这里是样式钩子，切页/关窗时会自动移除 */
#modal-head {
  position: relative; flex: none; padding: 22px 62px 12px 26px;
  border-bottom: 1px solid #f0f3fa;
}
#modal-title { font-size: 19px; font-weight: 800; }
#modal-close {
  position: absolute; top: 16px; right: 18px; width: 34px; height: 34px; border-radius: 50%;
  background: #f2f5fc; color: #7b88a3; font-size: 14px; line-height: 1;
  display: flex; align-items: center; justify-content: center; transition: .12s;
}
#modal-close:hover { background: #ffe9e9; color: #e03131; }
#modal-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 16px 26px 6px; }
#modal-actions {
  flex: none; display: flex; gap: 10px; justify-content: flex-end; flex-wrap: wrap;
  padding: 14px 26px 18px; border-top: 1px solid #f0f3fa; background: #fff;
}

/* 圆角细滚动条（全站统一）：系统默认的方头滚动条会顶在弹窗/卡片的圆角上，很难看。
 * 用 `*` 而不是逐个容器列举，是为了把"弹窗正文、右侧内容区、侧栏、词表内部滚动块"
 * 这些**所有**会出滚动条的地方一次盖住（词表弹窗里的 max-height 内层 div 就是靠自己滚的）。
 * -webkit-scrollbar 那套给 Chromium/Edge/Safari，scrollbar-width/color 给 Firefox。 */
*::-webkit-scrollbar { width: 10px; height: 10px; }
*::-webkit-scrollbar-track { background: transparent; }
*::-webkit-scrollbar-thumb {
  background: #dbe2f0; border-radius: 999px; border: 2px solid transparent; background-clip: content-box;
}
*::-webkit-scrollbar-thumb:hover { background: #c3ccdf; }
*::-webkit-scrollbar-corner { background: transparent; }
* { scrollbar-width: thin; scrollbar-color: #dbe2f0 transparent; }

/* ===== 儿童头像（彩色圆底 chip，全站统一）===== */
.av-chip {
  display: inline-flex; align-items: center; justify-content: center; flex: none;
  width: var(--av-s, 26px); height: var(--av-s, 26px); border-radius: 50%;
  font-size: calc(var(--av-s, 26px) * 0.62); line-height: 1; vertical-align: middle;
  box-shadow: inset 0 -2px 4px rgba(0,0,0,.06), 0 1px 3px rgba(40,60,120,.14);
}
/* 10 套固定渐变：色号由 avColor() 按头像字符算出来，同一个头像到哪都是同一个颜色 */
.av-c0 { background: linear-gradient(135deg, #ffe3c2, #ffc98a); }
.av-c1 { background: linear-gradient(135deg, #d6e6ff, #a9c8ff); }
.av-c2 { background: linear-gradient(135deg, #d8f6e4, #a6ebc6); }
.av-c3 { background: linear-gradient(135deg, #ffe0e6, #ffb3c1); }
.av-c4 { background: linear-gradient(135deg, #e6dcff, #c3acff); }
.av-c5 { background: linear-gradient(135deg, #d5f4f6, #9fe4ea); }
.av-c6 { background: linear-gradient(135deg, #fff2c9, #ffe08a); }
.av-c7 { background: linear-gradient(135deg, #f2e6d8, #ddc0a2); }
.av-c8 { background: linear-gradient(135deg, #ffdff2, #ffb0dd); }
.av-c9 { background: linear-gradient(135deg, #e3ebf5, #bccbdd); }

/* 头像选择器（新建/修改档案弹窗里） */
.av-line { display: flex; align-items: center; gap: 12px; }
.av-preview { display: inline-flex; align-items: center; padding: 6px; border-radius: 50%;
  background: #f7f9ff; border: 2px dashed #dfe6f5; }
.av-group { margin-top: 10px; }
.av-label { display: block; font-size: 13px; color: var(--text-light); margin-bottom: 6px; }
.av-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(46px, 1fr)); gap: 8px; }
.av-pick {
  padding: 3px; border-radius: 50%; background: transparent; border: 3px solid transparent;
  display: inline-flex; align-items: center; justify-content: center; transition: .12s;
}
.av-pick:hover { transform: translateY(-2px); }
.av-pick.sel { border-color: var(--primary); background: #eef3ff; }

/* ===== Toast ===== */
#toast {
  position: fixed; top: 76px; left: 50%; transform: translateX(-50%);
  background: rgba(35,42,70,.92); color: #fff; padding: 12px 26px; border-radius: 999px;
  font-size: 15px; z-index: 200; animation: popIn .2s; max-width: 80vw; text-align: center;
}

/* ===== 今日任务 ===== */
.task-item {
  display: flex; align-items: center; gap: 14px; padding: 14px 16px;
  border-radius: 14px; background: #f7f9ff; margin-bottom: 10px;
}
.task-item.done { background: #edfbf3; }
.task-icon { font-size: 30px; }
.task-info { flex: 1; }
.task-name { font-weight: 700; font-size: 15px; }
.task-desc { font-size: 12px; color: var(--text-light); margin-top: 2px; }
.task-state { font-size: 13px; font-weight: 700; }

/* ===== 单词学习卡 ===== */
.word-card {
  background: linear-gradient(160deg, #ffffff, #f0f5ff); border-radius: 24px;
  padding: 40px; text-align: center; box-shadow: var(--shadow); max-width: 640px; margin: 0 auto;
}
.word-meta { display: flex; gap: 8px; justify-content: center; margin-bottom: 10px; }
.word-big { font-size: 44px; font-weight: 800; letter-spacing: 1px; }
.word-phonetic { font-size: 18px; color: var(--text-light); margin: 8px 0; }
.word-meaning { font-size: 22px; margin: 10px 0; }
.word-example { font-size: 15px; color: var(--text-light); background: #f4f7ff; border-radius: 12px; padding: 12px 16px; margin-top: 14px; }
/* 词根分解：紫色知识条 */
.word-root {
  margin-top: 12px; background: linear-gradient(90deg, #f5eeff, #efe6ff);
  border: 1px solid #e4d4ff; border-radius: 12px; padding: 10px 16px;
  font-size: 15px; color: #6b3fd4; text-align: left;
}
/* 音形对照（2026-10-08）：青绿色知识条，字母组合逐块作胶囊——比整段文字好扫读，窄屏自然换行 */
.word-phonics {
  margin-top: 10px; background: linear-gradient(90deg, #eafaf7, #e4f6fb);
  border: 1px solid #cdeee8; border-radius: 12px; padding: 8px 14px 10px;
  font-size: 15px; color: #0f7a6c; text-align: left; line-height: 2;
}
.ph-chip {
  display: inline-block; margin: 2px 3px 2px 0; padding: 0 9px;
  background: #ffffff; border: 1px solid #d6f0ec; border-radius: 999px;
  color: #11776a; font-size: 14px; white-space: nowrap;
}
/* 词形变化（2026-10-13）：橙色知识条，动词四形/名词复数逐项作胶囊 */
.word-forms {
  margin-top: 10px; background: linear-gradient(90deg, #fff4e8, #ffeef0);
  border: 1px solid #ffe0c2; border-radius: 12px; padding: 8px 14px 10px;
  font-size: 15px; color: #b5651d; text-align: left; line-height: 2.1;
}
.word-forms .wf-lead { font-weight: 700; }
.form-chip {
  display: inline-block; margin: 2px 6px 2px 0; padding: 1px 10px;
  background: #ffffff; border: 1px solid #ffe0c2; border-radius: 999px;
  color: #b5651d; font-size: 14px; white-space: nowrap;
}
.form-chip i { font-style: normal; color: #c98a4b; font-size: 12px; margin-right: 2px; }
/* 词性派生（2026-10-13）：蓝色知识条 */
.word-derive {
  margin-top: 10px; background: linear-gradient(90deg, #eef3ff, #e8f0ff);
  border: 1px solid #d3e0ff; border-radius: 12px; padding: 10px 16px;
  font-size: 15px; color: #3557c9; text-align: left;
}
.word-example .we-cn { font-size: 13px; color: var(--text-light); margin-top: 4px; }
/* ===== 共用词详情（2026-10-16，阅读规格 D7）：点词弹窗的内容容器 =====
   故意不复用 .word-card：那个是整页学习卡（40px 内边距、限宽 640、居中），
   塞进弹窗会多一圈空白还被限宽。这里只管"弹窗正文里的一块"，
   知识条（词根/音形/词形/派生/例句）沿用上面那几套配色，不另起一套。 */
.wd-detail { text-align: center; }
/* 头部一行：大词 + 频次徽标 +（可选）替读标签。2026-10-17 从"徽标独占一行、出处再独占一行"
 * 改过来的——用户反馈弹窗上面全被头部占满、正文要滚很久才看得到。
 * 字号走 clamp：荣耀这类设备系统「显示大小」会把 WebView 字号整体放大，
 * 写死 40px 在放大后顶破行高、挤成一团（APK 与手机浏览器的观感差异就在这）。 */
.wd-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 8px; margin-bottom: 6px; }
.wd-word { font-size: clamp(28px, 7.2vw, 40px); font-weight: 800; letter-spacing: 1px; line-height: 1.15; word-break: break-word; }
/* 频次徽标：红/蓝/绿对应书里的高频/中频/基础（阅读规格 D5） */
.wd-tier { padding: 2px 12px; border-radius: 999px; font-size: 13px; font-weight: 700; color: #fff; flex: none; }
.wd-tier.wd-hi { background: #c0392b; }
.wd-tier.wd-mid { background: #1f6fd0; }
.wd-tier.wd-low { background: #4a9d2f; }
/* 文中词形与词条不一致时的说明条（answered / answer） */
.wd-surface {
  display: inline-block; margin: 0 auto 10px; padding: 8px 14px; font-size: 14px;
  background: #fff8ec; border: 1px solid #ffe6bd; border-radius: 10px; color: var(--text-light);
}
.wd-surface b { color: #b5651d; }
.wd-detail .word-meaning { font-size: 20px; }
/* 音标与朗读按钮挤成一行（原来音标一行、56px 大圆钮又一行）。
 * 圆钮在这个上下文里收小到 40px——它旁边有文字撑着，不再是唯一的大目标；
 * 触屏下由 @media (pointer: coarse) 补回 44px 下限（见文件末尾的窄屏/触屏段）。 */
.wd-say { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 8px; margin: 0 0 4px; }
.wd-phon { font-size: 17px; color: var(--text-light); line-height: 1.4; }
/* 圆钮与音标行同高（2026-10-17 用户："播放按钮都小一点，和音标这行高度一样"）。
 * 它旁边永远跟着音标或释义文字，不是页面上唯一的朗读入口，所以可以小到 28px；
 * 触屏下补到 30px（下面 @media (pointer: coarse) 那条），比 44px 的通用下限小，
 * 但它是"点一下就出声"的即时反馈控件、周围 8px 间距也是可点区，实测不难命中。 */
.wd-say .wd-say-btn { width: 28px; height: 28px; font-size: 15px; flex: none; box-shadow: 0 2px 6px rgba(79,124,255,.35); }
.wd-say .btn-mini { flex: none; }
/* 背单词学习卡底部的三个动作：桌面/横屏一行并排（顺序＝主按钮在前）。
 * 窄屏与竖屏的"主按钮整行 + 两个次按钮各占一半"写在文件末尾那两段里。 */
.wc-acts { display: flex; flex-wrap: wrap; justify-content: center; gap: 14px; margin-top: 16px; }
/* ===== 单词查询（2026-10-13）：入口卡 / 搜索框 / 历史列表 / 复习入口 / 答题反馈 ===== */
.wl-entry { display: flex; align-items: center; gap: 14px; cursor: pointer; margin-top: 14px; transition: .12s; }
.wl-entry:hover { box-shadow: 0 4px 16px rgba(79,124,255,.16); transform: translateY(-1px); }
.wl-entry-icon { font-size: 34px; flex: none; }
.wl-entry-text { flex: 1 1 auto; min-width: 0; }
.wl-entry-text h3 { margin: 0 0 4px; }
.wl-entry-text p { margin: 0; font-size: 13px; line-height: 1.5; }
.wl-entry .btn { flex: none; }
.wl-head { max-width: 720px; margin: 0 auto 12px; align-items: center; }
.wl-title { font-size: 18px; }
.wl-head-sp { width: 76px; flex: none; }
.wl-search-row { display: flex; gap: 10px; align-items: center; }
.wl-input {
  flex: 1 1 auto; min-width: 0; padding: 12px 14px; font-size: 16px;
  border: 1px solid var(--border, #dfe4ee); border-radius: 12px; background: #fff; color: var(--text, #222);
}
.wl-input:focus { outline: none; border-color: var(--primary); box-shadow: 0 0 0 3px rgba(79,124,255,.12); }
#wl-result:not(:empty) { margin-top: 14px; }
.wl-notfound {
  background: #fff6e9; border: 1px solid #ffe0b0; border-radius: 12px;
  padding: 14px 16px; font-size: 15px; color: #9a6b1f; line-height: 1.6;
}
.wl-hist-list { display: flex; flex-direction: column; gap: 6px; max-height: 320px; overflow-y: auto; margin-top: 8px; }
.wl-hist-item { display: flex; align-items: center; gap: 10px; padding: 6px 8px; border-radius: 10px; background: #f7f9ff; }
.wl-hist-word {
  flex: 1 1 auto; min-width: 0; text-align: left; background: none; border: none; cursor: pointer;
  font-size: 16px; font-weight: 600; color: var(--primary); padding: 4px 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.wl-hist-word:hover { text-decoration: underline; }
.wl-hist-prog { flex: none; font-size: 12px; color: #2e9e6b; background: #e6f7ee; border-radius: 999px; padding: 2px 9px; }
.wl-hist-del { flex: none; width: 30px; height: 30px; border-radius: 50%; border: none; background: #ffe9e9; color: #d64545; cursor: pointer; font-size: 14px; }
.wl-hist-del:hover { background: #ffd6d6; }
.wl-more { display: block; width: 100%; margin-top: 8px; padding: 10px; border: 1px dashed var(--border, #dfe4ee); border-radius: 10px; background: #fbfcff; color: var(--primary); font-size: 14px; cursor: pointer; }
.wl-more:hover { background: #f2f6ff; }
.wl-review-row { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-top: 10px; }
.wl-review-label { font-size: 14px; color: var(--text-light); }
.wl-input.wl-n { flex: none; width: 92px; text-align: center; }
.wl-fb { border-radius: 12px; padding: 12px 16px; font-size: 15px; margin-top: 14px; text-align: center; }
.wl-fb.ok { background: #e6f7ee; color: #1f7a4d; }
.wl-fb.bad { background: #ffecec; color: #c0392b; }
/* 常用短语：可点读胶囊 */
.phrase-box { margin-top: 12px; display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; }
.phrase-chip {
  cursor: pointer; background: #e8efff; border: 1px solid #cfdcff; border-radius: 999px;
  padding: 7px 14px; font-size: 14px; color: #3557c9; transition: .12s; user-select: none;
}
.phrase-chip:hover { background: #dbe7ff; transform: translateY(-1px); box-shadow: 0 3px 8px rgba(79,124,255,.18); }
.phrase-chip:active { transform: scale(.96); }
.speak-btn {
  width: 56px; height: 56px; border-radius: 50%; background: var(--primary); color: #fff;
  font-size: 24px; display: inline-flex; align-items: center; justify-content: center;
  box-shadow: 0 4px 12px rgba(79,124,255,.4); transition: .1s;
}
.speak-btn:active { transform: scale(.9); }
.quiz-options { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-top: 20px; }
.quiz-opt {
  padding: 16px; border-radius: 14px; background: #f2f5fd; font-size: 17px; font-weight: 600;
  border: 3px solid transparent; transition: .12s;
}
.quiz-opt:hover { border-color: var(--primary); }
.quiz-opt.correct { background: #e2f8ee; border-color: var(--green); }
.quiz-opt.wrong { background: #ffe9e9; border-color: var(--red); }
.spell-input { font-size: 24px !important; text-align: center; letter-spacing: 3px; width: 280px; }

/* ===== 数学计算练习 ===== */
.math-section { margin-top: 14px; }
.math-sec-title { font-weight: 700; font-size: 16px; color: var(--primary); margin: 12px 0 8px; padding-left: 10px; border-left: 5px solid var(--primary); }
/* 分区色带：口算蓝 / 竖式橙 / 单位绿（与阅读/语文的色带语言一致） */
.math-section.sec-blue .math-sec-title { color: var(--primary); border-left-color: var(--primary); }
.math-section.sec-orange .math-sec-title { color: #d9822b; border-left-color: var(--orange); }
.math-section.sec-green .math-sec-title { color: #1d9e5f; border-left-color: var(--green); }
.math-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 10px; }
.math-item {
  display: flex; align-items: center; gap: 8px; background: #f7f9ff; border-radius: 12px;
  padding: 10px 12px; border: 2px solid transparent; transition: .12s;
}
.math-item.ok { background: #edfbf3; border-color: var(--green); }
.math-item.bad { background: #fff5f5; border-color: var(--red); }
.math-item .mq { flex: 1; font-size: 15px; line-height: 1.5; }
/* 两行式题目：题面一行、输入+反馈一行——窄屏上不会再挤成三段 */
.math-item2 { display: block; }
.mq2 { font-size: 15px; line-height: 1.6; display: flex; gap: 8px; align-items: baseline; }
.mno { flex: none; display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; border-radius: 50%; background: var(--primary-light);
  color: var(--primary); font-size: 12.5px; font-weight: 700; }
.mrow2 { display: flex; align-items: center; gap: 10px; margin-top: 8px; }
.mrow2 .mgiven { font-size: 15px; font-weight: 700; min-width: 64px; }
.math-input {
  width: 104px; padding: 8px 10px; font-size: 16px; text-align: center;
  border: 2px solid #dde3f0; border-radius: 8px; background: #fff;
}
.math-input:focus { border-color: var(--primary); }
.math-item .mres { font-size: 13px; color: var(--text-light); min-width: 0; }
.math-item.bad .mres { color: var(--red); font-weight: 700; }
.math-item.ok .mres { color: var(--green); font-weight: 700; }
/* 卷面进度条 */
.mprog-row { display: flex; align-items: center; gap: 10px; margin: 10px 0 4px; }
.mbar { flex: 1; height: 8px; border-radius: 99px; background: #e8efff; overflow: hidden; }
.mbar i { display: block; height: 100%; background: linear-gradient(90deg, var(--primary), #7aa3ff); transition: width .15s; }
.mprog { font-size: 13px; color: var(--text-light); white-space: nowrap; }
/* 首页头卡与分区胶囊 */
.mh-hero { background: linear-gradient(135deg, #e8efff 0%, #f6fbff 60%, #fff3e6 100%); }
.mh-body { display: flex; align-items: center; gap: 14px; margin: 12px 0; }
.mh-icon { display: inline-flex; align-items: center; justify-content: center; flex: none;
  width: 64px; height: 64px; border-radius: 50%; background: #fff; font-size: 34px; box-shadow: var(--shadow); }
.mh-info { min-width: 0; }
.mh-title { font-size: 20px; font-weight: 800; }
.mh-chips { display: flex; flex-wrap: wrap; gap: 8px; }
.mchip { display: inline-block; background: #fff; border: 2px solid #e3edff; border-radius: 999px;
  padding: 4px 12px; font-size: 13px; font-weight: 600; }
.mchip.sec-blue { border-color: #cfe0ff; color: var(--primary); }
.mchip.sec-orange { border-color: #ffe0be; color: #d9822b; }
.mchip.sec-green { border-color: #c6f0da; color: #1d9e5f; }

/* "已会"按钮：绿色系，表示已掌握不再考 */
.known-btn { background: #e2f8ee; color: #1d9e5f; }
.known-btn:hover { background: #d2f2e2; }

/* ===== 语文：古诗文与词语 ===== */
.zh-learn { border-left: 5px solid #a06bff; }
.zh-lines { background: #faf7ff; border-radius: 12px; padding: 14px 18px; margin: 10px 0; text-align: center; }
/* 古文长文改左对齐：逐行居中会让折行参差不齐 */
.zh-lines-left { text-align: left; }
.zh-line { font-size: 19px; line-height: 1.9; letter-spacing: 2px; font-family: "KaiTi", "STKaiti", "PingFang SC", serif; }
/* 原文与译文成组对齐：每句原文下面紧跟它自己的译文（不再要孩子自己数行对应） */
.zh-pair { padding: 4px 0; }
.zh-pair + .zh-pair { border-top: 1px dashed #e8e0ff; }
.zh-tr { font-size: 13.5px; color: var(--text-light); line-height: 1.7; letter-spacing: 0; }
.zh-sub { display: block; font-size: 13px; color: var(--text-light); margin: 10px 0 4px; }
.zh-more { margin-top: 10px; font-size: 14px; }
.zh-more summary { cursor: pointer; color: var(--primary); font-size: 14px; }
.zh-more > *:not(summary) { margin-top: 6px; }
.zh-trans { background: #f4f7ff; border-radius: 12px; padding: 12px 16px; font-size: 14px; color: var(--text-light); line-height: 1.9; }
.zh-notes { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; align-items: center; }
.note-chip { background: #f1e9ff; color: #6b3fd4; border-radius: 999px; padding: 5px 12px; font-size: 13px; }
.zh-word { border-bottom: 1px dashed #e4e9f5; padding: 12px 0; }
.zh-word:last-child { border-bottom: none; }
.zh-w { font-size: 19px; }
.zh-mean { margin-top: 6px; }
.zh-eg { background: #f7f9ff; border-radius: 10px; padding: 8px 12px; margin-top: 6px; font-size: 14px; }
/* 两阶段页头 / 开始检测卡 / 原文回看 */
.zh-stagebar { background: linear-gradient(135deg, #f1e9ff 0%, #f8f5ff 65%, #eef4ff 100%); }
.zh-startcard { border: 2px dashed #d9c9ff; }
.zh-resrc { border-bottom: 1px dashed #e4e9f5; padding: 8px 0; }
.zh-resrc:last-child { border-bottom: none; }
/* 语文首页头卡（与数学/阅读同一套语言） */
.zh-hero { background: linear-gradient(135deg, #f1e9ff 0%, #f8f5ff 55%, #eef4ff 100%); }
.zh-hero-body { display: flex; align-items: center; gap: 14px; margin: 12px 0; }
.zh-hero-icon { display: inline-flex; align-items: center; justify-content: center; flex: none;
  width: 64px; height: 64px; border-radius: 50%; background: #fff; font-size: 34px; box-shadow: var(--shadow); }
.zh-hero-info { min-width: 0; }
.zh-hero-title { font-size: 20px; font-weight: 800; }
.mchip.sec-purple { border-color: #e0d0ff; color: #7b4bd8; }
.math-section.sec-purple .math-sec-title { color: #7b4bd8; border-left-color: #a06bff; }
.zh-grid { display: grid; grid-template-columns: 1fr; gap: 12px; }
.zh-item { background: #f7f9ff; border-radius: 12px; padding: 12px 14px; border: 2px solid transparent; }
.zh-item.ok { background: #edfbf3; border-color: var(--green); }
.zh-item.bad { background: #fff5f5; border-color: var(--red); }
.zh-q { font-size: 15px; line-height: 1.7; }
.zh-input { width: 100%; margin-top: 8px; padding: 10px 12px; font-size: 17px; letter-spacing: 2px; }
.zh-opts { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 8px; margin-top: 8px; }
.zh-opts .quiz-opt { font-size: 15px; padding: 12px; }
.zh-opts .quiz-opt.selected { background: var(--primary-light); border-color: var(--primary); }
.zh-res { font-size: 13px; margin-top: 6px; }
.zh-item.bad .zh-res { color: var(--red); font-weight: 700; }
.zh-item.ok .zh-res { color: var(--green); font-weight: 700; }

/* ===== 宠物 ===== */
.pet-stage {
  background: linear-gradient(180deg, #cde9ff 0%, #e8f7e0 70%, #d4f0c0 100%);
  /* 顶部留 52px：3D 模型的帽子/独角兽的角在头之上，留窄了会被 overflow 裁掉。
   * 底部留 54px：草地带（.pet-ground 高 44px）里要站布景（.pet-decor），再留 10px 透气——
   * 底部留白小于草地带时，在流内容（快捷按钮/提示文字）会落进草地与布景重叠（实测彩虹盖住提示字）。 */
  border-radius: 24px; padding: 52px 30px 54px; text-align: center; position: relative; min-height: 360px;
  overflow: hidden;
}
.pet-body {
  font-size: 110px; display: inline-block; position: relative; z-index: 2;
  animation: petBounce 2.4s ease-in-out infinite; cursor: pointer; user-select: none;
  transform-origin: 50% 85%;   /* 形变以脚为支点，动作才像"站在地上" */
  will-change: transform;
}
@keyframes petBounce { 0%,100% { transform: translateY(0); } 50% { transform: translateY(-12px); } }
.pet-body.sad { animation: petSad 3s ease-in-out infinite; filter: grayscale(.5); }
@keyframes petSad { 0%,100% { transform: translateY(0) rotate(-3deg); } 50% { transform: translateY(2px) rotate(3deg); } }

/* ---- 待机动作：JS 只切类名，形变交给下面这些关键帧 ---- */
@keyframes petJump { 0%,100% { transform: translateY(0) scale(1,1); } 30% { transform: translateY(-30px) scale(.94,1.06); } 65% { transform: translateY(0) scale(1.06,.94); } }
@keyframes petSpin { 0% { transform: rotate(0) scale(1); } 50% { transform: rotate(180deg) scale(1.1); } 100% { transform: rotate(360deg) scale(1); } }
@keyframes petStretch { 0%,100% { transform: scale(1,1); } 40% { transform: scale(.9,1.16); } 70% { transform: scale(1.07,.94); } }
@keyframes petWag { 0%,100% { transform: rotate(-8deg); } 50% { transform: rotate(8deg); } }
@keyframes petPeek { 0%,100% { transform: translateX(0) rotate(0); } 25% { transform: translateX(-16px) rotate(-5deg); } 75% { transform: translateX(16px) rotate(5deg); } }
@keyframes petThink { 0%,100% { transform: rotate(0); } 30% { transform: rotate(-13deg); } 70% { transform: rotate(7deg); } }
@keyframes petRead { 0%,100% { transform: translateY(0) rotate(-2deg); } 50% { transform: translateY(-7px) rotate(2deg); } }
@keyframes petYawn { 0%,100% { transform: scale(1,1); } 50% { transform: scale(1.16,.9) rotate(-3deg); } }
@keyframes petCheer { 0%,100% { transform: translateY(0) rotate(0); } 25% { transform: translateY(-20px) rotate(-11deg); } 75% { transform: translateY(-20px) rotate(11deg); } }
@keyframes petNap { 0%,100% { transform: translateY(0) scale(1,1); } 50% { transform: translateY(5px) scale(1.04,.96); } }
/* 动作时长与 JS 记录的 ms 区间对应：播完由 JS 摘掉类，回到基础呼吸动画 */
.pet-body.act-jump { animation: petJump .45s ease-in-out 2; }
.pet-body.act-spin { animation: petSpin 1.1s cubic-bezier(.45,0,.3,1) 1; }
.pet-body.act-stretch { animation: petStretch .9s ease-in-out 2; }
.pet-body.act-wag { animation: petWag .35s ease-in-out 4; }
.pet-body.act-peek { animation: petPeek .7s ease-in-out 2; }
.pet-body.act-think { animation: petThink .8s ease-in-out 2; }
.pet-body.act-read { animation: petRead 1.1s ease-in-out 2; }
.pet-body.act-yawn { animation: petYawn .8s ease-in-out 2; }
.pet-body.act-cheer { animation: petCheer .6s ease-in-out 3; }
.pet-body.act-nap { animation: petNap 1.6s ease-in-out 2; }
/* 情绪低落时不播活泼动作（JS 也只挑 calm 类），这里再兜一层 */
.pet-body.sad.act-jump, .pet-body.sad.act-spin, .pet-body.sad.act-cheer, .pet-body.sad.act-wag {
  animation: petSad 3s ease-in-out infinite;
}

/* 脚下阴影：随呼吸一起缩放，宠物"站得住"就靠它 */
.pet-shadow {
  width: 96px; height: 14px; margin: 2px auto 0; border-radius: 50%;
  background: radial-gradient(ellipse at center, rgba(60,90,60,.26), rgba(60,90,60,0) 70%);
  animation: petShadow 2.4s ease-in-out infinite;
}
@keyframes petShadow { 0%,100% { transform: scaleX(1); opacity: .8; } 50% { transform: scaleX(.86); opacity: .55; } }

.pet-name-tag { font-size: 20px; font-weight: 800; margin-top: 6px; }
/* 名字标签是 flex（为了让名字与 Lv./状态药丸同一条中心线），但 flex 容器不再受父级
 * text-align:center 管——首页那张卡的名字行会贴左（2026-10-18 用户："宠物名称那行文字没居中"）。
 * 两处用到它的地方（首页卡 / 宠物页状态卡顶部）都是要居中的，所以直接写死 justify-content。 */
.pet-name-tag { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; justify-content: center; }
.pet-dock .pet-name-tag { margin-top: 0; margin-bottom: 2px; }
.pet-mood { font-size: 14px; margin-top: 4px; }
.pet-status-bars { display: flex; gap: 20px; justify-content: center; margin-top: 16px; }
.pet-stat { width: 150px; text-align: left; }
.pet-stat .label { font-size: 12px; color: var(--text-light); margin-bottom: 4px; }

/* ---- 宠物台词气泡：用 transition 显隐（不重播关键帧就不会闪） ----
 * 气泡只占左侧 44%：右边留给宠物头顶的帽子/独角兽角，
 * 用 right:56% 而不是 max-width，是为了保证气泡右缘永远压不到宠物中轴那一列 */
.pet-bubble {
  position: absolute; left: 14px; right: 56%; top: 12px; max-width: none; z-index: 6;
  transform: translateY(8px) scale(.92);
  opacity: 0; pointer-events: none; cursor: pointer;
  transition: opacity .26s ease, transform .26s cubic-bezier(.2,1.5,.5,1);
  background: #fff; color: var(--text); border-radius: 16px; padding: 10px 16px;
  font-size: 14px; line-height: 1.6; text-align: left;
  box-shadow: 0 8px 22px rgba(60,90,140,.18);
}
.pet-bubble.show { opacity: 1; transform: translateY(0) scale(1); pointer-events: auto; }
.pet-bubble::after {
  content: ''; position: absolute; bottom: -7px; right: 34px;
  border: 7px solid transparent; border-top-color: #fff; border-bottom: 0;
}

/* ---- 舞台装饰层：太阳、飘云、草地（纯装饰，不拦点击） ---- */
.pet-sky { position: absolute; inset: 0; overflow: hidden; pointer-events: none; }
.pet-sun { position: absolute; right: 20px; top: 14px; font-size: 36px; animation: sunGlow 4.5s ease-in-out infinite; }
@keyframes sunGlow { 0%,100% { transform: scale(1); opacity: .85; } 50% { transform: scale(1.1); opacity: 1; } }
.pet-cloud { position: absolute; left: -60px; opacity: .9; animation-name: cloudDrift; animation-timing-function: linear; animation-iteration-count: infinite; }
@keyframes cloudDrift { from { transform: translateX(0); } to { transform: translateX(640px); } }
.pet-ground {
  position: absolute; left: 0; right: 0; bottom: 0; height: 44px; z-index: 1;
  background: linear-gradient(180deg, #b7e69b, #8ed473); border-radius: 0 0 24px 24px;
}
/* 布景要整只站在 44px 的草地带里：bottom 4px + 30px 字号 ≈ 40px 高，不再上探进内容区 */
.pet-decor { position: absolute; bottom: 4px; z-index: 1; font-size: 30px; animation: decorFloat 3.4s ease-in-out infinite; }
@keyframes decorFloat { 0%,100% { transform: translateY(0); } 50% { transform: translateY(-6px); } }

/* 点击宠物时飘出的表情 */
.pet-pop {
  position: absolute; top: 26%; font-size: 28px; z-index: 7; pointer-events: none;
  animation: petPop .95s ease-out forwards;
}
@keyframes petPop { 0% { opacity: 0; transform: translateY(6px) scale(.6); } 30% { opacity: 1; transform: translateY(-10px) scale(1.15); } 100% { opacity: 0; transform: translateY(-52px) scale(.9); } }

/* 宠物页快捷互动按钮 */
.pet-quick { display: flex; gap: 10px; justify-content: center; margin-top: 14px; flex-wrap: wrap; }

/* 首页宠物卡片里的"宠物说的话" */
.pet-say-home {
  margin: 10px auto 6px; max-width: 92%; background: #fff; border-radius: 14px; padding: 10px 14px;
  font-size: 13px; line-height: 1.6; text-align: left; box-shadow: 0 6px 16px rgba(60,90,140,.12);
  position: relative;
}
.pet-say-home::before {
  content: ''; position: absolute; top: -7px; left: 50%; margin-left: -7px;
  border: 7px solid transparent; border-bottom-color: #fff; border-top: 0;
}

/* 宠物舞台：拖拽转身在 .pet-stage 上，3D 画布放在 .pet-body 里。 */
.pet-stage { perspective: 1000px; perspective-origin: 50% 42%; }
/* 舞台是可以按住拖拽的：左右拖让宠物转身（360° 看一圈），上下拖抬高/压低视角。
 *  - cursor: grab/grabbing 让孩子一眼看出这里能拖
 *  - touch-action: pan-y —— 手机上允许上下滑页面，横向的拖动留给"转身"，
 *    否则手指按在宠物上就再也划不动页面了 */
.pet-stage { cursor: grab; touch-action: pan-y; }
.pet-stage.pm-dragging { cursor: grabbing; }
.pet-stage.pm-dragging .pet-bubble { pointer-events: none; }   /* 拖拽中别让气泡抢事件 */

/* 3D 画布的盒子。外层跳动在 .pm-3d 上关掉，避免透明画布被合成成白色。 */
.pet-body.pm-act {
  display: block; width: 220px; height: 212px; margin: 0 auto;
  position: relative; font-size: 0; line-height: 0;
  transform-style: preserve-3d;
}
/* ---------- 道具特效层 ---------- */
.pm-fly {
  position: absolute; font-size: 30px; z-index: 9; pointer-events: none;
  transition: transform .68s cubic-bezier(.4,-0.3,.6,1.3), opacity .68s ease-in;
  filter: drop-shadow(0 3px 6px rgba(0,0,0,.2));
}
.pm-float {
  position: absolute; z-index: 9; pointer-events: none; white-space: nowrap;
  font-size: 15px; font-weight: 800; color: #e2890b;
  text-shadow: 0 1px 0 #fff, 0 2px 6px rgba(0,0,0,.12);
  animation: pmFloatUp 1.5s ease-out forwards;
}
@keyframes pmFloatUp {
  0% { opacity: 0; transform: translate(-50%,6px) scale(.7); }
  22% { opacity: 1; transform: translate(-50%,-6px) scale(1.08); }
  100% { opacity: 0; transform: translate(-50%,-58px) scale(1); }
}
.pm-spark, .pm-note, .pm-bubble, .pm-crumb {
  position: absolute; z-index: 9; pointer-events: none; font-size: 20px;
  animation: pmParticle 1.1s ease-out forwards;
}
.pm-note { font-size: 24px; color: #7b5ea7; }
.pm-crumb { font-size: 14px; }
@keyframes pmParticle {
  0% { opacity: 0; transform: translate(-50%,0) scale(.4); }
  25% { opacity: 1; }
  100% { opacity: 0; transform: translate(calc(-50% + var(--dx, 0px)), var(--dy, -70px)) scale(1.1); }
}
/* 泡泡慢慢往上飘，比其它粒子久一点 */
.pm-bubble { animation: pmBubbleUp 1.6s ease-out forwards; }
@keyframes pmBubbleUp {
  0% { opacity: 0; transform: translate(-50%,0) scale(.3); }
  20% { opacity: .95; }
  100% { opacity: 0; transform: translate(calc(-50% + var(--dx, 0px)), var(--dy, -110px)) scale(1.25); }
}
/* 玩具球从左弹到右，带弹跳 */
.pm-ball {
  position: absolute; font-size: 30px; z-index: 9; pointer-events: none;
  transition: transform 1.05s cubic-bezier(.3,.1,.7,1);
  animation: pmBallHop .26s ease-in-out 4;
}
@keyframes pmBallHop { 0%,100% { margin-top: 0; } 50% { margin-top: -22px; } }
/* 家园场景刚买回来时的弹入 */
.pet-decor.pm-decor-in { animation: pmDecorIn .8s cubic-bezier(.2,1.5,.4,1) 1, decorFloat 3.4s ease-in-out .8s infinite; }
@keyframes pmDecorIn {
  0% { opacity: 0; transform: scale(.2) rotate(-25deg); }
  60% { opacity: 1; transform: scale(1.22) rotate(6deg); }
  100% { opacity: 1; transform: scale(1) rotate(0); }
}

/* 首页卡片里的小号模型：留出固定高度，别把卡片撑长 */
.pm-home { height: 210px; display: flex; align-items: flex-end; justify-content: center; overflow: hidden; position: relative; }

/* ================= 3D 宠物（three.js + Kenney 模型，见 js/pet3d.js）=================
 * 画布绝对定位在盒子里，尺寸跟 CSS 版一致（220×212），舞台排版不用改。
 * WebGL 可用时不再先垫 CSS 模型。画布 visibility 由脚本控制：第一帧之前是 hidden。
 * .pm-3d：3D 已经在画了，关掉 .pet-body 的跳动。那个 transform 会把透明画布合成成白色。 */
.pet-canvas-el {
  position: absolute; left: 0; top: 0; width: 100%; height: 100%;
  display: block; pointer-events: none;      /* 点宠物仍然由外层 .pet-body 接 */
}
/* 动作类选择器（.pet-body.pm-act.act-spin 等）比这里多一个类，不用 important 压不住，
 * 跳动一回来透明画布又会变白。3D 模型自己有待机和动作，外层不要再做 transform。 */
.pet-body.pm-3d { animation: none !important; will-change: auto; }
/* 3D 加载失败时的平面头像，不是以前那套 CSS 拼出来的宠物 */
.pet-flat { width: 168px; height: 168px; object-fit: contain; display: block; margin: 12px auto 0; }
.pm-home .pet-flat { width: 150px; height: 150px; margin: 0 auto; }
/* 首页小号版：模型本身由相机拉远缩小，这里只把 canvas 压到卡片内 */
.pm-home .pet-canvas-el { position: absolute; left: 0; right: 0; bottom: 0; }
.pet-body.pm-act .pet-canvas-el { width: 220px; height: 212px; }

/* 领养页：上面一只大预览，下面 24 个品种卡（2D 扁平头像） */
.adopt-stage { height: 240px; position: relative; margin: 4px auto 10px; }
.adopt-stage .pet-canvas-el { position: absolute; left: 0; top: 0; width: 100%; height: 100%; }
.adopt-pick { display: grid; grid-template-columns: repeat(8, 1fr); gap: 6px; max-width: 700px; margin: 0 auto; }
.adopt-card { padding: 4px 2px 6px; overflow: hidden; }
/* 2D 头像：平铺 PNG，去掉白色描边感；选中态由 .inv-item.equipped 的绿底表现 */
.adopt-img { width: 100%; max-width: 64px; height: 64px; object-fit: contain; display: block; margin: 0 auto; }
.adopt-img.cur { filter: drop-shadow(0 0 3px rgba(29,158,95,.55)); }
.adopt-img.big { width: 132px; height: 132px; }
.adopt-pick .ii-name { font-size: 11px; margin-top: 2px; line-height: 1.2; }
/* 重新领养的弹窗里也用同一套卡片，但弹窗窄，列数少一点 */
.modal-pick { grid-template-columns: repeat(6, 1fr); max-width: 100%; }

/* ---------- 宠物园（宠物页）：主宠 + 园子里的宠物，可切换 ---------- */
.park-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(104px, 1fr)); gap: 10px; }
.park-card {
  background: #f7f9ff; border-radius: 14px; padding: 8px 6px; text-align: center;
  border: 2px solid transparent; position: relative;
}
.park-card.active { background: #edfbf3; border-color: var(--green); }
.park-card.add { cursor: pointer; display: flex; flex-direction: column; align-items: center; justify-content: center; min-height: 118px; border: 2px dashed #c8d4ea; }
.park-card.add.locked { cursor: default; opacity: .72; border-style: dashed; background: #f4f6fb; }
.pc-img { width: 56px; height: 56px; object-fit: contain; display: block; margin: 0 auto; }
.pc-name { font-size: 13px; font-weight: 700; margin-top: 2px; }
.pc-sub { font-size: 11px; color: var(--text-light); line-height: 1.35; }
.pc-lock { font-size: 26px; }
.pc-btns { display: flex; gap: 4px; justify-content: center; margin-top: 4px; flex-wrap: wrap; }

/* ---------- 宠物头像（顶栏 / 名字标签）：2D 图 + 状态角标 ---------- */
.pa-wrap { position: relative; display: inline-block; vertical-align: middle; }
.pa-img { width: 100%; height: 100%; object-fit: contain; display: block; }
.pa-badge {
  position: absolute; right: -4px; top: -4px; font-size: 13px; line-height: 1;
  filter: drop-shadow(0 1px 1px rgba(0,0,0,.25));
}
.tag-avatar { display: inline-block; width: 30px; height: 30px; vertical-align: middle; margin-right: 6px; }
.tag-avatar .pa-img { filter: drop-shadow(0 1px 2px rgba(0,0,0,.14)); }
.pet-mini .pa-wrap { width: 30px; height: 30px; }
.pet-mini img { width: 30px; height: 30px; object-fit: contain; }

/* ---------- 家长中心：设置入口卡（每一项点开是弹窗） ---------- */
.set-entries { display: flex; flex-direction: column; gap: 8px; }
.set-entry {
  display: flex; align-items: center; gap: 12px; cursor: pointer;
  background: #f7f9ff; border: 2px solid transparent; border-radius: 14px; padding: 10px 12px; transition: .12s;
}
.set-entry:hover { border-color: var(--primary); background: #fff; transform: translateY(-1px); box-shadow: 0 4px 10px rgba(79,124,255,.12); }
.set-entry.danger { background: #fff5f5; }
.set-entry.danger:hover { border-color: var(--red); box-shadow: 0 4px 10px rgba(230,80,80,.14); }
.se-icon { font-size: 24px; }
.se-body { flex: 1; min-width: 0; }
.se-name { font-weight: 700; font-size: 15px; }
.se-desc { font-size: 12px; color: var(--text-light); line-height: 1.5; }
.se-arrow { font-size: 22px; color: #b7c3d8; }

/* ---------- 家长中心：安卓安装包弹窗（2026-10-16，规格 FR-9）----------
 * 二维码那张 svg 用固定边长 + max-width:100%（不给"固定高 + width:100%"那种会被
 * SVG meet 对齐拉出上下空白的写法——§7 插图那条坑同一类）。 */
.apk-info { display: flex; flex-direction: column; gap: 6px; margin: 4px 0 12px; }
.apk-row { display: flex; gap: 10px; align-items: baseline; font-size: 14px; }
.apk-row > span { color: var(--text-light); min-width: 76px; }
.apk-row > b { font-size: 15px; }
.apk-note {
  background: #fff8e6; border: 1px solid #f2d99a; border-radius: 12px;
  padding: 8px 10px; font-size: 13px; line-height: 1.6;
}
/* 壳里读到"线上包比本机新"时的那条更新提示（绿色，跟上面的黄色告警区分开） */
.apk-note-new {
  background: #e9f8ef; border-color: #b7e3c6; color: #1f7a4d;
}
.apk-dl { display: block; width: 100%; text-align: center; margin: 10px 0 4px; font-size: 16px; padding: 12px 14px; }
.apk-qr { display: flex; flex-direction: column; align-items: center; gap: 6px; margin: 14px 0 10px; }
.apk-qr svg { width: 260px; height: 260px; max-width: 100%; border: 1px solid #e6ecfa; border-radius: 12px; background: #fff; }
.apk-qr-tip { font-size: 12px; color: var(--text-light); text-align: center; line-height: 1.7; }
.apk-tips { font-size: 13px; line-height: 1.7; }
.apk-tips p { margin: 2px 0; }
.apk-mono {
  font-family: ui-monospace, Consolas, monospace; font-size: 12px; word-break: break-all;
  background: #f7f9ff; border-radius: 8px; padding: 6px 8px;
}
.apk-urlline { font-size: 11px; }

/* ===== 每日学习量（家长中心弹窗，2026-09-28 重做：分科卡片 + 步进器）===== */
.ss-top { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 12px; }
.ss-top .mchip { background: #f7f9ff; }
.ss-card {
  border: 1px solid #edf1fb; border-radius: 16px; padding: 12px 16px; margin-bottom: 12px;
  background: #fff; border-left: 5px solid var(--primary);
}
.ss-card.t-eng { border-left-color: #4f7cff; }
.ss-card.t-math { border-left-color: #ff9f43; }
.ss-card.t-zh { border-left-color: #a06bff; }
.ss-card.t-misc { border-left-color: #34c77b; }
.ss-head { display: flex; align-items: center; gap: 8px; margin-bottom: 4px; }
.ss-name { font-weight: 800; font-size: 15px; }
.ss-badge { font-size: 11px; font-weight: 700; padding: 2px 9px; border-radius: 999px; }
.ss-badge.ok { background: #e2f8ee; color: #1d9e5f; }
.ss-badge.soon { background: #eef1f8; color: var(--text-light); }
.ss-row { display: flex; align-items: center; gap: 12px; padding: 8px 0; border-top: 1px dashed #eef1f8; }
.ss-head + .ss-row, .ss-row:first-of-type { border-top: none; }
.ss-info { flex: 1 1 auto; min-width: 0; }
.ss-lab { font-size: 14.5px; font-weight: 600; }
.ss-hint { font-size: 12px; color: var(--text-light); line-height: 1.5; }
/* "已改动"小旗：默认不占位、不显示，行上有 .changed 才出现（避免行高跳动） */
.ss-flag { display: none; margin-left: 8px; font-size: 11px; font-weight: 700; color: #d9822b;
  background: #fff1e6; border-radius: 999px; padding: 1px 8px; vertical-align: middle; }
.ss-ctrl { display: flex; align-items: center; gap: 6px; flex: none; }
.ss-step {
  width: 30px; height: 30px; border-radius: 10px; background: #eef3ff; color: var(--primary);
  font-size: 17px; font-weight: 800; line-height: 1; display: inline-flex; align-items: center; justify-content: center;
}
.ss-step:hover { background: #e0e9ff; }
/* 注意权重：全局有 `input[type=text], input[type=number] { padding: 10px 14px; font-size: 16px }`
 * （0,1,1），比单纯一个类 `.ss-input`（0,1,0）高——所以这里必须写 `input.ss-input`，
 * 否则 padding 被全局规则顶掉，58px 的框里 "12" 会被裁成 "1"（真出现过）。
 * 字号仍保持 16px：iOS 对 <16px 的输入框聚焦时会放大整页（项目既有约定，别为了紧凑破例）。 */
input.ss-input {
  width: 64px; padding: 6px 4px; font-size: 16px; text-align: center;
  border: 2px solid #dde3f0; border-radius: 10px; background: #fff;
}
input.ss-input:focus { border-color: var(--primary); }
.ss-unit { font-size: 13px; color: var(--text-light); width: 16px; }
/* 下拉同样写全选择器（`.ss-select` 单类也压不过全局 select 的 font-size 规则） */
select.ss-select { flex: none; max-width: 60%; padding: 7px 10px; font-size: 16px;
  border: 2px solid #dde3f0; border-radius: 10px; background: #fff; }
/* 本次改过的行高亮，保存前一眼看出动了哪几项 */
.ss-row.changed { background: #fffaf0; margin: 0 -8px; padding-left: 8px; padding-right: 8px; border-radius: 10px; }
.ss-row.changed .ss-input { border-color: var(--orange); }
.ss-row.changed .ss-flag { display: inline-block; }
/* 开关（故事生词排进复习等布尔项） */
.ss-switch { position: relative; display: inline-block; width: 50px; height: 28px; flex: none; }
.ss-switch input { opacity: 0; width: 0; height: 0; }
.ss-switch i { position: absolute; inset: 0; background: #dde3f0; border-radius: 999px; transition: .15s; }
.ss-switch i::after { content: ''; position: absolute; top: 3px; left: 3px; width: 22px; height: 22px;
  border-radius: 50%; background: #fff; box-shadow: 0 1px 3px rgba(0,0,0,.18); transition: .15s; }
.ss-switch input:checked + i { background: var(--green); }
.ss-switch input:checked + i::after { transform: translateX(22px); }

/* 3D 画布不要被 .pet-body.sad 的灰度滤镜包住，滤镜会把透明背景铺脏。 */
.pet-body.pm-act.sad { filter: none; }
.inventory-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; }
.inv-item {
  background: #f7f9ff; border-radius: 14px; padding: 12px 8px; text-align: center;
  border: 2px solid transparent; cursor: pointer; transition: .12s; position: relative;
}
.inv-item:hover { border-color: var(--primary); }
.inv-item.equipped { border-color: var(--green); background: #edfbf3; }
.inv-item .ii-icon { font-size: 32px; }
.inv-item .ii-name { font-size: 12px; margin-top: 4px; }
.inv-item .ii-count { position: absolute; top: 4px; right: 8px; font-size: 11px; background: var(--orange); color: #fff; border-radius: 999px; padding: 0 6px; }

/* ===== 商城 ===== */
.shop-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; }
.shop-item {
  background: #f7f9ff; border-radius: 16px; padding: 16px 12px; text-align: center;
  position: relative; border: 2px solid transparent; transition: transform .15s, box-shadow .15s, border-color .15s;
}
.shop-item:hover { transform: translateY(-3px); box-shadow: 0 10px 22px rgba(80,110,200,.15); border-color: #dbe6ff; }
.shop-item .si-icon { font-size: 40px; }
.shop-item .si-name { font-weight: 700; margin: 8px 0 2px; }
.shop-item .si-desc { font-size: 12px; color: var(--text-light); min-height: 22px; }
/* 说明行原来按两行预留 32px，而商城里绝大多数只有一行（12px 字号、行高约 18px），
 * 于是"商品名/说明"与下面那颗 ⭐积分"之间白留 14px，加上 .si-price 自己的 6px 上边距 = 20px 空隙。
 * 第十九轮（2026-10-18 用户："商品和下面需要的积分距离缩小到原来的一半"）收到 22px → 空隙 10px。
 * 两行的说明仍然放得下（块会自己长高，不会被裁）。窄屏/竖屏有自己那套固定行高，不受这里影响。 */
.shop-item .si-price { color: #b08900; font-weight: 800; margin: 6px 0 10px; }
/* 已拥有的永久商品压暗但保持可读（hover 时恢复，方便看清是什么） */
.shop-item.owned { opacity: .6; }
.shop-item.owned:hover { opacity: 1; }
/* 消耗品持有数量角标 */
.shop-item .si-count {
  position: absolute; top: 8px; right: 8px; font-size: 11px; background: var(--orange);
  color: #fff; border-radius: 999px; padding: 1px 8px; font-weight: 700;
}
/* 「还差 N 分」那颗 2026-10-18 第十七轮按用户要求撤掉（买不起时那一格留空），
   动作行统一由 .si-act 占位，见文件末尾"商城逐行对齐"那一段 */
/* 商城头部永久收藏进度 */
.shop-progress { margin-top: 12px; }
.shop-progress .progress-bar { height: 8px; }

/* ===== 错题本 ===== */
.err-item {
  display: flex; align-items: center; gap: 14px; padding: 12px 16px; background: #fff8f0;
  border-radius: 12px; margin-bottom: 8px; border-left: 4px solid var(--orange);
}
.err-item .err-main { flex: 1; }
.err-word { font-weight: 800; font-size: 16px; }
.err-detail { font-size: 13px; color: var(--text-light); margin-top: 2px; }

/* ===== 生活打卡 ===== */
.checkin-card {
  border-radius: 18px; padding: 20px; text-align: center; background: #f7f9ff;
  border: 3px solid transparent; transition: .15s;
}
.checkin-card.done { background: #edfbf3; border-color: var(--green); }
.checkin-card .ck-icon { font-size: 44px; }
.checkin-card .ck-name { font-weight: 700; margin: 8px 0 2px; }
.checkin-card .ck-desc { font-size: 12px; color: var(--text-light); margin-bottom: 12px; }
/* 生活打卡首页头卡（与数学/语文/阅读同一套视觉语言） */
.lf-hero { background: linear-gradient(135deg, #e9fbf1 0%, #f6fffb 55%, #eef4ff 100%); }
.lf-hero-body { display: flex; align-items: center; gap: 14px; margin: 12px 0; }
.lf-hero-icon { display: inline-flex; align-items: center; justify-content: center; flex: none;
  width: 64px; height: 64px; border-radius: 50%; background: #fff; font-size: 34px; box-shadow: var(--shadow); }
.lf-hero-info { min-width: 0; }
.lf-hero-title { font-size: 20px; font-weight: 800; }

/* ===== 成长记录（身高体重，家长中心） ===== */
.gc-profile { background: #f7f9ff; border-radius: 14px; padding: 14px 16px; }
.gc-h { margin: 18px 0 10px; font-size: 15px; }
.gc-chart { background: #fff; border: 1px solid #edf1fb; border-radius: 14px; padding: 8px 6px; }
.gc-svg { display: block; width: 100%; height: auto; }
.gc-legend { display: flex; flex-wrap: wrap; gap: 14px; margin-top: 8px; font-size: 12px; color: var(--text-light); }
.gc-lg { display: inline-flex; align-items: center; gap: 6px; }
.gc-lg i { display: inline-block; width: 22px; height: 0; border-top-width: 2px; border-top-style: solid; }
.gc-solid { border-color: var(--primary); }
.gc-dash { border-top-style: dashed; border-color: #ff9f43; }
.gc-mid { border-top-style: dashed; border-color: #9fb4d8; border-top-width: 1.5px !important; }
.gc-empty { background: #fffaf2; border: 2px dashed #ffd9a8; border-radius: 14px;
  padding: 18px; text-align: center; line-height: 1.8; }
.gc-alert { background: #fff8e6; border-left: 4px solid var(--orange); border-radius: 10px;
  padding: 10px 14px; margin-top: 10px; font-size: 13.5px; line-height: 1.7; }
.gc-src { margin-top: 16px; font-size: 12px; color: var(--text-light); line-height: 1.7; }

/* ===== 报表 ===== */
.stat-box { background: #f7f9ff; border-radius: 14px; padding: 16px; text-align: center; }
.stat-box .sv { font-size: 28px; font-weight: 800; color: var(--primary); }
.stat-box .sl { font-size: 13px; color: var(--text-light); margin-top: 4px; }
.log-line { padding: 8px 4px; border-bottom: 1px dashed #e4e9f5; font-size: 14px; }
.log-line:last-child { border-bottom: none; }
.log-time { color: var(--text-light); font-size: 12px; margin-right: 8px; }

/* ===== 占位模块 ===== */
.placeholder-box { text-align: center; padding: 70px 20px; }
.placeholder-box .ph-icon { font-size: 64px; }
.placeholder-box h2 { margin: 14px 0 8px; }
.placeholder-box p { color: var(--text-light); max-width: 460px; margin: 0 auto; line-height: 1.8; }

/* ===== 单词表格（家长中心） ===== */
.word-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.word-table th, .word-table td { padding: 8px 10px; border-bottom: 1px solid #edf0f8; text-align: left; }
.word-table th { color: var(--text-light); font-weight: 600; background: #f7f9ff; }

/* 结果页星星动画 */
.stars-row { font-size: 44px; letter-spacing: 8px; animation: popIn .4s; }
@keyframes popIn { from { transform: scale(.3); opacity: 0; } }
.points-fly { font-size: 22px; color: #b08900; font-weight: 800; animation: popIn .4s; }

@media (max-width: 900px) {
  #sidebar { width: 64px; min-width: 64px; }
  .nav-item span { display: none; }
  .grid-4, .shop-grid, .inventory-grid { grid-template-columns: repeat(2, 1fr); }
  .grid-3 { grid-template-columns: 1fr; }
  .grid-2 { grid-template-columns: 1fr; }
}

/* ============================================================
 * 习惯打卡（habit-checkin.js，规格 docs/specs/2026-10-13-habit-checkin-design.md §3.6）
 * 现代卡片风：16px 圆角、浅投影、习惯色板（--hb 由卡片内联注入）。
 * 色板（与引擎 HABIT_COLORS 一致）：紫 #7C6FF0 / 蓝 #4D96FF / 青 #2BC5A8 / 橙 #FF9F45 / 粉 #F26D9D / 绿 #6BCB3D
 * ============================================================ */
.hb-strip { margin-top: 14px; }
.hb-strip-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; }
.hb-strip-head h3 { margin: 0; font-size: 16px; }
.hb-empty {
  background: #fff; border: 1.5px dashed #c9d4e8; border-radius: 16px;
  padding: 22px; text-align: center; font-size: 15px; font-weight: 600; color: #5a6b8c;
  cursor: pointer;
}
.hb-empty .muted { font-size: 12.5px; font-weight: 400; }
.hb-strip-row {
  display: flex; gap: 12px; overflow-x: auto; padding: 4px 2px 10px;
  -webkit-overflow-scrolling: touch;
}
.hb-card {
  position: relative; flex: 0 0 148px; background: #fff; border-radius: 16px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, .06);
  padding: 14px 12px 12px; text-align: center;
}
.hb-dot {
  position: absolute; top: 10px; right: 10px; width: 9px; height: 9px; border-radius: 50%;
  background: #ff5a5a; box-shadow: 0 0 0 3px rgba(255, 90, 90, .18);
}
.hb-ring-wrap { position: relative; display: inline-block; width: 56px; height: 56px; }
.hb-ring { width: 56px; height: 56px; transform: rotate(-90deg); }
.hb-ring-track { fill: none; stroke: rgba(90, 107, 140, .12); stroke-width: 6; }
.hb-ring-fill {
  fill: none; stroke-width: 6; stroke-linecap: round;
  transition: stroke-dasharray .3s ease;
}
.hb-ring-icon {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  font-size: 22px;
}
.hb-ring-icon.met { color: var(--hb); font-weight: 800; }
.hb-name { font-size: 15px; font-weight: 600; margin-top: 6px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hb-prog { font-size: 13px; font-weight: 500; color: #8a97b0; margin-top: 2px; }
.hb-streak {
  display: inline-block; margin-top: 5px; font-size: 11.5px; font-weight: 600;
  padding: 2px 8px; border-radius: 999px;
  color: var(--hb); background: color-mix(in srgb, var(--hb) 10%, #fff);
}
.hb-btn {
  margin-top: 9px; width: 100%; min-height: 40px; border: none; border-radius: 12px;
  font-size: 13.5px; font-weight: 700; cursor: pointer;
  color: #fff; background: var(--hb);
  transition: transform .12s ease, opacity .12s ease;
}
.hb-btn:active { transform: scale(.96); }
.hb-btn.done { background: color-mix(in srgb, var(--hb) 12%, #fff); color: var(--hb); font-weight: 600; }
.hb-note-link { display: inline-block; margin-top: 6px; font-size: 12px; color: #8a97b0; cursor: pointer; }
.hb-note {
  width: 100%; box-sizing: border-box; font-size: 16px; line-height: 1.5;
  border: 1.5px solid #dbe3f0; border-radius: 12px; padding: 10px 12px; resize: none;
}
/* 习惯列表子页（生活页「习惯打卡」标签） */
.hb-list-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; }
.hb-list-head h3 { margin: 0; font-size: 16px; }
.hb-row {
  display: flex; align-items: center; gap: 12px; background: #fff; border-radius: 16px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, .06); padding: 12px 14px; margin-bottom: 10px;
}
.hb-tile {
  flex: none; width: 44px; height: 44px; border-radius: 13px; font-size: 22px;
  display: flex; align-items: center; justify-content: center;
  background: color-mix(in srgb, var(--hb) 12%, #fff);
}
.hb-row-main { flex: 1; min-width: 0; }
.hb-row-name { font-size: 15.5px; font-weight: 600; }
.hb-row-sub { font-size: 12.5px; color: #8a97b0; margin-top: 2px; }
.hb-row-acts { display: flex; gap: 6px; flex-wrap: wrap; justify-content: flex-end; }
.hb-chip {
  min-height: 36px; padding: 0 12px; border: none; border-radius: 10px; font-size: 13px;
  font-weight: 600; cursor: pointer; background: #eef2fa; color: #5a6b8c;
}
.hb-chip.primary { background: var(--hb); color: #fff; }
.hb-chip:active { transform: scale(.96); }
.hb-summary-bar {
  margin-top: 4px; padding: 10px 14px; border-radius: 14px; font-size: 13px; font-weight: 600;
  color: #5a6b8c; background: #eef2fa;
}
/* 生活页二级标签条 */
.hb-tabs { display: flex; gap: 8px; margin-bottom: 12px; }
.hb-tab {
  min-height: 40px; padding: 0 18px; border: none; border-radius: 999px; font-size: 14px;
  font-weight: 700; cursor: pointer; background: #eef2fa; color: #5a6b8c;
}
.hb-tab.active { background: var(--primary); color: #fff; }
/* 详情页：月历热力 + 流水 */
.hb-detail-head { display: flex; align-items: center; gap: 12px; margin-bottom: 4px; }
.hb-detail-name { font-size: 18px; font-weight: 700; }
.hb-detail-stats { font-size: 13px; color: #8a97b0; margin: 6px 0 12px; }
.hb-cal-nav { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; }
.hb-cal-nav b { font-size: 15px; }
.hb-cal-arrow {
  min-width: 44px; min-height: 44px; border: none; border-radius: 12px; font-size: 18px;
  background: #eef2fa; color: #5a6b8c; cursor: pointer;
}
.hb-cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 3px; }
.hb-cal-wd { text-align: center; font-size: 11.5px; color: #a5b1c6; padding: 2px 0; }
.hb-cal-day {
  position: relative; aspect-ratio: 1; border-radius: 7px; border: none; cursor: pointer;
  background: #eef1f7; font-size: 12px; color: #5a6b8c; padding: 0;
}
.hb-cal-day.h1 { background: color-mix(in srgb, var(--hb) 25%, #fff); }
.hb-cal-day.h2 { background: color-mix(in srgb, var(--hb) 50%, #fff); }
.hb-cal-day.h3 { background: color-mix(in srgb, var(--hb) 75%, #fff); color: #fff; }
.hb-cal-day.h4 { background: var(--hb); color: #fff; }
.hb-cal-day.today { outline: 2px solid var(--hb); outline-offset: -2px; }
.hb-cal-day.sel { box-shadow: inset 0 0 0 2px #2b3a55; }
.hb-cal-day.future { opacity: .35; cursor: default; }
.hb-cal-day.other { visibility: hidden; }
.hb-logs { margin-top: 12px; }
.hb-log-line {
  display: flex; gap: 8px; align-items: baseline; padding: 8px 4px; font-size: 13.5px;
  border-bottom: 1px solid #eef1f7;
}
.hb-log-time { flex: none; color: #8a97b0; font-size: 12.5px; }
.hb-log-note { flex: 1; min-width: 0; }
.hb-log-del { flex: none; border: none; background: none; color: #c3cddd; cursor: pointer; font-size: 14px; padding: 4px; }
.hb-mode-tabs { display: flex; gap: 8px; margin: 10px 0; }
.hb-legend { display: flex; align-items: center; gap: 4px; font-size: 11.5px; color: #a5b1c6; margin-top: 8px; justify-content: flex-end; }
.hb-legend i { width: 12px; height: 12px; border-radius: 3px; display: inline-block; }
/* 编辑弹窗 */
.hb-form-row { margin-bottom: 14px; }
.hb-form-row > label { display: block; font-size: 13px; font-weight: 700; color: #5a6b8c; margin-bottom: 6px; }
.hb-icon-row { display: flex; flex-wrap: wrap; gap: 6px; }
.hb-icon-opt {
  width: 44px; height: 44px; border: 1.5px solid #e4e9f5; border-radius: 12px; font-size: 21px;
  background: #fff; cursor: pointer;
}
.hb-icon-opt.sel { border-color: var(--primary); background: #eef2ff; }
.hb-color-row { display: flex; gap: 10px; }
.hb-color-opt {
  width: 36px; height: 36px; border-radius: 50%; border: 3px solid transparent; cursor: pointer; padding: 0;
}
.hb-color-opt.sel { border-color: #fff; box-shadow: 0 0 0 2.5px currentColor; }
.hb-period-row { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
/* 权重 (0,2,1) 压过全局 input[type=number] 的 padding（§7 坑：压不过会把数字裁掉） */
.hb-period-row input[type=number] { width: 64px; text-align: center; padding: 8px 6px; }
.hb-period-desc { margin-top: 6px; font-size: 13px; color: var(--primary); font-weight: 600; }
.hb-switch-row { display: flex; align-items: center; gap: 10px; }
.hb-switch-label { font-size: 14px; font-weight: 500; color: #3a4660; }
.hb-danger { color: var(--red); }
/* 可点组件触屏基线（与上面全站清单同款，本模块自己的类） */
.hb-btn, .hb-chip, .hb-tab, .hb-card, .hb-empty, .hb-note-link, .hb-cal-day, .hb-cal-arrow,
.hb-icon-opt, .hb-color-opt, .hb-log-del {
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
}
@media (prefers-reduced-motion: reduce) {
  .hb-ring-fill, .hb-btn, .hb-chip { transition: none; }
}

/* ============================================================
 * 触屏 / 窄屏适配（手机、平板）
 * ============================================================
 * 目标：整页在触屏上"点得准、滑得动、不打架"。分四块：
 *   ① 触屏基线：去掉点击时那层灰蓝高亮、去掉长按选中与呼出菜单、禁双击缩放（保留滚动）
 *   ② 触屏可点尺寸：手指没有鼠标准，小控件（尤其 .btn-mini）要放大到 40px 上下
 *   ③ 表单字号 ≥16px：iOS Safari 里 <16px 的输入框一聚焦会把整页放大
 *   ④ 窄屏断点：顶栏精简、宠物气泡别被挤成一条、表格横向滚动、24 张领养卡改 4 列
 * 刻意没做的事：不锁 maximum-scale（锁了会破坏系统缩放，靠字号解决放大问题更正确）；
 * 也没给宠物舞台加 touch-action——那里的 pan-y 是拖拽转身与上下滚动共存的关键，
 * 加别的值会让"按住左右拖转身"失效。 */

/* 单词页的 5 个记忆箱：桌面固定 5 列；手机改自适应列（见下面的窄屏断点）。
 * 原来这个列数写在各页面的内联样式里，窄屏下每列只剩 50px、内容把格子撑出容器，所以挪到这里。 */
.grid-bins { grid-template-columns: repeat(5, 1fr); }

html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }
body { -webkit-tap-highlight-color: transparent; overscroll-behavior-y: contain; }

/* ① 可点组件：禁双击缩放（manipulation 仍允许滚动）、禁长按选中/呼出菜单。
 * 注意不含 .pet-stage / .pet-body —— 它们的 touch-action 由宠物层自己管。 */
.btn, .nav-item, .set-entry, .inv-item, .shop-item, .adopt-card, .park-card, .phrase-chip,
.quiz-opt, .task-item, .gate-profile, .pet-bubble, .pet-mini, .tag, #topbar-child,
.pet-name-tag, .se-arrow, .log-line, .note-chip {
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
  -webkit-touch-callout: none;
  user-select: none;
}
/* 滚动容器在 iOS 上要有惯性滚动；overscroll-behavior 阻止"滑到底把整页也带走" */
#view-root, #modal-body { -webkit-overflow-scrolling: touch; }
#view-root { overscroll-behavior: contain; }
#modal-body { overscroll-behavior: contain; }

/* 移动端地址栏会占掉一截高度，100vh 会把底部内容顶到看不见的地方 */
@supports (height: 100dvh) {
  #main-ui, #profile-gate { height: 100dvh; }
}

/* ② 触屏（粗指针）下把可点区域放大：手指约 9mm≈34px，留到 40px 以上才不容易点错 */
@media (pointer: coarse) {
  .btn { padding: 12px 22px; }
  .btn-mini { padding: 9px 14px; font-size: 14px; }        /* 原来 4px 12px ≈ 26px 高，太难点 */
  .nav-item { padding: 12px; }
  .set-entry { padding: 12px 14px; }
  .inv-item { padding: 14px 10px; }
  .phrase-chip, .note-chip { padding: 9px 16px; }
  .quiz-opt { padding: 16px 14px; }
  .log-line { padding: 10px 4px; }
  input[type=text], input[type=number], input[type=password], select, textarea { min-height: 46px; }
  .math-input { min-height: 46px; }
  .adopt-img { max-width: 72px; height: 72px; }             /* 领养卡的头像就是点按目标 */
  .park-card.add { min-height: 132px; }
  .pet-stat { width: 46%; max-width: 220px; }
}

/* ④ 手机竖屏：把横向挤的东西拆开 */
/* ===== 英语阅读 =====
 * rd-target：本篇目标词（淡色底），rd-hard：难词（虚线下划线，点了才出中文卡）。
 * 两者可以叠加在同一个词上，点击行为一致。
 * rd-art / rd-icon 等是展示层（数据来自 reading-story-art.js）：插图统一 640x260 viewBox，
 * 正文页插图用 .faint 压淡，避免和文字抢注意力。 */
.rd-icon { display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 44px; border-radius: 50%; background: var(--primary-light);
  font-size: 24px; flex: none; }
.rd-icon.big { width: 64px; height: 64px; font-size: 34px; }
.rd-icon.sm { width: 30px; height: 30px; font-size: 17px; vertical-align: -6px; }
/* 插图按"画框"渲染：固定高度、画面按高度等比缩放完整居中，衬一层中性卡纸。
 * 不用 width:100%——卡片比 640x260 画幅宽得多，meet 对齐会把画缩成中间一条、两侧大片白（截图验证过） */
.rd-art { height: 178px; display: flex; align-items: center; justify-content: center;
  border-radius: 14px; overflow: hidden; margin: 2px 0 14px; background: #eef4ff;
  box-shadow: var(--shadow); }
.rd-art svg { flex: none; height: 100%; width: auto; display: block; }
.rd-art.faint { opacity: .55; box-shadow: none; }
.rd-hero { background: linear-gradient(135deg, #e8efff 0%, #f6fbff 60%, #fff3e6 100%); }
.rd-hero-body { display: flex; align-items: center; gap: 14px; margin: 12px 0; }
.rd-hero-info { min-width: 0; }
.rd-hero-title { font-size: 20px; font-weight: 800; }
.rd-books { gap: 12px; }
/* 书架卡片可点（2026-10-08）：给点击态，孩子知道能点 */
.rd-book { cursor: pointer; transition: .12s; }
.rd-book:hover { transform: translateY(-2px); box-shadow: 0 6px 16px rgba(60, 90, 180, .14); }
/* ---------- 英语阅读 · 竖屏重规划（2026-10-08） ---------- */
/* 每句一块：英语按句换行，孩子不跳行（横竖屏共用） */
.rd-sent { display: block; }
.rd-sent + .rd-sent { margin-top: 4px; }
/* 段中文（默认收起）：浅底小字，点"中文"按钮展开 */
.rd-para-cn {
  margin: 8px 0 2px; padding: 8px 10px; background: #f6f8fd;
  border-radius: 10px; font-size: 14px; color: var(--text-light); line-height: 1.8;
}
.rd-para-actions { display: flex; gap: 8px; justify-content: flex-end; margin-top: 8px; }
/* 吸底操作条：横屏是普通一行；竖屏由竖屏段改成吸底 */
.rd-sticky { display: flex; gap: 10px; margin-top: 12px; }
.rd-sticky .btn { flex: 1 1 0; }
/* 阅读进度线：横屏是一根静态细条（位置无意义时也当分隔用，宽度 0）；竖屏吸顶 */
.rd-progress { height: 4px; background: #e4e9f5; border-radius: 999px; overflow: hidden; margin-bottom: 10px; }
.rd-progress-fill { height: 100%; width: 0; background: linear-gradient(90deg, var(--primary), #7aa2ff); border-radius: 999px; transition: width .12s linear; }
.rd-book { display: flex; align-items: center; gap: 12px; background: var(--card);
  border: 2px solid #e3edff; border-left: 6px solid var(--primary); border-radius: 14px;
  padding: 12px 14px; box-shadow: var(--shadow); }
.rd-book-info { flex: 1; min-width: 0; }
.rd-book-info b { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rd-g3 { border-left-color: var(--green); }
.rd-g4 { border-left-color: var(--primary); }
.rd-g5 { border-left-color: var(--orange); }
.rd-g6 { border-left-color: var(--purple); }
.rd-words { gap: 10px; }
.rd-wordcard { border: 2px solid #eef2fb; }
.rd-para { display: block; position: relative; margin: 12px 0; padding: 10px 44px 10px 16px;
  line-height: 2; font-size: 17px; background: var(--card);
  border-left: 5px solid #cfe0ff; border-radius: 12px; box-shadow: var(--shadow); }
.rd-pno { display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; border-radius: 50%; background: var(--primary);
  color: #fff; font-size: 13px; font-weight: 700; margin-right: 8px; vertical-align: -4px; }
.rd-para-say { position: absolute; right: 8px; top: 10px; }
.rd-target { background: #fff1b8; border-radius: 3px; padding: 0 2px; font-weight: 700; }
.rd-hard { border-bottom: 2px dashed var(--orange); cursor: pointer; }
.rd-hard:hover { background: #fff0f0; }
.rd-congrats { display: flex; align-items: center; gap: 14px; margin-bottom: 10px; }
.rd-chip { display: inline-block; background: var(--primary-light); color: var(--primary);
  border-radius: 999px; padding: 4px 14px; margin: 4px 6px 0 0; font-weight: 700; }
.rd-cmp > div { padding: 7px 0; border-bottom: 1px dashed #e3edff; }
.rd-cmp .cn { color: var(--text-light); }
.rd-no-voice { color: var(--muted); font-size: 13px; }

@media (max-width: 600px) {
  #topbar { height: 54px; padding: 0 12px; }
  #topbar-right { gap: 8px; }
  #topbar-name { max-width: 84px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .streak-badge, .points-badge { padding: 5px 10px; font-size: 13px; }
  .store-badge { display: none; }              /* 存储模式在手机顶栏太占位，家长中心里看得到 */
  #view-root { padding: 12px; }
  .grid { gap: 12px; }
  .card { padding: 14px; }
  /* 单词查询（2026-10-13）窄屏：入口卡竖排、搜索框与复习行换行、点击目标 ≥44px */
  .wl-entry { flex-wrap: wrap; gap: 10px; }
  .wl-entry-icon { font-size: 28px; }
  .wl-entry .btn { width: 100%; }
  .wl-search-row { flex-wrap: wrap; }
  .wl-search-row .wl-input { flex: 1 1 60%; }
  .wl-review-row { gap: 10px; }
  .wl-hist-word { font-size: 17px; padding: 8px 0; }
  .wl-hist-del { width: 36px; height: 36px; }
  /* 弹窗：内边距从 #modal-box 挪到了三段各自身上（头部/正文/底部），窄屏要一起收紧 */
  #modal-box { border-radius: 16px; max-height: 92vh; }
  #modal-head { padding: 16px 54px 10px 16px; }
  #modal-title { font-size: 17px; }
  #modal-close { top: 12px; right: 12px; width: 30px; height: 30px; }
  #modal-body { padding: 12px 16px 4px; overflow-x: auto; }   /* 宽表格横向滚，不撑破弹窗 */
  #modal-actions { padding: 12px 16px 14px; }
  /* 学习量卡片：窄屏步进器换到第二行，避免输入框被挤成一条 */
  .ss-row { flex-wrap: wrap; }
  .ss-ctrl { margin-left: auto; }
  .ss-select { max-width: 100%; width: 100%; }
  .av-grid { grid-template-columns: repeat(auto-fill, minmax(42px, 1fr)); }
  .word-table { font-size: 12px; }
  /* 宠物舞台：气泡原来靠 right:56% 让位给宠物，窄屏上会被压成一条竖排文字；
   * 底部留白与主样式同理给到 54px（草地带 + 布景），别让文字落进草地 */
  .pet-stage { padding: 46px 12px 54px; min-height: 300px; }
  .pet-bubble { left: 12px; right: 12px; top: 8px; }
  .pet-status-bars { flex-direction: column; align-items: center; gap: 10px; }
  .pet-stat { width: 100%; max-width: 280px; }
  .pet-name-tag { font-size: 17px; }
  .pet-say-home, .pet-mood { font-size: 13px; }
  /* 24 张品种卡在 375px 下 8 列只剩 40px，改 4 列 */
  .adopt-pick, .modal-pick { grid-template-columns: repeat(4, 1fr); }
  .adopt-img { max-width: 60px; height: 60px; }
  .adopt-img.big { width: 96px; height: 96px; }
  .gate-profile { width: 128px; padding: 16px 10px; }
  /* 没指定列数的 .grid（列宽由内容撑出来的那种）与单词页 5 个记忆箱，在窄屏都改成能换行的自适应列。
   * :not() 排除 grid-2/3/4，避免把它们的单列布局覆盖回多列。
   * min-width: 0 是 grid/flex 的经典兜底：子项默认 min-width:auto 会被内容撑破列宽。 */
  .grid:not(.grid-2):not(.grid-3):not(.grid-4), .grid-bins { grid-template-columns: repeat(auto-fit, minmax(88px, 1fr)); }
  .grid > *, .stat-box { min-width: 0; }
  .stat-box { padding: 12px; }
  .stat-box .sv { font-size: 24px; }
  .rd-para { font-size: 16px; line-height: 1.9; }
  /* 阅读展示层窄屏：插图降高、横幅收紧（覆盖必须排在主样式之后才生效） */
  .rd-art { height: 118px; }
  .rd-hero-body { gap: 10px; margin: 10px 0; }
  .rd-icon.big { width: 54px; height: 54px; font-size: 28px; }
  .rd-book { padding: 10px; gap: 8px; }
  /* 数学窄屏：题目网格单列、头卡图标收紧、批改反馈换行不挤出屏 */
  .math-grid { grid-template-columns: 1fr; }
  .mh-icon { width: 52px; height: 52px; font-size: 28px; }
  .mrow2 { flex-wrap: wrap; gap: 8px; }
  .mrow2 .mres { flex-basis: 100%; }
  /* 语文窄屏：头卡图标收紧、选项单列、原文行距压一点 */
  .zh-hero-icon { width: 52px; height: 52px; font-size: 28px; }
  .zh-opts { grid-template-columns: 1fr; }
  .zh-line { font-size: 17px; }
  /* 生活打卡 / 成长记录窄屏：头卡图标收紧；成长图在窄屏按宽度缩放，坐标字号偏小但可读 */
  .lf-hero-icon { width: 52px; height: 52px; font-size: 28px; }
  .gc-profile { padding: 12px; }
  .gc-chart { padding: 4px 2px; }
  .gc-legend { gap: 8px; }
  /* 习惯打卡窄屏：卡片收紧、列表行动作换行、详情头不挤（覆盖必须写在窄屏块内部——§6 纪律） */
  .hb-card { flex-basis: 132px; }
  .hb-row { flex-wrap: wrap; }
  .hb-row-acts { width: 100%; justify-content: flex-start; }
  .hb-detail-head .hb-tile { width: 40px; height: 40px; font-size: 20px; }
  /* 安卓安装包弹窗窄屏：二维码收一档并跟着容器缩（max-width 已在主样式里给）、信息行允许换行 */
  .apk-qr svg { width: 210px; height: 210px; }
  .apk-row { flex-wrap: wrap; gap: 4px 10px; }
  .apk-row > span { min-width: 0; }
  .apk-dl { font-size: 15px; }
}

/* ============================================================
 * 竖屏布局（2026-09-29）：手机/平板竖持的"App 形态"
 * ============================================================
 * 由 app.js 的布局控制器给 body 挂 .layout-portrait（自动判定 = 窗口/屏幕方向为竖向，
 * 2026-09-29 补丁去掉触屏条件；顶栏 📱 按钮可强制三态循环，
 * 偏好存 localStorage）。必须写在本文件末尾：覆盖上面所有主样式。
 * 横屏/桌面不受本段任何影响——选择器全部以 body.layout-portrait 开头。
 * 宠物舞台的竖屏细节仍沿用上面 600px 断点的规则，这里只管骨架。 */
/* ============================================================
 * 英语模块（2026-10-08：单词/阅读/口语 合并）
 * ============================================================
 * 总览页（.eng-hub 三张一级入口卡）+ 子页二级导航（.eng-head-sub）。
 * 竖屏的吸顶与单列覆盖写在文件末尾的竖屏段里（body.layout-portrait 开头）。 */
.eng-head { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-bottom: 12px; }
.eng-title { font-size: 20px; font-weight: 800; }
.eng-status { display: flex; gap: 8px; flex-wrap: wrap; }
.eng-back {
  border: 1px solid #dbe3f5; cursor: pointer; background: #fff; border-radius: 10px;
  padding: 8px 14px; font-size: 14px; font-weight: 700; color: var(--text-light);
}
.eng-back:hover { color: var(--primary); border-color: var(--primary); }
.eng-tabs { display: flex; gap: 4px; margin-left: auto; background: #eef2fb; padding: 4px; border-radius: 12px; }
.eng-tab {
  border: none; cursor: pointer; position: relative;
  background: transparent; border-radius: 9px; padding: 8px 20px;
  font-size: 15px; font-weight: 700; color: var(--text-light); transition: .12s;
}
.eng-tab:hover { color: var(--primary); }
.eng-tab.active { background: #fff; color: var(--primary); box-shadow: 0 2px 8px rgba(79,124,255,.18); }
/* 待办小红点：该子页今天还有要做的事 */
.eng-dot { position: absolute; top: 5px; right: 8px; width: 7px; height: 7px; border-radius: 50%; background: var(--red); }
/* ---------- 总览页：三张一级入口卡 ---------- */
.eng-hub { display: grid; gap: 16px; grid-template-columns: 1fr; align-items: stretch; }
.eng-entry {
  display: flex; flex-direction: column; cursor: pointer; position: relative;
  transition: .12s;
}
.eng-entry:hover { transform: translateY(-2px); box-shadow: 0 8px 22px rgba(60, 90, 180, .14); }
.eng-entry-head { display: flex; align-items: center; gap: 8px; font-size: 18px; position: relative; }
.eng-entry-head .tag { margin-left: auto; font-size: 12px; }
.eng-entry-icon { font-size: 22px; }
.eng-entry-body { flex: 1 1 auto; margin-top: 10px; display: flex; flex-direction: column; gap: 8px; }
.eng-entry-line { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; font-size: 14px; }
.eng-entry-book { display: flex; align-items: center; gap: 10px; }
.eng-entry-book-info b { display: block; font-size: 15px; }
.eng-entry-actions { display: flex; gap: 10px; margin-top: 12px; }
.eng-entry-actions .btn { flex: 1 1 0; }
/* 三张动作卡（学习/考核/复盘）的主按钮统一占满卡片宽：原来按文字宽度收缩，竖屏下一宽一窄（144 与 326）不齐，
 * 长文案（锁定提示）还会被挤成两行；等宽后三张卡视觉对齐，横屏三列同样适用。 */
.eng-actions .btn-big { width: 100%; }
.eng-entry-more { margin-top: 10px; font-size: 12.5px; color: var(--text-light); }
.eng-entry-soon { background: linear-gradient(135deg, #fffaf0, #ffffff 70%); }
@media (min-width: 901px) { .eng-hub { grid-template-columns: 1.15fr 1fr .85fr; } }
@media (min-width: 601px) and (max-width: 900px) { .eng-hub { grid-template-columns: 1fr 1fr; } }
/* 子页分栏：横屏（宽窗）两栏，窄窗与竖屏单列。
 * 注意顺序：本段在 600px 媒体块之后，若在这里直接写多列会压过那段应有的单列覆盖，
 * 所以多列只在 min-width:601px 时生效；下半区给记忆库多一点宽度（箱子标签太窄会挤成三行）。 */
.eng-split, .eng-lower { align-items: start; }
.eng-lower, .eng-books { grid-template-columns: 1fr; }
@media (min-width: 601px) {
  .eng-lower { grid-template-columns: 1.35fr 1fr; }
  .eng-books { grid-template-columns: repeat(3, 1fr); }
}
.eng-col { min-width: 0; }
.eng-hero { background: linear-gradient(135deg, #e8efff 0%, #f6fbff 60%, #fff3e6 100%); }
.eng-plan { display: flex; flex-direction: column; gap: 10px; margin-top: 12px; }
.eng-plan-row {
  display: flex; align-items: center; gap: 12px;
  background: #f7f9ff; border: 1px solid #e7ecf8; border-radius: 12px; padding: 10px 14px;
}
.eng-plan-icon { font-size: 26px; }

body.layout-portrait #main-ui { flex-direction: column; }

/* 内容区在上，占满除底栏外的全部高度 */
body.layout-portrait #content-wrap { order: 1; flex: 1; min-width: 0; min-height: 0; }

/* 侧栏 → 底部导航条：图标 + 小字，横向可滑动，拇指可达 */
body.layout-portrait #sidebar {
  order: 2;                                   /* flex 翻到下方 */
  width: 100%; min-width: 0; height: auto;
  flex-direction: row; align-items: center;
  padding: 4px 6px calc(4px + env(safe-area-inset-bottom, 0px));
  border-right: none; border-top: 1px solid #e4e9f5;
  box-shadow: 0 -4px 14px rgba(31, 45, 90, .06);
}
body.layout-portrait #sidebar .logo { display: none; }   /* 底栏不放 logo */
body.layout-portrait #nav-menu {
  flex-direction: row; align-items: stretch; gap: 2px;
  overflow-x: auto; overflow-y: hidden;
  flex: 1 1 auto; min-width: 0;                /* 占满底栏宽、内部横向滚动——flex:none 会取内容宽把整页撑破（实测抓到） */
  scrollbar-width: none;                       /* 底栏滚动条藏掉，滑动手感更像 tab bar */
  -webkit-overflow-scrolling: touch;
}
body.layout-portrait #nav-menu::-webkit-scrollbar { display: none; }
body.layout-portrait #nav-menu { user-select: none; }   /* 鼠标拖拽滚动时不要选中文字（app.js bindNavDrag） */
body.layout-portrait .nav-item {
  flex: none; flex-direction: column; justify-content: center; gap: 1px;
  min-width: 54px; padding: 6px 9px; text-align: center;
  font-size: 17px;                             /* emoji 继承这个尺寸（裸文本节点没法单独定） */
  border-radius: 10px;
}
body.layout-portrait .nav-item span {
  display: block;                              /* 900px 断点里被 display:none，竖屏布局恢复 */
  font-size: 10.5px; line-height: 1.25; font-weight: 600;
  max-width: 60px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
body.layout-portrait #nav-menu .nav-divider { display: none; }   /* 分隔线在横排里没意义 */
/* 第十八轮（2026-10-18 用户："底部功能栏在竖屏和 apk 的时候居中显示"）：左右各垫一条会收缩的空白。
 * 六个项目实测总宽约 340px，在 390~411 CSS px 的手机上右边空 50~70px，看着就贴左；
 * 有富余时这两条垫块均分剩余宽度 → 整条居中，放不下时它们被压回 0、横向滚动照常。
 * ⚠️ 这里**不能用 justify-content:center**：#nav-menu 带 overflow-x:auto，
 * 居中会让最左边溢出到 start 侧、既看不见也滑不回来（flex 溢出的经典坑），垫块法才安全。 */
body.layout-portrait #nav-menu::before,
body.layout-portrait #nav-menu::after { content: ''; flex: 1 1 0; min-width: 0; }

/* 顶栏收紧一点，给内容让高度 */
body.layout-portrait #topbar {
  height: auto; min-height: 48px; padding: 6px 10px; gap: 6px; flex-wrap: nowrap;
}
body.layout-portrait #topbar-child { min-width: 0; flex: 1 1 auto; gap: 6px; }
body.layout-portrait #topbar-name {
  max-width: 5.5em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
body.layout-portrait #topbar-right { gap: 6px; flex: 0 1 auto; min-width: 0; }
body.layout-portrait .streak-badge,
body.layout-portrait .points-badge { padding: 3px 8px; font-size: 12px; white-space: nowrap; }
.switch-kid {
  border: 1px solid #d7deee; background: #fff; color: #5c6b8a;
  border-radius: 999px; padding: 3px 10px; font-size: 12px; font-weight: 700; line-height: 1.4;
}
body.layout-portrait .pet-page .pet-stage { min-height: 42vh; padding: 28px 12px 36px; }
body.layout-portrait .pet-dock { margin-top: -18px; padding: 12px; }
body.layout-portrait .pet-dock .pet-stat {
  flex-direction: column; align-items: stretch; gap: 4px;
}
body.layout-portrait .pet-dock .pet-stat .label {
  font-size: 12px; overflow: hidden; text-overflow: ellipsis;
}
body.layout-portrait .pet-dock .pet-stat .progress-bar {
  width: 100%; min-width: 0; height: 8px;
}
body.layout-portrait .pet-quick { display: grid; grid-template-columns: 1fr 1fr; }
body.layout-portrait .pet-quick .btn { width: 100%; }
body.layout-portrait #view-root { padding: 12px 12px 20px; }

/* 档案门：竖屏下孩子卡片竖排（横排时 58px 头像左右挤） */
body.layout-portrait #profile-gate .gate-profiles { flex-direction: column; align-items: center; gap: 14px; }

/* ---------- 英语模块（2026-10-08）：竖屏下的吸顶与单列 ----------
 * 吸顶范围 = 整条子页头（← 英语 + 三个均分按钮），滚动内容时随时能切；
 * sticky 相对 #view-root（滚动容器）生效，负 margin 让吸顶条铺满整宽。
 * 吸顶时上缘会露出 12px 的滚动缝隙（sticky 相对内容盒生效，而内容从内边距区开始滚）：
 * 用一条同色阴影把这条缝盖住，看起来就是"贴顶"的整条头。 */
body.layout-portrait .eng-head {
  position: sticky; top: 0; z-index: 30;
  background: var(--bg);
  /* 纵向的负 margin 是条真 bug（2026-10-17 用户反馈"最上面状态显示不完整"）：
   * 原来写 -12px 想把吸顶时的缝隙顶掉，结果**没滚动时**头部也被往上抬 12px，
   * 第一行状态标签被 #view-root 的 overflow 裁掉一半。负 margin 只留横向（铺满整宽），
   * 吸顶那条缝由下面的同色 box-shadow 盖住。 */
  margin: 0 -12px 6px;
  /* 第十一轮（2026-10-17）：整条高度对齐专注页顶栏（#focus-bar 竖屏 3px 10px），
   * 并且**强制一行**——原来 flex-wrap:wrap 让四个状态胶囊掉到第二行，页头吃掉近 90px */
  padding: 3px 10px;
  /* 第十八轮（2026-10-18）：这行不再跟专注页顶栏共用 --row-h。顶栏要放「← 返回 + 标题 + 计时」，
   * 42px 是用户要的那档（"返回条的高度正好，保持不变"）；这行只有标题与四颗胶囊
   * （内容实测 21~23px），42px 里白吃近 20px，所以单独走 --head-h: 34px。
   * 两条横条不会同时出现在屏幕上，等高这条纪律在这里不适用。 */
  min-height: var(--head-h);
  flex-wrap: nowrap;
  box-shadow: 0 -12px 0 0 var(--bg), 0 6px 12px -10px rgba(31, 45, 90, .28);
  gap: 6px;
}
body.layout-portrait .eng-title { font-size: 15px; flex: none; }
/* 状态条：不折行，实在放不下就自己横向滚（隐藏滚动条），绝不把标题顶成两行 */
body.layout-portrait .eng-status {
  flex: 1 1 auto; min-width: 0; justify-content: flex-end; gap: 4px;
  flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none;
}
body.layout-portrait .eng-status::-webkit-scrollbar { display: none; }
body.layout-portrait .eng-status .tag { font-size: 11px; padding: 2px 6px; white-space: nowrap; flex: none; }
/* 子页二级导航：← 英语 一个紧凑键 + 标签整条均分（拇指好点）。
 * 2026-10-17 把 42px 的行高压到 36px、字号 15→14——竖屏上这一条加上页头要吃掉近 100px，
 * 地图/正文能看的区域就少了（用户："功能栏太占地方，文字缩小一些、间隔缩短一些"）。 */
body.layout-portrait .eng-head-sub { flex-wrap: nowrap; }
body.layout-portrait .eng-back { flex: 0 0 auto; padding: 6px 10px; min-height: 36px; font-size: 14px; }
body.layout-portrait .eng-head-sub .eng-tabs { flex: 1 1 auto; margin-left: 0; }
body.layout-portrait .eng-tab { flex: 1 1 0; padding: 6px 0; min-height: 36px; text-align: center; font-size: 14px; }
/* 竖屏单列：总览卡与三个子页的分栏都收成一列 */
body.layout-portrait .eng-hub,
body.layout-portrait .eng-actions,
body.layout-portrait .eng-lower,
body.layout-portrait .eng-split,
body.layout-portrait .eng-books { grid-template-columns: 1fr; }
body.layout-portrait .eng-actions .card { padding: 14px; }
body.layout-portrait .eng-actions .card h3 { font-size: 16px; }
/* 竖屏动作卡图标缩小一点，三张卡不至于占满一整屏 */
body.layout-portrait .eng-actions .card [style*="font-size:44px"] { font-size: 34px !important; }
/* 入口卡竖屏按钮全宽、拇指好点 */
body.layout-portrait .eng-entry-actions .btn { min-height: 44px; }
body.layout-portrait .eng-plan-row { padding: 10px 12px; }

/* ---------- 英语阅读 · 竖屏（2026-10-08 重规划） ----------
 * 注意：书架/词表这类"纯 .grid"元素会被 600px 断点里的
 * .grid:not(.grid-2):not(.grid-3):not(.grid-4) 规则自动铺成多列（优先级 0,4,0），
 * 所以这里的覆盖一律带 #view-root 提权，否则打不过它（书架被压成 3 列 100px 宽就是这样来的）。 */
body.layout-portrait #view-root .eng-books { grid-template-columns: repeat(2, 1fr); gap: 10px; }
body.layout-portrait .rd-book {
  flex-direction: column; align-items: center; text-align: center;
  gap: 6px; padding: 12px 8px;
}
body.layout-portrait .rd-book-info { display: flex; flex-direction: column; align-items: center; }
/* 中文当主标题、英文缩小当副标——竖屏窄卡片里中文字少、更好扫读 */
body.layout-portrait .rd-book-info .sl {
  order: 1; font-size: 14px; font-weight: 800; color: var(--text); line-height: 1.35;
}
body.layout-portrait .rd-book-info b {
  order: 2; font-size: 11px; font-weight: 600; color: var(--text-light);
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
body.layout-portrait .rd-book .rd-icon { font-size: 26px; }
body.layout-portrait .rd-book > .tag { font-size: 11px; padding: 2px 8px; }
/* 推荐卡：插图压矮、标题收一行、按钮全宽（首屏把正文/书架让出来） */
body.layout-portrait .rd-art { height: 88px; }
body.layout-portrait .rd-hero-title { font-size: 18px; }
body.layout-portrait .rd-hero .btn-big { width: 100%; margin-top: 8px; }
body.layout-portrait .rd-hero-body { margin: 8px 0; }
/* 正文页：控制按钮成一行、警告条一行小字、段落卡片收紧 */
body.layout-portrait .rd-read-tools { display: flex; gap: 6px; }
body.layout-portrait .rd-read-title { font-size: 16px; min-width: 0; }
body.layout-portrait .rd-read-head { flex-direction: column; align-items: stretch; gap: 8px; }
body.layout-portrait .rd-no-voice { font-size: 12px; margin: 6px 0 0; }
body.layout-portrait .rd-para { padding: 12px; }
/* 进度线吸顶、操作条吸底（在底部导航之上），动作随时可达 */
body.layout-portrait .rd-progress { position: sticky; top: 0; z-index: 21; }
body.layout-portrait .rd-sticky {
  position: sticky; bottom: 0; z-index: 20;
  margin: 12px -12px -12px; padding: 10px 12px calc(10px + env(safe-area-inset-bottom, 0px));
  background: linear-gradient(transparent, var(--bg) 28%);
}
body.layout-portrait .rd-sticky .btn { min-height: 46px; }
body.layout-portrait .rd-back-btn { width: 100%; }

/* ============================================================
 * 学习日报（2026-10-09）：结论在上，四科各一行；横屏左今天右一周。
 * 不走 .grid-2：宽度不到 900 时那条规则会把两列收成一列，
 * 手机和 APK 横过来正好踩中。竖屏只认 body.layout-portrait。
 * ============================================================ */
.report-split {
  display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(0, .85fr);
  gap: 16px; align-items: start;
}
body.layout-portrait .report-split { grid-template-columns: minmax(0, 1fr); }
.rpt-lead {
  background: linear-gradient(135deg, #e8efff 0%, #f6fbff 58%, #fff 100%);
  border-radius: 16px; padding: 14px 16px; margin-bottom: 4px;
}
.rpt-lead p { margin: 0 0 6px; font-size: 16px; line-height: 1.65; }
.rpt-lead p:first-child { font-weight: 800; }
.rpt-lead p:last-child { margin: 0; }
.rpt-row {
  display: flex; flex-wrap: wrap; gap: 8px 12px; align-items: baseline;
  margin-top: 8px; padding: 10px 12px; border-radius: 12px;
  background: #fffaf3; border-left: 4px solid var(--orange);
}
.rpt-row.done { background: #f3fbf6; border-left-color: var(--green); }
.rpt-name { font-weight: 800; min-width: 2.5em; }
.rpt-status { font-weight: 700; }
.rpt-row.done .rpt-status { color: var(--green); }
.rpt-row.todo .rpt-status { color: #c56a12; }
.rpt-meta { color: var(--text-light); font-size: 13px; }
.rpt-sum { font-weight: 800; margin: 4px 0 8px; }
.rpt-day {
  display: grid; grid-template-columns: 72px minmax(0, 1fr) 36px;
  gap: 8px; align-items: center; margin-top: 6px; font-size: 13px;
}
.rpt-day.today .rpt-day-d { font-weight: 800; color: var(--primary); }
.rpt-bar { height: 8px; background: #e7ecf8; border-radius: 99px; overflow: hidden; }
.rpt-bar i { display: block; height: 100%; width: 0; background: var(--primary); border-radius: 99px; }
.rpt-bar.full i { background: var(--green); }
.rpt-day-n { text-align: right; color: var(--text-light); }
.rpt-miss { padding: 8px 0; border-bottom: 1px solid #eef1f8; font-size: 14px; line-height: 1.55; }
.rpt-miss .tag { margin-right: 6px; }

/* 今日任务：四项并排，自己选，不单拎一项催 */
.today { max-width: 920px; }
.today-hello { display: flex; align-items: flex-end; justify-content: space-between; margin: 0 4px 14px; }
.today-hi { font-size: 13px; font-weight: 700; color: #7d89a6; }
.today-hello h2 { margin: 2px 0 0; font-size: 28px; letter-spacing: -.03em; }
.today-pips { display: flex; gap: 6px; padding-bottom: 8px; }
.today-pips i { width: 10px; height: 10px; border-radius: 50%; background: #d5deef; display: block; }
.today-pips i.on { background: #34c77b; box-shadow: 0 0 0 4px rgba(52,199,123,.15); }
.day-list, body:not(.layout-portrait) .day-list, body.layout-portrait .day-list {
  display: grid; grid-template-columns: 1fr 1fr; gap: 12px;
}
.day-list.extra { grid-template-columns: repeat(3, minmax(0, 1fr)); margin-top: 12px; }
.day-row {
  width: 100%; min-height: 138px; display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
  text-align: left; border: none; border-radius: 22px; padding: 16px;
  box-shadow: 0 10px 24px rgba(60, 90, 140, .08); border-left: none;
}
.day-row.subj-math { background: linear-gradient(165deg, #e7efff 0%, #fff 62%); }
.day-row.subj-zh { background: linear-gradient(165deg, #e5f8ec 0%, #fff 62%); }
.day-row.subj-en { background: linear-gradient(165deg, #fff0e3 0%, #fff 62%); }
.day-row:hover { transform: translateY(-2px); }
.day-row.done { box-shadow: none; }
.day-row.done .day-main b { color: #243049; }
.day-ico {
  width: 46px; height: 46px; border-radius: 16px; font-size: 24px; margin-bottom: 10px;
  display: flex; align-items: center; justify-content: center; background: #fff;
  box-shadow: 0 6px 14px rgba(60, 90, 140, .08); flex: none;
}
.day-main { display: flex; flex-direction: column; min-width: 0; }
.day-main b { font-size: 18px; }
.day-main small { color: #7d879c; font-size: 12px; margin-top: 2px; }
.day-go { margin-top: auto; font-style: normal; font-weight: 800; font-size: 13px; color: #4f7cff; }
.day-row.subj-zh .day-go { color: #1fa85a; }
.day-row.subj-en .day-go { color: #e07a1f; }
.day-row.done .day-go { color: #1fa85a; }
.day-list.extra .day-row {
  min-height: 0; align-items: center; text-align: center; background: #fff; padding: 14px 8px;
}
.day-list.extra .day-ico { margin-bottom: 6px; }
.day-list.extra .day-go { margin-top: 6px; }
.day-row.pet {
  grid-column: 1 / -1; min-height: 0; flex-direction: row; align-items: center; gap: 12px;
  margin-top: 12px; background: linear-gradient(90deg, #f3efff, #fff 70%);
}
.day-row.pet .day-ico { margin: 0; background: #fff; }
.day-row.pet .day-go { margin: 0 0 0 auto; color: #7b68d6; }

/* 宠物页：横竖屏都是上舞台、下内容。舞台铺满内容区宽度。 */
.pet-page { display: flex; flex-direction: column; gap: 14px; }
.pet-page .pet-stage { width: 100%; min-height: 46vh; box-sizing: border-box; }
.pet-page .pet-body.pm-act,
.pet-page .pet-body.pm-act .pet-canvas-el { width: 260px; height: 250px; }
.pet-dock {
  background: #fff; border-radius: 18px; padding: 14px 16px 12px;
  box-shadow: var(--shadow); margin-top: -36px; position: relative; z-index: 4;
}
.pet-dock .pet-status-bars {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 10px; margin-top: 8px; flex-wrap: nowrap;
}
.pet-dock .pet-stat {
  width: auto; min-width: 0; display: flex; align-items: center; gap: 8px;
}
.pet-dock .pet-stat .label {
  margin: 0; white-space: nowrap; flex: 0 0 auto; font-size: 13px;
}
.pet-dock .pet-stat .progress-bar { flex: 1 1 auto; min-width: 28px; margin: 0; }
.pet-hint { margin: 8px 0 0; font-size: 12px; }
.pet-lower { display: flex; flex-direction: column; gap: 12px; }
.pet-facts { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 8px; }
.pet-facts div { background: #fff; border-radius: 14px; text-align: center; padding: 10px 4px; box-shadow: var(--shadow); }
.pet-facts b { display: block; font-size: 20px; color: var(--primary); }
.pet-facts span { font-size: 12px; color: var(--text-light); }
.pet-band { background: #fff; border-radius: 16px; padding: 12px 14px; box-shadow: var(--shadow); }
.pet-sec-h { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 8px; }
.pet-bag-row, .pet-park-row { display: flex; gap: 10px; overflow-x: auto; padding-bottom: 2px; }
.pet-bag-item {
  flex: 0 0 auto; min-width: 76px; border: 1px solid #e7ecf8; background: #f7f9ff;
  border-radius: 14px; padding: 8px 10px; text-align: center; font-weight: 700; font-size: 13px;
}
.pet-bag-item span { display: block; font-size: 26px; }
.pet-bag-item i { display: block; font-style: normal; color: var(--text-light); font-size: 12px; }
.pet-bag-item.on { border-color: var(--green); background: #f3fbf6; }
.pet-park-row .park-card, .park-add {
  flex: 0 0 128px; min-height: 132px; margin: 0;
}
.park-add {
  border: 2px dashed #c8d4ea; background: #fff; border-radius: 14px;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px;
  font-size: 22px; color: var(--text-light); font-weight: 700;
}
.park-add span { font-size: 12px; }
.park-add.locked { opacity: .7; }
body.layout-portrait .pet-facts { grid-template-columns: repeat(3, minmax(0, 1fr)); }
body.layout-portrait .pet-page .pet-stage { min-height: 360px; }

/* 沙箱预览：首页主卡、学科色边、更多菜单、做题时只留返回、窄横屏仍两栏 */
.now-card {
  background: #fff; border-radius: 20px; padding: 22px 24px; margin-bottom: 14px;
  border-left: 6px solid var(--primary); box-shadow: var(--shadow);
}
.now-card h2 { margin: 4px 0 6px; font-size: 28px; }
.now-card p { margin: 0 0 14px; color: var(--text-light); }
.now-kicker { font-size: 13px; font-weight: 800; color: var(--primary); }
.now-card .btn-big { min-width: 140px; }
.now-dots { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 12px; }
.now-dot {
  background: #fff; border-radius: 999px; padding: 6px 12px; font-size: 13px; font-weight: 700;
  border: 2px solid #e7ecf8; color: var(--text-light);
}
.now-dot.done { color: var(--green); border-color: var(--green); background: #f3fbf6; }
.now-dot.subj-math { border-left: 4px solid var(--primary); }
.now-dot.subj-zh { border-left: 4px solid var(--green); }
.now-dot.subj-en { border-left: 4px solid var(--orange); }
.now-card.subj-math { border-left-color: var(--primary); }
.now-card.subj-zh { border-left-color: var(--green); }
.now-card.subj-en { border-left-color: var(--orange); }
.subj-math { border-left: 5px solid var(--primary); }
.subj-zh { border-left: 5px solid var(--green); }
.subj-en { border-left: 5px solid var(--orange); }
.now-extra, .now-pet {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 10px;
}
.now-count { margin-top: 4px; }
.nav-more { display: none; }
.nav-more.open { display: flex; flex-direction: column; gap: 2px; }
.nav-more-title { display: none; }
body.layout-portrait:has(#nav-more.open)::before {
  content: ''; position: fixed; inset: 0; z-index: 35;
  background: rgba(22, 30, 52, .32);
}
body.layout-portrait .nav-more.open {
  position: fixed; left: 12px; right: 12px; bottom: 84px; z-index: 40;
  background: #fff; border-radius: 24px; padding: 14px 10px 8px;
  box-shadow: 0 18px 50px rgba(30, 42, 80, .22);
}
body.layout-portrait .nav-more-title {
  display: block; font-size: 12px; font-weight: 800; letter-spacing: .12em;
  color: #9aa3b8; padding: 2px 12px 8px;
}
body.layout-portrait .nav-more .nav-item {
  flex-direction: row; justify-content: flex-start; align-items: center;
  width: 100%; min-width: 0; gap: 12px; padding: 12px 14px;
  font-size: 18px; text-align: left; border-radius: 14px;
}
body.layout-portrait .nav-more .nav-item span {
  display: inline; max-width: none; font-size: 15px; font-weight: 700; line-height: 1;
}
body.layout-portrait .nav-more .nav-item.active { background: #eef2ff; color: var(--primary); }
/* 专注页顶栏（← 返回 + 页面名）：2026-10-17 用户要求"整体变矮一些"。
 * 返回键本身在窄屏那一段里再压一档（padding 与字号），整行从约 50px 降到约 38px。 */
#focus-bar {
  display: flex; align-items: center; gap: 10px;
  padding: 4px 12px; background: #fff; border-bottom: 1px solid #e4e9f5;
}
#focus-title { font-weight: 800; }
body.focus-mode #sidebar { display: none; }
body.focus-mode #content-wrap { width: 100%; }
@media (max-width: 900px) {
  body:not(.layout-portrait) #sidebar { width: 196px; min-width: 196px; }
  body:not(.layout-portrait) .nav-item span { display: inline; }
  body:not(.layout-portrait) .grid-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
/* 第十八轮（2026-10-18 用户："横屏的时候在手机端或 apk 里，左边功能栏稍微窄一些"）：
 * 横屏时视口高只有约 400px，200px（窄一点的手机命中上面那条 196px）的侧栏白吃掉五分之一宽度，
 * 收到 150px 后"今日任务"四个字（15px 字号约 106px 含内边距）仍放得下。
 * 判据用 pointer:coarse + orientation:landscape，纯 CSS 不动 JS：
 * 桌面有鼠标是 fine 不受影响，代价是带触屏的笔记本横屏也会吃到这一档。
 * ⚠️ 必须排在上面 @media (max-width:900px) 那条**之后**：两条权重相同，靠后的才生效。 */
@media (pointer: coarse) and (orientation: landscape) {
  body:not(.layout-portrait) #sidebar { width: 150px; min-width: 150px; padding: 12px 8px; }
  body:not(.layout-portrait) .nav-item { padding: 8px 10px; gap: 8px; }
}
/* 首页宠物是透明画布，没有舞台时会透出白卡片，看起来像没背景 */
.pm-home {
  background: linear-gradient(180deg, #d7ecff 0%, #eef8e6 70%, #c9e8b4 100%);
  border-radius: 18px;
}
/* 今日任务结构同正式版，条目稍微提亮 */
.task-item { background: linear-gradient(90deg, #eef3ff, #f7f9ff); }
.task-item.done { background: linear-gradient(90deg, #e7f7ee, #f3fbf6); }
.task-icon {
  width: 44px; height: 44px; border-radius: 14px; flex: none;
  background: #fff; display: inline-flex; align-items: center; justify-content: center;
  box-shadow: 0 4px 12px rgba(60, 90, 140, .08);
}
.day-row.subj-math, .day-row.subj-zh, .day-row.subj-en { border-left: none; }

/* ============================================================
 * 英语语法（grammar-flow.js，2026-10-14）
 * 黄金句型：分区首页两卡 / 句型列表分组 / 句型详情（用法条 + 逐句朗读）。
 * 本块写在文件末尾：竖屏与窄屏的覆盖必须在主样式之后，否则被前面声明的规则盖掉。
 * ============================================================ */
.btn-icon {
  flex: none; width: 36px; height: 36px; border-radius: 11px; border: none;
  background: rgba(90, 140, 220, .12); color: #2f6fdd; cursor: pointer;
  font-size: 16px; line-height: 1; display: inline-flex; align-items: center; justify-content: center;
}
.btn-icon:active { transform: scale(.92); }

/* ---- 分区首页的两张卡 ---- */
.gs-card {
  display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
  margin-bottom: 14px; cursor: pointer; transition: transform .12s, box-shadow .12s;
}
.gs-card:active { transform: scale(.995); }
.gs-card-icon { font-size: 40px; flex: none; line-height: 1; }
.gs-card-text { flex: 1 1 240px; min-width: 0; }
.gs-card-text h3 { margin: 0 0 4px; font-size: 19px; }
.gs-card-text .muted { font-size: 13px; line-height: 1.7; }
.gs-card .progress-bar { max-width: 420px; }
/* 未实现的分区：整卡压暗、不响应点击手感（仍给 toast 说明，避免"死卡"错觉） */
.gs-card-soon { cursor: default; opacity: .72; background: linear-gradient(180deg, #fbfcfe, #f4f7fb); }
.gs-card-soon .gs-card-icon { filter: grayscale(.5); }

/* ---- 页内返回条 ---- */
.gs-topbar { align-items: center; gap: 12px; margin-bottom: 12px; }
.gs-topbar-title { font-size: 16px; }

/* ---- 列表 ---- */
.gs-list-tools { align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 10px; font-size: 13px; }
.gs-group { margin-bottom: 12px; padding: 0; overflow: hidden; }
.gs-group-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 12px 16px; background: rgba(90, 140, 220, .07); font-size: 13.5px;
}
.gs-group-head .muted { font-size: 12.5px; }
.gs-row {
  display: flex; align-items: flex-start; gap: 12px; padding: 13px 16px;
  border-top: 1px solid rgba(60, 90, 140, .08); cursor: pointer; min-height: 44px;
}
.gs-row:active { background: rgba(90, 140, 220, .07); }
.gs-no {
  flex: none; min-width: 34px; height: 24px; padding: 0 6px; border-radius: 8px;
  background: rgba(90, 140, 220, .14); color: #2f6fdd;
  font-size: 12.5px; font-weight: 700; display: inline-flex; align-items: center; justify-content: center;
}
.gs-name { flex: 1 1 auto; min-width: 0; font-size: 14.5px; line-height: 1.65; }
.gs-done .gs-name { color: #7a8798; }
.gs-done .gs-no { background: rgba(60, 160, 110, .14); color: #1f9d63; }
.gs-tick { flex: none; color: #1f9d63; font-weight: 700; font-size: 15px; line-height: 1.5; }

/* ---- 详情 ---- */
.gs-detail { padding-top: 16px; }
.gs-detail-head { display: flex; align-items: flex-start; gap: 12px; margin-bottom: 14px; }
.gs-detail-no {
  flex: none; min-width: 40px; height: 40px; padding: 0 10px; border-radius: 12px;
  background: linear-gradient(135deg, #4f8ef7, #6f6ae0); color: #fff;
  font-size: 17px; font-weight: 800; display: inline-flex; align-items: center; justify-content: center;
}
.gs-detail-name { flex: 1 1 auto; min-width: 0; margin: 0; font-size: 18px; line-height: 1.55; word-break: break-word; }
.gs-usage {
  display: flex; gap: 10px; align-items: flex-start; margin-bottom: 16px;
  padding: 12px 14px; border-radius: 12px; background: rgba(255, 176, 59, .12);
}
.gs-usage-tag {
  flex: none; padding: 3px 9px; border-radius: 7px; background: #f5a623; color: #fff;
  font-size: 12px; font-weight: 700;
}
.gs-usage-text { flex: 1 1 auto; min-width: 0; font-size: 13.5px; line-height: 1.85; color: #6a5320; }
.gs-ex-label { font-size: 13px; font-weight: 700; color: #7a8798; margin-bottom: 8px; letter-spacing: .5px; }
.gs-example { padding: 12px 0; border-top: 1px dashed rgba(60, 90, 140, .16); }
.gs-example:first-of-type { border-top: none; }
.gs-en { display: flex; align-items: flex-start; gap: 10px; }
.gs-en-text { flex: 1 1 auto; min-width: 0; font-size: 16px; line-height: 1.7; font-weight: 600; word-break: break-word; }
.gs-cn { margin-top: 5px; padding-left: 46px; font-size: 13.5px; line-height: 1.75; color: #66707f; }
.gs-src { margin-top: 14px; padding-top: 12px; border-top: 1px solid rgba(60, 90, 140, .1); font-size: 11.5px; }
.gs-nav-row { align-items: center; justify-content: space-between; gap: 10px; margin-top: 4px; }

/* ---- 窄屏 / 触屏：中文译文与例句按钮要收得下 390px ---- */
@media (max-width: 600px) {
  .gs-card { gap: 12px; padding: 16px; }
  .gs-card-icon { font-size: 32px; }
  .gs-card-text { flex-basis: calc(100% - 52px); }
  .gs-card .btn { width: 100%; }
  .gs-detail-name { font-size: 16.5px; }
  .gs-en-text { font-size: 15.5px; }
  /* 竖排时译文不再跟着英文缩进，否则手机上正文只剩半行宽 */
  .gs-cn { padding-left: 0; }
  .btn-icon { width: 40px; height: 40px; }
}
/* 竖屏 App 形态：分组卡的左右内边距再收一点，编号列不被挤成两行 */
body.layout-portrait .gs-row { padding: 12px 13px; gap: 10px; }
body.layout-portrait .gs-group-head { padding: 11px 13px; }

/* ============================================================
 * 常考短语模块（2026-10-14，星火 P2）—— pf- 前缀
 * 卡片在单词主页（WordFlow.renderHome 调 PhraseFlow.homeCardHtml），
 * 学习卡 / 复习卡整页接管 #view-root，复用 .word-card 与 .quiz-options 系样式。
 * ============================================================ */
.pf-card { margin-top: 14px; display: flex; flex-direction: column; gap: 12px; }
.pf-head { display: flex; align-items: center; gap: 14px; min-width: 0; }
.pf-icon { font-size: 34px; flex: none; line-height: 1; }
.pf-text { flex: 1 1 auto; min-width: 0; }
.pf-text h3 { margin: 0 0 4px; }
.pf-text p { margin: 0; font-size: 13px; line-height: 1.55; }
.pf-actions { display: flex; gap: 10px; flex-wrap: wrap; }
.pf-actions .btn { flex: 1 1 160px; min-height: 44px; }
.pf-note { font-size: 12px; line-height: 1.7; margin: 0; }
/* 今天做完转成淡绿底，和"今日已完成"的绿标呼应 */
.pf-done { background: linear-gradient(180deg, #f4fbf7, #eefaf2); }
.pf-headbar { max-width: 640px; margin: 0 auto 12px; }
/* 朗读按钮行：.speak-btn 是 56px 圆钮（只放图标），旁边挂"慢速"胶囊，整行居中 */
.pf-speak-row { display: flex; align-items: center; justify-content: center; gap: 10px; margin-top: 10px; }
.pf-fb { border-radius: 12px; padding: 12px 16px; font-size: 15px; margin-top: 14px; text-align: center; line-height: 1.6; }
.pf-fb.ok { background: #e6f7ee; color: #1f7a4d; }
.pf-fb.bad { background: #ffecec; color: #c0392b; }
/* 短语整串比单词长得多（on the one hand.../on the other hand...），44px 的 .word-big
 * 会直接撑破卡片。用双类选择器提权重压字号——单类压不过先声明的主样式，
 * 这条教训记在 AGENTS.md §7 的"表单权重把数字裁掉"那一行；同时允许按词断行。 */
.word-big.pf-phrase-big { font-size: 28px; line-height: 1.4; letter-spacing: 0; word-break: break-word; }
.quiz-options .quiz-opt.pf-opt { word-break: break-word; line-height: 1.5; }

/* ---- 窄屏 / 触屏 ---- */
@media (max-width: 600px) {
  .pf-head { gap: 10px; }
  .pf-icon { font-size: 28px; }
  .pf-actions .btn { flex: 1 1 100%; }
  .word-big.pf-phrase-big { font-size: 23px; }
  /* 只收短语复习卡的选项列数：.quiz-options 是单词考核/查询复习共用的，
   * 在这里全局改列数会顺带改掉那两处的排版（回归风险），所以限定在 .pf-card 内 */
  .pf-card .quiz-options { grid-template-columns: 1fr; }
}
/* 竖屏 App 形态：卡片左右内边距收一点 */
body.layout-portrait .pf-card { padding: 14px; }

/* ============================================================
 * 英语阅读 · 70 篇短文闯关（2026-10-15 重做，规格 docs/specs/2026-10-15-reading-70-passages-design.md）
 * 前缀 rp-。分五块：闯关地图 / 关卡列表 / 正文与播放条 / 词汇汇总 / 测验与成绩。
 * 地图是内联 SVG，颜色全靠 class（不用渐变、不用 <defs>，避免多张 SVG 撞 id）。
 * ============================================================ */

/* ---------- ① 闯关地图 ---------- */
.rp-mapwrap {
  position: relative; width: 100%;
  /* 用底图的宽高比撑高度：这样 SVG 不会上下留黑边（meet 对齐会 letterbox）。
   * 窗口特别扁的时候 max-height 才生效，那时留一点天空底也看不出来（底色与底图同为浅蓝） */
  aspect-ratio: 1000 / 720; max-height: 78vh; min-height: 260px;
  border-radius: 18px; overflow: hidden; background: #93cff2;
  box-shadow: var(--shadow); border: 1px solid #7fb8e4;
}
.rp-svg { display: block; width: 100%; height: 100%; touch-action: none; user-select: none; cursor: grab; }
.rp-svg:active { cursor: grabbing; }
.rp-land { pointer-events: none; }
/* 路（2026-10-16 第三轮按样图）：没走过 = 一根浅灰细虚线；走过 = 橙管三层
 * （深橙描边 + 中橙管身 + 顶高光），三层都用 pathLength="1000" + stroke-dasharray
 * 只画前 N/1000 段，解锁动画对三层一起插值 */
.rp-road-bg { fill: none; stroke: #e4e9f1; stroke-width: 4; stroke-dasharray: 8 6; stroke-linecap: round; stroke-linejoin: round; }
.rp-road-on { fill: none; stroke: #e8891f; stroke-width: 12; stroke-linecap: round; stroke-linejoin: round; }
.rp-road-mid { fill: none; stroke: #f9a63a; stroke-width: 8; stroke-linecap: round; stroke-linejoin: round; }
.rp-road-core { fill: none; stroke: #ffd9a0; stroke-width: 3; stroke-linecap: round; stroke-linejoin: round; }
/* 圆按钮（2026-10-16 样图质感）：面用 SVG 径向渐变（高光在左上），下沿是深色 rim；
 * 渐变 id 在 reading-flow.js 的 <defs> 里（rp-g-pass / rp-g-off），class 切换照旧只换 fill */
.rp-nodeg { cursor: pointer; }
#rp-nodesg { filter: drop-shadow(0 2px 2px rgba(70, 55, 25, .28)); }
.rp-node .rp-rim, .rp-node .rp-face { transition: fill .35s ease; }
.rp-node.rp-pass .rp-rim { fill: #c96a10; }
.rp-node.rp-pass .rp-face { fill: url(#rp-g-pass); }
.rp-node.rp-off .rp-rim { fill: #a9b2be; }
.rp-node.rp-off .rp-face { fill: url(#rp-g-off); }
/* 白圈：跟底图白色缝隙同色系，把按钮从底色里"描"出来（第四轮） */
.rp-node .rp-face { stroke: #ffffff; stroke-width: 1.8; }
.rp-node .rp-gloss { fill: #ffffff; opacity: .85; }
/* 落地小投影：让按钮"坐"在地图上而不是贴纸似的浮着（第四轮，配合白圈更立体） */
.rp-nsh { fill: #4a3a20; opacity: .15; }
.rp-lockg { transition: opacity .4s ease; }
.rp-lockg.rp-shake { animation: rp-shake .45s ease; }
/* 解锁第②段（第四轮）：整把锁放大到 1.4 倍，随后左右两半（rp-lh/rp-rh，
 * 由 lockSvg 拆出）向两侧推开 + 旋转 + 淡出 = "裂开"；transform-box 让缩放/旋转
 * 绕锁自己的中心，而不是 SVG 画布原点 */
.rp-lockg.rp-crack { animation: rp-lock-grow .5s ease forwards; transform-box: fill-box; transform-origin: 50% 65%; }
.rp-lockg.rp-crack .rp-lh { animation: rp-split-l .36s ease-in .14s forwards; transform-box: fill-box; transform-origin: 100% 60%; }
.rp-lockg.rp-crack .rp-rh { animation: rp-split-r .36s ease-in .14s forwards; transform-box: fill-box; transform-origin: 0% 60%; }
@keyframes rp-lock-grow { 0% { transform: scale(1); } 55% { transform: scale(1.42); } 100% { transform: scale(1.46); } }
@keyframes rp-split-l { to { opacity: 0; transform: translateX(-6px) rotate(-26deg); } }
@keyframes rp-split-r { to { opacity: 0; transform: translateX(6px) rotate(26deg); } }
@keyframes rp-shake {
  0%, 100% { transform: translateX(0); }
  20% { transform: translateX(-3px); }
  40% { transform: translateX(3px); }
  60% { transform: translateX(-2px); }
  80% { transform: translateX(2px); }
}
/* 当前关的发光圆座 + 角色（样图：柔光晕 → 橙环 → 渐变座面 + 浅边，三层同心） */
.rp-halo { fill: #ffe9a8; opacity: .5; transform-box: fill-box; transform-origin: center; animation: rp-pulse 2.2s ease-in-out infinite; }
.rp-halo2 { fill: #f79a3c; opacity: .95; }
.rp-seat { fill: url(#rp-g-seat); stroke: #ffd9a0; stroke-width: 1.5; }
/* 城堡底座：样图里城堡也站在橙色 glossy 圆盘上，不是奶油色椭圆 */
.rp-base { fill: url(#rp-g-seat); stroke: #d9791c; stroke-width: 2; opacity: .98; }
@keyframes rp-pulse {
  0%, 100% { opacity: .45; transform: scale(1); }
  50% { opacity: .8; transform: scale(1.09); }
}
/* 闯关宠物叠加层（2026-10-16 第五轮）：盒子按锚点定位，scale 由 syncPet 写进 transform
 * （永远 ≤1 = 超采样，放大也不糊）；transform-origin 50% 100% 与 translate(-50%,-100%)
 * 对齐——缩放的支点就是"脚底"。pointer-events:none：地图手势（拖/捏/点）全部穿透到下面的
 * svg，宠物本身不可拖。
 * 第六轮：盒子 192×228 → **230×274（上下左右各 1.2 倍）**——螃蟹这类横向宽的品种
 * 钳子会被画布左右裁掉；分母同步走 PET_BOX_W，所以宠物呈现尺寸不变，只是四周多了余量。
 * 改这两个数必须一起改 reading-flow.js 的 PET_BOX_W / PET_BOX_H。 */
.rp-petlayer { position: absolute; left: 0; top: 0; width: 230px; height: 274px;
  transform-origin: 50% 100%; pointer-events: none; z-index: 2; }
.rp-petlayer canvas { display: block; }
.rp-petlayer img { width: 100%; height: 100%; object-fit: contain; }
/* 点当前关的"原地跳"：用 margin-top 做，不碰 transform（transform 是 syncPet 在写的） */
.rp-petlayer.rp-hop { animation: rp-pet-hop .34s ease; }
@keyframes rp-pet-hop {
  0%, 100% { margin-top: 0; }
  45% { margin-top: -11px; }
}
/* 当前关旁的名牌：写这一篇的英文标题（图上不印任何地名） */
.rp-plate rect { fill: #ffffff; stroke: #e3e8f0; stroke-width: 1.5; filter: drop-shadow(0 2px 3px rgba(40, 60, 100, .18)); }
.rp-plate text { font-size: 14px; font-weight: 700; fill: #33415c; font-family: inherit; }
.rp-flag { filter: drop-shadow(0 1px 1px rgba(0, 0, 0, .15)); }
/* 顶部胶囊进度条（2026-10-16 第三轮按样图）：白边圈 + 浅灰轨道 + 内缩的橙色圆头填充，
 * 数字坐在轨道右段（填充涨到数字底下会被 max-width 挡住），右端一枚金圆星徽。
 * 2026-10-16 竖屏优化：所有尺寸改成 calc(原值 × var(--rp-s))，--rp-s 由 reading-flow.js 的
 * syncScale() 写在 .rp-mapwrap 上（地图宽 / 470，夹在 0.8~1.15）——
 * 地图窄下来，进度条与下面的药丸就跟着等比缩，不再出现"按钮比地图还抢眼"。
 * 默认值 1 保证 JS 没跑到时（列表页、静态核对页）排版与原来完全一致。 */
.rp-topbar { position: absolute; left: calc(8px * var(--rp-s, 1)); right: calc(8px * var(--rp-s, 1)); top: calc(8px * var(--rp-s, 1)); display: flex; justify-content: center; pointer-events: none;
  z-index: 5;   /* 与右下 .rp-pill 同层：宠物叠加层是 2，进度条不能被它盖住（2026-10-17 用户要求） */ }
.rp-cap {
  position: relative; pointer-events: auto; width: min(calc(360px * var(--rp-s, 1)), 92%); height: calc(34px * var(--rp-s, 1));
  background: #e9edf3; border: calc(3px * var(--rp-s, 1)) solid #ffffff; border-radius: 999px;
  box-shadow: 0 3px 10px rgba(40, 60, 100, .16); overflow: hidden;
}
.rp-cap-fill {
  position: absolute; left: 2px; top: 2px; bottom: 2px; border-radius: 999px;
  background: linear-gradient(180deg, #ffb54d, #f28c1c); transition: width .5s ease;
  max-width: calc(100% - 96px);
}
.rp-cap-num {
  position: absolute; right: calc(40px * var(--rp-s, 1)); top: 0; line-height: calc(28px * var(--rp-s, 1));
  font-size: calc(13px * var(--rp-s, 1));
  font-weight: 800; color: #5b6472; letter-spacing: .5px;
}
.rp-cap-star {
  position: absolute; right: calc(5px * var(--rp-s, 1)); top: 50%; transform: translateY(-50%);
  width: calc(24px * var(--rp-s, 1)); height: calc(24px * var(--rp-s, 1)); border-radius: 50%;
  font-size: calc(13px * var(--rp-s, 1)); line-height: 1; color: #fff;
  background: radial-gradient(circle at 35% 30%, #ffd978, #f6a92c);
  box-shadow: 0 0 0 2px #ffffff; display: flex; align-items: center; justify-content: center;
}
/* 右下角一条圆角工具条：放大 / 缩小 / 定位 / 全图 / 列表（样图：圆钮直接挨着，无分隔线）。
 * 同样跟着 --rp-s 等比缩：竖屏 427px 宽时 s≈0.91，六个钮总宽从约 300px 降到约 250px。 */
.rp-pill {
  position: absolute; right: calc(10px * var(--rp-s, 1)); bottom: calc(10px * var(--rp-s, 1));
  display: flex; align-items: center; gap: calc(6px * var(--rp-s, 1));
  padding: calc(5px * var(--rp-s, 1)) calc(8px * var(--rp-s, 1));
  background: rgba(255, 255, 255, .96); border: 1px solid #e3e8f0;
  border-radius: 999px; box-shadow: 0 4px 14px rgba(40, 60, 100, .18);
  z-index: 5;   /* 第五轮：药丸永远压在宠物叠加层（z-index:2）上面，宠物走到哪都不能挡按钮 */
}
.rp-pill button {
  width: calc(34px * var(--rp-s, 1)); height: calc(34px * var(--rp-s, 1)); border: none;
  background: #eef1f6; border-radius: 50%;
  font-size: calc(16px * var(--rp-s, 1)); line-height: 1; color: #8a94a3; cursor: pointer;
}
.rp-pill button:active { background: #e0e6ef; color: #5b6472; }

/* ---------- ② 关卡列表 ---------- */
.rp-list { display: flex; flex-direction: column; gap: 8px; margin-top: 10px; }
.rp-row {
  display: flex; align-items: center; gap: 10px; padding: 10px 12px;
  border-radius: 12px; background: #f7f9fd; border: 1px solid transparent; cursor: pointer;
}
.rp-row-pass { background: #fff6ec; border-color: #ffdcb4; }
.rp-row-cur { background: #eef4ff; border-color: #cddcff; }
.rp-row-lock { opacity: .72; }
.rp-rowico { flex: none; font-size: 18px; }
.rp-rowmain { flex: 1 1 auto; min-width: 0; }
.rp-rowmain b { display: block; font-size: 15px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rp-rowmain .sl { display: block; font-size: 12.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* 状态标签必须一行显示（2026-10-17 用户在手机上反馈"已通过"折成两行）：
 * flex 子项默认 flex-shrink:1，标题列有 min-width:0 会先让位，但标签自己也会被压扁换行，
 * 所以给它 flex:none + nowrap——放不下时省略号只砍标题（上面那条），不砍状态。 */
.rp-row .tag { flex: none; white-space: nowrap; }

/* ---------- ③ 正文与播放条 ---------- */
.rp-title { font-size: 19px; line-height: 1.35; word-break: break-word; margin: 0; }
/* 篇头与词汇页头那行同理（2026-10-17 用户："通过和未通过要始终保持一行显示，其余空间可以给标题"）：
 * `.row.spread` 是 flex，英文标题一长就把右边的「已通过 92分 / 未通过」挤成两行。
 * 给状态标签 flex:none + nowrap 保住整块，标题列 min-width:0 让它自己折行让位。 */
.rp-head .tag, .rp-vhead .tag { flex: none; white-space: nowrap; }
.rp-head .rp-title, .rp-vhead h3 { flex: 1 1 auto; min-width: 0; }
.rp-titlecn { margin-top: 4px; }
.rp-meta { display: flex; flex-wrap: nowrap; gap: 6px; margin-top: 8px; font-size: 12.5px; color: var(--text-light); }
.rp-meta span { padding: 2px 8px; border-radius: 999px; background: #f4f7ff; white-space: nowrap; }
.rp-meta .rp-t-hi { background: #fdeceb; color: #c0392b; }
.rp-meta .rp-t-mid { background: #e8f1fd; color: #1f6fd0; }
.rp-meta .rp-t-low { background: #eaf7e5; color: #3f8a26; }
.rp-audio { margin-top: 10px; padding: 8px 10px; background: #f7f9fd; border: 1px solid #e8edf7; border-radius: 12px; }
/* 不换行：播放键 / 当前时间 / 进度条 / 时长 / 倍速**始终一行**（2026-10-17 用户要求）。
 * 进度条 flex:1 1 auto + min-width 收小，窄屏下让位的是它，不是按钮。 */
.rp-audio-main { display: flex; align-items: center; gap: 6px; flex-wrap: nowrap; }
.rp-play {
  flex: none; width: 34px; height: 34px; border: none; border-radius: 50%;
  background: var(--primary); color: #fff; font-size: 14px; cursor: pointer;
}
.rp-time { flex: none; font-size: 12px; color: var(--text-light); font-variant-numeric: tabular-nums; }
.rp-seek { flex: 1 1 auto; min-width: 60px; height: 28px; accent-color: var(--primary); background: transparent; }
/* 倍速按钮整块退役（2026-10-17 用户："把 0.75 和 1 倍这种按钮都去掉"）——
 * 真人录音固定 1 速播放，播放条只剩 播放键 / 时间 / 进度条 / 时长。 */
/* 中译里书自带的英文括注（"年轻军官(young officer)"）：灰、小一号，只看不点 */
.rp-gloss { color: #97a1b3; font-size: .9em; }
.rp-audio-bad { font-size: 13.5px; color: #a15c00; background: #fff8ec; padding: 10px 12px; border-radius: 10px; line-height: 1.6; }
.rp-para {
  position: relative; padding: 4px 0 12px 30px; line-height: 2.05; font-size: 17px;
  border-bottom: 1px dashed #eef1f7;
}
.rp-para:last-of-type { border-bottom: none; }
.rp-pno {
  position: absolute; left: 0; top: 8px; width: 20px; height: 20px; border-radius: 50%;
  background: #eef2fb; color: #7b879f; font-size: 12px; text-align: center; line-height: 20px;
}
.rp-en { color: #22304d; }
/* 着色词：红=高频、蓝=中频、绿=低频，都能点开详情弹窗 */
.rp-w { cursor: pointer; border-radius: 4px; padding: 0 1px; font-weight: 700; }
.rp-w.rp-hi { color: #c0392b; background: #fdeceb; box-shadow: inset 0 -2px 0 #f2b8ae; }
.rp-w.rp-mid { color: #1f6fd0; background: #e8f1fd; box-shadow: inset 0 -2px 0 #b9d3f5; }
.rp-w.rp-low { color: #3f8a26; background: #eaf7e5; box-shadow: inset 0 -2px 0 #c2e3b4; }
.rp-cn {
  margin-top: 6px; padding: 8px 10px; font-size: 14.5px; line-height: 1.85; color: #6b7689;
  background: #f7f9fd; border-left: 3px solid #dfe6f3; border-radius: 0 8px 8px 0;
}
.rp-tip { font-size: 13px; margin: 10px 0 0; }
.rp-sticky {
  display: flex; gap: 10px; justify-content: center; margin-top: 12px; padding: 10px;
  background: rgba(255, 255, 255, .95); border-radius: 14px; box-shadow: var(--shadow);
  position: sticky; bottom: 8px; z-index: 18;
}
.rp-sticky .btn { flex: 1 1 0; }

/* ---------- ④ 词汇汇总 ---------- */
.rp-vg { border-left: 4px solid #dfe5f0; }
.rp-vg-hi { border-left-color: #c0392b; }
.rp-vg-mid { border-left-color: #1f6fd0; }
.rp-vg-low { border-left-color: #4a9d2f; }
.rp-vg h4 { margin: 0; font-size: 16px; }
.rp-wlist { display: flex; flex-direction: column; margin-top: 6px; }
.rp-wrow {
  display: flex; align-items: center; gap: 8px; padding: 9px 4px;
  border-bottom: 1px solid #f0f3f9; cursor: pointer;
}
.rp-wrow:last-child { border-bottom: none; }
.rp-ww { flex: none; min-width: 96px; max-width: 40%; font-weight: 800; font-size: 16px; color: #22304d; word-break: break-word; }
.rp-wp { flex: none; font-size: 13px; color: #8a94a8; }
.rp-wm { flex: 1 1 auto; min-width: 0; font-size: 14px; color: #4a5670; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rp-ws { flex: none; font-size: 12px; color: #b5651d; background: #fff8ec; border-radius: 999px; padding: 1px 8px; }
.rp-wsay {
  flex: none; width: 34px; height: 34px; border-radius: 50%; border: 1px solid #e3e8f2;
  background: #fff; cursor: pointer; font-size: 14px;
}

/* ---------- ⑤ 测验与成绩 ---------- */
.rp-quiz, .rp-result { max-width: 640px; margin: 0 auto; }
.rp-qbar { height: 8px; background: #eef1f7; border-radius: 999px; overflow: hidden; margin: 10px 0 14px; }
.rp-qbar i { display: block; height: 100%; background: linear-gradient(90deg, #7aa5ff, var(--primary)); transition: width .3s; }
.rp-qtype { font-size: 13px; color: #7b879f; margin-bottom: 10px; }
.rp-qtier { padding: 2px 10px; border-radius: 999px; font-size: 12px; color: #fff; margin-left: 6px; }
.rp-qtier.rp-t-hi { background: #c0392b; }
.rp-qtier.rp-t-mid { background: #1f6fd0; }
.rp-qtier.rp-t-low { background: #4a9d2f; }
.rp-prompt { text-align: center; padding: 12px 6px 4px; }
.rp-prompt .muted { margin: 8px 0 0; font-size: 13.5px; }
.rp-meaning { font-size: 21px; font-weight: 700; color: #22304d; line-height: 1.5; word-break: break-word; }
.rp-listen { display: flex; flex-direction: column; align-items: center; gap: 10px; }
.rp-big-say { width: 76px; height: 76px; font-size: 32px; }
.rp-opts { margin-top: 14px; }
.rp-spell { display: flex; gap: 8px; margin-top: 14px; justify-content: center; }
/* input.rp-spell-in 的双类写法是为了压过全局 input[type=text]（权重 0,1,1），
 * 单类会被盖掉 padding 与字号（AGENTS.md §7 那条坑） */
input.rp-spell-in {
  flex: 1 1 200px; max-width: 320px; min-height: 46px; padding: 10px 14px;
  font-size: 18px; text-align: center; letter-spacing: 1px; border-radius: 12px;
}
.rp-fb { border-radius: 12px; padding: 12px 14px; margin-top: 14px; font-size: 15px; line-height: 1.6; text-align: center; }
.rp-fb.good { background: #e6f7ee; color: #1f7a4d; }
.rp-fb.bad { background: #ffecec; color: #c0392b; }
.rp-qfoot { display: flex; justify-content: center; align-items: center; gap: 10px; margin-top: 14px; min-height: 44px; }
.rp-result { text-align: center; }
.rp-scorewrap { margin: 6px 0 10px; }
.rp-score { font-size: 58px; font-weight: 800; line-height: 1.05; color: #7b879f; }
.rp-score.ok { color: #1f9d57; }
.rp-score.no { color: #e07b39; }
.rp-reward { color: #1f7a4d; font-weight: 700; }
.rp-chips { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; margin-top: 8px; }
.rp-chip {
  display: inline-flex; flex-direction: column; align-items: flex-start; gap: 2px;
  padding: 6px 12px; background: #fff5f5; border: 1px solid #ffd9d9; border-radius: 12px;
  font-weight: 700; font-size: 15px; color: #c0392b; cursor: pointer; text-align: left;
}
.rp-chip i { font-style: normal; font-weight: 400; font-size: 12.5px; color: #9a7474; }
.rp-rfoot { display: flex; flex-wrap: wrap; gap: 10px; justify-content: center; margin-top: 18px; }

/* 2026-10-16 第四轮：这里原来有一段 @media (prefers-reduced-motion: reduce) 把
 * 呼吸光圈/跳跃/摇晃/变色过渡全关掉——已删。地图的走位与解锁动画是玩法本身，
 * 且不少 Windows 环境永远报 reduce，留着就等于功能被系统设置吃掉（详见 reading-flow.js 头注）。 */

/* ---- 窄屏 / 触屏（新功能的覆盖规则必须写在主样式之后，AGENTS.md §6） ---- */
@media (pointer: coarse) {
  /* 触屏本来按"点击目标 ≥44px"的约定把圆钮放大到 44，但竖屏地图上六个钮并排就吃掉约 300px
   * （地图才 427px 宽），把地图压成一条 —— 2026-10-16 改成 40px 基准再乘 --rp-s：
   * 手机竖屏 s≈0.91 → 实际约 36px，视觉不抢地图，指认仍然够（药丸本身还有 padding 与间距）。 */
  .rp-pill button { width: calc(40px * var(--rp-s, 1)); height: calc(40px * var(--rp-s, 1)); font-size: calc(17px * var(--rp-s, 1)); }
  .rp-wsay { width: 40px; height: 40px; }
  .rp-wrow { padding: 12px 4px; }
}
@media (max-width: 600px) {
  .rp-mapwrap { max-height: 56vh; min-height: 240px; border-radius: 14px; }
  .rp-cap { width: min(calc(360px * var(--rp-s, 1)), 94%); }
  .rp-pill { right: calc(6px * var(--rp-s, 1)); bottom: calc(6px * var(--rp-s, 1)); padding: calc(3px * var(--rp-s, 1)) calc(4px * var(--rp-s, 1)); }
  .rp-title { font-size: 17px; }
  .rp-para { font-size: 16px; line-height: 1.95; padding-left: 26px; }
  /* 音标在窄屏挤掉释义，词详情弹窗里还有，这里让位 */
  .rp-wp { display: none; }
  .rp-ww { min-width: 74px; font-size: 15px; }
  .rp-wm { font-size: 13px; }
  .rp-sticky { flex-wrap: wrap; }
  .rp-sticky .btn { flex: 1 1 100%; min-height: 46px; }
  .rp-meaning { font-size: 19px; }
  /* 只收阅读测验的选项列数：.quiz-options 是单词考核/短语/查询复习共用的，
   * 全局改列数会顺带改掉那几处的排版（同 .pf-card 的处理） */
  .rp-quiz .quiz-options { grid-template-columns: 1fr; }
  .rp-rfoot .btn { flex: 1 1 100%; }
  .rp-score { font-size: 48px; }
}
body.layout-portrait .rp-sticky {
  bottom: 0; margin: 12px -12px -12px; border-radius: 0;
  padding: 10px 12px calc(10px + env(safe-area-inset-bottom, 0px));
}

/* ---------- 竖屏下的闯关地图（2026-10-16 用户反馈"整体显示不合理、显得太小"） ----------
 * 原来 .rp-mapwrap 锁死 aspect-ratio: 1000/720（因为 SVG 是 meet 对齐，容器比例不对就会
 * letterbox），竖屏 427px 宽 → 地图只有约 260px 高，下面整片空白，节点和宠物都小。
 * 现在 reading-flow.js 的 viewBox 按容器真实宽高比出框，多出来的高度就是"多看到一截地图"，
 * 所以竖屏直接把地图撑到内容区高度；默认取景拉近（PORT.k=2.5）与进度条/药丸等比缩
 * （--rp-s 由 syncScale 写在容器上）分别在 JS 和上面几处 CSS 里。
 * 400px 下限保证小屏手机（CSS 高约 640）也不会比原来更矮；62vh 让它不至于顶出内容区
 * 造成双层滚动。横屏与桌面完全不受影响——选择器以 body.layout-portrait 开头。 */
body.layout-portrait .rp-mapwrap {
  aspect-ratio: auto;
  height: clamp(400px, 62vh, 720px);
  max-height: none; min-height: 0;
}

/* ---------- 背单词学习卡 / 单词详情弹窗的窄屏收口（2026-10-17 用户在手机与 APK 上反馈） ----------
 * ① 底部三个动作：一行三个在 390~430px 宽里挤成三行、每行宽度还不一样，改成
 *    "主按钮整行 + 两个次按钮各占一半"（窄屏与竖屏同一条口径，APK 竖屏走的就是这条）。
 * ② 学习卡内边距：主样式 40px 是给 640px 宽的桌面卡片定的，窄屏下白白吃掉 80px 内容宽。
 * ③ 朗读圆钮：主样式收小到 40px（它旁边有音标文字撑着一行，不再是唯一大目标），
 *    触屏下补回 44px 的点击目标下限（§6 的触摸基线）。 */
@media (max-width: 600px) {
  .wc-acts { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
  .wc-acts .wc-main { grid-column: 1 / -1; order: -1; }
  .wc-acts .btn { min-height: 46px; margin: 0; }
  /* 学习卡上内边距 24 → 14：用户"页面整体再往上一点，别空那么大的空间"。
   * 专注页（背词/考核/阅读正文）的 #view-root 顶部内边距也一并从 12 收到 8，
   * 两处加起来单词那行能往上挪约 20px，小屏上正好多出一行释义。 */
  .word-card { padding: 14px 16px; }
  body.focus-mode #view-root { padding-top: 8px; }
}
/* 专注页顶栏的返回键：窄屏与竖屏下压到 30px 高、字号 13px（用户："这一行整体变矮一些"） */
/* 行高 token（2026-10-17 第十二轮追加）：专注页顶栏与英语页状态行之前各写各的 padding，
 * 一个 34px 一个 26px，用户看着就是"两行不齐"，所以收成同一个 --row-h。
 * 2026-10-18 第十四轮：30 → 36px（"左上角带返回按钮的地方，高度稍微提高一些"，这行还要放计时）；
 * 第十五轮再加一档：36 → 42px（"稍微再高一些"）。
 * 第十八轮（2026-10-18）拆成两个值：--row-h 只管专注页顶栏（用户："返回条的高度正好，保持不变"），
 * --head-h 管英语页状态行（只有标题 + 四颗胶囊，42px 白吃近 20px → 34px）。
 * 两条横条不会同时出现在屏幕上，所以这里不再要求等高；但**窄屏与竖屏两处必须同值**。 */
body.layout-portrait { --row-h: 42px; --head-h: 34px; }
@media (max-width: 600px) {
  body { --row-h: 42px; --head-h: 34px; }
  #focus-bar { min-height: var(--row-h); }
  .eng-head { min-height: var(--head-h); }
}
body.layout-portrait #focus-bar { padding: 3px 10px; min-height: var(--row-h); }
body.layout-portrait #focus-back { padding: 2px 10px; font-size: 13px; }
body.layout-portrait .wc-topbar .btn-mini { padding: 5px 10px; font-size: 13px; }
body.layout-portrait .wc-acts { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
body.layout-portrait .wc-acts .wc-main { grid-column: 1 / -1; order: -1; }
body.layout-portrait .wc-acts .btn { min-height: 46px; margin: 0; }
body.layout-portrait .word-card { padding: 14px 16px; }
body.layout-portrait #view-root { padding-top: 8px; }
@media (pointer: coarse) {
  .wd-say .wd-say-btn { width: 30px; height: 30px; font-size: 16px; }
}

/* ---------- 手机端整体密度（2026-10-17 用户："每个页面各小模块之间间隔都有点大"） ----------
 * 主样式是给 900px+ 的桌面写的：卡片 20px 内边距 + 16px 下边距，一行卡片之间就是 36px 空白，
 * 在 390px 宽的屏上等于每屏少看一整块内容。这里统一收一档，**桌面与横屏不受影响**
 * （选择器要么带 max-width:600px，要么以 body.layout-portrait 开头）。
 * 商城是重灾区：2 列 × 大图标 × 描述占 32px 高，一屏只看得见 4 个商品 → 改 3 列 + 收图标 +
 * 描述不再预留两行高度（放不下就让卡片自己长）。 */
@media (max-width: 600px) {
  .card { padding: 14px; margin-bottom: 10px; }
  .card h3 { margin-bottom: 8px; font-size: 16px; }
  .word-meta { margin-bottom: 4px; }
  .word-meta .tag { font-size: 11px; padding: 1px 8px; }
  .shop-grid { grid-template-columns: repeat(3, 1fr); gap: 8px; }
  .shop-item { padding: 10px 6px; border-radius: 12px; }
  .shop-item .si-icon { font-size: 30px; }
  .shop-item .si-name { font-size: 13px; margin: 5px 0 2px; }
  .shop-item .si-desc { font-size: 11px; min-height: 0; line-height: 1.35; }
  .shop-item .si-price { font-size: 13px; margin: 4px 0 6px; }
}
body.layout-portrait .card { padding: 14px; margin-bottom: 10px; }
body.layout-portrait .shop-grid { grid-template-columns: repeat(3, 1fr); gap: 8px; }

/* ============================================================
 * 今日任务 / 数学 / 语文 的竖屏与窄屏收口（2026-10-17 用户：
 * "这三个界面没给 APK 和手机竖屏优化过，整体优化一下，能一行显示都一行显示，
 *  所有的状态类的都要一行显示"）
 * 三条口径：
 *  ① 状态类整块不折行：全站 `.tag` 已加 white-space:nowrap（见文件开头那条），
 *     这里再把 `.row.spread` 里的状态/计时/按钮设成 flex:none、标题列设成 min-width:0——
 *     flex 子项默认 flex-shrink:1，标题一长就会把右边的状态挤扁，放不下时该让的是标题。
 *  ② 任务卡说明文字收成两行封顶（原来"口算 x 道 · 竖式 x 道 · 单位换算 x 道 · 完成+20，
 *     正确率≥90% 再+…"在 390px 上要吃三行，四张卡就把首屏占满）。
 *  ③ 只在 max-width:600px 与 body.layout-portrait 下生效，桌面与横屏一字不变。
 * ============================================================ */
@media (max-width: 600px) {
  /* ① 标题让位、状态整块 */
  .row.spread > h2, .row.spread > h3 { min-width: 0; }
  .row.spread > .tag, .row.spread > .quiz-timer, .row.spread > .btn,
  .row.spread > .row, .row.spread > .btn-mini { flex: none; }
  /* ② 首页任务卡：图标/说明/状态收密度 */
  .task-item { padding: 10px 12px; gap: 10px; }
  .task-icon { font-size: 22px; flex: none; }
  .task-info { min-width: 0; }
  .task-name { font-size: 14px; }
  .task-desc { font-size: 11.5px; line-height: 1.45; }
  .task-state { flex: none; white-space: nowrap; font-size: 12px; }
  .task-item .btn-mini { flex: none; padding: 6px 10px; font-size: 12px; }
  /* ③ 数学/语文首页：胶囊、统计格、错题行 */
  .mh-chips { gap: 6px; }
  .mchip { font-size: 11.5px; padding: 3px 8px; }
  .mprog-row { gap: 8px; }
  .stars-row { font-size: 20px; gap: 2px; }
  .err-item { padding: 9px 10px; gap: 8px; }
  .err-item .btn-mini { flex: none; }
  .zh-stagebar { padding: 10px 12px; }
  .zh-hero-body { gap: 10px; }
  /* 语文词语行（.zh-word 里那行：词 + 拼音 + 类型 + 🔊 + 已会）：竖屏放不下就自己换行，
   * 但每个成员都不许被压扁（拼音与"已会"按钮被挤成两行很难看） */
  .zh-word > .row { flex-wrap: wrap; gap: 6px; }
  .zh-word > .row > * { flex: none; }
}
/* 竖屏（含 APK，走的是同一个 body 类）：与上面同值，另把说明文字封顶两行 */
body.layout-portrait .task-item { padding: 10px 12px; gap: 10px; }
body.layout-portrait .task-desc {
  font-size: 11.5px; line-height: 1.45;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden;
}
body.layout-portrait .task-state { flex: none; white-space: nowrap; font-size: 12px; }
body.layout-portrait .mchip { font-size: 11.5px; padding: 3px 8px; }
body.layout-portrait .row.spread > h2, body.layout-portrait .row.spread > h3 { min-width: 0; }
body.layout-portrait .row.spread > .tag, body.layout-portrait .row.spread > .quiz-timer,
body.layout-portrait .row.spread > .btn, body.layout-portrait .row.spread > .btn-mini { flex: none; }

/* ============================================================
 * 第十四轮（2026-10-18，用户拿手机竖屏截图逐条提）
 *  ① 计时全站只有一个显示口：专注页顶栏 #focus-bar 里的 #quiz-timer
 *     （index.html 固定结构）。各答题页卡片里那份 span 全部删掉——
 *     QuizTimer._update 只按 id 找元素，所以逻辑零改动，计时值照旧写回卷面对象。
 *  ② 数学/语文"当天结果"那一屏重排：三格统计在窄屏/竖屏收成一行、
 *     「✔ 今日已完成」并进同一行、三个按钮改成"主按钮独占一行 + 另两个各占一半"
 *     （与背单词卡 .wc-acts 同一套排法）。桌面仍是三格 + 标签自己一行，与改前一致。
 *  ③ 标题与右侧标签同高：`.card h3 { margin-bottom }` 在 flex 行里连外边距一起居中，
 *     文字就比右边标签高 6px——窄屏/竖屏把行内标题的下边距归零。
 *  ④ 状态标签竖向减约三分之一（22px → 15px）。
 *  ⑤ 带底色的药丸（点词弹窗的「高频」、宠物名旁的「Lv. 称号」）与文字对齐：
 *     inline-block 默认基线对齐，药丸会往下坠半截，这条是全站修 bug（桌面同样受益）。
 *  ⑥ 顶栏加高：--row-h 30 → 36px（专注页顶栏与英语页状态行仍共用这一个值）。
 * 除 ①⑤ 与"桌面统计标签"外，其余一律关在 max-width:600px 与 body.layout-portrait 里。
 * ============================================================ */
#focus-bar .quiz-timer { margin-left: auto; flex: none; }
/* 桌面：标签占统计组整行（保持改前的位置），窄屏/竖屏才跟着 flex 换行挤进同一行 */
.mh-stats .tag { grid-column: 1 / -1; justify-self: start; align-self: center; margin-top: 2px; }
.wd-tier { display: inline-block; vertical-align: middle; }
.pet-name-tag .tag { vertical-align: middle; }
/* 弹窗标题行：改 flex 居中（inline 对齐无论 baseline 还是 middle 都差 2~3px，且随字号变化；
 * flex 按各自盒子居中，桌面与手机同一口径）。宠物名标签同款规则在 .pet-name-tag 基础样式处。 */
.mt-line { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }

/* 两处同值：下面这份是"窄屏"，再下面那份是"竖屏（含 APK）"，改一处必须改两处 */
@media (max-width: 600px) {
  .row > h2, .row > h3 { margin-bottom: 0; }
  .tag { font-size: 11px; line-height: 1.15; padding: 1px 8px; }
  .wd-tier { font-size: 12px; line-height: 1.15; padding: 1px 10px; }
  .mh-stats { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
  .mh-stats .stat-box { flex: 1 1 0; min-width: 0; padding: 6px 4px; border-radius: 10px; }
  .mh-stats .stat-box .sv { font-size: 17px; }
  .mh-stats .stat-box .sl { font-size: 10.5px; margin-top: 0; }
  .mh-stats .tag { grid-column: auto; margin-top: 0; flex: none; }
  .mh-acts { flex-wrap: wrap; gap: 8px; }
  .mh-acts .btn { flex: 1 1 40%; min-width: 0; padding: 11px 8px; font-size: 14px; white-space: nowrap; }
  .mh-acts .btn-big { flex: 1 1 100%; padding: 12px 16px; font-size: 16px; }
  .mh-body, .zh-hero-body { gap: 10px; margin: 8px 0; }
  .mh-icon, .zh-hero-icon { width: 44px; height: 44px; font-size: 24px; }
  .mh-title, .zh-hero-title { font-size: 17px; }
}
body.layout-portrait .row > h2, body.layout-portrait .row > h3 { margin-bottom: 0; }
body.layout-portrait .tag { font-size: 11px; line-height: 1.15; padding: 1px 8px; }
body.layout-portrait .wd-tier { font-size: 12px; line-height: 1.15; padding: 1px 10px; }
body.layout-portrait .mh-stats { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
body.layout-portrait .mh-stats .stat-box { flex: 1 1 0; min-width: 0; padding: 6px 4px; border-radius: 10px; }
body.layout-portrait .mh-stats .stat-box .sv { font-size: 17px; }
body.layout-portrait .mh-stats .stat-box .sl { font-size: 10.5px; margin-top: 0; }
body.layout-portrait .mh-stats .tag { grid-column: auto; margin-top: 0; flex: none; }
body.layout-portrait .mh-acts { flex-wrap: wrap; gap: 8px; }
body.layout-portrait .mh-acts .btn { flex: 1 1 40%; min-width: 0; padding: 11px 8px; font-size: 14px; white-space: nowrap; }
body.layout-portrait .mh-acts .btn-big { flex: 1 1 100%; padding: 12px 16px; font-size: 16px; }
body.layout-portrait .mh-body, body.layout-portrait .zh-hero-body { gap: 10px; margin: 8px 0; }
body.layout-portrait .mh-icon, body.layout-portrait .zh-hero-icon { width: 44px; height: 44px; font-size: 24px; }
body.layout-portrait .mh-title, body.layout-portrait .zh-hero-title { font-size: 17px; }

/* ============================================================
 * 第十五轮（2026-10-18）：背词卡片上方那行"进度 + 退出"在手机上整行撤掉
 *   进度没丢——写进了顶栏标题（"单词学习 · 3/20"，见 word-flow.js 的 enterFocus）；
 *   "退出"由顶栏「← 返回」替代：会话本来就随 QuizTimer 的 tick 落盘，回来有续做横幅，
 *   与 quitSession 的差别只是不弹那句确认（数据一致）。桌面与横屏一字不变。
 *   同一批：宠物名标签居中（.pet-name-tag 基础样式处）、返回行 36 → 42px（--row-h 那一处）。
 * ============================================================ */
@media (max-width: 600px) {
  .wc-topbar { display: none; }
}
body.layout-portrait .wc-topbar { display: none; }

/* ============================================================
 * 第十七轮（2026-10-18）：商城同一排逐行对齐
 *   用户："需要多少分这行、每一行的所有商品这行需要在一行对齐显示"。
 *   商品名/说明长短不一，同一排三张卡的「⭐价格」和「兑换」就各在各的高度。
 *   做法：卡片改 flex 列，四行各给**固定行高**（名字 1 行超出省略、说明最多 2 行、
 *   价格 1 行、动作行固定高），于是同一排所有卡的这四行都压在同一条水平线上。
 *   动作行即使没有按钮（买不起 / 已拥有以外的空态）也占位，行高才不会塌。
 *   只在窄屏与竖屏生效，桌面与横屏一字不变（两处同值）。
 * ============================================================ */
@media (max-width: 600px) {
  .shop-item { display: flex; flex-direction: column; }
  .shop-item .si-name { height: 18px; line-height: 18px; margin: 5px 0 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .shop-item .si-desc {
    height: 30px; line-height: 15px; margin: 0; overflow: hidden;
    display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
  }
  .shop-item .si-price { height: 18px; line-height: 18px; margin: 4px 0 6px; }
  .shop-item .si-act { height: 32px; display: flex; align-items: center; justify-content: center; }
  .shop-item .si-act .btn { margin: 0; }
}
body.layout-portrait .shop-item { display: flex; flex-direction: column; }
body.layout-portrait .shop-item .si-name { height: 18px; line-height: 18px; margin: 5px 0 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
body.layout-portrait .shop-item .si-desc {
  height: 30px; line-height: 15px; margin: 0; overflow: hidden;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
}
body.layout-portrait .shop-item .si-price { height: 18px; line-height: 18px; margin: 4px 0 6px; }
body.layout-portrait .shop-item .si-act { height: 32px; display: flex; align-items: center; justify-content: center; }
body.layout-portrait .shop-item .si-act .btn { margin: 0; }

