/* ================================================================
   Board Sea Technology — 05-blocks.css
   S5 卡片列 ＋ 統計數字 ＋ footer ＋ 跑馬燈（區塊層）。首頁專屬的組裝。

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

/* ================================================================
   S5 BLOCK LAYER — 區塊層（規格 §B「S5 — Services 卡片列」；.card-rail/
   .card 命名空間，B4 派工單硬約束⑦：只寫 .card-rail/.card，不寫 atom 層）
   施工依據：docs/spec-s2-s6-components.md §B S5 段、§A-2 C7/C8/C9 詳規、
   §A-3 互動狀態矩陣 .card 列、§A-4 內容韌性 .card 列

   render_condition（承 hero__controls 既有 precedent，見 site.js hero
   carousel 段落開頭註解）：.section-head__actions 在 HTML 上預設帶
   hidden 屬性；site.js 的 S5 rail 模組量到 .card-rail 真的溢出
   （scrollWidth > clientWidth）才移除 hidden。JS 未執行或找不到必要節點
   時，actions 維持 hidden——.card-rail 本身 overflow-x:auto 仍可用滑鼠/
   觸控/軌跡板捲動，不因缺少 JS 控制項而不可捲動（規格 §A-2 C8 a11y
   硬約束）。

   互動實作：整卡可點（§A-3 .card 列）採 stretched-link 手法，同 .news-row
   （見上方 S4 區塊頂部大註解）——.card__cta（CTA 按鈕本身）的 ::after
   偽元素 position:absolute + inset:0，相對 .card（position:relative）
   撐滿整卡可點/可聚焦區；.card__cta 自身不設 position，讓 ::after 的
   containing block 落在 .card 而非按鈕自己身上。.card:has(.card__cta:
   focus-visible) 在卡外緣畫 ring；.card__cta:focus-visible 本身
   outline:none 有明確替代（ring 轉移到 .card，非違反硬約束③無替代裸關
   outline），選擇器刻意加 .card 前綴讓 specificity (0,3,0) 穩定蓋過
   atom 層 .btn:focus-visible 的 (0,2,0)，不依賴原始碼順序的 tie-break。

   等高與貼底：.card-rail 為 flex row，align-items 預設 stretch 讓五張卡
   等高（卡高由最長卡決定，§A-4）；.card__cta 的 margin-block-start:auto
   在等高後的卡內把按鈕推到底部，五卡按鈕底緣自動對齊，不需要 JS 量測。

   按鈕溢出風險（§A-4 .btn 列既定策略）：`.btn` 的 white-space:nowrap
   繼承自 atom 層不重寫；過長標籤的因應是「縮標籤不縮按鈕」，已由 content
   agent 把可見標籤壓到 ≤19 字元（見 content-s4-s5-s6-footer-copy.md），
   本層不加額外 max-width/overflow 防禦，交給實測驗證。

   rail padding-inline + 出血（D39，docs/spec-s5-carousel-2026-08-19.md
   §2 缺陷 1/2）：上一批「已嘗試但撤回」的結論已被推翻，推翻方式如下——
   當初撤回的真因是 padding-inline 讓 scroll-snap 的 snapport（padding
   box）把首張卡靜止捲走（rail.scrollLeft 靜止值＝24），害 prev 按鈕
   disabled 判準永遠量不到 0。carousel 規格指出正解不是「不用 padding」，
   是**同時**補上 scroll-padding-inline-start 同值——snapport 由
   scroll-padding 定義而非 padding（CSS Scroll Snap 規格），兩者同值後
   靜止 scrollLeft 重新變回 0，prev disabled 判準復活。現在的
   padding-inline-start/-end 同時身兼兩職：(1) 修復上述 snapport 問題，
   (2) 承擔 D39 出血的左緣錨定（= --container-gutter-inline，.container
   公式的代數補數）與右緣末卡留白（= --rail-trailing-inline，取代不可
   token 化的主規格 V10）。DOM 前提：.card-rail 已搬出 .container、成為
   .section 直接子元素，兩者共用同一個 containing block，百分比公式才
   能在 .card-rail 自己身上正確解析（見上方 D39 token 定義註解 + HTML）。
   overflow-x:auto 把子項目的水平溢出關在 rail 內部、不外洩到 document
   （硬約束⑤），不受本項調整影響。reduce 下 site.js 的 rail.scrollBy 改
   'instant'（carousel 規格 R1，見 site.js 對應修正）。
   ================================================================ */

.card-rail {
  display: flex;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  gap: var(--card-row-scroll-gap);
  padding-block: var(--space-3); /* 2026-08-19 uiux drag-to-scroll 規格 §改什麼 B／H12：
     吸收 --motion-transform-enter 的 translateY(12px) 進場位移量與焦點環外擴
     （--focus-ring-width 3px + --focus-ring-offset 2px = 5px），解掉 REBUILD-PLAN 5.1
     稽核 FAIL（焦點環被 overflow 裁切，V9 驗收點）。取 --space-3(12px) 而非
     spec-s5-carousel 原載的 --space-2(8px)：8px 蓋得住焦點環但蓋不住 12px 位移量，
     12px 同時滿足兩者，落在 4px grid 上，零新 token（規格「與既有文件的衝突」#1/#2）。 */
  margin-block: calc(-1 * var(--space-3)); /* 與上行絕對值相等、方向相反（H12），淨版面
     位移為 0——rail.getBoundingClientRect().top 改動前後差 0（V10 驗收點）。 */
  padding-inline-start: var(--container-gutter-inline);
  padding-inline-end: var(--rail-trailing-inline);
  scroll-padding-inline-start: var(--container-gutter-inline);
  /* 2026-08-19 派工單：藏掉原生橫向捲軸外觀，捲動功能（overflow-x:auto）
     不變——滾輪/觸控/軌跡板/鍵盤操作皆不受影響，只是不再畫出捲軸列。
     scrollbar-width 為標準屬性（Firefox 起草案已通過），::-webkit-
     scrollbar 為 WebKit/Blink 專屬偽元素，兩條規則涵蓋主流雙引擎，
     缺一則其中一邊仍會露出捲軸。與 html{scrollbar-gutter:stable}
     （L952，管頁面整體「縱向」捲軸的 gutter 預留）不衝突：規格上
     scrollbar-gutter 只對該元素自身的縱向捲軸生效，.card-rail 本身
     未宣告 scrollbar-gutter，兩者作用軸向不同、消費元素也不同，
     不會在 rail 內部留下空白帶。 */
  scrollbar-width: none;
}

.card-rail::-webkit-scrollbar {
  display: none;
}

/* S5 卡片列滑鼠拖曳捲動（drag-to-scroll，2026-08-19 uiux 規格 §改什麼 C）。H1/H2/H3：
   僅滑鼠可觸發（site.js pointerdown 第一個判斷 pointerType!=='mouse' 早退），觸控/筆
   完全走原生路徑，零 touch-action、零 touchstart/touchmove listener。.is-draggable／
   .is-dragging 由 site.js 依 hasOverflow／8px 拖曳門檻（--rail-drag-threshold）切換，
   前者與既有 hasOverflow 判斷共用同一個布林，見 site.js updateControls()。 */
@media (hover: hover) and (pointer: fine) {
  .card-rail.is-draggable {
    cursor: grab; /* C1 */
  }

  .card-rail.is-dragging {
    cursor: grabbing; /* C2 */
  }

  /* C5：.card__cta::after 是撐滿整卡的 stretched-link 覆蓋層（見上方 S5 區塊頂部大
     註解），<a> 預設 cursor:pointer 會蓋掉整張卡的 grab——不加這行，游標示能只會出現
     在卡與卡之間 24px 縫隙與左右 padding 上，等於沒有。指標當下真正能做的事是拖曳，
     顯示 pointer 才是說謊；CTA 按鈕本身的填色＋箭頭已足以傳達可點（uiux 規格判斷點 2）。
     2026-08-19 quality V5d FAIL 修正：cursor:inherit 原本只下在 ::after，但 ::after
     的直接父層是 .card__cta（<a> 本身），不是 .card-rail——UA 對 <a:any-link> 有
     cursor:pointer，繼承鏈在 .card__cta 這一層就被截斷，::after 繼承到的是 pointer
     不是 grab（8/8 寬度實測皆為 pointer，.card__cta 自身 computed cursor 亦為
     pointer）。修法：把 grab／grabbing 直接下在 .card__cta 自身，::after 的
     cursor:inherit 才有正確的值可繼承——閒置態吃 grab、拖曳中吃 grabbing（is-dragging
     與 is-draggable 拖曳全程同時掛在 rail 上，見 site.js updateControls()／
     pointermove，故不需要另寫一條 .is-dragging 專屬的 ::after 規則）。不可拖曳時兩條
     新規則皆不匹配，.card__cta 落回 UA 預設 pointer，整卡維持原生點擊語意。 */
  .card-rail.is-draggable .card__cta {
    cursor: grab;
  }

  .card-rail.is-dragging .card__cta {
    cursor: grabbing;
  }

  .card-rail.is-draggable .card__cta::after {
    cursor: inherit;
  }
}

/* C3：拖曳中關閉 snap（逐幀 scrollLeft 賦值與 scroll-snap-type:x mandatory 互斥——
   每次賦值後 UA 會重新吸附，手感變成「拖一下彈回去」）並停用文字選取（門檻跨過瞬間
   site.js 一併呼叫 window.getSelection().removeAllRanges()）。H7：user-select:none
   只能出現在這一條規則裡，.card-rail 本體／.card／.card__body 一律不掛，靜止態文字
   選取／全選／尋找／螢幕閱讀器取字皆不受影響。 */
.card-rail.is-dragging {
  scroll-snap-type: none;
  user-select: none;
}

/* C4：拖曳中放開 .card:active 既有的 1px 按壓位移（見上方 .card:active 規則）——拖曳
   手勢途中卡片不該同時出現「被按下」的視覺回饋。 */
.card-rail.is-dragging .card:active {
  transform: none;
}

/* 2026-08-19 撤除卡片 hover 邊框機制（uiux 規格 §3-1 D1–D3，用戶當面
   退稿：「邊框不要」）：border 與其 transition 一併刪除。抖動迴圈風險
   判斷仍成立（scale 縮放 .card 本身會在 hover 時改變卡片視覺寬，指標
   落卡緣造成迴圈），故新 hover 效果只縮放內部 .media__img，不縮放
   .card 本身（見下方新規則）。 */
.card {
  position: relative;
  display: flex;
  flex-direction: column;
  flex: 0 0 auto;
  width: var(--card-row-scroll-width);
  scroll-snap-align: start;
  transition: transform var(--motion-duration-press) var(--motion-easing-standard);
}

/* 2026-08-18 全站微動態規格 P2：hardcode 1px 改 token，理由同
   .primary-nav__list a:active 註解。.card__cta 未另建 :active 規則——
   :active 會沿 DOM 往上冒泡，按在 .card__cta（含其 stretched-link ::after
   覆蓋整卡）上時 .card:active 與 .btn--primary:active 皆同時觸發；已知
   兩者的 translateY 會疊加成 2px（CTA 相對卡片其餘內容多位移 1px）——
   這是本檔既有架構的既有特徵（兩條 active 規則各自早已存在，非本批新增）。
   2026-08-18 追加修正：位移預算硬性規定 active=1px，2px 已違規，補
   `.card .card__cta:active` 抵銷 .card__cta 自身那份疊加（見下方
   .card__cta 區塊），使按 CTA／按卡片其餘區域總位移統一收斂為 1px。 */
.card:active {
  transform: translateY(var(--motion-press-offset));
}

.card:has(.card__cta:focus-visible) {
  outline: var(--focus-ring-width) solid var(--focus-ring-on-light);
  outline-offset: var(--focus-ring-offset);
  border-radius: var(--media-radius);
}

/* 鍵盤與滑鼠拿到同一組視覺回饋（沿用 .news-row 既有裁決）：focus-visible
   時 media 放大與 CTA 變色比照 hover，另外多一圈上方的 outline ring。不
   包在 @media (hover: hover) 內——鍵盤操作不受 hover 能力限制（uiux 規格
   §4-2，2026-08-19）。 */
.card:has(.card__cta:focus-visible) .media__img {
  transform: scale(var(--media-scale-hover));
}

.card:has(.card__cta:focus-visible) .card__cta {
  background-color: var(--btn-primary-bg-hover);
}

.card > .media {
  margin-block-end: var(--card-media-space-after);
}

/* 卡片圖 hover 放大過場宣告（uiux 規格 §3-2 A1，2026-08-19）。hover 觸發
   的縮放值在下方 @media (hover: hover) 區塊；此處只宣告 transition，讓
   focus-visible（不受 hover:hover 限制，見下方 .card:has() 配套規則）與
   hover 共用同一過場時長。 */
.card .media__img {
  transition: transform var(--motion-duration-hover) var(--motion-easing-standard);
}

/* D38（docs/spec-s5-redesign.md §4 + spec-s5-carousel-2026-08-19.md）：
   原「S5 服務卡左下 scrim」規則（2026-08-18 追加授權，鎖定
   `.card > .media` 左下 45%×40%）已隨數字浮水印從左下實心搬到右下空心
   描邊而整條移除——舊機制解決的是舊位置在 service-3/4/5 上的對比問題，
   新位置／新視覺（描邊而非實心填色）是完全不同的對比機制（見規格 §3
   sRGB 門檻與 3:1 反解），留著只會在畫面左下角憑空壓暗一塊、對現在
   的內容沒有作用。新逃生口版本見 .media--ordinal-scrim（定義於
   .media--1x1 旁，預設不掛）。

   巨型浮水印數字（D38，docs/spec-s5-redesign.md §4：空心描邊、右下、
   Albert Sans 700、取消 tnum，取代原 A-5 D24 的 Inter 700 + tnum 實心
   方案，D24 已於 spec §9 撤回）。純裝飾，序號語意由 DOM 順序承擔，
   aria-hidden 由 HTML 負責；可見性地板由描邊 alpha 對 §3 素材亮度門檻
   的 3:1 反解把關（規格 §3 已附公式），本層只負責定位與字級。 */
.card__index {
  position: absolute;
  inset-inline-start: var(--card-ordinal-inset-inline-start);
  inset-block-end: var(--card-ordinal-inset-block-end);
  font-family: var(--font-family-body);
  font-size: var(--card-ordinal-size);
  font-weight: 700;
  font-variant-numeric: normal;
  line-height: 1;
  letter-spacing: 0;
  color: var(--card-ordinal-fallback-fill);
  pointer-events: none;
}

/* -webkit-text-stroke 非標準屬性但 Chrome/Safari/Edge/Firefox 皆已實作
   （規格 §4「跨瀏覽器技法與 fallback」逐字）。真正風險不是描邊沒畫出來，
   是 color:transparent 在不支援環境下讓數字整個消失——故用 @supports
   偵測支援後才切換到 transparent + 描邊，不支援時維持上面的實心
   fallback 分支，兩分支互斥、零重疊風險。 */
@supports (-webkit-text-stroke: 1px solid) {
  .card__index {
    color: transparent;
    -webkit-text-stroke-width: var(--card-ordinal-stroke-width);
    -webkit-text-stroke-color: var(--card-ordinal-stroke);
  }
}

/* h4 階，復用既有 --type-role-card-title-*（同 .news-row__title 手法，
   見 S4 區塊層註解——該 token 本身就是 h4 尺度，不新發明角色 token）。 */
.card__title {
  font-family: var(--type-role-card-title-family);
  font-size: var(--type-role-card-title-size);
  line-height: var(--type-role-card-title-line-height);
  letter-spacing: var(--type-role-card-title-letter-spacing);
  font-weight: var(--type-role-card-title-weight);
  color: var(--text-primary);
  margin-block-end: var(--card-stack-gap);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.card__body {
  font-family: var(--type-role-paragraph-family);
  font-size: var(--type-role-paragraph-size);
  line-height: var(--type-role-paragraph-line-height);
  letter-spacing: var(--type-role-paragraph-letter-spacing);
  font-weight: var(--type-role-paragraph-weight);
  color: var(--text-body);
  margin-block-end: var(--card-stack-gap);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.card__cta {
  margin-block-start: auto;
}

/* 抵銷 .btn--primary:active 疊加在 .card__cta 自身的 translateY——見上方
   .card:active 註解。.card__cta 同時是 .btn--primary（吃
   translateY(var(--motion-press-offset))）又是 .card 的後代（.card:active
   也吃同一個 token 的位移），兩層各自的 transform 對同一顆按鈕元素疊加成
   2px，唯獨 CTA 比卡片其餘內容多動一倍。用 `.card .card__cta:active`
   （3 個 class，specificity (0,3,0) 高於 .btn--primary:active 的 (0,2,0)，
   不依賴 source order）把 .card__cta 自身這份歸零，只留 .card 父層那 1px，
   使 CTA 與卡片其餘內容的總位移一致收斂為規格硬性規定的 1px。 */
.card .card__cta:active {
  transform: translateY(0);
}

/* stretched link：見本區塊頂部大註解 */
.card__cta::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 1;
}

.card .card__cta:focus-visible {
  outline: none; /* 替代：ring 轉移到 .card:has() 規則，非違反硬約束③無替代裸關 outline */
}

.icon-btn--prev .icon-btn__icon {
  transform: rotate(180deg);
}

/* iOS Safari pitfalls #8：hover 專屬樣式收在 hover-capable 裝置內
   （沿用本檔既有慣例，見 .news-row:hover 等處）。 */
@media (hover: hover) and (pointer: fine) {
  /* 卡片圖 hover 放大（uiux 規格 §3-2 A1，2026-08-19；用戶當面裁決：
     「圖片放大 cta 變色 就好」）。scale 作用在 .media__img 上、不在 .card
     上——.media 已有 overflow:hidden，縮放被裁在圖框內，卡片 layout
     寬高逐 px 不變。這與 2026-08-18 撤銷的 scale(1.03) 不是同一件事：
     那次撤銷的是縮放 .card 本身（改變卡片視覺寬 → 指標落在卡緣產生
     hover 抖動迴圈，見上方 .card 規則註解）。本規則零 layout 影響，
     .card-rail 的 overflow-x:auto 捲動容器不受任何影響。 */
  .card:hover .media__img {
    transform: scale(var(--media-scale-hover));
  }

  /* 2026-08-18 全站微動態規格 P1+P9：.card__cta.btn--with-icon hover——
     P1（bg 色變）已由既有 .btn--primary:hover 繼承涵蓋，不重複宣告；此處
     只新增 P9 icon 前推，共用單位是 --space-1 + --motion-duration-hover
     兩個 token（Unit 3 元件模組化裁決：父選擇器不同不抽 class，見
     .btn__icon 上方註解），選擇器精準對應規格命名 .btn--with-icon，避免
     波及其他未來可能使用 .btn__icon 但不想要此效果的按鈕。 */
  .btn--with-icon:hover .btn__icon {
    transform: translateX(var(--space-1));
  }
}


/* ══════════════════════════════════════════════════════════════
   S6 BLOCK LAYER — 區塊層（規格 §B「S6 — In numbers」；.stat-grid/.stat
   命名空間，B5 派工單硬約束⑦：只寫 .stat-grid/.stat，不寫 atom 層／
   .site-footer__*／.giant-wordmark）
   施工依據：docs/spec-s2-s6-components.md §B S6 段、§A-2 C11/C12 詳規、
   §A-4 內容韌性 .stat 列、§A-5 stat/hairline token、驗收點 1/2/8/13/16

   S6 是第二個深色表面（navy #00173E，經 .section--navy 消費
   --surface-inverse），與 S2 的 .section--dark（黑，--surface-inverse-
   deep）是兩個不同 token——本批不新增 surface，只消費既有 atom。

   欄間分隔線實作說明：規格 §A-2 C12 允許「column-gap ＋ 偽元素」或
   「border-inline-start」兩種寫法。本批選偽元素置中於 column-gap 內，
   理由：`.stat-grid` 的 `grid-template-columns: repeat(N,1fr)` 是硬性
   欄數公式（規格 §B 逐字）。若改用真實 `.rule--v` DOM 元素插入 4 個
   `.stat` 之間，grid 子項會變成 7 個，破壞「N 欄」的精確等分——偽元素
   不佔 grid 軌位，是唯一能同時滿足「repeat(N,1fr) 精確欄數」與「欄間
   有分隔線」兩條要求的寫法。偽元素的 background-color 引用同一個
   --border-hairline-on-inverse token，與 `.rule--v.rule--on-inverse`
   的最終 computedStyle 顏色值相同，只有「用什麼機制畫線」不同，符合
   交接契約 4「邊界」（frontend 可自行決定寫法，只要 computedStyle 的
   值等於規格值）。此手法與 S4 用 `border-block-start` 直接畫列間分隔線
   （同樣不透過 `.rule` DOM 元素）是同一個既有先例，非本批新發明。
   `.rule--h.rule--on-inverse`（section-head 與 stat-grid 之間原本的單一
   分隔線，獨立 DOM 節點，非逐項重複的分隔線）**2026-08-19 已依用戶當面
   指定移除**——實測（Playwright getBoundingClientRect，1440/1920 兩寬度）
   確認桌機下這是唯一橫跨整列、位於 4 個數字正上方的水平線，用戶所稱
   「數字上面的 1px 線」實指此線，非本檔 .stat::before 的欄間垂直分隔線
   （後者已實測確認 height≫width，維持不動）。移除做法是刪除 index.html
   內該 `.rule` DOM 元素本身，未動 `.rule`/`.rule--h`/`.rule--on-inverse`
   共用 class（footer 另有一處 `.rule--h`，見 index.html:579，未受影響）。

   斷點行為（原規格 §B S6「tablet 2×2 時垂直分隔線改 grid 分隔；mobile
   單欄時分隔線改水平」，token 層 --card-row-grid-columns 已於既有
   tablet/mobile media block 覆寫為 2／1。2026-08-19 用戶當面指定拿掉
   每個數字上方的 1px 橫線，下列 tablet/mobile 兩項的水平分隔線已移除
   ——DESIGN.md §rwd_behavior.tablet「In-numbers 2×2；欄間 hairline 改為
   grid 分隔線」一行現與實作不符，需回寫，未逕自改動，已列入交接報告）：
   - base/wide（4 欄 1 列）：僅垂直分隔線，第一欄不畫（3 條）——不受本次
     異動影響
   - tablet（2 欄 2 列）：垂直分隔線只留每列第 2 格（偶數項 nth-child(2)/
     (4)）；第 3 項（奇數、第 2 列第 1 格）無分隔線，原本補在此處的水平
     分隔線已移除
   - mobile（1 欄 4 列）：垂直分隔線全部關閉（`content:none` 明確覆寫，
     見下方註解——specificity 需與 base 規則相等且晚於 tablet 規則才能
     正確覆寫，不可只降 display），原本補在項與項之間的水平分隔線已移除，
     現在僅靠 --card-row-grid-gap 留白，無視覺分隔線
   ══════════════════════════════════════════════════════════════ */

/* ── 2026-08-19 uiux S6 reshoot pass（依 Figma 參考圖重做，見任務報告）──
   本次異動：C1 對齊 flex-start→center（≤768 仍 flex-start，同 D22 先例）；
   C2 數字色 --text-on-inverse→--stat-value-fg(accent.200)；C3 字級公式
   clamp(48px,min(14vw,30cqw),72px)→clamp(48px,30cqw,72px)（移除已不需要
   的 14vw 安全網，2026-08-18 QA 修正遺留，純 cqw 已驗證全斷點不溢出）；
   C4 padding-block 32→12px；C5 label 新增 align-self:stretch +
   min-block-size 鎖兩行；C6 label→badge 新增 16px 間距（見 .stat__label+
   .badge 規則）；C7 分隔線選擇器 :not(:first-child)→:not(:nth-child(4n+1))
   一般化（N=4 渲染結果逐 px 不變）；.stat-grid 新增 row-gap 響應式覆寫
   （tablet/mobile 40px，見下方 @media(max-width:1023px)）。C8/C9 無 CSS
   異動（零水平線已成立；badge 版位隨 C1 自動生效）。 ── */

.stat-grid {
  display: grid;
  grid-template-columns: repeat(var(--card-row-grid-columns), 1fr);
  column-gap: var(--card-row-grid-gap);
  row-gap: var(--card-row-grid-gap); /* base/wide 單列，此值無視覺作用；
     tablet/mobile 於 @media(max-width:1023px) 覆寫為 --space-7(40px) */
}

/* 防 grid item（nowrap 數字）撐爆軌道寬度——同 .split > * / .news > *
   既有先例（見該兩處註解），驗收點 13「6 字元數字 scrollWidth≤
   clientWidth（291px 欄）」要求欄寬確實鎖在 1fr，不能被內容撐寬。 */
.stat-grid > * {
  min-width: 0;
}

.stat {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center; /* 2026-08-19 uiux S6 reshoot C1／裁決A方向1：三條
     可見欄界線授權內容置中於被宣告的盒子裡（非浮動）。取代舊值
     flex-start——兩者都不是 stretch，沿用「防 flex column 預設 stretch
     把 .badge 撐成滿欄寬矩形」的原防護意圖不變，只是水平對齊方式從左
     改置中。≤768 單欄無欄界時置中授權消失，由下方
     @media (max-width:768px) 覆寫回 flex-start + text-align:start
     （同 D22 先例：唯一對齊錨不可被無依據的對齊打斷）。 */
  text-align: center; /* 配合上一行；.stat__label 另有 align-self:stretch
     （見該規則），故置中對其兩行文案在完整欄寬內生效 */
  gap: var(--stat-value-space-after);
  padding-block: var(--stat-padding-block);
  /* 2026-08-18 QA 修正配套：讓 .stat__value 的 cqw 字級能查詢「這一欄
     實際多寬」。安全：.stat 是 grid item 且 .stat-grid > * 已有
     min-width:0（見上方），寬度本來就是 1fr 軌道外部決定、不受自身內容
     影響，滿足 container-type:inline-size 要求「自身 inline 尺寸不依賴
     內容」的前提，不會造成量測循環。.stat 已是 position:relative（本規則
     上方即有），已是內部 ::before/::after 分隔線偽元素的 containing
     block，加這個屬性不改變那組定位。 */
  container-type: inline-size;
}

/* base/wide（4 欄 1 列）：欄間垂直分隔線，第一欄不畫，置中於
   column-gap 內（-half gap - half line width）。 */
.stat:not(:nth-child(4n+1))::before { /* 2026-08-19 C7：一般化自
   :not(:first-child)，N=4 時逐 px 相同（第一欄不畫），N≠4 起才顯出
   差異，見任務報告 C7 理由 */
  content: '';
  position: absolute;
  inset-block: 0;
  inset-inline-start: calc(var(--card-row-grid-gap) / -2 - var(--border-width-hairline) / 2);
  width: var(--border-width-hairline);
  background-color: var(--border-hairline-on-inverse);
}

@media (max-width: 1023px) {
  /* 第 3 項（奇數、非第 1 欄）在 2 欄版面是第 2 列第 1 格，不該有垂直
     分隔線——用 content:none 關閉上面 base 規則產生的偽元素。選擇器
     specificity 與 base 規則相等（.stat + :nth-child() + ::before，
     皆為 (0,2,1)），本區塊在檔案中晚於 base 規則，故對第 3 項生效；
     第 2/4 項（偶數）不受此規則影響，維持 base 規則的垂直分隔線。 */
  .stat:nth-child(odd)::before {
    content: none;
  }

  /* 2026-08-19 C7／RWD 表：row-gap 加大到 --space-7(40px)，讓 2×2 兩列
     讀成兩段獨立分組而非斷掉的一條線；column-gap 仍是 --card-row-grid-gap
     (12px) 不變。此覆寫同時涵蓋 mobile（≤768 為本條件子集，值相同，
     mobile 區塊不需再重複宣告）。 */
  .stat-grid {
    row-gap: var(--space-7);
  }
}

@media (max-width: 768px) {
  /* 單欄堆疊，垂直分隔線全部關閉。選擇器故意與 base 規則的
     `:not(:nth-child(4n+1))::before`（2026-08-19 C7 一般化，原
     `:not(:first-child)`）完全一致（specificity 相等、本區塊
     source order 更晚），才能正確覆寫第 2/4 項（tablet 區塊的
     `:nth-child(odd)` 選擇器不涵蓋偶數項，若這裡沿用會漏關）。 */
  .stat:not(:nth-child(4n+1))::before {
    content: none;
  }

  /* 2026-08-19 C1／裁決A配套：≤768 單欄無欄界線可視為容器，置中授權
     消失，強制回左對齊（同 D22 先例）。 */
  .stat {
    align-items: flex-start;
    text-align: start;
  }
}

/* Inter 700 tnum，白字，17.6:1（規格 §B S6）。nowrap＋內容上限 6 字元
   由 content agent 文案把關（實際四值 18/4800+/9/24-7 皆 ≤5 字元），本
   層不加防禦性 max-width——依賴 .stat-grid > * 的 min-width:0 讓欄寬
   誠實反映 1fr，溢出與否交給驗收點 13 實測，不用彈性寬度掩蓋真實結果。 */
.stat__value {
  font-family: var(--type-role-stat-number-family);
  font-size: var(--type-role-stat-number-size);
  line-height: var(--type-role-stat-number-line-height);
  letter-spacing: var(--type-role-stat-number-letter-spacing);
  font-weight: var(--type-role-stat-number-weight);
  font-feature-settings: var(--type-role-stat-number-font-feature-settings);
  color: var(--stat-value-fg); /* 2026-08-19 uiux S6 reshoot C2：取代
     --text-on-inverse(白)，參考圖數字為淺藍。對 #00173E 實算 13.55:1 */
  white-space: nowrap;
}

/* --text-secondary-on-inverse 表面鎖定：只准用在 .section--dark/
   .section--navy 內（對白底僅 1.42:1），S6 是 .section--navy，合法。
   clamp 2 行（規格 §A-4 內容韌性 .stat 列）。 */
.stat__label {
  font-family: var(--type-role-meta-family);
  font-size: var(--type-role-meta-size);
  line-height: var(--type-role-meta-line-height);
  letter-spacing: var(--type-role-meta-letter-spacing);
  font-weight: var(--type-role-meta-weight);
  color: var(--text-secondary-on-inverse);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  align-self: stretch; /* 2026-08-19 uiux S6 reshoot C5：撐滿欄寬，確保
     置中文案相對整欄置中而非相對自身內容寬度置中 */
  min-block-size: var(--stat-label-min-block-size); /* C5：鎖兩行高
     （44px@base），四欄 badge 底緣恆對齊，不隨行數錯開 */
}

/* 2026-08-19 uiux S6 reshoot C6：label→badge 16px（value→label 仍是
   .stat 的 flex gap 12px）。用相鄰兄弟選擇器疊加差額 4px，只在 .badge
   緊接 .stat__label 之後才生效——若日後裁決 5 移除 badge DOM，此選擇器
   天然不匹配、自動失效無害，見 uiux 規格 §11 裁決 B。 */
.stat__label + .badge {
  margin-block-start: calc(var(--stat-label-space-after) - var(--stat-value-space-after));
}


/* ══════════════════════════════════════════════════════════════
   FOOTER BLOCK LAYER — 區塊層（規格 §B「Footer」；.site-footer/
   .giant-wordmark 命名空間，B6 派工單硬約束：只寫 .site-footer/
   .giant-wordmark，不寫 atom 層。`.nav-overlay__eyebrow` 合併是本批
   對既有 header 相關規則的唯一例外編輯，已就地完成，見該處註解）
   施工依據：docs/spec-s2-s6-components.md §B Footer 段、§A-2 C4/C6/
   C12/C13/C14 詳規、交接契約 5 驗收點 4/5/7/8/14/15/16

   結構：<footer class="site-footer section section--page"> 沿用既有
   atom（.section 給 flush radius；.section--page 給白底），本規則只
   額外覆寫 padding-block 為 --footer-inset-block（80px，figma-exact，
   非一般 section 的 60px 節奏）——與 .section 同 specificity（皆單一
   class 選擇器），本規則 source order 較晚，正確覆寫。

   水平間距的自主決定（已知偏離 spec 字面寫法，回報中已完整揭露）：
   規格 §B 字面寫「padding-inline: var(--footer-inset-x)」（120px，
   D3）。本批改用 .container（既有 --inset-x 響應式公式）取代直接
   套用 --footer-inset-x——原因：--footer-inset-x 在 tablet/mobile
   兩個既有 media block 內都沒有被覆寫，維持字面 120px 不變；若真的
   直接套用，375px 視口會被吃掉 120×2=240px，只剩 135px 可用寬度，
   三欄 grid（含最窄需求 320+120+190+80×2=790px）物理上塞不下，會在
   驗收點 4（六寬度溢出）直接 FAIL。改用 .container 後：base/wide
   算出的 padding 與 --footer-inset-x 完全相同（--inset-x 在這兩個
   模式同為 120px，逐 px 相同，非「相近」），且 tablet/mobile 自動
   沿用與 S2–S6 一致的響應式左緣——這正好貼合 DESIGN.md
   `layout.footer_inset` 自己的 rule 註記「x 收斂到 120 與全站左緣
   一致」的字面意圖，也讓驗收點 5（跨區左緣對齊）在所有斷點而非僅
   base/wide 成立。--footer-inset-x token 本身保留在 :root（未刪除，
   不在本批授權範圍），本批只是未消費，非缺值。

   2026-08-20 追記（現況已偏離本段等式）：--inset-x 現值為 --space-9
   (60px)，非本段所述的 120px（沿革見上方 :root --space-11 定義旁註：
   「容器左緣語意已於 2026-08-19 移交 --inset-x」）。本段「base/wide
   算出的 padding 與 --footer-inset-x 完全相同（--inset-x 同為 120px）」
   的等式現已不成立（60px≠120px）。此為忠實記錄現況的事實更正，不代表
   對此落差是否為預期行為的設計裁決——是否需要調整留待複核，本次僅
   修訂註解不動 CSS。 ══════════════════════════════════════════════════════════════ */

.site-footer {
  padding-block: var(--footer-inset-block);
}

/* iOS Safari pitfalls #8/#9 一致性：footer 的 .logo 連結不在既有
   header 手列 focus-visible／tap-highlight 選擇器清單內（那兩條規則
   明確只列 .site-header/.nav-overlay，且屬於「header 全部完成」不得
   碰的範圍）。同 .news-row/.card 已建立的先例（各自的區塊層另外補一
   條 focus-visible scoped 規則，涵蓋 atom 共用規則沒蓋到的元素），
   這裡補一條只在 .site-footer 命名空間內生效的規則，值與既有共用
   focus-visible 規則完全相同（3px ring + 2px offset + on-light 色），
   不是新發明的視覺樣式。 */
.site-footer a {
  -webkit-tap-highlight-color: transparent;
}

.site-footer .logo:focus-visible {
  outline: var(--focus-ring-width) solid var(--focus-ring-on-light);
  outline-offset: var(--focus-ring-offset);
  border-radius: var(--radius-alias-control);
}

.site-footer__top {
  display: grid;
  grid-template-columns: var(--footer-columns);
  gap: var(--footer-columns-gap);
  margin-block-end: var(--space-10);
}

/* 防 grid item（尤其 SERVICES 欄最長字串 Observability & Monitoring）
   撐爆軌道寬度——同 .split>* / .news>* / .stat-grid>* 既有先例。 */
.site-footer__top > * {
  min-width: 0;
}

.site-footer__brand {
  display: flex;
  flex-direction: column;
  gap: var(--space-7);
}

/* .prose 是通用 atom，預設色 --text-body（#313131）。DESIGN.md
   `component.footer.statement` 明定此處 fg 為 semantic_alias.
   text_secondary（#616161），與 .prose 預設色不同——本規則在 B6 自己
   的命名空間內疊加既有 --text-secondary token 覆寫，不改 .prose 本身
   （同 .prose--on-inverse「疊加不改寫」的既有精神）。max-width 384px
   為規格 §B 逐字給定值。 */
.site-footer__statement {
  max-width: var(--footer-statement-max-width);
  color: var(--text-secondary);
}

/* eyebrow → list 間距：16px，與合併後 .nav-overlay__services
   .eyebrow--label 的 margin-bottom 同一數值（--section-head-gap 與
   --space-4 皆為 16px），兩處視覺節奏一致，非各自發明。 */
.site-footer__column {
  display: flex;
  flex-direction: column;
  gap: var(--section-head-gap);
}

/* ── `.giant-marquee`（footer 巨型跑馬燈，取代退場的 `.giant-wordmark`）。
   規格依據：docs/spec-footer-marquee.md（原檔 2026-08-18）＋
   docs/active/spec-footer-marquee-revision-2026-08-19.md（今日修訂，
   D41 滿版 / D42 __scrub 捲動耦合 / D43 reduced-motion 安全網補丁 /
   D44 copies 4→5）。

   本批再疊一層修訂：用戶第三次原話「上面放一些適合的標語」（複數）
   推翻 D42-D44 賴以成立的「單句重複 5 份」前提；改為 8 句標語
   （docs/footer-marquee-slogans.md）串接重複 3 份。copies=3／
   duration=98s·144s 是本批依同一公式重算的值，非規格原文的 5／
   14s·20s——推導見交接報告，非我杜撰。

   舊 `.giant-wordmark` 的 overflow-x:hidden + line-height:1 是它出現
   垂直捲軸的根因（overflow-x:hidden 使 overflow-y computed 為 auto，
   line-height:1 使字身 1.25em 溢出 1.0em 行框）——新結構把 overflow
   收斂到 __viewport 一個點（clip，不是 hidden），line-height 改用
   token 1.15，兩條根因一次消滅，`.giant-wordmark` class 整個退場。 ── */
.giant-marquee {
  position: relative;
  margin-block-end: var(--space-10);
}

.giant-marquee__viewport {
  overflow: clip;
  contain: paint;
  width: 100%;
  mask-image: linear-gradient(
    to right,
    transparent,
    black var(--giant-marquee-fade),
    black calc(100% - var(--giant-marquee-fade)),
    transparent
  );
  -webkit-mask-image: linear-gradient(
    to right,
    transparent,
    black var(--giant-marquee-fade),
    black calc(100% - var(--giant-marquee-fade)),
    transparent
  );
}

/* D42：捲動耦合層。animation-timeline:scroll(root block) 讓額外位移由
   捲動位置驅動，與下方 __track 的恆定漂移分屬兩個元素，在合成階段
   相加（同一元素上兩個 animation 會互相覆蓋，不會相加，這是必須分層
   的原因）。不支援 scroll-driven animation 的引擎會忽略
   animation-timeline，animation-duration 退回預設 0s，__scrub 保持
   translateX(0)——零成本的漸進增強，非破版風險。 */
.giant-marquee__scrub {
  animation-name: marquee-scrub;
  animation-timeline: scroll(root block);
  animation-timing-function: var(--motion-easing-linear);
  animation-fill-mode: none;
  will-change: transform;
}

.giant-marquee__track {
  display: flex;
  width: max-content;
  animation-name: marquee-drift;
  animation-duration: var(--giant-marquee-duration);
  animation-timing-function: var(--motion-easing-linear);
  animation-iteration-count: infinite;
  animation-fill-mode: none;
  will-change: transform;
  user-select: none;
}

.giant-marquee__copy {
  flex-shrink: 0;
  padding-inline-end: var(--giant-marquee-gap);
  font-family: var(--font-family-heading);
  font-size: var(--giant-wordmark-size);
  font-weight: var(--font-weight-regular);
  line-height: var(--giant-wordmark-line-height);
  letter-spacing: var(--giant-wordmark-letter-spacing);
  text-transform: uppercase;
  color: var(--giant-wordmark-color);
  white-space: nowrap;
}

/* 兩個 keyframes 共用同一個 --giant-marquee-step 終值（規格硬約束：
   份數與位移量單一來源，是循環封閉性的唯一保證，不得各寫各的）。 */
@keyframes marquee-drift {
  to {
    transform: translateX(var(--giant-marquee-step));
  }
}

@keyframes marquee-scrub {
  to {
    transform: translateX(var(--giant-marquee-step));
  }
}

/* D43：reduced-motion 下 __scrub 的處理已補在上方全域安全網
   （@media (prefers-reduced-motion: reduce) 區塊新增的
   animation-timeline: auto !important），元件層不寫
   @media (prefers-reduced-motion) ——規格硬約束 2。 */

.site-footer__bottom {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: center;
  gap: var(--space-3);
  padding-block: var(--space-5);
}

/* 自主決定（回報中已揭露）：copyright 全文 60 字元，在 mobile 窄容器
   下與右側 legal placeholder 同列會擠壓/斷行不可預期，改單欄堆疊避免
   flex-shrink 把句子攔腰截斷。沿用既有 768px 斷點，非新增斷點。 */
@media (max-width: 768px) {
  .site-footer__bottom {
    flex-direction: column;
    align-items: flex-start;
  }
}

/* 版權行與 .empty-state 視覺同型（meta 階／--text-secondary）但語意
   不同——版權行是真實內容，.empty-state 語意上專指「缺項佔位」，
   混用會誤導未來維護者。故只共用 token、不共用 class（同 .prose /
   .prose--on-inverse 分離色彩層而非分離規則的既有精神）。 */
.site-footer__copyright {
  font-family: var(--type-role-meta-family);
  font-size: var(--type-role-meta-size);
  line-height: var(--type-role-meta-line-height);
  letter-spacing: var(--type-role-meta-letter-spacing);
  font-weight: var(--type-role-meta-weight);
  color: var(--text-secondary);
}

/* ≤1023：兩欄，敘述獨占一列、EXPLORE/SERVICES 並排（規格 §B footer
   逐字）。--footer-columns 本身無 tablet/mobile token 覆寫（不在本批
   可改的 token 層範圍內），響應式版面改由本層 literal grid-template-
   columns 承擔——同 .news-row ≤768 grid-template-areas 的既定處理
   方式（區塊層自己的響應式，非 token 驅動），非本批新發明的模式。 */
@media (max-width: 1023px) {
  .site-footer__top {
    grid-template-columns: 1fr 1fr;
  }

  .site-footer__brand {
    grid-column: 1 / -1;
  }
}

/* ≤768：全單欄堆疊（規格 §B footer 逐字）。--giant-wordmark-size 的
   mobile 降級值已在既有 :root media block 內定義（B1 建立），本層
   不重複宣告。 */
@media (max-width: 768px) {
  .site-footer__top {
    grid-template-columns: 1fr;
  }
}


/* ── S4「看全部新聞」按鈕（2026-08-20 daniel 指定新增）──────────────
   .section-head 是 flex column，按鈕當第三個子元素自然落在標題下方。
   align-self: start 讓它只佔自己的寬度，不被拉滿整欄。
   ⚠️ 不要改用 .section-head--inline-actions：.news 是左右兩欄版型，
      標題在左窄欄，inline-actions 會讓按鈕擠在 NEWS 旁邊。 */
.news__all {
  align-self: start;
  margin-block-start: var(--space-5);
}
