/* ============================================================
   口袋學院物語2（school2）長頁閱讀排版 — guide.css?v=8

   只有 school2 的攻略頁引入，**自帶版號** → 改本檔只 bump 本地號，不必動共用軌的
   `?v`（反之全站 bump 時也不要順手把本地號一起改；`link-check.js` 第 6 節照兩軌
   各自斷言）。`guide.css` 與 `guide.js` 兩者必須同號。
   本檔只用既有 token（--brand / --ink / --line…）推導，深色模式因此免費。

   引入方式（head，兩行）：
       <link rel="stylesheet" href="../assets/guide.css?v=8">
       <script src="../assets/guide.js?v=8" defer></script>
   （`defer` 會在 DOM 解析完、Shell.mount() 之後才跑 → 量得到 --hdr-h、
     也找得到 shell.js 注入的浮動 ☰ 目錄。）

   本檔提供的 class 契約（其他頁面照這些名字寫就會有樣式）：
     .path-rail    主線 6 站的位置指示器（不是導覽列）
     .next-step    內容結束、延伸閱讀之前的「下一步 →」大卡
     .sec-num      h2 內的章節編號（<span class="sec-num">01</span>）
     .sec-lead     h2 之後一行 15–25 字的章節摘要
     .datablock    20–80 列的完整資料表容器（內捲 → sticky thead 生效）
     .zone-group   <details>/<summary> 手風琴
     .kpi-row      KPI 統計磚（取代 5 個鎮頁各自的頁內 .statgrid）
     .mapwrap      佈局地圖 SVG 的橫捲圖框（.wide＝26×26 的 612px 版）
     .fig-cap      圖說
     .zone/.zone-list  分區條目（前者四鎮的 div 版、後者湖岸的 ul 版）
     .note         施工旁註（比正文小一級、左邊線）
     .town-pills   五鎮完美佈局的橫向切換藥丸列
     .choice-card  兩選一決策卡（左右兩欄＋差價＋切換條件）
     .num5/.rank-look  反查工具的小輸入控件（掛 db 分類頁）
     .toc-rail     ≥1568px 才出現的側邊目錄軌（由 guide.js 自動生成；出現時收掉浮動 ☰）
     .read-progress 頁內捲動進度線（由 guide.js 自動生成）
     .sc-*         景點檢查器 widget（自 spot-check/ 頁內 style 搬來）

   ── 8bit 換皮落在哪 ──────────────────────────────────
   §13 是「形狀語言」的總覆寫（callout／btn／db-* 等 shell.css 元件），
   本檔既有各節（§2 §5 §6 §6b §8 §10）則就地改自己的元件。分界很簡單：
   **shell.css 的元件在 §13，guide.css 自己的元件在原節**。
   ============================================================ */


/* ---------- 0. 錨點落點修復（P0） ------------------------------------------
   shell.css:237 有正確值 `calc(var(--hdr-h) + 68px)`，但同檔 348/350 行的
   `.prose h2/h3 { scroll-margin-top: 72px }` 同特異度且在後面 → 蓋掉它。
   實際 sticky 高度是 106px（site-header 55 ＋ game-bar 51），72px 不夠，
   點目錄後標題會躲在功能列後面。用 `main.article` 提高特異度覆寫，不動共用檔。
   details / datablock 也要：手風琴與資料表都會被跨頁錨點指到。
--------------------------------------------------------------------------- */
main.article .prose h2[id],
main.article .prose h3[id],
main.article .prose h4[id],
main.article [id].datablock,
main.article details[id],
main.article section[id],
main.article h2[id],
main.article h3[id] {
    scroll-margin-top: calc(var(--hdr-h, 55px) + 62px);
}


/* ---------- 1. 捲動進度線（無狀態） ----------------------------------------
   由 guide.js 建立並以 rAF 更新 `--rp`（0–1）。刻意不加 transition：
   加了會落後手指；平滑感來自 scaleX 的次像素而非過渡。
   量的是 .article 的範圍而非整份文件——否則讀完內文時線只走到七成。
   z-index 45：壓過 site-header(40) 與 game-bar(35)，但讓「更多▾」下拉(50)
   與浮動 ☰(60) 蓋住它。
--------------------------------------------------------------------------- */
.read-progress {
    position: fixed;
    inset: 0 0 auto 0;
    height: 2px;
    z-index: 45;
    pointer-events: none;
    background: transparent;
}
.read-progress i {
    display: block;
    height: 100%;
    background: var(--brand);
    transform: scaleX(var(--rp, 0));
    transform-origin: 0 50%;
    will-change: transform;
}


/* ---------- 2. .path-rail：主線 6 站位置指示器 -----------------------------
   是位置指示器，不是導覽列（往上＝麵包屑、橫向＝game-bar 已各自負責）。
   手機必然橫捲（6 站中文標籤約 520–600px）→ guide.js 會把當前站捲到中央，
   並在可捲時加 .can-x（右緣淡出當「還有內容」的暗示）。
--------------------------------------------------------------------------- */
.path-rail {
    margin: 14px 0 18px;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
    scroll-snap-type: x proximity;
}
.path-rail::-webkit-scrollbar { display: none; }
.path-rail ol {
    display: flex;
    align-items: stretch;
    gap: 0;
    margin: 0;
    padding: 0;
    list-style: none;
    width: max-content;
    min-width: 100%;
}
.path-rail li { display: flex; margin: 0; }
.path-rail a,
.path-rail .cur {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    min-height: 44px;
    padding: 8px 12px 8px 10px;
    white-space: nowrap;
    font-size: 13px;
    font-weight: 800;
    color: var(--ink-soft);
    background: var(--card);
    border: 1px solid var(--line);
    border-right: 0;
    scroll-snap-align: center;
}
.path-rail li:first-child a,
.path-rail li:first-child .cur { border-radius: var(--radius-sm) 0 0 var(--radius-sm); }
.path-rail li:last-child a,
.path-rail li:last-child .cur { border-right: 1px solid var(--line); border-radius: 0 var(--radius-sm) var(--radius-sm) 0; }
.path-rail a:hover { background: var(--brand-soft); color: var(--brand-strong); text-decoration: none; }
.path-rail b {
    display: grid;
    place-items: center;
    width: 20px;
    height: 20px;
    flex: 0 0 auto;
    /* 2px（不是 5px）：pixel.css 的 `--radius-sm: 2px` 換值救得到 .path-rail 的首尾段
       （:120/:122 走 token），救不到這 6 顆寫死 5px 的編號晶片。而 .path-rail 是
       進頁後視線的第二個落點（h1 之下），一秒內的畫面裡 6 顆圓角小方塊嵌在直角
       分段軌裡是最容易被看見的殘留。
       刻意**不**給它像素字型：11.5px 的 Silkscreen 落在非 8 倍數尺寸，字幹不勻。 */
    border-radius: 2px;
    background: var(--card-2);
    border: 1px solid var(--line);
    color: var(--muted);
    font-size: 11.5px;
    font-weight: 900;
    line-height: 1;
    font-variant-numeric: tabular-nums;
}
/* 當前站：實心（＋markup 上的 aria-current="step"） */
.path-rail [aria-current="step"] {
    background: var(--brand);
    border-color: var(--brand);
    color: #fff;
}
.path-rail [aria-current="step"] b {
    background: rgba(255, 255, 255, .22);
    border-color: transparent;
    color: #fff;
}
/* 主線之外的專題分支（例如 combo/）：與主線同一條軌，但標成分支 */
.path-rail .branch[aria-current="step"] {
    background: var(--brand-strong);
    border-color: var(--brand-strong);
}
.path-rail.can-x {
    -webkit-mask-image: linear-gradient(90deg, #000 86%, transparent);
    mask-image: linear-gradient(90deg, #000 86%, transparent);
}


/* ---------- 3. 章節編號 ＋ 章節摘要 ----------------------------------------
   紅線：`.prose` 內 p/li/h2/h3 的字級/行高/字距/margin 一律不動。
   編號做成 h2 內的 block 子元素（不是前置兄弟）——`.prose h2` 有
   `border-top:2px`，做成兄弟會讓那條線插在編號與標題之間把兩者切開；
   也刻意不用 inline-block，否則標題換行會齊到編號下方。
--------------------------------------------------------------------------- */
main.article .prose h2 { position: relative; }
.sec-num {
    display: block;
    font-size: 12px;
    line-height: 1;
    letter-spacing: .08em;
    font-weight: 900;
    color: var(--muted);
    margin-bottom: 5px;
    font-variant-numeric: tabular-nums;
}
.sec-lead {
    margin: -2px 0 14px;
    color: var(--ink-soft);
    font-size: 15px;
    font-weight: 700;
}


/* ---------- 4. .datablock：20–80 列的完整資料表 ----------------------------
   標頭含表名／筆數／篩選框／「在資料庫開啟 →」；`.table-wrap` 拿到
   max-height 之後，shell.css 既有的 `table.data thead th{position:sticky}`
   才終於有滾動容器可貼（在此之前 `.table-wrap` 只有 overflow-x，永遠不會
   縱向捲動 → sticky 是死碼）。
   高度用 min(62vh,520px) 而非 70vh：70vh ＋ 106px sticky 必然 >100vh，
   整塊永遠無法完整落在功能列下方；iOS 的 vh 又取工具列收起時的大視窗。
--------------------------------------------------------------------------- */
.datablock {
    position: relative;
    margin: 20px 0 22px;
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    background: var(--card-2);
    overflow: hidden;
}
.dbk-h {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px 10px;
    padding: 9px 12px;
    border-bottom: 1px solid var(--line);
    background: var(--card-2);
}
.dbk-t { font-size: 14px; font-weight: 900; color: var(--ink); }
.dbk-n { font-size: 12.5px; font-weight: 700; color: var(--muted); font-variant-numeric: tabular-nums; }
.dbk-f {
    flex: 1 1 160px;
    min-width: 130px;
    font: inherit;
    font-size: 16px;          /* <16px 會讓 iOS 聚焦時自動放大整頁 */
    min-height: 44px;
    padding: 8px 11px;
    border: 1px solid var(--line);
    border-radius: 8px;
    background: var(--card);
    color: var(--ink);
}
.dbk-f:focus-visible { outline: 2px solid var(--brand); outline-offset: 1px; }
.dbk-open { margin-left: auto; font-size: 13px; font-weight: 800; white-space: nowrap; }
.datablock .db-embed { margin: 0; }
.datablock .table-wrap {
    margin: 0;
    border: 0;
    border-radius: 0;
    max-height: min(62vh, 520px);
    overflow: auto;
    overscroll-behavior: contain;   /* 捲到底不要把整頁帶走 */
}
@supports (height: 100dvh) {
    .datablock .table-wrap { max-height: min(62dvh, 520px); }
}
/* 單元素雙軸（同一個 .table-wrap 縱橫都捲）→ 不會有巢狀捲動劫持。
   解除 shell.css 的 min-width:460px，否則窄表在手機仍會左右漂移、
   直向滑動變成斜著跑；真的需要寬表時加 .datablock--wide。 */
.datablock table.data { min-width: 0; }
.datablock--wide table.data { min-width: 640px; }

/* ---------- 4b. 欄寬模型：讀的欄窄、查的表寬，而且**左緣對齊** -------------
   760px 是給**正文**的（中文約 40 字/行是舒適上限，行太長回行容易跳行），
   但資料表不是正文：`.container-narrow` 扣掉左右 padding 只剩 720px，
   7 欄的老師表放進去名稱一定折行。

   做法不是讓表格「往外出血」——那會造成表格左側凸出、與正文不對齊，看起來
   像壞掉。正確做法是**把容器加寬，讓正文自己限寬**：
     容器（main.article）→ 1080px
     正文元素（p/ul/ol/callout/h2/h3/toc/lead…）→ 自己 max-width 760px、靠左
     資料表與圖（.datablock / #dbApp / .table-wrap / figure / .mapwrap）→ 吃滿容器
   於是兩者**左緣對齊**、表格只往右長，不會有任何東西凸出去。 */
main.article { max-width: 1080px; }

/* 正文限寬（只限直系子元素，表格內部的文字不受影響） */
main.article > .lead,
main.article > .meta,
main.article > .toc,
main.article > .callout,
main.article > .path-rail,
main.article > h1,
main.article > .prose > h2,
main.article > .prose > h3,
main.article > .prose > h4,
main.article > .prose > p,
main.article > .prose > ul,
main.article > .prose > ol,
main.article > .prose > blockquote,
main.article > .prose > .callout,
main.article > .prose > .zone-group,
main.article > .prose > .next-step,
/* 這兩個原本漏了，兩處都造成「比它旁邊的正文寬 320px」的錯位：
   · .toc  —— 8 頁是 main.article 直系子（已限寬），但 4 個佈局頁的目錄包在
     .prose 裡 → 那 4 頁的目錄寬 1080px。目錄是「讀」的，本來就該在這張清單裡。
   · .fig-cap —— 它是圖說（散文）。.mapwrap 收成 fit-content（588/632px）
     之後，1080px 的圖說會比它說明的那張圖寬 450px 以上，讀起來像跑掉了。 */
main.article > .prose > .toc,
main.article > .prose > .fig-cap {
    max-width: 760px;
    margin-right: auto;
}
/* 寬元素吃滿容器：這幾類是「查」的，不是「讀」的。
   .mapwrap 刻意**不在**這張清單裡：它是一張有固定尺寸的圖，
   圖框要收成 fit-content 剛好包住圖，理由見 §6b。 */
main.article > .prose > .datablock,
main.article > .prose > .db-embed,
main.article > .prose > .table-wrap,
main.article > .prose > figure,
main.article > .prose > .kpi-row,
main.article > #dbApp,
main.article > .db-prose > .table-wrap {
    max-width: none;
    width: 100%;
}
/* 名稱欄不要折行：讀者是拿名字對照的，「森野熊克斯」斷成兩行比橫向捲動更難讀。
   欄寬不足時交給 `.table-wrap` 的橫捲（它本來就是雙軸捲動容器）。 */
.datablock table.data thead th:first-child,
.datablock table.data tbody td:first-child,
#dbApp table thead th:first-child,
#dbApp table tbody td:first-child { white-space: nowrap; }
/* border-collapse 下 sticky th 的 border-bottom 會隨內容捲走，資料列會從
   標頭底下穿出來。用一條 inset 陰影補線（刻意只用一條、不做階梯陰影：
   sticky 元素上的多層陰影會在捲動時每影格重繪整條表頭）。
   從 1px 改成 2px 的理由：同一個 .datablock 裡 .dbk-h 的分隔線已是 2px，表頭再用
   1px 就是同一個盒子裡兩種線寬。這是**改既有那一層 inset 的粗細**，沒有新增任何
   陰影層、沒有新增 border → 重繪成本與 1px 完全相同，不觸犯「sticky th 不得加
   border/box-shadow」那條紅線。 */
.datablock table.data thead th {
    box-shadow: inset 0 -2px 0 var(--line);
    z-index: 2;
}
.datablock figcaption { padding: 0 12px 10px; }
.dbk-empty { padding: 14px 12px; font-size: 13.5px; color: var(--muted); }
.dbk-empty button {
    font: inherit; font-size: 13px; font-weight: 800; margin-left: 8px;
    min-height: 32px; padding: 4px 10px; cursor: pointer;
    border: 1px solid var(--line); border-radius: 8px;
    background: var(--card); color: var(--brand-strong);
}
/* 可排序表頭 */
.datablock table.data thead th[data-sort] { cursor: pointer; user-select: none; }
.datablock table.data thead th[data-sort]:hover { color: var(--brand-strong); }
.datablock table.data thead th[aria-sort="ascending"]::after { content: ' ▲'; font-size: 9px; }
.datablock table.data thead th[aria-sort="descending"]::after { content: ' ▼'; font-size: 9px; }


/* ---------- 4c. 所有長表都要有黏住的表頭 -----------------------------------
   `shell.css:372` 早就寫了 `table.data thead th { position: sticky; top: 0 }`，
   但 `.table-wrap` 只有 `overflow-x: auto`、**永遠不會縱向捲動**，sticky 因此
   沒有可貼的滾動容器 → 那行是死碼。只有 `.datablock`（有 max-height）例外。
   於是資料庫分類頁（77 列設施、65 列學生、60 列進路）與城鎮頁的長表，
   往下滑就看不到欄位定義了。

   修法：把 `.datablock` 的那套（max-height ＋ 雙軸捲動 ＋ 補底線）套到 school2
   底下**所有**表格容器，包含 `#dbApp` 裡由 db.js 產生的那些。
   注意 max-height 只在內容超過時才生效 → 5 列的小表不會多出捲軸。

   為什麼不改成「貼視窗」（top: 106px、用整頁捲動）：那需要移除 `.table-wrap`
   的 overflow-x，但 9 欄的課題表與 8 欄的老師表在 1080px 下仍會超寬，
   移掉橫捲會讓表格溢出容器、甚至讓整頁長出橫向捲軸。所以維持容器內捲。 */
main.article .table-wrap,
main.article #dbApp .table-wrap {
    max-height: min(62vh, 520px);
    overflow: auto;
    overscroll-behavior: contain;
}
@supports (height: 100dvh) {
    main.article .table-wrap,
    main.article #dbApp .table-wrap { max-height: min(62dvh, 520px); }
}
/* 補 sticky th 被捲走的底線（border-collapse 下 border 不跟著黏），
   並確保表頭底色不透，否則資料列會從標頭底下透出來。 */
main.article table.data thead th,
main.article #dbApp table thead th {
    background: var(--card-2);
    box-shadow: inset 0 -2px 0 var(--line);   /* 與上面同一個理由，兩處必須同號 */
    z-index: 2;
}


/* ---------- 5. .zone-group：手風琴（只給查閱型內容，不給機制解說） ---------
   2px 硬邊（Tier 2「框住」——你穿過它閱讀，所以**不給階梯陰影**，理由見 §13）。
   邊色走 --px-edge-soft 而非滿的 --px-edge：一頁最多 15 個手風琴，全上滿會變黑格網。
--------------------------------------------------------------------------- */
.zone-group {
    border: 2px solid var(--px-edge-soft);
    border-radius: var(--radius-sm);
    background: var(--card);
    margin: 10px 0;
    overflow: hidden;
}
.zone-group > summary {
    display: flex;
    align-items: center;
    gap: 10px;
    min-height: 44px;
    padding: 10px 13px;
    cursor: pointer;
    font-size: 14.5px;
    font-weight: 800;
    background: var(--card-2);
    list-style: none;
}
.zone-group > summary::-webkit-details-marker { display: none; }
.zone-group > summary::after {
    content: '▾';
    margin-left: auto;
    color: var(--muted);
    font-size: 12px;
}
.zone-group[open] > summary::after { content: '▴'; }
.zone-group > summary:hover { background: var(--brand-soft); }
.zone-group > summary:focus-visible { outline: 2px solid var(--brand); outline-offset: -2px; }
/* 展開時把標頭與內容分開。用 **inset 陰影**而不是 border-bottom：border 會進 layout，
   展開的那一瞬間使用者的眼睛正好在那一行下方等內容出現，讓他要讀的東西跳 2px 是零收益
   的傷害。inset 外觀相同、零位移，而且是本檔已經在用的手法（見上面 sticky th 的補線）。 */
.zone-group[open] > summary { box-shadow: inset 0 -2px 0 var(--px-edge-soft); }
/* 按壓回饋：**齊邊元件不用位移**。summary 是齊邊貼在 `overflow: hidden` 的容器裡，
   translate(2px,2px) 會讓左上緣露出容器底色、右緣被 clip 切掉、外投影整條被吃掉
   —— 結果不是「按下去」而是「這一塊破了」。改用底色下沉，順帶免疫 reduced-motion。
   **這是一條通則**：本檔所有位在 `overflow: hidden` 容器內的齊邊子元件都照這個做。 */
.zone-group > summary:active { background: color-mix(in srgb, var(--ink) 8%, var(--card-2)); }
.zone-group .zg-n { margin-left: auto; font-size: 12.5px; font-weight: 700; color: var(--muted); }
.zone-group > summary:has(.zg-n)::after { margin-left: 8px; }
.zone-group .zg-body { padding: 4px 14px 12px; }
.zone-group .sw {
    width: 14px; height: 14px; flex: 0 0 auto;
    border-radius: 2px;   /* 與 .zone .zmeta i 同號（同一頁上的同一種色票） */
    border: 1px solid color-mix(in srgb, var(--ink) 20%, transparent);
}


/* ---------- 6. .kpi-row：KPI 統計磚 ----------------------------------------
   與 5 個鎮頁各自頁內 style 的 `.statgrid` 視覺等價（那是 8.6KB 近重複的
   技術債）→ 鎮頁把 class 換成 kpi-row、刪掉頁內 style 即可，零視覺變化。
   大數字留在 --brand-strong：--accent 對白只有 1.99:1，絕對不能當數字色。
   KPI 是資料不是連結，不給 hover 位移。
--------------------------------------------------------------------------- */
.kpi-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
    gap: 10px;
    margin: 14px 0;
}
/* Tier 1「浮起」。這是全站最像 HUD 的一塊 —— 9 塊同規格的凸起磚排在 grid 裡
   讀起來是**一台儀表板**，不是 9 個互相搶戲的物件，所以它是少數保留階梯陰影的容器。
   底色維持 --card-2（內凹底色是刻意的），不要跟著 .px-frame 的 --card 走。 */
.kpi-row > div {
    background: var(--card-2);
    border: 2px solid var(--px-edge-soft);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow);
    padding: 10px 12px;
}
.kpi-row b {
    display: block;
    font-size: 22px;
    font-weight: 900;
    color: var(--brand-strong);
    line-height: 1.2;
    font-variant-numeric: tabular-nums;
}
.kpi-row span { font-size: 12.5px; color: var(--muted); font-weight: 700; }


/* ---------- 6b. 五個城鎮完美佈局頁的共用元件 -------------------------------
   `layouts/{health,east,lake,valley,hill}/` 五頁原本各自帶一份頁內 <style>，
   逐字近重複（合計 10.4KB）：改一處要改五處，而 lake 那份已經漂走了
   （`.zone-list` 而非 `.zone`、`.fig-cap` 寫成 inline style、留著沒人用的
   `.tp.soon`）。這裡是它們的唯一正本；五頁的頁內 style 因此全部刪除。

   `.statgrid` 不在這裡：它與上一節的 `.kpi-row` 視覺等價，五頁已改用 kpi-row。
--------------------------------------------------------------------------- */

/* 地圖圖框：SVG 原尺寸 568/612px 都比 760px 的正文欄窄，但比手機視窗寬 →
   只需要橫捲，不縮圖（縮到 375px 寬時格子只剩 14px，字全糊掉，不如讓它捲）。
   底色刻意跟 SVG 內的 `<rect fill="#f8fafc">` 同色，圖與框才連成一塊。

   ── 這裡是「SVG 深色模式」那一題的答案（實測後，不是假設）─────────────
   原本的假設是「SVG 裡寫死淺色，深色模式下會看不見」。**實測推翻了它**：10 個
   layouts/*.svg（5 張 perfect ＋ 5 張 thumb）每一張的第一個元素都是全畫布的
   `<rect fill="#f8fafc"/>` → 深色模式下 SVG 自成一塊淺色畫布，座標軸（#94a3b8）、
   圖例（#334155/#64748b）、格子上的設施簡稱全部正常顯示，**沒有一個字看不見**。

   真正壞掉的是這個圖框：它 `background: #f8fafc` 寫死、又在寬元素清單裡吃滿容器
   1080px，而 SVG 只有 568px（26×26 的版本 612px）→ 深色模式下地圖右邊多出一片
   **512×696px 的純白空地**，地圖擠在左三分之一。淺色下看不出來（頁面本來就是淺的），
   深色下是整頁最亮的一塊。修法是讓圖框收成剛好包住圖：白色面積 1080×696 → 588×694
   （−46%），**而且不需要任何深色覆寫**。

   `width: fit-content` 一個宣告就夠（＝`min(available, max-content)`）：捲動容器的
   min-content 是 0，所以窄螢幕會自然縮回容器寬並橫捲，不必再寫 `max-width: 100%`。
   前提是它已經從上面的寬元素清單移出，否則 `main.article > .prose > .mapwrap`
   的 (0,3,1) 會壓過這裡的 (0,1,0)——加前綴打特異度戰是繞遠路，移出清單才是正解。

   border-color 刻意寫死 `#94a3b8`（＝SVG 自己畫座標軸與圖例色塊描邊用的色），
   **不用 --px-edge**：襯邊永遠是淺色的 #f8fafc，而 --px-edge 在淺色是暗的、在深色是
   亮的 → 用 token 會讓這條線在深色模式裡消失在襯邊上。這張圖是「一張有固定色票的
   圖片」（由 scripts/layout-gen/gen-assets.js 用遊戲的格子色產生），圖框是它的襯邊，
   襯邊跟圖同一套色票是裱框的基本原則，不跟 theme 走。

   刻意**不做**的兩件：①`<picture>` ＋ 深色版 SVG —— 5 張 perfect 合計 536KB，再產
   一份是 +536KB 與 5 個要同步的產物，而實測沒有可讀性問題，收益只有「不那麼亮」。
   ②`filter: brightness(.9)` —— 它壓縮亮端，會讓草地與空地兩個淺綠靠攏到分不開，
   同時吃掉 #334155 圖例文字對 #f8fafc 的對比：用全域濾鏡換「不那麼亮」，代價是這張
   圖存在的理由（格子可辨識）。深色模式下一張亮的圖片就是圖片該有的樣子。 */
.mapwrap {
    overflow-x: auto;
    width: fit-content;
    border: 2px solid #94a3b8;
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    background: #f8fafc;
    padding: 10px;
    margin: 14px 0;
}
/* 兩種寬度是地圖尺寸決定的，不是城鎮特例：26×24／24×24 → 568，26×26 → 612。
   `max-width: none` 是要壓過 shell.css 的 `img { max-width: 100% }`，
   否則圖會被壓縮成非整數縮放、格線出現摩爾紋。 */
.mapwrap img { display: block; width: 568px; max-width: none; height: auto; }
.mapwrap.wide img { width: 612px; }
/* margin-top 由 -4px 改 2px：亮塊貼著文字比亮塊本身更刺眼，圖框下緣到圖說要留呼吸。
   （font-size/color 不動——.fig-cap 不在 .prose 的紅線裡，但也沒有理由動它。） */
.fig-cap { font-size: 13px; color: var(--muted); margin: 2px 0 16px; }

/* 分區條目。兩種寫法並存且都要留著：
     .zone      四鎮用的 <div>（h4 標題 ＋ .zmeta 色塊列），分區數 12–17
     .zone-list 湖岸用的 <ul>（.zt 粗體開頭 ＋ 內文同段），分區數只有 4 組
   湖岸的分區描述長到每條 300 字以上，做成 h4 會讓目錄軌長出 20 個雜訊項目。 */
.zone { border-top: 1px solid var(--line); padding: 13px 0 3px; }
.zone h4 { margin: 0 0 5px; font-size: 16px; font-weight: 900; }
.zone .zmeta { display: flex; flex-wrap: wrap; gap: 5px 12px; margin: 0 0 7px; font-size: 12.5px; font-weight: 700; color: var(--muted); }
.zone .zmeta span { display: inline-flex; align-items: center; gap: 5px; }
.zone .zmeta i { width: 10px; height: 10px; border-radius: 2px; border: 1px solid rgba(0, 0, 0, .22); flex: none; }
.zone p { margin: 5px 0; }
.zone-list { list-style: none; padding: 0; margin: 12px 0; }
.zone-list li { border-left: 3px solid var(--brand); padding: 8px 0 8px 12px; margin: 10px 0; }
.zone-list .zt { font-weight: 900; }

/* 施工註記：比正文小一級、靠一條左邊線與正文分開（不是 callout —— 這些是
   「這一格不要蓋」等級的旁註，一頁二十幾條，做成 callout 會蓋掉正文）。 */
.note { font-size: 13.5px; color: var(--ink-soft); border-left: 2px solid var(--line); padding-left: 10px; margin: 8px 0; }

/* 五鎮橫向切換。它是 game-bar 之下的**同層切換**（五張圖互為平行選項），
   不是導覽分層裡的「往上」或「往下」，所以做成同層的按鍵列而不是麵包屑。
   當前頁是 <span> 不是 <a>（沒有連到自己的連結），靠 .on 標實心。

   藥丸（999px）改方角，並且用「凸起／沉入」把「你在哪一鎮」**多編碼一次**
   （原本只靠實心綠底）—— 未選中的 <a> 凸起（Tier 1，可操作物件），當前的 <span>
   沉入。這不是裝飾，是 8bit 分頁列的原生慣用語：選中的那一格是按進去的。
   gap 由 8px 改 10px：加了 3px 階梯陰影之後，窄螢幕換行時上下兩排只剩 5px 淨距。 */
.town-pills { display: flex; flex-wrap: wrap; gap: 10px; margin: 18px 0 6px; }
.town-pills .tp {
    display: inline-flex; flex-direction: column; line-height: 1.25;
    padding: 7px 14px; border-radius: var(--radius-sm);
    border: 2px solid var(--px-edge-soft); background: var(--card-2); color: var(--ink-soft);
    font-weight: 800; font-size: 14px; text-decoration: none;
}
.town-pills .tp i { font-style: normal; font-size: 11px; font-weight: 700; color: var(--muted); }
.town-pills a.tp {
    box-shadow: var(--shadow);
    transition: transform .1s steps(2, end), box-shadow .1s steps(2, end), border-color .1s steps(2, end);
}
.town-pills a.tp:hover { border-color: var(--brand); color: var(--brand-strong); box-shadow: var(--shadow-hover); }
.town-pills a.tp:active { transform: translate(2px, 2px); box-shadow: 2px 2px 0 0 var(--px-drop); }
.town-pills .tp.on { background: var(--brand); border-color: var(--brand); color: #fff; }
.town-pills .tp.on i { color: rgba(255, 255, 255, .8); }


/* ---------- 6c. .rank-rail：學園規模選擇器（now/） -------------------------
   形狀借 .town-pills 的 .tp（獨立方塊、有間隙），機制借 .path-rail 的橫捲。
   為什麼不直接用 .path-rail：那是接合式分段軌，語意是「主線第幾站」；
   規模是玩家自己的狀態，兩者並排出現會被讀成同一條軸。
   為什麼不直接用 .town-pills：它 flex-wrap，六顆在 390px 會折成三排 ≈150px，
   加上 106px 的 sticky 就吃掉第一屏一半。這裡改成不換行＋橫捲，單排 45px。
   副標 <i> 不可省略——只有主標時高度約 31.5px，低於 44px 命中區。
--------------------------------------------------------------------------- */
.rank-rail {
    margin: 14px 0 18px;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
    scroll-snap-type: x proximity;
}
.rank-rail::-webkit-scrollbar { display: none; }
.rank-rail ol {
    display: flex; gap: 10px; margin: 0; padding: 2px;
    list-style: none; width: max-content;
}
.rank-rail li { margin: 0; scroll-snap-align: center; }
.rank-rail .rk {
    display: inline-flex; flex-direction: column; justify-content: center;
    line-height: 1.25; min-height: 44px; padding: 6px 14px;
    border-radius: var(--radius-sm);
    border: 2px solid var(--px-edge-soft); background: var(--card-2); color: var(--ink-soft);
    font-weight: 800; font-size: 14px; text-decoration: none; white-space: nowrap;
    box-shadow: var(--shadow);
    transition: transform .1s steps(2, end), box-shadow .1s steps(2, end), border-color .1s steps(2, end);
}
.rank-rail .rk i { font-style: normal; font-size: 11px; font-weight: 700; color: var(--muted); }
.rank-rail .rk:hover { border-color: var(--brand); color: var(--brand-strong); box-shadow: var(--shadow-hover); text-decoration: none; }
.rank-rail .rk:active { transform: translate(2px, 2px); box-shadow: 2px 2px 0 0 var(--px-drop); }
.rank-rail .rk.on { background: var(--brand); border-color: var(--brand); color: #fff; }
.rank-rail .rk.on i { color: rgba(255, 255, 255, .8); }


/* ---------- 7. .toc-rail：側邊目錄軌（≥1568px） ----------------------------
   幾何：文字欄維持置中的 760px（中文 40 字/行已是舒適上限），軌用絕對定位
   放進右側留白 → 正文不會相對於 1120px 置中的 header/game-bar 左偏。
   內容由 guide.js 從 h2[id]／h3[id] 生成（純導覽，不是內容）。
--------------------------------------------------------------------------- */
.toc-rail { display: none; }
/* 軌要塞進右側留白，而容器是置中的 → 右側留白只有 (vw − 1080)/2。
   (vw + 1080)/2 ＋ 28（gap）＋ 200（軌寬）＋ 16（邊距）≤ vw  →  vw ≥ 1568
   實測 右邊距 = vw/2 − 760：1440 → −40px（被裁）、1568 → +24px。
   舊註解用「1080 ＋ 28 ＋ 200 ＋ 32」算成 1360，那是把整片右側留白都當成可用，
   但容器置中會把一半的留白留在左邊 —— 這就是 1360–1544 之間軌被裁掉的原因。 */
@media (min-width: 1568px) {
    main.article { position: relative; }
    main.article > .toc-rail {
        display: block;
        position: absolute;
        left: calc(100% + 28px);
        top: 0;
        bottom: 0;
        width: 200px;
    }
    .toc-rail nav {
        position: sticky;
        top: calc(var(--hdr-h, 55px) + 62px);
        max-height: calc(100vh - var(--hdr-h, 55px) - 90px);
        overflow: auto;
        padding-left: 14px;
        border-left: 2px solid var(--line);
    }
    .toc-rail .tr-h {
        font-size: 11.5px;
        font-weight: 900;
        letter-spacing: .06em;
        color: var(--muted);
        margin-bottom: 8px;
    }
    .toc-rail ol { list-style: none; margin: 0; padding: 0; }
    .toc-rail a {
        display: -webkit-box;
        padding: 4px 0;
        font-size: 13px;
        line-height: 1.45;
        color: var(--muted);
        overflow: hidden;
        -webkit-line-clamp: 2;
        -webkit-box-orient: vertical;
    }
    .toc-rail a:hover { color: var(--brand-strong); text-decoration: none; }
    .toc-rail li.lv3 a { padding-left: 12px; font-size: 12.5px; }
    .toc-rail a.on { color: var(--brand-strong); font-weight: 800; }
    .toc-rail li.lv2 > a.on { box-shadow: -16px 0 0 -14px var(--brand); }
    /* 軌出現時章節編號懸掛到左留白（position 不屬於字級/行高/字距紅線） */
    .sec-num { position: absolute; left: -52px; top: 15px; margin: 0; }
    /* 側邊目錄軌出現時，右下角浮動的 ☰ 章節目錄是同一份導覽的第二個入口 → 收掉。
       **只收 ☰ 與它的選單，不收整個 `.pagenav`**：那個容器裡還有一顆
       `↑ 回到頂端`（`.pagenav-top`，shell.js 在捲過 400px 後才顯示），它跟章節
       目錄無關，而且螢幕越寬、正文越長，它越有用——連容器一起 display:none
       會把它一起弄掉。`.pagenav-menu.open` 要一起寫：shell.css 的
       `.pagenav-menu.open{display:block}` 同特異度，靠本檔在後面才蓋得掉。
       只在 school2 的 guide.css 收，不動 shell.css（那是 29 款共用的元件）。 */
    .pagenav-btn:not(.pagenav-top) { display: none; }
    .pagenav-menu,
    .pagenav-menu.open { display: none; }
}


/* ---------- 8. .next-step：下一步大卡 --------------------------------------
   放在內容結束、延伸閱讀之前。「← 上一步」小字刻意在大卡之外——放在可點區
   內誤觸就往後跳。
--------------------------------------------------------------------------- */
.next-step { margin: 32px 0 6px; border-top: 1px solid var(--line); padding-top: 18px; }
/* Tier 1「浮起」，而且是全套。這是每一頁唯一的主 CTA（讀完這章要去哪），
   是讀者當天在這一頁的最後一個動作 —— 沒有理由讓它停在 1px 邊。
   修掉的既有缺陷：原本靜止無陰影、hover 才 `box-shadow: var(--shadow-hover)`，
   而且**沒有宣告 transition** → 改成把 --shadow-hover 換成 `5px 5px 0 --px-edge`
   之後，滑過去的瞬間是一個 5px 實色塊**憑空瞬跳**出現。 */
.next-step .ns-card {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 15px 18px;
    border: 2px solid color-mix(in srgb, var(--brand) 45%, var(--line));
    border-radius: var(--radius);
    background: var(--brand-soft);
    color: var(--ink);
    box-shadow: var(--shadow);
    transition: transform .1s steps(2, end), box-shadow .1s steps(2, end);
}
.next-step .ns-card:hover { text-decoration: none; transform: translate(-1px, -1px); box-shadow: var(--shadow-hover); }
.next-step .ns-card:active { transform: translate(2px, 2px); box-shadow: 2px 2px 0 0 var(--px-drop); }
/* 裸 emoji 給像素風容器（與 .db-cat-card .ic 同一組值，理由見 §13）。
   這裡整頁只有 1 個，框它是純收益。 */
.next-step .ns-ic {
    display: inline-grid; place-items: center;
    width: 36px; height: 36px; flex: 0 0 auto;
    border: 2px solid var(--px-drop);
    border-radius: var(--radius-sm);
    background: var(--card-2);
    font-size: 24px; line-height: 1;
}
.next-step .ns-t { font-size: 16.5px; font-weight: 900; color: var(--brand-strong); }
.next-step .ns-why { font-size: 13.5px; color: var(--ink-soft); margin-top: 2px; }
.next-step .ns-prev {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    margin-top: 12px;
    font-size: 12.5px;
    color: var(--muted);
}


/* ---------- 9. 表格手機強化 ------------------------------------------------ */
@media (max-width: 768px) {
    /* 首欄 sticky：橫捲時「這一列是哪個景點／設施」不能跟著跑掉。
       只改 background（不加 border/box-shadow）——border-collapse 下 sticky
       的 border 不會跟著黏，而多層陰影會讓橫捲每影格重繪。 */
    .datablock table.data thead th:first-child,
    .datablock table.data tbody td:first-child {
        position: sticky;
        left: 0;
        background: var(--card);
    }
    .datablock table.data thead th:first-child { background: var(--card-2); z-index: 3; }
    /* 右緣淡出：暗示「右邊還有欄」。標頭列不蓋（top 從標頭下緣起算）。 */
    .datablock.can-x::after {
        content: '';
        position: absolute;
        top: 46px;
        right: 0;
        bottom: 0;
        width: 20px;
        pointer-events: none;
        background: linear-gradient(to left, color-mix(in srgb, var(--card) 88%, transparent), transparent);
    }
    .dbk-open { margin-left: 0; }
}


/* ---------- 10. 景點檢查器 widget（自 spot-check/ 的頁內 style 搬來） -------
   這是攻略首頁上唯一一個讀者會**輸入文字**的互動工具，是那一頁互動意圖最高的
   區塊，而它上下都是 2px 硬邊直角的 callout／datablock，自己卻是一組 8–10px 圓角的
   軟盒子 → 第一眼會讀成「這個 widget 是外來的、或是舊版沒改到的」。
--------------------------------------------------------------------------- */
.sc-tools { display: flex; gap: 8px; margin: 12px 0 8px; }
.sc-tools input {
    flex: 1; font: inherit; font-size: 16px; min-height: 44px;
    padding: 9px 12px; border: 2px solid var(--px-edge);
    border-radius: var(--radius-sm); background: var(--card); color: var(--ink);
}
.sc-tools input:focus-visible { outline: 2px solid var(--brand); outline-offset: 0; }
.sc-tools button {
    font: inherit; font-size: 13px; font-weight: 800; min-height: 44px;
    padding: 0 14px; border: 2px solid var(--px-edge); border-radius: var(--radius-sm);
    background: var(--card-2); color: var(--ink-soft); cursor: pointer;
    box-shadow: var(--shadow);
    transition: transform .1s steps(2, end), box-shadow .1s steps(2, end);
}
.sc-tools button:active { transform: translate(2px, 2px); box-shadow: 2px 2px 0 0 var(--px-drop); }
.sc-res { margin-top: 6px; }
.sc-h { font-weight: 900; font-size: 15px; margin: 16px 0 6px; }
.sc-list { list-style: none; margin: 0; padding: 0; }
/* li 維持 1px 邊：一次會列出十幾條，全上 2px 會變成清單狀的黑格網（同 .zone-group
   的克制）。只把圓角收成直角，形狀語言就到位了。 */
.sc-list li {
    padding: 8px 10px; border-radius: var(--radius-sm); border: 1px solid var(--line);
    margin: 5px 0; font-size: 14px; background: var(--card);
}
.sc-list li.ok {
    border-color: color-mix(in srgb, var(--brand) 65%, var(--line));
    background: var(--brand-soft);
}
.sc-name { font-weight: 900; }
.sc-miss { color: var(--brand-strong); font-weight: 800; }
.sc-bonus { color: var(--muted); font-size: 12.5px; }


/* ---------- 10b. .choice-card：兩選一決策卡（Tier 2，一頁最多 2～3 張） -----
   左右兩欄各一個選項（名稱＋價格）、中間一條 vs 分隔線、底部一條跨欄的切換
   條件。邊寬用滿的 --px-edge（不是 --px-edge-soft）——這是稀有元件，一頁不會
   超過 2～3 張，不吃「8–15 個容器」那條要壓弱邊色的理由。
   `.cc-rec` 標記本頁預設推薦的那一邊：**背景色差只是輔助**，主要靠 `.cc-tag`
   的文字晶片——純色差會撞 WCAG 1.4.1，也會跟 §13d「填色＝選中/贏家」的站內
   慣用語衝突（那條規則是「使用者選的」，這裡是「作者建議的預設」，兩種語意
   混用同一個視覺信號會教壞讀者）。
   手機（≤768px）疊成單欄：vs 分隔線跟著轉成一條橫線，DOM 順序不變——
   不用 grid order 讓 `.cc-rec` 跳到前面，卡片內第一個孩子永遠是第一欄。 */
.choice-card {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    margin: 18px 0;
    border: 2px solid var(--px-edge);
    border-radius: var(--radius-sm);
    background: var(--card);
    overflow: hidden;
}
.choice-card .cc-opt { padding: 14px 16px; }
.choice-card .cc-opt.cc-rec { background: var(--brand-soft); }
.choice-card .cc-vs {
    display: grid;
    place-items: center;
    padding: 0 10px;
    font-size: 11px;
    font-weight: 900;
    letter-spacing: .04em;
    color: var(--muted);
    border-left: 1px solid var(--line);
    border-right: 1px solid var(--line);
}
.choice-card .cc-h {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 7px;
    font-weight: 900;
    font-size: 15px;
    margin-bottom: 5px;
}
.choice-card .cc-cost { font-size: 13px; font-weight: 800; color: var(--brand-strong); font-variant-numeric: tabular-nums; }
.choice-card .cc-tag {
    font-size: 11px;
    font-weight: 800;
    padding: 1px 7px;
    border-radius: 999px;
    background: var(--brand);
    color: #fff;
}
.choice-card .cc-b { margin: 0; font-size: 13.5px; color: var(--ink-soft); }
.choice-card .cc-when {
    grid-column: 1 / -1;
    margin: 0;
    padding: 10px 16px;
    font-size: 13.5px;
    border-top: 1px solid var(--line);
    background: var(--card-2);
}
@media (max-width: 768px) {
    .choice-card { grid-template-columns: 1fr; }
    .choice-card .cc-vs {
        padding: 4px 0;
        border-left: 0;
        border-right: 0;
        border-top: 1px solid var(--line);
        border-bottom: 1px solid var(--line);
    }
}


/* ---------- 10c. 反查工具的兩個小控件（掛 db 分類頁） ----------------------
   `.num5`：五科反查用的五個窄輸入框（52px×5＋4×6px gap＝284px，390px 螢幕
   扣掉容器 padding 後還有餘裕，不必疊行）。`.rank-look`：單一數字輸入＋
   即時結果句——比 `.sc-tools` 更輕，不需要按鈕，輸入即算。
   兩者都借 `.sc-tools input` 的 44px／2px 邊／16px 字級契約（觸控命中區、
   iOS 不因 <16px 字級自動放大）。 */
.num5 { display: flex; flex-wrap: wrap; gap: 6px; margin: 12px 0 6px; }
.num5 label { display: flex; flex-direction: column; align-items: center; gap: 3px; font-size: 11px; font-weight: 800; color: var(--muted); }
.num5 input {
    width: 52px;
    font: inherit;
    font-size: 16px;
    text-align: center;
    min-height: 44px;
    padding: 6px 4px;
    border: 2px solid var(--px-edge);
    border-radius: var(--radius-sm);
    background: var(--card);
    color: var(--ink);
}
.num5 input:focus-visible { outline: 2px solid var(--brand); outline-offset: 0; }
.rank-look { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin: 12px 0 6px; }
.rank-look input {
    width: 90px;
    font: inherit;
    font-size: 16px;
    text-align: center;
    min-height: 44px;
    padding: 6px 8px;
    border: 2px solid var(--px-edge);
    border-radius: var(--radius-sm);
    background: var(--card);
    color: var(--ink);
}
.rank-look input:focus-visible { outline: 2px solid var(--brand); outline-offset: 0; }
.rank-look .rl-unit { font-size: 13px; font-weight: 700; color: var(--muted); }


/* ---------- 11. 浮動 ☰ 目錄的第二層（h3，由 guide.js 補上） ---------------- */
.pagenav-menu a.lv3 { padding-left: 22px; font-size: 12.5px; color: var(--muted); }
.pagenav-menu a.lv3::before { content: '– '; opacity: .6; }


/* ---------- 12. prefers-reduced-motion ------------------------------------
   移除位移（「動」會誘發不適），保留狀態變化（陰影深淺是資訊）。
   全關會讓按鈕失去按下回饋 —— 那是拿無障礙的名義做出更差的無障礙。

   §13 的按壓位移**不是**靠 pixel.css 的 `.px-press` 實作（那個 media block 只選
   `.px-press` class，而我們是以選擇器採用，見 §13 開頭）→ 每一個新增的 transform
   都必須在這裡各補一條。清單要與 §13／§6b／§8／§10 的 `:active`／`:hover` 位移
   逐一對得上，加一個就補一個。
--------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
    .path-rail { scroll-behavior: auto; scroll-snap-type: none; }
    .read-progress i { will-change: auto; }
    /* 位移全部移除、陰影變化全部保留 */
    main.article .btn:active,
    main.article .db-cat-card:active,
    main.article .db-cat-card:hover,
    main.article .town-pills a.tp:active,
    main.article .next-step .ns-card:active,
    main.article .next-step .ns-card:hover,
    main.article .sc-tools button:active,
    main.article .dbk-empty button:active {
        transform: none;
    }
}


/* ============================================================================
   13. 8bit 元件換皮：shell.css 元件的形狀語言覆寫
   ============================================================================
   assets/pixel.css 建好了 token 層：`--px-edge`／`--px-drop`（用 color-mix
   從 --ink/--card 推導，淺深色都過對比）、`main.article { --radius: 2px;
   --shadow: 3px 3px 0 var(--px-drop); --shadow-hover: 5px 5px 0 var(--px-edge) }`。
   本節把那套語言真正套到元件上。**一律不改 assets/shell.css**（29 款共用），
   靠 `main.article` 提高特異度覆寫。

   ── 為什麼不往 HTML 加 .px-frame／.px-ico／.px-press ─────────────────────────
   目標清單裡有一半的 markup 不是手寫的，加不了 class：
     · .db-cat-card / .db-chip / .db-cat-card .ic  → assets/db.js 產生（29 款共用，禁改）
     · .next-step / .datablock                     → gen-embed.js 蓋章區段（手改會被覆蓋）
   半數能加、半數不能加 → 結果是兩套機制並存，比一套選擇器難維護。所以這裡以選擇器
   採用同一組宣告；pixel.css 的三個工具 class 留作**其他 28 款的可攜契約**。
   代價有兩個，都已經處理：
     ① 值有兩份要同步（改這裡要順手看一眼 pixel.css §5，反之亦然）
     ② `.px-press` 的 reduced-motion 逃生門綁在 class 上 → §12 已逐條補齊
   另外**不逐字複製 `.px-frame`**：它帶 `background: var(--card)`，而 .toc／
   .kpi-row > div／.datablock／.dbk-h 現在是刻意的 `--card-2` 內凹底色。只採用
   border / border-radius / box-shadow，`background` 一律不帶。

   ── 誰有資格拿到階梯陰影：三層權重，一個深度 ─────────────────────────────
   深度只用一個（`var(--shadow)` = 3px；hover 5px、按下 2px 是**狀態**不是第二三種
   深度）。但深度統一不等於可以到處灑：舊的 --shadow 是 `0 1px 2px rgba(...,.05)`
   幾乎不可見，新的是一條 3px 的實色帶（對 --card 3.54:1）。若 callout／toc／
   table-wrap／zone-group／datablock 全上，一頁會有 35–50 個實色帶 —— 位移陰影的語意
   是「這個東西浮在頁面之上」，**如果所有東西都浮著，就沒有東西浮著**。
     Tier 1 浮起（2px 硬邊 ＋ var(--shadow)）＝你會動手操作、或本質上被抬離文流的物件
       .db-cat-card / .btn / .next-step .ns-card / .town-pills a.tp /
       .kpi-row > div / .mapwrap / .sc-tools button
     Tier 2 框住（2px 硬邊，**無陰影**）＝你會穿過它閱讀的容器
       .callout / .toc / .zone-group / .datablock / .table-wrap
       （JRPG 的對話框是平的、厚邊的、沒有投影的；第 15 個投影不會讓它更 8bit）
     Tier 3 齊平（不動）＝ table.data thead th（sticky，紅線）、.ad-slot（AdSense 政策）

   ── 邊色只有兩個權重 ────────────────────────────────────────────────────
     --px-edge       滿的硬邊：小元件、輸入框、可點晶片、裸 .callout（要大聲）
     --px-edge-soft  45% 混色：一頁會出現 8–15 個的容器（卡牆／手風琴／KPI 磚／表框）
   14 張卡或 15 個手風琴全上滿 --px-edge 會讓整片變成黑格網 —— 那是吵，不是硬。

   ── 深色模式 ────────────────────────────────────────────────────────────
   本節所有值都走 --px-edge / --px-edge-soft / --px-drop / --shadow / --line /
   --brand，全部已 theme-derived → **不需要任何深色覆寫區塊**（站上深色有兩套
   `@media (prefers-color-scheme: dark) :root:not([data-theme="light"])` 與
   `:root[data-theme="dark"]`，要覆寫必須兩套逐字各補一份；本節刻意設計成不必）。
   唯一的例外是「選中態的沉入內陰影」，見下面 13d 的說明 —— theme-derived 不等於
   theme-correct。（另一處固定色是 §6b 的 #94a3b8/#f8fafc，理由在那裡。）

   ── 順手記一筆給下一個人（不是本批要修的）─────────────────────────────
   pixel.css §4 列了 5 個像素字型選擇器，其中 `.statgrid`／`.coord`／`.hero .pill`
   在 school2 是**零節點**（五個鎮頁早就改用 .kpi-row 了、school2 沒有 .hero）。
   實際只有 `.kpi-row b`（只在 5 個佈局頁）與 `.sec-num` 吃到像素字型 → L2「數與籤」
   層在其餘 23 頁只剩一個 12px 的章節編號。別以為 L2 已經鋪滿了。
   另外 pixel.css 的 `.px-frame` 用 4px 階梯陰影，是全站唯一的 4px（本節統一 3px）；
   它零使用所以不會與 3px 同時出現在畫面上，不能改（改 pixel.css 要全站 bump ?v）。
--------------------------------------------------------------------------- */

/* 宣告在 :root（跟 pixel.css 的 --px-edge 同一層）而不是 main.article：--shadow 那類
   「只在內容區生效」的換值才需要 scope，邊色是純推導值，放 :root 就不必擔心某天有元件
   落在 main.article 之外時 var() 解不出來（custom property 解不出來會讓整條
   border-color 失效成 currentColor —— 那種壞法很難查）。 */
:root {
    --px-edge-soft: color-mix(in srgb, var(--px-edge) 45%, var(--line));
}


/* ---- 13a. .callout（shell.css:362–366）—— Tier 2 --------------------------
   1px → 2px。variant 的邊色混色比例**必須跟著加**（35%→55%、45%→65%）：那些比例是
   為 1px 調的，1px 的低對比細線讀起來是「一條線」，2px 的低對比線讀起來是一條
   **糊掉的色帶** —— 正好是 8bit 語言的反面（8bit 對話框的邊是硬的）。
   色相與分工一個字都不動（tip 走 --brand、warn 走琥珀、key 走 --accent）：
   「顏色不動」的意思是不動色相，不是不動數值。
   裸 .callout 的邊色由 --line 改 --px-edge：2px 的 --line 看起來像沒關掉的 debug 框。
   padding / font-size 不動（那會動到框內的行寬與正文節奏）。 */
main.article .callout { border-width: 2px; border-color: var(--px-edge); }
main.article .callout.tip { border-color: color-mix(in srgb, var(--brand) 55%, var(--line)); }
main.article .callout.warn { border-color: color-mix(in srgb, #e0a020 65%, var(--line)); }
main.article .callout.key { border-color: color-mix(in srgb, var(--accent) 65%, var(--line)); }


/* ---- 13b. .toc（shell.css:377）與 .table-wrap（:369）—— Tier 2 -----------
   `.datablock .table-wrap` 是 border: 0（它的框由 .datablock 提供），而
   `main.article .table-wrap` 的 (0,2,1) 會壓過 `.datablock .table-wrap` 的 (0,2,0)
   → 必須明寫回 0，否則 datablock 內會多長一條 2px 邊。 */
main.article .toc { border-width: 2px; border-color: var(--px-edge-soft); }
main.article .table-wrap { border-width: 2px; border-color: var(--px-edge-soft); }
main.article .datablock .table-wrap { border-width: 0; }


/* ---- 13c. .btn（shell.css:414–423）—— Tier 1 ------------------------------
   `border-radius: 10px` 是寫死的、不走 token → --radius 換值救不到它，
   是全站唯一還圓著的主要按鈕。inset 陰影換成階梯陰影是同一件事的兩種說法
   （inset＝「這是一顆實體鍵」，drop 也是，但屬於這套語言）。
   順手補 :focus-visible —— .btn 在 shell.css 完全沒有鍵盤焦點樣式。 */
main.article .btn {
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow);
    transition: filter .15s ease, transform .1s steps(2, end), box-shadow .1s steps(2, end);
}
main.article .btn:active { transform: translate(2px, 2px); box-shadow: 2px 2px 0 0 var(--px-drop); }
main.article .btn:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
main.article .btn.ghost { border: 2px solid var(--px-edge); box-shadow: var(--shadow); }


/* ---- 13d. 「選中態」＝沉入：五個一起做 -----------------------------------
   main.article 裡有一整族「這一個是選中的」，五個都是實心 --brand ＋ #fff：
     .town-pills .tp.on（五鎮切換）／.db-chip.active（分類列）／.db-pick.on（比較挑選）
     .db-cmp-btn.on（比較模式開關）／.path-rail [aria-current="step"]（主線位置）
   一個新語意只做一處那不是語言、是裝飾——讀者學不到「沉入＝你在這」的規則，
   只會覺得那一顆長得怪。所以五個共用同一條規則。.path-rail 尤其該做：接合式分段軌
   的當前段沉入，正是這個慣用語的原生用法。

   ── 為什麼這裡用寫死的 rgba(0,0,0,.28) 而不是 color-mix(--ink …) ────────
   `--ink` 淺色是 #1c2b24（暗）、深色是 #e8f0eb（亮）→ `--ink` 混出來的內陰影在淺色
   是暗的（＝凹）、在深色是**亮的（＝凸）**，會把「你在這一頁」編碼成相反的意思。
   這五個元素的底都是 `var(--brand)`，兩個 theme 下都是**中間調的飽和綠**
   （#16794a / #4cc38a）、不是 --card → 飽和色塊上的內陰影是**物理打光**、不是主題色，
   黑色 alpha 在兩邊都正確。這就是「theme-derived ≠ theme-correct」的那個例外。
   陰影只在左上內緣，不影響 #fff 文字的對比。 */
main.article .town-pills .tp.on,
main.article .rank-rail .rk.on,
main.article .db-chip.active,
main.article .db-pick.on,
main.article .db-cmp-btn.on,
main.article .path-rail [aria-current="step"] {
    box-shadow: inset 2px 2px 0 0 rgba(0, 0, 0, .28);
}


/* ---- 13e. .db-cat-card（shell.css:471–480）—— Tier 1 ---------------------
   邊 1px → 2px 但走 --px-edge-soft（14 張卡全上滿會變黑格網），:hover 才升到滿的
   --px-edge。transition 整條統一 `.1s steps(2,end)`（原本是三條 .16s ease；.16s 下
   的 steps(2) 讀起來鈍——兩格各 80ms。border-color 分兩格跳在這個語言裡是對的）。
   hover 抬起（既有 translateY(-2px) ＋ --shadow-hover）、按下沉入，是一組完整的手感。 */
main.article .db-cat-card {
    border-width: 2px;
    border-color: var(--px-edge-soft);
    transition: transform .1s steps(2, end), box-shadow .1s steps(2, end), border-color .1s steps(2, end);
}
main.article .db-cat-card:hover { border-color: var(--px-edge); }
main.article .db-cat-card:active { transform: translate(2px, 2px); box-shadow: 2px 2px 0 0 var(--px-drop); }
/* 觸控裝置沒有 hover → 只有 3px 靜止沉到 2px＝差 1px，等於沒有回饋。位移做足。
   （桌機是 hover −2px/5px 按到 active +2px/2px，本來就有 4px 落差。） */
@media (hover: none) {
    main.article .db-cat-card:active { transform: translate(3px, 3px); box-shadow: none; }
}

/* 裸 emoji 給像素風容器。**刻意不照 pixel.css 的 .px-ico（32px 框 / 18px 字）**：
   卡片只有 150–200px 寬，emoji 是 14 張卡唯一的辨識錨點，26px→18px 是面積 −52%，
   等於「把容器變吵、把內容變小」—— chrome 不准比 content 大聲。
   36px 框 / 24px 字（幾乎不縮），邊用 --px-drop 而不是 --px-edge：卡片邊是
   --px-edge-soft（刻意壓弱），icon 框若用滿的 --px-edge 會變成整張卡上最強的一條線，
   比定義可點區域的卡片邊還強＝主從關係倒過來。
   高度影響：26px emoji 的 line box 約 30–32px，換 36px 框只 +4~6px，而 .db-cat-grid
   是 grid、同列自動 stretch → 不會高矮不齊。 */
main.article .db-cat-card .ic {
    display: inline-grid; place-items: center;
    width: 36px; height: 36px; flex: 0 0 auto;
    border: 2px solid var(--px-drop);
    border-radius: var(--radius-sm);
    background: var(--card-2);
    font-size: 24px; line-height: 1;
}


/* ---- 13f. db 分類頁的控制列（shell.css:431–465）--------------------------
   `.db-nav`（晶片列）**正上方 14px** 就是 `.db-tools`（.db-search ＋ .db-cmp-btn），
   再下面是 .db-cmp-clear。只方化晶片、放著同一畫面上體積更大的主搜尋框圓著，
   是這批改動裡最刺眼的不一致，而且出現在 14 個 db 分類頁的第一屏 → 一起改。
   `.db-pick` 是比較模式下視覺全等的第二排晶片，同理一起改。

   ── 命中區 44px 已上移共用軌（本節只留 8bit 邊框）────────────────────────
   `display: inline-flex` / `align-items: center` / `min-height: 44px` / `padding: 0 14px`
   這四行原本寫在這裡，於是**同一個元件在站上有兩種尺寸**：school2 的 db 頁 44px、
   其餘 28 款 36.1px —— 而它是 111 個 db 頁第一屏的主要篩選控件，比 header 差 8px
   更傷「並排要看得出是同一個站」。四行已搬到 `assets/shell.css` 的 `.db-chip`／
   `.db-pick`（29 款一起），本節只保留 school2 專屬的 8bit 邊框三行。
   兩處屬性不重疊、不會打架；留著重複才是兩個真相來源。
   （不設 `@media (pointer: coarse)` 的三條理由也一併移到 shell.css 那一節，
     那裡還多了第四條：桌機的高度代價可以用 `.bar` 的 padding 買回來 → 代價為負。）
   晶片刻意**不給階梯陰影**：一排 14 顆凸起是噪音；選中態靠 13d 的沉入表達。 */
main.article .db-chip,
main.article .db-pick {
    border-width: 2px;
    border-color: var(--px-edge);
    border-radius: var(--radius-sm);
}
main.article .db-search,
main.article .db-cmp-btn {
    border-width: 2px;
    border-color: var(--px-edge);
    border-radius: var(--radius-sm);
}
/* `.db-search:focus` 刻意不覆寫：shell.css:461 已經給了 outline ＋ border-color，
   而 outline-offset 沒被宣告過（＝預設 0）→ 這裡沒有 .dbk-f 那個「三條同心線」的問題，
   多寫一條只會把 shell.css 的 `border-color: var(--brand)` 弄掉。 */
main.article .db-cmp-clear { border-width: 2px; border-color: var(--px-edge); border-radius: var(--radius-sm); }


/* ---- 13g. .datablock 標頭（guide.css §4）—— Tier 2 -----------------------
   `thead th` 一個字都不碰（sticky 紅線；那條補線的粗細改動見 §4 的說明）。
   .dbk-f 的 outline-offset 由 1px 改 0：2px 暗邊 ＋ 1px 空隙 ＋ 2px 綠 outline
   會變成三條同心線。
   .dbk-empty button 32px → 44px：搜不到東西時那顆「清除篩選」是使用者唯一的出路，
   回頭的那顆鈕不該比別的鈕小。 */
main.article .datablock { border-width: 2px; border-color: var(--px-edge-soft); }
main.article .dbk-h { border-bottom-width: 2px; }
main.article .dbk-f { border-width: 2px; border-color: var(--px-edge); border-radius: var(--radius-sm); }
main.article .dbk-f:focus-visible { outline: 2px solid var(--brand); outline-offset: 0; }
main.article .dbk-empty button {
    min-height: 44px;
    border-width: 2px;
    border-color: var(--px-edge);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow);
    transition: transform .1s steps(2, end), box-shadow .1s steps(2, end);
}
main.article .dbk-empty button:active { transform: translate(2px, 2px); box-shadow: 2px 2px 0 0 var(--px-drop); }


/* ---- 13h. 刻意不碰（每條都有理由，不要自作主張加回來）--------------------
   · `.pagenav-btn`（圓形）／`.pagenav-menu`（14px 圓角＋柔和 blur）—— **不改**。
     ①它是 29 款 1:1 共用的唯一浮動控件。驗收標準是「並排要看得出是同一個站」，
       而外殼恆定正是「同一個站」的載體：內容區可以是分館，外殼不行。
     ②`.pagenav-menu` 的 blur 陰影在**做功能**：那是蓋在任意內容之上的 240px 浮層，
       模糊陰影是唯一能讓它的邊界從底下不確定的文字／表格裡分離出來的東西。零模糊
       位移塊是給「貼在平底上的行內盒子」用的，放在浮層上邊界會跟底下內容糊在一起。
     ③圓形＝「這是覆蓋層控件」，方形＝「這是內容」。方化＋硬邊＋階梯陰影之後它會讀成
       「一張卡片卡在文章上面」，像渲染 bug，不像控件。
     pixel.css:119–120 已經帶著書面理由把 site-header/game-bar/footer/.pagenav 劃在
     界外。guide.css 只影響這 28 頁在技術上為真，但技術上做得到不是設計上該做。
   · `table.data thead th` 不加 border／box-shadow（見 §4）。
   · `.ad-slot` 不加任何裝飾框線（AdSense：不得讓廣告看起來像網站內容）。
   · `.prose` 的 p/li/h1/h2/h3 字型／字級／行高／字距／顏色／margin —— L3 紅線。
     本節只動 border/radius/box-shadow/transform/min-height，一個字級都沒碰。
   · 表格族不用 clip-path（.table-wrap 是捲動容器，會裁掉內容）。
--------------------------------------------------------------------------- */
