/* ============================================================
 *  Pixel Campus · Stylesheet
 *  2D pixel style / landscape-first / mobile + desktop
 * ============================================================ */

:root{
  --ink:#4a3624;
  --paper:#fff6e6;
  --paper2:#f6e7cc;
  --wood:#c98a52;
  --wood-d:#8a5a34;
  --wood-dd:#6b4322;
  --grass:#8ecb6a;
  --sky1:#bfe6fb;
  --sky2:#eaf7ff;
  --gold:#ffcf5c;
  --gold-d:#e0a52e;
  --red:#ee7f6a;
  --blue:#6aa8e0;
  --green:#7bc47f;
  --purple:#b79ae0;
  --cream:#fffaf0;
  --shadow:0 4px 0 rgba(122,84,52,.28);
  --shadow-soft:0 6px 16px rgba(150,110,70,.22);
  --pix:'Courier New', ui-monospace, monospace;
  --ui:"PingFang SC","Microsoft YaHei","Segoe UI",system-ui,-apple-system,sans-serif;
  --r-lg:18px;
  --r-md:14px;
  --r-sm:10px;
}

*{box-sizing:border-box;margin:0;padding:0;-webkit-tap-highlight-color:transparent}

html,body{
  width:100%;height:100%;
  overflow:hidden;
  background:#1a1410;
  font-family:var(--ui);
  color:var(--ink);
  user-select:none;
  -webkit-user-select:none;
}

image-rendering:pixelated{image-rendering:pixelated}

#app{position:fixed;inset:0;display:flex;align-items:center;justify-content:center;z-index:1}
#scene-map{position:absolute;inset:0;display:none;z-index:1}
#scene-map.active{display:block}
#scene-story{position:absolute;inset:0;display:none;z-index:40}
#scene-story.active{display:block}
.scene{position:absolute;inset:0;display:none}
.scene.active{display:block}

/* 旋转提示整块已废弃（见 index.html 顶部注释）。CSS 一并删掉，
 * 免得留下一堆永远匹配不到元素的死规则。 */
@media (orientation:portrait) and (max-width:900px){
  /* ------------------------------------------------------------------
   * 竖屏不再拦人。
   *
   * 这里原先写的是 `#rotate-hint{display:flex}` —— 竖屏直接把整个游戏盖住，
   * 只留一句"请把手机横过来"。问题是很多手机（尤其国产 ROM 或学校统一
   * 管理的平板）在浏览器里根本转不了屏，学生就卡在这一屏进不去。
   *
   * 实测（390×844）：地图 scale 取 Math.max 后为 1407×844，横向裁掉 72%，
   * 但**纵向一格未裁** —— 因为地图是 40×24 的宽扁比例，竖屏恰好把整条
   * 高度装下。可见区域 11.1×24 格，单块 35.2px（横屏只有 46px 宽但纵向
   * 裁 23%）。也就是说竖屏不是"看不全地图"，而是变成一个更窄但纵向更
   * 完整的视窗，建筑细节反而更清楚。既然能用，就没有理由拦。
   * ------------------------------------------------------------------ */

  /* 竖屏纵向空间比横屏富余得多（844 vs 390），右侧那一列 5 个按钮不必
   * 再贴着右侧上下排。改成贴右缘的 2 列网格，减少遮挡地图的面积。
   *
   * ⚠ 尺寸和定位**不在这里写** —— 这一段排在 .hud-btn 的基础规则
   *   （244 行）之前，同特异性下会被基础规则覆盖，写了等于没写。
   *   实测踩过：这里写过 width:58px，按钮实际一直是 68×68。
   *   真正的竖屏 HUD 尺寸/定位统一放在文件末尾的竖屏区块里。 */

  /* 底部操作提示：上一版它和左下角的「Set up」同步角标重叠（两者都在
   * 左下角）。竖屏下把提示抬到角标上方，并避开左右两列 HUD 按钮。 */
  .map-hint{
    left:calc(74px + env(safe-area-inset-left,0px));
    right:calc(74px + env(safe-area-inset-right,0px));
    bottom:calc(60px + env(safe-area-inset-bottom,0px));
    transform:none;max-width:none;
    white-space:normal;text-align:center;line-height:1.35;
  }
  /* 「Set up」角标在竖屏下也挪到左下角按钮的上方，理由同上。 */
  .sync-pip{top:auto;bottom:calc(86px + env(safe-area-inset-bottom,0px));left:12px}
}

/* ============================================================
 *  Map screen
 * ============================================================ */
#map-canvas{width:100%;height:100%;display:block;image-rendering:pixelated}

/* Quest checklist (top-left) */
#quest-panel{
  position:absolute;top:14px;left:14px;
  width:min(30vw,324px);
  /* 面板高度必须锁在视口内。内容有 11+ 个子任务，在 360px 高的手机横屏上
   * 自然高度能到 699px（视口的 194%）—— 超出部分直接跑出屏幕，下面的任务
   * 看不见也点不到。这里给一个硬上限，超出部分靠 .quest-body 内部滚动。 */
  max-height:calc(100% - 28px);
  display:flex;flex-direction:column;
  background:linear-gradient(180deg,#fffaf0,#fbecd2);
  border:3px solid var(--wood-d);
  border-radius:var(--r-lg);
  box-shadow:var(--shadow-soft),inset 0 0 0 2px #fff9;
  padding:11px 13px 13px;
  z-index:20;
  animation:slideIn .35s ease;
  transition:width .22s ease,padding .22s ease;
}
@keyframes slideIn{from{transform:translateX(-20px);opacity:0}to{transform:none;opacity:1}}

/* —— Quest body: the scrollable region ——
 * 面板用 flex 列布局，头部固定、列表滚动。`min-height:0` 是关键：
 * flex 子项默认 min-height:auto，会拒绝收缩到内容高度以下，从而把
 * 溢出重新推回父级 —— 那样 max-height 就形同虚设。
 * 滚动用 overscroll-behavior 挡住"滑到底继续滚穿地图"的连锁滚动。 */
.quest-body{
  flex:1 1 auto;min-height:0;
  display:flex;flex-direction:column;
  overflow-y:auto;overscroll-behavior:contain;
  -webkit-overflow-scrolling:touch;
  scrollbar-width:thin;scrollbar-color:#d9bd8e transparent;
}
.quest-body::-webkit-scrollbar{width:6px}
.quest-body::-webkit-scrollbar-thumb{background:#d9bd8e;border-radius:3px}
.quest-body::-webkit-scrollbar-track{background:transparent}

/* 滚动提示：还有内容没露出来时，底部压一层渐隐，告诉学生"可以往下滑"。 */
#quest-panel.can-scroll-down::after{
  content:"";position:absolute;left:3px;right:3px;bottom:3px;height:26px;
  border-radius:0 0 var(--r-lg) var(--r-lg);
  background:linear-gradient(180deg,rgba(251,236,210,0),rgba(251,236,210,.96));
  pointer-events:none;
}
/* 同理，向上还有内容时顶部也压一层。 */
#quest-panel.can-scroll-up::before{
  content:"";position:absolute;left:3px;right:3px;top:3px;height:22px;
  border-radius:var(--r-lg) var(--r-lg) 0 0;
  background:linear-gradient(0deg,rgba(255,250,240,0),rgba(255,250,240,.96));
  pointer-events:none;z-index:2;
}

/* —— Collapsed: keep only the "next" sub-goal strip —— */
#quest-panel.collapsed{width:auto;max-width:min(46vw,300px);padding:8px 10px}
#quest-panel.collapsed .quest-body{display:none}
.quest-mini{display:none}
#quest-panel.collapsed .quest-mini{
  display:flex;align-items:center;gap:8px;
  width:100%;border:0;background:transparent;padding:2px 3px;
  font-family:inherit;cursor:pointer;text-align:left;
}
.qm-tag{
  flex-shrink:0;font-size:10px;font-weight:800;color:#fff;letter-spacing:.5px;
  background:linear-gradient(180deg,#e0a55c,#c98a52);
  border-radius:6px;padding:2px 7px;box-shadow:0 2px 0 rgba(122,84,52,.3);
}
.qm-txt{
  flex:1;min-width:0;font-size:12.5px;font-weight:700;color:#4a3320;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.qm-txt.all-done{color:#4fae66}
.qm-caret{flex-shrink:0;font-size:12px;color:#b08a5c;transition:transform .2s}
.quest-mini:hover .qm-caret{transform:translateY(1px);color:#c98a52}

/* Expand/collapse button (shown when expanded) */
.quest-collapse{
  position:absolute;top:9px;right:11px;
  width:24px;height:24px;border:0;border-radius:8px;cursor:pointer;
  background:#f3ddb8;color:#a9773f;font-size:13px;line-height:1;
  display:flex;align-items:center;justify-content:center;
  box-shadow:0 2px 0 rgba(122,84,52,.22);transition:background .15s,transform .15s;
}
.quest-collapse:hover{background:#ecd0a0;transform:translateY(-1px)}

.quest-head{border-bottom:2px dashed #e2c79a;padding-bottom:7px;margin-bottom:8px;position:relative}
.q-unit{display:inline-block;font-size:11px;font-weight:700;color:#fff;background:linear-gradient(180deg,#e0a55c,#c98a52);border-radius:6px;padding:2px 9px;letter-spacing:.5px;box-shadow:0 2px 0 rgba(122,84,52,.3)}
.quest-head h3{font-size:14px;color:var(--wood-dd);margin-top:6px;line-height:1.25}
.quest-head p{font-size:12px;color:#b08a5c;margin-top:2px}
.quest-list{list-style:none;display:flex;flex-direction:column;gap:4px}
.quest-list li{position:relative}

/* —— Level 1: task row —— */
.quest-list li.task{
  display:flex;align-items:center;gap:8px;
  font-size:13px;padding:6px 9px;border-radius:12px;
  background:linear-gradient(180deg,#fff8e8,#fbeacb);
  border:2px solid #e6c893;cursor:default;
  margin-top:3px;
}
.quest-list li.task .q-ico{font-size:15px;width:18px;height:18px;flex-shrink:0;display:flex;align-items:center;justify-content:center;filter:drop-shadow(0 1px 0 #0002)}
.quest-list li.task .q-ico svg{width:18px;height:18px;image-rendering:pixelated}
.quest-list li.task .q-txt{flex:1;line-height:1.2}
.quest-list li.task .q-txt b{font-size:12.5px;display:block;color:#4a3320;font-weight:800;letter-spacing:.2px}
.quest-list li.task .q-txt small{font-size:10px;color:#b0916a}
.quest-list li.task .q-check{font-size:13px;color:#4fae66;opacity:0}
.quest-list li.task.done{opacity:.7}
.quest-list li.task.done .q-check{opacity:1}
.quest-list li.task.done .q-txt b{text-decoration:line-through;text-decoration-color:#8bbf8b;color:#6f7f6f}

/* —— Level 2: sub-goal row (indented) —— */
.quest-list li.goal{
  display:flex;align-items:center;gap:7px;
  font-size:12px;padding:5px 8px 5px 10px;
  margin-left:16px;border-radius:10px;
  background:#fffdf6;border:2px solid #ecd6b0;cursor:pointer;
  transition:transform .12s,background .2s,box-shadow .2s;
  position:relative;
}
.quest-list li.goal::before{
  content:"";position:absolute;left:-9px;top:50%;width:7px;height:2px;
  background:#e2c79a;border-radius:2px;
}
.quest-list li.goal:hover{transform:translateX(3px);background:#fff;box-shadow:0 3px 8px rgba(150,110,70,.18)}
.quest-list li.goal .g-dot{width:7px;height:7px;border-radius:50%;background:#e0a55c;flex-shrink:0;box-shadow:0 1px 0 #0002}
.quest-list li.goal .g-txt{flex:1;line-height:1.18}
.quest-list li.goal .g-txt b{font-size:11.5px;display:block;color:#5a4028;font-weight:600}
.quest-list li.goal .g-txt small{font-size:9.5px;color:#b8a082}
.quest-list li.goal .q-check{font-size:12px;color:#4fae66;opacity:0}
.quest-list li.goal.done{opacity:.6;background:#eef7ee;border-color:#bfe0bf}
.quest-list li.goal.done .g-dot{background:#7dc86a}
.quest-list li.goal.done .q-check{opacity:1}
.quest-list li.goal.done .g-txt b{text-decoration:line-through;text-decoration-color:#8bbf8b}
/* Highlight current sub-goal (pulse) */
.quest-list li.goal.current{
  border-color:var(--gold);
  background:#fff7dd;
  animation:pulseGlow 1.4s ease-in-out infinite;
}
.quest-list li.goal.current .g-dot{background:var(--red)}
@keyframes pulseGlow{0%,100%{box-shadow:0 0 0 0 rgba(255,207,92,.7)}50%{box-shadow:0 0 0 7px rgba(255,207,92,0)}}
.quest-list li.locked{opacity:.5;cursor:not-allowed}

/* HUD buttons */
.hud-btn{
  position:absolute;z-index:20;cursor:pointer;
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;
  width:68px;height:68px;border-radius:var(--r-lg);
  background:linear-gradient(180deg,#fff8e8,#f2d8a8);
  border:3px solid var(--wood-d);
  box-shadow:var(--shadow-soft);transition:transform .12s,box-shadow .12s;
}
.hud-btn:active{transform:translateY(3px);box-shadow:0 2px 6px rgba(150,110,70,.2)}
.hud-btn .hud-ico{font-size:24px;line-height:1;width:28px;height:28px;display:flex;align-items:center;justify-content:center}
.hud-btn .hud-ico svg{width:28px;height:28px;image-rendering:pixelated}
.hud-btn .hud-ico .hud-ico-img{width:28px;height:28px;object-fit:contain;image-rendering:pixelated}

/* 按钮/标签里的内联矢量图标（替代 emoji，避免缺字形渲染成方框）。
 * 尺寸跟随文字大小，垂直居中，不参与换行判断 —— 否则图标会和文案
 * 被拆到两行。 */
.btn-ico{
  width:1.05em;height:1.05em;display:inline-flex;
  align-items:center;justify-content:center;
  vertical-align:-0.18em;margin-right:.35em;flex:0 0 auto;
}
.btn-ico svg{width:100%;height:100%;display:block;image-rendering:pixelated}
.btn-ico:last-child{margin-right:0}
/* 只有图标、没有文字的按钮（如 🔊 单按钮）不需要右边距 */
.btn-ico:only-child,
.hud-btn .hud-ico{margin-right:0}
.hud-btn .hud-label{font-size:11px;color:var(--wood-dd);font-weight:700}
.course-btn{top:14px;right:14px}
.settings-btn{top:92px;right:14px}
.wordbook-btn{bottom:14px;right:14px}
.practice-btn{bottom:92px;right:14px}
.bgm-btn{bottom:14px;left:14px}
.bgm-btn.muted{opacity:.62}
.bgm-btn.muted .hud-label{color:#9a8f80}

.map-hint{
  position:absolute;left:50%;bottom:10px;transform:translateX(-50%);
  background:rgba(43,29,18,.72);color:#f7ecd7;font-size:11.5px;
  padding:5px 14px;border-radius:20px;z-index:15;white-space:nowrap;
}
.map-hint .k{color:var(--gold);font-weight:700}
/* 触摸设备上不该显示 WASD/按键提示 —— 手机没有键盘。
 * 默认显示桌面文案；粗指针（触摸）设备改显示拖动/点击文案。
 * 用 @media (hover:none) and (pointer:coarse) 而不是靠 UA 判断，
 * 这样在带触摸屏的笔电上仍走桌面文案（那里有键盘）。 */
.map-hint .hint-touch{display:none}
@media (hover:none) and (pointer:coarse){
  .map-hint .hint-desktop{display:none}
  .map-hint .hint-touch{display:inline}
}

.enter-tip{
  position:absolute;z-index:25;pointer-events:none;
  background:#fff8e6;border:2px solid var(--wood-d);border-radius:8px;
  padding:5px 10px;font-size:12.5px;font-weight:600;color:var(--wood-d);
  box-shadow:var(--shadow);transform:translate(-50%,-100%);white-space:nowrap;
  animation:bob .9s ease-in-out infinite;
}
.enter-tip::after{content:"";position:absolute;left:50%;bottom:-8px;transform:translateX(-50%);
  border:6px solid transparent;border-top-color:var(--wood-d)}
@keyframes bob{0%,100%{margin-top:0}50%{margin-top:-5px}}
.hidden{display:none !important}

/* ============================================================
 *  Generic overlay dialog
 * ============================================================ */
.overlay{
  position:fixed;inset:0;z-index:50;display:none;
  align-items:center;justify-content:center;
  background:rgba(20,14,9,.66);backdrop-filter:blur(3px);
  padding:16px;
}
.overlay.show{display:flex;animation:fadeIn .2s ease}
@keyframes fadeIn{from{opacity:0}to{opacity:1}}
.overlay-card{
  background:linear-gradient(180deg,#fffaf0,#f6e5c6);
  border:3px solid var(--wood-d);border-radius:22px;
  box-shadow:0 14px 34px rgba(90,60,30,.34),inset 0 0 0 3px #fff9;
  width:min(94vw,860px);
  max-height:92vh;display:flex;flex-direction:column;
  animation:pop .28s cubic-bezier(.2,1.4,.4,1);
}
@keyframes pop{from{transform:scale(.9);opacity:0}to{transform:scale(1);opacity:1}}
.overlay-head{
  display:flex;align-items:center;gap:14px;
  padding:13px 18px;border-bottom:3px solid #e2c79a;
}
.overlay-head h2{font-size:17px;color:var(--wood-dd);flex:1;display:flex;align-items:center;gap:9px}
/* 面板标题里的图标：加载时由 JS 把 emoji 换成内联 SVG。
 * 给一个固定方框并让 svg 填满，避免矢量图标把行高撑歪。 */
.overlay-head h2 .h2-ico{
  width:20px;height:20px;flex:0 0 20px;display:inline-flex;
  align-items:center;justify-content:center;
}
.overlay-head h2 .h2-ico svg{width:100%;height:100%;display:block;image-rendering:pixelated}
.overlay-head h2 .wb-head-ico{width:26px;height:26px;object-fit:contain;image-rendering:pixelated}
.close-x{
  width:36px;height:36px;border-radius:12px;border:2px solid var(--wood-d);
  background:#f4dcaf;color:var(--wood-dd);font-size:16px;font-weight:700;cursor:pointer;
  transition:background .15s,transform .15s;
}
.close-x:hover{background:var(--red);color:#fff;border-color:#c05a48;transform:rotate(90deg)}

/* Course select */
.course-body{display:flex;gap:16px;padding:16px;overflow:auto;flex-wrap:wrap}
.unit-col{width:200px;display:flex;flex-direction:column;gap:10px;flex-shrink:0}
.unit-item{
  background:#fffdf6;border:3px solid #e0cda6;border-radius:10px;padding:10px 12px;
  display:flex;flex-direction:column;gap:2px;position:relative;cursor:pointer;transition:.15s;
}
.unit-item.active{border-color:var(--gold);background:#fff6d8;box-shadow:0 0 0 3px #f6c35044}
.unit-item.disabled{opacity:.55;cursor:not-allowed}
.u-no{font-size:11px;font-weight:700;color:var(--wood)}
.u-name{font-size:14px;font-weight:700;color:#3b2a1a}
.u-cn{font-size:11px;color:#9b8360}
.unit-item .lock{position:absolute;right:8px;top:8px;font-size:10px;color:#a98c62}
.passage-col{flex:1;min-width:280px}
.passage-col h4{font-size:13px;color:#8a6a44;margin-bottom:10px}
.passage-item{
  background:#fffdf6;border:3px solid #e0cda6;border-radius:10px;
  padding:12px 14px;margin-bottom:10px;cursor:pointer;transition:.15s;
}
.passage-item:hover{border-color:var(--gold);transform:translateY(-2px);box-shadow:var(--shadow)}
.p-tag{display:inline-block;font-size:11px;font-weight:700;color:#fff;background:var(--blue);border-radius:3px;padding:1px 8px}
.passage-item:nth-child(3) .p-tag{background:var(--purple)}
.p-title{font-size:15px;font-weight:700;color:#3b2a1a;margin-top:6px}
.p-cn{font-size:12.5px;color:#7a5f3c}
.p-meta{font-size:11px;color:#a98c62;margin-top:6px}

/* Chapter select (one building, many stories) */
.chapter-card{width:min(94vw,520px)}
.chapter-sub{font-size:12px;color:#9b8360;padding:10px 18px 0}
.chapter-list{display:flex;flex-direction:column;gap:9px;padding:12px 18px 18px;overflow:auto}
.chapter-item{
  display:flex;align-items:center;gap:11px;
  background:#fffdf6;border:3px solid #e0cda6;border-radius:12px;
  padding:10px 13px;cursor:pointer;transition:.15s;
}
.chapter-item:hover{border-color:var(--gold);transform:translateY(-2px);box-shadow:var(--shadow)}
.chapter-item .ci-no{
  flex-shrink:0;width:34px;height:34px;border-radius:9px;
  background:linear-gradient(180deg,#e0a55c,#c98a52);color:#fff;
  font-size:12px;font-weight:800;display:flex;align-items:center;justify-content:center;
  box-shadow:0 2px 0 rgba(122,84,52,.3);
}
.chapter-item .ci-txt{flex:1;line-height:1.25}
.chapter-item .ci-txt b{font-size:13.5px;display:block;color:#3b2a1a}
.chapter-item .ci-txt small{font-size:11px;color:#a98c62}
.chapter-item .ci-state{font-size:11.5px;font-weight:700;color:var(--wood);white-space:nowrap}
.chapter-item.done{opacity:.72;background:#eef7ee;border-color:#bfe0bf}
.chapter-item.done .ci-no{background:linear-gradient(180deg,#8fd07a,#5fae4a)}
.chapter-item.done .ci-state{color:#4e8a3a}

/* ---- Settings ---- */
.settings-card{width:min(96vw,680px)}
.settings-body{padding:16px 18px 20px;overflow:auto}
.set-group{display:flex;flex-direction:column;gap:6px}
.set-title{font-size:14px;color:var(--wood-dd);font-weight:800}
.set-hint{font-size:11.5px;color:#a98c62;margin-bottom:6px}
.avatar-grid{
  display:grid;grid-template-columns:repeat(5,1fr);gap:10px;
}
.avatar-opt{
  background:#fffdf6;border:3px solid #e0cda6;border-radius:14px;
  padding:8px 6px;display:flex;flex-direction:column;align-items:center;gap:7px;
  cursor:pointer;transition:transform .14s,border-color .14s,box-shadow .14s;
}
.avatar-opt:hover{transform:translateY(-2px);border-color:var(--gold);box-shadow:var(--shadow)}
.avatar-opt.active{border-color:var(--gold);background:#fff6d8;box-shadow:0 0 0 3px #f6c35044}
.avatar-thumb{
  width:100%;height:96px;display:flex;align-items:flex-end;justify-content:center;
  background:radial-gradient(ellipse at 50% 78%,#f7ecd7 42%,#efe1c4 100%);
  border-radius:10px;overflow:hidden;position:relative;
}
.avatar-thumb img{
  height:88px;width:auto;image-rendering:pixelated;
  filter:drop-shadow(0 3px 4px rgba(120,90,50,.28));
}
.avatar-name{font-size:11px;font-weight:800;color:#3b2a1a;text-align:center;line-height:1.15}
.avatar-opt .av-check{
  font-size:10px;font-weight:800;color:#4e8a3a;height:13px;line-height:13px;
}
.avatar-opt.active .av-check::before{content:"✓ Selected"}
.avatar-opt:not(.active) .av-check::before{content:"Tap to use"}

/* ============================================================
 *  Story screen
 * ============================================================ */
#scene-story{overflow:hidden}
#story-canvas{position:absolute;inset:0;width:100%;height:100%;image-rendering:pixelated}
.story-vignette{
  position:absolute;inset:0;pointer-events:none;
  background:radial-gradient(ellipse at 50% 40%,transparent 40%,rgba(0,0,0,.45) 100%);
}
.story-topbar{
  position:absolute;top:0;left:0;right:0;z-index:12;
  display:flex;align-items:center;gap:10px;padding:10px 16px;
  background:linear-gradient(180deg,rgba(0,0,0,.45),transparent);
}
.st-task{font-size:11px;font-weight:700;color:#6b4322;background:var(--gold);border-radius:8px;padding:3px 11px;box-shadow:0 2px 6px rgba(0,0,0,.25)}
.st-title{font-size:15px;font-weight:700;color:#fff;text-shadow:0 2px 6px rgba(0,0,0,.6)}
.story-back{
  position:absolute;top:48px;right:16px;z-index:14;
  background:rgba(80,50,28,.8);color:#fff4dd;border:2px solid rgba(255,240,220,.5);
  border-radius:12px;padding:7px 14px;font-size:12.5px;cursor:pointer;transition:.15s;
}
.story-back:hover{background:var(--red);border-color:#fff;transform:translateY(-1px)}

/* Portrait — stands above the dialogue box, head and upper body visible */
.portrait-layer{position:absolute;inset:0;z-index:10;pointer-events:none}
.portrait{
  /* --box-h is the dialogue box's measured height, published by story.js.
   * The box top edge sits (2.5% + box-h) above the stage bottom, so parking
   * the portrait's bottom on that line makes it sit flush on the panel. */
  position:absolute;bottom:calc(2.5% + var(--box-h, 132px));
  transition:transform .3s,opacity .3s,filter .3s;
  image-rendering:pixelated;image-rendering:crisp-edges;
  filter:drop-shadow(0 8px 16px rgba(60,40,20,.42));
}
.portrait.left{left:5%;transform:translateX(0);transform-origin:bottom center}
.portrait.right{right:5%;transform:translateX(0);transform-origin:bottom center}
.portrait.dim{filter:brightness(.6) saturate(.85) drop-shadow(0 8px 16px rgba(60,40,20,.36));transform:scale(.93)}
.portrait.enter{animation:pEnter .32s ease}
/* image-asset portrait (real pixel art) */
.portrait.img-portrait{image-rendering:pixelated;image-rendering:crisp-edges;object-fit:contain}
/* Bust portraits (head + shoulders) shown during dialogue.
 *
 * Anchored by its BOTTOM edge onto the dialogue box's TOP edge, so the figure
 * always sits flush on the panel — at every screen size, and for both a short
 * one-line line and a long wrapped one.
 *
 * The box is pinned at bottom:2.5% and is content-sized, so its top edge is
 * (2.5% + --box-h) above the stage bottom. story.js measures the box and
 * publishes --box-h, which is what the base .portrait rule already consumes.
 * Nothing is dead-reckoned, so nothing can drift.
 *
 * The portrait layer (z-index 10) sits UNDER the dialogue box (z-index 15),
 * so the bust's hard torso cut is simply hidden behind the panel. */
.portrait.img-portrait.bust{
  /* Bottom-anchored onto the panel's top edge.
   *
   * The HEIGHT is expressed here in stage units instead of being baked into
   * an inline `style.height` at render time. A pixel height set by JS goes
   * stale the moment the stage is resized (window drag, browser zoom, a
   * device rotation): the old pixel value is re-used, so at a smaller stage
   * the figure fills the whole screen and at a larger one it shrinks away.
   * A percentage of the stage can never go stale — it re-resolves on every
   * layout, so the figure keeps exactly the same relative size it had on the
   * first frame, which is what the player expects.
   *
   * 52% of the stage is the same budget bustMaxHeight() used; the figure's
   * torso is hidden behind the panel (z-index 10 < 15), so the visible part
   * always sits flush on the box.
   *
   * --box-h still supplies the anchor offset, and is also subtracted from the
   * height so a very tall wrapped line can never push the head off the top. */
  top:auto;
  bottom:calc(2.5% + var(--box-h, 132px));
  /* Percentage resolves against #scene-story, which is absolute+inset:0 and
   * therefore has a definite height — so this tracks every resize. */
  height:52%;
  max-height:none;
  width:auto;
  max-width:46%;
  object-fit:contain;
  left:3.13%;
  filter:drop-shadow(0 10px 20px rgba(60,40,20,.40));
}
.portrait.right.img-portrait.bust{left:auto;right:3.13%}
@keyframes pEnter{from{opacity:0;transform:translateY(24px)}to{opacity:1;transform:none}}

/* Dialogue box */
.dialogue-box{
  position:absolute;left:3%;right:3%;bottom:2.5%;z-index:15;
  background:linear-gradient(180deg,rgba(255,250,240,.98),rgba(246,229,198,.98));
  border:3px solid var(--wood-d);border-radius:20px;
  padding:17px 22px 21px;min-height:132px;
  box-shadow:0 12px 28px rgba(80,50,20,.34),inset 0 0 0 3px #fff9;
  backdrop-filter:blur(4px);
}
.speaker-name{
  position:absolute;top:-18px;left:22px;
  background:linear-gradient(180deg,#d8985c,#bd7d46);color:#fff4dd;font-size:13.5px;font-weight:700;
  padding:4px 18px;border-radius:12px;border:2px solid var(--wood-dd);
  box-shadow:0 3px 8px rgba(80,50,20,.32);
}
.dialogue-en{font-size:15.5px;line-height:1.5;color:#4a3624;font-weight:600;margin-top:6px;min-height:24px}
.dialogue-cn{font-size:13px;line-height:1.45;color:#9b7a52;margin-top:7px;min-height:20px}
.dialogue-next{
  position:absolute;right:18px;bottom:9px;color:var(--wood-d);font-size:16px;
  animation:blink 1s steps(2) infinite;
}
@keyframes blink{0%,50%{opacity:1}51%,100%{opacity:.15}}
.voice-btn{
  position:absolute;right:16px;top:12px;width:36px;height:36px;border-radius:12px;
  border:2px solid var(--wood-d);background:#f4dcaf;cursor:pointer;font-size:15px;
  transition:background .15s,transform .15s;
}
.voice-btn:hover{background:var(--gold);transform:scale(1.06)}

/* Choices */
.choice-box{
  position:absolute;left:50%;top:50%;transform:translate(-50%,-42%);
  z-index:20;display:flex;flex-direction:column;gap:12px;width:min(74vw,560px);
}
.choice-prompt{
  text-align:center;color:#fff;font-size:15px;font-weight:700;
  text-shadow:0 2px 6px #000;background:rgba(43,29,18,.72);
  padding:8px 14px;border-radius:8px;margin-bottom:4px;
}
.choice-prompt small{display:block;font-size:12px;opacity:.85;font-weight:400}
.choice-btn{
  background:linear-gradient(180deg,#fffaf0,#f5e2ba);
  border:3px solid var(--wood-d);border-radius:16px;
  padding:13px 18px;cursor:pointer;text-align:left;transition:.14s;
  box-shadow:var(--shadow);
}
.choice-btn:hover{transform:translateY(-2px);border-color:var(--gold);background:#fff8e2;box-shadow:0 8px 18px rgba(150,110,70,.28)}
.choice-btn:active{transform:translateY(2px);box-shadow:0 1px 0 rgba(0,0,0,.2)}
.choice-btn .c-en{font-size:14.5px;font-weight:700;color:#4a3624}
.choice-btn .c-cn{font-size:12.5px;color:#9b7a52;margin-top:4px}
.choice-btn .c-idx{display:inline-block;width:22px;height:22px;line-height:22px;text-align:center;
  background:linear-gradient(180deg,#d8985c,#bd7d46);color:#fff4dd;border-radius:7px;font-size:12px;margin-right:9px}

/* Ending */
.ending-box{
  position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);
  z-index:25;width:min(80vw,540px);text-align:center;
  background:linear-gradient(180deg,#fffaf0,#f8ead0);
  border:3px solid var(--wood-d);border-radius:22px;padding:28px 28px 24px;
  box-shadow:0 16px 36px rgba(90,60,30,.36),inset 0 0 0 3px #fff9;
  animation:pop .3s cubic-bezier(.2,1.4,.4,1);
}
.ending-badge{font-size:44px;color:var(--gold);text-shadow:0 3px 0 var(--gold-d);animation:pop .5s}
.ending-box h2{font-size:20px;color:var(--wood-dd);margin:6px 0 12px}
.ending-box p{font-size:14px;line-height:1.55;color:#7a5f3c;margin-bottom:6px}
#ending-en{font-weight:600;color:#4a3624}
.btn-primary{
  margin-top:14px;background:linear-gradient(180deg,#ffdd8a,#efb03a);
  border:3px solid var(--wood-dd);border-radius:14px;padding:11px 26px;
  font-size:15px;font-weight:700;color:#6b4322;cursor:pointer;box-shadow:var(--shadow);
  transition:transform .12s,box-shadow .12s;
}
.btn-primary:active{transform:translateY(3px);box-shadow:0 2px 4px rgba(0,0,0,.2)}

/* ============================================================
 *  Embedded mini-game overlay (Schulte Grid)
 *  The return button deliberately re-creates the game's own
 *  neo-brutalist art so it reads as part of the game rather than
 *  as Pixel Campus UI bolted on top.
 * ============================================================ */
.game-box{
  position:absolute;inset:0;z-index:40;
  display:flex;flex-direction:column;
  background:#e9e9ef;
  padding:clamp(8px,1.8vh,18px) clamp(10px,2vw,24px);
  gap:clamp(8px,1.6vh,14px);
  animation:pop .26s cubic-bezier(.2,1.3,.4,1);
  /* Local neo-brutalist palette, taken straight from the game. */
  --g-paper:#ffffff;
  --g-ink:#1c1c28;
  --g-dim:#8a8a99;
  --g-yellow:#ffd23f;
  --g-red:#ff4d5e;
}
.gb-stage{
  flex:1 1 auto;position:relative;min-height:0;
  display:flex;align-items:center;justify-content:center;
  background:var(--g-paper);
  border:clamp(3px,.6vh,5px) solid var(--g-ink);
  box-shadow:clamp(4px,.8vh,7px) clamp(4px,.8vh,7px) 0 var(--g-ink);
  overflow:hidden;
}
/* `auto` mode: the stage hugs a declared aspect ratio (set from the game node's
 * `aspect`) instead of stretching to fill the screen. A wide side-scroller such
 * as the hurdle runner would otherwise sit in a tall letterbox with acres of
 * empty paper above and below it. `width:100%` plus `max-height:100%` lets the
 * browser pick whichever of the two constraints binds first, so the box stays
 * inside the overlay at any window shape. */
.gb-stage.auto{
  flex:0 1 auto;
  width:100%;
  aspect-ratio:var(--gb-ar, 2);
  max-height:100%;
  margin:auto;
}
.gb-frame{width:100%;height:100%;border:0;display:block;background:transparent}

.gb-bar{flex:0 0 auto;display:flex;align-items:center;gap:clamp(10px,2vw,22px)}
.gb-hint{
  flex:1 1 auto;
  font-family:'Helvetica Neue',Helvetica,Arial,'PingFang SC','Microsoft YaHei',sans-serif;
  font-size:clamp(11px,1.6vh,15px);
  letter-spacing:.4px;color:var(--g-ink);
}
.gb-btn{
  font-family:inherit;
  font-size:clamp(12px,1.8vh,17px);
  font-weight:700;letter-spacing:1px;
  padding:clamp(9px,1.6vh,15px) clamp(20px,3vw,38px);
  color:var(--g-ink);cursor:pointer;
  background:var(--g-yellow);
  border:clamp(3px,.55vh,4px) solid var(--g-ink);
  box-shadow:clamp(3px,.6vh,5px) clamp(3px,.6vh,5px) 0 var(--g-ink);
  transition:transform .05s, box-shadow .05s, background .1s, opacity .15s;
}
.gb-btn:hover:not(:disabled){background:#ffdd6b}
.gb-btn:active:not(:disabled){transform:translate(4px,4px);box-shadow:0 0 0 var(--g-ink)}
.gb-btn:disabled{opacity:.42;cursor:default}
.gb-btn.ready{animation:gbPulse .9s ease-in-out infinite alternate}
@keyframes gbPulse{
  from{box-shadow:clamp(3px,.6vh,5px) clamp(3px,.6vh,5px) 0 var(--g-ink)}
  to  {box-shadow:clamp(3px,.6vh,5px) clamp(3px,.6vh,5px) 0 var(--g-ink),0 0 0 6px rgba(255,77,94,.42)}
}

/* Win banner — a slim strip above the bar, so it never covers the grid the
 * game itself is still drawing its own CLEAR overlay onto. */
.gb-win{
  position:absolute;left:0;right:0;bottom:0;z-index:3;
  display:flex;align-items:center;justify-content:center;gap:clamp(8px,1.6vw,18px);
  text-align:center;pointer-events:none;
  background:rgba(255,255,255,.96);
  border-top:clamp(3px,.6vh,5px) solid var(--g-ink);
  padding:clamp(9px,1.7vh,16px) clamp(12px,2vw,24px);
  animation:gbSlideUp .32s cubic-bezier(.2,1.3,.4,1);
}
@keyframes gbSlideUp{ from{transform:translateY(100%);opacity:0} to{transform:none;opacity:1} }
.gb-win-title{
  font-family:var(--pix);font-weight:700;
  font-size:clamp(13px,2.1vh,20px);color:var(--g-red);
  letter-spacing:2px;
}
.gb-win-sub{
  font-size:clamp(11px,1.7vh,16px);color:var(--g-ink);
  letter-spacing:.5px;
}

/* ============================================================
 *  Phone (in-story interaction: budget sheet, etc.)
 * ============================================================ */
.phone-box{
  position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);
  z-index:26;display:flex;align-items:center;justify-content:center;
}
.phone-frame{
  position:relative;
  width:min(88vw,420px);
  background:linear-gradient(160deg,#3b2f26,#241c16);
  border-radius:34px;
  padding:14px 12px 16px;
  box-shadow:0 22px 46px rgba(40,26,14,.5),inset 0 0 0 2px #5a4636;
  animation:pop .3s cubic-bezier(.2,1.4,.4,1);
}
.phone-notch{
  width:86px;height:9px;border-radius:6px;background:#15100c;
  margin:0 auto 8px;box-shadow:inset 0 1px 0 #000;
}
.phone-screen{
  background:linear-gradient(180deg,#fff8ea,#f4e3c6);
  border-radius:24px;padding:12px 14px 14px;
  display:flex;flex-direction:column;gap:9px;
  max-height:min(78vh,600px);overflow:auto;
}
.phone-status{
  display:flex;justify-content:space-between;align-items:center;
  font-size:11px;font-weight:700;color:#9b8058;letter-spacing:.5px;
}
.phone-status .ps-icons{opacity:.7;font-size:10px}
.phone-screen h3{text-align:center;font-size:17px;color:var(--wood-dd);margin:1px 0 0}
#phone-note{text-align:center;font-size:11.5px;color:#a98c62;line-height:1.4;margin-bottom:2px}

.phone-rows{display:flex;flex-direction:column;gap:10px}
.phone-row{background:#fffdf6;border:2px solid #e6d2ac;border-radius:12px;padding:8px 11px 9px}
.phone-row .pr-head{display:flex;justify-content:space-between;align-items:baseline;margin-bottom:5px}
.phone-row .pr-name b{font-size:13px;color:#4a3624}
.phone-row .pr-name small{font-size:10px;color:#b0916a;margin-left:5px}
.phone-row .pr-amt{font-size:13px;font-weight:800;color:var(--wood-d);font-variant-numeric:tabular-nums}
/* Slider */
.pr-range{
  -webkit-appearance:none;appearance:none;width:100%;height:22px;background:transparent;cursor:pointer;
}
.pr-range::-webkit-slider-runnable-track{
  height:9px;border-radius:6px;
  background:linear-gradient(90deg,#e9b877,#f2d6a4);border:1px solid #d9bd8b;
}
.pr-range::-moz-range-track{height:9px;border-radius:6px;background:#e9c894;border:1px solid #d9bd8b}
.pr-range::-webkit-slider-thumb{
  -webkit-appearance:none;appearance:none;width:22px;height:22px;margin-top:-7px;border-radius:50%;
  background:radial-gradient(circle at 35% 30%,#fff6e6,#e5a95c 70%);
  border:2px solid var(--wood-d);box-shadow:0 2px 4px rgba(90,60,30,.35);transition:transform .1s;
}
.pr-range::-moz-range-thumb{
  width:20px;height:20px;border-radius:50%;
  background:radial-gradient(circle at 35% 30%,#fff6e6,#e5a95c 70%);
  border:2px solid var(--wood-d);box-shadow:0 2px 4px rgba(90,60,30,.35);
}
.pr-range:active::-webkit-slider-thumb{transform:scale(1.12)}
.pr-range.limit::-webkit-slider-thumb{animation:shake .4s;background:radial-gradient(circle at 35% 30%,#ffdede,#e2574c 70%)}
.pr-range.limit::-moz-range-thumb{background:#e2574c}
@keyframes shake{0%,100%{transform:translateX(0)}25%{transform:translateX(-4px)}75%{transform:translateX(4px)}}

.phone-total{
  background:#fdf3de;border:2px dashed #e2c79a;border-radius:12px;
  padding:8px 11px;margin-top:1px;
}
.phone-total .pt-line{display:flex;justify-content:space-between;align-items:baseline}
.phone-total .pt-line span{font-size:12.5px;color:#8a6a44;font-weight:700}
.phone-total #phone-total-val{font-size:19px;font-weight:900;color:var(--wood-dd);font-variant-numeric:tabular-nums}
.phone-total #phone-total-val.warn{color:var(--red);animation:shake .4s}
.phone-total .pt-save{font-size:11px;color:#9b8a68;margin-top:2px}
.phone-total .pt-save b{color:#5f9a4a}
.phone-total.over .pt-save b{color:var(--red)}
.phone-confirm{width:100%;margin-top:2px}

/* ============================================================
 *  Word Book
 * ============================================================ */
/* flex:0 0 auto + min-height:0 —— 让卡片按内容取高、但绝不自适应拉伸。
 * .overlay 是 align-items:center 的 flex 容器，不写 flex 的话卡片会按
 * flex-basis:auto 撑到内容高度，min(92vh,600px) 直接失效。 */
.wordbook-card{width:min(94vw,900px);height:min(92vh,600px);min-height:0;flex:0 0 auto;overflow:hidden}
.wb-tabs{display:flex;gap:6px}
.wb-tab{
  border:2px solid var(--wood-d);background:#f4dcaf;color:var(--wood-dd);
  border-radius:11px;padding:6px 15px;font-size:12.5px;font-weight:700;cursor:pointer;
  transition:.15s;
}
.wb-tab.active{background:linear-gradient(180deg,#d8985c,#bd7d46);color:#fff4dd;box-shadow:0 3px 8px rgba(150,110,70,.3)}
.wb-progress{display:flex;align-items:center;gap:10px;padding:10px 16px 2px}
.wb-bar{flex:1;height:13px;background:#ecd6b0;border:2px solid var(--wood-d);border-radius:10px;overflow:hidden}
.wb-bar-fill{height:100%;width:0;background:linear-gradient(90deg,#9bd97f,#6bc471);transition:width .3s}
#wb-progress-text{font-size:12px;font-weight:700;color:var(--wood-dd)}
/* 下面三条 min-height:0 / min-width:0 是这套面板不溢出的全部理由，勿删。
 * flex 子项的 min-height 默认是 auto —— 也就是"不许比内容矮"。于是
 * .wb-list 里 894px 的 20 个词条会把 .wb-body 顶穿，overflow:hidden 只是把
 * 顶着的那部分剪掉而不是让 .wb-list 自己出现滚动条。实测横屏机型上因此有
 * 64~65 个元素渲染到卡片外（词条跑到卡片下方、右侧释义卡被拦腰截断）。 */
.wb-body{display:flex;gap:14px;padding:12px 16px;flex:1;overflow:hidden;min-height:0}
.wb-main{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:14px;min-width:0;min-height:0}

/* Flip card */
.flashcard{position:relative;width:min(100%,360px);height:230px;perspective:1000px;cursor:pointer}
.fc-inner{position:relative;width:100%;height:100%;transform-style:preserve-3d;transition:transform .5s}
.flashcard.flipped .fc-inner{transform:rotateY(180deg)}
.fc-face{
  position:absolute;inset:0;backface-visibility:hidden;-webkit-backface-visibility:hidden;
  border:3px solid var(--wood-d);border-radius:18px;
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  padding:18px;text-align:center;
  box-shadow:0 10px 24px rgba(150,110,70,.28),inset 0 0 0 3px #fff8;
}
.fc-front{background:linear-gradient(160deg,#fffaf0,#f1e0bb)}
.fc-back{background:linear-gradient(160deg,#eef8ee,#dcefdd);transform:rotateY(180deg)}
/* 标签改成正常文档流里的第一个子项，不再 absolute。
 * absolute 版本在卡片压到 150px（横屏手机）时必然和居中的单词打架：
 * 要么被单词盖住、要么加宽后把单词整个吞掉 —— 两者都试过，都不行。
 * 现在它参与 flex 布局，单词自然排在它下面，任何高度都不会重叠。 */
.fc-tag{
  font-size:10.5px;font-weight:700;color:#fff;
  background:linear-gradient(180deg,#d8985c,#bd7d46);border-radius:7px;padding:2px 10px;
  max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  margin-bottom:6px;flex:0 0 auto;
}
.fc-word{font-family:var(--pix);font-size:34px;font-weight:700;color:#4a3624;letter-spacing:1px;word-break:break-word;line-height:1.1}
.fc-ph{font-size:15px;color:#9b7a52;margin-top:8px;font-family:var(--pix)}
.fc-speak,.fc-speak2{
  margin-top:12px;border:2px solid var(--wood-d);background:#f4dcaf;color:var(--wood-dd);
  border-radius:10px;padding:6px 15px;font-size:12.5px;font-weight:700;cursor:pointer;
  transition:.15s;
}
.fc-speak:hover,.fc-speak2:hover{background:var(--gold)}
/* 翻转提示原本贴在 .fc-face 内部（absolute bottom:10px）。挪到 .flashcard
 * 外层后，定位基准变成 .flashcard —— 那里没有 perspective/3D，提示才不会
 * 跟着卡片一起翻转、在背面显示成镜像。 */
.fc-flip-hint{position:absolute;left:0;right:0;bottom:9px;text-align:center;font-size:11px;color:#b0916a;pointer-events:none}
.fc-pos-cn{font-size:16px;font-weight:700;color:#4a9952}
.fc-ex-en{font-size:14px;color:#4a3624;line-height:1.45;margin-top:12px;font-weight:600}
.fc-ex-cn{font-size:12.5px;color:#6f7d62;line-height:1.4;margin-top:6px}

/* 三个操作按钮必须能换行、能收缩。
 * 412×915 竖屏下 .wb-body 只有 388px，词表固定占 230px，留给 .wb-main 的
 * 只有 ~144px；而 "✓ Know / ✗ Don't know / ↻ Flip" 一行最小内容宽 ~280px。
 * flex 默认 nowrap 且不允许收缩，于是整行从左侧溢出 56px（实测 x 到 -56），
 * 按钮直接被推到屏幕外。允许换行 + 居中，任意宽度都留得住。 */
.wb-actions{display:flex;gap:10px;flex-wrap:wrap;justify-content:center;max-width:100%}
.wb-act{
  border:3px solid var(--wood-d);border-radius:14px;padding:10px 24px;
  font-size:14px;font-weight:700;cursor:pointer;box-shadow:var(--shadow);
  background:#fffaf0;color:var(--ink);transition:transform .12s,box-shadow .12s;
}
.wb-act.know{background:#dbf1db;color:#3d8a48}
.wb-act.unknown{background:#fddcd6;color:#b8452f}
.wb-act.flip{background:#e8e2fb;color:#5a49a0}
.wb-act:active{transform:translateY(3px);box-shadow:0 2px 4px rgba(0,0,0,.2)}
.wb-shortcuts{font-size:11px;color:#a98c62}

/* Word list */
/* overflow:visible —— 滚完的内容不该被祖先再剪一刀，否则越滚越"糊"，
 * 视觉上像是内容被裁掉。滚动条归 .wb-list 自己管。 */
.wb-list{
  width:230px;flex-shrink:0;overflow-y:auto;overscroll-behavior:contain;
  background:#fffaf0;
  border:3px solid #ecd6b0;border-radius:14px;padding:8px;min-height:0;
}
.wb-list-item{
  display:flex;align-items:center;gap:6px;padding:7px 9px;border-radius:10px;
  font-size:12.5px;cursor:pointer;border:2px solid transparent;transition:.12s;
}
.wb-list-item:hover{background:#f8eed8}
.wb-list-item.active{background:#fff3cf;border-color:var(--gold)}
.wb-list-item .li-w{font-family:var(--pix);font-weight:700;color:#4a3624;flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.wb-list-item .li-cn{font-size:10.5px;color:#b0916a;flex-shrink:0;max-width:44%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.wb-list-item .li-dot{width:9px;height:9px;border-radius:50%;background:#ecd6b0;flex-shrink:0}
.wb-list-item.known .li-dot{background:#6bc471}
.wb-list-item.unknown .li-dot{background:var(--red)}
.wb-footer{display:flex;align-items:center;justify-content:space-between;padding:8px 16px 12px;border-top:2px dashed #e2c79a;font-size:12px;color:var(--wood-dd)}
.btn-ghost{border:2px solid var(--wood-d);background:transparent;color:var(--wood-dd);border-radius:10px;padding:6px 13px;font-size:12px;cursor:pointer;transition:.15s}
.btn-ghost:hover{background:var(--red);color:#fff;border-color:#c05a48}

/* Toast */
.toast{
  position:fixed;left:50%;top:22px;transform:translateX(-50%);z-index:999;
  background:rgba(43,29,18,.92);color:#ffE9c2;font-size:13.5px;font-weight:600;
  padding:9px 20px;border-radius:10px;border:2px solid var(--gold);
  animation:toastIn .25s ease;
}
@keyframes toastIn{from{opacity:0;transform:translate(-50%,-12px)}to{opacity:1;transform:translate(-50%,0)}}

/* ---------------- Small screen (landscape phone) ---------------- */
@media (max-height:520px){
  .dialogue-box{min-height:104px;padding:12px 16px 16px}
  .dialogue-en{font-size:13.5px}
  .dialogue-cn{font-size:11.5px}
  .speaker-name{font-size:12px;padding:2px 12px}
  /* 面板现在靠内部滚动消化超高内容，所以窄屏上可以适当放宽宽度、
   * 放大字号 —— 代价只是多滚几下，换来的是英文标题不再被拦腰折断
   * （此前 218px 宽会把 "Managing Dorm Life" 拆成两行半）。 */
  #quest-panel{width:min(38vw,300px);padding:8px 11px}
  #quest-panel.collapsed{width:auto;max-width:min(56vw,260px);padding:6px 9px}
  .qm-txt{font-size:11.5px}
  .qm-tag{font-size:9px;padding:2px 6px}
  .quest-head h3{font-size:12.5px}
  .quest-collapse{top:8px;right:9px;width:22px;height:22px;font-size:12px}
  .quest-list li.task{font-size:12px;padding:5px 8px}
  .quest-list li.task .q-txt b{font-size:12px}
  .quest-list li.task .q-txt small{display:none}
  .quest-list li.goal{font-size:11px;padding:4px 7px 4px 10px;margin-left:12px}
  .quest-list li.goal .g-txt b{font-size:11.5px}
  .quest-list li.goal .g-txt small{display:none}
  .hud-btn{width:54px;height:54px}
  .hud-btn .hud-ico{font-size:20px}
  .hud-btn .hud-ico .hud-ico-img{width:24px;height:24px}
  /* 右侧 5 个按钮在 360px 高的横屏上原本占满 75% 的高度，几乎顶到地图
   * 中央。收窄间距、按下时再缩一点，把这一列压到 ~62%。 */
  .course-btn{top:10px;right:10px}
  .settings-btn{top:74px;right:10px}
  .wordbook-btn{bottom:10px;right:10px}
  .practice-btn{bottom:74px;right:10px}
  .bgm-btn{bottom:10px;left:10px}
  /* 底部操作提示在窄屏上会和地图中央的任务面板抢地方。
   * 面板从左侧 14px 起、宽 min(30vw,324px)，在 640px 宽屏上右缘到 ~36%。
   * 单纯居中（left:50% translateX(-50%)）会让提示左缘压到 28.6%，
   * 与面板重叠 49px。这里改成"从面板右侧起、在剩余空间内居中"，
   * 彻底避开重叠。 */
  /* 底部操作提示在窄屏上要和左右两侧的 HUD 都错开：
   *   左：任务面板（14px 起，宽 min(38vw,300px)）
   *   右：.wordbook-btn 按钮列（right:10px，宽 54px）
   * 只写 right:12px 会让提示右缘压到按钮上（实测重叠 ~52px），
   * 所以右边要留出「按钮宽 54 + 间距 10 + 再 12 缓冲」= 76px。 */
  .map-hint{font-size:10px;padding:4px 10px;bottom:8px;
            left:calc(min(38vw,300px) + 26px);right:76px;
            transform:none;max-width:none;
            white-space:normal;text-align:center;line-height:1.35}
  .flashcard{height:172px}
  .fc-word{font-size:26px}
  .fc-ph{font-size:13px;margin-top:4px}
  .fc-face{padding:12px}
  .fc-flip-hint{display:none}
  .fc-speak,.fc-speak2{margin-top:6px;padding:4px 12px;font-size:11.5px}
  .wb-act{padding:8px 16px;font-size:13px}
  /* 词书在横屏手机上竖向空间只有 ~150px，三列并排谁都放不下。
   * 词表收窄成窄条，闪卡降到 150px，靠 .wb-cards 自己滚。 */
  .wb-body{gap:10px;padding:8px 12px}
  .wb-list{width:150px;padding:6px}
  .wb-list-item{padding:5px 7px;gap:5px;font-size:11.5px}
  .wb-list-item .li-cn{font-size:9.5px}
  .wb-main{gap:8px}
  .flashcard{height:150px}
  .fc-word{font-size:23px}
  .wb-actions{gap:7px}
  .wb-act{padding:7px 13px;font-size:12px}
  .wb-shortcuts{display:none}
  .wb-progress{padding:5px 12px 0}
  .wb-bar{height:10px}
  .wb-view-tabs{padding:0 12px 5px}
  .wb-view-tab{padding:4px 12px;font-size:11.5px}
  .wb-footer{padding:5px 12px 8px;font-size:11px}
  .ending-box{padding:18px}
  /* Phone panel compression for small screens */
  .phone-frame{width:min(92vw,380px);padding:10px 9px 12px;border-radius:26px}
  .phone-screen{padding:9px 11px 11px;gap:6px;max-height:88vh;border-radius:18px}
  .phone-screen h3{font-size:15px}
  #phone-note{font-size:10.5px}
  .phone-rows{gap:6px}
  .phone-row{padding:5px 9px 6px;border-radius:10px}
  .phone-row .pr-head{margin-bottom:1px}
  .pr-range{height:16px}
  .pr-range::-webkit-slider-thumb{width:17px;height:17px;margin-top:-5px}
  .pr-range::-moz-range-thumb{width:16px;height:16px}
  .phone-total{padding:6px 9px}
  .phone-total #phone-total-val{font-size:16px}
  .phone-confirm{padding:8px 20px;font-size:13.5px}
}

/* ============================================================
 *  After-class practice
 *  Overlay stack: full-screen, sits above the map and the story stage.
 * ============================================================ */
.practice-root{
  position:fixed;inset:0;z-index:60;
  display:flex;align-items:center;justify-content:center;
  background:rgba(30,20,12,.62);
  backdrop-filter:blur(3px);
  font-family:var(--ui);
  padding:16px;
}
.practice-root.hidden{display:none}

.pr-screen{width:100%;max-width:760px;max-height:96vh;overflow-y:auto;display:flex;justify-content:center}
.pr-card{
  width:100%;background:linear-gradient(180deg,#fffaf0,#f6e7cc);
  border:3px solid var(--wood-d);border-radius:22px;
  padding:26px 30px 24px;box-shadow:0 14px 40px rgba(40,24,10,.4);
  text-align:center;position:relative;
}
.pr-badge{
  display:inline-block;background:var(--wood);color:#fff;font-size:11.5px;font-weight:800;
  letter-spacing:.06em;text-transform:uppercase;padding:4px 12px;border-radius:20px;margin-bottom:12px;
}
.pr-badge-green{background:var(--green)}
.pr-badge-yellow{background:var(--gold-d)}
.pr-badge-red{background:var(--red)}
.pr-card h2{margin:0 0 6px;font-size:24px;color:var(--wood-dd)}
.pr-sub{margin:0 0 16px;font-size:13.5px;color:#7a5c3c}

.pr-rules{list-style:none;margin:0 auto 16px;padding:0;max-width:520px;text-align:left}
.pr-rules li{
  position:relative;padding:7px 10px 7px 26px;font-size:13.5px;color:#5a4228;
  border-bottom:1px dashed rgba(138,90,52,.24);
}
.pr-rules li:last-child{border-bottom:none}
.pr-rules li::before{content:"▸";position:absolute;left:8px;color:var(--wood)}
.pr-t{background:var(--wood-dd);color:#fff;border-radius:6px;padding:1px 6px;font-size:11px;font-weight:800}
.pr-note{font-size:12.5px;color:#8a6a4a;margin:12px 0 0}
.pr-note.pr-warn{color:#b2543e;font-weight:700}

.pr-legend{display:flex;gap:10px;justify-content:center;flex-wrap:wrap;margin:16px 0 4px}
.lg{font-size:12px;font-weight:700;padding:5px 12px;border-radius:20px;color:#fff}
.lg-green{background:var(--green)}
.lg-yellow{background:var(--gold-d)}
.lg-red{background:var(--red)}

.pr-actions{display:flex;gap:12px;justify-content:center;margin-top:20px;flex-wrap:wrap}
.pr-btn{
  font-family:var(--ui);font-size:14.5px;font-weight:800;cursor:pointer;
  padding:11px 26px;border-radius:14px;border:3px solid var(--wood-d);transition:.14s;
  background:linear-gradient(180deg,#fff8e8,#f2d8a8);color:var(--wood-dd);
  box-shadow:0 4px 0 rgba(122,84,52,.24);
}
.pr-btn:hover{transform:translateY(-1px)}
.pr-btn:active{transform:translateY(2px);box-shadow:0 2px 0 rgba(122,84,52,.24)}
.pr-btn.pr-go{background:linear-gradient(180deg,#9fd98a,#6fb457);color:#2e4d1c;border-color:#4d7f33}
.pr-btn.pr-skip{opacity:.78}

/* ---- countdown ---- */
.pr-cd-screen{flex-direction:column;align-items:center;gap:6px}
.pr-countdown{
  font-family:var(--pix);font-size:110px;font-weight:900;color:#fff;
  text-shadow:0 6px 0 rgba(90,60,30,.5),0 0 46px rgba(255,220,160,.7);
  line-height:1;
}
.pr-countdown.pop{animation:cdPop .55s cubic-bezier(.2,1.6,.4,1)}
@keyframes cdPop{from{transform:scale(.3);opacity:0}to{transform:scale(1);opacity:1}}
.pr-cd-hint{color:#ffe9c8;font-size:15px;font-weight:700;letter-spacing:.1em}

/* ---- question ---- */
.pr-progress{
  position:absolute;top:14px;left:50%;transform:translateX(-50%);
  font-size:12px;font-weight:800;color:#fff;background:rgba(43,29,18,.7);
  padding:3px 14px;border-radius:20px;
}
.pr-qtag{font-size:12.5px;font-weight:800;color:var(--wood);letter-spacing:.04em;text-transform:uppercase;margin-bottom:14px}
.pr-word{
  font-family:var(--pix);font-size:46px;font-weight:800;color:var(--wood-dd);
  letter-spacing:.01em;margin:8px 0 4px;word-break:break-word;
}
.pr-word.pr-word-sm{font-size:34px}
.pr-ph{font-size:14px;color:#8a6a4a;margin-bottom:6px}
.pr-pos{display:inline-block;font-size:12px;font-weight:800;color:#7a5c3c;background:rgba(201,138,82,.2);
        border-radius:6px;padding:1px 8px;margin-bottom:8px}
.pr-choices{display:grid;gap:11px;margin-top:20px}
.pr-choice{
  font-family:var(--ui);font-size:14.5px;font-weight:700;cursor:pointer;
  padding:14px 18px;border-radius:13px;border:2.5px solid var(--wood);
  background:#fffaf0;color:#4a3624;transition:.12s;text-align:center;
}
.pr-choice:hover{background:#fff3dd;transform:translateY(-1px)}
.pr-choice.right{background:#dff3d4;border-color:#5f9c42;color:#2e4d1c}
.pr-choice.wrong{background:#ffe0d8;border-color:#c9563c;color:#8c2f1c}

/* ---- weighted score (vocabulary results) ----
   Mirrors the cloze score readout so a number looks the same wherever the
   learner meets one, but carries the `pr-` prefix because the two live in
   different cards. */
.pr-score{
  display:flex;align-items:center;justify-content:center;gap:14px;
  padding:12px 18px;border-radius:14px;margin:0 auto 6px;max-width:520px;
  border:2.5px solid #e2c99b;background:#fffaf0;
}
.pr-score.green {border-color:#6bc471;background:#eef8ec}
.pr-score.yellow{border-color:#e8c341;background:#fdf6e0}
.pr-score.red   {border-color:var(--red);background:#fdeee9}
.pr-score-num{
  font-family:var(--pix);font-size:40px;font-weight:800;line-height:1;
  color:var(--wood-dd);min-width:52px;text-align:right;
}
.pr-score-num::after{
  content:'pts';font-size:11px;font-family:var(--ui);font-weight:400;
  color:#a98c62;margin-left:3px;vertical-align:super;
}
.pr-score-side{text-align:left;min-width:0}
.pr-score-lab{font-family:var(--pix);font-size:13px;font-weight:700;color:var(--wood-dd)}
.pr-score-sub{font-size:11px;color:#8a6a4a;margin-top:3px;line-height:1.4}

/* ---- bubbles ---- */
.pr-bub-rows{display:grid;gap:14px;margin:18px 0 6px;text-align:left}
.pr-bub-row{border:2px dashed rgba(138,90,52,.3);border-radius:14px;padding:12px 14px}
.pr-bub-head{display:flex;align-items:center;gap:8px;font-size:13.5px;color:var(--wood-dd);margin-bottom:9px}
.pr-dot{width:13px;height:13px;border-radius:50%;display:inline-block;box-shadow:inset 0 -2px 0 rgba(0,0,0,.14)}
.pr-dot-green{background:var(--green)}
.pr-dot-yellow{background:var(--gold)}
.pr-dot-red{background:var(--red)}
.pr-bub-count{margin-left:auto;font-weight:800;background:rgba(201,138,82,.22);border-radius:20px;padding:1px 11px}
.pr-bub-wrap{display:flex;flex-wrap:wrap;gap:8px}
.pr-bub{
  cursor:pointer;font-size:13px;font-weight:800;padding:6px 14px;border-radius:20px;
  border:2px solid transparent;transition:.14s;
}
.pr-bub:hover{transform:translateY(-2px) scale(1.04)}
.pr-bub-green{background:#cdecc0;color:#2c5417;border-color:#7bc47f}
.pr-bub-yellow{background:#ffe9b3;color:#7a5a12;border-color:#e0a52e}
.pr-bub-red{background:#ffd2c8;color:#8c2f1c;border-color:#ee7f6a}
.pr-bub-empty{font-size:12.5px;color:#a8907a;font-style:italic}
.pr-bub-hint{font-size:11.5px;color:#8a6a4a;margin-top:8px}

/* ---- exit ✕ + leave-confirmation -------------------------------------
 * The ✕ lives inside .pr-screen, which is a scrolling flex column; using
 * position:fixed anchors it to the viewport instead, so it stays pinned to
 * the corner while a tall card scrolls underneath. Only the answering
 * screens render it — never the intro or the results screen. */
.pr-exit{
  position:fixed;top:14px;right:16px;z-index:65;
  width:38px;height:38px;padding:0;line-height:1;
  display:flex;align-items:center;justify-content:center;
  font-family:var(--pix);font-size:20px;font-weight:700;
  color:#7a5c3c;background:linear-gradient(180deg,#fffaf0,#f2e0c2);
  border:2.5px solid var(--wood);border-radius:50%;cursor:pointer;
  box-shadow:0 3px 0 rgba(122,84,52,.28);transition:transform .08s,background .15s;
}
.pr-exit:hover{background:linear-gradient(180deg,#fdeee9,#f8d9cf);
  color:var(--red);border-color:#c9553c;transform:translateY(-1px)}
.pr-exit:active{transform:translateY(2px);box-shadow:0 1px 0 rgba(122,84,52,.28)}

/* The confirm box reuses the word-card modal's layer (z 70) so it sits above
 * the ✕ and every card, and dims the run behind it. */
.pr-confirm{
  position:fixed;inset:0;z-index:70;display:flex;align-items:center;justify-content:center;
  background:rgba(20,12,6,.6);padding:16px;
}
.pr-confirm-card{
  width:min(92vw,420px);background:linear-gradient(180deg,#fffaf0,#f6e7cc);
  border:3px solid var(--wood-d);border-radius:20px;padding:26px 26px 22px;
  text-align:center;box-shadow:0 16px 40px rgba(40,24,10,.45);
}
.pr-confirm-card h3{font-family:var(--pix);font-size:26px;margin:0 0 8px;color:var(--wood-dd)}
.pr-confirm-card p{font-size:13.5px;color:#7a5c3c;line-height:1.55;margin:0}

/* ---- word card modal ---- */
.pr-modal{
  position:fixed;inset:0;z-index:70;display:flex;align-items:center;justify-content:center;
  background:rgba(20,12,6,.6);padding:16px;
}
.pr-modal-card{
  width:min(92vw,460px);background:linear-gradient(180deg,#fffaf0,#f6e7cc);
  border:3px solid var(--wood-d);border-radius:20px;padding:24px 26px 22px;
  text-align:center;box-shadow:0 16px 40px rgba(40,24,10,.45);position:relative;
}
.pr-modal-tier{position:absolute;top:0;left:0;right:0;height:7px;border-radius:17px 17px 0 0}
.pr-modal-tier.pr-dot-green{background:var(--green)}
.pr-modal-tier.pr-dot-yellow{background:var(--gold)}
.pr-modal-tier.pr-dot-red{background:var(--red)}
.pr-modal-card h3{font-family:var(--pix);font-size:32px;margin:6px 0 2px;color:var(--wood-dd)}
.pr-modal-cn{font-size:15px;color:#5a4228;margin:8px 0 0;font-weight:700}
.pr-modal-en{font-size:13px;color:#7a5c3c;font-style:italic;margin:8px 0 0}
.pr-modal-extra{
  margin:14px 0 0;padding:10px 12px;background:rgba(255,255,255,.6);
  border-radius:10px;font-size:12.5px;color:#5a4228;text-align:left;
}
.pr-modal-extra div{margin-bottom:4px}
.pr-modal-extra div:last-child{margin-bottom:0}
.pr-extra-box{
  margin:16px 0 0;padding:12px 14px;background:rgba(255,255,255,.62);
  border-radius:12px;font-size:13.5px;color:#5a4228;text-align:left;
}
.pr-extra-box div{margin-bottom:6px}
.pr-extra-box div:last-child{margin-bottom:0}

/* ---- read-through & spelling ---- */
.pr-read{text-align:center}
.pr-quote{
  display:block;margin:16px 0 0;padding:12px 16px;text-align:left;
  background:rgba(255,255,255,.66);border-left:4px solid var(--wood);
  border-radius:0 12px 12px 0;font-size:14px;color:#4a3624;font-style:italic;line-height:1.55;
}
.pr-input{
  width:100%;max-width:340px;margin:16px auto 0;display:block;
  font-family:var(--pix);font-size:22px;text-align:center;letter-spacing:.05em;
  padding:11px 14px;border-radius:12px;border:2.5px solid var(--wood);
  background:#fffaf0;color:var(--wood-dd);outline:none;
}
.pr-input:focus{border-color:var(--wood-dd);box-shadow:0 0 0 3px rgba(201,138,82,.28)}
.pr-feedback{margin-top:10px;font-size:14px;font-weight:800;min-height:20px}
.pr-feedback.ok{color:#4d7f33}
.pr-feedback.bad{color:#c9563c}

/* ---- recognition cue ---- */
.pr-cue-img{
  width:96px;height:96px;margin:8px auto 0;border-radius:16px;
  display:flex;align-items:center;justify-content:center;
  font-family:var(--pix);font-size:34px;font-weight:900;color:#fff;
  background:linear-gradient(160deg,#9fc9e8,#5f8fbb);box-shadow:0 6px 0 rgba(60,90,120,.3);
}
.pr-cue-en{font-size:13px;color:#7a5c3c;font-style:italic;margin:12px 0 0}
.pr-cue-audio{
  width:78px;height:78px;margin:8px auto 0;border-radius:50%;cursor:pointer;
  display:flex;align-items:center;justify-content:center;font-size:32px;
  background:linear-gradient(160deg,#ffd98a,#e0a52e);box-shadow:0 6px 0 rgba(160,110,30,.32);
  transition:.14s;
}
.pr-cue-audio:hover{transform:scale(1.05)}
.pr-cue-audio:active{transform:scale(.96)}

/* ---- small screens ---- */
@media (max-height:560px){
  .pr-card{padding:18px 20px 16px;border-radius:18px}
  .pr-card h2{font-size:19px}
  .pr-word{font-size:34px}
  .pr-word.pr-word-sm{font-size:26px}
  .pr-countdown{font-size:76px}
  /* 触摸目标不能小于 44×44（Apple HIG / WCAG 2.5.5）。
   * 手机上学生是拿手指戳的，42px 的卡片和 38px 的 ✕ 都容易点偏，
   * 这里统一抬到行高 ≥48px、✕ ≥46px，代价只是卡片略高一点。 */
  .pr-choice{padding:15px 14px;font-size:13.5px;min-height:48px;display:flex;align-items:center;justify-content:center}
  .pr-exit{width:46px;height:46px;font-size:22px;top:10px;right:12px}
  .pr-actions{margin-top:13px}
  .pr-btn{padding:9px 20px;font-size:13px}
  .pr-rules li{padding:5px 8px 5px 24px;font-size:12.5px}
  .pr-bub{font-size:12px;padding:5px 11px}
  .pr-bub-rows{gap:10px;margin:12px 0 4px}
  .pr-bub-row{padding:9px 11px}
  .pr-score{padding:9px 12px;gap:10px;margin-bottom:4px}
  .pr-score-num{font-size:30px;min-width:42px}
  .pr-score-lab{font-size:12px}
  .pr-score-sub{font-size:10px}
}

/* ============================================================
 *  Word Book · view tabs + Passage reader
 * ============================================================ */
.wb-view-tabs{display:flex;gap:6px;padding:0 16px 8px}
.wb-view-tab{
  font-family:var(--pix);font-size:11px;letter-spacing:.04em;
  padding:5px 14px;border-radius:9px;cursor:pointer;
  background:#f6e7c8;border:2px solid #e2c99b;color:#8a6a3f;
}
.wb-view-tab.active{background:var(--gold);border-color:#c99a44;color:#4a3624}

.wb-passage{display:none}
.wb-body.view-passage .wb-main,
.wb-body.view-passage .wb-list{display:none}
.wb-body.view-passage .wb-passage{display:block;flex:1;min-width:0;overflow-y:auto;padding-right:6px}

.kb-head h3{font-family:var(--pix);font-size:15px;color:#4a3624;margin-bottom:2px}
.kb-sub{font-size:11px;color:#b0916a;margin-bottom:10px}

.kb-legend{display:flex;flex-wrap:wrap;gap:12px;margin-bottom:12px;
  padding:8px 10px;background:#fdf6e7;border:1.5px dashed #e2c99b;border-radius:10px}
.kb-lg{display:flex;align-items:center;gap:5px;font-size:10.5px;color:#8a6a3f}
.kb-dot{width:10px;height:10px;border-radius:50%;display:inline-block}
.kb-green{background:#6bc471}
.kb-yellow{background:#e8c341}
.kb-red{background:#e07a6a}
.kb-none{background:#ecd6b0}

.kb-para{margin-bottom:12px}
.kb-sent{font-size:13px;line-height:1.85;color:#5a4530;margin:0}
.kb-sent + .kb-sent{margin-top:2px}

.kb-hl{
  background:transparent;color:inherit;cursor:pointer;
  padding:0 2px;border-radius:4px;
  border-bottom:2px solid #ecd6b0;font-weight:600;
  transition:background .15s;
}
.kb-hl:hover{background:#fff3cf}
.kb-hl.kb-green{background:rgba(107,196,113,.26);border-bottom-color:#6bc471}
.kb-hl.kb-yellow{background:rgba(232,195,65,.30);border-bottom-color:#e8c341}
.kb-hl.kb-red{background:rgba(224,122,106,.26);border-bottom-color:#e07a6a}

.kb-note{font-size:12px;color:#b0916a;padding:16px}

.kb-tip{
  position:absolute;left:50%;bottom:64px;transform:translateX(-50%) translateY(8px);
  background:#4a3624;color:#fdf6e7;font-size:12px;padding:7px 14px;border-radius:9px;
  opacity:0;pointer-events:none;transition:opacity .2s,transform .2s;white-space:nowrap;z-index:30;
}
.kb-tip.show{opacity:1;transform:translateX(-50%) translateY(0)}
.wordbook-card{position:relative}

/* ---- Due-review HUD button ---- */
.hud-btn.review-btn{position:absolute;bottom:92px;right:14px}
.hud-btn.review-btn.hidden{display:none}
.hud-badge{
  position:absolute;top:-4px;right:-4px;min-width:18px;height:18px;padding:0 5px;
  background:#e07a6a;color:#fff;font-family:var(--pix);font-size:10px;line-height:18px;
  text-align:center;border-radius:9px;border:2px solid #fdf6e7;
}

/* ============================================================
 *  Day planner (in-story interaction: arrange your day)
 *  Three blank slots on the left, five activities on the right.
 *  Visual language follows the reference board: a warm ruled card
 *  with pill-shaped activity chips.
 * ============================================================ */
.sched-box{
  position:absolute;inset:0;z-index:26;
  display:flex;align-items:center;justify-content:center;
  padding:clamp(10px,3vh,28px);
  background:rgba(58,40,24,.30);
  animation:scFade .22s ease;
}
@keyframes scFade{ from{opacity:0} to{opacity:1} }

.sc-board{
  width:min(96vw,940px);max-height:100%;
  display:flex;flex-direction:column;gap:clamp(7px,1.4vh,13px);
  padding:clamp(12px,2.2vh,20px) clamp(13px,2.2vw,24px) clamp(11px,2vh,17px);
  border-radius:16px;
  background:
    repeating-linear-gradient(0deg,transparent 0 27px,rgba(196,164,120,.14) 27px 28px),
    linear-gradient(165deg,#fffdf4,#f7ead2);
  border:3px solid var(--wood-d);
  box-shadow:0 18px 40px rgba(60,38,18,.44),inset 0 0 0 5px rgba(255,255,255,.6);
  animation:pop .3s cubic-bezier(.2,1.4,.4,1);
  overflow:auto;
}

.sc-head{text-align:center;flex:0 0 auto}
.sc-head h3{font-size:clamp(15px,2.5vh,19px);color:var(--wood-dd);letter-spacing:.06em}
#sched-note{
  font-size:clamp(10px,1.5vh,12px);color:#a98c62;line-height:1.45;
  margin-top:3px;max-width:56ch;margin-inline:auto;
}

.sc-body{
  flex:1 1 auto;min-height:0;
  display:grid;grid-template-columns:1fr 1.05fr;
  gap:clamp(8px,1.6vw,20px);align-items:start;
}

/* ---- left: the three time slots ---- */
.sc-slots{display:flex;flex-direction:column;gap:clamp(6px,1.3vh,11px);min-width:0}
.sc-slot{
  position:relative;display:flex;align-items:center;gap:clamp(7px,1.2vw,12px);
  width:100%;padding:clamp(8px,1.5vh,13px) clamp(10px,1.5vw,15px);
  border:2px solid var(--wood-d);border-radius:11px;cursor:pointer;
  background:linear-gradient(180deg,#fffdf6,#f6e9d2);
  box-shadow:var(--shadow);font:inherit;text-align:left;
  transition:transform .08s,border-color .15s,background .15s;
}
.sc-slot::before{                       /* lantern post, echoing the reference */
  content:"";position:absolute;left:-3px;top:9%;bottom:9%;width:4px;
  border-radius:3px;background:var(--wood);opacity:.55;
}
.sc-slot:active{transform:translateY(2px)}
.sc-slot-when{
  flex:0 0 auto;min-width:clamp(54px,7vw,72px);
  font-family:var(--pix);font-size:clamp(9.5px,1.4vh,11.5px);font-weight:700;
  letter-spacing:.08em;text-transform:uppercase;color:#a98c62;
}
.sc-slot-what{
  flex:1 1 auto;min-width:0;
  font-size:clamp(11px,1.7vh,14px);font-weight:800;color:var(--wood-dd);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.sc-slot-what i{font-style:normal;font-weight:700;color:#cbb693;letter-spacing:.18em}
.sc-slot.filled{
  border-color:var(--wood);background:linear-gradient(180deg,#fff8e6,#f3e0bd);
}
.sc-slot.filled .sc-slot-when{color:var(--wood-d)}
/* the most recently filled slot gets a soft emphasis, like the reference */
.sc-slot.filled{animation:scDrop .26s cubic-bezier(.2,1.5,.4,1)}
@keyframes scDrop{ from{transform:translateX(-10px);opacity:.4} to{transform:none;opacity:1} }

/* ---- right: the five activities ---- */
.sc-palette{min-width:0}
.sc-palette-label{
  font-family:var(--pix);font-size:clamp(9px,1.3vh,10.5px);font-weight:700;
  letter-spacing:.16em;text-transform:uppercase;color:#b0916a;
  margin-bottom:clamp(5px,1vh,8px);padding-left:2px;
}
.sc-palette-grid{
  display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
  gap:clamp(6px,1.2vh,10px);
}
.sc-opt{
  display:flex;align-items:center;justify-content:space-between;gap:7px;
  padding:clamp(8px,1.6vh,13px) clamp(9px,1.3vw,14px);
  border:2px solid var(--wood-d);border-radius:11px;cursor:pointer;
  background:linear-gradient(180deg,#fffdf6,#f6e9d2);
  box-shadow:var(--shadow);font:inherit;
  transition:transform .08s,background .15s,opacity .15s;
}
.sc-opt:active{transform:translateY(2px)}
.sc-opt-name{
  font-size:clamp(10.5px,1.6vh,13.5px);font-weight:800;color:var(--wood-dd);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.sc-opt-n{
  flex:0 0 auto;min-width:20px;height:20px;padding:0 5px;border-radius:10px;
  background:#efe0c4;color:#a98c62;
  font-family:var(--pix);font-size:10.5px;font-weight:700;line-height:20px;text-align:center;
}
.sc-opt.used{background:linear-gradient(180deg,#fff8e6,#f3e0bd);border-color:var(--wood)}
.sc-opt.used .sc-opt-n{background:var(--wood);color:#fffaf0}
.sc-opt.dim{opacity:.42}

/* ---- footer ---- */
.sc-foot{
  flex:0 0 auto;display:flex;align-items:center;justify-content:flex-end;
  gap:clamp(7px,1.4vw,13px);flex-wrap:wrap;
}
.sc-btn{
  font:inherit;cursor:pointer;
  padding:clamp(8px,1.5vh,12px) clamp(14px,2.4vw,26px);
  border:2px solid var(--wood-d);border-radius:11px;
  font-size:clamp(11px,1.6vh,14px);font-weight:800;letter-spacing:.06em;
  box-shadow:var(--shadow);transition:transform .08s,opacity .15s,filter .15s;
}
.sc-btn:active{transform:translateY(2px)}
.sc-btn-ghost{background:linear-gradient(180deg,#fffdf6,#f2e5cd);color:#8a5a34}
.sc-btn-run{
  background:linear-gradient(180deg,#f6b64f,#e09a2c);color:#4a3018;
  border-color:#a9721c;
}
.sc-btn-run:hover{filter:brightness(1.05)}
.sc-btn:disabled{opacity:.4;cursor:not-allowed;box-shadow:none}
.sc-btn:disabled:active{transform:none}

@media (max-width:720px), (max-height:520px){
  .sc-body{grid-template-columns:1fr}
  .sc-slots{flex-direction:row;gap:5px}
  .sc-slot{flex-direction:column;align-items:flex-start;gap:3px;padding:6px 8px}
  .sc-slot::before{display:none}
  .sc-slot-when{min-width:0}
  .sc-slot-what{font-size:11px;white-space:normal;line-height:1.25}
  .sc-palette-grid{grid-template-columns:repeat(2,minmax(0,1fr))}
  .sc-head h3{font-size:14px}
  #sched-note{font-size:10px}
  .sc-foot{justify-content:stretch}
  .sc-btn{flex:1 1 auto;text-align:center;padding:8px 12px}
}

/* ============================================================
 *  Cloze practice (banked fill-in-the-blank)
 *  Rendered by js/cloze.js into whatever host mounts it — the
 *  Practice hub today. Layout: passage on top (scrolls), word-bank
 *  chips under it, action row at the bottom, verdict panel below.
 *  The host is responsible for giving #cl-text a height, so the
 *  board is always mounted inside a flex column with min-height:0.
 * ============================================================ */

/* ---- head: passage picker · difficulty · counters ---- */
.cl-head{display:flex;align-items:center;gap:8px;flex-wrap:wrap;flex-shrink:0}
.cl-pick,.cl-modes{display:flex;gap:5px}
.cl-pick-btn,.cl-mode-btn{
  font-family:var(--pix);font-size:11px;font-weight:700;
  padding:5px 11px;border-radius:8px;cursor:pointer;
  background:#f6e7c8;border:2px solid #e2c99b;color:#8a6a3f;
}
.cl-pick-btn.active{background:linear-gradient(180deg,#d8985c,#bd7d46);color:#fff4dd;border-color:#a9682f}
.cl-mode-btn{
  font-size:10px;padding:4px 9px;background:#fffaf0;
}
.cl-mode-btn.active{background:var(--gold);border-color:#c99a44;color:#4a3624}
.cl-meta{
  margin-left:auto;display:flex;gap:10px;
  font-family:var(--pix);font-size:10.5px;color:#a98c62;white-space:nowrap;
}

/* ---- passage ---- */
.cl-text{
  flex:1 1 auto;min-height:0;overflow-y:auto;
  background:linear-gradient(180deg,#fffdf5,#fdf5e4);
  border:2px solid #ecd6b0;border-radius:12px;
  padding:14px 16px;
  font-size:14.5px;line-height:2.35;color:#5a4530;
}


.cl-blank{
  display:inline-block;vertical-align:middle;
  min-width:76px;padding:2px 9px;margin:0 2px;
  font-family:var(--pix);font-size:13px;font-weight:700;line-height:1.5;
  color:#a98c62;background:#fffdf6;
  border:2px solid #e2c99b;border-radius:8px;cursor:pointer;
  transition:background .12s,border-color .12s,box-shadow .12s;
}
.cl-blank:hover{border-color:var(--gold)}
.cl-blank.focus{
  background:#fff3cf;border-color:var(--gold);color:#8a6a3f;
  box-shadow:0 0 0 3px rgba(255,207,92,.40);
}
.cl-blank.filled{color:var(--ink);background:#fff;border-color:#d7bb8c}
.cl-blank.ok{background:#e4f6e2;border-color:#6bc471;color:#2f7a3c}
.cl-blank.bad{background:#fdeae5;border-color:var(--red);color:#b8452f}
.cl-blank.missed{font-style:italic;opacity:.9}

.cl-input{
  display:inline-block;vertical-align:middle;
  min-width:70px;padding:2px 8px;margin:0 2px;
  font-family:var(--pix);font-size:13px;font-weight:700;line-height:1.5;
  color:var(--ink);background:#fffdf6;
  border:2px solid #e2c99b;border-radius:8px;outline:none;
}
.cl-input:focus{border-color:var(--gold);box-shadow:0 0 0 3px rgba(255,207,92,.40)}
.cl-input.ok{background:#e4f6e2;border-color:#6bc471;color:#2f7a3c}
.cl-input.bad{background:#fdeae5;border-color:var(--red);color:#b8452f}

/* ---- word bank ---- */
.cl-bank{
  flex-shrink:0;display:flex;flex-wrap:wrap;gap:6px;
  max-height:96px;overflow-y:auto;
  padding:9px 10px;background:#fdf6e7;
  border:1.5px dashed #e2c99b;border-radius:10px;
}
.cl-bank:empty{display:none}
.cl-chip{
  font-family:var(--pix);font-size:12px;font-weight:700;
  padding:5px 12px;border-radius:999px;cursor:pointer;
  background:linear-gradient(180deg,#fffaf0,#f3e5c8);
  border:2px solid #e2c99b;color:#6b4322;
  box-shadow:0 2px 0 rgba(122,84,52,.18);
  transition:background .12s,transform .06s;
}
.cl-chip:hover{background:linear-gradient(180deg,#ffe9b0,#ffd97a);border-color:#c99a44}
.cl-chip:active{transform:translateY(2px);box-shadow:none}
.cl-chip.spent{
  opacity:.34;background:#efe4d0;color:#a98c62;border-color:#e0d0b4;
  box-shadow:none;cursor:default;text-decoration:line-through;
}

/* ---- action row ---- */
.cl-foot{display:flex;align-items:center;gap:8px;flex-shrink:0}
.cl-btn{
  font-family:var(--pix);font-size:11.5px;font-weight:700;
  padding:7px 15px;border-radius:10px;cursor:pointer;
  background:#f6e7c8;border:2px solid #e2c99b;color:#6b4322;
  box-shadow:0 2px 0 rgba(122,84,52,.20);
}
.cl-btn:active{transform:translateY(2px);box-shadow:none}
.cl-btn.primary{background:linear-gradient(180deg,#9bd97f,#6bc471);border-color:#4f9a55;color:#22401f}
.cl-btn.ghost{margin-left:auto;background:#fffaf0;color:#a98c62}
.cl-best{font-family:var(--pix);font-size:10.5px;color:#a98c62;white-space:nowrap}

/* ---- verdict ---- */
.cl-result{display:none;flex-shrink:0}
.cl-result.show{display:block;max-height:38%;overflow-y:auto}
.cl-score{
  display:flex;align-items:center;gap:12px;
  padding:9px 12px;border-radius:12px;margin-bottom:8px;
  border:2px solid #e2c99b;background:#fffaf0;
}
.cl-score.green{border-color:#6bc471;background:#eef8ec}
.cl-score.yellow{border-color:#e8c341;background:#fdf6e0}
.cl-score.red{border-color:var(--red);background:#fdeee9}
.cl-score-num{font-family:var(--pix);font-size:22px;font-weight:700;color:var(--ink)}
.cl-score-lab{font-family:var(--pix);font-size:13px;font-weight:700;color:var(--ink)}
.cl-score-sub{font-size:10.5px;color:#8a6a3f;margin-top:2px}

.cl-list{list-style:none;display:flex;flex-direction:column;gap:6px}
.cl-list li{
  padding:7px 11px;border-radius:10px;
  background:#fdf1e6;border-left:4px solid var(--red);
  font-size:11.5px;line-height:1.5;color:#5a4530;
}
.cl-list li.soft{background:#fdf8e4;border-left-color:#e8c341}
.cl-list li b{font-family:var(--pix);font-size:12.5px;color:var(--ink)}
.cl-list li s{color:#b8452f}
.cl-why{margin-top:3px;font-size:10.5px;color:#8a6a3f}
.cl-say{
  margin-left:6px;padding:0 6px;font-size:10px;cursor:pointer;
  background:#fff;border:1.5px solid #e2c99b;border-radius:6px;color:#8a6a3f;
}
.cl-allright{
  padding:10px 12px;border-radius:10px;
  background:#eef8ec;border-left:4px solid #6bc471;
  font-size:12px;color:#2f7a3c;
}
.cl-note{margin-top:8px;font-size:10.5px;color:#b0916a;line-height:1.5}

@media (max-width:720px), (max-height:520px){
  .cl-text{font-size:12.5px;line-height:2.0;padding:10px 12px}
  .cl-blank,.cl-input{min-width:60px;font-size:11.5px;padding:1px 7px}
  .cl-chip{font-size:11px;padding:4px 10px}
  .cl-bank{max-height:74px}
  .cl-btn{font-size:10.5px;padding:6px 11px}
  .cl-score-num{font-size:18px}
  .cl-meta{font-size:9.5px}
}

/* ---- Score readout (cloze results) ---- */
.cl-score-num{font-size:32px;line-height:1;min-width:64px;text-align:right}
.cl-score-num::after{
  content:'pts';font-size:11px;font-family:var(--ui);font-weight:400;
  color:#a98c62;margin-left:3px;vertical-align:super;
}
.cl-score-side{flex:1;min-width:0}
.cl-score-lab{font-size:14px}
.cl-btn:disabled{opacity:.45;cursor:not-allowed;box-shadow:none;transform:none}

/* ============================================================
 *  Practice hub
 *  Unit → Reading → Exercise drill-down, plus the host pane the
 *  cloze engine renders into.
 * ============================================================ */
.practice-hub{
  width:min(940px,94vw);max-height:92vh;
  display:flex;flex-direction:column;overflow:hidden;
}
.practice-hub .overlay-head{flex-shrink:0}

/* ---- breadcrumbs ---- */
.ph-crumbs{
  display:flex;align-items:center;gap:4px;flex-wrap:wrap;
  padding:12px 18px 0;flex-shrink:0;min-height:24px;
}
.ph-crumb{
  font-family:var(--pix);font-size:10.5px;font-weight:700;
  padding:3px 9px;border-radius:8px;cursor:pointer;
  background:#f6e7c8;border:2px solid #e2c99b;color:#8a6a3f;
}
.ph-crumb:hover{background:#fffaf0}
.ph-crumb-cur{background:transparent;border-color:transparent;color:#a98c62;cursor:default}
.ph-sep{color:#cbb28d;font-size:11px}

/* ---- the scrolling list area ---- */
.ph-body{flex:1;min-height:0;overflow-y:auto;padding:14px 18px 18px}
.ph-lead{margin:0 0 12px;font-size:12.5px;color:#8a6a4a;line-height:1.5}
.ph-empty{margin:14px 2px;font-size:13px;color:#a98c62}

.ph-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:10px}

.ph-card{
  display:flex;flex-direction:column;gap:3px;text-align:left;
  font-family:var(--ui);cursor:pointer;position:relative;
  padding:13px 15px;border-radius:14px;
  background:linear-gradient(180deg,#fffdf6,#f8ecd4);
  border:2px solid #ecd6b0;
  box-shadow:0 3px 0 rgba(150,110,70,.14);
  transition:transform .12s,box-shadow .12s,background .2s;
}
.ph-card:hover{transform:translateY(-2px);background:#fff;box-shadow:0 6px 14px rgba(150,110,70,.2)}
.ph-card:active{transform:translateY(1px);box-shadow:0 2px 0 rgba(150,110,70,.14)}
.ph-card-no{
  font-family:var(--pix);font-size:10px;font-weight:800;letter-spacing:.06em;
  text-transform:uppercase;color:#b98d55;
}
.ph-card-t{font-size:14.5px;font-weight:800;color:var(--wood-dd);line-height:1.25}
.ph-card-m{font-size:11.5px;color:#8a6a4a;line-height:1.35}
.ph-card.is-locked,.ph-card[disabled]{
  opacity:.5;cursor:not-allowed;filter:grayscale(.35);
}
.ph-card.is-locked:hover,.ph-card[disabled]:hover{
  transform:none;background:linear-gradient(180deg,#fffdf6,#f8ecd4);
  box-shadow:0 3px 0 rgba(150,110,70,.14);
}

/* A word-set row: title on the left, verdict badge pinned right. */
.ph-card-row{padding-right:150px}
.ph-card-row .ph-st{position:absolute;top:12px;right:12px}

.ph-st{
  font-family:var(--pix);font-size:9.5px;font-weight:800;
  padding:3px 8px;border-radius:20px;white-space:nowrap;color:#fff;
}
.ph-st.green {background:var(--green)}
.ph-st.yellow{background:var(--gold-d)}
.ph-st.red   {background:var(--red)}
.ph-st.none  {background:#d9c9ab;color:#7a6242}

/* ---- cloze host pane ---- */
.ph-cloze-wrap{
  flex:1;min-height:0;display:flex;flex-direction:column;
  padding:14px 18px 18px;
}
.ph-cloze-wrap[hidden]{display:none}
/* The engine's board needs a flex column with a real height, otherwise
 * .cl-text{flex:1} collapses to zero inside the overlay card. */
.ph-cloze{
  flex:1;min-height:0;display:flex;flex-direction:column;gap:8px;
}

/* Narrow / short viewports: single column, tighter padding. */
@media (max-width:720px), (max-height:520px){
  .practice-hub{width:96vw;max-height:94vh}
  .ph-grid{grid-template-columns:1fr}
  .ph-card{padding:11px 13px}
  .ph-card-t{font-size:13.5px}
  .ph-card-m{font-size:11px}
  .ph-card-row{padding-right:132px}
}

/* ============================================================
 *  Sync · student profile + score reporting
 *  ------------------------------------------------------------
 *  Three pieces, all built by js/sync.js at runtime:
 *    .sync-pip      corner status chip (always visible on the map)
 *    .sync-overlay  shared shell for the entry form and the panel
 *  Kept at the end of the sheet so the game can be shipped without
 *  them by simply not loading sync.js.
 * ============================================================ */

/* ---- corner pip ---- */
.sync-pip{
  position:fixed;left:12px;bottom:12px;z-index:120;
  display:flex;align-items:center;gap:7px;
  padding:6px 11px 6px 9px;border-radius:20px;
  background:rgba(255,250,240,.94);
  border:2px solid var(--wood-d);
  box-shadow:var(--shadow);
  font-family:var(--ui);font-size:11.5px;font-weight:700;color:var(--ink);
  cursor:pointer;user-select:none;
  max-width:46vw;overflow:hidden;
  transition:transform .12s,box-shadow .12s;
}
.sync-pip:hover{transform:translateY(-1px);box-shadow:var(--shadow-soft)}
.sync-pip.is-anon{border-color:var(--gold-d);background:rgba(255,247,224,.96)}
.sync-pip .sp-dot{
  width:9px;height:9px;border-radius:50%;flex:none;
  box-shadow:0 0 0 2px rgba(255,255,255,.7) inset;
}
.sync-pip .sp-dot.ok   {background:var(--green)}
.sync-pip .sp-dot.warn {background:var(--gold-d)}
.sync-pip .sp-dot.anon {background:var(--red)}
.sync-pip .sp-txt{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* 只用字号/内边距做小屏压缩，**不要**在这里写 bottom/left。
 * 这条规则排在竖屏媒体查询之后，一旦它写死 bottom:8px;left:8px 就会把
 * 竖屏那份"挪到左侧中部"的定位整个覆盖掉，角标又回到左下角去和地图提示
 * 重叠。定位交给竖屏/横屏各自的分支管。 */
@media (max-width:720px){
  .sync-pip{font-size:10.5px;padding:5px 9px;max-width:56vw}
}

/* ---- 左下角 bgm 按钮 vs 同步角标的避让（横屏/桌面） ----
 * 两者都在左下角：.bgm-btn 是 absolute bottom:14px left:14px，
 * .sync-pip 是 fixed bottom:12px left:12px —— 角标会把音乐按钮整个盖住，
 * 学生点不到切歌。这里让角标抬到 bgm 按钮上方。
 *
 * ⚠ 抬高量必须按**真实**按钮高度算，不能用拍脑袋的 54。
 *   基础 .hud-btn 是 68×68（border-box），bgm 在 bottom:14，
 *   所以它的上沿在 14+68 = 82px 处。角标要真正让开，bottom 至少 82，
 *   再留 8px 视觉间距 → 90。
 *   之前这里写的是 74（按"高 54"推），实测仍然重叠 68×8px。
 *   在 iPad 横屏 / 桌面这种按钮不缩小的机型上必现。
 * 竖屏那份覆写在文件末尾的竖屏区块里（那边按钮是 58 高，值不同）。 */
@media (orientation:landscape), (min-width:721px){
  .sync-pip{bottom:90px}
}

/* ---- shared overlay shell ---- */
.sync-overlay{
  position:fixed;inset:0;z-index:200;display:none;
  align-items:center;justify-content:center;
  background:rgba(26,20,16,.72);
  padding:20px;
  -webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px);
}
.sync-overlay.show{display:flex}
.sync-card{
  width:min(460px,94vw);max-height:92vh;overflow:auto;
  background:var(--paper);
  border:3px solid var(--wood-d);
  border-radius:var(--r-lg);
  box-shadow:var(--shadow-soft), 0 0 0 4px rgba(255,250,240,.35);
  padding:24px 24px 20px;
  text-align:center;
}
.sync-card-narrow{width:min(400px,94vw)}
.sync-card h2{
  font-family:var(--ui);font-size:19px;font-weight:800;color:var(--wood-dd);
  margin-bottom:10px;letter-spacing:.3px;
}
.sync-lead{font-size:12.5px;line-height:1.65;color:#7a6242;margin-bottom:18px}

.sync-field{display:block;text-align:left;margin-bottom:13px}
.sync-field span{
  display:block;font-size:11.5px;font-weight:700;color:var(--wood-d);
  margin-bottom:5px;letter-spacing:.2px;
}
.sync-field span i{font-style:normal;color:#a08a6c;font-weight:600;margin-left:4px}
.sync-field input{
  width:100%;padding:11px 13px;
  font-family:var(--ui);font-size:15px;font-weight:600;color:var(--ink);
  background:var(--cream);
  border:2px solid #d9c9ab;border-radius:var(--r-sm);
  outline:none;transition:border-color .15s,box-shadow .15s;
  user-select:text;
}
.sync-field input:focus{
  border-color:var(--gold-d);
  box-shadow:0 0 0 3px rgba(224,165,46,.22);
}
.sync-err{
  min-height:15px;font-size:11.5px;font-weight:700;color:#c05a48;
  margin:2px 0 10px;text-align:left;
}
.sync-go{
  width:100%;padding:12px 16px;
  font-family:var(--ui);font-size:14px;font-weight:800;color:#fff;
  background:var(--wood);border:none;border-radius:var(--r-sm);
  box-shadow:0 3px 0 var(--wood-dd);
  cursor:pointer;transition:transform .1s,box-shadow .1s,background .15s;
}
.sync-go:hover{background:var(--wood-d)}
.sync-go:active{transform:translateY(2px);box-shadow:0 1px 0 var(--wood-dd)}
.sync-go:disabled{opacity:.6;cursor:default;transform:none}
.sync-alt{
  padding:12px 18px;font-family:var(--ui);font-size:13px;font-weight:700;
  color:var(--wood-d);background:var(--paper2);
  border:2px solid #d9c9ab;border-radius:var(--r-sm);cursor:pointer;
}
.sync-alt:hover{background:#efdcc0}
.sync-foot{font-size:11px;color:#a08a6c;margin-top:13px;line-height:1.6}
.sync-foot a{color:var(--wood-d);font-weight:700}

/* ---- panel bits ---- */
.sync-id{
  display:flex;gap:10px;margin-bottom:14px;text-align:left;
}
.sync-id > div{
  flex:1;background:var(--cream);border:2px solid #e6d6b8;
  border-radius:var(--r-sm);padding:9px 11px;min-width:0;
}
.sync-id span{display:block;font-size:10px;font-weight:700;color:#a08a6c;margin-bottom:3px}
.sync-id b{font-size:14px;color:var(--wood-dd);word-break:break-all}

.sync-stats{display:flex;gap:10px;margin-bottom:14px}
.sync-stats > div{
  flex:1;background:var(--paper2);border:2px solid #e0cba4;
  border-radius:var(--r-sm);padding:11px 6px;
}
.sync-stats span{display:block;font-size:10px;font-weight:700;color:#8a7355;margin-bottom:4px}
.sync-stats b{font-family:var(--pix);font-size:23px;color:var(--wood-dd);line-height:1}
.sync-stats i{font-style:normal;font-size:10.5px;color:#a08a6c;margin-left:2px}

.sync-meta{
  font-size:11px;color:#8a7355;line-height:1.7;
  background:var(--cream);border-radius:var(--r-sm);
  padding:9px 11px;margin-bottom:14px;text-align:left;
  word-break:break-word;
}
.sync-btns{display:flex;gap:10px}
.sync-btns .sync-go{flex:1;width:auto}

/* 极窄屏（≤360px，如 iPhone SE 320px）词书头部放不下。
 * 头部三个子项——标题 80 + 两个书页签 173 + ✕ 36 = 289px，再加两处
 * 14px 间距就是 317px，而 320px 视口扣掉卡片内边距只剩 294px。
 * 结果 ✕ 被压到 17px 宽并推出卡片外（实测 x 311~328，卡片右缘 307）。
 * 解法：允许头部换行，让书页签整块掉到第二行。 */
@media (max-width:360px){
  .overlay-head{flex-wrap:wrap;gap:8px 10px}
  /* 标题占满第一行，把 ✕ 顶到最右 */
  .overlay-head h2{flex:1 1 auto;min-width:0}
  .overlay-head h2 .wb-head-ico{width:22px;height:22px}
  /* 书页签换行后独占一行，允许横向滚动以防更多的书 */
  .wb-tabs{order:3;width:100%;flex:0 0 auto}
  .wb-tab{padding:5px 11px;font-size:11.5px}
  .close-x{width:34px;height:34px;font-size:15px;flex:0 0 auto}
}

/* ============================================================
 *  竖屏适配（收尾覆写）
 *  ------------------------------------------------------------
 *  这些规则必须放在**文件最后**。CSS 里同优先级看的是出现顺序，而
 *  .sync-pip 的定位、.wb-list 的宽度等基础规则都写在文件中后段
 *  （sync 区块在 1690 行以后），若把竖屏覆写放在文件开头的竖屏媒体
 *  查询里，会被那些更靠后的规则覆盖掉 —— 实测就是这样：竖屏角标
 *  仍然回到左下角，和 bgm 按钮重叠 68×26px。
 * ============================================================ */
@media (orientation:portrait) and (max-width:900px){
  /* ---- HUD 按钮尺寸（必须在文件末尾重写一遍）----
   *
   * 文件开头（第 73 行）那份竖屏媒体查询里已经写过
   *   .hud-btn{width:58px;height:58px}
   * 但它**完全没生效** —— 因为 .hud-btn 的基础规则在 244 行，排在它之后，
   * 同特异性下后者赢，按钮实际一直是 68×68。实测确认：计算值 68px。
   *
   * 连带后果（都是实测出来的真实重叠，不是理论推算）：
   *   · 右侧列 settings-btn 的 top 按"12+58+8=78"算，但按钮真高 68，
   *     12+68=80 > 78 → 与 course-btn 重叠 2px；
   *   · 左下角 rotate-btn 的 bottom 按 58 高算，实际 68 高，于是它的
   *     上沿顶进了 sync-pip 的区域 → 实测重叠 68×28，
   *     sync-pip 把 rotate-btn 整个盖住，学生点不到横屏按钮。
   *
   * 这里重写一遍尺寸，后面的定位才成立。这也再次说明本文件的约定：
   * **所有覆写基础规则的规则都必须放在文件末尾。** */
  .hud-btn{width:58px;height:58px}

  /* 左下角竖排：bgm（最底）→ rotate（中）→ sync-pip（上）。
   * 完整纵向账见下面 .map-hint 那段注释。这里只放值：
   *   bgm      bottom 18
   *   rotate   bottom 88   （18+58+12）
   *   sync-pip bottom 158  （88+58+12），在下面单独写 */
  .bgm-btn {bottom:calc(18px + env(safe-area-inset-bottom,0px)); left:12px}
  .rotate-btn{bottom:calc(88px + env(safe-area-inset-bottom,0px)); left:12px; display:flex}

  /* 右侧列：按 58 高重排，间距 8。
   *   course   top 12            → 12~70
   *   settings top 78            → 78~136
   *   practice bottom 88         → 88~146
   *   words    bottom 20         → 20~78
   * 上下两两之间都留了 8px。 */
  .course-btn  {top:calc(12px + env(safe-area-inset-top,0px)); right:12px}
  .settings-btn{top:calc(78px + env(safe-area-inset-top,0px)); right:12px}
  .practice-btn{bottom:calc(88px + env(safe-area-inset-bottom,0px)); right:12px}
  .wordbook-btn{bottom:calc(20px + env(safe-area-inset-bottom,0px)); right:12px}

  /* 底部操作提示：
   * 竖屏左下角是一整列 HUD（自下而上 bgm → rotate → sync-pip），提示必须
   * 坐在**整列之上**，否则又会压到角标。实测就是这么翻车的：提示放
   * bottom:160、角标放 156，两者在 y 658~688 直接叠了 84×26。
   *
   * 这一列的完整纵向账（视口 844，按钮 58 高，角标 30 高）：
   *   bgm      bottom  18 → y 768..826
   *   rotate   bottom  88 → y 698..756   （间距 12）
   *   sync-pip bottom 158 → y 656..686   （间距 12）
   *   ── 角标上沿 656，所以提示的底沿必须在 656 以上 ──
   *   map-hint bottom 196 → y 622..648   （间距 8）
   *
   * 右侧按钮列最下面那个（practice-btn）占 x 308~376，所以提示右缘
   * 不能越过 308；取 right:88px（390-88-12 = 290 右缘）留出余量。
   * 左缘 12，与角标对齐。 */
  .map-hint{
    left:12px;
    right:calc(88px + env(safe-area-inset-right,0px));
    bottom:calc(196px + env(safe-area-inset-bottom,0px));
    transform:none;max-width:none;
    white-space:normal;text-align:center;line-height:1.35;
  }

  /* 同步角标在竖屏的定位也得跟着这一列走（值见上面的纵向账）。 */
  .sync-pip{top:auto;bottom:calc(158px + env(safe-area-inset-bottom,0px));left:12px}

  /* 竖屏纵向有 ~844px（横屏只有 390px），任务面板没必要再抠成 30vw。 */
  #quest-panel{width:min(62vw,320px)}
  #quest-panel.collapsed{max-width:min(70vw,280px)}

  /* 词表在横屏下是 230px 固定宽的侧栏；竖屏卡片整体只有 ~390px 宽，
   * 再并排三列必然把闪卡挤到 ~145px —— 实测单词 crucial 被拆成
   * "crucia / l"、音标被截断、词表条目全变成 "cr… im…"。
   * 竖屏改成**上下堆叠**：闪卡在上、词表在下，各占满整宽。 */
  .wb-body{flex-direction:column;gap:10px;padding:10px 12px}
  .wb-list{width:100%;flex:0 1 auto;max-height:34%;padding:6px}
  .wb-list-item{padding:6px 9px;font-size:12px}
  .wb-list-item .li-cn{font-size:10px;max-width:38%}
  .wb-main{flex:1 1 auto;gap:10px}
  /* 竖屏宽度足够，卡片按比例给高；固定 200px 在只有一行单词时会留白
   * 过多，而单词换行时又不够，用比例更稳。 */
  .flashcard{height:auto;min-height:168px;aspect-ratio:5/3;width:min(100%,420px)}
  .fc-word{font-size:32px;overflow-wrap:anywhere}
  .fc-ph{font-size:14px;margin-top:6px}
  /* 提示条压在 Pronounce 按钮上的根因：提示是 absolute bottom:9px，
   * 而窄卡片里按钮正好也落在那个高度。竖屏下直接藏掉 —— 卡片本身有
   * 明确的翻转按钮，提示是冗余的。 */
  .fc-flip-hint{display:none}
  /* 三个按钮横排一行。只写 nowrap 还不够 —— 按钮自身的 min-content 宽度
   * 之和会超过容器，于是仍被挤成两行。让它们等分收缩（flex:1 1 0）并
   * 允许内部省略号，才能稳定占满一行。 */
  .wb-actions{flex-wrap:nowrap;gap:7px;width:100%}
  .wb-act{
    padding:9px 8px;font-size:12.5px;flex:1 1 0;min-width:0;
    white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
  }
  .wb-shortcuts{display:none}

  /* 有浮层打开时把角标藏起来。
   * .sync-pip 是 z-index:120，而 .overlay 只有 50 —— 角标会浮在词书/
   * 练习面板之上，竖屏下正好压住面板底部的词表最后一行（实测盖住
   * "strained"）。角标本来只在看地图时才有意义，进面板时收起来最干净。
   * 用 :has() 做纯 CSS 判断，不必让 sync.js 去维护 body class。 */
  body:has(.overlay.show) .sync-pip,
  body:has(.sync-overlay.show) .sync-pip,
  body:has(.practice-root:not(.hidden)) .sync-pip,
  body:has(.pr-screen) .sync-pip {opacity:0;pointer-events:none}
}

/* ============================================================
 *  伪横屏（#rotate-btn / .pc-landscape / [data-rotated]）
 * ------------------------------------------------------------
 *  这里必须放在**文件末尾** —— 项目里已经被这条坑过一次：
 *  同等特异性下靠后的规则胜出，凡是覆写 base 规则的媒体查询都得
 *  排在后面（见上方竖屏区块的注释）。伪横屏改的是 #app 的几何，
 *  任何在它后面出现的 #app / .hud-btn 定位规则都会把它顶掉。
 *
 *  三件事必须成立，缺一不可（测试 tests/rotate_check.js 逐条断言）：
 *   1) #app 的盒子尺寸真的变成"视口高 × 视口宽"，旋转后才铺满；
 *   2) 旋转后的实际矩形（getBoundingClientRect）与视口重合 —— 这一步
 *      验证 translateY(-vw) 的轴向选择，而不是靠肉眼；
 *   3) 触摸方向跟着转（在 map.js 里读数 __pcRotate.isOn()）。
 * ============================================================ */

/* 按钮默认藏在左下角 bgm 按钮的**上方**。
 * 为什么不在下方：bottom:14px 那一行是 .bgm-btn 的地盘，两者的
 * 命中矩形会直接叠在一起（同一个 68×68 盒子），下面的按钮点不到。
 * 抬高一个按钮高 + 间距（68+8）即可错开。 */
.rotate-btn{bottom:90px;left:14px;display:none}

/* 只在竖屏窄屏下露出来。
 * 故意用 .pc-landscape 之外的裸媒体查询来显示按钮：这个类只在"已经
 * 旋转"时才有，而按钮恰恰要在"还没旋转"时可见 —— 用类判断会自相矛盾。
 * 竖屏且窄 = 正是需要它的场景；桌面浏览器和已经横过来的手机都不显示。 */
@media (orientation:portrait) and (max-width:900px){
  .rotate-btn{display:flex}
  /* 竖屏下 HUD 按钮缩到 58（见上方竖屏区块），bgm 底部离地 18。
   * 抬高量 = 按钮高 58 + 间距 12 = 70 → 18+70 = 88。
   * 一开始写的是 84（按 58+8 算），实测两个按钮的矩形只差 2px ——
   * 因为 .hud-btn 的盒模型是 **border-box**（*{box-sizing:border-box}），
   * 58px 已经含 3px 边框，所以"视觉间距"只有底边之上那一点点。
   * 直接用 88 留出 12px 实间距，视觉上才分得开、手指也点得准。 */
  /* 竖屏下 HUD 按钮缩到 58×58（基础规则是 68，见文件末尾那段说明）。
   * bgm 底部离地 18，抬高量 = 按钮高 58 + 间距 12 = 70 → 18+70 = 88。
   * 一开始写的是 84（按 58+8 算），实测两个按钮的矩形只差 2px ——
   * 因为 .hud-btn 的盒模型是 **border-box**（*{box-sizing:border-box}），
   * 58px 已经含 3px 边框，所以"视觉间距"只有底边之上那一点点。
   * 直接用 88 留出 12px 实间距，视觉上才分得开、手指也点得准。 */
  .rotate-btn{bottom:calc(88px + env(safe-area-inset-bottom,0px));left:12px}
}
/* 曾经这里有一条 @media (max-width:360px){ .rotate-btn{bottom:74px} }，
 * 想给 iPhone SE 这类极窄屏压缩左下角。**已删除**，因为它算错了：
 * 74 是按"按钮 54 高"推的，但竖屏按钮实际是 58 高，bgm 又在 bottom:18，
 * 于是 18+58=76 > 74 —— rotate-btn 反而嵌进 bgm 里 2px（实测重叠 58×2），
 * 小屏上点它容易误触到音乐开关。
 * 而且它本来就是多余的：上面那条 88px 对所有竖屏宽度都成立，
 * 靠 env(safe-area-inset-*) 已经能适应各种机型。 */
/* 已旋转时的按钮状态：点亮，并换一个更暗的底，表示"点一下回去"。 */
.rotate-btn.is-on{background:linear-gradient(180deg,#ffe9b8,#e8c477);border-color:var(--gold-d)}
.rotate-btn.is-on .hud-label{color:var(--wood-dd)}

/* ---- 旋转本体 ----
 * 尺寸互换由 JS 写内联样式完成（视口尺寸每次都要重算，写死在 CSS 里
 * 转屏后就错了）。CSS 这里只负责两件 JS 不好做的事：
 *   overflow:hidden —— 旋转中途旧尺寸还没被覆盖的一瞬间，元素可能比
 *     视口大，不禁掉会出现滚动条闪一下；
 *   position:fixed 由 base 规则提供（#app 本来就是 fixed inset:0），
 *   不变。
 * 注意：**不要**在这里再写 width/height —— 会被 JS 的内联样式覆盖倒
 * 无所谓，但一旦有内联样式缺失的路径（比如 JS 报错），写死的值就成了
 * 唯一几何来源，反而更难排查。 */
html.pc-landscape #app{overflow:hidden}

/* 旋转后整个视口就是"游戏画面"，把浏览器的滚动/回弹彻底按住。
 * 竖屏时 body 已经是 overflow:hidden，这里再补 touch-action 是因为
 * 旋转后 #app 的盒子比可视区高（视口宽 390 → 盒子高 390，但用户看到的
 * 是 844 宽的画面），拖拽手势容易触发浏览器的下拉刷新。 */
html.pc-landscape,
html.pc-landscape body{overscroll-behavior:none;touch-action:none}
/* 但游戏内部的滚动容器（词表、任务列表、设置面板）必须还能滚 ——
 * touch-action:none 会继承下去把它们一起冻住。逐个放行。 */
html.pc-landscape .wb-list,
html.pc-landscape .overlay-card,
html.pc-landscape #quest-panel,
html.pc-landscape .practice-root,
html.pc-landscape .pc-scroll{touch-action:pan-y}

/* 旋转后 is-on 的按钮标签是 "Portrait"，文案比 "Landscape" 短，
 * 但 58px 宽装 11px 字仍会换行，统一 nowrap。 */
.rotate-btn .hud-label{white-space:nowrap}

/* ---- 伪横屏下的 HUD / 提示修正 ----
 *
 * 关键事实：旋转只改了 #app 的**盒子**尺寸，没有改视口。手机的视口依然
 * 是 390×844，所以 `@media (orientation:portrait)` **照样匹配** ——
 * 上面那一大坨竖屏覆写会继续生效，只不过现在作用在一个
 * 844(宽)×390(高) 的盒子里。
 *
 * 于是原来为"390 宽的屏幕"调好的定位全部错位：
 *   .map-hint 原本 left:74 right:74，在 390 宽的盒子里只剩 242px，
 *   而它是 white-space:nowrap 的桌面文案 → 文字挤成一条 26×744 的
 *   竖带，横穿整个画面（实测截图里那条白带）。
 *   同理 _所有_ 用 left/right/bottom 贴边的 HUD 都会跑到奇怪的位置。
 *
 * 处理办法：在旋转态下**重新定义一次**这套定位，坐标全部按
 * "844 宽 × 390 高"来算。放在文件最末尾 —— 否则又被上面的竖屏规则
 * 盖回去（这个坑项目里已经踩过一次）。
 *
 * 选择器用 html.pc-landscape 提权：这样即使顺序上不占优，
 * 特异性 (0,1,1) 也能压过 `.map-hint` (0,1,0) 这类普通规则。 */

/* 底部操作提示：旋转态下的处理见**文件更靠后**的那条规则
 * （`html.pc-landscape .map-hint{display:none}`）。
 * 这里不重复定义，避免两处规则打架 —— CSS 里同特异性靠后的赢，
 * 留两份等于给未来埋一个"改了上面那份却不生效"的坑。
 *
 * 简版结论：横屏画面里放不下横排长句，所以旋转后直接隐藏。 */

/* 左下角一列（bgm + rotate）：重新按 390 高排布。
 * bgm 在底、rotate 就在它上面，间距 12（用 88 的理由见上面那条注释：
 * border-box 下 58px 已含边框）。 */
html.pc-landscape .bgm-btn{
  bottom:calc(16px + env(safe-area-inset-bottom,0px)); left:14px;
}
html.pc-landscape .rotate-btn{
  bottom:calc(86px + env(safe-area-inset-bottom,0px)); left:14px;
}
/* 同步角标：本来在左下角列的最上面，旋转后同一列继续往上 */
html.pc-landscape .sync-pip{
  top:auto; bottom:calc(156px + env(safe-area-inset-bottom,0px)); left:14px;
}

/* 右侧按钮列：旋转后盒子只有 390 高，两个按钮（Course/Settings 在上，
 * Practice/Words 在下）竖排会挤。改成**贴右缘两列并排**的紧凑布局，
 * 每组 2×2。这样占的高度从 4×68 降到 ~2×68，还给地图中间让出空间。 */
html.pc-landscape .course-btn{
  top:calc(14px + env(safe-area-inset-top,0px)); right:calc(12px + env(safe-area-inset-right,0px));
}
html.pc-landscape .settings-btn{
  top:calc(14px + env(safe-area-inset-top,0px)); right:calc(78px + env(safe-area-inset-right,0px));
}
html.pc-landscape .practice-btn{
  bottom:calc(14px + env(safe-area-inset-bottom,0px)); right:calc(78px + env(safe-area-inset-right,0px));
}
html.pc-landscape .wordbook-btn{
  bottom:calc(14px + env(safe-area-inset-bottom,0px)); right:calc(12px + env(safe-area-inset-right,0px));
}

/* 按钮整体缩一点。旋转后虽然"画面"变宽了，但按钮在屏幕上的**物理**
 * 尺寸没变，四个按钮挤在 390 高的右缘会显得很占地方。 */
html.pc-landscape .hud-btn{width:54px;height:54px}
html.pc-landscape .hud-btn .hud-ico{font-size:20px}
html.pc-landscape .hud-btn .hud-label{font-size:10px}

/* 任务面板：竖屏那边写的是 min(62vw,320px)，用的是**视口**宽 390 → 242px。
 * 旋转后真正可用的宽度是 844，按 62vw 只有 242 太窄了。给一个绝对值。 */
html.pc-landscape #quest-panel{width:min(38vw,320px)}
html.pc-landscape #quest-panel.collapsed{width:auto;max-width:min(46vw,270px)}

/* 词书：竖屏是上下堆叠（闪卡上、词表下，词表 max-height:34%）。
 * 旋转后盒子变成 844×390，"上下堆叠"就变成两块都很扁 —— 词表只剩
 * 132px 高，一次看不到几条。旋转后恢复成**左右分栏**，词表回到侧栏。 */
html.pc-landscape .wb-body{flex-direction:row;gap:12px;padding:12px 16px}
html.pc-landscape .wb-list{width:230px;flex:0 0 230px;max-height:none;padding:6px}
html.pc-landscape .wb-main{flex:1 1 auto}
html.pc-landscape .flashcard{width:min(100%,420px);height:auto;min-height:150px;aspect-ratio:5/3}
html.pc-landscape .wb-actions{flex-wrap:wrap}
html.pc-landscape .wb-act{flex:0 1 auto;min-width:0}

/* ---- HUD 不反向旋转：让它和地图一起转 ----
 *
 * 【为什么这里曾经写的是 rotate(-90deg)，现在删掉了】
 * 原实现给每个按钮反向转 -90°，用意是让按钮文字"正过来"。但那是**只
 * 盯着按钮、在竖屏原始帧里看**得出的结论 —— 把地图放在一起看，它就
 * 自相矛盾了：
 *
 *   · 地图（含画在图里的楼名）只受父级 +90° 影响 → 在竖屏原始帧里是
 *     躺倒的，**用户必须把手机横过来**才能正着读它。
 *   · 按钮再额外 -90°（父 +90 ∘ 子 -90 = 0）→ 在竖屏原始帧里就是正的，
 *     用户**把手机横过来之后反而变成歪的**。
 *
 * 也就是说：同一屏里，"地图要用户转手机"和"按钮不要用户转手机"两个
 * 假设打架，无论怎么拿手机，总有一半是歪的。这是原实现没发现的 bug
 * （作者只单独截了按钮来看，没和地图对照）。
 *
 * 【现在的选择】和用户确认过：**点完按钮后把手机横过来看**。这是最自然
 * 的用法，也和"横屏"的字面意思一致。既然要用户转手机，那就让所有内容
 * 一起转 —— 按钮不再反向旋转，跟地图保持同一个朝向。
 *
 * 【客观验证】用 getComputedStyle 累加祖先 transform 实测：
 *   修改前 按钮文字最终角 = 0°（竖屏帧里正、横过来歪）
 *   修改后 按钮文字最终角 = +90°（与楼名一致，横过来两者都正）
 * 这条断言已加进 tests/rotate_check.js 的 [E] 组，防止有人再"好心"
 * 把 -90° 加回来。 */
/* 按钮不反向旋转后，:active 的 translateY 就没有坐标系冲突了 ——
 * 但旋转态下 translateY 的方向已随父级转到"屏幕横向"，视觉上仍是横向
 * 位移。所以按下反馈统一改成缩放，竖屏/横屏行为一致。 */
html.pc-landscape .hud-btn:active{
  transform:scale(.94);
}

/* ---- 底部操作提示：旋转态下**藏起来** ----
 *
 * 这里有个绕不过去的几何矛盾，记下来免得以后又有人去"修"：
 *   用户在旋转后看到的画面是 844 宽 × 390 高，但手机屏幕物理上只有
 *   390 宽。一条要在画面里"横着读"的长句提示，如果给它反向旋转让文字
 *   正过来，它就会横跨屏幕的 390 —— 而这条提示有 644 宽（20 个中文字
 *   在 11px 下），必然溢出屏幕左右（实测 rect 左缘 -237）。
 *   不反向旋转的话，它在屏幕上变成一根 21×644 的竖条，白白横穿地图。
 *
 * 两条路都不行，是因为"横屏画面里的横排长句"在物理 390 宽的屏幕上
 * 本来就放不下。所以直接不显示。
 *
 * 代价可以接受：这条提示只在第一次上手时有用，而学生能点到 Landscape
 * 按钮就说明他已经在操作了。竖屏那份提示保留了。 */
html.pc-landscape .map-hint{
  display:none;
}
