/*
 * 色彩接力 · 主题与分区层。
 *
 * 两条纪律：
 * ① **只覆盖组件库的变量，不覆盖 `.gs-*` 选择器。** 覆盖选择器会把组件的可用性
 *    （44×44 触控下限、安全区、焦点圈定）一起改坏，而那些东西坏了在手机上看不出来。
 *    本作自己的东西一律用 `cr-` 前缀，和组件库井水不犯河水。
 * ② **只做手机端。** 全文没有一条不带 `max-height` 的 `@media (min-width: …)`——
 *    那种写法是在给桌面另写一套版式，而桌面跑的是共用库锁出来的一块手机画布
 *    （`shared/game-shell/ui/ui.css` 末尾「手机画布锁定」）。拿桌面的尺子量手机画布，
 *    分区当场被撑破。手机横屏是**手机的一个朝向**，写成
 *    `(orientation: landscape) and (max-height: 560px)` 这一档；
 *    560 来自 `shared/phone-canvas.json`，所有游戏同一个数。
 *
 * 分区（`.cr-zone`）的 0~1 坐标是**扣掉安全区之后**的比例，逐条来自
 * `game.config.json.zones`，而那份又由 `tools/sync-zones.mjs` 从 S2 冻结的
 * `art/SCREEN_SPEC.json` 机械同步。位置由 JS 写成内联 style，这里只管它们长什么样。
 */

:root {
  /* ---- 组件库令牌：换成本作的霓虹玩具街机配色（界面稿 rev-1 的取色） ---- */
  --gs-ink: #EDF3FF;
  --gs-ink-soft: rgba(198, 214, 245, 0.84);
  --gs-ink-dim: rgba(163, 180, 220, 0.62);
  --gs-ink-on-light: #0B1030;

  --gs-surface: rgba(23, 20, 66, 0.88);
  --gs-surface-raised: rgba(38, 32, 96, 0.95);
  --gs-surface-sunken: rgba(11, 9, 36, 0.72);
  --gs-hairline: rgba(126, 150, 245, 0.3);

  --gs-accent: #3FD2F2;
  --gs-accent-2: #6C5BE0;
  --gs-gold: #FFD257;
  --gs-danger: #FF5C6E;

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

  --gs-glow-accent: 0 0 0 1px rgba(63, 210, 242, 0.55), 0 0 22px rgba(63, 210, 242, 0.32);
  --gs-glow-gold: 0 0 22px rgba(255, 210, 87, 0.5);
  --gs-shadow-card: 0 14px 34px rgba(4, 5, 24, 0.58);
  --gs-shadow-button: 0 5px 0 rgba(4, 5, 24, 0.52), 0 10px 20px rgba(4, 5, 24, 0.45);
  --gs-shadow-press: 0 2px 0 rgba(4, 5, 24, 0.52), 0 5px 10px rgba(4, 5, 24, 0.45);

  --gs-cta: linear-gradient(180deg, #F6EEDA 0%, #E4D6B4 55%, #C9B888 100%);

  /* ---- 本作自己的令牌 ---- */
  /* 三个玩法色相。**与 `render.mjs` 的 `COLOUR_HEX` 是同一组值**——改一处要改另一处；
     那边画布上画，这边 DOM 上画，两处显示的必须是同一个青。 */
  --cr-cyan: #3FD2F2;
  --cr-magenta: #EE2E9B;
  --cr-amber: #FFB020;

  --cr-plate: linear-gradient(180deg, rgba(34, 30, 92, 0.95), rgba(16, 13, 52, 0.95));
  --cr-plate-line: rgba(126, 150, 245, 0.34);
  --cr-pad: clamp(8px, 2.4vw, 14px);
  /* 分区的**纵向**内边距单独一条：分区高度是 SCREEN_SPEC 冻结的比例，
     矮分区（HUD 只有画布高的 14%）里，横向那点留白照搬到纵向就是溢出。
     跟的是 `vh` 不是 `vw`：手机横屏时视口很宽而很矮，按宽度算的留白会把矮分区撑爆。 */
  --cr-pad-y: clamp(3px, 0.7vh, 9px);
  --cr-font: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  /* HUD 里那一格有多大。竖屏 14% 的高度要放下两行，所以它必须能跟着视口收——
     但**暂停键不跟着收**（见 `.cr-hud__pause`），44×44 是平台触控下限，不是建议值。

     `min()` 里那一项是**窄机保险**，不是第二套版式：HUD 第二行同时排着
     「本站色 / 下一站色」「背包 5 格」「到站推进条」三组东西，只按高度定格子大小时，
     375px 宽这一档会把前两组撑到把推进条挤成 25px——推进条是 §6.5 登记的常驻指示器，
     被挤掉就等于这一条指示器不存在。所以格子同时受宽度约束，先收格子、不牺牲指示器。 */
  --cr-slot: clamp(18px, min(3.6vh, 6.4vw), 34px);
}

html,
body {
  margin: 0;
  padding: 0;
  height: 100%;
  background: #05070f;
  color: var(--gs-ink);
  font-family: var(--cr-font);
  overscroll-behavior: none;
  -webkit-text-size-adjust: 100%;
}

#app {
  position: fixed;
  inset: 0;
  overflow: hidden;
  background: #070A22;
}

/* 安全区扣完之后剩下的那块可视矩形。分区的 0~1 全部相对它。 */
#cr-safe {
  position: absolute;
  top: var(--gs-safe-top);
  right: var(--gs-safe-right);
  bottom: var(--gs-safe-bottom);
  left: var(--gs-safe-left);
  overflow: hidden;
  /* §2.1 的唯一核心操作是「在画布任意处按一下」，也就是**连续快点**。
     不写这一行，iOS 会把连续两次点当成「双击放大」——整个页面当场缩放，
     而 `user-scalable=no` 拦不住它（iOS 10 起就不再听那个字段了）。
     另外三款拖拽类玩法早就有这一行，本作因为核心操作是"点"而被漏掉：
     点得越快越容易触发，也就是**玩得越好越容易坏**。
     判据是 automation/check_mobile.py 的 `touch-action`。 */
  touch-action: none;
}

#cr-canvas {
  position: absolute;
  inset: 0;
  /* **这两行少一行，整局游戏就错位一倍。** `<canvas>` 是替换元素：绝对定位下
     `width: auto` 取的是**内在尺寸**（也就是 `canvas.width` 属性 = CSS px × dpr），
     `inset: 0` 里的 `right/bottom` 会被当成过约束直接丢掉，**不会**把它撑开。
     于是在 dpr=2 的屏上画布的 CSS 盒子是安全区的 2 倍，`#cr-safe` 的 overflow:hidden
     只露出左上四分之一：信使、判定线、颜色环、跑道下半段全在屏幕外。 */
  width: 100%;
  height: 100%;
  display: block;
  z-index: 0;
  /* 画布不吃指针：按压是挂在 `#cr-safe` 上的一条路径（§2.1「按压区是整块画布」），
     所以落在 HUD 上的按下也照样穿透到画布。 */
  pointer-events: none;
}

.cr-layer {
  position: absolute;
  inset: 0;
  z-index: 1;
}

/* 浮层宿主铺满安全区，但**空着时不许接管命中测试**：它排在内容层后面，
   否则 HUD 与结算主按钮在 elementFromPoint 下全部变成"被盖住"。
   真正要挡住背景的是浮层根节点自己（`.cr-overlay` 带 scrim），所以它把指针收回来。 */
.cr-layer--overlay {
  pointer-events: none;
  z-index: 2;
}

.cr-layer--overlay > * {
  pointer-events: auto;
}

/* 读屏播报：分数、连击、本站色、下一站色、背包同色数，节流 1 s（§10.5）。 */
.cr-live {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
}

/* ---- 分区 ---- */

.cr-zone {
  position: absolute;
  box-sizing: border-box;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: stretch;
  gap: var(--cr-pad-y);
  padding: var(--cr-pad-y) var(--cr-pad);
}

.cr-zone[data-scroll='y'] {
  overflow-y: auto;
  justify-content: flex-start;
  -webkit-overflow-scrolling: touch;
}

/* **可滚分区的子项一律不许被压。** flex 子项默认 `flex-shrink: 1`：内容比分区高时
   浏览器不是让分区滚起来，而是先把每一件压到比它自己的内容还矮，
   溢出的部分照原样画到下一件身上——帮助页那三段说明就是这样叠成一团的
   （和 jelly-match `.jm-zone--scroll > *` 修的是同一个形状）。 */
.cr-zone[data-scroll='y'] > * {
  flex-shrink: 0;
}

/* 通用分区的纵向间距可以压到 3px 来容纳 HUD，但含成排控件的区域不能跟着压：
   平台基线要求相邻可点元素至少相隔 8px。body 覆盖设置开关 / 模式卡，foot 覆盖
   主次按钮；空间不够时它们本来就允许纵向滚动，所以不靠缩间距硬塞。 */
.cr-overlay .cr-zone[data-zone='body'],
.cr-zone[data-zone='foot'] {
  gap: var(--gs-space-2);
}

/* HUD 与游玩区之外的分区不吃指针，落在它们上面的按下穿透到画布（SCREEN_SPEC screen-04）。
   真正要点的东西（暂停键、浮层按钮）自己把指针收回来。 */
.cr-zone--ghost {
  pointer-events: none;
}

.cr-zone--ghost button,
.cr-zone--ghost [role='button'],
.cr-zone--ghost a {
  pointer-events: auto;
}

/* ---- 按压区：整块 play 分区，后果画在画布上 ---- */

.cr-press {
  position: absolute;
  inset: 0;
  border: 0;
  background: transparent;
  /* 键盘可达（§6.1：Space / ↑ / → 是等价路径），但不画焦点框以外的任何装饰——
     它是"整块画布"，不是一个按钮的样子。 */
  outline-offset: -3px;
}

/* ---- 标题 / 品牌 ---- */

.cr-title {
  margin: 0;
  font-size: clamp(28px, 8.4vw, 52px);
  font-weight: 900;
  letter-spacing: 0.01em;
  line-height: 1.02;
  text-align: center;
  color: #F6EEDA;
  text-shadow: 0 3px 0 rgba(10, 8, 40, 0.55), 0 0 26px rgba(238, 46, 155, 0.45);
}

.cr-title--sm {
  font-size: clamp(20px, 5.4vw, 30px);
}

/* 字号跟着**分区高度**走，不写死 px：矮分区（结算页的 head 只有画布高的 12%、
   横屏只有 18%）里，一句 14px 的三行说明就会顶破分区，而那正是走查报「布局乱」的样子。 */
.cr-sub {
  margin: 0;
  text-align: center;
  font-size: clamp(11px, 1.7vh, 14px);
  color: var(--gs-ink-soft);
  line-height: 1.35;
}

.cr-muted {
  color: var(--gs-ink-dim);
  font-size: var(--gs-text-xs);
}

.cr-brandmark {
  display: block;
  margin: 0 auto;
  height: clamp(34px, 8vh, 72px);
  width: auto;
}

.cr-row {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--gs-space-2);
  flex-wrap: wrap;
}

.cr-row--tight {
  gap: var(--gs-space-1);
  flex-wrap: nowrap;
}

.cr-col {
  display: flex;
  flex-direction: column;
  gap: var(--gs-space-2);
}

/* ---- HUD ---- */

.cr-hud {
  display: flex;
  flex-direction: column;
  gap: var(--cr-pad-y);
  justify-content: center;
}

.cr-hud__row {
  display: flex;
  align-items: center;
  gap: var(--gs-space-2);
  min-height: 0;
}

.cr-hud__pause {
  /* 44×44 是平台触控下限，**不跟着 --cr-slot 收**。 */
  flex: 0 0 auto;
  width: 44px;
  height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--gs-radius-sm);
  border: 1px solid var(--cr-plate-line);
  background: var(--cr-plate);
  color: var(--gs-ink);
}

.cr-hud__pause img {
  width: 22px;
  height: 22px;
  filter: var(--gs-icon-tint-ink);
}

.cr-stat {
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-width: 0;
  padding: 2px 8px;
  border-radius: var(--gs-radius-sm);
  border: 1px solid var(--cr-plate-line);
  background: var(--cr-plate);
}

.cr-stat__label {
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--gs-ink-dim);
  white-space: nowrap;
}

.cr-stat__value {
  font-size: clamp(14px, 2.6vh, 22px);
  font-weight: 800;
  line-height: 1.05;
  white-space: nowrap;
}

.cr-stat__value--gold {
  color: var(--gs-gold);
}

.cr-stat--wide {
  flex: 1 1 auto;
}

/* 到最后 10 秒变金色并放大——**催促，不是威胁**（§6.5）：
   `crash-red` 与红色闪烁一律不许用在冲刺倒计时上。 */
.cr-stat[data-urgent='true'] .cr-stat__value {
  color: var(--gs-gold);
  transform: scale(1.12);
  transform-origin: left center;
}

/* 本站色 / 下一站色 */
.cr-postchip {
  width: var(--cr-slot);
  height: var(--cr-slot);
  border-radius: 50%;
  border: 2px solid rgba(255, 255, 255, 0.75);
  flex: 0 0 auto;
}

.cr-postchip[data-when='next'] {
  filter: brightness(0.72);
}

/* 到站前两个物件开始按 gap 同频呼吸——**预警完全由视觉承担**（§10.5 那一行的音是留空的）。 */
.cr-postchip[data-breathing='true'] {
  animation: cr-breathe 1.05s ease-in-out infinite;
}

@keyframes cr-breathe {
  0%, 100% { box-shadow: 0 0 0 0 rgba(255, 255, 255, 0.05); }
  50% { box-shadow: 0 0 0 5px rgba(255, 255, 255, 0.22); }
}

/* 背包 5 格：§6.5 登记的四条之一，0 → 满的**增长**方向。 */
.cr-pocket {
  display: flex;
  gap: 6px;
  /* 收得比推进条晚、比推进条少：格子窄一点还认得出是 5 格，
     推进条被挤没了就是一条指示器消失。所以背包能收，收到 14px 为止。 */
  flex: 0 1 auto;
  min-width: 0;
}

.cr-pocket__slot {
  width: var(--cr-slot);
  height: var(--cr-slot);
  flex: 0 1 auto;
  min-width: 14px;
  border-radius: 8px;
  border: 1px dashed rgba(255, 255, 255, 0.28);
  background: rgba(255, 255, 255, 0.05);
}

.cr-pocket__slot[data-filled='true'] {
  border-style: solid;
  border-color: rgba(255, 255, 255, 0.7);
}

/* 与本站同色的那几格一起微亮——**那几格的数量就是 n，也就是分数本身**（§10.5）。 */
.cr-pocket__slot[data-matching='true'] {
  box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.55), 0 0 14px rgba(255, 255, 255, 0.35);
}

/* 到站推进条：**5 个离散刻度**（对应 5 条织带），不是连续横条——
   做成连续条会被读成血条（§6.5 明写这一点）。 */
.cr-postbar {
  display: flex;
  gap: 4px;
  flex: 1 1 auto;
  align-items: center;
  min-width: 0;
}

.cr-postbar__tick {
  /* `border-box`：`min-width` 要连边框一起算，否则 5 个刻度的实际最小宽度
     是 5×(6+2)+4×4 = 56px 而不是 46px，推进条会在自己的容器里被裁掉一截。 */
  box-sizing: border-box;
  flex: 1 1 0;
  height: 8px;
  min-width: 6px;
  border-radius: 4px;
  background: rgba(255, 255, 255, 0.14);
  border: 1px solid rgba(255, 255, 255, 0.2);
}

.cr-postbar__tick[data-on='true'] {
  background: var(--gs-gold);
  border-color: rgba(255, 210, 87, 0.9);
  box-shadow: 0 0 10px rgba(255, 210, 87, 0.55);
}

/* ---- 浮层 ---- */

.cr-overlay {
  position: absolute;
  inset: 0;
  /* 遮罩：浮层的定义就是**盖住底下**。组件库的 trapFocus 会顺带声明
     `role="dialog"` + `aria-modal="true"`，所以走查认得出这不是缺陷。 */
  background: rgba(6, 8, 26, 0.72);
  backdrop-filter: blur(3px);
}

.cr-panel {
  position: absolute;
  box-sizing: border-box;
  border-radius: var(--gs-radius-lg);
  border: 1px solid var(--cr-plate-line);
  background: linear-gradient(180deg, rgba(30, 26, 84, 0.97), rgba(14, 11, 44, 0.97));
  box-shadow: var(--gs-shadow-card);
  pointer-events: none;
}

.cr-card {
  display: flex;
  align-items: center;
  gap: var(--gs-space-3);
  width: 100%;
  box-sizing: border-box;
  padding: var(--gs-space-3);
  border-radius: var(--gs-radius-md);
  border: 1px solid var(--cr-plate-line);
  background: var(--cr-plate);
  color: var(--gs-ink);
  text-align: left;
  min-height: var(--gs-tap-min);
}

.cr-card[aria-pressed='true'] {
  border-color: var(--gs-gold);
  box-shadow: var(--gs-glow-gold);
}

/* 锁定项：**卡面内容仍然看得见，只写条件、不写编出来的进度百分比**（§10.1 第 11 屏）。
   用 aria-disabled 而不是 disabled——disabled 会让它从 Tab 序列里消失，
   读屏也不再念解锁条件，玩家就又不知道游戏里有这个东西了。 */
.cr-card[aria-disabled='true'] {
  border-style: dashed;
  opacity: 0.72;
}

.cr-card__body {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.cr-card__name {
  font-size: var(--gs-text-md);
  font-weight: 800;
}

.cr-card__note {
  font-size: var(--gs-text-xs);
  color: var(--gs-ink-soft);
  line-height: 1.3;
}

.cr-card__side {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: var(--gs-text-xs);
  color: var(--gs-gold);
  font-weight: 700;
}

.cr-card__side img {
  width: 16px;
  height: 16px;
  filter: var(--gs-icon-tint-gold);
}

.cr-grid {
  display: grid;
  gap: var(--gs-space-2);
}

.cr-grid--two { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.cr-grid--three { grid-template-columns: repeat(3, minmax(0, 1fr)); }

.cr-tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: var(--gs-space-2);
  min-height: var(--gs-tap-min);
  border-radius: var(--gs-radius-md);
  border: 1px solid var(--cr-plate-line);
  background: var(--cr-plate);
  color: var(--gs-ink);
  text-align: center;
}

.cr-tile[aria-pressed='true'] {
  border-color: var(--gs-accent);
  box-shadow: var(--gs-glow-accent);
}

.cr-tile[aria-disabled='true'] {
  border-style: dashed;
  opacity: 0.72;
}

.cr-tile__name {
  font-size: var(--gs-text-sm);
  font-weight: 800;
  line-height: 1.2;
}

.cr-tile__note {
  font-size: 11px;
  color: var(--gs-ink-soft);
  line-height: 1.25;
}

.cr-tile__lock {
  width: 14px;
  height: 14px;
  filter: var(--gs-icon-tint-ink);
  opacity: 0.7;
}

/* 设置行：图标 + 名字 + 开关。整行可点（≥44px），不是只有那个小滑块可点。 */
.cr-toggle {
  display: flex;
  align-items: center;
  gap: var(--gs-space-3);
  width: 100%;
  box-sizing: border-box;
  min-height: var(--gs-tap-min);
  padding: 6px var(--gs-space-3);
  border-radius: var(--gs-radius-md);
  border: 1px solid transparent;
  background: transparent;
  color: var(--gs-ink);
  text-align: left;
  font-size: var(--gs-text-md);
}

.cr-toggle:hover { border-color: var(--cr-plate-line); }

.cr-toggle__icon {
  width: 22px;
  height: 22px;
  flex: 0 0 auto;
  filter: var(--gs-icon-tint-ink);
}

.cr-toggle__label { flex: 1 1 auto; }

.cr-toggle__switch {
  flex: 0 0 auto;
  width: 52px;
  height: 30px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.14);
  border: 1px solid var(--cr-plate-line);
  position: relative;
}

.cr-toggle__switch::after {
  content: '';
  position: absolute;
  top: 3px;
  left: 3px;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: #C9CFE8;
  transition: transform var(--gs-fast) var(--gs-ease-out);
}

.cr-toggle[aria-pressed='true'] .cr-toggle__switch {
  background: linear-gradient(180deg, #FFD98A, #D69A17);
}

.cr-toggle[aria-pressed='true'] .cr-toggle__switch::after {
  transform: translateX(22px);
  background: #FFF6DF;
}

/* 结算页 */

.cr-hero {
  text-align: center;
  font-size: clamp(40px, 13vw, 76px);
  font-weight: 900;
  line-height: 1;
  color: var(--gs-gold);
  text-shadow: 0 0 28px rgba(255, 210, 87, 0.55);
}

/* 归因句：**paper、正常字重，不发光、不用红**——它是诊断不是指责（SCREEN_SPEC screen-07）。 */
.cr-reason {
  margin: 0;
  text-align: center;
  font-size: clamp(12px, 2.1vh, 17px);
  font-weight: 400;
  line-height: 1.32;
  color: var(--gs-ink);
}

.cr-detail {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  font-size: var(--gs-text-xs);
  color: var(--gs-ink-soft);
}

.cr-detail__chip {
  width: 16px;
  height: 16px;
  border-radius: 4px;
  border: 1px solid rgba(255, 255, 255, 0.6);
}

.cr-strike {
  text-decoration: line-through;
  color: var(--gs-danger);
  font-weight: 700;
}

.cr-meter {
  height: 10px;
  border-radius: 6px;
  background: rgba(255, 255, 255, 0.12);
  border: 1px solid var(--cr-plate-line);
  overflow: hidden;
}

.cr-meter__fill {
  display: block;
  height: 100%;
  background: linear-gradient(90deg, #FFB020, #FFD257);
}

/* 加载条：12 格离散刻度，**真实字节进度，不做假进度条**（§10.1 第 1 屏）。 */
.cr-boot {
  display: flex;
  gap: 4px;
}

.cr-boot__cell {
  flex: 1 1 0;
  height: 16px;
  border-radius: 4px;
  background: rgba(255, 255, 255, 0.1);
  border: 1px solid var(--cr-plate-line);
}

.cr-boot__cell[data-on='true'] {
  background: linear-gradient(180deg, #FFD98A, #D69A17);
}

.cr-mono {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 11px;
  color: var(--gs-ink-dim);
  word-break: break-all;
  text-align: center;
}

.cr-dots {
  display: flex;
  gap: 6px;
  justify-content: center;
}

.cr-dots__dot {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  border: 2px solid var(--cr-plate-line);
}

/* 满的圆点带勾、空的有凹陷方向——**灰度下也要分得开**（SCREEN_SPEC screen-11）。 */
.cr-dots__dot[data-on='true'] {
  border-color: var(--gs-gold);
  background: radial-gradient(circle at 50% 40%, rgba(255, 210, 87, 0.95), rgba(214, 154, 23, 0.5));
}

.cr-dots__dot[data-on='false'] {
  background: radial-gradient(circle at 50% 70%, rgba(0, 0, 0, 0.5), transparent 70%);
}

.cr-canvas-inline {
  display: block;
  border-radius: var(--gs-radius-sm);
}

.cr-help-demo {
  /* `box-sizing` 必须写出来：canvas 默认是 content-box，`width: 100%` 之后那圈 1px 描边
     会挂在 100% 之外，整块比 body 分区宽 2px。分区是 `overflow-x: hidden`（只有纵向可滚），
     所以那 2px 是**真的被裁掉**，剧本 05 的 `assertZonesFit` 判红的就是它。
     2px 在 en 下看不出来，但它是「内容比容器宽」这件事本身——换一门长语言就会放大。 */
  box-sizing: border-box;
  width: 100% !important;
  height: auto !important;
  aspect-ratio: 65 / 36;
  border: 1px solid var(--cr-plate-line);
  background: #070A22;
}

/* 引导层：**内嵌，不阻断**。没有遮罩、不圈焦点、不进弹窗栈、一个按钮都没有——
   §7.2 明写引导不暂停跑道、不注入输入、不需要玩家确认。
   它画在 play 分区之内的三块子区域上，与 hud 不相交。 */
.cr-coach {
  position: absolute;
  box-sizing: border-box;
  pointer-events: none;
  border-radius: var(--gs-radius-md);
  border: 2px solid rgba(255, 255, 255, 0.28);
  box-shadow: 0 0 22px rgba(63, 210, 242, 0.35) inset;
}

/* 站内嵌入框最矮会到 342×403。HUD 仍保留 44px 暂停键，但把第二行压成 12px，
   两行刚好落在 14% 的 HUD 分区里；原来垂直居中会把暂停键顶到 y=-3，手机上很难点中。 */
@media (orientation: portrait) and (max-height: 560px) {
  :root {
    --cr-slot: 8px;
  }

  .cr-hud {
    justify-content: flex-start;
    gap: 0;
    padding-top: 0;
    padding-bottom: 0;
  }

  .cr-hud__row:first-child {
    height: 44px;
    flex: 0 0 44px;
  }

  .cr-hud__row:nth-child(2) {
    height: 12px;
    flex: 0 0 12px;
  }

  .cr-pocket {
    gap: 3px;
  }

  .cr-pocket__slot {
    min-width: 8px;
    border-radius: 4px;
  }

  .cr-overlay > .cr-zone[data-zone='head'] {
    padding-top: 0;
    padding-bottom: 0;
  }
}

/* ---- 手机横屏：`(orientation: landscape) and (max-height: 560px)` 那一档 ----
 * 560 来自 `shared/phone-canvas.json` 的 `shortViewportMaxHeightPx`，所有游戏同一个数。
 * **横屏不是把竖屏拉宽**：HUD 从顶部横条改成左侧竖列（分区表已经这么写了），
 * 这里只把 HUD 内部的排布跟着转过来，八项内容一件不少（SCREEN_SPEC screen-05）。
 */
@media (orientation: landscape) and (max-height: 560px) {
  /* 帮助页 body 在手机横屏只有约 318px 高。演示画布若继续按 body 的整宽展开，
     65:36 的比例会把高度推到约 325px，替换元素本身就越过父容器。横屏按短边
     限制宽度并保持原比例（932×430 时为 516×286）；竖屏仍铺满 body 宽度。 */
  .cr-help-demo {
    width: min(100%, 120dvh) !important;
    height: auto !important;
    max-width: 100%;
    margin-inline: auto;
  }

  .cr-hud {
    justify-content: space-between;
    gap: var(--gs-space-2);
  }

  .cr-hud__row {
    flex-direction: column;
    align-items: stretch;
    gap: 6px;
  }

  .cr-pocket,
  .cr-postbar {
    flex-direction: column;
  }

  .cr-postbar__tick {
    height: auto;
    min-height: 6px;
    width: 100%;
    flex: 1 1 0;
  }

  .cr-stat {
    align-items: flex-start;
  }

  .cr-title {
    font-size: clamp(22px, 5.2vh, 38px);
  }

  .cr-hero {
    font-size: clamp(30px, 11vh, 52px);
  }

  /* ---- 浮层头部：横屏下并排，不摞 ----
   *
   * 横屏的 head 分区只有画布高的 14%（932×430 上 = 60.2 px），而"标题行 + 一行速览"
   * 竖着摞起来是 44（关闭键，平台触控下限，**不许缩**）+ 3（gap）+ 16（速览）= 63 px，
   * 顶破分区。走查实测：`zone-overflow  分区「head」的内容比它高 5px`（skins 屏，
   * landscape-932x430）；daily-tasks 的 head 是同一个形状，只是那一趟没走到。
   *
   * 分区几何是 S2 冻结的，不许动。所以按横屏本来就该有的样子改**内部排布**——
   * 同一条媒体查询里 HUD 已经从横条改成竖列，头部反过来从竖摞改成并排，
   * 整块回到一行 44 px，稳稳落在 54 px 的可用高度里。竖屏一个字不变（那一档
   * head 有 97 px，摞得下）。
   *
   * error-offline 除外：它的 head 在横屏是左侧那根 378 px 高的竖列（插画 + 标题），
   * 本来就该竖着排。 */
  .cr-overlay:not([data-screen='error-offline']) > .cr-zone[data-zone='head'] {
    flex-direction: row;
    flex-wrap: nowrap;
    align-items: center;
    justify-content: center;
    column-gap: var(--gs-space-2);
  }

  /* 并排之后每一块都必须能被压窄。不给 `min-width: 0`，长语言（ja / ko / pt-BR）的
     一句速览会把兄弟顶出分区，把纵向的溢出换成横向的溢出。压窄之后文字在自己内部
     折行，两行 32 px 仍然矮于 44 px 的标题行，分区高度不受影响。
     标题行本身锁成不换行：它一旦折行就是 30 + 8 + 44 = 82 px，比摞着还高。 */
  .cr-overlay:not([data-screen='error-offline']) > .cr-zone[data-zone='head'] > * {
    min-width: 0;
  }

  .cr-overlay:not([data-screen='error-offline']) > .cr-zone[data-zone='head'] > .cr-row {
    flex-wrap: nowrap;
  }
}
