/* app.css —— 《响器 / giltwire》
 *
 * 色只从四盏灯与九种材质来（见 docs/设计说明.md 第二节）。没有一处是「界面色」。
 * 底子是**中间调**：暗处是有色的暗（墨青 / 枣红 / 靛蓝），对比来自饱和差而不是亮暗差。
 *
 * 三种字体 = 三个说话的人：印刷体（局里的公文）／手写体（发报人）／等宽体（稽核室）。
 */

:root{
  --lamp:#FFB25A;  --lamp-hi:#FFF0C0;  --lamp-lo:#8A5F2A;
  --jade:#2E8B6E;  --jade-hi:#5FC79C;
  --brass:#D9A94E; --brass-lo:#6B4A20; --brass-dk:#33210F;
  --verdigris:#3E8E77;
  --night:#37508F; --night-dk:#1B2440;
  --teal:#16232A;  --teal-hi:#2B4550;
  --oxblood:#5A1F1E; --oxblood-lo:#2A0F10;
  --ink:#243A5E;   --ink-lo:#16233A;
  --vermilion:#C0392B; --silk:#B0182B;
  --paper:#EFE3C4; --paper-hi:#FFF8E4; --paper-lo:#BFAE89;
  --felt:#1F4A3A;
  --darkest:#140A10;
  --room:#1C1210;

  --f-print:"Songti SC","STSong","SimSun","Noto Serif CJK SC","Source Han Serif SC",serif;
  --f-hand:"Kaiti SC","STKaiti","KaiTi","楷体",cursive;
  /* ⚠️ 这里必须是「中文字体在前」：Courier New / Consolas 没有中文字形，
     中文会回退成系统默认，小字号下糊成一团 —— 实测就是「乱码」的来源。
     黑体是中文里最冷最机械的一套，正好当稽核室那个声音。 */
  --f-type:"Microsoft YaHei","PingFang SC","Heiti SC","Noto Sans CJK SC",
           "Source Han Sans SC","Hiragino Sans GB","Courier New",Consolas,monospace;

  --r:3px;
}

*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
/* 根字号随视口走：所有用 em 排的字（浮层、面板、提示）跟着一起放大。
   412px 宽的手机上还是 16px 出头，1280 以上长到 23px。 */
html{font-size:clamp(16px, calc(1.05vw + 11.6px), 23px)}
html,body{
  height:100%;background:var(--darkest);color:var(--paper);
  font-family:var(--f-print);overflow:hidden;
  -webkit-tap-highlight-color:transparent;overscroll-behavior:none;
}

/* ── 连续层 ① 三维场景 ─────────────────────────────────────────── */
#cvScene{position:fixed;inset:0;width:100%;height:100%;display:block;z-index:0;pointer-events:none}

/* ── 连续层 ② 文字层：纸贴在三维平面上 ──────────────────────────
   perspective 与 perspective-origin 每帧由 JS 写成与三维相机一致的值，
   于是 DOM 与 canvas 用同一个投影 —— 纸是真的躺在桌上的。 */
#world3d{
  /* ⚠️ 这里的三行是整层 DOM 能对齐三维相机的关键，动之前先读这段：
   *
   * CSS 的 perspective 做的是 x' = x·d/(d−z)，除的是**整个坐标**，
   * 不是「相对主点的偏移」。所以只有当 #world3d 的坐标原点本身落在主点上、
   * 且 perspective-origin 是 (0,0) 时，它才等价于canvas 那台小孔相机。
   *
   * 我一开始把 origin 写成屏幕中心的百分比 —— 结果透视除法把那个偏移量
   * 也放大了 60 多倍（206 → −13770），纸被推到两万像素外，屏幕上什么都没有。
   *
   * 所以：容器缩成 0×0、钉在主点上，origin 归零。纸的 left/top 都是 0，
   * 位置全部由 matrix3d 决定。 */
  position:fixed;left:50%;top:51.5%;width:0;height:0;
  z-index:10;pointer-events:none;
  perspective:1000px;perspective-origin:0 0;
}
#world3d>*{pointer-events:auto}

/* 一张「纸」：尺寸由 JS 按世界单位 × 每单位像素数写成 */
.paper{
  position:absolute;left:0;top:0;
  transform-origin:0 0;
  background:linear-gradient(168deg,var(--paper-hi) 0%,var(--paper) 58%,#D6C6A2 100%);
  color:var(--ink);
  border-radius:1px;
  box-shadow:0 22px 44px rgba(0,0,0,.66), inset 0 0 60px rgba(120,95,55,.16);
  padding:3.4% 3.6% 3.0%;
  display:flex;flex-direction:column;gap:1.8%;
  overflow:hidden;
  opacity:0;pointer-events:none;
}
.paper.is-on{opacity:1;pointer-events:auto}
/* 纸的右缘压一道窄影 —— 让它离桌面那么一点点 */
.paper::after{
  content:'';position:absolute;right:0;top:0;bottom:0;width:6px;
  background:linear-gradient(90deg,transparent,rgba(60,40,18,.22));
  pointer-events:none;
}

/* ── 顶部铜牌：桌面最里沿那一块 ────────────────────────────────── */
#rail{
  position:fixed;left:0;right:0;top:0;z-index:40;
  height:var(--railH,44px);
  display:flex;align-items:center;gap:10px;padding:0 12px;
  font-size:clamp(11px, calc(0.62vw + 8.4px), 17px);letter-spacing:.14em;color:#D9C39A;
  background:linear-gradient(180deg,#4A3115 0%,#2C1B08 56%,#190F04 100%);
  border-bottom:1px solid #6B4A20;
  box-shadow:0 3px 12px rgba(0,0,0,.8), inset 0 1px 0 rgba(255,214,150,.22);
  transform-origin:50% 0;
}
#rail>*{flex:0 0 auto;display:flex;align-items:center;gap:5px}
#rail .railK{color:#9C7F4E;font-size:.9em}
.railNight{color:var(--lamp-hi);letter-spacing:.3em;text-shadow:0 0 10px rgba(255,178,90,.45)}
.railTip{margin-left:auto;font-family:var(--f-type);font-size:.86em;letter-spacing:0;color:#9C7F4E;
  max-width:44%;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}

.coinStack{position:relative;width:17px;height:20px;display:block}
.coinStack i{
  position:absolute;left:1px;width:14px;height:5px;border-radius:50%;
  background:radial-gradient(60% 80% at 38% 30%, #F0CF92, #B87333 62%, #5C3D1C 100%);
  box-shadow:0 1px 2px rgba(0,0,0,.7), inset 0 0 0 .5px rgba(255,220,160,.34);
}
.railNum{color:#C9A876;font-family:var(--f-type);font-size:1em;min-width:1.8em;text-align:right}

.railLine{position:relative;display:block;width:52px;height:3px;background:#2A1C0D;border-radius:2px}
.railLine i{
  position:absolute;top:-2px;width:2px;height:7px;border-radius:1px;
  background:var(--vermilion);box-shadow:0 0 6px rgba(192,57,43,.75);
}
.railSlips{position:relative;display:block;width:32px;height:15px}
.railSlips i{
  position:absolute;left:0;bottom:0;width:14px;height:12px;
  background:linear-gradient(170deg,#F3EAD2,#CBBB96);
  border:1px solid #9C8A63;box-shadow:0 1px 3px rgba(0,0,0,.7);
}
.railSlots{display:flex;gap:3px}
.railSlots i{
  width:9px;height:12px;display:block;border:1px solid #6B4A20;border-radius:1px;
  background:linear-gradient(180deg,#190F04,#0C0702);
  box-shadow:inset 0 1px 2px rgba(0,0,0,.9);
}
.railSlots i.on{background:linear-gradient(180deg,#F3EAD2,#CBBB96);border-color:#9C8A63}

/* ══ 纸上的通用版式 ══════════════════════════════════════════════ */
.docHead{
  display:flex;justify-content:space-between;align-items:baseline;
  padding-bottom:5px;border-bottom:1px solid rgba(36,58,94,.42);
}
.docTitle{font-size:1.05em;letter-spacing:.34em;color:var(--ink)}
.docMeta{font-family:var(--f-type);font-size:.62em;letter-spacing:.02em;color:#7A6C4C}
.docNote{font-size:.66em;line-height:1.8;color:#6B5F42}
.hand{font-family:var(--f-hand);font-size:1.25em}

/* ══ 抄报台 ══════════════════════════════════════════════════════ */
.slip{
  flex:0 0 auto;
  padding:6px 9px 8px;
  background:linear-gradient(172deg,#FFF9EA,var(--paper) 70%);
  border:1px solid rgba(36,58,94,.26);
  border-radius:1px;
  box-shadow:inset 0 0 30px rgba(120,95,55,.14);
  transform:rotate(-.3deg);
}
.slipHead{
  display:flex;justify-content:space-between;align-items:baseline;
  padding-bottom:4px;margin-bottom:5px;
  border-bottom:1px solid rgba(36,58,94,.42);
  font-size:.62em;letter-spacing:.24em;color:var(--ink);opacity:.82;
}
.slipNo{font-family:var(--f-type);letter-spacing:.04em}
.slipRow{display:flex;gap:7px;align-items:baseline;font-size:.66em;line-height:1.65}
.slipK{flex:0 0 auto;font-size:.82em;letter-spacing:.18em;color:var(--ink);opacity:.5}
.slipBody{
  display:flex;flex-wrap:wrap;gap:4px;justify-content:center;
  margin:6px 0 5px;padding:6px 0;
  border-top:1px dashed rgba(36,58,94,.3);
  border-bottom:1px dashed rgba(36,58,94,.3);
  min-height:2.6em;align-items:center;
}
.cell{
  position:relative;width:1.85em;height:1.85em;
  display:flex;align-items:center;justify-content:center;
  border:1px solid rgba(36,58,94,.34);border-radius:1px;
  background:rgba(255,255,255,.30);
}
.cellChar{font-size:1.15em;line-height:1;color:var(--ink)}
.cellChar.blank{opacity:.16}
.cellChar.bad{color:var(--vermilion);text-decoration:line-through;text-decoration-thickness:1px}
.cellDigits{
  position:absolute;bottom:-2px;right:1px;font-family:var(--f-type);
  font-size:.42em;letter-spacing:0;color:var(--ink);opacity:.46;
}
.cellDigits.bad{color:var(--vermilion);opacity:.9}
.cell.is-now{border-color:var(--lamp-lo);background:rgba(255,178,90,.20);
  box-shadow:0 0 10px rgba(255,178,90,.28)}
.slipFoot{font-family:var(--f-type);font-size:.56em;letter-spacing:.02em;color:var(--ink);opacity:.62}

/* 示波器：实时显出点划。关掉它就是盲抄。 */
.scope{
  flex:0 0 auto;display:flex;align-items:center;gap:7px;height:2.1em;padding:0 7px;
  background:linear-gradient(180deg,#12161C,#0A0C10);
  border:1px solid rgba(36,58,94,.5);border-radius:var(--r);
  box-shadow:inset 0 0 14px rgba(0,0,0,.9);
  overflow:hidden;transition:none;
}
.scopeLbl{font-size:.56em;letter-spacing:.18em;color:#7A90A8;flex:0 0 auto}
#cvScope{flex:1 1 auto;height:1.5em;display:block;min-width:0}
.scope.is-off{opacity:.34}

/* 处置 */
.verdict{flex:0 0 auto;display:flex;flex-direction:column;gap:5px;opacity:0;pointer-events:none}
.verdict.is-on{opacity:1;pointer-events:auto}
.verdictQ{font-size:.62em;letter-spacing:.1em;color:#6B5F42;text-align:center;line-height:1.6}
.verdictRow{display:flex;gap:5px}
.vBtn{
  flex:1;display:flex;flex-direction:column;align-items:center;gap:1px;padding:6px 3px;
  border:1px solid rgba(36,58,94,.42);border-radius:var(--r);
  background:linear-gradient(168deg,rgba(255,255,255,.5),rgba(255,255,255,.16));
  color:var(--ink);font-family:var(--f-print);cursor:pointer;
}
.vBtn b{font-weight:400;font-size:.82em;letter-spacing:.14em}
.vBtn i{font-style:normal;font-size:.5em;letter-spacing:.04em;color:#7A6C4C;font-family:var(--f-type)}
.vBtn:active{transform:translateY(1px)}
.vReport{border-color:rgba(192,57,43,.55)}
.vReport b{color:var(--vermilion)}
.vBtn:disabled{opacity:.34;cursor:default}

.dialHead{
  flex:0 0 auto;margin-top:auto;
  display:flex;align-items:baseline;gap:8px;
  padding-top:5px;border-top:1px solid rgba(36,58,94,.28);
  font-size:.58em;letter-spacing:.16em;color:#6B5F42;
}
.dhPos{color:var(--oxblood);letter-spacing:.06em}
.dhChips{margin-left:auto;display:flex;gap:5px;letter-spacing:0}
.dhChips b{
  font-weight:400;font-size:1em;letter-spacing:.06em;padding:2px 6px;
  border:1px solid rgba(36,58,94,.4);border-radius:2px;color:#7A6C4C;cursor:pointer;
}
.dhChips b.on{color:var(--paper);background:rgba(36,58,94,.8);border-color:var(--ink)}
.dhChips b.act{color:var(--vermilion);border-color:rgba(192,57,43,.5)}

/* ══ 密电码本 ════════════════════════════════════════════════════ */
.bookBody{flex:1 1 auto;min-height:0;display:flex;gap:6px}
.pagePicker{
  flex:0 0 21%;overflow-y:auto;display:grid;grid-template-columns:repeat(2,1fr);
  gap:1px;align-content:start;border-right:1px solid rgba(36,58,94,.24);
}
.pp{
  position:relative;padding:3px 2px;border:0;background:none;cursor:pointer;
  font-family:var(--f-type);font-size:.60em;color:#7A6C4C;text-align:center;border-radius:1px;
}
.pp small{display:block;font-size:.72em;opacity:.6}
.pp.on{background:rgba(36,58,94,.16);color:var(--ink);font-weight:700}
.pp.has::before{
  content:'';position:absolute;left:2px;top:50%;width:3px;height:3px;border-radius:50%;
  background:var(--ink);opacity:.6;transform:translateY(-50%);
}
.bookMain{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;gap:4px}
.bookPageHead{display:flex;justify-content:space-between;align-items:baseline;font-size:.7em;letter-spacing:.12em}
.codeGrid{
  flex:1 1 auto;min-height:0;
  display:grid;grid-template-columns:repeat(10,1fr);grid-template-rows:repeat(10,1fr);
  gap:1px;background:rgba(36,58,94,.2);border:1px solid rgba(36,58,94,.38);
}
.cg{
  position:relative;background:rgba(255,255,255,.30);
  display:flex;align-items:center;justify-content:center;
  font-size:.9em;line-height:1;color:var(--ink);
}
.cg.empty{background:rgba(255,255,255,.06)}
.cg .cgn{position:absolute;left:1px;top:0;font-family:var(--f-type);font-size:.34em;color:var(--ink);opacity:.3}
.cg.hit{background:var(--lamp);color:#3A2508;box-shadow:inset 0 0 0 2px var(--lamp-lo)}
.cg.near{background:rgba(192,57,43,.18)}
.cg.tap{cursor:pointer}

/* ══ 稽核簿 ══════════════════════════════════════════════════════ */
.lvGrid{display:grid;grid-template-columns:repeat(2,1fr);gap:5px;flex:0 0 auto}
.lv{
  display:flex;flex-direction:column;gap:2px;padding:5px 6px;
  background:rgba(255,255,255,.34);border:1px solid rgba(36,58,94,.26);border-radius:2px;
}
.lvK{font-size:.54em;letter-spacing:.28em;color:#6B5F42}
.lvV{font-size:1.15em;letter-spacing:.02em;font-family:var(--f-type);color:var(--ink)}
.lvLine{height:3px;background:rgba(36,58,94,.2);border-radius:2px;position:relative;display:block}
.lvLine i{position:absolute;top:-2px;width:2px;height:7px;background:var(--vermilion);border-radius:1px}
.lvSlips{display:flex;gap:3px;height:11px;align-items:flex-end}
.lvSlips i{
  width:9px;height:11px;display:block;
  background:linear-gradient(170deg,#FFF9EA,#CBBB96);
  border:1px solid #9C8A63;box-shadow:0 1px 2px rgba(0,0,0,.4);
}
.lvSlips i.coin{
  width:11px;height:4px;border-radius:50%;border:0;
  background:radial-gradient(60% 80% at 38% 30%,#F0CF92,#B87333 66%,#5C3D1C);
}
.lvSub{font-size:.5em;color:#7A6C4C}
.ledgerLog{
  flex:1 1 auto;min-height:0;overflow-y:auto;
  border-top:1px solid rgba(36,58,94,.34);padding-top:5px;
  display:flex;flex-direction:column;gap:4px;
}
.lgRow{display:flex;gap:7px;align-items:baseline;font-size:.62em;line-height:1.5;
  padding-bottom:4px;border-bottom:1px dotted rgba(36,58,94,.24)}
.lgNight{flex:0 0 2.6em;font-family:var(--f-type);font-size:.86em;color:#7A6C4C}
.lgTxt{flex:1 1 auto;min-width:0;font-family:var(--f-hand);font-size:1.5em}
.lgTag{flex:0 0 auto;font-size:.86em;letter-spacing:.06em;font-family:var(--f-type);padding:1px 4px;border-radius:1px}
.lgTag.deliver{color:#3F6B3A;background:rgba(63,107,58,.16)}
.lgTag.report{color:var(--vermilion);background:rgba(192,57,43,.16)}
.lgTag.hold{color:#8A6F34;background:rgba(138,111,52,.18)}
.lgWrong{color:var(--vermilion);font-size:.8em;font-family:var(--f-type)}
.lgEmpty{font-size:.66em;color:#7A6C4C;line-height:2;text-align:center;padding:16px 0}

/* ══ 听音练习 ════════════════════════════════════════════════════ */
.drillBody{flex:1 1 auto;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:9px}
.drillChar{font-size:3.6em;line-height:1;color:var(--oxblood)}
.drillCode{font-family:var(--f-type);font-size:1.5em;letter-spacing:.42em;color:var(--ink)}
.dBtn{
  padding:6px 20px;border:1px solid rgba(36,58,94,.42);border-radius:var(--r);
  background:linear-gradient(168deg,rgba(255,255,255,.5),rgba(255,255,255,.16));
  color:var(--ink);font-family:var(--f-print);font-size:.72em;letter-spacing:.16em;cursor:pointer;
}
.dBtn:active{transform:translateY(1px)}
.drillStat{font-family:var(--f-type);font-size:.56em;color:#7A6C4C;text-align:center;line-height:1.8;white-space:pre-line}

/* ══ 听音台 ══════════════════════════════════════════════════════ */
.pnGroup{display:flex;flex-direction:column;gap:4px;flex:0 0 auto}
.pnRow{display:flex;align-items:center;gap:8px;font-size:.64em;color:#3C4A5E}
.pnRow>span:first-child{flex:0 0 3.6em;letter-spacing:.08em}
.pnRow input[type=range]{flex:1 1 auto;min-width:0;accent-color:var(--oxblood)}
.pnRow .pnV{flex:0 0 3.6em;text-align:right;font-family:var(--f-type);font-size:.86em}
.seg{display:flex;gap:3px;flex:1 1 auto}
.seg button{
  flex:1;padding:4px 2px;border:1px solid rgba(36,58,94,.34);border-radius:2px;
  background:rgba(255,255,255,.34);color:#5B6A7E;font-family:var(--f-print);
  font-size:.92em;letter-spacing:.06em;cursor:pointer;
}
.seg button.on{background:rgba(36,58,94,.84);color:var(--paper);border-color:var(--ink)}
.seg button:active{transform:translateY(1px)}
.pnNote{font-size:.54em;color:#7A6C4C;line-height:1.7}

/* ══ 物件名牌：面向相机的公告板 ══════════════════════════════════ */
.tag{
  position:absolute;left:0;top:0;transform-origin:0 0;
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:1px;
  padding:.34em .68em;border:1px solid rgba(255,214,150,.30);border-radius:.22em;
  background:linear-gradient(180deg,rgba(74,49,21,.86),rgba(25,15,4,.9));
  box-shadow:0 6px 16px rgba(0,0,0,.6), inset 0 1px 0 rgba(255,214,150,.22);
  color:#E8D0A4;font-family:var(--f-print);cursor:pointer;opacity:0;
}
.tag b{font-weight:400;font-size:1em;letter-spacing:.22em;color:var(--lamp-hi);
  text-shadow:0 0 10px rgba(255,178,90,.4)}
.tag i{font-style:normal;font-size:.82em;font-family:var(--f-type);letter-spacing:0;color:#A98C5C}
.tag.hot{border-color:rgba(255,178,90,.72);box-shadow:0 6px 18px rgba(0,0,0,.6), 0 0 22px rgba(255,178,90,.28), inset 0 1px 0 rgba(255,214,150,.3)}
.tag:active{transform-origin:0 0;filter:brightness(1.25)}

/* ══ 加载页：世界的第一帧 ════════════════════════════════════════ */
.bootCard{
  background:none;box-shadow:none;padding:0;
  align-items:center;justify-content:center;gap:14px;
  display:flex;flex-direction:column;
}
.bootTitle{
  font-size:2.6em;font-weight:400;letter-spacing:.9em;text-indent:.9em;
  color:var(--lamp-hi);opacity:0;
  text-shadow:0 0 30px rgba(255,178,90,.5), 0 0 70px rgba(255,140,50,.28);
}
.bootSub{font-size:.62em;letter-spacing:.34em;color:#A98C5C;opacity:0;font-family:var(--f-type)}
.bootTap{
  margin-top:6px;padding:8px 22px;border:1px solid var(--brass-lo);border-radius:var(--r);
  background:linear-gradient(180deg,rgba(74,49,21,.7),rgba(25,15,4,.8));
  color:var(--lamp);font-family:var(--f-print);font-size:.68em;letter-spacing:.28em;
  cursor:pointer;opacity:0;
}

/* ══ 连续层 ③ 后处理 ════════════════════════════════════════════ */
#cvFx{position:fixed;inset:0;width:100%;height:100%;display:block;z-index:60;pointer-events:none}

/* ══ 浮层 ════════════════════════════════════════════════════════
   底不消失：只压暗 + 失焦。浮层出现时背后的持续动不停（主循环一直在跑）。 */
#ovlRoot{position:fixed;inset:0;z-index:70;pointer-events:none}
.ovl{
  position:absolute;inset:0;display:flex;align-items:center;justify-content:center;
  padding:22px;pointer-events:auto;
}
.ovlScrim{
  position:absolute;inset:0;background:rgba(10,5,10,.60);
  backdrop-filter:blur(4px) saturate(.82);-webkit-backdrop-filter:blur(4px) saturate(.82);
}
.ovlCard{
  position:relative;width:100%;max-width:clamp(300px,44vw,560px);max-height:100%;overflow-y:auto;
  padding:18px 18px 16px;border-radius:2px;
  background:linear-gradient(168deg,var(--paper-hi) 0%,var(--paper) 58%,#D6C6A2 100%);
  color:var(--ink);
  box-shadow:0 26px 54px rgba(0,0,0,.82), inset 0 0 60px rgba(120,95,55,.16);
}
.ovlCard.dark{
  background:linear-gradient(168deg,#2A1A0C,#150D05);
  color:#EAD6AE;border:1px solid var(--brass-lo);
}
.ovlTitle{
  font-size:1.0em;letter-spacing:.34em;margin-bottom:10px;padding-bottom:7px;
  border-bottom:1px solid rgba(36,58,94,.42);
}
.ovlCard.dark .ovlTitle{border-color:var(--brass-lo);color:var(--lamp)}
.ovlMeta{font-family:var(--f-type);font-size:.58em;color:#7A6C4C;letter-spacing:.04em;margin-bottom:9px}
.ovlCard.dark .ovlMeta{color:#A98C5C}
.ovlBody{font-size:.74em;line-height:2.05;white-space:pre-wrap}
.ovlBody.hand{font-family:var(--f-hand);font-size:1.25em;line-height:1.95}
.ovlBtns{display:flex;gap:7px;margin-top:14px}
.ovlBtns button{
  flex:1;padding:10px 5px;border:1px solid rgba(36,58,94,.42);border-radius:var(--r);
  background:rgba(255,255,255,.4);color:var(--ink);
  font-family:var(--f-print);font-size:.74em;letter-spacing:.14em;cursor:pointer;
}
.ovlBtns button.pri{background:rgba(36,58,94,.86);color:var(--paper);border-color:var(--ink)}
.ovlCard.dark .ovlBtns button{background:rgba(255,178,90,.10);border-color:var(--brass-lo);color:#EAD6AE}
.ovlCard.dark .ovlBtns button.pri{background:linear-gradient(180deg,#8A5F2A,#5C3D1C);border-color:#A9743A;color:var(--lamp-hi)}
.ovlBtns button:active{transform:translateY(1px)}

.briefCard{background:linear-gradient(170deg,#F5EEDC,#DFD2B0)}
.briefCard .ovlTitle{font-size:.76em;letter-spacing:.5em;color:#6B5F42;text-align:center}
.briefBody{font-size:.72em;line-height:2.15;text-indent:2em}
.briefStamp{
  position:absolute;right:18px;bottom:60px;font-size:.54em;letter-spacing:.18em;color:var(--vermilion);
  border:1.5px solid var(--vermilion);border-radius:1px;padding:3px 6px;opacity:.6;transform:rotate(-7deg);
}

.resTapeWrap{margin:10px 0 4px;padding:7px;background:rgba(36,58,94,.08);
  border:1px solid rgba(36,58,94,.24);border-radius:2px}
.resTapeWrap canvas{width:100%;height:46px;display:block}
.resRows{display:flex;flex-direction:column;gap:1px;margin-top:9px}
.resRow{display:flex;justify-content:space-between;font-size:.66em;padding:4px 2px;
  border-bottom:1px dotted rgba(36,58,94,.24)}
.resRow b{font-weight:400;font-family:var(--f-type);font-size:1.04em}
.gStep{display:flex;gap:9px;font-size:.72em;line-height:1.85;margin-top:7px}
.gStep b{
  flex:0 0 17px;height:17px;border-radius:50%;background:rgba(36,58,94,.82);color:var(--paper);
  font-size:.62em;font-weight:400;display:flex;align-items:center;justify-content:center;
}

/* ══ 提示：排队、不夺焦 ══════════════════════════════════════════ */
#toastRoot{
  position:fixed;left:0;right:0;bottom:calc(14px + env(safe-area-inset-bottom,0px));
  z-index:80;display:flex;flex-direction:column;align-items:center;gap:6px;
  pointer-events:none;padding:0 14px;
}
.toast{
  max-width:min(560px,100%);padding:7px 12px;border-radius:var(--r);
  background:linear-gradient(180deg,rgba(58,39,18,.96),rgba(22,14,5,.96));
  border:1px solid var(--brass-lo);color:#EAD6AE;
  font-size:.66em;letter-spacing:.04em;line-height:1.6;
  box-shadow:0 10px 24px rgba(0,0,0,.72);
}
.toast.warn{border-color:#8E3A22;color:#F0B49E}
.toast.ok{border-color:#3F6B3A;color:#C4DDB8}

/* ══ 码本速览卡：翻到那一页，看见那个字在哪一格 ══════════════════
   它不是模态框：出现时不夺焦，也不停住背后的东西 —— 灯还在晃、雪还在下。
   动画由补间层驱动，不用 CSS transition。 */
.revealCard{
  position:fixed;left:50%;top:12%;z-index:65;
  display:flex;align-items:center;gap:10px;padding:9px 13px;
  background:linear-gradient(172deg,var(--paper-hi),#DED1AF);
  border-radius:2px;color:var(--ink);white-space:nowrap;
  box-shadow:0 18px 38px rgba(0,0,0,.76), 0 0 30px rgba(255,178,90,.10),
             inset 0 0 34px rgba(120,95,55,.16);
  opacity:0;pointer-events:none;transform:translate(-50%,0) scale(.94);
}
.rvCode{font-family:var(--f-type);font-size:20px;letter-spacing:.2em;color:var(--ink)}
.rvGrid{
  display:grid;grid-template-columns:repeat(3,1fr);gap:1px;padding:1px;
  background:rgba(36,58,94,.24);border:1px solid rgba(36,58,94,.32);border-radius:1px;
}
.rvCell{
  width:22px;height:22px;display:flex;align-items:center;justify-content:center;
  background:rgba(255,255,255,.44);font-style:normal;font-size:13px;color:var(--ink);
}
.rvCell.hit{background:var(--lamp);color:#3A2508;box-shadow:inset 0 0 0 1.5px var(--lamp-lo)}
.rvCell.was{background:rgba(192,57,43,.22)}
.rvRes{font-size:12px;text-align:center;line-height:1.5}
.rvRes em{font-style:normal;color:var(--vermilion);display:block;font-size:10.5px}
.revealCard.bad .rvCode{color:var(--vermilion)}

/* ══ 响应式：宽屏时字号与留白跟着走 ══════════════════════════════ */
@media (min-width:820px){
  #world3d{perspective-origin:50% 50%}
}
@media (max-height:520px) and (orientation:landscape){
  #rail{height:34px;font-size:10px}
  .railTip{display:none}
}
