/*
 * star-duel · 皮肤与版式。
 *
 * 两条纪律：
 * ① **只覆盖 `--gs-*` 变量，不覆盖任何 `.gs-*` 选择器**——覆盖选择器会把组件库的
 *    可用性（44px 触控下限、安全区、焦点环）一起改坏。本作自己的东西一律用 `.sd-` 前缀。
 * ② **不写任何「宽屏 / 桌面」版式。** 视口比手机宽时，组件库末尾的「手机画布锁定」
 *    把 `#app` 收成一块 540×960 的手机画布居中摆着，桌面玩家玩到的就是手机上验收过的那一屏。
 *    所以这份文件里**没有一条不带 `max-height` 限定的 `@media (min-width: …)`**。
 *    手机横屏是手机的一个朝向、不是桌面，它那一档写成
 *    `(orientation: landscape) and (max-height: 560px)`（560 取自 shared/phone-canvas.json）。
 *
 * 几何是**流式**的：`--sd-avail-h` / `--sd-avail-w` 由 ui.mjs 量**画布**
 * （分区容器的 getBoundingClientRect()，不读 window.innerWidth/Height）写进来，
 * 抢格尺寸由下面的 clamp() 现算，不是一张固定坐标表（GAME_PLAN §10.1 / §14 P36b）。
 */

:root {
  /* ---- 冷暖两侧：本作全部配色的根 ---- */
  --sd-warm: #ff9a1f;          /* P1（下 / 右） */
  --sd-warm-soft: #ffb43c;
  --sd-warm-deep: #b45c00;
  --sd-cool: #33d6ff;          /* P2（上 / 左） */
  --sd-cool-soft: #7ce6ff;
  --sd-cool-deep: #0b6f96;

  --sd-void: #070a1f;
  --sd-void-2: #0b1026;
  --sd-panel: rgba(14, 23, 48, 0.82);
  --sd-panel-raised: rgba(24, 36, 70, 0.92);
  --sd-rule: rgba(122, 162, 235, 0.28);
  --sd-rule-strong: #2f6fd0;

  /* 三个星等：大小之外还有形状与色相（§10.5 色觉：颜色不是唯一区分手段） */
  --sd-s1: #ffd257;
  --sd-s3: #58e0ff;
  --sd-s7: #e451c8;

  /* 流式几何。ui.mjs 每次重排后写这两个数（单位 px，值由画布矩形量出）。 */
  --sd-avail-h: 640px;
  --sd-avail-w: 360px;

  /* §10.1 竖屏标准档：抢格之间恒 8；抢格排↔共享条 / ↔HUD 是 12，抢格排↔星场是 8 */
  --sd-pad-gap: 8px;
  --sd-edge: 16px;
  --sd-gap-hud: 12px;
  --sd-gap-field: 8px;
  --sd-hud-top: 52px;
  --sd-hud-bottom: 56px;
  --sd-shared: 48px;           /* 共享条恒 48 高：它装着 48×48 的暂停键，三档都不收紧 */

  --sd-pad-w: clamp(44px, (var(--sd-avail-w) - 2 * var(--sd-edge) - 4 * var(--sd-pad-gap)) / 5, 96px);
  --sd-pad-h: clamp(56px, (var(--sd-avail-h) - 408px) / 2, 110px);

  /* ---- 组件库令牌：换皮只动这些 ---- */
  --gs-ink: #f4f8ff;
  --gs-ink-soft: rgba(212, 227, 255, 0.78);
  --gs-ink-dim: rgba(174, 195, 236, 0.55);
  --gs-ink-on-light: #0a1430;

  --gs-surface: var(--sd-panel);
  --gs-surface-raised: var(--sd-panel-raised);
  --gs-surface-sunken: rgba(6, 10, 26, 0.66);
  --gs-hairline: var(--sd-rule);

  --gs-accent: var(--sd-cool);
  --gs-accent-2: var(--sd-warm);
  --gs-gold: #ffd257;
  --gs-danger: #ff6b6b;

  --gs-glow-accent: 0 0 0 1px rgba(51, 214, 255, 0.5), 0 0 26px rgba(51, 214, 255, 0.32);
  --gs-glow-gold: 0 0 24px rgba(255, 210, 87, 0.5);
  --gs-shadow-card: 0 18px 40px rgba(3, 6, 20, 0.55);
  --gs-shadow-button: 0 6px 0 rgba(0, 0, 0, 0.34), 0 12px 24px rgba(3, 6, 20, 0.46);
  --gs-shadow-press: 0 2px 0 rgba(0, 0, 0, 0.34), 0 6px 12px rgba(3, 6, 20, 0.46);

  --gs-cta: linear-gradient(135deg, var(--sd-warm-soft), var(--sd-warm));
  --gs-bg-focus: 50% 42%;
  --gs-radius-md: 16px;
}

/* ── 六套涂装：纯外观，一个判定数都不碰（§5.2 / GAME_PLAN §12 skins_note）──
   换的只有两侧的色相与几处辉光。**两侧的冷暖分工与抢格上的方向箭头永不改变**：
   即使把两侧调成同一个色系，箭头与上下位置仍然分得出谁是谁。 */
:root[data-skin='ember']  { --sd-warm: #ff6a3d; --sd-warm-soft: #ffa06a; --sd-cool: #ffd0a8; --sd-cool-soft: #ffe7d4; }
:root[data-skin='frost']  { --sd-warm: #7fd4ff; --sd-warm-soft: #b9e9ff; --sd-cool: #4f7dff; --sd-cool-soft: #9db4ff; }
:root[data-skin='citrus'] { --sd-warm: #ffd02e; --sd-warm-soft: #ffe377; --sd-cool: #7be06a; --sd-cool-soft: #b4f0a8; }
:root[data-skin='violet'] { --sd-warm: #c86bff; --sd-warm-soft: #dfa6ff; --sd-cool: #5ad2c2; --sd-cool-soft: #9eeae0; }
:root[data-skin='mono']   { --sd-warm: #e6ecff; --sd-warm-soft: #ffffff; --sd-cool: #8ea4cc; --sd-cool-soft: #c3d2ee; }
:root[data-skin='prism']  { --sd-warm: #ff8ad0; --sd-warm-soft: #ffc2e6; --sd-cool: #6ee7ff; --sd-cool-soft: #b3f2ff; }

/* 高对比：星等改用形状区分（四角 / 五角 / 六角，画在 canvas 里），
   DOM 这一侧把描边与文字对比度拉满。**判定一个字不变。** */
:root[data-contrast='on'] {
  --gs-ink-soft: #ffffff;
  --gs-ink-dim: rgba(255, 255, 255, 0.82);
  --sd-rule: rgba(255, 255, 255, 0.55);
  --sd-panel: rgba(4, 8, 20, 0.95);
}

html, body {
  margin: 0;
  background: var(--sd-void);
  color: var(--gs-ink);
  font-family: 'Trebuchet MS', 'Hiragino Sans', 'Noto Sans', system-ui, sans-serif;
  overscroll-behavior: none;
  -webkit-text-size-adjust: 100%;
}

#app {
  position: relative;
  height: 100dvh;
  overflow: hidden;
  background: radial-gradient(120% 80% at 50% 8%, #16204a 0%, var(--sd-void-2) 52%, var(--sd-void) 100%);
}

/* ══════════════ 对局屏：六区骨架（§10.1） ══════════════
   区序恒为 P2 HUD → P2 抢格 → 共享条 → 星场 → P1 抢格 → P1 HUD。
   五处间距就是相邻六区之间的那五处，**写进 grid 的 row-gap 里，不是靠 margin 叠**。 */
.sd-stage {
  position: absolute;
  inset: 0;
  display: grid;
  grid-template-rows:
    var(--sd-hud-top)
    var(--sd-pad-h)
    var(--sd-shared)
    minmax(140px, 1fr)
    var(--sd-pad-h)
    var(--sd-hud-bottom);
  row-gap: 0;
  padding: var(--gs-safe-top) var(--gs-safe-right) var(--gs-safe-bottom) var(--gs-safe-left);
  box-sizing: border-box;
}

/* 五处间距（标准档 12 / 12 / 8 / 8 / 12），用 margin 写在分区自己身上，
   因为三档的取值不同而 grid 的 row-gap 只有一个值。 */
.sd-stage > [data-zone='hud-p2']    { margin-bottom: var(--sd-gap-hud); }
.sd-stage > [data-zone='pads-p2']   { margin-bottom: var(--sd-gap-hud); }
.sd-stage > [data-zone='shared-bar'] { margin-bottom: var(--sd-gap-field); }
.sd-stage > [data-zone='star-field'] { margin-bottom: var(--sd-gap-field); }
.sd-stage > [data-zone='pads-p1']   { margin-bottom: var(--sd-gap-hud); }

/* 紧凑档（400 ≤ H < 520）：HUD 收薄、五处间距一律 8 */
.sd-stage[data-tier='compact'] {
  --sd-hud-top: 40px;
  --sd-hud-bottom: 44px;
  --sd-gap-hud: 8px;
  --sd-gap-field: 8px;
  --sd-pad-h: clamp(44px, (var(--sd-avail-h) - 312px) / 2, 72px);
  grid-template-rows: var(--sd-hud-top) var(--sd-pad-h) var(--sd-shared) minmax(140px, 1fr) var(--sd-pad-h) var(--sd-hud-bottom);
}

/* 最小档（300 ≤ H < 400）：两条 HUD 并进共享条，于是只剩四区、三处间距。
   **区序仍然不变**——两侧的比分数字挪到共享条的左右两端。 */
.sd-stage[data-tier='minimal'] {
  --sd-gap-hud: 8px;
  --sd-gap-field: 8px;
  --sd-pad-h: clamp(44px, (var(--sd-avail-h) - 212px) / 2, 64px);
  grid-template-rows: var(--sd-pad-h) var(--sd-shared) minmax(140px, 1fr) var(--sd-pad-h);
}
.sd-stage[data-tier='minimal'] > [data-zone='hud-p2'],
.sd-stage[data-tier='minimal'] > [data-zone='hud-p1'] { display: none; }
.sd-stage[data-tier='minimal'] .sd-bar__score { display: flex; }
.sd-bar__score { display: none; align-items: center; gap: 4px; font-weight: 800; }
.sd-bar__score[data-side='p1'] { color: var(--sd-warm); }
.sd-bar__score[data-side='p2'] { color: var(--sd-cool); }

/* ── 两侧 HUD ── */
.sd-hud {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: var(--gs-space-2);
  padding: 0 var(--sd-edge);
  box-sizing: border-box;
  min-width: 0;
}
.sd-hud[data-side='p2'] { color: var(--sd-cool); }
.sd-hud[data-side='p1'] { color: var(--sd-warm); }

.sd-hud__score {
  font-size: clamp(22px, calc(var(--sd-avail-h) * 0.045), 40px);
  font-weight: 900;
  line-height: 1;
  letter-spacing: -0.02em;
  text-shadow: 0 0 18px currentColor;
  font-variant-numeric: tabular-nums;
}
.sd-hud__mid { min-width: 0; display: grid; gap: 4px; }

/* 分数条：两条从中线向外推，长度与比分成比例（SCREEN_SPEC screen-06 的 score-bar）。
   领先易主时 `data-flip` 让它对冲一次。 */
.sd-scorebar {
  position: relative;
  height: 8px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.12);
  overflow: hidden;
}
.sd-scorebar__fill {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 50%;
  width: var(--sd-fill, 0%);
  border-radius: 999px;
  background: currentColor;
  transform: translateX(-50%);
  transition: width var(--gs-normal) var(--gs-ease-out), height var(--gs-normal) var(--gs-ease-out);
}
.sd-scorebar[data-flip='true'] { animation: sd-flip 320ms var(--gs-ease-out) 2; }
@keyframes sd-flip { 50% { box-shadow: 0 0 0 2px currentColor; } }

.sd-points { display: flex; gap: 6px; }
.sd-points__dot {
  width: 12px; height: 12px; border-radius: 999px;
  border: 2px solid currentColor; opacity: 0.45;
}
.sd-points__dot[data-on='true'] { opacity: 1; background: currentColor; box-shadow: 0 0 12px currentColor; }

.sd-shields { display: flex; gap: 4px; }
.sd-shields__pip { width: 22px; height: 22px; }
.sd-shields__pip[data-state='broken'] { opacity: 0.5; }

/* 抢手锁定条：**从空充满**，方向与另外四条条相反。
   §6.5 的规矩——它归零是解除惩罚，做成减少的条就是在骗玩家。 */
.sd-locktrack {
  height: 6px; border-radius: 999px;
  background: rgba(255, 255, 255, 0.14);
  overflow: hidden;
}
.sd-locktrack__fill {
  display: block;
  height: 100%; width: 0%;
  background: linear-gradient(90deg, #3ddc84, #b6ffcf);
  box-shadow: 0 0 12px rgba(61, 220, 132, 0.7);
}

/* ── 共享条（恒 48 高，装着 48×48 的暂停键） ── */
.sd-bar {
  display: grid;
  grid-template-columns: auto 1fr auto 48px;
  align-items: center;
  gap: var(--gs-space-2);
  padding: 0 var(--sd-edge);
  box-sizing: border-box;
  min-width: 0;
}
/* 暂停键是唯一一个不随档位变的尺寸（§10.1）：三档都是 48×48，共享条也因此恒为 48 高。
   写在 `.sd-pause` 上而不是覆盖 `.gs-icon-btn`——组件的下限是 44，这里只把它放大到本作的固定值。 */
.sd-pause { width: 48px; height: 48px; min-width: 48px; min-height: 48px; }
.sd-bar__label { font-size: var(--gs-text-xs); color: var(--gs-ink-soft); white-space: nowrap; }
.sd-bar__clock {
  font-size: var(--gs-text-lg); font-weight: 900; font-variant-numeric: tabular-nums;
  min-width: 2.2em; text-align: center;
}
.sd-bar__clock[data-urgent='true'] { color: var(--gs-danger); animation: sd-pulse 1s infinite; }
@keyframes sd-pulse { 50% { opacity: 0.45; } }

.sd-roundtrack { height: 6px; border-radius: 999px; background: rgba(255,255,255,0.14); overflow: hidden; }
.sd-roundtrack__fill { display: block; height: 100%; background: linear-gradient(90deg, var(--sd-cool), var(--sd-warm)); }

/* 星表条：36 个小格，走完 = 不再有新星（§6.5） */
.sd-supply { display: flex; gap: 1px; height: 10px; align-items: stretch; min-width: 0; }
.sd-supply__cell { flex: 1 1 0; min-width: 1px; border-radius: 1px; background: rgba(255,255,255,0.16); }
.sd-supply__cell[data-left='true'] { background: var(--gs-gold); box-shadow: 0 0 6px rgba(255,210,87,0.6); }

/* ── 星场：唯一一块 canvas，只画不收输入 ── */
.sd-field { position: relative; min-height: 140px; }
.sd-field canvas { display: block; width: 100%; height: 100%; touch-action: none; }
/* 星场之上的覆盖层：入座图示、教学气泡、开局倒数、突星提示。
   它**不接收指针**（星场只画不收输入），也不占用别的分区——
   教学气泡压住玩家正要按的那一格，是上一版最常见的走查缺陷。 */
.sd-fieldlayer {
  position: absolute; inset: 0; pointer-events: none;
  display: grid; align-content: center; justify-items: center; gap: var(--gs-space-2);
  padding: var(--gs-space-2);
}
.sd-fieldlayer > * { pointer-events: auto; }
.sd-fieldlayer:empty { display: none; }
.sd-countin { font-size: clamp(40px, 14vw, 96px); font-weight: 900; text-shadow: 0 0 30px var(--gs-gold); }

/* ── 抢格排：十个真实 <button> ── */
.sd-pads {
  display: grid;
  grid-template-columns: repeat(5, var(--sd-pad-w));
  gap: var(--sd-pad-gap);
  justify-content: center;
  align-items: stretch;
  padding: 0 var(--sd-edge);
  box-sizing: border-box;
}

.sd-pad {
  position: relative;
  min-width: var(--gs-tap-min);
  min-height: var(--gs-tap-min);
  border: 0;
  padding: 0;
  border-radius: 14px;
  background: linear-gradient(180deg, rgba(255,255,255,0.1), rgba(255,255,255,0.02));
  box-shadow: inset 0 0 0 2px currentColor, 0 6px 0 rgba(0,0,0,0.3);
  touch-action: none;
  -webkit-tap-highlight-color: transparent;
  cursor: pointer;
  overflow: visible;
  display: grid;
  place-items: center;
  /* 命中区比视觉区大：沿排列轴各外扩 4（= 相邻间距 8 的一半，相邻恰好相接不重叠），
     垂直于排列轴各外扩 min(6, 到相邻可点元素间距的一半)。
     用 ::before 撑出去，视觉边框留在按钮本体上（§2.1 的命中区表）。 */
}
.sd-pad::before {
  content: '';
  position: absolute;
  inset: -6px -4px;           /* 竖屏：排列轴是 x → 左右 4，跨轴上下 6 */
  border-radius: 18px;
}
.sd-pad[data-side='p1'] { color: var(--sd-warm); }
.sd-pad[data-side='p2'] { color: var(--sd-cool); }
.sd-pad[data-held='true'] { background: radial-gradient(120% 100% at 50% 100%, currentColor, transparent 70%); }
.sd-pad[data-locked='true'] { filter: brightness(0.42) saturate(0.5); }
.sd-pad:focus-visible { outline: 3px solid var(--gs-gold); outline-offset: 3px; }

/* 格里那条光带：抢格自己的 DOM 上唯一会变的东西（后果画在画布上） */
.sd-pad__glow {
  position: absolute; left: 0; right: 0; bottom: 0;
  height: 0%;
  background: linear-gradient(180deg, transparent, currentColor);
  opacity: 0.8;
  pointer-events: none;
}
.sd-pad[data-side='p2'] .sd-pad__glow { bottom: auto; top: 0; background: linear-gradient(0deg, transparent, currentColor); }

/* 列徽记 + 方向箭头。箭头是**颜色之外的第二条线索**（P1 向下、P2 向上）。 */
.sd-pad__sigil { width: 56%; max-width: 34px; opacity: 0.9; filter: var(--gs-icon-tint-ink); pointer-events: none; }
.sd-pad__arrow { position: absolute; font-size: 11px; line-height: 1; opacity: 0.85; pointer-events: none; }
.sd-pad[data-side='p1'] .sd-pad__arrow { bottom: 3px; }
.sd-pad[data-side='p2'] .sd-pad__arrow { top: 3px; }

/* ══════════════ 菜单屏（标题 / 模式 / 帮助 / 战绩 / 每日 / 分享 / 错误） ══════════════
   这些屏用组件库的 Screen + GlowCard 骨架，这里只补本作特有的几种排布。 */
.sd-screen { position: absolute; inset: 0; display: flex; flex-direction: column; }
.sd-bg { position: absolute; inset: 0; z-index: 0; }
.sd-bg img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: var(--gs-bg-focus); }
.sd-bg__scrim { position: absolute; inset: 0; background: linear-gradient(180deg, rgba(4,7,22,0.35), rgba(4,7,22,0.82)); }
.sd-screen > .gs-screen { position: relative; z-index: 1; }

.sd-topbar {
  display: grid; grid-template-columns: auto 1fr auto; align-items: center;
  gap: var(--gs-space-2); padding: var(--gs-space-2) var(--sd-edge);
}
.sd-topbar__icons { display: flex; gap: var(--gs-tap-gap); }
/* 加载屏与错误屏的品牌徽章。**无字**——游戏名是八个不同的词，烧进图里等于锁死七种语言。 */
.sd-bootmark { width: min(44%, 180px); height: auto; margin: 0 auto; display: block; }

/* 标题页的 2×2 模式网格。锁着的卡**照样在网格里**，压暗 + 锁头 + 写明等级。 */
.sd-modegrid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--gs-space-3); }
.sd-modecard {
  position: relative; display: grid; gap: 6px; justify-items: start;
  min-height: 118px; padding: var(--gs-space-3);
  border-radius: var(--gs-radius-md); border: 2px solid var(--sd-rule-strong);
  background: var(--sd-panel); color: var(--gs-ink); text-align: left;
  box-shadow: var(--gs-shadow-card); cursor: pointer;
}
.sd-modecard[data-mode='duel']     { border-color: var(--sd-warm); }
.sd-modecard[data-mode='echo']     { border-color: var(--sd-cool); }
.sd-modecard[data-mode='daily']    { border-color: #a071ff; }
.sd-modecard[data-mode='practice'] { border-color: #6fe08a; }
.sd-modecard[data-locked='true']   { filter: saturate(0.35) brightness(0.68); }
.sd-modecard__art { width: 44px; height: 44px; }
.sd-modecard__name { font-size: var(--gs-text-md); font-weight: 800; }
.sd-modecard__blurb { font-size: var(--gs-text-xs); color: var(--gs-ink-soft); }
.sd-modecard__lock { display: flex; align-items: center; gap: 4px; font-size: var(--gs-text-xs); color: var(--gs-gold); }
.sd-modecard__lock img { width: 14px; height: 14px; filter: var(--gs-icon-tint-gold); }

/* 模式选择页的三段选择器 */
.sd-section { display: grid; gap: var(--gs-space-2); margin-bottom: var(--gs-space-4); }
.sd-section__title {
  align-self: start; margin: 0; padding: 4px 12px;
  font-size: var(--gs-text-sm); font-weight: 800; letter-spacing: 0.04em;
  border-radius: 8px 8px 0 0; background: var(--sd-panel-raised);
}
.sd-picker { display: flex; gap: var(--gs-tap-gap); flex-wrap: wrap; }
.sd-choice {
  position: relative; display: grid; gap: 4px; justify-items: center;
  min-width: var(--gs-tap-min); min-height: 68px; padding: 8px 6px;
  border-radius: 12px; border: 2px solid transparent; background: var(--sd-panel);
  color: var(--gs-ink); cursor: pointer; flex: 1 1 18%;
}
.sd-choice[aria-pressed='true'] { border-color: var(--gs-accent); box-shadow: var(--gs-glow-accent); }
.sd-choice[data-locked='true'] { filter: saturate(0.3) brightness(0.66); }
.sd-choice img { width: 34px; height: 34px; }
.sd-choice__name { font-size: var(--gs-text-xs); font-weight: 700; }
/* 五等分强度条：灰度与色觉障碍下**唯一**排得出顺序的那条线索 */
.sd-rank { display: flex; gap: 2px; }
.sd-rank__step { width: 6px; height: 4px; border-radius: 1px; background: rgba(255,255,255,0.2); }
.sd-rank__step[data-on='true'] { background: var(--gs-gold); }
.sd-choice__blurb { font-size: var(--gs-text-xs); color: var(--gs-ink-soft); }

/* 入座屏：与对局屏**同一套六区**，星场那块位置换成持握图示 */
.sd-seatfig { display: grid; place-items: center; gap: var(--gs-space-2); padding: 0 var(--sd-edge); }
.sd-seatfig img { max-width: 100%; max-height: 58%; object-fit: contain; }
.sd-seatfig p { margin: 0; text-align: center; font-size: var(--gs-text-sm); color: var(--gs-ink-soft); }
.sd-seatprompt { display: grid; place-items: center; font-size: var(--gs-text-sm); font-weight: 700; padding: 0 var(--sd-edge); }
.sd-seatprompt[data-ready='false'] { animation: sd-pulse 1.4s infinite; }
.sd-pad[data-seat='pending'] { animation: sd-pulse 1.2s infinite; }
.sd-pad[data-seat='ready']::after { content: '✓'; position: absolute; font-size: 20px; font-weight: 900; }

/* 结算页 */
.sd-scoreline { display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: var(--gs-space-3); }
.sd-scoreline__num { font-size: clamp(34px, calc(var(--sd-avail-h) * 0.07), 64px); font-weight: 900; line-height: 1; font-variant-numeric: tabular-nums; }
.sd-scoreline__num[data-side='p1'] { color: var(--sd-warm); text-align: left; }
.sd-scoreline__num[data-side='p2'] { color: var(--sd-cool); text-align: right; }
.sd-scoreline__num[data-winner='true'] { text-shadow: 0 0 26px currentColor; }
.sd-scoreline__label { font-size: var(--gs-text-sm); color: var(--gs-ink-soft); white-space: nowrap; }

.sd-attrib { display: grid; gap: var(--gs-space-2); }
.sd-attrib__row {
  display: grid; grid-template-columns: 28px 1fr auto; align-items: center; gap: var(--gs-space-2);
  padding: var(--gs-space-2) var(--gs-space-3);
  border-radius: 12px; border: 1px solid var(--sd-rule); background: var(--sd-panel);
}
.sd-attrib__row img { width: 26px; height: 26px; }
.sd-attrib__title { font-size: var(--gs-text-sm); font-weight: 700; }
.sd-attrib__sub { font-size: var(--gs-text-xs); color: var(--gs-ink-dim); }
.sd-attrib__value { font-size: var(--gs-text-lg); font-weight: 900; }

/* 逐局比分表，**含一条高亮的合计行**——赢了局却输在总分的玩家要看得懂（J12 第 3 条） */
.sd-roundtable { width: 100%; border-collapse: collapse; }
.sd-roundtable th, .sd-roundtable td { padding: 6px 8px; font-size: var(--gs-text-sm); }
.sd-roundtable td:first-child { color: var(--gs-ink-soft); text-align: left; }
.sd-roundtable .sd-p1 { color: var(--sd-warm); font-weight: 800; text-align: right; }
.sd-roundtable .sd-p2 { color: var(--sd-cool); font-weight: 800; text-align: left; }
.sd-roundtable tr { border-bottom: 1px solid var(--sd-rule); }
.sd-roundtable tr[data-total='true'] {
  background: rgba(255, 210, 87, 0.12);
  box-shadow: inset 0 0 0 1px var(--gs-gold);
}

/* 战绩册 2×4 */
.sd-recordgrid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--gs-space-2); }
.sd-record {
  display: grid; grid-template-columns: 44px 1fr; align-items: center; gap: var(--gs-space-2);
  padding: var(--gs-space-2); border-radius: 12px; border: 1px solid var(--sd-rule); background: var(--sd-panel);
}
.sd-record img { width: 40px; height: 40px; }
.sd-record__label { font-size: var(--gs-text-xs); color: var(--gs-ink-soft); }
.sd-record__value { font-size: var(--gs-text-xl); font-weight: 900; line-height: 1; }
.sd-record[data-empty='true'] { filter: brightness(0.72); }

/* 每日星图：36 枚离散代币串成的环（不是甜甜圈，环心是空的） */
.sd-chartring { position: relative; width: min(84vw, 280px); aspect-ratio: 1; margin: 0 auto; }
.sd-chartring__bead {
  position: absolute; border-radius: 999px; transform: translate(-50%, -50%);
  box-shadow: 0 0 10px currentColor;
}
.sd-chartring__bead[data-tier='s1'] { width: 10px; height: 10px; color: var(--sd-s1); background: var(--sd-s1); }
.sd-chartring__bead[data-tier='s3'] { width: 14px; height: 14px; color: var(--sd-s3); background: var(--sd-s3); }
.sd-chartring__bead[data-tier='s7'] { width: 20px; height: 20px; color: var(--sd-s7); background: var(--sd-s7); }

/* 分享卡预览：画的是**合成之后的成品**，不是底图 */
.sd-sharecard { width: 100%; border-radius: 14px; border: 1px solid var(--sd-rule); display: block; }

/* 教学：步进轨 + 意图箭头 */
.sd-steptrack { display: flex; gap: 6px; align-items: center; }
.sd-steptrack__dot { width: 10px; height: 10px; border-radius: 999px; background: rgba(255,255,255,0.22); }
.sd-steptrack__dot[data-on='true'] { background: var(--gs-accent); box-shadow: 0 0 10px var(--gs-accent); }
.sd-intent {
  position: absolute; width: 26px; pointer-events: none;
  filter: drop-shadow(0 0 8px var(--sd-cool));
  animation: sd-bob 900ms ease-in-out infinite;
}
@keyframes sd-bob { 50% { transform: translateY(-6px); } }

/* 视口过小：整屏浮层，没有按钮——唯一的出路是玩家去改屏幕 */
.sd-toosmall {
  position: absolute; inset: 0; z-index: 40;
  display: grid; align-content: center; justify-items: center; gap: var(--gs-space-4);
  padding: var(--gs-space-5); text-align: center;
  background: rgba(4, 7, 22, 0.96);
}
.sd-toosmall img { width: min(60%, 220px); }

/* S3 review: explicit safe-area zones, copied from SCREEN_SPEC by layout-zones.mjs. */
#app { box-sizing: border-box; padding: env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left); }
.sd-safe, .sd-stage { position: absolute; inset: env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left); padding: 0; }
.sd-zone { position: absolute; box-sizing: border-box; min-width: 0; min-height: 0; padding: 0 12px; }
.sd-zone p { margin: 0; line-height: 1.35; }
.sd-header { display: flex; flex-direction: column; justify-content: center; gap: 4px; }
.sd-topbar { padding: 0; min-height: 44px; gap: 8px; }
.sd-heading { font-size: clamp(19px, calc(var(--sd-avail-w)*.06), 32px); line-height: 1.1; margin: 0; text-align: center; }
.sd-header-mark { width: 44px; height: 44px; object-fit: contain; }
.sd-footer { display: flex; align-items: center; justify-content: center; text-align: center; font-size: 12px; color: var(--gs-ink-soft); }
.sd-play-content { display: flex; flex-direction: column; gap: 12px; }
.sd-content { display: flex; flex-direction: column; gap: 12px; flex: 1; min-height: 0; overflow: auto; padding: 2px; }
.sd-actions { display: flex; flex-wrap: wrap; gap: 8px; flex: 0 0 auto; }
.sd-actions > * { flex: 1 1 44px; min-width: 44px; min-height: 44px; box-sizing: border-box; }
.sd-actions:empty { display: none; }
/* 暂停屏有四颗按钮。挤在一排时每颗只剩 ~70px，而按钮自带 40px 的左右内边距——
   文字当场被裁掉（走查：分区「play」的内容比它宽 36px）。主按钮独占一行，其余的两个一行。 */
.sd-screen[data-screen='pause'] .sd-actions > * { flex: 1 1 calc(50% - 4px); }
.sd-screen[data-screen='pause'] .sd-actions > .gs-btn--wide { flex-basis: 100%; }
.sd-screen[data-modal='true'] { z-index: 30; background: rgba(3,6,20,.58); backdrop-filter: blur(2px); }
.sd-screen[data-modal='true'] .sd-safe::before { content: ''; position: absolute; inset: 9% 5% 7%; border-radius: 24px; background: linear-gradient(140deg,#162d50,#060c21); border: 2px solid #6aabed; box-shadow: inset 0 0 20px #3676af88,0 0 30px #020613; }
.sd-screen[data-underlay='true'] { pointer-events: none; }
.sd-bg__scrim { background: linear-gradient(180deg,#04071622,#04071655); }
.sd-loading-content { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 14px; text-align: center; }
.sd-loading-content > * { width: 100%; }
.sd-loading-content .sd-bootmark { width: 62%; max-width: 300px; }
.sd-display-title { font-size: clamp(40px,calc(var(--sd-avail-w)*.125),68px); font-style: italic; line-height: 1; margin: 0; color: #e9faff; text-shadow: -2px 2px #eb981f,2px -2px #23cfff,0 0 20px #1ccfff; }
.sd-modegrid { gap: 12px; flex: 1; }
.sd-modecard { min-height: 156px; justify-items: center; text-align: center; background: radial-gradient(ellipse at top,#20508066,#071127cc); padding: 10px; box-shadow: inset 0 0 18px #36aaff55,0 0 8px #43aaff55; }
.sd-modecard__art { width: 80px; height: 80px; object-fit: contain; }
.sd-modecard__name { font-size: 20px; }
.sd-modecard__blurb { font-size: 12px; }
.sd-section { border: 1px solid #359adc; border-radius: 16px; background: linear-gradient(140deg,#192844dd,#03091ddd); padding: 10px; margin: 0; gap: 8px; }
.sd-section[data-kind='opponent'] .sd-picker { display: grid; grid-template-columns: repeat(5,minmax(44px,1fr)); }
.sd-section[data-kind='skin'] .sd-picker { display: grid; grid-template-columns: repeat(4,minmax(44px,1fr)); }
.sd-section[data-kind='field'] .sd-picker { display: grid; grid-template-columns: repeat(3,minmax(44px,1fr)); }
.sd-choice { padding: 6px 2px; min-width: 44px; min-height: 84px; }
.sd-choice img { width: 100%; height: 52px; object-fit: contain; }
.sd-choice__name, .sd-choice__blurb { overflow-wrap: anywhere; }
.sd-choice[data-locked='true']::after { content: '🔒'; position: absolute; right: 1px; top: 1px; font-size: 12px; }
.sd-stage[data-tier='standard'] .sd-pads { align-content: center; grid-auto-rows: var(--sd-visual-height); }
.sd-pad { background: var(--sd-pad-idle) center/100% 100% no-repeat; box-shadow: 0 0 12px currentColor; }
.sd-pad[data-held='true'] { background: var(--sd-pad-press) center/100% 100% no-repeat; }
.sd-pad__glow { border-radius: 14px; }
.sd-seat-avatar { width: 40px; height: 40px; object-fit: contain; }
.sd-seatfig img { width: 100%; max-height: 160px; }
.sd-stage .sd-bar[data-tutorial='true'] { display: flex; gap: 8px; }
.sd-bar[data-tutorial='true'] > :nth-child(2), .sd-bar[data-tutorial='true'] > :nth-child(4) { display: none; }
.sd-bar[data-tutorial='true'] > :nth-child(3) { flex: 1; min-width: 0; }
.sd-bar[data-tutorial='true'] > :first-child { max-width: 95px; font-size: 10px; white-space: normal; }
.sd-bar[data-tutorial='true'] > button { flex: 0 0 auto; min-width: 44px; min-height: 44px; padding: 4px 8px; }
.sd-result-score, .sd-result-detail, .sd-result-actions { display: flex; flex-direction: column; justify-content: center; gap: 10px; overflow: auto; }
.sd-result-score .sd-scoreline { flex-shrink: 0; }
/* 比赛结算的头条块：大号总分 + 局点圆点 + 一行「Rounds won」小字 */
.sd-result-headline { display: flex; flex-direction: column; gap: 6px; flex-shrink: 0; }
.sd-result-headline__label { text-align: center; margin: 0; }
.sd-result-score .sd-scoreline__num { font-size: clamp(42px,calc(var(--sd-avail-w)*.13),70px); }
.sd-result-score > :first-child { align-self: center; }
.sd-result-detail .sd-attrib__row { padding: 10px; min-height: 62px; }
.sd-result-detail .sd-attrib__row img { width: 38px; height: 38px; }
.sd-result-detail .sd-attrib__row { grid-template-columns: 40px 1fr auto; }
.sd-round-actions { align-items: center; }
.sd-round-actions .sd-actions { width: 100%; }
.sd-recordgrid { flex: 1; grid-template-rows: repeat(4,minmax(86px,1fr)); }
.sd-record { grid-template-columns: 48px 1fr; padding: 8px; background: linear-gradient(130deg,#132242dc,#10295699); border-color: #4088bf; }
.sd-record img { width: 48px; height: 56px; object-fit: contain; }
.sd-record__value { font-size: clamp(26px,calc(var(--sd-avail-w)*.08),44px); color: #c4f6ff; text-shadow: 0 0 10px #21bfff; }
.sd-date { text-align: center; font-size: 16px; }
.sd-chartring { width: min(100%,330px); flex-shrink: 0; }
.sd-chartring__bead { display: grid; place-items: center; background: #15223c !important; border: 1px solid currentColor; }
.sd-chartring__bead img { width: 145%; height: 145%; object-fit: contain; }
.sd-chartring__bead[data-tier='s1'] { width: 14px; height: 14px; }
.sd-chartring__bead[data-tier='s3'] { width: 18px; height: 18px; }
.sd-chartring__bead[data-tier='s7'] { width: 22px; height: 22px; }
.sd-screen[data-screen='unlock'] .sd-content { align-items: center; text-align: center; }
.sd-screen[data-screen='unlock'] .sd-modecard__art { width: 130px; height: 130px; }
.sd-screen[data-screen='pause'] .sd-attrib__row { grid-template-columns: 24px 1fr auto; padding: 4px; }
.sd-screen[data-screen='pause'] .sd-attrib__row:has([role='group']) { grid-template-columns: 24px 1fr; }
.sd-screen[data-screen='pause'] .sd-attrib__row > [role='group'] { grid-column: 1/-1; }
.sd-help-tabs { display: flex; gap: 8px; overflow-x: auto; flex-shrink: 0; }
.sd-help-tabs > button { min-height: 44px; min-width: 44px; padding: 4px 6px; flex: 1; font-size: 12px; }

/* 矮画布（站点框在 375×667 的手机上交给游戏的是 342×403）：帮助屏的头部分区只有
   0.12×H ≈ 48px，而「返回栏 + 标签行」叠起来是 88——分区居中裁两头，返回键被顶到画布上沿之外，
   玩家退不出这一屏。矮的时候改成一行两栏：返回栏在左、标签行在右（标签行自己可横向滚）。
   条件用高度不用宽度：撑不下的是高度，而按宽度分档就是在给桌面另写一套版式。 */
@media (orientation: portrait) and (max-height: 560px) {
  .sd-safe .sd-header:has(.sd-help-tabs) { flex-direction: row; align-items: center; gap: 8px; }
  .sd-safe .sd-header:has(.sd-help-tabs) .sd-topbar { flex: 1 1 62%; min-width: 0; }
  /* 标签行在这一档**横向滚**，不再把五个标签压成十几像素宽——压窄了文字会折成三行，
     标签行变成 52 高，头部（48）又装不下。滚动条自己占 8px 高，一并藏掉：这是触控面，滑动照旧。 */
  .sd-safe .sd-header:has(.sd-help-tabs) .sd-help-tabs { flex: 1 1 38%; min-width: 0; scrollbar-width: none; }
  .sd-safe .sd-header:has(.sd-help-tabs) .sd-help-tabs::-webkit-scrollbar { display: none; }
  .sd-safe .sd-header:has(.sd-help-tabs) .sd-help-tabs > button { flex: 0 0 auto; white-space: nowrap; }
  /* 标题收成一行：折到第二行的话头部就是 68 高，又把返回键顶出画布上沿。 */
  .sd-safe .sd-header:has(.sd-help-tabs) .sd-heading { font-size: var(--gs-text-xs); white-space: nowrap; }
}
.sd-notch-row { display: grid; grid-template-columns: repeat(3,minmax(0,1fr)); gap: 8px; padding: 10px 0; }
.sd-notch { display: flex; flex-direction: column; align-items: center; text-align: center; gap: 8px; min-width: 0; }
.sd-notch-picture { width: 82px; height: 82px; position: relative; }
.sd-notch-picture > img { position: absolute; inset: 21px; width: 40px; height: 40px; }
.sd-notch-picture > i { position: absolute; inset: 0; }
.sd-notch-picture > i::before { content: ''; display: block; margin: -2px auto 0; width: 4px; height: 13px; background: #ffd257; box-shadow: 0 0 6px #fff; }
.sd-demo-card { border: 1px solid #3785bd; border-radius: 16px; padding: 10px; background: #09152add; display: grid; gap: 8px; }
.sd-demo-canvas { width: 100%; height: 190px; border-radius: 12px; background: radial-gradient(ellipse,#233d6855,#030716); }
.sd-demo-select { color: white; background: #13264b; border-radius: 10px; }
.sd-rotate-diagram { position: relative; height: 100%; width: 180px; display: flex; align-items: center; justify-content: center; }
.sd-rotate-diagram img { height: 100%; width: 90px; object-fit: contain; }
.sd-rotate-arrow { font-size: 44px; color: #68e1ff; }
.sd-window-outline { width: 40px; height: 32px; border: 2px dashed #ffc759; }
.sd-field-diagram { display: flex; gap: 12px; width: 100%; }
.sd-diagram-frame { display: grid; grid-template-columns: repeat(3,1fr); flex: 1; border: 1px solid #309ccc; border-radius: 8px; padding: 4px; }
.sd-diagram-frame span { min-width: 0; height: 36px; border-right: 1px solid #296181; }
.sd-diagram-frame img { width: 100%; height: 100%; object-fit: contain; }
.sd-field-diagram[data-field='mirror'] .sd-diagram-frame:nth-child(2) { animation: sd-mirror 3s ease-in-out infinite; }
@keyframes sd-mirror { 0%,100% { transform: rotateY(0); } 50% { transform: rotateY(180deg); } }
:root[data-reduced-motion='on'] .sd-safe *, :root[data-reduced-motion='on'] .sd-stage * { animation: none !important; transition: none !important; }
.sd-content summary { min-height: 44px; display: flex; align-items: center; cursor: pointer; }

/* ══════════════ 手机横屏（手机的一个朝向，不是桌面） ══════════════
   560 取自 shared/phone-canvas.json 的 shortViewportMaxHeightPx，不另写一个数。
   五列改五行：P2 抢格在左竖排、P1 在右竖排、星场在中间，共享条压在星场顶部。 */
@media (orientation: landscape) and (max-height: 560px) {
  :root {
    --sd-edge: 12px;
    /* 五列改五行：排列轴从 x 变成 y，所以行高由**高度**分配、列宽由宽度分配。
       上下边距 12、行间隙 8 → padH_L = clamp(44, (H − 2×12 − 4×8) / 5, 96)。 */
    --sd-pad-h: clamp(44px, (var(--sd-avail-h) - 56px) / 5, 96px);
    --sd-pad-w: clamp(64px, calc(var(--sd-avail-w) * 0.14), 110px);
    --sd-rail: 40px;
  }
  /* 紧凑横屏（360 ≤ W < 480）：两侧竖轨与边距收一档，抢格列跟着收 */
  .sd-stage[data-tier='l-compact'] {
    --sd-edge: 8px;
    --sd-rail: 32px;
    --sd-pad-w: clamp(44px, calc(var(--sd-avail-w) * 0.12), 80px);
  }

  /* 共享条在中间那一列**自己占一行**，不是压在星场上面。
     两块分区必须互斥——压着画的话 `playtest` 量出来就是「分区互相压住」，
     而那正是上一版底部提示条盖住棋盘最后一行的同一个病。
     星场高 = H − 上下边距 24 − 共享条 48 − 两者之间的 8（§10.1）。 */
  .sd-stage {
    grid-template-columns: var(--sd-rail) var(--sd-pad-w) minmax(160px, 1fr) var(--sd-pad-w) var(--sd-rail);
    grid-template-rows: var(--sd-shared) 1fr;
    grid-template-areas:
      'hud2 pads2 shared pads1 hud1'
      'hud2 pads2 field  pads1 hud1';
    column-gap: var(--gs-tap-gap);
    padding-block: var(--gs-space-3);
  }
  /* **横屏这六块不写 `grid-area`。** 它们由 `measureLayout()` 按冻结的 `SCREEN_SPEC.zones`
     绝对定位；而一个**显式放进某个 grid area** 的绝对定位子元素，包含块会变成那一格
     而不是整块舞台——于是 `left: 79.1%` 变成"抢格列这 110px 里的 79.1%"，
     十个抢格整排被挤到画布右缘之外，玩家按哪都按不到。竖屏那几块没有显式放置，
     包含块本来就是舞台，所以只有横屏踩到这一条。
     上面的 `grid-template-*` 留着是这六块**还没被量到之前**的兜底排布。 */
  .sd-stage > [data-zone='hud-p2']     { margin: 0; }
  .sd-stage > [data-zone='pads-p2']    { margin: 0; }
  .sd-stage > [data-zone='shared-bar'] { margin: 0 0 var(--sd-gap-field) 0; }
  .sd-stage > [data-zone='star-field'] { margin: 0; }
  .sd-stage > [data-zone='pads-p1']    { margin: 0; }
  .sd-stage > [data-zone='hud-p1']     { margin: 0; }

  /* 五列变五行：抢格列竖排，排列轴从 x 变成 y → 命中区外扩跟着换轴 */
  .sd-pads {
    grid-template-columns: 1fr;
    grid-auto-rows: var(--sd-pad-h);
    align-content: center;
    padding: 0;
  }
  .sd-pad::before { inset: -4px -6px; }   /* 横屏：排列轴是 y → 上下 4，跨轴左右 6 */

  /* 两侧 HUD 变成竖轨 */
  .sd-hud {
    grid-template-columns: none;
    grid-auto-rows: auto;
    justify-items: center;
    align-content: center;
    gap: 6px;
    padding: 0;
  }
  .sd-hud__score { font-size: clamp(18px, calc(var(--sd-avail-h) * 0.09), 34px); }
  .sd-hud__mid { width: 100%; }
  .sd-points { flex-direction: column; }
  .sd-scorebar { width: 6px; height: 56px; margin: 0 auto; }
  .sd-scorebar__fill { left: 0; right: 0; top: 50%; bottom: auto; width: auto; height: var(--sd-fill, 0%); transform: translateY(-50%); }

  /* 菜单屏：竖排分区改横排，不重画一套界面 */
  .sd-modegrid { grid-template-columns: repeat(4, 1fr); }
  .sd-scoreline { grid-template-columns: 1fr auto 1fr; }
  .sd-recordgrid { grid-template-columns: repeat(4, 1fr); }
  .sd-chartring { width: min(38vw, 210px); }
  .sd-safe .sd-header:has(.sd-help-tabs) { flex-direction: row; align-items: center; }
  .sd-safe .sd-header:has(.sd-help-tabs) .sd-topbar { flex: 0 0 34%; }
  .sd-safe .sd-header:has(.sd-help-tabs) .sd-help-tabs { min-width: 0; flex: 1; }
  .sd-stage { inset: env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left); padding: 0; }
  .sd-stage .sd-pads { padding: 4px 6px; grid-auto-rows: var(--sd-visual-height); grid-template-columns: minmax(44px,1fr); }
  .sd-stage .sd-bar { padding: 0 4px; gap: 8px; }
  .sd-stage .sd-steptrack { flex-direction: column; gap: 4px; }
  .sd-stage .sd-hud { gap: 4px; }
  .sd-stage .sd-bar__label { white-space: normal; font-size: 10px; }
  .sd-safe .sd-modegrid { grid-template-columns: repeat(4,minmax(0,1fr)); }
  .sd-safe .sd-modecard { min-height: 150px; }
  .sd-safe .sd-recordgrid { grid-template-columns: repeat(4,minmax(0,1fr)); grid-template-rows: repeat(2,minmax(86px,1fr)); }
  .sd-safe .sd-chartring { width: 190px; }
  .sd-safe .sd-result-score, .sd-safe .sd-result-detail { gap: 4px; }
  .sd-safe .sd-result-score .sd-scoreline__num { font-size: 36px; }
  .sd-safe .sd-result-detail .sd-attrib__row { padding: 4px; min-height: 40px; }
  .sd-safe .sd-result-actions .sd-actions { flex-direction: column; }
  .sd-safe .sd-result-score .sd-roundtable th, .sd-safe .sd-result-score .sd-roundtable td { padding: 2px 4px; font-size: 12px; }
  .sd-screen[data-modal='true'] .sd-safe::before { inset: 4% 11%; }
  .sd-screen[data-screen='pause'] .sd-actions { flex-wrap: nowrap; }
  .sd-screen[data-screen='pause'] .sd-actions > * { font-size: 12px; padding: 4px; }
  .sd-safe .sd-loading-content { gap: 6px; }
  .sd-safe .sd-loading-content .sd-bootmark { width: 64px; }
  .sd-safe .sd-display-title { font-size: 32px; }
}
