/* ==========================================================================
 * 235导航 · 专项游戏页共享骨架样式（tools/games/<id>.html）
 * ==========================================================================
 * 用途：所有「专项游戏页」共用的一套正文版式。一页 = 面包屑 → 主视觉 → 快捷入口 →
 *   本站工具 → 攻略资讯（含社区论坛）→ 实用外链工具 → 社群二维码 → 相关游戏 → 免责脚注。
 *
 * 为什么单独一份文件：
 *   以前 tools/games/nsh.html 把这段排布内联在自己的 <style> 里。一旦加第二个游戏，
 *   同一套骨架就会被抄成 N 份 —— 正是 V1.53 抽 css/nsh-theme.css 时踩过的坑
 *   （同品牌两套实现 → 必然漂移）。所以骨架抽成共享文件，**每页只剩自己的数据**。
 *
 * 引入方式（顺序不能变，子主题要能覆盖主站令牌）：
 *   <link rel="stylesheet" href="../../css/style.css">      ← 主站唯一样式表（含外壳）
 *   <link rel="stylesheet" href="../../css/game-page.css">  ← 本文件：游戏页骨架
 *   <link rel="stylesheet" href="../../css/<id>-theme.css"> ← 该游戏的子主题（可选）
 *
 * ⚠️ 本文件**不重复定义** style.css 里已有的类，只调用它们：
 *   .view--hub / .game-tools / .hub-card / .hub-card__* / .hub-foot / .hub-item*
 *   （改这些去 css/style.css，改本文件对它们无效）
 * ⚠️ 本文件**只消费主站令牌**（--text / --border / --accent / --radius-card …），
 *   不声明任何主色板，避免和 style.css 打架；每个游戏的主题色由自己的子主题文件
 *   在 `.game-page` 子树内做令牌重映射（见文件末尾说明）。
 *
 * ⚠️ 文字一般**不要用 --text-tertiary**：亮色下它是 #94A3B8，压任何浅底都只有 2.4–2.7:1
 *   （12px 小字需 4.5）。style.css 已为「导航副标题」踩过这个坑并留下判据
 *   （见 css/style.css 令牌注释），但本文件当时没跟着改 —— V1.84 一次性把这里 6 处
 *   color: var(--text-tertiary) 全收敛到 --text-secondary（亮 4.55:1 / 暗 6.38:1）。
 *   要「更弱」的层级请调字号或字重，不要再退回 tertiary。
 * ⚠️ 页面由 scripts/gen-game-pages.js 从 data/games-hub.json 生成，**不要手改生成页**。
 * ========================================================================== */

/* ---------- 容器 ---------- */
/* ⚠️ `width:100%` 不能省：.view--hub 是 flex 容器，本容器作为 flex 项默认按内容收缩
   （实测同一份 max-width:1080 会因内容不同量到 925 / 1046 两个宽度 → 页与页之间的正文
    宽度不一致，从图标墙点进来会有轻微横向跳动）。写死 100% + max-width 后，
    所有游戏页恒为 min(--content-max, 可用宽)，与内容无关。
   V1.68（待办 H）：原写死 1080px，比工具页 1240 / 工具库 1120 都窄，且 **居中**
   （1440 下 x=261，左右各空 85px）—— 从工具库点进来是肉眼可见的缩+跳。
   改引用 --content-max，与全站其余内容区同宽同左边距。 */
.game-page {
  width: 100%;
  max-width: var(--content-max);
  margin: 0 auto;
}

/* ---------- 面包屑 ---------- */
.game-breadcrumb {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
  margin: 0 0 4px;
  font-size: 12px;
  color: var(--text-secondary);
}
.game-breadcrumb a {
  color: var(--text-secondary);
  text-decoration: none;
}
.game-breadcrumb a:hover { color: var(--accent-text); }
.game-breadcrumb [aria-current="page"] { color: var(--text-muted); font-weight: 600; }

/* ---------- 主视觉（图标 + 名称 + 一句话定位 + 官网按钮） ---------- */
/* 图标沿用图标墙的 squircle 规格，让「卡片 → 本页」视觉连续，不会有换页断裂感 */
.game-hero {
  display: flex;
  align-items: center;
  flex-wrap: wrap;          /* ⚠️ 必须：主视觉「是否换行」靠 flex-basis 判据（见下方 .game-hero__meta） */
  gap: 18px;
  padding: 12px 0 22px;
}
.game-hero__icon {
  flex: none;
  width: 88px;
  height: 88px;
  border-radius: 22%;
  overflow: hidden;
  border: 1px solid var(--border);
  box-shadow: var(--shadow-sm);
  background: var(--bg-subtle);
}
.game-hero__icon img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* ⚠️ flex-basis 故意给 260px（不是 0）—— 这是「主视觉是否换行」的唯一判据：
   换行由各项的 hypothetical main size（= flex-basis）之和决定，与收缩后的实际宽度无关。
   88(图标)+18+260(标题)+18+257(按钮区) = 641px：
     内容列 ≥641px → 图标/标题/按钮同行；<641px → 按钮区自动落到第二行。
   ⚠️ 不要改回 `flex: 1`（basis 0）：basis=0 时标题能被压到 0 宽、永远「塞得下」，
      于是 flex-wrap 永不触发，标题会被挤成「一字一行」（窄屏真机实测踩过）。
   注意判据是「内容列宽度」而不是视口宽度 —— 外壳在 901~1100px 会给右侧预留一条
   空的 300px 栅格列（各页通有），此时内容列远小于视口，按视口做断点会漏判。 */
.game-hero__meta { min-width: 0; flex: 1 1 260px; }
.game-hero__name {
  margin: 0 0 6px;
  font-size: 22px;
  font-weight: 700;
  line-height: 1.3;
  color: var(--text);
  text-wrap: balance;
}
.game-hero__desc {
  margin: 0;
  font-size: 13px;
  line-height: 1.7;
  color: var(--text-secondary);
}
/* 按钮区：官网 +（可选的）官方交易平台。用 flex 容器而不是直接塞进 .game-hero，
   是为了「无官方交易平台的游戏」只渲染 1 个按钮时，排版与 2 个按钮时完全一致（不靠 :last-child 特判）。 */
.game-hero__actions {
  flex: none;
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 8px;
  max-width: 260px;
}
.game-hero__action {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 8px 16px;
  border-radius: var(--radius-btn);
  background: var(--accent);
  color: var(--accent-fg);
  font-size: 13px;
  font-weight: 700;
  text-decoration: none;
  box-shadow: var(--shadow-sm);
  transition: box-shadow .16s ease, transform .12s ease;
}
.game-hero__action:hover { box-shadow: var(--shadow-md); transform: translateY(-1px); }
.game-hero__action:active { transform: translateY(0) scale(.97); }
/* 次级按钮：描边式（官方交易平台走这个）—— 与主按钮同尺寸同圆角，
   只换填充，保证两个按钮「一眼是同一组」，不抢主按钮的视觉权重。 */
.game-hero__action--alt {
  background: var(--bg);
  color: var(--accent-text);
  border: 1px solid var(--border-strong);
  box-shadow: none;
}
.game-hero__action--alt:hover {
  background: var(--accent-soft);
  border-color: var(--accent);
  box-shadow: var(--shadow-sm);
}

/* ---------- 主视觉 · 横幅背景变体（可选；JSON 给了 `heroBg` 才产出） ---------- */
/* 结构（z 序）：`<img class="game-hero__bg">`(z0 铺底) → `::after` 遮罩(z1) → `::before` 光晕(z2)
   → 图标/文字/按钮(z2，与光晕同层、但**树序在后**，所以压在光晕之上)。
   五条硬约束：
   ① **文字色必须在本变体内强制浅色** —— 照片明暗不可控，不能沿用主站 `--text`
      （浅色主题下它是深字，压在深色照片上必瞎）。遮罩用「左重右轻」的多层渐变：
      标题/描述在左、按钮在右，两侧都得能读。
   ② ⚠️ **横向内边距会抬高换行判据阈值**：`.game-hero` 的判据是「各项 flex-basis 之和 641px
      对比『内容列内宽』」（推导见 .game-hero__meta）。本变体加了 26px×2 的横向内边距，
      内宽少了 52px → 实际断点从 **641px 抬到 693px**（按容器外宽算）。
      **改这里的横向内边距，必须同步改 693 这个数**，否则会在 641~693px 区间出现
      「以为不换行、实际换行」的误判。
   ③ **次级按钮不能用 `--bg` 填色**：深色主题下 `--bg` 是深色，压在照片上「深底+深字」。
      本变体内单独覆盖为半透明白底 + 白字，两个主题都成立。
   ④ **深浅主题共用同一套遮罩**（照片是同一张）：不做主题分支，否则两套遮罩会各自漂移。
   ⑤ **光晕（`::before`）的色值必须取 `var(--accent)`，透明度走 `opacity`**（V1.79 加）：
      不能写死 `rgba(245,158,11,…)` —— `--accent` 会被各游戏子主题在 `.game-page` 子树内重映射
      （见 css/nsh-theme.css），写死琥珀色会让换了主题的页面露出一块主站色、与子主题打架。
      渐变色标又没法直接降 alpha：全站未用 `color-mix`，而一旦浏览器不支持，**整条 `background-image`
      会连深色遮罩一起失效** → 照片直接失去可读性，那是最坏的降级形态。
      所以取「实心 `var(--accent)` + `mask-image` 径向遮罩 + `opacity`」：三个属性各自独立生效，
      `mask` 不支持时最多多一层 10% 的淡琥珀，**不会出现硬边色块**。
   ⚠️ **背景为什么是 `<img>` 而不是 CSS 背景图**（2026-09-22 实测踩坑）：
      写在 CSS 自定义属性里的**相对 URL 按「使用它的样式表」基准解析**，而 `<img src>` 按**文档**基准解析。
      本页在 `tools/games/`、样式表在 `css/`，两者上溯两级在**站点根目录部署**时**巧合得到同一路径**，
      所以在 http 下一切正常；但本地 `file://` 预览（`235/tools/games/` 退两级 = `Desktop/`）会错位到上一级目录，
      图 404 → 整块只剩兜底色，看起来就是「**一块黑**」。子目录部署同样会错位。
      所以横幅一律走 `<img>`：任何协议、任何浏览器、任何部署位置都按文档基准解析。
   ⚠️ `background` 这里**只留兜底渐变**（图 404 时是一块「有设计感的深色板」而不是黑洞），
      真正的照片由 `<img>` 提供。 */
.game-hero--bg {
  position: relative;
  padding: 26px;
  min-height: 232px;
  border-radius: var(--radius-card);
  overflow: hidden;
  background-color: #141a22;
  background-image: linear-gradient(135deg, #1d2632 0%, #141b24 55%, #101720 100%);
  box-shadow: var(--shadow-sm);
}
.game-hero--bg .game-hero__bg {
  position: absolute;
  inset: 0;
  z-index: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
}
.game-hero--bg::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 1;
  background-image:
    linear-gradient(90deg, rgba(9,12,17,.88) 0%, rgba(9,12,17,.74) 38%, rgba(9,12,17,.42) 70%, rgba(9,12,17,.60) 100%),
    linear-gradient(180deg, rgba(9,12,17,.18) 0%, rgba(9,12,17,.04) 45%, rgba(9,12,17,.46) 100%);
}
/* 光晕：一团取 `var(--accent)` 的柔光，压在暗遮罩之上、内容之下（z2，且**树序在子元素之前**）。
   位置刻意放在**右上**：那里是横向遮罩最亮的一侧（70% 处只有 .42），光晕落在这儿才看得出来；
   同时避开左侧的标题/描述（遮罩 .74~.88 最重、字最需要对比度）。
   ⚠️ `pointer-events: none` 不能省 —— 它是 `inset: 0` 的整幅层，否则会吃掉整块 hero 与照片的点击。 */
.game-hero--bg::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  background: var(--accent);
  opacity: .10;
  -webkit-mask-image: radial-gradient(58% 120% at 88% 6%, #000 0%, rgba(0, 0, 0, .5) 44%, transparent 74%);
  mask-image: radial-gradient(58% 120% at 88% 6%, #000 0%, rgba(0, 0, 0, .5) 44%, transparent 74%);
}
/* 抬到遮罩之上。⚠️ 必须放在 .game-hero__bg 规则之后由特异性决定：
   `.game-hero--bg .game-hero__bg`(0,2,0) 高于 `.game-hero--bg > *`(0,1,0)，横幅图不会被误抬。 */
.game-hero--bg > * { position: relative; z-index: 2; }
.game-hero--bg .game-hero__icon { border-color: rgba(255,255,255,.26); box-shadow: 0 2px 10px rgba(0,0,0,.45); }
.game-hero--bg .game-hero__name { color: #fff; }
.game-hero--bg .game-hero__desc { color: rgba(255,255,255,.84); }
.game-hero--bg .game-hero__action--alt {
  background: rgba(255,255,255,.14);
  color: #fff;
  border-color: rgba(255,255,255,.46);
}
.game-hero--bg .game-hero__action--alt:hover {
  background: rgba(255,255,255,.24);
  border-color: rgba(255,255,255,.74);
  box-shadow: var(--shadow-sm);
}

/* ---------- 快捷入口（胶囊） ---------- */
/* ⚠️ 只给**横幅变体之后**的那组补上间距：`.game-hero` 基础形态自带 `padding:12px 0 22px`，
   本身就留出了 22px；而 `.game-hero--bg` 是带底图与圆角的**卡片**，其 padding 收在卡片内，
   卡片底边与胶囊之间会变成 0 间距（用户反馈「两个按钮与横幅贴上了」）。用相邻兄弟选择器
   精准补 14px，基础形态不受影响、零回归。 */
.game-hero--bg + .game-links, .game-hero--bg + .game-quick { margin-top: 14px; }

/* ---------- 快捷入口 · 分组形态（V1.78） ----------
   什么时候用：同类入口按阵营/服务器成对出现（国服 / 国际服）。
   平铺时「国服官网 / 国际服官网 / 国服论坛 / 国际服论坛…」交错排列、文字高度相似，扫读困难
   （用户反馈：「官方类标太多，文字相近有点乱」）。
   做法：一行一组，行首一个纯文字标签（不可点、不是胶囊），组内胶囊**完全复用**
   `.game-links__item` —— 本块只负责「分行 + 标签对齐」，不重定义胶囊外观。 */
.game-quick { display: flex; flex-direction: column; gap: 8px; margin-bottom: 8px; }
.game-quick__row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
/* 组标签（V1.79 起加了左侧琥珀竖条）—— 与 `.game-group__title` 的竖条是**同一套视觉语汇**：
   本站凡「分组」一律「左侧 3px 强调色竖条 + 加粗小字」，section 级与组内级因此一眼看得出同族。
   ⚠️ 刻意**不给背景色**：带底色会被读成胶囊，而本页胶囊一律是可点入口
      （`.game-quick__label` 是 `<span>`，不可点）。竖条只承担「这里是一组」的语义。
   ⚠️ min-width 用 em 而不是 px：标签是「国服」(2 字) 与「国际服」(3 字) 两种长度，
      必须让两行胶囊的左边缘**对齐**；写死 px 会在改字号时错位。
      算式：竖条 3 + 间隔 7 + 最长标签「国际服」3 字 = 10px + 3em；字号 12px 时 3em = 36px，
      合计 46px，折成 em 即 3.9em。
      ⚠️ 改竖条宽度 / 间隔 / 字号，都要重算这个数（与横幅变体里 641→693 那类阈值同理）。 */
.game-quick__label {
  flex: none;
  min-width: 3.9em;
  font-size: 12px;
  font-weight: 700;
  color: var(--text-secondary);
  letter-spacing: .02em;
}
.game-quick__label::before {
  content: '';
  display: inline-block;
  width: 3px;
  height: 11px;
  margin-right: 7px;
  border-radius: 2px;
  background: var(--accent);
  vertical-align: -1px;
}
/* 组内 nav 自己不再占底部间距（间距由 `.game-quick` 的 gap 统一给） */
.game-quick .game-links { margin-bottom: 0; }
/* 窄屏：标签独占一行，胶囊整行铺开 —— 否则标签会把首行胶囊挤掉一个 */
@media (max-width: 640px) {
  .game-quick__row { align-items: flex-start; }
  .game-quick__label { min-width: 0; width: 100%; }
}
.game-links {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 8px;
}
.game-links__item {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 14px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--card);
  color: var(--text-muted);
  font-size: 12.5px;
  font-weight: 600;
  text-decoration: none;
  transition: color .14s ease, border-color .14s ease, background .14s ease;
}
/* 胶囊内的功能图标槽（生成器的 `uiIcon()`，内联 SVG / 14px / currentColor）。
   ⚠️ 只做「图标槽」，**不加箭头** —— 胶囊形态本身已表达可点击（见 TOPICS.md T13 硬规则 4）。 */
.game-links__icon { flex: none; display: inline-flex; align-items: center; justify-content: center; }
.game-links__icon svg { display: block; }
.game-links__item:hover {
  color: var(--accent-text);
  border-color: var(--accent);
  background: var(--accent-soft);
}

/* ---------- 分组（标题左侧竖条） ---------- */
.game-group { margin-top: 24px; }
.game-group__title {
  margin: 0 0 12px;
  padding-left: 12px;
  border-left: 3px solid var(--accent);
  font-size: 14px;
  font-weight: 700;
  color: var(--text);
  text-wrap: balance;
}
.game-group__sub {
  margin-left: 8px;
  font-size: 12px;
  font-weight: 400;
  color: var(--text-secondary);
}

/* ---------- 外链卡片列表（攻略资讯 / 社区论坛：两列） ---------- */
.link-list {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px;
}
.link-list__item {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 10px;
  padding: 14px 16px;
  min-height: 96px;
  border-radius: var(--radius-card);
  border: 1px solid var(--border);
  background: var(--card);
  text-decoration: none;
  transition: border-color .16s ease, box-shadow .16s ease, transform .12s ease, background .16s ease;
}
a.link-list__item:hover {
  border-color: var(--accent);
  box-shadow: var(--shadow-sm);
  transform: translateY(-1px);
  background: var(--bg-hover);
}
.link-list__meta { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 5px; }
.link-list__name {
  font-size: 13.5px;
  font-weight: 600;
  line-height: 1.4;
  color: var(--text);
}
.link-list__desc {
  font-size: 12px;
  line-height: 1.6;
  /* ⚠️ 这里原来用 --text-tertiary（亮 #94A3B8 / 暗 #64748B），两个主题都**不达标**：
     亮色压 --surface-1 #F8FAFC 只有 2.45:1，暗色压 #131C2E 只有 3.58:1（12px 小字需 4.5）。
     css/style.css 早就为「导航副标题」踩过同一个坑并留下了判据
     （见 style.css 令牌注释：「未选中副标题 #64748B on #F8FAFC = 4.55:1（原 --text-tertiary #94A3B8
     仅 2.45:1，不达标）」）—— 但这条规则当时没跟着改。
     改用 --text-secondary：亮 4.55:1 / 暗 6.64:1。整站游戏页共用本文件，改一次三个页都受益。 */
  color: var(--text-secondary);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
/* 右侧「站外链接」图标：内联 SVG（跨行文本里放字符 ↗ 会因基线对齐抖动，且各家字体字形差异大）。
   描边图形 + currentColor → 深浅主题自动跟随；hover 时变琥珀，与卡片描边同色，形成「整卡可点」的暗示。 */
.link-list__arrow {
  margin-left: auto;
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--text-secondary);
  transition: color .16s ease;
}
.link-list__arrow svg { display: block; }
a.link-list__item:hover .link-list__arrow { color: var(--accent-text); }
/* 左侧「来源站点图标」槽：显示攻略站/社区**自己的**图标（本地图片）。
   ⚠️ 路径一律站内 —— 绝不热链第三方 favicon 服务（既违零外部依赖，也会把访客 IP 泄给第三方）。
   ⚠️ **尺寸在 `.game-page` 的 `--site-icon-size` 上，只改那一处**（生成器写进 `<img width/height>`
      的值是 `SITE_ICON_PX`，**两者必须同步**，否则图未加载时会有一帧尺寸跳变）。
      定 32px 的依据：卡片 `min-height:96px` − `padding:14px`×2 = 内容区 66px，
      两行文字块实测 43px（窄屏 62px）——**20px 的 favicon 明显压不住，32px 才与文字块等量齐观**。
      原生 favicon 只有 16~48px，放到 32px 必糊，所以素材要另供 64×64（见各文件名旁的来源注释）。
   ⚠️ 缺图标时生成器**不产出该 span**，卡片退化成纯文字、不留空洞。 */
.link-list__icon {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--site-icon-size, 32px);
  height: var(--site-icon-size, 32px);
  /* 浅圆角 + 裁剪：让「满幅方形 logo」与「带留白的 logo」在同一列表里观感一致；
     圆形/透明底的图标裁不到内容，等同无操作。 */
  border-radius: 6px;
  overflow: hidden;
}
.link-list__icon img { width: 100%; height: 100%; object-fit: contain; display: block; }

/* ---------- 实用外链工具 · 组内小组标签（V1.79） ----------
   `extTools[].group` 的产物（生成器见 scripts/gen-game-pages.js §3 ⑥）。
   工具一多，同规格卡片平铺就是一面墙；加了组标签才有层级可扫读。

   ⚠️ **必须 `grid-column: 1 / -1`**：`.game-tools` 是 auto-fill 网格
      （style.css：`repeat(auto-fill, minmax(268px, 1fr))`），标签也是网格项 ——
      不跨列就会被当成一个 268px 的格子、与卡片并排坐，看起来像一张没写完的卡。
   ⚠️ 用 `<h3>` 而不是 `<div>`（语义 + 屏幕阅读器可跳读）：它确实是 h2「实用外链工具」
      下的一级分组。页内 `<h3>` 因此有**两个合法出处**（正文层小节 + 本标签），
      scripts/gen-game-pages.js 的 h3 断言按这两处之和校验。
   ⚠️ 样式**故意比 `.game-group__title` 低一级**：字号更小、颜色更淡、无竖条 ——
      section 级标题（h2）与组内标签（h3）之间必须有明确主次，否则整页读起来全是同级标题。 */
.game-tools__group {
  grid-column: 1 / -1;
  margin: 0;
  font-size: 12px;
  font-weight: 700;
  color: var(--text-secondary);
  letter-spacing: .02em;
}
/* 组与组之间多留一点缝：`.game-tools` 的 gap 是 12px，卡片↔卡片也是 12px，
   「上一组最后一张卡 → 下一组标签」若同样 12px，组边界就看不出来了。
   ⚠️ 用 `:not(:first-child)` 排除首条 —— 第一条标签上方由网格 gap 提供即可，不必再加。 */
.game-tools__group:not(:first-child) { margin-top: 6px; }

/* ---------- 「第三方」角标：扩展 style.css 的 .hub-card__badge（只加变体，不重写基类） ----------
   语义色用危险红，表达「不是我方内容」。这不是盈亏红绿，纯粹是「注意第三方」的提示色。
   ⚠️ 与之配对的「可用」绿角标 `.hub-card__badge--ok` 已于 2026-09-22 按用户要求删除
      （本站自研工具**全部可用**，那个角标零信息量）；只剩本文件这一个 badge 变体在用。 */
.game-page {
  --game-ext-text: #B91C1C;
  --game-ext-soft: #FEF2F2;
  --game-ext-border: rgba(185, 28, 28, .25);
  /* 攻略/社区卡的来源站点图标尺寸（**唯一源**）。
     ⚠️ 与 `scripts/gen-game-pages.js` 的 `SITE_ICON_PX` 必须同步 —— 那边写 `<img width/height>`。
     深浅主题共用同一尺寸，所以只声明在亮色块里。 */
  --site-icon-size: 32px;
}
[data-theme="dark"] .game-page {
  --game-ext-text: #FCA5A5;
  --game-ext-soft: rgba(248, 113, 113, .13);
  --game-ext-border: rgba(248, 113, 113, .32);
}
.hub-card__badge--ext {
  color: var(--game-ext-text);
  background: var(--game-ext-soft);
  border-color: var(--game-ext-border);
}

/* ---------- 社群二维码 ---------- */
.qr-panel {
  display: flex;
  gap: 20px;
  flex-wrap: wrap;
  padding: 14px 16px;
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  background: var(--card);
}
.qr-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 7px;
  transition: transform .2s ease;
}
.qr-card:hover { transform: translateY(-2px); }
/* 二维码必须留白底 —— 深色主题下也不改，否则扫描识别率下降 */
.qr-card__img {
  display: block;
  width: 104px;
  height: 104px;
  padding: 6px;
  box-sizing: border-box;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: #fff;
  transition: box-shadow .2s ease;
}
.qr-card:hover .qr-card__img { box-shadow: 0 4px 12px rgba(0, 0, 0, .10); }
.qr-card__img img { width: 100%; height: 100%; object-fit: contain; display: block; border-radius: 4px; }
.qr-card__label { font-size: 12px; color: var(--text-secondary); }
/* ⚠️ 这里原有 `.qr-card__img--ph`（虚线占位框）与 `.qr-card__note`（「上线前替换为真二维码」小字）两条规则，
   已随 V1.69「没给真图就整节隐藏」一并删除 —— 生成器现在会把 placeholder:true 的条目直接过滤掉、
   不产出任何占位 DOM，这两条选择器已不可能命中。**不要再加回来**：占位图本身就是给访客看的噪音。 */

/* ---------- 相关游戏（胶囊；本页只渲染已有页面，未上线的显式标「开发中」） ---------- */
.game-related {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 24px;
}
.game-related__item {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 5px 14px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--card);
  color: var(--text-muted);
  font-size: 12.5px;
  font-weight: 600;
  text-decoration: none;
  transition: color .14s ease, border-color .14s ease, background .14s ease;
}
a.game-related__item:hover {
  color: var(--accent-text);
  border-color: var(--accent);
  background: var(--accent-soft);
}
.game-related__item--soon {
  color: var(--text-secondary);
  background: var(--bg-subtle);
  border-style: dashed;
}

/* ---------- 常见问题（可见 FAQ —— 与 <head> 里的 FAQPage 结构化数据同源同文） ----------
   ⚠️ JSON-LD 的 FAQPage 要求问答正文在页面上**可见**，否则该标记无效（甚至算违规）。
   V1.67 及以前的 nsh.html 只有 markup、没有可见问答，这里补齐。 */
.game-faq { margin: 0; }
.game-faq dt {
  margin: 0 0 4px;
  font-size: 13px;
  font-weight: 700;
  color: var(--text);
}
.game-faq dt + dd { margin: 0 0 12px; }
.game-faq dd {
  font-size: 12.5px;
  line-height: 1.75;
  color: var(--text-secondary);
}
.game-faq dd:last-child { margin-bottom: 0; }

/* ---------- 入场动画：模块按 DOM 顺序依次浮现 ----------
   ⚠️ 与「待办 B · C2-3 级联入场」是同一件事 —— 目前只在游戏页启用。
   若以后要拉齐全站节奏，把这段整体挪进 style.css 并从本文件删掉。 */
.game-page > * { animation: gameIn .4s ease backwards; }
.game-page > :nth-child(1) { animation-delay: 0ms; }
.game-page > :nth-child(2) { animation-delay: 60ms; }
.game-page > :nth-child(3) { animation-delay: 120ms; }
.game-page > :nth-child(4) { animation-delay: 180ms; }
.game-page > :nth-child(5) { animation-delay: 240ms; }
.game-page > :nth-child(6) { animation-delay: 300ms; }
.game-page > :nth-child(7) { animation-delay: 360ms; }
.game-page > :nth-child(8) { animation-delay: 420ms; }
.game-page > :nth-child(9) { animation-delay: 480ms; }
.game-page > :nth-child(10) { animation-delay: 540ms; }
.game-page > :nth-child(11) { animation-delay: 600ms; }
.game-page > :nth-child(12) { animation-delay: 660ms; }
@keyframes gameIn {
  from { opacity: 0; transform: translateY(12px); }
  to { opacity: 1; transform: none; }
}

/* ---------- 正文层（页尾的长文，供搜索引擎与深度读者阅读） ----------
 * ⚠️ 只消费主站令牌（--text* / --accent*），**不重复定义 style.css 已有的类**。
 *    强调色在 .game-page 子树里已被子主题映射成琥珀金，这里直接用 --accent / --accent-text 即可。
 * ⚠️ 「内容栏宽」的唯一源仍是 --content-max（见 T6），本层**没有**改它 —— 下面的 max-width 不是栏宽，是
 *    **排印行长（measure）**：整页只有这一块是 3000+ 字连续正文，不设会在 1200px 栏里排到 ~89 全角字/行
 *    （约舒适的 2 倍，实测见 tmp/shot-body.js）。所以只把本层的文字收到 60em。
 *    ⚠️ 上层 FAQ 是短问答、按栏宽铺满；本层是长文 —— 体裁不同，刻意区别对待，不是漏改。
 *    ⚠️ 60em 按本元素自身 font-size 解析（13.5px）→ 810px ≈ 60 全角字/行；换字号会自动跟着变。
 *    ⚠️ 只收内容不收容器：.game-page 宽度不变 → og:image 的 1200px 截图裁切不受影响（T13）。 */
.game-body {
  max-width: 60em;
  font-size: 13.5px;
  line-height: 1.85;
  color: var(--text-secondary);
}
.game-body h3 {
  margin: 22px 0 8px;
  font-size: 14px;
  font-weight: 700;
  color: var(--text);
}
.game-body h3:first-child { margin-top: 0; }

/* 正文层目录（V1.79）：小节 >1 时生成器自动在正文层最前插一排胶囊，href 指向各节 `<h3 id="…">`。
   为什么要有：本层是 3000+ 字连续正文、且排在页尾 —— 没有目录时读者连「有几节、分别讲什么」都不知道。
   ⚠️ 复用 `.game-links__item` 的**胶囊语汇**（同 padding / 同圆角 / 同 hover 反应），但**不共用类名**：
      那一套是「外链入口」（生成器 outerA/innerA 会挂 target / rel 处理），
      这一套是**页内锚点**（`href="#id"`，无 target、无 rel）—— 语义不同，不该被同一批规则牵动。
   ⚠️ 放在 `.game-body` 之内 → 自动继承 60em 排印行长；换行交给 flex-wrap。 */
.game-body__toc {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 0 0 14px;
  padding-bottom: 14px;
  border-bottom: 1px solid var(--border);
}
.game-body__toc-item {
  display: inline-flex;
  align-items: center;
  padding: 4px 12px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--card);
  color: var(--text-muted);
  font-size: 12px;
  font-weight: 600;
  text-decoration: none;
  transition: color .14s ease, border-color .14s ease, background .14s ease;
}
a.game-body__toc-item:hover {
  color: var(--accent-text);
  border-color: var(--accent);
  background: var(--accent-soft);
}
/* 目录出现后，第一节标题不再命中 `.game-body h3:first-child`（首个孩子位被目录占了），
   于是会拿到 22px 上边距 → 目录与正文之间多出一段空白。这里把它补回 0。
   ⚠️ 与 `.game-body h3` 特异性相同（0,1,1），靠**位置在后**生效 —— 所以这条必须写在它下面。 */
.game-body__toc + h3 { margin-top: 0; }
.game-body p { margin: 0 0 10px; }
.game-body b { color: var(--text); }
.game-body a { color: var(--accent-text); text-underline-offset: 3px; }
.game-body ul { margin: 0 0 12px; padding-left: 20px; }
.game-body li { margin-bottom: 5px; }
.game-body li::marker { color: var(--accent); }
.game-body__note {
  margin-top: 4px;
  font-size: 12px;
  line-height: 1.7;
  color: var(--text-secondary);
}

/* ---------- 核心结论（V1.80）：正文层的「先给判断」摘要 ----------
 * 位置：目录之后、第一节标题之前（由生成器保证，selfCheck 有位置断言）。
 * 为什么要有：本层是 2,000+ 字连排，读者滚下来先撞见一排目录 —— 而目录只说明「有几节」，
 *   不说明「讲了什么」。三条结论把正文的判断先摆出来，给读者一个继续往下读的理由。
 * ⚠️ 序号由 CSS counter 生成、**不写进 HTML**：条数由数据决定，改条数不用回数据文件改序号。
 * ⚠️ 配色只用 --accent-soft / --accent-text：这两个令牌在 `.game-page` 子树里已被子主题重映射
 *   （见 css/nsh-theme.css 末段）→ 逆水寒页会自动换成暖石琥珀。**这里不要写死色值**。 */
.game-conclusions {
  counter-reset: gc;
  display: grid;
  gap: 8px;
  margin: 0 0 22px;
  padding: 0;
  list-style: none;
}
/* ⚠️ 必须排在 `.game-body li` **之后**：两条特异性同为 (0,1,1)，靠位置在后才压得住那个
   5px 的 `margin-bottom`（与本层的 grid gap 叠加会让三条间距不匀）。 */
.game-conclusions > li {
  position: relative;
  margin-bottom: 0;
  padding: 12px 14px 12px 46px;
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  background: var(--card);
}
.game-conclusions > li::before {
  counter-increment: gc;
  content: counter(gc);
  position: absolute;
  left: 13px;
  top: 12px;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border-radius: 7px;
  background: var(--accent-soft);
  color: var(--accent-text);
  font-size: 12px;
  font-weight: 700;
  line-height: 1;
}
.game-conclusions__t {
  display: block;
  font-size: 14px;
  font-weight: 700;
  line-height: 1.5;
  color: var(--text);
}
.game-conclusions__d {
  display: block;
  margin-top: 3px;
  font-size: 12.5px;
  line-height: 1.7;
  color: var(--text-secondary);
}
/* 结论卡出现后，第一节标题既不再命中 `.game-body h3:first-child`、也躲开了
   `.game-body__toc + h3`（中间夹了一层）→ 会拿到 22px 上边距。这里补回 0。
   ⚠️ 与 `.game-body h3` 特异性相同（0,1,1），靠**位置在后**生效 —— 所以务必写在它下面。 */
.game-conclusions + h3 { margin-top: 0; }

/* ---------- 页内内联小工具（miniCalc，V2.17） ----------
   一个数字输入框 + 两个实时结果，横排一行；窄屏自动换行（flex-wrap）。
   ⚠️ 这一节**只服务用了 miniCalc 的页面**（目前只有火炬之光：无限），但样式放在共享表里
      —— 原因同 .game-tools：本文件是各游戏页共用骨架，样式散进 HTML 内联就是漂移的起点
      （且内联 style 不随主题变量走，暗色下会直接串色）。
   ⚠️ 文字**不用 --text-tertiary**：亮色下它是 #94A3B8，12px 小字压浅底只有 2.4–2.7:1
      （需 4.5）。本文件 V1.84 已把同类问题统一收敛到 --text-secondary（见文件头说明）。 */
.game-minicalc {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 18px;
  align-items: flex-end;
  padding: 14px 16px;
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  background: var(--bg-subtle);
}
.game-minicalc__field {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 13px;
  color: var(--text-secondary);
}
.game-minicalc__input {
  width: 150px;
  padding: 7px 10px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-btn);
  background: var(--card);
  color: var(--text);
  font-family: inherit;
  font-size: 15px;
}
/* ⚠️ tabular-nums：结果每秒随输入重排，比例数字会让整块左右抖动（宽度在变）；
   等宽数字让「12 → 8」这类变化原地替换，不推动相邻元素。 */
.game-minicalc__out {
  font-size: 20px;
  font-weight: 700;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}
.game-minicalc__out--accent { color: var(--accent); }
.game-minicalc__note {
  margin: 8px 2px 0;
  font-size: 12px;
  color: var(--text-secondary);
}

/* ---------- 窄屏 ---------- */
@media (max-width: 640px) {
  /* ⚠️ 换行本身已在基线上由 flex-basis 判据自动完成（见 .game-hero__meta），
     这里只补两件基线做不到的事：图标缩小、按钮区占满整行并左对齐。 */
  .game-hero { gap: 14px; padding-bottom: 18px; }
  /* 窄屏：按钮区占满整行 → 文字可能横跨到照片右侧较亮处，
     所以把「左重右轻」的横向遮罩换成**整体压暗 + 底部加深**，保证任意位置都够对比度。 */
  .game-hero--bg {
    padding: 18px 16px;
    min-height: 200px;
  }
  .game-hero--bg::after {
    background-image: linear-gradient(180deg, rgba(9,12,17,.60) 0%, rgba(9,12,17,.70) 55%, rgba(9,12,17,.84) 100%);
  }
  .game-hero__icon { width: 64px; height: 64px; }
  .game-hero__name { font-size: 19px; }
  .game-hero__actions { flex: 1 1 100%; max-width: none; justify-content: flex-start; }
  .link-list { grid-template-columns: 1fr; }
}

@media (prefers-reduced-motion: reduce) {
  .game-page > * { animation: none; }
  .qr-card:hover { transform: none; }
}
