/* atlas 屏的 UI：把它变成场景里的石头（SKEUO_DRAFT 4.9）。
 *
 * 变量在 css/stone.css（`--stone-face` / `--stone-deep` / `--stone-seam` /
 * `--stone-lit`），全部挂在 `.view[data-view="atlas"]` 上。
 *
 * 三条判据（从占卜屏那一轮继承，账在 stone.css 开头）：
 *   ① 换底不换语义色
 *   ② 凸出还是凹进决定亮暗
 *   ③ 体是冷的，照在它上面的光才是暖的
 *
 * ---- 这一屏的特权：光是石头自己发的 ----
 *
 * 别的三屏各有一盏外来的灯（天光 / 柱顶 / 龛里），所以那三屏的 lit 只许出现在
 * **迎着那盏灯的那一侧**——上沿亮、下沿是缝。图鉴一盏灯都没有：唯一的光源是
 * 刻纹自己在发光（`--stone-lit: #aeced8`，冷）。
 *
 * 推论是这一屏独有的：**lit 可以出现在下沿、内缘、任何一条刻线上**，
 * 因为那不是"外面的光照上来"，是"这条槽自己在亮"。这一整个文件都在用这一条,
 * 它也是图鉴和鱼谱并排看时最大的差别（鱼谱全暖、只有上沿亮）。
 *
 * 而"发光"在这里始终是**两笔**，和画布那边一模一样（steleHall 的 glyphRow）：
 *   · 很宽很淡的溢光（inset 0 0 Npx -Mpx …）
 *   · 很细很亮的线（inset 0 0 0 1px … / 一道 1px 边）
 * 只有细线是一根荧光管，只有溢光是一团雾。两笔合起来才读作"光从槽里漫出来"。
 *
 * ⚠️ **作用域一律限定在 `.view[data-view="atlas"]` 之内。**
 * `button` / `input` / `select` 用在每一屏（对战 HUD、结算、弹窗、顶栏），
 * 不限定就会炸到全站——占卜屏那一轮的 `button:hover` 就是这么冒出来
 * 一个盖住整座宫殿的深蓝方块的。
 *
 * ⚠️ **别只写 `.cc-*` / `.codex-card`。** 这批类名是**图鉴和鱼谱两屏共用的**
 * （codexRender.js 同时服务两屏，`?gallery` 那页也用同一份）。不带
 * `.view[data-view="atlas"]` 的规则会当场把鱼谱屏一起改掉。
 */

/* ==================== 这一屏留给背景的两个数 ==================== */

.view[data-view="atlas"] {
  /* ---- 碑脚前面那一段地面 ----
     地平线 = 面板的下沿（steleHall.js 的 floorY，硬验收第①条）。
     所以"地面有多高"**不是背景自己定的，是这里留出来的**——碑立在地上,
     脚下总得有地。
     一点都不留的下场是实测的（1440×900 满账号）：面板下沿在 913，而文档
     只有 937 高，地面就剩 24px，基座、残碑、断块一件都画不出来。
     ⚠️ 这个数**不参与任何几何**。背景照量出来的盒子画地平线，它只决定
     "你看见多少地"——所以改它不可能让谁对不上，这也是它敢待在 CSS 里的理由。

     ---- 22vh → 14vh：因为面板矮了 467px ----

     原来是 `clamp(140px, 22vh, 220px)`（900 高时 198px）。那时候面板 1081 高,
     怎么算都要滚，所以这一段留得阔一点没有代价。
     词条图鉴改成墙 + 读碑之后面板只有 **614**，于是这 198px 变成了**唯一**
     还在撑出纵滚的东西：614 + 198 = 812，而 900 的视口只给到 ~780。
     实测就是那 22px。
     收到 14vh（900 高时 126px）之后 1440×900 归零。下界 110px 是"还画得出
     基座和残碑"的量——比那次实测的 24px 有余，比 140 省得下这一屏的滚动。 */
  --atlas-floor: clamp(110px, 14vh, 200px);

  /* ---- 碑的切角 ----
     圆角是 UI 的语言，石头没有圆角：它要么是切角，要么是崩口。
     ⚠️ 背景那层的裁剪必须**往里让出至少这么多**，否则被切掉的那几个角
     后面没有碑墙，露出来的是裸水色（steleHall.js 的 CHAMFER_BLEED）。 */
  --atlas-chamfer: 13px;

  /* 亮线的两档。都从 `--stone-lit` 推——**这一屏只有一种光**，
     另调一个色相就等于凭空多了一盏灯。
     （溢光那一笔不在这儿：它是 `inset 0 0 Npx -Mpx var(--stone-lit)`,
     浓淡靠那个负扩散调，不靠另开一个 alpha 档。） */
  --lit-hair: color-mix(in srgb, var(--stone-lit) 32%, transparent);
  --lit-faint: color-mix(in srgb, var(--stone-lit) 9%, transparent);
}

/* ⚠️ **这里原来在 `.screen-body` 下面留一段 `--atlas-floor` 当地面。删了。**

   那是壳之前的办法：页面会一直往下长，所以"想看见地"就只能自己在内容底下
   垫一段。现在每一屏是个固定的壳，地面由 `--horizon` 那条带管
   （style.css"固定的壳"那一节）。

   两套一起留着的后果用户一眼看见了：滚到底时内容早就结束了，而面板下面
   **还空着 114px**（14vh）——那一段既不是内容也不是房间，就是一块空。

   判据：**一件事换了负责人，旧的那个必须撤，不能"留着也不碍事"。**
   两处同时留地，量出来的就是两段。 */

/* ==================== ① 面板 = 碑林里的一块碑 ==================== */

/* 原来这块是 `rgb(13,27,40)` + 8px 圆角 + 1px 蓝边——**一张标准 UI 卡片浮在
   场景上**。它和身后那间碑室没有任何关系，读作"贴在照片前面的一张纸"。

   现在它是这间厅里立着的那块大碑（大厅入口那块碑的里面，pixiRenderer 的
   `stele` 分支）。背景那层已经按这个前提在画了：面板下沿就是地平线,
   面板中心就是消失点，面板底下有两级基座和一道贴地影。

   ⚠️ **它必须继续是不透明的。** steleHall 用 evenodd 把画面裁成"面板之外
   那一圈"，前提正是"面板底下的每一笔都是白烧的"。透出来的话，被裁掉的那
   一整块会是个洞。 */
.view[data-view="atlas"] .card {
  background: none;          /* 面挪到 ::before 上，理由见那一段 */
  border: 0;
  border-radius: 0;          /* 8px → 0：圆角是 UI 的语言 */
  box-shadow: none;
  /* 碑面上的字要往里收一点：贴着边刻的字读作"排版"，往里收才读作"碑面"。 */
  padding: 22px 26px 26px;
  position: relative;
  /* ::before 要沉到内容底下（z-index: -1），`isolation` 把这个 -1 关在这块碑
     里边。不加的话它会一路沉到 `.view` 甚至 body 的底色后面去。
     ⚠️ 用 `isolation` 而不是 `transform` / `filter` 来造层叠上下文：
     后两者会成为 `position: fixed` 后代的**包含块**（stone.css 那条账：
     占卜屏因此把一块铺满视口的画布收进了内容列）。isolation 不会。 */
  isolation: isolate;
}

/* ---- 碑面 ----
 *
 * 为什么在伪元素上而不是 `.card` 自己身上：**切角要能裁，接地影不能被裁**。
 * `clip-path` 会把整个元素连同它的 box-shadow 和所有后代一起裁掉,
 * 那道落在地上的影子（::after）就没了。所以面 + 切角归 ::before，影归 ::after。
 */
.view[data-view="atlas"] .card::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  /* 体是冷的、暗的。竖向只压很浅的一档（下部沉积更重）——
     **大面积不许刷亮**，整块面刷亮色会立刻读成另一种材料（判据③）。 */
  background: linear-gradient(178deg,
    var(--stone-face) 0%,
    color-mix(in srgb, var(--stone-face) 78%, var(--stone-deep)) 100%);
  /* 碑的角：上面两个是切角，下面两个是崩口（一大一小）。
     **不对称是故意的**——两边一样大读作"设计过的倒角"，那又回到 UI 了。 */
  clip-path: polygon(
    var(--atlas-chamfer) 0,
    calc(100% - var(--atlas-chamfer)) 0,
    100% var(--atlas-chamfer),
    100% calc(100% - 7px),
    calc(100% - 7px) 100%,
    calc(var(--atlas-chamfer) * 1.6) 100%,
    0 calc(100% - var(--atlas-chamfer) * 1.6)
  );
  /* 边缘那一圈刻线。**四条边都有，下沿也有**——石头自己在发光,
     不受"光从上面来"的约束（文件开头那条特权）。
     由外往里三层：1px 暗（碑的边在阴影里）→ 1px 亮线 → 一片溢光。 */
  box-shadow:
    inset 0 0 0 1px var(--stone-seam),
    inset 0 0 0 2px var(--lit-hair),
    inset 0 0 26px -10px var(--stone-lit),
    inset 0 0 0 3px var(--stone-deep);
}

/* ---- 接地影 ----
 * 碑有重量的唯一证据。落在**地面上**（面板下沿以下 10px），而地面正是背景
 * 那层从这条下沿往下画的——所以这道影子是 DOM 和画布唯一真正接上的地方。
 * 画布那边基座自己也有一道（steleHall 的 softShadow），两道叠起来正好是
 * "碑压在座上、座压在地上"。 */
.view[data-view="atlas"] .card::after {
  content: '';
  position: absolute;
  left: 3%;
  right: 3%;
  bottom: -11px;
  height: 24px;
  z-index: -2;
  background: radial-gradient(58% 100% at 50% 0,
    rgba(2, 6, 8, 0.66) 0%, rgba(2, 6, 8, 0.28) 52%, transparent 78%);
  pointer-events: none;
}

/* ---- 碑面上的两道横槽：标题和小标题 ----
 *
 * 槽的方向是定死的：**上缘暗、下缘亮**。颠倒过来就成浮雕了（贴上去的一条
 * 边），那是 steleHall 的 glyphRow 和 sceneryForm.steleForm 上面同一条账。
 * 这里的两条线就是那两笔：1px 的 seam 在上，1px 的 lit 在下。 */
.view[data-view="atlas"] .card > h2 {
  padding-bottom: 11px;
  margin-bottom: 10px;
  letter-spacing: 0.06em;    /* 刻出来的字距宽 */
  border-bottom: 1px solid var(--stone-seam);
  /* 非 inset、只往下偏 1px：只有下沿那一条会露出来，读作槽底的反光。 */
  box-shadow: 0 1px 0 var(--lit-hair);
}

.view[data-view="atlas"] .card h3 {
  padding-bottom: 6px;
  letter-spacing: 0.08em;
  color: color-mix(in srgb, var(--stone-lit) 52%, var(--ink-dim));
  border-bottom: 1px solid var(--stone-seam);
  box-shadow: 0 1px 0 var(--lit-faint);
}

/* 两栏之间那道竖槽：一块碑上的两列字，中间是一条刻线，不是两块板的缝。
   `.codex-cols` 只在 ≥1100px 才分栏（style.css 那条），所以这道线也跟着走。 */
@media (min-width: 1100px) {
  .view[data-view="atlas"] .codex-cols > .codex-col:first-child {
    padding-right: var(--sp-5);
    border-right: 1px solid var(--stone-seam);
    box-shadow: 1px 0 0 var(--lit-faint);
  }
}

/* ==================== ② UI 原子（全部限定在这一屏内） ==================== */

/* ---- 一个图鉴条目 = 碑面上凿出来的一格 ----
 *
 * 凹进去的形是定死的（判据②）：**上缘 inset 阴影 + 下缘极淡的 lit 反光**。
 * 占卜屏那一轮 `.name-row` 就是栽在这儿——等亮度换算没错，但把"凿出来的凹格"
 * 画成了"托起来的木盘"。
 *
 * 两个选择器是因为翻卡和不翻的卡，盒子长在不同的元素上：
 *   · 能翻的（`.flip`）：`.codex-card` 本身没有壳，两个面（`.cx-face`）各带一份
 *   · 不能翻的：`.codex-card` 自己就是那个盒子
 * 漏掉任一个都会有一半的格子还是 UI 卡片。 */
.view[data-view="atlas"] .codex-card:not(.flip),
.view[data-view="atlas"] .codex-card.flip .cx-face {
  border-radius: 1px;
  background: linear-gradient(180deg,
    var(--stone-deep) 0%,
    color-mix(in srgb, var(--stone-deep) 62%, var(--stone-face)) 100%);
  box-shadow:
    inset 0 2px 4px -1px rgba(0, 0, 0, 0.62),   /* 上缘：这一格是凿进去的 */
    inset 0 -1px 0 var(--lit-faint);            /* 下缘：极淡的反光 */
}

/* ⚠️ **左边那条边不许碰**，所以描边只写三面。
 *
 * 这条是实测抓出来的：原来这里写的是 `border: 1px solid var(--stone-seam)`
 * 一句话。而**左边那道边是语义色**——词条卡的档位色条（`.cc-affix` 的
 * 中性 #3a4a63 / 正面绿 / 取舍黄 / 负面红）和"还没解锁"那条金色虚线
 * （`.cc-affix.cc-todo` 的 `--other`）都长在 `border-left` 上，而且都只有
 * 2 个类，被这条 4 个类的规则整条盖掉：档位色条从 3px 的颜色变成 1px 的
 * 黑缝，**这一屏最要紧的那条信息就这么没了**（判据①：换底不换语义色）。
 *
 * 翻卡的两个面（`.cx-face`）上没有任何语义边，所以它们可以写整条 `border`。 */
.view[data-view="atlas"] .codex-card:not(.flip) {
  border-top: 1px solid var(--stone-seam);
  border-right: 1px solid var(--stone-seam);
  border-bottom: 1px solid var(--stone-seam);
}
.view[data-view="atlas"] .codex-card.flip .cx-face {
  border: 1px solid var(--stone-seam);
}
/* 唯一一张左边没有语义色的卡：还没凑齐的套装卡。它那条左边是
   `.codex-card` 的通用 UI 蓝（#1f3350）——三面是石缝、剩一面是蓝线,
   读起来就是没改完。 */
.view[data-view="atlas"] .set-card:not(.unlocked) {
  border-left: 1px solid var(--stone-seam);
}

/* ---- 未解锁 = **刻纹没亮**，不是灰掉的 UI 元素 ----
 *
 * `.codex-card` 原来靠 `opacity: 0.5` 表达"这条你还没见过"。那是 UI 的做法：
 * 半透明的控件读作"这个按钮现在按不了"。在一间碑室里同一件事有一个更便宜、
 * 也更贴世界观的说法——**那一格的字还没亮起来**。格子照样凿在那儿，深浅
 * 一模一样，只是不发光。
 *
 * 所以这里把 opacity 收回 1，区别整个压在下面那道溢光上。
 *
   （`.cc-affix.cc-todo` 那条 0.8 也被这一条盖住：它只有 2 个类，
   而这里有 3 个。） */
.view[data-view="atlas"] .codex-card {
  opacity: 1;
}

/* 亮起来的那一格：溢光 + 亮线两笔，和画布上的刻纹同一套。
   ⚠️ 同一条"左边不许碰"：亮起来的时候也只亮三面的边，
   左边那道档位色条该是什么颜色就还是什么颜色。 */
.view[data-view="atlas"] .codex-card.unlocked:not(.flip) {
  border-top-color: color-mix(in srgb, var(--stone-lit) 22%, var(--stone-seam));
  border-right-color: color-mix(in srgb, var(--stone-lit) 22%, var(--stone-seam));
  border-bottom-color: color-mix(in srgb, var(--stone-lit) 22%, var(--stone-seam));
}
/* 面（`.cx-face`）上没有语义边，所以整条 border-color 可以写。 */
.view[data-view="atlas"] .codex-card.unlocked.flip .cx-front,
.view[data-view="atlas"] .codex-card.flip:hover .cx-face,
.view[data-view="atlas"] .codex-card.flip:focus-visible .cx-face,
.view[data-view="atlas"] .codex-card.flip.flipped .cx-face {
  border-color: color-mix(in srgb, var(--stone-lit) 22%, var(--stone-seam));
}
.view[data-view="atlas"] .codex-card.unlocked:not(.flip),
.view[data-view="atlas"] .codex-card.unlocked.flip .cx-front,
.view[data-view="atlas"] .codex-card.flip:hover .cx-face,
.view[data-view="atlas"] .codex-card.flip:focus-visible .cx-face,
.view[data-view="atlas"] .codex-card.flip.flipped .cx-face {
  box-shadow:
    inset 0 2px 4px -1px rgba(0, 0, 0, 0.62),
    inset 0 -1px 0 var(--lit-hair),
    inset 0 0 16px -6px var(--stone-lit);
}

/* 翻过去那一面：刻得更深的一格（底更暗、亮线更足）。
   `.cx-back` 原来是 `#101d31` + 蓝边，是这一屏最后一块 UI 蓝。 */
.view[data-view="atlas"] .codex-card.flip .cx-back {
  border-color: color-mix(in srgb, var(--stone-lit) 26%, var(--stone-seam));
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--stone-deep) 70%, #000) 0%,
    var(--stone-deep) 100%);
  box-shadow:
    inset 0 3px 6px -1px rgba(0, 0, 0, 0.72),
    inset 0 -1px 0 var(--lit-hair),
    inset 0 0 20px -6px var(--stone-lit);
}

/* 组头那条进度槽：槽是凹的（deep + 上缘 inset），填充的那一段在发光。
 *
 * ⚠️ `:not(.complete)` 不是可选的。`.codex-group.complete .cg-bar > i` 用
 * `var(--good)`——**那是语义色**（这一组齐了），而它和这条规则的特异度完全
 * 相等（各 3 个类），只靠 atlas.css 加载得更晚就会把它盖掉。
 * 判据①：换底不换语义色。 */
.view[data-view="atlas"] .cg-bar {
  height: 4px;
  border-radius: 0;
  /* `overflow: hidden` 是给药丸形的圆角准备的（切掉溢出的填充头）。
     角没了它就只剩一个坏处：**把填充那道溢光一起裁掉**——那正是这一屏
     表达"亮起来"的唯一手段。 */
  overflow: visible;
  background: var(--stone-deep);
  box-shadow:
    inset 0 1px 2px rgba(0, 0, 0, 0.66),
    inset 0 -1px 0 var(--lit-faint);
}
.view[data-view="atlas"] .codex-group:not(.complete) .cg-bar > i {
  border-radius: 0;
  background: color-mix(in srgb, var(--stone-lit) 76%, transparent);
  box-shadow: 0 0 7px -1px var(--stone-lit);
}
/* 齐了的那一组：颜色仍旧是 `--good`（不动），只给它同一份溢光——
   发光是这一屏的语汇，绿是那一条信息，两件事互不干涉。 */
.view[data-view="atlas"] .codex-group.complete .cg-bar > i {
  border-radius: 0;
  box-shadow: 0 0 7px -1px color-mix(in srgb, var(--good) 62%, transparent);
}

/* ⚠️ 这里原来有一条 `.cg-grid { grid-auto-rows: minmax(var(--atlas-cell), auto) }`
 * ——给"每条词条一张卡"那一版统一行高用的。**词条图鉴已经改成墙 + 读碑**
 * （codexRender.js 里那段账），`.cg-grid` 这个元素不存在了,
 * 留着就是给一个渲染不出来的东西写规则。删。
 *
 * 那一轮真正留下来的判据在下面 `.cx-inner` 那一段：
 * **"统一"要验在有壳的那个元素上，不是验在布局盒上。** */

/* ---- 套装图鉴 / 血脉图鉴：用 1fr，不用写死的数 ----
 *
 * 这两个各自是**一个**网格，所以 `1fr` 就够：不定高的网格里所有 `1fr` 行
 * 会一起取"最高那一行的 max-content"，于是同一份清单里每张卡等高,
 * 而且**跟着宽度自适应**——窄屏上卡该矮就矮，不像写死的数那样白占高度。
 *
 * 词条图鉴不能这么做：它是**三个**网格（头 / 身 / 尾），`1fr` 只在各自
 * 网格内部生效，实测尾部那一组最高 79px、头部 98.5px，三组会各自一个高度。
 * 所以那边只能走"一个量出来的共同下界"（上面 --atlas-cell 那段）。
 *
 * 实测这里原来在窄档是分家的（1240 / 1280）：
 *     套装（已凑齐）94.69 / 113.53 两种 ┊ 血脉 77.67 / 96.35 两种
 * 宽档（1440 以上）只有一行或恰好齐，所以之前没看出来。 */
.view[data-view="atlas"] .sc-grid,
.view[data-view="atlas"] .mark-codex {
  grid-auto-rows: 1fr;
}

/* ---- ⚠️ 光把**格子**统一了不算，看得见的那块石头也得填满它 ----
 *
 * 上面那条 `grid-auto-rows` 让每个 grid 单元都是 100px，我当时就此收工了,
 * 而用户回了一句「改完还是大小不一」——**他是对的，我验错了对象。**
 *
 * 我是在空账号上验的：一张卡都没见过 ⇒ `flippable: false` ⇒ 没有 `.flip`
 * ⇒ 画着石面的就是 `.codex-card` 自己 ⇒ 它是 grid 项、被拉满 100px ⇒ 齐。
 * 而满账号上"见过"的卡是**能翻的**：`.codex-card.flip` 自己没有壳
 * （style.css 把它 `border/background` 全清了），石面画在 `.cx-face` 上,
 * 而 `.cx-face` 在一个 `height: auto` 的 `.cx-inner` 里，按**内容**定高。
 *
 * 实测（把全部词条灌成已见过）：格子全是 100px，而看得见的面有四个高度
 *
 *     58.2 ┊ 75.4 ┊ 77.7 ┊ 97.2      ← 用户截图里那几种高矮就是这个
 *
 * 套装图鉴同一个病，而且更明显：格子 132.4px，面 94.7 / 110 / 132.4 三种。
 *
 * 一句话就够：把 `.cx-inner` 撑满卡片，它那唯一一条 auto 行随之拉满
 * （grid 的 `align-content: normal` 就是 stretch），两个面都在 1/1,
 * 于是也一起拉满。三个网格（词条 / 套装 / 血脉）一并解决。
 *
 * ⚠️ 判据：**"统一"要验在有壳的那个元素上，不是验在布局盒上。**
 * 这两者平时重合，只在"卡片是壳的容器、不是壳本身"的时候分叉——
 * 而翻卡正好就是那种情况。 */
.view[data-view="atlas"] .codex-card.flip .cx-inner {
  height: 100%;
}

/* ==================== 一面刻满小格的墙 + 一块读碑 ====================
 *
 * 用户：「现在会导致页面变成需要往下滑才能全部看完了 / 信息密度有点大了」。
 *
 * 量到的账（1440×900，混合账号）：面板 1081px 而视口只给 792；
 * 左栏 955 / 右栏 469——右栏后面空了 486px，撑出滚动的全是左栏；
 * 而左栏 922px 里有 **900px 花在"未见过"的卡上**，每张念同一句话,
 * 而那件事进度条已经说过了。
 *
 * ---- 为什么是"墙 + 碑"而不是"把卡片改小" ----
 *
 * 这一屏是碑林里最深的一间，没有一盏人造灯，光全来自石头自己的刻纹。
 * 那正好给出这一块的形：**墙上一格格凿好的小牌**（刻过的字在发光,
 * 没刻的是一块素牌），**一次只凑近读一块碑**。
 * 密度因此从"26 段文字同时摊开"变成"一面有纹理的墙 + 一段字"。
 *
 * ---- 三种状态 ----
 *
 *   is-seen   刻过了：名字就是发光的刻纹
 *   is-blank  还没刻：一块素牌，**不写字**——那就是"这儿还没刻"的说法
 *   is-todo   还没解锁：名字给（一个连叫什么都不知道的目标不是目标）,
 *             刻纹换成 `--other`（金）。**那是语义色**，判据①：换底不换语义色。
 *
 * 每块牌子**一样大**（用户上一条：每个框大小不一样）。素牌比刻过的小是另一种
 * 做法，但那会让"还差几个"从数格子变成量面积——牌子等大，空位才数得清。 */
.view[data-view="atlas"] .affix-codex {
  display: grid;
  /* 读碑略宽于墙：墙上是 2~4 个字的牌子，碑上是整句描述加数值。
     `minmax(0, …)` 是必须的——不写的话 grid 项的 min-content 会把
     长词条名顶出去，整栏横向溢出。 */
  grid-template-columns: minmax(0, 0.92fr) minmax(0, 1fr);
  gap: 14px;
  align-items: start;
}

.view[data-view="atlas"] .affix-wall {
  display: flex;
  flex-direction: column;
  gap: 11px;
  min-width: 0;
}

.view[data-view="atlas"] .aw-slots {
  display: grid;
  /* 74px 装得下四个字（13px 字号 × 4 = 52 + 左右内边距）。
     `auto-fill` 让窄屏自己少排一列，而不是把字挤掉。 */
  grid-template-columns: repeat(auto-fill, minmax(74px, 1fr));
  gap: 5px;
}

/* 一块牌子：凿进墙里的小格。所以是**凹**的——上缘一道内阴影（墙的厚度
   压在上面），下缘一道极淡的反光。凸出的那套（上沿迎光 + 接地影）是按钮的
   语言，而这些不是按钮，是墙上的位置。 */
.view[data-view="atlas"] .aw-slot {
  min-height: 30px;
  padding: 0 6px;
  border: 0;
  border-radius: 1px;
  background: var(--stone-deep);
  box-shadow:
    inset 0 2px 4px -1px rgba(0, 0, 0, 0.66),
    inset 0 -1px 0 var(--lit-faint);
  color: transparent;
  font-size: var(--fs-sm);
  font-weight: var(--fw-bold);
  letter-spacing: 0.04em;
  line-height: 1.2;
  text-align: center;
  cursor: var(--cur-pointer);
  /* 名字比格子长时收一个省略号，而不是把格子顶大——牌子等大是这一块的前提 */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* 刻过了：字**自己在发光**。这一屏光的唯一来源就是刻纹，所以这里不是
   "有灯照上去"，而是石头本身在亮——发光可以出现在任何刻线上。 */
.view[data-view="atlas"] .aw-slot.is-seen {
  color: var(--stone-lit);
  text-shadow: 0 0 6px color-mix(in srgb, var(--stone-lit) 46%, transparent);
}

/* 还没刻：素牌。**一点字都没有**，只有那道凿痕。 */
.view[data-view="atlas"] .aw-slot.is-blank {
  color: transparent;
}

/* 还没解锁：名字在，刻纹是金的。金是语义色（`--other`：这是一个目标）,
   一个像素都不改，只借这一屏的发光语汇给它同一份溢光。 */
.view[data-view="atlas"] .aw-slot.is-todo {
  color: var(--other);
  text-shadow: 0 0 6px color-mix(in srgb, var(--other) 42%, transparent);
}

/* 正在读的那一格：刻纹更亮 + 一圈迎光的棱。
   **不是换背景色**——换底就变成了另一种材料。 */
.view[data-view="atlas"] .aw-slot.is-reading {
  box-shadow:
    inset 0 2px 4px -1px rgba(0, 0, 0, 0.66),
    inset 0 0 0 1px color-mix(in srgb, var(--stone-lit) 34%, transparent),
    0 0 9px -2px color-mix(in srgb, var(--stone-lit) 40%, transparent);
}
.view[data-view="atlas"] .aw-slot.is-reading.is-seen,
.view[data-view="atlas"] .aw-slot.is-reading.is-blank {
  color: var(--stone-lit);
  text-shadow: 0 0 9px color-mix(in srgb, var(--stone-lit) 66%, transparent);
}

/* 锁着的那块牌子下沿一道金线：长度就是进度。凹槽里一段发光的填充,
   和组头那条 `.cg-bar` 同一套语汇。金是语义色（这是一个目标），不动。 */
.view[data-view="atlas"] .aw-slot { position: relative; }
.view[data-view="atlas"] .aw-at {
  position: absolute;
  left: 0;
  bottom: 0;
  height: 2px;
  background: color-mix(in srgb, var(--other) 80%, transparent);
  box-shadow: 0 0 6px -1px var(--other);
}

.view[data-view="atlas"] .aw-slot:hover:not(.is-reading) {
  box-shadow:
    inset 0 2px 4px -1px rgba(0, 0, 0, 0.66),
    inset 0 -1px 0 color-mix(in srgb, var(--stone-lit) 20%, transparent);
}
.view[data-view="atlas"] .aw-slot:focus-visible {
  outline: 1px solid color-mix(in srgb, var(--stone-lit) 52%, transparent);
  outline-offset: 1px;
}

/* ---- 读碑：整屏唯一一块**凸出来**的石头 ----
 *
 * 墙是一格格凹进去的，碑是靠在墙上的一块——上棱迎光、下方一道缝、
 * 底下一道接地影。那个反差就是"这一块是拿来读的，别的是拿来数的"。 */
.view[data-view="atlas"] .affix-stele {
  position: sticky;
  /* 墙比碑高的时候（全收集了），滚动时碑跟着走——不然读到一半它就出画了。
     96 是内容顶（顶栏 + 面包屑），和 js/main.js 量到的那个数同源。 */
  top: 96px;
  min-height: 132px;
  padding: 12px 14px;
  border-radius: 1px;
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--stone-face) 88%, var(--stone-deep)) 0%,
    var(--stone-deep) 100%);
  box-shadow:
    inset 0 1px 0 color-mix(in srgb, var(--stone-lit) 16%, transparent),
    0 1px 0 var(--stone-seam),
    0 6px 10px -6px rgba(0, 0, 0, 0.72);
}

.view[data-view="atlas"] .as-head {
  display: flex;
  align-items: baseline;
  gap: 8px;
  margin-bottom: 2px;
}
.view[data-view="atlas"] .as-name {
  font-size: var(--fs-md);
  font-weight: var(--fw-bold);
  color: var(--stone-lit);
  text-shadow: 0 0 8px color-mix(in srgb, var(--stone-lit) 34%, transparent);
}
/* 没见过的那一条：名字位置写"未见过"，而且**不发光**——它还没被刻上。 */
.view[data-view="atlas"] .as-name.as-unknown {
  color: var(--ink-dim);
  text-shadow: none;
}
.view[data-view="atlas"] .as-tag,
.view[data-view="atlas"] .as-at {
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
  color: var(--other);        /* 语义色：这是一个目标 */
}
.view[data-view="atlas"] .as-at { margin-left: 6px; }
.view[data-view="atlas"] .as-kind {
  margin-bottom: 6px;
  font-size: var(--fs-xs);
  letter-spacing: 0.08em;
  color: color-mix(in srgb, var(--stone-lit) 40%, var(--ink-dim));
}
.view[data-view="atlas"] .as-body {
  font-size: var(--fs-sm);
  line-height: 1.5;
  color: var(--ink);
}
.view[data-view="atlas"] .as-body + .cx-nums { margin-top: 6px; }

/* 闲着的时候：一个大号的进度数。这是图鉴唯一真正要回答的问题。 */
.view[data-view="atlas"] .as-rest {
  display: flex;
  flex-direction: column;
  gap: 2px;
  justify-content: center;
  min-height: 108px;
}
.view[data-view="atlas"] .as-rest-num {
  font-size: 26px;
  line-height: 1.1;
  color: color-mix(in srgb, var(--stone-lit) 34%, var(--ink-dim));
  font-variant-numeric: tabular-nums;
}
.view[data-view="atlas"] .as-rest-num b {
  color: var(--stone-lit);
  text-shadow: 0 0 10px color-mix(in srgb, var(--stone-lit) 40%, transparent);
}
.view[data-view="atlas"] .as-rest-note {
  font-size: var(--fs-xs);
  letter-spacing: 0.1em;
  color: var(--ink-faint);
}

/* 套装那句提上来的话：横跨整个网格，说一次。 */
.view[data-view="atlas"] .cc-lede {
  margin: 0 0 6px;
  font-size: var(--fs-xs);
  color: var(--ink-faint);
}

.view[data-view="atlas"] .cg-title {
  color: color-mix(in srgb, var(--stone-lit) 44%, var(--ink-dim));
  letter-spacing: 0.08em;
}

/* 套装卡上那枚部位徽章：一个蓝底药丸 → 碑面上一个小凹格。 */
.view[data-view="atlas"] .cc-part {
  border-radius: 1px;
  background: var(--stone-deep);
  color: color-mix(in srgb, var(--stone-lit) 46%, var(--ink-dim));
  box-shadow:
    inset 0 1px 2px rgba(0, 0, 0, 0.6),
    inset 0 -1px 0 var(--lit-faint);
}

/* ---- 空状态 = 一块还没刻字的碑面 ----
 *
 * 不是"一个空盒子"：虚线框 + 圆角是 UI 说"这里缺东西"的方式。一间碑室里
 * "这里还什么都没有"的说法是**素石**——同样凿平的一块面，只是上面没有字,
 * 所以一点光都不发。
 *
 * （图鉴三块目前恒有内容，走不到这一支。留着是因为它一旦出现就必须是石头
 * ——`placeholders.js` 是全站共用的，哪天图鉴加一块要拉数据的东西就用得上。） */
.view[data-view="atlas"] .empty-state {
  border: 1px solid var(--stone-seam);
  border-radius: 1px;
  background: linear-gradient(180deg,
    var(--stone-deep) 0%,
    color-mix(in srgb, var(--stone-deep) 55%, var(--stone-face)) 100%);
  box-shadow:
    inset 0 3px 6px -1px rgba(0, 0, 0, 0.6),
    inset 0 -1px 0 var(--lit-faint);
}
.view[data-view="atlas"] .es-icon { opacity: 0.42; }

/* ---- 按钮凸出，输入框凹进 ----
 *
 * 图鉴这一屏眼下**一个按钮和输入框都没有**（三块图鉴是 div + tabindex,
 * 翻卡靠 CSS 和委托的 click）。这两条仍然要写，理由是它们是这一屏的"默认
 * 材料"：将来往这块碑上加任何一个控件，它一出生就是石头，而不是一个突然
 * 冒出来的深蓝药丸。
 *
 * ⚠️ **选择器带满 `.view[data-view="atlas"]`。** 裸 `button` 会同时打到对战
 * HUD、结算、弹窗、顶栏——占卜屏那一轮的 `button:hover` 就是这么冒出来一个
 * 盖住整座宫殿的深蓝方块的。
 *
 * ---- ⚠️ 屏内标签页要排除掉（2026-08-30 补）----
 *
 * 上面那句"这一屏眼下一个按钮和输入框都没有"写完之后，这一屏长出了三个
 * 标签页（图鉴 / 天赋 / 收藏）。于是这条"默认材料"打在了一个**自带状态机**
 * 的组件上，把它的选中态整个盖掉了：实测三块牌的底色、阴影、文字色**逐位
 * 相同**，选中和没选中的唯一区别是字重 400 → 600。
 *
 * 判据：**"给将来的控件一个默认材料"这条规则，遇到自带状态的组件必须让路。**
 * 默认材料只回答"它是什么做的"，回答不了"它现在是不是选中的"。 */
.view[data-view="atlas"] button:not(.danger-btn, .primary-btn, .screen-tabs > button) {
  border-radius: 1px;
  border: 1px solid var(--stone-seam);
  /* 面仍旧全是 face → deep（判据③：大面积不刷亮）。凸出这件事整个交给
     下面那三笔阴影——**把 lit 掺进渐变里是这一条最常见的破法**：
     一块掺了 16% 亮色的面会立刻读成"另一种材料做的按钮"。 */
  background: linear-gradient(180deg,
    var(--stone-face) 0%,
    color-mix(in srgb, var(--stone-face) 70%, var(--stone-deep)) 100%);
  color: var(--ink);
  /* 凸出：上沿一道 lit + 下方一条 seam + 一道接地影（判据②）。 */
  box-shadow:
    inset 0 1px 0 var(--lit-hair),
    inset 0 -2px 3px -1px var(--stone-seam),
    0 2px 5px -2px rgba(2, 6, 8, 0.7);
}
.view[data-view="atlas"] button:not(.danger-btn, .primary-btn, .screen-tabs > button):hover {
  /* 按下之前先亮一点刻纹——这一屏"可交互"的信号只有光。 */
  box-shadow:
    inset 0 1px 0 var(--lit-hair),
    inset 0 -2px 3px -1px var(--stone-seam),
    inset 0 0 18px -6px var(--stone-lit),
    0 2px 5px -2px rgba(2, 6, 8, 0.7);
}
.view[data-view="atlas"] button:not(.danger-btn, .primary-btn, .screen-tabs > button):active {
  /* 按下去 = 凹进去：上下两笔整个对调。 */
  box-shadow:
    inset 0 2px 4px -1px rgba(0, 0, 0, 0.66),
    inset 0 -1px 0 var(--lit-faint);
}

.view[data-view="atlas"] :is(input, select, textarea) {
  border-radius: 1px;
  border: 1px solid var(--stone-seam);
  background: var(--stone-deep);
  color: var(--ink);
  box-shadow:
    inset 0 2px 4px -1px rgba(0, 0, 0, 0.66),
    inset 0 -1px 0 var(--lit-faint);
}

/* ⚠️ 语义色一条都没动：
 *   · `.cc-affix.positive/.tradeoff/.negative.unlocked` 的左边那道色条
 *     ——⚠️ **实测这道色条宽 0px**：`.cc-affix` 只有 1 个类，打不过
 *     `.codex-card.flip { border: none }` 的 2 个类，而"已见过"的卡正好
 *     都是可翻的。所以这一条现在保的是一条**渲染不出来的**语义色。
 *     那是 style.css 的既有 bug（和 `.mark-card` 同一个病），不在这一屏的
 *     权限内；这里照旧不碰它，等那条修好之后色条会自己出现。
 *   · （原来这儿还列着 `.legend-item.*`——那一栏已删，见 index.html。）
 *   · `.cc-affix.cc-todo` 的虚线金边（`--other`：这是一个目标）
 *   · `.codex-group.complete` 的 `--good`
 *   · `.cx-nums` 那个青（数值）、`.cc-from` 那个紫（印记来路）
 * 它们是**信息**（这条词条是好是坏、这一组齐没齐、这一枚从哪来），
 * 不是风格。这一屏换的是底和光，不是这些。 */

/* ==================== 天赋树：**刻在碑上的一棵树**（第七十三批重画） ====================
 *
 * 结构和几何在 js/ui/views/talentView.js。这里只管它是什么材料。
 *
 * ---- 为什么推倒重画 ----
 *
 * 上一版是三列并排的方块。用户："都说是天赋树了，就要做成树状的有分支"。
 * 那一版的毛病不是不好看，是**"枝"这个字在画面上一点痕迹都没有**——
 * 前置关系（第二级要先点亮第一级）只在按钮的 title 里写了一句话。
 *
 * 现在枝是画出来的，而且**它的亮暗就是前置本身**：通往还没点亮的节点的
 * 那一段是暗槽，点亮之后那一段才迎光。规则一个字都没多写。
 *
 * ---- 材料仍然是这一屏的那一套 ----
 *
 * 这一屏是碑墙 + 自发光刻纹，而且是全站唯一**一盏暖灯都没有**的屏
 * （`--stone-lit` 是冷的）。所以：
 *
 *   脉     三笔画一道裂纹 —— 槽（暗、宽）、纹（迎光）、岔（走不远，永远暗）
 *   节点   凸出还是凹进决定亮暗（和全站那条一致）
 *          点亮了  **凸出** + 刻纹发光
 *          能点    **平的** —— 它是一个可选项，不是一个空槽
 *          差钱    平的，但价钱那一格自己说话
 *          还锁着  **凹进去**的暗格 —— 凹进在这套语言里就是"这里还没有东西"
 *
 * ⚠️ 节点是**HTML 按钮**压在 SVG 上，不是 SVG 里的图形：键盘、焦点、
 * disabled 都得是真的。两边落在同一个坐标系里，而那个坐标系只在
 * talentView.talentLayout 里算一次——CSS 这边一个坐标数都没有。 */

/* 这一整块是**查询容器**：下面那条"够宽就左右排"问的是它。
   ⚠️ 容器不能查询自己——把 `container-type` 写在 .tt-body 上的那一版，
   块里针对 .tt-body 自己的那条声明被整条忽略（而针对它孩子的那条生效了），
   于是"树不再居中占满"应验了、"分两列"没应验，看起来像只写对了一半。 */
.view[data-view="atlas"] .talent-tree {
  display: grid;
  gap: var(--sp-4);
  container-type: inline-size;
}

/* ---- 左边那块碑，右边那块牌子 ----
   用户 2026-08-30："左边放树，右边是具体说明"。

   ⚠️ 断点用 **container query 之外的常规媒体查询**不行——这一块的宽度取决于
   卡片有多宽，不取决于视口（图鉴屏在宽屏上是两列）。所以断点也交给容器：
   牌子挤到 14em 以下就读不成句子了，那时候退回上下排。 */
.view[data-view="atlas"] .tt-body {
  display: grid;
  gap: var(--sp-4);
  align-items: start;
}
@container (min-width: 34em) {
  .view[data-view="atlas"] .tt-body {
    /* 牌子**封顶**：它只有三四行字，给它半屏宽只会让那几行拉成一条线。 */
    grid-template-columns: minmax(0, 1fr) minmax(13em, 19em);
  }
  /* 并排的时候树不再居中：它靠左，牌子贴着它。
   *
   * ⚠️ 这里原来还写着 `max-width: none`。**删了**：树的高度是由宽度和
   * `aspect-ratio: 380/350` 一起定的，去掉宽度上限之后，1920 的窗口上这一列
   * 有一千多像素宽 ⇒ 树被撑到 **1400×1290**，一屏根本放不下（用户：
   * "宽屏模式下这个天赋树也太高了"）。
   *
   * 判据：**给一个带 `aspect-ratio` 的盒子放开宽度，就是放开高度。**
   * 它在窄屏上看不出来（那里宽度本来就被屏幕夹着），只有屏幕一宽才发作。
   *
   * 上限回到和窄屏同一个 30em（≈480×442）——树只有六个节点，再大也只是把
   * 那六颗摊得更开。 */
  .view[data-view="atlas"] .tt-body > .tt-tree { margin: 0; }
}

.view[data-view="atlas"] .tt-head-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-2);
  flex-wrap: wrap;
}
.view[data-view="atlas"] .tt-bal {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.04em;
  /* ⚠️ 它现在是个**按钮**（点开声望流水），所以要把浏览器给按钮的那身
     默认打掉——不打的话它会在一排石头里冒出一个圆角灰块。
     一个变成按钮的东西，形状不该跟着变；变的只是它现在能点。 */
  border: 0;
  font: inherit;
  cursor: var(--cur-pointer);
}
/* 余额那个数迎着这一屏的冷光——**不是暖的**（这一屏没有暖灯）。 */
.view[data-view="atlas"] .tt-bal b {
  color: color-mix(in srgb, var(--stone-lit) 84%, #fff);
  text-shadow: 0 0 7px color-mix(in srgb, var(--stone-lit) 34%, transparent);
}
.view[data-view="atlas"] .tt-total { color: var(--ink-faint); font-size: var(--fs-xs); }

/* ---- 那块画着树的碑 ----
   盒子的比例**跟着 viewBox**（360 × 340）：节点按百分比定位，一旦这里的
   比例和那边不一致，六颗就会全体偏出枝头。 */
.view[data-view="atlas"] .tt-tree {
  position: relative;
  width: 100%;
  max-width: 30em;
  margin: 0 auto;
  aspect-ratio: 380 / 350;
  /* ⚠️ **整棵树按框缩放，不按根字号。** 枝是 SVG（跟着 viewBox 缩），
     节点是 HTML（跟着 em）——两边不同步的话，框一窄，节点相对就变大,
     然后中间那一枝的两级当场叠在一起（375 上实测叠了）。
     容器单位把它们绑回同一个尺度：这棵树是一张画，画上的字跟着画缩。 */
  container-type: inline-size;
  /* 一块凹进去的碑面：树是刻在它上面的。 */
  border-radius: 1px;
  /* 石板：底色 + 两道极淡的层理。**层理是斜的**，和纹路的走向不平行——
     平行的话它们会被读成同一批线。 */
  background:
    repeating-linear-gradient(114deg,
      transparent 0 13px,
      color-mix(in srgb, var(--stone-lit) 4%, transparent) 13px 14px),
    linear-gradient(180deg,
      color-mix(in srgb, var(--stone-deep) 80%, var(--stone-face)) 0%,
      color-mix(in srgb, var(--stone-deep) 96%, #000) 100%);
  box-shadow:
    inset 0 2px 6px -1px rgba(0, 0, 0, 0.75),
    inset 0 -1px 0 color-mix(in srgb, var(--stone-lit) 8%, transparent);
}
.view[data-view="atlas"] .tt-canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

/* 槽：裂纹的底，石头被劈开的那道深。它一直都在——
   **看不见的纹等于没有前置关系可读**。

   ⚠️ 收口是 `butt` 不是 `round`：石头上的裂纹是**尖的**，越走越细直到断掉。
   圆头收口是笔画出来的东西，不是裂出来的（上一版那棵"树"就栽在这儿）。 */
.view[data-view="atlas"] .tl-groove {
  fill: none;
  stroke: rgba(0, 0, 0, 0.72);
  stroke-linecap: butt;
  stroke-linejoin: round;
}
/* 纹迎光的那一侧。还没走到的那一段只有一点点光。 */
.view[data-view="atlas"] .tl-vein {
  fill: none;
  /* ⚠️ 第一版给的是 16%，在实机上等于看不见——而**看不见的纹等于没有分支**,
     那正是那一轮要修的东西。这一档要能读出"这儿有一道"，
     又要明显弱于亮起来的那一段。 */
  stroke: color-mix(in srgb, var(--stone-lit) 34%, transparent);
  stroke-linecap: butt;
  stroke-linejoin: round;
  transition: stroke var(--t-fast) ease-out;
}
/* 岔纹：走不远，**永远暗**。它不通向任何节点，只是让这块石头是裂的。 */
.view[data-view="atlas"] .tl-spur {
  fill: none;
  stroke: color-mix(in srgb, var(--stone-lit) 15%, transparent);
  stroke-width: 0.9;
  stroke-linecap: butt;
}
/* 正看着的那条路：跟着选择走。**比"已点亮"淡一档**——
   它说的是"你正在看这一条"，不是"这一条已经是你的了"。 */
.view[data-view="atlas"] .tl-vein.is-traced {
  stroke: color-mix(in srgb, var(--stone-lit) 62%, #fff);
}
/* 已点亮：常亮，而且**只有它发光**。两种亮长一样的话，选中就成了假的点亮。 */
.view[data-view="atlas"] .tl-vein.is-lit {
  stroke: color-mix(in srgb, var(--stone-lit) 82%, #fff);
  filter: drop-shadow(0 0 4px color-mix(in srgb, var(--stone-lit) 44%, transparent));
}
/* 枝梢上那个字（俭 / 勤 / 承）。**不给它任何底**——给了就成了第七颗节点。 */
.view[data-view="atlas"] .tl-tip {
  fill: color-mix(in srgb, var(--stone-lit) 66%, var(--ink-faint));
  font-size: 16px;
  letter-spacing: 0.1em;
  dominant-baseline: middle;
}

/* ---- 枝头那六颗 ---- */
.view[data-view="atlas"] .tt-node {
  position: absolute;
  left: var(--x);
  top: var(--y);
  transform: translate(-50%, -50%);
  display: grid;
  justify-items: center;
  gap: 2px;
  /* ⚠️ **按内容宽，不给定宽。** 定宽 6.4em 的那一版让相邻两颗的名字在
     浏览器里首尾相接——名字比盘宽一倍，真正会撞的是它。 */
  width: max-content;
  max-width: 7em;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  /* 跟着框缩（见 .tt-tree 里那段）。夹住两头：小了读不出来，
     大了在宽屏上会撑破枝间距。

     ⚠️ **必须排在 `font: inherit` 后面。** 写在前面的那一版整整白写了一轮：
     `font` 是简写，它会把 font-size 一起重置回继承来的 16px，
     而浏览器不会为此出任何声。 */
  font-size: clamp(9.5px, 3.3cqw, 15px);
  cursor: var(--cur-pointer);
}
.view[data-view="atlas"] .tt-disc {
  width: 2.7em;
  height: 2.7em;
  display: grid;
  place-items: center;
  padding: 0.42em;
  border-radius: 50%;
  /* 平的：一个可选项。
     ⚠️ 实测这一屏的 `--stone-face` 是 #0f1b1f，往 deep 那边再兑一手就和
     碑面同一个值了——那时候"能点"读起来是**一个洞**，不是一块石头。
     所以这一档要往 face 之上再提一点，而不是往下压。 */
  background: color-mix(in srgb, var(--stone-face) 92%, var(--stone-lit));
  box-shadow:
    inset 0 1px 0 color-mix(in srgb, var(--stone-lit) 16%, transparent),
    0 1px 0 rgba(0, 0, 0, 0.6);
  color: color-mix(in srgb, var(--stone-lit) 72%, var(--ink-dim));
}
/* 还锁着：凹进去的暗窝。 */
.view[data-view="atlas"] .tt-node.is-locked .tt-disc {
  background: var(--stone-seam);
  box-shadow: inset 0 2px 5px -1px rgba(0, 0, 0, 0.8);
  color: color-mix(in srgb, var(--ink-faint) 80%, transparent);
}
/* 点亮了：凸出 + 冷光。 */
.view[data-view="atlas"] .tt-node.is-on .tt-disc {
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--stone-face) 84%, var(--stone-lit)) 0%,
    var(--stone-face) 100%);
  box-shadow:
    inset 0 1px 0 color-mix(in srgb, var(--stone-lit) 40%, transparent),
    0 1px 0 var(--stone-seam),
    0 0 12px -2px color-mix(in srgb, var(--stone-lit) 46%, transparent);
  color: color-mix(in srgb, var(--stone-lit) 88%, #fff);
}
/* 选中的那一颗：**加一圈沿**，不换材质——选中说的是"你正在看它"，
   不是"它的状态变了"，两件事不能长一个样。 */
.view[data-view="atlas"] .tt-node.is-picked .tt-disc {
  outline: 1px solid color-mix(in srgb, var(--stone-lit) 62%, transparent);
  outline-offset: 3px;
}
.view[data-view="atlas"] .tt-node:focus-visible .tt-disc {
  outline: 2px solid color-mix(in srgb, var(--stone-lit) 86%, #fff);
  outline-offset: 3px;
}
.view[data-view="atlas"] .tt-label {
  font-size: var(--fs-xs);
  letter-spacing: 0.04em;
  white-space: nowrap;
}
.view[data-view="atlas"] .tt-node.is-on .tt-label {
  text-shadow: 0 0 6px color-mix(in srgb, var(--stone-lit) 40%, transparent);
}
.view[data-view="atlas"] .tt-node.is-locked .tt-label { color: var(--ink-faint); }
/* 价钱不在树上（账在 talentView 的 treeHtml 上面）：它在下面那块牌子里。 */

/* ---- 下面那块牌子：选中那一颗的说明 + 唯一那个"点亮" ----
   树上摆得下六个图标，摆不下六段说明加六个按钮。 */
.view[data-view="atlas"] .tt-plaque {
  display: grid;
  gap: var(--sp-2);
  padding: var(--sp-3);
  border-radius: 1px;
  background: var(--stone-deep);
  box-shadow:
    inset 0 2px 4px -1px rgba(0, 0, 0, 0.6),
    inset 0 -1px 0 color-mix(in srgb, var(--stone-lit) 10%, transparent);
}
.view[data-view="atlas"] .tt-p-head {
  display: flex;
  align-items: baseline;
  gap: var(--sp-2);
  flex-wrap: wrap;
}
/* 枝名：一个小方牌，说清这一颗长在哪一枝上。 */
.view[data-view="atlas"] .tt-p-branch {
  padding: 1px 7px;
  border-radius: 1px;
  font-size: var(--fs-xs);
  letter-spacing: 0.12em;
  color: color-mix(in srgb, var(--stone-lit) 70%, var(--ink));
  background: color-mix(in srgb, var(--stone-face) 70%, var(--stone-deep));
  box-shadow: inset 0 1px 0 color-mix(in srgb, var(--stone-lit) 10%, transparent);
}
.view[data-view="atlas"] .tt-p-name {
  font-weight: var(--fw-bold);
  letter-spacing: 0.04em;
}
.view[data-view="atlas"] .tt-p-cost {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-variant-numeric: tabular-nums;
  color: color-mix(in srgb, var(--stone-lit) 80%, #fff);
}
.view[data-view="atlas"] .tt-p-done {
  color: color-mix(in srgb, var(--stone-lit) 66%, var(--ink-dim));
}
.view[data-view="atlas"] .tt-p-desc { margin: 0; font-size: var(--fs-sm); color: var(--ink-dim); }
/* 为什么点不了。**写出来**——只变灰的按钮会被当成坏了。 */
.view[data-view="atlas"] .tt-p-why { margin: 0; font-size: var(--fs-xs); color: var(--ink-faint); }

/* 「点亮」那颗：凸出的一小块石片，按下去翻成凹进。 */
.view[data-view="atlas"] .tt-buy {
  justify-self: start;
  padding: 4px 14px;
  border: 0;
  border-radius: 1px;
  color: inherit;
  font: inherit;
  font-size: var(--fs-xs);
  letter-spacing: 0.08em;
  cursor: var(--cur-pointer);
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--stone-face) 88%, var(--stone-lit)) 0%,
    var(--stone-face) 100%);
  box-shadow:
    inset 0 1px 0 color-mix(in srgb, var(--stone-lit) 26%, transparent),
    0 1px 0 var(--stone-seam),
    0 2px 5px -3px rgba(0, 0, 0, 0.85);
}
.view[data-view="atlas"] .tt-buy:active:not([disabled]) {
  transform: translateY(1px);
  background: var(--stone-deep);
  box-shadow: inset 0 2px 4px -1px rgba(0, 0, 0, 0.7);
}
/* 点不了：平的。**只降对比、不降到读不出来**——为什么点不了写在牌子上，
   那句话得有人看得见才有用。 */
.view[data-view="atlas"] .tt-buy[disabled] {
  cursor: not-allowed;
  color: var(--ink-faint);
  background: color-mix(in srgb, var(--stone-face) 56%, var(--stone-deep));
  box-shadow: inset 0 1px 0 color-mix(in srgb, var(--stone-lit) 6%, transparent);
}

.view[data-view="atlas"] .tt-empty {
  margin: 0;
  color: var(--ink-faint);
  font-size: var(--fs-sm);
}

/* ==================== 派遣：**碑墙下面的一排格子** ====================

   结构和账在 js/ui/views/dispatchView.js。这里只管它是什么材料。

   这一屏没有一盏暖灯（css/atlas.css 上面那段），所以收着的那几件发的是
   刻纹那种冷光，和天赋点亮的节点同一种——它们在这一屏上是同级的两件事。

   ---- 收藏那面墙：**空格子也要看得见** ----

   没拿到的格子是**凹进去的暗格**（凹进在这套语言里就是"这里还没有东西"），
   而且不写它是什么。看得见"这儿还有一格"是收集的全部动力；而提前把内容
   说出来，那一格就没什么可收的了。

   ⚠️ 少见的那件拿到之后**边上多一道冷沿**，不是换个颜色。换颜色会撞上
   「换底不换语义色」——这一屏的语义色留给词条那三档，稀有不能来抢。 */

.view[data-view="atlas"] .dispatch-board { display: grid; gap: var(--sp-4); }

/* ---- 在外面的那几趟 ---- */
.view[data-view="atlas"] .db-trips {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--sp-2);
}
.view[data-view="atlas"] .db-trip {
  display: flex;
  align-items: baseline;
  gap: var(--sp-2);
  flex-wrap: wrap;
  padding: 8px 10px;
  border-radius: 1px;
  /* 还在路上：平的一块。它不是一个可点的东西，也不是一个空槽。 */
  background: color-mix(in srgb, var(--stone-face) 72%, var(--stone-deep));
  box-shadow: inset 0 1px 0 color-mix(in srgb, var(--stone-lit) 8%, transparent);
}
/* 回来了：**凸出**——这一屏唯一在等玩家动手的东西该自己站出来。 */
.view[data-view="atlas"] .db-trip.is-ready {
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--stone-face) 84%, var(--stone-lit)) 0%,
    var(--stone-face) 100%);
  box-shadow:
    inset 0 1px 0 color-mix(in srgb, var(--stone-lit) 32%, transparent),
    0 1px 0 var(--stone-seam),
    0 3px 8px -4px rgba(0, 0, 0, 0.9);
}
.view[data-view="atlas"] .db-fish { font-weight: var(--fw-bold); letter-spacing: 0.04em; }
.view[data-view="atlas"] .db-where { color: var(--ink-dim); font-size: var(--fs-sm); }
.view[data-view="atlas"] .db-left {
  margin-left: auto;
  font-size: var(--fs-xs);
  font-variant-numeric: tabular-nums;
  color: var(--ink-faint);
}
.view[data-view="atlas"] .db-trip.is-ready .db-left {
  color: color-mix(in srgb, var(--stone-lit) 80%, #fff);
  text-shadow: 0 0 6px color-mix(in srgb, var(--stone-lit) 34%, transparent);
}
.view[data-view="atlas"] .db-claim {
  padding: 3px 12px;
  border: 0;
  border-radius: 1px;
  color: inherit;
  font: inherit;
  font-size: var(--fs-xs);
  cursor: var(--cur-pointer);
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--stone-face) 92%, var(--stone-lit)) 0%,
    var(--stone-face) 100%);
  box-shadow:
    inset 0 1px 0 color-mix(in srgb, var(--stone-lit) 26%, transparent),
    0 1px 0 var(--stone-seam),
    0 2px 5px -3px rgba(0, 0, 0, 0.85);
}
.view[data-view="atlas"] .db-claim:active {
  transform: translateY(1px);
  background: var(--stone-deep);
  box-shadow: inset 0 2px 4px -1px rgba(0, 0, 0, 0.7);
}

/* ---- 收藏那面墙 ---- */
.view[data-view="atlas"] .db-count {
  margin: 0 0 var(--sp-2);
  font-size: var(--fs-xs);
  font-variant-numeric: tabular-nums;
  color: var(--ink-faint);
}
.view[data-view="atlas"] .db-scene + .db-scene { margin-top: var(--sp-3); }
.view[data-view="atlas"] .db-scene-name {
  margin: 0 0 var(--sp-1);
  font-size: var(--fs-sm);
  letter-spacing: 0.12em;
  color: color-mix(in srgb, var(--stone-lit) 66%, var(--ink));
}
.view[data-view="atlas"] .db-items {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(9em, 1fr));
  gap: var(--sp-2);
}
/* 还没拿到：凹进去的暗格。 */
.view[data-view="atlas"] .db-item {
  display: grid;
  gap: 2px;
  /* 收着的那一格是「图 + 名 + 数 + 那句话」，四行；空格子只有一个「？」。
     两者**同高**：一面墙上高低不齐的格子读起来像坏了，而不像还没收齐。 */
  grid-template-rows: auto auto auto 1fr;
  min-height: 7.2em;
  padding: 7px 9px;
  border-radius: 1px;
  background: var(--stone-seam);
  box-shadow: inset 0 2px 4px -1px rgba(0, 0, 0, 0.7);
  color: var(--ink-faint);
}
/* 收着了：凸出的一小块，名字迎着冷光。 */
.view[data-view="atlas"] .db-item.is-got {
  color: var(--ink);
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--stone-face) 84%, var(--stone-lit)) 0%,
    var(--stone-face) 100%);
  box-shadow:
    inset 0 1px 0 color-mix(in srgb, var(--stone-lit) 30%, transparent),
    0 1px 0 var(--stone-seam),
    0 3px 8px -4px rgba(0, 0, 0, 0.9);
}
/* 少见的那件：多一道沿，不换颜色（理由在这一段开头）。 */
.view[data-view="atlas"] .db-item.is-rare.is-got {
  box-shadow:
    inset 0 0 0 1px color-mix(in srgb, var(--stone-lit) 38%, transparent),
    inset 0 1px 0 color-mix(in srgb, var(--stone-lit) 44%, transparent),
    0 1px 0 var(--stone-seam),
    0 3px 10px -4px rgba(0, 0, 0, 0.9);
}
/* 那件东西的画像。**只在收着的格子里出现**（dispatchView：给了图标就等于
   把答案写在问题上）。 */
.view[data-view="atlas"] .db-item-art {
  width: 2.4em;
  height: 2.4em;
  display: block;
  color: color-mix(in srgb, var(--stone-lit) 78%, var(--ink));
}
.view[data-view="atlas"] .db-item:not(.is-got) .db-item-art {
  width: auto;
  height: auto;
  color: var(--ink-faint);
}
.view[data-view="atlas"] .db-item-name {
  font-weight: var(--fw-bold);
  letter-spacing: 0.04em;
}
.view[data-view="atlas"] .db-item.is-got .db-item-name {
  text-shadow: 0 0 6px color-mix(in srgb, var(--stone-lit) 40%, transparent);
}
/* 空格子里那个「？」摆正中——它是一个位置，不是一条内容。
   ⚠️ 行模板也要跟着改回一行：收着的那一格是四行，空格子只有一个字，
   照着四行摆的话那个「？」会贴在格子顶上。 */
.view[data-view="atlas"] .db-item:not(.is-got) {
  grid-template-rows: 1fr;
  place-content: center;
  text-align: center;
}
.view[data-view="atlas"] .db-item-n {
  font-size: var(--fs-xs);
  font-variant-numeric: tabular-nums;
  color: var(--ink-dim);
}
.view[data-view="atlas"] .db-item-desc {
  font-size: var(--fs-xs);
  line-height: 1.45;
  color: var(--ink-dim);
}

.view[data-view="atlas"] .db-empty {
  margin: 0;
  color: var(--ink-faint);
  font-size: var(--fs-sm);
}
