/* ================================================================
   Board Sea Technology — 01-tokens.css
   :root token 層 ＋ @font-face ＋ 四斷點覆寫。**全站唯一的 token 來源。**

   Dawa 原本的硬約束 ⑨ 是「單一 styles.css、單一 :root、不拆檔」，用意是防止
   多頁之間的視覺漂移。2026-08-20 依 daniel 指示改成分檔（好維護好擴充），
   但**那個保證的機制是「只有一個 token 來源」，不是「只有一個檔案」** ——
   所以本檔維持單一 :root，保證仍在。

   ⚠️ 四個斷點的 token 覆寫**必須留在本檔、緊接 :root 之後**。它們是重新宣告
      custom property 的 @media 區塊，搬到別的檔案就會踩到層疊順序。
   ⚠️ 硬約束 ① 仍然有效：:root 以外禁止字面 hex / rgb() / hsl() / 非階梯 px。
      缺 token 就停下來回報，不要就地發明一個數字。

   ── 本檔是 styles.css（4,118 行）拆檔的第 1/5 段 ──
   拆檔只切不搬：五個檔案**依序串接回去逐位元組等於原檔**，層疊結果不變。
   由 includes/_head.php 依 01→05 的順序載入，**順序不可調換**。
   來源 banner 邊界見 git 歷史（2026-08-20 拆檔 commit）。
   ================================================================ */

/* ================================================================
   Board Sea Technology 形象網站
   （原文為「site-b — BroadSea Technology 形象網站（提案Ｂ mockup）」。
    品牌字串以報價單為準寫 Board Sea；她的 DESIGN.md 硬約束 ⑧ 自己也註明
    BroadSea 是「客戶正式確認前之暫定寫法」。docs/ 與 index.html 原件裡的
    寫法暫不動，當史料保留。）
   TOKEN LAYER + MINIMAL BASE — Unit 1 of 2

   單一真相源：/Users/dawa/博洋興業有限公司/site-b/DESIGN.md
   （含檔尾「待套用的連帶修正清單」，已套用 E1／F 組，見檔尾說明）
   佐證資料：/Users/dawa/博洋興業有限公司/site-b/docs/figma-spec.md

   本檔範圍：只有三塊——① :root token（原始 → 語意別名／角色）
   ② 四斷點 media query 覆寫（覆寫 :root 變數本身，不在別處 hardcode 斷點值）
   ③ 最小 reset / base。
   不含任何元件樣式（nav / hero / button / card / footer 留給 Unit 2）。

   confidence 標記（沿用 DESIGN.md，僅作註解、非 CSS 語法一部分）：
     figma-exact > figma-panel-verified > figma-measured-from-image
     > derived > UNVERIFIED_PENDING_PNG > UNVERIFIED
   本檔絕大多數值為 figma-exact／figma-panel-verified／derived，
   個別 UNVERIFIED_PENDING_PNG／quarantined 值另行標註，不得移除限制。

   硬規則（DESIGN.md 交接契約第 4 條）：色值／間距／字級／圓角／動畫時長
   只能 var(--token)；:root 以外不得出現 hex／rgb()／hsl()／非階梯 px；
   Marcellus 禁 faux bold（font-synthesis: none，見本檔 base 區）。
   ================================================================ */

:root {

  /* ── COLOR — primitive：4 軸 × 10 階（source: DESIGN.md `color`） ── */

  /* neutral（純灰軸，figma-exact 階為 100/200/300/500/700/950，其餘 derived） */
  --color-neutral-100: #FFFFFF;
  --color-neutral-200: #F3F3F3;
  --color-neutral-300: #E8E8E8;
  --color-neutral-400: #C6C6C6;
  --color-neutral-500: #A4A4A4;
  --color-neutral-600: #767676; /* derived：最淺可用正文灰地板，對白 4.54:1，不得再淺 */
  --color-neutral-700: #616161;
  --color-neutral-800: #313131;
  --color-neutral-900: #191919;
  --color-neutral-950: #000000;

  /* brand（深藍結構軸：figma-exact 900，其餘依 R2 hue218 明度階梯 derived） */
  --color-brand-100: #EBF2FF;
  --color-brand-200: #CADDFF;
  --color-brand-300: #8FB8FF;
  --color-brand-400: #3D84FF;
  --color-brand-500: #0055E7;
  --color-brand-600: #0047C2;
  --color-brand-700: #003899;
  --color-brand-800: #00276B;
  --color-brand-900: #00173E;
  --color-brand-950: #000D24;

  /* accent（亮藍互動軸：figma-exact 200/500，其餘依 R2 hue210 明度階梯 derived） */
  --color-accent-100: #EBF5FF;
  --color-accent-200: #CAE5FF;
  --color-accent-300: #8FC7FF;
  --color-accent-400: #3D9EFF;
  --color-accent-500: #0073E7;
  --color-accent-600: #0061C2; /* 本案所有 ≤13px 藍字一律用此階：6.02:1(白) / 5.42:1(#F3F3F3) */
  --color-accent-700: #004D99;
  --color-accent-800: #00366B;
  --color-accent-900: #001F3E;
  --color-accent-950: #001224;

  /* reserved：隔離中，本輪不指派任何用途，要用須先回報（DESIGN.md color.reserved） */
  --color-reserved-dark-brown: #443F3B; /* quarantined — 暖色，混入純灰軸會破壞單一色溫，禁直接使用 */

  /* ── COLOR — semantic alias（frontend 只准引用這層，不准直接摸階數） ── */
  --surface-page: var(--color-neutral-100);
  --surface-subtle: var(--color-neutral-200);
  --surface-inverse: var(--color-brand-900);
  --border-hairline: var(--color-neutral-300);   /* 純裝飾分隔線，對白僅 1.22:1，禁承載文字/功能邊框 */
  --border-functional: var(--color-neutral-600); /* 需承載狀態的邊框，≥3:1 (WCAG 1.4.11) */
  --border-interactive: var(--color-accent-600); /* 全站微動態規格新增：與 --text-link 同值(#0061C2)、不同語意——
     兩者分離承載，日後調其一不會誤動其二，這正是 semantic_alias 層存在的理由 */
  --text-primary: var(--color-neutral-950);
  --text-body: var(--color-neutral-800);          /* 長段正文，13.0:1，非純黑降光暈 */
  --text-secondary: var(--color-neutral-700);
  --text-on-inverse: var(--color-neutral-100);
  --text-link: var(--color-accent-600);           /* ≤13px 藍字一律用此階 */
  --text-link-large: var(--color-accent-500);     /* 僅限 ≥18px 或 ≥14px/700 */
  --text-link-inverse: var(--color-accent-200);
  --focus-ring-on-light: var(--color-accent-500);
  --focus-ring-on-dark: var(--color-accent-200);
  /* 2026-08-19 header stuck-glass（D32）：70% 白玻璃疊在深底時合成亮度落在中間調
     （L 0.448–1.000），既有 on-light(accent.500)／on-dark(accent.200) 兩端在最壞情況
     下皆不達 WCAG 1.4.11 的 3:1（實測 2.16:1／1.62:1）。加深一階到 accent.700 後
     全區間 3.95–8.33:1 達標。僅適用於 .site-header.is-stuck 內部，不影響其餘元件。 */
  --focus-ring-on-glass: var(--color-accent-700);
  --text-link-on-glass: var(--color-accent-700);
  --decorative-only: var(--color-neutral-500);    /* 對白僅 2.49:1，禁用於任何文字或功能邊框 */


  /* ── TYPE — font family（source: DESIGN.md `font`，self-host，@font-face 未建，見檔尾說明） ── */
  --font-family-heading: "Marcellus", Georgia, "Times New Roman", serif;
  --font-family-body: "Albert Sans", "Helvetica Neue", Arial, sans-serif;
  --font-family-mono-ui: "Inter", "Helvetica Neue", Arial, sans-serif;

  /* ── TYPE — weight primitive ── */
  /* Marcellus 僅發行 400（禁 faux bold）；Albert Sans 400/500/700；
     Inter 僅 700（原 500 唯一用途 type.label 已整級刪除，見修正清單 E1） */
  --font-weight-regular: 400;
  --font-weight-medium: 500;
  --font-weight-bold: 700;

  /* ── TYPE — scale primitive：8 階 × 4 斷點（下方為 base=1024–1440 欄，
       tablet/mobile/wide 於各自 media block 完整覆寫，floor_guard 只覆寫 h1/h2 size）
       letter-spacing 為 em、隨字級自動縮放，四斷點同值不覆寫（source: DESIGN.md type_scale） ── */
  --type-scale-h1-size: 76px;           /* c: figma-panel-verified；本輪備而不用，見 DESIGN.md h1._unassigned */
  --type-scale-h1-line-height: 84px;
  --type-scale-h1-letter-spacing: -0.01em; /* ls_c: derived，沿用同族 h2 值 */
  --type-scale-h1-family: var(--font-family-heading);

  --type-scale-h2-size: 56px;
  --type-scale-h2-line-height: 64px;
  --type-scale-h2-letter-spacing: -0.01em;
  --type-scale-h2-family: var(--font-family-heading);

  --type-scale-h3-size: 42px;
  --type-scale-h3-line-height: 56px;
  --type-scale-h3-letter-spacing: 0.01em;
  --type-scale-h3-family: var(--font-family-heading);

  --type-scale-h4-size: 24px;
  --type-scale-h4-line-height: 36px;
  --type-scale-h4-letter-spacing: 0.01em;
  --type-scale-h4-family: var(--font-family-heading);

  --type-scale-h5-size: 18px;
  --type-scale-h5-line-height: 36px;
  --type-scale-h5-letter-spacing: 0.01em;
  --type-scale-h5-family: var(--font-family-body);

  --type-scale-h6-size: 13px;
  --type-scale-h6-line-height: 22px;
  --type-scale-h6-letter-spacing: 0.01em;
  --type-scale-h6-family: var(--font-family-body);

  /* ⚠️ 2026-08-26：四個斷點的內文字級各 +1px（daniel：「有些字好像有點小」）。
     實測參照（1440，headless 量算出值）：Stripe 16px／Cloudflare 16px／
     NVIDIA DC 15px —— **企業站是 15–16px**，本站原本的 15px 並不算小；
     真正偏小的是被歸錯類的 .stat__label（見 05-blocks.css）。
     但 +1px 實測**零新增截斷、零水平溢出**（8 頁 × 4 斷點），而且順帶把新聞
     內頁的行寬從 540 拉到 576，更靠近長文規範（Rest of World 600／
     ProPublica 754／Ars Technica 675）。
     四斷點：≥1441 16→17、1024–1440 15→16、≤1023 14→15、≤768 14→15。
     行高同步 +1~2px 維持約 1.6 倍。 */
  --type-scale-body-size: 16px;
  --type-scale-body-line-height: 26px;
  --type-scale-body-letter-spacing: 0.01em;
  --type-scale-body-family: var(--font-family-body);
  /* 原 type.body_sm（15px）已取消：15px 即本階 base 值，不獨立成級（deviations.D5 撤銷） */

  --type-scale-caption-size: 13px;
  --type-scale-caption-line-height: 22px;
  --type-scale-caption-letter-spacing: 0.01em;
  --type-scale-caption-family: var(--font-family-body);

  /* type_outside_scale：不受四斷點響應式系統管的特例（各自理由見 DESIGN.md） */
  /* 2026-08-18 QA 修正（qaA 溢出 FAIL，1024px 撐出 31px、與鄰欄墨水重疊
     19.34px）：原始 `clamp(48px,14vw,72px)` 在 viewport≥514px 就已頂到
     72px 上限，這個「何時頂滿」的門檻與「4 欄版面何時真的容得下 72px
     數字」（欄寬需求≈909px container、對應 viewport≈1149px）完全脫鉤——
     514px 遠早於 1149px，中間 1024–1149px 這段變成「4 欄擠不下、但字級
     仍鎖 72px」的空窗。改用 min(14vw, 30cqw)：14vw 那支保留原本窄螢幕
     （<514px）壓縮到 48px 的既有視覺設計不變（純改 cqw 會讓 320px 手機版
     數字從 48px 跳大到 72px，是不必要的副作用）；30cqw 那支相對 `.stat`
     自身欄寬（見下方 `.stat` 新增 container-type:inline-size）取值，
     在 4 欄擠壓的窗口正確隨欄寬縮小，且在 tablet(2欄)/mobile(1欄) 欄寬
     寬裕時自然算出遠大於 72 而被 clamp 頂掉、等同沒變化，不需要另外用
     斷點覆寫。係數 30（非精算的臨界值 32.98）刻意保留安全餘裕，抵銷
     字型渲染次像素差異。不影響 tnum：font-feature-settings 未變動。 */
  --type-scale-stat-huge-size: clamp(48px, 30cqw, 72px); /* 2026-08-19 uiux S6 reshoot C3：移除 14vw 安全網（2026-08-18 QA 修正遺留），改純 cqw。原 hybrid 在 mobile/floor_guard 單欄範圍（14vw 相對窄視口偏小）會把數字壓到遠低於 72px 上限，未達 Figma 參考圖視覺意圖；純 cqw 在全部 7 個驗收寬度實算不溢出（V4），詳見任務報告 */
  --type-scale-stat-huge-line-height: 1.167; /* 比值，非絕對 px：系統外特例未變數化 */
  --type-scale-stat-huge-letter-spacing: -0.04em;
  --type-scale-stat-huge-weight: var(--font-weight-bold);
  --type-scale-stat-huge-family: var(--font-family-mono-ui);
  --type-scale-stat-huge-font-feature-settings: "tnum"; /* In-numbers 4 欄需 tabular figures 對齊 */

  --type-scale-logo-wordmark-size: 28px;   /* 區域值，不入字階系統、不隨斷點縮放 */
  --type-scale-logo-wordmark-family: var(--font-family-heading);
  --type-scale-logo-wordmark-weight: var(--font-weight-regular);
  --type-scale-logo-wordmark-transform: uppercase;

  --type-scale-logo-subline-size: 11px;    /* 區域值，同上 */
  --type-scale-logo-subline-family: var(--font-family-body);
  --type-scale-logo-subline-weight: var(--font-weight-medium);
  --type-scale-logo-subline-letter-spacing: 0.02em; /* local 值，不受全站 1% 規則約束 */

  /* ── TYPE — role（語意層，frontend 只准引用這層，不准直接摸 type-scale 階數）
       source: DESIGN.md `type_role`；weight/max-width 已內含各角色裁決 ── */
  --type-role-hero-title-size: var(--type-scale-h2-size);
  --type-role-hero-title-line-height: var(--type-scale-h2-line-height);
  --type-role-hero-title-letter-spacing: var(--type-scale-h2-letter-spacing);
  --type-role-hero-title-family: var(--type-scale-h2-family);
  --type-role-hero-title-weight: var(--font-weight-regular);
  /* 這就是本站唯一的 HTML <h1>：使用 h2 階是 Figma 實際綁定，非降級 */

  --type-role-hero-subtitle-size: var(--type-scale-h5-size);
  --type-role-hero-subtitle-line-height: var(--type-scale-h5-line-height);
  --type-role-hero-subtitle-letter-spacing: var(--type-scale-h5-letter-spacing);
  --type-role-hero-subtitle-family: var(--type-scale-h5-family);
  --type-role-hero-subtitle-weight: var(--font-weight-bold);
  /* 限 HERO 的 1–2 行副標；多行段落一律 paragraph role */

  --type-role-section-title-size: var(--type-scale-h2-size);
  --type-role-section-title-line-height: var(--type-scale-h2-line-height);
  --type-role-section-title-letter-spacing: var(--type-scale-h2-letter-spacing);
  --type-role-section-title-family: var(--type-scale-h2-family);
  --type-role-section-title-weight: var(--font-weight-regular);

  /* ── page-title：內頁大標（2026-08-20 新增）─────────────────────────────
     內頁的 <h1>。首頁的 <h1>（HERO 標題）用的是 h2 階，這一階比它高一級。

     ⚠️ 這不是發明新字級。h1 階早就在 primitive 層定義好、四斷點齊全，
        且 DESIGN.md 的 h1._unassigned 明文寫著：「登錄為合法階、備而不用。
        **日後新增更高一層標題（例如 9 頁中某內頁的大標）時直接引用**，
        不需回頭改本檔。」——這裡就是那個「日後」。
     ⚠️ 走 role 別名而不是讓元件直接引用 --type-scale-h1-*：
        DESIGN.md「frontend 只准引用 type_role，type_scale 是為了與
        Variables 面板 1:1 可回溯而保留的 primitive 層」。
        別名的好處是四斷點的 scale 覆寫會自動跟著走，不必重寫響應式。 */
  --type-role-page-title-size: var(--type-scale-h1-size);
  --type-role-page-title-line-height: var(--type-scale-h1-line-height);
  --type-role-page-title-letter-spacing: var(--type-scale-h1-letter-spacing);
  --type-role-page-title-family: var(--type-scale-h1-family);
  --type-role-page-title-weight: var(--font-weight-regular);

  /* ── breadcrumb：麵包屑（2026-08-20 新增）────────────────────────────
     與 meta 同階（caption），但語意不同：meta 是內容的附屬資訊，
     breadcrumb 是導覽。分開命名，日後要各自調整才不會互相牽動。 */
  --type-role-breadcrumb-size: var(--type-scale-caption-size);
  --type-role-breadcrumb-line-height: var(--type-scale-caption-line-height);
  --type-role-breadcrumb-letter-spacing: var(--type-scale-caption-letter-spacing);
  --type-role-breadcrumb-family: var(--type-scale-caption-family);
  --type-role-breadcrumb-weight: var(--font-weight-regular);

  --type-role-card-title-size: var(--type-scale-h4-size);
  --type-role-card-title-line-height: var(--type-scale-h4-line-height);
  --type-role-card-title-letter-spacing: var(--type-scale-h4-letter-spacing);
  --type-role-card-title-family: var(--type-scale-h4-family);
  --type-role-card-title-weight: var(--font-weight-regular);

  --type-role-eyebrow-size: var(--type-scale-caption-size);
  --type-role-eyebrow-line-height: var(--type-scale-caption-line-height);
  --type-role-eyebrow-letter-spacing: var(--type-scale-caption-letter-spacing);
  --type-role-eyebrow-family: var(--type-scale-caption-family);
  --type-role-eyebrow-weight: var(--font-weight-bold);
  --type-role-eyebrow-transform: none; /* 2x 圖目視為句首大寫，非全大寫 */

  --type-role-paragraph-size: var(--type-scale-body-size);
  --type-role-paragraph-line-height: var(--type-scale-body-line-height);
  --type-role-paragraph-letter-spacing: var(--type-scale-body-letter-spacing);
  --type-role-paragraph-family: var(--type-scale-body-family);
  --type-role-paragraph-weight: var(--font-weight-regular);
  --type-role-paragraph-max-width: 36em; /* ≈72 字元/行，取代 deviations.D5 */

  --type-role-card-body-size: var(--type-scale-body-size);
  --type-role-card-body-line-height: var(--type-scale-body-line-height);
  --type-role-card-body-letter-spacing: var(--type-scale-body-letter-spacing);
  --type-role-card-body-family: var(--type-scale-body-family);
  --type-role-card-body-weight: var(--font-weight-regular);
  --type-role-card-body-max-width: 36em;

  --type-role-meta-size: var(--type-scale-caption-size);
  --type-role-meta-line-height: var(--type-scale-caption-line-height);
  --type-role-meta-letter-spacing: var(--type-scale-caption-letter-spacing);
  --type-role-meta-family: var(--type-scale-caption-family);
  --type-role-meta-weight: var(--font-weight-regular);

  --type-role-nav-item-size: var(--type-scale-h6-size);
  --type-role-nav-item-line-height: var(--type-scale-h6-line-height);
  --type-role-nav-item-letter-spacing: var(--type-scale-h6-letter-spacing);
  --type-role-nav-item-family: var(--type-scale-h6-family);
  /* 2026-08-18 派工單 Unit 3：用戶「nav 的 font weight:400 就好」——桌機
     header 那條 nav。此 role token 現在只剩 .primary-nav__list a 一個消費者
     （.nav-overlay__list a 已在 Unit 2 改直接引用 --type-scale-body-*，不再
     透過本 role token，見該規則旁註解），故在此改動不會波及面板導覽項
     （面板原稿量測是粗體，維持不變，兩者已拆開）。 */
  --type-role-nav-item-weight: var(--font-weight-regular);

  --type-role-button-label-size: var(--type-scale-caption-size);
  --type-role-button-label-line-height: var(--type-scale-caption-line-height);
  --type-role-button-label-letter-spacing: var(--type-scale-caption-letter-spacing);
  --type-role-button-label-family: var(--type-scale-caption-family);
  --type-role-button-label-weight: var(--font-weight-bold);

  --type-role-tag-label-size: var(--type-scale-caption-size);
  --type-role-tag-label-line-height: var(--type-scale-caption-line-height);
  --type-role-tag-label-letter-spacing: var(--type-scale-caption-letter-spacing);
  --type-role-tag-label-family: var(--type-scale-caption-family);
  --type-role-tag-label-weight: var(--font-weight-regular);

  --type-role-stat-number-size: var(--type-scale-stat-huge-size);
  --type-role-stat-number-line-height: var(--type-scale-stat-huge-line-height);
  --type-role-stat-number-letter-spacing: var(--type-scale-stat-huge-letter-spacing);
  --type-role-stat-number-family: var(--type-scale-stat-huge-family);
  --type-role-stat-number-weight: var(--type-scale-stat-huge-weight);
  --type-role-stat-number-font-feature-settings: var(--type-scale-stat-huge-font-feature-settings);
  /* giant_wordmark 角色不在此列：其 size 僅定義於 DESIGN.md `component.giant_wordmark`
     （footer 專屬裝飾字），屬 Unit 2 footer 範圍，本輪不建 token，見回報缺口清單 */


  /* ── SPACE — 12 級（4px base / 8px 優先，source: DESIGN.md `space`） ── */
  --space-0: 0;
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 24px;
  --space-6: 32px;
  --space-7: 40px;
  --space-8: 48px;
  --space-9: 60px;
  --space-10: 80px;
  --space-11: 120px; /* 間距階梯第 11 級。容器左緣語意已於 2026-08-19 移交 --inset-x（現值 space-9=60px）；本階現有 3 個消費點：--footer-inset-x／--giant-marquee-gap／--giant-marquee-fade（2026-08-20 grep 覆核修正：原註解誤植「唯一消費點為 --giant-marquee-fade」，實為三者共用非唯一） */


  /* ── LAYOUT — 版面尺寸（base=1024–1440 欄；tablet/mobile 於各自 media block 覆寫，
       wide 與 base 相同不重複宣告。source: DESIGN.md `layout`，
       responsive 觸發寬度已套用修正清單 F 組：<=960→<=1023、<=420 刪除改 mobile 流體值） ── */
  --container-max-width: 1680px;
  --header-max-width: var(--container-max-width); /* header 專屬上限，方向 E（2026-08-19）。預設綁定 container（結構上隨其變動，不會因日後改其一而漂移），≥1441 於下方 wide media block 覆寫為 1680px。唯一消費點：.site-header__bar。source: DESIGN.md layout.header_max_width */
  /* container 公式（Unit 2 建 .container 時使用，非本檔輸出的 class）：
     width: min(100% - 2 * var(--inset-x), var(--container-max-width)); margin-inline: auto; */
  --min-width: 320px;
  --inset-x: var(--space-9);
  --section-padding-block: 60px;
  --header-height: 102px; /* tablet/mobile 覆寫為 **72px**（2026-08-25 由 58px 更正——舊值算的是 logo，但 bar 裡最高的是 48px 的 menu-toggle，見該斷點註解），wide 同 base 102px 本輪未動（figma-exact 源自舊 logo 塊，見 DESIGN.md layout.header_height，無公式可套新 logo，待 uiux 複核） */
  --header-height-compact: 64px; /* 2026-08-19 B1 浮島第 3 段：72px→64px（base/wide）。
     48(menu-toggle/logo layout box) + space.2(8)×2 = 64，與精簡態 padding-block 同步由
     space.3(12)→space.2(8)（見 .site-header.is-stuck .site-header__bar 規則）。tablet/mobile
     已恰好落在此下界（見各自 media block），四斷點精簡態高度自此統一為 64px。 */
  /* 2026-08-25：扣掉 --hero-inset —— hero 上緣現在也留白（margin-block-start），
     不扣的話「header + 留白 + hero」會超出首屏，捲軸一開始就出現。

     ⚠️⚠️ 2026-09-14：`--hero-inset` 由 **×1 改成 ×2**（daniel 在 MacBook Pro 14 上
        回報「hero 的下緣跟瀏覽器下緣貼在一起」）。舊算式只扣了**上**緣那一份留白，
        所以 hero 下緣落在 `header + inset + hero = 100svh`，**剛好等於視窗底**。
        量出來的：`gapBelow = 0` 發生在**視窗高 ≤ 926px 的每一個尺寸**
        （＝ 800 的上限還沒生效的整段），實測 750／870／890／900 全是 0，
        而 982 是 56、1200 是 274 —— 也就是說**本專案自己的驗收尺寸 1440×900
        一直都是貼齊的**，只是「滿版」讀起來像刻意的，沒人去量。
        ×2 之後那一段會固定留 `--hero-inset`(24px)，與左右／上緣同值 ——
        四邊等距，卡片才真的像浮在頁面上而不是被視窗底切掉。
     ⚠️ **只改這個基準層（≥1024）。** ≤1023／≤768 兩個斷點有自己的算式，
        刻意不動：手機上 hero 滿到底是常見且刻意的版位，而且那兩格的垂直空間
        本來就緊（上限 640／560）。
     ⚠️ 800px 上限**沒有動**，所以高視窗（≥950）的表現與改版前逐像素相同。
        改的只是「上限還沒生效」那一段。
     ⚠️ hero 文字對比是以「距 hero 底緣的百分比」量的（規則 7-1），高度等比縮放
        不影響那組 scrim 停點；但**改 800 這個上限或改 inset 就要回頭重量**。 */
  --hero-height: min(800px, calc(100vh - var(--header-height) - var(--hero-inset) * 2));
  --hero-height: min(800px, calc(100svh - var(--header-height) - var(--hero-inset) * 2)); /* svh 優先，上一行為不支援 svh 的 fallback */
  /* hero 內縮（2026-08-25 下午，daniel：「hero 的輪播牆要跟瀏覽器邊界有一點留白」）。
     hero 不再滿版出血，四邊留白之後與上方 header 的白、下方 S2 的白連成一片，
     hero 變成浮在白底上的一張大媒體卡。
     ⚠️ **文字左緣不跟著內縮。** --hero-text-padding 的左右值改成
        `calc(--container-gutter-inline - --hero-inset)`，兩者相加剛好等於
        --container-gutter-inline —— 也就是 hero 標題與底下所有區塊標題**仍然
        共用同一條左緣**（DESIGN.md 驗收點 5「各區標題 left 相等，容差 0」）。
        直接寫死一個內距會讓 hero 文字比其他區塊多縮 --hero-inset 那麼多，
        而且只在某些寬度看得出來。
     ⚠️ --container-gutter-inline 內含 `(100% - 1680px)/2`，那個 100% 解析的是
        **.hero 的包含塊（<main>，滿視口寬）**，不是 hero 自己的寬度 —— 所以
        內縮之後這條公式不必改，減法直接成立。 */
  --hero-inset: var(--space-5);   /* 24px；≤768 降為 16px，見該斷點覆寫 */
  --hero-text-padding: 120px calc(var(--container-gutter-inline) - var(--hero-inset)) 80px;
  /* F1（docs/spec-hero-carousel.md §2.10）：40px 原定義是「文字區↔控制區」
     間距（此 token 名稱本義），非「標題↔副標」——後者現改用新 token
     --hero-title-gap（見下方 Unit 2 hero carousel token 區塊）。歸位後
     本 token 消費點是 .hero__row 的 gap（.hero__slides 與 .hero__controls
     之間），不再是 .hero__content 的 gap。 */
  --hero-text-gap: var(--space-7);

  /* ── hero 捲動展開 scroll expand（2026-08-25，daniel 指定加入）────────
     技法來源：react-bits 的 ScrollExpand（MIT ＋ Commons Clause，
     授權檔在 `assets/vendor/react-bits/`）。移植成 vanilla JS，
     驅動端是 `assets/js/hero-expand.js`。

     ⚠️ **這一組全部是 unitless 數字（半徑除外），是為了 JS 讀。**
        JS 用 getComputedStyle().getPropertyValue() 取值再 parseFloat，
        沿用 --rail-drag-threshold／--hero-autoplay-interval 的既有先例
        （行為參數走 token，不另存 JS 常數，兩邊才不會漂開）。
        帶單位的話 JS 每一顆都要自己剝，而且 track 高度那條 calc 也用不上。

     ⚠️ **改任何一顆都要回頭量兩件事**：① p=0 時標題還放不放得進起始框
        （1440 實測 h1 墨水 570px，而 42% 的 demo 預設只有 578px —— 差 8px，
        等於塞不下，這就是本站起始框拉到 46%＋標題另外縮的原因）；
        ② p=0 時白字對合成後背景的對比（hero 底圖是**亮的**，196–202，
        規則 7-1 那組實測是針對 p=1 的版位做的，p=0 標題在正中央、
        既有 scrim 在那個高度只剩 α≈0.35，所以另外加了 --hero-expand-scrim）。 */
  --hero-expand-start-w: 0.46;      /* 起始框寬 ÷ hero 寬。demo 原值 0.42。
     ⚠️ 2026-08-26 更正：本站拉大到 0.46 原本的理由是「標題要放得進框裡」
        （1440 下 0.42 ＝ 578px，而 h1 墨水 570px 加內距放不下）。
        **那個理由在改成「從大到小」之後已經不成立** —— 標題現在是**刻意**
        超出框的。0.46 保留不動，因為它現在是**倍率的基準**：
        --hero-expand-title-overflow 算的就是「相對這個框寬多出多少」，
        改它會連帶改掉每個斷點的起始字級。 */
  --hero-expand-start-h: 0.62;      /* 起始框高 ÷ hero 高。demo 原值 0.58。 */
  --hero-expand-start-radius: var(--space-5); /* 24px。終點是 --radius-alias-media
     （16px，＝ hero 浮卡自己的圓角，規則 7-2），JS 在兩者之間內插。
     ⚠️ 終點**不是 0** —— demo 展到滿版直角，本站 2026-08-25 裁示停在浮卡。 */
  --hero-expand-zoom: 1.35;         /* p=0 時底圖的反向放大量，展開時縮回 1。
     ⚠️ 這一顆掛在 .hero__media（整個 slide 疊層）**不是 .hero__bg** ——
        後者身上是 Ken Burns 的 animation，同一個元素兩個 transform 會打架。
        父子分層之後兩者自然相乘，Ken Burns 一行都不必改。 */
  --hero-expand-distance: 0.8;      /* 展開要捲幾個 hero 高度才走完。demo 原值 1.2。
     ⚠️ 這一顆直接決定首頁變多高：track = hero 高 × (1 + distance + hold)。
        hero 高上限 800px，所以 0.8 + 0.2 ＝ track 1600px，S2 被往下推約 800px。
        demo 的 1.2 + 0.35 會推 1240px（約 1.5 個螢幕），首頁吃不消。 */
  --hero-expand-hold: 0.5;          /* 走完之後再黏住幾個 hero 高度才放開。
     ⚠️ 2026-08-25 傍晚由 0.2 調到 0.5（daniel：「捲太快 hero 會一下就跑到
        上面，應該要停一下」）。0.2 ＝ 155px，捲快一點根本感覺不到「展開完成」
        這個節點，畫面直接接到 S2。0.5 ＝ 387px，展完之後有一段是**完整的
        hero 停在那裡**，跟一般首頁一樣可以讀。
     ⚠️ 這一顆同時決定 header 什麼時候變精簡態（哨兵位移吃
        distance + hold，見 03-header-hero.css），改它要一起看那條。 */
  --hero-expand-smoothing: 0.1;     /* 指數平滑係數（秒）。0 ＝ 直接跟著捲動不平滑。 */
  /* ── p=0 時標題的縮放（2026-08-26 由「從小到大」改成「從大到小」）──────
     daniel：「讓文字變大，大概大到一開始超出邊框一點點…捲動時文字再慢慢縮到左下角。
     意思就是現在是從小到大，改成從大到小」。

     ⚠️ **舊值是一顆固定的 0.62，現在改成由 JS 算。** 固定值在這個方向行不通 ——
        「超出框一點點」是**相對關係**（墨水寬 vs 起始框寬），而那兩個量在各斷點
        的比例差很多：起始框是 hero 寬的 46%（1920 = 854px、1024 = 442px），
        而 h1 的字級只有兩階（1920 是 64px、1440 與 1024 都是 56px）。
        同一顆固定倍率實測會是「1920 根本沒超出、1024 超出 61%」。
     ⚠️ 走 transform 不走 font-size 這件事沒變 —— font-size 會重排、會改折行，
        每一幀重排等於動畫報廢。

     公式（見 assets/js/hero-expand.js 的 measure()）：
       scale = clamp(起始框寬 × (1 + overflow) ÷ 墨水寬, min, max) */
  --hero-expand-title-overflow: 0.12;  /* 墨水寬要比起始框寬多出多少（左右各一半）。 */
  --hero-expand-title-scale-min: 1.1;  /* 下限。**保證「從大到小」成立** ——
     窄螢幕上「框寬 × 1.12」可能還小於墨水寬（1024 算出來是 0.87），
     沒有這條下限的話那一格會變回「從小到大」，與裁示相反。 */
  --hero-expand-title-scale-max: 1.8;  /* 上限，免得寬螢幕上大到誇張。 */
  /* 框外那一段文字的顏色（daniel：「超出去的地方要變黑色」）。
     ⚠️ 走 token 不寫死 hex（硬約束①）。--text-primary 是站上的近黑。 */
  --hero-expand-title-echo-color: var(--text-primary);
  /* 標題與 echo 的**上下出血**（2026-08-26）。
     ⚠️ 存在的唯一理由是 mask-image 會把內容切在**邊界盒**上，而字的下伸部
        （g／p／y）本來就伸出行框外 —— 症狀是「g 和 p 的尾巴被齊平切掉」，
        而且**只有在遮罩生效那一段才會發生**，捲到底就恢復正常，極容易誤判成
        字型問題。實測 `mask-clip: no-clip` 在 Chrome **無效**（`CSS.supports`
        回 true，畫面照切），所以只能把盒子撐高。
     ⚠️ 撐高的作法是「padding 撐開 ＋ 負 margin/位移拉回」，**兩個元素要用
        同一顆 token**：它們的邊界盒必須完全重合，否則 transform-origin
        （兩邊都是 left top）會差這麼多，深色那一半就會上下錯位。
     0.35em ＝ 56px 字級下 19.6px，遠大於這套襯線字的下伸部。 */
  --hero-expand-title-bleed: 0.35em;
  --hero-expand-scrim: 0.5;         /* p=0 時額外壓在底圖上的深色不透明度，展開時歸零。
     ⚠️ 存在的唯一理由是**對比**：規則 7-1 那組 scrim 停點是照 p=1 的版位調的
        （標題在距底緣 29–38%），p=0 標題在正中央，既有 scrim 在那個高度
        只剩 α≈0.35，而底圖是亮的。實測數字見 CLAUDE.md 規則 7-4。
     ⚠️ 它是**額外疊上去的一層**，不是去改既有 scrim ——
        既有那組是量過、核定過的，p=1 時必須原封不動回到它。 */
  /* 副標／CTA／進度線段的淡入區間。
     ⚠️ **兩顆的單位是「框寬佔 hero 寬的比例」，不是進度 p。** 這是實作時
        改過來的：一開始寫的是 p（0.5→0.92），結果 p=0.5 時框只長到 80%，
        而文字距 hero 左緣只有 --hero-text-padding（1440 實測 60px）——
        內容在**框外的白底上**淡入，看起來像破圖。截圖才看得出來。
        改吃框寬之後這件事自己成立：框寬 0.93 代表左右各內縮 3.5%（1440 ＝
        48px），已經蓋過那 60px 的內距，內容一定落在框內。
     ⚠️ 所以**改 --hero-text-padding 或 --hero-expand-start-w 之後要回頭
        重算這兩顆**：條件是「(1 − 本值) / 2 × hero 寬 < 文字左內距」。 */
  --hero-expand-content-in-w: 0.93;
  --hero-expand-content-full-w: 0.995;
  /* 2026-08-18 派工單 Unit 5：修 1024px 水平溢出（hero 輪播支順手發現，已用
     git diff 排除非其造成，屬既有缺陷）。原固定 583px 577px：base(1024–1440)
     container = min(100vw − 2×120, 1200)，遠低於兩欄+gap 的固定需求
     583+577+40=1200px，導致溢出——推算範圍不只 1024 這一點，整個 1024–1440
     皆溢出（如 1300px 仍溢出約 140px），只有 ≥1441（container 封頂 1200，與
     需求 1200 相等）才不溢出。改 583fr 577fr：≥1441 時可用寬度剛好等於
     1160(=583+577)，等比分配後與原字面值逐 px 相同（wide 視覺零變化，非
     僅「相近」）；1024–1440 隨可用寬度等比縮窄，不再溢出。.split > * 已有
     min-width:0（見 .split 規則），fr track 的 auto 最小值不受內容撐開，
     不需疊加 minmax(0, …fr)。tablet(≤1023) 既有 --split-columns:1fr 單欄
     覆寫不受影響。

     2026-08-18 追加（同日，S4 限制解除後）：--news-columns 有同型 bug
     （360px+800px+40=1200，同一公式），原本因「不得動 S4」未修；限制解除
     後用同一招修正（360fr 800fr），理由與驗證方式同上，不重複展開。
     .news > * 已有 min-width:0（見 .news 規則），同 .split 的安全網。

     2026-08-20 追記（premise 後續變化，結論未重新驗證）：上方「≥1441
     container 封頂 1200」的前提已隨 --container-max-width 後改為 1680
     而變化——封頂點現落在視口 ≥1800（=1680+2×60）附近，≥1441 時
     container 尚未封頂（例如 1441 下實際約 1321px，非 1200px）。這是
     否代表本段「逐 px 相同」的結論在 1441–1799 區間不再成立，本次
     註解修訂範圍內未實測驗證，僅忠實記錄前提已變動，留待另行確認。 */
  --split-columns: 583fr 577fr; /* S2/S3 圖文雙欄；欄間 gap 為 space-7(40px)，非獨立 token */
  --split-media-ratio: 3 / 2;
  --news-columns: 360fr 800fr; /* News 標題/清單雙欄；欄間 gap 亦為 space-7(40px) */
  --news-row-height: 168px;
  --card-row-scroll-width: 380px; /* Services 捲動卡片寬，tablet 320px／mobile 280px */
  --card-row-scroll-gap: var(--space-5);
  /* /services/ 生命週期軸上的圖，**直向時**的寬度上限（2026-08-24）。
     ≥1200 是五欄橫排，圖跟著欄寬走（見 pages/inner.css 的 .svc-axis__media，
     該斷點解除上限）；1199 以下轉直向、每格是滿寬的，1:1 的圖會變成
     一格 700px 高 —— 五格就多出 3500px 的捲動。這個上限就是在擋那個。
     獨立字面 px token、不套 --space-* 階梯（280 屬本元件專屬值），
     比照既有 --news-thumb-width / --card-row-scroll-width 的 LAYOUT 區慣例。

     ⚠️ 280 不是 200。第一版取 200，結果 375 的內容欄有 276px 寬，
        圖比它下面的文字還窄 32px，看起來像沒對齊。280 讓最窄的斷點
        剛好填滿內容欄，同時 767 也不會長成一格 700px 高的方形。
     ⚠️ 這個值同時是 768–1199 那個「圖左文右」grid 的第一欄寬度，改它兩邊一起動。 */
  --svc-axis-media-max: 280px;

  /* 內頁 hero 的選填主圖寬度上限（2026-08-24）。目前只有五支服務內頁在用。
     ⚠️ 上限而不是靠比例壓高度：五張素材本來就是正方形，裁成 3:2 會把機櫃那張
        的上下切掉，而且與卡片／生命週期軸上看到的同一張圖構圖不一致。
        1:1 ＋ 380px 上限 → 圖高 380px，與左欄（階段標籤＋大標＋lead，約 250px）
        量體相當；不設上限的話桌機一欄有 577px，hero 會變成一堵牆。
     獨立字面 px token、不套 --space-* 階梯，比照 --svc-axis-media-max 的慣例。 */
  --page-hero-media-max: 380px;
  /* 新聞內頁的首圖寬度上限（2026-08-26，daniel：「圖片太大張了」）。
     ⚠️ **修的是寬度不是比例。** `_figure.php` 的比例白名單只有 3x2 與 1x1
        （Dawa 的 --media-ratio 註解：「複用既有值不另立數字」），所以
        news-detail.php 原本傳的 `ratio => '16x9'` **是無效值、會靜默退回 3x2**
        —— 那正是它長成 1305×870 的原因（1440 下容器 1305，3:2 就是 870 高，
        比視窗還高 ≈ 一整個螢幕）。加 16:9 到白名單等於在設計系統裡多開一個
        比例，成本比限寬高得多。
     ⚠️ 880px 是對著閱讀欄挑的：內文受 --type-role-paragraph-max-width（36em
        ≈ 610px）限制，圖 880 ＝ 文字欄的 1.44 倍，讀起來仍是「文章首圖」
        而不是與內文同寬的插圖，但 3:2 之後只有 587px 高，不再佔滿一屏。
     ⚠️ 獨立字面 px token、不套 --space-* 階梯，比照 --page-hero-media-max。 */
  --article-media-max: 520px;   /* ⚠️ 2026-08-26 二修：880 → 460，三修：460 → 520
     （daniel：「搞不好圖會再更小」）。880 是「首圖獨佔一整列」時代的值；
     改成左文右圖之後圖是**旁欄配圖**不是首圖。
     三修把 460 拉到 520（≈ 文字欄 540px 的 96%，3:2 之後 347px 高）：實測的
     三個新聞站圖都比內文**寬** 1.5–1.6 倍，460 那一版圖只有文字欄的 85%，
     一列裡圖比文字明顯小，讀起來像縮圖。並排版沒辦法真的破格到 1.5 倍
     （那會把文字擠掉），但至少不該小於文字欄。 */
  --article-dropcap-size: 4.6em;  /* 首字放大（2026-08-26，daniel 提的）。
     em 而不是 px —— 它是相對內文字級的，內文改字級首字要跟著改。
     4.6em 在 15px 內文下**目視剛好跨滿三行**（實測 4.6em 的 B 上緣齊第一行的
     cap height、下緣落在第三行 baseline）。3.6em 只有約兩行，讀起來像
     「這個字不小心變大了」而不是刻意的排版。≤768 縮到 3.5em，見該斷點覆寫。 */
  --article-wrap-max: calc(
    var(--article-text-measure) + var(--space-7) + var(--article-media-max)
  ); /* 文章欄的總寬（文字 ＋ 欄間 ＋ 圖）。**推導出來的,不要寫死** ——
     改文字欄或圖欄的寬度時它自己跟上。
     ⚠️ 2026-08-26 三修（daniel：「右邊空白好多 你有參考什麼新聞網站嗎」）。
        我實測了三個真實文章頁（1440 寬）：
          Rest of World  內文 600px／61 字元一行／左右留白 420·420（置中）
          ProPublica     內文 754px／79 字元一行／左右留白 343·343（置中）
          Ars Technica   內文 675px／76 字元一行／208·557（右邊是側欄）
        結論三條：① 內文 600–754px、61–79 字元一行（本站 540px／約 72 字元，
        字元數在範圍內，只是字級小所以物理寬度窄）；② **留白左右均分**，
        沒有人把窄文字欄靠左、右邊留一大片；③ 圖比內文寬 1.5–1.6 倍
        （圖是破格變寬，不是變小）。
        ⚠️ **留白總量其實一樣多** —— ProPublica 是 686px／1440 ＝ 48%，
           本站置中後也是這個量級。差別只在**它是左右均分的**。 */
  /* 新聞內頁的內文字級 —— **比全站內文再大一階**（2026-08-26，daniel 選了
     「新聞內頁另拉到 17px」）。
     ⚠️ **只給新聞內頁,不影響其他頁面。** 實測長文規範是 18–20px
        （Rest of World 20／ProPublica 20／Ars Technica 18），而企業站是
        15–16px（Stripe／Cloudflare／NVIDIA）。本站兩種內容都有，所以分兩階。
     ⚠️ 走 calc 從 --type-scale-body-size 推導，**不寫死 17px** ——
        全站內文四個斷點各有值，寫死的話另外三個斷點會漂掉。
        現值：≥1441 18px、1024–1440 17px、≤1023 16px、≤768 16px。 */
  --article-body-size: calc(var(--type-scale-body-size) + 1px);
  --article-body-line-height: calc(var(--type-scale-body-line-height) + 2px);
  /* ⚠️ 行寬跟著新聞內頁的字級走,不是跟著全站內文。
     原本是 `var(--type-role-paragraph-max-width)`（36em），而 em 是對
     **元素自己的字級**解析的 —— 欄容器是全站內文字級、段落是新聞內頁字級，
     兩者不同，36em 會算出兩個不同的數字，欄裡就又會出現一段空白
     （那正是 2026-08-26 二修在修的「圖跟文的距離不一致」）。
     改成明算 36 個字級寬，兩邊必定一致。 */
  --article-text-measure: calc(36 * var(--article-body-size)); /* 文字欄的固定寬度。
     ⚠️ **這是修「圖文距離不一致」的關鍵。** 段落本來就被 36em 鎖住，但欄寬原本
        是 `fr`、會跟著視窗長大 —— 1830 下文字欄 886px 而墨水只有 610px，
        多出來的 **276px 全落在文字與圖之間**（daniel：「中間空隙太大」）。
        第二列因為文字靠右，多餘寬度落在畫面外，所以看起來正常 —— **同一個
        版面在兩列給出兩種間距**，而且只有寬視窗才看得出來。
        把文字欄釘在墨水寬之後，欄間距就永遠等於 column-gap。 */

  /* 步驟清單區塊的配圖寬度（2026-08-24，daniel：「有點大張，應該要再縮小一半左右」）。
     ⚠️ 前一版是 --split-columns（583fr 577fr），圖在 1440 量到 **629px**；
        縮成 320px ≈ 對半。清單欄改吃 minmax(0, 1fr)，所以髮絲線變寬、
        圖靠齊容器右緣 —— 這比「把圖縮小卻仍留在原欄位」好：後者會在
        文字結束（.prose 上限 540px）與圖之間留下一段 340px 的空白。
     ⚠️ 只用在 ≥1024 的兩欄；≤1023 是單欄，圖佔整欄寬（比照 .split 的
        「圖先文後」既定行為），不另外壓寬 —— 375 的欄寬本來就只有 327px。
     獨立字面 px token、不套 --space-* 階梯，比照 --page-hero-media-max 的慣例。 */
  --step-media-width: 320px;

  /* 重音區塊（.section--feature）的深色遮罩（2026-08-24）。
     ⚠️ 色相刻意就是 --surface-inverse 的 #00173E（brand-900）：遮罩濃到 0.86
        之後，合成結果與那顆實色只差一點點材質 —— 也就是站上既有的
        on-inverse 文字 token（--text-secondary-on-inverse 11.75:1、
        --text-eyebrow-on-inverse 13.6:1，都是對 #00173E 量的）**仍然成立**。
        遮罩再淡下去就得重量每一格對比，那不是調一個數字的事。
     ⚠️ 不透明度是可調的**唯一**旋鈕：模糊已經烘進素材檔了（見
        data/service-details.php 的說明）。 */
  --section-feature-scrim: rgba(0, 23, 62, 0.86);
  --rail-drag-threshold: var(--space-2); /* 8px。S5 卡片列滑鼠拖曳捲動門檻（2026-08-19
     uiux drag-to-scroll 規格 §改什麼 A）：4px grid 上緣對齊主流 touch-slop 8–10px，取
     上緣因整卡為 stretched-link（.card__cta::after 撐滿整卡），誤觸成本是整頁跳轉。
     四斷點單一值，無 media query 覆寫。唯一消費點：site.js S5 rail IIFE（經
     getComputedStyle 讀取，沿用 --hero-autoplay-interval 既有先例，非另存 JS 常數）。 */
  /* D39（docs/spec-s5-carousel-2026-08-19.md §2 缺陷 1/2）：.card-rail
     右側出血用 padding 錨定，取代主規格 ④「邊界」列出、但兩個都已被
     實測推翻的候選手法（margin-right:calc(50%-50vw) 因 scrollbar-gutter:
     stable 恆定溢出 7.5px；負 margin 在 1920 差 232.5px 到不了視口邊緣）。
     --container-gutter-inline 是 .container 公式
     width:min(100% - 2*inset, cap) 的代數補數，全程零 vw、零負水平
     margin，四斷點自動成立；唯一前提是消費端（.card-rail）與 .container
     共用同一個 containing block，即 .card-rail 必須是 .section 直接
     子元素（不再收在 .container 內），見下方 .card-rail 規則與 HTML。
     --rail-trailing-inline 取代主規格 V10（該驗收點字面實作需要隨視口
     連續變動的 padding，1440 下 925px、1920 下 1165px，技術上無法
     token 化也視覺上是壞設計），改判「末卡右緣距視口右緣 = --inset-x」。
     2026-08-20 追記：--rail-trailing-inline 現已改指向
     --container-gutter-inline（見下方 token 定義，非本段原述的 --inset-x
     直接值）——兩者僅在容器未封頂（視口寬度未達 --container-max-width
     上限）時數值相等；容器封頂後 --container-gutter-inline 依 max()
     公式變大以貼齊容器右緣，「末卡右緣距視口右緣 = --inset-x」僅在未
     封頂視口下成立，非全斷點恆等，上段原意保留供沿革參考。 */
  --container-gutter-inline: max(var(--inset-x), (100% - var(--container-max-width)) / 2);
  --rail-trailing-inline: var(--container-gutter-inline);
  --card-row-grid-columns: 4; /* In-numbers 4 欄，tablet 2 欄／mobile 1 欄 */
  --card-row-grid-gap: var(--space-3);
  --footer-inset-x: var(--space-11);
  --footer-inset-block: var(--space-10);
  --footer-columns: minmax(320px, 1fr) auto auto; /* 2026-08-18 修正 1024px 6px 溢出：原 120px/190px 固定欄寬改 auto（content-based track，天然貼合最長字串「Observability & Monitoring」不換行，且比原固定值窄，釋出的寬度剛好消除短缺，見 agent 回報計算）。tablet/mobile 行為見 rwd_behavior，未給literal覆寫值，Unit 2 建 footer 時實作 */
  --footer-columns-gap: var(--space-10);

  /* z-index（source: DESIGN.md layout.z_index，全斷點恆定） */
  --z-header: 100;
  --z-skip-link: 1000;
  --z-hero-scrim: 1;
  /* 2026-09-14 移除 --z-hero-3d（hero 3D logo 的整套實作已刪除，
     理由見 `data/hero.php` 檔頭）。--z-hero-scrim 與 --z-hero-text 不受影響。 */
  --z-hero-text: 2;


  /* ── RADIUS — primitive：4 階 × 4 斷點（下方為 base 欄，
       tablet/mobile/wide 於各自 media block 完整覆寫，非單調不可靠繼承）
       source: DESIGN.md `radius`，figma-panel-verified ── */
  --radius-small: 12px;
  --radius-medium: 16px;
  --radius-large: 24px;
  --radius-max: 999px;  /* 四斷點恆為 999，不覆寫 */
  --radius-none: 0;     /* 恆為 0，不覆寫 */

  /* ── RADIUS — alias（frontend 只准引用這層，不准直接摸階名） ── */
  --radius-alias-control: var(--radius-max);    /* 所有控件：按鈕/chevron/tag/carousel */
  --radius-alias-media: var(--radius-medium);   /* 短邊 ≥133px 的攝影媒體 */
  --radius-alias-media-sm: var(--radius-small); /* 短邊 <133px 的攝影媒體（如 news 縮圖） */
  --radius-alias-panel: var(--radius-large);    /* 浮層／面板：mobile menu 展開面板 */
  --radius-alias-flush: var(--radius-none);     /* full-bleed 區帶、hairline、頁面容器 */


  /* ── ELEVATION — Figma 變數集零陰影 → hairline 語言（source: DESIGN.md `elevation`） ── */
  --elevation-level0: none;
  --elevation-level1: 0 0 0 1px var(--border-hairline);
  --elevation-level2: var(--elevation-level0); /* 已依修正清單 D-g 收斂：2x 圖目視卡片/News 灰底上無可見投影 */
  /* level3（D34，2026-08-19 header stuck-glass）：解除 UNVERIFIED_PENDING_PNG 佔位。
     全站第一個 box-shadow 宣告，授權來源為用戶 2026-08-19 直接指示「帶一點自然的陰影」。
     兩層：接觸陰影（0 1px 2px，α.06，邊緣定義）+ 投射陰影（0 10px 24px -8px，α.14，
     負 spread 使可見範圍收在下方約 14px）。色相 rgb(0,23,62) = color.brand.900 RGB
     三元組，非純黑——淺色/玻璃表面上純黑陰影讀成髒灰，跟隨品牌深色軸才是同一條色溫紀律。
     blast radius：本輪唯一消費點 .site-header__bar::before（2026-08-19 B1 第 2 段已將此選擇器
     由 .site-header::before 搬遷至此），不得套用 nav-overlay panel 或卡片。 */
  --elevation-level3: 0 1px 2px rgba(0, 23, 62, 0.06), 0 10px 24px -8px rgba(0, 23, 62, 0.14);
  --elevation-text-shadow-hero: 0 2px 2px rgba(0, 0, 0, 0.5); /* 原稿硬寫值，不計入對比達標 */
  --elevation-backdrop-blur-header: blur(20px); /* A2 第 4 段（2026-08-19，D40）：10px→20px，
     唯一消費點為下方 --header-stuck-backdrop（已 grep 確認全檔僅此一處消費），覆寫原
     figma-exact 來源值 10px（Menu backdrop-blur-[10px]），授權來源見 DESIGN.md D40 */


  /* ── MOTION（source: DESIGN.md `motion`，Figma motion context 未取得，皆為 derived） ── */
  --motion-easing-standard: cubic-bezier(0.4, 0, 0.2, 1);
  --motion-easing-exit: cubic-bezier(0.4, 0, 1, 1);
  --motion-easing-carousel: cubic-bezier(0.22, 0.61, 0.36, 1);
  --motion-easing-linear: linear; /* 全站微動態規格 §1.3：僅 footer marquee 循環位移用此曲線 */
  --motion-easing-glass: var(--motion-easing-standard); /* D45（2026-08-19）：具名別名，
     值＝--motion-easing-standard，cubic-bezier(0.4, 0, 0.2, 1)。本 token 曾短暫定義為
     cubic-bezier(0.34, 1.56, 0.64, 1)（overshoot 實際 9.78%，非原估 8%），用戶實測回報
     「彈過頭，看起來很像故障」後撤回。機械根因：反向動畫（離開 stuck）時
     padding-block-start 與 border-radius 的值域下限為 0，overshoot 目標為負值，被 CSS
     clamp 到 0——280ms 動畫中有 176.6ms（63%）完全凍結，同時 min-height／logo scale
     仍在越界回彈，兩者不同步讀成故障。永久禁令：禁止再將本 token 定義為任何 y 控制點
     超出 [0,1] 的曲線（含 overshoot／back-ease 系）。保留獨立 token（而非直接引用
     --motion-easing-standard）是為了 header 精簡態曲線可單獨調整，不必同動 5 條消費
     規則。 */
  --motion-duration-hover: 160ms;
  --motion-duration-press: 120ms;
  --motion-duration-menu: 280ms;
  --motion-duration-slide: 240ms; /* 補落地：DESIGN.md L584 已定義 motion.dur_slide，此前 :root 缺漏；底線劃出／方向性形變用 */
  --motion-duration-draw: 720ms; /* eyebrow 前導線的**捲動進場**專用（2026-08-25 二修，
     daniel：「速度太快了，導致根本看不出來」）。= 3 × --motion-duration-slide(240ms)，
     維持落在動效階梯整數倍（同 --motion-duration-count 的作法）。
     ⚠️ **不可以直接把 --motion-duration-slide 調慢來解決**：那顆有 3 個消費端，
        全部是 hover 觸發的底線（nav 底線、.link-list 的 wipe、服務卡的 wipe）。
        hover 回饋要跟手，240ms 在那三處是對的；慢下來會變成滑過去線還在爬。
     ⚠️ 兩者的差別是「觸發方式」不是「手勢」：hover 是使用者主動觸發、眼睛已經
        盯著那一格；捲動進場是被動發生的，眼睛還在別處，太快就等於沒發生。
        240ms ＋ standard 曲線實測 **122ms 就畫到 78%**（曲線前段陡），
        在 40px 的線上看起來就是「本來就在那」。 */
  --motion-duration-carousel: 480ms;
  --motion-duration-kenburns: 9000ms; /* D45（2026-08-19 hero motion pass）：Ken Burns 一輪時長，
     超出既有動效階梯(120/160/240/280/400/480/2000)，見 spec-hero-motion-2026-08-19 §2.11.2
     ——刻意 > autoplay 週期(6500ms)，Ken Burns 走 72.2% 就被換掉，讀成「持續的」而非
     「有始有終的」。reduce 下於下方 reduce block 歸零為 0.01ms。 */
  --motion-duration-enter: 400ms;
  --motion-duration-count: 2000ms; /* D29 減速（2026-08-19，原 1200ms）。
     = 5 × --motion-duration-enter(400ms)，維持落在動效階梯整數倍。
     總時長 = 3×--motion-stagger(180) + 400 + 2000 = 2580ms < 5000ms
     → WCAG 2.2.2 免除維持，不需暫停鍵。
     ⚠️ 硬上限 4420ms（總時長 5000ms）；工程上限 4000ms（留 420ms 餘裕）。
     越過即必須加暫停控制項，見 docs/spec-s6-stat-motion.md §2-A。 */
  --motion-stagger: 60ms;
  --motion-transform-enter: translateY(12px);
  --motion-press-offset: 1px; /* 全站微動態規格 §1.4：active 按下位移，統一 token 化；prefers-reduced-motion 下於安全網歸零為 0 */


  /* ── BORDER / FOCUS RING — 全站恆定值，原散落於每個元件的 5 態定義中，
       在此集中為 token（值未變，僅去重複；source: DESIGN.md 各 component.*.focus 一致寫法
       與「極端對比規則」第 5 條「:focus-visible 一律 3px ring + 2px offset」） ── */
  --border-width-hairline: 1px;
  --focus-ring-width: 3px;
  --focus-ring-offset: 2px;

  /* ── COMPONENT TOKEN — added by Unit 2 (nav/header/hero). Single addition,
       appended inside the existing :root block per DESIGN.md 交接契約 硬約束⑨
       (single styles.css / single :root — not a second :root block).
       Value is a literal spec value from DESIGN.md `component.footer.column_heading`
       (uppercase label letter-spacing), not derived from an existing token.
       Reused here by the nav-overlay "Services" eyebrow (same role as the future
       footer SERVICES column heading — see Unit 2 report 觀察 for why). ── */
  --letter-spacing-label-wide: 0.08em;

  /* HERO 遮罩 gradient——2026-08-19 用戶急件覆寫（原 DESIGN.md「極端對比
     規則」字面公式 linear-gradient(to top, rgba(0,0,0,0.88) 0%,
     rgba(0,0,0,0.70) 38%, rgba(0,0,0,0) 62%) 已停用，非刪除，見下）。
     用戶原話「Hero 做這樣是要幹嘛 完整放不就好了」——原公式是滿版垂直
     漸層，從底部 88% 黑一路到 62% 高度都還有明顯黑，全寬覆蓋，導致下半
     部整片看不清圖。改為 bottom-left 錨定的橢圓 radial-gradient：只在
     文字所在的左下角加深，右側／上方留給圖本身。峰值透明度也從 0.88 降
     到 0.75、中段從 0.70 降到 0.40，退場停點從 62% 提早到 70%（橢圓半
     徑百分比，非滿版高度百分比，實際覆蓋面積遠小於原案）。
     ⚠️ 與 DESIGN.md「極端對比規則」字面公式已不一致，需回寫 DESIGN.md
     （見 frontend 報告的 SCOPE-DELTA／需回寫清單），本檔不動 DESIGN.md。
     對比度未經量測（UNVERIFIED，需 QA 覆核 AA）。 */
  /* 2026-08-19 用戶原話「你Hero做這樣是要幹嘛 完整放不就好了」：原值
     ellipse 90% 80% 幾乎罩滿整個 hero，底圖下半部被洗成一片黑。收緊為
     只覆蓋左下文字塊的範圍，讓底圖完整可見；α 起點微升補償縮小的覆蓋
     面積，標題仍有遮罩保護。對比未量測（UNVERIFIED，待 QA 覆核 AA）。 */
  /* 2026-08-20（daniel）：桌機改用與小螢幕**相同**的滿版線性漸層。
     原因：桌機原本是錨在左下角的橢圓 radial，右半邊底緣 α=0，所以 hero 底緣
     與下方 S2（實測背景 rgb(0,0,0)）之間是**硬邊**；≤1023px 因為換成滿版
     linear 才剛好有過渡，變成大小螢幕不一致。

     ⚠️ 本檔原註解記載 2026-08-19「用戶急件覆寫」否決了滿版 linear
        （原話「Hero 做這樣是要幹嘛 完整放不就好了」）。daniel 2026-08-20 澄清
        那不是**客戶**的意見，是 Dawa 那邊的內部稱呼，不構成約束——故改回滿版。
        原 radial 的值保留在 git 歷史（本 commit 之前）。

     值與 ≤1023px 的覆寫**逐字相同**，所以跨越 1024px 斷點時不會有跳動。
     ≤768px 仍有自己的一組（停點 55%/80%），那是為了直幅版位另外調過的。 */
  /* 2026-08-24（daniel 指派）：α 全面重調。8-21 換成明亮版底圖後，這條 scrim
     還是照暗調時代的值在壓，實測文字對比 10.4-16.6:1（門檻 4.5:1），等於拿
     兩倍以上的餘裕去換一片灰。**同時發現舊值的第二個任務根本沒達成**：S2
     實測背景是 rgb(0,0,0) 純黑，而 α=0.88 讓 hero 底緣只到 22，接縫處一直有
     一道 ΔL*7.3 的可見階差（放大截圖可見硬邊，不是理論值）。
     故拆成兩段責任、各自負責：
       0%    α=1     底緣真正落到純黑，接縫消失（舊值 0.88 做不到這件事）
       6%    α=0.72  短促的下襬，收掉上一行那道階差
       18-40% α=0.66-0.58  文字帶：標題與副標實際落點（量的是字行盒，
                    非區塊盒——區塊盒滿容器寬，會把沒有字壓著的亮區也算進去）
       58-75% α=0.22-0   放掉，讓機櫃紋理與 LED 在下半部重新讀得到
     重調後實測（三張 slide 取最差、白字對合成後背景）：
       標題 6.5:1  副標 8.8:1  計數器 8.9:1  chevron 9.4:1  接縫 0
     全數高於 AA 4.5:1，副標與控制項另達 AAA 正文 7:1。留這麼多餘裕是因為
     第二期 hero 圖走 CMS，客戶換圖時不該需要重調 scrim。
     量法：讀底圖像素 -> 依 object-fit cover + object-position + Ken Burns
     矩陣反解取樣點 -> 套 α 合成 -> sRGB 轉線性算相對亮度。**不要用螢幕截圖
     取樣**，那條路要先把字藏起來，而 visibility:hidden 會被 .reveal 蓋掉、
     量到的是白字本身（會得到「對比 1.00:1」這種不合理值）。 */
  /* 2026-08-25（daniel 裁示）：**底緣那隻黑腳收掉**。上一輪（8-24）把 0% 壓到
     α=1，任務是「讓 hero 底緣真正落到純黑，跟 S2 的純黑接縫消失」——那個任務
     連同 S2 的純黑一起於今日撤銷（S2 改深藍 #00173E，見 04-components.css
     的 .section--dark）。α=1 一旦沒有接縫任務，就只剩副作用。

     副作用是量得出來的：hero 底圖 8-21 換成明亮機房後，底緣那一列照片實測
     亮度 **212**，α=1 把它壓成 **1** —— 壓掉 211 階，一張亮照片的下緣被抹成
     純黑。遮罩覆蓋範圍是 hero 下緣往上 600/800px＝**75%**。

     ⚠️ 上一輪的推導有一個前提今天不成立了：它假設「S2 是純黑」是既定事實，
        所以把 hero 往黑裡收是唯一解。實際上原稿的 hero 是**暗藍 plexus**、
        S2 的配圖也是**暗機房（平均亮度 33）**，那一整塊本來就是連續的深色；
        淺色改版把 hero 與 S2 的照片都換成亮的（196–202 / 142），卻沒有動
        這兩個深色設定。所以要修的是**深色那一側**，不是再往黑裡壓。

     新的責任分配（形狀不變，只重配 α 與停點）：
       0-14%   α=0.62-0.64  文字帶下段：副標與控制項（底緣不再落到純黑）
       28%     α=0.58       標題帶
       42-58%  α=0.26-0     放掉，讓走道與機櫃在下半部讀得回來
     實測（1440、三張 slide 各自取文字盒內最壞像素、白字對合成後背景）：
       標題 6.03:1  副標 6.92:1  計數器 7.10:1  chevron 6.99:1
       hero 底緣合成 rgb(50-83)（舊值 rgb(4-6)）
     hero 標題是 56px 大字，WCAG 門檻 3:1；副標 15px 才是 4.5:1。6.92:1 對
     4.5 留 53% 餘裕，理由與上一輪相同：第二期 hero 圖走 CMS，客戶換圖時
     不該需要回頭重調 scrim。
     ⚠️ 曾試過更輕的一組（0.58/0.56/0.50/0.24/0）：底緣只多亮 8-9 階，標題卻
        掉到 **4.45:1**。餘裕不在文字帶，在最底那 48px 與上半的長尾——**把文字帶
        調輕是純虧本**，要輕請只動 0% 與 42% 以上兩端。
     ⚠️ 也試過「白字改深字、遮罩幾乎不要」（既然底圖已經是亮的）：**量出來不行**。
        標題橫跨左側機櫃暗部，深字最差只有 **2.06:1**（slide 3）。白字＋深遮罩
        的方向是對的，要調的是遮罩不是文字。
     量法改用截圖取樣（與上一輪的矩陣反解不同）：把 .hero__content 整個
     visibility:hidden 之後截圖，再用 Range.getBoundingClientRect() 取「字行盒」
     去裁那塊背景、逐像素取最亮者算最壞情況。上一輪註解警告的
     「visibility:hidden 會被 .reveal 蓋掉」是指藏**單一文字節點**；藏
     .hero__content 這個祖先不受 .reveal 影響，實測可行。 */
  /* ⚠️ **2026-08-25 下午重調停點**（同日第二次）。α 值一個都沒動，動的是停點。
        原因是版位改了、不是照片變了：輪播控制項從「副標同一列」搬到 hero 最底下
        自己一列（進度線段），整個文字塊變高，**標題被往上推**：
            改版前  距 hero 底緣 17%–26%
            現在    距 hero 底緣 29%–38%（1440）／49%–71%（375，還多折一行）
        舊停點是照舊位置調的，標題現在落在已經開始收掉的那一段。
        ⚠️ 375 那一格特別慘（實測 1.06:1）：標題正好壓在走道盡頭那塊近全白的
           發光區（實測像素 248），而 scrim 在那個高度已經歸零。**那是照片的
           亮點不是版面破洞** —— 裁出標題盒目視確認過。
        ⚠️ 同日稍早那組「底緣 rgb(44–89)」的數字**已經作廢**：底圖同日換成
           平均亮度 107 的深色版（原 196），底緣合成現在是 rgb(24–52）。 */
  --hero-scrim-gradient: linear-gradient(
    to top,
    rgba(0, 0, 0, 0.62) 0%,
    rgba(0, 0, 0, 0.64) 16%,
    rgba(0, 0, 0, 0.62) 40%,
    rgba(0, 0, 0, 0.28) 56%,
    rgba(0, 0, 0, 0) 72%
  );

  /* HERO 底圖 focal position —— object-position 錨點。
     F4/D20（docs/spec-hero-carousel.md §2.10, §6 YAML `hero_bg_position`）：
     舊值錨定已作廢的單張 plexus 素材裁切座標（docs/hero-bg-spec.md 該碼自
     己已標失效）。D20 把 art-direction 由 4 家族收斂為 2 家族（L 1.80:1／
     P 0.67:1，object-position 取代裁切差異）后，四階改為置中值：wide 用
     50% 85%（3.2:1 框由 1.8:1 圖 cover，垂直裁 44%，85% 延續原 wide 組
     「取下半段、排除上方尖峰亮區」意圖，非 100% 是避免底緣切齊產生硬邊）；
     base/tablet/mobile 皆置中（各自的裁切幅度小或資產本身已是目標比例，
     見規格 D20 附表逐項理由）。三張 slide 共用同一組值；若個別 slide 的
     C4 驗收失敗，用 .hero__slide-media[data-slide="N"] 覆寫，禁 inline
     style（規格 assets.hero_bg_position._per_slide_override）。 */
  --hero-bg-position-wide: 50% 85%;
  --hero-bg-position-base: 50% 50%;
  --hero-bg-position-tablet: 50% 50%;
  --hero-bg-position-mobile: 50% 50%;

  /* ================================================================
     COMPONENT TOKEN — HERO CAROUSEL（Unit 2, 2026-08-18）
     source: docs/spec-hero-carousel.md §6 YAML + §7 token 表，逐字轉錄。
     新增 9 個：chevron 3 + surface-on-dark-subtle 3 + title-gap +
     controls-gap + counter-width。動效沿用既有 --motion-* token（見下方
     COMPONENT LAYER 區塊消費處），不新建同義 duration/easing token——
     規格自己標註未親眼confirm過那組變數名（UNVERIFIED），本檔核對後確認
     實際名稱為 --motion-duration-{hover,press,carousel,enter}／
     --motion-easing-{standard,exit}／--motion-stagger／
     --motion-transform-enter（皆已存在於本檔上方 MOTION 區塊）。
     ================================================================ */
  /* hero 進度線段（2026-08-25，取代原本右下角的兩顆 chevron 膠囊）。
     ⚠️ --hero-seg-hit 是**可點區高度**（觸控地板 44px），--hero-seg-thickness
        才是看得見的那條線。兩者刻意分開：把 hit 併進 thickness 會變成一條
        44px 高的粗棒，把 thickness 當 hit 用則會做出 3px 高的觸控目標。 */
  --hero-seg-hit: 44px;
  --hero-seg-thickness: 3px;
  --hero-seg-width: 56px;

  --hero-chevron-width: 72px;   /* D18：72×72 figma-exact 與 2x 圖目視橫向膠囊互斥，
     套 D7 先例（漢堡 48×40→48×48）取交集：寬留 72，高抬到觸控地板 44 */
  --hero-chevron-height: 44px;
  --hero-chevron-icon-size: 16px;
  --surface-on-dark-subtle: rgba(255, 255, 255, 0.10);        /* chevron default bg */
  --surface-on-dark-subtle-hover: rgba(255, 255, 255, 0.20);
  --surface-on-dark-subtle-active: rgba(255, 255, 255, 0.28);
  --hero-title-gap: var(--space-3);   /* F1：標題↔副標，2x 圖目視 9-14px（±5px），
     修正現行誤用 --hero-text-gap(40px) 的缺陷 */
  --hero-controls-gap: var(--space-5); /* 計數器 ↔ chevron 對之間 */
  --hero-kenburns-from: 1.02; /* Ken Burns 起點 scale（zoom-in 組）／終點（zoom-out 組），
     spec-hero-motion-2026-08-19 §2.11.1。兩端都 >1.0 是刻意的——終點取 1.0 會讓 object-fit:
     cover 的邊界與元素 box 貼齊，次像素捨入可能露 1px 底色縫。reduce 下於 reduce block 歸零為 1。 */
  --hero-kenburns-to: 1.06;
  --hero-autoplay-interval: 5000ms; /* 自動切換週期（每張停留時間，不含轉場時長），行為參數非
     motion duration。site.js 用 getComputedStyle 讀本 token（而非另存 JS 常數），避免兩處數字
     漂移。2026-08-19 用戶原話「Hero每隔5秒自動輪播」明確指定 5000ms，取代原 6500ms（舊值來源
     spec-hero-carousel.md，本次未回寫該 spec，見任務報告 SCOPE-DELTA）。 */
  --hero-counter-width: 7ch;           /* 固定寬，數字更換不得推擠版面 */
  --hero-video-load-timeout: 8000ms; /* docs/spec-hero-video-2026-08-20.md §2.9：8 秒內未進 playing
     視為 unavailable。行為參數非 motion duration，沿用 --hero-autoplay-interval 既有先例——
     site.js 用 getComputedStyle 讀本 token，不在 JS 裡寫魔術數字。不得進 reduce block 歸零。 */

  /* ================================================================
     COMPONENT TOKEN — Batch B1 addition (S2–S6 + Footer 共用 atom 層)
     source: docs/spec-s2-s6-components.md §A-5（uiux 2026-08-18 產出），
     逐字轉錄，未重新推導／未「優化」任何值。追加於既有 :root 尾端，
     未開第二個 :root（DESIGN.md 交接契約 硬約束⑩）。

     誠實揭露：規格 VERDICT / TOKENS / ENDPOINTS 三處皆自稱「新增 41 個
     token」，但下方 A-5 原始 css 區塊逐條實數為 47 個（逐類重新加總：
     1+3+1+3+13+4+4+3+4+5+2+4 = 47，非 41）。判斷為規格自己的加總誤差，
     不是缺值待發明——每條都有完整推導依據與實測比值。故全部 47 條照抄
     不砍，差異已於本批回報顯著標出，不靜默吞掉。 ── */

  /* ── surface（新，補 DESIGN.md 只有一個 inverse 的缺口）── */
  --surface-inverse-deep:        var(--color-neutral-950);   /* S2 黑帶；UNVERIFIED，若 quality 取樣證實為 #00173E，
     改引用既有 --surface-inverse、刪本 token；frontend 不得自行改，回報。 */
  /* ⚠️ 2026-08-25 結案（已回報 daniel、已裁示）——分兩件事，不要混在一起看：
     ① **取樣結果：純黑是對的，Dawa 沒抄錯。** 我去 docs/figma-ref/
        desktop-1448-fullpage.png（原稿全頁 2880x9299）的 S2 帶取樣 6 點
        （y=1900/2100/2300/2500/2700/2790 × x=30/700/2800），除了照片本身壓到的
        兩點以外**全部回 rgb(0,0,0)**，不是 #00173E。所以上面那句 UNVERIFIED
        的觸發條件**沒有成立**。
     ② **但 S2 還是改成深藍了**，理由不是這顆 token 抄錯，是**設計前提被換掉**：
        原稿的 hero 是暗藍 plexus、S2 配圖平均亮度 33，「hero 底→S2」本來是一整塊
        連續的深色；淺色改版之後 hero 亮到 196-202、S2 配圖亮到 142，純黑失去了
        深色鄰居，變成一面牆，而且亮照片釘在純黑上會因同時對比讓黑更黑。
        daniel 2026-08-25 裁示改深藍（＝ S6 的收尾色），全站深色表面收斂成一種。
     ⚠️ 這是**刻意偏離已核定設計**，不是修 transcription bug。日後若有人拿原稿
        來對，會發現 S2 應該是純黑——那不是漂移，是這一條。
     ⚠️ **本 token 現在零消費端**（.section--dark 已改引用 --surface-inverse）。
        上面那句話寫「刪本 token」，但本專案禁刪既有內容（CLAUDE.md 全域守則 2），
        比照 .ck-flow-* / .card-rail 的處理：保留 ＋ 標註。要復原純黑就是把
        04-components.css 的 .section--dark 那一行改回引用本 token。 */

  /* ── on-inverse 文字階層（新）── */
  --text-secondary-on-inverse:   var(--color-neutral-400);   /* 11.75:1 對 #00173E、13.8:1 對 #000000。
     表面鎖定：對白底僅 1.42:1 —— 只准用在 .section--dark / .section--navy 內，
     淺底使用即為驗收失敗（DESIGN.md 交接契約 硬約束⑧，驗收點 2）。 */
  --text-eyebrow:                var(--color-accent-600);    /* 6.02:1 白 / 5.42:1 #F3F3F3 */
  --text-eyebrow-on-inverse:     var(--color-accent-200);    /* 16.2:1 對黑 / 13.6:1 對 navy */

  /* ── hairline on dark（新）── */
  --border-hairline-on-inverse:  rgba(255, 255, 255, 0.10);  /* 1.32:1 對 navy、1.21:1 對黑，與淺底 1.22:1 同級。
     不可直接用 #E8E8E8：對 #00173E 是 14.57:1，那是一條白線不是分隔線。 */

  /* ── section 節奏（新）── */
  --section-head-gap:            var(--space-4);   /* eyebrow→title 16px */
  --section-head-space-after:    var(--space-9);   /* head→內容 60px，與 section padding 同數字 */
  --prose-stack-gap:             var(--space-5);   /* title→段落→CTA 24px */

  /* ── button（新，13 個）── */
  --btn-padding-block:           var(--space-3);   /* 12，硬下限（mobile 44px 觸控餘裕 0），不得再減 */
  --btn-padding-inline:          var(--space-5);   /* 24 */
  --btn-gap:                     var(--space-2);
  --btn-radius:                  var(--radius-alias-control);
  --btn-icon-size:               16px;
  /* lucide() 行內圖示（.licon）的預設邊長（2026-08-24）。
     ⚠️ 在此之前 .licon **全站零 CSS** —— lucide() 與 _chip.php 是套版時保留下來
        但沒有消費端的能力，它們的樣式原本在已刪除的 base.css 裡。
        服務內頁的 Scope 是第一個真正用它的地方，所以基礎尺寸在這裡才建立。
     20px 在 4px 格線上，比 --btn-icon-size(16px) 大一階 —— 那顆是按鈕裡的
     附屬箭頭，這顆是清單項目的主要標記，量體本來就不同。 */
  --licon-size:                  20px;

  /* Scope 項目 hover 時跟著游標的預覽圖（2026-08-24）。
     ⚠️ 刻意小：daniel 說「不用太大張」，而且它是掠過式的裝飾，
        大圖會蓋住正在讀的那一行。260px 寬、3:2 → 約 173px 高。 */
  --scope-peek-width:            260px;
  --btn-primary-bg:              var(--color-brand-900);
  --btn-primary-bg-hover:        var(--color-brand-800);   /* 13.97:1 */
  --btn-primary-bg-active:       var(--color-brand-950);
  --btn-primary-fg:              var(--color-neutral-100);
  --btn-on-inverse-bg:           var(--color-neutral-100);
  --btn-on-inverse-bg-hover:     var(--color-accent-100);   /* 16.19:1 */
  --btn-on-inverse-bg-active:    var(--color-accent-200);   /* 13.75:1 */
  --btn-on-inverse-fg:           var(--color-brand-900);

  /* ── media（新，4 個）── */
  --media-ratio:                 var(--split-media-ratio);   /* 3/2，複用既有值不另立數字 */
  --media-radius:                var(--radius-alias-media);
  --media-radius-sm:             var(--radius-alias-media-sm);
  --media-fallback-bg:           var(--surface-subtle);

  /* ── media--1x1（新，1 個，D37 S5 專用，docs/spec-s5-redesign.md §8.5）──
     單一消費點 .media--1x1 的 --media-ratio 覆寫；不直接覆寫 --media-ratio
     本身（全站僅 :root 定義一次、零覆寫的既有先例），改用具名 token 供
     .media--1x1 間接指向，維持規格宣告的 token 名可追溯。 */
  --card-media-aspect:           1 / 1;

  /* ── tag（新，4 個）── */
  --tag-bg:                      var(--color-accent-100);
  --tag-fg:                      var(--color-accent-700);    /* 7.55:1 */
  --tag-padding-block:           var(--space-1);
  --tag-padding-inline:          var(--space-3);             /* Figma 原稿 10 為 off-ladder → snap 到 4px grid */

  /* ── badge（新，3 個）── */
  --badge-bg:                    var(--color-neutral-100);
  --badge-fg:                    var(--color-brand-900);     /* 17.6:1 */
  --badge-padding-inline:        var(--space-3);

  /* ── news row（新，4 個）── */
  --news-thumb-width:            180px;                      /* derived：168 列高 − space.5×2 = 120 高 → 3:2 得 180 */
  --news-row-padding-block:      var(--space-5);
  --news-row-gap:                var(--space-5);
  --news-arrow-size:             20px;

  /* ── card / index 浮水印（新，5 個）── */
  --card-stack-gap:              var(--space-3);
  --card-media-space-after:      var(--space-5);
  /* 2026-08-18 QA 修正（qaB a11y FAIL，1.03:1）歷史脈絡：舊 --card-index-*
     三個 token（實心 64/56/48px、rgba(255,255,255,0.40)、單一 inset）與
     這段對比反解過程，已隨 2026-08-19 S5 重新設計（D38，
     docs/spec-s5-redesign.md §4 + §9）整批撤回——左下實心浮水印改為
     右下空心描邊，對比機制從「填色 alpha vs 照片像素」換成「描邊 alpha
     vs §3 素材亮度門檻的 3:1 反解」，舊推導不再適用，新推導見規格 §3。
     新 6 個 token 如下。 */
  --card-ordinal-size:               88px;                       /* wide/base 同值；tablet 74 / mobile 64，見下方兩個既有 media block 覆寫 */
  --card-ordinal-stroke-width:       2px;                         /* ≥769；mobile 覆寫 1.5px，見下方 mobile media block */
  --card-ordinal-stroke:             rgba(255, 255, 255, 0.55);
  --card-ordinal-fallback-fill:      rgba(255, 255, 255, 0.18);   /* @supports 不支援 -webkit-text-stroke 時的實心分支 */
  --card-ordinal-inset-inline-start: 77%;
  --card-ordinal-inset-block-end:    var(--space-1);
  --card-ordinal-scrim-gradient:     linear-gradient(to top left, rgba(0, 0, 0, 0.45), transparent 55%); /* D38 §3 逃生口專用，.media--ordinal-scrim 消費，硬約束①禁止 :root 外 hardcode */

  /* ── stat（新，2 個）── */
  --stat-padding-block:          var(--space-3);   /* 2026-08-19 uiux S6 reshoot C4：32→12px，分隔線長度精確等於參考圖「內容+24px」，見任務報告 */
  --stat-value-space-after:      var(--space-3);

  /* ── stat 新增（本 pass，3 個，source: uiux S6 reshoot spec §7-A）── */
  --stat-value-fg:                var(--color-accent-200);                    /* C2：參考圖數字為淺藍，對 #00173E 實算 13.55:1（大字門檻 3:1 ✓） */
  --stat-label-space-after:       var(--space-4);                             /* C6：label→badge 獨立為 16px，與 value→label 12px 分層 */
  --stat-label-min-block-size:    calc(2 * var(--type-role-paragraph-line-height)); /* C5：最少兩行高，四欄底緣不會因為某一句只有一行就明顯錯開。
     ⚠️ 2026-08-26 由 --type-role-meta-line-height 改成 paragraph —— .stat__label 的字級已改吃內文階（見 05-blocks.css），
        繼續用 meta 的行高會算出一個比實際行高還小的下限，等於這顆 token 失效。 */

  /* ── footer（新，4 個）── */
  --footer-column-gap-block:     var(--space-4);             /* 22 行高 + 16 = 38px 節奏，由 .link-list（C13）消費 */
  --footer-statement-max-width:  384px;                      /* figma-exact，Frame 3464 實測寬 */
  --giant-wordmark-size:         clamp(64px, 5.5vw, 88px);   /* spec-footer-marquee-revision-2026-08-19 §7 T2 裁決值；mobile 覆寫見下方既有 media block */
  --giant-wordmark-color:        var(--text-primary);
  --giant-wordmark-letter-spacing: -0.01em;                  /* 修正方向錯誤：88px 曾比 56px 的 h2 還鬆（DESIGN.md type_scale.h1.ls_rule） */
  --giant-wordmark-line-height:  1.15;                       /* 取代舊 .giant-wordmark 的字面 line-height:1（垂直捲軸根因之一） */

  /* ── giant-marquee（跑馬燈動效，新，6 個）：duration/copies 為 8 句標語版
     重算值，與 spec-footer-marquee-revision-2026-08-19.md D42-D44（單句×5份）
     不同——用戶第三次原話「上面放一些適合的標語」（複數）推翻單句重複的
     架構，Dispatcher 裁決 frontend 直接依同一公式重算，非規格原文數字，
     推導見交接報告。mobile 覆寫見下方既有 media block。 ── */
  --giant-marquee-duration:      98s;                        /* 8句串接後單位寬 ÷ 83px/s（原檔錨定速度，沿用不變） */
  --giant-marquee-gap:           var(--space-11);
  --giant-marquee-copies:        3;                           /* (N-2)×單位寬 ≥ 3440 視口，N=2 時 0 恆不成立，N=3 起才成立 */
  --giant-marquee-fade:          var(--space-11);
  --giant-marquee-step:          calc(-100% / var(--giant-marquee-copies));

  /* ── nav-overlay 右側面板（2026-08-18 派工單：用戶回報彈出選單應為右側面板、
     非全螢幕；寬度 372px 為用戶直接裁決值，非 Figma/量測值——原稿
     nav-overlay.png 面板寬度本身標 UNVERIFIED（單張截圖無面板外像素可供
     判定左緣位置，見任務報告 §1），無法從圖片精算，此數字由用戶口頭裁定。
     獨立成一個字面 px token，不套用 --space-* 階梯（372 非 4px grid 倍數，
     屬本元件專屬值），比照既有 --news-thumb-width / --card-row-scroll-width
     等 LAYOUT 區 literal px 慣例。 ── */
  --nav-overlay-panel-width: 372px;
  --nav-panel-inset-x: var(--space-6);   /* 32px — 面板自有左緣，與頁面 --inset-x 解耦（Nav toggle 規格 §2.1）；
     ≤768 面板滿寬時改回全站唯一左緣，見下方 @media (max-width: 768px) 覆寫 */

  /* ── Nav toggle 形變（source: docs/spec-nav-toggle.md §3，逐字轉錄，三數值不得四捨五入） ── */
  --icon-hamburger-stroke: #443F3B;   /* DESIGN.md color.reserved.dark_brown — 用戶 2026-08-17 就此用途裁示通過。
     刻意保留獨立字面值、不改指向 --color-reserved-dark-brown：該 token 本身仍標記 quarantined
     禁直接使用，此處是「已核准的單一用途」，非解除整體隔離 */
  --menu-toggle-bar-shift: 6px;       /* 兩線各自 y=13.5 / 25.5 收攏到共同光學中心 19.5，各走 6px */
  --menu-toggle-bar-angle: 17.354deg; /* atan(7.5/24) = 17.3540°，與 menu-close.svg 的 matrix 反解斜率完全相同 */
  --menu-toggle-bar-stretch: 1.0477;  /* 50.2892/48，讓線旋轉後水平投影仍佔滿 48 */

  /* Dispatcher 裁決 2026-08-18，非原稿量測值（原稿 nav-overlay.png 整張截圖
     皆為面板內部，無面板以外像素可供量測 backdrop 有無/顏色/透明度，見任務
     報告 §3）。命名比照既有 --surface-on-dark-subtle 慣例（語意型 surface
     別名，非 component 前綴），供 nav-overlay backdrop 使用，值為 drawer
     backdrop 通用值。 */
  --surface-scrim: rgba(0, 0, 0, 0.5);

  /* D38（docs/spec-s5-redesign.md §4 + spec-s5-carousel-2026-08-19.md）：
     原「S5 服務卡左下 scrim」token（2026-08-18 追加授權，供
     `.card > .media::before` 消費）已隨數字浮水印從左下實心搬到右下空心
     描邊、原消費規則整條移除而一併撤除——理由與新逃生口位置見
     .media--ordinal-scrim（定義於 .media--1x1 旁）。 */

  /* ================================================================
     COMPONENT TOKEN — HEADER STUCK-SCROLL GLASS（2026-08-19 派工單）
     source: uiux 回傳原文 §3-C／§3-G C7。用戶原話：「NAV 滾到下面才彈出
     高度比較短的 NAV Logo也變小 NAV 背景 70%白的 液態玻璃 帶一點自然的陰影」
     ================================================================ */
  --header-stuck-bg: rgba(255, 255, 255, 0.70); /* D31：用戶裁決值 70%，不得改動 */
  --header-stuck-backdrop: var(--elevation-backdrop-blur-header) saturate(1.8);
  /* saturate 非裝飾：70% 白只讓底下 30% 色度穿透，玻璃會讀成白紗覆蓋而非透視。原值 1.4
     把感知色度補回約 42% 穿透水準；對比地板不受影響（最壞底 #000000 為無彩，saturate 對
     無彩恆等變換），此性質不隨數值改變而改變。⚠️ WebKit bug 289800（backdrop-filter 吃不到
     var()，至 2026-07-02 skill 查核仍 NEW 未修）——2026-08-19 第 1 段派工單已於
     .site-header.is-stuck::before 改直寫字面值（DESIGN.md D36 例外條文；該選擇器已於
     第 2 段搬遷為 .site-header.is-stuck .site-header__bar::before，字面值隨遷未變），此
     token 仍為單一真相源，異動時須同步手動更新該處字面值。真機 iOS 渲染驗證仍待
     quality（見驗收 G2），非本 token 定義本身的缺口。
     2026-08-19 A2 第 4 段（D40）：1.4→1.8，隨主模糊 10px→20px 同步加大，數值為 uiux 規格
     §3 方向 A2 直接給定（非本檔重新推導 42% 穿透公式），下方兩行字面值已同步更新。 */
  --header-logo-scale-compact: 0.75; /* D35：transform:scale 而非改 width，避免 reflow
     與 space-between 下 nav 橫移；transform-origin:left center 保左緣零位移（D1） */

  /* 2026-08-19 用戶當面裁決撤回浮島（派工單「Header 去藥丸化」）：--header-stuck-detach
     （8px 脫離上緣）與 --header-stuck-tray-bg（D46 滿版托盤層 35% 白）兩個 token 與其
     唯一消費規則已刪除。用戶原話：「網頁往下滾 就置頂寬度滿版就好 你想太多了 我沒有要
     藥丸狀」。玻璃層歸位為單一滿版層（見 .site-header::before 規則），數值不變。
     2026-08-19 用戶裁決簡化（P1-6 修復批，取代上一版 A2「漸層模糊 + 邊緣高光 + 厚度梯度」）：
     原本這裡的 4 個 token（--header-glass-blur-edge／--header-glass-edge-mask／
     --header-glass-specular／--header-glass-sheen）與其消費規則已移除，玻璃收斂為單層：
     70% 白 + 字面值 blur(20px) saturate(1.8)。DESIGN.md 對應條目已成孤兒，待負責
     DESIGN.md 的 agent 清理，見任務報告。 */

  /* ================================================================
     COMPONENT TOKEN — S4 NEWS ROW HOVER（2026-08-19 uiux pass）
     授權來源：用戶原話「hover也太醜」。
     具名參考：Stripe 清單列 hover（整列升到更高一階表面 + 內容有內距 +
     不加投影） + Swiss rule-as-structure（線長 = 被標示內容的寬度）。
     硬約束：本組禁任何帶 blur/offset 的 box-shadow —— --elevation-level3
     的 blast radius 明文限定 .site-header::before（見本檔 :363-369），
     --elevation-level2 已收斂為 none。下方 ring 為 0 0 0 1px，
     零模糊零位移，屬 hairline 語言，不是 elevation。
     ================================================================ */
  --news-row-bg:             var(--surface-subtle);   /* #F3F3F3，＝ S4 .section--subtle 底色 */
  --news-row-bg-hover:       var(--color-neutral-300); /* #E8E8E8。2026-08-26 daniel：
     「news 那邊 hover 的時候該 item 會有背景變化」。
     ⚠️ **這是把 2026-08-19「NEWS hover 極簡化」拿掉的四項之一加回來,而且只加這一項。**
        當時被拿掉的是背景變色／圓角／外框 ring／border-color 四項，daniel 的原話是
        「news 的 Hover 這樣太奇怪 圖片放大 箭頭微動就好」—— 他反對的是那一整組裝飾，
        現在要的是單純的底色變化。**圓角與 ring 不要加回來。**
     ⚠️ **往深走不是往白走。** 舊值是 #FFFFFF，那只在首頁成立 —— 首頁 S4 是
        `.section--subtle`（#F3F3F3），列與區塊同色，hover 轉白才會浮起來。
        但 `/news/` 的區塊是 `.section--page`（**白色**），列是 #F3F3F3 的灰帶，
        hover 轉白會讓整列**融進頁面消失**，比沒有 hover 還糟。
        改用中性階往下一格（200 → 300），兩頁方向一致：首頁是灰底上再深一階、
        `/news/` 是白底上的灰帶再深一階，都讀得出來。
     ⚠️ 用既有色階不自己調色（硬約束①）。實測對比：--text-primary 13.76:1、
        --text-secondary 5.05:1，都在門檻之上；#F3F3F3 與 #E8E8E8 的明度差 0.089，
        看得出來但不吵。 */
  --news-row-padding-inline: var(--space-5);          /* 24px；≤768 覆寫為 space-4(16px)，見下方 media block */
  --media-scale-hover: 1.04; /* 2026-08-19 用戶當面裁決極簡化 hover（派工單「NEWS
     hover 極簡化」）：用戶原話「news 的 Hover 這樣太 奇怪 圖片放大 箭頭 微動就好」。
     --news-row-bg-hover／--news-row-radius／--news-row-ring-hover／--news-row-ring-rest
     四個 token 與其消費規則（背景變色/圓角/外框 ring）已刪除。本 token 為新增的圖片
     放大效果，4% 落在建議區間 3–5% 中段；沿用既有 --motion-duration-hover(160ms) +
     --motion-easing-standard，與同列箭頭 translateX 同一手勢同步（不用派工單建議的
     280ms，因箭頭效果已用 160ms，同一 hover 手勢不宜拆兩個時長）。消費點：
     .news-row__media .media__img（見下方規則）。 */
}


/* ================================================================
   @FONT-FACE — 三支自託管字型（source: DESIGN.md font, loading_policy）
   相對路徑，禁 CDN；latin subset；Marcellus 僅 400（單一字重，faux bold
   由下方 base 區 html{font-synthesis:none} 統一擋下）；Albert Sans 三個
   字重共用同一份 variable 字型檔（wght 軸，DESIGN.md font.body.files
   首選方案，非靜態三檔 fallback，src 相同、font-weight 各自宣告，由瀏覽器
   依 wght 軸插值渲染，Google Fonts 對 variable 字族的標準服務手法）；
   Inter 僅 700（stat_huge 專用，靜態單檔，見修正清單 E1）。
   file:// 協定下相對路徑一樣可解析，見 DESIGN.md loading_policy.v。
   ================================================================ */

@font-face {
  font-family: "Marcellus";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/assets/fonts/marcellus-400.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Albert Sans — 一份 variable 檔，三個 @font-face 各自宣告離散 font-weight，
   驗證見 U4 report（document.fonts 三態載入 + 視覺量測）。 */
@font-face {
  font-family: "Albert Sans";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/assets/fonts/albert-sans-var.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Albert Sans";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("/assets/fonts/albert-sans-var.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Albert Sans";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("/assets/fonts/albert-sans-var.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Inter — 僅 700，stat_huge 專用。tnum 特性須保留於字型檔本身（未經
   pyftsubset 裁切，Google latin 分割不動 GSUB/layout 表）；實際
   font-feature-settings 套用在 --type-scale-stat-huge-font-feature-settings
   token（本檔 :root 區塊已定義），於 stat_huge 元件建立時引用——該元件
   不在本次範圍，本檔只確保字型檔支援 tnum（見 U4 驗證）。 */
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("/assets/fonts/inter-700.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}


/* ================================================================
   BREAKPOINT OVERRIDES — desktop-first + 單一 min-width（wide）
   source: DESIGN.md breakpoints._cascade。四個模式各自完整宣告
   radius 與 type，不依賴繼承（tablet 低於 mobile，非單調）。
   ================================================================ */

/* ── TABLET：769–1023（source: DESIGN.md breakpoints.tablet / rwd_behavior.tablet） ── */
@media (max-width: 1023px) {
  :root {
    /* type_scale — 完整覆寫，不得只寫「有變的」 */
    --type-scale-h1-size: 72px;
    --type-scale-h1-line-height: 78px;
    --type-scale-h2-size: 48px;
    --type-scale-h2-line-height: 62px;
    --type-scale-h3-size: 34px;
    --type-scale-h3-line-height: 48px;
    --type-scale-h4-size: 22px;
    --type-scale-h4-line-height: 32px;
    --type-scale-h5-size: 17px;
    --type-scale-h5-line-height: 27px;
    --type-scale-h6-size: 13px;
    --type-scale-h6-line-height: 21px;
    --type-scale-body-size: 15px;
    --type-scale-body-line-height: 24px;
    --type-scale-caption-size: 12px;
    --type-scale-caption-line-height: 21px;

    /* radius — 完整覆寫（三階皆低於 mobile 欄，刻意非單調，照抄不修正） */
    --radius-small: 10px;
    --radius-medium: 14px;
    --radius-large: 20px;

    /* layout（觸發寬度已套用修正清單 F 組：原 <=960 → <=1023） */
    --inset-x: var(--space-9);
    --section-padding-block: 48px;
    /* ⚠️ 2026-08-25 修正：58px → 72px。**舊值從一開始就與實際高度對不上。**
       舊註解的推導是 `34(wordmark) + space.3(12)×2 = 58`，但那算的是 **logo**，
       而 bar 裡真正最高的是 **menu-toggle（48px 觸控目標）**。實際高度是
       `max(logo 34, toggle 48) + padding-block×2`，而且 `.site-header__bar`
       的 padding-block 在這個斷點**從來沒有被覆寫過**（一直是 --space-4 16px），
       所以實測是 48 + 16×2 = **80px**，token 卻說 58 —— 差 22px。
       症狀（2026-08-25 daniel 回報）：`<body>` 的 padding-top 吃這顆 token，
       所以內容只讓開 58px，而 header 佔了 80px —— **hero 上緣被蓋掉 6px**
       （實測 hero top 74、header bottom 80）。桌機看不到，因為 base 的
       min-height 102 > 內容高 80，token 剛好等於實際值。
       修法是兩邊一起改：padding-block 補上原本就打算給的 --space-3(12px)
       （見 03-header-hero.css 該斷點），高度就變成 48 + 12×2 = **72px**，
       token 照實填 72。
       ⚠️ 這顆有 7 個消費端（body padding-top、--hero-height、哨兵兩條、
          錨點 scroll-margin-top、nav overlay padding、hero 捲動展開的
          sticky 貼附點），全部走 token，所以改這一顆就整組跟上。
       ⚠️ 精簡態仍是 64px，所以 72→64 還留著 8px 的高度變化；
          若哪天把 padding 再壓到 --space-2(8px)，靜止態就會等於精簡態，
          捲動時只剩玻璃在變、幾何不動。 */
    --header-height: 72px;
    --header-height-compact: 64px; /* 2026-08-19 header stuck-glass：48 + space.2(8)×2 = 64，恰等於 min-height */
    --hero-height: min(640px, calc(100vh - var(--header-height) - var(--hero-inset)));
    --hero-height: min(640px, calc(100svh - var(--header-height) - var(--hero-inset)));
    --hero-text-padding: 80px calc(var(--container-gutter-inline) - var(--hero-inset)) 56px;

    /* D21（docs/spec-hero-carousel.md §2.6/§2.10）：加控制列後文字塊頂緣
       上移，實算距 hero 底 37%，原 38% 透明停點是骰邊緣 → 移到 70% 取得
       餘裕。α 值本身未動（頂緣仍 0.88、中段仍 0.70），只動停點百分比，
       hero-bg-spec §4 硬約束 2/3 未被違反。 */
    /* 2026-08-24：同 :root 的重調，值一樣逐字相同（跨 1024px 不跳動的既有
       不變式不動）。此斷點實測：標題 6.5:1、副標 8.8:1、計數器 8.9:1、
       chevron 9.4:1、接縫 0。理由與量法見 :root 同名 token 上方註解。 */
    /* 2026-08-25：同 :root 的收腳，值一樣逐字相同（跨 1024px 不跳動的既有
       不變式不動）。此斷點實測：標題 6.1:1、副標 6.9:1、底緣合成 rgb(56-88)。
       理由與量法見 :root 同名 token 上方註解。 */
    /* 2026-08-25 下午：跟著 :root 重調，值仍然逐字相同（跨 1024px 不跳動的
       既有不變式不動）。理由見 :root 同名 token 上方註解。 */
    --hero-scrim-gradient: linear-gradient(
      to top,
      rgba(0, 0, 0, 0.62) 0%,
      rgba(0, 0, 0, 0.64) 16%,
      rgba(0, 0, 0, 0.62) 40%,
      rgba(0, 0, 0, 0.28) 56%,
      rgba(0, 0, 0, 0) 72%
    );
    --split-columns: 1fr; /* 單欄，圖先文後，取消鏡射 */
    --news-columns: 1fr;
    --card-row-scroll-width: 320px;
    --card-row-grid-columns: 2;

    /* D38（docs/spec-s5-redesign.md §7）：Services 卡片數字，tablet 階降級。
       原 Batch B1 的 --card-index-size 已隨 D38 撤回，改為 --card-ordinal-size。 */
    --card-ordinal-size: 74px;

    /* 2026-08-19 S4 news-row hover pass（R6）：斷點完整宣告慣例，不依賴繼承 */
    --news-row-padding-inline: var(--space-5);
  }
}

/* ── MOBILE：375–768（source: DESIGN.md breakpoints.mobile / rwd_behavior.mobile） ── */
@media (max-width: 768px) {
  :root {
    --article-dropcap-size: 3.5em; /* 窄欄（375 實測 327px）下 4.6em 會吃掉半行 */
    /* ── 內頁大標的 role token（2026-08-21 新增，中央廚房 FE-02 的後續）──
       ⚠️ 覆寫的是 **--type-role-page-title-size**（內頁 hero 專用、套版時新建的
          role token），**不是 --type-scale-h1-size**。她的 scale 與其他消費端不受影響。
       為什麼需要：FLOOR GUARD 只覆蓋 max-width:374（大致只有 iPhone SE），
       但現代手機多落在 375–430px，那區間 --type-scale-h1-size 仍是 66px。
       內頁 hero 容器在 375px 下只有 312px 寬，66px 的「Infrastructure」實測 395px、
       「Technology」338px，都塞不下，overflow-wrap 只好把字攔腰砍斷。
       48px 是量出來的：395 × 48/66 ≈ 287px < 312px，留約 25px 餘裕。
       ⚠️ 必須放在這個（≤768）區塊、也就是 ≤374 floor guard **之前**：
          兩個 @media 特異性相同，後出現的勝；放在 374 之後會把 floor guard
          的 40px 蓋掉（第一版就是這樣寫錯的）。
       ⚠️ 日後新增更長的服務名／文章標題要重量一次。 */
    --type-role-page-title-size: 48px;
    /* type_scale — 完整覆寫 */
    --type-scale-h1-size: 66px;
    --type-scale-h1-line-height: 72px;
    --type-scale-h2-size: 50px;
    --type-scale-h2-line-height: 58px;
    --type-scale-h3-size: 26px;
    --type-scale-h3-line-height: 44px;
    --type-scale-h4-size: 20px;
    --type-scale-h4-line-height: 23px;
    --type-scale-h5-size: 16px;
    --type-scale-h5-line-height: 24px;
    --type-scale-h6-size: 12px;
    --type-scale-h6-line-height: 20px;
    --type-scale-body-size: 15px;
    --type-scale-body-line-height: 24px;
    --type-scale-caption-size: 12px;
    --type-scale-caption-line-height: 20px;

    /* radius — 必須顯式重設，不能繼承 tablet 的 10/14/20 */
    --radius-small: 12px;
    --radius-medium: 16px;
    --radius-large: 24px;

    /* layout（修正清單 F 組：原 <=768:40px/<=420:24px 兩段階梯 → 單一流體 clamp） */
    --inset-x: clamp(24px, 6.4vw, 40px);
    --nav-panel-inset-x: var(--inset-x);  /* 面板滿寬時回到全站唯一左緣（DESIGN.md D1）。
       僅此斷點覆寫——@media (max-width: 1023px) 不得加此行，tablet 面板仍是 372px 浮層 */
    --section-padding-block: clamp(32px, 8.5vw, 40px);
    --header-height: 72px; /* 同 tablet：max(logo 34, toggle 48) + --space-3(12)×2。舊值 58px 的完整說明見上面那個斷點。 */
    --header-height-compact: 64px; /* 2026-08-19 header stuck-glass：同 tablet 值，比照專案「四斷點各自完整覆寫」慣例顯式重複 */
    /* ≤768 的 --hero-inset 降階（16px）：本斷點的 --inset-x 是
       clamp(24px, 6.4vw, 40px) —— 375 只有 24px，內縮再吃掉 24 的話
       照片邊緣到文字之間就沒有東西了（calc 會歸零甚至變負）。 */
    --hero-inset: var(--space-4);
    --hero-height: min(560px, calc(100vh - var(--header-height) - var(--hero-inset)));
    --hero-height: min(560px, calc(100svh - var(--header-height) - var(--hero-inset)));
    /* ⚠️ 2026-08-25：≤768 的左右內距**不再**用「頁面 gutter 減掉 hero inset」
       那條公式（daniel：「手機版 hero 的 padding 好像太小了，文字跟按鈕都很靠
       近邊邊」）。他是對的，而且量得出來 ——
         375：gutter clamp() 解到 24px、inset 16px → 內距只有 **8px**
         430：28 − 16 = 11.5px
         768：24px（這一格其實還好）
       那條公式在桌機是對的（1440 是 60 − 24 = 36px），因為 gutter 夠大；
       到了 375 gutter 本身只有 24，扣掉 inset 就幾乎不剩。**在一張圓角照片卡
       裡面留 8px 的內距，看起來就是字快掉出去了。**
       改成固定 --space-5(24px)。
       ⚠️ **這是刻意偏離規則 7-2 的「文字左緣與其他區塊共用同一條」**，
          而且只偏離 ≤768 這一格：文字左緣由 24px 變成 **40px**（其他區塊仍是
          24px），差 16px。桌機三個斷點（1440/1200/1024 的 60px、768 以上）
          完全不動，容差仍然是 0。
       ⚠️ 判準是「hero 是一張照片卡、其他區塊是頁面本文」——卡片裡的字本來就
          該比頁面本文多留一點邊。而 375 的 gutter 只有 24px，**要同時做到
          「卡片浮起來」和「字不貼邊」在數學上就不可能**：就算 inset 歸零，
          內距也只有 24px，而那時卡片已經不浮了。
       ⚠️ 上下值（64/40）未動。 */
    --hero-text-padding: 64px var(--space-5) 40px;

    /* D21：加控制列後（此斷點控制列另起一行，文字塊頂緣升更多）實算距
       hero 底 54%，原 62% 停點處 α 已歸零，標題實質無遮罩保護 → 移到
       80% 取得餘裕。α 值未動，同上。 */
    /* 2026-08-24：本斷點仍需自己一組——文字在此落在距底緣 p=0.07-0.43
       （直幅版位、控制列另起一行），把 :root 那組照搬過來標題只剩 4.6:1。
       作法是沿用 :root 的 α 值、把停點整體上移（18->25、40->46、58->64、
       75->84），與本檔原本「行動版停點 55%/80% 比桌機高」的邏輯同源。
       實測：標題 6.4:1、副標 8.1:1、計數器 8.9:1、chevron 9.1:1、接縫 0
       ——與 :root 幾乎同水位，兩個斷點的觀感一致。 */
    /* 2026-08-25：同 :root 的收腳，但**本斷點仍需自己一組**，理由與上一輪相同
       ——文字在此落在直幅版位、控制列另起一行，把 :root 那組照搬過來標題會掉
       一截。作法同樣是沿用 :root 的 α、把停點整體上移。

       ⚠️ 但停點要照 **375** 算，不是照 768 算 —— 這是本輪抓到的一個既有錯誤。
       實測標題字行盒在 hero 高度中的落點（Range.getBoundingClientRect，
       百分比從底緣起算）：
           768px   標題 31.8% -> 43.0%（兩行）
           375px   標題 31.8% -> **53.4%**（三行，同一句話多折一行）
       兩者共用這一組值，所以**上緣要蓋到 53.4% 才算蓋住**。舊值在 46% 就開始
       收（0.60 -> 0.22@64%），推到 53.4% 時 α 只剩約 0.45。
       ⚠️ **舊值在 375 實測標題只有 3.55-3.83:1**（三張 slide），而舊註解寫的
          「標題 6.4:1」對得上的是 768 那一格 —— 375 這一格從來沒有被真的量到。
          很可能是踩到 headless 的最小視窗寬（--window-size=375 實測 clientWidth
          回 500，必須下 Emulation.setDeviceMetricsOverride 才量得到真的 375）。
          本輪四斷點全部改用 setDeviceMetricsOverride，並把 clientWidth 印出來自檢。
       故 0.60 的停點由 46% 拉到 **55%**（蓋過 53.4% 再留一點），之後才收。
       實測（白字對合成後背景、三張 slide 各取文字盒內最亮像素）：
           375px  標題 6.03-6.12:1  副標 6.48-7.18:1  底緣合成 rgb(44-89)
           768px  標題 6.14-6.27:1  副標 6.58-6.78:1  底緣合成 rgb(45-89)
       —— 375 的標題從 3.6:1 修到 6.0:1，同時底緣從 rgb(3-6) 抬到 rgb(44-89)。
       ⚠️ 標題折幾行會直接決定要蓋到多高。改 hero 文案、改 --type-scale-h2-size
          或改 --hero-text-padding 之後要回頭重量這個百分比，不要只看 768。 */
    /* 2026-08-25 下午：保護帶再往上拉一大截（55% -> 74%）。
       本斷點的標題在 375 會折三行，加上控制列另起一列，實測落在
       **距底緣 49%–71%**（768 是 43%–66%）——舊值在 55% 就開始收，
       標題上半整個裸露在走道盡頭那塊近全白的發光區上（實測 1.06:1）。
       實測（三張 slide 各取文字盒內最亮像素）：
           375  標題 6.19–6.20:1  副標 6.39:1  控制項 ≥8.67:1
           768  標題 6.29–6.30:1  副標 6.39–6.40:1  控制項 ≥6.41:1 */
    --hero-scrim-gradient: linear-gradient(
      to top,
      rgba(0, 0, 0, 0.62) 0%,
      rgba(0, 0, 0, 0.64) 20%,
      rgba(0, 0, 0, 0.62) 74%,
      rgba(0, 0, 0, 0.30) 86%,
      rgba(0, 0, 0, 0) 96%
    );
    --news-row-height: auto; /* 圖上文下 */
    --card-row-scroll-width: 280px;
    --card-row-grid-columns: 1;

    /* D38（docs/spec-s5-redesign.md §7）：Services 卡片數字，mobile 階降級；
       描邊寬同階降級為 1.5px（≥769 為 2px，見上方 base 定義）。原
       Batch B1 的 --card-index-size 已隨 D38 撤回，改為 --card-ordinal-size。 */
    --card-ordinal-size: 64px;
    --card-ordinal-stroke-width: 1.5px;
    --giant-wordmark-size: clamp(48px, 12vw, 64px);   /* spec-footer-marquee-revision §7 T2 mobile 值 */
    --giant-marquee-duration: 144s;                    /* 8句串接後單位寬 ÷ 34px/s（原檔錨定速度，沿用不變） */
    --giant-marquee-gap: var(--space-8);

    /* 2026-08-19 S4 news-row hover pass（R6）：mobile 降一階避免 375px 下內容區過窄 */
    --news-row-padding-inline: var(--space-4);
  }
}

/* ── FLOOR GUARD：≤374（非 Figma 模式，320–374 的安全層，職權嚴格限縮）
     source: DESIGN.md breakpoints.floor_guard。只覆寫 h1/h2 size，
     其餘一律照 mobile 模式，不得在此層改任何佈局/間距/圓角/元件規格。 ── */
@media (max-width: 374px) {
  :root {
    --type-scale-h1-size: 44px;
    --type-scale-h2-size: 44px;
    /* 內頁大標的 role token（2026-08-21 新增，見下方 ≤768 區塊的說明）。
       floor guard 這層要比 ≤768 那層再小一級，否則會被它蓋回去。 */
    --type-role-page-title-size: 40px;
  }
}


/* ── WIDE：≥1441（source: DESIGN.md breakpoints.wide / rwd_behavior.wide）
     內容容器與 base 完全相同（container 仍封頂 1680（2026-08-20 由 1200 上修，見下方 LAYOUT 小節），不在此新增覆寫——
     交接契約硬約束①），type 與 radius 升一級；header 專屬上限另外覆寫
     （方向 E，2026-08-19，見下方 LAYOUT 小節），NAV 因此可比內容容器寬。 ── */
@media (min-width: 1441px) {
  :root {
    --type-scale-h1-size: 82px;
    --type-scale-h1-line-height: 92px;
    --type-scale-h2-size: 64px;
    --type-scale-h2-line-height: 68px;
    --type-scale-h3-size: 50px;
    --type-scale-h3-line-height: 70px;
    --type-scale-h4-size: 26px;
    --type-scale-h4-line-height: 40px;
    --type-scale-h5-size: 20px;
    --type-scale-h5-line-height: 40px;
    --type-scale-h6-size: 14px;
    --type-scale-h6-line-height: 22px;
    --type-scale-body-size: 17px;
    --type-scale-body-line-height: 27px;
    --type-scale-caption-size: 14px;
    --type-scale-caption-line-height: 24px;

    --radius-small: 14px;
    --radius-medium: 18px;
    --radius-large: 28px;

    /* ── LAYOUT — header 專屬上限（方向 E，2026-08-19）。用戶在
       headerCap 1680 / 1440 二選一中拍板 1680。落差公式
       (headerCap−1200)/2＝240px 為理論值（滿視口寬），封頂後不隨視口
       線性擴大——1920 與 2560 兩個視口的落差須完全相同，這是驗收點 1
       的關鍵回歸判準。--container-max-width 本身不在此新增覆寫（交接契約硬約束①，現值 1680，見下方 2026-08-20
       追記）。source: DESIGN.md layout.header_max_width

       2026-08-20 追記：--container-max-width 之後已由 1200 改為 1680
       （用戶顯性推翻 DESIGN.md D28 對方向 D 的否決，見上方 :root 定義），
       與 --header-max-width 現值相等。上方 (headerCap−1200)/2＝240px 的
       落差公式與其理論值僅為歷史紀錄——header／container 現共用同一個
       cap（1680）與同一條公式，四斷點左緣逐 px 相等，落差恆為 0（可由
       本檔 :root 兩個 token 現值直接驗證）。 ── */
    --header-max-width: 1680px;

    /* 2026-08-19 S4 news-row hover pass（R6）：斷點完整宣告慣例，不依賴繼承 */
    --news-row-padding-inline: var(--space-5);
  }
}

