/* ============================================================================
 * St. John 二期 e-System — 後台設計系統（單一來源）
 * ============================================================================
 *
 * 【這份檔案的地位】
 * 五個系統（E-Registration／OMS Extension／Lecturer／POS／E-Learning）的**唯一**樣式來源。
 * 權威版在 `00Sys Dev/05-設計系統/_canonical/`，以 `_ds_sync.py` 同步到各系統。
 * ⚠ 任何系統都不得就地改這份檔案 —— 會被同步覆蓋，且改動傳不出去。
 *
 * 【設計依據：三層規範，缺一不可】
 *
 *   第一層｜WCAG 2.2 A／AA —— 硬性門檻，不通過即缺陷
 *     1.4.3  對比最低（文字 4.5:1／大字 3:1）
 *     1.4.10 回流（320px 寬不出現橫向捲動）
 *     1.4.11 非文字對比（元件邊界／圖示／狀態色 3:1）
 *     1.4.12 文字間距（放大行距字距詞距仍不裁切）
 *     1.4.13 懸停或焦點內容（可關閉／可懸停／可持續）
 *     2.4.7  焦點可見（focus-visible 一律 2px 外框 ＋ offset）
 *     2.4.11 焦點不被遮蔽（見 §0.4 的 scroll-margin-top）
 *     2.5.7  拖曳動作（拖曳一律附非拖曳替代操作）
 *     2.5.8  目標尺寸最小 24×24 CSS px（見 --target-min）
 *     3.3.7  重複輸入（表單一律帶 autocomplete）
 *     3.3.8  無障礙驗證（密碼欄不得阻擋貼上）
 *     4.1.3  狀態訊息（aria-live，見 §4.11）
 *
 *   第二層｜WAI-ARIA APG —— 每個元件的鍵盤與輔助技術行為
 *     每個元件區塊上方有一張表：APG pattern ＋ 鍵盤 ＋ ARIA。
 *     樣式必須服務那張表，不是反過來。
 *
 *   第三層｜設計系統慣例（MD3／Ant Design Pro／Carbon／Atlassian／Polaris／Fluent）
 *     外殼  固定側欄 ＋ 固定頂欄 ＋ 內容區為唯一捲動容器（六家一致）
 *     間距  4px 基準網格；元件內 8／12，區塊間 16／24
 *     層級  以邊框為主、陰影為輔（Carbon／Atlassian）—— 避免「到處浮起來」
 *     密度  三檔（緊湊 28／舒適 36／寬鬆 44），全站一個設定
 *     形狀  圓角 6–8px（非膠囊）；輸入與按鈕同高對齊
 *     狀態  一致四態（載入／空／錯／有資料）
 *
 * 【七層架構】改東西請改對應層，不要跨層硬幹
 *   §0 Reset 與基礎   §1 原生 token   §2 語意 token   §3 佈局原語
 *   §4 元件           §5 複合模式     §6 工具與 a11y
 *
 * 【禁止事項（規範明文；_ds_audit.py 會抓）】
 *   ✗ 視圖內寫 <style> 區塊      → 在 §4 補元件，或改用既有元件
 *   ✗ 自創 class 並定義在頁面內   → 同上（本檔案是唯一定義處）
 *   ✗ 用 inline style 改版面      → 用佈局原語（.stack／.cluster／.grid）
 *   ✗ 直接寫 hex 色值             → 只用 §2 的語意 token
 *   ✗ 用顏色單獨傳達狀態          → 必須同時有文字或圖示（WCAG 1.4.1）
 * ==========================================================================*/


/* ============================================================================
 * §0 Reset 與基礎
 * ==========================================================================*/

*,
*::before,
*::after { box-sizing: border-box; }

* { margin: 0; }

html {
  -webkit-text-size-adjust: 100%;   /* 1.4.4：橫向時不要自動放大字級 */
  text-size-adjust: 100%;
  /* 1.4.12 文字間距：行高基準由 token 控制，放大時不裁切 */
  line-height: var(--leading-normal);
}

body {
  font-family: var(--font-sans);
  font-size: var(--t-body);
  line-height: var(--leading-normal);
  color: var(--text-1);
  background: var(--surface-canvas);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  /* 3.3.8：不要用 text-security 或阻擋貼上；破折號自動斷行讓長字串不撐破版面 */
  overflow-wrap: break-word;
}

img, svg, video, canvas { display: block; max-width: 100%; }
img, video { height: auto; }

button, input, select, optgroup, textarea {
  font: inherit;
  font-feature-settings: inherit;
  letter-spacing: inherit;
  color: inherit;
}

button { background: none; border: 0; padding: 0; cursor: pointer; }
button:disabled { cursor: not-allowed; }

table { border-collapse: collapse; border-spacing: 0; text-indent: 0; }

ol, ul, menu { list-style: none; padding: 0; }

summary { cursor: pointer; }
summary::-webkit-details-marker { display: none; }

a { color: inherit; text-decoration: inherit; }

/* ── 0.1 標題階層 ──────────────────────────────────────────────────────────
   1.3.1 資訊與關聯：標題只用於結構，不用於「把字變大」。
   字級由 .t-* 工具類控制，標題的預設字級刻意接近正文，避免誤用。 */
h1, h2, h3, h4, h5, h6 { font-size: inherit; font-weight: inherit; }

/* ── 0.2 焦點指示（2.4.7 Focus Visible / 1.4.11 Non-text Contrast）────────
   ⚠ 只在 :focus-visible 畫外框：滑鼠點擊不畫（否則整頁都在閃），
     鍵盤 Tab 一定畫。這是六家設計系統一致的門檻做法。 */
:focus { outline: none; }

:focus-visible {
  outline: var(--focus-w) solid var(--focus-ring);
  outline-offset: var(--focus-offset);
  border-radius: var(--r1);
}

/* 焦點落在容器（如 <main>）時不外框：它只是「跳過導覽」的落點，不是可操作元素 */
[tabindex="-1"]:focus-visible { outline: none; }

/* 深色底（側欄）上的焦點改用品牌亮黃：深階琥珀對深底雖仍達標（4.72:1），
   但亮黃在深底上更醒目（10.56:1），且符合品牌辨識。
   ⚠ 不可反過來在淺底用亮黃 —— 對白只有 1.71:1，不合格。 */
.nav :focus-visible { outline-color: var(--focus-ring-on-dark); }

/* ── 0.3 減少動態（2.3.3 / 使用者偏好）───────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ── 0.4 焦點不被遮蔽（2.4.11 Focus Not Obscured，WCAG 2.2 新增）──────────
   sticky 頂欄會蓋住「捲動到該處的聚焦元素」。全域給一個上緣留白，
   任何元素被 Tab／錨點帶入時，都會停在頂欄之下，不會被遮住。
   高度取「頂欄 ＋ 標籤頁列 ＋ 8px 呼吸」，見 §1 的 --sticky-offset。 */
:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  scroll-margin-top: var(--sticky-offset);
}

:target { scroll-margin-top: var(--sticky-offset); }

/* ── 0.5 強制色彩模式（Windows 高對比）───────────────────────────────────
   自繪的框線／底色在 forced-colors 下會被系統覆蓋，需補回必要的邊界，
   否則「只靠底色區分」的元件會整個消失。 */
@media (forced-colors: active) {
  .card, .btn, .input, .select, .textarea, .chip, .kpi, .drawer, .confirm__box {
    border: 1px solid CanvasText;
  }
  .btn--primary { border: 1px solid ButtonText; }
  .chip::before { background: CanvasText; }
  mark, .pendingmark { background: Highlight; color: HighlightText; }
}


/* ============================================================================
 * §1 原生 token（L1）
 * ----------------------------------------------------------------------------
 * 這一層只放「原始的、無語意的」值：色階、字階、間距階、圓角、陰影、動效。
 * ⚠ 元件層**不得**直接引用 §1 —— 一律經由 §2 的語意 token。
 *    原因：直接引用會讓「同一個灰色」在不同元件有不同用途，改不動。
 * ==========================================================================*/

:root {
  /* ── 1.1 色階：暖紙底 ＋ 近黑墨（品牌中性色）────────────────────────── */
  --c-paper:        #f6f4f0;   /* 畫布底 */
  --c-paper-2:      #efece6;
  --c-white:        #ffffff;
  --c-neutral-50:   #faf9f7;
  --c-neutral-100:  #f1eee9;
  --c-neutral-200:  #e2ddd4;
  /* ⚠ 這兩個值由 WCAG 1.4.11（非文字對比 3:1）反推而來，不是「看起來順眼」選的：
       輸入框在淺底上只靠邊框辨識，因此邊框對白卡必須 ≥3:1。
       原值 #cfc8bc 只有 1.66:1、#a8a29a 只有 2.64:1 —— 皆不合格。
       現值：--c-neutral-300 對白 3.06:1（最淺的合格暖灰）、--c-neutral-400 對白 3.82:1。 */
  --c-neutral-300:  #9c9285;
  --c-neutral-400:  #8b8175;
  --c-neutral-500:  #8b9095;
  --c-neutral-600:  #676e75;
  --c-neutral-700:  #4b5158;
  --c-neutral-800:  #3b4149;
  --c-neutral-900:  #23262b;
  --c-neutral-950:  #14161a;

  /* ── 1.2 品牌：聖約翰琥珀黃（取自官網 stjohn.org.hk 實測用色）───────── */
  --c-amber-100: #fff8e6;
  --c-amber-200: #fdefc2;
  --c-amber-300: #f2d68f;
  --c-amber-400: #feba14;   /* 主強調 rgb(254,186,20) */
  --c-amber-500: #eca700;   /* 深階 rgb(236,167,0) */
  --c-amber-600: #a97a00;
  --c-amber-700: #6b4a00;   /* 淺黃底上的文字 */
  --c-amber-800: #fec337;   /* 品牌黃底上的淺黃字樣（僅裝飾） */

  /* ── 1.3 狀態色階（四態 ＋ 中性）───────────────────────────────────────
     每個狀態三件套：fg（文字／圖示）／bg（底）／bd（邊界）。
     ⚠ 所有 fg 對其 bg 的對比 ≥ 4.5:1；bd 對白 ≥ 3:1（1.4.11）。 */
  --c-ok-fg:  #1c6b3f;  --c-ok-bg:  #eef7f1;  --c-ok-bd:  #b6ddc4;
  --c-warn-fg:#8a5300;  --c-warn-bg:#fdf6e7;  --c-warn-bd:#f0d9a8;
  --c-info-fg:#1c4f8a;  --c-info-bg:#eef3fa;  --c-info-bd:#bcd2ea;
  --c-crit-fg:#a32020;  --c-crit-bg:#fdefef;  --c-crit-bd:#eec2c2;
  --c-mute-fg:#4b5158;  --c-mute-bg:#f1eee9;  --c-mute-bd:#d5cfc5;

  /* ── 1.4 字族 ────────────────────────────────────────────────────────────
     IBM Plex 超家族：Serif 機構語氣／Sans TC 介面／Mono 數字與代碼。
     ⚠ Sans TC 置於 Sans 之前：中文字先命中 TC 字重，避免英文字型缺字時回退。 */
  --font-sans:  'IBM Plex Sans TC', 'IBM Plex Sans', system-ui, -apple-system, 'Segoe UI',
                'Noto Sans TC', 'Microsoft JhengHei', sans-serif;
  --font-serif: 'IBM Plex Serif', 'IBM Plex Sans TC', Georgia, serif;
  --font-mono:  'IBM Plex Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  /* 登入頁的品牌字樣用官網同款（思源黑體香港版） */
  --font-wordmark: 'Noto Sans HK', var(--font-sans);

  /* ── 1.5 字階（1.4.4：使用者放大字級時版面不破）───────────────────────── */
  --t-micro: 11px;   /* 僅用於非必要標註；對比 ≥ 4.5:1 */
  --t-xs:    12px;
  --t-sm:    13px;
  --t-body:  14px;   /* 後台正文基準（比前台小一級，資訊密度優先） */
  --t-md:    15px;
  --t-lg:    16px;
  --t-h3:    18px;
  --t-h2:    21px;
  --t-h1:    25px;
  --t-d1:    34px;   /* KPI 數值 */

  --leading-tight: 1.25;
  --leading-normal: 1.6;   /* 中文行高比英文高，1.6 是中文可讀下限 */
  --leading-loose: 1.85;

  /* 閱讀行長（R-30：內文每行 40–60 字元，Material 量尺）—— 全站唯一量尺，
     內文 max-width 一律 var(--measure)，不得寫死 ch（ReadingMeasureTest 守門） */
  --measure: 60ch;

  --weight-regular: 400;
  --weight-medium: 500;
  --weight-semi: 600;
  --weight-bold: 700;

  /* ── 1.6 間距（4px 基準網格；第三層慣例）──────────────────────────────── */
  --s0: 0;
  --s1: 4px;
  --s2: 8px;
  --s3: 12px;
  --s4: 16px;
  --s5: 20px;
  --s6: 24px;
  --s8: 32px;
  --s10: 40px;
  --s12: 48px;
  --s16: 64px;

  /* ── 1.7 圓角（6–8px 為主，非膠囊）───────────────────────────────────── */
  --r1: 4px;
  --r2: 6px;
  --r3: 8px;
  --r4: 12px;
  --r-pill: 999px;

  /* ── 1.8 邊框寬度 ─────────────────────────────────────────────────────── */
  --bw1: 1px;
  --bw2: 2px;

  /* ── 1.9 陰影：克制。層級以邊框為主、陰影為輔（Carbon／Atlassian）──────
     只在「真的浮起來」的三種情況用：下拉、抽屜、對話框。 */
  --sh-1: 0 1px 2px rgba(20, 22, 26, .04);
  --sh-2: 0 2px 8px -2px rgba(20, 22, 26, .10), 0 1px 2px rgba(20, 22, 26, .06);
  --sh-3: 0 12px 28px -8px rgba(20, 22, 26, .22), 0 2px 6px rgba(20, 22, 26, .08);
  --sh-4: 0 28px 60px -18px rgba(20, 22, 26, .34), 0 4px 12px rgba(20, 22, 26, .10);

  /* ── 1.10 動效 ────────────────────────────────────────────────────────── */
  --dur-fast: 120ms;
  --dur-base: 180ms;
  --dur-slow: 260ms;
  --ease: cubic-bezier(.22, .68, .28, 1);

  /* ── 1.11 層級 z-index（集中管理，避免各頁亂給）──────────────────────── */
  --z-base: 0;
  --z-sticky: 10;      /* 內容區黏頂（表頭 thead） */
  /* ★ 頂欄必須**高於**內容區黏頂：`.topbar` 自己也是 z-index:10 且自成一層
     堆疊上下文 → 帳號下拉（z-index:50）被關在這一層裡，實際外顯層級＝10。
     內容區的 `.data thead th`（z-index:10）在 DOM 裡排在頂欄**之後**，
     同層級「後寫者勝」→ 有表格的頁面（如 /exams）下拉選單會被表頭蓋住。
     故頂欄另給一個高於 --z-sticky、低於 --z-drawer 的層級。 */
  --z-topbar: 20;      /* 固定頂欄（含其下拉面板） */
  --z-drawer: 40;      /* 側欄抽屜（窄螢幕） */
  --z-scrim: 39;
  --z-dropdown: 50;    /* 下拉、彈出面板 */
  --z-modal: 60;       /* 對話框 */
  --z-toast: 70;       /* 通知（最高，任何情況都看得到） */

  /* ── 1.12 可及性量測 ──────────────────────────────────────────────────── */
  --focus-w: 2px;
  --focus-offset: 2px;
  /* 2.5.8 目標尺寸最小：非文字小控制的點擊區下限 */
  --target-min: 24px;
  /* 2.4.11 焦點不被遮蔽：可視黏頂高度 ＋ 呼吸空間。
     有標籤頁列時由 body.has-tabbar 覆寫（見下）。 */
  --sticky-offset: calc(var(--topbar-h) + var(--s4));

  /* ── 1.13 外殼尺寸（第三層慣例：固定側欄 ＋ 固定頂欄）────────────────── */
  --sidebar-w: 248px;
  --sidebar-w-collapsed: 64px;
  --topbar-h: 56px;
  --tabbar-h: 40px;
  --content-pad: var(--s6);
  --content-max: 1600px;

  /* ── 1.14 控制項高度（三檔密度，見 §2.7）────────────────────────────── */
  --ctl-h-sm: 28px;
  --ctl-h: 36px;
  --ctl-h-lg: 44px;
  --ctl-pad-x: var(--s3);
  --row-h: 42px;
}


/* ============================================================================
 * §2 語意 token（L2）
 * ----------------------------------------------------------------------------
 * 元件層**只可以使用這一層**。命名用「用途」而不是「顏色」：
 *   ✓ --text-2        （次要文字）      ✗ --gray-600
 * 這樣改配色時只需改 §2 的映射，不必碰任何元件。
 * ==========================================================================*/

:root {
  /* ── 2.1 表面 ─────────────────────────────────────────────────────────── */
  --surface-canvas: var(--c-paper);      /* 頁面底 */
  --surface-raised: var(--c-white);      /* 卡片／面板 */
  --surface-sunken: var(--c-neutral-100);/* 輸入槽、表格表頭、內嵌區 */
  --surface-subtle: var(--c-neutral-50); /* 斑馬列、hover 淡底 */
  --surface-overlay: var(--c-white);     /* 下拉／對話框內容面 */
  --surface-inverse: var(--c-neutral-950);/* 側欄、toast */
  --surface-hover: rgb(20 22 26 / 4%);
  --surface-active: rgb(20 22 26 / 7%);
  --surface-brand-soft: var(--c-amber-100);
  --surface-selected: var(--c-amber-100);

  /* ── 2.2 文字（★ 對比為硬門檻，改值前必須重測）────────────────────────
     實測基準（WCAG 2.1 相對亮度公式）：
       --text-1 對 canvas 16.8:1 ／ 對 raised 18.9:1
       --text-2 對 canvas  9.6:1 ／ 對 raised 10.8:1
       --text-3 對 canvas  4.70:1 ／ 對 raised 5.17:1   ← 可讀小字的下限
       --text-4 對 raised 3.22:1                        ← 只可用於大字／非文字
     ⚠ 使用契約：--text-4 **只**用於 ≥18.66px 粗體、≥24px 的一般字，或非文字 UI
       （圖示、分隔線）。11–13px 的可讀小字一律用 --text-3。 */
  --text-1: var(--c-neutral-950);   /* 正文主色 */
  --text-2: var(--c-neutral-800);   /* 次要文字 */
  --text-3: var(--c-neutral-600);   /* 輔助文字（小字下限） */
  --text-4: var(--c-neutral-500);   /* 佔位／弱化（大字或非文字） */
  --text-inverse: var(--c-white);
  --text-brand: var(--c-amber-700);
  --text-brand-on-dark: var(--c-amber-400); /* 深底（側欄）上的品牌小標：亮黃對 #14161a 10.56:1（同 2.4 註） */
  --text-link: var(--c-info-fg);
  --text-on-brand: #23180a;         /* 品牌黃底上的文字 */

  /* ── 2.3 邊界 ─────────────────────────────────────────────────────────── */
  --border-subtle: var(--c-neutral-200);  /* 分隔線 */
  --border-default: var(--c-neutral-300); /* 輸入框、卡片邊 */
  --border-strong: var(--c-neutral-400);  /* 強調邊 */
  --border-brand: var(--c-amber-300);
  --border-focus: var(--c-amber-500);

  /* ── 2.4 焦點環 ───────────────────────────────────────────────────────────
     ⚠ WCAG 1.4.11：焦點指示對相鄰色須 ≥3:1。
       品牌亮黃（--c-amber-400 #feba14）對白只有 1.71:1、深階 #eca700 只有 2.08:1 ——
       兩者都不合格（等於鍵盤使用者看不到焦點）。
       改用 --c-amber-600 #a97a00：對白 3.84:1、對深底 #14161a 4.72:1，
       **單一值同時滿足亮底與深底**，不必依背景切換。 */
  --focus-ring: var(--c-amber-600);
  /* 深色底（側欄）上仍用亮黃：對 #14161a 為 10.56:1，且保留品牌辨識 */
  --focus-ring-on-dark: var(--c-amber-400);

  /* ── 2.5 狀態（語意層：四態 ＋ 中性）─────────────────────────────────── */
  --st-done-fg: var(--c-ok-fg);    --st-done-bg: var(--c-ok-bg);    --st-done-bd: var(--c-ok-bd);
  --st-pending-fg: var(--c-warn-fg);--st-pending-bg: var(--c-warn-bg);--st-pending-bd: var(--c-warn-bd);
  --st-progress-fg: var(--c-info-fg);--st-progress-bg: var(--c-info-bg);--st-progress-bd: var(--c-info-bd);
  --st-overdue-fg: var(--c-crit-fg);--st-overdue-bg: var(--c-crit-bg);--st-overdue-bd: var(--c-crit-bd);
  --st-return-fg: var(--c-mute-fg);--st-return-bg: var(--c-mute-bg);--st-return-bd: var(--c-mute-bd);
  --st-neutral-fg: var(--c-mute-fg);--st-neutral-bg: var(--c-mute-bg);--st-neutral-bd: var(--c-mute-bd);

  /* ── 2.6 控制項 ───────────────────────────────────────────────────────── */
  --ctl-bg: var(--c-white);
  --ctl-bg-disabled: var(--c-neutral-100);
  --ctl-fg: var(--text-1);
  --ctl-fg-disabled: var(--text-3);
  --ctl-bd: var(--border-default);
  --ctl-bd-hover: var(--border-strong);
  --ctl-placeholder: var(--c-neutral-500);

  --btn-primary-bg: var(--c-neutral-950);
  --btn-primary-bg-hover: var(--c-neutral-800);
  --btn-primary-fg: var(--c-white);
  --btn-danger-bg: var(--c-crit-fg);
  --btn-danger-fg: var(--c-white);
  --btn-ghost-fg: var(--text-2);

  /* ── 2.7 密度（第三層慣例：全站一個設定，掛在 <html data-density>）────── */
  --density-row-h: var(--row-h);
  --density-pad-y: var(--s3);
}

html[data-density="compact"] {
  --ctl-h: 32px;
  --ctl-h-sm: 26px;
  --ctl-pad-x: var(--s2);
  --row-h: 34px;
  --density-row-h: 34px;
  --density-pad-y: var(--s2);
  --content-pad: var(--s5);
}

html[data-density="spacious"] {
  --ctl-h: 44px;
  --ctl-h-sm: 32px;
  --ctl-pad-x: var(--s4);
  --row-h: 52px;
  --density-row-h: 52px;
  --density-pad-y: var(--s4);
  --content-pad: var(--s8);
}


/* ============================================================================
 * §3 佈局原語
 * ----------------------------------------------------------------------------
 * 第三層慣例：**版面用原語組合，不在頁面內寫 margin 魔術數字**。
 * 四個原語足以表達後台 95% 的版面需求：
 *   .stack    垂直堆疊（預設 gap，可調）
 *   .cluster  水平自動換行（按鈕列、標籤列）
 *   .grid     等分網格（自動響應，不需 media query）
 *   .split    兩欄（主 ＋ 輔），窄螢幕自動收單欄
 * ==========================================================================*/

/* ── 3.1 應用外殼 ─────────────────────────────────────────────────────────
   APG：無專屬 pattern（版面容器）。ARIA：nav[aria-label]、main、footer[contentinfo]
   WCAG：1.3.1（語意結構）、2.4.1（跳過導覽，見 §6.2）、1.4.10（回流）
   鍵盤：無（容器不可聚焦；唯 <main> 有 tabindex="-1" 作為跳過導覽的落點）

   慣例：六家一致 —— 固定側欄 ＋ 固定頂欄 ＋ **內容區為唯一捲動容器**。
   ⚠ 捲動所有權為什麼重要：若讓 body 捲動，`position: sticky` 的表頭會相對於
     body 定位，長表格一捲就與頁首錯位；改由內容區捲動後，sticky 表頭的行為
     才可預期。本專案既有測試（LayoutBudgetTest 的 docScrolls）也依賴此約定。
   ⚠ 焦點不被遮（2.4.11）由此模型的兩件事共同滿足：
     ① 捲動只發生在內容區（黏頂元素不參與捲動）；② --sticky-offset 見 §1.12。 */
html, body { height: 100%; }

/* 只有內容區捲動 → body 不捲 */
body.app-shell { overflow: hidden; }

/* 有標籤頁列時，焦點留白要加上標籤列的高度（否則會被它遮住） */
body.has-tabbar { --sticky-offset: calc(var(--topbar-h) + var(--tabbar-h) + var(--s4)); }

.shell {
  display: grid;
  grid-template-columns: var(--sidebar-w) minmax(0, 1fr);
  height: 100vh;
  height: 100dvh;      /* 手機：避開動態工具列造成的 100vh 誤差 */
}

.main {
  display: flex;
  flex-direction: column;
  min-width: 0;               /* 讓內部的橫向捲動（表格）不會撐破 grid */
  min-height: 0;              /* ★ 讓 .content 真正被限高：flex 子項預設 min-height:auto，
                                 內容一高就會把 .main 撐破 .shell（100dvh），
                                 超出部分被 body{overflow:hidden} 裁掉 → 看不到也捲不動 */
}

/* 內容區是**唯一**的捲動容器（見上方說明）*/
.content {
  flex: 1;
  min-height: 0;              /* ★ 同上：flex 子項要 min-height:0，overflow-y 才會生效 */
  width: 100%;
  max-width: var(--content-max);
  margin-inline: auto;
  padding: var(--content-pad);
  overflow-y: auto;
  overscroll-behavior: contain;   /* 捲到底不要把整頁帶動 */
}

/* 側欄收合（桌機）：只留圖示 */
.shell[data-nav-collapsed="true"] { grid-template-columns: var(--sidebar-w-collapsed) minmax(0, 1fr); }

/* ── 3.2 側邊導覽 ─────────────────────────────────────────────────────────
   APG：Disclosure（分組可展開）＋ Landmarks
   ARIA：<nav aria-label>；當前項 aria-current="page"；分組標題為文字而非標題元素
   WCAG：2.4.7、2.5.8（每項 ≥24px 高）、1.4.3
   鍵盤：Tab 逐項；Enter 進入；無方向鍵（非 roving tabindex 的 menubar —— 那是
         APG 的另一個 pattern，後台側欄普遍不用，因為項目多且需逐項 Tab 才易用）*/
.nav {
  grid-row: 1 / -1;
  background: var(--surface-inverse);
  color: var(--text-inverse);
  display: flex;
  flex-direction: column;
  height: 100%;
  min-height: 0;          /* 讓內部的 .nav__scroll 能正確取得高度並捲動 */
  z-index: var(--z-drawer);
}

.nav__brand {
  display: flex;
  align-items: center;
  gap: var(--s3);
  height: var(--topbar-h);
  padding-inline: var(--s4);
  border-bottom: 1px solid rgb(255 255 255 / 10%);
  flex: none;
}

.nav__brandcol { display: flex; align-items: center; gap: var(--s3); min-width: 0; }

/* LOGO 置於白底小片：官方 LOGO 是深色線稿，深底會看不清 */
.nav__logochip {
  flex: none;
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  /* ⚠ 徽章**不墊底色**。
     先前是「白底 ＋ 圓角 8px ＋ padding」—— 那等於在 logo 外面又加了一層白色方片，
     使用者看到的是白方塊而不是圓徽（PM 已指出）。
     現在：底色透明、圓形、無 padding，size 由這裡決定，圖填滿。 */
  background: transparent;
  border-radius: var(--r-pill);   /* 圓形（--r-pill = 999px，等同全圓角） */
  padding: 0;
  overflow: hidden;               /* 保險：即使源圖邊緣不是正圓，也裁成圓 */
}

/* 徽章圖：正方形源圖（stjohn_logo.png 的圓徽部分），填滿圓形容器。
   object-fit: contain 確保不變形。 */
.nav__logo { width: 100%; height: 100%; object-fit: contain; display: block; }

.nav__title {
  font-size: var(--t-sm);
  font-weight: var(--weight-semi);
  color: var(--c-amber-400);
  letter-spacing: .02em;
  white-space: nowrap;
  overflow: hidden;
}

.nav__close {
  margin-left: auto;
  display: none;            /* 僅窄螢幕抽屜顯示 */
  width: var(--target-min);
  height: var(--target-min);
  place-items: center;
  border-radius: var(--r2);
  color: rgb(255 255 255 / 70%);
}

.nav__close:hover { background: rgb(255 255 255 / 10%); color: var(--c-white); }

.nav__scroll {
  flex: 1;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: var(--s3) var(--s2);
}

.nav__group + .nav__group { margin-top: var(--s4); }

.nav__grouphead {
  padding: var(--s2) var(--s3);
  font-size: var(--t-micro);
  font-weight: var(--weight-semi);
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--text-brand-on-dark);
}

/* 2.5.8：項目高度 ≥ min-height --target-min，實際 36px */
.nav__item {
  display: flex;
  align-items: center;
  gap: var(--s3);
  min-height: 36px;
  padding: var(--s2) var(--s3);
  border-radius: var(--r2);
  font-size: var(--t-sm);
  color: rgb(255 255 255 / 82%);
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}

.nav__item:hover { background: rgb(255 255 255 / 8%); color: var(--c-white); }

/* 當前項：底色 ＋ 左側黃條 ＋ 粗體（不只靠顏色 —— 1.4.1）*/
.nav__item[aria-current="page"] {
  background: rgb(254 186 20 / 14%);
  color: var(--c-amber-400);
  font-weight: var(--weight-semi);
  box-shadow: inset 3px 0 0 var(--c-amber-400);
}

.nav__item .ph { font-size: 18px; flex: none; width: 18px; text-align: center; }

.nav__count {
  margin-left: auto;
  min-width: 20px;
  height: 18px;
  padding-inline: 5px;
  display: inline-grid;
  place-items: center;
  border-radius: var(--r-pill);
  background: rgb(255 255 255 / 14%);
  font-family: var(--font-mono);
  font-size: var(--t-micro);
  font-variant-numeric: tabular-nums;
}

/* 收合態：只留圖示，標籤收為螢幕閱讀器可讀（不是 display:none）*/
.shell[data-nav-collapsed="true"] .nav__title,
.shell[data-nav-collapsed="true"] .nav__grouphead,
.shell[data-nav-collapsed="true"] .nav__item > span:not(.nav__count),
.shell[data-nav-collapsed="true"] .nav__count { display: none; }

.shell[data-nav-collapsed="true"] .nav__item { justify-content: center; }
.shell[data-nav-collapsed="true"] .nav__logochip { margin-inline: auto; }
.shell[data-nav-collapsed="true"] .nav__brand { justify-content: center; padding-inline: 0; }

/* ── 3.3 頂欄 ─────────────────────────────────────────────────────────────
   APG：Landmarks（banner）＋ Menu（帳號選單，見 §4.6）
   WCAG：1.3.1、2.4.7、1.4.11（麵包屑分隔符的對比）
   慣例：左「你在哪裡」／中彈性空白／右全域動作，是六家的共同順序 */
.topbar {
  flex: none;
  height: var(--topbar-h);
  display: flex;
  align-items: center;
  gap: var(--s3);
  padding-inline: var(--s4);
  background: var(--surface-canvas);
  border-bottom: 1px solid var(--border-subtle);
  position: sticky;
  top: 0;
  /* ★ 用 --z-topbar（20）而非 --z-sticky（10）：頂欄自成一層堆疊上下文，
     若與內容區黏頂同為 10，DOM 順序在後的表格表頭會蓋住頂欄的子元素
     （帳號下拉／語言選單），即使它們自己的 z-index 是 50。 */
  z-index: var(--z-topbar);
}

.topbar__toggle {
  display: grid;
  place-items: center;
  width: var(--ctl-h);
  height: var(--ctl-h);
  border-radius: var(--r2);
  color: var(--text-2);
  flex: none;
}

.topbar__toggle:hover { background: var(--surface-hover); color: var(--text-1); }

.topbar__crumbs {
  display: flex;
  align-items: center;
  gap: var(--s2);
  min-width: 0;
  font-size: var(--t-sm);
  color: var(--text-3);
}

.topbar__crumbs > :last-child { color: var(--text-1); font-weight: var(--weight-medium); }
.topbar__crumbs .ph { font-size: 14px; color: var(--text-4); }

.topbar__spacer { flex: 1; }

.topbar__actions { display: flex; align-items: center; gap: var(--s2); flex: none; }

/* ── 3.3a 窄屏幕頂欄收斂（BLK-11 G-1：390px 橫向溢出修復）──────────────
   成因：`.topbar__actions{flex:none}` 不縮 ＋ `.userbtn` 角色＋電郵兩行，
         在 390px 窄屏使頂欄內容超出可用寬、整頁出現橫向捲動（WCAG 1.4.10 回流）。
   修法：窄屏讓 `.userbtn` 收單行（隱藏電郵行，本就是輔助資訊）＋
         `.topbar__actions` 允許縮小（flex 改 0 1 auto、min-width:0），
         使頂欄在 390px 不溢出。視覺尺寸不變、只對窄屏生效。 */
@media (max-width: 480px) {
  .userbtn__mail { display: none; }
  .topbar__actions { flex: 0 1 auto; min-width: 0; }
  /* G-1 補刀：頂欄容器已可縮，但 .userbtn 本體預設 min-width:auto 仍撐住寬度，
     導致 ops 側角色文字較長時（如「營運人員 test@example.com」）按鈕寬 202px 超出 390px 視窗。
     窄屏允許 .userbtn 收縮並對角色文字截斷，視覺不變、只對窄屏生效。 */
  .userbtn { min-width: 0; flex: 0 1 auto; overflow: hidden; }
  .userbtn__role { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
}

/* ── 3.4 頁尾 ─────────────────────────────────────────────────────────────
   ⚠ 必須放在 </main> 之外才會被對應成 contentinfo（HTML-AAM） */
.pagefoot {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s4);
  padding: var(--s3) var(--content-pad);
  border-top: 1px solid var(--border-subtle);
  font-size: var(--t-xs);
  color: var(--text-3);
}

.pagefoot__copy { min-width: 0; }
.pagefoot__meta { font-variant-numeric: tabular-nums; font-family: var(--font-mono); }

/* ── 3.5 標籤頁列（多頁籤導覽，第三層慣例：Ant Design Pro／Element Plus 皆有）──
   APG：Tabs（手動啟用模式 —— 後台頁籤數量不定，自動啟用會誤觸）
   ARIA：role="tablist"／role="tab"／aria-selected／aria-controls
   WCAG：2.1.1、2.4.7、2.5.8
   鍵盤：← → 移動焦點；Home／End 到首末；Enter／Space 啟用；Delete 關閉（可選） */
.tabbar {
  flex: none;
  display: flex;
  align-items: stretch;
  gap: var(--s1);
  height: var(--tabbar-h);
  padding-inline: var(--s4);
  background: var(--surface-raised);
  border-bottom: 1px solid var(--border-subtle);
  overflow-x: auto;
  scrollbar-width: thin;
}

.tabbar__tab {
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  min-height: var(--target-min);
  padding-inline: var(--s3);
  border-bottom: 2px solid transparent;
  font-size: var(--t-sm);
  color: var(--text-3);
  white-space: nowrap;
}

.tabbar__tab:hover { color: var(--text-1); }

.tabbar__tab[aria-selected="true"] {
  color: var(--text-1);
  font-weight: var(--weight-semi);
  border-bottom-color: var(--c-amber-500);
}

.tabbar__close {
  width: 18px; height: 18px;
  display: grid; place-items: center;
  border-radius: var(--r1);
  color: var(--text-4);
}

.tabbar__close:hover { background: var(--surface-active); color: var(--text-1); }

/* ── 3.6 佈局原語 ───────────────────────────────────────────────────────── */

/* 垂直堆疊 */
.stack { display: flex; flex-direction: column; gap: var(--s4); }
.stack--sm { gap: var(--s2); }
.stack--lg { gap: var(--s6); }
.stack--flush { gap: 0; }

/* 水平自動換行（按鈕列、標籤列）*/
.cluster { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s2); }
.cluster--end { justify-content: flex-end; }
.cluster--between { justify-content: space-between; }
.cluster--lg { gap: var(--s4); }

/* 等分網格（不需 media query，靠 auto-fit）*/
.grid { display: grid; gap: var(--s4); grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
.grid--kpi { grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }
.grid--2 { grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); }
.grid--tight { gap: var(--s3); }

/* 兩欄：主 ＋ 輔（輔欄固定 340px，窄螢幕自動收單欄）
   加寬輔欄：300px 塞不下五欄工作清單 table，改卡片列表後 340px 較舒適。 */
.detail {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 340px;
  gap: var(--s4);
  align-items: start;
}

.detail__aside { display: flex; flex-direction: column; gap: var(--s4); min-width: 0; }
.detail__main { display: flex; flex-direction: column; gap: var(--s4); min-width: 0; }

/* 輔欄工作清單：垂直卡片，避免窄欄裡 table 被壓扁換行 */
.queue {
  display: flex;
  flex-direction: column;
  gap: var(--s3);
  padding: var(--s3);
}

.queue__item {
  padding: var(--s3);
  border: 1px solid var(--border-subtle);
  border-radius: var(--r2);
  background: var(--surface-subtle);
}

.queue__title {
  font-weight: var(--weight-medium);
  line-height: 1.4;
  color: var(--text-1);
  margin-bottom: var(--s2);
}

.queue__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s1) var(--s2);
  font-size: var(--t-sm);
  color: var(--text-2);
  margin-bottom: var(--s2);
}

.queue__meta-label {
  color: var(--text-3);
}

.queue__meta .chip { font-size: var(--t-xs); }

.queue__action {
  display: flex;
  justify-content: flex-end;
}

/* 表單列（label 與控制項同列；窄螢幕收單欄）*/
.formrow {
  display: grid;
  grid-template-columns: 180px minmax(0, 1fr);
  gap: var(--s3) var(--s4);
  align-items: start;
}

.formrow__label {
  padding-top: 9px;      /* 與控制項基線對齊 */
  font-size: var(--t-sm);
  font-weight: var(--weight-medium);
  color: var(--text-2);
}

.formgrid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: var(--s4);
}


/* ============================================================================
 * §4 元件
 * ==========================================================================*/

/* ── 4.1 頁首（頁面標題區）───────────────────────────────────────────────
   APG：無專屬 pattern。ARIA：h1 為唯一頁面標題
   WCAG：1.3.1（h1 保留給文件大綱，視覺可隱藏）、2.4.6（標題與標籤）
   慣例：標題 ＋ 狀態標記 ＋ 一句說明在左，頁面動作在右 */
.pagehead { margin-bottom: var(--s5); }

.pagehead__row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--s4);
  flex-wrap: wrap;
}

.pagehead__title {
  /* h1 保留給文件大綱與螢幕閱讀器。**桌面上**視覺由頂欄的麵包屑承擔「你在哪裡」；
     **≤600px 麵包屑隱藏，h1 必須可見**（見檔尾窄屏段 `@media (max-width: 600px)`）。
     ⚠ 這兩處是一對：改任何一邊都要看另一邊 —— 兩邊都藏起來的話，
       手機上就沒有任何「你在哪裡」的指示，而且**不會有任何錯誤**。 */
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.pagehead__view {
  display: inline-flex;
  align-items: center;
  height: 22px;
  padding-inline: var(--s2);
  border: 1px solid var(--border-brand);
  border-radius: var(--r1);
  background: var(--surface-brand-soft);
  color: var(--text-brand);
  font-family: var(--font-mono);
  font-size: var(--t-micro);
  font-weight: var(--weight-semi);
  letter-spacing: .06em;
}

.pagehead__desc {
  margin-top: var(--s2);
  max-width: var(--measure); /* 行長量尺見 --measure（R-30：40–60ch，不寫死） */
  font-size: var(--t-sm);
  color: var(--text-3);
  line-height: var(--leading-loose);
}

.pagehead__actions { display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap; }

/* ── 4.2 卡片 ────────────────────────────────────────────────────────────
   APG：無（容器）。ARIA：需要時用 <section aria-labelledby>
   WCAG：1.3.1、1.4.11（邊界的 3:1）
   慣例：**邊框優先、陰影克制** —— 一頁可能有 20 張卡，全部加陰影會變成噪音 */
.card {
  background: var(--surface-raised);
  border: 1px solid var(--border-subtle);
  border-radius: var(--r3);
}

.card__head {
  display: flex;
  align-items: baseline;      /* 標題與副標同基線，視覺成一行 */
  flex-wrap: wrap;            /* 放不下時副標換行，而不是兩者互相擠壓 */
  justify-content: space-between;
  gap: var(--s1) var(--s3);
  min-height: 46px;
  padding: var(--s3) var(--s4);
  border-bottom: 1px solid var(--border-subtle);
  background: var(--surface-subtle);
  border-radius: var(--r3) var(--r3) 0 0;
}

/* BLK-9 規格 C：卡片頭齊邊（原 _session-form inline padding 歸零等價搬移） */
.card__head--flush { padding-inline: 0; }

/* ⚠ 標題與副標一律 flex: none（不壓縮）。
   先前兩者都用預設 flex-shrink:1，在 300px 的輔欄裡（「我的工作清單」＋其副標）
   會被一起壓縮，標題硬斷成兩行 —— 版面因此顯得雜亂。
   改為不壓縮 ＋ 父層 flex-wrap：放不下就讓副標整段換到第二行；
   寧可多一行，也不要半句話被切斷。

   這裡用 justify-content: space-between（而不是副標的 margin-left:auto）是刻意的：
   換行後那一行只剩副標，space-between 會讓它靠左、貼在標題下方；
   若用 margin-left:auto，它會被推到右緣而與標題錯開。 */
.card__title { font-size: var(--t-md); font-weight: var(--weight-semi); color: var(--text-1); flex: none; }
.card__sub { font-size: var(--t-xs); color: var(--text-3); flex: none; min-width: 0; }
.card__body { padding: var(--s4); }
.card__body--flush { padding: 0; }
.card__foot {
  padding: var(--s3) var(--s4);
  border-top: 1px solid var(--border-subtle);
  background: var(--surface-subtle);
  border-radius: 0 0 var(--r3) var(--r3);
}

/* 卡內嵌卡（.card 內不再用 .card，改用具名區塊，避免陰影疊加）*/
.subcard {
  border: 1px solid var(--border-subtle);
  border-radius: var(--r2);
  padding: var(--s4);
  background: var(--surface-subtle);
}

/* ── 4.3 按鈕 ────────────────────────────────────────────────────────────
   APG：Button（含 Toggle Button 的 aria-pressed 變體）
   ARIA：原生 <button>／<a>。載入中 aria-busy="true" ＋ aria-disabled（**不用 disabled**，
         否則焦點會跳走、讀屏不播報）。圖示鈕必須有 aria-label。
   WCAG：2.5.8（≥24px，實際 36px）、1.4.11（邊界的 3:1）、2.4.7
   鍵盤：Enter／Space 觸發；Tab 逐顆；不得用 div 假造 */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s2);
  height: var(--ctl-h);
  min-width: var(--ctl-h);           /* 圖示鈕也是 36×36 */
  padding-inline: var(--ctl-pad-x);
  border: 1px solid var(--border-default);
  border-radius: var(--r2);
  background: var(--ctl-bg);
  color: var(--ctl-fg);
  font-size: var(--t-sm);
  font-weight: var(--weight-medium);
  line-height: 1;
  white-space: nowrap;
  transition: background var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease),
              color var(--dur-fast) var(--ease);
}

.btn:hover { background: var(--surface-subtle); border-color: var(--ctl-bd-hover); }
.btn:active { background: var(--surface-sunken); }

/* 主要動作：一頁最多一顆（第三層慣例）*/
.btn--primary {
  background: var(--btn-primary-bg);
  border-color: var(--btn-primary-bg);
  color: var(--btn-primary-fg);
}

.btn--primary:hover { background: var(--btn-primary-bg-hover); border-color: var(--btn-primary-bg-hover); }

.btn--danger { background: var(--btn-danger-bg); border-color: var(--btn-danger-bg); color: var(--btn-danger-fg); }
.btn--danger:hover { filter: brightness(1.12); }

/* 幽靈鈕：無邊框，用於低優先動作與工具列。
   ⚠ 靜止態**全透明** ⇒ 只適合「有 hover 的次要動作」；觸控裝置無 hover，
     且 WCAG 1.4.11（非文字對比 3:1）沒有邊界可判 ⇒ 需要「靜止態可辨」者改用 `.btn--quiet`。
   ★ 本類定義**不動**（既有 26 處沿用）。 */
.btn--ghost { background: transparent; border-color: transparent; color: var(--btn-ghost-fg); }
.btn--ghost:hover { background: var(--surface-hover); border-color: transparent; }

/* 靜默鈕（REQ-23 B 方案；★ 2026-10-17 L-4-1 新增）：**靜止態即可辨**的弱化變體。
   與 ghost 的差別：**保留可見邊框＋淡底色**（非全透明）——
   供**表格操作欄與主要操作**取代 `--ghost`（那些位置在觸控裝置無 hover，
   全透明的 ghost 讓使用者無從辨識；且 WCAG 1.4.11 需要可判的邊界對比 ≥3:1）。
   語意＝「比 primary／danger 弱、但比 ghost 明確」：底 `--surface-subtle`、
   邊 `--border-default`（與預設 `.btn` 同邊框，故邊界對比與預設鈕一致，達 1.4.11）。 */
.btn--quiet { background: var(--surface-subtle); border-color: var(--border-default); color: var(--text-2); }
.btn--quiet:hover { background: var(--surface-hover); border-color: var(--ctl-bd-hover); color: var(--text-1); }

.btn--sm { height: var(--ctl-h-sm); padding-inline: var(--s2); font-size: var(--t-xs); min-width: var(--ctl-h-sm); }
.btn--lg { height: var(--ctl-h-lg); padding-inline: var(--s5); font-size: var(--t-md); }
.btn--icon { padding-inline: 0; width: var(--ctl-h); }
.btn--sm.btn--icon { width: var(--ctl-h-sm); }

.btn--block { width: 100%; }

/* 載入中：aria-busy ＋ aria-disabled（維持焦點與可播報）*/
.btn[aria-busy="true"] { color: transparent; position: relative; pointer-events: none; }
.btn[aria-busy="true"]::after {
  content: '';
  position: absolute;
  inset: 50% auto auto 50%;
  width: 14px; height: 14px;
  margin: -7px 0 0 -7px;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  color: var(--text-2);
  animation: ds-spin .7s linear infinite;
}

/* ── BLK-10 送出中／載入中指示（真差距 G-6 的 CSS 端落地）────────────────────
   成因：`app.js` 送出時掛的是 `form[data-submitting="true"]` 與 `body[data-loading="true"]`，
   而樣式表原本只有 `.btn[aria-busy="true"]` 有旋轉指示——JS 並不掛 `aria-busy`，
   結果是「防重複送出有效、畫面卻看不出正在送出」（指示斷鏈）。
   修法：**只加樣式、不改 JS 行為**（JS 為 verbatim，改它五系統連動），
   指示沿用同一個 `ds-spin`，視覺與 `.btn[aria-busy]` 完全一致。 */
body[data-loading="true"] { cursor: progress; }

/* 全頁送出中指示：頂部 2px 進度條（**不是覆蓋層**——不遮內容、不擋操作，
   純指示；z-index 取 --z-toast 讓它在任何情況都看得到） */
body[data-loading="true"]::before {
  content: '';
  display: block;              /* 明示 display：稽核 E9 要求覆蓋性質的規則必須控制顯示 */
  position: fixed;
  inset: 0 auto auto 0;
  width: 100%;
  height: 2px;
  background: var(--c-amber-600);
  z-index: var(--z-toast);
  pointer-events: none;
}

form[data-submitting="true"] button[type="submit"],
form[data-submitting="true"] .btn[type="submit"] {
  color: transparent;
  position: relative;
  pointer-events: none;
}

form[data-submitting="true"] button[type="submit"]::after,
form[data-submitting="true"] .btn[type="submit"]::after {
  content: '';
  position: absolute;
  inset: 50% auto auto 50%;
  width: 14px; height: 14px;
  margin: -7px 0 0 -7px;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  color: var(--text-2);
  animation: ds-spin .7s linear infinite;
}

.btn[aria-disabled="true"], .btn:disabled {
  background: var(--ctl-bg-disabled);
  border-color: var(--border-subtle);
  color: var(--ctl-fg-disabled);
  pointer-events: none;
}

/* 按鈕群（相鄰按鈕共用邊界）*/
.btnrow { display: inline-flex; }
.btnrow .btn { border-radius: 0; margin-left: -1px; }
.btnrow .btn:first-child { border-radius: var(--r2) 0 0 var(--r2); margin-left: 0; }
.btnrow .btn:last-child { border-radius: 0 var(--r2) var(--r2) 0; }

@keyframes ds-spin { to { transform: rotate(360deg); } }

/* ── 4.4 表單 ────────────────────────────────────────────────────────────
   APG：無專屬 pattern（原生控制項）；錯誤訊息用 aria-describedby 關聯
   ARIA：<label for> 必須存在（不可只用 placeholder）；aria-invalid="true"；
         aria-describedby 指向錯誤訊息；必填用 required ＋ 視覺 <span class="req">
   WCAG：1.3.1、1.4.11、2.5.8、3.3.1（錯誤辨識）、3.3.2（標籤或指示）、
         3.3.7（autocomplete，避免重複輸入）、3.3.8（密碼欄允許貼上）
   鍵盤：Tab 進入；Enter 在單行輸入送出表單 */
.field { display: flex; flex-direction: column; gap: 6px; }

.field__label {
  display: inline-flex;
  align-items: center;
  gap: var(--s1);
  font-size: var(--t-sm);
  font-weight: var(--weight-medium);
  color: var(--text-2);
}

/* 必填記號：同時有符號與 sr-only 文字（不只靠顏色／形狀 —— 1.4.1）*/
.req { color: var(--c-crit-fg); font-weight: var(--weight-bold); }

.field__hint { font-size: var(--t-xs); color: var(--text-3); line-height: var(--leading-loose); }

.input, .select, .textarea {
  width: 100%;
  min-height: var(--ctl-h);
  padding: 6px var(--ctl-pad-x);
  border: 1px solid var(--ctl-bd);
  border-radius: var(--r2);
  background: var(--ctl-bg);
  color: var(--ctl-fg);
  font-size: var(--t-sm);
  line-height: 1.5;
  transition: border-color var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease);
}

.input::placeholder, .textarea::placeholder { color: var(--ctl-placeholder); }

.input:hover, .select:hover, .textarea:hover { border-color: var(--ctl-bd-hover); }

/* 焦點：邊界變黃 ＋ 外框（1.4.11：3:1；不依賴 box-shadow 單獨表現，
   forced-colors 下 box-shadow 會消失，故同時改 border-color）*/
.input:focus-visible, .select:focus-visible, .textarea:focus-visible {
  border-color: var(--border-focus);
  outline: var(--focus-w) solid var(--focus-ring);
  outline-offset: 0;
}

.textarea { min-height: 88px; resize: vertical; padding-block: var(--s2); }

.select {
  appearance: none;
  padding-right: var(--s8);        /* 讓出箭頭的位置，避免文字壓在箭頭上 */
  background-image: linear-gradient(45deg, transparent 50%, var(--text-3) 50%),
                    linear-gradient(135deg, var(--text-3) 50%, transparent 50%);
  background-position: right 16px center, right 11px center;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}

/* 錯誤態：邊界變紅 ＋ 訊息（文字，不只顏色）*/
.input[aria-invalid="true"], .select[aria-invalid="true"], .textarea[aria-invalid="true"] {
  border-color: var(--c-crit-bd);
  background: var(--c-crit-bg);
}

.field__error {
  display: flex;
  align-items: flex-start;
  gap: 5px;
  font-size: var(--t-xs);
  color: var(--c-crit-fg);
  line-height: var(--leading-loose);
}

.field__error::before {
  content: '\26A0';                 /* ⚠ 文字型圖示，forced-colors 下仍在 */
  flex: none;
  font-size: var(--t-sm);
}

.input:disabled, .select:disabled, .textarea:disabled {
  background: var(--ctl-bg-disabled);
  color: var(--ctl-fg-disabled);
  border-color: var(--border-subtle);
  cursor: not-allowed;
}

/* 勾選與單選（BLK-11 G-2：命中區 ≥ --target-min 2.5.8）─────────────────
   原生 input 刻意維持 16×16（視覺），但**點擊／觸控命中區由包住的
   label 提供**：label 有 min-height 24 ＋ 四向 padding，整個標籤皆可點
   （原生行為：點 label 即切換 input）。故功能性目標尺寸已 ≥24，
   不靠放大 input 本身。 */
.checkbox, .radio {
  display: inline-flex;
  align-items: flex-start;
  gap: var(--s2);
  min-height: var(--target-min);
  padding: var(--s1);
  font-size: var(--t-sm);
  color: var(--text-1);
}

.checkbox input, .radio input {
  flex: none;
  width: 16px; height: 16px;
  margin-top: 3px;
  accent-color: var(--c-amber-500);
}

/* 開關（Toggle）：APG Switch —— role="switch" ＋ aria-checked，Space 切換 */
.switch { display: inline-flex; align-items: center; gap: var(--s3); min-height: var(--target-min); }
.switch input { position: absolute; opacity: 0; width: 0; height: 0; }

.switch__track {
  position: relative;
  width: 38px; height: 22px;
  border-radius: var(--r-pill);
  background: var(--c-neutral-300);
  transition: background var(--dur-fast) var(--ease);
  flex: none;
}

.switch__track::after {
  content: '';
  position: absolute;
  top: 3px; left: 3px;
  width: 16px; height: 16px;
  border-radius: 50%;
  background: var(--c-white);
  box-shadow: var(--sh-1);
  transition: transform var(--dur-fast) var(--ease);
}

.switch input:checked + .switch__track { background: var(--c-ok-fg); }
.switch input:checked + .switch__track::after { transform: translateX(16px); }
.switch input:focus-visible + .switch__track { outline: var(--focus-w) solid var(--focus-ring); outline-offset: var(--focus-offset); }

/* ── 4.5 資料表格 ────────────────────────────────────────────────────────
   APG：Grid（互動式）或 Table（靜態）。後台清單一律用**原生語意 table**，
         因為排序／選取是「額外加強」而非必要，語意 table 對讀屏最友善。
   ARIA：<caption> 或 aria-label 說明表格用途；表頭 <th scope="col">；
         sortable 用 aria-sort="ascending|descending|none"；
         選取欄用 <th scope="col"><input type="checkbox"></th>；列選取 aria-selected
   WCAG：1.3.1、1.4.11、2.5.8（列高 ≥34px）、1.4.10（窄螢幕卡片化，見 (5)）
   鍵盤：原生表格不可聚焦；互動元素（連結、按鈕、勾選）逐一 Tab。
         選取欄的「全選」checkbox 需支援 indeterminate（部分選取） */
.tablewrap {
  position: relative;               /* 供容器內 absolute 子元素（.sr-only 等）定位；
                                       原缺 ⇒ 這類元素逃到 viewport，撐大文件捲動高度 */
  width: 100%;
  overflow-x: auto;                 /* 1.4.10：**在容器內**捲動，不讓整頁橫向捲 */
  overscroll-behavior-x: contain;
}

.data {
  width: 100%;
  font-size: var(--t-sm);
}

.data thead th {
  position: sticky;
  top: 0;
  z-index: var(--z-sticky);
  background: var(--surface-sunken);
  border-bottom: 1px solid var(--border-default);
  padding: var(--s2) var(--s3);
  text-align: left;
  font-size: var(--t-xs);
  font-weight: var(--weight-semi);
  color: var(--text-2);
  white-space: nowrap;
}

.data tbody td {
  height: var(--density-row-h);
  padding: var(--s2) var(--s3);
  border-bottom: 1px solid var(--border-subtle);
  color: var(--text-1);
  vertical-align: middle;
}

.data tbody tr:hover { background: var(--surface-subtle); }
.data tbody tr[aria-selected="true"] { background: var(--surface-selected); }
.data tbody tr:last-child td { border-bottom: 0; }

/* ── 月曆格：7 欄等寬（2026-10-16）
   `.data` 未設 table-layout ⇒ 瀏覽器採 auto ⇒ 欄寬按**內容**分配：
   有事件膠囊的欄被撐開、其餘被壓縮（實測 1440 視窗下 7 欄出現 10 種寬度、
   5 種行高）。月曆是「固定 7 欄的網格」，不是「內容驅動的資料表」，
   故以修飾子明確固定 —— 只作用於月曆本體，不影響其他 .data 表。 */
.data--cal { table-layout: fixed; }
.data--cal > thead th,
.data--cal > tbody td { width: 14.2857%; }        /* 100 ÷ 7 */
.data--cal > tbody td {
  height: 136px;                                   /* 統一列高：--density-row-h（34／52）不敷月曆使用。
                                                      ⚠ `height` 對 <td> 只是**下限** —— 列高由該列
                                                      內容最高的格決定，故必須高到容納「日號＋單格
                                                      最多 2 個膠囊（標題各折 2 行）」才可能等齊。
                                                      實測（1440 視窗，示範資料）列高種類：
                                                      96px→4、120px→3、**136px→1**。 */
  vertical-align: top;                             /* 內容靠上；膠囊由頂端起排 */
  padding: var(--s2);
}
/* 格內膠囊不得撐破欄寬（.pill 是 inline-flex，min-width 預設 auto ⇒ 會溢出） */
.data--cal .pill {
  max-width: 100%;
  min-width: 0;
  overflow: hidden;
}
.data--cal .pill > .mono {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ── 月曆格線（縱線）＋ 非當月灰底（REQ-19-B 對標 8095；2026-10-19 canonical）
   ★ 為什麼用 `box-shadow: inset` 而不是 `border-right`：
     border 會**佔位**（每格多 1px），7 欄 ⇒ 表格總寬多 7px；在 `table-layout: fixed`
     下這 7px 只能從內容扣，膠囊的省略號會提早出現，600px 以下更可能引發橫向溢出
     （本設計系統明文禁止整頁橫向捲動）。inset 陰影畫在格子**內側**，幾何零變動。
   ★ 為什麼只作用於 `.data--cal`：縱線是月曆「格」的語義（一格一日）；
     一般資料表的橫線已足夠，給所有 `.data` 加縱線會讓 9 欄清單變成試算表。 */
.data--cal > thead th,
.data--cal > tbody td {
  box-shadow: inset -1px 0 0 var(--border-subtle);
}
/* 最後一欄不畫右線：表格外緣已有容器邊界，再畫一條會變成雙線 */
.data--cal > thead th:last-child,
.data--cal > tbody td:last-child { box-shadow: none; }

/* 非當月（前後補位空格）：灰底。
   用 `--surface-sunken`（與表頭同階）而**不是** hover／selected —— 那兩個是
   **互動態**語義，拿來表達「不屬於本月」會讓滑過的列看起來像過期資料。 */
.data--cal > tbody td.is-out { background: var(--surface-sunken); }

/* ⚠ 600px 以下走 §5.5 卡片化（`.data tbody td` 改 display:grid ＋ data-label）
   ⇒ fixed 佈局與 14.2857% 必須一併解除，否則卡片化會被固定欄寬破壞。 */
@media (max-width: 600px) {
  .data--cal { table-layout: auto; }
  .data--cal > thead th,
  .data--cal > tbody td { width: auto; }
  /* 卡片化後「格」的概念不存在（`tr` 變一張卡、`td` 變標籤＋值）
     ⇒ 縱線與灰底一併解除，否則卡內會出現無意義的內側陰影與灰塊。 */
  .data--cal > thead th,
  .data--cal > tbody td { box-shadow: none; }
  /* 補位空格沒有內容，卡片化後只會變成空白列 ⇒ 不顯示。 */
  .data--cal > tbody td.is-out { display: none; }
}

/* ── 代碼塊：原樣輸出的結構化內容（如 OMS 課程明細「下發內容」的 raw JSON）
   為什麼需要：`<pre>` 的原樣輸出若不加容器，長行會撐破版面（無法在容器內捲動），
   而整頁橫向捲動是本設計系統明文禁止的。
   ⚠ 高度上限 420px ＋ `overflow:auto` ⇒ 超長內容**在容器內**捲動，不撐高頁面。
   ⚠ `tabindex="0"`（由視圖帶）＋ `:focus-visible` ⇒ 鍵盤可用者能聚焦後捲動；
      可捲動區若不可聚焦，鍵盤使用者無法取得捲動（WCAG 2.1.1）。
   ⚠ 圓角 token 以 canonical 為準：本檔無 `--r-md`，故用 `--r2`（6px，中圓角）。 */
.codeblock {
  background: var(--surface-sunken);
  border: 1px solid var(--border-subtle);
  border-radius: var(--r2);
  padding: var(--s3);
  max-height: 420px;
  overflow: auto;
}
.codeblock:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }

/* BLK-9 規格 A：逾期列（原 courses/show 兩處 inline outline 等價搬移；data-tone 由視圖條件帶） */
tr[data-tone="overdue"] { outline: 2px solid var(--st-overdue-bd); }

/* 數字欄：等寬數字 ＋ 右對齊（第三層慣例：可比較的數值一律右對齊）*/
.col-num, .data .col-num { text-align: right; font-variant-numeric: tabular-nums; font-family: var(--font-mono); }
.col-key { width: 1%; white-space: nowrap; font-family: var(--font-mono); }
.col-check { width: 36px; text-align: center; }
.col-actions { width: 1%; white-space: nowrap; text-align: right; }

/* 排序按鈕（放在 th 內，佔滿整個表頭以維持 2.5.8）*/
.data th[aria-sort] button {
  display: inline-flex;
  align-items: center;
  gap: var(--s1);
  min-height: var(--target-min);
  color: inherit;
  font: inherit;
}

.data th[aria-sort] button::after {
  content: '↕';
  color: var(--text-4);
  font-size: var(--t-micro);
}

.data th[aria-sort="ascending"] button::after { content: '↑'; color: var(--text-1); }
.data th[aria-sort="descending"] button::after { content: '↓'; color: var(--text-1); }

/* ── 4.6 選單與下拉（帳號選單、表格工具、列動作）─────────────────────────
   APG：Menu Button（**非** menubar）。aria-haspopup="menu"／aria-expanded／
         role="menu" ＋ role="menuitem"。
   WCAG：1.4.13（可關閉：Esc 與點外部，可持續：不移開就還在）、2.4.7、2.5.8
   鍵盤：Enter／Space／↓ 開啟並聚焦首項；↑／↓ 移動；Home／End；Esc 關閉並
         把焦點還給觸發鈕（**必須**還焦，否則焦點會掉到 body） */
.menu {
  min-width: 180px;
  padding: var(--s1);
  background: var(--surface-overlay);
  border: 1px solid var(--border-subtle);
  border-radius: var(--r3);
  box-shadow: var(--sh-3);
  z-index: var(--z-dropdown);
}

.menu__item {
  display: flex;
  align-items: center;
  gap: var(--s3);
  width: 100%;
  min-height: 34px;
  padding: var(--s2) var(--s3);
  border-radius: var(--r2);
  font-size: var(--t-sm);
  color: var(--text-1);
  text-align: left;
}

.menu__item:hover { background: var(--surface-hover); }
.menu__item[aria-current="page"] { background: var(--surface-selected); font-weight: var(--weight-semi); }
.menu__sep { height: 1px; margin: var(--s1) 0; background: var(--border-subtle); }
.menu__head {
  padding: var(--s2) var(--s3) var(--s1);
  font-size: var(--t-micro);
  color: var(--text-3);
  text-transform: uppercase;
  letter-spacing: .06em;
}

/* 浮層容器（相對於觸發鈕定位）*/
.pop { position: relative; }
.pop__panel {
  position: absolute;
  top: calc(100% + 4px);
  right: 0;
  min-width: 180px;
}

/* ── 語言選單（下拉；原生 <details>，零 JS）───────────────────────────────
   取代原本的「點一下換下一個語系」循環鈕。三語下使用者無法預期下一個是哪個，
   也看不到全部選項（詳見 components/lang-menu.blade.php 的檔頭說明）。

   ⚠ 為什麼不用 `.pop` 而是自帶 `position: relative`
     觸發鈕在不同版面分別是 `.btn--ghost`（頂欄）／`.lg-lang`（登入卡）／
     `.ph-top__lang`（錯誤頁）。`<details>` 本身不是那三者的子元素，
     故定位基準放在 `<details>` 自己身上，才不會隨呼叫端版面而位移。 */
.langmenu { position: relative; display: inline-flex; }
.langmenu__trigger { gap: var(--s2, 6px); }

/* 原生三角標記去掉；箭頭用圖標（與 `.lg-dev` 同一手法） */
.langmenu__trigger::-webkit-details-marker { display: none; }
.langmenu__trigger::marker { content: ''; }

.langmenu__caret {
  font-size: 12px;
  color: var(--text-4);
  transition: transform .15s ease;
}
.langmenu[open] .langmenu__caret { transform: rotate(180deg); }

.langmenu__panel {
  min-width: 168px;
  padding: var(--s1);
  /* 面板脫離文件流 → 需自帶層級，否則會被下方的表格／卡片蓋住 */
  z-index: var(--z-dropdown);
}

.langmenu__item { justify-content: flex-start; color: var(--text-1); }
.langmenu__item:hover { background: var(--surface-hover); }
/* 目前語系：底色 ＋ 勾號（**不只靠顏色**，WCAG 1.4.1） */
.langmenu__item[aria-current="true"] {
  background: var(--surface-selected);
  font-weight: var(--weight-semi);
}
.langmenu__check { margin-left: auto; color: var(--st-done-fg); }

/* 登入卡的語言鈕原本貼在卡片標題右側；下拉面板改由卡片裁切範圍外彈出，
   故把面板右緣對齊鈕的右緣（`.pop__panel` 已是 right:0，這裡只收窄寬度）。 */
.lg-card__head .langmenu__panel { min-width: 156px; }

/* ── 4.7 狀態標籤（Chip／Tag／Badge）─────────────────────────────────────
   ARIA：純視覺用 <span>；若為可移除標籤，移除鈕需 aria-label
   WCAG：**1.4.1 不得只用顏色傳達** —— 每個 chip 前有一個圓點形狀 ＋ 文字本身，
         文字已說明狀態；顏色是輔助而非唯一手段。
   ⚠ 狀態色對比已逐項審計 ≥4.5:1 */
.chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 22px;
  padding-inline: var(--s2) 8px;
  border: 1px solid var(--border-default);
  border-radius: var(--r-pill);
  background: var(--surface-sunken);
  color: var(--st-neutral-fg);
  font-size: var(--t-xs);
  font-weight: var(--weight-medium);
  white-space: nowrap;
}

.chip::before {
  content: '';
  width: 6px; height: 6px;
  border-radius: 50%;
  background: currentColor;
  opacity: .78;
  flex: none;
}

.chip[data-tone="pending"]  { border-color: var(--st-pending-bd);  background: var(--st-pending-bg);  color: var(--st-pending-fg); }
.chip[data-tone="progress"] { border-color: var(--st-progress-bd); background: var(--st-progress-bg); color: var(--st-progress-fg); }
.chip[data-tone="done"]     { border-color: var(--st-done-bd);     background: var(--st-done-bg);     color: var(--st-done-fg); }
.chip[data-tone="return"]   { border-color: var(--st-return-bd);   background: var(--st-return-bg);   color: var(--st-return-fg); }
.chip[data-tone="overdue"]  { border-color: var(--st-overdue-bd);  background: var(--st-overdue-bg);  color: var(--st-overdue-fg); }
.chip[data-tone="neutral"]  { border-color: var(--st-neutral-bd);  background: var(--st-neutral-bg);  color: var(--st-neutral-fg); }

/* 徽章（計數）：數字必須有 aria-label 說明「什麼的數量」*/
.badge {
  display: inline-grid;
  place-items: center;
  min-width: 20px;
  height: 20px;
  padding-inline: 5px;
  border-radius: var(--r-pill);
  background: var(--c-crit-fg);
  color: var(--c-white);
  font-family: var(--font-mono);
  font-size: var(--t-micro);
  font-weight: var(--weight-semi);
  font-variant-numeric: tabular-nums;
}

.badge--mute { background: var(--surface-sunken); color: var(--text-2); }

/* ── 4.8 KPI 卡 ──────────────────────────────────────────────────────────
   ARIA：數字用 <output> 或帶 aria-label 的 span（讀屏唸得出「12 件待處理」）
   WCAG：狀態色**不是唯一線索** —— 卡上有標籤文字與圖示；左側色條只是強化 */
.kpi {
  position: relative;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  gap: var(--s1);
  padding: var(--s4) var(--s5);
  background: var(--surface-raised);
  border: 1px solid var(--border-subtle);
  border-radius: var(--r3);
}

.kpi::after {
  content: '';
  position: absolute;
  inset: 0 auto 0 0;
  width: 3px;
  background: var(--st-neutral-fg);
}

.kpi[data-tone="pending"]::after  { background: var(--st-pending-fg); }
.kpi[data-tone="progress"]::after { background: var(--st-progress-fg); }
.kpi[data-tone="done"]::after     { background: var(--st-done-fg); }
.kpi[data-tone="overdue"]::after  { background: var(--st-overdue-fg); }

.kpi__label {
  display: flex;
  align-items: center;
  gap: var(--s2);
  font-size: var(--t-xs);
  color: var(--text-3);
}

.kpi__value {
  font-size: var(--t-d1);
  font-weight: var(--weight-regular);
  line-height: var(--leading-tight);
  font-variant-numeric: tabular-nums;
  color: var(--text-1);
}

.kpi__value--muted { color: var(--text-4); font-size: 22px; font-weight: var(--weight-semi); }   /* 釘住 semi：≥18.66px 粗體 → text-4 合格（若隨父降為 400 則跌回 4.5:1 門檻） */

.kpi__foot {
  font-size: var(--t-micro);
  color: var(--text-3);
  font-family: var(--font-mono);
}

/* ── 4.9 進度與拖曳 ──────────────────────────────────────────────────────
   APG：Progressbar（role="progressbar" ＋ aria-valuenow／min／max）
   WCAG：2.5.7（拖曳動作）—— **任何拖曳都必須有非拖曳替代**。
         本系統的做法：排序／調整一律附「上移／下移」按鈕（.movebtn），
         拖曳只是加速器，不是唯一手段。 */
.progress {
  height: 6px;
  border-radius: var(--r-pill);
  background: var(--surface-sunken);
  overflow: hidden;
}

.progress__bar {
  height: 100%;
  border-radius: inherit;
  background: var(--c-amber-500);
}

.progress--done .progress__bar { background: var(--c-ok-fg); }
.progress--over .progress__bar { background: var(--c-crit-fg); }

/* 拖曳把手：視覺提示 ＋ 必須搭配可按的上移／下移鈕（2.5.7）*/
.draghandle {
  display: inline-grid;
  place-items: center;
  width: var(--target-min);
  height: var(--target-min);
  border-radius: var(--r1);
  color: var(--text-4);
  cursor: grab;
}

.draghandle:hover { background: var(--surface-hover); color: var(--text-2); }

.movebtns { display: inline-flex; gap: 2px; }
.movebtns .btn { width: var(--target-min); height: var(--target-min); padding: 0; }

/* ── 4.10 提示與反饋 ─────────────────────────────────────────────────────
   APG：Alert（靜態提示不必 aria-live；動態錯誤需 role="alert"）
   WCAG：4.1.3（狀態訊息）、1.4.11 */
.alert {
  display: flex;
  align-items: flex-start;
  gap: var(--s3);
  padding: var(--s3) var(--s4);
  border: 1px solid var(--border-default);
  border-radius: var(--r2);
  background: var(--surface-sunken);
  font-size: var(--t-sm);
  line-height: var(--leading-loose);
  color: var(--text-1);
}

.alert__body { min-width: 0; }
.alert__body > strong { display: block; margin-bottom: 2px; }

.alert[data-tone="error"] { border-color: var(--st-overdue-bd); background: var(--st-overdue-bg); color: var(--st-overdue-fg); }
.alert[data-tone="warn"]  { border-color: var(--st-pending-bd); background: var(--st-pending-bg); color: var(--st-pending-fg); }
.alert[data-tone="ok"]    { border-color: var(--st-done-bd);    background: var(--st-done-bg);    color: var(--st-done-fg); }
.alert[data-tone="info"]  { border-color: var(--st-progress-bd);background: var(--st-progress-bg);color: var(--st-progress-fg); }

.note {
  padding: var(--s3) var(--s4);
  border-left: 3px solid var(--border-brand);
  background: var(--surface-brand-soft);
  border-radius: 0 var(--r2) var(--r2) 0;
  font-size: var(--t-xs);
  color: var(--text-2);
  line-height: var(--leading-loose);
}

/* ── 4.11 通知（Toast）──────────────────────────────────────────────────
   APG：Alert Dialog 的輕量替代。ARIA：容器 role="status" aria-live="polite"
         （錯誤改 role="alert" aria-live="assertive" —— 但**不要**兩者都用，
          live region 疊加會重複播報）
   WCAG：4.1.3、1.4.13（可關閉）、2.2.1（停留時間可調 —— 提供關閉鈕即滿足）*/
.toasts {
  position: fixed;
  right: var(--s6);
  bottom: var(--s6);
  z-index: var(--z-toast);
  display: flex;
  flex-direction: column;
  gap: var(--s2);
  max-width: min(400px, calc(100vw - var(--s10)));
  pointer-events: none;              /* 空容器不吃點擊 */
}

.toast {
  pointer-events: auto;
  display: flex;
  align-items: flex-start;
  gap: var(--s3);
  padding: var(--s3) var(--s4);
  border-radius: var(--r3);
  background: var(--surface-inverse);
  color: var(--c-white);
  font-size: var(--t-sm);
  line-height: var(--leading-loose);
  box-shadow: var(--sh-4);
  animation: ds-toast-in var(--dur-slow) var(--ease) both;
}

.toast[data-tone="ok"]    { background: var(--c-ok-fg); }
.toast[data-tone="error"] { background: var(--c-crit-fg); }

.toast__close {
  margin-left: auto;
  flex: none;
  width: var(--target-min);
  height: var(--target-min);
  display: grid;
  place-items: center;
  border-radius: var(--r1);
  color: currentColor;
  opacity: .8;
}

.toast__close:hover { opacity: 1; background: rgb(255 255 255 / 14%); }

@keyframes ds-toast-in { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }

/* ── 4.12 對話框 ────────────────────────────────────────────────────────
   APG：Modal Dialog（確認型用 Alert Dialog）。
   ARIA：<dialog> ＋ showModal()（平台負責焦點鎖定與背景 inert）。
        aria-labelledby 指向標題；破壞性動作的確認鈕需明確動詞（不用「確定」）
   WCAG：2.1.2（無鍵盤陷阱 —— 原生 dialog 已滿足）、2.4.7、1.4.13
   鍵盤：Esc 關閉（原生）；Tab 循環在框內（原生）；開啟時焦點進第一個可操作項 */
.confirm {
  border: 0;
  padding: 0;
  background: transparent;
  max-width: min(460px, calc(100vw - var(--s8)));
}

.confirm::backdrop { background: rgb(20 22 26 / 56%); backdrop-filter: blur(1px); }

.confirm__box {
  background: var(--surface-overlay);
  border-radius: var(--r4);
  padding: var(--s6);
  box-shadow: var(--sh-4);
}

.confirm__title { font-size: var(--t-h3); font-weight: var(--weight-semi); color: var(--text-1); margin-bottom: var(--s2); }
.confirm__message { font-size: var(--t-sm); color: var(--text-2); line-height: var(--leading-loose); }
.confirm__actions { display: flex; justify-content: flex-end; gap: var(--s2); margin-top: var(--s6); }

/* ── 4.13 抽屜（Drawer／Side Panel）────────────────────────────────────
   APG：Modal Dialog 的變體（非模態時不用 dialog 元素，改 aside ＋ inert 管理）
   ARIA：role="dialog" aria-modal="true" aria-labelledby；或非模態時 aside
   WCAG：2.1.2、2.4.7、1.4.13
   鍵盤：Esc 關閉；Tab 鎖在抽屜內；關閉後焦點回到開啟鈕 */
.drawer {
  position: fixed;
  inset: 0 0 0 auto;
  width: min(520px, 100vw);
  display: flex;
  flex-direction: column;
  background: var(--surface-overlay);
  border-left: 1px solid var(--border-subtle);
  box-shadow: var(--sh-4);
  z-index: var(--z-modal);
  animation: ds-drawer-in var(--dur-slow) var(--ease) both;
}

@keyframes ds-drawer-in { from { transform: translateX(16px); opacity: 0; } to { transform: none; opacity: 1; } }

.drawer__head {
  display: flex;
  align-items: center;
  gap: var(--s3);
  padding: var(--s4);
  border-bottom: 1px solid var(--border-subtle);
  flex: none;
}

.drawer__title { font-size: var(--t-md); font-weight: var(--weight-semi); }
.drawer__body { flex: 1; overflow-y: auto; padding: var(--s4); }
.drawer__foot { flex: none; padding: var(--s4); border-top: 1px solid var(--border-subtle); display: flex; justify-content: flex-end; gap: var(--s2); }

/* 遮罩（抽屜／窄螢幕導覽共用）：純裝飾，aria-hidden。
   APG：Dialog (Modal) —— 模態期間遮罩可點擊關閉；另有鍵盤可及的關閉鈕（不可只靠點遮罩）。

   ⚠ 兩者的**顯示時機不同，因此不可共用 display**：
     · .scrim      由 app.js 在抽屜開啟時動態插入、關閉時移除 → 存在期間即可見；
     · .nav-scrim  永遠在 DOM 裡 → 預設必須隱藏，只在窄螢幕且側欄展開時顯示。

   這裡曾經把兩者寫成同一條規則**且漏了 display**，於是桌機（>900px，不套用
   下方 @media）上 .nav-scrim 以 position:fixed; inset:0 蓋住整頁並吃掉所有點擊
   —— 整個後台在桌機上完全無法操作。這個缺陷躲過了先前所有自動驗證，因為那些
   驗證只比對 HTTP 狀態、HTML 結構與樣式，**沒有真的點過任何東西**。 */
.scrim, .nav-scrim {
  position: fixed;
  inset: 0;
  background: rgb(20 22 26 / 48%);
  z-index: var(--z-scrim);
  border: 0;
}

.scrim { display: block; }     /* 僅在抽屜開啟期間存在（JS 動態插入／移除） */
.nav-scrim { display: none; }  /* 預設不出現；見 @media (max-width: 900px) */

/* ── 使用引導浮窗（REQ-24-B）────────────────────────────────────────────
   節點**只在引導啟動時**由 JS 插入 ⇒ 未啟動時頁面 DOM 與沒有這一段時相同
   （版面量測因此不必重跑）。故本區全部規則都是「節點存在才有意義」。
   ⚠ 覆蓋性質的規則一律**明示 `display`**（稽核 E9）。
   ⚠ z-index 取 `--z-modal`（60）：高於 sticky 10／topbar 20／scrim 39／
      drawer 40／dropdown 50；**低於 toast 70**（通知永遠看得到）。
   ⚠ 圓角／陰影以本檔既有 token 為準（`--r3`、`--sh-4`）。 */
.tour { display: block; }

/* 擋指標事件用的透明層。**視覺上的暗化不靠這一層** —— 它若著色，會把被聚光的
   目標一起壓暗（見 `.tour__spot` 的外陰影做法）。 */
.tour__scrim {
  display: block;
  position: fixed;
  inset: 0;
  background: transparent;
  z-index: var(--z-modal);
}

/* 聚光框：自身透明（目標才看得見）＋ 巨大外陰影（框外壓暗）。
   `pointer-events: none` 不搶事件（點擊由 `.tour__scrim` 承接）。 */
.tour__spot {
  display: block;
  position: fixed;
  border-radius: var(--r2);
  box-shadow: 0 0 0 9999px rgb(20 22 26 / 48%);
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
  pointer-events: none;
  z-index: calc(var(--z-modal) + 1);
}

/* 說明氣泡。位置（left／top）由 JS 依目標的 `getBoundingClientRect()` 算好後寫入
   inline style；此處只負責外觀與「箭頭朝哪一邊」。 */
.tour__bubble {
  display: block;
  position: fixed;
  width: max-content;
  max-width: min(320px, calc(100vw - 32px));
  padding: var(--s3) var(--s4) var(--s3);
  background: var(--surface-overlay);
  border: 1px solid var(--border-subtle);
  border-radius: var(--r3);
  box-shadow: var(--sh-4);
  z-index: calc(var(--z-modal) + 2);
  animation: ds-tour-in var(--dur-fast) var(--ease) both;
}

@keyframes ds-tour-in { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }

/* 箭頭：一個旋轉 45° 的方塊，只留朝外那兩邊的邊框。
   預設（氣泡在目標**下方**）⇒ 箭頭在上緣中央。 */
.tour__bubble::after {
  content: '';
  display: block;
  position: absolute;
  width: 10px;
  height: 10px;
  left: 50%;
  top: -6px;
  margin-left: -5px;
  background: var(--surface-overlay);
  border-left: 1px solid var(--border-subtle);
  border-top: 1px solid var(--border-subtle);
  transform: rotate(45deg);
}

.tour__bubble--top::after { top: auto; bottom: -6px; transform: rotate(225deg); }
.tour__bubble--right::after { left: -6px; top: 50%; margin: -5px 0 0; transform: rotate(-45deg); }
.tour__bubble--left::after { left: auto; right: -6px; top: 50%; margin: -5px 0 0; transform: rotate(135deg); }

.tour__progress {
  margin: 0 0 var(--s1);
  color: var(--text-3);
  font-size: var(--t-xs);
  font-variant-numeric: tabular-nums;
}

.tour__title {
  margin: 0 0 var(--s1);
  color: var(--text-1);
  font-size: var(--t-md);
  font-weight: 600;
}

.tour__body {
  margin: 0 0 var(--s3);
  color: var(--text-2);
  font-size: var(--t-sm);
}

.tour__actions {
  display: flex;
  align-items: center;
  gap: var(--s2);
}

.tour__spacer { flex: 1 1 auto; }

/* 位移動畫對「減少動態」的使用者關掉（本檔既有先例，見檔頭 §2 的可及性段）。 */
@media (prefers-reduced-motion: reduce) {
  .tour__bubble { animation: none; }
}

/* ── 4.14 頁籤（內容分頁）──────────────────────────────────────────────
   APG：Tabs。ARIA：role="tablist"／"tab"／aria-selected／aria-controls／
        tabpanel 的 role="tabpanel" aria-labelledby
   WCAG：2.1.1、2.5.8
   鍵盤：← → 移動；Home／End 首末；Enter／Space 啟用（手動啟用模式）*/
.tabs {
  display: flex;
  gap: var(--s1);
  border-bottom: 1px solid var(--border-subtle);
  overflow-x: auto;
}

.tabs__tab {
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  min-height: var(--ctl-h);
  padding: 0 var(--s3);
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  font-size: var(--t-sm);
  color: var(--text-3);
  white-space: nowrap;
}

.tabs__tab:hover { color: var(--text-1); }

.tabs__tab[aria-selected="true"] {
  color: var(--text-1);
  font-weight: var(--weight-semi);
  border-bottom-color: var(--c-amber-500);
}

.tabs__tab .n {
  font-family: var(--font-mono);
  font-size: var(--t-micro);
  color: var(--text-3);
}

.tabpanel { padding-top: var(--s4); }

/* ── 4.15 麵包屑 ────────────────────────────────────────────────────────
   APG：Breadcrumb。ARIA：nav[aria-label="麵包屑"] ＋ ol；最後一項 aria-current="page"
   WCAG：2.4.8（位置）、1.4.11（分隔符對比）、2.5.8 */
.trail {
  display: flex;
  align-items: center;
  gap: var(--s2);
  font-size: var(--t-sm);
  color: var(--text-3);
}

.trail__link { min-height: var(--target-min); display: inline-flex; align-items: center; }
.trail__link:hover { color: var(--text-1); text-decoration: underline; text-underline-offset: 3px; }
.trail__sep { color: var(--text-4); }
.trail__current { color: var(--text-1); font-weight: var(--weight-medium); }

/* ── 4.16 分頁器 ────────────────────────────────────────────────────────
   APG：無專屬 pattern。ARIA：nav[aria-label="分頁"]；當前頁 aria-current="page"；
        每頁筆數與跳頁為獨立表單控制項
   WCAG：2.5.8、2.4.7
   鍵盤：Tab 逐頁；Enter 前往 */
.pager {
  display: flex;
  align-items: center;
  gap: var(--s3);
  padding: var(--s3) var(--s4);
  border-top: 1px solid var(--border-subtle);
  font-size: var(--t-xs);
  color: var(--text-3);
  flex-wrap: wrap;
}

.pager__spacer { flex: 1; }
.pager__pages { display: flex; align-items: center; gap: 2px; }

.pager__page {
  display: inline-grid;
  place-items: center;
  min-width: var(--target-min);
  min-height: var(--target-min);
  padding-inline: var(--s2);
  border: 1px solid transparent;
  border-radius: var(--r2);
  color: var(--text-2);
  font-variant-numeric: tabular-nums;
}

.pager__page:hover { background: var(--surface-hover); border-color: var(--border-subtle); }

.pager__page[aria-current="page"] {
  background: var(--c-neutral-950);
  border-color: var(--c-neutral-950);
  color: var(--c-white);
  font-weight: var(--weight-semi);
}

.pager__rows { display: inline-flex; align-items: center; gap: var(--s2); }
.pager__rows .select { width: auto; min-width: 76px; height: var(--ctl-h-sm); font-size: var(--t-xs); }

/* ── 4.17 篩選列／工具列 ────────────────────────────────────────────────
   APG：無專屬 pattern。ARIA：<form role="search"> 或 aria-label；
        進階篩選用 <details>（Disclosure，原生鍵盤支援）
   WCAG：1.4.13、2.5.8、3.3.2
   慣例：左＝篩選欄位，右＝檢視切換（欄位顯示／密度）＋ 主要動作 */
.filterbar {
  display: flex;
  align-items: flex-end;
  gap: var(--s3);
  flex-wrap: wrap;
  padding: var(--s4);
  border-bottom: 1px solid var(--border-subtle);
  background: var(--surface-raised);
}

.filterbar__fields { display: flex; align-items: flex-end; gap: var(--s3); flex-wrap: wrap; }
.filterbar__spacer { flex: 1; }
.filterbar__actions { display: flex; align-items: center; gap: var(--s2); }

.filterbar__more > summary {
  display: inline-flex;
  align-items: center;
  gap: var(--s1);
  min-height: var(--ctl-h);
  padding-inline: var(--s3);
  border: 1px solid var(--border-default);
  border-radius: var(--r2);
  font-size: var(--t-sm);
  color: var(--text-2);
}

.filterbar__more > summary:hover { background: var(--surface-hover); }

.tabletools { display: inline-flex; align-items: center; gap: var(--s2); }
.tabletools__pop > summary { display: inline-flex; }

.tabletools__panel {
  position: absolute;
  right: 0;
  top: calc(100% + 4px);
  min-width: 200px;
  max-height: 300px;
  overflow-y: auto;
  padding: var(--s3);
  background: var(--surface-overlay);
  border: 1px solid var(--border-subtle);
  border-radius: var(--r3);
  box-shadow: var(--sh-3);
  z-index: var(--z-dropdown);
}

.tabletools__row {
  display: flex;
  align-items: center;
  gap: var(--s2);
  min-height: var(--target-min);
  font-size: var(--t-sm);
  cursor: pointer;
}

.tabletools__row input { accent-color: var(--c-amber-500); flex: none; }
.tabletools__row input:focus-visible { outline: var(--focus-w) solid var(--focus-ring); outline-offset: 2px; }

/* 表格工具用 details 作彈出面板時，需要相對定位的容器 */
.pop--anchor { position: relative; }

/* ── 4.18 列內動作收納 ─────────────────────────────────────────────────
   APG：Menu Button（同上）。把次要動作收進「⋯」，但**主要動作一律留在外面**
   WCAG：2.5.8、2.4.7、1.4.13 */
.rowactions { display: inline-flex; align-items: center; gap: var(--s1); justify-content: flex-end; }
.rowactions__pop { position: relative; display: inline-flex; }
.rowactions__panel {
  position: fixed;                  /* 由 JS 定位：.tablewrap 是 overflow:auto，絕對定位會被裁掉 */
  min-width: 160px;
  padding: var(--s1);
  background: var(--surface-overlay);
  border: 1px solid var(--border-subtle);
  border-radius: var(--r3);
  box-shadow: var(--sh-3);
  z-index: var(--z-dropdown);
}

/* ── 4.19 批次選取列 ───────────────────────────────────────────────────
   APG：Toolbar（動作工具列）。ARIA：role="toolbar" aria-label；
        顯示目前選取數量需 aria-live="polite"（勾選時唸出）
   WCAG：4.1.3、2.5.8 */
.batchbar {
  display: flex;
  align-items: center;
  gap: var(--s3);
  padding: var(--s2) var(--s4);
  background: var(--surface-brand-soft);
  border-bottom: 1px solid var(--border-brand);
  font-size: var(--t-sm);
}

[data-select-mode="false"] .batchbar { display: none; }
[data-select-mode="false"] .col-check,
[data-select-mode="false"] .data .col-check { display: none; }

/* ── 4.20 帳號控制 ─────────────────────────────────────────────────────
   APG：Menu Button。ARIA：aria-haspopup="menu"／aria-expanded；選單見 §4.6
   WCAG：2.5.8、1.4.11、1.4.3
   慣例：上行角色（代碼＋名稱），下行帳號 —— 分層寫以避免被讀成兩個角色標籤 */
.userbtn {
  display: inline-flex;
  align-items: center;
  gap: var(--s3);
  min-height: var(--ctl-h);
  padding: var(--s1) var(--s2) var(--s1) var(--s1);
  border: 1px solid transparent;
  border-radius: var(--r2);
}

.userbtn:hover { background: var(--surface-hover); border-color: var(--border-subtle); }
.userbtn[aria-expanded="true"] { background: var(--surface-active); border-color: var(--border-default); }

.userbtn__avatar {
  flex: none;
  width: 28px; height: 28px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--c-neutral-950);
  color: var(--c-white);
  font-size: var(--t-xs);
  font-weight: var(--weight-semi);
}

.userbtn__id { display: flex; flex-direction: column; gap: 1px; min-width: 0; text-align: left; }
.userbtn__role { display: inline-flex; align-items: center; gap: 5px; font-size: var(--t-sm); font-weight: var(--weight-medium); color: var(--text-1); }
.userbtn__mail { font-size: var(--t-micro); color: var(--text-3); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* 角色代碼 */
.rolechip__code {
  display: inline-grid;
  place-items: center;
  height: 16px;
  padding-inline: 4px;
  border: 1px solid var(--border-brand);
  border-radius: var(--r1);
  background: var(--surface-brand-soft);
  color: var(--text-brand);
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: var(--weight-bold);
  letter-spacing: .04em;
}

/* 固定視角小標 */
.rolechip__pin {
  display: inline-flex;
  align-items: center;
  height: 16px;
  padding-inline: 5px;
  border-radius: var(--r1);
  background: var(--surface-sunken);
  color: var(--text-3);
  font-size: 10px;
  font-weight: var(--weight-medium);
}

/* ── 4.21 定義清單（鍵值陳列）──────────────────────────────────────────
   ARIA：<dl> ＋ <dt>／<dd>（語意已足夠，不需 role）
   WCAG：1.3.1 */
.defs {
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr);
  gap: var(--s2) var(--s5);
  margin: 0;
  font-size: var(--t-sm);
}

.defs dt { color: var(--text-3); }
.defs dd { margin: 0; color: var(--text-1); }

/* ── 4.22 快速入口 ─────────────────────────────────────────────────────
   WCAG：2.5.8、2.4.4（連結目的需可從文字判斷）、2.4.7 */
.shortcuts { display: flex; flex-direction: column; gap: 2px; padding: var(--s2); }

.shortcut {
  display: flex;
  align-items: center;
  gap: var(--s3);
  min-height: 38px;
  padding: var(--s2) var(--s3);
  border-radius: var(--r2);
  font-size: var(--t-sm);
  color: var(--text-1);
}

.shortcut:hover { background: var(--surface-hover); }
.shortcut[aria-current="page"] { background: var(--surface-selected); font-weight: var(--weight-semi); }
.shortcut .ph:first-child { color: var(--text-3); font-size: 16px; flex: none; }
.shortcut i.shortcut__go { margin-left: auto; color: var(--text-4); font-size: 14px; }
.shortcut:hover i.shortcut__go { color: var(--text-2); }

/* ── 4.23 步驟 ─────────────────────────────────────────────────────────
   APG：無專屬 pattern（流程指示）。ARIA：<ol> 順序語意；當前步 aria-current="step"
   WCAG：1.3.1、1.4.1（**不只靠顏色** —— 有序號與文字）*/
.steps { display: flex; flex-direction: column; gap: var(--s3); }

.step { display: flex; align-items: flex-start; gap: var(--s3); }

.step__no {
  flex: none;
  width: 22px; height: 22px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--surface-sunken);
  border: 1px solid var(--border-default);
  color: var(--text-2);
  font-family: var(--font-mono);
  font-size: var(--t-micro);
  font-weight: var(--weight-bold);
}

.step[aria-current="step"] .step__no { background: var(--c-amber-500); border-color: var(--c-amber-500); color: var(--text-on-brand); }
.step__text { font-size: var(--t-sm); color: var(--text-2); line-height: var(--leading-loose); padding-top: 1px; }

/* ── 4.24 分段控制（Segmented）─────────────────────────────────────────
   APG：Radio Group（**不是** Tabs —— 它是「選一個值」而非「切換面板」）
   ARIA：role="radiogroup" ＋ role="radio" aria-checked，或原生 radio 視覺化
   WCAG：2.5.8、1.4.11、2.1.1
   鍵盤：Tab 進入群組；← → 在同群組內移動並選取（原生 radio 行為）*/
.segmented {
  display: inline-flex;
  padding: 2px;
  border: 1px solid var(--border-default);
  border-radius: var(--r2);
  background: var(--surface-sunken);
}

.segmented__item {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  min-height: calc(var(--ctl-h-sm) - 4px);
  padding-inline: var(--s3);
  border-radius: var(--r1);
  font-size: var(--t-xs);
  color: var(--text-2);
}

.segmented__item[aria-checked="true"], .segmented__item[aria-pressed="true"] {
  background: var(--surface-raised);
  border: 1px solid var(--border-subtle);
  color: var(--text-1);
  font-weight: var(--weight-semi);
}


/* ── 4.28 帳號下拉選單 ──────────────────────────────────────────────────
   APG：Menu Button 的面板（見 §4.6）。
   ⚠ 定位由**元件**負責，不寫在標籤上 —— 版面只有一處定義，改位置不必逐頁找。 */
.usermenu {
  position: absolute;
  top: calc(var(--topbar-h) - 4px);
  right: var(--s6);
  width: 240px;
  z-index: var(--z-dropdown);
  padding: var(--s1);
}

.usermenu__item {
  display: flex;
  align-items: center;
  gap: var(--s3);
  width: 100%;
  min-height: 34px;
  padding: var(--s2) var(--s3);
  border-radius: var(--r2);
  color: var(--text-1);
  font-size: var(--t-sm);
  text-align: left;
}

.usermenu__item:hover { background: var(--surface-hover); }

/* ── 4.29 通用排版小件 ──────────────────────────────────────────────────
   這些原本以 inline style 逐處寫在 Blade 上（稽核 E4）。收進來之後，
   版面調整只改一處，且能被設計系統的版本管理涵蓋。 */
.entrycard { width: 100%; max-width: 460px; }
.entrycard__title { font-size: var(--t-h2); margin: 0 0 var(--s3); }
.entrycard__lead { font-size: var(--t-sm); margin: 0 0 var(--s4); color: var(--text-3); }
.entrycard__body { font-size: var(--t-sm); line-height: var(--leading-loose); margin: 0 0 var(--s6); color: var(--text-2); }

.note--sm { font-size: var(--t-micro); line-height: 1.9; margin: var(--s4) 0 0; }

.p--flush { margin: 0; }
.p--flush-sm { margin: 0; font-size: var(--t-sm); line-height: var(--leading-loose); }

.field--gap { margin-bottom: var(--s4); }
.field--gap-lg { margin-bottom: var(--s5); }
.alert--gap { margin-bottom: var(--s5); }

/* 公告清單的單則項目（.card__body 內的堆疊，非首項加上分隔線）*/
.noticeitem { display: flex; flex-direction: column; gap: var(--s2); }
.noticeitem--sep { padding-top: var(--s4); margin-top: var(--s4); border-top: 1px solid var(--border-subtle); }

/* ── 4.30 設計系統預覽頁（/palette）──────────────────────────────────────
   ⚠ 色票的底色為什麼用**屬性選擇器**而不是 inline style：
     inline style 會違反本規範（稽核 E4），而設計系統自己的展示頁更沒有理由違反它。
     一則規則對一個 token，生成來源就是上面的 token 清單，改色時不必動這一頁。 */
.pal { width: min(880px, 100%); display: grid; gap: var(--s6); padding-top: var(--s2); }
.pal h1 { font-size: var(--t-h1); margin: 0; }
.pal h2 { font-size: var(--t-h3); margin: 0 0 var(--s4); }
.pal .lead { color: var(--text-3); font-size: var(--t-sm); line-height: var(--leading-loose); margin: var(--s2) 0 0; }
.pal__eyebrow {
  font-family: var(--font-mono); font-size: var(--t-micro); letter-spacing: .14em;
  color: var(--text-3); margin: 0 0 var(--s2); text-transform: uppercase;
}
.pal__src {
  margin: var(--s4) 0 0; font-size: var(--t-xs); line-height: var(--leading-loose);
  color: var(--text-2); padding: var(--s3) var(--s4);
  background: var(--surface-subtle); border-radius: var(--r2);
}
.pal__hint { font-size: var(--t-xs); color: var(--text-3); margin: 0 0 var(--s4); }

.sw { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: var(--s3); }
.sw__item { display: flex; align-items: flex-start; gap: var(--s3); }
.sw__chip { flex: none; width: 34px; height: 34px; border-radius: var(--r2); border: 1px solid var(--border-default); }
.sw__meta { min-width: 0; }
.sw__name { font-family: var(--font-mono); font-size: var(--t-xs); color: var(--text-1); }
.sw__hex { font-family: var(--font-mono); font-size: var(--t-micro); color: var(--text-3); text-transform: uppercase; }
.sw__use { font-size: var(--t-micro); color: var(--text-3); line-height: var(--leading-loose); }

.st-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: var(--s3); }
.st-cell {
  display: grid; gap: var(--s2); justify-items: start;
  padding: var(--s3); border: 1px solid var(--border-subtle); border-radius: var(--r2);
  background: var(--surface-subtle);
}
.st-cell__row { display: flex; align-items: center; gap: var(--s2); font-size: var(--t-micro); color: var(--text-3); }
.st-cell__k { font-family: var(--font-mono); color: var(--text-2); min-width: 18px; }
.st-dot { width: 14px; height: 14px; border-radius: var(--r1); border: 1px solid var(--border-default); flex: none; }

/* 以下由 token 清單生成：每個可顯示為色票的 token 一則 */
.sw__chip[data-var="--c-amber-100"], .st-dot[data-var="--c-amber-100"] { background: var(--c-amber-100); }
.sw__chip[data-var="--c-amber-200"], .st-dot[data-var="--c-amber-200"] { background: var(--c-amber-200); }
.sw__chip[data-var="--c-amber-300"], .st-dot[data-var="--c-amber-300"] { background: var(--c-amber-300); }
.sw__chip[data-var="--c-amber-400"], .st-dot[data-var="--c-amber-400"] { background: var(--c-amber-400); }
.sw__chip[data-var="--c-amber-500"], .st-dot[data-var="--c-amber-500"] { background: var(--c-amber-500); }
.sw__chip[data-var="--c-amber-600"], .st-dot[data-var="--c-amber-600"] { background: var(--c-amber-600); }
.sw__chip[data-var="--c-amber-700"], .st-dot[data-var="--c-amber-700"] { background: var(--c-amber-700); }
.sw__chip[data-var="--c-amber-800"], .st-dot[data-var="--c-amber-800"] { background: var(--c-amber-800); }
.sw__chip[data-var="--focus-w"], .st-dot[data-var="--focus-w"] { background: var(--focus-w); }
.sw__chip[data-var="--focus-offset"], .st-dot[data-var="--focus-offset"] { background: var(--focus-offset); }
.sw__chip[data-var="--surface-canvas"], .st-dot[data-var="--surface-canvas"] { background: var(--surface-canvas); }
.sw__chip[data-var="--surface-raised"], .st-dot[data-var="--surface-raised"] { background: var(--surface-raised); }
.sw__chip[data-var="--surface-sunken"], .st-dot[data-var="--surface-sunken"] { background: var(--surface-sunken); }
.sw__chip[data-var="--surface-subtle"], .st-dot[data-var="--surface-subtle"] { background: var(--surface-subtle); }
.sw__chip[data-var="--surface-overlay"], .st-dot[data-var="--surface-overlay"] { background: var(--surface-overlay); }
.sw__chip[data-var="--surface-inverse"], .st-dot[data-var="--surface-inverse"] { background: var(--surface-inverse); }
.sw__chip[data-var="--surface-hover"], .st-dot[data-var="--surface-hover"] { background: var(--surface-hover); }
.sw__chip[data-var="--surface-active"], .st-dot[data-var="--surface-active"] { background: var(--surface-active); }
.sw__chip[data-var="--surface-brand-soft"], .st-dot[data-var="--surface-brand-soft"] { background: var(--surface-brand-soft); }
.sw__chip[data-var="--surface-selected"], .st-dot[data-var="--surface-selected"] { background: var(--surface-selected); }
.sw__chip[data-var="--text-1"], .st-dot[data-var="--text-1"] { background: var(--text-1); }
.sw__chip[data-var="--text-2"], .st-dot[data-var="--text-2"] { background: var(--text-2); }
.sw__chip[data-var="--text-3"], .st-dot[data-var="--text-3"] { background: var(--text-3); }
.sw__chip[data-var="--text-4"], .st-dot[data-var="--text-4"] { background: var(--text-4); }
.sw__chip[data-var="--text-inverse"], .st-dot[data-var="--text-inverse"] { background: var(--text-inverse); }
.sw__chip[data-var="--text-brand"], .st-dot[data-var="--text-brand"] { background: var(--text-brand); }
.sw__chip[data-var="--text-link"], .st-dot[data-var="--text-link"] { background: var(--text-link); }
.sw__chip[data-var="--text-on-brand"], .st-dot[data-var="--text-on-brand"] { background: var(--text-on-brand); }
.sw__chip[data-var="--border-subtle"], .st-dot[data-var="--border-subtle"] { background: var(--border-subtle); }
.sw__chip[data-var="--border-default"], .st-dot[data-var="--border-default"] { background: var(--border-default); }
.sw__chip[data-var="--border-strong"], .st-dot[data-var="--border-strong"] { background: var(--border-strong); }
.sw__chip[data-var="--border-brand"], .st-dot[data-var="--border-brand"] { background: var(--border-brand); }
.sw__chip[data-var="--border-focus"], .st-dot[data-var="--border-focus"] { background: var(--border-focus); }
.sw__chip[data-var="--focus-ring"], .st-dot[data-var="--focus-ring"] { background: var(--focus-ring); }
.sw__chip[data-var="--focus-ring-on-dark"], .st-dot[data-var="--focus-ring-on-dark"] { background: var(--focus-ring-on-dark); }
.sw__chip[data-var="--st-done-fg"], .st-dot[data-var="--st-done-fg"] { background: var(--st-done-fg); }
.sw__chip[data-var="--st-done-bg"], .st-dot[data-var="--st-done-bg"] { background: var(--st-done-bg); }
.sw__chip[data-var="--st-done-bd"], .st-dot[data-var="--st-done-bd"] { background: var(--st-done-bd); }
.sw__chip[data-var="--st-pending-fg"], .st-dot[data-var="--st-pending-fg"] { background: var(--st-pending-fg); }
.sw__chip[data-var="--st-pending-bg"], .st-dot[data-var="--st-pending-bg"] { background: var(--st-pending-bg); }
.sw__chip[data-var="--st-pending-bd"], .st-dot[data-var="--st-pending-bd"] { background: var(--st-pending-bd); }
.sw__chip[data-var="--st-progress-fg"], .st-dot[data-var="--st-progress-fg"] { background: var(--st-progress-fg); }
.sw__chip[data-var="--st-progress-bg"], .st-dot[data-var="--st-progress-bg"] { background: var(--st-progress-bg); }
.sw__chip[data-var="--st-progress-bd"], .st-dot[data-var="--st-progress-bd"] { background: var(--st-progress-bd); }
.sw__chip[data-var="--st-overdue-fg"], .st-dot[data-var="--st-overdue-fg"] { background: var(--st-overdue-fg); }
.sw__chip[data-var="--st-overdue-bg"], .st-dot[data-var="--st-overdue-bg"] { background: var(--st-overdue-bg); }
.sw__chip[data-var="--st-overdue-bd"], .st-dot[data-var="--st-overdue-bd"] { background: var(--st-overdue-bd); }
.sw__chip[data-var="--st-return-fg"], .st-dot[data-var="--st-return-fg"] { background: var(--st-return-fg); }
.sw__chip[data-var="--st-return-bg"], .st-dot[data-var="--st-return-bg"] { background: var(--st-return-bg); }
.sw__chip[data-var="--st-return-bd"], .st-dot[data-var="--st-return-bd"] { background: var(--st-return-bd); }
.sw__chip[data-var="--st-neutral-fg"], .st-dot[data-var="--st-neutral-fg"] { background: var(--st-neutral-fg); }
.sw__chip[data-var="--st-neutral-bg"], .st-dot[data-var="--st-neutral-bg"] { background: var(--st-neutral-bg); }
.sw__chip[data-var="--st-neutral-bd"], .st-dot[data-var="--st-neutral-bd"] { background: var(--st-neutral-bd); }

/* ============================================================================
 * §4.25–4.27 設計系統自身的頁面
 * ----------------------------------------------------------------------------
 * 這三組樣式原本分別寫在 login.blade.php／entry.blade.php／palette.blade.php 的
 * **內嵌 <style> 區塊**裡。依規範「視圖不得內嵌樣式」，改由設計系統統一維護：
 *   ① 讓登入頁也吃到設計系統的版本管理（改配色時不會漏掉它）；
 *   ② 新增系統時不必再抄一份登入頁樣式；
 *   ③ 稽核（E1）得以保持**零例外** —— 有例外清單的規範最後都會被繞過。
 * ==========================================================================*/

/* ── §4.25 登入頁（.lg-*） ──────────────────────────────────────── */
/* 登入頁：左品牌（品牌黃底）／右表單（黑底 ＋ 白卡）。窄螢幕改單欄。 */
    .lg-wrap { min-height: 100vh; display: grid; grid-template-columns: minmax(320px, 38%) 1fr; }
    /* ⚠ 這裡的 .lg-brand 一定要寫成 `.lg-wrap .lg-brand`（兩層特異性）：
       本媒體查詢位於 §4.25 開頭，而 `.lg-brand { display: flex }` 在它**之後**（源序更晚），
       同特異性下後者勝 —— 若只寫 `.lg-brand`，品牌區在小螢幕**永遠不會隱藏**，
       375px 手機上會把登入卡往下頂出可視範圍（2026-10-05 實測 bodyTop=267.5、卡片下緣溢出 389px）。
       提高為兩層特異性後即不受源序影響。 */
    @media (max-width: 880px) { .lg-wrap { grid-template-columns: 1fr; } .lg-wrap .lg-brand { display: none; } }

    /* 左：品牌黃底。頂部為淺黃 STJOHN 字樣（橫向撐滿、上緣齊頂），
       徽章與機構名置於其下剩餘空間的中央。 */
    .lg-brand {
      position: relative; overflow: hidden;
      background: var(--c-amber-400);
      display: flex; flex-direction: column;
      container-type: inline-size;
    }

    /* 頂部字樣：六個字母以 space-between 撐滿整幅；字級綁定容器寬度，
       任何寬度都維持同一視覺密度 */
    .lg-brand__wordmark {
      display: flex; justify-content: space-between; align-items: flex-start;
      width: 100%; padding: 0 .025em;
      font-family: var(--font-wordmark);
      font-weight: 900;
      line-height: .78;
      font-size: clamp(44px, 7.7vw, 160px);
      color: var(--c-amber-800);
      text-transform: uppercase;
      user-select: none; pointer-events: none;
    }
    @supports (font-size: 1cqw) {
      .lg-brand__wordmark { font-size: clamp(44px, 21.5cqw, 160px); }
    }
    .lg-brand__wordmark span { display: block; }

    .lg-brand__inner {
      flex: 1;
      width: 100%; max-width: 300px;
      margin-inline: auto;
      display: grid; gap: var(--s5); justify-items: center; text-align: center;
      align-content: center;
      padding: var(--s8) var(--s8) var(--s10);
    }
    .lg-brand__lockup { display: grid; justify-items: center; gap: var(--s3); }
    /* 徽章本體：只給 max-height、寬度自動等比縮放，故不會變形。 */
    .lg-brand__emblem { display: block; width: auto; height: auto; max-height: 82px; max-width: 100%; }
    /* 機構名：中文 Light、英文 Bold。 */
    .lg-brand__org { display: grid; gap: 5px; }
    .lg-brand__org-cn {
      font-family: var(--font-wordmark);
      font-size: var(--t-h3); font-weight: 300; color: #23180a; letter-spacing: .07em;
    }
    .lg-brand__org-en {
      font-family: var(--font-wordmark);
      font-size: var(--t-sm); font-weight: 700; color: #23180a; letter-spacing: .002em;
    }
    .lg-brand__divider { width: 44px; height: 2px; background: rgba(35, 24, 10, .3); }
    .lg-brand__name { display: grid; gap: 6px; justify-items: center; }
    .lg-brand__name-en {
      font-family: var(--font-wordmark);
      font-size: 22px; font-weight: 700; letter-spacing: .03em; text-indent: .03em;
      color: #23180a;
    }
    .lg-brand__name-cn {
      font-family: var(--font-wordmark);
      font-size: var(--t-sm); font-weight: 300; letter-spacing: .34em; text-indent: .34em;
      color: rgba(35, 24, 10, .70);
    }

    /* 右：黑底。底層為漸層，其上疊氛圍照，最上才是白卡。 */
    .lg-panel { background: #14161a; display: flex; flex-direction: column; }
    .lg-body {
      position: relative; flex: 1; display: grid; place-items: center;
      padding: var(--s8) var(--s8) var(--s10);
      background:
        radial-gradient(140% 100% at 86% 0%, rgba(254, 186, 20, .06), rgba(254, 186, 20, 0) 62%),
        radial-gradient(150% 110% at 0% 100%, rgba(122, 152, 192, .05), rgba(122, 152, 192, 0) 66%),
        linear-gradient(180deg, #191c21 0%, #131519 56%, #0f1114 100%);
    }
    /* 氛圍照：上緣與下緣各壓一段暗幕，讓照片只在畫面中段透出來。 */
    .lg-photo {
      position: absolute; inset: 0;
      background: url("../img/login-scene.webp") center 38% / cover no-repeat;
    }
    .lg-photo::after {
      content: ""; position: absolute; inset: 0;
      background: linear-gradient(180deg,
        rgba(16, 18, 22, .90) 0%, rgba(16, 18, 22, .44) 26%,
        rgba(16, 18, 22, .30) 50%, rgba(14, 16, 20, .66) 78%,
        rgba(11, 13, 17, .96) 100%);
    }
    .lg-card {
      position: relative; z-index: 1;   /* 蓋在氛圍照之上 */
      width: 100%; max-width: 420px;
      background: var(--surface-raised); border-radius: var(--r3);
      padding: var(--s8);
      box-shadow:
        0 1px 2px rgba(0, 0, 0, .28),
        0 16px 36px -12px rgba(0, 0, 0, .60),
        0 44px 84px -26px rgba(0, 0, 0, .58);
    }
    /* 標題列：左「登入」、右語言切換 */
    .lg-card__head { display: flex; align-items: center; justify-content: space-between; gap: var(--s4); }
    .lg-card h1 { font-size: var(--t-h1); margin: 0; }
    .lg-lang {
      display: inline-flex; align-items: center; gap: 6px; flex: none;
      height: 28px; padding: 0 var(--s3);
      border: 1px solid var(--border-default); border-radius: var(--r-pill);
      font-size: var(--t-xs); font-weight: 600; letter-spacing: .05em;
      color: var(--text-2); text-decoration: none;
      transition: border-color .15s ease, background .15s ease, color .15s ease;
    }
    .lg-lang i { font-size: 14px; line-height: 1; }
    .lg-lang:hover { border-color: var(--c-amber-500); background: var(--c-amber-100); color: var(--c-amber-700); }
    .lg-card__lead { color: var(--text-3); font-size: var(--t-sm); margin: var(--s2) 0 var(--s6); }
    .lg-row { display: flex; align-items: center; justify-content: space-between; gap: var(--s3); flex-wrap: wrap; margin: var(--s4) 0 var(--s5); }
    /* ★ 2026-10-27（公開頁審計）：`.lg-check` 原本沒有 `min-height` ⇒ 可點區就是裡面那個
       15×15 的 `<input>`（WCAG 2.2 SC 2.5.8 要求 24×24）。實測 `below24: 1`。
       修法＝**只把 label 的命中區撐到 24**（`min-height: var(--target-min)`），
       **視覺不動**：勾選框仍是 15px、字級與間距不變。
       ⚠ 不要改成放大 `input` 的 width/height —— 那會改視覺，也不是本條要修的目標尺寸問題。
       ⚠ 量測端以「包住 input 的 label」為命中區（`ui-budget.mjs` 的 `effectiveRect`）：
         label ≥24 即算合規，與 `.checkbox`／`.radio` 的既定做法一致。 */
    .lg-check { display: inline-flex; align-items: center; gap: var(--s2); min-height: var(--target-min); font-size: var(--t-sm); color: var(--text-2); }
    .lg-check input { width: 15px; height: 15px; accent-color: var(--c-amber-500); }
    .lg-submit { width: 100%; height: 40px; font-size: var(--t-body); }

    /* 演示快速登入（僅 local／testing）：預設收起，避免影響正式介面的觀感；
       展開後帳號與密碼同列呈現，點擊任一列即填入兩欄（同參考站 8095）。 */
    .lg-dev { margin-top: var(--s6); border-top: 1px dashed var(--border-default); }
    .lg-dev__summary {
      display: flex; align-items: center; gap: var(--s2);
      padding: var(--s5) 0 var(--s1);
      cursor: pointer; list-style: none;
      font-size: var(--t-sm); font-weight: 600; color: var(--text-2);
    }
    .lg-dev__summary::-webkit-details-marker { display: none; }
    .lg-dev__summary:hover { color: var(--text-1); }
    .lg-dev__summary > i:first-child { color: var(--c-amber-700); }
    .lg-dev__caret { margin-left: auto; color: var(--text-4); transition: transform .15s ease; }
    .lg-dev[open] .lg-dev__caret { transform: rotate(180deg); }
    /* hint 用 --text-3（輔助文字，小字下限）而非 --text-4：
       --text-4 的註解明寫「僅大字或非文字」，而這裡是 11px 正文 ——
       用 --text-4 對白卡只有 3.04:1，未達 WCAG AA 的 4.5:1（2026-10-05 實測）。
       ⚠ 圖標（.lg-dev__caret）用 --text-4 是合規的，別一起改。 */
    .lg-dev__hint { font-size: var(--t-micro); color: var(--text-3); }
    .lg-dev__grid { display: grid; gap: var(--s2); margin-top: var(--s3); }
    /* 帳號列：兩行結構 —— 第一行角色名（跨滿），第二行「電郵（左）＋ 密碼（右）」。
       原本是單行三欄 minmax(118px,auto) 1fr auto，420px 卡內空間不足，
       電郵欄被擠成兩行、與其他欄錯位 10px（2026-10-05 實測 mailH=38.4、codeTop≠mailTop）。
       改兩行後各欄都有完整寬度，電郵過長時以省略號收尾、絕不折行。
       第二欄用 minmax(0, auto) 而非 auto：`auto` 會被內容撐開（超長密碼會溢出卡片），
       minmax(0,…) 允許收縮到 0 並配合 ellipsis。 */
    .lg-dev__btn {
      display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, auto); align-items: center;
      gap: 3px var(--s3);
      padding: var(--s2) var(--s3); border: 1px solid var(--border-default); border-radius: var(--r2);
      background: var(--surface-raised); cursor: pointer; font-family: inherit; text-align: left;
    }
    .lg-dev__btn:hover { border-color: var(--c-amber-500); background: var(--c-amber-100); }
    .lg-dev__btn:focus-visible { outline: 2px solid var(--c-amber-500); outline-offset: 2px; }
    .lg-dev__code {
      grid-column: 1 / -1;                       /* 角色名獨占第一行 */
      font-family: var(--font-mono); font-weight: 700; color: var(--c-amber-700); font-size: var(--t-micro);
      min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    }
    .lg-dev__mail {
      font-family: var(--font-mono); font-size: var(--t-xs); color: var(--text-2);
      min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    }
    /* 密碼：同 hint 的理由，用 --text-3（11px 正文須達 AA）。
       另加 ellipsis —— 超長密碼原會把卡片撐破（2026-10-05 實測）。 */
    .lg-dev__pwd {
      font-family: var(--font-mono); font-size: var(--t-micro); color: var(--text-3);
      min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    }
    /* 登入模式切換（REQ-24-C，僅該系統傳 $loginModeToggle=true 時才存在）：
       置於「測試提示」標題列，切「演示」⇒ 登入後帶 ?tour=1。
       點它不連帶開合清單（頁內 JS 以 stopPropagation 攔在這一層）。
       ⚠ 只用既有 token；選中態用文字色表達，不另加圖標（E13：圖標只用既有者）。 */
    .lg-dev__modes {
      display: inline-flex; align-items: center; gap: 2px; flex: none;
      margin-left: var(--s2); padding: 2px;
      border: 1px solid var(--border-default); border-radius: var(--r-pill);
      background: var(--surface-sunken);
    }
    .lg-dev__mode { display: inline-flex; align-items: center; gap: 4px; }
    .lg-dev__mode input { width: 11px; height: 11px; margin: 0; accent-color: var(--c-amber-500); }
    .lg-dev__mode label {
      font-size: var(--t-micro); font-weight: 600; color: var(--text-3);
      white-space: nowrap; cursor: pointer;
    }
    .lg-dev__mode:hover label { color: var(--text-1); }
    .lg-dev__mode input:checked + label { color: var(--c-amber-700); }
    .lg-dev__mode input:focus-visible { outline: 2px solid var(--c-amber-500); outline-offset: 2px; }

    /* 進場動效：品牌字樣 → 品牌區 → 氛圍照 → 表單卡依序淡入／微幅上移。
       幅度刻意壓小；尊重系統的「減少動態」偏好。 */
    @keyframes lg-fade { from { opacity: 0; } to { opacity: 1; } }
    @keyframes lg-rise { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: none; } }
    @keyframes lg-pop { from { opacity: 0; transform: translateY(16px) scale(.986); } to { opacity: 1; transform: none; } }

    .lg-brand__wordmark { animation: lg-fade .9s ease .05s both; }
    .lg-brand__inner { animation: lg-rise .8s cubic-bezier(.22, .68, .28, 1) .12s both; }
    .lg-photo { animation: lg-fade 1.2s ease .10s both; }
    .lg-card { animation: lg-pop .72s cubic-bezier(.22, .68, .28, 1) .18s both; }

    @media (prefers-reduced-motion: reduce) {
      .lg-brand__wordmark, .lg-brand__inner, .lg-photo, .lg-card { animation: none; }
    }

/* ── §4.26 入口外框（.ph-*） ─────────────────────────────────────── */
/* 占位內頁的最小外框：與登入頁共用同一套 app.css design token。
       PRD 的第一個內頁接手之後，這裡換成正式的 app shell。 */
    .ph-page { min-height: 100vh; display: grid; place-items: center; padding: var(--s8) var(--s5); background: var(--surface-canvas); }
    .ph-top {
      position: fixed; inset: 0 0 auto 0; height: 52px;
      display: flex; align-items: center; justify-content: space-between;
      gap: var(--s4); padding: 0 var(--s5);
      background: var(--surface-raised); border-bottom: 1px solid var(--border-subtle);
      font-size: var(--t-sm);
    }
    .ph-top__brand { font-weight: 700; letter-spacing: .02em; }
    .ph-top__lang {
      display: inline-flex; align-items: center; gap: 6px;
      height: 26px; padding: 0 var(--s3);
      border: 1px solid var(--border-default); border-radius: var(--r-pill);
      font-size: var(--t-xs); font-weight: 600; color: var(--text-2); text-decoration: none;
    }
    .ph-top__lang:hover { border-color: var(--c-amber-500); background: var(--c-amber-100); color: var(--c-amber-700); }

/* ── §4.27 設計系統預覽頁（.pal-*／.sw-*） ───────────────────────────── */
.ph-page { place-items: start center; }
  .pal { width: min(880px, 100%); display: grid; gap: var(--s6); padding-top: var(--s2); }
  .pal h1 { font-size: var(--t-h1); margin: 0; }
  .pal .lead { color: var(--text-3); font-size: var(--t-sm); line-height: 1.8; margin: var(--s2) 0 0; }
  .pal h2 { font-size: var(--t-h3); font-family: var(--font-sans); margin: 0 0 var(--s3); }
  .sw { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: var(--s3); }
  .sw__item { border: 1px solid var(--border-subtle); border-radius: var(--r2); overflow: hidden; background: var(--surface-raised); }
  .sw__chip { height: 52px; border-bottom: 1px solid var(--border-subtle); }
  .sw__meta { padding: var(--s2) var(--s3); }
  .sw__name { font-family: var(--font-mono); font-size: var(--t-micro); color: var(--text-3); }
  .sw__hex { font-family: var(--font-mono); font-size: var(--t-xs); color: var(--text-1); margin-top: 2px; }
  .sw__use { font-size: var(--t-micro); color: var(--text-4); margin-top: 2px; line-height: 1.5; }
  .st-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: var(--s3); }
  .st-cell { border: 1px solid var(--border-subtle); border-radius: var(--r2); padding: var(--s3); display: grid; gap: var(--s2); }
  .st-cell__row { display: flex; align-items: center; gap: var(--s2); font-family: var(--font-mono); font-size: var(--t-micro); }
  .st-dot { width: 14px; height: 14px; border-radius: 4px; border: 1px solid rgba(0,0,0,.08); flex: none; }
  .demo { display: flex; flex-wrap: wrap; gap: var(--s3); align-items: center; }
  .src { font-family: var(--font-mono); font-size: var(--t-xs); color: var(--text-3); background: var(--surface-sunken); border: 1px solid var(--border-subtle); border-radius: var(--r2); padding: var(--s3) var(--s4); line-height: 1.9; }


/* ============================================================================
 * §5 複合模式（四態 ＋ 常用組合）
 * ==========================================================================*/

/* ── 5.1 空狀態 ────────────────────────────────────────────────────────
   WCAG：1.1.1（裝飾圖示 aria-hidden）、1.3.1
   慣例：圖示 ＋ 一句話說明 ＋ **一個行動**（不要只說「沒有資料」）*/
.empty {
  padding: var(--s12) var(--s6);
  text-align: center;
  color: var(--text-3);
}

.empty__icon { font-size: 34px; color: var(--text-4); margin-bottom: var(--s3); }
.empty__title { font-family: var(--font-serif); font-size: var(--t-h3); color: var(--text-1); }
.empty__desc { font-size: var(--t-sm); margin-top: var(--s1); max-width: 46ch; margin-inline: auto; line-height: var(--leading-loose); }
.empty__actions { margin-top: var(--s4); display: flex; gap: var(--s2); justify-content: center; flex-wrap: wrap; }

/* ── 5.2 載入骨架 ──────────────────────────────────────────────────────
   WCAG：2.2.2（動畫可停 —— 骨架只是短暫過場，且尊重 reduced-motion）
   ARIA：容器 aria-busy="true"；骨架本身 aria-hidden（不進無障礙樹，
         改由 aria-live 播報「載入中」）*/
.skeleton {
  border-radius: var(--r1);
  background: linear-gradient(90deg, var(--surface-sunken) 25%, var(--surface-subtle) 37%, var(--surface-sunken) 63%);
  background-size: 400% 100%;
  animation: ds-skeleton 1.4s ease infinite;
}

.skeleton--line { height: 10px; margin-bottom: var(--s2); }
.skeleton--line:last-child { width: 62%; }
.skeleton--title { height: 16px; width: 40%; margin-bottom: var(--s4); }
.skeleton--block { height: 96px; }

@keyframes ds-skeleton { from { background-position: 100% 0; } to { background-position: 0 0; } }

/* ── 5.3 錯誤頁 ────────────────────────────────────────────────────────
   WCAG：3.3.1、2.4.2、2.4.4（提供回到安全頁的明確連結）*/
.errorpane { max-width: var(--measure); margin: var(--s12) auto; text-align: center; }
.errorpane__code { font-family: var(--font-mono); font-size: var(--t-d1); color: var(--text-4); }
.errorpane__title { font-family: var(--font-serif); font-size: var(--t-h2); color: var(--text-1); margin-top: var(--s2); }
.errorpane__desc { margin-top: var(--s3); font-size: var(--t-sm); color: var(--text-3); line-height: var(--leading-loose); }

/* ── 5.4 篩選晶片列 ────────────────────────────────────────────────────
   ARIA：可移除的篩選條件為按鈕，aria-label 需含「移除 XX 篩選」*/
.pills { display: flex; flex-wrap: wrap; gap: var(--s2); }

.pill {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  min-height: var(--target-min);
  padding-inline: var(--s3);
  border: 1px solid var(--border-default);
  border-radius: var(--r-pill);
  background: var(--surface-raised);
  font-size: var(--t-xs);
  color: var(--text-2);
}

.pill--on { border-color: var(--border-brand); background: var(--surface-brand-soft); color: var(--text-brand); font-weight: var(--weight-medium); }

/* ★ 2026-10-26：晶片裡若是**連結**，要讓它撐滿晶片高度。
   原本 `<li class="pill"><a>…</a></li>` 只有 li 有 min-height，a 仍只有文字高 19px
   ⇒ 量測判「可點目標 < 24px」（WCAG 2.5.8），實測 /reports 的 9 個報表入口全中。
   修在樣式層而不是逐頁加 padding：`.pills` 有五處使用者，逐頁改一定會漏一處。 */
.pills > .pill > a {
  display: inline-flex;
  align-items: center;
  min-height: var(--target-min);
  padding-inline: var(--s3);
  margin-inline: calc(var(--s3) * -1);
  color: inherit;
  text-decoration: none;
  border-radius: inherit;
}

/* 連結自己撐高之後，li 的左右內距交給 a —— 否則文字會被推進去兩次 */
.pills > .pill:has(> a) { padding-inline: 0; }

/* ── 5.5 版面切換（窄螢幕：表格變卡片）────────────────────────────────
   WCAG：1.4.10 回流。600px 以下，表格以「標籤 ＋ 值」堆疊，
         標籤來自 JS 由 <thead> 複製的 data-label（單一來源，不逐頁手寫）*/
@media (max-width: 600px) {
  .data thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }

  .data tbody tr {
    display: block;
    padding: var(--s3);
    border-bottom: 1px solid var(--border-subtle);
  }

  .data tbody td {
    display: grid;
    grid-template-columns: 40% minmax(0, 1fr);
    gap: var(--s3);
    height: auto;
    border: 0;
    padding: 5px 0;
  }

  .data tbody td::before {
    content: attr(data-label);
    font-size: var(--t-xs);
    color: var(--text-3);
    font-weight: var(--weight-medium);
  }

  .data tbody td.col-num { text-align: left; }
  .data tbody td[data-label=""]::before, .data tbody td.col-check::before { display: none; }
}


/* ============================================================================
 * §6 工具與可及性
 * ==========================================================================*/

/* ── 6.1 螢幕閱讀器專用 ────────────────────────────────────────────────
   ⚠ 不可用 display:none 或 visibility:hidden（那樣讀屏也看不到）*/
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* 可聚焦的跳過連結（見 6.2）：平時藏、聚焦時現 */
.sr-only-focusable:not(:focus):not(:focus-within) {
  position: absolute;
  width: 1px; height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
}

/* ── 6.2 跳過導覽（2.4.1 Bypass Blocks）───────────────────────────────
   鍵盤使用者的第一站。必須是**第一個可聚焦元素**，聚焦時要看得見。*/
.skip-link {
  position: fixed;
  top: var(--s2);
  left: var(--s2);
  z-index: calc(var(--z-toast) + 1);
  padding: var(--s2) var(--s4);
  border-radius: var(--r2);
  background: var(--c-neutral-950);
  color: var(--c-white);
  font-size: var(--t-sm);
  font-weight: var(--weight-medium);
  transform: translateY(-200%);
  transition: transform var(--dur-fast) var(--ease);
}

.skip-link:focus { transform: none; }

/* ── 6.3 文字工具 ────────────────────────────────────────────────────── */
.mono { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
/* BLK-9 規格 B：等寬自動換行區（原 pre inline 白空格＋橫向捲動等價搬移；import:155 該處多出 overflow-x 之 delta 已申報） */
.mono--wrap { white-space: pre-wrap; overflow-x: auto; }
.num { text-align: right; font-variant-numeric: tabular-nums; font-family: var(--font-mono); }
.muted { color: var(--text-3); }
.lead { font-size: var(--t-md); color: var(--text-2); line-height: var(--leading-loose); max-width: var(--measure); }
.nowrap { white-space: nowrap; }
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }

/* 行內連結：3:1 對比 ＋ 底線（1.4.1 不以顏色為唯一區分）*/
.link {
  color: var(--text-link);
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-thickness: 1px;
  /* BLK-11 G-2：行內連結命中區補到 ≥24×24（WCAG 2.5.8）。
     作法＝inline-block ＋ 上下內距再用等量負外距收回，視覺位置不動。 */
  display: inline-block;
  padding-block: 6px;
  margin-block: -6px;
}

.link:hover { text-decoration-thickness: 2px; }

.hr { height: 1px; border: 0; background: var(--border-subtle); margin-block: var(--s4); }

/* 標記（搜尋結果高亮）：必須同時改前景與背景，否則高亮後文字可能不可讀 */
.pendingmark, mark {
  background: var(--c-amber-200);
  color: var(--text-1);
  padding-inline: 1px;
  border-radius: 2px;
}

/* 「本頁需再確認」的標記（開發／審查期用）*/
.reviewdot {
  display: inline-block;
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--c-crit-fg);
  vertical-align: super;
}

[data-hide-review] .reviewdot { display: none; }

/* ── 6.4 響應式（1.4.10 回流：320px 起不出現橫向捲動）────────────────── */

/* 平板：兩欄收單欄 */
@media (max-width: 1180px) {
  .detail { grid-template-columns: minmax(0, 1fr); }
  .detail__aside { order: 2; }        /* 摘要移到主體之後 */
}

/* 窄桌機：側欄改離屏抽屜 */
@media (max-width: 900px) {
  .shell, .shell[data-nav-collapsed="true"] { grid-template-columns: minmax(0, 1fr); }

  .nav {
    position: fixed;
    inset: 0 auto 0 0;
    width: var(--sidebar-w);
    transform: translateX(-100%);
    transition: transform var(--dur-base) var(--ease);
    box-shadow: var(--sh-4);
  }

  .shell[data-nav-open="true"] .nav { transform: none; }
  .nav__close { display: grid; }
  .shell[data-nav-collapsed="true"] .nav__title,
  .shell[data-nav-collapsed="true"] .nav__grouphead,
  .shell[data-nav-collapsed="true"] .nav__item > span,
  .shell[data-nav-collapsed="true"] .nav__count { display: revert; }
  .shell[data-nav-collapsed="true"] .nav__item { justify-content: flex-start; }
  .nav-scrim { display: none; }
  .shell[data-nav-open="true"] .nav-scrim { display: block; }

  .formrow { grid-template-columns: minmax(0, 1fr); }
  .formrow__label { padding-top: 0; }
}

/* 手機：320px 起仍可用 */
@media (max-width: 600px) {
  :root { --content-pad: var(--s4); }

  .pagehead__row { flex-direction: column; align-items: stretch; }

  /* ★ 2026-10-27（PM 裁定）：麵包屑在手機讓位，**由 h1 兌現「你在哪裡」**。
     原本兩處註釋互相指向對方，而兩邊在 ≤600px 都不顯示 ⇒ 手機上沒有可見標題，
     且零錯誤、零症狀（正是本專案反覆清的那一族：兜底寫在註釋裡）。
     做法＝把 sr-only 的那組宣告逐項還原（每一項都是為了「只藏視覺、不藏語意」而加的，
     少還原一項就會留下一半的隱藏效果：`clip-path` 留著 ⇒ 寬高還原了但整塊被裁掉；
     `white-space: nowrap` 留著 ⇒ 長標題不換行、直接撐破版面）。 */
  .pagehead__title {
    position: static;
    width: auto; height: auto;
    margin: 0; overflow: visible;
    clip-path: none; white-space: normal;
  }

  .topbar { padding-inline: var(--s3); }
  .topbar__crumbs { display: none; }      /* 麵包屑在手機讓位給動作；「你在哪裡」改由上面的 h1 承擔 */
  .toasts { right: var(--s3); left: var(--s3); bottom: var(--s3); max-width: none; }
  .confirm__box { padding: var(--s5); }
}

/* ============================================================================
 * §7 棄用相容層 —— 已移除（2026-10-05，PM 裁決）
 * ============================================================================
 *
 * 【為什麼移除，而不是保留過渡】
 *   這一層原本把四代舊 token 名（--lecturer-*／--n-*／--sj-*／--ink-*）
 *   映射到 §2 的語意 token，讓 5,063 處舊引用不要破相。
 *   但它掩蓋了真正的問題：**舊頁面的版面與元件本身就是照抄來的產物**。
 *   只要舊引用還能運作，就永遠不會有人去重建那些頁面。
 *
 * 【本專案的立場】
 *   設計系統只認 §1（原生色階）與 §2（語意 token）。
 *   舊名一律不留 —— 引用舊名的地方會直接失去顏色，這是**刻意**的：
 *   缺陷要看得見，不要靜默沿用。
 *
 * 【頁面怎麼重生】
 *   ✗ 不是「把舊頁面的樣式換成新名字」。
 *   ✓ 是「依權威需求（需求包／接口契約／裁決）重新定義功能，再用本設計系統重畫」。
 *   舊頁面只可作為**對照樣本**，不可作為版面、class、元件行為的來源。
 * ==========================================================================*/
