/* ============================================================
   数字炸弹 · NUMBER BOMB
   赛博朋克 HUD 样式表
   配色由 src/visual/palette.js 在运行时写入 :root 变量，
   此处给的是 neon 默认值（同时也是无 JS 时的兜底）。
   ============================================================ */

:root {
  --bg: #000308;
  --c1: #00f0ff;        /* 主：青 */
  --c2: #ff2bd6;        /* 副：品红 */
  --c3: #ff2a3d;        /* 警报：红 */
  --c4: #ffc400;        /* 提示：琥珀 */
  --txt: #d8f4ff;
  --txt-dim: rgba(216, 244, 255, 0.42);
  --line: rgba(0, 240, 255, 0.22);
  --line-strong: rgba(0, 240, 255, 0.55);
  --panel: rgba(0, 240, 255, 0.035);

  --font-mono: ui-monospace, "Cascadia Mono", "Consolas", "SF Mono", "Menlo",
    "Microsoft YaHei", monospace;
  --font-sans: "Inter", "PingFang SC", "Microsoft YaHei", system-ui, sans-serif;

  --pad: clamp(14px, 2.2vw, 34px);
  --danger: 0; /* 0→1，由 JS 写入，驱动整体紧张度 */
}

* { margin: 0; padding: 0; box-sizing: border-box; }

html, body {
  height: 100%;
  background: var(--bg);
  color: var(--txt);
  font-family: var(--font-mono);
  overflow: hidden;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

body {
  cursor: default;
  user-select: none;
}

/* ---------------- 3D 画布与叠加层 ---------------- */

#gl {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  z-index: 0;
}

.fx {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 1;
}

/* 四角暗角，把视线压到中央 */
.fx-vignette {
  background:
    radial-gradient(120% 90% at 50% 50%, transparent 38%, rgba(0, 0, 0, 0.55) 78%, #000 100%),
    radial-gradient(60% 40% at 50% 110%, rgba(0, 240, 255, 0.07), transparent 70%);
}

/* CRT 扫描线 */
.fx-scanlines {
  background: repeating-linear-gradient(
    to bottom,
    rgba(255, 255, 255, 0.035) 0px,
    rgba(255, 255, 255, 0.035) 1px,
    transparent 1px,
    transparent 3px
  );
  mix-blend-mode: overlay;
  opacity: calc(0.5 + var(--danger) * 0.45);
  animation: scan-drift 7s linear infinite;
}

@keyframes scan-drift {
  from { transform: translateY(0); }
  to   { transform: translateY(3px); }
}

/* 噪点 / 颗粒（内联 SVG 湍流，不依赖外部图片，拷走即用） */
.fx-grain {
  opacity: 0.05;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size: 140px 140px;
  mix-blend-mode: screen;
  animation: grain-jitter 0.9s steps(3) infinite;
}

@keyframes grain-jitter {
  0%   { transform: translate(0, 0); }
  33%  { transform: translate(-2px, 1px); }
  66%  { transform: translate(1px, -2px); }
  100% { transform: translate(0, 0); }
}

/* 危险时的红色脉冲边框 */
.fx-danger {
  opacity: var(--danger);
  box-shadow: inset 0 0 120px rgba(255, 42, 61, 0.55);
  border: 1px solid rgba(255, 42, 61, 0.35);
  transition: opacity 0.3s ease;
}

/* ---------------- 通用面板装饰 ---------------- */

.panel {
  position: relative;
  background: linear-gradient(180deg, var(--panel), transparent 70%);
  border: 1px solid var(--line);
  backdrop-filter: blur(2px);
}

/* 四角括线 */
.panel::before,
.panel::after {
  content: "";
  position: absolute;
  width: 10px;
  height: 10px;
  border: 1px solid var(--line-strong);
  pointer-events: none;
}
.panel::before {
  top: -1px; left: -1px;
  border-right: 0; border-bottom: 0;
}
.panel::after {
  bottom: -1px; right: -1px;
  border-left: 0; border-top: 0;
}

.eyebrow {
  font-size: 10px;
  letter-spacing: 0.34em;
  text-transform: uppercase;
  color: var(--txt-dim);
  font-weight: 500;
}

/* ---------------- HUD 布局 ---------------- */

.hud {
  position: fixed;
  z-index: 5;
  pointer-events: none;
}

.hud-top {
  top: 0; left: 0; right: 0;
  padding: var(--pad);
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: start;
  gap: 20px;
}

/* 品牌 */
.brand {
  display: flex;
  align-items: baseline;
  gap: 10px;
  font-size: clamp(13px, 1.15vw, 16px);
  letter-spacing: 0.18em;
  color: var(--txt);
}
.brand-mark {
  color: var(--c1);
  text-shadow: 0 0 12px currentColor;
  font-size: 0.9em;
  letter-spacing: -0.1em;
}
.brand em {
  font-style: normal;
  font-size: 0.72em;
  letter-spacing: 0.3em;
  color: var(--txt-dim);
}

/* 剩余区间条 */
.range-block {
  width: clamp(280px, 34vw, 520px);
  text-align: center;
}
.range-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin-bottom: 7px;
}
.range-count {
  font-size: 11px;
  letter-spacing: 0.18em;
  color: var(--c1);
  opacity: 0.9;
}
.range-count b {
  font-size: 13px;
  text-shadow: 0 0 10px currentColor;
}

.range-track {
  position: relative;
  height: 8px;
  background:
    repeating-linear-gradient(
      135deg,
      rgba(255, 255, 255, 0.05) 0 3px,
      transparent 3px 6px
    );
  border: 1px solid var(--line);
  overflow: hidden;
}
.range-window {
  position: absolute;
  top: 0; bottom: 0;
  background: linear-gradient(90deg, var(--c1), var(--c2));
  box-shadow: 0 0 14px var(--c1), 0 0 30px rgba(0, 240, 255, 0.4);
  transition: left 0.55s cubic-bezier(0.16, 1, 0.3, 1),
              width 0.55s cubic-bezier(0.16, 1, 0.3, 1);
}
.range-window::after {
  content: "";
  position: absolute; inset: 0;
  background: repeating-linear-gradient(90deg, rgba(0,0,0,0.35) 0 2px, transparent 2px 4px);
}

.range-bounds {
  display: flex;
  justify-content: space-between;
  margin-top: 6px;
  font-size: clamp(12px, 1.05vw, 15px);
  letter-spacing: 0.1em;
}
.range-bounds span { color: var(--txt); }
.range-bounds .lo::before { content: "‹ "; color: var(--txt-dim); }
.range-bounds .hi::after  { content: " ›"; color: var(--txt-dim); }

/* 右上：模式 / 轮次 */
.meta {
  justify-self: end;
  text-align: right;
  display: grid;
  gap: 4px;
}
.meta-row {
  font-size: 11px;
  letter-spacing: 0.2em;
  color: var(--txt-dim);
}
.meta-row b {
  color: var(--c1);
  font-weight: 600;
  letter-spacing: 0.12em;
}
.meta-row .warn { color: var(--c4); }

/**
 * 局内的模式入口：点一下（或按 M）在单人 / 双人之间切。
 * 之前只认 URL 参数 ?mode=duo，界面上根本没有入口 ——
 * 现场对着摄像头的人不会去改地址栏，等于双人模式不存在。
 */
.meta-switch {
  pointer-events: auto;
  justify-self: end;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: inherit;
  font-size: 11px;
  letter-spacing: 0.2em;
  color: var(--txt-dim);
  /* 常驻的边框 + 底色：不这样它看起来就是一行普通文字，
     没人会想到能点。悬停只是为了确认，不是唯一的提示。 */
  background: rgba(0, 240, 255, 0.05);
  border: 1px solid var(--line);
  padding: 3px 9px;
  margin: -3px -9px;
  cursor: pointer;
  white-space: nowrap;
  transition: color 0.16s ease, border-color 0.16s ease, background 0.16s ease;
}
.meta-switch:hover {
  color: var(--txt);
  border-color: var(--c1);
  background: rgba(0, 240, 255, 0.12);
}
.meta-switch:focus-visible {
  outline: 2px solid var(--c1);
  outline-offset: 2px;
}
.meta-switch .meta-flip {
  font-style: normal;
  font-size: 11px;
  opacity: 0.7;
}

/**
 * 双人的「轮到谁」。
 * 之前只是 meta 里一行小字，靠 opacity 0.45→1 区分 —— 在投影仪上几乎看不出差别，
 * 两个人对着镜头时会互相等，或者同时伸手。
 * 改成带边框和底色的胶囊，并且用颜色把人分开（不靠颜色也还能读文字，是双重编码）。
 */
.turn-pill {
  justify-self: end;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 4px 10px 4px 8px;
  margin-top: 1px;
  border: 1px solid rgba(0, 240, 255, 0.45);
  background: rgba(0, 240, 255, 0.09);
  color: var(--txt);
  transition: color 0.2s ease, border-color 0.2s ease, background 0.2s ease;
}
.turn-pill .tp-dot {
  width: 6px;
  height: 6px;
  flex: none;
  border-radius: 50%;
  background: currentColor;
  box-shadow: 0 0 8px currentColor;
  animation: tpBreath 1.9s ease-in-out infinite;
}
@keyframes tpBreath {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: 0.45; transform: scale(0.72); }
}
.turn-pill[data-turn="1"] {
  color: var(--c2);
  border-color: rgba(255, 43, 214, 0.45);
  background: rgba(255, 43, 214, 0.09);
}
.turn-pill[data-turn="1"] b { color: var(--c2); }
.turn-pill[data-active="0"] {
  opacity: 0.5;
  animation: none;
}
.turn-pill[data-active="0"] .tp-dot { animation: none; }

/* ---------------- 中央读数 ---------------- */

.hud-center {
  inset: 0;
  display: grid;
  place-items: center;
  align-content: center;
  padding-bottom: 6vh;
}

.readout {
  position: relative;
  display: grid;
  place-items: center;
  gap: 14px;
  padding: 0 8px;
}

/**
 * 中央的换手牌。
 *
 * 为什么不放在右上角、也不放在 toast 那一行：
 *   · 右上角离读数太远，只有专门去找才看得见；
 *   · toast 是"刚刚发生了什么"的流水账，2 秒就消失，不能承担常驻状态。
 * 双人模式下"轮到谁"是**持续有效**的状态，必须一直挂在那里，
 * 而玩家的视线本来就压在读数与锁定环上，所以贴着它们放。
 *
 * 绝对定位、不占流：显示/隐藏时读数不会上下跳。
 */
.turn-call {
  position: absolute;
  left: 50%;
  top: calc(100% + 18px);
  display: grid;
  grid-auto-flow: column;
  align-items: center;
  gap: 12px;
  padding: 8px 20px;
  white-space: nowrap;
  pointer-events: none;
  color: var(--c1);
  border: 1px solid currentColor;
  background: rgba(0, 6, 14, 0.78);
  opacity: 0;
  transform: translate(-50%, 8px);
  transition: opacity 0.24s ease, transform 0.24s ease;
}
.turn-call[data-show="1"] {
  opacity: 1;
  transform: translate(-50%, 0);
}
/* 对局已结束：人还指得出来，但不再是"该你上了" —— 压暗而不是整块消失，
   否则结算卡弹出前的 1.2 秒里提示会莫名闪断一次 */
.turn-call[data-show="1"][data-active="0"] {
  opacity: 0.5;
  transform: translate(-50%, 0);
}
.turn-call[data-turn="0"] {
  color: var(--c1);
  box-shadow: 0 0 26px rgba(0, 240, 255, 0.28), inset 0 0 20px rgba(0, 240, 255, 0.07);
}
.turn-call[data-turn="1"] {
  color: var(--c2);
  box-shadow: 0 0 26px rgba(255, 43, 214, 0.28), inset 0 0 20px rgba(255, 43, 214, 0.07);
}
.turn-call .tc-tag {
  font-size: 9px;
  letter-spacing: 0.34em;
  opacity: 0.62;
}
.turn-call .tc-who {
  font-size: clamp(17px, 1.9vw, 24px);
  font-weight: 500;
  letter-spacing: 0.1em;
  color: currentColor;
  text-shadow: 0 0 18px currentColor;
  transform-origin: center;
}
.turn-call .tc-en {
  font-size: 9px;
  letter-spacing: 0.3em;
  opacity: 0.42;
}
/* 换手那一刻给一下明显的动作，否则静息状态下颜色变化太容易被忽略 */
.turn-call[data-flip="1"] .tc-who {
  animation: tcFlip 0.62s cubic-bezier(0.18, 0.9, 0.2, 1);
}
@keyframes tcFlip {
  0% { transform: scale(1); }
  30% { transform: scale(1.22); }
  100% { transform: scale(1); }
}
.turn-call[data-flip="1"] {
  animation: tcFlash 0.62s ease-out;
}
@keyframes tcFlash {
  0% { box-shadow: 0 0 6px 0 currentColor; }
  35% { box-shadow: 0 0 50px 10px currentColor; }
  100% { box-shadow: 0 0 26px 0 currentColor; }
}

/**
 * 键盘模式下收起这块大牌。
 * 数字键盘本身就压在读数正下方，两者在矮屏上必然打架；
 * 而且此时换手信息还有 toast 里的「· 轮到 玩家 X」和右上角胶囊两处兜底。
 */
body.input-keypad .turn-call { display: none; }

.digit-pair {
  display: flex;
  align-items: flex-end;
  gap: clamp(12px, 2vw, 30px);
}

.digit-cell {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  justify-items: center;
  gap: 6px;
  min-width: clamp(84px, 11vw, 150px);
  padding: 2px 10px 10px;
}

.digit-cell .dp {
  order: 2;
  font-size: 10px;
  letter-spacing: 0.34em;
  color: var(--txt-dim);
  text-transform: uppercase;
  /* 背后可能是粒子拼成的巨型数字，光靠 text-shadow 压不住密集光点，
     必须给一层极淡的暗底，保证这几个字在任何背景上都读得出来。 */
  padding: 2px 7px;
  border-radius: 2px;
  background: rgba(2, 6, 14, 0.55);
  text-shadow: 0 0 4px #000, 0 0 10px #000;
}

.digit-cell .dv {
  order: 1;
  font-size: clamp(64px, 11vw, 148px);
  line-height: 1;
  font-weight: 300;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.02em;
  color: var(--txt);
  text-shadow:
    -1px 0 rgba(255, 43, 214, 0.55),
    1px 0 rgba(0, 240, 255, 0.55),
    0 0 26px rgba(0, 240, 255, 0.35);
  transition: color 0.25s ease, opacity 0.25s ease;
}

.digit-cell[data-state="unknown"] .dv {
  color: rgba(216, 244, 255, 0.16);
  text-shadow: none;
}
.digit-cell[data-state="active"] .dv {
  color: #fff;
  text-shadow:
    -2px 0 rgba(255, 43, 214, 0.9),
    2px 0 rgba(0, 240, 255, 0.9),
    0 0 40px rgba(0, 240, 255, 0.7);
}

/* 手指指示点：五根手指是否伸出，颜色冗余于数字（不只靠数字辨识） */
.finger-dots {
  order: 3;
  display: flex;
  gap: 5px;
  margin-top: 2px;
}
.finger-dots i {
  width: 14px; height: 3px;
  background: rgba(216, 244, 255, 0.15);
  transition: background 0.15s ease, box-shadow 0.15s ease;
}
.finger-dots i[data-on="1"] {
  background: var(--c1);
  box-shadow: 0 0 8px var(--c1);
}
.digit-cell[data-state="active"] .finger-dots i[data-on="1"] {
  background: #fff;
  box-shadow: 0 0 10px #fff;
}

/* 组合数字间的分隔 */
.digit-sep {
  width: 1px;
  align-self: stretch;
  margin: clamp(30px, 5vw, 62px) 4px clamp(46px, 6vw, 72px);
  background: linear-gradient(180deg, transparent, var(--line-strong), transparent);
  box-shadow: 0 0 12px var(--c1);
}

/* 锁定环 */
.lock {
  display: grid;
  place-items: center;
  gap: 10px;
  min-height: 72px;
  margin-top: 4px;
}
.lock svg { width: 46px; height: 46px; transform: rotate(-90deg); overflow: visible; }
.lock .track { fill: none; stroke: rgba(216, 244, 255, 0.14); stroke-width: 2; }
.lock .bar {
  fill: none;
  stroke: var(--c1);
  stroke-width: 3;
  stroke-linecap: butt;
  filter: drop-shadow(0 0 6px var(--c1));
  transition: stroke 0.2s ease;
}
.lock-label {
  font-size: 10px;
  letter-spacing: 0.3em;
  color: var(--txt-dim);
  text-align: center;
  white-space: nowrap;
  text-shadow: 0 0 4px #000, 0 0 10px #000, 0 1px 2px #000;
}
.lock[data-state="locked"] .bar { stroke: var(--c2); filter: drop-shadow(0 0 10px var(--c2)); }
.lock[data-state="locked"] .lock-label { color: var(--c2); }

/* 这个数已经报过了：环空着，但读数还亮着 —— 必须用文字点明"换个数字"，
   否则看起来就是"识别成功但卡住不动" */
.lock[data-state="done"] .lock-label { color: var(--c4); }

/* ---------------- 画中画 ---------------- */

.pip {
  position: fixed;
  right: var(--pad);
  bottom: calc(var(--pad) + 46px);
  width: clamp(200px, 17vw, 300px);
  aspect-ratio: 4 / 3;
  z-index: 6;
  border: 1px solid var(--line);
  background: #000;
  overflow: hidden;
  transition: opacity 0.25s ease, transform 0.25s ease;
}
.pip[data-hidden="1"] {
  opacity: 0;
  transform: translateY(10px) scale(0.96);
  pointer-events: none;
}
/* 键盘模式：整个收掉，不然框里那句「把手放进虚线框内」是条错误指令 */
.pip[data-off="1"] {
  display: none;
}
/* 键盘模式下，只对手势有效的快捷键提示也一并隐去 */
body.input-keypad .key-hand {
  display: none;
}

.pip video,
.pip canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transform: scaleX(-1); /* 镜像：让玩家看到镜子效果 */
}
.pip video { filter: saturate(0.55) contrast(1.08) brightness(0.82) hue-rotate(-8deg); }
.pip canvas { pointer-events: none; }

/* 取景框 */
.pip-frame {
  position: absolute;
  inset: 8%;
  border: 1px dashed rgba(0, 240, 255, 0.3);
  pointer-events: none;
}
.pip-frame::before,
.pip-frame::after {
  content: "";
  position: absolute;
  background: var(--c1);
  opacity: 0.55;
}
.pip-frame::before { left: 0; right: 0; top: 50%; height: 1px; }
.pip-frame::after  { top: 0; bottom: 0; left: 50%; width: 1px; }

.pip-tag {
  position: absolute;
  left: 8px; top: 6px;
  font-size: 9px;
  letter-spacing: 0.24em;
  color: var(--c1);
  opacity: 0.85;
  text-shadow: 0 0 8px var(--c1);
}
.pip[data-hands="0"] { border-color: rgba(255, 42, 61, 0.6); }
.pip[data-hands="0"] .pip-tag { color: var(--c3); }
.pip[data-hands="1"] { border-color: rgba(255, 196, 0, 0.5); }

.pip-hint {
  position: absolute;
  inset: auto 0 0 0;
  padding: 5px 8px;
  font-size: 10px;
  letter-spacing: 0.08em;
  color: rgba(216, 244, 255, 0.75);
  background: linear-gradient(0deg, rgba(0, 0, 0, 0.8), transparent);
  text-align: center;
}

.pip-toggle {
  position: absolute;
  right: 4px; top: 4px;
  width: 20px; height: 20px;
  display: grid; place-items: center;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--txt-dim);
  background: rgba(0, 0, 0, 0.5);
  border: 1px solid var(--line);
  cursor: pointer;
  z-index: 2;
}
.pip-toggle:hover { color: var(--c1); border-color: var(--c1); }

/* ---------------- 底部状态栏 ---------------- */

.hud-bottom {
  left: 0; right: 0; bottom: 0;
  padding: 0 var(--pad) calc(var(--pad) * 0.7);
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 16px;
}

.stat-strip {
  display: flex;
  gap: clamp(14px, 2vw, 34px);
}
.stat {
  display: grid;
  gap: 3px;
}
.stat b {
  font-size: clamp(15px, 1.5vw, 20px);
  font-weight: 400;
  color: var(--txt);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.04em;
}

.keys {
  display: flex;
  gap: 12px;
  flex-wrap: wrap;
  justify-content: flex-end;
  font-size: 10px;
  letter-spacing: 0.14em;
  color: rgba(216, 244, 255, 0.3);
}
.keys kbd {
  display: inline-block;
  padding: 1px 4px;
  margin-right: 3px;
  border: 1px solid rgba(216, 244, 255, 0.2);
  font-family: inherit;
  font-size: 9px;
  color: rgba(216, 244, 255, 0.6);
}

/* ---------------- Toast ---------------- */

.toast {
  position: fixed;
  left: 50%;
  top: clamp(96px, 16vh, 190px);
  transform: translate(-50%, -6px);
  z-index: 8;
  padding: 9px 22px;
  font-size: clamp(13px, 1.2vw, 16px);
  letter-spacing: 0.16em;
  color: var(--txt);
  background: rgba(0, 6, 14, 0.82);
  border: 1px solid var(--line-strong);
  box-shadow: 0 0 30px rgba(0, 240, 255, 0.16);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.28s ease, transform 0.28s ease;
  white-space: nowrap;
  max-width: 92vw;
  overflow: hidden;
  text-overflow: ellipsis;
}
.toast[data-show="1"] {
  opacity: 1;
  transform: translate(-50%, 0);
}
.toast[data-tone="warn"] { border-color: rgba(255, 196, 0, 0.7); color: var(--c4); }
.toast[data-tone="bad"]  { border-color: rgba(255, 42, 61, 0.75); color: var(--c3); }

/* ---------------- 开始 / 结算遮罩 ---------------- */

.overlay {
  position: fixed;
  inset: 0;
  z-index: 20;
  display: grid;
  place-items: center;
  /* safe center：卡片比视口高时（小笔记本 + 新增的模式选择）
     普通的居中会把顶部裁到滚不到的地方，「启动摄像头」就可能永远点不着 */
  align-content: safe center;
  overflow-y: auto;
  padding: var(--pad);
  background:
    radial-gradient(90% 70% at 50% 40%, rgba(0, 240, 255, 0.06), transparent 70%),
    rgba(0, 2, 6, 0.86);
  backdrop-filter: blur(6px);
  opacity: 1;
  transition: opacity 0.4s ease, visibility 0.4s;
}
.overlay[hidden],
.overlay[data-hidden="1"] {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  display: grid; /* 保持布局，避免 display:none 造成动画跳变 */
}

.card {
  width: min(680px, 100%);
  padding: clamp(24px, 3.4vw, 46px);
  text-align: center;
  position: relative;
}

.card::before,
.card::after {
  content: "";
  position: absolute;
  left: 0; right: 0;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--line-strong), transparent);
}
.card::before { top: 0; }
.card::after  { bottom: 0; }

.card h1 {
  font-size: clamp(30px, 4.4vw, 60px);
  font-weight: 300;
  letter-spacing: 0.16em;
  line-height: 1.1;
  margin-bottom: 6px;
  text-shadow:
    -2px 0 rgba(255, 43, 214, 0.6),
    2px 0 rgba(0, 240, 255, 0.6),
    0 0 50px rgba(0, 240, 255, 0.35);
}
.card h1 small {
  display: block;
  font-size: 0.28em;
  letter-spacing: 0.42em;
  color: var(--txt-dim);
  margin-top: 12px;
  text-shadow: none;
}

.card .lede {
  font-size: clamp(12px, 1.05vw, 14px);
  line-height: 2;
  color: rgba(216, 244, 255, 0.66);
  letter-spacing: 0.06em;
  margin: 22px auto 26px;
  max-width: 46ch;
}
.card .lede b { color: var(--c1); font-weight: 500; }

.rules {
  display: grid;
  gap: 9px;
  text-align: left;
  margin: 0 auto 30px;
  max-width: 44ch;
  font-size: 12px;
  line-height: 1.75;
  color: rgba(216, 244, 255, 0.55);
  letter-spacing: 0.04em;
}
.rules li {
  list-style: none;
  padding-left: 18px;
  position: relative;
}
.rules li::before {
  content: "▸";
  position: absolute;
  left: 0;
  color: var(--c1);
  opacity: 0.65;
}

.actions {
  display: flex;
  gap: 12px;
  justify-content: center;
  flex-wrap: wrap;
}

/**
 * 封面上的模式选择。
 * 选中态不只靠颜色：右上角还有「已选」两个字 ——
 * 颜色在投影仪 / 色觉差异下会失真，文字不会。
 */
.mode-pick {
  display: grid;
  /* auto-fit：手机上两张卡片会自己竖排，不需要再写一个断点 */
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 10px;
  margin: 0 auto 28px;
  max-width: 480px;
}
.mode-opt {
  pointer-events: auto;
  position: relative;
  display: grid;
  gap: 7px;
  padding: 15px 16px;
  text-align: left;
  font-family: inherit;
  color: var(--txt-dim);
  background: rgba(0, 240, 255, 0.03);
  border: 1px solid var(--line);
  cursor: pointer;
  transition: color 0.18s ease, border-color 0.18s ease, background 0.18s ease,
    box-shadow 0.18s ease;
}
.mode-opt b {
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.18em;
}
.mode-opt span {
  font-size: 10px;
  letter-spacing: 0.06em;
  line-height: 1.7;
  opacity: 0.85;
}
.mode-opt:hover {
  color: var(--txt);
  border-color: var(--line-strong);
  background: rgba(0, 240, 255, 0.08);
}
.mode-opt:focus-visible {
  outline: 2px solid var(--c1);
  outline-offset: 2px;
}
.mode-opt[data-on="1"] {
  color: var(--txt);
  border-color: var(--c1);
  background: rgba(0, 240, 255, 0.1);
  box-shadow: 0 0 18px rgba(0, 240, 255, 0.18), inset 0 0 24px rgba(0, 240, 255, 0.06);
}
.mode-opt[data-on="1"] b { color: var(--c1); }
.mode-opt[data-on="1"]::after {
  content: "已选";
  position: absolute;
  top: 9px;
  right: 11px;
  font-size: 9px;
  letter-spacing: 0.18em;
  color: var(--c1);
}

.btn {
  pointer-events: auto;
  font-family: var(--font-mono);
  font-size: clamp(12px, 1.05vw, 14px);
  letter-spacing: 0.26em;
  text-transform: uppercase;
  padding: 14px 32px;
  color: var(--bg);
  background: var(--c1);
  border: 1px solid var(--c1);
  cursor: pointer;
  position: relative;
  transition: transform 0.16s ease, box-shadow 0.24s ease, background 0.24s ease;
  box-shadow: 0 0 22px rgba(0, 240, 255, 0.3);
}
.btn:hover {
  box-shadow: 0 0 34px rgba(0, 240, 255, 0.55);
  transform: translateY(-1px);
}
.btn:active { transform: translateY(0); }
.btn:focus-visible {
  outline: 2px solid var(--txt);
  outline-offset: 3px;
}

.btn.ghost {
  background: transparent;
  color: var(--txt);
  border-color: var(--line-strong);
  box-shadow: none;
}
.btn.ghost:hover {
  color: var(--c1);
  border-color: var(--c1);
  box-shadow: 0 0 22px rgba(0, 240, 255, 0.2);
}

.btn[disabled] {
  opacity: 0.35;
  pointer-events: none;
}

/* 加载进度 */
.loader {
  margin-top: 22px;
  display: grid;
  gap: 8px;
  justify-items: center;
}
.loader-bar {
  width: min(320px, 70%);
  height: 2px;
  background: rgba(216, 244, 255, 0.12);
  position: relative;
  overflow: hidden;
}
.loader-bar i {
  position: absolute;
  inset: 0;
  width: 0%;
  background: var(--c1);
  box-shadow: 0 0 12px var(--c1);
  transition: width 0.3s ease;
}
.loader-text {
  font-size: 10px;
  letter-spacing: 0.22em;
  color: var(--txt-dim);
}

/* 结算：胜负结论 */
/**
 * 结算卡之前只说"谁报中了炸弹"，把"谁赢"留给玩家自己反推。
 * 双人时这是最该先说的一句话，单人时则要说清"这局算不算打得过自己"。
 * 两种模式共用同一块，靠 data-kind 换语义，排版保持一致。
 */
.verdict {
  display: grid;
  justify-items: center;
  gap: 4px;
  margin: 4px 0 12px;
  opacity: 0;
  transform: translateY(6px);
  transition: opacity 0.32s ease 0.06s, transform 0.32s ease 0.06s;
}
.verdict[data-show="1"] {
  opacity: 1;
  transform: none;
}
.vd-tag {
  font-size: 9px;
  letter-spacing: 0.4em;
  color: var(--txt-dim);
}
.vd-line {
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: 12px;
  flex-wrap: wrap;
}
/* 双人是"玩家 2 获胜"一句话，横排；单人没有名字，横排会让结论孤零零挂在右边，
   改成三行递进：等级 → 成绩 → 结论 */
#result[data-mode="solo"] .vd-line {
  flex-direction: column;
  align-items: center;
  gap: 4px;
}
.vd-who {
  font-size: clamp(26px, 3.6vw, 44px);
  line-height: 1.12;
  font-weight: 400;
  letter-spacing: 0.02em;
  font-variant-numeric: tabular-nums;
}
#result[data-mode="duo"] .vd-who {
  font-size: clamp(34px, 5vw, 58px);
  letter-spacing: 0.04em;
}
.vd-sub {
  font-size: clamp(12px, 1.15vw, 15px);
  letter-spacing: 0.24em;
  opacity: 0.78;
}
/* 双人的"获胜"要能跟 58px 的名字配得上，太小就像事后贴上去的 */
#result[data-mode="duo"] .vd-sub {
  font-size: clamp(15px, 1.6vw, 21px);
  letter-spacing: 0.3em;
}

/**
 * 「上次 → 这次」。
 * 单人模式的成绩单看一个数字说明不了好坏，并排放才看得出是进是退，
 * 而且不需要读文字 —— 箭头方向本身就是结论。
 */
.vd-compare {
  display: none;
  align-items: center;
  gap: 10px;
  margin-top: 6px;
  padding: 4px 12px;
  font-size: 12px;
  letter-spacing: 0.16em;
  color: rgba(216, 244, 255, 0.62);
  border-top: 1px solid rgba(216, 244, 255, 0.14);
}
.vd-compare[data-show="1"] { display: inline-flex; }
.vd-compare b {
  font-size: 15px;
  font-weight: 500;
  color: var(--txt);
  font-variant-numeric: tabular-nums;
}
.vd-compare i {
  font-style: normal;
  font-size: 14px;
  opacity: 0.8;
}
/* 箭头方向 + 颜色双重编码，别只靠红绿 */
.vd-compare[data-dir="up"] { color: rgba(0, 240, 255, 0.8); }
.vd-compare[data-dir="up"] b { color: var(--c1); }
.vd-compare[data-dir="down"] { color: rgba(216, 244, 255, 0.5); }
.vd-compare[data-dir="down"] b { color: rgba(216, 244, 255, 0.82); }

/* 赢了：亮、带光。玩家 1 青 / 玩家 2 品红 —— 和局内换手牌同一套颜色，
   从头到尾一个人一个颜色，不用再记谁是谁 */
#result[data-verdict="win"][data-winner="0"] .vd-who {
  color: var(--c1);
  text-shadow: 0 0 34px rgba(0, 240, 255, 0.55);
}
#result[data-verdict="win"][data-winner="1"] .vd-who {
  color: var(--c2);
  text-shadow: 0 0 34px rgba(255, 43, 214, 0.55);
}
#result[data-verdict="win"] .vd-sub { opacity: 0.88; }
#result[data-verdict="win"][data-winner="0"] .vd-sub { color: var(--c1); }
#result[data-verdict="win"][data-winner="1"] .vd-sub { color: var(--c2); }

/* 没赢：不发光、不冒充庆祝，但仍然是这张卡的主语 */
#result[data-verdict="lose"] .vd-who {
  color: var(--txt);
  opacity: 0.86;
  text-shadow: none;
}

/* 双人时"谁赢"是主角，炸弹数退成配角，不然两个大数字互相抢 */
#result[data-mode="duo"] .result-number {
  font-size: clamp(38px, 6vw, 80px);
  margin: 4px 0 2px;
}
.rn-cap {
  font-size: 10px;
  letter-spacing: 0.3em;
  color: var(--txt-dim);
  margin-bottom: 14px;
}

/* 结算数字 */
.result-number {
  font-size: clamp(50px, 7.6vw, 96px);
  line-height: 1;
  font-weight: 300;
  font-variant-numeric: tabular-nums;
  color: var(--c3);
  text-shadow: 0 0 50px rgba(255, 42, 61, 0.55);
  margin: 6px 0 2px;
}

.result-stats {
  display: flex;
  justify-content: center;
  gap: clamp(20px, 4vw, 56px);
  margin: 26px 0 30px;
}
.result-stats div { display: grid; gap: 5px; }
.result-stats b {
  font-size: clamp(20px, 2.4vw, 30px);
  font-weight: 400;
  color: var(--txt);
  font-variant-numeric: tabular-nums;
}

/* ---------------- 数字键盘兜底 ---------------- */

.keypad {
  position: fixed;
  z-index: 9;
  left: 50%;
  bottom: calc(var(--pad) + 34px);
  transform: translateX(-50%);
  display: none;
  grid-template-columns: repeat(10, 1fr);
  gap: 5px;
  padding: 8px 10px;
  border: 1px solid var(--line);
  background: rgba(0, 6, 14, 0.82);
  backdrop-filter: blur(8px);
}
.keypad[data-show="1"] { display: grid; }
.keypad button {
  pointer-events: auto;
  width: clamp(32px, 3.2vw, 44px);
  height: clamp(32px, 3.2vw, 44px);
  font-family: var(--font-mono);
  font-size: 16px;
  color: var(--txt);
  background: rgba(0, 240, 255, 0.06);
  border: 1px solid var(--line);
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease;
}
.keypad button:hover { background: rgba(0, 240, 255, 0.2); color: #fff; }
.keypad button:focus-visible { outline: 2px solid var(--c1); outline-offset: 2px; }
.keypad .kp-hint {
  grid-column: 1 / -1;
  font-size: 9px;
  letter-spacing: 0.2em;
  color: var(--txt-dim);
  text-align: center;
  padding-top: 2px;
}

/* ---------------- 调试面板 ---------------- */

.debug {
  position: fixed;
  left: var(--pad);
  bottom: calc(var(--pad) + 46px);
  z-index: 10;
  font-size: 10px;
  line-height: 1.55;
  letter-spacing: 0.04em;
  color: rgba(216, 244, 255, 0.72);
  background: rgba(0, 4, 10, 0.85);
  border: 1px solid var(--line);
  padding: 8px 11px;
  max-width: 330px;
  white-space: pre-wrap;
  word-break: break-all;
  overflow: hidden;
  max-height: 46vh;
  display: none;
  pointer-events: none;
}
.debug[data-show="1"] { display: block; }
.debug b { color: var(--c1); font-weight: 500; }
.debug .bad { color: var(--c3); }

/* ---------------- 全局状态：危险 / 震动 / 闪光 ---------------- */

body { --shake: 0; }

#app {
  position: fixed;
  inset: 0;
  transform: translate3d(0, 0, 0);
}

body[data-shake="1"] #app {
  animation: shake 0.42s cubic-bezier(0.36, 0.07, 0.19, 0.97);
}
@keyframes shake {
  10%, 90% { transform: translate3d(-2px, 1px, 0); }
  20%, 80% { transform: translate3d(4px, -2px, 0); }
  30%, 50%, 70% { transform: translate3d(-6px, 2px, 0); }
  40%, 60% { transform: translate3d(6px, -1px, 0); }
}

.flash {
  position: fixed;
  inset: 0;
  z-index: 15;
  background: #fff;
  opacity: 0;
  pointer-events: none;
}
.flash[data-fire="1"] {
  animation: flash 0.55s ease-out;
}
@keyframes flash {
  0% { opacity: 0.92; }
  40% { opacity: 0.42; }
  100% { opacity: 0; }
}

/* 危险度高时整体轻微偏红 */
body[data-danger="mid"] { --line: rgba(255, 43, 214, 0.3); }
body[data-danger="high"] { --line: rgba(255, 42, 61, 0.45); }
body[data-danger="high"] .brand-mark { color: var(--c3); }

/* 横向故障条（爆炸瞬间） */
body[data-glitch="1"] .hud { animation: glitch-shift 0.4s steps(2) 3; }
@keyframes glitch-shift {
  0%   { clip-path: inset(0 0 0 0); transform: translateX(0); }
  25%  { clip-path: inset(20% 0 55% 0); transform: translateX(-8px); }
  50%  { clip-path: inset(60% 0 12% 0); transform: translateX(6px); }
  75%  { clip-path: inset(35% 0 40% 0); transform: translateX(-3px); }
  100% { clip-path: inset(0 0 0 0); transform: translateX(0); }
}

/* ---------------- 响应式 ---------------- */

@media (max-width: 860px) {
  .hud-top { grid-template-columns: 1fr auto; }
  .range-block { grid-column: 1 / -1; width: 100%; order: 3; }
  .meta { order: 2; }
  .pip {
    width: 132px;
    bottom: auto;
    top: 96px;
  }
  .keys { display: none; }
  .digit-sep { display: none; }
  .digit-cell { min-width: 70px; }
}

@media (max-height: 640px) {
  /* 矮屏（小笔记本 / 分屏）上把封面压紧一点：
     加了模式选择之后，卡片高度已经很接近视口高度了 */
  .card .lede { margin: 14px auto 16px; line-height: 1.8; }
  .mode-pick { margin-bottom: 18px; }
  .mode-opt { padding: 11px 13px; gap: 5px; }
  .rules { margin-bottom: 18px; gap: 5px; }
  .btn { padding: 11px 24px; }
}

@media (max-height: 560px) {
  .lock { height: 44px; }
  .digit-cell .dv { font-size: clamp(44px, 8vw, 84px); }
  /* 矮屏上把换手牌压成一条，否则会顶到底部统计条 */
  .turn-call { top: calc(100% + 10px); padding: 5px 14px; }
  .turn-call .tc-tag,
  .turn-call .tc-en { display: none; }
  .turn-call .tc-who { font-size: 16px; }
}

@media (prefers-reduced-motion: reduce) {
  .fx-scanlines,
  .fx-grain { animation: none; }
  body[data-shake="1"] #app { animation: none; }
  .range-window { transition: none; }
}

/* 性能降级时同步削减整屏装饰层。
   这两个层都用了 mix-blend-mode，成本压在 GPU 合成阶段，
   光降低粒子数并不能把这部分省回来 —— 必须显式关掉。 */
body[data-tier="0"] .fx-scanlines,
body[data-tier="0"] .fx-grain,
body[data-tier="1"] .fx-grain {
  display: none;
}

/* 手动关装饰：?fx=0（投影仪偏暗或画面发灰时用） */
body.fx-off .fx-scanlines,
body.fx-off .fx-grain {
  display: none;
}

/* 截图核对用：?nofade=1 关掉所有过渡 */
body.no-anim *,
body.no-anim *::before,
body.no-anim *::after {
  transition: none !important;
  animation: none !important;
}
