/*
 * 霓虹变道 · 主题与分区层。
 *
 * 两条纪律：
 * ① **只覆盖组件库的变量，不覆盖 `.gs-*` 选择器。** 覆盖选择器会把组件的可用性
 *    （44×44 触控下限、安全区、焦点圈定）一起改坏，而那些东西坏了在手机上看不出来。
 *    本作自己的东西一律用 `nl-` 前缀，和组件库井水不犯河水。
 * ② **只做手机端。** 全文没有一条不带 `max-height` 的 `@media (min-width: …)`——
 *    那种写法是在给桌面另写一套版式，而桌面跑的是共用库锁出来的一块手机画布
 *    （`shared/game-shell/ui/ui.css` 末尾「手机画布锁定」）。手机横屏是**手机的一个朝向**，
 *    写成 `(orientation: landscape) and (max-height: 560px)` 这一档；
 *    560 来自 `shared/phone-canvas.json`，所有游戏同一个数。
 *
 * 分区（`.nl-zone`）的 0~1 坐标是**扣掉安全区之后**的比例，逐条来自
 * `game.config.json.zones`，而那份由 `tools/sync_config.py` 从 S2 冻结的
 * `art/SCREEN_SPEC.json` 机械同步。位置由 JS 写成内联 style，这里只管它们长什么样。
 *
 * 取色来自 rev-1 的界面稿（见 SCREEN_REVIEW.json 的逐屏判词）：
 * 电光青 = 玩家车 / 主轨 / cut，品红 = 连击环 / close，金 = 分数与增益，紫 = 品牌与容器。
 * 这四个色的分工是**信息**不是装饰：§10.5 风险 3 要求 close 与 cut 在颜色、形状、音、
 * 震动上全都不同，玩家才学得会「切得多近」。
 */

:root {
  /* ---- 组件库令牌：换成本作的霓虹街机配色 ---- */
  --gs-ink: #EAF2FF;
  --gs-ink-soft: rgba(198, 214, 245, 0.82);
  --gs-ink-dim: rgba(160, 176, 218, 0.58);
  --gs-ink-on-light: #0A0616;

  --gs-surface: rgba(24, 16, 56, 0.86);
  --gs-surface-raised: rgba(40, 26, 88, 0.94);
  --gs-surface-sunken: rgba(11, 7, 30, 0.72);
  --gs-hairline: rgba(140, 120, 240, 0.28);

  --gs-accent: #35E8FF;
  --gs-accent-2: #B06CFF;
  --gs-gold: #FFC63D;
  --gs-danger: #FF5C6E;

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

  --gs-glow-accent: 0 0 0 1px rgba(53, 232, 255, 0.5), 0 0 22px rgba(53, 232, 255, 0.3);
  --gs-glow-gold: 0 0 22px rgba(255, 198, 61, 0.5);
  --gs-shadow-card: 0 16px 38px rgba(4, 2, 20, 0.6);
  --gs-shadow-button: 0 5px 0 rgba(4, 2, 20, 0.55), 0 10px 20px rgba(4, 2, 20, 0.45);
  --gs-shadow-press: 0 2px 0 rgba(4, 2, 20, 0.55), 0 5px 10px rgba(4, 2, 20, 0.45);

  --gs-cta: linear-gradient(135deg, #35E8FF 0%, #6FA8FF 52%, #B06CFF 100%);

  /* ---- 本作自己的令牌 ---- */
  /* 这四个色在 `render.mjs` 的 `PALETTE` 里有同名副本——画布上画的和 DOM 上画的
     必须是同一个青。改一处要改另一处，`tests/theme-contract.test.mjs` 会当场对账。 */
  --nl-cyan: #35E8FF;
  --nl-magenta: #FF3EA5;
  --nl-violet: #B06CFF;
  --nl-gold: #FFC63D;
  --nl-amber: #FF9B3D;
  --nl-red: #FF5C6E;
  --nl-void: #0A0616;
  --nl-asphalt: #140E2C;

  --nl-glass: linear-gradient(180deg, rgba(38, 26, 86, 0.92), rgba(18, 11, 46, 0.94));
  --nl-glass-line: rgba(140, 120, 240, 0.34);
  --nl-pad: clamp(8px, 2.6vw, 15px);
  /* 分区的**纵向**内边距单独一条：分区高度是 SCREEN_SPEC 冻结的比例，
     HUD 只有画布高的 16%，把横向留白照搬到纵向就是溢出。跟 `vh` 不跟 `vw`：
     手机横屏时视口很宽而很矮，按宽度算的留白会把矮分区撑爆。 */
  --nl-pad-y: clamp(3px, 0.8vh, 10px);
  --nl-font: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --nl-num: ui-monospace, "SF Mono", "Roboto Mono", Menlo, Consolas, monospace;
}

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

#app {
  position: fixed;
  inset: 0;
  overflow: hidden;
  background: var(--nl-void);
}

/* 安全区扣完之后剩下的那块可视矩形。**分区的 0~1 全部相对它。**
   底部那一条 `max(...,16px)` 不是装饰：`hint` 分区一直铺到 y=1.0，
   没有它的话底部提示条会贴死在画布最下沿（剧本 05 的 `hud-bottom` 逐档量这个余量）。 */
#nl-safe {
  position: absolute;
  top: var(--gs-safe-top);
  right: var(--gs-safe-right);
  bottom: max(var(--gs-safe-bottom), 16px);
  left: var(--gs-safe-left);
  overflow: hidden;
  /* §2.1 的唯一核心操作是**连续快点**。不写这一行，iOS 会把连续两次点当成
     「双击放大」——整个页面当场缩放，而 `user-scalable=no` 拦不住它。
     点得越快越容易触发，也就是**玩得越好越容易坏**。 */
  touch-action: none;
}

#nl-canvas {
  position: absolute;
  inset: 0;
  /* **这两行少一行，整局游戏就错位一倍。** `<canvas>` 是替换元素：绝对定位下
     `width: auto` 取的是内在尺寸（`canvas.width` = CSS px × dpr），
     `inset: 0` 的 right/bottom 会被当成过约束丢掉，不会把它撑开。 */
  width: 100%;
  height: 100%;
  display: block;
  z-index: 0;
  pointer-events: none;
}

/* 两层：场景层与浮层层。浮层层永远在上面，`.nl-scrim` 负责压暗背后那一层。
   两层本身都不吃指针——吃指针的是里面的按钮与按压面。 */
.nl-layer {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

#nl-scene { z-index: 1; }
#nl-overlays { z-index: 3; }

/* 一块分区。位置由 JS 按 `game.config.json.zones` 写成内联 style（S2 冻结的比例），
   这里只管它长什么样。分区本身**不吃指针**——吃指针的是里面的按压面与按钮，
   否则整块 hud 会把它下面的按压面挡掉。 */
.nl-zone {
  position: absolute;
  box-sizing: border-box;
  pointer-events: none;
  z-index: 1;
}

.nl-zone > * {
  pointer-events: auto;
}

/* 分区里的玻璃卡默认**铺满分区**。这一条写在这里而不是内联样式里，是因为
   文件末尾「矮画布」那一档要把它改成随内容长高、由分区自己滚——内联样式压不掉。 */
.nl-zone > .nl-panel {
  inset: 0;
}

/* ============================================================
 * 按压面（§2.1 的唯一操作）
 *
 * 它是 DOM 元素、后果画在画布上，所以必须自报 `data-canvas-driven="true"`：
 * 走查的 DOM 指纹看不见画布，不声明就会被报成假控件（A23 / P23）。
 * 触摸与鼠标走**同一条** pointer 路径，不写第二套输入分支。
 * ============================================================ */

.nl-input-layer {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
}

.nl-press {
  position: absolute;
  top: 0;
  bottom: 0;
  border: 0;
  padding: 0;
  margin: 0;
  background: transparent;
  color: inherit;
  -webkit-tap-highlight-color: transparent;
  touch-action: none;
  pointer-events: auto;
  user-select: none;
  -webkit-user-select: none;
  cursor: pointer;
  /* The play zone also contains read-only HUD blocks in landscape.  Keep the
     actual thumb surface above those decorative descendants so Playwright and
     real pointer hit-testing both land on the control, not on HUD copy. */
  z-index: 2;
  /* 按下去那一刻**这一侧整片亮一下**，是「点了有反应」的第一层（100 ms 之内）。
     常态不是全透明：一层几乎看不见的边缘辉光告诉玩家这半边是可以按的，
     同时把 `visible` 这条断言的 opacity 抬到 0.01 以上（剧本 05 逐档查它）。 */
  opacity: 1;
}

.nl-press::after {
  content: "";
  position: absolute;
  inset: 0;
  opacity: 0.14;
  transition: opacity var(--gs-fast) var(--gs-ease-out);
}

.nl-press[data-side="left"]::after {
  background: linear-gradient(90deg, rgba(53, 232, 255, 0.4), rgba(53, 232, 255, 0));
}

.nl-press[data-side="right"]::after {
  background: linear-gradient(270deg, rgba(53, 232, 255, 0.4), rgba(53, 232, 255, 0));
}

.nl-press[data-pressed="true"]::after {
  opacity: 0.75;
}

/* 电量 <18 时按下去是**被拒**，不是变道：这一侧闪一次暗红（§10.5 `locked` 那一行）。 */
.nl-press[data-locked="true"][data-pressed="true"]::after {
  background: linear-gradient(180deg, rgba(255, 92, 110, 0.55), rgba(255, 92, 110, 0.1));
  opacity: 0.9;
}

/* ============================================================
 * HUD（§6.5 登记的三个读数 + 段号 + 暂停）
 * 全部是 DOM：八语言本地化、读屏、安全区适配都是 DOM 的强项（§10.2）。
 * ============================================================ */

.nl-hud {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: var(--nl-pad-y);
  padding: var(--nl-pad-y) var(--nl-pad);
  box-sizing: border-box;
  background: var(--nl-glass);
  border-bottom: 1px solid var(--nl-glass-line);
  border-radius: 0 0 var(--gs-radius-md) var(--gs-radius-md);
  box-shadow: 0 10px 26px rgba(4, 2, 20, 0.45);
  /* HUD 是读数，不吃指针——落在它上面的按下照样穿透到按压面。
     唯一可交互的是暂停键，它自己把 pointer-events 打开。 */
  pointer-events: none;
}

.nl-hud__top {
  display: flex;
  align-items: center;
  gap: var(--nl-pad);
}

.nl-hud__score {
  flex: 1 1 auto;
  min-width: 0;
}

.nl-label {
  display: block;
  font-size: clamp(9px, 2.2vw, 11px);
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--gs-ink-dim);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.nl-value {
  display: block;
  font-family: var(--nl-num);
  font-size: clamp(22px, 7vw, 40px);
  font-weight: 700;
  line-height: 1.05;
  color: var(--gs-ink);
  text-shadow: 0 0 18px rgba(53, 232, 255, 0.35);
  font-variant-numeric: tabular-nums;
}

.nl-value[data-bump="true"] {
  animation: nl-bump var(--gs-normal) var(--gs-ease-out);
}

@keyframes nl-bump {
  0% { transform: scale(1); }
  40% { transform: scale(1.12); }
  100% { transform: scale(1); }
}

.nl-hud__seg {
  text-align: right;
  font-size: clamp(11px, 3vw, 14px);
  font-weight: 600;
  color: var(--gs-ink-soft);
  white-space: nowrap;
}

.nl-hud__pause {
  /* 44×44 是平台触控下限，**不跟着分区收**。视觉上只画 32px 的幽灵描边。 */
  flex: 0 0 auto;
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  border: 0;
  padding: 0;
  border-radius: var(--gs-radius-pill);
  background: rgba(11, 7, 30, 0.55);
  box-shadow: inset 0 0 0 1px var(--nl-glass-line);
  cursor: pointer;
  pointer-events: auto;
  position: relative;
  z-index: 4;
}

/* The 44px pause target deliberately does not shrink in short canvases.  The
   play zone starts immediately below the HUD zone, so give the HUD zone the
   higher stacking level instead of allowing the later play zone to intercept
   the part of that fixed-size target nearest the boundary. */
[data-zone-screen="play"][data-zone="hud"] { z-index: 4; }
[data-zone-screen="play"][data-zone="play"],
[data-zone-screen="play"][data-zone="play-right"] { z-index: 2; }

.nl-hud__pause img {
  width: 20px;
  height: 20px;
  filter: var(--gs-icon-tint-ink);
  opacity: 0.85;
}

.nl-hud__bottom {
  display: flex;
  align-items: center;
  gap: var(--nl-pad);
}

/* ---- 连击环（§6.5：它是**环不是条**） ----
   玩家看到一条在减少的条，默认理解就是「归零我会输」。连击归零不致死，
   做成条就是骗他（DEVIATIONS 的 `combo-ring-is-not-a-bar`）。
   所以它在形状、粗细、位置、颜色上都和电量条不同：环 / 细 / 分数旁 / 品红。 */
.nl-ring {
  position: relative;
  flex: 0 0 auto;
  width: clamp(38px, 12vw, 54px);
  height: clamp(38px, 12vw, 54px);
}

.nl-ring svg {
  width: 100%;
  height: 100%;
  transform: rotate(-90deg);
}

.nl-ring__track {
  fill: none;
  stroke: rgba(255, 62, 165, 0.18);
  stroke-width: 3;
}

.nl-ring__arc {
  fill: none;
  stroke: var(--nl-magenta);
  stroke-width: 3.5;
  stroke-linecap: round;
  filter: drop-shadow(0 0 6px rgba(255, 62, 165, 0.6));
  transition: stroke-dasharray var(--gs-fast) linear, opacity var(--gs-normal) var(--gs-ease-out);
}

/* 到顶（×2.0）：整圈画满并加一层白色外描边（SCREEN_SPEC 的 `ring-max`）。 */
.nl-ring[data-max="true"] .nl-ring__arc {
  stroke-width: 5;
  filter: drop-shadow(0 0 4px #fff) drop-shadow(0 0 10px rgba(255, 62, 165, 0.8));
}

/* 电量锁定时把环压暗（`ring-faded`）——那一刻玩家该看的是电量条，不是连击。 */
.nl-ring[data-faded="true"] .nl-ring__arc {
  opacity: 0.35;
}

/* 归零是**淡出**，不是闪红：无害的事件不许有报警的表现（§6.5）。 */
.nl-ring[data-empty="true"] .nl-ring__arc {
  opacity: 0;
}

.nl-ring__value {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  font-family: var(--nl-num);
  font-size: clamp(11px, 3.2vw, 15px);
  font-weight: 700;
  color: var(--gs-ink);
}

/* ---- 路线进度：8 格 pip，只涨不跌 ---- */
.nl-pips {
  display: flex;
  gap: 3px;
  flex: 1 1 auto;
  min-width: 0;
}

.nl-pips__pip {
  flex: 1 1 0;
  height: 6px;
  border-radius: 3px;
  background: rgba(53, 232, 255, 0.16);
  box-shadow: inset 0 0 0 1px rgba(53, 232, 255, 0.22);
  transition: background var(--gs-normal) var(--gs-ease-out);
}

.nl-pips__pip[data-on="true"] {
  background: linear-gradient(90deg, var(--nl-cyan), #7FD4FF);
  box-shadow: 0 0 8px rgba(53, 232, 255, 0.55);
}

/* ============================================================
 * 电量条（§2.2.1：主轨 0–100 + 超充段 100–130）
 *
 * **两段的宽度比不写在这里。** 它由 `logic.constants` 的 nominalMax /
 * overchargeMax / overchargePixelRatio 算出来（100 : 30×3 = 100 : 90，
 * 刻度落在条宽的 52.63%），由 JS 写成内联宽度。写死在 CSS 里就是第二份真值，
 * 而 A46 量的正是这个比：超充每一点必须占主轨每一点的 3 倍宽——
 * 否则玩家满电时打出一次完美贴身切线，屏幕上几乎什么都不会涨。
 * ============================================================ */

.nl-charge {
  position: relative;
  display: flex;
  align-items: stretch;
  flex: 1 1 auto;
  min-width: 0;
  height: clamp(10px, 2.6vh, 14px);
  border-radius: var(--gs-radius-pill);
  background: rgba(11, 7, 30, 0.72);
  box-shadow: inset 0 0 0 1px var(--nl-glass-line);
  overflow: hidden;
}

.nl-charge__main,
.nl-charge__over {
  position: relative;
  height: 100%;
  overflow: hidden;
}

.nl-charge__fill {
  position: absolute;
  inset: 0 auto 0 0;
  border-radius: var(--gs-radius-pill) 0 0 var(--gs-radius-pill);
  background: linear-gradient(90deg, #1E7BFF, var(--nl-cyan));
  transition: width var(--gs-fast) linear, background var(--gs-normal) var(--gs-ease-out);
}

/* 三段色 + 一个锁定态，逐条对应 §10.5：跌破 36 转琥珀并缓慢呼吸、
   跌破 18 转红并持续暗红呼吸（那一刻变道会被拒）。 */
.nl-charge[data-level="warn"] .nl-charge__fill {
  background: linear-gradient(90deg, var(--nl-amber), #FFC93D);
}

.nl-charge[data-level="low"] .nl-charge__fill {
  background: linear-gradient(90deg, #B3243C, var(--nl-red));
}

.nl-charge[data-level="warn"] {
  animation: nl-breathe 1.25s ease-in-out infinite;
}

.nl-charge[data-level="low"] {
  animation: nl-breathe 1.2s ease-in-out infinite;
  box-shadow: inset 0 0 0 1px rgba(255, 92, 110, 0.6), 0 0 10px rgba(255, 92, 110, 0.45);
}

@keyframes nl-breathe {
  0%, 100% { filter: brightness(1); }
  50% { filter: brightness(1.35); }
}

/* 超充段：白炽琥珀，与主轨的蓝 / 琥珀 / 红三段都不同（§6.5）。 */
.nl-charge__overfill {
  position: absolute;
  inset: 0 auto 0 0;
  width: 0;
  background: linear-gradient(90deg, #FFE9A8, #FFD066);
  box-shadow: 0 0 12px rgba(255, 214, 120, 0.75);
  transition: width var(--gs-fast) linear;
}

/* 标称满格那道刻度。玩家心里的「满」就在这里，超充是越过它的那一截。 */
.nl-charge__mark {
  position: absolute;
  top: -1px;
  bottom: -1px;
  width: 2px;
  background: rgba(255, 255, 255, 0.85);
  box-shadow: 0 0 6px rgba(255, 255, 255, 0.6);
  z-index: 2;
  pointer-events: none;
}

.nl-charge[data-level="low"] .nl-charge__mark {
  background: var(--nl-red);
  box-shadow: 0 0 6px rgba(255, 92, 110, 0.9);
}

.nl-charge__read {
  flex: 0 0 auto;
  font-family: var(--nl-num);
  font-size: clamp(11px, 3vw, 14px);
  font-weight: 700;
  color: var(--gs-ink);
  font-variant-numeric: tabular-nums;
  min-width: 3ch;
  text-align: right;
}

.nl-charge-row {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 1 1 auto;
  min-width: 0;
}

.nl-charge-row__lock {
  width: 14px;
  height: 14px;
  flex: 0 0 auto;
  filter: var(--gs-icon-tint-danger);
  opacity: 0;
  transition: opacity var(--gs-normal) var(--gs-ease-out);
}

.nl-charge-row[data-locked="true"] .nl-charge-row__lock {
  opacity: 1;
}

/* ============================================================
 * hint 分区：引导条与锁定提示
 *
 * **它们是内嵌横条，不是浮层**（§10.1 / `NON_MODAL_SCREENS`）：
 * 没有遮罩、不圈焦点、不进弹窗栈，玩家要能一边看提示一边操作。
 * 钉在 `hint` 分区里，所以结构上就压不到 play 分区的按压面。
 * ============================================================ */

.nl-hint {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px var(--nl-pad);
  box-sizing: border-box;
  border-radius: var(--gs-radius-md);
  background: linear-gradient(180deg, rgba(20, 12, 52, 0.9), rgba(12, 7, 36, 0.94));
  box-shadow: inset 0 0 0 1px rgba(53, 232, 255, 0.45), 0 -6px 20px rgba(4, 2, 20, 0.5);
  pointer-events: auto;
}

.nl-hint__icon {
  width: 18px;
  height: 18px;
  flex: 0 0 auto;
  filter: var(--gs-icon-tint-accent);
}

.nl-hint__text {
  flex: 1 1 auto;
  margin: 0;
  font-size: clamp(11px, 3.2vw, 14px);
  line-height: 1.3;
  color: var(--gs-ink);
}

/* 引导那一条走组件库的 `CoachStrip`（步骤点 / 图示位 / 跳过都在它里面），
   所以这里只留**锁定提示**这一条：它是警告不是教学，语义不同。
   形制与 CoachStrip 一致——它同样不阻塞游玩、同样不夺焦点。 */
.nl-hint[data-tone="warn"] {
  box-shadow: inset 0 0 0 1px rgba(255, 92, 110, 0.65), 0 -6px 20px rgba(4, 2, 20, 0.5);
}

.nl-hint[data-tone="warn"] .nl-hint__icon {
  filter: var(--gs-icon-tint-danger);
}

/* ============================================================
 * 浮层（五个：pause / result / help / share / error）
 *
 * 全部走 `shared/game-shell/dom.mjs::trapFocus`——圈焦点的同时声明
 * `role="dialog"` + `aria-modal="true"`。遮罩由 `.nl-scrim` 单独一层负责，
 * 玻璃面板只管质感：走查稿上底层画面没有压暗，弱设备上层次会糊。
 * ============================================================ */

.nl-scrim {
  position: absolute;
  inset: 0;
  background: rgba(6, 3, 20, 0.62);
  backdrop-filter: blur(3px);
  z-index: 2;
  /* 遮罩必须吃指针：这一层就是「盖住底下」的那件事本身。
     浮层打开时底下的按压面不该还能被点到。 */
  pointer-events: auto;
}

.nl-overlay {
  position: absolute;
  inset: 0;
  z-index: 3;
}

/* 玻璃卡：浮层的主体容器，四角一圈厚描边 + 外发光。
   `--nl-tone` 让失败态只换外框与光晕，**不换里面的排版**——
   完成态与失败态是同一个 result，差别是数据状态不是两套皮。 */
.nl-panel {
  position: absolute;
  box-sizing: border-box;
  border-radius: var(--gs-radius-lg);
  background: var(--nl-glass);
  box-shadow:
    inset 0 0 0 1px var(--nl-tone, var(--nl-glass-line)),
    0 0 26px var(--nl-tone-glow, rgba(140, 120, 240, 0.28)),
    var(--gs-shadow-card);
  pointer-events: auto;
}

.nl-overlay[data-tone="crash"] {
  --nl-tone: rgba(255, 92, 110, 0.7);
  --nl-tone-glow: rgba(255, 92, 110, 0.35);
}

.nl-overlay[data-tone="alert"] {
  --nl-tone: rgba(255, 122, 92, 0.75);
  --nl-tone-glow: rgba(255, 122, 92, 0.32);
}

.nl-head {
  display: flex;
  align-items: flex-start;
  gap: 10px;
}

/* 浮层抬头那一条的留白。写在样式表里（而不是内联）是为了让文件末尾
   「矮画布」那一档收得动它——36px 高的分区里，留白就是唯一能收的东西。 */
.nl-head--overlay {
  padding: 10px 12px;
}

/* 图标的默认尺寸。**没有这一条，任何没被更具体规则盖住的图标都会按 SVG 的
   固有尺寸铺开**——每日路线卡里那个 `icon-calendar` 实测撑到 96px 高，
   把只有画布高 10% 的 `feature` 分区顶穿。更具体的那些（`.nl-stat img`、
   `.nl-toggle img` 等）照旧生效，它们的选择器权重更高。 */
.nl-icon {
  width: 20px;
  height: 20px;
  flex: 0 0 auto;
  object-fit: contain;
}

.nl-head__body {
  flex: 1 1 auto;
  min-width: 0;
}

.nl-title {
  margin: 0;
  font-size: clamp(19px, 5.6vw, 28px);
  font-weight: 800;
  letter-spacing: 0.01em;
  color: var(--gs-ink);
  text-shadow: 0 0 18px rgba(53, 232, 255, 0.3);
}

.nl-sub {
  margin: 4px 0 0;
  font-size: clamp(11px, 3.2vw, 14px);
  line-height: 1.35;
  color: var(--gs-ink-soft);
}

/* 右上角的 ghost 关闭按钮。走查稿上五个浮层里有四个漏画了它，
   而它同时是 `trapFocus` 的第一个可 Tab 元素——DOM 顺序放在标题之前。 */
.nl-ghost {
  flex: 0 0 auto;
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  border: 0;
  padding: 0;
  border-radius: var(--gs-radius-pill);
  background: transparent;
  cursor: pointer;
}

.nl-ghost img {
  width: 18px;
  height: 18px;
  filter: var(--gs-icon-tint-ink);
  opacity: 0.7;
}

.nl-ghost:focus-visible {
  outline: 2px solid var(--nl-cyan);
  outline-offset: 2px;
}

/* 结算页的金色大数字。**失败态也是金色**——两屏的差别是数据状态，不是灰扑扑的失败页。 */
.nl-hero {
  text-align: center;
}

.nl-hero__label {
  display: block;
  font-size: clamp(9px, 2.6vw, 12px);
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--gs-ink-dim);
}

.nl-hero__value {
  display: block;
  font-family: var(--nl-num);
  font-size: clamp(42px, 13vw, 72px);
  font-weight: 800;
  line-height: 1.02;
  background: linear-gradient(180deg, #FFE9A8, var(--nl-gold) 55%, #FF9B3D);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  filter: drop-shadow(0 0 18px rgba(255, 198, 61, 0.45));
  font-variant-numeric: tabular-nums;
}

/* 三格数据块：图标 + 数值 + 标签，等宽并排、各自一色。 */
.nl-stats {
  display: flex;
  gap: 8px;
}

.nl-stat {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: 8px 4px;
  box-sizing: border-box;
  border-radius: var(--gs-radius-sm);
  background: rgba(11, 7, 30, 0.5);
  box-shadow: inset 0 0 0 1px var(--nl-stat-tone, var(--nl-glass-line));
}

.nl-stat img {
  width: 16px;
  height: 16px;
}

.nl-stat__value {
  font-family: var(--nl-num);
  font-size: clamp(15px, 4.6vw, 22px);
  font-weight: 700;
  color: var(--gs-ink);
  font-variant-numeric: tabular-nums;
}

.nl-stat__label {
  font-size: clamp(9px, 2.6vw, 11px);
  color: var(--gs-ink-dim);
  text-align: center;
  line-height: 1.2;
}

.nl-stat[data-tone="chain"] { --nl-stat-tone: rgba(255, 62, 165, 0.5); }
.nl-stat[data-tone="chain"] img { filter: var(--gs-icon-tint-danger); }
.nl-stat[data-tone="tight"] { --nl-stat-tone: rgba(53, 232, 255, 0.5); }
.nl-stat[data-tone="tight"] img { filter: var(--gs-icon-tint-accent); }
.nl-stat[data-tone="time"] { --nl-stat-tone: rgba(255, 198, 61, 0.5); }
.nl-stat[data-tone="time"] img { filter: var(--gs-icon-tint-gold); }

/* 星轨：**每颗星与它的条件文案同一行**。走查稿把三颗星堆一行、条件文案另起三行，
   于是空心星看不出对应哪一条没达成（screen-08 / 12 / 18 都报了这一条）。 */
.nl-stars {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.nl-star {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: clamp(10px, 3vw, 13px);
  line-height: 1.3;
  /* 未达成那一行的正文色不许低于这个值：两条未达成被画成两种权重，
     读起来会像其中一条更没戏（走查 screen-08 的 gap）。 */
  color: rgba(255, 255, 255, 0.74);
}

.nl-star[data-earned="true"] {
  color: var(--gs-ink);
}

.nl-star img {
  width: 18px;
  height: 18px;
  flex: 0 0 auto;
  filter: var(--gs-icon-tint-ink);
  opacity: 0.45;
}

.nl-star[data-earned="true"] img {
  filter: var(--gs-icon-tint-gold);
  opacity: 1;
}

/* 横条：解锁通知（青绿）与电量失血通知（红）。同一个组件，只换色与图标。 */
.nl-banner {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 10px;
  border-radius: var(--gs-radius-sm);
  background: rgba(11, 7, 30, 0.55);
  box-shadow: inset 0 0 0 1px var(--nl-banner-tone, rgba(84, 240, 184, 0.6));
  font-size: clamp(10px, 3vw, 13px);
  line-height: 1.35;
  color: var(--gs-ink);
}

.nl-banner img {
  width: 16px;
  height: 16px;
  flex: 0 0 auto;
}

.nl-banner__text {
  flex: 1 1 auto;
  min-width: 0;
}

.nl-banner[data-tone="warn"] {
  --nl-banner-tone: rgba(255, 92, 110, 0.65);
  background: rgba(48, 10, 26, 0.55);
}

.nl-banner[data-tone="warn"] img {
  filter: var(--gs-icon-tint-danger);
}

.nl-banner[data-tone="unlock"] img {
  filter: var(--gs-icon-tint-accent);
}

/* 按钮栈：主按钮通栏，两个次按钮等分一行。 */
.nl-actions {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.nl-actions__row {
  display: flex;
  gap: 8px;
}

.nl-actions__row > * {
  flex: 1 1 0;
  min-width: 0;
}

/* 设置开关：整行 ≥44px，左 icon + 标签，右轨道。
   开关态**不只靠颜色**——圆钮位移满行程，关掉颜色也认得出。 */
.nl-toggle {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  min-height: 44px;
  padding: 0 4px;
  box-sizing: border-box;
  border: 0;
  border-bottom: 1px solid var(--nl-glass-line);
  background: transparent;
  color: var(--gs-ink);
  font: inherit;
  font-size: clamp(12px, 3.4vw, 15px);
  text-align: left;
  cursor: pointer;
}

.nl-toggle img {
  width: 18px;
  height: 18px;
  flex: 0 0 auto;
  filter: var(--gs-icon-tint-ink);
  opacity: 0.75;
}

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

.nl-toggle__track {
  flex: 0 0 auto;
  position: relative;
  width: 46px;
  height: 26px;
  border-radius: var(--gs-radius-pill);
  background: rgba(70, 60, 120, 0.7);
  box-shadow: inset 0 0 0 1px var(--nl-glass-line);
  transition: background var(--gs-normal) var(--gs-ease-out);
}

.nl-toggle__track::after {
  content: "";
  position: absolute;
  top: 3px;
  left: 3px;
  width: 20px;
  height: 20px;
  border-radius: var(--gs-radius-pill);
  background: #C7CEEA;
  transition: transform var(--gs-normal) var(--gs-ease-out), background var(--gs-normal) var(--gs-ease-out);
}

.nl-toggle[aria-pressed="true"] .nl-toggle__track {
  background: linear-gradient(90deg, #1E7BFF, var(--nl-cyan));
}

.nl-toggle[aria-pressed="true"] .nl-toggle__track::after {
  transform: translateX(20px);
  background: #06121C;
}

.nl-toggle:focus-visible {
  outline: 2px solid var(--nl-cyan);
  outline-offset: 2px;
}

/* ============================================================
 * 可滚动的 body 分区
 *
 * 帮助页七段、星表八行、本机最佳十行都比分区高。分区是 S2 冻结的几何，
 * **不许为了塞下内容去挪边界**——所以内容滚，边界不动。
 * 下缘一层渐隐替代硬切边，玩家才知道下面还有东西（走查 screen-10 / 12 的 gap）。
 * ============================================================ */

.nl-scroll {
  position: absolute;
  inset: 0;
  overflow-y: auto;
  overflow-x: hidden;
  overscroll-behavior: contain;
  touch-action: pan-y;
  -webkit-overflow-scrolling: touch;
  padding-right: 2px;
  mask-image: linear-gradient(180deg, #000 0, #000 calc(100% - 20px), transparent 100%);
}

.nl-scroll::-webkit-scrollbar {
  width: 4px;
}

.nl-scroll::-webkit-scrollbar-thumb {
  background: rgba(53, 232, 255, 0.4);
  border-radius: 2px;
}

.nl-stack {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

/* ---- 帮助页 ---- */
.nl-section {
  display: flex;
  gap: 10px;
  padding: 10px;
  border-radius: var(--gs-radius-sm);
  background: rgba(11, 7, 30, 0.42);
  box-shadow: inset 0 0 0 1px var(--nl-glass-line);
}

.nl-section img {
  width: 20px;
  height: 20px;
  flex: 0 0 auto;
  margin-top: 2px;
  filter: var(--gs-icon-tint-accent);
}

.nl-section__body {
  flex: 1 1 auto;
  min-width: 0;
}

.nl-section__title {
  margin: 0 0 3px;
  font-size: clamp(12px, 3.4vw, 15px);
  font-weight: 700;
  color: var(--nl-cyan);
}

.nl-section__text {
  margin: 0;
  font-size: clamp(11px, 3.1vw, 14px);
  line-height: 1.45;
  color: var(--gs-ink-soft);
}

/* 「往最空的车道躲」那一段是本作最想教的一条（§6.7 P-C），所以它比别的段重一档。 */
.nl-section[data-emphasis="true"] {
  background: rgba(46, 12, 38, 0.55);
  box-shadow: inset 0 0 0 1px rgba(255, 62, 165, 0.55);
}

.nl-section[data-emphasis="true"] img {
  filter: var(--gs-icon-tint-danger);
}

.nl-section[data-emphasis="true"] .nl-section__title {
  color: var(--nl-magenta);
  font-size: clamp(13px, 3.7vw, 16px);
}

/* 计分表：三条**按分值成比例**的横条。走查稿把三条画成等长，
   一眼看不出紧切更值钱——那正是 SCREEN_SPEC 点名要避免的装饰条。 */
.nl-chart {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.nl-chart__row {
  display: flex;
  align-items: center;
  gap: 8px;
}

.nl-chart__bar {
  position: relative;
  height: 26px;
  border-radius: var(--gs-radius-pill);
  display: flex;
  align-items: center;
  padding: 0 10px;
  box-sizing: border-box;
  font-size: clamp(10px, 2.9vw, 12px);
  font-weight: 600;
  color: #06121C;
  white-space: nowrap;
  overflow: hidden;
}

.nl-chart__row[data-grade="cut"] .nl-chart__bar {
  background: linear-gradient(90deg, #1E7BFF, var(--nl-cyan));
}

.nl-chart__row[data-grade="close"] .nl-chart__bar {
  background: linear-gradient(90deg, var(--nl-magenta), #FF8AC8);
}

.nl-chart__row[data-grade="dual"] .nl-chart__bar {
  background: linear-gradient(90deg, var(--nl-gold), #FFE9A8);
  box-shadow: inset 0 0 0 2px rgba(255, 255, 255, 0.75);
}

/* ============================================================
 * 独立页（路线 / 车库）
 * ============================================================ */

.nl-page-head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 0 var(--nl-pad);
  height: 100%;
  box-sizing: border-box;
}

.nl-page-head__title {
  flex: 1 1 auto;
  min-width: 0;
}

.nl-count {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 5px;
  padding: 5px 10px;
  border-radius: var(--gs-radius-pill);
  background: rgba(11, 7, 30, 0.6);
  box-shadow: inset 0 0 0 1px rgba(255, 198, 61, 0.45);
  font-family: var(--nl-num);
  font-size: clamp(11px, 3vw, 14px);
  font-weight: 700;
}

.nl-count img {
  width: 14px;
  height: 14px;
  filter: var(--gs-icon-tint-gold);
}

/* tab 胶囊。选中 = 亮描边 + 内发光 + 底部短下划线；未选中 = 暗底细描边。 */
.nl-tabs {
  display: flex;
  gap: 8px;
  padding: 0 var(--nl-pad);
  height: 100%;
  align-items: center;
  box-sizing: border-box;
}

.nl-tab {
  flex: 1 1 0;
  min-width: 0;
  min-height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 5px;
  border: 0;
  border-radius: var(--gs-radius-pill);
  background: rgba(11, 7, 30, 0.55);
  box-shadow: inset 0 0 0 1px var(--nl-glass-line);
  color: var(--gs-ink-soft);
  font: inherit;
  font-size: clamp(10px, 3vw, 13px);
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
  overflow: hidden;
}

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

.nl-tab[aria-pressed="true"] {
  color: var(--gs-ink);
  background: linear-gradient(180deg, rgba(53, 232, 255, 0.22), rgba(53, 232, 255, 0.06));
  box-shadow: inset 0 0 0 1px rgba(53, 232, 255, 0.8), 0 0 14px rgba(53, 232, 255, 0.28);
}

.nl-tab[aria-pressed="true"] img {
  filter: var(--gs-icon-tint-accent);
  opacity: 1;
}

.nl-tab:focus-visible {
  outline: 2px solid var(--nl-cyan);
  outline-offset: 2px;
}

/* 卡片网格：路线两列、车漆三列。**锁态复用同一个块**，只降不透明度并把锁徽保持全亮，
   这样「有这么个东西、还没解开」一眼可读（走查 screen-11 / 14 的 impl_notes）。 */
.nl-grid {
  display: grid;
  gap: 8px;
}

.nl-grid[data-cols="2"] { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.nl-grid[data-cols="3"] { grid-template-columns: repeat(3, minmax(0, 1fr)); }

.nl-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-height: 44px;
  padding: 8px;
  box-sizing: border-box;
  border: 0;
  border-radius: var(--gs-radius-md);
  background: linear-gradient(180deg, rgba(38, 26, 86, 0.85), rgba(16, 10, 44, 0.9));
  box-shadow: inset 0 0 0 1px var(--nl-card-tone, var(--nl-glass-line));
  color: var(--gs-ink);
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.nl-card[data-locked="true"] {
  opacity: 0.55;
  cursor: not-allowed;
}

.nl-card[data-selected="true"] {
  --nl-card-tone: rgba(53, 232, 255, 0.85);
  box-shadow: inset 0 0 0 2px rgba(53, 232, 255, 0.85), 0 0 16px rgba(53, 232, 255, 0.3);
}

.nl-card:focus-visible {
  outline: 2px solid var(--nl-cyan);
  outline-offset: 2px;
}

.nl-card__name {
  font-size: clamp(11px, 3.2vw, 14px);
  font-weight: 700;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.nl-card__place {
  font-size: clamp(9px, 2.6vw, 11px);
  color: var(--gs-ink-dim);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.nl-card__best {
  font-family: var(--nl-num);
  font-size: clamp(12px, 3.6vw, 16px);
  font-weight: 700;
  color: var(--nl-gold);
  font-variant-numeric: tabular-nums;
}

.nl-card__hint {
  font-size: clamp(9px, 2.6vw, 11px);
  line-height: 1.25;
  color: var(--gs-ink-dim);
}

/* 车漆卡必须直接展示对应车辆；否则五个选择只剩文字，玩家无法在点选前比较外观。 */
.nl-card[data-kind="paint"] {
  align-items: center;
  text-align: center;
  overflow: hidden;
}

.nl-card__paint {
  display: block;
  width: min(100%, 92px);
  height: 58px;
  object-fit: contain;
  filter: drop-shadow(0 7px 7px rgba(0, 0, 0, 0.48));
}

.nl-card[data-locked="true"] .nl-card__paint {
  filter: grayscale(0.75) brightness(0.62) drop-shadow(0 7px 7px rgba(0, 0, 0, 0.48));
}

/* 卡上的三格星排：**空位必须占位**，否则 3/3 与 1/3 两张卡的基线对不齐。 */
.nl-card__stars {
  display: flex;
  gap: 3px;
}

.nl-card__stars img {
  width: 13px;
  height: 13px;
  filter: var(--gs-icon-tint-ink);
  opacity: 0.32;
}

.nl-card__stars img[data-on="true"] {
  filter: var(--gs-icon-tint-gold);
  opacity: 1;
}

.nl-card__lock {
  position: absolute;
  top: 6px;
  right: 6px;
  width: 16px;
  height: 16px;
  filter: var(--gs-icon-tint-accent);
  /* 锁徽保持全亮度：整张卡压暗之后它是唯一的焦点。 */
  opacity: 1;
}

/* 本机最佳：十行等高胶囊，四列基线对齐、等宽数字避免逐行跳动。 */
.nl-rank {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 10px;
  border-radius: var(--gs-radius-sm);
  background: rgba(11, 7, 30, 0.5);
  box-shadow: inset 0 0 0 1px var(--nl-glass-line);
}

.nl-rank[data-fresh="true"] {
  box-shadow: inset 0 0 0 1px rgba(53, 232, 255, 0.85), 0 0 12px rgba(53, 232, 255, 0.25);
}

.nl-rank__no {
  flex: 0 0 auto;
  width: 26px;
  text-align: center;
  font-family: var(--nl-num);
  font-size: clamp(10px, 2.8vw, 12px);
  color: var(--gs-ink-dim);
}

.nl-rank__score {
  flex: 0 0 auto;
  min-width: 5ch;
  text-align: right;
  font-family: var(--nl-num);
  font-size: clamp(14px, 4.2vw, 19px);
  font-weight: 700;
  color: var(--nl-gold);
  font-variant-numeric: tabular-nums;
}

.nl-rank__route {
  flex: 1 1 auto;
  min-width: 0;
  font-size: clamp(10px, 2.9vw, 12px);
  color: var(--gs-ink-soft);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.nl-rank__badge {
  flex: 0 0 auto;
  padding: 2px 7px;
  border-radius: var(--gs-radius-pill);
  background: rgba(53, 232, 255, 0.18);
  box-shadow: inset 0 0 0 1px rgba(53, 232, 255, 0.7);
  font-size: clamp(9px, 2.4vw, 10px);
  font-weight: 700;
  color: var(--nl-cyan);
}

.nl-note {
  display: flex;
  gap: 8px;
  padding: 10px;
  border-radius: var(--gs-radius-sm);
  background: rgba(11, 7, 30, 0.42);
  box-shadow: inset 0 0 0 1px var(--nl-glass-line);
  font-size: clamp(10px, 2.8vw, 12px);
  line-height: 1.4;
  color: var(--gs-ink-dim);
}

.nl-note img {
  width: 16px;
  height: 16px;
  flex: 0 0 auto;
  filter: var(--gs-icon-tint-ink);
  opacity: 0.6;
}

/* 底部铭牌：星表那一屏的 summary 脱离滚动流钉在分区底，滚多长都在。 */
.nl-summary {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  padding: 10px var(--nl-pad);
  box-sizing: border-box;
  text-align: center;
  border-radius: var(--gs-radius-md) var(--gs-radius-md) 0 0;
  background: linear-gradient(180deg, rgba(12, 7, 36, 0.5), rgba(12, 7, 36, 0.95) 40%);
}

.nl-summary__value {
  font-family: var(--nl-num);
  font-size: clamp(20px, 6vw, 30px);
  font-weight: 800;
  color: var(--nl-gold);
  text-shadow: var(--gs-glow-gold);
}

.nl-summary__hint {
  margin: 2px 0 0;
  font-size: clamp(10px, 2.8vw, 12px);
  color: var(--gs-ink-soft);
}

/* ============================================================
 * 品牌区（加载页 / 标题页）
 * ============================================================ */

.nl-brand {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  height: 100%;
  padding: 0 var(--nl-pad);
  box-sizing: border-box;
  text-align: center;
}

.nl-brand__logo {
  width: clamp(56px, 20vw, 96px);
  height: auto;
  filter: drop-shadow(0 0 18px rgba(176, 108, 255, 0.55));
}

.nl-brand__word {
  margin: 0;
  font-size: clamp(34px, 11vw, 58px);
  font-weight: 800;
  letter-spacing: 0.01em;
  line-height: 1;
  color: #fff;
  text-shadow:
    0 0 10px rgba(53, 232, 255, 0.85),
    0 0 30px rgba(53, 232, 255, 0.45),
    0 0 60px rgba(176, 108, 255, 0.35);
}

.nl-brand__tag {
  margin: 0;
  font-size: clamp(11px, 3.2vw, 15px);
  color: var(--gs-ink-soft);
}

.nl-progress {
  display: flex;
  flex-direction: column;
  gap: 6px;
  justify-content: center;
  height: 100%;
  padding: 0 var(--nl-pad);
  box-sizing: border-box;
}

.nl-progress__line {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
  font-size: clamp(10px, 2.8vw, 12px);
  color: var(--gs-ink-dim);
}

.nl-progress__pct {
  font-family: var(--nl-num);
  font-size: clamp(13px, 4vw, 18px);
  font-weight: 700;
  color: var(--nl-cyan);
  font-variant-numeric: tabular-nums;
}

.nl-progress__version {
  text-align: center;
  font-size: clamp(9px, 2.5vw, 11px);
  color: var(--gs-ink-dim);
}

/* ---- 分享卡预览 ---- */
.nl-cardview {
  width: 100%;
  border-radius: var(--gs-radius-md);
  box-shadow: inset 0 0 0 1px rgba(53, 232, 255, 0.5), var(--gs-shadow-card);
  display: block;
}

.nl-toast {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 100%;
  margin-bottom: 6px;
  padding: 7px 12px;
  border-radius: var(--gs-radius-pill);
  background: rgba(11, 7, 30, 0.92);
  box-shadow: inset 0 0 0 1px rgba(53, 232, 255, 0.7);
  text-align: center;
  font-size: clamp(10px, 2.9vw, 12px);
  color: var(--nl-cyan);
}

/* ---- 错误屏的诊断行 ---- */
.nl-diag {
  display: flex;
  gap: 8px;
  padding: 9px 10px;
  border-radius: var(--gs-radius-sm);
  background: rgba(44, 12, 16, 0.5);
  box-shadow: inset 0 0 0 1px rgba(255, 122, 92, 0.55);
  font-size: clamp(10px, 2.9vw, 13px);
  line-height: 1.4;
  color: var(--gs-ink-soft);
}

.nl-diag img {
  width: 16px;
  height: 16px;
  flex: 0 0 auto;
  margin-top: 1px;
  filter: var(--gs-icon-tint-danger);
}

.nl-alert-icon {
  width: 34px;
  height: 34px;
  filter: var(--gs-icon-tint-danger);
}

/* ---- 车漆预览下的说明 ---- */
.nl-preview {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  gap: 3px;
  height: 100%;
  padding: 0 var(--nl-pad) 4px;
  box-sizing: border-box;
  text-align: center;
}

.nl-preview__name {
  font-size: clamp(14px, 4.2vw, 19px);
  font-weight: 700;
  color: var(--nl-gold);
}

.nl-preview__note {
  margin: 0;
  font-size: clamp(9px, 2.6vw, 11px);
  line-height: 1.35;
  color: var(--gs-ink-dim);
}

/* ============================================================
 * 矮画布 —— 手机横过来，以及**站点在手机上交给游戏的那块框**。
 *
 * 站点在 375×667 的手机上给游戏 342×403（`shared/site-stage.json`：
 * 视口高减头部 56、底部标签栏 58、舞台预留 150）。它是**竖的**（342 < 403），
 * 所以走的是竖屏分区，可是只有 403 px 高——`SCREEN_SPEC` 的分区是按 9:16
 * 定的比例，同一个 0.09 在 1920 的设计画布上是 173 px，在这里只有 36 px。
 * 玩家在站点上看到的就是这一档，所以它必须成立。
 *
 * 这一档只做两件事，**一个分区边界都不动**：
 *   ① 把排版收紧、把动作区从竖排改成横排（同一批控件换个折行点，不是第二套设计）；
 *   ② 收紧之后仍装不下的分区，让**内容自己滚**——与 `.nl-scroll` 是同一条规矩
 *      （"边界不动，内容滚"），因为 44×44 的触控下限是平台基线，不许为了塞进
 *      36 px 的分区去破。
 *
 * 它**不新增组件、不改信息层级、不换配色**。
 * 560px 来自 shared/phone-canvas.json，所有游戏同一个数。
 * ============================================================ */

@media (max-height: 560px) {
  :root {
    --nl-pad: clamp(6px, 2.2vw, 12px);
    --nl-pad-y: 3px;
    /* 组件库只改**变量**，不覆盖 `.gs-*` 选择器（那会把它的可用性一起改坏）。
       收的是字号与左右内边距，`--gs-tap-min` 一个像素没动——44×44 是平台基线。 */
    --gs-text-md: 13px;
    --gs-text-sm: 12px;
    --gs-space-3: 8px;
    --gs-space-5: 12px;
  }

  /* 收紧之后还是装不下的，分区自己滚。**游玩屏除外**：它那三块分区里躺着
     按压面，一旦吃指针就把 §2.1 的唯一操作挡掉了。 */
  .nl-zone:not([data-zone-screen="play"]) {
    overflow-y: auto;
    overscroll-behavior: contain;
    touch-action: pan-y;
    pointer-events: auto;
    scrollbar-width: none;
  }

  .nl-zone:not([data-zone-screen="play"])::-webkit-scrollbar {
    width: 0;
    height: 0;
  }

  /* ---- 浮层抬头：ghost 关闭键恒为 44×44，收的是它周围的留白 ---- */
  .nl-head {
    align-items: center;
    gap: 6px;
  }

  .nl-head--overlay {
    padding: 2px 8px;
  }

  .nl-title {
    font-size: clamp(15px, 4.4vw, 20px);
  }

  .nl-sub {
    margin-top: 2px;
    font-size: clamp(10px, 2.8vw, 12px);
  }

  /* ---- 品牌块：徽标与字号各收一档 ---- */
  .nl-brand {
    gap: 4px;
  }

  .nl-brand__logo {
    width: clamp(28px, 11vh, 52px);
    height: auto;
  }

  .nl-brand__word {
    font-size: clamp(20px, 7.5vh, 34px);
  }

  .nl-brand__tag {
    font-size: clamp(10px, 2.9vw, 12px);
  }

  /* ---- 数据格：竖排三行改成横排一行 ---- */
  .nl-stat {
    flex-direction: row;
    align-items: center;
    justify-content: center;
    gap: 5px;
    padding: 3px 5px;
  }

  .nl-stat__value {
    font-size: clamp(13px, 3.8vw, 17px);
  }

  .nl-stat__label {
    font-size: clamp(9px, 2.4vw, 10px);
  }

  /* ---- 动作区：主按钮 + 次按钮**同一行**。控件一个不少，只是换了折行点 ---- */
  .nl-actions {
    flex-direction: row;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: 6px;
  }

  /* 折行点由**最小宽度**定，不由「有几个」定：挤不下就换行，
     绝不把按钮压到 44px 以下（站点框 342 宽时实测会压到 37px）。 */
  .nl-actions > .gs-btn {
    flex: 1 1 30%;
    min-width: 30%;
  }

  .nl-actions > .nl-actions__row {
    flex: 1 1 55%;
    min-width: 55%;
    gap: 6px;
  }

  .nl-actions__row > * {
    min-width: var(--gs-tap-min);
  }

  /* 语言行是一整行的说明文字，不参与折行。 */
  .nl-actions > .nl-progress__version {
    flex: 1 0 100%;
    margin: 0;
    font-size: 10px;
    line-height: 1.2;
  }

  /* ---- 每日路线卡：三行文字与 Drive 键并排，卡自己不留大留白 ---- */
  .nl-zone[data-zone="feature"] .nl-card {
    padding: 3px 6px;
  }
}

/* ============================================================
 * 手机横屏 —— **手机的一个朝向，不是桌面。**
 * 560px 来自 shared/phone-canvas.json，所有游戏同一个数。
 * 这一档只做三件事：收紧纵向留白、把大数字收一档、让两栏结算排得开。
 * 它**不新增任何组件、不改信息层级、不换配色**——那就成第二套设计了。
 * ============================================================ */

@media (orientation: landscape) and (max-height: 560px) {
  :root {
    --nl-pad-y: 3px;
  }

  .nl-hud {
    /* 横屏的 hud 是中间那 20% 的竖列：它够不着，所以只放读数与暂停键。
       读数画在左右两列的上沿由 JS 定位，这里只把容器改成竖排。 */
    flex-direction: column;
    justify-content: flex-start;
    border-radius: 0;
    border-bottom: 0;
    background: transparent;
    box-shadow: none;
  }

  .nl-hud__top,
  .nl-hud__bottom {
    flex-direction: column;
    align-items: center;
    gap: 4px;
  }

  .nl-value {
    font-size: clamp(18px, 4.4vh, 26px);
  }

  .nl-hero__value {
    /* 横屏缺的是**高**，所以大数字反而要收——不收就会顶破分区。 */
    font-size: clamp(30px, 12vh, 46px);
  }

  /* SCREEN_SPEC screen-17 的 `pip-grid-4x2`：横屏把 8 格折成 4×2。
     侧列上沿放得下一整行，但折成两行之后每一格更宽，扫一眼就数得出还剩几段。 */
  .nl-pips {
    flex-wrap: wrap;
    gap: 3px 4px;
  }

  .nl-pips__pip {
    flex: 0 0 calc(25% - 3px);
  }

  /* 横屏的浮层是宽而矮的一条，三格数据竖排读起来更顺。
     **标题页除外**：它的 `stats` 分区只有画布高的 16%，竖排当场溢出。 */
  .nl-zone:not([data-zone-screen="title"]) .nl-stats {
    flex-direction: column;
  }

  .nl-stat {
    flex-direction: row;
    justify-content: space-between;
    gap: 8px;
  }

  .nl-section {
    padding: 7px 9px;
  }

  .nl-grid[data-cols="3"] {
    /* 横屏的 play 分区窄而高（72% 宽里再分栏），三列会把卡挤到 60px 以下——
       文字全部截断。收成两列不是另一套版式，是同一批卡换个折行点。 */
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

/* ============================================================
 * 减少动态：**只去掉「动」，一个判定都不改**（§10.5）。
 * 唯一的例外写在 §10.5 末尾：箭头虚线的颜色变化属于**信息**不属于动，
 * 减少动态下照常变色（那一条在 render.mjs 里，不在这里）。
 * ============================================================ */

@media (prefers-reduced-motion: reduce) {
  .nl-charge[data-level="warn"],
  .nl-charge[data-level="low"] {
    animation: none;
  }

  .nl-value[data-bump="true"] {
    animation: none;
  }
}

html[data-reduced-motion="true"] .nl-charge[data-level="warn"],
html[data-reduced-motion="true"] .nl-charge[data-level="low"],
html[data-reduced-motion="true"] .nl-value[data-bump="true"] {
  animation: none;
}

html[data-reduced-motion="true"] .nl-press::after,
html[data-reduced-motion="true"] .nl-charge__fill,
html[data-reduced-motion="true"] .nl-charge__overfill,
html[data-reduced-motion="true"] .nl-toggle__track::after {
  transition: none;
}
