/*
 * bounce-defense · 皮肤与本作专有版式。
 *
 * 两条纪律：
 *
 * 1. **只覆盖设计令牌变量，不覆盖 `.gs-*` 选择器。** 覆盖组件的选择器会把
 *    44×44 触控下限、安全区、焦点样式这些可用性保证一起改坏——那些是组件在守的，
 *    不是调用方该重写的。本作自己的类一律用 `bd-` 前缀。
 *
 * 2. **不写「宽屏 / 桌面」版式。** 视口比手机宽的时候，共用组件库
 *    （`shared/game-shell/ui/ui.css` 末尾「手机画布锁定」）会把整个 `#app`
 *    收成一块 540×960 的手机画布居中摆着、两侧留黑边，桌面玩家玩到的就是
 *    手机上验收过的那一屏。所以这里**不许出现不带 `max-height` 限定的
 *    `@media (min-width: …)`**——那是在给桌面另写一套没人验收的版式。
 *    手机横屏仍然要做，条件写成 `(orientation: landscape) and (max-height: 560px)`，
 *    560 这个数来自 `shared/phone-canvas.json`，是唯一真值。
 */

:root {
  /* 霓虹玩具街机：近黑深空底 + 青色主调 + 紫色次调 + 金色专属给锐度。
     金色是**有主的**：它只属于球的锐度环、满锐度光环、核心兵的内核与结算大数字。
     HUD 的数字一律走青色——三处都金，玩家就分不出「格数对上了」这件事。 */
  --gs-accent: #3FD8FF;
  --gs-accent-2: #A46BFF;
  --gs-gold: #FFD66B;
  --gs-cta: linear-gradient(135deg, #6E4BFF 0%, #2FBBEE 100%);
  --gs-danger: #FF6B6B;

  --gs-surface: rgba(12, 18, 40, 0.82);
  --gs-surface-raised: rgba(20, 28, 56, 0.92);
  --gs-surface-sunken: rgba(6, 10, 24, 0.9);
  --gs-hairline: rgba(63, 216, 255, 0.28);

  --gs-ink: #EAF3FF;
  --gs-ink-soft: #A9BBD8;
  --gs-ink-dim: #6C7E9E;

  --gs-glow-accent: 0 0 24px rgba(63, 216, 255, 0.38);
  --gs-glow-gold: 0 0 26px rgba(255, 214, 107, 0.42);
  --gs-shadow-card: 0 18px 42px rgba(2, 6, 18, 0.55);

  --gs-radius-lg: 22px;
  --gs-radius-md: 16px;
  --gs-radius-sm: 10px;

  /* 本作专用（不属于组件库的令牌，一律 bd- 前缀）。 */
  --bd-void: #060A18;
  --bd-grid: rgba(63, 216, 255, 0.12);
  --bd-panel-edge: rgba(164, 107, 255, 0.35);
}

html, body {
  margin: 0;
  padding: 0;
  height: 100%;
  background: var(--bd-void);
  color: var(--gs-ink);
  font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  overscroll-behavior: none;
  -webkit-tap-highlight-color: transparent;
  /* 兜底：哪天入口的 viewport 被改坏，Safari 也不会自作主张放大正文——
     HUD 的数字是按 px 定死的，被系统放大一次分区就撑破了。 */
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

/* 画布锁定套在 `#app` 上（`shared/game-shell/ui/ui.css` 末尾），
   但**把它钉成定位元素是每款游戏 theme.css 的活**——不钉，下面这些
   `position: absolute` 的层就会去找视口当包含块，桌面上那圈黑边里也会铺满东西。 */
#app {
  position: fixed;
  inset: 0;
  overflow: hidden;
}

#bd-safe {
  position: absolute;
  inset: 0;
}

/* 刘海与底部横条**在这里一次性扣掉**。SCREEN_SPEC 的 zones 是「扣完之后」的比例，
   所以分区百分比不需要再各自算一遍安全区。 */
.bd-layer {
  position: absolute;
  inset: env(safe-area-inset-top) env(safe-area-inset-right)
         env(safe-area-inset-bottom) env(safe-area-inset-left);
}
/* 空的层**不许吃掉指针**。四层是叠在一起的，浮层层永远盖在场景层上面；
   不加这一条，一个什么都没有的透明层会把标题页的每一颗按钮都挡住——
   而屏幕上看起来一切正常，玩家的理解只会是「这游戏坏了」。 */
.bd-layer--bg { z-index: 0; pointer-events: none; }
.bd-layer--scene { z-index: 1; }
.bd-layer--inline { z-index: 2; pointer-events: none; }
.bd-layer--overlay { z-index: 3; pointer-events: none; }
.bd-layer--inline > *,
.bd-layer--overlay > * { pointer-events: auto; }

.bd-screen, .bd-panel, .bd-overlay { position: absolute; inset: 0; }
.bd-overlay {
  background: radial-gradient(120% 90% at 50% 30%, rgba(10, 16, 38, 0.72), rgba(4, 7, 18, 0.92));
  backdrop-filter: blur(6px);
}

/* 分区：位置由 ui.mjs 从 game.config.json.zones 逐屏写在 style 上，
   这里只给它们内部的排布。**分区几何一个数都不在 CSS 里。** */
.bd-zone {
  position: absolute;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  gap: var(--gs-space-2);
  min-width: 0;
  min-height: 0;
}

/* ───────────────────────── 文字层级 ───────────────────────── */

.bd-display-xl { font-size: var(--gs-text-hero); font-weight: 800; margin: 0; letter-spacing: -0.02em;
  background: linear-gradient(180deg, #FFFFFF 0%, var(--gs-accent) 100%);
  -webkit-background-clip: text; background-clip: text; color: transparent; }
.bd-display-lg { font-size: var(--gs-text-xl); font-weight: 800; margin: 0; }
.bd-display-md { font-size: var(--gs-text-lg); font-weight: 700; margin: 0; }
.bd-display-sm { font-size: var(--gs-text-md); font-weight: 700; margin: 0; }
.bd-label-sm { font-size: var(--gs-text-xs); font-weight: 700; letter-spacing: 0.09em;
  text-transform: uppercase; color: var(--gs-accent-2); margin: 0; }
.bd-body-sm { font-size: var(--gs-text-sm); line-height: 1.45; color: var(--gs-ink-soft); margin: 0; }
.bd-caption { font-size: var(--gs-text-xs); color: var(--gs-ink-dim); margin: 0; }
.bd-numeric-lg { font-size: var(--gs-text-lg); font-weight: 800; font-variant-numeric: tabular-nums;
  color: var(--gs-accent); }
.bd-numeric-md { font-size: var(--gs-text-md); font-weight: 700; font-variant-numeric: tabular-nums;
  color: var(--gs-ink); }
.bd-mistake { color: var(--gs-ink); font-weight: 600;
  border-left: 3px solid var(--gs-gold); padding-left: var(--gs-space-2); }

.bd-row { display: flex; gap: var(--gs-space-2); align-items: stretch; }
.bd-row--even > * { flex: 1 1 0; }
.bd-row--icons { justify-content: center; gap: var(--gs-space-3); }
.bd-row--stats > * { flex: 1 1 0; }
.bd-logo { width: clamp(48px, 14vw, 84px); height: auto; align-self: center; }

/* ───────────────────────── 加载 / 标题 ───────────────────────── */

.bd-hero-art { display: flex; align-items: center; justify-content: center; height: 100%; }
.bd-hero-canvas { width: 100%; height: 100%; object-fit: contain; }
.bd-title-stage { pointer-events: none; }

/* ───────────────────────── 竞技场 ───────────────────────── */

.bd-arena-zone {
  align-items: center;
  justify-content: center;
  position: absolute;
  /* `clip` 与 `hidden` 一样不给滚动，但它认 `overflow-clip-margin`：
     判定场下沿那条 1px 的命中唇（见 `.bd-arena-field::after`）要伸出分区
     才能把分界线那一行收回竞技场，而 `hidden` 会把它连同命中区一起剪掉——
     320×568 上判定场正好铺满分区，那一档全靠这 2px 余量。 */
  overflow: clip;
  overflow-clip-margin: 2px;
}
/* 画布**铺满整块 `play` 分区**：SCREEN_SPEC 的 `arena-drop-pin` 写的是
   `visible_relation: "same"`、热区与视觉区都是整块分区、没有任何不可点的死区。
   判定场恒为 1080:1560（§2.3 冻结），它在画布里各向同性缩放并居中，
   场外那一圈由渲染层画成墙体——**不是把图形拉宽**。
   像素尺寸仍由 `ui.mjs::fitArena()` 写在 style 上，因为输入映射读的是同一份 `view`；
   这里的 100% 只是 fitArena 跑起来之前的默认值，**不写 aspect-ratio**。 */
.bd-arena {
  touch-action: none;
  display: block;
  flex: 0 0 auto;
  width: 100%;
  height: 100%;
}

/* 判定场（§2.3 的 1080×1560）在画布里的那块矩形。它**不是装饰**：
   逻辑坐标 ↔ 屏幕靠它换算，指针捕获挂在它身上，玩家看见的场边也是它。
   位置由 `ui.mjs::placeFieldMarker()` 从渲染同一份 `view` 写进 style。
   仍然可命中（不写 pointer-events: none）——场内按下要真的命中「竞技场」；
   场外的墙体由画布接住，两处都走分区上那一个 pointerdown 监听。 */
.bd-arena-field {
  --bd-field-border: 2px;
  position: absolute;
  box-sizing: border-box;
  touch-action: none;
  border: var(--bd-field-border) solid rgba(63, 216, 255, 0.55);
  border-radius: var(--gs-radius-sm);
  background: transparent;
  /* 下面那条下唇要压在 HUD 带的第一行上，而分区之间按 DOM 顺序叠、HUD 在后面。 */
  z-index: 2;
}
/* §6.1 `boundary-press`：**分界线（逻辑 y=1560）归竞技场一侧**。
   CSS 的命中区是半开区间 [top, bottom)，所以判定场自己的下沿那一行**不属于它**：
   320×568 上判定场正好铺满分区，那一行于是归了 HUD 带——真手指按在场地最下缘，
   落钉不发生、屏幕上什么都没有，而玩家的理解是「这游戏坏了」。
   补一条 1px 的透明下唇把那一行收回竞技场。`::after` 不进
   `getBoundingClientRect()`，所以逻辑坐标 ↔ 屏幕的换算基准一个像素都没变。 */
.bd-arena-field::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  /* 绝对定位的偏移量是相对**内边距盒**算的，而判定场有一圈边框：
     只写 -1px 的话这条唇整个落在边框里面，一个像素都没伸出去。 */
  bottom: calc(-1px - var(--bd-field-border));
  height: calc(1px + var(--bd-field-border));
}

/* 存档提示：**不阻塞游玩**的一条常驻说明，贴在画布顶部安全区之下。
   它不吃指针，所以底下的竞技场照常可点（内嵌层本身已 pointer-events: none）。 */
.bd-save-notice {
  position: absolute;
  left: var(--gs-space-3);
  right: var(--gs-space-3);
  top: var(--gs-space-2);
  margin: 0;
  padding: 6px 10px;
  border-radius: var(--gs-radius-sm);
  background: var(--gs-surface-sunken);
  border: 1px solid var(--gs-hairline);
  color: var(--gs-ink-soft);
  text-align: center;
}

.bd-hint-band {
  position: absolute;
  pointer-events: none;
  border-top: 2px solid var(--gs-gold);
  background: linear-gradient(180deg, rgba(255, 214, 107, 0.16), rgba(255, 214, 107, 0));
}
.bd-hint-guide {
  position: absolute;
  height: 0;
  transform-origin: 0 0;
  pointer-events: none;
  border-top: 3px dashed var(--gs-gold);
  opacity: 0.9;
}
.bd-script-chips {
  position: absolute; top: var(--gs-space-2); left: var(--gs-space-2);
  display: flex; gap: var(--gs-space-1); pointer-events: none;
}
.bd-chip {
  font-size: var(--gs-text-xs); font-weight: 700; letter-spacing: 0.06em;
  padding: 4px 10px; border-radius: var(--gs-radius-pill);
  border: 1px solid var(--gs-hairline); background: var(--gs-surface-sunken); color: var(--gs-ink);
}
.bd-chip--script { color: var(--gs-gold); border-color: rgba(255, 214, 107, 0.45); }
.bd-chip--surge { color: var(--gs-danger); border-color: rgba(255, 107, 107, 0.5); }
.bd-reject-hint {
  position: absolute; left: 0; right: 0; bottom: var(--gs-space-3);
  text-align: center; color: var(--gs-danger); pointer-events: none;
}
.bd-coach-host { position: absolute; inset: 0; pointer-events: none; }
.bd-coach-host > * { pointer-events: auto; }

/* ───────────────────────── HUD ───────────────────────── */

.bd-hud {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--gs-space-2); height: 100%; padding: 0 var(--gs-space-3);
  box-sizing: border-box;
  border-top: 1px solid var(--gs-hairline);
  background: linear-gradient(180deg, rgba(8, 12, 28, 0.72), rgba(8, 12, 28, 0.95));
}
.bd-readout { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.bd-hud__end { display: flex; align-items: center; gap: var(--gs-space-2); margin-left: auto; }

/* 弹钉槽：**顺时针填充、往上涨**——它是在攒不是在流失。空槽的虚线轮廓必须看得见，
   三个槽全空时画成一片空白，玩家会以为这个游戏只有两个槽。 */
.bd-slots { display: flex; gap: 6px; align-items: center; }
.bd-slot { width: 22px; height: 22px; display: block; }
.bd-slots--reject { animation: bd-reject 0.32s steps(4) 1; }
@keyframes bd-reject {
  0%, 100% { filter: none; }
  50% { filter: drop-shadow(0 0 8px var(--gs-danger)) saturate(2); }
}

/* ───────────────────────── 浮层骨架 ───────────────────────── */

.bd-panel { position: absolute; inset: 0; }
.bd-panel__header { flex-direction: row; align-items: center; gap: var(--gs-space-2); }
.bd-panel__body, .bd-panel__side, .bd-panel__demo {
  overflow-y: auto; overscroll-behavior: contain;
  border-radius: var(--gs-radius-lg);
  border: 1px solid var(--bd-panel-edge);
  background: var(--gs-surface);
  box-shadow: var(--gs-shadow-card), var(--gs-glow-accent);
  padding: var(--gs-space-3);
  box-sizing: border-box;
}
.bd-panel__foot { justify-content: flex-end; gap: var(--gs-space-2); }
.bd-demo { width: 100%; height: 100%; display: block; border-radius: var(--gs-radius-md); }

/* ───────────────────────── 装载 ───────────────────────── */

.bd-chip-row { display: flex; gap: var(--gs-space-2); flex-wrap: wrap; }
.bd-chip-btn {
  flex: 1 1 30%; min-height: var(--gs-tap-min); min-width: var(--gs-tap-min);
  display: flex; flex-direction: column; gap: 2px; align-items: flex-start;
  padding: var(--gs-space-2); border-radius: var(--gs-radius-md);
  border: 1px solid var(--gs-hairline); background: var(--gs-surface-sunken);
  color: var(--gs-ink); text-align: left; cursor: pointer;
}
.bd-chip-btn--on { border-color: var(--gs-accent); box-shadow: var(--gs-glow-accent); }
.bd-chip-btn__name { font-weight: 700; }

.bd-pin-row { display: flex; gap: var(--gs-space-2); flex-wrap: wrap; }
.bd-pin-card {
  flex: 1 1 30%; min-width: 128px; min-height: var(--gs-tap-min);
  display: flex; flex-direction: column; gap: 4px; align-items: flex-start;
  padding: var(--gs-space-2); border-radius: var(--gs-radius-md);
  border: 1px solid var(--gs-hairline); background: var(--gs-surface-sunken);
  color: var(--gs-ink); text-align: left; cursor: pointer;
}
.bd-pin-card--on { border-color: var(--gs-accent); box-shadow: var(--gs-glow-accent); }
/* 锁着的卡**照样显示、照样能按**，只是变灰并写明还差多少——藏起来玩家就不知道有这回事。 */
.bd-pin-card--locked { filter: grayscale(0.75); opacity: 0.82; }
.bd-pin-card__art { width: 48px; height: 48px; }
.bd-pin-card__name { font-weight: 700; }

.bd-swatch-row { display: flex; gap: var(--gs-space-2); flex-wrap: wrap; }
.bd-swatch {
  flex: 1 1 20%; min-width: var(--gs-tap-min); min-height: var(--gs-tap-min);
  display: flex; flex-direction: column; align-items: center; gap: 4px;
  padding: var(--gs-space-1); border-radius: var(--gs-radius-md);
  border: 1px solid var(--gs-hairline); background: var(--gs-surface-sunken);
  color: var(--gs-ink); cursor: pointer;
}
.bd-swatch--on { border-color: var(--bd-swatch-hue, var(--gs-accent)); box-shadow: var(--gs-glow-accent); }
.bd-swatch--locked { filter: grayscale(0.8); opacity: 0.8; }
.bd-swatch__chip { position: relative; width: 44px; height: 44px; display: block; }
.bd-swatch__ball { position: absolute; inset: 0; width: 100%; height: 70%; object-fit: contain; }
.bd-swatch__guard { position: absolute; left: 0; right: 0; bottom: 0; width: 100%; height: 26%; object-fit: fill; }

.bd-lock { display: inline-flex; align-items: center; gap: 6px; font-size: var(--gs-text-xs);
  color: var(--gs-ink-dim); }
.bd-lock__icon { width: 14px; height: 14px; }

/* ───────────────────────── 开关 ───────────────────────── */

.bd-toggle {
  display: flex; align-items: center; gap: var(--gs-space-2);
  min-height: var(--gs-tap-min); padding: var(--gs-space-2);
  border-radius: var(--gs-radius-md); border: 1px solid var(--gs-hairline);
  background: var(--gs-surface-sunken); color: var(--gs-ink); cursor: pointer; width: 100%;
}
.bd-toggle__icon { width: 20px; height: 20px; }
.bd-toggle__label { flex: 1 1 auto; text-align: left; font-weight: 600; }
.bd-toggle__switch { width: 46px; height: 26px; }

/* ───────────────────────── 结算 ───────────────────────── */

.bd-result__header { align-items: center; text-align: center; }
.bd-tiers { display: flex; align-items: flex-end; gap: var(--gs-space-2); height: 132px; }
.bd-tier { flex: 1 1 0; display: flex; flex-direction: column; align-items: center;
  justify-content: flex-end; height: 100%; gap: 4px; }
.bd-tier__pct { font-size: var(--gs-text-xs); font-variant-numeric: tabular-nums; color: var(--gs-ink-soft); }
/* 柱高为 0 的那一档也必须画出**可见的柱底与 0%**，不许留空槽。 */
.bd-tier__bar { display: block; width: 100%; min-height: 4px; border-radius: 4px 4px 0 0;
  background: linear-gradient(180deg, var(--gs-accent), var(--gs-accent-2)); }
.bd-tier__tier { font-size: var(--gs-text-xs); color: var(--gs-ink-dim); }

.bd-share-preview { display: flex; align-items: center; justify-content: center; height: 100%; }
.bd-share-canvas { max-width: 100%; max-height: 100%; border-radius: var(--gs-radius-md); }

/* ───────────────────────── 内嵌横条 ───────────────────────── */

.bd-inline-slot {
  position: absolute; left: var(--gs-space-3); right: var(--gs-space-3);
  bottom: calc(var(--gs-space-4) + env(safe-area-inset-bottom));
}
/* 解锁横幅住在竞技场最上方 0.08–0.20 那条空白带里，`play` 分区从 0.21 起，两者不相交。
   **底层游玩不暂停、不降饱和、不清空**——解锁是好消息，不是打断。 */
.bd-inline-slot--unlock {
  top: 8%; bottom: auto; height: 12%;
  display: flex; align-items: center; gap: var(--gs-space-2);
  padding: var(--gs-space-2); box-sizing: border-box;
  border-radius: var(--gs-radius-lg); border: 1px solid var(--gs-gold);
  background: var(--gs-surface-raised); box-shadow: var(--gs-glow-gold);
}
.bd-inline-slot--coach { bottom: auto; top: 4%; }
.bd-inline-slot--lang { display: flex; flex-direction: column; gap: var(--gs-space-2);
  padding: var(--gs-space-3); border-radius: var(--gs-radius-lg);
  border: 1px solid var(--gs-hairline); background: var(--gs-surface-raised); }
.bd-unlock__art { width: 44px; height: 44px; }
.bd-unlock__body { display: flex; flex-direction: column; gap: 2px; flex: 1 1 auto; min-width: 0; }
.bd-lang-row { display: flex; flex-wrap: wrap; gap: var(--gs-space-1); }
.bd-lang { min-width: var(--gs-tap-min); min-height: var(--gs-tap-min);
  border-radius: var(--gs-radius-sm); border: 1px solid var(--gs-hairline);
  background: var(--gs-surface-sunken); color: var(--gs-ink); cursor: pointer; }
.bd-lang--on { border-color: var(--gs-accent); }

/* ───────────────────────── 里程碑 / 徽章 / 每日 ───────────────────────── */

.bd-track, .bd-badges, .bd-list, .bd-attempts { list-style: none; margin: 0; padding: 0;
  display: flex; flex-direction: column; gap: var(--gs-space-2); }
.bd-badges, .bd-attempts { flex-direction: row; flex-wrap: wrap; }
.bd-track__row { display: flex; align-items: center; gap: var(--gs-space-2);
  padding: var(--gs-space-2); border-radius: var(--gs-radius-md);
  border: 1px solid var(--gs-hairline); background: var(--gs-surface-sunken); }
.bd-track__row--done { border-color: var(--gs-accent); }
.bd-track__art { width: 36px; height: 36px; }
.bd-track__body { flex: 1 1 auto; display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.bd-track__progress, .bd-badge__progress { font-variant-numeric: tabular-nums;
  font-weight: 700; color: var(--gs-ink-soft); }

.bd-badge { flex: 1 1 18%; min-width: 84px; display: flex; flex-direction: column;
  align-items: center; gap: 4px; padding: var(--gs-space-2);
  border-radius: var(--gs-radius-md); border: 1px solid var(--gs-hairline);
  background: var(--gs-surface-sunken); text-align: center; opacity: 0.72; }
.bd-badge--done { opacity: 1; border-color: var(--gs-gold); box-shadow: var(--gs-glow-gold); }
/* 徽章中央是**对应格数的环缩影**，格数逐枚可数——颜色不是唯一信息通道。 */
.bd-badge__ring { display: flex; gap: 3px; }
.bd-badge__pip { width: 6px; height: 12px; border-radius: 3px; background: var(--gs-gold); display: block; }

.bd-attempt { min-width: 44px; min-height: 44px; display: flex; align-items: center;
  justify-content: center; border-radius: var(--gs-radius-pill);
  font-variant-numeric: tabular-nums; font-weight: 700; }
.bd-attempt--done { background: var(--gs-accent); color: #05121C; }
/* 下一局的位置是一枚**空心虚线点**：它表示「还可以继续玩」，不是次数上限。 */
.bd-attempt--next { border: 2px dashed var(--gs-hairline); }

.bd-stat { display: flex; flex-direction: column; gap: 2px; padding: var(--gs-space-2);
  border-radius: var(--gs-radius-md); border: 1px solid var(--bd-panel-edge);
  background: var(--gs-surface-sunken); }

/* ───────────────────────── 帮助 / 错误 ───────────────────────── */

.bd-list__row { display: flex; align-items: flex-start; gap: var(--gs-space-2); }
.bd-list__icon { width: 20px; height: 20px; flex: 0 0 auto; margin-top: 2px; }
.bd-compare { display: inline-flex; gap: 4px; align-items: center; flex: 0 0 auto; }
.bd-compare__art { width: 28px; height: 28px; }

.bd-details { border-radius: var(--gs-radius-md); border: 1px solid var(--gs-hairline);
  background: var(--gs-surface-sunken); padding: var(--gs-space-2); }
.bd-details__list { margin: var(--gs-space-1) 0 0; padding-left: var(--gs-space-3);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: var(--gs-text-xs);
  color: var(--gs-ink-dim); }
/* 断链环是**画出来的图形**（圆环 + 一道缺口），不是一个感叹号排版字符——
   排版字符会把一种文字体系烧进八语言共用的界面。 */
.bd-broken-link { display: block; width: 64px; height: 64px; align-self: center;
  border-radius: 50%; border: 6px solid var(--gs-danger);
  clip-path: polygon(0 0, 100% 0, 100% 38%, 50% 38%, 50% 62%, 100% 62%, 100% 100%, 0 100%);
  filter: drop-shadow(0 0 12px rgba(255, 107, 107, 0.5)); }

.bd-boot-error { padding: var(--gs-space-4); color: var(--gs-ink); }
.bd-boot-error__detail { font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: var(--gs-text-xs); color: var(--gs-ink-dim); }

/* ───────────────────────── 手机横屏 ─────────────────────────
   这是手机的一个朝向，**不是桌面**。560px 来自 shared/phone-canvas.json，
   是画布锁定给手机横屏留的出口高度——写别的数会让中间那一段高度两套规则都不管。 */
@media (orientation: landscape) and (max-height: 560px) {
  /* 底部留 20px：横屏下 HUD 竖条一直贴到屏幕最下沿，而暂停键就在这一条的最下方。
     `.bd-layer` 已经扣过 env(safe-area-inset-bottom)，所以这里再扣一次会双重计算——
     这 20px 是**安全区之外**还要留的那一截手指余量（走查的下限是 16px）。 */
  .bd-hud { flex-direction: column; justify-content: space-around;
    padding: var(--gs-space-2) 0 20px;
    border-top: none; border-left: 1px solid var(--gs-hairline); }
  .bd-readout { align-items: center; }
  /* 横屏下三个槽改成**竖排**，虚线轮廓仍要清晰。 */
  .bd-slots { flex-direction: column; }
  .bd-hud__end { margin-left: 0; margin-top: auto; flex-direction: column; }
  .bd-pin-row, .bd-chip-row { flex-wrap: nowrap; }
  .bd-inline-slot--unlock { top: 4%; height: 18%; }
}

/* 减少动态只去掉**动**，不去掉信息：格数环的离散状态、护线段数、槽位数一个不少。 */
@media (prefers-reduced-motion: reduce) {
  .bd-slots--reject { animation: none; }
  * { transition-duration: 0.01ms !important; }
}

/* ─────────────── 分区高度是比例，内容尺寸是 px：两者要能对上 ───────────────
   分区是 SCREEN_SPEC 冻结的**比例**，所以同一块分区在 375×812 上有 179px，
   在站点交给游戏的那块框（342×403，`shared/site-stage.mjs` 算出来的真值）上
   只有 89px。而字号、图标、按钮高度都是 px——一套 px 摊在两种高度上，
   矮的那一档必然把内容裁掉。走查实测：标题页 brand 分区在 375 上溢出 42px、
   在站点框上溢出 148px。

   两条对策，按「这块内容能不能被折叠」分开用：

   ①**能按比例缩的（品牌块）** → 容器查询把字号、图标绑到分区自己的高度上。
     `container-type: size` 让 `cqh` 指的是这块分区的高度，`min()` 保证
     它永远不超过原来的 px 值——宽松的档位一个像素都没变，只有矮档在收。
   ②**不能再缩的（统计条、六颗按钮）** → 44×44 是平台基线的触控下限，
     缩它等于把可用性换成一张好看的截图。所以让分区**滚动**：
     走查同样把「靠滚动容纳内容」算合格，玩家滚一下就够得到。 */

/* S3逐屏复核：分区内部保持图形层级及手机触控尺寸。 */
.bd-zone > * { flex-shrink: 0; }
.bd-zone[data-zone-name="brand"] { align-items: center; text-align: center; }

.bd-zone:is([data-zone-screen="title"], [data-zone-screen="loading"])[data-zone-name="brand"] {
  container-type: size;
  justify-content: center;
  gap: min(var(--gs-space-2), 3cqh);
}
.bd-zone:is([data-zone-screen="title"], [data-zone-screen="loading"])[data-zone-name="brand"] .bd-logo {
  width: min(84px, 26cqh);
  height: auto;
}
.bd-zone:is([data-zone-screen="title"], [data-zone-screen="loading"])[data-zone-name="brand"] :is(.bd-display-xl, .bd-display-lg) {
  font-size: min(var(--gs-text-hero), 22cqh);
  line-height: 1.05;
}
.bd-zone:is([data-zone-screen="title"], [data-zone-screen="loading"])[data-zone-name="brand"] .bd-body-sm {
  font-size: min(var(--gs-text-sm), 11cqh);
  line-height: 1.3;
}

/* 最佳分数条：`.gs-stat__icon` 是硬写死的 26px（组件库的选择器不许覆盖），
   所以矮档只能靠间距与字号收，收不下的那一档交给滚动。 */
.bd-zone[data-zone-screen="title"][data-zone-name="best"] {
  container-type: size;
  justify-content: center;
  overflow-y: auto;
  overscroll-behavior: contain;
  --gs-space-3: min(12px, 9cqh);
  --gs-space-2: min(8px, 7cqh);
  --gs-text-lg: min(22px, 20cqh);
  --gs-text-xs: min(12px, 13cqh);
}

.bd-zone[data-zone-screen="title"][data-zone-name="play"] {
  --gs-space-3: 8px;
  overflow-y: auto;
  overscroll-behavior: contain;
}

/* 浮层标题条：一行里只有一颗关闭键和一个标题，而关闭键的 44×44 是平台基线的
   触控下限，**不许为了塞进分区把它缩掉**。站点框（342×403）上 header 分区按比例
   只有 38px，于是这一行必然高出 6px（帮助屏 10px）。所以这一档交给滚动，
   不动触控尺寸：走查同样把「靠滚动容纳内容」算合格。 */
.bd-zone[data-zone-name="header"] { overflow-y: auto; overscroll-behavior: contain; }

/* 教学聚光圈比分区宽 6px（组件自带的外扩），分区已经把它裁掉了，
   但 `scrollWidth` 照样把这 6px 算进去，走查就报「竞技场被裁掉 6px」。
   在宿主这一层收边：不动 `.gs-*`，也不改聚光圈的观感。 */
.bd-coach-host { overflow: hidden; }
.bd-zone[data-zone-screen="title"][data-zone-name="play"] > p { text-align: center; }
.bd-row > button { flex: 1 1 0; min-width: 44px; }
.bd-screen--title .bd-row--icons { align-items: center; }
.bd-screen--title .bd-row--icons > .bd-caption { white-space: nowrap; }
.bd-panel--pause .bd-panel__foot { display: grid; grid-template-columns: 1fr 1fr; align-content: end; }
.bd-panel--pause .bd-panel__foot > button:first-child { grid-column: 1 / -1; }
.bd-panel__header > button { order: 2; margin-left: auto; }
.bd-panel__header > h2 { order: 1; }
.bd-panel--result { --gs-text-xl: 18px; --gs-space-4: 10px; }
.bd-panel--result .bd-result__header { --gs-text-hero: 60px; gap: 4px; }
.bd-panel--result .bd-panel__body { padding: 8px; }
.bd-precision { display: grid; grid-template-columns: minmax(0, .8fr) minmax(0, 1.2fr); gap: 8px; }
.bd-precision > * { min-width: 0; --gs-space-4: 8px; --gs-text-xl: 14px; }
.bd-exact-hero { display: block; font-size: 36px; color: var(--gs-accent); text-align: center; }
.bd-loss-hero { display: block; font-size: 28px; color: var(--gs-gold); text-align: right; }
.bd-share-actions { width: 100%; }
.bd-ring-diagram { width: 100%; max-width: 64px; height: auto; aspect-ratio: 1; }
.bd-compare .bd-ring-diagram { width: 36px; }
.bd-equals { width: 10px; height: 5px; border-block: 2px solid var(--gs-accent); }
.bd-track__done { width: 16px; height: 9px; border-bottom: 3px solid var(--gs-accent); border-left: 3px solid var(--gs-accent); transform: rotate(-45deg); }
.bd-daily-icon { width: 44px; height: 44px; filter: drop-shadow(0 0 5px var(--gs-accent)); }
.bd-details summary { min-height: 44px; display: flex; align-items: center; cursor: pointer; }
.bd-panel__demo .bd-demo { flex: 1 1 0; height: 0; min-height: 0; }
.bd-panel__demo > button { flex: 0 0 44px; }
.bd-panel--daily .bd-stat { min-height: 112px; justify-content: center; }
.bd-panel--daily .bd-numeric-lg { font-size: 32px; }
.bd-panel--daily .bd-attempts { gap: 16px; }
.bd-attempt { position: relative; }
.bd-attempt + .bd-attempt::before { content: ''; position: absolute; right: 100%; width: 16px; height: 2px; background: var(--gs-accent); }
.bd-coach-host > * { pointer-events: none; }
.bd-pin-row { flex-wrap: nowrap; }
.bd-pin-card { flex: 1 1 0; min-width: 0; }
.bd-pin-card__art { width: 100%; height: auto; aspect-ratio: 1; max-height: 100px; object-fit: contain; }
.bd-pin-card .bd-body-sm { font-size: 12px; }
.bd-badge { flex: 1 1 0; min-width: 0; padding: 4px; }
.bd-lang-row { gap: 8px; }
.bd-track__body { --gs-meter-height: 8px; }
.bd-track__art { width: 48px; height: 48px; }
.bd-slot { position: relative; }
.bd-slot > img { width: 100%; height: 100%; display: block; }
.bd-slot__refill { position: absolute; inset: -2px; border-radius: 50%; background: conic-gradient(var(--gs-accent) var(--bd-refill, 0deg), transparent 0); mask: radial-gradient(circle, transparent 56%, black 60%); }
.bd-button-icon { width: 18px; height: 18px; vertical-align: middle; margin-right: 8px; }
.bd-unlock__art { display: flex; flex-direction: column; flex: 0 0 44px; }
.bd-unlock__art > img { width: 100%; height: 100%; object-fit: contain; min-height: 0; }
.bd-unlock__art > .bd-unlock__guard { height: 8px; }
.bd-panel__foot { --gs-space-3: 8px; --gs-space-5: 12px; }
.bd-broken-link { flex-shrink: 0; }
.bd-panel__body, .bd-panel__side { scrollbar-width: thin; }
@media (orientation: landscape) and (max-height: 560px) {
  .bd-panel--pause .bd-panel__foot { display: flex; flex-direction: row; align-items: end; }
  .bd-panel--pause .bd-panel__foot > button { flex: 1 1 0; }
  .bd-panel--result .bd-panel__foot { display: grid; grid-template-columns: 1fr 1fr; align-content: end; }
  .bd-panel--result .bd-panel__foot > .bd-row { display: contents; }
  .bd-panel--result .bd-result__header { --gs-text-hero: 42px; }
  .bd-panel--result .bd-result__header .bd-body-sm { font-size: 12px; }
  .bd-panel--result .bd-tiers { height: 92px; }
  .bd-panel--result .bd-exact-hero { font-size: 28px; }
}
