/* Word Summit · 词山学海 · shared stylesheet (v0.2)
   BVSS palette: 晴空 sky, 旭日 sun, 青山 mountain, 碧海 sea.
   Landscape-first: iPads with keyboards and Chromebooks are the primary
   targets; portrait phones remain a functional fallback. */
:root{
  --sky1:#DFEDF7; --sky2:#BAD6E8;
  --sun-core:#FFEDA8; --sun:#F5C443;
  --mtn-haze:#A9C9B1; --mtn-far:#9CC0A6; --mtn-mid:#79AB89; --mtn-mid2:#4F8560;
  --mtn-front:#57906B; --mtn-deep:#2F5A3F; --ridge:#3F704F; --shade:#274A34;
  --sea1:#2E6391; --sea2:#1F4A70; --sea3:#173A5A;
  --ink:#243B4A; --ink-soft:#5A7080; --gold:#E3A63C;--gold-ink:#8A5A0F; --gold-deep:#B7791F;
  --card:rgba(255,255,255,.92); --card-line:#C9DCE9;
  /* ---- liquid glass ----
     Panels float ON the painted backdrops instead of covering them: a
     translucent white plate + a real backdrop blur + a bright top hairline
     that reads as the lit edge of a pane. Inner controls are translucent too,
     or the card looks like glass with opaque stickers on it.
     Alphas are deliberately high (.55-.72): these sit over busy painterly art
     on school Chromebooks, and legibility beats the effect. A @supports
     fallback near the bottom of this file raises them where there is no blur. */
  --glass:rgba(255,255,255,.60); --glass-strong:rgba(255,255,255,.72);
  --glass-soft:rgba(255,255,255,.40); --glass-line:rgba(255,255,255,.65);
  --glass-line-2:rgba(150,185,212,.45);
  --glass-blur:saturate(165%) blur(20px);
  --glass-lift:0 10px 34px rgba(23,58,90,.20);
  --glass-edge:inset 0 1px 0 rgba(255,255,255,.85), inset 0 -1px 0 rgba(255,255,255,.30);
  --ok:#3E7D57; --bad:#B44A3C;
  --serif:"Noto Serif SC","Songti SC","STSong",serif;
  --sans:"Noto Sans SC","PingFang SC","Microsoft YaHei",sans-serif;
}
*{margin:0;padding:0;box-sizing:border-box}
html,body{height:100%}
body{font-family:var(--sans);color:var(--ink);line-height:1.6;
  background:linear-gradient(180deg,#EAF4FA 0%,var(--sky1) 32%,var(--sky2) 58%,var(--sea1) 88%,var(--sea3) 100%);
  background-attachment:fixed;min-height:100vh}
button{font-family:inherit;cursor:pointer;border:none;background:none;color:inherit}
input{font-family:inherit}
img{max-width:100%}

/* ---------- horizon scene ---------- */
.horizon{position:relative;overflow:hidden}
.horizon .sun{position:absolute;left:70%;transform:translateX(-50%);border-radius:50%;
  background:radial-gradient(circle,var(--sun-core) 20%,var(--sun) 66%,rgba(245,196,67,0) 78%);
  box-shadow:0 0 60px rgba(245,196,67,.55)}
.horizon svg{display:block;position:absolute;left:0;right:0;bottom:0;width:100%}

/* ---------- landing ---------- */
.hero{height:min(72vh,560px);min-height:420px}
.hero .sun{width:clamp(96px,14vw,150px);height:clamp(96px,14vw,150px);top:16%}
.hero-title{position:absolute;top:9%;left:0;right:0;text-align:center;z-index:5}
.hero-title .zh{font-family:var(--serif);font-weight:900;font-size:clamp(34px,6vw,52px);letter-spacing:.16em;text-shadow:0 2px 18px rgba(255,255,255,.85)}
.hero-title .en{font-size:clamp(9px,1.4vw,12px);letter-spacing:.55em;color:#3A5A70;margin-top:8px;text-transform:uppercase}
.stream-grid{position:relative;z-index:6;max-width:860px;margin:-64px auto 0;padding:0 18px 56px;
  display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:14px}
.stream-card{display:block;text-decoration:none;color:var(--ink);background:var(--card);
  border:1px solid var(--card-line);border-radius:16px;padding:18px 16px;text-align:center;
  box-shadow:0 12px 30px rgba(23,58,90,.22);transition:transform .15s ease, box-shadow .15s ease}
.stream-card:hover{transform:translateY(-3px);box-shadow:0 18px 40px rgba(23,58,90,.3)}
.stream-card b{display:block;font-family:var(--serif);font-size:19px;letter-spacing:.08em}
.stream-card span{font-size:12px;color:var(--ink-soft)}
.stream-card .tagline{display:block;margin-top:7px;font-size:11.5px;color:var(--gold-ink)}
.landing-foot{position:relative;z-index:6;text-align:center;color:#DCEAF4;font-size:11.5px;padding:0 20px 34px;text-shadow:0 1px 6px rgba(23,58,90,.5)}

/* ---------- app shell ---------- */
/* ⚠️ NO PANEL (owner 2026-08-22). This was a full-width glass slab with a border
   and a drop shadow — the single heaviest piece of chrome on the page, and it read as
   a document header rather than as a game. The sea map has never had one: it floats a
   返回首页 pill, two aid pills, and hangs each name on its own plaque over the art.
   The bar is now just a layout row; every child carries its own glass.
   ⚠️ pointer-events:none on the ROW. It is sticky and z-50, so a transparent
   full-width strip would still swallow every click landing in the gaps between the
   controls — including on cards scrolled underneath it. Each real control switches
   pointer-events back on; the gaps stay dead.
   ⚠️ Keep the .topbar / .xh-top pair in step (§17 deliberate duplication). */
.topbar{position:sticky;top:0;z-index:50;display:flex;align-items:center;gap:12px;
  padding:12px 20px;background:none;border-bottom:0;box-shadow:none;
  -webkit-backdrop-filter:none;backdrop-filter:none;pointer-events:none}
.topbar .back,.topbar .tb-id,.topbar .tb-right>*{pointer-events:auto}
/* ⚠️ NO hard edge anywhere in this gradient — it fades to zero alpha at the
   bottom. A border-bottom or a box-shadow here would put the bar straight back.
   ⚠️ pointer-events:none: it spans the full width, and the row above it is only
   click-through because nothing in it catches events. */
.topbar::before{content:"";position:absolute;left:0;right:0;top:0;
  height:calc(100% + 30px);pointer-events:none;opacity:0;transition:opacity .2s;
  background:linear-gradient(180deg,rgba(244,250,253,.94),rgba(244,250,253,.66) 52%,rgba(244,250,253,0))}
.topbar.scrolled::before{opacity:1}
@media (prefers-reduced-motion:reduce){.topbar::before{transition:none}}

/* ⚠️ THE LABEL THAT HAD TO SURVIVE (owner: 「we just have to ensure that the labels
   stay visible (G1/2/3/hcl etc)」). It gets a plaque rather than bare type with a
   shadow: this sits directly on applyAmbience()'s photo backdrop, and §7 is explicit
   that on bright painted art the answer is dark ink on glass, never light type on the
   picture. It hugs its text, so it reads as a floating label and not as a bar. */
.topbar .tb-id{display:flex;flex-direction:column;justify-content:center;min-width:0;
  padding:6px 15px;border-radius:15px;background:rgba(255,255,255,.74);
  border:1px solid var(--glass-line);
  -webkit-backdrop-filter:var(--glass-blur);backdrop-filter:var(--glass-blur);
  box-shadow:var(--glass-edge),0 3px 12px rgba(23,58,90,.15)}
.topbar .back{width:36px;height:36px;border-radius:50%;background:var(--glass);border:1px solid var(--glass-line);
  -webkit-backdrop-filter:var(--glass-blur);backdrop-filter:var(--glass-blur);
  box-shadow:var(--glass-edge),0 2px 8px rgba(23,58,90,.14);
  color:#3A5A70;font-size:16px;flex:none}
/* ⚠️ 学段名永远是一行（owner 2026-08-23：「the subject level is way too narrow and
   tall」）。这块名牌是 flex 行里唯一可缩的东西（右边四颗控件 flex:none），所以在手机上
   它被压到 32px 宽，五个字**一个字一行**竖着排下来，顶栏高 394px——比内容还高。
   ⚠️ nowrap + ellipsis 是兜底，不是修法：真正的修法是下面那两档 media query 把
   副标与几个词标签让出去。兜底留着，是因为下一个被加进顶栏的控件会再抢一次宽度，
   而那时候的正确行为是**名字被截断**，不是名字变成一根竖条。 */
.topbar .tb-name{font-family:var(--serif);font-weight:700;font-size:18px;letter-spacing:.06em;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.topbar .tb-sub{font-size:11.5px;color:var(--ink-soft);letter-spacing:.06em;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* ⚠️ 名牌上的拼音与英文同样一行到底。它们是 flex column 的子项，一旦可以换行，
   这块名牌的 min-content 宽度就掉到一个字，右边四颗控件会把它压成一根竖条——
   码头上试过一次，量出来是 22px 宽、118px 高。 */
.topbar .tb-id .pylab,.topbar .tb-id .enlab{white-space:nowrap;overflow:hidden;
  text-overflow:ellipsis;margin:0}
.topbar .tb-right{margin-left:auto;font-size:12.5px;color:var(--ink-soft);
  display:flex;align-items:center;flex:none;min-width:0}
.wrapper{max-width:1200px;margin:0 auto;padding:18px 22px 70px}
.loading{color:#EAF4FA;text-align:center;padding:60px 0}
.beta-chip{position:fixed;right:12px;bottom:10px;z-index:60;font-size:10.5px;color:#DCEAF4;
  background:rgba(18,42,66,.7);border:1px solid rgba(255,255,255,.25);border-radius:999px;padding:4px 12px}
.error-box{background:var(--card);border:1px solid var(--card-line);border-radius:14px;padding:20px;max-width:520px;margin:40px auto}

/* ---------- toast ---------- */
.toast{position:fixed;left:50%;top:18px;transform:translateX(-50%) translateY(-80px);z-index:200;
  background:rgba(18,42,66,.94);color:#EAF4FA;border-radius:999px;padding:10px 22px;font-size:14px;
  box-shadow:0 10px 30px rgba(0,0,0,.3);transition:transform .25s ease;pointer-events:none;max-width:90vw;text-align:center}
.toast.show{transform:translateX(-50%) translateY(0)}

/* ---------- shared bits ---------- */
/* Section headers sit DIRECTLY on the painted ambience (bg-01..05 / the bamboo
   backdrop), not on a card — so light grey + wide tracking was unreadable on the
   bright ones. Same fix as .ach-hint and the 我的词山 HUD: dark ink on a glass
   pill. inline-block so the pill hugs its own text instead of striping the row;
   the 英文提示 gloss sits inside it, which reads as one label rather than two. */
.section-label{display:inline-block;font-size:12.5px;font-weight:700;letter-spacing:.2em;
  color:var(--ink);margin:0 2px 10px;padding:6px 15px;border-radius:999px;
  background:rgba(255,255,255,.78);border:1px solid var(--glass-line);
  -webkit-backdrop-filter:var(--glass-blur);backdrop-filter:var(--glass-blur);
  box-shadow:var(--glass-edge),0 3px 12px rgba(23,58,90,.14)}
/* ⚠️ A section label that follows a card needs its own top gap (owner 2026-08-16
   evening: ②选择学习方式 sat on the bottom edge of ①复习范围). .card carries no
   bottom margin and .section-label carried no top margin, so the two glass edges
   met at 0px — and the card's drop shadow reaches ~8px past its border, which
   reads as the pill overlapping the card. 18px clears the shadow. */
.card + .section-label{margin-top:18px}
.card{background:linear-gradient(150deg,rgba(255,255,255,.74),rgba(255,255,255,.56) 46%,rgba(232,243,251,.60));
  -webkit-backdrop-filter:var(--glass-blur);backdrop-filter:var(--glass-blur);
  border:1px solid var(--glass-line);border-radius:18px;
  padding:16px 18px;box-shadow:var(--glass-lift),var(--glass-edge)}
.tts{display:inline-flex;align-items:center;gap:6px;font-size:14px;background:var(--sea1);color:#fff;
  border-radius:999px;padding:8px 18px;box-shadow:0 4px 12px rgba(23,58,90,.25);transition:transform .12s;
  position:relative;touch-action:manipulation}
.tts:active{transform:scale(.95)}
/* label + its 拼音/English glosses stack as one flex item */
.tts-lab{display:inline-block;text-align:left;line-height:1.25}
/* ⚠️ THIS PAIR IS SET IN THE DARK-PLATE BLOCK FURTHER DOWN, NOT HERE. A bare
   `.tts-lab .pylab` is (0,2,0) and loses to `body.py-aid .pylab` (0,2,0, and later
   in the file), so the light colour written here never applied and the 🔊 button's
   两行注解 rendered dark gold and dark grey on a navy pill — owner 2026-08-16 晚,
   「G1/G2 的蓝色按钮里拼音和英文很难看清」. Same trap as §18e at the pier. */
.tts.sm{font-size:12.5px;padding:5px 13px}
/* invisible hit-area padding: keeps the visual pill size, widens what the finger hits */
.tts::after{content:"";position:absolute;inset:-8px}
.rail-tts{margin-top:14px}

/* ---------- home ---------- */
.home-grid{display:grid;grid-template-columns:1fr;gap:22px;align-items:start}
.mini-horizon{border-radius:18px;height:230px;margin-bottom:16px;overflow:hidden;
  background:url("../art/seamap/sea_tile.png") left top/cover no-repeat,
    linear-gradient(180deg,#EAF4FA 0%,var(--sky1) 45%,var(--sky2) 100%)}
.mini-horizon .sun{width:60px;height:60px;top:20px}
.mini-horizon .app-zh{position:absolute;z-index:5;left:0;right:0;top:32%;text-align:center;
  font-family:var(--serif);font-weight:900;font-size:30px;letter-spacing:.14em;color:#fff;
  text-shadow:0 2px 10px rgba(12,30,48,.85),0 1px 3px rgba(12,30,48,.9)}
.scope-level{font-size:13.5px;font-weight:700;color:#3A5A70;margin:10px 0 7px}
.units{display:flex;flex-wrap:wrap;gap:8px}
.unit{border:1px solid #B9CEDD;border-radius:14px;padding:7px 13px;font-size:14px;color:var(--ink-soft);background:#fff;
  display:inline-flex;flex-direction:column;align-items:flex-start;line-height:1.25;gap:1px}
.unit b{font-size:14px;color:var(--ink)}
.unit-theme{font-size:11.5px;color:#8AA0B0}
.unit-n{font-size:11px;color:var(--ink-soft)}
.unit.on{background:var(--gold);border-color:var(--gold)}
.unit.on b,.unit.on .unit-n{color:#3A2A08}
.unit.on .unit-theme{color:#6B5010}
/* #8A5A0F not var(--gold-deep): the token is 3.19:1 on a card, which passes only
   for large text. These small labels need 4.5:1 (measured 5.19:1). */
.scope-sum{font-size:13px;color:#8A5A0F;font-weight:700;min-width:0}
/* ONE header row: 已选 N 个单元 left, 全选/清空 pinned right (owner 2026-08-16
   evening). The pair used to own a row of its own, which cost a full line inside a
   fixed-height box. ⚠️ It is a normal flex child, NOT position:absolute — the card
   scrolls inside itself in landscape, and an absolute corner would scroll away. */
.scope-top{display:flex;align-items:center;justify-content:space-between;gap:10px;
  flex-wrap:wrap;margin-bottom:8px}
.scope-acts{display:flex;gap:6px;margin-left:auto;flex:none}
.scope-acts .unit{font-weight:700;padding:5px 12px;font-size:13px}
.scope-acts .unit b{font-size:13px}
.scope-acc{display:flex;align-items:center;width:100%;gap:8px;background:none;border:none;border-top:1px solid #E4EDF4;padding:11px 2px 8px;font-size:14px;font-weight:700;color:#3A5A70;cursor:pointer;text-align:left}
.scope-acc .cnt{font-weight:400;font-size:12.5px;color:var(--ink-soft)}
.scope-acc .chev{margin-left:auto;color:#4E6273;transition:transform .18s}
.scope-acc.open .chev{transform:rotate(90deg)}
.units.collapsed{display:none}

.home-search{padding:10px 12px;margin-bottom:16px}
/* .hs-* (in-stream search card) removed 2026-08-16 — one 查词 pill in the topbar now */
/* ---------- ② 的两扇门 (owner 2026-08-23) ----------
   .htabs/.htab（一对切换标签）已删除，不是改样式：它们的「选中态」正是学生把 ②
   读成切换的原因，留着那套 class 会让下一个画面顺手把它捡回去。
   ⚠️ 门是玻璃卡不是金色药丸：金色在这套设计里表示「你选中了它」（.unit.on ·
   .prof-chip.on · 榜单上自己那一行），一扇门不该长成一个已选中的东西。
   金色只留给 › ——那是「这里通往别处」的记号。 */
.hdoors{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-bottom:14px}
@media (max-width:420px){.hdoors{grid-template-columns:1fr}}
/* ⚠️ 门与它后面那一页的活动卡是**同一种材质**：`--card` 92% 配 `--card-line`，
   和 .camp 逐字相同。第一版给门做了一层 74% 的玻璃渐变，结果同一条动线上两级
   长得不一样，而且压在 ambience 照片上偏灰。92% 是这个平台「卡片」的既定值。 */
.hdoor{display:flex;align-items:center;gap:clamp(10px,1.1vw,16px);width:100%;text-align:left;
  min-width:0;padding:clamp(14px,1.7vw,24px) clamp(14px,1.5vw,20px);border-radius:18px;cursor:pointer;
  background:var(--card);border:1px solid var(--card-line);
  box-shadow:0 6px 20px rgba(23,58,90,.14);transition:transform .12s ease}
.hdoor:hover{transform:translateY(-2px)}
.hdoor:active{transform:scale(.985)}
/* ⚠️ 图标是**按视口缩放的**（owner 2026-08-23:「the emojis can be much bigger to
   illustrate its function and also have the panes respond dynamically to screen
   size」）。写死 46px 的方块在 1440 的笔电上和在 390 的手机上一样大，于是笔电上一整片
   空地配一颗小图标。⚠️ clamp 的下限保护手机，上限保护它不会在投影仪上变成一张海报。
   ⚠️ 宽高必须同一个 clamp：只写宽度，图标格子会被 place-items 拉成椭圆。 */
/* ⚠️ 门坐在**一条窄栏里**，不是整页上：clamp 的上限因此比门后那一页的活动卡小得多。
   第一版给它 4.4vw（1280 上 56px），码头那条更窄的鲁上就把「学习」挤成 学／习 两行。
   真正大的图标在门后面那一页（.path-wrap .camp .flag），那里才有整页宽度。 */
.hd-ic{width:clamp(44px,3.2vw,58px);height:clamp(44px,3.2vw,58px);flex:none;border-radius:16px;
  display:grid;place-items:center;font-size:clamp(23px,1.9vw,31px);
  background:#FDF3DC;border:1px solid #EFD9A4}
/* ⚠️ min-width:0 —— 门是 grid 子项里的 flex 子项，两层默认都不肯缩（§14），
   开着英文注解的 闯关 会把两栏挤成不等宽。 */
.hd-t{min-width:0;flex:1 1 auto}
/* ⚠️ 门的名字**永远一行**：学习／闯关 各两个字，一旦允许换行，窄栏里就是 学／习
   竖着排——与顶栏名牌那根竖条是同一个病（§14 flex 子项不肯缩）。 */
.hd-t b{display:block;font-family:var(--serif);font-weight:900;font-size:clamp(19px,1.4vw,24px);
  line-height:1.25;color:var(--ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.hd-go{flex:none;font-size:clamp(20px,1.8vw,26px);font-weight:700;color:var(--gold-ink)}

/* ---------- 门后面那一页 ---------- */
/* ⚠️ 单栏并且有宽度上限：首页是「我要做什么 ‖ 我走到哪了」两栏，这一页只有一个
   决定，摊到 1400px 宽会让两张卡长得像两条横杠。 */
.path-wrap{max-width:min(980px,94vw);margin:0 auto;padding-top:clamp(0px,3vh,36px)}
/* ⚠️ 大屏上这一页要**居中站着**，不是吊在顶上。它只有一个标题加两到四张卡，
   而笔电上那是三分之一屏内容配三分之二屏空地——读起来像页面没加载完。
   ⚠️ 只在又宽又高的视口上开：手机与矮横屏（≤700px 高）要的是内容立刻在手边，
   min-height 会把它推到折线以下。 */
@media (min-width:900px) and (min-height:700px){
  .path-wrap{min-height:calc(100vh - 215px);display:flex;flex-direction:column;
    justify-content:center;padding-bottom:clamp(0px,4vh,48px)}
  /* ⚠️ 药丸在 flex column 里会被拉成一整条横杠：column 的默认 align-items 是
     stretch，而 .section-label 的 inline-block 挡不住它（§14，guide.html 的
     .num 药丸踩过同一条）。 */
  .path-wrap>.section-label{align-self:flex-start}
}
.path-head{display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin-bottom:16px}
.path-ic{font-size:30px;line-height:1}
.path-t{min-width:0}
.path-t b{display:block;font-family:var(--serif);font-weight:900;font-size:26px;line-height:1.2;color:var(--ink);
  text-shadow:0 1px 0 rgba(255,255,255,.75)}
/* ⚠️ 这一行压在 ambience 照片上，所以它必须是**玻璃上的深墨字**，不是带阴影的裸字
   （§7）。同一条规矩把 .section-label 做成药丸。 */
.path-scope{margin-left:auto;display:flex;align-items:center;gap:10px;flex-wrap:wrap;
  padding:8px 14px;border-radius:999px;cursor:pointer;
  background:rgba(255,255,255,.80);border:1px solid var(--glass-line);
  -webkit-backdrop-filter:var(--glass-blur);backdrop-filter:var(--glass-blur);
  box-shadow:var(--glass-edge),0 3px 12px rgba(23,58,90,.14)}
.ps-sum{font-size:13px;font-weight:700;color:var(--ink)}
/* 共 0 词 是这一页最该被看见的一句话：每一张卡按下去都会弹一个「范围是空的」。 */
.ps-sum.zero{color:#A6431F}
.ps-go{font-size:13px;font-weight:800;color:var(--gold-ink)}
/* 卡片在这一页要大，而且**跟着屏幕长**：这里只有两到四张，它们是整页唯一的动作，
   而这一页在笔电上有大半屏是空的（owner 2026-08-23）。图标按视口缩放，卡片内距也是。
   ⚠️ 仍然是 auto-fit + minmax：学习 两张、闯关 三到四张（G3/HCL 有 词语汉兜），
   列数写死就会在某一个学段上落单。 */
/* ⚠️ 两列，写死（owner 2026-08-23:「make them all a grid of 2 columns」）。
   auto-fit 会按宽度装：闯关 在 1440 上排成三列一行，与 学习 那两张卡不是同一个版面，
   而它们是同一条动线上的同一层。三个玩法的学段因此是 2 + 1，最后一张落在左边——
   这是 owner 选的取舍，不是溢出。 */
.path-wrap .camps{grid-template-columns:repeat(2,minmax(0,1fr));
  gap:clamp(12px,1.4vw,20px)}
@media (max-width:560px){.path-wrap .camps{grid-template-columns:1fr}}
.path-wrap .camp{padding:clamp(16px,2vw,28px) clamp(14px,1.5vw,20px);
  gap:clamp(11px,1.2vw,18px);min-height:clamp(92px,16vh,168px)}
.path-wrap .camp .flag{width:clamp(52px,5.2vw,82px);height:clamp(52px,5.2vw,82px);
  font-size:clamp(26px,2.9vw,44px);border-radius:16px}
.path-wrap .camp b{font-size:clamp(17px,1.3vw,22px)}

/* Games sit in ONE row (owner 2026-08-14). auto-fit + a min track means 3 or 4
   cards share the row on a PLD and wrap gracefully on a phone, without the count
   being hardcoded — the stream decides how many modes exist (G1/G2 get 组词,
   G3/HCL get 汉兜). Dropping 出发 › is what buys the width to do this. */
.camps{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:10px}
.camp{display:flex;align-items:center;gap:11px;background:var(--card);border:1px solid var(--card-line);
  border-radius:16px;padding:13px 13px;width:100%;text-align:left;min-width:0;
  box-shadow:0 6px 20px rgba(23,58,90,.14);transition:transform .12s ease}
.camp:hover{transform:translateY(-2px)}
.camp:active{transform:scale(.985)}
.camp .flag{width:40px;height:40px;border-radius:12px;display:grid;place-items:center;font-size:21px;background:#FDF3DC;border:1px solid #EFD9A4;flex:none}
.camp b{font-size:15.5px;display:block;line-height:1.3}
.camp>div{min-width:0}
.camp span{font-size:12.5px;color:var(--ink-soft)}
.camp .go{margin-left:auto;font-size:13px;color:var(--gold-ink);font-weight:700;flex:none}

/* 成就徽章 + 我的词语表 + 词山风云榜 in ONE row (owner 2026-08-14). The badge
   strip needs more width than the other two because it carries the badge art, so
   this is a 3-track grid weighted 1.4/1/1 rather than three equal columns. */
/* ⚠️ auto-fit, NOT a fixed column count (owner 2026-08-16 evening:「this section is
   janky, can they collectively take up the entire right column with the contents
   centered」). It was `1.4fr 1fr 1fr` from when THREE entries lived here; 我的词语表
   has since moved into the ① scope card, so the third track stayed declared and
   empty and the two survivors huddled on the left. auto-fit makes whatever is
   present share the full width, so removing or adding an entry can never strand a
   dead column again. */
.home-entries{display:grid;grid-template-columns:repeat(auto-fit,minmax(210px,1fr));gap:10px;margin-top:14px}
.home-entries>*{margin-top:0;justify-content:center;text-align:center}
/* caption UNDER the art, never beside it (owner 2026-08-16): side-by-side, HCL's five
   36px badges left the note ~24px of card and it wrapped one character per line. */
.badge-strip{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:7px;margin-top:0;
  background:var(--card);
  border:1px solid var(--card-line);border-radius:16px;padding:11px 13px;width:100%;text-align:center;
  min-width:0;box-shadow:0 6px 20px rgba(23,58,90,.14);transition:transform .12s}
.badge-strip:hover{transform:translateY(-2px)}
.badge-chips{display:flex;gap:8px;align-items:center;justify-content:center;flex-wrap:wrap;min-width:0;width:100%}
.badge-chip{width:36px;height:36px;border-radius:50%;overflow:hidden;flex:none;background:#fff;
  border:2px solid var(--gold);box-shadow:0 3px 10px rgba(23,58,90,.18)}
.badge-chip img{width:100%;height:100%;object-fit:cover;display:block}
.badge-chip.locked{border-color:#C9DCE9}
.badge-chip.locked img{filter:grayscale(1) opacity(.45)}
.badge-note{font-size:12px;color:var(--ink-soft);text-align:center;line-height:1.4;
  flex:0 0 auto;min-width:0;max-width:100%}
/* HUD/stat glosses must never widen their pill — wrap under, never push out */
body.en-aid .harbour .enlab.i,body.en-aid .sprint-hud .enlab.i,body.en-aid .rain-hud .enlab.i,
body.en-aid .streak .enlab.i,body.en-aid .rail-pts .enlab.i,body.en-aid .comp-lab .enlab.i{
  display:block;margin:1px 0 0}

.harbour{background:rgba(18,42,66,.82);border:1px solid rgba(255,255,255,.2);border-radius:16px;
  padding:15px 22px;display:flex;gap:28px;color:#EAF4FA;margin-top:14px;justify-content:center;flex-wrap:wrap}
.harbour div{text-align:center}
.harbour div b{display:block;font-family:var(--serif);font-size:24px;color:var(--sun)}
.harbour div span{font-size:11px;opacity:.8;letter-spacing:.06em}
.home-foot{text-align:center;font-size:10.5px;color:#DCEAF4;text-shadow:0 1px 6px rgba(23,58,90,.5);margin-top:12px}

/* ---------- study layout: rail + stage ---------- */
/* ⚠️ owner 2026-08-23：「can we make the panes sit lower on the screen? currently
   across all levels it seems to be sitting high instead of being visually balanced」。
   一屏问答的内容通常比视口矮一大截，靠 .wrapper 那 18px 的上内距顶在天花板上，
   底下留一大片空山——重心全在上面。

   做法：给 .wrapper 一个占满视口的最小高度，再用 **auto 外距**把内容推到垂直居中。
   ⚠️ 用 `margin:auto` 而不是 `justify-content:center`：内容一旦比视口高，
   center 会把顶部**切掉且滚不回去**（flex 那个经典毛病），auto 外距不会。
   ⚠️ 只作用于 .study（一屏一题的那几块）。首页与成就墙本来就比视口长，
   给它们居中只会在短屏上把顶部推走。
   ⚠️ 用 dvh 不是 vh：手机浏览器的地址栏会伸缩，vh 在 iOS 上量的是最大高度，
   收起地址栏时内容会被顶出屏幕。100dvh 跟着可视区域走；不支持 dvh 的旧浏览器
   走上一行的 vh 兜底。
   ⚠️ `:has()` 在旧 Safari／旧 Chrome 上不支持——**那正是想要的降级**：
   整条规则被忽略，版面回到改之前的顶部对齐，不会错位。学校那些老 iPad
   因此最多是「没居中」，不会是「坏掉」。 */
.wrapper:has(>.study){min-height:calc(100vh - 92px);min-height:calc(100dvh - 92px);
  display:flex;flex-direction:column}
.wrapper>.study{margin-top:auto;margin-bottom:auto;width:100%}
.study{display:grid;grid-template-columns:1fr;gap:20px;align-items:start}
.rail .mode-name{font-family:var(--serif);font-weight:900;font-size:24px;letter-spacing:.1em;margin-bottom:2px}
.rail .mode-desc{font-size:13px;color:var(--ink-soft);margin-bottom:12px;line-height:1.6}
/* one instruction LINE plus its 拼音/English glosses (mdLine) */
.md-line+.md-line{margin-top:5px}
/* 词雨 best-score line: label+value together, glosses beneath each item */
.rain-best{display:flex;flex-wrap:wrap;gap:6px 18px;align-items:flex-start}
.rb-item{display:inline-block}
.md-line .pylab,.md-line .enlab{margin-top:1px}
/* the q-tag pill wraps to fit its glosses instead of clipping them */
.q-tag .pylab,.q-tag .enlab{letter-spacing:.06em;text-transform:none}
.prog-big{font-family:var(--serif);font-size:32px;color:var(--gold-deep);font-weight:900}
.prog-big small{font-size:15px;color:var(--ink-soft);font-weight:400}
.prog-track{height:8px;border-radius:99px;background:#D8E4EC;overflow:hidden;margin:8px 0 14px}
.prog-fill{height:100%;background:linear-gradient(90deg,var(--gold),var(--gold-deep));border-radius:99px;transition:width .3s ease}
.streak{display:flex;align-items:center;gap:8px;font-size:14px;color:var(--ink-soft)}
.streak b{font-family:var(--serif);font-size:21px;color:var(--bad)}

.diff-label{font-size:11.5px;letter-spacing:.24em;color:var(--ink-soft);margin:16px 0 8px}
/* 并列一排 (owner 2026-08-14): 题型 / 题目类型 / 出题方式 carry two to five
   characters each, so a full-width column wasted most of the panel. They wrap
   naturally when a label is long or the panel is narrow. */
.diff{display:flex;flex-wrap:wrap;gap:8px}
.dopt{display:flex;align-items:center;justify-content:center;gap:8px;flex:1 1 auto;
  border:1px solid var(--glass-line);border-radius:13px;
  padding:11px 15px;font-size:14.5px;background:rgba(255,255,255,.62);text-align:center;color:var(--ink);
  -webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.8),0 2px 8px rgba(23,58,90,.09);
  transition:background .15s ease,border-color .15s ease;touch-action:manipulation}
.dopt:hover{background:rgba(255,255,255,.8)}
.dopt .stars{color:var(--gold-ink);letter-spacing:.5px;font-size:13px;flex:none}
.dopt.on{border-color:rgba(46,99,145,.55);background:rgba(214,233,246,.72);font-weight:700;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.8),0 3px 12px rgba(23,58,90,.16)}

.q-card{background:var(--card);border:1px solid var(--card-line);border-radius:18px;
  padding:24px 26px;box-shadow:0 10px 26px rgba(23,58,90,.14);margin-bottom:16px}
.q-tag{display:inline-block;font-size:11.5px;letter-spacing:.22em;background:#FDF3DC;border-radius:18px;padding:4px 13px;color:#8A6A2E;margin-bottom:12px}
.q-text{font-size:24px;line-height:1.85}
.q-text u{text-decoration:none;border-bottom:3px solid var(--gold);padding:0 26px}
/* D2b 拼音辅助 · 句子/释义注音。<rt> needs vertical headroom or the reading
   clips into the line above, so the annotated line-height is raised.
   ⚠️ owner 2026-08-23：注音现在**永远画进 DOM**，看不看得见由这里决定。
   以前是 qHtml() 在输出时判断，于是切换拼音必须**重画整道题**——而 填空挑战
   那次重画发生在学生答完之后，把反馈、正确答案的高亮和 下一题 一起抹掉了。
   .has-py 因此也变成常驻，它要的那个行高才是跟着开关走的那一半：
   关掉时行高必须退回 1.85，不然会留下一条为看不见的注音撑开的空行。 */
.q-text.has-py,.sq-prompt.has-py{line-height:1.85}
body.py-aid .q-text.has-py,body.py-aid .sq-prompt.has-py{line-height:2.5}
/* ⚠️ ruby 只由 rubyText() 产生（qHtml 是唯一调用点），所以这条全局规则不会误伤别处。 */
body:not(.py-aid) rt{display:none}
/* a syllable is wider than the hanzi it sits on, so neighbouring readings run
   together without a little side room (…为提供 -> weitígōng). */
ruby{ruby-align:center;margin:0 .07em}
rt{font-size:.42em;font-weight:400;letter-spacing:0;line-height:1.15;
  padding:0 .18em;color:var(--ink-soft);-webkit-user-select:none;user-select:none}
.q-foot{display:flex;align-items:center;gap:12px;margin-top:14px}

.opts{display:grid;grid-template-columns:1fr;gap:11px}
.opt{background:#fff;border:1.5px solid var(--card-line);border-radius:14px;padding:15px 18px;font-size:19px;text-align:left;
  display:flex;align-items:center;gap:12px;box-shadow:0 4px 14px rgba(23,58,90,.1);transition:transform .1s;color:var(--ink);
  touch-action:manipulation}
.opt:hover{transform:translateY(-1px);border-color:var(--sea1)}
.opt .letter{width:29px;height:29px;border-radius:50%;background:#EAF2F8;color:#3A5A70;font-size:13.5px;display:grid;place-items:center;flex:none;font-weight:700}
.opt .py{font-size:13px;color:var(--ink-soft);margin-left:10px}
/* ---- 内容拼音的两个闸门（owner 2026-08-23，见 cs.js 的 pyAnsOn()）----
   在这之前，选项拼音是在**输出的时候**用 JS 判断的，答案反馈根本没判断：
   学生答到一半关掉拼音，屏幕上已经画好的那些照旧带着拼音。
   现在两处都永远输出，看不看得见由这里决定——切换只是 <body> 上翻一个 class，
   一道正在作答的题因此**不必重画**（重画就是泄题）。
   ⚠️ 两个闸门不能合并：.py-ans 在 HCL 上是开的（高级华文没有拼音按钮，
   合并会静默拿掉 HCL 的答案拼音，那不是 owner 要的）。 */
/* ⚠️⚠️ 元素闸门的类名与 <body> 的状态类名**必须不同**，这不是风格问题。
   2026-08-23 我把两者都叫 py-ans：<body> 因为 applyPyAid() 拿到 py-ans，
   而裸的 `.py-ans{display:none}` 同样命中 <body> —— 于是 **body 自己被隐藏了**。
   高级华文 的 pyAnsOn() 恒为真（那一科没有拼音按钮），所以 HCL 对所有人全白；
   G1/G2/G3 只要学生**把拼音打开**也会全白。
   ⚠️ 一行 JS 错误都不会有，控制台干干净净——这也是它躲过所有排查的原因。
   状态类：body.py-aid · body.py-ans（JS 写）
   闸门类：.py-gate · .py-ans-gate（标记写）—— 两组名字永不重叠。 */
.py-gate{display:none}
body.py-aid .py-gate{display:inline}
.py-ans-gate{display:none}
body.py-ans .py-ans-gate{display:inline}
.opt.right{background:#E8F3EC;border-color:var(--ok);color:#25513A}
.opt.right .letter{background:var(--ok);color:#fff}
.opt.wrong{background:#F9ECE9;border-color:var(--bad);color:#7A3228}
.opt.wrong .letter{background:var(--bad);color:#fff}
/* speaker is a SIBLING of the answer button (opt-row), not nested inside it — a
   near-miss tap now lands in the gap and does nothing, instead of submitting
   that option as the answer. Shared by all 3 MCQ surfaces (opt/sopt alike). */
.opt-row{display:flex;align-items:stretch;gap:10px}
.opt-row .opt,.opt-row .sopt{flex:1;min-width:0}
.opt-row .opt-tts{flex:none;align-self:center;margin-left:0}
.opt-tts{position:relative;width:31px;height:31px;border-radius:50%;display:grid;place-items:center;
  background:#EAF2F8;font-size:14px;cursor:pointer;border:0;touch-action:manipulation}
.opt-tts:hover{background:#DCEAF4}
.opt-tts::after{content:"";position:absolute;inset:-8px}

.answer-row{display:flex;gap:10px}
.answer-input{flex:1;border:1.5px solid #B9CEDD;border-radius:13px;padding:13px 16px;font-size:17px;background:#fff;color:var(--ink);min-width:0}
.answer-input:focus{outline:none;border-color:var(--sea1)}
.answer-input.shake{animation:shake .3s;border-color:var(--bad);background:#F9ECE9}
@keyframes shake{0%,100%{transform:translateX(0)}25%{transform:translateX(-5px)}75%{transform:translateX(5px)}}
.check-btn{background:var(--gold);color:#3A2A08;border-radius:13px;padding:0 24px;font-size:16px;font-weight:700;flex:none;touch-action:manipulation}
.hint-btn{margin-top:10px;font-size:13px;color:var(--sea1);text-decoration:underline;touch-action:manipulation}
.feedback{margin-top:14px;font-size:16px;border-radius:14px;padding:13px 17px;display:none;line-height:1.7}
.feedback.show{display:block}
.feedback.ok{background:#E8F3EC;color:#25513A;border:1px solid #BFDCCB}
.feedback.bad{background:#F9ECE9;color:#7A3228;border:1px solid #E3C0B8}
.nav-row{display:flex;gap:10px;margin-top:16px;flex-wrap:wrap}
.nav-btn{flex:1 1 116px;border:1px solid var(--glass-line);border-radius:13px;padding:13px;font-size:15px;
  background:rgba(255,255,255,.62);color:#3A5A70;
  -webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.85),0 2px 10px rgba(23,58,90,.12);touch-action:manipulation}
.nav-btn.primary{background:linear-gradient(180deg,#EEBC5E,var(--gold));border-color:rgba(183,121,31,.55);color:#3A2A08;font-weight:700;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.55),0 4px 14px rgba(227,166,60,.38)}
.nav-btn:disabled{opacity:.5;cursor:default}

/* ---------- flashcards ---------- */
.flash-stage{display:flex;align-items:center;gap:16px}
.arrow{width:52px;height:52px;border-radius:50%;background:rgba(255,255,255,.85);border:1px solid #B9CEDD;
  color:#3A5A70;font-size:21px;flex:none;box-shadow:0 6px 16px rgba(23,58,90,.18);display:none}
.flashcard{flex:1;background:var(--card);border:1px solid var(--card-line);border-radius:22px;
  min-height:380px;display:flex;flex-direction:column;align-items:center;justify-content:center;
  text-align:center;padding:32px 26px;box-shadow:0 14px 34px rgba(23,58,90,.16);cursor:pointer;user-select:none}
.flashcard .w{font-family:var(--serif);font-weight:900;font-size:62px;letter-spacing:.06em}
.flashcard .w-row{display:flex;align-items:center;justify-content:center;gap:14px}
.flashcard .w-row .tts{flex:none}
.flashcard .back-w{font-size:46px}
.flashcard .py{font-size:21px;color:#3A5A70;margin-top:8px;letter-spacing:.04em}
.flashcard .back-py{font-size:17px}
.flashcard .pos{display:inline-block;margin-top:12px;font-size:12.5px;background:#FDF3DC;border:1px solid #EFD9A4;color:#8A6A2E;border-radius:999px;padding:3px 13px}
.zh-row{display:flex;align-items:center;justify-content:center;gap:12px;margin-top:14px;flex-wrap:wrap}
.flashcard .zh{font-size:20px;line-height:1.75;max-width:620px}
.flashcard .en{font-size:15px;color:var(--ink-soft);margin-top:10px}
.flashcard .hinttap{font-size:11.5px;color:#9FB3C0;margin-top:22px;letter-spacing:.2em}

/* ---------- result ---------- */
.result{max-width:520px;margin:40px auto 0;text-align:center;background:var(--card);
  border:1px solid var(--card-line);border-radius:20px;padding:36px 28px;box-shadow:0 14px 34px rgba(23,58,90,.16)}
.result .big{font-family:var(--serif);font-weight:900;font-size:52px;color:var(--gold-deep)}
.result .sub{font-size:16px;color:var(--ink-soft);margin-top:4px}
.result .msg{font-family:var(--serif);font-size:17px;margin-top:16px}
/* ---------- 年度试炼的庆祝 (owner 2026-09-05) ----------
   ⚠️ 天幕是 `position:fixed` 且 `pointer-events:none`：彩带要从整个屏幕顶上落下来，
   而挂在结果卡里就成了「一张卡里下的一场雪」；不写 pointer-events 它会吃掉
   底下每一颗按钮的点击（顶栏就为同一件事踩过一次，§18ai）。
   ⚠️ 舞台用 width + height 定尺寸，**不用 `aspect-ratio`**：那是 Safari 15 才有的，
   而 G1/G2 学生的主力机型正是更旧的 iPad（§7）。
   ⚠️ 神兽用真美术（320×320 显示成 168px，仍是缩小，踩不到 §14 的像素画缩放陷阱）。 */
.cel-sky{position:fixed;inset:0;pointer-events:none;z-index:60}
.cel-stage{position:relative;width:min(232px,62vw);height:min(232px,62vw);
  margin:2px auto 12px;display:flex;align-items:center;justify-content:center}
.cel-rays{position:absolute;inset:-6%;border-radius:50%;
  background:repeating-conic-gradient(from 0deg,rgba(245,196,67,.32) 0deg 9deg,rgba(245,196,67,0) 9deg 22deg);
  -webkit-mask-image:radial-gradient(circle,#000 34%,rgba(0,0,0,.5) 58%,transparent 74%);
  mask-image:radial-gradient(circle,#000 34%,rgba(0,0,0,.5) 58%,transparent 74%);
  animation:cel-spin 26s linear infinite}
.cel-ring{position:absolute;width:73%;height:73%;border-radius:50%;
  background:radial-gradient(circle at 50% 42%,#FFF7E2 0%,#FDEAB9 54%,#F5C443 100%);
  box-shadow:0 10px 26px rgba(199,140,26,.34),inset 0 0 0 3px rgba(255,255,255,.72);
  animation:cel-pulse 2.6s ease-in-out infinite}
.cel-pet{position:relative;width:62%;height:auto;image-rendering:pixelated;
  filter:drop-shadow(0 8px 12px rgba(23,58,90,.28));
  animation:cel-pop .72s cubic-bezier(.16,1.16,.3,1) both,cel-float 3.4s ease-in-out 1s infinite}
@keyframes cel-spin{to{transform:rotate(360deg)}}
@keyframes cel-pulse{0%,100%{transform:scale(1)}50%{transform:scale(1.045)}}
@keyframes cel-pop{0%{opacity:0;transform:scale(.36) rotate(-12deg)}
  60%{opacity:1;transform:scale(1.10) rotate(2deg)}
  100%{opacity:1;transform:scale(1) rotate(0deg)}}
@keyframes cel-float{0%,100%{transform:translateY(0)}50%{transform:translateY(-7px)}}
.cel-msg b{color:var(--gold-ink)}
/* ⚠️ 静止时也必须看得见那只神兽——只去掉动作，不去掉东西（§7） */
@media (prefers-reduced-motion:reduce){
  .cel-rays,.cel-ring,.cel-pet{animation:none}
}

/* ---------- achievements ---------- */
.ach-wrap{max-width:900px;margin:0 auto}
.ach-level{margin-bottom:22px}
.ach-level-head{display:flex;align-items:center;gap:12px;margin-bottom:10px}
.ach-level-head b{font-family:var(--serif);font-size:22px;letter-spacing:.1em;color:#fff;text-shadow:0 1px 8px rgba(23,58,90,.6)}
.ach-seal{font-size:12px;background:#FDF3DC;border:1px solid #EFD9A4;color:#8A6A2E;border-radius:999px;padding:2px 12px;font-weight:700}
.ach-unit{margin-bottom:12px}
.ach-unit-name{font-size:15px;font-weight:700;color:#3A5A70;display:flex;align-items:center;gap:12px;margin-bottom:12px}
.ach-badges{display:flex;gap:18px;flex-wrap:wrap}
/* a glass pill, not white text: the wall sits on bright sky art where plain
   light type on the backdrop is unreadable */
.ach-hint{display:inline-block;font-size:12.5px;color:var(--ink);margin:-4px 2px 14px;
  background:rgba(255,255,255,.6);border:1px solid var(--glass-line);border-radius:999px;padding:5px 14px;
  -webkit-backdrop-filter:var(--glass-blur);backdrop-filter:var(--glass-blur);
  box-shadow:var(--glass-edge),0 3px 12px rgba(23,58,90,.14)}
/* the whole tile is the tap target for the badge detail card */
.ach-badge{display:flex;flex-direction:column;align-items:center;gap:4px;width:104px;text-align:center;
  background:none;border:none;padding:4px 2px;border-radius:14px;cursor:pointer;
  touch-action:manipulation;transition:transform .14s ease,background .14s ease}
.ach-badge:hover{transform:translateY(-2px);background:rgba(255,255,255,.35)}
.ach-badge:active{transform:scale(.97)}
/* repeat count: BESIDE the name, never over the art (badge spec) */
.ach-times{display:inline-block;margin-left:5px;font-size:11px;font-weight:800;color:#7A5B12;
  background:#FDF3DC;border:1px solid #EFD9A4;border-radius:999px;padding:0 7px;vertical-align:1px}
.ach-badge img{width:72px;height:72px;border-radius:50%;object-fit:cover;border:2.5px solid var(--gold);box-shadow:0 5px 14px rgba(23,58,90,.2);background:#fff}
.ach-badge.locked img{filter:grayscale(1) opacity(.4);border-color:#C9DCE9}
.ach-badge-name{font-size:12.5px;font-weight:700}
.ach-badge.locked .ach-badge-name{color:#9FB3C0}
.ach-badge-title{font-size:10px;color:#5A7080;line-height:1.25;max-width:104px}
.ach-badge.locked .ach-badge-title{color:#B4C4D0}
/* 意见反馈 floating button — bottom-LEFT, the gov.sg widget's placement.
   z-index 55 sits under .pop-overlay (60) and the arena overlay (90), so it can
   never float over a dialog or a live room. */
.fb-fab{position:fixed;left:16px;bottom:16px;z-index:55;display:flex;align-items:center;gap:7px;
  padding:10px 15px;border-radius:999px;cursor:pointer;touch-action:manipulation;
  border:1px solid var(--glass-line);background:rgba(255,255,255,.82);color:var(--ink);
  font-size:13.5px;font-weight:700;font-family:inherit;
  -webkit-backdrop-filter:var(--glass-blur);backdrop-filter:var(--glass-blur);
  box-shadow:var(--glass-edge),0 6px 18px rgba(23,58,90,.22)}
.fb-fab:hover{transform:translateY(-2px)}
.fb-fab:active{transform:scale(.96)}
.fb-fab-icon{font-size:16px;line-height:1}
/* phones: shrink to the icon alone so it never covers content */
@media (max-width:560px){
  .fb-fab{left:12px;bottom:12px;padding:0;width:44px;height:44px;justify-content:center;gap:0}
  .fb-fab-txt{display:none}
}
@media print{.fb-fab{display:none}}
/* the question a report is attached to */
.fb-ctx{margin:8px 0 2px;padding:9px 13px;border-radius:12px;font-size:13px;line-height:1.6;
  background:rgba(255,247,228,.85);border:1px solid rgba(227,166,60,.5);color:#6B4A05}
.fb-ctx b{font-weight:800}
.fb-ta{min-height:110px}

/* ---------- 板块 filter row (2026-08-14) ----------
   One compact row above the level accordions. Deliberately small type and tight
   chips: this narrows an existing selection, it is not the primary control. */
/* ⚠️ GRID, not wrapping flex, now that the chips carry art (owner 2026-08-16 evening).
   As flex items they size to their own text, so 「生活空间 / Everyday life」 came out
   104px and 「核心 / Core」 66px, the line broke after the first chip, and four chips
   took three ragged rows. A grid gives every 板块 the same box and packs them evenly.
   `stretch` levels each row to its tallest chip: only 生活空间 wraps its English. */
.comp-row{display:grid;grid-template-columns:repeat(auto-fill,minmax(92px,1fr));
  gap:6px;align-items:stretch;margin:2px 0 10px}
.comp-lab{grid-column:1 / -1;font-size:12px;font-weight:700;color:var(--ink-soft)}
/* four 板块 (G2 · G3) sit 2x2, never 3 + 1 — see the note in cs.js renderHome() */
.comp-row.n4{grid-template-columns:repeat(2,minmax(0,1fr))}
/* 徽章 + 小字 (owner 2026-08-16 evening). The chip is now a little COLUMN: the same
   medallion the 成就墙 uses on top, the 板块 name under it.
   ⚠️ The <img> needs BOTH `align-self:center` and `object-fit:contain` — as a plain
   flex child it inherits `stretch` and the medallion comes out as a tall oval
   (the same trap as the 我的词语表 cover, §14). */
.comp-chip{display:flex;flex-direction:column;align-items:center;gap:3px;
  border:1.5px solid #C4D5E2;background:rgba(255,255,255,.6);color:#7C8B98;
  border-radius:14px;padding:6px 8px 7px;font-size:12.5px;font-weight:600;cursor:pointer;
  min-width:0;touch-action:manipulation;
  transition:background .12s,color .12s,border-color .12s}
.comp-chip-img{width:28px;height:28px;flex:none;align-self:center;object-fit:contain;
  display:block;border-radius:50%;filter:grayscale(1) opacity(.45);transition:filter .12s}
.comp-chip.on .comp-chip-img{filter:drop-shadow(0 1px 3px rgba(23,58,90,.28))}
.comp-chip-lab{display:block;line-height:1.25;text-align:center}
.comp-chip.on{background:linear-gradient(180deg,#FBEBC4,#F2D89A);border-color:var(--gold);color:#6B4A05}
.comp-chip:active{transform:scale(.96)}
/* .lv-acts / .lv-act (全选本级 · 清空本级) removed 2026-08-16 evening — see the
   matching note in cs.js renderHome(). */

/* ---------- 数量滑杆 (2026-08-14, redesigned same day) ----------
   The readout is ABOVE the track, not beside it: as a flex sibling it changed the
   track's length every time the label's width changed, which made the bar look
   like it was stretching and shrinking as you dragged. Track is now a fixed
   full-width row. NO touch-action:manipulation — a range needs the drag gesture. */
.qty{margin:2px 0 6px}
.qty-val{display:block;font-family:var(--serif);font-weight:800;font-size:17.5px;
  color:#8A5A0F;line-height:1.35;margin:0 0 6px;min-height:22px}
.qty-track{position:relative;padding:9px 0}
.qty-ticks{position:absolute;left:13px;right:13px;top:50%;transform:translateY(-50%);
  display:flex;justify-content:space-between;pointer-events:none}
.qty-ticks i{width:3px;height:3px;border-radius:50%;background:rgba(23,58,90,.3)}
.qty-range{-webkit-appearance:none;appearance:none;display:block;width:100%;height:26px;
  background:transparent;cursor:pointer;margin:0}
.qty-range:focus{outline:none}
.qty-range:focus-visible{outline:2px solid var(--gold);outline-offset:3px;border-radius:8px}
.qty-range::-webkit-slider-runnable-track{height:8px;border-radius:999px;
  background:linear-gradient(90deg,rgba(227,166,60,.75),rgba(180,205,225,.7));
  border:1px solid var(--glass-line)}
.qty-range::-moz-range-track{height:8px;border-radius:999px;
  background:linear-gradient(90deg,rgba(227,166,60,.75),rgba(180,205,225,.7));
  border:1px solid var(--glass-line)}
/* 30px thumb: the whole control's tap target on a PLD, so it clears the same
   finger-size bar the 可及性 pass set for the answer buttons */
.qty-range::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:30px;height:30px;
  margin-top:-12px;border-radius:50%;background:linear-gradient(180deg,#FFF6E0,#F5C443);
  border:2.5px solid var(--gold-deep);box-shadow:0 3px 10px rgba(23,58,90,.32)}
.qty-range::-moz-range-thumb{width:30px;height:30px;border-radius:50%;
  background:linear-gradient(180deg,#FFF6E0,#F5C443);
  border:2.5px solid var(--gold-deep);box-shadow:0 3px 10px rgba(23,58,90,.32)}

.ach-badge-count{font-size:11px;color:var(--ink-soft)}
/* 对战徽章 block on 成就墙: its own labelled section AFTER the whole A层 ladder,
   so the five 里程碑徽章 keep the top of the wall to themselves (doc §7). */
.ach-sec{margin-top:26px}
.ach-fam-note{display:block;font-size:11.5px;font-weight:400;color:var(--ink-soft);margin-top:2px}
/* battle art is a transparent circular cutout, so contain (never cover — a
   crop would cut the outer ring, which the badge spec forbids) */
.ach-badge[data-bf] img{object-fit:contain}
/* ---------- 徽章详情卡 (badge detail, opened from 成就墙) ----------
   The art is shown LARGE and uncropped, with nothing drawn over it; the
   locked state is the same grayscale filter the wall uses. */
.bd-card{text-align:center}
.bd-art{width:150px;height:150px;margin:2px auto 12px;border-radius:50%;
  border:3px solid var(--gold);background:rgba(255,255,255,.75);overflow:hidden;
  box-shadow:0 10px 30px rgba(23,58,90,.26),inset 0 1px 0 rgba(255,255,255,.9)}
.bd-art img{width:100%;height:100%;object-fit:contain;display:block}
.bd-art.locked{border-color:#C9DCE9}
.bd-art.locked img{filter:grayscale(1) opacity(.42)}
/* 去挑战 mode picker (2026-08-14): the student chooses how to practise a 板块.
   The 点亮徽章 tag marks the one mode that actually confers mastery. */
.bd-modes{margin-top:14px;text-align:left}
.bd-modes-lab{font-size:12.5px;font-weight:700;color:var(--ink-soft);margin-bottom:7px}
.bd-mode{display:inline-flex;align-items:center;gap:6px;margin:0 6px 6px 0;padding:8px 13px;
  border-radius:999px;border:1.5px solid #C4D5E2;background:rgba(255,255,255,.7);
  color:#5A7080;font-size:13px;font-weight:600;cursor:pointer;touch-action:manipulation}
.bd-mode.on{background:linear-gradient(180deg,#FBEBC4,#F2D89A);border-color:var(--gold);color:#6B4A05}
.bd-mode-tag{font-size:10px;font-weight:800;background:#FFF;border:1px solid #EFD9A4;
  color:#7A5B12;border-radius:999px;padding:1px 6px}
.bd-name{font-family:var(--serif);font-weight:900;font-size:24px;letter-spacing:.08em;color:var(--ink)}
.bd-where{font-size:12.5px;color:var(--ink-soft);line-height:1.6;margin-top:3px}
.bd-earned{display:inline-block;margin-top:10px;font-size:13.5px;font-weight:700;color:#7A5B12;
  background:linear-gradient(180deg,#FDF3DC,#F7E4B4);border:1px solid #EFD9A4;border-radius:999px;padding:4px 16px}
.bd-locked{display:inline-block;margin-top:10px;font-size:13.5px;font-weight:700;color:#6A8296;
  background:rgba(255,255,255,.6);border:1px solid var(--glass-line);border-radius:999px;padding:4px 16px}
.bd-date{font-size:12.5px;color:var(--ink-soft);line-height:1.7;margin-top:6px}
.bd-prog{margin:14px 0 10px;font-size:12.5px;color:var(--ink-soft)}
.bd-prog-track{height:8px;border-radius:99px;background:rgba(216,228,236,.8);overflow:hidden;margin-bottom:6px}
.bd-prog-fill{height:100%;background:linear-gradient(90deg,var(--gold),var(--gold-deep));border-radius:99px}
.bd-words{display:flex;flex-wrap:wrap;gap:6px;justify-content:center;max-height:132px;overflow:auto;
  padding:10px;border-radius:12px;background:rgba(255,255,255,.42);border:1px solid var(--glass-line)}
.bd-word{font-size:13px;color:#6A8296;background:rgba(255,255,255,.7);border:1px solid var(--glass-line);
  border-radius:8px;padding:2px 9px}
.bd-word.done{color:#3A2A08;background:#FDF3DC;border-color:#EFD9A4;font-weight:700}
.ach-t4{text-align:center;margin:26px auto 10px;max-width:520px;background:rgba(18,42,66,.82);
  color:#DCEAF4;border:1px solid rgba(255,255,255,.25);border-radius:16px;padding:16px;font-size:15px}
.ach-t4.got{background:linear-gradient(135deg,#8A6A2E,#E3A63C);color:#fff;font-weight:700}

/* ---------- celebrations ---------- */
.cel-overlay{position:fixed;inset:0;z-index:300;background:rgba(10,26,42,.72);display:none;
  align-items:center;justify-content:center;padding:1.2rem}
.cel-card{width:100%;max-width:400px;border-radius:20px;overflow:hidden;background:#fff;
  box-shadow:0 16px 60px rgba(0,0,0,.5);text-align:center;animation:cel-drop .35s cubic-bezier(.22,1,.36,1)}
@keyframes cel-drop{from{opacity:0;transform:translateY(32px) scale(.96)}to{opacity:1;transform:translateY(0) scale(1)}}
.cel-band{height:6px;background:linear-gradient(90deg,var(--gold),var(--sun),var(--gold))}
.cel-body{padding:28px 26px 24px}
.cel-img{width:110px;height:110px;border-radius:50%;object-fit:cover;border:3px solid var(--gold);
  box-shadow:0 8px 24px rgba(23,58,90,.3);margin-bottom:12px;background:#fff}
.cel-emoji{font-size:64px;line-height:1;margin-bottom:10px}
.cel-title{font-family:var(--serif);font-weight:900;font-size:23px;letter-spacing:.08em}
.cel-sub{font-size:13.5px;color:var(--gold-ink);font-weight:700;margin-top:3px}
.cel-quote{font-family:var(--serif);font-size:16px;margin:15px 0 3px;color:#3A5A70;line-height:1.8}
.cel-src{font-size:12px;color:#9FB3C0}
.cel-btn{margin-top:18px;background:var(--gold);color:#3A2A08;border-radius:12px;padding:12px 34px;font-size:16px;font-weight:700}
.cel-card.t2{background:#1E3A5C;color:#EAF4FA}
.cel-card.t2 .cel-title{color:#fff}
.cel-card.t2 .cel-quote{color:#BAD6E8}
.cel-card.t3{background:#14202E;color:#EAF4FA}
.cel-card.t3 .cel-band{background:linear-gradient(90deg,#E3A63C,#fff,#E3A63C);background-size:200% 100%;animation:shimmer 2.2s linear infinite}
@keyframes shimmer{to{background-position:200% 0}}
.cel-card.t3 .cel-title{color:var(--sun)}
.cel-card.t3 .cel-quote{color:#BAD6E8}
.cel-card.t4{background:linear-gradient(160deg,#6B4E12,#B7791F 55%,#E3A63C);color:#fff}
.cel-card.t4 .cel-title,.cel-card.t4 .cel-quote{color:#fff}
.cel-card.t4 .cel-btn{background:#fff;color:#8A6A2E}

/* ---------- game config ---------- */
.game-config{max-width:560px;margin:24px auto 0}
.game-config .mode-name{font-family:var(--serif);font-weight:900;font-size:26px;letter-spacing:.08em}
.game-config .mode-desc{font-size:14px;color:var(--ink-soft);margin:6px 0 4px;line-height:1.7}
.rain-best{margin-top:16px;font-size:14px;color:var(--gold-ink);font-weight:700}

/* ---------- 词雨 ---------- */
.rain-shell{display:flex;flex-direction:column;gap:10px;height:calc(100vh - 130px);min-height:420px}
.rain-hud{display:flex;align-items:center;gap:18px;background:rgba(18,42,66,.85);color:#EAF4FA;
  border-radius:14px;padding:10px 18px;font-size:14px;flex-wrap:wrap}
.rain-hud b{font-family:var(--serif);font-size:19px;color:var(--sun)}
.rain-hud .nav-btn{flex:none}
.rain-area{position:relative;flex:1;overflow:hidden;border-radius:16px;
  background:linear-gradient(180deg,rgba(234,244,250,.55) 0%,rgba(186,214,232,.5) 70%,rgba(46,99,145,.55) 100%);
  border:1px solid rgba(255,255,255,.5)}
.rain-area{background-image:url("../art/bg/rain_bg.png"),linear-gradient(180deg,rgba(234,244,250,.55),rgba(46,99,145,.55));
  background-size:cover;background-position:center bottom;image-rendering:pixelated}
/* 接雨的角色 (owner 2026-08-16): stands on the sea line and runs after the drop
   closest to landing. z-index above the sea, below the falling words so a word is
   never hidden behind it. */
/* ⚠️ `* var(--av-k)` is the per-sheet size correction set inline from profile.js
   SPRITE_SCALE — the sheets are not drawn to a common size inside their cells, so
   without it 沙僧 renders at roughly half 鼠's visual size (owner 2026-08-17). The
   base clamp stays a plain number; the correction rides on top of it. */
.rain-runner{position:absolute;left:0;bottom:40px;
  height:calc(clamp(56px,9vh,88px) * var(--av-k,1));aspect-ratio:1/1;
  background-repeat:no-repeat;background-size:600% 100%;background-position:0 0;
  image-rendering:pixelated;pointer-events:none;z-index:2;
  filter:drop-shadow(0 4px 9px rgba(6,26,48,.5));transition:none}
.rain-sea{opacity:.35}
.rainfx{position:absolute;background-repeat:no-repeat;image-rendering:pixelated;pointer-events:none;z-index:3;
  background-image:url(data:image/png;base64,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)}
.rain-flash{position:absolute;inset:0;background:rgba(255,253,235,.5);z-index:2;pointer-events:none;
  animation:rainflash .3s ease-out forwards}
@keyframes rainflash{from{opacity:1}to{opacity:0}}
.rain-sea{position:absolute;left:0;right:0;bottom:0;height:40px;
  background:linear-gradient(180deg,rgba(46,99,145,.85),rgba(23,58,90,.95));border-top:2px solid rgba(255,255,255,.4)}
.rain-word{position:absolute;left:0;top:0;background:var(--card);border:1.5px solid var(--card-line);
  border-radius:12px;padding:7px 13px;box-shadow:0 6px 16px rgba(23,58,90,.2);text-align:center;will-change:transform}
.rain-word .rw{display:block;font-family:var(--serif);font-weight:700;font-size:21px;letter-spacing:.05em}
.rain-word .rp{display:block;font-size:11px;color:var(--ink-soft)}
.rain-word.pop{transition:transform .2s,opacity .2s;opacity:0;transform:scale(1.5)!important}
/* U-4: on phones, make falling words bigger + easier to read */
@media (max-width:480px){
  .rain-word{padding:8px 14px}
  .rain-word .rw{font-size:27px}
  .rain-word .rp{font-size:13px}
}
.rain-input-row{display:flex;gap:10px}
/* ---------- 同伴挑战 PK ---------- */
.mtn-pk{background:rgba(180,74,60,.9)}
.pk-scope{background:#F1F6FB;border:1px solid #DBE7F1;border-radius:12px;padding:10px 14px;
  margin:10px 0 4px;font-size:14px;color:var(--ink)}
.pk-scope b{font-family:var(--serif);color:var(--gold-ink);font-size:17px}
.pk-scope-note{display:block;font-size:12px;color:var(--ink-soft);margin-top:4px;line-height:1.6}
/* ---- 词雨 landscape split (DESIGN_peer_pk_duel §3, 2026-08-14) ----
   Falling words left, HUD + room code + typing input right. The input used to
   run full width UNDER the falling area and take vertical space from it; giving
   it its own column hands that height back to the words, and gives the PK room
   code a permanent home that is not a banner over the gameplay.
   Applies to SOLO 词雨 too — one visual language across modes. */
.rain-right{display:flex;flex-direction:column;gap:10px}
.rain-code{background:rgba(18,42,66,.85);color:#EAF4FA;border-radius:12px;
  padding:7px 14px;font-size:13px;letter-spacing:.06em;text-align:center;flex:none}
.rain-code b{font-family:ui-monospace,Menlo,Consolas,monospace;font-size:19px;
  color:var(--sun);letter-spacing:.14em;margin-left:6px}
@media (min-width:900px) and (orientation:landscape){
  .rain-shell{flex-direction:row;align-items:stretch;gap:14px}
  /* min-width:0 for the same reason the sprint split needs it: a flex item
     defaults to min-width:auto and refuses to shrink below its content, which
     pushed .sprint-right off-screen when that layout shipped untested. */
  /* NOT a 50/50 split (owner 2026-08-14): the falling area still owns most of
     the screen — the point of the split is only to get the typing field OUT from
     underneath it, so the words get that vertical space back. The right column
     is sized to what the input actually needs, not to a share of the width. */
  .rain-area{flex:1 1 auto;min-width:0}
  .rain-right{flex:0 0 clamp(190px,23%,290px);min-width:0;justify-content:flex-start}
  .rain-hud{flex-wrap:wrap;gap:8px 14px;padding:9px 12px;font-size:13px}
  .rain-right .rain-input-row{flex-direction:column}
  .rain-right .rain-input-row .check-btn{width:100%}
  .rain-input-row{margin-top:auto}   /* input sits at thumb height, not mid-column */
  .rain-code{order:-1}
}
/* portrait / phones: stacked, and the code is pinned at the TOP of the stack
   (it is first in the DOM), never trailing after the canvas where it would sit
   below the fold on exactly the screens with the least room. */
@media not all and (min-width:900px) and (orientation:landscape){
  .rain-code{margin-bottom:0}
}

/* ---------- 词语汉兜 ---------- */
/* HINT_REDESIGN_2026-08-15: three one-time 灵露-bought hints, not a
   progressive 声母 stack. ⚠️ .handle-hintrow/.handle-hint are kept as DEAD CSS
   on purpose: GitHub Pages ages each file independently, so a device can run
   the new cs.css beside a cached old cs.js, and that old build still emits
   the 声母 row. Three lines of dead style is cheaper than an unstyled row for
   ten minutes. Delete them a release later. */
.handle-hintrow{display:flex;gap:9px;justify-content:center;margin-bottom:10px}
.handle-hint{width:64px;height:32px;border-radius:9px;display:grid;place-items:center;
  font-weight:800;font-size:17px;color:#8A6D1F;background:rgba(245,196,67,.2);border:1.5px dashed var(--gold)}
.handle-hint.off{color:#9AA7B4;background:transparent;border-color:#C4D0DB;opacity:.6}
.handle-hintbar{display:flex;gap:8px;justify-content:center;flex-wrap:wrap;margin-bottom:12px}
.handle-hintchip{border-radius:9px;padding:7px 12px;font-weight:700;font-size:13px;
  display:inline-flex;align-items:center;gap:5px;border:1.5px solid var(--gold);
  background:rgba(245,196,67,.16);color:#8A6D1F;cursor:pointer}
.handle-hintchip.buy:disabled{opacity:.45;cursor:not-allowed}
.handle-hintchip.got{background:#3E7D57;border-color:#3E7D57;color:#fff;cursor:default}
.handle-hintchip.off{color:#9AA7B4;background:transparent;border-color:#C4D0DB;opacity:.55;cursor:default}
.handle-hintchip .ling-icon{width:14px;height:14px;vertical-align:-2px}
/* two blocks of six side by side (owner 2026-08-16) — no scroller, so a played
   row can never be hidden below the fold. Wraps to one column under 560px, where
   the screen is tall and narrow and stacking is the right shape anyway. */
.handle-grid{display:flex;flex-direction:row;gap:18px;margin-bottom:16px;
  align-items:flex-start;justify-content:center;flex-wrap:wrap;padding:2px}
.handle-col{display:flex;flex-direction:column;gap:6px}
.handle-row{display:flex;gap:6px;align-items:center}
/* ⚠️ was #9AA7B4 at 12px — owner: "the numbering has very poor visibility".
   That is 2.3:1 on the card, well under the 4.5:1 minimum. Now ink on a pale
   plate: 8.9:1, and wide enough that a two-digit number does not crowd the tile. */
.handle-rownum{min-width:22px;font-size:13px;color:var(--ink);font-weight:800;text-align:center;
  background:rgba(217,231,241,.9);border-radius:6px;padding:2px 4px}
.handle-tile{width:52px;height:52px;border-radius:10px;background:#fff;border:1.5px solid var(--card-line);
  display:grid;place-items:center;font-family:var(--serif);font-weight:900;font-size:24px}
.handle-tile.exact{background:#3E7D57;border-color:#3E7D57;color:#fff}
.handle-tile.present{background:var(--gold);border-color:var(--gold);color:#3A2A08}
.handle-tile.absent{background:#D8E4EC;border-color:#C9DCE9;color:#7B93A3}
.handle-input{max-width:420px;margin:0 auto}
@media (max-width:560px){.handle-grid{gap:10px}}

/* ---------- landscape (primary target: ≥900px) ---------- */
@media (min-width:900px){
  .home-grid{grid-template-columns:400px 1fr}
  /* ⚠️ 复习范围 is capped and scrolls INSIDE itself (owner 2026-08-16). Expanding
     中一 used to add ~200px to the card, which shoved ②选择学习方式 and
     ③词语游乐场 down the page — the left column changed height every time a level
     was opened, so nothing below it ever sat still. A fixed box means opening a
     level moves nothing outside the box.
     ⚠️ Landscape only. On a phone the whole page is one column and a scroller
     inside a scroller is worse than a long card — there is nothing beside it to
     stay aligned with. */
  /* height, not max-height: with max-height the card still GROWS until it hits the
     cap, so opening 中一 shifted ② by 54px before settling. A fixed height means
     the box never changes size at all, whichever level is open. */
  #scopeCard{height:clamp(300px,46vh,560px);overflow-y:auto;overscroll-behavior:contain}
  /* auto-fit already handles the row; no fixed 2-up override any more */
  .study{grid-template-columns:300px 1fr}
  .rail{position:sticky;top:78px}
  .arrow{display:block}
  .q-text{font-size:27px}
  .q-text.mcq{font-size:25px}
  .opts{grid-template-columns:1fr 1fr}
  .opts.n2{grid-template-columns:1fr 1fr}
  .opts.n3{grid-template-columns:1fr 1fr 1fr}
  .opt{font-size:21px;padding:18px 20px}
  .flashcard{min-height:430px}
  .flashcard .w{font-size:76px}
  .flashcard .back-w{font-size:52px}
  .flashcard .zh{font-size:23px}
  .rain-word .rw{font-size:24px}
}


/* ---------- landing couplet (书山有路勤为径 · 学海无涯苦作舟) ---------- */
.couplet-v{position:absolute;z-index:6;top:11%;writing-mode:vertical-rl;
  font-family:var(--serif);font-weight:700;font-size:clamp(15px,2vw,22px);
  letter-spacing:.44em;color:#3A5A70;text-shadow:0 1px 10px rgba(255,255,255,.85);
  user-select:none;pointer-events:none}
.couplet-v.left{left:clamp(12px,3.2vw,46px)}
.couplet-v.right{right:clamp(12px,3.2vw,46px)}
.couplet-h{display:none;text-align:center;font-family:var(--serif);font-weight:700;
  font-size:15px;letter-spacing:.3em;color:#DCEAF4;padding:0 20px 14px;
  text-shadow:0 1px 8px rgba(23,58,90,.5)}
@media (max-width:720px){
  .couplet-v{display:none}
  .couplet-h{display:block}
}


/* ---------- 集雨 (rain collection) ---------- */
.rain-fx{position:absolute;inset:0;pointer-events:none;opacity:.16;
  background:repeating-linear-gradient(105deg,transparent 0 26px,#8FB4CF 26px 27.5px);
  animation:rainDrift 1.1s linear infinite}
@keyframes rainDrift{from{background-position:0 0}to{background-position:-42px 120px}}
.rain-word.collect{transition:transform .45s cubic-bezier(.4,.1,.7,1),opacity .45s;opacity:.15;z-index:3}
.rain-barrel{position:absolute;right:14px;bottom:10px;width:58px;height:66px;z-index:4;
  border:3px solid #6B4F3A;border-top-width:4px;border-radius:6px 6px 10px 10px;
  background:rgba(255,255,255,.25);overflow:hidden;box-shadow:0 3px 10px rgba(23,58,90,.25)}
.rain-water{position:absolute;left:0;right:0;bottom:0;height:0;background:linear-gradient(180deg,#7FB3D9,#2E6391);
  transition:height .4s ease}
.rain-drops{position:absolute;left:0;right:0;bottom:4px;text-align:center;font-size:11.5px;
  font-weight:700;color:#FFFDF6;text-shadow:0 1px 4px rgba(23,58,90,.6)}
.rain-splash{position:absolute;bottom:34px;font-size:20px;pointer-events:none;
  animation:splashUp .5s ease-out forwards}
@keyframes splashUp{0%{transform:translateY(6px);opacity:1}100%{transform:translateY(-14px);opacity:0}}
@media (prefers-reduced-motion: reduce){.rain-fx{animation:none;opacity:.08}}

/* ---------- 汉兜 hints ---------- */
.handle-hints{margin-top:12px;border-top:1px solid #E4EDF4;padding-top:10px}
.hint-line{font-size:13px;color:#3A5A70;line-height:1.7}
.hint-line b{color:var(--gold-deep);letter-spacing:.08em}

/* ---------- shared popover ---------- */
.pop-overlay{position:fixed;inset:0;z-index:60;background:rgba(23,45,66,.42);
  -webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px);
  display:flex;align-items:center;justify-content:center;padding:18px}
/* alpha raised 2026-08-14: these cards float over bg-01..05 and the bamboo
   ambience, where .68 left the muted body text unreadable. The blur still
   carries the glass read; do not take these back down without opening a
   关卡 popover over the brightest backdrop first. */
.pop-card{background:linear-gradient(150deg,rgba(255,255,255,.95),rgba(255,255,255,.90) 46%,rgba(232,243,251,.93));
  -webkit-backdrop-filter:saturate(170%) blur(26px);backdrop-filter:saturate(170%) blur(26px);
  border:1px solid var(--glass-line);border-radius:20px;max-width:520px;width:100%;max-height:86vh;
  overflow:auto;padding:22px 24px;
  box-shadow:0 18px 50px rgba(12,30,48,.34),var(--glass-edge)}
.pop-title{font-family:var(--serif);font-weight:900;font-size:20px;color:var(--ink);margin-bottom:10px}
.pop-body{font-size:14.5px;line-height:1.8;color:#41576A}
.pop-note{font-size:12.5px;color:var(--ink-soft)}
.pop-label{font-size:12.5px;font-weight:700;color:#3A5A70;margin:10px 0 6px;letter-spacing:.06em}
/* ---------- 恢复码 (owner 2026-08-19) ----------
   ⚠️ BIG, MONOSPACE, WIDE-TRACKED. This is the one string in the whole app a child
   copies off a screen onto paper, so legibility beats elegance: the alphabet already
   drops 0/O/1/I/L, and letter-spacing plus a monospace face is the rest of the job.
   ⚠️ user-select:all so one tap selects the whole thing on a touch device — a partial
   selection is a wrong code, and a wrong code reads to a student as「我的进度没了」.
   ⚠️ Deliberately NOT a .code-ta: that box is a scrolling 74px textarea sized for an
   ~800-character paste, and putting ten characters in it makes the short route look
   like the long one. */
.claim-code{font-family:ui-monospace,Menlo,Consolas,monospace;font-size:23px;font-weight:800;
  letter-spacing:.16em;text-align:center;color:var(--ink);user-select:all;
  background:linear-gradient(180deg,#FFF8E6,#FDF0D2);border:2px solid var(--gold);
  border-radius:12px;padding:11px 8px;margin:2px 0 6px;word-break:break-all}
@media (max-width:560px){ .claim-code{font-size:19px;letter-spacing:.11em} }
.code-ta{width:100%;box-sizing:border-box;height:74px;border:1px solid var(--glass-line);border-radius:11px;
  padding:9px 11px;font-size:12px;font-family:monospace;resize:none;color:var(--ink);
  background:rgba(255,255,255,.5);box-shadow:inset 0 1px 3px rgba(23,58,90,.08)}
.code-link{display:inline-flex;align-items:center;gap:4px;
  background:#FDF6E4;border:2px solid var(--gold,#D9A72B);border-radius:8px;
  color:#5C4009;font-size:15px;font-weight:700;letter-spacing:.02em;
  text-decoration:none;cursor:pointer;padding:6px 12px;margin-top:4px;
  box-shadow:0 2px 0 0 #B8821A}
.code-link:active{transform:translateY(1px);box-shadow:none}
.np-roles{display:flex;gap:8px;margin-bottom:2px}
.np-role{flex:1;border:1.5px solid #B9CEDD;border-radius:12px;padding:10px 4px;font-size:14px;font-weight:700;
  color:var(--ink-soft);background:#fff;cursor:pointer}
.np-role.on{background:var(--gold);border-color:var(--gold);color:#3A2A08}
.np-select{width:100%;box-sizing:border-box;height:42px;border:1px solid var(--glass-line);border-radius:11px;
  padding:0 11px;font-size:15px;color:var(--ink);background:rgba(255,255,255,.55);
  box-shadow:inset 0 1px 3px rgba(23,58,90,.08)}
/* school search box: 150+ schools are unusable as a bare dropdown on a phone */
.np-search{width:100%;box-sizing:border-box;height:42px;margin:8px 0;border:1px solid var(--glass-line);
  border-radius:11px;padding:0 12px;font-size:15px;color:var(--ink);background:rgba(255,255,255,.72);
  box-shadow:inset 0 1px 3px rgba(23,58,90,.08);-webkit-appearance:none}
/* 昵称 confirm step: the rolled name + a dice that re-rolls it in place */
.np-name-row{display:flex;align-items:center;justify-content:center;gap:10px;flex-wrap:wrap;margin:8px 0 14px}
.np-name{font-size:19px;font-weight:700;color:var(--ink)}
.np-roll{border:1px solid rgba(183,121,31,.5);border-radius:999px;padding:8px 14px;font-size:14px;font-weight:700;
  color:#3A2A08;background:linear-gradient(180deg,#EEBC5E,var(--gold));cursor:pointer;touch-action:manipulation;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.55),0 2px 8px rgba(227,166,60,.32)}
.np-roll:active{transform:translateY(1px)}
/* the way out to the four-step manual picker. Deliberately quiet — a plain
   underlined line under the primary button — because the rolled name is now the
   default path and this is the exception. */
/* 换设备找回 (owner 2026-08-16). The link sits beside 我要自己选昵称 rather than
   above the name: a returning student is the minority case, and putting a code box
   in front of everyone else would be a barrier on the very screen the dice-first
   redesign existed to remove. */
.np-manual{text-align:center;margin-top:10px;display:flex;gap:14px;justify-content:center;flex-wrap:wrap}
.np-restored{background:rgba(62,125,87,.12);border:1px solid rgba(62,125,87,.4);
  border-radius:10px;padding:8px 11px;color:#2E5F42;margin-bottom:4px}
.np-code-err{background:rgba(180,74,60,.10);border:1px solid rgba(180,74,60,.4);
  border-radius:10px;padding:8px 11px;color:#8E3527;margin-top:8px}
/* ⚠️ THE 找回 SCREEN NEEDS A NON-RED STATE (学习编号, owner 2026-08-31). A 学习编号
   restores nothing by itself — the progress arrives on the next paste — so the box has a
   genuine「step 1 of 2 done」outcome that is neither an error nor an ending. Rendering it
   in .np-code-err made success and a mistyped ID look identical, and the only thing
   separating them was Chinese prose. Same geometry as its red sibling on purpose: they
   occupy the same slot and only the colour changes. */
.np-code-ok{background:rgba(29,158,117,.10);border:1px solid rgba(29,158,117,.45);
  border-radius:10px;padding:8px 11px;color:#0F6E56;margin-top:8px}
.np-manual button{border:0;background:none;padding:6px 10px;cursor:pointer;touch-action:manipulation;
  font-family:var(--sans);font-size:13px;font-weight:600;color:#4E6273;text-decoration:underline}
.np-manual button:hover{color:var(--ink)}

/* ---------- 我的档案 profile panel (profile.js) ---------- */
.pop-card.prof-wide{max-width:880px;padding:20px 22px}
.prof-grid{display:block}
.prof-col{display:block}
.prof-sec{margin-top:12px}
.prof-col>.prof-sec:first-child{margin-top:0}
.prof-sec:first-child{margin-top:2px}
.prof-nick{font-family:var(--serif);font-weight:900;font-size:23px;color:var(--ink)}
/* merged 身份+基本资料 header: avatar left, nickname/class/school + links right */
.prof-head{display:flex;align-items:center;gap:14px}
/* 我的档案 close affordance: the panel is a full-height scrolling sheet, so the
   ✕ is sticky to the card, not the page, or it scrolls away mid-panel. */
.prof-x{position:sticky;top:0;float:right;z-index:2;margin:-2px -4px 0 0;
  width:34px;height:34px;border-radius:50%;border:1px solid var(--glass-line);
  background:rgba(255,255,255,.72);color:#3A5A70;font-size:15px;line-height:1;cursor:pointer;
  -webkit-backdrop-filter:var(--glass-blur);backdrop-filter:var(--glass-blur);
  box-shadow:var(--glass-edge),0 3px 12px rgba(23,58,90,.18);touch-action:manipulation}
.prof-x:hover{background:rgba(255,255,255,.9);color:var(--bad,#B44A3C)}
.prof-x:active{transform:translateY(1px)}
.prof-head-txt{min-width:0;flex:1}
.prof-head-sub{font-size:12px;color:var(--ink-soft);margin-top:2px;line-height:1.5;word-break:break-word}
.prof-head-links{display:flex;gap:8px;flex-wrap:wrap;margin-top:7px}
/* ⚠️ 132px, not 66 (owner 2026-08-17: "make it way bigger and more prominent").
   The chosen avatar is the one thing on this panel that is purely the student's,
   and at 66px it read as a form field's icon next to two link-sized buttons.
   Safe at this size because the source art is 320×320: this is still a DOWNSCALE,
   so §14's pixel-art trap does not apply and no source file is touched. The gold
   ring thickens with it, or a 3px hairline round a 132px disc reads as unfinished.
   ⚠️ Step-down below is a MAX-WIDTH query and must stay after this rule (§14). */
.prof-avatar.lg{width:132px;height:132px;border-width:3px;
  box-shadow:var(--glass-edge),0 4px 14px rgba(23,58,90,.20)}
.prof-head{gap:18px}
@media (max-width:560px){
  /* portrait phone: the head row is the whole column width, so a 132px disc
     leaves the nickname about 150px and 换头像/换昵称 wrap one per line. */
  .prof-avatar.lg{width:96px;height:96px}
  .prof-head{gap:14px}
}
/* collapsed 技术编号+隐私说明 (expands on demand, keeps the panel short) */
.prof-more>summary{cursor:pointer;font-size:12.5px;font-weight:700;color:#3A5A70;
  letter-spacing:.05em;padding:8px 0;list-style:none}
.prof-more>summary::-webkit-details-marker{display:none}
.prof-more>summary::after{content:" ›";display:inline-block;transition:transform .15s}
.prof-more[open]>summary::after{transform:rotate(90deg)}
.prof-row{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.prof-chips{display:flex;gap:8px;flex-wrap:wrap;margin:4px 0 6px}
.prof-chip{border:1px solid var(--glass-line);border-radius:999px;padding:7px 15px;font-size:13.5px;
  background:rgba(255,255,255,.6);color:#3A5A70;cursor:pointer;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.85),0 2px 8px rgba(23,58,90,.1)}
.prof-chip.on{background:linear-gradient(180deg,#EEBC5E,var(--gold));border-color:rgba(183,121,31,.5);
  color:#3A2A08;font-weight:700;box-shadow:0 3px 12px rgba(227,166,60,.4)}
.prof-input{width:100%;box-sizing:border-box;height:42px;border:1px solid var(--glass-line);border-radius:11px;
  padding:0 12px;font-size:15px;font-family:inherit;color:var(--ink);background:rgba(255,255,255,.55);
  box-shadow:inset 0 1px 3px rgba(23,58,90,.08)}
/* 我的进度: a real grid, so four numbers never stretch into four wide, mostly
   empty plates the way flex:1 made them. */
/* ---------- 我的进度：一片放大，其余缩小 (owner 2026-09-05) ----------
   ⚠️ 这一段在 cs.css 与 xh.css 各有一份（§17 那种刻意的重复）：**改一处要改两处**，
   漏了的症状是「同一块面板在码头没有样式」。
   ⚠️ 五颗芯片写死五列，不用 auto-fit：陆地就是五片，让它按宽度换行会在窄屏上
   排成 3+2，而那一行读起来必须是一条完整的路（码头 → HCL）。 */
.prof-prog{display:block}
.pg-row{display:flex;align-items:stretch;gap:6px}
.pg-arrow{flex:none;width:30px;border:1px solid var(--glass-line);border-radius:11px;
  background:rgba(255,255,255,.5);color:var(--ink-soft);font-size:17px;font-weight:800;
  line-height:1;cursor:pointer;font-family:var(--sans);-webkit-appearance:none}
.pg-arrow:hover{background:rgba(255,255,255,.82);color:var(--ink)}
.pg-chips{flex:1 1 auto;min-width:0;display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:6px}
.pg-chip{min-width:0;padding:6px 2px;border:1px solid var(--glass-line);border-radius:11px;
  background:rgba(255,255,255,.5);cursor:pointer;font-family:var(--sans);color:var(--ink);
  text-align:center;line-height:1.2;-webkit-appearance:none}
.pg-chip b{display:block;font-size:12.5px;font-weight:800;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.pg-chip span{display:block;font-size:10px;color:var(--ink-soft);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.pg-chip.is-empty b,.pg-chip.is-empty span{color:#93A5B2}
.pg-chip.is-on{background:linear-gradient(180deg,#FFF3D4,#F7DFA6);border-color:var(--gold);
  box-shadow:0 2px 8px rgba(199,140,26,.22)}
.pg-chip.is-on b{color:#5B4109}
.pg-chip.is-on span{color:var(--gold-ink)}
.pg-focus{margin-top:8px;padding:12px 14px;border:1px solid var(--glass-line);border-radius:14px;
  background:rgba(255,255,255,.5);box-shadow:inset 0 1px 0 rgba(255,255,255,.8)}
.pg-big{font-family:var(--serif);font-size:34px;font-weight:800;color:var(--gold-deep);line-height:1.1}
.pg-big i{font-style:normal;font-size:16px;margin-left:3px}
.pg-big em{font-style:normal;font-size:20px;color:var(--ink-soft)}
.pg-name{margin-top:2px;font-size:13.5px;font-weight:800;color:var(--ink)}
.pg-name span{display:block;font-size:11.5px;font-weight:500;color:var(--ink-soft)}
.pg-stats{display:flex;flex-wrap:wrap;gap:6px;margin-top:9px}
.pg-stats div{flex:1 1 84px;min-width:0;padding:6px 8px;border-radius:10px;text-align:center;
  background:rgba(255,255,255,.55);border:1px solid var(--glass-line)}
.pg-stats b{display:block;font-family:var(--serif);font-size:16px;font-weight:800;color:var(--ink)}
.pg-stats span{font-size:10.5px;color:var(--ink-soft)}
.prof-uid{font-family:ui-monospace,Menlo,Consolas,monospace;font-size:12.5px;word-break:break-all;
  background:rgba(255,255,255,.5);border:1px solid var(--glass-line);border-radius:10px;padding:9px 12px;color:var(--ink)}
/* avatar + nickname pill — the single 我的档案 entry on a stream page */
.tb-profile{display:inline-flex;align-items:center;gap:8px;
  background:rgba(255,255,255,.55);border:1px solid var(--glass-line);border-radius:999px;
  -webkit-backdrop-filter:var(--glass-blur);backdrop-filter:var(--glass-blur);
  box-shadow:var(--glass-edge),0 3px 12px rgba(23,58,90,.14);
  height:36px;max-width:min(42vw,240px);font-size:17px;cursor:pointer;color:#3A5A70;flex:none;
  margin-left:6px;padding:2px 13px 2px 2px;overflow:hidden;touch-action:manipulation}
.tb-profile:active{transform:translateY(1px)}
.tb-av{width:30px;height:30px;border-radius:50%;flex:none;overflow:hidden;
  display:grid;place-items:center;background:rgba(241,246,251,.85);font-size:16px;
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.7)}
.tb-profile img{width:100%;height:100%;object-fit:contain;border-radius:50%;display:block}
.tb-nick{font-family:var(--serif);font-weight:700;font-size:13.5px;letter-spacing:.02em;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0}
/* phones: drop back to the bare circle so the stream name keeps its room */
@media (max-width:520px){
  .tb-nick{display:none}
  .tb-profile{padding:2px;width:36px;justify-content:center}
  .tb-en{padding:0 8px;gap:2px}
  .tb-en .tb-en-zh{font-size:14px}
  .tb-en .tb-en-en{font-size:10.5px}
}
/* ---------- 英文提示 (EN aid, G1/G2) ----------
   决定一 of DESIGN_english-toggle-fading-and-flow-numbering. The gloss spans
   are always rendered and gated here, so the toggle is one class flip on
   <body> and never needs a re-render. NAV/BUTTON SHELL TEXT ONLY — no rule
   here may ever be extended to 题干/释义/句子. */
/* 界面拼音 (2026-08-14): same always-in-DOM, CSS-gated mechanism as .enlab, so
   toggling is a single class flip on <body> and re-renders nothing. Sits under
   the Chinese; when BOTH aids are on, pinyin comes first (it decodes the same
   script the student is looking at) and English under it. */
.pylab{display:none}
/* ⚠️ 11.5px / #6A5A21, not 10.5px / #7A6A3C (owner 2026-08-16, "some words have
   poor visibility"). Measured on a glass card the old pair was 4.66:1 and the
   English gloss only 2.80:1 — under the 4.5:1 minimum for text this small. */
body.py-aid .pylab{display:block;font-family:var(--sans);font-size:11.5px;line-height:1.35;
  color:#6A5A21;letter-spacing:.01em;font-weight:600;margin-top:1px}
body.py-aid .unit.on .pylab,
body.py-aid .nav-btn.primary .pylab,body.py-aid .dopt.on .pylab,
body.py-aid .tb-en.on .pylab,body.py-aid .tb-py.on .pylab{color:#553F06}
body.py-aid .camp .go .pylab{color:var(--gold-deep);text-align:right}
/* HUD/stat pills: wrap under, never widen the container (same rule as .enlab.i) */
body.py-aid .harbour .pylab,body.py-aid .sprint-hud .pylab,body.py-aid .rain-hud .pylab,
body.py-aid .streak .pylab,body.py-aid .rail-pts .pylab,body.py-aid .comp-lab .pylab{
  display:block;margin:1px 0 0}
.enlab{display:none}
body.en-aid .enlab{display:block;font-family:var(--sans);font-size:11.5px;line-height:1.3;
  letter-spacing:.01em;font-weight:500;color:#4E6273;margin-top:1px;text-transform:none}
body.en-aid .enlab.i{display:inline-block;margin:0 0 0 6px;vertical-align:baseline}
/* ⚠️ DARK PLATES: the gloss colours above are tuned for a light glass card, and
   were being applied unchanged over the navy 概览 bar and the 词雨 HUD — dark gold
   on navy measured 1.62:1, i.e. effectively invisible, which is exactly what the
   owner reported. Any new dark surface carrying a gloss must be added here. */
body.py-aid .harbour .pylab,body.en-aid .harbour .enlab,
body.py-aid .rain-hud .pylab,body.en-aid .rain-hud .enlab,
body.py-aid .home-foot .pylab,body.en-aid .home-foot .enlab,
/* the landing page (owner 2026-08-17). ⚠️ Only the genuinely DARK ones belong here:
   the sea-map plaque is navy (31,74,112) and the course card is navy (14,27,54).
   .lp-enter-btn is GOLD and goes in the gold list below instead — it was in this
   list for one edit and came out near-white on gold, which is the same 1.62:1
   mistake §18e recorded, just with the colours swapped. Measure, do not assume. */
body.py-aid .sea-label .pylab,body.en-aid .sea-label .enlab,
body.py-aid .lp-card .pylab,body.en-aid .lp-card .enlab,
/* the 🔊 pill is var(--sea1) — a dark plate like any other, and the one a G1/G2
   student presses most often (owner 2026-08-16 晚). ⚠️ It needs BOTH the body prefix
   and its own descendant to outrank the base gloss rule; that is the whole reason
   the earlier attempt at line ~117 did nothing. */
body.py-aid .tts-lab .pylab,body.en-aid .tts-lab .enlab{
  color:rgba(232,243,250,.92)}
/* ⚠️ and the ENGLISH inside the pill is a full line, not an inline tail: `.enlab.i`
   sets display:inline-block with a 6px left margin, which on a three-line button
   ran the English up against the pinyin. */
body.en-aid .tts-lab .enlab.i{display:block;margin:1px 0 0}
/* on gold/selected surfaces the soft grey loses contrast */
.unit.on .enlab,.nav-btn.primary .enlab,
/* the landing page's gold hero button (owner 2026-08-17) */
.lp-enter-btn .enlab,
.tb-en.on .enlab,.tb-py.on .enlab,.dopt.on .enlab,
/* the 意见反馈 form's type chips use the same gold when selected (2026-08-16 晚) */
.prof-chip.on .enlab{color:#553F06}
body.py-aid .prof-chip.on .pylab{color:#553F06}
body.py-aid .lp-enter-btn .pylab{color:#553F06}
/* ⚠️ the sea-map plaque has `white-space:nowrap` and sits ON the island. A gloss
   that widened it would push the plaque over the mountain the owner already asked
   to keep clear (2026-08-16「the section titles are obscuring a good part of the
   mountain」), so the gloss wraps normally and the plaque grows DOWNWARD only. */
.sea-label{white-space:normal;max-width:min(42vw,260px)}
body.py-aid .sea-label .pylab,body.en-aid .sea-label .enlab{
  font-size:clamp(9.5px,.72vw,12px);line-height:1.25;margin-top:1px}
/* ⚠️ block, not the inline `.enlab.i` tail: these sit under a chip label and a
   dialog heading, both of which are their own line. */
body.en-aid .prof-chip .enlab,body.en-aid .pop-label .enlab,
body.en-aid .pop-title .enlab,body.en-aid .nav-btn .enlab,
body.en-aid .pop-note .enlab,body.en-aid .fb-ctx .enlab{display:block;margin:1px 0 0}
.pop-title .pylab,.pop-title .enlab{font-weight:600;letter-spacing:0}
.pop-label .pylab,.pop-label .enlab{letter-spacing:.01em;text-transform:none}
.camp .go .enlab{color:var(--gold-deep);text-align:right}
.enlab-always{display:block;font-size:12.5px;color:#4E6273;line-height:1.5}
/* 中/EN toggle — icon-only on purpose: a student who cannot read the interface
   must still be able to find the control that fixes that. */
/* ⚠️ .72, up from .55: these used to have the topbar's own glass behind them and
   now sit straight on the ambience photo. Same reason §7 pushes dense panels up. */
.tb-en{display:inline-flex;align-items:center;gap:4px;flex:none;height:42px;padding:0 14px;
  background:rgba(255,255,255,.72);border:1px solid var(--glass-line);border-radius:999px;
  -webkit-backdrop-filter:var(--glass-blur);backdrop-filter:var(--glass-blur);
  box-shadow:var(--glass-edge),0 3px 12px rgba(23,58,90,.14);
  color:#3A5A70;cursor:pointer;margin-left:6px;touch-action:manipulation;line-height:1}
.tb-en:active{transform:translateY(1px)}
.tb-en .tb-en-zh{font-family:var(--serif);font-weight:800;font-size:17px}
.tb-en .tb-en-en{font-size:13px;font-weight:700;letter-spacing:.04em;opacity:.55}
.tb-en.on{background:var(--gold);border-color:var(--gold);color:#3A2A08;
  box-shadow:0 3px 12px rgba(227,166,60,.35)}
.tb-en.on .tb-en-en{opacity:1}

/* 拼音辅助 — same shape and size as 中/EN so the two aids read as a pair.
   The label is INSIDE the pill and the pill grows to fit it; it never spills. */
.tb-py{display:inline-flex;align-items:center;gap:6px;flex:none;height:42px;padding:0 14px;
  background:rgba(255,255,255,.72);border:1px solid var(--glass-line);border-radius:999px;
  -webkit-backdrop-filter:var(--glass-blur);backdrop-filter:var(--glass-blur);
  box-shadow:var(--glass-edge),0 3px 12px rgba(23,58,90,.14);
  color:#3A5A70;cursor:pointer;margin-left:6px;touch-action:manipulation;line-height:1.15;
  white-space:nowrap}
.tb-py:active{transform:translateY(1px)}
.tb-py .tb-py-zh{font-family:var(--serif);font-weight:800;font-size:17px}
.tb-py .tb-py-lab{font-size:13px;font-weight:700;opacity:.7}
.tb-py.on{background:var(--gold);border-color:var(--gold);color:#3A2A08;
  box-shadow:0 3px 12px rgba(227,166,60,.35)}
.tb-py.on .tb-py-lab{opacity:1}
/* the topbar is now carrying two aid pills plus the profile pill — give it the
   height they need rather than letting anything clip */
.topbar{min-height:60px}
@media (max-width:640px){
  /* narrow: keep both toggles but drop their word labels, never the buttons */
  .tb-py .tb-py-lab{display:none}
  .tb-py{padding:0 12px}
}
/* ⚠️ 手机：四颗控件 + 一块名牌塞不进 390px，所以**副标先让位，名字最后让**
   （owner 2026-08-22 的条件就是学段名要留得住）。让出来的顺序是量出来的，不是估的：
   副标（最长的一行，且它说的是「哪一层」而不是「哪一座山」）→ 查词 的词标签
   （🔎 自己就是那个词）→ 一点点字号与内距。**一颗按钮都没有被藏起来。** */
@media (max-width:620px){
  .topbar{gap:8px;padding:10px 12px;min-height:52px}
  /* ⚠️ 手机上名牌只留**中文名 + 学段标签**：学段标签是 owner 明说要留的那一条
     （「keep the G1/2/3/HCL label」），而拼音与英文在这块**身份**牌上是装饰——
     它们该在的地方是按钮与题目，那里一个都没关。
     ⚠️ (0,4,0) 压得过 body.py-aid .pylab 的 (0,2,0)，与文件顺序无关。 */
  body.py-aid .topbar .tb-id .pylab,body.en-aid .topbar .tb-id .enlab{display:none}
  .topbar .tb-name{font-size:16px;letter-spacing:.02em}
  .topbar .tb-id{padding:5px 11px}
  #tbFind .tb-en-lab{display:none}
  #tbFind{padding:0 10px}
  .tb-en,.tb-py,.tb-profile{height:38px}
}
@media (max-width:400px){
  .topbar{gap:6px;padding:10px}
  .topbar .tb-name{font-size:15px}
  .tb-en,.tb-py{padding:0 9px}
}
/* ---------- 动线编号 (决定四) ----------
   Permanent, small, and ONLY on genuinely multi-step decision flows. Never on
   浏览/目的地 pages (排行榜/成就墙/我的词山) — see the note in cs.js stepNo(). */
.step-no{display:inline-grid;place-items:center;width:17px;height:17px;border-radius:50%;
  background:var(--gold);color:#3A2A08;font-family:var(--sans);font-size:11px;font-weight:800;
  letter-spacing:0;margin-right:7px;vertical-align:middle;flex:none}
/* ---------- 头像选择器 ---------- */
.prof-avatar{width:52px;height:52px;border-radius:50%;object-fit:cover;border:2px solid var(--gold);
  background:#F1F6FB;cursor:pointer;flex:none;padding:0;overflow:hidden}
.prof-avatar img{width:100%;height:100%;object-fit:contain;display:block}
.avatar-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(68px,1fr));gap:14px;margin-top:10px}
.avatar-cell{display:flex;flex-direction:column;align-items:center;gap:5px}
.avatar-thumb{width:64px;height:64px;border-radius:50%;border:3px solid transparent;padding:0;
  background:#F1F6FB;cursor:pointer;overflow:hidden;box-shadow:0 2px 8px rgba(23,58,90,.15)}
.avatar-thumb img{width:100%;height:100%;object-fit:contain;display:block}
.avatar-thumb.on{border-color:var(--gold);box-shadow:0 0 0 3px rgba(227,166,60,.35)}
.avatar-cell-label{font-size:10.5px;text-align:center;color:var(--ink-soft);line-height:1.3}
/* 未解锁头像: greyed but still SHOWN (HANDOFF §3.4) — seeing what can be earned is
   the motivation. The thumb stays a live button; its card explains the condition. */
.avatar-thumb.locked{position:relative}
.avatar-thumb.locked img{filter:grayscale(1);opacity:.42}
.avatar-thumb .av-lock{position:absolute;right:-1px;bottom:-1px;font-size:15px;line-height:1;
  background:rgba(255,255,255,.92);border-radius:50%;padding:2px 3px;box-shadow:0 1px 4px rgba(23,58,90,.3)}
/* 兑换价钱 sits on the cell, not one tap deep: the price IS the motivation */
.av-price{display:block;margin-top:2px;font-size:10.5px;font-weight:700;color:var(--gold-ink)}
.av-purse{float:right;font-size:13px;font-weight:700;color:var(--gold-ink)}
/* AvatarInfoCard: the enlarged 简介 card shown before a pick is committed. */
.pop-card.av-info{max-width:400px;text-align:center}
.av-info-img{width:180px;height:180px;margin:2px auto 0;border-radius:50%;overflow:hidden;
  background:radial-gradient(circle at 50% 42%,#FFFFFF 0%,#EAF2F9 72%,#DCE8F3 100%);
  border:3px solid var(--gold);box-shadow:0 6px 20px rgba(23,58,90,.2)}
.av-info-img img{width:100%;height:100%;object-fit:contain;display:block}
.av-info-name{font-family:var(--serif);font-size:21px;font-weight:700;color:var(--sea3);margin-top:12px}
.av-info-bio{font-size:13.5px;line-height:1.85;color:var(--ink);margin-top:8px;text-align:left}
.av-info-img.locked{position:relative;border-color:#B8C6D2}
.av-info-img.locked img{filter:grayscale(1);opacity:.42}
.av-info-lockmark{position:absolute;left:0;right:0;top:50%;transform:translateY(-50%);font-size:44px;line-height:1}
.av-info-lock{margin-top:8px;font-size:13px;font-weight:700;color:var(--sea3);
  background:rgba(255,255,255,.72);border:1px solid var(--glass-line);border-radius:999px;
  padding:6px 12px;display:inline-block}
.av-info-sub{margin-top:6px;font-size:12.5px;color:var(--ink-soft)}
@media (max-width:420px){
  .av-info-img{width:136px;height:136px}
  .av-info-name{font-size:19px}
  .av-info-bio{font-size:13px;line-height:1.75}
}
/* Two REAL columns, not four grid cells. As four cells the row heights were
   coupled, so the short 我的进度 / 技术编号 blocks sat beside the tall
   身份 / 进度码 blocks and left half the panel empty. Each column now flows
   independently and packs. Below 900px both columns collapse to one stack in
   source order: 身份 → 进度 → 进度码 → 技术. */
@media(min-width:900px){
  .prof-grid{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.04fr);gap:20px;align-items:start}
  .prof-col>.prof-sec{margin-top:14px}
  .prof-col>.prof-sec:first-child{margin-top:0}
}

/* ---------- 你的营地 / 营地商店 / 年度试炼 ---------- */
.camp-wallet{background:#FDF3DC;border:1px solid #EFD9A4;border-radius:12px;padding:10px 14px;
  font-size:14px;color:#7A5B12;margin-bottom:8px}
.camp-wallet b{font-family:var(--serif);font-size:18px;color:var(--gold-deep)}
.camp-pets{font-size:13.5px;color:#3F704F;font-weight:700;margin-bottom:6px}
/* ⚠️ .camp-board / .cb removed with the camp's 自由试炼 row (owner 2026-08-17).
   `.cb` was a two-letter naked class in the shared sheet, so leaving it behind
   was a live hazard, not just clutter: the next screen to invent a「camp button」
   would have inherited a border and a hover lift from nowhere. */
.gym-sec{margin-top:12px;padding-top:12px;border-top:1px solid #E4EDF4}
.gym-sec.done{color:#3F704F;font-weight:700;font-size:14px;line-height:1.7}
.gym-sec.lock{color:var(--ink);font-size:13.5px;line-height:1.7}
/* 顶峰卡上的四行年级清单（owner 2026-08-23：学生看不出怎样才算登顶）。
   一行一个年级，左边状态右边还差多少词——「还差什么」要能一眼数出来，
   而不是从两个总数里自己推。 */
.peak-lvs{margin-top:10px;display:grid;gap:5px}
.peak-lv{display:flex;justify-content:space-between;gap:10px;font-size:13px;
  padding:6px 10px;border-radius:9px;background:rgba(255,255,255,.6);
  border:1px solid var(--glass-line);color:var(--ink-soft)}
.peak-lv.got{background:#EAF5EE;border-color:#BFE0CB;color:#3F704F;font-weight:600}
.gym-todo{color:var(--gold-deep);font-weight:700}
.gym-go{width:100%}
/* ---------- 营地商店 shelf (owner 2026-08-17) ----------
   ⚠️ NO FRAME AROUND AN ITEM, art first, price as a coin and a number — the Pikmin
   Bloom shelf the owner pointed at, and the pier's 海滩小铺 wears the identical
   layout (xh.css carries its own copy: §17 deliberate duplication). This used to be
   a bordered row of thumb + text + price + button, four columns of chrome around a
   44px thumbnail, and the thumbnail is the only part of the row a student reads
   without effort.
   ⚠️ THE WHOLE TILE IS THE BUTTON, never a div with a button inside it
   (§14「按钮嵌套按钮」). One item offers one action at a time. */
.shop-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(122px,1fr));
  gap:4px 2px;margin:2px 0 10px;align-items:start}
.shop-tile{display:flex;flex-direction:column;align-items:center;gap:1px;text-align:center;
  width:100%;padding:8px 4px 12px;border:0;border-radius:16px;background:none;color:var(--ink);
  font-family:var(--sans);cursor:pointer;-webkit-appearance:none}
.shop-tile[disabled]{cursor:not-allowed;opacity:.45}
button.shop-tile:hover:not([disabled]){background:rgba(46,99,145,.06)}
button.shop-tile:active:not([disabled]){transform:translateY(1px)}
/* the art well. ⚠️ Fixed HEIGHT, free width: the shelf holds tents, lanterns and
   four boats of very different proportions, and a fixed width squashes the long
   ones. Nothing is painted here — the well is empty space, not a card. */
.shop-ic{position:relative;display:flex;align-items:flex-end;justify-content:center;
  width:100%;height:74px;margin-bottom:4px;border-radius:50%/38%}
.shop-ic img{max-width:100%;max-height:74px;width:auto;height:auto;object-fit:contain;
  image-rendering:pixelated;filter:drop-shadow(0 3px 6px rgba(23,58,90,.18))}
/* ⚠️ boats get a taller well: 简朴帆船 is drawn 285px tall against 舢板's 117, and
   at a flat height the junk came out NARROWER than the sampan and read as the
   smaller ship (owner 2026-08-17). Letting the natural proportions through inside
   one box is what makes the junks about twice the sampans' height, as drawn. */
.shop-boats .shop-ic,.shop-boats .shop-ic img{height:auto;max-height:92px}
.shop-boats .shop-ic{height:92px}
.shop-tile.is-on .shop-ic{background:radial-gradient(60% 70% at 50% 62%,rgba(227,166,60,.34),rgba(227,166,60,0) 72%)}
.shop-tile.is-own .shop-ic{background:radial-gradient(60% 70% at 50% 62%,rgba(63,112,79,.18),rgba(63,112,79,0) 72%)}
/* the held count, on the art rather than in a subtitle */
.shop-n{position:absolute;right:8%;top:2px;min-width:19px;height:19px;padding:0 5px;
  display:flex;align-items:center;justify-content:center;border-radius:999px;
  font-style:normal;font-size:11.5px;font-weight:800;color:var(--ink);
  background:#fff;border:1px solid var(--card-line);box-shadow:0 1px 3px rgba(23,58,90,.18)}
.shop-tile b{font-size:14.5px;font-weight:800;line-height:1.25;color:var(--ink)}
.shop-tile .shop-sub{font-size:11.5px;line-height:1.35;color:var(--ink-soft)}
/* ⚠️ the currency icon is 14px HERE TOO, and the selector is specific enough to win
   against the art rule above — the pier shipped the mirror-image bug, where
   `.beach-card img{height:62px}` (0,2,0) beat `.shell-icon` (0,1,0) and drew every
   price with a 62px seashell. Any new currency chip needs the same care. */
.shop-cost{display:inline-flex;align-items:center;gap:3px;margin-top:3px;
  font-size:14.5px;font-weight:800;color:var(--gold-ink)}
.shop-cost img{width:14px;height:14px;max-height:14px;object-fit:contain;filter:none}
.shop-tile[disabled] .shop-cost{color:#8A94A0}
.shop-state{margin-top:3px;font-size:12.5px;font-weight:800;color:var(--sea1,#2E6391)}
.shop-state.on{color:#3F704F}
.shop-state.locked{color:#7C8B99;font-weight:700}
.shop-alt{margin-top:1px;font-size:11px;font-weight:700;color:#7C8B99}

/* ---------- 攀山快答 ---------- */
/* The shell must never exceed the viewport — a scrolling page during a TIMED
   climb is the worst possible moment to lose the question below the fold.
   `min-height:520px` used to win over the height cap on any short screen
   (phone landscape, 1024x600 Chromebook) and pushed the wall off-frame.
   Nothing here may reintroduce a min-height larger than the cap. */
/* 94vh, not 100: a little breathing room under the round (owner 2026-08-14).
   One number to retune, ~88–96. */
.sprint-shell{display:flex;flex-direction:column;gap:8px;height:calc(94vh - 68px);
  max-height:calc(94vh - 68px);min-height:0;overflow:hidden}
.sprint-shell{height:calc(94dvh - 68px);max-height:calc(94dvh - 68px)}   /* Safari 15.4+: 100vh includes the URL-bar strip */
/* if the options still cannot fit, THIS column scrolls — never the page */
.sprint-right{min-height:0;overflow-y:auto}
.sprint-canvas{width:100%;flex:1 1 auto;min-height:120px;border-radius:16px;display:block;
  box-shadow:0 6px 20px rgba(23,58,90,.18);image-rendering:pixelated}
.sprint-hud{display:flex;align-items:center;gap:16px;font-size:14px;color:#3A5A70;flex-wrap:wrap}
.sprint-hud b{color:var(--ink);font-size:16px}
.sprint-timer{flex:1;min-width:120px;height:10px;border-radius:999px;background:#DCE8F1;overflow:hidden}
.sprint-timer-fill{height:100%;width:100%;border-radius:999px;background:linear-gradient(90deg,var(--gold),#D98B2B)}
.sprint-q{padding:14px 16px}
.sprint-q .sq-prompt{font-size:17.5px;line-height:1.65;color:var(--ink);margin-bottom:11px;min-height:44px}
/* single column, always (owner 2026-08-14): a 2x2 grid halves the tap target
   width and puts two options side by side under a thumb during a timed round. */
.sopts{display:grid;grid-template-columns:1fr;gap:9px}
.sopt{background:#fff;border:1.5px solid var(--card-line);border-radius:12px;padding:11px 13px;
  font-size:18px;text-align:left;display:flex;align-items:center;gap:9px;cursor:pointer;touch-action:manipulation}
.sopt .letter{width:24px;height:24px;border-radius:50%;background:#EAF2F8;color:#3A5A70;
  font-size:12px;display:grid;place-items:center;flex:none;font-weight:700}
.sopt.right{background:#E8F3EC;border-color:var(--ok)}
.sopt.wrong{background:#F9ECE9;border-color:var(--bad)}
.sopt .py{font-size:11px;color:var(--ink-soft);margin-left:6px}
.sprint-stats{display:flex;gap:22px;font-size:14.5px;color:#3A5A70;margin:14px 0 4px}
.sprint-stats b{color:var(--gold-ink)}   /* inherits 14.5px — small text, needs 4.5:1 */

/* ---------- 组字成词 ---------- */
/* 2026-08-15: targets are 2–8 characters now, so the answer row must wrap
   rather than run off a phone */
.asm-slots{display:flex;flex-wrap:wrap;gap:9px;justify-content:center;margin:14px 0}
.asm-slot{width:52px;height:52px;border:2px dashed #B9CEDD;border-radius:12px;background:#F7FAFC;
  display:grid;place-items:center;font-family:var(--serif);font-weight:900;font-size:27px;color:var(--ink)}
.asm-slot.filled{border-style:solid;border-color:var(--gold);background:#FDF6E4}
/* columns come from asmCols() so the grid is always a filled rectangle; width
   follows the column count so 4-wide does not squeeze the tiles */
.asm-chips{display:grid;grid-template-columns:repeat(var(--asm-cols,3),minmax(0,1fr));gap:9px;
  width:min(96%,calc(var(--asm-cols,3) * 104px));margin:0 auto}
.asm-chip{min-height:56px;padding:4px 2px;border:1.5px solid var(--card-line);border-radius:12px;background:#fff;
  font-family:var(--serif);font-weight:700;font-size:25px;color:var(--ink);cursor:pointer;
  display:flex;flex-direction:column;align-items:center;justify-content:center;line-height:1.15}
/* 拼音 under a tile: sans, small, never big enough to compete with the character */
.asm-py{font-family:var(--sans);font-weight:600;font-size:11px;letter-spacing:.02em;
  color:var(--ink-soft);margin-top:2px}
.asm-chip.used{opacity:.28;pointer-events:none;background:#EAF2F8}
.asm-chip.shake{animation:shake .3s}
/* the effective board size for THIS word, shown under the slider when a long
   target pushes the count past the student's own setting */
.asm-eff{margin-top:6px;font-size:12px;font-weight:700;color:var(--gold-ink)}
/* §2.4: past 20 tiles a phone gets a fixed 4-column scroller. Four columns on a
   375px screen still leaves ~84px per tile, so the tap target stays well over
   the 44px floor — the thing that must not be traded away as tiles multiply. */
@media (max-width:560px){
  .asm-slot{width:44px;height:44px;font-size:23px}
  .asm-chips.many{grid-template-columns:repeat(4,minmax(0,1fr));width:100%;
    max-height:46vh;overflow-y:auto;padding:2px;-webkit-overflow-scrolling:touch}
}

.sq-row{display:flex;align-items:flex-start;gap:10px}
.sq-row .sq-prompt{flex:1}

/* landscape split (tablets+): canvas left, HUD+question right, single-column
   options — the actual tap-target win (halving width at 1fr 1fr buys nothing). */
@media (min-width:820px) and (orientation:landscape){
  .sprint-shell{flex-direction:row;align-items:stretch;gap:14px}
  /* min-width:0 is load-bearing: a <canvas> carries its width ATTRIBUTE as an
     intrinsic min size, and a flex item defaults to min-width:auto, so without
     this the canvas refuses to shrink, overflows the row and pushes
     .sprint-right (the question!) off-screen. Worse, resize() then measures the
     overflowed box and writes an even larger width attribute each frame. */
  /* Space split reweighted 2026-08-14 (owner): the wall was taking roughly half
     the row while the question sat in a narrow column with small type. The wall
     is scenery — it carries no information the student has to read — so it is
     now capped and the question column takes the rest. */
  /* ⚠️ Do NOT cap the wall's HEIGHT. An earlier pass capped both axes to make it
     "smaller" and the result was a squat box that zoomed the tile and clipped the
     climber (owner: "I can't even see the full avatar"). What was actually too big
     was its WIDTH. So: narrow column, FULL height — the wall spans the shell top to
     bottom on the left, question and options take the rest on the right.
     align-self must stay stretch (the flex default here) for that to hold. */
  /* Reweighted again 2026-08-16 (owner, on an iPad): the answers are almost always
     2-4 characters, so full-width bars left a huge empty gutter inside each one and
     the screen read like a form rather than a game. The wall grows, and the question
     card is CAPPED and centred so prompt and options share one tidy width.
     ⚠️ Still only the WIDTH axis on the wall — see the height warning above. */
  .sprint-canvas{width:auto;flex:0 0 clamp(300px,42%,620px);min-height:0;min-width:0;align-self:stretch}
  .sprint-right{flex:1 1 auto;display:flex;flex-direction:column;gap:10px;min-width:0;
    justify-content:center;align-items:center}
  /* the cap lives on the CARD, not on .sopts: capping the bars alone would leave
     them floating inside a much wider panel, which looks like a bug rather than a
     choice. min-width:0 so a long prompt still wraps instead of widening the column. */
  .sprint-right>*{width:100%;max-width:620px;min-width:0}
  .sprint-aid{margin-top:2px}
  .sprint-q{padding:20px 24px}
  .sprint-q .sq-prompt{font-size:clamp(22px,2.1vw,30px);line-height:1.5;margin-bottom:18px;min-height:0}
  .sopts{gap:12px}
  .sopt{padding:18px 22px;font-size:clamp(21px,1.9vw,27px);border-radius:14px}
  .sopt .letter{width:32px;height:32px;font-size:15px;flex:none}
  .sopt .py{font-size:13px}
  .opt-tts{transform:scale(1.12)}
}

.sq-row .tts{flex:none;margin-top:2px}

/* ===== 我的词山 · Phase 3 ===== */
.mini-horizon{cursor:pointer;position:relative}
.mtn-enter{position:absolute;right:10px;bottom:8px;background:rgba(18,34,26,.74);color:#FFF3D8;
  font-size:12px;padding:4px 10px;border-radius:999px;pointer-events:none}
.mtn-shell{position:relative;height:calc(100vh - 92px);min-height:480px;border-radius:16px;
  overflow:hidden;background:#2E5FA8}
.mtn-canvas{width:100%;height:100%;display:block;touch-action:none;cursor:grab}
.mtn-canvas:active{cursor:grabbing}
.mtn-hud{position:absolute;top:10px;left:10px;right:10px;display:flex;gap:8px;align-items:center;
  flex-wrap:wrap;pointer-events:none;z-index:2}
.mtn-pill{background:rgba(15,30,50,.62);color:#fff;border:0;border-radius:999px;padding:6px 12px;
  font-size:13px;pointer-events:auto}
.mtn-pill b{color:#F5C443}
button.mtn-pill{cursor:pointer}
button.mtn-pill:active{transform:scale(.96)}
.mtn-goalbar{position:absolute;left:10px;bottom:10px;background:rgba(15,30,50,.62);color:#FFE9BD;
  border-radius:999px;padding:6px 12px;font-size:13px;z-index:2;pointer-events:none}
.pop-badge{width:28px;height:28px;vertical-align:-7px;margin-right:6px;border-radius:6px}
.chip-wrap{max-height:38vh;overflow:auto;margin-top:10px;-webkit-overflow-scrolling:touch}
.wchip{display:inline-block;margin:3px;padding:5px 11px;border-radius:999px;font-size:13px;
  cursor:pointer;user-select:none}
.wchip.got{background:linear-gradient(180deg,#F5C443,#D99A1B);color:#4A3305;font-weight:600;
  border:1.5px solid #B8821A}
.wchip.not{background:transparent;border:1.5px dashed #A8B4C0;color:#6B7885}
/* was #8A94A0 — near-invisible on a glass card over bright art */
.pop-hint{margin-top:8px;font-size:12px;color:#4E6273}
/* ===== 我的词山 landmark card (owner 2026-08-22) ===== */
/* ⚠️ ONE scroller around all the 板块 groups. .chip-wrap carries its own 38vh box,
   so a four-板块 unit would otherwise render four separate little scroll boxes. */
.pop-groups{max-height:44vh;overflow:auto;-webkit-overflow-scrolling:touch;margin-top:6px}
.chip-wrap.flat{max-height:none;overflow:visible;margin-top:4px}
.pop-sub{display:flex;align-items:center;gap:7px;margin-top:13px}
.pop-sub:first-child{margin-top:2px}
.pop-badge.sm{width:22px;height:22px;vertical-align:0;margin:0;flex:0 0 auto}
.pop-sub-n{font-weight:800;font-size:13.5px;color:var(--ink);min-width:0}
/* ⚠️ --gold-ink, not --gold-deep: this is small type on a light card (§7) */
.pop-sub-c{margin-left:auto;flex:0 0 auto;font-size:12.5px;font-weight:800;color:var(--gold-ink)}
/* label + number. The glossed span's visible Chinese is EXACTLY the PY_LAB key
   (§10); the number rides alongside and is never part of the key. */
.pop-stat{margin-top:5px}
.pop-k{display:inline-block;vertical-align:top;margin-right:9px;
  font-size:12.5px;font-weight:700;color:#3A5A70}
.pop-v{display:inline-block;font-size:14.5px;color:#41576A}
.pop-v b{font-size:16px;color:var(--ink)}
.pop-t-k{font-weight:900}
/* 单元 rows inside a 关卡 card — the map's small chapter circles moved in here
   (owner 2026-08-22). A row can say the unit's NAME, its theme and its count; a 19px
   dot on the trail could say none of the three without being hovered one at a time. */
.pop-units{display:flex;flex-direction:column;gap:6px;margin-top:8px;
  max-height:38vh;overflow:auto;-webkit-overflow-scrolling:touch}
.pop-unit{display:flex;align-items:baseline;gap:10px;width:100%;text-align:left;
  font-family:var(--sans);cursor:pointer;padding:9px 13px;border-radius:12px;
  background:rgba(255,255,255,.72);border:1px solid var(--glass-line);
  box-shadow:var(--glass-edge);transition:transform .12s,box-shadow .12s}
.pop-unit:hover{transform:translateY(-1px);box-shadow:var(--glass-edge),0 4px 12px rgba(23,58,90,.18)}
.pop-unit.done{background:linear-gradient(180deg,rgba(245,196,67,.92),rgba(217,154,27,.92));
  border-color:#B8821A}
.pu-n{font-weight:800;font-size:14px;color:var(--ink);flex:0 0 auto}
/* ⚠️ min-width:0 — the theme is the only elastic column, and without this a long one
   pushes the count off the card instead of ellipsing (§14 flex children do not shrink) */
.pu-t{flex:1 1 auto;min-width:0;font-size:12.5px;color:var(--ink-soft);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.pu-c{flex:0 0 auto;font-size:12.5px;font-weight:800;color:var(--gold-ink)}
.pop-unit.done .pu-t,.pop-unit.done .pu-c{color:#4A3305}
/* ⚠️ word chips finally render their 拼音, on the same body.py-aid gate as every
   other annotation (owner 2026-08-22). ⚠️ The body.py-aid prefix is load-bearing:
   a bare `.wchip .pylab` is (0,2,0) — it beats the display:none half and LOSES to
   `body.py-aid .pylab` (0,2,0, later in this file), so the override would silently
   do nothing. Same trap as §18e. */
body.py-aid .wchip .pylab{font-size:10.5px;line-height:1.25;margin-top:0;letter-spacing:0;
  font-weight:600}
body.py-aid .wchip.got .pylab{color:#553F06}
body.py-aid .wchip.not .pylab{color:#5A6875}
.goal-opts{display:flex;gap:6px;flex-wrap:wrap;margin-top:10px}


/* ===== Landing v2 · 8-bit hero (landscape-first) ===== */
.lp-body{margin:0;min-height:100vh;background:#12213F;font-family:"Noto Sans SC",sans-serif;overflow-y:auto;overflow-x:hidden}
.lp-hero{position:relative;width:100vw;height:100vh;
  background:url("../art/bg/landing_hero_bg.png") center center/cover no-repeat;
  image-rendering:pixelated}
/* ---------- 拼音 / 英文 pills on the landing page (owner 2026-08-17) ----------
   ⚠️ Top-LEFT, not top-right: 学海无涯苦作舟 runs down the left edge at top:max(12vh,78px)
   and the right couplet mirrors it, so the strip has to clear the couplets — and the
   couplets are the one thing on this page that must never be covered (§1). At
   top:14px it sits above both. The couplets are hidden entirely below 900px, where
   this drops to the same corner without conflict.
   ⚠️ It is INSIDE .lp-hero (position:relative), so it stays put when the gate is
   replaced by the sea map — the screens a non-Chinese reader most needs it on come
   after the gate, not before it. */
/* ⚠️ position:fixed with z-index 21, ABOVE .lp-sea (position:fixed, z-index 20).
   As an absolutely-positioned child of .lp-hero it was painted underneath the sea
   map the moment the gate opened — and the sea map is one of the two screens a
   non-Chinese reader most needs this on. It has to outrank the map, not sit in it. */
.lp-aid{position:fixed;left:14px;top:14px;z-index:21;display:flex;gap:6px;flex-wrap:wrap}
.lp-aid .tb-en,.lp-aid .tb-py{margin-left:0;height:38px}
/* ⚠️ and it moves out of the way of 返回首页, which claims the same corner while the
   map is up (.sea-back is left:14px/top:14px too). Top-right is free there: the
   couplets that own the right edge are hidden under .lp-sea-on. */
body.lp-sea-on .lp-aid{left:auto;right:14px}
@media (max-width:900px){.lp-aid{left:10px;top:10px}
  body.lp-sea-on .lp-aid{left:auto;right:10px}}
/* the profile panel's own pair, shown only where nothing else owns the gates */
.prof-aid{display:flex;gap:6px;flex-wrap:wrap;margin:-4px 0 12px}
.prof-aid .tb-en,.prof-aid .tb-py{margin-left:0;height:38px}
.lp-logo{position:absolute;left:50%;top:18vh;transform:translateX(-50%);width:min(44vw,560px);
  image-rendering:pixelated;filter:drop-shadow(0 4px 10px rgba(10,20,45,.55));
  animation:lpLogoFloat 4.2s ease-in-out infinite;will-change:transform}
/* undulate: gentle vertical bob + a slight breathing scale, like a title card
   floating over the sea. translateX(-50%) must be re-declared in every frame
   or the logo jumps out of centre. */
@keyframes lpLogoFloat{
  0%,100%{transform:translateX(-50%) translateY(0) scale(1)}
  50%    {transform:translateX(-50%) translateY(-11px) scale(1.014)}}
@media (prefers-reduced-motion:reduce){.lp-logo{animation:none}}
.lp-school{position:absolute;left:50%;top:calc(18vh + min(44vw,560px)*.375 + 14px);transform:translateX(-50%);
  color:#FFF4D6;font-size:15px;font-weight:500;text-shadow:0 1px 4px rgba(15,25,50,.9);white-space:nowrap}
/* 12vh normally, but never higher than 78px from the top: on a short landscape
   screen (1024x600 Chromebook) 12vh would put the right couplet level with the
   top-right 我的档案 pill. Both couplets share the floor so they stay symmetric. */
.lp-couplet{position:absolute;top:max(12vh,78px);writing-mode:vertical-rl;letter-spacing:.42em;
  font-family:"Noto Serif SC",serif;font-weight:900;font-size:clamp(22px,2.8vw,34px);color:#FFE9B0;
  text-shadow:0 0 3px rgba(18,28,52,1),0 0 10px rgba(18,28,52,.9),0 3px 8px rgba(18,28,52,.85)}
.lp-couplet.left{left:1.6vw}
.lp-couplet.right{right:1.6vw}
/* .lp-logo/.lp-school/.lp-couplet are position:absolute (out of flow), so
   without this spacer the first normal-flow child (gate, or greeting/cards
   once the gate is hidden) stacks at the TOP of .lp-hero's box instead of
   below the hero art. This reserves that same height so it never happens,
   regardless of which of gate/greeting/cards is the visible one. */
.lp-hero-spacer{display:none}   /* v0.4: gate/cards float over the full-height hero, no dark band */
/* single entry point: 这颗按钮要先被按过，才轮到昵称与路线。
   ⚠️ **2026-08-23 owner 推翻了原来的 16-bit 造型**（「don't make it so boxy -
   make it rounded and glowing」），文案同时从「进入词山学海」改成「开始旅程」。
   原注释写的是「hard edges, no border-radius, stepped/chunky box-shadow
   instead of soft blur, pixelated arrow glyph」——那是刻意的，现在被刻意换掉了，
   **不要当成回归再改回去**。
   ⚠️ 画面本身仍然是像素画，所以这颗按钮是**整张落地页唯一一件圆润发光的东西**。
   那不是不一致，那是它的工作：它是这一屏唯一要被按下去的地方，
   而「唯一一件长得不一样的东西」正是最省事的指路方式。
   ⚠️ 发光靠 box-shadow 的两层外扩，**不用 ::after 光环**：这颗按钮自己有背景，
   `z-index:-1` 的伪元素会被它自己的背景盖掉（试过）。 */
.lp-gate{position:absolute;right:7vw;bottom:15vh;margin:0;text-align:center;z-index:6}
.lp-enter-btn{
  display:inline-block;border:0;cursor:pointer;touch-action:manipulation;
  font-family:"Noto Serif SC",serif;font-weight:900;
  font-size:clamp(22px,2.6vw,30px);letter-spacing:.10em;
  padding:19px 58px;color:#4A3305;
  border-radius:999px;                       /* 药丸形，不再有直角 */
  /* ⚠️ `image-rendering` 是**继承属性**，落地页整页是像素画所以上层设了 pixelated；
     这颗按钮现在要的是柔和渐层与光晕，必须显式改回 auto，
     否则渐层可能被按像素放大规则处理，圆角边缘也更容易出锯齿。 */
  image-rendering:auto;
  /* 顶部亮、底部沉的径向渐层：圆角按钮用线性渐层会显得平，因为边缘没有受光变化 */
  background:radial-gradient(125% 165% at 50% -10%,
    #FFF3C4 0%, #FFE07A 24%, #F5C443 56%, #E2A828 80%, #CE8C12 100%);
  box-shadow:
    0 1px 0 rgba(255,255,255,.8) inset,        /* 上缘一线高光 */
    0 -12px 20px rgba(150,98,8,.30) inset,     /* 下缘内阴影，给一点厚度 */
    0 6px 16px rgba(18,11,2,.34),              /* 落影：让它浮在画面之上 */
    0 0 0 2px rgba(255,238,176,.92),           /* 细金圈，与背景分离 */
    0 0 20px 3px rgba(255,206,84,.55),         /* 近层光 */
    0 0 52px 12px rgba(255,190,60,.30);        /* 远层光 */
  transition:transform .18s cubic-bezier(.2,.8,.3,1), box-shadow .22s ease;
  animation:lpEnterGlow 2.8s ease-in-out infinite}
.lp-enter-btn:hover{transform:translateY(-3px) scale(1.02);
  box-shadow:
    0 1px 0 rgba(255,255,255,.9) inset,
    0 -12px 20px rgba(150,98,8,.26) inset,
    0 10px 22px rgba(18,11,2,.38),
    0 0 0 2px rgba(255,244,200,.98),
    0 0 28px 6px rgba(255,212,100,.72),
    0 0 74px 20px rgba(255,196,66,.42);
  animation:none}
.lp-enter-btn:active{transform:translateY(1px) scale(.99);
  box-shadow:
    0 -6px 14px rgba(150,98,8,.34) inset,
    0 3px 9px rgba(18,11,2,.34),
    0 0 0 2px rgba(255,238,176,.9),
    0 0 16px 2px rgba(255,206,84,.5);
  animation:none}
/* 呼吸的是**光**，不是亮度：brightness 会把字一起提亮，看着像闪烁；
   只放大外面那两层光晕，字始终一样清楚。 */
@keyframes lpEnterGlow{
  0%,100%{box-shadow:
    0 1px 0 rgba(255,255,255,.8) inset,
    0 -12px 20px rgba(150,98,8,.30) inset,
    0 6px 16px rgba(18,11,2,.34),
    0 0 0 2px rgba(255,238,176,.92),
    0 0 20px 3px rgba(255,206,84,.55),
    0 0 52px 12px rgba(255,190,60,.30)}
  50%{box-shadow:
    0 1px 0 rgba(255,255,255,.86) inset,
    0 -12px 20px rgba(150,98,8,.30) inset,
    0 6px 16px rgba(18,11,2,.34),
    0 0 0 2px rgba(255,242,192,.96),
    0 0 30px 7px rgba(255,214,104,.72),
    0 0 76px 22px rgba(255,196,66,.44)}}
@media (prefers-reduced-motion:reduce){.lp-enter-btn{animation:none}}

/* chosen nickname: was a faint 14px line that vanished against the artwork.
   Now a solid plaque so students can actually see who they are.
   2026-08-13: moved OUT of the centre (it covered the pixel climbers on the
   path) into the top-right corner as a compact rounded pill. It sits ABOVE
   the right couplet (which starts at top:12vh), so the two never collide. */
.lp-greeting{position:absolute;left:auto;right:1.6vw;top:14px;bottom:auto;transform:none;z-index:7;
  display:flex;justify-content:flex-end;align-items:center;
  gap:10px;flex-wrap:nowrap;margin:0;width:max-content;max-width:56vw;
  background:rgba(12,24,48,.88);border:2px solid var(--gold,#D9A72B);border-radius:999px;
  padding:7px 12px 7px 9px;box-shadow:0 0 0 2px #2A1B04,0 6px 16px rgba(8,16,34,.45)}
/* ⚠️ 这里**不能**用 flex（owner 2026-08-24 的截图：「出类拔萃·墨客」只剩「出类拔萃」，
   没有省略号，右边的 查词语 还压在上面）。
   原因：`.lp-nick` 曾是 `display:inline-flex`，而昵称是它的一个**匿名文字节点**——
   `text-overflow:ellipsis` **对 flex 容器里的匿名文字不起作用**，
   于是 overflow:hidden 变成硬切，一个字都不剩地切在半个字上。
   改回行内块：头像用 `inline-grid` + `vertical-align` 对齐（不再靠 flex 的 gap），
   文字回到正常行内流，省略号才真的会出现。
   ⚠️ `min-width:0` 也要：作为 .lp-greeting 的 flex 子项，
   默认 `min-width:auto` 会让它拒绝收缩，宁可把兄弟节点挤出去。 */
.lp-greeting .lp-nick{font-family:"Noto Serif SC",serif;font-weight:900;
  font-size:clamp(14px,1.35vw,17px);color:#FFE9B0;letter-spacing:.03em;
  text-shadow:0 2px 0 rgba(8,16,34,.9);display:block;min-width:0;flex:0 1 auto;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.lp-greeting .lp-av{width:30px;height:30px;border-radius:50%;flex:none;overflow:hidden;
  display:inline-grid;vertical-align:middle;margin-right:8px;
  place-items:center;background:rgba(255,255,255,.14);border:2px solid var(--gold,#D9A72B)}
.lp-greeting .lp-av img{width:100%;height:100%;object-fit:contain;display:block}
.lp-greeting .code-link{background:rgba(20,40,70,.75);border-color:#8FD3FF;color:#8FD3FF;
  box-shadow:0 2px 0 0 rgba(20,40,70,.9);border-radius:999px;font-size:13px;
  padding:5px 12px;margin-top:0;white-space:nowrap;touch-action:manipulation}
/* cards sit BELOW the hero photo (not overlaid), so the climbers in the
   artwork are never cropped/hidden behind the card row */
.lp-cards{position:absolute;left:50%;bottom:8vh;transform:translateX(-50%);display:flex;gap:14px;
  width:min(96vw,1140px);margin:0;z-index:6}
.lp-card{flex:1;display:flex;align-items:center;gap:12px;background:rgba(14,27,54,.85);
  border:2px solid var(--gold,#D9A72B);border-radius:14px;padding:14px 14px;text-decoration:none;
  transition:transform .15s,box-shadow .15s;min-width:0;touch-action:manipulation}
.lp-card:hover{transform:translateY(-4px);box-shadow:0 10px 24px rgba(8,16,36,.55);background:rgba(20,38,74,.92)}
.lp-plq{flex:none;width:64px;height:76px;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAlgAAACxCAMAAAAvbDONAAABgFBMVEXdpFtXIw7kmRygZyb6yyfr16PbYRGfnpT53GgeEBGWeFEYUJp0Sxn17tENAwwbMEVqbFOwlF83U2OiIgENAQ08QzNadoILAQvKuZ68xMN8kZe7wb3oOQAQPYK1iCx6gmGzvcAhbMjHekQAAAAIBQePRwTPNwCoWAIONW9UWUlyNwcyNjC1ZgJPJgVOZm+1JwDqyIRGRzgUFRMmKifHeALZQwDV19KNl5VmaE8IJ1RbYkavtrNxhYxwdnAvFwj6+fPELQBNVDwIGC5nfITThgKaUwOEPAzGysfZtm/nw3qrWCQzR1AvJRRbcnqYpaf756+LhnP71lAYJSyNGAFnKwgLLWOuh0pmaWjq6eX85o1LS0Skqqrx6NDpmAJGXGb0tw8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACuQ9txAAAAYHRSTlP//v/+/////v/z///+/5n+/v///1///yz//////////////wD+//////7//v/+/////v7+//////7//////v7///7+///////////+/v///////v/////+///+///////v3uB5AACGa0lEQVR42u29e2MS2dL23Yc0DQxISIwSdZz93APBpoGEBmmSpsEcCEYIIYgJAUK+/7d4q2qt1QcgMTrO4Y937dnqREex+VF11XFJf/6a8/rFixfZbPbln/+t84LOf+1FZenE/luvKsZe1etf9NtJv+a3eZnl5z9FlnhVsdf/Qa6y2Rf/oZf1OvaL38FfAtbrbLY/rVT6/VLrP/Qmvn6RNadTs1qtWv8d6wAvKlvRh8NEQW4BWv8h2A331qia1V9lSaVfBLvbbrfVyFkh+195WvSqZvCq7Kbd+8+YUrJWNrwqKRI5/89YeLLs3XZ70og3rF/0oqRf86wArHlSLdYKzMb/JwwDfwv1U3v6H3lV9KJa8KrmyXheVbP/jZfFBUOmPa9v3jbMX/Si/ipYr/FlHDuZ+lUyqXaKZ+9V61dKwL/yrG6dWX3SbjfLTTBa1X9f0zDYh8n6/GLejhfUSERR/32pxV6Vq9U1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) no-repeat;
  background-size:256px 76px;image-rendering:pixelated}
.lp-plq.p1{background-position:0 0}.lp-plq.p2{background-position:-64px 0}
.lp-plq.p3{background-position:-128px 0}.lp-plq.p4{background-position:-192px 0}
.lp-card div{min-width:0}
.lp-card b{display:block;color:#fff;font-size:19px;font-family:"Noto Serif SC",serif;font-weight:900}
.lp-card .lv{display:block;color:#F5C443;font-size:15px;font-weight:800;margin-top:6px;letter-spacing:.02em}
.lp-foot{position:absolute;left:0;right:0;bottom:0;z-index:5;color:#CBD8EA;
  background:linear-gradient(rgba(8,16,34,0),rgba(8,16,34,.82) 60%);
  font-size:11.5px;text-align:center;padding:16px 10px 8px;line-height:1.7;margin:0}
/* portrait fallback: stack and scroll */

@media (orientation:portrait){
  /* portrait rebuild (feedback U-1/2/3). NB: gate/greeting/cards/foot are all
     CHILDREN of .lp-hero, so the hero can't be a short banner. Instead make it a
     normal flex-column container: the logo sits at the top over the art, and
     everything flows straight down under it. Kills all three bugs at once —
     no per-frame translateX shoving the logo off-screen (animation off, in
     flow), no background-size:auto side-crop (cover), and no stacked 46vh
     margins leaving a screen of blank (natural flow). */
  .lp-body{overflow:auto}
  .lp-hero{position:relative;width:100%;height:auto;min-height:100vh;padding:0 0 26px;
    display:flex;flex-direction:column;align-items:center;
    background:url("../art/bg/landing_hero_bg.png") center top/cover no-repeat;background-color:#12213F}
  .lp-logo{position:relative;left:auto;top:auto;transform:none;display:block;
    margin:5vh auto 0;width:min(72vw,400px);animation:none}
  .lp-school{position:relative;left:auto;top:auto;transform:none;display:block;
    margin:10px auto 0;text-align:center;white-space:nowrap}
  .lp-couplet,.lp-hero-spacer{display:none}
  .lp-gate{position:relative;right:auto;left:auto;bottom:auto;transform:none;margin:24px auto 0}
  /* ⚠️ 竖屏上这条名牌**换行**（owner 2026-08-24 要求整体优化手机版）。
     一行放不下：头像 + 昵称 + 查词语 + 我的档案，在 375px 上留给昵称的只剩约
     75px，六个字的昵称被省略成「出类…」——那等于没显示。
     竖屏这一版本来就不缺**纵向**空间（页脚已经贴底，中间大片是背景画），
     所以让昵称独占一行、两颗按钮并排在下。**用高度换可读性，这里换得起。**
     ⚠️ `flex-basis:100%` 才会强制换行；只写 flex-wrap 不够，
     昵称会继续和按钮挤在同一行直到溢出。 */
  .lp-greeting{position:relative;left:auto;right:auto;top:auto;bottom:auto;transform:none;
    margin:24px auto 0;max-width:92vw;padding:10px 14px;
    flex-wrap:wrap;justify-content:center;gap:8px 10px}
  .lp-greeting .lp-nick{font-size:17px;flex:1 1 100%;text-align:center;
    white-space:normal;overflow:visible}
  .lp-greeting .lp-av{width:34px;height:34px}
  .lp-cards{position:relative;left:auto;bottom:auto;transform:none;flex-direction:column;
    width:min(92vw,520px);margin:20px auto 0;gap:12px}
  /* ⚠️ owner 2026-08-24：「the footer notes are smacked in the middle of the screen
     instead of responding dynamically to screen proportions」。
     病因：.lp-hero 是 `min-height:100vh` 的 flex 列，内容（标志＋校名＋按钮＋名牌）
     比一屏矮，于是**多出来的空间全堆在页脚下面**——页脚被顶在半屏高的位置，
     底下留一大片没人要的背景画。
     `margin-top:auto` 把那段空间移到页脚**上面**：内容矮时页脚贴着屏幕底，
     中间的空当正好露出画里那几个登山的小孩；内容高到超过一屏时 auto 收成 0，
     页脚照常跟在后面滚动。**一行解决，不需要任何 JS 或断点。**
     ⚠️ 最小间距靠 padding-top，不能靠 margin-top——margin-top 已经被 auto 占用了。
     ⚠️ align-self:stretch：flex 列的 align-items 是 center，不拉伸的话页脚
     只有文字那么宽，它那条渐层底纹就盖不满整行。 */
  .lp-hero{padding-bottom:0}
  .lp-foot{position:relative;left:auto;right:auto;bottom:auto;
    margin:0 auto;margin-top:auto;align-self:stretch;padding-top:30px}
}


/* ===== 航海选择页 · sea map (replaces .lp-cards as the stream picker) =====
   A fixed full-viewport layer over the landing hero. The hero stays in the DOM
   underneath, so 返回首页 is a class flip rather than a re-render.

   EVERY coordinate is a custom property on an .i-* class here, and nothing is
   set inline in index.html. That is load-bearing: an inline custom property
   beats any stylesheet rule including a media query, which is what silently
   broke the first portrait layout.
     --cx / --by   island centre-x and waterline, from the LEFT / BOTTOM
     --w           island width
     --tx / --ty   where the boat moors when sailing there (open water, and the
                   whole arc from the jetty verified clear of every landmass)

   SIZE ENCODES DIFFICULTY, NOT DISTANCE (design doc v5): G1 smallest → HCL
   largest, while vertical position and the art's own baked-in haze carry
   distance. G3 and HCL therefore sit higher up the frame AND render larger,
   which inverts real perspective on purpose. Do not "correct" it. */
.lp-sea{position:fixed;inset:0;z-index:20;overflow:hidden;
  background:url("../art/seamap/sea_tile.png") left top/cover no-repeat;
  image-rendering:pixelated}
/* left top, not center: the tile carries a warm sun-glint in its upper-left and
   every other asset is lit from the left, so pinning that corner keeps the
   lighting agreeing with the islands at any aspect ratio.
   The tile is 1536x1024 and is NOT downscaled on export — upscaling the sea is
   what made the water read as blocky next to the crisp islands. */
body.lp-sea-on{overflow:hidden}
/* the greeting pill is a child of .lp-hero, which the map now covers. Re-anchor
   it to the viewport with explicit offsets — the portrait block positions it
   in flow, so a bare position:fixed would strand it inside the hidden hero.
   The credits strip stays behind: it is a dark band across the bottom, exactly
   where the dock art sits, and it is one tap away on 返回首页. */
/* ⚠️ HIDDEN on the map, 2026-08-16 (owner: it covered HCL's summit). The pill sat
   top-right, which is exactly where the biggest island's peak is — and unlike the
   credits strip it is interactive, so it also stole taps meant for the island.
   Access is not lost: 返回首页 drops the map (it removes .lp-sea-on) and the pill
   is right there, and every stream page and the pier carry it in the topbar.
   The rule that positions it is kept below so removing this one line restores it. */
body.lp-sea-on .lp-greeting{display:none;
  position:fixed;left:auto;right:1.6vw;top:14px;bottom:auto;
  margin:0;z-index:22;max-width:min(58vw,320px)}
body.lp-sea-on .lp-foot{display:none}

.sea-isle{position:absolute;left:var(--cx);bottom:var(--by);width:var(--w);
  transform:translateX(-50%);background:none;border:0;padding:0;margin:0;
  cursor:pointer;touch-action:manipulation;transition:transform .16s ease-out}
.sea-isle img{display:block;width:100%;height:auto;image-rendering:pixelated}
.sea-isle:hover{transform:translateX(-50%) translateY(-6px)}
.sea-isle:hover img{filter:brightness(1.07)}
/* the button box includes transparent corners, so a default outline would ring
   empty water. Ring the silhouette instead. */
.sea-isle:focus{outline:none}
.sea-isle:focus-visible img{filter:drop-shadow(0 0 4px #FFE9B0) drop-shadow(0 0 12px #F5C443) brightness(1.08)}
.sea-isle:focus-visible .sea-label{box-shadow:0 0 0 3px rgba(245,196,67,.75)}

/* label plaque sits ON the island's flat shore ring (design doc v4 §4), not on
   the water below it — these islands are landmasses with a wide beach, and the
   ring is what that beach is for. Absolute, so it never adds to the button box
   and the hit area stays exactly the island. */
/* ⚠️ --lb pushes the plaque DOWN off the island (0 in landscape, set in portrait).
   The sprites are alpha-trimmed with no bottom padding, so bottom:0 put the whole
   44px plaque ON the mountain and swallowed its lower third — owner 2026-08-16:
   「the section titles are obscuring a good part of the mountain」. */
.sea-label{position:absolute;left:50%;bottom:calc(-1 * var(--lb,0px));transform:translateX(-50%);
  padding:5px 14px 7px;border-radius:12px;background:rgba(31,74,112,.9);
  border:2px solid var(--gold,#E3A63C);box-shadow:0 4px 12px rgba(6,26,48,.45);
  text-align:center;white-space:nowrap;pointer-events:none}
.sea-label b{display:block;font-family:"Noto Serif SC",serif;font-weight:900;color:#fff;
  font-size:clamp(15px,1.5vw,25px);line-height:1.25}
.sea-label .lv{display:block;font-family:"Noto Sans SC",sans-serif;font-weight:700;
  color:#F5C443;font-size:clamp(11px,.92vw,15px);margin-top:2px;letter-spacing:.02em}

/* ---- landscape positions (tuned against composite_test_v5 at 1600x900) ---- */
/* owner 2026-08-15: G1 further right off the pier · G2 further left · G3 in the
   middle of the screen · HCL on the top-right edge.
   --tx/--ty now sit AGAINST each island's painted shore rather than out in open
   water (they are computed by walking out from the island centre until the
   sprite's alpha ends), so the boat moors at the beach.
   ⚠️ Because the boat now STAYS at its last berth, a voyage can start from any
   island — that is 20 routes, not 4, and all 20 were verified clear of land
   with the arc included. Moving any island means re-verifying all of them. */
/* ⚠️ G2 moved OUT, not G1 in (owner 2026-08-16: 「G1 needs the shortest distance
   from the pier, now it looks like G2 is nearer」). Pulling G1 toward the jetty
   either collided with the dock art or broke routes at every position tried;
   pushing G2 out fixes the ordering AND makes distance read as difficulty:
   dock→G1 352 < G2 413 < G3 477 < HCL 803 px at 1600x900.
   ⚠️ --ty moves with --by. All 20 routes re-verified. */
.i-g2  {--cx:11%; --by:59%;  --w:20vw;    --tx:20.5%; --ty:62.1%}
/* ⚠️ G3 up / HCL down, 2026-08-16 (owner). At --by:50% HCL is 512px tall on a
   1920x990 viewport and its summit sat 17px ABOVE the top edge — the peak was
   simply cut off, and the 我的档案 pill covered what was left of it. Lowering HCL
   and raising G3 fits both peaks in frame and keeps the「G3/HCL sit higher」
   distance cue. They never share an x range (G3 701-1218, HCL 1351-1951 at
   1920w), so moving them vertically cannot collide them.
   ⚠️ --ty moves with --by, or the boat moors where the island used to be. */
/* ⚠️ 错落有致 (owner 2026-08-16): G3 up 5%, HCL down 5%. Before this their
   waterlines sat within 6% of each other and the right two thirds of the map read
   as one flat row. --ty moves by the SAME 5% or the boat moors where the island
   used to be. Verified by script: zero overlap, and all 20 landscape voyages clear
   of every third island (that check also turned up a PRE-EXISTING dock↔HCL clip
   through G1 — see SEA_DETOUR in nickname.js). */
.i-g3  {--cx:50%; --by:53%;  --w:26.9vw;  --tx:50%;   --ty:50.8%}
.i-hcl {--cx:86%; --by:37%;  --w:31.25vw; --tx:75.4%; --ty:39.8%}
/* ⚠️ G1 moors at the FOOT, not on the summit (owner 2026-08-15). The berths were
   all generated by walking from the island centre towards the centre of the
   screen until the sprite's alpha ended — which for G1 walks UPWARDS, because
   G1 sits below the middle of the frame. That put the boat on the island's far
   (top) edge, right beside the painted pavilion, so it read as having sailed up
   the mountain. G1's berth is now taken along the traced trail instead: it sits
   just off the beach at MTN_PATHS.g1[0], the point where the climb actually
   begins. All 20 inter-island routes were re-verified clear of third islands
   with this berth in place. */
.i-g1  {--cx:55%; --by:7.8%; --w:24vw;    --tx:48.2%; --ty:8.3%}
/* the dock is drawn to run off the left and bottom frame edges, so it is
   anchored into the corner and allowed to bleed. Inset it and the cut coastline
   reads as a floating slab.
   --by is NEGATIVE on purpose: at 0 the sprite's bottom edge sat exactly on the
   viewport edge, so the shared :hover lift raised the whole landmass and opened
   a strip of bare water underneath it. Sitting 6% below the frame means the
   lift has slack to move into and nothing is ever revealed. */
.i-dock{--cx:12.5%; --by:-6%;   --w:25vw;    --tx:26.8%; --ty:17.6%}
/* the dock has no flat ring to label, and its waterline is off-screen, so its
   plaque is placed on the sand beside the house instead. */
.i-dock .sea-label{left:44%;bottom:26%}

.sea-boat{position:absolute;left:var(--hx,27.8%);bottom:var(--hy,17.3%);width:var(--bw,4.4vw);
  transform:translate(-50%,50%);pointer-events:none;z-index:3;will-change:transform}
.sea-boat img{display:block;width:100%;height:auto;image-rendering:pixelated;
  filter:drop-shadow(0 3px 6px rgba(6,26,48,.35));
  animation:seaBob 1.6s ease-in-out infinite}
/* per-heading widths: the five sprites have very different aspect ratios, so a
   single width would make the head-on boat look twice as fat as the broadside. */
.sea-boat.h-away,.sea-boat.h-toward{--bw:3.4vw}
.sea-boat.h-away_diag,.sea-boat.h-toward_diag{--bw:4.4vw}
.sea-boat.h-broadside{--bw:5.6vw}
.sea-boat.flip img{transform:scaleX(-1)}
@keyframes seaBob{0%,100%{translate:0 0}50%{translate:0 -2px}}
/* the voyage. Animates TRANSFORM, not left/bottom: animating the offsets meant
   a full layout pass every frame, which is what made the sail visibly janky.
   The track is a quadratic Bezier from the berth (0,0) to --dx/--dy with
   control point --ctlx/--ctly, all set in pixels by nickname.js at click time:
       P(t) = 2t(1-t)*C + t^2*E
   Nine samples, because a three-stop arc visibly corners at its midpoint.
   ⚠️ Timing MUST stay linear. Per-keyframe easing would accelerate and brake
   eight separate times over one voyage, which is far worse than the corner it
   replaced. The easing is baked into WHICH t each keyframe samples instead —
   the t values below are ease-in-out, so the boat pulls away gently, cruises,
   and settles, with the interpolation between them left perfectly even.
   Note the transform is two chained translates. The first repeats the element's
   own centring; leave it in every keyframe or the boat jumps half its own width
   the instant the animation starts. */
@keyframes seaSail{
  0%    {transform:translate(-50%,50%) translate(0,0)}
  12.5% {transform:translate(-50%,50%) translate(
         calc(var(--ctlx,0px)*.0605 + var(--dx,0px)*.0010),
         calc(var(--ctly,0px)*.0605 + var(--dy,0px)*.0010))}
  25%   {transform:translate(-50%,50%) translate(
         calc(var(--ctlx,0px)*.2188 + var(--dx,0px)*.0156),
         calc(var(--ctly,0px)*.2188 + var(--dy,0px)*.0156))}
  37.5% {transform:translate(-50%,50%) translate(
         calc(var(--ctlx,0px)*.4043 + var(--dx,0px)*.0791),
         calc(var(--ctly,0px)*.4043 + var(--dy,0px)*.0791))}
  50%   {transform:translate(-50%,50%) translate(
         calc(var(--ctlx,0px)*.5000 + var(--dx,0px)*.2500),
         calc(var(--ctly,0px)*.5000 + var(--dy,0px)*.2500))}
  62.5% {transform:translate(-50%,50%) translate(
         calc(var(--ctlx,0px)*.4043 + var(--dx,0px)*.5166),
         calc(var(--ctly,0px)*.4043 + var(--dy,0px)*.5166))}
  75%   {transform:translate(-50%,50%) translate(
         calc(var(--ctlx,0px)*.2188 + var(--dx,0px)*.7656),
         calc(var(--ctly,0px)*.2188 + var(--dy,0px)*.7656))}
  87.5% {transform:translate(-50%,50%) translate(
         calc(var(--ctlx,0px)*.0605 + var(--dx,0px)*.9385),
         calc(var(--ctly,0px)*.0605 + var(--dy,0px)*.9385))}
  100%  {transform:translate(-50%,50%) translate(var(--dx,0px),var(--dy,0px))}}
.sea-boat.sailing{animation:seaSail 1.7s linear forwards}
.sea-boat.sailing img{animation:none}

.sea-back{position:absolute;left:14px;top:14px;z-index:5;cursor:pointer;
  touch-action:manipulation;font-family:"Noto Sans SC",sans-serif;font-size:14px;font-weight:700;
  color:#FFE9B0;background:rgba(12,24,48,.85);border:2px solid var(--gold,#D9A72B);
  border-radius:999px;padding:8px 16px;box-shadow:0 0 0 2px #2A1B04,0 4px 12px rgba(6,26,48,.4)}
.sea-back:hover{background:rgba(24,46,84,.92)}

/* landscape nudge. Orientation cannot be locked on the web (the Screen
   Orientation API needs fullscreen or an installed app, and this project has
   neither by decision), and CLAUDE.md forbids locking anyway — portrait must
   stay usable. So: a dismissible hint, never a block. */
.sea-rotate{position:absolute;left:50%;bottom:14px;transform:translateX(-50%);z-index:6;
  display:flex;align-items:center;gap:10px;white-space:nowrap;
  background:rgba(12,24,48,.9);border:2px solid var(--gold,#D9A72B);border-radius:999px;
  padding:8px 10px 8px 16px;color:#FFE9B0;font-size:13px;font-weight:700;
  box-shadow:0 0 0 2px #2A1B04,0 4px 12px rgba(6,26,48,.4)}
.sea-rotate button{background:none;border:0;color:#8FD3FF;font-size:15px;cursor:pointer;
  padding:2px 8px;touch-action:manipulation}
.sea-rotate[hidden]{display:none}

@media (prefers-reduced-motion:reduce){
  .sea-boat img{animation:none}
  .sea-boat.sailing{animation:none}
  .sea-isle{transition:none}}

/* ---- portrait: same scene, re-proportioned so the islands stack up the
   screen. The map still works here, so there is no fallback to .lp-cards.
   Stacked islands leave no clear sailing lane between the jetty and the far
   peaks, so nickname.js drops the voyage in portrait and navigates straight
   away — the design doc's own instruction where a route would cross land. ---- */
@media (orientation:portrait){
  /* --tx/--ty must be re-stated here, not just the positions: the boat now
     parks at its last berth, so a portrait layout inheriting the landscape
     moorings would leave it sitting in open water nowhere near its island. */
  /* ⚠️ ZIGZAG RESTORED, WITH THE CHANNEL KEPT — 2026-08-16 evening (owner: 「the
     revised mobile layout lost the zig zag pattern … and the boat still doesn't
     stop at the shore of each mountain, it stopped at sea」). Both complaints are
     fixed together, and the reasoning matters because the two pull against each
     other:

       · The FIRST portrait layout zigzagged across the full width at 46-66vw.
         G3 and HCL genuinely overlapped on a 390px screen, and no route from the
         jetty to the far peaks stayed on water — which is why sailing was off in
         portrait at all.
       · The SECOND put all four in a COLUMN down the left with a channel down the
         right. Routes worked, but the zigzag was gone and every berth sat far out
         in open water, nowhere near its island.

     This is the third: islands ALTERNATE against the left and right edges (a real
     zigzag), narrower (31-36vw, still a difficulty ladder G1<G2<G3<HCL), so a
     straight 66px channel survives between the two columns at x 42%-59%. Each
     berth then sits in that channel HARD AGAINST its own island's shore — 6px off
     the painted edge, on the side facing the channel — so the boat moors at land
     instead of stopping mid-sea.

     ⚠️ VERIFIED BY SCRIPT, NOT BY EYE: island alpha bounding boxes composited at
     390x780, zero pairwise overlap, and all 20 ordered voyages sampled along the
     quadratic (both arc directions) clear of every THIRD island. Re-run that check
     if ANY --cx / --by / --w / --tx / --ty below changes; a plausible-looking nudge
     is exactly what silently re-blocks a route. */
  /* ⚠️ --lb (label push-down) and G1's --by were SOLVED, not eyeballed: maximise the
     mountain handed back subject to every plaque clearing the island below it by
     >=8px. Result: plaques cover 23px of their island instead of 44, and G1 rises
     25px while its card stays exactly where it was (owner: 「G1 mountain can be
     placed higher and the title card stays the same position」) — which is why G1
     carries a LARGER --lb, 21+25. Re-solve if any --by / --w / label font changes. */
  /* ⚠️ on .sea-isle, NOT on .sea-label: --lb is inherited, so a value set on the
     label element itself would beat the per-island override below. Same element,
     same specificity, so .i-g1 wins purely by coming after this line. */
  .sea-isle{--lb:21px}
  /* ⚠️ --tx/--ty RE-SOLVED after G1 rose: G1's old berth was computed against its
     old position and would now sit just below the island instead of at its shore. */
  .i-g1  {--cx:24%; --by:29.2%; --w:31vw; --tx:45.7%; --ty:32.1%; --lb:46px}
  .i-g2  {--cx:76%; --by:42.5%; --w:33vw; --tx:53.3%; --ty:46.6%}
  .i-g3  {--cx:25%; --by:60%;   --w:34vw; --tx:48.2%; --ty:64.2%}
  .i-hcl {--cx:77%; --by:79.5%; --w:36vw; --tx:52.8%; --ty:84.1%}
  /* ⚠️ 58vw → 48vw: at 58 the dock's painted top reached y=565 on a 390x780 screen
     and swallowed G1's plaque (which sits at 554-598). Shrinking the dock is the
     only lever that does not fight something else — G1 cannot move up (G2's plaque
     is 1px below its head) and the plaque cannot move up (it would cover half the
     island). Owner 2026-08-16: 「on vertical screen G1 wording is blocked」. */
  .i-dock{--cx:21%; --by:-4%;   --w:48vw; --tx:50.5%; --ty:9.8%}
  .i-dock .sea-label{left:50%;bottom:29%}
  .sea-boat{--hx:50.5%;--hy:9.8%}
  /* the pill and 返回首页 both live on the top row of a 390px screen, so the
     pill has to give up its comfortable sizing or the nickname gets clipped */
  body.lp-sea-on .lp-greeting{max-width:min(62vw,238px);padding:5px 8px 5px 6px;gap:6px}
  body.lp-sea-on .lp-greeting .lp-nick{font-size:13px}
  body.lp-sea-on .lp-greeting .lp-av{width:26px;height:26px}
  body.lp-sea-on .lp-greeting .code-link{font-size:12px;padding:4px 9px}
  .sea-back{font-size:13px;padding:7px 12px}
  /* ⚠️ ~40% smaller than the first portrait pass (owner 2026-08-16: 「the boat also
     needs to be proportionally way smaller on mobile」). At 18vw the broadside hull
     was 70px on a 390px screen — wider than the 66px sailing channel it has to fit
     down, so it visually overlapped the islands it was passing. These widths keep
     the hull inside the channel, which is a correctness constraint now, not taste. */
  .sea-boat.h-away,.sea-boat.h-toward{--bw:7vw}
  .sea-boat.h-away_diag,.sea-boat.h-toward_diag{--bw:9vw}
  .sea-boat.h-broadside{--bw:11vw}
  /* ⚠️ the plaque's HEIGHT is the other half of「stop covering the mountain」:
     --lb can only push it down until it reaches the island below (8px clearance,
     already at the limit), so anything further has to come from the plaque itself.
     Measured 51px before this; the trim takes it to ~44px, all of it handed back
     to the mountain. Do not re-inflate the padding without re-solving --lb. */
  .sea-label{padding:3px 9px 4px;border-width:1.5px}
  .sea-label b{font-size:15.5px;line-height:1.18}
  .sea-label .lv{font-size:10.5px;margin-top:1px}}


/* ⚠️ SHORT LANDSCAPE (a phone on its side, ~390-520px tall). The islands are sized
   in vw, so on a wide-but-short viewport G3 grew taller than the screen and its
   peak was clipped off the top — measured top:-2px at 844x390 (owner 2026-08-16:
   「on mobile landscape G3 mountain top is partially blocked」). Narrow them so
   their HEIGHT fits; the layout and all the berths are unchanged. */
@media (orientation:landscape) and (max-height:560px){
  .i-g1  {--w:17vw}
  .i-g2  {--w:14vw}
  .i-g3  {--w:19vw}
  .i-hcl {--w:22vw}
  .i-dock{--w:18vw}
  .sea-label{--lb:10px}
  .sea-label b{font-size:13px}
  .sea-label .lv{font-size:9.5px}
}

/* ===== study ambience: campsite backdrop under all app pages ===== */
/* ⚠️ QUIET BACKGROUND since 2026-08-16 (handoff §3). The stream pages used to
   carry study_bg.png here AND applyAmbience painted bg-01..05 over it; now the
   earned plate renders once, inside 地景横幅, and the page itself steps back so the
   banner can be the focal element. It also removes the pressure that raised
   .pop-card alpha to .95 over busy art — but do NOT lower that alpha in the same
   change: the handoff wants this verified on its own first. */
body{background:linear-gradient(180deg,#F4F9FC 0%,#E8F1F8 46%,#DCEAF4 100%) fixed}
/* ⚠️ applyAmbience() sets ONLY background-image inline, so the plate arrives with
   background-size:auto and TILES the moment the viewport is wider than the art —
   owner 2026-08-16 saw the mountain repeat down the right edge of a wide screen.
   The size/repeat/position live here rather than in the inline style: an inline
   background-image cannot override these, so any future caller gets them free.
   cover, not contain: this is a backdrop, and letterboxing a backdrop looks like
   a bug. fixed keeps one image behind the whole scroll instead of one per screen. */
body{background-repeat:no-repeat;background-size:cover;background-position:center top;
  background-attachment:fixed}
/* the home card now shows this stream's OWN island (the one the student sailed
   to on the landing sea map) rather than the shared landing hero. contain, not
   cover: these are transparent island sprites, and cropping one would cut the
   summit hut that identifies the stream. The sea sits behind it. */
.mh-img{position:absolute;inset:0;width:100%;height:100%;object-fit:contain;
  object-position:center 78%;image-rendering:pixelated}
.mini-horizon .app-zh,.mini-horizon .mtn-enter{z-index:2}

/* ===== 我的词山 · Phase A exploration ===== */
.mtn-joy{position:absolute;left:16px;bottom:52px;width:64px;height:150px;border-radius:999px;
  background:rgba(15,30,50,.4);border:1.5px solid rgba(255,255,255,.35);touch-action:none;z-index:3}
.mtn-knob{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);width:44px;height:44px;
  border-radius:50%;background:rgba(245,196,67,.92);box-shadow:0 3px 8px rgba(10,20,40,.4);pointer-events:none}
.mtn-interact{position:absolute;left:50%;bottom:60px;transform:translateX(-50%);z-index:3;
  background:#FFF8E8;border:2px solid var(--gold);border-radius:999px;padding:10px 20px;
  font-size:15px;font-weight:700;color:#5A430F;box-shadow:0 6px 16px rgba(10,20,40,.35);cursor:pointer}
.mtn-interact:active{transform:translateX(-50%) scale(.96)}
.mtn-tip{position:absolute;right:12px;bottom:12px;color:rgba(255,255,255,.75);font-size:11.5px;
  background:rgba(15,30,50,.45);border-radius:999px;padding:4px 12px;z-index:2;pointer-events:none}
@media (hover:hover) and (pointer:fine){ .mtn-joy{display:none} }

/* ===== 个人词语表 (personal word list) ===== */
.wl-entry{display:flex;align-items:center;gap:11px;min-width:0;background:var(--card);border:1px solid var(--card-line);
  border-radius:16px;padding:15px 16px;width:100%;text-align:left;box-shadow:0 6px 20px rgba(23,58,90,.14);
  transition:transform .12s;margin-top:0;cursor:pointer}
.wl-entry:hover{transform:translateY(-2px)}
.wl-entry .flag{width:40px;height:40px;border-radius:12px;display:grid;place-items:center;font-size:21px;
  background:#EAF2FB;border:1px solid #CBDDEC;flex:none}
.wl-entry b{font-size:15.5px;display:block;color:var(--ink);line-height:1.3}
.wl-entry>div{min-width:0}
.wl-entry>div>span{font-size:12.5px;color:var(--ink-soft)}
.wl-entry .go{margin-left:auto;font-size:13px;color:var(--gold-ink);font-weight:700;flex:none}
.wl-wrap{max-width:760px;margin:0 auto;padding:6px 12px 24px}
.wl-head{display:flex;align-items:center;justify-content:space-between;gap:10px;flex-wrap:wrap}
.wl-title{font-family:var(--serif);font-weight:900;font-size:22px;color:var(--ink)}
.wl-streak{background:#FDF3DC;border:1px solid #EFD9A4;border-radius:999px;padding:6px 14px;font-size:14px;color:#7A5B12}
.wl-streak b{color:var(--gold-ink);font-size:16px}
.wl-sub{font-size:13px;color:var(--ink-soft);margin:6px 0 10px}
.wl-filters{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:12px}
.wl-chip{border:1px solid #B9CEDD;border-radius:999px;padding:7px 14px;font-size:13.5px;font-weight:700;
  color:var(--ink-soft);background:#fff;cursor:pointer}
.wl-chip.on{background:var(--gold);border-color:var(--gold);color:#3A2A08}
.wl-actions{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-bottom:12px}
.wl-flash{border:0;border-radius:12px;background:var(--gold);color:#3A2A08;font-weight:700;font-size:15px;
  padding:11px 18px;cursor:pointer;box-shadow:0 3px 0 0 #B8821A}
.wl-flash:active{transform:translateY(1px);box-shadow:none}
.wl-hint{font-size:12px;color:var(--ink-soft)}
.wl-group{font-size:12.5px;font-weight:800;color:#3A5A70;background:#EAF2FB;border-radius:8px;
  padding:6px 10px;margin:10px 0 4px}
.wl-list{display:flex;flex-direction:column;gap:8px}
.wl-row{display:flex;align-items:center;gap:12px;width:100%;text-align:left;background:var(--card);
  border:1px solid var(--card-line);border-radius:12px;padding:11px 14px;cursor:pointer;transition:transform .1s}
.wl-row:hover{transform:translateY(-1px)}
.wl-w{flex:none;min-width:88px}
.wl-w b{font-size:17px;color:var(--ink)}
.wl-py{display:block;font-size:11.5px;color:var(--ink-soft)}
.wl-zh{flex:1;font-size:13px;color:#41576A;overflow:hidden;text-overflow:ellipsis;white-space:normal}
.wl-en{display:block;font-size:11.5px;color:#7A8CA0;font-style:italic}
.wl-status{flex:none;font-size:12px;font-weight:700;border-radius:999px;padding:4px 10px}
.wl-status.s-done{background:#E3F1E6;color:#3F704F}
.wl-status.s-todo{background:#FCEBD0;color:#B7791F}
.wl-status.s-new{background:#EDF2F6;color:#8A94A0}
.wl-empty{text-align:center;color:var(--ink-soft);padding:30px}
/* ---------- 贴入老师的清单 (owner 2026-09-01) ----------
   ⚠️ The textarea reuses .code-ta's proportions rather than .prof-input's: a pasted
   revision list is 20-40 lines, and a one-line field would hide 90% of what the
   student just pasted at the moment they most need to see it. */
.paste-ta{width:100%;box-sizing:border-box;min-height:190px;border:1px solid var(--glass-line);
  border-radius:12px;padding:10px 12px;font-size:15px;line-height:1.9;font-family:inherit;
  color:var(--ink);background:rgba(255,255,255,.62);resize:vertical;margin-bottom:12px;
  box-shadow:inset 0 1px 3px rgba(23,58,90,.08)}
.paste-off{border:1px solid var(--glass-line-2);border-radius:12px;background:rgba(255,255,255,.7);
  color:#3A5A70;font-size:13.5px;font-weight:600;padding:10px 14px;cursor:pointer;
  touch-action:manipulation}
.paste-res{border:1px solid var(--glass-line-2);border-radius:14px;padding:12px 14px;
  background:rgba(255,255,255,.55);margin-top:4px}
.paste-res-h{font-size:15px;font-weight:700;color:var(--ink);margin-bottom:8px}
.paste-res-h b{color:var(--gold-deep)}
/* ⚠️ The misses are LISTED, not counted — a count cannot be acted on, a word can.
   Red-tinted because it is the one thing on this screen that needs a decision. */
.paste-miss{background:rgba(180,74,60,.08);border:1px solid rgba(180,74,60,.3);
  border-radius:11px;padding:9px 11px;margin-bottom:10px;font-size:13px;color:#8E3527}
.paste-x{display:inline-block;background:rgba(255,255,255,.85);border:1px solid rgba(180,74,60,.35);
  border-radius:999px;padding:1px 9px;margin:3px 4px 0 0;font-weight:600}
.paste-miss .wl-hint{margin-top:7px;color:#8E3527;opacity:.85}
/* the banner in ①复习范围 that says the unit chips below are no longer in charge */
.scope-paste{display:flex;align-items:center;gap:8px;flex-wrap:wrap;
  background:#FDF6E4;border:1px solid var(--gold);border-radius:11px;
  padding:8px 11px;margin-bottom:8px;font-size:12.5px;color:#6B4A05}
.scope-paste-t{flex:1 1 160px;min-width:0}
.scope-paste b{color:var(--gold-deep)}
.scope-paste-x{margin-left:auto;border:1px solid var(--gold);border-radius:999px;
  background:rgba(255,255,255,.8);color:#6B4A05;font-size:12px;font-weight:700;
  padding:3px 11px;cursor:pointer;touch-action:manipulation}

/* ===== 排行榜 (leaderboard) + 识别码 ===== */
.lb-wrap{max-width:640px;margin:0 auto;padding:6px 12px 24px}
.lb-toggle{display:flex;gap:8px;margin:10px 0 14px}
.lb-tab{flex:1;border:1px solid #B9CEDD;border-radius:12px;padding:9px 8px;font-size:13.5px;font-weight:700;
  color:var(--ink-soft);background:#fff;cursor:pointer}
.lb-tab.on{background:var(--gold);border-color:var(--gold);color:#3A2A08}
.lb-row{display:flex;align-items:center;gap:12px;border:1px solid var(--card-line);background:var(--card);
  border-radius:12px;padding:10px 14px;margin-bottom:7px}
.lb-row.gold{border-color:#E3A63C}
.lb-row.silver{border-color:#B9C6D2}
.lb-row.bronze{border-color:#CDA678}
/* ---------- 自己那一行 (owner 2026-08-19) ----------
   owner:「the whole bar just stands out from the rest eg a diff colour or it glows」,
   and「I don't want 你 to be there」. The old treatment was a flat #FDF6E4 with a hard
   outline — at arm's length on a class projector that is nearly the same as the plain
   card, which is exactly why the row needed a word bolted to the nickname to be found.
   ⚠️ FOUR SIGNALS, NOT ONE, and that is the point: a gold gradient fill, a solid gold
   border, an outer glow, and a gold bar down the leading edge. Colour alone fails for
   a colour-blind student and fails again on a washed-out projector; the leading bar
   and the weight survive both. This is what makes dropping「· 你」safe.
   ⚠️ Deliberately NOT animated. §7 keeps decoration behind behaviour, a pulsing row on
   a board that is re-rendered on every scope/board switch would fight its own repaint,
   and G1/G2 are on the old iPads where a permanent animation costs battery for nothing.
   ⚠️ inset, not outline: outline does not follow border-radius on older WebKit, so the
   old rule drew a square halo around a 12px-rounded card on exactly the iPads that
   matter. It also sat OUTSIDE the box and clipped against the neighbouring row.
   ⚠️ Tier borders (gold/silver/bronze) are declared ABOVE this, so a 1st-place student
   looking at their own row still gets the 自己 treatment — later rule wins at equal
   specificity (§14). That is the right way round: 「which row is mine」 is the question
   being asked, and the medal is already in the rank column. */
.lb-row.me{border-color:var(--gold);border-width:2px;padding:9px 13px;
  background:linear-gradient(180deg,#FFF6E0,#FDEDC8);
  box-shadow:0 0 0 3px rgba(227,166,60,.22),0 4px 16px rgba(227,166,60,.30);
  position:relative;overflow:hidden}
/* the leading edge bar — the one cue that still reads with colour stripped out */
.lb-row.me::before{content:"";position:absolute;left:0;top:0;bottom:0;width:5px;
  background:linear-gradient(180deg,var(--gold),var(--gold-deep))}
.lb-row.me .lb-id b{color:#5A3D05}
.lb-row.me .lb-alt{color:#8A5A0F}
/* ⚠️ the uid line needs its own ink on this fill. #9FB3C0 is a cool grey meant for a
   white card (already only 2.17:1 there) and drops to 1.87:1 on the gold — on the ONE
   row where it does its actual job, telling two identically-nicknamed students apart.
   A warm grey at 4.20:1 keeps it quiet without making it decorative. */
.lb-row.me .lb-uid{color:#7F7048}
.lb-row.me .lb-school{color:#6B5A2E}
.lb-rank{flex:none;width:58px;font-weight:800;color:var(--ink);font-size:14px}
.lb-id{flex:1;min-width:0}
.lb-id b{font-size:15px;color:var(--ink)}
.lb-school{display:block;font-size:11px;color:var(--ink-soft)}
.lb-uid{display:block;font-size:10px;color:#9FB3C0;font-family:monospace;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.lb-alt{flex:none;font-weight:800;color:var(--gold-ink);font-size:14px}
.lb-gap{text-align:center;color:#9FB3C0;letter-spacing:.3em;margin:4px 0}
.lb-tabs2{display:flex;gap:8px;margin:12px 0 6px}
.lb-tab2{flex:1;border:1px solid #B9CEDD;border-radius:12px 12px 0 0;padding:11px 8px;font-size:15px;
  font-weight:800;font-family:var(--serif);color:var(--ink-soft);background:#fff;cursor:pointer;letter-spacing:.06em}
.lb-tab2.on{background:var(--deep-sea,#1F4A70);border-color:var(--deep-sea,#1F4A70);color:#FFE9BD}
.lb-subtoggle{display:flex;gap:6px;justify-content:center;margin:0 0 8px}
.lb-sub{border:1px solid #B9CEDD;border-radius:20px;padding:5px 16px;font-size:12.5px;font-weight:700;
  color:var(--ink-soft);background:#fff;cursor:pointer}
.lb-sub.on{background:var(--gold);border-color:var(--gold);color:#3A2A08}
.lb-me-line{margin-top:12px;padding:12px 14px;border-radius:12px;background:rgba(18,42,66,.9);
  color:#EAF3FA;font-size:14px;text-align:center;line-height:1.6}
.lb-me-line b{color:var(--sun);font-family:var(--serif);font-size:18px}
.lb-me-rank{display:block;font-size:11.5px;color:#AEC6D8;margin-top:2px}
.lb-note{margin-top:14px;font-size:11.5px;color:#8FA6B4;text-align:center;line-height:1.6}
/* rail 历练值 + 连对 multiplier */
.rail-pts{display:flex;align-items:center;gap:8px;font-size:14px;color:var(--ink-soft);margin-top:6px}
.rail-pts b{font-family:var(--serif);font-size:19px;color:var(--gold-deep)}
.streak .mult{font-family:var(--serif);font-weight:900;font-size:16px;color:var(--gold-ink);
  background:#FDF1D4;border:1px solid var(--gold);border-radius:8px;padding:1px 8px;margin-left:2px}
.streak .mult.pop{animation:multPop .5s ease}
@keyframes multPop{0%{transform:scale(1)}30%{transform:scale(1.45);color:#B5760C}100%{transform:scale(1)}}
.pts-gain{display:inline-block;margin-left:10px;font-family:var(--serif);font-weight:800;font-size:15px;
  color:var(--gold-ink);animation:ptsGain .6s ease}
@keyframes ptsGain{0%{opacity:0;transform:translateY(6px)}40%{opacity:1;transform:translateY(0)}100%{opacity:1}}
/* 段位 rank pill on the mini-horizon */
.mtn-rank{position:absolute;left:10px;top:10px;z-index:3;background:rgba(18,34,26,.76);color:#FFF3D8;
  border:1px solid rgba(255,233,189,.35);border-radius:12px;padding:6px 11px;font-size:12.5px;font-weight:700;
  display:flex;flex-direction:column;gap:1px;line-height:1.3;max-width:60%}
.mtn-rank-next{font-size:11px;font-weight:500;opacity:.92}
/* 结伴登峰 / 同伴挑战 pills on the home mini-horizon.
   Both used to be position:absolute at left:10px;bottom:8px — i.e. stacked
   exactly on top of each other. They now share ONE absolutely-positioned flex
   row, so adding a third pill later cannot reintroduce the overlap. */
.mtn-rooms{position:absolute;left:10px;bottom:8px;z-index:3;display:flex;gap:8px;
  flex-wrap:wrap;max-width:calc(100% - 20px)}
.mtn-arena{background:rgba(227,166,60,.92);color:#3A2A08;
  border:1px solid #FFE9B0;border-radius:12px;padding:6px 12px;font-size:12.5px;font-weight:800;
  cursor:pointer;box-shadow:0 2px 0 0 #A9791C;white-space:nowrap;touch-action:manipulation}
.mtn-arena:active{transform:translateY(1px);box-shadow:none}
@media (max-width:420px){.mtn-arena{font-size:11.5px;padding:5px 9px}}
.camp-uid{margin-top:12px;padding-top:12px;border-top:1px solid #E4EDF4;font-size:12.5px;color:var(--ink-soft)}
.camp-uid code{font-family:monospace;font-size:11px;color:#3A5A70;word-break:break-all}
.uid-copy{margin-left:6px;border:1px solid var(--gold);background:#FDF6E4;color:#5C4009;border-radius:8px;
  padding:3px 10px;font-size:12px;font-weight:700;cursor:pointer}

/* ===== 我的词山 · static illustrated mountain (v0.4 redesign 2026-08-10) ===== */
.mtn2-wrap{position:relative;padding:4px 0 14px}
/* the stage takes ITS STREAM'S island aspect ratio (--ar, set inline by
   startMountain) so the art fills the frame exactly and the traced trail
   fractions stay in sprite space. Width is derived from the height cap rather
   than capped separately, because a max-width alongside an explicit
   aspect-ratio squashes the frame instead of shrinking it. */
.mtn2-stage{position:relative;aspect-ratio:var(--ar,1.6);
  width:min(96vw,calc(70vh * var(--ar,1.6)));
  margin:2px auto;background:url("../art/seamap/sea_tile.png") left top/cover no-repeat;
  image-rendering:pixelated;
  border-radius:14px;overflow:hidden;box-shadow:0 12px 34px rgba(12,30,48,.42)}
/* the island itself is inset inside the stage so it reads as land IN the sea
   rather than a frame full of land (owner 2026-08-15). 86% of the stage width,
   centred, keeping its own aspect ratio — because the stage already carries that
   same ratio, one number gives an even margin of water on all four sides. The
   pins live in here too, so sprite fractions are unchanged; the margin is also
   the room the summit ring and the top labels overflow into. */
.mtn2-isle{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);
  width:84%;aspect-ratio:var(--ar,1.6)}
.mtn2-art{position:absolute;inset:0;width:100%;height:100%;display:block;image-rendering:pixelated;
  filter:drop-shadow(0 6px 14px rgba(6,26,48,.38))}
/* --ox / --oy push a pin PERPENDICULAR to the trail, in px (set by startMountain).
   The left/top percentages still carry altitude exactly; only the sideways nudge
   lives here, which is what keeps the camp readable against the dots. In px on
   purpose: it has to track the pin sizes, which are px, not the stage. */
.mtn2-pin{position:absolute;
  transform:translate(calc(-50% + var(--ox,0px)),calc(-50% + var(--oy,0px)));
  border:0;cursor:pointer;padding:0;
  display:grid;place-items:center;border-radius:50%;line-height:1;font-size:13px;
  width:19px;height:19px;background:rgba(255,255,255,.4);box-shadow:0 2px 6px rgba(8,16,34,.55);
  border:2px solid rgba(255,255,255,.75);transition:transform .12s}
.mtn2-pin:hover{transform:translate(calc(-50% + var(--ox,0px)),calc(-50% + var(--oy,0px))) scale(1.2);z-index:5}
/* while mtnDeconflict settles the offsets, or the pins visibly slide on every visit */
.mtn2-isle.settling .mtn2-pin{transition:none}
.mtn2-pin.t-unit.done{background:var(--gold);border-color:#fff}
/* ⚠️ 38px, up from 30px, because there are FOUR of them now instead of twenty-seven
   (§18ag). They are the map's only navigation — each one is the door to a whole year's
   units — and 30px was a size chosen back when they had to squeeze into a chain. It is
   also a real touch target now, which it was not. */
.mtn2-pin.t-level{width:38px;height:38px;font-size:20px;background:rgba(20,36,58,.78);border-color:var(--gold)}
/* ⚠️ ABOVE the camp (z 7). The tent is the biggest thing on the trail, so wherever
   it lands the landmarks under it must still draw and still take their own taps —
   owner 2026-08-22: 「the campsite icon overlaps with a number of clickable circles
   so they are now unclickable」. ⚠️ Must stay BEFORE the :hover{z-index:20} rule
   below: media queries and hover states do not add specificity, and at equal
   specificity the later rule wins — put this after it and a hovered pin drops back
   to 9 and its label goes under its neighbours. */
.mtn2-pin.t-unit,.mtn2-pin.t-level{z-index:9}
/* Beyond the gate: faded, never disabled. The student can still open any of them and
   read the words — what is blocked is the CLIMB, not the map.
   ⚠️ Desaturate, do NOT grayscale. A student stopped at 中一年度试炼 can already have
   earned unit badges further up, and those pins are GOLD; full grayscale turns a badge
   they own into a badge they lost. Muted-but-still-gold says 「above your camp」, which
   is the true statement. */
/* ⚠️ a LIGHT fade, and it got lighter when the unit dots left the map (§18ag). With
   29 pins a faded one was still one of a crowd; with five, three faded 关卡 flags are
   the ONLY way into three years' worth of unit lists. A landmark that is the sole route
   to its content must never be hard to find. */
.mtn2-pin.beyond{filter:saturate(.72);opacity:.78}
.mtn2-pin.beyond:hover,.mtn2-pin.beyond:focus-visible{filter:none;opacity:1}
.mtn2-pin.t-level.gate{background:rgba(104,38,32,.86);border-color:#FFD9A0;z-index:10;
  box-shadow:0 2px 8px rgba(8,16,34,.6),0 0 0 4px rgba(245,196,67,.3)}
/* landmark name on hover / keyboard focus (owner 2026-08-15). The native title
   tooltip is still there for slow-hover and screen readers, but it takes about
   a second to appear and never shows on the camp, so the map read as a row of
   anonymous dots. Light plaque + dark ink, the same treatment .ach-hint and the
   HUD use, because these sit directly on bright painted art. */
.mtn2-name{position:absolute;left:50%;bottom:calc(100% + 9px);transform:translateX(-50%) scale(.84);
  white-space:nowrap;pointer-events:none;opacity:0;transition:opacity .12s;
  font-family:var(--sans);font-size:14px;font-weight:800;color:var(--ink);
  background:rgba(255,255,255,.93);border:2px solid var(--gold);border-radius:999px;
  padding:5px 13px;box-shadow:0 4px 12px rgba(6,26,48,.4)}
/* scale(.84) cancels the pin's own 1.2x hover scale, so the label stays the
   same size however big the pin under it is */
.mtn2-pin.lbl-below .mtn2-name{bottom:auto;top:calc(100% + 9px)}
.mtn2-pin:hover .mtn2-name,.mtn2-pin:focus-visible .mtn2-name{opacity:1}
.mtn2-pin:hover,.mtn2-pin:focus-visible{z-index:20}
@media (prefers-reduced-motion:reduce){.mtn2-name{transition:none}}
/* 你的营地 = the 你在这里 marker: it climbs with the student, so it is the one
   pin that must read at a glance. Bigger than every other pin, gold-ringed, with
   a breathing halo and a slow float. z-index above the other pins so it always
   draws on top when progress lands it next to a unit dot. */
/* ⚠️ 你在这里 map pin, not a disc on the path (owner 2026-08-22, with a reference
   image). It used to be a 64px circle centred ON the trail plus a 7px invisible
   ::before — a 78px click-swallowing disc lying across a chain of landmarks. The
   bubble now stands ABOVE the trail and ::after points down at the exact altitude
   point, so it covers nothing at all.
   ⚠️ translate(-50%,-100% - 13px) puts the bubble's bottom 13px clear of the point,
   which is exactly the height of the tail below it. Change one, change the other. */
.mtn2-pin.t-base{width:46px;height:46px;font-size:25px;background:rgba(63,112,79,.94);
  border:3px solid #FFE9B0;z-index:7;
  transform:translate(calc(-50% + var(--ox,0px)),calc(-100% - 13px + var(--oy,0px)));
  box-shadow:0 4px 14px rgba(8,16,34,.5),0 0 0 4px rgba(245,196,67,.34),0 0 18px 5px rgba(245,196,67,.34);
  animation:mtnCampGlow 2.8s ease-in-out infinite}
/* ⚠️ (0,3,0), so it beats the generic .mtn2-pin:hover (0,2,0) that would otherwise
   snap the pin back to a trail-centred transform on hover. Gentler scale than the
   other pins: this one is already the biggest thing on the map. */
.mtn2-pin.t-base:hover,.mtn2-pin.t-base:focus-visible{
  transform:translate(calc(-50% + var(--ox,0px)),calc(-100% - 13px + var(--oy,0px))) scale(1.08)}
/* the tail. ⚠️ top:100% is the PADDING box, i.e. inside the 3px border, so the tail
   is 16px tall to land its tip on the point 13px below the border. */
.mtn2-pin.t-base::after{content:"";position:absolute;left:50%;top:100%;pointer-events:none;
  margin-left:-8px;border:8px solid transparent;border-top:16px solid #FFE9B0;
  filter:drop-shadow(0 2px 3px rgba(8,16,34,.45))}
/* labels on the camp hang UNDER it, or they collide with the pin's own bubble */
.mtn2-pin.t-base .mtn2-name{bottom:auto;top:calc(100% + 15px)}
/* ⚠️ pointer-events:none: the glyph floats (mtnCampFloat) and overflows the button,
   so without this the tent takes taps outside its own hit area. */
.mtn2-tent{display:block;pointer-events:none;animation:mtnCampFloat 3.4s ease-in-out infinite;
  filter:drop-shadow(0 2px 3px rgba(8,16,34,.45))}
@keyframes mtnCampGlow{
  0%,100%{box-shadow:0 4px 14px rgba(8,16,34,.5),0 0 0 4px rgba(245,196,67,.34),0 0 18px 5px rgba(245,196,67,.34)}
  50%    {box-shadow:0 4px 14px rgba(8,16,34,.5),0 0 0 9px rgba(245,196,67,.12),0 0 28px 10px rgba(245,196,67,.5)}}
@keyframes mtnCampFloat{0%,100%{transform:translateY(-2px)}50%{transform:translateY(2px)}}
/* 顶峰 = a hollow ring drawn AROUND the summit building, never a pin on top of
   it (owner 2026-08-15: "the feature on the top of the mountain should not be
   blocked"). Its diameter comes from MTN_CROWN as an inline width %, so the
   height must follow the width — hence aspect-ratio:1 and height:auto, not the
   fixed px box every other pin uses. Transparent centre, gold rim, and a slow
   pulse so it reads as a goal marker rather than as part of the painting. */
.mtn2-pin.t-summit{width:12%;height:auto;aspect-ratio:1;font-size:0;
  background:transparent;border:4px solid rgba(255,233,176,.92);
  animation:mtnCrownGlow 2.6s ease-in-out infinite}
/* the size pulse rides on a second ring in ::after rather than on the button,
   because scaling the button would drag its hover label along with it (the
   label is a child, and .mtn2-name's scale(.84) already exists only to cancel
   the pin's own hover scale). */
.mtn2-pin.t-summit::after{content:"";position:absolute;inset:-7px;border-radius:50%;
  border:2px solid rgba(245,196,67,.6);pointer-events:none;
  animation:mtnCrownRing 2.6s ease-in-out infinite}
.mtn2-pin.t-summit:hover,.mtn2-pin.t-summit:focus-visible{border-color:#fff}
.mtn2-pin.t-summit.done{border-color:var(--gold)}
/* ⚠️ the outer glow is kept under ~26px because .mtn2-stage clips its overflow
   and G1's pavilion sits hard against the top of its sprite — a wider halo gets
   sliced off by the stage edge and reads as a rendering fault. If it is ever
   widened, widen the .mtn2-isle margin in the same pass. */
@keyframes mtnCrownGlow{
  0%,100%{box-shadow:0 0 12px 2px rgba(245,196,67,.5),inset 0 0 12px 2px rgba(245,196,67,.32)}
  50%    {box-shadow:0 0 22px 6px rgba(245,196,67,.8),inset 0 0 20px 5px rgba(245,196,67,.55)}}
@keyframes mtnCrownRing{
  0%  {transform:scale(1);opacity:.6}
  70% {transform:scale(1.18);opacity:0}
  100%{transform:scale(1.18);opacity:0}}
.mtn2-hud{display:flex;gap:8px;justify-content:center;flex-wrap:wrap;margin:12px auto 0;max-width:96vw}
.m2pill{background:rgba(20,36,58,.9);color:#EAF4FA;border:1px solid rgba(255,255,255,.18);
  border-radius:999px;padding:8px 15px;font-size:13.5px;font-weight:600}
.m2pill b{color:var(--sun,#F5C443)}
button.m2pill{cursor:pointer}
/* Both of these sat as bare text directly on the painted backdrop, which on the
   bright bamboo/sky ambiences is unreadable. Same fix .ach-hint already uses:
   dark ink on a glass pill, never light type on the art. */
.mtn2-goalbar{text-align:center;margin:12px auto 0}
.mtn2-goalbar:not(:empty){display:table;padding:6px 16px;border-radius:999px;
  color:#6B4A05;font-weight:800;font-size:13.5px;background:rgba(255,247,228,.86);
  border:1px solid rgba(227,166,60,.55);-webkit-backdrop-filter:var(--glass-blur);backdrop-filter:var(--glass-blur);
  box-shadow:var(--glass-edge),0 3px 12px rgba(23,58,90,.16)}
.mtn2-tip{display:table;margin:8px auto 0;text-align:center;color:var(--ink);font-size:12.5px;
  padding:5px 14px;border-radius:999px;background:rgba(255,255,255,.72);
  border:1px solid var(--glass-line);-webkit-backdrop-filter:var(--glass-blur);backdrop-filter:var(--glass-blur);
  box-shadow:var(--glass-edge),0 3px 12px rgba(23,58,90,.14)}
@media (prefers-reduced-motion:reduce){
  .mtn2-pin.t-base,.mtn2-tent,.mtn2-pin.t-summit,.mtn2-pin.t-summit::after{animation:none}}
/* ⚠️ Below ~700px the stage is WIDTH-limited (96vw) and the whole climb collapses:
   on a 390px phone the trail is ~143px of path carrying 27 landmarks, ~5px between
   centres, and a pin is 19px — most of the chain is unreachable no matter how it is
   offset. So the map keeps a floor width and PANS sideways instead. A map you scroll
   is honest; a map with dead landmarks is not.
   ⚠️ The scroller is .mtn2-scroll, wrapping the stage ONLY — putting it on .mtn2-wrap
   would drag the HUD, the goal bar and the tip line off-screen with it, and putting it
   on <body> is the §14 trap. 640px is measured, not picked: at ±9px alternation two
   consecutive dots clear each other from ~6.2px of path spacing apart, and 640px gives
   ~8.7px on g1 — the widest sprite, so the worst case of the four. */
@media (max-width:700px){
  .mtn2-scroll{overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch;
    margin:0 -2vw;padding:0 2vw}
  .mtn2-stage{min-width:640px}
}

/* ===== 你的营地 · static camp scene + shop (v2 场景化界面, 2026-08-13) ===== */
.camp2-wrap{position:relative;padding:4px 0 14px}
.camp2-stage{position:relative;aspect-ratio:1672/941;width:min(96vw,124vh);max-width:1180px;
  margin:2px auto;border-radius:14px;overflow:hidden;box-shadow:0 12px 34px rgba(12,30,48,.42);
  background:#3F704F}   /* fallback tone if art/camp/camp_bg.png fails to load */
.camp2-bg{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;image-rendering:pixelated}
.camp2-bg-fallback{background:linear-gradient(180deg,#DFEDF7,#5B8A66)}
.camp2-bg-fallback .camp2-bg{display:none}
.camp-deco,.camp-dwelling,.camp-pet{position:absolute;transform:translateX(-50%);
  image-rendering:pixelated;pointer-events:none;filter:drop-shadow(0 3px 4px rgba(10,20,10,.25))}
.camp-dwelling{z-index:5}
.camp-pet{z-index:900}
/* 地貌景观: belongs to the location, always furthest back, never interactive */
.camp-scenery{z-index:2}
/* 随身装备 free placement (2026-08-14). touch-action:none is REQUIRED — without
   it a touch drag scrolls the page instead of moving the item. Overrides the
   pointer-events:none above, so only equipped gear is grabbable. */
/* ⚠️ -webkit-user-drag:none is LOAD-BEARING on Safari. These sprites are <img>
   elements, and an <img> is natively draggable, so Safari starts its own
   image drag-and-drop (ghost image, no move) and our pointer drag never runs.
   preventDefault() on pointerdown does NOT reliably suppress it there. The
   matching draggable="false" attribute is set in campSprite(). */
.camp-move{pointer-events:auto;cursor:grab;touch-action:none;-webkit-user-select:none;user-select:none;
  -webkit-user-drag:none;user-drag:none}
.camp-move:active{cursor:grabbing}
.camp-move.camp-dragging{cursor:grabbing;
  filter:drop-shadow(0 8px 10px rgba(10,20,10,.4)) brightness(1.06)}
.camp-hint{text-align:center;font-size:12px;color:#5A7080;margin:6px auto 0}
.shop-slot-note{font-weight:600;color:#6A8296;letter-spacing:0}
.shop-note{font-size:12.5px;color:#5A7080;line-height:1.7;margin:2px 0 10px}
/* .shop-row/.shop-thumb/.shop-info/.shop-price/.shop-buy/.shop-owned/.shop-equip/
   .shop-locked are GONE with the framed-row shelf (owner 2026-08-17) — the tile
   rules live up beside .shop-grid. Do not re-add one of them on its own: half a
   row layout inside a tile grid is worse than either. */
.camp2-hud{display:flex;gap:8px;justify-content:center;flex-wrap:wrap;margin:12px auto 0;max-width:96vw}
.ling-icon{width:14px;height:auto;vertical-align:-2px;image-rendering:pixelated}
.shop2-card{background:var(--card,#fff);border-radius:18px;max-width:640px;width:100%;
  margin:8px auto;padding:20px 22px 24px;box-shadow:0 12px 34px rgba(12,30,48,.2)}
.shop-tier-label{font-size:12.5px;font-weight:800;color:var(--gold-ink);letter-spacing:.06em;
  margin:16px 0 8px}
.shop-tier-label:first-of-type{margin-top:6px}
/* ---------- 货架分组与排布 (owner 2026-09-05) ----------
   ⚠️ 九个装备格里有六个只装得下一件，而每一格原来独占一整行 grid：屏幕右边
   因此是一大片空白。盒子按件数跨列并排，dense 让只有一件的格子去补上一行剩下的洞。
   ⚠️ 列数写死（4 · 窄屏 2），不用 auto-fill：跨几列是 JS 按件数写在 data-n 上的，
   一个 span 3 落进只有两条轨道的 grid 会长出隐式轨道并把卡片撑破。
   ⚠️ 内层 .shop-grid 的列数必须等于盒子跨的列数，所以同样逐条写死——留 auto-fill
   会在盒子被 dense 拉宽时自己多开一列，空洞立刻回来。
   ⚠️ 盒子外面不画框（§18l「商品外面不画框」管的就是这块屏幕）：分组只靠金色
   小标题与列间距。 */
.shop-slots{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));
  column-gap:14px;grid-auto-flow:row dense;align-items:start}
.shop-slotbox{min-width:0}
.shop-slotbox[data-n="1"]{grid-column:span 1}
.shop-slotbox[data-n="2"]{grid-column:span 2}
.shop-slotbox[data-n="3"]{grid-column:span 3}
.shop-slotbox[data-n="4"]{grid-column:span 4}
.shop-slotbox[data-n="1"] .shop-grid{grid-template-columns:minmax(0,1fr)}
.shop-slotbox[data-n="2"] .shop-grid{grid-template-columns:repeat(2,minmax(0,1fr))}
.shop-slotbox[data-n="3"] .shop-grid{grid-template-columns:repeat(3,minmax(0,1fr))}
.shop-slotbox[data-n="4"] .shop-grid{grid-template-columns:repeat(4,minmax(0,1fr))}
.shop-slotbox .shop-tier-label{margin:12px 0 4px}
.shop-slotbox .shop-grid{margin:0 0 8px}
/* 两组之间那条线是这块货架上唯一的分隔线 */
.shop-group{margin:24px 0 2px;padding-top:14px;border-top:1px solid var(--card-line);
  font-family:var(--serif);font-size:17px;font-weight:800;color:var(--ink);letter-spacing:.02em}
.shop-group.first{margin-top:10px;padding-top:0;border-top:0}
.shop-group .shop-slot-note{font-family:var(--sans);font-size:12.5px;font-weight:600}
/* ⚠️ 这一档必须留在上面那批基础规则之后（§14：同优先级后者赢） */
@media (max-width:560px){
  .shop-slots{grid-template-columns:repeat(2,minmax(0,1fr))}
  .shop-slotbox[data-n="3"],.shop-slotbox[data-n="4"]{grid-column:span 2}
  .shop-slotbox[data-n="3"] .shop-grid,
  .shop-slotbox[data-n="4"] .shop-grid{grid-template-columns:repeat(2,minmax(0,1fr))}
}
@media (max-width:480px){.camp2-hud .m2pill{font-size:12px;padding:7px 10px}}

/* ---------- 成就墙 tabs (2026-08-14) ----------
   The 对战 block used to sit ~5,700px down a single scrolling wall, which is
   exactly why the owner could not find it. Two tabs, one row, same shape as the
   pier's .xh-tab pair. */
.ach-tabs{display:flex;gap:8px;margin:0 0 12px}
.ach-tab{flex:1;border:1px solid var(--glass-line);border-radius:14px;padding:11px 10px;
  font-size:14px;font-weight:700;color:var(--ink);cursor:pointer;touch-action:manipulation;
  background:rgba(255,255,255,.7);-webkit-backdrop-filter:var(--glass-blur);backdrop-filter:var(--glass-blur);
  box-shadow:var(--glass-edge),0 3px 12px rgba(23,58,90,.14)}
.ach-tab.on{background:linear-gradient(180deg,#FBEBC4,#F2D89A);border-color:var(--gold);color:#6B4A05}

/* ---------- 别人的对战徽章 (leaderboard name -> badge card) ---------- */
.lb-row{width:100%;text-align:left;cursor:pointer;font:inherit}
.pb-card{text-align:left}
.pb-sub{font-size:12.5px;color:var(--ink-soft);margin:-6px 0 12px}
.pb-fam{margin-top:12px}
.pb-fam-name{font-size:13px;font-weight:800;color:var(--ink);letter-spacing:.1em;margin-bottom:8px}
.pb-grid{display:flex;gap:14px;flex-wrap:wrap}
.pb-badge{width:92px;text-align:center;font-size:11.5px;color:var(--ink-soft);line-height:1.35}
.pb-badge img{width:62px;height:62px;border-radius:50%;object-fit:contain;background:rgba(255,255,255,.85);
  border:2px solid var(--gold);box-shadow:0 4px 12px rgba(23,58,90,.2);display:block;margin:0 auto 5px}
.pb-none{font-size:12.5px;color:var(--ink-soft);background:rgba(255,255,255,.55);
  border:1px dashed #B9CEDD;border-radius:12px;padding:10px 12px}
.pb-note{margin-top:14px;font-size:11.5px;color:#4E6273}

/* ---------- liquid glass fallback ----------
   Older ChromeOS builds and any browser without backdrop-filter would render
   the panels above as thin washes over busy artwork. Where the blur is not
   available, opacity carries the plate instead. Keep this block LAST so it
   wins on specificity-equal rules. */

/* ---------- 地景横幅 (HANDOFF_stream_page_layout §3) ----------
   The right column's focal element and the entry to 我的词山. 16:9 so it sits
   full-width at the top of a phone without any portrait special-casing (§7).
   ⚠️ The plate is the SAME image applyAmbience used to paint on the body — it is
   painted HERE and only here now, or the student sees it twice on one screen. */
/* ⚠️ The banner art is the stream's OWN island (art/mountain/mtn_*.png), not a
   bg-01..05 plate — owner, 2026-08-16 evening. Those PNGs are palette-TRANSPARENT
   island portraits at 1.17-1.63 aspect, so they are staged on the sea exactly as
   我的词山 stages them, and never cropped. */
.lscape{position:relative;display:block;width:100%;aspect-ratio:16/9;overflow:hidden;
  border-radius:18px;padding:0;border:1px solid var(--glass-line);
  box-shadow:var(--glass-lift);cursor:pointer;
  background:var(--sea1) url("../art/seamap/sea_tile.png") center/cover;
  image-rendering:pixelated;
  touch-action:manipulation;margin-bottom:10px}
/* ⚠️ contain, NEVER cover. cover crops a 1.2-aspect island inside a 16/9 box and
   takes the SUMMIT off — the same complaint that produced the 顶峰 crown pass.
   The side gaps are sea, which is what an island sits in. */
.lscape-bg{position:absolute;inset:0;width:100%;height:100%;object-fit:contain;display:block}
/* whole-plate tap target, under the pills and over the art. Transparent on
   purpose: the visible affordance is 全山纵览 in the corner. */
.lscape-hit{position:absolute;inset:0;z-index:1;background:none;border:0;padding:0;
  cursor:pointer;touch-action:manipulation}
/* the caption must never eat the tap */
.lscape-in,.lscape-go{pointer-events:none}
.lscape-rooms{position:absolute;left:12px;top:12px;z-index:2;display:flex;gap:8px;flex-wrap:wrap}
.lscape-room{font-size:12.5px;font-weight:700;color:var(--sea3);
  background:rgba(255,255,255,.9);border:1px solid var(--glass-line);border-radius:999px;
  padding:7px 13px;cursor:pointer;touch-action:manipulation;
  box-shadow:0 2px 10px rgba(10,28,48,.28)}
.lscape-room:active{transform:translateY(1px)}
@media (max-width:560px){
  .lscape-room{font-size:11.5px;padding:6px 10px}
}
/* ⚠️ NO DARK SCRIM (owner 2026-08-16). The banner IS the student's own mountain —
   washing 78% black over its lower half to make room for two lines of text was
   paying for legibility with the thing the panel exists to show. The text carries
   its own shadow instead (same double-shadow trick as .lp-couplet), which lifts
   the letters without touching the art. Only a whisper of gradient remains, and
   only in the bottom fifth where the caption actually sits. */
.lscape::after{content:"";position:absolute;inset:0;
  background:linear-gradient(180deg,rgba(10,28,48,0) 72%,rgba(10,28,48,.30) 100%)}
.lscape-in{position:absolute;left:14px;bottom:12px;z-index:1;text-align:left;color:#fff}
.lscape-rank{display:block;font-family:var(--serif);font-weight:900;font-size:21px;
  text-shadow:0 2px 8px rgba(6,20,40,.95),0 0 3px rgba(6,20,40,.9),0 1px 1px rgba(6,20,40,.8);
  text-shadow:0 2px 6px rgba(6,20,40,.8)}
.lscape-pts{display:block;font-size:13px;font-weight:700;color:#E4F0F9;margin-top:2px;
  text-shadow:0 2px 7px rgba(6,20,40,.95),0 0 3px rgba(6,20,40,.9);
  text-shadow:0 1px 5px rgba(6,20,40,.9)}
.lscape-go{position:absolute;right:12px;bottom:12px;z-index:1;font-size:13px;font-weight:700;
  text-shadow:0 2px 7px rgba(6,20,40,.95),0 0 3px rgba(6,20,40,.9);
  color:#FFE9B0;background:rgba(12,24,48,.6);border-radius:999px;padding:5px 12px;
  text-shadow:0 1px 4px rgba(6,20,40,.9)}
.lscape-acts{display:flex;gap:10px;margin-bottom:12px}
.lscape-chip{flex:1 1 0;min-width:0;display:flex;align-items:center;justify-content:center;gap:6px;
  min-height:44px;padding:6px 12px;border-radius:14px;cursor:pointer;touch-action:manipulation;
  background:var(--glass-strong);border:1px solid var(--glass-line);box-shadow:var(--glass-edge);
  color:var(--ink);font-size:13px}
.lscape-chip b{font-family:var(--serif);font-size:17px;color:var(--gold-deep)}
.lscape-chip span{color:var(--ink-soft);font-weight:700}
.lscape-chip:active{transform:translateY(1px)}

/* .rwd-* (游戏卡片上的 历练值/灵露 标记) removed 2026-08-16 evening — see the note
   above camp() in cs.js. */

/* §2 来源 row inside ①复习范围 */
/* ---------- 筛选 fold: 来源 + 板块 (owner 2026-08-16 late) ----------
   ⚠️ The closed header still STATES what is in force (来源, and how many 板块 are on)
   — folding may hide the controls, never the facts, or a student who narrowed to one
   板块 last week has no way to see why their word count looks wrong. */
.scope-filt-t{display:flex;align-items:center;gap:8px;width:100%;margin:2px 0 8px;
  padding:7px 12px;border-radius:12px;cursor:pointer;touch-action:manipulation;
  background:rgba(255,255,255,.55);border:1px solid #C4D5E2;
  font-family:var(--sans);text-align:left}
.scope-filt-lab{font-size:12.5px;font-weight:700;color:var(--ink-soft)}
.scope-filt-sum{margin-left:auto;font-size:12.5px;font-weight:700;color:#6B4A05}
.scope-caret{flex:none;font-size:12px;color:var(--sea2);opacity:.7}
.scope-filt.closed{display:none}
/* ⚠️ the glosses under 筛选 must not push the summary out of the row: same rule the
   HUD pills use (see the .enlab.i block). */
body.py-aid .scope-filt-lab .pylab,body.en-aid .scope-filt-lab .enlab.i{
  display:block;margin:1px 0 0}
.scope-src{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin:2px 0 8px}
.scope-src-lab{font-size:12.5px;font-weight:700;color:var(--ink-soft)}
.scope-src-on{font-size:12.5px;font-weight:700;color:#6B4A05;background:linear-gradient(180deg,#FBEBC4,#F2D89A);
  border:1.5px solid var(--gold);border-radius:999px;padding:4px 12px}
.scope-src-btn{font-size:12.5px;font-weight:700;color:var(--sea2);background:rgba(255,255,255,.85);
  border:1px dashed #B9CEDD;border-radius:999px;padding:4px 12px;cursor:pointer;
  touch-action:manipulation}
.scope-src-btn:active{transform:scale(.97)}


/* §7 orientation hint — an inline strip above the activity, never a modal, and
   never blocking: iOS cannot be made to rotate, so this can only ever advise. */
.orient-hint{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin:0 0 8px;
  padding:8px 12px;border-radius:14px;font-size:13px;font-weight:700;color:var(--sea2);
  background:var(--glass-strong);border:1px solid var(--glass-line);box-shadow:var(--glass-edge)}
.orient-x{margin-left:auto;font-size:12.5px;font-weight:700;color:var(--sea2);
  background:rgba(255,255,255,.7);border:1px solid var(--glass-line);border-radius:999px;
  padding:4px 10px;cursor:pointer;touch-action:manipulation}
.orient-x + .orient-x{margin-left:0}

/* ---------- 通用搜索 · 五站 (js/search.js, §3.3) ----------
   ⚠️ DUPLICATED VERBATIM IN xh.css. That is deliberate and matches the standing
   convention in this repo: XH_index.html never loads cs.css, so the dock needs
   its own copy, exactly as it already carries copies of the palette tokens and
   of the rules profile.js needs. Change one, change the other.
   z-index 72: above .pop-card (60) and the avatar info card (68) because it is a
   top-level dialog, below arena (90) and the celebration layer (300). */
.wss-overlay{position:fixed;inset:0;z-index:72;display:flex;align-items:flex-start;
  justify-content:center;padding:6vh 16px 16px;background:rgba(20,44,68,.42);
  -webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px)}
/* ⚠️ .95, NOT var(--glass-strong) (.72). CLAUDE.md warns against LOWERING the
   glass alphas; this raises one, for the same reason .pop-card went .82 -> .95
   on 2026-08-14: this is a dense list of small text sitting over busy pixel art
   (the pier, the sea map, bg-01..05), and readability beats the effect here. */
.wss-card{width:min(680px,100%);max-height:88vh;display:flex;flex-direction:column;
  border-radius:22px;padding:16px 16px 8px;background:rgba(255,255,255,.95);
  -webkit-backdrop-filter:var(--glass-blur);backdrop-filter:var(--glass-blur);
  border:1px solid var(--glass-line);box-shadow:var(--glass-lift),var(--glass-edge)}
.wss-head{display:flex;align-items:center;gap:10px;margin-bottom:10px}
.wss-title{flex:1 1 auto;min-width:0;font-family:var(--serif),serif;font-size:19px;
  font-weight:700;color:var(--ink)}
.wss-sub{display:block;font-family:var(--sans),sans-serif;font-size:12px;font-weight:600;
  color:var(--ink-soft);margin-top:2px}
.wss-x{flex:0 0 auto;width:38px;height:38px;border-radius:50%;font-size:16px;
  color:var(--sea2);background:rgba(255,255,255,.8);border:1px solid var(--glass-line);
  cursor:pointer;touch-action:manipulation}
.wss-input{width:100%;box-sizing:border-box;padding:13px 15px;font-size:17px;
  font-family:var(--sans),sans-serif;color:var(--ink);background:rgba(255,255,255,.88);
  border:1.5px solid var(--glass-line-2);border-radius:14px;outline:none}
.wss-input:focus{border-color:var(--gold)}
.wss-results{flex:1 1 auto;min-height:0;overflow-y:auto;margin-top:10px;padding-bottom:8px}
.wss-hint{padding:18px 6px;font-size:13.5px;font-weight:600;color:var(--ink-soft);
  text-align:center;line-height:1.7}
.wss-hint-en{display:block;font-size:12px;font-weight:600;color:var(--ink-soft);margin-top:3px}
.wss-row{display:flex;align-items:center;gap:11px;padding:8px 9px;border-radius:12px}
.wss-row + .wss-row{border-top:1px solid rgba(150,185,212,.28)}
.wss-pic{flex:0 0 auto;width:42px;height:42px;object-fit:contain;
  image-rendering:pixelated}
.wss-txt{flex:1 1 auto;min-width:0}
.wss-w{display:flex;align-items:baseline;gap:8px;flex-wrap:wrap}
.wss-w b{font-size:17px;font-weight:700;color:var(--ink)}
.wss-py{font-size:13px;font-weight:600;color:var(--gold-ink)}
.wss-chip{font-size:11px;font-weight:700;color:var(--sea2);background:rgba(255,255,255,.8);
  border:1px solid var(--glass-line-2);border-radius:999px;padding:1px 8px}
.wss-en{font-size:12.5px;color:var(--ink-soft);margin-top:2px;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* ⚠️ WHERE THE WORD IS TAUGHT, now that a word gets exactly ONE row (2026-09-01).
   This carries the fact the old per-station grouping carried by repeating the row up
   to four times. Quiet on purpose: it is provenance, not the answer the student came
   for, so it must never outweigh 词语 · 拼音 · 英文 above it. `.mine` lifts the
   student's own stream only — one chip at most, so it stays a highlight. */
.wss-srcs{display:flex;flex-wrap:wrap;gap:4px;margin-top:4px}
.wss-src{font-size:10.5px;font-weight:700;letter-spacing:.02em;color:var(--ink-soft);
  background:rgba(255,255,255,.62);border:1px solid var(--glass-line-2);
  border-radius:999px;padding:1px 7px}
.wss-src.mine{color:var(--gold-ink);background:#FDF6E4;border-color:var(--gold)}
.wss-count{padding:2px 2px 8px;font-size:12.5px;font-weight:600;color:var(--ink-soft)}
.wss-count b{color:var(--ink)}
.wss-count-n{margin-left:8px;font-weight:600;color:var(--gold-ink)}
/* ⚠️ Sibling button, never nested inside a tappable row — the 可及性 pass removed
   exactly this near-miss trap from every MCQ surface. ::after widens the target
   to ~46px without changing the visual size. */
.wss-spk{position:relative;flex:0 0 auto;width:34px;height:34px;border-radius:50%;
  font-size:15px;background:rgba(255,255,255,.85);border:1px solid var(--glass-line-2);
  cursor:pointer;touch-action:manipulation}
.wss-spk::after{content:"";position:absolute;inset:-6px}
.wss-more{padding:7px 9px;font-size:12px;font-weight:600;color:var(--ink-soft)}
@media (max-width:520px){
  .wss-overlay{padding:2vh 8px 8px}
  .wss-card{max-height:94vh;border-radius:18px}
}

/* ---------- 房间：花名册与头像 (owner 2026-08-16) ---------- */
.arena-roster{display:flex;flex-wrap:wrap;gap:8px;justify-content:center;margin-top:12px}
.arena-who{display:flex;align-items:center;gap:6px;padding:5px 11px 5px 5px;border-radius:999px;
  background:rgba(255,255,255,.14);border:1px solid rgba(255,255,255,.28);
  font-size:13px;font-weight:700;color:#EAF3FA;max-width:170px}
.arena-who img,.arena-who-none{width:26px;height:26px;border-radius:50%;object-fit:contain;flex:none;
  background:rgba(255,255,255,.9);border:1.5px solid var(--gold,#E3A63C)}
.arena-who-none{display:flex;align-items:center;justify-content:center;font-size:14px}
.arena-who span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.arena-who.me{background:rgba(227,166,60,.3);border-color:var(--gold,#E3A63C)}
.arena-av{width:24px;height:24px;border-radius:50%;object-fit:contain;flex:none;
  background:rgba(255,255,255,.9);border:1.5px solid rgba(255,255,255,.5);margin-right:2px}
.arena-av.none{display:flex;align-items:center;justify-content:center;font-size:13px}

/* ---------- 颁奖台 (js/podium.js) ----------
   ⚠️ 这段在 cs.css 与 teacher.html 各有一份：教师端不加载 cs.css。改一处要改两处。 */
/* 颁奖台底图 = 落地页那张登山图（owner 2026-08-16）。压淡是为了让名字和分数读得清：
   底图只是气氛，台上的字才是内容。
   ⚠️ url() 相对**样式表**解析，所以这里是 ../art/…；teacher.html 那一份的同一条规则
   写的是 art/…（内联 <style> 相对**页面**解析）。这是两份拷贝唯一合理的差异。 */
.pod-overlay{position:fixed;inset:0;z-index:200;display:flex;align-items:center;
  justify-content:center;padding:20px;overflow:hidden;
  background:linear-gradient(rgba(255,255,255,.55),rgba(255,255,255,.68)),
    url("../art/bg/landing_hero_bg.png") center/cover no-repeat;
  image-rendering:pixelated;animation:podFade .35s ease-out both}
@keyframes podFade{from{opacity:0}to{opacity:1}}
.pod-wrap{position:relative;z-index:2;width:min(900px,100%);display:flex;
  flex-direction:column;align-items:center}
.pod-x{margin-top:22px;font-size:15px;font-weight:800;color:#fff;
  background:linear-gradient(180deg,#3E7FB0,#2E6391);border:0;border-radius:999px;
  padding:11px 34px;cursor:pointer;touch-action:manipulation;
  box-shadow:0 6px 18px rgba(10,28,48,.35)}
.pod-x:active{transform:translateY(1px)}
.pod-confetti{position:absolute;inset:0;width:100%;height:100%;pointer-events:none;z-index:1}
.pod-title{font-family:var(--serif,"Noto Serif SC"),serif;font-weight:900;
  font-size:clamp(21px,2.8vw,40px);text-align:center;margin-bottom:12px}
.pod-stage{display:flex;align-items:flex-end;justify-content:center;gap:clamp(8px,2vw,22px)}
.pod-col{display:flex;flex-direction:column;align-items:center;flex:0 1 clamp(92px,21vw,210px);
  animation:podRise .55s cubic-bezier(.22,1.2,.36,1) both;animation-delay:var(--d,0s)}
@keyframes podRise{from{transform:translateY(26px);opacity:0}to{transform:none;opacity:1}}
.pod-who{display:flex;flex-direction:column;align-items:center;gap:3px;margin-bottom:7px;min-width:0;width:100%}
.pod-av{width:clamp(46px,7vw,96px);height:clamp(46px,7vw,96px);border-radius:50%;object-fit:contain;
  background:rgba(255,255,255,.9);border:2.5px solid var(--gold,#E3A63C);
  box-shadow:0 4px 14px rgba(10,28,48,.3)}
.pod-av.none{display:flex;align-items:center;justify-content:center;font-size:26px}
.pod-name{font-weight:800;font-size:clamp(12px,1.3vw,19px);text-align:center;line-height:1.25;
  max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pod-score{font-family:var(--serif,"Noto Serif SC"),serif;font-weight:900;
  font-size:clamp(15px,1.8vw,26px);color:var(--gold-deep,#B7791F)}
/* the block heights ARE the ranking — 1st tallest. Keep gold > silver > bronze. */
.pod-block{width:100%;border-radius:12px 12px 0 0;display:flex;flex-direction:column;
  align-items:center;justify-content:flex-start;gap:2px;padding-top:8px;
  border:1px solid rgba(255,255,255,.5);border-bottom:0}
.pod-col.gold   .pod-block{height:clamp(92px,13vw,182px);background:linear-gradient(180deg,#F7D774,#D9A02C)}
.pod-col.silver .pod-block{height:clamp(68px,9.5vw,134px);background:linear-gradient(180deg,#E4EBF1,#B4C2CE)}
.pod-col.bronze .pod-block{height:clamp(54px,7.5vw,106px);background:linear-gradient(180deg,#E6B489,#BE8151)}
.pod-medal{font-size:clamp(20px,2.4vw,30px);line-height:1}
.pod-rank{font-family:var(--serif,"Noto Serif SC"),serif;font-weight:900;
  font-size:clamp(15px,1.7vw,21px);color:rgba(10,28,48,.62)}
.pod-col.me .pod-av{border-color:#fff;box-shadow:0 0 0 3px var(--gold,#E3A63C),0 4px 14px rgba(10,28,48,.35)}
@media (prefers-reduced-motion:reduce){.pod-col{animation:none}}

@supports not ((backdrop-filter:blur(2px)) or (-webkit-backdrop-filter:blur(2px))){
  .wss-card{background:rgba(255,255,255,.97)}
  .wss-overlay{background:rgba(20,44,68,.55)}
  /* ⚠️ NOT .topbar — it has no panel any more (owner 2026-08-22). Repainting it
     here would put the slab back on exactly the old iPads the fallback exists for. */
  .topbar .tb-id{background:rgba(255,255,255,.95)}
  .card{background:rgba(255,255,255,.93)}
  .pop-card{background:rgba(255,255,255,.97)}
  .tb-profile,.tb-en,.tb-py,.topbar .back,.prof-x{background:rgba(255,255,255,.94)}
  .dopt,.nav-btn,.prof-chip,.ach-tab{background:rgba(255,255,255,.9)}
  .path-scope{background:rgba(255,255,255,.93)}
  .section-label,.mtn2-tip{background:rgba(255,255,255,.95)}
  .prof-input,.np-select,.code-ta,.prof-uid,.pg-chip,.pg-arrow,.pg-focus,.pg-stats div{background:rgba(255,255,255,.88)}
  .dopt.on{background:#E3EEF7}
}

/* ================= 道具 · 赛前槽位与局内道具栏 (2026-08-25) =================
   The pre-round picker reuses .shop-grid/.shop-tile wholesale, so a 道具 looks
   identical on the shop shelf and in a slot — one tile to restyle later, never
   two that quietly drift apart. Only the two wrappers below are new. */
.lo-grid{margin-bottom:4px}
.lo-purse{display:flex;align-items:baseline;gap:6px;flex-wrap:wrap;margin:0 0 8px;
  padding:8px 11px;border:1px solid #EFD9A4;border-radius:12px;background:#FDF3DC;
  font-size:12.5px;color:var(--ink)}
.lo-purse img{width:14px;height:14px;vertical-align:-2px;image-rendering:pixelated}
.lo-purse b{font-family:var(--serif);font-size:16px;color:var(--gold-deep)}
.lo-purse span{flex:1 1 100%;font-size:11.5px;line-height:1.6;color:#7A5310}
.lo-note{margin:0 0 4px;font-size:12px;line-height:1.6;color:var(--ink-soft)}

/* ⚠️ THE BAR WRAPS, it does not scroll. 词雨's right column is `clamp(190px,23%,290px)`
   wide in landscape and three chips do not fit across it — the first draft used
   overflow-x and clipped 定风珠, the ONE chip in that round a student can actually
   tap. An off-screen activated item is the same bug as no item at all. Wrapping
   costs a second ~40px row, and both columns have the room: 词雨's input row is
   pinned to the bottom by `margin-top:auto`, and .sprint-right already scrolls. */
.itembar{display:flex;flex-wrap:wrap;gap:6px;padding:2px}
.ib-chip{display:flex;align-items:center;gap:7px;flex:0 1 auto;min-width:0;
  padding:5px 10px 5px 6px;border:1px solid var(--card-line);border-radius:12px;
  background:rgba(255,255,255,.92);color:var(--ink);
  font-family:var(--sans);text-align:left;-webkit-appearance:none}
.ib-chip img{width:26px;height:26px;object-fit:contain;image-rendering:pixelated}
.ib-chip b{display:block;font-size:12.5px;font-weight:800;line-height:1.25}
.ib-chip .ib-sub{display:block;font-style:normal;font-size:10.5px;font-weight:700;
  line-height:1.3;color:var(--ink-soft)}
/* only the tappable ones look tappable — passive chips are spans, not buttons */
button.ib-chip{cursor:pointer;border-color:rgba(46,99,145,.55);background:rgba(214,233,246,.9)}
button.ib-chip:active:not([disabled]){transform:translateY(1px)}
button.ib-chip.is-used,button.ib-chip[disabled]{cursor:default;opacity:.42;transform:none;
  border-color:var(--card-line);background:rgba(255,255,255,.7)}
/* 词雨 runs on a dark shell, so its copy of the chip inverts to stay readable */
.rain-right .ib-chip{background:rgba(18,42,66,.85);border-color:rgba(234,244,250,.25);color:#EAF4FA}
.rain-right .ib-chip .ib-sub{color:rgba(234,244,250,.72)}
.rain-right button.ib-chip{background:rgba(46,99,145,.92);border-color:rgba(234,244,250,.5)}
.rain-right button.ib-chip.is-used{background:rgba(18,42,66,.55)}

/* 定风珠 · the sky stops. ⚠️ Scoped to .rain-area, never a body class: a body
   state class that shares a name with a gate class is how the whole site once
   went blank, and this only ever describes one round's field. */
.rain-area.is-frozen .rain-word{filter:brightness(1.22) drop-shadow(0 0 6px rgba(150,220,255,.9))}
.rain-area.is-frozen::after{content:"";position:absolute;inset:0;z-index:4;pointer-events:none;
  background:radial-gradient(120% 90% at 50% 40%,rgba(150,220,255,0),rgba(150,220,255,.22));
  box-shadow:inset 0 0 0 3px rgba(150,220,255,.45)}

/* 司南 struck an option out. Greyed in place, never removed: the row count stays
   put so nothing below jumps while the student is still reading the options. */
.sopt.is-out{opacity:.32;text-decoration:line-through;pointer-events:none}
/* ---------- 昵称弹窗里的 拼音 / EN 开关 (2026-08-25) ----------
   ⚠️ COPIED INTO BOTH STYLESHEETS ON PURPOSE. XH_index.html loads xh.css and never
   cs.css, but it shows the same nickname picker, so a pill styled in only one file
   is an unstyled pill on the pier. Same reason .pop-card itself lives in both.
   ⚠️ It sits ABOVE the title and reads as a utility, not as a step — a student
   walking the 大类 → 描述词 chips must never mistake it for a choice to make.
   ⚠️ NOT position:sticky. .pop-card scrolls its own overflow and a sticky row would
   sit over the 学校 dropdown's options on a short phone screen. */
.np-aid{display:flex;justify-content:flex-end;gap:6px;margin:-4px 0 8px}
.np-aid-btn{display:inline-flex;align-items:center;gap:6px;flex:none;
  height:32px;padding:0 11px;border:1px solid var(--card-line);border-radius:999px;
  background:rgba(255,255,255,.85);color:var(--ink-soft);
  font-family:var(--sans);cursor:pointer;-webkit-appearance:none}
.np-aid-btn .np-aid-ic{font-family:var(--serif);font-weight:800;font-size:15px}
.np-aid-btn .np-aid-lab{font-size:12px;font-weight:700;opacity:.72}
.np-aid-btn.on{background:var(--gold);border-color:var(--gold);color:#3A2A08}
.np-aid-btn.on .np-aid-lab{opacity:1}
.np-aid-btn:active{transform:translateY(1px)}
/* ---------- 昵称的英文 (2026-08-26) ----------
   ⚠️ In BOTH stylesheets, like every other nickname-picker rule: XH_index.html
   loads xh.css and never cs.css, and it shows the same picker.
   The span inside is np()'s .enlab, which is already display:block and already
   gated — this only positions it under the name and stops it inheriting the
   19px bold of .np-name, which would make the English compete with the name
   it is explaining. */
.np-name-en{margin:-8px 0 14px;text-align:center}
.np-name-en .enlab{font-size:13.5px;font-weight:600;line-height:1.4;color:var(--ink-soft)}
/* The English inside a picker chip: a second line in the same pill, so with the
   aid off the chip is byte-for-byte the shape it was before.
   ⚠️ TYPOGRAPHY ONLY — never `display`. The gate is `.enlab{display:none}` +
   `body.en-aid .enlab{display:block}` (and `body.xh-en-on .xh-en` on the pier),
   so a bare `.wchip .enlab{display:block}` at (0,2,0) beats the (0,1,0) OFF rule
   and pins the English on for everyone, aid or not. Shipped that for one build
   and caught it only because the chip height barely moved when EN was toggled
   off. Same specificity trap the pier records at xh.css §159 / §465 / §1881.
   Leaving `display` alone lets the gate keep owning it. */
.wchip .enlab,.wchip .xh-en{margin-top:1px;font-size:11px;font-weight:600;line-height:1.3;opacity:.85}
