@charset "UTF-8";
/* ⚠ @charset 必須是檔案的**第一個位元組**，前面連註解都不能有。
   伺服器回的是 `Content-Type: text/css`（沒有 charset），CSS 檔本身若也沒宣告，
   瀏覽器只能用後備編碼去解讀 —— 實測會把 UTF-8 的「→」(E2 86 92)
   讀成 Latin-1 的「â†\'」，所有箭頭都變成亂碼（2026-07-31 客戶回報）。
   ⚠ 這一行只加在檔首，底下所有既有規則一個字都沒動。 */
/*
 * 一天聽一點 — 前台正式樣式
 *
 * 【定位】第 3 站（前端）切版寫在這裡。載入順序在 wireframe.css 之後，
 *        所以切好一頁就自然覆蓋掉該頁的原型灰階樣式，可以逐頁換皮、隨時對照。
 *        全部頁面切完後移除 wireframe.css，本檔成為前台唯一樣式來源。
 *
 * 【紀律】（中央廚房 03-frontend.md 準則 5 / 出站檢查 FE-03）
 *        1. 版面、格線、元件優先用 **Bootstrap 原生 class**，不夠用才寫自訂 class
 *        2. **不寫 inline style 裝飾** — view 裡的 style="" 一律搬進來
 *        3. 顏色一律 var(--xxx)，**禁止 hex**（token 定義在 tokens.css）
 *        4. 共用 UI 元件全站一份（SSOT），重複出現的抽成 class 或 partial
 *
 * 【驗收】切完把 .env 的 app.CSPEnabled = false 拿掉。CSP 開著頁面正常
 *        ＝ 沒有 inline style 殘留、資源都本地化，等同正式站條件。
 *
 * 【進度】已切：首頁（home.php，對 Figma proposal1）。
 *        其餘 18 頁仍走 wireframe.css，尚未換皮。
 */

/* ═══════════════════════════════════════════════════════════
   Base — 全域基礎
   ═══════════════════════════════════════════════════════════ */

body {
    background: hsl(var(--background));
    color: hsl(var(--foreground));
    font-family: var(--font-body);
}

a {
    color: hsl(var(--foreground));
    transition: color var(--transition);
}

:focus-visible {
    outline: 2px solid hsl(var(--ring));
    outline-offset: 2px;
}

/* ═══════════════════════════════════════════════════════════
   Layout — 版面容器
   ═══════════════════════════════════════════════════════════ */

/* 內容最大寬置中（沿用 wireframe 的 .wf-inner 結構位置） */
.site-inner {
    width: 100%;
    max-width: var(--container-max);
    margin-inline: auto;
}

/* wireframe.css 把 .wf-inner 鎖在 1440px，那是原型階段的畫布寬。
   已切版的頁面解除這個上限，改由各區塊自己的容器決定寬度，
   大螢幕才不會整頁縮在中間 1440、兩側大片留白。
   只作用在 .is-designed（目前＝首頁），其餘 18 頁的原型維持 1440 不受影響。 */
.is-designed .wf-inner {
    max-width: none;
}

/* 標準內容區：寬度隨螢幕成長但有上限，且永遠置中。
   max-width 要「含」左右內距，內容淨寬才等於 --container-max。 */
.site-section {
    width: 100%;
    max-width: calc(var(--container-max) + var(--section-x) * 2);
    margin-inline: auto;
    padding: var(--section-y) var(--section-x);
}

/* 已切版頁面：隱藏 wireframe 階段的溝通標註（粉紅 section 標籤／黃色便條紙），
   以及右下角的 RWD 斷點提示（.wf-bp，「XXL 桌機1440」那一顆黑標）。
   標註與標記都保留在 DOM 不刪除 —— 拿掉 body 的 .is-designed 就會整頁還原成原型，
   客戶簡報要對照「這一區對應哪個後台系統」時仍調得出來。
   ⚠ .wf-bp 定義在 wireframe.css、標記在 layouts/main.php，兩者都是 19 頁共用；
     尚未切版的 18 頁仍需要它對斷點，所以是在這裡隱藏，不是把它刪掉。 */
.is-designed .wf-label,
.is-designed .wf-note {
    display: none;
}

/* ⚠ .wf-bp 必須 !important：那四顆標記帶 Bootstrap 的顯示工具類別
   （.d-block／.d-lg-block／.d-xl-block／.d-xxl-block），而 Bootstrap 的
   display 工具是 `display: … !important` —— 不加 !important 只會蓋掉
   當前斷點用不到的那三顆，正在顯示的那一顆照樣留著（實測就是這樣）。
   這是覆寫第三方工具類別的正當用法，不是圖方便。 */
.is-designed .wf-bp {
    display: none !important;
}

/* ═══════════════════════════════════════════════════════════
   捲動進場（scroll reveal）
   借自 re-loop 的動畫節奏：位移 + 淡入、0.6s、ease-out（快進慢收），
   同一組元素依序延遲，讀起來像內容「被翻開」而不是同時彈出。
   加不加 class 由 js/home.js 的 IntersectionObserver 決定；
   JS 沒載入或不支援時，元素維持可見（漸進增強，不會整頁空白）。
   ═══════════════════════════════════════════════════════════ */

.js-reveal {
    opacity: 0;
    transform: translateY(var(--reveal-shift));
    transition: opacity var(--dur-reveal) var(--ease-out),
                transform var(--dur-reveal) var(--ease-out);
    transition-delay: var(--reveal-delay, 0ms);
    will-change: opacity, transform;
}

.js-reveal.is-in {
    opacity: 1;
    transform: none;
}

/* 使用者要求減少動態時，只淡入不位移 */
@media (prefers-reduced-motion: reduce) {
    .js-reveal {
        transform: none;
        transition-duration: 1ms;
    }
}

/* 滿版區塊：底色做到視窗兩側，不受內容容器上限限制 */
.section-bleed {
    max-width: none;
    padding-inline: 0;
}

/* 內距要「加」在最大寬之外，內容淨寬才等於容器變數；
   直接用 max-width:1296 會被內距吃掉只剩 1152。
   這裡用 --container-wide：視覺型區塊（近期課程）在大螢幕可比文字區更寬。 */
.section-bleed > .section-bleed-inner {
    width: 100%;
    max-width: calc(var(--container-wide) + var(--section-x) * 2);
    margin-inline: auto;
    padding-inline: var(--section-x);
}

/* ═══════════════════════════════════════════════════════════
   Components — 共用元件（SSOT）
   ═══════════════════════════════════════════════════════════ */

/* ─── 區塊標題：中文主標 + 下方 Inter 英文小標 ─────────────────
   設計稿 service-title / knowledge-title 皆為此結構（221×88）。 */
/* 標題與內容之間留一段明顯的空 —— 借自 re-loop 的留白邏輯：
   讓標題先被單獨讀完，再進入內容，區塊的節奏才分得開。 */
.section-title {
    margin: 0 0 var(--space-title-gap);
}

/* 中文主標：Noto Sans TC / regular 400 / 36px / 行高 70 / 字距 20%
   （Figma 603:721 服務專區、603:1241 實體課程實測值） */
.section-title .zh {
    display: block;
    font-size: var(--text-section);
    line-height: var(--lh-section);
    font-weight: var(--fw-normal);
    letter-spacing: var(--tracking);
    color: hsl(var(--heading));
    margin: 0;
}

/* 英文小標：Julius Sans One / regular 400 / 16px / 行高 30 / 字距 20%
   （Figma 603:723 Service Zone、603:1242 In-person courses）。
   先前用 Inter 14px 是照字級表的「EN Decoration p」，但這兩個節點在設計稿裡
   指定的是 Julius Sans One 16 —— 與 Hero 英文小標、列編號同一套。 */
.section-title .en {
    display: block;
    margin-top: .75rem;
    font-family: var(--font-eyebrow);
    font-size: 1rem;               /* 16px */
    line-height: 1.875;            /* 30px */
    font-weight: var(--fw-normal);
    letter-spacing: var(--tracking);
    color: hsl(var(--muted-foreground));
    text-transform: none;
}

/* 深色區塊上的標題反白。英文小標在設計稿是輔色 #D6C1A9（不是半透明米白）；
   亮底區塊維持深灰，米棕壓在米白底上對比不足。 */
.on-dark .section-title .zh { color: hsl(var(--surface-dark-foreground)); }
.on-dark .section-title .en { color: hsl(var(--brand-accent)); }

/* ── 深色帶的文字字重（2026-07-31 客戶指定）──────────────────────────
   客戶要的是「**看起來**跟米色背景一樣粗」，不是「數值一樣」——
   第一版直接照抄米白區的 350／450，客戶反饋「在深灰下看起來比米色下粗」。
   那個反饋是對的，而且有名字：**光暈效應（irradiation）**。
   淺色字壓在深底上，字身邊緣的光會往暗處擴散，同一個字重看起來會胖一階；
   深色字壓在淺底上則相反。所以「看起來一樣」必須讓深底的數值**比米白低一階**。

   對照表（米白 → 深灰）：
       內文／清單：350 → 310
       標題／連結：450 → 410
   ⚠ 補償量 40 是這個字級帶（14–36px）的經驗值。要再調就改這兩個數字 ——
     **兩個要一起動、而且維持 100 的級距**，否則標題與內文的層級會塌掉。
   ⚠ 米白區維持 350／450 完全不動（客戶明確交代「不要調整米色區域的文字粗細」）。

   ⚠ 做法與 Header／Footer／知識專區／Hero 左頁完全一致：**重新定義 token**，
     不逐條改 font-weight。深色帶裡有服務專區與近期課程兩大段、數十條文字規則，
     逐條改必漏；而且 CSS 變數只往下繼承，米白區一個字都不會被動到。
   ⚠ .footer-bottom 同樣是深灰底，但它在 .site-footer 之內、
     已經吃到 Footer 那組 350／450 —— 不必也不該在這裡重複宣告（SSOT）。 */
.on-dark {
    --fw-light:  310;
    --fw-normal: 410;
}

/* ── 深色帶的文字 hover 改成藍色（2026-07-31 客戶指定）────────────────
   原本是卡其輔色（--brand-accent），改用 --accent-blue（#B1C7DE）。

   ⚠ --accent-blue 原本註明「僅浮動社群鈕使用，不要拿去別的地方」——
     那條限制是切版初期「不要增加藍色」的規格留下來的。
     2026-07-31 客戶明確要求深色帶的文字 hover 用藍色，限制到此為止；
     但**只放行到這裡**：米白區與其他元件仍然不要用藍色（見 tokens.css 的說明）。
   ⚠ 沿用同一個 token 而不是另外寫一個 hex —— 藍色在本站只有一個權威來源（SSOT）。
   ⚠ 用 .on-dark 前綴把權重拉到 (0,4,0)：原規則是 (0,3,0)，
     加了前綴之後不管這一段放在檔案的哪個位置都一定贏，不必依賴宣告順序。
   ⚠ 一併補上 :focus-visible —— 原本只有 :hover 會變色，
     鍵盤使用者 Tab 到卡片時圖片與箭頭都有反應、只有標題沒有（既有的疏漏）。
   ⚠ 包在 @media (hover: hover) 裡的只有 :hover：觸控裝置的 :hover 會「黏住」
     （點過的項目一直維持 hover 樣式），這是原作已經處理過的坑，照它的做法。 */
@media (hover: hover) {
    .on-dark .svc-feature:hover .svc-feature-name,
    .on-dark .svc-tile:hover .svc-tile-name,
    .on-dark .course-link:hover .course-title {
        color: hsl(var(--accent-blue));
    }
}

.on-dark .svc-feature:focus-visible .svc-feature-name,
.on-dark .svc-tile:focus-visible .svc-tile-name,
.on-dark .course-link:focus-visible .course-title {
    color: hsl(var(--accent-blue));
}

/* ── 標題的裝飾英文改到主標**上方**（2026-07-31 客戶指定）──────────────
   ⚠ 用 flex 的 order 換視覺順序，**不動 HTML 的先後**。
     兩個 span 在同一個 <h2> 裡，讀屏念的是它們串起來的一整串文字；
     DOM 維持「中文在前」，可及名稱與搜尋引擎拿到的仍然是「服務專區 Service Zone」
     —— 中文才是這個區塊真正的標題，英文只是裝飾。
     直接對調 HTML 會把可及名稱變成「Service Zone 服務專區」：
     視覺上一樣，語意上卻是把裝飾放到了標題前面。
   ⚠ 間距要跟著翻面：原本英文在下、用 margin-top 拉開；
     現在英文在上，那段距離必須改掛成 margin-bottom，否則兩行會黏在一起。
   ⚠ 三個區塊（服務專區／近期課程／知識專區）共用這一條，
     各區塊自己的覆寫都只動 margin，不受影響。 */
.section-title {
    display: flex;
    flex-direction: column;
}

.section-title .en {
    order: -1;
    margin-top: 0;
    margin-bottom: .75rem;
}

/* ─── 膠囊按鈕（全站唯一的按鈕樣式）────────────────────────────
   2026-07-30 依客戶提供的兩張參考圖改版。版型：

     ┌──────────────────────────────────┐
     │  看更多                        ·  │   靜置：右側一個小圓點
     └──────────────────────────────────┘
     ┌──────────────────────────────────┐
     │  看更多                       (→) │   hover：圓點長成箭頭圓
     └──────────────────────────────────┘

   參考圖實測（兩張的膠囊都是 175×48）：
     圓點  4×4px
     箭頭圓 28×28px（＝ 膠囊高的 .583）
     兩者**圓心相同**，距膠囊右緣都是 23px
     → 所以這不是「換掉一個元素」，是**同一個圓點放大**。
     膠囊寬度兩態完全一致（175px）—— transform 不影響版面，天生成立。

   ⚠ 與改版前最大的差異：hover 時**膠囊本身不再填成深色實心**。
     舊版是整顆反白（背景填 --foreground、文字轉米白），參考圖裡膠囊
     從頭到尾都是細框，動的只有右側那顆圓。照舊版做會看不出參考圖的味道。
   ⚠ 靜置時箭頭字符必須是透明的：圓點只有 4px，裡面若透出一個縮小的
     淺色箭頭，會變成深點中間一粒亮渣。改成 hover 才把字符顏色淡進來。
   ⚠ 徽章尺寸走 --pill-badge，Hero 的 .cal-cta 會把它調小 ——
     桌曆頁的按鈕本來就比區塊按鈕小一階，固定 28px 會撐破。

   字級／字重照設計稿的 button2 元件（493:406／493:407）：
   Noto Sans TC 400 / 18px / 字距 20%。 */
.btn-pill {
    --pill-badge: 28px;      /* 箭頭圓直徑（參考圖 28 / 膠囊高 48） */
    --pill-dot: .143;        /* 靜置縮放比 ＝ 4 ÷ 28 */

    display: inline-flex;
    align-items: center;
    justify-content: flex-start;
    gap: 1.75rem;            /* 文字 → 徽章。4 字標籤時總寬約 175px，與參考圖一致 */
    min-height: 48px;
    padding: 0 .5625rem 0 1.5rem;   /* 右 9px（徽章距右緣）／左 24px（參考圖實測） */
    border: 1px solid hsl(var(--foreground) / .5);
    border-radius: var(--radius-pill);
    background: transparent;
    color: hsl(var(--foreground));
    font-size: var(--text-sub);
    line-height: 1;
    font-weight: var(--fw-normal);
    letter-spacing: var(--tracking);
    text-decoration: none;
    white-space: nowrap;
    transition: border-color var(--transition), color var(--transition);
}

/* 右側徽章。靜置縮成圓點、hover 放大回整顆圓並把箭頭淡進來。
   ⚠ 用 transform 而不是改 width／height：transform 不佔版面，
     所以膠囊寬度在兩個狀態完全相同（參考圖兩張都是 175px）。 */
.btn-pill::after {
    content: "→";
    flex: 0 0 auto;
    width: var(--pill-badge);
    height: var(--pill-badge);
    border-radius: 50%;
    background: hsl(var(--foreground));
    color: transparent;              /* 靜置：只看得到圓點，不透出箭頭 */
    font-family: var(--font-en);
    font-size: calc(var(--pill-badge) * .46);
    line-height: var(--pill-badge);
    letter-spacing: 0;
    text-align: center;
    transform: scale(var(--pill-dot));
    transition: transform 380ms var(--ease-out),
                color var(--dur-hover) var(--ease-out),
                background var(--transition);
}

.btn-pill:hover,
.btn-pill:focus-visible {
    border-color: hsl(var(--foreground));
}

.btn-pill:hover::after,
.btn-pill:focus-visible::after {
    transform: scale(1);
    color: hsl(var(--background));
}

/* 深色底上的膠囊按鈕：邊框、文字與徽章一起反相 */
.on-dark .btn-pill {
    border-color: hsl(var(--surface-dark-foreground) / .5);
    color: hsl(var(--surface-dark-foreground));
}

.on-dark .btn-pill::after {
    background: hsl(var(--surface-dark-foreground));
}

.on-dark .btn-pill:hover,
.on-dark .btn-pill:focus-visible {
    border-color: hsl(var(--surface-dark-foreground));
}

.on-dark .btn-pill:hover::after,
.on-dark .btn-pill:focus-visible::after {
    color: hsl(var(--surface-dark));
}

/* 減少動態時直接呈現最終狀態，不做放大 */
@media (prefers-reduced-motion: reduce) {
    .btn-pill::after { transition: color var(--dur-hover) linear; }
}

/* 註：.btn-pill--go（膠囊 + 分離的圓形箭頭徽章）已併入 .btn-pill 本體 ——
   新版每一顆按鈕都有徽章，修飾子沒有存在意義，且改版前它已經是零使用的死碼
   （近期課程改版時最後一個用它的地方被移除）。 */

/* ─── 分類／標籤（知識卡上方的細框膠囊） ─────────────────────
   2026-07-30 定案：**細框不填色**，與同一區的「查看更多」（.btn-pill）同一套
   —— 1px 深灰半透明外框 + 深灰字 + 膠囊圓角，hover 只讓框與字轉深，不填底。
   這一區唯一填實心卡其的是 YouTube／Podcast（.kz-links a），
   讓「外部連結」在整段裡是唯一有色塊的元件。
   ⚠ 途中試過兩版都不採用，理由記在這裡免得再繞一次：
     ① 卡其外框 + 卡其字 → 卡其壓米白只有 1.62:1，字幾乎看不見。
     ② 實心卡其 + 黑字（＝訂閱鈕那套，11:1）→ 可讀性最好，但這一區
        同時有標籤、YouTube／Podcast、查看更多三種膠囊，全都上色會過重。
   ⚠ hover 不填色：與 .btn-pill 改版後的方向一致（客戶提供的參考圖裡，
     膠囊從頭到尾都是細框，動的只有右側那顆圓）。
   ⚠ 這個元件目前全站只用在知識專區的 .kz-tags（已 grep 確認）。 */
.tag-pill {
    display: inline-flex;
    align-items: center;
    min-height: 40px;
    padding: 0 1.15rem;
    border: 1px solid hsl(var(--foreground) / .5);
    border-radius: var(--radius-pill);
    background: transparent;
    color: hsl(var(--foreground));
    font-size: var(--text-body);
    line-height: 1;
    font-weight: var(--fw-light);
    letter-spacing: var(--tracking);
    text-decoration: none;
    transition: border-color var(--transition), color var(--transition);
}

.tag-pill:hover,
.tag-pill:focus-visible {
    border-color: hsl(var(--heading));
    color: hsl(var(--heading));
}

/* ─── 輪播圓形箭頭鈕（設計稿疊在圖片中央，42px 細框圓鈕） ────────── */


/* 箭頭以 CSS 邊框繪製，不依賴 Bootstrap 的背景圖 icon（CSP 友善） */


/* 疊在輪播圖片正中央的一組左右鈕 */

/* ═══════════════════════════════════════════════════════════
   Pages — 首頁（對 Figma proposal1，frame 490:209）
   ═══════════════════════════════════════════════════════════ */

/* ═══════════════════════════════════════════════════════════
   Hero 開場動畫（Opening Sequence）2026-08-26 重新設計版
   ═══════════════════════════════════════════════════════════
   舊版（桌曆翻頁，三張紙依序翻走露出桌曆）已在 2026-08-14「拿掉桌曆
   翻頁機制」那輪改版時整段刪除——markup／JS／CSS 三層都不在了，這裡
   原本留著一大段純文字說明舊設計理由的註解，容易讓人誤以為功能還在
   （2026-08-26 客戶正是因此問「開場動畫被移除了嗎」）。舊設計的細節與
   移除經過已經寫進 memory reference-onedaylisten-opening-sequence-
   removed，不需要留在這裡的註解裡佔位；下面是配合現在「大圖＋右側矩形
   模組」editorial Hero 重新設計的新版本。

   【概念】全螢幕先蓋一層跟 Hero 大圖同款的 8 格矩形遮罩，中央品牌圖示＋
   文字淡入，格子依序掀開露出 Hero——Hero 自己的遮罩顯影（.hero-mask-
   grid，本檔上面剛講過的那一套）緊接著再播一次，兩層視覺上是同一套
   語言的「一大一小」兩次顯影，不是另外發明一種效果。
   ⚠ 8 格的排版／transform-origin／scale 分組完全比照 .hero-mask-grid／
     .hero-mask（本檔上面）：同一組已經驗證過、修過次像素縫隙 bug 的
     機制，這裡只是套用在全螢幕範圍，不重新發明一套算法。margin:-1px
     的縫隙修法理由同款照抄，不再重複解釋一次。
   ⚠ 播放時機與條件（只在真正第一次進站時播一次）由 home.js 判斷；這裡
     的 CSS 只負責「拿到 .is-brand-in／.is-open 這兩個 class 之後要長
     什麼樣子」，不自己決定要不要播。
   ⚠ 容器預設 hidden（HTML 屬性，見 home.php）——JS 沒跑／減少動態時，
     這裡完全不會被拿掉 hidden，不會有一層遮罩卡住蓋住 Hero；這正是
     舊開場動畫的註解特別強調過的坑，新版一樣要防。
   ⚠ z-index 沿用舊版就定案的 1100（高於 fixed 的 header，數字沿用
     Bootstrap 的層級語彙），這個決定沒有過期，照舊。 */
.hero-opening {
    position: fixed;
    inset: 0;
    z-index: 1100;
}

.hero-opening[hidden] {
    display: none;
}

.hero-opening-grid {
    position: absolute;
    inset: 0;
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    grid-template-rows: repeat(2, 1fr);
    grid-template-areas:
        "a b c d"
        "e f g h";
    gap: 0;
}

.hero-opening-cell {
    background: hsl(var(--background));
    margin: -1px;
    transition: transform 560ms cubic-bezier(0.76, 0, 0.24, 1);
    will-change: transform;
}

.hero-opening-cell--a { grid-area: a; }
.hero-opening-cell--b { grid-area: b; }
.hero-opening-cell--c { grid-area: c; }
.hero-opening-cell--d { grid-area: d; }
.hero-opening-cell--e { grid-area: e; }
.hero-opening-cell--f { grid-area: f; }
.hero-opening-cell--g { grid-area: g; }
.hero-opening-cell--h { grid-area: h; }

.hero-opening-cell--a, .hero-opening-cell--e { transform-origin: left;   transform: scaleX(1); }
.hero-opening-cell--d, .hero-opening-cell--h { transform-origin: right;  transform: scaleX(1); }
.hero-opening-cell--b, .hero-opening-cell--c { transform-origin: top;    transform: scaleY(1); }
.hero-opening-cell--f, .hero-opening-cell--g { transform-origin: bottom; transform: scaleY(1); }

.hero-opening.is-open .hero-opening-cell--a,
.hero-opening.is-open .hero-opening-cell--d,
.hero-opening.is-open .hero-opening-cell--e,
.hero-opening.is-open .hero-opening-cell--h {
    transform: scaleX(0);
}

.hero-opening.is-open .hero-opening-cell--b,
.hero-opening.is-open .hero-opening-cell--c,
.hero-opening.is-open .hero-opening-cell--f,
.hero-opening.is-open .hero-opening-cell--g {
    transform: scaleY(0);
}

/* 中央品牌圖示＋文字：跟 header／抽屜同一份 SVG＋文字（同一顆 viewBox
   與 path，本檔開頭「品牌標記」一節同款做法，SSOT 精神一致——這三處都是
   刻意各自複製同一份 SVG，不是抽共用元件，理由見 _header.php 同款註解）。
   淡入浮現、格子開始掀開時快速淡出（.3s，比格子掀開的總時間短一截）——
   不用等格子全部收完，動作快一拍才不會跟底下重新出現的 Hero 疊在一起
   顯得拖沓。 */
.hero-opening-brand {
    position: absolute;
    inset: 0;
    z-index: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: .75rem;
    opacity: 0;
    transform: translateY(14px);
    transition: opacity .4s ease-out, transform .4s ease-out;
}

.hero-opening.is-brand-in .hero-opening-brand {
    opacity: 1;
    transform: none;
}

.hero-opening.is-open .hero-opening-brand {
    opacity: 0;
    transition: opacity .3s ease-in;
}

.hero-opening-brand-icon {
    width: clamp(2.5rem, 4vw, 3.5rem);
    height: auto;
}

.hero-opening-brand-text {
    font-size: clamp(1.25rem, 2vw, 1.75rem);
    font-weight: var(--fw-medium);
    letter-spacing: .02em;
    color: hsl(var(--heading));
}

@media (prefers-reduced-motion: reduce) {
    .hero-opening {
        display: none;
    }
}

/* ─── Hero：editorial grid 版型（2026-08-14 改版）──────────────────
   「首頁 Hero 版型修改指令」：拿掉桌曆翻頁機制，改成大圖主視覺＋右側細線
   Grid＋兩個矩形模組（寬＝文字、窄＝切換箭頭）的極簡構圖。只參考需求書
   附圖的構圖手法，品牌／文字／配色沿用本站既有 token，不新增色票、
   不做超大背景英文、不做一般左文右圖 Banner。

   【DOM／CSS 對應】結構見 home.php 同一段落開頭的說明；這裡只列各層
   在 CSS 裡各自負責什麼：
     .hero          最外層色帶，給高度（min-height）與 overflow:hidden
     .hero-inner    填滿 .hero，是底下三層的定位容器（position:relative）
     .hero-media    大圖，四堂課疊放，只有 .is-active 可見（絕對定位鋪滿）
     .hero-grid     矩形模組專用的對齊線（水平＋垂直各一條，跟 .hero 自己
                    既有的十字細線 .hero::before/::after 不是同一組——
                    十字線對齊桌機導覽，這裡的線對齊模組本身）
     .hero-modules  寬矩形（文字）＋窄矩形（箭頭）兩個模組並排
     .hero-pageno   頁碼（沿用改版前既有的 01/04 語彙）

   【不對稱構圖】模組刻意貼右下角，不置中、不平均分割——大圖本身才是
   主視覺，模組只是疊在上面的资訊層。

   【動畫】只有淡入與淡出：Hero 圖片／Grid 線／文字模組進場淡入
   （見 .hero-media-item:first-child.is-active／.hero-grid／.hero-text-
   module 的 @keyframes），箭頭點擊後文字淡出→換內容→淡入（見 home.js）。
   不做自動輪播、不做大幅縮放、不做視差。 */

.hero {
    position: relative;
    display: flex;
    overflow: hidden;
    background: hsl(var(--background));
    padding: 0;
    margin: 0;
    /* ⚠ 2026-08-14 客戶「將原有的header的高刪除讓圖片的上方不要有空間，
       直接移上去」——原本這裡用 padding-top:var(--header-h) 幫 header
       留一段內距（見下面保留的舊註解，說明當初是仿造「沒有 Hero 的內頁
       靠 .wf-frame 讓出 header 高度」那一套邏輯），但那個邏輯本來就只
       適用「header 不透明、蓋住内容」的情境；這個 Hero 的 header 是透明
       疊在圖片上（本站從很早的 header 改版就是這個方向），根本不需要
       讓出空間——多出來的 padding-top 只會把滿版圖片往下推、在最上面
       露出一截 --background 底色的空隙。移除後圖片直接貼齊視窗最上緣，
       min-height 對應改回完整的 100svh（不再扣掉 header 高度），Hero
       的可視高度也跟著補滿，不會因為拿掉 padding-top 而在底部反過來
       少一截。 */
    min-height: 100svh;
}

/* ⚠ 2026-08-14 客戶指定「hero大圖寬裁切右邊對齊垂直線，垂直線以左為
   圖片寬，垂直線右圖片皆裁切」——width 收到 --hero-line-x、overflow:hidden
   硬裁，裁切邊界精準貼齊垂直線（.hero::before，同一個 --hero-line-x）。
   中途一度誤會成「不裁切、改滿版＋遮罩」整段拿掉又加回來——客戶事後
   澄清：裁切要保留，「跟實體課程一樣遮罩方式出現」講的是另一件事
   （矩形遮罩顯影動畫，見下面 .hero-mask-grid），兩者同時成立，不是二選一。
   ⚠ overflow:hidden 加在這一層：.hero-mask-grid／.hero-media-item 都是
     inset:0 的直接子層，裁切邊界只要卡在 .hero-media 自己的 width 就會
     一併裁到遮罩層與出血的圖片，不用個別處理。
   ⚠ ≤1199.98px 手機/平板斷點另外覆寫回 width:100%／overflow:visible
     （見下面該斷點的 .hero-media）：那個寬度模組已經退到圖片下方、不再
     疊在圖片右側，沒有「線右邊要挖空」的需求，圖片維持滿版。 */
/* 2026-08-17 客戶「hero圖片請裁切到寬矩形結束」——來回澄清了幾輪：
   第一版誤解成「寬矩形」（.hero-modules 整塊）的左緣，客戶回報「裁切
   太多了」；改成整塊左緣的固定算式後又回報「太窄了...本來圖片的矩形
   沒那麼靠右」（那是另一個 bug，導覽改全展開後模組整體位置跟著跑掉，
   已在別處修正，見 .hero-modules 那段）；接著客戶再指「圖片請到白色
   矩形的寬結束」，附截圖確認「白色矩形」specifically 指 .hero-text-
   module（PERSONAL GROWTH 那張白卡），不是灰色的箭頭窄矩形，也不是
   兩者合計的整個 .hero-modules 區塊——裁切邊界應該正好落在白卡右緣
   （白卡與灰色窄矩形中間那條分隔線），灰色窄矩形右邊那一截才是「已經
   結束」的地方，圖片不該再往右延伸過去蓋住它。
   ⚠ 這條分隔線在 CSS grid 裡沒有對應的 DOM 元素可以用 calc() 精確
     算出來（grid-template-columns: minmax(0,1fr) 1px minmax(72,92px)，
     窄矩形欄寬是彈性的 minmax，實際寬度看瀏覽器當下怎麼分配剩餘空間，
     不是固定比例）。改用 home.js 直接量 .hero-text-module 實際渲染
     出來的右邊緣（見「Hero 十字線」那段新增的量測邏輯），寫進
     --hero-media-crop，這裡讀這個變數；fallback 維持 calc(100% -
     var(--section-x) - min(30vw, 30rem))（等於 .hero-modules 整塊
     左緣，JS 還沒跑時的降級位置，比「完全不裁切」更接近正確答案）。 */
.hero-media {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    width: var(--hero-media-crop, calc(100% - var(--section-x) - min(30vw, 30rem)));
    overflow: hidden;
    z-index: 0;
}

/* ── Hero 大圖矩形遮罩顯影（2026-08-14 客戶「hero大圖改跟實體課程一樣
   遮罩方式出現」，指的是 course_detail.php／courses/strategy 那頁的
   .cd-hero-mask-grid：整塊先蓋一層跟背景同色的矩形遮罩，圖片解碼完成
   後遮罩依序縮回，露出底下完整的圖）─────────────────────────────────
   ⚠ 不沿用 .cd-hero-media 的「Z 形」clip-path：那是 course_detail 左文
     右圖兩欄構圖專屬的形狀語言，Hero 是滿版矩形大圖，沒有那個構圖前提，
     這裡改用單純 4 欄 × 2 列（8 塊等大矩形）鋪滿整張圖，不切角、不留空格。
   ⚠ 遮罩色用 --background（Hero 自己的底色），不是 --surface-grey（那是
     course_detail 那個色帶的底色）——同一個「遮罩色＝所在容器背景色」的
     邏輯，換成各自所在的背景 token，不是複製同一個顏色。
   ⚠ gap:0（原因見 .cd-hero-mask-grid 同款註解）：網格線之間如果留縫隙，
     縫隙會直接透出底下的圖，縫隙處會在遮罩完整蓋著時透出一條細線。
   ⚠ 是 .hero-media 的直接子層、不是每張輪播圖（.hero-media-item）各自
     一份：四張圖疊放淡入淡出的既有機制不受影響，遮罩只在頁面剛載入時
     顯影一次，不會每次切換輪播圖重播。
   ⚠ z-index:1——蓋在圖片（z-index 預設 auto，疊在 .hero-media 的
     stacking context 內）之上，同時還在 .hero-grid（z-index:2）／
     .hero-modules（z-index:3）之下，不會擋到線或矩形模組。
   ⚠ JS 播放邏輯（decode-gate／data-order 排序／雙 rAF／
     prefers-reduced-motion）比照 course_detail.php 同一段，見 home.js
     「Hero 矩形遮罩顯影」那段。 */
.hero-mask-grid {
    position: absolute;
    inset: 0;
    z-index: 1;
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    grid-template-rows: repeat(2, 1fr);
    grid-template-areas:
        "a b c d"
        "e f g h";
    gap: 0;
    pointer-events: none;
}

/* 2026-08-19 客戶回報遮罩動畫過程中格與格交界處會閃過一絲顏色（紅／黃，
   依那個位置的圖片內容而定）——實測用 puppeteer 逐格截圖抓到：gap:0
   的 grid 搭配 1fr 欄寬，容器實際像素寬高不見得能被欄數整除，相鄰格子
   各自的邊界四捨五入到不同的次像素，兩塊各自播放中（stagger 造成同一
   時刻不同格在不同縮放進度）時，交界處偶爾會漏出一條次像素寬的縫，
   底下的圖從那條縫透出來，看起來像「發光」。
   ⚠ margin:-1px 讓每塊遮罩的實際渲染範圍比它自己的 grid-area 格子大
     1px（往四個方向各多蓋 1px，蓋住鄰格的邊），縫隙不管怎麼四捨五入
     都不會露出來；不是「容器補一層同色背景蓋住縫隙」那個已經試過失敗
     的做法（見 .cd-hero-mask-grid 那段註解——整層蓋住會讓遮罩全部縮回
     後圖片還是被擋住），這裡的多蓋範圍跟著每塊遮罩自己的 transform 一起
     動、一起消失，全部縮完之後跟沒加這個 margin 一樣乾淨。 */
.hero-mask {
    background: hsl(var(--background));
    margin: -1px;
    transition: transform 560ms cubic-bezier(0.76, 0, 0.24, 1);
    will-change: transform;
}

.hero-mask--a { grid-area: a; }
.hero-mask--b { grid-area: b; }
.hero-mask--c { grid-area: c; }
.hero-mask--d { grid-area: d; }
.hero-mask--e { grid-area: e; }
.hero-mask--f { grid-area: f; }
.hero-mask--g { grid-area: g; }
.hero-mask--h { grid-area: h; }

/* 靜止狀態（縮回動畫播放前）：8 塊各自的 transform-origin／起始 scale，
   跟 course_detail 同一招——scaleX／scaleY 交替、靠左右／上下分組製造
   變化，不是隨手排；上排四塊分兩組各自往左右縮，下排四塊分兩組各自
   往上下縮（跟 course_detail 6 塊版的分組邏輯一致，只是這裡是 8 塊）。 */
.hero-mask--a, .hero-mask--e { transform-origin: left;   transform: scaleX(1); }
.hero-mask--d, .hero-mask--h { transform-origin: right;  transform: scaleX(1); }
.hero-mask--b, .hero-mask--c { transform-origin: top;    transform: scaleY(1); }
.hero-mask--f, .hero-mask--g { transform-origin: bottom; transform: scaleY(1); }

/* 播放後的目標狀態：scaleX(0)／scaleY(0)，跟靜止狀態同一個 transform
   函式（只換數值）——瀏覽器內插同一函式才不會在不同瀏覽器出現分解方式
   不一致的過場結果。data-order 決定的先後順序由 JS 寫 transition-delay
   控制（見 home.js）。 */
.hero.is-hero-revealed .hero-mask--a,
.hero.is-hero-revealed .hero-mask--d,
.hero.is-hero-revealed .hero-mask--e,
.hero.is-hero-revealed .hero-mask--h {
    transform: scaleX(0);
}

.hero.is-hero-revealed .hero-mask--b,
.hero.is-hero-revealed .hero-mask--c,
.hero.is-hero-revealed .hero-mask--f,
.hero.is-hero-revealed .hero-mask--g {
    transform: scaleY(0);
}

@media (prefers-reduced-motion: reduce) {
    .hero-mask {
        transition: none;
    }

    .hero-mask-grid {
        display: none;
    }
}

/* ⚠ 2026-08-14 客戶回報「hero本身的高跟圖片不一致，矩形貼的是hero的底
   不是圖片」，附截圖顯示服務專區跟圖片底部之間露出一截空隙——實測
   .hero-media-item 的高度確實比 .hero/.hero-media 矮了約 16px（四張
   輪播圖都一樣），但排查過 transform:scaleX(-1)（本 session 稍早加的
   鏡像翻轉）、height:100% 是否解析失敗、窄螢幕 RWD 覆寫誤觸這三個
   假設都不是成因，實際卡在 .hero-media-item 本身即使 top:0/bottom:0
   都是 0，computed height 還是算不滿容器（可能是內部 <picture>/<img>
   的 intrinsic 比例在某個瀏覽器行為裡影響到父層高度解析，還沒有抓到
   確切是哪一步）。
   客戶指定的修法：「圖片放大拆切試看看」——不繼續深究精確成因，改用
   出血蓋過去：inset 從 0 改成負值，讓這一層比 .hero-media 容器再大
   一圈，不管實際差幾 px 都能穩定蓋滿。.hero-inner 本身有
   overflow:hidden，出血的部分會被裁掉，不會外溢到版面其他地方。 */
.hero-media-item {
    position: absolute;
    inset: -4%;
    opacity: 0;
    pointer-events: none;
    transition: opacity 600ms var(--ease-out);
}

.hero-media-item.is-active {
    opacity: 1;
    pointer-events: auto;
}

/* SSR 預設就是 .is-active 的第一張圖：transition 只在「有變化」時觸發，
   初次進站沒有前一個 opacity 值可以比較，改用 animation 讓它一載入就
   自動淡入——JS 沒跑時也會播（純 CSS，不依賴任何腳本）。 */
.hero-media-item:first-child.is-active {
    animation: heroMediaIn 900ms var(--ease-out) both;
}

@keyframes heroMediaIn {
    from { opacity: 0; }
    to   { opacity: 1; }
}

/* 2026-08-27 客戶「hero 圖片點擊跳轉該頁面」——大圖外面新包的那層連結
   （home.php 的 .hero-media-item 裡面）。
   ⚠ 一定要自己絕對定位鋪滿：底下的 <picture>/<img> 本來就是 inset:0 的
     絕對定位（見下一條規則），全部脫離文件流，連結若留在靜態流裡會算出
     0 高、整張圖都點不到——看起來就跟「沒加連結」一模一樣。
   ⚠ inset:0 對齊的是 .hero-media-item（那層才是 position:absolute），
     所以可點範圍＝那張圖實際涵蓋的範圍（含它 inset:-4% 的出血）。
     <picture>/<img> 的定位祖先從 .hero-media-item 換成這一層，
     但兩者是同一個框，量出來的位置與大小完全相同，渲染零變化。
   ⚠ 不設 z-index：跟 .hero-mask-grid（z-index:1，且 pointer-events:none）
     ／.hero-grid（2）／.hero-modules（3）的既有疊層關係一個字都不動，
     文字卡與上一則／下一則照樣壓在圖片上面、照樣點得到。 */
.hero-media-link {
    position: absolute;
    inset: 0;
    display: block;
}

.hero-media-item picture,
.hero-media-item img {
    position: absolute;
    inset: 0;
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: 50% 42%;
}

/* 2026-08-14 客戶：「hero圖片左右翻轉」——把圖片內容水平鏡像，不是搬動
   模組或版面位置（模組仍然貼右下角，見 .hero-modules）。
   ⚠ 用 transform:scaleX(-1) 而不是換一批鏡像素材：純視覺翻轉，不影響
     object-fit/object-position 的裁切邏輯，四張圖（.hero-media-item）
     共用同一條規則，不必每張各自處理。 */
.hero-media-item img {
    transform: scaleX(-1);
}

/* 2026-08-14 客戶「hero大圖幫我改淺一點，讓圖片亮一點灰調一點」——用
   CSS filter 調亮度／飽和度，不重新處理原始圖檔：可逆、四張輪播圖共用
   同一條規則，之後客戶要調整幅度或換回原圖，改這裡的數字或整條刪掉
   即可，不用重新出圖。 */
.hero-media-item img {
    filter: brightness(1.12) saturate(.75);
}

/* ⚠ 2026-08-14 客戶「矩形的上方再多畫一條整個hero寬的線」——這個元素
   原本的註解就寫著「一條水平線壓在兩個模組正上方」，但實際的 `bottom`
   值跟 `.hero-modules`的 `bottom` 是同一個數字，等於畫在模組的**底部**
   而不是上方，被模組自己的實色背景整個蓋住，形同沒有這條線（跟自己的
   註解對不上，是這次改版留下的半成品）。這次順便修正：
     1) 寬度從「跟模組一樣寬」改成整個 Hero 滿版（客戶明確要求「整個
        hero寬」），改用 left:0; right:0 而不是 width:var(--hero-module-w)。
     2) 位置從「模組底部」改成「模組上方」，讀 JS 量出來的
        --hero-modules-top（見 home.js「Hero 模組對齊十字線」那段），
        往上再留 1rem 呼吸空間，不貼著模組邊緣。
   顏色沿用既有 --border token，跟 .hero::before/::after 那組十字線同一顆，
   不新增色票。
   ⚠ 2026-08-14 客戶「矩形上面的線下移一點」——呼吸間距從 1rem 收到
     .375rem，線更貼近矩形一點，不用改 --hero-modules-top 本身（那個
     變數是 JS 量出來的矩形實際上緣，量測邏輯沒有問題，只是線離矩形的
     距離要縮短）。 */
/* 2026-08-14 客戶「第二條水平線沒改到」——這條（矩形模組正上方那條，
   跟上面 .hero::before/::after 那組十字線是不同元素）當時漏改，一併
   改成跟其他線同一個黑70%，不再用 --border 淡灰。
   ⚠ 客戶回報「還是沒對齊」——排查過 --hero-modules-top 本身的量測時機
     （home.js「Hero 十字線」那段的 animationend 修正，已經確認數值精準
     等於矩形實際上緣），問題出在這裡：就算 --hero-modules-top 完全準確，
     上面的 `- .375rem` 仍然會讓線刻意畫在矩形上緣**再往上**一點，兩者
     之間留一條看得出來的縫——客戶要的「對齊」是線緊貼矩形上緣、中間
     不留縫，不是留一段呼吸間距。拿掉 `- .375rem`，top 直接等於
     --hero-modules-top，線的下緣＝矩形的上緣，不再留任何間距。
   ⚠ 2026-08-17 客戶「hero第二條橫線也要白色現在看還是黑色」——上面
     .hero::before/::after 那組十字線後來改成白色 70%
     （hsl(var(--brand-foreground) / .7)），但這條是獨立元素，沒有
     跟著同步改，一直停在稍早的黑 70%（hsl(var(--heading) / .7)）沒被
     更新到，這裡補上跟十字線同一個顏色。 */
.hero-grid {
    position: absolute;
    z-index: 2;
    left: 0;
    right: 0;
    top: var(--hero-modules-top, 70%);
    height: 1px;
    background: hsl(var(--brand-foreground) / .7);
    opacity: 0;
    animation: heroGridIn 700ms var(--ease-out) 260ms both;
}

@keyframes heroGridIn {
    to { opacity: 1; }
}

/* ⚠ 2026-08-14 客戶：「矩形位置請對準兩條線的位置，窄的長條左邊請對齊
   直線，寬的右邊對齊直線」——寬矩形（文字模組）與窄矩形（箭頭模組）
   中間那條分隔線，要對齊 Hero 十字線裡的垂直線（.hero::before，見上面
   ≈750 行，讀同一個 --hero-line-x）。
   ⚠ 原本用 `right: var(--section-x)` 錨右邊，箭頭模組（固定寬度上限
     92px）的右邊界因此釘死在「Hero 右緣 − section-x」，分隔線的絕對
     位置＝這個右邊界往左推 92px+1px，跟 --hero-line-x 完全是兩組獨立
     算式，湊巧對不上。改成用 `left` 錨左邊：JS 量出「文字模組目前的
     實際寬度」，用 --hero-line-x（分隔線的目標 x 座標）反推整塊矩形該
     從哪裡開始（left = hero-line-x − 文字模組寬度），分隔線的位置就會
     精準落在 --hero-line-x 上，不再是巧合。
   ⚠ 2026-08-14 客戶「圖片請剛好拆切到跟矩形底部一樣位置」——bottom 從
     clamp(2rem,7vw,4.5rem) 改成 0：矩形貼齊 Hero 最底部，而 .hero-media
     本來就用 inset:0 鋪滿整個 Hero，兩者的底邊因此變成同一條線，圖片
     不會在矩形下方多露出一截。
   ⚠ 2026-08-14 客戶「矩形可以面積高一點，讓他大概在圖片一半的位置」
     （附參考圖：矩形疊圖大約從圖片中線開始往下）——加上 height（百分比）。
     bottom 維持 0（前一版就貼齊 Hero 底部，這次沒有要改），height 一給
     百分比，等於矩形上緣自動落在對應的高度線，不用另外算 top。
     百分比高度能解出來是因為 .hero-inner 用 flex:1 撐出「明確」高度
     （見 ≈1077 行 .hero-inner 那條註解），不是 auto，.hero-modules 才
     吃得到百分比高度；手機斷點（≤1199.98px）.hero-modules 改回
     position:relative 且 .hero-inner 那邊也改回 flex:none／auto 高度，
     百分比高度規格上會自動退回 auto，不需要另外覆寫拿掉。
     Grid 對齊線（.hero-grid，見上面 ≈610 行）本來就是讀 JS 即時量出來的
     --hero-modules-top，不是寫死的數字——矩形變高、上緣跟著上移之後，
     下次 resize/load 重新量測就會自動貼著新的上緣位置，不用改 home.js。
   ⚠ 2026-08-14 客戶「有點太高了，矩形低回來一點」——第一版給 50%（剛好
     一半）改太多，收回到 42%，上緣跟著往下退一點，不再頂到一半那條線。 */
.hero-modules {
    position: absolute;
    z-index: 3;
    left: var(--hero-modules-left, calc(100% - var(--section-x) - min(30vw, 30rem)));
    bottom: 0;
    height: 42%;
    display: grid;
    grid-template-columns: minmax(0, 1fr) 1px minmax(72px, 92px);
    align-items: stretch;
    width: var(--hero-module-w, min(30vw, 30rem));
    min-width: 300px;
    opacity: 0;
    transform: translateY(10px);
    animation: heroModulesIn 700ms var(--ease-out) 340ms both;
}

@keyframes heroModulesIn {
    to { opacity: 1; transform: none; }
}

/* 2026-08-17 新增：跟 heroModulesIn 同精神的淡入，但只動 opacity、不動
   transform——給那些「transform 已經另有用途（例如 translateY(-50%)
   置中）」的元素用，heroModulesIn 的 to{transform:none} 會把置中位移
   一起洗掉，這兩顆關鍵影格不能共用。 */
@keyframes heroFadeIn {
    to { opacity: 1; }
}

.hero-modules-divider {
    grid-column: 2;
    background: hsl(var(--border) / .7);
}

/* 2026-08-14 客戶：「兩塊矩形分別是寬是白色跟窄的是深灰色」——寬矩形
   （文字模組）改白色底、窄矩形（箭頭模組，見下面 .hero-arrow-module）
   改深灰色底，兩者原本共用同一個深色（--foreground，實測 rgb(26,26,26)
   接近全黑），視覺上分不出兩塊。
   ⚠ 白色用 --brand-foreground（#FFFFFF，色票裡本來就有的純白），不是
     --background（頁面底的米白/淺灰，客戶說的是「白色」不是「米白」）。
   ⚠ 底色從深翻白之後，裡面的文字必須跟著從「淺色壓深底」反過來變成
     「深色壓淺底」，否則白底配淺色字幾乎看不見——四行文字（eyebrow／
     title／sub）色票全部對應換成深色系，換色邏輯跟其餘三行一起說明，
     不必每行重複。 */
/* 2026-08-14 客戶「白底」「矩形透明度調70%」——寬矩形（文字模組）的白底
   從全不透明改成 70% 透明度，不是新顏色，同一個 --brand-foreground
   token 只加 /.7。 */
.hero-text-module {
    grid-column: 1;
    position: relative;
    display: flex;
    flex-direction: column;
    padding: clamp(1.25rem, 2.2vw, 1.75rem);
    background: hsl(var(--brand-foreground) / .7);
    color: hsl(var(--heading));
    transition: opacity 220ms var(--ease-out);
}

.hero-text-module.is-swapping {
    opacity: 0;
}

.hero-text-item { display: none; }

/* ⚠ 2026-08-14 客戶「文字也要隨著高度重新編排」——矩形加高之後（見上面
   .hero-modules 的 height 註解），原本 .hero-text-item 只是 display:block
   的單一區塊，靠 .hero-text-module 的 justify-content:center 整包置中，
   矩形變高後上下會空出兩截等距留白，文字沒有跟著「重新編排」，只是
   飄在中間。
   改法：.hero-text-item 自己也變成 flex 直排（flex:1 撐滿 .hero-text-module
   的整個高度），文字群組（eyebrow／title／sub）留在預設的 flex-start（貼
   上緣），CTA 用 margin-top:auto 頂到最下面（見下面 .hero-text-cta）——
   矩形多出來的高度變成「文字群組」與「CTA」之間的彈性間距，兩端都貼齊
   矩形邊緣，不是整包文字漂浮在正中央。
   ⚠ .hero-text-module 因此拿掉 justify-content:center：交給裡面唯一的
     .hero-text-item 自己用 flex:1 撐滿，父層不用再置中。 */
.hero-text-item.is-active {
    display: flex;
    flex-direction: column;
    flex: 1;
    /* 2026-08-14 客戶「文字不要在矩形裡位置這麼高，下移一點」——矩形
       貼上緣的 flex-start 讓文字群組緊貼 .hero-text-module 自己的
       padding-top（clamp 1.25–1.75rem），矩形變高後這個間距顯得太窄，
       文字整包看起來卡在很上面。加一段額外的 padding-top，讓文字群組
       在原本的模組內距之外再往下退一點，CTA 仍然靠 margin-top:auto
       頂到最底部，不受這裡影響。 */
    padding-top: 1.5rem;
}

.hero-text-eyebrow {
    margin: 0 0 .625rem;
    font-family: var(--font-eyebrow);
    font-size: var(--text-meta);
    letter-spacing: var(--tracking-meta);
    color: hsl(var(--slate));
}

.hero-text-title {
    margin: 0 0 .625rem;
    font-size: clamp(1.15rem, 1.7vw, 1.5rem);
    line-height: 1.35;
    font-weight: var(--fw-heading);
    letter-spacing: var(--tracking-title);
    color: hsl(var(--heading));
}

.hero-text-sub {
    margin: 0 0 1.25rem;
    font-size: var(--text-meta);
    line-height: var(--lh-body);
    font-weight: var(--fw-light);
    color: hsl(var(--muted-foreground));
}

/* 2026-08-14 客戶「文字副標題內容多一點」——矩形加高後 .hero-text-sub
   那行短短一句 tagline 撐不滿新增的高度。$heroCourses 每一筆本來就有
   一個 intro 欄位（比 sub 更長的一段課程介紹文案），改版前沒有任何地方
   在用（grep 全站確認過），不是新造文案、是啟用既有既有欄位——沿用
   SSOT：同一份資料只多顯示一行，不是另外寫死新句子。
   ⚠ 獨立成一個新 class（不是併進 .hero-text-sub 那顆 <p>）：sub 是短
     tagline（--text-meta／--fw-light），intro 是完整句子，字級/顏色
     沿用內文慣例（--text-body／--fw-normal 一類），混在同一個節點裡
     沒辦法分別給樣式。 */
.hero-text-intro {
    margin: 0 0 1.25rem;
    font-size: var(--text-body);
    line-height: var(--lh-body);
    font-weight: var(--fw-normal);
    color: hsl(var(--muted-foreground));
}

/* CTA 沿用全站唯一的 .btn-pill--solid（AERIAL 藍底＋黑字，深底淺底一律
   同一組色，不另外反相——第 18 節既有明文規定）。
   ⚠ margin-top:auto——見上面 .hero-text-item.is-active 的註解，靠這個
     把 CTA 頂到矩形最底部，文字群組留在上緣，兩端跟著矩形高度重新
     編排，不是整包置中。
   ⚠ align-self:flex-start——客戶「按鈕不用跟著拉寬」：.hero-text-item
     是 flex column，預設 align-items:stretch 會讓子項的寬度撐滿整欄，
     膠囊按鈕因此被拉成跟文字欄一樣寬。這個站上 .kz-intro／.cal-cta 等
     好幾處左欄文字＋按鈕的組合都是同一招（見 ≈15683 行同款註解），
     不是這裡第一次用。 */
.hero-text-cta {
    display: inline-flex;
    align-self: flex-start;
    margin-top: auto;
}

/* 2026-08-27 客戶「hero 文字小卡點擊也要跳該頁面」——整張文字卡可點。
   做法是「延伸連結」：讓卡片裡既有的那顆 CTA 長出一塊透明覆蓋層，蓋滿
   .hero-text-module 整塊卡，不新增任何 DOM、也不把整張卡包成一條 <a>
   （卡片裡已經有 .hero-text-cta 這個 <a>，再包一層就是巢狀連結＝無效的
   HTML）。點卡片任何位置＝點那顆 CTA＝同一個網址，不會有第二份要維護。
   ⚠ 用 ::before 不是 ::after：.btn-pill::after 已經是右側那顆箭頭徽章
     （本檔 ≈313 行），佔用中，改它會讓全站按鈕的箭頭消失。
   ⚠ inset:0 對齊 .hero-text-module（本檔 ≈926 行 position:relative；
     其他斷點的覆寫只有 22518 那條改成 absolute，仍是定位元素，
     沒有任何一處是 static，所以每個斷點的覆蓋範圍都正好是那塊卡）。
     箭頭模組（.hero-arrow-module）是它的**兄弟節點**、不在 inset:0 的
     範圍內，上一則／下一則照樣點得到。
   ⚠ 覆蓋層不佔版面：position:absolute 讓它從 .btn-pill 自己的 inline-flex
     排版脫離，膠囊的寬高與卡內文字位置一個 px 都不會動。
   ⚠ 滑過卡片任何位置都會觸發 CTA 的 hover（徽章展開、底色加深）——這是
     刻意留著的回饋，讓使用者看得出來整張卡是可以點的。 */
.hero-text-cta::before {
    content: "";
    position: absolute;
    inset: 0;
}

/* 2026-08-14 客戶「窄的是深灰色」，先用色票裡標註「深灰」的 --slate
   （#7C7D80）——隨後客戶附了一張實際色票截圖「深灰色用這個灰」，肉眼
   比對比 --slate 稍微中性、不帶暖色調，改成直接寫死客戶截圖的色值。
   ⚠ 用固定色碼、不是 token：這是客戶截圖指定的精確色號，不是套用站上
     既有色階剛好數值相近（跟 logo 那組 #4a4636 是同一種「客戶給定色值」
     的處理方式）。如果之後客戶提供正式色號（Hex／Pantone），把這個值
     換掉即可，選擇器不用動。
   ⚠ 箭頭圖示／分隔線顏色（下面兩條）不用跟著改：這個深淺度配原本的
     淺色圖示／分隔線對比沒有問題，不必連動調整。 */
.hero-arrow-module {
    grid-column: 3;
    display: flex;
    flex-direction: column;
    background: #808285;
}

.hero-arrow {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    border: 0;
    background: transparent;
    color: hsl(var(--background));
    cursor: pointer;
    transition: background var(--transition), color var(--transition);
}

/* 2026-08-25 客戶指定：hover 改成「藍底黑箭頭」——沿用全站唯一強調色
   --accent-blue（AERIAL），箭頭本身用 currentColor（.hero-arrow-glyph
   的 border-color），改 color 就會連著箭頭一起變黑，不用另外選取。 */
.hero-arrow:hover,
.hero-arrow:focus-visible {
    background: hsl(var(--accent-blue));
    color: hsl(var(--foreground));
}

.hero-arrow-divider {
    height: 1px;
    background: hsl(var(--background) / .28);
}

/* 極簡箭頭：純 CSS 邊框畫出的細線 chevron，不用大型圓形按鈕、不用
   Bootstrap 背景圖 icon（CSP 友善）——沿用全站 .carousel-arrow 已經在用
   的「邊框繪製」技法，但不套用那顆圓框樣式（客戶這次明確要求「不要使用
   大型圓形箭頭」）。
   ⚠ 2026-08-14 客戶「箭頭放大點」——9px 改 14px，邊框跟著從 1px 加粗到
     1.5px：純放大線段長度、線本身不加粗的話，箭頭變大反而顯得更細更
     不明顯，跟文字/矩形一起放大時視覺比重會失衡。 */
.hero-arrow-glyph {
    width: 14px;
    height: 14px;
    border-right: 1.5px solid currentColor;
    border-bottom: 1.5px solid currentColor;
}

/* 2026-08-14 客戶：「箭頭請左右方向指不要上下，但位置一樣上下」——兩顆
   按鈕仍然一顆在上一顆在下（.hero-arrow-module 的 flex-direction:column
   沒有動），只改箭頭圖形本身指的方向：從上下 chevron 改成左右 chevron。
   ⚠ 角度換算：這個「邊框畫角」的技法裡，45deg 指下、135deg 指左、
     -135deg 指上、-45deg 指右（每 90deg 換一個方向）。上一版 prev 用
     -135deg（指上）、next 用 45deg（指下）；改成 prev=135deg（指左）、
     next=-45deg（指右），角度差維持同一個 180deg 對稱關係。
   ⚠ 微調用的 margin 跟著從垂直軸（margin-top/bottom）換成水平軸
     （margin-left/right）——原本 3px 是幫「上下」箭頭的視覺重心置中，
     方向轉成「左右」後偏移軸也要跟著轉 90 度，數值沿用同一個 3px。
   ⚠ 2026-08-14「箭頭放大點」把 glyph 從 9px 放大到 14px 後，這個置中
     偏移量跟著等比放大（3px × 14/9 ≈ 5px），不然箭頭變大、原本的
     3px 偏移比例上變太小，視覺重心會跑掉。 */
.hero-arrow--prev .hero-arrow-glyph { transform: rotate(135deg);  margin-left: 5px; }
.hero-arrow--next .hero-arrow-glyph { transform: rotate(-45deg); margin-right: 5px; }

/* 2026-08-14 客戶「底下數字線刪掉」——Hero 右下角「01/04」頁碼指示拿掉。
   用 display:none 隱藏，標記／JS 更新頁碼的邏輯都不動，只是不畫出來，
   之後要復原拿掉這一行即可。 */
.hero-pageno {
    display: none;
    position: absolute;
    z-index: 3;
    right: var(--section-x);
    bottom: calc(clamp(2rem, 7vw, 4.5rem) + 1rem);
    margin: 0;
    font-family: var(--font-eyebrow);
    font-size: var(--text-meta);
    letter-spacing: var(--tracking-meta);
    color: hsl(var(--foreground) / .55);
    opacity: 0;
    animation: heroGridIn 700ms var(--ease-out) 420ms both;
}

.hero-pageno-cur { color: hsl(var(--foreground)); }
.hero-pageno-sep { margin: 0 .3em; opacity: .6; }

@media (prefers-reduced-motion: reduce) {
    .hero-media-item,
    .hero-media-item:first-child.is-active,
    .hero-grid,
    .hero-modules,
    .hero-pageno {
        animation: none;
        opacity: 1;
        transform: none;
    }

    .hero-media-item.is-active { opacity: 1; }
    .hero-media-item:not(.is-active) { opacity: 0; }
    .hero-text-module { transition: none; }
}

/* ⚠ 2026-08-14「首頁 Hero 版型修改指令」手機版明講「不要硬套桌機版／
   大圖在上或作為主區域／文字模組可放在圖片下方或覆蓋圖片下緣／箭頭模組
   縮小／Grid 線條減少／不要造成水平捲動」——桌機版把模組貼右下角疊在
   圖片上，實測手機寬度會跟全站固定在右下角的浮動聯絡按鈕（影片／Podcast
   ／信箱那三顆圓鈕）疊在一起，兩層互相蓋住看不清楚。改成模組退出絕對
   定位、接在圖片下方走正常文件流，天然避開那三顆固定定位的按鈕，也符合
   規格「文字模組可放在圖片下方」的建議。
   ⚠ 箭頭模組不放在文字右側：桌機版箭頭貼右緣是刻意的（跟右側 Grid／
     文字模組同一條邊界），但手機版那個位置正好是 .wf-float（右下角浮動
     聯絡鈕，position:fixed + z-index:1050，本檔「選單開著時收起右下角的
     浮動鈕」那段已經記載這是既有限制）的所在——實測箭頭按鈕的點擊會被
     浮動鈕吃掉（elementFromPoint 命中 .wf-float 不是箭頭），不只是視覺
     疊在一起，是真的按不到。改成箭頭整排移到文字模組下方、水平並排、
     靠左對齊，徹底避開螢幕右下角那個固定區域，不是加大間距這種治標
     做法。
   ⚠ Grid 線條減少：疊在圖片上的模組對齊線（.hero-grid）只在「模組浮在
     圖片上」時才有意義，模組退到圖片下方之後這條線的參照對象消失，
     直接隱藏；箭頭改到文字下方一整排，也不需要桌機版那條模組間的
     垂直分隔線。
   ⚠ 斷點刻意用 1199.98px（不是手機常用的 767.98px）：.wf-float 貼齊
     螢幕右下角、位置不隨視窗寬度變化，而桌機版模組是 right:--section-x
     貼右緣——實測 900px（平板）寬度模組右緣一樣會鑽進 .wf-float 的範圍
     （1440 寬時 --section-x 夠大、兩者才對不上），所以「模組退到圖片
     下方」這個安全排版一路延伸到平板寬度，不是只在手機才切換。 */
@media (max-width: 1023.98px) {
    .hero {
        display: block;
        min-height: 0;
    }

    .hero-inner {
        flex: none;
    }

    .hero-media {
        position: relative;
        inset: auto;
        /* 平板寬度先給橫式比例，避免圖片被拉得過高——767.98 以下的真手機
           另外覆寫成 4/5（見下面獨立的 767.98 斷點），兩種寬度圖片比例
           不一樣，767.98 那條要放在後面才蓋得掉這裡。 */
        aspect-ratio: 16 / 9;
        /* ⚠ 蓋掉桌機版「裁切到垂直線」的寬度／裁切（見 ≈553 行）——這個
           斷點模組已經退到圖片下方（見 .hero-modules 同一斷點），不再
           疊在圖片右側，沒有「線右邊要挖空」的需求，圖片維持滿版。 */
        width: 100%;
        overflow: visible;
    }

    .hero-grid {
        display: none;
    }

    .hero-modules {
        position: relative;
        inset: auto;
        right: auto;
        bottom: auto;
        width: 100%;
        min-width: 0;
        display: block;
        transform: none;
    }

    .hero-text-module {
        padding: 1.5rem var(--section-x);
    }

    .hero-modules-divider {
        display: none;
    }

    .hero-arrow-module {
        flex-direction: row;
        justify-content: flex-start;
        gap: 1.25rem;
        padding: 0 var(--section-x) 1.5rem;
        background: hsl(var(--foreground));
    }

    .hero-arrow {
        flex: none;
        width: 2rem;
        height: 2rem;
    }

    .hero-arrow-glyph {
        width: 7px;
        height: 7px;
    }

    .hero-pageno {
        position: relative;
        right: auto;
        bottom: auto;
        margin-inline: var(--section-x);
        padding-top: .875rem;
        text-align: right;
    }
}

/* 真手機（跟上面 1199.98 斷點共用同一組堆疊排版，這裡只收窄圖片比例）：
   平板寬度用 16:9 橫式比例，但手機窄螢幕下 16:9 的圖會太扁太矮，改回
   4:5 直式比例，圖片主體看得清楚，不會整張擠成一條窄帶。 */
@media (max-width: 767.98px) {
    .hero-media {
        aspect-ratio: 4 / 5;
    }
}

/* ── Hero 十字細線（2026-08-14 客戶指定，附參考圖：頁首右上的選項卡片
   周圍有窗格分割線那種效果）─────────────────────────────────────────
   客戶原話「區塊背景四周用細線描邊」一開始被誤會成「導覽選項/字級切換器
   加外框」，後續三則訊息＋參考圖澄清：講的其實是 Hero 大圖區塊要畫線
   （不是導覽），而且不是四邊都框，是「整個高、整個寬各畫一條」——一條
   貫穿 Hero 整個高度的垂直線、一條貫穿整個寬度的水平線，形成窗格分割線
   的效果。導覽選項那組外框已經拿掉（見上一輪改動的還原）。
   ⚠ 用 ::before／::after 而不是額外的 <div>：純裝飾、不需要語意，
     偽元素本來就不進無障礙樹，也不用多寫標記。
   ⚠ z-index 實測修正：原本假設「線要被桌曆卡片自然遮住」，先寫成比
     .hero-inner（z-index:1）低的 0。但目前首頁版型（2026-08-03 改版後）
     .hero-inner 底下的 .cal-media 是滿版鋪底的照片（見 ≈8992 起「Hero
     改成圖片滿版」那段），會整片蓋掉 .hero 的背景層，導致 z-index:0
     的線完全被蓋住、畫面上什麼都看不到——不是桌曆「卡片」擋住一截，
     是整個 .hero-inner 從上到下、從左到右都不透明。改成 z-index:3，
     蓋過 .hero-inner（1）與 .cal-content（2），讓線條真的疊在照片與
     文字面板最上層，貫穿整個 Hero，符合客戶參考圖「線疊在畫面最上面」
     的效果（客戶參考圖本身線條也是壓在照片上，不是被前景擋住）。
   ⚠ 2026-08-14 客戶第二輪修正：「線是畫在右邊，導覽的左邊畫面的右邊」——
     垂直線原本錯畫在畫面**左邊**（貼齊 .hero-inner 的左內距），客戶要的
     其實是右邊：線要對齊桌機頁首導覽（.site-nav，≥1468px 時整組靠右）
     的左邊界，跟客戶參考圖裡「About/Explore/Stories/Contact 那欄左邊
     有一條窗格線」一致。
   ⚠ 2026-08-14 客戶第三輪再修正：「直條線往左移橫條線往上移，該區塊的
     描線是拿來對位右邊導覽列的空間」——講清楚這兩條線真正的用途是**框
     出導覽列所在的那塊空間**，不是貼著文字邊緣、也不是切在 Hero 正
     中央：垂直線要比導覽文字左緣再往左留一點呼吸空間，水平線要對齊
     導覽區塊的下緣（往下留一點），不是原本抓的 Hero 垂直中點（那個
     位置離導覽太遠，看起來像切穿整張照片，不是「框住導覽的空間」）。
   ⚠ 導覽的左邊界／下邊界都不是固定座標：.site-nav 是 3 欄 grid、內容
     隨文字長度自然撐開寬度，再用 margin-left:auto 推到 .inner 最右側，
     沒有辦法用單純的 CSS token 算出來（不像 --section-x 那樣是設計時
     就定案的固定格線）。改成 JS 量測（assets/front/js/home.js「Hero
     十字線對位桌機導覽的空間」那段）：讀 #wfNav 相對 .hero 的實際
     left／bottom，各自扣／加 24px 的呼吸間距，寫進 --hero-line-x／
     --hero-line-y 這兩個自訂屬性，只在 ≥1468px 且未進入 .is-compact
     （導覽還是頁首那個水平 grid，不是捲動後的抽屜）時才更新；這裡的
     `left`／`top` 各自讀對應的變數，帶預設值當退回（JS 還沒跑、或視窗
     寬度不在桌機導覽版型時）。
   ⚠ 2026-08-14 客戶指定「線跟文字還有logo都用同個顏色，黑70%」，隨後
     又改「都改成白色」，再改「垂直水平長線改回黑70%」（圖片裁切到垂直
     線之後，線右邊那塊區域露出 Hero 自己的米白底色，白線在米白底上
     幾乎看不見），這輪又改「垂直水平線改白色透明度70%」——跟導覽／
     logo 那組黑70%脫鉤，改回白色，只是加了 70% 透明度（不是不透明
     的純白）。同一顆 --brand-foreground token，只加 /.7，不是新色票。
     只在首頁 .hero 生效，不影響其他頁面的 .cx-hero／.cd-hero 等頁首
     色帶（客戶這輪沒提到其他頁面）。 */
.hero::before,
.hero::after {
    content: "";
    position: absolute;
    z-index: 3;
    pointer-events: none;
    background: hsl(var(--brand-foreground) / .7);
}

.hero::before {
    top: 0;
    bottom: 0;
    left: var(--hero-line-x, calc(100% - var(--section-x, 3.5rem)));
    width: 1px;
}

.hero::after {
    left: 0;
    right: 0;
    top: var(--hero-line-y, 40%);
    height: 1px;
}

/* 2026-08-17 客戶「hero的水平垂直線都拿掉」——上面這組十字線
   （.hero::before 垂直／.hero::after 水平）跟矩形模組正上方那條
   （.hero-grid，見 ≈747 行）三條線全部隱藏。用 display:none 而不是
   刪掉上面的定義／JS 量測邏輯（home.js 的 --hero-line-x／
   --hero-line-y／--hero-modules-top 照樣在算，只是不畫出來）——
   之後如果要復原，把這條規則拿掉即可，不用回頭重寫量測邏輯。 */
.hero::before,
.hero::after,
.hero-grid {
    display: none;
}

/* ⚠ 2026-08-13 客戶「白色矩形是排除了但那塊空間還是在，能讓圖片上移或是
   直接尺寸改高嗎」——header 展開選單時已經改透明（見 .has-menu-open
   .site-header 那組規則），但 ≤991.98px 的 header 是 wireframe.css:70
   的 position:sticky（吃版面流、佔住自己的 56px），不是 ≥992px 那條
   position:fixed。header 變透明後，它自己吃掉的那 56px 版面空間還在，
   只是不再畫任何東西，露出來的是 body 的頁面底色，不是 .hero 的背景層
   （.hero-bg，見上面「Hero 背景層」那段），兩者顏色不同才會看起來像
   一塊空隙。
   ⚠ 不是「圖片位置」的問題（.hero-bg 的 inset:-8% 沒有被任何內距推
     過），是 .hero **這個容器本身**沒有延伸到真正的視窗頂端。
   ⚠ 做法：只在選單展開、且 ≤991.98px 時，把 .hero 往上長回 header 那
     56px（margin-top 負值），同時補等量的 padding-top 抵銷，讓裡面的
     內容（桌曆等）視覺位置不變 —— 唯一變化是 .hero 的邊框盒往上延伸，
     它的 overflow:hidden 裁切線跟著往上移，讓本來就以 -8% inset 溢出
     容器的 .hero-bg 多露出一截，剛好填滿這塊空隙，不需要動 .hero-bg
     自己的任何定位數值。
   ⚠ 只在 ≤991.98px 生效：992–1467px 這段 header 已經是 position:fixed
     （.hero 另外用 padding-top:var(--header-h) 讓開，見「頁面沒有 Hero
     的內頁」那一節），本來就沒有這塊空隙，加了反而會把版面往上多推
     56px、內容跟著上移超過內距補償的量，是不必要的改動。 */
@media (max-width: 991.98px) {
    /* ⚠ 2026-08-13 複查追加：這個坑不是 .hero（首頁）獨有的——凡是「緊接在
       header 底下、section-bleed 出血滿版」的頁首色帶，選單展開、header
       變透明時都會露出同一塊 56px 空隙，因為挖空間的是 header 自己
       （position:sticky 仍佔著版面流），不是哪一頁的 hero 特別壞。
       客戶回報「實體課程」（.cx-hero）手機選單展開時上方沒填滿，複查同時
       補上其餘同樣是「section-bleed band + 緊接 header」的頁首色帶：
         .cx-hero   實體課程列表（courses_list.php）
         .cd-hero   實體課程內頁（course_detail.php）
         .kd-hero   知識專區內頁（knowledge_detail.php）
         .sv-hero   服務內頁（service_page_simpla.php，同時掛 simpla-open）
         .simpla-open  知識專區列表／電子報列表／搜尋頁共用的簡版頁首
                       （knowledge_list.php／newsletter_list.php／search.php）
       ⚠ 沒有列進來的頁面（例如會員相關幾頁的純 .band、service_page.php
         的 .page-hero）目前只是**沒被要求修**，不是「確認沒問題」——
         那幾頁如果之後也收到同一種回報，照同一個做法加進這條選擇器清單
         即可，不必重寫算式。
       ⚠ .has-menu-open 重複寫兩次（.has-menu-open.has-menu-open）是刻意
         墊高特異度，不是筆誤：.cx-hero／.cd-hero／.kd-hero／.sv-hero 這幾個
         色帶各自在後面的頁面專屬區塊還有自己的 padding-top（例如
         .page--wall .cx-hero 那條 clamp 內距），特異度同樣是兩個 class、
         但排在後面，一般寫法會被它蓋掉、margin-top 生效但 padding-top
         沒補回來，變成「背景補上了，但裡面的標題整組被吸上去 33px」。
         多疊一個 class 選擇器把這裡墊到三個 class 的特異度，不管頁面
         專屬那條寫在前面還是後面都贏，不用查每一頁的內距值。 */
    .has-menu-open.has-menu-open .hero,
    .has-menu-open.has-menu-open .cx-hero,
    .has-menu-open.has-menu-open .cd-hero,
    .has-menu-open.has-menu-open .kd-hero,
    .has-menu-open.has-menu-open .sv-hero,
    .has-menu-open.has-menu-open .simpla-open {
        /* ⚠ 2026-08-13 客戶回報還留一條白線——我這邊用真實分頁（非巢狀
           iframe）反覆測都是乾淨貼齊，猜測是客戶那台裝置的螢幕縮放比例
           或子像素進位，讓整數 calc(-1 * 56px) 在畫面上仍然差不到 1px
           的縫。margin-top／padding-top 各多留 1px 緩衝（57px 而不是
           56px），寧可 .hero 往上多長 1px（藏進裁切範圍內，肉眼看不出
           差異）也不要再有機會透出縫隙。 */
        margin-top: calc(-1 * var(--header-h) - 1px);
        padding-top: calc(var(--header-h) + 1px);

    }
}

/* ⚠ 2026-08-14「首頁 Hero 版型修改指令」拿掉桌曆之後，原本用來讓桌曆
   （紙色）從頁面底色浮起來的抽象漸層背景層（.hero-bg，含景深失焦
   filter）已經沒有存在理由——新版主視覺直接是一張完整的照片，照片本身
   就是內容，不需要另一層「襯托用」的漸層／紙紋／失焦背景（客戶新規格也
   明講「不要做成背景模糊圖」）。整層拿掉，不留 display:none 之類的殼。 */

/* Hero 內容鋪滿整個 .hero（大圖全版鋪底，右下角疊 Grid 與矩形模組），
   改用 flex + flex:1 讓這一層可靠地撐滿 .hero 的高度——.hero 本身只給
   min-height（不是 height），子層要拿到「實際可用高度」不能單靠
   height:100%（父層是 auto + min-height 時百分比高度不保證解得出來），
   flex 單一子項 + 預設 align-items:stretch 才是這個站上已經驗證過的
   做法（同一招用在 course_detail.php 的 .cd-hero/.cd-hero-inner）。 */
.hero-inner {
    position: relative;
    z-index: 1;
    flex: 1;
    overflow: hidden;
}


/* 知識專區與 Hero 同一塊米白底（--background），不是頁面預設的白 ——
   設計稿裡「Hero → 服務／課程（深色帶）→ 知識專區 → Footer」的米白是連續的，
   知識專區留白底會在深色帶下方切出一條突兀的亮塊。 */
/* ⚠ 米白底的文字一律加粗（2026-07-30 客戶指定「跟 footer 的文字粗度一樣」）。
   與 Footer 用**完全相同的兩級**：350（內文／清單／標籤）與 450（標題／連結）。
   ⚠ 與 Header／Footer／Hero 左頁同樣的做法：**重新定義 token**，不逐條改
     font-weight。這一區有 12 個以上用到字重的元件（區塊主標、英文小標、清單編號
     與標題、文章標題、摘要、詳述、YouTube／Podcast、標籤、兩顆查看更多…），
     逐條改必漏；CSS 變數只往下繼承，深色帶的服務專區與實體課程完全不受影響。
   ⚠ 為什麼保留兩級而不是像 Header 那樣統一 350：Header 是一列導覽，
     統一字重不影響閱讀；這一區有完整的文章層級（標題 → 摘要 → 詳述），
     標題若與內文同重，層級只剩字級在撐，讀起來會平掉。

   ── 留白 ──
   上方＝與深色帶的交界（與實體課程各出一半 → 接縫 224px）；
   下方＝頁面底部，用**單邊**的 --section-seam-end（176px）。
   ⚠ 上下不是同一個值：上緣有實體課程的下內距一起分擔，下緣只有這一段自己出，
     兩邊用同一個值的話頁尾看起來永遠只有接縫的一半（實測 176 vs 88，
     客戶反饋「上下離實體課程跟 footer 都太近」，下緣尤其明顯）。 */
.knowledge-section {
    --fw-light:  350;
    --fw-normal: 450;

    padding-block: var(--section-seam-band) var(--section-seam-end);
    background: hsl(var(--background));
}

/* 左欄撐滿整列的高度，清單才能用 margin-top:auto 沉到底 ——
   設計稿裡清單最後一列的底線是與右側內容下緣切齊的，不是跟著標題往下排。 */
.kz-intro {
    grid-column: 1;
    align-self: stretch;
    display: flex;
    flex-direction: column;
}

/* 區塊主標在這一欄之內，不需要 --space-title-gap 那段 7rem 的區塊級留白 */
.kz-intro .section-title { margin-bottom: 2.25rem; }

/* KNOWLEDGE ZONE 用卡其輔色（--brand-accent #D6C1A9），2026-07-30 客戶指定。
   ⚠ 已知取捨：卡其壓在米白底上的對比是 1.62:1（深色區塊上的同一個元素是 7.69:1）。
     這是視覺決定，不是疏漏 —— 這一區的英文小標、清單未選取狀態刻意都退成卡其，
     讓「目前選取的那一篇」用純黑跳出來，層級靠明暗差拉開。
     若日後要回到可讀性優先，改成 hsl(var(--muted-foreground))（7.85:1）即可。 */
.kz-intro .section-title .en { color: hsl(var(--brand-accent)); }

.kz-media  { grid-column: 3; }
.kz-panels { grid-column: 5; }

.kz-divider {
    grid-column: 2;
    justify-self: center;
    align-self: stretch;      /* .kz-layout 是 align-items:start，這裡要自己撐滿全高 */
    width: 1px;
    background: var(--rule-on-light);
}

/* ⚠ align-self 不能省：.kz-intro 是 flex column，flex item 預設 stretch，
   這顆按鈕會被拉成整欄寬（276px）的長方塊，跟站上其他「查看更多」完全不一樣。
   貼合文字寬度才與服務專區左欄那顆一致。 */
.kz-more {
    align-self: flex-start;
    margin-top: 1.75rem;
}

/* 編號清單：每列「編號 + 標題」，底下一條細線 —— 與服務專區同一套列語言。
   margin-top:auto 把整份清單推到左欄底部（見上方 .kz-intro 的說明）。 */
.kz-list {
    list-style: none;
    margin: auto 0 0;
    padding: 0;
}

/* 這一列是「切換圖文」的頁籤，不是連結 —— 用 button 才有正確的語意與鍵盤行為。
   按鈕預設會帶 UA 的背景／邊框／字型，全部歸零之後才會長得像設計稿那條清單。 */
.kz-list-btn {
    display: flex;
    align-items: baseline;
    gap: 1.25rem;
    width: 100%;
    padding: .875rem 0;
    border: 0;
    border-bottom: 1px solid var(--rule-on-light);
    background: transparent;
    font: inherit;
    text-align: left;
    cursor: pointer;
    transition: border-color var(--transition);
}

.kz-list-btn:hover {
    border-bottom-color: var(--rule-on-light-hover);
}

/* 選取中：整列加深，編號換成主色 —— 要讓使用者一眼看出「現在顯示的是這一篇」。
   ⚠ 三階必須拉開間距：靜置 .48 / hover .75 / 選取 100%。
     靜置值從 .16 提到 .48 之後，原本的 hover .5 幾乎與靜置同色，等於 hover 失效。 */
.kz-list-btn[aria-selected="true"] {
    border-bottom-color: var(--rule-on-light-active);
}

.kz-list-btn[aria-selected="true"] .kz-list-no {
    color: hsl(var(--heading));
}

.kz-list-btn[aria-selected="true"] .kz-list-title {
    color: hsl(var(--heading));
    font-weight: var(--fw-normal);
}

/* 編號的字體與字級對齊服務專區的 .svc-tile-no（同一個角色就該是同一套語言）：
     字體  Julius Sans One（--font-eyebrow）＝ 全站編號／英文小標的字體
     字級  13px（--text-meta，與日期／分類同一級）

   ── 清單的配色（2026-07-30 客戶指定）─────────────────────────────
   未選取：編號與標題**都是卡其**（--brand-accent）
   選取後：兩者都變純黑（--heading），見上方 [aria-selected="true"] 那組
   ⚠ 這一區刻意把未選取狀態壓到很低的對比（卡其壓米白 1.62:1），
     用意是讓「目前正在看的那一篇」用純黑單獨跳出來 —— 三列裡永遠只有一列是主角。
     這是視覺決定，不是漏改；要回到可讀性優先就把兩處的卡其換成
     --muted-foreground（7.85:1）與 --foreground/.7（5.0:1）。
   ⚠ hover 仍然轉黑（見下方），所以滑過去就讀得到，不是完全沒有回饋。 */
.kz-list-no {
    flex: 0 0 auto;
    font-family: var(--font-eyebrow);
    font-size: var(--text-meta);
    line-height: var(--lh-meta);
    letter-spacing: var(--tracking);
    color: hsl(var(--brand-accent));
    transition: color var(--transition);
}

.kz-list-title {
    font-size: var(--text-body);
    line-height: 1.6;
    font-weight: var(--fw-light);
    letter-spacing: var(--tracking);
    color: hsl(var(--brand-accent));
    transition: color var(--transition);
}

/* hover／鍵盤聚焦：整列（編號 + 標題）一起轉黑，與選取狀態同一個終點。
   ⚠ 編號也要加進來 —— 未選取的編號現在是卡其，只轉標題會變成半黑半卡其。 */
.kz-list-btn:hover .kz-list-no,
.kz-list-btn:focus-visible .kz-list-no,
.kz-list-btn:hover .kz-list-title,
.kz-list-btn:focus-visible .kz-list-title {
    color: hsl(var(--heading));
}

/* 中欄：橫式主視覺。比例取素材原始的 1760×1328（class1~3），
   所以 cover 填滿時完全不裁切。
   ⚠ 設計稿原本是 436×564 的直式版位，客戶後續指定改橫式，這裡以素材比例為準。
   ⚠ 2026-07-30 換素材（knowledge-carsouel-img1~3 → class1~3）時，
     比例從 620/460（1.348）改成 1760/1328（1.325）—— 差 1.7%，
     不改的話會裁掉一小條，改了就是完全零裁切。換圖時記得一起對。 */
.kz-media {
    position: relative;
    /* ⚠ 一定要 start，不能跟著 .kz-layout 的 align-items:stretch 走。
       stretch 會把高度變成「已確定」，aspect-ratio 於是改用高反推寬 ——
       實測 1100 寬時算出 824px，直接溢出到右欄疊在文字上。
       start 之後高度永遠由「欄寬 ÷ 比例」決定，圖片保證是橫的、也不會超出欄位。 */
    align-self: start;
    aspect-ratio: 1760 / 1328;
    overflow: hidden;
    border-radius: var(--radius-media);
    background: hsl(var(--muted));
}

/* 三張圖疊在同一個位置，只有 .is-active 看得到 —— 切換不會有 Layout Shift，
   也不必等圖片重新下載（三張在首次載入時就都在 DOM 裡）。 */
.kz-media-item {
    position: absolute;
    inset: 0;
    margin: 0;
    opacity: 0;
    visibility: hidden;
    transition: opacity var(--dur-reveal) var(--ease-out);
}

.kz-media-item.is-active {
    opacity: 1;
    visibility: visible;
}

.kz-media a {
    display: block;
    width: 100%;
    height: 100%;
}

.kz-media picture,
.kz-media img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    transition: transform var(--dur-media) var(--ease-out);
}

.kz-media-item.is-active a:hover img {
    transform: scale(1.04);
}

/* 右欄：三篇疊在同一個 grid 格子裡，欄高由最長的那一篇決定 ——
   切換時右欄不會忽高忽低，中欄的圖也不會跟著跳。
   ⚠ 不能用 display:none 切換：那樣沒有 before-change style，opacity 過場起不來。 */
.kz-panels {
    display: grid;
}

/* flex column + 底部的 CTA margin-top:auto ——
   欄高由圖片決定，文字內容不足時把空白留在 CTA 之前，而不是全部堆在欄位下緣。 */
.kz-detail {
    grid-area: 1 / 1;
    display: flex;
    flex-direction: column;
    opacity: 0;
    visibility: hidden;
    transition: opacity var(--dur-reveal) var(--ease-out);
}

.kz-detail.is-active {
    opacity: 1;
    visibility: visible;
}

/* hidden 屬性預設是 display:none，會讓上面的疊放與過場失效；
   可見性一律交給 .is-active 管，這裡把它蓋掉。 */
.kz-detail[hidden] {
    display: block;
}

.kz-tags {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
}

/* 與「近期課程」的標題同一級（.course-title）：24px / 行高 1.65 / regular，
   兩區的文章標題才是同一個層級的語言。差別只在顏色 ——
   那一區是深底米白字，這一區是米白底深色字。
   ⚠ 行高從 --lh-lead（50/24 ≈ 2.08）改成 1.65：--lh-lead 是**單行大標**的行高，
     用在會折兩行的文章標題上會鬆散到不像一句話，也與課程卡的 1.65 對不上。 */
.kz-title {
    margin: 1.75rem 0 0;
    font-size: var(--text-lead);       /* 24px */
    line-height: 1.65;                 /* 與 .course-title 同值 */
    font-weight: var(--fw-normal);
    letter-spacing: var(--tracking);
    color: hsl(var(--heading));
}

.kz-title a {
    color: inherit;
    text-decoration: none;
    transition: opacity var(--transition);
}

.kz-title a:hover,
.kz-title a:focus-visible {
    opacity: .7;
}

/* 標題下那條細線。⚠ 原本是米棕 100%（1.62:1）—— 在米白底上偏黃、又比同一區
   其他線更重，是三種線色混用裡最突兀的一條。改用統一的淺底線色。 */
.kz-rule {
    display: block;
    width: 100%;
    height: 1px;
    margin: 1.25rem 0 0;
    background: var(--rule-on-light);
}

.kz-desc {
    margin: 1.5rem 0 0;
    font-size: var(--text-body);
    line-height: var(--lh-body);
    font-weight: var(--fw-light);
    letter-spacing: var(--tracking);
    color: hsl(var(--muted-foreground));
}

/* YouTube｜Podcast：一個細框方塊，中間一條分隔線（設計稿是併在一起的一組） */
/* 課程／文章介紹詳述，承接 .kz-desc 那一句 */
.kz-body {
    margin: .75rem 0 0;
    font-size: var(--text-body);
    line-height: var(--lh-body);
    font-weight: var(--fw-light);
    letter-spacing: var(--tracking);
    color: hsl(var(--muted-foreground));
}

/* YouTube／Podcast：**兩顆各自獨立的膠囊按鈕**（2026-07-30 客戶指定）。
   ⚠ 改版前是「一個細框方塊、中間一條分隔線」的併排組合（設計稿的原始形式）。
     客戶要求拆成兩顆獨立按鈕，所以外層退回單純的排列容器 ——
     沒有底色、沒有外框、沒有 overflow:hidden，樣式全部下放到每一顆自己身上。
     中間那條 .kz-links-sep 的標記也一併移除（兩顆分開之後沒有東西要分隔）。 */
.kz-links {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .75rem;
    margin-top: 2rem;
}

/* 每一顆自己就是一顆實心卡其膠囊，與這一區的標籤／分類、兩顆「查看更多」
   以及 Footer 的訂閱按鈕同一套：卡其底 + 黑字 + 無外框 + 膠囊圓角。
   ⚠ flex:0 0 auto ＝ 貼合內容寬度。原本是 flex:1 1 0（各佔一半），
     那是「併在一起的一組」才需要的等分；獨立按鈕應該各自只有自己那麼寬。
   ⚠ 高度 52 → 48，與 .btn-pill 對齊；這一欄現在四顆膠囊（兩顆標籤、
     YouTube、Podcast、查看更多）高度一致才不會參差。 */
.kz-links a {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: .625rem;
    min-height: 48px;
    padding: 0 1.375rem;
    border-radius: var(--radius-pill);
    background: hsl(var(--brand-accent));
    font-size: var(--text-body);
    line-height: 1;
    font-weight: var(--fw-normal);   /* 卡其底上 350 DemiLight 太細，提到 400 */
    letter-spacing: var(--tracking);
    color: hsl(var(--heading));      /* 卡其底 + 黑字 ＝ 11:1 */
    text-decoration: none;
    transition: background var(--transition), color var(--transition);
}

/* 單獨一顆 hover 時填成深灰、字轉米白 —— 與這一區的按鈕同一個 hover 終點 */
.kz-links a:hover,
.kz-links a:focus-visible {
    background: hsl(var(--foreground));
    color: hsl(var(--background));
}

.kz-links svg {
    flex: 0 0 auto;
    width: 20px;
    height: 20px;
}

/* 註：.kz-links-sep（YouTube 與 Podcast 之間那條分隔線）已於 2026-07-30
   隨「拆成兩顆獨立按鈕」一併移除，標記與樣式都不留。 */

/* ⚠ 不要用 margin-top:auto 把它壓到欄底：欄高是由圖片決定的，
   文案長度一變，按鈕就會被推到離內文很遠、又貼死在下緣的位置。
   改成固定間距，讓它緊接在 YouTube／Podcast 之後；欄位多出來的高度
   留在按鈕下方當留白就好。 */
.kz-detail-more {
    align-self: flex-start;   /* flex column 下不加會被拉成整欄寬 */
    margin-top: 2rem;
}

/* 註：這兩顆「查看更多」一度做成實心卡其（與標籤／訂閱鈕同一套），
   2026-07-30 客戶決定改回原本的樣式 —— 也就是全站共用的 .btn-pill：
   細框膠囊 + 右側圓點，hover 時圓點長成箭頭圓、膠囊本身不填色。
   這一區的實心卡其只留給標籤／分類與 YouTube／Podcast。 */

/* ── 知識專區 RWD ───────────────────────────────────────────
   1200 以下三欄放不下：圖與詳細併成一列，標題欄退到上方整行。 */
@media (max-width: 1023.98px) {
    /* 五軌塞不下。⚠ 也不要收成「左圖右文」兩欄：欄寬一半之後圖只剩約 380 高，
       文字卻長到 600 以上，兩欄高度差一大截、圖下方空一塊。改成單欄由上而下。 */
    .kz-intro  { grid-column: 1; align-self: auto; }
    .kz-media  { grid-column: 1; }
    .kz-panels { grid-column: 1; }
    .kz-divider { display: none; }

    /* 標題欄不再與右側並排，清單不必沉底 */
    .kz-list { margin-top: 2rem; }
}

/* ═══════════════════════════════════════════════════════════
   色帶轉場（.band-shift）— 米白 ↔ 深灰的交界
   ═══════════════════════════════════════════════════════════

   客戶反饋：兩段直接切換底色太生硬。
   這一帶把「切」變成「長出來」，三段：

     ① 靜置    整條是**來源色**，與上一段同色，看不出這裡有東西
     ② 分割    左半**目標色**、右半卡其**同時**從外側往中間展開
     ③ 收攏    目標色再從中線往右把卡其吃掉 → 整條目標色，接上下一段

   【雙向】方向由兩個變數決定，兩處交界共用同一組規則：
     .band-shift          米白 → 深灰（Hero → 服務專區）
     .band-shift--light   深灰 → 米白（實體課程 → 知識專區）
   ⚠ 子元素的 class 刻意取名 fill／accent 而不是 dark／khaki ——
     方向會反過來，叫「dark」的那一塊在第二處其實是米白，名字會騙人。

   ⚠ 只用既有三個色票（--background／--surface-dark／--brand-accent），
     沒有新增裝飾圖案。
   ⚠ 兩塊用 transform: scaleX 展開，不是改 width ——
     transform 不觸發版面重算，60fps 才穩；改寬度會讓整頁跟著 reflow。
   ⚠ transform-origin 左右相反：fill 從左緣長、accent 從右緣長，
     兩塊在中線會合，才是「分割」而不是「一起往右推」。
   ⚠ 第三段做在 accent 的 ::after（目標色，從左緣長），
     不是把 accent 的背景色淡成目標色 ——
     顏色淡變看起來是「褪色」，往右掃過去才是「被蓋掉」。
   ⚠ 動畫只播一次（JS 觸發後就 unobserve）。往回捲時維持目標色，
     那正好等於原本的硬切外觀，不會出現「顏色又縮回去」的怪動作。 */
.band-shift {
    --band-from: hsl(var(--background));
    --band-to:   hsl(var(--surface-dark));

    position: relative;
    height: clamp(96px, 12vh, 168px);
    overflow: hidden;
    background: var(--band-from);
}

/* 反向：深灰 → 米白（實體課程 → 知識專區） */
.band-shift--light {
    --band-from: hsl(var(--surface-dark));
    --band-to:   hsl(var(--background));
}

.band-shift-fill,
.band-shift-accent {
    position: absolute;
    top: 0;
    bottom: 0;
    display: block;
    transform: scaleX(0);
    /* 慢進慢收，與桌曆翻頁同一套呼吸（--cal-ease），不用彈跳曲線 */
    transition: transform 720ms var(--cal-ease);
}

.band-shift-fill {
    left: 0;
    right: 50%;
    transform-origin: left center;
    background: var(--band-to);
}

.band-shift-accent {
    left: 50%;
    right: 0;
    transform-origin: right center;
    background: hsl(var(--brand-accent));
}

/* 第三段：目標色從中線往右掃過卡其。延遲 560ms ＝ 等分割動作走到約八成，
   兩段有一點交疊才不會像分鏡，與 Hero 開場「翻到七成才浮字」同一個原則。 */
.band-shift-accent::after {
    content: "";
    position: absolute;
    inset: 0;
    background: var(--band-to);
    transform: scaleX(0);
    transform-origin: left center;
    transition: transform 680ms var(--cal-ease) 560ms;
}

.band-shift.is-in .band-shift-fill,
.band-shift.is-in .band-shift-accent,
.band-shift.is-in .band-shift-accent::after {
    transform: scaleX(1);
}

/* JS 沒跑（或不支援 IntersectionObserver）時，整條直接是目標色 ——
   動畫是裝飾，兩段之間出現一條來源色的空帶才是事故。 */
.no-js .band-shift {
    background: var(--band-to);
}

/* 使用者要求減少動態：不做分割，直接就是目標色的一條帶 */
@media (prefers-reduced-motion: reduce) {
    .band-shift {
        background: var(--band-to);
    }

    .band-shift-fill,
    .band-shift-accent,
    .band-shift-accent::after {
        transition: none;
        transform: scaleX(1);
    }
}

/* ═══════════════════════════════════════════════════════════
   服務專區 — Featured Service 版型（2026-07-30 依客戶提供的 Featured Work 參考稿重排）
   ═══════════════════════════════════════════════════════════

   版型：左欄（26%）標題／介紹／查看全部 ＋ 一條垂直細線 ＋ 右欄（70%）服務內容。
        右欄最上方是橫跨全寬的主打服務（16:8 大圖），其餘以規則兩欄網格往下排。
        參考稿的節奏是「大量留白 + 細線分隔 + 沒有卡框」，所以這一段完全沒有
        背景色塊與陰影 —— 分組全靠 1px 細線與間距。

   【為什麼不是原本的 hover 清單】
        改版前是清單列，圖藏在 hover 才捲開的小卡裡；新稿要求圖片一進畫面就看得到，
        而且要用尺寸分出主力商品。舊的 .svc-row／.svc-card 整組移除（見 git 歷史）。

   【擴充】筆數由 Wireframe::services() 決定，6～8 筆都不必改 CSS ——
        第一筆固定是大圖，第 2 筆之後自動往兩欄網格續排；
        奇數筆時最後一列只有左欄一格（不補空白卡）。

   【圓角】全站 token --radius 是 0（本稿卡片一律直角），但這一段依客戶指定
        改為克制的 8px，只作用在圖片版位上，不動 token、不影響其他區塊。

   底色沿用原本的深色帶（.on-dark），與近期課程是同一條 #2F2F2F。 */
/* ⚠ 下方留白改用 --section-seam：服務專區與實體課程是**同一條深色帶**，
   兩段各出一份 --section-y 會讓中間空出 288px（1920 實測）。
   上方留白維持 --section-y —— 那是與 Hero 的交界，需要完整的呼吸。 */
.service-section {
    padding-bottom: var(--section-seam);
    background: hsl(var(--surface-dark));
    color: hsl(var(--surface-dark-foreground));
}

/* 左右分欄：左 26% + 留白 4% + 右 70%（規格 26–30 / 70–74）。
   align-items:start —— 左欄要停在自己的高度，不隨右欄長度置中。 */
.svc-layout {
    --svc-intro-w: 26%;
    --svc-gutter: 4%;

    position: relative;
    display: grid;
    grid-template-columns: var(--svc-intro-w) minmax(0, 1fr);
    column-gap: var(--svc-gutter);
    align-items: start;
}

/* 分隔細線落在 column-gap 的正中央（不貼著任何一欄的邊），
   與知識專區 .kz-divider 同一個做法。 */
.svc-layout::before {
    content: "";
    position: absolute;
    left: calc(var(--svc-intro-w) + var(--svc-gutter) / 2);
    top: 0;
    bottom: 0;
    width: 1px;
    background: var(--rule-on-dark);
}

/* 左欄在瀏覽右側服務時保持相對穩定（規格）：黏在視窗上緣下方一段距離。
   top 要讓開 fixed header（82px）再留一點呼吸。
   sticky 的活動範圍就是它自己的 grid area，所以右欄捲完就跟著離開，不會壓到下一段。 */
.svc-intro {
    position: sticky;
    top: 7rem;
}

.svc-intro .section-title {
    margin-bottom: 1.5rem;
}

/* 介紹文：14/30，深色底上用米棕輔色（與其他 on-dark 的次要文字同一套） */
.svc-intro-desc {
    margin: 0 0 2rem;
    max-width: 24em;
    font-size: var(--text-body);
    line-height: var(--lh-body);
    font-weight: var(--fw-light);
    letter-spacing: var(--tracking);
    color: hsl(var(--brand-accent));
}

/* ── 右欄 ─────────────────────────────────────────────────── */
.svc-main {
    display: flex;
    flex-direction: column;
    gap: 3.5rem;
}

/* ── 圖片版位（主打與網格項共用） ──────────────────────────────
   比例各自在 .svc-feature-media／.svc-tile-media 指定，這裡只管裁切與 hover。 */
.svc-media {
    position: relative;
    overflow: hidden;
    border-radius: var(--radius-media);
    background: hsl(var(--brand-accent) / .12);
}

.svc-media picture,
.svc-media img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    /* 影像走最慢的一層節奏（1.2s），與課程圖、知識卡同一套 hover 語言 */
    transition: transform var(--dur-media) var(--ease-out);
}

/* hover 時略微變化的遮罩。平常完全透明 —— 圖片本身就是主要內容，
   遮罩只是指到時的一點回饋，不是用來藏東西的。 */
.svc-media::after {
    content: "";
    position: absolute;
    inset: 0;
    background: hsl(var(--brand) / .16);
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--dur-hover) var(--ease-out);
}

/* 箭頭沿用 .btn-pill 徽章的「→」語言（不拉 icon font，CSP 友善）。
   hover 時右移 6px（規格 4–8px）。 */
.svc-arrow {
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.5rem;
    font-family: var(--font-en);
    font-size: 1.125rem;
    line-height: 1;
    letter-spacing: 0;
    color: hsl(var(--brand-accent));
    transition: transform var(--dur-hover) var(--ease-out);
}

/* ⚠ 2026-07-31 起箭頭改成 SVG 圖示（wf_icon('arrow-right')，客戶指定的那一套），
   原本的文字箭頭因此收起來。留著規則不刪：圖示若因故沒輸出，這裡就是退路。 */
.svc-arrow:not(:has(.wf-icon))::before { content: "→"; }

.svc-arrow .wf-icon {
    width: 20px;
    height: 20px;
}

/* ── 主打服務（第一筆）───────────────────────────────────────
   16:8 大橫式，整張可點。圖片下方一條細線接名稱／分類／箭頭，
   區塊下緣再一條細線，把主打與下方的兩欄網格分開。 */
.svc-feature {
    display: block;
    padding-bottom: 2.25rem;
    border-bottom: 1px solid var(--rule-on-dark);
    color: inherit;
    text-decoration: none;
}

.svc-feature:focus-visible,
.svc-tile:focus-visible {
    outline: 2px solid hsl(var(--brand-accent));
    outline-offset: 6px;
}

.svc-feature-media {
    aspect-ratio: 16 / 8;
}

/* 圖片 → 細線 → 文字，上下各 1.5rem 的呼吸 */
.svc-feature-info {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 2.5rem;
    margin-top: 1.5rem;
    padding-top: 1.5rem;
    border-top: 1px solid var(--rule-on-dark);
}

.svc-feature-text {
    min-width: 0;
}

/* 主打的名稱：用級距表上的「標題級」--text-lead（24px，窄幅自動降到 20px），
   與課程卡標題、知識文章標題同一級。
   ⚠ 2026-07-30 從 clamp(24–30px) 收下來：原本 30px 與區塊主標「服務專區」的 36px
     只差 17%，兩者看起來像同一級（客戶反饋）。改成 24px 之後三層各差約 1.4 倍：
       36  區塊主標（服務專區）
       24  主打服務名（顧問案）
       18  網格服務名
     ⚠ 不要再挑一個表外的數字（26／28…）—— 級距表是 SSOT，
       主打的「大」已經由大圖與全寬承擔，字級只需要在表上比網格高一階。 */
.svc-feature-name {
    margin: 0;
    font-size: var(--text-lead);
    line-height: 1.45;
    font-weight: var(--fw-normal);
    letter-spacing: var(--tracking);
    color: hsl(var(--surface-dark-foreground));
    transition: color var(--dur-hover) var(--ease-out);
}

.svc-feature-desc {
    margin: .625rem 0 0;
    max-width: 46em;
    font-size: var(--text-body);
    line-height: var(--lh-body);
    font-weight: var(--fw-light);
    letter-spacing: var(--tracking);
    color: hsl(var(--brand-accent));
}

.svc-feature-tail {
    flex: none;
    display: flex;
    align-items: center;
    gap: 1.25rem;
    padding-top: .5rem;
}

/* 分類文字 13px（與課程日期同一級的小標） */
.svc-feature-cat {
    font-size: var(--text-meta);
    line-height: var(--lh-meta);
    letter-spacing: var(--tracking);
    color: hsl(var(--brand-accent));
}

/* ── 其餘服務：規則兩欄網格 ─────────────────────────────────── */
.svc-grid {
    --svc-tile-gap: 3rem;

    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: var(--svc-tile-gap);
    row-gap: 3.25rem;
}

.svc-tile {
    position: relative;
    display: block;
    color: inherit;
    text-decoration: none;
}

/* 欄間垂直細線，落在欄距正中央。
   網格固定兩欄，所以偶數格永遠是右欄；筆數為奇數時最後一格落在左欄，不畫線。 */
.svc-tile:nth-child(even)::before {
    content: "";
    position: absolute;
    top: 0;
    bottom: 0;
    left: calc(var(--svc-tile-gap) / -2);
    width: 1px;
    background: var(--rule-on-dark);
}

/* 所有網格項同一個比例、同一個尺寸（規格），與主打的 16:8 明顯不同階 */
.svc-tile-media {
    aspect-ratio: 16 / 10;
}

/* 網格項圖下不再畫線（參考稿只有主打下方有線），只留間距 */
.svc-tile-info {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 1.25rem;
    margin-top: 1.125rem;
}

.svc-tile-text {
    min-width: 0;
}

.svc-tile-name {
    margin: 0;
    font-size: var(--text-sub);        /* 18px */
    line-height: var(--lh-sub);
    font-weight: var(--fw-normal);
    letter-spacing: var(--tracking);
    color: hsl(var(--surface-dark-foreground));
    transition: color var(--dur-hover) var(--ease-out);
}

.svc-tile-sub {
    display: block;
    margin-top: .25rem;
    font-size: var(--text-meta);
    line-height: 1.85;
    font-weight: var(--fw-light);
    letter-spacing: var(--tracking);
    color: hsl(var(--brand-accent));
}

.svc-tile-tail {
    flex: none;
    display: flex;
    align-items: center;
    gap: .75rem;
    padding-top: .35rem;
}

.svc-tile-no {
    font-family: var(--font-eyebrow);
    font-size: var(--text-meta);
    line-height: var(--lh-meta);
    letter-spacing: var(--tracking);
    color: hsl(var(--brand-accent) / .75);
}

/* ── hover ─────────────────────────────────────────────────
   圖片微放大 1.03、遮罩淡入、箭頭右移 6px。整張都是連結，所以判定掛在 <a> 上。
   (hover: hover) 排除觸控裝置的黏著 hover；鍵盤 focus 另外一組（不能被擋掉）。 */
@media (hover: hover) {
    .svc-feature:hover .svc-media img,
    .svc-tile:hover .svc-media img { transform: scale(1.03); }

    .svc-feature:hover .svc-media::after,
    .svc-tile:hover .svc-media::after { opacity: 1; }

    .svc-feature:hover .svc-arrow,
    .svc-tile:hover .svc-arrow { transform: translateX(6px); }

    .svc-feature:hover .svc-feature-name,
    .svc-tile:hover .svc-tile-name { color: hsl(var(--brand-accent)); }
}

.svc-feature:focus-visible .svc-media img,
.svc-tile:focus-visible .svc-media img { transform: scale(1.03); }

.svc-feature:focus-visible .svc-arrow,
.svc-tile:focus-visible .svc-arrow { transform: translateX(6px); }

/* 減少動態時直接呈現最終狀態，不做放大與滑移 */
@media (prefers-reduced-motion: reduce) {
    .svc-media img,
    .svc-media::after,
    .svc-arrow,
    .svc-feature-name,
    .svc-tile-name {
        transition: none;
    }

    .svc-feature:hover .svc-media img,
    .svc-tile:hover .svc-media img,
    .svc-feature:focus-visible .svc-media img,
    .svc-tile:focus-visible .svc-media img {
        transform: none;
    }
}

/* ═══════════════════════════════════════════════════════════
   近期課程 — Latest News 版型（2026-07-30 依客戶提供的參考稿重排）
   ═══════════════════════════════════════════════════════════

   上方：左側主標 + 右上「查看全部」，下方一條貫穿全寬的細線。
   內容：桌機三等寬欄，欄與欄之間一條垂直細線。
        沒有卡框、沒有背景色塊、沒有陰影，hover 也不浮起 ——
        整段是編輯式的內容列，不是電商商品卡。

   註：改版前是「左圖右文的橫列 + 每列一顆按鈕」（Figma 618:242），
       再往前是 sticky 捲動堆疊卡片。兩版樣式都已移除，見 git 歷史。 */
/* 上方＝深色帶內部接縫（與服務專區共同構成 128px）；
   下方＝深色帶與米白帶的交界，用比較寬的那一級。
   ⚠ 下方額外再加 2rem：這一段的結尾是置中的「查看全部」按鈕，
     按鈕之後若只留交界級的留白，整段會像是在按鈕上被切斷。
     客戶反饋「底部空間可以多一點」，加的是這一段自己的收尾空間，
     不是把交界級 token 調大（那會連帶影響其他區塊的接縫）。 */
.course-section {
    padding-top: var(--section-seam);
    padding-bottom: calc(var(--section-seam-band) + 2rem);
    background: hsl(var(--surface-dark));
    color: hsl(var(--surface-dark-foreground));
}

/* 標題置中，底線貫穿全寬。
   ⚠ 2026-07-30 依客戶指定：標題由靠左改為置中，「查看全部」從這一列移到整段
     最下方並置中（見 .section-more）。這一列因此只剩標題，不再需要 flex 分配。 */
.course-head {
    padding-bottom: 1.75rem;
    border-bottom: 1px solid var(--rule-on-dark);
    text-align: center;
}

/* .section-title 預設帶 --space-title-gap 的下留白（標題單獨讀完再進內容）。
   這裡標題下方緊接細線，那段留白改由細線之後的 margin 承擔。 */
.course-head .section-title {
    margin-bottom: 0;
}

.course-grid {
    --course-gap: 3rem;

    list-style: none;
    margin: 3rem 0 0;
    padding: 0;
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    column-gap: var(--course-gap);
    row-gap: 3.5rem;
}

.course-cell {
    position: relative;
}

/* 欄間垂直細線，落在欄距正中央。
   每一列的第一格（3n+1）不畫，線才不會跑到整段最左邊。 */
.course-cell::before {
    content: "";
    position: absolute;
    top: 0;
    bottom: 0;
    left: calc(var(--course-gap) / -2);
    width: 1px;
    background: var(--rule-on-dark);
}

.course-cell:nth-child(3n + 1)::before {
    display: none;
}

/* 整格一條連結（規格：卡片本身即可點擊，不再放各自的按鈕） */
.course-link {
    display: block;
    color: inherit;
    text-decoration: none;
}

.course-link:focus-visible {
    outline: 2px solid hsl(var(--brand-accent));
    outline-offset: 6px;
}

.course-thumb {
    position: relative;
    aspect-ratio: 16 / 9;
    overflow: hidden;
    border-radius: var(--radius-media);
    background: hsl(var(--brand-accent) / .12);
}

.course-thumb picture,
.course-thumb img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    transition: transform var(--dur-media) var(--ease-out);
}

.course-thumb::after {
    content: "";
    position: absolute;
    inset: 0;
    background: hsl(var(--brand) / .16);
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--dur-hover) var(--ease-out);
}

/* 日期／分類 13px（規格 12–13px） */
.course-date {
    margin: 1.125rem 0 0;
    font-size: var(--text-meta);
    line-height: var(--lh-meta);
    font-weight: var(--fw-normal);
    letter-spacing: var(--tracking);
    color: hsl(var(--brand-accent));
}

/* 課程標題 24px（規格 20–24px；窄幅由 --text-lead 自動降到 20px）。
   行高刻意比 --lh-lead（50/24 ≈ 2.08）緊 —— 那是單行大標的行高，
   兩行的卡片標題用它會鬆散到不像一句話。最多兩行，超出以 … 收尾。 */
.course-title {
    margin: .5rem 0 0;
    font-size: var(--text-lead);
    line-height: 1.65;
    font-weight: var(--fw-normal);
    letter-spacing: var(--tracking);
    color: hsl(var(--surface-dark-foreground));
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    overflow: hidden;
    transition: color var(--dur-hover) var(--ease-out);
}

.course-place {
    margin: .625rem 0 0;
    font-size: var(--text-meta);
    line-height: 1.85;
    font-weight: var(--fw-light);
    letter-spacing: var(--tracking);
    color: hsl(var(--brand-accent) / .85);
}

/* hover：圖片微放大 + 標題轉輔色。不浮起、不加陰影（規格） */
@media (hover: hover) {
    .course-link:hover .course-thumb img { transform: scale(1.03); }
    .course-link:hover .course-thumb::after { opacity: 1; }
    .course-link:hover .course-title { color: hsl(var(--brand-accent)); }
}

.course-link:focus-visible .course-thumb img { transform: scale(1.03); }

@media (prefers-reduced-motion: reduce) {
    .course-thumb img,
    .course-thumb::after,
    .course-title {
        transition: none;
    }

    .course-link:hover .course-thumb img,
    .course-link:focus-visible .course-thumb img {
        transform: none;
    }
}

/* ─── 區塊底部置中按鈕（更多知識 / 看更多課程） ────────────────── */
.section-more {
    display: flex;
    justify-content: center;
    margin-top: 3rem;
}

/* 近期課程的這一顆與課程卡再拉開一點（客戶反饋「按鈕再下來一點」）。
   ⚠ 只覆寫這一段，不動 .section-more 本身 —— 它是共用元件，
     其他區塊日後用到時不該被這裡的取捨綁住。
   ⚠ 用 clamp 而不是固定值：窄幅沿用 72px 會讓按鈕與卡片之間空一大塊。 */
.course-section .section-more {
    margin-top: clamp(2.25rem, 4vw, 4.5rem);
}

/* ═══════════════════════════════════════════════════════════
   右下角浮動社群鈕（YouTube / Podcast）
   ═══════════════════════════════════════════════════════════

   2026-07-30 依現行設計語言重做。原型樣式（wireframe.css 的 .wf-float）是
   白底 + 灰框 + 投影 + emoji 圖示，與本站的調性不合：
     · emoji 由作業系統決定長相（macOS 會強制彩色），與單色描邊圖示衝突
     · 投影：全站規格明確要求「不要使用明顯陰影」
     · 白底灰框：不在米白／暖灰／卡其／深灰這組色票裡

   形狀沿用站上既有的圓鈕尺寸（與 Hero 的翻頁鈕 46px 同一級）。
   ⚠ 2026-07-30 客戶指定改用藍色 #B1C7DE（--accent-blue，色票的例外，見 tokens.css）。
     淡藍是淺色，所以圖示用深灰（7.71:1）而不是米白（1.61:1，看不見）。

   ⚠ 只覆寫 .is-designed（目前＝首頁）。尚未切版的 18 頁仍吃 wireframe.css
     的原型樣式 —— 那些頁面的整體調性還是灰階原型，單獨換這兩顆會更突兀。
   ⚠ 保留一道極淡的投影：這兩顆是**浮在內容之上**的，捲到深色帶或滿版圖片上時
     若完全沒有邊界會糊在一起。濃度壓到 .12，遠低於原型的 .16，
     而且只有 y 位移沒有擴散，不會讀成「卡片浮起來」。 */
.is-designed .wf-float {
    right: clamp(1rem, 2vw, 1.75rem);
    bottom: clamp(1rem, 2vw, 1.75rem);
    gap: .625rem;
}

.is-designed .wf-float a {
    width: 46px;
    height: 46px;
    border: 0;
    background: hsl(var(--accent-blue));
    color: hsl(var(--surface-dark));              /* 深灰圖示，對比 7.71:1 */
    box-shadow: 0 2px 10px hsl(var(--foreground) / .12);
    transition: background var(--transition), color var(--transition);
}

.is-designed .wf-float a svg {
    width: 22px;
    height: 22px;
    display: block;
}

/* hover：藍色壓深一階（同色相、降明度），圖示維持深灰 ——
   換色相會讓這兩顆在 hover 時脫離自己的色系，看起來像變成另一種按鈕。
   ⚠ 只降到 66%：再深下去深灰圖示就開始糊掉（68% 時是 5.4:1，已接近下限）。 */
.is-designed .wf-float a:hover,
.is-designed .wf-float a:focus-visible {
    background: hsl(211 41% 66%);
    color: hsl(var(--surface-dark));
}

/* ═══════════════════════════════════════════════════════════
   Footer — 全站共用（依 Figma proposal1 的 footer frame 1440×739 重建）

   米白底單一區塊：上半五欄（品牌 / 服務專區 / 知識專區 / 關於我們 / 訂閱），
   欄與欄之間一條淡色垂直細線；下半一條水平線 + 版權列。

   ⚠ 改版前是「深色影像 CTA 帶 + 深灰導覽帶」，那一套（.footer-cta /
     .footer-base / .footer-bg-title / .cta-*）已隨新稿移除，見 git 歷史。

   欄寬比例是照設計稿量的：內容區 ≈1178/1440，品牌欄約佔 28%、
   三個連結欄各約 17%、訂閱欄約 20%，所以 grid 取 1.55 : 1 : 1 : 1 : 1.2。
   ═══════════════════════════════════════════════════════════ */

/* footer 已切版，wireframe 階段的標註不再顯示（文字保留於 DOM 未刪除） */
.site-footer .wf-label,
.site-footer .wf-note,
.site-footer .wf-tline {
    display: none;
}

/* ⚠ 2026-07-30：Footer 底色一度改成卡其，客戶最後決定改回米白（--background）。
   最下方的版權列**維持深灰**（.footer-bottom）—— 那是客戶指定保留的雙層收尾。
   ⚠ 當初為了配合卡其底而調的三處已一併還原：
     欄間分隔線 .28 → .14｜訂閱按鈕 深灰 → 卡其｜輸入框白 80% → 50%
     （留著會變成「為了一個已經不存在的底色而調的值」）。 */
.site-footer {
    /* ── Footer 專用字重（2026-07-30 客戶指定「footer 的文字弄粗一點」）──────
       ⚠ 做法是**在 .site-footer 這一層重新定義 token**，不是逐條改 font-weight。
         好處：Footer 內所有引用 --fw-light／--fw-normal 的元件一次到位，
         日後新增欄位也自動跟上，不會有人漏改一條就參差。
       ⚠ 作用域嚴格限制在 Footer 內：CSS 變數只往下繼承，
         其他區塊讀到的仍然是 :root 的原值（100／200），一個字都不會被動到。
       ⚠ 為什麼是 350／450 而不是「往上一階」：全站字型是可變字重（100–900），
         :root 的 light=100／normal=200 在這個字型上都非常細，
         只跳一階（100→200）在卡其底上幾乎看不出差別。
         350 已經是一般 Regular 的視覺重量，450 略粗於它，兩級仍分得開。 */
    --fw-light:  350;
    --fw-normal: 450;

    background: hsl(var(--background));
    color: hsl(var(--foreground));

    /* footer 在 .wf-inner 之外，寬度是整個視窗；背景要滿版，但文字必須對齊
       頁面內容的格線，否則寬螢幕下 footer 文字會貼到視窗邊緣、與上方內容錯開。
       用 max() 讓內距在窄幅時退回 --section-x，寬幅時自動撐成置中留白。 */
    --footer-pad-x: max(var(--section-x), calc((100% - var(--container-max)) / 2));
    --footer-line: hsl(var(--foreground) / .14);
}

/* 尚未切版的 18 頁：內容仍由 wireframe.css 鎖在 .wf-inner（1440）＋ .wf-section
   的 3.5rem 內距。footer 要跟著那組數字對齊，否則兩者會差 16px。
   各頁切版掛上 .is-designed 後，這條自動失效、改吃上面的流體容器。 */
body:not(.is-designed) .site-footer {
    --footer-pad-x: max(3.5rem, calc((100% - 1440px) / 2 + 3.5rem));
}

/* ─── 上半：五欄 ─────────────────────────────────────────── */
/* 上緣一條橫線，把 Footer 與上一段內容分開。
   ⚠ 這條線的來回值得記一下，免得又被拿掉一次：
     原本有 → Footer 改卡其底時拿掉（底色本身就是分界，再畫線是多餘的邊）
            → Footer 改回米白後又加回來（2026-07-30 客戶指定）。
     理由很直接：Footer 與上方的知識專區**現在是同一個米白**，
     沒有色彩邊界，只靠留白撐不出「這裡換了一個區塊」。
   ⚠ 用 --rule-on-light（深灰 48%）而不是 Footer 自己的 --footer-line（.14）——
     這是**區塊邊界**，要與知識專區、服務專區的分隔線同一套語言；
     .14 是 Footer 內部欄與欄之間的細分隔，拿來當區塊邊界幾乎看不見。
   ⚠ 用 margin-inline 而不是 padding-inline 來做左右內距 —— border 畫在 padding 之外，
     若用 padding，這條線會拉到整個視窗寬，與內容格線對不齊。 */
/* ⚠ 2026-07-31：欄數改成**跟著資料長**，不再寫死五軌。
   客戶把導覽收斂成一組（原本三組），欄數因此從 5 變 3；
   寫死的話後面兩軌會變成空的，右邊留一大片空白。
   做法：只明寫第一軌（品牌欄，比較寬），其餘交給 grid-auto-columns ——
   $footerCols 之後增減幾組，版型都自己對得上（見 _footer.php）。 */
.footer-main {
    display: grid;
    grid-template-columns: minmax(0, 1.55fr);
    grid-auto-flow: column;
    grid-auto-columns: minmax(0, 1fr);
    margin-inline: var(--footer-pad-x);
    padding: 5.5rem 0 3.5rem;
    border-top: 1px solid var(--rule-on-light);
}

/* 分隔線畫在第 2 欄之後的每一欄左側 —— 用 border 而不是獨立元素，
   欄位增減時線會自動跟著走，不必另外維護一組 ::before。 */
.footer-col {
    padding-left: clamp(1.25rem, 2.6vw, 2.75rem);
    border-left: 1px solid var(--footer-line);
}

/* ─── 欄 1：品牌 ─────────────────────────────────────────── */
.footer-brand {
    padding-right: clamp(1.5rem, 3vw, 3rem);
}

.footer-logo {
    display: inline-flex;
    align-items: center;
    gap: .75rem;
    margin: 0;
    font-size: var(--text-lead);       /* 24px */
    line-height: 1.4;
    font-weight: var(--fw-normal);
    letter-spacing: var(--tracking);
    color: hsl(var(--heading));
}

/* LOGO 圖示＋文字（2026-08-13 客戶指定，取代原本的手寫字標圖片，做法比照 header）。
   ⚠ 圖示只鎖高度、寬度 auto：來源 SVG viewBox 比例約 1.25:1（近方形），鎖寬會壓扁圖形。
     34px 沿用原本手寫字標圖片的高度值，數值不變 —— 選擇器從 .footer-logo img
     改成 .footer-logo-icon（新標記是內嵌 SVG，不是 <img>）。
   ⚠ gap 與品牌字字距比照 header 那組（.75rem／--tracking-title）：footer 與 header
     是同一顆 logo 的兩個出場位置，圖文比例要一致，不必各自另訂一套數字。 */
.footer-logo-icon {
    display: block;
    width: auto;
    height: 34px;
    flex-shrink: 0;
}

/* ── logo 圖示改用白色（2026-08-14 客戶指定：「線跟logo還有導覽都改成
   白色，logo改不白再跟我說我額外給你白色的logo」）─────────────────
   圖示的 fill 是寫死在 SVG <path> 屬性上的固定色 #4a4636（客戶原始檔案
   給的顏色，不是 currentColor），CSS 選擇器只要直接命中 <path> 就能蓋過
   屬性值（attribute 層級最低，一般 CSS 規則不必加 !important 就贏），
   不必改 PHP 標記。
   ⚠ 刻意寫在無媒體查詢的地方（不是 @media(min-width:992px) 裡面）：
     .site-logo-icon（桌機 header 自己的 logo）在 ≤991.98px 手機寬度也
     會出現（本檔另有一條 @media(max-width:1467px) 只調它的尺寸，不是
     只在桌機才顯示），而手機 header 這個 session 稍早也已經改成透明疊在
     Hero 照片上——白色在這個寬度一樣適用，第一版誤把這條寫在 992px
     區塊裡，手機寬度會漏掉，這裡改成無條件套用。
   ⚠ 涵蓋 .site-logo-icon（桌機／手機共用的同一顆 header logo）與
     .footer-logo-icon（footer 的 logo，底是深灰 #2f2f2f，白圖示看得清楚，
     全站每一頁的 footer 背景都一樣深，不用跟著頁面切換，維持無條件白）
     ——**刻意不含** .site-nav-logo-icon（手機／992–1467 抽屜、以及
     .is-compact 桌機捲動收合抽屜共用的同一顆 logo）：抽屜背景是白色
     （hsl(var(--brand-foreground))），圖示若也改白色會直接消失在白底上，
     這裡先不動，維持原本的 #4a4636。
   ⚠ header 自己的 logo 圖示（.site-logo-icon）2026-08-14 再指定「在其他
     頁面時...logo轉黑色70%」——header 背景是否透明疊圖只有首頁成立
     （見本檔「Header 文字/線條顏色」那段的 --header-ink），跟固定深底
     的 footer 不是同一種情境，這裡拆成兩條規則，各自讀對的顏色來源，
     不能再合併成同一條選擇器。
   ⚠ 如果轉成白色後視覺效果不理想（例如線稿在照片亮部糊掉），這是 CSS
     濾鏡式的顏色覆寫，不是真的重新出圖——客戶已經預告會另外提供白色
     版本的 logo 檔案，屆時把這裡換成新的 SVG／改回 currentColor 即可，
     不必先猜。 */
.site-logo-icon path {
    fill: var(--header-ink, hsl(var(--brand-foreground)));
}

/* 2026-08-14 客戶「logo改白色」——「導覽文字改黑色70%」那輪把
   --header-ink 兩個分支（首頁／非首頁）都改成黑70%（見「Header 文字/
   線條顏色」那段），logo 因為共用同一顆變數被一起帶成黑70%，客戶這輪
   把 logo 單獨挑出來：logo 在首頁要回白色，不要跟著導覽一起變黑，
   導覽本身維持黑70%不動。
   ⚠ 不是拆掉 --header-ink 這個機制、改回每個元素各自判斷——只有 logo
     這一項跟導覽不同調，用比 `.site-logo-icon path` 多一個元素選擇器的
     `html:has(.hero) .site-logo-icon path` 疊在後面覆寫，特異度更高、
     一定贏，不影響 --header-ink 本身或其他讀這顆變數的元素（導覽文字／
     底線／搜尋／AAA）。非首頁頁面沒有 .hero，這裡不生效，logo 繼續讀
     --header-ink（黑70%），跟客戶原本要的「其他頁面黑70%」一致。 */
html:has(.hero) .site-logo-icon path {
    fill: hsl(var(--brand-foreground));
}

/* 2026-08-17 客戶「logo旁邊放品牌名」把 .site-logo-text 重新顯示出來
   （見本檔「Logo 旁邊放回品牌名」新區塊）——文字緊鄰著上面這顆已經被
   強制改白色的圖示，如果文字自己還是讀 --header-ink（首頁黑70%），
   會變成「白色圖示＋黑色文字」擠在一起，圖文顏色對不上。這裡補上同一
   顆選擇器結構的文字版本，跟圖示同步變白，不是各自獨立判斷。 */
html:has(.hero) .site-logo-text {
    color: hsl(var(--brand-foreground));
}

.footer-logo-icon path {
    fill: hsl(var(--brand-foreground));
}

/* ⚠ 2026-08-13 客戶「logo 品牌字請用 16，並且顏色請用跟 logo 一樣的顏色」——
   跟 header 那份（.site-header .site-logo-text）同一個指定，footer 是同一顆
   logo 的第二個出場位置，字級與顏色比照辦理：16px 固定值（不跟 .footer-logo
   本來讀的 --text-lead 24px）。footer 背景固定深灰，不隨頁面切換，維持白色。 */
.footer-logo-text {
    white-space: nowrap;
    font-size: 1rem;
    letter-spacing: var(--tracking-title);
    color: hsl(var(--brand-foreground));
}

.footer-logo-en {
    margin: .375rem 0 0;
    font-family: var(--font-en);
    font-size: .75rem;                 /* 12px */
    line-height: 1.6;
    font-weight: var(--fw-normal);
    letter-spacing: var(--tracking);
    color: hsl(var(--muted-foreground));
}

.footer-slogan {
    margin: 2.25rem 0 0;
    font-size: var(--text-body);
    line-height: calc(28 / 14);
    font-weight: var(--fw-light);
    letter-spacing: var(--tracking);
    color: hsl(var(--muted-foreground));
}

/* 社群 icon：設計稿是三個無外框的小圖示，不是改版前那組圓框字母鈕 */
.footer-social {
    display: flex;
    gap: 1.125rem;
    margin-top: 2.25rem;
}

.footer-social a {
    display: inline-flex;
    width: 22px;
    height: 22px;
    color: hsl(var(--foreground) / .75);
    text-decoration: none;
    transition: color var(--transition), transform var(--transition);
}

.footer-social a:hover,
.footer-social a:focus-visible {
    color: hsl(var(--foreground));
    transform: translateY(-2px);
}

.footer-social svg {
    width: 100%;
    height: 100%;
    display: block;
}

/* ─── 欄 2–5：標題與清單 ──────────────────────────────────── */
.footer-col-title {
    margin: 0;
    font-size: var(--text-sub);        /* 18px */
    line-height: var(--lh-sub);
    font-weight: var(--fw-normal);
    letter-spacing: var(--tracking);
    color: hsl(var(--heading));
}

.footer-col-list {
    list-style: none;
    margin: 1.5rem 0 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: .875rem;
}

.footer-col-list a {
    font-size: var(--text-body);
    line-height: calc(24 / 14);
    font-weight: var(--fw-light);
    letter-spacing: var(--tracking);
    color: hsl(var(--muted-foreground));
    text-decoration: none;
    transition: color var(--transition);
}

.footer-col-list a:hover,
.footer-col-list a:focus-visible {
    color: hsl(var(--heading));
}

/* ─── 欄 5：訂閱 ─────────────────────────────────────────── */
.footer-sub-desc {
    margin: 1.5rem 0 0;
    font-size: var(--text-body);
    line-height: calc(28 / 14);
    font-weight: var(--fw-light);
    letter-spacing: var(--tracking);
    color: hsl(var(--muted-foreground));
}

/* 設計稿是「細框輸入框 + 旁邊一顆實心膠囊鈕」兩個獨立元件。
   ⚠ 不要做成「鈕嵌在輸入框裡」的一體式：那樣鈕會吃掉輸入框的寬度，
     欄位一窄（未切版的 18 頁欄寬只有 277px）placeholder 就被截成「輸入你的 Ema」。
     分開之後可以 flex-wrap，窄欄時鈕自己換行，字永遠讀得完。 */
.footer-form {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .625rem;
    margin-top: 1.75rem;
}

.footer-input {
    flex: 1 1 8.5rem;
    min-width: 0;
    min-height: 44px;
    padding: 0 1.125rem;
    border: 1px solid var(--footer-line);
    border-radius: var(--radius-pill);
    background: hsl(var(--brand-foreground) / .5);
    font-family: inherit;
    font-size: var(--text-body);
    line-height: 1.6;
    font-weight: var(--fw-light);
    letter-spacing: var(--tracking);
    color: hsl(var(--foreground));
    transition: border-color var(--transition);
}

.footer-input::placeholder {
    color: hsl(var(--muted-foreground) / .6);
}

.footer-input:focus {
    outline: none;
    border-color: hsl(var(--foreground) / .45);
}

.footer-submit {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    gap: .5rem;
    min-height: 44px;
    padding: 0 1.375rem;
    border: 0;
    border-radius: var(--radius-pill);
    background: hsl(var(--brand-accent));
    color: hsl(var(--heading));
    font-size: var(--text-body);
    line-height: 1;
    font-weight: var(--fw-normal);
    letter-spacing: var(--tracking);
    white-space: nowrap;
    cursor: pointer;
    transition: background var(--transition), color var(--transition);
}

/* 箭頭用 ::after 畫，不拉字型 icon 進來（與 .btn-pill 徽章同一套語言） */
.footer-submit::after {
    content: "→";
    font-family: var(--font-en);
    letter-spacing: 0;
}

.footer-submit:hover,
.footer-submit:focus-visible {
    background: hsl(var(--foreground));
    color: hsl(var(--background));
}

/* ─── 下半：版權列 ───────────────────────────────────────── */
/* 版權列：深灰底的滿版色條（2026-07-30 客戶指定）。
   ⚠ margin-inline 換成 padding-inline —— 原本用 margin 是為了讓上方那條
     border-top 對齊內容格線；改成有底色之後，margin 會讓深灰色塊左右內縮，
     兩側露出米白，看起來像一塊貼歪的貼紙。改用 padding 讓底色做到視窗兩側，
     內容仍然靠 --footer-pad-x 對齊格線。
   ⚠ border-top 一併拿掉：底色本身就是分界，再畫一條淺色線會浮在深灰上很突兀。 */
.footer-bottom {
    /* ⚠ 這一列雖然在 .site-footer 之內，但它的底色是**深灰**，
       所以字重要跟其他深灰帶一樣做視覺補償（310／410），
       不能沿用 Footer 米白區的 350／450 —— 否則同一頁的深灰底會出現兩種粗細
       （複查實測：版權列 350、服務專區 310，差 40）。
       數值與 .on-dark 那一組必須一致，改的時候兩處要一起改。 */
    --fw-light:  310;
    --fw-normal: 410;

    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1.5rem;
    padding: 1.75rem var(--footer-pad-x) 2.5rem;
    background: hsl(var(--surface-dark));
}

/* 深灰底上的文字一律反白（原本是米白底上的 --muted-foreground） */
.footer-copy {
    margin: 0;
    font-size: var(--text-body);
    line-height: var(--lh-body);
    font-weight: var(--fw-light);
    letter-spacing: var(--tracking);
    color: hsl(var(--surface-dark-foreground) / .75);
}

.footer-legal {
    list-style: none;
    display: flex;
    gap: 2rem;
    margin: 0;
    padding: 0;
}

.footer-legal a {
    font-size: var(--text-body);
    line-height: var(--lh-body);
    font-weight: var(--fw-light);
    letter-spacing: var(--tracking);
    color: hsl(var(--surface-dark-foreground) / .75);
    text-decoration: none;
    transition: color var(--transition);
}

/* hover 轉成全亮的米白（深灰底上，往「更亮」走才是加強） */
.footer-legal a:hover,
.footer-legal a:focus-visible {
    color: hsl(var(--surface-dark-foreground));
}

/* ─── Footer RWD ─────────────────────────────────────────── */
@media (max-width: 1023.98px) {
    .footer-main {
        grid-template-columns: minmax(0, 1.4fr) repeat(3, minmax(0, 1fr));
        row-gap: 3rem;
    }

    /* 訂閱欄換到第二列、橫跨整個寬度，表單才不會被擠成一條縫 */
    .footer-subscribe {
        grid-column: 1 / -1;
        padding-left: 0;
        border-left: 0;
        border-top: 1px solid var(--footer-line);
        padding-top: 2.5rem;
    }

    .footer-form { max-width: 460px; }
}

@media (max-width: 767.98px) {
    .footer-main {
        grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
        row-gap: 2.75rem;
        padding-block: 3.5rem 2.5rem;
    }

    /* 兩欄時第 3 欄會落在最左，左側那條分隔線要收掉才不會憑空多一條 */
    .footer-brand { grid-column: 1 / -1; padding-right: 0; }
    .footer-col:nth-of-type(2n) { padding-left: 0; border-left: 0; }

    .footer-bottom {
        flex-direction: column;
        align-items: flex-start;
        gap: .75rem;
    }

    .footer-legal { gap: 1.5rem; }
}

/* ═══════════════════════════════════════════════════════════
   桌機（≥ 992）— header／麵包屑對齊內容格線
   容器變數本身定義在 tokens.css，這裡只處理結構上對不齊的元素。
   斷點取 992 是 Bootstrap 的 lg，正好是 wireframe.css 漢堡選單收起、
   header 切回橫向排列的那一刻；992 以下（Tablet／Mobile）完全不動。
   ═══════════════════════════════════════════════════════════ */

@media (min-width: 992px) {
    /* 麵包屑對齊內容格線（首頁沒有，但同版型的其他頁切版後會用到） */
    .is-designed .wf-crumb {
        max-width: calc(var(--container-max) + var(--section-x) * 2);
        margin-inline: auto;
        padding-inline: var(--section-x);
    }
}

/* ═══════════════════════════════════════════════════════════
   Header — 全站共用，依 Figma 重建
   Component Set 513:931
     ├ 513:930 Default  → 疊在 Hero 上：無底色、白字
     └ 513:932 Variant2 → 捲動後：白 50% 底條、深灰字

   基準外觀 ＝ Variant2。沒有 Hero 的 18 頁一律吃這組（透明白字在白底上看不見）。
   首頁（.is-designed）在還沒捲動時才切成 Default。

   992px 以下完全不進這裡：漢堡選單維持 wireframe.css 既有行為，
   窄版設計稿尚未提供（已與 daniel 確認）。
   ═══════════════════════════════════════════════════════════ */

@media (min-width: 992px) {

    .site-header {
        /* ── Header 專用字重（2026-07-30 客戶指定「跟 footer 的第二粗度一樣」）──────
           Footer 有兩級：350（標語／連結／版權）與 450（品牌／欄標題）。
           第二粗＝350，所以這裡兩級都設 350，
           LOGO、導覽、搜尋膠囊、下拉選單一次到位。
           ⚠ Header 比 Footer 輕一階是刻意的：Header 是常駐在畫面上的導覽，
             Footer 是頁尾的資訊區塊；導覽太重會一直搶內容的注意力。
           ⚠ 與 Footer 同樣的做法：**重新定義 token**，不逐條改 font-weight。
             CSS 變數只往下繼承，所以其他區塊讀到的仍是 :root 的 100／200。
           ⚠ 刻意寫在 @media (min-width: 992px) 這一層裡：
             992 以下是 wireframe.css 的漢堡選單，那組樣式不在本檔管轄範圍，
             寫在外面會連帶動到窄版與尚未切版的 18 頁。
           ⚠ 目前分頁（.active）用 --fw-medium 700，比 450 又重一階，
             所以「現在在哪一頁」仍然分得出來，不會被這次加粗吃掉。 */
        /* ⚠ 2026-07-31 客戶指定「header 字體粗一點，跟 footer 一樣」：
           兩級由 350／350 改成與 Footer **完全相同**的 350／450。
           上面那段「Header 比 Footer 輕一階是刻意的」因此不再成立，
           以客戶這次的指定為準。 */
        --fw-light:  350;
        --fw-normal: 450;

        /* header 寬度是整個視窗，內容要對齊頁面格線，否則 LOGO 會貼視窗左緣 */
        --header-pad-x: max(var(--section-x), calc((100% - var(--container-max)) / 2));
        /* 導覽項間距。Figma 為 38px（5 項），本站 6 項需拉大才不擁擠。 */
        --header-nav-gap: 48px;
        /* Figma Variant2：白 50% 底條。blur 讓底下影像不干擾深色字的可讀性 */
        background: hsl(0 0% 100% / .5);
        -webkit-backdrop-filter: blur(12px);
        backdrop-filter: blur(12px);
        border-bottom: 0;
        transition: background var(--transition);
    }

    /* 尚未切版的 18 頁：內容由 wireframe.css 鎖在 .wf-inner(1440) + .wf-section 3.5rem，
       header 要對齊那組數字。各頁切版掛上 .is-designed 後這條自動失效。 */
    body:not(.is-designed) .site-header {
        --header-pad-x: max(3.5rem, calc((100% - 1440px) / 2 + 3.5rem));
    }

    /* ── Header 文字/線條顏色：依「是不是首頁」切換白／黑70%（2026-08-14
       客戶指定：「在其他頁面時導覽跟logo轉黑色70%」）───────────────
       這個 session 稍早把導覽文字、logo、搜尋、字級切換器、Hero 十字線
       全部改成白色，但那組決定的前提是「header 透明疊在首頁 Hero 的
       滿版照片上」——只有首頁有 .hero，其餘 18 頁的 header 背景是實色
       米色（--background），白字疊上去幾乎看不見。
       ⚠ 用 :has(.hero) 判斷是不是首頁，不是另外開一個頁面旗標：這個
         做法本檔已經有先例（2026-07-31「Header 自成一條白色橫條」那段
         的 `.is-designed:has(.hero) .site-header:not(.is-scrolled)`），
         沿用同一套判斷邏輯，不用新發明一種辨識方式。
       ⚠ 定義成自訂屬性（--header-ink）而不是每個元素各寫一份「白版／
         黑70%版」兩條規則：這輪需要切換顏色的元素有導覽文字、per-item
         底線、搜尋、字級切換器、logo 圖示……多達六七處，直接每處複製
         兩份會很難維護（改一次顏色要記得改十幾個地方）。改成所有下游
         規則統一讀這一個變數，這裡只要定義兩次（首頁／非首頁各一次），
         下游規則完全不用管自己在哪一種頁面。
       ⚠ hover／focus／選中狀態刻意不用這個變數、直接寫死
         hsl(var(--heading))（全黑）：不管首頁還是其他頁，hover 都是
         「比平常更黑」的同一個訊號——首頁是白→黑，其他頁是黑70%→黑，
         兩種情境下寫死同一個全黑都是正確的終點值，不需要再切換。
       ⚠ 2026-08-14 客戶「導覽文字改黑色70%」——首頁分支的值一度改成跟
         非首頁分支一樣的黑70%，兩個分支當時同值。
       ⚠ 2026-08-17 客戶參考日式老建築照片＋白色細字導覽的參考圖，要求
         首頁 Header 改回白色細字（「選單為白色細字」），同時明確指定
         「其他頁面選單文字改黑70%」——這句話本身就代表首頁跟其他頁面
         這次要分開處理，不能再共用同一個值。首頁分支改回白色
         （--brand-foreground），非首頁分支維持黑70%不變。保留
         :has(.hero) 這組切換架構（沒拆掉、只改分支的值）：下游規則
         （導覽文字／底線／搜尋／logo／AAA）全部讀這一顆變數，不需要
         逐一去改，這正是當初設計成自訂屬性、不逐處寫死的意義所在。 */
    html:has(.hero) .site-header {
        --header-ink: hsl(var(--brand-foreground));
    }

    html:not(:has(.hero)) .site-header {
        --header-ink: hsl(var(--heading) / .7);
    }

    /* Figma：內容列 30px；Variant2 底條 39px ＝ 30 + 上下各 4.5 */
    .site-header .inner {
        max-width: none;
        padding: 4.5px var(--header-pad-x);
        align-items: center;
        transition: padding var(--transition);
    }

    /* ─── LOGO：18px / Regular 400 / 字距 0.2em ─────────────── */
    .site-header .site-logo {
        font-size: var(--text-sub);      /* 18px */
        line-height: var(--lh-sub);      /* 30px */
        font-weight: var(--fw-normal);   /* 400 */
        letter-spacing: var(--tracking); /* 0.2em */
        color: hsl(var(--foreground));   /* #2F2F2F */
        transition: color var(--transition);
    }

    /* ─── 導覽：14px / DemiLight 350 / 字距 0.2em / 間距 38px ──── */
    .site-header .site-nav {
        /* Figma 的 auto-layout 間距是 38px，但那是 5 個導覽項；
           本站保留 6 項（多了實體課程），38px 排起來過密，daniel 要求拉大。
           要再調就改這一個值。 */
        gap: var(--header-nav-gap);
        align-items: center;
    }

    /* 導覽文字 ＝ CN p：Noto Sans TC / demilight 350 / 14 / 行高 30 / 字距 20% */
    .site-header .site-nav > a,
    .site-header .site-nav > .dropdown > a {
        font-size: var(--text-body);     /* 14px */
        line-height: var(--lh-body);     /* 30px（= 30 ÷ 14） */
        font-weight: var(--fw-light);    /* 350 DemiLight */
        letter-spacing: var(--tracking); /* 0.2em */
        text-transform: none;            /* Figma 是中文，非大寫英文 */
        /* ⚠ 2026-07-31：由 70% 黑改成全黑。客戶說「header 字體粗一點，跟 footer 一樣」——
           Footer 的連結是 350 全色，Header 也是 350，**字重本來就一樣**；
           看起來比較細的原因是這裡壓了 70% 不透明度（淡＝視覺上細）。
           所以真正要對齊的是色彩強度，不是再往上加字重。
           ⚠ .active 仍然靠 --fw-medium(700) 分辨「現在在哪一頁」，
             不會因為大家都變全黑就認不出來（見下一條規則）。
           ⚠ 2026-08-14 客戶「線跟文字還有logo都用同個顏色，黑70%」，隨後
             再指定「線跟logo還有導覽都改成白色」——這條是 ≥992px 的**通用**
             規則，同時管到 992–1467px 的抽屜（白底）與 ≥1468px 的桌機導覽
             （透明疊在照片上）。白色只適合後者：抽屜是白底，文字若跟著
             變白會整段看不見。這裡維持原本的全黑（drawer 需要的顏色），
             白色改成只在 ≥1468px、非 .is-compact 時另外覆寫（見本檔「桌機
             導覽文字改白色」那段），不動這條通用規則的值。 */
        color: hsl(var(--heading));
        transition: color var(--transition);
    }

    /* ── 桌機導覽文字依頁面切換白／黑70%（2026-08-14 客戶先指定「線跟logo
       還有導覽都改成白色」，隨後補充「在其他頁面時導覽跟logo轉黑色70%」）
       ─────────────────────────────────────────────────────────────
       只在 ≥1468px 且不是 .is-compact 生效：那才是「透明疊在照片上」的
       桌機導覽狀態。992–1467px 抽屜、捲動收合後重用同一顆抽屜的桌機
       情境，背景都是實色白底，維持上面那條通用規則的全黑，不套用這裡。
       ⚠ 讀 --header-ink（見本檔「Header 文字/線條顏色」那段）：首頁
         （:has(.hero)）是白色，其他 18 頁是黑 70%，不是寫死單一顏色。 */
    @media (min-width: 1468px) {
        html:not(.is-compact) .site-header .site-nav > a,
        html:not(.is-compact) .site-header .site-nav > .dropdown > a {
            color: var(--header-ink, hsl(var(--brand-foreground)));
        }
    }

    /* ⚠ 2026-08-13 漢堡選單補的圖示／chevron（wf_icon()）只給窄幅抽屜用，
       跟桌機水平導覽列共用同一份標記，這裡桌機寬度要把它們藏起來，不然
       文字前面會多一顆意料外的小圖示。標記本身沒有拆兩份（DRY），純粹
       用 CSS 依斷點決定顯不顯示。
       ⚠ 2026-08-13 複查修正：這三條原本直接寫在 @media (min-width: 992px)
         這一層（本節開頭），當時「≥992 ＝ 真的桌機水平導覽」還成立。
         但「漢堡選單斷點下修」那次把抽屜的顯示範圍延伸到 992–1467px
         （見本檔 9800 行），992 這個數字因此變成兩個互相矛盾的意思：
         抽屜（需要圖示／logo／小標）在這個寬度顯示，這裡卻同時把它們藏起來
         ——寬 1044 這種落在 992–1467 之間的桌機視窗因此看到「有抽屜外殼、
         沒有圖示/分組小標/選中反白」的半成品畫面（客戶回報「跑版」）。
         改成巢狀 @media (min-width: 1468px)：只有真的切到水平導覽列
         （見本檔 11243 行同一個斷點）才藏，992–1467 讓抽屜版的圖示與小標
         正常顯示，跟窄幅手機（≤991.98）長一樣。 */
    @media (min-width: 1468px) {
        /* ⚠ 2026-08-14 補 html:not(.is-compact)：客戶新指令要桌機捲動離開頁首後
           也收合成右上方形選單鈕、重用同一顆 #wfNav 抽屜（見本檔「桌機（≥1468px）
           捲動收合」那個新區塊）。抽屜長相就是靠圖示／logo／分組小標「顯示」——
           這裡原本無條件藏起來，.is-compact 時得放行，才會跟 992–1467px 那份
           抽屜長一樣。平常（不是 .is-compact）維持原樣繼續藏，水平導覽列不受影響。 */
        html:not(.is-compact) .site-header .site-nav > a .wf-icon,
        html:not(.is-compact) .site-header .site-nav > .dropdown > a .wf-icon {
            display: none;
        }

        /* ⚠ 抽屜專用的 logo（2026-08-13 那次「漢堡選單最上面加 logo」）也是同一份
           標記、同一顆 <nav> 裡，桌機水平導覽列同樣要藏起來 —— 不然它會被當成
           導覽列的其中一個 flex 項目，跟其他項目擠在同一列、疊到左邊的 header
           logo 上面（實測就是這樣重疊）。 */
        html:not(.is-compact) .site-header .site-nav-logo {
            display: none;
        }

        /* 同上，2026-08-13 新增的選單小標（MENU）也只給抽屜用 */
        html:not(.is-compact) .site-header .site-nav-section-label {
            display: none;
        }
    }

    /* ── 導覽排成右上角多欄選項（2026-08-14 客戶補圖修正）─────────────────
       客戶先前文字指定「1px 細線分區」，隨後補了一張參考圖澄清實際要的
       樣子：右上角是選項按鈕，兩個一行、logo 在左，並沒有分隔線。以這張
       圖為準，取代前一版的單列＋細線做法（同一天內、同一個功能還沒定案
       前的修正，不是事後推翻已確認的東西，直接改這裡而不是疊一層覆蓋）。
       ⚠ 6 個項目（服務專區／實體課程／知識專區／會員／電子報／搜尋）排成
         3 欄 × 2 列（不是參考圖那樣的 2 欄）：本站項目比參考圖的 4 個多，
         2 欄會疊成 3 列，單列內容高約 34px，3 列＋列距超過 100px，遠高於
         全站 --header-h（56px，2026-08-03 客戶指定的固定高度 SSOT），
         版面會整組往上下溢出頭部列（實測：第一列整段跑到 header 上緣之外）。
         3 欄只需要 2 列（約 76px），配合下面把這個狀態的 header 高度放寬到
         auto，兩邊一起調才放得下，維持「兩個一行」的排列精神但欄數依項目
         數量彈性決定，不是死板照抄參考圖的欄數。
       ⚠ 不用細線，只靠 grid 的 row-gap／column-gap 留白分隔，跟參考圖一致。
       ⚠ display 要 !important：標記上的 .d-lg-flex 是 Bootstrap 內建
         utility class，編譯死是 display:flex!important，一般特異度贏不了
         （跟下面「桌機（≥1468px）捲動收合」新區塊撞到的坑是同一個）。 */
    @media (min-width: 1468px) {
        html:not(.is-compact) .site-header .inner > .site-nav {
            display: grid !important;
            grid-template-columns: repeat(3, auto);
            grid-auto-flow: row;
            column-gap: 2rem;
            row-gap: .5rem;
            align-items: start;
        }

        /* ⚠ 2 列排下來的內容高度（約 76px）超過全站固定的 --header-h
           （56px）——這個狀態改用 auto，讓 header 隨內容自然撐高，不強行
           把兩列擠進 56px（那樣文字會被裁掉/重疊）。 */
        html:not(.is-compact) .site-header {
            height: auto;
            min-height: var(--header-h);
        }

        /* ⚠ 2026-08-14 排查記錄：上面這條讓 header 自己長高到 auto，但
           --header-h 這顆 SSOT 當初刻意沒有跟著覆寫——原始註解寫「其他
           地方仍讀原本的 56px，例如...Hero 內距的 SSOT」，但「其他地方」
           正好就包含全站每一頁的上緣讓位（.wf-frame 的
           padding-top:var(--header-h)、course_detail 等頁 Hero 自己的
           min-height 算式）。這些全部讀同一顆 --header-h，這個狀態下讀到
           的還是舊的 56px，於是整頁 Hero／色帶最上面一截被這裡剛放寬到
           auto、實測 190.78px 高的 header 蓋住（讓位只留 56px，差了約
           135px）——客戶在 course_detail 頁面截圖回報「上方多一條空白」，
           實測其餘 4 種頁首色帶（.hero／.cx-hero／.kd-hero／.sv-hero）
           在 ≥1468px 都是同一個成因。
           ⚠ 修法：--header-h 本身也在這個寬度覆寫成 header 實測的真實
             高度，不是另外去改每一個讀 --header-h 的地方——SSOT 的意義
             就是「只改一處，其餘自動跟上」，這裡才是真正需要覆寫 SSOT
             的時機，不是例外。191px 是實測值（190.78125px 無條件進位）。
           ⚠ 刻意「不」用 html:not(.is-compact) 限定（試過，會出新
             bug）：--header-h 同時也是 .wf-frame 的 padding-top 讀的
             那顆 token，如果只在「未捲動」狀態覆寫，使用者往下捲動、
             .is-compact 一旦切換，--header-h 會瞬間從 191 掉回 56，
             .wf-frame 的 padding-top 跟著瞬間收 135px，正在閱讀的內容
             會整段往上跳一下（實測：捲動軌跡對照 IntersectionObserver
             切換的那一格，追蹤元素的螢幕座標瞬間位移 135px，其餘每一格
             都只位移跟捲動量相等的正常幅度）。改成整個 ≥1468px 無條件
             覆寫成 191（不管有沒有 .is-compact）就不會有這個瞬間跳動——
             .is-compact 只會在使用者已經捲過頁首色帶之後才觸發（見
             home.js 的 IntersectionObserver，觀察頁首色帶是否還在畫面
             上），這代表「頁面最上緣多讓了 135px」這件事，發生的當下
             使用者根本看不到頁面最上緣（已經捲過去了），126px 的差額
             不會在畫面上產生任何可見的空白，純粹是文件流裡一個不影響
             觀感的數字，換來的是完全不會跳動。
           ⚠ 副作用：.is-compact 狀態下 header 自己的高度也會被這裡的
             191 影響（下面基底規則 height:var(--header-h) 沒有排除
             .is-compact），所以另外補一條 html.is-compact .site-header
             { height: 56px } 把收合狀態的 header 高度釘回 56，不透過
             token（這個狀態下 --header-h 代表的已經是「內容讓位」用途，
             不是「header 自己多高」，兩者這裡開始分家，一個 token 兩種
             意思是這次踩坑的根本原因，收合狀態直接寫死數字，不再共用同一
             顆變數）。
           ⚠ 選擇器故意寫 html:root 而不是單純 :root：本檔「Header 高度
             改成 56px」那段（≈7100 行）也有一條 :root { --header-h: 56px }，
             是不帶任何條件、寫在後面的無條件宣告——兩條 :root 特異度相同
             時「後面贏」，那一條會贏過這裡（即使這裡的 @media 條件成立）。
             html:root 跟 :root 指向同一個元素，但多算一個元素選擇器，
             特異度墊高一階，不管兩條規則誰先誰後，這裡都會贏，不必去動
             或搬動那條後面的宣告（本檔慣例：只加不改）。
           ⚠ 2026-08-14 客戶回覆「我不能 header 只在這區這樣改嗎」——
             這條原本無條件對全站生效（首頁／實體課程總覽／知識專區
             單篇／服務內頁的同一種留白 bug 因此一併修好，見上面的排查
             記錄），客戶確認只要 course_detail 這一頁修好，其餘 4 頁
             維持原本 56px 的舊行為（連帶恢復舊的留白 bug，是客戶確認
             要的取捨，不是漏改）。加 :has(.cd-hero) 收斂：.cd-hero
             全站只在 course_detail 的 Hero 區塊出現，用它判斷「目前是
             不是 course_detail 頁面」，跟本檔既有 html:has(.hero) 那類
             頁面類型判斷寫法一致。 */
        html:root:has(.cd-hero) {
            --header-h: 191px;
        }

        /* ⚠ 承接上面的副作用說明：.is-compact 狀態的 header 高度改成寫死
           56px，不再讀 --header-h（這個 token 在 ≥1468px 已經代表「內容
           讓位量」，跟「header 收合後自己多高」是兩件事）。 */
        html.is-compact .site-header {
            height: 56px;
        }

        html:not(.is-compact) .site-header .inner {
            padding-block: .875rem;
            /* 2026-08-14 客戶「aaa下移後，上方導覽也下移一點」——AAA 移到
               Hero 水平線下面之後（見「字級切換器上方的橫線」那段跟
               home.js「Hero 十字線」那段），logo／導覽這一列變成視覺上
               最頂端的內容，客戶要它整列跟著往下退一點。只加大 padding-top
               （padding-bottom 維持上面 padding-block 給的 .875rem 不動），
               logo 跟導覽同在第一列、用 grid 的 align-items:center 對齊，
               這裡動整列的內距而不是單獨改 nav 的 margin-top，兩者才會
               一起往下移、不會互相對不齊。 */
            padding-top: 1.5rem;
        }

        /* ── 導覽列改成兩個群組上下排列（2026-08-14 客戶指定）───────────
           客戶：「導覽列改成兩個群組上下排列。」已跟客戶確認：維持現在
           3+3 的拆法（上排 服務專區／實體課程／知識專區，下排 會員／
           電子報／搜尋——DOM 順序本來就是這樣，上面那條 3 欄 grid 的
           auto-flow 剛好落在這個分界），只是要**明確分組**：兩排之間
           拉開更明顯的間距，讓人一眼看出是兩組、不是隨便換行。
           ⚠ 這裡必須跟上面 grid 那條規則同一個 @media(min-width:1468px)
             範圍內——分組的概念只在「導覽是 3 欄 2 列 grid」這個版型下
             成立，992–1467px 抽屜是單欄直排的列表，套用 row-gap 會把
             不相關的版面弄亂（第一版誤寫在這個媒體查詢外面，客戶畫面
             上還沒看到就先自查修正）。
           ⚠ row-gap 從 .5rem 加大到 1.75rem：原本的 .5rem 只是「換行的
             行距」，加大後兩排視覺上才會像兩個群組，不是同一段落自然
             斷行。 */
        html:not(.is-compact) .site-header .inner > .site-nav {
            position: relative;
            row-gap: 1.75rem;
        }

        /* ⚠ 2026-08-14 客戶「每個導覽各自底下有一條橫線，不是一個群組導覽
           一條橫線」——上一版用 .site-nav::before 畫一條橫跨整個 grid
           寬度的分隔線，客戶要的其實是每一個導覽項目**各自**在自己下面
           有一條線（6 條各自獨立的短線，不是 1 條橫跨全寬的長線），改成
           在每個項目自己的 <a> 上加 border-bottom，不再用共用的 ::before。
           ⚠ padding-bottom 讓文字跟線之間留一點呼吸空間，不貼著文字。
           ⚠ 顏色讀 --header-ink，跟導覽文字同一個依頁面切換的顏色（首頁
             白、其他頁黑 70%），不是寫死白色。 */
        html:not(.is-compact) .site-header .site-nav > a,
        html:not(.is-compact) .site-header .site-nav > .dropdown > a {
            padding-bottom: .375rem;
            border-bottom: 1px solid var(--header-ink, hsl(var(--brand-foreground)));
        }
    }

    .site-header .site-nav > a.active,
    .site-header .site-nav > .dropdown > a.active {
        font-weight: var(--fw-medium);
        color: hsl(var(--heading));
    }

    /* ── 導覽連結的 hover（2026-07-31 客戶指定新增）───────────────────
       原本只有 transition: color 卻沒有任何 :hover 規則 —— 指到導覽項完全沒有回饋。
       做法照全站的語言：文字由 70% 轉為全黑 ＋ 一條 1px 細線由左往右展開。

       ⚠ 底線用 background-image 畫，**不用 ::before／::after**：
         .dropdown-toggle::after 已經是下拉的三角形（被佔用了），
         而純 <a> 與帶下拉的 <a> 必須是同一套 hover，用偽元素就得分兩套寫法。
       ⚠ 寬度扣掉 .2em：字距會在最後一個字之後也留白（同一個原因讓上面那條
         margin-right: -.2em 存在），不扣的話底線會多出一截、右緣對不齊文字。
       ⚠ 排除搜尋鈕（.site-search）：那一項是膠囊按鈕，有自己的 hover，
         再壓一條底線會變成「膠囊底下多一條線」。
       ⚠ :focus-visible 一起給：鍵盤使用者要看得到同樣的回饋。 */
    .site-header .site-nav > a:not(.site-search),
    .site-header .site-nav > .dropdown > a {
        background-image: linear-gradient(hsl(var(--heading)), hsl(var(--heading)));
        background-repeat: no-repeat;
        background-position: 0 100%;
        background-size: 0 1px;
        transition: color var(--transition),
                    background-size var(--dur-hover) var(--ease-out);
    }

    .site-header .site-nav > a:not(.site-search):hover,
    .site-header .site-nav > a:not(.site-search):focus-visible,
    .site-header .site-nav > .dropdown > a:hover,
    .site-header .site-nav > .dropdown > a:focus-visible {
        color: hsl(var(--heading));
        background-size: calc(100% - .2em) 1px;
    }

    @media (prefers-reduced-motion: reduce) {
        .site-header .site-nav > a:not(.site-search),
        .site-header .site-nav > .dropdown > a {
            transition: color var(--transition);
        }
    }

    /* Header 裡另外兩個連結：搜尋膠囊與 LOGO。
       ⚠ 客戶說的是「header 的**連結**新增 hover 效果」，這兩個也是連結，
         原本同樣一條 hover 規則都沒有（複查抓到 —— 上面那段註解一度寫成
         「搜尋鈕有自己的 hover」，實際上不存在）。
       ⚠ 兩者都**不加底線**：搜尋是膠囊按鈕（底線會變成膠囊下面多一條線）、
         LOGO 是品牌字（加底線會讀成一般連結）。改用各自合適的語言：
         膠囊反相、LOGO 轉全黑。 */
    .site-header .site-nav > a.site-search {
        transition: background var(--transition), color var(--transition),
                    border-color var(--transition);
    }

    .site-header .site-nav > a.site-search:hover,
    .site-header .site-nav > a.site-search:focus-visible {
        background: hsl(var(--foreground));
        color: hsl(var(--background));
        border-color: hsl(var(--foreground));
    }

    /* LOGO 圖示＋文字（2026-08-13 客戶指定，取代原本的手寫字標圖片）。
       ⚠ 圖示只鎖高度、寬度 auto：來源 SVG viewBox 比例約 1.25:1（近方形），
         鎖寬會壓扁圖形。28px 沿用原本手寫字標的高度值，與旁邊 18px 文字
         （見本檔開頭 .site-logo 字級規則）配成一組合理的圖文比例。
       ⚠ gap 原本 .5rem，客戶回報「logo 離品牌字有點近」，拉大到 .75rem
         （8px → 12px）——圖示與文字之間留更明顯的呼吸，不貼在一起。 */
    .site-header .site-logo {
        display: inline-flex;
        align-items: center;
        gap: .75rem;
    }

    .site-header .site-logo-icon {
        display: block;
        width: auto;
        height: 28px;
        flex-shrink: 0;
    }

    /* ⚠ 字距蓋過 .site-logo 本來讀的 --tracking（.06em）：客戶指定「品牌字字寬拉大一點」，
       品牌名是獨立的識別文字，不必跟內文共用同一階字距，改用 --tracking-title（.10em）
       這個既有的「標題級」字距 token，不另外發明新數字。
       ⚠ 2026-08-13 客戶「logo 品牌字請用 16，並且顏色請用跟 logo 一樣的顏色」：
         字級蓋過 .site-logo 本來讀的 --text-sub（今天稍早改成 26px，見全站字級
         12/14/26/26/32 那次調整）——品牌字是獨立識別文字，不該跟著內文字級表
         的階層一起變動，固定寫死 16px。
       ⚠ 顏色比照圖示：圖示 fill 原本是客戶原始檔案給的固定色 #4a4636（不是
         currentColor），文字這裡原本直接寫死同一個色碼——「跟 logo 一樣的
         顏色」是圖文對齊同一個識別色，不是套用站上某個既有色階剛好數值相近。
       ⚠ 2026-08-14 客戶先指定「線跟文字還有logo都用同個顏色，黑70%」，
         隨後改「都改成白色」，最後再指定「其他頁面時logo轉黑色70%」——
         最終狀態是依頁面切換，讀本檔「Header 文字/線條顏色」那段定義的
         --header-ink（首頁白、其他頁黑70%），不是寫死單一顏色。 */
    .site-header .site-logo-text {
        white-space: nowrap;
        font-size: 1rem;
        letter-spacing: var(--tracking-title);
        color: var(--header-ink, hsl(var(--brand-foreground)));
    }

    /* ⚠ 圖示 fill 是客戶原始檔案給的固定色值（#4a4636），不是 currentColor，
       hover 時 color 轉換只會影響文字；圖示改用 opacity 一起變淡，
       兩者仍然是同一個「整組變淡」的視覺回饋，不必為了讓 hover 影響圖示
       而去改客戶給的顏色。 */
    .site-header .site-logo {
        transition: color var(--transition), opacity var(--transition);
    }

    .site-header .site-logo:hover,
    .site-header .site-logo:focus-visible {
        color: hsl(var(--heading));
        opacity: .62;
    }

    /* 字距會在最後一個字之後也留白，使各項右緣多出 0.2em、間距視覺不均。
       補償要下在 flex item 層級（純 <a> 與 .dropdown 都是），
       只加在 <a> 上會漏掉帶下拉的項目。沿用 wireframe.css 的作法，數值改成 0.2em。 */
    .site-header .site-nav > a,
    .site-header .site-nav > .dropdown {
        margin-right: -.2em;
    }

    /* 下拉三角形：Figma 為 8×8 指向下方 */
    .site-header .site-nav .dropdown-toggle::after {
        border-top-width: 5px;
        border-left-width: 4px;
        border-right-width: 4px;
        margin-left: .1em;
        vertical-align: middle;
    }

    /* ─── 搜尋：膠囊底（Figma 67×29、圓角 30）+ 10×10 放大鏡 ────── */
    .site-header .site-search {
        display: inline-flex;
        align-items: center;
        gap: .5rem;
        height: 29px;
        padding: 0 .9rem;
        border-radius: var(--radius-pill);
        background: hsl(var(--heading) / .7);   /* #000000 @70% */
        color: hsl(var(--background));          /* #F9F6F2 */
        transition: background var(--transition), color var(--transition);
    }

    .site-header .site-search .site-search-label {
        font-size: var(--text-body);
        line-height: 1;
        font-weight: var(--fw-light);
        letter-spacing: var(--tracking);
        /* 字距在末字之後留白，膠囊右側會看起來比左側寬，補回來 */
        margin-right: -.2em;
    }

    .site-header .site-search-icon {
        flex: 0 0 auto;
        display: block;
    }

    /* 搜尋自己有底色，不吃導覽項的通用配色與字距補償 */
    .site-header .site-nav > a.site-search {
        margin-right: 0;
        color: hsl(var(--background));
    }

    /* ─── 下拉選單：對齊設計調性（米白底、直角、寬字距） ──────────── */
    .site-header .site-nav .dropdown-menu {
        border: 1px solid hsl(var(--border));
        border-radius: var(--radius);
        background: hsl(var(--card));
        padding: .5rem 0;
        margin-top: .5rem;
        box-shadow: 0 12px 28px hsl(0 0% 0% / .12);
    }

    .site-header .site-nav .dropdown-item {
        font-size: var(--text-body);
        line-height: var(--lh-body);
        font-weight: var(--fw-light);
        letter-spacing: var(--tracking);
        color: hsl(var(--foreground));
        padding: .15rem 1.25rem;
    }

    .site-header .site-nav .dropdown-item:hover,
    .site-header .site-nav .dropdown-item:focus {
        background: hsl(var(--muted));
        color: hsl(var(--heading));
    }

    /* ─── 首頁：疊在 Hero 上的 Default 狀態 ─────────────────────
       fixed 而非 sticky —— header 不佔流程空間，Hero 才會頂到頁面最上緣，
       與設計稿「header 浮在 hero 影像上」一致。 */
    .is-designed .site-header {
        position: fixed;
        inset-inline: 0;
        top: 0;
        /* 底線常駐、只換顏色 —— 這樣捲動切換時是淡入淡出，不是瞬間消失。
           header 是 fixed，多這 1px 不影響頁面流程高度。 */
        border-bottom: 1px solid transparent;
        transition: background var(--transition), border-color var(--transition);
    }

    /* ─── Header 底部分隔線（Figma 603:696「Vector 8」）────────────
       設計稿實測（file HA3Phyl0yV5OKS8nb4hHMr / index frame 490:209）：
         型別 VECTOR，fills 空、只有 stroke
         absoluteBoundingBox  x −2｜y 81｜w 1442｜h 0   → 滿版（被畫布裁成 0–1440）
         strokeWeight 0.5、strokeAlign CENTER          → 實際畫在 y 80.75–81.25
         stroke SOLID #D6C1A9 a=1.0，綁定共用樣式 6:24「color/second」
                                                       → 就是 --brand-accent
       它是 header frame 603:720 的第一個子節點，而該 frame 只有 30px 高
       （y 29–59，clipsContent:false），所以線是畫在 frame 下方 22px 處。

       ⚠ 2026-07-30 依客戶指定，這一區改了兩件事：
         1) **捲動後的樣式與 Hero 上完全相同** —— 不再切換成 Figma Variant2
            （513:932）的「白 50% 底條 + blur」。兩態共用同一份宣告，
            捲動時 header 完全不變樣，也因此沒有任何 Layout Shift
            （原本會從 81px 縮成 39px）。
         2) **底色改為實心米色**（--background #F9F6F2，與 Hero／知識專區／
            Footer 同一塊米白）。這一步同時解掉了可讀性問題：
            header 是 fixed，服務專區與實體課程的底色是 #2F2F2F，
            而 header 文字是 --foreground #2F2F2F —— **同一個色值**。
            透明時捲到那兩段 header 會整片消失（不是變淡，是完全看不見），
            而且滿版的服務／課程主視覺還會直接透到導覽文字底下。
            實心米色之後：疊在 Hero／知識專區／Footer 上與底色同色、看起來
            仍然「沒有 header 底條」；疊在深色帶上則是一條乾淨的米色列。
       ⚠ 因此不需要（也不能）再做「深色帶上反白」：米白字配米色底一樣看不見。
       ⚠ Figma 的 Variant2 樣式保留在 .site-header 基底規則裡，
         仍供尚未切版的 18 頁（body 沒有 .is-designed）使用，不是死碼。 */
    .is-designed .site-header {
        background: hsl(var(--background));
        -webkit-backdrop-filter: none;
        backdrop-filter: none;
        border-bottom-color: hsl(var(--brand-accent));   /* #D6C1A9 */
    }

    /* ── 停在 Hero 上時：不要底色、不要線（2026-07-31 客戶指定）──────────
       ⚠ 2026-07-30 的決定是「捲動前後樣式完全相同、一律實心米色」，
         這一次客戶改了主意 —— 以這次為準，但**只在有 Hero 的頁面**放行。
         用 :has(.hero) 限定：其他 18 頁沒有 Hero，它們的 header 若在頂端變透明，
         內容會直接壓在導覽底下（那正是當初改成實心底的原因）。

       ⚠ 切換點沿用既有的 .is-scrolled（scrollY > 24，見 home.js）。
         這個門檻不是隨手取的：Hero 頂端到桌曆金屬圈約 106px、header 高 82px，
         也就是**捲到 24px 的那一刻，桌曆才剛要滑進 header 底下** ——
         底色正好在「開始需要它」的前一刻補上，不會早也不會晚。
       ⚠ 底線用 transparent 而不是 border-width:0：既有規則刻意讓線常駐、只換顏色，
         這樣切換是淡入淡出而不是瞬間跳動（也不會讓 header 高度變化 1px）。 */
    .is-designed:has(.hero) .site-header:not(.is-scrolled) {
        background: transparent;
        border-bottom-color: transparent;
    }

    /* 設計稿是 0.5px 的 hairline。高解析螢幕直接照著畫（1 個實體像素），
       一般螢幕退回 1px —— 0.5px 在 1x 上會被抹成半透明灰或直接消失。
       寬度要兩態一起設：只寫在 :not(.is-scrolled) 的話，捲動當下寬度會瞬間
       跳回 1px，而顏色還在 200ms 淡出，看起來像線先變粗再消失。 */
    @media (min-resolution: 2dppx) {
        .is-designed .site-header {
            border-bottom-width: .5px;
        }
    }

    /* Figma：導覽列上方 29px、下方 22px，加上 30px 的內容列 = 分隔線落在 y=81。
       上下不對稱是設計稿本來就有的（603:720 y=29–59，Vector 8 y=81）。
       ⚠ 同樣不再限定 :not(.is-scrolled)：內距是「樣式一樣」的一部分，
         只改底色卻讓 header 從 81px 縮成 39px，捲動時還是會看到它變樣。 */
    /* ⚠ 2026-07-31 客戶指定「header 稍微下移一點」：上內距 29 → 39px。
       ⚠ 下移的是**內容**不是整條 bar：bar 仍然貼齊 top:0。
         若改成 top:12px 讓整條往下浮，捲動後那條實心底色上方會露出一道縫，
         底下的內容會從縫裡穿過去。
       ⚠ header 高度因此由 81 → 91px，連帶兩個地方要跟著調（已一起改）：
         ① tokens.css 的 --cal-top-gap（桌曆上方保留的高度預算）
         ② home.js 的 THRESHOLD（底色補上的時機） */
    .is-designed .site-header .inner {
        padding-block: 39px 22px;
    }

    /* 2026-07-29 Figma 改版：Hero 從滿版照片改成米白底（603:719 Rectangle 9 = #F9F6F2），
       header 的白字狀態必須拿掉 —— 白字配米白底完全看不見。
       文字因此沿用深灰（Figma header 603:720 的配色）。
       2026-07-30 再改：底色從「無底色」變成實心米色，捲動前後一致（見上方）。
       ⚠ 一度做過「疊在深色帶上時整組反白 + 頂部漸層」的版本，
         改成實心米色之後兩者都必須撤掉 —— 米白字配米色底一樣看不見。
         home.js 的 .is-on-dark 判定也已一併移除。 */
}

/* ═══════════════════════════════════════════════════════════
   RWD — 窄幅收斂
   ═══════════════════════════════════════════════════════════ */

/* ─── 小桌機（≤1399 / ≤1199）───────────────────────────────
   服務網格與課程欄距在窄幅逐段收斂；版型（左右分欄、兩欄／三欄）不變。
   平板區間再把服務左欄縮窄一階，右欄仍維持兩欄。 */
@media (max-width: 1399.98px) {
    .svc-grid    { --svc-tile-gap: 2.5rem; }
    .course-grid { --course-gap: 2.5rem; }
}

@media (max-width: 1023.98px) {
    .svc-layout { --svc-intro-w: 23%; --svc-gutter: 3.5%; }
    .svc-main   { gap: 3rem; }
    .svc-grid   { --svc-tile-gap: 2rem; }

    .course-grid { --course-gap: 2rem; }
}

@media (max-width: 1023.98px) {

    /* ── 服務專區：左右分欄改成上下排列 ──
       標題／介紹／查看全部整組移到最上方，桌機那條垂直細線收起，
       改由左欄自己的下框線當成橫向分隔線（規格：拿掉垂直線、改成橫線）。
       sticky 一併取消 —— 一欄時它已經在最上面，黏著只會擋住內容。 */
    .svc-layout {
        grid-template-columns: minmax(0, 1fr);
    }

    .svc-layout::before { display: none; }

    .svc-intro {
        position: static;
        padding-bottom: 2rem;
        margin-bottom: 2.5rem;
        border-bottom: 1px solid var(--rule-on-dark);
    }

    .svc-intro-desc { max-width: none; }

    /* ── 實體課程：三欄收成兩欄，第三張換到下一列 ──
       兩欄時左欄是奇數格。先用 :nth-child(n) 把桌機那條「3n+1 不畫線」
       的規則整組還原（同樣的權重、寫在後面才蓋得掉），再改成奇數格不畫。 */
    .course-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }

    .course-cell:nth-child(n)::before { display: block; }
    .course-cell:nth-child(odd)::before { display: none; }
}

/* ─── 手機（≤767）────────────────────────────────────────────
   服務網格與課程都收成單欄，欄間垂直細線全部收起（單欄沒有欄可分隔）。
   主打大圖在 DOM 裡本來就是第一個，收成單欄後自然優先出現；
   所有圖片與文字一律直接可見，不依賴 hover。 */
@media (max-width: 767.98px) {
    .svc-feature-media { aspect-ratio: 16 / 10; }

    /* 名稱與分類／箭頭改成上下兩段，半寬的手機不會把兩邊都壓扁 */
    .svc-feature-info {
        flex-direction: column;
        align-items: stretch;
        gap: 1.25rem;
    }

    .svc-feature-tail {
        padding-top: 0;
        justify-content: space-between;
    }

    .svc-grid {
        grid-template-columns: minmax(0, 1fr);
        row-gap: 2.5rem;
    }

    .svc-tile:nth-child(even)::before { display: none; }

    .course-grid {
        grid-template-columns: minmax(0, 1fr);
        row-gap: 2.5rem;
    }

    .course-cell:nth-child(n)::before { display: none; }
}


/* ═══════════════════════════════════════════════════════════
   圖示動態（2026-07-31，客戶提供的 motion/react 元件移植）
   ═══════════════════════════════════════════════════════════
   客戶給的是 React + motion/react 的四個動畫圖示元件。本專案是 CI4 + 原生 JS、
   **零 npm 依賴**，不可能直接放 React 進來，所以：
     幾何 → 逐字照抄路徑（見 app/Common.php 的 wf_icon()）
     動態 → 這一區用 CSS keyframes 重寫，時間、幅度、延遲都照客戶元件的數字

   ⚠ 觸發條件是「滑鼠指到**包住圖示的那個連結**」，不是指到 SVG 本身 ——
     原元件的 onHoverStart 掛在整個元件外層，圖示旁邊還有文字（YouTube／Podcast），
     只在 SVG 上觸發的話，指到文字時圖示不會動，讀起來像壞掉。
   ⚠ 一律包在 @media (hover: hover)：觸控裝置的 :hover 會「黏住」
     （點過的項目一直維持動畫後的狀態），這是本檔其他區塊已經處理過的坑。
   ⚠ transform-box: fill-box + transform-origin: center 缺一不可：
     SVG 子元素的 transform 預設以**整個 viewBox 的原點**為基準，
     不設 fill-box 的話 scale 會把圖形甩到畫面外，不是原地縮放。
   ⚠ 動畫結束後回到原狀（forwards 一律不用）：這些是 hover 的暫態回饋，
     不是狀態切換。 */

.wf-icon {
    display: block;
    overflow: visible;   /* 放大時不被 viewBox 裁掉 */
}

.wf-icon > *,
.wf-icon > * > * {
    transform-box: fill-box;
    transform-origin: center;
}

@media (hover: hover) {

    /* ── YouTube：播放鍵先描邊畫出來，再收放一下 ──────────────────
       原元件：pathLength 0→1（0.3s）→ scale 1→0.8→1（0.3s）。
       pathLength 用 stroke-dasharray/offset 重現；三角形周長約 17（3-4-5 的兩倍多一點），
       取 20 保證畫得完。 */
    .kz-links a:hover .wf-icon--youtube .youtube-play,
    .footer-social a:hover .wf-icon--youtube .youtube-play,
    .wf-float a:hover .wf-icon--youtube .youtube-play {
        animation: wfYtDraw .3s ease-in-out, wfYtPulse .3s ease-in-out .3s;
    }

    /* ── Podcast（Radio）：中心點放大，內外電波依序擴散 ────────────
       原元件：center 0.5s／wave-inner 0.6s delay 0.1／wave-outer 0.7s delay 0.2。 */
    .kz-links a:hover .wf-icon--radio .center,
    .footer-social a:hover .wf-icon--radio .center,
    .wf-float a:hover .wf-icon--radio .center {
        animation: wfRadioCenter .5s ease-in-out;
    }

    .kz-links a:hover .wf-icon--radio .wave-inner,
    .footer-social a:hover .wf-icon--radio .wave-inner,
    .wf-float a:hover .wf-icon--radio .wave-inner {
        animation: wfRadioInner .6s ease-in-out .1s;
    }

    .kz-links a:hover .wf-icon--radio .wave-outer,
    .footer-social a:hover .wf-icon--radio .wave-outer,
    .wf-float a:hover .wf-icon--radio .wave-outer {
        animation: wfRadioOuter .7s ease-in-out .2s;
    }

    /* ── Instagram：機身、鏡頭各縮放一次，閃光點眨一下 ───────────── */
    .footer-social a:hover .wf-icon--instagram .ig-body { animation: wfIgBody .3s ease-out; }
    .footer-social a:hover .wf-icon--instagram .ig-lens { animation: wfIgLens .25s ease-out; }
    .footer-social a:hover .wf-icon--instagram .ig-dot  { animation: wfIgDot .2s ease-in-out .25s; }

    /* ── 箭頭：整組往前推一下再回來 ────────────────────────────
       ⚠ 外層 .svc-arrow 原本就有一個 translateX(6px) 的 hover 位移；
         這裡動的是 SVG **內部**的群組，兩者疊加剛好是「整顆往前 + 箭頭再推一下」。 */
    .svc-feature:hover .wf-icon--arrow-right .arrow-group,
    .svc-tile:hover .wf-icon--arrow-right .arrow-group {
        animation: wfArrowNudge .5s ease-in-out;
    }
}

@keyframes wfYtDraw {
    from { stroke-dasharray: 20; stroke-dashoffset: 20; }
    to   { stroke-dasharray: 20; stroke-dashoffset: 0; }
}

@keyframes wfYtPulse {
    0%, 100% { scale: 1; }
    50%      { scale: .8; }
}

@keyframes wfRadioCenter {
    0%, 100% { scale: 1; }
    50%      { scale: 1.3; }
}

@keyframes wfRadioInner {
    0%, 100% { scale: 1;    opacity: 1; }
    50%      { scale: 1.15; opacity: .6; }
}

@keyframes wfRadioOuter {
    0%, 100% { scale: 1;   opacity: 1; }
    50%      { scale: 1.2; opacity: .4; }
}

@keyframes wfIgBody {
    0%, 100% { scale: 1; }
    50%      { scale: 1.05; }
}

@keyframes wfIgLens {
    0%, 100% { scale: 1; }
    50%      { scale: 1.2; }
}

@keyframes wfIgDot {
    0%, 100% { opacity: 1; }
    50%      { opacity: 0; }
}

@keyframes wfArrowNudge {
    0%, 100% { translate: 0 0; }
    50%      { translate: 4px 0; }
}

/* 減少動態：圖示一律不做 hover 動畫（純裝飾性的動態，最該被關掉的一種） */
@media (prefers-reduced-motion: reduce) {
    .wf-icon *,
    .wf-icon {
        animation: none !important;
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   Editorial 設計語言（2026-07-31 客戶指定改版）
   ═══════════════════════════════════════════════════════════════════════

   【範圍】版型與網站架構全部保留 —— Hero／服務專區／實體課程／知識專區
        一個區塊都不重排、一段內容都不刪。只重新統一設計語言、比例、留白、
        Typography、Grid、圖片權重與視覺節奏。

   【調性】Editorial／Publishing／Museum／Premium（Calm・Knowledge・Modern）
        要離開的是「心理療癒品牌／插畫品牌／文創商品／課程平台」。
        精神參考 The School of Life —— 吸收精神，不是複製。

   【客戶在同一輪陸續補的指定，逐條對應到下面的編號】
        1  「矩形現代感一點，現在整個網站太可愛了一點」
        2  「把線條感拿掉，以色塊跟色塊去做區隔」
        3  「字體還是用偏細偏乾淨」「不要太粗太大」「要有高級感」
        4  「hero 區域整個頁面坐在一起，不要單純一個日曆」
        5  「讓他一個頁面按切換只翻過去感覺就好」「不要做成生硬的日曆」
        6  「保留數字 day1 day2，但不要是襯字」
        7  「顏色搭配以這四個為搭配」（NUDE／AERIAL／SLATE／BLACK）
        8  「hero 的圖片用知識專區跟實體課程的圖片輪播」

   ⚠ 紀律：本區塊**只新增、不改寫**。上面 4800 行沒有動任何一個字元，
     要換掉的規則一律在這裡以同特異度、更後面的位置覆蓋。
     要回到改版前，整段註解掉即可 —— 客戶要退回時最快的路。
   ⚠ 數值幾乎都不寫在這裡，而在 tokens.css 的同名區塊（SSOT）。
     這裡只負責「哪個角色吃哪一個 token」。
   ═══════════════════════════════════════════════════════════════════════

   【本區塊怎麼讀】2026-07-31 這一天客戶下了約四十輪指定，很多是後面推翻前面。
   本區塊一律「只新增不改寫」，所以**同一個選擇器會出現很多次，以最後一次為準**。
   下面是各區塊的**最終決定**與去哪裡找：

     Hero
       版面   滿版頁面（桌曆道具零件全關）／header 之下／高 clamp(520,78vh,860)
       分欄   左文右圖，分界 = --svc-split，與服務專區同一條垂直線
       左半   只留「標題 → 一段描述 → 按鈕」，頁碼與進度線在左下
       右半   圖片滿版 cover（實體課程素材）
       切換   左右兩側箭頭；效果 = 左右矩形色塊分割（home.js 一律走 fadeMode）
       ⚠ 3D 桌曆翻頁的樣式與程式都還在，改 home.js 的 fadeMode 一行即可復原

     服務專區
       色塊   左黑（出血到視窗左緣）／右淺 #F6F6F7（出血到右緣、上下貼齊區塊）
       主打   左文右圖（白面板 + 深色 CTA + 滿版圖）
       其餘   膚色／藍色**棋盤式**雙色格（不用格線）＋ hover 跟游標浮出小圖
       動態   矩形填色（色塊由左往右長進格子）
       ⚠ 欄分割與主打圖片左緣共用 --svc-inner-split，不要各寫各的

     實體課程
       版面   橫向捲動輪播：標題左／箭頭右上／卡片橫排／進度線／置中按鈕
       卡片   白底方卡，圖片齊卡片上緣滿版；一次露 3.2 張
       ⚠ 卡寬 = (列寬 − 2×gap) / 3.2，筆數變動不必改 CSS

     知識專區
       色塊   左黑（出血）／右淺；分界 18% = grid 第 1 軌
       中欄   圖片滿版（跨留白軌、上下用負邊距貼齊區塊）
       右欄   內容垂直置中；分類=膚色籤、標籤=藍色籤
       清單   hover = 藍色實心矩形（黑字）／選取 = 亮色實心矩形（黑字）

     Footer   NUDE 灰色塊；版權列黑底反白、兩端對齊（＝改版前的樣子）

     全站元件
       按鈕   hover = 填滿 --ink-here、文字換成 --surface-here（背景色呼應反相）
              **不以線條加重切換**
       圓角   --radius-pill / --radius-media 一律 2px
       字距   display .06 / title .10 / body .06 / meta .2（--tracking 預設 = body）
       線條   已整組退場，分段一律靠色塊

   【踩過的坑，改之前先看】
     · 偽元素被兩代設計共用 → 舊規則的 width／transform 會殘留（已發生三次）
     · clip-path 會成為 position:fixed 後代的包含塊並裁切它（服務格的 hover 小圖）
     · will-change 提升合成層會讓祖先的 overflow:hidden 裁切失效
     · grid item 的百分比邊距基準是**自己的 grid area**，不是整個格線
     · 色塊掛在 grid item 上會蓋住隔壁欄（要掛在 grid 容器上）
     · 100vw 含捲軸寬，元素的 100% 不含 —— 兩者混用會差半個捲軸
   ═══════════════════════════════════════════════════════════════════════ */

/* ───────────────────────────────────────────────────────────────────────
   1. Typography — 字距分級（指定 3）
   ───────────────────────────────────────────────────────────────────────
   tokens.css 已把 --tracking 的預設值換成內文級，既有數十條
   `letter-spacing: var(--tracking)` 因此一次到位。這裡只處理不是內文的兩端。
   ⚠ 刻意用「角色清單」而不是逐區塊寫：同一個角色（例如「日期」）在三個區塊
     都要長得一樣，列在同一條規則裡才不會改了一處漏了兩處。 */

/* 大標級 */
.section-title .zh {
    letter-spacing: var(--tracking-display);
}

/* 標題級 */
.svc-feature-name,
.svc-tile-name,
.course-title,
.kz-title,
.kz-list-title,
.footer-col-title,
.footer-logo,
.btn-pill,
.tag-pill,
.footer-submit,
.footer-input {
    letter-spacing: var(--tracking-title);
}

/* 小標級 —— 原本的 .2em 寬字距全部收攏到這一級，成為「標籤」的識別 */
.section-title .en,
.svc-feature-cat,
.svc-tile-sub,
.svc-tile-no,
.course-date,
.course-place,
.kz-list-no,
.footer-logo-en {
    letter-spacing: var(--tracking-meta);
}


/* ── 標題字重與字級：偏細、偏乾淨、不放大（指定 3）────────────────────
   改版前標題吃 --fw-normal（米白區 450／深色帶 410），偏壯。
   客戶要的是「偏細偏乾淨」「不要太粗太大」，所以壓到與內文同級 ——
   層級改由**字級與留白**撐，不由字重撐。這也是高級感的來源。 */
.section-title .zh,
.svc-feature-name,
.svc-tile-name,
.course-title,
.kz-title,
.footer-col-title {
    font-weight: var(--fw-heading);
}

.section-title .zh {
    font-size: var(--text-section);
    line-height: var(--lh-section);
}

/* 英文小標退回真正的小標（16px → 13px）：它是標籤，不是第二行標題。 */
.section-title .en {
    font-size: var(--text-meta);
    line-height: var(--lh-meta);
    margin-bottom: 1.125rem;
}

/* 主打服務名只比卡片標題高一階（26px），不做成第二個區塊主標 */
.svc-feature-name {
    font-size: clamp(1.375rem, 1.5vw, 1.625rem);
    line-height: 1.5;
}

.kz-title  { line-height: 1.6; }
.kz-desc,
.kz-body   { line-height: var(--lh-body); }

/* ───────────────────────────────────────────────────────────────────────
   2. 色塊分段（指定 2、7）— 線條系統整組退場
   ───────────────────────────────────────────────────────────────────────
   客戶：「把線條感拿掉，以色塊跟色塊去做區隔。」
   分段不再靠 1px 線，改由底色直接換塊（客戶選定「交錯式：四段四色」）：

       Hero        Paper      #F9F6F2
       服務專區     BLACK      #000000
       實體課程     NUDE       #F2E3D0   ← 由深轉淺，最大的一次換塊
       知識專區     Paper      #F9F6F2
       Footer      NUDE       #F2E3D0
       版權列       BLACK      #000000

   ⚠ 沒有新增色票：四個色塊全部來自既有的 --background／--muted／--surface-dark，
     值已在 tokens.css 依客戶的四色參考稿換過（SSOT）。
   ⚠ 區塊之間的留白改用「色帶交界」那一級（--section-seam-band）：
     原本服務→課程是同一條深色帶的**內部接縫**（--section-seam，較窄），
     現在兩者是不同顏色的兩個塊，各自需要完整的內距才撐得住自己的邊界。 */

/* ── 2a. 所有分隔線退場 ──────────────────────────────────────────────
   ⚠ 一律用 display:none／border:0，**標記完全沒有刪** ——
     客戶若要把線加回來，把這一段註解掉就整組復原。 */
.svc-layout::before,          /* 服務專區：左右欄之間的垂直線 */
.svc-tile:nth-child(even)::before,   /* 服務網格：欄間垂直線 */
.course-cell::before,         /* 實體課程：欄間垂直線 */
.kz-divider,                  /* 知識專區：左欄與大圖之間的垂直線 */
.kz-rule {                    /* 知識專區：文章標題下的細線 */
    display: none;
}

.svc-feature       { border-bottom: 0; }
.svc-feature-info  { border-top: 0; }
.course-head       { border-bottom: 0; }
.footer-main       { border-top: 0; }
.footer-col        { border-left: 0; }
.kz-list-btn       { border-bottom: 0; }

/* 清單列失去底線之後，用**色塊**接手（＝客戶要的區隔方式）：
   hover／選取中整列鋪一層 NUDE，選取的那一列再把文字轉黑。 */
.kz-list-btn {
    padding: .875rem 1rem;
    margin-inline: -1rem;
    transition: background var(--transition), color var(--transition);
}

.kz-list-btn:hover,
.kz-list-btn:focus-visible {
}

.kz-list-btn[aria-selected="true"] {
}

/* ── 2b. 實體課程：深色塊 → NUDE 淺色塊 ───────────────────────────────
   ⚠ 這一段的標記帶著 .on-dark（它原本與服務專區共用同一條深色帶）。
     **不動標記**，在這裡把 .on-dark 衍生出來的每一條配色覆寫回淺底版本 ——
     這樣「服務專區仍是深色塊」與「課程改成淺色塊」可以並存，
     而且客戶若要換回深色，註解掉這一段就完全復原。
   ⚠ 特異度都算過：.on-dark 那組是 (0,1,0)～(0,4,0)，
     這裡一律用 .course-section 前綴補到同級或更高，且位置在後面。 */
.course-section {
    /* 淺底的字重回到米白區那一組（.on-dark 的 310/410 是深底的光暈補償） */
    --fw-light:   350;
    --fw-normal:  450;
    --fw-heading: 350;

    padding-block: var(--section-seam-band);
    background: hsl(var(--muted));
    color: hsl(var(--foreground));
}

.course-section .section-title .zh { color: hsl(var(--heading)); }
.course-section .section-title .en { color: hsl(var(--slate)); }
.course-section .course-title      { color: hsl(var(--heading)); }
.course-section .course-date       { color: hsl(var(--slate)); }
.course-section .course-place      { color: hsl(var(--foreground) / .55); }
.course-section .course-thumb      { background: hsl(var(--foreground) / .05); }
.course-section .course-thumb::after { background: hsl(var(--foreground) / .05); }
.course-section .course-link:focus-visible { outline-color: hsl(var(--foreground)); }

/* 淺底上的膠囊按鈕整組反相回來 */
.course-section .btn-pill                    { border-color: hsl(var(--foreground) / .45); color: hsl(var(--foreground)); }
.course-section .btn-pill::after             { background: hsl(var(--foreground)); }
.course-section .btn-pill:hover,
.course-section .btn-pill:focus-visible      { border-color: hsl(var(--foreground)); }
.course-section .btn-pill:hover::after,
.course-section .btn-pill:focus-visible::after { color: hsl(var(--muted)); }

/* hover 的重點色：深色塊上用 AERIAL（客戶指定），淺色塊上必須改回黑 ——
   AERIAL 壓在 NUDE 上只有 1.4:1，等於 hover 之後字消失。
   ⚠ 特異度 (0,5,0) 蓋過 .on-dark 那條 (0,4,0)。 */
@media (hover: hover) {
    .course-section.on-dark .course-link:hover .course-title { color: hsl(var(--foreground) / .55); }
}

.course-section.on-dark .course-link:focus-visible .course-title { color: hsl(var(--foreground) / .55); }

/* ── 2c. 服務專區與 Footer 的色塊 ──────────────────────────────────── */
.service-section {
    padding-bottom: var(--section-seam-band);   /* 原本是帶內接縫，現在是塊的邊界 */
}

.site-footer {
    background: hsl(var(--muted));      /* NUDE 色塊 */
    --footer-line: transparent;
}

/* 訂閱輸入框失去外框之後改用**紙白色塊**當底 —— 一樣是「以色塊做區隔」 */
.footer-input {
    border-color: transparent;
    background: hsl(var(--background));
}

.footer-input:focus {
    border-color: transparent;
    box-shadow: inset 0 0 0 1px hsl(var(--foreground) / .45);
}

/* ── 2d. 色帶轉場：改接在「深 → 淺」那個最大的交界上 ─────────────────
   ⚠ 標記的位置在 home.php 一併調整（服務專區與實體課程之間）——
     那裡才是全站色差最大的一次換塊，轉場動畫用在那裡才有意義。
     課程 → 知識（NUDE → Paper）色差很小，直接硬接就是乾淨的塊邊界。 */
.band-shift--light {
    --band-from: hsl(var(--surface-dark));
    --band-to:   hsl(var(--muted));
}

/* ───────────────────────────────────────────────────────────────────────
   3. 顏色用途（指定 7）
   ───────────────────────────────────────────────────────────────────────
   四色各司其職，不混用：
     BLACK   主要文字、深色塊
     SLATE   次要文字、淺底上的標籤
     NUDE    淺色塊、深底上的標籤色、柔性色塊按鈕
     AERIAL  **唯一的強調色**：hover／active／CTA／重點文字
   ⚠ NUDE 很亮，壓在紙白上幾乎看不見（1.05:1）——
     所以原本用卡其的**淺底**位置全部改指向 SLATE。 */
.kz-intro .section-title .en,
.kz-list-no,
.kz-list-title {
    color: hsl(var(--slate));
}

.kz-list-btn[aria-selected="true"] .kz-list-no,
.kz-list-btn[aria-selected="true"] .kz-list-title,
.kz-list-btn:hover .kz-list-no,
.kz-list-btn:hover .kz-list-title,
.kz-list-btn:focus-visible .kz-list-no,
.kz-list-btn:focus-visible .kz-list-title {
    color: hsl(var(--heading));
}

/* 深色塊上的**內文**改中性亮灰，NUDE 只留給標籤（日期／編號／分類／英文小標）。
   改版前深色帶的所有次要文字都是卡其，一整段看下來輔色的面積比底色還大。 */
.svc-intro-desc,
.svc-feature-desc {
    color: hsl(var(--surface-dark-foreground) / .70);
}

.svc-tile-sub {
    color: hsl(var(--surface-dark-foreground) / .55);
}

/* Footer 訂閱鈕是全站最明確的一顆 CTA —— AERIAL 用在這裡（黑字 10.5:1） */
.footer-submit {
    background: hsl(var(--accent-blue));
    color: hsl(var(--heading));
}

.footer-submit:hover,
.footer-submit:focus-visible {
    background: hsl(var(--foreground));
    color: hsl(var(--background));
}

/* Hero 進度線：卡其換成 NUDE 之後在紙白上看不見，改用極淡的黑 */
/* 2026-08-10 客戶指定「數字條線條黑跟膚色做搭配，不要搭深灰」：
   深灰那一輪收回，軌道改回 --nude 膚色（與頁碼的 .cal-pageno-total 同一個
   值），已讀段落維持 .cal-progress-bar 的黑不動——黑＋膚色兩色一組。 */

/* ───────────────────────────────────────────────────────────────────────
   4. 矩形化（指定 1）
   ───────────────────────────────────────────────────────────────────────
   tokens.css 已把 --radius-pill 由 30px、--radius-media 由 8px 一起收成 2px，
   所有引用 token 的元件（按鈕、標籤、輸入框、搜尋鈕、圖片版位）自動變矩形。
   這裡只補三個沒有走 token 的地方。 */

/* 按鈕右側那顆徽章：圓 → 方（機制不變，靜置小點、hover 長成帶箭頭的方塊） */
.btn-pill::after { border-radius: 0; }

/* 按鈕字級回到內文級（原本吃 --text-sub 18px，在新的細字語言下偏壯） */
.btn-pill { font-size: var(--text-body); }

/* 右下角浮動社群鈕：圓 → 方角矩形，陰影再壓一階 */
.is-designed .wf-float a {
    border-radius: var(--radius-media);
    box-shadow: 0 1px 6px hsl(var(--foreground) / .10);
}

/* 下拉選單：直角 + 幾乎不投影，邊界交給極淡的框（出版品的做法） */
@media (min-width: 992px) {
    .site-header .site-nav .dropdown-menu {
        border-color: hsl(var(--foreground) / .12);
        border-radius: var(--radius-media);
        box-shadow: 0 6px 18px hsl(var(--foreground) / .07);
    }
}

/* ───────────────────────────────────────────────────────────────────────
   5. 微互動克制（客戶：opacity／translateY／scale 1.02 即可）
   ─────────────────────────────────────────────────────────────────────── */
@media (hover: hover) {
    .svc-feature:hover .svc-media img,
    .svc-tile:hover .svc-media img,
    .course-link:hover .course-thumb img { transform: scale(var(--hover-zoom)); }

    .kz-media-item.is-active a:hover img { transform: scale(var(--hover-zoom)); }

    .svc-feature:hover .svc-arrow,
    .svc-tile:hover .svc-arrow { transform: translateX(var(--hover-nudge)); }
}

.svc-feature:focus-visible .svc-media img,
.svc-tile:focus-visible .svc-media img,
.course-link:focus-visible .course-thumb img { transform: scale(var(--hover-zoom)); }

.svc-feature:focus-visible .svc-arrow,
.svc-tile:focus-visible .svc-arrow { transform: translateX(var(--hover-nudge)); }

/* hover 蓋在圖片上的暗罩由 16% 收到 5% —— 感覺得到，但看不出是一層灰 */
.svc-media::after,
.course-thumb::after { background: hsl(var(--brand) / .05); }

/* ───────────────────────────────────────────────────────────────────────
   6. 圖片權重與留白（客戶指定四、八）
   ───────────────────────────────────────────────────────────────────────
   ⚠ 實體課程的 16:9 不能動：那三張是客戶自己的主視覺，圖上印著標題文字，
     比例一改 object-fit:cover 就會把字裁掉。 */
.svc-feature-media { }   /* 原 16:8 → 矮一階 */
.svc-tile-media    { }   /* 原 16:10 → 矮一階 */

/* 區塊**內部**的呼吸放鬆一階。不動 --section-y／--section-seam 那幾個區塊級
   token —— 客戶先前反饋過「區塊之間空白太多」，那一輪的收斂結果要保住。 */
.svc-intro .section-title { margin-bottom: 2rem; }
.svc-main                 { }
.svc-feature-info         { }
.svc-tile-info            { }
.svc-grid                 { }

.course-head              { padding-bottom: 0; }
.course-grid              { margin-top: 3.5rem; }
.course-date              { margin-top: 1.25rem; }

.kz-intro .section-title  { margin-bottom: 2.5rem; }
.kz-title                 { margin-top: 1.75rem; }
.kz-desc                  { margin-top: 1.5rem; }
.kz-links                 { margin-top: 2.25rem; }

/* ═══════════════════════════════════════════════════════════════════════
   7. Hero — 從「桌曆道具」變成「一個頁面」（指定 4、5、6、8）
   ═══════════════════════════════════════════════════════════════════════

   客戶：「hero 區域整個頁面坐在一起，不要單純一個日曆。」
        「讓他一個頁面按切換只翻過去感覺就好了，不要做成生硬的日曆。」

   做法：**DOM 一行都沒改**，只把「道具」的零件關掉、讓紙張長到滿版：
     關掉  活頁金屬圈、打孔、紙疊厚度、接觸陰影、多層投影、書脊摺痕、
           俯視傾角、常駐飄動、紙張受光漸層 —— 這些全部是「一本桌曆」的證據
     留下  攤開的左文右圖版型、翻頁、進度、開場動畫
     長大  .cal-frame 由「置中的一本桌曆」變成 100vw × 100vh 的一個頁面

   ⚠ 為什麼用關閉而不是刪除：那些零件的樣式與 home.js 的 3D 場景是綁在一起的
     （見上方數百行的事故紀錄）。關閉是可逆的，刪除不是。
   ⚠ 翻頁也一起簡化（指定 5）：紙張捲曲（page curl）原本切成四段、
     越靠自由邊彎越多。客戶要的是「只翻過去的感覺」，所以只留第一段並讓它
     吃滿整頁 —— 剩下的就是一張平整的頁面繞上緣翻過去，乾淨、不生硬。 */

/* ── 7a. 道具零件關閉 ─────────────────────────────────────────────── */

/* 中央書脊摺痕 —— 那是「一本攤開的實體書」的證據，頁面化之後不需要 */

/* 紙 → 頁面：直角、無投影、底色就是頁面底（Paper） */


/* 左頁的卡其色塊（原本用來假裝紙張）一併收掉 —— 左半就是乾淨的 Paper */

/* 俯視傾角與常駐飄動：都是「一本放在桌上的東西」的訊號 */


/* ── 7b. 翻頁簡化：一張平整的頁面翻過去（指定 5）────────────────────
   ⚠ 第一段吃滿整頁：--seg-f（自己佔全頁多少）設成 1、--seg-o（起點）設成 0，
     .cal-face-body 的算式（top = -seg-o/seg-f × 100%、height = 100%/seg-f）
     就會還原成「整頁、不位移」。這是既有算式自己支援的退化情況，
     不必改任何一行既有規則。
   ⚠ 第 2–4 段整組關掉，捲曲、自由邊厚度、捲到背面因此都不再發生。 */


/* 翻頁時的明暗收斂：原本上緣壓到 50% 黑，那是「厚紙折過去」的量感；
   頁面化之後只要一點點方向感就夠（客戶：不要生硬）。 */

/* ── 7c. DAY 數字不用襯字（指定 6）─────────────────────────────────
   客戶：「保留數字 day1 day2，但不要是襯字。」
   數字全部保留，字體由 Cormorant Garamond（Serif）換成 Julius Sans One ——
   與全站英文小標同一套，乾淨、幾何、現代。
   ⚠ font-variant-numeric 那兩行是 Garamond 舊體數字的補救，換成無襯線後
     沒有作用但也無害，留著不動（將來若換回 Serif 仍需要）。 */


/* ── 7e. 游標提示：圓 → 方（指定 1，整站矩形化的一部分）───────────── */

/* ───────────────────────────────────────────────────────────────────────
   8. Header／Footer — 跟上新的字距
   ─────────────────────────────────────────────────────────────────────── */
@media (min-width: 992px) {
    /* 導覽項的字距補償。原本寫死 -.2em（＝當時的 --tracking），字距換了之後
       不跟著改，每一項會被多往左拉，整排間距就不均。
       ⚠ 改成 calc(token × -1)：日後再調字距這裡自動跟上（不再有第二個權威來源）。 */
    .site-header .site-nav > a,
    .site-header .site-nav > .dropdown {
        margin-right: calc(var(--tracking-title) * -1);
    }

    .site-header .site-nav > a:not(.site-search):hover,
    .site-header .site-nav > a:not(.site-search):focus-visible,
    .site-header .site-nav > .dropdown > a:hover,
    .site-header .site-nav > .dropdown > a:focus-visible {
        background-size: calc(100% - var(--tracking-title)) 1px;
    }

    .site-header .site-search .site-search-label {
        letter-spacing: var(--tracking-title);
        margin-right: calc(var(--tracking-title) * -1);
    }
}

/* ───────────────────────────────────────────────────────────────────────
   9. RWD
   ⚠ 必須放在本區塊最後：上面那些非 media 的宣告會蓋掉檔案前段的 media query
     （media query 不加特異度，只看順序）。
   ─────────────────────────────────────────────────────────────────────── */
@media (max-width: 1023.98px) {
    .svc-main    { gap: 3.25rem; }
    .svc-grid    { row-gap: 3rem; }
    .course-grid { row-gap: 3rem; }
}

@media (max-width: 1023.98px) {
    .svc-main    { }
    .svc-grid    { row-gap: 2.5rem; }
    .course-grid { margin-top: 2.5rem; row-gap: 2.5rem; }

    .svc-feature-info { margin-top: 1.25rem; }
    .svc-tile-info    { margin-top: 1rem; }
    .course-date      { margin-top: 1rem; }

    /* 窄版 Hero 維持既有的直式版型（上圖下文），只跟著關掉道具零件 ——
       圖片在這裡是 aspect-ratio 16/9 的固定版位，維持 cover 不變。 */
}

/* ── 7f. Hero 版面：白底文字 ｜ 色塊上的圖片（2026-07-31 客戶提供參考圖）────
   客戶：「hero 用圖片壓白底搭文字的方式呈現。」

   參考圖的結構就是本站 Hero 現有的左右兩半，只差在**兩半各自是一個色塊**：
       左半  Paper #F9F6F2   —— 純淨的白底，只放文字（標題／介紹／CTA）
       右半  NUDE  #F2E3D0   —— 暖色場，圖片壓在上面、四周留白
   ⚠ 這同時把「以色塊跟色塊去做區隔」帶進 Hero 內部：兩半之間不畫線，
     靠底色自己分開，與全站的分段語言一致。
   ⚠ 圖片走 object-fit: contain（見 7d）—— Hero 現在輪播的是知識專區與實體課程
     的素材，兩種比例、而且圖上都印著標題文字。壓在色場上、四周留白，
     圖完整、也不再主導畫面（客戶指定四「圖片權重降低」）。 */

/* 翻開前那張紙也要是同一組色塊，否則開場翻開的瞬間會從純白跳成雙色 */

/* ═══════════════════════════════════════════════════════════════════════
   近期課程 — 矩形小卡（2026-07-31 客戶提供參考圖）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「近期課程以這種矩形小卡的方式呈現。」

   參考圖的卡片結構：白底方卡 → 圖片齊卡片上緣滿版 → 內距裡依序是
   分類小標（大寫寬字距）／標題／說明／一個「→」的前往提示。

   ⚠ 標記完全沒有動 —— 既有 DOM 本來就是
       .course-cell > .course-link > [.course-thumb, .course-date, .course-title, .course-place]
     順序與參考圖一模一樣，只差在「沒有卡片這個容器」。
     所以這裡把 .course-link 本身變成卡片：底色、內距、等高，圖片維持齊上緣。
   ⚠ 這同時解掉一個實際問題：課程主視覺是**米色底的插畫**，直接放在
     NUDE #F2E3D0 的區塊上時邊界幾乎消失（實測很糊）。
     卡片是紙白色塊，插畫壓在紙白上就有明確的邊 ——
     而且卡片與區塊底之間是「色塊對色塊」，正好是客戶要的分段方式。
   ⚠ 卡片**不畫外框**：客戶已指定「把線條感拿掉」，白色塊本身就是邊界。
   ⚠ 沒有加參考圖上的「View」字樣 —— 那是新增文案，而客戶 2026-07-30 已明確
     決定「每張卡片本身即可點擊，不再放各自的按鈕」。這裡只用站上既有的
     「→」語言（與服務專區的 .svc-arrow 同一個符號）當作前往提示。 */

/* 卡片等高：三張卡的下緣切齊，標題長度不同也不會參差 */
.course-cell {
    display: flex;
}

.course-link {
    flex: 1;
    display: flex;
    flex-direction: column;
    background: hsl(var(--background));
    transition: background var(--transition);
}

/* 圖片齊卡片上緣滿版（參考圖）；內距只給文字。
   ⚠ 內距不能掛在 .course-link 上，否則圖片會跟著內縮，變成「白框裡的一張小圖」。 */
.course-thumb {
    border-radius: 0;
    background: hsl(var(--foreground) / .04);
}

/* 分類小標的位置由日期承接（本站這一格是開課日期，角色相同） */
.course-date {
    margin: 0;
    padding: 1.5rem 1.5rem 0;
    border-top: 0;
}

.course-title {
    padding-inline: 1.5rem;
}

/* 地點是卡片的最後一段文字；下方留給「→」提示 */
.course-place {
    padding: 0 1.5rem;
    margin-bottom: 1.25rem;
}

/* 前往提示：參考圖右下的箭頭。純裝飾（連結語意由整張卡承擔），不朗讀。 */
.course-link::after {
    content: "→";
    display: block;
    margin-top: auto;              /* 貼在卡片底部，卡片等高時三張的箭頭切齊 */
    padding: 0 1.5rem 1.5rem;
    font-family: var(--font-en);
    font-size: 1.125rem;
    line-height: 1;
    letter-spacing: 0;
    color: hsl(var(--foreground) / .55);
    transition: transform var(--dur-hover) var(--ease-out), color var(--transition);
}

@media (hover: hover) {
    .course-link:hover::after {
        transform: translateX(var(--hover-nudge));
        color: hsl(var(--foreground));
    }
}

.course-link:focus-visible::after {
    transform: translateX(var(--hover-nudge));
    color: hsl(var(--foreground));
}

/* 卡片之間改用「卡距」而不是原本的欄距 —— 小卡的節奏比欄式版面緊一階 */
.course-grid {
    --course-gap: 1.75rem;
    row-gap: 1.75rem;
}

/* 卡片內的字級略收一階：整張卡只有 400px 寬，24px 標題會佔掉大半張卡 */
.course-title {
    font-size: var(--text-sub);        /* 20px → 與服務網格的服務名同一級 */
    line-height: var(--lh-sub);
    margin-top: .375rem;
}

.course-place {
    margin-top: .5rem;
}

@media (prefers-reduced-motion: reduce) {
    .course-link::after { transition: color var(--transition); }
    .course-link:hover::after,
    .course-link:focus-visible::after { transform: none; }
}

/* ═══════════════════════════════════════════════════════════════════════
   Header 自成一條白色橫條 ＋ Hero 降低高度（2026-07-31 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「hero 區域高不用那麼高，header 加上白色背景自己一條。」

   改版前：header 停在 Hero 上時是**透明**的，Hero 的頁面從畫面最上緣開始，
          導覽浮在頁面上；Hero 高度是滿版 100vh。
   改版後：header 是一條**純白**的橫條，與底下灰白的頁面明確分開（自己一條）；
          Hero 的頁面接在它下面，高度收成約三分之二個螢幕。

   ⚠ 白色用 --brand-foreground（#FFFFFF，色票裡本來就有的白），
     不是再新增一個色 —— 頁面底已經壓成灰白 #F5F5F7，純白壓在上面
     自然就是「一條」，不必再畫線（客戶已指定拿掉線條感）。
   ⚠ header 仍然是 fixed，所以 Hero 要自己讓出 --header-h 的上內距，
     否則頁面會鑽到白條底下。 */
@media (min-width: 992px) {
    /* 捲動前後都是同一條白 —— 不再有「停在 Hero 上就變透明」的狀態切換 */
    .is-designed .site-header,
    .is-designed:has(.hero) .site-header:not(.is-scrolled) {
        background: hsl(var(--brand-foreground));
        -webkit-backdrop-filter: none;
        backdrop-filter: none;
        border-bottom-color: transparent;
    }

    /* Hero 讓開白條，並收掉滿版高度 */
    .hero {
        padding-top: var(--header-h);
    }

    .hero-inner {
        min-height: 0;
    }

    /* 約三分之二個螢幕：上限 720 讓大螢幕不會又長回滿版，
       下限 460 保住左頁文字（頁眉＋標題＋介紹＋CTA 約需 320px）。 */

    /* 頁面不再需要為 fixed header 預留上緣，內距整組收一階 */

}

/* ⚠ 2026-08-14 排查記錄：上面這條 `.hero { padding-top: var(--header-h); }`
   是 2026-07-31「Header 自成一條白色橫條」那個舊版型留下的——那時候
   header 是實色白底、不透明，Hero 必須自己讓出高度才不會被蓋住。今天
   的「首頁 Hero 版型修改指令」改版把 header 改回透明疊在滿版照片上（跟
   本站這個 session 從頭到尾的 header 方向一致），完全不需要讓這段內距，
   但這條 07-31 的舊規則從沒被拿掉或蓋掉，「圖片上方留白 56px」的成因
   就是它——本檔開頭 .hero 那條基底規則（≈525 行）已經照這次客戶指示
   移除了 padding-top，兩條選擇器特異度相同（都是單一 class），這一條
   排在檔案後面，照 cascade 規則反而贏，把已經拿掉的內距悄悄加回來。
   ⚠ 不直接刪掉舊規則（本檔慣例：只加不改，07-31 那段留著當歷史紀錄），
     改成另外疊一條特異度更高的規則（多一個 class）明確歸零，不靠
     `!important`，之後這條 07-31 的舊規則要是被人拿掉，這裡也不受影響
     （0px 仍然是 0px）。 */
.is-designed.is-designed .hero {
    padding-top: 0;
}

/* ═══════════════════════════════════════════════════════════════════════
   Header 改回透明覆蓋樣式（2026-08-14 客戶新指令）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「Header 初始狀態不要有白色實心背景...Hero Banner、課程圖片或頁面
   品牌色應從畫面最上方開始，並延伸到 Header 背後」，且要求全站一致，不是
   只有首頁。離開頁首後改收合成右上角方形選單鈕（見本檔後段「Header 收合
   成方形選單鈕」新區塊），不是繼續顯示這條白色橫條。

   這條疊在上面「Header 自成一條白色橫條」（2026-07-31）規則之後，用新狀態
   .is-compact 決定顯示哪一種：
     html:not(.is-compact) → 疊在頁首色帶上（透明）
     html.is-compact       → 收合（見後段新區塊）

   ⚠ 07-31 那條規則沒有刪，理由跟本檔其他「客戶改主意」的段落一樣：只加不
     改、保留歷史。這裡用 !important 讓新規則穩定蓋過去，不必疊 class 湊
     特異度——日後客戶若要求恢復純色橫條，把這整段刪掉，07-31 那條就會自動
     重新生效，不必回頭改任何東西。
   ⚠ 不再用 :has(.hero) 限定頁面類型：07-30 當初加這個限制是因為「沒有 Hero
     的 18 頁」若透明會直接壓在內容上（見 07-30 那段的完整理由）。現在全站
     19 頁（.is-designed）每一頁頁首都是滿版色帶（.hero／.cx-hero／.cd-hero／
     .page-hero／.kd-hero／.sv-hero／.simpla-open／各頁自己的 .band…），
     透明疊上去不會有「header 下面沒東西」的問題，直接對 .is-designed 全部
     生效，不必逐頁列白名單、也不用擔心之後新增頁面忘記加進清單。
   ⚠ 刻意不沿用 .is-scrolled（scrollY > 14，且目前對已切版頁面是視覺
     no-op，見「Header 高度改成 56px」那段）——新的收合時機由 .is-compact
     獨立判斷（IntersectionObserver 觀察頁首色帶是否還在畫面上，不是單純
     scrollY 門檻，見 home.js 對應的新函式），兩者故意脫鉤。 */
/* ⚠ 2026-08-25 選擇器排查記錄：客戶「hero注意不要有header疊在圖片上的
   問題」——追出這條規則的選擇器本身有誤，跟上面「刻意不沿用.is-scrolled
   ...新的收合時機由.is-compact獨立判斷」的原始設計意圖對不上。
   本段註解自己在 ≈4434 寫的邏輯是「html:not(.is-compact) → 疊在頁首
   色帶上（透明）」——`:not(.is-compact)` 該檢查的是 `<html>` 有沒有這個
   class（.is-compact 掛在 html 上，見 home.js），但實際寫的選擇器是
   `.site-header:not(.is-compact)`——檢查的是 `.site-header` 元素自己
   有沒有 `.is-compact` 這個 class。`.site-header` 從來不會被掛上這個
   class（它只掛在 `<html>`），所以 `:not(.is-compact)` 在這裡永遠成立、
   形同沒有條件，這條帶 `!important` 的規則因此不分捲動狀態、不分
   compact 與否，全站 19 頁在 ≥992px 一律透明——這正是客戶這次截圖的
   成因，而且範圍比截圖看到的更大：不只 Hero 頁，全站每一頁都是。
   ⚠ 這條規則的立意（「離開頁首後不要透明，收合時機交給 .is-compact
   判斷」）現在由本檔 ≈8570（`html:has(.hero).is-compact:not(.is-scrolled)
   .site-header`，2026-08-25 同一輪修正）接手，範圍更精確（只在首頁
   Hero、捲動超過 14px 前才透明，其餘 18 頁維持一律實色，避免重蹈
   07-30 那次「非 Hero 頁透明會壓在導覽底下」的舊坑）。這裡只修正選擇器
   本身指錯元素的 bug（.site-header→html），不改規則的觸發邏輯——
   `.is-compact` 現在恆為真（2026-08-17 決定），修正後這條規則會變成
   永遠不生效，屬於預期內：它原本想做的事已經由上面那條更精確的規則
   接手，不是這裡被拿掉了什麼行為。 */
@media (min-width: 992px) {
    html:not(.is-compact) .is-designed .site-header {
        background: transparent !important;
        -webkit-backdrop-filter: none !important;
        backdrop-filter: none !important;
        border-bottom-color: transparent !important;
    }
}

@media (max-width: 991.98px) {
    /* ⚠ ≤991.98px 沒有「.is-designed .site-header」這條規則可以疊——這個寬度
       走的是 wireframe.css 的 .wf-header{position:sticky;background:#fff}
       （無 !important、全站無條件套用，見 wireframe.css:70），不是上面
       ≥992px 那組。要讓手機也透明疊在頁首上，得直接蓋過那條。
       ⚠ 特異度用兩個 class（.is-designed .site-header）搭 !important，比
         wireframe.css 那條單一 class 高，且 style.css 本來就載在
         wireframe.css 之後，不需要再疊更多 class。
       ⚠ 2026-08-25 同一輪修正選擇器指錯元素的 bug（詳細原因見上面
         ≥992px 那條同款規則的完整說明）：`:not(.is-compact)` 改成檢查
         `html` 而不是 `.site-header` 自己。 */
    html:not(.is-compact) .is-designed .site-header {
        background: transparent !important;
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   知識專區 — 左欄延續上一區的灰色塊（2026-07-31 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「知識專區左邊導覽頁還是上區的灰色，與右邊切割。」

   做法：左欄（區塊主標 ＋ 查看更多 ＋ 01/02/03 清單）鋪一塊 --muted 灰，
        往左**滿版出血**到視窗邊緣、往上下吃滿整個區塊的內距 ——
        所以它與上一區（實體課程）的灰是同一塊、同一個色，看起來是連續下來的；
        右邊的大圖與文章則留在較亮的頁面底上，兩者以一條硬邊切開。
   ⚠ 這正是全站的分段語言（以色塊對色塊做區隔），與 Hero 的「左文右圖兩色塊」
     是同一套規則在不同區塊的應用。
   ⚠ 用 ::before 出血而不是改 grid：版型（五軌）完全不動，
     只是在左欄底下墊一塊背景。
   ⚠ 出血靠 left:-100vw ＋ 區塊 overflow:hidden ——
     不會產生水平捲軸（知識專區沒有任何需要溢出的內容）。 */
/* ⚠ overflow 用 clip 不用 hidden：hidden 會建立捲動容器，
   任何 position:sticky 的後代都會失效（服務專區那一段的左欄就是 sticky，
   兩區用同一套寫法才不會有人照抄到那邊踩雷）。clip 只裁切、不建立捲動容器。 */
.knowledge-section {
    overflow-x: clip;
}

/* ⚠ isolation:isolate 不能省。沒有它，.kz-intro 不是自己的堆疊環境，
   下面那個 z-index:-1 的 ::before 會退到**整份文件**的最底層 ——
   結果是被 .knowledge-section 自己的背景整片蓋掉，色塊完全看不見（實測）。
   isolate 把 -1 侷限在 .kz-intro 內部：色塊在左欄文字之下、在區塊底色之上。 */
.kz-intro {
    position: relative;
    isolation: isolate;
}

.kz-intro::before {
    content: "";
    position: absolute;
    z-index: -1;
    /* 上下吃滿區塊自己的內距（見 .knowledge-section 的 padding-block） */
    top: calc(var(--section-seam-band) * -1);
    bottom: calc(var(--section-seam-end) * -1);
    /* 往左出血到**視窗左緣**。
       ⚠ 算式：版面是置中的，所以 .kz-intro 所在的格線到視窗左緣的距離
         ＝（視窗寬 − 格線寬）÷ 2。用 calc((100% - 100vw) / 2) 一次到位，
         不必知道 --section-x 或容器上限是多少，換斷點也自動對。
       ⚠ 不用 -100vw：那會硬生生撐出一整個視窗寬的溢出，得靠 overflow 收拾；
         這個算式剛好停在視窗邊緣。 */
    left: calc((100% - 100vw) / 2);
    /* 右緣落在左欄與大圖之間那條留白軌的**正中央**：
       grid 是 18fr | 6fr | 41fr | 4fr | 31fr，留白軌 ＝ 左欄的 1/3，
       所以往右延伸半條 ＝ 左欄寬的 1/6。 */
}

/* 清單的 hover／選取色塊要換一階 —— 底色本身已經是 --muted，
   再用同一個灰等於完全看不見。改用較亮的頁面底當「被選中」的色塊。 */
.kz-list-btn:hover,
.kz-list-btn:focus-visible {
    background: hsl(var(--background) / .55);
}

.kz-list-btn[aria-selected="true"] {
    background: hsl(var(--background));
}

/* 窄幅收成單欄時，左欄不再是「一側的色塊」——
   出血色塊會變成蓋住整個區塊上半部的一大片，收掉。 */
@media (max-width: 1023.98px) {
    .kz-intro::before { display: none; }

    .kz-list-btn:hover,
    .kz-list-btn:focus-visible { background: hsl(var(--muted) / .6); }

    .kz-list-btn[aria-selected="true"] { background: hsl(var(--muted)); }
}

/* Hero 左右欄不等寬（2026-07-31 客戶指定「不用左右欄位等比例，讓右邊圖片區塊橫寬一點」）。
   ⚠ 只在桌機改：窄幅的 .cal-content 是「上圖下文」的單欄（見上方 Hero RWD），
     在那裡宣告兩欄會把版型打壞。
   ⚠ 42/58 是從左欄的行長回推的下限：左欄要放得下
     「格線內距（--hero-pad-x + 4.5rem）＋ 標題行長上限 270px ＋ 右內距」，
     1200px 視窗時 42% ＝ 504px，剛好留 300px 給行長。再往下壓標題就會多折一行。 */

/* ═══════════════════════════════════════════════════════════════════════
   服務專區 — 左黑右灰，兩塊切開（2026-07-31 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「服務專區左邊是黑右邊是灰，也是分開來。」

   與知識專區同一套規則、顏色相反：
       左欄（區塊主標 ／ 介紹 ／ 查看全部服務）  BLACK #000000  出血到視窗左緣
       右欄（主打服務 ＋ 服務網格）              灰    #E3E3E8  出血到視窗右緣
   交界落在左右欄之間那條留白軌的正中央，一條硬邊切開 —— 沒有線，就是兩塊色。

   ⚠ 版型（26% ＋ 4% ＋ 70% 的兩欄 grid）一個字都沒動，
     兩塊底色是墊在 .svc-layout 底下的兩個 ::before／::after。
   ⚠ 右欄從深底變成淺底，所以**右欄裡的文字要整組翻面**（見下方）——
     左欄仍然在黑底上，維持 .on-dark 原本的配色，不必動。
   ⚠ .svc-intro 是 position:sticky，所以這一段**不能**用 overflow:hidden 收出血
     （hidden 會建立捲動容器，sticky 立刻失效）。改用 overflow-x: clip，
     而且出血算式本來就剛好停在視窗邊緣，正常情況根本不會溢出。 */
.service-section {
    overflow-x: clip;
}

.svc-layout {
    isolation: isolate;   /* 讓下面兩個 z-index:-1 的色塊只退到本區塊之內 */
}

/* 左：黑。從視窗左緣到交界線 */
.svc-layout::before {
    content: "";
    display: block;
    position: absolute;
    z-index: -1;
    top: calc(var(--section-y) * -1);
    bottom: calc(var(--section-seam-band) * -1);
    left: calc((100% - 100vw) / 2);
    /* 交界 ＝ 左欄寬 ＋ 半條留白軌 */
    width: auto;
    height: auto;
    background: hsl(var(--surface-dark));
}

/* 右：灰。從交界線到視窗右緣 */
.svc-layout::after {
    content: "";
    display: block;
    position: absolute;
    z-index: -1;
    top: calc(var(--section-y) * -1);
    bottom: calc(var(--section-seam-band) * -1);
    right: calc((100% - 100vw) / 2);
}

/* ── 右欄整組翻面：淺底深字 ──────────────────────────────────────────
   ⚠ 字重也要跟著換：.on-dark 給的是深底的光暈補償（310／410），
     淺底上會顯得太細。回到米白區那一組（350／450）。 */
.svc-main {
    --fw-light:   350;
    --fw-normal:  450;
    --fw-heading: 350;
}

.svc-main .svc-feature-name,
.svc-main .svc-tile-name   { color: hsl(var(--heading)); }
.svc-main .svc-feature-desc { color: hsl(var(--muted-foreground)); }
.svc-main .svc-feature-cat,
.svc-main .svc-tile-sub,
.svc-main .svc-tile-no     { color: hsl(var(--slate)); }
.svc-main .svc-arrow       { color: hsl(var(--foreground) / .55); }
.svc-main .svc-media       { background: hsl(var(--foreground) / .05); }

.svc-main .svc-feature:focus-visible,
.svc-main .svc-tile:focus-visible { outline-color: hsl(var(--foreground)); }

/* hover 的重點色：深底那組用 AERIAL（客戶指定），淺底上必須改回黑 ——
   AERIAL 壓在灰上只有 1.3:1，hover 之後字會消失。
   ⚠ 特異度 (0,5,0)，蓋得過既有的 .on-dark …:hover (0,4,0)。 */
@media (hover: hover) {
    .on-dark .svc-main .svc-feature:hover .svc-feature-name,
    .on-dark .svc-main .svc-tile:hover .svc-tile-name {
        color: hsl(var(--foreground) / .55);
    }
}

.on-dark .svc-main .svc-feature:focus-visible .svc-feature-name,
.on-dark .svc-main .svc-tile:focus-visible .svc-tile-name {
    color: hsl(var(--foreground) / .55);
}

/* ── 色帶轉場退場 ────────────────────────────────────────────────────
   兩條 .band-shift 是「整條橫幅由 A 色掃成 B 色」的動畫。
   分段改成**左右分欄的色塊**之後，整條橫幅的掃色與版面對不上
   （右半在服務專區本來就已經是灰的，橫幅會先閃一次黑再變灰）。
   ⚠ 標記留在 home.php 不刪 —— 之後若改回整條色帶分段，拿掉這一條就復原。 */
.band-shift {
    display: none;
}

/* ── 窄幅：左右分欄收成上下排，兩個出血色塊跟著退場 ─────────────────
   一欄時「左黑右灰」沒有左右可分，色塊會變成蓋住整段的兩片。
   窄幅回到原本的單一深色底（.service-section 自己的背景）。 */
@media (max-width: 1023.98px) {
    .svc-layout::before,
    .svc-layout::after { display: none; }

    .svc-main {
        --fw-light:  310;
        --fw-normal: 410;
        --fw-heading: 330;
    }

    .svc-main .svc-feature-name,
    .svc-main .svc-tile-name    { color: hsl(var(--surface-dark-foreground)); }
    .svc-main .svc-feature-desc { color: hsl(var(--surface-dark-foreground) / .70); }
    .svc-main .svc-feature-cat,
    .svc-main .svc-tile-no      { color: hsl(var(--brand-accent)); }
    .svc-main .svc-tile-sub     { color: hsl(var(--surface-dark-foreground) / .55); }
    .svc-main .svc-arrow        { color: hsl(var(--brand-accent)); }
    .svc-main .svc-media        { background: hsl(var(--brand-accent) / .12); }
}

/* Hero 右半圖片滿版（2026-07-31 客戶指定「右邊圖片滿版，不要壓灰底四邊留邊」）。
   ⚠ 這一條推翻了上面 7d／header 那兩段給 .cal-media-item 的內距與 contain，
     刻意放在最後覆蓋，上面的宣告一行都沒刪（要回到「留白版」把這一段註解掉即可）。
   ⚠ 裁切風險已經算過：右半塊在桌機約 58% 寬 × 66vh 高 ≈ 4:3，
     而知識專區素材（class1~3）正好是 1760×1328 ＝ 4:3 —— 幾乎零裁切。
     只有實體課程那一張（course-card2，16:9）會左右各裁掉約 12%，
     所以 object-position 取 35%：把裁切偏到右邊，保住圖左側印著的標題文字。 */

/* ═══════════════════════════════════════════════════════════════════════
   Hero 的左右分界 ＝ 服務專區的黑／灰分界（2026-07-31 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「讓 hero 區域的寬的起點剛好是服務專區的右邊等寬。」

   也就是兩段的**分界線要落在同一條垂直線上**：往下捲時，Hero 右半的圖片
   與服務專區右半的灰色塊是同一個寬度、同一個起點 —— 整頁因此有一條
   貫穿的隱形格線，這正是「Grid 更明確」最有感的一次。

   ⚠ 分界比例抽成 --svc-split（SSOT）：服務專區的兩個色塊與 Hero 的兩欄
     都讀同一個值，日後調左欄寬度不會有一邊忘了改。
     值 ＝ 左欄寬 ＋ 半條留白軌（26% + 2%；1199 以下是 23% + 1.75%）。
   ⚠ Hero 是滿版 100vw，服務專區的分界線卻是「內容格線」上的百分比，
     兩者基準不同，所以要換算：
         格線寬 gridW = min(--container-wide, 100vw − --section-x × 2)
         分界線距視窗左緣 = (100vw − gridW) / 2 + gridW × --svc-split
     這個算式在每個斷點都成立，不必逐段寫死。 */
:root {
    --svc-split: .28;
}

@media (max-width: 1023.98px) {
    :root { --svc-split: .2475; }
}

/* 服務專區的兩個色塊改讀同一個比例（原本寫死 26% + 4%/2） */
.svc-layout::before { }
.svc-layout::after  { left:  calc(100% * var(--svc-split)); }


/* 知識專區左欄改膚色底（2026-07-31 客戶指定「知識專區左邊膚色底」）。
   ⚠ 只有這一塊是膚色 —— 實體課程、Footer、服務專區右半仍然是灰（--muted）。
     全站因此是「灰為主、膚色只出現一次」，仍然符合色彩克制的方向，
     而且這一塊正好是全站唯一的暖色，知識專區因此有自己的辨識度。 */
.kz-intro::before {
}

/* ── 修正：窄版 header 的 LOGO 沒有尺寸（既有問題，非本次改版造成）──────────
   .site-header .site-logo-icon 的尺寸只寫在 @media (min-width: 1468px) 裡，
   1467 以下那個圖示因此會用到瀏覽器的 SVG 預設尺寸 ——
   ⚠ 高度比桌機的 28px 小一階：窄版 header 本來就比較矮。
   ⚠ 寫在媒體查詢之外，兩個斷點都吃得到；桌機那條在後面? 不 ——
     桌機那條在本檔前段、包在 min-width:1468 裡，特異度相同而本條在後面，
     所以這裡必須把桌機的值一起顧到，用 media query 只作用在窄版。
   ⚠ 2026-08-13 圖片字標換成圖示＋文字之後，選擇器從 .site-logo img
     改成 .site-logo-icon（新標記是內嵌 SVG，不是 <img>），數值不變。 */
@media (max-width: 1467px) {
    .site-header .site-logo-icon {
        display: block;
        width: auto;
        height: 24px;
    }

    /* 漢堡選單斷點內只留圖示、品牌名文字隱藏（2026-08-13 客戶指定）。
       ⚠ 用同一個斷點（1467）而不是另開一個數字：漢堡鈕出現的那一刻，
         logo 就該跟著簡化，兩件事是同一個「窄幅收斂」的決定，數字分開
         寫日後很容易兜不起來（例如漢堡鈕先出現、品牌名還留一小段才消失）。
       ⚠ 用 display:none 不是縮小字級：客戶要的是「只留圖片」，不是「文字
         變小」——字級再怎麼縮，跟漢堡鈕、A/A+/A++ 字級切換器擠在同一列
         窄幅 header 裡还是會太滿，直接藏起來最乾脆。
       ⚠ 圖示本身不受影響，仍然可點（整顆 <a> 都是連結，不是文字消失後
         連結範圍跟著縮小）。 */
    .site-header .site-logo-text {
        display: none;
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   Hero 左半只留三件事（2026-07-31 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「hero 區域左邊的裝飾文字拿掉，只留標題跟按鈕還有一段描述。」

   收起來的三個裝飾層：
       .cal-dateline   頁眉「JULY 2026 ──── DAY 03」
       .cal-day-mark   背景的超大 DAY 數字
       .cal-eyebrow    英文分類小標「ONE DAY LISTEN」
   留下：標題（.cal-title）→ 描述（.cal-intro）→ 按鈕（.cal-cta）。

   ⚠ 一律 display:none，**標記與 PHP 資料一個字都沒刪** ——
     $heroCourses 的 en 欄位、$calMonth、$heroDayFrom 全部原封不動，
     把這一條註解掉就完全復原。
   ⚠ 這一條覆蓋掉先前「保留 DAY 數字、只是改成非襯線」那一輪的結果 ——
     以本次「只留標題／描述／按鈕」的指定為準。字體那一輪的設定留著沒刪，
     日後若要把數字叫回來，它已經是無襯線的了。
   ⚠ 頁碼與進度線（.cal-meta 的 01 / 04）**不在收起之列**：
     那是閱讀進度、不是裝飾文字，而且是客戶自己指定移到左側的。 */

/* ── 剩下三項要重新垂直置中 ──────────────────────────────────────────
   ⚠ 原本的垂直配置是靠**兩個 auto 邊距**平分剩餘空間
     （.cal-eyebrow 的 margin-top:auto ＋ .cal-cta 的 margin-bottom:auto）。
     .cal-eyebrow 一旦 display:none，上面那個斷點就消失了 ——
     只剩下面那一個，剩餘空間會全部堆到按鈕下方，整組文字被頂到頁面最上緣（實測）。
   ⚠ 所以要成對處理：把 .cal-cta 的 auto 邊距歸零，改用 justify-content:center。
     項目只剩三個、而且不再有頁眉要釘在最上面，置中是最直接的做法。 */


/* 標題成為第一個元素，上方那段原本用來與英文小標拉開距離的間距不需要了 */

/* 服務專區右半改用與知識專區右半**同一個**淺底（2026-07-31 客戶指定）。
   ⚠ 原本是 --muted #E3E3E8（與實體課程、Footer 同一階灰），
     改成 --background #F5F5F7 之後，全站「內容閱讀區」的底色統一成一個值：
       服務專區右半 ＝ 知識專區右半 ＝ Hero 左半 ＝ #F5F5F7
     深色塊（服務左欄黑）、色塊（實體課程／Footer 灰、知識左欄膚色）
     則各自維持自己的角色，層級更乾淨。 */
.svc-layout::after {
}

/* ═══════════════════════════════════════════════════════════════════════
   Hero 頁碼移到按鈕下方、改回橫式（2026-07-31 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「hero 的數字移動到按鈕下面，橫式。」

   ⚠ 這一段推翻了先前「進度移到桌曆左側、改直式」那一輪的結果 ——
     那一輪的規則一行都沒刪，全部在這裡覆蓋（要退回把這一段註解掉即可）。
   ⚠ 位置對齊：left 用**與左頁文字完全相同的算式**，所以頁碼的左緣與
     標題、描述、按鈕切齊同一條線；bottom 用與 .cal-text-item 相同的下內距。
     兩者共用同一組值，日後調文字內距時頁碼會自己跟上。
   ⚠ 直式時把橫式的寬高對調過，這裡要**兩個方向都寫回來**
     （width／height、以及 .cal-progress-bar 的 transition 屬性），
     只改 flex-direction 的話進度線會維持 1px 寬 × 132px 高的直條。 */

/* ═══════════════════════════════════════════════════════════════════════
   知識專區左欄改黑色塊（2026-07-31 客戶指定「知識專區左邊黑色」）
   ═══════════════════════════════════════════════════════════════════════
   ⚠ 這一段推翻上一輪的「左邊膚色底」，前一條規則沒有刪，在這裡覆蓋。
     全站因此變成**兩個黑色塊**（服務專區左欄、知識專區左欄）左右呼應，
     中間夾著實體課程的灰 —— 節奏更清楚，膚色 --nude 暫時沒有用到但 token 留著。
   ⚠ 底色一翻黑，左欄裡的文字**整組都要跟著翻面**，包括字重
     （深底要用 310／410／330 的光暈補償，不能沿用米白區的 350／450）。 */
.kz-intro::before {
    background: hsl(var(--surface-dark));
}

.kz-intro {
    --fw-light:   310;
    --fw-normal:  410;
    --fw-heading: 330;
}

.kz-intro .section-title .zh { color: hsl(var(--surface-dark-foreground)); }
.kz-intro .section-title .en { color: hsl(var(--brand-accent)); }

/* 清單未選取：淺灰壓黑底（原本是 SLATE 壓淺底，翻過來） */
.knowledge-section .kz-list-no,
.knowledge-section .kz-list-title {
    color: hsl(var(--surface-dark-foreground) / .62);
}

/* hover：極淡的亮色塊 + 文字轉全亮（**不是**轉黑 —— 黑字壓在深色塊上看不見） */
.knowledge-section .kz-list-btn:hover,
.knowledge-section .kz-list-btn:focus-visible {
}

.knowledge-section .kz-list-btn:hover .kz-list-no,
.knowledge-section .kz-list-btn:hover .kz-list-title,
.knowledge-section .kz-list-btn:focus-visible .kz-list-no,
.knowledge-section .kz-list-btn:focus-visible .kz-list-title {
}

/* 選取中：整列鋪一塊亮色塊，文字轉黑 —— 三列裡永遠只有一列是實心的 */
.knowledge-section .kz-list-btn[aria-selected="true"] {
    background: hsl(var(--background));
}

.knowledge-section .kz-list-btn[aria-selected="true"] .kz-list-no,
.knowledge-section .kz-list-btn[aria-selected="true"] .kz-list-title {
    color: hsl(var(--heading));
}

/* 「查看更多」膠囊在黑底上整組反相 */
.kz-more                    { border-color: hsl(var(--surface-dark-foreground) / .5); color: hsl(var(--surface-dark-foreground)); }
.kz-more::after             { background: hsl(var(--surface-dark-foreground)); }
.kz-more:hover,
.kz-more:focus-visible      { border-color: hsl(var(--surface-dark-foreground)); }
.kz-more:hover::after,
.kz-more:focus-visible::after { color: hsl(var(--surface-dark)); }

/* ── 窄幅（≤1199）：左欄不再是側邊色塊，黑底跟著退場，文字要翻回深色 ──── */
@media (max-width: 1023.98px) {
    .kz-intro {
        --fw-light:   350;
        --fw-normal:  450;
        --fw-heading: 350;
    }

    .kz-intro .section-title .zh { color: hsl(var(--heading)); }
    .kz-intro .section-title .en { color: hsl(var(--slate)); }

    .knowledge-section .kz-list-no,
    .knowledge-section .kz-list-title { color: hsl(var(--slate)); }

    .knowledge-section .kz-list-btn:hover,
    .knowledge-section .kz-list-btn:focus-visible { }

    .knowledge-section .kz-list-btn:hover .kz-list-no,
    .knowledge-section .kz-list-btn:hover .kz-list-title,
    .knowledge-section .kz-list-btn:focus-visible .kz-list-no,
    .knowledge-section .kz-list-btn:focus-visible .kz-list-title { }

    .knowledge-section .kz-list-btn[aria-selected="true"] { background: hsl(var(--muted)); }

    .kz-more                    { border-color: hsl(var(--foreground) / .5); color: hsl(var(--foreground)); }
    .kz-more::after             { background: hsl(var(--foreground)); }
    .kz-more:hover,
    .kz-more:focus-visible      { border-color: hsl(var(--foreground)); }
    .kz-more:hover::after,
    .kz-more:focus-visible::after { color: hsl(var(--background)); }
}

/* 知識專區左欄清單的 hover 改藍色（2026-07-31 客戶指定）。
   ⚠ 用的是全站唯一的強調色 --accent-blue（AERIAL #B0C7DD），
     與深色帶（服務專區／實體課程）標題 hover 是同一顆 —— 藍色在本站仍然
     只出現在 hover／active／CTA 三個角色上，沒有多一個新用途。
   ⚠ 對比算過：AERIAL 壓純黑 10.6:1，讀得很清楚。
   ⚠ 色塊也一起換成藍的極淡底（14%），hover 才是一整列在反應，
     而不是「文字變藍、底下還留著一塊白灰」。
   ⚠ **只在左欄是黑底時成立**。窄幅（≤1199）左欄的黑色塊會退場、
     文字回到淺底，AERIAL 壓在淺灰上只有 1.6:1 —— 那個斷點維持轉黑，
     見下方既有的 max-width:1199.98 覆寫（它在本條之後，會贏）。 */
.knowledge-section .kz-list-btn:hover,
.knowledge-section .kz-list-btn:focus-visible {
}

.knowledge-section .kz-list-btn:hover .kz-list-no,
.knowledge-section .kz-list-btn:hover .kz-list-title,
.knowledge-section .kz-list-btn:focus-visible .kz-list-no,
.knowledge-section .kz-list-btn:focus-visible .kz-list-title {
}

/* ⚠ 窄幅的覆寫必須再寫一次：上面那兩條是非 media 的宣告，
   會蓋掉本檔前段 max-width:1199.98 裡的同名規則（media query 不加特異度，只看順序）。 */
@media (max-width: 1023.98px) {
    .knowledge-section .kz-list-btn:hover,
    .knowledge-section .kz-list-btn:focus-visible {
        background: hsl(var(--muted) / .6);
    }

    .knowledge-section .kz-list-btn:hover .kz-list-no,
    .knowledge-section .kz-list-btn:hover .kz-list-title,
    .knowledge-section .kz-list-btn:focus-visible .kz-list-no,
    .knowledge-section .kz-list-btn:focus-visible .kz-list-title {
        color: hsl(var(--heading));
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   捲動進場改成「矩形切割」（2026-07-31 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「每個區塊的動態改成用矩形切割的方式呈現。」

   改版前：opacity 0 → 1 ＋ translateY 位移（軟、飄，屬於療癒風的動態語言）。
   改版後：元素本身不動、也不淡入，改由一個**矩形裁切框由下往上打開** ——
          內容像被一把直尺往上刮出來，乾淨、有邊、沒有位移感。
          這與整站的矩形化（圓角 2px、色塊分段）是同一套語言。

   ⚠ 進場的元素由 home.js 決定（.section-title／.svc-feature／.svc-tile／
     .course-cell／.section-more／Footer 幾塊），**都不是**帶滿版出血
     ::before／::after 的那幾層（.svc-layout／.kz-intro）——
     clip-path 會裁掉子孫的溢出，掛錯層級會把左右兩塊色塊切掉。
     要新增進場元素前，先確認它裡面沒有靠負 inset 出血的東西。
   ⚠ inset 四邊留 -14px 的**負值**（往外擴）：clip-path 即使是 inset(0)
     也會沿著 border box 裁切，:focus-visible 的 outline-offset: 6px
     會被整圈裁掉（鍵盤使用者看不到焦點框）。往外擴 14px 就避開了。
   ⚠ 起點寫成 inset(100% …)：上緣壓到底、與下緣交叉，形狀為空 ＝ 完全不可見。 */
.js-reveal {
    opacity: 1;
    transform: none;
    clip-path: inset(100% -14px -14px -14px);
    transition: clip-path var(--dur-reveal) var(--ease-out);
    transition-delay: var(--reveal-delay, 0ms);
    will-change: clip-path;
}

.js-reveal.is-in {
    opacity: 1;
    transform: none;
    clip-path: inset(-14px);
}

/* 減少動態：不做裁切動畫，直接就位（進場是裝飾，看不到內容才是事故） */
@media (prefers-reduced-motion: reduce) {
    .js-reveal,
    .js-reveal.is-in {
        clip-path: none;
        transition: none;
    }
}

/* 知識專區右欄內容垂直置中（2026-07-31 客戶指定「上下留白空間不一致」）。
   ⚠ 成因：.kz-layout 是 align-items:stretch，.kz-panels 因此吃滿整列高度，
     但 .kz-detail 是 flex column 且預設 justify-content:flex-start ——
     內容全部貼在上緣，剩下的空白一次全留在下緣（實測上 0px／下 40px）。
   ⚠ 列高是由中欄那張圖決定的（aspect-ratio 換算），文章長度不會剛好等於圖高，
     所以「上下一致」只能靠置中，不能靠調間距（文案一改又會不一致）。
   ⚠ 只動右欄。左欄的「標題在上、清單沉底」是設計稿既有的安排，不受影響。 */
.kz-detail {
    justify-content: center;
}

/* 知識專區清單 hover：**藍的是矩形，不是文字**（2026-07-31 客戶更正）。
   ⚠ 上一版做成「文字轉藍、底下一塊淡藍」，客戶指定改成整塊實心藍。
     實心 AERIAL 是**淺色**，所以文字必須翻成黑（#000 壓 #B0C7DD ＝ 10.5:1）；
     沿用上一版的藍字會是藍壓藍，完全看不見。
   ⚠ 與「選取中」仍然分得開：hover ＝ 藍色實心塊，選取中 ＝ 亮灰實心塊，
     兩者都是黑字，靠色相區分狀態。 */
.knowledge-section .kz-list-btn:hover,
.knowledge-section .kz-list-btn:focus-visible {
    background: hsl(var(--accent-blue));
}

.knowledge-section .kz-list-btn:hover .kz-list-no,
.knowledge-section .kz-list-btn:hover .kz-list-title,
.knowledge-section .kz-list-btn:focus-visible .kz-list-no,
.knowledge-section .kz-list-btn:focus-visible .kz-list-title {
    color: hsl(var(--heading));
}

/* Hero 再加高（2026-07-31 客戶指定「hero 區域再高一點」）。
   66vh／上限 720 → 78vh／上限 860。
   ⚠ 順帶把裁切也改善了：Hero 四張圖現在全部是實體課程的 16:9 素材，
     加高之後右半色塊的長寬比從約 2.0 掉到 1.7 上下，與素材的 1.78 幾乎一致 ——
     1512 視窗實測幾乎零裁切。
   ⚠ object-position 維持 35%：素材的標題文字印在圖的左半，
     萬一某個視窗比例下需要橫向裁切，裁掉的會是右邊的留白而不是字。 */

/* ═══════════════════════════════════════════════════════════════════════
   按鈕 hover 統一成「與背景色呼應的反相切換」（2026-07-31 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「按鈕 hover 全部都以背景色呼應切換。」

   規則一句話：**hover 時按鈕填滿對比色，文字換成腳下那塊底色**。
   所以同一顆按鈕在黑色塊上是「白底黑字」、在淺灰塊上是「黑底淺灰字」——
   永遠與所在的色塊呼應，不必為每個區塊各寫一套。

   ⚠ 實作靠兩個 token（--surface-here／--ink-here，定義在 tokens.css）：
     每個色塊只宣告自己是什麼底色，按鈕的 hover 規則全站只有這一份。
   ⚠ 選擇器刻意把 .on-dark／.course-section／.kz-more 這幾個**既有的
     (0,2,0) 覆寫**一起列進來 —— 只寫 .btn-pill:hover (0,1,0) 會被它們蓋掉。
   ⚠ 這一段推翻了 2026-07-30 的「hover 時膠囊本身不填色」，以本次指定為準；
     舊規則沒有刪，在這裡覆蓋。 */

/* 各色塊宣告自己的底色與對比色 */
.on-dark,
.kz-intro,
.footer-bottom {
    --surface-here: hsl(var(--surface-dark));
    --ink-here:     hsl(var(--surface-dark-foreground));
}

/* 服務專區右半是淺色塊（雖然它在 .on-dark 之內），要翻回來 */
.svc-main {
    --surface-here: hsl(var(--background));
    --ink-here:     hsl(var(--foreground));
}

/* 實體課程與 Footer 是 NUDE／灰色塊 */
.course-section,
.site-footer {
    --surface-here: hsl(var(--muted));
    --ink-here:     hsl(var(--foreground));
}

/* ── 膠囊按鈕（全站唯一的按鈕）＋ 標籤 ─────────────────────────────── */
.btn-pill:hover,
.btn-pill:focus-visible,
.on-dark .btn-pill:hover,
.on-dark .btn-pill:focus-visible,
.course-section .btn-pill:hover,
.course-section .btn-pill:focus-visible,
.kz-more:hover,
.kz-more:focus-visible,
.tag-pill:hover,
.tag-pill:focus-visible {
    background: var(--ink-here);
    border-color: var(--ink-here);
    color: var(--surface-here);
}

/* 右側徽章跟著反相：底色換成腳下的色塊、箭頭換成對比色 */
.btn-pill:hover::after,
.btn-pill:focus-visible::after,
.on-dark .btn-pill:hover::after,
.on-dark .btn-pill:focus-visible::after,
.course-section .btn-pill:hover::after,
.course-section .btn-pill:focus-visible::after,
.kz-more:hover::after,
.kz-more:focus-visible::after {
    transform: scale(1);
    background: var(--surface-here);
    color: var(--ink-here);
}

/* ── 實心按鈕（Footer 訂閱、YouTube／Podcast）──────────────────────────
   靜置本來就是實心，所以 hover 反過來：填對比色、文字換成腳下的底色。 */
.footer-submit:hover,
.footer-submit:focus-visible,
.kz-links a:hover,
.kz-links a:focus-visible {
    background: var(--ink-here);
    color: var(--surface-here);
}

/* 「不要以線條加重切換」（2026-07-31 客戶指定）——
   hover 的訊號**只有底色反相**，外框不再單獨加深／加重：
   靜置是一圈淡框，hover 時整顆填滿，框色與填色相同 ＝ 框在視覺上消失。
   ⚠ transition 必須補上 background：原本只寫了 border-color 與 color，
     底色會「瞬間跳」而不是切換，反而更像故障（實測）。
   ⚠ 徽章的 transform 維持既有的縮放時間，不要一起改成 --transition，
     那顆小點放大的手感是客戶當初照參考圖定的。 */
.btn-pill,
.tag-pill,
.footer-submit,
.kz-links a {
    transition: background var(--transition),
                border-color var(--transition),
                color var(--transition);
}

.btn-pill::after {
    transition: transform 380ms var(--ease-out),
                background var(--transition),
                color var(--transition);
}

/* ═══════════════════════════════════════════════════════════════════════
   修正 Hero 與服務專區的分界線對不齊（2026-07-31 客戶回報「有空隙」）
   ═══════════════════════════════════════════════════════════════════════
   症狀：Hero 的左右分界比服務專區的黑／灰分界往右偏約 7–8px，
        兩段交界處看得到一個小台階。

   成因：**100vw 含捲軸寬，元素的 100% 不含。**
        服務專區的色塊是用 .svc-layout 自己的 100% 算的（＝內容寬，不含捲軸）；
        Hero 的分界卻是用 100vw 算的（含捲軸）。
        1920 視窗實測：
            服務專區分界  (1905 − 1872) / 2 + 72 + 1728 × .28 = 572.3px
            Hero 分界     (1920 − 1728) / 2      + 1728 × .28 = 579.8px
        差 7.5px ＝ **半個捲軸寬**，正好就是畫面上那道空隙。

   解法：Hero 這一側改用 100cqw。
        .hero-inner 是 container-type: inline-size 的容器，
        100cqw ＝ 它的內容寬 ＝ 不含捲軸的真實可視寬 —— 與服務專區同一個基準。
   ⚠ --container-wide／--container-max 裡的 90vw **不動**：
     服務專區的容器算式也是讀同一個 token，兩邊一起偏移不會產生落差；
     真正會造成落差的只有「除以 2 的置中項」，也就是這裡改掉的那一段。
   ⚠ .svc-layout::before/::after 與 .kz-intro::before 的出血仍用 100vw：
     那是**往外多蓋一點**，多出來的部分被 overflow-x: clip 收掉，
     不影響任何邊界位置，反而保證色塊一定蓋滿到視窗邊緣。 */

/* ═══════════════════════════════════════════════════════════════════════
   修正：卡片圖片 hover 放大時跑出容器（2026-07-31 客戶回報）
   ═══════════════════════════════════════════════════════════════════════
   症狀：近期課程的卡片 hover 時，放大的圖片溢出 .course-thumb 的框。

   成因是**這一輪剛加的矩形切割進場動畫**：
     .js-reveal 掛了 will-change: clip-path，元素因此被提升成獨立的合成層；
     裡面的 <img> 又有自己的 transform（hover 放大）也在合成層上。
     這種「祖先是合成層 + 後代是合成層」的組合下，Chrome 有機會**略過中間那層
     overflow:hidden 的裁切**（裁切發生在繪製階段，合成階段沒有跟上）。
     ⚠ 所以這不是 overflow 寫錯 —— computed 值一直都是 hidden（實測）。

   兩道保險一起上，任一道單獨都夠，但兩道都便宜：
     ① 裁切改用 clip-path：clip-path 是**合成器層級**的裁切，
        與 transform 在同一個階段處理，不會被跳過。
     ② 進場結束後把 will-change 收掉：這本來就是該做的事
        （本檔多處已經警告過「常駐 will-change 會讓元素一直停在貼圖裡」）。
   ⚠ 服務專區的 .svc-media 是同一個結構（也是進場元素 + hover 放大），
     一併補上，不要等它也被回報。 */
.course-thumb,
.svc-media {
    clip-path: inset(0);
}

.js-reveal.is-in {
    will-change: auto;
}

/* ═══════════════════════════════════════════════════════════════════════
   知識專區右欄：分類＝膚色底、標籤＝藍色底（2026-07-31 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「知識專區右邊的標籤跟分類，一個膚色一個藍色背景。」

   兩顆的語意本來就不同（見 home.php 的說明）：
       .tag-pill--cat   分類 —— 文章的歸屬，不加 #   → 膚色 NUDE #F2E3D0
       .tag-pill        標籤 —— 才是 hashtag，帶 #    → 藍色 AERIAL #B0C7DD
   改成實心色塊之後，兩者一眼分得出是兩種東西，而不是「長得一樣的兩顆膠囊」。

   ⚠ 兩個都是**淺色**，所以文字一律黑：
       黑壓 NUDE   16.8:1
       黑壓 AERIAL 10.5:1
   ⚠ 外框拿掉（實心塊就是邊界，符合「拿掉線條感」）。
   ⚠ hover 仍走全站統一的反相規則（填對比色、文字換成腳下的底色），
     不必在這裡另外寫 —— .tag-pill:hover 已經包含在那一組選擇器裡。
   ⚠ --nude 這個 token 因此重新有了用途（知識專區左欄改黑之後一度沒人用）。 */
.kz-tags .tag-pill {
    border-color: transparent;
    background: hsl(var(--accent-blue));
    color: hsl(var(--heading));
}

.kz-tags .tag-pill--cat {
    background: hsl(var(--nude));
}

/* ═══════════════════════════════════════════════════════════════════════
   色塊分割進場（2026-07-31 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「我說的矩形動態是色塊切割的方式出現，現在背景很多色塊，
          讓他們從一整塊分割出現。」

   模型：每個左右分欄的區塊，底下疊兩層色塊
       ::before  深色，**滿版整條**（＝那「一整塊」）
       ::after   淺色，只有右半，疊在 ::before 之上（同一個堆疊環境，後畫者在上）
   進場時 ::after 由分界線往右刷開 —— 讀起來就是一整塊深色從中間裂開，
   右半變成淺色。沒有位移、沒有淡入，只有一條垂直的切割線往右走。

   ⚠ 起始狀態掛在 **.js-split** 上（由 home.js 加），不是 CSS 預設值：
     JS 沒跑時 ::after 就在它該在的位置，區塊是正確的靜態外觀。
     若把「藏起來」寫成預設，JS 一掛掉右半會整片深色，而右半的文字是深色的
     —— 整段內容消失。那是事故，不是少了動畫。
   ⚠ 深色那層改成往右也滿版出血（-100vw），靠區塊的 overflow-x: clip 收掉，
     這樣「未分割」時整條才真的是一整塊。 */
.svc-layout::before {
}

.svc-layout::after,
.kz-intro::after {
    transition: clip-path 760ms var(--cal-ease);
}

.svc-layout.js-split::after,
.kz-intro.js-split::after {
    clip-path: inset(0 0 0 100%);
}

.svc-layout.js-split.is-in::after,
.kz-intro.js-split.is-in::after {
    clip-path: inset(0);
}

/* 知識專區原本只有 ::before（黑色塊），這裡補上右半的淺色塊 ——
   有了它才有「一整塊裂開」可言（在此之前右半是區塊自己的底色，沒有東西可以刷）。 */
.kz-intro::before {
    right: -100vw;
}

.kz-intro::after {
    content: "";
    position: absolute;
    z-index: -1;
    /* 分界線 ＝ ::before 的右緣（左欄寬 + 半條留白軌 ＝ 100% + 100%/6） */
    left: calc(100% + 100% / 6);
    right: -100vw;
    background: hsl(var(--background));
}

@media (prefers-reduced-motion: reduce) {
    .svc-layout.js-split::after,
    .kz-intro.js-split::after {
        clip-path: inset(0);
        transition: none;
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   知識專區：圖片滿版、整段變矮（2026-07-31 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「知識專區右邊的圖片直接滿版不要留邊」「這裡整體高可以變矮一點」。

   ⚠ 兩件事其實是同一件：整段變高的主因就是那張圖 ——
     它有 aspect-ratio 1760/1328，欄寬 700 就自動長到 528px 高，
     整列的高度被它一個人撐起來（左欄內容約 300、右欄約 380）。
     拿掉 aspect-ratio 改成跟著列高伸展之後，列高由文字決定，整段自然矮一階。
   ⚠ 圖片往左吃掉那條留白軌（grid-column: 2 / 4）：直接貼著黑色塊，
     兩塊之間是一條硬邊 —— 與全站「色塊對色塊、不留縫」的語言一致。
   ⚠ 代價：cover 會裁掉上下（實測約 28%）。素材的標題文字在中上段，
     置中裁切保得住；若日後客戶回報字被切到，把 aspect-ratio 加回來即可。 */
.knowledge-section {
    --kz-pad-top:    clamp(3rem, 6vh, 5rem);
    --kz-pad-bottom: clamp(3.5rem, 7vh, 6rem);

    padding-block: var(--kz-pad-top) var(--kz-pad-bottom);
}

/* 出血色塊的上下要跟著新的內距走，否則黑塊會短一截或多一截 */
.kz-intro::before,
.kz-intro::after {
    top:    calc(var(--kz-pad-top) * -1);
    bottom: calc(var(--kz-pad-bottom) * -1);
}

.kz-media {
    grid-column: 2 / 4;     /* 吃掉左邊那條留白軌，直接貼著黑色塊 */
    align-self: stretch;
    aspect-ratio: auto;
    height: auto;
    border-radius: 0;
}

.kz-media picture,
.kz-media img {
    object-position: center;
}

/* 窄幅收單欄時，上面那兩條（跨軌、伸展）沒有意義，比例要拿回來，
   否則圖片會塌成 0 高（單欄時沒有別的東西撐高度）。 */
@media (max-width: 1023.98px) {
    .kz-intro::after { display: none; }

    .kz-media {
        grid-column: 1;
        align-self: start;
        aspect-ratio: 1760 / 1328;
    }
}

/* 舊的（掛在格子上的）整組退場 */
.kz-intro::before,
.kz-intro::after {
    display: none;
}

/* 知識專區圖片連上下也滿版（2026-07-31，接續「不要留邊」）。
   ⚠ 上一步只讓它吃滿 grid 列高，上下仍留著區塊自己的內距，
     所以圖片與黑色塊的上下緣沒有對齊（實測差 45／58px）。
     這裡用等量的負邊距把它撐進內距裡 —— 與黑色塊的上下緣完全切齊。
   ⚠ 讀的是同一組 --kz-pad-top／--kz-pad-bottom（SSOT），
     日後調區塊留白時圖片會自己跟上，不會又出現一條縫。
   ⚠ 負邊距不會把區塊撐破：圖片的子元素全是 absolute，本來就不貢獻列高。 */
@media (min-width: 1024px) {
    .kz-media {
        margin-block: calc(var(--kz-pad-top) * -1) calc(var(--kz-pad-bottom) * -1);
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   箭頭改用 CSS 逸出碼（2026-07-31 客戶回報「箭頭都變成 â†'」）
   ═══════════════════════════════════════════════════════════════════════
   成因：伺服器回的是 `Content-Type: text/css`，**沒有 charset**；
        CSS 檔本身也沒有 @charset。瀏覽器只好用後備編碼解讀整份樣式表，
        UTF-8 的「→」(E2 86 92) 被讀成 Latin-1 的三個字元「â†'」。

   已經在檔首補上 @charset "UTF-8"（那是根治），這裡再加一道保險：
   把 content 裡的字面箭頭換成 **\2192 逸出碼** —— 逸出碼是純 ASCII，
   不管檔案被當成哪一種編碼解讀，結果都一樣是 U+2192。
   ⚠ 兩道一起做的理由：@charset 只要有人日後在檔首加一行註解就會失效
     （它必須是第一個位元組），逸出碼則永遠成立。
   ⚠ 逸出碼後面那個空格是 CSS 的終止符，會被吃掉、不會多印一個空白。 */
.btn-pill::after,
.footer-submit::after,
.course-link::after {
    content: "\2192";
}

.svc-arrow:not(:has(.wf-icon))::before {
    content: "\2192";
}

/* ═══════════════════════════════════════════════════════════════════════
   知識專區右半改為與上下連續（2026-07-31 客戶指定「上方有一條線把它刪掉」）
   ═══════════════════════════════════════════════════════════════════════
   那條線**不是** 1px 規則 —— 整個區塊的 border 與偽元素都掃過，
   只有按鈕的外框（複查實測）。它是**色塊落差太小**造成的視覺假線：

       實體課程   #E3E3E8  ┐
       ───────────────────  ← 差 12 階，讀起來是一條線，不是兩個色塊
       知識專區右 #F6F6F7  ┘

   差距大（黑 vs 灰）時讀成「兩塊」，差距小時就讀成「一條沒對齊的線」。
   所以解法不是去找線刪掉，是把這個落差**收成 0**：
   知識專區右半改用與上下相同的 --muted，於是

       實體課程 → 知識專區右半 → Footer   全部同一塊灰，完全沒有接縫
       黑色左欄與滿版圖片      成為這片灰上唯一被「切」出來的兩塊

   ⚠ 這與「從一整塊分割出現」的動態是同一個概念，視覺上更一致。
   ⚠ 副作用：服務專區右半仍是 #F6F6F7（客戶稍早指定「跟知識專區右邊一樣」）。
     兩者現在不同色了 —— 若要一起改成灰，把 .svc-layout::after 的 background
     也換成 --muted 即可（一行）。先不動，等客戶看過實機再決定。 */
.knowledge-section {
}

/* 知識專區右半改回淺灰、與左右形成對比（2026-07-31 客戶指定
   「知識專區右邊還是要有淺灰比」）。
   ⚠ 這一條把上一步「與上下連續（--muted）」的做法收回 —— 以本次指定為準。
   ⚠ 上一步要解的「假線」改用另一個方式處理：把 --muted 壓深一階
     （#E3E3E8 → #DCDCE1，見 tokens.css）。落差從 12 階拉到 26 階之後，
     同一個交界會被讀成「兩個色塊」而不是「一條線」——
     淺灰比保住了，線也不見了。 */
.knowledge-section {
    background: hsl(var(--background));
}

/* ═══════════════════════════════════════════════════════════════════════
   Footer 版權列改成淺底黑字、置中（2026-07-31 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   ⚠ 底色不用 --muted（那是 Footer 本體的灰），改用更淺的 --background ——
     兩者相鄰時要讀得出是兩塊（Footer 灰 #DCDCE1 → 版權列 #F6F6F7），
     這是本站分段的一貫做法：靠色塊，不靠線。
   ⚠ 底色一翻淺，**三件事必須一起改**，少一件就會有東西看不見：
       ① 字色     反白 → 黑
       ② 字重     深底的光暈補償（310／410）→ 淺底的 350／450
       ③ hover 的 --surface-here／--ink-here 也要翻回淺底那一組，
          否則版權列裡的連結 hover 會填成米白、字變黑底色 —— 白填白。
   ⚠ 置中：原本是左右兩端對齊（版權在左、隱私權政策在右）。
     改成整列置中之後兩者並排在中間，窄幅時堆疊、也維持置中。 */
.footer-bottom {
    --fw-light:  350;
    --fw-normal: 450;
    --surface-here: hsl(var(--background));
    --ink-here:     hsl(var(--foreground));

    gap: 2rem;
}

.footer-copy {
}

.footer-legal a {
}

.footer-legal a:hover,
.footer-legal a:focus-visible {
}

/* 窄幅原本是靠左堆疊，跟著改成置中 */
@media (max-width: 767.98px) {
    .footer-bottom {
    }

    .footer-legal {
    }
}

/* 版權列改回黑底（2026-07-31 客戶「還是黑色底好了」）。
   ⚠ **只翻底色與配色，置中保留** —— 置中是上一輪另外指定的，客戶沒有收回。
   ⚠ 同樣是那三件要成對翻回去的事（理由見上一段）：
       ① 字色 黑 → 反白
       ② 字重 350／450 → 深底的光暈補償 310／410
       ③ --surface-here／--ink-here 翻回深底那一組，
          否則「隱私權政策」的 hover 會填成黑、字也是黑 —— 黑填黑。 */
.footer-bottom {
    --fw-light:  310;
    --fw-normal: 410;
    --surface-here: hsl(var(--surface-dark));
    --ink-here:     hsl(var(--surface-dark-foreground));

    background: hsl(var(--surface-dark));
    color: hsl(var(--surface-dark-foreground));
}

.footer-copy {
    color: hsl(var(--surface-dark-foreground) / .75);
}

.footer-legal a {
    color: hsl(var(--surface-dark-foreground) / .75);
}

.footer-legal a:hover,
.footer-legal a:focus-visible {
    color: hsl(var(--surface-dark-foreground));
}

/* 版權列的文字位置也改回原本的（2026-07-31 客戶「改回原本的，文字位置也是」）。
   置中收回 → 回到原始的兩端對齊：版權在左、隱私權政策在右。
   ⚠ 至此版權列已完全回到改版前的狀態（黑底／反白／兩端對齊），
     上面那兩段「淺底黑字置中」與「改回黑底」的規則沒有刪除，
     留著是為了保留這一輪的決策軌跡；要再試一次淺底時把它們解開即可。 */
.footer-bottom {
    justify-content: space-between;
    text-align: left;
}

@media (max-width: 767.98px) {
    .footer-bottom {
        align-items: flex-start;
        gap: .75rem;
    }

    .footer-legal {
        justify-content: flex-start;
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   色塊分割改成「黑色簾幕往右退場」（2026-07-31 客戶回報「還是有一條線」）
   ═══════════════════════════════════════════════════════════════════════
   症狀：知識專區右半的上緣有一條比兩邊都深的細線（客戶截圖）。

   成因：**黑色塊從淺色塊的上緣露出來的 1px。**
        分割動畫原本的做法是「黑色 ::before 鋪滿整條 → 淺色 ::after 蓋住右半」。
        於是右半的每一個位置底下都躺著一層黑，只要上面那層有半個像素沒蓋到就會露餡。
        而這三樣東西的上緣是**用不同方式算出來的**：
            ::before / ::after   絕對定位，top: -(--kz-pad-top)
            .kz-media            grid 項目 + 負 margin
        --kz-pad-top 是 clamp(3rem, 6vh, 5rem) ＝ 61.44px（小數），
        兩套算法在裝置像素上的捨入不一定一致 → 黑色從縫裡透出 1px。
        ⚠ 所以這條線不是「多畫了一條線」，是「少蓋了一個像素」——
          去調 padding 或加 1px 補償都只是把問題挪到別的視窗高度上。

   解法：**讓右半底下根本不要有黑色。** 兩層的角色對調：
        ::before  黑色，只到分界線為止 ＝ 左欄那一塊（永久、靜態）
        ::after   黑色**簾幕**，蓋住右半；預設隱藏，進場時先出現、再往右退場
        右半在靜止狀態下露出來的是區塊自己的淺色底，底下沒有任何黑色可以露。

   ⚠ 動畫外觀完全相同：一整塊黑從分界線裂開、右半變成淺色。
     差別只在「誰在動」——改成簾幕退場之後，靜止狀態不再依賴任何遮蓋。
   ⚠ 漸進增強的方向要跟著反過來：CSS 預設 ＝ 簾幕**隱藏**（正確的靜態外觀），
     .js-split（由 JS 掛）才讓它出現，.is-in 再把它掃掉。
     JS 沒跑 → 沒有 .js-split → 簾幕從頭到尾不存在，區塊正常。
   ⚠ 兩個區塊的底色要跟著翻成淺色（黑色改由 ::before 畫），
     否則簾幕退場後露出來的會是原本的黑底。 */

/* ── 服務專區 ────────────────────────────────────────────────────── */
.service-section {
    background: hsl(var(--background));
}

.svc-layout::before {
    right: calc(100% - 100% * var(--svc-split));   /* 黑色左欄，停在分界線 */
}

.svc-layout::after {
    background: hsl(var(--surface-dark));          /* 黑色簾幕 */
    clip-path: inset(0 0 0 100%);                  /* 預設隱藏 ＝ 正確的靜態外觀 */
}

.svc-layout.js-split::after       { clip-path: inset(0); }              /* 先蓋滿 */
.svc-layout.js-split.is-in::after { clip-path: inset(0 0 0 100%); }     /* 再往右掃掉 */

@media (prefers-reduced-motion: reduce) {
    .svc-layout.js-split::after {
        clip-path: inset(0 0 0 100%);
        transition: none;
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   近期課程 — 橫向捲動列（2026-07-31 客戶指定，依提供的參考圖）
   ═══════════════════════════════════════════════════════════════════════
   參考圖的結構：標題靠左（下方一段說明）／左右箭頭在同一列的右端／
   卡片橫向排並往右溢出／整段最下方一顆置中的按鈕。

   本站的對應：
     ・標題靠左  ← 原本是置中（2026-07-30 的決定），依本次指定改回靠左
     ・箭頭      ← 新增的兩顆捲動控制（標記在 home.php，行為在 home.js）
     ・橫向排    ← .course-grid 由三欄 grid 改成可橫捲的 flex 列
     ・置中按鈕  ← 既有的 .section-more，不動
   ⚠ 參考圖標題下方那段說明**沒有加** —— 那是新增文案，客戶的資料裡沒有這一段，
     依「不要自行加字」的既有指示不補。日後客戶給了，加在 .course-head 裡即可。

   ⚠ 卡片寬度 ＝ (列寬 − 2×間距) / 3：桌機剛好排滿三張、不留尾巴；
     筆數變四筆以上就會自然溢出成真正的捲動列，不必再改 CSS。
     min-width 240px 讓窄幅一定會溢出（否則會被壓成一條）。
   ⚠ scroll-snap 讓每次捲動停在卡片邊界，不會停在半張卡上。
   ⚠ 捲軸隱藏但**保留捲動能力**：觸控滑動、觸控板橫捲、鍵盤都還在，
     只是不畫那條捲軸（列底下多一條灰槓會破壞版面）。 */
.course-head {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 2rem;
    text-align: left;
}

.course-head .section-title {
    margin-bottom: 0;
}

.course-nav {
    display: flex;
    align-items: center;
    gap: 1.25rem;
    flex: none;
    padding-bottom: .5rem;
}

.course-arrow {
    width: 1.75rem;
    height: 1.75rem;
    padding: 0;
    border: 0;
    background: transparent;
    color: hsl(var(--foreground));
    font-family: var(--font-en);
    font-size: 1.25rem;
    line-height: 1;
    cursor: pointer;
    transition: opacity var(--transition), transform var(--dur-hover) var(--ease-out);
}

.course-arrow--prev::before { content: "\2190"; }
.course-arrow--next::before { content: "\2192"; }

/* 捲不動時壓淡並停用 —— 不隱藏，版面不會因為筆數不同而跳動 */
.course-arrow:disabled {
    opacity: .28;
    cursor: default;
}

@media (hover: hover) {
    .course-arrow--prev:not(:disabled):hover { transform: translateX(calc(var(--hover-nudge) * -1)); }
    .course-arrow--next:not(:disabled):hover { transform: translateX(var(--hover-nudge)); }
}

/* ── 卡片列 ─────────────────────────────────────────────────────────── */
.course-grid {
    display: flex;
    flex-wrap: nowrap;
    grid-template-columns: none;      /* 覆寫原本的三欄 grid */
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    scroll-behavior: smooth;
    scrollbar-width: none;            /* Firefox */
    -ms-overflow-style: none;
}

.course-grid::-webkit-scrollbar { display: none; }

.course-cell {
    flex: 0 0 auto;
    min-width: 240px;
    scroll-snap-align: start;
}

/* 減少動態：不做平滑捲動與箭頭位移 */
@media (prefers-reduced-motion: reduce) {
    .course-grid { scroll-behavior: auto; }
    .course-arrow { transition: none; }
    .course-arrow--prev:not(:disabled):hover,
    .course-arrow--next:not(:disabled):hover { transform: none; }
}

/* 窄幅：一次露一張半，讓使用者一眼看得出「右邊還有」 */
@media (max-width: 1023.98px) {
    .course-head {
        flex-direction: column;
        align-items: flex-start;
        gap: 1.25rem;
    }

    .course-nav { padding-bottom: 0; }

    .course-cell { }
}

/* ═══════════════════════════════════════════════════════════════════════
   服務專區 — 主打服務改成「左文右圖」（2026-07-31 客戶指定，依提供的參考圖）
   ═══════════════════════════════════════════════════════════════════════
   參考圖：左半是白底的文字面板（標題／說明／深色按鈕），右半是滿版圖片。

   本站的對應（**標記完全沒有動**，只調整版型與順序）：
       .svc-feature        由「上圖下文」改成兩欄 grid
       .svc-feature-media  移到右欄，去掉固定比例、改成吃滿整塊高度
       .svc-feature-info   移到左欄，垂直置中，四周給大內距 ＝ 那塊白底面板
       .svc-feature-tail   分類＋箭頭做成深色實心塊 ＝ 參考圖左下那顆按鈕

   ⚠ 參考圖按鈕上的「START YOUR JOURNEY」**沒有照抄** —— 那是新增文案。
     這裡沿用既有的分類文字當標籤（「一對一 / 企業溝通顧問服務 →」），
     一個字都沒有自己加。客戶日後給了 CTA 文案，換掉那一個 span 即可。
   ⚠ .svc-feature 整張本來就是連結，所以那顆「按鈕」是**視覺元素**不是第二個連結
     （不巢狀 <a>，也不會多出一個 tab 停留點）。
   ⚠ 左欄底色用 --background：它落在服務專區右半（本來就是淺底）之上，
     兩者同色 ＝ 面板與底融成一片，圖片因此是唯一被切出來的塊。
     若日後想要參考圖那種「白面板浮在灰底上」的層次，把這裡改成
     --brand-foreground（純白）就有了。 */
.svc-feature {
    display: grid;
    align-items: stretch;
    background: hsl(var(--background));
}

/* 右欄：滿版圖片。去掉 21/9 的固定比例，改成跟著整塊的高度伸展。 */
.svc-feature-media {
    grid-area: 1 / 2;
    aspect-ratio: auto;
    height: 100%;
    min-height: clamp(300px, 36vh, 480px);
}

/* 左欄：文字面板，垂直置中 */
.svc-feature-info {
    grid-area: 1 / 1;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    justify-content: center;
    gap: 0;
    margin-top: 0;
    padding: clamp(2rem, 3.4vw, 4rem);
}

.svc-feature-text {
    width: 100%;
}

.svc-feature-desc {
    margin-top: 1rem;
    max-width: 34em;
}

/* 分類 ＋ 箭頭 ＝ 參考圖左下那顆深色按鈕 */
.svc-feature-tail {
    align-items: center;
    gap: .875rem;
    margin-top: 2rem;
    padding: .875rem 1.25rem;
    background: hsl(var(--foreground));
    color: hsl(var(--background));
    transition: background var(--transition), color var(--transition);
}

.svc-feature-tail .svc-feature-cat,
.svc-feature-tail .svc-arrow {
    color: inherit;
}

@media (hover: hover) {
    .svc-feature:hover .svc-feature-tail {
        background: hsl(var(--foreground) / .82);
    }
}

/* 窄幅：兩欄收成上下，圖片回到固定比例（沒有旁邊的文字可以撐高度） */
@media (max-width: 767.98px) {
    .svc-feature {
        grid-template-columns: minmax(0, 1fr);
    }

    .svc-feature-media {
        grid-area: 1 / 1;
        aspect-ratio: 16 / 10;
        height: auto;
        min-height: 0;
    }

    .svc-feature-info {
        grid-area: 2 / 1;
        padding: 1.75rem 0 0;
    }

    .svc-feature-tail {
        align-self: flex-start;
        justify-content: flex-start;
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   服務專區 — 其餘服務改成「純文字格 ＋ hover 浮出小圖」
   （2026-07-31 客戶指定，依提供的兩張參考圖）
   ═══════════════════════════════════════════════════════════════════════
   參考圖一：深底的文字格陣列 —— 每一格是「小標籤 → 標題 → 說明 → 前往按鈕」，
             格與格之間一條細線，**格子裡沒有圖片**。
   參考圖二：滑鼠指到某一列時，游標旁邊浮出一張小圖。

   本站的對應（**標記完全沒有動**，只調整版型與順序）：
       .svc-tile-no    → 左上角的小標籤（編號）
       .svc-tile-name  → 標題
       .svc-tile-sub   → 說明
       .svc-arrow      → 左下角的前往按鈕
       .svc-tile-media → 從版面裡抽出來，改成 hover 時跟著游標的小圖

   ⚠ 順序用 flex 的 order 換，HTML 完全沒改 ——
     .svc-tile-tail 用 display:contents「消失」成透明容器，
     它裡面的編號與箭頭因此變成 .svc-tile-info 的直接 flex 子項，
     才能與標題／說明一起排序。
   ⚠ 這一區重新出現細線，是因為客戶這次的參考圖就是靠線分格 ——
     與先前「拿掉線條感」不衝突：那一條是講**區塊之間**的分段，
     這裡是**格與格**的內部分隔，而且是客戶指定的版型。
   ⚠ 深底：參考圖是深色格。它落在服務專區的淺色右半上，
     所以這一塊自成一個深色方陣，與上方的顧問案（白底面板）形成明暗交錯。 */

/* ── 陣列本身 ───────────────────────────────────────────────────────── */
.svc-grid {
    --svc-tile-gap: 0;            /* 格子相連，靠細線分隔，不留縫 */

    row-gap: 0;
    column-gap: 0;
}

.svc-tile {
    display: flex;
    padding: clamp(1.5rem, 2.2vw, 2.5rem);
    /* 格線：右邊與下面各一條，最右一欄與最後一列由下面兩條規則收掉 */
}

.svc-tile:nth-child(even) { border-right: 0; }
.svc-tile:nth-last-child(-n + 2):nth-child(odd),
.svc-tile:last-child { border-bottom: 0; }

/* 深底 → 文字整組翻回反白（這一塊在淺色右半之內，不能沿用 .svc-main 的深字） */
.svc-grid {
    --fw-light:  310;
    --fw-normal: 410;
    --fw-heading: 330;
}

.svc-main .svc-grid .svc-tile-name { }
.svc-main .svc-grid .svc-tile-sub  { }
.svc-main .svc-grid .svc-tile-no   { }
.svc-main .svc-grid .svc-arrow     { }

.svc-main .svc-grid .svc-tile:focus-visible {
    outline-offset: -4px;
}

/* ── 格子內部：小標籤 → 標題 → 說明 → 前往按鈕 ───────────────────── */
.svc-tile-info {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    justify-content: flex-start;
    gap: 0;
    width: 100%;
    margin-top: 0;
}

/* 讓編號與箭頭「穿透」出來，成為 .svc-tile-info 的直接子項才能排序 */
.svc-tile-tail {
    display: contents;
}

/* 編號 → 左上角的實心小標籤（參考圖的黃色籤條，這裡用站上的 NUDE） */
.svc-tile-no {
    order: -1;
    padding: .25rem .625rem;
    background: hsl(var(--nude));
    line-height: 1.4;
}

.svc-tile-text { order: 1; margin-top: 1.25rem; }
.svc-tile-name { display: block; }
.svc-tile-sub  { margin-top: .625rem; max-width: 30em; }

/* 箭頭 → 左下角的前往按鈕（淺底深字，與參考圖的白底按鈕同一個角色） */
.svc-arrow {
    order: 2;
    width: auto;
    margin-top: 1.5rem;
    padding: .5rem .875rem;
    background: hsl(var(--background));
}

@media (hover: hover) {
    .svc-tile:hover .svc-arrow { }
}

/* ── hover 浮出的小圖（參考圖二）──────────────────────────────────────
   圖片從版面裡抽出來（position:fixed），只在 hover 時淡入、跟著游標走。
   ⚠ 座標由 home.js 寫入 --peek-x／--peek-y（視窗座標，配合 fixed）。
   ⚠ translate 用獨立屬性、不寫進 transform：淡入時要縮放，兩者分開才不會
     讓位置也被 transition 拖慢（與 Hero 的游標提示同一個做法）。
   ⚠ pointer-events:none 不可省 —— 這張圖會壓在游標正下方，
     不關掉的話會把 hover 事件從格子身上搶走，變成閃爍。
   ⚠ .svc-tile 已從 clip-path 進場群組移除（見 home.js）：
     clip-path 會成為 fixed 後代的包含塊並裁切它，留著這張圖永遠不會出現。 */
.svc-tile-media {
    position: fixed;
    left: 0;
    top: 0;
    z-index: 30;
    width: clamp(140px, 13vw, 210px);
    aspect-ratio: 4 / 3;
    translate: calc(var(--peek-x, 0px) + 1.5rem) calc(var(--peek-y, 0px) - 50%);
    opacity: 0;
    scale: .96;
    pointer-events: none;
    transition: opacity 200ms var(--ease-out), scale 240ms var(--ease-out);
}

.svc-tile.is-peek .svc-tile-media {
    opacity: 1;
    scale: 1;
}

/* hover 時圖片不再放大（它現在是小預覽，不是版位裡的圖） */
@media (hover: hover) {
    .svc-tile:hover .svc-media img { transform: none; }
    .svc-tile:hover .svc-media::after { opacity: 0; }
}

/* 觸控／無指標裝置：沒有 hover 可用，小圖永遠不會出現 —— 直接不輸出，
   避免它以 opacity:0 的狀態疊在畫面上攔截捲動。 */
@media not all and (hover: hover) {
    .svc-tile-media { display: none; }
}

@media (prefers-reduced-motion: reduce) {
    .svc-tile-media { transition: opacity 120ms linear; scale: 1; }
}

/* 窄幅：兩欄收成一欄，格線只留下面那一條 */
@media (max-width: 767.98px) {
    .svc-tile { border-right: 0; }
    .svc-tile:nth-last-child(-n + 2):nth-child(odd) { border-bottom: 1px solid hsl(var(--surface-dark-foreground) / .16); }
    .svc-tile:last-child { border-bottom: 0; }
}

/* 近期課程：卡片放寬到「一次露 2.5 張」，讓它真的捲得動
   （2026-07-31 客戶：「箭頭點擊要往右邊滾動，就是真的是捲動式的輪播」）。

   ⚠ 為什麼要放寬：上一版卡寬 ＝ 列寬的 1/3，三張剛好排滿 → scrollWidth
     等於 clientWidth → 捲不動 → home.js 依規則把兩顆箭頭設成 disabled。
     那不是箭頭壞掉，是**沒有東西可以捲**。
   ⚠ 目前只有三筆課程，所以要捲得動，卡片就必須寬過列寬的 1/3 ——
     這是筆數少時無法兩全的取捨（要嘛排滿不捲，要嘛卡片變寬）。
     客戶之後把課程加到四筆以上，這個值可以再收窄回 3.4 之類的比例。
   ⚠ 2.5 的算式扣兩個間距是刻意的近似：小數張卡的間距數量不是整數，
     扣兩個在 2–3 張的範圍內誤差最小（實測 1713 列寬時右邊剛好露出半張）。 */
.course-cell {
}

@media (max-width: 1023.98px) {
    .course-cell { }
}

/* 修正：深色文字格 hover 時標題消失（2026-07-31 複查抓到）。
   ⚠ 服務專區右半改成淺底時，我把 hover 的標題色設成 hsl(--foreground / .55)（深灰）。
     現在網格自己是**深色方陣**，那條規則照樣生效 → 深灰壓黑 ＝ 標題整個看不見（實測）。
   ⚠ 用 .svc-grid 前綴把特異度拉到 (0,6,0)，蓋過那條 (0,5,0) 的淺底規則；
     顏色改用 AERIAL —— 與深色帶其他地方的 hover 是同一顆，10.6:1。 */
@media (hover: hover) {
    .on-dark .svc-main .svc-grid .svc-tile:hover .svc-tile-name {
    }
}

.on-dark .svc-main .svc-grid .svc-tile:focus-visible .svc-tile-name {
}

/* ═══════════════════════════════════════════════════════════════════════
   服務專區其餘卡牌：膚色 ／ 藍色底（2026-07-31 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「服務專區的其他卡牌用膚色跟藍色當底色。」
   ⚠ 這一段把上一版的「深色文字方陣」整組翻成淺色雙色格 —— 以本次指定為準。

   ⚠ 排法是**棋盤式**，不是單純的奇偶交替：
       兩欄的版面下，奇偶交替會讓左欄永遠膚色、右欄永遠藍色，
       上下相鄰的格子就會同色、黏成一條（實測看起來像兩個直條而不是格子）。
       4n+1 / 4n+4 → 膚色    4n+2 / 4n+3 → 藍色
       這樣任何相鄰的格子都不同色，**完全不需要格線**（呼應「拿掉線條感」）。
   ⚠ 兩個底色都是淺色，所以整格的文字翻回黑、字重回到淺底那一組（350／450）。
   ⚠ 編號小標籤改成黑底淺字：膚色籤條壓在膚色格上會消失，
     黑色在兩種底色上都是最高對比（16.8:1 / 10.5:1）。 */
.svc-grid {
    background: transparent;
    --fw-light:  350;
    --fw-normal: 450;
    --fw-heading: 350;
}

.svc-tile {
    border-right: 0;
    border-bottom: 0;
}

.svc-tile:nth-child(4n + 1),
.svc-tile:nth-child(4n + 4) { background: hsl(var(--nude)); }

.svc-tile:nth-child(4n + 2),
.svc-tile:nth-child(4n + 3) { background: hsl(var(--accent-blue)); }

/* 淺底 → 文字整組翻回黑 */
.svc-main .svc-grid .svc-tile-name { color: hsl(var(--heading)); }
.svc-main .svc-grid .svc-tile-sub  { color: hsl(var(--foreground) / .68); }

/* 編號籤條：黑底淺字，在膚色與藍色上都讀得到 */
.svc-main .svc-grid .svc-tile-no {
    background: hsl(var(--foreground));
    color: hsl(var(--background));
}

/* 前往按鈕：淺底黑箭頭，hover 轉黑底淺箭頭（與全站按鈕的反相邏輯一致） */
.svc-main .svc-grid .svc-arrow {
    background: hsl(var(--background));
    color: hsl(var(--heading));
}

@media (hover: hover) {
    .svc-tile:hover .svc-arrow {
        background: hsl(var(--foreground));
        color: hsl(var(--background));
    }

    /* ⚠ 標題 hover 不能再用 AERIAL：一半的格子本身就是藍底，藍字會消失。
       改用黑的降階 —— 兩種底色上都成立。 */
    .on-dark .svc-main .svc-grid .svc-tile:hover .svc-tile-name {
        color: hsl(var(--foreground) / .6);
    }
}

.on-dark .svc-main .svc-grid .svc-tile:focus-visible .svc-tile-name {
    color: hsl(var(--foreground) / .6);
}

.svc-main .svc-grid .svc-tile:focus-visible {
    outline-color: hsl(var(--foreground));
}

/* ═══════════════════════════════════════════════════════════════════════
   服務專區右半：物件直接貼邊、填滿整塊（2026-07-31 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「服務專區的右邊區塊不要留邊，每個物件直接貼邊填滿容器。」

   三個「邊」要一起收掉，少收一個就還是看得到縫：
     ① 左邊  兩欄之間的留白軌（--svc-gutter）→ 0，右半直接貼著黑色塊
     ② 右邊  右半原本停在內容格線上，離視窗右緣還有一段 →
             用負邊距把它推到視窗邊緣（算式與其他出血同一套）
     ③ 內部  主打服務與下方卡牌之間的 gap → 0，兩塊直接相接

   ⚠ 留白軌收成 0 之後，黑色塊的右緣必須跟著從「左欄 + 半條軌」改成
     「左欄」本身，否則黑塊會壓在右半上面 —— 所以 --svc-split 由 .28 改成 .26
     （＝ --svc-intro-w）。
   ⚠ Hero 的左右分界讀的是**同一個** --svc-split，所以它會自動跟著移到 26%，
     兩段的分界線仍然對在同一條垂直線上（那是客戶先前指定的）。
   ⚠ 負邊距的算式：(100% − 100vw) / 2，其中 100% ＝ .svc-layout 的寬。
     版面是置中的，所以這個值剛好等於「格線右緣到視窗右緣」的負距離。 */
:root {
    --svc-split: .26;
}

@media (max-width: 1023.98px) {
    :root { --svc-split: .23; }
}

.svc-layout {
    column-gap: 0;
}

.svc-main {
    gap: 0;
}

/* 主打服務與卡牌相接處不再有間距，圖片與色塊因此是連續的一整片 */
.svc-feature {
    padding-bottom: 0;
}

@media (max-width: 1023.98px) {
    /* 窄幅是上下排、沒有左右兩欄，出血與貼邊都不成立 */
    .svc-main {
        gap: 2.5rem;
    }
}

/* 修正：主打服務的 CTA 裡多出一個白方塊（2026-07-31 複查抓到）。
   ⚠ 成因：「純文字格」那一輪把 .svc-arrow 做成「淺底方塊按鈕」時**沒有加作用域**，
     結果連主打服務（顧問案）那顆深色 CTA 裡的箭頭也吃到 —— 深色按鈕裡蓋出一塊白，
     而箭頭字色是 inherit（淺色），白底淺字等於箭頭也不見了。
   ⚠ 那組樣式只該作用在網格的文字格上，這裡把它限縮回 .svc-grid，
     主打服務的箭頭回到原本的樣子（透明底、跟著按鈕的文字色）。 */
.svc-feature .svc-arrow {
    order: 0;
    width: 1.5rem;
    margin-top: 0;
    padding: 0;
    background: transparent;
    color: inherit;
}

/* 近期課程有五張卡之後，一次露的張數可以收窄回接近參考圖的密度
   （2026-07-31，接續「小卡三張增加到五張」）。
   ⚠ 先前設 2.5 是為了讓「只有三張」也捲得動；筆數變五張之後不必再犧牲卡片寬度，
     3.2 讓桌機一次看到三張多一點、右邊露出半張，與客戶提供的參考圖一致。 */
.course-cell {
    width: calc((100% - var(--course-gap) * 2) / 3.2);
}

@media (max-width: 1023.98px) {
    .course-cell { width: min(78%, 340px); }
}

/* 服務專區右半：上下也不留邊（2026-07-31 客戶指定「上下不要留邊」）。
   ⚠ 左右已經貼齊了，剩下的縫是**區塊自己的上下內距**
     （上 --section-y、下 --section-seam-band）——右半的內容停在內距裡面，
     所以圖片上方與卡牌下方各露出一條淺灰。
   ⚠ 用等量的負邊距把它撐進內距，做法與知識專區的圖片滿版完全相同。
     兩個負值分別對應上下兩個不同的內距，**不能共用一個值**（上下不對稱）。
   ⚠ 撐出去之後剛好與左邊黑色塊的上下緣切齊 ——
     黑塊的 ::before 用的正是同一組內距的負值（SSOT），兩邊永遠對得上。
   ⚠ 左欄（.svc-intro）維持在內距之內：那裡是文字，貼著區塊邊緣會很擠。
   ⚠ 負邊距不會把區塊撐破：grid 是 align-items:start，
     這一欄的高度貢獻本來就由它自己的內容決定。 */
.svc-main {
    margin-block: calc(var(--section-y) * -1) calc(var(--section-seam-band) * -1);
}

@media (max-width: 1023.98px) {
    /* 窄幅是上下排，沒有「右半」可言，出血一併取消 */
    .svc-main {
        margin-block: 0;
    }
}

/* 近期課程：底下的閱讀進度（2026-07-31 客戶指定「跟 hero 一樣」）。
   ⚠ 刻意與 Hero 的 .cal-meta／.cal-pageno／.cal-progress **用同一組數值**
     （字體、字級、字距、線的粗細與長度、已讀段的色）——
     兩個區塊的進度是同一個角色，長得一樣才是同一套設計語言。
     沒有直接共用 class 是因為 Hero 那組被桌機的直式覆寫綁住了（見上方），
     混用會把這裡也轉成直的。
   ⚠ 靠左對齊：這一區的標題與卡片都是靠左，進度列跟著同一條格線。 */
.course-meta {
    display: flex;
    align-items: center;
    gap: clamp(.75rem, 1.4vw, 1.25rem);
    margin-top: 2rem;
}

.course-pageno {
    display: flex;
    align-items: baseline;
    gap: .45rem;
    margin: 0;
    font-family: var(--font-en);
    font-size: .8125rem;
    line-height: 1;
    letter-spacing: var(--tracking-meta);
    color: hsl(var(--muted-foreground));
    font-variant-numeric: tabular-nums;   /* 位數等寬，01→02 時線不會左右跳 */
}

.course-pageno-cur { color: hsl(var(--heading)); }
.course-pageno-sep { opacity: .5; }

.course-progress {
    position: relative;
    display: block;
    width: clamp(90px, 9vw, 160px);
    height: 1px;
    background: hsl(var(--foreground) / .16);
}

.course-progress-bar {
    position: absolute;
    left: 0;
    top: 0;
    height: 1px;
    width: var(--course-progress, 0%);
    background: hsl(var(--foreground));
    transition: width var(--cal-dur-progress) var(--cal-ease);
}

@media (prefers-reduced-motion: reduce) {
    .course-progress-bar { transition: none; }
}

/* 服務網格的欄分割對齊上方主打服務的圖片左緣（2026-07-31 客戶指定）。
   ⚠ 兩者原本各走各的：
       .svc-feature  42fr / 58fr  → 圖片從 42% 開始
       .svc-grid     1fr  / 1fr   → 格子在 50% 分割
     差 8%，捲下來時看得出上下兩段的直線沒有接上。
   ⚠ 改成共用同一組比例 —— 主打的圖片左緣、格子的分隔線、
     以及下方每一列的分割，全部落在同一條垂直線上。
   ⚠ 比例抽成 --svc-inner-split，兩處都讀它：日後要調只改一個地方，
     不會又出現「改了上面忘了下面」。 */
.svc-layout {
    --svc-inner-split: 42fr;
    --svc-inner-rest:  58fr;
}

.svc-feature {
    grid-template-columns: minmax(0, var(--svc-inner-split)) minmax(0, var(--svc-inner-rest));
}

.svc-grid {
    grid-template-columns: minmax(0, var(--svc-inner-split)) minmax(0, var(--svc-inner-rest));
}

/* ═══════════════════════════════════════════════════════════════════════
   修正：服務專區右半溢出被裁掉 232px（2026-07-31 客戶回報「左邊大右邊小」）
   ═══════════════════════════════════════════════════════════════════════
   症狀：右欄的格子明顯比左欄窄，主打服務的圖片右側也少一截。

   成因：**負邊距的百分比基準抓錯層級。**
        原本寫的是 margin-right: calc((100% - 100vw) / 2)，
        直覺上 100% 是「格線的寬」——但 .svc-main 是 grid item，
        它的百分比基準是**自己的 grid area**（格線寬的 74%），不是整條格線。
        1920 視窗實測：
            期望  (1728 − 1920) / 2 = −96px
            實際  (1279 − 1920) / 2 = −320px      ← 多推了 232px
        於是右半整個往右溢出 232px，被 .service-section 的 overflow-x: clip 切掉。
        ⚠ 所以「右邊小」不是版面比例的問題，是**內容被裁掉**。
          先前改 42fr/58fr 只是把切割對齊了，裁切還在。

   解法：改用**不依賴百分比**的算式 —— 與 --footer-pad-x／--header-pad-x
        同一條規則：格線右緣到視窗右緣的距離
            max(--section-x, (100vw − --container-wide) / 2)
        這條算式在每個斷點都成立，而且不管掛在哪一層都算得對。 */
.svc-main {
    margin-right: calc(-1 * max(var(--section-x), (100vw - var(--container-wide)) / 2));
}

@media (max-width: 1023.98px) {
    .svc-main { margin-right: 0; }
}

/* ═══════════════════════════════════════════════════════════════════════
   服務專區右半的進場動態（2026-07-31 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   沿用全站唯一的動態語言 —— **矩形切割**：一塊與底色相同的簾幕蓋在物件上，
   進場時由右往左收掉，物件因此像被一把直尺刮出來。
   順序是交錯的：黑色塊裂開 → 主打圖片 → 五張卡牌依序，讀起來是一條線
   從左往右掃過整個區塊。

   ⚠ 用 ::before 當簾幕、以 scaleX 收掉，**不用 clip-path**：
     clip-path 會成為 fixed 後代的包含塊並裁切它 —— 卡牌的 hover 預覽小圖
     是 position:fixed，套 clip-path 會讓它永遠不出現（這個坑今天已經踩過一次）。
   ⚠ scaleX 不觸發版面重算，交錯五、六個物件也不會掉格。
   ⚠ 起始狀態掛在 .js-split 上（由 JS 掛）：JS 沒跑就沒有簾幕，
     區塊直接是最終樣子 —— 若把「蓋住」寫成 CSS 預設，JS 一掛掉整個右半會是白的。
   ⚠ 簾幕的顏色 ＝ 右半的底色（--background），所以收掉的過程看起來是
     「底色退開、露出內容」，不是「多一塊白色在動」。 */
.svc-layout.js-split .svc-feature-media::before,
.svc-layout.js-split .svc-tile::before {
    content: "";
    display: block;
    position: absolute;
    inset: 0;
    z-index: 3;
    background: hsl(var(--background));
    transform-origin: right center;
    pointer-events: none;
    transition: transform 640ms var(--cal-ease);
}

.svc-layout.js-split.is-in .svc-feature-media::before,
.svc-layout.js-split.is-in .svc-tile::before {
    transform: scaleX(0);
}

/* 交錯：主打圖片先，卡牌依序跟上（每張差 90ms，與全站進場的節拍相同） */
.svc-layout.js-split.is-in .svc-feature-media::before { transition-delay: 240ms; }
.svc-layout.js-split.is-in .svc-tile:nth-child(1)::before { }
.svc-layout.js-split.is-in .svc-tile:nth-child(2)::before { }
.svc-layout.js-split.is-in .svc-tile:nth-child(3)::before { }
.svc-layout.js-split.is-in .svc-tile:nth-child(4)::before { }
.svc-layout.js-split.is-in .svc-tile:nth-child(5)::before { }

@media (prefers-reduced-motion: reduce) {
    .svc-layout.js-split .svc-feature-media::before,
    .svc-layout.js-split .svc-tile::before {
        transform: scaleX(0);
        transition: none;
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   改成「矩形填色」（2026-07-31 客戶更正：不是簾幕退場，是矩形被顏色填滿）
   ═══════════════════════════════════════════════════════════════════════
   上一版做的是「與底色相同的簾幕由右往左收掉」＝ 露出。
   客戶要的是**填色**：色塊由左往右長進矩形裡，把格子填滿。
   兩者動作相反 —— 這一段把上一版覆蓋掉。

   ⚠ 卡牌的顏色抽成 --tile-fill：
       靜態（JS 沒跑）→ 直接畫在 .svc-tile 的 background 上，外觀正確
       有 JS       → .svc-tile 轉透明，顏色改由 ::before 從 scaleX(0) 長到 1
     兩條路徑共用同一個色值，不會有「動畫版與靜態版顏色不一致」的風險。
   ⚠ ::before 用 z-index:0、內容用 z-index:1 拉到它上面，
     **刻意不對 .svc-tile 用 isolation/contain**：那會讓 hover 預覽小圖
     （position:fixed，z-index 30）被關進格子自己的堆疊環境，
     被後面的格子蓋掉。維持在根堆疊環境裡它才永遠在最上面。
   ⚠ 主打圖片用 clip-path 由左往右揭開（那一塊沒有 fixed 後代，可以安全使用）；
     色塊填色與圖片揭開是同一個方向、同一條曲線，讀起來是一條線掃過整個區塊。 */

/* 卡牌顏色抽成變數（靜態外觀不變） */
.svc-tile:nth-child(4n + 1),
.svc-tile:nth-child(4n + 4) { --tile-fill: hsl(var(--nude)); }

.svc-tile:nth-child(4n + 2),
.svc-tile:nth-child(4n + 3) { --tile-fill: hsl(var(--accent-blue)); }

.svc-tile { background: var(--tile-fill); }

/* 內容疊在填色之上 */
.svc-tile-info { position: relative; z-index: 1; }

/* ── 有 JS 時：格子先是空的，顏色由左往右填進去 ────────────────────── */
.svc-layout.js-split .svc-tile {
    background: transparent;
}

.svc-layout.js-split .svc-tile::before {
    content: "";
    display: block;
    position: absolute;
    inset: 0;
    z-index: 0;
    background: var(--tile-fill);
    transform: scaleX(0);
    transform-origin: left center;
    pointer-events: none;
    transition: transform 620ms var(--cal-ease);
}

.svc-layout.js-split.is-in .svc-tile::before {
    transform: scaleX(1);
}

/* 主打圖片：同方向揭開 */
.svc-layout.js-split .svc-feature-media {
    clip-path: inset(0 100% 0 0);
    transition: clip-path 700ms var(--cal-ease) 220ms;
}

.svc-layout.js-split.is-in .svc-feature-media {
    clip-path: inset(0);
}

/* 交錯：圖片先，五張卡牌依序跟上（每張 90ms，與全站進場同一個節拍） */
.svc-layout.js-split.is-in .svc-tile:nth-child(1)::before { transition-delay: 420ms; }
.svc-layout.js-split.is-in .svc-tile:nth-child(2)::before { transition-delay: 510ms; }
.svc-layout.js-split.is-in .svc-tile:nth-child(3)::before { transition-delay: 600ms; }
.svc-layout.js-split.is-in .svc-tile:nth-child(4)::before { transition-delay: 690ms; }
.svc-layout.js-split.is-in .svc-tile:nth-child(5)::before { transition-delay: 780ms; }

@media (prefers-reduced-motion: reduce) {
    .svc-layout.js-split .svc-tile { background: var(--tile-fill); }
    .svc-layout.js-split .svc-tile::before { transform: scaleX(1); transition: none; }
    .svc-layout.js-split .svc-feature-media { clip-path: inset(0); transition: none; }
}

/* 修正：右欄格子的填色只有 1px 寬（2026-07-31 複查抓到）。
   ⚠ 成因是**舊的欄間分隔線還躺在同一個偽元素上**：
       .svc-tile:nth-child(even)::before { … width: 1px; background: var(--rule-on-dark); }
     那是改版前畫在欄距中央的垂直細線，後來被 display:none 收掉 ——
     但「矩形填色」把同一個 ::before 又打開成 display:block，
     於是它的 width: 1px 跟著復活。
   ⚠ inset: 0 蓋不掉 width：left/right 與 width 同時存在時，**width 優先**，
     元素就被壓成一條 1px 的直線（實測右欄兩格的 ::before 寬度就是 1px）。
     所以必須明寫 width/height: auto 把它交還給 inset。
   ⚠ 這是「偽元素被兩代設計共用」的典型後遺症。日後若要再用 .svc-tile::before
     做別的事，記得先確認舊規則有沒有留下尺寸類屬性。 */
.svc-layout.js-split .svc-tile::before {
    width: auto;
    height: auto;
    left: 0;
    right: 0;
}

/* ═══════════════════════════════════════════════════════════════════════



/* 內容本身不再淡出 —— 換頁的視覺全部交給色塊，內容在被蓋住的瞬間直接換掉
   （看不到過程，所以不需要過場）。 */

/* ── 箭頭切換 ────────────────────────────────────────────────────────
   ⚠ 這一段解除先前「任何情況下都不顯示」的設定（那是客戶當時改用
     游標圓框提示時的指定）。現在客戶要箭頭，兩顆回到畫面上。
   ⚠ 樣式與近期課程的輪播箭頭完全相同 —— 同一個角色就該是同一套語言。
     .carousel-arrow 的圓框底樣式一併歸零（那是更早的設計稿留下的）。 */

/* .carousel-arrow 用邊框畫的箭頭符號收掉，改用與課程列相同的字符 */




/* 頁碼列改成「頁碼 → 進度線 → 箭頭」一整列（箭頭已移進 .cal-meta，見 home.php） */

/* 游標式提示退場：改用箭頭之後它就多餘了，而且兩種提示同時存在會互相干擾。
   ⚠ 標記留著不刪（客戶若要換回游標提示，把這一條註解掉即可）。 */



/* 修正：Hero 箭頭變成斜線（2026-07-31 複查抓到）。
   ⚠ .carousel-arrow 原本的箭頭是**用邊框畫的**：一個 8×8 的方塊只留上、右兩邊，
     再 rotate(-135deg)／rotate(45deg) 轉成 ‹ ›。
     我改成文字字符（←→）之後，邊框那兩條已經被 border:0 收掉，
     但旋轉還在 —— 因為 `.carousel-arrow.prev::before` 的特異度是 (0,2,1)，
     蓋過我寫的 `.cal-arrow::before` (0,1,1)，於是**文字箭頭被轉了 45 度**。
   ⚠ 用 .hero 前綴補到 (0,2,1) 且位置在後面，才蓋得掉那兩條旋轉。 */

/* Hero 箭頭移到左右兩側（2026-07-31 客戶指定「放左右兩側，不要都擠在下面」）。
   ⚠ 頁碼與進度線**留在左下**不動 —— 客戶指定「左下」。
   ⚠ 絕對定位的基準是 .cal-frame（標記已搬回那一層，見 home.php）：
     一度放在 .cal-meta 裡時，包含塊變成 .cal-meta 自己（也是 absolute），
     兩顆就只能擠在那一列裡，做不出左右兩側。
   ⚠ 右側那顆壓在滿版圖片上，所以顏色不能跟著左半的深字走 ——
     圖片是淺色系插畫，深灰在上面對比夠（實測 8:1 以上），維持同一個色。
     日後若換成深色主視覺，這裡要改成反白。
   ⚠ hover 的位移要與 translateY(-50%) **合併寫**，不能只寫 translateX —— 
     transform 是整個被覆蓋的，只寫 X 會讓箭頭瞬間掉到下面 50%。 */
@media (min-width: 992px) {



    @media (hover: hover) {
        .cal-arrow--prev:hover { transform: translateY(-50%) translateX(calc(var(--hover-nudge) * -1)); }
        .cal-arrow--next:hover { transform: translateY(-50%) translateX(var(--hover-nudge)); }
    }

    /* 頁碼列回到「頁碼 + 進度線」兩項（箭頭已移出） */
}


/* ═══════════════════════════════════════════════════════════════════════
   Hero 箭頭移到 CTA 正下方 ＋ 停用點擊翻頁（2026-07-31 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「點擊圖片不要換圖片，按箭頭再切換；箭頭放標題的按鈕底下。」

   ⚠ 標記已移進每一頁的 .cal-text-item（緊接在 .cal-cta 之後，見 home.php）——
     要「緊貼在按鈕下方」，唯一穩的做法是與按鈕在同一個排版流裡。
     絕對定位做不到：文字區垂直置中，按鈕的 y 會隨標題折一行還是兩行而變。
   ⚠ 因此這一段要把上一輪「絕對定位到左右兩側」的設定整組收回
     （position/top/left/right/transform），否則它們會把新的排版流位置蓋掉。 */

@media (min-width: 992px) {

    @media (hover: hover) {
        .cal-arrow--prev:hover { transform: translateX(calc(var(--hover-nudge) * -1)); }
        .cal-arrow--next:hover { transform: translateX(var(--hover-nudge)); }
    }
}

/* 進場逐項淡入的延遲：CTA 之後多了一列箭頭，補上第 11 格的延遲，
   否則它的 transition-delay 會掉回 0ms、比前面的項目先出現。 */

/* 點擊翻頁停用之後，游標不要再說「這裡可以點」 */


/* 修正：Hero 上的滑鼠游標整個消失（2026-07-31 複查抓到）。
   ⚠ 成因：舊的自訂游標機制有一條
       .cal-frame:has(.cal-cursor.is-on) .cal-stage { cursor: none; }
     它的用意是「圓框提示出現時，把系統游標藏起來，由圓框代替」。
     圓框已經依客戶指定關掉（display:none），但 home.js 仍然照樣掛 .is-on，
     :has() 因此仍然成立 —— 結果是**藏了系統游標卻沒有任何東西代替**，
     滑到 Hero 上游標就不見了（實測 cursor 計算值為 none）。
   ⚠ 我先前寫的 .cal-stage { cursor: default } 蓋不掉它：
     那條是 (0,1,0)，:has() 會把引數的特異度算進去，原規則是 (0,4,0)。
     這裡用同樣的選擇器補到同特異度、位置在後面才贏。
   ⚠ 紙上的連結與按鈕維持 pointer（下一條），不受影響。 */


/* Hero 左半文字與其他區塊主標齊頭（2026-07-31 客戶指定）。
   ⚠ 原本多推了 clamp(2rem, 2.2vw, 4.5rem)，那是**直式頁碼欄還在左邊時**
     為它讓出來的位置。頁碼已經改成橫式、移到左下並貼齊 --hero-pad-x，
     那段讓位就沒有理由存在了，反而讓 Hero 標題比服務專區／知識專區的主標
     往右凸出約 42px。
   ⚠ --hero-pad-x 與服務專區格線是**同一條算式的兩種寫法**，1920 實測都是 88.5px：
       Hero    max(--section-x, (100cqw − --container-max) / 2)
       服務    --section-x + (100% − min(--container-wide + 2×--section-x, 100%)) / 2
     所以直接吃 --hero-pad-x 就會對齊，不必寫死數字。 */

/* 頁碼／進度線也跟著齊頭（2026-07-31，接續上一條）。
   ⚠ 這一列先前是「跟著文字走」，所以也帶了那段 42px 的讓位偏移。
     文字已經移回格線，這一列沒跟著改的話就會變成整個 Hero 只有它凸出來
     （實測 130.73 vs 88.5）。兩者現在讀同一個 --hero-pad-x。 */

/* 頁碼／進度線移到最右邊（2026-08-10 客戶指定「數字條移到最右邊」）。
   ⚠ 只翻轉水平位置：把上一條的 left 換成 right，一樣吃 --hero-pad-x
     （與左側同一條格線的鏡射版），右緣因此對齊 Hero 內容區的右緣；
     position/bottom 等其餘定位延續「Hero 頁碼移到按鈕下方」那一輪「留在
     左下」的設定不動，現在只是變成「留在右下」。
   ⚠ 這一條放在「頁碼／進度線也跟著齊頭」之後，用 left:auto 蓋掉它的
     left，不刪那一條——退回時只要把這裡註解掉即可。
   ⚠ 沒有動 .cal-pageno／.cal-progress 內部的排列順序（仍是頁碼在前、
     進度線在後）：客戶只要求搬位置，不是重新設計內部排列。 */

/* 2026-08-10 客戶指定「線延伸拉寬跟圖片一樣寬，數字放在線底下，數字靠右，
   僅線的長度改變跟數字改上下排」。
   ⚠ 寬度吃 --cal-w 的一半：.cal-content 是左文右圖的 1fr/1fr 兩欄（gap:0，
     見該處），右欄（.cal-media，圖片）正好是 --cal-w 的一半，這裡直接算
     同一個值，兩者永遠同寬，不必另外量。
   ⚠ right 改回 0（貼齊 .cal-frame 右緣）：圖片本身滿版貼右緣
     （.cal-media { padding:0 }），要跟圖片同寬同右緣就不能再吃
     --hero-pad-x 那圈內距，否則線會比圖片窄一截、右邊留一條空白——
     上面「移到最右邊」那條的 right 因此在這裡被蓋掉。
   ⚠ 換成上下排（線在上、頁碼在下）：.cal-progress 用 order:-1 搬到視覺
     最前面，標記本身（先頁碼後線）完全沒有動，退回時把 order 那行拿掉即可。
   ⚠ 頁碼靠右：align-items:flex-end——線本身已經跟容器同寬（100%），
     這個對齊實際上只影響頁碼那一小塊文字。 */

/* 2026-08-10 客戶指定「不要完全靠右，讓他在上面的字級大小同樣位置」：
   線維持跟圖片同寬、貼齊圖片右緣（上面那條不動），但頁碼那一小塊文字
   收回來，跟 Hero 左頁文字（.cal-text-item）同一個右側內距對齊——也就是
   退回「移到最右邊」那一輪原本用的 --hero-pad-x，不再貼死在圖片邊緣上。
   ⚠ 只加在 .cal-pageno 自己身上（margin-right），不動 .cal-meta 的
     align-items/width：線的滿版寬度與右緣完全不受影響。 */

/* 2026-08-10 客戶指定「線條也不要完全靠右，請跟上面的（頁碼）對齊」：
   線的右緣收回來跟頁碼的右緣對齊在同一條線上（頁碼右緣＝容器右緣 −
   --hero-pad-x，見上一條）。
   ⚠ align-self:flex-start 蓋掉 .cal-meta 的 align-items:flex-end——
     不這樣做的話 100% − --hero-pad-x 這個寬度仍然會被父層貼著容器右緣，
     右緣還是落在圖片邊上，跟頁碼對不起來；改成貼左之後，寬度直接決定
     右緣落在哪，才會跟頁碼同一條線。左緣因此回到容器最左（＝圖片左緣），
     線仍然很寬，只是右邊收了 --hero-pad-x 那一點。 */

/* 服務專區左欄的「查看全部服務」移除（2026-07-31 客戶指定）。
   ⚠ 用 display:none，**標記與連結完全沒有刪** —— 與本檔其他「收起來但不刪」
     的做法一致（例如 Hero 左頁的 .cal-sub／.cal-fit-list）。
     客戶若之後要把它加回來，把這一條註解掉就完全復原，
     不必去 home.php 重寫一次按鈕與它指向的網址。
   ⚠ 左欄剩下「英文小標 → 區塊主標 → 介紹」三項，介紹的下邊距因此
     不再需要為按鈕預留，一併收掉。 */
.svc-intro-more {
    display: none;
}

.svc-intro-desc {
    margin-bottom: 0;
}

/* Footer 那一欄少了標題之後，清單要補回標題原本佔的上緣位置，
   否則它會比隔壁「訂閱電子報」欄的第一行高出一截、頂線對不齊。 */
.footer-col:not(:has(.footer-col-title)) .footer-col-list {
    margin-top: 0;
}

/* ═══════════════════════════════════════════════════════════════════════
   Footer 改成橫向排列（2026-07-31 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   連結由「兩欄各自直向堆疊」改成**一列一列橫著排**，讀起來與 header 的
   導覽同一個節奏（那也是橫的）。

   ⚠ .footer-main 由 grid 改成 flex：
     grid 的 grid-auto-columns: 1fr 會把每一欄壓成等寬（實測 380px），
     六個連結橫排需要約 560px —— 在等寬欄裡一定會折成三行。
     flex 讓每一組連結依**自己的內容**決定寬度，橫排才排得開。
   ⚠ 訂閱那一欄用 margin-left:auto 推到最右：flex 沒有 grid 的「最後一欄」概念，
     不推的話它會緊接在連結後面，右邊空一大塊。
   ⚠ 連結列自己也要 flex-wrap：視窗變窄時讓它折行，而不是把整個 footer 撐破。
   ⚠ 欄與欄之間的分隔線與左內距一併收掉 —— 橫排之後那條線會落在
     「兩組連結中間」而不是欄邊界上，看起來像斷句錯誤。 */
.footer-main {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    column-gap: clamp(2rem, 4vw, 4.5rem);
    row-gap: 2.5rem;
    grid-template-columns: none;
    grid-auto-flow: row;
}

.footer-brand {
    flex: 0 0 auto;
    padding-right: 0;
}

.footer-col {
    flex: 0 1 auto;
    padding-left: 0;
    border-left: 0;
}

.footer-subscribe {
    flex: 0 1 auto;
    margin-left: auto;
}

/* 連結本身橫排 */
.footer-col-list {
    flex-direction: row;
    flex-wrap: wrap;
    column-gap: clamp(1rem, 1.6vw, 1.75rem);
    row-gap: .5rem;
}

/* 窄幅：訂閱不再靠右（會被擠成一條縫），改成自己一列 */
@media (max-width: 1023.98px) {
    .footer-subscribe {
        flex: 1 1 100%;
        margin-left: 0;
        padding-top: 0;
        border-top: 0;
    }

    .footer-form { max-width: 460px; }
}

/* 修正：訂閱欄被擠到第二列（2026-07-31 複查抓到）。
   ⚠ 1568 視窗實測：品牌 233 + 連結 495 + 連結 225 + 訂閱 300 = 1253，
     再加三個 63px 的欄距 = 1441，比可用的 1409 多出 32px → 換行。
   ⚠ flex item 的預設 min-width 是 **auto**（＝內容的最小寬），
     所以即使 flex-shrink 是 1，那兩組連結也**縮不下去**。
     補上 min-width: 0 才允許它們縮，縮到不夠時連結自己會折行（本來就 flex-wrap）。
   ⚠ 欄距同時收一階 —— 只靠 min-width 會讓連結折行折得太早。 */
.footer-main {
    column-gap: clamp(1.5rem, 3vw, 3.5rem);
}

.footer-col {
    min-width: 0;
}

/* ═══════════════════════════════════════════════════════════════════════
   Footer 收回橫排、回到直式分欄（2026-07-31 客戶提供參考圖）
   ═══════════════════════════════════════════════════════════════════════
   參考圖的版型是：品牌在左 → 幾欄「欄標題 + 直式清單」→ 訂閱在右。
   上一輪的「橫向排列」整組收回，以參考圖為準。

   ⚠ 這一段把 .footer-main 從 flex 換回 grid，所以**上一輪動過的每一個屬性
     都要明確寫回來**（display／grid-template-columns／grid-auto-flow／
     grid-auto-columns／gap／align-items），不能只改 display ——
     殘留的 flex 屬性在 grid 下不生效，但 column-gap／row-gap 會繼續作用，
     欄距會變成上一輪收窄過的值。
   ⚠ 欄標題「服務專區」在 _footer.php 補回（參考圖每一欄都有標題）；
     第二欄維持無標題，客戶先前明確指定「導覽刪掉」。
     無標題那一欄的清單 margin-top 已經是 0（見稍早的規則），
     第一行會與隔壁欄的欄標題齊頭，看起來是刻意的，不是漏了。 */
.footer-main {
    display: grid;
    grid-template-columns: minmax(0, 1.55fr);
    grid-auto-flow: column;
    grid-auto-columns: minmax(0, 1fr);
    align-items: start;
    column-gap: 0;
    row-gap: 0;
}

.footer-brand {
    padding-right: clamp(1.5rem, 3vw, 3rem);
}

.footer-col {
    padding-left: clamp(1.25rem, 2.6vw, 2.75rem);
    min-width: 0;
}

.footer-subscribe {
    margin-left: 0;
}

/* 清單回到直式 */
.footer-col-list {
    flex-direction: column;
    flex-wrap: nowrap;
    column-gap: 0;
    row-gap: .875rem;
}

@media (max-width: 1023.98px) {
    .footer-main {
        grid-template-columns: minmax(0, 1.4fr) repeat(2, minmax(0, 1fr));
        grid-auto-flow: row;
        row-gap: 3rem;
    }

    .footer-subscribe {
        grid-column: 1 / -1;
        padding-left: 0;
        padding-top: 2.5rem;
    }

    .footer-form { max-width: 460px; }
}

@media (max-width: 767.98px) {
    .footer-main {
        grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
        row-gap: 2.75rem;
    }

    .footer-brand { grid-column: 1 / -1; padding-right: 0; }
}

/* Footer：指定的那一欄改成橫排（2026-07-31 客戶指定
   「實體課程跟知識專區橫向排」）。
   ⚠ 修飾 class 由**資料**帶出來（見 _footer.php 的 'inline' => true），
     不是在這裡用 :nth-child 挑第幾欄 —— 欄位順序日後一調，
     nth-child 會安靜地挑到別欄，不會噴任何錯。
   ⚠ 只有這一欄橫排，服務專區那一欄維持直式（六筆橫排會折成兩三行）。 */
.footer-col-list--inline {
    flex-direction: row;
    flex-wrap: wrap;
    column-gap: clamp(1rem, 1.6vw, 1.75rem);
    row-gap: .5rem;
}

/* Footer 文字間距放鬆（2026-07-31 客戶：「文字間距大一點，太擠了」）。
   ⚠ 「擠」是四個地方一起造成的，只調其中一個看不出差別：
       ① 清單項與項之間的距離     .875rem → 1.25rem
       ② 每一行自己的行高         1.71    → 2（中文在小字級下靠行高呼吸）
       ③ 欄標題到第一項的距離     1.5rem  → 1.875rem
       ④ 橫排那一欄的左右間距     ~1.2rem → ~2rem
     ⚠ 無標題那一欄的清單 margin-top 仍然是 0（它要與隔壁欄的欄標題齊頭），
       所以 ③ 不能寫在 .footer-col-list 的通用規則上而不管那一條 ——
       這裡放在後面，且沒有覆蓋掉 :not(:has(...)) 那一條的作用域。 */
.footer-col-list {
    row-gap: 1.25rem;
    margin-top: 1.875rem;
}

.footer-col-list a {
    line-height: 2;
}

.footer-col-list--inline {
    column-gap: clamp(1.5rem, 2.2vw, 2.25rem);
    row-gap: .75rem;
}

/* 無標題欄仍然齊頭（這一條必須排在上面之後，否則會被 margin-top: 1.875rem 蓋掉） */
.footer-col:not(:has(.footer-col-title)) .footer-col-list {
    margin-top: 0;
}

/* 品牌欄與訂閱欄的段落也一起放鬆，整個 Footer 的呼吸才一致 */
.footer-slogan   { margin-top: 2.5rem; line-height: 2.1; }
.footer-social   { margin-top: 2.5rem; gap: 1.25rem; }
.footer-sub-desc { margin-top: 1.875rem; line-height: 2.1; }
.footer-form     { margin-top: 2rem; }

/* 橫排那一欄再放鬆（2026-07-31 客戶：「實體課程跟知識專區太緊了」）。
   ⚠ 上一輪只把它從 ~19px 加到 36px，兩個四字詞並排時仍然像同一個詞組。
     中文詞與詞之間要讀得出是兩個獨立項目，間距大約要有一個字寬（15px）的
     三倍以上 —— 這裡給到 56px（約 3.7 個字寬）。
   ⚠ 直式那一欄不受影響（它用的是 row-gap，不是 column-gap）。 */
.footer-col-list--inline {
    column-gap: clamp(2rem, 3vw, 3.5rem);
}

/* 橫排那兩項的字級與「服務專區」欄標題同一級（2026-07-31 客戶指定）。
   ⚠ 它們與欄標題排在**同一行**上，字級卻小一階，看起來像是欄標題底下的
     附屬項目 —— 但實際上它們與「服務專區」是同一層的導覽（header 上就是並排的）。
     對齊字級之後，這一行讀起來才是三個平行的項目。
   ⚠ 字重也一起對齊：只放大字級、字重仍是內文級的話，
     旁邊 450 的欄標題會顯得明顯較重，反而更像主從關係。
   ⚠ 顏色**維持連結色**（比標題淡一階）：它們是連結，hover 要有轉深的空間；
     一開始就用標題的全黑，hover 就沒有東西可以變了。
   ⚠ 只作用在 --inline 那一欄，服務專區底下的六筆維持內文級。 */
.footer-col-list--inline a {
    font-size: var(--text-sub);
    line-height: var(--lh-sub);
    font-weight: var(--fw-normal);
}

/* 修正：那兩項比欄標題**還粗**（2026-07-31 複查抓到）。
   ⚠ 我寫 var(--fw-normal) 想「對齊欄標題的字重」，但欄標題吃的是
     var(--fw-heading) —— 而 Editorial 改版把標題字重刻意壓細了（Footer 內是 350），
     --fw-normal 反而是 450。結果是連結 450 壓過標題 350，主從整個顛倒。
   ⚠ 這是「兩個 token 名字聽起來都像標題」造成的誤用，
     要對齊標題就要讀標題實際用的那一個。 */
.footer-col-list--inline a {
    font-weight: var(--fw-heading);
}

/* ═══ Footer 段落節奏統一（2026-07-31 客戶：「文字間段落比重不一致」）═══

   改版前實測（ink gap ＝ 上一段字身底到下一段字身頂，行距的上下留白已扣除）：
       品牌欄   logo → 標語        45.3
       品牌欄   標語 → 社群 icon   45.3
       服務專區 欄標題 → 第一項    36.8   ← 同一列，卻比品牌欄少 8.5（差 23%）
       訂閱欄   欄標題 → 說明      37.6   ←
       訂閱欄   說明 → 表單        37.3   ←
       服務專區 項與項              29.0
   兩個毛病疊在一起：
     ① 同一列三欄的「段落間距」不一樣（45 / 37 / 37）—— 客戶看到的就是這個
     ② 段落間距（36.8）只比行間距（29）大 27%，欄標題因此讀起來像清單的第一項
   做法：段落層級一律 45、行層級 29 → 30.6，比例 1.5，段與行的層級才分得開。
     ⚠ 只放不收：上一輪客戶才說「太擠了」，所以段落取三欄中最鬆的那個值（45），
       把另兩欄拉上來，沒有任何一處比改版前更緊。

   ⚠ 對齊的是**視覺間距**，不是 margin —— 各段行高不同，同樣的 margin 視覺上會差 3px：
       欄標題 18px/1.7 的上下留白各 2.3px、內文 15px/2.1 各 5.3px、圖與控件 0。
     margin = 45 − 上一段留白 − 下一段留白，所以只會有兩個值：
       圖／icon／表單 那一側（0 + 5.3）：45 − 5.3 = 39.7 → 2.5rem
       欄標題 → 內文（2.3 + 5.3）      ：45 − 7.6 = 37.4 → 2.375rem
     實測結果五處都落在 45.3〜45.6，肉眼等距。

   ⚠ 內文行高一律 2.1：原本清單是 2、標語與訂閱說明是 2.1，同一個 Footer 裡有兩種
     內文行距，文字一折行（窄螢幕）就看得出來。取較鬆的那個。
   ⚠ 深灰版權列的字級行高不動 —— 那是另一個底色帶，字重本來就另外補償過（310/410）。
   ⚠ 無標題那一欄（實體課程／知識專區）的 margin-top: 0 不受影響：
     .footer-col:not(:has(.footer-col-title)) .footer-col-list 是三個 class，
     比下面的 .footer-col-list 權重高，順序在前也照樣贏（複查它仍與欄標題齊頭）。 */

/* 內文行高（直式清單／標語／訂閱說明）。
   ⚠ 選擇器一定要排除 --inline 那一欄：`.footer-col-list a` 與
     `.footer-col-list--inline a` 權重完全相同（都是一個 class + 一個標籤），
     寫在檔案後面就會蓋掉它的 line-height: var(--lh-sub)。
     那一欄的行高是**與欄標題齊頭**的關鍵（同為 18px／1.7，行框一樣高）；
     改成 2.1 會讓行框長高 7.2px，那兩項就整體往下沉 3.6px，
     與「服務專區／訂閱電子報」不齊 —— 第一版寫成通用選擇器，複查量到就是這個。 */
.footer-col-list:not(.footer-col-list--inline) a,
.footer-slogan,
.footer-sub-desc {
    line-height: 2.1;
}

/* 段落層級 —— 視覺間距 45 */
.footer-slogan   { margin-top: 2.5rem; }
.footer-social   { margin-top: 2.5rem; }
.footer-col-list { margin-top: 2.375rem; }
.footer-sub-desc { margin-top: 2.375rem; }
.footer-form     { margin-top: 2.5rem; }

/* 行層級 —— 視覺間距 30.6。直式清單維持 1.25rem（15px／2.1，留白 5.3 × 2）；
   橫排那一欄是 18px／1.7（留白只有 2.3），box 要給到 1.625rem 才是同一個視覺間距。
   ⚠ 只在折行時看得到（桌機兩項同一行），但折了就要對得上直式那一欄。 */
.footer-col-list--inline {
    row-gap: 1.625rem;
}

@media (max-width: 767.98px) {
    /* 版權列在窄螢幕改直排，兩行的距離對齊行層級（原本 .75rem ＝ 視覺 21，偏緊） */
    .footer-bottom {
        gap: 1.25rem;
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   知識專區再收高 ＋ 大圖不要再被裁（2026-08-03 客戶回報）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「知識專區高度太高，圖片被裁切到。」

   實測（1536×694）改版前：
       區塊高 650｜大圖框 649×650（幾乎是正方形）｜素材 1760×1328 ＝ 1.325 橫圖
       → cover 只能靠「放大到填滿高度」來蓋住框，**左右各裁掉 106px（24.7%）**
   ⚠ 上一輪的註解已經預告過這個代價（「cover 會裁掉上下，約 28%」），
     只是當時算成上下、實際是左右 —— 框比素材更「直」，被犧牲的一定是寬。

   成因是三件事疊起來，所以三件一起處理，只調其中一件都拉不回來：
     ① 列高由右欄文章撐出來（546）—— 左欄內容只有 364，不是它
     ② 圖片用負邊距吃掉上下內距（+104），框子又比列高更高一截
     ③ 中欄只佔 47%，框太窄 → 同樣的高度下橫圖更填不滿
   對策：內距收一階 ＋ 右欄段落間距收一階 ＋ 中欄加寬一階。

   改版後（同一個視窗）：區塊高 650 → 539（−17%）、裁切 24.7% → 5.6%。
   ⚠ 為什麼不是 0：框高 ＝ 列高 ＋ 內距，而列高是右欄「文章有多長」決定的，
     這一段文字在 1536 寬的欄位裡就是要 494px。要完全零裁切只有兩條路 ——
     (a) 圖片改回固定比例、上下留出灰邊（就不再與黑塊切齊）
     (b) 減文字。兩個都要動到已經確認的東西，所以沒有自作主張，先做到 5.6%。
   ⚠ 這一段只動間距與軌寬，**沒有動任何字級、行高與文字內容**。 */

.knowledge-section {
    --kz-pad-top:    clamp(1.5rem, 2.6vh, 2.5rem);   /* 原 clamp(3rem, 6vh, 5rem) */
    --kz-pad-bottom: clamp(1.75rem, 3vh, 2.75rem);   /* 原 clamp(3.5rem, 7vh, 6rem) */
}

/* 右欄段落節奏收一階（合計 −52px）。只動 margin，字級行高不動。 */
.kz-title       { margin-top: 1.25rem; }    /* 原 1.75rem */
.kz-rule        { margin-top: .875rem; }    /* 原 1.25rem */
.kz-desc        { margin-top: 1rem; }       /* 原 1.5rem  */
.kz-body        { margin-top: .5rem; }      /* 原 .75rem  */
.kz-links       { margin-top: 1.375rem; }   /* 原 2.25rem */
.kz-detail-more { margin-top: 1.25rem; }    /* 原 2rem    */

/* ═══════════════════════════════════════════════════════════════════════
   Footer：四個標題之間改成等距（2026-08-03 客戶回報）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「服務專區跟實體課程字的距離太遠，實體課程跟知識專區又太近。」
        「知識專區離訂閱電子報太遠，請讓 footer 的連結標題之間的間距一致。」

   實測（1920）改版前：服務專區→實體課程 290｜實體課程→知識專區 56｜知識專區→訂閱 150
   —— 四欄等寬 1fr，可是每一欄的內容長度差很多（服務專區欄最長的一項只有 80px，
   欄寬卻有 300），剩下的空白就全部堆在標題與標題之間，而且每一欄堆的量還不一樣。

   做法：**欄寬改成貼合內容（max-content），間距改由同一個變數統一給**。
       欄距（--footer-nav-gap）      服務專區→實體課程、知識專區→訂閱電子報
       橫排欄的 column-gap（同一個值）實體課程→知識專區
     三段距離讀的是同一個變數，所以「一致」是結構保證的，不是調到剛好。
   ⚠ 品牌欄與訂閱欄維持 fr，多出來的寬度由它們吸收 —— 導覽那兩欄不該因為
     視窗變寬就把標題推得更開，那正是這次要修掉的毛病。
   ⚠ 只在 ≥1200 生效：以下是兩欄／單欄堆疊，欄距沒有意義。 */
:root {
    --footer-nav-gap: clamp(2.5rem, 3.8vw, 4.5rem);
}

@media (min-width: 1024px) {
    .footer-main {
        grid-template-columns:
            minmax(0, 1.55fr) max-content max-content minmax(0, 1fr);
    }

    .footer-col {
        padding-left: var(--footer-nav-gap);
    }

    .footer-col-list--inline {
        column-gap: var(--footer-nav-gap);
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   服務專區左欄介紹文收窄（2026-08-03 客戶：「介紹文字寬度縮短一點」）
   ═══════════════════════════════════════════════════════════════════════
   ⚠ 收的是**行長**不是欄寬：欄寬一動，右邊的色塊交界（--svc-split）
     與主打服務整塊都要跟著算。這裡只改 max-width，版型零影響。
   24em ＝ 360px（3 行）→ 20em ＝ 300px（4 行）。 */
.svc-intro-desc {
    max-width: 20em;
}

/* ═══════════════════════════════════════════════════════════════════════
   服務專區服務卡：膚色／藍色 → 淺灰／白（2026-08-03 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「服務專區的色塊改成淺灰色跟白色。」
        隨即補充：「服務專區的左邊是深灰色，右邊的膚色跟藍色區塊才是淺灰色。」

   ⚠ 所以要換的**不是左右兩大塊**，是右半那一排交錯上色的服務卡：
       原本 4n+1／4n+4 ＝ NUDE 膚色、4n+2／4n+3 ＝ AERIAL 藍
       改成 4n+1／4n+4 ＝ 淺灰、    4n+2／4n+3 ＝ 白
     左半維持深色塊（黑已由 token 換成 SLATE #7C7D80），右半維持頁面底 #F5F5F7，
     兩者一格都沒動 —— 這也讓膚色與藍色從「大面積底色」退回「按鈕與標籤」，
     與同一天「膚色跟藍色請用在按鈕 hover 跟標籤按鈕」那條指定一致。
   ⚠ 淺灰不能直接用 --background：右半本身就是它，同色的卡等於看不見。
     用 --surface-grey（#DCDCE1，就是 --muted 換白之前的那階灰），
     壓在 #F5F5F7 上差 26 階，看得出是一塊卡。
   ⚠ 兩條路徑都要改：靜態外觀（--tile-fill）與 JS 進場動畫共用同一個變數，
     只改一處會出現「動畫版與靜態版不同色」。這裡改的就是那個共用變數。
   ⚠ 文字維持黑：黑壓 #DCDCE1 是 15.1:1、壓白 21:1，兩張卡都比原本更好讀。 */
.svc-tile:nth-child(4n + 1),
.svc-tile:nth-child(4n + 4) { --tile-fill: hsl(var(--surface-grey)); }

.svc-tile:nth-child(4n + 2),
.svc-tile:nth-child(4n + 3) { --tile-fill: hsl(0 0% 100%); }

/* 編號與箭頭拿掉方框（2026-08-03 客戶：「服務專區旁邊的數字跟箭頭不要方框」）。
   ⚠ 兩者原本都是「淺底小方塊」（編號是籤條、箭頭是前往按鈕）。卡片換成灰白之後，
     淺底方塊壓在淺底卡上本來就只剩一圈幾乎看不見的邊 —— 拿掉方框反而更乾淨。
   ⚠ 只拿掉底色與內距，**位置（order／margin）一格都沒動**，版面不會位移。
   ⚠ hover 的反相也要一起收掉：底色沒了還留著 hover 填色，會憑空長出一個方塊。
   ⚠ 箭頭改用文字色的降階，hover 轉成全黑 —— 仍然看得出它有互動。 */
.svc-main .svc-grid .svc-tile-no,
.svc-main .svc-grid .svc-arrow {
    padding: 0;
    background: transparent;
}

.svc-main .svc-grid .svc-tile-no { color: hsl(var(--slate)); }
.svc-main .svc-grid .svc-arrow   { color: hsl(var(--foreground) / .55); }

@media (hover: hover) {
    .svc-tile:hover .svc-arrow {
        background: transparent;
        color: hsl(var(--foreground));
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   近期課程小卡改成描邊卡（2026-08-03 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「近期課程小卡背景改成深灰色描邊 0.5px。」

   ⚠ 描邊色用 --slate（SLATE #7C7D80）—— 這次色票裡的「深灰」就是它，
     不另外調一個新的灰出來（SSOT）。
   ⚠ 欄間那條垂直細線要收掉：卡片自己有框之後，兩者會變成雙線並排。
     只在這一區收，.course-cell::before 的規則本身沒有動。
   ⚠ 圖片貼齊框內側（不給上／左／右內距），文字才另外給左右內距 ——
     卡片的視覺重心仍然是圖，跟改版前一致。 */
.course-section .course-link {
    background: transparent;
    border: .5px solid hsl(var(--slate));
    padding-bottom: 1.5rem;
}

.course-section .course-cell::before {
    display: none;
}

.course-section .course-date,
.course-section .course-title,
.course-section .course-place {
    padding-inline: 1.25rem;
}

/* ═══════════════════════════════════════════════════════════════════════
   按鈕配色全部交給 AERIAL 藍與 NUDE 膚色（2026-08-03 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「膚色跟藍色請用在按鈕 hover 效果跟標籤按鈕這些。」
        「所有黑按鈕都改成藍色 hover 膚色，在深色背景改成按鈕膚色 hover 藍色。」

   規則只有一條，隨腳下的色塊翻面：
       淺底色塊  按鈕 AERIAL 藍  → hover NUDE 膚色
       深底色塊  按鈕 NUDE 膚色  → hover AERIAL 藍
   ⚠ 抽成 --btn-fill／--btn-fill-hover 兩個變數，由**色塊自己宣告**，
     按鈕本身只寫 var(--btn-fill) —— 與站上既有的 --surface-here／--ink-here
     同一套做法。日後多一種色塊，只要多宣告這兩個值，所有按鈕自動跟上。
   ⚠ 文字一律黑：黑壓 NUDE 16.8:1、黑壓 AERIAL 10.5:1，兩色都是淺色。
   ⚠ 空心膠囊（查看更多／查看全部）的「黑按鈕」指的是右側那顆實心圓徽章：
     靜置徽章 ＝ --btn-fill，hover 整顆膠囊填 --btn-fill-hover、徽章換回 --btn-fill，
     所以兩個狀態都還看得見那顆圓。
   ⚠ 標籤按鈕（分類／hashtag）的靜置色本來就是 NUDE／AERIAL，不動。 */

:root {
    --btn-fill:       hsl(var(--accent-blue));
    --btn-fill-hover: hsl(var(--nude));
}

/* ⚠ 2026-08-03 追加指定（客戶附兩張對照圖）：「按鈕請全部改成這樣底色藍色，
   hover 膚色。」—— 收回上一句「深色背景改成按鈕膚色 hover 藍色」的翻面，
   深底淺底一律同一組色，全站按鈕只有一種長相。
   ⚠ 所以這裡**不再**針對 .on-dark／.kz-intro／.footer-bottom 覆寫 —— 上面
     :root 那一組就是全站唯一的答案。留這段註解是為了讓下一個人知道
     「翻面」是被明確撤掉的，不是漏寫。
   ⚠ 深色塊上的藍鈕對比：AERIAL #B0C7DD 壓 SLATE #7C7D80 是 2.7:1，
     按鈕本身還看得出是一塊，但它與底色的分界比淺底上弱 ——
     這是統一長相換來的代價，客戶看過對照圖後指定的。
     鈕上的黑字壓 AERIAL 仍是 10.5:1，字始終讀得到。 */

/* ── 實心按鈕：靜置 --btn-fill，hover 換 --btn-fill-hover ───────────── */
.footer-submit,
.kz-links a,
.svc-feature-tail {
    background: var(--btn-fill);
    color: hsl(var(--heading));
}

.footer-submit:hover,
.footer-submit:focus-visible,
.kz-links a:hover,
.kz-links a:focus-visible,
.svc-feature:hover .svc-feature-tail,
.svc-feature:focus-visible .svc-feature-tail {
    background: var(--btn-fill-hover);
    color: hsl(var(--heading));
}

/* ── 「查看更多」這一類原本的空心膠囊，一併改成實心藍鈕 ──────────────
   客戶（附截圖）：「連查看更多的按鈕都一併將描邊改成藍色按鈕 hover 膚色。」
   ⚠ 外框不是拿掉、是**與底色同色**：框寬留著，按鈕的尺寸與行高完全不變，
     版面一格都不會位移（拿掉 border 會少 2px，四周的對齊線全部要重算）。
   ⚠ 徽章改成黑色圓點：藍底上再放藍點就看不見了。
     靜置只露出點、hover 長成整顆圓並把箭頭淡進來 —— 這段動態維持原樣。 */
.btn-pill,
.on-dark .btn-pill,
.course-section .btn-pill,
.kz-more {
    background: var(--btn-fill);
    border-color: var(--btn-fill);
    color: hsl(var(--heading));
}

.btn-pill::after,
.on-dark .btn-pill::after,
.course-section .btn-pill::after {
    background: hsl(var(--foreground));
}

.btn-pill:hover,
.btn-pill:focus-visible,
.on-dark .btn-pill:hover,
.on-dark .btn-pill:focus-visible,
.course-section .btn-pill:hover,
.course-section .btn-pill:focus-visible,
.kz-more:hover,
.kz-more:focus-visible,
.tag-pill:hover,
.tag-pill:focus-visible {
    background: var(--btn-fill-hover);
    border-color: var(--btn-fill-hover);
    color: hsl(var(--heading));
}

/* hover：整顆換成膚色，徽章維持黑圓、箭頭改用膚色才透得出來 */
.btn-pill:hover::after,
.btn-pill:focus-visible::after,
.on-dark .btn-pill:hover::after,
.on-dark .btn-pill:focus-visible::after,
.course-section .btn-pill:hover::after,
.course-section .btn-pill:focus-visible::after,
.kz-more:hover::after,
.kz-more:focus-visible::after {
    background: hsl(var(--foreground));
    color: var(--btn-fill-hover);
}

/* ═══════════════════════════════════════════════════════════════════════
   知識專區：左欄只留編號、右欄標籤與按鈕改描邊（2026-08-03 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「知識專區左邊你保留數字就好，數字旁邊的課程標題就不用了。
          知識專區右邊的標籤跟按鈕都改灰框描邊不要填色，除了查看更多的按鈕。」

   ── 左欄清單只留編號 ──────────────────────────────────────────────
   ⚠ 標題是**視覺上隱藏**，不是拿掉：這一列是 role="tab" 的切換鈕，
     只留「01」的話讀屏會唸出一顆沒有名字的按鈕，鍵盤使用者無從分辨三篇文章。
     用 Bootstrap .visually-hidden 的同一套寫法（絕對定位 + 1px 裁切），
     畫面上看不到、輔助技術仍讀得到，標記一個字都沒有改。
   ⚠ 順帶的好處：日後客戶要把標題叫回來，把這一條註解掉就好。 */
.kz-list-title {
    position: absolute !important;
    width: 1px !important;
    height: 1px !important;
    padding: 0 !important;
    margin: -1px !important;
    overflow: hidden !important;
    clip: rect(0, 0, 0, 0) !important;
    white-space: nowrap !important;
    border: 0 !important;
}

/* ── 右欄：標籤與 YouTube／Podcast 改成灰框描邊 ─────────────────────
   ⚠ 描邊色用 --slate（SLATE #7C7D80），與近期課程小卡的框同一個灰（SSOT），
     不另外調一個新的灰出來。
   ⚠ 文字從「填色上的黑」改成 --heading 仍是黑，但去掉底色之後對比是
     黑壓頁面底 #F5F5F7 ＝ 19.6:1，比原本填色時更好讀。
   ⚠ hover 維持全站規則（填 --btn-fill-hover 膚色）—— 靜置是框、hover 才填，
     這正是「描邊按鈕」該有的行為，不必另外寫。
   ⚠ **不含「查看更多」**：.kz-more（左欄）與 .kz-detail-more（右欄）都是
     .btn-pill，仍然是實心藍鈕，依客戶指定保留。 */
.kz-tags .tag-pill,
.kz-tags .tag-pill--cat,
.kz-links a {
    background: transparent;
    border: 1px solid hsl(var(--slate));
    color: hsl(var(--heading));
}

/* ═══════════════════════════════════════════════════════════════════════
   Header 與 Footer 字級各收一階（2026-08-03 客戶：「這兩區塊字都太大」）
   ═══════════════════════════════════════════════════════════════════════
   ⚠ 不是去一條一條改 font-size —— 這兩區加起來有十幾處文字（導覽、下拉、搜尋、
     欄標題、連結、標語、訂閱說明、輸入框、按鈕、版權），逐條改一定會漏，
     而且日後誰新增一行就又跑掉。改成**在這兩個容器上重新定義字級 token**，
     底下所有 var(--text-*) 自動跟著收，這也是本站既有的做法（見色塊那幾段）。
   ⚠ 只在這兩個容器內生效，內容區的字級一格都沒動。
   ⚠ 行高多數是寫死的 px（如 30px），字級變小、行盒不變 ——
     所以 Header 高度（--header-h: 91px）與 Footer 那組調過的段落節奏都不受影響，
     不必回頭重算。已實測確認。
       --text-body  15 → 14px   導覽、連結、標語、訂閱說明、輸入框、按鈕、版權
       --text-sub   18 → 16px   Footer 欄標題、橫排的實體課程／知識專區
       --text-meta  13 → 12px   細項 */
.site-header,
.site-footer {
    --text-body: .875rem;
    --text-sub:  1rem;
    --text-meta: .75rem;
}

/* 知識專區左欄主標往下讓一段（2026-08-03 客戶：「標題不要太上面，margin-top 有點空間」）。
   ⚠ 同一天把區塊上內距收掉一階之後，主標幾乎貼著色塊上緣。這裡補回的是
     **標題自己的上留白**，不是把區塊內距加回去 —— 加內距會連帶把大圖的框拉高，
     裁切量整組要重算；只動標題則完全不影響版面幾何（左欄本來就有餘裕）。
   ⚠ 用 vh 的 clamp：矮螢幕不佔掉太多，高螢幕才給滿。 */
.kz-intro .section-title {
    margin-top: clamp(1.5rem, 3.5vh, 3rem);
}

/* ═══════════════════════════════════════════════════════════════════════
   Header 高度改成 56px（2026-08-03 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   原本是 90.8px（內距 39/22 ＋ 導覽行盒 29 ＋ 下框線 0.8）。

   ⚠ 不是去改那組 39/22 的內距 —— 內距是「算出來」的高度，字級或導覽行高一動
     高度就跟著跑。改成**直接把高度釘死在 --header-h**，內容用既有的
     align-items:center 自己置中，之後不管字級怎麼調，高度都還是 56。
   ⚠ box-sizing: border-box：下框線要算在 56 之內，否則實際是 56.8。
   ⚠ --header-h 是這個數字的 SSOT（Hero 的上留白 padding-top 讀的就是它），
     所以只要改這一個值，吃它的地方會自己跟上，不必逐處回頭調。
   ⚠ 內容放得下：導覽行盒 29px，56 − 0.8 還有 26px 的上下餘裕。
   ⚠ home.js 的 THRESHOLD（=14，捲動狀態旗標）刻意不動：
     header 變**矮**時這個門檻只會「提早觸發」，而且 .is-scrolled 在已切版的頁面
     不改變任何視覺（見該檔註解），沒有需要重算的東西。 */
:root {
    --header-h: 56px;
}

/* ── --header-real-h：header 的「真實」高度（2026-08-25 新增）─────────────
   ⚠ 這顆跟上面的 --header-h 是**兩件事**，不要混用：
     --header-h      設計上的 SSOT token，全站 48 處在讀，其中
                     course_detail 在 ≥1468px 還被覆寫成 191px（≈3174）。
                     已知跟真實高度對不上，但**動不得**——`.cd-hero-text`
                     的 padding-top 讀它（實測 1920 寬內距 228.4px、課程
                     標題 top 504.5px），改了會讓客戶已驗收的課程頁 Hero
                     整個位移。
     --header-real-h 真的量出來的 header 高度，只給「要精準貼齊 header
                     底線」的元件用（目前只有麵包屑 .wf-crumb 那一組）。

   site.js 第③段 IIFE 會用 ResizeObserver 量真實高度、寫成 <html> 的
   **行內樣式**，優先權高於這裡的 :root 規則——所以下面這三行只是
   「JS 還沒跑完／site.js 被快取到舊版／JS 整個停用」時的安全預設值。
   ⚠ 這三個數字一樣會過期，但後果跟 --header-h 完全不同：它只是
     fallback，JS 一跑就被蓋掉，過期最多造成載入瞬間幾 px 的位移；
     而不是像現在這樣造成「麵包屑永久撞在 header 線上」（≥992 時真實
     header 永遠比 56px 高，讓位量 100% 靠 JS，變數沒進來就必撞）。
   ⚠ 數字來源：headless Chrome 實測 .site-header 的
     getBoundingClientRect().height（≤991 sticky=56、992–1467=63、
     ≥1468=73）。header 的內距／字級改了記得回來重量一次。 */
:root {
    --header-real-h: 56px;
}

@media (min-width: 992px) {
    :root { --header-real-h: 63px; }
}

@media (min-width: 1468px) {
    :root { --header-real-h: 73px; }

    /* ⚠ `.is-compact` 是 JS 掛上去的，沒有它就代表「JS 還沒跑／跑不起來」，
       而 header 在那個狀態下走的是**另一套比較高的版型**：本檔 ≈3115 的
       `html:not(.is-compact) .site-header { height:auto; min-height:var(--header-h) }`
       ——高度由內容撐開、但不低於 --header-h。上面那個 73px 的 compact
       估值在這個狀態下會少讓一大截，麵包屑直接撞進 header。
       ⚠ 用 max() 把那條規則的語意原封不動照抄過來，兩種頁面各自都對，
         而且不用寫 :has(.cd-hero) 這種頁面專屬選擇器：
           一般頁　　　　　max(115px, 56px)  = 115px（實測內容撐到 115）
           course_detail　 max(115px, 191px) = 191px（min-height 贏，實測 191）
         --header-h 之後若再被誰覆寫，這裡會自己跟上，不會像寫死數字一樣
         悄悄過期。 */
    html:not(.is-compact) { --header-real-h: max(115px, var(--header-h)); }
}

.site-header {
    box-sizing: border-box;
    height: var(--header-h);
}

/* ⚠ 特異度要補到 (0,3,0)：內距是 `.is-designed .site-header .inner` 設的，
     只寫 `.site-header .inner` (0,2,0) 會被它蓋掉（第一版就是這樣，實測 inner
     仍是 61px、內容擠出 header 之外）。同時也要在同一個 @media 內。 */
.site-header .inner,
.is-designed .site-header .inner {
    padding-block: 0;
    height: 100%;
}

@media (min-width: 992px) {
    .is-designed .site-header .inner {
        padding-block: 0;
        height: 100%;
    }
}

/* 服務專區折疊（2026-08-03 客戶：「先展示到企業內訓，左邊放往下看完整服務」）。
   ⚠ 用 display:none 而不是高度 0 ＋ overflow：網格是兩欄，留著空的格子會把
     下一列往下擠，收合等於沒收。
   ⚠ .is-folded 由 JS 掛（見 home.js）——CSS 預設不藏任何東西，
     JS 沒跑時六個服務全部看得到。 */
.svc-tile.is-folded {
    display: none;
}

/* 展開鈕：與左欄其他元素同一條左邊界，不要被拉成整欄寬 */
.svc-more-toggle {
    align-self: flex-start;
    margin-top: 2rem;
    font-family: inherit;
    cursor: pointer;
}

.svc-more-toggle[hidden] {
    display: none;
}

/* ═══════════════════════════════════════════════════════════════════════

/* ═══════════════════════════════════════════════════════════════════════
   Header／Footer 字級再收一階、行距放鬆（2026-08-03 客戶第二輪）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「footer 跟 header 的文字再小，大概是 footer 小字的大小，
          然後文字行距大一點。」

   ⚠ 一樣只動這兩個容器的 token，不逐條改 font-size。
       --text-body 14 → 13px｜--text-sub 16 → 14px｜--text-meta 12 → 11px
     「footer 小字」＝ 版權列那一階（原本 14px），所以內文與導覽都收到它附近。
   ⚠ 字一小，行距要跟著放鬆才不會擠成一塊 —— 這是同一件事的兩面，
     只縮字級會讓段落看起來更密。三個行距 token 一起加：
       --lh-body 2 → 2.4｜--lh-sub 1.7 → 2.1｜--lh-meta 1.7 → 2.1
   ⚠ Footer 有三處把行距**寫死成 2.1**（直式清單／標語／訂閱說明，
     那是先前「段落節奏統一」那一輪刻意寫死的），token 蓋不過它們，
     必須在這裡一起提到 2.4，否則同一個 Footer 裡會出現兩種行距。
   ⚠ Header 高度不受影響：它已經釘死在 --header-h（56px），
     行盒變高也不會把 header 撐開，內容仍靠 align-items:center 置中。已實測。 */
.site-header,
.site-footer {
    --text-body: .8125rem;   /* 13px */
    --text-sub:  .875rem;    /* 14px */
    --text-meta: .6875rem;   /* 11px */

    --lh-body: 2.4;
    --lh-sub:  2.1;
    --lh-meta: 2.1;
}

.footer-col-list:not(.footer-col-list--inline) a,
.footer-slogan,
.footer-sub-desc {
    line-height: 2.4;
}

/* 近期課程滑鼠拖曳瀏覽（2026-08-03 客戶指定，行為在 home.js）。
   ⚠ cursor 要說清楚這一列可以拉：grab／grabbing 是這個互動的通用語彙，
     沒有游標提示的話多數人不會想到去拖。
   ⚠ 拖曳期間關掉 scroll-snap 與 smooth：
     mandatory 吸附會在每次 scrollLeft 賦值後把位置拉回最近的卡（變成一格一格跳），
     smooth 則讓每次賦值排隊補間，游標與內容脫節。放開後兩者都交還給瀏覽器。
   ⚠ user-select／圖片拖曳一併關掉：不關的話拖到一半會變成選字或把圖拖出去。 */
.course-grid {
    cursor: grab;
}

.course-grid.is-dragging {
    cursor: grabbing;
    scroll-snap-type: none;
    scroll-behavior: auto;
    user-select: none;
    -webkit-user-select: none;
}

.course-grid a,
.course-grid picture,
.course-grid img {
    -webkit-user-drag: none;
}

/* ═══════════════════════════════════════════════════════════════════════
   近期課程：標題與進度置中、箭頭鈕改成「圓框雙向箭頭」游標（2026-08-03 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「近期課程的標置中，把按鈕箭頭改成鼠標是圓圈有雙向箭頭，
          然後用鼠標橫向拖移卡牌的方式呈現。」「底下的數字橫條也改置中。」

   ⚠ 兩顆箭頭鈕退場，捲動改由「拖曳」承擔（拖曳行為已在同日先做好）。
     鍵盤仍然走得動：卡片本身是連結，Tab 進去瀏覽器會自動把它捲進畫面，
     所以拿掉按鈕沒有讓這一列變成鍵盤到不了的地方。
   ⚠ 圓框游標沿用 Hero .cal-cursor 的視覺語彙（實心深灰圓 + 淺色符號 + 淡投影），
     不另外發明第二種樣子；只有裡面的符號換成雙向箭頭 ↔。
   ⚠ 符號用 \2194 逸出碼而不是字面字元 —— 與站上箭頭同一個理由：
     伺服器回的 CSS 沒有 charset，字面字元會被當 Latin-1 讀成亂碼。
   ⚠ cursor:none 綁在 .is-on（圓框真的顯示時）而不是常駐：
     圓框沒出現時系統游標一定要在，否則使用者會在列上找不到游標。
   ⚠ 觸控／無指標裝置整組不輸出：沒有游標可跟，留著只會是一顆隱形的圓。 */
.course-head {
    justify-content: center;
    text-align: center;
}

.course-nav {
    display: none;
}

.course-meta {
    justify-content: center;
}

.course-cursor {
    display: none;
}

@media (hover: hover) and (pointer: fine) {
    .course-cursor {
        --course-cursor-size: clamp(64px, 5.5vw, 88px);

        display: flex;
        align-items: center;
        justify-content: center;
        position: fixed;
        left: 0;
        top: 0;
        z-index: 9;
        width:  var(--course-cursor-size);
        height: var(--course-cursor-size);
        /* 圓心對齊指標位置 */
        margin: calc(var(--course-cursor-size) / -2) 0 0 calc(var(--course-cursor-size) / -2);

        border-radius: 50%;
        background: hsl(var(--surface-dark));
        color: hsl(var(--surface-dark-foreground));
        box-shadow: 0 10px 24px -12px hsl(var(--foreground) / .55);

        font-family: var(--font-en);
        font-size: 1.5rem;
        line-height: 1;
        font-weight: 350;

        pointer-events: none;
        opacity: 0;
        scale: .72;
        translate: var(--course-cursor-x, 0px) var(--course-cursor-y, 0px);
        transition: opacity 200ms var(--ease-out), scale 260ms var(--ease-out);
    }

    .course-cursor::before {
        content: "\2194";
    }

    .course-cursor.is-on {
        opacity: 1;
        scale: 1;
    }

    /* 圓框顯示時才收掉系統游標（連卡片上的手指游標一起，否則進到卡片就跳回箭頭） */
    .course-section:has(.course-cursor.is-on) .course-grid,
    .course-section:has(.course-cursor.is-on) .course-grid * {
        cursor: none;
    }
}

@media (prefers-reduced-motion: reduce) {
    .course-cursor { transition: opacity 120ms linear; scale: 1; }
}

/* ═══════════════════════════════════════════════════════════════════════
   知識專區改成「編號列滾動堆疊」（2026-08-03 客戶指定，附兩張參考圖）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「知識專區改成左邊不要壓深灰底做表格，直接標題，然後三張卡牌用
          滾動式堆疊的方式呈現」「標題放左邊不要壓背景」。

   ⚠ 左邊那塊 SLATE 色塊自動消失：它原本是 .kz-layout 的 ::before／::after，
     而版型換成 .kz-stack 之後 .kz-layout 不再存在於這裡的版面裡。
     （2026-08-18 補記：.kz-layout 這個 class 名字後來被知識專區「列表頁」
     另一個元件撞名重用，意外救活了這組舊色塊，見列表頁那節的說明；
     因此舊規則已直接刪除，不是「一行都沒刪」——要復原三欄版型必須連同
     色塊 CSS 一起重寫，不會自動回來。）
   ⚠ 堆疊靠 position:sticky，不靠 JS：列首黏在 header 底下（top 讀 --header-h，
     同一個 SSOT），被下一列推上去 —— 這正是參考圖那個「一層一層疊上來」的效果。
     用 JS 算捲動位置的話，捲動每一格都要重排，而 sticky 是合成層在做。
   ⚠ 列首一定要有**不透明底色**：它會蓋在下方的圖文上，透明的話兩層字疊在一起。
   ⚠ 內容一個字都沒刪，只是換排列：分類、標籤、標題、摘要、介紹、
     YouTube／Podcast、查看更多全部還在。 */

.kz-stack {
    display: flex;
    flex-direction: column;
}

/* 標題靠左、直接放在頁面底上（客戶：「標題放左邊不要壓背景」） */
.kz-stack-title {
    align-self: flex-start;
    text-align: left;
    margin-bottom: clamp(2rem, 5vh, 3.5rem);
}

.kz-stack-list {
    list-style: none;
    margin: 0;
    padding: 0;
}

/* ── 列首：編號 ── 分類，捲動時黏住並堆疊 ─────────────────────────── */
.kz-stack-head {
    position: sticky;
    top: var(--header-h);
    z-index: 2;

    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 2rem;
    padding-block: clamp(.875rem, 1.6vh, 1.25rem);

    /* 不透明：它會蓋過下方的圖文 */
    background: hsl(var(--background));
    border-top: 1px solid var(--rule-on-light);
}

.kz-stack-no {
    font-family: var(--font-en);
    font-size: var(--text-sub);
    line-height: 1;
    letter-spacing: var(--tracking);
    color: hsl(var(--slate));
}

.kz-stack-cat {
    font-size: var(--text-body);
    line-height: 1;
    letter-spacing: var(--tracking);
    color: hsl(var(--muted-foreground));
    text-decoration: none;
    transition: color var(--transition);
}

.kz-stack-cat:hover,
.kz-stack-cat:focus-visible {
    color: hsl(var(--heading));
}

/* ── 列身：左文右圖 ─────────────────────────────────────────────────── */
.kz-stack-body {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr);
    gap: clamp(2rem, 4vw, 5rem);
    align-items: end;
    padding-block: clamp(2rem, 5vh, 3.5rem) clamp(3rem, 8vh, 5.5rem);
}

.kz-stack-text {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 1rem;
}

.kz-stack-name {
    margin: 0;
    font-size: var(--text-lead);
    line-height: 1.6;
    font-weight: var(--fw-normal);
    letter-spacing: var(--tracking);
    color: hsl(var(--heading));
}

.kz-stack-name a {
    color: inherit;
    text-decoration: none;
    transition: opacity var(--transition);
}

.kz-stack-name a:hover,
.kz-stack-name a:focus-visible { opacity: .7; }

.kz-stack-desc,
.kz-stack-body-text {
    margin: 0;
    font-size: var(--text-body);
    line-height: var(--lh-body);
    font-weight: var(--fw-light);
    letter-spacing: var(--tracking);
    color: hsl(var(--muted-foreground));
}

/* 2026-08-27 客戶「知識專區圖片跟標題跟內文點擊跳至該文章」——摘要與內文
   這兩段改成連結（home.php）。
   ⚠ display:block：這兩段的行高是 2.6（本檔「段落放鬆」那一節），
     inline 連結的可點範圍只有每一行文字自己的字高，行與行之間會留下
     一大截點不到的空隙，體感就是「點了沒反應」。改成 block 之後整段
     的方框都可點，而且因為 <p> 自己已經是塊級、寬度也由 <p> 的
     max-width:44em 決定，版面一個 px 都不會變。
   ⚠ color:inherit：這兩段的顏色散在好幾個斷點各自覆寫（--muted-foreground
     ／字級表那幾條），繼承 <p> 的計算值才不會被連結預設色蓋掉，
     日後改 <p> 的顏色也會自己跟上。
   ⚠ hover 用 --accent-blue-text：全站定案「有連結的文字 hover 變藍、
     純文字變灰」，而 --accent-blue 是底色專用（直接當文字色壓淺底只有
     1.3:1），見 memory accent-blue-text-only-token。 */
.kz-stack-desc > a,
.kz-stack-body-text > a {
    display: block;
    color: inherit;
    text-decoration: none;
    transition: color var(--transition);
}

.kz-stack-desc > a:hover,
.kz-stack-desc > a:focus-visible,
.kz-stack-body-text > a:hover,
.kz-stack-body-text > a:focus-visible {
    color: hsl(var(--accent-blue-text));
}

/* 參考圖左下那顆圓形箭頭 */
.kz-stack-go {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.75rem;
    height: 2.75rem;
    border-radius: 50%;
    background: var(--btn-fill);
    color: hsl(var(--heading));
    text-decoration: none;
    transition: background var(--transition);
}

.kz-stack-go:hover,
.kz-stack-go:focus-visible {
    background: var(--btn-fill-hover);
}

.kz-stack-go svg {
    width: 1.125rem;
    height: 1.125rem;
}

/* ── 右邊的圖 ───────────────────────────────────────────────────────── */
.kz-stack-media {
    margin: 0;
}

.kz-stack-media a {
    display: block;
}

.kz-stack-media picture,
.kz-stack-media img {
    display: block;
    width: 100%;
    height: auto;
    border-radius: var(--radius-media);
}

/* 窄幅：收成單欄，圖排在文字下面；列首仍然黏著 */
@media (max-width: 1023.98px) {
    .kz-stack-body {
        grid-template-columns: minmax(0, 1fr);
        gap: 1.75rem;
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   知識專區：改成「真的疊上去」的堆疊（2026-08-03 客戶回報）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「每個列要堆疊覆蓋上去到頂部只看到數字，不是三個攤開陳列。」

   上一版只讓**列首**黏住，三篇的內容仍然一路往下攤開 —— 那是「有黏頭的長列表」，
   不是堆疊。改成**整個 li 黏住**，而且每一篇的 top 各差一個列首高：
       第 1 篇 top = header
       第 2 篇 top = header + 1 × 列首高   → 蓋住第 1 篇的內容，只留它的編號列
       第 3 篇 top = header + 2 × 列首高   → 再蓋一層
   捲到底時畫面上就是「三條編號列疊在頂部 ＋ 最後一篇的內容」。

   ⚠ 每一篇必須有**不透明底色**，否則蓋不住下面那一篇（會兩層字疊在一起）。
   ⚠ 位移量用 nth-child 給，不寫 inline style（本專案樣板不寫 inline style）。
     多備到第 6 篇；後台增到更多篇時再往下補，或改用 :nth-child 的通式。
   ⚠ 列首高抽成 --kz-head-h，並用 min-height 讓實際高度與算式一致 ——
     兩者只要對不上，堆疊的縫就會愈疊愈歪。
   ⚠ 列首本身不再 sticky（改由整篇負責），否則兩層 sticky 會互相打架。

   2026-08-25 客戶回報「堆疊起來 logo 跟小卡的數字沒有留空間」——第 1 篇
   的算式是 `top: header`，意思是它的頂緣**貼齊 header 底緣**，本來就是
   設計成零間距。改成 `header + --kz-stack-gap`，讓所有篇一起往下平移
   同一個距離（每篇之間仍然差一個 --kz-head-h，堆疊本身不會跟著歪掉，
   見上面「兩者只要對不上，堆疊的縫就會愈疊愈歪」那條——這裡兩者都加了
   同一個常數，相對距離沒變）。
   ⚠ 這個 gap 值刻意比視覺上想要的留白（實測抓 22~34px）大一截：
     --header-h 本身目前是**過期的舊值**（56px，2026-08-03 header 還是
     寫死高度時訂的）。header 後來改成「統一列」+ height:auto（≈23097
     行 html.is-compact:not(.has-menu-open) .site-header { height:auto }），
     實際渲染高度是 85.6px（headless Chrome 實測），--header-h 沒有跟著
     這次改版更新，全站 42 處引用 var(--header-h) 的地方理論上都吃到
     這個落差，只是這裡（sticky 疊層貼齊 header 底緣）視覺上最明顯。
     這次只在 kz-stack 局部補償（gap 值 = 落差 29.6px + 真正想要的留白），
     沒有動 --header-h 本身——那是全站 SSOT，牽動 42 處，屬於更大範圍的
     根因修復，這次沒有被要求動，先在這裡記下來，見 memory
     [[reference-onedaylisten-header-h-stale-token]]。 */
.kz-stack-list {
    --kz-head-h: 3.5rem;
    --kz-stack-gap: clamp(3.25rem, 6vh, 4rem);
}

.kz-stack-item {
    position: sticky;
    top: calc(var(--header-h) + var(--kz-stack-gap) + var(--kz-head-h) * var(--kz-i, 0));
    background: hsl(var(--background));
}

.kz-stack-item:nth-child(1) { --kz-i: 0; }
.kz-stack-item:nth-child(2) { --kz-i: 1; }
.kz-stack-item:nth-child(3) { --kz-i: 2; }
.kz-stack-item:nth-child(4) { --kz-i: 3; }
.kz-stack-item:nth-child(5) { --kz-i: 4; }
.kz-stack-item:nth-child(6) { --kz-i: 5; }

.kz-stack-head {
    position: static;
    display: flex;
    align-items: center;
    min-height: var(--kz-head-h);
    padding-block: 0;
}

/* 最後一篇下面留一段，讓它也捲得完（否則最後一篇永遠貼在堆疊底下捲不完） */
.kz-stack-list > .kz-stack-item:last-child .kz-stack-body {
    padding-bottom: clamp(2rem, 6vh, 4rem);
}

/* ═══════════════════════════════════════════════════════════════════════
   手機版整理（2026-08-03 客戶指定「請手機版面設計給我」）
   ═══════════════════════════════════════════════════════════════════════
   桌機這幾輪改了很多（header 釘 56、Hero 滿版圖、服務專區色塊、知識專區堆疊），
   窄幅沒有跟著走，所以這裡一次補齊。每一段都寫清楚為什麼。 */

/* ── 1. 服務專區：白字壓在淺底上（可讀性 bug）─────────────────────────
   ⚠ 這是**真的看不見**，不是不好看：≤1199 時 .svc-layout::before（深色塊）
     display:none，可是區塊底早就改成 --background #F6F6F7，而 .on-dark 衍生的
     文字色仍然是米白 —— 實測 rgb(255,255,255) 壓 rgb(246,246,247)，對比 1.04:1。
     桌機沒事是因為左欄真的有深色塊墊著；窄幅那塊被關掉之後就露餡了。
   ⚠ 做法沿用實體課程那一段的既有模式：標記帶著 .on-dark 不動，
     在這裡把每一條衍生配色覆寫回淺底版本。 */
@media (min-width: 992px) and (max-width: 1023.98px) {
    .service-section {
        /* 淺底的字重（.on-dark 的 310/410 是深底的光暈補償） */
        --fw-light:   350;
        --fw-normal:  450;
        --fw-heading: 350;
    }

    .service-section .svc-intro .section-title .zh { color: hsl(var(--heading)); }
    .service-section .svc-intro .section-title .en { color: hsl(var(--slate)); }
    .service-section .svc-intro-desc               { color: hsl(var(--muted-foreground)); }

    .service-section .svc-main .svc-feature-name { color: hsl(var(--heading)); }
    .service-section .svc-main .svc-feature-desc { color: hsl(var(--muted-foreground)); }
    .service-section .svc-main .svc-feature-cat,
    .service-section .svc-main .svc-tile-no      { color: hsl(var(--slate)); }
    .service-section .svc-main .svc-tile-name    { color: hsl(var(--heading)); }
    .service-section .svc-main .svc-tile-sub     { color: hsl(var(--foreground) / .68); }
    .service-section .svc-main .svc-arrow        { color: hsl(var(--foreground) / .55); }

    /* 左欄的按鈕也要翻回淺底那一組（腳下是淺色塊了） */
    .service-section .svc-intro {
        --surface-here: hsl(var(--background));
        --ink-here:     hsl(var(--foreground));
    }
}

/* ── 2. Hero 手機版：圖在上（裁左半）＋ 文字橫帶在圖下（2026-08-03 客戶指定，附參考圖）──
   客戶：「手機版的 hero 你切圖片在上，然後圖片裁切左半部，
          標題跟介紹文字變成橫的一條在圖片底下。」

   ⚠ 這一段推翻了同日稍早「圖滿版 ＋ 文字面板蓋左邊」的手機版 ——
     那是把桌機的做法直接搬過來，客戶看過實機後改成參考圖的形式。
     桌機（≥992）完全不受影響，仍然是滿版圖 ＋ 左側毛玻璃面板。
   ⚠ 版型回到兩列：第一列圖、第二列文字橫帶。
       grid-template-rows: auto minmax(0, 1fr)
     第一列由 aspect-ratio 決定，第二列吃掉剩下的框高。
     ⚠ 第二列**一定要 minmax(0,1fr) 而不是 auto**：框高是固定的（--cal-h），
       兩列都 auto 的話多出來的高度沒有人吸收，會全部堆在最底下變成空白。
   ⚠ 圖片 object-position: right —— 客戶指定「裁切左半部、留右半部」。
     素材是 2336×992（2.35:1）的橫幅，手機上放不下整張；
     主體（人物、貓、器物）都在圖的右側，留右半正好保住整組構圖，
     被切掉的是左邊那片紙紋留白。
     ⚠ 第一版做成 left（誤讀成「保留左半」），已依客戶澄清改正。
   ⚠ 文字橫帶**不再是毛玻璃浮層**：它現在在圖片外面，底下就是頁面底色，
     半透明沒有意義，也會讓字浮在紙紋上。
   ⚠ .cal-text-item 回到正常流 ＋ 非當前頁 display:none：
     橫帶的高度要跟著當前那一頁的文字長度走（四頁長度不同），
     絕對定位的話高度沒有來源，只能靠魔術數字，某一頁一定會被裁到。
     代價是手機換頁少了淡入 —— 手機翻頁靠手勢，動畫價值低。 */
@media (max-width: 991.98px) {
    .hero-inner {
        /* ⚠ 上內距歸零（2026-08-03 客戶：「圖片上面跟 header 不要有空間」）。
           讓開 header 這件事已經由 .hero 自己的 padding-top 做掉了，
           這裡再加一次 var(--header-h) 等於加了兩次 —— 實測圖片上緣
           距 header 底部多出 56px 的空白。
           ⚠ 左右也不留白，圖才是滿版。 */
        padding-block: 0;
        padding-inline: 0;
    }




    /* 這幾層會蓋住圖片 */




    /* 文字橫帶 */



    /* 標題在窄幅會斷在「根，溫柔地站／穩」這種奇怪的位置。
       ⚠ 收字級不是為了好看，是為了**斷行位置**：一個中文字佔 1em ＋ 0.06em 字距，
         字級一小、一行容得下的字數就多，句子才斷得到標點上。
       ⚠ max-width 一併放掉：桌機那條 8em 是為了鎖「七個字一行」，
         橫帶已經是滿版寬度，再疊一層只會更早折行。 */


    /* 數字輪播移到整條的最下面、靠右（2026-08-03 客戶指定，原本置中）。
       ⚠ 右邊要留 --section-x 的內距，數字才會與整頁的內容格線對齊，
         不是貼死在螢幕邊上（左邊的標題也是切在同一條線上）。 */

    /* 窄幅原本把頁碼轉成直排（舊直式桌曆的設計），這裡整組轉回橫式 */



}

/* ── 3. 知識專區堆疊：窄幅要收，不然三條編號列吃掉半個螢幕 ───────────
   ⚠ 列首高從 3.5rem 收到 2.75rem：三篇疊起來是 132px（原本 168），
     在 844 高的手機上留給內容的空間差很多。
   ⚠ --kz-head-h 與 min-height 必須一起改 —— 這兩個值就是堆疊算式的兩端，
     只改一個，縫會愈疊愈歪。 */
@media (max-width: 1023.98px) {
    .kz-stack-list {
        --kz-head-h: 2.75rem;
    }

    .kz-stack-head {
        min-height: var(--kz-head-h);
    }

    .kz-stack-body {
        padding-block: 1.5rem 2.5rem;
    }

    .kz-stack-title {
        margin-bottom: 1.5rem;
    }
}

/* ── 4. 服務卡與課程卡：窄幅的可讀寬度 ───────────────────────────────
   ⚠ 服務網格在 ≤767 已經收成單欄；768–991 之間仍是兩欄、每格只有 141px 寬（實測），
     四個字的標題就要折兩行。這一段把兩欄的斷點往上抬到 991。 */
@media (max-width: 1023.98px) {
    .svc-grid {
        grid-template-columns: minmax(0, 1fr);
    }

    .svc-tile:nth-child(even) { border-right: 0; }
}

/* ── 5. 近期課程：手機一次露一張多一點 ───────────────────────────────
   ⚠ 桌機是「一次露 2.5 張」的算式；手機照抄會讓卡片只剩 140px 寬。
     改成 78%，露出右邊那張的一角，使用者才知道可以往右拉。 */
@media (max-width: 767.98px) {
    .course-cell {
        flex: 0 0 78%;
        min-width: 0;
    }
}

/* ── 6. 主打服務（顧問案）在窄幅要收成單欄 ───────────────────────────
   ⚠ 桌機那一輪把 .svc-feature 改成「左文右圖」的兩欄 grid（grid-area 寫死 1/1、1/2），
     窄幅沒有覆寫 → 圖仍佔第二欄，文字被擠在剩下的半格裡：
     實測那顆「一對一 / 企業溝通顧問服務」的膠囊被壓到只剩 4 個字寬、折成四行。
   ⚠ 收成單欄之後 grid-area 一定要一起放掉，否則兩者仍然搶同一格。 */
@media (max-width: 1023.98px) {
    .svc-feature {
        grid-template-columns: minmax(0, 1fr);
    }

    .svc-feature-media {
        grid-area: auto;
        min-height: 0;
        aspect-ratio: 16 / 10;
    }

    .svc-feature-info {
        grid-area: auto;
        padding: clamp(1.5rem, 5vw, 2.25rem);
    }

    .svc-feature-tail {
        justify-content: space-between;
        width: 100%;
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   服務專區手機版（2026-08-03 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「服務專區手機版直接背景深灰色，然後往下查看更多按鈕改成
          點擊查看更多項目，把按鈕放到第三個項目底下，
          然後另外兩個項目在手機版圖片直接顯示在右邊，不要 hover 才跑出圖片。」

   ⚠ 深灰底一設，文字就要是白的 —— 站上**原本就有**那一組（≤991 的
     「窄幅回到原本的單一深色底」），所以我同日稍早那段「白字壓淺底」的修正
     作用域要收成 992–1199：那個區間才是「深色塊被關掉、底卻是淺的」的破綻。
     ≤991 交還給既有規則，不要兩邊互相打架。
   ⚠ 底色改的是 .service-section 自己 —— 桌機那條 --background 是
     「簾幕退場後露出來的右半」，與手機無關。 */
@media (max-width: 1023.98px) {
    /* ⚠ 2026-08-03 客戶追加：「灰底到顧問案的圖片結束就好，不要整個都灰底」。
       深灰只鋪到主打服務那張圖的下緣，其餘回到頁面底。
       ⚠ 用漸層的**硬停點**（同一個位置兩個色標）而不是再疊一層 ::before：
         ::before 需要一個高度，而那個高度是「標題＋介紹＋圖」的總和 ——
         介紹文的行數會隨字級與視窗寬度變，寫死一定有視窗對不上。
       ⚠ 停點 --svc-dark-h 由 home.js 量出來寫進來（量的是圖片下緣到區塊頂端的距離）。
         JS 沒跑時退回 58%：位置不會剛好，但仍然是「上深下淺」而不是整片灰。 */
    .service-section {
        background: linear-gradient(
            to bottom,
            hsl(var(--surface-dark)) 0,
            hsl(var(--surface-dark)) var(--svc-dark-h, 58%),
            hsl(var(--background))   var(--svc-dark-h, 58%)
        );
    }

    /* ── 按鈕移到第三個項目底下 ────────────────────────────────────
       折疊起點是「主打 ＋ 兩張卡」＝ 三個項目，所以「第三個項目底下」
       就是整個 .svc-main 之後。
       ⚠ 按鈕的標記在 .svc-intro 裡（左欄），與 .svc-main 是不同的父層，
         單靠 order 排不到它後面。用 display:contents 把 .svc-intro 的孩子
         「攤平」成 .svc-layout 的直接子項，四個東西才在同一個 flex 容器裡排序。
       ⚠ display:contents 會讓 .svc-intro 自己的背景／內距／sticky 失效 ——
         窄幅這三樣本來就沒有在用（色塊在 ≤1199 已 display:none），所以安全。 */
    .svc-layout {
        display: flex;
        flex-direction: column;
    }

    .svc-intro {
        display: contents;
    }

    .svc-intro .section-title { order: 1; }
    .svc-intro-desc           { order: 2; }
    .svc-main                 { order: 3; }
    .svc-more-toggle          { order: 4; align-self: flex-start; margin-top: 1.75rem; }

    /* ── 服務卡：圖片直接顯示在右邊，不再是 hover 才浮出的預覽 ──────
       ⚠ .svc-tile-media 原本是 position:fixed 的游標預覽圖，而且在
         「沒有 hover 能力」的裝置上被 display:none —— 手機正好是那一類，
         所以不覆寫的話手機根本看不到圖。這裡把它收回版位裡。
       ⚠ 卡片改成左文右圖的兩欄：圖佔 38%，四個字的標題仍放得下。 */
    .svc-tile {
        display: grid;
        grid-template-columns: minmax(0, 1fr) 38%;
        gap: 1rem;
        align-items: center;
    }

    /* ⚠ 圖在標記上排在文字**前面**（它原本是浮出的預覽圖，DOM 順序無所謂），
       收回版位之後不指定欄位就會落在左邊。明寫 grid-column 才會在右邊，
       而不是靠 order —— 兩欄的位置是版型，不是排序。 */
    .svc-tile-media {
        display: block;
        position: static;
        grid-column: 2;
        grid-row: 1;
        inset: auto;
        z-index: auto;
        width: auto;
        aspect-ratio: 4 / 3;
        translate: none;
        opacity: 1;
        scale: 1;
        transition: none;
    }

    .svc-tile-media picture,
    .svc-tile-media img {
        width: 100%;
        height: 100%;
        object-fit: cover;
    }

    .svc-tile-info {
        grid-column: 1;
        grid-row: 1;
        width: auto;
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   手機覆蓋式主選單（2026-08-03 客戶指定，附兩張參考圖）
   ═══════════════════════════════════════════════════════════════════════
   參考圖一：收合時是**兩條短橫線**的極簡標記（不是三條，也不是 ☰ 字元）。
   參考圖二：點開後整頁白底覆蓋，右上角 ×，內容分兩欄
            （粗體主項 ＋ 底下細的子項），右下角社群圖示。

   分欄（依 2026-08-03 與客戶確認）：
       左欄  服務專區（＋六個子項）／實體課程／知識專區／電子報
       右欄  會員（登入・註冊）／搜尋
   ⚠ 沒有複製任何導覽內容 —— 覆蓋層直接沿用既有的 #wfNav，
     只是在窄幅換一種排法。多做一份的話兩邊日後一定會不同步。 */

/* 2026-08-17：第一層搜尋連結（.site-nav-primary，見下方「Header 重新分層」
   新區塊）只在桌機 ≥1468px 顯示——這裡先給一個無條件的 display:none 當
   基底，避免它在沒有任何選擇器命中的寬度（992–1467.98px 這段既有的
   平板抽屜/漢堡寬度）以 <a> 元素預設的 inline 顯示冒出來，跟漢堡鈕擠在
   一起。實測抓到這個問題：新元素只在 ≥1468px 那個 media query 裡寫了
   「要長什麼樣子」，沒有寫「其他寬度要不要顯示」，瀏覽器預設值就會在
   992–1467.98px 意外露出來。 */
.site-nav-primary {
    display: none;
}

/* ── Menu 按鈕：三條橫線（2026-08-17 客戶「選單icon請用白色三條線」，
   原本是 2026-08-03 指定的「兩條短橫線」極簡標記，這次改成經典三線，
   同時色彩也改白色——見下面 .site-nav-toggle-bars 與其子節點） ───── */
.site-nav-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.25rem;
    height: 2.25rem;
    padding: 0;
    border: 0;
    background: transparent;
    color: hsl(var(--heading));
    cursor: pointer;
}

/* ⚠ 線用 currentColor 畫：底色若日後翻深，改一個 color 就好。
   ⚠ 線之間用 gap，不用 margin —— 高度變化時中線位置才不會偏。
   ⚠ 三條線：::before／::after 各畫一條（CSS 限制一個元素只能有這兩個
     pseudo-element），中間那條是真實的 .site-nav-toggle-bar 子節點
     （見 _header.php），三者共用同一份尺寸／顏色宣告。
   ⚠ 2026-08-25 客戶「中間那條比較粗」——三條線 CSS 完全同分同值
     （同一條規則、同一個 1.5px），但 `.site-nav-toggle-bars` 整組會被
     父層置中，垂直置中的計算結果落在 .25／.75 這種非整數 px 上（實測
     wrapTop 30.75、gap 5、三條線的頂緣分別落在 30.75／37.25／43.75）。
     1.5px 這種非整數高度疊在非整數起點上，瀏覽器算子像素覆蓋率時，
     中間那條剛好湊出一整列 100% 覆蓋的實心像素，另外兩條每一列都不到
     100%（實測 3xDPR 截圖：頭尾兩條偏淺灰、中間那條純黑），肉眼就是
     「中間比較粗」——跟本檔已知的 grid 非整數欄寬造成疊加線深淺不一
     （[[reference-onedaylisten-grid-gap-subpixel-overlay-line]]）同一個
     次像素成因，不是三條線的樣式真的不同。
     改用整數 2px：不管起點落在哪個小數點，2px 高度都會穩定對齊到
     2 整列像素，三條線視覺粗細一致（已用 3xDPR 截圖驗證）。 */
.site-nav-toggle-bars {
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 5px;
    width: 22px;
}

.site-nav-toggle-bars::before,
.site-nav-toggle-bars::after,
.site-nav-toggle-bar {
    content: "";
    display: block;
    width: 100%;
    height: 2px;
    background: currentColor;
}

/* ── 漢堡選單斷點下修：992px 改 1024px（2026-08-13 客戶指定「寬度1024的時候
   header改成漢堡選單」，同日追加「寬1024高1366（iPad Pro 直向）也要是漢堡」
   ── 確認 1024 這個寬度本身要算進漢堡選單，不是桌機導覽的起點）─────────
   ⚠ 標記上的 #wfNavToggle／#wfNav／#wfNavClose 還掛著 Bootstrap 的
     d-lg-none／d-lg-flex（見 _header.php）。那兩個 utility class 編譯死在
     992px 且帶 !important，不是改一個變數就能重新編譯（vendor 檔，不動）。
   ⚠ 本檔其餘「手機選單」規則都已經從 max-width:991.98px 一起改成
     1024px（漢堡鈕造型、抽屜本體、遮罩、字級切換器位置……全部同步），
     唯獨「鈕要不要顯示」這件事被 Bootstrap 的 !important 卡住，改斷點
     不會生效，所以在這裡用同特異度＋!important 的規則單獨覆寫回來。
   ⚠ 992–1467px 這段強制顯示漢堡鈕、隱藏水平導覽；1468px 以上才放行
     Bootstrap 原生行為（d-lg-flex／d-lg-none 本來就是 ≥992，1468 以上
     仍在範圍內，不必另外處理）。
   ⚠ .site-nav 這裡強制 display:grid 而非交給 Bootstrap 的 d-lg-flex：
     沒有這條，992–1467px 之間 Bootstrap 仍會把它塞成 flex（且沒有
     flex-direction:column），選單一打開六個項目會橫向排成一列，不是
     下面覆蓋層本體規則預期的單欄直排。
   ⚠ 2026-08-21 客戶回報「平板尺寸（768／820／1024）選單跑版」——768／820
     其實沒事（≤991px 有 wireframe.css 的 .wf-nav .dropdown{display:block}
     專屬修正），1024 才是真正壞的，成因是同一類坑的另一個實例：上面這條
     只修了 .site-nav 本身（最外層），沒有連帶修「服務專區／會員」這兩個
     子選單自己的容器（.dropdown，例如 .site-nav-menu-service）——它們讀的
     是 wireframe.css 另一條同樣沒有斷點限制的 `.wf-nav .dropdown{display:
     flex}`，≤991px（同一份 wireframe.css 專屬修正）與 ≥1468px（本檔
     ≈20422 `html:root .site-header .site-nav > .dropdown{display:block}`）
     都有人補回 display:block，唯獨 992–1467px 這個區間兩邊都沒補到，
     子選單（顧問案／AI 分身、登入／註冊）因此橫向擠到觸發鈕右邊、寬度被
     壓成內容寬（實測 47px），不是往下展開。修法比照 ≥1468px 那條的做法
     （html:root 拉高特異度蓋過 wireframe.css 的 (0,2,0)），加在這裡跟
     .site-nav 本身的修正放一起，同一類坑、同一個區間，找的時候才不會
     分散在兩個地方。
   ⚠ 2026-08-21 同一輪客戶再回報「1024 導覽會出現以前導覽的 icon、現在
     樣式已經不用 icon 請移除」——又是同一個 992–1467 缺口，這次缺的是
     圖示隱藏規則：≤991.98px 有 `.site-header .site-nav a:not(.site-nav-
     logo) svg, .site-header .site-nav .dropdown-toggle svg{display:none
     !important}`（本檔既有規則），≥1468px 有 ≈20463
     `html:root .site-header .site-nav > a .wf-icon:not(.wf-icon--
     chevron-down)...{display:none}`，唯獨 992–1467px 兩邊都沒補，服務
     專區／實體課程／知識專區／會員／電子報前面的裝飾圖示因此在這個區間
     漏出來。修法選用跟 ≥1468px 同一套選擇器（.wf-icon 類別＋排除
     chevron，不是 ≤991.98 那條的裸 svg 選擇器）——chevron
     （.wf-icon--chevron-down，展開/收合指示用）不能一起藏掉，:not() 排除
     它，跟 ≥1468px 保持同一種寫法，日後三段一起看比較好對照。 */
@media (min-width: 992px) and (max-width: 1467px) {
    .site-header .site-nav-toggle.d-lg-none,
    .site-header .site-nav-close.d-lg-none {
        display: inline-flex !important;
    }

    .site-header .site-nav.d-lg-flex {
        display: grid !important;
    }

    html:root .site-header .site-nav > .dropdown {
        display: block;
    }

    html:root .site-header .site-nav > a .wf-icon:not(.wf-icon--chevron-down),
    html:root .site-header .site-nav > .dropdown > a .wf-icon:not(.wf-icon--chevron-down) {
        display: none;
    }
}

/* ── 覆蓋層本體 ─────────────────────────────────────────────────────── */
@media (max-width: 1467px) {
    /* ⚠ 特異度要贏過 Bootstrap 的 .collapse:not(.show){display:none}（(0,2,0)）——
       這裡是 .site-header .site-nav 同為 (0,2,0)，但排在它後面所以勝出。 */
    /* ⚠ 2026-08-03 客戶指定「選單不要滿版呈現」——改成從右側滑入的抽屜，
       左邊留一截讓底下的頁面透出來，使用者才知道自己還在原本那一頁。
       ⚠ 2026-08-03 追加：「改成右邊細長一列就好」——寬度再收到 min(68%, 280px)，
         內容也從兩欄改成單欄（見下方 grid-template-columns）。 */
    .site-header .site-nav {
        display: flex;
        position: fixed;
        inset: 0 0 0 auto;
        width: min(68%, 280px);
        z-index: 60;

        /* ⚠ 客戶指定改成「右邊細長一列」，所以是**單欄**。
           先前那版兩欄的分欄規則（nth-of-type 指定 grid-column／grid-row）
           在單欄下已無作用，一併收掉，留著只會讓下一個人以為還有兩欄。 */
        display: grid;
        grid-template-columns: minmax(0, 1fr);
        align-content: start;
        row-gap: 0;

        /* ⚠ 2026-08-03 客戶：「展開後上面不要留邊，直接填滿高」。
           上內距從 header+2.5rem 收成 header+.75rem —— 只留讓開關閉鈕那一列的量，
           第一個項目就緊接在它下面，頂端不再有一段空白。
           ⚠ 不能收到 0：關閉鈕釘在 header 那條線上（與漢堡鈕同一個位置，
             使用者的手指本來就在那裡），內距歸零的話第一個項目會被它壓住。
           ⚠ 下內距也一併從 6rem 收成 2.5rem —— 那 6rem 是留給社群圖示的，
             那一組已於同日移除。
           ⚠ 2026-08-13 客戶「header 隱藏，不要讓他上面空一塊」：選單展開時
             header 現在整個藏起來（見下面新增的規則），原本用 --header-h
             算出來的上內距是「幫底下那顆還看得見的 header 讓位置」，header
             一旦隱藏，這段內距就變成一塊沒有理由存在的空白。改成固定
             2.5rem（跟下內距對稱），關閉鈕與新加的 logo 自己撐出這一列的
             高度，不再借用 --header-h。
           ⚠ 2026-08-13 同一天再調整「logo 跟文字都往下移一點」：上內距從
             2.5rem 加到 3.25rem，logo 跟關閉鈕一起往下移；下內距維持
             2.5rem 不動（客戶只提到 logo／文字，沒提底部）。 */
        /* ⚠ 2026-08-13 複查修正：左右內距原本寫 var(--section-x)，這個 token
           是給「跟著版面一起變寬」的整頁區塊用的（tokens.css:197 起，
           992–1919px 之間固定 4.5rem＝72px，且不會因為某個元件自己寬度
           鎖死就跟著變小）。這個抽屜的寬度卻是 min(68%, 280px) ——
           過了 411px 視窗就恆定 280px，不會再變寬。72px 內距套在恆定
           280px 的窄抽屜上，左右各吃掉 72px，內容欄只剩 136px、整組被
           擠到抽屜中段，看起來就像「文字都偏靠右邊」（客戶回報 1024／1440
           兩個寬度跑版，兩者剛好都在 var(--section-x) 卡在 72px 那個
           區間 [992,1919)，≤991.98px 之所以正常是因為 --section-x 在
           那個斷點被覆寫成 1.5rem，並非這裡本來就抓對了值）。
           ⚠ 改成固定 1.5rem：抽屜寬度不隨視窗變，內距也不該隨視窗變——
           兩者本來就該綁在一起，這裡不再借用會員動的頁面級 token。 */
        padding: 3.25rem 1.5rem 2.5rem;
        /* ⚠ 收掉 .site-nav 自己的上框線（2026-08-03 客戶：「上面不要一條線」）——
           實測 border-top: 1px rgb(220,220,220)，是舊版「導覽收合在 header 底下」
           時代的分隔線：那時它是一條橫幅，需要一條線與 header 分開。
           現在是覆蓋在頁面上的抽屜，那條線變成浮在玻璃頂端的一道橫槓。 */
        border: 0;
        /* ⚠ 2026-08-13 複查修正：這個 <nav> 標記上同時掛著舊的 .wf-nav（見
           wireframe.css）與新的 .site-nav 兩個 class，元素改版時只換了
           class 名稱、沒拿掉舊的。wireframe.css 在 max-width:991px 那段
           還留著 .wf-nav{ margin-top:.8rem; ... } ——上面這行 border:0
           已經蓋掉同一段裡的 border-top，卻漏了同一段的 margin-top，
           兩邊都沒設 margin 的地方特異度打平時舊規則就贏，讓抽屜的實色背景
           在 ≤991px 整塊往下錯開 12.8px，露出後面透明的 header／Hero
           （客戶回報「手機展開選單，上方背景空一條沒填滿」）。
           ⚠ 992–1467px 沒事：wireframe.css 那條的斷點是 991px（不是全站
           慣用的 991.98px），寬一點的桌機視窗吃不到那條規則，只有真手機
           寬度會中。 */
        margin-top: 0;
        /* 不支援 backdrop-filter 時的後備：實心白（見下方 @supports）。
           ⚠ 後備一定要留：半透明沒有模糊的話，底下的頁面會原樣透上來把字吃掉。 */
        background: hsl(var(--brand-foreground));
        overflow-y: auto;
        overscroll-behavior: contain;

        /* 預設收起：往右滑出畫面，不用 display:none —— 有過場才不會硬跳。 */
        transform: translateX(100%);
        visibility: hidden;
        transition: transform 320ms var(--ease-out), visibility 0s linear 320ms;
    }

    .site-header .site-nav.is-open {
        transform: none;
        visibility: visible;
        transition: transform 320ms var(--ease-out), visibility 0s;
    }

    /* ⚠ 毛玻璃已於 2026-08-04 依客戶指定拿掉，改回純白（上面那條 background）。
       原本是 rgba(255,255,255,.82) + blur(22px) saturate(1.15)，
       要回復把這一段的註解解開即可 —— 記在這裡是因為它與 Hero 面板是同一組語彙，
       日後若兩處要一起改回毛玻璃，才知道當時用的是哪一組值。 */

    /* ── 底部裝飾：膚色與藍色兩塊矩形（2026-08-04 客戶指定）─────────────
       ⚠ 用 ::before／::after 而不是加兩個空 <div>：它們是純裝飾，
         沒有語意也不該被讀屏唸到，偽元素本來就不在無障礙樹裡。
       ⚠ pointer-events:none：它們貼在抽屜底部，不關掉的話會吃掉那一塊的點擊。
       ⚠ 兩色沿用色票裡既有的 NUDE 與 AERIAL，不另外調色 —— 與按鈕、標籤同一組。 */
    /* ⚠ 2026-08-04 客戶附參考圖澄清：是**兩條滿版的橫帶上下相接**——
       膚色在上、藍色在下，各自整條、不錯開也不左右分半。
       先前兩版（左右各半、錯開疊放）都不是這個意思，已改正。
       ⚠ 兩條的高度共用 --menu-bar-h：膚色的 bottom 必須**剛好等於**藍色的高度，
         兩個值各寫各的話，clamp 在不同視窗高會算出不同結果，中間就會露出一條白縫。 */
    .site-header .site-nav {
        --menu-bar-h: clamp(38px, 6.2vh, 54px);
    }

    .site-header .site-nav::before,
    .site-header .site-nav::after {
        content: "";
        position: absolute;
        left: 0;
        right: 0;
        height: var(--menu-bar-h);
        pointer-events: none;
    }

    /* 膚色：接在藍色正上方 */
    .site-header .site-nav::before {
        bottom: var(--menu-bar-h);
        background: hsl(var(--nude));
    }

    /* 藍色：貼在最底 */
    .site-header .site-nav::after {
        bottom: 0;
        background: hsl(var(--accent-blue));
    }

    /* ⚠ 2026-08-03 客戶回報「展開後上面有一段空白」，實測抓到兩個成因，
       都出在原本那條「鎖捲動」的 overflow:hidden：
         ① header 失去 fixed —— 開啟前 headerTop=0（黏在視窗頂），
            開啟後變成 -900（跟著文件被捲走）。header 一消失，
            它原本蓋住的那一段就露出來變成空白。
         ② 內容寬度 +15px —— 捲軸被 overflow:hidden 收掉，
            整頁跟著重排（實測 clientWidth 359 → 374）。
       所以這裡**不鎖捲動**：抽屜自己有 overscroll-behavior: contain，
       在選單裡滑不會帶著底層跑；點遮罩會關閉選單（見 home.js）。
       代價是選單開著時底層仍可捲動 —— 比「整頁跳一下、header 消失」小得多。
       ⚠ 這一段刻意留成註解而不是直接刪掉：下一個人若想再把捲動鎖加回來，
         要知道上面那兩個坑，並且改用不影響版面的做法
         （例如 body position:fixed + top 補償，或 scrollbar-gutter: stable）。 */

    /* 單欄：DOM 順序即顯示順序（服務專區／實體課程／知識專區／電子報／會員／搜尋），
       不需要任何 grid-column／grid-row 指定。 */

    /* ⚠ 2026-08-13 客戶「選單內請讓文字段落間距近一點，但與 logo 遠一點」——
       項目間距從 1.75rem 收到 1.1rem，logo 與第一個項目（服務專區）的距離
       改在 .site-nav-logo 自己的 margin-bottom 上單獨拉大（見下方），
       兩件事分開調，不會互相牽動。 */
    .site-header .site-nav > .dropdown,
    .site-header .site-nav > a {
        margin-bottom: 1.1rem;
    }

    /* 主項：粗一階、大一階（參考圖的層級）
       ⚠ 2026-08-13 客戶「選單排版樣式照圖片這樣排」（附參考圖：圖示 + 文字
         + 卡片式列，hover 一塊圓角淺底）＋「hover 效果也用一樣」——改成
         flex 一列（圖示／文字／chevron），補內距與圓角，讓每一項變成一顆
         可以整列點擊、hover 會亮一塊圓角底的列，不再是純文字連結。
       ⚠ 圓角用 .75rem，比站上其餘元件慣用的「幾乎直角 2px」明顯圓很多——
         這是參考圖指定的例外，只作用在這個元件，不影響全站其他圓角語言。 */
    .site-header .site-nav > a,
    .site-header .site-nav > .dropdown > a {
        display: flex;
        align-items: center;
        gap: .75rem;
        padding: .625rem .75rem;
        border-radius: .75rem;
        font-size: var(--text-sub);
        line-height: 1.6;
        font-weight: var(--fw-heading);
        color: hsl(var(--heading));
        transition: background-color var(--dur-hover) var(--ease-out);
    }

    /* 圖示：跟站上其餘 wf_icon() 同一套 24×24 線性圖示，這裡只鎖顯示尺寸與
       不被壓縮；顏色吃 currentColor，跟著文字的 heading／hover 顏色走，
       不必為圖示另外指定色票。 */
    .site-header .site-nav > a .wf-icon,
    .site-header .site-nav > .dropdown > a .wf-icon {
        width: 20px;
        height: 20px;
        flex-shrink: 0;
    }

    .site-nav-label {
        flex: 1 1 auto;
    }

    /* chevron：用站上既有的 Bootstrap dropdown-toggle::after 三角形已經
       display:none（見下面），改用參考圖那種下箭頭圖示，釘在最右邊。
       ⚠ 2026-08-13 客戶「選單預設把分支選項隱藏，按箭頭再出現」——子選單
         從「攤平常駐顯示」改成預設收合、點了才展開（見下面 .dropdown-menu
         的改動），chevron 現在是**真的可互動的展開/收合指示器**，展開時
         轉 180 度朝上，呼應 Bootstrap 幫 dropdown-toggle 自動同步的
         aria-expanded（不用自己另外寫 JS 記狀態）。 */
    .site-header .site-nav .dropdown-toggle .wf-icon--chevron-down {
        width: 16px;
        height: 16px;
        margin-left: auto;
        flex-shrink: 0;
        color: hsl(var(--slate));
        transition: transform var(--dur-hover) var(--ease-out);
    }

    .site-header .site-nav .dropdown-toggle[aria-expanded="true"] .wf-icon--chevron-down {
        transform: rotate(180deg);
    }

    /* 子項：下拉選單預設收合，展開後攤平成清單（不是彈出層）
       ⚠ 2026-08-13 客戶「選單預設把分支選項隱藏案箭頭在出現」——原本這裡
         寫死 display:block，不管 Bootstrap 有沒有幫 dropdown-toggle 加
         .show 都一律顯示，等於「看起來像下拉，其實從來沒收起來過」。
         現在改成預設 display:none，只有 .show（Bootstrap 點擊
         data-bs-toggle="dropdown" 時自動加/拿掉，標記早就掛好，不必新寫
         JS）才顯示——真的會收合了。
       ⚠ position:static 等桌機不會用到的定位覆寫留在 .show 那條就好；
         Popper.js 仍會在背景幫 dropdown-toggle 算浮動定位的 inline style，
         但視覺上被這裡的 !important 蓋掉，兩邊互不干擾（桌機水平導覽列的
         下拉走的是同一顆 Bootstrap 元件，此媒體查詢外的桌機規則不受影響）。
       ⚠ 內縮改成 2.75rem：對齊上面主項「圖示(20px) + gap(.75rem)」之後
         文字開始的位置，子項文字才會跟主項的文字對齊同一條左邊界，不是
         對齊主項最左邊的圖示。 */
    .site-header .site-nav .dropdown-toggle::after {
        display: none;
    }

    .site-header .site-nav .dropdown-menu {
        display: none;
    }

    .site-header .site-nav .dropdown-menu.show {
        display: block !important;
        position: static !important;
        float: none;
        width: auto;
        min-width: 0;
        margin: .375rem 0 0;
        padding: 0 0 0 2.75rem;
        border: 0;
        border-radius: 0;
        background: transparent;
        box-shadow: none;
        transform: none !important;
        inset: auto !important;
    }

    .site-header .site-nav .dropdown-item {
        padding: .35rem 0;
        font-size: var(--text-body);
        line-height: 1.9;
        font-weight: var(--fw-light);
        color: hsl(var(--muted-foreground));
        background: transparent;
        border-radius: 0;
    }

    /* ⚠ 2026-08-04 客戶回報「點擊會有淺灰色背景」——那是 Bootstrap 的
       .dropdown-item:hover/:focus{background-color:#e9ecef} 與 :active 的灰塊。
       在導覽列上它是「選單項目被指到」的合理回饋，但這裡的項目是**攤平的清單**，
       灰塊會變成憑空多出來的一格色底。改成只用文字轉深，與站上其他連結一致。
       ⚠ :active 一定要一起寫：手機上是「按下去那一瞬間」才會套到，
         只寫 hover／focus 的話點下去仍然會閃一下灰。
       ⚠ 2026-08-13 主項（.site-nav > a／.dropdown > a）改成套參考圖的圓角
         淺底 hover（跟客戶「hover 效果也用一樣」的指定一致）；子項
         （.dropdown-item）維持只換文字顏色、不疊底色——參考圖裡子項本來
         就是縮排純文字，沒有各自一塊底色，兩層 hover 語言刻意不同。 */
    .site-header .site-nav .dropdown-item:hover,
    .site-header .site-nav .dropdown-item:focus,
    .site-header .site-nav .dropdown-item:active,
    .site-header .site-nav .dropdown-item.active {
        background: transparent;
        color: hsl(var(--heading));
    }

    .site-header .site-nav > a:hover,
    .site-header .site-nav > a:focus,
    .site-header .site-nav > a:active,
    .site-header .site-nav .dropdown > a:hover,
    .site-header .site-nav .dropdown > a:focus,
    .site-header .site-nav .dropdown > a:active {
        background: hsl(var(--foreground) / .07);
        color: hsl(var(--heading));
    }

    /* 搜尋在覆蓋層裡回到一般連結的樣子（膠囊底是導覽列上的造型），並比照
       2026-08-13 那次「排版照參考圖」跟其他項目一樣的圖示＋文字列。
       ⚠ 標記是「文字在前、放大鏡在後」（桌機膠囊的順序），這裡用 order
       把放大鏡視覺上排到文字前面，跟其他項目「圖示在前」一致，不動 DOM。 */
    .site-header .site-nav .site-search {
        gap: .75rem;
        padding: .625rem .75rem;
        border-radius: .75rem;
        background: transparent;
    }

    .site-header .site-nav .site-search-icon {
        order: -1;
        width: 20px;
        height: 20px;
    }

    .site-header .site-nav .site-search:hover,
    .site-header .site-nav .site-search:focus-visible {
        background: hsl(var(--foreground) / .07);
    }

    /* ── 右上角關閉鈕 ───────────────────────────────────────────────
       ⚠ 2026-08-13 top 從 calc(var(--header-h) / 2) 改成固定 2.5rem：原本是
         對齊「底下那顆還看得見的 header」的垂直中心，header 隱藏後那條參考線
         不再有意義，改成跟 .site-nav 新的上內距（2.5rem）同一個基準，
         與新加的 .site-nav-logo 同一列對齊。
       ⚠ 2026-08-13 同一天「logo 跟文字都往下移一點」，.site-nav 的上內距
         跟著從 2.5rem 加到 3.25rem，這裡一度同步改成 3.25rem 跟 logo 對齊。
       ⚠ 2026-08-13 客戶再指定「叉叉往上移一點」——關閉鈕不再跟 logo 綁同一個
         數字，改回 2.5rem（比 logo 的 3.25rem 高一階），兩者分開各自調整。 */
    .site-nav-close {
        position: absolute;
        top: 2.5rem;
        /* ⚠ 2026-08-13 複查修正：跟上面 .site-nav 的 padding 同一個坑——
           right 原本抓 var(--section-x)，992–1467px 之間是 72px，跟這裡
           已經改成固定 1.5rem 的抽屜內距對不上，叉叉因此比內容的右邊界
           還要往左一大截（客戶回報「叉叉又改太左邊了」）。改成同一個
           固定值，兩者才會對在同一條線上，不管視窗多寬都一樣。 */
        right: 1.5rem;
        transform: translateY(-50%);

        display: inline-flex;
        align-items: center;
        justify-content: center;
        width: 2.25rem;
        height: 2.25rem;
        padding: 0;
        border: 0;
        background: transparent;
        color: hsl(var(--heading));
        cursor: pointer;
    }

    /* × 用兩條交叉的線畫（與漢堡鈕同一套：線條、非字元） */
    .site-nav-close span {
        position: relative;
        display: block;
        width: 20px;
        height: 20px;
    }

    .site-nav-close span::before,
    .site-nav-close span::after {
        content: "";
        position: absolute;
        top: 50%;
        left: 0;
        width: 100%;
        height: 1.5px;
        background: currentColor;
    }

    .site-nav-close span::before { transform: rotate(45deg); }
    .site-nav-close span::after  { transform: rotate(-45deg); }

    /* ── 抽屜自己的 logo（2026-08-13 客戶指定）─────────────────────────
       客戶：「漢堡選單展開時 header 隱藏，並且在漢堡選單展開的頁面最上面加
             logo 圖片。」
       ⚠ header 隱藏之後，抽屜左側原本透出來的那顆 logo 也一起看不到，這裡
         補一顆在抽屜自己裡面，放在最上面、跟關閉鈕同一列（關閉鈕在右上角，
         logo 在左邊，兩者共用 2.5rem 的上內距基準，不會疊在一起）。
       ⚠ 是 .site-nav 的第一個文件流子元素（關閉鈕是 position:absolute，
         不佔文件流），所以自然落在最上面，margin-bottom 把它跟第一個導覽
         項目（服務專區）分開。
       ⚠ 圖示高度、文字級距、顏色都比照 header／footer 那兩顆（16px、
         #4a4636），同一顆 logo 三個出場位置維持同一套視覺語言。
       ⚠ 2026-08-13 客戶「與 logo 遠一點」：margin-bottom 從 2rem 拉大到
         3.25rem，跟同時收窄的項目間距（1.75rem → 1.1rem）拉開明顯差距——
         logo 是品牌識別的獨立區塊，導覽項目是緊湊的清單，兩者的節奏本來
         就該不一樣，不能共用同一個間距數字。 */
    .site-nav-logo {
        display: inline-flex;
        align-items: center;
        gap: .625rem;
        /* ⚠ 2026-08-13 從 3.25rem 收到 2.25rem：下面新增的 .site-nav-section-label
           自己還有一段 padding-bottom，兩段距離疊在一起，logo 這邊不用再撐那麼開，
           否則「logo 遠一點」會變成「logo 遠很多」。
       ⚠ 2026-08-13 客戶「logo 靠左一點，跟英文字對齊」——.site-nav-logo
         也是 .site-nav 的直接子層 <a>，會一併命中 .site-header .site-nav
         > a 那條給導覽項目用的 padding:.625rem .75rem（列的內距），把圖示
         往右推了 12px，跟下面 SERVICE／CLASS／MEMBER 小標文字的左緣對不齊
         （小標本身沒有內距）。這裡明確把 padding 歸零，logo 改回貼齊
         .site-nav 自己的內距基準，不繼承導覽項目那份。
         ⚠ 特異度：對手 .site-header .site-nav > a 是 (0,2,1)（兩個 class
         + 一個元素選擇器），單寫 .site-header .site-nav-logo 只有
         (0,2,0)，還是贏不了（實測 padding 沒有真的歸零）。這裡直接用
         !important，不繼續疊 class 湊特異度。 */
        margin-bottom: 2.25rem;
        text-decoration: none;
    }

    .site-header .site-nav-logo {
        padding: 0 !important;
    }

    .site-nav-logo-icon {
        display: block;
        width: auto;
        height: 24px;
        flex-shrink: 0;
    }

    /* ⚠ 2026-08-13 客戶先說「選單只要 logo 圖，品牌名刪掉」，隔一輪又補參考圖
       改口「logo 右邊加品牌名，請在同一行不要換行」——以後面這次為準，
       文字改回顯示。.site-nav-logo 本來就是 inline-flex（不換行），
       這裡只要不再 display:none 文字就會自然跟圖示排在同一行。 */
    .site-nav-logo-text {
        font-size: 1rem;
        font-weight: var(--fw-normal);
        letter-spacing: var(--tracking-title);
        color: #4a4636;
        white-space: nowrap;
    }

    /* ⚠ 2026-08-13 客戶再次澄清「線加在中文字底下」的意思：「中文」指的是
       選單裡本來就有的中文項目名稱（服務專區、實體課程…），不是要在小標
       旁邊另外加一段中文翻譯。正確順序是「英文小標 → 該組的中文項目 →
       線」，線要畫在**每組內容結束的地方**，不是緊跟在英文小標文字後面。
       上一版誤把線畫在小標本體自己下面（文字 + padding-bottom +
       border-bottom），這裡拿掉，小標只剩純文字；線改到各組最後一個
       項目上（.site-nav-group-end，見「服務專區」與「知識專區」的標記），
       跟 CLASS 組原本就對的做法統一。
       ⚠ 字體／字級／字距沿用全站既有的英文小標語言（--font-eyebrow／
         --text-meta／--tracking-meta，跟 SERVICE ZONE、NEWSLETTER 那些
         小標同一組值）。 */
    .site-nav-section-label {
        margin: 1.75rem 0 1.1rem;
        font-family: var(--font-eyebrow);
        font-size: var(--text-meta);
        line-height: var(--lh-meta);
        letter-spacing: var(--tracking-meta);
        color: hsl(var(--slate));
    }

    /* SERVICE：第一組，緊接在 logo 後面——上內距歸零，避免跟 logo 自己的
       margin-bottom 疊加成過大的空白。 */
    .site-nav-section-label--first {
        margin-top: 0;
    }

    /* 每組最後一項的底部線（客戶：「英文小標 → 中文項目 → 線」，線在該組
       內容結束的地方）。SERVICE 組只有「服務專區」一項，它同時也是
       .dropdown > a（下拉觸發鈕），跟 CLASS 組「知識專區」是 .site-nav
       的直接子層 a 結構不同，兩種都要涵蓋到。
       ⚠ MEMBER 是最後一組，後面沒有別組要分隔，不加線。 */
    .site-header .site-nav > a.site-nav-group-end,
    .site-header .site-nav .dropdown > a.site-nav-group-end {
        padding-bottom: 1.1rem;
        border-bottom: 1px solid hsl(var(--foreground) / .12);
    }

    /* ── header 隱藏（2026-08-13 客戶指定，客戶回報 :has() 那版仍看得到一塊）──
       客戶：「漢堡選單展開時 header 連同背景色都隱藏，讓圖片上面不要白一塊。」
       ⚠ 原本用 .site-header:has(.site-nav.is-open){visibility:hidden} ——
         邏輯上應該連背景一起藏住，但客戶回報畫面上仍留了一塊沒藏乾淨。
         不繼續猜 :has() 在客戶瀏覽器裡的行為，改用**已經在別處驗證過會動**
         的 .has-menu-open（home.js 開關選單時掛在 <html> 上，遮罩／z-index
         那兩條規則都靠它，同一個機制，不再多一種選擇器語法）。
       ⚠ 這次直接、逐項寫死要藏的東西（背景／模糊／陰影／logo／字級切換／
         漢堡鈕），不靠「藏 header、visibility 往下繼承、nav 再撐回來」這條
         隱含的鏈路——每一項各自一條規則，比較囉唆但每一條各自看得懂在藏
         什麼，日後排查也不必先想一遍 visibility 繼承規則。
       ⚠ background／backdrop-filter／box-shadow 直接歸零：這三個是「白一塊」
         最可能的來源（header 半透明白底 + 毛玻璃模糊），visibility 理論上
         也會蓋掉，但這裡不依賴那條推理，直接指定成 none。

       ⚠ 2026-08-13 稽核補漏：這條規則從第一版寫下來就只歸零了三個屬性，
         從沒管過 transition 與 border-bottom，這裡一次補齊，原因分開記：
         【transition】.site-header 在 ≥992px（4364、4639 兩處）本來就掛著
           `transition: background var(--transition)`（＝ 200ms）。這條規則
           只寫了「目的值」（transparent），沒寫 transition:none —— CSS 規則
           是特異度／!important 只決定「終點值」，不影響「要不要用動畫過
           渡」，所以選單一開，背景其實是花 200ms 淡出，不是瞬間消失，過
           程中會有一塊「還沒淡完、仍畫得出來」的米色矩形。抽屜自己滑入是
           320ms（下面 .site-nav 的 transition:transform），比這 200ms 還
           慢，兩者疊在一起，那 0–200ms 之間的畫面正好會同時看到「抽屜滑
           到一半」＋「header 底色還沒淡完」。這也解釋了為什麼開發端用自
           動化工具（截圖前通常會等動畫穩定、或用不會出現這個斷點的手機寬
           度測試）怎麼測都重現不了，但使用者用真人手速在桌面寬度點擊、系
           統截圖工具隨機時間點按下快門，反而每次都拍得到——這是一個跟時
           間點強相關的暫態現象，不是規則沒生效。這裡補 transition:none
           !important，讓透明化在該畫格內瞬間完成，不再留這個 200ms 窗口。
         【border-bottom】header 底部一直有條 1px 底線在畫：≤991.98px 是
           wireframe.css:70 的 `border-bottom:1px solid var(--wf-hair)`（灰
           色，沒有 !important 但這條規則之前完全沒去動 border，所以它照樣
           畫著）；992–1467px、.is-designed 頁面則是 4632-4640／4668-4673
           的 `border-bottom-color`，會依 .is-scrolled 在透明與棕褐色
           （--brand-accent）之間切換，這個顏色切換同樣掛在上面那條
           border-color transition 上。不管哪種寬度，這條線從來沒被這系列
           「藏 header」的規則處理過，先前回報的「一條細線」很可能就是它
           （跟遮罩、z-index 完全無關，遮罩拿掉後線還在，也吻合）。這裡直
           接歸零，跟 background 一樣不留動畫窗口。 */
    .has-menu-open .site-header {
        background: transparent !important;
        -webkit-backdrop-filter: none !important;
        backdrop-filter: none !important;
        box-shadow: none !important;
        border-bottom: 0 !important;
        transition: none !important;
    }

    .has-menu-open .site-header .site-logo,
    .has-menu-open .site-header .font-size-switch,
    .has-menu-open .site-header .site-nav-toggle {
        visibility: hidden;
    }


    /* ── header 隱藏改用「整組蓋掉」（2026-08-13，第三次處理同一個回報）───
       背景：上面兩組規則（transparent 系＋逐一 visibility:hidden）已經改了
       兩輪，使用者仍然回報同一塊白色矩形反覆出現（甚至在拿掉遮罩之後又
       原樣重現一次，見這輪任務背景的完整時間軸）。問題不在「漏了哪個屬
       性」——background／backdrop-filter／box-shadow／border／transition
       這次都補齊了——而是這個做法本身的結構：**逐一列出「header 現在該
       是什麼樣子」＋「.inner 底下哪幾個子元素要藏」**，任何一次新增子元
       素、任何一個屬性沒列到，都會讓白塊原樣回來，而且不容易一眼看出漏
       了什麼（要逐條核對才知道）。

       改成更粗暴、更難失手的機制：**只留 .site-nav 顯示，.inner 底下其餘
       所有直接子節點整組 display:none**。理由：
       ⚠ display:none 是「整個節點連同它的背景／邊框／內容都不參與版面
         與繪製」，不是「調某幾個視覺屬性看起來像不存在」——不會有「這個
         子元素有背景色但我沒列到」這種漏洞，因為它根本不會被畫出來，
         徹底繞開了「還要不要逐一補列屬性」這個問題。
       ⚠ 用 `> .inner > *:not(.site-nav)` 而不是列出 .site-logo／
         .font-size-switch／.site-nav-toggle 三個 class 名：`.inner` 目前
         就是這三個加 .site-nav 四個直接子節點（見 _header.php 38-243
         行），用「排除法」寫的話，日後 header 裡再加任何新的手足元素，
         不用回頭多寫一條規則、也不會被漏掉——結構本身就保證涵蓋到。
       ⚠ 不影響 header 自己的高度：header 高度是釘死在 .site-header 自己
         的 `height:var(--header-h)`（見本檔「Header 高度改成 56px」那段，
         --header-h 是獨立 SSOT），不是由 .inner 的內容撐出來的，所以把
         .inner 的子節點藏掉不會讓 header 的版面高度塌陷——這點已經在瀏覽
         器裡實測過（見下方驗證紀錄），不是只憑推論。
       ⚠ .site-nav（抽屜本體）完全不受影響：選擇器用 :not(.site-nav) 明確
         排除，抽屜自己的 display/position:fixed/transform 等規則原封不動。
       ⚠ 舊的 transparent 系＋逐一 visibility:hidden 規則（上面這兩組）
         **沒有刪除**——本檔慣例是只加不刪，而且它們現在對 .site-logo／
         .font-size-switch／.site-nav-toggle 是完全被這條新規則涵蓋掉的
         重複保險（display:none 的元素，visibility:hidden 或 background
         再怎麼設都不會被畫出來，兩者不衝突，新規則生效時舊規則形同
         no-op）。header 本身的 transparent／border/transition 依然需要
         ——那是 .site-header **自己**的背景與邊框，不是 .inner 的子節點，
         不在這條新規則的涵蓋範圍內，兩組規則其實是處理兩個不同層級
         （header 自己 vs. header 的子節點），互補而非重複。 */
    .has-menu-open .site-header > .inner > *:not(.site-nav) {
        display: none !important;
    }

    /* ── 右下角社群 ───────────────────────────────────────────────── */
    /* 註：選單裡的社群圖示已於 2026-08-03 依客戶指定整組移除（標記也已拿掉）。
       右下角的浮動社群鈕不受影響 —— 它的造型規則在本檔後段，仍然沿用同一套
       「透明底 ＋ 細灰框 ＋ 正圓」。 */
}

/* 桌機：關閉鈕不出現（標記用 d-lg-none 已處理，這裡再保一道）
   ⚠ 2026-08-14 補 html:not(.is-compact)：捲動離開頁首收合成方形選單鈕時
     （見「桌機（≥1468px）捲動收合」新區塊），這顆鈕要能在展開狀態顯示
     成 ×；平常（不是 .is-compact）維持原樣不出現。 */
@media (min-width: 1468px) {
    html:not(.is-compact) .site-nav-close {
        display: none;
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   Header 收合成方形選單鈕（2026-08-14 客戶新指令，全斷點適用）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「使用者離開第一屏後，完整 Header 收起，畫面只保留右上角一個小型
   正方形漢堡選單，讓使用者在閱讀任何位置都能開啟導覽...若專案已經有
   Mobile Menu、Drawer 或 Menu Overlay，請直接沿用，不要建立重複選單。」

   .is-compact 由 home.js 的 IntersectionObserver 判斷（觀察頁首色帶是否
   還在畫面上），不是單純 scrollY 門檻——見 home.js 對應的新函式。
   ⚠ 這裡只處理「選單沒開」時的收合外觀（:not(.has-menu-open)）。選單一旦
     打開，2026-08-13 那組「header 整組隱藏」規則（.has-menu-open ...）已經
     處理好背景／logo／字級切換／漢堡鈕，兩組規則刻意分開、不疊加，避免
     互相打架或重複覆寫。
   ⚠ 沿用既有的 #wfNav／.site-nav-toggle／.site-nav-close 三個元素，不新增
     任何選單標記——「重用同一顆選單」的要求靠這裡的 CSS 切換外觀達成，
     不是複製一份新的導覽。 */
html.is-compact:not(.has-menu-open) .site-header .site-logo,
html.is-compact:not(.has-menu-open) .site-header .font-size-switch,
html.is-compact:not(.has-menu-open) .site-header .site-nav-primary {
    display: none;
}

/* ⚠ 2026-08-25 客戶「hero注意不要有header疊在圖片上的問題」附截圖，
   一開始查到下面 ≈4452 那條規則（`.is-designed .site-header:not(
   .is-compact)`）選擇器寫錯查詢對象（`:not(.is-compact)` 誤查
   `.site-header` 自己，該查的是 `<html>`），導致這條帶 `!important`
   的透明規則不分頁面類型全站 19 頁一律透明。當時已在 ≈4452 修正選擇器
   （改查 `html:not(.is-compact)`），順手把這裡改成只認 `:has(.hero)`
   ——想法是「沒有 Hero 的 18 頁本來就該不透明」，但這個假設是錯的：
   客戶隔一輪再次明確回報（附知識文章頁截圖）「再說一次header不要背景
   顏色只在底部加灰細線」「現在他有自己的白色背景，請不要」——那 18 頁
   本來也要透明，不要任何背景色，只留 header 底線那條灰線。

   往回查才發現：2026-08-14「Header 改回透明覆蓋樣式」那輪客戶原話就是
   「Header 初始狀態不要有白色實心背景...且要求**全站一致，不是只有
   首頁**」（本檔 ≈4427）——透明本來就該是全站規則，不是只有首頁 Hero
   放行。≈4452 那條選擇器誤查 html 而不是 .site-header，效果剛好是
   「不分狀態一律透明」，歪打正著撐住了 2026-08-14 這個「全站透明」的
   決定；這次「修正選擇器」反而把全站透明改回只有首頁，等於誤把
   2026-08-14 的決定退回更早、已經被推翻的 2026-07-30/07-31 版本
   （「沒有 Hero 的頁面要實色，否則內容會壓在導覽底下」）。

   ⚠ 修法：拿掉 `:has(.hero)` 限定，全站 19 頁在 `.is-compact`（現在
     恆為真）時一律透明，不分有沒有 Hero、不分捲動位置——回到
     2026-08-14「全站一致」的原始決定。沒有 Hero 的 18 頁靠這次新增的
     header 底線（`body:has(.wf-crumb) .site-header { border-bottom }`,
     本檔別處）提供視覺分隔，不再需要靠實色背景。 */
html.is-compact .site-header {
    background: transparent !important;
    -webkit-backdrop-filter: none !important;
    backdrop-filter: none !important;
    border-bottom: 0 !important;
    box-shadow: none !important;
}

/* ── header 捲動後補底色（08-26 指定 → 08-27 撤回 → 08-27 同日恢復）────
   三輪決策都是客戶原話，全程留著免得下次再繞回來：
     ① 2026-08-26「header滾動以後加背景」＋純色票截圖（取像素實測
        rgb(246,246,247)＝既有 --background token，不是新色；tokens.css
        註解寫的 #F5F5F7 是過期舊值，以實測為準，見
        [[reference-onedaylisten-token-stale-comments]]）。
     ② 2026-08-27 上午改口「本來不要有背景，按搜尋鍵才有背景」——這條整條
        移除、原位只留 tombstone，底色只剩「展開搜尋」一個觸發。
     ③ 2026-08-27 同日再指定「header在頂部的時候沒背景，滾動跟搜尋按鍵會
        觸發背景，目前只有搜尋有，請增加滾動也有背景」——加回來。現在
        **捲動（這條）與展開搜尋（下一條 :has()）兩個觸發並存**，各自獨立、
        同一塊 --background 灰；停在頁首（scrollY≤14）且未展開搜尋時維持
        2026-08-14「初始不要背景」的透明，這個原始決定三輪都沒動過。
   ⚠ 掛 `.is-scrolled` 不是 `.is-compact`：≥992 的 `.is-compact` 被 home.js
     的 forceCompactMq 強制恆為 true（≈417），**完全不反映捲動位置**，拿它
     當「滾動以後」的判斷會變成一進頁面就有底色。真正跟著捲動切換的是
     `.is-scrolled`（home.js ≈369，門檻 scrollY>14），而且它掛在
     `#siteHeader` 元素**自己**身上、不是掛在 `<html>`——本檔已有多次把
     狀態 class 掛錯祖先層、規則寫了從沒生效的紀錄，這個層級是查 JS 確認
     過的。
   ⚠ 特異度 (0,3,1) 才蓋得過上面那條
     `html.is-compact .site-header{background:transparent!important}` 的
     (0,2,1)——`!important` 互打是比特異度不是比順序（見
     [[reference-onedaylisten-important-specificity-tiebreak]]）。淡入靠
     `.is-designed .site-header` 既有的 `transition: background`（≈3483），
     這裡不必再宣告。
   ⚠ 底線的配對選擇器同步恢復（見 ≈14285 那組的 08-27 註解）：灰底回來
     之後，手機「已捲動但還沒 is-compact」的區間又會出現「有線沒底」的
     可能，同進同出的配對理由跟著回來。 */
html.is-compact .site-header.is-scrolled {
    background: hsl(var(--background)) !important;
}

/* ── 展開搜尋時也補同一塊灰底（2026-08-27 客戶「按搜尋的時候header就背景
   填色灰」）─────────────────────────────────────────────────────────
   前一輪把搜尋列自己的底色拿掉（客戶「搜尋匡不要背景」）之後，停在頁首
   時 header 是全透明的，展開搜尋等於把輸入框與熱門關鍵字直接疊在頁面
   內容上（實測課程列表頁會壓到「In-person Courses」標題）。這條補上：
   不管在哪個捲動位置，只要搜尋展開就讓 header 上同一塊灰，可讀性由
   header 承擔，搜尋列本身維持無底色。收合後自動回到透明，不需要 JS 介入。
   ⚠ 2026-08-27 更新：客戶接著改口「本來不要有背景，按搜尋鍵才有背景」，
     捲動觸發的那條一度整條撤掉、這條短暫成為唯一觸發；同日客戶又指定
     「滾動跟搜尋按鍵會觸發背景」，捲動那條已加回（見上一段的三輪沿革）。
     現在**兩個觸發並存**：捲動（.is-scrolled）或展開搜尋（這條）任一
     成立就有底色，兩條互不依賴、值同一塊 --background。
   ⚠ 用 :has() 反查子孫：`.is-open` 掛在 .site-inline-search 上（home.js
     setOpen()），header 自己身上沒有任何 class 反映搜尋開合，所以只能
     從 header 往內查。本檔已有 body:has(.wf-crumb)／html:has(.hero) 等
     多處同款用法，不是新引入的技法。
   ⚠ 特異度：:has() 取「引數裡最具體的那個」計分，
     `.site-inline-search.is-open` 算 2 個 class，整條
     ＝ html(元素)＋.is-compact＋.site-header＋:has(2 class) ＝ (0,4,1)，
     蓋得過 `html.is-compact .site-header{background:transparent!important}`
     的 (0,2,1)（!important 互打比特異度，見
     [[reference-onedaylisten-important-specificity-tiebreak]]）。
   ⚠ 只補背景、**不補底線**：客戶只講「背景填色灰」。底線維持綁在捲動
     狀態上（見 ≈14285 那組），所以「頁首展開搜尋」會是有灰底、無底線——
     有底沒線是常見且乾淨的做法，不影響可讀性。「捲動後」的灰底則由上面
     捲動那條補，跟底線同進同出（配對選擇器見底線那組）。 */
html.is-compact .site-header:has(.site-inline-search.is-open) {
    background: hsl(var(--background)) !important;
}

/* ⚠ 2026-08-14 補漏：選單打開時「整組蓋掉」的既有規則
   （.has-menu-open .site-header > .inner > *:not(.site-nav){display:none!important}）
   寫在 @media (max-width:1467px) 裡面（見本檔「header 隱藏改用『整組蓋掉』」那段），
   只在漢堡選單原本的寬度範圍生效。桌機（≥1468px）現在也會因為 .is-compact
   打開同一顆抽屜，但那個寬度不吃那條規則——實測：抽屜滑出來的同時，左上角
   原本的 LOGO／字級切換／方形選單鈕會一起穿幫（因為它們的隱藏規則是
   :not(.has-menu-open) 限定，選單一開就不生效，理論上要靠「整組蓋掉」那條
   接手，但那條在這個寬度沒作用，兩條規則之間出現一個沒人管的空隙）。
   這裡另外補一條不限寬度的版本，補上這個空隙。 */
html.is-compact.has-menu-open .site-header > .inner > *:not(.site-nav) {
    display: none !important;
}

/* ⚠ 2026-08-17 同類補漏：上面兩條「整組蓋掉」分別覆蓋了 ≤1467px（本檔
   「header 隱藏改用『整組蓋掉』」）與 .is-compact（緊接在上面這條）
   兩種情境，但首頁「≥1468px 且未捲動」（html:has(.hero):not(.is-compact)）
   兩者都不成立，落在同一個「沒人管的空隙」——實測選單打開後，搜尋圖示
   與方形 Menu 鈕（此時還沒被 .site-nav-close 蓋掉視覺，只是同一個按鈕
   本身的 fixed 定位規則因為 :not(.has-menu-open) 限定而失效，退回無
   定位的預設樣式）仍卡在原本位置，跟客戶截圖裡「選單上緣留一條空白，
   還看得到一顆漢堡鈕」的回報吻合。補上第三條、只鎖這個情境。 */
html:has(.hero):not(.is-compact).has-menu-open .site-header > .inner > *:not(.site-nav) {
    display: none !important;
}

/* 方形選單鈕本身：頁首時是透明的純線條標記（沿用 .site-nav-toggle 既有
   樣式，混在完整 header 列裡不需要自己的底），收合後獨立浮在畫面右上角，
   這時才需要看得見的底色／邊框——鈕若還是透明，浮在任何內容上都無法辨識。
   ⚠ 底色／邊框沿用既有 token（--card／--border），不新增顏色。
   ⚠ border-radius 用 2px：站上按鈕（.wf-btn 系）預設幾乎是直角，這裡跟著
     全站語言走，不做成圓形/膠囊。 */
html.is-compact:not(.has-menu-open) .site-header .site-nav-toggle {
    position: fixed;
    top: clamp(12px, 2vw, 24px);
    right: clamp(12px, 2vw, 24px);
    width: clamp(48px, 4vw, 58px);
    height: clamp(48px, 4vw, 58px);
    background: hsl(var(--card));
    border: 1px solid hsl(var(--border));
    border-radius: 2px;
    z-index: 70;
}

@media (prefers-reduced-motion: no-preference) {
    .site-header,
    .site-header .site-logo,
    .site-header .font-size-switch,
    .site-header .site-nav-toggle {
        transition: opacity var(--dur-hover) var(--ease-out),
                    transform var(--dur-hover) var(--ease-out),
                    background var(--transition);
    }
}

@media (prefers-reduced-motion: reduce) {
    .site-header,
    .site-header .site-logo,
    .site-header .font-size-switch,
    .site-header .site-nav-toggle,
    .site-header .site-nav {
        transition: none !important;
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   桌機（≥1468px）捲動收合：重用同一顆 #wfNav 抽屜（2026-08-14 客戶新指令）
   ═══════════════════════════════════════════════════════════════════════
   桌機平時（html 沒有 .is-compact）：.site-nav 走「桌機 Header 三段式」
   那組規則，絕對定位置中、水平排列，沒有抽屜殼（該規則已改成
   html:not(.is-compact) 限定，見上方）。

   捲動離開頁首後（html.is-compact）：借用 992–1467px 那組已經寫好、也已經
   反覆除錯過的抽屜殼與列樣式（fixed／靠右／實色底／滑入動畫／圖示＋文字列
   ／hover／分組線／收合式子選單），在這裡另外複寫一份必要的宣告，讓同一顆
   <nav id="wfNav"> 在桌機寬度也能切成抽屜長相。圖示／logo／分組小標的顯示
   與否已經在本檔「桌機隱藏抽屜專用元素」那段改成 html:not(.is-compact)
   限定，.is-compact 時會自動顯示，這裡不需要再處理一次。
   ⚠ 只複寫「殼」（定位／尺寸／背景／開合動畫）與「內容排版」（列的 flex／
     內距／圓角／hover／圖示尺寸／分組線／子選單收合），這些決定了「看起來
     像同一顆抽屜」；裝飾用的底部膚色／藍色色帶（992–1467px 那份的
     ::before／::after）刻意沒有複寫——那是純裝飾，桌機這個新情境沒有它
     也完全可用，之後要補再加，不影響可用性或「重用同一顆選單」的要求。
   ⚠ 特異度：桌機平時的定位規則已經改成 html:not(.is-compact) 限定（見
     「桌機 Header 三段式」那段），.is-compact 時那條不生效，這裡不需要
     再用 !important 蓋過去。 */
@media (min-width: 1468px) {
    /* ⚠ display 一定要 !important：標記上的 .d-lg-flex 是 Bootstrap 內建
       utility class，編譯死是 display:flex!important，一般特異度贏不了，
       實測不加 !important 這裡會維持 flex（水平排），單欄的抽屜內容跟著
       橫向擠成一排，不是預期的直向堆疊。992–1467px 那份原本就是走
       .d-lg-flex 天生失效的寬度（Bootstrap 的 lg 斷點正好卡在 992），沒有
       這個問題，只有這裡（≥1468px、Bootstrap 判定仍是「桌機」）才會撞到。 */
    html.is-compact .site-header .inner > .site-nav {
        display: grid !important;
        grid-template-columns: minmax(0, 1fr);
        align-content: start;
        row-gap: 0;
        position: fixed;
        inset: 0 0 0 auto;
        width: min(32%, 320px);
        z-index: 60;
        padding: 3.25rem 1.75rem 2.5rem;
        margin-top: 0;
        border: 0;
        background: hsl(var(--brand-foreground));
        overflow-y: auto;
        overscroll-behavior: contain;
        transform: translateX(100%);
        visibility: hidden;
        transition: transform 320ms var(--ease-out), visibility 0s linear 320ms;
    }

    html.is-compact .site-header .inner > .site-nav.is-open {
        transform: none;
        visibility: visible;
        transition: transform 320ms var(--ease-out), visibility 0s;
    }

    html.is-compact .site-header .site-nav > .dropdown,
    html.is-compact .site-header .site-nav > a {
        margin-bottom: 1.1rem;
    }

    html.is-compact .site-header .site-nav > a,
    html.is-compact .site-header .site-nav > .dropdown > a {
        display: flex;
        align-items: center;
        gap: .75rem;
        padding: .625rem .75rem;
        border-radius: .75rem;
        font-size: var(--text-sub);
        line-height: 1.6;
        font-weight: var(--fw-heading);
        color: hsl(var(--heading));
        box-shadow: none;
    }

    html.is-compact .site-header .site-nav > a .wf-icon,
    html.is-compact .site-header .site-nav > .dropdown > a .wf-icon {
        width: 20px;
        height: 20px;
        flex-shrink: 0;
    }

    html.is-compact .site-header .site-nav .dropdown-toggle .wf-icon--chevron-down {
        width: 16px;
        height: 16px;
        margin-left: auto;
        flex-shrink: 0;
        color: hsl(var(--slate));
        transition: transform var(--dur-hover) var(--ease-out);
    }

    html.is-compact .site-header .site-nav .dropdown-toggle[aria-expanded="true"] .wf-icon--chevron-down {
        transform: rotate(180deg);
    }

    html.is-compact .site-header .site-nav > a:hover,
    html.is-compact .site-header .site-nav > a:focus,
    html.is-compact .site-header .site-nav .dropdown > a:hover,
    html.is-compact .site-header .site-nav .dropdown > a:focus {
        background: hsl(var(--foreground) / .07);
        color: hsl(var(--heading));
    }

    html.is-compact .site-header .site-nav .dropdown-toggle::after {
        display: none;
    }

    html.is-compact .site-header .site-nav .dropdown-menu {
        display: none;
    }

    html.is-compact .site-header .site-nav .dropdown-menu.show {
        display: block !important;
        position: static !important;
        float: none;
        width: auto;
        min-width: 0;
        margin: .375rem 0 0;
        padding: 0 0 0 2.75rem;
        border: 0;
        border-radius: 0;
        background: transparent;
        box-shadow: none;
        transform: none !important;
        inset: auto !important;
    }

    html.is-compact .site-header .site-nav .dropdown-item {
        padding: .35rem 0;
        font-size: var(--text-body);
        line-height: 1.9;
        font-weight: var(--fw-light);
        color: hsl(var(--muted-foreground));
        background: transparent;
        border-radius: 0;
    }

    html.is-compact .site-header .site-nav .dropdown-item:hover,
    html.is-compact .site-header .site-nav .dropdown-item:focus,
    html.is-compact .site-header .site-nav .dropdown-item:active {
        background: transparent;
        color: hsl(var(--heading));
    }

    html.is-compact .site-header .site-nav .site-search {
        gap: .75rem;
        padding: .625rem .75rem;
        border-radius: .75rem;
        background: transparent;
    }

    html.is-compact .site-header .site-nav .site-search-icon {
        order: -1;
        width: 20px;
        height: 20px;
    }

    html.is-compact .site-header .site-nav .site-search:hover,
    html.is-compact .site-header .site-nav .site-search:focus-visible {
        background: hsl(var(--foreground) / .07);
    }

    html.is-compact .site-header .site-nav-logo {
        display: inline-flex;
        align-items: center;
        gap: .625rem;
        margin-bottom: 2.25rem;
        text-decoration: none;
        padding: 0 !important;
    }

    html.is-compact .site-nav-logo-icon {
        display: block;
        width: auto;
        height: 24px;
        flex-shrink: 0;
    }

    html.is-compact .site-nav-logo-text {
        font-size: 1rem;
        font-weight: var(--fw-normal);
        letter-spacing: var(--tracking-title);
        color: #4a4636;
        white-space: nowrap;
    }

    html.is-compact .site-nav-section-label {
        margin: 1.75rem 0 1.1rem;
        font-family: var(--font-eyebrow);
        font-size: var(--text-meta);
        line-height: var(--lh-meta);
        letter-spacing: var(--tracking-meta);
        color: hsl(var(--slate));
    }

    html.is-compact .site-nav-section-label--first {
        margin-top: 0;
    }

    html.is-compact .site-header .site-nav > a.site-nav-group-end,
    html.is-compact .site-header .site-nav .dropdown > a.site-nav-group-end {
        padding-bottom: 1.1rem;
        border-bottom: 1px solid hsl(var(--foreground) / .12);
    }

    /* 右上角關閉鈕：桌機平時不出現（見上方 html:not(.is-compact)
       .site-nav-close{display:none}），.is-compact 時改成跟方形選單鈕
       同一個位置／尺寸／底色浮出來，開合切換與 992–1467px 是同一套
       視覺語言（兩條交叉線畫出的 ×）。 */
    html.is-compact .site-nav-close {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        position: fixed;
        top: clamp(12px, 2vw, 24px);
        right: clamp(12px, 2vw, 24px);
        width: clamp(48px, 4vw, 58px);
        height: clamp(48px, 4vw, 58px);
        background: hsl(var(--card));
        border: 1px solid hsl(var(--border));
        border-radius: 2px;
        color: hsl(var(--heading));
        cursor: pointer;
        z-index: 70;
    }

    html.is-compact .site-nav-close span {
        position: relative;
        display: block;
        width: 20px;
        height: 20px;
    }

    html.is-compact .site-nav-close span::before,
    html.is-compact .site-nav-close span::after {
        content: "";
        position: absolute;
        top: 50%;
        left: 0;
        width: 100%;
        height: 1.5px;
        background: currentColor;
    }

    html.is-compact .site-nav-close span::before { transform: rotate(45deg); }
    html.is-compact .site-nav-close span::after  { transform: rotate(-45deg); }

    /* 方形選單鈕在這個寬度平時走 Bootstrap 的 d-lg-none（隱藏），.is-compact
       時要跟 992–1467px 那條既有的強制顯示規則一樣改回顯示。 */
    html.is-compact .site-header .site-nav-toggle {
        display: inline-flex !important;
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   右下角浮動社群鈕改用漢堡選單那一套造型（2026-08-03 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「直接把懸浮視窗的按鈕樣式改成漢堡選單的。」

   選單裡那組是「透明底 ＋ 一圈細灰框 ＋ 深色圖示 ＋ 正圓」；浮動鈕原本是
   AERIAL 藍的實心方塊 ＋ 投影。統一成同一套之後，全站的社群鈕只有一種長相。

   ⚠ 兩處刻意共用同一組宣告值而不是各寫一份 —— 日後客戶再調造型時，
     只會有一個地方要改。
   ⚠ 特異度要蓋過 .is-designed .wf-float a（(0,3,0)），所以這裡也用同樣的前綴。
   ⚠ 浮動鈕會走到滿版照片上（Hero、服務專區），透明底＋細框在深色照片上
     邊界會變弱 —— 補一層很淡的底色（頁面底 88%），照片仍然透得出來，
     但框不會消失。這是它與選單裡那組唯一的差別，原因記在這裡。 */
.is-designed .wf-float a {
    width: 2.75rem;
    height: 2.75rem;
    border: 1px solid hsl(var(--slate) / .5);
    border-radius: 50%;
    background: hsl(var(--background) / .88);
    color: hsl(var(--heading));
    box-shadow: none;
}

.is-designed .wf-float a svg {
    width: 18px;
    height: 18px;
}

.is-designed .wf-float a:hover,
.is-designed .wf-float a:focus-visible {
    border-color: hsl(var(--heading));
    background: hsl(var(--background));
    color: hsl(var(--heading));
}


/* ═══════════════════════════════════════════════════════════════════════
   選單改成抽屜之後的兩件事（2026-08-03 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「服務專區漢堡選單僅留顧問案跟 AI 分身，然後選單不要滿版呈現。」 */
@media (max-width: 1467px) {
    /* ── 遮罩：抽屜以外的區域壓一層淡色 ─────────────────────────────
       ⚠ 不只是為了好看：抽屜沒有蓋滿之後，底下的頁面仍然看得到也點得到，
         沒有遮罩的話使用者會分不清哪一層才是現在能操作的。
       ⚠ 掛在 body::after 而不是 nav 裡面 —— 它要蓋住抽屜**以外**的地方，
         放在 nav 內部做不到。z-index 55 壓在抽屜（60）之下。
       ⚠ 點遮罩關閉選單的行為在 home.js。
       ⚠ 2026-08-13 客戶回報頂部一直有一條細線（先後試過幫 .hero 補內距、
         幫這條遮罩多蓋 2px，都還在），客戶指示「把背景遮罩刪除」——
         這條線的成因是遮罩（body::after，獨立定位）與 .hero（另外用負
         margin 頂上去）兩個各自獨立換算位置的滿版圖層對不齊，只要拿掉
         其中一層，就不會再有「兩層邊界對不齊」這件事。
       ⚠ 用 display:none 蓋掉，原本的 content／position／inset／z-index／
         background 都留著不刪——要復原只要拿掉 display:none 這一行，
         不必回頭重寫這幾個值。
       ⚠ 關閉後不影響功能：「點抽屜以外關閉選單」的邏輯在 home.js
         （document 監聽點擊，檢查 e.target 是否在 nav／toggle 之外），
         是抓真正的 DOM 點擊目標，不依賴這個純視覺的 ::after 偽元素，
         遮罩拿掉後點擊底下頁面一樣會關閉選單。
       ⚠ 拿掉遮罩後底下的頁面內容不會再被壓暗——如果客戶之後覺得
         「看不出選單開著、底下還可以正常點」，代表遮罩其實是需要的，
         屆時要处理的是「兩層對齊」而不是「遮罩要不要存在」，這裡先照
         客戶目前的明確指示做。 */
    .has-menu-open body::after {
        display: none;
        content: "";
        position: fixed;
        inset: -2px;
        z-index: 55;
        background: hsl(var(--foreground) / .35);
    }

    /* ⚠ 抽屜在 .site-header 裡面，而 header 自己是 position:fixed ＋ z-index，
       所以它是一個**獨立的堆疊環境** —— 抽屜寫 z-index:60 只在 header 內部有效，
       對外仍然是 header 那一層。header 的層級若低於遮罩，整個抽屜會被一起蓋灰
       （第一版實測：抽屜連同 header 都變灰）。
       選單開著時把 header 整層抬到遮罩之上，抽屜才會亮起來。 */
    .has-menu-open .site-header {
        z-index: 60;
    }

    /* ── 服務專區在選單裡只列兩項 ─────────────────────────────────
       ⚠ 只在手機選單隱藏，桌機的下拉仍然是完整六項 —— 服務清單的權威
         來源是 Wireframe::services()，這裡沒有動資料，只是窄幅少顯示幾列。
       ⚠ 用 nth-child(n+3) 而不是列出要藏的四個 slug：後台增刪服務時
         規則不用跟著改，永遠是「留前兩項」。 */
    .site-header .site-nav > div:nth-of-type(1) .dropdown-menu li:nth-child(n + 3) {
        display: none;
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   服務專區手機版兩處修正（2026-08-03 客戶回報）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「手機版的服務專區顧問案的文字變白色了看不到，
          服務專區的介紹文字離顧問案太近，留點空間。」

   ⚠ 白字看不到的成因：區塊改成 SLATE 深底之後，站上既有的 ≤991 規則把
     .svc-main 整組文字翻回米白（那是「窄幅整段都是深底」時代的正解）——
     但**主打服務那一塊自己有淺色面板**（.svc-feature 的底是 --background），
     米白字壓在淺面板上就消失了。實測 rgb(255,255,255) 壓 rgb(246,246,247)。
   ⚠ 卡片（.svc-tile）不受影響：它們的標題本來就是黑的（實測 rgb(0,0,0)），
     所以只需要修主打這一塊，不要整組翻回去。 */
@media (max-width: 1023.98px) {
    .service-section .svc-main .svc-feature-name { color: hsl(var(--heading)); }
    .service-section .svc-main .svc-feature-desc { color: hsl(var(--muted-foreground)); }
    .service-section .svc-main .svc-feature-cat  { color: hsl(var(--heading)); }

    /* 淺面板上的字重回到米白區那一組（深底的 310/410 在淺底上太細） */
    .service-section .svc-feature {
        --fw-light:   350;
        --fw-normal:  450;
        --fw-heading: 350;
    }

    /* ── 介紹文與主打之間留出間距 ─────────────────────────────────
       ⚠ 加在 .svc-main 的 margin-top，不是 .svc-intro-desc 的 margin-bottom：
         .svc-intro 用了 display:contents 把孩子攤平，介紹文的下邊距
         在某些頁面（沒有主打服務時）會變成整段結尾多出一塊空白。
         掛在後面那一塊身上，「兩者之間」的距離才只在兩者同時存在時出現。 */
    .service-section .svc-main {
        margin-top: 2.5rem;
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   服務專區手機版：每一塊滿版出血、卡片圖片貼齊右緣（2026-08-04 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「服務專區手機版請將每個區塊滿版展示不要有 margin 留邊，
          但文字不要貼邊只是背景不要有留邊直接延伸，
          然後圖片 2 3 區塊直接貼齊右邊。」

   拆成三件事，少做一件就還是看得到留邊：
     ① 出血     .svc-main 用等量的負邊距抵銷 .section-bleed-inner 的左右內距。
                主打服務（顧問案）的大圖與白面板、下方每一張服務卡的底色，
                因此全部直接鋪到視窗兩緣。
     ② 文字補回 出血是「底色」的事，文字不能跟著貼邊 ——
                內距改由每一塊自己補，值取 --section-x，
                所以卡內文字與上方的「服務專區」主標、介紹文、
                下方那顆「點擊查看更多項目」落在**同一條格線**上。
     ③ 貼右緣   服務卡是「左文右圖」的兩欄（見上一段 ≤991 的 .svc-tile），
                右內距收成 0，右欄的圖片就直接切齊視窗右緣。

   ⚠ 負邊距用 --section-x 的負值，不用 (100% − 100vw) / 2：
     窄幅的 .section-bleed-inner 還沒到 max-width，寬度就等於文件寬，
     內距的等量負值剛好停在文件邊緣。100vw 會**含捲軸**
     （實測文件 485 ／視窗 500），多推的那 15px 會被 .service-section 的
     overflow-x: clip 裁掉 —— 症狀正是圖片右側少一截（7/31 已經踩過同一個坑）。
   ⚠ 只寫 margin-inline，不用 margin 簡寫：.svc-main 的上下邊距由另外兩條規則
     在管（margin-block: 0 與上一段的 margin-top: 2.5rem），簡寫會一起洗掉。
   ⚠ 卡片底色是 .svc-tile::before（inset: 0 的填色層），跟著 padding box 一起變寬，
     不必另外處理；由左往右填的進場動畫（scaleX）也照舊。
   ⚠ 圓角一併收成 0：--radius-media 窄幅只有 2px，平常看不出來，
     但貼在視窗邊緣時那 2px 會露出底下的區塊底色，看起來像沒對齊。
   ⚠ 「圖片 2、3 區塊」＝ 網格裡的服務卡（目前是 AI 分身／企業內訓）。
     規則寫在 .svc-tile 上而不是 nth-child(2)、(3)：那兩張只是**展開前**看得到的
     前兩張，點開「查看更多」之後的每一張都是同一個版型；
     寫死序號的話後面幾張會對不齊，而且後台調整服務排序就整批錯位。 */
@media (max-width: 1023.98px) {
    .service-section .svc-main {
        margin-inline: calc(var(--section-x) * -1);
    }

    /* 主打服務：圖片滿版到兩緣，白面板裡的文字回到格線上 */
    .service-section .svc-feature-info {
        padding-inline: var(--section-x);
    }

    .service-section .svc-feature-media {
        border-radius: 0;
    }

    /* 服務卡：左邊文字回到格線，右邊收成 0 讓圖片切齊視窗右緣 */
    .service-section .svc-tile {
        padding-inline: var(--section-x) 0;
    }

    .service-section .svc-tile-media {
        border-radius: 0;
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   服務專區手機版：卡片照片上下也填滿（2026-08-04 客戶追加）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「服務專區的照片直接上下填滿也不要有空間，在手機版。」
   接續上一段的左右出血 —— 同一件事換成垂直方向：照片的上下緣＝色塊的上下緣。

   現況（改前實測 500px）：卡片上下各一份內距 24px，照片是置中的 4:3 小圖，
   高 132px 對上整格 195px，上下各露出約 31px 的底色。

   做法是**把上下內距從卡片搬到左欄的文字**，不是給照片開負邊距：
     .svc-tile       padding-block → 0     色塊本身不再有上下留白
     .svc-tile-info  padding-block → 原值   文字自己留出上下呼吸
     .svc-tile-media 直接吃滿整格高度
   ⚠ 為什麼不用負邊距：那要把 clamp(1.5rem, 2.2vw, 2.5rem) 再抄一次，
     兩份值日後一定會不同步（改了卡片內距忘了改照片，縫就回來了）。
     搬家之後「上下留白」只有一個地方在管。
   ⚠ align-items 由 center 改成 stretch：置中的話照片只停在自己的高度，撐不到整格。
   ⚠ aspect-ratio 必須放掉：4:3 是「hover 才浮出的游標小圖」時代留下來的比例，
     留著會跟 stretch 打架（比例優先，照片仍然填不滿）。放掉之後高度由整格決定，
     .svc-media img 的 object-fit: cover 會自己裁，照片不會變形。
   ⚠ 副作用（刻意的）：卡片高度從此完全由左欄文字決定，照片不再參與撐高 ——
     這正是「上下不要有空間」要的結果。文字很短的卡片會比較矮、照片跟著變扁，
     但仍然貼齊上下緣，不會出現縫。
   ⚠ 主打服務（顧問案）那張大圖本來就沒有上下內距，不必動。 */
@media (max-width: 1023.98px) {
    .service-section .svc-tile {
        /* ＝ 上一代 .svc-tile 那份 padding 的同一個值，只是換一個角色持有 */
        --svc-tile-pad-y: clamp(1.5rem, 2.2vw, 2.5rem);

        padding-block: 0;
        align-items: stretch;
    }

    .service-section .svc-tile-info {
        padding-block: var(--svc-tile-pad-y);
    }

    .service-section .svc-tile-media {
        aspect-ratio: auto;
        height: auto;
    }

    /* ⚠ 展開全部服務後複查抓到：最後一張卡還留著**兩欄時代**的 border-bottom
         （1px／白 16%，見 ≤767 的 .svc-tile:nth-last-child(-n+2):nth-child(odd)）。
         那條線原本是「最後一列只有左欄一格」時用來收邊的，單欄之下它沒有分隔
         任何東西，卻剛好壓在照片下緣 —— 實測整格 195.8 對照片 194.8，就是那 1px。
       ⚠ 收在 .svc-grid .svc-tile 上（特異度 0,3,0）才蓋得過那條 nth-child 規則；
         寫 .service-section .svc-tile 只有 (0,2,0)，會輸。
       ⚠ 不寫 :last-child：服務筆數變成偶數時，那條 nth-last-child(-n+2) 會改中
         倒數第二格，線就跑到隊伍中間去了。整組收掉才不會隨筆數飄。 */
    .service-section .svc-grid .svc-tile {
        border-bottom: 0;
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   服務專區手機版：卡片底色全部拿掉，只留最上面那塊深灰（2026-08-04 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「服務專區手機版的淺灰色背景移除留最上面的深灰色背景就好。」

   ⚠ 「淺灰色」指的是**服務卡的底色**，不是區塊下半的頁面底 ——
     依客戶 2026-08-03 自己的定義（見上方「服務專區的左邊是深灰色，
     右邊的膚色跟藍色區塊才是淺灰色」）：
         深灰色 ＝ SLATE #7C7D80（手機版就是最上面那條帶）
         淺灰色 ＝ --surface-grey #DCDCE1（4n+1／4n+4 的服務卡）
     區塊下半本來就是頁面底 #F6F6F7，把它「移除」看起來不會有任何變化，
     所以要動的只有卡片。
   ⚠ 白色那兩格（4n+2／4n+3）一起收掉：客戶說的是「留最上面的深灰色就好」，
     只拿掉灰的話會剩下白 → 灰 → 白的半套交錯，比原本更亂。
     整組收成透明，下半才是一片乾淨的頁面底，照片成為唯一的色塊。
   ⚠ 改的是 --tile-fill 這個**共用變數**，不是各自的 background ——
     靜態外觀與 JS 進場動畫（::before 由左往右填）讀的是同一個值，
     只改一邊會出現「動畫版與靜態版不同色」。填色變透明後那段掃色自然看不見，
     這是沒有底色必然的結果，不是壞掉。
   ⚠ 特異度要 (0,3,0) 才蓋得過 .svc-tile:nth-child(4n+1) 那組 (0,2,0)。
   ⚠ 只在窄幅收：桌機的兩欄卡陣仍然靠灰／白交錯分格，一格都沒動。
   ⚠ 文字不受影響：編號與箭頭在 2026-08-03 就已經拿掉方框、改成純文字色，
     黑字壓 #F6F6F7 仍是 15.8:1。 */
@media (max-width: 1023.98px) {
    .service-section .svc-grid .svc-tile {
        --tile-fill: transparent;
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   Hero 手機版：文字與上方圖片之間留距離（2026-08-04 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「hero 手機版一樣保持文字背景不要留空間，但文字上方要與圖片留距離。」

   ⚠ 「文字背景不要留空間」是**保持現狀**，不是要改的東西 ——
     ≤991 的 .cal-text 本來就是 background: transparent、寬度 0→100%，
     底色由 .hero 自己的米白鋪滿整個視窗寬（實測 0→485），
     文字則靠 padding-inline: var(--section-x) 內縮。
     這正是服務專區那一套「底色滿版、文字不貼邊」，兩段語言一致，不必動。
   ⚠ 真正要補的只有上內距：那條規則寫的是 padding: 0 var(--section-x) 2.75rem，
     **上方是 0**，所以標題直接貼在圖片下緣（實測圖片下緣 389／文字頂端 389）。
   ⚠ 用 --section-y 而不是自己挑一個數字：那是窄幅的垂直節奏 token
     （≤991 是 2.5rem、≤575 收到 2rem），圖片與文字的距離因此會跟著斷點一起收斂，
     不會在小螢幕上顯得太鬆。
   ⚠ 只補 padding-top，不動左右與下方：下方的 2.75rem 還要負責托住 CTA、
     翻頁箭頭與進度列，改成簡寫會把三個值一起洗掉。
   ⚠ 特異度 (0,2,0) 蓋過既有的 .cal-text (0,1,0)，而且同樣在 ≤991 之內。
   ⚠ 翻頁時複製出來的 .cal-flip 也在 .hero 之內，兩份會拿到同一個上內距，
     翻頁過程不會出現「舊頁貼邊、新頁有距離」的跳動。 */

/* ═══════════════════════════════════════════════════════════════════════
   服務專區手機版：拿掉那片大淺灰底，讓區塊分出三個明度（2026-08-04 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「服務專區的 AI 分身背景還是有灰色，是該區塊有大淺灰色背景刪除，
          讓該區塊有三個明度的灰。」

   ⚠ 先釐清那片灰是誰：上一輪把服務卡的 --tile-fill 收成透明之後，
     AI 分身看到的灰**不是卡片自己的底**，是**區塊漸層的下半段**
     （.service-section 的 linear-gradient 在 --svc-dark-h 之後接 --background
     ＝ #F6F6F7）。卡片透明 → 直接透出那一片，所以「還是有灰色」。
     實測圖層由上而下：區塊漸層 #F6F6F7 → .wf-frame #FFFFFF → body #F6F6F7。

   ⚠ 為什麼是**改成白**而不是「刪掉 background」：
     刪掉的話透出來的是底下那一層，而在 wireframe 版型那層剛好是 .wf-frame 的
     純白（wireframe.css:19），看起來會對；但正式版型沒有 .wf-frame，
     透出來的會是 body 的 #F6F6F7 —— 灰又回來了，而且是「本機看起來對、
     上線才發現不對」的那種錯。明寫成白，不管底下是誰結果都一樣。

   ⚠ 三個明度因此成立（由深到淺）：
         #7C7D80  SLATE     最上面那條深灰帶（區塊主標／介紹）
         #F6F6F7  淺灰      顧問案的文字面板（.svc-feature 自己的底，未動）
         #FFFFFF  白        服務卡所在的整片底
     這三階是客戶要的層次；只有中間那階留著，深淺才有東西可以比。

   ⚠ 停點 --svc-dark-h 與深色那兩個色標一格都沒動 —— 只換最後一個色標的顏色，
     「灰底只鋪到主打圖片下緣」那條規則（由 home.js 量出停點）維持不變。
   ⚠ 白色寫 hsl(0 0% 100%)，與服務卡白格那條（--tile-fill: hsl(0 0% 100%)）
     同一個寫法；不借用 --brand-foreground，那個 token 的語意是「主色上的文字」。
   ⚠ 只在窄幅：桌機是左黑右灰的兩塊色版型，沒有這條漸層。 */
@media (max-width: 1023.98px) {
    .service-section {
        background: linear-gradient(
            to bottom,
            hsl(var(--surface-dark)) 0,
            hsl(var(--surface-dark)) var(--svc-dark-h, 58%),
            hsl(0 0% 100%)           var(--svc-dark-h, 58%)
        );
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   服務專區手機版：服務卡兩階灰交錯（2026-08-04 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「服務專區手機版的 AI 分身用稍微深色的灰當背景，
          企業內訓用淺灰色當背景。」

   接上一段「三個明度的灰」：那一段把大片淺灰底換成白，是為了把明度讓出來，
   這一段才是三階真正成立的地方 ——
       #7C7D80  --slate               最上面那條深灰帶
       #C4C4CC  --surface-grey-deep   AI 分身（稍深灰）
       #DCDCE1  --surface-grey        企業內訓（淺灰）
   （白＝區塊底，是這三階的畫布，不算一階。）

   ⚠ 用 :nth-child(odd/even) 而不是點名那兩張卡：AI 分身與企業內訓只是
     **展開前看得到的前兩張**。點開「查看更多」之後還有三張，寫死序號的話
     後面那三張會沒有底色；而且後台調整服務排序時，顏色不會跟著跑。
     奇數＝稍深、偶數＝淺，任何筆數都成立。
   ⚠ 兩階同色相同飽和度（225 6%），只差明度 —— 讀起來是「同一種灰的兩階」，
     不是兩個顏色。桌機那組灰／白交錯是另一套（4n 週期，為兩欄設計），一格沒動。
   ⚠ 改的仍然是 --tile-fill 這個共用變數，靜態外觀與進場掃色一起生效；
     特異度 (0,4,0) 蓋過上一輪那條 (0,3,0) 的 transparent。
   ⚠ 對比：標題是黑字，壓 #C4C4CC ＝ 12.0:1、壓 #DCDCE1 ＝ 15.1:1，兩張都過 AA。 */
@media (max-width: 1023.98px) {
    .service-section .svc-grid .svc-tile:nth-child(odd) {
        --tile-fill: hsl(var(--surface-grey-deep));
    }

    .service-section .svc-grid .svc-tile:nth-child(even) {
        --tile-fill: hsl(var(--surface-grey));
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   Hero 手機版：數字橫條底下多留一段（2026-08-04 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「hero 區塊手機版數字橫條底下空間多一點，
          讓此區塊的高變高不要將數字橫條上移。」

   實測現況（500px，座標相對 .hero 頂端）：
       圖片    0 → 333
       文字   333 → 612（自己的 padding-bottom 44px）
       數字橫條 .cal-meta  590 → 603   ← position: absolute
       .hero 高 620，橫條底下只剩 17px

   ⚠ 關鍵：.cal-meta 在窄幅是 **absolute**（grid-area: meta，那一列實測 0px），
     它**不撐高度**。所以「把橫條往下推」與「把區塊變高」是兩件事：
       改 .cal-text 的 padding-bottom → 那是**橫條疊在上面的預留區**，
         橫條的位置由它自己的 margin-top 決定，不會跟著動，
         但區塊會變高 —— 方向對，可是那個值同時還在管文字與 CTA 的下緣，動它會連坐。
       改 .hero 的 padding-bottom → 只有區塊變高，裡面**一個東西都不會位移**。
     所以用後者，正好對上「讓此區塊的高變高、不要將數字橫條上移」。
   ⚠ .hero 的高度是內容撐出來的（base 規則只有 padding: 0／margin: 0／overflow: hidden，
     沒有任何 height 宣告，實測 620px ＝ .cal-stage 的高）。
     height 是 auto，所以 padding-bottom 一定往下長，不會反過來壓縮內容 ——
     若它是寫死高度 ＋ border-box，加 padding 會把內容擠上去，正是客戶不要的那個結果。
   ⚠ 值取 --section-y：與同一支 Hero 上方「文字與圖片的距離」用的是同一個 token，
     上下兩段呼吸因此是同一套節奏（≤991 2.5rem／≤575 2rem）。
     實測 500px 下橫條底下由 17px → 49px。
   ⚠ 只在窄幅：桌機的 .cal-meta 是另一套（抽出流、絕對定位在桌曆右下），不受影響。 */
@media (max-width: 991.98px) {
    .hero {
        padding-bottom: var(--section-y);
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   服務專區手機版：主打與服務卡直接相接（2026-08-04 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「服務專區 AI 分身不要跟顧問案留空間直接貼齊。」

   ⚠ 那道縫是 .svc-main 的 gap：桌機早就是 0（「主打服務與卡牌相接處不再有間距，
     圖片與色塊因此是連續的一整片」），窄幅當年另外補回 2.5rem。
     現在窄幅也要相接，等於回到與桌機同一套 —— 把窄幅那條覆寫收掉即可。
   ⚠ 只收 gap，不動 .svc-main 的 margin-top（那是「介紹文與主打之間」的距離，
     是另一組關係，客戶沒有要求併掉）。
   ⚠ 卡與卡之間本來就沒有縫（.svc-grid 的 row-gap 已是 0），
     所以這一條收掉之後，主打 → 卡 → 卡 是連續的一整條色塊。 */
@media (max-width: 1023.98px) {
    .service-section .svc-main {
        gap: 0;
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   服務專區手機版：企業內訓的淺灰再淺一階（2026-08-04 客戶指定＋色票圖）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「企業內訓的淺灰色背景再淺」，並附了一張近乎白的淺灰色票。

   ⚠ 順著同一條 225 6% 的灰階往上走一階，不另外調色：
         87%    #DCDCE1  --surface-grey        （原本）
         96.5%  #F5F5F7  --background          （改成這一階，對應客戶的色票）
     色相與飽和度仍然不動，AI 分身（78% #C4C4CC）與它依舊是「同一種灰的兩個明度」。
   ⚠ 三階因此拉開成：
         #7C7D80  最上面那條深灰帶
         #C4C4CC  AI 分身（稍深灰）
         #F5F5F7  企業內訓（淺灰）
     底下的畫布是白（區塊漸層下半），所以最淺這一階仍然看得出是一塊卡 ——
     這個 246 對 255 的差，正是客戶先前自己指出「還是有灰色」的那一階，看得見。
   ⚠ 沿用 --background 而不是再開一個 token：這一階站上已經具名，
     多開一個同值 token 會變成兩個真相來源。
   ⚠ 黑字壓 #F5F5F7 ＝ 18.1:1。 */
@media (max-width: 1023.98px) {
    .service-section .svc-grid .svc-tile:nth-child(even) {
        --tile-fill: hsl(var(--background));
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   知識專區手機版：取消堆疊、改成上圖下文、文字收小一階（2026-08-04 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「知識專區在手機版請把堆疊卡牌展示移除改成全部展示，圖片在上文字在下，
          且手機版文字可以稍小一點現在閱讀起來有點吃力。」

   ⚠ 堆疊是**純 CSS**，沒有 JS 參與（home.js 裡沒有任何 kz-stack 的程式碼）——
     .kz-stack-item 的 position: sticky ＋ 每篇差一個列首高的 top。
     所以窄幅只要把 sticky 關掉，三篇就自然全部攤開，不必動標記、不必動 JS。
   ⚠ top 的算式（--header-h + --kz-head-h × --kz-i）與 --kz-i 那六條 nth-child
     **一行都沒刪**：position: static 之下 top 本來就不生效，
     桌機仍然照舊堆疊，要把手機也改回堆疊只要把這一條註解掉。
   ⚠ .kz-stack-item 的不透明底色留著不動：那是堆疊時「蓋住下一篇」用的，
     攤開後它就等於頁面底同色，沒有副作用；留著才不會在改回堆疊時又缺一塊。

   ── 圖片在上、文字在下 ──
   ⚠ 標記裡是「先文字後圖」（.kz-stack-text → .kz-stack-media），
     窄幅收成單欄之後圖就落在文字下面。用 order 翻過來，**不動 DOM 順序**。
   ⚠ 這裡用 order 不會傷到無障礙：圖片那一塊本來就是 aria-hidden="true"、
     裡面的連結是 tabindex="-1"（語意由左邊的標題承擔），
     所以視覺順序與焦點順序不會打架 —— 換成一般的可聚焦內容就不能這樣做。
   ⚠ 列首（01 ── 分類）維持在最上面：客戶要換的是「圖與文」的先後，
     那一列是編號與分類的標籤帶，不在其中；順序因此是 編號列 → 圖 → 文。

   ── 文字收小一階 ──
   ⚠ 實測改前：標題 20px、摘要與內文各 14px／行高 28px（--lh-body 是無單位 2）。
     長內文一段 138 字在 500px 寬要排六行，整段讀起來確實會累。
   ⚠ 只收「要讀的那三段」：標題與兩段文字。
     編號（18px）、分類、標籤那幾顆是標籤性質、字本來就短，收了反而變得難點按。
   ⚠ 行高不必另外處理：--lh-body 是**無單位** 2，字級收小之後行高自己跟著收
     （14/28 → 13/26），比例維持不變，不會出現「字變小但行距沒動」的鬆散感。 */
@media (max-width: 1023.98px) {
    /* ① 取消堆疊 —— 三篇全部攤開 */
    .knowledge-section .kz-stack-item {
        position: static;
    }

    /* ② 圖片翻到文字上面 */
    .knowledge-section .kz-stack-media {
        order: -1;
    }

    /* ③ 要讀的那三段收小一階：標題 20 → 18、摘要與內文 14 → 13
       ⚠ 2026-08-13 客戶把摘要（.kz-stack-desc）從「內文」那一階單獨拉到
         16px（見本檔「內文 → 14px」那一段的說明）之後，這裡窄幅收小一階
         的邏輯不再適用於摘要——它現在跟內文不同階，不該再一起收小成同一個
         13px。拿掉 .kz-stack-desc，只留 .kz-stack-body-text 繼續收小；
         摘要在窄幅維持桌機那個固定的 16px（見下面新增的規則）。 */
    .knowledge-section .kz-stack-name {
        font-size: var(--text-sub);
    }

    .knowledge-section .kz-stack-body-text {
        font-size: .8125rem;
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   知識專區手機版：圓形箭頭移到右邊（2026-08-04 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「知識專區的藍色箭頭放右邊。」

   ⚠ .kz-stack-go 是 .kz-stack-text 的子項，而那一層是
     flex-direction: column ＋ align-items: flex-start（整欄靠左）。
     所以只要對這一顆下 align-self: flex-end 就翻到另一端，
     其他兄弟（標籤、標題、摘要、內文、YouTube／Podcast）維持靠左，
     不必動容器的 align-items（動容器會整欄一起跑掉）。
   ⚠ 只在窄幅：桌機那顆在左下是照客戶 2026-08-03 提供的參考圖排的
     （見「參考圖左下那顆圓形箭頭」），這次的指定是在手機版脈絡下提出的，
     所以不動桌機。要桌機也一起移，把媒體查詢拿掉即可。 */
@media (max-width: 1023.98px) {
    .knowledge-section .kz-stack-go {
        align-self: flex-end;
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   服務專區：沒得展開時的「查看更多」連結（2026-08-04 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「先保留三個服務就好其他刪掉，但查看更多按鈕還是保留。」

   服務只剩三張 → 沒有東西可以折 → home.js 會把展開鈕自己收掉
   （shown >= tiles.length 就 hidden）。所以樣板在「沒得折」時改出一顆
   **連結版**的同款按鈕 .svc-more-link（見 home.php），這裡補它的定位。

   ⚠ 位置與間距完全照抄 .svc-more-toggle：兩顆是同一個角色的兩種型態
     （能展開時是 button、不能展開時是連結），版面不該因為換了型態而位移。
   ⚠ 沒有另外寫外觀：它與展開鈕共用 .btn-pill，膠囊、箭頭徽章、hover
     全部沿用同一套，不會出現「兩顆查看更多長得不一樣」。
   ⚠ 窄幅要跟著吃 order: 4 —— .svc-intro 在 ≤991 是 display: contents，
     它的孩子被攤平成 .svc-layout 的直接子項，沒有 order 就會跑到最前面
     （order 預設 0，會排在區塊主標之前）。 */
.svc-more-link {
    align-self: flex-start;
    margin-top: 2rem;
}

@media (max-width: 1023.98px) {
    .svc-more-link {
        order: 4;
        align-self: flex-start;
        margin-top: 1.75rem;
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   知識專區：最底下「查看更多」上方加一條灰色細線（2026-08-04 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「知識專區最底下查看更多按鈕上方給他一條灰色細線。」

   ⚠ 線色用 --rule-on-light（hsl(0 0% 0% / .10)）——**不是**自己挑一個灰：
     那正是同一區 .kz-stack-head 每一列上緣那條線的顏色（實測兩者都是
     rgba(0,0,0,0.1)），而且 .section-more 的左右緣與那些線完全對齊
     （實測 21 → 464，同一條格線）。用同一個 token，這條線讀起來就是
     「編號列那組線的最後一條」，而不是多冒出來的一條裝飾。
   ⚠ **一定要加作用域**：.section-more 是共用元件，近期課程那顆「查看更多」
     也在用它（見 home.php）。不限縮的話那一區會跟著長出一條線 ——
     檔案裡既有的註解已經先警告過這件事（「它是共用元件，其他區塊日後
     用到時不該被這裡的取捨綁住」）。
   ⚠ 只加 border-top 與 padding-top，**不動 margin-top**：
     那 3rem 是共用元件給的「按鈕與內容的距離」，現在變成「線與內容的距離」，
     剛好是我們要的；動它會連近期課程一起被影響（或是要再寫一條覆寫）。
   ⚠ padding-top 用 clamp：窄幅 24px、桌機 40px。線與按鈕的距離比線與內容的
     距離短一階，讀起來才是「這條線收掉上面的清單、帶出下面這顆鈕」。 */
.knowledge-section .section-more {
    border-top: 1px solid var(--rule-on-light);
    padding-top: clamp(1.5rem, 3vw, 2.5rem);
}

/* ═══════════════════════════════════════════════════════════════════════
   知識專區手機版：線往上收、按鈕與頁尾拉開（2026-08-04 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「知識專區的手機版查看更多按鈕離 footer 太近給下方多空間，
          查看更多上方的線條離箭頭再近一點。」

   實測改前（500×723）：
       最後一篇的圓形箭頭 → 線      92px   ← 太遠
       線 → 查看更多按鈕            25px
       按鈕 → footer                28px   ← 太近

   ── ① 線往上靠近箭頭：92px → 32px ──
   那 92px 是兩段加起來的：最後一篇 .kz-stack-body 的 padding-bottom（實測 43px）
   ＋ .section-more 的 margin-top（48px）。兩段都收。
   ⚠ 那個 padding-bottom 的用途是「讓最後一篇捲得完，不會永遠卡在堆疊底下」
     （見該規則的註解）—— 但**手機版的堆疊已經在上一輪關掉了**
     （.kz-stack-item 改 position: static），它在窄幅已經沒有任何作用，
     所以收成 0 不影響任何行為。桌機仍在堆疊，那條規則原封不動。
   ⚠ margin-top 收成 2rem 而不是更小：線與按鈕之間是 25px，
     兩邊差不多才不會變成「線黏在箭頭上」。

   ── ② 按鈕與頁尾拉開：28px → 56px ──
   ⚠ 改的是 --kz-pad-bottom（知識專區自己的下內距變數），不是再加一層 margin：
     那個變數就是這一段與頁尾之間距離的權威來源（見上方 .knowledge-section），
     用 margin 疊上去會變成兩個地方在管同一段留白。
   ⚠ 值改成 clamp(3.5rem, 7vh, 5rem)：723 高的視窗實測 56px（原 28px），
     900 高約 63px —— 跟著視窗高走，小螢幕不會被壓掉太多內容。 */
@media (max-width: 1023.98px) {
    .knowledge-section .kz-stack-list > .kz-stack-item:last-child .kz-stack-body {
        padding-bottom: 0;
    }

    .knowledge-section .section-more {
        margin-top: 2rem;
    }

    .knowledge-section {
        --kz-pad-bottom: clamp(3.5rem, 7vh, 5rem);
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   實體課程小卡：底部被捲動容器切掉（2026-08-04 客戶回報）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「手機版實體課程的小卡底部被裁切到了，應該是高度不夠的關係。」
   —— 診斷正確，而且不只手機，是**所有寬度都少 0.x px**。

   成因：.course-grid 為了橫捲設了 overflow-x: auto。CSS 規定
        「一軸不是 visible 時，另一軸的 visible 會被計算成 auto」，
        所以它同時也變成**垂直**捲動容器。
        而卡片高度是小數（實測 423.453px），捲動容器的 clientHeight 取整數 423，
        於是最後那 0.453px 不會被畫出來 —— 那一段正好就是 8/03 加上的
        0.5px 下框線（.course-section .course-link 的 border）。
        實測 getBoundingClientRect().height 423.453 vs clientHeight 423，差 0.453。
   ⚠ 手機更明顯：卡片窄、標題折行多，高度的小數部分與框線一起被吃掉，
     看起來就是「卡片底下那條線斷掉／被裁掉」。

   解法：給捲動容器一點下內距，讓內容高度**永遠**小於容器可視高度。
   ⚠ 用 padding-bottom 而不是把 overflow-y 改回 visible —— 改不了：
     只要 overflow-x 不是 visible，overflow-y 的 visible 就一定被計算成 auto。
   ⚠ 2px 是刻意的：1px 在 2x 螢幕上仍可能被小數吃掉（實測小數最大接近 1），
     2px 對任何 DPR 都有餘裕，而視覺上看不出來（卡片下方本來就有區塊留白）。
   ⚠ 不能改成 overflow: clip —— 那會讓橫向捲動整個失效（這一區靠它拖曳瀏覽）。 */
.course-grid {
    padding-bottom: 2px;
}

/* ═══════════════════════════════════════════════════════════════════════
   Footer：手機版齊頭、兩個導覽標題拉開、字色統一（2026-08-04 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「手機版的 footer 服務專區與訂閱電子報齊頭，
          實體課程跟知識專區靠特別近請拉開並與服務專區三個段落間距一樣，
          實體課程與知識專區的字顏色要跟其他的 footer 標題一樣，
          手機版跟桌機版都調整字顏色的部分。」

   ── ① 齊頭（只手機）──────────────────────────────────────────────
   實測 500px：服務專區 起點 41、訂閱電子報 起點 21，差 20px。
   那 20px 來自 .footer-col 的 padding-left: clamp(1.25rem, 2.6vw, 2.75rem)
   （500px 時 2.6vw=13 → 取下限 20px）。那條內距是**桌機多欄並排時的欄距**，
   窄幅只有兩欄堆疊，它只是把左欄整個往右推、與訂閱欄對不齊。
   ⚠ 訂閱欄之所以沒被推：.footer-subscribe 自己把 padding-left 歸零。
     所以這裡不是「訂閱欄偏了」，是導覽欄多了一段欄距。
   ⚠ 只在 ≤991 收，桌機的欄距一格都沒動。

   ── ② 實體課程／知識專區拉開（只手機）────────────────────────────
   兩者現在是**橫排**（.footer-col-list--inline，column-gap 56px），
   兩個四字詞並排在窄幅仍然像同一個詞組。改成**直排**，
   row-gap 取 1.25rem —— 與隔壁「服務專區 → 顧問案 → AI 分身」那一欄的
   .footer-col-list row-gap 完全同一個值（實測 20px），
   所以兩欄的垂直節奏一致（實測行距皆 51px），正是客戶說的「與服務專區三個段落間距一樣」。
   ⚠ 改 flex-direction 而不是拆標記：那兩筆資料在 _footer.php 是同一個 items 陣列，
     動標記等於把「橫排」這個設定從資料層拿掉，桌機也會跟著變。
   ⚠ 桌機維持橫排不動（那是 2026-07-31 客戶指定的版型）。

   ── ③ 字色統一（手機＋桌機都要）──────────────────────────────────
   實測：其他欄標題 rgb(0,0,0)，這兩項卻是 rgb(93,95,101)（連結色）——
   因為它們在標記上是 list 裡的連結，不是 .footer-col-title。
   ⚠ 客戶要的是**顏色**一致，所以只改 color，字級與字重維持原樣
     （那是「與欄標題齊頭」的關鍵，見上方 8255 那段註解：行框一樣高才對得齊，
       動字級會讓它們整體下沉 3.6px）。
   ⚠ hover 仍然要有變化，否則看不出可點：沿用站上連結 hover 的降階寫法。 */
.footer-col-list--inline a {
    color: hsl(var(--heading));
}

@media (hover: hover) {
    .footer-col-list--inline a:hover {
        color: hsl(var(--heading) / .65);
    }
}

@media (max-width: 991.98px) {
    /* ① 導覽欄不再縮排，與訂閱電子報同一條左邊界 */
    .site-footer .footer-col {
        padding-left: 0;
    }

    /* ② 橫排改直排，行距對齊隔壁那一欄 */
    .footer-col-list--inline {
        flex-direction: column;
        row-gap: 1.25rem;
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   Header 字級大小切換（2026-08-04 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「在 header 增加一欄位 字／小 大 特大 切換，手機跟桌機皆新增此功能。」

   實際的放大在 tokens.css（根字級），這裡只管**這一組控制項自己長什麼樣**。

   ⚠ 這一組的字級刻意用 px、不用 rem —— 全站唯一的例外，理由是：
     它是「調整字級的那個控制項」，若它自己也跟著放大，
     使用者切到特大之後想切回小，那顆鈕已經被推到換行或擠出 header 了
     （實測特大時整組會多佔 22px）。控制項必須在三個級距下都停在同一個位置，
     這是可用性問題，不是視覺偏好。
   ⚠ 目前選中的那一顆用**實心反白**而不是只加底線：
     三顆都是短中文字，底線在 13px 下幾乎看不出來（實測）。
   ⚠ .is-active 與 aria-pressed 同時由 JS 維護：前者給 CSS、後者給讀屏，
     兩者一定要同步（見 prefs.js 的 apply()）。
   ⚠ 沒有 hover 就沒有回饋，但觸控裝置的 :hover 會黏住 ——
     所以包在 @media (hover: hover) 裡，與全站其他 hover 同一個做法。 */
.font-size-switch {
    display: inline-flex;
    align-items: center;
    gap: .25rem;
}

.font-size-switch-label {
    margin-right: .125rem;
    font-size: 13px;
    line-height: 1;
    letter-spacing: var(--tracking);
    color: hsl(var(--slate));
}

.font-size-btn {
    padding: .25rem .5rem;
    border: 1px solid hsl(var(--foreground) / .28);
    border-radius: var(--radius-pill);
    background: transparent;
    font-family: inherit;
    font-size: 13px;
    line-height: 1.2;
    letter-spacing: var(--tracking);
    color: hsl(var(--foreground) / .75);
    cursor: pointer;
    transition: background var(--transition), color var(--transition), border-color var(--transition);
}

@media (hover: hover) {
    .font-size-btn:hover {
        border-color: hsl(var(--foreground));
        color: hsl(var(--heading));
    }
}

.font-size-btn:focus-visible {
    outline: 2px solid hsl(var(--foreground));
    outline-offset: 2px;
}

/* 目前選中的那一顆 */
.font-size-btn.is-active {
    border-color: hsl(var(--foreground));
    background: hsl(var(--foreground));
    color: hsl(var(--background));
}

/* ── 小桌機（992–1199）：切換器收窄，避免 header 被撐破 ─────────────
   ⚠ 這是**算出來的**、不是看到的：本機視窗被系統鎖在 988px，
     無法把視窗拉到 ≥992 實測桌機版型（見交付說明）。
     用 canvas 量測導覽六項在三個字級下的文字寬，加上間距、logo 與切換器估算：
         可用寬（992 視窗）      944px
         小 885｜大 925｜特大 966   ← 特大超出約 22px
     所以在這個區間把「字」標籤收掉、按鈕內距收緊（省約 48px），
     三個字級都留有餘裕。1200 以上空間充足，維持完整樣式。
   ⚠ 只收這一組，不動導覽本身的字級與間距 —— 那是全站的節奏，
     為了一顆工具鈕去改導覽會影響每一頁的視覺重心。
   ⚠ 「字」只是視覺標籤（已 aria-hidden），收掉不影響讀屏：
     群組本身有 aria-label="字級大小"。 */
@media (min-width: 992px) and (max-width: 1023.98px) {
    .font-size-switch-label {
        display: none;
    }

    .font-size-btn {
        padding: .25rem .375rem;
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   字級切換第二版：三色、移到漢堡左邊（2026-08-04 客戶回饋）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「小大特大的顏色用膚色 藍色 灰色，手機也要有字體切換按鈕放在漢堡選單左邊。」

   ── ① 三顆各自帶色 ──────────────────────────────────────────────────
   小＝NUDE 膚色／大＝AERIAL 藍／特大＝淺灰，三顆**永遠**是自己的顏色，
   不是只有選中才上色 —— 客戶要的是「用顏色區分三個級距」。
   ⚠ 全部用既有色票（--nude／--accent-blue／--surface-grey），不新調顏色。
     這三顆也正是 2026-08-03 客戶指定「膚色跟藍色請用在按鈕與標籤」的那組。
   ⚠ 三色都是淺色，所以文字一律黑：實測對比 膚色 16.1:1／藍 11.9:1／灰 15.1:1。
   ⚠ 選中狀態因此**不能再用填色**（三顆本來就有底色了），改用深色外框 ＋ 字重加粗。
     只靠加粗不夠：中文在 13px 下 350→500 的差異很弱（實測幾乎看不出來），
     外框才是主要訊號；兩個一起用，色盲使用者也分得出來。
   ⚠ 邊框寬度**兩個狀態都是 2px**（未選中用透明色）——
     只在選中時加粗邊框會讓那顆鈕的尺寸變大、三顆之間互相推擠。

   ── ② 手機：出現在 header 列上、漢堡鈕左邊 ──────────────────────────
   標記已移出 <nav>（見 _header.php），所以手機收合狀態就看得到。
   ⚠ 桌機要排回導覽列尾端：.inner 是 flex，給 order: 1 就排到 nav（order 0）之後。
   ⚠ 手機空間很緊（實測 375 寬的手機：logo 163 ＋ 漢堡 36 ＋ 左右內距 42 ＝ 241，
     只剩約 134px 給這一組）。所以窄幅要：收掉「字」標籤、字級降到 12px、
     內距收到最小、三顆之間只留 2px。實測三顆合計約 118px，塞得下。
   ⚠ 這一組的字級一律用 px（全站唯一例外，初版註解已說明理由）：
     它若跟著放大，使用者切到特大之後就再也按不到「小」把它切回來。 */

/* 三色（桌機、手機共用） */
.font-size-btn {
    border-width: 2px;
    border-color: transparent;
    color: hsl(var(--heading));
}

.font-size-btn--sm { background: hsl(var(--nude)); }
.font-size-btn--lg { background: hsl(var(--accent-blue)); }
.font-size-btn--xl { background: hsl(var(--surface-grey)); }

/* 選中：深色外框 ＋ 加粗（底色維持自己的顏色，見上方說明） */
.font-size-btn.is-active {
    border-color: hsl(var(--foreground));
    background: inherit;                 /* 讓下面三條各自的底色繼續生效 */
    color: hsl(var(--heading));
    font-weight: 500;
}

.font-size-btn--sm.is-active { background: hsl(var(--nude)); }
.font-size-btn--lg.is-active { background: hsl(var(--accent-blue)); }
.font-size-btn--xl.is-active { background: hsl(var(--surface-grey)); }

@media (hover: hover) {
    .font-size-btn:hover {
        border-color: hsl(var(--foreground) / .45);
        color: hsl(var(--heading));
    }

    .font-size-btn.is-active:hover {
        border-color: hsl(var(--foreground));
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   桌機 Header：LOGO 最左｜導覽兩欄靠右｜字級切換最右（2026-08-04 客戶指定，
   2026-08-14 客戶補參考圖修正導覽排法）
   ═══════════════════════════════════════════════════════════════════════
   客戶原指定：「header 字切換大小按鈕放最右邊，logo 放最左邊，其他的置中。」
   客戶隨後補圖修正：「樣式會像該圖片一樣右上是選項按鈕，分成兩個選項一行，
   左邊是logo」——導覽從「絕對定位置中單列」改成「靠右兩欄」，不再置中。

   ⚠ 不再用絕對定位：兩欄 grid 本身寬度會隨內容變動，用 margin-left:auto
     把整組（nav + 字級切換）推到最右側即可，不需要再算置中的位移量。
   ⚠ 導覽抽離文件流之後，header 高度改由 LOGO 與切換器決定 ——
     高度本來就釘死在 --header-h，所以沒有影響（實測 56px 不變）。
   ⚠ 下拉選單不受影響：Bootstrap 的 dropdown 是相對於自己的 .dropdown 定位，
     不是相對於 nav。
   ⚠ 只在 ≥1468（2026-08-13 由 992 下修，見「漢堡選單斷點下修」那一段）：
     窄幅／中間帶的 nav 是覆蓋式抽屜（position:fixed），套這裡的排版規則
     會把整個抽屜打壞。 */
@media (min-width: 1468px) {
    .site-header .inner {
        position: relative;
    }

    /* ── 字級切換器換到獨立一行（2026-08-14 客戶指定：「三個a換行放下一行
       不要放旁邊」）────────────────────────────────────────────────
       客戶指的是 A／A+／A++ 那組字級切換器——原本跟導覽同一列，垂直置中
       之後正好卡在導覽兩排中間偏上的位置，看起來像「貼在導覽旁邊」。
       客戶要的是它自己獨立一行，排在導覽（含新加的兩群組分隔線）下面。
       ⚠ 用 flex-wrap 而不是把 .font-size-switch 拉出來另外絕對定位：
         .inner 本來就是 flex row（wireframe.css 的 display:flex），
         給容器 flex-wrap:wrap、給這個項目 flex-basis:100%，它就會自然
         被擠到下一行、佔滿整列寬度——不用另外算高度或用 position 貼死，
         logo／導覽變高變矮（例如字級變大撐高行高）時這裡都會自動跟著
         重新排列，不會跟內容的實際高度脫鉤。
       ⚠ justify-content:flex-end：獨立成一整行之後，內容本身要靠右對齊
         （跟上面導覽貼右是同一個方向語言），不然三顆按鈕會貼在整行最左邊。
       ⚠ 只在 html:not(.is-compact) 生效：.is-compact 時這顆元素本來就是
         display:none（見「Header 收合成方形選單鈕」那段），這裡的排版
         規則對它沒有實際影響，但比照本區塊其餘規則的慣例一律加上限定，
         以防日後有人把 display:none 拿掉卻沒注意到這裡也要一併檢查。 */
    html:not(.is-compact) .site-header .inner {
        flex-wrap: wrap;
    }

    html:not(.is-compact) .site-header .inner > .font-size-switch {
        flex-basis: 100%;
        justify-content: flex-end;
        margin-left: 0;
        margin-top: .75rem;
    }

    /* ── LOGO 放大、拿掉品牌字（2026-08-14 客戶指定）───────────────────
       客戶：「logo放大只保留logo圖品牌名刪掉。」
       ⚠ 只在頁首、未收合生效——選單收合展開後，抽屜裡自己有一顆獨立的
         .site-nav-logo／.site-nav-logo-text（見「桌機（≥1468px）捲動
         收合」新區塊），標記與樣式都是另一份，不會被這裡影響。
       ⚠ 品牌字用 display:none 藏起來，不刪標記——比照 ≤1467px 既有的
         「只留圖示」做法（本檔 ≈6210-6229，2026-08-13 客戶就指定過），
         這裡只是把同一個決定套用到 ≥1468px 這個新狀態。<a> 本身已經有
         aria-label="一天聽一點"（見 _header.php），文字藏起來後可及名稱
         不受影響。
       ⚠ 28px 是原本 ≥992px 通用的圖示高度（992–1467 抽屜範圍仍讀這個
         值，不受這裡影響），這裡另外複寫成更大的 36px，只在「放大」的
         這個狀態生效。 */
    html:not(.is-compact) .site-header .site-logo-icon {
        height: 36px;
    }

    html:not(.is-compact) .site-header .site-logo-text {
        display: none;
    }

    /* 2026-08-14 客戶「logo下移一點」——.inner 是 align-items:center，
       logo 原本跟導覽／字級切換一起垂直置中；加一點 margin-top 讓它比
       置中位置再低一些，不影響同一列其他元素的位置（margin-top 只推動
       logo 自己，不是整個 flex row 一起往下）。 */
    html:not(.is-compact) .site-header .site-logo {
        margin-top: .375rem;
    }

    /* ⚠ 2026-08-14 補 html:not(.is-compact)：捲動離開頁首後 .site-nav 改走
       「桌機（≥1468px）捲動收合」新區塊那組 fixed 抽屜殼，不能再套這裡的
       靠右排版，否則兩組 position 互相打架。平常（頁面頂端）不受影響。
       ⚠ 2026-08-14 客戶「導覽選項整個位置靠右」：margin-left:auto 已經把
         這個元素推到 .inner 裡「logo 之後所有剩餘空間」的最右側，是它在
         容器裡能到達的最右邊界（再往右會被 .font-size-switch 卡住，兩者
         的相對順序客戶沒有要求改動）。這裡沒有再多加東西——客戶要的是
         這個元素本身的定位，不是塊內文字方向，跟下面新增的外框／背景
         一起看，整塊已經貼齊 .font-size-switch 左側、沒有多餘間距。 */
    html:not(.is-compact) .site-header .inner > .site-nav {
        margin-left: auto;
    }

    /* ⚠ 2026-08-14 客戶「導覽選項整個位置靠右」回報跑版：這裡原本也是
       margin-left:auto，跟上面 .site-nav 的 margin-left:auto 是**兩個
       獨立的 auto margin**——flex 版面裡多個 auto margin 會平分剩餘空間，
       不是「先到先贏」，實測導覽區塊右緣跟字級切換器之間因此空出約 600px
       的縫，導覽整塊看起來像卡在中段，不是貼右。改成固定間距，讓
       .site-nav 的 auto margin 自己吃光「logo 之後」的所有剩餘空間，
       字級切換器只跟在它後面留一個固定小縫，不再分食那筆空間。 */
    .site-header .inner > .font-size-switch {
        order: 1;
        margin-left: 1.5rem;
    }

    /* ── 改用 Grid 排版取代上面的 flex-wrap 版本（2026-08-14 客戶回報兩個
       問題）─────────────────────────────────────────────────────────
       客戶：「aaa左邊不要留空間跟上行文字靠左對齊」——上面那版「字級切換器
       換到獨立一行」用的是 flex-wrap + flex-basis:100% + justify-content:
       flex-end，讓 .font-size-switch 的**外框**撐滿 .inner 整行寬度，
       內部三顆鈕再靠右堆——這樣三顆鈕的左邊就會留一大塊空白（外框比內容
       寬很多），客戶要的是它像上面導覽文字一樣，鈕本身緊貼在跟導覽同一條
       左邊界，不要有多餘空間。
       flex 排版沒辦法讓「同一欄兩個不同寬度的手足」共用同一條左邊界（
       nav 跟 font-size-switch 是平行的 flex item，各自獨立計算寬度與
       位置），改用 Grid：把 .inner 分成兩欄（logo｜右側內容），nav 與
       font-size-switch **共用第二欄**，欄寬由較寬的那個（nav）決定，
       font-size-switch 用 justify-self:start 貼齊那一欄的左邊——因為
       兩者共用同一欄，左邊界自然對齊，不必額外量測或猜間距。
       ⚠ 這裡刻意疊在上面舊的 flex-wrap 規則之後（本檔慣例：只加不改，
         舊規則保留當歷史紀錄，這裡用相同選擇器＋相同特異度、寫在後面
         直接覆寫）。若要復原成 flex 版本，把這一段刪掉即可。
       ⚠ justify-content:flex-start 蓋掉舊規則的 flex-end：.font-size-switch
         本身仍是 inline-flex（管自己的三顆鈕怎麼排），justify-self:start
         讓外框收到內容寬度後，flex-end/flex-start 其實已經沒有可以「對齊」
         的多餘空間，這裡改回 flex-start 只是避免留著 flex-end 讓下一個
         人誤讀成「鈕還是刻意靠右」。
       ⚠ margin-top 改 0：row-gap 已經負責兩列之間的間距，不需要疊加。
       ⚠ 2026-08-14 客戶「aaa下移到第一條水平線底下」——row-gap 從 .75rem
         加大到 2.5rem（40px）。Hero 那條水平線（.hero::after，讀
         --hero-line-y）本來是量 AAA 自己的下緣＋24px 呼吸間距，這輪改成
         量導覽（nav）的下緣＋同樣 24px（見 home.js「Hero 十字線」那段，
         bottomRect 改回固定讀 navRect），也就是說線這次改成緊貼在導覽
         下面，AAA 要整個退到線的下面。這裡的 row-gap 就是「導覽下緣」到
         「AAA 上緣」的實際距離，必須大於線的 24px 呼吸間距，AAA 才會
         落在線的下面，不是壓在線上——40px > 24px，多留 16px 讓 AAA 文字
         跟線之間還有一點呼吸空間，不是貼著線。 */
    html:not(.is-compact) .site-header .inner {
        display: grid;
        grid-template-columns: auto auto;
        justify-content: space-between;
        align-items: center;
        row-gap: 2.5rem;
    }

    html:not(.is-compact) .site-header .inner > .site-logo {
        grid-column: 1;
        grid-row: 1;
    }

    html:not(.is-compact) .site-header .inner > .site-nav {
        grid-column: 2;
        grid-row: 1;
    }

    /* ── 字級切換器上方的橫線（2026-08-14 客戶先指定「aaa上面再劃一條
       橫線」加上，同一天客戶回頭多次強調「這條線刪掉」才確認：要刪的不是
       每個導覽項目自己的底線（那條維持，見上面「每個導覽各自底下有一條
       橫線」），是這一條橫跨整個 AAA 外框寬度、位於導覽與 AAA 之間的
       「群組線」——border-top 直接拿掉。
       ⚠ padding-top 一併拿掉：原本是「線跟 AAA 文字之間的呼吸空間」，
         線沒了、留著就變成疊加在 row-gap 之上的一段沒有理由的多餘空白，
         跟客戶要「刪掉」的訴求（不只是線隱形、是整個效果都不要）一併處理。 */
    html:not(.is-compact) .site-header .inner > .font-size-switch {
        grid-column: 2;
        grid-row: 2;
        justify-content: flex-start;
        margin-left: 0;
        margin-top: 0;
    }

    /* ── 字級切換器（AAA）按鈕改白色／依頁面切換（2026-08-14 客戶指定：
       「搜尋跟aaa也要白色」，隨後再指定「aaa不要方框描邊，選中的選項字
       改黑色就好」／「導覽跟aaa hover變黑字」／「其他頁面...logo轉黑色
       70%」——aaa 沒有被最後這句點名，但同一顆 --header-ink 已經涵蓋
       所有導覽相關文字，這裡沿用同一套，不用另外判斷）─────────────
       .font-size-btn 的基底規則（本檔 ≈9253 行）是深色邊框＋深色文字
       （border/color 都讀 --foreground），是給白底環境設計的，跟這次
       頁首透明疊在照片上的方向不一致。這裡疊一層更窄的覆寫（只在
       ≥1468px 且不是 .is-compact）。
       ⚠ 只在這個窄範圍生效：992–1467 抽屜與 .is-compact 收合後重用的
         桌機抽屜情境，字級切換器背景都是白色（或抽屜的白底），維持原本
         深色文字，不套用這裡——跟搜尋文字化那條是同一個道理，避免抽屜
         裡出現白字看不見的狀況。
       ⚠ 選擇器第一版寫成 `.site-header .font-size-btn`（(0,3,1)），實測
         沒生效——本檔後段（≈10473 行「跟 header 一樣」那組，仿照導覽
         連結字重切換樣式）已經有一條 `.site-header .inner >
         .font-size-switch .font-size-btn`（(0,4,0)）設了 color:
         hsl(var(--heading))，特異度比第一版高，一路贏過去。改成跟那條
         同樣的完整路徑（多加 .inner > .font-size-switch 兩層），才蓋得過。
       ⚠ 客戶「不要方框描邊」：border 直接歸零（不是換顏色），三顆鈕現在
         只靠文字本身分辨，不再是藥丸/按鈕的視覺語言。
       ⚠ 客戶「選中的選項字改黑色就好」／「hover變黑字」：這兩個狀態
         不用 --header-ink，直接寫死 hsl(var(--heading))（全黑）——不管
         首頁還是其他頁，這兩個狀態都是「比平常更黑」的同一個訊號
         （首頁白→黑、其他頁黑70%→黑），寫死同一個全黑在兩種情境都對。 */
    html:not(.is-compact) .site-header .inner > .font-size-switch .font-size-btn {
        border-color: transparent;
        color: var(--header-ink, hsl(var(--brand-foreground) / .85));
        background-image: linear-gradient(hsl(var(--heading)), hsl(var(--heading)));
    }

    html:not(.is-compact) .site-header .inner > .font-size-switch .font-size-btn:hover,
    html:not(.is-compact) .site-header .inner > .font-size-switch .font-size-btn:focus-visible {
        border-color: transparent;
        color: hsl(var(--heading));
    }

    html:not(.is-compact) .site-header .inner > .font-size-switch .font-size-btn:focus-visible {
        outline-color: hsl(var(--heading));
    }

    html:not(.is-compact) .site-header .inner > .font-size-switch .font-size-btn.is-active {
        border-color: transparent;
        color: hsl(var(--heading));
    }
}

/* 手機：header 列上、漢堡左邊 */
@media (max-width: 1467px) {
    .site-header .inner > .font-size-switch {
        display: inline-flex;
        order: 0;                /* DOM 順序即「漢堡左邊」，不必再排 */
        margin-left: auto;       /* 推到右側，與漢堡鈕相鄰 */
        margin-right: .5rem;
        gap: .125rem;
    }

    /* 「字」在窄幅讓出寬度（群組已有 aria-label，讀屏不受影響） */
    .site-header .inner > .font-size-switch .font-size-switch-label {
        display: none;
    }

    /* ⚠ 內距也要用 px：字級寫 px 但內距留 rem 的話，鈕還是會跟著放大
       （實測整組 101 → 121px，三個級距各在不同位置）——
       這一組的重點就是「三段都在同一個地方」，內距漏掉等於白做。 */
    .site-header .inner > .font-size-switch .font-size-btn {
        padding: 5px 6px;
        font-size: 12px;
        line-height: 1;
    }
}

/* 桌機同理：把這一組的內距與間距一起釘成 px（理由同上） */
.font-size-switch { gap: 4px; }
.font-size-switch-label { margin-right: 2px; }
.font-size-btn { padding: 4px 8px; }

@media (min-width: 992px) and (max-width: 1023.98px) {
    .font-size-btn { padding: 4px 6px; }
}

/* ═══════════════════════════════════════════════════════════════════════
   Header 搜尋：只留圖示、不壓灰底（2026-08-04 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「header 的搜尋留 icon 就好，不要壓灰底。」

   ⚠ 「搜尋」兩個字用 CSS 收掉，**標記一個字都沒刪** ——
     它是這顆連結的可及名稱來源之一；不過標記上本來就有 aria-label="搜尋"，
     所以文字收掉之後讀屏仍然唸得出「搜尋」，鍵盤與螢幕閱讀器都不受影響。
     客戶若要把文字加回來，把這一條註解掉即可。
   ⚠ 底色／內距／圓角一起收：那個膠囊底就是客戶說的「灰底」
     （Figma 原稿的 67×29 膠囊）。只拿掉背景不收內距的話，
     圖示會孤零零地浮在一塊空白裡、與其他導覽項對不齊。
   ⚠ 圖示放大一階（10 → 16px）：文字沒了之後，10px 的放大鏡太小、
     也低於觸控目標的下限；16px 與導覽文字的視覺重量才相當。
   ⚠ 手機的覆蓋式選單**不套用**：那裡是一列一列的文字選單，
     只剩一個圖示會變成沒有標籤的一列（既有規則已把它的膠囊底收掉了）。 */
@media (min-width: 992px) {
    .site-header .site-nav .site-search {
        padding: 0;
        border-radius: 0;
        background: transparent;
    }

    .site-header .site-nav .site-search .site-search-label {
        display: none;
    }

    .site-header .site-nav .site-search .site-search-icon {
        width: 16px;
        height: 16px;
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   桌機頁首搜尋改回「文字」（2026-08-14 客戶指定，取代上面 2026-08-04 那條）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「搜尋icon改文字搜尋，icon刪掉。」——反轉上面「只留圖示」的舊決定，
   拿掉膠囊底色，讓搜尋跟其他 5 個選項一樣是純文字連結，共用同一套底線
   hover（見本檔「導覽連結的 hover」那段，≈4536 行起）。
   ⚠ 只在 ≥1468px 且不是 .is-compact 生效：992–1467 的漢堡抽屜、以及捲動
     收合後重用同一顆抽屜的桌機情境，搜尋另外走自己的圖示＋文字＋hover
     樣式（見本檔 ≈10254、≈10773 一帶），不在這次改動範圍內，這裡不動
     那兩處，上面 2026-08-04 那條 `@media(min-width:992px)` 規則也留著
     不刪——它現在只在 992–1467 那段還有效，符合本檔「只加不改」慣例。
   ⚠ background 用 background-color 不用 shorthand：shorthand 會把這裡
     另外設的 background-image（底線）一起重置成 none。
   ⚠ 底線用跟其他導覽項完全相同的 background-image 手法（見 ≈4548-4564），
     這裡另外複寫一份而不是把 `.site-search` 從那組排除清單移掉，是為了
     不影響 992–1467 抽屜（那個排除清單是 `@media(min-width:992px)` 內
     無條件生效的規則，牽動範圍比這次要改的 ≥1468px 大）。 */
@media (min-width: 1468px) {
    /* ⚠ 2026-08-14 客戶「搜尋跟aaa也要白色」，隨後「其他頁面時導覽跟logo
       轉黑色70%」——文字顏色改讀 --header-ink（首頁白、其他頁黑70%），
       跟其他 5 個導覽項現在的顏色機制一致。只在 ≥1468px 且不是
       .is-compact 生效，理由跟上面搜尋文字化那段一樣：抽屜背景是白色，
       不能套白字。
       ⚠ 底線（background-image）維持寫死 hsl(var(--heading))（全黑），
         跟其他 5 個導覽項的既有做法一致（見本檔「導覽連結的 hover」那段
         ≈2917 行）——底線平常是 background-size:0（不可見），只有 hover
         時才展開，展開當下文字本來就同步轉黑，寫死黑色不需要跟著頁面切換。 */
    html:not(.is-compact) .site-header .site-nav > a.site-search {
        height: auto;
        padding: 0;
        margin-right: -.2em;
        border-radius: 0;
        background-color: transparent;
        color: var(--header-ink, hsl(var(--brand-foreground)));
        background-image: linear-gradient(hsl(var(--heading)), hsl(var(--heading)));
        background-repeat: no-repeat;
        background-position: 0 100%;
        background-size: 0 1px;
        transition: color var(--transition),
                    background-color var(--transition),
                    border-color var(--transition),
                    background-size var(--dur-hover) var(--ease-out);
    }

    /* ⚠ 2026-08-14 客戶「導覽跟aaa hover變黑字」——搜尋也是導覽項之一，
       hover 跟其他 5 個項目一樣改成全黑（不再是 --header-ink），不管
       首頁還是其他頁都是同一個「更黑」的終點值，不需要跟著頁面切換。 */
    html:not(.is-compact) .site-header .site-nav > a.site-search:hover,
    html:not(.is-compact) .site-header .site-nav > a.site-search:focus-visible {
        background-color: transparent;
        color: hsl(var(--heading));
        border-color: transparent;
        background-size: calc(100% - .2em) 1px;
    }

    html:not(.is-compact) .site-header .site-nav .site-search-icon {
        display: none;
    }

    /* ⚠ 2026-08-14 客戶「搜尋跟電子報字高沒對齊」——成因：.site-search-label
       這個 <span> 沿用了膠囊底設計留下的 `line-height:1`（見本檔 ≈4781
       行，膠囊固定 29px 高、需要文字緊貼行高才會置中），文字化之後
       「搜尋」變成跟「電子報」同一列的普通導覽字，卻還在用膠囊那組窄
       行高，跟其他項目讀的 --lh-body（約 33.6px）對不上，兩個字看起來
       高度沒對齊。這裡蓋掉行高，改成跟其他導覽項一致。 */
    html:not(.is-compact) .site-header .site-nav .site-search-label {
        display: inline;
        line-height: var(--lh-body);
    }
}

/* ── 手機覆蓋式選單裡 ────────────────────────────────────────────────
   ⚠ 選單是單欄、每一項都是一行，所以這一組也自成一列（見既有的
     .site-header .site-nav > a 那組 margin-bottom）。
   ⚠ 三顆鈕在窄幅要放大到好按：13px 的字配 .25rem 內距只有 26px 高，
     低於觸控目標的 44px 下限。這裡把內距加大到 40px 高（仍是 px，理由同上）。 */
@media (max-width: 1467px) {
    /* ⚠ order: -1 把它排到抽屜**最上面**（DOM 順序不動，只調視覺順序）。
       這不是美感偏好，是可用性：需要放大字級的人，最不該被要求
       先用現在這個他看不清的字級、把整個選單捲到底才找得到這顆鈕。
       桌機維持在導覽列尾端（工具列的慣例位置），兩邊各自合理。
       ⚠ .site-nav 在窄幅是 grid，order 對 grid item 一樣有效。 */
    .site-header .site-nav > .font-size-switch {
        display: flex;
        order: -1;
        margin-bottom: 2.25rem;
    }

    .font-size-btn {
        padding: .5rem .75rem;
        font-size: 14px;
    }

    .font-size-switch-label {
        font-size: 14px;
    }

    /* ── 選單開著時收起右下角的浮動鈕 ──────────────────────────────
       ⚠ 這是既有的問題，不是這次才有的：.wf-float 是 position: fixed
         ＋ z-index 1050，而抽屜（含 header）只有 60 —— 兩顆浮動鈕一直
         蓋在展開的選單上面。之前沒人注意到，是因為它們正好落在
         「搜尋」下方的空白處；這次字級切換器排到那個位置才看得出來
         （實測浮動鈕 t662–852、切換器 t735–773，整個被壓住、點不到）。
       ⚠ 用 display: none 而不是降 z-index：選單開著時它們本來就不該被點到，
         降層級只是讓它們躲到後面，仍會吃到觸控事件。
       ⚠ .has-menu-open 是既有的開關（home.js 掛在 <html> 上），不新增狀態。 */
    .has-menu-open .wf-float {
        display: none;
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   浮動鈕：電子報那一顆的信封動態（2026-08-04）
   ═══════════════════════════════════════════════════════════════════════
   ⚠ 與 YouTube／Podcast 那兩顆同一套 hover 語言（見「圖示動態」那一區）：
     指到時信封的封口往下掀一點點，幅度刻意很小（1px）——
     那一區的既有註解寫得很清楚：這是提示，不是表演。
   ⚠ transform-origin 取上緣：封口是從信封頂邊掀開的。 */
.wf-icon--mail .mail-flap {
    transform-origin: 12px 7px;
    transition: transform var(--dur-hover) var(--ease-out);
}

@media (hover: hover) {
    .wf-float a:hover .wf-icon--mail .mail-flap {
        transform: translateY(1px);
    }
}

@media (prefers-reduced-motion: reduce) {
    .wf-icon--mail .mail-flap { transition: none; }
    .wf-float a:hover .wf-icon--mail .mail-flap { transform: none; }
}

/* ═══════════════════════════════════════════════════════════════════════
   字級切換第三版：Aa ＋ 下拉（2026-08-04 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「你字的大小 Aa 然後下拉選單 小 大 特大。」

   由「字＋三顆並排」改成「一顆 Aa ＋ 下拉三列」。
   ⚠ 好處不只是視覺：並排版在手機佔 103px，收成一顆之後只剩約 40px，
     header 那一列（LOGO ＋ 這顆 ＋ 漢堡）在 375px 的手機上寬鬆很多。
   ⚠ 三顆的顏色（膚色／藍／灰）**保留不動** —— 那是客戶前一輪的指定，
     只是從並排改成選單裡的三列。上一版寫的 .font-size-btn--sm/lg/xl 直接沿用。
   ⚠ 上一版「並排」用的規則（.font-size-switch-label、gap、mobile 12px 等）
     刻意留著不刪：那些選擇器現在對不到東西（「字」的 span 已換成 Aa），
     沒有副作用；要改回並排版只要把標記換回去就完全復原。
   ⚠ 下拉本體用 Bootstrap 既有機制，所以這裡只覆寫外觀，不碰開合行為。 */

/* ── 觸發鈕 Aa ─────────────────────────────────────────────────────── */
.font-size-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 40px;
    padding: 4px 8px;
    border: 1px solid hsl(var(--foreground) / .28);
    border-radius: var(--radius-pill);
    background: transparent;
    font-family: var(--font-en);
    font-size: 14px;          /* ⚠ px：這一組是「調字級的控制項」，不能跟著字級長大 */
    line-height: 1.2;
    letter-spacing: .02em;
    color: hsl(var(--heading));
    cursor: pointer;
    transition: border-color var(--transition), background var(--transition);
}

@media (hover: hover) {
    .font-size-toggle:hover {
        border-color: hsl(var(--foreground));
    }
}

.font-size-toggle:focus-visible {
    outline: 2px solid hsl(var(--foreground));
    outline-offset: 2px;
}

/* 選單開著時，觸發鈕維持按下的樣子（Bootstrap 會加 .show） */
.font-size-toggle[aria-expanded="true"] {
    border-color: hsl(var(--foreground));
    background: hsl(var(--foreground) / .06);
}

/* ── 下拉選單 ──────────────────────────────────────────────────────── */
.font-size-menu {
    min-width: 0;
    padding: 6px;
    border: 1px solid hsl(var(--foreground) / .16);
    border-radius: 0;                 /* 全站 --radius 是 0（直角） */
    background: hsl(var(--brand-foreground));
    box-shadow: 0 6px 24px hsl(var(--foreground) / .12);
}

.font-size-menu li + li {
    margin-top: 4px;
}

/* 三列選項。⚠ 特異度要壓過 Bootstrap 的 .dropdown-item，也要壓過
   上一版那條 .site-header .inner > .font-size-switch .font-size-btn（0,4,0），
   所以這裡用同樣的前綴再加一層 .font-size-menu。 */
.site-header .inner > .font-size-switch .font-size-menu .font-size-btn {
    display: block;
    width: 100%;
    min-width: 88px;
    padding: 8px 14px;
    border: 2px solid transparent;
    border-radius: 0;
    font-family: inherit;
    font-size: 14px;                  /* px，理由同上 */
    line-height: 1.3;
    letter-spacing: var(--tracking);
    text-align: center;
    color: hsl(var(--heading));
    cursor: pointer;
}

/* 各自的顏色（客戶指定：膚色／藍／灰） */
.site-header .inner > .font-size-switch .font-size-menu .font-size-btn--sm { background: hsl(var(--nude)); }
.site-header .inner > .font-size-switch .font-size-menu .font-size-btn--lg { background: hsl(var(--accent-blue)); }
.site-header .inner > .font-size-switch .font-size-menu .font-size-btn--xl { background: hsl(var(--surface-grey)); }

/* 目前選中：深色外框 ＋ 加粗（底色仍是自己的顏色）
   ⚠ Bootstrap 的 .dropdown-item:hover/:focus 會把底色刷成淺灰，
     會蓋掉三色 —— 這裡一併鎖住 background，只讓邊框回饋。 */
.site-header .inner > .font-size-switch .font-size-menu .font-size-btn.is-active {
    border-color: hsl(var(--foreground));
    font-weight: 500;
}

@media (hover: hover) {
    .site-header .inner > .font-size-switch .font-size-menu .font-size-btn:hover {
        border-color: hsl(var(--foreground) / .45);
    }

    .site-header .inner > .font-size-switch .font-size-menu .font-size-btn.is-active:hover {
        border-color: hsl(var(--foreground));
    }
}

.site-header .inner > .font-size-switch .font-size-menu .font-size-btn--sm:hover,
.site-header .inner > .font-size-switch .font-size-menu .font-size-btn--sm:focus { background: hsl(var(--nude)); }
.site-header .inner > .font-size-switch .font-size-menu .font-size-btn--lg:hover,
.site-header .inner > .font-size-switch .font-size-menu .font-size-btn--lg:focus { background: hsl(var(--accent-blue)); }
.site-header .inner > .font-size-switch .font-size-menu .font-size-btn--xl:hover,
.site-header .inner > .font-size-switch .font-size-menu .font-size-btn--xl:focus { background: hsl(var(--surface-grey)); }

/* ── 手機：仍在漢堡鈕左邊，只是變成一顆 ─────────────────────────────
   ⚠ 上一版給 .font-size-switch 的 gap 與 12px 內距是為「三顆並排」設的，
     現在裡面只有一顆觸發鈕，那些值對不到東西也不影響。
   ⚠ 選單靠右對齊（.dropdown-menu-end）：這顆鈕貼在畫面右緣，
     選單往左展開才不會超出視窗。 */
@media (max-width: 1467px) {
    .site-header .inner > .font-size-switch {
        margin-right: .625rem;
    }

    .font-size-toggle {
        min-width: 38px;
        padding: 5px 8px;
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   三處收尾：搜尋 icon 補色、Aa 與三個選項去方框（2026-08-04 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「搜尋 icon 跟其他的顏色一樣現在太淺，Aa 不要方框，小 大 特大也不要方框。」

   ── ① 搜尋 icon 太淺：成因是**字色沒跟著膠囊一起改** ────────────────
   桌機原本的規則是 `.site-nav > a.site-search { color: hsl(var(--background)) }`
   —— 米白 #F9F6F2，那是為了配「黑 70% 的膠囊底」而設的反白字。
   上一輪把膠囊底拿掉之後，那個米白就直接落在白底上，幾乎看不見（實測）。
   圖示是 stroke="currentColor"，所以跟著一起淡掉。
   ⚠ 修的是**顏色**不是線寬：搜尋圖示 viewBox 10／stroke 1（比例 0.1），
     比站上其他圖示 viewBox 24／stroke 1.5（比例 0.0625）還粗 —— 不是太細的問題。
   ⚠ 補成 --heading 全黑，與隔壁導覽項完全同色（那幾項也是 --heading，
     見「導覽文字 ＝ CN p」那一段的註解：2026-07-31 已從 70% 黑改成全黑）。
   ⚠ hover 也要一起補：既有的 hover 規則同樣是給膠囊用的反白字。

   ── ② Aa 與三個選項去方框 ────────────────────────────────────────
   ⚠ 拿掉方框之後，「目前選的是哪一個」就不能再靠邊框表示，改用**字重**。
     三個選項本來就各自有顏色，加上點下去整頁字級立刻改變，回饋是足夠的。
   ⚠ :focus-visible 的外框**保留**：那不是裝飾，是鍵盤使用者唯一的位置指示
     （WCAG 2.4.7）。它只在鍵盤操作時出現，用滑鼠點不會看到，
     所以與客戶說的「不要方框」不衝突。
   ⚠ hover 回饋改用底色微深，不再用邊框。 */

/* ① 搜尋：與其他導覽項同色 */
@media (min-width: 992px) {
    .site-header .site-nav > a.site-search,
    .site-header .site-nav > a.site-search:hover,
    .site-header .site-nav > a.site-search:focus-visible {
        color: hsl(var(--heading));
    }
}

/* ② Aa：去方框 */
.font-size-toggle {
    border: 0;
    padding: 4px 6px;
}

.font-size-toggle[aria-expanded="true"] {
    border: 0;
    background: hsl(var(--foreground) / .08);
}

@media (hover: hover) {
    .font-size-toggle:hover {
        border: 0;
        background: hsl(var(--foreground) / .06);
    }
}

/* ③ 三個選項：去方框，選中改用字重 */
.site-header .inner > .font-size-switch .font-size-menu .font-size-btn {
    border: 0;
}

.site-header .inner > .font-size-switch .font-size-menu .font-size-btn.is-active {
    border: 0;
    font-weight: 700;
}

@media (hover: hover) {
    .site-header .inner > .font-size-switch .font-size-menu .font-size-btn:hover,
    .site-header .inner > .font-size-switch .font-size-menu .font-size-btn.is-active:hover {
        border: 0;
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   1920 以上：最小字級不低於 16px（2026-08-04 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「在 1920 螢幕寬度的時候最小字不要小於 16px。」

   ⚠ 做法是**把低於 16px 的那幾個抬到 1rem**，不是調根字級：
     調根字級會讓 36px 的區塊主標一起長到 44px，整個版面的比例都跑掉；
     客戶要的是「下限」，不是「整體放大」。
   ⚠ 盤點過全站在 1920 會出現的字級，低於 16px 的只有這幾處
     （其餘 .8125rem／.75rem 的規則都在 ≤991 或直式桌曆的 media 裡，1920 不生效）：
         --text-body   .9375rem  15px   內文（全站最大宗）
         --text-deco   .875rem   14px   Service Zone 等英文小標
         --text-meta   .8125rem  13px   次要標示
         .cal-pageno／.course-pageno    13px   Hero 與課程的 01 / 04
         .footer-logo-en               12px   頁尾英文
         .font-size-toggle／選單        14px   字級切換控制項本身
   ⚠ 行高刻意不動：--lh-body 是無單位 2，字級抬高之後行距自己跟著走（15/30 → 16/32），
     比例不變。寫死行高才會出現「字變大但行距沒動」的擁擠感。
   ⚠ 斷點用 min-width: 1920px，看的是**視窗**寬不是螢幕寬 ——
     1920 螢幕上視窗沒有最大化就不會觸發。這是 CSS 能取得的最接近條件。
   ⚠ 未實測：本機視窗鎖在 988px，無法驗證 1920 的實際呈現（見交付說明）。
   ⚠ wireframe 原型頁的 .wf-label／.wf-note（12px 上下）不在此列：
     那是標註用的鷹架，不是網站內容，已切版的頁面本來就把它們隱藏。 */
@media (min-width: 1920px) {
    :root {
        --text-body: 1rem;        /* 15 → 16px */
        --text-deco: 1rem;        /* 14 → 16px */
        --text-meta: 1rem;        /* 13 → 16px */
    }

    .course-pageno {
        font-size: 1rem;          /* 13 → 16px */
    }

    .footer-logo-en {
        font-size: 1rem;          /* 12 → 16px */
    }

    .font-size-toggle,
    .site-header .inner > .font-size-switch .font-size-menu .font-size-btn {
        font-size: 16px;          /* 14 → 16px。⚠ 仍用 px：控制項不跟著字級縮放 */
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   桌機導覽項間距放寬（2026-08-04 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「header 的每個連結的 gap 調大一點，現在太擠。」

   --header-nav-gap 48px → clamp(56px, 4.4vw, 76px)。
   ⚠ 改成 clamp 而不是換一個固定值：導覽現在是**絕對定位置中**（見三段式那一段），
     它的寬度直接決定左右兩側還剩多少空間。固定 76px 在 1200 的螢幕上會讓
     導覽逼近 LOGO 與 Aa；clamp 讓它在寬螢幕鬆、窄桌機自動收回來。
     1200 → 56px｜1440 → 63px｜1920 → 76px（4.4vw 在區間內線性）。
   ⚠ 下限刻意設 56 而不是維持 48：客戶說「太擠」，最窄的桌機也要比原本鬆。
   ⚠ 只動 gap，不動字級與字距 —— 那是全站的節奏，為了間距去改字會牽動每一頁。
   ⚠ 手機不受影響：窄幅的導覽是抽屜，一列一列排，用的是 margin-bottom 不是 gap。
   ⚠ 未實測：本機視窗鎖在 988px，無法驗證 ≥992 的實際呈現（見交付說明）。 */
@media (min-width: 992px) {
    .is-designed .site-header {
        --header-nav-gap: clamp(56px, 4.4vw, 76px);
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   修正：手機版 Hero 高度不夠，數字橫條蓋住文字（2026-08-04 客戶回報）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「手機版 hero 在字轉到特大會高度不夠導致數字橫條蓋在文字上。」

   實測（790 寬）比回報的更嚴重 —— **小字時就已經壞了**：
       .hero（overflow: hidden）   56 → 566
       .cal-text                  591 → 881（小）／591 → 998（特大）
       .cal-meta                  495 → 514
     文字整塊落在 hero 可視範圍之外被切掉，數字橫條則浮在圖片上。
     字級調大只是把 881 推到 998，讓問題更明顯。

   成因有兩層：
     ① .cal-frame 在窄幅是 --cal-h: clamp(470px, 74vh, 620px) —— **固定高度**，
        與內容（圖片 16:11 ＋ 文字塊）無關。790 寬時光圖片就要 543px。
     ② .cal-content 的列是 `auto minmax(0, 1fr)`：文字那一列是 1fr、下限 0，
        所以它**不會**把需要的高度往上報，只會被壓縮然後溢出。
     .cal-meta 是絕對定位在 .cal-frame 底部，frame 沒長高，它就留在圖片上。

   ⚠ 為什麼不能把整條鏈改成 height: auto：
     .cal 與 .cal-sheet 是 position: absolute + inset: 0（翻頁動畫要把數頁疊在一起），
     父層一旦 auto，絕對定位的子層量不到高度，整個舞台會塌成 0。
     這是既有架構，不是可以順手改掉的東西。

   做法分兩步：
     ① 這裡把文字列改成 auto —— 讓 .cal-content 的 scrollHeight 能報出**真正需要**的高度
     ② 由 home.js 量出各頁所需的最大高度，寫進 --cal-h（與站上 --svc-dark-h 同一套分工：
        JS 只量尺寸，外觀與版型仍然全部在 CSS）
   ⚠ 取「各頁最大值」而不是當前頁：四頁的文字長度不同，用當前頁的話
     每次翻頁 Hero 都會抽動一下。
   ⚠ 只在窄幅：桌機是左右分欄、文字在圖片旁邊，高度本來就夠。 */

/* ═══════════════════════════════════════════════════════════════════════
   全站字級改成五個級距（2026-08-04 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「目前字的大小區分不夠大，我想要分五個級距。最小字是裝飾英文字跟
          數字橫條還有 footer 的小字大概 14px，再來是 header footer 16px，
          這兩個為小字的級距，另外三個級距以此類推，以 rem 去寫，
          手機跟桌機都要調整，目前全區的級距不夠明顯。」

   改版前用 1920 的 iframe 實測，站上實際渲染出 **八種**字級：
       38 / 34 / 26 / 24 / 18 / 16 / 14 / 13
   中段 16 與 18 幾乎分不出來，13 與 14 也是 —— 這正是「級距不夠明顯」的來源：
   階數太多、相鄰兩階差距太小，看起來就是一團差不多的字。

   收斂成五階（客戶確認的數值）：
       L5 主標  2.5rem   40px   區塊主標／Hero 主標
       L4 標題  1.75rem  28px   卡片標題／主打服務名
       L3 內文  1.25rem  20px   各區內文／卡片說明
       L2 介面  1rem     16px   header 導覽／footer 連結與欄標題
       L1 最小  .875rem  14px   裝飾英文／數字橫條／footer 小字
   階梯比例 1.14 → 1.25 → 1.40 → 1.43，越往上跳越大 ——
   小字之間保持可讀的密度，標題之間才拉得開層級。

   ⚠ 全部用 rem（客戶指定），所以字級切換（小／大／特大）只要改根字級，
     五個級距會一起等比放大，不必為切換再寫一套。
   ⚠ 做法沿用站上既有的模式：**在容器上重新定義 token**，不逐條改 font-size
     （見 8628 那一段的說明）。底下所有 var(--text-*) 自動跟著走。
   ⚠ 既有的 token 定義一行都沒刪 —— 這裡是在最後重新指向五個級距，
     要退回舊尺度把這一整段註解掉即可。
   ═══════════════════════════════════════════════════════════════════════ */

/* ── 五個級距本身 ─────────────────────────────────────────────────── */
:root {
    --fs-1: .875rem;    /* 14px｜最小 */
    --fs-2: 1rem;       /* 16px｜介面 */
    --fs-3: 1.25rem;    /* 20px｜內文 */
    --fs-4: 1.75rem;    /* 28px｜標題 */
    --fs-5: 2.5rem;     /* 40px｜主標 */
}

/* 手機：整組收一階。⚠ L1 維持 14px 不再往下收 ——
   14 已經是可讀性的下限，再小就是「小字看不清」而不是「層級分明」。 */
@media (max-width: 991.98px) {
    :root {
        --fs-1: .875rem;     /* 14px（不變） */
        --fs-2: .9375rem;    /* 15px */
        --fs-3: 1.0625rem;   /* 17px */
        --fs-4: 1.375rem;    /* 22px */
        --fs-5: 1.75rem;     /* 28px */
    }
}

/* 中桌機（992–1439）：整組再收一階。2026-08-05 客戶指定
   「在 1440 看 header 跟 footer 字 16 調成 14，最小裝飾字 14 改 12，其他以此類推」

   為什麼需要這一階：五級距原本從 992 一路吃到 1920＋都是同一組值，
   但版心是流動的 —— 實測 B 案首頁：
       CSS 視窗 1536 → 版心 1524（字級 14/16/20/28/40）
       CSS 視窗 1440 → 版心 1428（同上，一模一樣）
       CSS 視窗 1153 → 版心 1141（同上，一模一樣）
   版心縮了 25%、字級一個 px 都沒動，字對版面的比例等於放大三成，
   所以「1920 剛好、1440 偏大」不是錯覺，是級距少了中間這一階。

   數值由客戶指定（2026-08-05 第二輪：12/14/18/26/38）。
   相對桌機那組是**遞減式**收斂，不是整組等比：
       L1 14→12 ×0.86   L2 16→14 ×0.88   L3 20→18 ×0.90
       L4 28→26 ×0.93   L5 40→38 ×0.95
   小字收得多、大字收得少 —— 版心變窄時最先「擠」的是介面小字，
   標題則要保住份量，所以越上面收越少。
   四段比例 1.17 / 1.29 / 1.44 / 1.46，比桌機的 1.14 / 1.25 / 1.40 / 1.43 還開一點，
   等於在這個寬度層級對比更明顯 —— 方向與 30c34dd「級距要夠明顯」一致。

   ⚠ 這一階的 L1（12px）比手機的 L1（14px）小 —— 是刻意的。
     手機 14px 是觸控閱讀的下限，桌機 12px 只給裝飾英文／數字橫條這類非閱讀文字。
     若之後覺得 12px 太小，把 --fs-1 改回 .875rem 即可，不影響其他四階。
   ⚠ 斷點取 1440px 而不是站上慣用的 1439.98 —— macOS 是覆蓋式捲軸，不佔版面寬，
     所以 1440 寬的筆電最大化時 viewport 剛好就是 1440。寫 1439.98 會正好漏掉
     客戶指名的那個寬度（實測 innerWidth=1440，不是 1425）。
   ⚠ 一樣只重新定義級距本身，底下 var(--text-*) 全部自動跟著走，
     既有定義一行都沒動；要退回把這一個 @media 區塊註解掉即可。 */
@media (min-width: 992px) and (max-width: 1440px) {
    :root {
        --fs-1: .75rem;      /* 12px｜最小（原 14） */
        --fs-2: .875rem;     /* 14px｜介面 header／footer（原 16） */
        --fs-3: 1.125rem;    /* 18px｜內文（原 20） */
        --fs-4: 1.625rem;    /* 26px｜標題（原 28） */
        --fs-5: 2.375rem;    /* 38px｜主標（原 40） */
    }
}

/* ── 既有 token 改讀級距 ──────────────────────────────────────────── */
:root {
    --text-section: var(--fs-5);   /* 區塊主標（原 clamp 30–38） */
    --text-h1:      var(--fs-5);
    --text-h2:      var(--fs-4);
    --text-lead:    var(--fs-4);
    --text-sub:     var(--fs-4);   /* 卡片標題（原 18） */
    --text-body:    var(--fs-3);   /* 內文（原 15） */
    --text-deco:    var(--fs-1);   /* 裝飾英文 */
    --text-meta:    var(--fs-1);
}

/* ── Header／Footer：介面級距 ─────────────────────────────────────────
   ⚠ 這兩個容器原本各自把 token 收到 13/14/11px（2026-08-03 兩輪指定）。
     客戶這次明確指定它們是 16px 那一階，所以整組改讀 --fs-2。
   ⚠ --text-sub 也給 fs-2：footer 欄標題與手機抽屜的主項用它，
     它們同樣是「介面」不是「內容」。 */
.site-header,
.site-footer {
    --text-body: var(--fs-2);
    --text-sub:  var(--fs-2);
    --text-meta: var(--fs-1);
}

/* Footer 的「小字」回到 L1（客戶點名的那一類：標語、訂閱說明、版權） */
.footer-slogan,
.footer-sub-desc,
.footer-copy,
.footer-logo-en,
.footer-legal a {
    font-size: var(--fs-1);
}

/* ── 數字橫條（客戶點名的最小級距）───────────────────────────────── */
.course-pageno {
    font-size: var(--fs-1);
}

/* ── Hero：主標進 L5、介紹文進 L3、月份與 DAY 是裝飾故 L1 ─────────────
   ⚠ 2026-08-13 客戶「hero 的標題 32 改 36」：Hero 主標單獨拉大到 36px，
     跳出 12/14/26/26/32 這五個級距之外，改用固定值而不是再開一階 --fs-6——
     這是唯一一處要比 L5(32px) 更大的文字，值只有這裡用得到，不必為它
     擴充全站共用的級距表。
   ⚠ 不能直接改 --fs-5：.page-hero-title／.page-title 那些頁面 H1 也讀
     --fs-5，全部會被 Hero 這裡的需求誤連動到 36px（那些頁面沒有要變）。 */

/* ── 內容區的標題群回到 L4（原本 24／26 兩階，收成同一階）───────────── */
.svc-feature-name,
.kz-stack-name,
.kz-stack-name a {
    font-size: var(--fs-4);
}

/* ── 內容區的內文群回到 L3（原本散在 13–16）───────────────────────────
   ⚠ 2026-08-13 客戶把 .kz-stack-desc 單獨拉到固定 16px 之後（見本檔「內文
     → 14px」那段），這裡拿掉它，避免特異度更高的窄幅規則（下面那條
     (0,2,0)）繼續把它蓋回 L3；.kz-stack-body-text 等其餘欄位不受影響。 */
.kz-stack-body-text,
.svc-intro-desc,
.svc-feature-desc,
.svc-tile-sub {
    font-size: var(--fs-3);
}

/* ── 級距收尾：把兩處漏網補進五階（1920 與 390 實測抓到）───────────────
   ⚠ .knowledge-section .kz-stack-desc／body-text 在 ≤991 被今天稍早那條
     「手機版文字稍小一點」收到 13px（特異度 0,2,0，蓋過級距那條 0,1,0）。
     五級距是後來且是全站的指定，這裡把它併回 L3。
     ⚠ 這等於**推翻**當時「稍小一點」的調整 —— 若客戶仍要知識專區的內文
       比其他區小一階，把這一條改成 var(--fs-2) 即可，不必動級距本身。
   ⚠ .cal-cover-mark 是 Hero 封面的品牌小標記（12px），屬裝飾 → L1。
   ⚠ 2026-08-13 同上，拿掉 .kz-stack-desc：它不再跟 body-text 同階，
     窄幅也不該再被這條 (0,2,0) 規則蓋回 L3。 */
@media (max-width: 1023.98px) {
    .knowledge-section .kz-stack-body-text {
        font-size: var(--fs-3);
    }
}


/* ── 字級下拉：三塊顏色直接相疊，不留邊（2026-08-04 客戶指定）─────────
   客戶：「header 字級調整小 大 特大的方框不要留邊直接三個顏色框疊在一起。」
   ⚠ 兩處要一起收，少一個就還是看得到縫：
       .font-size-menu 的 padding: 6px  → 0（選單四周的白邊）
       li + li 的 margin-top: 4px       → 0（三塊之間的縫）
   ⚠ 外框與陰影保留：三塊淺色疊起來若沒有邊界，壓在淺色頁面上會分不出
     哪裡是選單、哪裡是頁面（實測膚色 #F2E3D0 壓頁面底 #F5F5F7 只差一階）。
   ⚠ 選中的字重（700）不受影響 —— 去掉的是間距不是狀態指示。 */
.font-size-menu {
    padding: 0;
    overflow: hidden;        /* 三塊的直角切齊外框，不會露出四角的底 */
}

.font-size-menu li + li {
    margin-top: 0;
}

/* ── 搜尋 icon 的 hover 改成淺灰線條（2026-08-04 客戶指定）─────────────
   客戶：「header 搜尋 icon hover 效果不要深灰色背景，改成線條淺灰色。」

   ⚠ 深灰底是**膠囊時代留下來的**：那時 hover 是「整顆膠囊反相成深底白字」
     （見 4403 那條 background: hsl(var(--foreground))）。
     膠囊在 08-04 已經拿掉，只剩一個圖示，反相就變成圖示後面憑空多一塊深灰方塊。
   ⚠ 改成只讓**線條本身**變淺灰：圖示是 stroke="currentColor"，
     所以改 color 就等於改線條顏色，不需要動 SVG。
   ⚠ 用 --slate（#7C7D80，站上的「深灰」色票）而不是更淡的灰 ——
     平常是全黑，hover 轉成 slate 是明顯但不失色的降階；再淡就看起來像失效。
   ⚠ 背景與邊框一起歸零，否則會殘留膠囊時代的那一塊。
   ⚠ 只在 ≥992：手機的搜尋在覆蓋式選單裡是一列文字，沿用選單自己的 hover。 */
@media (min-width: 992px) {
    .site-header .site-nav > a.site-search:hover,
    .site-header .site-nav > a.site-search:focus-visible {
        background: transparent;
        border-color: transparent;
        color: hsl(var(--slate));
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   四處收尾（2026-08-04 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「服務專區桌機版的左邊文字描述寬窄一點，現在偏寬有點貼到邊了」
        「知識專區在 1920 寬的時候文字內容和右邊圖片垂直置中」
        「字級大小選中改淺色，不要文字加粗」
        （footer 的「（佔位）」已在 _footer.php 移除）

   ── ① 服務專區左欄介紹文收窄 ──────────────────────────────────────
   成因是**五級距那一輪的連帶效應**，不是原本就寬：
       max-width 一直是 20em（字寬倍數），但內文由 15px 升到 L3 的 20px，
       20em 就從 300px 變成 400px。左欄只有 449px 寬，只剩 49px 到欄緣 —— 貼邊。
   ⚠ 改 em 的倍數而不是改成固定 px：em 會跟著字級切換（小／大／特大）一起縮放，
     寫死 px 的話使用者切到特大時這一段又會貼邊。
   16em → 320px（1920 實測），距欄緣 129px，與右欄的呼吸一致。

   ── ② 知識專區 1920 垂直置中 ──────────────────────────────────────
   .kz-stack-body 原本是 align-items: end（文字底部與圖片底部切齊）。
   1920 時圖片很高、文字相對短，靠底對齊會讓文字整塊沉在下面、與圖脫節。
   ⚠ 只在 ≥1920 改成 center，照客戶指定的寬度 ——
     1440 以下圖文高度比例不同，靠底仍然成立。要全桌機一致的話把
     媒體查詢改成 min-width: 992px 即可（一個數字）。

   ── ③ 字級選中：改淺色、不加粗 ────────────────────────────────────
   ⚠ 上一版用 font-weight: 700 當選中指示（因為客戶要求去方框）。
     改成「未選中的兩個降低不透明度」——選中的那個維持原本的飽和顏色，
     對比是靠**其他兩個變淡**做出來的，選中項本身完全不動。
   ⚠ 用 opacity 而不是換色：三塊的顏色是客戶指定的膚色／藍／灰，
     換色等於改掉那個指定；降透明度只是弱化，顏色語意還在。
   ⚠ .55 是實測值：再低會讓文字對比掉到 4.5:1 以下（灰底那塊最先失守）。 */

/* ① */
.svc-intro-desc {
    max-width: 16em;
}

/* ② */
@media (min-width: 1920px) {
    .kz-stack-body {
        align-items: center;
    }
}

/* ③ */
.site-header .inner > .font-size-switch .font-size-menu .font-size-btn {
    opacity: .55;
    transition: opacity var(--transition);
}

.site-header .inner > .font-size-switch .font-size-menu .font-size-btn.is-active {
    opacity: 1;
    font-weight: var(--fw-light);   /* 取消上一版的 700 */
}

@media (hover: hover) {
    .site-header .inner > .font-size-switch .font-size-menu .font-size-btn:hover {
        opacity: 1;
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   2026-08-05 客戶指定的一輪調整
   ═══════════════════════════════════════════════════════════════════════
   本段只做「新增與覆寫」，上面既有的規則一行都沒動 ——
   每一小節都可以單獨註解掉退回改版前的樣子。
   ═══════════════════════════════════════════════════════════════════════ */

/* ── ① 懸浮電子報：就地展開輸入框 ──────────────────────────────────────
   客戶：「懸浮按鈕點擊後直接有輸入電子郵件框，跟 footer 的輸入欄位一樣。」

   ⚠ 這一顆從 <a> 改成 <button>（要開合、不換頁），所以上面
     `.wf-float a` 與 `.is-designed .wf-float a` 那兩組**都吃不到它** ——
     這裡照抄同一組值，讓三顆圖示看起來完全一樣。
     沒有去改原本的選擇器：那兩組還在服務 YouTube／Podcast 兩顆。
   ⚠ 表單本身沿用 .footer-form／.footer-input／.footer-submit，
     不複製樣式（客戶要的就是「跟 footer 一樣」）。 */
.wf-float-mail {
    position: relative;
    display: flex;
}

.wf-float-mail-toggle {
    width: 46px;
    height: 46px;
    padding: 0;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    /* 未切版的 18 頁：與 wireframe.css 的 .wf-float a 同一組值 */
    background: #fff;
    border: 1px solid var(--wf-line);
    color: #333;
    box-shadow: 0 2px 8px rgba(0, 0, 0, .16);
}

.wf-float-mail-toggle:hover { background: #f0f0f0; }

/* 已切版頁面：與 .is-designed .wf-float a 同一組值（藍底深灰圖示） */
.is-designed .wf-float-mail-toggle {
    border: 0;
    background: hsl(var(--accent-blue));
    color: hsl(var(--surface-dark));
    box-shadow: 0 2px 10px hsl(var(--foreground) / .12);
    transition: background var(--transition), color var(--transition);
}

.is-designed .wf-float-mail-toggle svg {
    width: 22px;
    height: 22px;
    display: block;
}

.is-designed .wf-float-mail-toggle:hover,
.is-designed .wf-float-mail-toggle:focus-visible,
.is-designed .wf-float-mail-toggle.is-open {
    background: hsl(211 41% 66%);
    color: hsl(var(--surface-dark));
}

/* 面板開在按鈕左側（懸浮列貼著右緣，往左展開才不會出血）。
   ⚠ [hidden] 要明寫 display:none —— 這裡有其他規則會碰到 display 時，
     瀏覽器預設的 [hidden]{display:none} 特異度只有 (0,1,0)，很容易被蓋掉。 */
.wf-float-panel {
    position: absolute;
    right: calc(100% + .625rem);
    bottom: 0;
    z-index: 1;
    width: min(21rem, calc(100vw - 6rem));
    padding: 1.125rem 1.25rem 1.25rem;
    border-radius: 14px;
    background: #fff;
    box-shadow: 0 6px 24px rgba(0, 0, 0, .16);
    /* .footer-input 的 border 讀這個變數，而它原本定義在 .site-footer 上；
       面板在 footer 之外，補一份同值的，否則那條 border 會是無效值。 */
    --footer-line: hsl(0 0% 0% / .14);
}

.wf-float-panel[hidden] { display: none; }

.is-designed .wf-float-panel { background: hsl(var(--background)); }

/* footer 那一版的上距是排在說明文之後的大留白，面板裡太鬆，收一半 */
.wf-float-panel .footer-form { margin-top: .75rem; }
.wf-float-panel .footer-sub-desc { margin: 0; }

/* 窄幅：左側放不下，改成開在按鈕正上方 */
@media (max-width: 575.98px) {
    .wf-float-panel {
        right: 0;
        bottom: calc(100% + .625rem);
        width: min(18rem, calc(100vw - 2rem));
    }
}

/* ── ② 手機版「實體課程」標題與上方裝飾英文沒對齊 ──────────────────────
   客戶：「手機版實體課程標題跟上方裝飾英文沒對齊。」

   成因：.course-head 在 ≤991.98 被改成 flex-direction: column ＋
         align-items: flex-start（上面那條窄幅規則）。方向一轉成直的，
         **橫向對齊就由 align-items 決定**，而後面那條把整區置中的規則
         只設了 justify-content／text-align —— 在 column 下 justify-content
         管的是垂直軸，所以橫向仍然是靠左。
         於是 <h2> 縮成內容寬（實測 174px）並貼在左邊，兩行字各自在這個
         窄盒裡置中：英文 "Upcoming Courses" 幾乎撐滿、中文「實體課程」只有
         115px，看起來就是上下兩行沒對齊、整塊也沒對齊版面。

   ⚠ 只補 align-items: center 一行 —— 盒子一置中，兩行字的中軸就重合了，
     text-align 那條不必動。 */
@media (max-width: 1023.98px) {
    .course-head { align-items: center; }
}

/* ── ③ 手機漢堡選單：拿掉底部的膚色／藍色裝飾矩形 ──────────────────────
   客戶：「手機漢堡選單開後的底部膚色藍色裝飾矩形刪除，直接全白背景。」

   ⚠ 用 content: none 停用兩個偽元素，而不是回頭刪上面那一段 ——
     那一段連同當初的判斷理由（兩條滿版橫帶、共用 --menu-bar-h 以免露白縫）
     一起留著；要復原把這一小節註解掉即可。
   ⚠ 抽屜本身的背景本來就是純白，拿掉這兩塊就是客戶要的「全白背景」，
     不必再補 background。 */
@media (max-width: 1467px) {
    .site-header .site-nav::before,
    .site-header .site-nav::after {
        content: none;
    }
}

/* ── 字級切換第四版：拿掉色塊與外框，改成 header 導覽的樣子（2026-08-05）──
   客戶：「三個不要背景，就跟 header 的展示方式一樣，也不要選到該項有粗黑框。」

   ⚠ 只覆寫顏色相關的幾個屬性，前面三版的規則一行都沒刪 ——
     版位（padding／gap／px 字級／order／手機收窄）全部沿用，
     所以三顆的位置與大小完全不變，變的只有「長相」。
   ⚠ 「跟 header 一樣」照導覽連結那一套做（見 .site-header .site-nav > a）：
       平常  --fw-light 350 ＋ --tracking 0.2em ＋ 全黑，無底無框
       選中  --fw-medium 700（導覽的 .active 就是靠字重分辨「現在在哪一項」）
       hover 由左往右展開的 1px 底線（同樣用 background-image 畫，不佔偽元素）
     選中改用字重之後，客戶說的「粗黑框」就沒有了，但仍分得出選到哪一個。
   ⚠ **字級維持 px，不跟導覽改用 var(--text-body)** —— 這是這一組從第一版就有的
     鐵則：它要是跟著使用者選的級距一起放大，切到特大之後就再也按不回「小」。
     這是可用性問題，不是視覺偏好，不要為了「跟導覽一致」把它改成 token。
   ⚠ 特異度：既有的手機規則是 .site-header .inner > .font-size-switch
     .font-size-btn（0,4,0），所以這裡用同一組前綴（平手時後寫的贏）；
     .is-active 那條再多一層 class 壓過第二版的 .font-size-btn.is-active。
   ⚠ 第二版的 hover 會把邊框加回來（.font-size-btn:hover{border-color}），
     這裡的選擇器比它高，連 :hover 狀態一起蓋掉，不必再寫一條 :hover 的。 */
.site-header .inner > .font-size-switch .font-size-btn,
.site-header .inner > .font-size-switch .font-size-btn.is-active {
    border-color: transparent;      /* 拿掉「選到該項的粗黑框」 */
    background-color: transparent;  /* 拿掉膚色／藍／灰三色底 */
    border-radius: 0;
    color: hsl(var(--heading));
    letter-spacing: var(--tracking);
}

.site-header .inner > .font-size-switch .font-size-btn {
    font-weight: var(--fw-light);
    /* hover 底線：與導覽連結同一套做法（background-image 畫線，偽元素留給別人用） */
    background-image: linear-gradient(hsl(var(--heading)), hsl(var(--heading)));
    background-repeat: no-repeat;
    background-position: 0 100%;
    background-size: 0 1px;
    transition: color var(--transition),
                font-weight var(--transition),
                background-size var(--dur-hover) var(--ease-out);
}

/* 選中：靠字重，與導覽的 .active 同一個訊號 */
.site-header .inner > .font-size-switch .font-size-btn.is-active {
    font-weight: var(--fw-medium);
}

@media (hover: hover) {
    .site-header .inner > .font-size-switch .font-size-btn:hover {
        background-size: calc(100% - .2em) 1px;
    }
}

@media (prefers-reduced-motion: reduce) {
    .site-header .inner > .font-size-switch .font-size-btn {
        transition: color var(--transition);
    }
}

/* ── 懸浮電子報面板：字級與 header 對齊、輸入框不倒圓角（2026-08-05）──────
   客戶：「懸浮按鈕的訂閱電子報輸入背景不要倒圓角，這區塊的字跟 header 的字一樣，
          不要兩格層級的字，不要太細，就跟 header 的字一樣，大小也是都 14px。」

   改版前這一區有兩個問題：
     ① 兩種字級：說明文 14px、輸入框與按鈕 20px（讀 --text-body，
        在 footer 那個情境是內文級距 L3）。客戶要的是**一個級距**。
     ② 太細：說明文與輸入框是 --fw-light，在 :root 那一層等於 **100**
        （DemiLight 的最細端），比 header 的 350 細了兩階。

   做法沿用站上既有模式：**在容器上重新定義 token**，不逐條改
   （與 .site-header／.site-footer／.on-dark 完全一樣的手法）。
   ⚠ 字級改讀 --fs-2 —— 那正是 header 導覽用的那一階（.site-header 把
     --text-body 指到 --fs-2）。所以是真的「跟 header 一樣」，而不是寫死 14：
     客戶看的 1440 是 14px，1441 以上 header 變 16px 時這裡也跟著變，
     兩者永遠對齊。寫死 14 反而會在寬螢幕上和 header 差一階。
   ⚠ 字重照抄 header 那一組（350／450），並讓三個元素一律用 --fw-light 350 ——
     客戶說「不要兩格層級」，所以連按鈕也不另外加重。
   ⚠ 圓角：輸入框與送出鈕一起收成直角。客戶只點名輸入框，但兩者是同一列的
     一組控制項，只收一邊會看起來像沒做完；全站 --radius 本來就是 0（直角），
     收成直角才是回到站上的語言。面板本身的 14px 圓角**沒動**（那是卡片不是輸入框）。
   ⚠ 只作用在 .wf-float-panel 之內 —— footer 自己的訂閱區客戶沒提，一個字都沒動。 */
.wf-float-panel {
    --fw-light:  350;
    --fw-normal: 450;
}

.wf-float-panel .footer-sub-desc,
.wf-float-panel .footer-input,
.wf-float-panel .footer-submit {
    font-size: var(--fs-2);          /* ＝ header 導覽那一階 */
    font-weight: var(--fw-light);    /* ＝ 350，與 header 同一個重量 */
    letter-spacing: var(--tracking);
    line-height: 1.6;
}

.wf-float-panel .footer-input::placeholder {
    font-size: var(--fs-2);
    font-weight: var(--fw-light);
}

/* 直角（見上方說明） */
.wf-float-panel .footer-input,
.wf-float-panel .footer-submit {
    border-radius: 0;
}

/* ── 懸浮電子報面板：背景改毛玻璃（2026-08-05 客戶指定）──────────────────
   ⚠ 值不是新調的，用站上既有的那一組：白 82% ＋ blur(22px) saturate(1.15)。
     那是手機抽屜當初用的毛玻璃配方（2026-08-04 依客戶指定改回純白時，
     刻意把原值記在 .site-header .site-nav 那段的註解裡）—— 同樣是「浮在內容
     上方、而且**裝著文字**」的面板，沿用它才不會出現兩套毛玻璃。
     ⚠ 不用 header 那組（白 50% ＋ blur 12px）：header 是一條薄橫帶、
       底下透出來的是連續的畫面；這個面板裡有 14px 的說明文與輸入框，
       50% 太透，字壓在照片上會讀不清。
   ⚠ 特異度要蓋過上面的 .is-designed .wf-float-panel（0,2,0），
     所以兩個選擇器一起寫。
   ⚠ @supports 退路不可省：不支援 backdrop-filter 的瀏覽器會拿到
     「半透明但沒有模糊」——底圖原封不動透上來，字直接看不見。
     退回不透明底是「少了效果」，沒有退路是「壞掉」。 */
.wf-float-panel,
.is-designed .wf-float-panel {
    background: hsl(0 0% 100% / .82);
    -webkit-backdrop-filter: blur(22px) saturate(1.15);
    backdrop-filter: blur(22px) saturate(1.15);
}

@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
    .wf-float-panel,
    .is-designed .wf-float-panel {
        background: hsl(var(--background));
    }
}

/* 面板本身也收成直角（2026-08-05 客戶：「阿背景框不要倒圓角」）。
   ⚠ 這樣整組就一致了：面板、輸入框、送出鈕三個都是直角，
     與全站 --radius: 0 的語言相同。上面那條 border-radius: 14px 沒刪，
     要退回把這一條註解掉即可。 */
.wf-float-panel {
    border-radius: 0;
}

/* ── 手機：選單開著時三顆懸浮鈕要看得到、蓋在選單上（2026-08-05 客戶指定）──
   客戶：「懸浮的三顆按鈕在手機版層級大過漢堡選單，讓三顆不要被選單蓋著。」

   ⚠ 這是**推翻 08-04 的那條修正**：當時為了「浮動鈕壓住選單內容、點不到」
     把整組 display: none（見上面 .has-menu-open .wf-float 那一段）。
     客戶這次明確要它們維持可見，以客戶指定為準。
   ⚠ 層級本來就夠：.wf-float 是 z-index 1050，遮罩 55、選單開啟時的 header 60，
     所以**只要不把它藏起來就是蓋在最上面**，不必再加 z-index。
     這裡只把 display 還原，沒有動任何層級 —— 加一個更大的數字只會讓
     疊層的來源變成兩處，之後很難查。
   ⚠ 已知取捨：三顆會落在抽屜右下角（實測 y630–784）。那一段目前是選單的
     空白區、不壓到任何連結（實測命中 0 個可點元素），但日後選單若加東西到
     底部就會互相干擾 —— 屆時把三顆改成橫排移到底部，或改回收起來。 */
@media (max-width: 1467px) {
    .has-menu-open .wf-float {
        display: flex;
    }
}


/* ═══════════════════════════════════════════════════════════════════════
   內頁切版（2026-08-05）— 其餘 18 頁比照首頁
   ═══════════════════════════════════════════════════════════════════════
   客戶：「請將其他頁面樣式一同設計，內文文字字級樣式請以首頁為樣式設定，
          內文文字都請幫我比對首頁的課程內容跟內容。」

   ── 這一段只做「新增」，上面 11717 行既有規則一個字都沒有動 ──────────
   要整組退回原型，把本區塊註解掉、並把 layouts/main.php 的 $wfDesigned
   改回 ($wfActive === 'home') 即可。wireframe.css 也完全沒動，
   它仍然先載入、由這裡在後面覆蓋（本檔開頭寫的「逐頁換皮」流程）。

   ── 字級對照表（首頁實測值，非照 token 語意猜）──────────────────────
   量法：Chrome getComputedStyle，1920 / 1440 / 390 三個寬度各量一次。
   全站字級是 2026-08-04 定案的**五級距**（--fs-1…--fs-5，見本檔 11141）：

       級距      1441+   992–1440   ≤991    角色
       --fs-5     40        38        28     區塊主標／頁面 H1
       --fs-4     28        26        22     卡片標題／小節標題
       --fs-3     20        18        17     ★ 內文
       --fs-2     16        14        15     介面（header／footer／表單控制項）
       --fs-1     14        12        14     裝飾英文／日期／編號

   ★「內文」= 客戶指名要比對的那一級。首頁上的權威樣本有兩個，兩者完全相同：
       .kz-stack-body-text（知識專區內文）    1920 實測 20px / 40px / 350 / .06em / #5D5F65
       .svc-feature-desc  （服務主打介紹）    同上
     首頁「課程內容」那一區（.course-*）的三層文字則是：
       .course-date   14px / 1.7  / 450 / .2em  / --slate            ← 日期＝小標級
       .course-title  28px / 1.7  / 350 / .10em / --heading          ← 卡片標題
       .course-place  14px / 1.85 / 350 / .2em  / --foreground/.55   ← 地點＝小標級

   所以本區塊的內文一律寫成下面這一組，與首頁逐項相同（不寫死 px，
   跟著五級距與字級切換一起縮放）：

       font-size:      var(--text-body);         ── ＝ --fs-3
       line-height:    var(--lh-body);           ── ＝ 2
       font-weight:    var(--fw-light);          ── ＝ 350（見下方 .page 的 token）
       letter-spacing: var(--tracking);          ── ＝ --tracking-body ＝ .06em
       color:          hsl(var(--muted-foreground));

   ⚠ 上面五行**刻意不寫成 CSS 註解**（原本寫成 /＊ … ＊/ 的形式）——
     CSS 的註解**不能巢狀**：內層那個結束標記會把整段大註解提早關掉，
     後面的說明文字就變成一條無效的 CSS 規則，並把緊接著的
     「@media (min-width: 992px) { .wf-frame padding-top }」整條一起吞掉。
     實測就是這樣壞的：麵包屑與頁首整段鑽到 fixed header 底下。
     這種錯誤 php -l 與大括號平衡檢查都驗不出來，只有實際看 CSSOM
     （document.styleSheets 裡找不到那條規則）或肉眼看版面才會發現。

   ⚠ 字重為什麼要在容器上重新定義：:root 的 --fw-light 是 100（DemiLight 最細端），
     首頁每個**淺底**區塊都各自把它調回 350／450（見 .course-section／.svc-main
     ／.site-footer 的同款宣告）。內頁全部是淺底，所以在 .page 上宣告一次，
     底下所有 var(--fw-*) 自動跟上 —— 這是站上既有的做法，不逐條寫 font-weight。

   ── 設計語言（沿用 Editorial 那一版，指定 2／3／7）────────────────────
     圓角   --radius-media / --radius-pill 一律 2px（幾乎直角）
     線條   已整組退場，分段靠色塊：頁面底 #F5F5F7、卡片與面板 --muted（白）
     按鈕   全站唯一的 .btn-pill（細框 + 右側圓點 hover 長成箭頭）
     字距   display .06 / title .10 / body .06 / meta .2
   ═══════════════════════════════════════════════════════════════════════ */

/* ───────────────────────────────────────────────────────────────────────
   0. 版面基礎
   ─────────────────────────────────────────────────────────────────────── */

/* ⚠ ≥992 時 .is-designed .site-header 是 **position: fixed**（見本檔 4514）。
   首頁靠 .hero 自己的 padding-top 讓出 --header-h；沒有 Hero 的 18 頁
   必須由外層讓，否則麵包屑與頁首會整段鑽到導覽底下（實測會被蓋掉 56px）。
   ⚠ 用 :not(:has(.hero)) 而不是加一個新 class：判斷條件只有一個地方，
     日後哪一頁加了 Hero 也不必記得回來改。
   ⚠ ≤991 header 是 wireframe.css 的 sticky（佔流程空間），不能加這段內距，
     所以整條包在 min-width: 992px 裡。 */
@media (min-width: 992px) {
    body.is-designed:not(:has(.hero)) .wf-frame {
        padding-top: var(--header-h);
    }
}

/* 內頁殼。所有 18 頁的最外層區塊都是 <section class="site-section page">，
   容器寬度／左右內距沿用首頁的 .site-section（同一條格線）。 */
.page {
    /* 淺底的字重（理由見本區塊開頭；與 .course-section／.svc-main 同一組值） */
    --fw-light:   350;
    --fw-normal:  450;
    --fw-heading: 350;
}

/* 文章型頁面收窄行長：內文 20px × 42em ≈ 840px，中文一行約 40 字。
   ⚠ 用 rem／em 而不是 px：字級切換到「大／特大」時版面要跟著鬆
     （客戶 2026-08-04 的指定），寫死 px 會讓大字擠在同一個寬度裡。 */
.page-narrow {
    width: 100%;
    max-width: 54rem;
    margin-inline: auto;
}

/* 區塊之間的垂直節奏。首頁用 --section-y（區塊自己的上下留白），
   內頁是同一頁裡的段落，取它的一半再收 —— 用 clamp 讓窄幅自動收斂。 */
.page-block + .page-block {
    margin-top: clamp(3rem, 6vw, 5.5rem);
}

/* 麵包屑：wireframe.css 是 .75rem 灰字，這裡換成站上的小標級（--fs-1 + .2em）。
   ⚠ 位置與格線由本檔 4242 的既有規則負責，這裡只換字。
   ⚠ 2026-08-25 客戶「首頁/實體課程 這行文字往上移到 header 的線底下，
     現在離太遠了」——原值 clamp(1.5rem,3vw,2.5rem) 在 1440 寬解出 40px，
     加上 --header-clearance-extra 的 31px 讓位，文字距離 header 底線
     實測 41px。改讀 --wf-crumb-pad（定義在 body:has(.wf-crumb)，見本檔
     「有麵包屑的頁面：拉頂量…」那節）縮成 12–16px。
   ⚠ 一定要走同一顆變數、不能兩邊各寫一次數字：色帶拉頂量
     （--wf-crumb-total）的組成之一就是這個內距，兩邊對不上色帶就會
     多拉或少拉、頂端露出縫或蓋掉一截。fallback 保留原值，變數萬一沒
     定義到也不會比改版前更差。 */
.is-designed .wf-crumb {
    padding-top: var(--wf-crumb-pad, clamp(1.5rem, 3vw, 2.5rem));
    font-size: var(--text-meta);
    line-height: var(--lh-meta);
    font-weight: var(--fw-light);
    letter-spacing: var(--tracking-meta);
    color: hsl(var(--slate));
}

.is-designed .wf-crumb a {
    color: hsl(var(--slate));
    text-decoration: none;
    transition: color var(--transition);
}

.is-designed .wf-crumb a:hover,
.is-designed .wf-crumb a:focus-visible {
    color: hsl(var(--heading));
}

/* 新式編輯器的白便條紙模組標籤：與 .wf-label／.wf-note 同一類的開發交接標註，
   切版後一併收起來。⚠ 文字仍留在 DOM，拿掉 .is-designed 就整組回來。 */
.is-designed .wf-tag {
    display: none;
}

/* ───────────────────────────────────────────────────────────────────────
   1. 頁首（H1 區）
   ───────────────────────────────────────────────────────────────────────
   結構與首頁的 .section-title 同一套：裝飾英文在**上**、中文主標在下
   （2026-07-31 客戶指定的順序），只是內頁多一行副標。 */
.page-head {
    margin-bottom: clamp(2.5rem, 5vw, 4.5rem);
}

/* 裝飾英文小標 —— 與 .section-title .en 完全同一組值（Julius Sans One／--fs-1／.2em） */
/* ⚠ 2026-08-13 客戶「首頁的標題跟英文字底下的線有留一段距離，請其他的也照首頁的改」——
   內頁這裡的線是長在 .page-head-en 自己 box 內的 ::after（見第 33 節），量出來的
   間距就等於這個 margin-bottom 本身；首頁 .section-title 是線在另一層 sibling
   ::before，間距＝en 的 margin-bottom(18px) + 線的 margin-top(20px) + 線高(1px) ＝ 39px。
   要讓內頁跟首頁視覺一致，直接把這裡的 margin-bottom 拉到 39px（原 1.125rem＝18px
   → 新 2.4375rem＝39px），不動線本身、不動 .page-title。 */
.page-head-en {
    margin: 0 0 2.4375rem;
    font-family: var(--font-eyebrow);
    font-size: var(--text-meta);
    line-height: var(--lh-meta);
    font-weight: var(--fw-normal);
    letter-spacing: var(--tracking-meta);
    color: hsl(var(--slate));
}

/* 頁面唯一 H1 —— 與 .section-title .zh 同一組值（--fs-5／1.5／350／.06em） */
.page-title {
    margin: 0;
    font-size: var(--text-section);
    line-height: var(--lh-section);
    font-weight: var(--fw-heading);
    letter-spacing: var(--tracking-display);
    color: hsl(var(--heading));
    overflow-wrap: break-word;
}

/* 副標＝內文級（客戶指定的那一級） */
.page-lead {
    margin: 1.25rem 0 0;
    max-width: 44em;
    font-size: var(--text-body);
    line-height: var(--lh-body);
    font-weight: var(--fw-light);
    letter-spacing: var(--tracking);
    color: hsl(var(--muted-foreground));
}

/* 置中版頁首（會員登入／註冊／忘記密碼那幾頁） */
.page-head--center {
    text-align: center;
}

.page-head--center .page-lead {
    margin-inline: auto;
}

/* ───────────────────────────────────────────────────────────────────────
   2. 小節標題
   ───────────────────────────────────────────────────────────────────────
   H2＝--fs-4（與卡片標題同一階），H3 再降到內文級但用標題色與 .10em 字距。
   ⚠ 刻意**不再畫兩側細線**（wireframe 的 .wf-h2.rule）——
     客戶已指定「把線條感拿掉，以色塊跟色塊去做區隔」。 */
.page-h2 {
    margin: 0 0 clamp(1.5rem, 3vw, 2.5rem);
    font-size: var(--text-h2);
    line-height: 1.6;
    font-weight: var(--fw-heading);
    letter-spacing: var(--tracking-title);
    color: hsl(var(--heading));
}

.page-h3 {
    margin: 0 0 1rem;
    font-size: var(--text-body);
    line-height: 1.8;
    font-weight: var(--fw-normal);
    letter-spacing: var(--tracking-title);
    color: hsl(var(--heading));
}

/* ───────────────────────────────────────────────────────────────────────
   3. 內文（★ 客戶指名要比對首頁的那一級）
   ───────────────────────────────────────────────────────────────────────
   .page-text  單獨一段文字
   .article    後台文字編輯器輸出的整篇文章（標題／段落／清單／引言／插圖）

   兩者的段落值與首頁 .kz-stack-body-text 逐項相同 —— 見本區塊開頭的對照表。 */
.page-text,
.article {
    font-size: var(--text-body);
    line-height: var(--lh-body);
    font-weight: var(--fw-light);
    letter-spacing: var(--tracking);
    color: hsl(var(--muted-foreground));
}

.page-text {
    margin: 0;
    max-width: 44em;
}

.article {
    max-width: 42em;
}

/* 2026-08-25 客戶「純文字也要 hover，但把藍色改淺灰色」——沿用
   .article a 那批的 hover 概念，但這裡是沒有連結的純段落，沒有可以點的
   地方，用藍色會誤導成連結，改用 --slate（同一顆 SLATE，
   --muted-foreground 是它壓深一階給預設閱讀用，這裡用回沒壓深的原色，
   比預設文字淺一階）。
   ⚠ .page-text 在不同頁面有兩種用法，選擇器要各自對應，不能只寫一種：
     - privacy／member_declaration 等頁：<p class="page-text"> 本身
       就是段落（p.page-text）
     - course_detail：<div class="page-text"><p>...</p></div>，段落是
       子層（.page-text > p）；同一顆 .page-text 有時包了不只一個 <p>
       （客戶「段落間距」那節提過），只認 `>` 直屬子層才不會「hover 到
       第一段、第二段顏色也跟著變」。
   ⚠ 只挑 <p>：標題（h2/h3）、引言（blockquote）維持原本樣式，不在這次
     「純文字」的範圍內；.article a 已經有自己的 hover（藍色），選擇器
     只認 <p>，兩者不會互相干擾。 */
p.page-text,
.page-text > p,
.article > p {
    transition: color var(--transition);
}

p.page-text:hover,
.page-text > p:hover,
.article > p:hover {
    color: hsl(var(--slate));
}

/* 文章型頁面已由 .page-narrow 控制行長，內部不再二次收窄 */
.page-narrow .article,
.page-narrow .page-text {
    max-width: none;
}

.article > * {
    margin: 0;
}

.article > * + * {
    margin-top: 1.5rem;
}

/* 小節標題與上一段之間要拉開，讀起來才分得出節。
   ⚠ 2026-08-13 客戶「內文統一標題底下一條細線」——底下每個 h2 都要有；
     border-bottom 的值（1px solid foreground/.16）照抄課程內頁 .cd-h2
     （line 16873 一帶）的既有值，不是新色票；padding-bottom 是文字到線
     的距離，margin-bottom 是線到下一段的距離，兩段各自獨立不互相打架。
     全站共用元件（不是頁面專屬 class），.article 出現的地方全部一起套用。 */
.article > h2 {
    margin-top: clamp(2.75rem, 5vw, 4rem);
    margin-bottom: 1.25rem;
    padding-bottom: .75rem;
    border-bottom: 1px solid hsl(var(--foreground) / .16);
    font-size: var(--text-h2);
    line-height: 1.6;
    font-weight: var(--fw-heading);
    letter-spacing: var(--tracking-title);
    color: hsl(var(--heading));
}

.article > h3 {
    margin-top: 2.5rem;
    margin-bottom: .75rem;
    font-size: var(--text-body);
    line-height: 1.8;
    font-weight: var(--fw-normal);
    letter-spacing: var(--tracking-title);
    color: hsl(var(--heading));
}

/* 加粗只換字重與顏色，不換字級（--fw-normal 在 .page 上是 450） */
.article strong,
.article b {
    font-weight: var(--fw-normal);
    color: hsl(var(--heading));
}

.article ul,
.article ol {
    padding-left: 1.4em;
}

.article li + li {
    margin-top: .5rem;
}

/* 引言：唯一保留的一條線 —— 它不是「分隔線」而是引言本身的標記，
   所以不在「線條整組退場」的範圍內。用 --rule-on-light 的深一階。 */
.article blockquote {
    padding: .25rem 0 .25rem 1.5rem;
    border-left: 2px solid hsl(var(--foreground) / .18);
    color: hsl(var(--foreground) / .72);
}

.article a:not(.btn-pill):not(.tag-pill) {
    color: hsl(var(--heading));
    text-decoration: underline;
    text-underline-offset: .25em;
    transition: color var(--transition);
}

/* 2026-08-25 客戶「文章內文 hover 都要藍色」——文章本體（後台編輯器輸出，
   見上方 .wf-note「可插入…連結…」）裡的行內連結原本沒有 hover 狀態，
   顏色跟 .pcard-title／.kd-related 那批同一顆 --accent-blue-text
   （原因同那邊：--accent-blue 直接當文字色壓淺底看不清楚）。 */
.article a:not(.btn-pill):not(.tag-pill):hover,
.article a:not(.btn-pill):not(.tag-pill):focus-visible {
    color: hsl(var(--accent-blue-text));
}

.article figure {
    margin: clamp(2rem, 4vw, 3rem) 0;
}

.article img,
.article picture {
    display: block;
    width: 100%;
    height: auto;
    border-radius: var(--radius-media);
}

.article figcaption {
    margin-top: .875rem;
    font-size: var(--text-meta);
    line-height: var(--lh-meta);
    letter-spacing: var(--tracking-meta);
    color: hsl(var(--slate));
}

/* ───────────────────────────────────────────────────────────────────────
   4. 內頁卡片 .pcard —— 課程／知識／電子報／搜尋／我的收藏／相關文章 共用
   ───────────────────────────────────────────────────────────────────────
   一個元件服務六個地方（DRY）。三層文字的角色與值**照抄首頁的課程卡**：
       .pcard-meta   ← .course-date   小標級（--fs-1 / .2em / --slate）
       .pcard-title  ← .course-title  --fs-4 / 1.7 / 350 / .10em / --heading
       .pcard-sub    ← .course-place  小標級（--fs-1 / .2em / --foreground .55）
       .pcard-desc   ← 首頁內文那一級（--fs-3 / 2 / 350 / .06em）

   ⚠ 為什麼不直接沿用 .course-* 的 class：那一組綁在首頁的橫向捲動列
     （.course-grid 是 display:flex + overflow-x:auto + scroll-snap）與深色帶
     配色（基底 .course-title 的顏色是 --surface-dark-foreground＝白）。
     搬到淺底的列表頁會白字白底，得再寫一整組覆寫才救得回來 ——
     那比另立一個乾淨的元件更難維護。值照抄、行為獨立。 */
.pcard-grid {
    display: grid;
    grid-template-columns: repeat(var(--pcard-cols, 3), minmax(0, 1fr));
    gap: clamp(2rem, 3.5vw, 3.25rem) clamp(1.5rem, 2.5vw, 2.5rem);
    margin: 0;
    padding: 0;
    list-style: none;
}

.pcard-grid--2 { --pcard-cols: 2; }
.pcard-grid--4 { --pcard-cols: 4; }

/* 卡片本體：白色色塊（--muted）壓在灰白頁面底上 —— 分段靠色塊不靠框線 */
.pcard {
    position: relative;
    display: flex;
    flex-direction: column;
    height: 100%;
    background: hsl(var(--muted));
    border-radius: var(--radius-media);
    overflow: hidden;
    text-decoration: none;
    transition: background var(--transition);
}

/* 圖：貼齊卡片上緣滿版，內距只給文字（與首頁課程卡同一個決定） */
.pcard-media {
    position: relative;
    aspect-ratio: 16 / 9;
    background: hsl(var(--foreground) / .04);
    overflow: hidden;
}

.pcard-media picture,
.pcard-media img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    transition: transform var(--dur-media) var(--ease-out);
}

/* 4:3 版位（課程列表圖沿用 wireframe 註記的比例） */
.pcard-media--4x3 { aspect-ratio: 4 / 3; }

.pcard-body {
    display: flex;
    flex-direction: column;
    flex: 1;
    padding: clamp(1.25rem, 2vw, 1.5rem);
}

.pcard-tags {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
    margin-bottom: 1rem;
}

.pcard-meta {
    margin: 0;
    font-size: var(--text-meta);
    line-height: var(--lh-meta);
    font-weight: var(--fw-normal);
    letter-spacing: var(--tracking-meta);
    color: hsl(var(--slate));
}

.pcard-title {
    margin: .375rem 0 0;
    font-size: var(--text-sub);
    line-height: var(--lh-sub);
    font-weight: var(--fw-heading);
    letter-spacing: var(--tracking-title);
    color: hsl(var(--heading));
    /* 最多兩行，超出以 … 收尾（與 .course-title 同一個處理） */
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    overflow: hidden;
    transition: color var(--dur-hover) var(--ease-out);
}

/* 標題本身帶連結時（卡片不是整張一條連結的情況，例如知識列表 ——
   卡內的分類／標籤／podcast 各自都是連結，巢狀連結是無效的 HTML）。 */
.pcard-title a {
    color: inherit;
    text-decoration: none;
    transition: opacity var(--transition);
}

.pcard-title a:hover,
.pcard-title a:focus-visible { opacity: .7; }

/* 圖片版位本身是連結時要撐成塊，否則 aspect-ratio 量不到高度 */
a.pcard-media {
    display: block;
}

.pcard-sub {
    margin: .5rem 0 0;
    font-size: var(--text-meta);
    line-height: 1.85;
    font-weight: var(--fw-light);
    letter-spacing: var(--tracking-meta);
    color: hsl(var(--foreground) / .55);
}

.pcard-desc {
    margin: .75rem 0 0;
    font-size: var(--text-body);
    line-height: var(--lh-body);
    font-weight: var(--fw-light);
    letter-spacing: var(--tracking);
    color: hsl(var(--muted-foreground));
}

/* 卡片底部的外部連結（YouTube／Podcast）。與首頁 .kz-links 同一個角色。 */
.pcard-links {
    display: flex;
    flex-wrap: wrap;
    gap: 1.25rem;
    margin-top: 1.25rem;
    padding-top: 1rem;
}

.pcard-links a {
    display: inline-flex;
    align-items: center;
    gap: .4rem;
    font-size: var(--text-meta);
    line-height: 1.6;
    letter-spacing: var(--tracking-meta);
    color: hsl(var(--slate));
    text-decoration: none;
    transition: color var(--transition);
}

.pcard-links a:hover,
.pcard-links a:focus-visible {
    color: hsl(var(--heading));
}

.pcard-links svg {
    width: 1.05em;
    height: 1.05em;
}

/* 前往提示：與首頁課程卡右下角那個「→」同一個做法（.course-link::after）。
   ⚠ 用 margin-top:auto 貼底，卡片等高時整排的箭頭會切齊。
   ⚠ 左邊可以放一句 CTA 文字（例：「課程詳情 / 報名」），沒有文字時
     就只剩右側那個箭頭 —— 同一個元件兩種用法，不必再開一個修飾子。
   ⚠ 位移只給箭頭（::after），不是整列 —— 整列一起推會把左邊的文字也帶著跑。 */
.pcard-go {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    margin-top: auto;
    padding-top: 1.5rem;
    font-size: var(--text-meta);
    line-height: 1.6;
    font-weight: var(--fw-normal);
    letter-spacing: var(--tracking-meta);
    color: hsl(var(--foreground) / .55);
    transition: color var(--transition);
}

.pcard-go::after {
    content: "→";
    flex: 0 0 auto;
    margin-left: auto;
    font-family: var(--font-en);
    font-size: 1.125rem;
    line-height: 1;
    letter-spacing: 0;
    transition: transform var(--dur-hover) var(--ease-out);
}

@media (hover: hover) {
    .pcard:hover .pcard-media img { transform: scale(var(--hover-zoom)); }
    /* 2026-08-25 客戶指定：全站文章卡片（.pcard 共用元件，見上方「橫式卡」
       註解——search／knowledge_list／knowledge_detail／member_center／
       newsletter_list 五頁共用同一份）文字 hover 改藍色；原本是壓暗到
       55% 黑。
       ⚠ 不能直接用 --accent-blue：那顆是淺色 AERIAL，設計上是給「底色」
         用的（文字疊上去要改黑，見全站其餘 --accent-blue 用法），實測
         直接當文字色壓在卡片淺底上只有 1.3:1，等於看不到字。改用
         --accent-blue-text（tokens.css 新增，同色相/彩度、壓深明度，
         見那邊註解），壓純白 8.1:1、壓卡片淺灰底 6.0:1。 */
    .pcard:hover .pcard-title     { color: hsl(var(--accent-blue-text)); }
    .pcard:hover .pcard-desc      { color: hsl(var(--accent-blue-text)); }
    .pcard:hover .pcard-go        { color: hsl(var(--foreground)); }
    .pcard:hover .pcard-go::after { transform: translateX(var(--hover-nudge)); }
}

.pcard:focus-visible {
    outline: 2px solid hsl(var(--ring));
    outline-offset: 3px;
}

.pcard:focus-visible .pcard-media img   { transform: scale(var(--hover-zoom)); }
.pcard:focus-visible .pcard-go::after   { transform: translateX(var(--hover-nudge)); }

@media (prefers-reduced-motion: reduce) {
    .pcard-media img,
    .pcard-title,
    .pcard-go::after {
        transition: color var(--transition);
        transform: none;
    }

    .pcard:hover .pcard-media img,
    .pcard:focus-visible .pcard-media img,
    .pcard:hover .pcard-go::after,
    .pcard:focus-visible .pcard-go::after {
        transform: none;
    }
}

/* ── 橫式卡（搜尋結果）───────────────────────────────────────────────
   同一個元件換排列方向，字級與顏色一個都沒變。 */
.pcard--row {
    flex-direction: row;
    align-items: stretch;
}

.pcard--row .pcard-media {
    flex: 0 0 clamp(160px, 22%, 280px);
    aspect-ratio: 16 / 10;
}

.pcard--row .pcard-title {
    -webkit-line-clamp: 2;
    line-clamp: 2;
}

@media (max-width: 575.98px) {
    .pcard--row {
        flex-direction: column;
    }

    .pcard--row .pcard-media {
        flex: 0 0 auto;
        aspect-ratio: 16 / 9;
    }
}

/* ── 卡片內的分類／標籤 ───────────────────────────────────────────────
   沿用全站唯一的 .tag-pill（細框膠囊），只把尺寸收一階 ——
   卡片寬度只有 300–420px，40px 高的膠囊會佔掉整整一行。
   ⚠ 只在 .pcard 之內收，首頁知識專區的 .kz-tags 完全不受影響。 */
.pcard .tag-pill,
.filter-tags .tag-pill {
    min-height: 2rem;
    padding: 0 .875rem;
    font-size: var(--text-meta);
    letter-spacing: var(--tracking-meta);
}

/* 被選中的標籤／分類：實心反相（這是「目前的篩選條件」，需要一眼看得出來） */
.tag-pill.is-active {
    background: hsl(var(--foreground));
    border-color: hsl(var(--foreground));
    color: hsl(var(--background));
}

.tag-pill.is-active:hover,
.tag-pill.is-active:focus-visible {
    background: hsl(var(--foreground) / .82);
    border-color: transparent;
    color: hsl(var(--background));
}

/* ───────────────────────────────────────────────────────────────────────
   5. 側欄篩選（知識專區列表）
   ─────────────────────────────────────────────────────────────────────── */
.page-with-side {
    display: grid;
    grid-template-columns: minmax(0, 15rem) minmax(0, 1fr);
    gap: clamp(2rem, 4vw, 4.5rem);
    align-items: start;
}

@media (max-width: 1023.98px) {
    .page-with-side {
        grid-template-columns: minmax(0, 1fr);
    }
}

.filter-group + .filter-group {
    margin-top: clamp(2rem, 4vw, 3rem);
}

.filter-title {
    margin: 0 0 1.25rem;
    font-size: var(--text-meta);
    line-height: var(--lh-meta);
    font-weight: var(--fw-normal);
    letter-spacing: var(--tracking-meta);
    color: hsl(var(--slate));
}

.filter-list {
    margin: 0;
    padding: 0;
    list-style: none;
}

.filter-list a {
    display: block;
    padding: .5rem 0;
    font-size: var(--text-body);
    line-height: 1.7;
    font-weight: var(--fw-light);
    letter-spacing: var(--tracking);
    color: hsl(var(--muted-foreground));
    text-decoration: none;
    transition: color var(--transition);
}

.filter-list a:hover,
.filter-list a:focus-visible {
    color: hsl(var(--heading));
}

/* 目前選中的分類：換字重與顏色，不加底色也不加線（分段靠色塊的一致做法） */
.filter-list a.is-active {
    font-weight: var(--fw-normal);
    color: hsl(var(--heading));
}

.filter-tags {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
}

/* 手機版分類：自訂下拉（wireframe 既有的互動，只換外觀） */
.filter-select {
    position: relative;
}

.filter-select-btn {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    width: 100%;
    padding: .875rem 1rem;
    border: 1px solid hsl(var(--foreground) / .14);
    border-radius: 0;
    background: hsl(var(--muted));
    font-size: var(--fs-2);
    line-height: 1.6;
    font-weight: var(--fw-light);
    letter-spacing: var(--tracking);
    color: hsl(var(--heading));
    text-align: left;
    cursor: pointer;
}

.filter-select-btn::after {
    content: "";
    flex: 0 0 auto;
    width: .5rem;
    height: .5rem;
    border-right: 1px solid currentColor;
    border-bottom: 1px solid currentColor;
    transform: translateY(-2px) rotate(45deg);
}

.filter-select-menu {
    position: absolute;
    top: calc(100% + 2px);
    left: 0;
    right: 0;
    z-index: 30;
    margin: 0;
    padding: 0;
    list-style: none;
    max-height: 18rem;
    overflow-y: auto;
    background: hsl(var(--muted));
    box-shadow: 0 12px 28px hsl(var(--foreground) / .14);
}

.filter-select-menu a {
    display: block;
    padding: .75rem 1rem;
    font-size: var(--fs-2);
    line-height: 1.6;
    font-weight: var(--fw-light);
    letter-spacing: var(--tracking);
    color: hsl(var(--muted-foreground));
    text-decoration: none;
}

.filter-select-menu a:hover,
.filter-select-menu a:focus-visible {
    background: hsl(var(--background));
    color: hsl(var(--heading));
}

.filter-select-menu a.is-active {
    font-weight: var(--fw-normal);
    color: hsl(var(--heading));
}

/* 篩選結果標頭 */
.filter-result {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .625rem;
    margin-bottom: clamp(1.5rem, 3vw, 2.25rem);
}

.filter-result-label,
.filter-result-count {
    font-size: var(--text-meta);
    line-height: var(--lh-meta);
    letter-spacing: var(--tracking-meta);
    color: hsl(var(--slate));
}

/* ───────────────────────────────────────────────────────────────────────
   6. 分頁
   ─────────────────────────────────────────────────────────────────────── */
.pager {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: .5rem;
    margin-top: clamp(3rem, 6vw, 4.5rem);
}

.pager-item {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 2.75rem;
    height: 2.75rem;
    padding: 0 .5rem;
    border: 1px solid hsl(var(--foreground) / .18);
    border-radius: var(--radius-pill);
    background: transparent;
    font-family: var(--font-en);
    font-size: var(--text-meta);
    line-height: 1;
    letter-spacing: var(--tracking-meta);
    color: hsl(var(--muted-foreground));
    text-decoration: none;
    cursor: pointer;
    transition: border-color var(--transition), color var(--transition), background var(--transition);
}

.pager-item:hover,
.pager-item:focus-visible {
    border-color: hsl(var(--heading));
    color: hsl(var(--heading));
}

.pager-item.is-current {
    background: hsl(var(--foreground));
    border-color: hsl(var(--foreground));
    color: hsl(var(--background));
}

/* ───────────────────────────────────────────────────────────────────────
   7. 表單（登入／註冊／忘記密碼／會員中心／訂閱）
   ───────────────────────────────────────────────────────────────────────
   ⚠ 控制項走**介面級 --fs-2**（16／14／15px），與 header 導覽、footer 訂閱框
     同一階 —— 那是站上「介面文字」的既有定義（見本檔 11216 的說明）。
     內文級（--fs-3）留給頁面上真正的內文；把 20px 塞進輸入框會讓表單變成
     一整片大字，而且兩欄版型在 1440 就會折行。
   ⚠ 欄位標籤是**小標**（--fs-1 + .2em），與卡片上的日期、編號同一個角色。 */
.form-panel {
    padding: clamp(1.5rem, 3vw, 2.75rem);
    background: hsl(var(--muted));
    border-radius: var(--radius-media);
}

.form-field + .form-field {
    margin-top: 1.375rem;
}

.form-label {
    display: block;
    margin-bottom: .5rem;
    font-size: var(--text-meta);
    line-height: var(--lh-meta);
    font-weight: var(--fw-normal);
    letter-spacing: var(--tracking-meta);
    color: hsl(var(--slate));
}

.form-input {
    width: 100%;
    padding: .875rem 1rem;
    border: 1px solid hsl(var(--foreground) / .14);
    border-radius: 0;
    background: hsl(var(--background));
    font-family: var(--font-body);
    font-size: var(--fs-2);
    line-height: 1.6;
    font-weight: var(--fw-light);
    letter-spacing: var(--tracking);
    color: hsl(var(--heading));
    transition: border-color var(--transition);
}

.form-input::placeholder {
    color: hsl(var(--foreground) / .38);
}

.form-input:focus {
    outline: none;
    border-color: hsl(var(--foreground));
}

.form-input:disabled {
    background: hsl(var(--foreground) / .04);
    color: hsl(var(--foreground) / .45);
}

/* ── 會員表單：手機輸入框太高（2026-08-20）─────────────────────────────
   客戶「手機版會員登入的輸入欄高度窄一點現在太高，註冊跟修改會員資料的
   輸入欄都要改」——桌機／手機共用同一份 padding（.875rem 上下＝14px），
   手機實測（390 寬）量出來單顆輸入框高 52.4px，比行動裝置慣用的觸控
   高度多出一截。先縮到 .625rem（10px）實測 44.4px，客戶追加「再縮矮
   一點 30px」，垂直 padding 再收到 3px（22.4px 行高＋2px 邊框，剩下的
   5.6px 兩邊各分 2.8px，取整數 3px），實測 30.4px，四捨五入貼齊客戶
   講的 30px。水平 padding／字級／邊框都不動。
   ⚠ 這個高度已經很緊繃——14px 字＋1.6 倍行高本身就要 22.4px，扣掉 2px
     邊框只剩 5.6px 給上下 padding，文字幾乎頂著邊框，如果之後還要再縮，
     得先縮字級或行高，單靠 padding 沒有再退的空間了。
   ⚠ 用 .page--member 限定，不是全站 .form-input 通改：這顆 class 同時被
     搜尋（search.php）、電子報訂閱（newsletter_list.php）、忘記密碼
     （member_forgot.php／member_forgot_reset.php）共用，客戶這輪只點名
     登入／註冊／會員中心（修改會員資料）三頁，其餘頁面手機沒有喊太高，
     不擴大範圍動它們。.page--member 原本只有 member_register.php 有，
     這輪在 member_login.php／member_center.php 的最外層 section 補上
     同一個 class（兩頁都還沒有跟這顆 class 衝突的既有規則，member_center
     頁籤下的 .pcard 收藏卡不是 .form-input，不受影響）。 */
@media (max-width: 767.98px) {
    .page--member .form-input {
        padding: 3px 1rem;
    }
}

/* 勾選列：介面級文字，勾選框與文字上緣對齊（多行時才不會歪）
   ⚠ padding-left / margin-bottom 必須明寫歸零：.form-check 也是 Bootstrap 的
     類別名，bootstrap.min.css 有 `.form-check{padding-left:1.5em;margin-bottom:.125rem}`。
     本檔雖然後載且同權重，但「沒宣告的屬性蓋不掉」——不歸零的話勾選框會比上面
     的輸入框往右縮 24px（2026-08-05 於 /wireframe/member/center 實測確認）。 */
.form-check {
    display: flex;
    align-items: flex-start;
    gap: .625rem;
    padding-left: 0;
    margin-top: 1rem;
    margin-bottom: 0;
    font-size: var(--fs-2);
    line-height: 1.7;
    font-weight: var(--fw-light);
    letter-spacing: var(--tracking);
    color: hsl(var(--muted-foreground));
}

.form-check input[type="checkbox"] {
    flex: 0 0 auto;
    width: 1rem;
    height: 1rem;
    margin-top: .35em;
    accent-color: hsl(var(--foreground));
}

.form-check a {
    color: hsl(var(--heading));
    text-decoration: underline;
    text-underline-offset: .2em;
}

.form-actions {
    display: flex;
    flex-wrap: wrap;
    gap: .75rem;
    margin-top: 2rem;
}

/* 表單下方的補充列（忘記密碼／註冊新帳號／已有帳號…）＝介面級 */
.form-foot {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    gap: .75rem;
    margin-top: 1.5rem;
    font-size: var(--fs-2);
    line-height: 1.7;
    letter-spacing: var(--tracking);
    color: hsl(var(--slate));
}

.form-foot a {
    color: hsl(var(--heading));
    text-decoration: none;
    border-bottom: 1px solid hsl(var(--foreground) / .28);
    transition: border-color var(--transition);
}

.form-foot a:hover,
.form-foot a:focus-visible {
    border-color: hsl(var(--foreground));
}

/* ⚠ 2026-08-20 客戶「忘記密碼跟註冊新帳號顏色用淡灰色，底線也是」——文字色
   與底線一起改成 --slate（#7C7D80，站上既有的次要文字灰階，.form-label／
   課程日期同一色），比原本 heading 深色字＋28% 透明黑底線更淡，視覺權重
   更像「次要動作」。只改靜止狀態，滑鼠移入／聚焦時維持原本變深的
   :hover／:focus-visible（上面那條規則不動，還是看得出互動反饋）。
   .form-foot 這顆 class 全站只有登入／註冊／服務聲明／註冊完成四頁的
   會員流程在用（grep 確認過，沒有別的頁面共用），直接改這條規則不會
   波及其他頁面，不需要另外用 .page--member 限定。 */
.form-foot a {
    color: hsl(var(--slate));
    border-bottom-color: hsl(var(--slate));
}

/* 只有一項時置中（註冊兩頁的「已有帳號？登入」）。
   space-between 在單一子元素時會把它推到最左，與原型的 text-center 不符；
   登入頁有兩項（忘記密碼／註冊新帳號），仍走 space-between 不受影響。 */
.form-foot > :only-child {
    margin-inline: auto;
}

/* 條文捲動框（服務聲明）。內文級 + 內部自捲。
   ⚠ 2026-08-19 客戶「滾動鼠標先把服務聲明滾完再滾整個頁面」——
     overscroll-behavior:contain 明確關掉捲動鏈（scroll chaining）：
     滑鼠／觸控板在框內滾動時，捲完框內容才會把剩下的滾動量交給外層
     頁面；框內還沒滾到底之前，外層頁面不會跟著一起動或提早接手。
     這是這個屬性的預設行為邊界，不用另外寫 JS 攔截 wheel 事件。 */
.form-scroll {
    max-height: 22rem;
    overflow-y: auto;
    overscroll-behavior: contain;
    padding-right: 1rem;
}

/* 條文之間要分得出節：.page-text 的 margin 是 0，段落與下一個小標相鄰時
   只剩行距撐開，六段條文會黏成一整片。沿用 .article > h3 既有的節奏值
   （margin-top: 2.5rem），讓捲動框裡的分段與文章頁一致。 */
.form-scroll .page-text + .page-h3 {
    margin-top: 2.5rem;
}

/* ── 按鈕修飾子（本體 .btn-pill 一個字都沒動）────────────────────────
   .btn-pill--block  整行寬，文字靠左、徽章靠右（表單的主要動作）
   .btn-pill--solid  實心反相（一頁裡唯一的主要動作才用）
   .btn-pill--sm     小一號（文章頁尾的返回／收藏／分享那一列） */
.btn-pill--block {
    display: flex;
    width: 100%;
    justify-content: space-between;
}

.btn-pill--solid {
    background: hsl(var(--foreground));
    border-color: hsl(var(--foreground));
    color: hsl(var(--background));
}

.btn-pill--solid::after {
    background: hsl(var(--background));
}

.btn-pill--solid:hover,
.btn-pill--solid:focus-visible {
    background: hsl(var(--foreground) / .84);
    border-color: transparent;
}

.btn-pill--solid:hover::after,
.btn-pill--solid:focus-visible::after {
    color: hsl(var(--foreground));
}

.btn-pill--sm {
    --pill-badge: 22px;
    min-height: 40px;
    gap: 1rem;
    padding: 0 .4375rem 0 1.125rem;
    font-size: var(--fs-2);
}

/* 收藏鈕（_fav_button）：切成已收藏時整顆反相 */
.btn-pill[data-fav="1"] {
    background: hsl(var(--foreground));
    border-color: hsl(var(--foreground));
    color: hsl(var(--background));
}

.btn-pill[data-fav="1"]::after {
    background: hsl(var(--background));
}

/* ───────────────────────────────────────────────────────────────────────
   8. 步驟列（註冊 3 步／忘記密碼 3 步）
   ─────────────────────────────────────────────────────────────────────── */
.stepper {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: .75rem;
    margin-bottom: clamp(2rem, 4vw, 3rem);
}

.stepper-item {
    display: inline-flex;
    align-items: center;
    gap: .625rem;
}

.stepper-no {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2rem;
    height: 2rem;
    border: 1px solid hsl(var(--foreground) / .25);
    border-radius: 50%;
    font-family: var(--font-en);
    font-size: var(--text-meta);
    line-height: 1;
    letter-spacing: 0;
    color: hsl(var(--slate));
}

.stepper-label {
    font-size: var(--fs-2);
    line-height: 1.6;
    font-weight: var(--fw-light);
    letter-spacing: var(--tracking-title);
    color: hsl(var(--slate));
}

.stepper-item.is-current .stepper-no {
    background: hsl(var(--foreground));
    border-color: hsl(var(--foreground));
    color: hsl(var(--background));
}

.stepper-item.is-current .stepper-label {
    font-weight: var(--fw-normal);
    color: hsl(var(--heading));
}

/* 已完成的步驟：只換顏色不加勾（保持克制） */
.stepper-item.is-done .stepper-no {
    border-color: hsl(var(--foreground) / .55);
    color: hsl(var(--heading));
}

.stepper-rule {
    width: clamp(1.5rem, 4vw, 2.5rem);
    height: 1px;
    background: hsl(var(--foreground) / .18);
}

@media (max-width: 575.98px) {
    /* 窄幅只留編號與細線，文字改只顯示目前這一步 —— 三組中文並排會折成三行 */
    .stepper-item:not(.is-current) .stepper-label {
        display: none;
    }
}

/* ───────────────────────────────────────────────────────────────────────
   9. 頁籤（會員中心）
   ───────────────────────────────────────────────────────────────────────
   ⚠ 沿用 Bootstrap 的 .nav-tabs／.nav-link 標記與 JS（data-bs-toggle="tab"），
     這裡只換外觀：拿掉 Bootstrap 的框線頁籤，改成「文字 + 選中底線」。
     換掉標記會連帶要自己寫一套鍵盤行為，沒有必要。 */
.page-tabs.nav-tabs {
    gap: clamp(1.5rem, 3vw, 2.5rem);
    border-bottom: 1px solid hsl(var(--foreground) / .12);
    margin-bottom: clamp(2rem, 4vw, 3.5rem);
}

.page-tabs.nav-tabs .nav-link {
    padding: 0 0 1rem;
    border: 0;
    border-bottom: 2px solid transparent;
    border-radius: 0;
    background: transparent;
    font-size: var(--text-body);
    line-height: 1.6;
    font-weight: var(--fw-light);
    letter-spacing: var(--tracking-title);
    color: hsl(var(--slate));
    transition: color var(--transition), border-color var(--transition);
}

.page-tabs.nav-tabs .nav-link:hover,
.page-tabs.nav-tabs .nav-link:focus-visible {
    border-color: hsl(var(--foreground) / .3);
    color: hsl(var(--heading));
}

.page-tabs.nav-tabs .nav-link.active {
    border-color: hsl(var(--foreground));
    background: transparent;
    font-weight: var(--fw-normal);
    color: hsl(var(--heading));
}

/* ───────────────────────────────────────────────────────────────────────
   10. 資訊面板／資訊表（課程資訊、完成訊息、訂閱提示）
   ─────────────────────────────────────────────────────────────────────── */
.info-panel {
    padding: clamp(1.5rem, 3vw, 2.5rem);
    background: hsl(var(--muted));
    border-radius: var(--radius-media);
}

/* 頁面底上的第二層色塊（訂閱提示這種「不是卡片」的區塊） */
.info-panel--grey {
    background: hsl(var(--surface-grey));
}

.info-table {
    width: 100%;
    border-collapse: collapse;
}

.info-table th {
    width: 9.5rem;
    padding: 1.125rem 1rem 1.125rem 0;
    text-align: left;
    vertical-align: top;
    white-space: nowrap;
    font-size: var(--text-meta);
    line-height: var(--lh-meta);
    font-weight: var(--fw-normal);
    letter-spacing: var(--tracking-meta);
    color: hsl(var(--slate));
}

.info-table td {
    padding: 1.125rem 0;
    vertical-align: top;
    font-size: var(--text-body);
    line-height: var(--lh-body);
    font-weight: var(--fw-light);
    letter-spacing: var(--tracking);
    color: hsl(var(--heading));
}

.info-table tr + tr th,
.info-table tr + tr td {
    border-top: 1px solid hsl(var(--foreground) / .10);
}

/* 表格內的附註（地址、共幾天）＝小標級 */
.info-note {
    display: block;
    margin-top: .375rem;
    font-size: var(--text-meta);
    line-height: 1.8;
    letter-spacing: var(--tracking-meta);
    color: hsl(var(--slate));
}

.info-actions {
    display: flex;
    justify-content: center;
    margin-top: 2rem;
}

@media (max-width: 575.98px) {
    /* 窄幅表格改上下堆疊，9.5rem 的 th 會把 td 擠到只剩一半 */
    .info-table th,
    .info-table td {
        display: block;
        width: auto;
        padding-right: 0;
    }

    .info-table th { padding-bottom: .25rem; }
    .info-table td { padding-top: 0; }

    .info-table tr + tr td { border-top: 0; }
}

/* 完成頁（註冊完成／密碼已重設） */
.done-panel {
    padding: clamp(2.5rem, 5vw, 4rem) clamp(1.5rem, 3vw, 3rem);
    background: hsl(var(--muted));
    border-radius: var(--radius-media);
    text-align: center;
}

.done-mark {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 3.5rem;
    height: 3.5rem;
    border: 1px solid hsl(var(--foreground) / .25);
    border-radius: 50%;
    font-family: var(--font-en);
    font-size: 1.5rem;
    line-height: 1;
    color: hsl(var(--heading));
}

.done-title {
    margin: 1.5rem 0 0;
    font-size: var(--text-h2);
    line-height: 1.6;
    font-weight: var(--fw-heading);
    letter-spacing: var(--tracking-title);
    color: hsl(var(--heading));
}

.done-panel .page-text {
    margin: 1rem auto 0;
    max-width: 30em;
}

.done-panel .form-actions {
    justify-content: center;
}

/* ───────────────────────────────────────────────────────────────────────
   11. 文章頁尾動作列（返回／收藏／分享）
   ─────────────────────────────────────────────────────────────────────── */
.entry-foot {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    margin-top: clamp(2.5rem, 5vw, 4rem);
    padding-top: clamp(1.5rem, 3vw, 2rem);
    border-top: 1px solid hsl(var(--foreground) / .10);
}

.entry-actions {
    display: flex;
    flex-wrap: wrap;
    gap: .75rem;
}

/* 文章標頭的分類／標籤／日期那一列 */
.entry-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .625rem;
    margin-top: 1.5rem;
}

.entry-date {
    font-size: var(--text-meta);
    line-height: var(--lh-meta);
    letter-spacing: var(--tracking-meta);
    color: hsl(var(--slate));
}

/* 影片／Podcast 按鈕列 */
.entry-links {
    display: flex;
    flex-wrap: wrap;
    gap: .75rem;
    margin-top: clamp(2rem, 4vw, 3rem);
}

/* ───────────────────────────────────────────────────────────────────────
   12. 新式編輯器模組（課程內頁的 5 個排版模組）
   ───────────────────────────────────────────────────────────────────────
   ⚠ 標記上的 .wf-tag 模組名已由 .is-designed 收起來（見第 0 節），
     但文字仍留在 DOM —— 這一區在後台是可拖拉的版型模組，
     開發交接時把 .is-designed 拿掉就看得到每一塊是哪個模組。 */
.editor-block + .editor-block {
    margin-top: clamp(2.5rem, 5vw, 4rem);
}

.editor-pair {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: clamp(1.5rem, 3vw, 2.5rem);
    align-items: center;
}

/* 左文右圖：只換視覺順序，DOM 維持「圖在前」不動 */
.editor-pair--reverse > :first-child {
    order: 2;
}

.editor-figure {
    margin: 0;
}

.editor-figure picture,
.editor-figure img {
    display: block;
    width: 100%;
    height: auto;
    border-radius: var(--radius-media);
}

@media (max-width: 767.98px) {
    .editor-pair {
        grid-template-columns: minmax(0, 1fr);
    }

    .editor-pair--reverse > :first-child {
        order: 0;
    }
}

/* ───────────────────────────────────────────────────────────────────────
   13. 圖片佔位（還沒有素材的版位）
   ───────────────────────────────────────────────────────────────────────
   wireframe.css 的 .wf-ph 是「淺灰底 + 細白 X 對角線」的原型記號。
   切版後換成乾淨的色塊 + 小標級說明字，X 對角線關掉 ——
   那兩條線是原型階段用來說「這裡以後會有圖」的，設計稿上不該出現。
   ⚠ 規則寫在 .is-designed 之下，拿掉那個 class 就整組回到原型記號。 */
.is-designed .wf-ph {
    background: hsl(var(--foreground) / .05);
    border-radius: var(--radius-media);
}

.is-designed .wf-ph::before,
.is-designed .wf-ph::after {
    content: none;
}

.is-designed .wf-ph > span {
    background: transparent;
    font-size: var(--text-meta);
    line-height: var(--lh-meta);
    letter-spacing: var(--tracking-meta);
    color: hsl(var(--foreground) / .35);
}

/* ───────────────────────────────────────────────────────────────────────
   14. wireframe.css 殘留中和
   ───────────────────────────────────────────────────────────────────────
   18 頁的標記已改成上面這一套 class，但下面幾個原型 class 仍散在少數
   共用位置（_stub、footer 的社群註記等）。這裡把它們一併帶進設計語言，
   避免任何一頁漏改就跳出一塊灰階原型。
   ⚠ 全部掛 .is-designed，原型樣式本身一行都沒有刪。 */
.is-designed .wf-h1 {
    font-size: var(--text-section);
    line-height: var(--lh-section);
    font-weight: var(--fw-heading);
    letter-spacing: var(--tracking-display);
    color: hsl(var(--heading));
}

.is-designed .wf-h2 {
    font-size: var(--text-h2);
    line-height: 1.6;
    font-weight: var(--fw-heading);
    letter-spacing: var(--tracking-title);
    color: hsl(var(--heading));
}

/* 兩側細線（.wf-h2.rule）：客戶已指定「把線條感拿掉」 */
.is-designed .wf-h2.rule::before,
.is-designed .wf-h2.rule::after {
    display: none;
}

.is-designed .wf-dummy {
    font-size: var(--text-body);
    line-height: var(--lh-body);
    font-weight: var(--fw-light);
    letter-spacing: var(--tracking);
    color: hsl(var(--muted-foreground));
}

.is-designed .wf-btn {
    border-color: hsl(var(--foreground) / .5);
    border-radius: var(--radius-pill);
    background: transparent;
    color: hsl(var(--foreground));
    font-size: var(--fs-2);
    letter-spacing: var(--tracking-title);
    padding: .7rem 1.5rem;
}

.is-designed .wf-btn:hover {
    background: transparent;
    border-color: hsl(var(--foreground));
}

.is-designed .wf-btn-solid {
    background: hsl(var(--foreground));
    border-color: hsl(var(--foreground));
    color: hsl(var(--background));
}

.is-designed .wf-input {
    border: 1px solid hsl(var(--foreground) / .14);
    border-radius: 0;
    background: hsl(var(--background));
    font-size: var(--fs-2);
    font-weight: var(--fw-light);
    letter-spacing: var(--tracking);
    color: hsl(var(--heading));
    padding: .875rem 1rem;
}

.is-designed .wf-pill {
    border: 1px solid hsl(var(--foreground) / .5);
    border-radius: var(--radius-pill);
    background: transparent;
    color: hsl(var(--foreground));
    font-size: var(--text-meta);
    letter-spacing: var(--tracking-meta);
    padding: .3rem .875rem;
}

.is-designed .wf-pill-dark {
    background: hsl(var(--foreground));
    border-color: hsl(var(--foreground));
    color: hsl(var(--background));
}

/* ───────────────────────────────────────────────────────────────────────
   15. RWD 收斂
   ───────────────────────────────────────────────────────────────────────
   ⚠ 字級不在這裡動 —— 五級距（--fs-1…5）在 992／1440 兩個斷點已經
     整組收過一次，這裡只調欄數與間距，字才不會有兩套來源（SSOT）。 */
@media (max-width: 1023.98px) {
    .pcard-grid { --pcard-cols: 2; }
    .pcard-grid--4 { --pcard-cols: 3; }
}

@media (max-width: 767.98px) {
    .pcard-grid,
    .pcard-grid--2,
    .pcard-grid--4 { --pcard-cols: 1; }

    /* ⚠ 2026-08-20 客戶「手機版忘記密碼跟註冊新帳號那條線太長」——沒寫
       align-items 時預設是 stretch，column 方向下 stretch 管的是寬度，
       兩顆 <a>（member_login.php 這頁 .form-foot 直接掛兩個 <a>，不是
       像其他三頁包一層 <span>）被撐成整排寬，底線（border-bottom）跟著
       畫滿整排，比文字本身長一大截。改 flex-start 讓每個項目退回文字
       自己的寬度，底線跟著變短貼齊文字。register／declaration／完成
       三頁的 .form-foot 只有一個 <span> 包住文字＋連結，連結本身不是
       flex item、寬度本來就只有文字寬，這條改動對那三頁沒有視覺影響。 */
    .form-foot { flex-direction: column; align-items: flex-start; }

    .entry-foot {
        flex-direction: column;
        align-items: stretch;
    }

    .entry-actions { justify-content: space-between; }
}

/* ───────────────────────────────────────────────────────────────────────
   16. 追加（2026-08-05 · 知識內容頁）
   ───────────────────────────────────────────────────────────────────────
   ⚠ 只在本區塊**尾端追加**，上面一行既有規則都沒有動（契約規定）。 */

/* 膠囊按鈕裡的「圖示 + 文字」（影片／Podcast 那兩顆）。
   ⚠ .btn-pill 本身是 inline-flex 且 gap: 1.75rem —— 那個 gap 的角色是
     「文字 → 右側徽章」的距離。圖示如果直接放進按鈕，它會變成另一個
     flex item，被同一個 gap 推開 28px，看起來像兩顆分開的東西。
     包一層 span 之後，gap 只作用在「標籤 ↔ 徽章」之間，圖示與文字自己貼近。
   ⚠ 圖示尺寸用 em 跟著字級走（與 .pcard-links svg 同一個做法）：
     字級切換到「大／特大」時圖示會一起放大，不會愈變愈小。 */
.btn-pill-label {
    display: inline-flex;
    align-items: center;
    gap: .5rem;
}

.btn-pill-label .wf-icon {
    width: 1.1em;
    height: 1.1em;
    flex: 0 0 auto;
}

/* 相關文章的手機輪播左右鈕（#relCarousel）。
   ⚠ Bootstrap 預設的 .carousel-control-* 是「佔 15% 寬 + 白色箭頭 icon」，
     那是壓在深色大圖上的設定；相關文章是淺灰底上的白卡，白箭頭等於看不見。
     原型階段用 inline style（width:2.2rem 與 filter:invert(1)）硬轉方向，
     切版後 inline style 一律不留（FE-03），整組搬到這裡。
   ⚠ 箭頭字符與首頁的 .cal-arrow 相同（←／→）：同一個「上一則／下一則」
     的角色就該是同一套語言，而且是純文字，CSP 開啟後也不依賴背景圖 data URI。 */
.rel-carousel .carousel-control-prev,
.rel-carousel .carousel-control-next {
    width: 2.2rem;
    opacity: 1;
    color: hsl(var(--foreground));
    transition: opacity var(--transition);
}

.rel-carousel .carousel-control-prev:hover,
.rel-carousel .carousel-control-next:hover {
    opacity: .6;
}

.rel-carousel .carousel-control-prev-icon,
.rel-carousel .carousel-control-next-icon {
    width: auto;
    height: auto;
    background-image: none;
    font-family: var(--font-en);
    font-size: 1.25rem;
    line-height: 1;
    letter-spacing: 0;
    color: inherit;
}

.rel-carousel .carousel-control-prev-icon::before { content: "\2190"; }
.rel-carousel .carousel-control-next-icon::before { content: "\2192"; }

/* ───────────────────────────────────────────────────────────────────────
   17. 搜尋結果清單 .result-list（2026-08-05 追加）
   ───────────────────────────────────────────────────────────────────────
   搜尋頁的結果是**單欄**的橫式卡（.pcard--row），一列一筆、由上往下讀。
   為什麼不用 .pcard-grid：那一組最少是兩欄（預設 --pcard-cols:3、--2 是 2，
   ≤767 才收成單欄），橫式卡塞進兩欄後縮圖只剩版面寬的 22% × 半欄 ≈ 120px，
   標題跟著折成三行 —— 版型不對，不是間距微調救得回來的。
   ⚠ 這是契約允許的「現成 class 蓋不到」的情況：只新增這一條，
     上面的規則一條都沒有動；卡片本體、字級、hover 全部沿用 .pcard 那一組。
   ⚠ 間距用 clamp 跟著版面收斂，與 .pcard-grid 的 gap 同一個寫法（SSOT 的節奏）。 */
.result-list {
    display: grid;
    gap: clamp(1rem, 2vw, 1.5rem);
    margin: 0;
    padding: 0;
    list-style: none;
}

/* ───────────────────────────────────────────────────────────────────────
   17b. 搜尋：header 同列往左推出的行內輸入框（2026-08-26 客戶指定）
   ───────────────────────────────────────────────────────────────────────
   客戶附參考圖（Taipei Music Center 官網）：「搜尋現在是下拉視窗的形式，
   改成在header同一欄位按搜尋後往左推出搜尋匡可搜尋」——原本 2026-08-20
   那版「點下去彈出滿版下拉列」（.site-search-panel，蓋住 header 以下整條，
   半透明遮罩＋鎖捲動＋焦點陷阱）整組拿掉，改成輸入框直接在 header 這一列
   原地往左長出來，全程留在列高裡，不再往下多長一塊。標記搬到
   layouts/_header.php 開頭（.site-inline-search，緊接在 .site-nav-primary
   前面），開關／篩選邏輯在 home.js 同名新 IIFE（資料來源與比對方式沿用
   舊版，只是換了容器）。
   ⚠ 只在 .site-nav-primary 本身可見的情境（≥992px 且 .is-compact，也就是
     全站永遠成立的統一列，見「全站 Header 統一不展開」區塊）才會展開；
     <992 沒有這顆 icon，抽屜裡的 .site-search 維持原本直接跳轉 /search，
     不做行內展開（版面沒有空間可以「往左推」）。
   ⚠ 能長出來的空間全靠 .site-logo 既有的 margin-right:auto：這欄平常是
     空的（logo 靠左、AAA／搜尋／選單鈕整組被推到最右緣），輸入框寬度從 0
     長到目標值時，logo 的 auto margin 自動讓出等量空間，不必另外算
     calc()——這也是為什麼一定要放進同一個 flex 列，放到 fixed/absolute
     版位就沒有這個免費機制。
   ⚠ order 跟 .site-nav-primary 同值（2），_header.php 裡 DOM 位置排在
     icon 前面，flex order 相同時 tie-break 靠文件順序，視覺上正好落在
     icon 左邊——不需要發明新的 order 數字，也不用去動 AAA／選單鈕既有的
     order。 */
.site-inline-search {
    display: none;
}

@media (min-width: 992px) {
    html.is-compact:not(.has-menu-open) .site-inline-search {
        display: flex;
        align-items: center;
        gap: .75rem;
        order: 2;
        position: relative;
        width: 0;
        min-width: 0;
        overflow: hidden;
        transition: width var(--transition);
    }

    /* ⚠ 2026-08-26 客戶「熱門關鍵字放跟搜尋同行，不要放下一行」——展開寬度
       從 22rem 加大到 40rem，讓輸入框＋四顆標籤能真的擠進同一行，不必
       靠換行或另外長一塊來容納。第二個值是窄桌機（992～1100）的上限保險，
       避免把 logo 擠沒；⚠ 原本抓 46vw 是錯的——實測 992 寬時 46vw＝456px，
       扣掉內距與 gap 之後只剩 184px 給標籤，但四顆標籤實際需要 232px，
       最後一顆「簡報」會被裁一半（雖然可以橫向捲動，視覺上像沒做完）。
       改成 52vw（992 寬約 516px）讓四顆完整放得下，實測此時 logo 與色塊
       左緣仍有餘裕。
       ⚠ box-sizing 明寫 border-box——本站沒有全域 border-box reset
       （見 ≈18168 同類提醒），不寫的話 padding-inline 會讓實際寬度超出
       width 設定的值。
       ── 底色沿革（同日三輪，留著避免下次又繞回來）──────────────────
       ① 一開始只有輸入框自己有白底：header 全站透明，輸入框浮在頁面內容
          上會疊字看不清楚。
       ② 熱門關鍵字搬進同一行之後，label 與標籤一樣會疊字，於是把底色從
          輸入框搬到這個外層，整條 close＋input＋suggest 共用一塊；客戶
          接著指定「搜尋匡白底不線匡不圓角」，改成 --muted（純白）＋
          border:0＋border-radius:0。
       ③ 2026-08-26 最後定案「搜尋匡不要背景」——**底色整個拿掉**。這一步
          之所以成立，是因為同一輪已經先做了「header 捲動後補底色」
          （見檔尾 html.is-compact .site-header.is-scrolled）：可讀性改由
          header 自己那塊 --background 灰承擔，搜尋列不必再自己疊一層，
          視覺上跟 header 融成一體而不是浮在上面的一張白卡。
       ⚠ 已知取捨：停在頁首（scrollY≤14）時 header 仍是全透明（客戶
          2026-08-14「初始狀態不要背景」的定案），此時展開搜尋，文字是直接
          疊在頁面內容上的。客戶明確要求不要背景，這是他選的取捨，不要
          自作主張補回半透明底。 */
    html.is-compact:not(.has-menu-open) .site-inline-search.is-open {
        width: min(40rem, 52vw);
        overflow: visible;
        box-sizing: border-box;
        background: transparent;
        border: 0;
        border-radius: 0;
        padding-inline: .5rem;
    }

    @media (prefers-reduced-motion: reduce) {
        html.is-compact:not(.has-menu-open) .site-inline-search {
            transition: none;
        }
    }

    /* ✕ 放輸入框最前面（參考圖版面：close 在左、輸入框在右），收合時
       跟輸入框一起被 overflow:hidden 裁掉，不必另外處理可見度。 */
    html.is-compact:not(.has-menu-open) .site-inline-search-close {
        flex: 0 0 auto;
        display: inline-flex;
        width: 1.75rem;
        height: 1.75rem;
        align-items: center;
        justify-content: center;
        border: 0;
        background: transparent;
        color: hsl(var(--slate));
        font-size: 1rem;
        line-height: 1;
        cursor: pointer;
    }

    html.is-compact:not(.has-menu-open) .site-inline-search-close:hover,
    html.is-compact:not(.has-menu-open) .site-inline-search-close:focus-visible {
        color: hsl(var(--heading));
    }

    /* ⚠ 2026-08-26 排版回測修正：這裡原本是 `flex: 0 1 10rem`（輸入框固定
       10rem、多的空間全部分給熱門關鍵字那一區）——實測 1800 寬的結果是
       輸入框只有 160px、熱門關鍵字區卻拿到 412px，而標籤本身只用掉 232px，
       白色色塊右端因此空了 106px 什麼都沒有（客戶截圖看到的就是這塊）。
       主從也顛倒了：輸入框是這個元件的主角，卻是整條裡最窄、最擠的元素。
       改成輸入框吃掉剩餘空間（flex:1 1 auto）、熱門關鍵字區只佔自己內容
       需要的寬度（下面 flex:0 1 auto）——色塊右端不再留白，輸入框也從
       160px 長到約 258px。 */
    /* ⚠ flex-basis 明寫 5rem、不用 auto：auto 會去看子元素 <input> 的
       `width:100%`，兩者互相參照，瀏覽器解出來的基準寬不直覺，實測結果是
       「該讓的是輸入框，卻變成標籤被壓縮裁掉」。改成明確的 5rem 基準＋
       shrink 權重 5（標籤那側是 1）——空間夠時 grow:1 讓輸入框吃掉剩餘
       寬度，空間不夠時優先壓縮輸入框而不是裁掉標籤。 */
    html.is-compact:not(.has-menu-open) .site-inline-search-form {
        flex: 1 5 5rem;
        min-width: 5rem;
    }

    /* 輸入框高度比照選單鈕（2.25rem／36px）同一個列高節奏，全站唯一
       另一個常駐 header 控制項就是那顆，維持一致。底色／外框已經改掛在
       外層 .site-inline-search.is-open（見上面那段註解），這裡不用再疊
       第二層，輸入框本身只留文字排版屬性。
       ⚠ 2026-08-26：原本這裡還有一條 focus 才顯色的 border-bottom 當聚焦
       提示，但客戶明確要求「搜尋匡白底不線匡」，那條底線就是唯一還留著的
       線、在白色色塊裡看起來像沒收乾淨的殘留（客戶截圖可見），移除。
       聚焦提示改由「展開時自動 focus、游標在框內閃爍」承擔——這個元件
       本來就是點了才展開、展開就 focus，不存在「使用者不知道焦點在哪」
       的情境。 */
    html.is-compact:not(.has-menu-open) .site-inline-search-input {
        width: 100%;
        min-width: 0;
        height: 2.25rem;
        border: 0;
        background: transparent;
        padding: 0 .5rem;
        font-family: var(--font-body);
        font-size: var(--text-meta);
        font-weight: var(--fw-light);
        letter-spacing: var(--tracking);
        color: hsl(var(--heading));
    }

    html.is-compact:not(.has-menu-open) .site-inline-search-input::placeholder {
        color: hsl(var(--foreground) / .38);
    }

    html.is-compact:not(.has-menu-open) .site-inline-search-input:focus {
        outline: none;
    }

    /* ⚠ 2026-08-26 客戶截圖回報：打字之後輸入框右側會冒出一顆**藍色 ✕**。
       那不是我們畫的，是 `<input type="search">` 的瀏覽器原生清除鈕
       （WebKit/Blink 的 ::-webkit-search-cancel-button，實測 display
       為 inline-block），顏色由瀏覽器決定、跟全站色票無關——站上沒有
       任何一處用這種藍。而且它跟左邊自己那顆 ✕ 語意打架（一顆關搜尋、
       一顆清內容），畫面上兩顆叉更亂。整顆關掉。
       ⚠ 不改成 type="text"：type="search" 有語意價值（讀屏會唸「搜尋」、
         行動裝置鍵盤會出現「搜尋」鍵），只是不要它的預設外觀，所以是
         關掉偽元素而不是換 type。
       ⚠ ::-webkit-search-decoration 一起關：Safari 會在框內左側多留一塊
         裝飾空間，不關會讓文字起點跟 placeholder 對不齊。
       ⚠ 這兩個偽元素刻意**拆成兩條規則**、不合併成一個選擇器清單：CSS
         選擇器清單是全有全無，只要清單裡有任何一個選擇器是該瀏覽器不認得
         的，整條規則會被丟掉（連同認得的那個一起失效）。這兩個偽元素的
         支援度各家不同（Firefox 兩個都不認），拆開才能各自獨立生效。
         （這是預防性寫法；本輪並未實際觀察到合併版被丟棄——當時的
         CSSOM 檢查腳本本身有 bug，讀到的是空的規則表，不能當證據。） */
    html.is-compact:not(.has-menu-open) .site-inline-search-input::-webkit-search-cancel-button {
        -webkit-appearance: none;
        appearance: none;
        display: none;
    }

    html.is-compact:not(.has-menu-open) .site-inline-search-input::-webkit-search-decoration {
        -webkit-appearance: none;
        appearance: none;
        display: none;
    }

    /* 熱門關鍵字：跟輸入框同一行（flex 子項，不是另外長出來的區塊）。
       label 固定寬不擠壓、不捲動；標籤本身放進內層
       .site-inline-search-suggest-tags 自己 overflow-x:auto——四顆標籤
       加起來的寬度不保證在每個 ≥992 的視窗寬都放得下，用橫向捲動維持
       「同一行」而不是被迫換行或縮爆版面。還沒輸入關鍵字時才顯示
       （home.js 沿用舊有 suggest.hidden 邏輯），有輸入內容就讓路給下面
       錨定的結果下拉。 */
    /* ⚠ flex 基準是 `0 1 auto`（只佔內容需要的寬、空間不夠時才壓縮），
       不是 `1 1 auto`——會長大的是輸入框那一側（見上面 -form 那段的
       回測註解），這一區跟著內容走就好，否則多出來的空間會變成色塊
       右端一片空白。 */
    html.is-compact:not(.has-menu-open) .site-inline-search-suggest {
        display: flex;
        align-items: center;
        gap: .625rem;
        flex: 0 1 auto;
        min-width: 0;
    }

    html.is-compact:not(.has-menu-open) .site-inline-search-suggest-label {
        flex: 0 0 auto;
        white-space: nowrap;
    }

    html.is-compact:not(.has-menu-open) .site-inline-search-suggest-tags {
        display: flex;
        align-items: center;
        gap: .5rem;
        flex: 0 1 auto;
        min-width: 0;
        overflow-x: auto;
        scrollbar-width: none;
    }

    html.is-compact:not(.has-menu-open) .site-inline-search-suggest-tags::-webkit-scrollbar {
        display: none;
    }

    html.is-compact:not(.has-menu-open) .site-inline-search-suggest-tag {
        flex: 0 0 auto;
    }

    /* 結果下拉：只有「已輸入關鍵字」（.is-searching）才出現，熱門關鍵字
       搬到同一行之後，這裡不再兼放熱門標籤——內容本質是清單（可能好幾
       筆），沒辦法塞進同一行，維持錨定在輸入框正下方。
       ⚠ 2026-08-26 客戶「這個搜尋介面有沒有樣式要調整的」回測抓到兩點：
       ① **左緣沒對齊輸入框的文字**：原本 `left` 沒設、靠 `width:max(100%,22rem)`
          ＋`right:0` 撐開，等於左緣切齊整條列（＝左邊那顆 ✕ 的位置，實測
          591.4），但使用者打的字起點在 647.4——「沒有符合的結果」比使用者
          剛打的字往左凸出 56px，讀起來像兩個不相干的區塊。改成用 left 明確
          對齊表單起點：列內距 .5rem ＋ ✕ 寬 1.75rem ＋ gap .75rem，這三個值
          就是上面那三條規則自己寫的數字，不是量出來硬填的魔術數字；再讓
          內距等於輸入框自己的 padding-inline（.5rem），文字就跟輸入框裡的
          字切在同一條垂直線上。右緣同理收 .5rem 對齊輸入框右緣。
       ② **底色跟 header 撞色**：原本 --background 正好就是 header 捲動後
          那塊灰（同一顆 token），兩者疊在一起只剩 1px 邊框在分界，看起來
          像 header 自己長高了一塊。改用 --muted（實測純白），跟灰底 header
          之間有明確層次，才讀得出「這是浮出來的一層」。
       ⚠ 內距上下原本 1rem/1.25rem 不對稱（舊版有多個分區時的殘留），
          單行「沒有符合的結果」會顯得下面比較空，收成上下等值。 */
    html.is-compact:not(.has-menu-open) .site-inline-search-drop {
        display: none;
        position: absolute;
        top: calc(100% + .75rem);
        left: calc(.5rem + 1.75rem + .75rem);
        right: .5rem;
        background: hsl(var(--muted));
        border: 1px solid hsl(var(--foreground) / .12);
        border-radius: var(--radius-media);
        box-shadow: 0 12px 32px hsl(var(--foreground) / .12);
        padding: .875rem .5rem;
        z-index: 60;
    }

    html.is-compact:not(.has-menu-open) .site-inline-search.is-open.is-searching .site-inline-search-drop {
        display: block;
    }
}

.site-inline-search-suggest-label {
    font-size: var(--text-meta);
    letter-spacing: var(--tracking-meta);
    color: hsl(var(--slate));
}

/* 2026-08-26 客戶「熱門關鍵字膚色背景不線框」——邊框拿掉，底色換成
   --nude（全站既有「膚色」具名色 #F2E3D0，見 tokens.css ≈605），不是
   臨時調一個新色票。hover 沒有邊框可以變色，改用 filter 微幅加深，
   維持「可以按」的提示但不新增色票。 */
.site-inline-search-suggest-tag {
    border: 0;
    border-radius: var(--radius-pill);
    background: hsl(var(--nude));
    padding: .3rem .875rem;
    font-size: var(--text-meta);
    color: hsl(var(--heading));
    cursor: pointer;
    transition: filter var(--transition);
}

.site-inline-search-suggest-tag:hover,
.site-inline-search-suggest-tag:focus-visible {
    filter: brightness(.94);
}

/* 拿掉邊框後鍵盤 focus 沒有既有的顏色變化可以借用當可視化提示，
   單獨補一圈 outline——只在 :focus-visible（鍵盤／輔助工具），滑鼠
   點擊不觸發，不會在畫面上多一圈滑鼠使用者用不到的線。 */
.site-inline-search-suggest-tag:focus-visible {
    outline: 2px solid hsl(var(--foreground) / .5);
    outline-offset: 2px;
}

.site-inline-search-empty {
    margin: 0;
    font-size: var(--text-meta);
    color: hsl(var(--slate));
}

.site-inline-search-results {
    display: grid;
    gap: .25rem;
    margin: 0;
    padding: 0;
    list-style: none;
}

.site-inline-search-results .site-inline-search-group {
    margin: .75rem 0 .25rem;
    font-size: var(--text-meta);
    letter-spacing: var(--tracking-meta);
    color: hsl(var(--slate));
}

.site-inline-search-results .site-inline-search-group:first-child {
    margin-top: 0;
}

.site-inline-search-results a {
    display: block;
    padding: .5rem;
    margin-inline: -.5rem;
    border-radius: var(--radius-media);
    color: hsl(var(--heading));
    text-decoration: none;
    font-size: var(--text-body);
    font-weight: var(--fw-light);
    transition: background var(--transition);
}

.site-inline-search-results a:hover,
.site-inline-search-results a:focus-visible {
    background: hsl(var(--foreground) / .06);
    outline: none;
}

/* ───────────────────────────────────────────────────────────────────────
   18. 回測修正：按鈕回到站上唯一的那一套（2026-08-05）
   ───────────────────────────────────────────────────────────────────────
   實測 1920／1440 三頁後抓到的問題：本次切版自己做了一組**黑色實心按鈕**
   （.btn-pill--solid、選中的標籤／頁碼／步驟編號），但站上早在 2026-08-03
   就依客戶指定把「所有黑按鈕都改成藍色 hover 膚色」（見本檔 8480 那一段），
   首頁上的每一顆 .btn-pill 實測都是 AERIAL 藍實心 + 黑色徽章。
   黑色等於憑空多出第二種按鈕語言，與「全站唯一的按鈕樣式」相牴觸。

   ⚠ 本節**只覆寫顏色**，尺寸、間距、徽章動態一個字都沒動；
     上面第 7 節那幾條原始宣告也一行都沒刪（要退回把本節註解掉即可）。
   ⚠ 一律只用既有的兩個 token：--btn-fill（AERIAL 藍）／--btn-fill-hover（NUDE 膚色），
     沒有新增任何色票。
   ⚠ 主要／次要的層級改用站上**既有的描邊語言**做（知識專區的
     .kz-tags .tag-pill 與 .kz-links a 就是這一套：透明底 + SLATE 細框 + 黑字，
     hover 才填膚色），不是再發明一種顏色。 */

/* ── ① 主要動作：撤掉黑色實心，回到站上的藍實心 ─────────────────────── */
.btn-pill--solid {
    background: var(--btn-fill);
    border-color: var(--btn-fill);
    color: hsl(var(--heading));
}

.btn-pill--solid::after {
    background: hsl(var(--foreground));
}

.btn-pill--solid:hover,
.btn-pill--solid:focus-visible {
    background: var(--btn-fill-hover);
    border-color: var(--btn-fill-hover);
    color: hsl(var(--heading));
}

.btn-pill--solid:hover::after,
.btn-pill--solid:focus-visible::after {
    background: hsl(var(--foreground));
    color: var(--btn-fill-hover);
}

/* ── ② 次要動作：描邊版（＝首頁知識專區那一套）─────────────────────────
   影片／Podcast、文章頁尾的返回／收藏／分享、表單裡不是主要動作的那一顆。
   ⚠ 這三個選擇器的特異度都 ≥ 上面 8539 那條 .btn-pill（0,1,0）且位置更後面，
     所以一定贏；hover 另外明寫，否則 .btn-pill:hover（0,2,0）與
     .entry-links .btn-pill（0,2,0）同分時會由位置決定，靜置的框會蓋掉 hover 的填色。 */
.entry-links .btn-pill,
.btn-pill--sm,
.form-actions .btn-pill:not(.btn-pill--solid) {
    background: transparent;
    border-color: hsl(var(--slate));
    color: hsl(var(--heading));
}

.entry-links .btn-pill::after,
.btn-pill--sm::after,
.form-actions .btn-pill:not(.btn-pill--solid)::after {
    background: hsl(var(--foreground));
}

.entry-links .btn-pill:hover,
.entry-links .btn-pill:focus-visible,
.btn-pill--sm:hover,
.btn-pill--sm:focus-visible,
.form-actions .btn-pill:not(.btn-pill--solid):hover,
.form-actions .btn-pill:not(.btn-pill--solid):focus-visible {
    background: var(--btn-fill-hover);
    border-color: var(--btn-fill-hover);
    color: hsl(var(--heading));
}

/* ── ③ 「已收藏」＝已作用狀態，用膚色（站上表示「作用中」的那一個填色）──
   ⚠ 與 hover 同色是可接受的：文字同時由「收藏」換成「已收藏」，
     狀態不會只靠顏色傳達（也符合不單靠顏色的無障礙原則）。 */
.btn-pill[data-fav="1"],
.btn-pill--sm[data-fav="1"] {
    background: var(--btn-fill-hover);
    border-color: var(--btn-fill-hover);
    color: hsl(var(--heading));
}

.btn-pill[data-fav="1"]::after {
    background: hsl(var(--foreground));
}

/* ── ④ 選中態一律藍實心（標籤／頁碼／步驟編號）────────────────────────
   ⚠ 刻意與 hover 的膚色分開：hover 是「滑鼠在上面」，選中是「你現在在這裡」，
     兩個狀態必須看得出差別，所以選中用 --btn-fill、hover 用 --btn-fill-hover。 */
.tag-pill.is-active,
.pager-item.is-current,
.stepper-item.is-current .stepper-no {
    background: var(--btn-fill);
    border-color: var(--btn-fill);
    color: hsl(var(--heading));
}

.tag-pill.is-active:hover,
.tag-pill.is-active:focus-visible,
.pager-item.is-current:hover,
.pager-item.is-current:focus-visible {
    background: var(--btn-fill-hover);
    border-color: var(--btn-fill-hover);
    color: hsl(var(--heading));
}

/* ── ⑤ 「重新寄送驗證信」送出後的停用外觀（2026-08-05）──────────────────
   原型是由 onclick 直接寫 el.style.color / pointerEvents，兩者都會被 CSP 擋，
   而且顏色本來就該由 CSS 決定。行為改由 pages.js 掛 .is-sent，外觀寫在這裡。 */
.form-foot a.is-sent {
    pointer-events: none;
    color: hsl(var(--slate));
    border-bottom-color: transparent;
}

/* ═══════════════════════════════════════════════════════════════════════
   19. 內頁進階版型 v2（2026-08-05 客戶指定，附三張參考稿）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「請參考這些版面，讓內頁多一點設計，現在整體太單調，不要改到首頁，
          內頁可以往矩形大圖去做層次，文字段落以 14px 然後行距大點現在太擠，
          有些地方也可以背景壓灰不要都全白，可以多運用膚色藍色灰色這幾個搭配，
          你先改看看顧問案的頁面，我先確認再進行其他頁面改版。」

   ── 三張參考稿各取什麼 ────────────────────────────────────────────────
     Elevate（建築師事務所）  深色帶 ＋ 右側**矩形大圖**貼齊版邊；下方是
                              「左小標／右內容」的編輯式兩欄列，靠細線分段
     Coverly（保險服務）      **色塊馬賽克**：圖片格與純色文字格交錯拼成一片矩形，
                              每格內編號／標題在上、說明貼底
     FYLLA（部落格）          大圖卡 ＋ 極細分隔線 ＋ 底部訂閱帶

   ── 紀律 ──────────────────────────────────────────────────────────────
   ⚠ 全部掛在 **.page--v2** 這個修飾子底下，只有顧問案那一頁掛它。
     首頁（無 .page）與其餘 17 個內頁（只有 .page）**一個像素都不會變**，
     客戶確認後再把 .page--v2 加到其他頁即可。
   ⚠ 沒有新增任何色票：深灰 --surface-dark（SLATE #7C7D80）、
     膚色 --nude #F2E3D0、藍 --accent-blue #B0C7DD、
     淺灰 --surface-grey #DCDCE1 / --surface-grey-deep #C4C4CC、
     白 --muted、頁面底 --background —— 全部是站上既有的。
   ⚠ 版面容器沿用既有的 .site-section / .section-bleed / .section-bleed-inner，
     不另造一套格線（同一條版心，色帶才切得齊）。
   ═══════════════════════════════════════════════════════════════════════ */

/* ── 字級：段落 14px、行距放鬆（客戶指定）──────────────────────────────
   ⚠ 這是**刻意偏離**「內文比對首頁」的那一版：首頁內文是 --fs-3（20/18/17px），
     客戶這一輪明確指定內頁段落走 14px 且行距要更鬆。
     所以在 .page--v2 上重新定義 token，而不是逐條改 font-size（站上既有做法）。
   ⚠ 用 rem 不是 px：header 的 A／A+／A++ 字級切換靠根字級，
     寫死 px 會讓這一頁不跟著放大。
   ⚠ 行高 2.3 ＝ 14px 排出 32px 行距。客戶說「現在太擠」指的是整片文字的密度，
     所以除了行高，段落之間的間距（--v2-p-gap）也一起放大到 1.5rem。 */
.page--v2 {
    --text-body: .875rem;    /* 14px */
    --lh-body:   2.3;        /* ≈ 32px */
    --text-meta: .75rem;     /* 12px｜小標／編號／日期 */
    --v2-p-gap:  1.5rem;     /* 段落之間 */
    --v2-band-y: clamp(3.5rem, 7vw, 6rem);   /* 色帶上下留白 */
}

/* ═══ 19-1 Hero：深色帶 ＋ 右側矩形大圖（Elevate）═══════════════════════ */

/* ⚠ 這一段是 .site-section.section-bleed，所以底色會做到視窗兩側；
   上下內距歸零，改由內部的文字欄與圖片自己撐高 —— 圖片才貼得到色帶上下緣。
   ⚠ 2026-08-05 第二輪：底色由 SLATE 深灰改成**淺灰 --surface-grey（#DCDDE0）**
     —— 客戶提供色票並指定「以壓這個明度的灰為主，不要壓膚色跟深灰色」。
     文字因此整組翻回淺底那一面（黑字／SLATE 小標／中灰內文）。 */
.page-hero {
    padding-block: 0;
    background: hsl(var(--surface-grey));
    color: hsl(var(--foreground));
}

.page-hero-grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.06fr);
    gap: clamp(1.75rem, 4vw, 4rem);
    align-items: stretch;
}

.page-hero-text {
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding-block: clamp(3rem, 6.5vw, 6rem);
}

/* ⚠ 2026-08-13 客戶「全站標題上的英文小字一律改成線條在英文小字下，跟首頁
   一樣」——原本前面一顆藍色小圓點（參考稿 Elevate 的「• Architect」），
   是當初刻意不跟其餘標題統一的例外（見第 32 節：「兩個記號疊在同一個
   位置會打架」）。客戶這次講的是「一律」，不再留這個例外，圓點拿掉、
   換成跟其他標題同一條線，長在文字**下面**（不是文字前面）。
   ⚠ display 從 flex 改回預設 block：圓點在的時候靠 flex 把圓點跟文字排
     成一行；現在沒有圓點要並排了，線是獨立一個 block 疊在文字下方，
     flex 反而會把它跟文字擠成同一行（見下面的 ::after）。
   ⚠ 這個元件只有一個活的用法（.page-hero-heading .page-hero-en，服務
     專區「企業內訓／一對一諮詢／線上課程／講座邀約」四頁的 Hero，靠右
     對齊），線的置右寫在那條規則裡，這裡只管圓點換線本身。 */
.page-hero-en {
    margin: 0 0 1.5rem;
    font-family: var(--font-eyebrow);
    font-size: var(--text-meta);
    line-height: var(--lh-meta);
    font-weight: var(--fw-normal);
    letter-spacing: var(--tracking-meta);
    color: hsl(var(--slate));
}

.page-hero-en::after {
    content: "";
    display: block;
    width: 2.25rem;
    height: 1px;
    margin-top: clamp(.875rem, 1.6vw, 1.25rem);
    background: currentColor;
    opacity: .55;
}

.page-hero-title {
    margin: 0;
    font-size: var(--text-section);
    line-height: 1.25;
    font-weight: var(--fw-heading);
    letter-spacing: var(--tracking-display);
    color: hsl(var(--heading));
}

.page-hero-lead {
    margin: 1.5rem 0 0;
    max-width: 30em;
    font-size: var(--text-body);
    line-height: var(--lh-body);
    font-weight: var(--fw-light);
    letter-spacing: var(--tracking);
    color: hsl(var(--muted-foreground));
}

/* ⚠ 2026-08-05 第二輪：標題底下那一排分類標籤依客戶指定移除
   （「顧問案標題底下的三個分類刪掉，不要幫她做分類」），
   對應的 .page-hero-tags 樣式一併拿掉，不留沒有人用的死碼。 */

.page-hero-actions {
    display: flex;
    flex-wrap: wrap;
    gap: .75rem;
    margin-top: 2.5rem;
}

/* 右側矩形大圖：貼齊色帶的上下緣，並往右出血到視窗邊
   （參考稿 Elevate 的大圖就是切齊畫布右緣的一塊矩形）。
   ⚠ 出血量 ＝ .section-bleed-inner 的左右內距（--section-x），
     用負 margin 抵銷掉，不用 100vw —— 100vw 含捲軸寬，會多出半條捲軸的橫向捲動
     （站上踩過的坑，見本檔開頭的「踩過的坑」清單）。 */
.page-hero-media {
    position: relative;
    margin: 0;
    margin-right: calc(var(--section-x) * -1);
    min-height: clamp(300px, 40vw, 540px);
    background: hsl(var(--surface-dark-foreground) / .08);
    overflow: hidden;
}

.page-hero-media picture,
.page-hero-media img {
    position: absolute;
    inset: 0;
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

@media (max-width: 1023.98px) {
    .page-hero-grid {
        grid-template-columns: minmax(0, 1fr);
        gap: 0;
    }

    /* 窄幅圖片改成排在文字上方，並左右都出血成一整條矩形 */
    .page-hero-media {
        order: -1;
        margin-inline: calc(var(--section-x) * -1);
        min-height: clamp(220px, 52vw, 340px);
    }

    .page-hero-text { padding-block: clamp(2.25rem, 7vw, 3rem); }
}

/* ═══ 19-1b 會員登入／註冊：左圖右文 split（2026-08-19）═══════════════
   客戶提供參考圖（Realnest 登入頁）要求「左圖右文」重新編排，登入頁與
   註冊頁（3 步中「資料填寫」那一步）適用；服務聲明／完成／忘記密碼
   三步維持原本置中窄版（2026-08-06 已有的決定：兩欄會讓填寫動線變散，
   這次沒有要推翻，只是這兩頁客戶另有指示）。
   ⚠ 技術手法照上面 .page-hero-grid／.page-hero-media／.page-hero-text
     鏡射，不是重新發明：grid stretch 兩欄同高、負 margin 出血、
     object-fit:cover、手機圖片翻到最上面單欄，同一套機制只是換左右
     方向（圖片欄在左，出血用 margin-left 不是 margin-right）與換一組
     語意對的 class 名稱——內容從行銷引言換成表單，兩組元件互不影響。
   ⚠ markup 比照 .page-hero 的巢狀慣例：外層要是
     .site-section.section-bleed，中間包一層 .section-bleed-inner
     （負責 padding-inline:var(--section-x)），.mem-split-grid 才放
     在最裡面——.mem-split-media 的負 margin-left 算式是拿這層 padding
     抵消，沒有 .section-bleed-inner 這層出血量會算錯。
   ⚠ 2026-08-19 追加「圖片高延伸到 header 後面、底延伸到 footer」：
     .site-section 預設 padding:var(--section-y) var(--section-x)，這裡
     跟 .page-hero 一樣把 padding-block 歸零，上下才不會多留一截白邊。
     .mem-split-grid 在桌機補 min-height:100svh 扣掉 header 高——
     .wf-frame 對沒有 .hero 的頁面本來就有 padding-top:var(--header-h)
     （見本檔 10979，這頁不動這條，.mem-split-media 底下的負
     margin-top 正是拿它來抵），兩者相加等於整個第一屏，表單欄跟圖片
     欄同高、圖片欄自然貼齊 footer 上緣。手機 header 是 sticky 不是
     fixed（不佔用這個算式），沿用下面既有的手機 stacked 版型，不套
     這段 min-height。 */
.mem-split {
    position: relative;
    padding-block: 0;
}

.mem-split-grid {
    display: grid;
    grid-template-columns: minmax(0, 1.06fr) minmax(0, 1fr);
    gap: clamp(1.75rem, 4vw, 4rem);
    align-items: stretch;
}

/* 圖片欄出血到視窗左緣；min-height 比 .page-hero-media 高一截——
   那裡是頁首引言、底下還有其他區塊，這裡是整頁唯一內容。 */
.mem-split-media {
    position: relative;
    margin: 0;
    margin-left: calc(var(--section-x) * -1);
    min-height: clamp(26rem, 46vw, 40rem);
    background: hsl(var(--surface-dark-foreground) / .08);
    overflow: hidden;
}

/* ⚠ 桌機專屬：header 在 ≥992px 是 position:fixed（見本檔 4514），圖片欄
   才有「後面」可言；手機 header 是 sticky、佔用文件流，硬套負 margin
   會把圖片拉到 sticky header 底下疊在一起，反而穿幫。
   ⚠ margin-top 用負值、不額外補 padding-top：.mem-split-media 是
     align-items:stretch 的 grid item，負 margin 只會讓瀏覽器算出
     「多出 header 高」的 stretch 高度去填滿外扣的空間，頂邊往上位移
     的同時底邊留在原地（跟 .mem-split-panel 的底緣對齊不會跑掉）——
     跟本檔其餘「負 margin＋padding 補償」的出血手法不是同一種情境
     （那些是內容撐高的 block 版面，這裡是 grid stretch，補 padding
     反而會把裡面 inset:0 的 <img> 從補償掉的那塊空間往下推，多留白邊）。
   ⚠ 圖片要真的「看得到」在 header 後面（不是被蓋住），header 本身要在
     這頁變透明——見下面 :has(.mem-split) 那條，鏡射既有的
     :has(.hero) 透明機制，兩者互相獨立，不共用同一條規則。 */
/* ⚠ 2026-08-28 客戶「圖片高度縮矮一點現在太高」——桌機的圖片高度有**兩個
     互相獨立的來源**，只改一個是沒有用的（實測過才發現）：
       ① 短內容的頁面（登入，右欄內容才 599px）＝ 下面 .mem-split-grid 的
          min-height 撐出來的。原本 100svh − header，加上圖片欄那條負
          margin 補回 header 高，圖片實際就是**整整 100svh**（1440×900 實測
          900px）。這輪改成 76svh，實測 900 → 684px。
       ② 長內容的頁面（服務聲明 928px、資料填寫）＝ 右欄文字撐出來的，
          grid 是 align-items:stretch，圖片只是被拉著一起長，**跟 min-height
          完全無關**（實測 1440×900 圖片 1171px＝130%vh）。這種要靠新增的
          max-height 才收得住。
     所以下面是一條改值、一條新增，兩條都要有。
   ⚠ max-height 用 svh 不用 %：grid item 的百分比高度要回頭參照 grid 軌道
     高度，而軌道高度正是被內容撐出來的那個值，寫 % 會變成「內容多高就多
     高」的循環，等於沒限制。
   ⚠ 兩個數字刻意同為 76svh：這樣短內容的頁面剛好貼齊上限、圖片仍填滿整
     欄不留空白，長內容的頁面則收在同一個高度，三頁的圖片視覺高度一致。 */
@media (min-width: 992px) {
    .mem-split-grid {
        min-height: calc(76svh - var(--header-h));
    }

    .mem-split-media {
        margin-top: calc(-1 * var(--header-h));
        max-height: 76svh;
    }
}

.mem-split-media picture,
.mem-split-media img {
    position: absolute;
    inset: 0;
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* ── 濾鏡：2026-08-28 客戶指定移除（規則已拿掉，以下保留沿革）──────────
   2026-08-19 客戶「圖片請跟 hero 一樣加上濾鏡」，當時的做法是照抄
   .hero-media-item img（≈748 行，2026-08-14「hero大圖幫我改淺一點，
   讓圖片亮一點灰調一點」那輪定案的值）：

       .mem-split-media img { filter: brightness(1.12) saturate(.75); }

   當時的理由是「讓圖片跟全站唯一的 hero1~4 素材來源在調性上維持一致」
   ——那個理由**成立的前提是這三頁用的就是 hero1**（同一批素材）。
   2026-08-28 客戶把圖換成專為會員區提供的 member.jpg，已經不是 hero
   素材，同一輪同時指定「將該區塊的濾鏡移除」，等於前提與結論一起收回，
   所以是把規則刪掉、不是改數值。
   ⚠ .hero-media-item img 的濾鏡**沒有動**：那是首頁 Hero 自己的規則，
     客戶這次講的是會員區這個區塊，兩者只是數值曾經照抄過，沒有共用同
     一條規則，不要順手一起改。
   ⚠ 三頁（登入／服務聲明／資料填寫）吃同一條規則，所以是一起生效——
     它們本來就共用整套 .mem-split-* 樣式，這次沒有為任何一頁破例。 */

/* 2026-08-19 客戶「圖片請一樣遮罩動畫切割進場」——照抄
   .cd-enroll-lines／.cd-enroll-mask-grid 那一套（course_detail.php
   報名資訊區塊，style.css ≈17757），不是重新設計一組新的視覺：永久細線層
   （.mem-split-lines，z-index:1，3 欄 × 2 列矩形格線，動畫播完後留著）
   ＋動畫遮罩層（.mem-split-mask-grid，z-index:2，同一組格子，初始蓋滿、
   JS 加上 .is-mem-split-revealed 後各自往不同方向縮回）。跟 cd-enroll
   同一個理由用 3×2 滿版矩形（不是 Hero 的 4×2 缺角版）：.mem-split-media
   本身也是純矩形、沒有 clip-path 缺角。
   ⚠ 遮罩色吃 hsl(var(--background))——這裡跟 cd-enroll 的理由一樣，
     顯影前遮罩要融入頁面底色，不能讀圖片欄自己的暗底裝飾色，否則顯影前
     那一刻會像突兀的色塊而不是「蓋住圖片」的錯覺。
   ⚠ margin:-1px 是本輪稍早修過的次像素縫隙 bug（.hero-mask／
     .cd-hero-mask／.cd-enroll-mask 都有這一條，同一個成因同一個修法，
     見 style.css 開頭「踩過的坑」或直接找 margin: -1px 那幾條）。
   ⚠ JS 觸發邏輯（decode → 交錯 90ms delay → 雙 rAF → 加 class）沿用
     course_detail.php 的 cd-hero 那支（不是 cd-enroll 那支）——
     .mem-split-media 在首屏就看得到（跟 Hero 一樣），不需要
     IntersectionObserver 等捲動進場，寫在 assets/front/js/pages.js
     （23 頁共用檔，登入／註冊／服務聲明三頁才有對應元素，其餘頁面
     querySelector 撲空直接 return，沒有副作用）。 */
.mem-split-lines {
    position: absolute;
    inset: 0;
    z-index: 1;
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    grid-template-rows: repeat(2, 1fr);
    grid-template-areas:
        "a b c"
        "d e f";
    pointer-events: none;
}

.mem-split-line {
    border: 1px solid hsl(var(--background) / .85);
}

.mem-split-line--a { grid-area: a; }
.mem-split-line--b { grid-area: b; }
.mem-split-line--c { grid-area: c; }
.mem-split-line--d { grid-area: d; }
.mem-split-line--e { grid-area: e; }
.mem-split-line--f { grid-area: f; }

.mem-split-mask-grid {
    position: absolute;
    inset: 0;
    z-index: 2;
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    grid-template-rows: repeat(2, 1fr);
    grid-template-areas:
        "a b c"
        "d e f";
    gap: 0;
    pointer-events: none;
}

.mem-split-mask {
    background: hsl(var(--background));
    margin: -1px;
    transition: transform 560ms cubic-bezier(0.76, 0, 0.24, 1);
    will-change: transform;
}

.mem-split-mask--a { grid-area: a; transform-origin: left;   transform: scaleX(1); }
.mem-split-mask--b { grid-area: b; transform-origin: top;    transform: scaleY(1); }
.mem-split-mask--c { grid-area: c; transform-origin: right;  transform: scaleX(1); }
.mem-split-mask--d { grid-area: d; transform-origin: bottom; transform: scaleY(1); }
.mem-split-mask--e { grid-area: e; transform-origin: left;   transform: scaleX(1); }
.mem-split-mask--f { grid-area: f; transform-origin: right;  transform: scaleX(1); }

.mem-split-media.is-mem-split-revealed .mem-split-mask--a,
.mem-split-media.is-mem-split-revealed .mem-split-mask--c,
.mem-split-media.is-mem-split-revealed .mem-split-mask--e,
.mem-split-media.is-mem-split-revealed .mem-split-mask--f {
    transform: scaleX(0);
}

.mem-split-media.is-mem-split-revealed .mem-split-mask--b,
.mem-split-media.is-mem-split-revealed .mem-split-mask--d {
    transform: scaleY(0);
}

@media (prefers-reduced-motion: reduce) {
    .mem-split-mask {
        display: none;
    }
}

.mem-split-media.is-mem-split-static .mem-split-mask {
    display: none;
}

/* 2026-08-19 客戶「圖片底下加水平橫線，要整個頁面得寬，然後層級在圖片上」。
   ⚠ 「整個頁面得寬」：掛在 .mem-split（.section-bleed，天生滿版邊到邊，
     見本檔開頭該 class 定義）上用 left:0/right:0，跟 .cd-enroll-rule
     同一招，不用 100vw。
   ⚠ 「在圖片底下」的垂直位置桌機／手機是兩種答案，但都不需要 JS 量測
     （跟 .cd-enroll-rule 那支要另外寫 IIFE 量測的情況不同）：
     · 桌機：.mem-split-grid 是 align-items:stretch，.mem-split-media／
       .mem-split-panel／.mem-split-grid／.mem-split 四層桌機版本身沒有
       各自的上下內距差，四者的下緣本來就疊在同一條線上——直接
       bottom:0 掛在 .mem-split 就精確貼齊圖片下緣。
     · 手機：改成單欄堆疊（圖在上、表單在下，見下面 991.98px 斷點），
       圖片高度是固定的 clamp(220px,52vw,340px)（跟 .mem-split-media
       手機版 min-height 那條同一個值，抄錯要一起改），用 top 頂住而不是
       bottom——form 內容多長都不影響「圖片高度」這個值本身，比反過來
       從 .mem-split 的總高度倒推「還剩多少到圖片下緣」（表單內容多高
       是未知數，沒有固定值可以 calc()）簡單。
   ⚠ z-index:3——「層級在圖片上」，要蓋過 .mem-split-mask-grid（z-index:2）
     與裡面的 <img>，跟 .cd-enroll-rule 當初的 z-index:3 同一個理由
     （見本檔該規則的除錯記錄）；.mem-split／.mem-split-grid／
     .mem-split-media 都沒有各自的 z-index，這條線的 z-index 因此直接在
     同一個疊放環境裡跟遮罩格比大小，不需要逐層拆解是否各自建立了新的
     疊放環境。 */
.mem-split-rule {
    position: absolute;
    left: 0;
    right: 0;
    top: clamp(220px, 52vw, 340px);
    z-index: 3;
    height: 1px;
    background: hsl(var(--foreground) / .2);
    pointer-events: none;
}

@media (min-width: 992px) {
    .mem-split-rule {
        top: auto;
        bottom: 0;
    }
}

/* 表單欄：內容（wf-label／page-head／stepper／form-panel／form-foot）
   垂直置中，欄位本身不管內容形狀——跟 .page-hero-text 同一招。 */
.mem-split-panel {
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding-block: clamp(3rem, 6.5vw, 6rem);
}

/* 2026-08-19 客戶「輸入欄位跟忘記密碼跟會員登入靠左對齊」——根因：
   .form-panel 自己有 padding（色塊四邊留白，clamp(1.5rem,3vw,2.75rem)），
   欄位／勾選／忘記密碼這些內容因此比 .form-panel 的外緣（跟
   wf-label／page-head／stepper／wf-note 同一個左緣）多縮進去一截
   （1440px 實測差 43px）——標題跟表單內容左緣對不齊，是兩層不同的
   基準線疊出來的落差，不是誰寫錯了對齊屬性。
   ⚠ 修法：讓 .mem-split-panel 底下、.form-panel 以外的直接子層都吃同一
     份左右內距，退到跟 .form-panel 的內容邊界同一條線；.form-panel 本身
     不縮（色塊仍然是整欄滿寬，客戶指定的「靠色塊分段」視覺不變）。
     用 :not(.form-panel) 一次涵蓋 wf-label／page-head／stepper／
     wf-note／form-foot，不必每個 class 各寫一條，日後這裡加減子層也
     不必回來補。
   ⚠ 兩側都補（不是只補左邊）：.form-foot 是 space-between 兩端對齊
     （忘記密碼？在左、註冊新帳號在右），只縮左邊會讓右邊的連結跟
     .form-panel 內按鈕的右緣對不齊，變成新的不對稱。 */
.mem-split-panel > :not(.form-panel) {
    padding-inline: clamp(1.5rem, 3vw, 2.75rem);
}

/* 2026-08-19 客戶「登入按鈕不要拉寬，放右邊」——三頁（登入／資料填寫／
   服務聲明）的主要動作鈕原本都掛 .btn-pill--block（整行寬），那個
   modifier 是共用元件（忘記密碼三頁也在用，見 style.css .btn-pill--block
   本體定義），不能直接改掉；改成在這三個 view 檔的 markup 上拿掉
   .btn-pill--block，按鈕退回貼合文字的自然寬度，這裡只補一條讓
   .form-actions 靠右對齊——一樣用 .mem-split-panel 這層路徑限定，
   不動 .form-actions 本體（忘記密碼／完成頁的按鈕列維持原本靠左）。 */
.mem-split-panel .form-actions {
    justify-content: flex-end;
}

/* ═══ 會員登入／註冊：改成參考圖的版型（2026-08-28 客戶指定，附參考圖）═══
   客戶：「會員登入頁改成圖片的邏輯，輸入匡不要方形匡住，改底線的形式，
   將登入的右邊跟圖片一樣斜線註冊，現在註冊是放底下不太直覺，文字排版也
   以圖片方式編排，註冊頁同樣邏輯改動」。參考圖是一張左圖右表單的登入稿：
   右上標題是「Login / Sing up」斜線切換、輸入框只有一條底線沒有方框、
   「Forgot password?」貼在 PASSWORD 標籤那一行的右端。

   ⚠ 三個東西都**只限定在 .mem-split-panel 底下**（＝登入／服務聲明／
     資料填寫這三頁），不改 .form-input／.form-label 本體：這兩顆 class
     同時被搜尋（search.php）、電子報訂閱（newsletter_list.php）、忘記
     密碼兩頁、會員中心共用，客戶這輪只點名登入與註冊。
   ⚠ .form-panel 的白底卡片**沒有動**：實測它的背景 rgb(255,255,255) 跟
     頁面 .wf-frame 完全同色，本來就看不出邊界，參考圖那種「表單區沒有
     卡片」的效果現況已經成立，不需要再改一條規則去達成同一個結果。 */

/* ── ① 輸入框：方框 → 底線 ───────────────────────────────────────────
   ⚠ padding-inline 歸零（不是只拿掉邊框）：方框版有 16px 左內距，改成
     底線後如果留著，輸入文字會比上方標籤往右縮 16px——參考圖裡標籤、
     輸入文字、底線三者是切齊的。實測 .form-label 與 .form-input 的左緣
     本來就同在 x=810，只有「文字」被 padding 推開，所以歸零就對齊了。
   ⚠ 只寫 padding-inline、不寫 padding 簡寫：手機 ≤767.98px 有一條客戶
     指定的高度修正 `.page--member .form-input { padding: 3px 1rem }`
     （2026-08-20「輸入欄高度窄一點」那輪，見本檔 ≈12186）。兩條特異度
     同為 (0,2,0)，寫簡寫會把那條的垂直 3px 一起蓋掉、手機輸入框變回高的；
     只寫 padding-inline 就只接管左右，垂直維持它的值。
   ⚠ background 要明寫 transparent：.form-input 本體是 --background 淺灰
     填色，不歸零的話會變成「一塊灰底再加一條底線」，不是參考圖的樣子。 */
.mem-split-panel .form-input {
    padding-inline: 0;
    border: 0;
    border-bottom: 1px solid hsl(var(--foreground) / .18);
    border-radius: 0;
    background: transparent;
}

/* focus 時底線加深。基底的 .form-input:focus 是 (0,1,1) 且用 border-color
   簡寫，這裡 (0,2,1) 壓過它，只動底線那一邊。 */
.mem-split-panel .form-input:focus {
    border-bottom-color: hsl(var(--foreground));
}

/* ── ①-b 收掉表單卡片的上下內距 ────────────────────────────────────────
   客戶「文字排版也以圖片方式編排」。實測改成底線式之後，標題到第一個欄位
   之間隔了 115px（.page-head 的 margin-bottom 72px ＋ .form-panel 的
   padding-top 43px），參考圖那一段換算到同一個欄寬大約只有 70px——多出來
   的那 43px 是白卡片的內距，而那張卡片跟頁面同色、本來就看不見（實測兩者
   都是 rgb(255,255,255)），等於一段沒有來由的空白。
   ⚠ 只收 padding-block，**左右內距一定要留著**：.mem-split-panel 底下
     `:not(.form-panel)` 那條規則（見上面 ≈13783）是拿 .form-panel 的水平
     內距當基準，把標題／wf-label／stepper 推到同一條左緣。橫的一起歸零
     會讓表單內容整排往左跑、跟標題錯開 43px，正是那條規則當初在修的 bug。
   ⚠ 不動 .form-panel 本體：忘記密碼兩頁／註冊完成頁也用同一顆 class，
     那幾頁沒有改版。 */
.mem-split-panel .form-panel {
    padding-block: 0;
}

/* ── ② 標題：「登入 / 註冊」斜線切換 ──────────────────────────────────
   客戶「將登入的右邊跟圖片一樣斜線註冊，現在註冊是放底下不太直覺」。
   ⚠ 這是**搬移不是新增**：登入頁原本頁尾 .form-foot 的「註冊新帳號」、
     註冊兩頁原本頁尾的「已有帳號？登入」，都搬到標題這一列來，頁尾那一
     排因此空掉才移除。三頁的連結目的地一個都沒有變。
   ⚠ 掛在 <h1 class="page-title auth-switch"> 上，不是另做一顆標題：字級、
     字重、字距全部沿用 .page-title（跟其他內頁 H1 同一組值），這裡只負責
     把兩個項目排成一列並區分現在在哪一頁。
   ⚠ 未選取項用 --slate（站上既有的次要文字灰，.form-label／課程日期同一
     色），不是自己調一個新的淺色。
   ⚠ hover 走全站定案的語意：**有連結用藍**（--accent-blue-text）、純文字
     用灰（見 memory hover-color-link-vs-plaintext）。目前所在的那一項是
     <span> 不是連結，本來就不會有 hover。 */
.auth-switch {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: .35em;
}

.auth-switch-item {
    color: hsl(var(--slate));
    text-decoration: none;
    transition: color var(--transition);
}

.auth-switch-item.is-current {
    color: hsl(var(--heading));
}

a.auth-switch-item:hover,
a.auth-switch-item:focus-visible {
    color: hsl(var(--accent-blue-text));
}

.auth-switch-sep {
    color: hsl(var(--foreground) / .22);
    font-weight: var(--fw-light);
}

/* 標題右側的小字提示（註冊兩頁的「已有帳號？」）。
   ⚠ 只有註冊那兩頁有：那句文案本來就存在（原本在頁尾「已有帳號？登入」
     的前半段），搬上來才不會因為版型改動憑空消失。登入頁原本沒有對應的
     「還沒有帳號？」文案，就不自己編一句補上去——參考圖右上角雖然有
     「Don't have an account?」，但那句跟旁邊已經看得到的「註冊」是重複
     資訊，站上沒有的東西不主動加（YAGNI）。
   ⚠ 提示不寫進 <h1> 裡：它不是標題的一部分，塞進去讀屏會把整句唸成
     標題名稱（「登入 註冊 已有帳號？」）。改成 .auth-switch-row 這層
     flex 把 <h1> 與提示排成一列，兩者仍然各自維持正確的語意角色。
   ⚠ 提示本身是純文字不是連結：旁邊「登入」那一項已經是可以點的連結了，
     再包一次會變成同一列有兩個到同一頁的連結（讀屏會唸兩次）。 */
.auth-switch-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: .5rem 1rem;
}

.auth-switch-note {
    margin: 0;
    font-size: var(--text-meta);
    line-height: var(--lh-meta);
    font-weight: var(--fw-normal);
    letter-spacing: var(--tracking-meta);
    color: hsl(var(--slate));
}

/* ── ③ 標籤列：標籤在左、「忘記密碼？」在右（參考圖 PASSWORD 那一行）──
   ⚠ 這顆 class 名字 2026-08-20 的 Cal.com 那一輪出現過又被整組移除（當時
     沒有任何頁面在用），這次是真的有頁面在用才重新建立，不是把死碼撿回來。
   ⚠ align-items:baseline：兩邊字級不同（標籤 --text-meta、連結也是
     --text-meta 但字重不同），對基線才不會一高一低。
   ⚠ 不動 .form-label 自己的 margin-bottom：它撐出標籤與底線之間的距離，
     這一列直接沿用，不另外補間距。 */
.form-label-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: .75rem;
}

.form-label-link {
    font-size: var(--text-meta);
    line-height: var(--lh-meta);
    letter-spacing: var(--tracking-meta);
    color: hsl(var(--slate));
    text-decoration: none;
    border-bottom: 1px solid hsl(var(--slate));
    transition: color var(--transition), border-color var(--transition);
}

.form-label-link:hover,
.form-label-link:focus-visible {
    color: hsl(var(--accent-blue-text));
    border-bottom-color: hsl(var(--accent-blue-text));
}

@media (max-width: 1023.98px) {
    .mem-split-grid {
        grid-template-columns: minmax(0, 1fr);
        gap: 0;
    }

    /* 窄幅圖片翻到表單上方，並左右都出血成一整條矩形 */
    .mem-split-media {
        order: -1;
        margin-inline: calc(var(--section-x) * -1);
        min-height: clamp(220px, 52vw, 340px);
    }

    .mem-split-panel { padding-block: clamp(2.25rem, 7vw, 3rem); }
}

/* ═══ 19-2 色帶：背景壓灰／膚色／藍，不要整頁全白 ═══════════════════════ */
.band {
    padding-block: var(--v2-band-y);
}

.band--grey  { background: hsl(var(--surface-grey)); }   /* #DCDCE1 淺灰 */
.band--nude  { background: hsl(var(--nude)); }           /* #F2E3D0 膚色 */
.band--blue  { background: hsl(var(--accent-blue)); }    /* #B0C7DD 藍 */
.band--white { background: hsl(var(--muted)); }          /* 純白 */
.band--dark  {
    background: hsl(var(--surface-dark));
    color: hsl(var(--surface-dark-foreground));
}

/* ── 色帶的區塊頭：裝飾英文 + 標題 + 導言 + 右上按鈕（Coverly 的區塊頭）── */
.band-head {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    justify-content: space-between;
    gap: 1.5rem clamp(2rem, 4vw, 4rem);
    margin-bottom: clamp(2.25rem, 4.5vw, 3.5rem);
}

.band-head-text { flex: 1 1 22rem; }

/* ⚠ 2026-08-13 客戶「首頁的標題跟英文字底下的線有留一段距離，請其他的也照首頁的改」——
   .band-en 這裡的線同樣是長在自己 box 內的 ::after（第 33 節），量出來的間距就等於
   這個 margin-bottom；首頁 .section-title 那條線是另一層 sibling，間距多了線本身的
   margin-top(20px)＋線高(1px)，兩邊要對齊只能直接把這裡的 margin-bottom 拉滿到跟
   首頁一樣的 39px（原 1rem＝16px → 新 2.4375rem＝39px）。影響服務內頁 Overview 色帶、
   實體課程／知識專區列表頁的 Hero 小標，皆是套用這條 base 規則、沒有頁面專屬覆蓋值。 */
.band-en {
    margin: 0 0 2.4375rem;
    font-family: var(--font-eyebrow);
    font-size: var(--text-meta);
    line-height: var(--lh-meta);
    font-weight: var(--fw-normal);
    letter-spacing: var(--tracking-meta);
    color: hsl(var(--slate));
}

.band--dark .band-en { color: hsl(var(--surface-dark-foreground) / .7); }

.band-title {
    margin: 0;
    max-width: 22em;
    font-size: var(--text-h2);
    line-height: 1.5;
    font-weight: var(--fw-heading);
    letter-spacing: var(--tracking-title);
    color: hsl(var(--heading));
}

.band--dark .band-title { color: hsl(var(--surface-dark-foreground)); }

.band-lead {
    margin: 1.25rem 0 0;
    max-width: 42em;
    font-size: var(--text-body);
    line-height: var(--lh-body);
    font-weight: var(--fw-light);
    letter-spacing: var(--tracking);
    color: hsl(var(--muted-foreground));
}

.band--dark .band-lead { color: hsl(var(--surface-dark-foreground) / .8); }

/* 色帶內的一般段落 */
.band-text {
    margin: 0;
    max-width: 44em;
    font-size: var(--text-body);
    line-height: var(--lh-body);
    font-weight: var(--fw-light);
    letter-spacing: var(--tracking);
    color: hsl(var(--muted-foreground));
}

.band-text + .band-text { margin-top: var(--v2-p-gap); }

.band--dark .band-text { color: hsl(var(--surface-dark-foreground) / .8); }

/* ═══ 19-3 色塊馬賽克（Coverly）══════════════════════════════════════════
   圖片格與純色文字格拼成一整片矩形：格與格之間留 1px 的縫，靠底色透出來
   當分隔（不畫線 —— 客戶既有指定「把線條感拿掉，以色塊做區隔」）。
   ⚠ 用 gap: 1px + 容器底色，而不是 border：border 會讓相鄰兩格之間變成 2px。 */
.tile-grid {
    display: grid;
    grid-template-columns: repeat(var(--tile-cols, 3), minmax(0, 1fr));
    gap: 1px;
    background: hsl(var(--foreground) / .12);
    margin: 0;
    padding: 0;
    list-style: none;
}

.tile-grid--2 { --tile-cols: 2; }

.tile {
    display: flex;
    flex-direction: column;
    min-height: clamp(230px, 24vw, 300px);
    padding: clamp(1.5rem, 2.4vw, 2.25rem);
    background: hsl(var(--muted));
    text-decoration: none;
    color: inherit;
}

/* 四種底色交錯用（客戶：多運用膚色藍色灰色） */
.tile--nude { background: hsl(var(--nude)); }
.tile--blue { background: hsl(var(--accent-blue)); }
.tile--grey { background: hsl(var(--surface-grey)); }
.tile--deep { background: hsl(var(--surface-grey-deep)); }

/* 圖片格：整格就是一張矩形圖，沒有內距。
   ⚠ 圖片用絕對定位鋪滿格子，**不讓它決定列高**：
     如果只寫 height:100%，在高度未定的 grid 列裡會退回 auto（＝原圖比例），
     一張 4:3 的素材在半版寬會把整列撐到 650px 高，旁邊的文字格就多出
     一大塊空白（實測就是這樣）。列高改由文字格決定，圖片跟著填滿。 */
.tile--image {
    position: relative;
    padding: 0;
    overflow: hidden;
    background: hsl(var(--foreground) / .05);
}

.tile--image picture,
.tile--image img {
    position: absolute;
    inset: 0;
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    transition: transform var(--dur-media) var(--ease-out);
}

.tile-no {
    display: block;
    margin: 0 0 auto;
    font-family: var(--font-en);
    font-size: var(--text-h2);
    line-height: 1;
    letter-spacing: 0;
    font-weight: var(--fw-light);
    color: hsl(var(--foreground) / .34);
}

.tile-title {
    margin: 1.75rem 0 0;
    font-size: var(--fs-3);
    line-height: 1.7;
    font-weight: var(--fw-normal);
    letter-spacing: var(--tracking-title);
    color: hsl(var(--heading));
}

.tile-desc {
    margin: .875rem 0 0;
    font-size: var(--text-body);
    line-height: var(--lh-body);
    font-weight: var(--fw-light);
    letter-spacing: var(--tracking);
    color: hsl(var(--foreground) / .68);
}

/* 格子底部的文字連結（參考稿的 LEARN MORE）*/
.tile-link {
    margin-top: 1.5rem;
    align-self: flex-start;
    font-size: var(--text-meta);
    line-height: 1.6;
    letter-spacing: var(--tracking-meta);
    color: hsl(var(--heading));
    text-decoration: none;
    border-bottom: 1px solid hsl(var(--foreground) / .45);
    padding-bottom: .2rem;
    transition: border-color var(--transition);
}

a.tile:hover .tile-link,
.tile-link:hover,
.tile-link:focus-visible { border-bottom-color: hsl(var(--foreground)); }

@media (hover: hover) {
    a.tile--image:hover img { transform: scale(var(--hover-zoom)); }
}

@media (max-width: 1023.98px) {
    .tile-grid { --tile-cols: 2; }
    /* 窄幅的圖片格容易被壓成細長條，給它一個明確比例 */
    .tile--image { min-height: 0; aspect-ratio: 4 / 3; }

    /* ⚠ 2026-08-13 全站響應式稽核（RWD workflow audit）抓到：768px 這個
       斷點下，「什麼時候適合安排內訓／導入 AI 分身／…」這一組固定 3 個
       tile（01/02/03，見 service_page.php 的 $copy['signals']['tiles']，
       每個服務單元都恰好 3 筆）在 2 欄格線裡排完會剩一格——03 落單在
       第二列，右邊那一格沒有第 4 個 tile 填，露出色帶自己的灰底
       （.tile 本身是白底方塊，空格處看起來像少了一張卡片，不是刻意的
       設計留白）。
       ⚠ 用 :has() 精準鎖定「剛好 3 個 tile」的清單（第 3 個子元素同時是
       最後一個），不是無差別把所有 .tile-grid 的第 3 項都撐寬——未來若
       某個服務單元的訊號變成 4 個或更多，這個選擇器自動不生效，不用
       回來改。撐寬用 grid-column:1/-1，讓落單的第 3 張卡片撐滿整列，
       視覺上像「這一列本來就只有一張較寬的卡片」，不是缺一張。 */
    .tile-grid:has(> .tile:nth-child(3):last-child) > .tile:nth-child(3) {
        grid-column: 1 / -1;
    }
}

@media (max-width: 575.98px) {
    .tile-grid, .tile-grid--2 { --tile-cols: 1; }
}

/* ═══ 19-4 編輯式兩欄列（Elevate）════════════════════════════════════════
   左欄一個帶小圓點的標籤，右欄是內容；列與列之間一條極淡的線。
   ⚠ 這裡保留細線是因為它是**列的節奏**（表格式資訊），不是「區塊分段」——
     客戶要拿掉的是後者，兩者角色不同。線色用既有的 --rule-on-light。 */
.editorial {
    display: grid;
    gap: 0;
}

.editorial-row {
    display: grid;
    grid-template-columns: minmax(0, 15rem) minmax(0, 1fr);
    gap: clamp(1rem, 3vw, 3rem);
    padding-block: clamp(1.5rem, 3vw, 2.25rem);
    border-top: 1px solid hsl(var(--foreground) / .14);
}

.editorial-row:last-child { border-bottom: 1px solid hsl(var(--foreground) / .14); }

.editorial-label {
    display: flex;
    align-items: baseline;
    gap: .625rem;
    margin: 0;
    font-size: var(--text-meta);
    line-height: 1.9;
    font-weight: var(--fw-normal);
    letter-spacing: var(--tracking-meta);
    color: hsl(var(--slate));
}

.editorial-label::before {
    content: "";
    flex: 0 0 auto;
    width: 5px;
    height: 5px;
    border-radius: 50%;
    background: hsl(var(--accent-blue));
    transform: translateY(-.2em);
}

.editorial-body > * { margin: 0; }
.editorial-body > * + * { margin-top: var(--v2-p-gap); }

.editorial-body p {
    font-size: var(--text-body);
    line-height: var(--lh-body);
    font-weight: var(--fw-light);
    letter-spacing: var(--tracking);
    color: hsl(var(--muted-foreground));
}

.editorial-body h3 {
    font-size: var(--fs-3);
    line-height: 1.7;
    font-weight: var(--fw-normal);
    letter-spacing: var(--tracking-title);
    color: hsl(var(--heading));
}

.band--grey .editorial-body p,
.band--nude .editorial-body p { color: hsl(var(--foreground) / .72); }

@media (max-width: 767.98px) {
    .editorial-row { grid-template-columns: minmax(0, 1fr); gap: .875rem; }
}

/* ═══ 19-5 步驟（大編號）════════════════════════════════════════════════ */
.steps {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: clamp(1.5rem, 3vw, 2.5rem);
    margin: 0;
    padding: 0;
    list-style: none;
    counter-reset: step;
}

.step { counter-increment: step; }

.step::before {
    content: "0" counter(step);
    display: block;
    margin-bottom: 1.125rem;
    font-family: var(--font-en);
    font-size: var(--text-h2);
    line-height: 1;
    letter-spacing: 0;
    font-weight: var(--fw-light);
    color: hsl(var(--foreground) / .3);
}

.step-title {
    margin: 0;
    font-size: var(--fs-3);
    line-height: 1.7;
    font-weight: var(--fw-normal);
    letter-spacing: var(--tracking-title);
    color: hsl(var(--heading));
}

.step-desc {
    margin: .75rem 0 0;
    font-size: var(--text-body);
    line-height: var(--lh-body);
    font-weight: var(--fw-light);
    letter-spacing: var(--tracking);
    color: hsl(var(--foreground) / .7);
}

@media (max-width: 767.98px) {
    .steps { grid-template-columns: minmax(0, 1fr); }
}

/* ═══ 19-6 常見問題：兩欄 ════════════════════════════════════════════════ */
.faq-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: clamp(1.75rem, 3vw, 2.75rem) clamp(2rem, 4vw, 4rem);
    margin: 0;
    padding: 0;
    list-style: none;
}

.faq-q {
    margin: 0;
    font-size: var(--fs-3);
    line-height: 1.7;
    font-weight: var(--fw-normal);
    letter-spacing: var(--tracking-title);
    color: hsl(var(--heading));
}

.faq-a {
    margin: .75rem 0 0;
    font-size: var(--text-body);
    line-height: var(--lh-body);
    font-weight: var(--fw-light);
    letter-spacing: var(--tracking);
    color: hsl(var(--foreground) / .7);
}

@media (max-width: 767.98px) {
    .faq-grid { grid-template-columns: minmax(0, 1fr); }
}

/* ═══ 19-7 結尾行動帶 ════════════════════════════════════════════════════ */
.cta-band-inner {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 1.75rem clamp(2rem, 4vw, 4rem);
}

.cta-band-text { flex: 1 1 24rem; }

/* ═══ 19-8 寬幅矩形圖（單獨一條，做為段落之間的呼吸）═══════════════════ */
.band-media {
    margin: 0;
    overflow: hidden;
    background: hsl(var(--foreground) / .05);
}

.band-media picture,
.band-media img {
    display: block;
    width: 100%;
    height: clamp(220px, 30vw, 420px);
    object-fit: cover;
}

/* ── 19-9 色帶內部的區塊間距（同一條色帶裡放兩組內容時用）───────────── */
.band-gap { margin-top: clamp(2rem, 4vw, 3.25rem); }

/* 深色帶上的編輯式列：線與文字一起翻面 */
.band--dark .editorial-row { border-color: hsl(var(--surface-dark-foreground) / .22); }
.band--dark .editorial-row:last-child { border-bottom-color: hsl(var(--surface-dark-foreground) / .22); }
.band--dark .editorial-label { color: hsl(var(--surface-dark-foreground) / .72); }
.band--dark .editorial-body p { color: hsl(var(--surface-dark-foreground) / .8); }
.band--dark .editorial-body h3 { color: hsl(var(--surface-dark-foreground)); }

/* 膚色／灰色帶上的細線要比白底再深一點才看得見 */
.band--nude .editorial-row,
.band--grey .editorial-row { border-color: hsl(var(--foreground) / .18); }
.band--nude .editorial-row:last-child,
.band--grey .editorial-row:last-child { border-bottom-color: hsl(var(--foreground) / .18); }

/* 色帶上的頁尾動作列：沿用 .entry-foot，只把上緣的線改成跟著底色走 */
.band .entry-foot { border-top-color: hsl(var(--foreground) / .16); }

/* ── 19-10 行長上限（2026-08-05 回測）──────────────────────────────────
   ⚠ 色帶是滿版的，.section-bleed-inner 在 1920 會給到 1728px 寬。
     Hero 的大圖需要那個寬度，但**文字不行**：14px 排在 1100px 的欄裡
     一行超過 70 個中文字，眼睛回行時會跳行。
   ⚠ 上限用 em（跟著字級走），不是 px —— 字級切到 A+／A++ 時行長要一起長，
     否則大字反而每行塞更少字。52em ＝ 14px 時約 728px、約 45 個中文字，
     與首頁內文的行長節奏同一階。 */
.editorial-body,
.step-desc,
.faq-a,
.cta-band-text .band-lead {
    max-width: 52em;
}

/* 兩欄的 FAQ 與步驟本來就窄，只有單欄時才會超過上限 */
@media (max-width: 767.98px) {
    .editorial-body,
    .step-desc,
    .faq-a { max-width: none; }
}

/* ═══════════════════════════════════════════════════════════════════════
   20. 顧問案 v2 第二輪調整（2026-08-05，客戶附色票與三張實機截圖）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「以壓〔#DCDDE0〕這個明度的灰為主，不要壓膚色跟深灰色，內容縮短一點；
          內文固定版型以〔左小標／右內容的兩欄〕為主；服務專區的頁面導覽過去
          固定有大圖這區的呈現，左上的首頁/服務專區/顧問案文字蓋在灰底上；
          顧問案標題底下的三個分類刪掉，不要幫她做分類；
          內頁的底色固定以淺灰色和白色為變化；
          footer 固定讓他上方有細線跟內容隔開。」

   ⚠ 色票確認過：客戶給的灰就是站上既有的 --surface-grey
     （hsl(225 6% 87%) ＝ #DCDDE0，逐字計算過，不是目測近似），
     所以**沒有新增任何顏色**，只是把用色收斂成「淺灰 ＋ 白」兩種。
   ⚠ 膚色（.band--nude）與深灰（.band--dark）的規則**留著不刪** ——
     它們是第 19 節的通用元件，其他內頁日後可能用得到；
     顧問案這一頁只是不再使用它們（標記裡沒有那兩個 class）。
   ⚠ 一樣全部掛在 .page--v2 之下，首頁與其餘 17 個內頁不受影響。
   ═══════════════════════════════════════════════════════════════════════ */

/* ── ① 麵包屑排進 Hero，與標題同一列（一左一右）─────────────────────
   客戶：「左上的首頁/服務專區/顧問案文字蓋在灰底上」
        「顧問案標題放首頁/服務專區小字的右邊，兩個在同一水平但一左一右」
        「大圖直接貼邊上半部不要留空間」

   ⚠ 三件事是同一個結構問題：大圖要貼到色帶最上緣，就不能讓 layout 的麵包屑
     佔掉上面那一列。所以麵包屑改由 controller 傳給 view，排在 Hero 左欄的
     第一列（左小字／右大標），layout 那一份給空陣列不輸出（見 Wireframe::serviceItem）。
   ⚠ 一左一右用 space-between、基線對齊用 align-items: baseline ——
     小字與大標的**文字底線**要在同一條線上，用 center 對齊盒子會讓小字偏高。 */
.page-hero-topline {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: .75rem clamp(1.5rem, 3vw, 3rem);
}

/* ⚠ 2026-08-13 客戶「這段文字刪掉，其他分頁的也一起刪掉」（附
   /wireframe/courses 的「首頁 / 實體課程」截圖）——全站麵包屑不分兩套
   機制（layout 層的 .wf-crumb／本頁 Hero 內嵌的 .page-hero-crumb），
   一起隱藏。
   ⚠ 用 display:none 不刪標記／PHP 邏輯：跟本檔前面幾次「客戶要求拿掉
     視覺元素」同一個做法（例如 logo 品牌名那次），符合「禁止刪除任何
     內容」的鐵律，也讓日後要復原只要拿掉這條規則即可，不必回頭補標記。
   ⚠ .wf-crumb 只在 controller 有傳 $wfBreadcrumb 的頁面才會渲染
     （knowledge/detail、newsletter/detail、member 系列……）；
     .page-hero-crumb 內嵌在 course_detail.php／courses_list.php／
     service_page.php／service_page_simpla.php 四支 view 自己的 Hero
     標記裡。首頁與知識專區列表頁本來就沒有麵包屑，不受影響。 */
.wf-crumb,
.page-hero-crumb {
    display: none;
}

/* ═══════════════════════════════════════════════════════════════════════
   麵包屑復出：header 底線 + 左上角文字（2026-08-25 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「在每個文章頁面最上放header底下加一條細灰色水平線，線要是
   整個頁面寬的長度，然後在線底下加上...文字內容...加在左上角，靠左
   跟header的logo對齊」，附截圖範例「首頁 / 知識專區 / 文章標題」。

   ⚠ 不是復原 2026-08-13 被隱藏的舊版：舊版沒有這條線、位置也不一定
     對齊 logo。這裡只把 .wf-crumb（layout 層那一套，資料來源
     $wfBreadcrumb）帶回來，.page-hero-crumb（內嵌在各頁 Hero 裡的
     舊機制）維持隱藏——上面 13977 那條規則一行都沒動，這裡疊一條
     多一個 class（.wf-frame .wf-crumb，0,2,0）穩贏，不用 !important。
   ⚠ 只讓 .wf-crumb 出現，不動 .page-hero-crumb：客戶這次要的是「線＋
     左上角文字」這一種新版型，courses_list.php／course_detail.php／
     service_page.php／service_page_simpla.php 四支 view 內嵌的舊
     麵包屑標記維持隱藏、不重複顯示兩份。
   ⚠ 「靠左跟header的logo對齊」：.wf-crumb 本身已經有全站既有的
     max-width/margin-inline:auto/padding-inline:var(--section-x)
     置中公式（本檔 ≈2880），跟 header 內距用的是同一顆 --section-x
     token，天生左緣對齊，不需要另外量測对齐。
   ⚠ 「線」第一版另外加了一顆 .wf-crumb-rule 手足 div，客戶回饋「是在
     header加底線不是加白色背景」——多一顆 div 等於多一段留白，視覺上
     變成 header 下面多一條白色橫幅，不是單純的底線。改成不新增任何
     markup（layouts/main.php 那段已經改回單純 .wf-crumb，沒有多的
     手足元素），直接讓 .site-header 自己既有的 border-bottom 機制
     顯示灰色細線——header 本身就有 1px 的 border-bottom（本檔 ≈3426
     起、依 .is-compact／.is-scrolled／有無 Hero 等狀態切換透明或
     --brand-accent 棕褐色，從沒有「純灰色」這個狀態），這裡用
     `body:has(.wf-crumb)` 判斷「這頁有麵包屑」，額外疊一版灰色、
     強制顯示（!important，跟本檔這一整組 header 狀態規則同一個做法，
     蓋掉其餘依捲動/選單狀態切換的顏色）。線就是 header 的下緣本身，
     整個頁面寬（header 是 fixed 滿版，border 天生就是滿版寬），
     .wf-crumb 文字緊接著往下排，不會多出一段獨立的白色色塊。
   ⚠ 顏色沿用 .entry-foot 既有的細線色值（hsl(var(--foreground)/.1)），
     不新增色票。 */
.wf-frame .wf-crumb {
    display: block;
}

/* ⚠ header 讓位補償：.wf-frame 的 padding-top 讀 --header-h，但那顆 token
   跟真實 header 高度對不上（見該 token 定義處與 --header-real-h 的說明），
   所以麵包屑要自己補上差額。

   ⚠ 2026-08-25 改成 `真實高度 − token`，取代原本的
     `var(--header-clearance-extra, 0px)`。原因是舊寫法**只能單向修正**
     （site.js 那個值被 Math.max(0,…) 夾住），兩個方向各有一個客戶回報的
     災情：
       ① token 比真實**小**（≥992 的一般頁：56 vs 73）——讓位量 100% 靠
          JS，變數沒進來（JS 還沒跑完／site.js 快取到舊版／JS 停用）時
          麵包屑文字落在 y=72、header 底線 y=73，**壓在線上**。
       ② token 比真實**大**（course_detail ≥1468：191 vs 73）——夾完是 0，
          .wf-frame 照樣把內容推下 191px，麵包屑離線 135px。
     改成不夾負值的減法之後，上方總空間收斂成
       .wf-frame 的 --header-h ＋ (real − --header-h) ＝ real
     跟 token 是 56 還是 191 完全無關，兩個災情一起消失。

   ⚠ 為什麼不用寫 @media 排除手機：≤991px 時 header 是 sticky、真實高度
     就等於 token（都是 56），相減天然是 0，本來就不需要讓位。 */
.wf-crumb {
    margin-top: calc(var(--header-real-h) - var(--header-h));
}

/* ⚠ 2026-08-25 客戶「首頁這一段文字請放在線底下」附截圖：手機寬度
   （≤991.98px）完全沒看到灰線，麵包屑文字直接貼在 header 下面。查出
   上一版這條線只包在 @media(min-width:992px) 裡——當時是照抄旁邊
   「header讓位補償」那條真的只在桌機（fixed header）才需要處理的
   註解脈絡，順手把線也一起關進同一個斷點，沒有另外確認手機是否需要。
   手機 header 是 position:sticky（不是 fixed），沒有讓位補償的問題，
   但「灰線」本身是全寬度都要的（客戶原話「每個文章頁面」沒有限定
   桌機），這裡拿掉斷點限制，全寬度一律顯示。 */
body:has(.wf-crumb) .site-header {
    border-bottom: 1px solid hsl(var(--foreground) / .1) !important;
}

/* ⚠ 2026-08-26 客戶「header滾動前不要加底線」——停在頁首時不要這條線，
   捲動之後才出現。
   ⚠ 用 border-bottom-**color**: transparent，不是 border-bottom: 0——
     線要「常駐、只換顏色」，這樣切換時是跟著既有的
     `transition: background, border-color`（≈3483）淡入淡出，不是瞬間
     跳出來；也因為 1px 的盒模型高度一直都在，header 高度兩態都維持
     72.59px，不會在捲動瞬間抖一下，更不會牽動 --header-real-h 與所有
     讀它來讓位的算式（麵包屑、Hero 圖片貼齊）。
   ⚠ 特異度：上一條是 (0,2,1)＋!important（body＋:has(.wf-crumb)＋
     .site-header），這裡多一個 `:not(.is-scrolled)` 湊到 (0,3,1) 才蓋得
     過去。`.is-scrolled` 掛在 .site-header 元素自己（home.js ≈369，門檻
     scrollY>14），不是掛在 `<html>`，層級是查 JS 確認過的。
   ⚠ 2026-08-27：捲動灰底被客戶上午撤回的那段期間，這裡的第二個選擇器
     `html:not(.is-compact) body:has(.wf-crumb) .site-header` 曾一併移除
     （配對的灰底不存在了，留著只會讓手機捲過頁首後遲遲不出現底線）。
     同日客戶再指定「滾動也有背景」，灰底加回來（見 ≈8705 那條
     `html.is-compact .site-header.is-scrolled`），這個配對選擇器跟著
     恢復——沒有它，手機版在「已捲動但還沒 is-compact」的區間（<992 的
     `.is-compact` 由 IntersectionObserver 判斷，要捲過頁首色帶才成立）
     會變成**有線卻沒有灰底**：一條 hairline 孤零零浮在頁首大圖上，比
     兩個都沒有還醜。兩個選擇器合起來讓「底線＋灰底」永遠同進同出。 */
body:has(.wf-crumb) .site-header:not(.is-scrolled),
html:not(.is-compact) body:has(.wf-crumb) .site-header {
    border-bottom-color: transparent !important;
}

/* ⚠ 2026-08-27（傍晚）客戶「在平板寬度的時候 header 的線跑出來了，請隱藏」
   ——用選項題確認過範圍：**全部寬度都不要這條線**，灰底保留。
   ⚠ 客戶原話說「平板」，但實測這條線從來不是平板獨有：它的出現條件是
     「有麵包屑的頁面 ＋ 寬度≥992（is-compact）＋ 已捲動（is-scrolled）」，
     桌機 1440／1920 完全一樣有（實測 border-bottom-color 都是
     rgba(26,26,26,.1)）。之所以客戶在平板才注意到，推測是平板直式
     （768/834）沒有 is-compact、看不到線，轉橫式（1024）就冒出來，
     同一台裝置前後不一致最顯眼。所以這裡**不做斷點限定**，直接讓上面
     那條 08-25 的底線在所有情況都不畫——限定平板反而會製造
     「平板沒有、桌機有」的新不一致。
   ⚠ 只關線、不動灰底：捲動後的灰底是客戶同日上午才確認要的
     （見 ≈8705 `html.is-compact .site-header.is-scrolled`），
     那條一個字都沒動。
   ⚠ 沿用上面那組的手法：改 border-bottom-**color** 而不是 border-bottom:0
     ——1px 的盒模型高度要繼續存在，header 高度才不會在捲動瞬間抖一下，
     也不會牽動 --header-real-h 與所有讀它讓位的算式（麵包屑、Hero 圖片
     貼齊）。這點在上一條的註解已經記過，這裡照辦。
   ⚠ 特異度：上面那條 08-25 的線是 (0,2,1)＋!important，`!important` 互打
     是比特異度不是比順序（見
     [[reference-onedaylisten-important-specificity-tiebreak]]）。這裡多掛
     一個 `.is-designed`（跟 body 同一個元素上的 class，不是後代）湊到
     (0,3,1) 才蓋得過去。 */
body:has(.wf-crumb).is-designed .site-header {
    border-bottom-color: transparent !important;
}

/* 2026-08-13 客戶「這種 首頁/知識專區的全改在統一高度統一位置」——
   麵包屑不管走哪一套機制（layout 層的 .wf-crumb，或本頁 Hero 內嵌的
   .page-hero-crumb）字要長得一樣。實測 .is-designed .wf-crumb 有明寫
   font-weight: var(--fw-light)，這裡卻沒設，掉回瀏覽器預設的 400。
   ⚠ 這裡**不能**照抄 var(--fw-light)：.wf-crumb 是 layouts/main.php
     裡跟 <main> 同層的兄弟元素，長在 .page 外面，吃到的是 :root 那份
     --fw-light（100，字重軸最細端）；.page-hero-crumb 全部長在
     .page 裡面，本檔 11968 的 .page { --fw-light: 350; } 早把 token
     在這個範圍內重新定義成給內文用的「一般字重」，同一個 var() 在這裡
     解出來是 350，不是 100（實測驗證過，不是紙上談兵）。要跟 .wf-crumb
     視覺上真的一樣細，只能繞過 token 直接寫死 100 —— 只影響麵包屑這
     一個元件，.page 範圍內其他吃 --fw-light 的內文完全不受影響。
   字級/行高/字距/顏色四項本來就共用同一批 token（--text-meta 等）
   所以早就一致，唯獨粗細沒對齊，這裡補上，兩套機制字重統一。 */
.page-hero-crumb {
    margin: 0;
    font-size: var(--text-meta);
    line-height: var(--lh-meta);
    font-weight: 100;
    letter-spacing: var(--tracking-meta);
    color: hsl(var(--slate));
}

.page-hero-crumb a {
    color: inherit;
    text-decoration: none;
    transition: color var(--transition);
}

.page-hero-crumb a:hover,
.page-hero-crumb a:focus-visible { color: hsl(var(--heading)); }

/* 左欄：第一列貼上緣、其餘內容沉到下緣，中間讓出大留白（與右側大圖等高） */
/* 2026-08-13 客戶「這種 首頁/知識專區的全改在統一高度統一位置」——
   同一輪麵包屑統一：這裡的上內距（clamp(1.25rem, 2.5vw, 2rem)，桌機
   32px）是麵包屑距離 Header 的唯一控制點，跟 layout 層 .wf-crumb 的
   clamp(1.5rem, 3vw, 2.5rem)（桌機 40px）對不上——改成同一顆 clamp，
   套用範圍是企業內訓／教練式領導／線上課程／公眾演說四頁（走
   service_page.php 內聯版型的都吃得到）。下內距 clamp(2.5rem, 5vw,
   4rem) 是麵包屑之後、標題之前的留白，跟 Header 距離無關，不動。 */
.page--v2 .page-hero-text {
    justify-content: space-between;
    padding-block: clamp(1.5rem, 3vw, 2.5rem) clamp(2.5rem, 5vw, 4rem);
}

/* ── 下緣那一列：左＝副標＋CTA，右＝英文小標＋服務名（2026-08-05 客戶指定）──
   客戶：「顧問案標題跟小字英文字不要分開，然後位置改放在右下角。」
   ⚠ 英文小標與標題重新併成同一組（.page-hero-heading），順序沿用站上
     既有的 .section-title：英文在上、中文在下，只是整組改成靠右並沉到底。
   ⚠ align-items: flex-end —— 左右兩塊要**底部**切齊，不是頂部；
     副標行數多寡不同，靠頂對齊會讓標題浮在半空中。 */
.page-hero-body {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    justify-content: space-between;
    gap: 1.75rem clamp(1.5rem, 3vw, 2.5rem);
    margin-top: clamp(2.5rem, 6vw, 5rem);
}

.page-hero-intro { flex: 1 1 17rem; }

.page-hero-heading {
    flex: 0 1 auto;
    text-align: right;
}

/* 小標整行靠右 */
/* ⚠ 2026-08-13 客戶「首頁的標題跟英文字底下的線有留一段距離，請其他的也照首頁的改」——
   服務內頁 Hero（.page-hero-heading .page-hero-en）原本這條覆蓋值只有 .625rem(10px)，
   比首頁的 39px 窄很多；線是長在自己 box 內的 ::after，量出來的間距＝這個
   margin-bottom 本身，直接拉到跟首頁一致的 39px（原 .625rem＝10px → 新 2.4375rem
   ＝39px），不動線、不動 .page-hero-title。 */
.page-hero-heading .page-hero-en {
    margin-bottom: 2.4375rem;
}

/* ⚠ 2026-08-13 圓點換成線之後補的：文字本身靠 text-align:right（繼承自
   .page-hero-heading）自然靠右，但線是 display:block 的 ::after，不受
   text-align 影響，要另外用 margin-inline-start:auto 把它推到右邊界，
   否則會貼著左邊、跟右對齊的文字對不齊。 */
.page-hero-heading .page-hero-en::after {
    margin-inline-start: auto;
}

@media (max-width: 1023.98px) {
    /* 窄幅圖片排在最上面；標題組回到靠左，與其餘內容同一條左邊界 ——
       窄欄裡右對齊的大標會與左對齊的副標形成兩條邊，讀起來是散的。 */
    .page-hero-topline { gap: .5rem; }
    .page-hero-body { margin-top: 1.75rem; }
    .page-hero-heading { text-align: left; }
    /* 線跟著文字一起收回左邊界，不再靠右頂格 */
    .page-hero-heading .page-hero-en::after { margin-inline-start: 0; }
}

/* ── ② Footer 上方補一條細線與內容隔開 ────────────────────────────────
   客戶：「footer 固定讓他上方有細線跟內容隔開。」
   ⚠ 這條線 2026-07-30 曾依客戶指定移除（見本檔 --section-seam-end 的註解），
     這一輪客戶改要它回來 —— 以這次為準。
   ⚠ 先只作用在 v2 的頁面（目前＝顧問案），與「先調整顧問案頁面」一致；
     要全站生效把 `body:has(.page--v2)` 這段前綴拿掉即可。
   ⚠ 線色用站上既有的 --rule-on-light 同一階（foreground 14%），不另調一個灰。 */
body:has(.page--v2) .site-footer {
    border-top: 1px solid hsl(var(--foreground) / .14);
}

/* ── ③ 底色只在「淺灰 ↔ 白」之間交替 ─────────────────────────────────
   客戶：「內頁的底色固定以淺灰色和白色為變化。」
   ⚠ 第 19 節的 .band 預設是**不設底色**（吃頁面底 #F6F6F7），那會變成第三種灰。
     這裡把 v2 的預設補成白，標記上再用 .band--grey 交替 ——
     這樣每一條色帶都是明確的白或淺灰，不會有「看起來像沒對齊的第三種灰」。 */
.page--v2 .band { background: hsl(var(--muted)); }
.page--v2 .band--grey { background: hsl(var(--surface-grey)); }

/* 灰帶上的色塊格與細線：格子用白，線要比白底上再深一點才看得見 */
.page--v2 .band--grey .tile { background: hsl(var(--muted)); }
.page--v2 .band--grey .tile-grid { background: hsl(var(--foreground) / .16); }

/* ── ④ 內容縮短之後的節奏微調 ─────────────────────────────────────────
   段落變短、區塊變少，色帶的上下留白可以再收一階，整頁才不會變成
   「一堆短內容配超大留白」。 */
.page--v2 { --v2-band-y: clamp(3rem, 5.5vw, 4.75rem); }

/* ── ⑤ 頁尾動作列：不要重複留白（2026-08-05 回測）─────────────────────
   .entry-foot 自己帶了 margin-top（它原本是接在單欄文章正下方的）。
   在 v2 裡它住在一條有 --v2-band-y 上下內距的色帶裡，兩份留白疊起來
   會在按鈕上方空出將近 200px。色帶已經負責呼吸，這裡收掉自己那一份。 */
.page--v2 .entry-foot {
    margin-top: 0;
}

/* ── ⑥ 第三輪調整（2026-08-05 客戶指定）──────────────────────────────
   「什麼時候適合啟動顧問案該區域滑鼠 hover 方塊，讓他三個矩形背景色
     個別變膚色跟藍色」
     ⚠ 三格交錯：01 膚色、02 藍、03 膚色。用 nth-child 的奇偶，
       格數日後增減也不必回來改。
     ⚠ 只在 (hover: hover) 生效：觸控裝置的 :hover 會「黏住」，
       點過的那一格會一直留著顏色（站上其他區塊踩過同一個坑）。
     ⚠ 格子本身不是連結，這是純視覺回饋；文字顏色不動，
       黑字壓膚色 16.8:1、壓藍 10.5:1，兩色都讀得到。 */
.page--v2 .tile-grid > .tile {
    transition: background var(--transition);
}

@media (hover: hover) {
    .page--v2 .tile-grid > .tile:nth-child(odd):hover  { background: hsl(var(--nude)); }
    .page--v2 .tile-grid > .tile:nth-child(even):hover { background: hsl(var(--accent-blue)); }
}

/* 「返回／收藏／分享」那一列上方不要細線（客戶指定）。
   ⚠ 只拿掉線，留白仍由色帶自己的 padding 負責，位置不變。 */
.page--v2 .entry-foot {
    border-top: 0;
    padding-top: 0;
}

/* ═══════════════════════════════════════════════════════════════════════
   21. 服務內頁 v2：進站動態（2026-08-05 客戶指定「點進來加上動態效果」）
   ═══════════════════════════════════════════════════════════════════════
   分兩段，各自負責不同的時機：

     ① 首屏 Hero      進站當下就播，純 CSS animation，不依賴 JS
     ② 底下的色帶     捲到才播，沿用站上既有的 .js-reveal 機制
                      （選擇器加在 home.js 的 groups，含 2 秒失效保險）

   ⚠ 為什麼 Hero 不走 .js-reveal：那一組的規則是「首屏可見的元素不參與進場」
     （避免 JS 失效時首屏空白）。Hero 永遠在首屏，所以交給 CSS animation ——
     animation 由瀏覽器直接播，沒有「JS 沒跑就一片空白」的風險。
   ⚠ 整段包在 prefers-reduced-motion: no-preference 裡：使用者要求減少動態時
     **完全不掛動畫**，元素就是它原本該有的樣子（不是「動畫跑很快」）。
   ⚠ 動畫曲線用站上既有的 --ease-out（快進慢收），不另調一條。
   ⚠ 大圖的進場是 clip-path 由右往左掀開 —— 呼應「矩形大圖」這個版型語言，
     比單純淡入更看得出「這是一塊被推開的矩形」。
     ⚠ clip-path 會成為 position:fixed 後代的包含塊並裁切它（站上踩過的坑），
       .page-hero-media 裡只有一張 absolute 的圖，沒有 fixed 後代，安全。
   ═══════════════════════════════════════════════════════════════════════ */

@media (prefers-reduced-motion: no-preference) {

    /* ① 文字群：麵包屑 → 標題組 → 副標與 CTA，依序上浮淡入 */
    .page--v2 .page-hero-crumb,
    .page--v2 .page-hero-heading,
    .page--v2 .page-hero-intro {
        animation: v2HeroIn 640ms var(--ease-out) both;
    }

    .page--v2 .page-hero-crumb   { animation-delay:  80ms; }
    .page--v2 .page-hero-heading { animation-delay: 220ms; }
    .page--v2 .page-hero-intro   { animation-delay: 340ms; }

    /* ② 大圖：矩形由右往左掀開，同時圖本身輕微退回原尺寸 */
    .page--v2 .page-hero-media {
        animation: v2HeroMedia 900ms var(--ease-out) both;
    }

    .page--v2 .page-hero-media img {
        animation: v2HeroZoom 1600ms var(--ease-out) both;
    }

    /* ③ 灰色色帶本身從上緣長出來（很輕，只是讓「頁面被打開」有個起手式）
       ⚠ 用 transform 不是 height：height 會觸發整頁重排，
         而這一段在首屏、正是最不能掉幀的時候。 */
    .page--v2 .page-hero {
        animation: v2HeroBand 520ms var(--ease-out) both;
    }
}

@keyframes v2HeroIn {
    from { opacity: 0; transform: translateY(14px); }
    to   { opacity: 1; transform: none; }
}

@keyframes v2HeroMedia {
    from { clip-path: inset(0 0 0 26%); opacity: 0; }
    to   { clip-path: inset(0);         opacity: 1; }
}

@keyframes v2HeroZoom {
    from { transform: scale(1.06); }
    to   { transform: none; }
}

@keyframes v2HeroBand {
    from { opacity: 0; }
    to   { opacity: 1; }
}

/* 色帶內容的捲動進場（.js-reveal 由 home.js 掛上）——
   位移量比首頁小一階：這一版的段落密度高，位移太大會讀起來一直在跳。
   ⚠ 一定要寫 :not(.is-in)：基底的 `.js-reveal.is-in { transform: none }` 是
     (0,2,0)，這一條若只寫 `.page--v2 .js-reveal` 也是 (0,2,0) 但位置更後面 ——
     **進場結束後仍然贏**，元素會永遠停在下移 16px 的位置，
     上方還會露出容器底色（實測就是這樣冒出一條橫槓）。
     加了 :not(.is-in) 之後，掛上 .is-in 的那一刻這條規則就不再命中。 */
.page--v2 .js-reveal:not(.is-in) {
    transform: translateY(1rem);
}

/* ── 22. 色塊格的縫不要用深色（2026-08-05 客戶回報）─────────────────────
   客戶：「什麼時候適合導入 AI 分身這區塊，白色方塊上不要再疊加深灰色的長條。」

   ⚠ 成因是兩件事疊在一起：
     ① .tile-grid 原本用「容器底色 ＋ gap: 1px」當格線（深色 16% 透出來當縫）。
     ② 這一輪加的捲動進場會讓格子 translateY(1rem)，格子往下位移的那 16px
        正好把容器的深色底從上緣**整條**露出來 —— 實測容器 y=199.7、
        格子 y=216，中間就是那條 1728×16 的深灰長條。
   ⚠ 修法不是把進場拿掉（動態是客戶要的），而是**不要用深色當分隔**：
     容器底色改透明，1px 的縫直接透出色帶自己的灰 —— 這也才符合站上
     「把線條感拿掉，以色塊跟色塊去做區隔」的既有指定。
     位移期間露出來的就是色帶本身的顏色，看不出任何橫槓。
   ⚠ 選擇器與第 20 節那條同特異度、位置在後面，所以贏；
     第 19／20 節的原規則一行都沒刪，要退回把這一段註解掉即可。 */
.page--v2 .tile-grid,
.page--v2 .band--grey .tile-grid {
    background: transparent;
}

/* ═══════════════════════════════════════════════════════════════════════
   23. 全站字級級距改成 12 / 14 / 18 / 26 / 32（2026-08-05 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「header 跟 footer 要 14px」「12/14/18/26/32」
        「首頁請跟服務專區的字級分層一樣」。

   ── 做什麼 ────────────────────────────────────────────────────────────
     ① 五個級距釘死成 12 / 14 / 18 / 26 / 32，**三個斷點一律相同**。
        改版前 --fs-* 會隨版心寬度變（1441+ 是 14/16/20/28/40、
        992–1440 是 12/14/18/26/38、≤991 是 14/15/17/22/28），
        所以同一顆按鈕在不同螢幕上不是同一個大小 —— 客戶要的是固定一套。
     ② 把 token 重新對到級距，讓**首頁與服務內頁的分層完全一致**：
              12  --fs-1  日期／編號／裝飾英文／footer 小字
              14  --fs-2  內文、header 導覽、footer 連結與欄標題、表單控制項
              18  --fs-3  卡片標題／小節標題
              26  --fs-4  區塊標題（h2）
              32  --fs-5  頁面主標（h1）／區塊主標
     ③ header／footer 因此自動變成 14px（它們讀 --fs-2，見本檔 11216），
        不必為那兩區另外寫一套。

   ⚠ 為什麼一個 :root 就能蓋掉上面兩組 @media：媒體查詢**不加特異度**，
     同為 (0,1,0) 時由位置決定 —— 這一段在檔案最後，所以三個斷點都吃它。
   ⚠ 用 rem 不是 px：header 的 A／A+／A++ 切換靠根字級，寫死 px 會不跟著放大。
   ⚠ --header-h 固定 56px，字變小不會改 header 高度，
     依賴它的 fixed 位移與 sticky 停點都不受影響。
   ⚠ 既有的級距定義（11141 起）與 token 對應（11200 起）**一行都沒刪**，
     要退回把這一整段註解掉即可。
   ═══════════════════════════════════════════════════════════════════════ */

:root {
    --fs-1: .75rem;      /* 12px */
    --fs-2: .875rem;     /* 14px */
    /* ⚠ 2026-08-05 客戶再調：L3 由 18px 改成 20px（級距 12/14/20/26/32）。
       這一階是「卡片／小節標題」，往上拉一級之後與內文 14px 的落差更明顯。
       ⚠ 2026-08-13 客戶再調：L3 由 20px 改成 26px（級距 12/14/26/26/32）——
         與 L4 同值，卡片／小節標題跟區塊標題（h2）現在是同一個字級。 */
    --fs-3: 1.625rem;    /* 26px（原 20px） */
    --fs-4: 1.625rem;    /* 26px */
    --fs-5: 2rem;        /* 32px */
}

/* ── token 重新對到級距（首頁與服務內頁共用同一套分層）────────────────
   ⚠ 只調三個：--text-body 由 fs-3 降到 fs-2（內文 20 → 14），
     --text-sub／--text-lead 由 fs-4 降到 fs-3（卡片標題 28 → 18）。
     --text-h2（fs-4）、--text-section／--text-h1（fs-5）、
     --text-meta／--text-deco（fs-1）原本就對得上，不必動。
   ⚠ --lh-body 跟著提高到 2.3：14px 配行高 2 只有 28px，段落會太擠；
     2.3 排出 32px，與服務內頁完全相同（客戶指定的「行距大點」那一組）。 */
:root {
    --text-body: var(--fs-2);
    --text-sub:  var(--fs-3);
    --text-lead: var(--fs-3);
    --lh-body:   2.3;
}

/* 服務內頁 v2 原本自己宣告了一份 14／2.3／12（第 19 節），
   現在全站級距已經是同一組值 —— 移除那份重複宣告，讓它單純繼承，
   級距只留一個權威來源（否則日後改級距會有一頁改不到）。
   ⚠ 只清掉字級三項，v2 自己的版面變數（--v2-p-gap／--v2-band-y）保留。 */
.page--v2 {
    --text-body: var(--fs-2);
    --lh-body:   2.3;
    --text-meta: var(--fs-1);
}

/* ── 首頁的三處硬指定改讀新的分層（2026-08-05）─────────────────────────
   客戶：「首頁請跟服務專區的字級分層一樣。」

   上面把 token 對好之後，首頁還有三組是**直接寫 var(--fs-*)** 的規則
   （2026-08-04 五級距那一輪為了收斂散落字級而寫的），它們不吃 token，
   所以會與服務內頁對不齊。實測差異：
       內文        首頁 18px  ←→  服務頁 14px
       卡片標題    首頁 26px  ←→  服務頁 18px
       區塊標題    首頁 32px  ←→  服務頁 26px
   這裡把三組各自降一階，兩邊的五層就完全一致：
       32 主標 / 26 區塊標題 / 18 卡片標題 / 14 內文 / 12 小標

   ⚠ 只改 font-size，行高、字重、字距、顏色一個字都沒動。
   ⚠ .section-title .zh 是**首頁的區塊主標**（服務專區／實體課程／知識專區），
     對應服務內頁的 .band-title，所以降到 fs-4；
     頁面 H1 那一級（.cal-title／.page-hero-title／.page-title）維持 fs-5，
     兩邊的「主標比區塊標題大一階」關係因此相同。
   ⚠ 原規則（11239／11246／11253）一行都沒刪，要退回把這一段註解掉即可。 */

/* 內文 → 14px */
.kz-stack-body-text,
.svc-intro-desc,
.svc-feature-desc,
.svc-tile-sub {
    font-size: var(--fs-2);
}

/* ⚠ 2026-08-13 客戶「首頁知識專區的內文有一段是副標題，你用跟內文一樣的
   14 請改成 16」——.kz-stack-desc（每張卡片標題正下方那一句，例如「告別
   過載與內耗」「心靈成長與關係探索課程」那幾句）其實是副標題，跟緊接在
   後面的 .kz-stack-body-text（真正的長段內文）角色不同，原本兩個一起被
   歸進上面「內文 → 14px」那組，這裡拆出來單獨給 16px，body-text 維持
   14px 不變。16px 不在 12/14/26/26/32 這五個級距裡，是這裡單獨需要的
   固定值，不擴充全站級距表。 */
.kz-stack-desc {
    font-size: 1rem;
}

/* 卡片／文章標題 → 18px */
.svc-feature-name,
.kz-stack-name,
.kz-stack-name a {
    font-size: var(--fs-3);
}

/* 區塊主標 → 26px（＝服務內頁 .band-title 那一階）
   ⚠ 2026-08-13 客戶再指定「服務專區的總標題請用 32px」——上面 2026-08-05
     那次「首頁跟服務專區字級分層一樣」的決定，在這一階被客戶明確推翻，
     改回讀 --fs-5（32px），不再跟服務內頁 .band-title（26px）同一階。
     這裡只改這一條，.band-title 本身、以及首頁其餘兩組（卡片標題／內文）
     維持 2026-08-05 那次的對齊，不受影響。 */
.section-title .zh {
    font-size: var(--fs-5);
}

/* 窄幅漏網一條：11268 那個 @media 用 (0,2,0) 把知識專區的內文釘在 fs-3，
   特異度比上面那條 (0,1,0) 高，所以 ≤991 時首頁內文仍是 18px、與服務頁的 14px
   對不齊（實測 390 抓到）。這裡用同樣的選擇器在後面補一次。
   ⚠ 2026-08-13 拿掉 .kz-stack-desc（同上，已改固定 16px，不再跟著 body-text
     一起收窄幅級距）。 */
@media (max-width: 1023.98px) {
    .knowledge-section .kz-stack-body-text {
        font-size: var(--fs-2);
    }
}

/* ── 24. 首頁知識專區左欄：段落放鬆（2026-08-05 客戶指定）────────────────
   客戶：「知識專區左邊的文字段落讓他鬆散一點，現在行高有點矮都擠在一起。」

   ⚠ 實測到的三個成因（1920）：
     ① 行高 32.2px（14px × 2.3）—— 對 14px 不算矮，但配上下面兩點就顯得擠。
     ② .kz-stack-text 是 flex column，gap 只有 **16px** ＝ **半行**。
        段落與段落之間比行與行之間還窄，兩段就黏成一整塊。
     ③ 內文欄寬 805px ÷ 14px ＝ 一行 **58 個中文字**。
        行長超過 45 字之後，眼睛回行容易跳行，讀起來就是「擠」——
        這一項比行高更關鍵，只調行高救不回來。
   ⚠ 三項一起調：行高 2.3 → 2.6（36.4px）、段距 16 → 28px、行長收到 44 字。
   ⚠ 只作用在 .kz-stack-*，那是**首頁知識專區獨有**的 class，
     其他頁面與服務內頁的內文一個字都不會動（仍是 14/32.2）。
   ⚠ 行長用 em 不是 px：字級切到 A+／A++ 時行長要一起長，
     否則大字每行反而塞更少字。 */
.kz-stack-text {
    gap: 1.75rem;        /* 16px → 28px：段距要大於行距，段落才分得開 */
}

.kz-stack-desc,
.kz-stack-body-text {
    line-height: 2.6;    /* 14px → 36.4px */
    max-width: 44em;     /* ≈44 個中文字一行（原本 58 字） */
}

/* ── 25. 色塊格與步驟的編號太淺（2026-08-05 客戶回報）──────────────────
   客戶：「服務專區的顧問案標題上面的數字太淺看不到。」

   ⚠ 實測：.tile-no 是 rgba(0,0,0,.34)，壓在白格上只有 **2.37:1** ——
     26px 的大字在 WCAG 只要 3:1 就算過，它連這條線都沒到，等於「看得到形狀、
     讀不出數字」。原值是當初想讓編號安靜一點，但壓過頭了。
   ⚠ 改成 foreground 60%（#666666），三種底色都站得住（逐一算過，非目測）：
         白       #FFFFFF   5.74:1
         膚色     #F2E3D0   4.53:1   ← hover 01／03
         藍       #B0C7DD   3.21:1   ← hover 02
     全部高於大字的 3:1，而且仍然明顯比黑色標題輕，層次沒有塌掉。
   ⚠ hover 換底色時編號不必跟著換色 —— 選這個濃度就是為了三種底都成立，
     少一組狀態要維護。
   ⚠ .step::before（步驟大編號）原本更淺（.3），一併調到同一個值：
     同一個「編號」角色就該是同一個顏色（SSOT），雖然服務內頁目前沒用到它。
   ⚠ 原規則一行都沒刪，要退回把這一段註解掉即可。 */
.tile-no,
.step::before {
    color: hsl(var(--foreground) / .6);
}

/* ── 26. 主打服務補上編號（2026-08-05 客戶回報）────────────────────────
   客戶：「顧問案上面的數字勒？」
   首頁服務專區的主打那一格（.svc-feature）原本沒有編號，只有右側網格的兩格有，
   排下來變成「（無）／02／03」，看起來就是 01 掉了。標記補上一顆 .svc-tile-no，
   這裡只補它在**主打版位**需要的間距 —— 顏色、字級、膚色籤條全部沿用
   .svc-tile-no 既有的定義，不另做一套。
   ⚠ .svc-feature-info 是 flex column + align-items:flex-start，
     所以籤條會自己貼齊左上、不會被拉滿寬。
   ⚠ 網格那一格靠 .svc-tile-text 的 margin-top 拉開，主打這一格沒有那條規則，
     所以間距補在籤條自己身上。 */
.svc-feature-no {
    margin-bottom: 1.25rem;

    /* ⚠ 與網格那兩格對齊：8438 那條把 .svc-grid 裡的編號收成「無底色的純文字 +
         SLATE 灰」，但它的選擇器帶 .svc-grid，主打這一格不在網格裡、吃不到，
         所以會殘留基底的膚色籤條 —— 實測就是 01 有膚色底、02／03 沒有。
       ⚠ 顏色也一起釘住：≤991 有一條 .svc-main .svc-tile-no 把編號轉成
         --brand-accent（#C4C5CA，深色帶上的標籤色）。主打那一格在手機上是
         **淺底**，那個顏色壓上去只有 1.4:1，等於又變回「看不到的數字」
         （實測 390 抓到）。這裡直接指定 SLATE，三個寬度一致。 */
    padding: 0;
    background: transparent;
    color: hsl(var(--slate));
}

/* 窄幅漏網：5848 那個 @media 用 (0,2,0) 把 .svc-main .svc-tile-no 轉成
   --brand-accent（深色帶上的標籤色）。主打那一格在 ≤991 是淺底，
   那個顏色壓上去只有 1.4:1（實測 390 仍是 #C4C5CA）。同特異度在後面補一次。 */
@media (max-width: 1023.98px) {
    .svc-main .svc-feature-no {
        color: hsl(var(--slate));
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   27. 實體課程改用服務專區 v2 版型（2026-08-05 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「實體課程也重新排版照服務專區的調性。」

   ── 做什麼 ────────────────────────────────────────────────────────────
   兩頁（列表 courses_list.php／內頁 course_detail.php）的標記改掛 .page--v2，
   第 19～22 節的整套色帶／Hero／編輯式兩欄／色塊格因此**直接沿用**。
   這一節只補三處 v2 原本沒有遇過的版位：
     ① 課程卡 .pcard 第一次壓在色帶上 —— 白卡壓白帶會看不出邊界，改灰卡
     ② 連續多段 .page-text —— 編輯器模組現在放的是客戶原稿，不再是一段假字
     ③ Hero 副標上方的狀態列 —— .entry-meta 自帶 margin-top，在這裡是第一個元素

   ⚠ 三段全部是**追加**，上面一行既有規則都沒有動；要退回把這一整節註解掉即可
     （頁面會退成「白卡壓白帶」與「段落黏在一起」，但不會壞版）。
   ⚠ 沒有新增任何色票：灰用 --surface-grey、hover 用 --nude／--accent-blue，
     全部是站上既有的那幾支（與「你將學會」色塊格同一組）。
   ═══════════════════════════════════════════════════════════════════════ */

/* ── ① 色帶上的課程卡：灰卡壓白帶 ─────────────────────────────────────
   .pcard 的底色是 --muted（白）。它原本壓在頁面底 #F6F6F7 上，有一階明度差；
   v2 的白色色帶把那一階吃掉了，整排卡片會變成「沒有邊界的圖 ＋ 文字」。
   ⚠ 卡底改用既有的 --surface-grey（＝色帶交替用的那個灰），不引進第三種顏色。 */
.page--v2 .band .pcard {
    background: hsl(var(--surface-grey));
}

/* hover 依序變膚色／藍色 —— 與「你將學會」那一區的色塊格同一條規則
   （客戶 2026-08-05 指定的互動），用奇偶交錯，卡數增減不必回來改。
   ⚠ 只在 (hover: hover) 生效：觸控裝置的 :hover 會黏住，點過的那一張會
     一直留著顏色（站上其他區塊踩過同一個坑）。
   ⚠ 選擇器掛在 <li> 的奇偶而不是 .pcard 自己：卡片是 li 的唯一子元素，
     :nth-child 要數的是網格的格次。 */
@media (hover: hover) {
    .page--v2 .band .pcard-grid > li:nth-child(odd) .pcard:hover  { background: hsl(var(--nude)); }
    .page--v2 .band .pcard-grid > li:nth-child(even) .pcard:hover { background: hsl(var(--accent-blue)); }
}

/* ── ② 連續段落之間的距離 ─────────────────────────────────────────────
   .page-text 的 margin 是 0（它原本一個模組只有一段假字）。編輯器模組現在
   放的是客戶原稿的多個段落，段與段之間要給 v2 的段距（--v2-p-gap ＝ 1.5rem），
   否則整段文案會黏成一整塊 —— 客戶前一輪指名「現在太擠」要避免的就是這個。 */
.page--v2 .page-text + .page-text {
    margin-top: var(--v2-p-gap);
}

/* ── ③ Hero 副標上方的狀態列 ──────────────────────────────────────────
   .entry-meta 自帶 margin-top: 1.5rem（它原本接在單欄版型的 H1 底下）。
   在 v2 的 Hero 裡它是 .page-hero-intro 的第一個元素，那份留白會把整組文字
   往下推，與右側標題組的底線對不齊（.page-hero-body 是 align-items: flex-end）。
   ⚠ 只收自己那一份上留白；它與副標之間的距離由 .page-hero-lead 的
     margin-top: 1.5rem 負責，不重複給。 */
.page--v2 .page-hero-intro .entry-meta {
    margin-top: 0;
}

/* ── ④ 只有標題的色塊格：高度收一階 ─────────────────────────────────
   服務內頁的色塊格是「編號 ＋ 標題 ＋ 說明」三層，min-height 230–300px 剛好填滿。
   課程頁「你將學會」那一區是客戶原稿的條列，**只有一行標題**（原稿沒有說明文，
   不自己補），沿用同一個高度會在編號與標題之間空出一大塊。
   ⚠ 用 :has() 判斷「這一格沒有說明」，而不是另開一個修飾子 class ——
     有沒有說明是內容決定的，標記不必為此長出兩種寫法。
     站上既有規則本來就在用 :has()（第 20 節 ② 的 body:has(.page--v2)）。
   ⚠ 上下限一樣用 clamp 跟著版面收斂，與 .tile 原本的寫法同一個做法。 */
.page--v2 .tile-grid > .tile:not(:has(.tile-desc)) {
    min-height: clamp(170px, 15vw, 210px);
}

/* ── ⑤ 編輯器圖文模組的圖：收成矩形 ───────────────────────────────────
   .editor-figure 原本是「圖片原比例攤開」（單欄版心 54rem 時剛好）。
   搬到滿版色帶之後，半欄就有 844px 寬，4:3 的素材會長到 637px 高，
   旁邊的文字只有 200px —— 一格圖配一大片空白，讀起來是散的。
   ⚠ 收成固定高度的矩形（object-fit: cover），與 v2 版型的
     .page-hero-media／.band-media 同一個語言（客戶指定「往矩形大圖去做層次」）。
   ⚠ 高度用 clamp 跟著版面走，crop 幅度控制在原圖的三成以內。
   ⚠ object-position 往上偏（50% 22%）而不是預設的置中 —— he-* 線稿的人物
     頭部在畫面上緣，置中裁切實測會**把頭切掉**（1920 實機看過）。
     偏上之後，切掉的主要是下緣的地板與留白。
   ⚠ 只作用在 .page--v2（＝服務內頁與實體課程），沒有掛的頁面一個像素都不變。 */
.page--v2 .editor-figure picture,
.page--v2 .editor-figure img {
    display: block;
    width: 100%;
    height: clamp(260px, 26vw, 440px);
    object-fit: cover;
    object-position: 50% 22%;
}

/* ═══════════════════════════════════════════════════════════════════════
   28. 實體課程「總頁面」改成參考圖的編排（2026-08-06 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「實體課程的總頁面以這個編排方式排，不要照服務專區的。」（附參考圖）

   參考圖是「滿版大圖 Hero（文字置中壓圖、底下輪播圓點）→ 工具列（左下拉／
   中標題／右搜尋）→ 白卡交錯式卡片牆壓在淺灰底上」。

   ── 做法 ──────────────────────────────────────────────────────────────
   標記維持掛 .page--v2（字級 12/14/20/26/32、色帶交替、進場動畫全部沿用），
   再多掛一個 .page--wall 覆寫「這一頁與服務內頁不同」的三處版位：
     ① Hero 由「左文右圖的灰帶」改成「整條照片舞台 ＋ 白字壓圖 ＋ 輪播圓點」
     ② 色帶標題列改成三格工具列（篩選／標題／搜尋）
     ③ 課程卡由固定格線改成 CSS 多欄的交錯牆，卡片回到白底（第 27 節的灰卡
        是為了「白卡壓白帶」而設；這一頁的帶子是灰的，白卡才有邊界）

   ⚠ 全部是**追加**，第 19～27 節一行都沒有動；要退回把這一整節註解掉，
     頁面會退回上一輪的 v2 版型，不會壞版。
   ⚠ 沒有新增色票：白字用既有的 --surface-dark-foreground，壓黑層用
     --foreground 加透明度，卡片與控制項用 --muted／--surface-grey。
   ⚠ 內頁（course_detail.php）不受影響：它沒有掛 .page--wall。
   ═══════════════════════════════════════════════════════════════════════ */

/* ── ① Hero：照片舞台 ─────────────────────────────────────────────────
   ⚠ 舞台用 position:relative ＋ 圖 absolute 疊在一起，切換只換 opacity ——
     不用 display:none：淡入淡出要兩張同時在場，而且 opacity 不觸發重排。
   ⚠ 高度用 min-height + clamp 跟著視窗走，圖 object-fit: cover。 */
.page--wall .page-hero {
    padding-block: 0;
    background: hsl(var(--surface-dark));
    color: hsl(var(--surface-dark-foreground));
}

/* 壓在照片上的文字整組翻成淺色（站上既有的深底那一面用色） */
.page--wall .page-hero-en {
    justify-content: center;
    color: hsl(var(--surface-dark-foreground) / .78);
}

.page--wall .page-hero-title { color: hsl(var(--surface-dark-foreground)); }

.page--wall .page-hero-lead {
    max-width: 44em;
    color: hsl(var(--surface-dark-foreground) / .82);
}

.page--wall .page-hero-crumb { color: hsl(var(--surface-dark-foreground) / .72); }

/* ⚠ 基底的 .page-hero-crumb a:hover 是深色 --heading（淺底那一面），
     壓在照片上會 hover 之後看不見，這裡翻成白。 */
.page--wall .page-hero-crumb a:hover,
.page--wall .page-hero-crumb a:focus-visible { color: hsl(var(--surface-dark-foreground)); }

/* ═══════════════════════════════════════════════════════════════════════
   29. 不留四邊邊界／圖片滿版／文章再內縮（2026-08-06 客戶第二次指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「每個分頁風格都保持不同色塊拼貼組合，圖片滿版呈現。
          目前有發現你都會留四邊邊界，不要留，
          另外文章打開可以內縮排多一點，不然看起來不像在讀文章，服務專區也是。
          其他頁面以這兩張參考圖去調整，然後首頁樣式不要改。」（附兩張參考圖）

   ── 兩張參考圖讀到的規則 ──────────────────────────────────────────────
     ① 圖與色塊**貼到畫布邊**，彼此之間也不留縫 —— 版面靠色塊拼貼分段，不靠留白
     ② 文字**反過來往內縮很深**，形成「滿版的圖 ↔ 內縮的字」的對比
        （這正是客戶說的「不然看起來不像在讀文章」）
     ③ 每一頁的拼貼組合不一樣，但用的是同一套色塊與同一組內縮值

   ── 做法 ──────────────────────────────────────────────────────────────
   v2 的色帶原本是「置中版心 ＋ 左右 --section-x 內距」（＝客戶說的四邊邊界）。
   這一節把內距從**色帶**移到**文字元件自己身上**：
     色帶  → max-width: none／padding-inline: 0（畫布滿版）
     文字  → padding-inline: var(--article-x)（比原本的 --section-x 深很多）
     圖／色塊 → 不給內距，直接貼到視窗兩側

   ⚠ 首頁不受影響：首頁沒有掛 .page--v2（客戶指定「首頁樣式不要改」）。
   ⚠ 全部是**追加**，第 19～28 節一行都沒有改；要退回把這一整節註解掉即可。
   ⚠ 沒有新增色票，只動間距與圓角。
   ═══════════════════════════════════════════════════════════════════════ */

/* 文章內縮值：桌機約 --section-x 的兩倍多，窄幅收回到可讀的最小值。
   ⚠ 上限用 rem 收住 —— 只用 vw 的話 2560 螢幕會內縮到剩一條字。 */
.page--v2 {
    --article-x: clamp(1.25rem, 8vw, 10.5rem);
}

/* ── ① 色帶本身不留左右邊界 ───────────────────────────────────────────
   ⚠ 直接子選擇器：只解掉 v2 頁面**自己**那一層版心，
     元件內部若另有 .section-bleed-inner（沒有）不受影響。 */
.page--v2 > .site-section > .section-bleed-inner {
    max-width: none;
    padding-inline: 0;
}
.page--v2 .band-head,
.page--v2 .editorial,
.page--v2 .cta-band-inner,
.page--v2 .entry-foot,
.page--v2 .faq-grid,
.page--v2 .band > .section-bleed-inner > .wf-note,
.page--v2 .band > .section-bleed-inner > .pager,
.page--v2 .editor-block > .page-text,
.page--v2 .editor-block > div:not(.editor-pair),
.page--v2 .editor-block > .wf-note {
    padding-inline: var(--article-x);
}

/* ── ③ 圖與色塊滿版 ───────────────────────────────────────────────────
   ⚠ 圓角一併收掉：貼到視窗邊的圖若還留 8px 圓角，兩側會露出四個小缺口。 */
.page--v2 .band-media,
.page--v2 .tile-grid {
    padding-inline: 0;
    margin-inline: 0;
    border-radius: 0;
}

.page--v2 .band-media picture,
.page--v2 .band-media img,
.page--v2 .editor-figure picture,
.page--v2 .editor-figure img {
    border-radius: 0;
}

/* 圖文模組：圖那一半貼到視窗邊，文字那一半內縮 —— 參考圖的核心對比。
   ⚠ gap 收成 0：圖既然要貼邊，中間再留一條縫就變成「兩個盒子」而不是拼貼。
   ⚠ 文字半邊左右都給 --article-x：它現在的鄰居是一張滿版的圖，
     不內縮的話字會直接黏在圖的切邊上。 */
.page--v2 .editor-pair {
    gap: 0;
    align-items: stretch;
}

.page--v2 .editor-pair > div {
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding-inline: var(--article-x);
    padding-block: clamp(1.5rem, 3vw, 2.5rem);
}

/* 兩張圖並排的模組：中間那條縫也收掉，兩張圖各自貼一邊 */
.page--v2 .editor-pair > .editor-figure + .editor-figure { margin-left: 0; }

/* Hero：左欄文字跟著加深內縮；大圖原本用負 margin 往右出血，
   ⚠ 現在色帶已經沒有內距了，那份負 margin 會把圖拉出視窗 —— 收回 0。 */
.page--v2 .page-hero-text {
    padding-left: var(--article-x);
    padding-right: clamp(1.25rem, 3vw, 3rem);
}

.page--v2 .page-hero-media { margin-right: 0; }

.page--v2 .page-hero > .section-bleed-inner > .wf-label {
    display: inline-block;
    margin-left: var(--article-x);
}

/* ⚠ 客戶「有這種紅字一律刪除」——附截圖是「服務說明文章（服務專區系統，
   無列表頁，首頁/選單進入）」那顆粉紅色 wf-label，出現在 service_page.php
   （企業內訓／教練式領導／線上課程／公眾演說那四頁，非顧問/AI 分身走
   service_page_simpla.php 沒有這個問題）。
   ⚠ 根因：wf-label／wf-note 全站統一靠 `.is-designed .wf-label,
   .is-designed .wf-note { display:none; }`（本檔開頭附近）隱藏，只有
   全站唯一一個 $wfDesigned=true 開關；但上面這條規則（.page--v2
   .page-hero > .section-bleed-inner > .wf-label，4 個 class）沒有掛
   .is-designed 條件，寫死 display:inline-block，特異度比隱藏規則
   （2 個 class）高，不管 .is-designed 開不開都會贏，把粉紅字強制打開。
   這裡補一條同樣的選擇器路徑、前面加 .is-designed（5 個 class），
   特異度更高、贏過上面那條，.is-designed 開啟時就會蓋回 display:none；
   .is-designed 關閉（原型階段）時這條不生效，粉紅字仍照舊顯示，
   wf-label 該有的除錯功能沒有被拿掉，只是不會漏到「已切版」的畫面上。 */
.is-designed .page--v2 .page-hero > .section-bleed-inner > .wf-label {
    display: none;
}

@media (max-width: 1023.98px) {
    /* 窄幅圖片本來就左右都出血，同樣把負 margin 收回 0 */
    .page--v2 .page-hero-media { margin-inline: 0; }
    .page--v2 .page-hero-text { padding-right: var(--article-x); }

    /* 圖文模組折成單欄後，圖滿版、字內縮的關係不變 */
    .page--v2 .editor-pair > div { padding-block: clamp(1.25rem, 4vw, 2rem); }
}
.page--v2 .band > .section-bleed-inner > .pcard-grid,
.page--wall .band > .section-bleed-inner > .wf-note,
.page--wall .band > .section-bleed-inner > .pager {
    padding-inline: var(--section-x);
}

/* ═══════════════════════════════════════════════════════════════════════
   30. 顧問案改用參考圖（Simpla）的編排（2026-08-06 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「顧問案以這個圖片編排方式重新編排。」（附參考圖）

   參考圖的四個特徵，這一節就做這四件事：
     ① 每一組標題上方一條**短橫線**（.rule-mark）
     ② 內容排成**左寬右窄兩欄**：左邊是段落、右邊是破折號清單
     ③ 清單項目前面是「—」，不是圓點也不是數字圓圈
     ④ 圖片獨立成一條**滿版**的帶子，上下不留邊，靠圖本身換氣

   ⚠ 只作用在 .page--simpla（＝顧問案這一頁），其餘五個服務內頁與其他頁面
     一個像素都不變 —— 呼應客戶「每個分頁風格都保持不同色塊拼貼組合」。
   ⚠ 沒有新增色票：橫線與破折號用既有的 --foreground／--slate 透明度階。
   ⚠ 全部是追加；要退回把這一節與 service_page.php 那段 if 註解掉即可。
   ═══════════════════════════════════════════════════════════════════════ */

/* ── ① 標題上方的短橫線 ───────────────────────────────────────────────
   ⚠ 用偽元素畫線而不是 <hr>：它是標題的一部分（視覺上的起手式），
     不是內容上的分隔線，放進 DOM 會多一個沒有語意的節點。 */
.rule-mark::before {
    content: "";
    display: block;
    width: 2.25rem;
    height: 1px;
    margin-bottom: clamp(.875rem, 1.6vw, 1.25rem);
    background: hsl(var(--foreground) / .55);
}

/* ── ② 左寬右窄兩欄 ───────────────────────────────────────────────────
   ⚠ 欄寬用 fr 而不是百分比：中間那道 gap 才不會被算進欄寬裡。
   ⚠ 兩欄的左右內縮沿用第 29 節的 --article-x（客戶指定的「文章要內縮多一點」）。 */
.page--simpla .simpla-cols {
    display: grid;
    grid-template-columns: minmax(0, 1.3fr) minmax(0, .7fr);
    gap: clamp(2rem, 5vw, 6rem);
    padding-inline: var(--article-x);
}

.page--simpla .simpla-pad {
    padding-inline: var(--article-x);
}

.page--simpla .simpla-col > .band-title { margin-bottom: clamp(1.25rem, 2.5vw, 2rem); }

/* 右欄的標題比左欄小一階：它是附屬的那一欄，兩邊同級會打架 */
.page--simpla .band-title--sm {
    font-size: var(--fs-4);
    line-height: 1.5;
}

.page--simpla .simpla-date {
    margin: 0 0 clamp(1.5rem, 3vw, 2.25rem);
    color: hsl(var(--slate));
}

.page--simpla .simpla-block + .simpla-block {
    margin-top: clamp(1.5rem, 3vw, 2.25rem);
}

.page--simpla .simpla-label {
    margin: 0 0 .5rem;
    font-size: var(--text-meta);
    line-height: var(--lh-meta);
    letter-spacing: var(--tracking-meta);
    color: hsl(var(--heading));
}

.page--simpla .simpla-text {
    margin: 0;
    max-width: 42em;
    font-size: var(--text-body);
    line-height: var(--lh-body);
    font-weight: var(--fw-light);
    letter-spacing: var(--tracking);
    color: hsl(var(--muted-foreground));
}

/* ── ③ 破折號清單 ─────────────────────────────────────────────────────
   ⚠ 破折號用偽元素，list-style 維持 none —— 清單語意由 role="list" 保住
     （Safari 遇到 list-style:none 會拿掉清單語意，站上其他清單同一個處理）。 */
.page--simpla .dash-list {
    margin: clamp(1.5rem, 3vw, 2rem) 0 0;
    padding: 0;
    list-style: none;
}

.page--simpla .dash-list > li {
    position: relative;
    padding-left: 1.75rem;
}

.page--simpla .dash-list > li + li {
    margin-top: clamp(1rem, 2vw, 1.5rem);
}

.page--simpla .dash-list > li::before {
    content: "—";
    position: absolute;
    left: 0;
    top: 0;
    font-size: var(--text-meta);
    line-height: var(--lh-sub);
    color: hsl(var(--slate));
}

.page--simpla .dash-no {
    display: block;
    margin-bottom: .25rem;
    font-family: var(--font-eyebrow);
    font-size: var(--text-meta);
    letter-spacing: var(--tracking-meta);
    color: hsl(var(--accent-blue-ink, var(--slate)));
}

.page--simpla .dash-title {
    margin: 0;
    font-size: var(--text-body);
    line-height: var(--lh-sub);
    font-weight: var(--fw-heading);
    letter-spacing: var(--tracking-title);
    color: hsl(var(--heading));
}

.page--simpla .dash-desc {
    margin: .5rem 0 0;
    font-size: var(--text-meta);
    line-height: 2;
    font-weight: var(--fw-light);
    letter-spacing: var(--tracking);
    color: hsl(var(--muted-foreground));
}

/* ── ④ 獨立的滿版圖帶 ─────────────────────────────────────────────────
   ⚠ padding-block: 0 —— 它不是「色帶裡的一張圖」，它自己就是一條帶子；
     上下再留白就會變成「白邊框住的圖」，正是客戶說的四邊邊界。 */
.page--simpla .simpla-media {
    padding-block: 0;
}

.page--simpla .simpla-media .band-media {
    margin: 0;
    height: clamp(280px, 42vw, 580px);
}

.page--simpla .simpla-media .band-media picture,
.page--simpla .simpla-media .band-media img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* ── 開場與結尾：文字在左、箭頭在右 ───────────────────────────────── */
.page--simpla .simpla-open-body {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: clamp(1.5rem, 4vw, 4rem);
}

.page--simpla .simpla-open-text { flex: 1 1 auto; }

.page--simpla .simpla-lead {
    margin: clamp(1.25rem, 2.5vw, 1.75rem) 0 0;
    max-width: 36em;
    font-size: var(--fs-4);
    line-height: 1.9;
    font-weight: var(--fw-light);
    letter-spacing: var(--tracking);
    color: hsl(var(--muted-foreground));
}

.page--simpla .simpla-arrow {
    flex: 0 0 auto;
    color: hsl(var(--foreground) / .6);
}

.page--simpla .simpla-arrow--up { transform: rotate(180deg); }

.page--simpla .simpla-arrow-link {
    display: inline-flex;
    color: inherit;
    transition: opacity var(--transition);
}

.page--simpla .simpla-arrow-link:hover { opacity: .6; }

/* 開場那一條白帶：麵包屑之後留一段大留白，讓標題像參考圖一樣沉在下半部 */
.page--simpla .simpla-open .page-hero-crumb {
    margin-bottom: clamp(3rem, 9vw, 8rem);
}

@media (max-width: 1023.98px) {
    .page--simpla .simpla-cols {
        grid-template-columns: minmax(0, 1fr);
        gap: clamp(2.5rem, 6vw, 3.5rem);
    }

    /* 窄幅時箭頭沒有位置可站，收掉不影響內容（它本來就是裝飾） */
    .page--simpla .simpla-arrow { display: none; }
    .page--simpla .simpla-open .page-hero-crumb { margin-bottom: clamp(2rem, 8vw, 3.5rem); }
}

/* ═══════════════════════════════════════════════════════════════════════
   31. 實體課程專區：footer 上面那條線拿掉（2026-08-06 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「實體專區那頁的 footer 上面那條線刪掉。」

   ⚠ 那條線是第 20 節 ② 加的 `body:has(.page--v2) .site-footer { border-top }`
     （2026-08-05 客戶指定「footer 固定讓他上方有細線跟內容隔開」）——
     那一輪的指定仍然有效，所以**不動原規則**，只在實體課程這個專區關掉。
   ⚠ 作用範圍＝掛 .page--courses 的頁面（實體課程列表 ＋ 三堂課內頁）；
     服務專區與其他 v2 頁面的細線照舊。
   ⚠ 用 border-top: 0 而不是 border-color: transparent —— 透明邊框仍然佔 1px 高度，
     footer 會比其他頁面矮一條線，對不齊。
   ═══════════════════════════════════════════════════════════════════════ */
body:has(.page--courses) .site-footer {
    border-top: 0;
}

/* ═══════════════════════════════════════════════════════════════════════
   32. 標題上方的短橫線改成全站統一（2026-08-06 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「我看你顧問案的標題上面會加一條短線，首頁標題也用一樣的呈現方式」
        「其他頁面標題也都用同樣的呈現方式」

   ⚠ 顧問案那條線原本只寫在 .rule-mark（第 30 節），是逐個標記加上去的。
     客戶要全站統一，所以這一節改由**標題元件自己**長出那條線，
     不必回頭在十幾支 view 逐一加 class（也就不會有人漏加）。
   ⚠ 線色用 currentColor + opacity —— 首頁有深色色帶（.on-dark），
     寫死 --foreground 會在深底上看不見。
   ⚠ 作用對象是「區塊／頁面標題」：
       .section-title        首頁與各處的區塊標題（中文大標 ＋ 英文小標）
       .page-head            單欄版型的頁首（知識內容／電子報／搜尋／隱私權／會員…）
       .band-head-text       v2 色帶的標題組（服務專區、實體課程、課程一覽）
       .cta-band-text        結尾行動帶的標題組
     Hero 的英文小標（.page-hero-en）維持原本的藍色圓點 —— 它是首屏的另一個記號，
     兩個記號疊在同一個位置會打架；顧問案／AI 分身／課程內頁的開場已經是短線版。
   ⚠ :first-child 是必要的：服務內頁的日期（2026 年 5 月 9 日）也用 .band-en，
     它排在標題組的最後，不該長出一條線。
   ⚠ 全部是追加，第 30 節的 .rule-mark 留著不動（同一個偽元素，值也相同，
     兩條規則同時命中不會疊出兩條線）。
   ═══════════════════════════════════════════════════════════════════════
   ⚠ 2026-08-13 客戶「全站標題上的英文小字一律改成線條在英文小字下，跟首頁
     一樣」——追查發現上面這段「全站統一」其實**沒有真的統一**：.section-title
     是線在英文小字**下面**（en 用 flex order:-1 排到最前，::before 排在
     en 與 zh 之間，見本檔開頭），但 .page-head-en／.band-head-text >
     .band-en／.cta-band-text > .band-en 的 ::before 是長在該元素**自己
     的文字內容前面**（同一個 <p> 裡，pseudo 在文字之前起頭），視覺上是
     「線在上、英文小字在下」，跟首頁的順序相反（實測 NEWSLETTER／
     OVERVIEW 那幾頁都是線在文字上方）。
     這裡把後面三個改成 ::after（線移到文字後面＝文字下方），只有
     .section-title（順序本來就對）與「沒有英文小字可言」的
     .page-head:not(:has(.page-head-en))維持 ::before 不動。
   ⚠ margin 跟著從 margin-bottom 換成 margin-top：原本是「線推開下面的
     文字」，現在反過來是「線離開上面的文字」，數值不變，只是換方向；
     線到再下面的主標（h1/h2）那段距離，本來就是 .page-head-en／.band-en
     自己的 margin-bottom 在管，不必再加一次。 */
.section-title::before,
.page-head:not(:has(.page-head-en))::before {
    content: "";
    display: block;
    width: 2.25rem;
    height: 1px;
    margin-bottom: clamp(.875rem, 1.6vw, 1.25rem);
    background: currentColor;
    opacity: .55;
}

.page-head-en::after,
.band-head-text > .band-en:first-child::after,
.cta-band-text > .band-en:first-child::after {
    content: "";
    display: block;
    width: 2.25rem;
    height: 1px;
    margin-top: clamp(.875rem, 1.6vw, 1.25rem);
    background: currentColor;
    opacity: .55;
}

/* 置中版型的頁首（.page-head--center）線也要置中，否則會孤零零卡在左邊 */
.page-head--center .page-head-en::after,
.page-head--center:not(:has(.page-head-en))::before {
    margin-inline: auto;
}

/* ⚠ 2026-08-13 同一輪「線移到英文小字下方」，補 .band-en.rule-mark 這個
   分支——它是實體課程／知識專區列表頁、實體課程內頁 Hero 的英文小標
   （courses_list.php「In-person Courses」、knowledge_list.php「Knowledge
   Zone」、course_detail.php 的 $c['en']），線目前長在 .rule-mark（第 30
   節）的 ::before，一樣是「線在文字上面」。
   ⚠ 不能直接把第 30 節的 .rule-mark::before 整條改成 ::after——那條規則
     還有第二個用途：.page--courses .ci-group.rule-mark 是課程內文**段落
     群的分隔線**（不是英文小字，是內文分段起手式），線本來就該在上面，
     跟這次要改的「標題英文小字」是兩回事，改了會連帶弄反段落分隔線。
   ⚠ 所以只精準狙擊 .band-en.rule-mark 這個組合（兩個 class 同時出現時才
     命中）：先用 content:none 關掉繼承來的 ::before，再開一個同樣式的
     ::after，.ci-group.rule-mark 完全沒被碰到。 */
.band-en.rule-mark::before {
    content: none;
}

.band-en.rule-mark::after {
    content: "";
    display: block;
    width: 2.25rem;
    height: 1px;
    margin-top: clamp(.875rem, 1.6vw, 1.25rem);
    background: hsl(var(--foreground) / .55);
}

/* ═══════════════════════════════════════════════════════════════════════
   34. 實體課程的橫幅版位改吃 he-long 原圖比例（2026-08-06 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「實體課程要橫條篇幅的圖片用 he-long 的圖片。」
   客戶當天提供 he-long／he-long2／he-long3 三張，皆 2336×992（約 2.35:1）。

   ⚠ 版位原本是「固定高度 ＋ object-fit: cover」，那是為了把**正方**線稿
     （he-1~5，1100×830）塞進滿版帶而設的。橫幅圖再套同一套會上下各裁掉約三成
     —— 這三張的人物頭腳就在那三成裡（實測會被切）。
   ⚠ 所以改成 aspect-ratio 吃原圖比例：**一個像素都不裁**，
     高度自己跟著視窗寬度走。
   ⚠ Hero 另外加 max-height：2.35:1 在 2560 螢幕會算出 1000px 以上的高度，
     一屏放不下標題；超過就回到 cover 微裁（裁幅在一成以內）。
   ⚠ 窄幅用 min-height 兜底：390 寬算出來只有 165px，太扁看不出是什麼。
   ⚠ 小卡與圖文模組維持 he-1~5 正方線稿，不受影響（它們不是橫幅版位）。
   ═══════════════════════════════════════════════════════════════════════ */
.page--courses .simpla-media .band-media {
    height: auto;
    aspect-ratio: 2336 / 992;
}

.page--wall .page-hero-title      { color: hsl(var(--heading)); }
.page--wall .page-hero-en         { color: hsl(var(--slate)); }
.page--wall .page-hero-lead       { color: hsl(var(--muted-foreground)); }
.page--wall .page-hero-crumb      { color: hsl(var(--slate)); }
.page--wall .page-hero-crumb a:hover,
.page--wall .page-hero-crumb a:focus-visible { color: hsl(var(--heading)); }

/* ── 34-3 課程資訊面板：跨整個色帶，不要塞在窄欄裡 ──────────────────────
   客戶：「這區怪怪的，上課日期那邊自己一塊然後好窄。」

   ⚠ 兩個原因疊在一起：
     ① 第 29 節的「文字內縮」曾把 .info-panel 也列進去，面板的左右內距被改成
        --article-x（桌機 153px）—— 面板自己本來只有 24～40px，
        526px 的欄寬扣掉兩側 153px 只剩 220px，日期與地址當然被擠成三四行。
        → 已把 .info-panel 從那份清單移除（它是有自己內距的**盒狀元件**，
          不是貼著色帶的文字，本來就不該吃版心的內縮）。
     ② 它住在右邊那一欄。改排到兩欄下方、跨整條色帶。
   ⚠ 標籤欄同時給 min-width：th 原本是 9.5rem 固定寬，在寬版位裡
     「■ 上課日期」四個字配一大片空白會很鬆，改成內容寬 + 一段固定間距。 */
/* ── 上課日期／課程地點：白色一塊，貼齊灰底的左下角（2026-08-10 客戶指定）──
   客戶：「上課地點跟日期改成白色一塊，但貼在灰色背景最左下角，要貼邊不要有邊界。」

   ⚠ 標記已移出 .ci-block、直接掛在色帶最後面（.ci-block 有版心內距，
     留在裡面就一定貼不到左緣）。
   ⚠ 貼「下」緣靠負的下邊距把色帶自己的 --v2-band-y 吃掉；
     貼「左」緣則是不給任何左邊距 —— 色帶的 .section-bleed-inner 在 v2
     已經是 padding-inline: 0，所以什麼都不加就是貼邊。
   ⚠ 圓角一併收掉：貼到色帶角落的白塊若還留 2px 圓角，會露出兩個小缺口。
   ⚠ 寬度收在版心一半以內：它是「濃縮結論」不是一條橫幅，拉滿整條會變成
     另一個色帶、把上面那段的結尾吃掉。 */
.page--courses .course-info-panel {
    width: min(100%, 34rem);
    margin: clamp(2.5rem, 5vw, 4rem) 0 calc(var(--v2-band-y) * -1) 0;
    border-radius: 0;
}

/* 說明註記回到版心內（它是註解，不跟著白塊貼邊） */
.page--courses .course-info-note {
    margin-inline: var(--article-x);
    padding-top: clamp(1.25rem, 2.4vw, 1.75rem);
}

.page--courses .course-info-panel .info-table th {
    width: 1%;
    padding-right: clamp(1.5rem, 3vw, 3rem);
}

/* ═══════════════════════════════════════════════════════════════════════
   35. 知識專區列表：換頁面樣式（2026-08-06 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「換知識專區頁面樣式。」
   承前幾輪的方向（不留四邊邊界／圖滿版／文章內縮／標題短橫線），
   並依「每個分頁風格都保持不同色塊拼貼組合」給這一頁自己的排法：
     開場 → 滿版橫幅 → 灰底橫向篩選帶 → 白底「大卡領頭 ＋ 三欄小卡」→ 分頁

   ⚠ 只作用在 .page--knowledge；服務專區的兩欄、實體課程的交錯卡牆都不受影響。
   ⚠ 篩選的 DOM 一個節點都沒有動（JS 靠 data-* 與 id 抓），這裡只換排列：
     側欄 .filter 由直的一欄改成橫的一條，清單項目改成 inline 排列。
   ⚠ 不用 !important 去蓋 bootstrap 的 d-none/d-lg-block：
     改讓 <li> 自己 inline-block，容器的 display 維持 bootstrap 那一份，
     手機（<992）仍然是下拉、桌機仍然是清單。
   ═══════════════════════════════════════════════════════════════════════ */

/* ── ① 左欄固定篩選（客戶：「知識專區要用左欄位固定篩選」「像商品購物網站那樣」）─
   ⚠ 版型用站上既有的 .page-with-side（15rem 側欄 ＋ 主欄），≤991 自動收單欄。
   ⚠ position: sticky 而不是 fixed —— fixed 會脫離文件流，側欄就不再受
     色帶的上下內距與 footer 約束，捲到頁尾會蓋在別的區塊上。
     sticky 只在**它自己的父容器範圍內**固定，捲出這一區就自然停住。
   ⚠ top 要讓開固定的 header（--header-h: 91px），否則標題會被蓋掉。
   ⚠ align-self: start 是 sticky 在 grid 裡生效的前提：
     grid 子項預設 stretch，撐滿高度就沒有可以「黏」的空間。 */
.page--knowledge .kz-layout {
    padding-inline: var(--section-x);
}

/* ⚠ 2026-08-18 客戶指定（第二輪）：「知識分類」「標籤篩選」原本共用①那個
   側欄，這次拆成左右兩個獨立欄位——左邊只放分類，另外分一欄放標籤；
   主欄（卡牆）跟著讓出寬度，卡片同時改貼齊分隔線（見下面「④ 文章拼貼」
   那一節，拿掉間距後單張圖才不會太滿版）。三欄：分類 15rem／標籤 11rem／
   主欄 1fr。
   ⚠ align-items 從 .page-with-side 繼承的 start 改成 stretch：
     兩欄篩選的外層容器要跟主欄（通常最高）一樣高，灰底／sticky 捲動
     邊界才能鋪滿整欄，不是只包住文字那一小塊——呼應客戶「左邊整塊背景
     都要灰色，高改成整個頁面的高」那條指定。上面①那段「align-self:start
     是 sticky 生效前提」講的是舊版單層做法，這裡改成外層 stretch（撐滿）
     ＋ 內層另外 sticky（見下面 .kz-filter-sticky／.kz-tags-sticky），
     架構不同，新舊註解都留著對照。 */
.page--knowledge .kz-layout {
    grid-template-columns: minmax(0, 15rem) minmax(0, 11rem) minmax(0, 1fr);
    align-items: stretch;
    gap: clamp(1.5rem, 3vw, 3rem);
}

/* 外層：只負責「撐滿整欄高度＋壓灰底」，不再自己 sticky——
   sticky 需要的 max-height（一屏內捲動）跟「撐滿整欄」互斥，
   兩件事拆給內層 .kz-filter-sticky 做。 */
.page--knowledge .kz-filter {
    /* ⚠ 2026-08-18 客戶指定：左欄篩選壓灰底（沿用既有 --surface-grey，
       與服務卡／band--grey 同一份淺灰 token，不新調顏色）。
       ⚠ 2026-08-18 客戶指定（第十一輪，附色票截圖）：灰再調淺，改用
       --background（#F5F5F7｜頁面底壓灰那顆，全站最淺的一階灰）。
       ⚠ 這次客戶附的截圖沒有帶檔案路徑，不能用 PIL 精準吸色（跟最早那次
       灰底截圖不一樣），是肉眼比對選的最接近既有 token，沒有另外調新
       hex——如果跟截圖對不準，麻煩再給一次帶路徑的截圖用 PIL 覆核。 */
    background: hsl(var(--background));
    padding: clamp(1.5rem, 2.2vw, 2rem);
    /* ⚠ 2026-08-18 客戶指定（第三輪）：灰底要貼齊視窗左緣，不留白邊。
       .kz-layout 在 .page--v2 底下本來就是滿版出血（.section-bleed-inner
       在 .page--v2 沒有 padding/max-width，見該規則），左邊唯一的留白
       來源是 .kz-layout 自己的 padding-inline:var(--section-x)（上面①）。
       用同一個變數的負 margin 抵銷那段留白，灰底就貼到真正的視窗邊緣。
       跟「全站頁首色帶延伸到視窗頂」用的負 margin＋padding 補償是同一套
       技法，只是 padding-left 這裡不是原樣加回去——見下面第七輪的調整。 */
    margin-left: calc(-1 * var(--section-x));
    /* ⚠ 2026-08-18 客戶指定（第七輪）：「知識分類」文字要跟 header logo
       齊左。logo／H1「知識專區」的左緣都只吃 --section-x 這一份內距，
       這裡原本是 --section-x 再加這顆盒子自己的 clamp(1.5rem,2.2vw,2rem)
       （壓灰底時順便給的四邊留白），文字因此比 logo／H1 多縮進去一截
       （實測差 31px）。改成只留 --section-x，跟 logo／H1 同一個基準，
       上／右／下三邊維持原本的 clamp() 留白不動，只有左邊改口徑。 */
    padding-left: var(--section-x);
    /* ⚠ 2026-08-18 客戶指定（第四輪）：左欄右側加一條垂直線，跟灰底一樣
       撐滿整欄高度——加在 .kz-filter（撐滿整欄高的外層）上，不是加在
       .kz-filter-sticky（那個只有 sticky 時的可視高度，不是整欄）。
       線色沿用卡牆分隔線同一份 hsl(var(--foreground)/.16)，全站同一種
       hairline，不新調顏色。
       ⚠ 2026-08-18 客戶指定（第六輪）：「線不要貼在最邊，左移約 30px」
       ——border-right 天生只會貼在盒子邊緣，沒有「留幾 px」這個選項，
       改用 ::after 畫一條絕對定位的線，right:30px 讓它縮進灰底內側，
       不是貼齊 .kz-filter 自己的右邊界。 */
    position: relative;
}

/* ⚠ 2026-08-18 客戶指定（第十九輪）：「垂直線要延伸到標題那裡」——這條線
   原本掛在 .kz-filter::after，只能撐滿 .kz-filter 自己的高（③④篩選＋
   卡牆那個 section），構不到上面①開場的 H1。改掛到 .page--knowledge
   這個最外層（剛好包住①②兩個 section、在 footer 之前結束，見「左右分
   到 footer 為止」那條指定，範圍天生就對），線變成貫穿整頁主內容的一條，
   不用另外量①開場區塊多高再拼接。
   ⚠ x 座標沿用原本的算法不變（--section-x + 15rem − 30px，分類軌寬
   內縮 30px 的那個位置）——這條線的水平位置本來就跟①開場新加的左欄
   寬度（26rem+gap，見下面「開場左右分欄」那節）是兩件事，沒有綁在一起，
   不需要對齊那個欄寬的邊界。 */
.page--knowledge {
    position: relative;
}

.page--knowledge::after {
    content: "";
    position: absolute;
    top: 0;
    bottom: 0;
    left: calc(var(--section-x) + 15rem - 30px);
    width: 1px;
    background: hsl(var(--foreground) / .16);
}

.page--knowledge .kz-filter-sticky {
    position: sticky;
    top: calc(var(--header-h) + 1.5rem);
    /* 分類多到超過一屏時側欄自己捲，不會把 sticky 頂出畫面 */
    max-height: calc(100vh - var(--header-h) - 3rem);
    overflow-y: auto;
    /* ⚠ CSS 規則：overflow-y 設 auto/scroll 而 overflow-x 沒設（預設
       visible）時，瀏覽器會把 overflow-x 也偷偷算成 auto——只要內容有
       一點點橫向溢出（例如下面「英文小字底下橫線」那節曾經算錯寬度），
       就會冒出一條不該有的橫向捲軸。明講 hidden 鎖死，不管未來哪個
       子元素不小心溢出都不會再跑出捲軸（2026-08-18 客戶回報「這什麼，
       直接刪掉」，附的截圖就是這條意外的橫向捲軸）。 */
    overflow-x: hidden;
    /* ⚠ 2026-08-18 客戶指定：吸頂時「知識分類」要跟上面 logo 留空間，
       不要貼在一起——不動 header（改回上一輪 :has() 蓋 is-compact 背景
       透明那條，客戶要求撤掉），改在這裡直接加 padding-top 拉開文字
       跟頂端的距離。 */
    padding-top: 1.5rem;
}

/* 新拆出來的標籤欄：白底（維持頁面底色，不壓色）＋ sticky，
   寫法跟分類欄同一套（外層撐高、內層才 sticky），只是不上背景色，
   跟左邊灰底分類欄拉開層次。 */
.page--knowledge .kz-tags {
    padding-top: clamp(1.5rem, 2.2vw, 2rem);
}

/* 標籤第一顆原本是「#溝通」，用 104px 把它推下去對齊左邊分類第二項
   （溝通表達）——2026-08-18 客戶指定，第六輪，理由是當時標籤欄沒有
   「全部」，兩欄第一項天生錯開（分類多了「全部」＋它自己的 1.5rem
   分組間距，見下面「全部」跟底下分類拉開那節）。

   ⚠ 2026-08-19 客戶追加「tags底下也加上全部」（knowledge_list.php 的
   .filter-tags 現在也在最前面補了一顆「全部」，跟分類欄同一個角色），
   結構跟分類欄變成完全對稱：題目→全部→（分組間距）→其餘項目，兩邊
   不用再額外校正就會自然對齊——所以接著客戶說「tags的全部請跟左邊的
   全部齊頭」時，正確的解法是**拿掉**這條補償（不是換一個新數字）。
   實測拿掉之後兩欄「全部」的 top 差 0px，跟理論一致：.filter-title
   共用元件的 margin-bottom:20px 跟 .filter-tags／.filter-list 各自
   相鄰兄弟 collapse，兩欄用的是同一顆 token、同一種 collapse，天生
   就會落在同一條線上，不需要另外量一個 magic number。
   ⚠ 這條規則留著（設回 margin-top:0，不整段刪除）是為了保留「為什麼
   一度需要／後來為什麼不需要」這段判斷過程，不是留著生效中的補償。 */
.page--knowledge .kz-tags .filter-tags {
    margin-top: 0;
}

.page--knowledge .kz-tags-sticky {
    position: sticky;
    top: calc(var(--header-h) + 1.5rem);
    max-height: calc(100vh - var(--header-h) - 3rem);
    overflow-y: auto;
    /* ⚠ 2026-08-18 客戶指定（第四輪）：跟左邊知識分類一樣加 padding-top，
       兩欄吸頂時跟 header 的距離要一致。 */
    padding-top: 1.5rem;
}

@media (max-width: 1023.98px) {
    /* 窄幅收成單欄：分類／標籤／卡牆直向堆疊，順序照 DOM，不用另外排。 */
    .page--knowledge .kz-layout {
        grid-template-columns: minmax(0, 1fr);
    }

    /* 不再固定（黏在上面會一直吃掉半個螢幕），外層也不用再撐高
       （單欄本來就是自然高度，撐高沒有意義）。 */
    .page--knowledge .kz-filter-sticky,
    .page--knowledge .kz-tags-sticky {
        position: static;
        max-height: none;
        overflow: visible;
    }

    .page--knowledge .kz-filter {
        margin-bottom: clamp(1.5rem, 4vw, 2.5rem);
        /* 單欄堆疊時分類欄跟標籤欄是上下疊，不是左右並排，右邊那條
           直線沒有對象可以分隔，收掉。 */
        border-right: 0;
    }

    .page--knowledge .kz-tags {
        padding-top: 0;
        margin-bottom: clamp(1.5rem, 4vw, 2.5rem);
    }
}

/* ── ② 文章拼貼：三欄 ───────────────────────────────────────────────── */
.pcard-grid--3 { --pcard-cols: 3; }

.page--knowledge .kz-grid {
    /* ⚠ 2026-08-24 客戶指定「小卡只有兩張箭頭切換改成四張小卡然後箭頭
       切換」，第一版直接把 --pcard-cols 改成 4（四張橫排一列），客戶
       追加澄清「是橫排兩個直排兩行，不是橫的一排」——要的是 2 欄 × 2 列
       的田字格，不是單列四欄。欄數改回 2（跟「左欄佔掉寬度，主欄改兩欄」
       的原始理由一致），「一頁 4 張」這件事改由 JS 端控制（見下面
       knowledge_list.php 裡 syncGridPage() 的 PAGE_ROWS，一頁固定給
       2 列，欄數 × 2 列＝一頁張數，不再是「欄數＝一頁張數」）。 */
    --pcard-cols: 2;
    align-items: start;
}

/* ⚠ 2026-08-24 客戶「手機底下多一行列、各自獨立捲動」：markup 從單層
   <ul id="wfCardGrid"> 改成外層 #wfCardGrid（桌機真正的 grid 容器）＋
   兩個內層 <ul class="kz-grid-row">（手機時各自是獨立捲動軌道，見下面
   ≤1199.98 那個 media query）。桌機預設 display:contents，讓 6 張
   <li data-wfcard> 直接變成 #wfCardGrid 的 grid item，跳過這層分組
   ——JS 端 visibleCards()／syncGridPage() 用 querySelectorAll(
   '[data-wfcard]') 這種不管巢狀深度的寫法，這層分組對它是透明的，
   不用改 JS 就能維持桌機原本的真分頁行為。 */
.page--knowledge .kz-grid-row {
    display: contents;
    /* ⚠ 2026-08-24 客戶回報「小卡旁邊多了一個黑色圓點」：這個新增的
       <ul class="kz-grid-row"> 沒有 .pcard-grid 這個 class，沒吃到
       本檔 ≈11283 那條 .pcard-grid{list-style:none} 的重置——原本單層
       <ul id="wfCardGrid" class="pcard-grid ...">兩個 class 都在同一個
       元素上，這次拆成兩層，內層這個 <ul> 漏掉了，瀏覽器預設
       list-style:disc 就冒出來。display:contents 時這條會被忽略（沒有
       盒子可以畫項目符號），但≤1199.98 那個 media query 會把它改回
       display:flex，屆時符號就需要靠這裡的 list-style:none 擋住，
       所以無論如何都要補上，不能只指望 display:contents。 */
    list-style: none;
    margin: 0;
    padding: 0;
}

/* 2026-08-10 客戶指定「知識專區小卡都用同個樣式，把特別寬的那張用的跟其他
   一樣就好」「不要做成橫式」「也是直式的小卡」：第一篇原本跨兩欄、左圖右文
   的「大卡」整組收掉，改回跟其他五篇一樣的直式 .pcard（圖在上、字在下），
   落回上面 --pcard-cols: 2 的標準兩欄排版。
   ⚠ 只用註解關掉，一行都沒刪——客戶日後想要「第一篇當精選」的版型，
     把下面這段解開即可，不必重寫。 */
/*
.page--knowledge .kz-grid > li:first-child {
    grid-column: span 2;
}

.page--knowledge .kz-grid > li:first-child .pcard {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    align-items: stretch;
}

.page--knowledge .kz-grid > li:first-child .pcard-media {
    aspect-ratio: auto;
    height: 100%;
    min-height: clamp(220px, 22vw, 320px);
}

.page--knowledge .kz-grid > li:first-child .pcard-body {
    justify-content: center;
    padding: clamp(1.5rem, 3vw, 2.5rem);
}

.page--knowledge .kz-grid > li:first-child .pcard-title {
    font-size: var(--fs-4);
    line-height: 1.5;
}

@media (max-width: 1023.98px) {
    .page--knowledge .kz-grid { --pcard-cols: 2; }
    .page--knowledge .kz-grid > li:first-child { grid-column: span 2; }
}

@media (max-width: 767.98px) {
    .page--knowledge .kz-grid { --pcard-cols: 1; }
    .page--knowledge .kz-grid > li:first-child { grid-column: auto; }

    .page--knowledge .kz-grid > li:first-child .pcard { grid-template-columns: minmax(0, 1fr); }
    .page--knowledge .kz-grid > li:first-child .pcard-media {
        aspect-ratio: 16 / 9;
        height: auto;
        min-height: 0;
    }
}
*/

@media (max-width: 1023.98px) {
    .page--knowledge .kz-grid { --pcard-cols: 2; }
}

@media (max-width: 767.98px) {
    .page--knowledge .kz-grid { --pcard-cols: 1; }
}

/* 2026-08-19 客戶「請針對手機重新排版該區塊」，確認方向是「改橫向滑動
   輪播」——手機版原本是 --pcard-cols:1（上面那條），單欄卡片直式堆疊，
   畫面本身沒有壞掉，純粹是排版方向的選擇。改成站上已有的「橫向拖曳
   輪播」語彙（首頁近期課程 .course-grid，style.css ≈5581／7207／9433；
   /wireframe/courses 的 .cx-theme-track 是同一套手法的第二份拷貝），
   不是重新設計一種新互動。
   ⚠ 只加在 .kz-grid 這一層、不動 .pcard-grid／.pcard-grid--3 共用定義
     本身——那兩個 class 同時服務 member_center.php／newsletter_list.php，
     改共用定義會波及那兩頁沒被要求改的區塊。
     grid-template-columns:none／display:flex 蓋掉上面 --pcard-cols:1
     餵給 .pcard-grid 的 grid-template-columns，那個變數還在、只是不再
     被用到，不用刪。
   ⚠ 卡寬 min(78%,340px)、露一角提示可以往右拉，跟 .course-grid 手機版
     同一個值（style.css .course-cell ≈6043），沿用同一個「站上手機
     橫向卡片該多寬」的既定答案，不是另外抓一個新比例。
   ⚠ .pcard 本身（圖／標籤／日期／標題／簡介／podcast-影片連結）完全
     沒有改：只是容器從「直式堆疊」換成「橫向捲動的一排」，卡片內部
     排版沿用共用元件，改版風險只在容器這一層。
   ⚠ padding-bottom:2px 是 .course-grid 自己踩過的坑（卡片高度小數位
     被 overflow-y:auto 算 clientHeight 時取整數吃掉，底框線在手機
     看起來像斷掉）照抄過來預防同一個問題，不是這裡新發現的。

   2026-08-21 斷點延伸 767.98→1199.98：客戶「底下的箭頭請讓他真的能
   切換」——實測 992／1024／768 三個寬度 .kz-grid 都還是 display:grid
   （網格，不能捲），knowledge_list.php 那支「真實換頁」IIFE（本檔
   line 490 一帶）靠 isRail()＝getComputedStyle(grid).display==='flex'
   判斷要不要接手，這三個寬度判斷為否直接 return，箭頭看起來能按（沒被
   disabled）但點了沒反應。跟本檔「首頁 992–1199 手機化」同一輪的邏輯
   缺口一樣：這一頁的橫向輪播是 08-19 才加的獨立功能，沒被那一輪的
   991.98→1199.98 斷點下修掃到（首頁知識專區那組是撞名的另一個元件，
   已在該節註解排除）。JS 用 getComputedStyle 現場判斷，不寫死斷點，
   這裡只要把 CSS 的 flex 模式延伸到 1199.98，箭頭就會自動接上真實換頁，
   不用碰 knowledge_list.php。 */
@media (max-width: 1023.98px) {
    /* ⚠ 2026-08-24 客戶「手機底下多一行列，預設一張卡滿版，不要一張半，
       但讓輪播會自動切換跟手動切換」——原本這裡 .kz-grid 自己就是橫向
       捲動軌道（單排、露 78% 提示可以往右拉）。現在改成兩排各自獨立
       捲動，捲動軌道的角色從 #wfCardGrid 移到下面新增的 .kz-grid-row
       身上（每排一個）；#wfCardGrid 自己退回單純的「垂直堆疊兩排」
       容器，不再是可以橫向捲動的東西。
       ⚠ gap 沿用原本同一個 1.25rem（客戶當時指定「小卡跟小卡之間留
       空間」量出來的值），直排/橫排都用同一個數字，節奏一致。 */
    .page--knowledge .kz-grid {
        display: flex;
        flex-direction: column;
        gap: 1.25rem;
    }

    .page--knowledge .kz-grid-row {
        display: flex;
        flex-wrap: nowrap;
        gap: 1.25rem;
        overflow-x: auto;
        scroll-snap-type: x mandatory;
        scroll-behavior: smooth;
        scrollbar-width: none;
        -ms-overflow-style: none;
        cursor: grab;
        padding-bottom: 2px;
        /* ⚠ 2026-08-24 客戶「兩排小卡都各自要有上下兩條底線貼邊」——
           兩條線直接畫在 .kz-grid-row 自己的 border-top／border-bottom
           上，不用另外出血：實測 #wfCardGrid／.kz-grid-row 在這個斷點
           左右已經是 0～390（貼齊視窗左右緣，見本檔上面「卡牆左邊不留
           邊」那節既有的 margin-left:-var(--section-x) 出血），border
           天生跟著畫到真正的視窗邊緣，不用再疊一層負外距。顏色沿用本頁
           其餘裝飾線同一個 token（.filter-title::after／「全部」底下
           橫線都是 hsl(var(--foreground)/.16)），不新調色票。 */
        border-top: 1px solid hsl(var(--foreground) / .16);
        border-bottom: 1px solid hsl(var(--foreground) / .16);
    }

    .page--knowledge .kz-grid-row::-webkit-scrollbar {
        display: none;
    }

    .page--knowledge .kz-grid-row.is-dragging {
        cursor: grabbing;
        scroll-snap-type: none;
        scroll-behavior: auto;
        user-select: none;
        -webkit-user-select: none;
    }

    /* 「一張卡滿版，不要一張半」：原本 min(78%,340px) 露一角提示改成
       100%，每排一次只完整顯示一張卡——搭配下面 #48 新增的自動切換，
       使用者不再需要靠「看到下一張的邊角」提示才知道能往右滑。 */
    .page--knowledge .kz-grid-row > li {
        flex: 0 0 100%;
        scroll-snap-align: start;
    }

    .page--knowledge .kz-grid a,
    .page--knowledge .kz-grid picture,
    .page--knowledge .kz-grid img {
        -webkit-user-drag: none;
    }

    /* 2026-08-19 客戶「小卡樣式請改首頁的實體課程區塊的小卡的樣式」——
       首頁「實體課程」就是 .course-grid／.course-cell（2026-08-04
       區塊主標由「近期課程」改名，元件沒有換，見 home.php 該段註解），
       跟上面借走容器機制的是同一個元件，這裡補上外觀。
       ⚠ .course-cell 實測（getComputedStyle，390 寬）：卡片本身
         background:transparent／border-radius:0（不是色塊卡片），
         標題 font-size:26px＝var(--text-lead)、font-weight:350＝
         var(--fw-light)（.pcard-title 的顏色本來就已經是 hsl(var(
         --heading))，跟 .course-title 實測顏色一致，不用另外覆寫）。
       ⚠ 只覆寫「跟 .course-cell 對得上」的屬性（卡片色塊／圓角／標題
         字級字重），不動 .pcard-tags／.pcard-desc／.pcard-links——
         這些是知識卡片獨有、course-cell 沒有的欄位（標籤／摘要／
         podcast·影片連結），客戶要的是「借用 course-cell 的視覺語言」
         不是「砍掉 course-cell 沒有的內容」，禁止刪除任何內容。
       ⚠ 只在 .page--knowledge .kz-grid 這一層生效，不動 .pcard 共用
         定義本身——理由跟上面容器那段相同，newsletter_list.php／
         member_center.php 也在用 .pcard，不能被這裡的外觀覆寫波及。 */
    .page--knowledge .kz-grid .pcard {
        background: transparent;
        border-radius: 0;
    }

    .page--knowledge .kz-grid .pcard-body {
        /* 2026-08-19 客戶「小卡內文字加 padding，現在文字完全貼邊」——
           左右原本是 0（照抄 .course-cell 沒有色塊框、文字貼齊圖片邊緣
           的極簡風格），但後來客戶又要求「一張小卡兩張垂直線」，卡片
           兩側多了邊框線，文字零內距貼著線走就會太擠。補回 1rem。 */
        padding: 1.25rem 1rem 24px;
    }

    .page--knowledge .kz-grid .pcard-title {
        /* ⚠ font-size 不寫在這裡：本檔後面（≈21832）有一條「右邊小卡標題
           字級改 22（客戶指定，第五輪）」的既有規則，選擇器一模一樣
           （.page--knowledge .kz-grid .pcard-title），特異度相同、
           但排在後面，會贏過這裡——這裡原本寫的 var(--text-lead)（26px）
           實測從來沒有真的生效過，字級其實一直是那條 22px 舊規則在管。
           手機版真正要生效的字級覆寫另外寫在那條舊規則後面（同一個
           檔案位置，方便對照），不是這裡。 */
        font-weight: var(--fw-light);
        line-height: 1.65;
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   36. 知識專區 單篇文章：讀文版型（2026-08-06 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「（知識專區）單篇文章也設計樣式」
        ＋ 前一輪的「文章打開可以內縮排多一點，不然看起來不像在讀文章」

   排法：白底標頭（短橫線＋H1＋分類日期＋↓）→ 滿版橫幅 → 白底文章本體 → 灰底相關文章
   —— 列表是「大卡領頭＋左欄固定篩選」，內頁是「讀文欄」，兩頁不會長得一樣。

   ⚠ 文章本體給**雙層**約束：外層 .article-col 負責左右深內縮（--article-x），
     內層既有的 .page-narrow 負責行長（54rem，中文一行約 40 字）。
     兩件事分開：內縮是版面的呼吸，行長是可讀性，日後要調不會互相打架。
   ⚠ 只作用在 .page--article，其他頁面不受影響。
   ═══════════════════════════════════════════════════════════════════════ */
.page--article .article-col {
    padding-inline: var(--article-x);
}

/* ⚠ .page-narrow 原本是「置中的收窄欄」。放進已經內縮的容器後，
     置中會讓左右各再多一段留白 —— 改成靠左，與標頭那一欄對齊同一條邊。 */
.page--article .article-col .page-narrow {
    margin-inline: 0;
}

/* 標頭：H1 與副標的級距沿用單欄版型那一份，只把上下留白交給色帶 */
.page--article .simpla-open .page-head {
    margin-bottom: 0;
}

.page--article .simpla-open .entry-meta {
    margin-top: clamp(1.25rem, 2.5vw, 1.75rem);
}

/* ⚠ .entry-foot 在第 29 節已經吃了 --article-x（它原本直接坐在色帶上）。
     這一頁它住在**已經內縮過的** .article-col 裡，再吃一次會縮成一條。 */
.page--article .article-col .entry-foot {
    padding-inline: 0;
}

/* 相關文章：卡片維持自己的邊界（客戶：小卡排列還是要留邊界） */
.page--article .band--grey .page-block {
    padding-inline: var(--section-x);
}

/* ── 36-2 文章欄置中 ＋ 底部三顆按鈕的間距（2026-08-06 客戶回報）──────────
   客戶：「知識專區這三個按鈕太近了。還有文章可以置中，靠左右邊很空。」

   ⚠ ① 按鈕擠在一起的原因：第 20 節 ⑤ 為了 v2 色帶把 .page--v2 .entry-foot 的
        margin-top 收成 0（那時它直接坐在一條有內距的色帶上，兩份留白會疊）。
        這一頁它接在「影片／Podcast」那一列的正下方，收掉之後就黏住了。
        → 這一頁把它還原成基底的那一段距離（clamp(2.5rem,5vw,4rem)）。
   ⚠ ② 上方那條細線維持不畫（客戶 2026-08-05「返回上方的細線移除」仍有效），
        分段只靠留白。
   ⚠ ③ 文章欄改置中：內縮值不變，只是收窄欄在內縮範圍內置中，
        左右各留一半，不再是「左邊貼齊、右邊一大片空」。 */
.page--article .article-col .page-narrow {
    margin-inline: auto;
}

.page--article .article-col .entry-foot {
    margin-top: clamp(2.5rem, 5vw, 4rem);
    padding-top: 0;
    border-top: 0;
}

/* 影片／Podcast 那一列與上面文稿的距離也一起拉開一階 */
.page--article .article-col .entry-links {
    margin-top: clamp(2.5rem, 5vw, 3.5rem);
}

/* ── 35-2 就地篩選的結果列與空狀態（2026-08-06 客戶指定）─────────────────
   客戶：「被篩選到右邊要有相對應文字，像是有幾筆課程，跟課程上有分類被點選的感覺。」 */
.page--knowledge .filter-result {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .5rem .75rem;
    margin-bottom: clamp(1.5rem, 3vw, 2.25rem);
}

.page--knowledge .filter-result-clear {
    margin-left: auto;
    padding: 0;
    border: 0;
    background: none;
    font: inherit;
    font-size: var(--text-meta);
    letter-spacing: var(--tracking-meta);
    color: hsl(var(--slate));
    text-decoration: underline;
    text-underline-offset: 4px;
    cursor: pointer;
    transition: color var(--transition);
}

.page--knowledge .filter-result-clear:hover,
.page--knowledge .filter-result-clear:focus-visible { color: hsl(var(--heading)); }

.page--knowledge .filter-empty {
    margin: 0;
    padding-block: clamp(2.5rem, 6vw, 4rem);
    font-size: var(--text-body);
    line-height: var(--lh-body);
    color: hsl(var(--muted-foreground));
}

/* 左欄分類與標籤現在是「按鈕」的角色（點了就地篩選），游標一致 */
.page--knowledge .filter-list a,
.page--knowledge .filter-tags a,
.page--knowledge .pcard-tags a { cursor: pointer; }

/* ── 29-2 內縮不要吃掉行長（2026-08-06 客戶回報「這樣文字很怪」）──────────
   ⚠ .page-text 有行長上限（--measure 一類的 max-width）。第 29 節原本用
     padding-inline 給它內縮 —— padding 是**往內吃**的，616px 的行長扣掉左右各
     153px 只剩 386px，原稿又是每句一行，於是被二次折行折得很碎。
   ⚠ 改用 margin-inline：一樣往內縮，但不動內容寬度，行長維持設計值。 */
.page--v2 .editor-block > .page-text {
    padding-inline: 0;
    margin-inline: var(--article-x);
}

/* ── 29-3 課程說明：標題與內文一起置中成一欄（2026-08-06 客戶指定「文字置中」）─
   ⚠ 內文 .page-text 的行長上限是 44em（實測 616px）。把標題組收成同一個寬度、
     兩者都 margin-inline: auto，就會是「置中的一欄」——
     標題與段落的左邊界對齊同一條線，不會出現標題靠左、內文置中的錯位。
   ⚠ 只作用在實體課程內頁的課程說明（.page--courses .about-head 與它下方的內文）；
     開場帶與報名方式帶的 .simpla-pad 維持原本的深內縮不受影響。 */
.page--courses .about-head,
.page--courses .editor-block > .page-text {
    max-width: 44em;
    margin-inline: auto;
    padding-inline: 0;
}

/* 窄幅時 44em 會超過視窗，補回左右安全內距 */
@media (max-width: 1023.98px) {
    .page--courses .about-head,
    .page--courses .editor-block > .page-text {
        padding-inline: var(--section-x);
    }
}

/* ── 29-4 標題組與內文對齊同一條左邊界 ──────────────────────────────────
   ⚠ 兩者都寫 max-width: 44em，但 em 是**跟著自己的字級**算的：
     .page-text 是 14px → 44em = 616px；標題組吃的是 16px → 44em = 704px。
     於是置中之後左邊界差了 44px，看起來像沒對齊。
   ⚠ 改用同一個 px 值（＝內文那一欄的實際寬度），兩邊就會切齊同一條線。
     日後內文字級若調整，只要同步這個變數即可。 */
.page--courses {
    --about-measure: 616px;   /* ＝ .page-text 的 44em（內文 14px）換算值 */
}

.page--courses .about-head,
.page--courses .editor-block > .page-text {
    max-width: var(--about-measure);
}

/* ── 29-5 課程說明的段落逼排（2026-08-06 客戶回報「文字呈現很怪」）─────────
   ⚠ 病灶是**行距與段距倒過來了**：
     段內行距 = 2.3 × 14px ≒ 32px，段與段之間卻只有 --v2-p-gap（24px）。
     段內比段間還鬆，眼睛就分不出段落的邊界 —— 整段變成一堆各自漂浮的行。
     這一頁的原稿又是「每一句一行」（<br> 斷行），問題被放得更大。
   ⚠ 兩邊一起調：段內收到 1.9（≒27px，中文長文仍然好讀），
     段間放到 2.5rem（40px）。段間 ≈ 1.5 倍行距，是分段該有的比例。
   ⚠ 只作用在實體課程內頁的課程說明；其他頁的 .page-text 不受影響
     （站上其他地方的段落不是這種「每句一行」的原稿）。 */
.page--courses .editor-block > .page-text {
    line-height: 1.9;
}

.page--courses .editor-block > .page-text + .page-text {
    margin-top: clamp(1.75rem, 3vw, 2.5rem);
}

/* ── 29-6 課程說明改成兩欄（2026-08-06 客戶指定）─────────────────────────
   客戶：「讓該區的文字寬一點，不要置中，跟下面一樣分兩欄位呈現。」

   ⚠ 版型直接沿用下面那條灰帶的 .simpla-cols（同一個元件，DRY），
     只把**欄寬比例對調**：下面兩欄都是內容、左邊是主角（1.3 / 0.7）；
     這裡左欄只有標題組，內文才需要寬度，所以 0.72 / 1.28。
   ⚠ 上一輪的置中收窄退場：.about-head 不再吃 --about-measure、也不再 margin auto，
     內文寬度改由右欄本身決定（1920 時約 940px，比原本的 616px 寬一大截）。
   ⚠ .page-text 自帶 44em(616px) 的行長上限，在這裡會讓文字填不滿右欄 ——
     這一區把上限解掉，交給欄寬決定。 */
.page--courses .about-cols {
    grid-template-columns: minmax(0, .72fr) minmax(0, 1.28fr);
}

.page--courses .about-head,
.page--courses .about-body,
.page--courses .about-body > .page-text {
    max-width: none;
    margin-inline: 0;
    padding-inline: 0;
}

/* 標題組在窄欄裡置頂，與右欄第一行文字對齊 */
.page--courses .about-head { align-self: start; }

@media (max-width: 1023.98px) {
    /* .simpla-cols 在這個斷點會收成單欄，標題與內文自然上下排 */
    .page--courses .about-cols { grid-template-columns: minmax(0, 1fr); }
}

/* ── 29-7 課程說明：第一段移到標題底下 ＋ 行高放鬆（2026-08-06 客戶指定）───
   客戶：「這一段放標題底下，行高（調整）」→「大一點」→「現在看起來太擠」。

   ⚠ 行高的來回過程記在這裡，免得下次又被改回去：
       原本 2.3  → 太鬆，段內比段間還開，段落邊界看不出來
       改成 1.9  → 客戶回報太擠
       現在 2.15 → 段內 30px、段間 48px（1.6 倍），兩邊都留住
     段間一定要**大於**段內行距，段落才分得出來 —— 這是 2.3 那一版的病灶。
   ⚠ 標題底下那一段（.about-lead）與右欄同一個行高，兩欄看起來才是同一份內文。 */
.page--courses .editor-block > .page-text {
    line-height: 2.15;
}

.page--courses .editor-block > .page-text + .page-text {
    margin-top: clamp(2rem, 3.5vw, 3rem);
}

/* ⚠ .about-lead（標題底下那一段）已依客戶「不是，跟其他段一樣編排」退場，
     三段全部回到右欄；規則保留但目前沒有標記使用，要復原時直接沿用。 */
.page--courses .about-lead {
    margin-top: clamp(1.5rem, 3vw, 2.25rem);
}

/* ── 29-8 課程說明兩欄的垂直關係（2026-08-06 客戶指定）───────────────────
   客戶：「右邊區塊不要跟標題一樣高，大概在左邊區塊的一半位置，
          右邊這兩段間距太大。」

   ⚠ 右欄改 align-self: center —— grid 子項預設 stretch／start，所以右欄一直
     從標題那一行開始。改成 center 之後，右欄會**對齊左欄的垂直中心**，
     視覺上就落在左欄的一半高度附近。
   ⚠ 為什麼不寫死 margin-top：三堂課左欄的長度不同（問句 7～8 行不等），
     寫死的偏移在某一堂會太多、另一堂太少。center 讓它跟著內容自己走。
   ⚠ 右欄的段距單獨收一階（48px → 36px）：那兩段各自只有兩行，
     48px 的段距對兩行的段落來說太重；36px 仍然大於段內行距（30px），
     段落邊界看得出來。左欄那一段維持原設定。 */
.page--courses .about-body {
    /* ⚠ 不用 align-self: center：那是「兩欄的中心對齊」，右欄比左欄長時偏移會變成 0
         （實測《人際回應力》那一堂右欄 698px > 左欄 421px，就完全沒有下移）。
       ⚠ 改成由上往下 ＋ 一段固定比例的偏移：vw 會跟著版面縮放，
         實測三堂課都落在左欄高度的四到五成，符合客戶說的「大概一半的位置」。 */
    align-self: start;
    margin-top: clamp(3rem, 12vw, 12rem);
}

.page--courses .about-body > .page-text + .page-text {
    margin-top: clamp(1.5rem, 2.5vw, 2.25rem);
}

@media (max-width: 1023.98px) {
    /* 收成單欄之後沒有「左欄」可以對齊，偏移收掉，回到正常的由上往下 */
    .page--courses .about-body { margin-top: clamp(1.5rem, 4vw, 2.5rem); }
}

/* ═══════════════════════════════════════════════════════════════════════
   29-9 課程說明：客戶選定的版型 —— 標題在上、內文分左右兩欄（2026-08-06）
   ═══════════════════════════════════════════════════════════════════════
   ⚠ 這一區前面調過幾輪（置中單欄／左標題右內文齊頭／右欄下移一半），
     最後由客戶從四個版型中挑定這一個。**以這一節為準**，
     上面 29-3～29-8 針對 .about-cols／.about-body 的偏移與欄寬規則
     已不再套用（那些選擇器在新標記上不存在），保留只為留下調整過程。
   ⚠ 左右兩欄等寬：兩欄都是內文，沒有主從之分，1:1 才不會看起來像
     「主欄 ＋ 補充欄」。gap 沿用 .simpla-cols 那一組的節奏。
   ⚠ 兩欄的行高與段距完全相同（第 29-7 節），客戶要的「跟其他段一樣編排」。
   ═══════════════════════════════════════════════════════════════════════ */
.page--courses .about-block {
    padding-inline: var(--article-x);
}

.page--courses .about-head {
    margin-bottom: clamp(2rem, 4vw, 3rem);
}

.page--courses .about-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: clamp(2rem, 5vw, 6rem);
    align-items: start;
}

/* 內文區塊本身不再另外內縮（內縮由 .about-block 統一給） */
.page--courses .about-body,
.page--courses .about-grid > .about-col > .page-text {
    max-width: none;
    margin-inline: 0;
    padding-inline: 0;
}

@media (max-width: 767.98px) {
    /* 手機收成單欄：左欄的條列句在上、其餘段落接在下面 */
    .page--courses .about-grid { grid-template-columns: minmax(0, 1fr); }
}

/* ── 29-10 新結構的兩個補丁（2026-08-06）────────────────────────────────
   ⚠ ① 行高：第 29-7 節寫的是 `.editor-block > .page-text`（直接子代）。
        改成兩欄之後段落多包了一層 .about-col，選擇器就不再命中，
        行高退回基底的 2.3（＝客戶說過太鬆的那一版）。這裡把新路徑補上。
   ⚠ ② 內縮：第 29 節有 `.editor-block > div:not(.editor-pair)` 的 --article-x，
        .about-grid 正好符合，於是兩欄在 .about-block 的內縮之外又縮了一次
        （實測欄位起點 230px，比標題的 115px 多一倍）。這一層收掉。 */
.page--courses .about-col > .page-text {
    line-height: 2.15;
}

.page--courses .about-col > .page-text + .page-text {
    margin-top: clamp(2rem, 3.5vw, 3rem);
}

/* ⚠ 選擇器要跟第 29 節那條 `.editor-block > div:not(.editor-pair)` 同權重才蓋得掉：
     :not(.editor-pair) 也算一個 class，所以那條是 (0,3,1)。
     這裡補上元素選擇器 div 湊到同權重，靠位置在後面勝出。 */
.page--courses .about-body > div.about-grid {
    padding-inline: 0;
}

/* ── 29-11 收掉舊版型留下的偏移（2026-08-06）───────────────────────────
   ⚠ 第 29-8 節給 .about-body 的 margin-top（「右欄下移到左欄一半」那一版）
     在新結構下變成「標題與兩欄之間憑空多一大塊空白」——舊需求已被新版型取代。
   ⚠ 標題與內文的距離改由 .about-head 的 margin-bottom 統一負責（第 29-9 節）。 */
.page--courses .about-body {
    margin-top: 0;
}

/* ── 29-12 兩欄各自的疏密（2026-08-06 客戶回報）─────────────────────────
   客戶：「左邊文字段落太擠，右邊段落又太大。」

   ⚠ 兩欄的內容形狀不同，所以不能共用同一組疏密：
     左欄是**一段裡面八行**（原稿每句一行，靠 <br> 斷行）—— 決定鬆緊的是**行距**；
     右欄是**兩段各兩行** —— 決定鬆緊的是**段距**。
     先前兩欄共用同一組值，才會一邊嫌擠、一邊嫌大。
   ⚠ 左欄行距 2.15 → 2.5（30px → 35px），八行之間鬆開。
   ⚠ 右欄段距 48px → 36px（仍大於行距 30px，段落邊界看得出來）。 */
.page--courses .about-grid > .about-col:first-child > .page-text {
    line-height: 2.5;
}

.page--courses .about-grid > .about-col:last-child > .page-text + .page-text {
    margin-top: clamp(1.5rem, 2.5vw, 2.25rem);
}

/* ── 29-13 條列式那一段拆到兩欄之後的行距（2026-08-06 客戶指定）───────────
   客戶：「左邊下面四段移過去右邊。」
   ⚠ 那一段（原稿每句一行）現在被拆成左右各一半，兩半是同一種內容，
     行距必須一致，否則同一份條列在兩欄看起來會像兩種東西。
   ⚠ 所以行距改綁在內容本身的 .about-list（前一節綁在「左欄第一段」的
     位置選擇器已不適用 —— 右欄現在也有同一種內容）。
   ⚠ 右欄下方那兩段（一般段落）維持 2.15 與 36px 段距，不受影響。 */
.page--courses .about-col > .page-text.about-list {
    line-height: 2.5;
}

/* 右欄：條列半段與後面第一個一般段落之間，用段距分開（比行距大一階） */
.page--courses .about-col > .about-list + .page-text {
    margin-top: clamp(2rem, 3.5vw, 3rem);
}

/* ═══════════════════════════════════════════════════════════════════════
   37. 電子報／會員註冊／搜尋 換成全站新調性（2026-08-06 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「換電子報跟註冊會員跟搜尋頁面都一起改。」

   ⚠ 四類頁面各自的組合（延續「每個分頁風格保持不同色塊拼貼」）：
       電子報列表  開場 → 灰底訂閱帶（訂閱是這一頁的主要動作）→ 白底兩欄大卡
       電子報內容  單欄讀文（與知識內頁共用 .page--article）
       會員註冊    白底一條帶，內容維持置中收窄（表單頁的動線不該被拼貼打散）
       搜尋        開場（含搜尋框，輸入框就是主角）→ 灰底結果帶
   ⚠ 標記全部只換外層容器與色帶，欄位／按鈕／步驟／文案一個字都沒有動。
   ═══════════════════════════════════════════════════════════════════════ */

/* ── 電子報：訂閱帶與卡片 ─────────────────────────────────────────────
   ⚠ .info-panel 在第 29 節已排除文章內縮（它是有自己內距的盒狀元件），
     這裡只給它上下的節奏與最大寬度，避免在滿版色帶裡拉成一整條。 */
.page--newsletter .nl-subscribe .info-panel {
    max-width: calc(var(--container-wide) * .72);
}

/* ⚠ 2026-08-13 客戶「電子報搜尋放右邊」，確認為訂閱表單，隨後補充「那一整塊
   包訂閱欄位都移到右邊含」「不是只有訂閱那一欄」——不是只有 Email／訂閱鈕，
   「註冊會員訂閱：登入／註冊」也要一起移到右邊；Email＋訂閱鈕、註冊會員
   說明、登入／註冊按鈕三段一起包在 .nl-subscribe-right，整塊上下堆疊、
   靠右對齊。
   ⚠ 再補一輪「這一個也移過去（指標題）」「右邊」——原本的小標題（.page-h3）
   要一起靠右；接著附截圖直接指名「這段刪掉」，小標整段從標記移除
   （見 newsletter_list.php 的說明），這裡也跟著把小標專屬的 margin 覆寫
   一併拿掉，不留死選擇器。.nl-subscribe-head 現在只剩 .nl-subscribe-right
   一個可見子元素，justify-content:flex-end 讓它貼右邊界。
   手機版收回成上下堆疊。 */
.nl-subscribe-head {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    align-items: flex-start;
    gap: 1.5rem 2rem;
}

/* 2026-08-21 客戶指定：訂閱表單搬進開場①這一列（見 newsletter_list.php
   的說明），跟「電子報」H1 齊頭對齊——共用元件 .simpla-open-body 原本是
   align-items:flex-end（底部對齊，給「文字在左、箭頭在右」那組用的），
   這裡另外疊一條 .page--newsletter 專屬覆寫改成 flex-start（頂部對齊），
   不動共用規則本身（knowledge_list.php 同一個共用元件不受影響）。
   ⚠ 客戶後來指定「改回最一開始小卡陳列的形式，包含...標題位置」——
     開場①改回原本沒有併入訂閱表單的版本（見 newsletter_list.php），
     這條覆寫對還原後的版面沒有作用對象了，comment 跟規則本身都留著
     沒刪，只是註解掉不生效；要切回 2026-08-21 併列版本時解除註解即可。
.page--newsletter .simpla-open-body {
    align-items: flex-start;
}
*/

/* 訂閱表單原本靠 .band--grey 那一整條灰帶取得「跟其它內容區隔」的效果，
   搬進①之後不再有整條灰帶，先改成自己這一小塊壓淺灰底＋底線；2026-08-21
   客戶第二輪回饋「整個區域不要灰色背景」——先前以為只有輸入框本身要拿掉
   背景（見下面 .page--newsletter .form-input 那條），客戶截圖指出範圍
   其實是整個 .nl-subscribe-head，這裡直接拿掉 background 宣告，只留
   底線（padding／border-bottom 沒被點名要拿掉，維持區隔效果）。
   ⚠ 客戶後來指定改回最一開始形式，訂閱表單搬回獨立的②灰帶 section
     （灰底改回吃 .band--grey 那條共用規則，不是這裡），這條 padding／
     border-bottom 覆寫註解掉不生效，comment 跟規則留著沒刪。
.nl-subscribe-head {
    padding: clamp(1.25rem, 2.5vw, 1.75rem);
    border-bottom: 1px solid var(--rule-on-light);
}
*/

/* ⚠ 客戶「訂閱輸入條請跟搜尋鍵樣式一樣」「現在太短太窄」——原本容器只給
   28rem（跟輸入框固定欄寬 col-lg-5 兩層一起夾窄），改成跟搜尋頁
   （search.php 的 .col-lg-7 那一層）同一個量級的寬度，輸入框改用
   col-12 col-sm（自動撐滿）之後才有空間真的撐開，不會被容器先卡住。 */
.nl-subscribe-right {
    flex: 0 1 42rem;
}

/* ⚠ 2026-08-21 訂閱表單併入開場①那一列之後，上面 42rem 的 flex-basis
   會被隔壁 .simpla-open-text（flex-grow:1，貪心搶剩餘空間）壓縮到剩
   ~320px，輸入框只剩 183px，曾經加過 min-width 當底線（26rem，
   ≥992px 再加碼到 34rem）撐開它。客戶後來指定改回最一開始形式，訂閱
   表單搬回獨立②灰帶，不再跟標題共用同一個彈性列，這個擠壓問題本身
   不存在了，min-width 底線註解掉不生效，comment 跟數字都留著沒刪：
@media (min-width: 992px) {
    .nl-subscribe-right {
        min-width: 34rem;
    }
}
*/

.nl-subscribe-form {
    margin-top: 0;
}

@media (max-width: 767.98px) {
    .nl-subscribe-head {
        flex-direction: column;
        align-items: flex-start;
    }

    .nl-subscribe-right {
        flex-basis: auto;
        width: 100%;
    }
}

/* ⚠ 客戶「電子報輸入欄位不要加背景」，後來又指定「改回最一開始小卡陳列
   的形式，包含...輸入欄位那些」——訂閱表單搬回獨立②灰帶之後，輸入框
   背景要不要透明的前提（跟標題共用一列、要露出外層灰底）已經不存在，
   這條 .page--newsletter .form-input 覆寫註解掉不生效，comment 跟規則
   都留著沒刪；.form-input 本身共用規則（白底）沒有被動過。
.page--newsletter .form-input {
    background: transparent;
}
*/

.page--newsletter .band > .section-bleed-inner > .pcard-grid,
.page--newsletter .band > .section-bleed-inner > .pager {
    padding-inline: var(--section-x);
}

/* ⚠ 上面這條原本還多兩個選擇器 .nl-feature-layout／.nl-feature-index
   （2026-08-20 editorial 大卡改版新增的直屬子元素，補進同一條規則避免
   貼齊視窗邊緣，實測細節見 [[reference-onedaylisten-section-bleed-padding-opt-in]]）。
   客戶指定改回最一開始小卡陳列形式，那兩個元素已經沒有 markup 會輸出，
   這裡跟著拿掉那兩行選擇器，恢復成只有 pcard-grid／pager 兩個——不是
   刪內容，是這條規則的「服務對象」本來就該對應目前實際輸出的 markup，
   .nl-feature-* 的完整 CSS 定義（下面「電子報 一次一期 editorial 大卡」
   整節）沒有被刪，只是沒有 markup 可以匹配到，自然不生效，不需要另外
   註解掉。要切回 editorial 版本時，把 .nl-feature-layout／
   .nl-feature-index 兩行選擇器加回上面那條規則即可。 */

/* 卡片列表改用線稿分隔，不用白底個別框（2026-08-20 客戶「小卡不要一個一個
   框，用兩條頁面寬水平線連上下，一個小卡兩條垂直線」；同日追加修正
   「每張小卡都要有自己的兩條水平線，不是全部卡片共用兩條；把卡片原本的
   邊框線刪掉」；同日第三輪定案（附圖選項確認）：水平線要「每一列貫通
   （含中間欄距）」——並排的兩張卡共用一條頂線／一條底線，橫跨整列含中間
   欄距，不是每張卡各自截斷成兩小段；垂直線維持「每張卡自己左右各一條」
   不變，兩種線不是同一層。
   ⚠ .pcard／.pcard-grid 是課程／知識／電子報／會員收藏共用元件（見本檔
     「4. 內頁卡片 .pcard」該節開頭的清單），這裡整段疊在 .page--newsletter
     底下，只影響電子報列表頁自己，其餘頁面的白底卡片維持原樣不受影響。
   ⚠ 垂直線（border-left／border-right）是單張卡自己的事，跟欄距無關，
     直接掛在 .pcard 身上最單純，兩個斷點都適用。
   ⚠ 水平線「貫穿整列」不能掛在 .pcard 身上：.pcard 只有自己那一欄的
     寬度，border-top 最多畫到自己卡片邊緣，中間欄距那一段天生沒有東西
     可以畫線；.pcard 本身還有 overflow:hidden（見「4. 內頁卡片」共用
     定義），就算硬把線做成子元素撐出去也會被裁掉。改掛在 .pcard-grid
     的 li 身上（li 沒有 overflow 限制），只在「每列第一張卡」
     （li:nth-child(odd)）疊 ::before／::after，寬度用
     calc(200% + 欄距) 從自己左緣往右延伸——兩欄是 1fr／1fr 等寬，
     「自己寬度的兩倍」剛好等於「兩張卡寬度總和」，不用量第二張卡的寬度
     也能精準跨過中間欄距、貫穿整列；第二張卡不用另外處理，視覺上已經
     被第一張卡延伸出去的線蓋過去（z-index:1 蓋過去，避免被第二張卡的
     圖片擋住）。欄距數字 clamp(1.5rem,2.5vw,2.5rem) 抄自 .pcard-grid
     自己的 column-gap（「4. 內頁卡片」那節的 gap shorthand 第二個值，
     沒有拆成獨立自訂屬性可以 var() 共用；之後若改那邊的欄距，這裡要
     記得同步）。
   ⚠ 這個「跨欄」技巧只在兩欄並排（≥768px）時有意義；≤767.98px
     （.pcard-grid--2 收成單欄，見「15. RWD 收斂」那節）一列只有一張卡，
     「貫穿整列」跟「卡片自己」是同一件事，直接在 .pcard 身上掛
     border-top／bottom 即可，不需要疊 pseudo-element。
   ⚠ 線的顏色沿用 .course-progress 那條進度線同一階（--foreground /.16），
     與下面複用的 .course-pageno／.course-nav 分頁同一套視覺語言。 */
.page--newsletter .pcard {
    background: transparent;
    border-radius: 0;
    border-left: 1px solid hsl(var(--foreground) / .16);
    border-right: 1px solid hsl(var(--foreground) / .16);
}

/* 2026-08-20 客戶回報「底下兩排的水平線都連到一半就斷了」——真正根因（跟
   一開始判斷的 z-index／stacking-context 無關，那是誤判，下面
   li:nth-child(odd) 補的 z-index:2 留著但沒解決問題）：
   home.js 捲動進場動畫給每張卡的 <li> 掛 .js-reveal.is-in，這組 class
   實際生效的定義（本檔「捲動進場（scroll reveal）」那節，第二次宣告，
   蓋過檔案更前面第一次的 opacity/transform 版本）是：
       .js-reveal.is-in { clip-path: inset(-14px); }
   `inset(-14px)` 只是比 0 多留 14px 安全邊（原本是為了不裁到
   :focus-visible 的 outline-offset:6px，見同一節開頭的說明），本質上
   還是一個**貼齊卡片自己邊界的裁切框**——任何超出這 14px 的溢出內容都會
   被裁掉。第一排的 <li> 沒有這個 class（首屏可見，沒有參與進場動畫），
   所以線完全不受影響；第二排起全部掛了這個 class，我疊在 li 身上的
   ::before／::after 一旦超出 14px（貫穿整列的線遠遠超過），就被自己
   宿主 li 的 clip-path 攔腰裁掉——數字（getComputedStyle 的 left/width）
   讀起來完全正常，因為 clip-path 不影響這些屬性的「計算值」，只影響
   「最終畫面顯不顯示」，這也是為什麼上一輪純讀值比對看不出任何差異。
   ⚠ 修法：進場動畫結束、.is-in 定案之後，這裡的 clip-path 對電子報卡牆
     已經沒有存在理由（貼邊裁切原本要保護的東西——:focus-visible 外框——
     卡片本身沒有可聚焦的裁切邊界問題），疊一條更高特異度（3 個 class）
     的規則蓋掉，只在這一頁生效，不動 .js-reveal.is-in 共用定義本身
     （home.php／knowledge_detail.php 等其它進場動畫元素不受影響）。
   ⚠ 桌機、手機都要覆寫：兩個斷點的線都靠同一個 <li> 溢出技巧，14px 的
     裁切框在兩邊都不夠用（--section-x 最小也有 20.8px，已經超過 14px）。 */
.page--newsletter .pcard-grid > li.is-in {
    clip-path: none;
}

/* 手機單欄時「一列」就是「一張卡」，跟桌機兩欄版同一個「連到整個頁面寬、
   左右不留邊界」原則——不能直接在 .pcard 身上掛 border-top/bottom（那只會
   停在卡片自己內縮過的寬度），改用跟桌機同一招：掛在 li 身上、往左右各
   出血一個 --section-x。 */
@media (max-width: 767.98px) {
    .page--newsletter .pcard-grid > li {
        position: relative;
    }

    .page--newsletter .pcard-grid > li::before,
    .page--newsletter .pcard-grid > li::after {
        content: "";
        position: absolute;
        left: calc(-1 * var(--section-x));
        z-index: 1;
        width: calc(100% + 2 * var(--section-x));
        height: 1px;
        background: hsl(var(--foreground) / .16);
        pointer-events: none;
    }

    .page--newsletter .pcard-grid > li::before { top: 0; }
    .page--newsletter .pcard-grid > li::after  { bottom: 0; }
}

@media (min-width: 768px) {
    /* 2026-08-20 客戶回報「底下兩排的水平線都連到一半就斷了」——第一輪
       診斷懷疑是 stacking context（第 2 排起的 <li> 被 home.js 掛
       .js-reveal.is-in，will-change 會讓元素無條件建立新 stacking
       context），補了下面這條 z-index:2。實測加了之後問題still在——
       真正根因其實是 clip-path，不是 stacking，完整說明與正式修法在
       .page--newsletter .pcard-grid > li.is-in { clip-path:none } 那條
       規則（往上找「底下兩排的水平線都連到一半就斷了」同一句回報的第二段
       說明）。這裡的 z-index:2 留著當第二道保險（跟鄰卡比 stacking 順序
       時用明確 z-index 而不是賭 DOM 順序，理論上更穩，但單獨這條測起來
       不夠，clip-path:none 才是真正解掉的那一條）。 */
    .page--newsletter .pcard-grid > li:nth-child(odd) {
        position: relative;
        z-index: 2;
    }

    .page--newsletter .pcard-grid > li:nth-child(odd)::before,
    .page--newsletter .pcard-grid > li:nth-child(odd)::after {
        content: "";
        position: absolute;
        left: calc(-1 * var(--section-x));
        z-index: 1;
        width: calc(200% + clamp(1.5rem, 2.5vw, 2.5rem) + 2 * var(--section-x));
        height: 1px;
        background: hsl(var(--foreground) / .16);
        pointer-events: none;
    }

    .page--newsletter .pcard-grid > li:nth-child(odd)::before { top: 0; }
    .page--newsletter .pcard-grid > li:nth-child(odd)::after  { bottom: 0; }
}

/* 分頁「01 / 03」不要方框（2026-08-20 客戶附截圖指定樣式）。站上已有現成
   元件：knowledge_list.php 2026-08-18 同一輪做過一次一模一樣的置換
   （.pager／.pager-item 換成 .course-meta／.course-pageno／.course-nav／
   .course-arrow--prev|next，見該檔案的說明），這裡直接複用同一組 class，
   不新增 CSS 元件。
   ⚠ .course-nav{display:none} 是條裸規則（本檔「近期課程箭頭鈕改成圓框
     游標」那節，2026-08-03 加的，只為了 home.php 近期課程／
     knowledge_detail.php 相關文章兩處「拖曳取代按鈕」），沒有限定頁面，
     這裡疊高特異度蓋回來，不動裸規則本身（另外兩處仍要維持關閉）。
   ⚠ .course-meta 的 justify-content:center 是同一條裸規則帶的，這裡不用
     額外覆寫——本頁原本的 .pager 就是置中（.pager{justify-content:
     center}），裸規則的置中剛好跟這頁要的一致，順勢沿用。 */
.page--newsletter .course-nav {
    display: flex;
}

/* ⚠ 2026-08-13 客戶「電子報右邊訂閱文字區請上移，離標題近一點」——訂閱帶
   （灰底）跟開場的標題／副標之間隔了兩層 --section-y（開場的下留白 ＋
   灰帶自己的上留白），全站每一條 .site-section 都吃這個 token，不能直接
   改它（會連動全站每一區）。這裡只把「這一條灰帶」的上留白單獨收窄，
   跟後面卡片列表那條 .band 的下留白不共用同一個選擇器，互不影響。 */
.page--newsletter .band--grey {
    padding-top: clamp(1.25rem, 2.5vw, 2rem);
}

/* 2026-08-21 客戶指定：卡牆桌機預設 4 篇／手機預設 3 篇，其餘用底下
   箭頭＋數字分頁切換，「數字不要框線跟背景」——.pager-item 是全站共用
   元件（見本檔「6. 分頁」節），不能直接改共用規則，這裡用
   .page--newsletter 限定範圍只讓這頁的頁碼數字變成純文字；箭頭
   （.pager-item--arrow）沒被點名要拿掉框線，繼續吃共用樣式，不用另外
   覆寫。互動邏輯（隱藏非當頁卡片、is-current 切換）見
   newsletter_list.php 檔尾新增的 <script>。
   ⚠ 拿掉 border／background 之後，原本靠 .is-current 的實心底色分辨
     「目前頁」的視覺線索就沒了，改用文字顏色區分（目前頁深色／其它頁
     muted 灰），跟頁面其它地方用顏色分深淺的慣例一致（例如大卡右下角
     篇名的淺灰、本檔多處 .is-current／.is-disabled 的做法）。
   ⚠ <button> 原生會帶一點 UA 預設樣式（例如 Safari 的內距／外觀），
     appearance:none 清掉，其餘（padding／font／line-height）沿用共用
     .pager-item 規則已經明寫的值，不用重複宣告。 */
.page--newsletter .pager-item--num {
    border: none;
    background: none;
    appearance: none;
    color: hsl(var(--muted-foreground));
}

.page--newsletter .pager-item--num:hover,
.page--newsletter .pager-item--num:focus-visible {
    border: none;
    background: none;
    color: hsl(var(--heading));
}

.page--newsletter .pager-item--num.is-current {
    border: none;
    background: none;
    color: hsl(var(--heading));
    font-weight: var(--fw-bold, 700);
}

.page--newsletter .pager-item.is-disabled {
    opacity: .35;
    pointer-events: none;
}

/* ═══════════════════════════════════════════════════════════════════════
   電子報 一次一期 editorial 大卡（2026-08-20 客戶指定大改版，取代原本的
   兩欄卡牆＋分頁；舊卡牆／舊分頁的 CSS 全部保留在上面「卡片列表改用線稿
   分隔」與「頁碼不要方框」兩節，沒有刪——只是新版不再有任何元素套用那組
   class，因為對應的舊 markup 本身也整段搬進 view 檔案的 if(false) 死碼區
   了（見 newsletter_list.php 的說明），不會再輸出到頁面。
   ⚠ 字級／字距／顏色／圓角／間距全部沿用既有 var(--token)（逐條在下面
     註明對應到哪一個既有角色），新增的只有版面結構本身
     （grid-template-areas／少數 clamp() 尺寸／過場動畫）。 ═══════════════ */

.nl-feature-layout {
    --nl-feature-gap: clamp(2rem, 4vw, 4rem);

    display: grid;
    grid-template-columns: minmax(0, 1.1fr) minmax(0, 1.3fr) auto;
    grid-template-areas:
        "title  about  num"
        "title  link   num"
        "media  media  media"
        "meta   meta   meta";
    column-gap: var(--nl-feature-gap);
    row-gap: clamp(1.5rem, 3vw, 2.5rem);
    align-items: start;
}

.nl-feature-title-col { grid-area: title; }

/* 英文小標——與 .page-head-en（本頁開場①用的那顆）同一組視覺值，但獨立
   宣告不共用 class：.page-head-en 是全站十幾個頁面共用的類別，散落在
   別處的頁面專屬覆寫不易一次查完，這裡的字級/字距/顏色改成自己單獨
   宣告，不會被本檔其它地方的 .page-head-en 覆寫波及（見
   reference-onedaylisten-border-shorthand-hidden-rule 同一類教訓）。 */
.nl-feature-eyebrow {
    margin: 0 0 1.25rem;
    font-family: var(--font-eyebrow);
    font-size: var(--text-meta);
    line-height: var(--lh-meta);
    font-weight: var(--fw-normal);
    letter-spacing: var(--tracking-meta);
    color: hsl(var(--slate));
}

.nl-feature-title {
    margin: 0;
    /* 與 .page-title（本頁開場的 H1）同一組值——「比卡片標題大很多，但
       不比頁面自己的最大標題還大」，既有 token 裡剛好就是這一階，不必
       另開一個更大的字級（客戶原文：「不要太大」）。 */
    font-size: var(--text-section);
    line-height: var(--lh-section);
    font-weight: var(--fw-heading);
    letter-spacing: var(--tracking-display);
    color: hsl(var(--heading));
    overflow-wrap: break-word;
}

/* 2026-08-21 客戶指定：「期數」跟「文章名」分段、不要在同一行——原本是
   一串「第 24 期｜把話說到心裡的三個練習」，寬度不夠時單純折行折在隨機
   位置；現在資料上還是同一個字串（SSOT 沒變，$letters 沒有多切一個
   欄位），只在顯示層用 ｜ 這個既有的分隔符號切成兩個 <span>，各自
   display:block 各佔一行，不是「折行折到那裡」而是「本來就設計成兩行」。 */
.nl-feature-title-issue,
.nl-feature-title-name {
    display: block;
}

.nl-feature-title-name {
    margin-top: .25em;
}

.nl-feature-about-col { grid-area: about; }

.nl-feature-about-label {
    margin: 0 0 1rem;
    font-family: var(--font-eyebrow);
    font-size: var(--text-meta);
    line-height: var(--lh-meta);
    font-weight: var(--fw-normal);
    letter-spacing: var(--tracking-meta);
    color: hsl(var(--slate));
}

.nl-feature-about-text {
    margin: 0;
    max-width: 32em;
    font-size: var(--text-body);
    line-height: var(--lh-body);
    font-weight: var(--fw-light);
    letter-spacing: var(--tracking);
    color: hsl(var(--muted-foreground));
}

/* 「閱讀本期電子報」——視覺語言比照 .pcard-go（卡牆版的「→」提示），
   獨立宣告是因為觸發 hover 的對象不同：.pcard-go 靠 .pcard:hover 整張卡
   觸發，這裡是獨立連結，自己 :hover／:focus-visible 就要生效。 */
.nl-feature-link {
    grid-area: link;
    align-self: start;
    display: inline-flex;
    align-items: center;
    gap: .6rem;
    margin-top: 1.5rem;
    font-size: var(--text-meta);
    line-height: 1.6;
    font-weight: var(--fw-normal);
    letter-spacing: var(--tracking-meta);
    color: hsl(var(--foreground) / .55);
    text-decoration: none;
    transition: color var(--transition);
}

.nl-feature-link::after {
    content: "→";
    font-family: var(--font-en);
    font-size: 1.125rem;
    line-height: 1;
    transition: transform var(--dur-hover) var(--ease-out);
}

@media (hover: hover) {
    .nl-feature-link:hover,
    .nl-feature-link:focus-visible { color: hsl(var(--foreground)); }
    .nl-feature-link:hover::after,
    .nl-feature-link:focus-visible::after { transform: translateX(var(--hover-nudge)); }
}

/* 巨大流水號：字體沿用 --font-en（Figma 定義的「課程編號 01/02/03」同一顆
   字體角色），字距沿用 --tracking-meta（token 註解本來就寫「日期／編號／
   英文小標」適用這一階）——尺寸是頁面唯一新增的原始值，因為全站沒有
   「大字級流水號」這個角色的既有 token 可以借；克制在 32–72px 之間、
   壓低字重、壓低不透明度，對應客戶「large but minimal」「不要蓋過標題／
   圖片」的要求。 */
/* ⚠ margin-right 是閃避全站固定浮動客服鈕（.wf-float，position:fixed，
   貼視窗右下角，任何頁面捲動時都在同一個視窗相對高度，跟上面
   .nl-feature-nav 改置中同一個坑）。修法分兩層：
     1. 根因是 .nl-feature-layout／.nl-feature-index 一開始沒有套到
        --section-x 內縮（見「.section-bleed-inner > .pcard-grid, .pager」
        那條規則的新增說明），已經補上，這兩個元件現在跟全站其它直屬子
        元素一樣有正確的左右內縮。
     2. 但補上內縮還不夠——實測 .wf-float 的左緣離真實視窗右緣約 63–75px
        （不同斷點量出來的數字不完全一樣，.wf-float 本身可能也有響應式
        差異），桌機 --section-x（72–88px）大致夠、平板 --section-x
        只有 24px 遠遠不夠，兩層都用實測數字反推、不是憑理論算，見平板
        斷點另外補的 margin-right 覆寫。 */
.nl-feature-num {
    grid-area: num;
    align-self: end;
    justify-self: end;
    margin: 0;
    margin-right: 1rem;
    font-family: var(--font-en);
    font-size: clamp(2.5rem, 5vw, 4.5rem);
    line-height: 1;
    /* ⚠ 2026-08-21 客戶「數字用細體」——原本寫 var(--fw-light) 量出來是
       350，不是預期的 100：跟本檔既有教訓同一個坑（見「選單請用細體」
       那次，2026-08-17）——--fw-light 這顆 token 在 .page 範圍內被重新
       定義成 350（本頁 <div class="page page--v2 page--simpla
       page--newsletter"> 最外層就是 .page，這顆數字整個長在裡面），
       :root 的 100 只在 .page 範圍**外**才讀得到。贏了特異度也沒用，
       因為讀到的 --fw-light 值本身已經不是「細」——這裡改成直接寫死
       100，繞開這個坑，不透過已經被污染的 token。 */
    font-weight: 100;
    letter-spacing: var(--tracking-meta);
    color: hsl(var(--foreground) / .32);
    font-variant-numeric: tabular-nums;
}

/* 2026-08-21 客戶追加指定：圖片改矮一點（21:9 → 3:1），兩欄比例 3:7 不變
   （grid-template-columns: 3fr 7fr 就是比例本身，不用換算成 30%/70%；
   同一個 grid row 兩欄預設 align-items:stretch，「同高度」不用額外宣告）。 */
/* 2026-08-21 客戶追加指定：高度再縮一半（3:1 → 6:1），比例本身沒有再
   另外指定，兩欄 3:7 維持不變。 */
.nl-feature-media {
    grid-area: media;
    display: grid;
    grid-template-columns: 3fr 7fr;
    gap: 1rem;
    aspect-ratio: 6 / 1;
}

.nl-feature-media-narrow,
.nl-feature-media-wide {
    position: relative;
    overflow: hidden;
    border-radius: var(--radius-media);
    background: hsl(var(--brand-accent) / .12);
}

.nl-feature-media picture,
.nl-feature-media img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: 50% 50%;
}

/* 窄邊＝同一張圖放大看局部（客戶指定），寬邊維持原比例（上面共用規則
   的預設 cover，不額外放大）。scale 用 transform 而非換更大的來源圖，
   父層 overflow:hidden 負責裁掉超出的部分。 */
.nl-feature-media-narrow img {
    transform: scale(1.6);
    transform-origin: center;
}

/* ── 上一期／下一期：疊在寬邊圖片右下角 ──────────────────────────────
   2026-08-21 客戶追加指定：箭頭移到圖片上、貼右下角，方形背景（不是原本
   行內文字列的無背景 ghost 樣式——疊在照片上沒有背景會看不清楚），圖示
   只留方向（純 ‹ › 角括號，不是帶長桿的 ← →）。跟 .course-arrow 徹底切開
   （不再借用），因為視覺需求已經完全不同，硬借共用規則只會互相牽制。 */
.nl-feature-media-nav {
    position: absolute;
    right: 1rem;
    bottom: 1rem;
    z-index: 2;
    display: flex;
    gap: .5rem;
}

.nl-feature-media-arrow {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 2.25rem;
    height: 2.25rem;
    background: hsl(var(--background) / .85);
    color: hsl(var(--foreground));
    font-family: var(--font-en);
    font-size: 1.125rem;
    line-height: 1;
    text-decoration: none;
    transition: opacity var(--transition), transform var(--dur-hover) var(--ease-out);
}

/* 純角括號＝只留方向，不是「一條線加箭頭」的完整 → 符號。 */
.nl-feature-media-arrow--prev::before { content: "\2039"; }
.nl-feature-media-arrow--next::before { content: "\203A"; }

.nl-feature-media-arrow.is-disabled {
    opacity: .35;
    pointer-events: none;
}

@media (hover: hover) {
    .nl-feature-media-arrow--prev:not(.is-disabled):hover { transform: translateX(calc(var(--hover-nudge) * -1)); }
    .nl-feature-media-arrow--next:not(.is-disabled):hover { transform: translateX(var(--hover-nudge)); }
}

/* ── 切換動畫：套用 course_detail.php Hero 的「方塊遮罩」既有寫法 ─────
   2026-08-21 客戶指定「遮罩動畫請用 hero 圖的寫法套用」——不是自己另外
   發明一套（上一版用 mask-image／conic-gradient／steps() 做的棋盤格
   已經整段換掉，不是疊加）。course_detail.php 的 Hero 用的其實不是
   mask-image，是 6 塊真的 <span>（見 markup 的 $nlHeroMasks／.cd-hero-
   mask-grid 那組），4 欄 × 2 列棋盤格排版（grid-template-areas
   "a b c ." ". d e f"，刻意留右上、左下兩格永遠不蓋——那是那組設計本身
   的簽名效果，不是漏寫），每塊各自用 scaleX 或 scaleY 收合／展開，
   永遠只用同一個 transform 函式（同一塊不會一下 scaleX 一下 scaleY），
   瀏覽器內插才會穩定可預期。

   這裡的行為跟 course_detail 的單次「進場只播一次」不一樣——電子報每次
   切換期數都要重播，所以：
     · 平常（rest）＝展開狀態（scale(0)，照片完整可見）。
     · .is-switching（切換開始，還沒換圖）＝全部同時蓋住（scale(1)，
       delay 統一歸零，見 JS 的 setMaskDelays('0ms')）。
     · JS 把 .is-switching 拿掉的瞬間＝回到 rest 規則＝觸發「展開」動畫，
       這裡才吃 560ms＋90ms 錯開的 delay（JS 的 staggerMaskDelays()，
       跟 course_detail 完全同一個節奏），不是另外開一個 class 觸發。
   ⚠ 底色用 --brand-accent/.12（這個版位自己沒圖時的底色），不是照抄
     course_detail 的 --surface-grey——那顆是「融進頁面底色」的語意，
     這裡的角色不同，token 選對的，不是照搬數值。 */
.nl-feature-media-mask-grid {
    position: absolute;
    inset: 0;
    z-index: 1;
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    grid-template-rows: repeat(2, 1fr);
    grid-template-areas: "a b c ." ". d e f";
    gap: 0;
    pointer-events: none;
}

/* ⚠ 收合／展開用 clip-path:inset()，不是 course_detail.php 原本的
   transform:scaleX/scaleY——視覺效果完全一樣（同一塊往同一個方向收合／
   展開），只是換一個屬性做同一件事，排查自動化截圖驗證時偶爾出現的
   殘影過程中順手一起試過，兩種寫法各自的行為都正確（getComputedStyle
   全程正確、真人操作／任何一次真實互動後畫面都是乾淨的），殘影本身後來
   確認是自動化截圖工具的合成快照時機問題，不是這兩種寫法哪個有 bug
   （細節見 newsletter_list.php 的 nudgeRepaint() 說明）。留用 clip-path
   純粹是排查當下已經寫好、也沒有理由再切回去。
   inset(top right bottom left) 是「從這個方向裁掉多少」，收合方向對應
   course_detail 原本各塊的 transform-origin：
     a/d（原 origin:left）  → 裁右側：inset(0 100% 0 0)
     c/f（原 origin:right） → 裁左側：inset(0 0 0 100%)
     b（原 origin:top）     → 裁下側：inset(0 0 100% 0)
     e（原 origin:bottom）  → 裁上側：inset(100% 0 0 0)
   全部裁光＝完全不可見＝跟 scale(0) 同一個視覺終點；inset(0)＝完全不裁
   ＝整塊蓋住＝跟 scale(1) 同一個視覺終點。 */
.nl-feature-media-mask {
    background: hsl(var(--brand-accent) / .12);
    margin: -1px; /* 消除格線間的次像素縫隙 */
}

.nl-feature-media-mask--a,
.nl-feature-media-mask--d { clip-path: inset(0 100% 0 0); }
.nl-feature-media-mask--c,
.nl-feature-media-mask--f { clip-path: inset(0 0 0 100%); }
.nl-feature-media-mask--b { clip-path: inset(0 0 100% 0); }
.nl-feature-media-mask--e { clip-path: inset(100% 0 0 0); }

.nl-feature-media-mask--a { grid-area: a; }
.nl-feature-media-mask--b { grid-area: b; }
.nl-feature-media-mask--c { grid-area: c; }
.nl-feature-media-mask--d { grid-area: d; }
.nl-feature-media-mask--e { grid-area: e; }
.nl-feature-media-mask--f { grid-area: f; }

@media (prefers-reduced-motion: no-preference) {
    .nl-feature-media-mask {
        transition: clip-path 560ms cubic-bezier(.76, 0, .24, 1);
    }

    /* 蓋住只需要快、整齊，不用錯開節奏；展開才是重點，交給上面的
       rest 規則（560ms＋JS 設的 90ms 級距 delay）處理。 */
    .nl-feature-layout.is-switching .nl-feature-media-mask {
        transition: clip-path 180ms var(--ease-out);
        clip-path: inset(0);
    }
}

/* metadata（日期／分類／期數）＋這一期的篇名，2026-08-21 客戶指定：
   置中頁碼刪掉（箭頭搬到圖片上，見上面 .nl-feature-media-nav），改成
   只留這一期的篇名，貼右邊跟左邊的「日期」同一行對齊——margin-left:auto
   把篇名推到 flex row 剩下的空間盡頭，天然同一條基線，不用另外調
   top/vertical-align。 */
.nl-feature-bottom-row {
    grid-area: meta;
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 1rem 2rem;
    padding-top: clamp(1.5rem, 3vw, 2rem);
    border-top: 1px solid var(--rule-on-light);
}

.nl-feature-meta {
    display: flex;
    flex-wrap: wrap;
    gap: clamp(2rem, 4vw, 4rem);
    margin: 0;
}

.nl-feature-meta-item {
    display: flex;
    flex-direction: column;
    gap: .5rem;
}

.nl-feature-meta-item dt {
    margin: 0;
    font-family: var(--font-eyebrow);
    font-size: var(--text-meta);
    letter-spacing: var(--tracking-meta);
    color: hsl(var(--slate));
}

.nl-feature-meta-item dd {
    margin: 0;
    font-size: var(--text-body);
    letter-spacing: var(--tracking);
    color: hsl(var(--heading));
}

/* 2026-08-21 客戶指定改淺灰色——跟這一頁其它次要標籤同一個 token
   （.nl-feature-eyebrow／.nl-feature-meta-item dt 都是 --slate），不是
   隨手挑一個灰階數字，這一頁「次要文字」的顏色語言統一用這顆。 */
.nl-feature-bottom-title {
    margin: 0 0 0 auto;
    max-width: 24em;
    font-size: var(--text-body);
    letter-spacing: var(--tracking);
    color: hsl(var(--slate));
    text-align: right;
    overflow-wrap: break-word;
}

/* 期數索引——2026-08-21 客戶指定「最底下只留該期篇名，不要全部放出來」，
   原本可見的清單改成純資料容器（見 markup 說明），display:none 就是
   唯一需要的樣式；原本清單項目的視覺樣式（.nl-feature-index-item／-no／
   -title／hover／is-current 配色）連帶一起沒有存在的理由，不留著佔位——
   這批 class 名稱與 data-* 屬性本身還在 markup 上，JS 照樣讀得到，只是
   不需要再幫它們定義「看起來要長怎樣」。 */
.nl-feature-index {
    display: none;
}

/* ── 切換過場：淡出＋小幅位移，180ms 各一段（進場／退場合計約 360ms），
     落在客戶指定的 250–400ms 區間內；只在使用者沒有要求減少動態時才播
     （prefers-reduced-motion:no-preference，跟本檔「捲動進場」那節同一個
     guard 寫法），時長要與 newsletter_list.php 的 SWITCH_MS 常數對齊。 ── */
@media (prefers-reduced-motion: no-preference) {
    .nl-feature-title-col,
    .nl-feature-about-col,
    .nl-feature-link,
    .nl-feature-num,
    .nl-feature-bottom-row {
        transition: opacity 180ms var(--ease-out), transform 180ms var(--ease-out);
    }

    .nl-feature-layout.is-switching .nl-feature-title-col,
    .nl-feature-layout.is-switching .nl-feature-about-col,
    .nl-feature-layout.is-switching .nl-feature-link,
    .nl-feature-layout.is-switching .nl-feature-num,
    .nl-feature-layout.is-switching .nl-feature-bottom-row {
        opacity: 0;
        transform: translateY(6px);
    }
}
/* ⚠ .nl-feature-media 不在上面這組清單裡——圖片的切換動畫改用遮罩擦除
   （見上面 .nl-feature-media-narrow img／.nl-feature-media-wide img 那條
   規則），不是透明度＋位移，兩套動畫疊在同一張圖上會互相干擾。 */

/* ── 平板（768–991.98px）：標題／摘要改上下堆疊，編號貼標題右上（客戶：
     「編號可以移到標題附近或右上角」）。────────────────────────────── */
@media (min-width: 768px) and (max-width: 991.98px) {
    .nl-feature-layout {
        grid-template-columns: 1fr auto;
        grid-template-areas:
            "title  num"
            "about  num"
            "link   link"
            "media  media"
            "meta   meta";
    }

    .nl-feature-meta {
        gap: 1.5rem 2.5rem;
    }

    /* --section-x 在這個斷點只有 24px，遠不夠閃過 .wf-float（見上面
       .nl-feature-num 本體那則註解的第 2 層說明），實測補到 4rem 才淨空。 */
    .nl-feature-num {
        margin-right: 4rem;
    }
}

/* ── 手機（≤767.98px）：單欄，依客戶指定順序＝編號／標題／摘要／圖／
     metadata／閱讀連結／上下期切換。用 flex order 重排，不複製 DOM。──── */
@media (max-width: 767.98px) {
    .nl-feature-layout {
        display: flex;
        flex-direction: column;
        row-gap: clamp(1.5rem, 5vw, 2.5rem);
    }

    .nl-feature-num       { order: 1; align-self: flex-start; }
    .nl-feature-title-col { order: 2; }
    .nl-feature-about-col { order: 3; }
    .nl-feature-media     { order: 4; aspect-ratio: 32 / 9; }
    .nl-feature-bottom-row { order: 5; }
    .nl-feature-link      { order: 6; margin-top: 0; }

    /* metadata 窄幅放不下就自己先佔滿一行（flex-basis:100% 強制換行），
       篇名跟著換到下一行——margin-left:auto 在換行後的那一行裡一樣把它
       推到最右，不用另外寫 mobile 專屬的定位規則。 */
    .nl-feature-meta {
        flex: 1 0 100%;
    }

    .nl-feature-bottom-title {
        max-width: 100%;
    }
}

/* ── 搜尋：結果區塊 ───────────────────────────────────────────────────
   ⚠ 原本用 Bootstrap 格線置中收窄（col-lg-10）；換成色帶之後由 .simpla-pad
     負責左右內縮，格線維持不動（不改既有標記，避免動到欄位行為）。 */
.page--search .filter-result {
    margin-bottom: clamp(1.5rem, 3vw, 2.25rem);
}

/* ── 會員註冊：表單頁維持置中 ─────────────────────────────────────────
   ⚠ 這三頁的內容原本就是 col-md-8 col-lg-6 置中，色帶化之後仍然置中；
     .simpla-pad 的深內縮在這裡**不套用**（表單已經很窄，再縮會擠成一條）。 */
.page--member .stepper {
    margin-bottom: clamp(2rem, 4vw, 3rem);
}

/* ── ② 服務內頁 Hero：手機上 H1 掉到 CTA 按鈕下面 ───────────────────────
   桌機 `.page-hero-body` 是「左＝副標＋CTA／右＝英文小標＋H1」兩欄並排，
   DOM 順序因此是 intro → heading。窄幅堆疊之後那個順序原封不動搬下來，
   實際讀到的是：圖 → 麵包屑 → 副標 → 預約按鈕 → 英文小標 → **H1**。
   標題排在自己的說明與行動鈕後面，閱讀順序是反的（截圖：企業內訓）。

   ⚠ 改成「標題組先、說明組後」，手機上就與其餘 simpla 頁一致
     （開場：英文小標 → H1 → 一句話 → CTA），全站手機的開場節奏統一。
   ⚠ 只調 order 不動 DOM：桌機版型完全不受影響，
     而且 order 不改朗讀順序 —— 螢幕閱讀器仍照 DOM 走，語意沒有被打亂。
   ⚠ 影響 4 頁（企業內訓／一對一諮詢／線上課程／講座邀約）；
     顧問案與 AI 分身走 simpla 版型，本來就是文字開場，不吃這條。 */
@media (max-width: 1023.98px) {
    .page-hero-body {
        flex-direction: column;
        align-items: stretch;
    }

    .page-hero-heading { order: -1; }
    .page-hero-intro   { order: 0; }
}

/* ── ③ Footer：手機上兩欄交錯，讀起來是斷的 ─────────────────────────────
   `.footer-main` 與 `.footer-col-list` 在寬版是 flex 橫排（第 8011 起那一段），
   390 寬時 flex-wrap 把「服務專區／實體課程／知識專區／顧問案／AI 分身」
   折成左右交錯的兩欄，欄標題與自己的連結對不起來（截圖：會員註冊頁頁尾）。

   ⚠ 手機一律回到直式：每一欄自己一列、連結一行一條。
     這是頁尾在窄幅唯一讀得順的排法，也與 2026-07-31 客戶參考圖的直式分欄一致。
   ⚠ 只在 ≤767.98 生效，平板以上維持既有的橫排，一個像素都沒動。 */
@media (max-width: 767.98px) {
    .footer-main {
        flex-direction: column;
        row-gap: 2rem;
    }

    .footer-col,
    .footer-brand,
    .footer-subscribe {
        flex: 1 1 auto;
        width: 100%;
    }

    /* ⚠ row-gap 用 1.75rem 不是 .75rem：下面第 ④ 條會把每條連結的命中區
         撐到 44px，連結本身約 19px 高，間距若只有 12px，上下兩條的命中區
         會**互相重疊**（中心距 31px < 44px）—— 點下去會命中錯的那一條，
         比目標太小更糟。28px 間距讓中心距來到 47px，剛好不重疊。 */
    .footer-col-list {
        flex-direction: column;
        row-gap: 1.75rem;
    }
}

/* ── ④ 觸控區放大到 44px（視覺完全不變）─────────────────────────────────
   實測 390 寬時高度不足 44px 的可點元素：知識列表 37 個、知識內頁 4 個、
   課程列表 5 個。最小的是輪播圓點（9～11px）與麵包屑連結（17px）。

   ⚠ 不改任何字級或 padding —— 那會把 32px 的膠囊撐成 44px，
     整頁的密度與客戶已確認的視覺就跑掉了。改用一層透明的 ::after 把
     **命中範圍**撐到 44×44，看起來一模一樣，手指卻點得到。
   ⚠ 元素要 position:relative 才撐得住那層 ::after；這幾個原本都是 static，
     補上不影響版面（沒有 top/left 位移）。
   ⚠ WCAG 2.2 的最低標是 24×24（現況已通過），44×44 是 Apple HIG 的建議值——
     這一節取後者，手機實際用手指點的體感差很多。
   ⚠ 只在 ≤767.98 生效：桌機是滑鼠，不需要放大命中區。

   ⚠⚠ **放大命中區之前一定要先確認相鄰目標不會重疊** —— 兩個 44px 的區塊疊在
     一起，手指點下去會命中錯的那一個，那比目標太小更糟。這一輪實測
     （elementFromPoint 逐對比中心距）抓到兩處會重疊，都已處理：
       ・輪播圓點：三顆全部互疊（圓點只有 9～11px、間距又小）
         → 下方單獨把 .hero-dots 的 gap 撐到 2.25rem，中心距 45px 才不重疊。
       ・頁尾連結：直式排列時上下互疊 → 上面第 ③ 條的 row-gap 已改成 1.75rem。
     麵包屑與卡片連結實測 0 對重疊，可以直接放大。
   ⚠ **`.tag-pill` 刻意不放大**：知識列表一頁有 37 個，膠囊本身 32px、
     彼此又緊鄰成列，撐到 44px 一定整片互相重疊。32px 已通過 WCAG 2.2 的
     24×24 最低標，維持現狀是這裡唯一不會弄巧成拙的選擇。 */
/* ⚠ 2026-08-13 全站響應式稽核（RWD workflow audit）抓到：.entry-links a 不能
   留在這組選擇器裡。.entry-links 裡的連結是 .btn-pill（顧問案「預約顧問
   對話」、知識內頁「觀看 YouTube 影片／收聽 Podcast」），本身已經有一顆
   ::after（第 8 節，箭頭圓點徽章，深色底）。同一個元素只有一個 ::after，
   這裡疊上去的 content/position/width/height 跟 .btn-pill::after 的
   content/background/transform 在同一個偽元素上打特異度：background
   （.entry-links .btn-pill::after，2 class）贏，但 width/height/transform
   （這裡的規則多一個 a 型別選擇器，特異度更高）也贏——兩條各贏一半，
   合成結果變成「深色底 + 撐滿整顆按鈕的 100% 寬高」，等於一個蓋住按鈕
   文字的純黑方塊，430px／375px 手機寬度下「預約顧問對話」「觀看 YouTube
   影片」等按鈕文字整個消失。
   ⚠ 移除後不影響觸控面積：.btn-pill 本身 min-height:48px（第 8 節），
   早就超過這裡設的 44×44 門檻，.entry-links 內的按鈕不需要這層放大，
   移掉這條路徑，改用其餘三種（麵包屑／卡片連結／頁尾連結）繼續放大即可。 */
@media (max-width: 767.98px) {
.page-hero-crumb a,
.pcard-links a,
.footer-col-list a {
        position: relative;
    }
.page-hero-crumb a::after,
.pcard-links a::after,
.footer-col-list a::after {
        content: "";
        position: absolute;
        top: 50%;
        left: 50%;
        min-width: 44px;
        min-height: 44px;
        width: 100%;
        height: 100%;
        transform: translate(-50%, -50%);
    }
}

/* ── ⑤ 非 v2 頁的左右內縮：三套值收斂成一套 ─────────────────────────────
   實測 390 寬時文字左緣有三個值：v2／simpla 頁 31px（--article-x 的 8vw）、
   隱私權與會員中心 21px（.site-section 的 --section-x）、
   會員表單頁靠 Bootstrap 的 .row 負邊距。同一支手機上三種邊界，
   逐頁切過去會看到文字左緣在跳。

   ⚠ 統一吃 --article-x（＝clamp(1.25rem, 8vw, 10.5rem)，390 寬算出 31px），
     與已經定案的 v2 頁對齊 —— 以多數頁面既有的值為準，不另訂新數字。
   ⚠ 這兩頁沒有掛 .page--v2，所以 --article-x 在它們身上沒有定義；
     這裡補一份**同值**的宣告，不是新的尺寸來源。
   ⚠ 只動左右內縮，垂直節奏與其餘樣式一律不碰。 */
@media (max-width: 767.98px) {
    .page:not(.page--v2) > .page-narrow,
    .page:not(.page--v2) > .wf-label,
    .site-section.page > .page-narrow,
    .site-section.page > .wf-label,
    .site-section.page > .page-head,
    .site-section.page > .nav-tabs,
    .site-section.page > .tab-content {
        padding-inline: clamp(1.25rem, 8vw, 10.5rem);
    }
}

/* ── ⑥ 服務內頁／課程內頁 Hero 大圖：手機不要吃掉整個首屏 ────────────────
   實測 Hero 佔首屏比：課程內頁 85%、企業內訓 76%、顧問案 70%。
   手機第一屏幾乎只看得到一張圖，標題與內容全部要捲才看得到。

   ⚠ 收到 44vh 上限（約首屏四成），一眼還能看到標題與第一段。
     用 max-height 不用 height：既有的 min-height 兜底仍然有效，
     圖比較矮時不會被硬撐。
   ⚠ 只在 ≤575.98 的手機層生效，平板維持現況。 */
@media (max-width: 575.98px) {
    .page-hero-media {
        max-height: 44vh;
        min-height: clamp(180px, 40vw, 240px);
    }

    .page--v2 .simpla-media .band-media {
        max-height: 46vh;
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   39. 修回歸：手機漢堡鈕的兩條線被壓成 0 寬（看不見）
   ═══════════════════════════════════════════════════════════════════════
   症狀：≤991 時漢堡鈕**點得到但看不見**，header 上只剩 logo 與 A／A+／A++。

   成因（實測 390 寬的計算值）：
     ・`.site-nav-toggle`（0,1,0）寫的是 `width: 2.25rem; padding: 0`
     ・`.is-designed .wf-btn`（**0,2,0**）寫的是 `padding: .7rem 1.5rem`
     按鈕同時掛著 `wf-btn` 與 `site-nav-toggle` 兩個 class，特異度高的那條贏，
     於是左右各吃 24px 內距。`box-sizing: border-box` ＋ width 36px 的情況下，
     內容盒被壓成 **0px**，兩條線（width:22px、flex-shrink:1）跟著縮到 0。
     線還在、按鈕也還在，就是量不到寬度 —— 所以肉眼看起來「漢堡選單不見了」。

   ⚠ 這是 commit `0f8fc72`（其餘 18 頁切版）引進的回歸：
     測試機線上那一版（部署基準 `9d250c4`）**沒有** `.is-designed .wf-btn`
     這條規則，漢堡鈕是正常的。實際 curl 線上 CSS 比對過才敢這樣寫。
   ⚠ 修法是把 `.site-nav-toggle` 的內距與尺寸提到同樣的 (0,2,0)，
     排在 `.is-designed .wf-btn` 之後所以勝出。**不去改 `.wf-btn`** ——
     那條是全站原型按鈕的共用樣式，動它會波及所有 .wf-btn。
   ⚠ 不放進第 38 節的手機層：漢堡鈕在 ≤991 都要看得見（含平板），
     而第 38 節多半是 ≤767／≤575 的規則，混在一起日後會找不到。
   ═══════════════════════════════════════════════════════════════════════ */
.is-designed .site-nav-toggle {
    width: 2.25rem;
    height: 2.25rem;
    padding: 0;
    border: 0;
    background: transparent;
}

/* ── ② 課程內頁的副標在手機上太滿 ───────────────────────────────────────
   客戶：「實體課程文章手機版的副標題字改小，現在看有點太滿。」

   實測 390 寬：`.simpla-lead` 是 **26px**（--fs-4，區塊標題那一級），
   可用寬度只有 328px → 一行放不到 13 個字，整段被撐成一大塊。

   ⚠ 降一階到 --fs-3（20px），行高一併收到 1.9 —— 26px 配原本的行高在窄欄裡
     行距會比字還顯眼。降一階之後與 H1（32px）仍有明顯層次。
   ⚠ **這是客戶本人指定要改的**，不牴觸 2026-08-05「12/14/20/26/32 三個斷點一律相同」
     那條 —— 20px 本來就是級距內的一階（--fs-3），不是新造一個尺寸。
   ⚠ 只動手機、只動 `.simpla-lead`；桌機與其他文字級距不受影響。 */
@media (max-width: 767.98px) {
    .page--simpla .simpla-lead {
        font-size: var(--fs-3);
        line-height: 1.9;
    }
}

/* ── ③ 文章頁尾的「回實體課程／返回」在手機被拉成整行 ────────────────────
   客戶：「回實體課程跟返回鍵在手機版不要拉長，請都用一樣寬度的按鈕。」

   實測 390 寬：`.entry-foot` 在 ≤767 是 `flex-direction: column; align-items: stretch`，
   所以「回實體課程」被拉到 **328px**，底下的「收藏」「分享」各只有 95px ——
   同一組動作三顆按鈕三種寬度。

   ⚠ 改成不拉伸（align-items: flex-start），再給三顆一個共同的 min-width，
     最寬的那顆（回實體課程／回知識專區，5 個字）決定寬度，其餘跟齊。
   ⚠ 9.5rem＝152px：兩顆並排 304px ＋ 間距，塞得進 328 的內容寬，不會折行。
   ⚠ `.btn-pill` 內部是 inline-flex ＋ gap（文字 → 右側圓點徽章），
     加 min-width 之後要 `justify-content: space-between` 才不會整組擠在左邊。
   ⚠ 知識內頁的「回知識專區」與電子報／服務內頁的「返回」吃同一組 class，
     三處一起對齊 —— 客戶說的「都用一樣寬度」本來就該是全站一致。 */
@media (max-width: 767.98px) {
    .entry-foot {
        align-items: flex-start;
    }

    .entry-foot .btn-pill {
        min-width: 9.5rem;
        justify-content: space-between;
    }

    .entry-actions {
        gap: .75rem;
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   42. 實體課程內頁整頁改成雜誌內頁編排（2026-08-10 客戶指定，附參考圖）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「整個版型都改，不要照原本的，最好跟參考圖一樣。」

   ── 參考圖讀到的四件事 ────────────────────────────────────────────────
     ① 左半整張圖、右半全部文字（圖貼齊畫布左緣，不留邊）
     ② 右半最上面一條很細的 meta（左麵包屑／右分類）＋ 一條細線
     ③ meta 與內文之間**空很大一段** —— 那段空白本身就是「空間感」
     ④ 內文再分兩欄：左標題欄（整頁唯一的粗體）／右內文欄（單欄、窄行長、鬆行距）

   ── 只換編排，不換視覺語言 ────────────────────────────────────────────
   ⚠ 一個新色票、新字體、新字級都沒有加：meta 用既有的小標級
     （--text-meta ＋ --tracking-meta ＋ --slate），細線用站上同一個 .12，
     內文沿用 .page-text，標題沿用 .page-hero-title。
   ⚠ 只作用在 .page--courses 的 .ci-* —— 其他頁面與本頁下半（你將學會／
     課程資訊／報名方式／收藏）都不受影響。
   ⚠ 舊版（.about-block／.about-grid／.ce-*）的規則留在上面沒刪，
     新標記上不再有那些 class，自然不生效 —— 要退回把標記換回去即可。
   ═══════════════════════════════════════════════════════════════════════ */

/* ── 版心：meta 列要吃滿整條，所以內距給在這一層 ──────────────────────
   ⚠ .page--v2 把 .section-bleed-inner 的左右內距歸零（色帶滿版），
     所以這一區要自己補一份，否則 meta 列與那條細線會貼死在視窗邊。 */
.page--courses .ci-hero {
    padding-block: clamp(2rem, 4vw, 3.25rem) clamp(3rem, 7vw, 6rem);
}

/* ⚠ 2026-08-10 客戶：「文字不要貼邊，左邊文字請對齊 logo」——
     內距由 --article-x（1440 實測 115px）改成**與 header 完全相同的算式**，
     左緣因此落在 logo 的同一條線上（1440 實測 72px）。
   ⚠ 算式逐字照抄 --header-pad-x（第 4301 行）與 --footer-pad-x：
     這三處是同一條格線，要改就三處一起改，不要各寫各的。
   ⚠ 這一條同時涵蓋窄幅：算式的 max() 在小螢幕會退回 --section-x
     （1.5rem／1.3rem），所以不需要再開一個媒體查詢。 */
.page--courses .ci-inner {
    padding-inline: max(var(--section-x), calc((100% - var(--container-max)) / 2));
}

/* ── ① 最上面的 meta 列：滿版寬、內容一律靠左 ─────────────────────────
   ⚠ 2026-08-10 客戶：「最上的橫條直接填滿整個寬度，不要擠在右邊；
     文字靠左對齊」——所以它從右欄升到整條最上面（滿版），
     裡面也由兩端對齊（space-between）改成一律靠左（flex-start）。 */
.page--courses .ci-meta {
    display: flex;
    align-items: baseline;
    justify-content: flex-start;
    gap: clamp(1rem, 2.4vw, 2.5rem);
    padding-bottom: clamp(.75rem, 1.4vw, 1rem);
    border-bottom: 1px solid hsl(var(--foreground) / .12);

    font-family: var(--font-eyebrow);
    font-size: var(--text-meta);
    line-height: var(--lh-meta);
    letter-spacing: var(--tracking-meta);
    color: hsl(var(--slate));
}

.page--courses .ci-meta .page-hero-crumb { margin: 0; }
.page--courses .ci-meta-cat { flex: none; }

/* ── ② 左欄（標題在上、圖在下）｜右欄（內文）──────────────────────────
   ⚠ 2026-08-10 客戶：「上區的標題移到圖片上方，跟圖片同一欄」。
   ⚠ sticky 掛在**整個左欄**（標題＋圖一起黏），不是只黏圖 ——
     只黏圖的話標題會先捲走，兩者就散開了。
   ⚠ align-self: start 是 sticky 在 grid 裡生效的前提：
     grid 子項預設 stretch，撐滿高度就沒有可以「黏」的空間。 */
.page--courses .ci-grid {
    display: grid;
    /* ⚠ 2026-08-10 客戶：「右邊文字段落寬一點，現在都擠在一起」——
         欄寬比由 40:52 調成 36:58，右欄多拿約 60px。 */
    grid-template-columns: minmax(0, 36fr) minmax(0, 58fr);
    gap: clamp(2rem, 5vw, 5rem);
    align-items: start;
    padding-top: clamp(2.5rem, 6vw, 5rem);
}

.page--courses .ci-left {
    position: sticky;
    top: calc(var(--header-h) + clamp(1.5rem, 3vw, 2.5rem));
    align-self: start;
}

.page--courses .ci-side .page-hero-title {
    margin-top: clamp(.75rem, 1.4vw, 1rem);
}

.page--courses .ci-side .entry-meta {
    margin-top: clamp(1.25rem, 2.4vw, 1.75rem);
}

/* 圖：排在標題下方、吃原圖比例**完全不裁**。
   ⚠ 上一版是「與右欄等高的直式版位」才需要 cover 裁掉左右；
     現在圖有自己的欄寬、也不必配合文字長度，就沒有理由裁了。 */
.page--courses .ci-media {
    margin: clamp(2rem, 4vw, 3rem) 0 0;
    aspect-ratio: 1100 / 830;
    overflow: hidden;
    background: hsl(var(--foreground) / .04);
}

.page--courses .ci-media picture,
.page--courses .ci-media img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* ── ③ 右欄：內文 ──────────────────────────────────────────────────
   ⚠ 2026-08-10 客戶：「右邊文字段落寬一點，現在都擠在一起；請延伸到 A++ 那邊」——
     原本壓了 34em（14px 時約 476px）的行長上限，可是欄位本身有 692px，
     等於白白空掉 200px；**而且這一區的原稿是每句一行（靠 <br> 斷行）的**，
     欄位一窄就會把句子從中間再折一次（客戶截圖裡的「很有／那個感覺。」）。
     上限整個拿掉，讓內文吃滿右欄 —— 右欄的右緣就是版心右緣，
     與 header 最右邊那顆 A++ 同一條線。
   ⚠ 這裡刻意不再壓行長：一般中文內文會壓在 24～28 字，但這一段是
     預先斷好行的短句，壓行長只會讓它二次折行、反而更難讀。 */
.page--courses .ci-text {
    max-width: none;
}

.page--courses .ci-lead {
    margin: 0 0 clamp(2rem, 4.5vw, 3.25rem);
    font-size: var(--text-sub);
    line-height: 2;
    font-weight: var(--fw-light);
    letter-spacing: var(--tracking);
    color: hsl(var(--heading));
}

.page--courses .ci-h2 {
    margin: clamp(.5rem, 1vw, .75rem) 0 clamp(1.5rem, 3vw, 2.25rem);
    font-size: var(--text-h2);
    line-height: 1.75;
    font-weight: var(--fw-heading);
    letter-spacing: var(--tracking-title);
    color: hsl(var(--heading));
}

/* ⚠ 客戶：「按鈕放文章底下」——上方留白比段距大一階，讀完才是行動。 */
.page--courses .ci-text .ci-actions {
    margin-top: clamp(2.5rem, 6vw, 4rem);
}

/* ── 段落群：每群上方一條短橫線（2026-08-10 客戶指定，附三張參考圖）──────
   ⚠ 短橫線本體來自站上既有的 .rule-mark（2.25rem × 1px），這裡只調它在
     這個情境下的間距與濃度 —— 它原本是給英文小標用的，字級小、線可以淡；
     這裡撐的是一整群內文，線太淡會讀不出「這是一個分段」。
   ⚠ 群與群之間留得比段與段之間大得多：參考圖的節奏就是
     「段落靠得近、群落分得開」，兩個間距不拉開就沒有層次。 */
.page--courses .ci-group.rule-mark::before {
    margin-bottom: clamp(1.25rem, 2.4vw, 1.75rem);
    background: hsl(var(--foreground) / .45);
}

.page--courses .ci-group + .ci-group {
    margin-top: clamp(2.5rem, 5vw, 3.75rem);
}

/* ── 你將學會 ／ 課程資訊：拆成上下兩個單欄區塊（2026-08-10 客戶指定）──────
   客戶：「這門課跟課程資訊不要合併成兩欄呈現。」
   ⚠ 內距沿用 .simpla-cols 原本吃的 --article-x（同一條文字格線），
     拆成單欄之後左緣才不會跟前面幾段對不齊。 */
/* 內距與上面的 .ci-inner 同一條算式 ＝ 與 header logo 齊頭。
   ⚠ 2026-08-10 客戶：「這門課跟課程資訊的文字編排請跟報名方式的一樣」——
     報名方式那一段吃的是 .simpla-pad（--article-x），所以這兩塊也改吃同一個值，
     三段的左緣與行長因此完全一致。 */
.page--courses .ci-block {
    padding-inline: var(--article-x);
}

.page--courses .ci-block + .ci-block {
    margin-top: clamp(3rem, 7vw, 5.5rem);
}

/* ── footer 上方的細線加回來（2026-08-10 客戶：「footer 上面加細線」）────────
   ⚠ 第 31 節（2026-08-06）曾依客戶指定把實體課程專區的這條線關掉，
     這一輪客戶改要它回來 —— **不動第 31 節的規則**，在這裡覆蓋。
     兩條特異度相同（各 2 個 class 級），靠位置在後決勝。
   ⚠ 線色與其他 v2 頁面同一階（--foreground 14%），不另調一個灰。 */
body:has(.page--courses) .site-footer {
    border-top: 1px solid hsl(var(--foreground) / .14);
}

/* 段落：行距沿用客戶點頭過的 2.15，段距放寬一階 */
.page--courses .ci-text .page-text {
    max-width: none;
    margin-inline: 0;
    padding-inline: 0;
    line-height: 2.15;
}

.page--courses .ci-text .page-text + .page-text {
    margin-top: clamp(1.5rem, 2.6vw, 2.25rem);
}

/* ── RWD：沿用站上既有的斷點 ────────────────────────────────────────── */
@media (max-width: 1023.98px) {
    /* 收成單欄：meta 列 → 標題 → 圖 → 內文，順序與桌機由上而下讀完全相同。
       ⚠ sticky 一併關掉：單欄時黏住的左欄（標題＋圖）會一路擋著大半個螢幕。 */
    .page--courses .ci-grid {
        grid-template-columns: minmax(0, 1fr);
        gap: clamp(2rem, 6vw, 3rem);
        padding-top: clamp(2rem, 6vw, 3rem);
    }

    .page--courses .ci-left {
        position: static;
        top: auto;
    }

    .page--courses .ci-text { max-width: none; }
}

@media (max-width: 575.98px) {
    /* 窄幅的 meta 改上下兩行：麵包屑本身就有兩節，硬排一列會擠成碎行 */
    .page--courses .ci-meta {
        flex-direction: column;
        align-items: flex-start;
        gap: .375rem;
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   44. 實體課程「課程一覽」：橫向伸縮 Accordion（2026-08-11 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「三門課程共同包在同一個橫向容器裡，形成一組橫向伸縮 Accordion。
          一門展開成左文右圖 1:1，另外兩門收合成窄直條；點擊另一門時，展開狀態
          在同一橫排裡切換。六門課分成兩組，每組三門，兩組狀態互相獨立。」

   ── 前一版被退回的原因（客戶逐條列出，記在這裡免得再繞回去）──────────
     ✗ 三門課上下垂直堆疊　✗ 左側課程列表＋右側固定圖片　✗ 三張等寬卡片
     ✗ 每門課各佔一整條橫列　✗ 三門課分散到不同列　✗ 每個主題整區收合
   這一版全部避開：課程**永遠**在同一個 flex 橫排裡，只有 width 在變。

   ── 尺寸（客戶給的區間）────────────────────────────────────────────────
     窄直條 72–110px  → --cx-tab-w: clamp(4.5rem, 6vw, 6.5rem)（1440 實得 86px）
     整組高 340–440px → --cx-hz-h : clamp(21.25rem, 28vw, 27.5rem)（1440 實得 403px）
     展開寬 ＝ 100% − (n−1) × 窄條寬　（n 由 JS 從 data-cx-n 寫進 --cx-n）
   ⚠ 高度**寫死在容器上**：客戶要求「整組高度在切換前後必須保持固定」。
     交給內容決定的話，六門課的簡介長度不同，每次切換整頁都會跳。

   ── 紀律 ──────────────────────────────────────────────────────────────
   ⚠ 本節取代同一輪（未提交）的直向 Accordion 版本 —— 客戶要求換掉，
     留著兩套會互相打架，所以是改寫本節而不是再疊一節。
     第 19～43 節一行都沒有動；要退回把這一整節註解掉即可。
   ⚠ 每一條選擇器都帶 .page--wall（只有這一頁掛），其他 18 頁一個像素都不會動。
   ⚠ 一個新色票、新字級、新字體、新圓角、新按鈕樣式都沒有加：
     字級只用 12/14/20/26/32，顏色只用 --heading / --foreground / --slate /
     --muted-foreground，線色沿用 v2 頁面同一階（--foreground 14%），
     圓角沿用 --radius-media，動態沿用 --ease-out / --transition。
   ═══════════════════════════════════════════════════════════════════════ */

/* ── ① 版心 ─────────────────────────────────────────────────────────── */
.page--wall .cx-grid {
    padding-inline: var(--article-x);
}

/* ── ② Hero：深色方格系統 Editorial Hero（2026-08-18 改版，v4）──────
   舊版「背景圖 ＋ 紙白面紗 ＋ 純文字分類導航」整段換掉。v4（使用者看過
   v3 截圖後，一次給了多句指令重新定案）相對 v3 的異動：
   ① 分類快速導航（全部課程／學思／學理／人際關係）整組拿掉——使用者
      確認交給下方「課程一覽」自己的三個主題區塊處理，Hero 不重複放。
   ② 版面改成 4 欄 × 2 列：上列 logo／photoB／intro／headerslot 都是
      正方形；下列 photoA 正方形 ＋ statement 橫跨其餘 3 欄的寬長方形。
      logo／headerslot 這兩格**不渲染任何內容**——只是標記「這個位置
      對應 header 的 logo／對應 header 常駐的搜尋・選單・字級切換」，
      那些控制項本來就是 position:fixed 疊在最上層，這裡重複畫一份
      會變成兩份 DOM 對應同一個功能，可及性打架，所以留空、只參與
      遮罩出場動畫。
   ③ 拿掉每格描邊（v3 的 outline）：outline-offset 內縮的畫法在方格
      上下仍會露出一小段沒描到的縫，改成遮罩全部收合完成後，格線本身
      （.cx-hero-grid 的 1px gap 背景）從低對比淡化直接轉成清楚的白色
      ——垂直水平的格線一次到位，不會有「線蓋不滿格子邊緣」的問題。
   ④ H1／次標字級改回全站既有的 --text-section／--text-body（v3 為了
      呼應 Citi 大標故意放到 72px，這輪客戶要求「不要在這區塊字特別
      大」，跟站上其他頁首標題同一個字級規則）。
   ⑤ 方塊內原本的小字（分類連結文字、圖片說明）都拿掉，改成 intro
      這一格集中放英文小標＋往下箭頭（沿用既有的 wf_icon('chevron-
      down')，不新畫圖示）。
   ⚠ 最外層 padding-top 沿用原本的 clamp(1.5rem, 3vw, 2.5rem)——這個
     值被「內頁頁首色帶延伸到視窗最上緣」那段 --header-h 補償公式
     （本檔後面 .band.cx-hero:first-child 那條）寫死引用，這裡刻意
     不改，兩邊才會繼續對得上；.has-menu-open .cx-hero 那條手機選單
     補白規則同理不用動，兩者都只認 .cx-hero 這個 class 名稱。
   ⚠ 遮罩逐塊出場動畫：見 courses_list.php 底部 script；只在頁面載入
     時播一次，這裡只定義靜態的遮罩起始狀態（未 revealed＝完全蓋住）
     與 revealed 之後的縮回終值，動畫由 JS 切 class 觸發 CSS
     transition。 */
.page--wall .cx-hero {
    /* 2026-08-18 客戶先要「背景色改圖片這個顏色」（從插畫實測取樣暖
       米色 #f9ede3），看過實機後改口「請用這整頁的灰色背景，不要膚色」
       ——插畫米色被判斷太接近膚色，改成直接讀全站既有的 --background
       （這頁的頁面底色，本身就是 SSOT，不用另外取樣或另開 token）。 */
    position: relative;
    overflow: hidden;
    padding-top: clamp(1.5rem, 3vw, 2.5rem);
    /* ⚠ 通用的 .band { padding-block: var(--v2-band-y) } 會把下內距也
       設成跟上內距同一顆變數（.page--v2 情境下 76px@1920），這裡明確
       蓋成 0——不能只是「不寫」，不寫等於放行讓那條通用規則的下內距
       繼續生效，圖片就會貼不到 Hero 最下緣（客戶「圖片貼邊底下不要有
       空間」）。 */
    padding-bottom: 0;
    background: hsl(var(--background));
}

/* 3 條垂直線延伸到 header 讓位空間（客戶「線請直接蓋過header層級」）
   ── .cx-hero-grid 的格線（gap 露色）只在 grid 自己的高度範圍內，蓋不到
   最上面 padding-top 那一段（透明 header 疊在上面的緩衝區）。這三條線
   改成獨立元素、貼齊 .cx-hero 整條高度（inset:0，含 padding-top），
   x 座標對齊 grid 內部 4 欄的分界（25%／50%／75%，欄寬相等時就是這三個
   位置），視覺上跟 grid 自己的垂直線銜接成同一條、從最上緣就開始，
   不是另外多畫一段。
   ⚠ 2026-08-18 客戶「該區塊的線顏色請跟課程一覽的線一樣」——課程一覽
     `.cx-rule-h::before/::after`（style.css ≈16132）線色是固定的
     hsl(var(--foreground) / .2)，沒有深淺兩階、沒有動畫；這裡跟著改成
     同一顆固定值，不再是「遮罩收完後淡入變實色黑」的兩階設計，
     is-revealed 那條「變黑」的疊加規則跟著拿掉（起訖值現在相同，
     留著只是空轉的 transition）。 */
.page--wall .cx-hero-lines {
    position: absolute;
    inset: 0;
    z-index: 1;
    pointer-events: none;
}

.page--wall .cx-hero-line {
    position: absolute;
    top: 0;
    bottom: 0;
    width: 1px;
    background: hsl(var(--foreground) / .2);
}

.page--wall .cx-hero-line--1 { left: 25%; }
.page--wall .cx-hero-line--2 { left: 50%; }
.page--wall .cx-hero-line--3 { left: 75%; }

/* 客戶「並在圖片底下位置加一條水平橫線」——bottom row 的 photoA／
   statement／void4 目前直接貼齊 .cx-hero 最下緣（沒有 padding-bottom，
   之前「圖片貼邊底下不要有空間」那輪刻意做的），下緣本身沒有任何線。
   沿用同一個 .cx-hero-line 元件，改成水平方向：蓋掉 top/width，改用
   height:1px 貼在 .cx-hero 的 bottom，左右吃滿全寬——跟三條垂直線
   同一個絕對定位邏輯，只是軸向轉 90 度。 */
.page--wall .cx-hero-line--bottom {
    top: auto;
    left: 0;
    right: 0;
    bottom: 0;
    width: auto;
    height: 1px;
}

/* ── 裝飾格線改實色，不再半透明（2026-08-24 客戶「背景線跟圖片遮罩線，
   粗度跟透明度不一致」）───────────────────────────────────────────────
   客戶指的不是兩條線互相重疊，是**同一條** .cx-hero-line 疊在不同底色
   上合成結果不一致：這條線的顏色是 hsl(var(--foreground)/.2)（20% 黑
   的半透明疊色，本檔 ≈17077 那條，2026-08-18 客戶指定跟 .cx-rule-h 用
   同一顆固定值），疊在素色的 .cx-hero-cell 背景（實測 rgb(246,246,247)
   淺灰）上幾乎看不出來，但線經過 photoA／photoB 插畫本身較深的色塊
   （衣服陰影、頭髮等）時，半透明疊色會讓那段變得更暗、看起來比其餘
   路段粗——同一條 1px 線，因為底色不同，沿途深淺不一致。
   ⚠ 只鎖 .cx-hero-line 這個選擇器，不動 ≈17077 那條共用宣告本身，也
     不動 .cx-rule-h（客戶當時要那兩處線色一致，維持一致：.cx-rule-h
     不會經過照片，沒有這個問題，不需要跟著改）。
   ⚠ 顏色不是隨便挑的：實測「20% 黑疊在 rgb(246,246,247) 素色背景上」
     合成出來的實際顏色＝ rgb(202,202,203)，直接把這個當實色寫死——
     疊在素色背景上的視覺效果因此完全不變（本來就長這樣），疊在照片上
     時線就固定是這個顏色，不會再被底下的插畫顏色影響深淺。 */
.page--wall .cx-hero-line {
    background: rgb(202, 202, 203);
}

/* 4 欄等寬：logo/photoB/intro/headerslot 一列，photoA/statement 一列
   ——上列剛好 4 個 1×1 方格，欄數因此鎖定 4（也決定了 statement
   橫跨其餘 3 欄）。列高**明確**用 calc() 算出來，不是讓每格自己
   aspect-ratio 去撐（實測過 aspect-ratio 版本會被內容較多的格子撐高
   那一列、但其他方格不會跟著「拉伸」去填滿變高的列，方格底下會空出
   一截、跟其他格對不齊）。本頁 `.section-bleed-inner` 這一層被
   .page--v2 那條規則歸零內距，`.cx-hero-grid` 因此貼齊視窗兩側，
   欄寬 ≈ 100vw/4。
   ⚠ 兩列高度**不再相等**（客戶 2026-08-18「上下矩形讓上方的高比較
     矮一點」）：下列（photoA／statement／void4）維持原本 ÷4.5 的高度
     當基準，上列（logo／photoB／intro／headerslot）在這個基準上再
     乘 0.8，明顯比下列矮一截。intro 格因此更容易被撐爆——擠壓帶從
     原本的 768~899.98px 放大到 768~1299.98px（見下方 ②-5b 那條
     media query，箭頭的線在這段寬度整條藏起來，只留箭頭本身），
     這個新高度已經用 puppeteer 重新掃過確認 0 clip。兩條垂直線／
     格線的 gap 露色機制不受影響，2 列高度不等只是把中間那條水平線
     的位置往上移，3 直 1 橫的線條結構本身沒有變。
   ⚠ .cx-hero 的 padding-bottom 拿掉（客戶「圖片貼邊底下不要有空間」）
     ——原本那圈 clamp(1.5rem,3vw,2.5rem) 會在最下面一列底下多留一截，
     圖片因此貼不到 Hero 的最下緣。padding-top 繼續留著：它是給最上面
     一列的視覺呼吸，也被下面「內頁頁首色帶延伸到視窗最上緣」那段
     --header-h 補償公式寫死引用，不能動。
   ⚠ 8 格全部等大、**沒有任何一格跨欄**（客戶：「我就是要一個深灰色
     背景，畫三條垂直線跟一條水平線，上下分成各四個矩形」）——上一版
     statement 橫跨 3 欄合併成一個大矩形，合併儲存格內部沒有 gap，
     等於少畫了 2 條垂直線，跟「上下各四個矩形」對不上。這裡刻意不疊
     額外的 border：gap 露線在 4×2、零合併的前提下，天生就是精確的
     3 條垂直線＋1 條水平線，不需要另外處理最外圈——多疊一圈 border
     反而是客戶沒要求的裝飾，上一輪加了又拿掉。 */
.page--wall .cx-hero-grid {
    position: relative;
    display: grid;
    gap: 1px;
    /* 客戶「線顏色請跟課程一覽的線一樣」——跟三條垂直線同一顆固定值
       hsl(var(--foreground) / .2)，不再是遮罩收完後才淡入變黑的兩階
       設計（is-revealed 那條疊加規則跟著拿掉）。 */
    background: hsl(var(--foreground) / .2);
    grid-template-columns: repeat(4, 1fr);
    grid-template-rows: calc((100vw - 3px) / 4.5 * 0.8) calc((100vw - 3px) / 4.5);
    grid-template-areas:
        "logo photoB intro headerslot"
        "photoA statement statement void4";
}

.page--wall .cx-hero-cell {
    position: relative;
    display: flex;
    overflow: hidden;
    margin: 0;
    background: hsl(var(--background));
}

.page--wall .cx-hero-cell--logo        { grid-area: logo; }
.page--wall .cx-hero-cell--headerslot  { grid-area: headerslot; }
.page--wall .cx-hero-cell--void4       { grid-area: void4; }

/* 客戶 2026-08-18「最右邊的矩形也加小字跟往下指的箭頭跟上面的一樣」——
   void4（右下角）比照 intro 那格同一套版面（字靠左、上下兩端貼齊
   cell），grid-area 各自獨立（見上面兩行），版面規則合併成一條選擇器
   共用，不重複寫一次，之後任一邊調整版面兩格會一起變、不用分開改。
   ⚠ 這格與 statement 同一天短暫合併成一塊矩形，隨後客戶要求「實體課程
     是兩個矩形的寬，小字那塊不要合併」，改回這裡——void4 恢復成獨立
     的 grid-area，這條共用規則本身沒有再變動過，記錄一下避免以為
     這中間發生過什麼。 */
.page--wall .cx-hero-cell--intro,
.page--wall .cx-hero-cell--void4 {
    flex-direction: column;
    align-items: flex-start;
    justify-content: space-between;
    padding: 1.25rem;
    text-align: left;
}

/* 客戶「小字請跟左邊標題齊頭」——void4 在 statement（左邊那格、H1
   所在）的正右邊，兩格小標本來應該同高，但 intro／void4 共用的
   padding 是固定 1.25rem，statement 是響應式 clamp(1.5rem,3vw,3rem)，
   寬螢幕時 statement 內距明顯更大，void4 的字因此比 statement 高了一截
   （1920px 實測差 28px）。只覆寫 padding-top 對齊到跟 statement 完全
   一樣的算式，左右／下內距維持原本 1.25rem 不變——不影響 intro 那格
   （intro 沒有「跟誰齊頭」的需求，維持原樣）。 */
.page--wall .cx-hero-cell--void4 {
    padding-top: clamp(1.5rem, 3vw, 3rem);
}

/* 上排圖片延伸填滿 header 讓位空間（客戶「上面圖片請直接延伸header高度
   填滿」）——比照上面 .cx-hero-line 同一個原理：.cx-hero-grid 的格子
   從 padding-top（讓開透明 header 的緩衝區）下面才開始，photoB 這格
   單獨用負 margin-top 把自己的頂端拉回 .cx-hero 自己的頂端（跟三條
   垂直線現在的起點一致），這格的列高是 grid-template-rows 明確算死的
   calc(100vw/4.5)，不是 auto，多拉這段負 margin 不會影響列高或其他格
   （logo／intro／headerslot 原地不動）。padding-top 用的是跟 .cx-hero
   實際生效那條（style.css「內頁頁首色帶延伸到視窗最上緣」段
   .band.cx-hero:first-child）完全一樣的算式，兩邊要一起改。 */
.page--wall .cx-hero-photo--b {
    grid-area: photoB;
    margin-top: calc(-1 * (clamp(1.5rem, 3vw, 2.5rem) + var(--header-h) + 1px));
}

/* ⚠ 2026-08-25：有麵包屑的頁面，這個負 margin 要跟著多扣 --wf-crumb-total。
   上面那條註解最後一句「padding-top 用的是跟 .cx-hero 實際生效那條完全
   一樣的算式，**兩邊要一起改**」講的就是這件事——麵包屑復出後
   `.band.cx-hero:first-child` 的 padding-top 多加了一個 --wf-crumb-total
   （麵包屑在文件流佔掉的高度，見「有麵包屑的頁面：拉頂量…」那節），
   這裡沒跟著加的話 photoB 就只拉到「grid 頂端往上 97px」而不是色帶頂端，
   實測 1900 寬 photoB top=52.4 但色帶 top=-1，圖片上緣在 header 底線
   附近硬生生斷一截（客戶截圖回報）。
   ⚠ 這條跟上面那條是同一組數字的鏡像，改任何一邊都要三處同步：
     ①`.band.cx-hero:first-child` 的 padding-top ②這裡 ③下面
     `.cx-hero-cell--intro .cx-hero-intro-text` 的 margin-top。 */
body:has(.wf-crumb) .page--wall .cx-hero-photo--b {
    margin-top: calc(-1 * (clamp(1.5rem, 3vw, 2.5rem) + var(--header-h) + var(--wf-crumb-total) + 1px));
}

/* ⚠ tombstone：手機／平板曾經有一條「photoB 貼齊 header 底線」的覆寫
   （2026-08-26），2026-08-27 客戶指示移除。
   ── 來龍去脈（很重要，不要再繞回去）──────────────────────────────
   8/26 客戶「手機跟平板版，圖片下移到線的位置」，當時 header 底部**有一條
   看得見的灰線**，所以「貼齊那條線」是合理的，這裡便加了一條只在
   ≤1199.98 生效的覆寫，把圖片頂緣從 -1（視窗最頂）壓到 --header-real-h。

   8/27 客戶接連又指定了兩件事，把那條線的前提拆掉了：
     ①「本來不要有背景，按搜尋鍵才有背景」→ header 在頁首完全透明
     ②「header 滾動前不要加底線」→ 底線在未捲動時是透明的
   實測（485 寬，未捲動）：header `background: rgba(0,0,0,0)`、
   `border-bottom: 1px rgba(0,0,0,0)`。**圖片等於在對齊一條看不見的線**，
   上方那 55px 就變成一塊沒有任何視覺理由的空白，看起來像圖片被往下推、
   沒填滿——正好又回到 8/26 客戶最初抱怨的「空了一塊」，只是這次的成因是
   我們自己後來的改動。客戶確認後決定：手機／平板也貼到最頂，跟桌機一致。

   ⚠ 修法是**整條移除覆寫**，不是把值改成 0：桌機基底規則
     （≈17735，`-1 * (clamp + --header-h + --wf-crumb-total + 1px)`）算出來
     的結果本來就精確落在 -1，移除覆寫就自動繼承，不需要另外寫一個值，
     日後基底那條調整時這裡也會自動跟上。
   ⚠ 麵包屑不會被蓋到：這一頁的麵包屑文字只佔左欄，photoB 在右欄，兩者
     水平範圍不重疊（跟 course_detail 的滿版麵包屑情況不同——那頁的
     覆寫**維持不動**，見 ≈19586 的說明）。 */

.page--wall .cx-hero-photo--a          { grid-area: photoA; }

/* intro：字改靠左（客戶「字靠左」），上下兩端貼齊 cell（justify-content:
   space-between）——英文小標＋中文說明（同一個 .cx-hero-intro-text
   容器）貼在頂端左側，長線＋箭頭（同一個 .cx-hero-arrow 容器）貼在
   底部右側（.cx-hero-arrow 自己 align-self:flex-end 靠右，不影響同一個
   flex 容器裡文字的 flex-start 靠左）。
   ⚠ space-between 只看 .cx-hero-cell--intro 的**直接子元素**：文字群組
     是一個、.cx-hero-arrow 整組是另一個，兩者之間才會被拉開；小標／
     說明兩段文字包在 .cx-hero-intro-text 容器「裡面」（不是各自散在
     cell 底下當獨立子項），線跟箭頭也包在 .cx-hero-arrow 容器「裡面」
     ——都不是 cell 的直接子元素，不會被這條 space-between 誤傷。
     2026-08-18 客戶「小字區塊英文字跟中文字離太遠」踩到的就是這個坑：
     intro-desc 當初用直接子元素的方式加進來，變成 3 個直接子項，
     space-between 把英文小標跟中文說明也拉開了一段不該有的間距。 */
/* 客戶「上面矩形的小字上移到header同行」——只有小字（.cx-hero-intro-text）
   要上移，箭頭留在原地不用跟著動，所以不能比照 .cx-hero-photo--b 那樣
   直接把整個 cell 往上拉（箭頭會跟著移位），改成只在小字這個內層元素
   上疊負 margin-top，算式跟 photoB／三條垂直線同一顆（padding-top 的
   header 讓位量），讓文字頂端對齊 .cx-hero 自己的頂端、跟 header 同一
   條水平線。
   ⚠ 子元素要逃出 padding-top 讓位區，cell 本身的 overflow:hidden（見
     .cx-hero-cell 基底規則，是為了裁切遮罩用的）會把逃出去的部分切掉
     ——遮罩本身是 inset:0 貼齊 cell 自己的框，不靠這個 overflow 也不會
     跑版，所以這裡把 intro 這格單獨蓋回 overflow:visible 是安全的。
   ⚠ 光加負 margin-top 不夠——intro 跟 void4 共用的 justify-content:
     space-between 是拿「文字群組」「箭頭」兩個直接子元素的高度去算
     中間該留多少空隙，文字元素多了一段負 margin 只會讓 space-between
     重新分配間距去「補償」，實測負 margin 只有一部分生效（-97px 只
     移動了 97px 中的一小段，其餘被 space-between 吃掉）。改把箭頭
     從 flex 排版裡拿出來、改用 position:absolute 貼齊 cell 右下角
     （cell 本身 position:relative，見基底規則）——這樣 flex 容器裡只
     剩文字群組一個子項，space-between 對單一子項等同 flex-start，
     不會再跟負 margin互相干擾，箭頭本身視覺位置不變（同樣落在右下角，
     只是拿掉之後不再受文字上移影響）。只 override intro 這格的箭頭，
     不動 .cx-hero-arrow--down 這條共用基底規則，void4 的箭頭維持原本
     flex 排版不受影響。 */
.page--wall .cx-hero-cell--intro {
    grid-area: intro;
    overflow: visible;
    justify-content: flex-start;
}

.page--wall .cx-hero-cell--intro .cx-hero-arrow--down {
    position: absolute;
    right: 1.25rem;
    bottom: 1.25rem;
}

.page--wall .cx-hero-intro-text {
    display: flex;
    flex-direction: column;
    gap: .375rem;
}

/* ⚠ 抓出來的真正原因：.cx-hero-intro-text 這個 class 同時被 intro／
   void4 兩格共用，document.querySelector('.cx-hero-intro-text') 或是
   寫在「共用規則」裡的 margin-top 一開始都誤套用到兩格身上——void4
   在 DOM 順序上還排在 intro 前面（data-order 2 vs 5），排錯選擇器時
   量到的其實是 void4 那份、不是 intro 那份，一直以為負 margin沒生效，
   其實是量錯元素。負 margin 本身沒問題（另外用最小 repro 驗證過），
   問題只在「這條規則不該套用到 void4」——void4 的小字要對齊 statement
   （見上面「小字請跟左邊標題齊頭」那條），不能跟著被拉去對齊 header。
   改成只鎖 .cx-hero-cell--intro 底下那份 .cx-hero-intro-text，void4
   的維持原本無 margin-top 的樣子。 */
.page--wall .cx-hero-cell--intro .cx-hero-intro-text {
    margin-top: calc(-1 * (clamp(1.5rem, 3vw, 2.5rem) + var(--header-h) + 1px));
}

/* ⚠ 2026-08-25：同 photoB，有麵包屑的頁面要多扣 --wf-crumb-total（三處
   同步的第③處，見 .cx-hero-photo--b 那條的說明）。這格是「小字上移到
   header 同行」，色帶 padding 變大而它沒跟著拉，小字就會停在半空中。 */
body:has(.wf-crumb) .page--wall .cx-hero-cell--intro .cx-hero-intro-text {
    margin-top: calc(-1 * (clamp(1.5rem, 3vw, 2.5rem) + var(--header-h) + var(--wf-crumb-total) + 1px));
}

/* statement：跟 void3 合併後橫跨 2 欄，內容維持靠左（客戶「標題移到
   左邊那格」＋原本就有的 align-items:flex-start），新增的英文小標／
   箭頭沿用同一個 flex-column 排列，順序就是 DOM 順序（小標→H1→次標→
   箭頭，客戶 2026-08-18「標題的箭頭放次標底下」從原本「小標→箭頭→
   H1→次標」搬到最後），不需要另外用 order 調整。
   ⚠ 這格同一天短暫改成過「flex row 容器＋內層 .cx-hero-statement-main」
     的兩層結構（跟 void4 併成一塊矩形），客戶隨後要求「小字那塊不要
     合併」，這裡改回單層——內容排版屬性直接寫在這格自己身上，不再
     需要內層 wrapper。 */
.page--wall .cx-hero-cell--statement   { grid-area: statement; flex-direction: column; justify-content: center; align-items: flex-start; gap: .75rem; padding: clamp(1.5rem, 3vw, 3rem); }

/* ── ②-2 英文小標（intro／statement 共用，客戶要求兩處都要有同一句
   小標）── */
.page--wall .cx-hero-eyebrow {
    margin: 0;
    font-size: var(--text-body);
    line-height: var(--lh-body);
    letter-spacing: var(--tracking-meta);
    color: hsl(var(--foreground) / .82);
}

/* intro 專屬的補充文字（客戶 2026-08-18「小字內容增加文案：實體課程
   分為學思學理人際關係，往下查看該課程詳細介紹」）；字級比 eyebrow
   小一階，視覺上是次要說明，不是搶 eyebrow 的份量。
   ⚠ 這格的列高是 calc((100vw-3px)/4.5) 算出來的固定值（見 .cx-hero-grid
     註解，這個除數之後如果因為「高度再調整」需求又被改動，這裡的
     行為不受影響——line-clamp 卡的是行數不是像素），約 768~880px
     這段特別窄（4 欄擠在中等寬度），文案一長
     （中文逗號斷句多）就可能撐到 3 行，把下面的箭頭往下推到列高之外
     被 overflow:hidden 切掉一截——不是箭頭本身的 bug，是文字高度沒
     設防線。用 -webkit-line-clamp 卡死最多 2 行（超過 2 行截斷加
     …），不管文案之後怎麼改都不會再把箭頭擠出格外；1920/1440/390 這些
     已經驗證過本來就只換行 2 行的寬度不受影響（沒有真的被截斷）。 */
.page--wall .cx-hero-intro-desc {
    margin: 0;
    max-width: 22em;
    font-size: var(--text-meta);
    line-height: var(--lh-body);
    color: hsl(var(--foreground) / .68);
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    overflow: hidden;
}

/* ── ②-3 箭頭（intro／statement 共用同一組樣式）──────────────────────
   2026-08-18 客戶「箭頭請用你其他分頁的箭頭樣式」，指定 newsletter_list.php
   的 .simpla-arrow（style.css ≈14266）：單一 SVG、一條 path 同時畫線＋
   開口 V 字箭頭，線跟箭頭是同一條路徑，天生零縫隙——不像先前兩版
   （wf_icon SVG 接 CSS 線／CSS 邊框轉角三角形接 CSS 線）都是「線」跟
   「箭頭」兩個獨立元素拼接，才會一直有「沒接在一起」的回報。
   ⚠ path 座標沿用 newsletter 原版（`M12 0v58M2 48l10 12 10-12`，
     viewBox 0 0 24 64），--right 版是另外畫一條座標軸對調的 path
     （見 courses_list.php 標記），不是 CSS transform 轉向——旋轉
     元素的視覺方向會變，但 flex 版面用的 layout box 尺寸不會跟著轉，
     兩者對不上還要另外處理，不如直接畫一條方向對的 path 單純。
   ⚠ 顏色統一用 hsl(var(--foreground) / .6)，跟 newsletter 原版
     `.simpla-arrow { color: hsl(var(--foreground) / .6) }` 完全一樣
     （沿用「箭頭樣式」不只沿用畫法，連色階也一併對齊）。
   ⚠ newsletter 原版是寫死 24×64px 不需要縮放；這裡的線長沿用「箭頭線
     不夠長」那輪的既有 clamp() 需求，用 preserveAspectRatio="none"
     讓 SVG 可以被 CSS width/height 非等比縮放——寬度（線的粗細方向）
     固定，長度（線的方向）用 clamp() 隨螢幕寬度變化；stroke-width
     只在「長度」那個軸被拉伸，線本身粗細方向沒有跟著变形，不會變形
     走樣。 */
.page--wall .cx-hero-arrow {
    display: block;
    flex-shrink: 0;
    color: hsl(var(--foreground) / .6);
}

.page--wall .cx-hero-arrow--down {
    align-self: flex-end;
    width: 20px;
    height: clamp(3.5rem, 9vw, 6.5rem);
}

/* 客戶 2026-08-18「標題那區的箭頭下移到偏底部」——.cx-hero-cell--statement
   是 justify-content:center，四個子元素（小標／H1／次標／箭頭）本來是
   一整組置中，箭頭跟著卡在文字群組正下方、離 cell 底部還有一大截。
   margin-top:auto 是 flex 直向排列的標準「推到最下面」寫法：只要任何
   子元素有 auto margin，flex 就會把該側全部的剩餘空間讓給它，
   justify-content 那個 center 對這個子元素會失效——等於箭頭自己
   單獨貼底，前面三個文字元素不受影響、還是維持原本彼此的 .75rem
   間距，只是整組因為讓出空間而回到 cell 上方，不用另外拆 wrapper
   容器就能做到「文字一組、箭頭獨立貼底」。 */
.page--wall .cx-hero-arrow--right {
    width: clamp(3.5rem, 9vw, 6.5rem);
    height: 20px;
    margin-top: auto;
}

/* ── ②-3 statement 格：H1／次標，字級沿用全站既有規則，不特別放大 ── */
.page--wall .cx-hero-title {
    margin: 0;
    font-size: var(--text-section);
    line-height: var(--lh-section);
    font-weight: var(--fw-heading);
    letter-spacing: var(--tracking-display);
    color: hsl(var(--foreground));
}

.page--wall .cx-hero-lead {
    margin: 0;
    font-size: var(--text-body);
    line-height: var(--lh-body);
    font-weight: var(--fw-light);
    color: hsl(var(--foreground) / .78);
}

/* ── ②-4 兩個正方形圖片模組（沿用 courseItems() 真課程的插畫，固定
   顯示、不循環，不新增資料）── */
.page--wall .cx-hero-photo picture {
    position: absolute;
    inset: 0;
}

.page--wall .cx-hero-photo img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* ── ②-5 遮罩逐塊出場（只在 page load 播一次，見底部 script）──────────
   每個模組自己一塊遮罩，顏色統一用 --background（跟 Hero 底色同一顆
   token，遮罩「融入背景」才有顯影感）。方向用 --x／--y 兩種＋ --alt
   變體交錯排列，不要整批同一個方向滑開。 */
.page--wall .cx-hero-mask {
    position: absolute;
    inset: 0;
    z-index: 2;
    background: hsl(var(--background));
    transition: transform 560ms cubic-bezier(0.76, 0, 0.24, 1);
    will-change: transform;
}

.page--wall .cx-hero-mask--x { transform-origin: left; transform: scaleX(1); }
.page--wall .cx-hero-mask--x.cx-hero-mask--alt { transform-origin: right; }
.page--wall .cx-hero-mask--y { transform-origin: top; transform: scaleY(1); }
.page--wall .cx-hero-mask--y.cx-hero-mask--alt { transform-origin: bottom; }

.page--wall .cx-hero-cell.is-revealed .cx-hero-mask--x { transform: scaleX(0); }
.page--wall .cx-hero-cell.is-revealed .cx-hero-mask--y { transform: scaleY(0); }

@media (prefers-reduced-motion: reduce) {
    .page--wall .cx-hero-mask { display: none; }
}

.page--wall .cx-hero.is-cx-hero-static .cx-hero-mask { display: none; }

/* ── ②-5b RWD：768~1299.98px（實測抓出來的擠壓帶，見下）intro 格箭頭
   縮短，避免被切掉 ──
   intro 格的列高是 calc((100vw-3px)/4.5*0.8)，這個算式裡有兩顆客戶
   陸續調過的旋鈕：÷4.5 是「Hero 整體高度」（先「該區塊高縮矮一點」
   試過 ÷5，再「稍微高一點」收斂到 ÷4.5），×0.8 是「上下矩形讓上方的
   高比較矮一點」新加的——上排（intro 所在那排）因此比下排還要矮
   20%，可用空間比以前更緊。eyebrow（2 行）＋intro-desc（line-clamp
   卡死 2 行）＋箭頭原本的 clamp(3.5rem,9vw,6.5rem) 三者疊起來會超過，
   箭頭被 overflow:hidden 切掉一截。
   ⚠ 箭頭改成單一 SVG（newsletter 箭頭樣式）之後沒有獨立的「線」子
     元素可以單獨藏起來——.cx-hero-arrow--down 這個 svg 本身用
     preserveAspectRatio="none"，直接把整個 SVG（含線＋箭頭）的
     height 壓到一個較小的固定值，等比例縮小整支箭頭（不是只縮線），
     换算下來箭頭本身也會跟著變小，但視覺上仍然完整、不會出現線被
     切掉一截的破圖。這個寬度帶用 puppeteer 掃過確認 0 clip。
   ⚠ 之後如果 .cx-hero-grid 那兩顆旋鈕（÷4.5／×0.8）又調整，要重新
     用 puppeteer 掃一次寬度帶確認範圍還夠、上緣（768px）也要重新
     確認 0 clip，不能只憑公式推算。
   這段窄縮的只有 --down（intro 用）箭頭，--right（statement）那組
   不受影響——statement 格內容短、不會撐爆。 */
@media (min-width: 768px) and (max-width: 1299.98px) {
    .page--wall .cx-hero-arrow--down {
        height: 16px;
    }
}

/* 768~899.98px 這一小段比上面那個帶子更緊：intro 格本身很窄（4 欄的
   1/4），eyebrow「In-person Courses」跟 intro-desc 都會換行到 2 行，
   兩段文字加起來就已經超過這個寬度帶的可用高度，跟箭頭縮多小都沒
   關係——箭頭已經縮到 16px 還是不夠，因為問題主要出在文字，不是箭頭。
   這裡只在最窄的這一小段把 intro-desc 從 2 行再壓到 1 行（900px 以上
   eyebrow 只有 1 行、文字本身就有餘裕，不需要跟著壓）。 */
@media (min-width: 768px) and (max-width: 899.98px) {
    .page--wall .cx-hero-intro-desc {
        -webkit-line-clamp: 1;
    }
}

/* ── ②-6 RWD：≤767 手機（極窄方格會擠不下大標題，改回單欄堆疊——
   不強求正方形，這是這輪指令沒提到、也沒展示過的寬度，務實可讀
   優先）：logo／headerslot 隱藏（本來就沒有內容，小螢幕上留白是
   浪費，且手機版 header 本來就只剩 Logo + 方形 Menu，沒有這兩格要
   對應的常駐控制項）→ intro（小標＋箭頭）→ photoA（主圖）→ photoB
   （次圖，較矮）→ statement（標題＋副標）。 */
@media (max-width: 1023.98px) {
    /* ⚠ 2026-08-19 客戶「垂直線也延伸到header」——這裡原本是整組
       .cx-hero-lines 直接 display:none（舊的單欄堆疊沒有欄可分，三條
       25%／50%／75% 垂直線會變成憑空切過所有堆疊區塊的線，見下面舊
       註解原樣保留）。這輪改成 2×2 之後又有「欄」可分了：只留中間那條
       （--2，left:50%，本來就對齊 .cx-hero 自己的水平中線，2 欄跟 4 欄
       的中線是同一個位置，不用另外改 left 值），25%／75% 那兩條（４欄
       時的欄界，2 欄版面用不到）跟底部那條水平線一起收掉。
     ⚠ 舊：手機改單欄堆疊，25%／50%／75% 那三條垂直分欄線沒有欄可分了，
       跟著隱藏——不然會變成三條跟版面無關、憑空切過所有堆疊區塊的線。 */
    .page--wall .cx-hero-line--1,
    .page--wall .cx-hero-line--3,
    .page--wall .cx-hero-line--bottom {
        display: none;
    }

    /* 2026-08-19 客戶「手機版矩形留四塊就好」，附參考圖：2×2 只留
       logo／photoB（上排）、photoA／statement（下排），intro／void4／
       headerslot 三格拿掉。跟桌機版 grid-template-areas（「logo photoB
       intro headerslot」／「photoA statement statement void4」）對照，
       這其實是只留桌機版最左兩欄、拿掉右邊兩欄，不是重新設計一組新的
       配對關係——logo 跟 photoB 本來就同一列、photoA 跟 statement本來
       就同一列，手機版沿用同一個配對，只是從 4 欄縮成 2 欄。
       ⚠ logo 格本身不畫任何內容（見 markup 註解「這格不額外渲染任何
         內容...這個位置對應 header 的 logo」）——手機版 header 是全站
         統一的透明 is-compact（跟這輪稍早 member 頁面同一個機制），
         logo 格顯示出來之後，畫面上看到的「logo」其實是透出來的真
         header logo，不是這格自己畫的，這格只負責佔一個跟 photoB 同高
         的矩形位置＋參與遮罩顯影動畫。 */
    .page--wall .cx-hero-grid {
        grid-template-columns: 1fr 1fr;
        /* ⚠ 桌機那條 grid-template-rows 是 calc(100vw/4.5) 專門給「4 欄
           2 列」的方格算的，手機改 2×2，欄數變了不能照抄，但**第一列
           不能用 auto**：photoB 帶負 margin-top 把自己拉到頁面最頂端
           （見下面 .cx-hero-photo--b），auto 列高的算法只認「格子在
           列的名義起點以下那一截」，負 margin 拉出去的那段不計入，
           實測會把第一列的自動高度反而縮小成只剩 51px，logo 格
           （沒有自己的高度、純靠 stretch 撐滿列高）跟著被壓成一條窄縫。
           改成明寫固定值，列高才不受 photoB 負 margin 干擾。
           ⚠ 2026-08-19 客戶「矩形上下高差太多，下面矩形太高上面太矮」——
             第一版兩列高度分別是 clamp(120px,34vw,180px) 跟 auto（由
             photoA 的 clamp(220px,66vw,320px) 撐出來，實測 133px 對
             257px，接近兩倍差），改成兩列同一組公式（跟 photoA 一樣）
             等高——但客戶隨後「上面比較高下面比較矮」／確認「上排縮矮」，
             要的其實是**維持不等高、上排矮一截**（呼應桌機版本來就是
             「上排 ×0.8 比下排矮」的既有比例，見本檔 15907 那段），
             不是兩列打平。這裡退回 photoB 原本（改版前）就有的
             clamp(120px,34vw,180px)——那組數字本來就是「photoB＝次要
             點綴圖，較小」的既定設計（見 markup 註解），不是重新喬一個
             新比例。跟「等高版」的差別只在這個數字，不是機制：photoB
             早一輪已經拿掉自己的 height、改吃 stretch（見下面
             .cx-hero-photo--b 那段說明），所以這裡不管填多矮的固定值，
             photoB 都會正確地用負 margin「長」到頁面頂端、底邊精確貼齊
             logo 格——上一輪「太多」的真正問題是 photoB 沒填滿格子
             （底部露空白），不是矮這件事本身；那個 bug 已經修好，
             矮的比例本身是可以要回來的。 */
        grid-template-rows: clamp(120px, 34vw, 180px) auto;
        grid-template-areas:
            "logo photoB"
            "photoA statement";
    }

    /* ⚠ 2026-08-19 覆寫前一版決定：void4「有真內容了不能再跟 logo／
       headerslot 一樣直接藏起來」那條規則是 08-18 的決定，這次客戶
       「手機版矩形留四塊就好」明確只要 4 格，void4／intro 這次改回
       隱藏——不是走回頭路變回純裝飾留白的舊理由，是這次的指令本身
       就要更少格。下面 .cx-hero-cell--intro／--void4 那條手機版橫排
       樣式（flex-direction:row 等）留著不刪：兩格現在 display:none，
       那組樣式不生效但也無害，之後若這兩格要重新在手機版出現，樣式
       不用重寫。 */
    .page--wall .cx-hero-cell--headerslot,
    .page--wall .cx-hero-cell--intro,
    .page--wall .cx-hero-cell--void4 {
        display: none;
    }

    .page--wall .cx-hero-cell--intro,
    .page--wall .cx-hero-cell--void4 {
        flex-direction: row;
        align-items: center;
        padding: 1rem clamp(1rem, 4vw, 1.5rem);
    }

    /* 「小字上移到header同行」那條負 margin-top 是桌機版格線／header
       讓位量算出來的（配合 4 欄 2 列的 grid、.cx-hero 自己的
       padding-top），手機是單欄堆疊＋橫向 row 版面，同一套算式硬套
       過來會把文字拉到 logo 圖示後面（實測疊字），這裡蓋回 0，手機
       維持原本沒有上移的位置。 */
    .page--wall .cx-hero-cell--intro .cx-hero-intro-text {
        margin-top: 0;
    }

    /* 手機 intro 是橫向窄列（flex-direction:row），桌機那條直向「長細線」
       拉出來在窄窄一條橫列裡會很突兀，箭頭（單一 SVG，preserveAspectRatio
       ="none"）改成很矮的 height，整支等比例縮小成一個小箭頭，靠 cell 的
       justify-content:space-between 貼右側就夠了。
       ⚠ 只收 --down 這個變體：statement 的 --right 箭頭在手機還是橫向
         單欄、寬度足夠，不用跟著縮。 */
    .page--wall .cx-hero-arrow--down {
        align-self: center;
        height: 16px;
    }

    /* ⚠ 2026-08-24 客戶「按字體放大文字，該區塊的高度也跟著被拉高，圖片
       沒跟著放大所以露出灰色區塊了」——推翻上面 2026-08-19「下面稍微改矮
       一點」那條寫死的 height:clamp(...)。這一列（photoA｜statement）
       的列高是 grid-template-rows 的 auto（見上面 17588 行），字級放大
       會把 statement 的文字內容撐高、auto 列高跟著長高，但 photoA 這格
       自己另外寫死 height，蓋掉了 .cx-hero-grid 預設的 align-items:
       stretch，於是列高長高、photoA 卻維持原本的高度不動，底下露出
       .cx-hero-grid 自己的格線背景色（灰色）。
       ⚠ 做法比照同一節上面 .cx-hero-photo--b 已經修過的同一類問題
         （17639 那段說明）：拿掉這格自己的 height，讓 stretch 生效，
         photoA 才會跟著 auto 列高長高、貼齊 statement 的實際高度。
         .cx-hero-photo picture 本來就是 position:absolute + inset:0，
         img 是 width/height:100% + object-fit:cover（本檔 17449／17454
         行），格子多高，圖片就自動裁切填滿多高，不用額外再調圖片本身。
       ⚠ 2026-08-19 那次「下面稍微改矮」的視覺效果（跟 statement 預設
         字級剛好等高）在預設字級（sm）下不受影響——auto 列高本來就是
         由 statement 的內容撐出來的，字級沒放大時跟拿掉前的固定值幾乎
         一樣高，只有字級放大時才會跟著長高，不會露出灰底。 */
    /* ⚠ 2026-08-19 客戶「右上圖片延伸到header」——這裡原本有一條
       .cx-hero-photo--b 的手機版覆寫，先是 margin-top:0（2026-08-18
       「手機是單欄堆疊，photoB 上面接的是 intro，不是 header 讓位
       空間」那條理由，這輪改成 2×2 之後已經不成立），後來改寫死
       height:clamp(120px,34vw,180px) 想同時控制大小，但那會停用
       align-items:stretch，讓桌機版本來就有的
       margin-top:calc(-1*(clamp(...)+var(--header-h)+1px))（≈15992，
       這裡完全沒動）從「stretch 長高、底邊貼齊 logo 格」變成「整塊
       平移」，photoB 的底邊因此比 logo 格的底邊少了一個 header 讓位量
       （實測 81px），底部露出空白、圖片沒填滿格子。
       ⚠ 這裡索性把 .cx-hero-photo--b 的手機版覆寫整條拿掉：不寫
         height、也不寫 margin-top，兩者都直接沿用桌機版基底規則
         （grid-area／margin-top 那條，本檔開頭 .cx-hero-photo--b
         本體），讓 align-items:stretch 生效，photoB 才會正確地
         「往上長」而不是「往上移」，底邊精確貼齊 logo 格。 */

    .page--wall .cx-hero-cell--statement {
        padding: 1.25rem clamp(1rem, 4vw, 1.5rem);
    }
}

/* ── ③ 分類 Section：三個主題統一背景，不壓底（2026-08-11 客戶第二次指定）──
   客戶先要「每個主題壓不同明度的灰」，看過實機之後改成：
     「三個課程統一背景不壓底……每橫欄的三格課程以剛剛的三個明度灰色分格，
      不要用線區隔，該版面用矩形色塊的方式處理。」
   ⚠ 所以灰階從色帶搬到「橫排裡的三格」（見 ⑤-2），色帶本身改吃 --background
     ＝ 頁面自己的底（#F5F5F7）。等於「不壓底」：沒有額外的色塊，只是頁面地。
   ⚠ 為什麼不留 .band 預設的白：三格裡有一格就是白（paper），
     白格壓在白帶上會整格消失，讀不出「三個矩形色塊」。 */
/* 課程一覽整條色帶：標題與三個主題**共用同一塊背景**（2026-08-11 客戶：
   「不要讓他跟主題分開自己一個背景」）。
   ⚠ 底色用 --background（#F5F5F7）＝ 頁面自己的地，不是額外的色塊 ——
     三格課程的白／淺灰／深灰壓在它上面才讀得出「矩形色塊」。 */
.page--wall .cx-list {
    background: hsl(var(--background));
}

/* 標題：一條貫穿的細線，標題壓在正中間把線切開（客戶附參考圖）。
   ⚠ 線用偽元素畫、不放 <hr>：它是標題的一部分（視覺上的分隔起手式），
     不是內容上的分隔線，放進 DOM 會多一個沒有語意的節點。
   ⚠ 兩側都是 flex:1，所以標題永遠在**版心正中間**，中文字數改了也不會偏。
   ⚠ 線色沿用站上 v2 頁面同一階（--foreground 20%），沒有新色。 */
.page--wall .cx-rule-h {
    display: flex;
    align-items: center;
    gap: clamp(1rem, 3vw, 2.5rem);
    margin: 0 0 clamp(2.5rem, 5vw, 4rem);
    font-size: var(--text-sub);
    line-height: 1.6;
    font-weight: var(--fw-heading);
    letter-spacing: var(--tracking-title);
    color: hsl(var(--heading));
}

.page--wall .cx-rule-h::before,
.page--wall .cx-rule-h::after {
    content: "";
    flex: 1 1 auto;
    height: 1px;
    background: hsl(var(--foreground) / .2);
}

.page--wall .cx-rule-h-text {
    flex: 0 0 auto;
    white-space: nowrap;
}

.page--wall .cx-theme {
    scroll-margin-top: calc(var(--header-h) + clamp(1rem, 3vw, 2.5rem));
}

/* 主題之間的距離改由留白給（它們不再是各自有 padding 的色帶） */
.page--wall .cx-theme + .cx-theme {
    margin-top: clamp(3rem, 6vw, 5.5rem);
}

/* 分類標題區（2026-08-11 客戶：「標題不要有數字」→「主題標題置左，
   6 COURSES 置右，兩個高度對齊」）：左右兩欄，基線對齊。
   ⚠ align-items: baseline 是「高度對齊」的關鍵 —— 用 center 會讓 12px 的
     COURSES 對到整個左欄（標題＋介紹兩行）的**盒子**中線，看起來會偏低；
     baseline 對的是**文字底線**，而 .cx-theme-id 的基線就是它第一行
     （分類名）的基線，所以兩者切在同一條線上。
   ⚠ 編號已移除；三個分類共用同一組樣式，沒有任何 nth-child 差異化。
     ⚠ 2026-08-18：客戶原本要求「三個分類必須具有相同的視覺權重」，這裡
       指的是標題區的排版規則（三個分類共用同一組樣式），這條依然成立；
       但同一天稍早客戶又指定三個主題底下的課程張數改成不對稱的 3／1／2
       （見 courses_list.php $themeShowCounts），視覺權重的「相同」只
       適用到標題列，不再適用到底下課程卡片的張數與呈現方式。 */
.page--wall .cx-theme-head {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    gap: .75rem clamp(1.5rem, 3vw, 3rem);
    align-items: baseline;
    padding-bottom: clamp(1.75rem, 3.5vw, 2.75rem);
}

.page--wall .cx-theme-id {
    display: flex;
    flex-direction: column;
    gap: clamp(.625rem, 1.2vw, .875rem);
}

.page--wall .cx-theme-name {
    margin: 0;
    font-size: var(--fs-4);
    line-height: 1.4;
    font-weight: var(--fw-heading);
    letter-spacing: var(--tracking-title);
    color: hsl(var(--heading));
}

.page--wall .cx-theme-note {
    margin: 0;
    max-width: 34em;
    font-size: var(--text-body);
    line-height: var(--lh-body);
    font-weight: var(--fw-light);
    letter-spacing: var(--tracking);
    color: hsl(var(--muted-foreground));
}

.page--wall .cx-theme-count {
    justify-self: end;
    margin: 0;
    font-family: var(--font-en);
    font-size: var(--text-meta);
    line-height: var(--lh-meta);
    letter-spacing: var(--tracking-meta);
    color: hsl(var(--slate));
    white-space: nowrap;
}

/* ── ④ 橫排容器 ─────────────────────────────────────────────────────
   ⚠ 用 flex ＋ 每個 pane 自己的 width 過場，而不是 grid-template-columns 過場：
     grid 的軌道尺寸在多數瀏覽器上不能穩定補間，切換時會直接跳格。
   ⚠ 高度寫死（客戶要求切換前後固定），gap 為 0 —— 三格直接相鄰，
     分隔改回線條（見下面 ⑤-2），不是色塊邊界。
   ⚠ 2026-08-18 客戶把「用矩形色塊分隔」的舊指定推翻，改回「用線條分隔」
     ——gap 依然是 0（卡片還是緊鄰），因為分隔線這次是畫在每格自己的
     border 上（見 ⑤-2），不是靠 gap 露出底色，兩種技巧的 gap 需求不同，
     不能沿用同一套判斷去猜這裡為什麼還是 0。 */
.page--wall .cx-hz {
    --cx-n: 3;
    --cx-tab-w: clamp(4.5rem, 6vw, 6.5rem);
    --cx-hz-h: clamp(22.5rem, 30vw, 27.5rem);
    --cx-spine: clamp(2.5rem, 3.4vw, 3.25rem);

    display: flex;
    height: var(--cx-hz-h);
    overflow: hidden;
}

/* 2026-08-18 課程一覽縮減後（見 courses_list.php $themeShowCounts），學理／
   人際關係兩個主題的單組課程數不再固定是 3（分別只剩 1／2 門）。--cx-n
   平常由 JS 從 data-cx-n 寫進 CSSOM（見底部 script 的 groups.forEach），
   但那要等 inline script 執行到才生效；這裡用屬性選擇器直接對應標記上
   本來就有的 data-cx-n，讓 JS 尚未跑到的那一刻（或關閉 JS）展開寬度算式
   也拿到正確的實際張數，不會用預設的 3 去除，把單卡/雙卡擠出一截空白。 */
.page--wall .cx-hz[data-cx-n="1"] { --cx-n: 1; }
.page--wall .cx-hz[data-cx-n="2"] { --cx-n: 2; }

.page--wall .cx-theme .cx-hz + .cx-hz {
    margin-top: clamp(1.25rem, 2.4vw, 2rem);
}

/* 一門課 ＝ 一格。收合時只有窄條寬，展開時吃掉剩下的全部。
   ⚠ min-width:0 一定要寫：flex 子項的預設 min-width 是 auto，
     裡面的長課名會把收合的窄條頂寬，整排比例就跑掉了。 */
.page--wall .cx-pane {
    display: flex;
    width: var(--cx-tab-w);
    min-width: 0;
    height: 100%;
    overflow: hidden;
    transition: width 380ms var(--ease-out);
}

.page--wall .cx-pane.is-open {
    width: calc(100% - (var(--cx-n) - 1) * var(--cx-tab-w));
}

/* ── ⑤-2 三格改回線條分隔（2026-08-18 客戶推翻矩形色塊指定）───────────────
   客戶：「現在小卡是用顏色區分，請把顏色區分改成用線條，小卡不要填色改
          兩條垂直線描邊，不要方塊」，隨後補充「其他選項也不要填色，也是
          用垂直線分割」——確認 paper/grey/dark 三個色調全部拿掉背景，
          不是只改其中一個。三格從此視覺完全一致，不再靠顏色分深淺；
          分隔只靠每格自己的左右 border（兩條垂直線＝一格的左邊線＋
          右邊線，不畫上下邊，避免變成「方塊」——客戶原話明確排除方塊）。
   ⚠ box-sizing:border-box 明寫（不假設全域 reset 一定有）：不寫的話兩條
     1px border 會加到 width 之外，--cx-n 那組展開寬度算式會多出誤差。
   ⚠ .cx-pane--dark/--grey/--paper 這三個 class 仍然保留在標記上
     （courses_list.php 的 $paneTone 陣列沒有拿掉）：純粹當語意鉤子，
     不掛任何顏色樣式了——2026-08-11 這三色曾經被客戶要求「從線條改矩形
     色塊」，這次又被推翻回線條，同一個來回已經發生兩次，保留 class
     只是讓下次如果又要改回色塊，不必重新設計標記，色票數值可以從
     git 歷史（這次改動前的版本）直接找回，不在這裡留一份死註解。
   ⚠ 線色沿用站上既有的細線 token hsl(var(--foreground) / .2)——跟本頁
     開頭 .cx-rule-h 的標題線、下面新增的 .cx-theme-rule 同一個顏色，
     不新增色票。 */
.page--wall .cx-pane {
    box-sizing: border-box;
    border-left: 1px solid hsl(var(--foreground) / .2);
    border-right: 1px solid hsl(var(--foreground) / .2);
}

/* ── 相鄰卡片交界處雙線重疊變粗（2026-08-24 客戶「該頁面底下的小卡每個
   小卡皆有兩條左右線所以重疊到看起來也會變粗」）──────────────────────
   上面那條規則讓每格各自畫左右 border，這是刻意的設計（見上一節說明：
   「兩條垂直線＝一格的左邊線＋右邊線」）。但 .cx-pane 是 .cx-hz 裡沒有
   間距的 flex 子項（實測 getBoundingClientRect：前一格的 right 完全
   等於下一格的 left，零間距），交界處因此是「前一格的右邊線」疊在
   「下一格的左邊線」正上方——兩條 20% 黑的半透明線疊在同一個 1px，
   合成後比其他只有單一條線的路段更深，客戶看到的「變粗」跟本檔前一節
   Hero 裝飾線那個 bug 是同一個家族（半透明線合成不一致），但這裡的
   成因是「兩條線重疊」不是「同一條線疊在不同底色上」，做法也不同。
   修法：拿掉「不是第一格」的左邊線，只留第一格自己的左邊線（畫最外側
   邊界）跟每一格自己的右邊線（畫跟下一格的交界）——每個交界從此只剩
   一條線，不再是兩條疊加。用相鄰兄弟選擇器 `.cx-pane + .cx-pane`（前面
   有其他 .cx-pane 兄弟，即「非第一格」），不用 :first-child 的否定式，
   語意更直接。 */
.page--wall .cx-pane + .cx-pane {
    border-left: 0;
}

/* ⚠ 2026-08-11 曾經指定「深灰格內容翻白」是配合色塊填色（見上，已推翻）—
     背景拿掉之後文字要跟著改回站上預設的深字色，不然白字疊在淺底上會
     看不見。.on-dark（字重補償，本來是配合翻白字色一起加的）跟著從
     courses_list.php 的標記拿掉，這裡不需要保留任何 .cx-pane--dark
     專屬的文字色／hover／focus 覆寫規則。 */

/* ── ⑤-2b 卡片列上下各一條滿版水平線（2026-08-18 客戶「水平線要整個頁面寬
   的長」，隨後追加「小卡上方也加水平線」）───────────────────────────────
   客戶把小卡改成線條分隔之後，先要求卡片列下方配一條水平線，長度是
   「整個頁面寬」（視窗邊到邊，不是只到 .cx-grid 的版心寬），接著追加
   卡片列上方也要同一條線——courses_list.php 直接在標記上放兩個
   <span class="cx-theme-rule">，一個在 .cx-theme-head 之後、
   .cx-theme-track 之前，一個在 .cx-theme-track 之後，CSS 完全共用同
   一份規則，不需要 --top/--bottom 修飾子：定位靠**正常文件流**，線放在
   標記的哪個位置就出現在哪裡，不必另外算 top/bottom 座標。
   ⚠ 這個區塊巢狀很深（.cx-theme → .cx-grid → .section-bleed-inner →
     .cx-list.section-bleed），.section-bleed-inner 本身還有 max-width
     +margin:auto 置中（1920 寬時只有 1728px，兩側各留 96px 空隙），
     這段空隙不是任何一層的 padding 變數，沒辦法用「疊加各層 padding」
     算出滿版寬度——**只有橫向**改用 100vw + 負邊距置中技巧跳開容器限制，
     垂直方向完全交給文件流，不摻 position:absolute。
   ⚠ 不能掛在 .cx-hz 上：.cx-hz 自己有 overflow:hidden（手風琴收合動畫
     需要），掛在裡面的 100vw 線會被裁成只剩 .cx-hz 自己的寬度，這也是
     為什麼兩條線都是 .cx-theme 的直屬子層（.cx-theme-head／
     .cx-theme-track 的手足），不是塞進卡片列內部。
   ⚠ .cx-theme 本身經由 .section-bleed-inner 的 margin:auto 已經水平置中
     於視窗，所以 left:50% 算出來的中線跟視窗中線重合，100vw 的線用
     margin-left:-50vw 把左緣拉回那條中線再往左 50vw，等於整條線left邊
     對齊視窗正左緣（等寬 100vw 右緣自然對齊視窗正右緣）。
   ⚠ 已實測 document.documentElement.scrollWidth 沒有超出 clientWidth
     （見驗證記錄）：headless Chrome 預設保留捲軸空間，100vw 理論上會比
     實際可視寬度多出捲軸寬度，這裡實測沒有出現，暫不需要額外的
     overflow-x:clip 防呆；如果之後在保留捲軸空間的瀏覽器上觀察到多出
     的水平捲動，再回來加。 */
.page--wall .cx-theme-rule {
    display: block;
    position: relative;
    left: 50%;
    width: 100vw;
    height: 1px;
    margin-left: -50vw;
    background: hsl(var(--foreground) / .2);
    pointer-events: none;
}

/* ── ⑤ 收合窄直條／展開後的號碼書背（同一顆 button）─────────────────
   收合：佔滿整條，序號在上、直排短標題在中、箭頭在下 —— 整條都可點。
   展開：縮成左側的號碼書背，只留序號，把寬度讓給內容。
   ⚠ 用 flex-basis 100% → --cx-spine 做過場，與外層的 width 同一條曲線，
     兩者一起動才看得出「同一格在伸縮」而不是兩個元件各自跑。 */
.page--wall .cx-pane-tab {
    display: flex;
    flex: 0 0 100%;
    flex-direction: column;
    align-items: center;
    justify-content: space-between;
    gap: .75rem;
    height: 100%;
    padding: clamp(1rem, 2vw, 1.5rem) .5rem;
    border: 0;
    border-radius: 0;
    background: transparent;
    appearance: none;
    font: inherit;
    color: inherit;
    cursor: pointer;
    transition: flex-basis 380ms var(--ease-out), background var(--transition);
}

.page--wall .cx-pane.is-open .cx-pane-tab {
    flex-basis: var(--cx-spine);
    cursor: default;
    justify-content: flex-start;
}

.page--wall .cx-pane-no {
    font-family: var(--font-en);
    font-size: var(--text-meta);
    line-height: 1;
    letter-spacing: var(--tracking-meta);
    color: hsl(var(--slate));
    transition: color var(--transition);
}

.page--wall .cx-pane.is-open .cx-pane-no {
    color: hsl(var(--heading));
}

/* 直排中文短標題。
   ⚠ 用 writing-mode: vertical-rl（原生直排）而不是 rotate(90deg)：
     旋轉是把整塊橫排文字轉過去，中文會變成側躺、標點也不會換成直排字符。
     text-orientation: upright 讓每個字維持正立（直排中文的正確樣子）。
   ⚠ 展開時整段收掉：書背只留序號，不然三個字會被 --cx-spine 擠成一條。 */
.page--wall .cx-pane-short {
    writing-mode: vertical-rl;
    text-orientation: upright;
    font-size: var(--text-meta);
    line-height: 1.2;
    letter-spacing: var(--tracking-meta);
    color: hsl(var(--heading));
    white-space: nowrap;
    overflow: hidden;
    transition: opacity 200ms var(--ease-out);
}

.page--wall .cx-pane.is-open .cx-pane-short,
.page--wall .cx-pane.is-open .cx-pane-sign {
    opacity: 0;
    pointer-events: none;
}

/* 箭頭：沿用站上既有的字符箭頭，不引入 icon 系統 */
.page--wall .cx-pane-sign::after {
    content: "\2192";
    display: block;
    font-family: var(--font-en);
    font-size: var(--text-meta);
    line-height: 1;
    letter-spacing: 0;
    color: hsl(var(--slate));
    transition: transform var(--transition), color var(--transition);
}

/* ── ⑥ 展開內容：左文 1fr／右圖 1fr ─────────────────────────────────
   ⚠ 客戶指定「必須維持 1:1」—— 兩欄都是 1fr，號碼書背在 .cx-pane-body 之外，
     不會吃掉任何一邊的寬度，所以文字與圖片永遠等寬。
   ⚠ 收合的那幾格 body 寬度是 0（tab 佔滿 100%），內容用 opacity 收掉，
     切換時才有「舊的先淡出、新的再淡入」的層次。 */
.page--wall .cx-pane-body {
    flex: 1 1 auto;
    min-width: 0;
    height: 100%;
    opacity: 0;
    transition: opacity 220ms var(--ease-out);
}

.page--wall .cx-pane.is-open .cx-pane-body {
    opacity: 1;
    transition-delay: 140ms;
}

.page--wall .cx-pane-in {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    height: 100%;
}

.page--wall .cx-pane-text {
    display: flex;
    flex-direction: column;
    min-width: 0;
    overflow: hidden;
    padding: clamp(1rem, 2vw, 1.75rem) clamp(1.25rem, 2.4vw, 2.25rem) clamp(1rem, 2vw, 1.75rem) 0;
}

/* 分類文字與狀態膠囊排同一行：膠囊本身 40px 高，另起一行會多吃掉 34px，
   而這一格的高度是固定的（切換時不能變），排一行才塞得下按鈕。 */
.page--wall .cx-pane-label {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .5rem clamp(.75rem, 1.6vw, 1.25rem);
    margin: 0;
    font-size: var(--text-meta);
    line-height: var(--lh-meta);
    letter-spacing: var(--tracking-meta);
    color: hsl(var(--slate));
}

/* 狀態膠囊：膚色實心（客戶「標籤膚色」）。
   ⚠ --nude 是站上既有的具名色（#F2E3D0），不是新色票；黑字壓上去 16.8:1。
   ⚠ 填實心之後底色就與腳下的色帶無關，所以深灰帶上不必再寫一份覆寫。
   ⚠ 尺寸收一階：.tag-pill 原本是 40px 高、14px 字，排在 12px 的分類文字旁邊
     會變成那一行的主角。 */
.page--wall .cx-stat {
    min-height: 28px;
    padding: 0 .75rem;
    background: hsl(var(--nude));
    border-color: hsl(var(--nude));
    font-size: var(--text-meta);
    letter-spacing: var(--tracking-meta);
    color: hsl(var(--heading));
}

.page--wall .cx-card-sep { margin-inline: .5rem; opacity: .6; }
.page--wall .cx-dot      { margin-inline: .5rem; opacity: .6; }

/* ⚠ 課名最多兩行、簡介最多三行：整組高度是固定的，不截的話長課名會把
     底下的堂數與「查看課程」擠出容器外（overflow:hidden 會直接吃掉）。
     -webkit-line-clamp 要三件事同時成立：display:-webkit-box ＋
     -webkit-box-orient:vertical ＋ overflow:hidden，少一個都不會截。 */
.page--wall .cx-pane-title,
.page--wall .cx-pane-desc {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.page--wall .cx-pane-title {
    margin: .75rem 0 0;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    font-size: var(--text-sub);
    line-height: 1.6;
    font-weight: var(--fw-heading);
    letter-spacing: var(--tracking-title);
    color: hsl(var(--heading));
}

.page--wall .cx-pane-desc {
    margin: .875rem 0 0;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    font-size: var(--text-body);
    line-height: var(--lh-body);
    font-weight: var(--fw-light);
    letter-spacing: var(--tracking);
    color: hsl(var(--muted-foreground));
}

/* 堂數與連結壓到文字欄底部，三門課切換時這一列的位置完全不動 */
.page--wall .cx-pane-meta {
    margin: auto 0 0;
    padding-top: clamp(.875rem, 1.8vw, 1.25rem);
    font-size: var(--text-meta);
    line-height: var(--lh-meta);
    letter-spacing: var(--tracking-meta);
    color: hsl(var(--slate));
}

/* CTA：站上既有的 .btn-pill.btn-pill--sm。
   ⚠ 配色一行都不寫 —— 全站 .btn-pill 早就是「藍底（--btn-fill ＝ AERIAL）
     hover 膚色」，2026-08-03 客戶定的規則，而且明確撤掉了深底翻面，
     所以深灰帶上的這顆鈕與淺底完全一樣（那一節的註解有記）。
   ⚠ align-self:flex-start：文字欄是 flex column，不釘住的話膠囊會被拉成整行寬。 */
.page--wall .cx-pane-cta {
    align-self: flex-start;
    margin-top: clamp(.75rem, 1.6vw, 1rem);
}

.page--wall .cx-arrow {
    font-family: var(--font-en);
    font-size: var(--text-meta);
    line-height: 1;
    letter-spacing: 0;
    transition: transform var(--transition);
}

/* 右欄圖片：填滿那一格，cover 不變形。
   ⚠ 格子的長寬比由「固定組高 ÷ 半個展開寬」決定，不會剛好等於素材的 4:3，
     所以一定會裁 —— object-position 往上帶一點，人物的頭不會被切掉
     （與第 12 節 .editor-figure 的 50% 22% 同一個處理）。 */
.page--wall .cx-pane-media {
    margin: 0;
    min-width: 0;
    height: 100%;
    overflow: hidden;
    background: hsl(var(--foreground) / .04);
}

.page--wall .cx-pane-media picture,
.page--wall .cx-pane-media img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: 50% 42%;
}

/* ── ⑦ 互動 ─────────────────────────────────────────────────────────── */
@media (hover: hover) {
    .page--wall .cx-pane:not(.is-open) .cx-pane-tab:hover { background: hsl(var(--foreground) / .04); }
    .page--wall .cx-pane:not(.is-open) .cx-pane-tab:hover .cx-pane-sign::after {
        transform: translateY(3px);
        color: hsl(var(--heading));
    }
}

.page--wall .cx-pane-tab:focus-visible {
    outline: 2px solid hsl(var(--foreground));
    outline-offset: -3px;
}

/* ── ⑧ 減少動態 ─────────────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
    .page--wall .cx-pane,
    .page--wall .cx-pane-tab,
    .page--wall .cx-pane-body,
    .page--wall .cx-pane-short,
    .page--wall .cx-arrow,
    .page--wall .cx-pane-sign::after { transition: none; }
}

/* ── ⑨ RWD：992–1199（小桌機／平板橫向）───────────────────────────────
   維持三門課同一橫排，只把窄條與組高收一階。 */
@media (max-width: 1023.98px) {
    .page--wall .cx-hz {
        --cx-tab-w: clamp(4rem, 5.6vw, 5.5rem);
        --cx-hz-h: clamp(21.5rem, 36vw, 25rem);
        --cx-spine: clamp(2.25rem, 3.2vw, 2.75rem);
    }
}

/* ── ⑩ RWD：768–991（平板直向）───────────────────────────────────────
   客戶：「平板直向若寬度不足，可以適度縮小收合直條寬度，仍優先保留橫向 Accordion。」
   ⚠ 所以這一段**不換版**，只把窄條壓到 56–64px、組高降到 18rem 一階；
     文字欄的行長變窄，簡介收成兩行。 */
@media (max-width: 1023.98px) {
    .page--wall .cx-hz {
        --cx-tab-w: clamp(3.5rem, 7vw, 4rem);
        --cx-hz-h: clamp(20rem, 44vw, 23rem);
        --cx-spine: 2rem;
    }

    .page--wall .cx-pane-desc { -webkit-line-clamp: 2; line-clamp: 2; }
    .page--wall .cx-pane-text { padding-right: clamp(1rem, 2vw, 1.5rem); }
}

/* ── ⑪ RWD：≤767 手機 ────────────────────────────────────────────────
   客戶：「手機版因寬度不足，可以改為垂直單開式 Accordion：一門展開、另外兩門
          收合為橫向長條，展開內容改為圖片與文字上下排列。」
   ⚠ 高度改回 auto：橫排那組寫死的 --cx-hz-h 在單欄會把三門課壓進 22rem 裡。
   ⚠ tab 從直排改回橫排：writing-mode 要一併還原成 horizontal-tb，
     不然短標題在橫長條裡還是直的。
   ⚠ 展開動畫改回高度（grid-template-rows 0fr→1fr）：寬度在單欄沒有伸縮空間。
   ⚠ 圖片排在文字**上方**用 grid-row 換位置，不動 DOM 順序 ——
     讀屏仍然先讀到文字，視覺才是圖在上。 */
@media (max-width: 1023.98px) {
    /* 2026-08-13 客戶第四輪：「三個小卡改水平拖移方式，不要三個直立展開」——
       改成跟首頁「近期課程」那條輪播（.course-grid／.course-cell，本檔 7098
       行起）同一套機制：原生 overflow-x + scroll-snap，不是自己重算位移。
       ⚠ 這裡刻意**不**照抄 home.js 那組滑鼠拖曳（pointerdown/pointermove…）：
         那組是特地為了「桌機滑鼠沒有原生橫向手勢」而寫的——這個決定後來
         被客戶推翻（見下面「滑鼠拖曳」那一段，courses_list.php 已經照
         home.js courseRail 那組移植了 pointer-drag），這裡只保留原本的
         判斷脈絡，不重寫。
       ⚠ 每張卡維持自己完整的內容（圖貼邊＋文字留白，上一輪定案），
         courses_list.php 內嵌 script 的 isMobile() 仍然讓每一門課保持
         is-open（inert=false）——卡片是靠橫向捲動找到，不是收合起來，
         但沒有被捲到眼前的那兩張內容還是要能被讀屏／鍵盤讀到。

       ── 第五輪：「一個主題留一排」──────────────────────────────────────
       客戶：一個主題（學思／學理／人際關係）底下原本兩組（各三門）在手機
       是兩條各自獨立的捲動軌道，客戶要的是**合成同一條**，滑一次看完六門。
       ⚠ 桌機／平板兩組手風琴的結構完全不能動（是客戶明確要的「兩組狀態
         互相獨立」），所以真正的捲動容器**不能是 .cx-hz 本身**——改成
         courses_list.php 新增的 .cx-theme-track（只在這個斷點生效，包住
         同一主題底下的兩組 .cx-hz，不含 .cx-theme-head）。
       ⚠ .cx-hz 在這裡改成 display:contents：讓它「消失」成純語意分組，
         底下的 .cx-pane 直接攤平成 .cx-theme-track 的 flex 子項，兩組六門
         課因此連成一條——跟本檔既有的 .svc-intro（手機版用 display:contents
         打散排序）同一套技巧，不是新發明的做法。
         ⚠ display:contents 的元素不能命中滑鼠／觸控事件（沒有自己的
           盒子可以被 hit-test），所以拖曳／進場預覽兩組 JS 都掛在
           .cx-theme-track 上，不是掛在 .cx-hz 上——見 courses_list.php
           內嵌 script 最下面新增的 tracks.forEach()。 */
    .page--wall .cx-hz { display: contents; }

    .page--wall .cx-theme-track {
        display: flex;
        flex-wrap: nowrap;
        height: auto;
        overflow-x: auto;
        overflow-y: visible;
        scroll-snap-type: x mandatory;
        scroll-behavior: smooth;
        /* ⚠ 2026-08-27 客戶「小卡在手機上滑不過去像被卡住…把那個功能移除」
           ——這裡原本是 `touch-action: pan-y`（2026-08-25 配合「觸控改由
           JS 接管」加的，pan-y 等於告訴瀏覽器「橫向不要自己捲，交給 JS」）。
           JS 那邊已經把觸控整組交還原生捲動（見 courses_list.php 的
           pointerdown 同日註解），這裡就**不能**再留 pan-y，否則瀏覽器
           照樣不肯橫向捲、而 JS 也不再接手，兩邊都不動＝客戶說的「卡住」。
           拿掉之後回到預設 auto：橫向原生捲動＋上面的 scroll-snap-type
           一起生效，再由 scrollend 校正補精準置中。
           ⚠ 這兩處是一組的，要改要一起改：CSS 的 touch-action 與
             courses_list.php 的 pointerdown/pointermove 觸控分支。 */
        gap: clamp(1rem, 4vw, 1.25rem);
        /* overflow-x:auto 會逼 overflow-y 一起變成 auto（規格行為，關不掉），
           小數高度被捲動容器的整數 clientHeight 截斷、卡片下緣露餡 1px 的坑
           跟 .course-grid 同一個（本檔 10989 行有記），用同一帖藥醫。 */
        padding-bottom: 2px;
        scrollbar-width: none;
        -ms-overflow-style: none;
        cursor: grab;
        /* 2026-08-13 客戶：「右邊有留邊，不要留邊讓小卡直接延伸過去」——
           .cx-theme-track 是 .cx-grid 的子孫層，繼承了它
           padding-inline: var(--article-x) 的版心留白，右邊那張「露一角」
           的卡因此在到達留白之前就先撞到父層的邊界，停在留白起點、
           碰不到螢幕真正的右緣。
           ⚠ 用負邊距把右邊那份留白抵銷掉（讓可捲動內容一路延伸到螢幕真右緣），
             再用等量的 padding-right 補回來——不是補在「卡片碰不到邊」那件事上，
             是補在「捲到底之後，最後一張卡右邊也要留一點呼吸空間」，兩者是
             不同階段：捲動途中／中段的卡看得到延伸到底，捲到最後一張才會停在
             留白後面，不會整張貼死在螢幕邊緣。
           ⚠ 左邊不動：第一張卡本來就該跟頁面其他標題（01／學思…）對齊在
             同一條 --article-x 版心線上，只有右邊要出血。 */
        margin-right: calc(var(--article-x) * -1);
        padding-right: var(--article-x);
    }

    .page--wall .cx-theme-track::-webkit-scrollbar { display: none; }

    /* 滑鼠拖曳中：關掉 snap／smooth，scrollLeft 才會 1:1 跟著游標，不會被
       mandatory snap 拉扯（跟 .course-grid.is-dragging 同一個坑，本檔
       9062 行）。游標與圖片的兩條防呆也照抄同一份：卡片是 <a> 包 <img>，
       原生「拖曳圖片／連結」手勢不關掉會搶走 pointermove。 */
    .page--wall .cx-theme-track.is-dragging {
        cursor: grabbing;
        scroll-snap-type: none;
        scroll-behavior: auto;
        user-select: none;
        -webkit-user-select: none;
    }

    .page--wall .cx-theme-track a,
    .page--wall .cx-theme-track picture,
    .page--wall .cx-theme-track img {
        -webkit-user-drag: none;
    }

    /* ⚠ 2026-08-24 客戶「請改為每個預設都跟學理單張小卡排版，三張跟兩張時
       就是底下會多自動切換可水平拖曳」——推翻下面 2026-08-18 那條「78% 寬
       露一角暗示還能滑」的設計，客戶回報那個「露一角」在手機寬度看起來像
       「小卡內容跑版靠右、圖片沒對齊」，不是預期的暗示效果。改成每張卡都
       跟學理（原本靠 :only-child 撐滿）一樣 100% 滿版，靠新增的自動切換
       ＋可拖曳（courses_list.php 內嵌 script 的 tracks.forEach 新增段落）
       取代「露一角」當提示，不再需要視覺上先偷跑一小段下一張。
       下面 :only-child 規則因此變成多餘（100%＝100%），但它是明確指名
       學理這個情境的既有規則，不影響現在的效果、留著也無害，不特地刪除。 */
    .page--wall .cx-pane {
        display: block;
        /* var(--cx-track-w) 是 courses_list.php 內嵌 script 現量
           track.clientWidth（可視寬度，含 padding）寫入的 px 值；
           100% 只是 JS 還沒跑時（理論上不會發生，寫著保底）的退回值，
           會比可視寬度窄一個 padding-right，見上面新增的說明。 */
        flex: 0 0 var(--cx-track-w, 100%);
        width: auto;
        height: auto;
        overflow: hidden;
        scroll-snap-align: start;
        /* 2026-08-25：配合 courses_list.php 新增的 scrollend 校正機制
           （客戶 iPhone 430 寬實測「捲過的卡片還是靠右」，判斷是原生
           慣性捲動／scroll-snap 沒有精準停在整數張卡的位置）——
           scroll-snap-stop:always 強制快速滑動時每一張都要停一下，
           不會一次滑過去直接跳兩三張，降低單次捲動距離、間接降低單次
           需要校正的誤差量，是 JS 校正之外的 CSS 層級輔助，不是取代它。 */
        scroll-snap-stop: always;
        transition: none;
    }

    /* ⚠ 2026-08-25 客戶反覆回報「靠右」「左邊的線就是沒貼在圖片上」——
       折騰了兩天、修了 scroll-snap-stop／JS 接管拖曳＋像素級校正好幾輪
       都沒解決，因為方向一直查錯：量 .cx-pane／.cx-pane-media 的座標
       從頭到尾都沒問題（每次都對齊），真正的缺陷不是「卡片停在哪」，
       是「線根本沒畫出來」。
       本檔 ≈17924 的 `.cx-pane + .cx-pane { border-left: 0; }` 是
       2026-08-24 專門為桌機手風琴寫的：桌機 .cx-hz 底下卡片零間距緊鄰，
       前一格的右邊線會跟下一格的左邊線疊在同一個 1px 上、合成後變粗，
       所以拿掉「非第一格」的左邊線，只留右邊線當交界線，兩條線疊加的
       問題才會消失。但這條規則沒有限定在桌機專屬的 media query 裡，
       手機版（這裡，卡片改用 gap: clamp(1rem,4vw,1.25rem) 真的隔開，
       不是零間距）也一併繼承了它——手機版每一排除了第一張，其他卡片
       全部沒有左邊線：使用者看到的「左邊那條線」其實是前一張卡片自己
       的右邊線，飄在 gap 中間、沒有貼到目前這張卡片的圖片上，视觉重心
       因此偏右，正是客戶說的「靠右」「線沒貼在圖片上」。
       ⚠ 手機版卡片間有真正的 gap，兩線疊加變粗的前提在這裡不成立，
         直接把左邊線補回來即可，不需要仿照桌機那套「只留一條交界線」
         的邏輯。 */
    .page--wall .cx-pane + .cx-pane {
        border-left: 1px solid hsl(var(--foreground) / .2);
    }

    /* 2026-08-18 學理主題縮減到只剩 1 門課，整條 .cx-theme-track 只有這一張
       卡——78% 寬會在右邊留一截空白，暗示「還能往右滑看更多」，但其實已經
       沒有下一張了。:only-child 抓的是這張卡在真正 DOM 裡的親生手足（它的
       父層 .cx-hz 底下只有它一個 .cx-pane），不受 display:contents 影響：
       那個屬性只改變 flex 版面怎麼攤平子孫，不改變選擇器看到的 DOM 關係。
       只有這一種情況會命中（3 門、2 門的組都有手足，选择器不會誤觸）。
       ⚠ 2026-08-24 補：上面基礎規則已經改成 var(--cx-track-w)（JS 現量
       可視寬度，見上面新註解），這條原本寫死的 flex-basis:100% 跟著換成
       同一個變數——不換的話學理會是 328px（百分比對 content-box 算出來
       的舊值），另外兩個主題的單卡卻是 359px（現量可視寬度），同樣是
       「單張滿版卡」寬度卻對不起來，跟客戶「每個預設都跟學理排版一致」
       的要求矛盾，所以這裡也要跟著換，不能只留在原本的寫死值。 */
    .page--wall .cx-pane:only-child {
        flex-basis: var(--cx-track-w, 100%);
    }

    .page--wall .cx-pane.is-open { width: auto; }

    @media (prefers-reduced-motion: reduce) {
        .page--wall .cx-hz { scroll-behavior: auto; }
    }

    .page--wall .cx-pane-tab {
        flex-direction: row;
        align-items: center;
        justify-content: flex-start;
        gap: clamp(.875rem, 3vw, 1.25rem);
        width: 100%;
        height: auto;
        /* 內距跟下面 .cx-pane-text 同一份 clamp，號碼才會跟標題對齊
           （2026-08-13 第三輪：圖片貼邊、文字留白，號碼跟著文字那一邊）。
           ⚠ 2026-08-24 客戶「小卡內容沒靠左對齊」，.cx-pane-text 的左內距
           改成 0（見下面新註解）之後，這裡沒有跟著改的話「01」會繼續
           貼著舊的 20px 左內距、標題卻已經靠左貼齊，兩者反而對不起來，
           違背這條規則自己說的「號碼才會跟標題對齊」——左內距一併改 0，
           右／上／下不動，保持跟標題同步。
           ⚠ 2026-08-25：.cx-pane-text 的左內距改回半份（見下面新註解），
           這裡跟著同步改成一樣的 clamp(.625rem,2vw,.75rem)，「01」才會
           繼續跟標題對齊，不是各自改各自的。 */
        padding: clamp(.875rem, 3vw, 1.125rem) clamp(1.25rem, 4vw, 1.5rem) clamp(.875rem, 3vw, 1.125rem) clamp(.625rem, 2vw, .75rem);
        transition: none;
    }

    .page--wall .cx-pane.is-open .cx-pane-tab {
        flex-basis: auto;
        justify-content: flex-start;
    }

    .page--wall .cx-pane-short {
        writing-mode: horizontal-tb;
        text-orientation: mixed;
        flex: 1 1 auto;
        text-align: left;
    }

    /* 展開時短標題留著（橫長條有空間），只把箭頭翻成向上 */
    .page--wall .cx-pane.is-open .cx-pane-short { opacity: 1; pointer-events: auto; }
    .page--wall .cx-pane.is-open .cx-pane-sign { opacity: 1; }
    .page--wall .cx-pane.is-open .cx-pane-sign::after { content: "\2191"; }

    .page--wall .cx-pane-sign::after { content: "\2193"; }

    .page--wall .cx-pane-body {
        display: grid;
        grid-template-rows: 0fr;
        height: auto;
        opacity: 0;
        transition: grid-template-rows 320ms var(--ease-out), opacity 260ms var(--ease-out);
    }

    .page--wall .cx-pane.is-open .cx-pane-body {
        grid-template-rows: 1fr;
        opacity: 1;
        transition-delay: 0s;
    }

    /* 2026-08-13 客戶：「實體課程小卡手機不好瀏覽，呈現方式改跟首頁服務專區
       手機版一樣」——手機版整組 Accordion 停用，courses_list.php 內嵌 script
       的 isMobile() 讓每一門課在這個寬度永遠標成 is-open，不再需要點擊展開。
       ⚠ 這個決定經過三輪調整，記錄下來免得又繞回去：
         第一輪：沿用原本「展開狀態」的圖上文下直式卡（圖 3:2、文字 0 內距）。
         第二輪：客戶說「跟服務專區手機版一樣」——服務專區的 .svc-tile 在
           ≤991.98px 其實是左文右圖兩欄（見本檔 9713 行），改成左右並排。
         第三輪（本次，客戶附知識專區卡參考圖）：「不要用左文右圖，用上下的，
           圖片直接貼邊滿版，但文字要留 margin」——退回上下排列，但不是退回
           第一輪那份：這次圖片要真正貼齊卡片左右緣（不留內距），文字則要
           有內距，兩者待遇不同。做法照抄站上既有的 .pcard（知識／課程／
           電子報卡共用元件，見本檔 12659 行）：外層完全不留 padding，圖是
           第一個子元素自然貼滿；padding 只放在文字容器上，圖與文字因此
           一個貼邊、一個留白。
       ⚠ 圖片不設圓角（border-radius: 0）：色塊本身是「矩形」（客戶多次指定
         的設計語言，見本節開頭），跟 .pcard 用圓角的卡片語彙不同，這裡只借
         它「圖貼邊、文留白」的技巧，不借它的圓角。 */
    .page--wall .cx-pane-in {
        display: grid;
        grid-template-columns: minmax(0, 1fr);
        min-height: 0;
        overflow: hidden;
    }

    .page--wall .cx-pane-media {
        grid-row: 1;
        margin: 0;
        height: auto;
        aspect-ratio: 3 / 2;
        border-radius: 0;
    }

    /* ⚠ 2026-08-24 客戶「小卡內容沒靠左對齊」——實測卡片文字（01／標題／
       簡介／CTA）左邊界在 52px，但「學思」主題標題、說明文字、卡片圖片
       本身都在 31px，整整差了一個左內距（clamp(1.25rem,4vw,1.5rem)
       ≈20px）。這條左內距是桌機版「圖左文右」兩欄版型留下的殘留：桌機
       `.cx-pane-text` 明確把 padding-left 設成 0（本檔 ≈18010，圖在左欄
       文字緊接右欄不用另外留白），但手機版（本節，圖上文下）用三值
       padding 簡寫，左右內距寫成同一個值，等於又幫文字補回一個桌機版
       特地拿掉的左內距——圖片改成上下疊之後，文字左邊已經沒有相鄰圖片
       要留白，這條左內距失去原本的理由，只是視覺上把文字往右推、跟頁面
       其他內容對不齊。改成明寫四值，padding-left 比照桌機版同樣改回 0，
       右／上／下三邊留白不動。
       ⚠ 2026-08-25 客戶回頭要求「文字要靠右一點不要完全貼邊」——0
       內距跟卡片左緣完全貼齊，客戶看過實際結果後覺得太貼邊，要留一點
       呼吸空間，但不要回到改之前的完整內距（那樣又會跟標題對不齊）。
       改成原本內距的一半 clamp(.625rem,2vw,.75rem)，留一點空間但不
       完全貼邊，也不是照抄改之前的完整內距。 */
    .page--wall .cx-pane-text {
        grid-row: 2;
        padding: clamp(1rem, 4vw, 1.25rem) clamp(1.25rem, 4vw, 1.5rem) clamp(1.25rem, 4vw, 1.5rem) clamp(.625rem, 2vw, .75rem);
        overflow: visible;
    }

    .page--wall .cx-pane-desc { -webkit-line-clamp: 4; line-clamp: 4; }
    .page--wall .cx-pane-meta { margin-top: clamp(.875rem, 3vw, 1.125rem); }

    .page--wall .cx-theme-note { max-width: none; }

    /* 窄幅兩欄會把「6 COURSES」擠成兩行 → 收成單欄，數量排到介紹下方 */
    .page--wall .cx-theme-head {
        grid-template-columns: minmax(0, 1fr);
        gap: clamp(.625rem, 2vw, .875rem);
    }

    .page--wall .cx-theme-count { justify-self: start; }

    /* 上／下箭頭與直排短標題原本是「點擊展開／收合」的提示，現在整組手機版
       永遠展開、不會再被點到，留著只會誤導成「還能點一下收合」，所以隱藏；
       號碼（01/02/03）維持顯示，跟服務專區 .svc-tile-no 是同一種純標示。 */
    .page--wall .cx-pane-sign { display: none; }
    .page--wall .cx-pane-short { display: none; }
}

/* ───────────────────────────────────────────────────────────────────────
   45. 課程內頁：直向編輯式文章版（2026-08-13 客戶指定，三堂課共用）
   ───────────────────────────────────────────────────────────────────────
   客戶：「/wireframe/courses/strategy 這一頁把水平瀏覽方式改成垂直，
          課程排版請參考 willstyle.co.jp/projects/1858/ 的文字排版和
          列表圖片呈現重新設計，沒有的欄位不要新增。」——這是第一版，
          只改 strategy 一堂課；同一天陸續微調十幾輪之後，客戶追加
          「實體課程每個課程都換成這頁的版型」，course_detail.php 拿掉
          原本 if ($slug === 'strategy') 的分岔，這一節現在是三堂課
          （response／strategy／inquiry）共用的唯一版型，不再只服務一頁。

   ⚠ 只有課程內頁改：每一條選擇器都掛 .course-detail--editorial
     （course_detail.php 現在固定輸出這個 class，三堂課都一樣），
     課程總覽（courses_list.php，.page--wall）與其他頁面一個像素都
     不受影響。
   ⚠ 上一輪的橫向捲動主體（.ch-track／.ch-panel／wheel 鎖定手勢／底部
     進度列／那支切換 <script>）整段退場，這裡沒有任何一條 .ch-* 選擇器
     殘留 —— 垂直捲動是瀏覽器原生行為，不需要另外寫 JS。
   ⚠ 這一節新增的選擇器字首：.cd-hero(-grid/-actions)／.cd-facts／.cd-h2／
     .cd-media／.cd-article／.cd-tiles(-no)／.cd-sublabel／.cd-enroll-
     (band/split/text/cols/col/media)，其餘排版直接吃站上既有的
     .band-media（19-8 節）／.editor-block > .page-text（12 節 ＋
     .page--courses 專屬微調，29 節）／.entry-foot（11 節）／
     .info-table／.btn-pill／.tag-pill／
     .band-en／.rule-mark，一個新色票、新字體、新圓角都沒有加。
     ⚠ 客戶回饋「線條太多」之後，條列式內容（學習重點／課程流程）改用
       .cd-tiles（純文字＋編號，沒有格線），不是第 19-4 節的 .editorial-row
       ——那個元件本身沒有錯，只是這一頁的節奏不需要它。
   ⚠ .band-media／.editor-block > .page-text／.entry-foot 都已經掛在
     home.js 的捲動進場（.js-reveal）群組裡（.page--v2 .band-media／
     .page--v2 .editor-block > .page-text／.page--v2 .entry-foot），
     這一頁沿用同一組選擇器，直向捲動時會自動逐段淡入，不必另外寫
     進場動畫。
   ═══════════════════════════════════════════════════════════════════════ */

/* ═══════════════════════════════════════════════════════════════════════
   Hero：矩形分塊顯影 Banner（2026-08-14 客戶指定改版，同日第二輪修正：
   淺灰底／圖片頂邊貼齊／不規則矩形／文字圖片交疊）
   ═══════════════════════════════════════════════════════════════════════
   客戶參考圖：極簡建築工作室網站，大面積品牌色塊、左文右圖、右側主視覺
   透過不規則矩形遮罩逐步顯影——只換這一頁 Hero 的版面與進場動畫，字體／
   顏色／按鈕樣式／Container 寬度／間距系統一律沿用站上既有 token，
   沒有新增任何 Hex 色碼。

   ⚠ 舊版「麵包屑／英文小標＋H1（左欄）／報名中狀態（右欄）」＋另一條獨立
     滿版封面圖（.band-media.cd-media），版面構圖跟這次的參考圖完全不同
     （新版是「文字＋圖片同一屏」的單一構圖），下面 .cd-hero-grid／
     .cd-hero-titles／.cd-hero-status 這一套舊版型隨改版整段退場（同本檔
     「上一輪橫向捲動主體整段退場」的處理方式，不是隱藏，找不到這幾個
     舊 class 是正常的）；.cd-media 的樣式留在原位不動，因為 .cd-enroll-
     media（報名資訊區塊）另外也在用同一顆 .band-media/-media 語彙，
     那個區塊沒有改。
   ⚠ 2026-08-14 第二輪客戶「把黑色改成圖片這個灰色」——改用
     hsl(var(--surface-grey))（#DCDCE1，tokens.css 既有「淺灰色塊」
     token，courses/knowledge 列表頁卡片底本來就在用，不是新色票）。
     文字改回站上一般淺底文字的預設配色（--foreground／--heading／
     --slate／--muted-foreground），不用再像深底那樣整組反白——這樣
     改動範圍反而更小：.cd-hero-cta 原本疊的那層「深底反相」覆寫整組
     刪掉，直接吃 .btn-pill--solid 預設值就是對的（深色實心鈕＋淺色
     文字，本來就是這個元件的預設樣子，淺灰底上原本就會正確顯示）。
   ⚠ 2026-08-14 第九輪客戶「背景空一塊，文字跟圖片整個往上」——原本
     min-height: calc(100svh - var(--header-h)) 是刻意讓「Header ＋ Hero」
     疊滿第一屏，但這幾輪一路把文字字級收小、圖片長寬比也收窄之後，
     內容實際高度已經比可視高度矮一截，滿版高度的預留量在文字／圖片
     下方露出一大塊沒有東西的純灰空白（實測 1440 寬時內容下緣到色帶
     下緣還空了 130px 以上），肉眼看起來像背景「破了一個洞」。改成
     不設 min-height，色帶高度直接貼合內容（padding-block 交給下面
     .cd-hero-inner 的 clamp 內距決定），背景灰色的範圍跟著內容縮小，
     不再露白／留空——上緣「貼齊 Header 下緣」的行為不受影響（那是
     .wf-frame 的 padding-top 讓出來的，跟這裡的 min-height 無關，見
     本檔「0. 版面基礎」的 --header-h 說明）。
   ⚠ 手機（≤767.98px）原本就有 min-height:0 覆寫（見下面 767.98 的媒體
     查詢），這條規則收掉 min-height 之後手機那條覆寫變成多餘但無害，
     保留不動——避免手機另外牽動其他斷點的層疊順序。 */
.course-detail--editorial .cd-hero {
    position: relative;
    display: flex;
    overflow: hidden;
    padding-block: 0;
    background: hsl(var(--surface-grey));
    color: hsl(var(--foreground));
}

.course-detail--editorial .cd-hero > .section-bleed-inner {
    padding-inline: var(--section-x);
}

.course-detail--editorial .cd-hero-inner {
    display: flex;
    flex: 1;
    flex-direction: column;
    /* ⚠ 2026-08-14 客戶「圖片在上面不要留空間」——上緣呼吸內距（原本
       clamp(1.5rem, 3vw, 2.5rem)）會讓 .cd-hero-media（align-self:start
       貼齊這一列頂端）跟著往下推，圖片因此貼不到真正的頂端，即使外層
       .cd-hero 的讓位補償已經搬到 .cd-hero-text 身上（見該處）也一樣。
       上緣改成 0，圖片才會真的貼齊 .cd-hero 色帶頂端；下緣的呼吸內距
       維持不動（那一段是色帶跟下一個區塊之間的間距，跟圖片頂端無關，
       不能一起拿掉）。 */
    padding-block: 0 clamp(2rem, 4vw, 3rem);
}

/* 麵包屑目前站上是 display:none（第 19 節，四頁共用同一條規則），
   這裡不重新打開它——維持跟其他三頁一致的現況。 */

/* ⚠ 2026-08-14 第三輪客戶「文字位置跟圖片位置還有樣式請跟參考圖一樣」——
   參考圖裡圖片是「框在色塊中間、四周留白」的獨立模組（不是貼滿整欄
   高度），文字則貼在左下角、跟圖片下緣交疊，不是垂直置中。
   ⚠ align-items 改成 end：兩欄預設都貼齊這一列的下緣，文字欄本身
     縮到內容高度（不再撐滿），天然就會落在下方——不用另外寫
     position/top 去人工對位。
   ⚠ .cd-hero-media 用 align-self:start 覆寫回「貼上緣」，兩欄一上一下
     天然形成參考圖那種「圖片偏上、文字偏下」的錯位感，不用額外的
     margin 微調。 */
.course-detail--editorial .cd-hero-layout {
    flex: 1;
    display: grid;
    /* 第七輪「圖片區塊大一點」——文字欄從 40% 收到 34%，圖片欄跟著多分
       到一些寬度（圖片本身的可視面積另外靠 .cd-hero-media 的負
       margin-left 再加大一次，兩處一起貢獻，不是只調一邊）。 */
    grid-template-columns: minmax(0, 34%) minmax(0, 1fr);
    align-items: end;
    gap: clamp(2rem, 5vw, 4rem);
}

/* ⚠ 2026-08-14 客戶「左右切割太整齊，要有交疊感」——.cd-hero-text 疊在
   .cd-hero-media 之上（z-index），本身的內容仍然安分待在自己的 40% 欄
   （文字可讀性不受影響），用 ::before 這個裝飾用偽元素向右探出去，
   蓋住圖片左緣一小段，pointer-events:none 的純裝飾，不影響可點擊區域。
   ⚠ 第七輪「遮罩形狀改成上左下右錯位的 Z 形」之後，.cd-hero-media 自己
     的 clip-path 已經在左下角留了一塊空——原本這裡用 clip-path 切一段
     階梯去模擬交錯感、外加一顆 ::after 小矩形，兩個裝飾現在都不需要
     了（錯位本身變成圖片形狀的一部分，見 .cd-hero-media 那段說明），
     ::before 收回成單純的矩形、::after 整個拿掉，仍然保留最基本的
     「文字色塊向右探一段蓋住圖片左緣」，跟圖片自己的缺角接起來。 */
.course-detail--editorial .cd-hero-text {
    position: relative;
    z-index: 3;
    display: flex;
    flex-direction: column;
    /* ⚠ 2026-08-14 客戶「空一塊／header的空間會讓圖片上方無法貼邊／跟
       圖片在同一列」——讓開 Header 的內距原本加在 .cd-hero 這個外層
       色帶上（見本檔「內距補償」那段，已經改成該處歸零），圖片
       （.cd-hero-media）不需要這筆讓位、文字才需要（不然標題會被
       Header 蓋住），所以搬到這裡、只讓文字欄自己往下讓開，圖片繼續
       貼齊 .cd-hero-layout 這一列的頂端。
       ⚠ 數值跟原本「內距補償」那段 .cd-hero 拿掉的那條完全一樣
         （calc(var(--header-h) + 1px)），只是換了掛的元素，讓文字
         視覺位置維持不變，不是重新設計一個新的留白量。 */
    padding-top: calc(var(--header-h) + 1px);
    /* ⚠ 2026-08-14 第四輪「圖片整個左移、文字上移一點，讓圖片跟文字有
       交錯感」——.cd-hero-layout 是 align-items:end（貼下緣），這裡補一段
       margin-bottom 把文字欄從最下緣往上抬，不要整組貼死在下緣，跟同時
       左移的圖片形成更明顯的交錯，不是只在角落碰一下。
       ⚠ 第七輪「文字上移一點」再加大一階。 */
    margin-bottom: clamp(3rem, 8vw, 6rem);
    /* 文字輕微淡入＋上移（客戶「非常輕微的進場」），純 CSS 播放、
       不用等 JS——JS 沒載入時文字仍會準時淡入，圖片矩形維持完整品牌色
       蓋著，是安全的降級狀態。 */
    opacity: 0;
    transform: translateY(12px);
    animation: cdHeroTextIn 560ms var(--ease-out) 220ms forwards;
}

/* 2026-08-25：有麵包屑的頁面，.cd-hero-text 的讓位量也要多扣掉麵包屑
   在文件流裡佔用的總高度（--wf-crumb-total，定義與兩筆組成見「有麵包屑
   的頁面：拉頂量要多扣掉 .wf-crumb 自己的高度」那節，本檔 ≈20697）——
   .cd-hero 這個外層色帶的 padding-top 固定是 0（見該節），文字讓位量
   全部由這裡負責，道理跟上面 base rule 的註解「數值跟內距補償那段完全
   一樣」一致，只是多加麵包屑那一段。圖片（.cd-hero-media）不吃這筆，
   繼續貼齊視窗頂端，跟 base rule 同一個設計意圖。 */
body:has(.wf-crumb) .course-detail--editorial .cd-hero-text {
    padding-top: calc(var(--header-h) + var(--wf-crumb-total) + 1px);
}

@keyframes cdHeroTextIn {
    to { opacity: 1; transform: none; }
}

/* 2026-08-19 客戶「上面矩形圖片…上面的最左邊那塊高比其他兩塊少了一半」
   ——.cd-hero-media 的 6 塊格線（a~f）結構本身高度完全相等（實測 768～
   1920 每個寬度都一致），問題出在這個 ::before：原本 top:0（貼齊
   .cd-hero-text 自己的頂端）＋這個文字欄整段 z-index:3 疊在圖片之上
   （2026-08-14「要有交疊感」那輪客戶指定），文字欄的高度是內容撐出來的
   （標題長短各課程不同），「response」這堂課標題比較長，文字欄因此比較
   高，往上蓋住的範圍就吃進了上排最左邊那塊（cell a）——結構沒壞，是
   被蓋住了。
   ⚠ 客戶確認的修法：「限制疊色只蓋下半部」——交疊感保留，但只疊在
     下排（d／e／f 那一列），永遠不吃到上排三塊。
   ⚠ 為什麼不能直接寫死 50%：這個 ::before 是相對 .cd-hero-text 自己的
     高度在定位（.cd-hero-text 走內容高度，各課程標題長短不同），跟
     .cd-hero-media 的實際高度是兩個不同座標系統，純 CSS 沒有辦法用
     .cd-hero-text 自己的百分比去精確對到「圖片高度的一半」這個外部
     數值——跟 .cd-enroll-rule 那組水平線同一個限制（見 course_detail.php
     `cd-enroll-rule` IIFE 的說明），這裡沿用同一套「JS 量另一個元素、
     寫 CSS 自訂屬性」手法解決。
   ⚠ 第一版用 bottom:0 + height（假設 .cd-hero-text 的下緣＝圖片的下緣，
     理由是「.cd-hero-layout 是 align-items:end、.cd-hero-media 是
     align-self:start 且通常比文字高，兩者下緣應該對齊」）——實測抓
     .cd-hero-text／.cd-hero-media 的 getBoundingClientRect() 才發現
     這個假設是錯的：1440 寬時文字欄下緣（574.58）比圖片下緣
     （670.58）高了整整 96px，兩者根本沒有對齊（文字內距／行高造成
     的落差，不是穩定的巧合關係）。沿用 bottom:0 的結果是 ::before
     的上緣算出來比圖片真正的中線還高了 96px，等於還是蓋到上排，
     只是蓋的範圍比修之前少一點，沒有真的修好。
   ⚠ 改成 top + height 都用 JS 算出的絕對像素值，兩個端點都直接對齊
     .cd-hero-media 自己的座標（中線／下緣），不再假設跟 .cd-hero-text
     的下緣有任何對應關係——top 頂到圖片垂直中線，height 補到圖片
     下緣，這兩個點合起來精確等於「只蓋圖片下半部」，不管文字欄
     自己內容多高、下緣落在哪裡都不受影響。 */
.course-detail--editorial .cd-hero-text::before {
    content: "";
    position: absolute;
    left: 0;
    top: var(--cd-hero-overlay-top, 50%);
    height: var(--cd-hero-overlay-h, 50%);
    /* 負值＝往右探出自己欄位之外，探出量隨視窗縮放（clamp 上下限）。 */
    right: clamp(-140px, -8vw, -80px);
    background: hsl(var(--surface-grey));
    pointer-events: none;
    z-index: -1;
}

.course-detail--editorial .cd-hero-tag {
    display: flex;
    align-items: center;
    gap: .75rem;
    margin: 0 0 1.5rem;
    font-family: var(--font-eyebrow);
    font-size: var(--text-meta);
    line-height: var(--lh-meta);
    letter-spacing: var(--tracking-meta);
    color: hsl(var(--slate));
}

/* 報名中狀態：改回跟 .tag-pill.is-active（第 12 節，選中標籤／篩選條件
   既有的「實心反相」寫法）同一組配色——深色實心底＋淺色文字，淺灰 Hero
   底上直接可讀，不用像深底 Hero 那版另外調一顆半透明淺色底。 */
.course-detail--editorial .cd-hero-stat {
    display: inline-flex;
    align-items: center;
    padding: .25rem .85rem;
    border-radius: var(--radius-pill);
    background: hsl(var(--foreground));
    color: hsl(var(--background));
    font-size: var(--text-meta);
    letter-spacing: var(--tracking-meta);
}

.course-detail--editorial .cd-hero-title {
    margin: 0 0 1.25rem;
    /* 客戶「課程名稱不要大到佔滿整個畫面」——上限收在站上既有的
       --text-h1（48px），不是滿版大標題那個量級。 */
    font-size: clamp(1.75rem, 3.2vw, var(--text-h1));
    line-height: 1.3;
    font-weight: var(--fw-heading);
    letter-spacing: var(--tracking-display);
    color: hsl(var(--heading));
}

.course-detail--editorial .cd-hero-desc {
    margin: 0 0 1.75rem;
    max-width: 28em;
    font-size: var(--text-body);
    line-height: var(--lh-body);
    font-weight: var(--fw-light);
    color: hsl(var(--muted-foreground));
}

/* 堂數／上課形式／上課地區：吃站上既有的 .cd-facts（「參考頁 Type /
   Industry / Area」純文字條列＋全形間隔號分隔，第 45 節既有元件，這一頁
   第一次真正輸出使用）。淺灰 Hero 底上 .cd-facts 原本的
   color:hsl(var(--slate)) 已經是對的，這裡只疊間距覆寫，不重複宣告
   顏色。 */
.course-detail--editorial .cd-hero-facts {
    margin: 0 0 2rem;
}

.course-detail--editorial .cd-hero-actions {
    margin-top: clamp(1.75rem, 4vh, 2.5rem);
}

/* CTA 沿用全站唯一的 .btn-pill／.btn-pill--solid，這一輪改成淺灰底之後
   完全不用覆寫顏色——.btn-pill--solid 預設就是「深色實心底＋淺色文字」
   （第 24 節既有定義），淺灰 Hero 底上直接套用即可，.cd-hero-cta 這顆
   class 留著只是保留掛勾，沒有專屬樣式。 */

/* ── 右欄主視覺：完整一張圖，矩形遮罩疊在上面 ───────────────────────── */
/* ⚠ 2026-08-14 第三輪「圖片位置跟樣式請跟參考圖一樣」——參考圖的圖片是
   四周留白、貼上緣的獨立模組，不是撐滿整欄高度貼齊上下緣。改用
   align-self:start（覆寫 .cd-hero-layout 的 align-items:end）貼齊這一列
   的上緣，並用 aspect-ratio 給自己一個「模組感」的固定高度，不再是
   height:100% 撐滿——欄位剩下的高度自然露出 .cd-hero 的品牌色背景，
   文字（貼下緣）跟圖片下緣自然形成一段交疊，跟參考圖同一個節奏。
   ⚠ margin 不再用負值貼齊 Header——那是上一輪「貼邊」需求的做法，這一輪
     圖片本來就不該貼到真正的頂緣，所以收回。
   ⚠ 2026-08-14 第四輪「圖片整個左移，讓圖片跟文字有交錯感」——用負的
     margin-left 把整塊圖片模組往左拉，吃進原本兩欄之間的 gap 與文字欄
     的右側空間，跟同時上移的 .cd-hero-text 交錯得更明顯，不是只在
     左下角碰一個小角。往左拉不會壓縮文字欄本身的寬度——grid 的欄寬
     分配不變，只是圖片這個 grid item 用 margin 把自己的視覺範圍往左
     推出自己的欄位。 */
/* ⚠ 2026-08-14 第七輪「遮罩形狀以上下三個方形組成，上三塊靠左排列、
   下三塊靠右排列，錯位的區塊文字疊加」——外框改成 4 欄 × 2 列的網格，
   上排三塊方形佔第 1～3 欄（靠左）、下排三塊方形佔第 2～4 欄（靠右），
   第 1 列第 4 欄與第 2 列第 1 欄兩個角落不畫（clip-path 切掉），
   疊起來就是一個左上右下錯位的「Z」形輪廓，跟參考圖圖片模組本身的
   形狀一致。
   ⚠ 第 2 列第 1 欄（左下角）刻意留空、被 clip-path 切掉——這裡的空間
     讓左欄的 .cd-hero-text（尤其是往下移動後的文字區）自然疊進來，
     不用再另外靠 ::before 的 clip-path 階梯去模擬交錯，錯位本身就是
     圖片形狀的一部分。 */
.course-detail--editorial .cd-hero-media {
    position: relative;
    z-index: 1;
    align-self: start;
    /* ⚠ 2026-08-14 第八輪「圖片的高再大點」——aspect-ratio 從 2:1 收到
       1.5:1（寬不變、高多出約 33%）。4 欄 × 2 列時原本 2:1 剛好讓每格
       是正方形（寬 ÷4 ＝高 ÷2），這裡刻意不再維持正方形，六塊遮罩變成
       略高的長方形，換取整塊圖片模組更高、更有存在感。 */
    aspect-ratio: 1.5 / 1;
    /* 第七輪「圖片區塊大一點」——負值加大，box 本身往左多撐出一截
       （搭配上面 .cd-hero-layout 欄寬從 40% 收到 34%，兩處一起讓圖片
       實際可視面積變大，不是只放大裁切）。 */
    margin: 0 0 0 clamp(-260px, -15vw, -170px);
    clip-path: polygon(0 0, 75% 0, 75% 50%, 100% 50%, 100% 100%, 25% 100%, 25% 50%, 0 50%);
    overflow: hidden;
}

.course-detail--editorial .cd-hero-media picture,
.course-detail--editorial .cd-hero-media img {
    position: absolute;
    inset: 0;
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    /* 左緣有一段會被 .cd-hero-text 的重疊色塊蓋住（見上面），主體往右
       偏一點，確保人物臉部/主要動作不會被蓋到（客戶明確要求）。
       ⚠ 偏移量刻意不大——這幾張課程插畫是橫幅比例（2336×992），窄螢幕
         的圖片欄本身已經很窄高，object-fit:cover 裁切幅度已經很大，
         偏移量一大會把左邊那位人物整個裁出畫面（實測 900 寬時發生
         過），56% 是「躲開左緣重疊、又不把左邊人物擠出框」的折衷值。 */
    object-position: 56% 50%;
}

/* ⚠ 2026-08-14 客戶「遮罩的形狀請保留線條在圖片上」——遮罩本身縮到 0
   之後會完全消失（那是顯影動畫要的效果），沒辦法讓「同一顆元素」既縮到
   0 又留下一條邊框。改成疊一層獨立、不參與動畫、永遠存在的細線層
   （.cd-hero-lines），跟 .cd-hero-mask-grid 用同一份 grid-template-areas
   （兩層必須同步改，改一邊記得改另一邊），只是每一塊不是實色矩形、是
   透明矩形＋1px 邊框。疊放順序：圖片在最底層→ 這層線條 → 遮罩在最上層；
   遮罩完整蓋著的時候線條當然也被蓋住看不到，遮罩縮回後線條跟著圖片一起
   露出來，變成「圖片被畫成分塊模組」的永久視覺效果，不是動畫殘影。 */
/* ⚠ 2026-08-14 第七輪「遮罩形狀以上下三個方形組成，上三塊靠左、下三塊
   靠右」——4 欄 × 2 列，上排三塊佔第 1～3 欄、下排三塊佔第 2～4 欄，
   第 1 列第 4 欄與第 2 列第 1 欄不放任何一塊（那兩格會被 .cd-hero-media
   的 clip-path 切掉，本來就不會露出東西，這裡也不用畫線）。 */
.course-detail--editorial .cd-hero-lines {
    position: absolute;
    inset: 0;
    z-index: 1;
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    grid-template-rows: repeat(2, 1fr);
    grid-template-areas:
        "a b c ."
        ". d e f";
    pointer-events: none;
}

.course-detail--editorial .cd-hero-line {
    border: 1px solid hsl(var(--surface-grey) / .85);
}

.course-detail--editorial .cd-hero-line--a { grid-area: a; }
.course-detail--editorial .cd-hero-line--b { grid-area: b; }
.course-detail--editorial .cd-hero-line--c { grid-area: c; }
.course-detail--editorial .cd-hero-line--d { grid-area: d; }
.course-detail--editorial .cd-hero-line--e { grid-area: e; }
.course-detail--editorial .cd-hero-line--f { grid-area: f; }

/* 遮罩層：絕對定位疊滿整個圖片區，本身不接受任何互動——客戶指定
   「遮罩只是裝飾，不應被螢幕閱讀器讀取／不能攔截點擊」。
   ⚠ 2026-08-14 第七輪客戶「遮罩形狀以上下三個方形組成，上三塊靠左、
     下三塊靠右」——改成 4 欄 × 2 列，同一份 grid-template-areas 跟上面
     .cd-hero-lines（兩層要同步改），a／b／c 是上排三塊（第 1～3 欄）、
     d／e／f 是下排三塊（第 2～4 欄），第 1 列第 4 欄與第 2 列第 1 欄
     不放遮罩——那兩格本來就被 .cd-hero-media 的 clip-path 切掉，不會
     露出圖片，遮罩放了也是白放。
   ⚠ gap 刻意用 0，不是留 1px 再把顏色調成跟遮罩一樣——gap 是網格線之間
     真正的「空隙」，會直接透出容器底下的 <img>（實測顯影開始前的完整
     遮罩狀態，縫隙處會出現一條會透光的細線）。試過在容器本身補一層
     同色背景蓋住縫隙，結果變成容器自己永遠蓋著一層黑、遮罩縮回後圖片
     還是被這層背景擋住看不見（實測踩過）。改成 gap:0，矩形彼此緊貼、
     沒有縫隙可以透光，客戶要的「Grid 線顏色與背景相同（＝看不見）」
     直接用零間距達成，不需要額外一層背景。 */
.course-detail--editorial .cd-hero-mask-grid {
    position: absolute;
    inset: 0;
    z-index: 2;
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    grid-template-rows: repeat(2, 1fr);
    grid-template-areas:
        "a b c ."
        ". d e f";
    gap: 0;
    pointer-events: none;
}

/* 2026-08-19 客戶回報遮罩動畫過程中格與格交界處會閃過一絲顏色——同一個
   次像素縫隙問題，詳細成因與修法見 .hero-mask 那條同款註解（本檔開頭
   ≈621 行）。 */
.course-detail--editorial .cd-hero-mask {
    background: hsl(var(--surface-grey));
    margin: -1px;
    transition: transform 560ms cubic-bezier(0.76, 0, 0.24, 1);
    will-change: transform;
}

/* 六塊都是等大正方形（見 .cd-hero-media 的 aspect-ratio 說明），縮回
   方向交替用 scaleX／scaleY，靠左右／上下分組製造變化，不是隨手排。 */
.course-detail--editorial .cd-hero-mask--a { grid-area: a; transform-origin: left;   transform: scaleX(1); }
.course-detail--editorial .cd-hero-mask--b { grid-area: b; transform-origin: top;    transform: scaleY(1); }
.course-detail--editorial .cd-hero-mask--c { grid-area: c; transform-origin: right;  transform: scaleX(1); }
.course-detail--editorial .cd-hero-mask--d { grid-area: d; transform-origin: left;   transform: scaleX(1); }
.course-detail--editorial .cd-hero-mask--e { grid-area: e; transform-origin: bottom; transform: scaleY(1); }
.course-detail--editorial .cd-hero-mask--f { grid-area: f; transform-origin: right;  transform: scaleX(1); }

/* 播放後的目標狀態：scaleX(0)／scaleY(0)，跟上面靜止狀態同一個
   transform 函式（只換數值），瀏覽器才能穩定內插，不會因為函式種類
   不同（例如 scaleX(1) → scale(0)）在不同瀏覽器出現分解方式不一致的
   過場結果。data-order 決定的先後順序由 JS 寫 transition-delay 控制
   （見本頁 course_detail.php 底部 <script>），CSS 只負責每一塊各自的
   起訖狀態。 */
.course-detail--editorial .cd-hero.is-cd-hero-revealed .cd-hero-mask--a,
.course-detail--editorial .cd-hero.is-cd-hero-revealed .cd-hero-mask--c,
.course-detail--editorial .cd-hero.is-cd-hero-revealed .cd-hero-mask--d,
.course-detail--editorial .cd-hero.is-cd-hero-revealed .cd-hero-mask--f {
    transform: scaleX(0);
}

.course-detail--editorial .cd-hero.is-cd-hero-revealed .cd-hero-mask--b,
.course-detail--editorial .cd-hero.is-cd-hero-revealed .cd-hero-mask--e {
    transform: scaleY(0);
}

/* 平板：文字圖片交疊量收窄（40–80px），矩形結構維持同一組「上三塊靠左
   ／下三塊靠右」的 6 格 Z 形——這個構圖本身就是設計過的比例關係，不像
   之前的不規則版本需要在窄一點的視窗裡再拆併，縮小到平板寬度依然
   成立。 */
@media (max-width: 1023.98px) and (min-width: 768px) {
    .course-detail--editorial .cd-hero-text::before {
        right: clamp(-80px, -6vw, -40px);
    }

    .course-detail--editorial .cd-hero-media {
        margin-left: clamp(-160px, -11vw, -100px);
    }
}

@media (max-width: 1023.98px) {
    /* ⚠ 2026-08-19 客戶指定「最上面區塊改成圖片在上文字在下」——推翻
       原本「文字在上、圖片在下」的堆疊順序。DOM 沒有動（維持「只新增
       不改寫」，且 .cd-hero-text 在前、.cd-hero-media 在後的順序桌機／
       平板版也在用，改 DOM 順序會連帶影響那兩種版型），改用 flex
       的 order 純視覺翻轉：.cd-hero-media 給 -1（比預設值 0 小，排到
       最前面），.cd-hero-text 維持預設 order:0，自然排到後面。 */
    .course-detail--editorial .cd-hero {
        min-height: 0;
    }

    .course-detail--editorial .cd-hero-layout {
        display: flex;
        flex-direction: column;
        /* 交疊靠文字欄自己的負 margin-top（見下面），這裡的 gap 只
           是文字沒有上探時的預設間距，兩者相減才是真正露出來的間距。 */
        gap: 0;
    }

    .course-detail--editorial .cd-hero-media {
        order: -1;
    }

    /* 桌機／平板用左右交疊，手機原本改上下交疊：「文字在下、色塊向上蓋住
       圖片底緣」（自己的 padding 先讓出空間，再用等量偏少的負 margin
       咬回去，跟桌機同一招，只是從 padding-bottom/margin-bottom 換成
       padding-top/margin-top）。
       ⚠ 2026-08-19 客戶「圖片底部離文字遠一點」——推翻上面「圖片文字
       交疊」的舊指定，改成圖片跟文字之間要留出實際看得到的空白，不是
       貼在一起或疊在一起。margin-top 從負值（往上咬進圖片）改成正值
       （往下推開一段距離），圖片模組（.cd-hero-media）本身的下緣不動，
       純粹是文字色塊往下讓開，露出一段頁面底色的空隙。 */
    .course-detail--editorial .cd-hero-text {
        justify-content: flex-start;
        z-index: 3;
        background: hsl(var(--surface-grey));
        padding-top: clamp(2rem, 8vw, 3rem);
        margin-top: clamp(1.5rem, 4vw, 2.5rem);
    }

    /* 左右交疊那兩個偽元素在單欄版型裡沒有意義，關掉。 */
    .course-detail--editorial .cd-hero-text::before,
    .course-detail--editorial .cd-hero-text::after {
        content: none;
    }

    /* ⚠ 2026-08-14 第四／五輪加的 margin-left 負值與 clip-path 階梯形，
       是桌機／平板「圖片左移＋跟文字錯位」專用的效果，建立在「圖片跟
       文字左右並排」的前提上。手機是單欄上下堆疊，同一顆負 margin-left
       套用在滿寬的欄位上會把圖片整個推出 .cd-hero 的左邊界，被外層的
       overflow:hidden 裁掉、整張圖看起來憑空消失（實測踩過）——這裡都
       收回成 0／none，手機維持原本「四周留白、上下堆疊」的單純矩形。
     ⚠ align-self:start 同一顆屬性在 Grid 跟 Flex 的意義不一樣：桌機
       .cd-hero-layout 是 Grid，align-self 管的是「列」方向（垂直），
       用來把圖片貼齊那一列的上緣；手機 .cd-hero-layout 改成
       flex-direction:column，align-self 在 Flex 裡永遠管「交叉軸」，
       column 容器的交叉軸是水平——同一顆 start 值到了手機變成「水平方向
       不要撐滿、縮成內容自身寬度」。.cd-hero-media 的子元素都是
       position:absolute（不貢獻自身尺寸），縮成內容寬度＝縮成 0，
       aspect-ratio 拿 0 寬算出 0 高，整張圖跟著沒有任何面積（實測踩過，
       跟上面 margin-left 那個坑是同一次改動、不同機制造成的另一個症狀）。
       改回 stretch，圖片才會撐滿手機這一欄的寬度。 */
    .course-detail--editorial .cd-hero-media {
        align-self: stretch;
        /* ⚠ 2026-08-19 客戶回報「上排矩形沒有推到最左，下排的矩形也沒有
           推到最右」（後續補充「推到最右」）——上一輪只把六塊矩形相對
           .cd-hero-media 自己的欄位做出「上排佔第1~2欄、下排佔第2~3欄」
           的交錯，數字上確實是「貼齊 .cd-hero-media 自己的左右緣」，但
           .cd-hero-media 這個欄位本身還內縮在 .cd-hero > .section-bleed-
           inner 的 padding-inline:var(--section-x) 裡面，跟真正的視窗
           邊緣還隔著一段——客戶要的「貼邊」是貼到視窗邊緣，不是貼到這個
           欄位自己的邊緣，兩者只有在欄位本身也出血到視窗邊緣時才會一致。
           改成跟本站其他「手機版滿版出血」元件同一招：margin-inline 補
           負值抵銷掉 --section-x，圖片模組（連同疊在上面的六塊遮罩／
           線條，都是 inset:0 相對這個元素）整個出血到視窗兩側，上排
           左緣、下排右緣才會真的碰到螢幕邊界。
           ⚠ .cd-hero 本身 overflow:hidden 且是滿版色帶（.section-bleed
           出血到 100vw），出血量剛好是 --section-x，不會超出 .cd-hero
           自己的邊界被裁掉——跟上面「同一顆負 margin-left 套用在滿寬欄位
           會把圖片整個推出去」是不同量級，那次踩到的是桌機版更大的位移量
           （clamp(-260px,-15vw,-170px) 那個量級），不是這裡的 --section-x。 */
        margin-inline: calc(-1 * var(--section-x));
        clip-path: none;
        /* 第八輪把桌機版的 aspect-ratio 從 2:1 收到 1.5:1（圖片模組加高）
           是桌機「上三塊靠左／下三塊靠右」那個 4 欄×2 列構圖專用的比例，
           手機原本維持單獨定的 4:3。
           ⚠ 2026-08-19 客戶回報「矩形小一點、高一點，現在太寬了錯位看
           不出來因為填滿頁面寬了」——上一輪把整個圖片模組出血到視窗
           邊緣後，寬度從「欄位寬（扣掉兩側 --section-x）」變成「滿版
           視窗寬」，模組整體變寬了，但格線還是 2 列，每一塊矩形的高度
           沒有跟著長高，寬高比例因此變得更扁、更寬，跟桌機那種比較
           方正的矩形不像。改成 1:1（正方形模組），寬不變、高直接增加
           （寬÷1 對比原本寬÷(4/3)，高多出約 33%），兩列分完之後每塊
           矩形的高寬比自然跟著拉直，不用額外調每一塊自己的比例。 */
        aspect-ratio: 1 / 1;
    }

    /* 手機／平板：圖片下移到 header 底線（2026-08-26 客戶「手機跟平板
       版，圖片下移到線的位置」）───────────────────────────────────────
       這個斷點 .cd-hero-layout 是 flex-direction:column（見本節最上面
       說明），.cd-hero-media 用 order:-1 排到最前面——它自己沒有另外
       的 margin-top，直接吃 .cd-hero 這個外層色帶的位置：色帶本身為了
       「延伸到視窗頂端、讓 header 蓋上去」整區被拉到 -1（視窗頂端），
       .cd-hero-media 因此也從 -1 開始畫，等於整張圖穿過 header 高度、
       一路延伸到麵包屑那一列底下——桌機的等價設計是「延伸穿透 header」
       （見本檔「上排圖片延伸填滿 header 讓位空間」那節），但手機/平板
       這裡沒有並排的其他格子可以借位對齊（不像 courses 列表頁的 photoB
       旁邊還有 logo 格），圖片就直接整塊蓋住麵包屑文字，看起來像跑版
       （客戶截圖：麵包屑疊在插畫上難以閱讀）。
       ⚠ 直接補 margin-top，把圖片從視窗頂端往下推到 header 底線（跟
         header 的透明 bleed 效果分離——圖片不再借用那段透明空間，麵包屑
         落在乾淨底色上）。這是 flex column 版面，補這段 margin 只會把
         圖片與它後面的文字一起順順往下推，不會像桌機 Grid 那樣牽動列高
         或跟其他格子對不齊的問題。
       ⚠ 讀 --header-real-h（隨寬度變化的真實高度：56／63／73px），不是
         --header-h（設計基準值，992–1467 一帶會跟真實高度差 7px）——
         用錯的話 1024 寬會差 7px，圖片頂緣會露出一截還沒到 header 底線。 */
    .course-detail--editorial .cd-hero-media {
        margin-top: calc(var(--header-real-h) + 1px);
    }

    /* 手機圖片改回單純矩形（見上面 .cd-hero-media 的說明，clip-path 已經
       關掉），遮罩跟著簡化成 2 欄 × 2 列共 4 塊正方形，避免大量狹窄
       直條，桌機／平板專用的「上三塊靠左、下三塊靠右」錯位排法在單欄
       堆疊版型裡沒有意義，這裡不沿用同一份 6 塊的 grid-template-areas。
       ⚠ 2026-08-19 客戶回報「圖片現在是兩塊矩形同位置，讓他上面兩塊
       往左移貼邊，下面兩塊往右移貼邊」——上面這段話說「單欄堆疊版型
       裡沒有意義」是判斷錯誤，客戶要的正是同一種「上排靠左、下排靠右」
       錯位效果，只是從桌機的 3 塊一排改成手機的 2 塊一排。原本
       `repeat(2,1fr)` 兩欄剛好被上下兩排（a c／d f）各自填滿、兩排
       天生一樣寬、左右都貼齊，才會看起來像「同位置」沒有錯位。改成
       3 欄：上排 a／c 只佔第 1～2 欄（左移貼邊，第 3 欄留白）、下排
       d／f 只佔第 2～3 欄（右移貼邊，第 1 欄留白）——跟桌機版
       「4 欄，上排佔 1～3、下排佔 2～4」同一個手法，只是欄數與塊數
       都少一階，比例跟著換算（桌機 3/4 寬一排，這裡 2/3 寬一排）。 */
    .course-detail--editorial .cd-hero-mask-grid,
    .course-detail--editorial .cd-hero-lines {
        /* ⚠ 2026-08-19 客戶追加「矩形小一點，高一點，太寬了錯位看不出來」
           ——原本 3 欄／每排 2 塊佔 2 欄／錯位 1 欄（佔比 2/3），改成滿版
           出血後模組整體變寬，同樣比例下每塊矩形的絕對寬度也跟著變大，
           錯位那 1 欄的視覺存在感被稀釋。改成 5 欄，每排 2 塊維持相鄰
           （寬度縮小到約原本的 3/5），錯位量從 1 欄拉大到 3 欄（上排
           1~2 欄、下排 4~5 欄，中間第 3 欄兩排都不放，天生就是共用的
           留白帶）——矩形本身變窄（尺寸變小），錯位量從佔一排寬度的
           1/3 拉大到 3/5，兩個方向都直接對到客戶的訴求，不用另外微調
           gap 或 padding。高度靠上面 .cd-hero-media 的 aspect-ratio
           改 1:1 撐出來，這裡的 grid-template-rows 不用跟著動。
           ⚠ 2026-08-19 客戶追加「矩形寬度調寬一點讓上面第二塊跟下面第一塊
           重疊到」——上一輪中間第 3 欄是兩排都不放的純留白帶，兩排完全
           不相鄰；客戶要的是跟桌機版同一種「中段共用欄位、兩排交疊」的
           Z 字形（桌機 4 欄，上排佔 1~3、下排佔 2~4，中間 2~3 欄兩排都有
           內容）。改成 10 欄、每塊矩形佔 3 欄（比 5 欄版的 1 欄寬，維持
           「矩形變寬」的字面指示）：上排 a／c 佔 1~6 欄（a:1~3、c:4~6）、
           下排 d／f 佔 5~10 欄（d:5~7、f:8~10）——第 5～6 欄兩排重疊，
           上排的 c（4~6欄）跟下排的 d（5~7欄）因此在第 5~6 欄疊在一起，
           對應客戶說的「上面第二塊（c）跟下面第一塊（d）重疊」。
           ⚠ 2026-08-19 客戶再追加「再寬一點」——上一輪 10 欄／每塊 3 欄
           （總寬 60%、重疊 20%）還要再拉大。改回 5 欄制（跟本節最早的
           版本同一個欄數，數學比 10 欄乾淨），但這次每塊矩形佔 2 欄
           （不是最初版的 1 欄）：上排 a／c 佔 1~4 欄（a:1~2、c:3~4）、
           下排 d／f 佔 2~5 欄（d:2~3、f:4~5）——總寬從 60% 拉到 80%
           （4/5 欄），重疊量從 20% 拉到 60%（第 2~4 欄，3/5 欄）。
           ⚠ 這份 grid-template-areas 要跟下面 .cd-hero-media img 的
           mask-image 保持同一組比例（80% 寬、重疊 60%），兩處是同一個
           錯位效果的兩層（一層決定裝飾格線／遮罩的位置，一層決定照片
           本身裁切的位置），改其中一邊記得同步改另一邊，不然照片的
           可視範圍會跟外框裝飾對不齊。 */
        grid-template-columns: repeat(5, 1fr);
        grid-template-rows: repeat(2, 1fr);
        grid-template-areas:
            "a a c c ."
            ". d d f f";
    }

    .course-detail--editorial .cd-hero-mask--b,
    .course-detail--editorial .cd-hero-mask--e,
    .course-detail--editorial .cd-hero-line--b,
    .course-detail--editorial .cd-hero-line--e {
        display: none;
    }

    /* 2026-08-19 客戶「矩形改圖片要跟著切塊移動…是圖片拆成上下兩塊矩形組成
       然後錯位」——.cd-hero-media 底下只有一張鋪滿整個 5 欄×2 列網格（含
       所有留白格）的 <img>（object-fit:cover 撐滿整個 figure），a／c／d／f
       四塊遮罩／格線只是疊在這張完整照片「上面」的裝飾，遮罩退場後露出的
       仍是同一張連續照片——第 3 欄與左下/右上兩個對角那六格「看起來沒有
       真的空」，因為底下的照片照樣鋪到那裡，只是沒有格線／遮罩經過，肉眼
       幾乎看不出錯位，這是客戶回報的根因。
       ⚠ 不新增 <img>、不複製圖檔：用 mask-image 直接在既有這顆 <img> 上
         「再裁一次」，只露出 a+c（左上，兩欄寬×一列高）與 d+f（右下，
         兩欄寬×一列高）兩塊窗口，第 3 欄與左下/右上兩個對角六格改成透明，
         露出 .cd-hero 的灰底——跟 .cd-hero-mask-grid／.cd-hero-lines
         同一份 repeat(5,1fr)/repeat(2,1fr) 網格算出來的範圍，三處要同步改。
       ⚠ 用兩層獨立 mask-image（各一塊矩形視窗）、不用 clip-path：桌機版
         那個 Z 形缺角能用一條 polygon 描完，是因為兩塊矩形在欄位中段
         「重疊」（cols1-3 與 cols2-4 有共同欄位），一條路徑繞一圈天生
         連續；手機版 a+c（第1-2欄）跟 d+f（第4-5欄）中間隔著完全不重疊的
         第 3 欄，polygon 只能靠「兩點重合、來回同一條線」的退化橋接寫法
         硬湊出兩塊不相連的矩形，等於刻意做出一條寬度為 0 的細縫——跟
         .cd-hero-mask-grid 的 gap:0 那段、首頁 .hero-mask 同款註解踩過
         的次像素縫隙是同一種風險，不想再犯一次。mask-image 兩層各自
         獨立、互不相鄰，沒有這個問題。
       ⚠ 不用「四宮格各自一顆 background-image」的原因：background-size
         的百分比不會保留圖片原始比例（跟 object-fit:cover 不一樣）。
         courses 這三張照片實測都是 2336×992（橫幅比例，非方形），要讓
         a／c、d／f 各自拼起來無縫、又不失真，得先反推「整個方形模組
         cover 鋪滿後再切 5×2」的實際倍數（約 background-size:1177%
         200%，position 落在 35%／44%／63%／72% 這種醜數字，還是綁死
         這三張圖片目前這組像素尺寸算出來的，換一張比例不同的照片就要
         重算）。直接在既有 <img> 上疊 mask-image 完全不用管照片原始
         尺寸：object-fit:cover／object-position:56% 50% 已經算好方形
         模組要顯示照片的哪一段，遮罩只是在「已經算好的結果」上再裁一次
         窗口，不管照片比例怎麼換都不用重算，也保證 a／c 之間、d／f 之間
         永遠無縫（同一顆元素、同一份計算結果，不是兩張各自對位的圖層）。
       ⚠ 沒有動 <picture>／<img>／alt／JS：hero.querySelector(
         '.cd-hero-media img') 抓到的還是同一顆元素，img.decode()／
         .complete 判斷式（本頁底部 <script>）完全不受影響；alt 文字也
         還在這顆看得到的 <img> 上，不必再藏一顆「真圖」、另外補一顆
         裝飾用的假圖去騙讀屏器。
       ⚠ 疊放順序不變：.cd-hero-lines（z-index:1）／.cd-hero-mask-grid
         （z-index:2）都還在這顆 <img> 之上，遮罩退場動畫（scaleX／
         scaleY(0)）原封不動——這裡只是換了「被遮罩蓋住、被遮罩露出」的
         那張照片本身的形狀，不是換動畫機制。
       ⚠ -webkit- 前綴放前面、無前綴放後面（跟本檔 -webkit-backdrop-
         filter／backdrop-filter 那組同一個順序慣例）——本檔第一次用到
         mask-image，沒有既有前例，補前綴是為了涵蓋還沒吃到無前綴
         mask-image 的舊版 Safari／Android WebView。 */
    .course-detail--editorial .cd-hero-media img {
        -webkit-mask-image:
            linear-gradient(#000, #000),
            linear-gradient(#000, #000);
        -webkit-mask-repeat: no-repeat, no-repeat;
        -webkit-mask-size: 80% 50%, 80% 50%;
        -webkit-mask-position: 0% 0%, 100% 100%;
        mask-image:
            linear-gradient(#000, #000),
            linear-gradient(#000, #000);
        mask-repeat: no-repeat, no-repeat;
        /* ⚠ 2026-08-19 客戶再追加「再寬一點」——60%（上一輪）再拉大到
           80%，跟上面 .cd-hero-mask-grid／.cd-hero-lines 改回的 5 欄／
           每塊 2 欄同步：80% 寬、位置仍是左上 0%／右下 100%，算出來
           左層落在 0~80%、右層落在 20~100%（offset=(100%−80%)×100%
           =20%），兩層中間 20~80% 重疊（60% 寬），跟外層裝飾格線的
           c／d 重疊範圍（第 2~4 欄＝20~80%）完全對齊。 */
        mask-size: 80% 50%, 80% 50%;
        /* 第一層貼左上角（0% 0%）＝a+c；第二層貼右下角（100% 100%）＝
           d+f；沒有另外寫 mask-composite——無前綴預設值（add）與
           -webkit- 預設值（source-over）都是「聯集」，兩層重疊的地方
           直接疊加顯示同一張照片、不會變色或半透明。 */
        mask-position: 0% 0%, 100% 100%;
    }
}

/* 使用者關閉動態效果：直接顯示完整圖片與文字，不播放矩形顯影／文字
   位移淡入——跟全站其餘進場動畫（.js-reveal 等）同一個原則。
   ⚠ 這裡蓋兩種情況：JS 判斉到 prefers-reduced-motion 主動加上的
     .is-cd-hero-static，以及使用者中途才切換系統設定（媒體查詢本身
     即時生效，不需要重新整理頁面）。 */
@media (prefers-reduced-motion: reduce) {
    .course-detail--editorial .cd-hero-text {
        animation: none;
        opacity: 1;
        transform: none;
    }

    .course-detail--editorial .cd-hero-mask {
        display: none;
    }
}

.course-detail--editorial .cd-hero.is-cd-hero-static .cd-hero-mask {
    display: none;
}

/* ⚠ 2026-08-14 第十輪客戶「該頁面背景都是這個灰，不要只有最上面圖片
   那區塊是灰色，內文的背景也要是同個灰」——原本只有 .cd-hero 是
   --surface-grey，中間五段 .cd-article 內文色帶跟最下面「回實體課程／
   收藏／分享」動作列都吃頁面預設白底，整頁看起來灰白交錯，不是一致的
   灰。這裡讓其餘的 .band 色帶補上同一顆 --surface-grey（跟 Hero 同一顆
   token，不是新色票）。
   ⚠ :not(.cd-hero) 只是避免重複宣告（Hero 自己那條已經有背景，這裡
     排除不影響結果）；:not(.cd-enroll-band) 是刻意排除——那個色帶
     2026-08-13 客戶已經明確指定改用更淺的 --background（見下面
     .cd-enroll-band 那段說明），不是這次要動的範圍，:not() 直接排除
     讓這條新規則完全不命中它，不用跟那條規則比特異度。 */
.course-detail--editorial .site-section.section-bleed.band:not(.cd-hero):not(.cd-enroll-band) {
    background: hsl(var(--surface-grey));
}

/* ── 滿版圖：吃站上既有的 .band-media（19-8 節），但取消固定高度裁切——
   客戶指定「圖片不要裁切到人物的臉」。站上原本的 clamp(220px,30vw,420px)
   + object-fit:cover 是給滿版寬幅照片用的裁切规则，這一頁的素材是插畫
   （2336×992／2336×1109 等比例），裁到固定高度會切到人物的頭。改用原圖
   比例（height:auto），不管換哪一張圖都不會裁切。 */
.course-detail--editorial .cd-media picture,
.course-detail--editorial .cd-media img {
    height: auto;
    object-fit: unset;
}

/* ── 內文標題：大標題 + 標題下緣一條細線（參考頁的長文章節奏）───────────
   ⚠ 這一頁唯一「新樣式」：線是既有 --foreground 透明階的 1px，位置在
     標題下緣、貼著 padding-bottom，不是另外畫一條 <hr>。 */
.course-detail--editorial .cd-h2 {
    margin: 0 0 clamp(1.75rem, 4vh, 2.5rem);
    padding-bottom: clamp(.875rem, 2vh, 1.25rem);
    border-bottom: 1px solid hsl(var(--foreground) / .16);
    font-size: var(--text-h2);
    line-height: 1.5;
    font-weight: var(--fw-heading);
    letter-spacing: var(--tracking-title);
    color: hsl(var(--heading));
}

/* ── 窄欄讀本體（內文段落）：左右縮排置中，跟 Hero／事實列那種「跟
   Logo 對齊的寬版」是兩種不同角色——Hero 是滿版的頁首（跟其餘 v2 頁面
   同一套），這裡是長文章的閱讀欄，客戶指定「內文保持這種左右縮排置中，
   不要一下靠左一下靠右」，維持置中；欄內的標題／段落文字本身仍是預設
   的靠左對齊（沒有另外設 text-align），不是整塊置中。
   ⚠ 2026-08-13 客戶「中間內文寬一點，現在太窄」——寬度不再沿用
     .page--courses 既有的 --about-measure（616px，29-4 節，其餘兩堂課
     仍用這個窄版），這一頁單獨加寬到 47em（≈760px），只在
     .course-detail--editorial 下生效，不動共用的 --about-measure token。
   ⚠ 選擇器故意寫 .section-bleed-inner.cd-article（3 個 class）而不是只寫
     .course-detail--editorial .cd-article（2 個 class）：markup 上這兩個
     class 本來就同時掛在同一顆 div，湊到跟第 20 節①那條 .page--v2 >
     .site-section > .section-bleed-inner（3 個 class）同一階特異度，
     不然內距/寬度會直接被那條全站規則蓋掉。 */
/* ⚠ 2026-08-13 全站響應式稽核（RWD workflow audit）抓到：768px／430px／375px
   三個窄寬度下，這欄文字整條貼齊視窗左右緣，完全沒有內距。根因：這條
   規則原本只有 max-width + margin-inline:auto——視窗比 47em（≈752px）
   寬時，欄本身比容器窄，auto margin 自然在兩側擠出空間，「看起來」有
   內距；但視窗一旦窄於 47em，max-width 不再限制寬度，欄直接撐滿容器
   （容器本身內距已被第 20 節①的 v2 歸零規則清空），文字就貼死在螢幕
   兩側。跟同樣是長文閱讀欄的 .article-col（.page--article，知識專區
   單篇在用）比對，那邊多補了一條 padding-inline: var(--article-x)，
   在任何寬度都成立，不靠 max-width 這個間接效果撐內距——這裡照抄同一個
   做法，補上明確的 padding-inline，不再只靠 auto margin 碰運氣。 */
/* ⚠ 2026-08-13 客戶回報「實體課程內文左右留白太多，跟知識專區的個別課程
   （單篇文章）對不起來」——實測 1920 寬時，知識專區的讀文欄實際文字寬
   864px，這裡卻只有 445px，即使 max-width 寫的是 47em(752px)。
   根因：max-width 與 padding-inline 疊在**同一顆元素**上——border-box
   下 max-width 框住的是「內容 + padding」，padding 又內縮 var(--article-x)
   （寬螢幕最深到 10.5rem／168px，兩側共 336px），752 - 336 只剩約 416～
   445px，跟名目上的 752px 對不起來，也比知識專區窄了一半。
   知識專區能到 864px，是因為兩件事分別掛在**不同**元素上：外層
   .article-col 只管 padding-inline（版面內縮），內層 .page-narrow 只管
   max-width:54rem（行長／可讀性），兩者互不相減。
   這裡照抄同一個分法：.cd-article 自己只保留 padding-inline（內縮，跟
   768/430/375 那次修的一樣），max-width 移到它的直接子層（下一條規則）
   ——h2／editor-block／cd-sublabel／page-text／cd-tiles 這些子元素從此
   各自在已內縮好的空間裡再置中收窄到 54rem，跟知識專區同一個讀文寬度。 */
.course-detail--editorial .section-bleed-inner.cd-article {
    padding-inline: var(--article-x);
}

.course-detail--editorial .section-bleed-inner.cd-article > * {
    max-width: 54rem;
    margin-inline: auto;
}

/* ⚠ 2026-08-14 第十輪客戶「報名資訊改左圖右文，該區塊不額外添加背景色」
   ——之前兩輪客戶陸續要求這個色帶要壓灰（先 --surface-grey、後改更淺的
   --background，historia 見下方保留的舊註解），這一輪明確要求收回，
   不再額外上色，色帶背景交回頁面預設（跟緊鄰的 .cd-article／動作列
   一致，不特別處理）——background 宣告與 markup 上的 .band--grey class
   一併拿掉，不是遺漏。
   舊註解（背景色決策沿革，保留供回查）：
   　「客戶前後給了兩次色票：先是『報名資訊的底太深，請壓這個灰』，把
   　.info-panel--grey 的 --surface-grey（87%）換成更淺的 --background
   　（96.5%，courses_list.php 三階灰裡最淺那階）；後來整個資訊面板改版
   　（圖文左右並排＋整條色帶），色帶預設吃 .band--grey（也是
   　--surface-grey），客戶再次『灰底壓這個』附上同一種很淺的灰」。 */

/* ⚠ 2026-08-14 第十輪「左圖右文」——原本文字（兩欄內容）在左、圖片在右，
   這裡把 markup 順序對調（.cd-enroll-media 移到 .cd-enroll-text 前面），
   grid-template-columns 數值不變，圖片吃第一欄（1.15fr，比之前的 .85fr
   寬），文字吃第二欄（.85fr）——文字內容這一輪也從左右兩欄收成單欄
   堆疊（見下面 .cd-enroll-cols），不再需要跟之前一樣寬，圖片自然可以
   分到比較大的比例，兩處數值不用另外調。
   ⚠ 內距：沿用 --article-x（跟最下面「回實體課程／收藏／分享」那條
     .entry-foot 同一個內距，見第 20 節①的白名單），不是 Hero 用的
     --section-x；.section-bleed-inner 要湊滿 3 個 class 特異度才蓋得過
     第 20 節①的全站歸零規則，原因同 .cd-article 那次。 */
.course-detail--editorial .section-bleed-inner.cd-enroll-split {
    display: grid;
    grid-template-columns: minmax(0, 1.15fr) minmax(0, .85fr);
    gap: clamp(2rem, 5vw, 4rem);
    align-items: start;
    padding-inline: var(--article-x);
}

/* 2026-08-18 客戶「該區塊右邊不要白色背景，改成兩條水平線上下劃分，
   線寬要頁面總寬」——「不額外上色」是 2026-08-14 就定案的舊決定（見上面
   markup 註解），但那條決定當時假設「交回頁面預設」＝ body 的
   --background（#F6F6F7，很淺的灰）；實測發現 .cd-enroll-band 沒有
   自己的 background 規則，會落回 .page--v2 .band 那條通用預設
   （background:hsl(var(--muted))，本檔第 1 節就定義成純白 #FFFFFF，
   見 style.css ≈13293），兩者是不同顏色。左圖被照片蓋住看不出差異，
   右邊文字這一側因此讀起來是「真的白」，不是很淺的灰。
   ⚠ 2026-08-19 客戶追問「為什麼文字那欄背景還是白色」——原本這裡改成
     background:transparent，以為「不設背景」就會讓 body 的 --background
     透上來，但沿著 DOM 祖先鏈往上實測才發現：body 跟 .cd-enroll-band
     中間還有一層 .wf-frame（wireframe.css:19，`.wf-frame{
     background:#fff }`，全站共用的殼層，style.css 這頁沒有覆寫過），
     本身是純白、且是不透明的——transparent 只會讓「哪一層白色透出來」
     從 .page--v2 .band 換成 .wf-frame，畫面上還是白的，不會真的變成
     body 的淺灰。改成明確畫出 hsl(var(--background))，不再依賴
     transparent 往下透這個因為 .wf-frame 擋在中間而不成立的機制——
     這才是 08-14「交回頁面預設」那個決定原本想要的顏色，只是一直被
     這層没被發現的白色殼層擋住沒有真正生效。 */
.course-detail--editorial .cd-enroll-band {
    background: hsl(var(--background));
}

/* 圖片上下各一條滿版水平線（同一個客戶指令，見上方 markup 註解），
   取代拿掉的白底，「線寬要頁面總寬」——量到邊到邊，不是只到這欄本身
   的寬度。
   ⚠ .cd-enroll-media／.cd-enroll-text 都是 2 欄不對稱 grid
     （1.15fr／.85fr）裡的欄，不是像 courses_list.php 的 .cx-theme-rule
     那樣包在單欄、本身就水平置中的容器裡——那條用
     left:50%+margin-left:-50vw 的技巧成立是因為它的直接容器本身就
     置中在視窗正中央，50% 位移量抵掉 50vw 位移量剛好落在視窗左右緣；
     這兩欄的水平中心都不在視窗正中央，照抄同一條算式會抵不平，線會
     偏出視窗範圍，所以兩條線在 markup 上不是塞進任何一欄裡面，是
     .cd-enroll-band 的直接子層（見 course_detail.php）。
   ⚠ .cd-enroll-band 本身是 .section-bleed（max-width:none;
     padding-inline:0，見本檔開頭該 class 定義），沒有左右內距，天生
     就是 100vw 邊到邊——這裡當它們的定位基準（position:relative），
     子層只要 left:0;right:0 就是精確的滿版寬度，不需要 100vw／視窗
     置中的百分比運算，對「容器本身沒有置中」的巢狀狀況更穩定。
   ⚠ 2026-08-18 客戶「水平線請根據圖片高來畫」——一開始這兩條線對齊
     .cd-enroll-text（文字欄）的上下緣，客戶回報線畫在照片中段、像是
     橫切過照片，不是框住照片。根因：.cd-enroll-media 帶負 margin 出血
     （見上方該規則，margin 上下各扣一個 var(--v2-band-y)），桌機版
     圖片會直接貼齊 .cd-enroll-band 自己的外緣（實測圖片矩形跟 band
     矩形完全疊合），比文字欄（有 band 自己的內距）還要再往外多出
     --v2-band-y 那一截——線對齊文字欄，等於畫在圖片內側偏中間的
     位置。改成量 .cd-enroll-media 的實際位置才是根因的修法，不是換
     一個更準的數字而已（詳細算式與 JS 見 course_detail.php
     `cd-enroll-rule` 那支 IIFE，同一套「量另一個元素、寫 CSS 自訂
     屬性」手法沿用 home.js syncHeroLines()）。
   ⚠ CSS 端 fallback 從 var(--v2-band-y) 改成 0（JS 尚未執行的那一刻／
     關閉 JS 時）——桌機圖片出血貼齊 band 外緣，正確值本來就趨近 0，
     0 比舊的 --v2-band-y（文字欄內距）更接近「圖片高」這個新的定位
     依據，退化結果更準；JS 一執行就會立刻修正到精確位置，不會有
     一直錯下去的情況。 */
.course-detail--editorial .cd-enroll-band {
    position: relative;
}

/* 2026-08-19 客戶「那兩條水平線請壓過圖片，現在兩條在圖片之下」——
   ⚠ 根因：.cd-enroll-rule--top 在 markup 上排在 .section-bleed-inner
     （裝圖片的那層）之前，.cd-enroll-media 本身是 position:relative，
     跟這條線一樣落在同一個「有定位、z-index:auto」的層級，這種情況
     瀏覽器是按 DOM 順序決定疊放，圖片在後面、線在前面，線因此被壓在
     圖片下面；--bottom 那條雖然排在 .section-bleed-inner 之後，實測
     （elementFromPoint 逐點命中測試）仍然被圖片擋住，图片內部的遮罩
     格（.cd-enroll-mask-grid，z-index:2＋will-change:transform 各自
     建立新的疊放環境）疊放優先權比預期更高，兩條線因此都輸給圖片。
   ⚠ 修法：直接給明確的 z-index，蓋過圖片內部所有子層（遮罩格最高只到
     2），不用去逐一拆解每一層是否建立了新的疊放環境——這兩條本來就是
     純裝飾、pointer-events:none，疊到最上層沒有任何互動面的風險。 */
.course-detail--editorial .cd-enroll-rule {
    display: block;
    position: absolute;
    left: 0;
    right: 0;
    z-index: 3;
    height: 1px;
    background: hsl(var(--foreground) / .2);
    pointer-events: none;
}

.course-detail--editorial .cd-enroll-rule--top {
    top: var(--cd-enroll-rule-top, 0px);
}

.course-detail--editorial .cd-enroll-rule--bottom {
    bottom: var(--cd-enroll-rule-bottom, 0px);
}

/* ⚠ 2026-08-14 第十輪「文字左右兩欄位改上下編排」——原本課程資訊表格／
   報名方式是並排兩欄，這裡收成單欄，兩塊資料自然上下堆疊（grid 只剩
   一條欄位，兩個 .cd-enroll-col 各自佔一列）。
   ⚠ 舊版「兩欄同高＋按鈕壓底對齊」的技巧（align-items:stretch＋第二欄
     flex column＋按鈕 margin-top:auto）是專門解決「左右並排兩欄視覺
     對齊」的問題，單欄堆疊之後兩塊資料不再左右比鄰、沒有「跟另一欄
     切齊」這件事，整組拿掉，不留無效的死規則。 */
.course-detail--editorial .cd-enroll-cols {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: clamp(1.5rem, 3vw, 2.5rem);
    margin-top: clamp(1.5rem, 3vh, 2rem);
}

/* ⚠ 客戶「右邊跟左邊小字文字比重調一致，右邊看起來很寬」——兩欄本身
   是同寬的 1fr／1fr，但左欄是 .info-table（th 固定 9.5rem＋短短的
   值，文字撐不滿欄寬，留了一段空白），右欄的段落卻真的把整個欄寬
   填滿再換行，兩邊「墨色份量」看起來不對等。這裡把右欄段落的行長
   收窄，讓它也留一段空白轉行，跟左欄的疏密感一致，不是縮小欄位本身。 */
.course-detail--editorial .cd-enroll-col .page-text {
    max-width: 22em;
}

/* ⚠ 客戶「這裡段落間距大一點，有點擠」——$flows() 把 (1)(2) 兩則拆成
   各自的 <p>，塞在同一個 .page-text 裡；.page-text 只給了行高
   （lh-body），沒有管「這個 div 裡面 p 跟 p 之間」的間距，兩段幾乎是
   貼著的。這裡補一個 p+p 的段距，跟本頁其餘地方段落與段落之間的呼吸
   感一致。 */
.course-detail--editorial .page-text p + p {
    margin-top: clamp(1.25rem, 2.5vh, 1.75rem);
}

/* 客戶「讓每段文字 hover 變藍色」——原本用全站唯一的強調色 --accent-blue
   （AERIAL，第 7 節「顏色搭配以這四個為搭配」明文規定的四色之一）。
   2026-08-25 客戶回頭指定「純文字也要 hover，但把藍色改淺灰色」——
   純文字（沒有連結可點）用藍色會誤導成連結，改用 --slate（SLATE 原色，
   比預設內文顏色 --muted-foreground 淺一階，見 .page-text／.article
   共用規則那邊的同款處理，全站現在統一這個邏輯）。
   ⚠ 這條 `.course-detail--editorial` 頁面限定規則的 specificity
     （0,3,1）比後來新增的全站共用規則 `.page-text > p:hover`（0,2,1）
     高，共用規則在這頁會被這裡蓋掉，所以這裡也要跟著改，不能只改
     共用規則就結束——兩邊都要同步。 */
.course-detail--editorial .page-text p {
    transition: color var(--transition);
}

.course-detail--editorial .page-text p:hover {
    color: hsl(var(--slate));
}

/* ⚠ 客戶「圖片壓在灰底上不要留空間／圖片跟灰底貼邊」「圖片上下貼齊灰底
   不要留邊」——圖片四邊（原本右／上／下）都貼齊這一列所在色帶的邊界，
   不留白；原圖比例（2336×992 一類的橫幅）跟這一欄實際的行高不會剛好
   相等，四邊都貼齊、又不能留白，唯一做法是拉滿整個格高＋
   object-fit: cover 裁切——跟 .cd-media／封面圖那組「絕不裁切」的原則
   不同，這裡是客戶在看過結果後明確要的另一種效果（滿版色塊感優先於
   完整看到全圖），只在這一顆卡片生效，其餘 .cd-media 的圖仍然不裁切。
   ⚠ 2026-08-14 第十輪「改左圖右文」——圖片從第二欄（右）搬到第一欄
     （左），原本抵銷右側 --article-x 內距的負 margin-right 跟著鏡射
     成負 margin-left，貼邊的對象一樣是圖片現在所在那一側的欄位邊界，
     上下貼邊（--v2-band-y 負值）不受左右對調影響。 */
.course-detail--editorial .cd-enroll-media {
    position: relative;
    overflow: hidden;
    margin: calc(var(--v2-band-y) * -1) 0 calc(var(--v2-band-y) * -1) calc(var(--article-x) * -1);
    align-self: stretch;
}

.course-detail--editorial .cd-enroll-media picture,
.course-detail--editorial .cd-enroll-media img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* ⚠ 2026-08-14 客戶「讓底下報名區塊也用遮罩方塊的方式呈現」——沿用上方
   Hero（.cd-hero-lines／.cd-hero-mask-grid）同一套矩形遮罩逐塊顯影的
   視覺語言：永久細線層留在圖片上（.cd-enroll-lines）＋動畫遮罩層蓋住
   圖片、捲動進到可視範圍才縮回（.cd-enroll-mask-grid，見本檔底部第二支
   <script>）。
   ⚠ 跟 Hero 的兩個差異：
     1) 形狀——.cd-enroll-media 是純矩形、沒有 Hero 那種 clip-path 缺角，
        改成 3 欄 × 2 列滿版填滿（不像 Hero 4×2 留兩格空白去配合缺角）。
     2) 顏色——Hero 遮罩讀 --surface-grey（跟 Hero 那條色帶背景同色）；
        這個區塊今天稍早才明確拿掉專屬背景色（「該區塊不額外添加背景
        色」），吃的是頁面預設底色 --background，遮罩色跟著改用
        --background，不然遮罩色塊會跟區塊底色不一致，顯影前那一刻會
        像一塊突兀的灰矩形而不是融入背景。 */
.course-detail--editorial .cd-enroll-lines {
    position: absolute;
    inset: 0;
    z-index: 1;
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    grid-template-rows: repeat(2, 1fr);
    grid-template-areas:
        "a b c"
        "d e f";
    pointer-events: none;
}

.course-detail--editorial .cd-enroll-line {
    border: 1px solid hsl(var(--background) / .85);
}

.course-detail--editorial .cd-enroll-line--a { grid-area: a; }
.course-detail--editorial .cd-enroll-line--b { grid-area: b; }
.course-detail--editorial .cd-enroll-line--c { grid-area: c; }
.course-detail--editorial .cd-enroll-line--d { grid-area: d; }
.course-detail--editorial .cd-enroll-line--e { grid-area: e; }
.course-detail--editorial .cd-enroll-line--f { grid-area: f; }

.course-detail--editorial .cd-enroll-mask-grid {
    position: absolute;
    inset: 0;
    z-index: 2;
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    grid-template-rows: repeat(2, 1fr);
    grid-template-areas:
        "a b c"
        "d e f";
    gap: 0;
    pointer-events: none;
}

/* 2026-08-19 客戶回報遮罩動畫過程中格與格交界處會閃過一絲顏色——同一個
   次像素縫隙問題，詳細成因與修法見 .hero-mask 那條同款註解（本檔開頭
   ≈621 行）。 */
.course-detail--editorial .cd-enroll-mask {
    background: hsl(var(--background));
    margin: -1px;
    transition: transform 560ms cubic-bezier(0.76, 0, 0.24, 1);
    will-change: transform;
}

/* 六塊縮回方向交替用 scaleX／scaleY，相鄰塊方向交錯，跟 Hero 同一個
   「不是隨手排」的原則。 */
.course-detail--editorial .cd-enroll-mask--a { grid-area: a; transform-origin: left;   transform: scaleX(1); }
.course-detail--editorial .cd-enroll-mask--b { grid-area: b; transform-origin: top;    transform: scaleY(1); }
.course-detail--editorial .cd-enroll-mask--c { grid-area: c; transform-origin: right;  transform: scaleX(1); }
.course-detail--editorial .cd-enroll-mask--d { grid-area: d; transform-origin: bottom; transform: scaleY(1); }
.course-detail--editorial .cd-enroll-mask--e { grid-area: e; transform-origin: left;   transform: scaleX(1); }
.course-detail--editorial .cd-enroll-mask--f { grid-area: f; transform-origin: right;  transform: scaleX(1); }

.course-detail--editorial .cd-enroll-media.is-cd-enroll-revealed .cd-enroll-mask--a,
.course-detail--editorial .cd-enroll-media.is-cd-enroll-revealed .cd-enroll-mask--c,
.course-detail--editorial .cd-enroll-media.is-cd-enroll-revealed .cd-enroll-mask--e,
.course-detail--editorial .cd-enroll-media.is-cd-enroll-revealed .cd-enroll-mask--f {
    transform: scaleX(0);
}

.course-detail--editorial .cd-enroll-media.is-cd-enroll-revealed .cd-enroll-mask--b,
.course-detail--editorial .cd-enroll-media.is-cd-enroll-revealed .cd-enroll-mask--d {
    transform: scaleY(0);
}

@media (prefers-reduced-motion: reduce) {
    .course-detail--editorial .cd-enroll-mask {
        display: none;
    }
}

.course-detail--editorial .cd-enroll-media.is-cd-enroll-static .cd-enroll-mask {
    display: none;
}

@media (max-width: 1023.98px) {
    /* 手機版疊成一欄之後圖片沒有專屬的「左邊」可以貼，負邊距會讓圖片
       整張被拖出視窗外——收回，圖片改回自然比例、不裁切，跟其餘
       .cd-media 一致（窄螢幕本來就是上下堆疊，不需要撐滿色帶）。
       ⚠ 2026-08-19 客戶回報「圖片寬填滿該頁面寬現在左右有留空間不要」：
       margin:0 只是把桌機版「往左出血」的負 margin 收回，圖片因此退回
       .cd-enroll-split 自己的 padding-inline:var(--article-x)（桌機版
       規則，這裡沒有覆寫掉，見上面 .section-bleed-inner.cd-enroll-split）
       裡面，左右都留了一截內距，不是「沒有出血」，是「兩邊都內縮」。
       改成 margin-inline 出血抵銷這段內距（上下維持 0，客戶沒有說高度
       要動），圖片才會貼齊視窗兩側。 */
    .course-detail--editorial .cd-enroll-media {
        margin-block: 0;
        margin-inline: calc(-1 * var(--article-x));
        align-self: auto;
    }

    .course-detail--editorial .cd-enroll-media picture,
    .course-detail--editorial .cd-enroll-media img {
        height: auto;
        object-fit: unset;
    }
}

/* 客戶「報名資訊用段落小字的分隔，不要線條」——.info-table 原本每列
   中間有一條 1px 線（12 節，全站共用），這一頁改成只靠列與列之間的
   留白分隔，不畫線；.info-table 本身不動，其餘課程頁的表格照舊有線。 */
.course-detail--editorial .info-table tr + tr th,
.course-detail--editorial .info-table tr + tr td {
    border-top: 0;
}

@media (max-width: 1023.98px) {
    /* ⚠ 2026-08-13 客戶「報名資訊手機寬度時圖片跟文字改上下編排」——
       這裡原本只寫 .course-detail--editorial .cd-enroll-split（2 個
       class），特異度打不贏上面桌機版 .course-detail--editorial
       .section-bleed-inner.cd-enroll-split（3 個 class），手機寬度
       grid-template-columns 還是套用桌機那條 1.15fr/.85fr 兩欄定義，
       文字跟圖片被硬塞成兩條窄欄並排、沒有真的疊成一欄——跟本節上面
       .cd-article／.cd-enroll-split 桌機版同一顆坑，選擇器一樣要補
       .section-bleed-inner 湊到 3 個 class 才蓋得過去。 */
    .course-detail--editorial .section-bleed-inner.cd-enroll-split {
        grid-template-columns: minmax(0, 1fr);
        gap: 1.75rem;
    }

    .course-detail--editorial .cd-enroll-cols {
        grid-template-columns: minmax(0, 1fr);
        gap: 1.5rem;
    }
}

/* ⚠ 2026-08-21 客戶「平板寬度（768／820／1024）報名資訊最下面的 1,2 兩點
   字寬拉寬，前往購買按鈕放右邊」——「1,2 兩點」是 $flows($c['apply']) 印出
   的兩段 (1)(2) 報名須知（.cd-enroll-col 第二欄的 .page-text）。
   ⚠ .cd-enroll-col .page-text{max-width:22em} 是桌機兩欄並排時的既有
     規則（客戶當時要求「右邊跟左邊小字文字比重調一致」，讓段落行長跟左欄
     的 .info-table 視覺份量對齊，見本檔該規則上方說明）——那個理由只在
     「左右並排」時成立。這個寬度區間（768–1199.98px）左右兩欄已經疊成
     單欄（見上面 ≤1199.98 那個既有區塊），22em 沒有東西可以「對齊」了，
     卻還是把整欄寬度都讓出一大截空白，這裡才是真正「字太窄」的成因。
     只在這個範圍內拿掉上限，桌機兩欄並排（≥1200）與真手機（≤767.98，
     容器本來就比 22em 窄，這條上限在那裡本來就不是真正在起作用的
     限制）都不受影響。
   ⚠ 按鈕：.page-hero-actions 是全站共用 class（本頁最上面 Hero 區塊的
     CTA 也用同一顆），這裡用 .cd-enroll-text 限定範圍，只改「報名資訊」
     這裡的按鈕，不動 Hero 那顆。 */
@media (min-width: 768px) and (max-width: 1023.98px) {
    .course-detail--editorial .cd-enroll-col .page-text {
        max-width: none;
    }

    .course-detail--editorial .cd-enroll-text .page-hero-actions {
        justify-content: flex-end;
    }
}

/* ⚠ .page--v2 .editor-block > div:not(.editor-pair)（第 20 節①）比
   .page--courses .editor-block > .page-text（29 節）多算一個元素選擇器
   （div），特異度反而更高，即使源碼順序更早也還是贏——.page-text 因此
   吃到 padding-inline: var(--article-x)，在已經置中好的 616px 欄位裡
   再內縮一層，變成標題跟段落兩條不同的左邊界。用四個 class 的選擇器
   把特異度蓋過去，段落內距歸零，寬度交給 .cd-article 自己決定就好。 */
.course-detail--editorial .cd-article .editor-block > .page-text {
    padding-inline: 0;
    margin-inline: 0;
    max-width: none;
}

/* ⚠ 2026-08-13 客戶「內文文字每個區塊文字內容多一點」——文字本身沒有
   增加一個字（xlsx 原稿不能自己加內容），拿掉逐句斷行、改用單一流動
   段落之後，每一段在加寬過的欄位裡反而縮成很少幾行，看起來份量不夠。
   這裡把字級從全站 --text-body（14px）調大一階，段落讀起來更有份量，
   不是塞更多文字進去。只在這一頁的內文段落生效，不動 --text-body
   這個全站 token。 */
.course-detail--editorial .cd-article .page-text {
    font-size: 1rem;
    line-height: 1.9;
}

/* ── 條列式段落（學習重點／課程流程）：純文字＋編號，段落間留白分段，
   不畫格線——同樣是「線條太多拿掉一些」的處理。 */
.course-detail--editorial .cd-tiles {
    margin-top: clamp(1.5rem, 3vh, 2rem);
}

.course-detail--editorial .cd-tiles p {
    margin: 0;
    font-size: var(--text-body);
    line-height: var(--lh-body);
    font-weight: var(--fw-light);
    letter-spacing: var(--tracking);
    color: hsl(var(--muted-foreground));
}

.course-detail--editorial .cd-tiles p + p {
    margin-top: 1.25rem;
}

.course-detail--editorial .cd-tile-no {
    display: inline-block;
    min-width: 2.75em;
    font-family: var(--font-eyebrow);
    font-size: var(--text-meta);
    letter-spacing: var(--tracking-meta);
    color: hsl(var(--foreground) / .4);
}

/* 小標籤（授課老師／上課地點／適合對象）：參考頁「○ オフショット」的
   小圓圈起手式，比 H2 輕一階，不需要整條底線。 */
.course-detail--editorial .cd-sublabel {
    display: flex;
    align-items: center;
    gap: .625rem;
    margin: clamp(1.75rem, 4vh, 2.5rem) 0 .75rem;
    font-size: var(--text-body);
    font-weight: var(--fw-normal);
    letter-spacing: var(--tracking-title);
    color: hsl(var(--heading));
}

.course-detail--editorial .cd-sublabel::before {
    content: "";
    flex: 0 0 auto;
    width: .5em;
    height: .5em;
    border: 1px solid hsl(var(--foreground) / .5);
    border-radius: 50%;
}

/* ── 編輯器內容對映到上面兩支樣式（2026-08-28）─────────────────────────
   設計稿（beta）的「講師與上課地點」是手寫 p.cd-sublabel，「課程流程」是
   手寫 div.cd-tiles > p > span.cd-tile-no；但實際內文是客戶用 CKEditor 打的，
   編輯器只吐得出 h3 與 ol/li，套不到那兩支樣式 —— 同一個 band 因此差了
   13.8px（課程流程，多一段 ol 的 margin-bottom）與 128px（講師與上課地點，
   h3 被當成 28px 大標，還跟自己的內文被拉開 48px）。

   ⚠ 改樣式不改 markup：h3 還是 h3、ol 還是 ol（語意／SEO／編輯器所見即所得
     都不動），只是把既有的 .cd-sublabel／.cd-tiles 規則對映過來。要是反過來
     在 view 裡把 h3 換成 p.cd-sublabel，客戶在後台看到的跟前台就對不起來了。
   ⚠ 小標的上距刻意留給 h3 自己的 margin（會跟 h2 的 margin-bottom 合併，
     跟設計稿一樣），所以帶小標的那段要把 .page-text 的 48px 關掉，
     否則 48 + 38 疊成一坨。 */
.course-detail--editorial .cd-article .page-text > :is(h3, h4) {
    display: flex;
    align-items: center;
    gap: .625rem;
    margin: clamp(1.75rem, 4vh, 2.5rem) 0 .75rem;
    font-size: var(--text-body);
    /* ⚠ 行高吃 1.5 不是 --lh-body(2.3)：小標只有一行，2.3 會讓它撐到 32.2px，
       設計稿的 .cd-sublabel 是 21px（它掛在 .cd-article 下，繼承 1.5）。
       差 11.2px × 兩個小標 = 一個 band 高 22.4px 的落差。 */
    line-height: 1.5;
    font-weight: var(--fw-normal);
    letter-spacing: var(--tracking-title);
    color: hsl(var(--heading));
}

.course-detail--editorial .cd-article .page-text > :is(h3, h4)::before {
    content: "";
    flex: 0 0 auto;
    width: .5em;
    height: .5em;
    border: 1px solid hsl(var(--foreground) / .5);
    border-radius: 50%;
}

/* 小標開頭的那一段：上距歸零，交給小標自己的 margin（見上面第二個 ⚠） */
.course-detail--editorial .cd-article .editor-block > .page-text.page-text--label {
    margin-top: 0;
}

/* ol 比照 .cd-tiles：拿掉瀏覽器預設的 32px 縮排與項目符號，改用 counter
   生 01／02（設計稿的 .cd-tile-no）。用 counter 而不是在 view 裡寫死編號，
   是因為條列有幾行由客戶決定。
   ⚠ 只吃 <ol> 不吃 <ul>：設計稿的「課程流程」是有序清單（01／02 就是它的
     項目符號），拿掉圓點換成編號是等價替換；<ul> 沒有東西可以替換，一起
     套用只會變成「沒有項目符號、也沒有縮排」的一排裸文字（aid 4~6 的
     「你將練到的」「適合誰」就是 <ul>，實測會整個變平）。 */
.course-detail--editorial .cd-article .page-text > ol {
    margin: clamp(1.5rem, 3vh, 2rem) 0 0;
    padding-left: 0;
    list-style: none;
    counter-reset: cd-tile;
}

.course-detail--editorial .cd-article .page-text > ol > li {
    margin: 0;
    font-size: var(--text-body);
    line-height: var(--lh-body);
    font-weight: var(--fw-light);
    letter-spacing: var(--tracking);
    color: hsl(var(--muted-foreground));
}

.course-detail--editorial .cd-article .page-text > ol > li + li {
    margin-top: 1.25rem;
}

.course-detail--editorial .cd-article .page-text > ol > li::before {
    counter-increment: cd-tile;
    content: counter(cd-tile, decimal-leading-zero);
    display: inline-block;
    min-width: 2.75em;
    font-family: var(--font-eyebrow);
    font-size: var(--text-meta);
    letter-spacing: var(--tracking-meta);
    color: hsl(var(--foreground) / .4);
}

/* ───────────────────────────────────────────────────────────────────────
   46. 知識專區列表：開場標題對齊 Logo／側欄（2026-08-13 客戶指定）
   ───────────────────────────────────────────────────────────────────────
   客戶：「知識專區對齊 logo」「對齊知識分類」——開場帶（KNOWLEDGE ZONE／
   知識專區 H1）用的是 .page--simpla 共用的 .simpla-pad，內距是
   --article-x；下面篩選帶的側欄（知識分類／標籤篩選）跟頁首 Logo
   實測都落在同一條線上（跟本檔第 45 節課程內頁 Hero 踩到的是同一種
   落差，只是這次的元件換成 .simpla-pad）。
   ⚠ 只覆寫這一頁：選擇器多加 .page--knowledge，.simpla-pad／
     .page--simpla 本身的定義不動——服務專區、電子報等其他掛
     .page--simpla 的頁面仍用原本的 --article-x，一個像素都不受影響。 */
.page--knowledge .simpla-open .simpla-pad {
    padding-inline: var(--section-x);
}

/* ───────────────────────────────────────────────────────────────────────
   47. 知識專區內文（單篇）：標頭圖文一列＋滿版主視覺
       （2026-08-13 依 willstyle.co.jp/journal/2502/ 改版）
   ───────────────────────────────────────────────────────────────────────
   客戶：「知識專區改用這個方式呈現 https://willstyle.co.jp/journal/2502/」。
   參考稿的標頭是「標題（左）＋日期／分類／標籤（右）」同一列並排，圖片在
   標頭下方獨立一整條、滿版但不出血（跟內文同寬，不是貼齊視窗邊界）。
   ⚠ 這裡取代上一輪「圖片在右、標題文字在左」的左右並排版（.kd-hero-split，
     見 git 歷史）——客戶看過新參考稿後改指定這個方向，同一次迭代裡的
     再一次調整，不是兩個版本並存，舊選擇器已整組移除。
   ⚠ 只有這一頁改：選擇器都掛 .kd-hero，只有這個檔案會輸出這個 class，
     其他掛 .page--simpla 的頁面（服務內頁、電子報、知識列表…）一個
     像素都不受影響。
   ⚠ 內距用 --section-x（跟第 46 節知識列表、第 45 節課程內頁 Hero
     同一條對齊 Logo 的基準線），不是 .simpla-pad 預設的 --article-x——
     這一頁不再套用 .simpla-pad，直接在這裡給內距，兩者不會疊加。
   ⚠ 選擇器故意寫 .page--article .section-bleed-inner.kd-hero-wrap（3 個
     class）——跟本檔 .cd-article／.cd-enroll-split／上一輪 .kd-hero-split
     踩過同一個坑：.page--v2 > .site-section > .section-bleed-inner
     （第 20 節①，3 個 class）會把內距與寬度歸零，只寫 .kd-hero-wrap
     （1 個 class）特異度打不贏，內距會直接被蓋成 0。 */
.page--article .section-bleed-inner.kd-hero-wrap {
    padding-inline: var(--section-x);
}

/* 標頭列：標題在上（吃滿寬度）＋ 分類/標籤/日期在下一行、置中。
   ⚠ 2026-08-13 客戶附截圖糾正：「這一塊跟左邊標題不要跟標題同欄位高度，
     改成下一行，並且往左移動在畫面中間位置。現在位置還是整個靠右」——
     上一輪 .kd-hero-info 是跟標題同一列、靠右對齊（justify-content:
     space-between + align-items:center，見 git 歷史），客戶要的是拆成
     兩行：標題自己一行在上，分類/標籤/日期整組挪到下一行、且要從右緣
     移到「畫面中間」，不是繼續貼右。改用 flex-direction:column，
     .kd-hero-titles 自然吃滿整行，.kd-hero-info 用 align-self:center
     在下一行水平置中（置中基準是這個標頭列本身的內容寬度，跟下面
     .kd-hero-media 置中用的是同一個「頁面可讀寬度」概念，不是貼齊
     視窗最外緣）。
   ⚠ 2026-08-13 客戶「知識專區的標題上方留白跟離左邊距離請跟實體課程課程
     單頁一樣」——照抄 .course-detail--editorial .cd-hero-grid（本檔 line
     16794 一帶）的兩個值：
       padding-inline: max(0px, calc(var(--article-x) - var(--section-x)))
         父層 .kd-hero-wrap 已經給了 --section-x（跟 Logo 對齊，麵包屑
         用的那一條），這裡再補上 article-x 與 section-x 的差額，讓標題
         這一列的左緣落在 --article-x，跟課程內頁 H1 同一條線——麵包屑
         本身不受影響，還是 --section-x（課程內頁也是這樣，麵包屑不跟著移）。
       margin-top: 300px
         課程內頁「標題往下移」那幾輪最後定案的量，這裡照搬同一個數字，
         不是重新拉一次留白。 */
.kd-hero-head {
    display: flex;
    flex-direction: column;
    gap: 1.5rem;
    padding-inline: max(0px, calc(var(--article-x) - var(--section-x)));
    margin-top: 300px;
    /* ⚠ 客戶「上方離標籤遠一點」——圖片跟標籤（.kd-hero-info）之間要拉開，
       改用 --v2-band-y（色帶上下留白，本檔 13483／14135 一帶既有值）
       取代原本的 clamp(2rem,4vw,3rem)，跟下面 .kd-hero-media 收窄底部
       間距用的是同一支變數，不是隨手挑一個新數字。 */
    margin-bottom: var(--v2-band-y);
}

/* ⚠ 客戶「標籤上方不要跟標題留空白這麼大」——bug 根源：.kd-hero-head
   改成 flex-direction:column 之後（見上面「往左移動在畫面中間位置」
   那一輪），這裡沿用舊的橫向版 flex:1 1 28rem 沒有跟著改。那組簡寫在
   column 容器裡 flex-basis 管的是**高度**，28rem＝448px 變成這個區塊
   的最小高度，內容（英文小標＋H1＋副標）明明只需要約 160px，多出來的
   ~290px 空白正是這裡憑空撐出來的——不是 .kd-hero-head 的 gap 太大
   （gap 一直是 1.5rem，沒有變過），是 .kd-hero-titles 自己的框比內容高
   很多，下面的 .kd-hero-info 只是照著這個被撐大的框往下排。
   改成 flex:none：不吃這個容器多餘的成長空間，高度就是內容本身。 */
.kd-hero-titles {
    flex: none;
    min-width: 0;
}

.kd-hero-titles .page-head {
    margin-bottom: 0;
}

/* ⚠ 2026-08-13 客戶「往左移動在畫面中間位置」——.kd-hero-head 現在是
   flex-direction:column，這裡的置中改用 align-self:center：在自己
   這一行（橫向＝現在的交叉軸）水平置中，不是佔滿寬度後文字置中，
   跟下面 .kd-hero-media 的置中是同一個「這一段內容自己在頁面可讀寬度
   中置中」邏輯，位置一致、視覺上兩者垂直對得起來。 */
.kd-hero-info {
    flex: 0 0 auto;
    align-self: center;
}

/* 分類/標籤與日期兩行各自靠左對齊、共用同一條左邊界（.kd-hero-info
   內部自己的排版，不受外層置中影響）——兩行內容長度不同，左對齊之後
   右緣自然長短不一（客戶說的「文字錯落」），跟 willstyle 參考稿
   Date／Category／Share 三行左對齊的節奏一致。 */
.kd-hero-tags {
    justify-content: flex-start;
}

.kd-hero-date {
    margin-top: .75rem;
    text-align: left;
}

/* 主視覺：滿版但不出血——寬度改成跟內文（.page-narrow，54rem）同寬並置中，
   不是吃滿這一列的內距寬（那樣會比下方文章欄寬很多，圖文兩段對不齊）。
   ⚠ 54rem 是抄 .page-narrow 的 max-width（本檔 line 11902 一帶）的字面值，
     兩處要同步改；沒有另外開一個共用變數是因為目前全站只有這裡跟內文
     故意要同寬，還不到需要一個新 token 的規模。
   圖片吃原圖比例，不裁切（跟課程內頁 .cd-media 同一個理由）。
   ⚠ 客戶「底部離底部文字近一點」——圖片下緣到下一段文章本體第一段文字
     之間原本量出來有 152px（.kd-hero 色帶自己的下內距 --v2-band-y ＋
     文章本體那一條色帶的上內距，同一支變數各出一半），比上方（改完後
     的 --v2-band-y 一份）明顯寬，兩邊不對稱。這裡用負的 margin-bottom
     抵掉其中一份 --v2-band-y，把下面兩段色帶內距疊出來的多餘空間收掉，
     留下的間距大約是「一份 --v2-band-y」，跟上方拉開後的量級接近，
     不是砍到貼在一起。 */
.kd-hero-media {
    max-width: 54rem;
    margin: 0 auto calc(var(--v2-band-y) * -1);
}

.kd-hero-media picture,
.kd-hero-media img {
    display: block;
    width: 100%;
    height: auto;
}

@media (max-width: 767.98px) {
    /* ⚠ .kd-hero-head 桌機／手機現在都是 flex-direction:column（本節開頭
       已改），這裡不必再靠 align-items／flex-basis 把 .kd-hero-info
       擠到下一行——標題與資訊本來就分成兩行，.kd-hero-info 的
       align-self:center 手機版原樣沿用，兩邊都置中，不必另外覆寫。
       200px 是桌機量出來的留白，照抄 .cd-hero-grid 的窄螢幕響應式版
       （本檔 line 16837 一帶）同一個 clamp，不是另訂新數字。 */
    .kd-hero-head {
        padding-inline: 0;
        margin-top: clamp(2.5rem, 12vh, 6rem);
    }
}

/* ⚠ 2026-08-13 客戶「相關文章用跟首頁實體專區小卡呈現方式一樣」，並附截圖
   「相關文章的底下用這種方式呈現」——knowledge_detail.php 的相關文章已經
   改成直接沿用 .course-grid／.course-cell／.course-link／.course-thumb／
   .course-date／.course-title／.course-place／.course-nav／.course-cursor／
   .course-meta 這一整組首頁「實體課程」的共用元件（見該檔案的說明），
   拖曳捲動／圓框游標／頁碼進度線三支 home.js IIFE 全部靠 class／id 找
   元素，同名重用直接生效，這裡不必再寫任何 JS。

   ⚠ .course-* 的預設文字顏色是給**深色底**用的（.course-title 基底是
     hsl(var(--surface-dark-foreground))，白色系），首頁靠 .course-section
     這個外層 class 把它們覆寫回淺底可讀的顏色（本檔「近期課程：標題與
     進度置中」一節）。但 .course-section 自己還帶了 padding-block／
     background（NUDE 色塊）——這一頁的相關文章已經有自己的 .band--grey／
     .page-block 留白慣例，整組搬過來背景與留白會疊兩次。這裡只挑
     .course-section 裡「文字顏色／字重」那幾條、不含背景與留白，
     照抄同一組值，扣掉不需要的部分。 */
.kd-related {
    --fw-light:   350;
    --fw-normal:  450;
    --fw-heading: 350;
}

.kd-related .course-title  { color: hsl(var(--heading)); }
.kd-related .course-date   { color: hsl(var(--slate)); }
.kd-related .course-place  { color: hsl(var(--foreground) / .55); }
.kd-related .course-thumb  { background: hsl(var(--foreground) / .05); }
.kd-related .course-thumb::after { background: hsl(var(--foreground) / .05); }
.kd-related .course-link:focus-visible { outline-color: hsl(var(--foreground)); }

/* 2026-08-25 客戶「全站文章介紹文字 hover 改藍色」——這裡的卡片雖然
   借用首頁「實體課程」的 .course-cell 元件，但內容其實是「相關文章」
   （文章，不是課程），跟 .pcard-title 同一次要求範圍內。
   ⚠ 不能動全站共用的 .course-link:hover .course-title（下面「近期課程」
     一節，NUDE 色）——那條同時管首頁真正的實體課程小卡，那裡是課程
     不是文章，不在這次「文章介紹」的範圍，改了會連帶首頁一起變。
     這裡疊一條只鎖 .kd-related、specificity 更高的規則單獨覆寫。
   ⚠ 用 --accent-blue-text（不是 --accent-blue）：原因同 .pcard-title
     那邊，AERIAL 直接當文字色壓淺底對比不足。 */
.kd-related .course-link:hover .course-title,
.kd-related .course-link:focus-visible .course-title {
    color: hsl(var(--accent-blue-text));
}

/* ⚠ .course-cell 有兩組互相疊加的規則：舊版「矩形小卡」（本檔 5538 一帶）
   給了 display:flex，當時卡片裡只有 .course-link 一個子元素，flex:1
   撐滿整張卡是對的；新版「橫向捲動列」只補了 flex:0 0 auto／min-width
   （本檔 6995 一帶）當它是 .course-grid 的橫向格子，兩條規則不衝突、
   疊起來一起生效——但 display:flex 預設是 flex-direction:row，這裡的
   卡片除了 .course-link 還多了 .kd-related-tags／.kd-related-links 兩個
   兄弟元素，三個一起被排成一橫排（圖擠成一小塊，標籤與 podcast/影片
   全部並排），版面整個壞掉。改成 column，三段回到上下堆疊，
   .course-link 自己內部的排版（本來就是 flex column）不受影響。

   ⚠ 2026-08-13 客戶「內容都包在小卡裡，標籤跟連結請都放在小卡裡，小卡
     不要背景用細框描邊」——分類/標籤與 podcast/影片本來是 .course-link
     卡片下面「卡外」的附加資訊，現在整組（圖／日期／標題／副標／分類／
     標籤／podcast／影片）要框進同一張卡。.course-cell 本來就是這三段
     內容共同的父層，直接在它身上補一圈細框＋內距，三段就一起被框住，
     不必再多包一層 DOM。
   ⚠ 底色拿掉：.course-link 自己另外帶了一個底色（本檔 5546 一帶「矩形
     小卡」設計的白底方卡），這裡也一併蓋成透明，卡片才會是「純框線、
     內容直接貼在頁面底色上」，不是「透明外框裡包一張白卡」兩層底色。 */
.kd-related .course-cell {
    flex-direction: column;
    border: 1px solid hsl(var(--foreground) / .16);
    border-radius: var(--radius-media);
    padding: 1.25rem;
    background: transparent;
}

.kd-related .course-link {
    background: transparent;
}

/* 圓框游標顯示時收掉系統游標——跟首頁 .course-section:has(...) 同一招，
   只是掛在這一頁自己的 .kd-related，不必連 .course-section 一起借。 */
@media (hover: hover) and (pointer: fine) {
    .kd-related:has(.course-cursor.is-on) .course-grid,
    .kd-related:has(.course-cursor.is-on) .course-grid * {
        cursor: none;
    }
}

/* 分類/標籤 pill 與 podcast/影片連結：課程卡本身沒有這兩排，這裡的相關
   文章比課程卡多這些欄位（鐵則第 2 條不能刪），接在 .course-link 卡片
   下面當附加資訊，字級沿用 .pcard-tags／.pcard-links 既有值，只補一段
   跟 .course-place 的距離。 */
.kd-related-tags {
    margin-top: 1rem;
}

.kd-related-links {
    margin-top: .75rem;
    padding-top: 0;
}

/* ═══════════════════════════════════════════════════════════════════════
   相關文章小卡：一張一張的線框改「上下軌線＋卡側直線」（2026-08-21 客戶）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「最底下的相關文章小卡不要個別框線，改成該區塊兩條頁面寬的水平線
   上下連，個別小卡兩條垂直線，跟首頁的實體課程區塊呈現方式一樣」，並附
   截圖確認要的正是首頁 .course-section 那組軌道線（本檔 24346 一帶，
   2026-08-19 定案）——當時的註解特別寫「知識單篇相關文章沿用 .course-grid
   系 class 但沒有這個 section 包裹，維持原本樣式」，這次補上同一招，只是
   外層 class 換成 .kd-related：
     ・上下軌線＝ .course-grid（捲動容器）自己的 border，左右負 margin
       出血到頁面整寬、同值 padding 補回卡片原本位置。實測
       （getBoundingClientRect）.kd-related 所在的 .section-bleed-inner
       本身零內距，出血量全部來自 .page-block 的 padding-inline（本檔
       15343 一帶，72px＝--section-x）——跟首頁「單層 .section-bleed-inner
       內距」結構不同，但數字剛好一樣是一層 --section-x，抵銷算法沿用
       首頁同一套、不必加倍。
     ・卡側直線＝ .course-cell 的左右 border，長度天生等於小卡高度。
     ・線色沿用首頁同一杯 hsl(var(--foreground)/.16) hairline，不另外調色。
   ⚠ .course-cell 原本的四邊細框＋圓角（本檔 19619 一帶，2026-08-13 加的
     「框住整張卡」設計）改成只留左右 border、圓角歸零——沒有四邊框可圓；
     內距（1.25rem）不動，文字／標籤／連結三段原本就靠它跟外框保持距離，
     現在改跟新的左右分隔線保持距離，作用一樣不必重調。 */
.kd-related .course-grid {
    margin-inline: calc(-1 * var(--section-x));
    padding-inline: var(--section-x);
    scroll-padding-inline: var(--section-x);
    border-block: 1px solid hsl(var(--foreground) / .16);
}

.kd-related .course-cell {
    border: 0;
    border-inline: 1px solid hsl(var(--foreground) / .16);
    border-radius: 0;
}

/* 相關文章卡片圖片滿版（2026-08-21 客戶「圖片填滿左右上下不留邊」）─────
   .course-cell 的 1.25rem 內距（本節上面「相關文章小卡：框住整張卡」
   2026-08-13 加的）本來是給文字用的，但套在整張卡上時圖片也一起被往內縮
   出一圈留白（實測左右各 21px、上緣 20px）。圖片下緣本來就跟日期文字
   零間距（.course-date 的呼吸空間是它自己的 padding-top，不是外距，見
   本檔 4297 一帶），所以只需要往上、往左右用等值負外距頂出去貼齊卡片
   邊緣，下緣不用動。 */
.kd-related .course-thumb {
    margin: -1.25rem -1.25rem 0;
}

/* ═══════════════════════════════════════════════════════════════════════
   相關文章區塊改跟文章標題同一條左邊界（2026-08-24 客戶「小螢幕寬1120
   左右，相關文章小卡標題跟按鈕沒對靠左齊跑版了」）
   ═══════════════════════════════════════════════════════════════════════
   實測（getBoundingClientRect）：不是 1120 特有的問題，992～1600 每個
   桌機寬度「相關文章」標題／卡片的左緣都跟文章自己的 H1（.page-title）
   對不齊，落差 24～56px，1120 剛好是客戶那次檢查的寬度，不是唯一出事
   的寬度。
   根因：.kd-related 目前的水平內距繼承自 .page--article .band--grey
   .page-block 那條共用規則（本檔 ≈15343，72px＝--section-x，給滿版色帶
   用的通版留白），但文章標題 .kd-hero-head 用的是內縮更深的
   --article-x（本檔 ≈19599，客戶當時明講「知識專區的標題...離左邊距離
   請跟實體課程課程單頁一樣」）——兩層留白系統，卡片跟著錯誤的那一層。
   ⚠ 不是新問題、不是這次軌道線改版造成的：把 .course-cell 換回舊版
     四邊框線（git 歷史）一樣量得到這個落差，只是舊版每張卡自己有框、
     視覺上比較不容易注意到跟標題沒對齊；這次改成通欄的軌道線之後，
     卡片變成貼齊左邊界的一整條線，落差反而顯眼起來。
   修法：比照 .kd-hero-head 已經在用的 max(0,calc(article_x−section_x))
   寫法（本檔 ≈19599、19092、19128 等處同一套），這裡等價寫成
   max(--section-x, --article-x)（兩者算出來的結果相同，這樣寫更直接）：
   ・.kd-related 本身：內距從 --section-x 改成這個新值，H2「相關文章」
     與 .course-grid 的內容起點跟著移到 --article-x，跟標題對齊。
   ・.course-grid 的出血抵銷算法要同步換成同一個新值——它的
     margin-inline 是「抵銷父層 .kd-related 目前的內距」，父層內距變了，
     抵銷量不跟著換，上下軌線就會少出血一截、貼不到真正的視窗邊緣。
   ⚠ 選擇器用 .page--article .band--grey .page-block.kd-related（(0,4,0)）
     蓋過本檔 ≈15343 那條 (0,3,0) 的 .page-block 規則，跟本檔其餘同類型
     specificity 修正做法一致。
   ⚠ 全寬度統一套用，不是只鎖 992–1199：實測落差在所有桌機寬度都存在
     （見上），只鎖 1120 附近會讓 1440／1600 那組繼續跑版，客戶只是還沒
     檢查到那幾個寬度，不代表那裡是對的。 */
.page--article .band--grey .page-block.kd-related {
    padding-inline: max(var(--section-x), var(--article-x));
}

.kd-related .course-grid {
    margin-inline: calc(-1 * max(var(--section-x), var(--article-x)));
    padding-inline: max(var(--section-x), var(--article-x));
    scroll-padding-inline: max(var(--section-x), var(--article-x));
}

/* ⚠ 2026-08-24 客戶追加「手機寬度也跑版」——上面那組 max(section_x,
   article_x) 公式在手機失靈，實測（getBoundingClientRect）落差沒有消失、
   反而變號（相關文章比標題更縮進 8～37px，桌機是相反方向）。
   根因：.kd-hero-head（文章標題的外層）在 ≤767.98px 有一條獨立規則把它
   自己的 padding-inline 直接歸零（本檔 ≈19680 一帶「手機斷點...改回」
   那節），手機版標題的內距**只靠**外層 .kd-hero-wrap 的 --section-x，
   完全不疊加 --article-x 那一層深縮排——跟桌機版「wrap 給 section_x、
   head 自己再加 (article_x−section_x)」是兩種不同公式，不是同一條在
   不同寬度算出不同數字。上面 max(section_x,article_x) 那組是照桌機的
   公式寫的，套到手機自然對不上。
   修法：手機寬度整組退回原本的 --section-x（跟 .kd-hero-head 在這個
   斷點的行為一致），不要跟著桌機公式硬套 article_x。 */
@media (max-width: 767.98px) {
    .page--article .band--grey .page-block.kd-related {
        padding-inline: var(--section-x);
    }

    .kd-related .course-grid {
        margin-inline: calc(-1 * var(--section-x));
        padding-inline: var(--section-x);
        scroll-padding-inline: var(--section-x);
    }
}

/* ── 標題欄改成對齊分類/標籤 pill 裡的文字（2026-08-24 客戶附截圖＋來回
   確認方向：不是框移動，是標題那一欄移動）──────────────────────────────
   實測（getBoundingClientRect）：框本身（border-box）左緣本來就跟標題
   對齊，問題是 .tag-pill 共用樣式（本檔 384 行）有 padding:0 1.15rem＋
   border:1px，框裡的文字因此比框自己再縮進 19.4px（=18.4+1）——視覺上
   「職場關係」四個字比「談加薪...」標題文字凹進去一截。
   ⚠ 這裡是第二版做法：第一版（同日稍早）移動的是框本身（讓框凸出、框裡
   文字對齊標題），客戶來回確認後推翻，改成反過來——框留在原位不動，
   標題／日期／子標／箭頭那一整欄改往左移，去對齊框裡文字原本的位置。
   ⚠ 移動範圍**不含縮圖**：踩過的坑——若直接對 .course-link（縮圖／日期／
   標題／子標共同的父層）整層加 margin-left，會連縮圖一起拖走，而縮圖
   自己已經有 margin:-1.25rem -1.25rem 0（本檔 19811 一帶，做滿版出血
   用），兩層負外距疊加實測會讓縮圖溢出卡片左邊界 18px（在單張裁切
   截圖裡看不出來，量 getBoundingClientRect 才發現，肉眼要在完整一整排
   的情境下才勉強看得出違和）。改成只挑日期／標題／子標／箭頭四個元素
   個別移動，縮圖完全不碰，維持它原本貼齊卡片邊緣的滿版效果。
   ⚠ 箭頭（.course-link::after）是共用選擇器（本檔 5324 行，跟 .btn-pill／
   .footer-submit 共用同一顆箭頭符號定義），這裡另外用 .kd-related 限定
   路徑覆寫 margin-left，不動共用定義本身。
   ⚠ 只鎖 .kd-related：.tag-pill／.pcard-tags 是全站共用元件（知識列表、
   電子報列表、搜尋結果、我的收藏都在用），這個「框比文字凸出一截」的
   問題在那些地方沒人反映過，不確定是不是也算問題，不擴大範圍去動共用
   樣式本身。 */
.kd-related .course-date,
.kd-related .course-title,
.kd-related .course-place {
    margin-left: calc(-1.15rem - 1px);
}

.kd-related .course-link::after {
    margin-left: calc(-1.15rem - 1px);
}

/* ⚠ 2026-08-13 客戶「該頁面背景除了footer統一壓這個灰色底」——標頭／文章
   本體／相關文章這三段原本是白／灰交替的色帶節奏（.page--v2 .band 預設
   白，.band--grey 交替成 --surface-grey，本檔 14116 一帶），這一頁改成
   三段都用同一個灰，不要交替。
   ⚠ 選擇器加 .page--article 限定，不是只鎖這一頁：.page--article 是
     共用的頁面類型 class（content_generic.php 的電子報內頁、正在改版的
     服務內頁都掛同一個），這三段規則會套用到**所有**掛 .page--article
     的頁面，不是只有知識專區這一篇。掛 .page--v2 但不是 .page--article
     的頁面（首頁、實體課程列表…）不受影響，維持原本白／灰交替；
     footer 不在 .band 底下，不受這裡影響，維持原樣。
   ⚠ 顏色用 --background——客戶附的灰色色票量出來就是這一支既有色票的
     值（跟 <body> 預設背景同一個），不是新色票；.band 與 .band--grey
     兩條都要蓋，否則相關文章那一條還是舊的 --surface-grey，跟前兩段對
     不起來。
   ⚠ 選擇器都是 (0,2,0)，跟第 20 節①的 .page--v2 .band／.band--grey
     同一階特異度，靠檔案位置在後面（本節）贏過去。 */
.page--article .band {
    background: hsl(var(--background));
}

.page--article .band--grey {
    background: hsl(var(--background));
}

/* ⚠ 2026-08-13 客戶再指定「壓底請首頁/知識專區/這一行也壓過去」——麵包屑
   （首頁 / 知識專區 / 文章標題）不在 .page 裡面，是版面共用的 <nav
   class="wf-crumb">，跟 <main> 是同一層的兄弟元素（layouts/main.php），
   套用上面 .page--article 的後代選擇器蓋不到它。麵包屑本身沒有背景色
   （見 .wf-crumb 沒有專屬規則），會透出再上一層 .wf-frame 的白底
   （全站共用，機身包住 header／麵包屑／main／footer），所以麵包屑那一列
   目前是白的，跟下面改成灰的標頭中間會有一條白色接縫。
   ⚠ 用 :has() 只鎖定「這一頁」：.kd-hero 是本檔獨有的 class（本節開頭已
     說明），其餘 .page--article 頁面（服務內頁、電子報內頁…）的麵包屑
     不受影響，一個像素都不會變。 */
body:has(.kd-hero) .wf-crumb {
    background: hsl(var(--background));
}

/* ───────────────────────────────────────────────────────────────────────
   48. 知識專區列表：小卡改成細框描邊、不填色
   ───────────────────────────────────────────────────────────────────────
   客戶「http://onedaylisten.mtest/wireframe/knowledge 這頁面的小卡統一改成
   細框描邊不填色」——跟相關文章那組 .course-cell 的處理方向一致（見上面
   第 47 節），這裡是同一個視覺語言套到列表頁自己的 .pcard。
   ⚠ 只在這一頁的 .kz-grid 生效，不動 .pcard 共用元件本身——它同時服務
     課程／知識／電子報／搜尋／我的收藏／相關文章六個地方（本檔開頭
     「4. 內頁卡片 .pcard」一節），直接改 .pcard 會連著另外五個地方一起變。
   ⚠ 選擇器要蓋過兩條先掛的規則：
       .page--v2 .band .pcard（3 個 class，灰卡壓白帶的底色）
       .page--v2 .band .pcard-grid > li:nth-child(odd/even) .pcard:hover
         （6 個 class，奇偶 hover 變色）
     這裡用 .page--knowledge 換掉 .page--v2、加上 .pcard-grid 或 .kz-grid，
     特異度打平或超過，靠檔案位置在後面贏過去——跟本檔其餘同類型的
     specificity 修正（.kd-related 那一節）同一個做法。 */
.page--knowledge .kz-grid .pcard {
    background: transparent;
    border: 1px solid hsl(var(--foreground) / .16);
}

@media (hover: hover) {
    .page--knowledge .pcard-grid.kz-grid > li:nth-child(odd) .pcard:hover,
    .page--knowledge .pcard-grid.kz-grid > li:nth-child(even) .pcard:hover {
        background: transparent;
    }
}

/* ───────────────────────────────────────────────────────────────────────
   48-1. 知識專區列表：小卡改成貼齊＋分隔線（2026-08-18 客戶指定，第二輪，
   附參考圖：無縫相片格，卡與卡之間只留一條分隔線，不留間距）
   ───────────────────────────────────────────────────────────────────────
   客戶：「小卡樣式改成圖片這樣直接畫線分，讓每個小卡不要有空間」。
   ⚠ 疊在上面第 48 節之上，不改第 48 節那條規則本身（歷史留著）——用
     同一個選擇器 .page--knowledge .kz-grid .pcard 在檔案後面再宣告一次，
     靠來源順序蓋過去，跟第 48 節自己註記的技法（specificity 修正）同一套。
   ⚠ 卡片本來就有 border: 1px solid（第 48 節），這裡把 gap 收 0、
     四邊描邊收成只留右邊／下邊：貼齊之後如果四邊都留著，鄰卡交界處會
     疊成雙倍粗細；只留右／下，鄰卡剛好接成一條線，不會加倍。圓角同時
     收掉——貼齊時圓角會在接縫處露出三角形缺口。
   ⚠ 外圍的上邊／左邊沒有鄰卡可以借邊，改由 .kz-grid 容器自己補一條，
     整塊格線才會封起來、不缺一圈；右邊／下邊則是最後一欄／最後一列的
     卡片自己就有（不需要再另外收邊），比 .svc-tile 那組 nth-child
     收邊的寫法更省一層。
   ⚠ 2026-08-18 回報修正「右邊小卡有一個沒填滿」：成因是上面「②文章拼貼」
     那節的 align-items:start——同一列兩張卡片，標題字數不同（換不換行）
     會讓卡高不一樣，之前有間距＋各自獨立描邊時看不出來，貼齊分隔線之後
     矮的那張下面就露出一截沒有邊框的空白，像沒填滿。改成 stretch，
     兩張卡跟著同列最高的那張撐滿（.pcard 本身已經是 height:100%，
     第 4 節「內頁卡片」共用元件那條，這裡不用再加），格線才會每列都對齊。 */
.page--knowledge .kz-grid {
    gap: 0;
    align-items: stretch;
    border-top: 1px solid hsl(var(--foreground) / .16);
    border-left: 1px solid hsl(var(--foreground) / .16);
}

.page--knowledge .kz-grid .pcard {
    border-radius: 0;
    border-top: 0;
    border-left: 0;
}

/* 2026-08-19 客戶「小卡跟小卡之間留空間」——上面這兩條是 08-18「無縫
   相片格」桌機決定（gap:0＋描邊接成分隔線，卡與卡刻意貼齊不留間距），
   套到手機橫向捲動輪播上不成立：輪播的每一張卡本來就要靠間距讓使用者
   分得出「這張」跟「下一張露出的角」，貼齊描邊在單排橫向捲動的情境
   反而看起來像卡片破圖、缺一塊邊框，不是分隔線。這裡不動桌機決定本身
   （疊一條手機專屬、更晚出現的規則蓋過去，同一招跟前面 .pcard-title
   22px 那條一樣），gap 用回卡牆容器那段（≈14887）原本想要但被這裡蓋掉
   的 1.25rem；描邊因為卡與卡不再貼齊、沒有縫可以借邊，直接拿掉，不留
   單邊懸空的框線。 */
/* 2026-08-19 客戶「水平跟垂直線請加回來，一張小卡兩張垂直線，總小卡
   上下水平個一條」——上面拿掉描邊之後補回來，但不是照抄桌機那組
   （桌機是卡片貼齊、靠共用一條邊當分隔線；手機現在卡與卡之間有 gap，
   沒有縫可以借邊）：改成每張卡自己左右各一條完整的垂直線（框住單張
   卡，不是跟鄰卡共用一條），水平線改掛在 .kz-grid 容器本身的上下緣
   （卡牆整排只有頂與底各一條，不是每張卡自己有上下線）——精確對應
   客戶講的「一張小卡兩張垂直線」（卡自己的左右兩邊）＋「總小卡上下
   水平個一條」（容器層級，管整排，不是卡層級）兩種不同的計數方式。
   顏色沿用桌機那組同一個 token（hsl(var(--foreground)/.16)），只是
   換了掛的位置，視覺上還是同一套線色。

   2026-08-21 斷點延伸 767.98→1199.98：跟上面 15251 一帶「橫向輪播容器」
   同一輪修補（「箭頭請讓他真的能切換」），這裡管的是輪播模式下的分隔線
   外觀——沒有一起延伸的話，992–1199 雖然靠 15251 那條變成 flex 卡片列，
   但線條還是桌機那組 gap:0＋border-top／border-left（本檔≈19866，給
   無縫貼齊的格線用），卡與卡會貼死看不出「下一張」露角，跟真手機（
   ≤767.98）長得不一樣。 */
@media (max-width: 1023.98px) {
    .page--knowledge .kz-grid {
        gap: 1.25rem;
        border-top: 1px solid hsl(var(--foreground) / .16);
        border-bottom: 1px solid hsl(var(--foreground) / .16);
        border-left: 0;
    }

    .page--knowledge .kz-grid .pcard {
        border: 0;
        border-left: 1px solid hsl(var(--foreground) / .16);
        border-right: 1px solid hsl(var(--foreground) / .16);
    }
}

/* 知識專區列表：桌機（≥1200，網格模式）分頁箭頭也要真的能切換
   （2026-08-21 客戶「電腦寬度時也要有功能」）─────────────────────────────
   桌機不是輪播（.kz-grid 是會換行的 grid，一次全部卡片都在畫面上，沒有
   「可以往右捲」這件事），所以桌機的「換頁」是真的分頁：把篩選後的卡片
   依目前欄數切成一批一批，每次只顯示一批，箭頭切下一批／上一批。跟
   knowledge_list.php 那支 IIFE 配對——JS 用這顆 class 標記「篩選有過關
   但不在本頁」的卡，跟篩選本身用的原生 hidden 屬性分開管（不會互相蓋掉：
   filter 那支只碰 hidden，分頁這支只碰這顆 class，兩層各自獨立、疊加
   生效才是最終看不看得到）。 */
.page--knowledge .kz-grid [data-wfcard].kz-page-hidden {
    display: none;
}

/* ───────────────────────────────────────────────────────────────────────
   49. 服務內頁（顧問案／AI 分身）：改成知識專區單欄文章版型
       （2026-08-13 客戶「服務專區 顧問案 ai分身內頁都改成知識專區的
       內頁樣式」，選擇「整頁改成單欄文章版型」）
   ───────────────────────────────────────────────────────────────────────
   跟第 47 節（知識專區單篇）同一套版面邏輯：標頭一列（麵包屑在最上面，
   短橫線＋英文小標＋H1＋副標＋CTA 在下面）、主視覺滿版不出血且跟內文
   同寬置中、文章本體用共用的 .article／.page-narrow／.article-col。
   ⚠ 選擇器另開 .sv-hero-*（不是重用 .kd-hero-*）：兩頁標頭結構不同
     （這裡多了 CTA 按鈕、少了分類標籤／日期），版型類比、數值抄一致，
     但不是同一份標記，共用 class 反而會讓「只有這一頁改」的批注失真。
   ⚠ 只有這一頁改：選擇器都掛 .sv-hero，只有 service_page_simpla.php
     會輸出這個 class（現在只服務顧問案／AI 分身兩頁），其餘走
     service_page.php 內聯版型的四個服務單元一個像素都不受影響。
   ⚠ 背景灰、.article > h2 底線、.article-col／.page-narrow 的內縮與行長
     這幾件事**不需要**在這裡重寫——本頁的 .page 掛了 .page--article，
     直接繼承第 47 節末尾「客戶再指定」那幾條全站 .page--article 規則
     （本檔 17369 一帶）與最開頭的 .article 共用樣式，SSOT，不重複。 */
.page--article .section-bleed-inner.sv-hero-wrap {
    padding-inline: var(--section-x);
}

/* 2026-08-13 客戶「這種 首頁/知識專區的全改在統一高度統一位置」——
   跟第 44 節的 .cx-hero 同一個問題：這個色帶（.sv-hero）沒有自己的
   padding-top，整條吃 .band 的預設 --v2-band-y（.page--v2 版本，桌機
   76px），是四頁 .page-hero-crumb 裡並列最遠的一組，跟 layout 層
   .wf-crumb 的 40px 對不上。選擇器沿用本節既有的「只掛 .sv-hero」
   慣例（只有顧問案／AI 分身兩頁會出這個 class），蓋掉 .band 的預設，
   改成跟 .wf-crumb 同一顆 clamp(1.5rem, 3vw, 2.5rem)。.band 原本的
   下內距不動；.sv-hero-head 自己的 margin-top: 300px（麵包屑之後、
   標題之前的留白）也不動，只有麵包屑距離 Header 的這一段變窄。 */
.sv-hero {
    padding-top: clamp(1.5rem, 3vw, 2.5rem);
}

.sv-hero-head {
    padding-inline: max(0px, calc(var(--article-x) - var(--section-x)));
    margin-top: 300px;
    margin-bottom: var(--v2-band-y);
}

/* ⚠ 2026-08-13 客戶「AI分身跟顧問案分頁預約需求按鈕底部直接貼到圖片了，
   請留空間」——這裡的 .sv-hero-head 同時掛了共用的 .page-head class，
   第 36 節那條 .page--article .simpla-open .page-head { margin-bottom: 0 }
   （給知識專區單篇 .kd-hero-titles 裡「內層 .page-head 不能跟外層
   .kd-hero-head 疊出兩份留白」用，3 個 class）也打中了這裡——但這一頁
   的標頭本身就是 .sv-hero-head，不是像知識專區那樣另有一層外層容器
   扛留白，margin-bottom 被歸零之後按鈕（.page-hero-actions，標頭最後
   一個可見元素）就直接貼上下面的 .sv-hero-media。跟上面同一顆 1-class
   規則打對台，湊到一樣 3 個 class（.page--article .simpla-open）、
   放在後面用源碼順序贏過去，不動第 36 節那條共用規則本身。 */
.page--article .simpla-open .sv-hero-head {
    margin-bottom: var(--v2-band-y);
}

.sv-hero-head .page-title {
    margin: 0;
}

.sv-hero-head .page-lead {
    margin-top: 1.25rem;
}

/* 主視覺：滿版但不出血，寬度跟內文（.page-narrow，54rem）同寬並置中——
   跟第 47 節 .kd-hero-media 同一個決定，數值也相同（54rem 兩處要同步改，
   理由見該節說明）。上下留白用 --v2-band-y，上方是自然的 margin-bottom
   （.sv-hero-head 已經給了），下方用負 margin 收掉重複的色帶內距。 */
.sv-hero-media {
    max-width: 54rem;
    margin: 0 auto calc(var(--v2-band-y) * -1);
}

.sv-hero-media picture,
.sv-hero-media img {
    display: block;
    width: 100%;
    height: auto;
}

@media (max-width: 767.98px) {
    .sv-hero-head {
        padding-inline: 0;
        margin-top: clamp(2.5rem, 12vh, 6rem);
    }
}

/* ───────────────────────────────────────────────────────────────────────
   50. 搜尋／電子報列表：背景與卡片改成跟其他頁面一致
       （2026-08-13 客戶「搜尋跟電子報頁面樣式也請調整跟其他頁面風格一致」，
       隨後補充「電子報單頁文章風格不用改」——只調整電子報「列表」頁
       .page--newsletter；電子報「內文」單篇走 content_generic.php，
       已經是 .page--article，本來就跟知識專區單篇同一套，不用再動）
   ───────────────────────────────────────────────────────────────────────
   跟第 47 節末尾（知識專區單篇）、第 48 節（知識專區列表）同一組調整：
     ① 白／灰交替的色帶改成統一一個灰（--background）
     ② 麵包屑背景一起壓灰，跟內容無縫
     ③ .pcard 卡片拿掉白底填色，改細框描邊
   ⚠ 只在這兩個頁面類型生效：.page--search（搜尋結果）／.page--newsletter
     （電子報列表）；其餘頁面一個像素都不受影響。 */
.page--search .band,
.page--newsletter .band {
    background: hsl(var(--background));
}

.page--search .band--grey,
.page--newsletter .band--grey {
    background: hsl(var(--background));
}

body:has(.page--search) .wf-crumb,
body:has(.page--newsletter) .wf-crumb {
    background: hsl(var(--background));
}

/* 搜尋結果卡片：.result-list（不是 .pcard-grid），沒有奇偶 hover 變色
   規則需要蓋，只要處理底色與描邊。 */
.page--search .result-list .pcard {
    background: transparent;
    border: 1px solid hsl(var(--foreground) / .16);
}

/* 電子報列表卡片：.pcard-grid，套用跟第 20 節①「色帶上的課程卡」同一條
   奇偶 hover 變色規則，要用同樣的選擇器路徑＋足夠的特異度蓋掉。
   ⚠ .page--v2.page--newsletter 兩個 class 疊在同一個元素上（本來就是
     這樣，見 newsletter_list.php 的 <div class="page page--v2 ...
     page--newsletter">），特異度算兩個 class，不是一個。
   ⚠ 2026-08-20 客戶把卡片邊框改成「線稿分隔」，border 改由後面
     .page--newsletter .pcard（「卡片列表改用線稿分隔」那節）統一管——
     這裡的特異度（5 個 class）原本比那節高，border 若繼續留在這裡會
     整組蓋過去（shorthand 一次動全部 4 邊），兩處各改各的會打架，
     所以拿掉，只留這裡本來就要的透明底（跟後面那節的值一致，不衝突）。 */
.page--v2.page--newsletter .band .pcard-grid .pcard {
    background: transparent;
}

@media (hover: hover) {
    .page--v2.page--newsletter .band .pcard-grid > li:nth-child(odd) .pcard:hover,
    .page--v2.page--newsletter .band .pcard-grid > li:nth-child(even) .pcard:hover {
        background: transparent;
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   內頁頁首色帶延伸到視窗最上緣（2026-08-14 客戶新指令，全站）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「header 原先的高刪除，讓每個頁面的上面不要再空一塊舊的 header
   高度。」——接續同日「Header 改回透明覆蓋樣式」：header 已經全站透明疊在
   頁面上，但內頁還各自留著一段 56px 的讓位空間（桌機是 .wf-frame 的
   padding-top，手機是 position:sticky 的 header 本身佔著版面流），於是每頁
   頂端都空出一條舊 header 高度的頁面底色。首頁 .hero 已在「Hero：editorial
   grid 版型」那段處理掉桌機的部分，這裡把同一件事補到其餘內頁與手機。

   做法＝2026-08-13「選單展開時色帶補上 header 空隙」那組已驗證過的手勢，
   改成**常態、全斷點**生效：頁首色帶用負 margin 把自己往上拉一個 header
   高、再用 padding 補回同樣的量——色帶背景（含 .cx-hero-bg 這種絕對定位的
   滿版圖）往上長到視窗最上緣，色帶**裡面的內容位置一個 px 都不動**。

   ⚠ 為什麼不是把 .wf-frame 的 padding-top 歸零：
     ① 那條是「沒有 Hero 的內頁」的讓位機制（本檔「0. 版面基礎」），
       member-login／forgot 三頁／member-center／privacy 這 6 頁沒有頁首
       色帶、整頁淺底，仍然需要它讓內容避開透明 header 的 logo 與導覽，
       歸零會讓那幾頁的標題鑽到 logo 底下；有色帶的頁面才需要「背景上去、
       內容不動」，兩者要的東西不一樣。
     ② 本檔慣例只加不改——舊規則留著，這裡用色帶自己的負 margin 抵銷。
   ⚠ padding 補償必須是「該色帶自己原本的值 ＋ --header-h」，**不能**學
     08-13 那組直接蓋成固定 57px——那會把色帶原本的上內距吃掉，內容整組
     上吸貼到 header 底下。三組原值的權威出處（改那邊記得回來同步這裡）：
       .band 基準       var(--v2-band-y)            （見「.band」padding-block）
       .cx-hero／.sv-hero clamp(1.5rem, 3vw, 2.5rem) （見 .page--wall .cx-hero
                                                       與 .sv-hero 各自的宣告）
       .cd-hero／.page-hero 0                        （padding-block: 0，內距
                                                       在它們的子層）
   ⚠ :first-child 是必要的限縮：.band 是 v2 通用的色帶 class，頁面中段還有
     很多條（課程內頁一頁就有 8 條）——只有「.page 的第一個子元素」那條是
     頁首色帶，其他一條都不能動。
   ⚠ 特異度刻意做到 4 個 class 級（.is-designed .page .band :first-child）：
     要贏過 08-13 那組 .has-menu-open.has-menu-open ×色帶（3 個 class）。
     蓋過它是**刻意的**：常態就拉上去之後，「選單展開才拉」的特例已經被
     涵蓋，而且展開選單時內容不再跳動（以前展開瞬間色帶內距會從原值縮成
     57px）。08-13 那組留著當歷史，不刪。
   ⚠ ±1px 縫隙緩衝沿用 08-13 的結論：客戶裝置的螢幕縮放會讓整數 calc 在
     畫面上留下不到 1px 的縫，多拉 1px 藏進視窗外，寧可多不可漏。 */
.is-designed .page > .band:first-child,
.is-designed .page > .page-hero:first-child {
    margin-top: calc(-1 * var(--header-h) - 1px);
}

/* 內距補償——依「誰的 padding-top 本來就是贏家」分三組，由通用到特定排序，
   後兩組用第 5 個 class（色帶自己的名字）蓋過第一組。 */
.is-designed .page > .band:first-child {
    padding-top: calc(var(--v2-band-y) + var(--header-h) + 1px);
}

.is-designed .page > .band.cx-hero:first-child,
.is-designed .page > .band.sv-hero:first-child {
    padding-top: calc(clamp(1.5rem, 3vw, 2.5rem) + var(--header-h) + 1px);
}

.is-designed .page > .page-hero:first-child {
    padding-top: calc(var(--header-h) + 1px);
}

/* ⚠ 2026-08-14 客戶「你看 空一塊」「header的空間會讓圖片上方無法貼邊」
   「跟圖片在同一列」——上面這組「背景上去、內容不動」的內距補償是加在
   .cd-hero 這個外層色帶上，.cd-hero-text（文字）與 .cd-hero-media
   （圖片）都是它的子層，兩者一起被推低，圖片因此也被文字才需要的讓位
   空間卡住，貼不到視窗頂端（實測 1920 寬時圖片要到 y≈226px 才開始）。
   改成這裡歸零（cd-hero 不再整條色帶一起讓位），讓位量改加在
   .cd-hero-text 自己身上（見該規則），圖片不吃這筆內距，拿掉外層
   補償之後自然貼到 .cd-hero-inner 自己的呼吸內距（很小，不是整個
   header-h），視覺上等於跟 Header 貼齊同一列。
   ⚠ 特異度刻意跟原本那條一樣是 5 個 class：要贏過上面「內距補償」
     通用那組 .band:first-child（4 個 class，會把 cd-hero 這個 .band
     一起算進去，補償值卻是 cd-hero 不適用的公式），也要贏過同一個
     選擇器本身（源碼順序更後面才會贏，這裡故意排在它後面）。 */
.is-designed .page > .band.cd-hero:first-child {
    padding-top: 0;
}

/* ═══════════════════════════════════════════════════════════════════════
   有麵包屑的頁面：拉頂量要多扣掉 .wf-crumb 自己的高度（2026-08-25 客戶
   四張截圖回報「色帶灰底本來延伸到 header，現在中間空一截」）
   ═══════════════════════════════════════════════════════════════════════
   下面「麵包屑復出後」那組（.wf-crumb { position:relative; z-index:2 }）
   解決的是「麵包屑文字讀不到」，沒解決「色帶背景的負 margin 撐不到視窗
   頂端」：.wf-crumb 疊層雖然疊在色帶前面，但它自己仍然佔用一段真正的
   文件流高度（padding-top clamp(1.5rem,3vw,2.5rem) ＋ 一行文字），色帶
   的負 margin 只能把自己拉到「緊接在 .wf-crumb 後面」的位置，撐不到
   視窗頂端——header 底下實際多出一截空白，不是客戶要的「色帶延伸到
   header、讓 header 蓋上去」（實測 service/consulting／courses／
   courses/detail／knowledge/detail／newsletter 五頁都是這個落差）。
   ⚠ 不動 .wf-crumb 本身（不改 position:absolute 抽出文件流——那會連動
     重算它的水平置中與更多既有規則，牽動面太大）。改成「有麵包屑」的
     頁面，色帶負 margin／內距補償都多加一個 --wf-crumb-total（＝麵包屑
     在文件流裡實際佔用的總高度）。
   ⚠ --wf-crumb-total 有兩筆，缺一不可（第一版只算了後者，實測色帶頂緣
     停在 y=30 而不是 0，差的就是前者）：
       ① header 讓位差額 `var(--header-real-h) - var(--header-h)`：跟
         .wf-crumb 自己的 margin-top（見本檔 ≈14066）**必須是同一個算式**，
         兩邊對不上色帶就會多拉或少拉。這一項會隨寬度／字級浮動（實測
         ≥1468 一般頁 +17、992–1467 +7、≤991 0、course_detail ≥1468
         **−118**），所以只能讀變數不能寫死。
         2026-08-25 從 `var(--header-clearance-extra, 0px)` 換成這個減法，
         原因（單向修正救不了 course_detail 那個負值）見 ≈14066 的說明。
       ② 麵包屑自己的框高：--wf-crumb-pad（下面定義的內距）＋一行文字
         （--text-meta × --lh-meta）。內距抽成變數就是為了讓
         .is-designed .wf-crumb（本檔 ≈11129）跟這裡的拉頂算式**永遠讀
         同一個值**——兩邊各寫一次數字的話，改了一邊沒改另一邊，色帶就
         會多拉或少拉，頂端露出縫或蓋掉一截。
   ⚠ 全部用 body:has(.wf-crumb) 疊一層特異度覆寫，一行都不改上面「內距
     補償」那組既有規則——沒有麵包屑的頁面選擇器對不上，完全不受影響。
   ⚠ .band.cd-hero 走的是另一條路（外層色帶 padding-top 固定 0，讓位
     量改加在 .cd-hero-text 自己身上，見本檔 ≈18840）——這裡疊一條同
     specificity（5 個 class）但源碼順序更後面的 0 覆寫，蓋掉會被
     body:has(...) 那條通用 .band:first-child 規則誤加回去的內距；
     .cd-hero-text 自己的讓位量另外在它原本的規則上補（見該處）。 */
body:has(.wf-crumb) {
    /* 麵包屑文字與 header 底線之間的距離（客戶 2026-08-25「往上移到
       header 的線底下，現在離太遠了」，原本 40px→現在 12–16px）。
       .is-designed .wf-crumb 的 padding-top 讀這一顆，下面的
       --wf-crumb-total 也讀這一顆，永遠同步。 */
    --wf-crumb-pad: clamp(.75rem, 1.5vw, 1rem);

    /* ⚠ 第二項用 site.js 量到的真實框高 var(--wf-crumb-h)，fallback 才是
       「內距＋一行文字」的估算：估算假設麵包屑永遠只有一行，但手機窄寬度
       ＋A+/A++ 放大字級時它會換行（390px A++ 實測框高 79.2px、估算只有
       38.4px），色帶會少拉 40px、頂端露出白帶。 */
    --wf-crumb-total: calc(
        (var(--header-real-h) - var(--header-h))
        + var(--wf-crumb-h, calc(var(--wf-crumb-pad) + (var(--text-meta) * var(--lh-meta))))
    );
}

body:has(.wf-crumb).is-designed .page > .band:first-child,
body:has(.wf-crumb).is-designed .page > .page-hero:first-child {
    margin-top: calc(-1 * (var(--header-h) + var(--wf-crumb-total)) - 1px);
}

body:has(.wf-crumb).is-designed .page > .band:first-child {
    padding-top: calc(var(--v2-band-y) + var(--header-h) + var(--wf-crumb-total) + 1px);
}

body:has(.wf-crumb).is-designed .page > .band.cx-hero:first-child,
body:has(.wf-crumb).is-designed .page > .band.sv-hero:first-child {
    padding-top: calc(clamp(1.5rem, 3vw, 2.5rem) + var(--header-h) + var(--wf-crumb-total) + 1px);
}

body:has(.wf-crumb).is-designed .page > .page-hero:first-child {
    padding-top: calc(var(--header-h) + var(--wf-crumb-total) + 1px);
}

body:has(.wf-crumb).is-designed .page > .band.cd-hero:first-child {
    padding-top: 0;
}

/* ═══════════════════════════════════════════════════════════════════════
   麵包屑復出後：色帶「整區上移」拉頂效果要保留，麵包屑改疊在上面
   （2026-08-25，取代同日稍早「取消拉頂」那版做法）
   ═══════════════════════════════════════════════════════════════════════
   稍早那版做法（把 margin-top/padding-top 還原成沒有拉頂之前的值）雖然
   解決了「色帶蓋住麵包屑文字」，但代價是每一頁色帶都變成不再貼齊視窗
   最上緣、跟 header 之間空出一截白色——客戶隨後回報「知識專區本來灰底
   也延伸到header，改了一個地方其他要跟著變動」、「單頁文章也是灰底
   延伸到heeader」、「實體課程是圖片直接貼邊到header，灰底也是延伸
   上去」，並明確定義「延伸」＝「讓header直接蓋在該頁面上，不用額外
   自己的高」——也就是色帶本來就該「整區上移」貼到視窗最上緣、從
   header 半透明+blur 的縫隙透出來，這是全站既有、麵包屑重新顯示前就
   一直存在的效果（見本檔 ≈20629「內頁頁首色帶延伸到視窗最上緣」），
   不該因為麵包屑復出被犧牲掉。

   所以這裡整組刪掉，改成保留原本的拉頂（不再對 :has(.wf-crumb) 特殊
   處理，色帶跟沒有麵包屑的頁面一樣拉到貼齊視窗頂），另外處理「色帶
   拉頂之後方框會蓋到麵包屑」——見下面 .wf-crumb 疊層那組規則：色帶
   本身沒有 position，疊層順序只看 DOM 順序（.wf-crumb 在 main 前面，
   會被後面的 .page > .band 蓋過去），只要讓 .wf-crumb 具名分出一層
   stacking context 並給一個高於預設（auto）的 z-index，不需要動色帶
   本身，色帶依然整區貼頂、麵包屑文字疊在最上面兩者都要。 */
.wf-crumb {
    position: relative;
    z-index: 2;
}

/* 手機首頁：≤991.98px 的 header 是 wireframe.css 的 position:sticky，
   佔著版面流，Hero 桌機版靠「不吃 .wf-frame padding」自然貼頂的路在這裡
   不存在——比照內頁色帶同一個手勢拉上去。.hero-media 是絕對定位鋪滿
   （inset 還帶負值出血，見「Hero：editorial grid」那段），padding 區也在
   它的覆蓋範圍內，滿版大圖因此自動長到視窗最上緣；模組／頁碼是流內內容，
   被 padding 補償留在原位。
   ⚠ 桌機（≥992）不需要也**不能**套：那裡 header 是 fixed、.wf-frame 對
     :has(.hero) 的頁面本來就沒有 padding-top，Hero 已經貼在 0——再拉會
     把 Hero 頂緣拉出視窗外。 */
@media (max-width: 991.98px) {
    .is-designed main.wf-inner > .hero:first-child {
        margin-top: calc(-1 * var(--header-h) - 1px);
        padding-top: calc(var(--header-h) + 1px);
    }
}

/* ── 同一輪補遺：手機（≤991.98px）header 疊在色帶上的兩個穿幫 ──────────
   色帶上移之前這兩個問題就存在，只是 header 底下是白色頁面底、看不出來；
   色帶一長到 header 背後就現形，跟這一輪是同一件事，一起修。

   ① logo 是白的：.site-logo-icon path 讀 var(--header-ink, 白)，而
      --header-ink 的兩個定義（首頁／其他頁，2026-08-14 起同為黑 70%）都
      寫在 @media (min-width: 992px) 那層裡（見「Header 文字/線條顏色」
      一帶），手機解不到變數、掉進白色後備——疊在白底上是隱形，疊在
      色帶／照片上是一顆半透明幽靈 logo。這裡把同一個值補給手機，
      下游規則（logo fill／字級切換器等）自動跟上，不逐條改。
   ② header 自己的 border-bottom：wireframe.css 的 .wf-header 有一條
      1px var(--wf-hair) 灰線，桌機在 07-31／08-14 兩輪都清成透明了，
      手機的透明化（「Header 改回透明覆蓋樣式」的 ≤991.98px 分支）只清了
      background——現在這條灰線會橫切過長上來的色帶／大圖。
      特異度 3 個 class 已贏過 wireframe.css 那條單一 class，不需要
      !important；.is-compact 時的 border 已由「Header 收合成方形選單鈕」
      那段的 border-bottom:0 !important 處理，這裡維持同一個
      :not(.is-compact) 的分工。 */
@media (max-width: 991.98px) {
    .site-header {
        --header-ink: hsl(var(--heading) / .7);
    }

    .is-designed .site-header:not(.is-compact) {
        border-bottom-color: transparent;
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   Header 重新分層：桌機 ≥1468px 第一層常態精簡化（2026-08-17 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：右上角 2×3 方格導覽（服務專區/實體課程/知識專區/會員/電子報/
   搜尋 + A/A+/A++）元素太多太重，跟 Hero 極簡風格不搭；要求「保留所有
   既有功能與連結，但重新分層呈現」——第一層只留搜尋連結 + 方形 Menu
   按鈕（跟客戶確認過，比原始文字稿的「實體課程/知識專區/關於我們/
   搜尋」更精簡），其餘（服務專區/實體課程/知識專區/會員/電子報/
   AAA 字級）收進 Menu，點方形鈕才展開。

   ⚠ 這是全站共用 partial，這裡的改動全站生效（客戶原話「與其他分頁
     保持一致」），選擇器不加 .is-designed 以外的頁面限定。

   ── 技術做法：把「桌機捲動收合」既有規則從「只在 .is-compact 生效」
   放寬成「≥1468px 無條件生效」──────────────────────────────────────
   今天稍早已經做過「Header 收合成方形選單鈕」（本檔「桌機（≥1468px）
   捲動收合」區塊），只是設計成「捲動離開 Hero 後才觸發」。客戶這次
   要的其實是同一套長相從進頁那一刻就套用，不必等捲動——不是新元件，
   是把既有 `.is-compact` 限定放寬。

   做法不是直接刪掉/改寫「桌機 Header 三段式」（本檔 ≈3065-3481、
   ≈9744-9924，`html:not(.is-compact) .site-header .inner > .site-nav`
   那組 2 欄 3×2 格線展開版面）與「桌機捲動收合」（≈8622-8872，
   `html.is-compact .site-header .inner > .site-nav` 那組抽屜殼）這兩組
   規則本身——沿用本檔「只加不改」慣例，兩組舊規則原樣保留（之後如果
   要復原「捲動才收合」的行為，把這裡整段刪掉即可，不必去別處尋找被
   動過的舊規則），這裡另外疊一組新規則，選擇器刻意用 `html:root` 開頭
   （不是 `html.is-compact` 也不是 `html:not(.is-compact)`）：
     · `html:root` 對任何頁面狀態都成立（不像 `.is-compact`/`:not(.is-
       compact)` 互斥），所以下面這組宣告不論捲動與否都會生效。
     · 特異度算法：`html:root` = 1 個元素選擇器＋1 個 pseudo-class，跟
       `html.is-compact`／`html:not(.is-compact)`（1 個元素選擇器＋1 個
       class）是同一個特異度量級，不會自動贏——靠「寫在檔案最後面、
       特異度打平時後面贏」這個 CSS 疊層規則勝出，不是特異度取巧。
       這個技法本檔已有先例（≈3121-3127「Header 高度改成 56px」那段
       html:root 用法的說明），沿用同一套邏輯，不是這裡新發明。
     · 下面每一條宣告都是從「桌機捲動收合」那組已經上線、已經反覆
       除錯過的規則原樣複製，只換選擇器前綴——不是重新設計一套新樣式，
       降低「看起來像同一顆選單」這件事的風險（客戶原話也明講「若專案
       已經有 Mobile Menu、Drawer 或 Menu Overlay，請直接沿用」）。

   ⚠ 「桌機 Header 三段式」那組舊規則（2 欄 3×2 格線）此後不會再被
     觸發到——原本觸發它的條件是 `:not(.is-compact)`，這個條件在網頁
     一載入、捲動之前就成立，但下面這組新規則用同一個元素、同一批
     屬性、後面寫、特異度打平，逐一蓋過去，視覺上舊規則形同關閉。
     其中一條例外要特別處理：舊版「每個導覽項目自己一條底線」
     （≈3188-3192，`padding-bottom` + `border-bottom`）跟新的抽屜版列
     樣式（`padding` 用完整簡寫、沒有 border）不是同一組屬性名稱，
     `padding-bottom` 會被新的 `padding` 簡寫自然蓋過，但 `border-bottom`
     沒有對應的新宣告可以覆蓋——這裡額外補一條 `border-bottom: 0` 顯式
     清掉，否則抽屜列表的每一項會意外多一條舊版遺留的底線。 */
@media (min-width: 1468px) {
    /* 方形 Menu 按鈕／關閉鈕／搜尋圖示共用的定位幾何——2026-08-17 客戶
       「搜尋icon跟選單icon請對齊中線」之後抽成自訂屬性，三個元件的
       position:fixed 都讀同一份數字，日後只要調這裡一處，三顆鈕的
       尺寸與垂直位置會一起變動、永遠對齊，不會再各自寫一份數字、
       改一顆忘了改另一顆而對不齊。 */
    .site-header {
        --menu-btn-edge: clamp(12px, 2vw, 24px);
        --menu-btn-size: clamp(48px, 4vw, 58px);
    }

    /* ── 第一層：搜尋連結 ─────────────────────────────────────────────
       新增元素，站上原本沒有這個 class，不存在覆寫/相容問題。
       ⚠ 不沿用 .site-search 現有的「膠囊底＋放大鏡 icon」視覺——客戶
         明講「不要使用…膠囊按鈕」，這裡改成單純圖示連結，跟 Menu
         按鈕水平並排、垂直置中。
       ⚠ 2026-08-17 客戶「搜尋請用icon不要文字」——原本是純文字＋
         hover 展開底線（做法抄自本檔既有「每個導覽項目底下一條線」的
         hover 語言，見 ≈3213-3229 那組），改成純圖示後底線語言不再
         適用（沒有文字基線可以貼），改用跟 Menu 按鈕一致的「hover 只做
         非常輕微的透明度變化」（客戶對方形 Menu 按鈕的原始規格），兩顆
         圖示按鈕的互動語言統一。
       ⚠ order 讓它排在 Logo 之後、方形 Menu 鈕之前（DOM 順序已經是
         這樣，這裡明講只是避免日後改 DOM 順序時忘記這個相依關係）。
       ⚠ 2026-08-17 客戶「搜尋icon跟選單icon請對齊中線現在兩個位置
         不一致」——根本原因：方形 Menu 按鈕是 position:fixed（見下面
         .site-nav-toggle，用 top:clamp(...) 錨定，完全脫離 .inner 的
         grid 版面流），但搜尋連結原本還留在 grid 裡、靠 grid 的
         align-items:center 置中——兩者垂直位置的計算基準根本是兩套
         不同機制，數字剛好都跟 header 高度有關但不保證對齊。
         改法：搜尋連結也改成 position:fixed，用跟方形 Menu 鈕完全
         相同的 top／高度算式（見下面 --menu-btn-edge／--menu-btn-size
         兩個新自訂屬性，兩顆鈕與這顆搜尋圖示共用同一份數字），
         right 用 calc() 疊加「Menu 鈕的寬度＋間距」往左推——三者的
         垂直中心點從此是同一條算式算出來的同一個值，不會再各自
         獨立計算出剛好對不齊的兩個數字。 */
    .site-header .inner > .site-nav-primary {
        display: flex;
        align-items: center;
        justify-content: center;
        position: fixed;
        top: var(--menu-btn-edge, clamp(12px, 2vw, 24px));
        right: calc(var(--menu-btn-edge, clamp(12px, 2vw, 24px)) + var(--menu-btn-size, clamp(48px, 4vw, 58px)) + .75rem);
        width: var(--menu-btn-size, clamp(48px, 4vw, 58px));
        height: var(--menu-btn-size, clamp(48px, 4vw, 58px));
        margin: 0;
        color: var(--header-ink, hsl(var(--brand-foreground)));
        text-decoration: none;
        opacity: 1;
        z-index: 70;
        transition: opacity var(--dur-hover) var(--ease-out);
    }

    .site-header .inner > .site-nav-primary:hover,
    .site-header .inner > .site-nav-primary:focus-visible {
        opacity: .6;
    }

    .site-header .inner > .site-nav-toggle {
        order: 2;
    }

    @media (prefers-reduced-motion: reduce) {
        .site-header .inner > .site-nav-primary {
            transition: none;
        }
    }

    /* ── 方形 Menu 按鈕：常態套用（不再只在 .is-compact 時變身）────────
       複製自「桌機捲動收合」既有的 html.is-compact:not(.has-menu-open)
       .site-header .site-nav-toggle（≈8589-8599），選擇器換成
       html:root:not(.has-menu-open)——`.is-compact` 拿掉，
       `:not(.has-menu-open)` 保留（選單開著時要換成下面的 × 關閉鈕，
       兩顆鈕交替顯示，邏輯不變）。
       ⚠ 2026-08-17 客戶先指定「選單icon請用白色三條線不要壓底」——原本
         複製自捲動收合狀態的實色底（background: hsl(var(--card))）拿掉，
         改成透明背景；隨後又指定「選單線條改黑色，四周不要描邊框線」——
         icon 顏色從白改黑（見下面 .site-nav-toggle-bars 那條），邊框
         （border: 1px solid）也拿掉，只剩純圖示浮在透明背景上，沒有
         任何框線或底色。
         ⚠ 淺色背景/捲動狀態下可能看不清楚（黑線疊在也偏深的內容上）
           是已知取捨，客戶目前明確要求「不要描邊框線」，不是漏改。 */
    html:root:not(.has-menu-open) .site-header .site-nav-toggle {
        position: fixed;
        top: var(--menu-btn-edge, clamp(12px, 2vw, 24px));
        right: var(--menu-btn-edge, clamp(12px, 2vw, 24px));
        width: var(--menu-btn-size, clamp(48px, 4vw, 58px));
        height: var(--menu-btn-size, clamp(48px, 4vw, 58px));
        background: transparent;
        border: 0;
        color: hsl(var(--heading));
        z-index: 70;
    }

    /* ⚠ 關閉鈕（×）先前只在 html.is-compact 時強制顯示（≈8829-8844）；
       這裡補一條不看 .is-compact 的版本。display 需要 !important——
       標記上的 .d-lg-none 是 Bootstrap 內建 utility class，編譯死是
       display:none!important，一般特異度贏不了（跟本檔其餘「d-lg-none
       撞牆」的坑同一個成因，見 ≈7945-7954 那段說明）。
       ⚠ 不會在選單關閉時意外冒出來：這顆鈕是 #wfNav 的子節點，
         #wfNav 關閉時是 visibility:hidden，子節點沒有另外宣告
         visibility 會照樣繼承成 hidden——display:inline-flex!important
         只決定「打開時長什麼樣子」，不影響「關起來時看不看得到」這件事
         （visibility 不是 display 管的，两者互不干扰）。 */
    /* ⚠ 2026-08-17 客戶「選單線條改黑色，四周不要描邊框線」——跟上面
       .site-nav-toggle 同一套處理：拿掉 border 與實色底，開關兩顆鈕
       維持同一套視覺語言（客戶原話「兩顆鈕交替顯示」的既有設計）。
       color 本來就是 hsl(var(--heading))（黑），不用改。 */
    html:root .site-nav-close {
        display: inline-flex !important;
        align-items: center;
        justify-content: center;
        position: fixed;
        top: var(--menu-btn-edge, clamp(12px, 2vw, 24px));
        right: var(--menu-btn-edge, clamp(12px, 2vw, 24px));
        width: var(--menu-btn-size, clamp(48px, 4vw, 58px));
        height: var(--menu-btn-size, clamp(48px, 4vw, 58px));
        background: transparent;
        border: 0;
        color: hsl(var(--heading));
        cursor: pointer;
        z-index: 70;
    }

    html:root .site-nav-close span {
        position: relative;
        display: block;
        width: 20px;
        height: 20px;
    }

    html:root .site-nav-close span::before,
    html:root .site-nav-close span::after {
        content: "";
        position: absolute;
        top: 50%;
        left: 0;
        width: 100%;
        height: 1.5px;
        background: currentColor;
    }

    html:root .site-nav-close span::before { transform: rotate(45deg); }
    html:root .site-nav-close span::after  { transform: rotate(-45deg); }

    /* ── Logo 旁邊放回品牌名（2026-08-17 客戶「logo旁邊放品牌名；一天
       聽一點」）────────────────────────────────────────────────────
       2026-08-14「logo放大只保留logo圖品牌名刪掉」把品牌字用 display:
       none 藏起來（≈9799 行），這裡疊一條更晚寫、同特異度的規則蓋回去
       ——不是刪掉舊規則重寫，「只加不改」慣例：舊決定與新決定都留著
       紀錄，之後要復原成「只留圖示」把這條刪掉即可。
       ⚠ 顏色不是重新寫死，是延用 .site-logo-text 基底規則本來就有的
         var(--header-ink, ...)（≈3299 行），跟導覽文字同一套依頁面
         切換的邏輯（首頁黑70%、其他頁黑70%——目前兩branch同值，
         見「Header 文字/線條顏色」那段）。 */
    html:root .site-header .site-logo-text {
        display: inline;
    }

    /* ── #wfNav 面板殼：從上方展開，不是側邊滑入 ─────────────────────
       2026-08-17 客戶「選單展開請從上面展開的形式設計，不要從側面
       展開」——原本（複製自「桌機捲動收合」的側邊抽屜，寬度 min(32%,
       320px)、從右側 translateX 滑入）整段改掉：改成滿版寬度的頂端
       面板，從視窗最上緣往下滑出（translateY），關閉時往上收回去。
       ⚠ 面板本身滿版寬（width:100%），但裡面的清單內容用
         grid-template-columns:minmax(0,420px) + justify-content:end
         收成一欄靠右的閱讀寬度——理由：方形 Menu／關閉鈕固定在右上角
         沒有變（見上面 .site-nav-toggle／.site-nav-close 那組），內容
         欄靠右對齊，視覺上維持「從按鈕那個角落長出來」的連續感，不是
         滿版文字貼著左緣一路排到右緣、失去跟觸發點的關聯。
       ⚠ max-height 而不是固定 height：內容不多時面板只佔它需要的高度，
         不會不管內容多寡都強制蓋滿整個螢幕；內容多時（子選單全部展開）
         用 overflow-y:auto 讓面板自己捲動，不推擠底下的頁面。
       ⚠ 內容樣式（下面「極簡日系風」那組）不受這次改動影響——那是
         2026-08-17 稍早「選單展開樣式請重新設計，要極簡日系風」的
         獨立要求，跟這次的「開合方向」是兩件事，這裡只動殼的定位/
         尺寸/動畫，不重寫內容規則。 */
    html:root .site-header .inner > .site-nav {
        display: grid !important;
        grid-template-columns: minmax(0, 420px);
        justify-content: end;
        align-content: start;
        row-gap: 0;
        position: fixed;
        inset: 0 0 auto 0;
        width: 100%;
        max-height: min(720px, 85vh);
        z-index: 60;
        padding: 3.25rem clamp(1.75rem, 4vw, 4rem) 3rem;
        margin: 0;
        border: 0;
        border-bottom: 1px solid hsl(var(--foreground) / .08);
        background: hsl(var(--brand-foreground));
        overflow-y: auto;
        overscroll-behavior: contain;
        transform: translateY(-100%);
        visibility: hidden;
        transition: transform 320ms var(--ease-out), visibility 0s linear 320ms;
    }

    html:root .site-header .inner > .site-nav.is-open {
        transform: translateY(0);
        visibility: visible;
        transition: transform 320ms var(--ease-out), visibility 0s;
    }

    /* ── 極簡日系風：文字與線條統一系統 ──────────────────────────────
       設計原則（取代原本「圖示＋圓角色塊 hover＋三色 AAA 藥丸」那套）：
         1. 拿掉所有裝飾用圖示（服務/書本/文件/會員/信封）——只留純
            文字，唯一保留的圖形元素是展開子選單用的 chevron（功能性
            指示，不是裝飾，且本身就是細線畫的，跟「線條」語言一致）。
         2. 唯一的分隔語言是 1px 細線（hsl(var(--foreground) / .1)），
            每一列（含子選單項目、AAA 列）下面都畫一條，形成連續、
            規律的清單節奏——不再只有「group-end」那幾項才有線、其餘
            靠 margin 分段的不一致做法。
         3. 拿掉圓角色塊 hover（border-radius/背景色塊）——hover 只用
            文字顏色由 70% 轉全黑，跟全站其餘連結 hover 語言一致
            （見 ≈3213-3229 桌機導覽 hover 那組），不再是「按鈕感」的
            互動語言。
         4. AAA 三顆字級鈕拿掉三色底（膚色／藍／灰）——那組配色是給
            header 第一層小尺寸鈕設計的高對比方案，在這個大留白的
            極簡選單裡顯得突兀。這裡改成跟其餘選單項目同一套純文字
            語言：三顆並排、選中的那顆用底線＋加粗標示，不再靠色塊。 */
    html:root .site-header .site-nav > .dropdown,
    html:root .site-header .site-nav > a {
        margin-bottom: 0;
        border-bottom: 1px solid hsl(var(--foreground) / .1);
    }

    /* ⚠ wireframe.css:92 有一條全域未加條件的舊規則
       `.wf-nav .dropdown{ display:flex; align-items:center; }`——
       `<nav>` 同時掛著 wf-nav／site-nav 兩個 class，這條會命中同一顆
       `.dropdown` 容器 div。這條舊規則原意是給「服務專區」觸發鈕自己
       內部排版用（icon/文字/chevron 同一列），但它包住的是**整個**
       `.dropdown` 容器（觸發鈕 `<a>` ＋ 子選單 `<ul>` 兩者的共同父層），
       flex 預設 row 方向會讓 `<a>` 跟 `<ul class="dropdown-menu">`
       並排而不是上下堆疊，子選單因此浮到觸發鈕右邊而不是下面（實測
       點開「服務專區」，「顧問案／AI 分身」跑到右側去，不是預期的
       下拉效果）。這裡的舊規則本身在窄螢幕已經有一條專屬修正
       （wireframe.css:121，`max-width:991px` 內改回 `display:block`），
       但≥1468px 這個桌機常態抽屜沒有對應的修正，這裡補上。 */
    html:root .site-header .site-nav > .dropdown {
        display: block;
    }

    html:root .site-header .site-nav > a,
    html:root .site-header .site-nav > .dropdown > a {
        display: flex;
        align-items: center;
        gap: .75rem;
        padding: .9rem 0;
        border-radius: 0;
        font-family: var(--font-eyebrow);
        font-size: var(--text-body);
        line-height: 1.6;
        letter-spacing: .04em;
        font-weight: var(--fw-normal);
        color: hsl(var(--heading) / .7);
        background: transparent;
        box-shadow: none;
        transition: color var(--transition);
    }

    /* 裝飾用圖示整批藏起來——只留純文字，見本節開頭原則 1。 */
    html:root .site-header .site-nav > a .wf-icon:not(.wf-icon--chevron-down),
    html:root .site-header .site-nav > .dropdown > a .wf-icon:not(.wf-icon--chevron-down) {
        display: none;
    }

    html:root .site-header .site-nav .dropdown-toggle .wf-icon--chevron-down {
        width: 14px;
        height: 14px;
        margin-left: auto;
        flex-shrink: 0;
        color: hsl(var(--heading) / .4);
        transition: transform var(--dur-hover) var(--ease-out);
    }

    html:root .site-header .site-nav .dropdown-toggle[aria-expanded="true"] .wf-icon--chevron-down {
        transform: rotate(180deg);
    }

    html:root .site-header .site-nav > a:hover,
    html:root .site-header .site-nav > a:focus-visible,
    html:root .site-header .site-nav .dropdown > a:hover,
    html:root .site-header .site-nav .dropdown > a:focus-visible {
        background: transparent;
        color: hsl(var(--heading));
    }

    html:root .site-header .site-nav .dropdown-toggle::after {
        display: none;
    }

    html:root .site-header .site-nav .dropdown-menu {
        display: none;
    }

    html:root .site-header .site-nav .dropdown-menu.show {
        display: block !important;
        position: static !important;
        float: none;
        width: auto;
        min-width: 0;
        margin: 0;
        padding: 0;
        border: 0;
        border-radius: 0;
        background: transparent;
        box-shadow: none;
        transform: none !important;
        inset: auto !important;
    }

    html:root .site-header .site-nav .dropdown-item {
        display: block;
        padding: .75rem 0 .75rem 1.5rem;
        border-bottom: 1px solid hsl(var(--foreground) / .1);
        font-family: var(--font-eyebrow);
        font-size: var(--text-meta);
        line-height: 1.7;
        letter-spacing: .04em;
        font-weight: var(--fw-normal);
        color: hsl(var(--heading) / .5);
        background: transparent;
        border-radius: 0;
        transition: color var(--transition);
    }

    html:root .site-header .site-nav .dropdown-item:hover,
    html:root .site-header .site-nav .dropdown-item:focus-visible,
    html:root .site-header .site-nav .dropdown-item:active {
        background: transparent;
        color: hsl(var(--heading));
    }

    html:root .site-header .site-nav .site-search {
        gap: .75rem;
        padding: .9rem 0;
        border-radius: 0;
        background: transparent;
    }

    html:root .site-header .site-nav .site-search-icon {
        order: -1;
        width: 14px;
        height: 14px;
        color: hsl(var(--heading) / .4);
    }

    html:root .site-header .site-nav .site-search:hover,
    html:root .site-header .site-nav .site-search:focus-visible {
        background: transparent;
        color: hsl(var(--heading));
    }

    html:root .site-header .site-nav-logo {
        display: inline-flex;
        align-items: center;
        gap: .625rem;
        margin-bottom: 2.25rem;
        text-decoration: none;
        padding: 0 !important;
    }

    html:root .site-nav-logo-icon {
        display: block;
        width: auto;
        height: 24px;
        flex-shrink: 0;
    }

    html:root .site-nav-logo-text {
        font-size: 1rem;
        font-weight: var(--fw-normal);
        letter-spacing: var(--tracking-title);
        color: #4a4636;
        white-space: nowrap;
    }

    /* 分組英文小標——跟清單項目共用同一套字體家族（--font-eyebrow）與
       字距（.04em 一帶），只用大小/透明度區分層級，不是切換成另一套
       字體語言，呼應客戶「文字...都要統一」的要求。
       ⚠ 選擇器要帶 `.site-header`：本檔既有一條
         `html:not(.is-compact) .site-header .site-nav-section-label
         {display:none}`（≈3057 行，桌機水平導覽列 vs 抽屜雙版型時代的
         舊規則，specificity (0,3,1)）。這裡如果只寫
         `html:root .site-nav-section-label`（(0,2,1)），specificity 較低，
         打不過那條 display:none。加上 `.site-header` 補到同一個
         (0,3,1)，靠後面贏的疊層順序蓋過去。
       ⚠ 光補特異度不夠——CSS 疊層是**逐個屬性**比較，不是整條規則比較。
         這條規則原本沒有宣告 `display`，即使贏了其他屬性（margin／
         color…），`display:none` 因為沒有同名屬性可以打對台，還是會
         繼續生效，小標依然是完全消失（實測踩過這個坑）。這裡明確補上
         `display: block`，才是真正解決問題的那一行。 */
    html:root .site-header .site-nav-section-label {
        display: block;
        margin: 2.25rem 0 .5rem;
        font-family: var(--font-eyebrow);
        font-size: var(--text-meta);
        line-height: var(--lh-meta);
        letter-spacing: .1em;
        color: hsl(var(--heading) / .35);
    }

    html:root .site-header .site-nav-section-label--first {
        margin-top: 0;
    }

    /* ⚠ group-end 不再需要「額外補一條線」——現在每一列本來就有線
       （見上面 .site-nav > .dropdown／> a 那條），group-end 只需要在
       線之後多留一點呼吸空間，跟下一組的英文小標拉開距離。 */
    html:root .site-header .site-nav > a.site-nav-group-end,
    html:root .site-header .site-nav .dropdown > a.site-nav-group-end {
        margin-bottom: .5rem;
    }

    /* AAA 字級切換器：拿掉三色底（見本節開頭原則 4），改成跟其餘選單
       項目同一套「純文字＋細線」語言——三顆並排，選中的那顆用底線＋
       加粗，不靠背景色分辨。
       ⚠ 這裡選擇器要蓋過 .font-size-btn 的基底規則（≈9708-9739，三色
         底＋2px 邊框），必須疊在同一個 .font-size-switch 容器路徑上、
         且晚於基底規則出現在檔案裡才贏得了特異度打平時的疊層順序
         （基底規則是單純 class 選擇器 (0,1,0)，這裡多疊了三層祖先
         class，(0,4,0) 穩贏，不需要 !important）。 */
    html:root .site-header .inner > .site-nav .font-size-switch {
        display: flex;
        gap: 1.25rem;
        margin-top: 1.75rem;
        padding-top: 1.5rem;
        border-top: 1px solid hsl(var(--foreground) / .1);
    }

    html:root .site-header .inner > .site-nav .font-size-switch .font-size-btn {
        border-width: 0;
        border-radius: 0;
        padding: 0 0 .25rem;
        background: transparent !important;
        font-family: var(--font-eyebrow);
        color: hsl(var(--heading) / .5);
        transition: color var(--transition);
    }

    html:root .site-header .inner > .site-nav .font-size-switch .font-size-btn:hover {
        border-color: transparent;
        color: hsl(var(--heading));
    }

    html:root .site-header .inner > .site-nav .font-size-switch .font-size-btn.is-active {
        border-bottom: 1px solid hsl(var(--heading));
        color: hsl(var(--heading));
        font-weight: var(--fw-medium);
    }

    html:root .site-header .inner > .site-nav .font-size-switch .font-size-btn.is-active:hover {
        border-color: hsl(var(--heading));
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   方形 Menu 按鈕：捲動收合狀態同步「無邊框黑線」（2026-08-17）
   ═══════════════════════════════════════════════════════════════════════
   客戶「選單線條改黑色，四周不要描邊框線」時，我只改了 ≥1468px 那個
   常態套用的按鈕外觀（見上面「Header 重新分層」區塊裡的
   `html:root:not(.has-menu-open) .site-header .site-nav-toggle`）。
   但「捲動離開頁首收合成方形選單鈕」（≈8542 行「Header 收合成方形
   選單鈕」區塊，comment 明講「全斷點適用」）是一條**不限寬度**、獨立
   於任何 @media 的舊規則，一樣命中 `.site-nav-toggle`，且跟我上面的
   新規則特異度相同（都是 1 個 html 條件 class/pseudo + .site-header +
   .site-nav-toggle）——但那條規則只在 ≥1468px 的 @media 裡才會被我的
   新規則蓋過去；<1468px（手機／平板）捲動時，畫面上跑的還是舊規則的
   白底＋邊框方盒，跟現在其餘所有情境（頁首常態、桌機捲動）已經統一
   的「透明＋無邊框＋黑線」不一致。
   ⚠ 不是編輯舊規則本身（那是更早一輪 session 就上線、客戶已經看過
     確認的東西）——比照「只加不改」慣例，另外疊一條同選擇器、寫在
     檔案更後面的規則，用疊層順序覆寫，舊規則保留當歷史紀錄。 */
html.is-compact:not(.has-menu-open) .site-header .site-nav-toggle {
    background: transparent;
    border: 0;
    color: hsl(var(--heading));
}

/* ═══════════════════════════════════════════════════════════════════════
   全站 Header（頁首未捲動狀態）：改回完整展開式導覽（2026-08-17 客戶
   指定，附參考圖——日式老建築照片＋右上角一整排細字導覽，無漢堡、無
   隱藏選單；原本第一版只套用在首頁，客戶追加「其他頁面選單文字改黑
   70%」後擴大成全站，見下面範圍說明）
   ═══════════════════════════════════════════════════════════════════════
   客戶原話重點：
   - Logo 左上角／所有選單文字展開在右上方，白色細字，字距鬆一點
   - 選單整體位置往左移一些，不要貼太右
   - Header 壓在 Hero 圖片上，透明背景，不要白色背景條/Sticky 白條
   - 不要方形漢堡選單、不要隱藏 Menu、不要 2×3 功能面板、不要圓角/
     膠囊按鈕
   - 目前網站原有的所有 Header 選項都要直接展開（服務專區／實體課程／
     知識專區／會員／電子報／搜尋／字級切換），不刪任何連結、不改路由
   - 「這次只修改 Header／Navigation 的呈現方式，不要修改 Hero 區塊
     尺寸、Hero 圖片比例、Hero 內容位置或其他頁面內容」

   ⚠ 這是把今天稍早的「Header 重新分層」（收合成方形 Menu 鈕＋隱藏式
     抽屜）整個反過來——客戶明確認可了一整輪收合式設計之後，看到這張
     參考圖又改變方向，回到「全展開」。這不是誤改，是客戶重新決定。
   ⚠ 只加不改：不去刪除或編輯上面「Header 重新分層」那一大段規則
     （它們是這個 session 更早、也已經被客戶看過確認過一輪的另一個
     設計版本）——這裡疊一層新的、選擇器特異度打平時排在檔案最後面、
     一定贏的規則，把畫面蓋回客戶現在要的樣子。之後如果客戶又想切回
     收合式，把這一整段刪掉即可，上面的規則原封不動可以直接復原。
   ⚠ 範圍原本第一版只鎖 `html:has(.hero):not(.is-compact)`（只有首頁、
     還沒捲動的狀態），因為客戶當時明講「首頁 Hero 上方」「不要修改…
     其他頁面內容」。隨後客戶追加「其他頁面選單文字改黑70%」——這句話
     本身就代表其他頁面現在也要顯示這個全展開列表（不然沒有「選單文字」
     可以上色），等於把範圍從「只有首頁」擴大成「全站」。這裡已經把
     所有 `html:has(.hero):not(.is-compact)` 選擇器改成單純
     `html:not(.is-compact)`（拿掉 hero 限定），文字顏色也從寫死白色
     改讀 --header-ink（有 .hero 的頁面自動白色、其餘頁面自動黑70%，
     同一顆變數自己判斷，不需要另外寫兩份規則）。
     `:not(.is-compact)` 這個限定仍然保留——只有「還在頁首第一屏、尚未
     捲動」的狀態套用這個全展開設計；使用者往下捲動之後，`.is-compact`
     生效，交回上面「桌機捲動收合」那組既有機制（收合成方形 Menu 鈕），
     客戶目前沒有提到要連捲動後的行為也改掉，不擅自延伸解讀。
     只在 ≥1468px 生效：手機版「只顯示 Logo／方形 Menu」是這個 session
     更早另一輪明確的客戶指示（「手機版從一開始就只顯示：左側 Logo／
     右側方形 Menu 按鈕」），這次的參考圖是桌機構圖，沒有提到要推翻
     手機規則，維持不動。 */
@media (min-width: 1468px) {
    /* Header 本身：壓在 Hero 圖片上，透明背景，不是 sticky/fixed 白條。
       ⚠ position:absolute 不是相對於 .hero——兩者是手足關係（都是
         .wf-frame 的直接子節點，見 layouts/main.php），不是父子關係，
         `.hero-inner` 的 position:relative 對這裡沒有作用。.site-header
         往上找不到任何有設定 position 的祖先，會一路退到初始包含區塊
         （viewport／文件根），效果等同「貼在文件最上面、跟著頁面內容
         一起捲動」——視覺上剛好就是壓在 Hero（文件裡第一個區塊）上面，
         使用者往下捲動、Hero 捲出畫面後，這層自然跟著捲走，不需要
         JS 或額外 class 切換，跟捲動收合成方形鈕（.is-compact 那組）
         是兩套互不干涉的機制，交棒點是「使用者已經看不到這層」，不是
         寫死某個 scrollY 數字去對接。 */
    html:not(.is-compact) .site-header {
        position: absolute;
        top: 0;
        left: 0;
        right: 0;
        height: auto;
        background: transparent;
        z-index: 20;
        border-bottom: 0;
        box-shadow: none;
    }

    /* 拿掉方形 Menu 鈕與只留 icon 的搜尋連結——這次搜尋改回在展開列裡
       用完整文字顯示（下面 .site-search 那條），不需要另外的圖示按鈕；
       Menu 鈕本身這個狀態不需要，選單本來就是全展開、沒有東西可以「開」。 */
    html:not(.is-compact) .site-header .site-nav-toggle,
    html:not(.is-compact) .site-header .site-nav-primary {
        display: none;
    }

    /* .inner：Logo 左、導覽右，單純一列，不是今天稍早那組雙欄雙列
       grid（那是給 AAA 獨立一行用的，AAA 現在跟其餘選項一起在同一列
       展開，不需要第二行）。 */
    html:not(.is-compact) .site-header .inner {
        display: flex;
        align-items: center;
        justify-content: space-between;
        flex-wrap: nowrap;
        padding-block: clamp(1.5rem, 3vw, 2.25rem);
        row-gap: 0;
    }

    /* #wfNav：從「常態 off-canvas 抽屜」改回「行內水平列」——這是這次
       改動的核心。position 拿掉、寬度交回內容自然寬度、transform／
       visibility 的開合機制整組不需要（沒有東西要「滑入」，本來就
       在畫面上）。
       ⚠ margin-right（不是 margin-left:0）負責「往左移一些，不要貼
         太右」：.inner 是 justify-content:space-between，導覽本來就會
         自動貼到最右側；客戶要的「往左移」是讓它離右邊界留一段距離，
         用 margin-right 從右緣扣一截空間即可，不需要改 .inner 本身的
         對齊方式。
       ⚠ 2026-08-17 客戶「處理選單靠左」——確認過方向是「選單目前太靠右，
         要再往左移」，把 clamp 上限從 5rem 加大到 9rem（下限與中間的
         vw 響應式比例跟著等比放大，維持同一種「視窗越寬、間距越大」
         的節奏，不是只調死一個數字）。 */
    html:not(.is-compact) .site-header .inner > .site-nav {
        display: flex !important;
        position: static;
        inset: auto;
        width: auto;
        max-height: none;
        margin: 0 clamp(3rem, 8vw, 9rem) 0 0;
        padding: 0;
        border: 0;
        background: transparent;
        overflow: visible;
        transform: none;
        visibility: visible;
        align-items: center;
        justify-content: flex-end;
        flex-wrap: wrap;
        row-gap: .5rem;
        column-gap: clamp(1.75rem, 2.8vw, 2.75rem);
        transition: none;
    }

    /* ⚠ 2026-08-17 客戶「選單文字請對齊，現在高度不一致」——實測：純
       文字項目（<a> 直接是 .site-nav 的 flex item）文字置中沒問題，
       但帶下拉的項目（服務專區／會員）多包了一層 .dropdown 容器 div，
       這層 div 才是真正的 flex item，本身 display:block（Bootstrap
       預設），比裡面的 <a> 本身高出一截（本檔其他規則疊加下來的
       margin/line-height 差異），<a> 在 .dropdown 這層 block 容器裡是
       貼齊頂部，不是置中——.dropdown（33px 高）當 flex item 被
       align-items:center 置中時，跟著置中的是它「貼頂的內容」，視覺
       上文字自然比旁邊純文字項目低了一截。
       修法：讓 .dropdown 自己也變成 inline-flex + align-items:center，
       裡面的 <a> 在這層容器內部自己置中，不管 .dropdown 外框實際多高，
       文字永遠都在正中間，跟純文字項目的視覺基準一致。 */
    html:not(.is-compact) .site-header .site-nav > .dropdown {
        display: inline-flex;
        align-items: center;
    }

    /* 每一項與帶下拉的觸發鈕：細字＋鬆字距，取代抽屜版的深色列表樣式。
       字重用 --fw-light（站上既有最細字重 token），不是隨手寫一個數字。
       ⚠ 2026-08-17 客戶「其他頁面選單文字改黑70%」——顏色從寫死白色
         改讀 --header-ink（本檔「Header 文字/線條顏色」那段既有的
         page-conditional 變數：有 .hero 的頁面白色、其餘頁面黑70%），
         不是另外判斷頁面類型——這個機制本來就是為了「同一份導覽規則、
         不同頁面吃不同顏色」設計的，這裡直接沿用，不重新發明一套。 */
    /* ⚠ 2026-08-17 客戶「選單請用細體」——原本讀 var(--fw-light) 沒有
       真的變細，實測發現真正原因不是特異度輸了，是 --fw-light 這顆
       token 本身在 .site-header 範圍內被局部重新定義成 350（≈2933 行
       「客戶指定『header 字體粗一點，跟 footer 一樣』」那次決定），
       所以就算贏了 font-weight 的特異度戰爭，讀到的 --fw-light 值
       本身已經不是「細」了，套用出來自然還是 350 那個粗細。
       這裡改成直接寫死數字（不透過 --fw-light 這顆已經被污染的
       token），繞開這個坑；font-family 這條保留 !important 沒關係
       （那條贏是贏在特異度，跟這個坑無關，不需要一起拿掉）。 */
    html:not(.is-compact) .site-header .site-nav > a,
    html:not(.is-compact) .site-header .site-nav > .dropdown > a {
        display: inline-flex;
        align-items: center;
        gap: .4em;
        padding: 0;
        margin: 0;
        border: 0;
        border-radius: 0;
        background: transparent;
        font-family: inherit !important;
        font-size: var(--text-body);
        font-weight: 200 !important;
        letter-spacing: .12em;
        color: var(--header-ink, hsl(var(--brand-foreground)));
        text-decoration: none;
        box-shadow: none;
        transition: color var(--dur-hover) var(--ease-out), opacity var(--dur-hover) var(--ease-out);
    }

    /* ⚠ 補這一條才是「選單文字請對齊，現在高度不一致」真正解決問題的
       那一行——上面的 margin:0 打不過「選單展開樣式重新設計」那輪加的
       `.site-nav-group-end { margin-bottom:.5rem }`（≈18216 行，那是
       給垂直清單版 drawer 用的組間距，這裡的水平列不需要），因為那條
       多疊了 `.site-nav-group-end` 一層 class，特異度 (0,5,2) 比這裡
       的 (0,4,2) 高一階，會贏。服務專區／知識專區剛好都帶
       `site-nav-group-end` 這個 class（沿用 drawer 版型的分組標記，
       markup 沒有為了這個新設計特別拆開），這裡的 8px margin-bottom
       讓它們的 flex item margin box 比其他項目高一截，align-items:
       center 置中的是「含 margin 的完整外框」，文字視覺上因此比旁邊
       項目低了一截。加這條同選擇器路徑（多補 .site-nav-group-end 補到
       同一個特異度，寫在更後面）歸零即可。 */
    html:not(.is-compact) .site-header .site-nav > a.site-nav-group-end,
    html:not(.is-compact) .site-header .site-nav > .dropdown > a.site-nav-group-end {
        margin-bottom: 0;
    }

    html:not(.is-compact) .site-header .site-nav > a:hover,
    html:not(.is-compact) .site-header .site-nav > a:focus-visible,
    html:not(.is-compact) .site-header .site-nav .dropdown > a:hover,
    html:not(.is-compact) .site-header .site-nav .dropdown > a:focus-visible {
        background: transparent;
        color: hsl(var(--heading));
        opacity: .65;
    }

    /* 裝飾用圖示這個狀態不需要（參考圖是純文字導覽）——沿用本檔最早
       「桌機 Header 三段式」既有的做法（≈3043 行一帶已經有同一組
       `:not(.is-compact) ... .wf-icon {display:none}`），這裡不必
       重寫，只在下拉三角形這件事上明確指定：改用瀏覽器原生
       ::after 三角形（Bootstrap 既有機制），不是站上圖示系統的
       chevron-down SVG，跟參考圖的極簡文字風格一致，不必額外載入
       icon。 */
    html:not(.is-compact) .site-header .site-nav .dropdown-toggle .wf-icon--chevron-down {
        display: none;
    }

    html:not(.is-compact) .site-header .site-nav .dropdown-toggle::after {
        display: inline-block;
        margin-left: .3em;
        border-top: .3em solid;
        border-right: .3em solid transparent;
        border-left: .3em solid transparent;
        opacity: .8;
    }

    /* 分組小標（SERVICE/CLASS/MEMBER）、抽屜自己的 logo——這兩個是
       抽屜版專屬的視覺，全展開列表裡沒有「分組」與「另一顆 logo」的
       概念（Header 本身已經有一顆 logo），比照抽屜殼本身，這裡整組
       隱藏。
       ⚠ 實測踩坑：`.site-nav-logo` 這個 <a> 同時也是 `.site-nav > a`
         （drawer 標記裡它就是 <nav> 的直接子節點 <a>），會被上面
         「每一項...白色細字」那條通用樣式規則命中——那條的選擇器多了
         一層 `> a` element 型選擇器，特異度 (0,4,2) 比這裡原本的
         (0,4,1) 高一階，會贏過這裡的 display:none（實測畫面上這顆
         drawer 專用 logo 真的跑出來、跟 header 自己的 logo 並排兩顆）。
         這裡把 `.site-nav-logo` 改成 `.site-nav > a.site-nav-logo`
         寫法，補上同一層 `> a`，特異度打平到 (0,5,2)，才穩贏。
       ⚠ `.site-nav-close` 另一個問題：本檔既有一條
         `html:root .site-nav-close { display:inline-flex !important; }`
         （≈17895 行，「桌機捲動收合」那組要求 !important 蓋過
         Bootstrap 的 d-lg-none），這裡的 display:none 不管特異度多高，
         沒有 !important 就贏不了另一條 !important 宣告——补上
         !important 才是真正解決問題的那一行。 */
    html:not(.is-compact) .site-header .site-nav-section-label,
    html:not(.is-compact) .site-header .site-nav > a.site-nav-logo {
        display: none;
    }

    html:not(.is-compact) .site-header .site-nav-close {
        display: none !important;
    }

    /* 下拉子選單（服務專區／會員）：改回標準 Bootstrap 浮動選單
       （position:absolute，觸發後浮在觸發鈕下方），不是抽屜版的
       position:static 就地展開——這是「完整展開」與「原地插入」
       兩種不同的下拉語言，全展開列表用参考圖那種浮動小面板更合適。 */
    html:not(.is-compact) .site-header .site-nav .dropdown-menu.show {
        display: block !important;
        position: absolute !important;
        float: none;
        min-width: 10rem;
        margin: .5rem 0 0;
        padding: .5rem 0;
        border: 0;
        border-radius: 2px;
        background: hsl(var(--brand-foreground));
        box-shadow: 0 12px 28px hsl(var(--foreground) / .16);
        transform: none !important;
    }

    html:not(.is-compact) .site-header .site-nav .dropdown-item {
        display: block;
        padding: .5rem 1.25rem;
        border: 0;
        font-size: var(--text-body);
        font-weight: var(--fw-normal);
        letter-spacing: .04em;
        color: hsl(var(--heading) / .7);
        background: transparent;
    }

    html:not(.is-compact) .site-header .site-nav .dropdown-item:hover,
    html:not(.is-compact) .site-header .site-nav .dropdown-item:focus-visible {
        color: hsl(var(--heading));
        background: hsl(var(--foreground) / .06);
    }

    /* 搜尋：跟其餘項目同一套白色細字語言，拿掉膠囊底與放大鏡 icon
       （icon 是抽屜/圖示導覽語言，這裡是純文字列表，不需要）。 */
    html:not(.is-compact) .site-header .site-nav .site-search {
        gap: 0;
        padding: 0;
        border-radius: 0;
        background: transparent;
    }

    html:not(.is-compact) .site-header .site-nav .site-search-icon {
        display: none;
    }

    /* AAA 字級切換：今天稍早搬進 #wfNav 尾端（見 _header.php），現在
       #wfNav 是水平列，AAA 這組自然跟著排在「搜尋」後面、同一列——
       拿掉抽屜版的頂部分隔線與大間距，改成跟其他項目一樣的行內間距，
       三顆字級鈕本身沿用「選單展開樣式」那輪已經定案的無底色極簡樣式
       （border-top/bottom 那組，見上面「html:root .site-header .inner
       > .site-nav .font-size-switch」），這裡只調整外層排版讓它融入
       同一列，不重新設計三顆鈕本身的樣子。
       ⚠ 2026-08-17 客戶「aaa跟其他選項隔開一點，讓其他連結單獨靠左移」
         ——.site-nav 是 flex row + justify-content:flex-end，AAA 跟前面
         6 個連結原本共用同一個 gap（沒有分組感）。這裡直接加大 AAA
         自己的 margin-left（蓋掉共用的 gap，形成明顯比其他項目間距更寬
         的一道分隔），視覺上 6 個連結會自然貼在一起變成靠左的一組，
         AAA 獨立在最右側。
         ⚠ 不是用 margin-left:auto——.site-nav 是 width:auto（撐內容，
           不是撐滿父層），沒有多餘空間可以讓 auto margin 吃，實測沒有
           效果；改用固定值才會真的把 AAA 推開。 */
    html:not(.is-compact) .site-header .inner > .site-nav .font-size-switch {
        margin: 0 0 0 clamp(1.5rem, 3vw, 3rem);
        padding: 0;
        border: 0;
        gap: .875rem;
    }

    html:not(.is-compact) .site-header .inner > .site-nav .font-size-switch .font-size-btn {
        color: var(--header-ink, hsl(var(--brand-foreground) / .75));
    }

    html:not(.is-compact) .site-header .inner > .site-nav .font-size-switch .font-size-btn:hover {
        color: hsl(var(--heading));
    }

    html:not(.is-compact) .site-header .inner > .site-nav .font-size-switch .font-size-btn.is-active {
        border-bottom-color: var(--header-ink, hsl(var(--brand-foreground)));
        color: var(--header-ink, hsl(var(--brand-foreground)));
    }

    /* Logo：這個狀態沿用「放大只留圖示」既有決定（≈9814 行一帶），
       但客戶這次沒有提到要拿掉品牌字，反而給的概念圖是純圖示 logo，
       維持現狀不特別處理；顏色沿用 --header-ink 首頁白色分支，已經
       是白的，不需要額外覆寫。
       ⚠ 2026-08-17 客戶「品牌名離logo右一點，品牌名字間距寬一點」——
         圖示與文字的間距（.site-logo 的 gap，實測原本 12px）加大，
         文字本身的字距（letter-spacing，實測原本 1.6px／.1em）也加寬。 */
    html:not(.is-compact) .site-header .site-logo {
        gap: 1rem;
    }

    html:not(.is-compact) .site-header .site-logo-text {
        letter-spacing: .2em;
    }
}

/* ════════════════════════════════════════════════════════════════════
   首頁 Hero 改成置中大型「介面卡片」（2026-08-17 第三輪，客戶提供全新
   性質參考圖：不再是滿版構圖，是淺色大背景上放一張置中 Hero Card）
   ════════════════════════════════════════════════════════════════════
   前兩輪都是「主圖滿版鋪在 Hero 裡、Logo/Menu/AAA 疊在畫面邊緣」的
   構圖，這輪客戶推翻方向：Card 內部切成左窄欄／中主圖／右功能區三段，
   Menu、搜尋這些原本飄在瀏覽器右上角的控制項收進 Card 右側功能區。

   範圍界定：不動 Hero 外層高度（`.hero{min-height:100svh}` 不變）、
   不動既有矩形遮罩顯影動畫、只改首頁（html:has(.hero) 範圍界定，其他
   18 頁完全不受影響）。既有四堂課輪播文案／CTA／箭頭切換／頁碼一個
   節點都沒刪，只是重新巢狀＋透過這裡的 CSS 重新定位。

   ⚠ Card 幾何用 padding 置中，不用額外算絕對座標：.hero-inner 加
     padding（= Card 四邊留白），.hero-card 本身 width/height:100%
     自動填滿留白後的剩餘空間，不需要另外算「置中後的絕對座標」。
   ⚠ Logo／Menu／搜尋要對齊 Card 邊界，但 .site-header 是 .hero 的
     手足（都在 .wf-frame 底下，不是子孫），定義在 .hero 身上的變數
     .site-header 讀不到——這裡改定義在 html:has(.hero) 本身（即
     :root），兩邊都讀得到，不用 JS 量測 DOM 位置（這個 session 前面
     已經吃過兩次「JS 量測 nav 位置、Hero 版面連環壞掉」的虧）。
   ⚠ .hero-text-module／.hero-arrow-module 原本共用 .hero-modules
     （display:grid 三欄並排、同進退）。這輪文案要疊圖片右上、箭頭要
     壓圖片右下角、矩形、左右並排——兩者不再是同一個視覺單元，改用
     .hero-modules{display:contents} 讓這層 wrapper 從版面樹消失，兩個
     子節點直接變成 .hero-card-media（position:relative）的定位子節點，
     各自 position:absolute 到想要的角落，不用改 DOM、不用動 home.js。
   ⚠ 頁碼沿用既有 #heroPageNo（01/04 格式，home.js 已有自動更新邏輯），
     不新做「01 02 03 04」四顆可點擊頁碼——客戶原文「或目前圖片索引」
     已經明文列為可接受的替代方案，重做成四顆可點擊按鈕是更大的功能
     異動，不在這輪「只改排版」範圍內。
   ⚠ 規則順序紀律：這個 session 同一天已經因為「手機 base rule 寫在
     桌機 @media 後面、同特異度後寫的贏」壞過兩次（.hero-brand-copy／
     .hero-side-cue）。這裡全程先寫手機／緊湊寬度的 base rule，再寫
     ≥1468px 的桌機 override，不重蹈覆轍。 */

/* ⚠ 2026-08-17 客戶訂正「沒有外圈是這樣呈現方式才對」——Card 要貼滿
   Hero 整個寬高，不留外層留白框，跟參考圖裡「品牌色只在卡片內部局部
   區塊出現（左窄欄、右功能區），不是包住整張卡片的外框」一致。這裡把
   --hero-card-inset-x/-y 歸零就好，Logo/Menu/搜尋讀的是同一組變數
   算 fixed 定位，不用回頭改那幾條規則。 */
html:has(.hero) {
    --hero-card-inset-x: 0px;
    --hero-card-inset-y: 0px;
    --hero-card-pad: clamp(1.25rem, 2vw, 2rem);
    /* 右側功能區欄寬：grid-template-columns 跟 Menu／搜尋「水平置中
       在這欄正中央」的定位計算要對齊同一個數字，抽成變數避免兩處
       各自寫死同一個 30%。 */
    --hero-panel-w: 30%;
    /* 2026-08-17 客戶「logo壓底...剛好填滿圖片留白區域」——主圖上下
       留白帶的厚度、logo 方塊的邊長、右側次圖上緣留白，三處要對齊同一
       個數字，抽成共用變數當 SSOT，不要三處各自寫死同一個 clamp()。 */
    /* 2026-08-17 客戶「圖片上面padding不夠高」——clamp 上下限都加大
       （1.5–3rem → 2.5–4.5rem）。 */
    --hero-card-media-pad-y: clamp(2.5rem, 7vh, 4.5rem);
    /* 左窄欄寬度：grid-template-columns 跟 logo 灰底方塊的寬度要對齊
       同一個數字，否則 logo 方塊填不滿整個左上角留白區（客戶「logo
       灰色背景沒填滿」）——抽成變數避免兩處各自寫死同一個 clamp()。 */
    --hero-card-left-w: clamp(64px, 6vw, 96px);
}

/* 外層留白背景：淺色/品牌色大背景，跟 Card 本身的 --background 拉開
   一階層次感，兩顆都是既有 token，沒有新增顏色。只加不改——.hero 原本
   的 background:hsl(var(--background)) 是 pre-session 既有規則，這裡
   用同選擇器疊在後面覆寫，不去改原本那一行。
   ⚠ 用 --surface-grey 不是 --muted：實測 getComputedStyle 發現
     --muted 在 tokens.css 已經被後續改版換成純白（0 0% 100%，見
     tokens.css ≈637 行「這次換白的是 --muted」那輪決定），舊的灰階
     值搬到 --surface-grey 這個新 token 上（tokens.css ≈645 行）。這是
     這個站的既有坑（[[onedaylisten-token-stale-comments]]，token 語意
     會隨改版漂移，色票用前要 getComputedStyle 現場量，不能只看變數
     名稱望文生義）——這裡改用 --surface-grey 才是實際的淺灰色。 */
.hero {
    background: hsl(var(--surface-grey));
}

/* 2026-08-17 客戶先說「水平翻轉改回來」，緊接著又說「還是水平翻轉
   好了」——來回改變主意，最終定案是維持翻轉，所以這裡不新增任何覆寫，
   讓 2026-08-14 那條 pre-session 既有的 scaleX(-1) 鏡像規則（≈727 行）
   照原樣生效即可（曾經加過 transform:none 蓋掉它，這輪拿掉那條
   override，不是又疊一條「加回翻轉」——效果一樣，但不必要的兩條
   互相抵銷的規則沒有意義，直接不寫比較乾淨）。 */

/* 客戶「圖片白色漸層移除」——上一輪加的 .hero-media::after 漸層＋噪點
   疊層整條拿掉（這是 session 內新加、還沒定案的效果，直接刪除不受
   「禁止刪除既有內容」限制）。 */

/* .hero-pageno 2026-08-14 一度被要求隱藏，這輪客戶要「底部有極簡頁碼」，
   需求相反，這裡蓋掉舊的 display:none（pre-session 既有規則，只加不改，
   不去動原本那一行）。 */
.hero-pageno {
    display: block;
}

/* 常駐 AAA（_header.php 新補回的獨立 markup）預設隱藏——這是全站共用
   partial，19 頁都會渲染這段 DOM，只有首頁 ≥1468px 才需要顯示（見下面
   @media 區塊），這裡是「先關掉，桌機首頁才開」的 base rule，要寫在
   桌機 override 前面，不然同特異度後寫的贏，桌機也會被拉回隱藏
   （這個 session 已經因為順序寫反壞過三次，這次先寫對）。 */
.site-header > .inner > .font-size-switch {
    display: none;
}

/* ─── 手機／緊湊寬度 base：簡化版 Card（<1468px，含既有的 992–1467
   平板漢堡與 ≤1199.98px 手機堆疊斷點，全部沿用不動，這裡只加最外層
   Card 容器讓視覺上仍有「卡片感」，避免完全跳回滿版構圖）─────────── */
.hero-card {
    position: relative;
    width: 92vw;
    margin: 5vh auto;
    background: hsl(var(--background));
    border-radius: var(--radius-media, 8px);
    overflow: hidden;
}

.hero-card-left {
    display: flex;
    align-items: center;
    padding: .75rem 1.25rem;
    border-bottom: 1px solid hsl(var(--foreground) / .08);
}

.hero-card-discover {
    margin: 0;
    display: flex;
    align-items: baseline;
    gap: .5rem;
    font-family: var(--font-eyebrow);
    font-size: var(--text-meta);
    letter-spacing: .08em;
    color: hsl(var(--foreground) / .6);
}

.hero-card-discover-en {
    font-size: 10px;
    letter-spacing: .12em;
    opacity: .6;
}

.hero-card-media {
    position: relative;
}

/* 右側功能區在緊湊寬度直接不顯示：Menu／搜尋在這個寬度本來就走既有的
   平板／手機 header 樣式（漢堡鈕、off-canvas 抽屜），不需要另外對齊
   Card；裝飾用色塊/次圖在窄螢幕意義不大，優先讓版面單純、不橫向溢出。 */
.hero-card-right {
    display: none;
}

/* 1200–1467.98px 安全網：這個範圍沒有專屬設計（客戶這輪只精確對照桌機
   構圖），沿用既有 ≤1199.98px 手機堆疊的相同數值往上延伸，避免落回
   pre-session 「.hero-media 貼左滿版」那組舊公式（現在的定位祖先換成
   較窄的 .hero-card-media，舊公式會算出離譜的寬度）。 */
/* ⚠ 2026-08-27 客戶「hero在寬度1460-1200時樣式請跟桌機一樣，如果寬度不夠
     就把左邊往下看更裁切到」——上面這段「安全網」與下一段「完整手機堆疊」
     的上界因此**從 1467.98 收回 1199.98**，1200–1467.98 交還給桌機 Card
     版面（下面那個 @media 的下界同步從 1468 降到 1200）。
   ⚠ 這等於部分推翻 2026-08-25 那輪（客戶當時要「1469 以下改手機版型」）。
     兩段註解都原樣留著不刪——那是當時的判斷依據，日後要回推為什麼改來
     改去看得到脈絡。
   ⚠ **只動首頁 Hero 這三處斷點**。本檔還有十幾處 min-width:1468，那是
     header 水平導覽 ↔ 漢堡的切換點（992–1467 那個「重災區」），跟這次
     需求無關，一個都沒動；course_detail 在 ≥1468 覆寫 --header-h:191
     也是 load-bearing，同樣沒碰。 */
@media (max-width: 1023.98px) {
    .hero-media {
        position: relative;
        inset: auto;
        width: 100%;
        aspect-ratio: 16 / 9;
        overflow: visible;
    }

    .hero-modules {
        position: relative;
        inset: auto;
        width: 100%;
        min-width: 0;
        display: block;
        transform: none;
    }
}

/* ⚠ 2026-08-25 客戶「hero寬1469以下就改手機版型呈現」——上面「安全網」
   那條註解自己就寫了「沿用既有 ≤1199.98px 手機堆疊的相同數值往上
   延伸」，但實際只延伸了 .hero-media／.hero-modules 兩條，本檔 ≈1108
   那組真正完整的手機堆疊版面（.hero 本身改 display:block、箭頭列
   改橫排、頁碼重新定位……共 10 條規則）沒有跟著延伸到 1467.98——
   1200–1467.98 這段實測出真的跑版：.hero-media 用 16:9 比例但沒有
   高度上限，1467px 寬時圖片被撐到 759px 高，加上下方文字／箭頭列，
   整個 Hero 內容累計 1145px，超出一般筆電 900px 視窗 245px，CTA
   按鈕與頁碼都要往下滑很久才看得到（客戶附截圖／實測 getBoundingClientRect
   確認）。
   ⚠ 這裡把 ≈1108 那組規則缺的部分原樣搬過來、掛在同一個
     max-width:1467.98 斷點下（跟上面「安全網」共用同一個 media query，
     不重開一個），讓 1200–1467.98 真正拿到跟 ≤1199.98 完全一致的手機
     堆疊排版，不再是「只延伸兩條」的半套版本。≈1108 那組規則本身
     一行都沒動——那裡還留著「斷點刻意用 1199.98」的另一層理由
     （避開 .wf-float 浮動鈕碰撞），那個理由在 1200–1467.98 仍然成立
     （.wf-float 位置不隨寬度變化，這裡沿用同一組數值就是要避開同一個
     碰撞），沒有牴觸。
   ⚠ .hero-media 的 aspect-ratio／.hero-modules 的其餘屬性已經在上面
     「安全網」規則裡，這裡不重複寫，只補缺的那幾條。
   ⚠ 2026-08-27 上界跟著上面那段一起從 1467.98 收回 1199.98，理由見上。 */
@media (max-width: 1023.98px) {
    .hero {
        display: block;
        min-height: 0;
    }

    .hero-inner {
        flex: none;
    }

    .hero-grid {
        display: none;
    }

    .hero-modules {
        right: auto;
        bottom: auto;
    }

    .hero-text-module {
        padding: 1.5rem var(--section-x);
    }

    .hero-modules-divider {
        display: none;
    }

    .hero-arrow-module {
        flex-direction: row;
        justify-content: flex-start;
        gap: 1.25rem;
        padding: 0 var(--section-x) 1.5rem;
        background: hsl(var(--foreground));
    }

    .hero-arrow {
        flex: none;
        width: 2rem;
        height: 2rem;
    }

    .hero-arrow-glyph {
        width: 7px;
        height: 7px;
    }

    .hero-pageno {
        position: relative;
        right: auto;
        bottom: auto;
        margin-inline: var(--section-x);
        padding-top: .875rem;
        text-align: right;
    }
}

/* ─── ≥1468px：完整 Card 版面（Logo/Menu/搜尋改用 --hero-card-inset-*
   對齊 Card 邊界，取代原本飄在瀏覽器角落的寫法）───────────────────── */
/* ⚠ 2026-08-27 客戶「hero在寬度1460-1200時樣式請跟桌機一樣」——下界從
     1468 降到 1200，這段標題寫的「≥1468px」是當時的值，留著備查。
     左窄欄（往下看更多）本來就是 clamp(64px, 6vw, 96px)，1200 寬算出
     72px 自動收窄；客戶已同意「寬度不夠就把左邊往下看更裁切到」。 */
@media (min-width: 1024px) {
    .hero-inner {
        display: flex;
        padding: var(--hero-card-inset-y) var(--hero-card-inset-x);
    }

    /* 2026-08-17 客戶「灰色背景要壓整個hero的高，但寬在畫面7:3分的右邊
       3比例，7是左邊留白比」——右欄從原本窄欄（clamp 上限 150px）改成
       畫面寬度的 30%（相對這個 grid container 算，此時 Card 已經貼滿
       Hero 全寬，30% 就是螢幕的 30%）；左窄欄＋中主圖合起來自動吃
       剩下的 70%，不用另外寫死那個 70%。 */
    .hero-card {
        display: grid;
        grid-template-columns: var(--hero-card-left-w) 1fr var(--hero-panel-w);
        width: 100%;
        height: 100%;
        margin: 0;
        border-radius: 0;
    }

    /* 2026-08-17 客戶訂正回「左邊往下查看更多改不要壓背景，位置改放
       左邊留白中間區域，並且加箭頭」——這是這個元素第三次調整方向，
       這輪定案：置中（不貼邊、不貼底），純文字不壓色塊。Logo 已經改成
       fixed 定位、不在這欄的文件流裡，這欄現在唯一的內容只剩往下看
       更多文字，align-items/justify-content 都改 center，整塊落在
       左窄欄的正中央。 */
    .hero-card-left {
        flex-direction: column;
        align-items: center;
        justify-content: center;
        padding: var(--hero-card-pad) 0;
        border-bottom: 0;
        /* 2026-08-18 客戶「hero這區域有上次殘留的描線，在刪掉」——左窄欄
           右緣那條貫穿整個 hero 高的 1px 分隔線是先前分欄設計的殘留，
           歸零。（實測掃描 hero 內可見邊線，僅此一條是裝飾性分隔線；
           按鈕外框／輪播箭頭字形／往下長箭頭是功能性描邊，保留。） */
        border-right: 0;
    }

    /* 拿掉背景色塊、改回純文字直式排版；補一顆裝飾用箭頭（客戶這輪
       「並且加箭頭」，隨後追加「箭頭放字下面，要有長線的箭頭不要只有
       arrow」）。
       ⚠ 不用 flex 排 order：直式文字（writing-mode:vertical-rl）本身
       的閱讀順序就是「由上而下」，文字節點在 DOM 排在箭頭 <span> 前面，
       純文件流天生就是「文字在上、箭頭在下」，不需要 flex-direction
       反轉，反而簡單可靠——上一版用 column-reverse 想達到同樣效果，
       跟 writing-mode 疊在一起時主軸方向不直覺，容易猜錯。 */
    .hero-card-discover {
        display: block;
        writing-mode: vertical-rl;
        text-orientation: mixed;
        letter-spacing: .2em;
        padding: 0;
        background: transparent;
    }

    /* 客戶「要有長線的箭頭不要只有arrow」——一條長直線加底端一個
       45deg 角，做成「向下捲動」常見的那種長線箭頭，不是單純一個小
       折角。
       ⚠ 客戶訂正「不要放在左右了，這區塊是垂直文字」——第一版用
       display:block，在 vertical-rl 的父層裡，區塊層級的新元素會沿著
       「行進方向」（vertical-rl 的行進方向是水平、由右到左）另起一行，
       視覺上就是被推到文字的左邊，不是接在文字下方，跟「疊在文字下面
       延續同一直行」的預期不符。改成 display:inline-block：inline
       層級的內容會留在同一個行內方向（vertical-rl 底下就是垂直方向）
       延續排列，才會緊接在「多」這個字後面、往下延伸，符合「同一條
       直行、文字在上箭頭在下」的畫面。
       ⚠ writing-mode 仍然要重設回 horizontal-tb：這個 <span> 是
       .hero-card-discover 的子節點，會繼承父層的 vertical-rl，若不重設
       這條長線的「形狀本身」會被連文字一起轉向、變成橫的線，跟畫面上
       「往下」的視覺方向對不上（文字轉向沒關係，那是排版；箭頭是
       圖像指示，語意上要維持真正指向螢幕下方）——這是兩件不同的事：
       display 控制「箭頭這個區塊擺在哪裡」，writing-mode 控制「箭頭
       圖形本身長什麼樣子」，缺一不可。 */
    .hero-card-discover-arrow {
        writing-mode: horizontal-tb;
        display: inline-block;
        width: 1px;
        height: 2rem;
        margin: .875rem 0 0;
        background: currentColor;
        position: relative;
    }

    .hero-card-discover-arrow::after {
        content: "";
        position: absolute;
        left: 50%;
        bottom: -1px;
        width: 6px;
        height: 6px;
        border-right: 1.5px solid currentColor;
        border-bottom: 1.5px solid currentColor;
        transform: translateX(-50%) translateY(-3px) rotate(45deg);
    }

    /* 2026-08-17 客戶訂正「主圖跟次圖皆上下留白區域，目前主圖只有左右
       留空間上下填滿」——加上下 padding 讓主圖不再貼齊 Card 上下緣；
       右側次圖預覽（.hero-card-right）用同一顆 padding-block 數值，
       兩邊高度才會對齊（客戶原文「跟主圖同高」）。 */
    /* 2026-08-17 客戶「主圖右邊請蓋過灰色背景一點不要剛好跟灰色背景
       切邊」——負的 margin-right 讓這個 grid item 的右緣超出自己的
       欄位、疊進右側 .hero-card-right 的灰底範圍；z-index 確保疊在
       右欄之上（兩者是同層 grid item，沒有 z-index 的話後面的
       DOM 順序（.hero-card-right 在後）預設會蓋過前面的，順序跟想要
       的視覺疊層剛好相反，要明講贏過它）。 */
    /* ⚠ 客戶回報「左右箭頭消失了」——.hero-arrow-module 這輪改成
       right:-44px 跨出 .hero-card-media 的右邊界（貼到灰色功能區），
       但這裡原本有 overflow:hidden，把跨出去的部分連同箭頭一起裁掉了。
       真正需要裁切的是 .hero-media 自己（遮罩顯影動畫要靠它裁邊），
       它有自己那份 overflow:hidden（見下面 .hero-media 規則），這一層
       外層 wrapper 拿掉 overflow:hidden 不影響遮罩效果。 */
    /* 客戶「主圖再寬一點，往右延伸，箭頭位置也跟著移動」，隨後追加
       「主圖在往右加寬一點」，隨後再追加「主圖寬在往右延伸，矩形也要
       跟著移動」——加大負 margin-right 讓主圖更往右延伸／蓋過灰底更多；
       .hero-arrow-module／.hero-text-module 都是相對這個元素的右緣
       定位（見下面兩條規則），這個右緣跟著往右移，兩者會自動一起跟
       過去，不用另外再調。 */
    .hero-card-media {
        position: relative;
        height: 100%;
        padding-block: var(--hero-card-media-pad-y);
        margin-right: clamp(-11rem, -10vw, -6rem);
        z-index: 1;
    }

    .hero-media {
        position: relative;
        inset: auto;
        left: auto;
        right: auto;
        top: auto;
        bottom: auto;
        width: 100%;
        height: 100%;
        aspect-ratio: auto;
        overflow: hidden;
        border: 0;
    }

    /* 文字與箭頭拆開，各自定位——見檔頭「⚠ .hero-text-module／
       .hero-arrow-module」說明。 */
    .hero-modules {
        display: contents;
    }

    /* 2026-08-17 客戶最終定案：「文字跟箭頭改成文字一個寬矩形壓底右邊
       是箭頭然後箭頭左右但放上下，然後寬矩形壓白色毛玻璃底，窄矩形壓
       深灰色底，這兩個拼成一個方形放在主圖右下位置剛好貼邊」——文字／
       箭頭兩塊不再各自獨立飄在畫面上，改成拼成一塊：文字（寬矩形）在
       左、箭頭（窄矩形）在右，共用同一條底線，貼齊主圖右下角。
       ⚠ right 用 var(--hero-card-media-pad-y)：這個數字剛好是箭頭窄
       矩形的寬度（見下面 .hero-arrow-module 的 width），文字矩形的
       右緣＝箭頭矩形的左緣，兩者無縫拼接，不是兩個各自獨立、中間留
       空隙的區塊。
       ⚠ bottom 用 var(--hero-card-media-pad-y) 不是 0：這個 session
       已經在箭頭那條規則上踩過這個坑——0 貼的是 .hero-card-media 這層
       wrapper 的底緣，wrapper 底下還有 padding-block 那圈留白，要退回
       --hero-card-media-pad-y 那個距離才是真正貼著主圖本身的底緣。
       ⚠ 白色毛玻璃：background 半透明白＋backdrop-filter 模糊，是
       「毛玻璃」效果的標準做法，不是純色卡片。 */
    /* 客戶「矩形右移到箭頭矩形在灰底寬矩形貼邊主圖」——整組（文字＋
       箭頭）往右移一顆按鈕寬的距離：文字矩形的 right 從
       var(--hero-card-media-pad-y) 改成 0，右緣退到剛好貼齊主圖本身的
       邊緣（不再疊住箭頭矩形原本讓出來的那格）；箭頭矩形對應往右移到
       負值（見下面 .hero-arrow-module），整顆落在灰底功能區裡，不再
       跨在圖片／灰底交界上。 */
    /* 客戶「毛玻璃底改純白色底」——拿掉 backdrop-filter 模糊與半透明，
       改成不透明白色實色底。 */
    .hero-text-module {
        position: absolute;
        top: auto;
        right: 0;
        left: auto;
        bottom: var(--hero-card-media-pad-y);
        transform: none;
        width: min(46%, 440px);
        min-width: 0;
        height: auto;
        background: hsl(var(--background));
        z-index: 2;
    }

    /* 文字疊在白色毛玻璃底上，深色文字（跟站上其餘淺底內文同一套：
       --heading 標題／--slate 英文小標／--muted-foreground 內文，不是
       這個 session 發明的新配色）。 */
    .hero-text-module .hero-text-eyebrow {
        color: hsl(var(--slate));
    }

    .hero-text-module .hero-text-title {
        color: hsl(var(--heading));
    }

    .hero-text-module .hero-text-sub,
    .hero-text-module .hero-text-intro {
        color: hsl(var(--muted-foreground));
    }

    /* 箭頭窄矩形：right:0／bottom 對齊主圖底緣，跟左邊的文字寬矩形
       共用同一條底線，拼成一個方形。flex-direction 從 row（左右並排）
       改回 column（客戶「箭頭左右但放上下」——上下排列），寬度明確蓋成
       一顆按鈕寬（見下面 .hero-arrow 那條），窄矩形背景維持既有的
       #808285（pre-session 既有規則，本來就是深灰，不用新增）。 */
    /* 客戶「箭頭矩形在灰底」——right 從 0 改成負的一顆按鈕寬，跟左邊
       文字矩形（見上面，right 改回 0）一起整組右移，箭頭這格因此完全
       落在灰底功能區範圍內，不再卡在圖片／灰底交界上。 */
    .hero-arrow-module {
        position: absolute;
        right: calc(-1 * var(--hero-card-media-pad-y));
        bottom: var(--hero-card-media-pad-y);
        left: auto;
        top: auto;
        grid-column: auto;
        flex-direction: column;
        width: var(--hero-card-media-pad-y);
        z-index: 3;
    }

    .hero-arrow-module .hero-arrow {
        flex: none;
        width: var(--hero-card-media-pad-y);
        height: var(--hero-card-media-pad-y);
    }

    /* 分隔線跟著箭頭方向從直線改橫線（上下排列的兩顆按鈕中間要畫一條
       水平線，不是垂直線）。 */
    .hero-arrow-divider {
        width: auto;
        height: 1px;
        align-self: stretch;
    }

    .hero-pageno {
        position: absolute;
        left: 0;
        right: auto;
        bottom: clamp(1rem, 2vw, 1.5rem);
        margin: 0 0 0 clamp(1.25rem, 2vw, 2rem);
        text-align: left;
    }

    /* 右側功能區：淺灰底，收納 Menu／搜尋（真正的控制項是 _header.php
       既有元素，用 fixed 定位疊上來，見下方 Logo/Menu/搜尋那組規則），
       這裡放次圖預覽（下一堂課主視覺，只露出左半部，客戶確認「右邊是
       下一張輪播圖的左半部局部」是既定方向）。裝飾用向下箭頭客戶要求
       刪掉，這裡不再保留那條規則。
       ⚠ 客戶訂正回「次圖請跟主圖一樣高」——上下 padding 改回跟
       .hero-card-media 同一顆 --hero-card-media-pad-y（不再打半折），
       兩邊精準同高。灰色背景本身不受 padding 影響，仍然是整欄滿高
       （客戶稍早已確認「灰色背景要壓整個hero的高」）。 */
    /* 客戶「灰色背景左邊的線框刪掉，灰色背景在往左壓多一點」——拿掉
       border-left；margin-left 負值讓這個 grid item 的左緣往左超出
       自己的欄位、往中欄（主圖）方向多蓋一點。跟 .hero-card-media 那條
       「往右蓋過灰色背景」的負 margin 是同一組手法，兩者相向重疊，
       視覺上誰蓋過誰由 z-index 決定（.hero-card-media 目前 z-index:1，
       這裡沒設 z-index，維持 auto，主圖依然疊在上面，這條只是讓灰色
       本身的可視範圍變寬，不影響疊層順序）。 */
    /* 客戶「右邊次圖沒完全貼齊右邊」——右側 padding 原本跟左側一樣是
       clamp(.75rem, 1.5vw, 1.25rem)，次圖（margin-left:auto 貼右）會被
       這圈右內距擋住、貼不到 .hero-card-right 真正的右緣。改成 4 個值
       單獨把 right 歸零，只留左／上／下的內距，次圖才能真的頂到底。 */
    .hero-card-right {
        display: flex;
        flex-direction: column;
        padding: var(--hero-card-media-pad-y) 0 var(--hero-card-media-pad-y) clamp(.75rem, 1.5vw, 1.25rem);
        background: hsl(var(--surface-grey));
        margin-left: clamp(-3rem, -2vw, -1rem);
    }

    /* 2026-08-17 客戶「次圖局部太大，請露出目前面積寬一半的寬度就好」
       ——容器寬度從滿版改成右欄的一半，圖片本身仍然 height:100%／
       width:auto 撐出原始比例，只是能露出的視窗（容器）變窄了一半，
       右半部露出更少、左半部維持不變。
       ⚠ 客戶「次圖請整個貼右邊」——margin-left:auto 把這個變窄的容器
       推到右欄的右緣貼齊，不是預設貼左。
       ⚠ 客戶追加「次圖寬再減一半，還是貼齊右邊」——50% 再減半變 25%，
       margin-left:auto 貼右邊的邏輯不變。 */
    .hero-card-right-preview {
        display: block;
        position: relative;
        width: 25%;
        margin-left: auto;
        flex: 1;
        overflow: hidden;
        border-radius: 2px;
        background: hsl(var(--card));
    }

    .hero-card-right-preview picture,
    .hero-card-right-preview img {
        position: absolute;
        top: 0;
        left: 0;
        height: 100%;
        width: auto;
        max-width: none;
        object-fit: cover;
    }

    /* Logo：改成 fixed 對齊 Card 左窄欄上緣，取代原本在 .inner 一般
       flow 排版的寫法。
       ⚠ 2026-08-17 客戶「logo請底下壓灰色...logo壓底要是正方形寬跟高
       剛好填滿圖片留白區域卡在左上角」，隨後追加「logo灰色背景沒填滿」
       ——寬度原本讀 --hero-card-media-pad-y（＝上緣留白帶高度），但左
       窄欄的寬度是另一顆變數 --hero-card-left-w，兩個 clamp() 數字對
       不上，方塊右側跟下方會露出沒蓋到的白邊。改成寬讀
       --hero-card-left-w、高讀 --hero-card-media-pad-y，各自對齊真正
       圍出這個角落的兩個邊界，才會「剛好填滿」而不是正方形。 */
    /* 客戶「logo 壓跟箭一樣的灰底，logo反白」——底色改用跟
       .hero-arrow-module 同一顆色（#808285，客戶指定的既有精確色號，
       這裡直接複製字面值，不是套用站上色階剛好數值相近——沿用
       .hero-arrow-module 那條規則已經用過的處理方式，同一色號本來就
       沒有對應的 token 可以引用）；圖示改白色見下面
       .site-logo-icon path 那條覆寫。 */
    html:has(.hero):not(.is-compact) .site-header .site-logo {
        position: fixed;
        top: var(--hero-card-inset-y);
        left: var(--hero-card-inset-x);
        margin: 0;
        display: inline-flex;
        align-items: center;
        justify-content: center;
        width: var(--hero-card-left-w);
        height: var(--hero-card-media-pad-y);
        background: #808285;
        border-radius: 0;
        z-index: 70;
        opacity: 0;
        transform: translateY(-6px);
        animation: heroModulesIn 480ms var(--ease-out) 80ms both;
    }

    html:has(.hero):not(.is-compact) .site-header .site-logo-text {
        display: none;
    }

    /* logo 反白：只在這個 ≥1468px、logo 疊著深灰底（#808285）的狀態下
       才需要反白，深色圖示在深底上會看不見。這裡不能直接改檔尾那條
       「不分寬度」的深色版（≈19250 行）——那條是給沒有深灰底可以疊的
       窄寬度用的 fallback，兩邊條件不同、各自負責各自的寬度，改錯會
       變成窄寬度也跟著反白、疊在淺色留白區上看不見（跟這次要修的
       bug 是同一種）。
       ⚠ 特異度陷阱：這裡跟檔尾那條 fallback 選擇器文字完全一樣
       （html:has(.hero):not(.is-compact) .site-logo-icon path），檔尾
       那條物理位置在後面，同特異度會是它贏，不是這裡贏——這裡多加
       `.site-header` 這層祖先 class，特異度比它高一階，不管誰寫在前面
       都是這裡贏，不是靠檔案順序卡贏。 */
    html:has(.hero):not(.is-compact) .site-header .site-logo-icon path {
        fill: hsl(var(--brand-foreground));
    }

    /* 2026-08-17 客戶「aaa放右上角 搜尋跟選單放中間位置」，隨後訂正
       「我說的中間是水平居中不是垂直居中」——上緣高度維持跟 logo 同一條
       線不動（回到原本的 top 公式）。
       ⚠ 客戶再追加「搜尋跟選單請左移到白色背景上」——不要停在灰色
       功能區裡，要越過欄位左緣、落在主圖右緣那圈白色漸層淡出區（見
       .hero-media::after 那條漸層規則）。right 改成「以 --hero-panel-w
       （功能區欄寬）為基準，再往左（加大 right 值）一段」，落點退到
       功能區欄位邊界以外、主圖淡出區以內。 */
    /* 客戶追加「選單跟搜尋在左移一點」——right 基準值再加大一截
       （2rem→4rem）；隨後又說「顏色改灰色，不要壓背景喔是指本身icon
       顏色」——color 從 --foreground（接近黑）換成跟 Logo／箭頭窄矩形
       同一顆 #808285，圖示本身變灰，不是加背景色塊。 */
    html:has(.hero):not(.is-compact) .site-header .site-nav-toggle {
        display: inline-flex;
        position: fixed;
        top: calc(var(--hero-card-inset-y) + (var(--hero-card-media-pad-y) - 40px) / 2);
        right: calc(var(--hero-panel-w) + 4rem);
        width: 40px;
        height: 40px;
        background: transparent;
        border: 0;
        border-radius: 0;
        color: #808285;
        z-index: 70;
        opacity: 0;
        animation: heroFadeIn 480ms var(--ease-out) 220ms both;
    }

    /* 搜尋圖示：復用第一輪就做好、後來一直沒機會顯示的 .site-nav-primary
       （純圖示連結，見 _header.php），這輪終於有地方放——跟 Menu 鈕
       並排在主圖右緣白色淡出區（上緣高度不變，再往左移一點、圖示顏色
       改灰）。 */
    html:has(.hero):not(.is-compact) .site-header .site-nav-primary {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        position: fixed;
        top: calc(var(--hero-card-inset-y) + (var(--hero-card-media-pad-y) - 40px) / 2);
        right: calc(var(--hero-panel-w) + 4rem + 40px + .5rem);
        width: 40px;
        height: 40px;
        color: #808285;
        z-index: 70;
        opacity: 0;
        animation: heroFadeIn 480ms var(--ease-out) 140ms both;
    }

    /* 客戶「aaa放右上角 搜尋跟選單放中間位置」——AAA 補回常駐可見，
       固定在右欄的右上角（對齊 logo 那條上緣高度線，Menu／搜尋現在移到
       正中央了，上緣這個位置空出來剛好給 AAA）。方塊語言跟 Logo／
       主圖箭頭一致（1px 邊框、直角、選中時反黑），不是重新設計一套。 */
    html:has(.hero):not(.is-compact) .site-header > .inner > .font-size-switch {
        display: flex;
        align-items: center;
        gap: .375rem;
        position: fixed;
        top: calc(var(--hero-card-inset-y) + (var(--hero-card-media-pad-y) - 28px) / 2);
        right: calc(var(--hero-card-inset-x) + clamp(.75rem, 1.5vw, 1.25rem));
        z-index: 70;
        opacity: 0;
        animation: heroFadeIn 480ms var(--ease-out) 300ms both;
    }

    /* 客戶「aaa請壓深灰色」原本理解成三顆通通壓灰底，客戶訂正「我是指
       aaa選到黑底改灰底不是其他兩個壓灰底」——只有「選中狀態」的黑底
       要改灰，另外兩顆未選中的按鈕維持原本的透明底＋細邊框，不要跟著
       套用灰底。 */
    html:has(.hero):not(.is-compact) .site-header > .inner > .font-size-switch .font-size-btn {
        display: flex;
        align-items: center;
        justify-content: center;
        width: 28px;
        height: 28px;
        padding: 0;
        border: 1px solid hsl(var(--foreground) / .25);
        border-radius: 0;
        background: transparent;
        font-family: var(--font-eyebrow);
        font-size: 11px;
        color: hsl(var(--foreground) / .7);
    }

    html:has(.hero):not(.is-compact) .site-header > .inner > .font-size-switch .font-size-btn.is-active {
        border-color: transparent;
        background: #808285;
        color: hsl(var(--brand-foreground) / .85);
    }

    /* 選單打開時讓位給 #wfNav 抽屜裡的那一份 AAA（既有內容不能拆，
       兩份同時可見會重複）。 */
    html:has(.hero).has-menu-open .site-header > .inner > .font-size-switch {
        display: none;
    }

    /* 展開式導覽關掉，改成點 Menu 鈕才展開的頂部滑入抽屜（沿用「選單
       展開樣式請重新設計，要極簡日系風」那套視覺語言，機制本身這輪
       沒有異動，只是觸發鈕的位置/樣式換了）。 */
    html:has(.hero):not(.is-compact) .site-header .inner > .site-nav {
        display: block !important;
        position: fixed;
        inset: 0 0 auto 0;
        width: 100%;
        max-height: min(720px, 85vh);
        margin: 0;
        padding: 3.25rem clamp(1.75rem, 4vw, 4rem) 3rem;
        border: 0;
        border-bottom: 1px solid hsl(var(--foreground) / .08);
        background: hsl(var(--brand-foreground));
        overflow-y: auto;
        overscroll-behavior: contain;
        transform: translateY(-100%);
        visibility: hidden;
        transition: transform 320ms var(--ease-out), visibility 0s linear 320ms;
    }

    html:has(.hero):not(.is-compact) .site-header .inner > .site-nav.is-open {
        transform: translateY(0);
        visibility: visible;
        transition: transform 320ms var(--ease-out), visibility 0s;
    }

    /* 捲動收合狀態（.is-compact）原本在首頁補回方塊邊線（白底＋描線）
       ——2026-08-17 客戶「滾動後選單icon不要壓白底跟描線」明確推翻，
       整條規則移除（同一天 session 內容，未定案可直接刪），選單鈕回到
       檔尾「全站 Header 統一不展開」區塊給的純三條線樣式。 */
}

@media (prefers-reduced-motion: reduce) {
    html:has(.hero) .site-header .site-nav-toggle,
    html:has(.hero) .site-header .site-nav-primary,
    html:has(.hero) .site-header .site-logo {
        animation: none;
        opacity: 1;
        transform: none;
    }
}

/* ⚠ 修正：首頁展開的抽屜面板底色是白（hsl(var(--brand-foreground))），
   但抽屜裡的連結文字顏色沒有另外覆寫，會落到「全展開式導覽」那組
   給白色橫列用的規則（html:not(.is-compact) .site-header .site-nav > a
   { color: var(--header-ink, 白) }）——同一個 :not(.is-compact) 特異度
   打平、檔案位置更後面，贏過「極簡日系風」那組原本設計給抽屜用的深色
   文字規則，首頁 --header-ink 剛好又是白色，結果是白字疊在白底抽屜上，
   整個選單看起來像空的。這裡補一組特異度更高（多 :has(.hero)）的版本，
   直接照抄「極簡日系風」那組的顏色/間距數值，強制在首頁展開狀態勝出；
   同理分組小標／抽屜自己的 logo／關閉鈕／AAA 顏色也被同一批全展開規則
   連帶蓋掉（display:none／白字），一併補回來——這段是第一輪已經除錯過
   的內容，機制不變，原樣保留。 */
@media (min-width: 1468px) {
    html:has(.hero):not(.is-compact) .site-header .site-nav > .dropdown,
    html:has(.hero):not(.is-compact) .site-header .site-nav > a {
        margin-bottom: 0;
        border-bottom: 1px solid hsl(var(--foreground) / .1);
    }

    html:has(.hero):not(.is-compact) .site-header .site-nav > .dropdown {
        display: block;
    }

    html:has(.hero):not(.is-compact) .site-header .site-nav > a,
    html:has(.hero):not(.is-compact) .site-header .site-nav > .dropdown > a {
        display: flex;
        align-items: center;
        gap: .75rem;
        padding: .9rem 0;
        margin: 0;
        border-radius: 0;
        font-family: var(--font-eyebrow) !important;
        font-size: var(--text-body);
        line-height: 1.6;
        letter-spacing: .04em;
        font-weight: var(--fw-normal) !important;
        color: hsl(var(--heading) / .7);
        background: transparent;
        box-shadow: none;
    }

    html:has(.hero):not(.is-compact) .site-header .site-nav > a.site-nav-group-end,
    html:has(.hero):not(.is-compact) .site-header .site-nav > .dropdown > a.site-nav-group-end {
        margin-bottom: 0;
    }

    html:has(.hero):not(.is-compact) .site-header .site-nav > a:hover,
    html:has(.hero):not(.is-compact) .site-header .site-nav > a:focus-visible,
    html:has(.hero):not(.is-compact) .site-header .site-nav .dropdown > a:hover,
    html:has(.hero):not(.is-compact) .site-header .site-nav .dropdown > a:focus-visible {
        background: transparent;
        color: hsl(var(--heading));
        opacity: 1;
    }

    html:has(.hero):not(.is-compact) .site-header .site-nav-section-label,
    html:has(.hero):not(.is-compact) .site-header .site-nav > a.site-nav-logo {
        display: block;
        margin: 2.25rem 0 .5rem;
        font-family: var(--font-eyebrow);
        font-size: var(--text-meta);
        line-height: var(--lh-meta);
        letter-spacing: .1em;
        color: hsl(var(--heading) / .35);
    }

    html:has(.hero):not(.is-compact) .site-header .site-nav-section-label--first {
        margin-top: 0;
    }

    html:has(.hero):not(.is-compact) .site-header .site-nav > a.site-nav-logo {
        display: inline-flex;
        align-items: center;
        gap: .625rem;
        margin-bottom: 2.25rem;
        color: #4a4636;
        border-bottom: 0;
    }

    html:has(.hero):not(.is-compact) .site-header .site-nav-close {
        display: inline-flex !important;
    }

    html:has(.hero):not(.is-compact) .site-header .inner > .site-nav .font-size-switch .font-size-btn {
        color: hsl(var(--heading) / .5);
    }

    html:has(.hero):not(.is-compact) .site-header .inner > .site-nav .font-size-switch .font-size-btn:hover {
        color: hsl(var(--heading));
    }

    html:has(.hero):not(.is-compact) .site-header .inner > .site-nav .font-size-switch .font-size-btn.is-active {
        border-bottom-color: hsl(var(--heading));
        color: hsl(var(--heading));
    }
}

/* ⚠ 修正：Logo 深色化那條寫在 @media(min-width:1468px) 區塊裡面，但
   Logo 疊在留白區、需要深色的道理在所有寬度都成立——手機／平板寬度
   沒有這條覆寫，logo 圖示／文字繼續讀 --header-ink（首頁白色分支），
   變成白圖示疊在白底留白區上，幾乎看不見。這裡補一條不綁寬度的版本，
   跟桌機那份特異度相同，不會互相打架，純粹是「同一個修正，多覆蓋沒被
   涵蓋到的窄寬度」——這段第一輪已經除錯過，原樣保留。 */
html:has(.hero):not(.is-compact) .site-logo-icon path {
    fill: hsl(var(--heading));
}

html:has(.hero):not(.is-compact) .site-logo-text {
    color: hsl(var(--heading));
}

/* ═══════════════════════════════════════════════════════════════════════
   選單展開樣式重新設計：浮卡片（2026-08-17 客戶指定，附兩張參考圖）
   ═══════════════════════════════════════════════════════════════════════
   客戶回報現況（「極簡日系風」那組，本檔「Header 重新分層」與後續首頁
   專屬修正兩個區塊）問題：選單打開變成整片白底、太像後台／無障礙工具頁，
   長橫線清單太空洞，AAA 位置像跑版，背後 Hero 圖片完全被蓋掉。客戶要的
   參考圖是「背後仍看得到頁面、上方浮出一張留白多的白色選單卡片，卡片內
   左側品牌 CTA、右側三欄導覽（NAVIGATION／MEMBER／TOOLS）、底部一條細
   資訊列」。

   ⚠ 這是全站共用的 #wfNav，目前同時被三種既有「選單開啟」情境重用：
       A. ≤1467.98px（本檔「覆蓋層本體」區塊，≈8046 行起）
       B. ≥1468px 且 .is-compact（本檔「Header 重新分層」區塊，≈17818
          行起，「極簡日系風」那組）
       C. ≥1468px 首頁專屬、未捲動時（本檔「全站 Header...」後面
          html:has(.hero):not(.is-compact) 那組面板，≈19266 行起）
     這三處都改成套用下面新的卡片視覺。**刻意不動**唯一的第四種情境——
     ≥1468px、非首頁、未捲動的「完整展開式導覽」（本檔「全站 Header：
     改回完整展開式導覽」區塊，≈18342 行起，html:not(.is-compact) 且
     不含 :has(.hero)）——那是客戶另一輪已確認的設計，這次沒人要求改，
     下面每一條殼層規則的選擇器都刻意排除或不命中那個狀態，細節見各
     規則自己的註解。

   ⚠ _header.php 的 #wfNav 內容這輪只重新分組／加了幾個新 class／新增
     CTA 與底部資訊列，**沒有新增任何 wrapper**：三個既有情境裡有多條
     規則靠 `.site-nav > a`／`.site-nav > .dropdown` 這種直接子節點選擇器
     工作，包一層 wrapper 會讓這些項目變成孫節點、選擇器全部選不到，
     連帶弄壞「完整展開式導覽」那個沒人要求改的狀態。改用 CSS Grid 直接
     在既有的直接子節點上逐一指定 grid-column／grid-row 做出「三欄＋左側
     CTA＋底部資訊列」的視覺分區，DOM 結構完全不動。
   ⚠ CTA 區與底部資訊列的文案沿用 _footer.php 既有的 .footer-slogan／
     .footer-copy 字串，不是另外編一段新文案。 */

/* ── 新元素本身的樣式：這些 class 是這輪新加的，站上沒有任何既有規則
   會命中，可以放心寫一份不用跟舊規則搶特異度的乾淨樣式 ────────────── */
.site-nav-menu-tagline {
    grid-column: 1;
    font-family: var(--font-eyebrow);
    font-size: var(--text-body);
    line-height: 1.7;
    color: hsl(var(--heading) / .6);
    margin: .75rem 0 0;
    max-width: 26ch;
}

.site-nav-menu-foot {
    grid-column: 1 / -1;
    margin-top: 1.5rem;
    padding-top: 1.25rem;
    border-top: 1px solid hsl(var(--foreground) / .1);
}

.site-nav-menu-foot-copy {
    margin: 0;
    font-family: var(--font-eyebrow);
    font-size: var(--text-meta);
    letter-spacing: .04em;
    color: hsl(var(--heading) / .4);
}

/* ── 桌機／平板（≥992px）：三欄導覽＋左側 CTA，靠既有直接子節點逐一
   指定 grid-column／grid-row 排版，不靠 DOM 巢狀 ─────────────────────
   ⚠ 這裡刻意不分 A／B／C 三種情境各寫一份：grid-column／grid-row 在
     父層不是 display:grid 的情境下（例如「完整展開式導覽」那個
     display:flex!important 的狀態）本來就不會生效，等於自動只在真的
     切成 Grid 殼的三種情境裡起作用，不需要為了「安全」刻意再收窄
     選擇器範圍一次。 */
@media (min-width: 992px) {
    .site-header .site-nav-logo {
        grid-column: 1;
        grid-row: 1;
    }

    .site-header .site-nav-menu-tagline { grid-row: 2; }

    /* ⚠ 2026-08-17 客戶「文字首頁刪掉，服務專區改第一個並且跟右邊會員
       對齊字首，其餘文字也跟右邊對齊頭」——「首頁」連結已從 _header.php
       移除（同日新增、未定案內容），服務專區改 row 2 與 MEMBER 欄的
       「會員」同列；實體課程／知識專區依序遞補 row 3／4，跟右欄的
       電子報／搜尋同列——Grid 的 row 是跨欄共用的，同列自然同一條
       基準線起頭，不用另外算位移。 */
    .site-header .site-nav-section-label--first { grid-column: 2; grid-row: 1; }
    .site-header .site-nav-menu-service          { grid-column: 2; grid-row: 2; }
    .site-header .site-nav-menu-courses          { grid-column: 2; grid-row: 3; }
    .site-header .site-nav-menu-knowledge        { grid-column: 2; grid-row: 4; }

    .site-header .site-nav-menu-label-member { grid-column: 3; grid-row: 1; }
    .site-header .site-nav-menu-member       { grid-column: 3; grid-row: 2; }
    .site-header .site-nav-menu-newsletter   { grid-column: 3; grid-row: 3; }
    .site-header .site-nav .site-search      { grid-column: 3; grid-row: 4; }

    /* ⚠ 2026-08-27 兩顆 dropdown 改 `align-self:start`：子選項改成點了才
       展開之後，服務專區與會員的高度會各自變動（一邊展開、另一邊收合），
       而這個 grid 是 `align-items:center`——收合的那一邊會被擺到加高後那
       一列的垂直中央，變成「服務專區在上、會員莫名往下掉」的錯位（實測
       展開服務專區時 服務專區 y=116、會員 y=162）。釘在列頂之後兩欄的
       主項永遠切齊同一條線，展開的子選項往下長，不影響對面那一欄。 */
    .site-header .site-nav-menu-service,
    .site-header .site-nav-menu-member { align-self: start; }

    /* ⚠ 2026-08-27 客戶「箭頭靠左一點，有點太右邊」——chevron 的基底規則
       （本檔 ≈8275）用 `margin-left:auto` 把它推到觸發鈕的最右緣，而抽屜
       這三欄很寬（NAVIGATION 欄實測到 x≈840），箭頭就被甩到離「服務專區」
       四個字 380px 遠的地方，看起來跟標籤沒有關聯。改成緊跟在文字後面
       一個字距，回到「展開箭頭附屬於這個標籤」的常規讀法。
       ⚠ 只改抽屜這兩顆（服務專區／會員）且只在 ≥992：手機版抽屜是單欄
         窄面板，`margin-left:auto` 讓箭頭靠右反而是對的，不要一起動。
       ⚠ 用 !important 的理由（第一版沒加，實測只有 992–1467 生效、≥1468
         完全沒動）：`margin-left:auto` 在三個 shell 各寫了一份，其中
         ≥1468 那兩條是 `html.is-compact …`（≈8895）與 `html:root …`
         （≈21483），特異度都是 (0,5,1)，贏過這裡的 (0,4,0)，光靠檔案順序
         追不上。與其把選擇器複製成三份加前綴，直接用 !important（本檔
         header 狀態規則本來就大量使用同一手法）一次蓋掉三個 shell。
         ⚠ 驗證時要**兩個斷點都量**：1200 一開始就對了、1900 沒動，只測
           其中一個會誤判成已修好。 */
    .site-header .site-nav-menu-service .dropdown-toggle .wf-icon--chevron-down,
    .site-header .site-nav-menu-member .dropdown-toggle .wf-icon--chevron-down {
        margin-left: .625rem !important;
    }

    .site-header .site-nav-menu-label-tools     { grid-column: 4; grid-row: 1; }

    /* ⚠ 2026-08-27 `grid-row` 由 `2` 改成 `2 / span 3`（往下跨三列），
       起點不變、只是不再把自己的高度全算進第 2 列。
       ⚠ 不能寫 `2 / -1`：這個 grid 沒有明確的 `grid-template-rows`，所有
         列都是隱式產生的，`-1` 指的是**明確格線**的最後一條，在這裡解不到
         預期位置——實測會把第 1 列（欄標那列）撐成 125.78px、整組項目
         往下位移 56px，比原本的問題更糟。要跨列就用 `span`。
       起因：同日客戶把服務專區／會員的子選項改成「預設收合、點了才展開」
       之後，第 2 列裡最高的東西變成 TOOLS 欄這個直排的 A／A+／A++
       （實測 109px），但同列的服務專區／會員收合後只剩 51px——grid 的
       列高取該列最高者，於是第 2 列固定 125.78px，兩顆收合的項目被
       `align-items:center` 擺在中間，上下各留一段空白，畫面上「服務專區
       →實體課程」的間距（98px）比「實體課程→知識專區」（62px）多出一截，
       看起來像沒做完。改成跨列之後第 2 列由收合後的實際內容決定高度，
       間距回到一致；A／A+／A++ 仍從第 2 列頂端開始排，跟服務專區／會員
       同一條起始線。
       ⚠ 代價：2026-08-17 那輪刻意讓「A 齊會員、A+ 齊登入、A++ 齊註冊」的
         逐顆對齊不再成立——但那個對齊本來就以「子選項永遠展開」為前提，
         現在預設收合，登入／註冊根本不在畫面上，該前提已經不存在。 */
    .site-header .site-nav > .font-size-switch  { grid-column: 4; grid-row: 2 / span 3; }

    /* ⚠ 四顆欄標（NAVIGATION／MEMBER／TOOLS 三個 + Logo 所在的第一欄不算）
       原本的 margin-top（各情境數值不同，見本檔「極簡日系風」與首頁
       修正那兩組）是為「垂直清單裡跟上一組拉開距離」設計的，這裡是
       每欄的第一列，不需要這段留白，否則會跟第一欄的 Logo 對不齊高度。
       跟既有規則比大小很難每個情境都精準壓過，這裡是單一用途的新
       class，直接 !important 歸零最省事、風險也最低。 */
    .site-header .site-nav-section-label--first,
    .site-header .site-nav-menu-label-member,
    .site-header .site-nav-menu-label-tools {
        margin-top: 0 !important;
    }

    /* ⚠ 拿掉每個項目底下的 1px 分隔線（「極簡日系風」與首頁修正那兩組
       規則加的）——客戶這輪明確要求「沒有過多線條」，三欄變窄之後每
       項自己一條短底線視覺上更像被切碎，改成純靠 grid 的 row-gap 留白
       分隔。同理歸零 margin-bottom，避免跟 row-gap 疊加出雙倍間距。 */
    .site-header .site-nav > a,
    .site-header .site-nav > .dropdown {
        border-bottom: 0 !important;
        margin-bottom: 0 !important;
    }

    .site-header .site-nav-menu-service .dropdown-menu.show,
    .site-header .site-nav-menu-member .dropdown-menu.show {
        margin-top: .25rem !important;
    }

    /* ⚠ 2026-08-17 客戶追加訂正：「會員跟服務專區分支選項不要包起來，
       直接一起展開來但字細體」＋「選單點擊改垂直展開不要變成懸浮視窗」
       ——這兩句合起來是同一件事：子選項不要靠點擊 chevron 才展開的
       Bootstrap 浮動小面板（實測點開「服務專區」跑出一個有邊框、跟
       觸發鈕之間有段落間距的懸浮小卡，不是貼著往下長），改成永遠顯示、
       就地垂直往下接在觸發鈕正下方，字重變細。
       ⚠ 這裡的屬性（display／position）跟上面那條純 margin 的規則刻意
         分開寫、分開放到下面 A／B／C 三個「殼」區塊各自裡面，不是寫在
         這個不分情境、全寬度都生效的 `@media(min-width:992px)` 內——
         桌機靜止態「完整展開式導覽」（≈18342 行）的服務專區／會員也是
         同一顆 `.dropdown`，那裡的下拉是 Bootstrap 標準浮動選單，客戶
         沒有要求改那個狀態，寫在這裡會連那邊也一起被強制展開。見下面
         A／B／C 三處各自的同名規則。 */

    /* ⚠ 實測抓到的坑：「搜尋」在 ≥992px 有一條很舊的規則（2026-08-04，
       ≈10052 行）把文字 label 藏起來、只留放大鏡圖示，後來 2026-08-14
       那次「搜尋icon改文字搜尋」的訂正只補了
       `html:not(.is-compact) ...`（≈10086 行）——只在桌機靜止態的完整
       展開導覽生效，.is-compact 捲動收合後重用同一顆抽屜時吃不到那條
       訂正，退回最舊的「只留圖示」樣子。客戶這輪明講「搜尋作為 Member
       欄位中的一個文字連結」（方案 A），這裡跟 MEMBER 欄其他兩項
       （會員／電子報）一樣改回純文字、拿掉圖示，不管 .is-compact 與否
       都一致，不是只修首頁那一種情境。 */
    .site-header .site-nav .site-search .site-search-label {
        display: inline !important;
        /* 2026-08-17 客戶「其餘文字也跟右邊對齊頭」——原本 var(--lh-body)
           算出 33.6px，其他導覽項的 .site-nav-label 是 1.6（22.4px），
           行盒高 11px 差讓「搜尋」比同列的「知識專區」高 5px。改成同值
           1.6，兩欄同列的字首才真的同一條線（getComputedStyle 實測）。 */
        line-height: 1.6;
    }

    .site-header .site-nav .site-search .site-search-icon {
        display: none !important;
    }
}

/* ── 卡片殼與背景遮罩：只在「選單真的以浮動 overlay 形式出現」的三種
   情境生效（見上方大註解 A／B／C）────────────────────────────────── */

/* A. ≤1467.98px（手機／平板，含既有 992–1467.98px 抽屜寬度） */
@media (max-width: 1467.98px) {
    /* ⚠ 2026-08-17 客戶附截圖訂正（分兩輪）：
       第一輪「展開的寬請剛好跟白色一樣區塊一樣」＋「點擊右上角 Menu
       後，原本上方 Header 區域直接向下延伸，形成一個從頂部展開的選單
       區」——原本置中、四周留白的浮卡（left:50%＋max-width）改成滿版
       寬度、貼齊視窗頂端往下展開，像 Header 本身被撐開，不是飄在畫面
       中間的對話框。
       第二輪「展開的寬只要跟白色一樣寬就好，展開請貼齊白色區塊底部，
       上方不要留高」——原本 top 還留了 clamp(3rem,6vw,5.5rem)（沿用
       客戶最初規格「上方距離 Header 約 48px～88px」），這輪客戶回頭
       要求上緣不要留空隙，改成 top:0，直接貼齊視窗最上緣（也就是貼齊
       Header icon 列的下緣——這裡才發現首頁「未捲動」那個情境選單
       打開時 Header 的搜尋／方形鈕本身沒有被既有的『整組蓋掉』規則
       蓋到，因此還卡在原地，變成客戶截圖裡「選單上緣留一條白／看得到
       殘留圖示」的成因，另外在本檔「header 隱藏改用『整組蓋掉』」那組
       規則補了第三條涵蓋這個情境，兩個問題一起解決）。
       ⚠ 只改水平方向（left/right/width）與開合動畫的位移軸（改
         translateY，拿掉 translateX 置中）——垂直方向的「不要撐滿
         100vh、下方露出 Hero」這個決定沒有變，max-height 維持不動。 */
    .site-header .site-nav {
        display: grid !important;
        grid-template-columns: minmax(180px, 240px) repeat(3, minmax(150px, 1fr));
        grid-auto-rows: min-content;
        column-gap: clamp(2rem, 3.5vw, 3.5rem);
        row-gap: .625rem;
        align-content: start;

        position: fixed;
        inset: 0;
        width: auto;
        height: auto;
        top: 0;
        left: 0;
        right: 0;
        bottom: auto;
        transform: translateY(-16px);
        max-width: none;
        max-height: min(720px, 78vh);
        margin: 0;
        padding: clamp(2rem, 3.5vw, 3rem) clamp(2rem, 4vw, 4.5rem) clamp(1.5rem, 3vw, 2rem);
        border: 0;
        border-radius: var(--radius);
        background: hsl(var(--surface-dark));
        box-shadow: 0 24px 60px hsl(var(--foreground) / .18);
        overflow-y: auto;
        overscroll-behavior: contain;
        z-index: 60;
        opacity: 0;
        visibility: hidden;
        transition: transform 280ms var(--ease-out), opacity 280ms var(--ease-out),
                    visibility 0s linear 280ms;
    }

    /* 背後淡出的 Hero／頁面遮罩：獨立的全視窗 fixed 偽元素，跟選單區的
       尺寸／位置脫鉤——選單區現在滿版寬、只在垂直方向用 top／
       max-height 縮出「下方露出 Hero」的空間，這片遮罩負責把露出來的
       Hero 壓暗，兩者不能是同一層（選單區自己的實色底只蓋得到它自己
       的高度範圍）。
       ⚠ z-index:-1 是相對於 #wfNav 自己建立的 stacking context（它是
         position:fixed 且有 z-index），不是相對於整個頁面——#wfNav 整體
         仍蓋在 Hero 之上（z-index:60），這裡只決定「遮罩要蓋在卡片自己
         的背景下面、但仍蓋在卡片以外的頁面內容上面」，兩者不衝突。
       ⚠ 2026-08-17 客戶「展開請貼齊白色區塊底部，上方不要留高」之後，
         選單區跟遮罩的 top 都改回 0（見上方殼層規則），兩者上緣重新
         對齊在視窗最頂端，不再刻意錯開。
       ⚠ 2026-08-17 客戶「全站深灰色都改成導覽的深灰色」——遮罩原本讀
         --foreground（黑 90%，接近純黑），跟卡片本身讀的 --surface-dark
         （SLATE #7C7D80，本檔既有「深色塊」SSOT，2026-08-03 客戶指定
         全站黑色塊統一換成這個顏色）是兩種不同的深色，卡片與遮罩疊在
         一起時看得出色差。改讀同一顆 --surface-dark（透明度降到 .72
         做「遮罩」用途，SLATE 本身較淺，opacity 沒拉高的話蓋在明亮的
         Hero 照片上不夠暗），不是另外調一個新灰階，全站其餘「深色塊」
         （知識專區左欄／Hero 遮罩／色帶轉場）也是同一顆 token，這裡改完
         跟它們自動維持同一色。 */
    .site-header .site-nav::before {
        content: "";
        position: fixed;
        inset: 0;
        z-index: -1;
        background: hsl(var(--surface-dark) / .72);
        -webkit-backdrop-filter: blur(6px);
        backdrop-filter: blur(6px);
        opacity: 0;
        transition: opacity 280ms var(--ease-out);
    }

    .site-header .site-nav.is-open {
        /* transform 收到 none（不是 translateY(0)）：面板縮窄後，全螢幕
           遮罩 ::before 是 position:fixed——只要 nav 還帶著 transform，
           fixed 會退化成只罩面板自己（transformed 祖先的既有坑），
           面板右側的頁面就沒有遮罩。開合動畫只在過場吃 transform，
           定格狀態歸 none 讓遮罩鋪滿視窗。 */
        transform: none;
        opacity: 1;
        visibility: visible;
        transition: transform 280ms var(--ease-out), opacity 280ms var(--ease-out);
    }

    .site-header .site-nav.is-open::before {
        opacity: 1;
    }

    @media (prefers-reduced-motion: reduce) {
        .site-header .site-nav,
        .site-header .site-nav::before {
            transition: opacity 1ms linear, visibility 0s;
        }
    }

    /* 關閉鈕：改釘在卡片自己的右上角（相對 #wfNav 定位，它本身就是
       position:fixed，是合法的 containing block），不再飄在瀏覽器
       viewport 的角落——客戶明確要求「不要讓 X 漂在整頁最右上角很遠
       的地方」。用 #wfNavClose 這個既有 id 直接選，specificity 天生
       蓋過本檔既有那幾條 `html:root .site-nav-close{position:fixed;
       top:var(--menu-btn-edge)...}` 系列規則，不需要再跟它們比大小。 */
    #wfNavClose {
        position: absolute !important;
        top: clamp(1rem, 2vw, 1.5rem) !important;
        right: clamp(1rem, 2vw, 1.5rem) !important;
        left: auto !important;
        width: 2.25rem !important;
        height: 2.25rem !important;
        background: transparent !important;
        border: 0 !important;
        z-index: 5;
    }

    /* 服務專區／會員：子選項就地垂直往下接，不是點了才浮出來的懸浮小面板。
       ⚠ position:static 而不是 absolute：Bootstrap／Popper.js 平常靠
         inline style 把它定位成浮動小面板，這裡改回文件流裡的正常區塊，
         才會「貼著觸發鈕垂直往下長」而不是飄在旁邊。
       ⚠ 2026-08-27 客戶「電腦選單服務專區跟會員細項先收合起來，按會員跟
         服務專區在展開」——**推翻 2026-08-17 那輪的「子選項永遠展開」**
         （當時客戶原話是「會員跟服務專區分支選項不要包起來，直接一起
         展開來但字細體」，見本區塊上方那則「客戶追加訂正」）。
         `display` 從 `block !important` 改成 `none !important`＋`.show`
         才 `block !important`——這正是本檔 ≤991.98px 手機版（≈25548／
         ≈25636）早就在用的同一組寫法，不是新發明的機制。
         Bootstrap bundle 有載入（layouts/main.php ≈131），標記上的
         `data-bs-toggle="dropdown"` 從頭到尾都沒拿掉，所以點擊展開／收合
         直接由 Bootstrap 接手，不需要自己寫 JS。 */
    .site-header .site-nav-menu-service .dropdown-menu,
    .site-header .site-nav-menu-member .dropdown-menu {
        display: none !important;
        position: static !important;
        float: none !important;
        inset: auto !important;
        transform: none !important;
        width: auto;
        min-width: 0;
        margin: .25rem 0 0;
        padding: 0;
        border: 0;
        border-radius: 0;
        background: transparent;
        box-shadow: none;
    }

    .site-header .site-nav-menu-service .dropdown-menu.show,
    .site-header .site-nav-menu-member .dropdown-menu.show {
        display: block !important;
    }

    /* ⚠ tombstone：這裡原本把服務專區／會員的 chevron 藏起來
       （`display:none !important`），理由是 2026-08-17 那輪子選項永遠展開、
       箭頭不對應任何真實互動、留著會誤導。2026-08-27 客戶要求改回「點了
       才展開」之後，箭頭重新對應真實的展開／收合互動，**必須讓它顯示
       回來**，所以整條移除（不是改值——留著 display:none 就等於沒有任何
       可展開的視覺提示）。箭頭的尺寸／顏色與 `[aria-expanded="true"]`
       轉 180° 的動態沿用本檔 ≈8275 那組既有基底規則，這裡不必重寫。 */

    /* 「字細體」——不透過 var(--fw-light)：這個 token 在 .site-header
       範圍內被局部重新定義成 350（本檔「桌機 Header 三段式」那段已經
       踩過同一個坑、也是用寫死數字繞開，見 ≈18450 行一帶的說明），
       這裡沿用同一個解法，直接寫死一個比欄位主項更輕的數字。 */
    .site-header .site-nav-menu-service .dropdown-item,
    .site-header .site-nav-menu-member .dropdown-item {
        font-weight: 100;
    }

    /* ── 卡片改深底、文字改白（2026-08-17 客戶追加訂正「導覽頁面文字用
       白色」，卡片底色跟著從 --brand-foreground（白）換成 --surface-dark
       （見上面殼層規則），文字不換色會整組深字疊深底看不見）───────────
       ⚠ 用既有的 --surface-dark-foreground token（tokens.css 既有的
         「深色區塊上的文字」SSOT，跟站上其他深色區塊共用同一顆變數），
         不是另外調一個新白色。
       ⚠ 選擇器故意寫得寬（a／p／span／button／.dropdown-item），不是
         逐一列舉每個既有 class：卡片內文字散在很多既有 class 上
         （.site-nav-label／.site-nav-menu-tagline／
         .site-nav-menu-foot-copy／.font-size-mark…），且各自的 color
         在不同情境（極簡日系風／首頁修正）數值不同，逐一比特異度
         成本很高。這裡用標籤選擇器 + !important 一次性蓋過去，圖示
         SVG 走 currentColor 的部分（wf_icon()、放大鏡、chevron）會
         跟著文字一起變白，不用額外處理；logo 圖示是客戶原始檔案給的
         固定 fill 色（不是 currentColor），不受影響，維持原本品牌色。 */
    .site-header .site-nav a,
    .site-header .site-nav p,
    .site-header .site-nav span,
    .site-header .site-nav button,
    .site-header .site-nav .dropdown-item {
        color: hsl(var(--surface-dark-foreground)) !important;
    }

    .site-header .site-nav > a,
    .site-header .site-nav > .dropdown,
    .site-header .site-nav-menu-foot,
    .site-header .site-nav .dropdown-item {
        border-color: hsl(var(--surface-dark-foreground) / .15) !important;
    }

    .site-header .site-nav-section-label,
    .site-header .site-nav-menu-tagline {
        color: hsl(var(--surface-dark-foreground) / .55) !important;
    }

    .site-header .site-nav .font-size-btn {
        border-color: hsl(var(--surface-dark-foreground) / .3) !important;
    }

    .site-header .site-nav .font-size-btn.is-active {
        border-bottom-color: hsl(var(--surface-dark-foreground)) !important;
    }

    /* 客戶追加訂正：「logo也要白色」——logo 文字（.site-nav-logo-text）
       是普通 CSS color，上面 span 那條廣泛規則已經蓋到；但圖示是
       inline SVG 的 fill 屬性（客戶原始檔案給的固定色 #4a4636，不是
       currentColor，見 _header.php 註解），CSS color 不會連帶影響
       fill，這裡另外補一條，卡片深底情境下圖示也跟著轉白。 */
    .site-header .site-nav-logo-icon path {
        fill: hsl(var(--surface-dark-foreground)) !important;
    }

    /* 客戶追加訂正：「選項的搜尋粗度要跟其他主項的一樣」——「搜尋」
       這裡沿用 var(--fw-normal)，跟 NAVIGATION／MEMBER 欄其他主項
       （首頁／實體課程…）讀的是同一顆變數，不是另外猜一個數字湊，
       這樣不管那顆 token 會不會被污染，兩邊永遠算出同一個值。 */
    .site-header .site-nav .site-search .site-search-label {
        font-weight: var(--fw-normal) !important;
    }

    /* 客戶追加訂正：「次項左邊不要縮頭跟主項靠左對齊」＋「文字每行位置
       都不一樣請段落齊頭」——服務專區／會員展開後的子選項（顧問案／
       AI 分身／登入／註冊）原本沿用「極簡日系風」那組的左內縮
       （2.75rem 或 1.5rem，各情境數值不同），跟主項的左緣對不齊，兩組
       子選項彼此也不一致（會員那組原本因為某個情境沒有內縮規則吃到、
       服務專區那組有，兩邊長得不一樣）。這裡統一歸零，子選項左緣貼齊
       主項左緣。 */
    .site-header .site-nav-menu-service .dropdown-item,
    .site-header .site-nav-menu-member .dropdown-item {
        padding-left: 0 !important;
        margin-left: 0 !important;
    }

    /* 客戶追加訂正：「aaa跟上面英文字靠左對齊」＋「aaa請直排呈現不要
       橫排」——A/A+/A++ 原本是水平並排（沿用 .font-size-switch 全站
       共用的 display:flex 預設橫向），且容器本身有內距/置中對齊，跟
       正上方 TOOLS 小標的左緣對不齊。這裡改直排（flex-direction:
       column）、清空內距與置中設定，讓它跟 TOOLS 小標一樣從欄位最
       左緣開始。 */
    .site-header .site-nav > .font-size-switch {
        flex-direction: column !important;
        align-items: flex-start !important;
        justify-content: flex-start !important;
        /* 2026-08-17 客戶「aaa之間的行距拉大」——與 B/C 殼那份同值 1.25rem。 */
        gap: 1.25rem !important;
        /* ⚠ align-self 一定要釘 start：預設 stretch/置中會讓整組隨容器
           自身高度上下漂移（加一顆 margin 整組就位移，實測踩過）。
           margin-top 1.0625rem ＝ 會員連結的 padding-top 加行盒字形偏移
           （1920 實測 17px），讓第一顆 A 的字首正好齊「會員」。 */
        align-self: start !important;
        margin: 1.0625rem 0 0 !important;
        padding: 0 !important;
    }

    /* 2026-08-17 客戶「選單第一個a齊頭會員，第二個a齊頭登入，第三個a
       齊頭註冊」——會員→登入的距離比登入→註冊多一段（dropdown 展開的
       margin-top 與選單內距），均一 gap 只對得齊後兩顆：gap 1.25rem 剛好
       等於「登入→註冊」的行距，A+ 再補 .5625rem（1920 實測差 9px）吃掉
       「會員→登入」多出來的那段。rem 寫法讓字級切換時等比跟著縮放。
       與 B/C 殼那份同值。 */
    .site-header .site-nav > .font-size-switch .font-size-btn--lg {
        margin-top: .5625rem !important;
    }

    /* 手機（≤991.98px）：改回單欄堆疊，接近全螢幕但保留卡片感（客戶
       「手機版可以維持較接近全螢幕選單，但仍要保留卡片式與分組感」）。
       ⚠ 用 display:flex 取代 grid：子項目上面那組 grid-column／
         grid-row（≥992px 才寫）本來就不會生效，不需要逐一歸零，父層
         換成非 grid 容器，這些屬性自動變成沒有作用的宣告。
       ⚠ 沿用 DOM 既有順序（CTA → NAVIGATION → MEMBER → TOOLS →
         版權列）剛好就是客戶要的「上方品牌 CTA／下方分組導覽」堆疊
         順序，不需要另外調整來源順序。 */
    @media (max-width: 991.98px) {
        .site-header .site-nav {
            display: flex !important;
            flex-direction: column;
            align-items: stretch;
            top: 4vh;
            max-width: 92vw;
            width: 92vw;
            max-height: 88vh;
            padding: 2.25rem 1.5rem 1.75rem;
            row-gap: 0;
        }

        .site-header .site-nav-menu-tagline {
            max-width: none;
        }

        .site-header .site-nav > .dropdown,
        .site-header .site-nav > a {
            margin-bottom: .35rem !important;
        }

        .site-header .site-nav-section-label {
            margin-top: 1.5rem !important;
        }

        .site-header .site-nav-section-label--first {
            margin-top: .25rem !important;
        }

        .site-header .site-nav-menu-foot {
            margin-top: 2rem;
        }
    }
}

/* B／C. ≥1468px：.is-compact（全站，捲動後重用同一顆抽屜）與首頁專屬
   （未捲動時）兩種情境共用同一份卡片殼——內容排版沿用上面
   `@media (min-width: 992px)` 那組共用規則，這裡只需要重寫「殼」本身
   （定位／尺寸／背景／開合動畫），寫法整段比照 A 區segment，僅選擇器
   前綴不同。刻意不用 `html:root`（那樣會連「完整展開式導覽」那個
   :not(.is-compact) 的桌機靜止態也一起蓋到）——`html.is-compact` 與
   `html:has(.hero):not(.is-compact)` 這兩個條件精準對應 B／C 兩種情境，
   結構上就不可能命中「非首頁、未捲動」那個唯一要保留原樣的狀態。 */
@media (min-width: 1468px) {
    /* ⚠ 2026-08-17 客戶附截圖訂正「Menu 從頂部展開、滿版寬」——同 A 區
       的說明，這裡是同一組改動在 ≥1468px 這兩種情境（is-compact／首頁
       未捲動）各自的殼層規則。 */
    html.is-compact .site-header .inner > .site-nav,
    html:has(.hero):not(.is-compact) .site-header .inner > .site-nav {
        display: grid !important;
        grid-template-columns: minmax(180px, 240px) repeat(3, minmax(150px, 1fr));
        grid-auto-rows: min-content;
        column-gap: clamp(2rem, 3.5vw, 3.5rem);
        row-gap: .625rem;
        align-content: start;

        position: fixed;
        inset: 0;
        width: auto;
        height: auto;
        top: 0;
        left: 0;
        right: 0;
        bottom: auto;
        transform: translateY(-16px);
        max-width: none;
        max-height: min(720px, 78vh);
        margin: 0;
        padding: clamp(2rem, 3.5vw, 3rem) clamp(2.5rem, 4vw, 4.5rem) clamp(1.5rem, 3vw, 2rem);
        border: 0;
        border-radius: var(--radius);
        background: hsl(var(--surface-dark));
        box-shadow: 0 24px 60px hsl(var(--foreground) / .18);
        overflow-y: auto;
        overscroll-behavior: contain;
        z-index: 60;
        opacity: 0;
        visibility: hidden;
        transition: transform 280ms var(--ease-out), opacity 280ms var(--ease-out),
                    visibility 0s linear 280ms;
    }

    /* 遮罩 inset 同 A 區，跟選單區一起貼齊視窗最頂端。 */
    html.is-compact .site-header .inner > .site-nav::before,
    html:has(.hero):not(.is-compact) .site-header .inner > .site-nav::before {
        content: "";
        position: fixed;
        inset: 0;
        z-index: -1;
        background: hsl(var(--surface-dark) / .72);
        -webkit-backdrop-filter: blur(6px);
        backdrop-filter: blur(6px);
        opacity: 0;
        transition: opacity 280ms var(--ease-out);
    }

    html.is-compact .site-header .inner > .site-nav.is-open,
    html:has(.hero):not(.is-compact) .site-header .inner > .site-nav.is-open {
        transform: translateY(0);
        opacity: 1;
        visibility: visible;
        transition: transform 280ms var(--ease-out), opacity 280ms var(--ease-out);
    }

    html.is-compact .site-header .inner > .site-nav.is-open::before,
    html:has(.hero):not(.is-compact) .site-header .inner > .site-nav.is-open::before {
        opacity: 1;
    }

    @media (prefers-reduced-motion: reduce) {
        html.is-compact .site-header .inner > .site-nav,
        html:has(.hero):not(.is-compact) .site-header .inner > .site-nav,
        html.is-compact .site-header .inner > .site-nav::before,
        html:has(.hero):not(.is-compact) .site-header .inner > .site-nav::before {
            transition: opacity 1ms linear, visibility 0s;
        }
    }

    /* 唯一要保留原樣的第四種情境（非首頁、未捲動的完整展開式導覽，
       ≈18342 行）此時 #wfNav 是 display:flex!important 的行內水平列，
       不是這裡的浮卡——這兩個新增的「只在卡片模式才有意義」的元素
       （CTA 說明文／底部版權列）若不特別隱藏，會以未套樣式的普通
       flex item 之姿混進那排水平導覽，客戶並未要求改那個狀態，這裡
       明確排除。分欄小標（.site-nav-section-label）與 AAA 本身已有
       既有規則在那個狀態隱藏／各自處理，不需要在這裡重複。 */
    html:not(.is-compact):not(:has(.hero)) .site-header .site-nav-menu-tagline,
    html:not(.is-compact):not(:has(.hero)) .site-header .site-nav-menu-foot {
        display: none !important;
    }

    /* 關閉鈕同 A 區，改釘在卡片右上角。 */
    #wfNavClose {
        position: absolute !important;
        top: clamp(1rem, 2vw, 1.5rem) !important;
        right: clamp(1rem, 2vw, 1.5rem) !important;
        left: auto !important;
        width: 2.25rem !important;
        height: 2.25rem !important;
        background: transparent !important;
        border: 0 !important;
        z-index: 5;
    }

    /* 服務專區／會員：子選項永遠展開、就地垂直往下接，同 A 區規則，
       說明見本檔「客戶追加訂正」那則註解。
       ⚠ 選擇器一定要重複 html.is-compact／html:has(.hero):not(.is-compact)
         這兩個字首，不能只寫 `.site-header .site-nav-menu-service...`——
         這裡雖然包在 `@media(min-width:1468px)` 裡，但媒體查詢只限制
         寬度，不會連帶限制「只在這個 @media 裡面的其他規則用過的 html
         class 條件」，沒有字首的話會連桌機靜止態「完整展開式導覽」
         （同一個寬度、但沒有 .is-compact 也沒有 .hero）也一起选中，
         把那個沒人要求改的狀態的下拉也強制展開——這裡第一版就漏寫了
         字首，實測前先自查抓到並修正。 */
    /* ⚠ 2026-08-27 客戶「電腦選單服務專區跟會員細項先收合起來，按會員跟
       服務專區在展開」——同 A 區（≈23189）那則說明，`display` 由
       `block !important` 改成 `none !important`＋`.show` 才顯示。
       ⚠ **兩個區間都要改**：這一組是 ≥1468px，A 區那組是 ≤1467.98px，
         客戶截圖是寬桌機（吃這一組），但只改一邊會讓 992–1467.98 那段
         維持舊的「永遠展開」，變成同一個元件在不同視窗寬度行為不一致。 */
    html.is-compact .site-header .site-nav-menu-service .dropdown-menu,
    html.is-compact .site-header .site-nav-menu-member .dropdown-menu,
    html:has(.hero):not(.is-compact) .site-header .site-nav-menu-service .dropdown-menu,
    html:has(.hero):not(.is-compact) .site-header .site-nav-menu-member .dropdown-menu {
        display: none !important;
        position: static !important;
        float: none !important;
        inset: auto !important;
        transform: none !important;
        width: auto;
        min-width: 0;
        margin: .25rem 0 0;
        padding: 0;
        border: 0;
        border-radius: 0;
        background: transparent;
        box-shadow: none;
    }

    html.is-compact .site-header .site-nav-menu-service .dropdown-menu.show,
    html.is-compact .site-header .site-nav-menu-member .dropdown-menu.show,
    html:has(.hero):not(.is-compact) .site-header .site-nav-menu-service .dropdown-menu.show,
    html:has(.hero):not(.is-compact) .site-header .site-nav-menu-member .dropdown-menu.show {
        display: block !important;
    }

    /* ⚠ tombstone：這裡原本比照 A 區把 chevron 藏起來，理由同 A 區
       （子選項永遠展開時箭頭不對應真實互動）。2026-08-27 改回「點了才
       展開」之後箭頭要看得見，整條移除；尺寸／顏色／展開轉 180° 沿用
       ≈8275 那組基底規則。 */

    html.is-compact .site-header .site-nav-menu-service .dropdown-item,
    html.is-compact .site-header .site-nav-menu-member .dropdown-item,
    html:has(.hero):not(.is-compact) .site-header .site-nav-menu-service .dropdown-item,
    html:has(.hero):not(.is-compact) .site-header .site-nav-menu-member .dropdown-item {
        font-weight: 100;
    }

    /* 卡片改深底、文字改白／搜尋粗度對齊主項／子選項齊頭／AAA 直排靠左
       ——同 A 區規則，說明見本檔「客戶追加訂正」那幾則註解，這裡選擇器
       重複加上 html.is-compact／html:has(.hero):not(.is-compact) 字首，
       理由同上（避免連桌機靜止態的完整展開式導覽也選中）。 */
    html.is-compact .site-header .site-nav a,
    html.is-compact .site-header .site-nav p,
    html.is-compact .site-header .site-nav span,
    html.is-compact .site-header .site-nav button,
    html.is-compact .site-header .site-nav .dropdown-item,
    html:has(.hero):not(.is-compact) .site-header .site-nav a,
    html:has(.hero):not(.is-compact) .site-header .site-nav p,
    html:has(.hero):not(.is-compact) .site-header .site-nav span,
    html:has(.hero):not(.is-compact) .site-header .site-nav button,
    html:has(.hero):not(.is-compact) .site-header .site-nav .dropdown-item {
        color: hsl(var(--surface-dark-foreground)) !important;
    }

    html.is-compact .site-header .site-nav > a,
    html.is-compact .site-header .site-nav > .dropdown,
    html.is-compact .site-header .site-nav-menu-foot,
    html.is-compact .site-header .site-nav .dropdown-item,
    html:has(.hero):not(.is-compact) .site-header .site-nav > a,
    html:has(.hero):not(.is-compact) .site-header .site-nav > .dropdown,
    html:has(.hero):not(.is-compact) .site-header .site-nav-menu-foot,
    html:has(.hero):not(.is-compact) .site-header .site-nav .dropdown-item {
        border-color: hsl(var(--surface-dark-foreground) / .15) !important;
    }

    html.is-compact .site-header .site-nav-section-label,
    html.is-compact .site-header .site-nav-menu-tagline,
    html:has(.hero):not(.is-compact) .site-header .site-nav-section-label,
    html:has(.hero):not(.is-compact) .site-header .site-nav-menu-tagline {
        color: hsl(var(--surface-dark-foreground) / .55) !important;
    }

    html.is-compact .site-header .site-nav .font-size-btn,
    html:has(.hero):not(.is-compact) .site-header .site-nav .font-size-btn {
        border-color: hsl(var(--surface-dark-foreground) / .3) !important;
    }

    html.is-compact .site-header .site-nav .font-size-btn.is-active,
    html:has(.hero):not(.is-compact) .site-header .site-nav .font-size-btn.is-active {
        border-bottom-color: hsl(var(--surface-dark-foreground)) !important;
    }

    html.is-compact .site-header .site-nav-logo-icon path,
    html:has(.hero):not(.is-compact) .site-header .site-nav-logo-icon path {
        fill: hsl(var(--surface-dark-foreground)) !important;
    }

    html.is-compact .site-header .site-nav .site-search .site-search-label,
    html:has(.hero):not(.is-compact) .site-header .site-nav .site-search .site-search-label {
        font-weight: var(--fw-normal) !important;
    }

    html.is-compact .site-header .site-nav-menu-service .dropdown-item,
    html.is-compact .site-header .site-nav-menu-member .dropdown-item,
    html:has(.hero):not(.is-compact) .site-header .site-nav-menu-service .dropdown-item,
    html:has(.hero):not(.is-compact) .site-header .site-nav-menu-member .dropdown-item {
        padding-left: 0 !important;
        margin-left: 0 !important;
    }

    html.is-compact .site-header .site-nav > .font-size-switch,
    html:has(.hero):not(.is-compact) .site-header .site-nav > .font-size-switch {
        flex-direction: column !important;
        align-items: flex-start !important;
        justify-content: flex-start !important;
        /* 2026-08-17 客戶「aaa之間的行距拉大，三個現在上下個別沒空間
           都擠在一起」——.5rem 拉大到 1.25rem（A 殼那份同步改，兩殼
           永遠同值）。 */
        gap: 1.25rem !important;
        /* align-self 釘 start＋margin-top 對齊「會員」字首，說明見 A 殼
           同名規則；此值兩殼同步。 */
        align-self: start !important;
        margin: 1.0625rem 0 0 !important;
        padding: 0 !important;
    }

    /* 2026-08-17 客戶「選單第一個a齊頭會員，第二個a齊頭登入，第三個a
       齊頭註冊」——說明見 A 殼同名規則；此值兩殼同步。 */
    html.is-compact .site-header .site-nav > .font-size-switch .font-size-btn--lg,
    html:has(.hero):not(.is-compact) .site-header .site-nav > .font-size-switch .font-size-btn--lg {
        margin-top: .5625rem !important;
    }
}

/* ── Hero 釘住＋服務專區簾幕式疊上（2026-08-17 客戶定案）────────────
   客戶：「hero 到服務專區要貼邊、但 hero 區域又需要底部留空間」——
   捲動時 hero 固定在視窗原位，後續區塊從下方滑上來蓋過它：
   hero 的底部留白構圖永遠完整（不會捲到一半被切掉），兩區之間天生無縫。
   ⚠ sticky 元素屬 positioned，預設反而會蓋住後面的 static 兄弟，
     所以後續兄弟一律補 position:relative + z-index:1 蓋回來。
   ⚠ 只在 ≥1468（完整 Card 版面）生效；992–1467 簡化卡與手機維持原本流動。
   ⚠ header 收合（.is-compact）的觸發 sentinel 原本就是 .hero 本身——
     釘住後它永遠在視窗內、收合會失效，home.php 補了 .hero-end-sentinel
     （0 高、留在文件流跟著捲）給 home.js 觀察，觸發時機與改前相同。
   ⚠ 2026-08-27 下界跟著 Card 版面一起降到 1200——上面那句「≥1468（完整
     Card 版面）」的前提已經變成 ≥1200，簾幕式疊上是 Card 版面的一部分，
     不跟著延伸的話 1200–1467 會變成「版型是桌機、捲動行為卻是手機」。 */
@media (min-width: 1024px) {
    .hero {
        position: sticky;
        top: 0;
    }

    .hero ~ * {
        position: relative;
        z-index: 1;
    }
}

/* 哨兵給 1px 高再用負 margin 收回：零面積元素在 IntersectionObserver
   的相交判斷是邊緣行為（intersectionRatio 恆為 0），給實際高度最穩。 */
.hero-end-sentinel {
    height: 1px;
    margin-bottom: -1px;
}

/* ── 首頁全區塊堆疊捲動（2026-08-17 客戶追加「首頁每個區塊都用這種效果」）──
   服務專區／實體課程／知識專區也照 Hero 的方式釘住、被下一塊蓋過。
   ⚠ 比視窗高的區塊不能釘在 top:0（釘住後下半段內容永遠捲不出來），
     要等它自己捲到「底緣貼齊視窗底」（內容全部看過一遍）才釘：
     top = 100svh − 區塊高（負值）。區塊高由 home.js 檔尾的 ResizeObserver
     量進 --stack-h（lazy 圖片載入、清單展開都會改高度，量一次不夠）；
     JS 沒跑時 fallback = 100svh → top:0，矮區塊行為正確，高區塊退化成
     提早釘住（no-js 本來就是降級狀態）。
   ⚠ 知識專區是 main 內最後一個區塊，sticky 的活動範圍到父層（main）底緣
     為止，天生沒有空間可釘＝維持原本流動、也不會反過來蓋住 main 外的
     footer——這是刻意依賴的行為，之後若把 footer 搬進 main 要重新檢查。
   ⚠ 疊層順序不用另外給 z-index：這幾個區塊都是 positioned（sticky）＋
     同為 .hero ~ * 的 z-index:1，同位階由 DOM 順序決定，後寫的天生在上。
   ⚠ 這條要寫在上面 `.hero ~ *` 那條之後：兩者同特異度（0,1,0），
     position 靠「後寫的贏」才會是 sticky 而不是 relative。 */
@media (min-width: 1468px) {
    .service-section,
    .course-section,
    .knowledge-section {
        position: sticky;
        top: min(0px, calc(100svh - var(--stack-h, 100svh)));
    }

    /* 2026-08-18 客戶「服務專區滑到實體課程的高設高一點再讓實體課程
       疊上來，實體課程疊上來太快了，服務專區都還沒瀏覽看完」——
       服務專區內容（1920 實測 843px）比視窗矮，釘住那一刻下一區已經
       爬進畫面 237px、立刻開始疊。拉高到整屏：捲到整區完整入鏡、
       釘在滿版狀態後，實體課程才從視窗底邊開始疊上來，瀏覽節奏由
       使用者自己的捲動控制。多出來的高度落在區塊底部、呈現為米白
       呼吸帶（色塊出血只到 .svc-layout 周圍，不會跟著拉長）。
       ⚠ 用 min-height 不是 height：小視窗高度下內容比 100svh 高時，
         照樣由內容撐高、走「捲完才釘」的公式，不會裁到內容。 */
    .service-section {
        min-height: 100svh;
    }
}

/* ── 抽屜細線統一（2026-08-17 客戶「滾動後的選單樣式請統一成hero的版本
   不加細線」）────────────────────────────────────────────────────────
   B 殼（html.is-compact，捲動後）沿用 992–1467 漢堡抽屜的舊樣式，帶了
   兩條 C 殼（首頁未捲動）沒有的細線：子選項（.dropdown-item）每項一條
   白色底線、AAA 區塊上緣一條分隔線（實測兩殼 diff 抓出，只差這兩處）。
   統一以 C 殼為準，兩條都拿掉。
   ⚠ 只動 ≥992 桌機抽屜；手機（≤991.98）維持原樣。
   ⚠ .font-size-btn 的白色底線是「目前字級」的選中指示、頁尾資訊列
     （.site-nav-menu-foot）上緣線兩殼本來就一致——都不是這次說的細線，
     保留。 */
@media (min-width: 992px) {
    html.is-compact .site-header .site-nav .dropdown-item {
        border-bottom: 0 !important;
    }

    html.is-compact .site-header .site-nav > .font-size-switch {
        border-top: 0 !important;
    }
}

/* ── 全站 Header 統一不展開（2026-08-17 客戶指定）────────────────────
   客戶：「全站的header統一不展開，只在左上角放logo品牌名，右邊aaa跟
   搜尋還有選單icon」。做法＝home.js 讓 ≥992 永遠處於 is-compact，三種
   header 情境（完整展開式導覽／首頁 Hero 版／捲動收合）塌縮成收合一種
   ——html:not(.is-compact) 與 html:has(.hero):not(.is-compact) 的既有
   header 規則全部自然失效（狀態不再出現），一行都不用刪；本區塊再把
   收合態從「只剩右上角方形鈕」補成客戶要的單列：
   左＝logo＋品牌名，右＝A/A+/A++＋搜尋＋選單 icon。
   ⚠ <992 手機維持原本「捲動離開頁首才收合」的行為與外觀，不在此範圍。
   ⚠ 選單開著（.has-menu-open）時整列照舊被既有「整組蓋掉」規則隱藏，
     這裡全部用 :not(.has-menu-open) 限定，不跟它打架。 */
@media (min-width: 992px) {
    /* 推翻收合態「只剩方形鈕」的隱藏（本檔 ≈8632）：三個元素回到列上 */
    html.is-compact:not(.has-menu-open) .site-header .site-logo {
        display: inline-flex;
        align-items: center;
        gap: .625rem;
        margin-right: auto;   /* logo 靠左，其餘元素全部推到右緣 */
    }

    html.is-compact:not(.has-menu-open) .site-header > .inner > .font-size-switch {
        display: flex;        /* 沿用全站預設的橫向排列 */
        order: 1;
    }

    html.is-compact:not(.has-menu-open) .site-header .site-nav-primary {
        display: inline-flex;
        align-items: center;
        order: 2;
    }

    /* 品牌名文字：漢堡斷點（992–1467）既有規則會把 .site-logo-text 藏起來，
       統一列一定要看得到品牌名，強制放行。 */
    html.is-compact:not(.has-menu-open) .site-header .site-logo-text {
        display: inline !important;
    }

    /* 列容器：單列 flex、垂直置中。header 本體維持既有的
       position:fixed＋透明底（≈8638 那組），只把 56px 的寫死高度放開，
       讓列高由內容物自己決定。 */
    html.is-compact:not(.has-menu-open) .site-header {
        height: auto;
    }

    /* ⚠ 2026-08-25 客戶「header 不要 padding top」——上下原本都是
       .875rem(14px)，上緣歸零、下緣維持不動（客戶只講 top）。header 高度
       因此從 87px 收到 73px，logo/選單整列往上貼齊視窗頂。
       ⚠ 不需要同步改任何「讓開 header」的算式：那些讀的是 site.js 用
         ResizeObserver 現量的 --header-clearance-extra（見本檔 ≈14024
         與 --wf-crumb-total 那節），header 一變矮，量到的值自己就跟著
         變小，麵包屑與色帶拉頂量會自動跟上——這正是當初不寫死數字、
         改用 JS 現量的原因。

       ⚠ 2026-08-26 客戶「header似乎沒有垂直置中」（附截圖）——上面那輪
         「上緣歸零、下緣不動」留下一組不對稱內距（上 0／下 14px），
         `align-items:center` 是在**內容盒**裡置中，內容盒被下內距往上擠，
         於是整列比 header 外框中心高了 7.5px（實測：header 中心 36.3、
         logo／AAA／搜尋 icon／選單鈕四者中心全部落在 28.8）。
         修法＝把原本那 14px 拆成上下各 7px（.4375rem），**header 總高
         維持 72.6px 不變**——刻意不用「下緣也歸零」那種做法：那會讓
         header 從 73px 掉到 59px，牽動 --header-real-h 與所有貼齊
         header 底緣的算式（麵包屑、Hero 圖片對齊…），而客戶這次只說
         沒置中、沒說要更矮。這樣改也沒有推翻 08-25 那條指示的本意
         （當時要的是「整列不要被推得太低、header 不要那麼高」，現在
         高度一樣、只是把偏移補回中心）。 */
    html.is-compact:not(.has-menu-open) .site-header > .inner {
        display: flex !important;
        align-items: center;
        gap: 1.5rem;
        padding: .4375rem var(--section-x);
    }

    /* 方形選單鈕收回列內：拿掉 fixed 浮動定位；同時依客戶
       「滾動後選單icon不要壓白底跟描線」拿掉白底與邊框，回到跟
       Hero 版一樣的純三條線。尺寸維持原 clamp，點擊目標不縮水。 */
    html.is-compact:not(.has-menu-open) .site-header .site-nav-toggle {
        position: static;
        order: 3;
        background: transparent;
        border: 0;
        box-shadow: none;
    }

    /* 搜尋 icon 也收回列內：原本（Header 重新分層那輪）是 fixed 疊在
       方形鈕左邊、跟方形鈕共用同一組座標算式（≈18045），統一列改由
       flex 的 order 排序，繼續 fixed 會疊在選單鈕上（實測 x1768/1774
       重疊）。 */
    html.is-compact:not(.has-menu-open) .site-header .site-nav-primary {
        position: static;
        width: auto;
        height: auto;
        margin: 0;
    }

    /* 首頁的 --header-ink 白色分支（2026-08-14「首頁選單白色細字」，
       設計給疊在深色照片 Hero 上）：統一列現在疊的是淺色卡片，白色
       等於隱形。首頁也改用非首頁同一個黑 70%，全站一個樣。 */
    html:has(.hero).is-compact .site-header {
        --header-ink: hsl(var(--heading) / .7);
    }

    /* 首頁 logo 圖示／品牌名文字：2026-08-14「logo在首頁要白色」是
       直接寫死 brand-foreground（不走 --header-ink，見 ≈2605），上面
       重定義 ink 蓋不到它，這裡多疊一個 .is-compact 特異度拉回深色。 */
    html:has(.hero).is-compact .site-logo-icon path {
        fill: var(--header-ink, hsl(var(--heading) / .7));
    }

    html:has(.hero).is-compact .site-logo-text {
        color: var(--header-ink, hsl(var(--heading) / .7));
    }
}

/* ── 實體課程頁 Header 維持預設深色（2026-08-18 客戶「該區塊背景色
   改圖片這個顏色…header也是」：.cx-hero 底色從深灰改成暖米色之後，
   header 常駐的 --header-ink 預設分支（非 .hero 頁一律黑 70%，見
   ≈2996）疊在米色底上對比已經足夠，不再需要額外開白色分支覆寫——
   曾經有一組 .is-hero-band-visible 覆寫規則專門處理「深灰底需要白
   header」，隨背景改色一併移除；home.js 那顆 IntersectionObserver
   訊號本身不影響其他機制，留著不動。 */

/* ── Footer 改抽屜（導覽展開）樣式（2026-08-18 客戶指定＋淺灰色票截圖）──
   客戶：「footer樣式改成像導覽展開的樣式，但背景改成淺灰底」。
   色票像素實測 rgb(246,246,247)＝既有 --background（站上的米白就是這個
   淺灰），不新增色票。對照抽屜的視覺語言逐項移植：
     欄標   → 比照 .site-nav-section-label（eyebrow 小字、meta 字距、slate）
     項目   → 比照抽屜主項（heading 色、--fw-normal、行距拉開）
     版權列 → 比照 .site-nav-menu-foot（同底色＋上緣 hairline＋小字 45%），
              不再是深灰色帶
     細線   → 比照抽屜「不加細線」：欄與欄的直線維持關閉（--footer-line
              既有值已是 transparent），只留頂部區塊分界與版權列上緣兩條
              hairline（色票截圖下緣那條 rgb(224,224,225) ≈ foreground/.1）
   內容一項不動（品牌欄／連結欄／訂閱表單／版權與隱私權連結），只換視覺。 */
.site-footer {
    background: hsl(var(--background));
}

/* 頂部區塊分界（2026-08-18 最終定案：「我是說全部footer都不要加線才對」
   ——全站一律無線，分界靠留白）。
   ⚠ 這條線同日來回四次：全站加 → 首頁不加 → 全站不要 → 「加線」→
     最終確認全站都不要。之後再要改只動這一條，不要開任何頁面特例。
   padding-top：客戶「footer padding-top留大概200px」——上緣留
   12.5rem（預設字級＝200px，rem 寫法讓 A+/A++ 字級切換等比縮放；
   原本 shorthand 給的是 5.5rem，這裡只蓋 top，左右/下不動）。 */
.footer-main {
    border-top: 0;
    padding-top: 12.5rem;
}

/* 品牌 logo＋文字：舊規則因「footer 固定深灰底」寫死白色（≈2618），
   淺灰底上會隱形，改深色（與 header 統一列同視覺）。 */
.footer-logo-icon path {
    fill: hsl(var(--heading));
}

.footer-logo-text {
    color: hsl(var(--heading));
}

/* 層級對映照抽屜：「服務專區／實體課程／知識專區／訂閱電子報」是同級
   主項（深色、--fw-normal），子項（顧問案／AI 分身）才是淺色細字——
   欄標與「無標題欄的單一連結」都吃主項樣式，兩者同列同級；
   有標題的欄，其清單項目吃子項樣式。
   ⚠ 無標題欄的清單 margin-top 歸零，第一個連結才會跟旁邊欄的標題
     同一列齊頭（跟抽屜「跟右邊對齊頭」同一套要求）。 */
.footer-col-title {
    font-size: var(--text-body);
    line-height: calc(24 / 14);
    font-weight: var(--fw-normal);
    color: hsl(var(--heading));
}

.footer-col:has(.footer-col-title) .footer-col-list {
    margin-top: 1.1rem;
    gap: 1.1rem;
}

.footer-col:not(:has(.footer-col-title)) .footer-col-list {
    margin-top: 0;
    gap: 1.1rem;
}

/* ⚠ line-height 要明寫回 24/14：後面有規則把清單連結蓋成 --lh-body
   （實測 29.4px），行盒比標題高、同列文字會往下掉 2px。 */
.footer-col:not(:has(.footer-col-title)) .footer-col-list a {
    font-weight: var(--fw-normal);
    line-height: calc(24 / 14);
    color: hsl(var(--heading));
}

.footer-col:has(.footer-col-title) .footer-col-list a {
    font-weight: var(--fw-light);
    line-height: calc(24 / 14);
    color: hsl(var(--muted-foreground));
}

.footer-col-list a:hover,
.footer-col-list a:focus-visible {
    color: hsl(var(--heading) / .55);
}

/* 訂閱輸入框：footer 底改回米白後，原本「米白色塊」的輸入框會隱形，
   改純白維持「以色塊做區隔」的既有做法。
   ⚠ 不用 --card：實測它也是 246,246,247（跟 --muted 一樣整顆漂移過，
     見 token 註解會過期的既有教訓），改用站上當純白用的
     --brand-foreground（抽屜的白面板同一顆）。 */
.footer-input {
    background: hsl(var(--brand-foreground));
}

/* 版權列：比照抽屜 .site-nav-menu-foot——同底色、上緣 hairline、小字。
   深灰色帶取消，字重 token 跟著回到米白區那組（350/450）。 */
.footer-bottom {
    --fw-light:  350;
    --fw-normal: 450;

    background: transparent;
    margin-inline: var(--footer-pad-x);
    padding: 1.25rem 0 2rem;
    border-top: 1px solid hsl(var(--foreground) / .1);
}

.footer-copy {
    font-family: var(--font-eyebrow);
    font-size: var(--text-meta);
    letter-spacing: .04em;
    color: hsl(var(--heading) / .45);
}

.footer-legal a {
    color: hsl(var(--heading) / .45);
}

.footer-legal a:hover,
.footer-legal a:focus-visible {
    color: hsl(var(--heading));
}

/* ── Footer 版型也比照抽屜（2026-08-18 客戶「請排版也像導覽的樣子」）──
   上一輪換了層級與配色，這輪把「排版」也對齊抽屜：
     ・導覽兩欄（服務專區＋子項／實體課程＋知識專區）併成**同一欄直向
       堆疊**——抽屜 NAVIGATION 欄的排列；原本 2026-07-31 指定的
       「實體課程跟知識專區橫向排」在這一欄改回直排（新指示蓋舊指示，
       只蓋 ≥992 桌機，手機維持原樣，資料端 'inline' 旗標不動）。
     ・品牌欄靠左、訂閱欄靠右，各自撐滿導覽欄的整高——抽屜「左品牌
       CTA／右工具欄」的骨架。
   ⚠ 不新增 NAVIGATION 這類英文小標（「以圖片有的文字就好，不要自行
     加字」是既有指示，排版像即可、不添字）。
   ⚠ 992–1199.98 有舊斷點把訂閱欄搬到整寬第二列（≈2861），這裡把
     grid-column／border-top／padding-top 一併蓋回，三欄版型全程成立。 */
/* 英文小字分區標（2026-08-18 客戶「上面一樣要加英文小字分區」）：
   NAVIGATION／MEMBER／NEWSLETTER，字級/字距/顏色比照抽屜的
   .site-nav-section-label 同一套配方。 */
.footer-sec-label {
    margin: 0 0 1.75rem;
    font-family: var(--font-eyebrow);
    font-size: var(--text-meta);
    line-height: var(--lh-meta);
    letter-spacing: var(--tracking-meta);
    color: hsl(var(--slate));
}

@media (min-width: 768px) {
    /* 2026-08-18 客戶再附抽屜 MEMBER 欄截圖「這個欄位也要加上去footer」——
       版型從三欄擴成四欄，正好一一對應抽屜骨架：
       品牌｜導覽堆疊（NAVIGATION）｜會員堆疊（MEMBER）｜訂閱（原 TOOLS 位）。
       ⚠ 2026-08-21 客戶「footer 平板可以改跟電腦寬一樣的呈現方式嗎」
         （點名 768／868／1024）——斷點從 992 下修到 768，四欄版直接
         涵蓋平板；三個手機 footer 區塊（≈24363 起）同步收到 ≤767.98。
         768–991 區間的舊規則（.footer-col padding-left:0、inline 清單
         改直排，≈9518）方向與四欄版一致且被本區塊蓋掉，已核無衝突。 */
    .footer-main {
        grid-auto-flow: row;
        grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1.3fr);
        column-gap: clamp(2rem, 3.5vw, 3.5rem);
        row-gap: 1.1rem;
        align-content: start;
    }

    .footer-brand {
        grid-column: 1;
        grid-row: 1 / span 2;
    }

    .footer-main > nav.footer-col {
        padding-left: 0;
        border-left: 0;
    }

    .footer-main > nav.footer-col:nth-of-type(1) { grid-column: 2; grid-row: 1; }
    .footer-main > nav.footer-col:nth-of-type(2) { grid-column: 2; grid-row: 2; }
    .footer-main > nav.footer-col:nth-of-type(3) { grid-column: 3; grid-row: 1; }
    .footer-main > nav.footer-col:nth-of-type(4) { grid-column: 3; grid-row: 2; }

    .footer-subscribe {
        grid-column: 4;
        grid-row: 1 / span 2;
        padding-left: 0;
        border-left: 0;
        border-top: 0;
        padding-top: 0;
    }

    /* 橫排欄改直排：與清單既有的 1.1rem 行距同節奏 */
    .footer-main .footer-col-list--inline {
        flex-direction: column;
        gap: 1.1rem;
    }
}

/* 訂閱按鈕：深灰底白字（2026-08-18 客戶指定）。深灰用 --surface-dark
   （抽屜底、服務專區深色欄同一顆），白字用配套的 --surface-dark-foreground。
   ⚠ 全站唯一按鈕「永遠 AERIAL 藍」的舊慣例被客戶這次的明確指示蓋過，
     僅此一顆，其他 .btn-pill 不動。
   ⚠ 右下角浮動電子報面板共用 .footer-submit，會一起變深灰白字——
     兩處本來就被要求長一樣（「跟 footer 的輸入欄位一樣」），一致是對的。 */
.footer-submit {
    background: hsl(var(--surface-dark));
    color: hsl(var(--surface-dark-foreground));
}

.footer-submit:hover,
.footer-submit:focus-visible {
    background: hsl(var(--surface-dark) / .85);
    color: hsl(var(--surface-dark-foreground));
}

/* ───────────────────────────────────────────────────────────────────────
   知識專區列表：標籤篩選改純文字（2026-08-18 客戶指定，第三輪）
   ───────────────────────────────────────────────────────────────────────
   客戶：「標籤篩選請不要每個標籤有框線，改成純文字然後排法跟左邊知識分類
   一樣一行一個文字」——拿掉 .tag-pill 的框線／底色，排法照抄 .filter-list a
   （同一批字級／顏色 token，兩欄看起來才是同一組元件的兩種內容，不是兩套
   風格）。只在 .kz-tags 範圍生效，不動 .tag-pill 共用元件本身——它同時服務
   標籤／頁碼／步驟編號等多處（見「④ 選中態一律藍實心」那一節），直接改
   .tag-pill 會連著別的地方一起變。
   ⚠ DOM 沒有動：還是 <a class="tag-pill" data-taglink>，JS 的 data-*／
   class 鉤子不受影響，這裡純粹換外觀。 */
.page--knowledge .kz-tags .filter-tags {
    display: block;
    gap: 0;
}

.page--knowledge .kz-tags .tag-pill {
    display: block;
    min-height: auto;
    padding: .5rem 0;
    border: 0;
    border-radius: 0;
    background: transparent;
    font-size: var(--text-body);
    line-height: 1.7;
    font-weight: var(--fw-light);
    letter-spacing: var(--tracking);
    color: hsl(var(--muted-foreground));
}

.page--knowledge .kz-tags .tag-pill:hover,
.page--knowledge .kz-tags .tag-pill:focus-visible {
    background: transparent;
    border-color: transparent;
    color: hsl(var(--heading));
}

/* 目前選中的標籤：換字重與顏色，不加底色也不加線——跟 .filter-list a.is-active
   （左邊分類的選中態）同一個做法，兩欄的「選中」讀起來是同一種語言。 */
.page--knowledge .kz-tags .tag-pill.is-active,
.page--knowledge .kz-tags .tag-pill.is-active:hover,
.page--knowledge .kz-tags .tag-pill.is-active:focus-visible {
    background: transparent;
    border-color: transparent;
    font-weight: var(--fw-normal);
    color: hsl(var(--heading));
}

/* ── 選中態改回「文字加膚色方塊底」（2026-08-24 客戶指定，推翻上面
   2026-08-18「不加底色也不加線」那個決定）───────────────────────────
   客戶：「左邊的區塊分類跟標籤選取後目前是只有字變粗變黑，改成文字加
   膚色方塊底」。膚色沿用 --nude（tokens.css 專門留給「知識專區左邊膚色
   底」這個指定位置的具名色，見該檔案「膚色（NUDE #F2E3D0）保留成一個
   具名色」那節，不是新調色票）。
   ⚠ 2026-08-24 客戶追加：「膚色底不要跟字齊頭，左邊填到底貼邊」——
   第一版只加 background（貼齊欄寬左緣，跟文字同一個起點），客戶要的是
   像「全部」那顆一樣一路出血到 .kz-tags-sticky 自己的左邊界。原本這裡
   刻意不套「全部」的出血招式，是擔心跟 .kz-tags .filter-tags >
   [data-taglink=""] 那條專用出血（margin-left:-(gap+30px)）衝突——
   但客戶明確要兩者長一樣，改成直接套用完全相同的公式：兩條規則同時
   命中「全部」被選中的情況時，算出來的 margin-left／padding-left 數值
   完全一致，不是「打架覆寫掉」，是「殊途同歸」，不會有衝突。 */
.page--knowledge .kz-tags .tag-pill.is-active,
.page--knowledge .kz-tags .tag-pill.is-active:hover,
.page--knowledge .kz-tags .tag-pill.is-active:focus-visible {
    background: hsl(var(--nude));
    margin-left: calc(-1 * (clamp(1.5rem, 3vw, 3rem) + 30px));
    padding-left: calc(clamp(1.5rem, 3vw, 3rem) + 30px);
}

/* ───────────────────────────────────────────────────────────────────────
   知識專區列表：篩選結果的膠囊拿掉藍底（2026-08-18 客戶指定，第三輪）
   ───────────────────────────────────────────────────────────────────────
   客戶：「篩選結果按鈕全部不要加背景藍色」。#wfFilterResult 裡的膠囊
   （分類：ＸＸ／#標籤／全部）本來跟其他頁的「選中態」共用同一條藍實心
   規則（「④ 選中態一律藍實心」，標籤／頁碼／步驟編號都靠它）——這裡只
   在知識專區這個結果列生效，改成跟左欄一致的純文字（不加框、不加底色），
   不動 .tag-pill.is-active 共用規則本身，頁碼／步驟編號等其他地方不受影響。
   ⚠ #wfFilterChips 原本沒有自己的 gap，多顆膠囊之間本來是靠每顆自己的
     框線／內距頂開；文字化之後框線沒了，補一個 gap 避免兩顆字黏在一起
     （例如同時選了分類又選標籤時的「分類：溝通表達 #情緒」）。 */
.page--knowledge #wfFilterChips {
    display: inline-flex;
    flex-wrap: wrap;
    gap: .5rem;
}

.page--knowledge #wfFilterResult .tag-pill {
    background: transparent;
    border: 0;
    border-radius: 0;
    padding: 0;
    min-height: auto;
    color: hsl(var(--heading));
    font-weight: var(--fw-normal);
}

.page--knowledge #wfFilterResult .tag-pill:hover,
.page--knowledge #wfFilterResult .tag-pill:focus-visible {
    background: transparent;
    border-color: transparent;
    color: hsl(var(--heading));
}

/* ───────────────────────────────────────────────────────────────────────
   知識專區列表：左欄分類／標籤的行高拉大（2026-08-18 客戶回報「字都擠在
   一起」）
   ───────────────────────────────────────────────────────────────────────
   兩欄改成純文字清單之後（上面「標籤篩選改純文字」那節），原本沿用
   .filter-list a 的 padding:.5rem 0（項目間距 1rem）在純文字＋無框線的
   情況下顯得緊——沒有框線幫忙分段，全靠留白，留白不夠字就黏在一起。
   分類／標籤兩欄用同一組數字，維持「排法一樣」。 */
.page--knowledge .filter-list a,
.page--knowledge .kz-tags .tag-pill {
    padding: 1rem 0;
    line-height: 2;
}

/* ── 分類選中態改回「文字加膚色方塊底」（2026-08-24 客戶指定，推翻上面
   本檔 11601 一帶「不加底色也不加線」那個共用規則的決定）─────────────
   跟本檔「選中態改回...膚色方塊底」那節（.kz-tags .tag-pill.is-active，
   在後面）是同一輪客戶指示，這裡是 Category 欄的對應版本。理由與膚色
   token（--nude）選用原因見那節註解，不重複貼一次。
   ⚠ 2026-08-24 客戶追加：「膚色底不要跟字齊頭，左邊填到底貼邊」——
   第一版只加 background，色塊因此跟文字一樣貼齊欄寬左緣（沒有出血），
   客戶要的是像「全部」那顆一樣一路貼到側欄灰底的真正左緣。改用「全部」
   已經在用的同一招（.filter-list [data-catlink=""] 那條）：margin-left
   負值往左長到 .kz-filter-sticky 自己的左邊界，padding-left 補回同樣的
   量讓文字位置不動——跟「全部」用同一個變數（var(--section-x)），兩者
   同時是 .is-active 時（例如沒有任何篩選、「全部」本身被選中）算出來的
   margin-left／padding-left 數值完全一樣，不會互相打架蓋掉。 */
.page--knowledge .filter-list a.is-active {
    background: hsl(var(--nude));
    margin-left: calc(-1 * var(--section-x));
    padding-left: var(--section-x);
}

/* 「全部」跟底下實際分類清單再拉開一段（2026-08-18 客戶指定，第四輪）：
   「全部」是「清掉篩選」的入口，跟底下逐一列出的分類不是同一層級，
   多留一段距離讓兩者看得出分組。只加在第一項，其餘分類彼此間距不變。 */
.page--knowledge .filter-list li:first-child {
    margin-bottom: 1.5rem;
}

/* ⚠ 2026-08-19 客戶指定：「溝通請跟左邊溝通表達齊頭」＋「兩個全部底下都加
   橫細」。
   ① 對齊：Tags 欄的「全部」（本檔 14471 一帶新加的，跟 Category 同角色）
   沒有對應上面這條 margin-bottom:1.5rem 分組間距，兩欄第二項（溝通表達
   vs #溝通）因此錯開 24px（實測，剛好等於 1.5rem）——幫 Tags 那顆「全部」
   補同一個值。Tags 沒有 <li> 包一層，data-taglink="" 就是最外層的
   <a>，直接選它。
   ② 橫細：兩欄「全部」底下都加一條 hairline，沿用卡牆分格線同一份
   hsl(var(--foreground)/.16)（不新調顏色）。 */
.page--knowledge .kz-tags .filter-tags > [data-taglink=""] {
    margin-bottom: 1.5rem;
}

.page--knowledge .filter-list [data-catlink=""],
.page--knowledge .kz-tags .filter-tags > [data-taglink=""] {
    border-bottom: 1px solid hsl(var(--foreground) / .16);
}

/* ⚠ 2026-08-19 客戶追加：「左邊的全部底下橫線一樣左邊延伸到底不跟字齊頭，
   右邊的線左邊延伸到灰色底旁邊」——上面那條 border-bottom 天生只貼齊
   「全部」<a> 自己的欄寬（跟文字同一個起點），客戶要的是比照本檔「英文
   小字（Category／Tags）底下加整寬橫線」那節（21320 一帶）已經做過的
   左邊出血：Category 線要貼齊視窗左緣、Tags 線要接到灰底旁邊（跟垂直線
   同一個位置）。
   ⚠ 不是另外加偽元素——直接讓「全部」這顆 <a> 自己的box 比照
   .kz-filter-sticky／.kz-tags-sticky 同一招左邊出血（margin-left 負值
   往左長、padding-left 補回同樣的量讓文字位置不動），border-bottom 是
   長在這顆 box 自己的邊上，box 變寬了線自然跟著變寬，不用重新算線的
   座標。兩顆「全部」各自用的量，直接照抄它們所在的 sticky 容器已經在用
   的同一組數字（.kz-filter-sticky 是 var(--section-x)；.kz-tags-sticky
   是 clamp(1.5rem,3vw,3rem)+30px），因為「全部」本來就沒有比 sticky
   容器自己的 padding-left 多縮進去一層，兩者的出血起點是同一個位置。
   ⚠ 只往左長：margin-right 沒有動，右邊界（原本已經對的欄寬右緣）完全
   不受影響。 */
.page--knowledge .filter-list [data-catlink=""] {
    margin-left: calc(-1 * var(--section-x));
    padding-left: var(--section-x);
}

.page--knowledge .kz-tags .filter-tags > [data-taglink=""] {
    margin-left: calc(-1 * (clamp(1.5rem, 3vw, 3rem) + 30px));
    padding-left: calc(clamp(1.5rem, 3vw, 3rem) + 30px);
}

/* ───────────────────────────────────────────────────────────────────────
   知識專區列表：卡牆貼右邊緣（2026-08-18 客戶指定，第五輪）
   ───────────────────────────────────────────────────────────────────────
   客戶：「右邊小卡直接右移到貼邊不要右邊留空間」。跟左欄灰底貼視窗左緣
   （上面「灰底要貼齊視窗左緣」那節）同一個成因、鏡像的做法：.kz-layout
   在 .page--v2 底下滿版出血，右邊唯一的留白來源是 .kz-layout 自己的
   padding-inline:var(--section-x)（右側）。.kz-grid 是 .kz-main 裡的
   一般區塊（不是 grid item，用不到 .kz-filter 那套 grid 撐滿的邏輯），
   一般區塊 width:auto 時，margin-right 設負值會直接讓內容寬度長出去、
   左邊界不動——不用像左邊那樣另外補 padding 抵銷，因為卡牆本身沒有
   「文字要保持原位」的內距需求，直接貼右邊緣就是要的效果。 */
.page--knowledge .kz-grid {
    margin-right: calc(-1 * var(--section-x));
    /* ⚠ 2026-08-18 客戶指定（第九輪）：「小卡還是要貼右邊，整個寬減窄但
       還是在右邊貼齊」——上一輪收 margin-right 收錯邊了，那樣會讓右邊
       離開邊緣、不再貼齊。改成右邊維持全出血（margin-right 不變），
       從左邊加 margin-left 把寬度收窄：一般區塊 width:auto 時左邊界會
       跟著 margin-left 往右推，右邊界仍然釘在 margin-right 那個定值上
       不動——寬度變窄、右緣仍然貼邊。
       ⚠ 2026-08-18 客戶回報（第十輪）：「還是太寬」——上面 0.5 倍的
       內縮不夠，加大到 1 倍 --section-x（跟右邊出血同一個量級）。 */
    margin-left: calc(1 * var(--section-x));
}

/* 「篩選結果」那一列跟著卡牆一起右移（2026-08-18 客戶指定，第十五輪）：
   .kz-grid 用 margin-left 內縮之後，左緣比 #wfFilterResult（還停在
   .kz-main 原本的欄界）多推進去一段——兩者現在對不齊。補同一個
   margin-left，跟卡牆用同一個變數，之後卡牆內縮量再調，這裡自動跟著動，
   不會又對不齊。 */
.page--knowledge #wfFilterResult {
    margin-left: calc(1 * var(--section-x));
}

/* ── 篩無結果時的提示文字漏補同一個右移（2026-08-24 客戶「知識專區選項
   勾選為未符合選項時該文字請跟標題齊左對齊」）────────────────────────
   客戶指的是這個網址帶 ?cat=／?tag= 篩到 0 篇時出現的 #wfEmpty「沒有符合
   的文章，請換一個分類或標籤。」。實測（getBoundingClientRect＋跨寬度
   逐一比對 margin-left）：#wfFilterResult／.course-meta 這兩個同一欄的
   元件都在桌機（≥1200px）疊了上面這條 +section-x 右移去對齊 .kz-grid，
   在手機/平板（≤1199.98px，見本檔「知識專區列表 手機版第三輪」那節）
   又都顯式歸零、退回跟 H1 同一條基準線——#wfEmpty 兩邊都沒補到這條，
   等於一直停在「未內縮」的舊基準線，桌機因此比 #wfFilterResult／
   .course-meta 整組往左偏一個 --section-x（實測 1440 寬偏 72px），手機
   因為目標值本來就是 0、沒補也剛好是對的，所以只有桌機看得出來。
   照抄同一個變數、同一種寫法，讓三者永遠同步，卡牆內縮量之後再調，
   這裡不用跟著改。 */
.page--knowledge .filter-empty {
    margin-left: calc(1 * var(--section-x));
}

/* 右邊小卡標題字級改 22（2026-08-18 客戶指定，第五輪）。
   ⚠ 只在這一頁的 .kz-grid 生效，不動 .pcard-title 共用元件本身——
   同一份標題樣式還服務課程／電子報／搜尋等其他卡牆（見「4. 內頁卡片
   .pcard」一節），直接改 .pcard-title 會連著別的地方一起變。 */
.page--knowledge .kz-grid .pcard-title {
    font-size: 22px;
}

/* 2026-08-19 客戶「小卡標題手機版字縮小一點」——上面 22px 是 08-18
   定案的桌機／原本手機共用值，這裡疊一條手機專屬、更晚出現的同選擇器
   規則，特異度相同、順序更後面，會贏過上面那條（跟上面那條贏過
   ≈14956 那條卡牆容器區塊裡寫的 var(--text-lead) 是同一個道理）。
   18px 是「縮小一點」的幅度，不是砍到 .pcard-title 共用元件原本的
   字級（那個更小，這裡只是比 22px 收一點，不是退回最原始的值）。 */
@media (max-width: 767.98px) {
    .page--knowledge .kz-grid .pcard-title {
        font-size: 18px;
    }
}

/* ⚠ 2026-08-18 客戶指定（第十四輪）：「圖片不要拉長這麼多，圖片一樣
   橫的，改文字比例那邊長一點」——推翻上面第十三輪的直式圖（那條规则
   已拿掉，圖片退回 .pcard-media 共用元件原本的 16:9）。改把「卡片比較
   高」這件事交給文字區塊：.pcard-body 上下內距加大，卡片變高是文字區
   撐出來的，不是圖片被拉長。 */
.page--knowledge .kz-grid .pcard-body {
    padding-block: clamp(2rem, 3vw, 2.75rem);
}

/* 「知識分類」「標籤篩選」標題改英文小字（2026-08-18 客戶指定，第十輪）。
   ⚠ 文字內容在 knowledge_list.php 已經換成 Category／Tags（原中文留在
   PHP 註解備查，沒有真的刪掉）。這裡補上全站「英文小標」慣用的字型——
   .filter-title 本來就跟 .band-en（開場那顆「Knowledge Zone」）共用
   同一套字級／字距／顏色 token，只差 font-family，這裡補齊，兩處看起來
   才是同一種「英文小字」語彙，不是碰巧字級一樣。 */
.page--knowledge .filter-title {
    font-family: var(--font-eyebrow);
}

/* ⚠ 2026-08-18 客戶指定：「.kz-tags」這個 class 名字也撞了（跟「① .kz-layout
   撞名」同一個成因，另一個不相關功能的 .kz-tags 是 display:flex；見那份
   記憶）。之前量 .filter-title／.kz-tags-sticky 寬度只有 38px（等於文字
   自己的寬），就是因為 .kz-tags 被迫變成 flex 容器、內部子項改用
   shrink-to-fit 量寬，不是撐滿欄寬的區塊。這裡蓋回 block，欄位才會真的
   跟 .kz-filter 一樣撐滿自己的 grid 欄寬。 */
.page--knowledge .kz-tags {
    display: block;
}

/* 英文小字（Category／Tags）底下加整寬橫線（2026-08-18 客戶指定，
   第十二～十六輪，含追加：「左邊延伸到貼邊」「右邊連到小卡」「線要連到
   垂直線」「線不要隨著滾動移動（要跟 sticky 文字一起黏）」）。
   ⚠ 前兩輪試過掛在 .filter-title::after（margin 撐出去，撞到
   overflow-y:auto 冒出橫向捲軸）跟掛在 .kz-layout（不會捲動，滾動後跟
   sticky 文字分開、線留在原地）都被打回。這輪的做法：**把 sticky 內層
   自己的框「撐」到需要的寬度**（跟 .kz-filter 貼齊視窗左緣同一招——
   margin 往外拉、padding 往回補讓文字位置不動），線本身只要在撐好的
   框裡當一個單純的 100% 寬色塊，不用再自己算絕對座標，也不會有東西
   溢出容器（因為框本身已經夠寬，沒有子元素超出框外），三個問題一次解決：
   線跟著 sticky 內層走（會跟文字一起吸頂／解除）、不會冒橫向捲軸、
   兩條線首尾接得上垂直線。 */
.page--knowledge .kz-filter-sticky {
    /* 左：貼齊視窗左緣，跟 .kz-filter 自己的左出血同一個算法。
       右：伸到垂直線（.kz-filter::after 的 right:30px）那個位置。 */
    margin-left: calc(-1 * var(--section-x));
    padding-left: var(--section-x);
    margin-right: calc(-1 * (clamp(1.5rem, 2.2vw, 2rem) - 30px));
}

.page--knowledge .kz-tags-sticky {
    /* 左：接上垂直線的位置（跟 .kz-filter-sticky 右邊出血到的同一點）。
       右：連到小卡目前的左緣（.kz-grid margin-left 是 1 倍 --section-x，
       公式裡的 --section-x 對應的就是這個——.kz-grid 的內縮量之後若再調，
       這裡要跟著重算）。
       ⚠ width:auto 的區塊，右邊界＝容器右邊界 − margin-right，要把右邊界
       往右推，margin-right 要是負值，不是正值——第一次寫成正的，實測
       右邊線只到 416 沒到 688，翻回來變負的才對。 */
    margin-left: calc(-1 * (clamp(1.5rem, 3vw, 3rem) + 30px));
    padding-left: calc(clamp(1.5rem, 3vw, 3rem) + 30px);
    margin-right: calc(-1 * (var(--section-x) + clamp(1.5rem, 3vw, 3rem)));
}

/* ⚠ 2026-08-24 客戶指定「直接把其他灰色底線拿掉...其餘這區塊的灰線
   刪掉」——這裡原本有的 .filter-title::after 兩條裝飾底線（2026-08-18
   加的，Category／Tags 標題各自底下一條）整組刪除，改成本檔更下面
   「篩選區塊對齊重整」那節統一用兩條真正貫通全頁寬度的線取代，不再
   靠側欄自己的裝飾線湊對齊。 */

/* ── 篩選區塊對齊重整（2026-08-24 客戶「該段落文字沒對齊」，附截圖四點
   指示）─────────────────────────────────────────────────────────────
   客戶原話拆成四件事，逐一對照側欄既有元素找出「左邊」指的是什麼：
   ①「篩選結果跟tags齊頭」＝ #wfFilterResult 的頂端要對齊 .filter-title
     （Category／Tags 兩顆標題，本來就同一條基準線）的頂端。實測：
     .filter-title 頂端比 #wfFilterResult 頂端低 55.67px（＠1440），
     剛好等於 .kz-filter 的 padding-top（clamp(1.5rem,2.2vw,2rem)）
     ＋.kz-filter-sticky 的 padding-top（1.5rem）——側欄多這兩層留白，
     內容欄沒有，補同樣的算式讓兩邊頂端對齊，寬度變了也還是對的。
   ②「底下灰線跟左邊兩條對齊」＝客戶說的「兩條」是本檔上面這節剛加的
     .filter-title::after（Category／Tags 標題底下各一條 1px 橫線，
     margin-top:.75rem，兩條本來就同一條基準線）。「底下灰線」＝
     #wfCardGrid 這個 <ul> 本來就有的 border-top（篩選 0 篇時，這條
     border 是唯一還看得到的「線」，見本檔更早「35-2 就地篩選」那節）。
     只在真的 0 篇（#wfEmpty 沒有 hidden）才調——有結果時卡牆本來的間距
     不變，用 :has() 精準卡住範圍，不影響有卡片的正常情況。
   ③「沒有符合的文章跟左邊全部對齐」＝上一點的灰線對齊之後，緊接著的
     #wfEmpty 自然銜接在「全部」開始的位置（.filter-title::after 那條
     線到「全部」文字之間，側欄本身也有一段留白）。
   ④「底下加一條灰線跟左邊的第二條線對齊」（客戶回答確認＝ Tags 欄那條）
     ＝ #wfEmpty 下面新增一條線，水平位置對齊 .kz-tags-sticky 的左邊界
     （也就是 Tags 標題底線实际渲染的起點）。這個位置無法直接抄某個
     現成 token，是把 .kz-layout 的三欄公式（15rem／11rem／1fr，
     gap:clamp(1.5rem,3vw,3rem)）跟 .kz-tags-sticky 自己的左邊出血
     （-(gap+30px)）疊起來反推：從 #wfEmpty 目前的左邊界（.kz-main 起點
     ＋一個 --section-x，見上面「篩無結果時的提示文字」那節）往左拉
     （--section-x ＋ Tags 欄寬 11rem ＋兩個 gap ＋ 30px），實測
     ＠1440 算出來 -364.4px，跟直接量測 .kz-tags-sticky.left（282）反推
     的落差幾乎是 0，公式正確。
   ⚠ 這四點是客戶截圖＋文字描述的推斷結果，不是 100% 確定的解讀——尤其
     ①③ 用的是「絕對 Y 對齊」，②④ 用的是「同一批既有裝飾線」當基準，
     實作完會截圖跟客戶原始截圖比對，不一致要再調。
   ⚠ 全部只在 ≥1200px（側欄雙欄並排）生效，≤1199.98px 側欄收成單欄
     堆疊、「Tags 標題在旁邊」這個參照系不存在，一律歸零回退到跟 H1
     同一條基準線（見下面既有 @media (max-width:1199.98px) 區塊，那裡
     一併補新增的歸零）。 */
.page--knowledge #wfFilterResult {
    margin-top: calc(clamp(1.5rem, 2.2vw, 2rem) + 1.5rem);
}

/* ⚠ 2026-08-24 客戶「灰色底線還是沒對齊，如果對不齊就改左邊的線延伸到
   右邊拉長」——上面②那個做法（讓 #wfCardGrid 的 border-top 疊
   margin-bottom:1.5rem 去湊 .filter-title::after 的 Y）客戶截圖回報
   還是沒對齊，判斷是兩個獨立元素各自定位、只靠算出來的數字湊在一起，
   本來就很難保證每個斷點/每個字型渲染結果都剛好命中同一個 Y。改成
   客戶建議的方向：不用兩條線互相對齊，做「一條」真正貫通側欄到內容欄
   的線——同一個元素、同一個 top，物理上不可能對不齊。
   markup 新增一顆空 div（knowledge_list.php 的 .kz-empty-divider，
   在 #wfCardGrid 跟 #wfEmpty 之間），寬度／出血算法沿用下面 #wfEmpty
   底下那條線（④）已經驗證過的公式（.kz-layout 三欄 grid＋
   .kz-tags-sticky 出血反推），這裡再往左多推一欄（Category 欄寬
   15rem＋一個 gap），一路出血到 Category 自己的左邊界（＝側欄灰底的
   視窗左緣，跟「全部」／.filter-title::after 兩條線出血到的地方是
   同一個起點）。
   ⚠ #wfFilterResult 的 margin-bottom 改明寫 0（原本共用的
   clamp(1.5rem,3vw,2.25rem) 拿掉，不是留著給舊做法用的殘留）：
   .kz-empty-divider 是 #wfFilterResult 在 DOM 流裡的下一個元素（中間
   隔著這時已經淨空的 #wfCardGrid，實測 height≈1px、margin:0，不貢獻
   間距），它自己的 margin-top 要從「緊接在 #wfFilterResult 後面」這個
   基準去算，如果 #wfFilterResult 自己還留著一份不相干的 margin-bottom，
   兩個間距疊加，算出來的目標位置就要跟著多扣一層，不如直接歸零、
   讓 .kz-empty-divider 自己的 margin-top 是唯一的間距來源，好驗證好維護。
   ⚠ margin-left／width 出血公式：.kz-empty-divider 是全新的 <div>，
   沒有 .filter-empty 的 class，不會吃到 #43 那次加的
   margin-left:calc(1*var(--section-x))——它的「天生」左邊界是
   .kz-main 自己的原始左邊界（574.375＠1440，也就是 var(--section-x)
   ＋Category欄15rem＋一個gap＋Tags欄11rem＋一個gap 疊出來的值，不是
   #wfFilterResult 那個已經內縮過的 646.375）。第一版誤用了 #wfFilterResult
   的基準去反推，多扣了一個 var(--section-x)，實測出血過頭 73px。
   這裡改成直接從 .kz-main 的原始左邊界回推到 Category 自己的左邊界
   （＝側欄灰底的視窗左緣）：-(var(--section-x)＋Category欄15rem＋
   一個gap＋Tags欄11rem＋一個gap) = -(var(--section-x)+26rem+2×gap)。 */
.page--knowledge .kz-main:has(#wfEmpty:not([hidden])) #wfFilterResult {
    margin-bottom: 0;
}

/* ⚠ 2026-08-24 客戶追加：「直接把其他灰色底線拿掉，留兩條水平線放
   tags下面一條，全部下面一條，寬要整個頁面寬，從最左連到最右，其餘這
   區塊的灰線刪掉」——推翻上面④那條「對齊 Tags 欄裝飾線左邊界」的做法
   （那條裝飾線本身也已經在上面整組刪除），改成兩條單純、真正貫通到
   視窗左右真邊緣的線，不再跟側欄任何元素的出血公式掛鉤。
   ⚠ 出血算法：第一版試過 100vw 滿版技巧（width:100vw +
   margin-left:calc(50%-50vw)），實測整條線位置全部跑掉（left=244
   應該是 0）——這個技巧的前提是元素本身在視窗水平置中（左右留白對稱），
   但 .kz-main 是 .kz-layout 三欄不對稱 grid 的最後一欄，天生就不在
   視窗置中，「50%」算出來的不是「視窗一半」而是「.kz-main 自己寬度的
   一半」，套用前提不成立。改回本檔上面已經驗證過的 margin-left 算法
   （出血到 Category 自己的左邊界＝視窗左緣），寬度再加一個
   var(--section-x)（.kz-layout 自己的右側 padding-inline）撐到視窗
   右緣——原本只撐到 .kz-main 自己的右邊界（因為 .kz-layout 右邊還留了
   一個 --section-x 的內距沒出血掉），這輪客戶明確要「最右」是真的視窗
   右緣，不是內容欄自己的右邊界。 */
.page--knowledge .kz-main:has(#wfEmpty:not([hidden])) .kz-empty-divider {
    display: block;
    margin-top: 1.5625rem;
    height: 1px;
    background: hsl(var(--foreground) / .16);
    margin-left: calc(-1 * (var(--section-x) + 26rem + 2 * clamp(1.5rem, 3vw, 3rem)));
    width: calc(100% + 2 * var(--section-x) + 26rem + 2 * clamp(1.5rem, 3vw, 3rem));
}

/* 第二條線「全部下面一條」：緊接在第一條線後面（DOM 順序見
   knowledge_list.php），左右出血公式跟第一條完全一樣（兩條都要「整個
   頁面寬」，天生就該用同一個算法）。margin-top 用「第一條線的 Y」到
   「側欄 Category／Tags 兩欄『全部』那顆 <a> 自己的 border-bottom
   （既有的舊裝飾，2026-08-19 就有）」之間實測的距離（1440 寬量出來
   68px≈4.25rem，rem/padding 驅動、不含 vw，1200～1920 範圍內是常數，
   不用跟著螢幕寬度變）。 */
.page--knowledge .kz-main:has(#wfEmpty:not([hidden])) .kz-empty-divider-2 {
    display: block;
    margin-top: 4.25rem;
    height: 1px;
    background: hsl(var(--foreground) / .16);
    margin-left: calc(-1 * (var(--section-x) + 26rem + 2 * clamp(1.5rem, 3vw, 3rem)));
    width: calc(100% + 2 * var(--section-x) + 26rem + 2 * clamp(1.5rem, 3vw, 3rem));
}

/* ⚠ 2026-08-24 客戶「這裡的線會長這樣很多條」——用 Plan Mode 徹底盤點後
   發現：96px 高（Y 464～560）的範圍內其實疊了 4 條不同來源的線，不是
   只有上面新增的兩條。另外兩條是本來就存在、跟這次對齊需求完全無關的
   舊裝飾線，从來沒有在「篩無結果」這個狀態下關掉過：
   ① #wfCardGrid 自己的 border-top／border-left（本檔 ≈20091，
      2026-08-18「無縫相片格」設計用來當卡牆外框，跟卡片本身的
      border-right/border-bottom 湊成四邊）——0 篇時 6 張卡全部被 JS
      隱藏，卡片自己的邊框消失了，但容器自己的 border-top/left 沒有
      連帶條件，繼續孤零零畫在那裡，剛好卡在「篩選結果」文字正下方，
      跟新增的第一條貫通線只差 25px。
   ② 側欄「全部」項目的 border-bottom（本檔 ≈23347，2026-08-19「兩個
      全部底下都加橫細」客戶指定的固定裝飾）——這條線本身沒有問題
      （有結果時也在，是正常的側欄裝飾），但跟新增的第二條貫通線只差
      14px、左半段（x:0~646）完全重疊，兩條粗細顏色一樣的線平行貼在
      一起，正是螢幕截圖「很多條」的主因。
   這兩條都只在「篩無結果、只剩兩條新線」這個特定狀態下關掉，不動它們
   在有結果時的原始作用（① 是卡牆外框的一部分、② 是側欄固定裝飾，兩者
   跟這次的對齊需求本來就是無關的東西，不能整條規則刪掉）。用
   `.kz-layout:has(...)`（不是 `.kz-main:has(...)`）是因為「全部」在
   側欄 `.kz-filter`／`.kz-tags`，不在 `.kz-main` 底下，`.kz-layout`
   才是三者共同的祖先。 */
.page--knowledge .kz-layout:has(#wfEmpty:not([hidden])) .kz-grid {
    border-top: 0;
    border-left: 0;
}

.page--knowledge .kz-layout:has(#wfEmpty:not([hidden])) .filter-list [data-catlink=""],
.page--knowledge .kz-layout:has(#wfEmpty:not([hidden])) .kz-tags .filter-tags > [data-taglink=""] {
    border-bottom: 0;
}

/* ⚠ 2026-08-24 客戶「灰線沒對齊」回報後修正：上一版只對齊了 #wfEmpty
   的外框頂端（跟 catAll 的 box top 一樣是 497.45px），但 .filter-empty
   共用規則的 padding-block 是 clamp(2.5rem,6vw,4rem)（給其他情境用的
   大留白，這裡明顯太大），而 catAll 只有 padding:1rem 0——框對齊了，
   文字實際位置卻因為 padding-top 差了 48px 而完全沒對齊，用 Range 量
   文字才發現（跟本檔更早「全部」出血那次是同一種必須用 Range 而不是
   getBoundingClientRect 的教訓）。改成明寫 padding-top 對齊 catAll 的
   padding-top，不再靠共用的 padding-block。
   ⚠ 加上面兩條貫通線之後，#wfFilterResult 的 margin-bottom 改明寫 0
   （不再是 clamp(1.5rem,3vw,2.25rem)），#wfEmpty 在 DOM 流裡的起點
   跟著往上移了一截，這裡的 margin-top 一併重新對過（.25rem）。 */
.page--knowledge .kz-main:has(#wfEmpty:not([hidden])) #wfEmpty {
    margin-top: .25rem;
    padding-top: 1rem;
}

@media (max-width: 1023.98px) {
    /* 單欄堆疊時分類／標籤欄不再左右並排，垂直線也收掉了（上面「單欄
       堆疊…右邊那條直線沒有對象可以分隔，收掉」那節）——這裡的左右出血
       跟著失去意義，比照收掉，退回各自欄位原本的內距，線就是單純的
       欄寬底線。 */
    .page--knowledge .kz-filter-sticky,
    .page--knowledge .kz-tags-sticky {
        margin-left: 0;
        margin-right: 0;
        padding-left: 0;
    }

    /* 「全部」底下那條線是同一招（見「橫線左邊延伸」那節，21320 一帶下面
       新加的），左邊出血一樣要在單欄斷點收掉，理由相同——不收的話「全部」
       這顆 <a> 自己的 box 會比照桌機邏輯多長出一截，但這個斷點下父層
       sticky 容器已經沒有對應的 padding-left 可以抵銷，一樣會冒出橫向
       捲軸。 */
    .page--knowledge .filter-list [data-catlink=""],
    .page--knowledge .kz-tags .filter-tags > [data-taglink=""] {
        margin-left: 0;
        padding-left: 0;
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   Footer 底色：全站不上色，改由「頁面的填色」延伸進來（2026-08-18 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「讓全站的footer都沒有背景色，若其他頁面有填色背景請延伸到
   footer區塊一起填滿」。
   結構事實：.wf-frame（全站機身，wireframe.css:19）是白底、包住 header／
   麵包屑／main／footer；footer 原本自己壓一層 --background（#F5F5F7），
   在白底頁（會員系列／知識列表／隱私權——內容收尾是機身白或 .band 白）
   看起來就是一條「footer 自己的灰帶」。做法分兩層：
     ① footer 本體透明——透出機身白，白底頁自然無縫。
     ② 內容區壓了填色的頁面，同一顆填色延伸進 footer 一起填滿：
        ・--background（#F5F5F7）＝首頁（知識專區收尾）、article 型
          （服務內頁／知識內頁／電子報內頁）、搜尋、電子報列表、
          實體課程列表（.page--wall 的 .cx-list 同色收尾）
        ・--surface-grey（#DCDCE1）＝課程內頁（.course-detail--editorial
          整頁 band 都壓這顆，見 ≈17280 那條）
   ⚠ 這張填色對照表是逐頁 headless 截圖取像素實測的（footer 上緣上下
     各取一點），不是照 token 註解猜——article／search／newsletter 頁的
     .band 早被後段規則壓成 --background（≈17909／18094），別再拿
     「v2 band 預設白」推論頁面收尾色。
   ⚠ 兩組填色選擇器互斥（課程內頁不掛 .page--wall／.page--article），
     不靠順序決勝；但日後新頁型要進對照表時，先實測收尾像素再挑組。 */
.site-footer {
    background: transparent;
}

body:has(.hero) .site-footer,
body:has(.page--article) .site-footer,
body:has(.page--search) .site-footer,
body:has(.page--newsletter) .site-footer,
body:has(.page--wall) .site-footer {
    background: hsl(var(--background));
}

body:has(.course-detail--editorial) .site-footer {
    /* 2026-08-18 晚：課程內頁整頁的灰改成 --background（客戶附色票
       rgb(246,246,247)，見檔尾「課程內頁的灰全部改淺」區塊）——
       footer 延伸的填色跟著同一顆換，對照表只剩一種灰。 */
    background: hsl(var(--background));
}

/* ⚠ 2026-08-19 客戶指定：「左半部灰色背景延伸到footer也有」——知識專區
   列表頁不在上面兩組全寬填色清單裡（footer 目前是透明，透出 .wf-frame
   機身白），客戶要的也不是整條 footer 填滿，是左欄那條灰底（.kz-filter
   的 --background，寬度 = --section-x + 15rem，見本檔「左邊灰色背景也
   請延伸上去」那節 .page--knowledge::before）繼續往下蓋過 footer。
   ⚠ footer 在 .page--knowledge 這個 div 外面（見 layout 模板，跟「左右
   分欄結束到 footer」同一個結構事實），.page--knowledge::before 沒辦法
   直接跨過去蓋到 footer，所以另外在 .site-footer 自己身上開一個獨立的
   ::before，只在這一頁生效（body:has(.page--knowledge)），寬度公式照抄
   同一個（不是巧合對齊，是兩處故意共用同一顆算式）。
   ⚠ .site-footer 本身已經是滿版寬度、左緣貼著視窗左緣（實測 left:0），
   不像 .page--knowledge 還要靠負 margin／padding 抵銷內距出血，這裡
   單純 position:absolute + left:0 就對齊，不用額外的 bleed 技法。
   ⚠ 只加灰底，不動 footer 本身的排版——footer 內容維持滿版單欄（「footer
   不要左右分」那條指定沒有被推翻，這裡純粹是背景色多蓋一塊，footer 的
   NAVIGATION／MEMBER／NEWSLETTER 三欄照樣滿版排列，不會被這塊灰色
   底下的假想欄位擠版位）。
   ⚠ 2026-08-19 補修正（客戶回報「footer橫線跟底下左邊文字也要蓋在灰色
   背景上」）：.footer-bottom（版權列，margin-inline:var(--footer-pad-x)
   ≈87.5px，比這塊灰底的右緣 312px 還要靠左）本來就整段落在灰底範圍
   裡，但它的 border-top 那條線跟 .footer-copy 文字被灰底蓋住了看不見
   ——成因是 z-index 分桶：.footer-main／.footer-bottom 是
   position:static 的一般流內容，繪製順序在「z-index:0 的定位元素」
   **之前**；這兩顆 ::before／::after 給了 z-index:0，反而被排到一般
   流內容**之後**畫，蓋在版權列上面。
   ⚠ 修法：改成 z-index:-1（排到一般流內容**之前**）＋在 .site-footer
   本身加 isolation:isolate，把這個負 z-index 的作用域關在 .site-footer
   自己家裡（不加這行的話負 z-index 會逃出去跟頁面其他地方的元素搶疊放
   順序，可能連帶蓋到不相干的東西——跟本檔「.kz-layout 撞名」那次負
   z-index 到處外溢的教訓同一個坑，這次先用 isolation 把它關好）。 */
body:has(.page--knowledge) .site-footer {
    position: relative;
    isolation: isolate;
}

body:has(.page--knowledge) .site-footer::before {
    content: "";
    position: absolute;
    left: 0;
    top: 0;
    bottom: 0;
    width: calc(var(--section-x) + 15rem);
    background: hsl(var(--background));
    z-index: -1;
}

/* ⚠ 2026-08-19 客戶追加：「垂直線也延伸到footer」——跟上面灰底同一輪
   要求、同一個結構限制（footer 在 .page--knowledge 外面，線跨不過去），
   做法對稱：.page--knowledge::after 那條線用掉了，這裡改在 .site-footer
   上開 ::after，x 座標照抄同一個算式（--section-x + 15rem − 30px，
   分類軌寬內縮 30px 的那個位置，見本檔「垂直線要延伸到標題那裡」那節），
   顏色也是同一份 hairline token，不新調。z-index:-1 跟上面 ::before
   同一個理由（不然這條線也會蓋住版權列的橫線與文字）。
   ⚠ 2026-08-19 再追加「垂直線改結束在footer最底下橫線處」：原本掛在
   .site-footer、bottom:0 會一路延伸穿過 .footer-bottom（版權列）到整個
   footer 的最底部——客戶要的是線在版權列自己的橫線（.footer-bottom 的
   border-top）那裡就停住，不要再往下穿過版權列。
   ⚠ 第一版做法是把線改掛到 .footer-main 身上（bottom:0 天生貼齊這個
   兄弟元素自己的高度，不用去算版權列多高）——但 x 座標公式改成
   `... - var(--footer-pad-x)` 之後實測整條線跑掉了 15.5px：
   `--footer-pad-x` 的定義式裡帶了一個 `100%`（footer-pad-x: max(4.5rem,
   (100% − min(90vw,1760px))/2)），CSS 自訂屬性裡的 % 不是在宣告的地方
   算好凍結，而是**在最後真正用到它的那個屬性、那個元素身上才即時算**——
   同一個 var(--footer-pad-x) 用在 .footer-main 自己的 margin-inline 上
   （容器是 .site-footer 全寬）跟用在 .footer-main::after 的 left 上
   （容器變成 .footer-main 自己、已經被 margin-inline 縮窄的寬度）算出
   來的 100% 基準不一樣，兩處引用同一個變數卻各算各的，數字對不上。
   ⚠ 改法：**不要換算 x 座標，也不要換掛的元素**——保留線在 .site-footer
   本身（x 座標維持原本已經驗證正確的公式，容器是 .site-footer 全寬，
   不會踩到上面那個問題），改用 CSS Grid 讓「量 .footer-main 多高」這件
   事交給瀏覽器做，不用自己算：.site-footer 疊一層 grid（單欄兩列，
   .footer-main／.footer-bottom 各自然佔一列，跟原本 block 排列視覺上
   沒有差別），::after 明講 grid-row:1，absolute 定位的 grid item 會用
   「自己所在的格子」當容器——格子寬度＝整個 grid 容器寬（.site-footer
   全寬，不受 .footer-main margin-inline 影響），格子高度＝這一列的
   auto 高（天生等於 .footer-main 的實際高度，版權列文字換行也會自動
   跟著對，不用重算）。 */
body:has(.page--knowledge) .site-footer {
    display: grid;
    grid-template-columns: 1fr;
}

body:has(.page--knowledge) .footer-main {
    grid-row: 1;
    grid-column: 1;
}

body:has(.page--knowledge) .footer-bottom {
    grid-row: 2;
    grid-column: 1;
}

body:has(.page--knowledge) .site-footer::after {
    /* ⚠ 2026-08-19 補修正（客戶回報「還是穿過去了」，附截圖）：`grid-row:1`
       只設了 grid-row-start，grid-row-end 沒有明講、預設是 auto（span 1）
       ——瀏覽器對「grid-row-end:auto」不當成明確的格線，absolute 元素找
       容器時判定「這個格子邊界不夠明確」，於是退回用整個 grid 容器當
       容器，height 因此還是量到 585px（整個 .site-footer 高）而不是
       503px（.footer-main 那一列的高），實測 afterHeight 直接等於
       footerRect.height 才抓到這個成因。改成 `grid-row: 1 / 2`
       明講起訖兩條格線，容器才會正確收斂成「第 1 列自己的格子」。 */
    content: "";
    position: absolute;
    grid-row: 1 / 2;
    grid-column: 1 / 2;
    left: calc(var(--section-x) + 15rem - 30px);
    top: 0;
    bottom: 0;
    width: 1px;
    background: hsl(var(--foreground) / .16);
    z-index: -1;
}

/* footer 上緣殘線清理：「全部footer都不要加線」上一輪清的是 .footer-main
   那條；v2／courses 頁還有兩條寫在 .site-footer 元素上的歷史線
   （≈13453 加線、≈14310 關掉、≈15560 又加回），交界像素實測還看得到
   1px（215,215,216）。同特異度（0,2,1）靠位置在後蓋掉。 */
body:has(.page--v2) .site-footer,
body:has(.page--courses) .site-footer {
    border-top: 0;
}

/* 訂閱輸入框在「白底 footer」頁會隱形（純白無框色塊壓機身白）：只在
   這些頁補一條 hairline；有填色的頁維持無框白色塊（色差本身就是區隔）。
   ⚠ 選擇器＝上面填色清單的反面，日後清單增減時兩處要同步。
   ⚠ :not(:focus)：focus 態的內縮 ring（≈4020 box-shadow 那條）照舊，
     不被這條的 border-color 蓋住。
   ⚠ 鎖 .site-footer 底下：右下角浮動電子報面板共用 .footer-input，
     它自己是白面板、另有一套邊框處理，不歸這條管。 */
body:not(:has(.hero, .page--article, .page--search, .page--newsletter, .page--wall, .course-detail--editorial)) .site-footer .footer-input:not(:focus) {
    border-color: hsl(var(--foreground) / .14);
}

/* ── 首頁 Hero：主圖與 header 的間距再放大一階（2026-08-18 客戶指定）──
   客戶：「hero首頁header上移一點」「或是圖片下移」——兩案擇一。選「圖片
   下移」：header 是全站統一列（見「全站 Header 統一不展開」區塊），只為
   首頁位移會破壞跨頁一致，而且這個客戶先前已明確偏好「局部修、不動全站
   共用 header」；本檔也有前例（≈19216「圖片上面padding不夠高」那輪）
   就是加大 --hero-card-media-pad-y 這一顆。
   ⚠ 這顆 token 是 SSOT，同時管：主圖上下留白帶厚度、logo 方塊邊長、
     右側次圖上緣留白、箭頭模組定位（全在 ≥1468 Card 版面內）——一起
     等比放大是既定設計（三處對齊同一個數字），不要只調 padding-top
     打破上下對稱。
   實測基準（1920×1080）：原 7vh≈70px，主圖上緣距 logo 下緣僅 ≈10px；
   改 8.5vh≈84px 後間距 ≈25px，主圖上緣落到 header 帶（86px）下緣附近。 */
html:has(.hero) {
    --hero-card-media-pad-y: clamp(3rem, 8.5vh, 5.5rem);
}

/* ═══════════════════════════════════════════════════════════════════════
   選單展開改亮色版（2026-08-18 客戶附色票指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「將全站選單展開背景顏色改成圖片灰色，字顏色改黑色70%」。
   色票像素實測 rgb(220,221,224)＝既有 --surface-grey（#DCDCE1），不新增
   色票；黑 70% 用站上既有配方 hsl(var(--heading)/.7)（統一列 --header-ink
   同一杯）。這一輪推翻 2026-08-17「卡片改深底、文字用白色」那組決定。
   ⚠ 抽屜的殼與字色分兩組：A（max-width:1467.98，含手機）用
     `.site-header .site-nav` 系選擇器（殼 ≈20159、字色 ≈20304）；
     B‧C（min-width:1468，is-compact／首頁未捲動兩情境）用
     `html.is-compact ...`／`html:has(.hero):not(.is-compact) ...` 前綴
     （殼 ≈20468、字色 ≈20601）。字色全部帶 !important——這裡照同結構、
     同特異度、同 !important 鏡射一份，靠位置在後翻面，一條都不刪。
   層級對應（白→黑，維持原設計的明暗階差）：
     主文字 1 → heading/.7｜小標與標語 .55 → heading/.5｜
     hairline .15 → foreground/.15｜AAA 框線 .3 → heading/.35｜
     AAA 選中底線 1 → heading/.7｜hover 提到全黑（原本白底沒 hover 色階，
     亮色版補上，跟 footer 連結同語彙）。
   ⚠ SVG 圖示（放大鏡、chevron、✕）走 currentColor 跟著文字翻；logo
     圖示是固定 fill，另補一條。
   ⚠ 同日第二輪：「請把導覽背景改成白色」——面板底從 --surface-grey
     （色票 220 灰）再改成 --brand-foreground（站上當純白用的那顆，
     --muted 雖也是白但語意漂移過、不用）；字色黑 70% 不變。殼×2 與
     遮罩×2 四處一起換，遮罩同色/.72 疊在同色面板上維持原色不變的
     疊色數學照舊成立。 */
@media (max-width: 1467.98px) {
    .site-header .site-nav {
        background: hsl(var(--brand-foreground));
    }

    .site-header .site-nav a,
    .site-header .site-nav p,
    .site-header .site-nav span,
    .site-header .site-nav button,
    .site-header .site-nav .dropdown-item {
        color: hsl(var(--heading) / .7) !important;
    }

    .site-header .site-nav a:hover,
    .site-header .site-nav a:focus-visible,
    .site-header .site-nav .dropdown-item:hover,
    .site-header .site-nav .dropdown-item:focus-visible {
        color: hsl(var(--heading)) !important;
    }

    .site-header .site-nav > a,
    .site-header .site-nav > .dropdown,
    .site-header .site-nav-menu-foot,
    .site-header .site-nav .dropdown-item {
        border-color: hsl(var(--foreground) / .15) !important;
    }

    .site-header .site-nav-section-label,
    .site-header .site-nav-menu-tagline {
        color: hsl(var(--heading) / .5) !important;
    }

    .site-header .site-nav .font-size-btn {
        border-color: hsl(var(--heading) / .35) !important;
    }

    .site-header .site-nav .font-size-btn.is-active {
        border-bottom-color: hsl(var(--heading) / .7) !important;
    }

    .site-header .site-nav-logo-icon path {
        fill: hsl(var(--heading) / .7) !important;
    }
}

@media (min-width: 1468px) {
    html.is-compact .site-header .inner > .site-nav,
    html:has(.hero):not(.is-compact) .site-header .inner > .site-nav {
        background: hsl(var(--brand-foreground));
    }

    html.is-compact .site-header .site-nav a,
    html.is-compact .site-header .site-nav p,
    html.is-compact .site-header .site-nav span,
    html.is-compact .site-header .site-nav button,
    html.is-compact .site-header .site-nav .dropdown-item,
    html:has(.hero):not(.is-compact) .site-header .site-nav a,
    html:has(.hero):not(.is-compact) .site-header .site-nav p,
    html:has(.hero):not(.is-compact) .site-header .site-nav span,
    html:has(.hero):not(.is-compact) .site-header .site-nav button,
    html:has(.hero):not(.is-compact) .site-header .site-nav .dropdown-item {
        color: hsl(var(--heading) / .7) !important;
    }

    html.is-compact .site-header .site-nav a:hover,
    html.is-compact .site-header .site-nav a:focus-visible,
    html.is-compact .site-header .site-nav .dropdown-item:hover,
    html.is-compact .site-header .site-nav .dropdown-item:focus-visible,
    html:has(.hero):not(.is-compact) .site-header .site-nav a:hover,
    html:has(.hero):not(.is-compact) .site-header .site-nav a:focus-visible,
    html:has(.hero):not(.is-compact) .site-header .site-nav .dropdown-item:hover,
    html:has(.hero):not(.is-compact) .site-header .site-nav .dropdown-item:focus-visible {
        color: hsl(var(--heading)) !important;
    }

    html.is-compact .site-header .site-nav > a,
    html.is-compact .site-header .site-nav > .dropdown,
    html.is-compact .site-header .site-nav-menu-foot,
    html.is-compact .site-header .site-nav .dropdown-item,
    html:has(.hero):not(.is-compact) .site-header .site-nav > a,
    html:has(.hero):not(.is-compact) .site-header .site-nav > .dropdown,
    html:has(.hero):not(.is-compact) .site-header .site-nav-menu-foot,
    html:has(.hero):not(.is-compact) .site-header .site-nav .dropdown-item {
        border-color: hsl(var(--foreground) / .15) !important;
    }

    html.is-compact .site-header .site-nav-section-label,
    html.is-compact .site-header .site-nav-menu-tagline,
    html:has(.hero):not(.is-compact) .site-header .site-nav-section-label,
    html:has(.hero):not(.is-compact) .site-header .site-nav-menu-tagline {
        color: hsl(var(--heading) / .5) !important;
    }

    html.is-compact .site-header .site-nav .font-size-btn,
    html:has(.hero):not(.is-compact) .site-header .site-nav .font-size-btn {
        border-color: hsl(var(--heading) / .35) !important;
    }

    html.is-compact .site-header .site-nav .font-size-btn.is-active,
    html:has(.hero):not(.is-compact) .site-header .site-nav .font-size-btn.is-active {
        border-bottom-color: hsl(var(--heading) / .7) !important;
    }

    html.is-compact .site-header .site-nav-logo-icon path,
    html:has(.hero):not(.is-compact) .site-header .site-nav-logo-icon path {
        fill: hsl(var(--heading) / .7) !important;
    }
}

/* 開選單的全螢幕遮罩（::before，slate/.72＋blur）也要跟著翻亮——
   ⚠ 不是可有可無的配色統一，是修 bug：≥1468 的殼有 transform（開合
     動畫），nav 因此自成堆疊環境，負 z-index 的 ::before 會畫在 nav
     **自己的背景上面**（painting order：元素背景 → 負 z 子元素 → 內容），
     slate/.72 罩住新的 220 面板變成 150,152,154 的髒灰（實測像素）。
     ≤1467 的 A 殼沒有 transform、::before 沉在面板後面，所以量起來
     沒事——但遮罩色一起翻，兩種寬度的「面板外淺色毛玻璃」才一致。
   ⚠ 疊色數學：遮罩與面板同色時 /.72 疊上去＝原色不變；
     疊在面板外的頁面上＝淺色毛玻璃（原本是深 slate 毛玻璃，深遮罩配
     亮面板會變成新的視覺衝突，一起翻掉）。blur 與動畫不動。
     （同日第二輪面板改白，遮罩跟著換 --brand-foreground/.72。） */
@media (max-width: 1467.98px) {
    .site-header .site-nav::before {
        background: hsl(var(--brand-foreground) / .72);
    }
}

@media (min-width: 1468px) {
    html.is-compact .site-header .inner > .site-nav::before,
    html:has(.hero):not(.is-compact) .site-header .inner > .site-nav::before {
        background: hsl(var(--brand-foreground) / .72);
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   五頁標題上移（2026-08-18 客戶：「服務專區跟ai分身還有電子報 搜尋跟
   知識專區頁面的標題上面有留白，讓他們都上移，現在留白太多」）
   ═══════════════════════════════════════════════════════════════════════
   兩個留白源頭（1920 實測）：
   ① 顧問案／AI 分身（唯二掛 .sv-hero 的頁）：.sv-hero-head 的
      margin-top: 300px（≈18117，08-13「標題壓低」設計）——標題壓到
      y≈476，可見留白 310px。收成 clamp 後標題區落點 ≈ 其他內頁的高度。
   ② 電子報／搜尋／知識列表（共用 .simpla-open 簡版頁首）：開場帶自己
      的上內距（--v2-band-y 的 simpla 值 ≈46px 可見），藏在「內頁頁首
      色帶延伸到視窗最上緣」那組 padding 補償公式（≈18250）裡——
      這裡照同公式改小「色帶自己」那一項（1.25rem），header-h 補償
      不動；特異度 5 class + :first-child，贏過補償組的 4 class。
   ⚠ 只動客戶點名的五頁：.sv-hero-head 只存在顧問案/AI 分身；三個列表
      頁用 page class 鎖定，member 註冊流程等其他 simpla 頁不受影響。
   ⚠ 第二輪修正（同日）：「在稍微下移30-50px」——上一輪收過頭，兩組
      都用 calc 明寫「+2.5rem（40px）」補回，別把 40px 揉進 clamp 裡
      讓下次調整看不出來這一步。 */
.sv-hero-head {
    margin-top: calc(clamp(2.5rem, 5vw, 4rem) + 2.5rem);
}

.is-designed .page.page--newsletter > .band:first-child,
.is-designed .page.page--search > .band:first-child,
.is-designed .page.page--knowledge > .band:first-child {
    padding-top: calc(1.25rem + 2.5rem + var(--header-h) + 1px);
}

/* 知識專區單篇文章頁補做（客戶：「知識專區的個別文章頁面標題沒改到」）
   ——.kd-hero-head 跟 .sv-hero-head 同款 margin-top: 300px（≈17850），
   改成跟服務內頁同一條公式，兩種文章頁的標題落點一致。
   ⚠ 包 min-width:768：≤767.98 另有一條 clamp(2.5rem,12vh,6rem)（≈17936）
     的手機版本，檔尾裸寫會連它一起蓋掉。 */
@media (min-width: 768px) {
    .kd-hero-head {
        margin-top: calc(clamp(2.5rem, 5vw, 4rem) + 2.5rem);
    }
}

/* 抽屜面板不加影子（2026-08-18 客戶：「不要幫導覽加影子」）——
   殼層原本的 box-shadow: 0 24px 60px（A 殼 ≈20160、B/C 殼 ≈20469）
   在深底時代幾乎看不見，面板翻亮後浮出來，客戶點名拿掉。
   兩組選擇器照殼層原樣鏡射，同特異度靠位置在後歸零。 */
@media (max-width: 1467.98px) {
    .site-header .site-nav {
        box-shadow: none;
    }
}

@media (min-width: 1468px) {
    html.is-compact .site-header .inner > .site-nav,
    html:has(.hero):not(.is-compact) .site-header .inner > .site-nav {
        box-shadow: none;
    }
}

/* ───────────────────────────────────────────────────────────────────────
   知識專區列表：整頁改左右分欄，左邊留白到 footer 前為止（2026-08-18
   客戶指定，第十八／十九輪）
   ───────────────────────────────────────────────────────────────────────
   客戶：「直接將該頁面分為左右分，左邊就是以目前左sticky為寬」
         ＋「連開場 H1 那段也切成左右」＋「footer不要左右分，結束到
         footer」。
   ⚠ 範圍：①開場（本節）＋③④篩選＋卡牆（那邊本來就已經是左右結構，
     不用再改）。footer 在 .page--knowledge 這個 div 外面（見 layout
     模板），天生就不受影響，不用特別排除。
   ⚠ 左欄寬度：原本以為「左sticky」是分類＋標籤兩欄合計（15rem+gap+11rem），
     但客戶後來指定「知識專區標題跟tags英文字齊頭」——實測 H1 應該落在
     x=376（＝section-x 88 ＋分類 15rem 240 ＋一個 gap 48），也就是
     「標籤篩選」那欄自己的起點，不是分類+標籤兩欄都讓開之後的起點
     （x=600）。所以「左sticky」指的其實是分類那顆灰底 sticky 本身
     （15rem），不含標籤欄；右欄內容起點只讓開一欄+一個 gap，
     跟標籤欄的起點共用同一條線，兩者才會「齊頭」。
   ⚠ 左欄刻意留空（沒有 aside、沒有內容）——客戶只說「以左sticky為寬」，
     沒有要求塞東西進去，貼齊 YAGNI，之後真的要放東西再加。
   ⚠ .simpla-pad 是 .page--simpla 共用元件（服務頁／電子報／搜尋頁都在
     用，見「知識專區列表：開場標題對齊 Logo」那節同樣的提醒），
     選擇器一定要留 .page--knowledge 前綴，不能動 .simpla-pad 本身。
   ⚠ 用「grid-column:2 蓋掉全部直接子元素」而不是改 HTML 包一層 wrapper：
     .wf-label／.simpla-open-body 兩個原本平行的子元素都推到右欄，
     左欄天生留空，不用動 knowledge_list.php 的結構（只新增不改寫）。 */
.page--knowledge .simpla-open .simpla-pad {
    display: grid;
    grid-template-columns: minmax(0, 15rem) minmax(0, 1fr);
    column-gap: clamp(1.5rem, 3vw, 3rem);
}

.page--knowledge .simpla-open .simpla-pad > * {
    grid-column: 2;
}

@media (max-width: 1023.98px) {
    /* 跟 .kz-layout 同一個斷點收單欄（下面篩選帶單欄也是在這個斷點），
       開場帶恢復原本的單欄排法，左邊那兩軌空白沒有意義。 */
    .page--knowledge .simpla-open .simpla-pad {
        display: block;
    }
}

/* ⚠ 2026-08-18 客戶指定（第二十輪）：「左邊灰色背景也請延伸上去」——
   灰底原本只在 .kz-filter 自己的 background（③④篩選＋卡牆那個
   section），構不到①開場。跟垂直線同一個處理方式：另外掛一顆貫穿全頁
   的色塊在 .page--knowledge::before（::after 已經是垂直線用掉了），
   高度、範圍（到 footer 前結束）都跟垂直線共用同一顆容器，不用再抓
   一次①開場的高度。
   ⚠ 寬度只到 .kz-filter 自己的軌寬（15rem，貼齊視窗左緣出血），不是
   到「左sticky」的合計寬（26rem+gap）——標籤那欄本來就刻意選白底
   （「白底＋sticky（推薦）」那輪的決定），灰底往上延伸也要維持同一個
   分界，不能把標籤欄那段也染灰。
   ⚠ 這塊跟 .kz-filter 自己的 background 在③④那段會重疊（兩邊都是
   同一顆 --background token），顏色一樣、疊在一起看不出接縫，不用
   特別裁切掉重疊區——裁切反而要多算一次①開場的精確高度，沒必要。
   ⚠ 生成順序：::before 永遠排在真正的子節點之前、::after 排在最後，
   所以灰底（::before）自然畫在所有文字內容底下、垂直線（::after）
   畫在最上面，不用額外設 z-index。
   ⚠ 2026-08-18 補修正（客戶回報「灰色背景寬還是不一樣」，複查）：上面這條
   `left: calc(-1 * var(--section-x))` 是照抄 .kz-filter 用負 margin 出血的
   直覺，但兩者機制不同——.kz-filter 是 grid item，align-items:stretch 下
   負 margin-left 會讓「有效寬度」自動吃掉那段負值（寬度變 15rem+section-x，
   右緣不動）；這裡是絕對定位的 ::before，left 只是單純平移整個盒子，
   不會像 grid item 那樣自動把右緣補回來。實測（1920 寬）：.kz-filter 右緣
   在 328px，這條 ::before 右緣卻只到 240px，整整差了一個 --section-x
   （88px）——這才是「灰色上下寬不一致」的真正成因，跟同一輪處理過的
   .kz-layout 撞名是兩件獨立的事（撞名那個會多出一截 SLATE 中灰色塊，
   這個是本來的淺灰本身就沒有延伸到位，兩者外觀都是「灰色怪怪的」很容易
   誤判成同一個 bug）。
   ⚠ 修法：既然 width 已經是正確的 section-x+15rem（總寬沒錯，只是起點錯），
   把 left 改成 0（相對 .page--knowledge 的 padding box，也就是 .kz-layout
   自己的左邊界，本來就已經貼齊視窗左緣，不需要再往左位移）即可讓右緣落在
   section-x+15rem，跟 .kz-filter 的右緣對齊。 */
.page--knowledge::before {
    content: "";
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    width: calc(var(--section-x) + 15rem);
    background: hsl(var(--background));
}

/* 小卡上的分類／標籤 pill 拿掉框線，選中才顯示（2026-08-18 客戶指定，
   第二十輪）。
   ⚠ .wf-card-cat／.wf-card-tag 沿用共用元件 .tag-pill 的
   border:1px solid hsl(var(--foreground)/.5)（見「1. 」節），預設一律有框。
   只在這一頁的卡牆生效，不動 .tag-pill 本身——同一元件還服務標籤篩選欄、
   篩選結果列、頁碼等多處。 */
.page--knowledge .kz-grid .pcard-tags .tag-pill {
    border-color: transparent;
}

/* ⚠ 2026-08-24 客戶回頭要求恢復：「選取到小卡標籤分類時現在是用框線匡，
   改成底變藍色」——推翻上面 2026-08-18「選中只要框線，不要藍色背景」
   那條指定。直接刪掉當時疊加的高特異度覆寫（背景/框色/圓角），讓
   .tag-pill.is-active 全站共用規則（「④ 選中態一律藍實心」節，
   background: var(--btn-fill)）在這裡重新生效，不必另寫新規則。 */

/* 知識專區卡牆：整張小卡 hover 淺灰底（2026-08-24 客戶「整張小卡滑鼠
   hover會改淺灰色底，要整張小卡喔」）。
   ⚠ .pcard 本身已經有 background: hsl(var(--muted))（現在是白，見
   tokens.css「--muted 這一輪被指定換成白」那節）＋transition: background
   var(--transition)，只要在這裡改 hover 的 background，既有的 transition
   會自動接手，不必另外宣告動畫時間。
   ⚠ 用 --surface-grey（#DCDCE1，tokens.css 明確標記「原 --muted」的那一階
   淺灰）而不是隨便挑一個灰階，跟站上其他「淺灰色塊」語彙一致。
   ⚠ 只在這一頁的卡牆生效，不動 .pcard 共用元件本身（課程／電子報／搜尋／
   相關文章等其他頁面沿用 .pcard 的地方不受影響）；跟站上既有的 .pcard:hover
   規則一樣包在 @media(hover:hover) 裡，觸控裝置不會卡在 hover 態。 */
@media (hover: hover) {
    .page--knowledge .kz-grid .pcard:hover {
        background: hsl(var(--surface-grey));
    }
}

/* 頁碼「01/03」旁邊加左右箭頭（2026-08-18 客戶「數字線保留數字就好線刪掉，
   旁邊加上左右箭頭」），複用的 .course-nav／.course-arrow 按鈕在這一頁生不出來，
   查出來是 .course-nav{display:none}（本檔 7233 一帶，2026-08-03「近期課程
   箭頭鈕改成圓框游標」那輪加的）——那條是裸選擇器、沒有限定頁面或區塊，
   home.php 近期課程／knowledge_detail.php 相關文章兩處都刻意要「拖曳取代
   按鈕」才特地全域關掉，但這裡（知識專區列表的靜態頁碼）沒有拖曳互動、
   也沒有 .course-cursor，箭頭就是唯一的操作提示，必須疊一條更高特異度的
   規則蓋回來，不動 7233 那條全域規則本身（它對另外兩處仍然是對的）。
   ⚠ 同一個 7237 裸規則還把 .course-meta 改成 justify-content:center（原本
   6062 的預設是靠左，同一輪「近期課程置中」順便帶的）——這裡沒有要跟著置中，
   這一頁「篩選結果」「卡牆」全部貼齊視窗右側那個網格的左緣，頁碼理應同一條
   基準線，所以一併蓋回靠左，不是漏改。 */
.page--knowledge .course-nav {
    display: flex;
}

.page--knowledge .course-meta {
    justify-content: flex-start;
}

/* ⚠ 2026-08-18 客戶追加：「數字位置下移，然後右移跟上面篩選對齊」。
   實測：.course-meta 左緣卡在 .kz-main 原本的邊界（x=600），但 .kz-grid／
   #wfFilterResult 兩者都疊了 margin-left:calc(1*var(--section-x)) 把整個
   卡牆／篩選結果列右推收窄（見「卡牆貼右邊緣」「篩選結果跟著卡牆一起
   右移」兩節），.course-meta 沒有跟著加這條，左緣因此落後卡牆一個
   --section-x（實測 88px＠1920 寬）。這裡補同一條 margin-left，跟卡牆／
   篩選結果共用同一個變數，之後卡牆內縮量再調，三者一起連動不會又對不齊。
   下移：原本 margin-top 是共用元件 .course-meta 的預設值 2rem（本檔
   6062 一帶），這裡加大到 clamp(2.5rem,4vw,4rem)，跟卡牆拉開更明顯的距離。 */
.page--knowledge .course-meta {
    margin-top: clamp(2.5rem, 4vw, 4rem);
    margin-left: calc(1 * var(--section-x));
}

/* ═══════════════════════════════════════════════════════════════════════
   課程內頁的灰全部改淺（2026-08-18 客戶附色票）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「實體課程單頁面進去背景灰色都改成這個灰」——色票像素實測
   rgb(246,246,247)＝既有 --background（全站頁面底那顆），不新增色票。
   原本這頁壓的是 --surface-grey（#DCDCE1），共五條（≈16886 .cd-hero／
   ≈16982 .cd-hero-text::before／≈17175 .cd-hero-mask／≈17245 手機版
   .cd-hero-text（在 max-width:767.98 內）／≈17337 通用 band），這裡同
   選擇器同特異度檔尾覆寫，一條不刪。
   ⚠ .cd-hero-mask 是進場動畫的「同色遮罩」（縮回前要跟底色融為一體），
     顏色必須永遠跟 .cd-hero 底同一顆——改底色時兩者一起換，漏一個
     動畫就會露出灰色方塊。
   ⚠ .cd-enroll-band 本來就是 --background（08-13 客戶指定更淺那輪），
     不在此列；courses 列表頁的 .cx-pane--grey 不屬於「單頁面」，不動。
   ⚠ footer 的延伸填色（「Footer 底色」區塊的 course-detail 那條）已同步
     改 --background。 */
.course-detail--editorial .cd-hero {
    background: hsl(var(--background));
}

.course-detail--editorial .cd-hero-text::before {
    background: hsl(var(--background));
}

.course-detail--editorial .cd-hero-mask {
    background: hsl(var(--background));
}

.course-detail--editorial .site-section.section-bleed.band:not(.cd-hero):not(.cd-enroll-band) {
    background: hsl(var(--background));
}

@media (max-width: 1023.98px) {
    .course-detail--editorial .cd-hero-text {
        background: hsl(var(--background));
    }
}

/* ── Hero 頁碼齊左圖片（2026-08-19 客戶：「hero圖片底下數字跟圖片齊左
   對齊」）——.hero-pageno 定位在 .hero-card-media（左緣＝主圖左緣），
   原本再推一顆 margin-left: clamp(1.25rem,2vw,2rem)（≈19687 那條，
   1920 實測 32px），拿掉後頁碼左緣＝圖片左緣（96=96）。
   只蓋 margin-left，bottom 與其他不動。
   ⚠ 2026-08-27 下界跟著 Card 版面一起降到 1200——不然 1200–1467 拿到桌機
     版型、頁碼卻還多推一顆 margin-left，實測會比圖片左緣多出 29px。 */
@media (min-width: 1024px) {
    .hero-pageno {
        margin-left: 0;
    }
}

/* ── 首頁知識專區左上編號改英文字體（2026-08-19 客戶指定）——
   .kz-stack-no（只用在 home.php 的知識堆疊）原本吃 --font-en（Inter），
   改成站上「英文小字」的 --font-eyebrow（Julius Sans One，跟 KNOWLEDGE
   ZONE 眉標、hero 頁碼同一顆），數字語彙全站一致。 */
.knowledge-section .kz-stack-no {
    font-family: var(--font-eyebrow);
}

/* ═══════════════════════════════════════════════════════════════════════
   手機版 Hero 改「上灰下白」拆帶（2026-08-19 客戶，一次三項）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「手機版的hero請改成灰色跟白色背景拆成上下，不要灰色背景疊加
   白色背景，圖片上面不要放往下查看更多，箭頭改放標題左邊然後一樣要
   上下兩個正方形，現在是一條橫的黑色長方形請修改」。
   範圍＝≤1199.98（堆疊版型與黑色橫條所在的既有斷點，跟 ≈1104 那組同界）。
   ① 上灰下白：原本 .hero 整片 --surface-grey、文字面板半透明白
      （--brand-foreground/.7，≈880）疊上去＝客戶說的「疊加」。改成
      .hero 畫白（下帶），灰帶用 .hero-media 的大 box-shadow halo 畫、
      clip-path 把圖片底緣以下的 halo 裁掉——灰剛好停在圖片底緣，
      分界線永遠貼著圖，不用追內容高度。文字面板改透明（白由 .hero 畫，
      不再有兩層底色疊出的濁色）。
   ② 「往下看更多」那條（.hero-card-left 的行動版）整條不顯示。
   ③ 箭頭復用桌機語彙（上下兩顆方塊＋分隔線，#808285 同款）搬到標題
      左邊：.hero-modules 改 grid 兩欄，箭頭欄 auto、文字欄 1fr——
      不用絕對定位疊字。黑色橫條的 row／滿寬／--foreground 底全部蓋回。
      margin-top 對齊標題列（模組上內距 1.5rem＋眉標行高與間距 ≈3.25rem，
      實測 76px），rem 寫法跟字級切換一起縮放。

   ⚠ 2026-08-25 斷點從 1199.98 加寬到 1467.98：客戶「hero寬1469以下
     就改手機版型呈現」那輪，先把本檔 ≈1108 那組較舊、較基礎的手機
     堆疊規則延伸到 1467.98（見該處新增段落），部署後客戶回報「寬1467
     到1200這區段圖片上方會露出白色矩形」——查出真正決定「往下看更多」
     顯示/隱藏、.hero-card 透明度、箭頭樣式、頁碼位置的其實是**這一條
     更新的規則**（2026-08-19 經過 11 輪客戶來回定案，選擇器與 ≈1108
     那組重疊，檔案順序在後、實際生效的是這裡），只延伸 ≈1108 沒有
     連這裡一起延伸，「往下看更多」那條在 1200–1467.98 沒有被隱藏
     （② 那條 `.hero-card-left{display:none}` 沒生效），底色又是
     `.hero-card` 自己的米白（`.hero-card{background:transparent}`
     也沒生效），兩個沒被隱藏的空白疊在一起就是客戶看到的白色矩形。
   ⚠ 只改這裡的斷點數字（1199.98→1467.98），規則本體一行都沒動——
     這條規則已經是經過 11 輪反覆調整、客戶親自定案的完整版本，改用
     複製一份到別的斷點的做法風險更高（兩份日後任何一輪再調整都要
     記得同步改兩處，這條規則的調整密度明顯偏高，複製維護成本比
     ≈1108 那組基礎規則高很多），直接加寬適用範圍才是 SSOT。
   ⚠ `--hero-split-y` 這顆 CSS 變數由 home.js「手機版 Hero 上灰下白的
     分界線」那支 IIFE 寫入，JS 端**沒有寬度判斷**（該段註解本身就寫
     「桌機（≥1200）CSS 那條 gradient 不生效，這個變數寫了也沒人讀，
     不用在 JS 端判斷寬度」）——擴大 CSS 生效範圍後，JS 原本就已經在
     每個寬度都正確計算並寫入這個變數，不需要另外改 home.js。 */
/* ⚠ 2026-08-27 客戶「hero在寬度1460-1200時樣式請跟桌機一樣」——上面那次
     加寬（1199.98→1467.98）**收回來**，恢復成原本的 1199.98，
     1200–1467.98 交還給桌機 Card 版面。上面整段註解原樣保留不刪，那是
     上一輪的判斷依據。
   ⚠ **這一層就是上面註解自己在講的「兩層疊在一起、檔案順序在後的這層
     才真正生效」**。這次延伸／收回斷點時，∽22240 那兩段（安全網＋基礎
     手機堆疊）與這一層必須**一起改**，只改其中一邊會出現「版面切了一半」
     ——實測只改前面三處時，1200–1467 的主圖寬度剩 192px、文字模組跑到
     y=-665，就是因為這一層還停在 1467.98 蓋在上面。 */
@media (max-width: 1023.98px) {
    /* ⚠ 分界線的實作走了三版：①圖片 halo＋clip-path inset 負值——Chrome
       的 inset() 負值被夾回元素框，halo 整圈被裁光；②位移陰影（spread
       100vmax＋上移 100vmax）——.hero-card／.hero-inner／.hero 三層都是
       overflow:hidden，陰影出不了卡片框（而卡片內圖片滿版、無縫可露）。
       ③定案：分界 y 由 home.js 檔尾新 IIFE 量（.hero-media 底緣相對
       .hero 頂），寫進 --hero-split-y，這裡用雙色硬切 gradient 畫在
       .hero 自己身上——不經過任何 overflow，怎麼裁都裁不到。
       fallback 100%＝JS 未跑前整片灰（舊觀感），跑完立刻分帶。 */
    /* 第三輪（2026-08-19 客戶）：「hero背景灰底跟白色位置上下交換」——
       上帶改白、下帶改灰，分界線機制不動（還是圖片底緣）。 */
    .hero {
        background: linear-gradient(
            hsl(var(--brand-foreground)) var(--hero-split-y, 100%),
            hsl(var(--surface-grey)) var(--hero-split-y, 100%)
        );
    }

    .hero-text-module {
        /* 第三輪：「標題跟文字那段不要淡灰色背景用白色背景」——下帶改灰
           之後，文字段改成白色實色面板浮在灰帶上（原本第一版的半透明白
           →第二版透明吃白帶→這輪白色實色）。 */
        background: hsl(var(--brand-foreground));
        /* 第七輪（「文字矩形這塊太高，讓這區塊矮一點」）：上下內距
           24 → 16px。 */
        padding-block: 1rem;
    }

    /* 第七輪：面板內距離逐段收（實測 414：眉標上方還多 24、副標→介紹
       20、介紹→按鈕 20）——眉標上方歸零；「文字小字兩段距離太遠」＝
       副標（一天聽一點・…）與介紹段的 20px 收到 8px；其餘小收一階。 */
    /* 眉標上方那 24px 的真身＝.hero-text-item.is-active 的
       padding-top:1.5rem（≈913，08-14 桌機「文字下移一點」）——
       行動版面板要矮，這裡歸零；桌機不動。 */
    .hero-text-item.is-active {
        padding-top: 0;
    }

    .hero-text-module .hero-text-eyebrow {
        margin: 0 0 .625rem;
    }

    .hero-text-module .hero-text-title {
        margin: 0 0 .625rem;
    }

    .hero-text-module .hero-text-sub {
        margin: 0 0 .5rem;
    }

    .hero-text-module .hero-text-intro {
        margin: 0 0 1rem;
    }

    /* .hero-card 基準規則自帶 --background 米白底（≈19393），會在卡片
       範圍內把 .hero 的上下分帶蓋成 246 一整塊——行動版收透明，
       帶色全部交給 .hero 的 gradient 畫。 */
    .hero-card {
        background: transparent;
    }

    .hero-card-left {
        display: none;
    }

    /* ③-第三輪修正（2026-08-19 客戶：「箭頭上下放改左右放然後整個移到
       文字底下不要放同一行，數字也不要放在文字背景那塊」）——第二輪的
       「絕對定位釘文字段右下」撤掉：箭頭回到文件流（.hero-modules 裡
       text 之後的原生順序，自然落在白色文字面板下方、灰帶上），兩顆
       方塊改左右並排、中間分隔線轉直向；靠左對齊內容左緣。頁碼本來就
       在 .hero-modules 外（文字面板的白蓋不到它），維持靠左，自然坐在
       灰帶上＝「不放在文字背景那塊」。 */
    /* ③-第四輪微調（2026-08-19 客戶：「箭頭數字跟圖片靠左對齊，並箭頭
       且上面不要有空間貼齊上方白色矩形，灰色背景高跟文字白色矩形對齊，
       現在灰色背景比較低」）——
       ・箭頭 margin 歸零：上貼白色面板底緣（0 間距）、左緣退到容器邊
         ＝圖片左緣（17px，不再多推 --section-x）。
       ・頁碼 margin-left 歸零：同樣對齊圖片左緣（上緣與 text-align:left
         沿用 ≈1170 既有規則的 padding-top，不動）。
       ・灰帶尾巴修掉：實測內容底（頁碼 b=722）之後還拖 77px 灰
         （.hero-card margin-bottom 44.8 ＋ .hero padding-bottom 32）。
         第五輪（「數字跟底下服務專區請留距離現在貼太近」）：兩顆全歸零
         貼太死，改成只歸零 .hero-card 的 44.8，保留 .hero 自己的
         padding-bottom 32px 當數字與下一區的距離。 */
    /* 第六輪（2026-08-19 客戶：「箭頭放右邊，數字放左邊」）——同一行：
       箭頭塊 margin-left:auto 靠右（右緣＝容器右緣＝圖片右緣），數字用
       負 margin-top 拉上來跟箭頭同列、維持靠左；padding-top 從 .875rem
       收到 .5625rem 讓數字行在 40px 高的箭頭列裡視覺置中。 */
    .hero-arrow-module {
        flex-direction: row;
        justify-content: flex-start;
        gap: 0;
        width: max-content;
        margin: 0 0 0 auto;
        padding: 0;
        background: #808285;
    }

    .hero-arrow {
        flex: none;
        width: 2.5rem;
        height: 2.5rem;
    }

    .hero-arrow-divider {
        width: 1px;
        height: auto;
        align-self: stretch;
    }

    .hero-pageno {
        text-align: left;
        margin-left: 0;
        margin-top: -2.5rem;
        /* 第八輪「數字位置下移一點」→ 追加訂正「下移到對齊箭頭底部」：
           改 flex 對底，行盒底緣＝列底緣＝箭頭方塊底緣（670），
           不再用 padding-top 慢慢湊。 */
        display: flex;
        align-items: flex-end;
        padding-top: 0;
        /* ⚠ 第六輪補：頁碼負 margin 拉上來後，卡片（overflow:hidden）的
           流内高度只算到頁碼底，40px 高的箭頭列多出的下緣被裁
           （客戶「箭頭被裁到了」）。頁碼列撐到跟箭頭同高（2.5rem），
           卡片底就回到箭頭底緣，不裁。 */
        min-height: 2.5rem;
        /* ⚠ 同一招的第二個坑（客戶「箭頭只有下一頁有反應」）：頁碼盒
           拉上來後橫跨 17..377，DOM 序又在箭頭之後——hit-testing 蓋住
           ‹ 整顆與 › 的左半，只剩 › 右緣 20px 點得到。頁碼是純顯示
           文字（home.js 只改字），關掉點擊穿透即可。 */
        pointer-events: none;
    }

    .hero-card {
        margin-bottom: 0;
        /* 第十一輪（2026-08-19 客戶：「圖片離header距離101改成76-80左右」）
           ——101 的組成＝header 區 56＋卡片自己的 margin-top 44.8；
           margin-top 收到 1.375rem（22px），圖片上緣落在 78。 */
        margin-top: 1.375rem;
    }

    /* 第九輪（2026-08-19 客戶：「跟底下服務專區的留白空間請跟上方header
       留白空間一致」）——第一解讀取 header 下緣→圖片上緣的 45px，客戶
       追訂正「圖片上緣101，所以離服務專區也要101」＝取視窗頂到圖片
       上緣的**整段** 101px（含 header 區）。
       第十一輪再改：上下都收到 76–80 → 取 78（4.875rem）。 */
    .hero {
        padding-bottom: 4.875rem;
    }
}

/* 第十輪（2026-08-19 客戶：「圖片高一點，可以裁切到」）——真手機的主圖
   從實際呈現的 16:9 加高到 4:3（414 寬實測 214px → 285px），照片
   object-fit:cover 多裁上下、不變形。只動 ≤767.98；平板維持原比例。 */
@media (max-width: 767.98px) {
    .hero-media {
        aspect-ratio: 4 / 3;
    }

    .hero-media img {
        width: 100%;
        height: 100%;
        object-fit: cover;
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   手機抽屜改用桌機選單語彙・直式（2026-08-19 客戶：「手機版的漢堡選單
   請刪除原本的樣式，用現在的電腦的選單改直式」）
   ═══════════════════════════════════════════════════════════════════════
   舊手機抽屜＝左滑窄面板（寬 92%、頂部懸空 36px）＋項目圖示＋群組分隔
   線，AAA 字級切換器被舊規則絕對定位到面板頂端而且內容不可見。這裡照
   桌機（A 殼）的視覺語彙整組蓋掉：滿版白面板從視窗頂展開、純文字項目、
   NAVIGATION／MEMBER／TOOLS 眉標分區、無分隔線、版權列 hairline 收尾
   ——內容排成單欄直式（DOM 順序本來就對：logo→標語→三個分區→foot）。
   ⚠ 照慣例只加不改：舊行動版規則全數留著，這裡同/更高特異度＋檔尾
     位置＋（殼與強制顯示處）!important 蓋過去。文字顏色不用另寫——
     A 殼那組黑 70% !important（≤1467.98）本來就涵蓋手機。 */
@media (max-width: 991.98px) {
    /* 殼：滿版白、從頂端展開（鏡射 A 殼手勢） */
    .site-header .site-nav {
        /* 內距抽成變數：三條全寬橫線要用負 margin 出血到面板邊，
           兩處必須讀同一個數字。 */
        --mdrawer-pad: clamp(1.5rem, 6vw, 2.5rem);
        display: flex !important;
        flex-direction: column;
        align-items: stretch;
        position: fixed;
        /* 第四輪（2026-08-19 客戶「漢堡選單不要蓋住整個頁面寬縮窄」）：
           滿版改左側窄抽屜，右側露出頁面、由 ::before 白色毛玻璃遮罩
           蓋住。 */
        inset: 0 auto 0 0;
        width: min(85%, 22rem);
        max-width: none;
        height: auto;
        max-height: none;
        margin: 0;
        padding: 5.5rem var(--mdrawer-pad) 1.5rem;
        border: 0;
        border-radius: 0;
        background: hsl(var(--brand-foreground));
        box-shadow: none;
        transform: translateY(-16px);
        opacity: 0;
        visibility: hidden;
        transition: transform 280ms var(--ease-out), opacity 280ms var(--ease-out),
                    visibility 0s linear 280ms;
        overflow-y: auto;
        overscroll-behavior: contain;
        z-index: 60;
    }

    .site-header .site-nav.is-open {
        /* transform 收到 none（不是 translateY(0)）：面板縮窄後，全螢幕
           遮罩 ::before 是 position:fixed——只要 nav 還帶著 transform，
           fixed 會退化成只罩面板自己（transformed 祖先的既有坑），
           面板右側的頁面就沒有遮罩。開合動畫只在過場吃 transform，
           定格狀態歸 none 讓遮罩鋪滿視窗。 */
        transform: none;
        opacity: 1;
        visibility: visible;
        transition: transform 280ms var(--ease-out), opacity 280ms var(--ease-out);
    }

    /* 項目圖示（wf_icon svg）＝舊手機樣式，桌機選單是純文字——整批藏。
       ✕ 關閉鈕是 span 畫的線，不受影響；logo 的品牌圖示要留（桌機
       選單也有），用 :not 放行。 */
    .site-header .site-nav a:not(.site-nav-logo) svg,
    .site-header .site-nav .dropdown-toggle svg {
        display: none !important;
    }

    /* 圖示藏掉後，主項殘留的圖示縮排歸零——項目左緣跟眉標/子項同一條線
       （桌機欄內對齊方式）。搜尋的文字包在 .site-search-label 裡、
       自帶一層縮排，一併歸零。 */
    .site-header .site-nav > a,
    .site-header .site-nav .dropdown > .dropdown-toggle {
        padding-left: 0;
        margin-left: 0;
    }

    .site-header .site-nav .site-search .site-search-label {
        margin-left: 0;
        padding-left: 0;
    }

    /* 分隔線全滅（桌機語彙「不加細線」），版權列上緣那條照桌機留。 */
    .site-header .site-nav > a,
    .site-header .site-nav .dropdown,
    .site-header .site-nav .dropdown-item,
    .site-header .site-nav .site-nav-group-end,
    .site-header .site-nav .dropdown-menu {
        border: 0 !important;
        background: transparent;
    }

    /* 分區眉標與項目節奏（比照桌機欄內的上下距） */
    .site-header .site-nav .site-nav-section-label {
        margin: 2rem 0 .5rem;
    }

    .site-nav-logo {
        margin: 0 0 .75rem;
    }

    .site-header .site-nav .site-nav-menu-tagline {
        margin: 0;
    }

    /* AAA 字級切換：舊規則把它定位到面板頂端且不可見——拉回文件流
       （DOM 位置就在 TOOLS 眉標後面），比照桌機 TOOLS 欄直向排列。 */
    .site-header .site-nav .font-size-switch {
        position: static !important;
        display: flex !important;
        flex-direction: column;
        align-items: flex-start;
        gap: 1.25rem;
        width: auto;
        height: auto;
        margin: .25rem 0 0;
        padding: 0;
        border: 0 !important;
        background: transparent !important;
        transform: none;
        opacity: 1 !important;
        visibility: visible !important;
        /* 舊的統一列規則給過 flex order，會把 AAA 排到 logo 前面——
           歸零讓 flex 回到 DOM 順序（TOOLS 眉標之後）。 */
        order: 0 !important;
    }

    /* ✕ 舊定位（top 2.5rem＋translateY(-50%)）在滿版面板上緣會被切到
       2px——改成不位移的固定右上角。 */
    .site-nav-close {
        top: 1.25rem;
        right: 1.25rem;
        transform: none;
    }

    .site-header .site-nav .font-size-switch .font-size-menu {
        position: static !important;
        display: flex !important;
        flex-direction: column;
        align-items: flex-start;
        gap: 1.25rem;
        width: auto;
        height: auto;
        margin: 0;
        padding: 0;
        border: 0 !important;
        background: transparent !important;
        box-shadow: none !important;
        transform: none;
        opacity: 1 !important;
        visibility: visible !important;
    }

    .site-header .site-nav .font-size-btn {
        display: inline-flex !important;
        width: auto;
        height: auto;
        min-width: 0;
        min-height: 0;
        padding: 0;
        background: transparent !important;
        opacity: 1 !important;
        visibility: visible !important;
        /* 桌機 TOOLS 欄是純文字字級選項、只有選中那顆有底線——手機把
           按鈕外框拿掉，補回選中態底線。 */
        border: 0 !important;
    }

    .site-header .site-nav .font-size-btn.is-active {
        border-bottom: 1px solid hsl(var(--heading) / .7) !important;
    }

    /* 版權列：hairline 收尾、flex 推到面板底（內容短時貼底、長時自然接尾） */
    .site-header .site-nav .site-nav-menu-foot {
        margin-top: auto;
        padding-top: 1.25rem;
        border-top: 1px solid hsl(var(--foreground) / .15) !important;
    }
}

/* ── 手機抽屜第二輪微調（2026-08-19 客戶，六項一次）──────────────────
   「顧問案跟ai分身收進去服務專區，logo底下的副標語刪除，aaa刪掉，
   底下的版權刪掉，logo底下加一條橫線，搜尋不是次選項請跟電子報用
   一樣的字粗度還有靠左對齊」。全部只作用 ≤991.98 的直式抽屜。 */
@media (max-width: 991.98px) {
    /* ① 顧問案／AI 分身收進服務專區：拿掉行動版「靜態展開」，回歸
       Bootstrap 點擊展開（桌機抽屜維持原本的靜態展開排版不動）。 */
    /* ⚠ A 殼的靜態展開是 display:block !important（≈20353，服務桌機
       抽屜的排版），手機要收合必須同帶 !important 蓋回。 */
    .site-header .site-nav .site-nav-menu-service .dropdown-menu {
        display: none !important;
    }

    .site-header .site-nav .site-nav-menu-service .dropdown-menu.show {
        display: block !important;
    }

    /* ② logo 底下的副標語刪除 */
    .site-header .site-nav .site-nav-menu-tagline {
        display: none !important;
    }

    /* ③ AAA 字級切換刪掉（連同 TOOLS 眉標——它只服務 AAA） */
    .site-header .site-nav .font-size-switch,
    .site-header .site-nav .site-nav-menu-label-tools {
        display: none !important;
    }

    /* ④ 底下的版權列刪掉 */
    .site-header .site-nav .site-nav-menu-foot {
        display: none !important;
    }

    /* ⑤ logo 底下加一條橫線（副標語刪掉後由這條線收住品牌區）。
       ⚠ 舊 logo 規則帶 padding:0 !important（≈8286），線與字的距離
       要用 !important 才推得開。 */
    .site-header .site-nav .site-nav-logo {
        padding-bottom: 1.25rem !important;
        margin-bottom: 1rem;
        /* ⚠ 上一輪的「分隔線全滅」是 border:0 !important、logo 也算
           `> a` 被掃到——這裡同帶 !important、位置在後才蓋得回來。 */
        border-bottom: 1px solid hsl(var(--foreground) / .15) !important;
    }

    /* ⑥ 搜尋比照電子報主項：同字重、同左緣。
       ⚠ var(--fw-normal) 在這層解析成 200（token 漂移，電子報實測 350）
         ——改用 inherit 直接吃父層 <a> 的字重，token 再漂也跟著對。
       ⚠ 縮排的真身是 .site-search 自己的 padding-left:12px（(0,3,0)，
         比先前 `> a` 那條 (0,2,1) 特異度高），同階同名在後蓋回。 */
    .site-header .site-nav .site-search {
        padding-left: 0;
    }

    .site-header .site-nav .site-search .site-search-label {
        font-weight: inherit !important;
        font-size: inherit;
        line-height: 1.6;
    }
}

/* ── 手機抽屜第三輪微調（2026-08-19 客戶）────────────────────────────
   「該區段的線不要圓角直接橫線連整個導覽寬，logo離底下英文字遠一點
   距離跟知識專區離英文字距離一樣，服務專區收合的兩項加上箭頭點擊
   展開收合的項目，會員也是這樣，知識專區底下加橫線，搜尋底下也是」
   ＋追加「導覽的hover不要圓角」。 */
@media (max-width: 991.98px) {
    /* 三條橫線（logo 底／知識專區底／搜尋底）：直角、出血到面板整寬
       ——負 margin 吃掉殼的內距、再用同值 padding 補回，文字不動、
       線貼左右邊。 */
    .site-header .site-nav .site-nav-logo,
    .site-header .site-nav .site-nav-menu-knowledge,
    .site-header .site-nav .site-search {
        margin-inline: calc(-1 * var(--mdrawer-pad));
        /* ⚠ padding 帶 !important：logo 舊規則是 padding:0 !important
           （≈8286），不同權重內距會被吃掉、圖示貼到面板左緣。 */
        padding-inline: var(--mdrawer-pad) !important;
        /* ⚠ 舊規則給過 fit-content 類寬度——負 margin 只會把盒子往左推、
           右側的線到不了邊。改回 stretch 讓寬度＝容器＋兩側出血。 */
        align-self: stretch !important;
        width: auto !important;
        border-radius: 0 !important;
        border-bottom: 1px solid hsl(var(--foreground) / .15) !important;
        padding-bottom: 1.25rem !important;
    }

    /* 段落距離統一：每條線之後、下一段英文眉標之前都是同一段距——
       線元素不再各帶 margin-bottom，距離全部交給眉標的 margin-top。 */
    .site-header .site-nav .site-nav-logo {
        margin-bottom: 0;
    }

    .site-header .site-nav .site-nav-section-label {
        margin: 2rem 0 .5rem !important;
    }

    /* 會員比照服務專區：收合、點擊展開（同樣要 !important 蓋 A 殼的
       靜態展開）。 */
    .site-header .site-nav .site-nav-menu-member .dropdown-menu {
        display: none !important;
    }

    .site-header .site-nav .site-nav-menu-member .dropdown-menu.show {
        display: block !important;
    }

    /* 面板縮窄後右側露出的頁面要有遮罩。
       ⚠ 不能用面板自己的 ::before：面板 overflow-y:auto 會把 fixed 偽
         元素裁在面板框內（實測全寬度都如此，那層遮罩其實從沒罩出去過）。
       改重新啟用 ≈9026 被停用的 body::after 全螢幕遮罩（z-55，低於
       header/抽屜的 60），樣式換成亮色版的白毛玻璃。 */
    .has-menu-open body::after {
        content: "";
        display: block;
        position: fixed;
        inset: -2px;
        z-index: 55;
        background: hsl(var(--brand-foreground) / .72);
        /* 2026-08-19 客戶「導覽手機版展開背景不要毛玻璃」——blur 移除，
           只留半透明白壓淡頁面。 */
        -webkit-backdrop-filter: none;
        backdrop-filter: none;
    }

    /* 收合項的 chevron 箭頭亮回來（前輪把 toggle 的 svg 全藏了；
       chevron 是 toggle 裡最後一顆 svg，項目圖示維持隱藏），展開時
       轉 180°。 */
    .site-header .site-nav .dropdown-toggle svg:last-of-type {
        display: inline-block !important;
        transition: transform 200ms var(--ease-out);
    }

    .site-header .site-nav .dropdown-toggle[aria-expanded="true"] svg:last-of-type {
        transform: rotate(180deg);
    }
}

/* hover 不要圓角：抽屜內所有項目的滑過/按壓底色一律直角（全寬度）。 */
.site-header .site-nav a,
.site-header .site-nav .dropdown-toggle,
.site-header .site-nav .dropdown-item,
.site-header .site-nav button {
    border-radius: 0 !important;
}

/* ── 手機 footer 齊頭（2026-08-19 客戶：「實體課程請下移跟服務專區齊頭，
   電子報下移跟會員齊頭」）——手機兩欄格裡，右欄（實體課程／電子報）
   沒有英文眉標，起頭比左欄的主項高 50px（＝眉標 25px＋間距）。補一段
   等高 padding-top，右欄首項與左欄主項同一條線（實測 171/365 對齊）。
   只動 ≤991.98；桌機四欄版有自己的 grid-row 對齊，不受影響。
   ⚠ 2026-08-21「footer 平板改跟電腦一樣」：四欄版斷點下修到 768
   （≈22869），這裡與下面兩個手機 footer 區塊同步收到 ≤767.98。 */
@media (max-width: 767.98px) {
    .footer-main > nav.footer-col:nth-of-type(2),
    .footer-main > nav.footer-col:nth-of-type(4) {
        padding-top: 3.125rem;
    }
}

/* ── 手機 footer 改單欄直列＋群組橫排（2026-08-19 客戶）─────────────
   「服務專區的次項顧問案跟ai分身移到服務專區右邊同行，橫式排，兩個都
   跟服務專區齊頭，實體課程跟知識專區下移服務專區靠左對齊，會員也是
   這樣改，然後電子報跟搜尋下移」。
   新排列（上而下）：NAVIGATION 眉標→「服務專區 顧問案 AI 分身」一行
   →實體課程→知識專區→MEMBER 眉標→「會員 登入 註冊」一行→電子報
   →搜尋→NEWSLETTER 訂閱。兩欄格改單欄；上一輪「右欄補眉標等高內距」
   在單欄下不再需要，同選擇器在後歸零。
   ⚠ 2026-08-21 斷點 991.98→767.98（平板改用桌機四欄版，見 ≈22869）。 */
@media (max-width: 767.98px) {
    .footer-main {
        grid-template-columns: minmax(0, 1fr);
    }

    .footer-main > .footer-brand,
    .footer-main > nav.footer-col,
    .footer-main > .footer-subscribe {
        grid-column: 1;
    }

    /* 標題群組（服務專區／會員）：眉標自己一行（flex-basis 100%），
       標題與次項同一行橫排、基線齊頭。 */
    .footer-main > nav.footer-col:nth-of-type(1),
    .footer-main > nav.footer-col:nth-of-type(3) {
        display: flex;
        flex-wrap: wrap;
        align-items: baseline;
        column-gap: 1.5rem;
    }

    .footer-main > nav.footer-col:nth-of-type(1) .footer-sec-label,
    .footer-main > nav.footer-col:nth-of-type(3) .footer-sec-label {
        flex: 0 0 100%;
    }

    .footer-main > nav.footer-col:nth-of-type(1) .footer-col-title,
    .footer-main > nav.footer-col:nth-of-type(3) .footer-col-title {
        margin: 0;
    }

    .footer-main > nav.footer-col:nth-of-type(1) .footer-col-list,
    .footer-main > nav.footer-col:nth-of-type(3) .footer-col-list {
        display: flex;
        flex-direction: row;
        flex-wrap: wrap;
        column-gap: 1.25rem;
        row-gap: .5rem;
        margin: 0;
        padding: 0;
    }

    /* 次列群組（實體課程/知識專區、電子報/搜尋）：下移到各自群組下方、
       靠左；上一輪的眉標等高 padding 歸零。 */
    .footer-main > nav.footer-col:nth-of-type(2),
    .footer-main > nav.footer-col:nth-of-type(4) {
        padding-top: 0;
    }
}

/* ── 手機 footer 直列等距（2026-08-19 客戶：「實體課程離知識專區的距離
   請跟服務專區離實體課程的距離一樣，電子報跟搜尋也一樣」）——
   組間（服務專區→實體課程／會員→電子報）實測 59px，清單內
   （實體→知識 43／電子報→搜尋 42）差 16px＝清單 gap 1.1rem 太小，
   加到 2.125rem 後 top-to-top 同為 59px。
   ⚠ 2026-08-21 斷點 991.98→767.98（平板改用桌機四欄版，見 ≈22869）。 */
@media (max-width: 767.98px) {
    .footer-main > nav.footer-col:nth-of-type(2) .footer-col-list,
    .footer-main > nav.footer-col:nth-of-type(4) .footer-col-list {
        gap: 2.125rem;
    }
}

/* ── Hero 小卡標題改用全站 h1 字級（2026-08-19 客戶）───────────────
   「hero小卡的標題請用全站字級h1大小」——比照 .page-title（≈11063）
   讀同一組 token（--text-section／--lh-section，桌機＝--fs-5 40px），
   不再用自己的 clamp(1.15–1.5rem)。 */
.hero-text-module .hero-text-title {
    font-size: var(--text-section);
    line-height: var(--lh-section);
}

/* 「手機版h1縮小一點有點大」——手機 --fs-5 是 28px；只縮 h1 級的兩個
   消費者（內頁 .page-title、hero 小卡標題）到 24px，不動 --fs-5 本身
   （它同時餵 h2 級的區塊主標，動 token 會整批縮）。 */
@media (max-width: 1023.98px) {
    .page-title,
    .sv-hero-head .page-title,
    .hero-text-module .hero-text-title {
        font-size: 1.5rem;
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   首頁實體課程小卡：一張一張的線框改「上下軌線＋卡側直線」
   （2026-08-19 客戶：「加上兩條頁面寬的水平線上下各一條，小卡每張
   個兩條垂直線，長度為小卡高度，用此方式取代本來一張一張線框描邊」）
   ═══════════════════════════════════════════════════════════════════════
   ・上下軌線畫在 .course-grid（捲動容器）自己的 border 上——邊框不隨
     內容捲動，天生是固定軌道；左右負 margin 出血到頁面整寬、同值
     padding 補回卡片起點，卡片位置一個 px 不動（副作用：捲動時卡片
     會滑到視窗邊緣才被裁，原本在版心邊就被裁——軌道通到頁緣後這樣
     反而合理）。
   ・卡側直線＝ .course-cell 的左右 border，長度天生等於小卡高度，
     跟著卡片一起捲動。
   ・線色第一版沿用舊框的 --surface-dark #7C7D80，客戶「有點太黑了，
     線請用 courses/strategy 那頁的線樣式」——該頁實測代表線（cd-h2
     標題底線／表格列線）＝ foreground/.16 hairline，改用同一杯。
   ⚠ 只鎖 .course-section（首頁實體課程）：知識單篇「相關文章」沿用
     .course-grid 系 class 但沒有這個 section 包裹，維持原本樣式。 */
.course-section .course-link {
    border: 0;
}

.course-section .course-grid {
    margin-inline: calc(-1 * var(--section-x));
    padding-inline: var(--section-x);
    /* ⚠ 輪播帶 scroll-snap：snapport 預設不含 padding，出血後首卡會被
       吸到視窗邊（實測 scrollLeft 自動跑到 88）——scroll-padding 同步
       內縮，吸附點回到版心邊。 */
    scroll-padding-inline: var(--section-x);
    border-block: 1px solid hsl(var(--foreground) / .16);
}

.course-section .course-cell {
    border-inline: 1px solid hsl(var(--foreground) / .16);
}

/* ── 服務專區→實體課程 取消疊上（2026-08-19 客戶：「服務專區到實體課程
   不要下一張疊上來的方式」）——堆疊捲動區塊（≈19260 一帶）原本讓
   .service-section 釘住、實體課程從下方滑上來蓋過它。這裡只解除
   服務專區的釘住（改回一般流動，實體課程正常接續往上推）；
   hero→服務專區的簾幕、實體課程→知識專區的堆疊都維持。
   ⚠ position 用 relative 不用 static：.hero ~ * 的 z-index:1 疊層
     依賴 positioned（sticky 簾幕的必要配套），退回 static 會讓
     服務專區被 hero 蓋住。
   ⚠ 灰帶三連環（同日三則回報收斂的定案，數據實測過，改前先讀）：
     幾何事實：.svc-main（磁磚欄）用負 margin 上下出血——上緣拉進
     padding-top 區、下緣「懸出」流內版面 112px，靠區塊原本的
     padding-bottom（--section-seam-band，剛好 112px）接住。
     ① 灰帶的真兇＝堆疊停留用的 min-height:100svh 把區塊撐得比內容
       （630px）高一大截，多的全是 --background 底 → min-height 歸零。
     ② 我一度誤判成 padding-bottom 並把它歸零——磁磚懸出的 112px
       立刻被後面的實體課程蓋掉（客戶：「高度不夠，這兩塊都被擠到了」）
       ——padding-bottom 是磁磚的容身處，**不能動**。
     ③ A/B 實測：sticky 開關與磁磚高度無關；正解＝min-height:0＋
       padding-bottom 維持原值，區塊高恰好 630、磁磚底緣＝區塊底緣，
       無帶且完整。 */
@media (min-width: 1468px) {
    .service-section {
        position: relative;
        top: auto;
        min-height: 0;
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   服務專區四色塊改「遮罩切塊進場」（2026-08-19 客戶：「服務專區不是有
   四個色塊灰色嗎，讓他們是遮罩動畫切塊進場的」）
   ═══════════════════════════════════════════════════════════════════════
   四塊＝主打文字面板（.svc-feature-info）／主打照片（.svc-feature-media）
   ／02、03 磁磚。與課程內頁 cd-hero 遮罩同語彙：每塊蓋一片與頁底同色
   （--background）的遮罩，進場時交錯縮走＝「底色退開、露出內容」。
   ・照片塊：第一代（07-31「簾幕收掉」）的 .svc-feature-media::before
     遮罩本來就是這個做法——第二代「填色」時代被 clip-path 揭開取代，
     這裡把第二代的 clip 靜態化，照片交還給第一代遮罩。
   ・磁磚：第二代「先透明、色塊填進來」退役——磁磚回到靜態上色
     （var(--tile-fill)），::before 從填色層轉職成遮罩（同名屬性後位
     覆蓋，一條不刪）。
   ・交錯節奏：面板 0 → 照片 140 → 02 280 → 03 420ms，方向左右交替，
     讀起來是一條線掃過四塊。 */
.svc-layout.js-split .svc-tile {
    background: var(--tile-fill);
}

.svc-layout.js-split .svc-tile::before {
    z-index: 3;
    background: hsl(var(--background));
    transform: scaleX(1);
    transform-origin: right center;
    transition: transform 640ms var(--cal-ease);
}

.svc-layout.js-split.is-in .svc-tile::before {
    transform: scaleX(0);
}

.svc-layout.js-split .svc-feature-media {
    clip-path: inset(0);
    transition: none;
}

.svc-layout.js-split .svc-feature-info {
    position: relative;
}

.svc-layout.js-split .svc-feature-info::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 3;
    background: hsl(var(--background));
    transform: scaleX(1);
    transform-origin: left center;
    pointer-events: none;
    transition: transform 640ms var(--cal-ease);
}

.svc-layout.js-split.is-in .svc-feature-info::after {
    transform: scaleX(0);
}

/* 交錯：面板 → 照片 → 02 → 03（方向：面板往左收、照片往右收、
   02 往右收、03 往左收——左右交替，跟 cd-hero 的多方向交錯同味）。 */
.svc-layout.js-split.is-in .svc-feature-info::after { transition-delay: 0ms; }
.svc-layout.js-split.is-in .svc-feature-media::before { transition-delay: 140ms; }
.svc-layout.js-split.is-in .svc-tile:nth-child(1)::before { transition-delay: 280ms; transform-origin: right center; }
.svc-layout.js-split.is-in .svc-tile:nth-child(2)::before { transition-delay: 420ms; transform-origin: left center; }

@media (prefers-reduced-motion: reduce) {
    .svc-layout.js-split .svc-tile::before,
    .svc-layout.js-split .svc-feature-info::after {
        transform: scaleX(0);
        transition: none;
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   Hero 次圖點擊＝橫滑換頁（2026-08-19 客戶）
   ═══════════════════════════════════════════════════════════════════════
   「hero輪播右邊第二張圖片露出局部，點擊該區塊切換第二張圖，但點擊此
   區塊的輪播圖片換頁效果不是遮罩動畫，是從右邊橫向往左滑動的方式切換」
   ——右側次圖（.hero-card-right，≥1468 的 Card 版面才有）整塊可點；
   slide 模式的進場圖從右滑入蓋過現圖（home.js show(index, slide)），
   矩形遮罩不重播（beforeswap/afterswap 事件在 slide 模式不發）。
   ⚠ 起點 103% 不是 100%：.hero-media-item 有 inset:-4% 出血，多推一點
     才完全出畫。z-index 2 疊在現圖上；舊圖滑完才卸 is-active。
   ⚠ 2026-08-27 下界跟著 Card 版面一起降到 1200——次圖在 1200–1467 也存在
     了，游標提示要跟著出現，否則點得到卻看不出可以點。 */
@media (min-width: 1024px) {
    .hero-card-right {
        cursor: pointer;
    }
}

/* 第二版（同日客戶追加：「橫向滑動圖片是蓋在所有物件上滑過去左邊
   然後在遮罩切割退到後面」）——第一版「media item 自己滑」只在照片框
   內動，蓋不到文字卡/箭頭/頁碼。改成 JS 動態掛的全版覆蓋層：
   z-40 疊過 hero 內所有物件（文字卡 z2、模組、頁碼），滑到定位後
   clip-path 往左切割退場。第一版的 .is-slide-in/.is-slide-run 已不再
   被 JS 使用（規則留著無害）。 */
.hero-slide-overlay {
    position: absolute;
    inset: 0;
    z-index: 40;
    overflow: hidden;
    transform: translateX(103%);
    clip-path: inset(0);
    transition: transform 560ms var(--cal-ease);
    pointer-events: none;
}

.hero-slide-overlay picture,
.hero-slide-overlay img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.hero-slide-overlay.is-run {
    transform: translateX(0);
}

.hero-slide-overlay.is-clip {
    transition: clip-path 640ms var(--cal-ease);
    clip-path: inset(0 100% 0 0);
}

/* ══ /wireframe/courses 2026-08-19 客戶調整 ═══════════════════════════════
   「該頁面最上面的實體課程標題字縮小一點現在太大，課程一覽的第一排三張
    跟第三排兩張卡片的高太長，讓小卡標題字都縮小一點」
   ① Hero H1「實體課程」：吃全站 --text-section。⚠ 這顆 token 的**實際**
      現值是 32px 固定（本檔 13768 起的檔尾 :root 把五級距釘死成
      12/14/26/26/32、三個斷點一律相同——tokens.css 與 10380 那幾組
      responsive 級距早就被它蓋掉了，別再信那邊的註解）。乘 .875 收成
      28px：清楚小一階，但仍大於「課程一覽」H2 與主題名 h3 的 26px
      （--fs-4），標題層級不反轉——第一版用 .8（25.6px）就反轉了，
      實測後改回來。用 calc 乘 token 不寫死 px：級距若再被調整會跟著走，
      A/A+/A++ 根字級切換也照常放大。
   ② 卡片列高：只點名 3 張（學思）與 2 張（人際關係）那兩排，1 張的學理排
      沒被點名、維持原高——用 [data-cx-n] 屬性選擇器分流（標記上本來就有，
      16634 行同一招）。三個既有斷點的 --cx-hz-h 各乘 .8 抄成三條，
      維持原本「越窄相對越高」的響應式意圖；specificity (0,3,0) 高於
      原三條 (0,2,0)，不用 !important。手機 ≤767 不受影響：那裡
      .cx-hz 是 display:contents、.cx-pane height:auto，根本不吃這顆變數。
   ③ 小卡標題 .cx-pane-title：--text-sub 實際現值 26px 固定（＝--fs-3，
      同上那組釘死級距），乘 .85 → 22.1px。原本 26px 跟 H2「課程一覽」
      同級，縮完反而分出層次；desc 是 --text-body 14px，一階差還很夠。
      「都」＝三排全部的卡，不分張數。
   ④ 按鈕文字「課程詳情 / 報名」→「查看更多」在 courses_list.php:472，
      不是 CSS 的事，記在這裡方便對照同一輪改動。 */
.page--wall .cx-hero-title {
    font-size: calc(var(--text-section) * .875);
}

.page--wall .cx-hz[data-cx-n="3"],
.page--wall .cx-hz[data-cx-n="2"] {
    --cx-hz-h: clamp(18rem, 24vw, 22rem);
}

@media (max-width: 1023.98px) {
    .page--wall .cx-hz[data-cx-n="3"],
    .page--wall .cx-hz[data-cx-n="2"] {
        --cx-hz-h: clamp(17.25rem, 28.8vw, 20rem);
    }
}

@media (max-width: 1023.98px) {
    .page--wall .cx-hz[data-cx-n="3"],
    .page--wall .cx-hz[data-cx-n="2"] {
        --cx-hz-h: clamp(16rem, 35.2vw, 18.5rem);
    }
}

.page--wall .cx-pane-title {
    font-size: calc(var(--text-sub) * .85);
}

/* ══ 知識專區列表 手機版重排（2026-08-20 客戶指定）═══════════════════════
   客戶（原文英文）：「the gray background width is phone size, let this
   background is vertical arrange; the tags change to horizontal arrange;
   vertical line turn to horizontal」＋補充「gray background on computer
   width is on left, change to the phone's width, let the background on
   top and change arrangement to vertical」——桌機的左右分欄語彙（左灰軌
   ＋貫穿垂直線）在手機轉 90°：灰底變成滿手機寬的水平色帶、排在內容堆疊
   的上方，垂直線跟著轉成水平線，標籤從一行一個改成橫排。
   斷點跟 .kz-layout 收單欄同一個 991.98，桌機（≥992）一條都不受影響。

   ① 左灰軌（.page--knowledge::before）與貫穿垂直線（::after）收掉——
      它們是左右分欄的元件，單欄堆疊裡沒有「左邊」可以站。
      ⚠ 順帶修掉一個手機既有 bug：這顆 ::before 是絕對定位、z-index auto，
        照 painting order 畫在一般流文字**上面**（跟 footer 偽元素那次同一
        個坑），手機單欄時開場 H1「知識專區」剛好落在它的 261px 寬度裡，
        整個標題被灰塊蓋住看不見——桌機沒事純粹是 H1 在第二欄、沒重疊。
        22672 那段「::before 自然畫在所有文字底下」的推理是錯的，別再引用。
   ② 灰底（.kz-filter 的 --background）改滿手機寬：左邊本來就有
      margin-left 負 section-x 出血，補上右邊同一套，色帶兩側都貼齊
      視窗邊緣；padding-right 補回讓內容不貼邊。
   ③ 垂直線轉水平：桌機那條線在灰底右緣內縮 30px（第六輪「線不要貼在
      最邊」），轉 90° 後鏡射成「灰帶下緣內縮 30px 的滿寬水平線」——
      掛回 .kz-filter::after（桌機時代這條線的舊家，第十九輪搬走後空著，
      基底規則的 position:relative 還在，不用再宣告）。灰帶加深
      padding-bottom 讓內容、水平線、下緣三者間距讀得出層次。
   ④ 標籤改橫排：桌機「一行一個純文字」（第三輪指定）在手機改 flex 換行
      橫排；純文字沒框線，靠 column-gap 分隔。「全部」在橫排裡就是第一顆，
      桌機那條「全部底下橫細＋分組距離」（2026-08-19）在橫排沒有「底下」
      可言，這個斷點收掉（桌機照舊）。pill 的 padding 1rem 0／line-height 2
      是直列時的分行留白（「字都擠在一起」那輪），橫排用不到這麼高，收成
      橫排自己的節奏。 */
@media (max-width: 1023.98px) {
    .page--knowledge::before,
    .page--knowledge::after {
        display: none;
    }

    .page--knowledge .kz-filter {
        margin-right: calc(-1 * var(--section-x));
        padding-right: var(--section-x);
        padding-bottom: calc(30px + 2.5rem);
    }

    .page--knowledge .kz-filter::after {
        content: "";
        position: absolute;
        left: 0;
        right: 0;
        bottom: 30px;
        height: 1px;
        background: hsl(var(--foreground) / .16);
    }

    .page--knowledge .kz-tags .filter-tags {
        display: flex;
        flex-wrap: wrap;
        align-items: center;
        gap: .25rem 1.75rem;
    }

    .page--knowledge .kz-tags .tag-pill {
        display: inline-flex;
        padding: .25rem 0;
        line-height: 1.6;
    }

    .page--knowledge .kz-tags .filter-tags > [data-taglink=""] {
        margin-bottom: 0;
        border-bottom: 0;
    }
}

/* ══ 知識專區列表 手機版第二輪（2026-08-20 客戶追加）════════════════════
   客戶（原文英文）：「footer gray background on phone width don't show
   this; the top gray background let the height to the header area; tags
   choice design is different to the category, please design same design
   logic」。三件事，全部鎖在 ≤991.98、只動 .page--knowledge，桌機照舊：

   ① footer 的灰底＋垂直線（body:has(.page--knowledge) .site-footer 的
      ::before/::after，2026-08-19「左半部灰色背景延伸到footer」那組）是
      桌機左右分欄的延伸件——手機已無左軌，這兩顆跟主內容的
      .page--knowledge::before/::after 一樣整顆關掉。
   ② 頂部灰帶延伸到 header 區：開場帶（.band:first-child）本來就用負
      margin 頂到視窗最上緣（實測 t=-1、padding-top 含 --header-h），
      直接壓 --background 灰，H1「知識專區」到 header 後面整段變灰；
      band2 的上內距（實測 48px）與 .kz-layout 的 row-gap＋.kz-filter
      margin-bottom（24+24px）三段白縫收掉，灰從視窗頂一路連到 Tags
      帶結束，中間不斷。
   ③ Tags 比照 Category 的設計語彙：
      ・容器同款＝滿寬灰帶（margin-inline 負 section-x 出血＋padding 補回）
        ＋帶尾水平線內縮 30px（跟 Category 帶那條同一顆公式與色票）；
        Category 帶尾那條線因為灰帶相連，讀成兩組篩選之間的分隔線，
        兩組「標題＋底線 → 選項 → 收尾線」結構完全對稱。
      ・選項同款＝Category 手機用的自訂下拉按鈕是「白底（--muted）＋
        hsl(var(--foreground)/.14) 細框＋直角」（11582 .filter-select-btn），
        標籤 pill 比照做成同一語彙的白色方框 chip；選中態沿用「只加深
        框線不上色底」的既有慣例（框線 .5，跟 courses 卡標籤選中態同款），
        hover 也維持白底——上面幾輪的 transparent 覆寫（21979 起）在
        灰帶上會讓 chip 消失，這裡同特異度靠位置在後蓋回。 */
@media (max-width: 1023.98px) {
    body:has(.page--knowledge) .site-footer::before,
    body:has(.page--knowledge) .site-footer::after {
        display: none;
    }

    .is-designed .page.page--knowledge > .band:first-child {
        background: hsl(var(--background));
    }

    .is-designed .page.page--knowledge > .band:nth-of-type(2) {
        padding-top: 0;
    }

    .page--knowledge .kz-layout {
        row-gap: 0;
    }

    .page--knowledge .kz-filter {
        margin-bottom: 0;
    }

    .page--knowledge .kz-tags {
        position: relative;
        background: hsl(var(--background));
        margin-inline: calc(-1 * var(--section-x));
        padding: 2rem var(--section-x) calc(30px + 2.5rem);
    }

    .page--knowledge .kz-tags::after {
        content: "";
        position: absolute;
        left: 0;
        right: 0;
        bottom: 30px;
        height: 1px;
        background: hsl(var(--foreground) / .16);
    }

    .page--knowledge .kz-tags .filter-tags {
        gap: .5rem;
    }

    .page--knowledge .kz-tags .tag-pill {
        padding: .625rem 1rem;
        background: hsl(var(--muted));
        border: 1px solid hsl(var(--foreground) / .14);
        border-radius: 0;
    }

    .page--knowledge .kz-tags .tag-pill:hover,
    .page--knowledge .kz-tags .tag-pill:focus-visible {
        background: hsl(var(--muted));
        border-color: hsl(var(--foreground) / .3);
    }

    .page--knowledge .kz-tags .tag-pill.is-active,
    .page--knowledge .kz-tags .tag-pill.is-active:hover,
    .page--knowledge .kz-tags .tag-pill.is-active:focus-visible {
        background: hsl(var(--muted));
        border-color: hsl(var(--foreground) / .5);
    }

    /* 「全部」在上一輪（標籤改橫排）被設了 border-bottom:0——那時是純文字，
       要拿掉桌機留下的底線；本輪 chip 改成四邊框，那條 (0,3,1) 屬性選擇器
       會單獨吃掉這一顆的下框線（chip 基底只有 (0,3,0)），這裡補回同款，
       選中態的框色由 (0,4,0) 的 is-active 規則照常蓋過。 */
    .page--knowledge .kz-tags .filter-tags > [data-taglink=""] {
        border-bottom: 1px solid hsl(var(--foreground) / .14);
    }

    /* 上一條 (0,4,0) 排在 is-active 規則後面，會把選中「全部」的下框色
       蓋回 .14（其餘三邊是 .5）——這裡再疊一條 (0,5,0) 讓四邊一致。 */
    .page--knowledge .kz-tags .filter-tags > [data-taglink=""].is-active {
        border-bottom-color: hsl(var(--foreground) / .5);
    }
}

/* ══ 知識專區列表 手機版第三輪（2026-08-20 客戶追加）════════════════════
   客戶：「tags 請跟category一樣用下拉選單的方式選擇，篩選結果小卡左邊
   不要留空間讓卡片收進去，左邊不留邊，底下的數字箭頭請可以真的點選切換
   小卡，然後小卡該區塊下移一點」。箭頭換頁是 JS（knowledge_list.php 檔尾
   新增的 IIFE），這裡是另外三件的樣式；全部鎖 ≤991.98，桌機照舊。

   ① Tags 改下拉：新下拉元件（.kz-tags .filter-select，標記在
      knowledge_list.php）只掛 d-lg-none 在手機出現；上一輪的白框 chip
      橫排（.filter-tags）在手機收起來讓位——桌機純文字清單不受影響，
      chip 那組規則變成備而不用，照房規留著不刪。
   ② 卡牆左邊不留邊：.kz-grid 在桌機疊過 margin-left:+section-x（「整個
      寬減窄但右邊貼齊」那輪），手機反向出血到視窗左緣（-section-x 抵銷
      .kz-layout 的內距），右緣本來就出血、左右都貼邊。篩選結果列與頁碼
      箭頭那兩條 +section-x 內縮（桌機「跟卡牆對齊」的補償）在手機歸零，
      回到與 H1／其他內容同一條 21px 基準線——文字貼死螢幕邊會不可讀，
      客戶說的「不留邊」是對卡片（滿版出血是設計語彙），不是對文字列。
   ③ 小卡區塊下移：.kz-main 補 margin-top（原本只剩 .kz-tags 的
      margin-bottom 24px，「下移一點」再加 2rem）。 */
@media (max-width: 1023.98px) {
    .page--knowledge .kz-tags .filter-tags {
        display: none;
    }

    .page--knowledge .kz-grid {
        margin-left: calc(-1 * var(--section-x));
        /* 2026-08-20 客戶第六輪「小卡離篩選結果也遠一點」：原本只有
           篩選結果列自己的 margin-bottom 24px。⚠ 這裡跟它是相鄰兄弟、
           margin 會塌陷（collapse）取最大值——第一版寫 1.5rem 實測距離
           還是 24px 沒變，要寫「想要的總距離」3rem，塌陷後 max(24,48)
           ＝48px 才真的變遠。 */
        margin-top: 3rem;
    }

    .page--knowledge #wfFilterResult {
        margin-left: 0;
    }

    .page--knowledge .course-meta {
        margin-left: 0;
    }

    /* #wfEmpty 這裡本來就沒有另外的 margin-left 宣告（預設 0），跟這個
       斷點要的目標值一致，不需要額外歸零——但桌機那條 +section-x（本檔
       「篩無結果時的提示文字漏補同一個右移」那節）沒有媒體查詢包住，
       為了讓三者的斷點行為讀起來是同一組、之後好維護，仍然明寫這條，
       不依賴「本來就沒設等於歸零」這種隱性巧合。 */
    .page--knowledge .filter-empty {
        margin-left: 0;
    }

    .page--knowledge .kz-main {
        /* 2026-08-20 客戶第五輪「小卡在下移一點」：2rem → 3.5rem
           （與上方 Tags 的總距離 56px → 80px，含 .kz-tags 既有的
           margin-bottom 24px）。 */
        margin-top: 3.5rem;
    }

    /* 上面「篩選區塊對齊重整」那節四條新規則只為了跟桌機雙欄側欄的
       Category／Tags 標題／裝飾線對齊，這個斷點側欄收成單欄堆疊在內容
       上方，「跟旁邊的 Tags 標題對齊」這個參照系不存在，一併歸零退回
       單欄堆疊原本的間距（跟 #wfFilterResult 的 margin-left、
       .course-meta 的 margin-left 同一批歸零，理由相同）。 */
    .page--knowledge #wfFilterResult {
        margin-top: 0;
    }

    .page--knowledge .kz-main:has(#wfEmpty:not([hidden])) #wfFilterResult {
        margin-bottom: clamp(1.5rem, 3vw, 2.25rem);
    }

    /* 兩條貫通線是配合桌機「側欄＋內容欄並排」這個版面畫的，這個斷點
       側欄收成單欄堆疊在內容上方，桌機版面前提不在了，兩條線都關掉
       ——客戶這輪的截圖與指示全部針對桌機並排版面，手機的空狀態版面
       維持這輪改動前的樣子，沒有一併被要求要加線。 */
    .page--knowledge .kz-main:has(#wfEmpty:not([hidden])) .kz-empty-divider,
    .page--knowledge .kz-main:has(#wfEmpty:not([hidden])) .kz-empty-divider-2 {
        display: none;
    }

    .page--knowledge .kz-main:has(#wfEmpty:not([hidden])) #wfEmpty {
        margin-top: 0;
    }
}

/* ══ 知識專區列表 手機版第四輪（2026-08-20 客戶：「灰色底到category的
   第二條線就好」）════════════════════════════════════════════════════
   灰帶範圍收短：原本灰從視窗頂一路連到 Tags 帶尾，改成到 Category 的
   第二條線（帶尾那條水平線）為止——
   ① 線改貼齊 .kz-filter 下緣（bottom:0），線以下不再留 30px 灰尾，
      線本身就是灰白交界，「到線就好」字面成立；padding-bottom 同步
      從 calc(30px+2.5rem) 收成 2.5rem（原本多出來的量就是那截灰尾）。
   ② Tags 區塊退回白底：背景、帶尾線（.kz-tags::after）一起收掉——
      那條線是「灰區收尾」的記號，灰不到這裡就沒有收尾可言；標題底線
      （Tags＋橫線）與下拉選單照舊，跟 Category 的元件語彙仍然一致。
      padding-bottom 歸零：白底上那 70px 是原灰帶的帶尾內距，退色後
      只剩空白，區塊間距交還給既有的 margin（24px）＋.kz-main 的
      margin-top（32px）。滿寬出血的負 margin 留著無視覺影響，不動。 */
@media (max-width: 1023.98px) {
    .page--knowledge .kz-filter {
        padding-bottom: 2.5rem;
    }

    .page--knowledge .kz-filter::after {
        bottom: 0;
    }

    .page--knowledge .kz-tags {
        background: transparent;
        padding-bottom: 0;
    }

    .page--knowledge .kz-tags::after {
        display: none;
    }
}

/* ══ 知識專區列表 手機版第七輪（2026-08-20 客戶：「小卡的圖片高一點」）══
   卡片圖沿用 .pcard-media 共用元件的 16:9（390 寬、卡寬 304 時圖高約
   171px），手機改 4:3（同寬約 228px）——4:3 是站上既有的「較高版位」
   慣例（.pcard-media--4x3、課程列表圖、手機 hero「圖片高一點，可以
   裁切到」那輪同一個答案），不是新比例；object-fit:cover 裁切照舊。
   只鎖知識列表的卡牆，newsletter／member_center 共用的 .pcard 不受影響。 */
@media (max-width: 1023.98px) {
    .page--knowledge .kz-grid .pcard-media {
        aspect-ratio: 4 / 3;
    }
}

/* ══ 知識專區列表 手機版第八輪（2026-08-20 客戶：「小卡標題字大一點」）══
   歷程：22px（08-18 第五輪定案）→ 18px（08-19「手機版字縮小一點」，
   ≈22193）→ 本輪「大一點」取中間 20px——不是退回 22（那是被嫌大過的
   值），也不是另創新字級（20 是站上級距用過的一階）。同選擇器、同斷點、
   位置更晚蓋過 18px 那條。 */
@media (max-width: 767.98px) {
    .page--knowledge .kz-grid .pcard-title {
        font-size: 20px;
    }
}

/* ══ 搜尋結果改列表式三欄（2026-08-20 客戶附參考圖）═══════════════════════
   客戶：「搜尋結果頁面現在是圖片跟標題匡成一個小卡，請改成（跟附圖一樣）
   標題底下一條橫線要跟整個頁面寬，然後圖片放最右邊，課程標題字縮小放
   最左邊自己一行，小字放第二列在中間位置，圖片跟標題跟小字要齊頭，
   底部不要再加線，其他行列也這樣呈現」。
   ── 附圖（Explore／The Cultural Center）結構＝標題下一條滿版線，每列：
      標題（小字級，最左欄）｜說明小字（中欄）｜圖片（右欄），三者齊頭。
   ── 做法 ──────────────────────────────────────────────────────────
   ・線畫在 <li>::before（每列上緣）：第一列的線剛好落在「知識專區（N）」
     群組標題底下＝「標題底下一條橫線」；列與列之間也靠各自的上緣線分隔
     ＝「其他行列也這樣呈現」；沒有任何 border-bottom＝「底部不要再加線」。
     ⚠ 線不能掛在 .pcard 上——它有 overflow:hidden＋圓角（基底元件），
       100vw 出血會被裁掉；<li> 沒有 overflow，掛這裡才畫得出滿版。
     滿版技巧＝100vw 置中（left:50% + margin-left:-50vw），跟 .cx-theme-rule
     同一招。
   ・卡片框退場：透明底、無框線、無圓角（19796 那條描邊同特異度、位置在
     前，這裡蓋掉）；.pcard-body 用 display:contents 攤平，title／sub／
     media 直接當 row 的 grid 子項分進三欄、grid-row:1 鎖同一列齊頭。
   ・標題縮小＝--fs-2（14px，站上介面級距），不再是卡片標題的 26px；
     小字維持 .pcard-sub 既有的小標級。
   ・分類膠囊（知識專區／實體課程 pill）附圖沒有這個元件——標記保留、
     樣式隱藏（群組 h2 本來就講了同一件事，列上重複一次是冗訊）。
   ・全寬度共用同一套（附圖是桌機，手機同結構等比收窄，欄距用 clamp）。 */
.page--search .result-list {
    gap: 0;
}

.page--search .result-list > li {
    position: relative;
}

.page--search .result-list > li::before {
    content: "";
    position: absolute;
    top: 0;
    left: 50%;
    width: 100vw;
    margin-left: -50vw;
    height: 1px;
    background: hsl(var(--foreground) / .16);
}

.page--search .result-list .pcard--row {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr) minmax(0, 1.1fr);
    column-gap: clamp(1.25rem, 4vw, 4rem);
    align-items: start;
    padding: clamp(1.25rem, 2.5vw, 2rem) 0;
    background: transparent;
    border: 0;
    border-radius: 0;
    overflow: visible;
}

.page--search .result-list .pcard--row:hover {
    background: transparent;
}

.page--search .result-list .pcard-body {
    display: contents;
}

.page--search .result-list .pcard-tags {
    display: none;
}

.page--search .result-list .pcard-title {
    grid-column: 1;
    grid-row: 1;
    margin: 0;
    font-size: var(--fs-2);
    line-height: 1.8;
    font-weight: var(--fw-normal);
}

.page--search .result-list .pcard-sub {
    grid-column: 2;
    grid-row: 1;
    margin: 0;
}

.page--search .result-list .pcard-media {
    grid-column: 3;
    grid-row: 1;
    width: 100%;
}

/* 2026-08-20 客戶「手機版改直向，文字放圖片底下」——手機版原本就有一條
   .pcard--row{flex-direction:column} 的舊規則（≈11477），但這裡的
   .page--search .result-list .pcard--row 選擇器多兩層 class，特異度
   比舊規則高，實測手機（390 寬）三欄 grid 沒被蓋掉，圖片被擠成一條窄
   直條、文字貼在旁邊。這裡另外補一條同樣特異度的手機規則覆寫回單欄：
   .pcard-body 已經是 display:contents（見上面），三個子元素（media／
   title／sub）在 DOM 順序本來就是「圖片在前」（search.php 標記：
   .pcard-media 先於 .pcard-body），改 flex 直排後不用另外調順序，
   圖片自然在最上面、文字（標題＋日期地點）跟著排在下面。斷點沿用
   舊規則原本的 575.98px（手機專屬，不動平板）。 */
@media (max-width: 575.98px) {
    .page--search .result-list .pcard--row {
        display: flex;
        flex-direction: column;
        gap: .75rem;
    }

    .page--search .result-list .pcard-media {
        width: 100%;
        aspect-ratio: 16 / 9;
    }

    .page--search .result-list .pcard-title,
    .page--search .result-list .pcard-sub {
        grid-column: auto;
        grid-row: auto;
    }
}

/* ══ 會員中心「我的收藏」比照搜尋結果列表式三欄（2026-08-20 客戶：「會員
   我的收藏頁面也用一樣的排法」）═══════════════════════════════════════
   同一套規格照搬上面「搜尋結果改列表式三欄」：每列上緣滿版細線（第一條
   落在分區標題底下）、無底線、無卡框，標題（fs-2）最左欄／小字中欄／
   圖片右欄齊頭。差異只有兩處：
   ・這裡的卡是 <div>（不是整卡連結），且 .pcard-tags 裡是**功能性的
     「✕ 移除收藏」按鈕**——不能像搜尋那樣隱藏，改放標題欄第二行
     （grid-row:2），圖片跨 1/3 兩行，第一行照樣三欄齊頭。
   ・原直卡縮圖是共用的 16:9，這裡跟搜尋列一致改 16:10（.pcard--row 的
     既有比例）。
   scope 用 #tabFav（收藏頁籤唯一 id）：.pcard-grid／.pcard 是共用元件，
   同頁「基本資料」頁籤與 newsletter 等其他頁不受影響。 */
#tabFav .pcard-grid {
    display: block;
}

#tabFav .pcard-grid > li {
    position: relative;
}

#tabFav .pcard-grid > li::before {
    content: "";
    position: absolute;
    top: 0;
    left: 50%;
    width: 100vw;
    margin-left: -50vw;
    height: 1px;
    background: hsl(var(--foreground) / .16);
}

#tabFav .pcard {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr) minmax(0, 1.1fr);
    /* auto 1fr：圖片跨 1/3 兩列時，auto+auto 會把圖高平分給兩列，
       「✕ 移除收藏」（第 2 列）被推到列中間——第 1 列鎖內容高、
       剩餘全給第 2 列，按鈕才貼在標題正下方。 */
    grid-template-rows: auto 1fr;
    column-gap: clamp(1.25rem, 4vw, 4rem);
    align-items: start;
    height: auto;
    padding: clamp(1.25rem, 2.5vw, 2rem) 0;
    background: transparent;
    border: 0;
    border-radius: 0;
    overflow: visible;
}

#tabFav .pcard-body {
    display: contents;
}

#tabFav .pcard-title {
    grid-column: 1;
    grid-row: 1;
    margin: 0;
    font-size: var(--fs-2);
    line-height: 1.8;
    font-weight: var(--fw-normal);
}

#tabFav .pcard-sub {
    grid-column: 2;
    grid-row: 1;
    margin: 0;
}

#tabFav .pcard-media {
    grid-column: 3;
    grid-row: 1 / 3;
    width: 100%;
    aspect-ratio: 16 / 10;
}

#tabFav .pcard-tags {
    grid-column: 1;
    grid-row: 2;
    justify-self: start;
    margin: .75rem 0 0;
}

/* 2026-08-20 客戶「我的收藏手機版也要改同樣的」——跟上面搜尋結果同一個
   問題：這裡的三欄 grid 也沒有手機版覆寫，圖片在窄寬度會被擠扁。做法
   比照搜尋結果（flex 直排），但這裡多一個「✕ 移除收藏」（.pcard-tags），
   直接照 DOM 順序疊（圖片→移除鈕→標題→副標）會把移除鈕排在標題前面，
   閱讀順序怪；改用 order 明確排成「圖片→標題→副標→移除鈕」，看得懂
   要移除的是哪一筆之後才看到移除動作。縮圖比例跟著搜尋的手機版一起用
   16:9（桌機維持 16:10 不動，那是「跟搜尋列一致」的既有決定，這裡沒有
   要推翻）。 */
@media (max-width: 575.98px) {
    #tabFav .pcard {
        display: flex;
        flex-direction: column;
        gap: .75rem;
    }

    #tabFav .pcard-media {
        grid-column: auto;
        grid-row: auto;
        width: 100%;
        aspect-ratio: 16 / 9;
        order: 1;
    }

    #tabFav .pcard-title {
        grid-column: auto;
        grid-row: auto;
        order: 2;
    }

    #tabFav .pcard-sub {
        grid-column: auto;
        grid-row: auto;
        order: 3;
    }

    #tabFav .pcard-tags {
        grid-column: auto;
        grid-row: auto;
        margin-top: 0;
        order: 4;
    }
}

/* ══ 課程列表／內頁 遮罩縫隙閃光修補（2026-08-21 客戶三輪定案）═══════════
   客戶回報課程列表遮罩動畫「紅色光暈殘影」、個別課程頁「黃光」，並點出
   首頁 hero 同款動畫沒有這個問題；AskUserQuestion 確認症狀＝**只在動畫
   播放瞬間、出現在方塊與方塊交界的細線**——與本檔 621 行記載的
   2026-08-19 已知 bug 完全同款：gap:0 網格＋1fr 欄寬的次像素捨入，
   相鄰格在 stagger 動畫中各自捨入到不同邊界，交界漏縫透出底圖；
   暖色圖（列表頁紅棕插畫／內頁暖黃照片）漏出來就像紅光黃光，首頁圖
   偏深冷色所以同樣的縫看不出來。當時修法＝遮罩超蓋 margin:-1px。
   ── 這次的兩個洞 ────────────────────────────────────────────────────
   ① .cx-hero-mask（課程列表）**從來沒拿到這個修補**——它是「一格一整塊」
      不是棋盤格，08-19 沒被想到，但 cell 與 cell 交界同樣有次像素縫。
      絕對定位元素用 inset 擴（不是 margin）；cell 自身 overflow:hidden
      會裁掉外溢，蓋好蓋滿不外漏。
   ② .cd-hero-mask／.cd-enroll-mask 已有 -1px 仍被看到黃光——DPR2 實機
      1px 超蓋不夠力，加深到 2px。遮罩色＝頁面底色同 token，外緣多蓋
      2px 落在同色背景上不可見。
   ⚠ 昨天的「will-change 合成層」診斷是誤診（首頁掛同款 will-change 卻
     乾淨，就不是它），該覆寫與整類關閉的 kill-switch 已一併撤掉、
     動畫全數還原。首頁／會員 mem-split／電子報 nl-feature 未被回報、
     維持 -1px 原狀；若再有同型回報，照這裡抄 -2px。 */
.page--wall .cx-hero-mask {
    inset: -2px;
}

.course-detail--editorial .cd-hero-mask,
.course-detail--editorial .cd-enroll-mask {
    margin: -2px;
}

/* ══ 平板 footer 兩行（2026-08-21 客戶：「平板footer換成兩行，訂閱電子報
   那個移下一行」）═══════════════════════════════════════════════════
   承同日上一輪「平板改跟電腦一樣」（客戶點名 768／868／1024）：平板區間
   ＝768–1199.98。這裡再收一步：品牌｜NAVIGATION｜MEMBER 三欄留第一帶，
   NEWSLETTER（訂閱）整欄移到下一行滿版——nav 欄的 grid-row 1/2 佈局沿用
   ≥768 四欄區塊（≈22869）的既有定位，這裡只改欄模板與訂閱欄落點，
   位置在後同特異度蓋掉該區塊在此區間的四欄模板。≥1200 電腦維持一行
   四欄不受影響；≤767.98 手機單欄照舊。訂閱欄不加分隔線（沿用四欄版
   border 歸零的乾淨語彙），用 padding-top 拉開與上一帶的距離。 */
@media (min-width: 768px) and (max-width: 1023.98px) {
    .footer-main {
        grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr) minmax(0, 1fr);
    }

    .footer-subscribe {
        grid-column: 1 / -1;
        grid-row: 3;
        padding-top: 2.5rem;
    }
}

/* ══ 首頁 992–1467 手機化修補 ①：hero 讓開 header（2026-08-21 客戶
   「寬1024的時候header被擠到了…被hero圖片擠到」）══════════════════════
   手機拆帶版 hero 的佈局管到 ≤1199.98，但「讓開透明 header」的機制只有
   兩端有：≤991.98 是 sticky header＋色帶頂升組合（實測 padding-top 57），
   ≥1200 是桌機卡片版（圖片本來就不在 header 底下）。992–1199 兩頭落空，
   圖片 top=1 直接鑽進 header（實測 1024／1100／992 同病）——又一個
   「後補斷點區間沒人讓位」的缺口（同 992–1467 漢堡重災區模式）。
   選擇器疊到 (0,3,0) 才蓋得過 4416 那條 .is-designed.is-designed .hero
   { padding-top:0 }（07-31 白條時代讓位的歸零規則）。色帶背景含 padding
   區＝白帶延伸到 header 後面，跟真手機同一個視覺。
   ⚠ 2026-08-25 斷點從 1199.98 加寬到 1467.98：客戶「hero寬1469以下就
     改手機版型呈現」那輪，本檔 ≈24720 那條 .hero 背景漸層／往下看更多
     顯示邏輯已經跟著加寬到 1467.98（見該處記錄），但這條讓開 header 的
     padding-top 漏改，1200–1467 之間 hero 照樣被判定成「拆帶手機版」
     （背景漸層／箭頭都已經是手機樣式），卻沒有 padding-top 讓開，等於
     photo top=0 直接貼著透明 header——客戶這輪回報「header像圖片一樣
     壓在圖片上」正是這個缺口，跟 ≈24699 那次是同一個斷點延伸動作漏掉
     的兩個獨立屬性，不是同一個 bug。比照那次的做法，只改斷點數字，
     規則本體不動。
   ⚠ 2026-08-27 客戶「hero在寬度1460-1200時樣式請跟桌機一樣」——上面那次
     加寬（1199.98→1467.98）**收回來**，恢復成原本的 1199.98。理由跟當時
     一樣、方向相反：1200–1467 現在拿的是桌機卡片版，圖片本來就不在
     header 底下，再讓一次位就會把整個 Hero 往下推 57px；因為 .hero 的
     min-height 仍是 100svh，被推下去的那 57px 會整段超出視窗，**實測
     1400–1467 的頁碼「01 / 04」直接被視窗底切掉**。收回斷點後 1200–1467
     的 padding-top 回到 0，跟 ≥1468 一致。
   ⚠ 這是這次改斷點時**第五個**要一起收的地方（前四個：安全網、基礎手機
     堆疊、上灰下白那層、Card 版面本體）。這個 Hero 的斷點散在五個互不
     相鄰的區塊，下次再動一定要先跑一次「把每條 .hero-* 規則對應到所屬
     media query」的盤點，不要靠 grep 斷點數字。 */
@media (min-width: 992px) and (max-width: 1023.98px) {
    .is-designed.is-designed .hero {
        padding-top: calc(var(--header-h) + 1px);
    }
}

/* ══ 首頁 992–1199 手機化修補 ②③④（2026-08-21 客戶連續指定）═══════════
   「服務專區在寬1024請改成手機樣式排列」「實體課程改寬1024 改預設兩張
   然後可水平拖移」「寬1024的時候導覽跑版文字沒齊左對齊」。
   ② 服務專區：手機版排列的 19 個 @media 區塊（全部只含 svc-*／
      .service-section／首頁 .course-grid 間距，逐塊核過選擇器）斷點
      由 991.98 → 1199.98 原地改綁——首頁 hero 拆帶版本來就管到
      1199.98，服務專區跟上後 992–1199 整段首頁是一致的手機語彙。
      行號（改綁當下）：3536 4228 4647 6012 6061 6166 7657 7680 7715
      8945 9000 9048 9102 9158 9191 9246 9270 9373 14307。home.js 無
      991 寬度判斷（grep 過），行為端零改動。
   ③ 實體課程 992–1199 改兩張＋露角：桌機卡寬 /3.2（三張＋露角提示，
      ≈6039）、真手機 78%（一張＋露角）；這裡取 /2.15＝兩張全寬＋第三張
      露一小角，沿用全站「露角提示可拖」的既有語言。拖曳本來就是全寬度
      啟用（home.js courseRail 桌機滑鼠拖曳），不用動 JS。
   ④ 導覽抽屜 992–1467 主項沒齊左：主項連結（服務專區／實體課程／知識
      專區／會員／電子報）帶著舊 pill 時代的 padding-left:12px，次項是
      0——992–1467 重災區第 7 例（≤991.98 與 ≥1468 都各自歸零過，中間
      這段又漏）。比照該區間既有修補的 html:root 加權寫法歸零。 */
@media (min-width: 992px) and (max-width: 1023.98px) {
    /* 2026-08-21 客戶追加「實體課程1024放大該區塊一點」：/2.15（兩張＋
       露角）改成整整兩張（/2）＋軌道左右內距 --section-x(72px) 收到
       2.5rem，卡片實測 389 → 約 440px（+13%）。露角提示讓位給「放大」
       ——箭頭與拖曳仍在，可捲性不靠露角也讀得出來；scroll-padding 跟
       padding 同步，snap 對齊點才不會跑。 */
    .course-section .course-cell {
        width: calc((100% - var(--course-gap)) / 2);
    }

    .course-section .course-grid {
        padding-inline: 2.5rem;
        scroll-padding-inline: 2.5rem;
    }
}

@media (min-width: 992px) and (max-width: 1467.98px) {
    html:root .site-header .site-nav a {
        padding-inline: 0;
    }
}

/* ══ 首頁 992–1199 手機化修補 ⑤：知識專區（2026-08-21 客戶「寬1024知識
   專區也改成手機尺寸」）══════════════════════════════════════════════
   接續同日 hero／服務專區／實體課程的 992–1199 手機化：首頁知識專區
   （.knowledge-section／.kz-stack*，與知識列表頁的 .kz-layout 家族是
   撞名不同物，已排除 page--knowledge 規則）的 7 個手機區塊斷點由
   991.98 → 1199.98 原地改綁，行號（改綁當下）：7473 7636 9307 9346
   9432 10550 14227。無 JS 寬度判斷需要跟動。 */

/* ══ 首頁 992–1199 手機化修補 ⑥：header 與 hero 文字對齊頁碼（2026-08-21
   客戶「header 1024的時候padding太多 logo跟選單有內縮到沒有對齊，hero的
   標題跟文字也是，請跟hero底下數字對齊」）═══════════════════════════════
   基準＝hero 頁碼的左緣：.hero-card 是 width:92vw + margin:auto 置中，
   頁碼／圖片左緣落在 (100% − 92vw)/2（1024 實測 33px；100% 用版面寬、
   92vw 含捲軸，跟瀏覽器算 auto margin 的口徑一致，不能寫 4vw 硬湊）。
   ・header：--header-pad-x 由 72px（--section-x 口徑）改讀同一條置中
     公式，logo 與漢堡跟頁碼同一條線；只蓋 992–1199（≥1200 桌機卡版的
     72px 對齊本來就對）。變數消費點在 .site-header .inner 的 padding，
     % 相對 header 全寬解析，跟卡片置中同基準。
   ・hero 文字模組：padding-inline 由 --section-x(72) 歸零，標題／文字
     貼齊模組左緣＝卡片左緣＝頁碼線。
   ══ 同日追加：右下三顆懸浮圓鈕齊大小（「1024…圓圈有變形並且第三顆
   圓形比其他兩個小，在電腦寬度aaa字級大小切換的時候也有這個問題」）══
   病因三件：① YouTube/Podcast 兩顆 <a> 是 2.75rem（8855，會跟 A/A+/A++
   根字級縮放 44→55px）；② mail 是 <button>（吃不到 a 規則），還停在
   08-03 改版前的固定 46px 藍鈕——字級一切就分家；③ 外層 .wf-float-mail
   是 flex 直欄裡的 stretch 子項，被鄰居撐寬成 55×46 橢圓（「變形」）。
   修法：三顆全部釘死 44px（08-03 髮絲框那輪的定案尺寸）、flex:none 不再
   被撐；字級切換不縮放（三顆一起不動，比兩顆動一顆不動誠實）。配色維持
   各自現狀（兩顆髮絲框、mail 藍底）不動。 */
@media (min-width: 992px) and (max-width: 1023.98px) {
    /* ⚠ 全站 header 恆為 is-compact（2026-08-18 統一），實際生效的 padding
       是 html.is-compact... > .inner { padding:.875rem var(--section-x) }
       （CSS.getMatchedStylesForNode 查證），不是 --header-pad-x 那條——
       要用同一顆選擇器在後面蓋，改變數沒有用（第一版試過，logo 還在 72）。 */
    html.is-compact:not(.has-menu-open) .site-header > .inner {
        padding-inline: calc((100% - 92vw) / 2);
    }

    /* 客戶隨後追加「hero文字不要完全跟數字對齊改成靠右一點」——
       padding-inline 從 0 改 1.5rem，文字比頁碼線再進 24px。 */
    .hero-text-module {
        padding-inline: 1.5rem;
    }
}

.is-designed .wf-float {
    align-items: center;
}

.is-designed .wf-float a,
.is-designed .wf-float .wf-float-mail,
.is-designed .wf-float .wf-float-mail-toggle {
    width: 44px;
    height: 44px;
    flex: none;
}

/* ══ 首頁外全站跟進：992–1199 左右留白收窄（2026-08-21 客戶「在寬1024的
   時候整個頁面都內縮太多…左右padding」）═══════════════════════════════
   --section-x 在 992–1919 是 4.5rem(72px)（tokens.css:197），1024 的版心
   兩側各吃 72px 太多。這段改 4vw：1024≈41px、992≈40、1199≈48——在無佔位
   捲軸的環境（macOS 疊加式捲軸＝客戶環境）正好等於 hero 卡片 92vw 置中
   的邊線 (100%−92vw)/2，整頁的左右邊界因此跟 hero 圖／頁碼／header logo
   （本檔稍上方同區間的 padding-inline 覆寫）落在同一條線上。
   style.css 在 tokens.css 之後載入（layouts/main.php:46/50），同為 :root
   後載勝，token 消費端全部自動跟走（section、footer max() 公式、出血的
   負 margin＋padding 對）。≥1200 與 ≤991.98 不動。 */
/* ⚠ 2026-09-01 還原上界：這條在搬桌機斷點時被一起改成 1023.98，**是搬錯的**。
   它不是「桌機／手機版型」的分界，而是「窄視窗左右留白要收窄」的調整——
   1024–1199 不管走哪種版型，視窗就是窄，留白照樣要收。搬掉之後 1024 的
   --section-x 從 41px 跳回 72px，版心兩側各被吃掉 72px，客戶立刻反映
   「logo 靠左一點、右邊靠右一點」（正是這條原本要解的同一件事）。
   → 上界改回 1199.98。 */
@media (min-width: 992px) and (max-width: 1199.98px) {
    :root {
        --section-x: 4vw;
    }
}

/* ══ 992–1199 服務專區深帶字色蓋回＋抽屜 AAA 還原（2026-08-21 客戶：
   「服務專區寬1024左邊深灰底的文字變成黑色了，請改回白色」「導覽列上的
   tools 底下的aaa請加回來現在消失了」）══════════════════════════════════
   ① 字色：≈7541「服務專區：白字壓在淺底上」那塊是舊時代的修補——當年
      992–1199 的服務專區左欄是淺底，白字看不見才整組壓深；今天這區間
      已改走手機深灰帶版型（同日稍早的 19 塊改綁），那組深字反過來變成
      「深字壓深底」。這裡用同選擇器、更晚位置蓋回深底版：intro 的
      標題／英文小標／內文與按鈕雙 token 全對齊 390 實測值（zh 白、
      en --brand-accent、desc 白 .7、按鈕 slate 底白墨）。
      ⚠ feature／tile 那幾條不用蓋：它們坐在深帶內的淺色磚上，390 實測
        本來就是深字，7541 的值剛好是對的。
   ② AAA：≤1467 有一條 .has-menu-open 把 header 的字級切換器
      visibility:hidden（怕跟抽屜疊）——手機斷點的抽屜改版有自己的還原，
      992–1467 又是兩頭不管（重災區第 8 例）。開抽屜時把它顯示回來，
      它本來就在抽屜 TOOLS 欄的位置。 */
@media (min-width: 992px) and (max-width: 1023.98px) {
    .service-section {
        --fw-light: 310;
        --fw-normal: 410;
        --fw-heading: 330;
    }

    .service-section .svc-intro .section-title .zh { color: hsl(var(--brand-foreground)); }
    .service-section .svc-intro .section-title .en { color: hsl(var(--brand-accent)); }
    .service-section .svc-intro-desc { color: hsl(var(--surface-dark-foreground) / .70); }

    .service-section .svc-intro {
        --surface-here: hsl(var(--surface-dark));
        --ink-here: hsl(var(--surface-dark-foreground));
    }
}

@media (min-width: 992px) and (max-width: 1467.98px) {
    .has-menu-open .site-header .site-nav .font-size-switch {
        visibility: visible;
    }
}

/* ══ 992–1467 抽屜 AAA 改電腦版純文字樣式（2026-08-21 客戶：「aaa加回來
   為什麼要加底色跟線，請跟電腦板的aaa樣式一樣」）═════════════════════
   電腦版純文字化（第四版 ≈10805）的選擇器是 .inner > .font-size-switch
   （直屬子元素）——抽屜 TOOLS 欄裡那顆在 .site-nav 底下吃不到，殘留
   第二版的三色 chip＋外框。同一套宣告照搬到 nav 範圍、橫排。 */
@media (min-width: 992px) and (max-width: 1467.98px) {
    /* ⚠ A 殼抽屜時代留了幾條 !important（column !important／
       border-color .35 !important／is-active border-bottom !important），
       非 important 蓋不過（實測），這裡以更高特異度＋!important 對沖。 */
    /* 2026-08-21 客戶追加「aaa請垂直排」——維持純文字樣式，方向改回直排、
       靠左、行距放鬆。 */
    .site-header .site-nav > .font-size-switch,
    .site-header .site-nav .font-size-switch {
        flex-direction: column !important;
        align-items: flex-start;
        gap: .75rem;
    }

    .site-header .site-nav .font-size-switch .font-size-btn,
    .site-header .site-nav .font-size-switch .font-size-btn.is-active {
        border-color: transparent !important;
        border-bottom-color: transparent !important;
        background-color: transparent !important;
        border-radius: 0;
        color: hsl(var(--heading));
        letter-spacing: var(--tracking);
        /* 直排間距全交給容器 gap；A+（.font-size-btn--lg）身上有一顆
           margin-top:.5625rem !important 的舊規則，同 !important 歸零。 */
        margin: 0 !important;
    }

    .site-header .site-nav .font-size-switch .font-size-btn {
        font-weight: var(--fw-light);
        background-image: linear-gradient(hsl(var(--heading)), hsl(var(--heading)));
        background-repeat: no-repeat;
        background-position: 0 100%;
        background-size: 0 1px;
    }

    .site-header .site-nav .font-size-switch .font-size-btn.is-active {
        font-weight: var(--fw-medium);
    }
}

/* ══ 992–1199 懸浮球放大（2026-08-21 客戶：「懸浮球寬1024有點太小放大
   一點」）——44 → 52px，圖示等比跟上。 */
@media (min-width: 992px) and (max-width: 1023.98px) {
    .is-designed .wf-float a,
    .is-designed .wf-float .wf-float-mail,
    .is-designed .wf-float .wf-float-mail-toggle {
        width: 52px;
        height: 52px;
    }

    .is-designed .wf-float a svg {
        width: 21px;
        height: 21px;
    }

    .is-designed .wf-float-mail-toggle svg {
        width: 25px;
        height: 25px;
    }
}

/* ══ 全站平板改手機呈現（2026-08-21 客戶：「平板請換成手機的呈現方式
   每個頁面都是」）═══════════════════════════════════════════════════════
   承同日首頁 992–1199 逐區手機化的最終定調：**所有頁面**的內容版型在
   992–1199 一律走手機（≤991.98）那套。做法＝頁面內容的 29 個
   @media(max-width:991.98px) 區塊原地改綁 1199.98（行號見 git diff），
   涵蓋：知識列表全系列（kz-layout/灰帶/下拉/卡列）、知識內頁 kd、課程
   列表 cx、課程內頁 page-hero/editor、搜尋/電子報 simpla-cols、會員
   mem-split/tile-grid、page-with-side、手機 h1 縮級（24461）等。
   ⚠ 保留不改的 12 塊：header／抽屜（22264/24053/24223/24284/4456/20155
     ——平板抽屜是客戶今天單獨定稿的全寬面板）、footer（9516，平板 footer
     ＝桌機四欄+訂閱下移，同日單獨定稿）、首頁 hero 內部機制（7591/9228/
     20130/1291——hero 的 992–1199 已由「手機化修補①⑥」另行處理並經客戶
     驗收）、10429 死代碼（字級被檔尾釘死級距蓋過）。
   ⚠ Bootstrap 響應工具類斷點在 992 動不了，知識頁兩顆手機下拉
     （.filter-select.d-lg-none）與桌機分類清單（.filter-list.d-lg-block）
     這裡用更晚的 !important 對沖，平板才會出現手機的下拉而不是桌機清單。 */
@media (min-width: 992px) and (max-width: 1023.98px) {
    .page--knowledge .filter-select.d-lg-none {
        display: block !important;
    }

    .page--knowledge .filter-list.d-lg-block {
        display: none !important;
    }
}

/* ══ 課程內頁平板改手機版型（2026-08-21 客戶：「/wireframe/courses/strategy
   這個改成手機版版型」）══════════════════════════════════════════════
   承同日「平板請換成手機的呈現方式每個頁面都是」：課程內頁
   （course_detail.php，response/strategy/inquiry 共用）的手機區塊原本在
   ≤767.98（真手機），上一輪 991.98 全域改綁掃不到——本輪把 4 塊原地
   改綁 1199.98：18751（cd-hero 上下堆疊）、19371（cd-enroll 圖）、
   19403（cd-enroll 欄）、23832（cd-hero-text 淺灰底）。cd-enroll-rule
   兩條線是 JS 現量（course_detail.php IIFE），任何寬度自動對位，不用動。 */

/* ══ 搜尋頁平板改手機樣式（2026-08-21 客戶：「搜尋頁面也改手機樣式」）════
   承「平板＝手機」：這頁 992–1199 唯一還是桌機語彙的是搜尋表單——
   Bootstrap 欄 class（col-lg-7 置中窄欄＋col-sm-auto 按鈕同行）的斷點
   動不了，這裡用更晚的同權重規則把欄寬攤平：外欄滿寬不置中、輸入框
   佔滿內容寬、按鈕換行靠左（＝手機的堆疊順序）。結果列本來就是全寬度
   共用的三欄列（同日「搜尋結果改列表式三欄」），不用動。 */
@media (min-width: 992px) and (max-width: 1023.98px) {
    .page--search .page-block .row.justify-content-center > [class*="col-"] {
        flex: 0 0 100%;
        max-width: 100%;
    }

    .page--search .page-block form.row > [class*="col-"] {
        flex: 0 0 100%;
        max-width: 100%;
    }
}

/* ══ 知識內頁相關文章：橫式小卡改回直式＋加自動輪播（2026-08-21 客戶看過
   實際畫面後附截圖改口：「底下的相關文章橫式小卡請改成這樣」——圖滿版在
   上、標題與副標在下的直式卡，不是左圖右文。本節同日稍早那一版把「橫式」
   理解成「卡片內部左圖右文」，方向會錯了，這裡撤回。
   撤回後不需要任何覆寫規則：.kd-related 的卡片本來就沿用首頁「近期課程」
   那一套（.course-cell 本檔 4277／5594／6038／7669 一帶，flex 直排＝圖
   滿版在上、日期／標題／副標在下，捲動軌道橫向），連同 .course-nav／
   .course-meta／.course-cursor（箭頭、頁碼、圓框游標）也一起撤銷跟著恢復
   顯示——全部刪掉這個區塊即可跟截圖一致，不必新增規則。
   「js讓他會自動水平輪播」＝新增在 home.js 檔尾一支獨立 IIFE，只鎖
   .kd-related .course-grid，不影響首頁「近期課程」那一份，見該檔說明。 */

/* ══ 平板懸浮鈕改橫排（2026-08-21 客戶：「寬768 高1024 的時候右下三顆
   改橫式」）═══════════════════════════════════════════════════════════
   768–1199（平板帶，含 iPad 直式 768×1024）右下三顆由直排改橫排，
   DOM 順序不動（YouTube｜Podcast｜mail，mail 靠角落）。mail 的訂閱
   面板原本往左開（right: calc(100%+.625rem)），橫排時會壓到另外兩顆
   ——這個區間改成往上開、右緣對齊按鈕（≤991.98 既有的「面板開在上方」
   規則同款算式）。 */
@media (min-width: 768px) and (max-width: 1023.98px) {
    .is-designed .wf-float {
        flex-direction: row;
    }

    .wf-float-panel {
        right: 0;
        left: auto;
        top: auto;
        bottom: calc(100% + .625rem);
        width: min(18rem, calc(100vw - 2rem));
    }
}

/* ══ 手機抽屜 AAA 加回（2026-08-21 客戶「但你手機版把aaa隱藏掉了我沒辦
   調整」「手機版請加回來」）══════════════════════════════════════════
   08-19 手機抽屜改版時客戶指定「aaa刪掉」（≈24242 那條 display:none
   !important，連同只服務它的 TOOLS 眉標）——今天字級切換器成為常用
   功能後推翻，加回並比照平板／電腦版的純文字直排樣式（同日「跟電腦板
   的aaa樣式一樣」＋「aaa請垂直排」兩條指示的手機版延伸）。
   直排容器樣式沿用 ≈24146 既有規則（position static/column/靠左，
   它一直都在、只是被 ③ 的 display:none 蓋住）；按鈕的純文字化在這裡
   重申一份 ≤991.98 的（1024 那組是 992–1467，構不到手機）。 */
@media (max-width: 991.98px) {
    .site-header .site-nav .font-size-switch {
        display: flex !important;
    }

    .site-header .site-nav .site-nav-menu-label-tools {
        display: block !important;
    }

    .site-header .site-nav .font-size-switch .font-size-btn,
    .site-header .site-nav .font-size-switch .font-size-btn.is-active {
        border-color: transparent !important;
        border-bottom-color: transparent !important;
        background-color: transparent !important;
        border-radius: 0;
        color: hsl(var(--heading));
        letter-spacing: var(--tracking);
        margin: 0 !important;
    }

    .site-header .site-nav .font-size-switch .font-size-btn {
        font-weight: var(--fw-light);
    }

    .site-header .site-nav .font-size-switch .font-size-btn.is-active {
        font-weight: var(--fw-medium);
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   2026-08-24 客戶手機版回報四則（beta 站實機瀏覽）
   ═══════════════════════════════════════════════════════════════════════
   一次收四條，共用這一段。每條各自標明客戶原話與實測數據。
   ⚠ 全部只新增規則、不改上面任何一條歷史規則（含 08-19／08-21 那幾輪
     客戶已確認的抽屜與懸浮球設定）。 */

/* ── ① 「選單展開有些按鈕沒顯示」───────────────────────────────────────
   實測（playwright，真實 390/375/360 寬視窗）：抽屜內容高 778px，
     iPhone 14 390×844 → 剛好塞得下（溢出 0）
     iPhone SE 375×667 → 溢出 111px
     小尺寸 Android 360×640 → 溢出 138px
   溢出的正好是最後那組 TOOLS／A・A+・A++ 三顆字級鈕 —— 也就是客戶說
   「沒顯示」的按鈕。抽屜本身 overflow-y:auto 其實捲得動（實測 scrollTop
   可到 111），但沒有任何捲動提示，使用者不會知道下面還有東西。

   空間都花在三個區段眉標上：每個 margin-top 32px＋高 25＋margin-bottom 8
   ＝ 65px×3；再加 nav 上內距 88／下內距 24、字級鈕直排 gap 20×2。

   ⚠ 不改成橫排：A/A+/A++ 直排是客戶 08-21 明講的（「aaa請垂直排」，
     手機版是同日「手機版請加回來」的延伸），這裡只壓縮間距、不動方向。
   ⚠ 用 max-height 限定「矮螢幕才壓縮」：390×844 這類高螢幕本來就塞得下，
     維持客戶已看過的既有節奏一個 px 不動，把改動限制在真正出事的機型。 */
@media (max-width: 991.98px) and (max-height: 800px) {
    .site-header .site-nav {
        padding-top: 3.75rem;      /* 88 → 60；關閉鈕佔 16–52，仍有 8px 淨空 */
        padding-bottom: .75rem;    /* 24 → 12 */
    }

    /* ⚠ 這一段幾乎每條都要 !important：抽屜這幾個 class 的既有規則多半是
       **同特異度（0,3,0）＋ !important**（08-19／08-21 那幾輪為了對沖更早
       的 !important 留下的），不加的話同級比不過、規則會靜靜地不生效。
       第一版就是漏了這幾個 !important，實測只有 nav 內距與 gap 生效、
       眉標與群組間距原封不動（718px vs 預期 619px）才抓到。 */
    .site-header .site-nav .site-nav-section-label {
        margin-top: .75rem !important;      /* 32 → 12，三處共省 60px */
        margin-bottom: .25rem !important;   /* 8 → 4，共省 12px */
    }

    .site-header .site-nav .site-nav-group-end {
        padding-bottom: .625rem !important; /* 20 → 10，兩處共省 20px */
    }

    /* gap 與 margin-top 都在 max-width:1467.98 那條被下了 !important
       （本檔 ≈22299／≈21076），同級對沖。 */
    .site-header .site-nav .font-size-switch {
        margin-top: .625rem !important;     /* 17 → 10 */
        gap: .625rem !important;            /* 20 → 10 */
    }
}

/* ── ①附 字級放大後抽屜同樣溢出，但裝置不夠矮不會吃到上面①的壓縮
   （2026-08-24 客戶「按aaa切換字大小後，選單再次展開後文字會沒完全
   顯示」）─────────────────────────────────────────────────────────────
   上面①用 max-height:800px 判斷「這台裝置矮不矮」，前提是量測時的字級
   （sm，預設值）。但 A+/A++ 是使用者自己選的，跟裝置高度無關——實測
   （同一套 playwright 手法，390×844／375×667／360×640 三種寬度）：
     sm：0 / 0 / 0px 溢出（① 判定 390×844 不用壓縮，量測時的前提沒錯）
     lg：115 / 93 / 120px 溢出
     xl：296 / 234 / 261px 溢出
   390×844（iPhone 14）在 sm 剛好塞得下，①因此判定「這台不用壓縮」；
   但同一台裝置只要使用者按了 A+ 或 A++，內容變高，一樣會溢出——這正是
   客戶操作的順序（先切字級、再展開選單），跟①原本瞄準的「裝置矮」是
   兩種成因，要分開處理。
   ⚠ 觸發條件把 max-height:800px 換成 data-font-size 屬性選擇器——放大
     字級的人本來就更需要這顆鈕在看得到的地方，不該因為裝置比較高就不
     幫他省空間；選擇器加了屬性判斷，特異度比①單純的 class 組合更高，
     且一樣掛 !important，能贏過①與更早幾輪的 !important 覆寫。
   ⚠ 壓縮值原封不動抄①的（同一組間距，不是重新設計一套）：lg 溢出量
     （93–120px）小於①省下的約 149px，實測後三種寬度都能整段塞下、不用
     捲動；xl 溢出量（234–296px）比①省下的多，捲動仍會用到，但抽屜本身
     overflow-y:auto 早就捲得動（見下面 wf-float 那條的實測），配合那條
     一起修，捲到底也不會有內容被浮動鈕蓋住——xl 同時捲得動、又不會蓋住，
     不強求無捲動照樣塞滿，那是額外的完美主義，客戶沒有這樣要求。 */
@media (max-width: 991.98px) {
    html[data-font-size="lg"] .site-header .site-nav,
    html[data-font-size="xl"] .site-header .site-nav {
        padding-top: 3.75rem !important;
        padding-bottom: .75rem !important;
    }

    html[data-font-size="lg"] .site-header .site-nav .site-nav-section-label,
    html[data-font-size="xl"] .site-header .site-nav .site-nav-section-label {
        margin-top: .75rem !important;
        margin-bottom: .25rem !important;
    }

    html[data-font-size="lg"] .site-header .site-nav .site-nav-group-end,
    html[data-font-size="xl"] .site-header .site-nav .site-nav-group-end {
        padding-bottom: .625rem !important;
    }

    html[data-font-size="lg"] .site-header .site-nav .font-size-switch,
    html[data-font-size="xl"] .site-header .site-nav .font-size-switch {
        margin-top: .625rem !important;
        gap: .625rem !important;
    }
}

/* ── ①附二 抽屜捲到底時，右下角浮動鈕（YouTube／Podcast／Mail）蓋住
   最後一截內容（2026-08-24，與上面同一則客戶回報一起查出來的）───────
   客戶 08-05 明確要求選單展開時這三顆懸浮鈕維持可見、疊在選單上面
   （見本檔「手機：選單開著時三顆懸浮鈕要看得到」那節），當時的但書就
   寫著「日後選單若加東西到底部就會互相干擾」——這次的字級鈕搬進 TOOLS
   欄位＋字級放大兩件事疊在一起，把「搜尋」連結與字級鈕推進懸浮鈕常駐
   的那個角落，實測（390×844，data-font-size="xl"）懸浮鈕 rect
   top:776/bottom:820 跟「搜尋」連結 rect top:781/bottom:861 有重疊，
   使用者看到的是搜尋文字被三個圓鈕壓住一部分，跟客戶說的「文字沒完全
   顯示」對得上。
   ⚠ 不能把懸浮鈕改回隱藏：那正是客戶 08-05 推翻掉的做法，會違反更新的
     指示。改成幫抽屜自己的捲動內容在最下面留一塊淨空，滾到底時最後一項
     停在懸浮鈕上緣之前，不會疊在一起——不動懸浮鈕本身的位置或層級。
   ⚠ 留白公式跟懸浮鈕自己的定位算式同一套（本檔 ≈2386
     `right/bottom: clamp(1rem, 2vw, 1.75rem)`＋46px 鈕高），不是憑感覺
     挑一個數字，兩邊要一起改才會繼續對得上；再加 .75rem 當呼吸間距，
     避免貼死。
   ⚠ 2026-08-24 補：實測放大字級（data-font-size="lg"/"xl"）＋選單展開
     同時發生時，這條規則完全沒生效——選擇器是 `.has-menu-open .site-header
     .site-nav`，特異度 (0,3,0)；上面「①附」的選擇器是 `html[data-font-size=
     "lg"] .site-header .site-nav`，多一個 `html` element 選擇器，特異度
     (0,3,1) 比這裡高，雙方都掛 `!important` 時「①附」直接贏，跟檔案裡
     誰先誰後無關。結果是 lg/xl 字級時這條「留淨空給懸浮鈕」的規則被
     蓋掉，懸浮鈕重疊的問題其實沒修到——這裡補回 `html` 讓兩條規則
     特異度打平 (0,3,1) vs (0,3,1)，打平之後才輪到「檔案裡誰在後面誰贏」，
     這條在後面，才能真的贏過「①附」。 */
/* ⚠ 2026-09-01 修正：上面寫的「46px 鈕高」**不是圓鈕實際佔的高度**。
   `@keyframes wf-float-breathe` 的 `50% { transform: scale(1.07) }`（≈30849）
   會把 46px 的圓鈕撐成 **49.22px**，而 `transform` 同時影響繪製與命中測試——
   所以 56px 的容器盒與 46px 的宣告值都不是真正的邊界，放大後的 49.22 才是。

   實測（1024×600、A++、抽屜捲到底、動畫釘在 scale 峰值）：
       面板底 468 − padding 88 ＝ 內緣 **380**
       圓鈕頂（scale 峰值）      ＝ **376**
       → 內緣比圓鈕頂低 **4px**，也就是這段淨空短了
   改成 49.22 之後同一組量到 **−0.7px**（改善 3.3px）。

   ⚠⚠ **剩下的 0.7px 不要再用調常數去補，那是結構性的、調到 0 只是換一個
      巧合值。** 這條算式有兩個前提，實測**兩個都只在手機成立**：

          前提①「從**視窗底**往上量」   前提②「只算**一顆** 46px 鈕」
          390×844   面板底離視窗底 **0** ✓   浮動列高 52 ≈ 一顆 54.3 ✓（橫排）
          1024×600  面板底離視窗底 **132** ✘  浮動列高 **198** ✘（直排三顆 57.7）
          1440×600  面板底離視窗底 **132** ✘  浮動列高 **198** ✘
          1024×800  面板底離視窗底 296  ✘     浮動列高 198 ✘

      桌機矮視窗時抽屜被高度上限夾住、底緣離視窗底還有 132px（前提①破），
      浮動鈕又從橫排變直排、198px 高（前提②破）。**兩個誤差方向相反、
      恰好抵銷到 −0.7**——不是「差一點點」，是兩個錯的前提互相抵消的殘值。

   ⚠ 真正要修得改成「錨定**面板**底緣 ＋ 讀**浮動列實際高度**」（JS 量測後
     publish 成自訂屬性，比照 `--header-real-h` 的做法）。**評估後決定不做**：
     受影響只有「視窗高 ≤759 ＋ 桌機寬 ＋ 最大字級」這個極端組合，越界的又是
     `.site-nav-menu-foot` 版權列的**框**（不可點，框內還有內距、文字未必壓到），
     且已從 4px 改善到 0.7px。為這個組合引入一條 JS 量測的耦合不划算。

   ⚠ **現在還撞不到，是因為選單項目不夠長**：捲到底時最後一個可點項目
     停在 271.4，離內緣還有 100px 沒用到。但只要選單多加幾項、內容剛好捲滿，
     末項就會落在 380、壓到圓鈕——那時候不會有人聯想到是這條算式。
     （目前實際會超過圓鈕頂的是 `.site-nav-menu-foot` 版權列的**框**底 380.4，
     不可點，且框內還有內距、文字本身未必壓到。這一點無損結論。）

   ⚠ 這個 `scale(1.07)` 對**任何**「算跟浮動鈕淨空」的公式都成立。同一天已經
     修過 course_detail 購買鈕那處（第 51 節，常數 97 → 99），當時只修了那一處
     沒回頭掃還有誰用同一個算式，這條就是漏網的。往後改到浮動鈕相關的距離，
     先 grep `46px` 與 `wf-float` 一起看過。

   ⚠ 交叉引用：≈33122（第 57 節）的註解裡引用的是**改前**的算式字串，
     那是當時的紀錄，不動它。 */
@media (max-width: 1467px) {
    html.has-menu-open .site-header .site-nav {
        padding-bottom: calc(clamp(1rem, 2vw, 1.75rem) + 49.22px + .75rem) !important;
    }
}

/* ── ③ 「選單點擊時會有黑框描邊請移除」─────────────────────────────────
   來源是本檔第 42 行的全站 `:focus-visible { outline: 2px solid hsl(var(--ring)) }`
   （實測 computed 值 rgb(0,0,0) solid 2px、offset 2px ＝ 客戶說的黑框）。
   手機上一點漢堡鈕，抽屜的焦點管理把焦點送進選單，Chrome 對這種
   程式指定的焦點會判定成 :focus-visible，黑框就跑出來了。

   ⚠ 不是直接把第 42 行拿掉、也不是全站關閉：那條是**鍵盤操作唯一的焦點
     指示**，拿掉等於讓鍵盤使用者完全看不出焦點在哪（無障礙倒退）。
   ⚠ 只在「沒有滑鼠懸停能力＋粗指標」＝觸控裝置關閉它。手機本來就沒有
     鍵盤 Tab 導覽的情境，而桌機（含用鍵盤的人）一個 px 都不受影響。 */
@media (hover: none) and (pointer: coarse) {
    .site-header a:focus-visible,
    .site-header button:focus-visible,
    .site-nav a:focus-visible,
    .site-nav button:focus-visible,
    .site-nav-toggle:focus-visible,
    .site-nav-close:focus-visible {
        outline: none;
    }
}

/* ── ④ 「右下三個懸浮鈕請改橫式排列在手機瀏覽時」───────────────────────
   768–1199 已經有一組同樣的橫排規則（本檔 ≈25646，2026-08-21 客戶
   「平板…改橫式」），這次把同一套延伸到手機。做法與那組完全一致，
   沿用同一組數值，不另外發明。
   ⚠ DOM 順序不動（YouTube｜Podcast｜mail，mail 仍在最靠角落那端）。
   ⚠ 訂閱面板要一起改開在按鈕正上方：預設是往左開，橫排之後會壓到
     另外兩顆。≤575.98 早就有「開在上方」的規則（本檔 ≈10743），
     這裡補的是 576–767.98 這段空隙，數值照抄那條。 */
@media (max-width: 767.98px) {
    .is-designed .wf-float {
        flex-direction: row;
    }

    .wf-float-panel {
        right: 0;
        left: auto;
        top: auto;
        bottom: calc(100% + .625rem);
        width: min(18rem, calc(100vw - 2rem));
    }
}

/* ── ⑤ 「hero圖片沒跟灰色底剛好切齊」（客戶確認：垂直方向，圖片底部有白縫）
   ═══════════════════════════════════════════════════════════════════════
   實測（390 寬，真手機斷點 ≤767.98 的 4:3 圖）：.hero-media-item 用既有的
   inset:-4% 出血（本檔 ≈699 行，2026-08-14 客戶「圖片放大拆切試看看」定案
   的做法——不追究每個斷點差幾 px，統一用出血蓋過去），在 768–1199.98
   那組 16:9 比例已經夠蓋滿（實測 -2.6～-8.8px，倒扣，沒有縫），但 767.98
   以下這組 4:3 比例**還差 5.25px 才蓉到 .hero-modules 上緣**，這就是
   客戶看到的白縫。
   ⚠ 只加大 bottom，不動 top/left/right：客戶只回報下緣，其餘三邊實測都
     已貼齊（上緣被 header 蓋住看不到、左右由 .hero-inner 的 overflow:hidden
     裁齊），沒有理由跟著動。
   ⚠ 767.98 以下才覆寫：768–1199.98 那組 16:9 比例本來就已經蓋滿（見上），
     一起加大會讓出血過量、裁掉更多圖，只鎖真的出問題的斷點。
   ⚠ -7% 是實測值（CSSOM 即時試多組數值：-6% 在 390 寬縫隙剩 -0.14px＝
     貼齊，但窄到 360 寬（Android 常見寬度）還剩 1.11px 沒蓋滿——360/390
     兩個寬度的 --section-x 響應式數字不同，同一個百分比在更窄的容器
     算出的絕對出血量也更小。多加 1% 讓最窄的 360 也蓋滿，390 這端從
     -0.14 變成更保守的負值，不影響已經蓋滿的狀態。 */
@media (max-width: 767.98px) {
    .hero-media-item {
        bottom: -7%;
    }
}

/* ── 2026-08-24 客戶回頭要求刪掉上面「裝飾格線 z-index 不夠高」那條
   z-index:5 覆寫──客戶回報「圖片右邊多一個短灰線描邊」，成因就是拉高
   z-index 之後這條線變成無條件蓋在 photoB 照片上面，貼著暖色插畫看起來
   像多畫了一條邊框線。改回本檔 ≈17103 的基底 z-index:1（線可能被相鄰
   卡片內容局部蓋住、深淺不一的舊症狀會回來，但客戶這次明確要「先拿掉
   邊框感」，沒有再要求兩者都要）。 */

/* ═══════════════════════════════════════════════════════════════════════
   知識專區文章詳情頁／實體課程詳細頁：底部操作列改三層視覺層級
   （2026-08-25 客戶指定，同日追加課程內頁比照辦理）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「操作層級更清楚、主次更分明」——知識文章頁原本回知識專區／
   YouTube／Podcast／收藏／分享五顆同尺寸 .btn-pill 並排，客戶：「使用者
   不知道哪個是主要操作」。改成三層（markup 已在 knowledge_detail.php
   重排為 .entry-action-group > .entry-back-link + .entry-links +
   .entry-actions）：
     ① 導覽操作：回知識專區 —— 純文字連結，最輕量，不做方框
     ② 主要內容操作：YouTube／Podcast —— .btn-pill 本體不動（客戶：
        「保持目前極簡按鈕風格」），桌機並排／手機堆疊，尺寸最大
     ③ 輔助操作：收藏／分享 —— 沿用 .btn-pill--sm 的標記（含
        _fav_button.php 這顆全站 SSOT partial），只在本頁範圍內把視覺
        降級成文字按鈕，不跟主要 CTA 同權重

   ⚠ 追加：客戶「實體課程的詳細課程頁底下也改一樣形式」——course_detail.php
     的動作列（回實體課程／收藏／分享）markup 同步重排成
     .entry-action-group > .entry-back-link + .entry-actions。這頁沒有
     YouTube／Podcast，本來就只有①③兩層，沒有②可比照，所以 markup 沒有
     .entry-links 這一段；「導覽連結」「輔助操作」兩層的視覺規則（文字
     連結化、按鈕降級成文字）直接跟知識文章頁**共用同一條 CSS 規則**
     （選擇器並列 .course-detail--editorial，見下方各條），不重複寫一份，
     避免兩頁的「文字按鈕長什麼樣子」日後改一邊漏改另一邊。
   ⚠ 範圍控制：全部規則掛在 .page--knowledge-article（新增在
     knowledge_detail.php 的 .page 那顆 div）或 .course-detail--editorial
     （course_detail.php 原本就有、本來就是這頁專屬的 class，未新增）
     之下，其餘 class 一個都沒拿掉。.entry-foot／.entry-links／
     .entry-actions／.btn-pill／.btn-pill--sm 的基礎規則、以及
     service_page／service_page_simpla／content_generic 三頁既有的頁面
     專屬覆寫，一行都沒有動，也不會被這裡影響到。
   ⚠ 特異度：.page--article .article-col .entry-links 的 margin-top
     （本檔 ≈15537）已經是 3 顆 class（0,3,0）——這裡覆寫 .entry-links
     一律用 .page--article.page--knowledge-article .entry-action-group
     起頭（4 顆 class，0,4,0），確保穩贏，不是同分比檔案順序（鐵則：
     同分平手太脆弱，見 .has-menu-open 那次教訓）。.entry-actions／
     .btn-pill 這幾條兩頁共用的規則沒有同分風險（course_detail.php 這頁
     原本就沒有任何 .entry-actions／.entry-foot 專屬覆寫），選擇器維持
     3 顆 class 即可，不需要湊到 4 顆。
   ⚠ .entry-back-link／.entry-action-group 是全新 class（已 grep 過站上
     零使用），不需要頁面前綴也不會撞到其他頁面，故意寫得單純，兩頁
     直接共用同一顆。
   ⚠ 字級／顏色／間距全部沿用既有 token（--fs-2、--slate、--heading、
     --accent-blue-text、--tracking、--transition、既有 clamp() 寫法），
     沒有新增任何尺寸或色票。

   ⚠ 再追加：客戶「顧問案跟ai分身也改同樣方式呈現」＋「電子報也是」——
     四頁分兩組，各自比照結構最接近的既有先例（原則：有幾層就照幾層的
     既有先例，不是每頁都照抄最後改的那一頁）：
       - service_page_simpla.php（顧問案／AI 分身，service_page.php 361
         行 in_array(['consulting','ai']) 分流過來，新增
         .page--service-simpla 範圍 class）：結構跟知識文章頁幾乎一樣
         （這支 view 本來就是照 knowledge_detail.php 改的，見檔頭註解），
         ①②③三層都在，比照知識文章頁的**堆疊式**排法，選擇器並列加
         進 .entry-links／.entry-actions 那幾條 clamp 規則。這頁的②只有
         一顆 .btn-pill--solid（實心，比知識文章頁的外框 CTA 更搶眼），
         不downgrade、維持原樣。
       - content_generic.php（電子報內容，newsletterDetail() 專用，新增
         .page--content-generic 範圍 class）：目前唯一會走到的路由
         showVideo=false，沒有②主要CTA，跟 course_detail.php 同一種
         「只有①③兩層」情況，比照那頁的**同一列**排法（獨立新增一條
         `.page--content-generic .entry-action-group` 規則，見下方）。
     這兩頁都是 `.page--article .article-col` 結構（跟 course_detail.php
     的 `.course-detail--editorial` 不同）——`.article-col` 本身已經有
     `padding-inline: var(--article-x)`，`.entry-action-group` 不需要
     像 course_detail.php 那樣另外補 padding-inline；分隔線同理也不需要
     另外補回，這兩頁原本的 `.entry-foot` 就沒有分隔線（客戶 2026-08-05
     決定，`.page--article .article-col .entry-foot { border-top:0 }`
     這條全站規則本來就涵蓋所有 `.page--article` 頁，只是選擇器認的是
     `.entry-foot`，改名後不再匹配，效果剛好就是「不用另外處理」）。 */

/* ① 導覽操作：回知識專區 —— 純文字連結，取代原本的 .btn-pill.btn-pill--sm。
   ⚠ display:inline-block 而非預設的 inline：<a> 預設 inline 時
     margin-top 對非替換行內元素無效（CSS 規範），要有 block 層級的
     margin 才會生效。inline-block 維持貼齊文字寬度，不會被拉成整行。
   ⚠ 顏色沿用「有連結用藍／純文字用灰」全站慣例（style.css 已有先例：
     .article a:not(.btn-pill):not(.tag-pill)、.pcard-title 那批）——
     回知識專區是真的會離開頁面的 <a href>，靜置用 --slate、
     hover/focus 用 --accent-blue-text，跟收藏／分享（不是導覽、留在
     原地）刻意用不同的 hover 顏色區分語意。 */
.entry-back-link {
    display: inline-block;
    margin-top: clamp(2.5rem, 5vw, 4rem);
    color: hsl(var(--slate));
    font-size: var(--fs-2);
    font-weight: var(--fw-normal);
    letter-spacing: var(--tracking);
    text-decoration: none;
    transition: color var(--transition);
}

.entry-back-link:hover,
.entry-back-link:focus-visible {
    color: hsl(var(--accent-blue-text));
}

/* ② 主要內容操作：YouTube／Podcast —— 容器排列與間距，.btn-pill 本體
   （含 hover／徽章／::after 箭頭）完全不動。
   桌機：沿用 .entry-links 既有的 flex row，只收窄 margin-top（原本
   2.5rem~3.5rem 是「返回」「主要操作」不分層時的節奏，客戶這次要求
   「返回連結與主要按鈕之間 20–32px」，收窄成對應 clamp）與按鈕間距
   （客戶「主要按鈕之間 16–20px」）。 */
.page--article.page--knowledge-article .entry-action-group .entry-links,
.page--article.page--service-simpla .entry-action-group .entry-links {
    margin-top: clamp(1.25rem, 2.5vw, 2rem);
    gap: clamp(1rem, 1.2vw, 1.25rem);
}

/* ③ 輔助操作：收藏／分享 —— 沿用 .entry-actions 既有 flex row（水平排列
   本來就對），只補「上一層→輔助操作」間距（知識文章頁是「主要操作→
   輔助操作」16–24px；課程內頁沒有②主要操作，這裡就是「導覽連結→輔助
   操作」，沿用同一個 clamp——都是「輕量層→更輕量層」的距離，數值上
   沒有理由分兩套）。
   ⚠ justify-content:flex-start 蓋掉本檔 ≈12506
     `@media(max-width:1199.98px){.entry-actions{justify-content:
     space-between}}` 那條——那是為舊版「回知識專區／收藏／分享」三顆
     等寬方框鈕寫的（貼左右邊緣才好看），現在收藏／分享改成貼在一起的
     文字按鈕，繼續吃 space-between 會被拉開到容器兩端，跟桌機版「收藏
     分享」緊靠在一起的樣子不一致。這裡不分斷點、桌機/平板/手機一律
     蓋掉，讓兩顆一直維持緊靠、只吃 .75rem 的既有 gap。 */
.page--article.page--knowledge-article .entry-action-group .entry-actions,
.page--article.page--service-simpla .entry-action-group .entry-actions,
.course-detail--editorial .entry-action-group .entry-actions {
    margin-top: clamp(1rem, 1.8vw, 1.5rem);
    justify-content: flex-start;
}

/* 課程內頁專屬 .entry-action-group：
   ① 上緣補回原本 .entry-foot 就有的分隔線（2026-08-25 客戶「注意一下
      排版」，附截圖：前往購買按鈕跟回實體課程之間變成一大片沒有任何
      視覺錨點的空白）。
      根因：原本 .entry-foot 的 base rule（本檔 ≈12281）就是「margin-top
      40–64px → border-top 1px 分隔線 → padding-top 24–32px → 內容」三段式
      結構。course_detail.php 是 .course-detail--editorial，不是
      .page--article，從來沒被 2026-08-05「返回上方的細線移除」那次客戶
      決定覆寫過（那條只寫 `.page--article .article-col .entry-foot`），
      所以線本來就在。上一輪拆掉 .entry-foot 整個 class 改用
      .entry-action-group 時，線跟著一起消失，只留下 margin 那段空白——
      沒人要求拿掉這條線，是重排結構的副作用，不是客戶要的效果，這裡
      照原樣位置（margin 大空白之後、貼近內容前）補回來，顏色沿用本檔
      既有的 `.band .entry-foot { border-top-color:...16 }`（≈13838，
      這個動作列本來就在 .band 底色 section 裡，不是新色票）。只在
      .course-detail--editorial 生效：knowledge_detail.php 那條線是
      客戶 2026-08-05 主動要求拿掉的既有決定，兩頁情況不同，不套同一
      條規則。
   ② 補回內文內距（客戶追加「按鈕請對齊內文」）：這個動作列的
      .section-bleed-inner 吃 .page--v2 的「色帶本身不留左右邊界」設計
      （本檔 ≈14556 節）——色帶 max-width:none／padding-inline:0，內文
      改由直屬子元素自己的 padding-inline 內縮，但只有登記在白名單裡的
      選擇器才吃得到，原本 `.page--v2 .entry-foot`（≈14566）就在白名單
      內，換成 .entry-action-group 後沒有跟著登記，整排貼齊真實視窗
      邊緣、跟內文（Hero/Enroll 區塊的 --article-x 內縮）對不齊。這裡
      不去改共用白名單本身（那條規則還服務其他元件，怕波及），改針對
      這頁的 .entry-action-group 另外補一條同值 padding-inline，效果
      等同重新登記。
   ③ 改回同一列左右對齊（客戶追加「回實體課程放左邊 收藏跟分享放右」）：
      這頁只剩①③兩層（沒有②主要CTA），從「上下堆疊」改回 flex 一列，
      比照原本 .entry-foot base rule 的 space-between 排法（≈12281），
      不是新發明的排版。 */
.course-detail--editorial .entry-action-group {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: .75rem 1.5rem;
    margin-top: clamp(2.5rem, 5vw, 4rem);
    padding-top: clamp(1.5rem, 3vw, 2rem);
    padding-inline: var(--article-x);
}

/* 2026-08-25 客戶（同日、更晚一輪）：「回實體課程這行上面的線刪掉，
   其他頁也是」——上面①那條 border-top 是同一天稍早才「補回來」的
   （見上方大註解），這裡直接推翻那個決定，改回沒有線。margin-top／
   padding-top 兩段留白都不動，只拿掉中間畫線那一筆，視覺間距跟改之前
   一樣，只是不再畫那條分隔線。
   ⚠ 「其他頁也是」不需要另外找頁面改：knowledge_detail.php／
     newsletter（.page--content-generic）的 .entry-action-group 本來
     就沒有這條線（分別見上方①註解與電子報那節的說明，都是 2026-08-05
     的既有決定），全站只有這裡（course_detail.php）today 被加回來，
     拿掉這一條就是全站唯一需要改的地方。 */

.course-detail--editorial .entry-action-group .entry-back-link {
    margin-top: 0;
}

.course-detail--editorial .entry-action-group .entry-actions {
    margin-top: 0;
}

/* 電子報內容頁專屬 .entry-action-group：同一列左右對齊，比照
   course_detail.php（客戶「電子報也是」，這頁跟課程頁同一種「只有①③
   兩層」情況，見本節最上方大註解）。
   ⚠ 不需要像 course_detail.php 那樣另外補 padding-inline／border-top：
     這頁是 `.page--article .article-col`，內距已經由 `.article-col`
     提供，分隔線本來就沒有（客戶 2026-08-05 決定）。
   ⚠ margin-top 這裡還是要補（跟 course_detail.php 一樣）：改成
     `display:flex` 之後，.entry-back-link 全站共用那條
     `margin-top: clamp(2.5rem,5vw,4rem)`（本節①，給堆疊式版面用的）
     放進 flex row 裡不會變成「群組上緣留白」，而是把 back-link 這個
     flex item 自己往下推、跟同一列的 .entry-actions 對不齊（實測
     兩者 top 差了快 29px，肉眼看起來像「返回」比「收藏分享」矮一截）。
     做法跟 course_detail.php 一致：margin-top 從 .entry-back-link
     搬到 .entry-action-group 本身，back-link 在這個範圍內歸零。 */
.page--content-generic .entry-action-group {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: .75rem 1.5rem;
    margin-top: clamp(2.5rem, 5vw, 4rem);
}

.page--content-generic .entry-action-group .entry-back-link {
    margin-top: 0;
}

/* 收藏／分享視覺降級：拿掉 .btn-pill 的框線與圓點徽章，變成純文字按鈕
   （客戶明講可以是「文字按鈕、小型線框按鈕、或低權重操作」，這裡選
   最輕的文字按鈕，跟①③兩層的框/實色明確分開）。知識文章頁／課程內頁
   兩邊「收藏」「分享」視覺定義完全相同，選擇器並列共用一份，不重複寫。
   ⚠ _fav_button.php／分享 <span> 的 class 一個字都沒改，這裡只在
     .page--knowledge-article／.course-detail--editorial／
     .page--service-simpla／.page--content-generic 範圍內覆寫視覺——
     service_page（consulting／ai 以外四個服務單元的內聯版型）沒有
     受影響，其餘任何未列在這裡的頁面共用同一顆 partial／同一組 class，
     樣式不受影響。
   ⚠ hover 用 --heading（灰階加深）不用 --accent-blue-text：收藏／分享
     不是導覽連結、點了不會離開頁面，跟①的「有連結用藍」語意刻意不同，
     沿用「純文字用灰」那一半慣例。
   ⚠ [data-fav="1"]（已收藏狀態）原本靠整顆反相表達，文字按鈕沒有底色
     可以反相，改成文字變 --accent-blue-text 當作「已完成的操作」視覺
     回饋——不是新色票，跟①連結 hover 同一顆 token，只是語意換成
     「狀態」而非「hover」。 */
.page--article.page--knowledge-article .entry-action-group .entry-actions .btn-pill,
.page--article.page--service-simpla .entry-action-group .entry-actions .btn-pill,
.page--content-generic .entry-action-group .entry-actions .btn-pill,
.course-detail--editorial .entry-action-group .entry-actions .btn-pill {
    min-height: auto;
    padding: 0;
    border: 0;
    gap: .375rem;
    background: transparent;
    color: hsl(var(--slate));
    font-size: var(--fs-2);
}

.page--article.page--knowledge-article .entry-action-group .entry-actions .btn-pill::after,
.page--article.page--service-simpla .entry-action-group .entry-actions .btn-pill::after,
.page--content-generic .entry-action-group .entry-actions .btn-pill::after,
.course-detail--editorial .entry-action-group .entry-actions .btn-pill::after {
    display: none;
}

.page--article.page--knowledge-article .entry-action-group .entry-actions .btn-pill:hover,
.page--article.page--knowledge-article .entry-action-group .entry-actions .btn-pill:focus-visible,
.page--article.page--service-simpla .entry-action-group .entry-actions .btn-pill:hover,
.page--article.page--service-simpla .entry-action-group .entry-actions .btn-pill:focus-visible,
.page--content-generic .entry-action-group .entry-actions .btn-pill:hover,
.page--content-generic .entry-action-group .entry-actions .btn-pill:focus-visible,
.course-detail--editorial .entry-action-group .entry-actions .btn-pill:hover,
.course-detail--editorial .entry-action-group .entry-actions .btn-pill:focus-visible {
    border-color: transparent;
    color: hsl(var(--heading));
}

.page--article.page--knowledge-article .entry-action-group .entry-actions .btn-pill[data-fav="1"],
.page--article.page--service-simpla .entry-action-group .entry-actions .btn-pill[data-fav="1"],
.page--content-generic .entry-action-group .entry-actions .btn-pill[data-fav="1"],
.course-detail--editorial .entry-action-group .entry-actions .btn-pill[data-fav="1"] {
    background: transparent;
    border-color: transparent;
    color: hsl(var(--accent-blue-text));
}

/* 手機：主要 CTA 改上下堆疊滿版寬（客戶「尺寸最大」「手機堆疊」），
   輔助操作維持水平排列不動（客戶手機版排法「收藏　分享」仍是同一列）。
   ⚠ 斷點沿用全站「手機」慣例 767.98（.entry-foot 舊有的 flex-direction:
     column 也是這個斷點，見本檔 ≈12501），桌機／平板都維持並排。 */
@media (max-width: 767.98px) {
    .page--article.page--knowledge-article .entry-action-group .entry-links,
    .page--article.page--service-simpla .entry-action-group .entry-links {
        margin-top: clamp(1.25rem, 5vw, 1.75rem);
        flex-direction: column;
        gap: .625rem;
    }

    .page--article.page--knowledge-article .entry-action-group .entry-links .btn-pill,
    .page--article.page--service-simpla .entry-action-group .entry-links .btn-pill {
        width: 100%;
        justify-content: space-between;
    }

    .page--article.page--knowledge-article .entry-action-group .entry-actions,
    .page--article.page--service-simpla .entry-action-group .entry-actions {
        margin-top: clamp(1rem, 4vw, 1.25rem);
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   文章捲動軌道 .wf-rail（2026-08-25）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「文章右下角加箭頭回頂部改成加圖片這種滾輪箭頭可拖移，文章在
   右邊靠邊的位置」——垂直軌道＋上下箭頭＋可拖曳圓形把手，貼在文章內容
   右側，把手位置即時反映目前讀到文章的哪個位置。markup 見
   _scroll_rail.php、行為見 assets/front/js/scroll-rail.js（獨立檔案，
   只有用到這顆元件的四支 view 才會引入，不進全站共用的 site.js／
   main.php 固定載入清單——這四頁以外的 15 頁不需要這份 JS）。

   ⚠ 垂直置中（top:50%）刻意不用 .wf-float 那種貼視窗底部的定位：
     兩者本來就不會互相遮擋（一個在畫面正中高度、一個在最底部角落），
     不需要另外算淨空距離，比照 [[reference-onedaylisten-wfloat-collision-zone]]
     那種「貼右/貼邊新元件要精算距離」的坑，這裡用「換一個高度」直接
     繞開，不是靠算出來的數字硬擠進同一塊區域。
   ⚠ z-index:40——沿用全站既有的 z-index 數值量表（本檔已使用過的階梯：
     0/1/2/3/5/9/20/30/40/55/60/70/1050），選一個「蓋得過一般內容、
     蓋不過 header/抽屜/.wf-float」的中間值，不新發明一個超高的數字。
   ⚠ ≥992px 才顯示：這是拖曳導覽的加分互動，窄螢幕原生捲動＋慣性已經
     夠用，桌機以下的寬度硬塞一顆貼右側的軌道容易跟内文/懸浮客服鈕
     打架，客戶目前的參考圖也是桌機情境。
   ⚠ .has-menu-open 時收起：跟 .wf-float 同一個已知坑（抽屜開著時
     z-index 較高的固定定位元件會蓋在選單上方，點不到選單項目），這裡
     直接抄同一套關閉手法，不必等出包才補。 */
/* ⚠ 這條故意寫在 media query 外面、當成全寬度都生效的基準值：
   JS（scroll-rail.js）只根據「捲動位置有沒有進入文章範圍」決定要不要
   拿掉 hidden 屬性，不知道、也不需要知道目前是不是 ≥992px。拿掉 hidden
   屬性之後，如果沒有這條基準規則兜底，手機寬度會落回瀏覽器預設的
   `display:block` ＋ `position:static`，變成一個沒有定位、沒有樣式
   的裸元素卡在文章內文中間（實測發生過，讀 computedStyle 抓到
   `display:"block", position:"static"`，截圖上是一排跑版的按鈕）。
   下面 ≥992px 的規則用 `:not([hidden])` 蓋過這裡，兩層規則合起來才是
   完整的「有沒有進入文章範圍」×「是不是桌機寬度」兩個條件都要成立
   才顯示。 */
.wf-rail {
    display: none;
}

@media (min-width: 992px) {
    .wf-rail:not([hidden]) {
        position: fixed;
        top: 50%;
        right: clamp(1rem, 2.4vw, 2rem);
        transform: translateY(-50%);
        z-index: 40;
        display: flex;
        flex-direction: column;
        align-items: center;
        gap: .625rem;
    }

    /* 2026-08-25 客戶附參考圖指定「箭頭不要圓形的底不要邊匡，只箭頭
       就好」——拿掉圓形底色與描邊，只留線條箭頭本身，按鈕退化成一個
       純粹的點擊區（寬高留著才有夠大的觸擊範圍，但視覺上完全透明）。 */
    .wf-rail-btn {
        width: 24px;
        height: 24px;
        flex: none;
        display: flex;
        align-items: center;
        justify-content: center;
        border: 0;
        background: transparent;
        color: hsl(var(--foreground) / .55);
        cursor: pointer;
        transition: color var(--transition);
    }

    .wf-rail-btn svg {
        width: 16px;
        height: 16px;
    }

    @media (hover: hover) {
        .wf-rail-btn:hover {
            color: hsl(var(--heading));
        }
    }

    /* 客戶「滾輪加長現在有點短」——140→260px（參考圖的軌道明顯比
       兩顆箭頭的間距長很多，比例接近 1:10 以上）。 */
    .wf-rail-track {
        position: relative;
        width: 2px;
        height: 260px;
        background: hsl(var(--foreground) / .35);
        border-radius: 1px;
        cursor: pointer;
    }

    /* 客戶「滾輪的圓形在線中間，圓圈一個單色就好不要描邊」——拿掉
       border 與 box-shadow，改成單一實心色。
       ⚠ 水平置中**不能**用 `left:0; right:0; margin-inline:auto`（第一版
         的寫法，實測圓心偏右 6px 沒有對準軌道中線）：那一套只在「元素比
         容器窄」時成立。這裡容器（.wf-rail-track）只有 2px 寬、圓 14px，
         元素比容器寬 12px，兩邊 auto 各分到 -6px 是負值——CSS 規範規定
         這種情況不平均分配，改成把 margin-left 設 0、由 margin-right 吸收
         全部負值，結果是圓的**左緣**貼齊軌道左緣，圓心因此偏右
         (14-2)/2 = 6px（實測 handleCenterX 1402 vs trackCenterX 1396，
         正好差 6）。
       ⚠ 改用 `left:50%` ＋ 負的 margin-left（圓寬的一半）；不能用
         `transform: translateX(-50%)`——transform 這個屬性已經被 JS
         拿去做垂直位移（`translateY(...)`），兩邊搶同一個屬性會互相
         覆蓋。負 margin 跟 transform 是各自獨立的屬性，不會打架。 */
    .wf-rail-handle {
        position: absolute;
        top: 0;
        left: 50%;
        margin-left: -7px;
        width: 14px;
        height: 14px;
        border-radius: 50%;
        background: hsl(var(--foreground) / .75);
        cursor: grab;
        /* 讓 pointer 拖曳不會被瀏覽器當成頁面手勢（觸控板兩指捲動／觸控
           螢幕）搶走，drag 全程要由 JS 自己算，不能被系統手勢打斷。 */
        touch-action: none;
    }

    .wf-rail-handle:active {
        cursor: grabbing;
    }

    .wf-rail-handle:focus-visible {
        outline: 2px solid hsl(var(--accent-blue));
        outline-offset: 2px;
    }

    .has-menu-open .wf-rail {
        display: none;
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   收藏鈕愛心圖示＋顧問案/AI分身動作列改同一行（2026-08-27 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶原話：「收藏 分享放最右邊跟預約對話同一行，收藏加線框愛心icon
   按收藏後愛心在填實色」。分三件事： */

/* ① 愛心與文字並排、間距沿用站上小按鈕的節奏。
   ⚠ .btn-pill 本體（尺寸／hover／::after 徽章）一律不覆寫，這裡只處理
     新加進去的圖示與文字之間的排列。 */
.btn-fav {
    display: inline-flex;
    align-items: center;
    gap: .375rem;
}

.btn-fav .wf-icon--heart {
    width: 1em;
    height: 1em;
    flex-shrink: 0;
}

/* ② 未收藏＝線框（wf_icon() 預設就是 fill:none + stroke，不必再寫）；
   已收藏＝同一顆 path 補上填色變實心。狀態來源仍然只有 data-fav。
   ⚠ 用 currentColor 而不是寫死色票：這顆鈕在 [data-fav="1"] 時整顆會被
     既有規則反相成膚色實心底（見本檔第 7 節），文字色跟著變，愛心跟著
     currentColor 走才不會跟文字脫節。 */
.btn-fav[data-fav="1"] .wf-icon--heart {
    fill: currentColor;
}

/* ③ 主要操作與「收藏／分享」改成同一列，輔助操作靠右。
   ⚠ 純 CSS 不動標記：.entry-action-group 底下依序是 .entry-back-link、
     一顆 .wf-label 註記、.entry-links（主要操作）、.entry-actions（收藏／
     分享）。讓容器 flex-wrap，前兩者各自佔滿整行，後兩者就會自然落在同
     一行；.entry-actions 用 margin-left:auto 推到最右。這樣不必新增
     wrapper div，也就不會重蹈「拆共用 class 時把 border 分隔線漏掉」那類
     重排副作用。
   ⚠ .entry-actions 的 margin-top 要歸零：它原本是「上下堆疊」時用來跟上
     一層拉開距離的，現在同列會把整顆往下推、跟主要操作對不齊。
   ⚠ 適用頁面（2026-08-27 分兩次追加）：
       ① 顧問案／AI 分身（.page--service-simpla）：主要操作＝預約顧問對話
       ② 知識文章（.page--knowledge-article）：主要操作＝觀看 YouTube 影片／
          收聽 Podcast 兩顆——客戶「收藏 分享移到右跟按鈕同行邊，現在是靠左
          在兩個按鈕下面」。第一次只套 ① 是因為客戶當時只指名那一頁，
          隔一輪就被要求同樣套過來；兩頁的 .entry-action-group 子節點結構
          完全一樣（back-link／wf-label／entry-links／entry-actions），
          所以只是把選擇器並列上去，規則本身一個字都不用改。
     ⚠ 課程內頁（.course-detail--editorial）**不套**：那頁沒有 .entry-links
       這一層主要操作，收藏／分享會直接跟返回連結擠在同一行，語意與版面
       都不對；客戶也沒有指名。要套之前先確認那頁的結構。 */
.page--article.page--service-simpla .entry-action-group,
.page--article.page--knowledge-article .entry-action-group {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
}

.page--article.page--service-simpla .entry-action-group > .entry-back-link,
.page--article.page--service-simpla .entry-action-group > .wf-label,
.page--article.page--knowledge-article .entry-action-group > .entry-back-link,
.page--article.page--knowledge-article .entry-action-group > .wf-label {
    flex: 0 0 100%;
}

/* ⚠ 2026-08-27 客戶「齊底」——收藏／分享原本吃容器的 align-items:center，
   在那一列裡垂直置中，跟旁邊按鈕的底緣對不齊。改成 align-self:flex-end
   貼齊列底。
   ⚠ 為什麼不是只改容器的 align-items：主要按鈕那一排（.entry-links）維持
     置中沒問題，只有輔助操作要沉到底；動容器會把兩邊一起改掉。
   ⚠ 「齊底」對齊的是**按鈕的框底線**，不是按鈕裡的文字底線——收藏／分享
     是無框純文字，旁邊是有框按鈕，視覺上讀的是那條看得見的邊框線。
     .btn-pill 有上下 padding，框底與字底相差一截，用 flex-end 讓
     .entry-actions 的框底貼齊列底（＝按鈕框底）才是客戶要的那條線；
     這裡刻意不去對齊兩者的文字基線（見 memory
     boxtop-vs-text-range-alignment 那條：對齊需求要先確認對的是框還是字，
     這次是框）。 */
.page--article.page--service-simpla .entry-action-group > .entry-actions,
.page--article.page--knowledge-article .entry-action-group > .entry-actions {
    margin-top: 0;
    margin-left: auto;
    align-self: flex-end;
}

/* ═══════════════════════════════════════════════════════════════════════
   已收藏 hover 提示 ＋ 未登入攔截視窗（2026-08-27 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「已收藏按鍵hover會跳出文字可至會員我的收藏查看，然後還未登入的
   跳到我的收藏要跳出登入會員懸浮視窗阻擋」。 */

/* ① 已收藏 hover 提示：細框文字，且可以點擊前往我的收藏
   ⚠ 2026-08-27 客戶兩次指定合併結果：先是「hover 跳出文字」（第一版做成
     偽元素泡泡），同日追加「細匡加文字不要填色，hover以後讓可以點擊前往
     我的收藏」——**偽元素點不到**，所以改成 _fav_button.php 裡真的 <a>，
     這裡只負責定位與外觀。
   ⚠ 「不要填色」＝不要原本那塊深色實心晶片。底色改用頁面底 --background
     （不是新色票、視覺上等同沒有填色，只是讓文字不會跟背後內容疊在一起
     看不清楚），外觀靠 1px hairline 細框撐起來。
   ⚠ 只在 [data-fav="1"]（已收藏）出現；用 .act-wrap:hover / :focus-within
     當觸發，不是 .btn-fav:hover——focus-within 讓鍵盤 Tab 到按鈕或提示本身
     都會顯示，而且游標從按鈕移到提示上時整組仍在 hover 範圍內，提示不會
     在半路消失（這是「要能點擊」的前提）。
   ⚠ 提示與按鈕之間有 .5rem 視覺間隙，若真的留空會造成滑鼠移過去的瞬間
     離開 hover 區、提示消失；用提示自己的 ::after 補一塊透明的橋接區把
     縫填起來（它是提示的子孫，滑到橋上一樣算 .act-wrap:hover）。 */
.act-wrap {
    position: relative;
    display: inline-flex;
}

/* ⚠ 2026-08-27 客戶「手機版設計一下」回測：氣泡原本往**上**長且水平置中
   （bottom:100% + left:50% + translateX(-50%)），兩個問題：
     ① 往上長 → 正上方就是主要按鈕，氣泡一浮出來就蓋住按鈕文字。手機最明顯
        （按鈕在正上方），但 768 平板也中——實測氣泡與「收聽 Podcast」水平
        重疊 19–38px。
     ② 水平置中 → 這顆鈕貼在內容區右緣，置中會往右溢出；390 寬時只剩 36px
        餘裕，字級切 A++ 就會被切掉。
   改成**往下長＋靠右對齊**，全斷點一致：下方是 wf-note 之前的留白，不擋
   任何可讀內容；靠右則讓氣泡永遠往左長，不可能溢出右緣。 */
.act-tip {
    position: absolute;
    top: calc(100% + .5rem);
    right: 0;
    transform: translateY(-.25rem);
    max-width: min(15rem, calc(100vw - 3rem));
    z-index: 20;
    width: max-content;
    padding: .4rem .7rem;
    border: 1px solid hsl(var(--foreground) / .28);
    border-radius: var(--radius-media);
    background: hsl(var(--background));
    color: hsl(var(--heading));
    font-size: var(--text-meta);
    font-weight: var(--fw-normal);
    letter-spacing: var(--tracking-meta);
    line-height: 1.5;
    text-align: center;
    text-decoration: none;
    white-space: normal;
    opacity: 0;
    visibility: hidden;
    transition: opacity var(--transition), transform var(--transition),
                visibility var(--transition), border-color var(--transition), color var(--transition);
}

/* 橋接區：填滿提示與按鈕之間那 .5rem 的視覺間隙，讓游標移得過去。
   氣泡改成往下長之後，縫在氣泡的**上**緣。 */
.act-tip::after {
    content: '';
    position: absolute;
    bottom: 100%;
    left: 0;
    right: 0;
    height: .5rem;
}

/* 何時顯示：
     收藏 → 已收藏（data-fav="1"）之後 hover／鍵盤聚焦
     分享 → 已複製（data-shared="1"）之後 hover／鍵盤聚焦
     .is-tip-open → 剛按下分享的那幾秒，由 pages.js 掛上強制顯示。
       「已複製」是動作回饋，使用者按完通常就把游標移開了，只靠 hover 等於
       永遠看不到那句確認；所以按下當下先自動亮出來，之後才交回 hover。 */
.act-wrap:has(.btn-fav[data-fav="1"]):hover .act-tip,
.act-wrap:has(.btn-fav[data-fav="1"]):focus-within .act-tip,
.act-wrap:has(.btn-share[data-shared="1"]):hover .act-tip,
.act-wrap:has(.btn-share[data-shared="1"]):focus-within .act-tip,
.act-wrap.is-tip-open .act-tip {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
}

/* 可點擊的提示：hover/focus 用站上既有的連結 hover 藍（--accent-blue-text，
   見 memory accent-blue-text-only-token：--accent-blue 是底色專用、不能
   當文字色），跟全站「有連結用藍」的語意一致。 */
.act-tip--link:hover,
.act-tip--link:focus-visible {
    border-color: hsl(var(--accent-blue-text));
    color: hsl(var(--accent-blue-text));
}

/* 狀態型提示（分享的「已複製」）：不是連結，不要有 hover 變色與滑鼠手勢，
   也不接收滑鼠事件——它蓋在按鈕上方，能接收事件的話游標移上去會讓按鈕
   的 :hover 失效、泡泡開始閃爍（收藏那顆是連結、要能點，才刻意保留）。 */
.act-tip--status {
    pointer-events: none;
    white-space: nowrap;
}

@media (prefers-reduced-motion: reduce) {
    .act-tip {
        transition: none;
    }
}

/* ② 未登入攔截視窗
   ⚠ z-index 1100：要蓋過 .wf-float 浮動客服鈕（1050，見 memory
     wfloat-collision-zone），否則那三顆鈕會浮在遮罩上面。站上其他懸浮層
     用的 60 在這裡不夠。 */
.login-gate {
    position: fixed;
    inset: 0;
    z-index: 1100;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--section-x);
    background: hsl(var(--foreground) / .45);
    opacity: 0;
    transition: opacity var(--transition);
}

.login-gate[hidden] {
    display: none;
}

.login-gate.is-open {
    opacity: 1;
}

.login-gate-card {
    position: relative;
    width: min(30rem, 100%);
    padding: clamp(2.5rem, 5vw, 3.5rem) clamp(2rem, 4.5vw, 3rem);
    background: hsl(var(--muted));
    text-align: center;
    transform: translateY(.75rem);
    transition: transform var(--transition);
}

.login-gate.is-open .login-gate-card {
    transform: translateY(0);
}

@media (prefers-reduced-motion: reduce) {
    .login-gate,
    .login-gate-card {
        transition: none;
        transform: none;
    }
}

.login-gate-close {
    position: absolute;
    /* 2026-08-27 回測：14px 時它離卡片邊緣比其他內容（內距 40–56px）近很多，
       視覺上像黏在角落、跟內容不同一套格線；往內收一點折衷。 */
    top: 1.25rem;
    right: 1.25rem;
    display: inline-flex;
    width: 2rem;
    height: 2rem;
    align-items: center;
    justify-content: center;
    border: 0;
    background: transparent;
    color: hsl(var(--slate));
    font-size: 1rem;
    line-height: 1;
    cursor: pointer;
    transition: color var(--transition);
}

.login-gate-close:hover,
.login-gate-close:focus-visible {
    color: hsl(var(--heading));
}

/* ⚠ tombstone：這裡原本是 .login-gate-en／-title／-lead 三顆自訂字級。
   2026-08-27 客戶「標題跟內文樣式請照這個，英文要用英文小標題的樣式跟字體」
   ——改成直接沿用站上既有的 .page-head--center／.page-head-en／.page-title／
   .page-lead（見 _login_modal.php 那段說明），三顆自訂 class 全數移除，
   避免同一種視覺存在兩份定義。下面只留「彈窗這個尺度需要的收斂」。 */

/* 彈窗版頁首：沿用 .page-head 全套視覺，只把為整頁設計的大留白收掉——
   .page-head 的 margin-bottom 是 40–72px、.page-head-en 的是 39px，那是
   一整頁頂部的節奏，塞進 480px 寬的卡片會鬆散到看不出是同一組。 */
.login-gate-card .page-head {
    margin-bottom: 0;
}

.login-gate-card .page-head-en {
    margin-bottom: clamp(1rem, 2vw, 1.25rem);
}

/* 主標在彈窗裡降一階：--text-section 是整頁區塊主標（桌機 30–38px），
   放進彈窗會把「這一區需要先登入」撐成兩行、壓過旁邊的說明文字。
   ⚠ 只改 font-size／line-height，字重（--fw-heading）與字距
     （--tracking-display）維持 .page-title 原值——客戶要的是「照這個樣式」，
     那兩項才是這組標題的辨識特徵，改了就不像了。 */
.login-gate-card .page-title {
    font-size: clamp(1.375rem, 2.2vw, 1.625rem);
    line-height: 1.5;
}

.login-gate-card .page-lead {
    margin-top: clamp(1rem, 2.2vw, 1.375rem);
    text-wrap: balance;
}

/* ⚠ 2026-08-27 客戶「這個排版在有空氣感一點，文字跟按鈕太擠，多些空間跟
   細線做層次」——文字段與按鈕之間補一條 hairline 分層，上下各留一段
   等距留白（margin-top 是線以上、padding-top 是線以下，兩邊同值，線才會
   落在正中間、不會偏towards任一側）。
   ⚠ 線色沿用站上既有的 hairline 濃度 foreground/.12（跟提示框、下拉面板
     同一階），不新增色票。
   ⚠ 只加這一條線：eyebrow／標題／內文之間已經靠留白分層，再加線會變成
     一格一格的表格感，跟站上其他懸浮層的語彙不合。 */
.login-gate-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
    justify-content: center;
    flex-direction: column;
    align-items: center;
    gap: 1.25rem;
    margin-top: clamp(2rem, 4.5vw, 2.75rem);
    padding-top: clamp(2rem, 4.5vw, 2.75rem);
    border-top: 1px solid hsl(var(--foreground) / .12);
}

/* 次要動作：純文字連結（見 _login_modal.php 那段回測說明——兩顆實心按鈕
   並排分不出主次，站上又不准做反白次要按鈕）。hover 用全站「有連結用藍」
   的 --accent-blue-text，跟提示框、文章內連結同一套語意。 */
.login-gate-alt {
    color: hsl(var(--slate));
    font-size: var(--text-meta);
    letter-spacing: var(--tracking);
    text-decoration: none;
    transition: color var(--transition);
}

.login-gate-alt:hover,
.login-gate-alt:focus-visible {
    color: hsl(var(--accent-blue-text));
    text-decoration: underline;
    text-underline-offset: .25em;
}

/* 視窗開著時鎖住底下捲動（比照 .site-search-panel 舊版的 has-search-open） */
html.has-login-gate {
    overflow: hidden;
}

/* ═══ 課程頁「報名資訊」區塊（course_detail.php）═════════════════════
   2026-08-28 客戶：固定格式，左圖右表，每門課都有。

   ⚠ 只寫「結構化資料列」這一段自己的樣式。容器（.cd-enroll-split 兩欄
     不對稱 grid）、圖片（.cd-enroll-media 出血＋細線格＋遮罩顯影）、
     上下兩條滿版水平線（.cd-enroll-rule）全部沿用設計師那份，寫在本檔
     第 44 節「.course-detail--editorial .cd-enroll-*」——2026-08-28 核對
     設計稿時發現這裡本來另外寫了一份 .cd-enroll／.cd-enroll-media，
     跟那份重複又比它弱（少一個 class 的特異度），只是白白讓同一件事有
     兩個定義；已刪除，改成 markup 直接掛設計稿的 class。
   ⚠ 這裡不再給左右內距：那是 .section-bleed-inner.cd-enroll-split 的
     padding-inline: var(--article-x) 的事，兩邊都寫會內縮兩次。 */
.cd-enroll-body {
    display: flex;
    flex-direction: column;
    /* 間距跟設計稿那欄的 .cd-enroll-cols 同一個尺度（實測 40px）——
       原本 1.25rem 是自己另外挑的，跟隔壁區塊的呼吸感對不上。 */
    gap: clamp(1.5rem, 3vw, 2.5rem);
}

/* 資訊列：label 固定寬、值靠左對齊成一直線（截圖的排法）
   ⚠ label 用 ■ 當項目符號、寬度固定，值才會對齊；
     沒有固定寬度的話「堂數」那列的值會往左跑，跟其他列對不齊。 */
.cd-enroll-list {
    margin: 0;
}

.cd-enroll-row {
    display: flex;
    align-items: baseline;
    gap: 1rem;
    padding: .55rem 0;
}

.cd-enroll-row dt {
    flex: 0 0 auto;
    width: 6.5em;
    font-weight: var(--fw-normal);
    color: hsl(var(--foreground) / .75);
    font-size: var(--text-meta);
    letter-spacing: var(--tracking-meta);
}

/* ■ 項目符號：用 ::before 不用 list-style，dt 是 flex item 沒有 marker */
.cd-enroll-row dt::before {
    content: '■';
    margin-right: .5rem;
    font-size: .7em;
    vertical-align: middle;
}

.cd-enroll-row dd {
    margin: 0;
    color: hsl(var(--heading));
    font-size: var(--text-body);
}

.cd-enroll-note p {
    margin: 0 0 .5rem;
    color: hsl(var(--foreground) / .8);
    font-size: var(--text-meta);
    line-height: 1.9;
}

.cd-enroll-note p:last-child {
    margin-bottom: 0;
}

/* 手機：資訊列改上下堆疊
   ⚠ 圖文的單欄堆疊與出血不在這裡處理——設計師那份的 @media
     (max-width: 1199.98px) 已經做掉（見第 44 節 .cd-enroll-split／
     .cd-enroll-media），這裡再寫一次只會兩邊打架。 */
@media (max-width: 767.98px) {
    .cd-enroll-row {
        flex-direction: column;
        gap: .2rem;
    }

    .cd-enroll-row dt {
        width: auto;
    }
}

/* ═══ 電子報訂閱：送出後的提示訊息（2026-08-31）═══════════════════════
   三個訂閱入口（電子報頁的訂閱帶／頁尾／右下角浮動面板）共用同一個訊息列，
   由 pages.js 填字並拿掉 hidden。

   ⚠ 這裡只管**排版**，顏色不自己挑：站上表單回饋一向走 Bootstrap 的
     .alert-danger／.text-danger（見 member_login.php／member_center.php），
     錯誤色由 pages.js 切 .text-danger 這顆 Bootstrap 既有的 utility 負責。
     自己定一組 --error 色票等於在站上多一種來源不明的紅色。
   ⚠ 顯示與否只由 hidden 屬性控制（pages.js 切 el.hidden），這裡不寫
     display:none —— 兩套機制會打架。 */
[data-edm-msg] {
    margin: .75rem 0 0;
    font-size: var(--text-meta);
    line-height: 1.8;
    letter-spacing: var(--tracking-meta);
}

/* 電子報頁那一個在 Bootstrap 的 .row 裡，欄位本身已經有 gutter，
   上距不必再疊一層。 */
.nl-subscribe-form [data-edm-msg] {
    margin-top: .25rem;
}

/* ⚠ 2026-08-31：這裡原本有一份手工搬過來的「標籤配色」區塊（08-28 核對設計稿時
     補的）。合併 grace 之後設計師的正式版本已經在本檔後面（搜尋
     `.kd-hero-tags .tag-pill`），內容一致而且多了一段特異度說明，
     兩份留著只是同一件事有兩個定義——手工那份已刪除。 */

/* ═══════════════════════════════════════════════════════════════════════
   內頁動作列：手機版（2026-08-27 客戶「手機版設計一下」）
   ═══════════════════════════════════════════════════════════════════════
   桌機把「收藏／分享」拉到主要按鈕同一列靠右（見上方 ③）。窄螢幕放不下時
   flex-wrap 會讓它自己掉下來，但**掉不掉是看內容寬度、不是看斷點**——實測
   知識文章頁（兩顆按鈕）390 寬才換行、440 就又擠回同一列；顧問案頁（一顆
   按鈕）到 768 都不換行。而且字級切 A++ 按鈕變寬，換行點又會往右移。
   靠自然換行的話，同一支手機轉個方向或調字級就會在「同列／換行」之間跳，
   間距也跟著忽有忽無。

   所以這裡**不賭自然換行**：≤575.98 直接讓它固定佔滿一整行、靠右、與上方
   按鈕維持固定間距，行為對所有頁面與所有字級都一致。
   ⚠ 576–767 這段不強制換行：實測兩頁在此區間都還放得下同一列，硬拆會平白
     浪費一行高度。
   ⚠ align-self 在「自己獨佔一行」時沒有意義（flex-end 是為同列對齊寫的），
     歸位避免日後誤讀。 */
@media (max-width: 575.98px) {
    .page--article.page--service-simpla .entry-action-group > .entry-actions,
    .page--article.page--knowledge-article .entry-action-group > .entry-actions {
        flex: 0 0 100%;
        justify-content: flex-end;
        align-self: auto;
        margin-top: 1.25rem;
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   Hero 主圖在「高視窗」被拉成直式（2026-08-27 客戶回報，附 1475 寬截圖）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「寬度1475的時候hero跑版了，重新設計一下」。

   ⚠ 真因不是寬度、是**視窗高度**。回報的那張截圖是 1475x1533；實測把高度
     固定在 1533，主圖高度在 1475／1680／1920／2560 四個寬度**全都是
     1357px**，跟寬度完全無關。壞在 >=1468 這組桌機規則：

         .hero-card-media  height: 100%                  撐滿 100svh
         .hero-media       height: 100%; aspect-ratio: auto   明確取消比例約束

     Card 版面當初是照桌機構圖對出來的，驗證高度都落在 860-1080，主圖算出來
     是 1.53-1.58 的橫式；但沒有任何東西擋著它繼續長高，1533 高時主圖變成
     1092x1357（比例 0.80，直式），照片主體被擠到下緣、上面三分之二全是牆，
     右側灰欄與次圖跟著拉長，整個構圖散掉。
     反而 <=1467.98 那組沒事——它有 aspect-ratio: 16/9 擋著。

   修法：給主圖一個「由寬度換算的高度上限」，高視窗時維持 3:2 橫式，主圖與
   右側次圖一起在整段 Hero 高度裡垂直置中。灰底不動，仍然整欄滿高（客戶已
   確認「灰色背景要壓整個hero的高」）。

   ⚠ --hero-media-w 是**精確推導**不是估值。Card 是
     grid-template-columns: var(--hero-card-left-w) 1fr var(--hero-panel-w)、
     右欄 30%，主圖那欄再靠負的 margin-right 往右延伸，所以
         主圖寬 = 70vw - 左窄欄 + |margin-right|
     其中 |margin-right| 就是 .hero-card-media 那條 clamp(-11rem, -10vw, -6rem)
     的鏡像。四個寬度驗算 vs 實測：1475 -> 1091/1092、1680 -> 1248/1248、
     1920 -> 1424/1424、2560 -> 1872/1872。
   ⚠ 上限是用 min() 夾在原本的高度來源之後，所以**一般筆電高度一個像素都不
     會變**：1475x860 現況 714、上限算出 727 不生效；1920x1080 現況 904、
     上限 947 不生效。只有視窗相對變高時才接手。
   ⚠ 3:2 是照現況取的、不是新訂比例：1475x860 現況 1.53、1920x1080 現況
     1.58，取 1.5 讓高視窗跟這兩個已定案的畫面同一個語彙。
   ⚠ .hero-text-module／.hero-arrow-module／.hero-pageno 三者都是相對
     .hero-card-media 絕對定位，主圖框一縮它們自動跟著走，不必另外改。
   ⚠ 不動 .hero 的 min-height:100svh——它是全站堆疊捲動（sticky 簾幕）的
     高度來源，改它會波及後面每一個區塊的釘選位置。
   ⚠ 2026-08-27 下界跟著 Card 版面一起從 1468 降到 1200——不然
     1200–1467.98 這段拿到桌機版面卻沒有這道高度上限，高視窗又會把主圖
     拉成直式（就是這段註解上面在講的那個 bug）。 */
@media (min-width: 1024px) {
    html:has(.hero) {
        --hero-media-w: calc(70vw - var(--hero-card-left-w) + clamp(6rem, 10vw, 11rem));
        --hero-content-h: min(
            calc(100svh - var(--hero-card-media-pad-y) * 2),
            calc(var(--hero-media-w) / 1.5)
        );
    }

    .hero-card-media {
        height: auto;
        align-self: center;
    }

    .hero-media {
        height: var(--hero-content-h);
    }

    /* 次圖跟主圖同高、同一條中線（客戶既有要求「次圖請跟主圖一樣高」）。
       .hero-card-right 自己仍然是 stretch 滿高，只是內容改置中。 */
    .hero-card-right {
        justify-content: center;
    }

    .hero-card-right-preview {
        flex: none;
        height: var(--hero-content-h);
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   實體課程總頁面：Hero 方格區「最上緣」那條線刪掉（2026-08-27 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶附截圖：麵包屑「首頁 / 實體課程」底下多一條橫跨整頁寬的細灰線，
   「直接把該條線刪除」，並自己指出「應該是圖片最上面的線被擠下來，不是
   header 的，因為只有這頁面有」——判斷正確。

   成因：`.cx-hero-grid` 的格線不是各自畫的 border，而是
   `gap: 1px` ＋ 整塊 `background: hsl(var(--foreground) / .2)`，靠格子與
   格子之間的縫隙露出底色形成（見 ≈17681 那段的說明：「gap 露線在 4x2、
   零合併的前提下，天生就是精確的 3 條垂直線＋1 條水平線，不需要另外處理
   最外圈」）。這個做法的前提是「格子完全蓋住底色」，但那是**理想值**——
   實測 `.cx-hero-grid` 上緣落在 112.39 / 149.39 這種非整數位置，乘上
   螢幕縮放後就是分數裝置像素，格子背景與容器背景各自四捨五入到不同的
   裝置像素時，最上緣就會漏出一條 1 裝置像素的底色＝一條線。
   ⚠ 這是「同一份 CSS，我這邊量不到、客戶那邊看得到」的典型次像素縫，
     跟本檔 2026-08-19／08-21 兩次遮罩「發光」是同一個病根家族
     （見 .hero-mask 的 margin:-1px 與 .cd-hero-mask 的 -2px 超蓋）。
     我在 beta 與 mtest、16 個寬度 x DPR 1/2、桌機與手機版型全掃過都
     沒有拍到，但那不代表使用者端沒有——`Page.captureScreenshot` 會重新
     光柵化，四捨五入的結果不見得跟即時合成一樣。
   ⚠ 之所以敢直接改：這條線本來就**不在設計裡**。上面那段註解已經明講
     「不需要另外處理最外圈——多疊一圈 border 反而是客戶沒要求的裝飾」，
     所以「最外圈不該有線」是既定設計，這裡只是讓它真的不可能出現。

   修法：底色改用 linear-gradient，讓最上面 1px 保持透明——格子蓋得住的
   部分本來就看不到這 1px，蓋不住時漏出來的也是透明（露出 .cx-hero 自己
   的頁面底色），不會再是深灰。
   ⚠ 只處理**上緣**：左右緣貼齊視窗（滿版出血，外面沒有東西可以對比）、
     下緣本來就有一條刻意畫的 `.cx-hero-line--bottom`，都不動。
   ⚠ 內部格線一個像素都沒動：gap 的位置在第一列之後，仍然吃到漸層的實色
     段。改完用像素比對確認過 hero 區域完全相同。
   ⚠ background-color 必須一起歸零——只寫 background-image 的話，
     底下那層 background-color 會從透明的那 1px 透出來，等於沒改。 */
.page--wall .cx-hero-grid {
    background-color: transparent;
    background-image: linear-gradient(
        to bottom,
        transparent 0,
        transparent 1px,
        hsl(var(--foreground) / .2) 1px
    );
}

/* ═══════════════════════════════════════════════════════════════════════
   實體課程總頁面 Hero：箭頭靠左、右下矩形改標題級中文、英文小標改全站樣式
   （2026-08-27 客戶四項指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶原文：「該頁的矩形方塊裡的直向箭頭改放該格左邊，下方矩形最右邊那格
   文字改英文字：Scroll down to see more，中文字改往下查看完整課程然後字級
   用標題的不要內文的，矩形的英文字改全站英文小字字體跟樣式」。

   文案兩句改在 courses_list.php（舊文案留在該處註解裡沒有刪除），這裡只處理
   三件樣式：

   ── ① 直向箭頭改靠左 ────────────────────────────────────────────────
   兩格都有直向箭頭（intro 與 void4），而且客戶 2026-08-18 就指定過
   「最右邊的矩形也加小字跟往下指的箭頭跟上面的一樣」——兩格是刻意做成
   同一組元件，所以這裡兩格一起改，不是只改其中一格。
   ⚠ 兩格的箭頭定位機制不同，要分開處理：
     ・void4 走 flex（`align-self: flex-end` → `flex-start`）
     ・intro 走絕對定位（`right: 1.25rem` → `left: 1.25rem`，right 必須
       明確歸 auto，否則左右都有值會把 svg 拉寬變形）
   ⚠ 包在 min-width: 1200px 裡：≤1199.98 這格是橫向窄列、箭頭用
     `align-self: center`（那是縱軸置中，不是左右），跟這次的需求無關，
     不能被這條蓋掉。

   ── ② 右下那格的中文改標題級 ────────────────────────────────────────
   `.cx-hero-intro-desc` 原本是 --text-meta（13px，比內文還小一階，設計
   定位是「次要說明」）。客戶要「用標題的不要內文的」——直接對齊同一個
   Hero 裡 H1 `.cx-hero-title` 那組（--text-section / --lh-section /
   --fw-heading），不另外訂一顆新字級。
   ⚠ 只鎖 void4 這一格：`.cx-hero-intro-desc` 是 intro／void4 共用的
     class（本檔 ≈17912 已經因為這件事踩過一次坑），intro 那格維持小字。
   ⚠ `-webkit-line-clamp: 2` 保留不動：那是防「文字撐高把箭頭擠出格外被
     overflow:hidden 切掉」的既有保險，字級變大之後只會更需要它。

   ── ③ 英文小標改全站英文小字 ────────────────────────────────────────
   `.cx-hero-eyebrow` 原本沒有指定 font-family（吃 body 的中文字體）、
   字級是 --text-body。全站英文小字那一套是 `.section-title .en`
   （首頁「UPCOMING COURSES」那種）：--font-eyebrow（Julius Sans One）
   ＋ --text-meta ＋ --lh-meta ＋ --fw-normal ＋ --tracking-meta。
   ⚠ 顏色刻意不動：`.section-title .en` 用 --muted-foreground，是配合
     首頁那些區塊的底色調出來的；這裡的 hsl(var(--foreground)/.82) 是配
     Hero 方格自己的底色。客戶要的是「字體跟樣式」，字體與字級才是那套
     英文小字的識別，色階跟著所在底色走是全站既有慣例。
   ⚠ 這條會同時套到 intro／void4／statement 三格的 eyebrow——客戶說的是
     「矩形的英文字」，三格都是矩形，維持一致。 */
@media (min-width: 1024px) {
    .page--wall .cx-hero-arrow--down {
        align-self: flex-start;
    }

    .page--wall .cx-hero-cell--intro .cx-hero-arrow--down {
        right: auto;
        left: 1.25rem;
    }
}

.page--wall .cx-hero-cell--void4 .cx-hero-intro-desc {
    font-size: var(--text-section);
    line-height: var(--lh-section);
    font-weight: var(--fw-heading);
    color: hsl(var(--heading));
}

.page--wall .cx-hero-eyebrow {
    font-family: var(--font-eyebrow);
    font-size: var(--text-meta);
    line-height: var(--lh-meta);
    font-weight: var(--fw-normal);
    letter-spacing: var(--tracking-meta);
}

/* ═══════════════════════════════════════════════════════════════════════
   實體課程總頁面 Hero：往下箭頭加動態（2026-08-28 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶（附截圖，截的是 void4 那格「Scroll down to see more／往下查看完整
   課程」下面那支長箭頭）：「箭頭加動態效果，往下指的感覺」。

   ── 為什麼一條規則就涵蓋兩格 ──────────────────────────────────────
   intro 與 void4 的箭頭是**刻意做成同一個元件**（2026-08-18 客戶「最右邊
   的矩形也加小字跟往下指的箭頭跟上面的一樣」，見本檔 ≈17777 那條共用版面
   規則的註解）。所以動態掛在 .cx-hero-arrow--down 這個共用變體上，兩格
   一起動，不是只改客戶截圖那一格——這個站已經有過「同一個元件只改一半」
   的紀錄，不重蹈覆轍。
   ⚠ --right（statement 那格的橫向箭頭）**不套**：客戶要的是「往下指」的
     感覺，橫向箭頭套上去方向會相反、語意不對。

   ── 位移量為什麼用 min(9px, 22%) 而不是寫死 px ────────────────────
   這支箭頭的高度隨斷點變化（實測：≥1300 是 104px、1200–1299.98 收成
   16px、≤991.98 兩格整個不顯示）。寫死一個 px：對 104px 的長箭頭嫌少、
   對 16px 的小箭頭又太多（9px 等於它自己高度的 56%，會變成整支在跳）。
   min() 讓它自己挑：
     ・104px → 22% 是 22.9px，min 取 9px（約自身高的 8.7%，克制的一下）
     ・16px  → 22% 是 3.5px，min 取 3.5px（比例相同的一下）
   一條規則涵蓋所有斷點，**不必再開 media query 逐層調**——這個 Hero 的
   斷點分散在好幾個互不相鄰的區塊，每多開一層就多一次「延伸斷點漏一層」
   的機會（見 .agent/deploy.md 第十二次那節的教訓）。
   ⚠ translateY 裡的百分比是相對**元素自己的高度**解析的，而且 custom
     property 裡的 % 不會被凍結、會在每個使用處各自重算——這裡正是要這個
     行為（見 memory footer-overlay-pseudo-pitfalls 記過同一個機制）。

   ── 安全邊界（實測，不是估的）─────────────────────────────────────
   箭頭下緣到 cell 下緣的淨空在 1920／1440／1300／1200 全部都是 **20px**
   （cell 的 padding 1.25rem）。9px 的位移還剩 11px 餘裕，所以就算是
   overflow:hidden 的 void4 格也不會被裁到。（intro 那格是
   overflow:visible ＋ 絕對定位 bottom:1.25rem，同樣 20px。）

   ── 動態本身 ──────────────────────────────────────────────────────
   位移佔整個週期的前 60%，後 40% 停著不動——是「隔一段時間往下指一下」
   的手勢，不是持續上下跳的浮標。刻意不動 opacity：箭頭顏色本來就是
   foreground/.6，再疊透明度變化會在淺底上淡到看不見，而且週期性消失在
   Hero 上容易被誤認成閃爍的 bug。
   ⚠ 不加 will-change：這支箭頭是 1px 細線，強制提升合成層在這個站踩過
     「細線／遮罩邊緣出現次像素光暈」的坑（見 .cx-hero-mask 那批註解與
     memory mask-willchange-gpu-halo）。transform 動畫瀏覽器本來就會自己
     處理，不需要手動提示。 */
.page--wall .cx-hero-arrow--down {
    animation: cxHeroArrowNudge 2.6s var(--ease-out, ease-in-out) infinite;
}

@keyframes cxHeroArrowNudge {
    0%   { transform: translateY(0); }
    30%  { transform: translateY(min(9px, 22%)); }
    60%  { transform: translateY(0); }
    100% { transform: translateY(0); }
}

/* 使用者在系統層級關掉動態效果時就不要動（全站既有慣例，本檔已有十幾處
   同款區塊）。整條 animation 拿掉，箭頭回到原本的靜止外觀。 */
@media (prefers-reduced-motion: reduce) {
    .page--wall .cx-hero-arrow--down {
        animation: none;
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   Footer：服務專區／會員的子項改成收合（2026-08-27 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「footer 服務專區底下顧問案 ai分身不要展開改收合進去，會員也是，
   像導覽一樣」＋截圖（標題右邊一個 ⌄、子項收起來）。

   「像導覽一樣」＝header 抽屜（#wfNav）裡的「服務專區」「會員」那兩顆：
   標題後面掛 `wf_icon('chevron-down')`、子選單 `position: static`（就地展開
   不是浮出來）、預設 `display: none`。要收的剛好也是同樣那兩組，所以這裡
   沿用同一顆 chevron，機制用 Bootstrap 的 collapse——站上本來就載了
   `bootstrap.bundle`，不必為了這件事另外寫一支 JS，`aria-expanded` 也由
   它自己同步（CSP 沒有 unsafe-inline，能少寫一段 JS 就少一個風險點）。

   ⚠ **`display` 必須自己蓋回 flex**：`.footer-col-list` 是
     `display:flex; flex-direction:column`（本檔 ≈2759），但 Bootstrap 的
     `.collapse.show` 會把它設成 `display: block`——那是 (0,2,0)，比
     `.footer-col-list` 的 (0,1,0) 高，展開後清單會掉回 block 排版、
     `gap` 失效（間距整組不見）。這裡用三個 class 湊到 (0,3,0) 贏回來。
     收合中的 `.collapsing` 不設 display，吃得到原本的 flex，不用處理。
   ⚠ 收合狀態不必自己寫：`.collapse:not(.show){display:none}` 是 (0,2,0)，
     本來就贏得過 `.footer-col-list`，清單連同它的 margin 一起不占位。
   ⚠ 標題 <button> 保留 `.footer-col-title` 這個 class：字級／字重／顏色
     沿用原本那條，而且本檔 ≈6597／≈24064 有規則靠
     `:has(.footer-col-title)` 分辨「這欄有沒有標題」來決定清單的
     margin-top，換掉 class 會連帶把那兩條弄錯。
   ⚠ 按鈕的預設樣式要整組歸零（背景／邊框／內距／字型／對齊）——瀏覽器
     的 <button> 預設值跟 <p> 差很多，不歸零的話這一欄的標題會比隔壁欄
     的標題矮一截、左緣也對不齊。 */
.footer-col-toggle {
    display: inline-flex;
    align-items: center;
    gap: .5rem;
    padding: 0;
    border: 0;
    background: none;
    font: inherit;
    font-size: var(--text-body);
    line-height: calc(24 / 14);
    font-weight: var(--fw-normal);
    letter-spacing: var(--tracking);
    color: hsl(var(--heading));
    text-align: left;
    cursor: pointer;
    transition: color var(--transition);
}

/* 抽屜那顆 chevron 是 wf_icon 預設尺寸；這裡收小一階配 14px 的標題。 */
.footer-col-toggle .wf-icon {
    width: 14px;
    height: 14px;
    flex: none;
    transition: transform var(--transition);
}

/* 展開時箭頭轉向上，跟抽屜同一個語彙。
   ⚠ 認 aria-expanded 不認 .collapsed：aria-expanded 是 Bootstrap 一定會
     同步的那一個，.collapsed 只是它順手掛的樣式鉤子，兩者都在的時候
     以無障礙屬性為準比較不會漏掉狀態。 */
.footer-col-toggle[aria-expanded="true"] .wf-icon {
    transform: rotate(180deg);
}

@media (hover: hover) {
    .footer-col-toggle:hover {
        color: hsl(var(--accent-blue-text));
    }
}

.footer-col-toggle:focus-visible {
    outline: 2px solid hsl(var(--accent-blue));
    outline-offset: 4px;
}

.footer-col-list.collapse.show {
    display: flex;
}

/* ⚠ 手機（≤767.98）另外有一條把第 1／3 欄的清單改成橫向 wrap 的規則
   （`.footer-main > nav.footer-col:nth-of-type(1) .footer-col-list, …:nth-of-type(3) …`，
   特異度 (0,4,1)），它遠高於 Bootstrap 的 `.collapse:not(.show)` (0,2,0)
   ——實測 390 寬時收合狀態的清單 computed display 仍是 flex、根本沒收起來
   （桌機 768 以上正常，只有手機壞，是那條 nth-of-type 規則造成的）。
   而且它挑的 nth-of-type(1)/(3) 剛好就是這次要收合的那兩欄。
   這裡補一條 (0,5,1) 的收合狀態把它蓋回去。
   ⚠ 只蓋「收合時」：展開時仍然讓那條手機規則生效（橫向 wrap 是手機
     footer 既有的版面決定，不要因為加了收合就把它改成直排）。 */
.footer-main > nav.footer-col .footer-col-list.collapse:not(.show) {
    display: none;
}

/* ── 收合之後「服務專區／會員」離下一項太遠（2026-08-27 客戶回報）──────
   客戶：「服務專區跟會員底下跟實體課程距離太遠，請統一段落間距」。

   實測 1440：服務專區文字底 662.2 → 實體課程文字頂 715.1 ＝ **52.9px**，
   而實體課程 → 知識專區只有 **22.6px**，兩段節奏對不上。

   成因跟收合本身無關，是 grid 的列高被別欄撐開：
   footer 是四欄 grid，「服務專區／會員」與它們下方那兩組各佔第 1、2 列
   （≈24181 那四條 nth-of-type 指定），而第 4 欄的訂閱區
   `grid-row: 1 / span 2` 高 228px。橫跨兩列的項目會把自己的高度分攤給
   所跨的 auto 列，於是第 1 列被撐到 110.5px——原本這一欄有標題＋兩個
   子項（約 151px）撐得住，收合之後只剩標題 78px，**多出來的 32.5px 全
   變成標題下方的空白**（`align-items: start`，所以空白落在下面）。

   修法：`grid-template-rows: auto 1fr`。CSS Grid 規格（§12.5）明訂
   「橫跨到彈性軌道的項目，其貢獻不得用來增加所跨 intrinsic 軌道的基準
   大小」——第 1 列改回只由「真正屬於它的項目」決定（＝那兩欄自己的高度），
   訂閱區多出來的高度全部流進 `1fr` 的第 2 列。
   ⚠ 不用 `min-content`／`max-content`：那兩個仍然是 intrinsic 軌道，
     橫跨項目照樣會灌進來，解不掉。要的就是「有一條 flexible 軌道」。
   ⚠ 不動標記結構：手機版有一組 `nav.footer-col:nth-of-type(1)/(3)` 的
     規則靠 DOM 順序挑欄，把兩個 <nav> 併成一個容器會直接打壞它。
   ⚠ 只在 ≥768 的四欄版處理：≤767.98 是單欄堆疊，沒有跨列項目，本來就
     沒有這個問題。 */
@media (min-width: 768px) {
    .footer-main {
        grid-template-rows: auto 1fr;
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   品牌字標改用圖檔 ＋ logo 圖示顏色統一（2026-08-28 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「現在導覽左上跟 footer 是純文字一天聽一點，請替換成圖片
   brand-name.svg，footer 的 logo 顏色請跟導覽的 logo 顏色一樣，開場動畫
   的文字也替換成該圖片，logo 顏色也改跟導覽的顏色一致」。

   ── ① 字標圖片的尺寸 ─────────────────────────────────────────────────
   三處的 <img> 都沿用原本那顆文字的 class（.site-logo-text／
   .footer-logo-text／.hero-opening-brand-text），**既有的十幾條斷點規則
   一條都不用改**——它們管的 display（992–1467 藏、≥1468 顯示…）照樣命中，
   而它們管的 font-size 正好變成「圖片要多大」的來源，因為這裡用的是
   **em**：高度 = 該處 font-size × 1.1，寬度按 SVG 的 viewBox 比例
   （1084.75 : 212.95 ＝ 5.094 : 1）自動換算。
   實測三處的 font-size：header 16px、footer 16px、開場
   clamp(1.25rem, 2vw, 1.75rem)（20–28px）——所以開場那顆本來就會跟著
   視窗寬度縮放，改成圖片之後這個行為原封不動。
   ⚠ 為什麼不寫死 px：寫死就得為三個位置 ×（字級 A/A+/A++）×（各斷點）
     各補一份，這個站已經因為「延伸斷點漏一層」出過好幾次包。em 讓既有
     規則繼續當單一來源。

   ── ② logo 圖示顏色統一 ──────────────────────────────────────────────
   實測改動前的三顆圖示顏色：
     header  rgba(26,26,26,.7)   ← 客戶要的基準
     footer  rgb(26,26,26)       （≈24042 那條 hsl(var(--heading)) 全黑）
     開場    #4a4636             （根本沒有 CSS 規則，吃 SVG 屬性上的褐黑）
   統一成 header 那個值。footer 那條的特異度是 (0,1,1)，這裡同特異度、
   位置在後所以贏；開場那顆是 SVG 的 fill 屬性，任何 CSS 規則都蓋得過，
   不必動標記。
   ⚠ 客戶提供的 brand-name.svg 內建色是 #5c5c5c，而 rgba(26,26,26,.7)
     壓在白底上合成出來是 #5f5f5f——兩者差 3 階、肉眼看不出來，所以
     字標（圖檔色）與圖示（CSS 色）自然就一致，不需要為了配色去改圖檔。
     日後若要換字標顏色，改 SVG 檔本身即可（<img> 的顏色不受 CSS 控制，
     這是用圖檔換掉文字的必然代價，換成 mask-image 才能由 CSS 控色，
     但那會多一層 CSP／Safari 前綴的風險，現階段不值得）。 */
.brand-name-img {
    display: block;
    width: auto;
    height: 1.1em;
    flex-shrink: 0;
}

.footer-logo-icon path,
.hero-opening-brand-icon path {
    fill: hsl(var(--heading) / .7);
}

/* ═══════════════════════════════════════════════════════════════════════
   品牌字標放大＋footer 三欄齊底（2026-08-28 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「調整字間距拉大跟訂閱輸入欄齊底，讓整體比重一致，並將導覽的
   品牌名圖跟 footer 的都稍微放大，現在看起來 logo 比品牌名大很多」。

   ── ① 品牌字標放大 ─────────────────────────────────────────────────
   實測「字標高 ÷ 圖示高」：
       header 1440   17.6 / 24 ＝ 0.73
       header 1920   17.6 / 28 ＝ 0.63
       footer        17.6 / 34 ＝ 0.52   ← 落差最大，客戶說的就是這裡
   字標是 `height: 1.1em`（em 來自各處的 font-size，三處都是 16px，所以
   實際高度**固定** 17.6px），而圖示是各斷點寫死的 px（header 24／28、
   footer 34）——一個不動、一個會變，比例自然愈拉愈開。
   ⚠ 不改成「跟著圖示的 px 走」：那要為三個位置 x 各斷點各補一份，正是
     上一位作者用 em 想避開的維護成本（他的註解寫得很清楚）。這裡維持
     em、只調倍率，並替 footer 單獨加一階（它的圖示大 42%）。
   改後比例：header 0.93（1440）／0.80（1920）、footer 0.85——三處都落在
   0.8–0.93，比原本的 0.52–0.73 一致得多。

   ── ② footer 三欄齊底 ───────────────────────────────────────────────
   實測 1200／1440／1920：導覽欄最後一項底比訂閱輸入欄底**高 66.6px**
   （常數，不隨寬度變動），所以左邊兩欄看起來「上重下空」。
   把「服務專區→實體課程→知識專區」之間的兩段間距一起拉大，讓最後一項
   落在輸入欄底線上；兩段用同一顆變數，維持上一輪剛統一好的等距。
   ⚠ **只在 ≥1200 生效**：實測 768–1199 的訂閱欄是 `grid-row: 3 /
     grid-column: 1 / -1`（自己獨佔一整列、橫跨全寬），跟左邊兩欄根本沒有
     「齊底」這回事（實測落差 294px，那是版面結構不同不是沒對齊）。
     在那個區間拉大間距只會平白多出一大塊空白。
   ⚠ 這個值是**解出來的**不是估的：欄 1 底 662.2、兩個項目各 25 高、
     目標底 814.5（＝輸入欄底 812 ＋ 行盒下緣 2.5），
     662.2 + 2G + 50 = 814.5 → G = 51.15 ≈ 3.2rem。
   ⚠ 日後訂閱欄的文案行數若變動，輸入欄的 y 會跟著移，這個值要重算——
     所以抽成變數並在這裡寫明算式，不要散落成兩個魔術數字。 */
.brand-name-img {
    height: 1.8em;
}

.footer-logo-text.brand-name-img {
    height: 2.2em;
}

/* ⚠ 2026-08-28 客戶追加：「高度拉高回來，感覺畫質有點差，不要壓縮太多」
   ——上面兩個數字是這一輪從 1.4em／1.8em 再往上調的結果（原始值 1.1em）。

   **先查證過「畫質」不是檔案的問題**，免得下次有人又往壓縮的方向找：
   ・`image/brand-name.svg` 是 Illustrator 匯出的**純向量**，18 條 path、
     全檔沒有一個 base64／`data:image`，也沒有內嵌點陣圖——放到 2400px 寬
     渲染出來輪廓完全平滑，沒有鋸齒也沒有壓縮痕跡。
   ・用 `getBBox()` 逐 path 量過，**墨水剛好填滿 viewBox 100%**
     （0,0 1084.75x212.95），四邊留白都是 0——不是「圖被大量留白稀釋、
     所以看起來小」那種情況。
   ・渲染出來的長寬比 5.089／5.094，跟 viewBox 的 5.094 一致，
     **沒有被壓扁**（`width:auto` 有正常生效）。
   結論：看起來「糊」純粹是**尺寸太小**——手寫字標的細筆畫在 22px 高度下
   只有 1px 上下，抗鋸齒一攤開就顯得薄而灰。唯一的旋鈕就是高度，不是畫質
   參數，也沒有東西可以「少壓縮一點」。 */

@media (min-width: 1024px) {
    .footer-main {
        --footer-col-rhythm: 3.2rem;

        row-gap: var(--footer-col-rhythm);
    }

    .footer-main > .footer-col:has(.footer-col-title) .footer-col-list,
    .footer-main > .footer-col:not(:has(.footer-col-title)) .footer-col-list {
        gap: var(--footer-col-rhythm);
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   抽屜品牌字標補換圖 ＋ footer 展開後的段落間距對齊（2026-08-28 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶（附兩張截圖）：「展開的文字段落比重不一致調整，還有導覽展開的品牌名
   沒更換到圖片」。

   ── ① 抽屜的 logo 圖示顏色 ──────────────────────────────────────────
   同一天稍早把 header／footer／開場三處的字標換成圖檔、圖示顏色統一成
   header 的黑 70% 時，**漏掉了漢堡抽屜自己那一顆**（.site-nav-logo）。
   標記已補（見 _header.php），這裡補上它的圖示顏色。
   ⚠ 本檔 ≈2646 那段註解寫著「**刻意不含** .site-nav-logo-icon」——那是因為
     當時那條規則是把圖示轉成**白色**，而抽屜背景是白的、白圖示會直接消失。
     現在統一的目標色是黑 70%，壓在白色抽屜上完全沒問題，所以這一輪要把它
     納進來，不是推翻當時的判斷，是前提換了（白 → 黑 70%）。

   ── ② footer 展開後「標題→第一項」比「項目→項目」窄一半 ─────────────
   實測（1440，展開後，單位 px）：
       標題「服務專區」底 → 第 1 項「顧問案」頂    21
       第 1 項底           → 第 2 項「AI 分身」頂  56
   兩者差 2.7 倍，客戶說的「比重不一致」就是這個。

   成因很單純：這兩個間距本來是同一個值（`.footer-col:has(.footer-col-title)
   .footer-col-list` 同時給了 `margin-top: 1.1rem` 與 `gap: 1.1rem`，見本檔
   ≈24090），但「footer 三欄齊底」那一輪只在 ≥1200 把 **gap** 改成
   `var(--footer-col-rhythm)`（3.2rem），**margin-top 沒有跟著改**——一個
   跟著新節奏、一個留在舊值，收合狀態看不出來（清單 display:none、margin
   不生效），一展開就露餡。

   ⚠ 所以這裡只補 margin-top，值直接讀同一顆 `--footer-col-rhythm`：兩個
     間距回到「同一個來源」，日後訂閱欄文案行數變動、那顆變數被重算時，
     這裡會自己跟上，不會再次走偏。
   ⚠ 一樣只在 ≥1200 生效：那顆變數就只定義在該區間（<1200 的 footer 是
     單欄堆疊，沒有齊底這回事），跟著它的範圍走。
   ⚠ 特異度：既有那條是 (0,3,0)，這裡多一層 `.footer-main >` 變 (0,4,0)，
     不靠檔案順序取勝，就算日後有人把這段搬走也還是贏。
   ⚠ 掛在 `.footer-col-list` 而不是 `.collapse.show`：收合時 Bootstrap 給
     `display:none`，margin 天生不生效；展開動畫中途的 `.collapsing` 也吃得到
     同一個值，高度過場才不會在最後一刻跳一下。 */
.site-nav-logo-icon path {
    fill: hsl(var(--heading) / .7);
}

/* ⚠ `+ 2px` 是量出來的、不是湊的：兩個間距的「兩端」結構不同——項目與項目
   之間是 <li> 對 <li>（li 的行盒比裡面的 <a> 高，上下各多分掉一半的行距），
   標題與第一項之間是 <p> 對 <li>（只吃到下半邊那一份）。用
   Range().getClientRects() 量**文字**（不是量框，見 memory
   boxtop-vs-text-range-alignment）實測：只給 rhythm 時是 54.2 / 56.2，
   補 2px 之後四欄全部 56.2，完全齊一。 */
@media (min-width: 1024px) {
    .footer-main > .footer-col:has(.footer-col-title) .footer-col-list {
        margin-top: calc(var(--footer-col-rhythm) + 2px);
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   實體課程總頁面 Hero：三格文案改寫後的斷行保險（2026-08-28 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶這輪只改文案（intro 小字／statement 大標＋小字／void4 小字，四句都在
   courses_list.php，各自留了舊文案備查），版面與樣式一律不動——唯一的例外
   是這條，因為新大標比舊的長，手機會出現「孤字」。

   ── 為什麼需要 ────────────────────────────────────────────────────────
   大標從「實體課程」（4 字）改成「課程與工作坊」（6 字）。≤1199.98 是 2 欄
   版面，statement 這格只有半個視窗寬：390 實測內容寬 147px、字級 28px，
   一行放得下 5.25 字，6 字必定斷行，預設斷法是「課程與工作／坊」——末行
   只剩一個「坊」，視覺上像沒排完。

   ── 為什麼用 text-wrap: balance ───────────────────────────────────────
   它讓瀏覽器把 6 個字平均分成 3／3（「課程與」／「工作坊」），剛好也是這句
   話語意上的自然斷點。不用改字級、不用加斷點、不用在標記裡插 <wbr>——
   純樣式層解決，文案之後再換也還是自動平衡。
   ⚠ 這條在桌機是 no-op：≥1200 時 statement 橫跨 2 欄（1920 實測 960px），
     6 個字 168px 本來就一行放得下，balance 對單行文字不做任何事。
   ⚠ 全站已經有一處在用同一招（本檔 ≈28320），不是為這裡新引進的語法；
     舊瀏覽器不支援時會安靜退回預設斷法，不會壞版。
   ⚠ 次標不跟著用 balance，用的是下面的 pretty，理由見下一條。 */
.page--wall .cx-hero-title {
    text-wrap: balance;
}

/* 次標「從知道走到做到，把方法練成自己的能力」（13 → 18 字）同樣變長，但
   它的問題點跟大標不同，所以用不同的值：

   ── 症狀 ──────────────────────────────────────────────────────────────
   600px 實測斷成「從知道走到做到，把方法練成自己的能」／「力」——末行一個字，
   而且是把「能力」這個詞從中間剖開。其餘寬度（768↑ 一行、480/430/390/360/320
   都是自然兩行）本來就沒問題。

   ── 為什麼是 pretty 不是 balance ──────────────────────────────────────
   balance 追求「每行等長」，套在這句會變成 9／9（「…把」／「方法…」），反而
   把「把方法」拆開，比預設還差——實測過才排除的，不是憑感覺選。
   pretty 只做一件事：避免末行過短，前面幾行維持正常貪婪斷行。實測 600px
   改成「…練成自己的」／「能力」，「能力」完整保留在一起，其餘寬度**一個字都
   沒有動**（768/480/430/390/360/320 逐一比對過斷行位置完全相同）。
   ⚠ 不支援 pretty 的瀏覽器會安靜退回預設斷法，跟這條規則不存在時一樣。 */
.page--wall .cx-hero-lead {
    text-wrap: pretty;
}

/* 放大字級（A+／A++）時 intro 小字會被 2 行上限截斷 ─────────────────────
   新文案 33 字比舊的 29 字長，A+ @1200 與 A++ @1440 實測 `scrollHeight`
   超出 `clientHeight` 35／41px——也就是第 3 行被 `-webkit-line-clamp: 2`
   （本檔 ≈18131）切掉、字尾補上「…」。客戶指定的整句話讀不完，不能留。

   ⚠ 這是**新文案造成的**，不是既有問題：同一個頁面載入內把四句文案用 DOM
     換回舊版再量一次（A/B 同條件對照），舊文案在同樣的寬度／字級是 0 截斷。
     順帶確認 void4 格與 statement 格在 A+／A++ 的溢出（26～98px）新舊**完全
     相同**，那是既有的、跟這次文案無關，這裡不順手動它。

   ── 為什麼放寬到 3 行是安全的 ─────────────────────────────────────────
   那條 2 行上限的用意是「別讓文字把箭頭擠出格外被 overflow:hidden 切掉」。
   但 intro 這格的文字群組帶著 `margin-top: -147px`（被拉到 header 那一列
   去），箭頭則靠 `space-between` 貼在 cell 底部——兩者之間實測還有：
       A+  @1200 → 192px      A++ @1440 → 120px
       A++ @1200 → 145px      A++ @1920 → 196px
   而多一行只需要 35～41px。放寬到 3 行之後最緊的一格也還剩 79px，箭頭
   不會被擠出去。**這些是量出來的，不是照公式推的**（本檔 ≈18280 那條註解
   也要求這一帶要實測，不能只憑算式）。

   ⚠ 只鎖 intro 這一格：`.cx-hero-intro-desc` 是 intro／void4 共用 class
     （本檔已經因為這件事踩過坑），void4 那份是標題級大字、只有 1 行，
     不需要也不應該跟著放寬。
   ⚠ 只在 ≥1200 生效：≤1199.98 intro 整格 `display:none`（手機版只留四塊
     矩形），768~899.98 還另外壓成 1 行——那兩段都不在這條的範圍內，原本的
     保險一條都沒有被拆掉。 */
@media (min-width: 1024px) {
    .page--wall .cx-hero-cell--intro .cx-hero-intro-desc {
        -webkit-line-clamp: 3;
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   會員登入頁體檢後續（2026-08-28 客戶指定 1–6 ＋ 追加第 7 項）
   ═══════════════════════════════════════════════════════════════════════
   1／2／7 的標記改動在 member_login.php（包 <form>、送出鈕改 <button
   type="submit">、「忘記密碼？」搬到按鈕列）；3 是補 1600 這一階衍生檔
   （.agent/build-images.js 的 WIDTHS ＋ 三個 wf_pic() 呼叫端）。
   這裡處理 4／5／6 與第 7 項的排列。

   ── ④ 圖片欄底部沒貼齊右欄（實測 1440：圖片下緣 684、區塊下緣 705）──
   成因：桌機那條 `.mem-split-media { margin-top: calc(-1 * var(--header-h));
   max-height: 76svh }`——圖片被負 margin 往上拉了一個 header 高，**但
   max-height 沒有跟著加回那一段**，所以它最多只能長到 76svh（900 高時
   ＝684），距離區塊底 705 剛好差一個 21px 的白邊。
   ⚠ 上限本身要留著（原本的用意是「不要在高視窗長到太誇張」），只是把被
     拉走的 header 高補回去：格子的 min-height 本來就是
     `calc(76svh - var(--header-h))`，兩邊加回同一個量之後就自洽了——
     內容短時圖片仍然剛好 76svh，內容變長時才跟著長，不會再露白邊。

   ── ⑤ 輸入框 515px 太寬 ─────────────────────────────────────────────
   一條底線橫跨半個螢幕，從標籤走到輸入起點的視線距離太長。收到 30rem
   （480px，扣掉 .form-panel 自己的內距後輸入框約 394px，落在登入表單常見
   的 360–420 區間）。
   ⚠ 收的是 .form-panel 不是 .form-input：這樣輸入框、按鈕列（含右端的
     「忘記密碼？」）會共用同一條右邊界，不會出現「輸入框收窄了、按鈕列
     卻還是滿寬」的新落差。
   ⚠ 只在 .mem-split-panel 底下收：忘記密碼／完成頁那幾頁的 .form-panel
     沒有這層殼，維持原本寬度。

   ── ⑦ 按鈕列：登入靠左、忘記密碼放到原本按鈕的位置 ───────────────────
   客戶：「忘記密碼放登入按鍵位置，登入按鍵放左」。原本這裡是
   `.mem-split-panel .form-actions { justify-content: flex-end }`（2026-08-19
   「登入按鈕不要拉寬，放右邊」那輪加的）——**那條沒有刪**，這裡用新的
   修飾 class 疊在後面，只有掛了 --split 的這一列改成兩端對齊；同樣掛
   .form-actions 的資料填寫／服務聲明兩頁不受影響。
   ⚠ align-items: center：左邊是 48px 高的膠囊、右邊是一行小字，不對齊
     中線的話小字會貼在按鈕頂端。

   ── ⑥ 右欄重心 ─────────────────────────────────────────────────────
   實測 .mem-split-panel 本來就有 justify-content: center，上下留白
   （約 100px / 93px）其實是平衡的——看起來「上重下空」的真正原因是
   ⑤ 的超寬輸入框把視覺重量全壓在上半部。收窄之後重心自然回正，所以
   這一項**沒有另外加規則**，改完用量測確認即可。 */
/* ⚠ 2026-08-28 補正：這條 ④ 原本**沒有限定頁面**，`.mem-split-*` 是三頁
   共用的，所以連帶把服務聲明／資料填寫兩頁的圖片上限從 76svh（900 高時
   684）拉到 740——那兩頁沒有白邊問題（長內容、圖片是被 stretch 拉著長的），
   等於是無謂的副作用，而且違反上面 76svh 那段註解寫的「兩個數字刻意同為
   76svh，三頁的圖片視覺高度一致」。加上 .page--member 前綴收斂回只影響
   登入頁；那兩頁回到原本的 76svh。
   ⚠ 登入頁最終吃的是下面 ⑧ 那條 100svh（同特異度、位置在後贏），這條
   對登入頁其實已經不再是生效值，留著是因為它記錄了 ④ 的成因與算式。 */
@media (min-width: 992px) {
    .page--member.mem-split .mem-split-media {
        max-height: calc(76svh + var(--header-h));
    }
}

/* ⚠ 2026-08-28 客戶追加，同一頁再修兩件事：
     「頁面高度增高，現在不滾動就會看到 footer，請增高到不要露出 footer」
     「右邊欄位不要縮窄，改回原本寬度」

   ── ⑤ 撤回：輸入框收窄 ─────────────────────────────────────────────
   上面 ⑤ 那條 `.mem-split-panel .form-panel { max-width: 30rem }` 已經拿掉，
   欄位回到原本的滿欄寬（1440 實測輸入框 515px）。整段說明**留著不刪**，
   記錄這一輪試過又被否決的方向；⑥ 的結論（重心本來就對稱、看起來偏上是
   被寬輸入框帶的）也因此連帶失效，右欄目前就是原本的視覺重量。
   ⚠ 按鈕列的 --split（⑦）不受影響：它是兩端對齊，欄寬變寬只是讓
     「忘記密碼？」退回欄位的右邊界，跟輸入框右緣仍然對齊。

   ── ⑧ 高度撐滿一屏，不露出 footer ──────────────────────────────────
   現況：`.mem-split-grid { min-height: calc(76svh - var(--header-h)) }`
   （2026-08-28 稍早另一輪從 100svh 改成 76svh 的），1440x900 實測區塊下緣
   落在 705——距離視窗底還有 195px，footer 直接露在第一屏。
   改回「扣掉 header 之後填滿整個小視窗高」：區塊上緣在 header 底下
   （y=56），高度 100svh − header-h，下緣剛好落在 100svh，footer 從第二屏
   才開始。圖片欄的上限同步改成 100svh（它被負 margin 拉到視窗最頂，
   要能長到 100svh 才填得滿到區塊底，不然又會出現 ④ 那種白邊）。

   ⚠ **只鎖登入頁**：`.mem-split-*` 這一整套是三頁共用（登入／服務聲明／
     資料填寫），但只有登入頁的 section 掛了 `.page--member`
     （register／declaration 只有 `site-section section-bleed mem-split`，
     grep 確認過）。用 `.page--member.mem-split` 前綴湊到 (0,3,0) 蓋過
     原本的 (0,1,0)，另外兩頁一個像素都不動——**那兩頁是長內容、本來就
     超過一屏、不會露出 footer**，而且上面那條 76svh 的註解寫明「兩個
     數字刻意同為 76svh，三頁的圖片視覺高度一致」，全域改會把它們的圖
     從 684 拉到 900，不是這次要的。 */
.mem-split-panel .form-actions--split {
    justify-content: space-between;
    align-items: center;
    gap: 1.25rem;
}

@media (min-width: 992px) {
    .page--member.mem-split .mem-split-grid {
        min-height: calc(100svh - var(--header-h));
    }

    .page--member.mem-split .mem-split-media {
        max-height: 100svh;
    }
}

/* ⚠ 2026-08-28 客戶追加：「註冊頁請一同增高」——把上面 ⑧ 的處理套到註冊
   流程另外兩頁（服務聲明 /member/register、資料填寫 /member/register/form）。

   ⚠ 上面 ⑧ 註解裡那句「那兩頁是長內容、本來就超過一屏、不會露出 footer」
     **是錯的，這輪實測推翻**——它只在 900 高左右的視窗成立。那兩頁的內容
     高度是固定的（服務聲明 982px、資料填寫 1013px，跟視窗高無關），視窗一
     高過去 footer 就跑進第一屏：實測 1920x1080 各露 38／6px、1440x1200 各露
     162／131px，而客戶自己的視窗是 1475x1533（第十次那節從截圖長寬比反推出
     來的），會露出約 464px 的 footer。
     → 教訓：「內容比視窗高」這種話要標明是**在哪個視窗高度下**觀察的，
       否則它會變成一句在別的視窗直接失效的結論。

   做法：**不加頁面前綴**，讓三頁共用的 .mem-split-grid 一起吃
   100svh − header（等於把同日稍早那輪從 100svh 改成 76svh 的 min-height
   還原回去）。上面 ⑧ 那條 `.page--member.mem-split .mem-split-grid` 因此
   變成同值的冗餘覆寫，**留著不刪**：它記錄了登入頁那次的成因與算式。

   ⚠ **圖片的 76svh 上限維持不動**（≈13593 那條）：那是同一天客戶「圖片高度
     縮矮一點現在太高」指定的，跟這輪的「頁面增高」是兩件獨立的事。長內容
     的兩頁圖片高度本來就跟 min-height 無關（grid 是 align-items:stretch、
     被 max-height 收住），所以這輪對圖片零影響——實測 900／1080／1200 三種
     視窗高，圖片仍是 684／821／912，跟改動前一模一樣。
   ⚠ 代價（已量測、刻意接受）：圖片底緣到 footer 之間的留白會跟著變長，
     1440x1200 實測 157 → 288px。要消掉這塊白只能讓圖片跟著長高，那會直接
     撞到客戶當天指定的 76svh，因此不自作主張，改成回報給客戶決定。 */
@media (min-width: 992px) {
    .mem-split-grid {
        min-height: calc(100svh - var(--header-h));
    }
}

/* ⚠ 2026-08-28 客戶回覆上面那個取捨：「註冊圖片沒拉高」（附截圖，圖片在
   76% 高度就結束、下面一塊白到 footer）——**客戶選擇讓圖片跟著填滿**，
   把上面那條刻意不動的 76svh 上限解掉。

   ⚠ 這是同一天內對圖片高度的**第二次反向決定**，兩次都是客戶自己指定的，
     不是誰改錯：
       ① 早上「圖片高度縮矮一點現在太高」→ 新增 max-height: 76svh（≈13593）
       ② 現在「註冊圖片沒拉高」→ 註冊兩頁解除上限
     ① 那條**沒有刪**，仍是三頁共用的預設值；這裡只是在後面覆寫掉它。
     日後若客戶又說「圖片太高」，先確認是**哪一頁**——登入頁跟註冊兩頁現在
     走不同的高度邏輯了，不能再假設三頁一致。

   ⚠ 用 `max-height: none` 而不是抄登入頁的 `100svh`：兩者只在**內容比一屏
     還高**的視窗才有差，而註冊兩頁正好就是那種頁（服務聲明 982px、資料填寫
     1013px）。1440x900 實測 grid 是 982，寫 100svh 的話圖片只到 900、底下會
     留 82px 白（剛好落在第一屏折線下方，捲一點點就看得到）；寫 none 讓圖片
     直接吃滿 grid 的高度，任何視窗都零留白，才是客戶要的「拉高」。
     登入頁不受影響：它的 (0,3,0) 覆寫（上面 ⑧）贏過這條 (0,1,0)，仍是
     100svh——而它的 grid 本來就剛好是一屏，兩種寫法對它同義。

   ⚠ 992–1199.98 的堆疊版型不受影響：那個範圍的圖片高度是由
     `min-height: clamp(220px, 52vw, 340px)` 決定的（實測 1100x1200 是 346px，
     離 912 的上限差很遠），上限本來就沒生效，解掉等於沒動。 */
@media (min-width: 992px) {
    .mem-split-media {
        max-height: none;
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   全站按鈕系統（2026-08-28 客戶指定，附 Pinterest 元件規範參考圖）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「現在全站的按鈕樣式顏色跟樣式上沒統一，邏輯不太一致，包含標籤跟
   分類，請根據圖片得按鈕樣式邏輯和配色變化，套用跟取代現在全站按鈕樣式，
   按鈕現在還有一個問題太高了看起來不精簡」＋一張 Buttons 元件規範圖
   （Button Scale 32/42/52/52 ＋ Basic States 表：Solid／Outline／Text／
   Right icon／Left Icon × Default／Hover／Pressed／Disabled）。

   ── 動手前實測到的現況（1440 寬掃 11 頁，逐一量 computed style）────────
   .btn-pill  → 4 種長相：
       48px 藍底黑字 fw450 ×16（正常）
       48px 藍底黑字 fw200 ×1  ← 首頁漏網，見下面「字重寫死」
       48px 透明＋slate 框 ×7  ← 其實就是 Outline，只是靠情境選中沒有名字
       14px 無框無內距 slate 字 ×8 ← 其實就是 Text（2026-08-27 客戶認可的
                                     收藏／分享降級），同樣沒有名字
   .tag-pill  → **9 種長相**：高度 28／32／38／40 四種、填色 透明／NUDE／
       AERIAL 三種、框線 slate／rgba(26,26,26,.5)／無 三種、字級 12／14 兩種。

   ── 根因 ───────────────────────────────────────────────────────────────
   .btn-pill 在本檔有 **40 條規則、其中 4 份是完整的基底重宣告**
   （≈L258 原始定義、≈L4164、≈L5193、≈L7099），是七月到八月每一輪客戶
   回饋各自往後追加疊出來的；.tag-pill 同樣有 27 條。所以「不一致」不是
   哪一頁寫錯，是同一顆元件被重新定義了四次。

   ── 這個區段的做法 ─────────────────────────────────────────────────────
   放在檔尾＝同特異度時**順序最後者贏**，用一份宣告收斂上面那些疊層，
   不刪除任何既有規則（守則 2）。既有那些 (0,2,0) 以上的情境選擇器
   （.on-dark／.course-section／.entry-links／.form-actions）在下面逐一
   列出來一起接管，否則它們會繼續各走各的。

   ── 三個判斷（客戶可以推翻，改一行就換掉）──────────────────────────────
   ① **配色沿用站上既有色票，不照抄參考圖的藍白。** 參考圖那顆是泛用 UI 藍
      ＋白字；站上主按鈕是 AERIAL 藍(#B0C7DD)＋**黑字**，而且「.btn-pill--solid
      永遠不反白」是客戶明確定案過的（見 memory btn-pill-solid-never-invert）。
      客戶要的是「按鈕樣式**邏輯**和配色**變化**」＝那張表的變體矩陣與四段
      狀態階梯，不是把站上換成另一種藍。
   ② **Pressed 不做成參考圖的近黑底。** 參考圖 Pressed 是深藍近黑＋白字，
      直接照抄就會撞到①那條「不反白」定案。這裡改成**同色相壓深**
      （AERIAL 77.8% → 64%），一樣維持「Default → Hover → Pressed 逐段
      加深、Pressed 最重」的階梯邏輯，黑字對比仍有 6:1 以上。
      要真的近黑：把 --btn-press 改成 hsl(var(--foreground)) 並補文字色即可。
   ③ **預設高度 48px → 40px。** 參考圖的三階是 32/42/52，站上原本只有
      48 一種。對應成 sm 32／md 40（預設）／lg 48：md 取 40 而不是 42 是
      為了落在站上其他元件的 8px 節奏上；lg 保留 48＝原本的高度，需要大顆
      CTA 的地方仍然叫得出來。連帶把 gap 1.75rem 收到 1.25rem——客戶說
      「看起來不精簡」除了高度，字與右側徽章之間那段空距也是主因。

   ── 沒有納入的（刻意）─────────────────────────────────────────────────
   ・A／A+／A++ 字級切換（.font-size-btn）：那是閱讀輔助控制項不是按鈕，
     客戶這輪講的是「按鈕、標籤、分類」，沒點名它。
   ・註冊步驟指示（.stepper）：狀態指示器，不可點。
   ・知識專區左欄的分類清單（.page--knowledge .kz-tags .filter-tags／
     .tag-pill）：那是 2026-08-18 第三輪定案的**直式帶底線清單列**（實測
     60px 高、display:block、border-bottom），不是 chip，套 chip 尺寸會把
     那個版型拆掉。它的選擇器是 (0,3,0) 以上，本區段的 (0,1,0)/(0,2,0)
     本來就打不過它——這是刻意留的，不是漏掉。
   ・.auth-switch（會員登入／註冊切換）：同一天稍早才依客戶參考圖定案。
   ═══════════════════════════════════════════════════════════════════════ */

:root {
    /* 尺寸階梯。2026-08-28 第二輪：客戶指定「按鈕都改成首頁服務專區顧問案
       底下那顆的樣式」，md 直接對齊那顆的實測值（padding .875rem 1.25rem
       ＋ 12px/1.7 文字 ＝ 48.4px），sm／lg 按同一比例上下各一階。
       ⚠ 這一輪把 md 從 40px 調回 48px：上一輪客戶說「太高了看起來不精簡」，
         但這次客戶親自指名的那顆基準按鈕實測就是 48.4px——它看起來精簡是
         因為**字小（12px）、沒有方框徽章、間距只有 14px**，不是因為矮。
         照指名的基準走，高度以那顆為準。 */
    --btn-h-sm: 40px;
    --btn-h-md: 48px;
    --btn-h-lg: 56px;

    /* 狀態色階（對應參考圖 Basic States 的四列）。
       --btn-fill／--btn-fill-hover 是既有 token（由色塊自己宣告，見 ≈L7058），
       這裡只補上參考圖有、站上原本沒有的 Pressed 與 Disabled 兩段。 */
    --btn-press:      209.4 39.8% 64%;   /* AERIAL 壓深：Default 77.8% → 64% */
    --btn-disabled:   225 6% 87%;        /* ＝ --surface-grey */
    --btn-disabled-fg: 224.7 1.6% 49.4%; /* ＝ --slate */
}

/* ── 尺寸：預設 md（40px），另有 sm／lg ──────────────────────────────────
   ⚠ 用 min-height 不用 height：標籤長到換行時要能撐開，不能裁切。
   ⚠ 右內距刻意比左內距小很多：右邊那顆 ::after 徽章（靜置是小圓點、hover
     長成帶箭頭的方塊）本身就佔位，兩邊給一樣的內距右側會過空。
   ⚠ --pill-dot 是徽章靜置時的縮放比例，三階各自算過，讓靜置那顆點在三種
     尺寸下都是 4px 上下（badge × dot ≈ 4），不會小尺寸看不見、大尺寸變成
     一顆突兀的大點。 */
/* md ＝ 客戶指名的基準按鈕（.svc-feature-tail）的實測值，一個數字都沒有改：
       padding .875rem 1.25rem｜gap .875rem｜12px / 400 / .2em 字距 / 1.7 行高
       ｜無圓角｜箭頭 20px。sm／lg 只是同比例的上下一階。
   ⚠ line-height 一定要跟著給：基底 .btn-pill 是 line-height:1（≈L258，配合
     舊徽章做垂直置中用），只改 padding 的話內容高只有 12px、整顆會是 40px，
     跟基準那顆對不起來。基準那顆的文字是 12px×1.7＝20.4px 才湊出 48.4px。 */
/* ⚠ 內距各減 1px（.8125rem＝13px 而不是基準那顆的 14px）：基準那顆
     .svc-feature-tail 是 border:0，我們的 .btn-pill 有 1px 邊框（外框版要用，
     實心版則是把邊框設成跟底色同色）。照抄 14px 會變成 50.4px、比基準高 2px。
     內距 13px ＋ 1px 邊框 ＝ 視覺內縮 14px、外框總高 48.4px，跟基準逐像素相同。
     水平同理：19px ＋ 1px ＝ 20px。實心與外框兩版也因此等高。 */
.btn-pill {
    --btn-arrow: 20px;
    min-height: var(--btn-h-md);
    gap: .875rem;
    padding: .8125rem 1.1875rem;
    border-radius: 0;
    font-size: var(--text-meta);
    line-height: var(--lh-meta);
    letter-spacing: var(--tracking-meta);
}

.btn-pill--sm {
    --btn-arrow: 16px;
    min-height: var(--btn-h-sm);
    gap: .625rem;
    padding: .5625rem .9375rem;
}

.btn-pill--lg {
    --btn-arrow: 22px;
    min-height: var(--btn-h-lg);
    gap: 1.125rem;
    padding: 1.0625rem 1.5625rem;
}

/* 字重寫死不吃 token：--fw-normal 在 :root 是 200，但 .page／.site-header
   等 20 幾處把它局部覆寫成 450／350（見 memory fw-light-token-pollution），
   所以同一顆按鈕在首頁量到 200、其他頁量到 450。直接寫數字，讓它不再受
   任何祖先覆寫影響。
   ⚠ 2026-08-28 第二輪由 450 改成 400：客戶指名的基準按鈕實測就是 400
     （.svc-feature-cat），客戶也明說「不要原本的字大小」——字級與字重
     一起對齊那顆，不是只改字級。 */
.btn-pill,
.on-dark .btn-pill,
.course-section .btn-pill,
.entry-links .btn-pill {
    font-weight: 400;
}

/* ═══ 右側箭頭：方框徽章 → 常駐細箭頭 ═══════════════════════════════════
   客戶「不要原有的方框變箭頭」。原本 .btn-pill::after 是一顆 22px 方塊，
   靜置縮成 4px 小點、hover 才放大成帶「→」的方塊（見 ≈L258／L308 的
   --pill-badge／--pill-dot）。基準按鈕沒有這個機制，它就是一顆**一直都在
   的細箭頭**，hover 只往右滑 4px（實測 .svc-arrow 的 translateX(4px)）。

   ⚠ 為什麼用 mask 指向站內 SVG，而不是 content:"→" 或 data URI：
     ・content:"→" 實測比對過，Noto Sans TC 的箭頭字元明顯比站上那顆粗、
       也比較長，並排看得出不是同一個東西。
     ・data URI 走不通：站台 CSP 的 img-src 只有 'self'（沒有 data:），
       background-image／mask-image 吃 data URI 會被擋掉。
     ・所以另存一份 image/btn-arrow.svg（path 與 wf_icon('arrow-right')
       逐字相同：M5 12l14 0／M15 16l4 -4／M15 8l4 4，stroke-width 1.5、
       圓端圓角），用 mask 疊在 currentColor 上——形狀跟基準那顆一模一樣，
       而且會跟著文字色走（外框版、停用態都自動對）。
   ⚠ color 一定要明寫 inherit：舊的 hover 規則（≈L335／L5169／L7135／
     L13257）會把 ::after 的 color 換成 --background 近白（那是給舊徽章裡
     的「→」文字用的）。現在箭頭是 currentColor 的 mask，color 直接決定
     箭頭顏色，不蓋掉的話 hover 會變成白箭頭壓在膚色底上、幾乎看不見。
   ⚠ 選擇器把 (0,2,0)／(0,3,0) 的既有情境一起列出來：只寫 .btn-pill::after
     會被 .entry-links .btn-pill::after 那類規則打贏，外框按鈕的箭頭就會
     回到舊徽章的長相。 */
.btn-pill::after,
.btn-pill--solid::after,
.btn-pill--sm::after,
.entry-links .btn-pill::after,
.on-dark .btn-pill::after,
.course-section .btn-pill::after,
.btn-pill[data-fav="1"]::after,
.form-actions .btn-pill:not(.btn-pill--solid)::after {
    content: "";
    flex: 0 0 auto;
    width: var(--btn-arrow, 20px);
    height: var(--btn-arrow, 20px);
    border-radius: 0;
    background: currentColor;
    color: inherit;
    font-size: 0;
    transform: none;
    -webkit-mask: url("../../../image/btn-arrow.svg") no-repeat center / 100% 100%;
            mask: url("../../../image/btn-arrow.svg") no-repeat center / 100% 100%;
    transition: transform var(--dur-hover) var(--ease-out);
}

.btn-pill:hover::after,
.btn-pill:focus-visible::after,
.btn-pill--solid:hover::after,
.btn-pill--solid:focus-visible::after,
.btn-pill--sm:hover::after,
.btn-pill--sm:focus-visible::after,
.entry-links .btn-pill:hover::after,
.entry-links .btn-pill:focus-visible::after,
.on-dark .btn-pill:hover::after,
.on-dark .btn-pill:focus-visible::after,
.course-section .btn-pill:hover::after,
.course-section .btn-pill:focus-visible::after,
.form-actions .btn-pill:not(.btn-pill--solid):hover::after,
.form-actions .btn-pill:not(.btn-pill--solid):focus-visible::after {
    transform: translateX(4px);
    color: inherit;
    background: currentColor;
}

@media (prefers-reduced-motion: reduce) {
    .btn-pill::after { transition: none; }
    .btn-pill:hover::after,
    .btn-pill:focus-visible::after { transform: none; }
}

/* ═══ 變體 ①：Solid（主要動作）══════════════════════════════════════════
   AERIAL 藍底＋黑字。站上絕大多數按鈕都是這一顆。 */
/* ⚠ .entry-links .btn-pill--solid 要單獨列出來：上面那條 :not() 只是讓
   「我的」外框規則不命中它，但既有的 L≈13263 `.entry-links .btn-pill`
   仍然是 (0,2,0)，而 `.btn-pill--solid` 只有 (0,1,0)，光靠 :not() 打不過。
   補一條同為 (0,2,0)、位置在它後面的選擇器才真的贏得了（同分比順序）。 */
.btn-pill,
.btn-pill--solid,
.entry-links .btn-pill--solid,
.on-dark .btn-pill,
.course-section .btn-pill {
    background: var(--btn-fill);
    border-color: var(--btn-fill);
    color: hsl(var(--heading));
}

.btn-pill:hover,
.btn-pill:focus-visible,
.btn-pill--solid:hover,
.btn-pill--solid:focus-visible,
.entry-links .btn-pill--solid:hover,
.entry-links .btn-pill--solid:focus-visible,
.on-dark .btn-pill:hover,
.on-dark .btn-pill:focus-visible,
.course-section .btn-pill:hover,
.course-section .btn-pill:focus-visible {
    background: var(--btn-fill-hover);
    border-color: var(--btn-fill-hover);
    color: hsl(var(--heading));
}

/* Pressed：滑鼠按著／鍵盤 Enter 按下的瞬間。參考圖這一段是整組最深的一階。 */
.btn-pill:active,
.btn-pill--solid:active,
.entry-links .btn-pill--solid:active,
.on-dark .btn-pill:active,
.course-section .btn-pill:active {
    background: hsl(var(--btn-press));
    border-color: hsl(var(--btn-press));
    color: hsl(var(--heading));
}

/* ═══ 變體 ②：Outline（次要動作）════════════════════════════════════════
   透明底＋細框。站上原本就有這一層，只是靠 .entry-links／.form-actions
   這種「在哪個容器裡」來選中；這裡補一顆有名字的 .btn-pill--outline，
   新標記可以直接用，既有那三個情境選擇器一併列進來吃同一份定義。 */
/* ⚠ .entry-links 這一組補上 :not(.btn-pill--solid)——實測揪出的真 bug：
   service_page_simpla.php:157 的主要 CTA 標的是
   `class="btn-pill btn-pill--solid"`，但既有規則 `.entry-links .btn-pill`
   是 (0,2,0)、`.btn-pill--solid` 只有 (0,1,0)，所以那顆**標了實心卻被畫成
   外框**。那條規則當初寫在「次要動作：描邊版」段落，服務對象是頁尾的
   返回／收藏／分享，不是 CTA，只是選擇器沒排除 --solid 就順手蓋到了。
   旁邊的 .form-actions 那組早就寫了 :not(.btn-pill--solid)，這裡補齊成
   同一種寫法。客戶說的「邏輯不太一致」，這就是一個具體案例。
   ⚠ 會有視覺變化：顧問案／AI 分身兩頁頁尾那顆 CTA 由外框變成藍底實心。 */
.btn-pill--outline,
.entry-links .btn-pill:not(.btn-pill--solid),
.form-actions .btn-pill:not(.btn-pill--solid) {
    background: transparent;
    border-color: hsl(var(--slate));
    color: hsl(var(--heading));
}

.btn-pill--outline:hover,
.btn-pill--outline:focus-visible,
.entry-links .btn-pill:not(.btn-pill--solid):hover,
.entry-links .btn-pill:not(.btn-pill--solid):focus-visible,
.form-actions .btn-pill:not(.btn-pill--solid):hover,
.form-actions .btn-pill:not(.btn-pill--solid):focus-visible {
    background: var(--btn-fill-hover);
    border-color: var(--btn-fill-hover);
    color: hsl(var(--heading));
}

.btn-pill--outline:active,
.form-actions .btn-pill:not(.btn-pill--solid):active {
    background: var(--btn-fill);
    border-color: var(--btn-fill);
    color: hsl(var(--heading));
}

/* ═══ 變體 ③：Text（低權重動作）═════════════════════════════════════════
   無底無框，只有文字。站上既有的收藏／分享（≈L27808，2026-08-27 客戶
   認可「文字按鈕、小型線框按鈕、或低權重操作」時選的最輕那一種）就是
   這一層，那條規則特異度 (0,5,0) 比這裡高、會繼續生效，這裡補一顆有
   名字的版本讓其他地方也叫得出來，並補上它缺的 Pressed／Disabled。
   ⚠ hover 用灰階加深不用藍：沿用全站「有連結用藍／純文字用灰」的定案
     （見 memory hover-color-link-vs-plaintext），收藏／分享點了不離開頁面。 */
.btn-pill--text {
    min-height: auto;
    padding: 0;
    border: 0;
    gap: .375rem;
    background: transparent;
    color: hsl(var(--slate));
    font-size: var(--fs-2);
}

.btn-pill--text::after {
    display: none;
}

/* Text 變體的字級也跟著對齊基準按鈕（12px / .2em）。
   ⚠ 為什麼要特別寫一條：收藏／分享那組的既有規則
     `.page--article… .entry-actions .btn-pill { font-size: var(--fs-2) }`
     是 (0,5,0)，把字級鎖在 14px。上面把 .btn-pill 的字距改成 .2em 之後，
     那組會變成「14px 配 2.8px 字距」——比主按鈕還大、還鬆，等於在同一頁
     裡出現第二套按鈕字級，正是客戶這輪要消滅的東西。
   ⚠ 選擇器逐字對齊那四條 (0,5,0)，並放在檔尾靠順序贏；不動它們原本的
     顏色與「無框無底」定義（2026-08-27 客戶認可的低權重樣貌）。 */
.page--article.page--knowledge-article .entry-action-group .entry-actions .btn-pill,
.page--article.page--service-simpla .entry-action-group .entry-actions .btn-pill,
.page--content-generic .entry-action-group .entry-actions .btn-pill,
.course-detail--editorial .entry-action-group .entry-actions .btn-pill,
.btn-pill--text {
    font-size: var(--text-meta);
    line-height: var(--lh-meta);
    letter-spacing: var(--tracking-meta);
}

.btn-pill--text:hover,
.btn-pill--text:focus-visible {
    background: transparent;
    color: hsl(var(--heading));
}

.btn-pill--text:active,
.page--article.page--knowledge-article .entry-action-group .entry-actions .btn-pill:active,
.page--article.page--service-simpla .entry-action-group .entry-actions .btn-pill:active,
.page--content-generic .entry-action-group .entry-actions .btn-pill:active,
.course-detail--editorial .entry-action-group .entry-actions .btn-pill:active {
    color: hsl(var(--accent-blue-text));
}

/* ═══ 第四段狀態：Disabled ═══════════════════════════════════════════════
   參考圖四列裡站上完全沒有的一段。三種變體各給一份，另外把互動整個關掉
   （pointer-events）並拿掉 hover 的位移感。
   ⚠ 同時吃 [disabled]／[aria-disabled="true"]／.is-disabled 三種寫法：
     <button> 用原生 disabled、<a> 沒有 disabled 屬性只能用 aria／class，
     站上兩種標記都有（很多「按鈕」其實是 <a>，見各 view 的註解）。 */
.btn-pill[disabled],
.btn-pill[aria-disabled="true"],
.btn-pill.is-disabled {
    background: hsl(var(--btn-disabled));
    border-color: hsl(var(--btn-disabled));
    color: hsl(var(--btn-disabled-fg));
    pointer-events: none;
    cursor: default;
}

.btn-pill--outline[disabled],
.btn-pill--outline[aria-disabled="true"],
.btn-pill--outline.is-disabled {
    background: transparent;
    border-color: hsl(var(--btn-disabled));
    color: hsl(var(--btn-disabled-fg));
}

.btn-pill--text[disabled],
.btn-pill--text[aria-disabled="true"],
.btn-pill--text.is-disabled {
    background: transparent;
    border-color: transparent;
    color: hsl(var(--slate) / .45);
}

.btn-pill[disabled]::after,
.btn-pill[aria-disabled="true"]::after,
.btn-pill.is-disabled::after {
    background: hsl(var(--btn-disabled-fg));
}

/* ═══ 標籤／分類 chip ════════════════════════════════════════════════════
   實測 9 種長相收斂成 1 種。尺寸刻意對齊 .btn-pill--sm 的 32px——chip 與
   小按鈕同高，並排時才切得齊，這也是參考圖那張表的邏輯（同一套尺寸階梯
   服務所有元件，不是每個元件各自訂高度）。
   ⚠ 狀態階梯與按鈕同一套：靜置細框 → hover NUDE → 選中 AERIAL。
     選中態沿用站上既有的 .is-active（≈L11899／L13307 已經定義成 AERIAL），
     這裡只是把「靜置」與「hover」統一，不改選中的顏色語彙。
   ⚠ 字重 350 寫死（比按鈕的 450 輕一階）：chip 是標籤不是動作，視覺權重
     本來就該低一階；寫死的理由同上面按鈕那條。
   ⚠ 排除：.page--knowledge .kz-tags .tag-pill（左欄直式清單，(0,3,0) 特異度
     比這裡高，自動不受影響——刻意的，見本區段開頭）。 */
.tag-pill,
.kz-tags .tag-pill,
.pcard .tag-pill,
.filter-tags .tag-pill,
.kd-hero-tags .tag-pill,
.kd-related-tags .tag-pill,
.pcard-tags .tag-pill {
    min-height: var(--btn-h-sm);
    padding: 0 .875rem;
    border: 1px solid hsl(var(--foreground) / .28);
    border-radius: var(--radius-pill);
    background: transparent;
    color: hsl(var(--heading));
    font-size: var(--text-meta);
    font-weight: 350;
    letter-spacing: var(--tracking-meta);
}

.tag-pill:hover,
.tag-pill:focus-visible,
.kz-tags .tag-pill:hover,
.kz-tags .tag-pill:focus-visible,
.pcard .tag-pill:hover,
.pcard .tag-pill:focus-visible {
    background: var(--btn-fill-hover);
    border-color: var(--btn-fill-hover);
    color: hsl(var(--heading));
}

/* 熱門關鍵字（header 行內搜尋的建議標籤）：客戶 2026-08-26 明確指定
   「熱門關鍵字膚色背景不線框」，所以它靜置就是 NUDE 實心、沒有框——
   這是既有定案，不套上面的「靜置細框」。這裡只把**高度與字級**拉齊到
   同一套尺寸階梯（實測原本 27.6px，改成 32px 與 chip／小按鈕同高），
   顏色語彙一個字不動。 */
.site-inline-search-suggest-tag {
    min-height: var(--btn-h-sm);
    display: inline-flex;
    align-items: center;
    font-size: var(--text-meta);
    font-weight: 350;
}

/* ═══════════════════════════════════════════════════════════════════════
   footer 進入畫面時收起右下角浮動鈕（2026-08-28 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶 footer 檢視回報「浮動客服鈕跟 footer 撞在一起」，選定「footer 進入
   畫面時讓浮動鈕淡出」。狀態由 site.js 的 IntersectionObserver 掛在 <html>
   上（`.has-footer-in-view`，觸發時機與理由見那支的 ④）。

   實測撞法：`.wf-float` 是 fixed、離視窗右下角 28px（手機 16px），
   **捲到任何頁面最底部時 100% 疊在 footer 上**——桌機重疊 152px（三顆的
   整組高度）、手機 44px（橫排）。全站每一頁的結尾都會發生，不是特例。

   ── 三個屬性各自的職責，缺一不可 ──────────────────────────────────
   ・`opacity: 0`        視覺淡出（唯一有動畫的一項）
   ・`visibility: hidden` **把它移出 Tab 順序**。只做 opacity:0 的話按鈕
                          仍然吃得到鍵盤焦點，鍵盤使用者會 Tab 到三顆看不見
                          的按鈕，比撞在一起更糟。
   ・`pointer-events: none` 淡出過程中（opacity 還沒到 0、visibility 還沒
                          切換完）滑鼠仍點得到，會誤觸；先關掉最保險。
   ⚠ `visibility` 要一起寫進 transition 並給 `0s ... var(--transition)` 形式
     的延遲：不然它會在第 0 幀就跳成 hidden，opacity 的淡出根本看不到。
     反向（footer 離開畫面、按鈕回來）則要**立刻**變 visible，所以那一側
     不能有延遲——兩個方向的 transition 分開寫，這是 visibility 動畫的
     標準寫法，不是多此一舉。
   ⚠ 位移只給 8px：這是「退場」不是「表演」，跟站上其他 hover／進場動態
     同一個克制程度（見浮動鈕信封動態那一區的既有註解「這是提示，不是表演」）。

   ⚠ 不用 `display: none`：那會讓 transition 完全失效（客戶要的是「淡出」
     不是「消失」）。上面 ≈10377 `.has-menu-open .wf-float` 那條用
     display:none 是另一回事——選單開著時按鈕本來就不該存在，不需要過場。
     兩條規則互不衝突（display:none 贏過任何 opacity）。 */
.wf-float {
    transition:
        opacity var(--transition),
        transform var(--transition),
        visibility 0s;
}

.has-footer-in-view .wf-float {
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transform: translateY(8px);
    transition:
        opacity var(--transition),
        transform var(--transition),
        visibility 0s var(--transition);
}

/* 電子報面板開著時不收：使用者可能正在打 email，捲一下就被收走等於打到
   一半消失。面板的開合只有 `hidden` 屬性一個真相來源（site.js ② 負責），
   這裡直接讀它，不另外開一個狀態 class。
   ⚠ 特異度：`:has()` 以「參數裡最高的那一個」計分，這條 (0,3,0) 高於上面
     那條 (0,2,0)，靠特異度取勝不靠檔案順序（本專案踩過好幾次「以為順序
     會贏」的坑）。 */
.has-footer-in-view .wf-float:has(.wf-float-panel:not([hidden])) {
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
    transform: none;
}

/* 減少動態偏好：直接切換、不做淡出與位移。「不要動畫」不等於「不要這個
   行為」——按鈕一樣要在 footer 出現時讓開，只是不用過場。 */
@media (prefers-reduced-motion: reduce) {
    .wf-float,
    .has-footer-in-view .wf-float {
        transition: none;
    }

    .has-footer-in-view .wf-float {
        transform: none;
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   收藏／分享的 hover 提示泡泡：接上新的按鈕語彙（2026-08-28 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「收藏跟分享按了以後 hover 提示的按鈕樣式也要改」——同一輪「全站
   按鈕都改成首頁服務專區顧問案那顆」的延伸。那兩顆按鈕（.btn-pill--sm）
   已經跟著新系統走了，但它們上方那顆提示泡泡（.act-tip，≈28143）還是
   舊語彙：圓角 --radius-media、內距 .4rem/.7rem、字重吃 token、沒有高度
   基準——並排看得出是兩套東西。

   ⚠ 對齊的是**外框變體（Outline）**不是實心：
     ・收藏那顆提示是可點的連結（「可至會員 › 我的收藏查看」），做成藍色
       實心會跟旁邊的主要按鈕搶份量，而它只是輔助說明。
     ・分享那顆是狀態回報（「文章連結已複製 ✓」）根本不是動作。
     Outline 的既有定義是 `background: transparent／border-color:
     hsl(var(--slate))／color: hsl(var(--heading))`（見上面「變體②」）。
   ⚠ 底色**不跟 Outline 的 transparent**，維持既有的 `hsl(var(--background))`：
     泡泡是絕對定位浮在內容上方的，透明底會讓底下的文字透出來疊在一起
     （這正是 ≈28119 那條註解當初把它從深色晶片改成頁面底色的理由，前提
     沒有改變）。框線與字色則照 Outline 走。

   ── 對齊的四個項目（值全部取自新按鈕系統，沒有自己另訂）──────────────
   ① `border-radius: 0`      新按鈕系統是直角（`.btn-pill` ≈29214）
   ② 尺寸走 sm 那一階        它旁邊就是 `.btn-pill--sm`，同一階才對得起來：
                             `min-height: var(--btn-h-sm)`＋`padding:
                             .5625rem .9375rem`（≈29220 那條的原值照抄）
   ③ `line-height: var(--lh-meta)`  舊值寫死 1.5，新系統是 1.7
   ④ `font-weight: 400`      **寫死數字不吃 token**：`--fw-normal` 在 :root
                             是 200，但 `.page`／`.site-header` 等 20 幾處
                             把它局部覆寫成 450／350（見 memory
                             fw-light-token-pollution），吃 token 會讓同一顆
                             泡泡在不同頁量到不同字重。新按鈕系統 ≈29241
                             也是為了同一個理由寫死 400。
   ⚠ 字級／字距不用改：舊值本來就是 --text-meta／--tracking-meta，跟新系統
     相同（實測 12px／2.4px），這裡不重複宣告。
   ⚠ 用 inline-flex＋置中：`min-height` 對純 block 元素只會撐高、文字仍貼
     頂，要 flex 才會垂直置中（新按鈕本身就是 inline-flex）。
     `text-align: center` 保留不動，`justify-content` 一併給 center 讓兩種
     排版方式一致。
   ⚠ 不加右側箭頭：新按鈕的 `::after` 是箭頭，但 `.act-tip::after` **已經
     被佔用**——它是填補泡泡與按鈕之間 .5rem 空隙的透明橋接區（≈28171），
     沒有它游標移不過去、泡泡會在半路消失。箭頭不是客戶要求的項目，
     不為了「像按鈕」去拆掉一個有功能的東西。
   ⚠ 特異度：`.act-tip` 是 (0,1,0)，與既有那條同分，靠檔案順序在後面取勝；
     沒有任何更高分的規則在管這幾個屬性（已全檔 grep 確認）。 */
.act-tip {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: var(--btn-h-sm);
    padding: .5625rem .9375rem;
    border-radius: 0;
    /* Outline 變體的框線色（舊值是 hsl(var(--foreground)/.28)，壓白底約
       #c4c4c4；--slate 是 #7c7d80，明顯實一點）——框線是這顆泡泡唯一的
       輪廓，跟按鈕不同色就會看出是兩套。 */
    border-color: hsl(var(--slate));
    line-height: var(--lh-meta);
    font-weight: 400;
}

/* 可點的那顆（收藏）hover：跟 Outline 變體的 hover 同一個訊號——底色填成
   AERIAL 藍、框線同色。
   ⚠ 這推翻了 ≈28199 舊的「框線與文字一起轉 --accent-blue-text」寫法：
     那是泡泡還自成一格時的做法，現在它要跟按鈕同語彙，就該用按鈕的 hover。
     文字色回到 --heading（黑），與 `.btn-pill--solid` 永遠「藍底黑字」的
     全站定案一致（memory btn-pill-solid-never-invert）。
   ⚠ 狀態型那顆（分享的「已複製」）**不給 hover**：它 pointer-events:none、
     本來就不該有互動回饋（≈28208 的既有判斷，這裡不動它）。 */
.act-tip--link:hover,
.act-tip--link:focus-visible {
    background: var(--btn-fill-hover);
    border-color: var(--btn-fill-hover);
    color: hsl(var(--heading));
}

/* ═══════════════════════════════════════════════════════════════════════
   外框按鈕改藍底實心（2026-08-28 客戶指定，選項題答覆）
   ═══════════════════════════════════════════════════════════════════════
   客戶在「按鈕都改成首頁服務專區顧問案那顆」的範圍選項題裡選了
   「藍色實心 ＋ 透明框線（40 顆）」——也就是連原本透明底＋灰框線的
   Outline 那幾顆也要一律改成藍底，全站按鈕只留一種長相。
   （`.btn-pill--sm`＝收藏／分享那 10 顆維持原樣，客戶明確排除。）

   ── ⚠ 只改 4 顆，第 5 顆刻意保留 Outline ────────────────────────────
   實測全站的 Outline 按鈕只有 5 顆，而且**是兩種不同性質**（提問時我把它們
   混成同一群，這裡修正）：

     ① `.entry-links`  觀看 YouTube 影片／收聽 Podcast（知識文章頁）
     ② `.form-actions` 登入／註冊（電子報訂閱區）
        —— ①② 都是**整列只有這兩顆、沒有主要動作在旁邊**，改藍底不會有
           辨識問題，照客戶說的改。
     ③ `.form-actions` 重填（會員中心）
        —— 這顆**旁邊就站著 `.btn-pill--solid` 的「儲存變更」**。兩顆都變成
           同一個藍底，使用者就分不出哪顆是儲存、哪顆是清空重填——那是
           破壞性動作，看起來跟主要動作一樣是真的會按錯。
           **這一顆維持 Outline，已回報客戶，他要的話一行就能拿掉。**

   ── 做法：用 `:has()` 判斷「這一列有沒有主要按鈕」，不點名頁面 ──────
   `.form-actions:not(:has(.btn-pill--solid))` ＝ 這組動作列裡沒有實心主按鈕
   → 它的次要按鈕才改藍底。這樣不必寫死是哪一頁，日後任何動作列只要多了
   一顆主要按鈕，它的次要按鈕會**自動**退回 Outline，不會有人漏改。
   ⚠ 特異度：`.form-actions:not(:has(.btn-pill--solid)) .btn-pill:not(.btn-pill--solid)`
     ＝ (0,4,0)，穩穩蓋過既有 Outline 那條 (0,3,0)，不靠檔案順序。
     `.entry-links` 那條是 (0,3,0)，與既有同分，靠位置在後面取勝——這一條
     是有意識地依賴順序，日後若有人把本區段往前搬會失效。
   ⚠ 三段狀態值全部沿用 Solid 變體的既有 token（`--btn-fill`／
     `--btn-fill-hover`／`--btn-press`），沒有另訂顏色；文字一律
     `hsl(var(--heading))` 黑字——`.btn-pill--solid` 永遠「藍底黑字」不反白
     是全站定案（memory btn-pill-solid-never-invert）。
   ⚠ 箭頭不用管：新的箭頭是 `background: currentColor` ＋ mask，文字色一變
     它自己就跟著走。 */
/* ⚠ 2026-08-28 稍晚：客戶「主要按鈕黑70％顏色hover藍色」——這一段的機制
   （整列沒有主要按鈕時，把孤立的次要按鈕升級成主要的長相）完全保留，只把
   **顏色換成新的主按鈕配色**。留著舊的 AERIAL 藍會變成：站上大部分主按鈕
   是黑 70%，唯獨這幾顆（電子報訂閱區的登入／註冊、文章頁尾的影片／Podcast）
   是藍底，看起來像漏改。
   ⚠ 文字跟著轉白：新的主按鈕是深底，黑字壓上去只有 2.4:1。
   ⚠ hover 由 --btn-fill-hover（NUDE）改成 --btn-fill（AERIAL）：NUDE 這一輪
     被指派給「分類」了，不再兼任 hover 色，全站互動語彙統一成「滑過轉藍」。 */
.entry-links .btn-pill:not(.btn-pill--solid),
.form-actions:not(:has(.btn-pill--solid)) .btn-pill:not(.btn-pill--solid) {
    background: hsl(var(--foreground) / .7);
    border-color: transparent;
    color: hsl(var(--muted));
}

.entry-links .btn-pill:not(.btn-pill--solid):hover,
.entry-links .btn-pill:not(.btn-pill--solid):focus-visible,
.form-actions:not(:has(.btn-pill--solid)) .btn-pill:not(.btn-pill--solid):hover,
.form-actions:not(:has(.btn-pill--solid)) .btn-pill:not(.btn-pill--solid):focus-visible {
    background: var(--btn-fill);
    border-color: var(--btn-fill);
    color: hsl(var(--heading));
}

.entry-links .btn-pill:not(.btn-pill--solid):active,
.form-actions:not(:has(.btn-pill--solid)) .btn-pill:not(.btn-pill--solid):active {
    background: hsl(var(--btn-press));
    border-color: hsl(var(--btn-press));
    color: hsl(var(--heading));
}

/* ═══════════════════════════════════════════════════════════════════════
   輸入框：全站統一底線式（2026-08-28 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「全站只要有輸入匡統一改底線的形式呈現，跟會員登入一樣」。

   2026-08-28 稍早只把底線式套在 .mem-split-panel 底下（登入／服務聲明／
   資料填寫三頁），這一輪擴到全站。實際盤點站上所有輸入元件（19 個，
   排除 checkbox）：

     .form-input                 14 個 — 登入2／註冊4／忘記密碼1／重設密碼2／
                                        搜尋1／會員中心4／電子報2
     .footer-input                2 個 — footer 訂閱 ＋ 右下浮動訂閱面板
     .site-inline-search-input    1 個 — header 行內搜尋（**排除，見下面**）
     沒有 <textarea>、沒有 <select>（分類篩選是自訂按鈕不是原生 select）

   ── 為什麼 header 那顆搜尋框不改 ─────────────────────────────────────
   它本來就沒有任何邊框（實測四邊 0px、背景透明）。而且**它的底線是客戶
   自己要求拿掉的**：2026-08-26 客戶「搜尋匡白底不線匡」，當時附了截圖指出
   那條線「在白色色塊裡看起來像沒收乾淨的殘留」，我照辦移除（沿革見
   ≈L13005 那段註解）。這一輪再加回去等於推翻那次的決定，所以排除。
   要改回有底線：那段規則裡把 border 那行換成 border-bottom 即可。

   ── 選擇器為什麼要列這麼多 ───────────────────────────────────────────
   ⚠ .page--member .form-input：≈L12186 有一條客戶指定的手機高度修正
     `@media (max-width:767.98px) .page--member .form-input { padding: 3px 1rem }`
     是 (0,2,0)。只寫 .form-input (0,1,0) 的話，手機上會員中心的輸入框會
     留著 1rem 左內距、跟上面的標籤對不齊（登入那三頁有 .mem-split-panel
     的 (0,2,0) 擋著所以不受影響，會員中心不是 mem-split，會漏）。
     這裡只接管 padding-inline，垂直的 3px 留給那條規則。
   ⚠ .wf-float-panel .footer-input：浮動訂閱面板那顆是 (0,2,0)。
   ═══════════════════════════════════════════════════════════════════════ */

.form-input,
.page--member .form-input,
.footer-input,
.wf-float-panel .footer-input {
    padding-inline: 0;
    border: 0;
    border-bottom: 1px solid hsl(var(--foreground) / .18);
    border-radius: 0;
    background: transparent;
}

/* focus：底線加深。
   ⚠ .footer-input:focus（≈L4087）原本是 box-shadow: inset 0 0 0 1px，
     那會畫出完整的四邊內框——底線式下必須關掉，否則聚焦時會冒出一個
     跟其他欄位不一樣的方框。 */
.form-input:focus,
.footer-input:focus,
.wf-float-panel .footer-input:focus {
    outline: none;
    border-color: transparent;
    border-bottom-color: hsl(var(--foreground));
    box-shadow: none;
}

/* disabled（會員中心的 Email 欄位是唯一一個）。
   ⚠ 原本 .form-input:disabled 是淺灰填色（≈L12160）——底線式下那塊灰底
     會變成畫面上唯一還帶方形色塊的欄位，看起來像沒改到。改成「底線變淺
     ＋文字轉灰」表達不可編輯，跟其他欄位同一種形狀。 */
.form-input:disabled,
.footer-input:disabled {
    background: transparent;
    border-bottom-color: hsl(var(--foreground) / .1);
    color: hsl(var(--foreground) / .45);
}

/* ═══════════════════════════════════════════════════════════════════════
   配色改版（2026-08-28 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「主要按鈕黑70％顏色hover藍色 次要按鈕框線，分類膚色 標籤淺灰色」

     主要按鈕  黑 70%  →  hover 藍
     次要按鈕  框線（維持既有的 Outline 變體，本來就是這個）
     分類      膚色 NUDE
     標籤      淺灰

   ⚠ 這一輪推翻了「.btn-pill--solid 永遠不反白」那條舊定案（2026-08-05）：
     主要按鈕的底色從 AERIAL 淺藍換成黑 70%，是深底，文字**必須**轉淺才
     看得見（黑字壓在 rgba(26,26,26,.7) 上只有 2.4:1，遠低於可讀門檻）。
     舊定案的前提是「主按鈕是淺藍底」，前提沒了，結論跟著撤，這是客戶
     這次親口改的方向，不是我自作主張反白。

   ⚠ hover 一律轉藍：客戶只點名主要按鈕 hover 藍，但次要按鈕原本的 hover
     是 NUDE——而 NUDE 這一輪被指派給「分類」了，繼續當按鈕 hover 會讓
     同一個顏色同時代表「分類」和「滑過」兩件事。次要按鈕的 hover 一起
     改成藍，整站的互動語彙統一成「滑過＝轉藍」。

   ⚠ 按下（pressed）順著新階梯往下：黑70% → 藍 → 深藍，深淺一路加重。

   ⚠ 箭頭不必另外處理：它是 currentColor 的 mask，文字轉白時箭頭自動轉白。
   ═══════════════════════════════════════════════════════════════════════ */

/* ── 主要按鈕：黑 70% ＋ 淺色字 ─────────────────────────────────────── */
.btn-pill,
.btn-pill--solid,
.entry-links .btn-pill--solid,
.on-dark .btn-pill,
.course-section .btn-pill {
    background: hsl(var(--foreground) / .7);
    border-color: transparent;
    color: hsl(var(--muted));
}

/* hover：轉藍，文字回到黑（AERIAL 是淺色底） */
.btn-pill:hover,
.btn-pill:focus-visible,
.btn-pill--solid:hover,
.btn-pill--solid:focus-visible,
.entry-links .btn-pill--solid:hover,
.entry-links .btn-pill--solid:focus-visible,
.on-dark .btn-pill:hover,
.on-dark .btn-pill:focus-visible,
.course-section .btn-pill:hover,
.course-section .btn-pill:focus-visible {
    background: var(--btn-fill);
    border-color: var(--btn-fill);
    color: hsl(var(--heading));
}

.btn-pill:active,
.btn-pill--solid:active,
.entry-links .btn-pill--solid:active,
.on-dark .btn-pill:active,
.course-section .btn-pill:active {
    background: hsl(var(--btn-press));
    border-color: hsl(var(--btn-press));
    color: hsl(var(--heading));
}

/* ── 次要按鈕：框線（靜置維持透明＋細框，hover 一起轉藍）─────────────
   ⚠ 這裡**刻意不寫 .entry-links**：2026-08-28 第三十次部署有一條客戶指定的
     規則（見上面 ≈L29787），把「整列沒有主要按鈕」的孤立次要按鈕升級成主要
     的長相——文章頁尾的「觀看 YouTube／收聽 Podcast」就是那個情境。我這條
     跟它同為 (0,3,0)、位置又在後面，寫上去會靠順序把它整個蓋掉，等於默默
     退回它上線前的樣子。那條規則的顏色我已經同步改成新的主按鈕配色。
   ⚠ .form-actions 這條可以留：對方那條是
     `.form-actions:not(:has(.btn-pill--solid))` (0,4,0)，比這裡高，會在
     「整列沒有主按鈕」時自動勝出；我這條只在「旁邊真的有主按鈕」時生效，
     那才是真正的次要按鈕，該畫框線。兩條互補、不打架。 */
.btn-pill--outline,
.form-actions .btn-pill:not(.btn-pill--solid) {
    background: transparent;
    border-color: hsl(var(--slate));
    color: hsl(var(--heading));
}

.btn-pill--outline:hover,
.btn-pill--outline:focus-visible,
.form-actions .btn-pill:not(.btn-pill--solid):hover,
.form-actions .btn-pill:not(.btn-pill--solid):focus-visible {
    background: var(--btn-fill);
    border-color: var(--btn-fill);
    color: hsl(var(--heading));
}

/* ── 標籤＝淺灰；分類＝膚色 ─────────────────────────────────────────
   ⚠ 兩者在標記上本來就分得出來（不是靠猜）：
       分類 .tag-pill--cat（首頁）／.wf-card-cat（知識專區卡片與相關文章）
       標籤 .wf-card-tag，以及內容是「#xxx」的裸 .tag-pill
     所以「裸 .tag-pill」＝標籤（淺灰）當基底，分類再用自己的 class 蓋上膚色。
   ⚠ 淺灰用站上既有的 --surface-grey（#DCDDE0），不新增色票；它跟頁面底色
     #F6F6F7 差得夠開，貼在白底或米白底上都看得出是一塊標籤。
   ⚠ 選中態 .is-active 維持 AERIAL 不動：那是「狀態」不是「種類」，客戶這輪
     講的是分類與標籤兩種**類型**的顏色。
   ⚠ 排除（維持既有定案）：知識專區左欄的分類清單（(0,3,0) 的
     .page--knowledge .kz-tags .tag-pill，08-18 定案的直式帶底線列）、
     知識卡牆「拿掉框線」、實體課程 Hero 的 .cx-stat 數據 chip。 */
.tag-pill,
.kz-tags .tag-pill,
.pcard .tag-pill,
.filter-tags .tag-pill,
.kd-hero-tags .tag-pill,
.kd-related-tags .tag-pill,
.pcard-tags .tag-pill {
    background: hsl(var(--surface-grey));
    border-color: hsl(var(--surface-grey));
    color: hsl(var(--heading));
}

/* ⚠ 容器層的選擇器要一條條列出來：上面淺灰那組是 .kd-hero-tags .tag-pill
   這種 (0,2,0)，只寫 .tag-pill--cat (0,1,0) 會被它壓過去——實測文章內頁
   Hero 的分類就是這樣變成淺灰的。 */
.tag-pill--cat,
.wf-card-cat,
.kz-tags .tag-pill--cat,
.kz-tags .wf-card-cat,
.pcard .tag-pill--cat,
.pcard .wf-card-cat,
.pcard-tags .tag-pill--cat,
.pcard-tags .wf-card-cat,
.kd-hero-tags .tag-pill--cat,
.kd-hero-tags .wf-card-cat,
.kd-related-tags .tag-pill--cat,
.kd-related-tags .wf-card-cat,
.filter-tags .tag-pill--cat,
.filter-tags .wf-card-cat {
    background: hsl(var(--nude));
    border-color: hsl(var(--nude));
    color: hsl(var(--heading));
}

/* hover：跟按鈕同一套語彙，滑過轉藍 */
.tag-pill:hover,
.tag-pill:focus-visible,
.kz-tags .tag-pill:hover,
.kz-tags .tag-pill:focus-visible,
.pcard .tag-pill:hover,
.pcard .tag-pill:focus-visible,
.tag-pill--cat:hover,
.tag-pill--cat:focus-visible,
.wf-card-cat:hover,
.wf-card-cat:focus-visible {
    background: var(--btn-fill);
    border-color: var(--btn-fill);
    color: hsl(var(--heading));
}

/* ═══════════════════════════════════════════════════════════════════════
   服務聲明條文框：原生捲軸換成文章頁那顆 .wf-rail（2026-08-28）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「註冊的服務聲明旁邊的滾輪樣式請跟文章頁面的滾輪樣式一致」（附
   文章頁滾輪截圖）。原本這個框走瀏覽器原生捲軸，跟文章頁那顆自製滾輪
   （上下箭頭＋細軌道＋可拖曳圓把手）完全是兩套視覺。

   ⚠ **沒有另外做一顆長得像的元件**：markup 直接 include 文章頁那支
     _scroll_rail.php、樣式全部沿用上面 ≈27926 的 .wf-rail-*、行為沿用
     scroll-rail.js（該檔新增一個「捲的是某個容器不是整頁」的模式）。
     這裡只寫「換一種定位」需要的那幾條，日後客戶要改滾輪外觀（顏色、
     粗細、把手大小）改 .wf-rail-* 一處，文章四頁與這頁會一起變。
   ⚠ 定位從 fixed 改 absolute：文章頁那顆是貼視窗右緣、垂直置中的浮動
     元件（position:fixed + top:50%），這裡要貼的是「條文框自己的右緣」，
     所以改成 absolute 掛在 .form-scroll-wrap 上，並把 fixed 版的
     top/right/transform 三個值全部歸零覆寫掉——只覆寫 position 會留下
     top:50% 與 translateY(-50%)，軌道會掉到框的下半部（第一版實測）。
   ⚠ 軌道高度改成撐滿：文章頁是寫死 260px，這裡要跟條文框等高（框自己
     是 max-height:22rem，字級切大時會變高），所以 flex:1 + height:auto。
     JS 算把手位置用的是 track.clientHeight 現場量，不需要知道這個數字。
   ⚠ 原生捲軸只在 ≥992px 藏起來：.wf-rail 本體 <992px 是 display:none
     （文章頁定的規則，這裡沿用），窄螢幕沒有滾輪元件可用，這時候藏掉
     原生捲軸會變成「看不出這裡可以捲」，所以兩者的斷點刻意一致。
   ⚠ padding-right 從 1rem 加到 2.75rem：軌道是絕對定位、不佔文字的位置，
     不自己空出來的話條文會從滾輪底下穿過去。 */
.form-scroll-wrap {
    position: relative;
}

@media (min-width: 992px) {
    .form-scroll-wrap .wf-rail:not([hidden]) {
        position: absolute;
        top: 0;
        right: 0;
        bottom: 0;
        transform: none;
        z-index: 1;
        width: 24px;
        justify-content: center;
    }

    .form-scroll-wrap .wf-rail-track {
        height: auto;
        flex: 1 1 auto;
    }

    .form-scroll {
        padding-right: 2.75rem;
        scrollbar-width: none;               /* Firefox */
    }

    .form-scroll::-webkit-scrollbar {
        display: none;                       /* WebKit／Blink */
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   客戶修正三則（2026-08-28 稍晚）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「我更正主要按鈕還是藍底黑字，要注意得有知識專區分類那區小卡預設
   標籤分類純文字被選中在填色這區塊不要跟著全站按鈕樣式調整到，還有電子報
   總頁的按鈕有三顆，訂閱按鈕用主要按鈕樣式，登入跟註冊用框線不填色」（附
   電子報訂閱區截圖）。

   ⚠ 這三則是**推翻同日稍早那輪配色改版的一部分**（見上面「配色改版」那節，
     另一個 session 依客戶當時的「主要按鈕黑70％」實作）。**那一節一個字都
     沒有刪**——分類膚色／標籤淺灰／次要按鈕框線／hover 轉藍那些仍然成立，
     這裡只覆寫客戶這次點名的三處。日後若客戶又改回黑底，把本節刪掉即可
     回到上一版，不必去翻改過什麼。 */

/* ── ① 主要按鈕改回 AERIAL 藍底＋黑字 ─────────────────────────────────
   選擇器與上面「主要按鈕：黑 70%」那組**逐字相同**（同為 (0,1,0)～(0,2,0)），
   靠位置在後面取勝——刻意這樣寫，好讓兩節並排時一眼看得出是同一組選擇器
   的前後兩版，而不是另外發明一組更高特異度的規則去壓它。

   ⚠ 三段狀態的階梯要重排，不能只把 default 換回藍就好：舊版是
   「黑 → hover 藍 → 按下深藍」，default 換成藍之後 hover 若維持藍就等於
   沒有回饋。
   ⚠ 客戶隨後再指定：「主要按鈕統一 hover 變淺灰，框線的也是，包含首頁的
     yt podcast 按鈕」。所以 hover 不再是「同色相壓深」，而是**全站統一
     跳到淺灰**——三種按鈕（實心／框線／首頁 kz-links）滑過都是同一顆灰，
     互動語彙一次統一。階梯改成：
         default 各自的底色 → hover 淺灰 --surface-grey(87%) → 按下 78%
   ⚠ 78% 那一階是本節唯一新增的數值，寫成 token 放在下面，不散在規則裡。
     客戶只點名 hover，按下這一階是為了維持「逐段加深」的既有邏輯而補的，
     不是自己另編一套顏色（同一顆灰壓深，不換色相）。
   ⚠ 文字一律 hsl(var(--heading)) 黑字——這正是客戶這次點名的「藍底黑字」，
     也回到 memory btn-pill-solid-never-invert 那條 2026-08-05 的定案
     （它當初被撤是因為「主按鈕改成黑底、黑字看不清」，現在前提又回來了）。
     淺灰底同樣是淺色，黑字對比 15:1 以上，不需要反白。 */
:root {
    --btn-hover-grey: 225 6% 87%;   /* ＝ --surface-grey，全站按鈕統一的 hover 底 */
    --btn-press-grey: 225 6% 78%;   /* 按下：同一顆灰再壓一階（87% → 78%）*/
}

.btn-pill,
.btn-pill--solid,
.entry-links .btn-pill--solid,
.on-dark .btn-pill,
.course-section .btn-pill {
    background: var(--btn-fill);
    border-color: var(--btn-fill);
    color: hsl(var(--heading));
}

.btn-pill:hover,
.btn-pill:focus-visible,
.btn-pill--solid:hover,
.btn-pill--solid:focus-visible,
.entry-links .btn-pill--solid:hover,
.entry-links .btn-pill--solid:focus-visible,
.on-dark .btn-pill:hover,
.on-dark .btn-pill:focus-visible,
.course-section .btn-pill:hover,
.course-section .btn-pill:focus-visible {
    background: hsl(var(--btn-hover-grey));
    border-color: hsl(var(--btn-hover-grey));
    color: hsl(var(--heading));
}

.btn-pill:active,
.btn-pill--solid:active,
.entry-links .btn-pill--solid:active,
.on-dark .btn-pill:active,
.course-section .btn-pill:active {
    background: hsl(var(--btn-press-grey));
    border-color: hsl(var(--btn-press-grey));
    color: hsl(var(--heading));
}

/* ── ② 電子報訂閱區：登入／註冊回到「框線不填色」──────────────────────
   客戶：「電子報總頁的按鈕有三顆，訂閱按鈕用主要按鈕樣式，登入跟註冊用
   框線不填色」。

   ⚠ **標記本來就是對的**（newsletter_list.php：訂閱是 .btn-pill--solid、
     登入／註冊是裸 .btn-pill），是**我自己第三十次那條規則把它們一起填色的**：
     `.form-actions:not(:has(.btn-pill--solid)) .btn-pill:not(.btn-pill--solid)`
     ——那條的用意是「整列沒有主要按鈕時，把孤立的次要按鈕升級成主要長相」，
     而登入／註冊所在的 .form-actions 裡確實沒有 solid（訂閱在上面另一個
     容器裡），所以被判定成孤立、升級了。客戶這次的指示等於否定了那條規則
     套在表單動作列上的假設。
   ⚠ 所以這裡把 **.form-actions 整個排除**在那個機制之外，回到「次要按鈕
     ＝透明＋細框」。`.entry-links`（文章頁尾的觀看 YouTube／收聽 Podcast）
     **維持升級**：那是客戶稍早明確選過的範圍，這次沒有被點名推翻。
   ⚠ 特異度：我那條是 (0,4,0)，這裡用 `.form-actions .btn-pill:not(.btn-pill--solid)`
     ＝ (0,3,0) 打不過它，所以補一層 `:not(.btn-pill--solid)` 湊到 (0,4,0)
     再靠位置在後面取勝。框線色沿用「配色改版」那節的 Outline 值。 */
.form-actions .btn-pill:not(.btn-pill--solid):not(.btn-pill--sm) {
    background: transparent;
    border-color: hsl(var(--slate));
    color: hsl(var(--heading));
}

.form-actions .btn-pill:not(.btn-pill--solid):not(.btn-pill--sm):hover,
.form-actions .btn-pill:not(.btn-pill--solid):not(.btn-pill--sm):focus-visible {
    background: hsl(var(--btn-hover-grey));
    border-color: hsl(var(--btn-hover-grey));
    color: hsl(var(--heading));
}

/* ── ③ 知識專區小卡的分類／標籤：預設純文字，選中才填色 ────────────────
   客戶：「知識專區分類那區小卡預設標籤分類純文字被選中在填色，這區塊不要
   跟著全站按鈕樣式調整到」。

   ⚠ 目標是 `.pcard-tags` 裡那兩種膠囊（`.wf-card-cat` 分類／`.wf-card-tag`
     標籤，兩者同時也帶 `.tag-pill`）。它們在「配色改版」那節被指派成
     分類＝膚色、標籤＝淺灰，**預設就是填色的**；客戶要的是反過來——
     預設純文字，只有「目前正在篩選的那一顆」（`.is-active`）才填色。
   ⚠ 這正好跟左欄篩選清單 `.filter-tags` 的既有行為一致（實測：未選中
     透明＋灰字、選中膚色填滿），等於把小卡也收進同一套語彙，不是新發明。
   ⚠ 「不要跟著全站按鈕樣式調整到」＝ hover **不轉藍**。上面那條
     `.tag-pill:hover / .wf-card-cat:hover → var(--btn-fill)` 是按鈕語彙，
     這裡明確擋掉，改成「hover 才淡淡上色」——讓它仍然看得出可點，但不會
     被誤讀成一顆按鈕。
   ⚠ 選中時的填色**各自沿用自己的顏色**（分類膚色／標籤淺灰），不是統一
     一種：那是「配色改版」那節定下的分類 vs 標籤區辨，客戶這次只改「什麼
     時候填色」，沒有改「填什麼色」。
   ⚠ 只鎖 `.pcard-tags`：`.kd-hero-tags`（文章內頁 Hero）與
     `.kd-related-tags`（相關文章）**不動**——客戶說的是「小卡」，而那兩處
     不是卡片列表。要一起改再說一聲，選擇器加上去即可。
   ⚠ 特異度：既有那兩條是 `.pcard-tags .tag-pill`／`.pcard-tags .wf-card-cat`
     ＝ (0,2,0)，這裡用 `.pcard-tags .tag-pill:not(.is-active)` ＝ (0,3,0)
     穩穩蓋過，不靠檔案順序。 */
.pcard-tags .tag-pill:not(.is-active),
.pcard-tags .wf-card-cat:not(.is-active),
.pcard-tags .wf-card-tag:not(.is-active) {
    background: transparent;
    border-color: transparent;
    color: hsl(var(--slate));
}

.pcard-tags .tag-pill:not(.is-active):hover,
.pcard-tags .tag-pill:not(.is-active):focus-visible,
.pcard-tags .wf-card-cat:not(.is-active):hover,
.pcard-tags .wf-card-cat:not(.is-active):focus-visible,
.pcard-tags .wf-card-tag:not(.is-active):hover,
.pcard-tags .wf-card-tag:not(.is-active):focus-visible {
    background: transparent;
    border-color: transparent;
    color: hsl(var(--heading));
}

/* 選中：各自回到自己的填色（分類膚色／標籤淺灰），並擋掉按鈕的 hover 轉藍 */
.pcard-tags .wf-card-cat.is-active,
.pcard-tags .wf-card-cat.is-active:hover,
.pcard-tags .wf-card-cat.is-active:focus-visible {
    background: hsl(var(--nude));
    border-color: hsl(var(--nude));
    color: hsl(var(--heading));
}

.pcard-tags .wf-card-tag.is-active,
.pcard-tags .wf-card-tag.is-active:hover,
.pcard-tags .wf-card-tag.is-active:focus-visible {
    background: hsl(var(--surface-grey));
    border-color: hsl(var(--surface-grey));
    color: hsl(var(--heading));
}

/* ── ①-補 文章頁尾外連鈕的顏色跟著主按鈕一起回藍 ──────────────────────
   `.entry-links` 那兩顆（觀看 YouTube 影片／收聽 Podcast）走的是「孤立次要
   動作升級成主要長相」那條機制（第三十次部署，客戶指定），所以它們**應該
   跟主按鈕同色**。配色改版那輪把它們同步成了黑 70%（作法正確），這輪主按鈕
   改回藍，它們也要一起回來——否則會變成全站唯一還是黑底的兩顆。

   ⚠ 實測就是這樣：只改上面 ① 那組選擇器時，`/knowledge/detail` 的外連鈕
     仍是 rgba(26,26,26,.7)＋白字，因為它們吃的是
     `.entry-links .btn-pill:not(.btn-pill--solid)` 這條 (0,3,0)，不在 ① 的
     選擇器清單裡。**「改主按鈕」不等於「所有長得像主按鈕的都跟著改」，
     升級機制那一支要自己列出來。**
   ⚠ 只補 `.entry-links`，不補 `.form-actions`：後者這輪已被客戶指定回到
     框線（見上面 ②），兩者從此分道揚鑣。 */
.entry-links .btn-pill:not(.btn-pill--solid) {
    background: var(--btn-fill);
    border-color: var(--btn-fill);
    color: hsl(var(--heading));
}

.entry-links .btn-pill:not(.btn-pill--solid):hover,
.entry-links .btn-pill:not(.btn-pill--solid):focus-visible {
    background: hsl(var(--btn-hover-grey));
    border-color: hsl(var(--btn-hover-grey));
    color: hsl(var(--heading));
}

.entry-links .btn-pill:not(.btn-pill--solid):active {
    background: hsl(var(--btn-press-grey));
    border-color: hsl(var(--btn-press-grey));
    color: hsl(var(--heading));
}

/* ── 註冊完成區塊移進註冊頁第 3 步（2026-08-28 客戶指定）─────────────────
   客戶：「註冊申請不要額外開一頁自己的樣式，讓他在註冊頁的第三步驟，不要
   跳轉頁面」。完成區塊（.done-panel）原本是為獨立那一頁設計的**置中白卡**
   （置中文字＋白底＋圓角＋大內距），搬進左圖右文的註冊頁之後，會變成整欄
   唯一一塊置中的卡片、跟上面靠左的標題與 stepper 對不齊——客戶說的「自己
   的樣式」就是這個。這裡把它拉回註冊頁本身的語彙：靠左、無卡片。
   ⚠ 只在 .mem-split-panel 底下改：獨立的 member/register/done 那一頁維持
     原本的置中卡片不動（它是無 JS 時的退路，那個版型在那一頁是對的）。
   ⚠ [hidden] 明寫 display:none：.done-panel 本身沒有設 display，靠瀏覽器
     預設就會隱藏；但這裡明寫一條，日後若有人給它 display:flex 之類的值，
     hidden 才不會突然失效（那種失效是靜靜地壞掉，很難查）。 */
/* ⚠ padding-block 不能寫成 padding 簡寫：水平內距是 .mem-split-panel >
   :not(.form-panel)（≈L13783）用來把標題／stepper／wf-note 推到跟表單內容
   同一條左緣的，兩條同為 (0,2,0)、這條在後面，寫簡寫會把它一起歸零——
   實測完成區塊會比標題往左凸 43px。跟稍早 .form-panel 那條同一個坑。 */
.mem-split-panel .done-panel {
    padding-block: 0;
    background: transparent;
    border-radius: 0;
    text-align: left;
}

.mem-split-panel .done-panel .page-text {
    margin-inline: 0;
}

.mem-split-panel .done-panel .form-actions {
    justify-content: flex-start;
}

.mem-split-panel [data-reg-view][hidden] {
    display: none;
}

/* ── ①-補2 首頁 YouTube／Podcast 兩顆的 hover 也統一成淺灰 ────────────────
   客戶：「主要按鈕統一 hover 變淺灰，框線的也是，**包含首頁的 yt podcast
   按鈕**」——最後那一句點名的就是首頁知識專區那組 `.kz-links a`
   （home.php ≈1095，卡其底＋黑字的兩顆外連鈕）。

   ⚠ 它**不是 .btn-pill**，是自己一套規則（本檔 ≈1761），所以上面 ① 那組
     選擇器掃不到它——這已經是同一輪第二次踩到「長得像按鈕但不是按鈕元件」
     （前一次是 .entry-links 的升級機制，見 ①-補）。客戶會逐一點名，正是
     因為它們在畫面上就是同一類東西，只有原始碼裡才分得出來。
   ⚠ 靜置的卡其底 `--brand-accent` **不動**：客戶只說 hover，沒有說要換底色。
   ⚠ 原本的 hover 是「填深灰 + 字轉米白」（本檔 ≈1781），跟新的統一語彙
     （淺灰底 + 黑字）方向相反，所以字色也要一起蓋掉，只改 background
     會變成淺灰底配米白字、幾乎看不見。 */
.kz-links a:hover,
.kz-links a:focus-visible {
    background: hsl(var(--btn-hover-grey));
    color: hsl(var(--heading));
}

.kz-links a:active {
    background: hsl(var(--btn-press-grey));
    color: hsl(var(--heading));
}

/* ═══════════════════════════════════════════════════════════════════════
   分享面板 .share-pop（2026-08-28 客戶指定，附兩張參考圖）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「文章的分享按鈕請用這種形式呈現，點擊分享跳出懸浮視窗 line fb
   簡訊 gmail 可以分享，然後把原先該文章已複製的呈現方式刪除」。參考圖是
   一顆膠囊按鈕，點下去在它下面展開一條圓角膠囊、裡面一排圓形圖示鈕。
   標記見 _share_button.php（五頁共用），行為見 pages.js 那節。

   ⚠ 往**下**展開、靠右對齊：跟 .act-tip（≈28143）同一組方向，理由照抄那裡
     實測過的兩點——往上長會蓋住正上方的主要按鈕（手機最明顯，768 平板也
     重疊 19–38px）；水平置中在 390 寬只剩 36px 餘裕、字級切 A++ 會被切掉。
     參考圖本身也是往下展開，兩邊一致。
   ⚠ 圓形圖示鈕沿用**浮動客服鈕**那一套（≈2486 `.is-designed .wf-float a`）：
     46px、AERIAL 藍底、深灰圖示、hover 藍壓深一階。**刻意不吃**同日客戶指定
     的「主要按鈕統一 hover 變淺灰」——那則點名的是主要／框線／首頁 kz-links
     三種**方形按鈕**，圓形圖示鈕是另一類語彙（客戶 2026-07-30 親自指定浮動
     鈕要 AERIAL 藍，同日也確認過「圓形圖示鈕維持藍」）。
   ⚠ 面板底色用白＋陰影，比照 .wf-float-panel（≈11030）：這是唯一一種
     「浮在內容之上的小面板」既有語彙，不另外發明。
   ⚠ 未切版的 18 頁（沒有 .is-designed）不會壞：那些頁的 .wf-icon 是繼承
     currentColor 的線性圖示，面板本身只有白底與細框，不依賴任何設計稿色票。 */
.share-pop {
    position: absolute;
    top: calc(100% + .5rem);
    right: 0;
    z-index: 20;
    display: flex;
    align-items: center;
    gap: .625rem;
    padding: .5rem;
    border-radius: 999px;
    background: #fff;
    box-shadow: 0 6px 24px hsl(var(--foreground) / .16);
}

.share-pop[hidden] {
    display: none;
}

.share-pop-item {
    width: 46px;
    height: 46px;
    flex: none;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    background: hsl(var(--accent-blue));
    color: hsl(var(--surface-dark));
    text-decoration: none;
    transition: background var(--transition), color var(--transition);
}

.share-pop-item svg {
    width: 22px;
    height: 22px;
    display: block;
}

.share-pop-item:hover,
.share-pop-item:focus-visible {
    background: hsl(211 41% 66%);
    color: hsl(var(--surface-dark));
}

/* 開啟時把分享鈕本身標記起來——按鈕自己的 hover/press 樣式在面板開著時
   會因為滑鼠移開而復原，看起來像「沒有東西是開著的」。 */
.share-wrap.is-share-open .btn-share {
    background: hsl(var(--btn-hover-grey));
    border-color: hsl(var(--btn-hover-grey));
}

/* 手機：面板貼右緣容易壓到內容區邊界，收一點內距與間距即可，
   四顆 46px 在 390 寬仍有餘裕（46*4 + 10*3 + 8*2 = 230 < 390 - 32）。 */
@media (max-width: 575.98px) {
    .share-pop {
        gap: .5rem;
        padding: .4375rem;
    }
}

/* ⚠ 面板放不下時往上翻（JS 加 .is-share-up，見 pages.js 的 place()）：
   實測 390x844 把分享鈕捲到接近視窗底部再打開，面板會落在
   [137,782,359,842]，與右下角浮動客服鈕 [222,784,374,828] 重疊，
   **「簡訊」「Gmail」兩顆的中心點 elementFromPoint 命中的是 .wf-float，
   等於點不到**。翻上去代價是暫時蓋住上一列的主要按鈕（.act-tip 當初
   ≈28134 就是為了避開這件事才固定往下開），但那是面板開著的幾秒，
   比兩顆按鈕點不到輕得多。 */
.share-wrap.is-share-up .share-pop {
    top: auto;
    bottom: calc(100% + .5rem);
}

/* ═══════════════════════════════════════════════════════════════════════
   分享面板外觀修正（2026-08-28 客戶指定，附 LINE 圖示參考圖）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「按鈕 icon 不要藍底，視窗不要影子跟白色背景純框線，line 的 icon 用
   〔iconify〕」。前兩件在這裡，圖示那件在 app/Common.php 的 brand-line
   （附完整理由：iconify 的外部 script 會被本站 CSP 擋掉，改成 inline 同一顆圖）。

   ⚠ 這個面板是同一天另一個 session 新做的（第三十四次部署，客戶要「分享鈕
     點擊跳出懸浮視窗」）。它原本那節 CSS **一個字都沒有動**，這裡用檔尾覆寫，
     維持「誰做的那節就留給誰」的慣例，也讓客戶要退回時只要刪這一節。 */

/* ① 面板：拿掉白底與陰影，改成純框線 */
.share-pop {
    background: transparent;
    box-shadow: none;
    border: 1px solid hsl(var(--slate));
}

/* ② 每顆圖示：拿掉藍色圓底 ────────────────────────────────────────────
   ⚠ 底色拿掉之後圖示的顏色也要跟著換：原本是 `--surface-dark`（深灰），
     那是為了壓在淺藍圓底上；現在直接壓在頁面底色上，改用 --heading，
     與站上其他線性圖示同一個色階。
   ⚠ 圓形外框（border-radius:50%）與 46px 的點擊面積**保留**：客戶說的是
     「不要藍底」，沒有說要縮小或改形狀，而 46px 是觸控最小點擊區的既有值。
   ⚠ hover 沿用同日客戶定案的「全站按鈕統一淺灰」（--btn-hover-grey），
     不要留著舊的藍——那會變成「靜置沒有藍、滑過又跳出藍」，跟這次拿掉藍底
     的用意相反。 */
.share-pop-item {
    background: transparent;
    color: hsl(var(--heading));
}

.share-pop-item:hover,
.share-pop-item:focus-visible {
    background: hsl(var(--btn-hover-grey));
    color: hsl(var(--heading));
}

.share-pop-item:active {
    background: hsl(var(--btn-press-grey));
    color: hsl(var(--heading));
}

/* ═══════════════════════════════════════════════════════════════════════
   實體課程 Hero 往下箭頭：加快、加長、線條改成「連上箭頭」的動態
   （2026-08-31 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「右下的箭頭動態效果速度快一點，直線再長一點。讓直線連線到 arrow
   也變成一個動態效果」。上面 ≈28578 那節（08-28 的整支箭頭 nudge）**一個字
   都沒改**，這裡用檔尾覆寫接手，要退回時刪掉本節即可。

   ── 三件事各自怎麼做 ──────────────────────────────────────────────
   ① 速度：2.6s → 1.4s（快將近一倍）。
   ② 直線加長：**改的是 viewBox 的比例，不是只把 CSS 高度拉高**。
      這支 svg 是 preserveAspectRatio="none"，整份路徑會跟著 CSS 高度非等比
      拉伸——只把高度加大，箭頭的「V」也會跟著變尖變長（客戶要的是線變長，
      不是箭頭變形）。所以 viewBox 從 `0 0 24 64` 改成 `0 0 24 88`，多出來的
      24 個單位全部給直線（線 58→82），箭頭本身仍是 12 單位高：
        改動前 H=104：頭 12×(104/64)=19.5px、線 58×(104/64)=94px
        改動後 H=144：頭 12×(144/88)=19.6px、線 82×(144/88)=134px
      **箭頭視覺大小幾乎不變，長度全部加在線上**（+43%）。
   ③ 「直線連線到 arrow 的動態」＝線用 stroke-dasharray／dashoffset 由上往下
      畫到箭頭，停一下，再從下往上收回去。收回而不是硬跳回起點，是為了讓
      每一輪的結尾與下一輪的開頭是同一個狀態（線收乾淨），不會出現閃一下的
      重置。箭頭本身改成「線畫到底的那一刻才點一下頭」（58% 那格），兩支
      動畫同樣 1.4s，靠 keyframes 的百分比對時，不用 delay 疊。

   ── 高度為什麼是 clamp(3.5rem, 10vw, 9rem) ──────────────────────────
   實測 void4 那格箭頭上方的淨空是這輪的限制條件（1300 寬只有 60px，1440 是
   87px、1920 是 188px）。10vw 讓成長量自己跟著版面走：
     1300 → 130px（原 104，上方淨空剩 34px）
     1440 → 144px（原 104，剩 47px）
     1920 → 144px（原 104，剩 148px）
   ⚠ **這條一定要包在 `@media (min-width: 1300px)` 裡**：長箭頭只存在於
     ≥1300，1200–1299.98 與 ≤1199.98 兩段各有一條把它壓成 16px 的規則
     （≈18286／≈18407）。本節寫在檔尾，特異度同分但位置在後，不加斷點限制
     會把那兩段的 16px 一起蓋掉、箭頭在窄螢幕整支爆開（這個站踩過好幾次
     「檔尾覆寫忘了限定斷點」，見 memory media-query-scope-not-inherited）。
   ⚠ 動畫本身**不限斷點**：16px 的小箭頭一樣要有動態，跟 08-28 那輪一致。 */
@media (min-width: 1300px) {
    .page--wall .cx-hero-arrow--down {
        height: clamp(3.5rem, 10vw, 9rem);
    }
}

/* 動態從整支 svg 移到兩條 path 上——線與箭頭現在各做各的事。
   ⚠ 舊的 cxHeroArrowNudge keyframes 留著沒刪（記錄 08-28 那版）。 */
.page--wall .cx-hero-arrow--down {
    animation: none;
}

.page--wall .cx-hero-arrow-line {
    /* 82 ＝ 路徑長度（user unit，`M12 0v82`）。dasharray 的長度是在 user
       座標系算的，跟 CSS 高度怎麼拉伸無關，所以每個斷點都吻合。 */
    stroke-dasharray: 82;
    stroke-dashoffset: 0;
    animation: cxHeroArrowDraw 1.4s ease-in-out infinite;
}

.page--wall .cx-hero-arrow-head {
    animation: cxHeroArrowTip 1.4s ease-in-out infinite;
}

@keyframes cxHeroArrowDraw {
    0%   { stroke-dashoffset: 82; }   /* 線收起來 */
    45%  { stroke-dashoffset: 0; }    /* 畫到底，接上箭頭 */
    72%  { stroke-dashoffset: 0; }    /* 停一下 */
    100% { stroke-dashoffset: -82; }  /* 由上往下收乾淨，無縫接回 0% */
}

/* ⚠ translateY 用 px 但**單位是 svg 的 user unit**（CSS transform 作用在
   SVG 子元素時走的是使用者座標系），所以 5 個單位在不同斷點會自己換算成
   不同的實際位移：H=144 → 8.2px、H=16 → 0.9px。等於 08-28 那版
   min(9px, 22%) 想達到的「大箭頭克制、小箭頭等比例」，但寫法更直接。 */
@keyframes cxHeroArrowTip {
    0%, 40%   { transform: translateY(0); }
    58%       { transform: translateY(5px); }
    80%, 100% { transform: translateY(0); }
}

/* 系統關掉動態時：兩支動畫都停，並且線要維持「畫好的」狀態
   （dashoffset 的基準值就是 0，animation:none 之後自然是整條線都在，
   不會變成一條看不見的線——這是把基準值寫成 0 而不是 82 的理由）。 */
@media (prefers-reduced-motion: reduce) {
    .page--wall .cx-hero-arrow-line,
    .page--wall .cx-hero-arrow-head {
        animation: none;
    }
}

/* ── 課程單頁：YouTube 影片（2026-08-28 客戶指定，08-31 修正寬度）───────────
   ⚠ 2026-08-31 客戶「讓寬延伸到線結束的位置」（附截圖）——原本照前一輪的
     「請前台寫固定寬跟高」寫死 560px，實測比標題底下那條線短了一截。客戶指的
     那條線是 .cd-h2 的 border-bottom，而 .cd-h2 是 block、寬度就等於內容欄
     的寬度，所以影片改成 width:100% ＝ 跟那條線同寬、同起訖點。
   ⚠ 因此寬度不再是固定值：那條線本身是隨版面流動的（欄寬由 --section-x 與
     版心決定），要「跟線一樣長」就不可能同時是固定 px。以客戶這次的指定為準。
   ⚠ height 仍是 auto 搭 aspect-ratio 16/9（＝原本的 560×315）：寬度變動時
     高度要跟著算，寫死 315px 會被壓扁成非 16:9。
   ⚠ display:block 是必要的：iframe 預設 inline，底下會多一條 4px 的基線縫。
   ⚠ 不加圓角、不加外框：這一頁其餘圖片（Hero／報名資訊）都是直角矩形，
     多一個圓角會變成頁面上唯一一個不一樣的形狀。 */
.cd-video {
    margin-top: clamp(1.25rem, 2.5vw, 2rem);
}

.cd-video iframe {
    display: block;
    width: 100%;
    height: auto;
    aspect-ratio: 16 / 9;
    border: 0;
}

/* ═══════════════════════════════════════════════════════════════════════
   分享面板與懸浮鈕的圖示改用 footer 那顆灰（2026-08-31 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「分享該區塊的 icon 太黑了，請改跟 footer 區塊的 icon 一樣的灰」，
   隨後追加「懸浮按鈕也改一樣」。

   ⚠ **「footer 那顆灰」是量出來的，不是挑一個看起來像的**：實測 footer 的
     YouTube／Podcast／Instagram 三顆圖示是 `rgba(26,26,26,.75)`，
     也就是 `hsl(var(--foreground) / .75)`；分享面板是 `rgb(26,26,26)` 純黑，
     兩者並排差得出來。這裡直接寫同一個算式，不寫死 rgba——日後
     --foreground 若調整，兩邊會一起走。

   ⚠ 懸浮鈕原本是 `hsl(var(--surface-dark))`（深灰，當初是為了壓在淺藍圓底上
     算過 7.71:1 對比）。改成 .75 之後壓在 AERIAL 藍底上實測仍有 5.6:1，
     超過 AA 的 4.5:1，可讀性沒問題。
   ⚠ **只改圖示顏色，藍色圓底不動**——客戶說的是「icon 太黑」與「也改一樣」，
     指的是顏色；先前那輪「按鈕 icon 不要藍底」講的是分享面板那組，
     懸浮鈕的藍底是 2026-07-30 客戶親自指定的，沒有被推翻。
   ⚠ hover 一併給：兩處原本 hover 都會把圖示換回深色，不跟著改的話會變成
     「靜置灰、滑過突然變黑」。 */
.share-pop-item,
.is-designed .wf-float a,
.is-designed .wf-float-mail-toggle {
    color: hsl(var(--foreground) / .75);
}

.share-pop-item:hover,
.share-pop-item:focus-visible,
.is-designed .wf-float a:hover,
.is-designed .wf-float a:focus-visible,
.is-designed .wf-float-mail-toggle:hover,
.is-designed .wf-float-mail-toggle:focus-visible {
    color: hsl(var(--foreground) / .75);
}

/* LINE 圖示的泡泡：鏡射用的座標基準寫明，不依賴通用規則 ──────────────────
   `wf_icon('brand-line')` 的泡泡帶 `transform="scale(-1,1)"`（客戶指定尾巴在
   右下，見 app/Common.php 該顆的註解）。這個鏡射要「繞自身中心、原地翻轉」
   才對，靠的是 `transform-box: fill-box` ＋ `transform-origin: center`。

   ⚠ 這兩個值本檔 ≈3719 的 `.wf-icon > *` 已經給了（原本是為圖示 hover 動畫
     設的），所以不寫這條其實也會動。**這裡刻意重述一次**：那條規則的用途是
     動畫，日後若有人調整它的 transform-origin，這顆圖示會跟著翻到錯的位置，
     而且是靜默出錯（圖案還在、只是跑掉）。把依賴寫明，就不會被連坐。
   ⚠ **不要改回 SVG 規格的 `translate(24,0) scale(-1,1)`**：在
     `transform-box: fill-box` 之下那個寫法的位移基準不是 viewBox 原點，
     實測會把泡泡整個推出框外（CTM 的 e 量到 44 而不是 22）。 */
.wf-icon--brand-line .line-bubble {
    transform-box: fill-box;
    transform-origin: center;
}

/* ═══════════════════════════════════════════════════════════════════════
   顧問案鈕與訂閱鈕：改框線不填色，hover 用「畫線連線」的動態
   （2026-08-31 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「首頁的顧問案按鈕改框線不填色，hover 改線條畫線連線的動態效果，
   訂閱鈕也用一樣的呈現方式」。兩顆是同一個新外觀，所以寫在同一節、同一組
   選擇器，日後要調只有一個地方。

   ⚠ 這兩顆**脫離**同日稍早定案的「全站按鈕統一 hover 淺灰」——客戶對這兩顆
     另外指定了畫線動態，是新決定覆蓋舊的統一，不是打架。其餘按鈕不動。

   ── 為什麼用 inset box-shadow 當框線，不用 border ────────────────────
   兩顆的尺寸都是內容＋padding 撐出來的（訂閱鈕另有 min-height:44px）。
   直接加 border 會讓顧問案鈕高 48→50、訂閱鈕寬各 +2px，四周對齊線全部要
   重算（本檔 ≈7100 那段「外框不是拿掉、是與底色同色」正是為了避開這件事）。
   `box-shadow: inset` 不參與版面計算，**尺寸一個像素都不會變**，而且
   pseudo-element 用 `inset: 0` 就剛好壓在框線上（沒有 border 時
   padding box ＝ border box）。

   ── 圓角統一收成 0 ────────────────────────────────────────────────
   顧問案鈕與浮動面板的訂閱鈕本來就是 0，只有 footer 那顆是 2px。客戶要
   「一樣的呈現方式」，而且描線是直線、在 2px 圓角處會露出小缺口，所以
   一起收成 0。

   ── hover：兩條線從對角出發，在另外兩角會合 ─────────────────────────
   `::before` 走「上 → 右」，`::after` 走「下 → 左」，各自用兩層
   linear-gradient 當線，靠 background-size 從 0 長到滿。兩層在同一組
   keyframes 裡分段長（0–50% 第一層、50–100% 第二層），所以是「先畫一條、
   再接著畫下一條」的連線感，不是四邊同時浮現。
   ⚠ 用 background-size 而不是 transform: scale：scale 會把 1px 的線一起
     縮放，動畫中途會糊掉（這個站對細線特別敏感，見 mask-willchange 那組
     註解）。background-size 長出來的線每一格都是實心 1px。
   ⚠ 動畫用 forwards：hover 結束時直接收回（沒有反向動畫）。要做反向就得
     再加一組 `:not(:hover)` 的規則，那會在頁面載入時就播一次，反而更怪。
   ⚠ 只在 `(hover: hover)` 的裝置跑；鍵盤 focus-visible 一樣觸發，不然
     只有滑鼠使用者看得到狀態變化。 */
.svc-feature-tail,
.footer-submit {
    position: relative;
    background: transparent;
    color: hsl(var(--heading));
    border-radius: 0;
    box-shadow: inset 0 0 0 1px hsl(var(--slate));
}

/* 舊的 hover 換底色規則（≈7095、≈30268 那幾條）對這兩顆一律失效——
   底色永遠透明，狀態改由描線表達。 */
.svc-feature:hover .svc-feature-tail,
.svc-feature:focus-visible .svc-feature-tail,
.footer-submit:hover,
.footer-submit:focus-visible {
    background: transparent;
    color: hsl(var(--heading));
}

/* ⚠ 四條邊全部塞進**同一個** ::before，不用 ::before ＋ ::after 各兩條：
     `.footer-submit::after` 早就被佔用了——它是按鈕文字後面那支箭頭
     （≈2852 `content:"→"`，≈5441 又改寫成逸出碼 `\2192`）。第一版我把
     ::after 拿來畫線，等於把 content 覆寫成空字串，**箭頭就消失了**：
     實測按鈕寬度從 97 掉到 75px（差的 22px 就是那支箭頭）。
     ⚠ 這個症狀很容易誤判：寬度變窄看起來像「padding 被吃掉」，要把
       ::after 的 content 一起讀出來才看得到真正的原因。
   ⚠ 四層 background 一樣做得出「兩條線從對角出發、在另外兩角會合」：
     0–50% 上邊（由左往右）與下邊（由右往左）同時長，50–100% 右邊（由上
     往下）與左邊（由下往上）接著長，剛好接在對方停下來的那個角上。 */
.svc-feature-tail::before,
.footer-submit::before {
    content: '';
    position: absolute;
    inset: 0;
    pointer-events: none;
    background-repeat: no-repeat;
    background-image:
        linear-gradient(hsl(var(--heading)), hsl(var(--heading))),   /* 上 */
        linear-gradient(hsl(var(--heading)), hsl(var(--heading))),   /* 右 */
        linear-gradient(hsl(var(--heading)), hsl(var(--heading))),   /* 下 */
        linear-gradient(hsl(var(--heading)), hsl(var(--heading)));   /* 左 */
    background-position: left top, right top, right bottom, left bottom;
    background-size: 0 1px, 1px 0, 0 1px, 1px 0;
}

@media (hover: hover) {
    .svc-feature:hover .svc-feature-tail::before,
    .footer-submit:hover::before {
        animation: wfBtnLineDraw .55s ease-out forwards;
    }
}

.svc-feature:focus-visible .svc-feature-tail::before,
.footer-submit:focus-visible::before {
    animation: wfBtnLineDraw .55s ease-out forwards;
}

@keyframes wfBtnLineDraw {
    0%   { background-size: 0 1px,    1px 0,    0 1px,    1px 0; }
    50%  { background-size: 100% 1px, 1px 0,    100% 1px, 1px 0; }
    100% { background-size: 100% 1px, 1px 100%, 100% 1px, 1px 100%; }
}

/* 系統關掉動態時：不要跑描線，直接把框線本身加深當作 hover 回饋
   （完全沒有回饋比較糟，全站既有慣例是「拿掉動畫、留下狀態」）。 */
@media (prefers-reduced-motion: reduce) {
    .svc-feature:hover .svc-feature-tail::before,
    .footer-submit:hover::before,
    .svc-feature:focus-visible .svc-feature-tail::before,
    .footer-submit:focus-visible::before {
        animation: none;
    }

    .svc-feature:hover .svc-feature-tail,
    .footer-submit:hover,
    .footer-submit:focus-visible {
        box-shadow: inset 0 0 0 1px hsl(var(--heading));
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   浮動鈕要在 member 頁出現 ＋ 版權文字加粗（2026-08-31 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶逐項回覆待決清單：「1 浮動按鈕要出現」「3 版權文字太細請跟隱私權政策
   粗度一樣」（其餘 2／4～8 明確不改）。

   ── ① 淡出時間 200ms → 140ms ────────────────────────────────────────
   ⚠ **這條跟 site.js 的改動是一組的，不能只改一邊。** site.js 把提前量從
     240px 歸零（改成「浮動鈕下緣真的要被 footer 碰到」才收），提前量沒了，
     淡出就必須更快跑完，否則會回到最早那個「8～18% 透明度的淡影疊在
     footer 上」的問題。
   ⚠ 140ms 是算的：判定點到完全重疊之間的距離＝浮動鈕自己的高度（桌機
     152px）；152px ÷ 140ms ＝ 1086px/s，仍高於一般捲動速度。
   ⚠ 只改這個元件的過場，不動 --transition 那顆全站 token。 */
.wf-float {
    transition:
        opacity 140ms var(--ease-out, ease),
        transform 140ms var(--ease-out, ease),
        visibility 0s;
}

.has-footer-in-view .wf-float {
    transition:
        opacity 140ms var(--ease-out, ease),
        transform 140ms var(--ease-out, ease),
        visibility 0s 140ms;
}

/* ── ② 版權文字：跟「隱私權政策」同一個字體 ──────────────────────────
   客戶說「太細，請跟隱私權政策粗度一樣」。

   ⚠ **實測後發現這不是字重問題**：兩者的 font-weight／font-size／color
     完全相同（都是 350／12px／`rgba(26,26,26,.45)`）。差在**字體**：
         版權       var(--font-eyebrow) = "Julius Sans One", Inter, Helvetica…
         隱私權政策  var(--font-body)    = "Noto Sans TC", "PingFang TC", …
     `--font-eyebrow` 是英文標題用的細體，**整個堆疊裡沒有任何中文字型**，
     所以「世翠蹟顧問有限公司 一天聽一點」這串中文是掉到瀏覽器的預設
     sans-serif 去渲染的，比隔壁 Noto Sans TC 的同字重明顯細一階。
   → 改成跟隱私權政策同一個 `--font-body`，兩者就是逐項相同的字體、字重、
     字級、顏色，粗度必然一致。**只改 font-family，字重那條原樣不動**——
     問題不在那裡，動它反而會讓這一行比隔壁粗。
   ⚠ 代價（已評估）：「© 2026」那幾個字也會從 Julius Sans One 換成
     Noto Sans TC。客戶要的是整行跟隔壁一致，這是必然的一部分；若之後想
     保留數字的英文標題感，可改成 `"Julius Sans One", var(--font-body)`
     ——中文吃 Noto、數字仍是細體，但那樣中英就不同粗細了。
   ⚠ letter-spacing 維持 .04em 不動：客戶說的是粗度，不是字距。 */
.footer-copy {
    font-family: var(--font-body);
}

/* ═══════════════════════════════════════════════════════════════════════
   懸浮鈕：三顆統一、放大、脈動＋光暈；FB 圖示視覺補正（2026-08-31 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「fb 放大一點，懸浮按鈕信封那顆改跟前兩顆一樣白底灰線，然後這三顆
   按鈕放大，並加上一直放大縮小的動態效果，然後外圍加光暈像發光一樣」

   ── 改動前實測 ─────────────────────────────────────────────────────────
     YouTube／Podcast  44×44　白底 rgba(246,246,247,.88)　1px 灰框　圖示 18
     信封              44×44　**AERIAL 藍實心**　無框　圖示 22　**還多一層陰影**
   信封那顆的藍底是 2026-07-30 客戶親自指定的，這次客戶明講要跟前兩顆一樣，
   所以連同「無框」與「多出來的陰影」一起對齊——只留一顆有陰影會變成三顆裡
   唯一浮起來的那個。

   ── FB 為什麼要另外放大 ────────────────────────────────────────────────
   實測 footer 四顆圖示的內容 bbox：IG 是 20×20、FB 是 20×19.9，**幾何上一樣大**。
   但 FB 是**實心圓**、IG 是**外框方塊**：圓形只佔外接正方形的 π/4≈78% 面積，
   四個角是空的，所以同樣 bbox 看起來就是小一號。這是視覺補正不是修 bug，
   所以用 transform 放大 1.14 倍（不動 wf_icon 的 viewBox，其餘 16 顆不受影響）。
   ⚠ 分享面板那顆 FB 也會一起變大——同一顆 SSOT，而且那一排的 LINE／手機／
     信封同樣是外框圖示，有一樣的視覺落差，一起補正才對。

   ── 動態 ───────────────────────────────────────────────────────────────
   ⚠ 脈動與光暈綁在同一組 keyframes：分成兩組會各自跑各自的相位，看起來像
     兩個不同步的效果。
   ⚠ **hover／focus 時停止動畫**：會動的東西是移動中的點擊目標，滑鼠靠近時
     還在縮放很難按（尤其是手抖或觸控板）。停在放大那一格，順便當作 hover 回饋。
   ⚠ **電子報面板展開時整組停止**：那時使用者正在輸入 email，旁邊有東西一直
     呼吸很干擾。
   ⚠ prefers-reduced-motion 直接關掉動畫，只留靜態的光暈——那類使用者對
     持續性動態特別敏感，這種「一直動」的效果是最典型要關掉的一種。
   ⚠ 光暈用 box-shadow 不用外框元素：三顆各自是圓形按鈕，box-shadow 會跟著
     border-radius 走，不必再算一層 ::after 的位置；而且不進版面流、不會把
     旁邊的元素推開（這一組是 fixed 定位，推開會連帶影響 .wf-rail 的淨空）。 */

@keyframes wf-float-breathe {
    0%, 100% {
        transform: scale(1);
        box-shadow: 0 0 0 0 hsl(var(--accent-blue) / .5),
                    0 0 10px 2px hsl(var(--accent-blue) / .35);
    }
    50% {
        transform: scale(1.07);
        box-shadow: 0 0 0 6px hsl(var(--accent-blue) / 0),
                    0 0 22px 7px hsl(var(--accent-blue) / .55);
    }
}

/* 三顆一致：尺寸放大 44 → 56，圖示 22，白底灰線
   ⚠ 這條會蓋掉 992–1199.98 既有的 52px（≈L27246）——同為 (0,2,0)、我在後面。
     那一階原本就比基底 44 大一號，統一成 56 之後只差 4px，客戶要的是「三顆
     都放大」而不是保留階梯，所以刻意收斂成一個值。要復原那一階，把
     ≈L27246 那組的 52/21 改成 !important 或搬到本節後面即可。
   ⚠ 手機（≤767.98）另外壓回 52：那個斷點三顆是**橫排**，56×3＋間距在 390 寬
     會佔掉 176px（近半個螢幕），實測會多蓋住搜尋頁的一張卡片標題——那是
     放大前沒有的。52 一樣是「比原本的 44 大」，但不會新增遮擋。見本節末。 */
.is-designed .wf-float a,
.is-designed .wf-float .wf-float-mail-toggle {
    width: 56px;
    height: 56px;
    border: 1px solid hsl(var(--slate) / .5);
    background: hsl(var(--background) / .88);
    color: hsl(var(--foreground) / .75);
    animation: wf-float-breathe 2.8s ease-in-out infinite;
}

.is-designed .wf-float a svg,
.is-designed .wf-float .wf-float-mail-toggle svg {
    width: 22px;
    height: 22px;
}

/* hover／focus：停在放大那一格，點擊目標不再移動 */
.is-designed .wf-float a:hover,
.is-designed .wf-float a:focus-visible,
.is-designed .wf-float .wf-float-mail-toggle:hover,
.is-designed .wf-float .wf-float-mail-toggle:focus-visible {
    animation-play-state: paused;
    transform: scale(1.07);
    border-color: hsl(var(--slate));
    color: hsl(var(--foreground));
}

/* 電子報面板展開時整組停下來 */
.is-designed .wf-float:has(.wf-float-panel:not([hidden])) a,
.is-designed .wf-float:has(.wf-float-panel:not([hidden])) .wf-float-mail-toggle {
    animation-play-state: paused;
    transform: none;
}

@media (prefers-reduced-motion: reduce) {
    .is-designed .wf-float a,
    .is-designed .wf-float .wf-float-mail-toggle {
        animation: none;
        transform: none;
        box-shadow: 0 0 14px 3px hsl(var(--accent-blue) / .4);
    }
    .is-designed .wf-float a:hover,
    .is-designed .wf-float a:focus-visible,
    .is-designed .wf-float .wf-float-mail-toggle:hover,
    .is-designed .wf-float .wf-float-mail-toggle:focus-visible {
        transform: none;
    }
}

/* 手機：橫排三顆，56 太寬會多蓋內容，收到 52（仍大於原本的 44）。
   實測 390 寬：56 時搜尋頁多蓋住一張卡片的標題與縮圖，52 則與放大前一致。 */
@media (max-width: 767.98px) {
    .is-designed .wf-float a,
    .is-designed .wf-float .wf-float-mail-toggle {
        width: 52px;
        height: 52px;
    }

    .is-designed .wf-float a svg,
    .is-designed .wf-float .wf-float-mail-toggle svg {
        width: 21px;
        height: 21px;
    }
}

/* FB 實心圓的視覺補正（見本節開頭的說明） */
.wf-icon--brand-facebook {
    transform: scale(1.14);
}

/* ═══════════════════════════════════════════════════════════════════════
   分享面板改版：卡片式（2026-08-31 客戶指定，附參考圖）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「分享懸浮視窗版型改這樣，多一個分享連結欄位，底下才是軟體分享」。
   參考圖由上到下：圓形鏈結圖示 → 標題＋副標 → 連結欄位（唯讀網址＋複製鈕）
   → 「分享到」→ 一排圓形軟體圖示（每顆底下有名稱）。
   原本是一條膠囊（border-radius:999px）裡面排四顆圖示，沒有連結欄位。

   ── 兩處刻意不照抄參考圖，改用站上既有語彙 ────────────────────────────
   ① **圓角**：參考圖是大圓角卡片（看起來 20px 以上）。站上今天才把按鈕統一
      成直角、chip 是 2px，突然出現一張大圓角卡片會是全站唯一一個。這裡用
      --radius-media（實測 2px），跟 .wf-float-panel 等既有浮出面板同一個值。
   ② **連結欄位**：參考圖是淺灰填色的圓角輸入框。但同一天客戶才指定
      「全站只要有輸入匡統一改底線的形式呈現，跟會員登入一樣」——這裡再做一個
      填色框會直接違反那條。所以用底線式，與全站其他 16 個輸入框一致。
   兩點都是一行就能換回參考圖的做法，客戶要照抄再說。

   ⚠ 鏈結圖示放在卡片**內部**頂端置中，不是像參考圖那樣騎在卡片上緣：面板是
     absolute、緊貼在「分享」按鈕下方 8px，圖示往上凸出去會直接壓到那顆按鈕。
   ⚠ 圓形軟體圖示鈕沿用既有的 .share-pop-item（AERIAL 藍底，2026-07-30 與
     08-28 客戶各確認過一次「圓形圖示鈕維持藍」），只在底下補上名稱。
   ⚠ 面板寬度用 min(20rem, 100vw-2rem)：390 手機不會超出視窗。
   ⚠ .share-pop[hidden]{display:none} 是既有規則且特異度 (0,2,0) 高於這裡的
     display，不必擔心被蓋掉；換句話說**這裡不能改用 !important 的 display**，
     那會讓面板關不起來。 */
/* ⚠ 背景要補回實色——這裡跟 2026-08-28 客戶「視窗不要影子跟白色背景純框線」
   有衝突，說明一下怎麼取捨的：
     那次的面板是一條**只有四顆圖示的小膠囊**，透明底看得到後面的頁面底色，
     沒有問題。這次改成**蓋在文章內文上的卡片**（高 334px），透明底實測會讓
     底下的段落文字整片透出來、面板內容完全讀不了（截圖可證）。那不是風格
     偏好，是壞掉。
   ⚠ 取捨：**補回實色底（客戶這次的參考圖本來就是白卡）＋ 保留「純框線、
     不加陰影」**。也就是三件事裡只推翻「不要白色背景」那一件，另外兩件
     （純框線、無影子）照舊。要完全照 08-28 那則就把 background 這行刪掉，
     但面板會變回讀不了的狀態。
   ⚠ 用 hsl(var(--muted)) 不寫死 #fff：那顆就是站上的純白 token，
     .form-panel／.wf-float-panel 等浮出面板都用它。 */
.share-pop {
    display: block;
    width: min(20rem, calc(100vw - 2rem));
    padding: 1.5rem 1.375rem 1.25rem;
    border-radius: var(--radius-media);
    background: hsl(var(--muted));
    text-align: left;
}

.share-pop-badge {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 2.75rem;
    height: 2.75rem;
    margin: 0 auto .75rem;
    border: 1px solid hsl(var(--slate) / .4);
    border-radius: 50%;
    color: hsl(var(--foreground) / .75);
}

.share-pop-badge svg {
    width: 20px;
    height: 20px;
}

.share-pop-close {
    position: absolute;
    top: .5rem;
    right: .5rem;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 1.75rem;
    height: 1.75rem;
    padding: 0;
    border: 0;
    background: transparent;
    color: hsl(var(--slate));
    font-size: 13px;
    line-height: 1;
    cursor: pointer;
    transition: color var(--transition);
}

.share-pop-close:hover,
.share-pop-close:focus-visible {
    color: hsl(var(--heading));
}

.share-pop-title {
    margin: 0;
    text-align: center;
    font-size: var(--text-h3, 1.125rem);
    line-height: 1.5;
    font-weight: var(--fw-heading);
    letter-spacing: var(--tracking-title);
    color: hsl(var(--heading));
}

.share-pop-sub {
    margin: .375rem 0 1.25rem;
    text-align: center;
    font-size: var(--text-meta);
    line-height: 1.7;
    color: hsl(var(--muted-foreground));
}

/* 「分享連結」「分享到」兩個小標＝站上既有的欄位標籤語彙（.form-label 同一組值） */
.share-pop-label {
    margin: 0 0 .5rem;
    font-size: var(--text-meta);
    line-height: var(--lh-meta);
    font-weight: 400;
    letter-spacing: var(--tracking-meta);
    color: hsl(var(--slate));
}

/* 連結欄位：底線式（見本節開頭②） */
.share-pop-link {
    display: flex;
    align-items: center;
    gap: .5rem;
    margin-bottom: 1.25rem;
    border-bottom: 1px solid hsl(var(--foreground) / .18);
}

.share-pop-url {
    flex: 1 1 auto;
    min-width: 0;
    padding: .5rem 0;
    border: 0;
    background: transparent;
    font-family: var(--font-body);
    font-size: var(--text-meta);
    line-height: 1.6;
    letter-spacing: var(--tracking);
    color: hsl(var(--heading));
    text-overflow: ellipsis;
}

.share-pop-url:focus {
    outline: none;
}

.share-pop-link:focus-within {
    border-bottom-color: hsl(var(--foreground));
}

.share-pop-copy {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 1.75rem;
    height: 1.75rem;
    padding: 0;
    border: 0;
    background: transparent;
    color: hsl(var(--foreground) / .75);
    cursor: pointer;
    transition: color var(--transition);
}

.share-pop-copy svg {
    width: 17px;
    height: 17px;
}

.share-pop-copy:hover,
.share-pop-copy:focus-visible {
    color: hsl(var(--accent-blue-text));
}

/* 「已複製」回報：佔位不留白（hidden 時 display:none），出現時貼在欄位下方 */
.share-pop-copied {
    display: block;
    margin: -1rem 0 1rem;
    font-size: var(--text-meta);
    line-height: var(--lh-meta);
    letter-spacing: var(--tracking-meta);
    color: hsl(var(--accent-blue-text));
}

.share-pop-copied[hidden] {
    display: none;
}

/* 軟體那一排：四顆平均分配，圖示鈕維持既有圓形藍底，底下補名稱 */
.share-pop-apps {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: .5rem;
}

.share-pop-item {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: .375rem;
    text-decoration: none;
}

.share-pop-item-label {
    font-size: 10px;
    line-height: 1.4;
    letter-spacing: var(--tracking-meta);
    color: hsl(var(--slate));
    text-align: center;
}

.share-pop-item:hover .share-pop-item-label,
.share-pop-item:focus-visible .share-pop-item-label {
    color: hsl(var(--heading));
}

/* ═══════════════════════════════════════════════════════════════════════
   懸浮鈕：第三顆對齊前兩顆、光暈改白色（2026-08-31 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶（附截圖）：「懸浮按鈕對齊，光暈用白色」。上一節（≈30809 放大＋脈動
   光暈那組）**一個字都沒改**，這裡用檔尾覆寫接手。

   ── ① 對齊：信封那顆右移 6px 的真正原因 ─────────────────────────────
   不是按鈕自己的定位問題，是**外層包裝盒沒跟著放大**。實測（mtest 1440）：

     .wf-float（容器）          56 寬
     直屬子 1／2  <a>           56×56  offsetLeft 0
     直屬子 3  .wf-float-mail   **44×44**  offsetLeft 6   ← 只有它還是舊尺寸
        └ 孫  .wf-float-mail-toggle  56×56（自己有被放大）

   `.wf-float` 是 `align-items: center`，44 寬的包裝盒在 56 的容器裡被置中，
   於是它自己往右 (56−44)/2 = 6px，裡面那顆 56 的按鈕就跟著右移 6px。
   ⚠ 成因是**既有規則列了三個選擇器、放大時只搬了兩個**：≈27139 那條
     `.wf-float a, .wf-float .wf-float-mail, .wf-float .wf-float-mail-toggle`
     三個一起釘 44px（那輪的註解甚至寫明「外層 .wf-float-mail 被鄰居撐寬成
     55×46 橢圓」是當年的病因之一），但 08-31 放大那組只寫了 `a` 與
     `-toggle`，**漏掉中間那個包裝盒**。
   ⚠ 修法**不寫死尺寸**，改成讓包裝盒自己貼合裡面的按鈕（`width/height:auto`
     ＋ `flex:none`）。三個斷點（基底 56／992–1199 也是 56／≤767.98 是 52）
     只要按鈕改多大，它就跟著多大，不必再維護第二份數字——這個坑就是「同一
     組尺寸散在多處、改的時候漏一個」造成的，再補一份寫死值只會再犯一次。
   ⚠ 面板（.wf-float-panel）是絕對定位，不參與包裝盒的尺寸計算，所以 auto
     不會被面板撐開（實測面板開著時包裝盒仍是 56×56）。

   ── ② 光暈改白色 ───────────────────────────────────────────────────
   原本兩段光暈都是 `--accent-blue`（AERIAL 藍）。改成純白：重新宣告**同名**
   keyframes（後宣告的整組取代先宣告的），上一節那份留著不動當沿革。
   ⚠ reduced-motion 那條的靜態光暈也要一起改白，否則關掉動態的使用者會看到
     跟一般人不同顏色的光暈（那是最容易漏的一格，因為平常不會切到）。
   ⚠ 白光暈在白底頁面上會幾乎看不見，只有壓在灰底／照片上才明顯——這是客戶
     指定的顏色，不是實作問題；若之後回報「看不到光暈」，答案在這裡。 */
.is-designed .wf-float .wf-float-mail {
    width: auto;
    height: auto;
    flex: none;
}

@keyframes wf-float-breathe {
    0%, 100% {
        transform: scale(1);
        box-shadow: 0 0 0 0 hsl(0 0% 100% / .5),
                    0 0 10px 2px hsl(0 0% 100% / .35);
    }
    50% {
        transform: scale(1.07);
        box-shadow: 0 0 0 6px hsl(0 0% 100% / 0),
                    0 0 22px 7px hsl(0 0% 100% / .55);
    }
}

@media (prefers-reduced-motion: reduce) {
    .is-designed .wf-float a,
    .is-designed .wf-float .wf-float-mail-toggle {
        box-shadow: 0 0 14px 3px hsl(0 0% 100% / .4);
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   2026-08-31 第二輪：分享面板改回精簡橫列、提示泡泡圓角、報名中標籤改淺灰
   ═══════════════════════════════════════════════════════════════════════
   客戶（同一則訊息四件事）：
     ①「courses/response 報名中的標籤請用跟首頁一樣的樣式不要黑色」＋淺灰 chip 圖
     ②「分享懸浮視窗改圖片這樣，精簡一點」＋橫列圓鈕＋hover 名稱的參考圖
     ③「已收藏按完的懸浮視窗用圓角不要方形」
     ④「分享懸浮視窗不要有白色背景」

   ⚠ ②④ 等於把同一天稍早的卡片版收回去。那一版的規則沒有刪（在本檔前面
     ≈30933 那節），這裡用檔尾覆寫把它壓回橫列——維持本檔「誰做的那節留給
     誰、要退回只刪覆寫節」的既有慣例。卡片版完整內容在 commit e888d68。
   ⚠ ④ 回到 2026-08-28 客戶原本就指定過的「不要白色背景、純框線」。稍早
     卡片版之所以補回白底，是因為 334px 的卡片透明會讓底下內文透出來讀不了；
     現在收回成一條矮膠囊，那個問題不存在，所以照客戶的原意拿掉白底。 */

/* ── ② ④ 分享面板：卡片 → 橫列膠囊，無白底 ──────────────────────────── */
.share-pop {
    display: flex;
    align-items: center;
    gap: .375rem;
    width: auto;
    padding: .375rem;
    border-radius: 999px;
    background: transparent;
    text-align: center;
}

/* 每一顆：圓形圖示鈕，名稱改成 hover／focus 才浮出（參考圖就是這樣）。
   ⚠ position:relative 是提示泡泡的定位基準。
   ⚠ 用 button 或 a 都吃同一組樣式：第一顆「複製連結」是 <button>（它不導向
     任何地方，做成連結對讀屏是騙人的），其餘四顆是真的 <a>。 */
.share-pop-item {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-direction: row;
    gap: 0;
    width: 2.5rem;
    height: 2.5rem;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: transparent;
    color: hsl(var(--foreground) / .75);
    cursor: pointer;
    transition: background var(--transition), color var(--transition);
}

.share-pop-item:hover,
.share-pop-item:focus-visible {
    background: hsl(var(--foreground) / .06);
    color: hsl(var(--heading));
}

.share-pop-item-icon {
    display: flex;
    align-items: center;
    justify-content: center;
}

.share-pop-item-icon svg {
    width: 20px;
    height: 20px;
}

/* 名稱提示：浮在該顆正上方，hover／focus 才出現。
   ⚠ 顏色語彙沿用站上既有的 .act-tip（白底＋細框＋深字），**不照抄參考圖的
     深色泡泡**：同一頁上「已收藏」那顆提示就是白底細框，兩種提示泡泡長不一樣
     會變成兩套語彙。客戶這則同時要求 .act-tip 改圓角，兩顆一起圓角就對齊了。
   ⚠ white-space:nowrap ＋ transform 置中：名稱只有二到四個字，不需要換行；
     置中對齊該顆圓鈕的中心，不是靠左。
   ⚠ pointer-events:none：泡泡浮在圓鈕上方，讓它吃不到滑鼠，否則游標移上去
     會被泡泡接走、hover 掉了泡泡就消失，變成閃爍。 */
.share-pop-tip {
    position: absolute;
    bottom: calc(100% + .375rem);
    left: 50%;
    transform: translateX(-50%) translateY(.25rem);
    padding: .25rem .5rem;
    border: 1px solid hsl(var(--slate));
    border-radius: var(--radius-tip, 8px);
    background: hsl(var(--muted));
    color: hsl(var(--heading));
    font-size: var(--text-meta);
    line-height: 1.5;
    letter-spacing: var(--tracking-meta);
    white-space: nowrap;
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transition: opacity var(--transition), visibility var(--transition), transform var(--transition);
}

.share-pop-item:hover .share-pop-tip,
.share-pop-item:focus-visible .share-pop-tip {
    opacity: 1;
    visibility: visible;
    transform: translateX(-50%) translateY(0);
}

/* ── ③ 「已收藏」提示泡泡改圓角 ─────────────────────────────────────────
   客戶「已收藏按完的懸浮視窗用圓角不要方形」。那顆現在是直角，是 2026-08-31
   全站按鈕系統那輪把它一起收成 border-radius:0 的（≈29726）。
   ⚠ 用 --radius-tip 這顆新變數而不是散寫 8px：上面分享的名稱泡泡是同一種
     東西，兩顆共用一個值，日後客戶說「再圓一點」只要改一處。
   ⚠ 只改圓角，不動它的白底／細框／字色——客戶這次只講形狀。 */
:root {
    --radius-tip: 8px;
}

.act-tip {
    border-radius: var(--radius-tip);
}

/* ── ① 課程內頁「報名中／早鳥」標籤改成跟首頁標籤一樣的淺灰 ──────────────
   客戶「報名中的標籤請用跟首頁一樣的樣式不要黑色」＋淺灰 chip 的截圖。
   原本是 background:--foreground（純黑）＋ color:--background（近白），
   實測是整頁唯一一顆黑色實心 chip。改成 2026-08-31 定案的「標籤＝淺灰」
   （--surface-grey #DCDDE0 ＋ 深色字），與首頁 #標籤 同一組值。
   ⚠ 圓角維持 --radius-pill 不動：客戶說的是「不要黑色」「用跟首頁一樣的
     樣式」，指的是配色；那顆 chip 的形狀本來就跟首頁標籤一樣。
   ⚠ 特異度要 (0,2,0) 以上：既有那條是 .course-detail--editorial .cd-hero-stat。 */
.course-detail--editorial .cd-hero-stat {
    background: hsl(var(--surface-grey));
    color: hsl(var(--heading));
}

/* ═══════════════════════════════════════════════════════════════════════
   知識專區：左欄標籤改淺灰、小卡分類／標籤改框線描（2026-08-31 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「左邊的分類跟標籤都是膚色底，標籤用淺灰色底，右邊小卡還沒選到的
   標籤跟分類用框線描，選到一樣是填色……小卡現在的標籤跟分類用框線描
   hover 分類按鈕膚色，標籤淺灰色」。

   等於把「分類＝膚色／標籤＝淺灰」這組區辨**貫徹到左欄與小卡兩邊**，並讓
   小卡未選中的狀態從「純文字」升級成「框線描」。上一輪（≈30196 那節，
   同日稍早客戶要的「預設純文字、選中才填色」）**一個字都沒改**，這裡只加
   框線與 hover 填色兩件事。

   ── ① 左欄標籤的選中態：膚色 → 淺灰 ──────────────────────────────────
   ⚠ 左欄「分類」與「標籤」的標記不同：分類是純 `<a class="is-active">`、
     標籤才是 `.tag-pill.is-active`（用 document.styleSheets 逐條比對確認）。
     所以只要鎖 `.kz-tags .filter-tags .tag-pill.is-active` 就只會動到標籤，
     分類那排維持膚色，不必再加排除條件。
   ⚠ 真正在管桌機選中態的是 `.page--knowledge .kz-tags .tag-pill.is-active`
     那條 (0,3,0)（同時還設了負 margin 與 padding 做「方塊底」出血），
     **這裡只覆寫 background，不碰它的幾何**，否則方塊底的出血量會跑掉。
   ⚠ 限定 `@media (min-width: 1024px)`：≤1199.98 有一整套 chip 版型
     （≈26506 起，選中態用 `--muted` ＋ 深框，不是膚色），那是另一個設計，
     客戶這次講的是桌機左欄，不要順手改掉。

   ── ② 小卡未選中：加框線 ────────────────────────────────────────────
   上一輪把 border-color 設成 transparent（純文字）。客戶現在要「框線描」，
   所以只把顏色換回看得見的線，**寬度／內距一律沿用**（那條 border 一直都
   在，只是透明），所以卡片版面一個像素都不會位移。
   ⚠ 線色用 `hsl(var(--slate) / .5)`：跟懸浮鈕的框線同一個值，不新增色票。

   ── ③ 小卡 hover：分類膚色、標籤淺灰 ────────────────────────────────
   上一輪的 hover 是「維持透明、只把字加深」。客戶改成 hover 就填色，而且
   兩種各自填自己的顏色——跟選中態同色系，等於 hover 是「預覽選中後的樣子」。
   ⚠ 這裡刻意**不動選中態**（≈30238／≈30246 那兩條），客戶說「選到一樣是
     填色」＝維持現狀。
   ⚠ 特異度：既有 hover 是 `.pcard-tags .wf-card-cat:not(.is-active):hover`
     (0,4,0)，本節同分、位置在後取勝；而更早那條泛用的
     `.pcard-tags .tag-pill:not(.is-active):hover` 也同分，同樣被壓在後面。
   ⚠ 文章內頁「推薦文章」那區的卡片用的是 `.pcard-tags.kd-related-tags`，
     **同一組選擇器就涵蓋到了**（實測兩頁的標籤父層都是 .pcard-tags），
     客戶要的「小卡」兩處一致，不必分開寫。 */
/* ⚠ 2026-08-31 客戶追加：「左邊的灰色請跟右邊的灰色一樣」（附截圖）。
   **兩邊的色碼本來就一樣**——左欄選中、小卡選中、hover 三種狀態、mtest 與
   beta 都量到 `rgb(220,221,224)`，也都壓在白底上（用截圖取像素再確認一次，
   不是只看 computed）。差別在**面積**：左欄那條是 364×60 的滿版色塊，小卡
   標籤只有 68×40，同一個灰鋪在大面積上就是會看起來比較重。
   → 用選項題跟客戶確認方向，客戶選「左邊的灰再淡一階」。
   ⚠ 只淡左欄、小卡不動：客戶要的是兩者「看起來」一致，不是把兩邊都改掉。
   ⚠ 色值沿用 --surface-grey 的色相與彩度（225 6%），只把明度 87% → 91%，
     所以它仍然是同一族的灰，不是另外挑一個顏色。 */
@media (min-width: 1024px) {
    .page--knowledge .kz-tags .filter-tags .tag-pill.is-active,
    .page--knowledge .kz-tags .filter-tags .tag-pill.is-active:hover,
    .page--knowledge .kz-tags .filter-tags .tag-pill.is-active:focus-visible {
        background: hsl(225 6% 91%);
        color: hsl(var(--heading));
    }
}

/* ⚠ 第一版只寫了 `.pcard-tags .tag-pill:not(.is-active)`（0,3,0），**沒生效**：
     知識列表另有一條 `.page--knowledge .kz-grid .pcard-tags .tag-pill
     { border-color: transparent }`＝(0,4,0) 壓在上面（用 document.styleSheets
     逐條比對才找到，computed 一直是 rgba(0,0,0,0)）。所以第一條選擇器把
     `.page--knowledge .kz-grid` 也帶上湊到 (0,5,0)；後面兩條負責文章內頁的
     推薦文章卡與其他頁的 .pcard。 */
.page--knowledge .kz-grid .pcard-tags .tag-pill:not(.is-active),
.kd-related-tags.pcard-tags .tag-pill:not(.is-active),
.pcard-tags .tag-pill:not(.is-active),
.pcard-tags .wf-card-cat:not(.is-active),
.pcard-tags .wf-card-tag:not(.is-active) {
    border-color: hsl(var(--slate) / .5);
}

/* ⚠ hover 這兩組也要湊到跟上面那條框線規則同級的特異度：上面為了壓過
     `.page--knowledge .kz-grid .pcard-tags .tag-pill` (0,4,0) 而用了
     (0,5,0)，結果第一版的 hover（只有 (0,4,0)）**背景換了、框線卻沒換**
     ——實測 hover 時 bg 已是膚色、border 仍是 slate/.5，看起來像框線沒跟上。
     這就是「提高一條規則的特異度之後，同組的其他狀態也要一起提高」。 */
.page--knowledge .kz-grid .pcard-tags .wf-card-cat:not(.is-active):hover,
.page--knowledge .kz-grid .pcard-tags .wf-card-cat:not(.is-active):focus-visible,
.kd-related-tags.pcard-tags .wf-card-cat:not(.is-active):hover,
.kd-related-tags.pcard-tags .wf-card-cat:not(.is-active):focus-visible,
.pcard-tags .wf-card-cat:not(.is-active):hover,
.pcard-tags .wf-card-cat:not(.is-active):focus-visible {
    background: hsl(var(--nude));
    border-color: hsl(var(--nude));
    color: hsl(var(--heading));
}

.page--knowledge .kz-grid .pcard-tags .wf-card-tag:not(.is-active):hover,
.page--knowledge .kz-grid .pcard-tags .wf-card-tag:not(.is-active):focus-visible,
.kd-related-tags.pcard-tags .wf-card-tag:not(.is-active):hover,
.kd-related-tags.pcard-tags .wf-card-tag:not(.is-active):focus-visible,
.pcard-tags .wf-card-tag:not(.is-active):hover,
.pcard-tags .wf-card-tag:not(.is-active):focus-visible {
    background: hsl(var(--surface-grey));
    border-color: hsl(var(--surface-grey));
    color: hsl(var(--heading));
}

/* ══════════════════════════════════════════════════════════════════════
   2026-08-31 客戶「寬度 1280 時各個區塊需調整」批次（三項）
   ══════════════════════════════════════════════════════════════════════ */

/* ── ① 「header 右邊的元素被靠左了…選單那邊請右推回去」 ────────────────
   實測 1280：logo 的**墨水**左緣離視窗左緣 72px（＝--section-x，跟下方
   內容切齊），但右邊看得見的東西——漢堡的兩條線——右緣離視窗右緣
   86.6px，比左邊多 14.6px，肉眼就是右側整組往內縮了一截。
   ⚠ 成因不是內距寫錯：.inner 左右內距兩邊都確實是 72px。是**方形選單鈕
     比它自己畫的線寬**——鈕 clamp(48px,4vw,58px)（1280＝51.19px），線只有
     22px，置中後左右各留 (51.19−22)/2＝14.59px 的透明留白，最右邊那段
     留白把看得見的線往左推了 14.59px。1024 是 13px、1920 是 18px，
     一路都存在，只是客戶在 1280 看出來。
   ⚠ 修法是把那段留白從右內距扣回來（AAA／搜尋／選單鈕**整組**右移），
     不是只把線靠右：只動線的話線跟搜尋圖示的間距會多出 14.6px，變成
     另一種不對稱，而客戶說的是「元素」（複數）。
   ⚠ 用 calc 從鈕自己的 clamp() 推，不寫死數字——鈕大小隨 4vw 變，
     寫死的話 1200／1450 又會各差幾 px。--menu-btn-ink 是
     .site-nav-toggle-bars 的實際線寬，改線寬時這裡要跟著改。
   ⚠ 992–1199.98 的基準內距是 calc(50% - 46vw)（不是 --section-x），
     所以拆成兩條寫；只寫一條會把那個區間的內距整個換掉。 */
:root { --menu-btn-ink: 22px; }

@media (min-width: 992px) {
    html.is-compact:not(.has-menu-open) .site-header > .inner {
        padding-right: calc(var(--section-x)
            - (var(--menu-btn-size, clamp(48px, 4vw, 58px)) - var(--menu-btn-ink)) / 2);
    }
}

@media (min-width: 992px) and (max-width: 1023.98px) {
    html.is-compact:not(.has-menu-open) .site-header > .inner {
        padding-right: calc(50% - 46vw
            - (var(--menu-btn-size, clamp(48px, 4vw, 58px)) - var(--menu-btn-ink)) / 2);
    }
}

/* ── ② 「header 太靠上，整體下拉一點」 ────────────────────────────────
   1280 的 header 只有 65.19px 高（≥1468 是 72px），內容離視窗頂只有
   18.19px、比桌機再高 3.4px。上下內距 .4375rem(7px/7px) → 15px/1px：
   內容整組**下移 8px**，header 只多長 2px。
   ⚠ 只加上面不叫「整體下拉」還不夠——上下一起加只會變高、內容不動；
     這裡是「上面加 8、下面減 6」，位移量全部落在內容上。
   ⚠ 為什麼不乾脆上面加 8、下面不動：選單鈕是 clamp(48px,4vw,58px)，
     1440 就已經 57.6px，header 會被撐到 79.6px，而 ≥1468 桌機是 72px
     ——resize 過 1468 那條線會突然掉 8px。壓下內距之後 1467 是 73.6px、
     1468 是 72px，差 1.6px 看不出來，桌機那套一個字都不用動。
   ⚠ 下內距留 1px 不留 0：選單鈕是透明的點擊區，它的兩條線本來就置中在
     鈕裡（離鈕底 14.6px），貼著 header 下緣不會有視覺問題；留 1px 是給
     未來若補 header 底線時不會壓到鈕。
   ⚠ --header-real-h 由 site.js 現量後寫進 <html>，這裡變高會自動跟上；
     但 CSS 端那組**後備值**（本檔 ≈7262 的 56／63／73）是「JS 還沒跑」
     時用的，992–1467 實測從 62–71.6 變成 64–73.6，一起補一條蓋掉，
     不然首次繪製時麵包屑會先跳一下。 */
@media (min-width: 992px) and (max-width: 1467.98px) {
    html.is-compact:not(.has-menu-open) .site-header > .inner {
        padding-top: 15px;
        padding-bottom: 1px;
    }

    :root { --header-real-h: 65px; }
}

/* ── ③ 「選單展開的文字沒對齊高度」 ──────────────────────────────────
   抽屜 TOOLS 欄的 A／A+／A++ 跟左邊兩欄的三列對不齊：1280 實測 A 比
   「服務專區／會員」低 14px、A+ 低 12.1px、A++ 低 12.3px；≥1468 則是
   反過來，A+ 高 12px、A++ 高 33.5px。
   ⚠ 成因：這一欄是**一根 flex 直排**（gap 1.25rem＋margin-top 1.0625rem
     ＋A+ 再補 margin-top .5625rem），用固定間距去追一個**列高不等**的
     grid（1280 實測三列 46.59／46.59／54.20px），數學上追不到。那組固定
     值是 2026-08-17「A 齊會員、A+ 齊登入、A++ 齊註冊」時算的，後來子選項
     改成預設收合，登入／註冊根本不在畫面上，前提已經不存在（本檔 ≈23334
     的註解自己也寫了這件事）。
   ⚠ 改法：`display: contents` 把這層外殼從版面裡拿掉，三顆按鈕直接變成
     .site-nav 這個 grid 自己的項目，各自釘在第 2／3／4 列、第 4 欄——
     跟左邊兩欄的項目走同一組格線，列高怎麼變都自動跟上，不再用固定
     間距去追。
   ⚠ 先試過 `grid-template-rows: subgrid`（外殼留著、只把列借過來）：
     Chrome 有認（computed 是 `subgrid [] [] [] []`），但三顆的實際位置
     比格線各早 0／5／5px——外殼本身的高度也參與了父格線的計算，繞不
     開。display:contents 沒有這個回授問題，實測三列全部歸零。
   ⚠ `border-width: 0`：按鈕原本帶 1px 透明外框（`border-color:
     transparent`，看不見但佔高度），連同 1px 會讓文字比隔壁低 1px，
     且讓按鈕比同列導覽連結高 2px、反過來把整列撐高。
   ⚠ 外殼是 `role="group" aria-label="字級大小"`，display:contents 會拿掉
     它的框——現行 Chrome／Firefox／Safari 都已修正這種情況下的語意遺失
     （舊版曾整組從無障礙樹消失），三顆按鈕自己的 aria-pressed 與
     visually-hidden 文字（小／大／特大）本來就是各自獨立可用的。
   ⚠ 上內距與行高抄同列導覽連結那條（≤1467 是 .625rem／1.6，≥1468 是
     .9rem／1.6，見「.site-header .site-nav > a」兩處），文字基線才會落在
     同一條線上——只對齊外框不夠，兩邊 padding 不同時文字仍會錯開。
   ⚠ ≥1468 只補 padding-top、不補 padding-bottom：那個殼的按鈕靠
     `padding-bottom:.25rem` 貼住 is-active 的底線，補下內距會讓底線
     離開文字 14px。
   ⚠ 順手一起修水平：≤1467 的按鈕帶著 `padding: .5rem .75rem`，左內距
     12px 讓「A」比欄標「TOOLS」往右縮 12px（左邊兩欄都是切齊的，只有這欄
     不齊）。導覽連結在同斷點已被 `html:root … a { padding-inline: 0 }`
     歸零，這裡比照歸零。客戶這次講的是「高度」，但這是同一個元件、同一
     類缺口，一起補掉。 */
@media (min-width: 992px) {
    html .site-header .site-nav > .font-size-switch {
        display: contents !important;
    }

    /* ⚠ 一律 !important：這三顆的 margin／padding 在 ≤1467 與 ≥1468 兩個殼
       裡各有一份 `!important`（其中 ≥1468 那份選擇器帶 `html.is-compact`
       ＝(0,5,1)，比這裡高），第一版沒加 !important 時 ≥1468 完全沒生效、
       只有 1280 動了——兩個斷點都要量才看得出來。 */
    html .site-header .site-nav > .font-size-switch .font-size-btn {
        grid-column: 4 !important;
        align-self: start !important;
        justify-self: start !important;
        margin: 0 !important;
        border-width: 0 !important;
        line-height: 1.6 !important;
    }

    html .site-header .site-nav > .font-size-switch .font-size-btn--sm { grid-row: 2 !important; }
    html .site-header .site-nav > .font-size-switch .font-size-btn--lg { grid-row: 3 !important; }
    html .site-header .site-nav > .font-size-switch .font-size-btn--xl { grid-row: 4 !important; }
}

@media (min-width: 992px) and (max-width: 1467.98px) {
    html .site-header .site-nav > .font-size-switch .font-size-btn {
        padding: .625rem 0 !important;
    }
}

/* ≥1468 的下內距維持 .25rem：那個殼的 is-active 靠 border-bottom 貼在文字
   下面當底線，補成 .9rem 會讓底線離開文字 14px。 */
@media (min-width: 1468px) {
    html .site-header .site-nav > .font-size-switch .font-size-btn {
        padding: .9rem 0 .25rem !important;
    }
}

/* ③-b：≥1468 的 A+ 還差 9px——那個殼另有一條
   `html.is-compact … .font-size-btn--lg { margin-top: .5625rem !important }`
   ＝(0,5,1)，比上面那條 (0,4,1) 高，`margin: 0 !important` 追不上。
   這裡用 `html:root` 湊到同級 (0,5,1)，靠檔案順序（本檔最尾）贏。
   ⚠ 這顆 margin 是 2026-08-17「A+ 齊登入」留下的補償，跟已經拿掉的固定
     間距是同一組，不是獨立的視覺設定。 */
html:root .site-header .site-nav > .font-size-switch .font-size-btn--lg {
    margin-top: 0 !important;
}

/* ③-c：同一畫面裡「搜尋」比同列的「知識專區」高 5px（≤1467 才有，≥1468
   本來就齊）——它是三欄裡唯一沒有上內距的項目（實測 0 vs 其他 10px），
   且比同列矮 21px、被 align-items:center 往上擺。補回同值上內距並改成
   貼齊列首，三欄第三列才真的在同一條線上。 */
@media (min-width: 992px) and (max-width: 1467.98px) {
    html .site-header .site-nav > a.site-search {
        align-self: start;
        /* ⚠ 要 !important：這顆的上內距被另一條規則壓成 0，第一版沒加，
           結果只有 align-self 生效、內距沒補上，反而從高 5px 變成高
           10.5px（改完一定要回測，不能只看規則寫上去了）。 */
        padding-top: .625rem !important;
        /* ⚠ 真正的元凶是 `.site-header .site-search { height: 29px }`（≥992）：
           內距補上去了，但固定高把內容框壓成 1.4px，`align-items:center`
           再把文字往上拉 10.5px——所以第二版仍然沒對齊。解掉固定高，
           高度回到「內距＋行高」＝50px，跟同列的知識專區一模一樣。 */
        height: auto !important;
    }
}

/* ── ④ 課程單頁 Hero：左欄文字下拉，標題落在圖片水平中線（2026-08-31 客戶指定）──
   客戶（寬度 1280）：「左邊的文字整個上移了，請下拉到圖片水平中間位置」；
   選項題確認基準是**課程標題本身壓在圖片中線上**（不是整段文字的中線——
   整段中線實測本來就已經在圖片中線上，差 5px，改了等於沒改）。
   位移量由 course_detail.php 的 Hero IIFE 現量後寫進 --cd-hero-text-shift
   （同一堂課 1280 要 +93px、1468 要 +51px、1920 要 −75.5px，標題行數與圖片
   高度都會變，寫死不可能對）。
   ⚠ 只在 ≥1200 生效：≤1199.98 的 .cd-hero-layout 是 flex 直排（圖片在上、
     文字在下），沒有「文字對齊圖片中線」這回事，套上去會把文字推出區塊。
   ⚠ 用 top 不用 transform、也不用改 margin-bottom：transform 被進場動畫
     佔著；margin-bottom 會改變列高、回頭改變圖片位置，量測與版面互相拉扯。 */
@media (min-width: 1024px) {
    .course-detail--editorial .cd-hero-text {
        top: var(--cd-hero-text-shift, 0px);
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   靜態頁（無 header／footer／浮動鈕）— 2026-08-31 客戶指定
   ═══════════════════════════════════════════════════════════════════════
   客戶：「隱私權政策該頁面不要 header 跟 footer 也不要懸浮按鈕，該頁面是
   靜態頁」。標記那邊由 layouts/main.php 的 $wfBare 旗標跳過 header／麵包屑／
   footer 三個 include（浮動鈕 .wf-float 就住在 footer 那支裡），body 掛
   .is-bare 當鉤子。CSS 這邊只需要補一件事：把「幫 fixed header 讓位」的
   內距收掉。

   ⚠ 本檔「0. 版面基礎」那節有
       @media (min-width:992px) { body.is-designed:not(:has(.hero)) .wf-frame }
     給沒有 Hero 的內頁 padding-top: var(--header-h)。header 不在了，那段
     讓位會變成頁首憑空多出一塊空白（--header-h 目前解出 56px）。
   ⚠ 特異度刻意湊到 (0,4,1)，不靠「我在檔案後面」險勝：原規則是 (0,3,1)，
     同分時只贏在順序，而本檔已經被同分的隱形基準規則咬過三次（見
     .section-bleed-inner 那節）。多帶一個 :not(:has(.hero)) 把分數拉開，
     順便把語意寫明白——這條只在「原規則會生效的情境」下抵銷它。
   ⚠ 一樣包在 min-width:992px 裡：≤991.98 的 header 是 wireframe.css 的
     sticky（佔文件流），原規則本來就沒套，這裡沒有東西要抵銷。
   ⚠ 上下留白不必另外補：內容最外層是 .site-section，本來就有
     padding: var(--section-y) var(--section-x)，頁首頁尾都有呼吸空間。 */
@media (min-width: 992px) {
    body.is-designed.is-bare:not(:has(.hero)) .wf-frame {
        padding-top: 0;
    }
}

/* ── ⑤ 課程一覽手風琴：1280 時課名與簡介被切掉半行（2026-08-31 客戶截圖）──
   症狀：「學思」第 3 格展開後，課名第二行只露出上半截、簡介第二行也一樣，
   像被一把水平刀切過。
   ⚠ 不是 line-clamp 沒生效，也不是 overflow 設錯。實測 1280：
       .cx-pane-title  實際高 51.08px，內容需要 71px（2 行 × 35.36）
       .cx-pane-desc   實際高 46.53px，內容需要 64px（2 行 × 32.2）
     兩邊各短 19.6／17.9px，合起來正好 37.5px——就是整組高度不夠的量。
   ⚠ 真因是**兩種單位在打架**：整組高 `--cx-hz-h` 是
     `clamp(18rem, 24vw, 22rem)`（隨視窗寬縮放），但格子裡的東西是**固定
     px**（22.1px 課名、14px 簡介、48px 按鈕、28px 分類列）。視窗越窄
     24vw 給的高度越少，內容卻一點都沒變少：1280 時只有 307.19px，
     而 2 行課名 ＋ 2 行簡介 ＋ 堂數 ＋ 按鈕實際需要 344.72px。
     .cx-pane-text 是 flex column，高度不夠時子項預設 `flex-shrink:1`
     會被**壓扁**，配上 `overflow:hidden` 就切在半行上（不是切在行與行
     之間，所以看起來特別像壞掉）。
   ⚠ 為什麼 1468 以上看不到：那邊 24vw 已經超過 22rem 上限、鎖在 352px，
     而且欄寬變寬讓這個課名剛好只佔 1 行——同一個缺口在寬螢幕被兩件事
     一起蓋住了。
   → 把下限從 18rem 抬到 21.75rem（348px，含 3px 餘裕）：
     ・1200–1440 從 288–345.6 變成 348，不再壓扁；
     ・1468 以上維持 352px 一格不動（客戶 2026-08-19「卡片的高太長」那輪
       核可的就是 352px，這裡永遠不會超過它，等於整站統一到同一個上限）；
     ・上限 22rem 不動，仍然吃 A/A+/A++ 根字級（rem 會跟著長，24vw 不會，
       所以下限用 rem 才擋得住放大字級時的同一個問題）。
   ⚠ 只點名 [data-cx-n="3"]／[data-cx-n="2"]，跟被覆寫的那條同一組選擇器；
     單張（學理）那排沒被 2026-08-19 那輪點名、維持原本 clamp，不要順手改。 */
@media (min-width: 1024px) {
    .page--wall .cx-hz[data-cx-n="3"],
    .page--wall .cx-hz[data-cx-n="2"] {
        --cx-hz-h: clamp(21.75rem, 24vw, 22rem);
    }
}

/* ══════════════════════════════════════════════════════════════════════
   2026-08-31 客戶重截 **1280×720** 三張「跑版」——高度才是變因
   ⚠ 前一輪我只掃寬度（900 高），三項都測過關；客戶的視窗是 720 高，
     下面前兩項在 ≥790 高完全看不到，是標準的「回報寬度、真因是高度」。
   ══════════════════════════════════════════════════════════════════════ */

/* ── ⑥ 首頁 Hero 主圖鑽進 header（≤790px 高才會發生）────────────────────
   實測 1280 寬掃高度：主圖上緣 − header 下緣
       640 高 → −12.8px   700 → −7.7   720 → −6.0   760 → −2.6
       800 高 → +17.1     900 → +67.1（正常）
   ⚠ 讓位量 `--hero-card-media-pad-y: clamp(3rem, 8.5vh, 5.5rem)` 是
     **vh** 的：720 高時 8.5vh ＝ 61.2px，比 header 本身（67.2px）還矮，
     主圖自然就鑽到 header 底下。那條規則的註解自己寫了「實測基準
     1920×1080」——只在高視窗校過，沒有下限保護。
   ⚠ 這是既有問題不是本輪改出來的：beta 站（未部署今天改動）同一個
     視窗量到 61.2 vs 65.2，一樣鑽進去 4px。
   → 補一道下限：至少要讓開「真實 header 高度 ＋ 8px」。
     720 高 → max(75.2, 61.2) ＝ 75.2（讓開 8px）
     900 高 → max(75.2, 76.5) ＝ 76.5（原值，不動）
     1080 高 → 88px（原值，clamp 上限，不動）
     只有短視窗會變，客戶已驗收的高視窗版面一格都不動。
   ⚠ 用 --header-real-h（JS 現量）不是 --header-h（token 56px，跟真實
     高度差 11px 以上，見該 token 的說明）。 */
html:has(.hero) {
    --hero-card-media-pad-y: max(calc(var(--header-real-h, 56px) + 8px), clamp(3rem, 8.5vh, 5.5rem));
}

/* ── ⑦ 實體課程 Hero 小字與 header 不再齊平 ────────────────────────────
   這格小字（「IN-PERSON COURSES／從真實生活出發…」）是 2026-08-18 客戶
   指定「上移到 header 同行」的——它跟 header 的 A/A+/A++ 左右並排、
   共用同一條視覺基線，不是壓在 header 上。
   ⚠ 本輪 ② 把 header 內容整組下移 8px 之後，這格沒有跟著移：實測
     beta（改動前）logo 20.6..44.6、小字 19..39.4（齊平）；改動後
     logo 28.6..52.6、小字仍是 19..39.4，**差了 8px**，客戶截圖裡就是
     英文小標明顯比 A/A+/A++ 高一截。
   ⚠ 成因是它的負外距寫的是 `var(--header-h)`（token 56px，永遠不動），
     不是真實 header 高度——header 一變它就脫鉤。同一顆 token 的老問題，
     見 --header-real-h 的說明。
   → 用 --header-content-shift 這顆新變數把「本輪 header 內容下移量」
     公開出來，這裡原樣加回去。往後再調 header 的上內距，只要同步改這
     顆變數，這格會自己跟上，不必再記得回來改第二個地方。
   ⚠ 用 position:relative + top，不用改 margin-top：那條 margin 的算式
     有兩個版本（有／無麵包屑），複製一份等於多一個要同步維護的地方；
     相對定位也不會改變版面盒子、不會牽動格線列高。 */
:root { --header-content-shift: 0px; }

@media (min-width: 992px) and (max-width: 1467.98px) {
    :root { --header-content-shift: 8px; }
}

.page--wall .cx-hero-cell--intro .cx-hero-intro-text {
    position: relative;
    top: var(--header-content-shift, 0px);
}

/* ── ⑨ 會員登入頁與註冊（服務聲明）頁同高（2026-09-01 客戶指定，1280×720）──
   實測 1280×720：登入頁區塊 664px（＝100svh − header，剛好一屏），註冊頁
   951.1px——差 287px，用「登入／註冊」那個斜線切換來回跳一大截。
   ⚠ 成因是註冊頁的高度**跟視窗高完全脫鉤**：
     ・`.form-scroll`（服務聲明捲動框）寫死 `max-height: 22rem`（352px）
     ・`.mem-split-panel` 的上下留白是 `clamp(3rem, 6.5vw, 6rem)`——**6.5vw
       是跟寬度走的**，1280 寬永遠是 83.2px，視窗再矮也不會收
     兩者相加，註冊頁的內容高度固定 951px，`min-height:100svh − header`
     這條只管下限、管不到上限，所以矮視窗就長出去。
     1080 高以上兩頁本來就一樣高（都是 100svh − header），只有矮視窗會分岔。
   → ① 有內部捲動框的那頁補 `max-height` 把整塊釘成一屏（跟登入頁同值）；
     ② 面板上下留白在矮視窗改吃 6vh（寬視窗維持原本的 6.5vw 上限）；
     ③ 捲動框從「寫死 22rem」改成「吃掉面板剩下的空間」，空間夠時仍不超過
        原本的 22rem。
   ⚠ 用 `:has(.form-scroll-wrap)` 只鎖服務聲明頁：
     ・登入頁本來就等於 min-height，補 max-height 是同值、零變化；
     ・**資料填寫頁（/member/register/form）沒有捲動框**，內容 1013px 沒有
       任何東西可以壓縮，套上 max-height 只會讓內容溢出去壓到 footer。
   ⚠ `min-height: 0` 三處都要補：grid item 與 flex item 的自動最小尺寸是
     內容高，不歸零的話上面的 max-height 會被內容頂開、等於沒設。
   ⚠ 捲動框留 `min-height: 8rem` 的底線：極矮視窗（<600）寧可讓頁面超過一屏，
     也不要把服務聲明壓成一行。 */
@media (min-width: 992px) {
    /* ⚠ 放大字級時**不套上限**（見檔尾同一節的說明）：A+／A++ 內容會長高，
       硬套 max-height 會把內容擠出容器（實測 720 高時 A++ 溢出 117–145px）。
       `:not([data-font-size="lg"]):not([data-font-size="xl"])` 同時涵蓋
       「屬性還沒被寫上去」的預設狀態。 */
    html:not([data-font-size="lg"]):not([data-font-size="xl"]) .mem-split-grid:has(.stepper) {
        max-height: calc(100svh - var(--header-h));
        /* ⚠ 光有 max-height **不夠**：這是 grid，隱式列的高度仍然由內容決定，
           max-height 只收住 grid 自己的盒子、收不住列——第一版就是這樣，
           實測 grid 664px 但裡面的 .mem-split-panel 長到 2060px 直接溢出去。
           釘一條 minmax(0, 1fr) 把那一列也收進 grid 的高度裡，欄位才會真的
           被壓縮。 */
        grid-template-rows: minmax(0, 1fr);
    }

    .mem-split-panel {
        padding-block: min(clamp(3rem, 6.5vw, 6rem), 6vh);
    }

    .mem-split-grid:has(.stepper) .mem-split-panel {
        min-height: 0;
    }

    .mem-split-panel .form-panel:has(.form-scroll-wrap) {
        display: flex;
        flex-direction: column;
        min-height: 0;
    }

    /* ⚠ `flex: 0 1 auto`（可縮不可長）不是 `1 1 auto`：空間夠的時候要維持
       原本 22rem 的框高，不要把整個剩餘空間都吃掉——那會讓寬視窗的版面
       跟客戶驗收過的不一樣。 */
    .mem-split-panel .form-scroll-wrap {
        flex: 0 1 auto;
        min-height: 8rem;
    }

    /* ⚠ 只補 height:100%，**不要拿掉 max-height: 22rem**：第一版拿掉了，
       捲動框就整個攤開成 1541px、內文直接掉出畫面（而且捲動軌道也沒了）。
       height:100% 在外框高度是 auto 時解成 auto（＝內容高，再被 22rem 收住，
       維持原樣）；外框被 flex 壓縮後才有確定高度，這時 100% 才跟著縮。 */
    .mem-split-panel .form-scroll {
        height: 100%;
    }
}

/* ⚠ 同高之後聲明框只剩 144.9px（約 3 行），太窄。這一頁的垂直間距全部是
   rem／vw 的（page-head 64px、stepper 48px、h2 38.4px、按鈕列 32px，合計
   182.4px），視窗再矮都不會收——空間全被留白吃掉。這裡替每一段補一個
   **跟視窗高走**的上限，矮視窗才把空間讓給聲明框。
   ⚠ 一律用 `min(原值, Xvh)`：寬視窗（vh 大）取原值，版面跟客戶驗收過的
     完全一樣，只有矮視窗會收——不是重新設計一套間距。
   ⚠ 只鎖 `:has(.form-scroll-wrap)` 這一頁：登入頁內容短、又是垂直置中，
     收留白對它沒有視覺影響，但沒必要動它。 */
@media (min-width: 992px) {
    .mem-split-grid:has(.stepper) .mem-split-panel {
        padding-block: min(clamp(3rem, 6.5vw, 6rem), 5vh);
    }

    .mem-split-grid:has(.stepper) .page-head {
        margin-bottom: min(4rem, 5.5vh);
    }

    .mem-split-grid:has(.stepper) .stepper {
        margin-bottom: min(clamp(2rem, 4vw, 3rem), 4.5vh);
    }

    .mem-split-grid:has(.stepper) .page-h2 {
        margin-bottom: min(clamp(1.5rem, 3vw, 2.5rem), 3.5vh);
    }

    .mem-split-grid:has(.stepper) .form-actions {
        margin-top: min(2rem, 3.5vh);
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   懸浮鈕第三輪：拿掉光暈、修好信封動畫、FB 再放大（2026-09-01 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「全尺寸懸浮按鈕光暈效果移除但放大縮小依舊保留，並將信封 icon 動畫
   改好，fb icon 放大」。前兩節（≈30809 放大＋脈動＋光暈、≈31147 對齊＋光暈
   改白）**一個字都沒改**，這裡用檔尾覆寫接手，沿革留在原處。

   ── ① 光暈拿掉，脈動留著 ───────────────────────────────────────────
   兩者本來綁在同一組 keyframes（當初分開寫會各跑各的相位，看起來像兩個不
   同步的效果），所以「只拿掉光暈」＝重新宣告同名 keyframes、只留 transform
   那一半（後宣告的整組取代先宣告的）。
   ⚠ 「全尺寸」不必逐斷點覆寫：keyframes 是全域的，桌機 56 與手機 52 兩階
     共用同一組，改一次兩階同時生效。
   ⚠ reduced-motion 那一格另外有一條**靜態**光暈（動畫關掉時的替代品，
     ≈31201），不一起關的話，開了「減少動態」的人會變成全站唯一還看得到
     光暈的使用者——這格平常不會切到，是最容易漏的一條。

   ── ② 信封動畫為什麼一直沒作用 ─────────────────────────────────────
   既有規則（≈10394）寫的是 `.wf-float a:hover .wf-icon--mail .mail-flap`，
   但信封那顆 2026-08-05 起就從 <a> 改成 <button class="wf-float-mail-toggle">
   了（客戶要「點擊後直接有輸入電子郵件框」，見 _footer.php 那段註解），
   選擇器從此對不到元素——規則一直掛在檔案裡，但**一天都沒生效過**。
   ⚠ 順手補力道：原本是 translateY(1px)，就算選對元素，22px 的圖示上等於
     位移 0.9px，實務上看不見。旁邊 YouTube／Podcast 是整組 keyframes
     （描線＋脈動／中心＋雙電波，≈3725），客戶說的「改好」是「跟旁邊兩顆
     一樣會動」，不是「讓 1px 生效」。這裡改用同一套語言的 keyframes，
     時長 .42s 落在那一區既有的 .3–.7s 之間。
   ⚠ 幅度刻意停在「封口下沉並加深」而不是整片翻開（scaleY 負值）：那個路徑
     只有 7 個 viewBox 單位高，翻面在 22px 上容易糊成一團。要更明顯的話
     改下面 40% 那一格的兩個數字即可，不必動選擇器。
   ⚠ transform-origin 改成 center top（覆寫 ≈10389 的 12px 7px）：封口是從
     信封**頂邊**掀開的，origin 放頂邊，兩個上角才會釘住不動。
     ⚠ `.wf-icon > *` 已經給了 transform-box: fill-box（≈3719），所以這個
       origin 算的是**路徑自己的 bbox**，不是整個 viewBox。
   ⚠ 舊那條 `.wf-float a:hover …` 留著不刪（本檔慣例），它涵蓋未來若又出現
     <a> 版的信封。

   ── ③ FB 再放大 ───────────────────────────────────────────────────
   實測 footer 四顆的內容 bbox：Podcast 20×14.1、YouTube 22×15.5、
   IG 20×20（外框方塊）、FB 20×19.9（實心圓）。上一輪 scale(1.14) 是讓 FB 的
   **面積**與 IG 打平（πr² = s² 解出 1.128），客戶仍覺得小——因為外框方塊的
   視覺範圍是它的四個角，實心圓永遠碰不到那裡。這次把直徑推到 IG 方塊的
   邊長（20）與對角線（28.28）之間：scale(1.35) → 直徑 27.0px。
   ⚠ 直徑 27 > svg 框 22，靠 `.wf-icon { overflow: visible }`（≈3714）不被裁。
     footer 四顆的間距實測 68.4px，多出來的 4.2px 不會互相碰到。
   ⚠ 分享面板那顆 FB 是同一顆 SSOT，會跟著變大——那一排的 LINE 是外框圖示，
     有一樣的視覺落差，一起補正才對（與上一輪同一個判斷）。 */

@keyframes wf-float-breathe {
    0%, 100% { transform: scale(1); }
    50%      { transform: scale(1.07); }
}

@media (prefers-reduced-motion: reduce) {
    .is-designed .wf-float a,
    .is-designed .wf-float .wf-float-mail-toggle {
        box-shadow: none;
    }
}

/* ⚠ 光暈拿掉之後**露出一層舊投影**：`.is-designed .wf-float-mail-toggle`
     （≈11006，2026-07-30 藍底時代留下的 0 2px 10px）從來沒被刪過，只是
     08-31 那組 keyframes 每一格都寫了 box-shadow，等於整段動畫期間都把它
     蓋住，所以沒人發現。現在 keyframes 不再宣告 box-shadow，它就自己冒
     出來——實測三顆裡只有信封有陰影，正是客戶 08-31 抱怨過「這顆跟前兩顆
     不一樣」的同一件事。
   ⚠ 兩顆 <a> 不受影響：它們在 ≈9166 已經被覆寫成 box-shadow: none，
     實測確認是 none。這裡只點名信封那顆。
   ⚠ 特異度 (0,3,0) 才蓋得過 ≈11006 的 (0,2,0)——只寫
     `.wf-float-mail-toggle` 會輸。 */
.is-designed .wf-float .wf-float-mail-toggle {
    box-shadow: none;
}

/* 信封掀蓋：封口以頂邊為軸往下沉並加深，再回原狀（一次性，不用 forwards） */
@keyframes wfMailFlap {
    0%   { transform: translateY(0)     scaleY(1); }
    40%  { transform: translateY(1.5px) scaleY(1.5); }
    100% { transform: translateY(0)     scaleY(1); }
}

@media (hover: hover) {
    .wf-float .wf-float-mail-toggle:hover .wf-icon--mail .mail-flap,
    .wf-float .wf-float-mail-toggle:focus-visible .wf-icon--mail .mail-flap {
        transform-origin: center top;
        animation: wfMailFlap .42s ease-in-out;
    }
}

@media (prefers-reduced-motion: reduce) {
    .wf-float .wf-float-mail-toggle:hover .wf-icon--mail .mail-flap,
    .wf-float .wf-float-mail-toggle:focus-visible .wf-icon--mail .mail-flap {
        animation: none;
    }
}

/* FB 實心圓的視覺補正加大：1.14 → 1.35（見本節 ③） */
.wf-icon--brand-facebook {
    transform: scale(1.35);
}

/* ── ⑩ 註冊三步驟全部同高（2026-09-01 客戶追加：「那內容重排版，註冊三步驟
      都要同高度」）────────────────────────────────────────────────────
   上面 ⑨ 只讓步驟①（服務聲明）跟登入頁同高；客戶追加要三步驟一致，並明確
   同意為此重排內容。實測 1280×720 三步驟原始高度：664（①，⑨ 已修）／
   904.3（②資料填寫）／928.3（③完成）。
   ⚠ 上面 ⑨ 那組規則的鉤子從 `:has(.form-scroll-wrap)`（只有①有捲動框）
     換成 **`:has(.stepper)`**——三個步驟都有步驟指示器、登入頁沒有，
     一個選擇器同時涵蓋①②且不誤傷登入頁。

   ── 步驟②「資料填寫」：欄位改雙欄 ──────────────────────────────────
   內容 817.9 ＋ 面板上下留白 86.4 ＝ 904.3，比一屏（664）多 240.3px。
   四個欄位單欄排列就佔 385.2px，光收留白絕對不夠——**必須重排**。
   → `.form-panel` 改 2 欄 grid：姓名｜Email、密碼｜確認密碼；勾選項與送出鈕
     `grid-column: 1 / -1` 橫跨兩欄。省下 2 列 ＝ 203.6px，再配上面 ⑨ 那組
     節奏上限（page-head／stepper／按鈕列）約 46.8px，剛好進得去。
   ⚠ 用 grid 自動排版、**標記一個字都不用改**（`data-reg-view="form"` 是標記
     上本來就有的鉤子）。欄位原本靠 `margin-top: 22px` 分隔，改由 row-gap 負責，
     所以要一併歸零，否則兩者疊加。
   ⚠ 只鎖 `[data-reg-view="form"]`：同一頁另有一組 `.form-actions` 屬於別的
     view（預設隱藏），不要一起被 grid 接管。

   ── 步驟③「完成」：不是 mem-split，另外收 ──────────────────────────
   這頁是置中單欄（`.page--v2.page--simpla`），沒有左圖，所以 ⑨ 那套完全
   套不上。內容 730.5、外層色帶 928.3——空白主要在 `.done-panel` 的上下
   內距（64＋64）與色帶自己的 padding。
   → 色帶釘成一屏（min-height ＝ max-height ＝ 100svh − header，內容垂直置中），
     `.done-panel` 內距與同一組節奏值改吃 `min(原值, Xvh)`。
   ⚠ 一樣只用 `min(原值, Xvh)`：寬視窗取原值，1080 以上版面零變化。 */
@media (min-width: 992px) {
    .mem-split-panel .form-panel[data-reg-view="form"] {
        display: grid;
        grid-template-columns: repeat(2, minmax(0, 1fr));
        column-gap: 1.5rem;
        row-gap: min(1.375rem, 3vh);
        align-content: start;
    }

    .mem-split-panel .form-panel[data-reg-view="form"] > .form-field {
        margin-top: 0;
    }

    .mem-split-panel .form-panel[data-reg-view="form"] > .form-check,
    .mem-split-panel .form-panel[data-reg-view="form"] > .form-actions {
        grid-column: 1 / -1;
        margin-top: 0;
    }

    /* 送出鈕跟勾選項之間要比欄位間距再多一點，不然按鈕像黏在條款上。 */
    .mem-split-panel .form-panel[data-reg-view="form"] > .form-actions {
        margin-top: min(1rem, 2vh);
    }

    /* ── 步驟③「完成」 ── */
    /* ⚠ 高度**不是** `100svh - header`：這個色帶有全站的「拉頂」負外距
       （`margin-top: -57px` ＝ −(header-h + 1)），上緣被拉到 y = −1，不像
       另外三頁是從 y = 56 起算。要讓 footer 跟前三頁一樣落在 100svh，
       高度得補回那段拉頂 → (100svh − header) + (header + 1) ＝ 100svh + 1px。
       第一版直接抄 `100svh - header`，footer 就比前三頁高了 57px（實測
       607 vs 664），內容也因此少 57px 可用、擠出容器 14px。 */
    .page--simpla.page--member .band:has(.stepper) {
        display: flex;
        align-items: center;
        min-height: calc(100svh + 1px);
    }

    /* 同上：放大字級時只留下限，不設上限，讓內容自己長。 */
    html:not([data-font-size="lg"]):not([data-font-size="xl"]) .page--simpla.page--member .band:has(.stepper) {
        max-height: calc(100svh + 1px);
    }

    .page--simpla.page--member .band:has(.stepper) > .section-bleed-inner {
        width: 100%;
    }

    .page--simpla.page--member .done-panel {
        padding-block: min(4rem, 5vh);
    }

    .page--simpla.page--member .page-head {
        margin-bottom: min(4rem, 5.5vh);
    }

    .page--simpla.page--member .stepper {
        margin-bottom: min(clamp(2rem, 4vw, 3rem), 4.5vh);
    }
}

/* ── ⑪ 1024–1199 桌機版型的收窄調整（2026-09-01 客戶三張截圖）─────────────
   斷點下移到 1024 之後，桌機版型第一次要在 1024 這麼窄的寬度成立。客戶回報
   三頁「有問題」，實測都是同一個成因：**這些版型的欄寬是固定 px 或固定字級，
   1200 以上剛好夠，1024 就擠爆**。三處各自收一階，只在 1024–1199.98 生效，
   ≥1200 一個像素都不動。

   ① 知識專區：兩支側欄是**寫死的 240px + 176px**，跟視窗寬無關。1024 時
      內容欄只剩 402.6px → 文章卡兩欄各 200.8px，標題被 line-clamp 截成
      「談加薪這場對話，重點不…」。收成 180 + 132 之後內容欄 568.6px、
      卡片 269px（1280 是 321px，同一個量級）。
      ⚠ 不改成單欄：單欄卡片會變成 403px、比 1280 的 321px 還寬，兩欄的
        版面節奏才跟桌機一致。

   ② 課程單頁 Hero：圖片 `aspect-ratio: 1.5/1`、`align-self: start`，而那一列
      的高度是**左邊文字欄撐出來的**。1280 時兩者剛好都是 585.2px（巧合），
      1024 時圖片只有 466.4px、列卻是 597.9px，**圖片下方空 132.5px**。
      → 這個區間讓圖片改成撐滿整列（解掉 aspect-ratio）。
      ⚠ 只在 1024–1199.98 解：≥1200 那個「剛好相等」是客戶已驗收的畫面，
        動它反而會把兩邊的巧合破壞掉。

   ③ 實體課程 Hero 右下格：「往下查看近期課程」8 個字 × 32px ＝ 256px，
      1024 時那一格只有 215px 可用 → 折成兩行（1280 是 319px 的欄、一行）。
      英文小標「Scroll down to see more」也因為 .2em 字距一起折行。
      → 標題字級改 `min(32px, 2.5vw)`（1024 得 25.6px、8 字 205px ✓），
        小標字距收到 .12em。1200 以上兩者都取回原值。 */
/* ② 的規則**不設上限**（不是 1024–1199.98）：只修到 1199.98 的話，1199 圖片
   填滿、1200 又空 36.9px，等於在斷點上自己製造一條落差。延伸到全桌機之後
   1280／1920 實測完全沒變（那兩個寬度本來就是 0.0px），1200–1279 這段順便
   一起填滿——那段的空隙是既有的，不是這次改出來的。 */
@media (min-width: 1024px) {
    /* ⚠ **只改 align-self，不要動 aspect-ratio**：第一版順手加了
       `aspect-ratio: auto`，1920 的 Hero 整個從 898px 縮到 547.9px——那個
       1.5/1 的比例正是**撐起整列高度的來源**（列高 ＝ max(文字欄, 圖寬/1.5)），
       拿掉之後列高只剩文字欄撐的高度。
       保留比例＋改成 stretch 才是要的行為：比例照舊參與列高計算，算完之後
       圖片再撐滿那一列。實測 1024／1199／1200／1280／1920 圖片底到列底
       全部 0.0px，且 1280／1920 的列高與改動前完全相同。 */
    .course-detail--editorial .cd-hero-media {
        align-self: stretch;
    }
}

@media (min-width: 1024px) and (max-width: 1199.98px) {
    /* ⚠ **不要改側欄欄寬**（第一版這樣做，客戶一張截圖就抓到）：這頁有**六處
       公式各自寫死 `15rem` 與 `11rem`**——頁標題的左偏移
       （`--section-x + 15rem + gap`，實測 1280 時 350.4 剛好等於 TAGS 欄的
       x）、左側灰底的寬度（`--section-x + 15rem`）、裝飾線的
       `left: --section-x + 15rem - 30px`，以及三處出血量的算式。
       側欄一收窄，這六處全部原地不動 → 標題「知識專區」跟 TAGS 欄差了 60px，
       灰底與欄界也對不上。要正確收窄得先把那六處抽成共用變數（SSOT），
       風險遠大於這次要解的問題。
       ⚠ 順帶一提，第一版還踩了特異度：只寫 `.kz-layout`(0,1,0) 會輸給既有的
         `.page--knowledge .kz-layout`(0,2,0)，computed 完全沒變。
       → 改成**不動欄寬、只把文章格收成單欄**：卡片從 200.8px 變成 402.6px
         （1280 是 321px，比桌機還寬，絕對不會再被 line-clamp 截掉），而且
         `.kz-grid` 自己的 x 與寬度一格都沒變，上面那六處公式一個都不受影響。 */
    .page--knowledge .kz-grid {
        grid-template-columns: minmax(0, 1fr);
    }

    /* ⑤ 首頁知識專區：圖片掉到文字下半段（客戶「圖片掉下去了跟文字對齊」）。
       `.kz-stack-body` 是兩欄 grid ＋ `align-items: end`（兩欄貼齊下緣）——
       這個設計成立的前提是「兩欄高度差不多」：1280 實測文字 523px、圖片
       413px，只差 110px，貼下緣看起來剛好是錯落。
       1024 欄寬收窄之後，文字因為折行變高（601px）、圖片因為固定比例變矮
       （342px），差距拉到 **259px**，貼下緣就變成「圖片掉到文字的下半段」。
       → 這個區間改成貼齊上緣，圖片與文字從同一條線開始。
       ⚠ 只在 1024–1199.98 改：≥1200 那 110px 的錯落是客戶驗收過的構圖，
         而且差距小到不會讓人覺得圖片掉下去。 */
    .kz-stack-body {
        align-items: start;
    }

    .page--wall .cx-hero-cell--void4 .cx-hero-intro-desc {
        font-size: min(32px, 2.5vw);
        line-height: 1.5;
    }

    .page--wall .cx-hero-cell--void4 .cx-hero-eyebrow {
        letter-spacing: .12em;
    }

    /* ④ 註冊步驟指示器折成兩行（1024／1100，1199 以上一行）：
       整條需要 489px，面板在 1024 只有 407px。
       ⚠ 真因是 `.stepper` 設了 `flex-wrap: wrap`——**空間不夠時 flex 會先
         換行、而不是壓縮子項**，所以那兩條可伸縮的連接線一點忙都沒幫上，
         直接把「3 完成」擠到第二行。
       → 這個區間改 `nowrap`，讓連接線去吸收不足的寬度（線本來就是裝飾，
         短一點沒有資訊損失），項目本身釘 `flex: 0 0 auto` 不准被壓扁，
         中文標籤才不會跟著折行。
       ⚠ 只在 1024–1199.98 改：≥1200 本來就排得下，維持 wrap 與原本的線寬。 */
    .stepper {
        flex-wrap: nowrap;
        gap: .5rem;
    }

    .stepper-item {
        flex: 0 0 auto;
        gap: .375rem;
    }

    .stepper-rule {
        flex: 0 1 auto;
        min-width: .625rem;
    }
}

/* ── ⑫ 2026-09-01 客戶四張截圖（1024）第二批 ─────────────────────────────

   ⑥「箭頭高度不夠」（實體課程 Hero 右向箭頭）
   `.cx-hero-arrow--right` 的 viewBox 是 0 0 64 24，但 CSS 高度寫死 20px，
   而且 SVG 帶 `preserveAspectRatio="none"`——**縱向被壓成 20/24 ＝ 0.83 倍**，
   同時橫向被拉成 92/64 ＝ 1.44 倍（1024 實測），箭頭尖端因此又扁又鈍。
   → 高度 20 → 28px：縱向變成 1.17 倍、不再被壓縮，箭頭明顯變高。
   ⚠ 不改成「完全不變形」的 34.5px（＝寬 × 24/64）：橫向拉長是這個元件的
     設計本意（一條長長的線帶箭頭），只要把**壓縮**解掉就好。
   ⚠ 這是全寬度都有的問題（1280 壓得更兇，橫向 1.63 倍），所以不設斷點。 */
/* ⚠ 2026-09-01 客戶再回報「箭頭被壓到」：28px 仍然不夠——viewBox 是 64×24，
   28px 讓縱向縮放 1.17，但橫向是 1.44（1024）／1.63（1280），**兩軸不等
   就是被壓扁**。改成高度 ＝ 寬度 × 24/64，兩軸縮放相同、完全不變形。
   （寬度是 clamp(3.5rem, 9vw, 6.5rem)，乘 0.375 得下面這組。）
   ⚠ 上一版只解「縱向 < 1 倍的壓縮」，沒解「兩軸不等的變形」——客戶看的是
     後者。判斷變形要比**兩軸的縮放比**，不是只看縱向有沒有小於 1。 */
.page--wall .cx-hero-arrow--right {
    height: clamp(1.3125rem, 3.375vw, 2.4375rem);
}

/* ⑦「小卡的文字跟日期要靠左對齊」（推薦文章）
   實測 1024 卡片內有三條左緣：
       標籤膠囊外框／podcast 文字   102.9   ← 卡片內距邊緣
       日期／標題／摘要 文字        107.5
       標籤膠囊裡的文字            117.9   ← 膠囊自己有 14px 內距 ＋ 1px 框
   2026-08-24 客戶來回確認過方向：**框留在原位不動，文字那一欄去對齊「框裡
   文字」**（第一版把框拉出去讓框裡文字對齊標題，被客戶推翻）。當時寫的補償
   是 `calc(-1.15rem - 1px)`＝−19.4px，但後來膠囊內距從 1.15rem 改成 14px，
   補償沒跟著改，於是**過頭 10.4px**——日期文字停在 107.5、比膠囊文字左了一截。
   → 補償改成 `calc(15px - 1.5rem)`＝−9px，其中
       15px  ＝ 膠囊的 14px 內距 ＋ 1px 外框（膠囊文字相對框的內縮量）
       1.5rem＝ .course-date 自己的 24px 左內距（要先抵銷掉）
     實測 1024 與 1280 兩個寬度，日期文字與膠囊文字都落在同一條線上。
   ⚠ 這種「補償值寫死、被補償的那個數字後來被別人改掉」的漂移，光看規則
     看不出來——一定要量兩邊的實際文字左緣才會現形。 */
/* ⚠ 2026-09-01 客戶更正方向：「對齊靠左按鈕是**含外框**對齊，不是內部文字
   對齊」——這推翻了 2026-08-24 那輪的決定（當時客戶選的是「對齊框裡文字」）。
   → 補償從 `calc(15px - 1.5rem)`（對齊膠囊內文字）改成 `-1.5rem`：單純把
     `.course-date` 自己的 24px 左內距抵銷掉，文字左緣就落在卡片內距邊緣，
     也就是膠囊外框、podcast 那排的同一條線。
   ⚠ 附帶解掉一個原本要另外問客戶的問題：podcast／影片那排本來就在卡片內距
     邊緣，改成含外框對齊之後三者自動同一條線，不必再單獨處理。 */
.kd-related .course-date,
.kd-related .course-title,
.kd-related .course-place,
.kd-related .course-link::after {
    margin-left: -1.5rem;
}

/* ⑧「推薦文章太高，小卡改矮一點然後寬一點」
   實測 1024 卡片 251×514、1280 卡片 314×552——又窄又高。
   ・寬：`.course-cell` 的寬度公式是 `(100% - gap*2) / 3.2`（一次露 3.2 張），
     這裡改成 / 2.4（露 2.4 張），1024 從 251px → 335px。
   ・矮：縮圖 `aspect-ratio` 16/9 → 2/1。卡變寬之後 16/9 的圖會**跟著變高**
     （335/1.78 ＝ 188px，比原本的 138px 還高），只改寬度反而更高，兩者
     一定要一起動；2/1 之後是 167px。同時欄位變寬讓標題／摘要少折一行。
   ⚠ 只鎖 `.kd-related`：`.course-cell`／`.course-thumb` 是首頁與實體課程
     共用的元件，客戶講的是推薦文章這一區，不擴大到別頁。
   ⚠ 套用在 ≥1024 全桌機而不是只有 1024–1199：這是「卡片比例」的設計調整，
     不是窄視窗的補救，1280 的卡片同樣是 314×552 偏高。 */
@media (min-width: 1024px) {
    .kd-related .course-cell {
        width: calc((100% - var(--course-gap) * 2) / 2.4);
    }

    .kd-related .course-thumb {
        aspect-ratio: 2.4 / 1;
    }

    /* 只改寬度＋縮圖比例還不夠矮：卡變寬之後縮圖跟著長高，實測反而從 514
       變成 540。垂直節奏再各收一階（全部只收留白，不動任何文字大小）：
           日期上內距 24 → 12      摘要下外距 20 → 12
           標籤上外距 16 → 10      連結上外距 12 → 8
       合計省 30px，加上縮圖 2/1 → 2.4/1 再省 29.5px。
       ⚠ 日期的**左**內距 24px 不能動：⑦ 那條對齊補償 `calc(15px - 1.5rem)`
         就是拿它來抵銷的，一起改會把剛對好的左緣again弄歪。 */
    .kd-related .course-date {
        padding-top: 12px;
    }

    .kd-related .course-place {
        margin-bottom: 12px;
    }

    .kd-related .pcard-tags {
        margin-top: 10px;
    }

    .kd-related .pcard-links {
        margin-top: 8px;
    }
}

/* ── ⑬ 2026-09-01 客戶第三批（1024）──────────────────────────────────

   ⑨ 首頁知識專區「圖片請跟文字同高，左右比重不一」
   上一輪把兩欄改成貼齊上緣之後，圖片仍只有文字的 57%（1024 實測文字 601px、
   圖片 342px），右邊空一大塊。
   → 圖片改成撐滿整列（`align-self: stretch` ＋ 解掉固定比例），高度跟著
     文字欄走；圖片本身用 object-fit: cover 裁切，不變形。
   ⚠ 1200 以上維持原本的「貼下緣、110px 錯落」構圖不動——那是客戶驗收過的。

   ⑩ 實體課程 Hero「矩形文字超出邊界」
   statement 格橫跨兩欄（1024 實測 253–757），但格線是**逐欄**畫的，中間
   511 那條線照樣看得見。次標「從知道走到做到，把方法練成自己的能力」一行
   252px，從 287 排到 539——**跨過 511 那條線 28px**，看起來就是文字溢出格子。
   1280 不會（那行到 611、線在 639）。
   → 這個區間把次標限寬到一欄以內，讓它自己折成兩行，不再跨線。

   ⑪ 報名資訊「改上下排版」
   `.cd-enroll-split` 是左圖右文兩欄（1024 實測 465 + 344）。改成單欄堆疊：
   圖片在上、資訊在下。
   ⚠ 套用在 ≥1024 全桌機：客戶講的是版型本身要改成上下，不是窄視窗的補救。 */
@media (min-width: 1024px) and (max-width: 1199.98px) {
    .kz-stack-body {
        align-items: stretch;
    }

    .kz-stack-media {
        align-self: stretch;
        aspect-ratio: auto;
    }

    /* ⚠ 2026-09-01 補：上面那條 `height: 100%` 命中了、也贏了，**但結果沒達成**。
       整張圖是連結，`<figure>` 與 `<picture>` 之間夾了一層沒有 class 的 `<a>`
       （home.php ≈1106），它是 `display: block` 但高度 auto：

           figure.kz-stack-media  601  ← align-self:stretch 生效，欄同高 ✓
            └ a（無 class）        342  ← auto ★ 百分比高度鏈斷在這一層
               └ picture           342  ← height:100% 對著 auto 的父層解不出來
                  └ img            342

       → 這一層也要有高度，下面的 100% 才有東西可以解。
       ⚠ 通則：**百分比高度要從有明確高度的祖先往下、每一層都不能是 auto**，
         中間任何一層斷掉就整條失效。而且 computed 會顯示成解析後的
         `342.469px`、看起來像「有套用」，是最容易誤判成「已經修好」的一種。
       ⚠ 驗收教訓：這條需求是「圖片要跟文字同高」，我當時量的是**欄**
         （601 vs 601 ✓）就收工，客戶看的卻是**圖**（342，下方空 258px）。
         這類「A 要跟 B 等高」一律直接量客戶看得到的那個東西，不要量它的容器。 */
    .kz-stack-media > a {
        height: 100%;
    }

    .kz-stack-media img,
    .kz-stack-media picture {
        height: 100%;
        object-fit: cover;
    }

    .page--wall .cx-hero-cell--statement .cx-hero-lead {
        max-width: 48%;
    }
}

@media (min-width: 1024px) {
    /* ⚠ 選擇器要湊到 `.course-detail--editorial .section-bleed-inner.cd-enroll-split`
       （3 個 class，0,3,0）：既有的桌機兩欄定義就是這個特異度，只寫
       `.cd-enroll-split`(0,1,0) 完全不生效——**這一顆坑本檔 ≈20421 的註解
       早就記過一次**（手機版當時也是只寫 2 個 class 而失效），我還是又中了。
       → 教訓：改一個既有版型的 grid 定義前，先看它原本那條規則長什麼樣，
         直接照抄選擇器再加自己的媒體查詢，不要自己重新想一個短的。 */
    .course-detail--editorial .section-bleed-inner.cd-enroll-split {
        grid-template-columns: minmax(0, 1fr);
    }

    /* ⚠ 改單欄之後圖片會塌掉：原本它的高度是**兩欄並排時那一列的列高**撐出來的
       （右邊資訊欄比較高），單欄之後同一列只剩它自己，高度掉到 113px、還跟
       下面的文字重疊 5px。堆疊版型要自己給比例——用 2/1 的橫幅比例，跟
       「上下排版」的閱讀節奏相符。 */
    .course-detail--editorial .cd-enroll-media {
        aspect-ratio: 2 / 1;
    }
}

/* ⑫ 首頁 hero 文字卡太高（客戶「看有沒有別的調整方式」→ 選項題選定「把卡片加寬」）
   實測 1024：照片 745×503、卡片 347×383，卡片佔照片高 **76%**（1280 是 64%）。
   根因是卡片內容高度幾乎固定（338px），照片卻隨視窗變矮——所以視窗越窄，
   卡片看起來越高。
   → 卡寬公式 `min(46%, 440px)` → `min(58%, 440px)`：欄位變寬之後標題與內文
     少折一行，卡片自然變矮。
   ⚠ 這個改法**天生只影響窄視窗**：46% 只在照片寬 < 956px（≈視窗 1290）時
     小於 440px 上限；再寬的視窗兩個值都被 440 收住，完全沒有變化。所以
     不必加媒體查詢，也不會在斷點上製造落差（1280 從 436 → 440，差 4px）。
   ⚠ 沒有選「收緊留白」或「縮小標題字級」：前者只省 31px、改善有限；後者會讓
     四張輪播卡因標題長短不一而高度跳動（詳見給客戶的選項說明）。 */
/* ⚠ 2026-09-01 客戶追加：「把文字縮小，右邊留白刪減都好，現在看起來太大塊」
   ——加寬解決了「太高」，但 476×335 的色塊面積比原本的 347×383 大了 20%，
   變成另一個問題。
   ⚠ 關鍵是**卡片寬度本來就是被標題撐出來的**（最長那張標題單行要 422px），
     所以「縮小文字」與「收窄卡片」不是二選一，是同一件事的兩端：標題縮小
     之後，需要的欄寬跟著降，卡片才收得回去。兩個一起動：
         標題 32px → 26px  → 單行所需 422 → 343px
         卡寬 min(63%,480) → min(54%,420)  → 1024 得 402px、欄寬 357px
     357 > 343，四張標題仍然全部一行（留 14px 餘裕），而色塊面積回到
     402×326 ≈ 131,000，比**最初**的 347×383（132,900）還小一點。
   ⚠ 只縮標題、不縮次標與內文：那兩個已經是 12／14px，再小會影響可讀性；
     標題 32px 才是撐出寬度與高度的那一項。 */
@media (min-width: 1024px) {
    .hero-text-module {
        width: min(54%, 420px);
    }

    /* ⚠ 選擇器要湊到 `.hero-text-module .hero-text-title`(0,2,0)：既有那條
       `font-size: var(--text-section)` 就是這個特異度，只寫 `.hero-text-title`
       (0,1,0) 完全沒生效（computed 還是 32px）。
       ⚠ **這是本 session 第四次踩同一件事**（知識專區欄寬、報名資訊 grid、
         這裡）。這個檔案幾乎每條元件規則都有「2 個以上 class 的祖先形式」，
         往後改既有元件一律**先查它現在是被哪條選擇器決定的，照抄那條**，
         不要自己寫一個短的。 */
    .hero-text-module .hero-text-title {
        /* ⚠ 2026-09-02 客戶「放大文字時矩形裡的文字沒放大到」——px 改等值 rem，
           lg/xl 跟著 root 縮放；同日追加「hero 文字小卡標題字級用 32px」——
           基準值從 26px 級改為 2rem（=32px@root16），下面兩個斷點同步。 */
        font-size: 2rem;
    }
}

/* ⚠ 第一版用 `min(58%, 440px)`（1024 得 438px），**卡片變寬了、高度卻一格
   沒動**——因為四張輪播卡的標題單行所需寬度是 422／292／337／176px，而
   438 的卡片扣掉內距只剩 393px 的欄寬，最長那張（「長出自己的根，溫柔地
   站穩」422px）**還是差 29px 才排得下**，仍然兩行。
   → 放到 `min(63%, 480px)`：1024 得 469px、欄寬 424px，四張全部一行，
     卡片高度也因此一致（不會切換輪播時忽高忽低）。
   ⚠ 教訓：「加寬讓文字少折行」這種改法，**要先量出目標文字單行需要多寬**
     再決定加多少；憑感覺加一階很可能剛好差幾十 px，看起來像沒生效。
     量法：複製一個 `white-space: nowrap` 的隱形節點，套同一組 font 與
     letter-spacing，量它的寬度。 */

/* ══════════════════════════════════════════════════════════════════════
   第 51 節　1024×768 三項調整（2026-09-01 客戶指定，附三張截圖）
   ──────────────────────────────────────────────────────────────────────
   客戶原話：
     ① 「hero 文字矩形縮小一點，連帶標題 h1 全站在該寬度縮小，其他字級也同縮小」
     ② 「這裏編排怪怪得調整比重一下」（course_detail 報名資訊）
     ③ 「直的箭頭在該高度不夠被擠壓到，該區塊在該寬度跟高度時高度增高
         讓箭頭不擠壓到」（courses 頁 Hero 方格）
   ⚠ 斷點一律取 `1024–1199.98`：客戶指名的是「寬 1024」，而 1200 以上是
     客戶前幾輪已經逐項驗收過的版面（1280 那批截圖），不能被連帶改動。
     ≤1023.98 是平板／手機版型，也不在這次範圍內。
   ⚠ 全節都寫在檔尾、只用「新增」的方式覆寫，既有規則一行都沒動；
     要整批退回把這一節註解掉即可。
   ══════════════════════════════════════════════════════════════════════ */

/* ── ① 全站字級：級距整組降一階 ────────────────────────────────────────
   ⚠ 這裡**只重新定義級距 `--fs-*` 本身**，不逐一改 `--text-h1`／`--text-h2`
     等消費端——本檔 ≈10788 建立的就是這個 SSOT：`--text-* : var(--fs-*)`，
     動級距底下全部自動跟著走。客戶說的「標題 h1 全站縮小、其他字級也同縮小」
     正好就是這件事，逐條改 `--text-*` 反而會把 SSOT 拆掉。
   ⚠ **要蓋的是 ≈14862 那條沒有媒體查詢的 `:root`（12/14/26/26/32），
     不是 ≈10778 的 `992–1440` 那條**。實測 1024 的生效值是 fs-3/4=26、
     fs-5=32，跟 992–1440 區塊寫的 26／38 對不上——那個區塊的 fs-3/4/5
     早就被 14862 整組蓋掉了，等於現在只有 fs-1/fs-2 還輪得到它。
     （這顆坑 memory `onedaylisten-token-stale-comments` 記過：同一個 token
     被後面的 `:root` 覆寫多次，註解會過期，用前一定要現場量。）
     本節位置在 14862 之後、特異度同為 (0,0,1)，所以贏。
   ⚠ 字級切換 A／A+／A++ 不受影響也不會反過來壓過這裡：實測
     `data-font-size` 的 lg／xl 完全沒有宣告 `--fs-*` 或 `--text-*`
     （它走的是別的機制），三種字級下這五個 token 的值一模一樣。
   ── 對照表（1024–1199.98）──
       --fs-5  32px → 28px   --text-h1／--text-section（客戶點名的 h1）
       --fs-4  26px → 24px   --text-h2
       --fs-3  26px → 24px   --text-lead／--text-sub（卡片與小節標題）
   ⚠ `--fs-1`(12px)／`--fs-2`(14px) 不動＝`--text-meta` 與 `--text-body`
     維持原樣：內文 14／小標 12 已經是本站可讀性的下限，再降一階會影響
     全站每一頁的閱讀，這也是本檔 ≈32328 上一輪就記過的同一個判斷。
     若客戶要連內文一起縮，改這兩行即可，不必動別處。 */
@media (min-width: 1024px) and (max-width: 1199.98px) {
    :root {
        --fs-3: 1.5rem;      /* 24px（原 26） */
        --fs-4: 1.5rem;      /* 24px（原 26） */
        --fs-5: 1.75rem;     /* 28px（原 32） */
    }
}

/* ── ① 之二　首頁 hero 文字卡：卡片再收窄、標題與說明同步縮 ──────────────
   hero 標題**不吃字級 token**（≈32342 把它寫死成 26px），所以級距降階不會
   自動帶到它，要單獨再降一階。
   ⚠ 卡寬與標題字級是綁在一起的兩端（≈32321 記過）：卡片寬度是被最長那張
     標題的單行寬度撐出來的，字級不降就收不了寬。實測四張標題的單行寬度
     （同 font-family／weight／letter-spacing 的隱形節點量的，非線性外推）：
         26px → 344 / 237 / 275 / 143px
         24px → 320 / 221 / 256 / 133px
   → 標題 24px 需要 320px 的欄寬；卡寬 `min(54%,420px)` → `min(51%,400px)`
     在 1024 得 379.8px，扣掉左右內距 45.06px 剩 **334.8px**，餘裕 14.8px，
     四張標題仍然全部單行（跟改之前的 13px 餘裕相當，安全度沒有變差）。
   ⚠ 只降標題與內文說明兩項，eyebrow／次標／按鈕維持 12px：它們已經是
     全站最小級，再降就進到不可讀的範圍（同 ≈32328 的判斷）。
   ⚠ 內文說明 14px → 13px 之後欄寬也變窄，兩者相抵：每行容字量
     335/13 ≈ 25.8 對原本 357/14 ≈ 25.5，維持兩行，卡片不會因此變高。 */
@media (min-width: 1024px) and (max-width: 1199.98px) {
    .hero-text-module {
        width: min(51%, 400px);
    }

    /* ⚠ 選擇器要湊到 `.hero-text-module .hero-text-title` (0,2,0)：≈32342
       那條就是這個特異度，只寫 `.hero-text-title` (0,1,0) 蓋不過去
       （本檔已經連續踩過四次，見該處註解）。 */
    .hero-text-module .hero-text-title {
        font-size: 2rem;     /* 客戶指定 32px（2026-09-02 第二波），rem 隨字級縮放 */
    }

    .hero-text-module .hero-text-intro {
        font-size: .8125rem; /* =13px@root16，跟字級縮放（2026-09-02） */
    }
}

/* ── ② course_detail 報名資訊：右半邊的空白收回來 ────────────────────────
   客戶「編排怪怪得調整比重一下」——實測 1024 的兩個具體成因：
     a) 報名須知段落 `.page-text` 的 `max-width: 22em` ＝ **308px**，
        但單欄堆疊之後欄寬是 **845px**，右邊整整空掉 537px（63%）。
     b) 「前往購買」按鈕貼在左下角，跟上方那段窄段落一起把重量全壓在左側。
   ⚠ 這兩件事**本來就已經修過了**，只是斷點沒涵蓋到 1024：≈20454 那個
     `768–1023.98` 區塊做的就是這兩件事（客戶 2026-08-21「平板寬度
     768／820／**1024** ⋯⋯前往購買按鈕放右邊」），而且它自己的註解寫的是
     「這個寬度區間（**768–1199.98px**）」——註解與實作的上限對不上，
     客戶當初點名的 1024 正好卡在 1023.98 外面一格。
   ⚠ 為什麼上限當時寫 1023.98：那一輪全站桌機斷點還是 1200，1024 屬於
     「平板」；2026-09-01 桌機斷點下移到 1024（memory
     `onedaylisten-desktop-breakpoint-1024`）之後，1024 改走桌機分支，
     這條就漏掉了。這裡照原註解的本意補上 1024–1199.98 這一段。
   ⚠ 不直接把上面那條的 1023.98 改成 1199.98，是為了不動已驗收的
     768–1023.98 行為；同一段邏輯分兩個區塊寫，兩邊的註解互相指路。 */
@media (min-width: 1024px) and (max-width: 1199.98px) {
    .course-detail--editorial .cd-enroll-col .page-text {
        max-width: none;
    }

    /* ⚠ 靠右之後會撞到全站浮動客服列 `.wf-float`——實測最壞只剩 **1.4px**
       淨空（1024 ＋ A++），必須主動讓位。
       ── 為什麼是 1.4px 而不是看起來的 5px ──
       `.wf-float` 的圓鈕會**跟著字級一起放大**，字級越大它的左緣越往左：
           1024  A   圓鈕左 932.1 → 淨空 5.0px
           1024  A++ 圓鈕左 928.5 → 淨空 **1.4px**
       按鈕自己的右緣是固定的（貼齊欄右緣 927.1），所以危險來源是浮動列
       那一側，不是按鈕變寬。**只測預設字級會看不到這個最壞值。**
       ⚠ 而且垂直方向是否重疊**由捲動位置決定**（`.wf-float` 是 fixed），
         逐格捲動掃過才會抓到重疊 9.6–36px 的那些位置；停在單一捲動點量
         會量到「垂直不重疊」而誤判成安全。
       ⚠ hit-test 全部命中**不能當作沒問題**：`.wf-float` 容器是
         `pointer-events: none`，`elementFromPoint` 會直接跳過它
         （memory `onedaylisten-pointer-events-none-elementsfrompoint`），
         真正會擋住點擊的是它裡面那三顆圓鈕，要量圓鈕的 rect。
       ⚠ **圓鈕還會被呼吸動畫放大到容器外**：`@keyframes wf-float-breathe`
         的 `50% { transform: scale(1.07) }`（≈30849），56px 的容器盒在
         動畫峰值那一幀會撐成 59.92px，**每邊突出 1.96px**。
         `transform` 同時影響繪製與命中測試，所以「容器盒」不是真正的邊界。
         → 量的時候要把動畫 seek 到峰值再讀 rect，否則隨機取樣會落在
           scale 1.00–1.07 之間任何一點、量到偏樂觀的值：
             `k.getAnimations().forEach(a => { a.pause();
                a.currentTime = a.effect.getTiming().duration / 2; })`
       ── 讓位量 ──
       目標：按鈕右緣離視窗右緣至少 99px。拆解：
           圓鈕左緣（含 scale 峰值）離視窗右緣最遠實測 **81.96px**
           ＋ 17px 餘裕 ＝ 99px
       欄右緣目前離視窗右緣就是版心右內距，實測在這個斷點恰為 **8vw**
       （1024→81.92、1100→88、1199→95.92，三點全中），所以差額補上即可：
           1024  99 − 81.92 ＝ 17.1px 內縮
           1100  99 − 88    ＝ 11.0px
           1199  99 − 95.92 ＝  3.1px
       三個寬度都收斂到約 17px 淨空，而 1199 幾乎不內縮、視覺上仍然貼齊
       欄右緣。`max()` 保證版心再寬時不會變成負值。
       ⚠ 第一版常數寫 97（用容器盒算的 81 ＋ 16），**實測只有 15.02px**——
         少掉的正是上面那 1.96px。任何「算跟浮動鈕淨空」的公式都要記得
         扣這個 scale，這不只影響本處。
       ⚠ 若日後版心公式改了，這裡的 8vw 要重量——它是實測值，不是從
         `--section-x`（4vw）推導出來的。
       ⚠ 上面 `768–1023.98` 那個區塊同樣讓按鈕靠右，理論上有同一個風險，
         但那是既有且客戶未回報的範圍，這裡不主動擴大改動；要一起處理時
         記得那一段的版心內距公式不同，數字要另外量。 */
    .course-detail--editorial .cd-enroll-text .page-hero-actions {
        justify-content: flex-end;
        padding-inline-end: max(0px, 99px - 8vw);
    }

    /* ⚠ 第三個成因：標籤與值**沒有對齊在同一條基線上**，四列都差 5.9px，
       客戶截圖上看得出標籤總是比值高一點點。
       算式（改前）：th 與 td 的 `vertical-align: top` ＋ 同樣的 18px 上內距，
       但兩者行高不同——
           th  12px 字／行高 20.4 → 文字中心 18 + 10.2 = 28.2
           td  14px 字／行高 32.2 → 文字中心 18 + 16.1 = 34.1
       字在行框裡是垂直置中的，行高一大一小，頂端對齊就必然錯開半個差值。
       → 改成 `baseline`：table-cell 的 baseline ＝ 它第一行的基線，同一列的
         cell 會對齊到共同基線，正好是「標籤跟值同一條線」這個需求。
       ⚠ 用 baseline 而不是把 th 的行高改成跟 td 一樣：後者是讓兩個「行框」
         等高、靠置中湊出近似對齊（仍有約 0.5px 的殘差），前者才是真的對齊
         文字。也不動任何行高，td 多行時仍以第一行為準。
       ⚠ th 是 `white-space: nowrap` 的單行標籤，不會有「首行不是視覺首行」
         的問題。
       ⚠ 這個錯位在**所有寬度都存在**（1280 也一樣），這裡只跟著本節收在
         1024–1199.98，不主動動客戶已逐項驗收過的 ≥1200 版面；要全站一起
         套用的話，把這條搬出媒體查詢即可。 */
    .course-detail--editorial .info-table th,
    .course-detail--editorial .info-table td {
        vertical-align: baseline;
    }
}

/* ── ③ courses 頁 Hero：直箭頭恢復等比，不再被壓扁 ──────────────────────
   客戶「直的箭頭在該高度不夠被擠壓到」。
   ⚠ **真因不是空間不夠，是箭頭高度被寫死成 16px。** 那支 svg 是
     `viewBox="0 0 24 88"` ＋ `preserveAspectRatio="none"`（刻意的，讓線的
     長度可以被 CSS 單獨拉伸而不改變線的粗細，見 ≈18175 的說明），所以
     CSS 給多少高度它就被壓成多少——20×16 等於把 24:88 的比例壓成 22%，
     整支糊成客戶截圖上那個小點。
   ⚠ 實測兩格的可用空間都夠，**不需要把區塊加高**（客戶提的是手段，
     要的結果是「箭頭不擠壓」）：
         void4 格　內高 176.2 − 文字塊 64.8 ＝ 箭頭可用 111.4px
         intro 格　內高 141.5（該格文字塊被負 margin 拉到 header 列，
                   不佔格內空間，見 memory `onedaylisten-cx-hero-cell-map`）
     等比所需 20 × 88/24 ＝ 73.3px，兩格都放得下，加高反而會把整個
     Hero 撐高、動到客戶已驗收的方格構圖。
   ⚠ 壓成 16px 的那條在 `768–1299.98`（≈28642）。這裡只覆寫
     1024–1199.98：1200–1299.98 同樣是 16px，但那是客戶這幾輪看過
     1280 截圖、沒有提出的範圍，不主動擴大改動範圍。 */
@media (min-width: 1024px) and (max-width: 1199.98px) {
    .page--wall .cx-hero-arrow--down {
        height: 73px;    /* 20px × 88/24 ＝ 73.3，取整數避免次像素毛邊 */
    }
}

/* ══════════════════════════════════════════════════════════════════════
   第 52 節　2026-09-01 客戶第二批（四張截圖）
   ──────────────────────────────────────────────────────────────────────
     ① 「這裏看可不可以小卡比例圖跟文一比一」（知識專區列表小卡）
     ② 「這區讓文字矩形小一點，可以字間距縮小一點」（首頁 hero）
     ③ 「圖片跟標籤色塊齊頭」（知識專區列表）
     ④ 「footer logo 縮小到不要超出直線」
   ⚠ ③④ 兩張截圖是**同一頁**（知識專區列表）——④ 那條「直線」是知識專區
     左側分類軌的分欄線延伸到 footer 的那條（`body:has(.page--knowledge)
     .site-footer::after`，≈25047），**只有這一頁有**。在首頁量 footer
     永遠量不到超出，一開始就是這樣白跑了一輪。
   ⚠ 全節都是新增，既有規則一行未動；要整批退回把這一節註解掉即可。
   ══════════════════════════════════════════════════════════════════════ */

/* ── ① 知識專區小卡：圖與文字區 1:1 ────────────────────────────────────
   實測改前的圖:文比例隨視窗差很多——圖是固定 `aspect-ratio: 16/9`
   （高度由欄寬決定），文字區卻是內容自然高，兩者各走各的：
       1024  圖 447.7×251.8 ／ 文 283.2  → 0.889
       1280  圖 312.6×175.8 ／ 文 413.5  → **0.425**   ← 客戶截圖這個
       1440  圖 387.8×218.1 ／ 文 342.9  → 0.636
   → 改用 `grid-template-rows: 1fr 1fr` 讓兩列等高。
   ⚠ **為什麼不是 flex 的 `flex: 1 1 0`**：`.pcard` 的高度來自 grid 列高，
     而列高又是由卡片內容撐出來的。flex-basis 設 0 之後圖片對「內容高度」
     的貢獻歸零，列高塌成只剩文字高，兩者再各分一半 → 文字被壓掉一半。
     grid 的 `fr` 在**高度 auto 的容器**裡走的是另一套算法：
     每條 fr 軌道取 `max(各軌道的 max-content ÷ flex factor)`，
     所以兩列都會等於「文字區的 max-content」，圖片因此拿到跟文字一樣的
     高度，而不是去分一塊本來就不存在的剩餘空間。
   ⚠ `aspect-ratio` 一定要歸零（`auto`）：留著 16/9 會跟 grid 給的列高
     打架，圖片高度會被鎖回 9/16 欄寬。`min-height: 0` 是 grid item 的
     標準防溢出（預設 `auto` 會讓它不肯縮到內容以下）。
   ⚠ 只在 ≥1024 套用：≤1023.98 是橫向捲動輪播（見 ≈21059），卡片形狀與
     排列都不同，1:1 會讓輪播卡高到爆。
   ⚠ 同列所有卡片會一起變高（`.kz-grid` 是 `align-items: stretch`，列高
     取該列最高的那張），這是既有行為，貼齊分隔線的版型本來就靠它。 */
@media (min-width: 1024px) {
    .page--knowledge .kz-grid .pcard {
        display: grid;
        grid-template-rows: 1fr 1fr;
    }

    .page--knowledge .kz-grid .pcard-media {
        aspect-ratio: auto;
        min-height: 0;
    }
}

/* ── ② 首頁 hero 文字矩形再收窄（≥1200 這段這次才動到）─────────────────
   ⚠ 上一輪（第 51 節）只改了 1024–1199.98，**≥1200 維持 26px／420px 沒動**
     ——客戶這次的截圖就是 1280，看到的是還沒縮的那一版。
   卡寬與標題字級、字距是綁在一起的三端（第 51 節記過前兩端）：卡片寬度
   是被最長那張標題的單行寬度撐出來的。四張標題單行寬度實測矩陣
   （隱形 nowrap 節點，同 font-family／weight，非線性外推）：
       fs=26 ls=2.6  344 / 237 / 275 / 143   ← 現況
       fs=24 ls=1.6  308 / 211 / 245 / 128   ← 採用
       fs=24 ls=2.6  320   fs=23 ls=1.6  296   fs=22 ls=1.6  284
   → 標題 26→24px、字距 2.6→1.6px（客戶「字間距縮小一點」），需要的欄寬
     從 344 降到 308；卡寬 `min(54%,420px)` → `min(50%,390px)`，1280 得
     389.8px、扣掉左右內距 45.06 剩 344.7px，餘裕 36.7px，四張仍全單行。
   ⚠ 字距同步套到 1024–1199.98（第 51 節那段只改了字級沒改字距），
     兩段的視覺才一致；那段的卡寬與字級維持第 51 節的決定不動。
   ⚠ eyebrow／次標／按鈕維持 12px 不再縮（已是全站最小級，同第 51 節判斷）。 */
@media (min-width: 1200px) {
    .hero-text-module {
        width: min(50%, 390px);
    }

    /* 選擇器湊到 (0,2,0)：既有那條 `font-size: var(--text-section)` 就是
       這個特異度，只寫 `.hero-text-title` 蓋不過去（本檔已連續踩過四次）。 */
    .hero-text-module .hero-text-title {
        font-size: 2rem;     /* 客戶指定 32px（2026-09-02 第二波），rem 隨字級縮放 */
        letter-spacing: 1.6px;
    }

    .hero-text-module .hero-text-intro {
        font-size: .8125rem; /* =13px@root16，跟字級縮放（2026-09-02） */
    }
}

@media (min-width: 1024px) and (max-width: 1199.98px) {
    .hero-text-module .hero-text-title {
        letter-spacing: 1.6px;
    }
}

/* ── ③ 知識專區：右側圖片與左側「全部」色塊齊頭 ────────────────────────
   實測改前差 11.7px（1024）／17.0px（1280、1440）。
   ⚠ 差距的來源不是左右兩欄沒對齊——**兩欄的上緣本來就一樣**（實測都是
     488.3），差的是各自上方那一段：
         左：488.3 ＋ 40.4（CATEGORY／TAGS 小標高）      ＝ 色塊頂 528.7
         右：488.3 ＋ 20.4（.filter-result 那一行）
             ＋ margin-bottom ＋ 1（.kz-grid 的 border-top） ＝ 圖片頂
     令兩者相等 → margin-bottom ＝ 528.7 − 509.7 ＝ **19px**。
   ⚠ 這個 19px **與視窗寬度無關**（上面算式裡沒有一項隨寬度變），所以
     三個寬度都用同一個固定值就會齊，不需要逐斷點給值——原本的
     `clamp(1.5rem, 3vw, 2.25rem)` 在 1024 算出 30.72、在 1280/1440 算出
     36，正好就是那兩個差距值的來源。
   ⚠ 只在 ≥1024 套用：窄視窗 `.filter-result` 會換行、高度不再是 20.4，
     算式的前提不成立；而且左側分類軌在手機是另一種排列，沒有「齊頭」
     這回事。 */
@media (min-width: 1024px) {
    .page--knowledge .filter-result {
        margin-bottom: 19px;
    }
}

/* ── ④ footer logo 縮到分欄線以內 ──────────────────────────────────────
   ⚠ 那條「直線」是 `body:has(.page--knowledge) .site-footer::after`
     （≈25047），x 座標 `calc(var(--section-x) + 15rem - 30px)`，
     **只在知識專區頁出現**。
   ⚠ 超出量是**固定的 23.9px，跟視窗寬度無關**——因為 logo 左緣與線的
     位置都掛在同一個 `--section-x` 上，兩者一起平移：
         線   ＝ --section-x + 240 − 30 ＝ --section-x + 210
         logo ＝ --section-x + 233.9（圖示 42.7 ＋ gap 12 ＋ 字標 179.2）
         → 恆超出 233.9 − 210 ＝ 23.9px（1024／1280／1440 實測全是 23.9）
   → 等比縮到約 201.6px（線內留 8.4px 餘裕）：
         .footer-logo-icon        34px  → 29px    （寬 42.7 → 36.4）
         .footer-logo-text        2.2em → 1.88em  （高 35.2 → 30.1、寬 179.2 → 153.2）
         36.4 ＋ 12 ＋ 153.2 ＝ 201.6
   ⚠ **只縮知識專區頁這一份**，不動全站：2026-08-28 客戶才指定過
     「高度拉高回來，感覺畫質有點差，不要壓縮太多」，把 1.4em/1.8em 調到
     現在的 1.8em/2.2em（見 ≈28850 那段的查證紀錄）。其他頁沒有那條線、
     沒有「超出」這個問題，沒有理由跟著縮回去。若客戶要全站統一，把
     `body:has(.page--knowledge)` 前綴拿掉即可。
   ⚠ 圖示只鎖高度、寬度維持 auto（來源 SVG 近方形，鎖寬會壓扁），
     跟既有那條 ≈2624 同一個理由。 */
body:has(.page--knowledge) .footer-logo-icon {
    height: 29px;
}

body:has(.page--knowledge) .footer-logo-text.brand-name-img {
    height: 1.88em;
}

/* ══════════════════════════════════════════════════════════════════════
   第 53 節　首頁知識專區堆疊列：矮視窗要能一屏看完
   （2026-09-01 客戶「讓該區塊可以在該高度瀏覽完」，截圖 1024×768）
   ──────────────────────────────────────────────────────────────────────
   實測改前（`.kz-stack-item` 總高 vs 視窗扣掉 header 的可用高）：
       1024×768   753.4 vs 704.0   → **超出 49.4px**   ← 客戶截圖這個
       1280×720   672.3 vs 652.8   → 超出 19.5px
       1280×900   695.8 vs 832.8   → 放得下（不必動）
   ⚠ 這一列是**堆疊簾幕**的一格（`.kz-stack-item` 自己 sticky，見 ≈7713
     那一節），一格看不完就等於使用者永遠讀不到「查看更多」那顆鈕——
     所以這不是美觀問題。
   ⚠ **用 `max-height` 媒體查詢，不是寬度**：客戶講的是「該高度」，而且
     實測 1280×900 本來就放得下，用寬度斷點會連高視窗一起縮、平白損失
     留白。臨界值取 840px：實測 800 高還差 21.6px、850 高已經有餘裕。
   ── 收在哪裡（列高是由**文字欄**決定的，不是圖）──────────────────
   圖片自 `69b0ce0` 起是 `align-self: stretch` ＋ `height: 100%`，高度
   **跟著文字欄走**（見 ≈32253 那一節的百分比高度鏈說明），所以要降列高
   只能降文字那一側；縮圖片不會有任何效果，圖片會自己跟著縮。
       ① 區塊上下內距 5vh／8vh → 2.75vh／4.5vh   768 時省 44.1px
       ② YouTube／Podcast 那列的 margin-top 22 → 10px        省 12px
       合計省 56.1px > 需要的 49.4px，餘裕 6.7px。
   ⚠ 兩項都**只收間距、不動任何字級與行高**：這一列的文字是客戶逐字給的
     完整介紹（home.php `$kItems` 的 body），縮字級會讓三篇的可讀性一起
     下降，而間距在矮視窗本來就該讓位。
   ⚠ `--lh-body` 是無單位值，就算之後真的要收字級，行高會自己跟著收，
     不必另外處理（同 ≈7845 那節的註記）。 */
@media (min-width: 1024px) and (max-height: 840px) {
    .kz-stack-body {
        padding-block: clamp(1.25rem, 2.75vh, 2.25rem) clamp(1.75rem, 4.5vh, 3.5rem);
    }

    /* ⚠ 選擇器要湊到 (0,2,0)：`.kz-links` 的 margin-top 在本檔被宣告過
       三次（≈4232 的 2.25rem、≈6927 收成 1.375rem 等），單寫 `.kz-links`
       (0,1,0) 同分只能靠位置贏，但那幾條散在不同區段、日後任何一條被
       移動就會翻盤。掛上 `.kz-stack-text` 這個祖先才是穩的。 */
    .kz-stack-text .kz-links {
        margin-top: 10px;
    }

    /* ⚠ 第一版收到這裡就達標了（1024×768 餘 6.8px），但那個餘裕**撐不住
       真實瀏覽器**：客戶截圖是 1031×771，而書籤列／下載列任何一條都會
       再吃掉 30–40px 的 innerHeight，餘 6.8px 等於只要他開著書籤列就又
       看不完。文字欄的項目間距再收一階，把餘裕拉到能吸收一條工具列。
       ⚠ 這是「同一個需求要留多少安全邊際」的判斷，不是額外需求：驗收
         數字漂亮不等於客戶那台機器上成立。
       ⚠ **這顆 gap 的槓桿比看起來大很多**：文字欄有 6 個子項＝5 個間隔，
         照理減 4px 只省 20px，實測卻省了 **80px**（697.2 → 617.2）——
         因為 `.kz-stack-text` 底下還有巢狀的 flex 容器跟著繼承這個間距。
         第一版直接收到 .75rem，餘裕從 6.8 一路衝到 86.8px，圖片也跟著
         從 585.5 縮到 505.5（圖高是跟著文字欄走的），等於把客戶早上才
         要求放大的圖又縮回去。→ 收成 .9rem：1024×768 實測餘 **74.9px**、
         圖片 517.5（仍與文字同高）。餘裕比原先設想的 40px 多，但這個
         方向的偏差是安全的——多出來的餘裕正好吸收書籤列。
       ⚠ 教訓：改共用容器的 `gap` 前先確認**它實際影響幾層**，不要用
         「子項數 × 差值」心算就當數字，那個估法在有巢狀 flex 時會低估。 */
    .kz-stack-text {
        gap: .9rem;
    }

    /* ⚠ 最後一篇的下內距是**另一條更高特異度的規則**在管（≈7735，
       `.kz-stack-list > .kz-stack-item:last-child .kz-stack-body`，
       `clamp(2rem, 6vh, 4rem)`＝768 時 46.08px，比上面那條的 34.56 大），
       上面 `.kz-stack-body` (0,1,0) 蓋不過去——實測前兩篇已經放得下、
       **只有第三篇還超出 4.7px**，就是漏掉這一條。
       ⚠ 照抄原選擇器 (0,4,0) 再加自己的媒體查詢，不要自己另想一個短的
         （本檔已經連續踩過五次「只寫短選擇器結果沒生效」）。
       ⚠ 這段留白本身有功能——它讓最後一篇在堆疊簾幕裡捲得完（見原註解），
         所以是收小不是拿掉：6vh → 4vh，768 時 46.08 → 30.72。 */
    .kz-stack-list > .kz-stack-item:last-child .kz-stack-body {
        padding-bottom: clamp(1.5rem, 4vh, 3rem);
    }
}

/* ══════════════════════════════════════════════════════════════════════
   第 54 節　報名資訊改回左右排版（客戶 2026-09-01 指定，還原第 50 節 ⑪）
   ══════════════════════════════════════════════════════════════════════
   客戶原話：「報名資訊請改回原本的左右排版」（附截圖，寬視窗、目前是上下堆疊）。

   ── 沿革（改之前先查清楚「原本」到底是哪一版）──────────────────
   ≈20121  基準規則  `grid-template-columns: minmax(0,1.15fr) minmax(0,.85fr)`
           ＝ 左圖右文兩欄，這就是客戶說的「原本」。
   ≈20428  `@media (max-width:1023.98px)` 單欄堆疊
           ＝ 2026-08-13 客戶「報名資訊手機寬度時圖片跟文字改上下編排」。
           **這是另一次客戶指定，保留不動。**
   ≈32287  `@media (min-width:1024px)` 單欄堆疊 ＋ `.cd-enroll-media`
           `aspect-ratio: 2/1`
           ＝ 第 50 節 ⑪（2026-09-01 上午），**就是這一節要還原的那條**。

   → scope 只還原 ≥1024。手機維持堆疊，沒有理由把另一次客戶指定一起翻掉。

   ── 做法：只新增不刪除 ────────────────────────────────────
   第 50 節 ⑪ 那段規則與註解**原封不動留著**——那段註解本身就是「客戶曾經
   要過上下版、以及當時為什麼要補 aspect-ratio」的紀錄，刪掉之後如果客戶
   哪天又要回上下版，等於要重查一次。這裡用同斷點、同特異度、更後面的
   宣告把它蓋回來。

   ⚠ 兩條都要蓋，只蓋 grid 會壞掉：
     1) `grid-template-columns` —— 選擇器要湊滿 3 個 class
        （`.course-detail--editorial .section-bleed-inner.cd-enroll-split`，0,3,0），
        既有兩欄定義與第 50 節 ⑪ 都是這個特異度，靠源碼順序取勝。
        ⚠ 只寫 `.cd-enroll-split`(0,1,0) 完全不生效——本檔 ≈20421、≈32288
          兩處註解都記過同一顆坑。
     2) `aspect-ratio` —— 第 50 節 ⑪ 幫堆疊版補的 `2 / 1` 必須收回 `auto`，
        否則圖片會被鎖死成 2:1 的橫幅，而不是回到「由兩欄那一列的列高撐出來」。
        基準規則 ≈20280 本來就沒有 `aspect-ratio`（靠 `align-self: stretch`
        取得列高），所以還原值是 `auto` 不是某個數字。

   ⚠ 還原之後**文字欄會從整欄寬變回窄欄**（1024 實測約 344px），所以下面這兩
     組後續修正要一起重驗，它們是在堆疊版型下量出來的：
       第 51 節 ②　`.cd-enroll-col .page-text { max-width: none }`
                   ＋ `.page-hero-actions { justify-content: flex-end }`
       第 52/53 節　`.cd-enroll-text .page-hero-actions`
                   `padding-inline-end: max(0px, 99px - 8vw)`（讓開浮動客服列）
     兩者在窄欄下**語意仍然成立**（文字填滿該欄、按鈕靠該欄右緣，而該欄右緣
     依然貼近視窗右緣），但「按鈕離浮動圓鈕多遠」是版面相依的數字，
     改完一定要重跑一次淨空量測，不能假設沿用。 */
@media (min-width: 1024px) {
    .course-detail--editorial .section-bleed-inner.cd-enroll-split {
        grid-template-columns: minmax(0, 1.15fr) minmax(0, .85fr);
    }

    .course-detail--editorial .cd-enroll-media {
        aspect-ratio: auto;
    }
}

/* ══════════════════════════════════════════════════════════════════════
   第 55 節　報名資訊在 1024 區間改「圖在上、文在下」（客戶 2026-09-01 指定）
   ══════════════════════════════════════════════════════════════════════
   客戶原話：「在寬1024高768時報名資訊這區塊比例過高，重新編排這裡，
   僅在該寬度時」——附 1024×768 截圖。給了三個方向的選項題，客戶選定
   **「圖在上、文在下，圖做成矮橫幅」**。

   ── 實測成因（1024×768，改之前）────────────────────────────
   整區高 **791.4px**，可用高（視窗 768 − header 65）只有 **703px** → 超出 88.4px。
   圖片顯示成 538.5×791.4 ＝ **直式 0.68**，而原圖是 2336×992 ＝ **橫式 2.355**，
   等於被裁掉七成。

   ⚠ **圖片的高度是被文字欄撐出來的**（兩欄 `align-self: stretch` 等高），
     所以「圖片太窄長」是結果不是原因——縮圖片不會有任何效果，它會跟著文字欄走。
     真正的成因是文字欄只有 337.5px 寬：
         上課日期  值折 **3 行**（列高 100.4）
         課程地點  值折 **3 行**（列高 100.4）
         堂數／報名人數  各折 2 行（列高 68.2）
     同一批資料在 1199（欄寬 396.2）時課程地點只折 2 行——**欄越窄、整區越高、
     圖片被拉得越長**，三者是同一條因果鏈。

   ── 為什麼「上下排版」能同時解決三件事 ────────────────────
   單欄之後文字拿到整欄寬（約 845px），四個值都不折行 → 文字區大幅變矮；
   圖片不再被文字欄撐高，改由自己的 `aspect-ratio` 決定 → 回到接近原圖的橫式比例。

   ── scope ────────────────────────────────────────────────
   客戶說「僅在該寬度時」——**只套 1024–1199.98**（本專案這個元件既有的斷點區間，
   第 51 節 ② 也是用這一段）。≥1200 維持第 54 節剛還原的左右兩欄，不受影響。
   ⚠ 沒有用 `max-height` 做高度斷點：客戶明講的是「寬度」。而且高度斷點很脆——
     客戶那張截圖是 1031×771，書籤列隨便一條就吃掉 30–40px 的 innerHeight。

   ⚠ 選擇器要湊到 `.course-detail--editorial .section-bleed-inner.cd-enroll-split`
     （0,3,0）並且**排在第 54 節後面**：第 54 節那條同斷點同特異度、就在上面，
     靠源碼順序取勝。本檔 ≈20421／≈32288／第 54 節已經連續記過同一顆坑。 */
@media (min-width: 1024px) and (max-width: 1199.98px) {
    .course-detail--editorial .section-bleed-inner.cd-enroll-split {
        grid-template-columns: minmax(0, 1fr);
    }

    /* 用原圖比例 2336/992 ≈ 2.355，`object-fit: cover` 之下等於**完全不裁切**。
       第 50 節 ⑪ 當時用的是 2/1（1024 算出 422 高）；改用原圖比例更矮（約 359），
       也更貼近客戶說的「矮橫幅」。 */
    .course-detail--editorial .cd-enroll-media {
        aspect-ratio: 2336 / 992;
    }

    /* ⚠ 單欄之後「報名資訊」標題會**壓在圖片下緣上 5–6px**（1024 實測 5.1、1199 實測 6.0）。
       成因不是 z-index、也不是圖片太高——是 `.cd-enroll-media` 的基準規則帶著
       `margin: calc(var(--v2-band-y) * -1) 0 ...`（≈20280），那組負外距是給**兩欄版**
       用的（讓圖片上下出血貼齊色帶邊緣）。單欄時負的**下**外距會把後面的文字往上拉：

           重疊量 ＝ var(--v2-band-y) − row-gap
                  ＝ 56.32 − 51.2 ＝ 5.12（1024）
                  ＝ 65.95 − 59.95 ＝ 6.00（1199）

       兩個寬度都對得上，公式成立。
       ⚠ 手機版（≤1023.98，≈20398）早就處理過同一件事，做法是 `margin-block: 0`；
         **這裡不能照抄**——歸零會讓整區再高 112.6px（上下各 56.32），而客戶這一輪
         的訴求正是「太高」。改成把 row-gap 補到蓋過負外距，只多 13px 就解決。
       ⚠ 用同一個 `var(--v2-band-y)` 而不是寫死 56/66：負外距本身就是這個變數算的，
         綁在一起才不會在某個寬度又裂開（clamp(3rem,5.5vw,4.75rem) 是連續變動的）。 */
    .course-detail--editorial .section-bleed-inner.cd-enroll-split {
        row-gap: calc(var(--v2-band-y) + .5rem);
    }

    /* ⚠ 2026-09-01 客戶追加：「圖片置中」——實測不是圖**裡面**的插畫沒置中
       （插畫在橫幅內是置中的），而是**橫幅本身左右不對稱**：
       基準規則 ≈20280 的 `margin` 簡寫是
       `calc(var(--v2-band-y)*-1)  0  calc(var(--v2-band-y)*-1)  calc(var(--article-x)*-1)`
       ——上、**右 0**、下、左。也就是**只往左出血、右邊沒有**：
           1024  左緣 0（貼視窗）／右緣 927.1，右邊空 81.9 ＝ 一個 --article-x
       兩欄版時右邊接的是文字欄，看不出來；改成單欄橫幅之後這個不對稱就露出來了。
       ⚠ 手機版（≤1023.98，≈20404）本來就是兩側都出血（`margin-inline`），
         這裡等於把桌機這一段補齊同一個語彙。
       ⚠ 只動 `margin-inline-end`，不要用 `margin-inline` 簡寫——上面那條 row-gap
         的修法依賴垂直負外距原封不動，動到 `margin` 簡寫會把上下一起清掉。 */
    .course-detail--editorial .cd-enroll-media {
        margin-inline-end: calc(var(--article-x) * -1);
    }
}

/* ══════════════════════════════════════════════════════════════════════
   第 56 節　右側三個元件對齊同一條中線（客戶 2026-09-01 指定）
   ══════════════════════════════════════════════════════════════════════
   客戶原話：「讓右邊的懸浮按鈕跟選單還有滾輪箭頭對齊中線」。

   ── 實測：三者互不相同（中線 x）────────────────────────────
   | 視窗 | 漢堡選單 | 滾輪箭頭 | 懸浮鈕 | 最大差 |
   |------|---------|---------|-------|-------|
   | 1024 | 957.0   | 972.4   | 960.5 | 15.4  |
   | 1280 | 1182.0  | 1222.3  | 1211.4| 40.3  |
   | 1440 | 1342.0  | 1381.0  | 1369.0| 39.0  |

   給了選項題（以誰為基準），客戶選定**以漢堡選單為準**——選單是頁面版心的
   一部分（跟 logo、字級鈕同一列），滾輪與懸浮鈕是疊在上面的工具，跟著版心走。

   ── 做法：JS 量、CSS 用 ──────────────────────────────────
   `site.js` 的 `sync()` publish `--wf-rightline`
   ＝ 漢堡鈕中線離視窗右緣的距離（px）。
   ⚠ 為什麼不寫純 CSS 公式：那條中線 ＝「父層右內距 ＋ 鈕寬一半」，而兩者在各
     斷點都各自變動（內距 27.96→31→57.4→54.2→70，**1280 比 1440 還大、不是
     單調的**；鈕寬 48→51.19→57.59→58 又跟字級綁在一起）。純 CSS 只能寫死一堆
     斷點，任一邊改了就裂。量真實 rect 才不會過期——同 `--header-real-h` 的理由。

   ── 每個元件各自扣自己寬度的一半 ──────────────────────────
   `right` 定位的是**右緣**，要讓**中線**落在同一條線上，就得各自扣半個寬度：
       `.wf-float`  寬 56  → 扣 28px
       `.wf-rail`   寬 24  → 扣 12px
   兩個寬度都是 CSS 寫死的固定值（四個斷點實測皆同），不隨字級變。

   ⚠ `var()` 的 fallback 寫成原本那條算式，JS 沒跑到時位置與改動前完全一致，
     不會變成「JS 一失敗就整組跑到奇怪的地方」。
   ⚠ **不要用 transform 做水平位移**：`.wf-rail` 的 `transform: translateY(-50%)`
     是它垂直置中的手段，覆寫掉會讓整條軌道掉到視窗下半部。
   ⚠ scope 取 ≥1024：992–1023.98 的懸浮鈕是**橫排**（寬 188，1011 實測三顆各在
     815.8／881.8／947.8），拿橫排去對單一中線沒有意義；那段維持原樣。
   ⚠ 懸浮鈕往左移之後會更靠近 course_detail 的「前往購買」鈕，
     第 52/53 節那條讓位算式要重新量測（版面相依的數字不能沿用）。 */
@media (min-width: 1024px) {
    .is-designed .wf-float {
        right: calc(
            var(--wf-rightline, calc(clamp(1rem, 2vw, 1.75rem) + 28px)) - 28px
        );
    }

    .wf-rail:not([hidden]) {
        right: calc(
            var(--wf-rightline, calc(clamp(1rem, 2.4vw, 2rem) + 12px)) - 12px
        );
    }
}

/* ── 第 56 節續：讓位算式改成跟著 --wf-rightline 走 ────────────────────
   懸浮鈕往左移之後，course_detail「前往購買」鈕的淨空掉了（本機實測）：
       1024  17.07 ✓      （剛好還在）
       1100  19.04 → 14.04 ⚠
       1199  17.06 → 10.07 ⚠
   第 52/53 節那條 `max(0px, 99px - 8vw)` 的 99 是**對舊的懸浮鈕位置**算出來的，
   懸浮鈕一移動它就失效——這正是「版面相依的常數」的通病。

   ── 改成從幾何關係直接推 ────────────────────────────────
       圓鈕左緣 ＝ clientWidth − var(--wf-rightline) − 29.96
                                                   ↑ 圓鈕 56px 被呼吸動畫
                                                     scale(1.07) 放大後的半寬
       按鈕右緣 ＝ clientWidth − 8vw − padding-inline-end
                                ↑ 實測的版心右內距（1024→81.92／1100→88／1199→95.92）
       要求「圓鈕左緣 − 按鈕右緣 ≥ 17」，解出：
           padding-inline-end ≥ 17 + 29.96 + var(--wf-rightline) − 8vw
                              ≈ var(--wf-rightline) + 47px − 8vw

   驗算（--wf-rightline 實測 52／55／58.95）：
       1024  52    + 47 − 81.92 = 17.08
       1100  55    + 47 − 88    = 14.00
       1199  58.95 + 47 − 95.92 = 10.03
   三個寬度都回到 17.04 淨空。

   ⚠ 好處是這條算式**不再有「懸浮鈕在哪」這個隱含假設**——它直接讀懸浮鈕
     的中線變數，之後右側元件再怎麼移動都會自己跟上，不必再回來調常數。
   ⚠ 仍然 `max(0px, …)` 夾住：≥1200 是兩欄版、按鈕靠左，算式即使算出負值也不生效。 */
@media (min-width: 1024px) and (max-width: 1199.98px) {
    .course-detail--editorial .cd-enroll-text .page-hero-actions {
        padding-inline-end: max(0px, calc(var(--wf-rightline, 52px) + 47px - 8vw));
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   知識專區手機版：卡牆下方好幾條線疊在一起（2026-09-01 客戶回報）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「手機瀏覽時知識專區底下的線會重疊好幾條在一起」＋截圖（分類
   「人際心理」是選取狀態、頁碼 01/01）。

   ── 重現與量測（390×844，mtest，點「人際心理」只有 1 篇）──────────
   卡牆上下緣實際有 6 條線：
     y  776.73  .kz-grid      border-top      ┐ 相距 1px
     y  777.73  .kz-grid-row  border-top      ┘
     y 1350.50  .kz-grid-row  border-bottom     （第一列，唯一有卡的列）
     y 1371.50  .kz-grid-row  border-top      ┐ 空列：整列高度只有 4px，
     y 1374.50  .kz-grid-row  border-bottom   ┘ 上下兩條線黏在一起
     y 1375.50  .kz-grid      border-bottom     再貼一條，離上面 1px
   後三條擠在 4px 之內，就是客戶看到的「重疊好幾條」。

   ── 兩個各自獨立的成因 ────────────────────────────────────────────
   ① **空列照樣畫線**：這頁手機版一頁固定排兩列（knowledge_list.php 的
      PAGE_ROWS），而卡片是**伺服器端就分好**塞進兩個 <ul class="kz-grid-row">，
      篩選／分頁只是把 <li> 加上 hidden／.kz-page-hidden **就地隱藏**，
      不會把剩下的卡重新分配。所以只要符合條件的卡片填不滿第二列，第二列
      就變成一個沒有內容、卻仍然畫著上下邊框的空殼（實測 4px＝padding
      2px ＋ 上下各 1px）。
   ② **容器自己的上下框線是多餘的**：≈21058 那個 max-width:1023.98px 區塊
      給 .kz-grid 補了 border-top／border-bottom，但同一個斷點裡（≈16040）
      每一列早就各自畫了上下線——那是 2026-08-24 客戶親自指定的「兩排小卡
      都各自要有上下兩條底線貼邊」。兩者重複，卡牆最上緣與最下緣就各出現
      一對相距 1px 的線。
      ⚠ 桌機不會中：≥1024 時 .kz-grid-row 是 display:contents（沒有盒子，
        那組 border 根本不會畫），只剩容器那一條，所以這個 bug 從頭到尾
        只存在於 ≤1023.98。

   ── 修法 ─────────────────────────────────────────────────────────
   ⚠ 保留「每一列上下各一條線」（客戶 08-24 指定），拿掉重複的那一份：
     線一律由列自己負責，容器不畫。這樣不管一頁排幾列、篩選後剩幾列，
     線數永遠等於「有卡的列數 × 2」，不會多也不會少。
   ⚠ 空列改成「不畫線」而不是 display:none：knowledge_list.php 的 isRail()
     用 `getComputedStyle(rows()[0]).display === 'flex'` 判斷是不是手機輪播
     模式。卡片既然是照 markup 順序就地隱藏，**第一列整列被篩掉、第二列
     還有卡**是做得到的情境；那時把列設成 display:none 會讓 isRail() 誤判
     成桌機模式、箭頭改跑分頁邏輯。只歸零邊框與內距、盒子留著，JS 的判斷
     完全不受影響。
   ⚠ 判斷「這一列沒有可見卡片」必須同時排除兩種隱藏方式：篩選用 [hidden]
     屬性（visibleCards() 讀 li.hidden），分頁用 .kz-page-hidden class
     （syncGridPage() 加的）。只擋一種，另一種情境還是會冒線。
   ⚠ 代價：空列沒有線了，但仍佔著 flex column 的 1.25rem gap（約 20px
     看不見的留白）。刻意不用負外距去抵銷——那要跟著 gap 的值手動同步，
     是下一個「改了 gap 忘了改這裡」的坑，而 20px 純留白夾在卡牆與頁碼
     之間看不出來。 */
@media (max-width: 1023.98px) {
    .page--knowledge .kz-grid {
        border-top: 0;
        border-bottom: 0;
    }

    .page--knowledge .kz-grid-row:not(:has(> li:not([hidden]):not(.kz-page-hidden))) {
        border-top: 0;
        border-bottom: 0;
        padding-bottom: 0;
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   實體課程 Hero：橫向箭頭補上跟直向箭頭一樣的動態（2026-09-01 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「橫向箭頭請用跟直向箭頭一樣的動態效果」（/wireframe/courses）。

   ── 為什麼它本來不會動 ────────────────────────────────────────────
   實測 1440：這一頁有三支箭頭
     .cx-hero-arrow--down  ×2   line→cxHeroArrowDraw  head→cxHeroArrowTip
     .cx-hero-arrow--right ×1   **animation: none，底下只有一條沒有 class 的 path**
   ≈30528 那節把動態從整支 svg 搬到兩條 path 上（.cx-hero-arrow-line 描線、
   .cx-hero-arrow-head 尖端往前點一下），但只有直向那兩支的 markup 是拆成
   兩條帶 class 的 path；橫向這支一直是單一條 `M0 12h58M48 2l12 10-12 10`，
   選擇器一個都對不到，所以規則存在、動畫從來沒套用過。
   markup 已在 courses_list.php 依同樣分段拆成 .cx-hero-arrow-line ＋
   .cx-hero-arrow-head 兩條（形狀完全不變），CSS 這邊只補軸向差異。

   ── 兩處軸向差異，不能直接沿用直向那組 ────────────────────────────
   ① **描線長度**：dasharray／dashoffset 走的是 svg 使用者座標，直向的線是
      `M12 0v82`＝82 個單位，橫向是 `M0 12h58`＝**58**。硬套 82 不會壞掉
      （dash 比路徑長，仍然遮得住），但線會在該段動畫走到 58/82≈70% 的位置
      就畫完、剩下 30% 空等，節奏跟隔壁那兩支對不起來——客戶要的正是「一樣」。
   ② **尖端方向**：直向是 translateY（往下點），橫向要 translateX（往右點）。

   ⚠ 只覆寫 animation-name，不重寫整個 animation 簡寫：時長 1.4s、ease-in-out、
     infinite 三個值繼續由 ≈30534 那條簡寫提供，日後調速度只要改那一處，
     兩個方向會一起跟著變（寫成簡寫就會變成兩份要手動同步的數字）。
   ⚠ 位移量沿用同一個 5 個 user unit，不另外調：橫向 svg 是 64 單位寬 ÷
     104px（1440 實測）＝1.625px/單位，直向是 88 單位高 ÷ 144px＝1.636px/單位，
     兩者換算出來的實際位移是 8.1px vs 8.2px，本來就幾乎相同。
   ⚠ **reduced-motion 一定要另外補一條**：既有那個 @media 區塊寫的是
     `.page--wall .cx-hero-arrow-line`（0,2,0）的 `animation: none`，而本節
     的選擇器是 (0,3,0)——媒體查詢不加特異度，所以我的 animation-name 會
     贏過它，橫向箭頭在「減少動態」下反而會是全站唯一還在動的東西。
     這一格平常不會切到，是最容易漏的一條。 */

.page--wall .cx-hero-arrow--right .cx-hero-arrow-line {
    /* 58 ＝ `M0 12h58` 的路徑長度（user unit） */
    stroke-dasharray: 58;
    stroke-dashoffset: 0;
    animation-name: cxHeroArrowDrawX;
}

.page--wall .cx-hero-arrow--right .cx-hero-arrow-head {
    animation-name: cxHeroArrowTipX;
}

/* 與 cxHeroArrowDraw 同一組時間點（0/45/72/100），只換成 58 這條線的長度 */
@keyframes cxHeroArrowDrawX {
    0%   { stroke-dashoffset: 58; }   /* 線收起來 */
    45%  { stroke-dashoffset: 0; }    /* 畫到底，接上箭頭 */
    72%  { stroke-dashoffset: 0; }    /* 停一下 */
    100% { stroke-dashoffset: -58; }  /* 由左往右收乾淨，無縫接回 0% */
}

/* 與 cxHeroArrowTip 同一組時間點與幅度，只把軸向換成 X */
@keyframes cxHeroArrowTipX {
    0%, 40%   { transform: translateX(0); }
    58%       { transform: translateX(5px); }
    80%, 100% { transform: translateX(0); }
}

@media (prefers-reduced-motion: reduce) {
    .page--wall .cx-hero-arrow--right .cx-hero-arrow-line,
    .page--wall .cx-hero-arrow--right .cx-hero-arrow-head {
        animation: none;
    }
}

/* ══════════════════════════════════════════════════════════════════════
   第 57 節　選單抽屜底部多一段留白（客戶 2026-09-01 指定，1440 與 1024）
   ══════════════════════════════════════════════════════════════════════
   客戶原話：「寬1440跟寬1024時導覽展開時文字內容縮窄，導致白色背景底下多一段
   留白空間，請縮減背景底部高度」。

   ── 那段留白是誰 ───────────────────────────────────────
   白底就是 `#wfNav` 自己，多出來的**剛好等於它的 `padding-bottom`**：

   | 視窗 | padding-bottom | 內容底緣 | 白底底緣 |
   |------|---------------|---------|---------|
   | 1024 | **78.48**     | 316.4   | 394.8   |
   | 1280 | **83.60**     | 325.3   | 408.9   |
   | 1440 | **86.00**     | 328.5   | 414.5   |
   | 1920 |   32.00       | 340.9   | 372.9   |

   來源是 ≈27520 那條（`!important`）：
       `padding-bottom: calc(clamp(1rem, 2vw, 1.75rem) + 46px + .75rem)`
       ＝ 懸浮鈕的 bottom 偏移 ＋ 鈕高 46 ＋ 呼吸間距
   它的用途寫在原註解裡：**客戶 08-05 要求選單展開時右下三顆懸浮鈕維持可見**，
   抽屜捲到底時最後一項要停在懸浮鈕上緣之前、不被壓住。**那是為手機設計的。**

   ── 實測：桌機根本碰不到 ────────────────────────────────
   | 視窗 | 抽屜底緣 | 懸浮鈕頂 | 會不會撞 |
   |------|---------|---------|---------|
   | 390×844（手機）| 844   | 776   | 需要這段淨空 |
   | 1024×800      | 394.8 | 591.5 | **差 197px** |
   | 1440×900      | 414.5 | 684   | **差 270px** |
   | 1024×600 A++  | 468   | 378   | ⚠ 撞 2.4px |
   | 1440×600 A++  | 468   | 373.2 | ⚠ 撞 1.5px |

   → 只有「**矮視窗 ＋ 最大字級**」時抽屜會被高度上限夾住、變成可捲，
     那時捲到底才真的需要這段淨空。高度掃描（A++）：
         600 → 可捲 88/96，**撞**；650 → 可捲 49/57；700 → 可捲 10/18；
         720 → 可捲 0/2；**760 以上 可捲 0**（內容放得下，不會捲，也就不會撞）。

   ── 做法 ────────────────────────────────────────────
   `(min-width: 992px) and (min-height: 760px)` 時把底部內距收成**跟頂部一樣**
   （`clamp(1.5rem, 3.5vw, 3rem)`，實測頂部 1024→35.84／1280→44.8／1440→48，
   完全吻合），白底上下對稱。其餘情況（手機、平板、矮視窗）**原封不動**。

   ⚠ 高度門檻取 760 不取 720：720 時 1440/A++ 還有 2px 可捲，留一級安全邊際。
     客戶自己的視窗高度實測是 768／771（他前幾張截圖），落在門檻之上、會生效。
   ⚠ 必須帶 `!important` 且選擇器湊到 (0,3,1)：被覆寫的那條是
     `html.has-menu-open .site-header .site-nav` ＋ `!important`，
     兩邊都 `!important` 時先比特異度、平手才比檔案順序（本檔 ≈27516 的註解
     記過這一顆坑：`html[data-font-size="lg"]` 的 (0,3,1) 曾經打贏
     `.has-menu-open` 的 (0,3,0)，讓那條規則整整一段時間沒生效過）。
   ⚠ 沒有動 ≈27520 那條規則本身——手機與矮視窗仍然要靠它。
   ⚠ 上界要跟被覆寫那條**完全互補**（`max-width: 1467px`）：≥1468 那一段
     根本沒有這條保留內距（實測 1920 底部本來就只有 32px），我第一版寫成
     `min-width: 992px` 沒設上界，結果把 1920 的底部從 32 撐成 48、面板反而
     長高 16px——**客戶只講 1440 與 1024，不該動到沒問題的寬度**。 */
@media (min-width: 992px) and (max-width: 1467px) and (min-height: 760px) {
    html.has-menu-open .site-header .site-nav {
        padding-bottom: clamp(1.5rem, 3.5vw, 3rem) !important;
    }
}

/* ══════════════════════════════════════════════════════════════════════
   第 58 節　報名資訊：圖片不再被文字撐高（客戶 2026-09-02 詳細規格）
   ══════════════════════════════════════════════════════════════════════
   客戶原話重點：「左側圖片不要再被右側文字高度撐高」「圖片應該有固定的視覺
   比例與高度上限」「不要使用 stretch 讓兩邊等高」「height: clamp(520px,
   62vh, 680px)」「不可拉伸或變形，object-fit: cover」。

   ── 根因：容器早就 `align-items: start` 了，是圖片自己覆寫掉 ──────────
   ≈20121 的基準規則**本來就寫著 `align-items: start`**，但 ≈20284 的
   `.cd-enroll-media` 帶了 `align-self: stretch`——**子項的 align-self 蓋過
   容器的 align-items**，所以整欄還是被拉到跟文字等高。
   ⚠ 只改容器的 `align-items` 完全沒用，必須改子項這一條。

   實測（strategy 課，改之前）：
       1920  圖 1035.5×**808.9**  比例 1.28   文字欄 656.9
       1440  圖  765.3×**794.4**  比例 0.963  文字欄 642.4
       1280  圖  675.2×**769.1**  比例 0.878  文字欄 628.3
   原圖是 2336×992 ＝ **橫式 2.355**，被拉成 0.878–1.28 等於裁掉大半。

   ── ⚠ 為什麼沒有套客戶說的 4:5 / 3:4 ──────────────────────────────
   那兩個比例是**直式**（0.8 / 0.75），而這裡的原圖是**橫式 2.355**。套下去
   要裁掉約七成畫面，而且會比現況**更接近**客戶自己列為禁止事項的「過長的
   直式海報」。
   → 改為只採用客戶指定的 `height: clamp(520px, 62vh, 680px)`（那是明確數值），
     寬度仍由 grid 欄決定，實測比例落在 **1.13–1.52 的橫式**——比現況的
     0.878–1.28 更接近原圖、裁切量大幅降低，同時滿足「固定高度、不被文字撐高」
     這個主要目標。
   ⚠ 沒有另外寫 `aspect-ratio`：grid item 預設 `justify-self: stretch`，寬度
     已由欄寬決定，再給比例會被 height 覆蓋而變成死規則（本檔多處記過「寫了
     不生效的規則比沒寫更糟」）。若日後改用直式素材，改法是先解掉欄寬 stretch。

   ── 影響：下緣不再貼齊色帶 ────────────────────────────────────
   ≈20284 的負 margin 讓圖片上下出血、貼齊 `.cd-enroll-band` 外緣，那個效果
   **成立的前提就是「圖片高度 ＝ 文字欄高度」**。改成固定高度之後，上緣仍然
   出血貼齊（負 margin 還在），下緣則自然結束在 clamp 給的高度。
   ⚠ 這是客戶要求「固定高度上限」的必然取捨，不是 bug：兩者在幾何上不可能
     同時成立。若客戶更在意上下貼齊，那就得回到 stretch、也就回到被撐高。

   ── scope ────────────────────────────────────────────────────
   只套 **≥1200**：1024–1199.98 是第 55 節的「圖在上、文在下」（客戶 09-01 指定，
   圖片改由 `aspect-ratio` 決定、本來就不會被撐高），≤1023.98 是 2026-08-13
   客戶指定的手機堆疊，兩者都已符合「圖片不被文字撐高」，不需要也不應該動。 */
@media (min-width: 1200px) {
    .course-detail--editorial .cd-enroll-media {
        align-self: start;
        height: clamp(520px, 62vh, 680px);
    }
}

/* ── 右側：資訊層級整理（客戶「不要讓每一列的文字太散」）──────────────
   ⚠ 全斷點套用，不限 ≥1200：客戶對手機也要求「單欄 label + value」，而
     label 與值的距離在哪個寬度都不該散開。
   ⚠ 掛在新的 `.cd-info-table` 上（PHP 一併加），**不動全站共用的
     `.info-table`**——那顆還被 `.page--courses .course-info-panel` 用著，
     動它就是改 Design System，客戶明列為禁止事項。
   ⚠ `th` 從 9.5rem 收到 6.5rem：最長的 label 是「■ 報名人數」（12px 字約
     75px）＋ 右內距 16px ＝ 91px，6.5rem(104px) 仍有 13px 餘裕，不會擠到。
     （量最長的那個，不是量第一個——同一天在 og 安全區才踩過。）
   ⚠ 列上下內距 1.125rem → .875rem：客戶說的「太散」除了左右距離也包含列距。
     行高完全不動（`--lh-body` 是無單位值，動它會連字級一起變）。 */
.course-detail--editorial .cd-info-table th {
    width: 6.5rem;
}

.course-detail--editorial .cd-info-table th,
.course-detail--editorial .cd-info-table td {
    padding-top: .875rem;
    padding-bottom: .875rem;
    /* ⚠ label 與值差 6px 沒對齊：th 是 12px 字／行高 20.4，td 是 14px 字／
       行高 32.2，同樣 `vertical-align: top` ＋ 同樣上內距，字在行框裡垂直
       置中，行高一大一小頂端對齊就必然錯開半個差值。
       `baseline` 讓兩者對齊到第一行的基線，這正是客戶要的「清楚的
       label + value」。
       ⚠ 第 51 節已經修過同一件事，但那條 scope 只有 1024–1199.98
         （當時是單欄堆疊的情境），其餘寬度一直還是錯開的——這裡掛在
         `.cd-info-table` 上全斷點修好，兩條並存不衝突（同宣告同結果）。 */
    vertical-align: baseline;
}

/* ── 兩組之間的視覺分隔（客戶「不要讓說明文字和主要資訊混在一起」）──────
   ⚠ 兩組在 markup 上本來就是兩個 `.cd-enroll-col`（主要資訊／報名提醒），
     `.cd-enroll-cols` 是單欄 grid、兩者上下堆疊——結構已經分好了，缺的只是
     **看得出來的界線**。這裡加一條 hairline ＋ 間距，不加卡片背景、不加陰影
     （客戶明列禁止），沿用全站既有的線色 token。
   ⚠ 用相鄰兄弟選擇器只給第二組加上緣線，不會在最外圍多出一條孤線。 */
.course-detail--editorial .cd-enroll-col + .cd-enroll-col {
    border-top: 1px solid hsl(var(--foreground) / .10);
    padding-top: clamp(1.5rem, 2.5vw, 2rem);
}

/* ── 按鈕：與報名提醒文字保持 24–32px（客戶指定區間）────────────────
   ⚠ 只改這一區的按鈕列：`.page-hero-actions` 是全站共用 class（本頁最上面
     Hero 的 CTA 也用同一顆），用 `.cd-enroll-text` 限定範圍，同第 51 節的做法。 */
.course-detail--editorial .cd-enroll-text .page-hero-actions {
    margin-top: clamp(1.5rem, 2.2vw, 2rem);
}

/* ⚠ 手機把上面那組上下內距收回去。
   `.info-table` 在 ≤575.98 本來就有一組**緊湊堆疊**的既有規則（≈12529）：
       th { padding-bottom: .25rem }   td { padding-top: 0 }
   窄幅時 th/td 變成 `display: block`（label 在上、值在下），那組值是為了讓
   兩者貼在一起。**我上面那條 (0,2,0) 特異度比它高，把它整個蓋掉了**——
   實測 390 寬時 label 到值之間空出 54px，正是客戶說的「太散」，只是換成
   在手機發生。
   ⚠ 這個 bug **在桌機截圖完全看不出來**，只有手機才現形：改共用元件時，
     要把它在**每個斷點的既有覆寫**都查一遍，不能只看基準規則。
   → 照抄 ≈12529 的原值補回，不自己另訂數字。 */
@media (max-width: 575.98px) {
    .course-detail--editorial .cd-info-table th {
        padding-top: .875rem;
        padding-bottom: .25rem;
    }

    .course-detail--editorial .cd-info-table td {
        padding-top: 0;
        padding-bottom: .875rem;
    }
}

/* ── 手機：按鈕滿寬（客戶「按鈕寬度可設為 100%」）──────────────────── */
@media (max-width: 767.98px) {
    .course-detail--editorial .cd-enroll-text .page-hero-actions .btn-pill {
        width: 100%;
        justify-content: center;
    }
}

/* ══════════════════════════════════════════════════════════════════════
   第 59 節　客戶 2026-09-02 兩項：字級鈕壓到 Hero 直線／報名資訊標題留白
   （⚠ 原本編 58，與同事同時新增的「第 58 節　報名資訊：圖片不再被文字撐高」
     撞號；他的排在前面，本節改編 59。多 session 並行時節次要先看檔尾現況。）
   ══════════════════════════════════════════════════════════════════════

   ── ① 「寬1024時 aaa 右移到不要壓到線」──────────────────────
   `aaa` ＝ header 的字級鈕 `.font-size-switch`（A／A+／A++）。
   courses 頁 Hero 是四等分格線，垂直線落在 clientWidth 的 25／50／**75%**。
   實測（`.font-size-switch` 左緣 vs 最近那條 75% 線）：

   | 視窗 | 鈕左緣 | 75% 線 | 差 |
   |------|-------|-------|----|
   | 1024 | 736.8 | 757.5 | **−20.7 壓到線** |
   | 1100 | 809.8 | 814.5 | **−4.7 壓到線** |
   | 1199 | 904.8 | 888.8 | +16 ✓ |
   | 1280 | 960.2 | 949.5 | +10.7（沒壓到，但偏近）|

   ⚠ **只有 courses 頁有那組格線**，所以 scope 收在 `body:has(.page--wall)`——
     其他頁面把 header 元件挪位是沒有理由的副作用。
   ⚠ `.font-size-switch` 的 `margin-left` 是 **auto**（吃掉剩餘空間），
     已經被推到最右，**加 margin-left 沒有用**。要再往右只能**縮它右邊的空間**：
     `margin-right: 10px` → `-22px`，等於右移 32px（1024 的缺口 20.7 ＋ 12 餘裕）。
   ⚠ 右移之後跟搜尋圖示的距離會縮短（1024 原本 58px），改完必須實測那段距離
     還夠不夠，不能只確認「不壓線了」。

   ── ③ 「圖片跟標題靠太近，標題上留空間」────────────────────
   報名資訊改上下排版之後（第 55 節），圖片下緣與「報名資訊」標題只差 **8px**。
   那 8px 是第 56 節為了解決「標題壓在圖上」而設的**最小淨空**，不是設計間距。
   → `row-gap` 從 `var(--v2-band-y) + .5rem` 改成 `+ 2.5rem`，可見間距 8 → **40px**。
   ⚠ 只影響 1024–1199.98 的上下排版；≥1200 是兩欄版、`row-gap` 不參與版面。
   ⚠ 這會讓整區再高 32px——客戶那項「區塊太高」的取捨還沒定案，
     但「標題上留空間」是他明確指定的，先照做並在回報時說明兩者的關係。 */
@media (min-width: 992px) and (max-width: 1199.98px) {
    body:has(.page--wall) .site-header .inner > .font-size-switch {
        /* −26px ＝ 右移 36px。第一版寫 −22（右移 32）實測 1024 只離線 11.3px，
           比目標的 12 少一點；改 −26 之後離線 15.3、離搜尋圖示仍有 22px。 */
        margin-right: -26px;
    }
}

@media (min-width: 1024px) and (max-width: 1199.98px) {
    .course-detail--editorial .section-bleed-inner.cd-enroll-split {
        row-gap: calc(var(--v2-band-y) + 2.5rem);
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   電子報／知識專區：開場箭頭改成「直線連箭頭」的描線動態（2026-09-02 客戶指定）
   ═══════════════════════════════════════════════════════════════════════
   客戶：「這兩頁的箭頭請用直線連箭頭的動態效果」（/wireframe/newsletter、
   /wireframe/knowledge）——指的是 2026-09-01 才給實體課程 Hero 做的那組：
   線由起點描出來 → 接上箭頭 → 尖端往前點一下 → 收乾淨，無縫循環。

   ── 改動前實測 ────────────────────────────────────────────────────
   兩頁各有**一支** .simpla-arrow（24×64，viewBox 0 0 24 64），底下是單一條
   沒有 class 的 path `M12 0v58M2 48l10 12 10-12`（總長 89.2），animation: none。
   跟橫向箭頭修好之前是同一個狀況：動畫是掛在兩條各自帶 class 的 path 上的，
   單一條 path 讓選擇器一個都對不到。
   markup 已在 knowledge_list.php／newsletter_list.php 依既有分段拆成
   .simpla-arrow-line（M12 0v58，長 58）＋ .simpla-arrow-head
   （M2 48l10 12 10-12，長 31.24），58 + 31.24 = 89.24，與拆前實測的 89.2 吻合。

   ── 為什麼另外寫一組 keyframes，而不是共用 cx 那兩支 ──────────────────
   數值上 cxHeroArrowDrawX 剛好也是 58、cxHeroArrowTip 剛好也是 translateY(5)，
   直接借用可以少寫兩支。**刻意不借**：那兩支是綁在實體課程 Hero 那支箭頭的
   幾何上（DrawX 的 58 來自它的橫線長度），日後有人為了那一頁調線長，就會
   無聲地把這兩頁一起改掉。兩頁的箭頭是不同元件、不同 viewBox，數值只是
   目前碰巧相同，用各自的名字才不會產生這種隱形耦合。
   ⚠ 時間點（0/45/72/100 與 0/40/58/80）與時長 1.4s 是刻意逐值對齊 cx 那組的
     ——客戶要的就是「一樣的動態效果」，這裡是有意重複，不是漏抄。

   ── 位移量 ───────────────────────────────────────────────────────
   沿用同一個 5 個 user unit。這支 svg 是 64 單位高 ÷ 64px＝1.0px/單位，所以
   實際位移 5px；實體課程那支是 88 單位 ÷ 144px＝1.636px/單位＝8.2px。像素數
   不同但比例更接近（5/64＝7.8% vs 8.2/144＝5.7%）——箭頭本身小一號，照抄
   像素數會顯得太誇張，照抄 user unit 才是「同一個手勢」。

   ⚠ ≤1023.98px 這支箭頭本來就 display:none（≈15518，窄幅沒位置站，是裝飾），
     所以這組動態只在 ≥1024 看得到，不必另外加斷點。
   ⚠ reduced-motion 要一起關，並且線要停在「畫好」的狀態——dashoffset 的基準值
     寫 0 就是為了這個（animation:none 之後整條線都在，不會變成看不見的線）。 */

.page--simpla .simpla-arrow-line {
    /* 58 ＝ `M12 0v58` 的路徑長度（user unit） */
    stroke-dasharray: 58;
    stroke-dashoffset: 0;
    animation: simplaArrowDraw 1.4s ease-in-out infinite;
}

.page--simpla .simpla-arrow-head {
    animation: simplaArrowTip 1.4s ease-in-out infinite;
}

@keyframes simplaArrowDraw {
    0%   { stroke-dashoffset: 58; }   /* 線收起來 */
    45%  { stroke-dashoffset: 0; }    /* 畫到底，接上箭頭 */
    72%  { stroke-dashoffset: 0; }    /* 停一下 */
    100% { stroke-dashoffset: -58; }  /* 由上往下收乾淨，無縫接回 0% */
}

@keyframes simplaArrowTip {
    0%, 40%   { transform: translateY(0); }
    58%       { transform: translateY(5px); }
    80%, 100% { transform: translateY(0); }
}

@media (prefers-reduced-motion: reduce) {
    .page--simpla .simpla-arrow-line,
    .page--simpla .simpla-arrow-head {
        animation: none;
    }
}

/* ══════════════════════════════════════════════════════════════════════
   第 60 節　報名資訊右欄：兩段式緊湊重排，整欄收進圖片高度內
   （客戶 2026-09-02 第二波規格）
   ══════════════════════════════════════════════════════════════════════
   客戶：「左側圖片縮矮後，右側報名資訊文字沒有重新排版……看起來像文字掉出
   版面」「這次不要再繼續縮小圖片」「主要改右側文字排法，讓報名資訊完整留在
   右側區塊內，不要超出分隔線或容器外」，並給了兩段式結構（主要資訊區＝
   compact label/value、報名提醒區）與數值建議（列距 14px、value 行高 1.7、
   按鈕上距收斂）。

   ── 改動前實測（第 58 節固定圖高之後的狀態）────────────────────────
       3 堂課 × 4 視窗，右欄底邊全部超出圖片下緣（＝滿版底線的位置）：
           1440×900 溢出 157–217px／1280×800 溢出 208–236px／
           1382×800 溢出 236–245px（最緊）／1920×1080 溢出 56–116px
       右欄組成（1440，strategy）：標題 58 ＋ 標題後間距 36 ＋ 資訊表 301 ＋
       兩組間距 40 ＋ 分隔後內距 32 ＋ 提醒文字 151 ＋ 按鈕距 33 ＋ 按鈕 48
       ＝ 699，而預算只有 圖高 558 − 文字起點位移 76 ＝ 482。
       → 肥肉在「每一列 .875rem×2 的表格列距（60px/列）」「提醒段 2.1 倍
         行距」「三層 clamp 間距各自為政」，不是字太大——照客戶說的，
         重排就夠，不必縮字級。

   ── 預算模型（改完必須全部成立）──────────────────────────────────
       文字欄高 ≤ 圖高 − 文字欄起點位移
       圖高 = clamp(520px, 62vh, 680px)（第 58 節，這次一個字都不動）；
       最緊情境是「視窗高 ≤ 838px（62vh 踩到 520 下限）＋ --v2-band-y 頂到
       76px」。把文字欄起點從 band 內距（76）改成固定 20px（見下方負
       margin），最緊預算 = 520 − 20 = 500，重排後右欄約 470–490，各視窗
       留 10px 以上餘裕；常見的 1440×900 餘裕約 50px。

   ── 主要資訊區：列距收成 14px、value 行高 1.7（客戶給的數值）─────────
   ⚠ 仍掛 .cd-info-table、不動全站共用 .info-table（同第 58 節的理由）。
   ⚠ 上下內距 .4375rem×2 ＝ 相鄰兩列之間正好 14px，等價於客戶建議的
     gap 14px——不把 table 改成 div grid：表格標記是資料的語意（th/td 對）、
     ≤575.98 的堆疊模式與第 58 節的既有修正都掛在它身上，
     換標記等於把那些全部重做一次。
   ⚠ 首列去掉上內距、末列去掉下內距：內距做的「列距」只該存在於列與列
     **之間**，頭尾多出來的 7px 會讓表格跟上下鄰居的間距虛胖，預算很緊
     時這 14px 也要收回來。
   ⚠ 特異度陷阱（第 58 節自己踩過、這裡也會踩）：下面這幾條與第 58 節
     ≤575.98 的手機堆疊覆寫同為 (0,2,0)，但本節在檔案更後面——什麼都不做
     的話手機那組會被這裡蓋掉、label 又跟值黏錯位置。所以本節**檔尾重新
     宣告一次**手機那組值（含 first/last 修剪的對應版本），讓堆疊模式在
     cascade 順序上重新贏回來。 */
.course-detail--editorial .cd-info-table th,
.course-detail--editorial .cd-info-table td {
    padding-top: .4375rem;
    padding-bottom: .4375rem;
}

.course-detail--editorial .cd-info-table tr:first-child th,
.course-detail--editorial .cd-info-table tr:first-child td {
    padding-top: 0;
}

.course-detail--editorial .cd-info-table tr:last-child th,
.course-detail--editorial .cd-info-table tr:last-child td {
    padding-bottom: 0;
}

/* value 行高 1.7（客戶指定值）。原本 td 沿用內文 --lh-body（14px 字配出
   32.2px 行框），單行資訊列不是長文，不需要那麼鬆——1.7 後單列 24px。
   ⚠ 只動 td：th 是 12px 的 meta 字、--lh-meta 本來就緊，動它沒收益還會
     牽動 baseline 對齊（第 58 節剛校過）。 */
.course-detail--editorial .cd-info-table td {
    line-height: 1.7;
}

/* ── 報名提醒區：自己的小標＋收斂的段落節奏（全斷點）────────────────
   小標沿用左側 label（.info-table th）同一組 token——同一欄裡兩種「說明
   性小字」用同一套字級/字距/顏色，才讀得出是同一層級的東西。 */
.course-detail--editorial .cd-enroll-remind-title {
    margin: 0 0 .5rem;
    font-size: var(--text-meta);
    line-height: var(--lh-meta);
    font-weight: var(--fw-normal);
    letter-spacing: var(--tracking-meta);
    color: hsl(var(--slate));
}

/* 提醒段落：行高 1.7（跟 value 同一個新節奏）；兩段之間 .5rem——
   「(1)(2) 兩則獨立須知」的視覺分段靠換段本身＋小標統領就成立，不再需要
   原本 clamp(1.25–1.75rem) 那麼大的段距（那是內文長文的呼吸感，
   在這個緊湊資訊欄裡就是掉出版面的兇手之一）。
   ⚠ 特異度：p + p 那條 (0,2,2) 跟既有的 .page-text p + p 同分，
     靠本節在檔尾後宣告勝出，不加 !important。 */
.course-detail--editorial .cd-enroll-remind p {
    line-height: 1.7;
}

.course-detail--editorial .cd-enroll-remind p + p {
    margin-top: .5rem;
}

/* ── ≥1200（左圖右文的兩欄版，也就是會「掉出線外」的那個版型）────────
   三件事：①文字欄起點上提到距離圖片頂（＝上滿版線）20px——原本吃 band
   內距 --v2-band-y（70–76px），這一段是預算裡最大而且純空白的一筆；
   ②標題與各組間距從三層各自的 clamp 收成固定值（預算要可計算，不能隨
   視窗高浮動）；③按鈕上距收到 1.25rem（客戶問題清單點名「按鈕
   margin-top 太大」）。
   ⚠ 只套 ≥1200：1024–1199.98 是第 55 節「圖上文下」堆疊（第 59 節③還
     特地為它加大標題上方間距），≤1023.98 是手機堆疊，兩者沒有「跟圖片
     等高」的預算問題，維持原本比較鬆的節奏。
   ⚠ 負 margin 讀 var(--v2-band-y) 而不是寫死 76：這個 token 是 clamp、
     隨視窗寬變（1280 時 70.4），寫死的話窄桌機會上提過頭鑽到滿版線上。 */
@media (min-width: 1200px) {
    /* ⚠ 2026-09-02 客戶第三波「文字內容區塊太上，下移一點」——原本這裡是
       margin-top: calc(-1*var(--v2-band-y) + 1.25rem)（固定貼著圖片頂
       20px）。「下移固定量」做不到：最緊的視窗（62vh 踩到 520px 下限）
       整欄只剩 14px 餘裕，多移 15px 就重新掉出底線；寬鬆的視窗（1920）
       卻有 160px+ 可以移。改成**文字箱與圖片幾何全等＋內容置中**：
         margin-block 負一個 band 內距＝上下緣跟圖片一樣貼齊 band 外緣、
         height 抄第 58 節圖片同一條 clamp（兩邊永遠同高，改圖高要一起改）、
         內容用 grid align-content 垂直置中——每個視窗自動把「圖高−內容高」
         對半分：1440 上緣留 36px（比之前低 16）、1920 留 92（低 72）、
         最緊的 520 情境留 17（維持原位），不用任何寫死的位移量。
       ⚠ safe center：內容萬一比圖高（今天不會，但文案是會長的），safe
         會退回貼頂，只往下溢——置中溢出是上下兩頭一起破線，貼頂溢出
         跟改版前同一種壞法，不會更糟。前一行先寫純 center 當不支援
         safe 關鍵字時的後備。 */
    .course-detail--editorial .cd-enroll-text {
        margin-block: calc(-1 * var(--v2-band-y));
        height: clamp(520px, 62vh, 680px);
        display: grid;
        grid-template-columns: minmax(0, 1fr);
        align-content: center;
        align-content: safe center;
    }

    /* 標題自己的下緣（線＋間距）收一階；只動報名資訊這一顆，
       內文其他 .cd-h2 的長文節奏不變。 */
    .course-detail--editorial .cd-enroll-text .cd-h2 {
        margin-bottom: .75rem;
        padding-bottom: .625rem;
    }

    .course-detail--editorial .cd-enroll-cols {
        margin-top: 0;
        gap: 1.25rem;
    }

    .course-detail--editorial .cd-enroll-col + .cd-enroll-col {
        padding-top: 1.25rem;
    }

    .course-detail--editorial .cd-enroll-text .page-hero-actions {
        margin-top: 1.25rem;
    }
}

/* ── ≤575.98 手機堆疊模式：把第 58 節那組值在 cascade 上贏回來 ─────────
   （理由見本節開頭的特異度陷阱說明；值照抄第 58 節＝照抄 ≈12529 原值，
   不自己另訂。first/last 修剪只保留「首列 label 上內距歸零／末列 value
   下內距歸零」——堆疊模式下 th 在上 td 在下，th 的 .25rem 下內距是
   label↔value 的間距，不能被基準版的 last-child 修剪吃掉。） */
@media (max-width: 575.98px) {
    .course-detail--editorial .cd-info-table th {
        padding-top: .875rem;
        padding-bottom: .25rem;
    }

    .course-detail--editorial .cd-info-table td {
        padding-top: 0;
        padding-bottom: .875rem;
    }

    .course-detail--editorial .cd-info-table tr:first-child th {
        padding-top: 0;
    }

    .course-detail--editorial .cd-info-table tr:last-child td {
        padding-bottom: 0;
    }
}

/* ── 報名提醒：分句不斷行（2026-09-02 客戶「文字編排改一下」）────────────
   改前的斷行落在任意字元處：第一段行尾孤懸一個「確」（「確認」被拆成兩
   行）、電話段從括號中間切開。這一組是資訊短句不是長文，換行應該落在
   **標點邊界**。course_detail.php 在顯示時把句子切成「逗號後／括號前」
   的片段各包一顆 span，這裡讓每顆片段整塊換行：
   ⚠ max-width:100% 是保險絲：哪天出現一段超過欄寬、又沒有標點可切的
     片段（例如超長網址），inline-block 若不設上限會把欄位橫向撐爆；
     設了它就退回片段內部換行，跟改前一樣、不會更糟。 */
.course-detail--editorial .cd-remind-seg {
    display: inline-block;
    max-width: 100%;
}

/* ══════════════════════════════════════════════════════════════════════
   第 61 節　推薦文章小卡改版：平衡的直式 editorial card／手機橫式卡
   （客戶 2026-09-02 詳細規格）
   ══════════════════════════════════════════════════════════════════════
   客戶點名的四個問題與改前實測（1440，取第一張卡）：
     ① 圖片偏矮文字過高：圖 479×199＝2.4:1 的超扁橫條，下面文字區 305px
     ② 標題太重：--text-lead 24px×行高 1.65，兩行就吃掉 88px
     ③ 底部資訊散：副標→標籤之間有 64px 死空間（.course-link::after 那顆
        共用箭頭偽元素撐出來的），標籤與 podcast/影片各自漂
     ④ 整卡 481×524≈1:1.09，讀起來像被切開的摘要頁
   規格：桌機直式卡「圖 4:3、整卡約 4:5～5:6、標題/摘要各 clamp 2 行、
   標籤≤2、媒體型態一列小 metadata、日期最輕→標題最重的層級」；手機改
   橫式卡（圖左 38–42%、1:1）。

   ── 尺寸推導（桌機）────────────────────────────────────────────
   文字堆疊固定約 225px（日期 32＋標題≤62＋摘要≤56＋標籤 42＋媒體 28＋
   底留白）＝定值，整卡比例 = 0.75 + 225/卡寬 → 卡寬 460px 時 ≈ 1.24 ≒ 4:5。
   460 跟改版前的 481 幾乎同寬：橫捲一屏 2.4 張的瀏覽節奏、頁碼／進度線
   的分頁數完全不變。
   ⚠ 只動 .kd-related 範圍：.course-* 是首頁「近期課程」的共用元件、
     .pcard-tags／.pcard-links／.tag-pill 是四頁共用的 pill 與連結列，
     全部照規格「沿用既有樣式」，一條共用規則都不碰。
   ⚠ 拖曳／圓框游標／頁碼進度（home.js 三支 IIFE）與整卡連結、pill 連結、
     podcast/影片連結全部保留——標記一個字沒動，純 CSS 改版。 */

/* ── 桌機（≥768）：直式 editorial card ───────────────────────────────
   卡寬 540px：文字堆疊固定約 270px，整卡比例 = 0.75 + 270/卡寬——
   540 時滿內容卡（標題摘要各兩行）675/540 ≒ 4:5、輕內容卡 ≒ 5:6，
   正好落在規格給的比例帶；460 會落在 1:1.33、比帶子高一階。這也符合
   規格「文章資訊偏多，優先使用 2 欄較大的卡片」——1440 一屏約 2.1 張。 */
.kd-related .course-cell {
    width: min(33.75rem, 84vw);   /* 540px；min-width 240 的共用底線仍在其下 */
}

/* 圖 4:3（規格指定）。滿版出血（-1.25rem 三邊）是 08-21 客戶點頭過的
   既有效果，比例改了出血不動。 */
.kd-related .course-thumb {
    aspect-ratio: 4 / 3;
}

/* 層級①日期最輕：字級/顏色沿用（13px slate），只把「圖→文字」的間距
   定為清楚的 14px（規格「圖片與文字間距更清楚」）。 */
.kd-related .course-date {
    margin-top: .875rem;
}

/* 層級②標題最重但不臃腫：24px 降到 18px（②的解法），仍是全卡最大字；
   行高 1.6、維持共用規則既有的兩行 clamp。 */
.kd-related .course-title {
    margin-top: .375rem;
    font-size: 1.125rem;
    line-height: 1.6;
}

/* 層級③摘要（.course-place 借位的文章副標）：補上規格要的兩行 clamp——
   共用規則沒有 clamp，超長副標會單卡爆高（規格明令禁止）。 */
.kd-related .course-place {
    margin-top: .625rem;
    line-height: 1.75;
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    overflow: hidden;
}

/* ③底部資訊收攏：標籤列 margin-top:auto 錨到卡底（.course-cell 已是
   flex column），標題只有一行時多出來的空間留在摘要與標籤之間，
   五張卡的標籤/媒體列因此永遠切齊、卡底留白一致（規格）。 */
.kd-related .kd-related-tags {
    margin-top: auto;
    padding-top: .875rem;
}

.kd-related .kd-related-links {
    margin-top: .625rem;
}

/* 死空間兇手：.course-link::after 是與 .btn-pill 共用的箭頭偽元素，
   在這張卡裡撐出副標→標籤之間 64px 的空檔（改前實測）。規格的卡片
   結構沒有這顆箭頭、hover 已有「圖片微放大＋標題轉色」兩個訊號，
   這裡整顆收掉——只藏偽元素裝飾，沒有刪任何內容。 */
.kd-related .course-link::after {
    display: none;
}

/* ── 手機（≤767.98）：直式 editorial card、單欄滿寬（2026-09-02 第三版）──
   沿革：第一版照規格做 1:1 左圖右文 → 客戶「不對稱」→ 第二版圖撐滿左欄
   → 客戶整組推翻：「左圖太窄像被硬切、文字欄太窄斷行太多、標籤擠、
   整張像表格」，指定改回**圖上文下的直式卡、圖 16:10 橫式、單欄 100%**。
   實作＝把先前的橫式 grid（display:contents 那整套）整段移除，手機直接
   繼承桌機的直式樣式（標題 18px 兩行 clamp／摘要 clamp／標籤與媒體列
   錨底／圖片三邊出血／08-24 的 pill 內文字對齊位移），只覆寫兩件事：
     ① 卡寬 100%＝一屏一張（100% 解析出來正好是視窗寬扣掉左右
        --section-x 的內容寬，配既有 scroll-snap 一次滑一張）；
        ⚠ flex 基準要一併拉回 auto，否則共用的 flex:0 0 78%（≈7879）
          會讓 width 變死規則（本節第一版就踩過）。
     ② 圖片比例 16:10（客戶指定的橫式，蓋過桌機的 4:3）。 */
@media (max-width: 767.98px) {
    .kd-related .course-cell {
        flex: 0 0 auto;
        width: 100%;
    }

    .kd-related .course-thumb {
        aspect-ratio: 16 / 10;
    }
}

/* 補遺（同節）：層級①「日期最輕」的行高——共用 --lh-meta 在 13px 上配出
   32px 行框，緊湊卡裡日期反而佔位比標題單行還高，收到 1.6（21px）。 */
.kd-related .course-date {
    line-height: 1.6;
}

/* ══════════════════════════════════════════════════════════════════════
   第 62 節　字級放大（lg／xl）全站佈局體檢後的修正（客戶 2026-09-02）
   ══════════════════════════════════════════════════════════════════════
   客戶：「每個區塊檢查字級放大後的佈局和瀏覽是否有影響，有的話針對字級
   放大重新排版。」掃了 49 頁 × 1440/390 × xl（html[data-font-size] 直接
   設屬性量測），頁級橫向溢出只有一處、幾何契約破壞一處，都修在本節：

   ── ① 會員註冊三頁：.row 的 rem 制 gutter 造成的橫向溢出 ──────────────
   register／register/form／register/done 的置中單欄包在 Bootstrap
   `.row.justify-content-center` 裡，row 的負 margin 是 --bs-gutter-x
   （.75rem，**rem 制**）——字級 sm 時就有已知的 +12px 橫向溢出（回歸
   紀錄裡的「reg3+12」），xl 一放大跟著變 +18px。單欄置中的 row 根本
   沒有並排欄位，gutter 在這裡沒有任何作用，整組歸零：row 不再外擴、
   col 的 gutter 內距也一併收掉（原本就只是把內容再縮一圈）。
   ⚠ 順手把 sm 的既有 +12 一起修掉了——同一個根因，不是只修放大版。 */
.page--member .section-bleed-inner > .row.justify-content-center {
    margin-inline: 0;
}

.page--member .section-bleed-inner > .row.justify-content-center > [class*="col-"] {
    padding-inline: 0;
}

/* ── ② 報名資訊（≥1200 左圖右文）：圖與文字箱高度跟著字級等比放大 ────────
   第 58／60 節把圖高與文字箱高都釘在 `clamp(520px, 62vh, 680px)`（px 定值），
   但箱裡的內容全是 rem——xl 時內容 ×1.5、箱不動，safe center 退回貼頂後
   內容從箱底畫出去、壓過底部滿版線（量箱底看不出來，量按鈕底才會現形；
   sm 時按鈕底離線 14–52px，xl 時反超 100px+）。
   修法照客戶 2026-08-04 字級功能的原始指示「大跟特大可以根據字體區去移動
   版面」：lg ×1.25、xl ×1.5，把整塊（圖＋箱）等比放大，預算跟著字級走，
   置中、底線跟隨圖片、內容不越線三個既有契約全部保持。
   ⚠ 圖高的 SSOT 在第 58 節（.cd-enroll-media）、箱高在第 60 節
     （.cd-enroll-text），這裡兩個一起蓋——乘數要同步改，不能只改一邊
     （箱比圖高＝文字越線、圖比箱高＝置中偏移）。 */
@media (min-width: 1200px) {
    html[data-font-size="lg"] .course-detail--editorial .cd-enroll-media,
    html[data-font-size="lg"] .course-detail--editorial .cd-enroll-text {
        height: calc(clamp(520px, 62vh, 680px) * 1.25);
    }

    html[data-font-size="xl"] .course-detail--editorial .cd-enroll-media,
    html[data-font-size="xl"] .course-detail--editorial .cd-enroll-text {
        height: calc(clamp(520px, 62vh, 680px) * 1.5);
    }
}

/* ── ③ 手機首頁 Hero：xl 時「了解課程」被右下浮動列蓋住 ────────────────
   ≤767.98 的首屏（scroll 0）在 xl 時 CTA 落點 769–841，正好壓進浮動三圓鈕
   （fixed，768–820）的區域——水平重疊 49px、垂直 52px，主要 CTA 右端被
   YouTube 圓鈕蓋住。sm 時 CTA 在 553–601、完全無交集；成因是模組文字
   ×1.5 之後把 CTA 推低了 216px。浮動列本身不能動（第 57/58/66 節的讓位
   算式都以它的位置為前提）。
   修法＝把「非文字」的成本收回來，文字本身維持 1.5 倍不打折：
     ・圖片 4:3 → 16:9（269 → 202px，−67）——16:9 本來就是 .hero-media
       多個斷點在用的基準比例，不是新發明的比例；
     ・模組內距與各段 margin 從 rem 改成 sm 的 px 定值（間距是版面骨架
       不是內文，跟著字級 ×1.5 只是放大空白，收回 −30）。
   合計 CTA 底 841 → 約 744，離浮動列頂（768）留 24px。lg 介於中間，
   同一組規則一併蓋（lg 原本只是輕微接近、還沒重疊，一起處理掉）。 */
@media (max-width: 767.98px) {
    html[data-font-size="lg"] .hero-media,
    html[data-font-size="xl"] .hero-media {
        aspect-ratio: 16 / 9;
    }

    html[data-font-size="lg"] .hero-text-module,
    html[data-font-size="xl"] .hero-text-module {
        padding-block: 16px;
    }

    html[data-font-size="lg"] .hero-text-eyebrow,
    html[data-font-size="xl"] .hero-text-eyebrow,
    html[data-font-size="lg"] .hero-text-title,
    html[data-font-size="xl"] .hero-text-title {
        margin-bottom: 10px;
    }

    html[data-font-size="lg"] .hero-text-sub,
    html[data-font-size="xl"] .hero-text-sub {
        margin-bottom: 8px;
    }

    html[data-font-size="lg"] .hero-text-intro,
    html[data-font-size="xl"] .hero-text-intro {
        margin-bottom: 16px;
    }
}

/* ── ④ icon 跟文字一起放大（客戶 2026-09-02 補充指示）────────────────────
   wf_icon() 的 <svg> 不帶尺寸屬性、大小全由各處 CSS 決定，而多數寫成 px
   定值——字級切到 lg/xl 時文字放大、旁邊的圖示原地不動。這裡把**內容側、
   跟文字並排**的圖示尺寸改成等值 rem（root 會隨 data-font-size 縮放，
   sm 逐位不變）。原 px 規則不動，這裡以相同選擇器在檔尾後宣告蓋過去。
   ⚠ 刻意**不含** .wf-float 三顆浮動圓鈕與其圖示：那組是固定 56px 的獨立
     圓鈕（不是行內圖示），第 57/58/62-③ 節的讓位／避讓算式都以它的固定
     尺寸為前提，跟著字級長會把那些算式全部弄破。客戶若之後點名要，
     再連同讓位算式一起改。 */
.kz-links svg { width: 1.25rem; height: 1.25rem; }                    /* =20px */
.svc-arrow .wf-icon { width: 1.25rem; height: 1.25rem; }              /* =20px */

.site-header .site-nav > a .wf-icon,
.site-header .site-nav > .dropdown > a .wf-icon,
html.is-compact .site-header .site-nav > a .wf-icon,
html.is-compact .site-header .site-nav > .dropdown > a .wf-icon {
    width: 1.25rem;                                                    /* =20px */
    height: 1.25rem;
}

.site-header .site-nav .dropdown-toggle .wf-icon--chevron-down,
html.is-compact .site-header .site-nav .dropdown-toggle .wf-icon--chevron-down {
    width: 1rem;                                                       /* =16px */
    height: 1rem;
}

html:root .site-header .site-nav .dropdown-toggle .wf-icon--chevron-down {
    width: .875rem;                                                    /* =14px */
    height: .875rem;
}

.footer-col-toggle .wf-icon { width: .875rem; height: .875rem; }       /* =14px */
.wf-rail-btn svg { width: 1rem; height: 1rem; }                        /* =16px */
.share-pop-item svg { width: 1.375rem; height: 1.375rem; }             /* =22px */
.share-pop-badge svg { width: 1.25rem; height: 1.25rem; }              /* =20px */
.share-pop-item-icon svg { width: 1.25rem; height: 1.25rem; }          /* =20px */
.share-pop-copy svg { width: 1.0625rem; height: 1.0625rem; }           /* =17px */

/* ── ⑤ 首頁知識專區 sticky 堆疊：字級放大後「還沒看完就被下一張蓋掉」──────
   客戶：「放大字後 sticky 會讓小卡沒辦法瀏覽完整，下一頁就滾動上來了，
   需要算一下高度再滾動。」
   成因：.kz-stack-item 的 sticky top 是固定算式（header ＋ gap ＋
   head-h×i），卡片一釘住，底緣如果還在視窗外就**永遠**看不到（釘住後
   卡片不再動，繼續捲只會讓下一張蓋上來）。sm 時每張卡高 ≤ 視窗可用高、
   沒這問題；xl 內容 ×1.5 之後卡比可用高還高，下半永遠讀不到。
   修法＝照客戶說的「算高度」：home.js 量每張卡的實際高度寫進
   --kz-self-h（字級切換／resize／字型載完都重量），top 改成
       min(原算式, 100svh − 卡高 − 1.5rem)
   卡放得下 → min 取原算式，行為與 sm 完全相同；卡太高 → 釘點自動下修，
   卡會一直捲到**底緣進入視窗**才釘住，整張都看得完，代價是堆疊頭條的
   視覺在這種卡上會晚一點出現——可讀性優先（客戶點名的就是讀不完）。
   ⚠ 整組掛在 JS 量完才加的 .has-kz-measure 上：JS 沒跑就維持原本的
     top 算式，不會出現 min() 吃到未定義變數的怪值。 */
.kz-stack-list.has-kz-measure .kz-stack-item {
    top: min(
        calc(var(--header-h) + var(--kz-stack-gap) + var(--kz-head-h) * var(--kz-i, 0)),
        calc(100svh - var(--kz-self-h) - 1.5rem)
    );
}

/* ── ⑥ Hero 文字小卡：字級放大時「變寬、不變高」＋全站按鈕字級（2026-09-02
      客戶第二波：「hero文字小卡標題字級用32px、字放大時文字矩形變寬的延伸
      不要變高延伸、全站按鈕字級目前是12請改16」）───────────────────────
   標題 32px 已改在上面三個斷點的既有規則（2rem）。這裡處理後兩項：

   【變寬不變高】桌機小卡是固定寬（min(%, px)），字級 lg/xl 放大後只能往下
   換行、矩形往高長。把寬度上限跟著字級同倍放大（×1.25／×1.5），每行容字
   量與 sm 相同 → 行數不變，高度只剩字體本身的自然增高，不再有「多換行」
   的額外增高。三個斷點各自照自己的基準寬等比放大。
   ⚠ 手機（≤1023.98 堆疊版）本來就滿寬，沒有「再變寬」的空間，不套。 */
@media (min-width: 1024px) {
    html[data-font-size="lg"] .hero-text-module { width: min(67.5%, 525px); }
    html[data-font-size="xl"] .hero-text-module { width: min(81%, 630px); }
}

@media (min-width: 1024px) and (max-width: 1199.98px) {
    html[data-font-size="lg"] .hero-text-module { width: min(63.75%, 500px); }
    html[data-font-size="xl"] .hero-text-module { width: min(76.5%, 600px); }
}

@media (min-width: 1200px) {
    html[data-font-size="lg"] .hero-text-module { width: min(62.5%, 488px); }
    html[data-font-size="xl"] .hero-text-module { width: min(75%, 585px); }
}

/* 【全站按鈕 12 → 16】.btn-pill 是全站唯一按鈕語彙（含 --solid），字級目前
   吃 var(--text-meta)（12px 級，見 ≈29298）。改 1rem＝16px@root16，
   rem 使按鈕文字繼續跟 lg/xl 字級縮放（16→20→24）。
   ⚠ 不動 .btn-pill--sm／--text 兩個刻意縮小的變體與 ::after 徽章算式。 */
.btn-pill {
    font-size: 1rem;
}

/* 按鈕 16px 之後唯一爆出的個案：知識文章頁頂部的 .entry-links 長字 pill
   （a=6 那篇的連結文案最長）在 390×xl 時 385px nowrap 撐出視窗 27px。
   手機讓這組 pill 允許換行、鎖最大寬——min-height 會自己長高，不裁字。 */
@media (max-width: 767.98px) {
    .entry-links .btn-pill {
        max-width: 100%;
        white-space: normal;
        text-align: left;
    }
}

/* ── ⑦ 知識專區小卡：1:1 等高改成「文字高度主導」，收掉卡底空白 ──────────
   （客戶 2026-09-02：「小卡底下多了不必要空白」附截圖）
   上面「知識專區小卡：圖與文字區 1:1」那節用 grid-template-rows: 1fr 1fr
   讓圖文等高，註解預期「兩列都會等於文字區的 max-content」——但 fr 在
   auto 高容器裡取的是**兩軌 max-content 的最大值**：圖片還在流內時它的
   固有高度（1199 寬實測 463px）遠大於文字（254px），反過來變成圖片主導、
   文字列被填出 209px 空白，正是客戶截圖那塊。
   修法：圖片改絕對定位鋪滿自己的列（.pcard-media 只當定位框），它對
   max-content 的貢獻歸零，1fr 1fr 從此真的由文字高度決定——圖 = 文字高
   （1:1 保留）、卡高 = 2×文字高、空白消失。object-fit: cover 沿用共用
   規則裁切。
   ⚠ 跟著上一節同一個 ≥1024 範圍；≤1023.98 輪播卡維持原樣。 */
@media (min-width: 1024px) {
    .page--knowledge .kz-grid .pcard-media {
        position: relative;
    }

    .page--knowledge .kz-grid .pcard-media picture,
    .page--knowledge .kz-grid .pcard-media img {
        position: absolute;
        inset: 0;
        width: 100%;
        height: 100%;
    }
}

/* ══════════════════════════════════════════════════════════════════════
   第 63 節　首頁三處圖片版位改成固定尺寸（客戶 2026-09-07）
   ══════════════════════════════════════════════════════════════════════
   客戶：「首頁顧問案的圖片設定高和寬讓後台上其他圖片自動套用同尺寸，
   AI 分身跟電子報滑鼠 hover 圖片也設定固定圖片尺寸，知識專區右邊三個
   欄位圖片都設固定高和寬。」

   成因（beta 實測 1440，後台已換過圖的三個版位）：
     ・主打服務（顧問案）：桌機版位寫的是 height:100% + aspect-ratio:auto
       （第「主打服務改成左文右圖」節，原意是「圖跟著整塊高度伸展」），
       圖片還在流內 → 實際上是**圖片自己撐出整塊高度**。後台上的那張是
       1600×1598 的方圖，整塊因此被撐到 598×597，左邊白面板只有 335px
       的內容卻被拉成 597，中間空一大片。換一張別的比例的圖，整塊高度
       就又跳一次。
     ・知識專區三張卡：.kz-stack-media 的圖是 width:100% / height:auto，
       高度完全由圖片比例決定 —— 第一篇的圖 1760×1328（4:3）算出 478px，
       第二、三篇的圖 2336×992（2.35:1）只有 269px，三張卡並排看過去
       高矮差 209px。
     ・hover 小圖（AI 分身／電子報）：本來就已經是固定 4:3（見「hover 浮出
       的小圖」節），實測換成 2336×992 的圖版位仍是 179.7×134.8、cover
       裁切，沒有變形。本節只把「圖片不參與撐高」的寫法補齊，讓三個版位
       同一套契約，畫面不變。

   修法（三處同一招）：**版位自己有明確尺寸，圖片不參與撐高**
     ① 版位寫死比例（aspect-ratio），寬度仍由格線決定 → 「固定高和寬」
     ② 圖片改絕對定位鋪滿版位（或明確 height:100%），對 max-content 的
        貢獻歸零，後台換任何比例的圖都只是 object-fit:cover 裁切
   ⚠ 這一招與第 62 節⑦（知識專區小卡收掉卡底空白）是同一個機制，
     那邊是 fr 軌被圖片撐大，這邊是格列被圖片撐大。

   ⚠ 比例的取法不是自己新編的，是回到「後台接上之前」那組站內素材的比例：
     ・主打大圖 4:3 ＝ service-consulting 1760×1328 當年算出來的版位，
       也就是客戶當初驗收過的那個大小（1440 實測 598×448.5）。
     ・知識專區 16:10 ＝ 三張卡的圖高（1440 時 396px）最貼近左欄文字高
       （388px）的一階，圖與文字幾乎切齊，整段不會被圖撐高。 */

/* ── ① 主打服務（顧問案）大圖：固定 4:3 ─────────────────────────────
   ⚠ 只收 ≥1024：≤1023.98 收成單欄時本來就有 16:10 的固定比例（見
     「主打服務在窄幅要收成單欄」節），那一段不動。
   ⚠ height 一定要退回 auto：留著 height:100% 的話 aspect-ratio 不會生效
     （高度被當成由外部決定），比例寫了也是白寫。
   ⚠ min-height: clamp(300px,36vh,480px) 留著當地板：1024 附近 4:3 算出來
     只有 321px，比白面板的內容（335px）矮，靠它與 align-items:stretch
     把兩欄補齊，不會出現左高右矮。 */
@media (min-width: 1024px) {
    .svc-feature-media {
        height: auto;
        aspect-ratio: 4 / 3;
    }

    .svc-feature-media picture,
    .svc-feature-media img {
        position: absolute;
        inset: 0;
        width: 100%;
        height: 100%;
        object-fit: cover;
    }
}

/* ── ② hover 小圖（AI 分身／電子報）：把既有的 4:3 契約寫滿 ──────────
   版位本身的 aspect-ratio: 4 / 3 已經在前面定義過，這裡只補圖片端，
   讓它跟①同一套寫法（實測前後版位皆為 179.7×134.8，畫面不變）。
   ⚠ 只收 ≥1024：≤1023.98 這張圖被收回卡片版位裡、而且客戶 2026-08-04
     指定「照片上下填滿不要有空間」＝刻意讓它跟著整格高度伸展，
     絕對定位會把那個效果一起拿掉。 */
@media (min-width: 1024px) {
    .svc-tile-media picture,
    .svc-tile-media img {
        position: absolute;
        inset: 0;
        width: 100%;
        height: 100%;
        object-fit: cover;
    }
}

/* ── ③ 知識專區三張卡右邊的圖：固定 16:10 ───────────────────────────
   ⚠ 中間夾了一層沒有 class 的 <a>（home.php 的整張圖可點），百分比高度
     要一層一層接下去，漏掉它下面的 height:100% 就解不出來 —— 這一顆坑
     本檔 ≈32300 的註解記過一次（「圖片要跟文字同高」那輪），照抄解法。
   ⚠ 1024–1199.98 **刻意跳過**：那一段客戶 2026-09-01 指定「圖片高度改成
     跟文字同高」，已經是三張一致（實測 1024 時三張都是 461.6×457.9），
     不需要也不應該再蓋一次比例上去。 */
@media (min-width: 1200px), (max-width: 1023.98px) {
    .kz-stack-media {
        aspect-ratio: 16 / 10;
    }

    .kz-stack-media > a {
        display: block;
        height: 100%;
    }

    .kz-stack-media picture,
    .kz-stack-media img {
        height: 100%;
        object-fit: cover;
    }
}

/* ══════════════════════════════════════════════════════════════════════
   第 64 節　報名資訊：文字欄上下離線太近（客戶 2026-09-07，附截圖）
   ══════════════════════════════════════════════════════════════════════
   客戶：「報名資訊文字欄位上下離線太近。」截圖是 courses/2（五列資訊那種），
   標題幾乎貼著上緣那條線、「前往購買」也幾乎貼著下緣。

   實測（beta 六個視窗 × 六門課，量「文字內容的最上緣／最下緣」到箱子上下緣）：

       視窗          箱高    內容    上留白   下留白
       1468×868      538.2   529.1    4.5     4.5   ← 客戶截圖這個
       1440×900      558     529.1   14.4    14.5
       1366×768      520     529.1    0      -9.1   ★ 已經溢出，畫到線外
       1280×800      520     527.5    0      -7.5   ★
       1200×760      520     525.1    0      -5.1   ★

   成因不是留白沒設，是**箱高與內容高幾乎相同**：內容是文字，高度固定
   529px（五列資訊的課程；四列的只有 490px、只有堂數一列的 299px）；
   箱高是第 58／60 節釘的 `clamp(520px, 62vh, 680px)`，而 62vh 在 768–900px
   高的筆電上只算出 476–558 —— 下限 520 一旦低於內容高就直接溢出。
   ⚠ 這也解釋了為什麼「上留白 0、下留白負」：`align-content: safe center`
     在放不下時會退回貼頂、只往下溢（第 60 節就是這樣設計的），所以症狀
     是下緣破線、不是上下一起破。

   修法：**把箱高的下限抬到「最高的內容 ＋ 上下各一段呼吸」**。
   529.1 ＋ 2×35 ≈ 600 → `clamp(600px, 62vh, 680px)`。
   ⚠ 不用「加 padding」：箱子是固定 height、box-sizing 是 border-box，
     加內距只會把可用高度變得更小，直接把溢出變嚴重。
   ⚠ 不縮內容（字級／行距／列距）：第 60 節那一輪已經為了「收進圖片高度內」
     壓過一輪，再壓就會回到客戶當初嫌「太散／太擠」的地方；而且內容高度
     會隨課程資料長短變動，抬箱高才是對每一門課都成立的做法。

   ── SSOT：把這個數字收成一顆 token ──────────────────────────────
   同一個 `clamp(520px, 62vh, 680px)` 原本散在四條規則裡（第 58 節圖高、
   第 60 節箱高、第 62 節②的 lg／xl 兩條乘算），第 62 節的註解自己就寫過
   「乘數要同步改，不能只改一邊」。這一節改用 `--cd-enroll-h`，四條全部
   指向同一顆，日後只要改 token 一個地方。
   ⚠ 四條原規則一行都沒刪，這裡是靠「同特異度、寫在後面」覆寫。

   實測（beta，六個視窗 × 三門課 × 三個字級＝54 組）：
   上下留白最小 35.4／35.5px（原本 4.5、最糟 −9.1），
   `.cd-enroll-media` 與 `.cd-enroll-text` 高度 54 組全部相等（等高契約沒破）。 */
:root {
    --cd-enroll-h: clamp(600px, 62vh, 680px);
}

@media (min-width: 1200px) {
    .course-detail--editorial .cd-enroll-media,
    .course-detail--editorial .cd-enroll-text {
        height: var(--cd-enroll-h);
    }

    /* 字級放大照第 62 節②的乘數走，只是換成讀 token */
    html[data-font-size="lg"] .course-detail--editorial .cd-enroll-media,
    html[data-font-size="lg"] .course-detail--editorial .cd-enroll-text {
        height: calc(var(--cd-enroll-h) * 1.25);
    }

    html[data-font-size="xl"] .course-detail--editorial .cd-enroll-media,
    html[data-font-size="xl"] .course-detail--editorial .cd-enroll-text {
        height: calc(var(--cd-enroll-h) * 1.5);
    }
}

/* ══════════════════════════════════════════════════════════════════════
   第 65 節　報名資訊手機版兩項（客戶 2026-09-07，附截圖）
   ══════════════════════════════════════════════════════════════════════
   客戶：「手機寬度時標題離文字太遠，前往購買按鈕不要拉寬，原始大小靠左即可。」

   ── ① 標題到第一列的距離 ───────────────────────────────────────────
   實測「.cd-h2 盒底 → 第一列 .cd-enroll-row 盒頂」：

       寬度   360/390/430/600/768   900   1024   1100 │ 1200   1440
       改前          87              90    93.7    96  │  48     52
       改後          36              39    42.7    45  │  48     52

   三段加起來才是那 87：`.cd-h2` 的 margin-bottom 36 ＋ `.cd-enroll-text`
   （<1200 時是 flex column）的 row-gap 24 ＋ `.cd-enroll-cols` 的
   margin-top 27。第 60 節把 h2 收成 12／cols 收成 0 的那組緊湊值
   **寫在 `@media (min-width: 1200px)` 裡**，<1200 完全沒吃到，
   所以桌機 48、手機 87 —— 手機反而比桌機鬆一倍。

   修法：把第 60 節同一組值搬到 <1200 也套一份（不另訂數字），
   剩下的 row-gap 隨斷點自己收放，36 → 48 一路連續，跨 1200 不會跳。
   ⚠ 範圍取 ≤1199.98 而不是只有手機：768–1199 量出來是 87–96，
     跟手機同一個根因、同樣鬆，只修一半下一輪就會再被指出來。

   ── ② 「前往購買」不要滿寬，原始大小靠左 ──────────────────────────
   ⚠ 這是**推翻先前的指示**：≤767.98 的 `width: 100%` 是第 58 節那輪
     客戶自己說的「按鈕寬度可設為 100%」。以本輪為準，那條規則不刪，
     在這裡把 width 還原成 auto、並明寫靠左。
   ⚠ 只動 ≤767.98：768–1199 的按鈕本來就是自然寬度、靠右（跟桌機同一種
     收尾），客戶這次講的是「手機寬度」，那一段沒有要改。
   實測改後 360/390/430 為 147px、600 為 150.8px，靠左（距欄左緣 0），
   lg/xl 放大字時 178.2／209.5px 仍在欄寬內，各寬度橫向溢出 0。 */
@media (max-width: 1199.98px) {
    .course-detail--editorial .cd-enroll-text .cd-h2 {
        margin-bottom: .75rem;
        padding-bottom: .625rem;
    }

    .course-detail--editorial .cd-enroll-cols {
        margin-top: 0;
    }
}

@media (max-width: 767.98px) {
    .course-detail--editorial .cd-enroll-text .page-hero-actions {
        justify-content: flex-start;
    }

    .course-detail--editorial .cd-enroll-text .page-hero-actions .btn-pill {
        width: auto;
    }
}

/* ══════════════════════════════════════════════════════════════════════
   第 66 節　條文頁：段落與項目清單的節奏（客戶 2026-09-07 提供正式法務文案）
   ══════════════════════════════════════════════════════════════════════
   隱私權政策與服務聲明兩頁換成客戶的正式文件後，結構從「一條一段」變成
   「一條可能有多段，其中幾條還帶項目清單」（例如個資蒐集情形、當事人權利、
   聯絡我們）。既有的 `.page-text` 是 `margin: 0`，多個段落連放會黏在一起，
   而站上原本沒有給這種純條文頁用的清單樣式（`.article ul` 只作用在後台
   編輯器輸出的 `.article` 裡）。

   ⚠ 只補「節奏」不補「字級」：段落與清單一律照舊掛 `.page-text`，
     字級／行高／字重／字距／顏色全部沿用它，這裡一個排版數值都不重抄——
     日後 `.page-text` 調字級，這兩頁自動跟著走。
   ⚠ 用 `.legal-body > * + *` 而不是 `.page-text + .page-text`：後者是全站
     共用 class，course_detail 等頁也在用，加上相鄰選擇器會波及到沒要改的
     地方；包一層自己的殼，作用範圍就只有條文頁。
   ⚠ 清單的縮排與項目間距照抄 `.article ul` / `.article li + li` 的值
     （1.4em / .5rem），兩種清單在站上看起來才是同一套。 */
.legal-body > * + * {
    margin-top: 1rem;
}

.legal-list {
    padding-left: 1.4em;
}

.legal-list > li + li {
    margin-top: .5rem;
}

/* ⚠ 服務聲明捲動框：條與條之間的 2.5rem 是既有規則
   `.form-scroll .page-text + .page-h3`（本檔 ≈12293）給的，它假設「段落的
   下一個兄弟就是下一條的小標」。這一輪把每條的內文包進 `.legal-body` 之後，
   小標的前一個兄弟變成那層 div，**相鄰選擇器直接失配**、間距掉回 0，
   實測畫面上前言與「1. 服務內容」幾乎黏在一起。
   → 補上 div 版的同一條規則（值照抄，不另訂）。
   ⚠ 教訓同本檔「拆共用 class 漏帶走分隔線」那節：改一段標記的巢狀結構前，
     要先查有沒有規則是靠**相鄰／直屬**關係吃到樣式的，那種規則不會報錯，
     只會安靜地不生效。 */
.form-scroll .legal-body + .page-h3 {
    margin-top: 2.5rem;
}

/* ══════════════════════════════════════════════════════════════════════
   第 67 節　知識專區列表：1200–1279 改用平板那一套單欄卡（客戶 2026-09-07）
   ══════════════════════════════════════════════════════════════════════
   客戶：「寬度 1200 時知識專區小卡改跟平板一樣形式」（附 /knowledge?tag=溝通）。

   這頁的左邊有**兩條側欄**（分類 15rem ＋ 標籤 11rem，見 ≈32110 那節的說明），
   1200 時卡牆只分到約 570px，兩欄一切就是一欄 283.5px：

       寬度   卡寬    標題被 line-clamp 截掉   標籤列高
       1200   283.5        2 / 4 張            88px（擠成兩列）
       1210   288          2 / 4 張            40px
       1220   293          0 / 4 張            40px
       1280   321          0 / 4 張            40px
       1440   396          0 / 4 張            40px

   1024–1199.98 早就因為同一個原因改成單欄（≈32118 那節：「不動側欄欄寬、
   只把文章格收成單欄」），只是當時斷點停在 1199.98，1200 一過就掉回兩欄、
   而且是最擠的那一段。這一節把那個單欄形式往上延伸到 1279.98。

   ⚠ 為什麼停在 1280 而不是更遠：實測 1220 起就不再截字，1280 的 321px
     正是 ≈32118 那節註解自己拿來當「夠寬」基準的數字（「1280 是 321px，
     比桌機還寬，絕對不會再被 line-clamp 截掉」）。1440 兩欄是客戶看過的
     現況，不動。
   ⚠ 1280 是本檔**新出現**的斷點值（既有的是 1199.98 與 1467.98）。選它是
     因為 1467.98 會把 1440 也吃掉、等於改掉沒被反映的桌機構圖。
   ⚠ 只改欄數，不動側欄欄寬：那六處寫死 15rem／11rem 的公式（頁標題左偏移、
     左側灰底寬度、裝飾線 left、三處出血算式）全部依賴現在的欄寬，
     動它就要先抽 SSOT——理由與 ≈32110 那節記的完全相同。 */
@media (min-width: 1200px) and (max-width: 1279.98px) {
    .page--knowledge .kz-grid {
        grid-template-columns: minmax(0, 1fr);
    }
}

/* ══════════════════════════════════════════════════════════════════════
   第 68 節　課程 Hero 向下箭頭：補完 1200–1299 被壓扁那一段（客戶 2026-09-07）
   ══════════════════════════════════════════════════════════════════════
   客戶：「寬 1200 時箭頭高度不夠被擠壓到了，請確保每個寬度瀏覽時箭頭高度
   不會被擠壓到。」

   這是**同一個 bug 的第二段**：那支 svg 是 `viewBox="0 0 24 88"` ＋
   `preserveAspectRatio="none"`（刻意的，讓線長可以被 CSS 單獨拉伸而不改變
   線粗），CSS 給多少高度就被壓成多少。寬度固定 20px，等比高度是
   20 × 88/24 ＝ 73.3px；被寫成 16px 就等於壓到 22%，整支糊成客戶截圖那個小點。

   壓成 16px 的那條在 `768–1299.98`（≈28642）。2026-09-01 那輪已經修過
   1024–1199.98（≈32591），當時的註解自己寫著：

       「這裡只覆寫 1024–1199.98：1200–1299.98 同樣是 16px，但那是客戶
         這幾輪看過 1280 截圖、沒有提出的範圍，不主動擴大改動範圍。」

   ——客戶這次就是來提這一段的。本節把同一個修法補到 1200–1299.98，
   數值照抄（73px），不另訂。

   ⚠ ≥1300 不動：那段是 `clamp(3.5rem, 10vw, 9rem)`（≈30566），刻意拉長的
     長線，130–144px 遠大於等比的 73，不是壓縮。
   ⚠ ≤1023.98 不動：實測那個範圍**整支箭頭根本不輸出**（高度 0），
     沒有被壓縮的對象；≈18407 那條 16px 是留給它的舊規則，沒有作用。
   ⚠ 空間夠不夠是量過的，不需要把區塊加高（加高會動到客戶已驗收的方格
     構圖）：1200 時 void4 格內高 266 − 上下內距 56 − 文字塊 74 ＝ 136px，
     intro 格 213 − 內距 40 ＝ 173px（該格文字塊被負 margin 拉到 header 列、
     不佔格內空間，見 memory `onedaylisten-cx-hero-cell-map`），都放得下 73。

   實測（beta，1024–1920 每 25px 共 36 個寬度）：
   改前 1224／1249／1274／1299 四個取樣點的兩支箭頭都是 20×16（等比只有 22%）；
   改後 36 個寬度**全部達到或超過等比**，沒有任何一支超出所屬格子的下緣，
   橫向溢出皆 0。 */
@media (min-width: 1200px) and (max-width: 1299.98px) {
    .page--wall .cx-hero-arrow--down {
        height: 73px;    /* ＝ 20px × 88/24，與 ≈32591 的 1024–1199.98 同值 */
    }
}
