/* =============================================================================
 * shadcn/ui 主題 — 賣賣 seller（2025-seller）
 * -----------------------------------------------------------------------------
 * 移植自 2026-fatboy 的 shadcn-theme.css（該版已於 2026-07-28 完成 ERP 前後台
 * 100% 移除 Color Admin 並上正式站，是實證過的版本）。seller 的品牌色
 * --bs-app-theme-rgb: 255,217,0 與肥肥的 #FFD200 幾乎相同，token 層直接沿用。
 * seller 專有的補位規則集中在檔案最末的「§S 賣賣 seller 專有補位」章節，
 * 與肥肥共用的部分保持原樣，方便日後兩邊互相同步。
 * -----------------------------------------------------------------------------
 * 這是全站樣式的唯一 SSOT。view 內禁止寫死顏色、禁止 @push('css') 做顏色覆蓋。
 *
 * ★ 設計基準＝賣賣 App（2026-mymy-app）
 *   token 由 App 的 lib/core/constants/app_colors.dart 與 lib/core/theme/shad_theme.dart
 *   翻譯而來，App 與 web 只有一個顏色權威。App 改色時本檔跟著改。
 *   primary = 品牌金黃 #FFD200（前景恆黑）、灰階全走 zinc、radius 10px。
 *
 * ★ 載入順序（_link.blade.php）：
 *   vendor.min.css → bootstrap.min.css → 各 plugin css → 【本檔最後】
 *   最後載入才蓋得過 plugin 原廠樣式。
 *
 * ★ 鐵律
 *   - 背景與文字必須取自同一組 pair（--primary 底配 --primary-foreground 字）
 *   - --border 只能用在 border-*，絕不可當 color（對比 1.2:1，亮暗都看不見）
 *   - --primary 不可當「深色底」用（會隨主題翻轉）；深色區用 --muted 或 --dark-panel
 *   - 視覺收斂規則只能作用於非互動元素，絕不可掃到 <a> / <button>
 *
 * ★ 已作廢條文索引表見 §S-127（檔尾）。修改任何章節前先查那張表，確認你要依據的條文還有效。
 *
 * 詳見 .agent/shadcn-refactor-plan.md
 * ============================================================================= */

/* ---------- §1 Design Tokens（來源：賣賣 App AppColors，亮色） ---------- */
:root {
  /* 品牌主色（兩模式相同；App: AppColors.primary / textOnPrimary） */
  --primary: 49.4 100% 50%;              /* #FFD200 品牌金黃 */
  --primary-foreground: 0 0% 10.2%;      /* #1A1A1A 黃底恆黑字 */
  --primary-light: 50.9 100% 60%;        /* #FFE033 hover */
  --primary-dark: 49.3 100% 45.1%;       /* #E6BD00 active */

  /* 中性（App: background / surface / surfaceVariant / textPrimary…） */
  /* ★2026-08-07：暗色不受 §S-68 影響 —— `html.dark` (0,1,1) 特異度高於 `:root` (0,1,0)，
     暗色仍然是 §1b 的 `240 10% 3.9%`，本行在暗色本來就不參賽。 */
  /* ★已被 §S-68（亮）覆蓋（實測生效值：body background-color=rgb(255, 255, 255)） */
  --background: 0 0% 98%;               /* #FAFAFA zinc-50 —— ★這一顆刻意不吃 shadcn 官方值，見下方原委。

                                            ── 原註解（2026-08-07 上午，逐字保留）──
                                            「#FFFFFF 純白 = shadcn 官方值（原為 0 0% 98% #FAFAFA zinc-50，
                                              2026-08-07 依『一切以 shadcn 為主』改回官方純白。
                                              檔尾 §S-68 原本用同特異度後蓋的方式覆蓋這顆，
                                              但同一顆 token 有三處宣告、純靠原始碼順序決勝負太脆，
                                              故統一在源頭改正，§S-68 那段保留作為變更理由備查。）」

                                            ── ★2026-08-07 下午改回 0 0% 98%，理由如下（這是業主裁示，不是我的偏好）──
                                            業主看了賣賣Work OS（`/Users/monet/OS/mywork_os`）之後說
                                            「我就覺得 OS 版本設計的比較好看」「web 版可以學習一下」。
                                            實測 OS 的 `src/assets/shadcn.css`：
                                                --background: 0 0% 98%   ← 淺灰
                                                --card:       0 0% 100%  ← 純白
                                            ⇒ 業主偏好的那個外觀，靠的正是「灰底 + 白卡」的層次。
                                            改成官方純白之後 `--background` 與 `--card` 逐字同值，
                                            卡片只剩 1px 邊框可辨，整個「卡片浮在頁面上」的層次消失。

                                            ★取捨講清楚：shadcn 官方 A（apps/v4/app/globals.css）確實是
                                              `oklch(1 0 0)` 純白，所以這一顆是**已知且刻意的偏離**。
                                              其餘中性色仍全部對齊官方 achromatic（見 §S-72 / §S-74）。
                                              判準是業主的具體視覺參照物（OS 版）勝過抽象的「照官方」——
                                              而且 OS 自己也是 shadcn，只是選了 zinc-50 當底。
                                            ★暗色不受影響（官方暗色本來就是 --background 0 0% 3.9% 深於 --card 0 0% 9%，
                                              同樣是「卡片比底亮一階」的同一個層次原則）。 */
  --foreground: 0 0% 0%;                 /* #000000 ★2026-08-07 §S-72 改成官方 A 的 oklch(0% 0 0) 純黑（原 240 10% 3.9% #09090B zinc-950） */
  --card: 0 0% 100%;                     /* #FFFFFF */
  --card-foreground: 0 0% 0%;            /* #000000 ← oklch(0% 0 0)（原 240 10% 3.9%） */
  --popover: 0 0% 100%;
  --popover-foreground: 0 0% 0%;         /* #000000 ← oklch(0% 0 0)（原 240 10% 3.9%） */
  --secondary: 0 0% 96.1%;               /* #F5F5F5 ← oklch(0.97 0 0)（原 240 4.8% 95.9% #F4F4F5 zinc-100） */
  --secondary-foreground: 0 0% 9%;       /* #171717 ← oklch(0.205 0 0)（原 240 10% 3.9%） */
  --muted: 0 0% 96.1%;                   /* #F5F5F5 ← oklch(0.97 0 0)（原 240 4.8% 95.9%） */
  /* ★ 2026-07-31 無障礙盤點的決定：這顆**刻意不動**。
     實測在白底上是 4.40:1，離 WCAG AA 的 4.5 只差 0.10，但：
       1. 它是 shadcn upstream zinc 主題的原值（zinc-500），官方 Tabs 的
          未選中態就是這個組合 —— 動它等於整套 token 跟上游分岔，日後對版對不回來；
       2. 全站實際受影響的只有 2 處（都是次要說明文字，非操作路徑上的關鍵資訊）。
     用「差 0.1」去換掉一個上游基準值不划算，先記錄在案，日後若上游調整再一起跟。
     ★同一次盤點中真正該修的是紅字（3.76 / 2.82，差很多）→ 見 --destructive-fg。 */
  /* ★★2026-08-07 §S-72 推翻上面那段「刻意不動」的決定（原文一字未刪，保留存查）：
     當時不動的理由是「它是 upstream zinc 主題的原值，動了就跟上游分岔」。
     現在業主定調「一切都恢復 shadcn 官方設定」且目標改成 apps/v4 的 achromatic 一套，
     這顆的官方值就是 oklch(0.556 0 0)＝#737373。改了之後白底對比 4.83 → 4.74，
     ★仍然 **未達 WCAG AA 4.5**（實測 4.74 是對純白背景；對卡片白底同值）——
       活頁上實際量到的最低文字對比就是這一顆，它是官方本來就長這樣，不是本次改壞的。
     ★原註解提到的「4.40:1」是早期量測（那時頁面底是 #FAFAFA）；
       2026-08-07 頁面底已回純白，同一顆 zinc-500 實測是 4.83。 */
  --muted-foreground: 0 0% 45.1%;        /* #737373 neutral-500 ← oklch(0.556 0 0)（原 240 3.8% 46.1% #71717A zinc-500） */
  --accent: 0 0% 96.1%;                  /* #F5F5F5 ← oklch(0.97 0 0)（原 240 4.8% 95.9%） */
  --accent-foreground: 0 0% 9%;          /* #171717 ← oklch(0.205 0 0)（原 240 10% 3.9%） */
  --border: 0 0% 89.8%;                  /* #E5E5E5 neutral-200 ← oklch(0.922 0 0)（原 240 5.9% 90% #E4E4E7 zinc-200） */
  --input: 0 0% 89.8%;                   /* #E5E5E5 ← oklch(0.922 0 0)（原 240 5.9% 90%） */
  /* ★★★§S-72：這顆是業主點名「ring 改回官方值」的那一顆，但它有代價，寫清楚：
     官方的 focus 環是 `focus-visible:ring-[3px] ring-ring/50` + `focus-visible:border-ring`，
     本專案則是「2px 背景色間隙 + 4px hsl(var(--ring)/.5)」（§focus ring、§S-25）。
     近黑 → #A1A1A1 之後，白底上實實環對比 19.9 → 2.58，再乘 .5 疊完實測約 1.65，
     低於 WCAG 2.2 SC 1.4.11 對非文字（含焦點指示）要求的 3:1。
     ★上游本身就是這樣（這是 shadcn 已知的特性，不是我換錯值），業主也明確選了官方，
       所以照做；但這是一個 **已知的無障礙倒退**，要不要回調由業主拍板。
     ★暗色同理：0 0% 98%（純白）→ 0 0% 45.1%（#737373），對暗底 18.97 → 4.18。 */
  --ring: 0 0% 63.1%;                    /* #A1A1A1 ← oklch(0.708 0 0)（原 240 10% 3.9% 近黑） */
  --hint: 0 0% 63.1%;                    /* #A1A1A1 佔位符 ← §S-74 achromatic（原 240 5% 64.9% #A1A1AA zinc-400；L 64.9%→官方階 63.1%，色差 9/255）。與 --ring 亮色同階 */

  /* 一般主要動作＝zinc 近黑（黃鈕只留給「新增 / 儲存」，見 .agent 方案書） */
  --dark: 0 0% 9%;                       /* #171717 ← §S-74 achromatic（原 240 5.9% 10% #18181B zinc-900；官方階 9%＝oklch(0.205)，色差 4/255）。上行「zinc 近黑」的語意不變，只是灰不再帶藍 */
  --dark-foreground: 0 0% 98%;
  /* ═══ ★★★ 使用 --dark-panel 之前必讀（2026-08-06 追加，純註解，零行為變更）═══
     這顆 token 在 :root（本行下方）與 html.dark（本檔 §1b）**寫的是同一個值**
     `240 5.9% 10%`，是**刻意不翻轉**的 —— 不是漏改、不是複製貼上忘了改。
     它的語意是「**恆深底面板**」：不管使用者選亮色還是暗色，這塊區域都要是深底白字
     （對應 App 的 AppColors.darkPanel，兩端必須一致，所以不能只在網頁這邊翻）。

     ── 什麼情況可以用 ──
     · 整塊區域的身分就是「深色識別列 / 深色工作面板」，亮色模式下也**應該**是深的。
       本檔既有的正當用例：§POS-4 的 `.pos-sidebar-header`（POS 恆深識別列）、
       登入頁 `.login-v2` 的深色側欄、`.swal2-container` 的遮罩底（用 alpha）、
       `.gritter` 通知、以及各種蓋在圖片上的 scrim（那些底色來自圖片，本來就要恆深）。
     · 判斷法只有一句：**這個面板在亮色模式下也該是深底嗎？**
       答「是」→ 可以用；答「否 / 沒想過」→ 不要用。

     ── 什麼情況會出事（實際踩過兩次，都不是理論）──
     ★根因：`--dark-panel` 與**暗色**的 `--card` / `--popover` 逐字同值
       （`--card` 在 html.dark 也是 `240 5.9% 10%` ＝ #18181B）。
       所以任何「畫在卡片上的 --dark-panel 色塊」，暗色下對比是 **1.00:1 —— 整塊消失**。
       亮色下反而看得見（#18181B 疊白卡 17.6:1），於是**亮色驗收會過、暗色才爆**，
       這正是它難抓的原因。

       ① 商店設定的品牌磚（StoreSetting/_picker_card 的「自訂商店 / 雲端小幫手」中性磚）
          —— 暗色下磚 vs 卡片 **1.00:1**，八張卡裡只有這兩張沒有色塊，畫面上只剩一行粗體字。
          治法紀錄在本檔 §S-54 ②：改吃會翻轉的 `--dark` / `--dark-foreground`
          （亮 #18181B 底＋近白字、暗 #FAFAFA 底＋近黑字，兩邊都與 --card 拉開，皆 19:1 量級）。
       ② POS 掃描卡也踩過同一顆：整張卡吃 --dark-panel ⇒ **亮色也是黑的**，
          在一片淺色作業區裡變成唯一一塊黑，那是「恆深」語意被誤用到一般卡片上。

     ── 誤用時該換成什麼 ──
     · 要「主要 / 啟用狀態」的實心深塊（亮近黑、暗反白）→ `--dark` / `--dark-foreground`
     · 要「一般卡片表面」→ `--card` / `--card-foreground`
     · 要「次級灰底」→ `--muted` / `--secondary`
     ★`--dark-panel` 本身沒有錯，它是給「恆深底面板」用的；
       錯在把它當成「一般卡片上的中性底色」—— 那正是它不翻轉的代價。
     ★同理 `--dark-panel-foreground`（0 0% 98%，兩模式同值）：它是「恆亮前景」，
       只有疊在**確定恆深**的底上才成立；疊在會翻轉的表面上，亮色模式會變成白底白字。
     ═══════════════════════════════════════════════════════════════════════ */
  /* 固定深色面板（兩模式皆深底白字；App: AppColors.darkPanel） */
  /* ★§S-74 訂正上面那段警告裡的一個**已經過期的數字**（原文一字未刪，只補這行）：
     警告寫「--dark-panel 與暗色 --card 逐字同值 240 5.9% 10%」——
     §S-72 已把暗色 --card 換成 0 0% 9%，所以在 §S-74 之前它們其實是 #18181B vs #171717
     （對比 1.0119，仍等於看不見）。§S-74 把 --dark-panel 也 achromatic 成 0 0% 9% 之後，
     兩者**又變回逐字同值**（對比 1.0000）⇒ 那段警告重新變成字面上正確的，照它做就對。
     ★這裡刻意讓 :root 與 html.dark 兩處寫**同一個新值**，不翻轉的性質完全保留。 */
  --dark-panel: 0 0% 9%;                 /* #171717 ← §S-74 achromatic（原 240 5.9% 10% #18181B，色差 4/255） */
  --dark-panel-foreground: 0 0% 98%;     /* 原本就是 achromatic，§S-74 未動 */

  /* 狀態色（兩模式相同；App 同名值） */
  /* ★★2026-08-04 稽核：這兩行在亮色下**是死值** —— 真正生效的是 L9003 的
       `--destructive: 0 72.2% 50.6%`（#DC2626 red-600，實測白字 4.83:1 過 AA）。
       讀到這裡會以為紅色是 #EF4444，實跑不是。這與同日側欄那個坑是同一族：
       「檔案前段讀到的宣告」≠「瀏覽器算出來的現況」。
     ★另一個沒被覆蓋的事實：`html.dark` **完全沒有宣告 --destructive**
       （上游暗色應為 `0 62.8% 30.6%`），所以暗色紅底直接沿用亮色調校的那顆
       —— 實測兩個主題都是 rgb(220,38,38)、對比 4.83 剛好過，沒壞，但不對稱且無紀錄。
       要不要補暗色值屬視覺決策（會動到全站紅底按鈕），留給業主拍板，本輪不動。 */
  --destructive: 357.1 100% 45.3%;       /* #E7000B ← oklch(0.577 0.245 27.325)（★溢 sRGB 色域，已裁切）；原 0 84.2% 60.2% #EF4444。★已被 §S-39（亮）覆蓋，見上 */
  --destructive-foreground: 0 0% 100%;
  --success: 142.1 70.6% 45.3%;          /* #22C55E */
  --success-foreground: 0 0% 100%;
  --info: 217.2 91.2% 59.8%;             /* #3B82F6 */
  --info-foreground: 0 0% 100%;
  --warning: 37.7 92.1% 50.2%;           /* #F59E0B amber，與 primary 黃區隔 */
  --warning-foreground: 0 0% 10.2%;
  /* 柔和語意底（狀態 pill / alert 用，禁止 view 再寫死 hex） */
  --success-bg: 142 60% 94%;
  --success-fg: 142 65% 28%;
  --warning-bg: 38 92% 94%;
  --warning-fg: 32 81% 33%;
  /* ★★ 2026-07-31 無障礙修正：紅色「文字」專用前景色。
     問題：--destructive(#EF4444) 同時被當「底色」（.btn-danger 紅底白字）與「文字色」
     （.text-danger / .alert-danger 紅字）兩用。它在白底上實測只有 3.76:1、在淡紅底
     （.alert-danger 的 destructive/.1）上只有 2.82:1，兩者都低於 WCAG AA 的 4.5。
     ★但直接把 --destructive 調暗會連紅底按鈕的配色一起變 → 不動 --destructive 本身。
     作法＝把「文字用途」獨立成自己的 token，與本檔既有的 --success-fg / --warning-fg
     同一套慣例（那兩個語意色早就是 bg / fg 分家，只有 danger 漏掉＝SSOT 破口）。
     值是反推出來的：要同時滿足白卡片底(#FFF)、頁底(#FAFAFA)、淡紅底(#F8D7D7)
     三種底都 ≥4.5 → 相對亮度須 ≤0.124；0 74% 42% 實測 L=0.114 → 6.42 / 6.15 / 4.81 全過。 */
  --destructive-fg: 0 74% 42%;           /* #BA1B1B 紅字專用，勿當底色 */

  /* 請假 / 加班 / 打卡語意色（來源：App AppColors 同名） */
  --leave-annual: 258 90% 66%;           /* #8B5CF6 */
  --leave-sick: 330 81% 60%;             /* #EC4899 */
  --leave-personal: 189 94% 43%;         /* #06B6D4 */
  --early-leave: 25 95% 53%;             /* #F97316 */

  /* 圖表調色盤（低飽和可區分；canvas 不吃 CSS 變數，必須經 fatboyToken() 解析） */
  /* ★2026-08-04 稽核：同上，這行是死值 —— 生效的是 L7470（亮 40 92% 38%）
       與 L7476（暗 43 96% 56%）。原值 49 100% 50% 對白卡只有 1.43:1。 */
  --chart-1: 49 100% 50%;                /* ★已被 §S-12 覆蓋 */
  /* ★§S-74：--chart-2 不在原派工清單上，是自己 grep 出來的漏網 —— 它與 --dark 同值
     （zinc-900），圖表的近黑長條會與旁邊的 btn-dark 並排出現，一藍一中性就露餡。
     它另有第二處宣告（html.dark，見 §S-12 (b) 區塊），兩處都改了。 */
  --chart-2: 0 0% 9%;                    /* #171717 ← §S-74 achromatic（原 240 5.9% 10% #18181B，色差 4/255） */
  --chart-3: 220 9% 46%;
  --chart-4: 215 16% 65%;
  --chart-5: 200 18% 46%;
  --chart-6: 173 25% 42%;
  --chart-7: 12 40% 58%;
  --chart-8: 262 25% 58%;

  --radius: 0.625rem;                    /* 10px，對齊 App ShadThemeData.radius */

  /* ★★ 2026-07-29 SSOT：shadcn/tailwind 的圓角尺規只有四階，這裡是唯一定義處。
     以前全檔到處寫 calc(var(--radius) - 3px) / + 1px / + 2px，
     其中 -3 / +1 / +2 根本不在尺規上，是被發明出來的階
     —— 同一個尺規有好幾個真相，就是 SSOT 破口。
     以後一律用這四個具名變數，不要再自己 calc。
     （衍生值例外：卡片內圓角＝外圓角減邊框寬，那是幾何推導不是新階，
       寫成 calc(var(--radius-xl) - 1px)。） */
  /* ★§S-72：階梯公式改成官方的**乘法**（原為 -4px / -2px / +4px 的加減法）。
     --radius 目前是 0.625rem＝10px，兩套公式算出來 **逐階完全相同**：
       sm 10−4＝6  vs 10×0.6＝6｜md 10−2＝8 vs 10×0.8＝8｜xl 10+4＝14 vs 10×1.4＝14
     ★所以這是 **零視覺變化** 的純對齊；改公式是為了日後有人調 --radius 時兩邊不會分岔
       （加減法在 --radius 變小時會算出負值，乘法不會）。
     ★官方還有 2xl(1.8) / 3xl(2.2) / 4xl(2.6) 三階，本檔沒有任何地方用到 → 不新增（YAGNI）。 */
  --radius-sm: calc(var(--radius) * 0.6);   /* 6px  —— DropdownMenuItem、code、kbd */
  --radius-md: calc(var(--radius) * 0.8);   /* 8px  —— Button 全系列、Input、Badge、Popover、縮圖 */
  /* ★★★ 2026-08-09 更正（上面那行原文保留不刪，以本註解為準）：
     1. **Badge 不在 8px 之列，請把它從上行的清單裡讀掉。**
        官方出處 shadcn-ui/ui `apps/v4/registry/new-york-v4/ui/badge.tsx`，badgeVariants 的 base class
        是 `rounded-full`（CSS 9999px＝藥丸），不是 rounded-md。§24 `.badge` 已據此改為 9999px。
     2. **為什麼會錯 —— 這是本則更正最重要的一點。**
        有人把「專案裡的這行註解」當成 shadcn 的權威來源，據此把 112 顆唯讀膠囊從藥丸改成 8px 方角。
        ★但這行註解**不是獨立來源**：它當初就是照著同一個錯誤認知寫下來的，是**同源的回音**
        （echo），不是第二個證人。拿它去「交叉驗證」官方規格，等於自己驗證自己。
        ⇒ 交叉驗證前必須先問一句：**這兩個來源是不是同一個源頭？**
           同源的兩份資料再一致，也只是同一個錯誤被抄了兩遍。唯一有效的做法是回去讀 badge.tsx 原始碼。
     3. **判準（適用於本檔所有引用外部規格的註解）：**
        專案註解引用外部規格時，它的可信度**等於當初寫註解那個人查證的品質**——
        不會因為它被寫進權威檔案（本主題檔）、格式工整、或存在很久而升級成權威。
        ⇒ 要以官方規格做決策時，一律回官方原始碼取值；註解只能當「線索」，不能當「依據」。
     ★附帶存疑（未改，留待日後查證）：上行把 Input 列為 8px，但實測 .form-control 的 computed
       border-radius 是 10px（＝--radius / --radius-lg）。此清單的其餘項目同樣建議逐一實測後再引用。 */
  --radius-lg: var(--radius);               /* 10px —— DialogContent（Modal） */
  --radius-xl: calc(var(--radius) * 1.4);   /* 14px —— Card */

  /* 超連結＝前景色 + 底線（shadcn 做法，不用藍色） */
  --link: var(--foreground);
  --link-hover: var(--foreground);

  /* 側欄（shadcn 官方 sidebar＝亮色淺底深字。★深色側欄會被判定成「沒套 shadcn」） */
  /* ★§S-74：側欄整組 achromatic。規則是「保 L、H/S 歸零，能對上官方階就對上」，
     但**有兩顆刻意不吸到官方階**，理由寫在各自那行 —— 吸過去會改到功能，不是去色相。 */
  --sidebar-bg: 0 0% 98%;                /* 原本就是 achromatic，§S-74 未動（＝官方 --sidebar oklch(0.985)） */
  --sidebar-fg: 0 0% 34%;                /* #575757 ← §S-74 保 L 去色相（原 240 5% 34% #52525B zinc-600，色差 5/255）。
                                            ★不吸官方階：最近的是 25.1%，差 8.9pp＝23/255，會把側欄文字整體壓黑一階，
                                              且與 --sidebar-fg-strong(9%) 的三階層次被壓掉約三成。對比 7.41→6.92，仍遠高於 AA。 */
  --sidebar-fg-strong: 0 0% 9%;          /* #171717 ← §S-74 achromatic（原 240 6% 10% #18181B，官方階 9%，色差 4/255） */
  --sidebar-border: 0 0% 89.8%;          /* #E5E5E5 ← §S-74 achromatic（原 240 6% 90% #E4E4E7，官方階 89.8%＝--border，色差 2/255） */
  --sidebar-accent: 0 0% 94%;            /* #F0F0F0 ← §S-74 保 L 去色相（原 240 5% 94% #EFEFF0，色差 1/255）。
                                            ★不吸官方階（96.1%）：這顆是側欄**目前所在頁**的底色，也是 hover 底色。
                                              吸到 96.1% 會讓它與 --sidebar-bg(98%) 的差距從 ΔRGB 11 掉到 5（對比 1.1009→1.0445），
                                              等於把全站唯一的「我在哪一頁」指示器削掉一半。
                                            ★而且這顆有前科：本檔 §側欄：對齊 shadcn/ui 的註解記著業主當場的
                                              「馬的，我要你恢復正常的左邊選單顏色」，判準是 2026-fatboy 正式站的 #efeff0。
                                              §S-74 只把藍味拿掉（#EFEFF0→#F0F0F0，肉眼等值），亮度一格沒動。
                                            ★要改成 100% 官方就是把這行換成 `0 0% 96.1%`，一行的事，但那是業主要拍板的取捨。 */
  --sidebar-active-bg: 0 0% 89.8%;       /* #E5E5E5 ← §S-74 achromatic（原 240 5% 90% #E4E4E7，官方階 89.8%，色差 2/255） */

  /* ---- Color Admin 專有變數補位 ----
     view 的 inline style 直接引用這三個（共 10 處：Chat/index、AiTool/*、Ai/*），
     拔掉 app.min.css 後會變 undefined → 不報錯但靜默失效（分隔線消失、卡片透明）。 */
  --bs-component-bg: hsl(var(--card));
  --bs-component-bg-rgb: 255, 255, 255;
  --bs-component-border-color: hsl(var(--border));

  /* Color Admin 主題色 remap → pace 載入條 / active / accent 全部跟著品牌黃 */
  --bs-app-theme: hsl(var(--primary));
  --bs-app-theme-rgb: 255, 210, 0;
  --bs-app-theme-color: hsl(var(--primary-foreground));

  /* ---- Bootstrap 全域變數 remap 到 shadcn token ---- */
  --bs-body-bg: hsl(var(--background));
  --bs-body-color: hsl(var(--foreground));
  --bs-border-color: hsl(var(--border));
  --bs-border-radius: var(--radius);
  --bs-border-radius-sm: var(--radius-sm);
  --bs-border-radius-lg: var(--radius-xl);
  --bs-link-color: hsl(var(--link));
  --bs-link-hover-color: hsl(var(--link-hover));
  /* BS5.3 的 a 實際吃 rgba(var(--bs-link-color-rgb),…)，rgb 版必須一起蓋 */
  --bs-link-color-rgb: 9, 9, 11;
  --bs-link-hover-color-rgb: 9, 9, 11;
  --bs-emphasis-color: hsl(var(--foreground));
  --bs-secondary-color: hsl(var(--muted-foreground));
  --bs-tertiary-bg: hsl(var(--muted));
}

/* ---------- §1b 暗色模式（App: AppColors._dark = true 那一組） ---------- */
html.dark {
  --background: 0 0% 3.9%;               /* #0A0A0A ← oklch(0.145 0 0)（原 240 10% 3.9% #09090B zinc-950） */
  --foreground: 0 0% 98%;                /* #FAFAFA */
  --card: 0 0% 9%;                       /* #171717 ← oklch(0.205 0 0)（原 240 5.9% 10% #18181B zinc-900）；比 --background 亮一階，與官方同向 */
  --card-foreground: 0 0% 98%;
  --popover: 0 0% 9%;                    /* #171717 ← oklch(0.205 0 0)（原 240 5.9% 10%） */
  --popover-foreground: 0 0% 98%;
  --secondary: 0 0% 14.9%;               /* #262626 ← oklch(0.269 0 0)（原 240 3.7% 15.9% #27272A zinc-800） */
  --secondary-foreground: 0 0% 98%;
  --muted: 0 0% 14.9%;                   /* #262626 ← oklch(0.269 0 0)（原 240 3.7% 15.9%） */
  --muted-foreground: 0 0% 63.1%;        /* #A1A1A1 ← oklch(0.708 0 0)（原 240 5% 64.9% #A1A1AA zinc-400） */
  --accent: 0 0% 25.1%;                  /* #404040 ← oklch(0.371 0 0)（原 240 3.7% 15.9%）。★官方暗色 --accent 比 --muted/--secondary 亮一大階，hover 態才看得見 */
  --accent-foreground: 0 0% 98%;
  --border: 0 0% 100% / 10%;             /* ← oklch(1 0 0 / 10%) 白色 10% 疊層（原 240 3.7% 15.9% #27272A 不透明）。★語法見 §S-72 一-(3) */
  --input: 0 0% 100% / 15%;              /* ← oklch(1 0 0 / 15%) 白色 15% 疊層（原 240 3.7% 15.9%） */
  /* ★★2026-08-04 補上暗色 --destructive（原本 html.dark 完全沒宣告 → 靜靜沿用亮色那顆）。
       ── 為什麼是「明確宣告成目前這個值」而不是照抄上游 ──
       上游兩個世代的方向**相反**（已用 context7 查證原始碼，不是憑印象）：
         v3（HSL 世代，本檔同世代）  .dark  destructive: 0 62.8% 30.6%   ← 暗紅
         v4（oklch 世代，現行）      .dark  oklch(0.704 …) 比亮色 0.577 **更亮**
       v4 等於推翻了 v3 的「暗色用暗紅」。照 v3 抄是往被淘汰的方向走；
       照 v4 的亮度走，白字疊上去會掉到 4.5 以下（本站 --destructive-foreground 是白）。
       所以取兩者之間、也就是**目前實際在跑的那顆**，把它明確寫下來：
         視覺零變動、實測白字 4.83:1 過 AA，但「暗色沒有宣告」這個沉默的破口補起來了。
       ★真正的問題從來不是值不對，是**它是被動繼承來的**——下次有人改亮色，暗色會跟著動而沒人知道。 */
  --destructive: 0 72.2% 50.6%;          /* #DC2626：與亮色同值，實測白字 4.83:1（刻意，理由見上） */
  --destructive-foreground: 0 0% 98%;

  /* ★2026-08-04 稽核：上游暗色 --ring 是 `240 4.9% 83.9%`，這裡拉到純白且原本零註解。
       焦點框是無障礙元件，純白在暗底上更醒目、可視為刻意加強（亮色那顆 L60 與上游一致）。
       記錄下來，避免下次對版時被當成抄錯而「修」回去。 */
  --ring: 0 0% 45.1%;                    /* #737373 ← oklch(0.556 0 0)（原 0 0% 98% 純白）。★上面那段「拉到純白是刻意加強」的紀錄保留，但 §S-72 已依業主指示改回官方值（原文保留存查，不是刪掉） */
  --hint: 0 0% 45.1%;                    /* #737373 ← §S-74 achromatic（原 240 3.8% 46.1% #71717A zinc-500；官方階 45.1%，色差 7/255）。與 --ring 暗色同階 */

  /* 一般主要動作鈕在暗色翻成淺底深字（App: secondary 淺色=黑 / 深色=白） */
  --dark: 0 0% 98%;                      /* 原本就是 achromatic，§S-74 未動 */
  --dark-foreground: 0 0% 9%;            /* #171717 ← §S-74 achromatic（原 240 5.9% 10% #18181B，官方階 9%，色差 4/255） */
  /* ★★★ 用之前先讀：--dark-panel 是本檔**唯一一顆刻意不翻轉**的表面色（警告全文見 :root 那份）。
     暗色的 --dark-panel（240 5.9% 10%）與暗色的 --card / --popover **逐字同值**
     （三者都是 240 5.9% 10% ＝ #18181B）⇒ 把它當卡片上的底色用，暗色下對比 1.00:1，
     色塊會整塊消失。判斷法只有一句：**這個面板在亮色模式也該是深底嗎？**
     不是的話就用會翻轉的 --dark / --dark-foreground。 */
  /* darkPanel 兩模式皆深，不翻轉 */
  /* ★§S-74：與 :root 那份**寫同一個新值** 0 0% 9%，不翻轉的性質原樣保留。
     上面警告裡「暗色 --dark-panel 與 --card / --popover 逐字同值」在 §S-72 之後一度變成
     「差 4/255」，§S-74 之後又回到逐字同值（都是 #171717，對比 1.0000）—— 警告照舊有效。 */
  --dark-panel: 0 0% 9%;                 /* #171717 ← §S-74 achromatic（原 240 5.9% 10% #18181B，色差 4/255） */
  --dark-panel-foreground: 0 0% 98%;     /* 原本就是 achromatic，§S-74 未動 */

  --success-bg: 142 40% 16%;
  --success-fg: 142 60% 70%;
  --warning-bg: 38 50% 16%;
  --warning-fg: 38 90% 70%;
  /* 暗色的紅字要往「亮」走（深底上越亮對比越高），與 --success-fg / --warning-fg
     暗色都上調到 70% 同一個規律。實測：暗卡片底 6.45、深淡紅底 6.05（原本 4.71 / 4.42）。 */
  --destructive-fg: 0 91% 71%;           /* #F87171 */

  --link: var(--foreground);
  --link-hover: var(--foreground);

  /* ★§S-74：暗側欄整組 achromatic，五顆吸到官方階、一顆刻意保 L（理由在該行）。 */
  --sidebar-bg: 0 0% 9%;                 /* #171717 ← §S-74 achromatic（原 240 5.9% 10% #18181B，官方階 9%，色差 4/255）。
                                            ★這讓它與暗色 --card 變成同值 —— 官方就是這樣（--sidebar 與 --card 都是 oklch(0.205)），
                                              側欄靠 --background(#0A0A0A) 的頁底與 1px --sidebar-border 分界，不靠自身色差。 */
  --sidebar-fg: 0 0% 63.1%;              /* #A1A1A1 ← §S-74 achromatic（原 240 5% 65% #A1A1AA zinc-400；官方階 63.1%，色差 9/255）。對比 6.91→6.94 */
  --sidebar-fg-strong: 0 0% 98%;         /* 原本就是 achromatic，§S-74 未動 */
  --sidebar-border: 0 0% 14.9%;          /* #262626 ← §S-74 achromatic（原 240 3.7% 15.9% #27272A，官方階 14.9%，色差 4/255）。
                                            ★官方暗色 --sidebar-border 是 `oklch(1 0 0 / 10%)` 白色疊層；這裡**刻意維持不透明**：
                                              本檔已有 `hsl(var(--sidebar-accent) / .6)` 這類再加 alpha 的用法，
                                              一旦 token 本身帶 alpha，`hsl(var(--x) / .6)` 會展開成雙斜線語法錯誤而整條被丟棄
                                              （§S-72 記過這個坑）。--sidebar-border 目前雖然沒有 alpha 用法，但同組不該一半帶一半不帶。 */
  --sidebar-accent: 0 0% 14.9%;          /* #262626 ← §S-74 achromatic（原 240 3.7% 15.9% #27272A，官方階 14.9%＝官方 --sidebar-accent oklch(0.269)，色差 4/255） */
  --sidebar-active-bg: 0 0% 19%;         /* #303030 ← §S-74 保 L 去色相（原 240 3.7% 19% #2F2F32，色差 2/255）。
                                            ★不吸官方階：最近的 14.9% 正好等於 --sidebar-accent，吸過去兩顆就撞成同色（Δ0），
                                              hover 與 active 分不出來。保 L 之後兩者 ΔRGB 由 8 變 10，反而更分得開。 */

  --bs-component-bg: hsl(var(--card));
  --bs-component-bg-rgb: 24, 24, 27;
  --bs-component-border-color: hsl(var(--border));

  --bs-link-color-rgb: 250, 250, 250;
  --bs-link-hover-color-rgb: 250, 250, 250;

  color-scheme: dark;   /* 元素級，蓋掉 head 的 meta color-scheme only light */
}

/* ---------- §2 基礎 ---------- */
body,
.app,
#content.app-content {
  background-color: hsl(var(--background));
  color: hsl(var(--foreground));
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  letter-spacing: -0.011em;
}
h1, h2, h3, h4, h5, h6 {
  font-weight: 600;
  letter-spacing: -0.02em;
  color: hsl(var(--foreground));
}
a { text-decoration: none; }
/* ★已被 §28 覆蓋（實測生效值：color=rgb(113, 113, 122)） */
.text-muted { color: hsl(var(--muted-foreground)) !important; }
/* ★已被 L843 覆蓋（實測生效值：border-color=rgb(228, 228, 231)） */
hr, .dropdown-divider { border-color: hsl(var(--border)); opacity: 1; }
::selection { background: hsl(var(--primary) / .35); }

/* 按鈕內的圖示一律繼承按鈕前景色 —— view 不准再寫 text-white
   （黃底黑字的鈕若寫死白 icon 會直接隱形） */
.btn i { color: inherit; }

/* =============================================================================
 * 以下移植自 merp shadcn-theme.css（章節對照見 .agent/shadcn-refactor-plan.md）
 * ============================================================================= */

/* =============================================================================
 * 以下為「merp 章節移植」段落（接在 §1 Design Tokens / §2 基礎 之後）
 * 來源：/Users/monet/mtest/merp-div-tw/public/assets/css/shadcn-theme.css（2478 行）
 * -----------------------------------------------------------------------------
 * ★★ 顏色語意轉換總則（與 merp 最大的差異，改動前務必先看懂）
 *   merp 的 --primary = 近黑；肥肥的 --primary = 品牌金黃 #FFD200。
 *   所以 merp 凡是「用 primary 表示強調」的地方，在肥肥一律改吃 --dark / --foreground：
 *     .btn-dark、側欄 active、checkbox 勾選、switch、進度條、分頁 active、
 *     select2 已選項、daterangepicker 選中日、dropdown active、tooltip 底、
 *     nav-pills active、list-group active、badge bg-primary/bg-dark…
 *   只有這三類才留黃（--primary）：
 *     1) .btn-primary（＝「新增 / 儲存」級主要動作，每頁最多一顆）
 *     2) 登入頁 brand-badge（品牌識別）
 *     3) .pace .pace-progress 載入進度條（品牌識別）
 *   判斷準則：問「這裡是不是『新增/儲存』級主要動作或品牌識別？」
 *            是 → --primary（黃）；否 → --dark。
 *
 * ★ 其他轉換規則
 *   - merp 前綴 .merp-* → .fb-*（.merp-tabs/.merp-tab → .fb-tabs/.fb-tab）
 *   - merp 寫死的 #fff / #f8f9fa / hsl(32 81% 18%) 等 → 一律換成 token
 *   - --border 絕不可當 color:（對比 1.2:1）→ 一律改 --muted-foreground
 *   - 任何 pointer-events:none 或針對 rounded-pill / btn-rounded「形狀」的收斂規則，
 *     一律限縮到 span（merp 為此出過事故：全站 8 個新增按鈕點擊穿透）
 * ============================================================================= */


/* =============================================================================
 * §3 Page header（移植自 merp §3 L113-120）
 * ============================================================================= */
.page-header {
  border-bottom: 1px solid hsl(var(--border));
  padding-bottom: 0.75rem;
  margin-bottom: 1.25rem;
}
.page-header h1, .page-header h4 { color: hsl(var(--foreground)); }


/* =============================================================================
 * §4 Button 全套（移植自 merp §4 L121-250）
 * ★ 這一節是顏色語意轉換的重災區，merp 的 `.btn-dark, .btn-primary` 共用一組
 *   近黑 primary；肥肥必須拆成兩顆：dark = 一般主要動作、primary = 黃（新增/儲存）。
 * ============================================================================= */
.btn {
  --bs-btn-border-radius: var(--radius);
  /* ★已被 §28 覆蓋（實測生效值：font-weight=500） */
  font-weight: 500;
  border-width: 1px;
  transition: background-color .15s ease, color .15s ease, border-color .15s ease, opacity .15s ease;
  box-shadow: none !important;
  /* BS5 拿掉了 BS4 的 .btn 預設 nowrap → 手機上 flex tab 列的按鈕會被壓縮成中文直排（一字一行）。
     shadcn 按鈕本就不換行；nowrap 也讓 flex 子項的 min-content 撐住不被壓縮。 */
  white-space: nowrap;
}
.btn-sm { --bs-btn-border-radius: var(--radius-md); }

/* ★轉換 1／2：.btn-dark ＝ 一般主要動作（zinc 近黑；暗色自動翻成淺底深字）
   對應 merp 的 `.btn-dark, .btn-primary` 那一組。hover/active 用 alpha 而非
   寫死 hsl(240 5.9% 18%)，這樣暗色翻轉後也不會又變回近黑。 */
.btn-dark {
  --bs-btn-bg: hsl(var(--dark));
  --bs-btn-border-color: hsl(var(--dark));
  --bs-btn-color: hsl(var(--dark-foreground));
  --bs-btn-hover-bg: hsl(var(--dark) / .88);
  --bs-btn-hover-border-color: hsl(var(--dark) / .88);
  --bs-btn-hover-color: hsl(var(--dark-foreground));
  --bs-btn-active-bg: hsl(var(--dark) / .8);
  --bs-btn-active-border-color: hsl(var(--dark) / .8);
  --bs-btn-active-color: hsl(var(--dark-foreground));
  --bs-btn-disabled-bg: hsl(var(--dark));
  --bs-btn-disabled-border-color: hsl(var(--dark));
  --bs-btn-disabled-opacity: .5;
}
/* ★轉換 2／2：.btn-primary ＝ 品牌金黃，黃底恆黑字。
   只留給「新增 / 儲存」級主要動作，每頁最多一顆（全站灑黃會變黃斑馬）。
   hover/active 走 --primary-light / --primary-dark（App 同名 token，兩模式相同）。 */
.btn-primary {
  --bs-btn-bg: hsl(var(--primary));
  --bs-btn-border-color: hsl(var(--primary));
  --bs-btn-color: hsl(var(--primary-foreground));
  --bs-btn-hover-bg: hsl(var(--primary-light));
  --bs-btn-hover-border-color: hsl(var(--primary-light));
  --bs-btn-hover-color: hsl(var(--primary-foreground));
  --bs-btn-active-bg: hsl(var(--primary-dark));
  --bs-btn-active-border-color: hsl(var(--primary-dark));
  --bs-btn-active-color: hsl(var(--primary-foreground));
  --bs-btn-disabled-bg: hsl(var(--primary));
  --bs-btn-disabled-border-color: hsl(var(--primary));
  --bs-btn-disabled-opacity: .5;
}
/* btn-white（Color Admin 慣用「淺底細框」鈕）→ shadcn outline。照抄，不變。 */
/* ★★★ 2026-08-10 更正：自本日起 .btn-white 已加進 §28 的選擇器清單（見 §28 該處說明），
   ⇒ 下方每一條宣告對 .btn-white 也一併由 §28 接管，
     現行生效值與 .btn-secondary／.btn-light 完全相同。
   ★下方 8 則註解的分項因此由「二對一」改成「三者皆同」——
     這是**把假話改成真話**，不是刪資料：括號內的〈實測生效值〉逐字保留未動。
   ★〈原註解逐字存查〉原本 8 則的分項寫法一律是（僅此存查，故意把〔〕換成［］，
     免得守門測試 ThemeOverrideAnnotationsTest 把這份歷史紀錄當成一筆現行宣稱再判一次）：
       ★［.btn-secondary／.btn-light］已被 §28 覆蓋；［.btn-white］未被覆蓋（實測生效值：…）
     那句「［.btn-white］未被覆蓋」在 2026-08-10 之前是**正確的事實紀錄**，
     正是它揭露了 §28 漏掉 .btn-white、進而讓 104 處 btn-white→btn-secondary 改名產生色差。
   ★.btn-white 改名前的舊生效值不必另存：它就是下方這些宣告的字面值
     （--bs-btn-bg: hsl(var(--background)) 等），一個字都沒動，隨時可回推。 */
.btn-white,
.btn-secondary, .btn-light {
  /* ★〔.btn-secondary／.btn-light／.btn-white〕已被 §28 覆蓋（實測生效值：--bs-btn-bg=hsl(240 4.8% 95.9%)） */
  --bs-btn-bg: hsl(var(--background));
  /* ★〔.btn-secondary／.btn-light／.btn-white〕已被 §28 覆蓋（實測生效值：--bs-btn-border-color=hsl(240 5.9% 90%)） */
  --bs-btn-border-color: hsl(var(--border));
  /* ★〔.btn-secondary／.btn-light／.btn-white〕已被 §28 覆蓋（實測生效值：--bs-btn-color=hsl(240 10% 3.9%)） */
  --bs-btn-color: hsl(var(--foreground));
  /* ★〔.btn-secondary／.btn-light／.btn-white〕已被 §28 覆蓋（實測生效值：--bs-btn-hover-bg=hsl(240 4.8% 95.9%)） */
  --bs-btn-hover-bg: hsl(var(--accent));
  /* ★〔.btn-secondary／.btn-light／.btn-white〕已被 §28 覆蓋（實測生效值：--bs-btn-hover-border-color=hsl(240 5.9% 90%)） */
  --bs-btn-hover-border-color: hsl(var(--border));
  /* ★〔.btn-secondary／.btn-light／.btn-white〕已被 §28 覆蓋（實測生效值：--bs-btn-hover-color=hsl(240 10% 3.9%)） */
  --bs-btn-hover-color: hsl(var(--accent-foreground));
  /* ★〔.btn-secondary／.btn-light／.btn-white〕已被 §28 覆蓋（實測生效值：--bs-btn-active-bg=hsl(240 4.8% 95.9%)） */
  --bs-btn-active-bg: hsl(var(--accent));
  --bs-btn-active-border-color: hsl(var(--border));
  /* ★〔.btn-secondary／.btn-light／.btn-white〕已被 §28 覆蓋（實測生效值：--bs-btn-active-color=hsl(240 10% 3.9%)） */
  --bs-btn-active-color: hsl(var(--accent-foreground));
}
/* btn-default（Color Admin 遺留；Bootstrap 5 已無此 class）→ shadcn ghost。
   ★不接管的話它完全沒有樣式：文字色不隨主題翻轉，暗色下對比 1.00 直接消失
   （merp /projects/{id} 未選中的 13 個 tab 全部看不見）。
   語意＝「無強調的中性按鈕」：未選中 tab、次要 toggle。
   透明底 + muted 文字（隨主題翻轉），hover 才浮出 accent 底，
   與選中態的 .btn-light（灰底）自然區分。 */
.btn-default {
  /* ★已被 §修正 覆蓋（實測生效值：--bs-btn-bg=transparent） */
  --bs-btn-bg: transparent;
  /* ★已被 §修正 覆蓋（實測生效值：--bs-btn-border-color=hsl(240 5.9% 90%)） */
  --bs-btn-border-color: transparent;
  /* ★已被 §修正 覆蓋（實測生效值：--bs-btn-color=hsl(240 10% 3.9%)） */
  --bs-btn-color: hsl(var(--muted-foreground));
  /* ★已被 §修正 覆蓋（實測生效值：--bs-btn-hover-bg=hsl(240 4.8% 95.9%)） */
  --bs-btn-hover-bg: hsl(var(--accent));
  /* ★已被 §修正 覆蓋（實測生效值：--bs-btn-hover-border-color=hsl(240 5.9% 90%)） */
  --bs-btn-hover-border-color: transparent;
  /* ★已被 §修正 覆蓋（實測生效值：--bs-btn-hover-color=hsl(240 10% 3.9%)） */
  --bs-btn-hover-color: hsl(var(--accent-foreground));
  /* ★已被 §修正 覆蓋（實測生效值：--bs-btn-active-bg=hsl(240 4.8% 95.9%)） */
  --bs-btn-active-bg: hsl(var(--accent));
  /* ★已被 §修正 覆蓋（實測生效值：--bs-btn-active-border-color=hsl(240 5.9% 90%)） */
  --bs-btn-active-border-color: transparent;
  /* ★已被 §修正 覆蓋（實測生效值：--bs-btn-active-color=hsl(240 10% 3.9%)） */
  --bs-btn-active-color: hsl(var(--accent-foreground));
}
/* outline 系列收斂為 shadcn outline */
.btn-outline-secondary, .btn-outline-dark, .btn-outline-light {
  /* ★〔.btn-outline-secondary／.btn-outline-dark〕已被 §28 覆蓋；〔.btn-outline-light〕未被覆蓋（實測生效值：--bs-btn-bg=hsl(240 4.8% 95.9%)） */
  --bs-btn-bg: transparent;
  /* ★〔.btn-outline-secondary／.btn-outline-dark〕已被 §28 覆蓋；〔.btn-outline-light〕未被覆蓋（實測生效值：--bs-btn-border-color=hsl(240 5.9% 90%)） */
  --bs-btn-border-color: hsl(var(--border));
  /* ★〔.btn-outline-secondary／.btn-outline-dark〕已被 §28 覆蓋；〔.btn-outline-light〕未被覆蓋（實測生效值：--bs-btn-color=hsl(240 10% 3.9%)） */
  --bs-btn-color: hsl(var(--foreground));
  /* ★〔.btn-outline-secondary／.btn-outline-dark〕已被 §28 覆蓋；〔.btn-outline-light〕未被覆蓋（實測生效值：--bs-btn-hover-bg=hsl(240 4.8% 95.9%)） */
  --bs-btn-hover-bg: hsl(var(--accent));
  /* ★〔.btn-outline-secondary／.btn-outline-dark〕已被 §28 覆蓋；〔.btn-outline-light〕未被覆蓋（實測生效值：--bs-btn-hover-border-color=hsl(240 5.9% 90%)） */
  --bs-btn-hover-border-color: hsl(var(--border));
  /* ★〔.btn-outline-secondary／.btn-outline-dark〕已被 §28 覆蓋；〔.btn-outline-light〕未被覆蓋（實測生效值：--bs-btn-hover-color=hsl(240 10% 3.9%)） */
  --bs-btn-hover-color: hsl(var(--accent-foreground));
  /* ★〔.btn-outline-secondary／.btn-outline-dark〕已被 §28 覆蓋；〔.btn-outline-light〕未被覆蓋（實測生效值：--bs-btn-active-bg=hsl(240 4.8% 95.9%)） */
  --bs-btn-active-bg: hsl(var(--accent));
  --bs-btn-active-border-color: hsl(var(--border));
  /* ★〔.btn-outline-secondary／.btn-outline-dark〕已被 §28 覆蓋；〔.btn-outline-light〕未被覆蓋（實測生效值：--bs-btn-active-color=hsl(240 10% 3.9%)） */
  --bs-btn-active-color: hsl(var(--accent-foreground));
}
.btn-link {
  --bs-btn-color: hsl(var(--foreground));
  --bs-btn-hover-color: hsl(var(--muted-foreground));
  text-decoration: none;
}
/* destructive（照抄，不變） */
.btn-danger {
  /* ★已被 §28 覆蓋（實測生效值：--bs-btn-bg=hsl(0 72.2% 50.6%)） */
  --bs-btn-bg: hsl(var(--destructive));
  /* ★已被 §28 覆蓋（實測生效值：--bs-btn-border-color=hsl(0 72.2% 50.6%)） */
  --bs-btn-border-color: hsl(var(--destructive));
  /* ★已被 §28 覆蓋（實測生效值：--bs-btn-color=hsl(0 0% 100%)） */
  --bs-btn-color: hsl(var(--destructive-foreground));
  /* ★已被 §28 覆蓋（實測生效值：--bs-btn-hover-bg=hsl(0 72.2% 50.6% / .9)） */
  --bs-btn-hover-bg: hsl(var(--destructive) / .88);
  /* ★已被 §28 覆蓋（實測生效值：--bs-btn-hover-border-color=hsl(0 72.2% 50.6% / .9)） */
  --bs-btn-hover-border-color: hsl(var(--destructive) / .88);
  /* ★已被 §28 覆蓋（實測生效值：--bs-btn-hover-color=hsl(0 0% 100%)） */
  --bs-btn-hover-color: hsl(var(--destructive-foreground));
  --bs-btn-active-bg: hsl(var(--destructive) / .8);
}
/* ★2026-08-04 稽核：blade 0 命中 */
.btn-outline-danger {
  --bs-btn-bg: transparent;
  --bs-btn-border-color: hsl(var(--border));
  --bs-btn-color: hsl(var(--destructive));
  --bs-btn-hover-bg: hsl(var(--destructive));
  --bs-btn-hover-border-color: hsl(var(--destructive));
  --bs-btn-hover-color: hsl(var(--destructive-foreground));
}
/* shadcn 為單色設計：success/warning/info 一律收斂為中性 secondary（語意靠圖示/文字，不靠彩色填底）。
   僅 destructive(刪除/下架) 保留紅色，主要動作保留 .btn-dark 近黑 / .btn-primary 品牌黃。
   如此全站按鈕一致為 shadcn 風。 */
.btn-success,
.btn-warning,
.btn-info {
  /* ★已被 §28 覆蓋（實測生效值：--bs-btn-bg=hsl(240 4.8% 95.9%)） */
  --bs-btn-bg: hsl(var(--secondary));
  /* ★已被 §28 覆蓋（實測生效值：--bs-btn-border-color=hsl(240 5.9% 90%)） */
  --bs-btn-border-color: hsl(var(--border));
  /* ★已被 §28 覆蓋（實測生效值：--bs-btn-color=hsl(240 10% 3.9%)） */
  --bs-btn-color: hsl(var(--secondary-foreground));
  /* ★已被 §28 覆蓋（實測生效值：--bs-btn-hover-bg=hsl(240 4.8% 95.9%)） */
  --bs-btn-hover-bg: hsl(var(--accent));
  /* ★已被 §28 覆蓋（實測生效值：--bs-btn-hover-border-color=hsl(240 5.9% 90%)） */
  --bs-btn-hover-border-color: hsl(var(--border));
  /* ★已被 §28 覆蓋（實測生效值：--bs-btn-hover-color=hsl(240 10% 3.9%)） */
  --bs-btn-hover-color: hsl(var(--accent-foreground));
  /* ★已被 §28 覆蓋（實測生效值：--bs-btn-active-bg=hsl(240 4.8% 95.9%)） */
  --bs-btn-active-bg: hsl(var(--accent));
  --bs-btn-active-border-color: hsl(var(--border));
  /* ★已被 §28 覆蓋（實測生效值：--bs-btn-active-color=hsl(240 10% 3.9%)） */
  --bs-btn-active-color: hsl(var(--accent-foreground));
}

/* Color Admin 命名色彩鈕（btn-purple/indigo/blue…）：全部收斂為中性 secondary，紅/粉→destructive。
   避免任何頁面殘留彩色鈕（merp 實案：商品頁「搜尋排序初始化」btn-purple）。 */
.btn-purple, .btn-indigo, .btn-blue, .btn-teal, .btn-cyan, .btn-aqua,
.btn-lime, .btn-green, .btn-orange, .btn-yellow, .btn-grey, .btn-gray, .btn-silver {
  /* ★已被 L4453 覆蓋（實測生效值：--bs-btn-bg=hsl(49.4 100% 50%)；--bs-btn-border-color=hsl(49.4 100% 50%)；--bs-btn-color=hsl(0 0% 10.2%)） */
  --bs-btn-bg: hsl(var(--secondary)); --bs-btn-border-color: hsl(var(--border)); --bs-btn-color: hsl(var(--secondary-foreground));
  /* ★已被 L4453 覆蓋（實測生效值：--bs-btn-hover-bg=hsl(50.9 100% 60%)；--bs-btn-hover-border-color=hsl(50.9 100% 60%)；--bs-btn-hover-color=hsl(0 0% 10.2%)） */
  --bs-btn-hover-bg: hsl(var(--accent)); --bs-btn-hover-border-color: hsl(var(--border)); --bs-btn-hover-color: hsl(var(--accent-foreground));
  /* ★已被 L4453 覆蓋（實測生效值：--bs-btn-active-bg=hsl(49.3 100% 45.1%)；--bs-btn-active-border-color=hsl(49.3 100% 45.1%)；--bs-btn-active-color=hsl(0 0% 10.2%)） */
  --bs-btn-active-bg: hsl(var(--accent)); --bs-btn-active-border-color: hsl(var(--border)); --bs-btn-active-color: hsl(var(--accent-foreground));
  background-color: hsl(var(--secondary)); border-color: hsl(var(--border)); color: hsl(var(--secondary-foreground));
}
.btn-red, .btn-pink {
  --bs-btn-bg: hsl(var(--destructive)); --bs-btn-border-color: hsl(var(--destructive)); --bs-btn-color: hsl(var(--destructive-foreground));
  --bs-btn-hover-bg: hsl(var(--destructive) / .88); --bs-btn-hover-border-color: hsl(var(--destructive) / .88); --bs-btn-hover-color: hsl(var(--destructive-foreground));
  background-color: hsl(var(--destructive)); border-color: hsl(var(--destructive)); color: hsl(var(--destructive-foreground));
}
/* ★安全網：上述色鈕（info/success/warning…）收斂成「淺色 secondary 底」後，view 裡寫死的 text-white
   會變成白字＋白底＝整顆隱形（merp 實例：已派發任務 in_progress pill `btn-info text-white`，
   圖示與 #單號全看不見）。在這些收斂鈕上，強制把 text-white 打回按鈕前景色（深色可見）。
   bg-dark/bg-danger 等真深底的 text-white 不受影響。 */
.btn-success.text-white, .btn-warning.text-white, .btn-info.text-white,
.btn-secondary.text-white, .btn-light.text-white, .btn-white.text-white,
.btn-purple.text-white, .btn-indigo.text-white, .btn-blue.text-white, .btn-teal.text-white,
.btn-cyan.text-white, .btn-aqua.text-white, .btn-lime.text-white, .btn-green.text-white,
.btn-orange.text-white, .btn-yellow.text-white, .btn-grey.text-white, .btn-gray.text-white, .btn-silver.text-white {
  color: hsl(var(--secondary-foreground)) !important;
}
/* ★肥肥專屬安全網：.btn-primary 是黃底黑字，view 若寫死 text-white 會直接隱形。
   （merp 沒有這條，因為它的 primary 是近黑底白字，text-white 剛好對。） */
.btn-primary.text-white { color: hsl(var(--primary-foreground)) !important; }


/* =============================================================================
 * §5 Card / Panel + #content 連結色（移植自 merp §5 L251-318）
 * ============================================================================= */
.card {
  --bs-card-bg: hsl(var(--card));
  --bs-card-color: hsl(var(--card-foreground));
  --bs-card-border-color: hsl(var(--border));
  /* shadcn Card = rounded-xl。內圓角＝外圓角減 1px 邊框寬（幾何推導，不是新階） */
  --bs-card-border-radius: var(--radius-xl);
  --bs-card-inner-border-radius: calc(var(--radius-xl) - 1px);
  /* ★已被 §23 覆蓋（實測生效值：background=hsl(var(--card))） */
  background: hsl(var(--card));
  border: 1px solid hsl(var(--border));
  box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.04);
}
/* 既有頁面用 .card.border-0 做 flush 卡片 → 仍給細框與圓角，維持 shadcn 面板感 */
/* ★已被 §23 覆蓋（實測生效值：border=1px solid rgb(228, 228, 231)） */
.card.border-0 { border: 1px solid hsl(var(--border)) !important; }
/* 表格貼卡片底（全站通病）：末列底線與卡框疊成雙線、列背景（hover/striped）戳破卡片圓角。
   末列去底線；貼底的 table/table-responsive 繼承卡片圓角把背景裁掉（overflow-y hidden 只裁圓角溢出，
   不影響 dropdown——表格內下拉本就依 uiux 規範用 dropdown-toggle 版型避開 overflow 容器）。 */
.card .table > tbody > tr:last-child > td,
.card .table > tbody > tr:last-child > th { border-bottom-width: 0; }
.card > .table-responsive:last-child,
  /* 卡片內表格的下緣圓角 = 卡片外圓角減 1px 邊框寬（幾何推導，跟著卡片走） */
.card > .table:last-child {
  border-bottom-left-radius: calc(var(--radius-xl) - 1px);
  border-bottom-right-radius: calc(var(--radius-xl) - 1px);
  overflow-x: auto; overflow-y: auto;
}
.card > .table-responsive:last-child .table { margin-bottom: 0; }
.card-header, .card-footer {
  /* ★已被 §23 覆蓋（實測生效值：background=hsl(var(--card)) !important） */
  background: transparent;
  /* ★已被 §23 覆蓋（實測生效值：border-color=rgb(228, 228, 231)） */
  border-color: hsl(var(--border));
  font-weight: 600;
  /* ★已被 §23 覆蓋（實測生效值：color=rgb(9, 9, 11)） */
  color: hsl(var(--card-foreground));
}
/* ★2026-07-27：上面那條 font-weight:600 是從 merp 原樣移植的，在 merp 成立是因為
   merp 的 card-header 幾乎只放標題。肥肥不同 —— card-header 被大量當成
   「篩選 / 批次操作工具列」用（Product/index 有兩層、Order 全家、BoxSpec…），
   於是工具列裡的說明小字、篩選 label、分頁統計全部一起變粗
   （實例：/buy/suppliers 的「與 mallbic 雙向 — 增刪改經 Chrome 擴充寫回」
     明明是 text-muted small，卻渲染成 14px 粗體，比表頭還搶眼）。
   真正的標題在兩個專案裡都自己帶 .fw-bold（`card-header p-3 fw-bold`），
   所以這裡只把「明確標成次要」的內容還原成一般字重，標題不受影響。 */
.card-header .text-muted,
.card-header small,
.card-header .small,
.card-header label,
.card-header .form-label,
.card-header .form-control,
.card-header .form-select,
.card-footer .text-muted,
.card-footer small,
.card-footer .small { font-weight: 400; }

/* 表格內固定尺寸縮圖（修 Color Admin img 規則把 inline 尺寸撐大成原圖）；本檔最後載入故 !important 可勝出 */
/* ★2026-08-04 稽核：blade 0 命中 */
.dash-thumb { width: 32px !important; height: 32px !important; object-fit: cover; flex: 0 0 auto; display: inline-block; vertical-align: middle; }
.table img.rounded { max-width: 48px; max-height: 48px; }
/* 列表商品縮圖（商品/訂單列表共用，SSOT）。Color Admin img{max-width:100%} 會撐爆 → !important 鎖尺寸 */
/* ★2026-08-04 稽核：blade 0 命中 */
.list-thumb { width: 40px !important; height: 40px !important; object-fit: cover; border-radius: 6px; border: 1px solid hsl(var(--border)); background: hsl(var(--muted)); flex: 0 0 auto; display: inline-block; vertical-align: middle; }
/* ★2026-08-04 稽核：blade 0 命中 */
.list-thumb-placeholder { display: inline-flex; align-items: center; justify-content: center; width: 40px; height: 40px; border-radius: 6px; border: 1px solid hsl(var(--border)); background: hsl(var(--muted)); color: hsl(var(--muted-foreground)); flex: 0 0 auto; vertical-align: middle; font-size: .85rem; }
/* 訂單列表：多商品縮圖疊放（avatar stack） */
/* ★2026-08-04 稽核：blade 0 命中 */
.list-thumb-stack { display: inline-flex; align-items: center; }
/* ★2026-08-04 稽核：blade 0 命中 */
.list-thumb-stack .list-thumb, .list-thumb-stack .list-thumb-placeholder { margin-inline-start: -10px; box-shadow: 0 0 0 2px hsl(var(--card)); }
/* ★2026-08-04 稽核：blade 0 命中 */
.list-thumb-stack > :first-child { margin-inline-start: 0; }
/* ★2026-08-04 稽核：blade 0 命中 */
.list-thumb-more { font-size: .75rem; color: hsl(var(--muted-foreground)); margin-inline-start: 6px; }
/* 安全網：內容區表格內任何「未套縮圖 class」的圖片一律鎖成縮圖尺寸（防某些 plugin CSS 的
   img max-width:100% 把 inline 尺寸撐成原圖）。符合「列表頁圖片一律縮圖」原則。 */
.app-content table td img:not(.list-thumb):not(.dash-thumb):not(.fb-thumb-img):not(.fb-doc-img) { max-width: 56px !important; max-height: 56px !important; object-fit: cover; }
/* 安全網：卡片標題列殘留的 text-white 反轉為卡片前景色，不隱形 */
.app-content .card-header .text-white { color: hsl(var(--card-foreground)) !important; }

/* Color Admin 把 a 設成主題色，蓋過 --bs-link-color。內容區連結改 shadcn 前景色
   （按鈕 .btn 不動；Bootstrap .text-* 語意色用 !important 會自然勝出，保留語意）。
   ＝正宗 shadcn 連結樣式：前景色＋淡底線(underline-offset 4px)標可點、hover 底線加深；不用藍色。
   ★以「語意」排除非內容連結（比照 .btn）：
     - .dropdown-item：下拉選單項目，套底線會變成一條條假超連結
     - .nav-link：分頁/導覽項目。nav-tabs 自己已有選中指示線，再疊一條文字底線 =「兩條底線」
     - .fb-tab：本檔 §13 的 shadcn Tabs
     - .page-link：分頁數字鈕（merp 原本漏掉，肥肥補上；否則 1 2 3 4 全被畫底線） */
#content a:not(.btn):not(.dropdown-item):not(.nav-link):not(.fb-tab):not(.page-link),
.app-content a:not(.btn):not(.dropdown-item):not(.nav-link):not(.fb-tab):not(.page-link) {
  color: hsl(var(--link));
  font-weight: 500;
  text-decoration: underline;
  text-underline-offset: 4px;
  text-decoration-color: hsl(var(--muted-foreground) / .45);
}
#content a:not(.btn):not(.dropdown-item):not(.nav-link):not(.fb-tab):not(.page-link):hover,
.app-content a:not(.btn):not(.dropdown-item):not(.nav-link):not(.fb-tab):not(.page-link):hover {
  color: hsl(var(--link-hover));
  text-decoration-color: hsl(var(--foreground));
}


/* =============================================================================
 * §6 Form（移植自 merp §6 L319-342，另補 merp §2 L101-111 的統一 focus ring）
 * ============================================================================= */
.form-control, .form-select {
  --bs-border-color: hsl(var(--input));
  /* ★2026-08-07 §S-68 訂正：下一行括號裡那個「rgb(250, 250, 250)」是 --background 還是
     `0 0% 98%` 的時候量的，§S-68 把它改成純白之後現在實測是 rgb(255, 255, 255)。
     章節引用（§25）本身沒變、仍然正確，過期的只有那個實測值。
     ★輸入框因此在白卡片上變成齊平白底＋1px --input 框 —— 官方 Input 就是
       `bg-transparent` + `border-input`，這是回到官方不是壞掉（邊框對比 22 → 27，變強）。 */
  /* ★已被 §25 覆蓋（實測生效值：background-color=rgb(250, 250, 250)） */
  background-color: hsl(var(--background));
  border: 1px solid hsl(var(--input));
  border-radius: var(--radius);
  /* ★已被 §25 覆蓋（實測生效值：color=rgb(9, 9, 11)） */
  color: hsl(var(--foreground));
  box-shadow: none;
}
.form-control-sm, .form-select-sm { border-radius: var(--radius-md); }
.form-control::placeholder { color: hsl(var(--hint)); }
.form-control:disabled, .form-select:disabled, .form-control[readonly] { background-color: hsl(var(--muted)); opacity: 1; }
.form-label { font-weight: 500; color: hsl(var(--foreground)); margin-bottom: 0.35rem; }
.input-group-text {
  /* ★已被 §25 覆蓋（實測生效值：background=hsl(var(--muted))） */
  background: hsl(var(--muted));
  /* ★已被 §25 覆蓋（實測生效值：border-color=rgb(228, 228, 231)） */
  border-color: hsl(var(--input));
  /* ★已被 §S-31 覆蓋（實測生效值：color=rgb(105, 105, 114)） */
  color: hsl(var(--muted-foreground));
}
/* ★轉換：merp checkbox 勾選用 primary（近黑）→ 肥肥改 --dark（黃勾勾會糊掉且太搶眼）。
   完整 checkbox / radio / switch 見 §35。 */
.form-check-input { border-color: hsl(var(--input)); }
.form-check-input:checked { background-color: hsl(var(--dark)); border-color: hsl(var(--dark)); }
/* ★§S-73：原本是 `border-color: hsl(var(--ring)); box-shadow: none;`（原值記在此存查）。
   官方 checkbox 的焦點寫法是 `focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50`
   —— border 換 ring 色**加上**一圈 3px 淡環，兩層缺一不可。這裡補上外環那一層。 */
.form-check-input:focus { border-color: hsl(var(--ring)); box-shadow: 0 0 0 3px hsl(var(--ring) / .5); }
/* form-floating（登入頁等）label 色 */
.form-floating > label { color: hsl(var(--muted-foreground)); }

/* 統一 focus ring（shadcn：2px ring + offset）。merp 放在 §2，肥肥目標檔 §2 沒有 → 這裡補上。 */
.btn:focus-visible,
.form-control:focus,
.form-select:focus,
.page-link:focus-visible,   /* ★2026-08-04 稽核：原為 :focus —— <a> 用 :focus
                               連滑鼠點擊都會留著焦點框（點完第 3 頁那顆一直亮）。
                               shadcn 一律用 :focus-visible（只有鍵盤導覽才亮）。
                               ★同組的 .form-control/.form-select 維持 :focus 是對的：
                                 瀏覽器對文字輸入框本來就把點擊也算 focus-visible，
                                 而且輸入中看得到框是使用者預期。 */
.dropdown-item:focus-visible {
  /* ★§S-73：原本是 `outline: 2px solid hsl(var(--ring)); outline-offset: 1px; box-shadow: none;`
     （原值記在此存查）。改成官方的兩層：border 換 ring 色（實色）＋ 3px 的 ring/50 柔環。 */
  outline: none;
  outline-offset: 0;
  box-shadow: 0 0 0 3px hsl(var(--ring) / .5);
  border-color: hsl(var(--ring));
}


/* =============================================================================
 * §7 Table（移植自 merp §7 L343-380）
 * ============================================================================= */
.table {
  --bs-table-bg: transparent;
  --bs-table-color: hsl(var(--foreground));
  --bs-table-border-color: hsl(var(--border));
  /* merp 寫死 hsl(240 4.8% 97.5%) 並另外在暗色再蓋一次 → 改吃 token + alpha，自動翻轉 */
  --bs-table-striped-bg: hsl(var(--muted) / .55);
  --bs-table-striped-color: hsl(var(--foreground));
  --bs-table-hover-bg: hsl(var(--accent));
  --bs-table-hover-color: hsl(var(--accent-foreground));
  vertical-align: middle;
  border-color: hsl(var(--border));
}
/* ★★§S-74 表頭 SSOT 收斂（2026-08-07）—— 下面兩條原本是同一條，拆開的理由與量測全記在這裡。
 *
 * ═══ 收斂前：同一顆 <thead> 的 th 上有「三條」背景宣告在打架 ═══
 *   （CDP `CSS.getMatchedStylesForNode` 對真實 th 節點問出來的，不是推論）
 *     ① 本條           `.table > thead th`                      (0,1,2)  background: --muted      !important
 *     ② §23  L2090     `.table th, .table-panel th, .table-panel thead th`
 *                                                               (0,1,1)  background: --card       !important
 *     ③ §7b  L958      `.app-content .table > thead > tr > th`   (0,2,3)  background: transparent  !important
 *   三條都帶 !important，只靠特異度分勝負 → **實際的全站贏家是 ③**（不是 ① 也不是 ②）。
 *   前景同樣三處（①② 帶 !important，③ 與 §補位7 L5903 不帶）→ 贏家是 ①。
 *   「同一件事有三處決定」＝ 沒有 SSOT：任何人要改表頭底色，都得先自己推一次特異度
 *   才知道改的那一行到底會不會生效。這正是本次要收掉的東西。
 *
 * ═══ 收斂後：`.table > thead th` 的背景與前景各由哪一行決定 ═══
 *   背景 ＝ 本條（基底，非後台外殼的頁面吃這個，例如買家端 inv.*）
 *          ＋ §7b L958 `.app-content …`（後台外殼的變體，特異度 0,2,3 明確較高）
 *   前景 ＝ 本條（基底）
 *          ＋ §補位7 L5903 `.app-content …`（後台外殼的變體，同上）
 *   §23 從此不再碰任何 <thead> 的 th（見該處註解）；`.table-light` 的字色由 §S-31 一處決定。
 *   ★「基底 ＋ .app-content 變體」是刻意保留的兩層，不是打架：變體的特異度高一個量級、
 *     而且背景與前景成對出現（給了 transparent 就同時給 --muted-foreground），
 *     不會像收斂前那樣「背景吃 A 條、前景吃 B 條」。
 *
 * ═══ 原值記在此存查（收斂前的完整原文，含它自己那行註解，一字不動）═══
 *     `.table > thead th,`
 *     `.table-light, .table-light > th, .table-light > td {`
 *     `  background: hsl(var(--muted)) !important;`
 *     （此處原本夾了一行 CSS 註解，內文一字不動抄錄如下 —— 只把它的 /／* 與 *／/ 括號拿掉，
 *       否則抄在註解裡會提早把本註解關掉：）
 *       ★〔.table-light／.table-light > th／.table-light > td〕已被 §S-31 覆蓋；〔.table > thead th〕未被覆蓋（實測生效值：color=rgb(105, 105, 114)）
 *     `  color: hsl(var(--muted-foreground)) !important;`
 *     `  font-weight: 500;`
 *     `  text-transform: none;`
 *     `  border-color: hsl(var(--border));`
 *     `}`
 *   ★上面那行舊註解說「實測生效值 color=rgb(105,105,114)」與本輪實測不符：
 *     本輪 CDP 量到的生效值是 rgb(115,115,115)（＝ §S-72 把調色盤從 zinc 換成 neutral 之後的
 *     --muted-foreground `0 0% 45.1%`）。舊註解的 rgb(105,105,114) 是
 *     --muted-foreground-on-muted（zinc 時代的 `240 3.8% 43%`）的值。舊註解原文保留不刪，
 *     但它已經過期，不要照著它推論。
 *
 * ═══ !important 逐項查證（不是「看起來不需要」就拿掉）═══
 *   background：**保留**。拿掉 §23 的 `.table th` 之後，同名競爭者只剩
 *     bootstrap.min.css `.table > :not(caption) > * > *`(0,1,1) 與本檔 §23 L2102 同名那條(0,1,1)，
 *     這兩條確實輸給 (0,1,2)；但 DataTables 還有一條
 *     `table.table.dataTable > :not(caption) > * > *`(0,2,2, datatables.css:355)
 *     特異度比本條高 —— 今天它打不到（DataTables 只出現在後台，那裡由 ③ 作主），
 *     但一旦哪天 DataTables 出現在沒有 .app-content 的頁面，拿掉就會破。留著，代價是零。
 *   color：**刻意拿掉**。加了 !important 會讓本條 (0,1,2) 反過來壓過
 *     §補位7 L5903 `.app-content …`(0,2,3，不帶 !important)，等於把「後台每一張表的表頭」
 *     一起改深 —— 而後台表頭的底是 ③ 的 transparent（透出 --card 白），
 *     白底配 --muted-foreground 實測 4.74:1 本來就過 AA，
 *     §S-31 L9224-9225 也早就寫過「不需要也不該動（動了整站表頭會無謂變深）」。
 *     不帶 !important 的結果才是對的配對：
 *       誰給了 --muted 底（本條）→ 就由誰給 --muted 上的配對前景（--muted-foreground-on-muted）
 *       誰給了 transparent/--card 底（③ / §補位7）→ 就由誰給白底上的前景（--muted-foreground）
 *     背景與前景永遠來自同一層，不會再被拆開。
 *
 * ═══ 實測（CDP 取渲染像素，1600×1000，亮/暗各一遍）═══
 *   後台 /inventory/product 亮  改前 rgb(115,115,115) on rgb(255,255,255) = 4.74
 *                               改後 rgb(115,115,115) on rgb(255,255,255) = 4.74（本條不影響後台，如上）
 *   後台 /inventory/product 暗  改前後同為 rgb(161,161,161) on rgb(23,23,23) = 6.94
 *   買家端 /result/… 亮        改前 rgb(115,115,115) on rgb(255,255,255) = 4.74
 *                               改後 rgb(105,105,114) on rgb(255,255,255) = 5.43
 *     （買家端的底仍是白，因為 buyer/layout.blade.php:358 有一行頁面自己的
 *       `.table > thead th{background:hsl(var(--card))!important}` 蓋掉本條的 --muted。
 *       那行是本輪之前為了同一個對比問題加的，收斂後已成冗餘 —— 改 blade 不在本輪授權內，已回報。）
 *   若把買家端那一行拿掉，本條自己的配對是 rgb(105,105,114) on rgb(245,245,245) = 4.98（過 AA）。
 *   ★工單假設的「全站表頭 --muted 底、4.35:1」在本專案**一頁都量不到** ——
 *     後台被 ③ 蓋成白底、買家端被頁面自己那行蓋成白底。4.35 是本條「如果贏了」的理論值，
 *     不是任何一頁的實際值。本條改吃配對前景，是把那個理論值一併補起來（4.35 → 4.98）。 */
.table > thead th {
  background: hsl(var(--muted)) !important;
  /* ★改吃 §S-31 的配對前景：本條自己給了 --muted 底，就必須給 --muted 上過 AA 的前景。
     原值記在此存查：`color: hsl(var(--muted-foreground)) !important;`（--muted 上實測 4.35:1，未達 4.5）。 */
  color: hsl(var(--muted-foreground-on-muted));
  font-weight: 500;
  text-transform: none;
  border-color: hsl(var(--border));
  /* ★從 §23 L2090 原值搬過來的（那條收斂後不再碰 thead，但表頭的下框線仍然需要）：
     `border-bottom: 1px solid hsl(var(--border));` —— 值一字不動，只是換人管。 */
  border-bottom: 1px solid hsl(var(--border));
}
/* `.table-light` 是 Bootstrap 的「淺色列／淺色區段」語彙，可以掛在 <thead>／<tr>／<th>／<td> 上，
   跟「表頭」根本不是同一件事 —— 它原本跟 `.table > thead th` 綁在同一個選擇器清單裡，
   正是上面那場三方混戰看不出誰管誰的原因之一，所以拆成獨立一條。
   ★這條的 !important **不能拿掉**：`.table-light > th` / `.table-light > td` 是 (0,1,1)，
     與本檔 §23 L2102 `.table > :not(caption) > * > * { background-color: transparent; }` 同分，
     而那條排在後面 → 不帶 !important 會被它蓋掉，淺色區段整片消失。
   ★字色**刻意不再宣告**：`.table-light` 的字色本來就已經由 §S-31 L9236-9242 接手
     （`color: hsl(var(--muted-foreground-on-muted)) !important`，那裡有完整的量測理由與
       「三種掛法都收」的說明），留在這裡只是重複宣告、而且永遠輸給它。
     原值記在此存查：`color: hsl(var(--muted-foreground)) !important;` */
.table-light, .table-light > th, .table-light > td {
  background: hsl(var(--muted)) !important;
  font-weight: 500;
  text-transform: none;
  border-color: hsl(var(--border));
}
.table.table-bordered, .table.table-bordered > :not(caption) > * > * { border-color: hsl(var(--border)); }
.table > tbody > tr { border-color: hsl(var(--border)); }
.table > :not(caption) > * > * { border-bottom-color: hsl(var(--border)); }
/* 排序 icon hook（行為不動，只調色） */
/* ★2026-08-04 稽核：blade 0 命中 */
.table-field-sort { color: hsl(var(--muted-foreground)); cursor: pointer; }
/* ★2026-08-04 稽核：blade 0 命中 */
.table-field-sort.sort-acitve { color: hsl(var(--foreground)); }
/* ★2026-08-04 稽核：`sort-acitve` 是 `sort-active` 的拼錯，且 resources/views 底下
   **兩種拼法都 0 命中** → 這條規則永遠不會套到任何東西。保留不刪（本專案禁刪），
   但若日後要接排序高亮，要用的是正確拼法，不要照抄這一行。 */

/* 凍結欄：table 加 .table-stick、要固定的 th/td 加 .stick（必須是最左邊連續數欄），
   最後一根加 .stick-end 畫分界線。left 位移由 JS 實測欄寬後寫入，
   不可在頁面寫死 px（欄寬隨內容變，寫死必錯位）。 */
.table-stick > thead > tr > th.stick,
.table-stick > tbody > tr > td.stick { position: sticky; left: 0; z-index: 2; }
.table-stick > tbody > tr > td.stick { background: hsl(var(--card)); }
.table-stick > thead > tr > th.stick { z-index: 3; }
.table-stick > tbody > tr:hover > td.stick { background: hsl(var(--accent)); color: hsl(var(--accent-foreground)); }
.table-stick > thead > tr > th.stick-end,
.table-stick > tbody > tr > td.stick-end { border-right: 1px solid hsl(var(--border)); }

/* ---------------------------------------------------------------------------
 * §7b 表格 shadcn 化：去直框 / 去斑馬，只留橫線 + muted 表頭 + row hover
 *     （移植自 merp §11 L1057-1100 的表格段；merp 選擇器是 .admin-content（它的新外殼），
 *      肥肥沿用 Color Admin 外殼 → 改成 .app-content）
 * --------------------------------------------------------------------------- */
.app-content .table { --bs-table-bg: transparent; border-color: hsl(var(--border)); margin-bottom: 0; }
/* 去掉 table-bordered 的直向框線，只保留橫線 */
.app-content .table.table-bordered > :not(caption) > * > * { border-inline-width: 0; border-block-color: hsl(var(--border)); }
.app-content .table > :not(:first-child) { border-top: 0; }
/* 表頭：透明底 + muted 字 + 細下框 */
.app-content .table > thead > tr > th {
  /* ★已被 L5333 覆蓋（實測生效值：font-weight=500） */
  background: transparent !important; color: hsl(var(--muted-foreground)); font-weight: 500;
  border-bottom: 1px solid hsl(var(--border)); border-top: 0;
  padding: .6rem .75rem; white-space: nowrap; vertical-align: middle;
}
/* 內容列：去斑馬、舒適 padding、垂直置中、細橫線 */
.app-content .table.table-striped > tbody > tr > * { --bs-table-bg-type: transparent; --bs-table-accent-bg: transparent; }
.app-content .table:not(.table-bordered) > tbody > tr > td { border-color: hsl(var(--border)); border-inline-width: 0; padding: .6rem .75rem; vertical-align: middle; }
/* ★★ .table-bordered 必須留直線（2026-07-27 逐頁掃描抓到）：
   上面那條 border-inline-width: 0 是為了讓一般列表「只有橫線比較清爽」，
   但它把請假日曆（hr/leave/calendar）與月排班表（hr/schedule/monthly）
   一起殺了 —— 那兩頁明確寫了 table-bordered，因為日曆沒有直線就分不出欄位。
   實測 35 個 td 的四邊 border 全是 0px，畫面上 7 欄日曆變成一整條橫槓。
   月曆這種版型「格線是資訊不是裝飾」，所以排除 .table-bordered。 */
.app-content .table.table-bordered > tbody > tr > td,
.app-content .table.table-bordered > thead > tr > th {
    border: 1px solid hsl(var(--border));
    padding: .6rem .75rem;
    vertical-align: middle;
}
.app-content .table > tbody > tr:last-child > td { border-bottom: 0; }
.app-content .table-hover > tbody > tr:hover > * { background: hsl(var(--muted)); color: hsl(var(--foreground)); }

/* 表格內動作圖示鈕（編輯 pencil / 檢視 eye 等）→ shadcn 次要按鈕：有底色+邊框，
   看得出是按鈕（不用透明 ghost，避免只像一個灰圖示）；hover 變亮。
   ★刻意偏離 merp：merp 這條選擇器還含 `.btn-sm.rounded-pill`（形狀選擇器），
     那會把 uiux 規範的「新增」鈕（a.btn.btn-sm.btn-rounded.rounded-pill）壓成 32px 方塊。
     依肥肥硬規「形狀收斂不得掃到 a/button」，只留 href 語意選擇器。 */
.app-content .table td a.btn[href*="modify"],
.app-content .table td a.btn[href*="edit"],
.app-content .table td a.btn[href*="view"] {
  /* ★只接管「尺寸」，顏色一律交還按鈕變體決定。
     原本這裡用 !important 寫死 secondary 底，導致規範的「列表操作鈕＝btn-dark」
     永遠出不來（全站列表頁的編輯鈕都被壓成淺灰）。 */
  /* ★★ 2026-07-27 逐頁掃描修正：原本這裡寫 width: 32px + padding: 0，
     等於假設「href 含 edit/modify/view 的按鈕一定是純圖示鈕」——
     那個假設是錯的，href 是「連到哪裡」不是「長什麼樣」。
     實際被壓爛的：商店設定的「訂單授權」→「單授」、「廣告授權」→「告授」、
     「未授權」→「未授」（scrollWidth 41 > clientWidth 30，文字直接被裁掉）。
     這跟註解上面自己寫的「形狀收斂不得掃到 a/button」是同一個錯誤，
     只是把「形狀」換成了「網址」。
     改成 min-width：純圖示鈕維持 32px 正方，帶文字的自己撐開。 */
  min-width: 32px; height: 32px; padding: 0 .5rem;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: var(--radius) !important;
}

.app-content .table td .fa-lg { font-size: .95rem; }
/* 表格內按鈕不換行（如「強制登出」被擠斷）（merp §16 L1244） */
.app-content .table .btn { white-space: nowrap; }


/* =============================================================================
 * §8 Badge（移植自 merp §8 L381-393）
 * ★轉換：merp 的 .badge.bg-dark/.bg-primary 吃 primary（近黑）→ 肥肥改 --dark。
 *   黃底 badge 會在列表頁大量出現，全站灑黃 = 黃斑馬。
 * ============================================================================= */
.badge {
  font-weight: 500;
  /* ★2026-08-04 稽核：這行**不生效** —— 被後面 L1714 的 .badge{border-radius:var(--radius-md)}
     蓋掉，實測全站 .badge 是 8px 圓角矩形，不是藥丸。
     真正還是藥丸的是明確加了 .rounded-pill 的那些（L1330 帶 !important）。
     上面講「藥丸造型」的設計說明因此與現況不符，保留原文但以此註記為準。 */
  /* ★★★2026-08-09 更正（上面整段 2026-08-04 稽核原文保留不刪，以本段為準）：
     ⑴ 「本行不生效、被 §24 蓋掉」這半句**仍然成立**（§24 在後、同分 (0,1,0)，勝出者仍是 §24）。
     ⑵ 但「實測全站 .badge 是 8px 圓角矩形，不是藥丸」這半句**已經過時**：
        §24 的值已於 2026-08-09 依 shadcn 官方 `badge.tsx`（base class 為 `rounded-full`）
        改成 9999px。⇒ 現在兩條 `.badge` 同值，全站 badge 是藥丸，**不需要**靠呼叫端的
        `.rounded-pill` 才會圓。實測（無痕、注入探針、量 computed borderRadius）：
        `.badge` / `.badge.bg-*` 一律 9999px，高度維持 20px。
     ⑶ 唯一例外是 `.reissue-type-badge`（§S-90，另有 `border-radius: var(--radius-md) !important`），
        它仍是 8px —— 那是別條線的規則，不在本輪授權範圍，未更動。
     ★★★2026-08-10 更正（上面 ⑶ 原句一字不動保留，以本段為準）：
        ⑶ 描述的例外**已經不存在**。§S-90 那條 `border-radius: var(--radius-md) !important` 已於
        2026-08-10 由該節持有者移除（存查與撤銷理由見 §S-90 `.reissue-type-badge` 規則區塊內、
        該行原文旁的移除註記）。`.reissue-type-badge` 現在不再自己宣告 `border-radius`，
        改吃 §24 這裡的基底（9999px）＋ markup 上 `.rounded-pill` 的 `!important`（同為 9999px）——
        兩者同值，全站已無方角 badge，「唯一例外」不再成立。
        ★這不是 ⑶ 原句當時寫錯：那句記錄的是**量測當下的事實**（`border-radius: var(--radius-md)
        !important` 當時確實排在檔案更後面、贏過基底與 `.rounded-pill` 的兩條 `!important`，
        實測 8px 屬實）——它是**過時**，不是**錯誤**，兩者要分開判斷，否則讀的人沒辦法信任
        其他還沒過時的註解。
        ★判準：註解若描述「目前唯一的例外」這類會隨他人改動而失效的斷言，要標明量測時間點；
        該例外一旦被移除，要回頭在原句下方追加更正（而不是改掉原句，也不是放著不管）。
     ★教訓：上面那段稽核把「當下量到的 8px」寫成了「.badge 應該是 8px」。
       量測值是**現況快照**，不是**規格**；把快照寫成規格，下一個人就會拿它當依據去改別的地方。
       描述現況的註解請一律寫成「目前量到 X（YYYY-MM-DD）」，不要寫成「就是 X」。 */
  border-radius: 9999px;
  /* ★已被 §24Badge 覆蓋（實測生效值：border=1px solid rgba(0, 0, 0, 0)） */
  border: 1px solid transparent;
}
/* ★已被 L4574 覆蓋（實測生效值：background=hsl(var(--secondary)) !important；color=rgb(9, 9, 11)） */
.badge.bg-secondary, .badge.text-bg-secondary { background: hsl(var(--secondary)) !important; color: hsl(var(--secondary-foreground)) !important; }
/* ★已被 L1862 覆蓋（實測生效值：background=hsl(var(--dark)) !important；color=rgb(250, 250, 250)） */
.badge.bg-dark, .badge.bg-primary { background: hsl(var(--dark)) !important; color: hsl(var(--dark-foreground)) !important; }
/* ★已被 L4572 覆蓋（實測生效值：background=hsl(var(--destructive)) !important；color=rgb(255, 255, 255)） */
.badge.bg-danger { background: hsl(var(--destructive)) !important; color: hsl(var(--destructive-foreground)) !important; }
.badge.bg-success { background: hsl(var(--success)) !important; color: hsl(var(--success-foreground)) !important; }
/* ★已被 L4562 覆蓋（實測生效值：background=hsl(var(--warning-bg)) !important；color=rgb(152, 89, 16)） */
.badge.bg-warning { background: hsl(var(--warning)) !important; color: hsl(var(--warning-foreground)) !important; }
.badge.bg-light, .badge.bg-white { background: hsl(var(--muted)) !important; color: hsl(var(--muted-foreground)) !important; border-color: hsl(var(--border)); }


/* =============================================================================
 * §9 Alert（移植自 merp §9 L394-404；柔和底色改吃 --success-bg/--warning-bg token，
 *    完整覆蓋在 §28）
 * ============================================================================= */
.alert {
  border-radius: var(--radius);
  border: 1px solid hsl(var(--border));
}
/* ★已被 L2044 覆蓋（實測生效值：background=hsl(var(--muted)) !important；color=rgb(9, 9, 11)）
   ★2026-08-06 訂正行號：L2044 已漂掉，實際勝出者是 §4 後面那條
   `.alert-success,.alert-info,.alert-primary,.alert-secondary,.alert-light{...!important}`。
   值本身仍正確 —— .alert-info 實測 bg=rgb(244,244,245) / color=rgb(9,9,11)。 */
.alert-info { background: hsl(var(--muted)); color: hsl(var(--foreground)); border-color: hsl(var(--border)); }
.alert-light { background: hsl(var(--card)); color: hsl(var(--muted-foreground)); border-color: hsl(var(--border)); }
/* merp 寫死 hsl(38 92% 95%) / hsl(142 71% 96%) / hsl(0 86% 97%) → 換成 token，暗色自動翻轉 */
.alert-warning { background: hsl(var(--warning-bg)); color: hsl(var(--warning-fg)); border-color: hsl(var(--warning-fg) / .25); }
/* ★2026-08-06：.alert-success 全檔共有 4 條宣告（本行、§4 後面的中性化、§修正的還原，
   加上更早的 .alert 通用底）。最終勝出的是**檔尾 §修正「3. alert-success 底色還原」**，
   它用 !important 把值改回 --success-bg/--success-fg —— 也就是**跟本行宣告的值相同**。
   ⇒ 本行「看起來」生效，但實際生效的是檔尾那條；改本行不會改變畫面。
   實測 bg=rgb(231,249,237) / color=rgb(25,118,59) / border=rgba(25,118,59,.25)。 */
.alert-success { background: hsl(var(--success-bg)); color: hsl(var(--success-fg)); border-color: hsl(var(--success-fg) / .25); }
/* 紅字一律走 --destructive-fg（見 §1 token 註解）：底維持 --destructive/.1 不動，
   只把「文字」換成深一階的紅 —— 原本 #EF4444 在這個淡紅底上只有 2.82:1。 */
/* ★已被 background→L2054(hsl(var(--destructive) / .1) !important)、color→L2054(rgb(186, 28, 28))、border-color→L1948(rgba(220, 38, 38, 0.25)) 覆蓋 */
.alert-danger  { background: hsl(var(--destructive) / .1); color: hsl(var(--destructive-fg)); border-color: hsl(var(--destructive) / .25); }


/* =============================================================================
 * §10 Dropdown（移植自 merp §10 L405-420）
 * ★轉換：active 項 merp 用 primary → 肥肥用 --dark（選單反白不需要品牌黃）
 * ============================================================================= */
.dropdown-menu {
  --bs-dropdown-bg: hsl(var(--popover));
  --bs-dropdown-color: hsl(var(--popover-foreground));
  --bs-dropdown-border-color: hsl(var(--border));
  --bs-dropdown-border-radius: var(--radius);
  --bs-dropdown-link-color: hsl(var(--popover-foreground));
  --bs-dropdown-link-hover-bg: hsl(var(--accent));
  --bs-dropdown-link-hover-color: hsl(var(--accent-foreground));
  --bs-dropdown-link-active-bg: hsl(var(--dark));
  --bs-dropdown-link-active-color: hsl(var(--dark-foreground));
  padding: 0.35rem;
  box-shadow: 0 8px 24px -6px rgb(0 0 0 / 0.12), 0 2px 6px -2px rgb(0 0 0 / 0.08);
}
.dropdown-item { border-radius: var(--radius-md); padding: 0.45rem 0.65rem; }
.dropdown-item:hover, .dropdown-item:focus { background: hsl(var(--accent)); color: hsl(var(--accent-foreground)); }
.dropdown-divider { margin: .3rem 0; border-color: hsl(var(--border)); }


/* =============================================================================
 * §11 Breadcrumb（移植自 merp §11 L421-425）
 * ============================================================================= */
.breadcrumb { --bs-breadcrumb-divider-color: hsl(var(--muted-foreground)); }
.breadcrumb-item, .breadcrumb-item a { color: hsl(var(--muted-foreground)); }
.breadcrumb-item.active { color: hsl(var(--foreground)); }


/* =============================================================================
 * §12 Pagination（移植自 merp §12 L426-442 + merp §33 L2107-2148）
 * ★轉換：active 頁碼 merp 用 primary → 肥肥用 --dark
 *   （分頁 active 是「你在第幾頁」的狀態指示，不是主要動作，不該用品牌黃）
 * ============================================================================= */
.pagination {
  --bs-pagination-bg: transparent;
  --bs-pagination-color: hsl(var(--muted-foreground));
  --bs-pagination-border-color: hsl(var(--border));
  --bs-pagination-border-radius: var(--radius);
  --bs-pagination-hover-bg: hsl(var(--accent));
  --bs-pagination-hover-color: hsl(var(--accent-foreground));
  --bs-pagination-hover-border-color: hsl(var(--border));
  --bs-pagination-focus-bg: hsl(var(--accent));
  --bs-pagination-focus-color: hsl(var(--accent-foreground));
  --bs-pagination-focus-box-shadow: 0 0 0 3px hsl(var(--ring) / .5);   /* §S-73：原 `0 0 0 2px hsl(var(--ring) / .2)`，改官方 ring-[3px] ring-ring/50 */
  --bs-pagination-active-bg: hsl(var(--dark));
  --bs-pagination-active-color: hsl(var(--dark-foreground));
  --bs-pagination-active-border-color: hsl(var(--dark));
  --bs-pagination-disabled-bg: transparent;
  --bs-pagination-disabled-color: hsl(var(--muted-foreground) / .5);
  --bs-pagination-disabled-border-color: hsl(var(--border));
  gap: .25rem;
}
.page-link {
  margin: 0;
  background: transparent !important;
  color: hsl(var(--muted-foreground)) !important;
  border: 1px solid hsl(var(--border)) !important;
  border-radius: var(--radius-md) !important;
  font-size: 13px;
}
.page-link:hover {
  background: hsl(var(--accent)) !important;
  color: hsl(var(--accent-foreground)) !important;
}
.page-item.active .page-link {
  background: hsl(var(--dark)) !important;
  color: hsl(var(--dark-foreground)) !important;
  border-color: hsl(var(--dark)) !important;
}
.page-item.disabled .page-link {
  background: transparent !important;
  color: hsl(var(--muted-foreground) / .45) !important;
  border-color: hsl(var(--border)) !important;
}


/* =============================================================================
 * §13 Tabs：忠實移植 shadcn/ui Tabs（移植自 merp §13 L443-505）
 * -----------------------------------------------------------------------------
 * 來源：shadcn-ui/ui apps/v4/registry/new-york-v4/ui/tabs.tsx
 *   TabsList(default) = inline-flex w-fit items-center justify-center h-9 rounded-lg p-[3px]
 *                       bg-muted text-muted-foreground
 *   TabsTrigger       = relative inline-flex h-[calc(100%-1px)] items-center justify-center gap-1.5
 *                       rounded-md border border-transparent px-2 py-1 text-sm font-medium
 *                       whitespace-nowrap text-foreground/60 transition-all hover:text-foreground
 *     active(default) = bg-background text-foreground shadow-sm
 * ★刻意偏離原版一處：不套 `flex-1`。原版讓每個 trigger 等寬，但標籤長短差很多
 *   （「全部」vs「30天內到期 14」）等寬會很醜 → 改為依內容寬度。
 * ★.fb-tabs 加 overflow-x:auto：分頁數量多，窄螢幕要能捲，不可被切掉。
 * ★命名：merp 的 .merp-tabs / .merp-tab → 肥肥 .fb-tabs / .fb-tab
 * ============================================================================= */
.fb-tabs {
  display: inline-flex;
  width: fit-content;
  max-width: 100%;
  align-items: center;
  justify-content: center;
  height: 2.25rem;                        /* h-9 */
  /* ★已被 §S-52 覆蓋（實測生效值：4px —— 業主要白色 chip 比框再內縮一點；軌道總高仍 36px）*/
  padding: 3px;                           /* p-[3px] */
  border-radius: var(--radius);           /* rounded-lg */
  background: hsl(var(--muted));          /* bg-muted */
  color: hsl(var(--muted-foreground));
  overflow-x: auto;
}
.fb-tab {
  position: relative;
  display: inline-flex;
  height: calc(100% - 1px);
  align-items: center;
  justify-content: center;
  gap: .375rem;                           /* gap-1.5 */
  padding: .25rem .75rem;                 /* py-1；px 略放寬給中文 */
  border: 1px solid transparent;
  border-radius: var(--radius-md); /* rounded-md */
  font-size: .875rem;                     /* text-sm */
  font-weight: 500;
  white-space: nowrap;
  color: hsl(var(--foreground) / .6);     /* text-foreground/60 */
  text-decoration: none;
  transition: color .15s, background-color .15s, box-shadow .15s;
}
.fb-tab:hover { color: hsl(var(--foreground)); }
.fb-tab.active {
  /* ★2026-08-07：亮色仍然由本行生效（官方亮色就是 `data-[state=active]:bg-background`）；
     箭頭步驟條 `.fb-tabs--flow` 亮暗兩邊也都仍然吃本行，§S-68 帶了 `:not(.fb-tabs--flow)`。 */
  /* ★已被 §S-68（暗）覆蓋〈跨模型：(a) 跨 selector 由特異性決勝〉（實測生效值：暗 background=rgba(255, 255, 255, 0.3)，疊在 --muted 軌道上合成 rgb(104, 104, 106)） */
  background: hsl(var(--background));     /* data-[state=active]:bg-background */
  /* ★已被 §S-68（暗）覆蓋〈跨模型：(a) 跨 selector 由特異性決勝〉（實測生效值：暗 color=rgb(250, 250, 250)） */
  color: hsl(var(--foreground));
  box-shadow: 0 1px 2px 0 rgb(0 0 0 / .05); /* shadow-sm */
}

/* Bootstrap nav-tabs / nav-pills
   ★轉換：nav-pills active 底 merp 用 primary → 肥肥用 --dark */
.nav-tabs {
  --bs-nav-tabs-border-color: hsl(var(--border));
  --bs-nav-tabs-link-active-color: hsl(var(--foreground));
  --bs-nav-tabs-link-active-border-color: hsl(var(--border)) hsl(var(--border)) hsl(var(--card));
}
.nav-tabs .nav-link { color: hsl(var(--muted-foreground)); border-radius: var(--radius) var(--radius) 0 0; }
.nav-tabs .nav-link:hover { color: hsl(var(--foreground)); border-color: hsl(var(--border)); }
.nav-pills {
  --bs-nav-pills-border-radius: var(--radius);
  --bs-nav-pills-link-active-bg: hsl(var(--dark));
  --bs-nav-pills-link-active-color: hsl(var(--dark-foreground));
}
.nav-pills .nav-link { color: hsl(var(--muted-foreground)); }


/* =============================================================================
 * §14 Modal（外觀，不動行為）（移植自 merp §14 L506-516）
 * ============================================================================= */
.modal-content {
  /* ★已被 §25 覆蓋（實測生效值：border=1px solid rgb(228, 228, 231)） */
  border: 1px solid hsl(var(--border));
  border-radius: var(--radius-lg);   /* shadcn DialogContent = rounded-lg */
  box-shadow: 0 16px 48px -12px rgb(0 0 0 / 0.25);
  /* ★已被 §25 覆蓋（實測生效值：background=hsl(var(--card))） */
  background: hsl(var(--card));
  /* ★已被 §25 覆蓋（實測生效值：color=rgb(9, 9, 11)） */
  color: hsl(var(--card-foreground));
}
/* ★已被 §25 覆蓋（實測生效值：border-color=rgb(228, 228, 231) rgb(228, 228, 231) rgb(9, 9, 11)；border-color=rgb(9, 9, 11) rgb(228, 228, 231) rgb(228, 228, 231)） */
.modal-header, .modal-footer { border-color: hsl(var(--border)); }
.modal-header { font-weight: 600; }


/* =============================================================================
 * §15 Sidebar（移植自 merp §15 L517-590 + §30 L1989-2030 + §25 L1640-1688 側欄段）
 * ★ shadcn 官方 sidebar ＝亮色淺底深字（深色側欄會被判定成「沒套 shadcn」）
 * ★轉換：merp §30 的「active 用 primary 反轉色」→ 肥肥改 --dark / --dark-foreground。
 *   側欄 active 是全站最常出現的強調，用品牌黃會整條側欄變黃。
 * ============================================================================= */
#sidebar.app-sidebar,
.app-sidebar,
.app-sidebar-bg {
  /* ★已被 §25 覆蓋（實測生效值：background=hsl(var(--sidebar-bg)) !important） */
  background: hsl(var(--sidebar-bg)) !important;
}
/* 與內容區之間的細分隔線（shadcn 慣例） */
#sidebar.app-sidebar,
/* ★已被 §25 覆蓋（實測生效值：border-right=1px solid rgb(228, 228, 231)） */
.app-sidebar { border-right: 1px solid hsl(var(--sidebar-border)); }
#sidebar.app-sidebar { color: hsl(var(--sidebar-fg)); }

/* 側欄捲軸（merp §30） */
#sidebar.app-sidebar::-webkit-scrollbar { width: 6px; }
#sidebar.app-sidebar::-webkit-scrollbar-thumb { background: hsl(var(--sidebar-border)); border-radius: 3px; }
#sidebar.app-sidebar::-webkit-scrollbar-track { background: transparent; }

/* 分組標題 */
/* ★2026-08-04 稽核：blade 0 命中 */
.app-sidebar .menu-header {
  color: hsl(var(--sidebar-fg) / .6);
  text-transform: none;
  letter-spacing: 0.04em;
  font-size: 0.7rem;
  font-weight: 500;
}
.app-sidebar .menu-link {
  color: hsl(var(--sidebar-fg));
  border-radius: var(--radius);
  /* ★已被 §側欄：對齊shadcn 覆蓋（實測生效值：margin=1px 4px 4px） */
  margin: 1px 8px;
  font-weight: 400;
  transition: background-color .12s ease, color .12s ease;
}
.app-sidebar .menu-link .menu-icon { color: hsl(var(--sidebar-fg)); opacity: .9; }
.app-sidebar .menu-link:hover,
.app-sidebar .menu-link:focus {
  background: hsl(var(--sidebar-accent));
  color: hsl(var(--sidebar-fg-strong));
}
.app-sidebar .menu-link:hover .menu-icon { color: hsl(var(--sidebar-fg-strong)); }

/* active：目前頁＝深色反轉（有感）。★這裡 merp 用 --primary，肥肥改 --dark。 */
#sidebar .menu-item.active > .menu-link {
  /* ★已被 §側欄：對齊shadcn 覆蓋（實測生效值：background=hsl(var(--sidebar-accent))） */
  background: hsl(var(--dark));
  /* ★已被 §側欄：對齊shadcn 覆蓋（實測生效值：color=rgb(24, 24, 27)） */
  color: hsl(var(--dark-foreground));
  /* ★已被 §側欄：對齊shadcn 覆蓋（實測生效值：font-weight=500） */
  font-weight: 500;
}
#sidebar .menu-item.active > .menu-link .menu-icon,
/* ★已被 L6314 覆蓋（實測生效值：color=rgb(24, 24, 27)；opacity=1） */
#sidebar .menu-item.active > .menu-link .menu-caret { color: hsl(var(--dark-foreground)); opacity: 1; }
/* has-sub 群組（自己不是目的頁，只是展開中）→ 只用淡 accent，不要整條反白 */
#sidebar .menu-item.has-sub.active > .menu-link,
#sidebar .menu-item.expand > .menu-link {
  /* ★〔#sidebar .menu-item.expand > .menu-link〕已被 §側欄：對齊shadcn 覆蓋；〔#sidebar .menu-item.has-sub.active > .menu-link〕未被覆蓋（實測生效值：background=transparent） */
  background: hsl(var(--sidebar-accent));
  /* ★〔#sidebar .menu-item.expand > .menu-link〕已被 §側欄：對齊shadcn 覆蓋；〔#sidebar .menu-item.has-sub.active > .menu-link〕未被覆蓋（實測生效值：color=rgb(24, 24, 27)） */
  color: hsl(var(--sidebar-fg-strong));
}
#sidebar .menu-item.has-sub.active > .menu-link .menu-icon,
#sidebar .menu-item.has-sub.active > .menu-link .menu-caret { color: hsl(var(--sidebar-fg-strong)); }
#sidebar .menu-submenu .menu-item.active > .menu-link {
  /* ★已被 §側欄：對齊shadcn 覆蓋（實測生效值：background=hsl(var(--sidebar-accent))） */
  background: hsl(var(--dark));
  /* ★已被 §側欄：對齊shadcn 覆蓋（實測生效值：color=rgb(24, 24, 27)） */
  color: hsl(var(--dark-foreground));
}
.app-sidebar .menu-submenu { background: transparent; }
.app-sidebar .menu-submenu .menu-link { font-size: 0.85rem; }
.app-sidebar .menu-caret { color: hsl(var(--sidebar-fg) / .75); }

/* 側欄統計 badge */
/* ★2026-08-04 稽核：blade 0 命中 */
.app-sidebar .menu-badge {
  background: hsl(var(--sidebar-active-bg)) !important;
  color: hsl(var(--sidebar-fg-strong)) !important;
  border-radius: 9999px;
  font-weight: 500;
}
/* 側欄 profile 區：移除原 theme 的山景封面照，改 shadcn 純色 + 細底線 */
.app-sidebar .menu-profile-cover,
.app-sidebar .menu-profile-cover.with-shadow {
  background-image: none !important;
  background-color: hsl(var(--sidebar-bg)) !important;
}
.app-sidebar .menu-profile-cover.with-shadow:before {
  background-image: none !important;
  background: transparent !important;
}
.app-sidebar .menu-profile {
  /* ★已被 §可展開篩選 覆蓋（實測生效值：border-bottom=1px solid rgb(228, 228, 231)） */
  border-bottom: 1px solid hsl(var(--sidebar-border));
}
.app-sidebar .menu-profile-link { color: hsl(var(--sidebar-fg-strong)) !important; }
.app-sidebar .menu-profile-info,
.app-sidebar .menu-profile-info .text-white-50,
/* ★〔.app-sidebar .menu-profile-info〕已被 §可展開篩選 覆蓋；〔.app-sidebar .menu-profile-info .text-white-50／.app-sidebar .menu-profile-info small〕未被覆蓋（實測生效值：color=rgb(24, 24, 27)） */
.app-sidebar .menu-profile-info small { color: hsl(var(--sidebar-fg)) !important; }
.app-sidebar .menu-profile-image { border: 1px solid hsl(var(--sidebar-border)); }
/* profile 區內的前台/登出小連結 */
.app-sidebar .menu-profile a,
.app-sidebar .menu-profile .text-white-50 { color: hsl(var(--sidebar-fg)) !important; }
.app-sidebar .menu-profile a:hover { color: hsl(var(--sidebar-fg-strong)) !important; }
/* 收合鈕 */
.app-sidebar-minify-btn {
  background: hsl(var(--sidebar-accent));
  color: hsl(var(--sidebar-fg)) !important;
  /* ★已被 §S-39 覆蓋（實測生效值：border-radius=8px） */
  border-radius: 9999px;
}
.app-sidebar-minify-btn:hover { background: hsl(var(--sidebar-active-bg)); color: hsl(var(--sidebar-fg-strong)) !important; }

/* 側欄的 data-bs-theme="dark"（Color Admin 遺留，肥肥 _nav.blade.php L2 也有）
   會讓亮色模式下的 Bootstrap 元件仍套暗色變數 → 明確以 token 覆蓋（merp §31 L2049-2055） */
#sidebar[data-bs-theme="dark"] {
  --bs-body-color: hsl(var(--sidebar-fg));
  --bs-body-bg: hsl(var(--sidebar-bg));
  --bs-emphasis-color: hsl(var(--sidebar-fg-strong));
}


/* =============================================================================
 * §16 Login 頁（移植自 merp §16 L591-662 + merp §14 L1199-1245）
 * ★ brand-badge 是「品牌識別」→ 這是全檔少數保留 --primary（金黃）的地方
 * ============================================================================= */
/* ★2026-08-04 稽核：blade 0 命中 */
.login.login-v1 {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
  background: hsl(var(--muted));
}
/* ★2026-08-04 稽核：blade 0 命中 */
.login.login-v1 .login-container {
  position: static;
  width: 100%;
  max-width: 400px;
  margin: 1.5rem;
  background: hsl(var(--card));
  border: 1px solid hsl(var(--border));
  border-radius: var(--radius-xl);
  box-shadow: 0 12px 32px -14px rgb(0 0 0 / 0.18);
  overflow: hidden;
  /* 登入頁上方留白收斂（移除 icon 後） */
  padding: 1.6rem 1.6rem 1.8rem;
}
/* ★2026-08-04 稽核：blade 0 命中 */
.login.login-v1 .login-header,
.login.login-v1 .login-body {
  position: static;
  float: none;
  width: auto;
  background: transparent !important;
}
/* ★2026-08-04 稽核：blade 0 命中 */
.login.login-v1 .login-header .brand {
  text-align: center;
  color: hsl(var(--foreground));
}
/* ★品牌識別：黃底黑字方塊 */
/* ★2026-08-04 稽核：blade 0 命中 */
.login .brand-badge,
.login.login-v1 .brand-badge {
  width: 52px;
  height: 52px;
  margin: 0 auto .8rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: hsl(var(--primary));
  color: hsl(var(--primary-foreground));
  border-radius: var(--radius-xl);
  font-size: 1.3rem;
  box-shadow: 0 4px 10px -3px hsl(var(--primary) / 0.4);
}
.login .brand { text-align: center; margin-bottom: 1.1rem; }
/* ★2026-08-04 稽核：blade 0 命中 */
.login .brand-title {
  font-size: 1.4rem;
  font-weight: 700;
  letter-spacing: -0.02em;
  color: hsl(var(--foreground));
  line-height: 1.2;
  margin-top: .2rem;
}
/* ★2026-08-04 稽核：blade 0 命中 */
.login .brand-sub {
  margin-top: 0.2rem;
  font-size: 0.82rem;
  font-weight: 400;
  color: hsl(var(--muted-foreground));
}
/* ★2026-08-04 稽核：blade 0 命中 */
.login.login-v1 .login-header .icon { display: none; }
.login .login-header { margin: 0; padding: 0; }
/* ★2026-08-04 稽核：blade 0 命中 */
.login .login-body { padding: 0; }
/* ★2026-08-04 稽核：blade 0 命中 */
.login.login-v1 .login-content { width: auto; padding: 0; }
.login .form-label { font-size: .82rem; font-weight: 500; color: hsl(var(--foreground)); margin-bottom: .35rem; }
.login .form-control, .login .form-select { min-height: 40px; padding: .5rem .7rem; }
/* 登入送出鈕：登入＝該頁唯一主要動作，維持 .btn-dark（近黑）。
   若業主要求登入鈕改品牌黃，view 改成 .btn-primary 即可，本檔不需動。 */
.login .btn-dark, .login .btn-primary { min-height: 40px; font-weight: 500; }
.login .form-check-label { font-size: .82rem; color: hsl(var(--muted-foreground)); }
.login .alert { border-radius: var(--radius); font-size: .85rem; }
/* 登入頁輸入框：Color Admin app.min.css 有 5 層 class 深色規則，用同鏈 + !important 蓋成淺色 shadcn */
/* ★2026-08-04 稽核：blade 0 命中 */
.login.login-v1 .login-container .login-body .login-content .form-control,
.login.login-v1 .login-content .form-control,
.login.login-v1 .login-content .form-select {
  background-color: hsl(var(--background)) !important;
  border: 1px solid hsl(var(--input)) !important;
  color: hsl(var(--foreground)) !important;
}
/* ★2026-08-04 稽核：blade 0 命中 */
.login.login-v1 .login-content .form-control::placeholder { color: hsl(var(--hint)) !important; }
/* ★2026-08-04 稽核：blade 0 命中 */
.login.login-v1 .form-floating > label { color: hsl(var(--muted-foreground)); }

/* 全站 autofill：去掉瀏覽器藍底，對齊主題（merp §14 L1218-1228） */
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus,
select:-webkit-autofill,
textarea:-webkit-autofill {
  -webkit-box-shadow: 0 0 0 1000px hsl(var(--background)) inset !important;
  -webkit-text-fill-color: hsl(var(--foreground)) !important;
  caret-color: hsl(var(--foreground));
  transition: background-color 9999s ease-in-out 0s;
}

/* 系統版本資訊等 label-value 列表（merp §14 L1237-1241） */
/* ★2026-08-04 稽核：blade 0 命中 */
.app-content .system-info-item { display: flex; align-items: center; justify-content: space-between; gap: 1rem; padding: .55rem 0; border-bottom: 1px solid hsl(var(--border)); font-size: .875rem; }
/* ★2026-08-04 稽核：blade 0 命中 */
.app-content .system-info-item:last-child { border-bottom: 0; }
/* ★2026-08-04 稽核：blade 0 命中 */
.app-content .system-info-item > span:first-child { color: hsl(var(--muted-foreground)); white-space: nowrap; }
/* ★2026-08-04 稽核：blade 0 命中 */
.app-content .system-info-item strong { font-weight: 500; text-align: end; }


/* =============================================================================
 * §17 Select2（可搜尋下拉，完整 shadcn 化；亮/暗都吃）（移植自 merp §17 L663-736）
 * -----------------------------------------------------------------------------
 * ★★ 肥肥與 merp 的關鍵差異：肥肥同時載入 select2 原廠 css + select2-bootstrap-5-theme，
 *    實際 render 出來的是 `.select2-container--bootstrap-5`，不是 merp 的 `--default`。
 *    因此：
 *      - merp 針對 `.select2-container--default .select2-selection__arrow b` 的
 *        border-triangle 箭頭規則「不會」命中 bootstrap-5 theme（後者箭頭是 SVG 背景圖）
 *        → 照抄安全，且必須維持 --default 前綴，絕不可拿掉
 *        （肥肥 2026-05-20 有過事故：硬套 default theme 的 b 三角形，箭頭多畫一個歪的）
 *      - 顏色/邊框類規則同時加上 --bootstrap-5 前綴，才真的吃得到
 *      - 尺寸不動：肥肥 _link.blade.php 已用 !important 鎖 30px 緊湊高度，本檔不碰
 * ★轉換：已選項（aria-selected）merp 用 primary 反轉 → 肥肥用 --dark
 * ============================================================================= */
/* selection 本體（單選/多選）*/
.select2-container .select2-selection--single,
.select2-container .select2-selection--multiple,
.select2-container--default .select2-selection--single,
.select2-container--default .select2-selection--multiple,
.select2-container--bootstrap-5 .select2-selection--single,
.select2-container--bootstrap-5 .select2-selection--multiple {
  border: 1px solid hsl(var(--input)) !important;
  border-radius: var(--radius) !important;
  background: hsl(var(--background)) !important;
  color: hsl(var(--foreground)) !important;
}
.select2-container--default .select2-selection--single .select2-selection__rendered,
.select2-container--bootstrap-5 .select2-selection--single .select2-selection__rendered {
  color: hsl(var(--foreground)) !important;
}
.select2-container--default .select2-selection--single .select2-selection__placeholder,
.select2-container--bootstrap-5 .select2-selection__placeholder { color: hsl(var(--hint)) !important; }
/* ★只針對 --default theme 的 border-triangle 箭頭；bootstrap-5 theme 用 SVG，別碰 */
.select2-container--default .select2-selection--single .select2-selection__arrow b { border-color: hsl(var(--muted-foreground)) transparent transparent transparent !important; }
.select2-container--default.select2-container--open .select2-selection__arrow b { border-color: transparent transparent hsl(var(--muted-foreground)) transparent !important; }
/* focus / open 態 */
.select2-container--default.select2-container--focus .select2-selection--single,
.select2-container--default.select2-container--focus .select2-selection--multiple,
.select2-container--bootstrap-5.select2-container--focus .select2-selection,
.select2-container--open .select2-selection--single,
.select2-container--open .select2-selection--multiple {
  border-color: hsl(var(--ring)) !important;
  box-shadow: 0 0 0 3px hsl(var(--ring) / .5) !important;   /* §S-73：原 `0 0 0 2px hsl(var(--ring) / .25)`，改官方 3px + /50 */
}
/* dropdown 面板（append 到 body，靠 html.dark 生效）*/
.select2-container--default .select2-dropdown,
.select2-container--bootstrap-5 .select2-dropdown,
.select2-dropdown {
  border: 1px solid hsl(var(--border)) !important;
  border-radius: var(--radius);
  box-shadow: 0 8px 24px -6px rgb(0 0 0 / 0.25);
  background: hsl(var(--popover)) !important;
  color: hsl(var(--popover-foreground)) !important;
}
/* 搜尋框 */
.select2-container--default .select2-search--dropdown .select2-search__field,
.select2-container--bootstrap-5 .select2-search--dropdown .select2-search__field,
.select2-search--dropdown .select2-search__field {
  background: hsl(var(--background)) !important;
  border: 1px solid hsl(var(--input)) !important;
  border-radius: var(--radius-md);
  color: hsl(var(--foreground)) !important;
  outline: none;
}
/* §S-73：原本只有 `border-color`，缺官方的 3px 柔環那一層，補上（原本那半層一字未動）。 */
.select2-container--default .select2-search--dropdown .select2-search__field:focus,
.select2-container--bootstrap-5 .select2-search--dropdown .select2-search__field:focus { border-color: hsl(var(--ring)) !important; box-shadow: 0 0 0 3px hsl(var(--ring) / .5) !important; }
/* 選項 */
.select2-container--default .select2-results__option,
.select2-container--bootstrap-5 .select2-results__option,
.select2-results__option {
  color: hsl(var(--popover-foreground)) !important;
  background: transparent !important;
}
.select2-container--default .select2-results__option--highlighted,
.select2-container--default .select2-results__option--highlighted[aria-selected],
.select2-container--bootstrap-5 .select2-results__option--highlighted,
.select2-results__option--highlighted {
  background: hsl(var(--accent)) !important;
  color: hsl(var(--accent-foreground)) !important;
}
/* 已選項＝深色反轉（hover 淺灰、已選反轉，「選了」一眼可辨）。★merp 此處用 primary，肥肥用 --dark */
.select2-container--default .select2-results__option[aria-selected="true"],
.select2-container--default .select2-results__option[data-selected="true"],
.select2-container--bootstrap-5 .select2-results__option[aria-selected="true"],
.select2-container--bootstrap-5 .select2-results__option--selected {
  background: hsl(var(--dark)) !important;
  color: hsl(var(--dark-foreground)) !important;
}
.select2-results__message { color: hsl(var(--muted-foreground)) !important; }
/* 多選 tag */
.select2-container--default .select2-selection--multiple .select2-selection__choice,
.select2-container--bootstrap-5 .select2-selection--multiple .select2-selection__choice {
  background: hsl(var(--secondary)) !important;
  border: 1px solid hsl(var(--border)) !important;
  border-radius: var(--radius-md);
  color: hsl(var(--secondary-foreground)) !important;
}
.select2-container--default .select2-selection--multiple .select2-selection__choice__remove,
.select2-container--bootstrap-5 .select2-selection--multiple .select2-selection__choice__remove { color: hsl(var(--muted-foreground)) !important; }
.select2-container--default .select2-selection--multiple .select2-search__field,
.select2-container--bootstrap-5 .select2-selection--multiple .select2-search__field { color: hsl(var(--foreground)) !important; }

/* select2 在篩選列內不要撐滿整行（全域 init 是 width:100%，會把每個下拉推成一行）（merp §26 L1693-1697） */
.card-header .select2-container,
.toolbar-row .select2-container { width: auto !important; min-width: 140px; }

/* Select2 sm —— ★尺寸改對齊規格 §0 的 32px（原本移植 merp 時是 31px，
   這條特異度 (0,3,0) 比 §select2 的 (0,2,0) 高，會把高度鎖在 31px，
   造成同一列上 select2 比其他控制項矮 1px（實測訂單頁篩選列）。 */
.form-select-sm + .select2-container .select2-selection--single {
  height: 32px !important;
  min-height: 32px !important;
  padding: 0 28px 0 10px !important;
  font-size: 13px !important;
}
.form-select-sm + .select2-container .select2-selection--single .select2-selection__rendered { line-height: 30px; font-size: 13px; }
.form-select-sm + .select2-container .select2-selection--single .select2-selection__arrow { display: none !important; }
.select2-container .select2-results__option { font-size: .75rem; }
.select2-container .select2-search--dropdown .select2-search__field { font-size: .75rem; }


/* =============================================================================
 * §18 Daterangepicker（完整 shadcn 化；原本只調局部 → 暗色下白底白字看不見）
 *     （移植自 merp §18 L737-828）
 * ★轉換：選中日 / 快捷區間 active，merp 用 primary → 肥肥用 --dark
 *   （日曆一片黃太刺眼，且黃底上的灰字對比不足）
 * ============================================================================= */
.daterangepicker {
  background: hsl(var(--popover)) !important;
  color: hsl(var(--popover-foreground)) !important;
  border: 1px solid hsl(var(--border)) !important;
  border-radius: var(--radius-md);   /* shadcn PopoverContent = rounded-md */
  box-shadow: 0 8px 24px -6px rgb(0 0 0 / 0.25);
  font-family: inherit;
  font-size: 13px;
}
/* 小箭頭 */
.daterangepicker:before { border-bottom-color: hsl(var(--border)) !important; }
.daterangepicker:after { border-bottom-color: hsl(var(--popover)) !important; }
.daterangepicker.drop-up:before { border-top-color: hsl(var(--border)) !important; }
.daterangepicker.drop-up:after { border-top-color: hsl(var(--popover)) !important; }

/* 月曆表頭（月份標題 / 星期） */
.daterangepicker .calendar-table {
  background: hsl(var(--popover)) !important;
  border: 0 !important;
}
.daterangepicker th,
.daterangepicker .month {
  color: hsl(var(--foreground)) !important;
  font-weight: 500;
}
.daterangepicker th.available:hover { background: hsl(var(--accent)) !important; }
/* 上/下月箭頭 */
.daterangepicker .prev span,
.daterangepicker .next span { border-color: hsl(var(--foreground)) !important; }

/* 日期格 */
.daterangepicker td {
  background: transparent !important;
  color: hsl(var(--foreground)) !important;
  border-radius: var(--radius-md);
}
.daterangepicker td.available:hover {
  background: hsl(var(--accent)) !important;
  color: hsl(var(--accent-foreground)) !important;
}
.daterangepicker td.off,
.daterangepicker td.off.in-range,
.daterangepicker td.off.start-date,
.daterangepicker td.off.end-date {
  background: transparent !important;
  color: hsl(var(--muted-foreground) / .5) !important;
}
.daterangepicker td.active,
.daterangepicker td.active:hover,
.daterangepicker td.start-date,
.daterangepicker td.end-date {
  background: hsl(var(--dark)) !important;
  color: hsl(var(--dark-foreground)) !important;
}
.daterangepicker td.in-range {
  background: hsl(var(--accent)) !important;
  color: hsl(var(--accent-foreground)) !important;
}
.daterangepicker td.today { font-weight: 700; text-decoration: underline; text-underline-offset: 3px; }
.daterangepicker td.disabled { color: hsl(var(--muted-foreground) / .4) !important; text-decoration: line-through; }

/* 年/月下拉（showDropdowns） */
.daterangepicker select {
  background: hsl(var(--background)) !important;
  color: hsl(var(--foreground)) !important;
  border: 1px solid hsl(var(--input)) !important;
  border-radius: var(--radius-md);
  padding: 2px 4px;
}
.daterangepicker select option { background: hsl(var(--popover)); color: hsl(var(--popover-foreground)); }

/* 時間選擇 */
.daterangepicker .calendar-time select { margin: 0 2px; }

/* 快捷區間清單 */
.daterangepicker .ranges { background: hsl(var(--popover)) !important; }
.daterangepicker .ranges li {
  color: hsl(var(--foreground)) !important;
  border-radius: var(--radius-sm);
}
.daterangepicker .ranges li:hover { background: hsl(var(--accent)) !important; color: hsl(var(--accent-foreground)) !important; }
.daterangepicker .ranges li.active { background: hsl(var(--dark)) !important; color: hsl(var(--dark-foreground)) !important; }

/* 底部按鈕列 */
.daterangepicker .drp-buttons {
  background: hsl(var(--popover)) !important;
  border-top: 1px solid hsl(var(--border)) !important;
}
.daterangepicker .drp-selected { color: hsl(var(--muted-foreground)) !important; }
.daterangepicker .drp-buttons .btn { font-weight: 500; }


/* =============================================================================
 * §20 雜項對齊（移植自 merp §20 L835-846）
 * ★不移植 merp 的 ::selection：目標檔 §2 已定義（--primary / .35），SSOT 不重複宣告
 * ★轉換：.text-primary 若吃 --primary（金黃）在白底上完全讀不到 → 改 --foreground
 * ============================================================================= */
/* Bootstrap 的 .rounded 本來就等於 --bs-border-radius(=--radius)，
   原本硬加 +2px 讓它跟這個 token 對不起來 —— 收回 lg 這一階。 */
.rounded, .rounded-3 { border-radius: var(--radius-lg) !important; }
.rounded-pill { border-radius: 9999px !important; }
.border { border-color: hsl(var(--border)) !important; }
.shadow, .shadow-sm { box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.08), 0 1px 2px -1px rgb(0 0 0 / 0.06) !important; }
.bg-light { background-color: hsl(var(--muted)) !important; }
/* ★〔.text-primary〕已被 §28 覆蓋；〔a.text-primary〕未被覆蓋（實測生效值：color=rgb(9, 9, 11)） */
.text-primary, a.text-primary { color: hsl(var(--foreground)) !important; }
/* ★文字用 --destructive-fg，不是 --destructive（後者是底色 token，白底上只有 3.76:1）。
   旁邊的 .text-success 早就是走 --success-fg，這裡本來就該對齊。 */
/* ★已被 §28 覆蓋（實測生效值：color=rgb(186, 28, 28)） */
.text-danger { color: hsl(var(--destructive-fg)) !important; }
/* ★已被 §28 覆蓋（實測生效值：color=rgb(25, 118, 59)） */
.text-success { color: hsl(var(--success-fg)) !important; }


/* =============================================================================
 * §21 Color Admin 顏色工具類 → shadcn 收斂（移植自 merp §21 L847-880）
 * Color Admin 的 bg-blue/green/red… 是鮮色工具類（常被 JS 動態套在按鈕/badge，
 * 如列表編輯鈕 bg-blue），這裡整套映射到 token，確保任何頁面、含 JS 動態產生的
 * 元件都看不到 Color Admin 原色。
 * ★轉換：merp 把藍/青/靛/紫映到 primary（近黑）→ 肥肥映到 --dark
 * ============================================================================= */
/* 按鈕：藍/青/靛/紫/aqua → 近黑主要動作鈕（編輯/檢視等） */
.btn.bg-blue, .btn.bg-aqua, .btn.bg-cyan, .btn.bg-teal,
.btn.bg-indigo, .btn.bg-purple, .btn.bg-info, .btn.bg-blue-600, .btn.bg-blue-700 {
  background-color: hsl(var(--dark)) !important;
  border-color: hsl(var(--dark)) !important;
  color: hsl(var(--dark-foreground)) !important;
}
.btn.bg-green, .btn.bg-lime, .btn.bg-success { background-color: hsl(var(--success)) !important; border-color: hsl(var(--success)) !important; color: hsl(var(--success-foreground)) !important; }
.btn.bg-red, .btn.bg-pink, .btn.bg-danger { background-color: hsl(var(--destructive)) !important; border-color: hsl(var(--destructive)) !important; color: hsl(var(--destructive-foreground)) !important; }
.btn.bg-orange, .btn.bg-yellow, .btn.bg-warning { background-color: hsl(var(--warning)) !important; border-color: hsl(var(--warning)) !important; color: hsl(var(--warning-foreground)) !important; }
.btn.bg-grey, .btn.bg-gray, .btn.bg-silver, .btn.bg-default, .btn.bg-secondary {
  background-color: hsl(var(--secondary)) !important; border-color: hsl(var(--border)) !important; color: hsl(var(--secondary-foreground)) !important;
}
.btn.bg-dark, .btn.bg-black { background-color: hsl(var(--dark)) !important; border-color: hsl(var(--dark)) !important; color: hsl(var(--dark-foreground)) !important; }
/* badge 上的 Color Admin 鮮色 */
.badge.bg-blue, .badge.bg-aqua, .badge.bg-cyan, .badge.bg-teal, .badge.bg-indigo, .badge.bg-purple, .badge.bg-info { background: hsl(var(--dark)) !important; color: hsl(var(--dark-foreground)) !important; }
/* ★2026-08-04 稽核：blade 0 命中 */
.badge.bg-green, .badge.bg-lime { background: hsl(var(--success)) !important; color: hsl(var(--success-foreground)) !important; }
/* ★2026-08-04 稽核：blade 0 命中 */
.badge.bg-orange, .badge.bg-yellow { background: hsl(var(--warning)) !important; color: hsl(var(--warning-foreground)) !important; }
/* ★2026-08-04 稽核：blade 0 命中 */
.badge.bg-red, .badge.bg-pink { background: hsl(var(--destructive)) !important; color: hsl(var(--destructive-foreground)) !important; }
/* ★安全網：.btn.bg-default 早就有收（見上方 .btn.bg-grey…那條），但 .badge.bg-default 一直漏掉
   → 底色不會被套（透明），偏偏 view 常寫成 `badge bg-default text-white`，而 .text-white 被
   收斂成近白 → 亮色模式整顆 badge 隱形（merp tasks/index、dashboard/index「已略過」實案）。
   本選擇器 2 個 class，特異度高於 .text-white(1 個)，穩定勝出、不依賴宣告順序。 */
.badge.bg-grey, .badge.bg-gray, .badge.bg-silver, .badge.bg-default { background: hsl(var(--secondary)) !important; color: hsl(var(--secondary-foreground)) !important; }
/* ★2026-08-04 稽核：blade 0 命中 */
.badge.bg-grey, .badge.bg-gray, .badge.bg-silver { background: hsl(var(--muted)) !important; color: hsl(var(--muted-foreground)) !important; }
/* 文字語意色：保留語意但用 token 色值。
   ★藍/靛/紫等資訊色 merp 映到 primary → 肥肥若映到金黃，白底上完全讀不到 → 用 --foreground */
/* ★2026-08-04 稽核：blade 0 命中 */
.text-blue, .text-indigo, .text-purple, .text-teal, .text-cyan, .text-aqua, .text-info { color: hsl(var(--foreground)) !important; }
.text-green, .text-lime { color: hsl(var(--success-fg)) !important; }
/* ★2026-08-04 稽核：blade 0 命中 */
.text-red, .text-pink { color: hsl(var(--destructive-fg)) !important; }   /* 同 .text-danger：文字走 -fg */
/* ★〔.text-warning〕已被 §28 覆蓋；〔.text-orange／.text-yellow〕未被覆蓋（實測生效值：color=rgb(152, 89, 16)） */
.text-orange, .text-yellow, .text-warning { color: hsl(var(--warning-fg)) !important; }
/* Color Admin 灰階文字（merp §2 L99） */
.text-gray-500, .text-gray-600, .text-gray-700 { color: hsl(var(--muted-foreground)) !important; }


/* =============================================================================
 * §22 ★★★ 外殼版面（移植自 merp §22 L1349-1522）
 * -----------------------------------------------------------------------------
 * 取代 Color Admin app.min.css 的 layout。拔掉 Color Admin 後，
 * 固定側欄／頂 bar／內容位移全靠這段。
 * HTML 結構沿用既有 #header.app-header / #sidebar.app-sidebar / #content.app-content
 * （肥肥 _nav.blade.php 600+ 行含權限判斷，不重寫 view — shadcn 化全靠本檔驅動，SSOT）
 *
 * ★★ 上線前提醒（TODO 待確認）：
 *   肥肥 _link.blade.php 目前「仍載入」assets/css/default/app.min.css，
 *   _script.blade.php 仍載入 assets/js/app.min.js。本節是為「拔掉 app.min.css 之後」
 *   寫的；在兩者共存期間，本檔載在最後可以蓋過大部分 layout，但：
 *     1) _link.blade.php 的 inline `<style>` 內 `.app-content{padding-*:20px !important}`
 *        會蓋過本節的 padding → 拔 Color Admin 時要一併移除那段 inline style
 *     2) 手機抽屜：merp 用自家 JS 加 body.sidebar-open；肥肥的 app.min.js 是在
 *        #app 加 .app-sidebar-mobile-toggled → 本節兩種 hook 都掛，任一生效都能開
 * ========================================================================== */

:root {
    --fb-sidebar-w: 220px;
    --fb-header-h: 50px;
}

body { background: hsl(var(--background)); }

/* 頂 bar：固定 */
#header.app-header {
    position: fixed;
    top: 0; left: 0; right: 0;
    height: var(--fb-header-h);
    z-index: 1020;
    display: flex;
    align-items: center;
    /* ★已被 §25 覆蓋（實測生效值：background=hsl(var(--card)) !important） */
    background: hsl(var(--card));
    /* ★已被 §25 覆蓋（實測生效值：border-bottom=1px solid rgb(228, 228, 231)） */
    border-bottom: 1px solid hsl(var(--border));
    box-shadow: none;
    padding: 0;
}
#header .navbar-header {
    width: var(--fb-sidebar-w);
    display: flex;
    align-items: center;
    padding: 0 1rem;
    flex: 0 0 var(--fb-sidebar-w);
}
#header .navbar-brand {
    display: flex;
    align-items: center;
    gap: .5rem;
    font-weight: 700;
    color: hsl(var(--foreground));
    text-decoration: none;
    padding: 0;
}
#header .navbar-nav {
    display: flex;
    align-items: center;
    flex-direction: row;
    margin-left: auto;
    padding-right: 1rem;
    gap: .25rem;
}
#header .navbar-nav .navbar-item { list-style: none; display: flex; align-items: center; }
#header .navbar-nav .navbar-link,
#header .navbar-nav .nav-link {
    color: hsl(var(--muted-foreground));
    padding: .5rem .75rem;
    border-radius: var(--radius);
    display: flex;
    align-items: center;
}
#header .navbar-nav .navbar-link:hover,
#header .navbar-nav .nav-link:hover { background: hsl(var(--accent)); color: hsl(var(--accent-foreground)); }
#header .navbar-nav .dropdown-menu { border: 1px solid hsl(var(--border)); box-shadow: 0 8px 24px rgb(0 0 0 / .08); }
#header .navbar-mobile-toggler { display: none; background: none; border: 0; color: hsl(var(--foreground)); }

/* 側欄：固定，頂 bar 下方 */
#sidebar.app-sidebar {
    position: fixed;
    top: var(--fb-header-h);
    bottom: 0;
    left: 0;
    width: var(--fb-sidebar-w);
    padding-top: 0;
    z-index: 1010;
    overflow-y: auto;
    overscroll-behavior: contain;
}
#sidebar .app-sidebar-content { height: auto; }
/* SidebarGroup = p-2（原本上下 12px、左右 8px，上下多出來的 4px 沒有依據） */
#sidebar .menu { padding: .5rem; }
/* ★2026-08-04 稽核：blade 0 命中 */
#sidebar .menu-header {
    font-size: 11px;
    letter-spacing: .04em;
    padding: .5rem .75rem;
}
#sidebar .menu-item { position: relative; }
#sidebar .menu-link {
    display: flex;
    align-items: center;
    /* ★★ 2026-07-29 對齊 shadcn SidebarMenuButton 實際規格（業主：「選單呈現方式不好看，用 shadcn 設計」）
       原本這裡的值是隨手訂的，而下面 §側欄 那條 .app-sidebar .menu-link 雖然寫了正確規格，
       卻因為 ID 特異性（1,1,0 > 0,2,0）被這條整個壓掉 —— 等於規格從未生效。
       SidebarMenuButton = h-8 gap-2 p-2 rounded-md text-sm；
       SidebarMenu = flex flex-col gap-1（項目間 4px，原本只有 1px 擠成一片）。 */
    height: 32px;               /* h-8 —— 固定高，不讓 line-height 把它撐成 36px */
    gap: .5rem;                 /* gap-2 = 8px（原 10px） */
    padding: .5rem;             /* p-2 = 8px */
    border-radius: var(--radius-md);   /* rounded-md：10 - 2 = 8px（原本吃整顆 10px） */
    font-size: 14px;            /* text-sm */
    text-decoration: none;
    margin-bottom: .25rem;      /* SidebarMenu gap-1 = 4px（原 1px） */
}
/* ★已被 L6286 覆蓋（實測生效值：width=16px；flex=0 0 16px） */
#sidebar .menu-icon { width: 18px; text-align: center; flex: 0 0 18px; }
#sidebar .menu-text { flex: 1; }
#sidebar .menu-caret {
    margin-left: auto;
    transition: transform .15s ease;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 16px;
    height: 16px;
}
/* 展開箭頭改用「純 CSS 邊框 chevron」，不依賴 Font Awesome 字型
   （merp 遷移 lucide 後 FA 已移除，原 \f054 字符變 tofu 隱形；
     肥肥雖仍有 Color Admin 的 FA，純 CSS chevron 也一樣正確且更穩） */
#sidebar .menu-caret::before {
    content: "";
    display: inline-block;
    width: 6px;
    height: 6px;
    border-right: 1.5px solid currentColor;
    border-bottom: 1.5px solid currentColor;
    transform: rotate(-45deg);
    margin-left: -2px;
}
/* ★已被 §側欄二修：靠左 覆蓋（實測生效值：transform=matrix(0, 1, -1, 0, 0, -8)） */
#sidebar .menu-item.expand > .menu-link .menu-caret { transform: rotate(90deg); }

/* 子選單：預設收合，.expand 展開（取代 Color Admin app.min.js 的行為）
   ※ Color Admin app.min.js 仍在時，它用 jQuery slideDown 寫 inline display
      → inline 勝出，動畫照常，兩者不衝突 */
/* ★已被 L6556 覆蓋（實測生效值：padding-left=10px） */
#sidebar .menu-submenu { display: none; padding-left: 1.75rem; }
#sidebar .menu-item.expand > .menu-submenu { display: block; }
#sidebar .menu-submenu .menu-link {
    font-size: 13px;
    padding: .375rem .75rem;
    /* ★★ 2026-07-30 稽核：alpha 由 .75 → .9（與 §側欄 profile 的 h6 客戶代號同一筆帳）。
       .75 疊在 --sidebar-bg 上實測 **亮色 3.97 / 暗色 4.44，兩模式都沒過 AA 4.5**。
       這是側欄第二層的導覽項（「出貨人效統計」「出貨紀錄」「職位頁面權限」…），
       是使用者要點的功能入口，不是裝飾字，而且每頁展開選單就會出現。
       ★動的是本主題自己的 alpha 修飾，不是與 2026-fatboy 共用的 --sidebar-fg token
         → 兩專案的配色基準不會漂移。
       層次仍在：第一層 14px 是滿版 --sidebar-fg，這層 13px 且加 .9 alpha，
       作用中項目另有 --sidebar-fg-strong + 底色，三階分得開。 */
    color: hsl(var(--sidebar-fg) / .9);
}
/* ★修 merp bug：merp 這裡寫 hsl(var(--sidebar-active-fg))，但該 token 從未定義過
   （亮暗兩組都沒有）→ 整條 color 宣告無效被丟棄。肥肥改用實際存在的 --sidebar-fg-strong。 */
#sidebar .menu-submenu .menu-item.active > .menu-link {
    /* ★已被 §側欄：對齊shadcn 覆蓋（實測生效值：color=rgb(24, 24, 27)） */
    color: hsl(var(--sidebar-fg-strong));
    /* ★已被 §側欄：對齊shadcn 覆蓋（實測生效值：background=hsl(var(--sidebar-accent))） */
    background: hsl(var(--sidebar-active-bg));
}

/* 內容區：讓開側欄
   ★頂 bar 的讓位由 `.app-header-fixed { padding-top: 50px }` 提供（見補位段），
     這裡再加一次 var(--fb-header-h) 就是重複計算 → 每一頁的標題都被往下推 70px。
     實測：頂 bar 底 y=50、內容卻從 y=120 才開始。規格 §0 的區塊間距是 16px。 */
#content.app-content {
    margin-left: var(--fb-sidebar-w);
    padding: 1rem 1.5rem 1.5rem;
    min-height: 100vh;
}

/* RWD：側欄收成抽屜 */
@media (max-width: 991.98px) {
    #header .navbar-header { width: auto; flex: 0 0 auto; }
    /* 漢堡鈕三條槓：.icon-bar 樣式原本在 Color Admin app.min.css，拔掉後沒人補
       → span 全塌成 0 高、按鈕剩 12px 寬，手機上看不到也點不到（= 無法開選單） */
    #header .navbar-mobile-toggler {
        /* 漢堡鈕排到 navbar-header 最左（brand 前）：手機慣例＝選單開關在最左邊。
           order:-1 讓它在 flex 內排第一，margin 收左移右。桌機此鈕 display:none 不受影響。 */
        display: inline-flex; flex-direction: column; justify-content: center; gap: 4px;
        order: -1; margin-left: 0; margin-right: .35rem; padding: 10px 12px 10px 0; cursor: pointer;
    }
    #header .navbar-mobile-toggler .icon-bar {
        display: block; width: 20px; height: 2px; border-radius: 1px;
        background: hsl(var(--foreground));
    }
    /* ★★ shadcn 明確把手機抽屜跟桌面側欄分開訂寬：
         SIDEBAR_WIDTH        = 16rem (256px)  ← 桌面
         SIDEBAR_WIDTH_MOBILE = 18rem (288px)  ← 手機 Sheet
       因為手機是覆蓋式面板、沒有「擠壓內容」的成本，寬一點才好按。
       肥肥桌面沿用 Color Admin 的 220px（比 shadcn 還窄），
       在 430pt 的手機上只佔半個螢幕，文字被壓在左半邊 → 業主說的「不好看」。
       改寬只影響覆蓋層，內容區在手機已 margin-left:0，不會有連鎖版面變動。
       關閉態是 translateX(-100%)，跟著寬度自動走，不必再各處改 -220px。 */
    :root { --fb-sidebar-w: 288px; }
    #sidebar.app-sidebar {
        /* ★★ 2026-07-29 修（業主：「選單左上角斷一半怪怪的」）：
           桌機版側欄從 header 下方開始（top: 50px）是對的 —— 頂 bar 橫跨整個寬度。
           但手機版的抽屜是**覆蓋式面板**，從 50px 開始會在左上角露出一截 header，
           看起來就像面板被切掉一角。
           原廠 Color Admin 的手機規則是
             @media(max-width:767.98px){ .app-sidebar{ padding-top:0 } }
           搭配 base 的 top:0 → **抽屜蓋滿全高**（已從 747fe38c9^ 取證）。
           shadcn 的 Sheet 也是 inset-y-0 全高。兩邊一致，改回全高。
           關閉方式不受影響：點遮罩即可（漢堡鈕被蓋住是 Sheet 的正常行為）。 */
        top: 0;
        transform: translateX(-100%);
        transition: transform .2s ease;
        box-shadow: 0 0 24px rgb(0 0 0 / .18);
    }
    /* 兩種 hook 都吃：body.sidebar-open（merp 自家 JS）／
       .app.app-sidebar-mobile-toggled（Color Admin app.min.js，肥肥目前用這個） */
    body.sidebar-open #sidebar.app-sidebar,
    /* ★★ 2026-07-29 修 bug（業主回報手機版「選單叫不出來」）：
       原本只掛兩種 hook —— body.sidebar-open（merp 自家 JS）與
       .app.app-sidebar-mobile-toggled（Color Admin app.min.js）。
       但 app.min.js 已被 includes/_shell.blade.php 取代，而 _shell §3 寫的是
           document.body.classList.toggle('app-sidebar-mobile-toggled')
       —— class 名跟 Color Admin 一樣，但掛在 body 不是 .app，
       兩個既有 hook 一個都對不上 → 側欄 transform 永遠停在 -220px。
       ★遮罩是另一組規則、掛在 body 上，所以會出現「頁面變暗但選單沒出來」
         的詭異狀態（業主截圖正是如此）。
       補上 _shell 實際使用的那組，三種 hook 並存。 */
    body.app-sidebar-mobile-toggled #sidebar.app-sidebar,
    .app.app-sidebar-mobile-toggled #sidebar.app-sidebar { transform: translateX(0); }
    #content.app-content { margin-left: 0; padding-left: 1rem; padding-right: 1rem; }
      /* ★肥肥走 _nav 的實體遮罩元素（它同時負責變暗與「點一下關閉」），
         這裡不再疊 ::after —— 會變成雙重變暗，而且 ::after 攔不到點擊。
         下面 body.sidebar-open::after 留給 merp 那套。 */
    body.sidebar-open::after,
    .app.app-sidebar-mobile-toggled::after {
        content: "";
        position: fixed; inset: var(--fb-header-h) 0 0 0;
        background: rgb(0 0 0 / .35);
        z-index: 1005;
    }
}

/* 亮/暗切換鈕的圖示互斥顯示 */
.d-block-dark { display: none !important; }
html.dark .d-none-dark { display: none !important; }
html.dark .d-block-dark { display: inline-block !important; }

/* ★品牌識別：Pace 頁面載入進度條維持品牌金黃（--bs-app-theme 已在 §1 remap，
   這裡再明確寫一次，蓋掉 _link.blade.php 的 inline `var(--bs-yellow)`）。
   TODO 待確認：拔掉 _link.blade.php inline style 後這條就是唯一來源。 */
.pace .pace-progress { background: hsl(var(--primary)) !important; }


/* =============================================================================
 * §23 徹底拔除 Color Admin 殘留（移植自 merp §23 L1523-1599）
 * 原本散在 _head / _link inline style 的寫死顏色 hack，全部改用 token
 * → 亮/暗自動翻轉，不再有 #fff !important
 *
 * ★★ TODO 待處理（給主 session）：肥肥 _link.blade.php 的 inline `<style>` 內仍有
 *    `.card-header{background:#fff!important}`、`.card-footer{background:#fff!important}`、
 *    `.table th,.table-panel th{background:#fff!important}`、`.nav-tabs .nav-link{color:#6c757d}`
 *    等寫死色，本節就是它們的 token 版取代品 → 上線時應把那段 inline style 刪掉。
 * ========================================================================== */

/* 卡片頭尾、表頭：原本寫死 background:#fff !important（暗色會白底白字看不見） */
.card-header,
.card-footer {
    background: hsl(var(--card)) !important;
    color: hsl(var(--card-foreground));
    border-color: hsl(var(--border));
}
/* ★★§S-74 收斂（2026-08-07，本條位於 §23）：本條原本的選擇器清單是
     `.table th, .table-panel th, .table-panel thead th`
   —— 其中 `.table th` 與 `.table-panel thead th` 都會打到 <thead> 的 th，
   跟 §7 的 `.table > thead th` 管同一件事（兩邊都帶 !important，只靠特異度分勝負，
   實際上本條 (0,1,1) 一直輸給 §7 的 (0,1,2)，等於寫了不生效的宣告）。
   收斂後本條只管 **非表頭** 的 th（tbody / tfoot 裡當列標題用的 th），
   表頭一律由 §7 `.table > thead th` 一處決定。原值記在此存查：
     `.table th,`
     `.table-panel th,`
     `.table-panel thead th {`
     `    background: hsl(var(--card)) !important;`
     `    color: hsl(var(--muted-foreground)) !important;`
     `    border-bottom: 1px solid hsl(var(--border));`
     `    font-weight: 500;`
     `}`
   ★`border-bottom: 1px solid hsl(var(--border));` 這一項對表頭仍然需要，
     已原值搬進 §7 `.table > thead th` 那一條（是搬家，不是刪掉）。
   ★誰還吃得到本條：實測 resources/views 底下只有兩支 blade 在 tbody 裡放 th
     （Center/chartPackage/index、Order/_partials/operation_log_timeline），
     兩支都是 `class="table table-panel …"`。
   ★`.table-panel …` 兩行保留在清單裡（不刪選擇器）：實測 189 處 `table-panel` 有 188 處
     寫成 `class="table table-panel …"`，唯一例外是
     PackagingMachine/Order/pickingPrinter/picking_a4.blade.php:213 的 A4 列印版型，
     而那支是自帶 <style> 的獨立 HTML、根本沒有載入本檔 →
     對本檔而言 `.table-panel tbody th` 等同 `.table tbody th`，留著只是維持原清單的形狀。
     ⇒ 順帶回答工單的第 4 題：`.table-panel` **不是死碼**，60 支以上 blade 在用；
       死掉的只是它「管表頭」的那一半（永遠輸給 §7），那一半這次收掉了。
   ★!important **保留**：DataTables 有一條
     `table.table.dataTable > :not(caption) > * > *`(0,2,2, datatables.css:355)
     `{ background-color: var(--bs-table-bg) }`，特異度比本條 (0,1,2) 高，
     而 Center/chartPackage 正好是 DataTables 頁 → 拿掉 !important，那頁的列標題 th
     底色會變成 --bs-table-bg（本檔 §7b 設為 transparent），灰底整片消失。

   ★★★2026-08-08 更新：上面那句「`.table-panel` 不是死碼，60 支以上 blade 在用」**已經不成立了**
     —— 業主指示「套用 shadcn、移除 color-admin」，已把 markup 裡的 `table-panel`
     全數移除（103 支 blade、188 處，+153/-153，每行差異只有這個 token）。
     現在 `resources/views` 底下活標記的 `table-panel` 只剩 1 處：
     `PackagingMachine/Order/pickingPrinter/picking_a4.blade.php`，而那支是自帶 <style>、
     不載入本檔的獨立列印 HTML，**它的樣式由它自己那份 CSS 給**，與本條無關。
     ⇒ 本條的 `.table-panel …` 兩行至此才真的成為死選擇器，故移除。原清單記此存查：
       `.table tbody th,`
       `.table tfoot th,`
       `.table-panel tbody th,`
       `.table-panel tfoot th {`
     ★移除前的驗證（不是靜態推論）：在瀏覽器對真實表格加上／拿掉 `table-panel` 比對
       computed style —— 4 頁（new-orders／center/chart 3 張表含 tbody th／inventory/product
       亮＋暗）約 150 個屬性，**差異 0**。因為每一條含 `.table-panel` 的規則，
       同一條規則裡本來就列了 `.table` 等價選擇器，而 markup 中兩者恆同時出現。 */
.table tbody th,
.table tfoot th {
    background: hsl(var(--card)) !important;
    color: hsl(var(--muted-foreground)) !important;
    border-bottom: 1px solid hsl(var(--border));
    font-weight: 500;
}

/* 表格：shadcn（去外框、橫線分隔、hover accent） */
.table { color: hsl(var(--foreground)); border-color: hsl(var(--border)); --bs-table-bg: transparent; }
.table > :not(caption) > * > * {
    background-color: transparent;
    border-bottom-color: hsl(var(--border));
    box-shadow: none;
    color: hsl(var(--foreground));
}
.table-hover > tbody > tr:hover > * { background-color: hsl(var(--accent)) !important; color: hsl(var(--accent-foreground)); }

/* 卡片：暗色下要看得出邊界 */
.card {
    background: hsl(var(--card));
    color: hsl(var(--card-foreground));
    border: 1px solid hsl(var(--border));
}
.card.border-0 { border: 1px solid hsl(var(--border)) !important; }

/* page-header（原 inline style，保留版型、去寫死色） */
.page-header {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-bottom: 16px;
    border-bottom: none;
    padding-bottom: 0;
    color: hsl(var(--foreground));
}
.page-header h4 { margin: 0; font-weight: 600; }

/* ★按鈕內圖示一律繼承按鈕前景色 —— 殺掉 view 裡寫死的 text-white
   （肥肥 .btn-primary 是黃底黑字，白 icon 會直接隱形；.btn-dark 在暗色是淺底，
     白 icon 一樣消失。uiux.md 的 <i class="fas fa-pen text-white"> 就是這個雷） */
.btn i,
.btn .fa,
.btn .fas,
.btn .far,
.btn .fab,
.btn span { color: inherit !important; }

/* 密碼欄 / 複製鈕（原 inline style） */
/* ★2026-08-04 稽核：blade 0 命中 */
.password-field { font-family: monospace; letter-spacing: 1px; }
/* ★2026-08-04 稽核：blade 0 命中 */
.btn-copy { cursor: pointer; }
/* ★2026-08-04 稽核：blade 0 命中 */
.btn-copy:hover { color: hsl(var(--foreground)); }


/* =============================================================================
 * §24 Badge 系統：Color Admin 彩色 → shadcn（移植自 merp §24 L1600-1639）
 * shadcn 單色為主，語意靠深淺不靠彩度
 * ★轉換：merp 的 bg-success/bg-primary/bg-dark → primary（近黑）；肥肥 → --dark
 * ★轉換：進度條 merp 用 primary → 肥肥用 --dark（品牌黃只留給 pace 載入條）
 * ============================================================================= */
.badge {
    font-weight: 500;
    /* ★★★ 2026-08-09 訂正：本行原為 `border-radius: var(--radius-md);`（＝8px 方角），
       現改為 9999px（藥丸）。原值保留在本註解中供追溯，不是刪除。
       ★依據＝shadcn-ui/ui `apps/v4/registry/new-york-v4/ui/badge.tsx` 的 badgeVariants base class：
         "inline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-hidden
          rounded-full border border-transparent px-2 py-0.5 text-xs font-medium …"
       ⇒ 官方 base 是 **rounded-full**（CSS 9999px），不是 rounded-md。
       ★為什麼改基底而不是在呼叫端補 .rounded-pill：官方把 rounded-full 內建在 Badge 的 base，
         每顆 badge 天生藥丸、不依賴呼叫端加 class。改這一處＝全站 badge 到位，
         且日後新增的 badge 自動正確；散到呼叫端等於把規格複製 N 份（違反 SSOT）。
       ★本檔無 --radius-full token（全檔 0 命中），故直接寫 9999px，與 §20 .rounded-pill、
         .app-sidebar .menu-badge 等既有寫法一致。
       ★不動高度：官方 Badge 沒有 size variant，20px（本檔 font-size:12px + padding:.25em .6em）是對的。
       ★L1028 §8 那條 `.badge{border-radius:9999px}` 一直被本規則蓋掉（同分、本規則在後）；
         本次改完兩條同值，L1028 的「不生效」註記仍為事實，但已無視覺分岔。 */
    border-radius: 9999px;
    /* ★★ 2026-07-28 量測補：font-size 一定要寫死，不能讓 Bootstrap 的 .75em 生效。
       BS 預設 .badge{font-size:.75em}，而本檔 §表格 把 td 設成 13px，
       .75em × 13px = 9.75px —— 實測全站 508 顆 badge 都在 9.8px，
       低於 shadcn 最小階 text-xs(12px)，在 1440 桌機上已經是勉強能讀的程度。
       em 會隨容器疊乘，同一顆 badge 在 body(14px) 下是 10.5px、在 td 下是 9.8px，
       同一個元件兩種大小本身就違反 SSOT。改成固定 12px = shadcn Badge 的 text-xs。
       padding 維持 em（跟著 12px 走 = 3px/7.2px，仍是 shadcn 的 px-2 py-0.5 量級）。 */
    font-size: 12px;
    padding: .25em .6em;
    border: 1px solid transparent;
}
/* 主要/啟用狀態 = shadcn default（近黑／暗色反轉為白） */
.badge.bg-success,
.badge.bg-primary,
.badge.bg-dark {
    background: hsl(var(--dark)) !important;
    color: hsl(var(--dark-foreground)) !important;
}
/* 次要/停用/中性 = shadcn secondary */
.badge.bg-secondary,
.badge.bg-info,
.badge.bg-light,
.badge.bg-warning {
    /* ★已被 background→L4574(hsl(var(--secondary)) !important)、background→L4562(hsl(var(--warning-bg)) !important) 覆蓋 */
    background: hsl(var(--secondary)) !important;
    /* ★已被 color→L4574(rgb(9, 9, 11))、color→L4562(rgb(152, 89, 16)) 覆蓋 */
    color: hsl(var(--secondary-foreground)) !important;
    border-color: hsl(var(--border));
}
/* 只有真正的錯誤保留紅色 */
.badge.bg-danger {
    /* ★已被 §補位6 覆蓋（實測生效值：background=hsl(var(--destructive)) !important） */
    background: hsl(var(--destructive)) !important;
    /* ★已被 §補位6 覆蓋（實測生效值：color=rgb(255, 255, 255)） */
    color: hsl(var(--destructive-foreground)) !important;
}
.badge.bg-light.text-dark { color: hsl(var(--secondary-foreground)) !important; }

/* 進度條：軌道 muted、填充 --dark；語意色僅保留 danger/warning 的深淺差異 */
.progress { background: hsl(var(--muted)); }
.progress-bar { background-color: hsl(var(--dark)); }
.progress-bar.bg-success { background-color: hsl(var(--dark)) !important; }
.progress-bar.bg-warning { background-color: hsl(var(--muted-foreground)) !important; }
.progress-bar.bg-danger { background-color: hsl(var(--destructive)) !important; }
/* merp 這條用 hsl(var(--border)) 當填充色 → 對比 1.2:1 等於沒填充，改 muted-foreground 淡版 */
.progress-bar.bg-secondary { background-color: hsl(var(--muted-foreground) / .4) !important; }


/* =============================================================================
 * §25 側欄區隔 + 元件暗色可讀性（移植自 merp §25 L1640-1688）
 * 側欄/頂 bar 一律吃 token（亮色淺底、暗色深底，自動翻轉）
 * ============================================================================= */
#sidebar.app-sidebar,
.app-sidebar,
.app-sidebar-bg {
    background: hsl(var(--sidebar-bg)) !important;
    border-right: 1px solid hsl(var(--sidebar-border));
}
#header.app-header {
    background: hsl(var(--card)) !important;
    border-bottom: 1px solid hsl(var(--border));
}

/* 表單元件在暗色的可讀性（Bootstrap 預設白底） */
.form-control, .form-select {
    /* ★★ 必須用 background-color 不能用 background 簡寫（2026-07-27 逐頁掃描抓到）：
       簡寫會把 Bootstrap 5.3 給 .form-select 畫下拉箭頭用的
       background-image: var(--bs-form-select-bg-img) 一併清成 none
       → 全站所有下拉選單長得跟純文字輸入框一模一樣，使用者看不出可以點開
       （旁邊的 date input 還有原生日曆圖示，對比之下更明顯）。
       影響所有含 .form-select 的頁面。 */
    background-color: hsl(var(--background));
    border-color: hsl(var(--input));
    color: hsl(var(--foreground));
}
.form-control:focus, .form-select:focus {
    background: hsl(var(--background));
    color: hsl(var(--foreground));
    border-color: hsl(var(--ring));
}
.form-control::placeholder { color: hsl(var(--hint)); }
.input-group-text {
    background: hsl(var(--muted));
    border-color: hsl(var(--input));
    /* ★已被 §S-31 覆蓋（實測生效值：color=rgb(105, 105, 114)） */
    color: hsl(var(--muted-foreground));
}
.dropdown-menu {
    background: hsl(var(--popover));
    border: 1px solid hsl(var(--border));
    color: hsl(var(--popover-foreground));
}
.dropdown-item { color: hsl(var(--popover-foreground)); }
.dropdown-item:hover, .dropdown-item:focus { background: hsl(var(--accent)); color: hsl(var(--accent-foreground)); }
.modal-content { background: hsl(var(--card)); color: hsl(var(--card-foreground)); border: 1px solid hsl(var(--border)); }
.modal-header, .modal-footer { border-color: hsl(var(--border)); }
.alert { border: 1px solid hsl(var(--border)); background: hsl(var(--muted)); color: hsl(var(--foreground)); }
/* ★已被 L2044 覆蓋（實測生效值：background=hsl(var(--muted)) !important；color=rgb(9, 9, 11)）
   ★★2026-08-06 訂正 —— 這一筆註解「對一半」，是最容易誤導人的那種：
     本行同時管 .alert-success 與 .alert-info，但兩者的最終命運已經不同。
     · .alert-info    → 註解正確。勝出者＝§4 後面的中性化那條，
                        實測 bg=rgb(244,244,245) / color=rgb(9,9,11)。
     · .alert-success → 註解**已不成立**。檔尾 §修正「3. alert-success 底色還原」
                        又用 !important 把它改回綠色（該處註解寫明「前面章節有兩條後來的
                        規則把 .alert-success 壓回中性 muted」，指的就是本行與 §4 那條）。
                        實測 bg=rgb(231,249,237) / color=rgb(25,118,59)，不是 muted。
     行號 L2044 也早已漂掉，一併改引章節名。 */
.alert-success, .alert-info { background: hsl(var(--muted)); color: hsl(var(--foreground)); }
/* ★已被 L2054 覆蓋（實測生效值：background=hsl(var(--destructive) / .1) !important；color=rgb(186, 28, 28)） */
.alert-danger { background: hsl(var(--destructive) / .12); color: hsl(var(--destructive-fg)); border-color: hsl(var(--destructive) / .3); }
.list-group-item { background: hsl(var(--card)); color: hsl(var(--card-foreground)); border-color: hsl(var(--border)); }
/* nav-tabs 底線式（取代 _link.blade.php inline 寫死的 #6c757d / #212529）
   ★轉換：選中底線 merp 用 primary → 肥肥用 --foreground
     （底線是「線」不是「底」，用金黃在白底上幾乎看不見） */
.nav-tabs { border-bottom-color: hsl(var(--border)); }
.nav-tabs .nav-link { color: hsl(var(--muted-foreground)); border: 0; border-bottom: 2px solid transparent; }
.nav-tabs .nav-link:hover { border-bottom-color: hsl(var(--border)); }
.nav-tabs .nav-link.active {
    color: hsl(var(--foreground));
    background: transparent;
    border: 0;
    border-bottom: 2px solid hsl(var(--foreground));
    font-weight: 600;
}
.card-header-tabs { margin-bottom: -1px; }


/* =============================================================================
 * §28 全站彩色 utility 收斂（移植自 merp §28 L1841-1913）
 * SSOT：任何頁面、含 JS 動態產生的元件都看不到舊配色。
 * shadcn 是單色設計：語意靠深淺與圖示，不靠鮮豔填底。只有真正的錯誤保留紅。
 * ★轉換：merp 這裡把 .btn-primary 與 .btn-dark 綁在一起 → 肥肥必須拆開（見 §4）
 * ============================================================================= */

/* ---- 按鈕：全部收斂 ---- */
/* ★注意：這裡「不」重複宣告 .btn-primary / .btn-dark，避免蓋掉 §4 拆開後的黃/黑分工。
   §4 已給完整 bg/hover/active/disabled，此處只補其餘色系。 */
/* ★★★ 2026-08-10 補 .btn-white（唯一改動：選擇器清單加一個 class，宣告區一字未動）
   〔為什麼加〕§4（L490）把 .btn-white / .btn-secondary / .btn-light 三者寫在同一條規則，
     但 §28 這條清單原本**漏了 .btn-white** —— 於是 .btn-secondary / .btn-light 被 §28 收斂、
     .btn-white 卻停留在 §4 的值。兩者長得不一樣，卻被當成同義詞使用：
     「移除 COLOR_ADMIN 詞彙」把 104 處 btn-white 改名成 btn-secondary，就整批變了色
     （底 #fafafa→#f5f5f5、字 #000→#171717）。當時的「零視覺變化」證明是錯的，
     因為它掃的是「同時提到 .btn-white 與 .btn-secondary 的規則塊」，
     而 §28 從未提到 .btn-white，結構上就進不了樣本。
   〔影響〕所有 .btn-white 的 bg / color / border-color / hover / active 一律對齊 .btn-secondary，
     亦即 --bs-btn-bg 由 hsl(var(--background)) 改吃 hsl(var(--secondary))、
     --bs-btn-color 由 hsl(var(--foreground)) 改吃 hsl(var(--secondary-foreground))。
     已改名的 104 處與尚未改名的剩餘處就此回到同一外觀，後續改名才真正是零視覺變化。
     ★更高特異度的既有覆蓋不受影響（例：.app-content td …btn-xs.btn-white、
     .pos .pos-sidebar-footer .btn.btn-white、.btn-white.text-white 仍各自勝出）。
   〔原值存查〕本規則原本的選擇器清單逐字如下（僅供還原比對，勿刪）：
     .btn-success, .btn-info, .btn-warning, .btn-secondary, .btn-light,
     .btn-outline-secondary, .btn-outline-success, .btn-outline-info, .btn-outline-warning, .btn-outline-primary, .btn-outline-dark */
.btn-success, .btn-info, .btn-warning, .btn-white, .btn-secondary, .btn-light,
.btn-outline-secondary, .btn-outline-success, .btn-outline-info, .btn-outline-warning, .btn-outline-primary, .btn-outline-dark {
    --bs-btn-bg: hsl(var(--secondary)); --bs-btn-border-color: hsl(var(--border)); --bs-btn-color: hsl(var(--secondary-foreground));
    --bs-btn-hover-bg: hsl(var(--accent)); --bs-btn-hover-border-color: hsl(var(--border)); --bs-btn-hover-color: hsl(var(--accent-foreground));
    --bs-btn-active-bg: hsl(var(--accent)); --bs-btn-active-color: hsl(var(--accent-foreground));
}
.btn-danger, .btn-outline-danger {
    --bs-btn-bg: hsl(var(--destructive)); --bs-btn-border-color: hsl(var(--destructive)); --bs-btn-color: hsl(var(--destructive-foreground));
    --bs-btn-hover-bg: hsl(var(--destructive) / .9); --bs-btn-hover-border-color: hsl(var(--destructive) / .9); --bs-btn-hover-color: hsl(var(--destructive-foreground));
}
/* merp 用 hsl(var(--border)) 當底線色（對比 1.2:1 看不見）→ 改 muted-foreground */
.btn-link { color: hsl(var(--foreground)); text-decoration-color: hsl(var(--muted-foreground) / .45); }
.btn {
    /* ★★ 2026-07-29 SSOT：同一個按鈕家族原本有三種圓角
       —— .btn 10px（var(--radius)）／.btn-sm 8px／.btn-xs 7px（-3px，還是個不存在的階）。
       shadcn 的 Button 不分 size，全部都是 rounded-md。
       tailwind 對應：--radius-sm=-4 / --radius-md=-2 / --radius-lg=0 / --radius-xl=+4，
       -3px 從來就不在這四階裡，是被發明出來的。 */
    border-radius: var(--radius-md);
    font-weight: 500;
}
/* ★§S-73：原本是 `box-shadow: 0 0 0 2px hsl(var(--background)), 0 0 0 4px hsl(var(--ring));`
   （原值記在此存查）。官方 button 的焦點寫法是
   `focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50`
   —— 沒有背景色間隙，改成「border 換 ring 色 ＋ 3px ring/50 柔環」。 */
.btn:focus-visible { border-color: hsl(var(--ring)); box-shadow: 0 0 0 3px hsl(var(--ring) / .5); }
/* 計數 / 狀態 chip → shadcn 中性 chip。
   ★★只作用在「非互動元素」(span)：形狀不代表語意。
   uiux.md 明定列表頁「新增」鈕＝`btn btn-sm btn-dark btn-rounded rounded-pill`，
   它是 <a>，同樣長得像 pill。merp 早期這條規則沒限定 span，把新增鈕一併壓成灰底
   且 pointer-events:none → 全站列表頁的新增按鈕點擊直接穿透、完全不能點（8 個頁面）。
   互動元素(a/button)一律走正常按鈕語意，不得在此收斂。 */
/* ★★ pointer-events 已整條移除：本專案有「用 jQuery 綁 click 的 span pill」
   （限時特賣啟用/停用切換等），沒有 onclick 屬性可判別 → 任何屬性層面的
   :not() 防護都會漏。視覺收斂可以做，但絕不准碰點擊行為。 */
span.btn.btn-rounded.rounded-pill,
span.btn.rounded-pill {
    background: hsl(var(--secondary)) !important;
    border-color: hsl(var(--border)) !important;
    color: hsl(var(--secondary-foreground)) !important;
    font-weight: 500;
}

/* ---- 背景 utility（非 badge/btn）---- */
/* ★這裡原本把 bg-success / bg-info / bg-warning 全壓成同一個灰，
   結果「週六列 vs 週日列」「達標 vs 未達標」這種用底色分類的表格會變成同一色、
   分類資訊整個消失（品保月報實例）。
   改成保留可辨識的柔和語意底（shadcn 的 soft variant 做法），
   前景一律 --foreground 才不會在亮暗兩模式失控。 */
.bg-primary:not(.badge):not(.btn):not(.progress-bar) { background-color: hsl(var(--secondary)) !important; color: hsl(var(--secondary-foreground)) !important; }
.bg-success:not(.badge):not(.btn):not(.progress-bar) { background-color: hsl(var(--success) / .14) !important; color: hsl(var(--foreground)) !important; }
.bg-info:not(.badge):not(.btn):not(.progress-bar)    { background-color: hsl(var(--info) / .13) !important; color: hsl(var(--foreground)) !important; }
.bg-warning:not(.badge):not(.btn):not(.progress-bar) { background-color: hsl(var(--warning) / .16) !important; color: hsl(var(--foreground)) !important; }
.bg-light:not(.badge):not(.btn) { background-color: hsl(var(--muted)) !important; color: hsl(var(--foreground)) !important; }
.bg-white { background-color: hsl(var(--card)) !important; }
/* ★merp 用 primary（近黑）；肥肥用 --dark，否則 bg-dark 會變成黃色區塊 */
.bg-dark:not(.badge):not(.btn) { background-color: hsl(var(--dark)) !important; color: hsl(var(--dark-foreground)) !important; }

/* ---- 文字語意色：保留語意，改用柔和 token 色值（暗色自動翻轉）---- */
.text-success { color: hsl(var(--success-fg)) !important; }
.text-warning { color: hsl(var(--warning-fg)) !important; }
.text-info, .text-primary { color: hsl(var(--foreground)) !important; }
.text-danger { color: hsl(var(--destructive-fg)) !important; }   /* 同上：紅「字」走 -fg token */
.text-muted, .text-secondary { color: hsl(var(--muted-foreground)) !important; }
.text-dark, .text-black { color: hsl(var(--foreground)) !important; }
/* ★★肥肥與 merp 最大的分歧：merp 把 .text-white 收成 --primary-foreground（近白）。
   肥肥的 --primary-foreground 是「黃底上的黑字」(#1A1A1A) —— 收成它會讓所有
   深底上的 text-white 變黑字＝隱形。肥肥必須維持真白，並靠 §4 的按鈕安全網
   （.btn-success.text-white 等）處理淺底誤用的情況。 */
.text-white { color: hsl(var(--dark-panel-foreground)) !important; }

/* ---- Alert：shadcn（細框 + 淡底，不用鮮色）---- */
.alert-success, .alert-info, .alert-primary, .alert-secondary, .alert-light {
    background: hsl(var(--muted)) !important;
    color: hsl(var(--foreground)) !important;
    border: 1px solid hsl(var(--border)) !important;
}
.alert-warning {
    background: hsl(var(--warning-bg)) !important;
    color: hsl(var(--warning-fg)) !important;
    border: 1px solid hsl(var(--warning-fg) / .25) !important;
}
.alert-danger {
    background: hsl(var(--destructive) / .1) !important;
    color: hsl(var(--destructive-fg)) !important;
    border: 1px solid hsl(var(--destructive) / .25) !important;
}


/* =============================================================================
 * §29 語意狀態 pill / chip（移植自 merp §29 L1914-1938）
 * SSOT：取代各 view 自己寫死 #d4edda 之類的底色
 * 用法：<span class="stat-pill status-working">；顏色一律由此檔決定
 * ============================================================================= */
/* ★2026-08-04 稽核：blade 0 命中 */
.stat-pill, .status-pill {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 4px 10px;
    border-radius: 999px;
    font-size: 12px;
    font-weight: 500;
    background: hsl(var(--secondary));
    color: hsl(var(--secondary-foreground));
    border: 1px solid hsl(var(--border));
}
/* ★2026-08-04 稽核：blade 0 命中 */
.stat-pill .n, .status-pill .n { font-size: 14px; font-weight: 600; }
/* ★2026-08-04 稽核：blade 0 命中 */
.stat-pill.status-working, .status-working { background: hsl(var(--success-bg)) !important; color: hsl(var(--success-fg)) !important; border-color: hsl(var(--success-fg) / .2) !important; }
/* ★2026-08-04 稽核：blade 0 命中 */
.stat-pill.status-off, .status-off { background: hsl(var(--muted)) !important; color: hsl(var(--muted-foreground)) !important; }
/* ★2026-08-04 稽核：blade 0 命中 */
.stat-pill.status-leave, .status-leave { background: hsl(var(--warning-bg)) !important; color: hsl(var(--warning-fg)) !important; border-color: hsl(var(--warning-fg) / .2) !important; }
/* ★2026-08-04 稽核：blade 0 命中 */
.stat-pill.status-late, .status-late,
.stat-pill.status-early, .status-early { background: hsl(var(--warning-bg)) !important; color: hsl(var(--warning-fg)) !important; border-color: hsl(var(--warning-fg) / .2) !important; }
/* ★2026-08-04 稽核：blade 0 命中 */
.stat-pill.status-absent, .status-absent { background: hsl(var(--destructive) / .1) !important; color: hsl(var(--destructive-fg)) !important; border-color: hsl(var(--destructive) / .2) !important; }
/* ★2026-08-04 稽核：blade 0 命中 */
.stat-pill.status-none, .status-none { background: hsl(var(--muted)) !important; color: hsl(var(--muted-foreground)) !important; }

/* ---------------------------------------------------------------------------
 * §29.4 步驟卡 + CTA 按鈕（移植自 merp §29.4 L1939-1958）
 * 共用元件，SSOT 在此，勿在頁內重定義
 * ★.btn-cta-primary ＝「新增/儲存」級主要 CTA → 保留品牌黃 --primary
 * ★.ck-step-num 只是序號圓點（裝飾強調，非動作）→ 用 --dark
 * --------------------------------------------------------------------------- */
/* ★2026-08-04 稽核：blade 0 命中 */
.btn-cta-primary { background: hsl(var(--primary)); color: hsl(var(--primary-foreground)); border: 1px solid hsl(var(--primary)); }
/* ★2026-08-04 稽核：blade 0 命中 */
.btn-cta-primary:hover, .btn-cta-primary:focus { background: hsl(var(--primary-light)); color: hsl(var(--primary-foreground)); border-color: hsl(var(--primary-light)); }
/* ★2026-08-04 稽核：blade 0 命中 */
.btn-cta-secondary { background: hsl(var(--card)); color: hsl(var(--foreground)); border: 1px solid hsl(var(--border)); }
/* ★2026-08-04 稽核：blade 0 命中 */
.btn-cta-secondary:hover, .btn-cta-secondary:focus { background: hsl(var(--accent)); color: hsl(var(--accent-foreground)); border-color: hsl(var(--border)); }
/* ★2026-08-04 稽核：blade 0 命中 */
.btn-cta-ghost { background: transparent; color: hsl(var(--muted-foreground)); border: 1px solid hsl(var(--border)); }
/* ★2026-08-04 稽核：blade 0 命中 */
.btn-cta-ghost:hover, .btn-cta-ghost:focus { background: hsl(var(--accent)); color: hsl(var(--accent-foreground)); }
/* ★2026-08-04 稽核：blade 0 命中 */
.ck-step { border: 1px solid hsl(var(--border)); border-radius: var(--radius-xl); padding: 16px; height: 100%; display: flex; flex-direction: column; gap: .5rem; background: hsl(var(--card)); }
/* ★2026-08-04 稽核：blade 0 命中 */
.ck-step-num { width: 26px; height: 26px; border-radius: 50%; background: hsl(var(--dark)); color: hsl(var(--dark-foreground)); font-weight: 700; font-size: .8rem; display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; }
/* ★2026-08-04 稽核：blade 0 命中 */
.ck-step .ck-step-desc { color: hsl(var(--muted-foreground)); font-size: .82rem; flex-grow: 1; }
/* ★2026-08-04 稽核：blade 0 命中 */
.ck-step-tag { font-size: .72rem; color: hsl(var(--muted-foreground)); border: 1px solid hsl(var(--border)); border-radius: 9999px; padding: 1px 8px; }
/* ★2026-08-04 稽核：blade 0 命中 */
.ck-step code.ck-cmd { background: hsl(var(--muted)); padding: 2px 6px; border-radius: 6px; }
/* 點開式 FAQ（details/summary，免 JS） */
/* ★2026-08-04 稽核：blade 0 命中 */
.ck-faq details { border: 1px solid hsl(var(--border)); border-radius: 8px; padding: .55rem .9rem; margin-bottom: .5rem; background: hsl(var(--card)); }
/* ★2026-08-04 稽核：blade 0 命中 */
.ck-faq details:last-child { margin-bottom: 0; }
/* ★2026-08-04 稽核：blade 0 命中 */
.ck-faq summary { cursor: pointer; font-weight: 600; font-size: .9rem; }
/* ★2026-08-04 稽核：blade 0 命中 */
.ck-faq details[open] summary { margin-bottom: .5rem; }
/* ★2026-08-04 稽核：blade 0 命中 */
.ck-faq details > *:not(summary) { font-size: .875rem; }

/* ---------------------------------------------------------------------------
 * §29.45 兩欄式表單表格手機直式（.table-stack-sm）（移植自 merp L1960-1974）
 * 「label td + 值/輸入框 td」的表單表格在手機上要橫捲很難用 → ≤575px 改直式堆疊
 * （label 變上方小灰字、輸入框滿版）。只掛在表單表格，清單表格不要用。
 * --------------------------------------------------------------------------- */
@media (max-width: 575.98px) {
  /* 表格常掛 .text-nowrap（!important）→ 這裡必須同強度反制，否則長提示字撐爆寬度 */
  .table-stack-sm, .table-stack-sm td, .table-stack-sm small { white-space: normal !important; }
  .table-stack-sm tbody, .table-stack-sm tr, .table-stack-sm td { display: block; width: 100% !important; }
  .table-stack-sm tr { padding: .55rem .85rem; border-bottom: 1px solid hsl(var(--border)); }
  .table-stack-sm tr:last-child { border-bottom: 0; }
  .table-stack-sm td { border: 0 !important; padding: 0; }
  .table-stack-sm td:first-child { font-size: .78rem; color: hsl(var(--muted-foreground)); padding-bottom: 2px; }
  .table-stack-sm td:first-child .col-form-label { padding: 0; font-size: inherit; color: inherit; }
}

/* ---------------------------------------------------------------------------
 * §29.5 全站捲軸 shadcn 化（移植自 merp L1976-1987）
 * 瀏覽器預設捲軸是亮灰粗軌道，暗色下非常突兀（看板/表格等橫向捲動區最明顯）。
 * 保持「看得見」（細軌 + token 色），不可用 scrollbar-width:none（macOS overlay
 * 捲軸會讓使用者看得到內容捲不到，見 feedback_hscroll_visible_scrollbar）。
 * --------------------------------------------------------------------------- */
* { scrollbar-width: thin; scrollbar-color: hsl(var(--border)) transparent; }
*::-webkit-scrollbar { width: 8px; height: 8px; }
*::-webkit-scrollbar-track { background: transparent; }
*::-webkit-scrollbar-thumb { background: hsl(var(--border)); border-radius: 4px; }
*::-webkit-scrollbar-thumb:hover { background: hsl(var(--muted-foreground) / .5); }
*::-webkit-scrollbar-corner { background: transparent; }


/* =============================================================================
 * §31 表格：表頭永不直排（移植自 merp §31 L2031-2047）
 * 中文欄名被擠壓會逐字換行成直的。
 * shadcn table 本來就 whitespace-nowrap，寬度靠 table-responsive 橫向捲動
 * ============================================================================= */
.table th,
.table thead th {
    white-space: nowrap;
}
/* 內容欄可換行（長文字/備註），其餘 td 預設不換行以維持整齊 */
.table td { white-space: nowrap; }
.table td.text-wrap,
.table td .text-wrap,
.table td.wrap,
.table td p,
.table td .quill-content { white-space: normal; }
/* 表格容器：超寬時橫向捲動（不要壓縮欄位） */
.table-responsive { -webkit-overflow-scrolling: touch; }


/* =============================================================================
 * §33b code / kbd / pre / tag / list-group（移植自 merp §34 L2150-2196，
 *     落在指定的 L2107-2197 範圍內）
 * Bootstrap 預設 code 是桃紅 #d63384，從沒 shadcn 化
 * ★轉換：list-group active merp 用 primary → 肥肥用 --dark
 * ============================================================================= */
code {
    background: hsl(var(--muted));
    color: hsl(var(--foreground));
    padding: .15em .4em;
    border-radius: var(--radius-sm);
    font-size: .875em;
}
a code, .btn code { background: transparent; padding: 0; }
kbd {
    background: hsl(var(--muted));
    color: hsl(var(--foreground));
    border: 1px solid hsl(var(--border));
    border-radius: var(--radius-sm);
    box-shadow: none;
}
pre {
    background: hsl(var(--muted));
    color: hsl(var(--foreground));
    border: 1px solid hsl(var(--border));
    border-radius: var(--radius);
    padding: .75rem 1rem;
}
pre code { background: transparent; padding: 0; }

/* 語意標籤（假別/狀態 chip）：柔和淡底 + 深字，不要用鮮豔實心底 */
.tag, .info-list .tag {
    display: inline-block;
    padding: .1rem .45rem;
    border-radius: 999px;
    font-size: 11px;
    font-weight: 500;
    border: 1px solid transparent;
}

/* list-group 語意色（Bootstrap 預設是鮮豔藍/綠/黃底）→ shadcn 收斂 */
.list-group-item-primary, .list-group-item-info, .list-group-item-secondary, .list-group-item-light {
    background: hsl(var(--muted)) !important;
    color: hsl(var(--foreground)) !important;
    border-color: hsl(var(--border)) !important;
}
.list-group-item-success { background: hsl(var(--success-bg)) !important; color: hsl(var(--success-fg)) !important; }
.list-group-item-warning { background: hsl(var(--warning-bg)) !important; color: hsl(var(--warning-fg)) !important; }
.list-group-item-danger  { background: hsl(var(--destructive) / .1) !important; color: hsl(var(--destructive-fg)) !important; }
.list-group-item.active  { background: hsl(var(--dark)) !important; color: hsl(var(--dark-foreground)) !important; border-color: hsl(var(--dark)) !important; }


/* =============================================================================
 * §35 Checkbox / Radio / Switch（移植自 merp §35 L2198-2265）
 * Bootstrap 預設在暗色下邊框太淡，幾乎看不見。
 * shadcn checkbox：未勾也有清楚邊框；勾選＝實心。
 * ★轉換：勾選底色 merp 用 primary → 肥肥用 --dark。
 *   這是刻意的：金黃底 + 白勾勾對比只有 1.7:1，勾了跟沒勾一樣；
 *   而 --dark 在亮色是近黑（配白勾）、暗色翻成近白（配深勾），兩模式都清楚。
 *   勾勾 SVG 的顏色剛好對應 --dark-foreground（亮 #FAFAFA / 暗 #18181B），
 *   與 merp 原本的兩組 SVG 完全一致，所以 SVG 直接照抄。
 * ============================================================================= */
.form-check-input {
    background-color: hsl(var(--background));
    border: 1px solid hsl(var(--muted-foreground) / .55);
    box-shadow: none;
}
.form-check-input:hover { border-color: hsl(var(--foreground) / .7); }
.form-check-input:focus {
    border-color: hsl(var(--ring));
    box-shadow: 0 0 0 3px hsl(var(--ring) / .5);   /* §S-73：原 `0 0 0 2px hsl(var(--ring) / .25)`，改官方 3px + /50 */
}
.form-check-input:checked {
    background-color: hsl(var(--dark));
    border-color: hsl(var(--dark));
}
/* 勾勾 / 圓點：用 --dark-foreground 的實色（暗色下 --dark 是白底 → 勾要是深色） */
.form-check-input:checked[type="checkbox"] {
    background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fafafa' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3e%3c/svg%3e");
}
html.dark .form-check-input:checked[type="checkbox"] {
    background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%2318181b' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3e%3c/svg%3e");
}
.form-check-input:checked[type="radio"] {
    background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%23fafafa'/%3e%3c/svg%3e");
}
html.dark .form-check-input:checked[type="radio"] {
    background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%2318181b'/%3e%3c/svg%3e");
}
/* Switch：未勾的軌道要有足夠對比（--muted 在白底上僅 1.05:1，等於看不見） */
/* ★2026-08-06：下面那條 `.form-switch .form-check-input:checked` 的
   background-color / border-color 兩個屬性，已由檔尾 §S-56 接手改成語意綠
   （--success-fg；業主：黑底會被讀成「停用」）。這裡刻意**不用**那套制式的
   「已被／覆蓋」宣稱寫法：守門測試 ThemeOverrideAnnotationsTest 明定帶狀態偽類
   （:checked）的候選者不參與自動判定，寫成制式宣稱等於給它一個它驗不了的斷言。
   ★這一行本身就是誘餌實測出來的：原本寫成制式宣稱時，守門立刻紅
   「L2376 引用了不存在的章節」—— 那一紅同時證明了這支守門是活的。
   本行同時管的 background-image（圓鈕 SVG）**沒有**被 §S-56 動到，仍然由這裡生效。
   unchecked / :hover / :disabled 三態也全部維持本節原值。 */
.form-switch .form-check-input {
    background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23ffffff'/%3e%3c/svg%3e");
    background-color: hsl(var(--muted-foreground) / .45);
    border-color: transparent;
}
.form-switch .form-check-input:hover { background-color: hsl(var(--muted-foreground) / .6); }
.form-switch .form-check-input:checked {
    background-color: hsl(var(--dark));
    border-color: hsl(var(--dark));
    background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fafafa'/%3e%3c/svg%3e");
}
html.dark .form-switch .form-check-input:checked {
    background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%2318181b'/%3e%3c/svg%3e");
}
.form-check-input:disabled { opacity: .5; }
.form-check-label { color: hsl(var(--foreground)); }

/* 等 JS 填入的預覽圖：空 src 會被瀏覽器解析成當前頁 URL → 顯示破圖 icon。
   沒有 src（或 src 為空）時先不顯示，有值才出現。 */
img:not([src]),
img[src=""] { display: none !important; }

/* 破圖佔位（圖片載入失敗時取代原圖，不露出瀏覽器破圖 icon）。★.merp-img-fallback → .fb-img-fallback */
.fb-img-fallback {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 24px;
    min-height: 24px;
    background: hsl(var(--muted));
    border: 1px dashed hsl(var(--border));
    border-radius: var(--radius-md);
    color: hsl(var(--muted-foreground));
    font-size: 12px;
}


/* =============================================================================
 * §36 第三方 plugin shadcn 化（移植自 merp §36 L2266-2378）
 * ★只取 SweetAlert / Tooltip / Popover / Lightbox 三段；
 *   Quill 與 Switchery 依指示跳過（肥肥 Quill 另有 skill 規範，Switchery 未使用）
 * ============================================================================= */

/* ---- SweetAlert（刪除確認彈窗） ----
   ★轉換：確認鈕 merp 用 primary（近黑）→ 肥肥用 --dark。
     swal 的預設鈕多半是「確定刪除 / 確定取消」，不是新增/儲存，不該用品牌黃。 */
.swal-modal {
    background: hsl(var(--card)) !important;
    border: 1px solid hsl(var(--border));
    border-radius: var(--radius-lg);        /* 與 .modal-content 同階（shadcn DialogContent = rounded-lg） */
}
.swal-title { color: hsl(var(--card-foreground)) !important; font-size: 1.125rem; font-weight: 600; }
.swal-text { color: hsl(var(--muted-foreground)) !important; font-size: .875rem; }
/* shadcn 的 AlertDialogFooter 沒有分隔線 —— 標題／內文／按鈕是同一張卡上的三段，不是三個區塊 */
.swal-footer { border-top: 0; }
/* ★圖示收小：原廠是 80px 的大圈，在 shadcn 的對話框裡過重
   （AlertDialog 本身根本沒有圖示，這裡保留但降到輔助角色） */
.swal-icon { width: 48px !important; height: 48px !important; margin: 4px auto 8px !important; }
.swal-icon--warning__body { height: 22px !important; top: 8px !important; }
.swal-icon--warning__dot  { bottom: 6px !important; }
.swal-button {
    background: hsl(var(--dark)) !important;
    color: hsl(var(--dark-foreground)) !important;
    border-radius: var(--radius-md);        /* shadcn Button = rounded-md */
    font-weight: 500;
}
.swal-button:not([disabled]):hover { background: hsl(var(--dark) / .88) !important; }
.swal-button--cancel {
    background: hsl(var(--secondary)) !important;
    color: hsl(var(--secondary-foreground)) !important;
    border: 1px solid hsl(var(--border));
}
.swal-button--danger { background: hsl(var(--destructive)) !important; color: hsl(var(--destructive-foreground)) !important; }
.swal-icon--warning__body, .swal-icon--warning__dot { background-color: hsl(var(--warning-fg)) !important; }
.swal-icon--warning { border-color: hsl(var(--warning-fg)) !important; }

/* ---- SweetAlert**2** v11（`.swal2-*`）—— 與上面那套是**兩個不同的函式庫** ----
   ★盤點結果（2026-07-29）：
     · sweetalert 2.x（`swal()` API、`.swal-modal`）：includes/_script.blade.php 全域載入，
       IoT×3 + POS×4 共 7 檔在用 —— 就是上面那段樣式。
     · sweetalert2 v11（`Swal.fire()` API、`.swal2-*`）：**只有**
       marketing/flash-sale/index.blade.php 第 449 行自己從 jsdelivr CDN 載，
       該頁用了 73 次 —— 主題裡原本一行樣式都沒有，整組是原廠外觀
       （大橘圈圖示、紫色確認鈕），跟全站語言完全對不上。
   ★為什麼全都要 !important：
     swal2 是 JS 在執行期把自己的 <style> 注入 <head>，
     排在本主題之後 → 同特異性下它會贏，只能用 !important 蓋。
   ★對齊 shadcn AlertDialog：
     Content = rounded-lg + border + shadow-lg + p-6 + max-w-lg
     Title = text-lg/600、Description = text-sm/muted、Footer 無分隔線、按鈕靠右
     Action = Button default（此處用 --dark，因為 swal 多半是刪除確認，不該用品牌黃）
     Cancel = Button outline */
.swal2-container { background: hsl(var(--dark-panel) / .5) !important; }
.swal2-popup {
    background: hsl(var(--card)) !important;
    color: hsl(var(--card-foreground)) !important;
    border: 1px solid hsl(var(--border)) !important;
    border-radius: var(--radius-lg) !important;
    box-shadow: 0 16px 48px -12px rgb(0 0 0 / .25) !important;
    padding: 1.5rem !important;                 /* p-6 */
    max-width: 32rem !important;                /* max-w-lg */
    gap: 0 !important;
}
.swal2-title {
    color: hsl(var(--card-foreground)) !important;
    font-size: 1.125rem !important;             /* text-lg */
    font-weight: 600 !important;
    padding: 0 !important;
    margin: 0 0 .5rem !important;
}
.swal2-html-container {
    color: hsl(var(--muted-foreground)) !important;
    font-size: .875rem !important;              /* text-sm */
    line-height: 1.25rem !important;
    margin: 0 !important;
    padding: 0 !important;
}
/* Footer：靠右、gap-2、無分隔線（shadcn AlertDialogFooter） */
.swal2-actions {
    margin: 1.5rem 0 0 !important;
    gap: .5rem !important;
    justify-content: flex-end !important;
    width: 100% !important;
}
.swal2-styled {
    margin: 0 !important;
    border-radius: var(--radius-md) !important; /* Button = rounded-md */
    font-size: .875rem !important;
    font-weight: 500 !important;
    padding: .5rem 1rem !important;             /* h-9 px-4 */
    box-shadow: none !important;
    transition: background-color .12s ease, color .12s ease;
}
.swal2-styled.swal2-confirm {
    background: hsl(var(--dark)) !important;
    color: hsl(var(--dark-foreground)) !important;
    border: 1px solid transparent !important;
}
.swal2-styled.swal2-confirm:hover { background: hsl(var(--dark) / .88) !important; }
.swal2-styled.swal2-cancel {
    background: hsl(var(--secondary)) !important;
    color: hsl(var(--secondary-foreground)) !important;
    border: 1px solid hsl(var(--border)) !important;
}
.swal2-styled.swal2-cancel:hover { background: hsl(var(--accent)) !important; }
.swal2-styled.swal2-deny {
    background: hsl(var(--destructive)) !important;
    color: hsl(var(--destructive-foreground)) !important;
    border: 1px solid transparent !important;
}
.swal2-styled:focus-visible {
    outline: none !important;
    /* §S-73：原 `0 0 0 2px hsl(var(--background)), 0 0 0 4px hsl(var(--ring) / .5)`（間隙環），
       改官方的 border-ring ＋ 3px ring/50。 */
    border-color: hsl(var(--ring)) !important;
    box-shadow: 0 0 0 3px hsl(var(--ring) / .5) !important;
}
/* 圖示：原廠 80px 大圈 → 48px 輔助角色，顏色改吃 token */
.swal2-icon { width: 48px !important; height: 48px !important; margin: 0 auto 1rem !important; border-width: 2px !important; }
.swal2-icon .swal2-icon-content { font-size: 1.75rem !important; }
.swal2-icon.swal2-warning  { border-color: hsl(var(--warning-fg)) !important;  color: hsl(var(--warning-fg)) !important; }
.swal2-icon.swal2-error    { border-color: hsl(var(--destructive)) !important; color: hsl(var(--destructive)) !important; }
.swal2-icon.swal2-error [class^='swal2-x-mark-line'] { background-color: hsl(var(--destructive)) !important; }
.swal2-icon.swal2-success  { border-color: hsl(var(--success-fg)) !important;  color: hsl(var(--success-fg)) !important; }
.swal2-icon.swal2-success [class^='swal2-success-line'] { background-color: hsl(var(--success-fg)) !important; }
.swal2-icon.swal2-success .swal2-success-ring { border-color: hsl(var(--success-fg) / .3) !important; }
.swal2-icon.swal2-info,
.swal2-icon.swal2-question { border-color: hsl(var(--muted-foreground)) !important; color: hsl(var(--muted-foreground)) !important; }
/* 輸入元件跟著全站 Input 規格 */
.swal2-input, .swal2-textarea, .swal2-select {
    border: 1px solid hsl(var(--border)) !important;
    border-radius: var(--radius-md) !important;
    background: hsl(var(--background)) !important;
    color: hsl(var(--foreground)) !important;
    font-size: .875rem !important;
    box-shadow: none !important;
}
.swal2-input:focus, .swal2-textarea:focus, .swal2-select:focus {
    border-color: hsl(var(--ring)) !important;
    box-shadow: 0 0 0 3px hsl(var(--ring) / .5) !important;   /* §S-73：原 `0 0 0 2px hsl(var(--ring) / .35)`，改官方 3px + /50 */
}
.swal2-validation-message {
    background: hsl(var(--destructive) / .1) !important;
    color: hsl(var(--destructive-fg)) !important;
    border-radius: var(--radius-md) !important;
    font-size: .8125rem !important;
}
.swal2-close { color: hsl(var(--muted-foreground)) !important; box-shadow: none !important; }
.swal2-close:hover { color: hsl(var(--foreground)) !important; }
.swal2-timer-progress-bar { background: hsl(var(--dark) / .25) !important; }
/* toast 模式（右上角小提示）維持精簡，不套 p-6 */
.swal2-popup.swal2-toast { padding: .75rem 1rem !important; max-width: none !important; }
.swal2-popup.swal2-toast .swal2-title { margin: 0 !important; font-size: .875rem !important; }
.swal2-popup.swal2-toast .swal2-icon { width: 24px !important; height: 24px !important; margin: 0 .5rem 0 0 !important; }

/* ---- Tooltip / Popover ----
   ★轉換：tooltip 底 merp 用 primary → 肥肥用 --dark（shadcn tooltip 就是深底淺字，
     黃底黑字的 tooltip 會像警告標籤） */
.tooltip-inner {
    background: hsl(var(--dark));
    color: hsl(var(--dark-foreground));
    border-radius: var(--radius-md);
    font-size: 12px;
}
.tooltip .tooltip-arrow::before { border-top-color: hsl(var(--dark)); border-bottom-color: hsl(var(--dark)); }
.popover {
    background: hsl(var(--popover));
    border: 1px solid hsl(var(--border));
    border-radius: var(--radius);
}
.popover-header { background: hsl(var(--muted)); color: hsl(var(--foreground)); border-bottom-color: hsl(var(--border)); }
.popover-body { color: hsl(var(--popover-foreground)); }

/* ---- Lightbox（圖片放大） ---- */
.lb-outerContainer, .lb-dataContainer {
    background: hsl(var(--card)) !important;
    border-radius: var(--radius);
}
.lb-data .lb-caption, .lb-data .lb-number { color: hsl(var(--card-foreground)) !important; }
.lb-closeContainer .lb-close { filter: invert(var(--lb-invert, 0)); }
html.dark .lb-closeContainer .lb-close { filter: invert(1); }


/* =============================================================================
 * §37 按鈕 disabled 狀態（移植自 merp §37 L2379-2398）
 * §4/§28 只設了 bg/hover/active，沒設 disabled → 沿用 Bootstrap 預設 →
 * 白字白底白邊，對比 1:1 完全看不見（merp 驗收代理在 /subscriptions 的停用「續約」鈕抓到）
 * ============================================================================= */
.btn:disabled,
.btn.disabled,
fieldset:disabled .btn {
    --bs-btn-disabled-bg: hsl(var(--muted));
    --bs-btn-disabled-color: hsl(var(--muted-foreground));
    --bs-btn-disabled-border-color: hsl(var(--border));
    background-color: hsl(var(--muted)) !important;
    color: hsl(var(--muted-foreground)) !important;
    border-color: hsl(var(--border)) !important;
    opacity: .7;
    cursor: not-allowed;
    pointer-events: auto;   /* 保留 title 提示 */
}
.btn:disabled i, .btn.disabled i { color: inherit !important; }


/* =============================================================================
 * §38 Lucide icons（移植自 merp L2399-2431）
 * lucide.createIcons() 把 <i data-lucide> 換成 <svg class="lucide ...">，
 * 並保留原本的 class（fa-lg / fa-spin / fa-fw 等 modifier 沿用）。
 *
 * ★★ 刻意偏離 merp：merp 已完全移除 Font Awesome，所以它可以裸寫 `.fa-lg{...}`。
 *    肥肥「還在用真的 Font Awesome」（Color Admin vendor.min.css 內建），裸寫會蓋掉
 *    FA 原廠尺寸（FA 的 .fa-lg 是 1.33333em + line-height/vertical-align 調整），
 *    全站每個圖示都會被改動 = 無謂的視覺 regression。
 *    → 這裡把所有 .fa-* modifier 限縮在 lucide svg 上（.lucide / i[data-lucide]）。
 *    TODO 待確認：等肥肥真的完成 FA → lucide 遷移後，可把 :is(...) 前綴拿掉還原成 merp 版。
 * ============================================================================= */
/* 讓 svg 像字型圖示：隨字級縮放、對齊文字基線 */
.lucide,
i[data-lucide] {          /* [data-lucide] 尚未渲染時（lucide.js 載入前的短暫閃現）也先撐好尺寸 */
    width: 1em;
    height: 1em;
    display: inline-block;
    vertical-align: -0.125em;
    flex-shrink: 0;
}
/* FA size modifier：設 font-size，svg width/height=1em 會跟著縮放 */
.lucide.fa-xs, i[data-lucide].fa-xs { font-size: .75em; }
.lucide.fa-sm, i[data-lucide].fa-sm { font-size: .875em; }
.lucide.fa-lg, i[data-lucide].fa-lg { font-size: 1.25em; }
.lucide.fa-2x, i[data-lucide].fa-2x { font-size: 2em; }
.lucide.fa-3x, i[data-lucide].fa-3x { font-size: 3em; }
.lucide.fa-4x, i[data-lucide].fa-4x { font-size: 4em; }
.lucide.fa-5x, i[data-lucide].fa-5x { font-size: 5em; }
/* fa-fw 固定寬（選單對齊用） */
.lucide.fa-fw, i[data-lucide].fa-fw { width: 1.25em; }
/* fa-spin：FA 移除後自帶旋轉動畫 */
@keyframes lucide-spin { to { transform: rotate(360deg); } }
.lucide.fa-spin, i[data-lucide].fa-spin { animation: lucide-spin 1s linear infinite; }
/* fa-rotate-* 靜態旋轉 transform（非 icon，是套在 icon 上的角度） */
.lucide.fa-rotate-90,  i[data-lucide].fa-rotate-90  { transform: rotate(90deg); }
.lucide.fa-rotate-180, i[data-lucide].fa-rotate-180 { transform: rotate(180deg); }
.lucide.fa-rotate-270, i[data-lucide].fa-rotate-270 { transform: rotate(270deg); }


/* =============================================================================
 * §39 Markdown 檢視（移植自 merp L2432-2461）
 * 串接說明等由 Markdown 渲染的內容
 * ============================================================================= */
.markdown-body { color: hsl(var(--foreground)); line-height: 1.7; }
.markdown-body h1 { font-size: 1.35rem; font-weight: 700; margin: 0 0 1rem; }
.markdown-body h2 { font-size: 1.1rem; font-weight: 700; margin: 1.75rem 0 .75rem; padding-top: .5rem; border-top: 1px solid hsl(var(--border)); }
.markdown-body h3 { font-size: .98rem; font-weight: 600; margin: 1.25rem 0 .5rem; }
.markdown-body p, .markdown-body li { font-size: .9rem; }
.markdown-body ul, .markdown-body ol { padding-inline-start: 1.25rem; }
.markdown-body hr { border-color: hsl(var(--border)); margin: 1.5rem 0; }
.markdown-body blockquote {
  margin: 1rem 0; padding: .6rem .9rem; font-size: .875rem;
  border-inline-start: 3px solid hsl(var(--border)); background: hsl(var(--muted)); border-radius: var(--radius);
}
.markdown-body blockquote p:last-child { margin-bottom: 0; }
.markdown-body code {
  font-size: .85em; padding: .12em .35em; border-radius: 4px;
  background: hsl(var(--muted)); color: hsl(var(--foreground));
}
.markdown-body pre {
  padding: .9rem 1rem; border-radius: var(--radius); overflow-x: auto;
  background: hsl(var(--muted)); border: 1px solid hsl(var(--border)); font-size: .82rem;
}
.markdown-body pre code { padding: 0; background: transparent; }
/* 表格：窄螢幕橫捲（可見捲軸，見全站 .x-scroll 規範） */
.markdown-body table {
  width: 100%; margin: 1rem 0; border-collapse: collapse; font-size: .875rem; display: block; overflow-x: auto;
}
.markdown-body th, .markdown-body td { padding: .5rem .75rem; border: 1px solid hsl(var(--border)); text-align: start; }
.markdown-body th { background: hsl(var(--muted)); font-weight: 600; white-space: nowrap; }


/* =============================================================================
 * §40 字級 utility（移植自 merp L2462-2478）
 * 收斂散落各頁的 inline font-size。
 * !important 是刻意的：複現 inline style 的高優先級，換 class 後視覺 100% 不變。
 * 值 = 原本寫死的 px，1:1 對應。未來要統一字級尺度改這裡即可。
 *
 * ★★2026-08-06 訂正：上面這兩句對 .fs-7 / .fs-9 / .fs-10 / .fs-11 已經不成立。
 *   §S-5「字級下限 12px」把這四個（連同 .fs-9px/.fs-10px/.fs-11px）統一抬到 12px。
 *   兩邊同為 (0,1,0) 特異性、同帶 !important → 後宣告者勝 → §S-5 贏。
 *   ⇒「1:1 對應」現在只對 .fs-12 以上成立；「改這裡即可」對這 7 個小字級也不成立
 *      （改這裡不會生效，要改 §S-5）。
 *   實測（登入後 /main 注入探針，量 computed font-size）：
 *      fs-7 / fs-9 / fs-10 / fs-11 / fs-9px / fs-10px / fs-11px → 全部 12px（勝出者 §S-5）
 *      fs-12 → 12px、fs-13 → 13px（勝出者本章節，未被覆蓋）
 * ============================================================================= */
/* ★已被 §S-5 覆蓋（實測生效值：font-size=12px，四個都是） */
.fs-7{font-size:7px!important}.fs-9{font-size:9px!important}.fs-10{font-size:10px!important}
/* ★.fs-11 同上被 §S-5 覆蓋成 12px；.fs-12 / .fs-13 未被覆蓋，值即所見 */
.fs-11{font-size:11px!important}.fs-12{font-size:12px!important}.fs-13{font-size:13px!important}
/* ★2026-08-04 稽核：blade 0 命中 */
.fs-14{font-size:14px!important}.fs-15{font-size:15px!important}.fs-16{font-size:16px!important}
/* ★2026-08-04 稽核：blade 0 命中 */
/* ★2026-08-04 稽核：blade 0 命中 */
.fs-17{font-size:17px!important}.fs-18{font-size:18px!important}.fs-20{font-size:20px!important}
/* ★2026-08-04 稽核：blade 0 命中 */
/* ★2026-08-04 稽核：blade 0 命中 */
/* ★2026-08-04 稽核：blade 0 命中 */
.fs-22{font-size:22px!important}.fs-28{font-size:28px!important}.fs-48{font-size:48px!important}

/* ── 包裹式下拉的多餘 caret ────────────────────────────────────────────────
   uiux 規範：overflow 容器內的下拉外層用 <div class="dropdown-toggle"> 包裹避免裁切，
   但 BS5 對「任何」.dropdown-toggle::after 都畫 caret → 外層 div 會在按鈕旁多一個三角形。
   真正的開關一律是 button/a（自帶 caret），div 上的一律隱藏。 */
div.dropdown-toggle::after { display: none; }

/* =============================================================================
 * 以下為肥肥專有補位：Color Admin 拔除後會消失、merp 主題未涵蓋的 class
 * ============================================================================= */

/* =============================================================================
 * gap-fatboy.css — Color Admin 拔除後的「缺口補位」樣式（肥肥 ERP / 2026-fatboy）
 * -----------------------------------------------------------------------------
 * 這個檔只放「Color Admin(app.min.css) 拔掉後會消失、且 merp 主題沒有涵蓋」的樣式。
 * merp 主題已涵蓋的 58 個 class 由另一份移植檔負責，本檔不重複。
 * POS 模組（.pos-* 共 15 class）不在本檔範圍，另批處理。
 *
 * ★ 載入順序：必須排在 shadcn-theme.css 之前或同層（本檔不覆蓋 shadcn token，
 *   只補 utility / 元件；shadcn-theme.css 仍是顏色 SSOT，最後載入）。
 *
 * ★ 鐵律（沿用 shadcn-theme.css 的規範）
 *   - 一律 hsl(var(--token))，本檔零 hex / 零 rgb 字面值
 *   - --primary（品牌金黃 #FFD200）只用於「新增 / 儲存」與品牌識別；
 *     一般強調 / active / hover 一律走 --dark 或 --muted
 *   - 背景與文字必須同一組 pair
 *   - --border 只能用在 border-*，絕不可當 color
 *   - 不寫任何 pointer-events:none 的收斂規則
 *
 * ★ Color Admin 私有變數 → shadcn token 對照（本檔統一用右邊）
 *     --bs-light                 → hsl(var(--muted))
 *     --bs-component-bg          → hsl(var(--card))       ※token 檔已定義同名 alias
 *     --bs-component-color       → hsl(var(--card-foreground))
 *     --bs-component-border-color→ hsl(var(--border))
 *     #348fe2（CA 藍 active）    → hsl(var(--dark))        ※依配色規則不用彩色 active
 *     #2d353c / #20252a（CA 深）  → hsl(var(--dark-panel))
 *     #adb5bd / #6c757d（CA 灰字）→ hsl(var(--muted-foreground))
 *     #f59c1a（CA 星星）          → hsl(var(--warning))
 *     --bs-gray-rgb / --bs-silver-rgb / --bs-gray-300-rgb → --muted-foreground / --hint
 *
 * 尺寸一律照 app.min.css 原值（用 grep 逐一核對過，見檔尾 §TODO 的差異說明）。
 * ============================================================================= */


/* =============================================================================
 * §A  px 級 utility（Color Admin 專有，Bootstrap 5 沒有）
 *     補位：.w-*px / .h-*px / .fs-*px / padding / margin / 負 margin / opacity / fw-600
 *     所有數值＝app.min.css 原值，逐條 grep 核對過。
 * ============================================================================= */

/* ---- 補位：.w-{n}px（Color Admin 原本提供） ---- */
.w-10px  { width: 10px  !important; }
.w-20px  { width: 20px  !important; }
.w-25px  { width: 25px  !important; }
.w-70px  { width: 70px  !important; }
.w-100px { width: 100px !important; }   /* 全站 79 處：表格欄寬，改動會全站錯位 */
.w-200px { width: 200px !important; }
.w-350px { width: 350px !important; }

/* .w-sm-200px 在 CA 內是包在 @media (min-width:576px) 裡（已用 grep 確認） */
@media (min-width: 576px) {
  .w-sm-200px { width: 200px !important; }
}

/* ---- 補位：.h-{n}px（Color Admin 原本提供） ---- */
.h-15px  { height: 15px  !important; }
.h-20px  { height: 20px  !important; }
.h-25px  { height: 25px  !important; }
.h-30px  { height: 30px  !important; }
.h-35px  { height: 35px  !important; }
/* ★2026-08-04 稽核：blade 0 命中 */
.h-45px  { height: 45px  !important; }
.h-60px  { height: 60px  !important; }
.h-100px { height: 100px !important; }
.h-150px { height: 150px !important; }
.h-200px { height: 200px !important; }

/* ---- 補位：.fs-{n}px（Color Admin 原本提供） ----
   ★2026-08-06 訂正：下面原本寫「已被 L7322 覆蓋」——那個行號早就漂掉了（實際在 §S-5，
     2026-08-06 當下是 L7565）。改引章節名，行號會隨改動漂移、章節不會。
     另補上原本漏標的 .fs-9px（它同樣在 §S-5 的選擇器清單裡，一樣被抬到 12px）。 */
/* ★已被 §S-5 覆蓋（實測生效值：font-size=12px） */
.fs-9px  { font-size: 9px  !important; }
/* ★已被 §S-5 覆蓋（實測生效值：font-size=12px） */
.fs-10px { font-size: 10px !important; }
/* ★已被 §S-5 覆蓋（實測生效值：font-size=12px） */
.fs-11px { font-size: 11px !important; }
.fs-12px { font-size: 12px !important; }
.fs-13px { font-size: 13px !important; }
.fs-14px { font-size: 14px !important; }
.fs-15px { font-size: 15px !important; }
.fs-18px { font-size: 18px !important; }
/* ★2026-08-04 稽核：blade 0 命中 */
.fs-24px { font-size: 24px !important; }

/* ---- 補位：px 級 padding（Color Admin 原本提供） ---- */
.ps-35px { padding-left: 35px !important; }   /* 57 處 */
.pt-10px { padding-top: 10px !important; }
.pb-10px { padding-bottom: 10px !important; }
/* ★2026-08-04 稽核：blade 0 命中 */
.px-35px { padding-left: 35px !important; padding-right: 35px !important; }
/* ★2026-08-04 稽核：blade 0 命中 */
.p-20px  { padding: 20px !important; }
.p-30px  { padding: 30px !important; }
.py-2px  { padding-top: 2px !important; padding-bottom: 2px !important; }

/* ---- 補位：px 級 margin（Color Admin 原本提供） ---- */
.mt-1px  { margin-top: 1px !important; }
.mt-10px { margin-top: 10px !important; }
.mb-10px { margin-bottom: 10px !important; }
.mb-20px { margin-bottom: 20px !important; }
.me-10px { margin-right: 10px !important; }
.my-10px { margin-top: 10px !important; margin-bottom: 10px !important; }

/* ---- 補位：負 margin（Color Admin 原本提供）----
   ★重要：本站的 bootstrap.min.css（shadcn build）「沒有」出貨負 margin utility
     （已 grep 確認 mt-n1/ms-n2/mt-n5/ms-n5 皆 0 筆），所以必須自己補，不能靠 BS5。
   ★數值採 Color Admin 原值（CA 的 $spacer = 0.9375rem／15px，不是 BS 的 1rem）：
       mt-n1 = -.234375rem  (0.25 × 0.9375)
       ms-n2 = -.46875rem   (0.5  × 0.9375)
       n5    = -2.8125rem   (3    × 0.9375)
     若改用 BS 級距（-.25 / -.5 / -3rem）會有 0.25px～3px 位移，故照 CA 原值。 */
.mt-n1 { margin-top: -.234375rem !important; }
.mt-n5 { margin-top: -2.8125rem  !important; }
.ms-n2 { margin-left: -.46875rem !important; }
.ms-n5 { margin-left: -2.8125rem !important; }

/* ---- 補位：Color Admin 專有 opacity 級距（Color Admin 原本提供）----
   ⚠ BS5 原生只有 25/50/75/100，這裡只補 CA 多出來的級距，不碰 BS5 既有的。 */
/* ★2026-08-04 稽核：blade 0 命中 */
.opacity-10 { opacity: .1 !important; }
.opacity-70 { opacity: .7 !important; }
/* ★2026-08-04 稽核：blade 0 命中 */
.text-opacity-10 { --bs-text-opacity: 0.1; }
/* ★2026-08-04 稽核：blade 0 命中 */
.text-opacity-20 { --bs-text-opacity: 0.2; }
/* ★2026-08-04 稽核：blade 0 命中 */
.bg-opacity-15   { --bs-bg-opacity: 0.15; }

/* ---- 補位：.fw-600（Color Admin 原本提供，BS5 只有 normal/bold/bolder/…） ---- */
.fw-600 { font-weight: 600 !important; }


/* =============================================================================
 * §B  高頻單品
 * ============================================================================= */

/* ---- 補位：.btn-xs（Color Admin 原本提供）— 全站 613 處 / 214 檔，最大宗 ----
   CA 原值：.btn.btn-xs{font-size:.6875rem;padding:2px 6px}
   加上圓角：CA 的 .btn 半徑是固定 4px，這裡對齊 shadcn 的 --radius 縮小版，
   與 shadcn-theme.css 的 --bs-border-radius-sm 同一個算式，維持 SSOT。 */
/* ★★ 2026-07-28 量測校正：字級 .6875rem(11px) → 12px。
   CA 原值是 11px，之前為了相容照抄，但 11px 低於 shadcn 最小階 text-xs(12px)，
   而且它是全站最大宗的按鈕（613 處 / 214 檔）—— 逐頁量測時 1861 個過小字裡
   有一大半是它。同日已把 .badge 與狀態 chip 都校正到 12px，
   btn-xs 若留在 11px，會變成「同一列裡 chip 12px、按鈕 11px」的新破口。
   padding 維持 2px 6px 不動：高度由 §列內動作鈕 的 min-height 32px 決定，
   這裡只改字級，不改盒模型，避免影響非表格內的 btn-xs 版面。 */
.btn.btn-xs {
  font-size: 12px;
  padding: 2px 6px;
  --bs-btn-font-size: 12px;
  --bs-btn-padding-y: 2px;
  --bs-btn-padding-x: 6px;
  --bs-btn-border-radius: var(--radius-md);
}

/* ---- 補位：.btn-icon（Color Admin 原本提供）— 27 處，常與 .btn-circle 連用 ----
   CA 原值：.btn.btn-icon{width:28px;height:28px;padding:0;border:none;
            line-height:28px;text-align:center;font-size:14px;border-radius:28px}
   ⚠ .btn-circle 在 app.min.css 內「完全查無定義」（grep 0 筆）——
     圓形效果本來就是 .btn-icon 的 border-radius:28px 給的，所以不另外補 .btn-circle。 */
.btn.btn-icon {
  width: 28px;
  height: 28px;
  padding: 0;
  border: none;
  line-height: 28px;
  text-align: center;
  font-size: 14px;
  border-radius: 28px;
}
.btn.btn-icon.btn-xs { width: 16px; height: 16px; font-size: 8px;  line-height: 16px; }
.btn.btn-icon.btn-sm { width: 22px; height: 22px; font-size: 11px; line-height: 22px; }
.btn.btn-icon.btn-lg { width: 34px; height: 34px; font-size: 17px; line-height: 34px; }
/* CA 只在 panel 標頭把 btn-icon 改 flex 置中，照搬 */
/* ★2026-08-04 稽核：blade 0 命中 */
.panel .panel-heading .panel-heading-btn .btn-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

/* ---- 補位：.bg-none（Color Admin 原本提供）— 422 處 ----
   CA 原值：.bg-none{background:0 0!important} */
.bg-none { background: none !important; }

/* ---- 補位：.caret（Color Admin 原本提供）— 212 處 ----
   BS5 移除了 BS3 的 <b class="caret">，CA 有補回。
   ★樣式改在檔尾的「下拉箭頭重複」段統一定義（shadcn chevron，不用 CA 的實心三角），
     同一個地方也負責把 Bootstrap 的 .dropdown-toggle::after 收掉，避免兩個三角形疊在一起。
     這裡不再重複定義，SSOT 只有一處。 */

/* ---- 補位：.flex-1（Color Admin 原本提供）— 45 處。BS5 只有 .flex-fill ---- */
.flex-1 { flex: 1; }

/* ---- 補位：.cursor-*（Color Admin 原本提供） ---- */
.cursor-pointer { cursor: pointer !important; }
.cursor-default { cursor: default !important; }

/* ---- 補位：.hide（Color Admin 原本提供）— 18 處，BS3 遺留 ---- */
.hide { display: none !important; }

/* ---- 補位：.input-group-addon（Color Admin 原本提供）— 26 處，BS3 遺留 ----
   等同 BS5 的 .input-group-text。fatboy 的 26 處目前都是
   `class="input-group-text input-group-addon"`（兩個一起掛），所以主要是保險。
   另外 CA 有一條 datepicker 專用的 addon 樣式，一併照搬（換 token）。 */
.input-group-addon {
  display: flex;
  align-items: center;
  padding: .375rem .75rem;
  font-size: 1rem;
  font-weight: 400;
  line-height: 1.5;
  color: hsl(var(--muted-foreground));
  text-align: center;
  white-space: nowrap;
  background-color: hsl(var(--muted));
  border: 1px solid hsl(var(--border));
  border-radius: var(--bs-border-radius);
}
.datepicker .input-daterange .input-group-addon {
  margin: 0;
  font-weight: 600;
  background: hsl(var(--muted));
  color: hsl(var(--muted-foreground));
  display: flex;
  align-items: center;
  border-radius: 0;
}
body .datetimepicker + .input-group-addon {
  border-top-left-radius: 0;
  border-bottom-left-radius: 0;
}

/* ---- 補位：.form-group（Color Admin 原本提供的「有 context」版本）— 27 處 ----
   ⚠ app.min.css 裡「沒有」單獨的 .form-group 規則（已 grep 確認），
     只有下面兩條有前綴的。所以拔掉 CA 後，裸 .form-group 的視覺是「零變化」。
     若在這裡補上 BS3 的 margin-bottom:1rem，反而會讓現在這 27 處全部多出 16px 間距 =
     主動製造版面位移。故不補，只照搬 CA 真的有的兩條。詳見 §TODO-3。 */
/* ★2026-08-04 稽核：blade 0 命中 */
.app-header .navbar-form .form-group { margin: 0; position: relative; }
/* ★2026-08-04 稽核：blade 0 命中 */
.form-horizontal.form-bordered .form-group {
  /* ★★§S-72 必要的連帶改動（全檔唯一一處，已用 grep 數過）：
     原寫 `hsl(var(--border) / .5)`。暗色 --border 改成帶 alpha 的 `0 0% 100% / 10%` 之後，
     它會展開成 `hsl(0 0% 100% / 10% / .5)` → **語法錯誤，整條宣告被丟掉，邊框直接消失**。
     ★不是「看起來沒差」而是真的會壞，所以這一行必須改。
     亮色的影響：分隔線從 --border 的 50% 變成 100%（白底上 241 → 229，細線略深）。
     ★這條規則本身旁邊已有 2026-08-04 稽核註記「blade 0 命中」（.form-horizontal.form-bordered 全站沒人用），
       所以實際畫面零影響；但還是改正，不在檔裡留一條暗色一定失效的 CSS。 */
  border-bottom: 1px solid hsl(var(--border));
  margin: 0;
}

/* ---- 補位：.nav-tabs-v2（Color Admin 原本提供）— 16 處（Order/* 各狀態列表頁）----
   CA 用 --bs-component-active-bg（＝CA 主題藍）當 active 底線，
   依配色規則改用 --dark（亮色近黑 / 暗色近白），不用品牌黃。 */
/* ★2026-08-04 稽核：blade 0 命中 */
.nav.nav-tabs.nav-tabs-v2 { border-bottom: 1px solid hsl(var(--border)); }
/* ★2026-08-04 稽核：blade 0 命中 */
.nav.nav-tabs.nav-tabs-v2 > .nav-item { margin-bottom: -1px; }
/* ★2026-08-04 稽核：blade 0 命中 */
.nav.nav-tabs.nav-tabs-v2 > .nav-item > .nav-link {
  border: none;
  border-bottom: 3px solid transparent;
  padding: 1rem 1rem .875rem;
  color: hsl(var(--muted-foreground));
  background: none;
}
/* ★2026-08-04 稽核：blade 0 命中 */
.nav.nav-tabs.nav-tabs-v2 > .nav-item > .nav-link:hover {
  border-bottom-color: hsl(var(--muted-foreground) / .45);
  color: hsl(var(--foreground));
}
/* ★2026-08-04 稽核：blade 0 命中 */
.nav.nav-tabs.nav-tabs-v2 > .nav-item > .nav-link.active {
  border-bottom-color: hsl(var(--dark));
  background: none;
  color: hsl(var(--foreground));
}
/* ★2026-08-04 稽核：blade 0 命中 */
.nav.nav-tabs.nav-tabs-v2 > .nav-item.show > .nav-link {
  background: none;
  border-bottom-color: hsl(var(--border));
  color: hsl(var(--foreground));
}

/* ---- 補位：灰階文字 utility（Color Admin 原本提供）----
   CA 走 --bs-gray-rgb / --bs-silver-rgb / --bs-gray-300-rgb，這些變數會跟著 CA 一起消失。
   映射：gray / silver → --muted-foreground；gray-300（更淺）→ --hint。
   ⚠ 這幾個是「文字色」，絕不可用 --border（對比 1.2:1）。 */
.text-gray     { color: hsl(var(--muted-foreground) / var(--bs-text-opacity, 1)) !important; }
/* ★2026-08-04 稽核：blade 0 命中 */
.text-silver   { color: hsl(var(--muted-foreground) / var(--bs-text-opacity, 1)) !important; }
.text-gray-300 { color: hsl(var(--hint) / var(--bs-text-opacity, 1)) !important; }

/* ---- 補位：灰階底色 / 框線 utility（Color Admin 原本提供）----
   底色一律連前景一起給（同一組 pair），否則暗色模式下深底配深字會看不見。 */
.bg-gray-100 {
  background-color: hsl(var(--secondary) / var(--bs-bg-opacity, 1)) !important;
  color: hsl(var(--secondary-foreground));
}
.bg-gray-500 {
  background-color: hsl(var(--muted-foreground) / var(--bs-bg-opacity, 1)) !important;
  color: hsl(var(--background));
}
.bg-gray-800 {
  /* CA 的 gray-800 是固定近黑；用兩模式皆深的 --dark-panel，不隨主題翻轉 */
  background-color: hsl(var(--dark-panel) / var(--bs-bg-opacity, 1)) !important;
  color: hsl(var(--dark-panel-foreground));
  /* ★★ 2026-07-27 逐頁掃描抓到：只設 color 是不夠的。
     Bootstrap 5.3 的 .card-body 吃的是 var(--bs-card-color)，不是繼承 color。
     這個 utility 掛在 .card 上時，卡片自己變深了，裡面的 .card-body 卻回頭吃
     §卡片那條 .card { --bs-card-color: hsl(var(--card-foreground)) } ——
     亮色模式的 --card-foreground 是近黑 → 深底配黑字，實測對比 1.05:1 等於看不見。
     暗色模式剛好不壞（--card-foreground 本來就是亮的），所以只在亮色炸。
     受害頁：/center/main 五張卡、/center/desktop 一張卡（揀貨/包貨/檢核數字全糊在黑底上）。
     連同 --bs-card-cap-* 一起覆寫，否則 .card-header 會是白底黑字、跟深色卡身撕裂。 */
  --bs-card-color: hsl(var(--dark-panel-foreground));
  /* 註：--bs-card-bg 這顆同樣贏不了上面自己那行 background-color !important（實測 computed
     仍是 --dark-panel），是刻意留著的冗餘保險，不是漏修 —— 日後掃描再掃到不用重查。 */
  --bs-card-bg: transparent;
  --bs-card-cap-bg: transparent;
  --bs-card-cap-color: hsl(var(--dark-panel-foreground));
  --bs-card-border-color: hsl(0 0% 100% / .10);
  /* ★★ 2026-07-31 全檔掃描抓到：上面那行 --bs-card-border-color 跟 .btn-yellow 犯同一個病 ——
     只設自訂屬性，贏不了 §5 `.card { border: 1px solid hsl(var(--border)) }` 這個**實體宣告**。
     實測修前這張深色卡的 computed border-top-color 是亮色 rgb(228,228,231) / 暗色 rgb(39,39,42)，
     而不是預期的 rgba(255,255,255,0.1) → 近黑面板外面套一圈淺灰框，跟卡身撕裂。
     （下面 .bg-gray-800 > .card-header/.card-footer 那條早就發現同一個坑並改用實體宣告，
       卡片本體這一顆是漏網的。）修法一致：補實體 border-color，同特異度但排在 §5 之後。 */
  border-color: hsl(0 0% 100% / .10);
}
/* ★主題裡有兩條同名的 `.card-header, .card-footer`，後面那條的 hsl(var(--card))
   會蓋掉前面的 transparent；亮色模式的 --card 就是純白 →
   深色卡片上出現一條白色標題列，跟卡身撕裂（實測 rgb(255,255,255)）。
   CSS 變數在這裡贏不了直接的 background 宣告，所以用更高特異度直接接手。 */
.bg-gray-800 > .card-header,
.bg-gray-800 > .card-footer {
  /* ★第 1555 行那條帶 !important（它是為了解決「寫死 #fff 在暗色會白底白字」而加的），
     光靠特異度贏不了，必須同樣用 !important 才蓋得過。 */
  background: transparent !important;
  color: hsl(var(--dark-panel-foreground)) !important;
  border-color: hsl(0 0% 100% / .10) !important;
}
.border-gray     { border-color: hsl(var(--border)) !important; }
.border-gray-300 { border-color: hsl(var(--border)) !important; }
.border-silver   { border-color: hsl(var(--border)) !important; }

/* ---- 補位：.bg-component（Color Admin 原本提供）— 4 處 ----
   --bs-component-bg-rgb 已在 shadcn-theme.css §1 定義（亮/暗各一份）。 */
.bg-component {
  --bs-bg-opacity: 1;
  background-color: rgba(var(--bs-component-bg-rgb), var(--bs-bg-opacity)) !important;
}

/* ---- 補位：.text-theme（Color Admin 原本提供）----
   CA 原本＝主題色（本站 remap 後會變品牌金黃 #FFD200）。
   ⚠ 金黃字在白底對比不足（Seller/bill/purchase 的點數、Seller/plan 的勾勾），
     依配色鐵律「一般強調用 --dark」，這裡改 --dark。見 §TODO-1。 */
.text-theme { color: hsl(var(--dark) / var(--bs-text-opacity, 1)) !important; }

/* ---- 補位：.alert-yellow（Color Admin 原本提供）— Seller/plan/subscription ----
   CA 走 --bs-yellow-*-subtle（CA 自加的變數，會一起消失）→ 改用 warning 柔和語意底。 */
.alert-yellow {
  --bs-alert-color: hsl(var(--warning-fg));
  --bs-alert-bg: hsl(var(--warning-bg));
  --bs-alert-border-color: hsl(var(--warning) / .35);
  --bs-alert-link-color: hsl(var(--warning-fg));
  color: hsl(var(--warning-fg));
  background-color: hsl(var(--warning-bg));
  border-color: hsl(var(--warning) / .35);
}

/* ---- 補位：.text-bg-indigo / .text-bg-pink（Color Admin 原本提供）— Order/create 的 badge ----
   CA 走 --bs-indigo-rgb / --bs-pink-rgb。改用 token 檔已有的語意色：
   indigo → --leave-annual(#8B5CF6 violet)、pink → --leave-sick(#EC4899)。
   前景用 --destructive-foreground（＝純白，token 檔中「彩色底上的白字」那一組）。 */
/* ★2026-08-04 稽核：blade 0 命中 */
.text-bg-indigo {
  color: hsl(var(--destructive-foreground)) !important;
  background-color: hsl(var(--leave-annual) / var(--bs-bg-opacity, 1)) !important;
}
/* ★2026-08-04 稽核：blade 0 命中 */
.text-bg-pink {
  color: hsl(var(--destructive-foreground)) !important;
  background-color: hsl(var(--leave-sick) / var(--bs-bg-opacity, 1)) !important;
}

/* ---- 補位：.bg-gradient-blue-indigo（Color Admin 原本提供）— 方案推薦卡 ----
   CA 原值：linear-gradient(to bottom,#348fe2 0,#8753de 100%)
   （注意 Seller/bill/index.blade.php 自己有 inline 覆蓋成 135deg 版，那頁不受影響） */
.bg-gradient-blue-indigo {
  background-image: linear-gradient(to bottom,
    hsl(var(--info)) 0,
    hsl(var(--leave-annual)) 100%) !important;
}

/* ---- 補位：.note（Color Admin 原本提供）— 4 處提示區塊 ----
   CA 原本吃 --bs-alert-bg/color/border-color（要搭配 .alert-* 修飾）。
   fatboy 的 4 處都是裸 .note（沒帶修飾），CA 變數消失後會整塊變透明，
   所以這裡給一組 muted 預設底；若日後加 .alert-* 修飾，下面的 var() 會自動接手。 */
.note {
  margin-bottom: 20px;
  position: relative;
  border-radius: var(--radius);
  display: flex;
  flex: 1;
  background: var(--bs-alert-bg, hsl(var(--muted)));
  color: var(--bs-alert-color, hsl(var(--foreground)));
  border-color: var(--bs-alert-border-color, hsl(var(--border)));
  border-left: 3px solid;
}
/* ★2026-08-04 稽核：blade 0 命中 */
.note .note-icon {
  width: 80px;
  font-size: 56px;
  display: flex;
  align-items: center;
  justify-content: center;
}
/* ★2026-08-04 稽核：blade 0 命中 */
.note .note-content { padding: 15px; flex: 1; }
/* ★2026-08-04 稽核：blade 0 命中 */
.note .note-content h1, .note .note-content h2, .note .note-content h3,
.note .note-content h4, .note .note-content h5, .note .note-content h6,
.note .note-content .h1, .note .note-content .h2, .note .note-content .h3,
.note .note-content .h4, .note .note-content .h5, .note .note-content .h6 { color: inherit; }
/* ★2026-08-04 稽核：blade 0 命中 */
.note.note-with-end-icon { border-left: none; border-right: 3px solid; }

/* ---- 補位：.iconify（Color Admin 原本提供）----
   CA 只在側欄 / 頂欄 menu-icon 內給 iconify 尺寸，其餘位置本來就沒樣式。
   （fatboy 的 <span class="iconify"> 靠 iconify JS 換成 svg，不是 CSS 的事） */
.app-sidebar .menu .menu-item .menu-icon .iconify,
.app-sidebar .menu .menu-item .menu-icon ion-icon {
  width: 18px; height: 18px; font-size: 18px; margin: 0 -2px;
}
/* ★2026-08-04 稽核：blade 0 命中 */
.app-top-menu .menu .menu-item .menu-icon .iconify,
.app-top-menu .menu .menu-item .menu-icon ion-icon {
  margin-right: 8px; width: 18px; font-size: 18px; margin-left: -2px; display: block;
}

/* ---- 補位：.nav-wizards-container / .nav-wizards-1（Color Admin 原本提供）----
   使用處：Pick/package/edit.blade.php:54（.nav-no / .nav-text 也只在這個家族內出現）。
   CA 的 active/completed 是 #348fe2 藍 → 依配色規則改 --dark。 */
@media (max-width: 767.98px) {
  .nav-wizards-container { overflow: scroll; }
  .nav-wizards-container .nav.nav-wizards-1 { min-width: 600px; }
}
.nav.nav-wizards-1 { flex-wrap: nowrap; }
.nav.nav-wizards-1 .nav-item { padding: 0; }
.nav.nav-wizards-1 .nav-item .nav-link {
  position: relative;
  color: hsl(var(--muted-foreground));
}
.nav.nav-wizards-1 .nav-item .nav-link .nav-no {
  width: 40px;
  height: 40px;
  margin: 0 auto;
  font-weight: 600;
  background: none;
  border: 2px solid hsl(var(--border));
  position: relative;
  z-index: 10;
  font-size: .875rem;
  border-radius: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: all .2s linear;
}
.nav.nav-wizards-1 .nav-item .nav-link .nav-text {
  text-align: center;
  margin-top: .46875rem;
  font-weight: 600;
  font-size: .75rem;
}
.nav.nav-wizards-1 .nav-item .nav-link.active,
.nav.nav-wizards-1 .nav-item .nav-link.completed { color: hsl(var(--foreground)); }
.nav.nav-wizards-1 .nav-item .nav-link.active .nav-no,
.nav.nav-wizards-1 .nav-item .nav-link.completed .nav-no {
  border-color: hsl(var(--dark));
  color: hsl(var(--foreground));
}
.nav.nav-wizards-1 .nav-item .nav-link.active .nav-no {
  box-shadow: 0 0 0 5px hsl(var(--dark) / .2);
}
.nav.nav-wizards-1 .nav-item .nav-link.disabled { color: hsl(var(--hint)); }
.nav.nav-wizards-1 .nav-item .nav-link:before,
.nav.nav-wizards-1 .nav-item .nav-link:after {
  content: "";
  position: absolute;
  top: 1.6875rem;
  height: 2px;
  background: hsl(var(--border));
  left: 0;
  right: calc(50% + 20px);
}
.nav.nav-wizards-1 .nav-item .nav-link:after {
  transition: all .2s linear;
  left: calc(50% + 20px);
  right: 0;
}
.nav.nav-wizards-1 .nav-item .nav-link.active:before,
.nav.nav-wizards-1 .nav-item .nav-link.active:after,
.nav.nav-wizards-1 .nav-item .nav-link.completed:before,
.nav.nav-wizards-1 .nav-item .nav-link.completed:after { background: hsl(var(--dark)); }
.nav.nav-wizards-1 .nav-item .nav-link.active:after,
.nav.nav-wizards-1 .nav-item .nav-link.completed:after { width: calc(50% - 20px); }
.nav.nav-wizards-1 .nav-item:first-child .nav-link:before,
.nav.nav-wizards-1 .nav-item:first-child .nav-link:after { left: calc(50% + 20px); }
.nav.nav-wizards-1 .nav-item:last-child .nav-link:before { right: calc(50% + 20px); }
.nav.nav-wizards-1 .nav-item:last-child .nav-link.active:before,
.nav.nav-wizards-1 .nav-item:last-child .nav-link.completed:before { background: hsl(var(--dark)); }
.nav.nav-wizards-1 .nav-item:last-child .nav-link:after { display: none; }

/* ---- 補位：.media / .media-list / .media-heading（Color Admin 原本提供）----
   使用處：includes/_head.blade.php:23 通知下拉（.dropdown-menu.media-list）
           + includes/_script.blade.php:796 動態塞入的 .dropdown-item.media。
   ★ .media 一律維持在 .dropdown-menu.media-list 底下（CA 原本就是巢狀），
     絕不寫裸 .media，避免污染全站。 */
.dropdown-menu.media-list { max-width: 360px; padding: 0; }
.dropdown-menu.media-list .media {
  border-top: 1px solid hsl(var(--border));
  border-bottom: 1px solid hsl(var(--border));
  display: flex;
}
.dropdown-menu.media-list .media + .media { border-top: none; }
/* ★2026-08-04 稽核：blade 0 命中 */
.dropdown-menu.media-list .media .media-object {
  height: 48px; width: 48px; line-height: 48px; font-size: 24px;
  color: hsl(var(--dark-panel-foreground));
  text-align: center; border-radius: 6px; display: block;
}
/* ★2026-08-04 稽核：blade 0 命中 */
.dropdown-menu.media-list .media .media-object-icon {
  position: absolute;
  border: 3px solid hsl(var(--popover));
  background: hsl(var(--popover));
  margin-top: -11px; display: block;
  width: 21px; height: 21px;
  text-align: center; font-size: 16px; line-height: 16px;
  border-radius: 40px; margin-left: 30px;
}
/* ★2026-08-04 稽核：blade 0 命中 */
.dropdown-menu.media-list .media .media-left  { padding-right: 12px; }
/* ★2026-08-04 稽核：blade 0 命中 */
.dropdown-menu.media-list .media .media-right { padding-left: 12px; }
.dropdown-menu.media-list .media .media-body  { flex: 1; min-width: 0; }
.dropdown-menu.media-list .media .media-heading {
  color: hsl(var(--popover-foreground));
  margin-bottom: 0;
  font-size: 12.5px;
  line-height: 18px;
}
.dropdown-menu.media-list .media p {
  text-overflow: ellipsis; overflow: hidden;
  margin-bottom: 5px; line-height: 14px; max-width: 270px;
  color: hsl(var(--muted-foreground));
  font-size: 11px;
}
.dropdown-menu.media-list .dropdown-header {
  font-weight: 700;
  color: hsl(var(--popover-foreground));
  font-size: 10px;
  padding: .61875rem .9375rem;
}
.dropdown-menu.media-list .dropdown-item   { padding: .61875rem .9375rem; }
/* ★2026-08-04 稽核：blade 0 命中 */
.dropdown-menu.media-list .dropdown-footer { padding: .61875rem .9375rem; }


/* =============================================================================
 * §C  外殼零件（merp 主題沒有）
 * ============================================================================= */

/* ---- 補位：.app-header-fixed / .app-sidebar-fixed（Color Admin 原本提供）----
   使用處：layouts/default.blade.php:50 `app app-header-fixed app-sidebar-fixed`
   ★ padding-top 是「頂欄高度」的硬尺寸，改了整站內容會被頂欄蓋住。 */
.app-header-fixed { padding-top: 50px; }
.app-header-fixed .app-header { position: fixed; }
@media (max-width: 767.98px) {
  /* ★★ 2026-07-29 修（業主：「手機版標題跟上面距離過大」）：
     101px 是 Color Admin 原廠值 —— CA 手機版的 header 是**兩排**
     （品牌列 + 導覽列）所以要讓 101px。
     但本站改成 shadcn 之後手機版 header 只有**一排 50px**（實測 .app-header 高 50），
     padding 卻沒跟著改 → 每一頁的標題上方都憑空多出 51px 空白。
     實測 /order-management/orders 430px：header 底 50、標題 top 117，中間空 67px。
     桌機版一直是 50px（正確），只有手機這條沒跟上。 */
  .app-header-fixed { padding-top: var(--fb-header-h); }
}
@media (min-width: 768px) {
  .app-with-top-menu.app-header-fixed { padding-top: 90px; }
}
.app-sidebar-fixed .app-sidebar { position: fixed; }
@media (min-width: 992px) {
  .app-with-hover-sidebar:not(.app-sidebar-minified).app-sidebar-fixed .app-sidebar { bottom: 0; }
}

/* ---- 補位：.app-content-full-height / .app-without-sidebar / .app-without-header ----
   使用處：layouts/pos.blade.php:22 `app app-content-full-height app-without-sidebar app-without-header`
   ★ 這三個沒補＝POS 全螢幕版面直接塌掉（內容不滿版、側欄留白）。 */
.app-content-full-height { height: 100%; }
@media (min-width: 768px) {
  .app-content-full-height .app-content { height: calc(100vh - 50px); }
  .app-content-full-height.app-empty .app-content,
  .app-content-full-height.app-without-header .app-content { height: 100vh; }
}
@media (max-width: 767.98px) {
  .app-content-full-height .app-content { min-height: calc(100vh - 101px); }
}
.app-without-sidebar .app-content { margin: 0; }
@media (max-width: 575.98px) {
  .app-without-sidebar:not(.app-with-top-menu) .app-header .navbar-brand { justify-content: center; }
}

/* ---- 補位：.app-sidebar-mobile-backdrop（Color Admin 原本提供）----
   ★ 搭配 includes/_shell.blade.php §3 的 body.app-sidebar-mobile-toggled。
     沒補＝手機開側欄時背後不變暗、也點不到關閉遮罩。
   CA 的 --bs-app-sidebar-bg-mobile-backdrop 會一起消失，改用 --dark-panel。 */
.app-sidebar-mobile-backdrop {
  position: fixed;
  top: 0; left: 0; right: 0; bottom: 0;
  /* ★★ 2026-07-29 修 bug（業主回報手機版「還是一樣被遮罩」）：
     遮罩 1020 比側欄的 1010 高 → 遮罩蓋在選單上面。
     實測命中測試：點「系統首頁」的座標，最上層是遮罩的 .stretched-link，
     elementFromPoint 回傳的元素不在 #sidebar 內 → 選單完全點不到，
     而且整個選單被 50% 黑蓋住看起來是灰的（業主兩張截圖都是這狀態）。
     ★真兇不是這裡的 1020，是**側欄少了手機版的抬升**：
       原廠 Color Admin 有 @media(max-width:767.98px){.app-sidebar{z-index:1021}}
       （已從 747fe38c9^ 的 app.min.css 取證），我重寫主題時漏抄這條。
       所以遮罩維持 1020 不動（它要蓋過 header 1020 才能整頁一致變暗），
       改成把側欄補回 1021 —— 見下方 §手機抽屜疊層。
     ★這也正是 shadcn Sheet 的語意：SheetOverlay 蓋住所有頁面內容，
       SheetContent 疊在 overlay 之上，overlay 永遠不蓋面板。 */
  z-index: 1020;
  width: 100%; height: 100%;
  padding: 0; border: 0; margin: 0; outline: 0;
  display: none;
  background: transparent;
}
@media (max-width: 767.98px) {
  /* ★★ 側欄抬到遮罩之上（還原原廠 Color Admin 的 .app-sidebar{z-index:1021}）。
     只在手機媒體查詢裡做 —— 桌機的疊層是 header(1020) > 側欄(1010)，
     那是刻意的（header 橫跨整個寬度壓在側欄上方），不能被這條污染。 */
  body.app-sidebar-mobile-toggled #sidebar.app-sidebar,
  .app-sidebar-mobile-toggled .app-sidebar:not(.app-sidebar-end) {
    z-index: 1021;
  }
  .app-sidebar-mobile-toggled .app-sidebar-mobile-backdrop:not(.app-sidebar-end) {
    display: block;
    background: hsl(var(--dark-panel) / .5);
    animation: appSidebarMobileBackdrop .2s ease;
  }
  .app-sidebar-mobile-closed .app-sidebar-mobile-backdrop:not(.app-sidebar-end) {
    display: block;
    background: transparent;
    animation: appSidebarMobileRemoveBackdrop .2s ease;
  }
  .app-sidebar-end-mobile-toggled .app-sidebar-end.app-sidebar-mobile-backdrop {
    display: block;
    background: hsl(var(--dark-panel) / .5);
    animation: appSidebarMobileBackdrop .2s ease;
  }
  .app-sidebar-end-mobile-closed .app-sidebar-end.app-sidebar-mobile-backdrop {
    display: block;
    background: transparent;
    animation: appSidebarMobileRemoveBackdrop .2s ease;
  }
}
@keyframes appSidebarMobileBackdrop {
  from { background: transparent; }
  to   { background: hsl(var(--dark-panel) / .5); }
}
@keyframes appSidebarMobileRemoveBackdrop {
  from { background: hsl(var(--dark-panel) / .5); }
  to   { background: transparent; }
}

/* ---- 補位：.menu-divider（Color Admin 原本提供）— includes/_nav.blade.php:79 ---- */
.app-sidebar .menu .menu-divider {
  height: 1px;
  background-color: hsl(var(--sidebar-border));
  margin: 7px 0;
}

/* ---- 補位：.navbar-user（Color Admin 原本提供）— 頂欄使用者頭像區 ---- */
.app-header .navbar-user img {
  width: 30px; height: 30px;
  margin-top: -5px; margin-bottom: -5px;
  border-radius: 30px; margin-right: 10px;
}
.app-header .navbar-user .image {
  width: 30px; height: 30px;
  margin-top: -5px; margin-bottom: -5px;
  overflow: hidden;
  background-size: cover; background-position: center; background-repeat: no-repeat;
  border-radius: 30px; margin-right: 10px;
}
/* ★2026-08-04 稽核：blade 0 命中 */
.app-header .navbar-user .image.image-icon {
  font-size: 22px; text-align: center;
  display: flex; align-items: center; justify-content: center;
}
/* ★2026-08-04 稽核：blade 0 命中 */
.app-header .navbar-user .image.image-icon i {
  float: none; display: block; font-size: 22px; text-align: center;
  width: auto; margin: 0 auto -10px;
}
@media (max-width: 767.98px) {
  .app-header .navbar-user img,
  .app-header .navbar-user .image { margin-right: 5px; }
}

/* ---- 補位：.app-loader + .spinner（Color Admin 原本提供）★★最危險的一條★★ ----
   .app-loader 是 position:fixed 的全螢幕遮罩，layouts/app.blade.php:22、
   layouts/empty.blade.php:21、includes/_load.blade.php:3 都有實體元素。
   ⚠⚠ 必須搭配 includes/_shell.blade.php §1 的 JS：
        loader.classList.add('fading') + document.body.classList.add('loaded')
        （另有 600ms 的 style.display='none' 保險）
      只拔 app.min.js 不補這段 JS、或只補 CSS 不補 JS → 全站永久白屏。
   ⚠ CA 原本是 .app-loader.loaded{display:none}（class 掛在 loader 上），
     但 _shell.blade.php 是把 loaded 掛在 <body> 上 → 兩種寫法都留，避免任一邊改壞。 */
.app-loader {
  position: fixed;
  top: 0; left: 0; bottom: 0; right: 0;
  background-color: hsl(var(--background));
  z-index: 1050;
  height: 100vh;
  opacity: 1;
  display: flex;
  align-items: center;
  justify-content: center;
}
.app-loader.fading { opacity: 0; animation: appLoaderFadeOut .2s ease; }
.app-loader.loaded,
body.loaded .app-loader { display: none; }
.app-loader.loaded .spinner,
body.loaded .app-loader .spinner { animation: none; }
@keyframes appLoaderFadeOut { from { opacity: 1; } to { opacity: 0; } }

/* CA 原值：border:2px solid #2d353c;border-top-color:#fff（深色環＋亮色缺口）。
   換 token 後亮/暗都成立：底環 --foreground 半透明，轉頭 --foreground 實色。 */
.spinner {
  height: 36px;
  width: 36px;
  border: 2px solid hsl(var(--foreground) / .2);
  border-top-color: hsl(var(--foreground));
  border-radius: 36px;
  animation: rotation .6s infinite linear;
}
.spinner.spinner-sm { height: 28px; width: 28px; }
@keyframes rotation { from { transform: rotate(0); } to { transform: rotate(359deg); } }

/* ---- 補位：.theme-panel 家族（Color Admin 原本提供）＝賣賣小幫手側拉板 ----
   使用處：includes/_head.blade.php:74。★業主天天用，失效一定被發現。
   搭配 includes/_shell.blade.php §6 的 JS（toggle .active + localStorage）。 */
.theme-panel {
  position: fixed;
  width: 260px;
  right: -260px;
  top: 0; bottom: 0;
  z-index: 1035;
  background: hsl(var(--card));
  color: hsl(var(--card-foreground));
  transition: right .2s linear;
  border-radius: var(--radius) 0 0 var(--radius);
}
.theme-panel.active { box-shadow: 0 5px 25px hsl(var(--foreground) / .3); right: 0; }
.theme-panel .theme-collapse-btn {
  position: absolute;
  /* ★已被 §S-39 覆蓋（實測生效值：left=-32px） */
  left: -30px;
  top: 30%;
  /* ★已被 §S-39 覆蓋（實測生效值：margin-top=-16px） */
  margin-top: -15px;
  /* ★已被 §S-39 覆蓋（實測生效值：width=32px；height=32px；line-height=32px） */
  width: 30px; height: 30px; line-height: 30px;
  font-size: 18px;
  color: hsl(var(--card-foreground));
  background: hsl(var(--card));
  text-align: center;
  border-radius: var(--radius) 0 0 var(--radius);
  box-shadow: 0 0 15px 0 hsl(var(--foreground) / .15);
}
@media (max-width: 575.98px) {
  .theme-panel .theme-collapse-btn { top: 35%; }
}
/* CA 用 FontAwesome 的 \f00d 換掉展開圖示；fatboy 的按鈕是 fa-face-laugh-squint，
   換成 \f00d 會把小幫手表情變成叉叉 → 不照搬這條（刻意省略，非遺漏）。 */
.theme-panel .theme-panel-content {
  background: hsl(var(--card));
  padding: 20px;
  position: absolute;
  top: 0; left: 0; right: 0; bottom: 0;
  overflow: scroll;   /* CA 原值。此容器有 data-scrollbar="true"，由 PerfectScrollbar 接手 */
  margin: 0;
  z-index: 10;
}
.theme-panel .theme-panel-divider {
  height: 1px;
  margin: 15px 0;
  background-color: hsl(var(--border));
}

/* ---- 補位：.btn-scroll-to-top（Color Admin 原本提供）— 5 處 ----
   搭配 includes/_shell.blade.php §7 的 JS（>200px 時加 .show）。 */
.btn-scroll-to-top {
  position: fixed !important;
  bottom: 20px;
  z-index: 1020;
  opacity: 0;
  display: none;
}
.btn-scroll-to-top.show { opacity: 1; display: block; animation: btnFadeIn .2s ease; }
/* ★2026-08-04 稽核：blade 0 命中 */
.app-with-end-sidebar .btn-scroll-to-top { left: 25px; right: auto; }
@media (min-width: 768px)  { .btn-scroll-to-top { right: 15px; } }
@media (max-width: 767.98px) { .btn-scroll-to-top { right: 10px; } }
@keyframes btnFadeIn { from { opacity: 0; } to { opacity: 1; } }

/* ---- 補位：.pace-top（Color Admin 原本提供）— layouts/empty.blade.php:18 ----
   只是把 pace 進度條頂到畫面最上緣。pace 本體樣式若也隨 app.min.css 消失，
   見 §TODO-5。進度條顏色是品牌識別，允許用 --primary。 */
.pace-top .pace .pace-progress { top: 0 !important; }
.pace-top .pace .pace-activity { top: 15px !important; }

/* ---- 補位：.error / .error-code / .error-desc（Color Admin 原本提供）----
   ★ 全部維持在 .error 底下（CA 原本就是巢狀），不寫裸 .error-code，
     以免和 errors/404.blade.php、errors/500.blade.php 自帶的同名 inline 樣式打架。
   ★ CA 原本 .error-content 有 background-image:url(images/error-page-pattern.png)，
     那是相對 assets/css/default/ 的路徑，本檔不在該目錄 → 直接拿掉，改純色深底。 */
.error {
  margin: 0 auto;
  text-align: center;
  height: 100vh;
  display: flex;
  flex-direction: column;
  justify-content: center;
}
/* ★2026-08-04 稽核：blade 0 命中 */
.error .error-code,
.error .error-content { flex: 1; transform: translateZ(0); }
/* ★2026-08-04 稽核：blade 0 命中 */
.error .error-code {
  color: hsl(var(--foreground));
  font-size: 200px;
  line-height: 1;
  text-shadow: 10px 10px 0 hsl(var(--foreground) / .2);
  padding: 1.875rem;
  height: 50vh;
  display: flex;
  justify-content: center;
  align-items: flex-end;
}
/* ★2026-08-04 稽核：blade 0 命中 */
@media (max-width: 767.98px) { .error .error-code { font-size: 160px; } }
/* ★2026-08-04 稽核：blade 0 命中 */
@media (max-width: 575.98px) { .error .error-code { font-size: 120px; } }
/* ★2026-08-04 稽核：blade 0 命中 */
.error .error-content {
  bottom: 0;
  padding: 1.875rem;
  background: hsl(var(--dark-panel));
  color: hsl(var(--dark-panel-foreground));
}
/* ★2026-08-04 稽核：blade 0 命中 */
.error .error-message {
  color: hsl(var(--dark-panel-foreground));
  font-size: 1.5rem;
  line-height: 1.2;
  letter-spacing: .5px;
  margin-bottom: .46875rem;
}
/* ★2026-08-04 稽核：blade 0 命中 */
.error .error-desc {
  font-size: .75rem;
  color: hsl(var(--dark-panel-foreground) / .5);
  letter-spacing: .5px;
}

/* ---- 補位：.login-v2 / .login-cover 家族（Color Admin 原本提供）----
   使用處：auth/login.blade.php、auth/forgotPassword.blade.php、
           auth/accountEdit.blade.php、auth/accountPasswordEdit.blade.php
   ★ login-cover 是深色照片底＋黑色漸層遮罩，文字恆白（不隨主題翻轉），
     所以這裡用 --dark-panel-foreground 而不是 --foreground。 */
.login.login-v2 {
  min-height: 100vh;
  color: hsl(var(--dark-panel-foreground));
  display: flex;
  justify-content: center;
  align-items: center;
  border-radius: var(--radius);
}
.login.login-v2 .login-container { max-width: 400px; padding: 0 20px; position: relative; flex: 1; }
.login.login-v2 .login-header { margin-bottom: 20px; position: relative; display: flex; align-items: center; }
.login.login-v2 .login-header .brand { padding: 0; font-size: 28px; font-weight: 300; flex: 1; }
.login.login-v2 .login-header .brand .logo {
  /* 品牌識別，允許用 --primary（金黃） */
  border: 14px solid transparent;
  border-color: transparent hsl(var(--dark-panel) / .15) hsl(var(--dark-panel) / .3);
  background-color: hsl(var(--primary));
  width: 28px; height: 28px;
  position: relative;
  font-size: 0;
  border-radius: 6px;
  margin-right: .703125rem;
}
.login.login-v2 .login-header .brand small,
.login.login-v2 .login-header .brand .small {
  font-size: 13px; display: block; opacity: .5; font-weight: 600;
}
.login.login-v2 .login-header .icon { opacity: .75; }
.login.login-v2 .login-header .icon i { font-size: 56px; }
@media (max-width: 767.98px) {
  .login.login-v2 .login-header .brand { font-size: 24px; }
  .login.login-v2 .login-header .brand .logo { width: 24px; height: 24px; border-width: 12px; }
  .login.login-v2 .login-header .brand small,
  .login.login-v2 .login-header .brand .small { font-size: 12px; }
  .login.login-v2 .login-header .icon i { font-size: 48px; }
}
.login.login-v2 .login-content .form-control:focus {
  box-shadow: 0 0 0 5px hsl(var(--dark-panel-foreground) / .35);
}
.login.login-v2 .login-content .form-check-input:checked {
  background-color: hsl(var(--primary));
  border-color: hsl(var(--primary));
}
.login-cover { position: fixed; top: 0; left: 0; right: 0; bottom: 0; }
.login-cover .login-cover-img,
.login-cover .login-cover-bg {
  position: fixed;
  top: 0; left: 0; right: 0; bottom: 0;
  background-size: cover;
  background-repeat: no-repeat;
  background-position: center;
}
.login-cover .login-cover-bg {
  background: linear-gradient(to bottom,
    hsl(var(--dark-panel) / .45) 0,
    hsl(var(--dark-panel) / .9));
}
.login-cover .login-cover-img { transition: background .2s ease; }


/* =============================================================================
 * §D  模組元件（merp 完全沒有）
 * -----------------------------------------------------------------------------
 * ★★ 本節所有短名（.title / .desc / .text / .price / .img / .info / .rating /
 *    .date / .time / .message / .image …）一律「維持巢狀」，
 *    絕不出現裸選擇器，否則會污染全站。
 * ★ Color Admin 原本把 .product 家族全部掛在 `.pos` 底下；本檔改成不強制 .pos 前綴，
 *   讓 pos 以外的頁面（Center/inventoryStats、StoreSetting/ApiPlatform）也能用，
 *   但仍以 .product / .product-row 為根，短名永遠是它的子選擇器。
 * ============================================================================= */

/* ---- 補位：.product-row / .product-container（Color Admin 原本提供）----
   CA 原值（.pos .product-row）：margin:-.703125rem + flex-wrap，
   欄寬斷點 576→50% / 768→33.33% / 992→50% / 1200→33.33% / 1800→25%。
   ⚠ Center/inventoryStats/index.blade.php:185 是 <tr class="product-row">，
     在 CA 底下本來就吃不到（沒有 .pos 祖先）→ 這裡用 :not(table) 的方式無法涵蓋 tr，
     故加 `div.product-row` 限定，避免把表格列變成 flex 而整張表崩掉。 */
div.product-row {
  padding: 0;
  margin: -.703125rem;
  display: flex;
  flex-wrap: wrap;
}
div.product-row .product-container { padding: .703125rem; width: 100%; }
@media (min-width: 576px)  { div.product-row .product-container { width: 50%; } }
@media (min-width: 768px)  { div.product-row .product-container { width: 33.33%; } }
@media (min-width: 992px)  { div.product-row .product-container { width: 50%; } }
@media (min-width: 1200px) { div.product-row .product-container { width: 33.33%; } }
@media (min-width: 1800px) { div.product-row .product-container { width: 25%; } }
div.product-row .product-container .product { height: 100%; }

/* ---- 補位：.product 商品格（Color Admin 原本提供）---- */
.product {
  background: hsl(var(--card));
  color: hsl(var(--card-foreground));
  margin: 0;
  text-decoration: none;
  overflow: hidden;
  position: relative;
  border-radius: var(--radius);
  border: 1px solid hsl(var(--border));
  transition: all .2s linear;
  display: flex;
  flex-direction: column;
}
.product:hover { box-shadow: 0 .5rem 1rem hsl(var(--foreground) / .12); }
.product .img {
  background-size: cover;
  background-repeat: no-repeat;
  background-position: center;
  min-height: 180px;
  transition: all .2s linear;
}
.product .text {
  flex: 1;
  display: flex;
  flex-direction: column;
  padding: .703125rem .9375rem;
}
.product .text .title {
  font-size: .875rem;
  font-weight: 600;
  color: hsl(var(--card-foreground));
  margin: 0 0 2px;
}
.product .text .desc {
  color: hsl(var(--muted-foreground));
  font-weight: 600;
  margin: 0 0 .9375rem;
  line-height: 1.4;
  flex: 1;
}
.product .text .price {
  margin: 0;
  font-weight: 600;
  color: hsl(var(--card-foreground));
  font-size: .875rem;
}
/* 商品資訊區（CA 的 .product-img / .product-info / .product-option 那組） */
/* ★2026-08-04 稽核：blade 0 命中 */
.product .product-img .img {
  padding-top: 75%;
  min-height: 0;
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
}
/* ★2026-08-04 稽核：blade 0 命中 */
@media (max-width: 767.98px) { .product .product-img .img { padding-top: 56.25%; } }
.product .product-info { padding: .9375rem 1.171875rem; flex: 1; }
.product .product-info .title { font-size: .875rem; font-weight: 600; }
.product .product-info .desc  { color: hsl(var(--muted-foreground)); margin-bottom: .9375rem; }

/* .not-available：售罄 / 不可選狀態 */
.product.not-available { cursor: not-allowed; box-shadow: none !important; }
.product.not-available .img,
.product.not-available .text { opacity: .65; }
.product.not-available .not-available-text {
  position: absolute;
  top: 0; left: 0; right: 0; bottom: 0;
  min-height: 180px;
  background: hsl(var(--dark-panel) / .75);
  color: hsl(var(--dark-panel-foreground));
  font-size: 1.5rem;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* ---- 補位：.widget-list 家族（Color Admin 原本提供）— Order/create 等 ---- */
.widget-list { background: hsl(var(--card)); color: hsl(var(--card-foreground)); }
.widget-list .widget-list-item {
  text-decoration: none;
  color: hsl(var(--card-foreground));
  display: flex;
}
.widget-list .widget-list-item + .widget-list-item .widget-list-content,
.widget-list .widget-list-item + .widget-list-item .widget-list-action {
  border-top: 1px solid hsl(var(--border));
}
.widget-list .widget-list-item .widget-list-media,
.widget-list .widget-list-item .widget-list-content,
.widget-list .widget-list-item .widget-list-action { padding: .61875rem .9375rem; }
.widget-list .widget-list-item .widget-list-media + div,
.widget-list .widget-list-item .widget-list-content + div,
.widget-list .widget-list-item .widget-list-action + div { padding-left: 0; }
/* ★2026-08-04 稽核：blade 0 命中 */
.widget-list .widget-list-item .widget-list-media.icon {
  padding-top: .3121875rem;
  padding-bottom: .3121875rem;
}
/* ★2026-08-04 稽核：blade 0 命中 */
.widget-list .widget-list-item .widget-list-media.icon i {
  display: block;
  width: 30px; height: 30px;
  text-align: center; line-height: 30px;
  font-size: 14px;
  border-radius: 8px;
}
/* ★2026-08-04 稽核：blade 0 命中 */
.widget-list .widget-list-item .widget-list-media img { max-width: 100%; max-height: 100%; }
.widget-list .widget-list-item .widget-list-content {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
}
/* ★2026-08-04 稽核：blade 0 命中 */
.widget-list .widget-list-item .widget-list-action { display: flex; align-items: center; }
/* ★2026-08-04 稽核：blade 0 命中 */
.widget-list .widget-list-item .widget-list-title { font-size: .75rem; margin: 0; }
/* ★2026-08-04 稽核：blade 0 命中 */
.widget-list .widget-list-item .widget-list-desc {
  font-size: .6875rem;
  margin: 0;
  color: hsl(var(--muted-foreground));
}

/* ---- 補位：.widget-chat 家族（Color Admin 原本提供）— Chat/index 等 ----
   ⚠ Chat/index.blade.php 自己有一大段 inline 覆蓋（氣泡圓角 / 顏色 / 版面），
     那些會蓋在本節上面；本節只負責「拔掉 CA 後不至於整個塌掉」的基底。 */
/* ★2026-08-04 稽核：blade 0 命中 */
.widget-chat { background: hsl(var(--card)); color: hsl(var(--card-foreground)); }
/* ★2026-08-04 稽核：blade 0 命中 */
.widget-chat .widget-chat-header { padding: .9375rem; display: flex; align-items: center; }
/* ★2026-08-04 稽核：blade 0 命中 */
.widget-chat .widget-chat-header .widget-chat-header-icon { padding-right: .9375rem; }
/* ★2026-08-04 稽核：blade 0 命中 */
.widget-chat .widget-chat-header .widget-chat-header-content { flex: 1; }
/* ★2026-08-04 稽核：blade 0 命中 */
.widget-chat .widget-chat-header .widget-chat-header-title { font-size: .75rem; margin: 0; }
/* ★2026-08-04 稽核：blade 0 命中 */
.widget-chat .widget-chat-header .widget-chat-header-desc {
  margin: 0;
  color: hsl(var(--muted-foreground));
}
/* ★2026-08-04 稽核：blade 0 命中 */
.widget-chat .widget-chat-body { padding: .9375rem; background: hsl(var(--muted)); }
/* ★2026-08-04 稽核：blade 0 命中 */
.widget-chat .widget-chat-item {
  color: hsl(var(--card-foreground));
  position: relative;
  min-height: 38px;
  padding: .3121875rem 0;
}
/* ★2026-08-04 稽核：blade 0 命中 */
.widget-chat .widget-chat-media {
  width: 38px; height: 38px;
  position: absolute;
  bottom: 3px;
  overflow: hidden;
  border-radius: 40px;
  display: flex;
  justify-content: center;
  align-items: center;
  left: 0;
}
/* ★2026-08-04 稽核：blade 0 命中 */
.widget-chat .widget-chat-media img { max-width: 100%; max-height: 100%; display: block; }
/* ★2026-08-04 稽核：blade 0 命中 */
.widget-chat .widget-chat-info { display: flex; margin-left: 12px; }
/* ★2026-08-04 稽核：blade 0 命中 */
.widget-chat .widget-chat-info-container {
  background: hsl(var(--card));
  display: inline-flex;
  align-items: flex-end;
  flex-wrap: wrap;
  padding: .46875rem .703125rem;
  position: relative;
  color: hsl(var(--card-foreground));
  border-radius: 16px;
}
/* ★2026-08-04 稽核：blade 0 命中 */
.widget-chat .widget-chat-message { font-weight: 600; color: hsl(var(--card-foreground)); flex: 1; }
/* ★2026-08-04 稽核：blade 0 命中 */
.widget-chat .widget-chat-name { font-weight: 600; width: 100%; }
/* ★2026-08-04 稽核：blade 0 命中 */
.widget-chat .widget-chat-time {
  font-style: italic;
  color: hsl(var(--muted-foreground));
  font-weight: 600;
  font-size: .675rem;
  padding-left: 12px;
}
/* ★2026-08-04 稽核：blade 0 命中 */
.widget-chat .with-media .widget-chat-info { min-width: 60%; margin-left: 53px; }
/* ★2026-08-04 稽核：blade 0 命中 */
.widget-chat .with-media .end .widget-chat-info { margin-right: 53px; }
/* ★2026-08-04 稽核：blade 0 命中 */
.widget-chat .end .widget-chat-media { left: auto; right: 0; }
/* ★2026-08-04 稽核：blade 0 命中 */
.widget-chat .end .widget-chat-info { margin-right: 12px; margin-left: 0; justify-content: flex-end; }
/* ★2026-08-04 稽核：blade 0 命中 */
.widget-chat .end.with-media .widget-chat-info { margin-right: 53px; }
/* ★2026-08-04 稽核：blade 0 命中 */
.widget-chat .widget-chat-input .widget-chat-toolbar {
  padding: .9375rem .9375rem .46875rem;
  display: flex;
  flex-wrap: wrap;
}
/* ★2026-08-04 稽核：blade 0 命中 */
.widget-chat .widget-chat-input .widget-chat-toolbar .widget-chat-toolbar-link {
  color: hsl(var(--muted-foreground));
  text-decoration: none;
}
/* ★2026-08-04 稽核：blade 0 命中 */
.widget-chat .widget-chat-input .widget-chat-toolbar .widget-chat-toolbar-link:hover,
.widget-chat .widget-chat-input .widget-chat-toolbar .widget-chat-toolbar-link:focus {
  color: hsl(var(--foreground));
}
/* ★2026-08-04 稽核：blade 0 命中 */
.widget-chat .widget-chat-input .widget-chat-toolbar .widget-chat-toolbar-link
  + .widget-chat-toolbar-link { margin-left: .9375rem; }
/* CA 用 :before 畫氣泡尖角；Chat/index 已 display:none 掉，這裡保留基底以免其他頁少了尖角 */
/* ★2026-08-04 稽核：blade 0 命中 */
.widget-chat .widget-chat-message:before {
  content: "";
  position: absolute;
  bottom: 0;
  border: 0 solid transparent;
  border-top-width: 15px;
  border-radius: 50%;
  width: 35px; height: 35px;
  left: -24px;
  border-right-width: 10px;
  border-right-color: hsl(var(--card));
  transform: rotate(10deg);
}
/* ★2026-08-04 稽核：blade 0 命中 */
.widget-chat .end .widget-chat-message:before {
  bottom: -5px;
  left: auto;
  right: -24px;
  border-right-width: 0;
  border-right-color: transparent;
  border-left-width: 10px;
  border-left-color: hsl(var(--card));
}

/* ---- 補位：.widget-todolist 家族（Color Admin 原本提供）— pos/machine 收據清單 ---- */
.widget-todolist { background: hsl(var(--card)); color: hsl(var(--card-foreground)); }
.widget-todolist:before,
.widget-todolist:after { content: ""; display: table; clear: both; }
.widget-todolist .widget-todolist-header {
  padding: .61875rem .9375rem;
  border-bottom: 1px solid hsl(var(--border));
  display: flex;
  align-items: center;
}
.widget-todolist .widget-todolist-header .widget-todolist-header-title {
  margin: 0;
  font-size: 1.125rem;
  font-weight: 400;
  color: hsl(var(--card-foreground));
  flex: 1;
}
.widget-todolist .widget-todolist-header .widget-todolist-header-total {
  text-align: center;
  font-size: 1.125rem;
  font-weight: 600;
  color: hsl(var(--card-foreground));
}
.widget-todolist .widget-todolist-header .widget-todolist-header-total small,
.widget-todolist .widget-todolist-header .widget-todolist-header-total .small {
  display: block;
  font-size: .6rem;
  line-height: 1;
}
.widget-todolist .widget-todolist-body {
  border-bottom: 1px solid hsl(var(--border));
  margin-bottom: 20px;
}
/* ★2026-08-04 稽核：blade 0 命中 */
.widget-todolist .widget-todolist-item { display: flex; }
/* ★2026-08-04 稽核：blade 0 命中 */
.widget-todolist .widget-todolist-item .widget-todolist-icon {
  padding: .61875rem .9375rem;
  text-align: center;
  border-top: 1px solid hsl(var(--border));
}
/* ★2026-08-04 稽核：blade 0 命中 */
.widget-todolist .widget-todolist-item .widget-todolist-input {
  padding: .61875rem .9375rem;
  width: 48px;
  display: flex;
  align-items: center;
  justify-content: center;
}
/* ★2026-08-04 稽核：blade 0 命中 */
.widget-todolist .widget-todolist-item .widget-todolist-input .form-check { padding-left: 18px; }
/* ★2026-08-04 稽核：blade 0 命中 */
.widget-todolist .widget-todolist-item .widget-todolist-input .form-check .form-check-input { margin-left: -18px; }
/* ★2026-08-04 稽核：blade 0 命中 */
.widget-todolist .widget-todolist-item .widget-todolist-input + div { padding-left: 0; }
/* ★2026-08-04 稽核：blade 0 命中 */
.widget-todolist .widget-todolist-item .widget-todolist-content {
  padding: .61875rem .9375rem;
  border-top: 1px solid hsl(var(--border));
  flex: 1;
}
/* ★2026-08-04 稽核：blade 0 命中 */
.widget-todolist .widget-todolist-item:first-child .widget-todolist-icon,
.widget-todolist .widget-todolist-item:first-child .widget-todolist-content { border-top: none; }

/* ---- 補位：.widget-chart-full-width（Color Admin 原本提供）— Center/mainPackage ---- */
/* ★2026-08-04 稽核：blade 0 命中 */
.widget-chart .widget-chart-full-width { margin: 0 -15px; }

/* ---- 補位：.messenger 家族（Color Admin 原本提供）— Chat/index.blade.php:77-189 ----
   ⚠ Chat/index.blade.php 有大量 !important 的 inline 覆蓋（寬度 340/300、
     手機 offcanvas 等），本節提供的是它蓋不到的基底（配色、內距、列表列）。 */
/* ★2026-08-04 稽核：blade 0 命中 */
.messenger {
  --bs-messenger-menu-width: 60px;
  --bs-messenger-chat-width: 360px;
  display: flex;
  overflow: hidden;
  position: relative;
  height: 100%;
}
/* ★2026-08-04 稽核：blade 0 命中 */
.messenger .messenger-menu {
  display: flex;
  flex-direction: column;
  width: var(--bs-messenger-menu-width);
  min-width: var(--bs-messenger-menu-width);
  background: hsl(var(--card));
  padding: .46875rem;
}
/* ★2026-08-04 稽核：blade 0 命中 */
@media (min-width: 992px)   { .messenger .messenger-menu { border-right: 1px solid hsl(var(--border)); } }
/* ★2026-08-04 稽核：blade 0 命中 */
@media (max-width: 991.98px){ .messenger .messenger-menu { display: none; } }
/* ★2026-08-04 稽核：blade 0 命中 */
.messenger .messenger-menu .messenger-menu-item { margin-bottom: .234375rem; }
/* ★2026-08-04 稽核：blade 0 命中 */
.messenger .messenger-menu .messenger-menu-item .messenger-menu-link {
  color: hsl(var(--card-foreground));
  text-decoration: none;
  padding: .46875rem;
  display: block;
  border-radius: var(--radius-md);
}
/* ★2026-08-04 稽核：blade 0 命中 */
.messenger .messenger-menu .messenger-menu-item .messenger-menu-link:hover,
.messenger .messenger-menu .messenger-menu-item .messenger-menu-link:focus { opacity: .5; }
/* ★2026-08-04 稽核：blade 0 命中 */
.messenger .messenger-menu .messenger-menu-item.active .messenger-menu-link {
  color: hsl(var(--foreground));
  background: hsl(var(--accent));
}
/* ★2026-08-04 稽核：blade 0 命中 */
.messenger .messenger-chat {
  display: flex;
  flex-direction: column;
  background: hsl(var(--card));
}
/* ★2026-08-04 稽核：blade 0 命中 */
@media (max-width: 991.98px) { .messenger .messenger-chat { flex: 1; } }
@media (min-width: 992px) {
  /* ★2026-08-04 稽核：blade 0 命中 */
  .messenger .messenger-chat {
    width: var(--bs-messenger-chat-width);
    border-right: 1px solid hsl(var(--border));
  }
}
/* ★2026-08-04 稽核：blade 0 命中 */
.messenger .messenger-chat .messenger-chat-header {
  padding: .9375rem;
  border-bottom: 1px solid hsl(var(--border));
}
/* ★2026-08-04 稽核：blade 0 命中 */
.messenger .messenger-chat .messenger-chat-body { flex: 1; overflow: hidden; }
/* ★2026-08-04 稽核：blade 0 命中 */
.messenger .messenger-chat .messenger-chat-list .messenger-chat-item .messenger-chat-link {
  text-decoration: none;
  padding: .9375rem;
  display: flex;
  color: hsl(var(--card-foreground));
  align-items: center;
}
/* ★2026-08-04 稽核：blade 0 命中 */
.messenger .messenger-chat .messenger-chat-list .messenger-chat-item .messenger-chat-link:hover,
.messenger .messenger-chat .messenger-chat-list .messenger-chat-item .messenger-chat-link:focus,
.messenger .messenger-chat .messenger-chat-list .messenger-chat-item.active .messenger-chat-link {
  background: hsl(var(--accent));
}
/* ★2026-08-04 稽核：blade 0 命中 */
.messenger .messenger-chat .messenger-chat-list .messenger-chat-item .messenger-chat-link .messenger-chat-media {
  min-width: 44px; width: 44px; height: 44px;
  border-radius: var(--radius-md);
  background: hsl(var(--muted));
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 28px;
  color: hsl(var(--muted-foreground));
}
/* ★2026-08-04 稽核：blade 0 命中 */
.messenger .messenger-chat .messenger-chat-list .messenger-chat-item .messenger-chat-link .messenger-chat-media img {
  max-width: 100%; max-height: 100%;
  border-radius: var(--radius-md);
}
/* ★2026-08-04 稽核：blade 0 命中 */
.messenger .messenger-chat .messenger-chat-list .messenger-chat-item .messenger-chat-link .messenger-chat-content {
  flex: 1;
  max-width: calc(100% - 44px);
  padding-left: .703125rem;
}
/* ★2026-08-04 稽核：blade 0 命中 */
.messenger .messenger-chat .messenger-chat-list .messenger-chat-item .messenger-chat-link .messenger-chat-title {
  display: flex;
  font-weight: 600;
  color: hsl(var(--card-foreground));
  font-size: .84375rem;
}
/* ★2026-08-04 稽核：blade 0 命中 */
.messenger .messenger-chat .messenger-chat-list .messenger-chat-item .messenger-chat-link .messenger-chat-title
  > div:not(.messenger-chat-time) {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* ★2026-08-04 稽核：blade 0 命中 */
.messenger .messenger-chat .messenger-chat-list .messenger-chat-item .messenger-chat-link .messenger-chat-time {
  font-size: .675rem;
  color: hsl(var(--muted-foreground));
  padding-left: .46875rem;
}
/* ★2026-08-04 稽核：blade 0 命中 */
.messenger .messenger-chat .messenger-chat-list .messenger-chat-item .messenger-chat-link .messenger-chat-desc {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: hsl(var(--muted-foreground));
  font-weight: 600;
  font-size: .75rem;
}
/* ★2026-08-04 稽核：blade 0 命中 */
.messenger .messenger-content { flex: 1; display: none; }
/* ★2026-08-04 稽核：blade 0 命中 */
@media (min-width: 992px)    { .messenger .messenger-content { display: block; } }
/* ★2026-08-04 稽核：blade 0 命中 */
@media (max-width: 767.98px) { .messenger .messenger-content { overflow: scroll; flex-wrap: nowrap; } }
/* ★2026-08-04 稽核：blade 0 命中 */
.messenger .messenger-content .widget-chat {
  display: flex;
  flex-direction: column;
  height: 100%;
  overflow: hidden;
}
@media (min-width: 992px) {
  /* ★2026-08-04 稽核：blade 0 命中 */
  .messenger .messenger-content .widget-chat .widget-chat-item .widget-chat-info .widget-chat-info-container {
    max-width: 51%;
  }
  /* ★2026-08-04 稽核：blade 0 命中 */
  .messenger .messenger-content .widget-chat .widget-chat-body { overflow: hidden; }
}
/* 自己發出的訊息：CA 用主題色底。依配色規則不用品牌黃，改 --dark（黑底白字 / 暗色反過來） */
/* ★2026-08-04 稽核：blade 0 命中 */
.messenger .messenger-content .widget-chat .widget-chat-item.end .widget-chat-info .widget-chat-info-container {
  background: hsl(var(--dark));
  margin-left: auto;
}
/* ★2026-08-04 稽核：blade 0 命中 */
.messenger .messenger-content .widget-chat .widget-chat-item.end .widget-chat-info .widget-chat-info-container
  .widget-chat-message { color: hsl(var(--dark-foreground)); }
/* ★2026-08-04 稽核：blade 0 命中 */
.messenger .messenger-content .widget-chat .widget-chat-item.end .widget-chat-info .widget-chat-info-container
  .widget-chat-message:before { border-left-color: hsl(var(--dark)); }
/* ★2026-08-04 稽核：blade 0 命中 */
.messenger .messenger-content .widget-chat .widget-chat-item.end .widget-chat-info .widget-chat-info-container
  .widget-chat-time { color: hsl(var(--dark-foreground) / .5); }
/* ★2026-08-04 稽核：blade 0 命中 */
.messenger .messenger-content .widget-chat .widget-chat-header { border-bottom: 1px solid hsl(var(--border)); }
/* ★2026-08-04 稽核：blade 0 命中 */
.messenger .messenger-content .widget-chat .widget-chat-body { flex: 1; }
/* ★2026-08-04 稽核：blade 0 命中 */
.messenger .messenger-content .widget-chat .widget-chat-input { border-top: 1px solid hsl(var(--border)); }

/* ---- 補位：.timeline 家族（Color Admin 原本提供）— Seller/systemMessage/index:14-40 ----
   ★ .date / .time 一律巢狀在 .timeline .timeline-time 底下（CA 原本就是）。 */
.timeline { list-style-type: none; margin: 0; padding: 0; position: relative; }
.timeline:before {
  content: "";
  position: absolute;
  top: 5px; bottom: 5px;
  width: 5px;
  background: hsl(var(--border));
  left: 20%;
  margin-left: -2.5px;
}
@media (max-width: 767.98px) { .timeline:before { left: 15%; } }
@media (max-width: 575.98px) { .timeline:before { left: 50%; } }
.timeline img { max-width: 100%; display: block; }
.timeline .timeline-item { position: relative; min-height: 50px; padding: 20px 0; }
.timeline .timeline-time {
  position: absolute;
  width: 18%;
  text-align: right;
  top: 30px;
  left: 0;
}
@media (max-width: 767.98px) { .timeline .timeline-time { width: 12%; } }
@media (max-width: 575.98px) { .timeline .timeline-time { width: 50%; padding-right: 20px; } }
.timeline .timeline-time .date {
  display: block;
  font-weight: 600;
  color: hsl(var(--foreground));
}
.timeline .timeline-time .time {
  display: block;
  font-weight: 600;
  line-height: 24px;
  font-size: 1.5rem;
  color: hsl(var(--foreground));
}
.timeline .timeline-icon { position: absolute; width: 10%; text-align: center; top: 40px; left: 15%; }
@media (max-width: 767.98px) { .timeline .timeline-icon { left: 10%; } }
@media (max-width: 575.98px) { .timeline .timeline-icon { left: 45%; } }
.timeline .timeline-icon a {
  text-decoration: none;
  width: 20px; height: 20px;
  display: inline-block;
  background: hsl(var(--background));
  line-height: 10px;
  color: hsl(var(--foreground));
  font-size: 14px;
  border: 5px solid hsl(var(--border));
  transition: border-color .2s linear;
  border-radius: 24px;
}
.timeline .timeline-content {
  background: hsl(var(--card));
  color: hsl(var(--card-foreground));
  position: relative;
  font-size: 14px;
  max-width: 640px;
  border-radius: var(--radius);
  margin-left: 23%;
}
.timeline .timeline-content:before {
  content: "";
  display: block;
  position: absolute;
  border: 10px solid transparent;
  top: 20px;
  left: -20px;
  border-right-color: hsl(var(--card));
}
@media (max-width: 991.98px) { .timeline .timeline-content { margin-left: 25%; margin-right: 5%; } }
@media (max-width: 767.98px) { .timeline .timeline-content { margin-left: 20%; margin-right: 0; } }
@media (max-width: 575.98px) {
  .timeline .timeline-content { margin-top: 60px; margin-left: 0; }
  .timeline .timeline-content:before {
    border-bottom-color: hsl(var(--card));
    top: -20px;
    border-right-color: transparent;
    left: 50%;
    margin-left: -10px;
  }
}
.timeline .timeline-header { padding: .9375rem; display: flex; align-items: center; }
/* ★2026-08-04 稽核：blade 0 命中 */
.timeline .timeline-header .userimage {
  width: 40px; height: 40px;
  overflow: hidden;
  border-radius: 36px;
  margin-right: .75rem;
}
.timeline .timeline-header .username {
  font-size: .875rem;
  font-weight: 600;
  color: hsl(var(--card-foreground));
  flex: 1;
}
.timeline .timeline-header .username a { color: hsl(var(--card-foreground)); text-decoration: none; }
.timeline .timeline-header + .timeline-body { padding-top: 0; }
.timeline .timeline-body { padding: .9375rem; }

/* ★★★【死碼標註 2026-08-06 —— 只標不刪】
   底下整段 `.gallery` 家族（`.gallery` / `.gallery .image` / `.img` / `.image-inner` /
   `.image-caption` / `.image-info` / `.image-info .title` / `.rating` / `span.star`
   （含 `.star.active`）/ `.desc`，到 `.gallery .image .image-info .desc` 那條為止）
   **在 blade 端已經 0 呼叫端**。

   ── 何時失去呼叫端 ──
   2026-08-06。這段的檔頭原本寫「— StoreSetting/ApiPlatform/create:45-57」，
   那就是它僅有的兩個呼叫端所在（另一個是 StoreSetting/Store/create）。
   同日這兩頁改用 shadcn 的 `.card` 語彙、收斂到共用的
   StoreSetting/_picker_card.blade.php，整個 Color Admin 的 gallery 版面隨之退場
   （改版理由與逐條對照寫在 _picker_card.blade.php 的檔頭註解裡）。

   ── 確認方式（照著跑，不要憑印象）──
     find resources app public/assets/js config database -type f \
       \( -name '*.php' -o -name '*.js' -o -name '*.vue' \) \
       -exec grep -HnE 'class="[^"]*\b(gallery|image-inner|image-info|image-caption)\b|classList\.(add|toggle)\(.(gallery|image-inner|image-info)|@class\([^)]*(gallery|image-inner)' {} \;
   ★一定要用 `find` 遞迴，不可以用 `<目錄>/*.php` 這種 shell glob（不進子目錄、也漏 .mjs）。
   ★2026-08-06 實跑結果：專案內**零命中**。兩類看起來像命中、實際不是的東西：
       · StoreSetting/{ApiPlatform,Store}/create、_picker_card 三支裡的
         `{{-- --}}` 註解 —— 那是在記錄「以前用 gallery、為什麼不用了」，不是 markup。
       · AiTool/alpha/index.blade.php 的 `galleryItems` / `galleryIndex`
         —— JS 變數名，與 CSS class 無關。
   ★這道掃描本身有先用誘餌驗過會紅（在 scratchpad 的**子目錄**放含 `class="gallery"`
     與 `class="image-inner"` 的假檔，確認抓得到，才敢說零命中）。
   ★掃不到的死角，誠實列出：① DB 裡的富文字（Quill）內容無法用檔案掃描證實；
     ② 已 minify 的 vendor JS 內容無法逐字確認（但那是第三方套件，不會產生本站的
     `.gallery` 版面）；③ 未掃 git 歷史與已刪分支。

   ── 要退場的話，請主題那邊（ray）決定 ──
   本輪**只加註解、一行都沒有刪**（專案守則：禁止刪除任何內容）。
   真要退場時請注意兩件事：
     ① `.gallery .image .image-info .desc` 那條寫死 `height: 51px; overflow: hidden`
        —— 這正是 §S-54 ② 註解裡提到「原本會把長說明剪掉」的那條，
        新的 `.sc-picker-desc` 就是為了不要它才存在的。它已經沒有作用對象。
     ② `.gallery .image .image-caption` 用了 `--dark-panel`（見本檔 :root 那顆 token
        上方的警告）；它是絕對定位、疊在圖片上的浮層，屬於 --dark-panel 的**正當**用法，
        不是誤用 —— 別因為看到 --dark-panel 就以為這段是壞的。
   ★留著的成本：這段有實際宣告（不是空規則），但沒有任何元素會匹配到，
     瀏覽器只會多解析約 70 行選擇器，對執行期沒有影響。 */
/* ---- 補位：.gallery 家族（Color Admin 原本提供）— StoreSetting/ApiPlatform/create:45-57 ----
   ★ .image / .img / .title / .desc / .rating 全部巢狀在 .gallery 底下。
   CA 的 .gallery .image 是靠 width% + 外層 d-flex 排版（fatboy 有掛 d-flex）。 */
.gallery { margin: -.3121875rem; display: flex; flex-wrap: wrap; }
.gallery .image { width: 100%; display: block; overflow: hidden; padding: .3121875rem; }
@media (min-width: 768px)  { .gallery .image { width: 50%; } }
@media (min-width: 992px)  { .gallery .image { width: 33.33%; } }
@media (min-width: 1200px) { .gallery .image { width: 25%; } }
.gallery .image a { transition: all .2s linear; }
.gallery .image a:hover,
.gallery .image a:focus { opacity: .8; }
.gallery .image img,
.gallery .image .img {
  width: 100%;
  height: 200px;
  border-radius: var(--radius) var(--radius) 0 0;
}
.gallery .image .img {
  background-size: cover;
  background-repeat: no-repeat;
  background-position: center;
}
.gallery .image .image-inner {
  position: relative;
  background: hsl(var(--card));
  border-radius: var(--radius) var(--radius) 0 0;
}
.gallery .image .image-caption {
  position: absolute;
  top: .9375rem;
  left: 0;
  background: hsl(var(--dark-panel) / .85);
  color: hsl(var(--dark-panel-foreground));
  font-weight: 600;
  padding: .3121875rem .9375rem;
  margin: 0;
}
.gallery .image .image-info {
  background: hsl(var(--card));
  padding: .9375rem;
  color: hsl(var(--card-foreground));
  border-radius: 0 0 var(--radius) var(--radius);
}
.gallery .image .image-info .title {
  margin: 0 0 .46875rem;
  line-height: 1.2;
  font-size: .9375rem;
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  color: hsl(var(--card-foreground));
}
.gallery .image .image-info .rating { margin: 0; display: flex; }
.gallery .image .image-info .rating span.star { margin-right: 1px; }
.gallery .image .image-info .rating span.star:before {
  content: "\f005";
  color: hsl(var(--muted-foreground) / .35);
  font-family: "Font Awesome 6 Free", "Font Awesome 6 Pro", FontAwesome !important;
  font-weight: 900;
  font-style: normal;
  font-variant: normal;
  text-rendering: auto;
}
.gallery .image .image-info .rating span.star.active:before { color: hsl(var(--warning)); }
.gallery .image .image-info .desc {
  line-height: 17px;
  font-size: 12px;
  height: 51px;
  overflow: hidden;
  color: hsl(var(--muted-foreground));
}


/* =============================================================================
 * §TODO  待確認（不編造，全部列出）
 * -----------------------------------------------------------------------------
 * TODO-1  .text-theme：CA ＝主題色，本站 remap 後會是品牌金黃 #FFD200，
 *         白底對比不足 → 本檔改成 --dark。若業主要求維持金黃，改成
 *         `color: hsl(var(--primary))` 並確認那 3 處（Seller/bill/purchase:42,86、
 *         Seller/plan/index:66）的背景夠深。
 *
 * TODO-2  負 margin 級距：本檔採 Color Admin 原值（$spacer=0.9375rem），
 *         與交辦單上寫的 Bootstrap 級距（1rem）不同：
 *           mt-n1  CA -.234375rem  vs BS -.25rem
 *           ms-n2  CA -.46875rem   vs BS -.5rem
 *           n5     CA -2.8125rem   vs BS -3rem
 *         若要統一走 BS 級距請明確指示（差 0.25px～3px）。
 *
 * TODO-3  .form-group：app.min.css 內「沒有」裸 .form-group 規則（grep 已確認），
 *         只有 .app-header .navbar-form .form-group 和
 *         .form-horizontal.form-bordered .form-group 兩條，本檔照搬這兩條。
 *         交辦單要求的 `margin-bottom:1rem` 會讓現有 27 處全部多出 16px 間距＝
 *         主動製造版面位移，故「刻意不加」。若確定要 BS3 行為，取消下面註解：
 *           .form-group { margin-bottom: 1rem; }
 *
 * TODO-4  .btn-circle：app.min.css 內「完全查無定義」（grep 0 筆），
 *         圓形效果本來就由 .btn-icon 的 border-radius:28px 提供 →
 *         本檔不補 .btn-circle。若前端另有期待請告知。
 *
 * TODO-5  .pace-top：本檔只補了 CA 的兩條 top 覆寫。pace 本體（.pace / .pace-progress /
 *         .pace-activity）的樣式是否也隨 app.min.css 一起消失、由誰補，需要確認。
 *         若 pace 整組消失，進度條會完全不顯示（不會報錯，屬靜默失效）。
 *
 * TODO-6  以下 class 在交辦清單裡，但 app.min.css 內「沒有會消失的定義」，
 *         或定義只存在於 fatboy 用不到的 context，故本檔不補（避免無中生有）：
 *           .control-label  → CA 只有 .mailbox … .control-label；
 *                             fatboy 用在 _head.blade.php:80 的 `col-8 control-label`，
 *                             拔 CA 後視覺零變化。
 *           .back-btn       → CA 只有 .pos .pos-sidebar .pos-sidebar-header .back-btn（POS 批次）；
 *                             payment/error.blade.php 自帶 inline 樣式。
 *           .btn-row        → CA 只有 .result-list .result-item .result-info .btn-row，
 *                             fatboy 沒有 .result-list；使用處全在 pos/*（POS 批次）。
 *           .divider        → fatboy 只用 BS5 原生 .dropdown-divider（shadcn-theme 已處理）。
 *           .status         → fatboy 無裸 .status（只有 .status-item-check 等自訂名）。
 *           .completed      → 只出現在 nav-wizards-1 內，已於 §B nav-wizards 補齊。
 *           .hidden         → fatboy 只用 BS5 的 .visually-hidden / .overflow-hidden。
 *           .total-price    → CA 無定義；pos/_machine 的用法是純標記（配 col-3）。
 *           .no / .in / .order / .info / .left / .right
 *                           → CA 內只有其他外掛的巢狀定義（datepicker / daterangepicker /
 *                             introjs），與 fatboy 的商品格無關；寫裸選擇器會污染全站，
 *                             故一律不補。若確有畫面破圖，請提供具體頁面與截圖再逐一補巢狀版。
 *           .gallery-group-3→ 全專案（含 CA）查無定義，屬 fatboy 自己的死 class。
 *
 * TODO-7  §D 的 .product 家族：CA 原本全部掛在 `.pos` 底下，本檔拿掉 .pos 前綴以涵蓋
 *         Center/inventoryStats、StoreSetting/ApiPlatform。其中 .product-row 特意寫成
 *         `div.product-row`，避免打到 Center/inventoryStats/index.blade.php:185 的
 *         `<tr class="product-row">`（變 flex 會讓整張表格崩掉）。需視覺驗收確認。
 *
 * TODO-8  §D 的 .widget-chat / .messenger 只提供基底；Chat/index.blade.php 內有大量
 *         帶 !important 的 inline 覆蓋（含寫死的 #d1fae5 / #e8f5e9 / #ee4d2d），
 *         那些 hex 不在本檔範圍，建議另開一輪把 Chat/index 的 inline 顏色收進 token。
 * ============================================================================= */

/* =============================================================================
 * §修正 — 靜態元件驗證頁（kitchen-sink）跑出來的問題，2026-07-26
 * 本節在檔尾，同權重下最後宣告者勝，用來收斂前面章節互相打架的規則。
 * ============================================================================= */

/* ── 1. btn-default（294 處）原本 transparent 底 + transparent 框 + muted 字
   ＝看起來就是一段灰字，跟旁邊的按鈕完全不像同一族。
   肥肥的 btn-default 大量用在「未選中的 tab / 次要 toggle」，必須看得出是可點的。
   給它 shadcn outline 的外觀：透明底 + 實線細框 + 前景字。 */
.btn-default {
  --bs-btn-bg: transparent;
  --bs-btn-border-color: hsl(var(--border));
  --bs-btn-color: hsl(var(--foreground));
  --bs-btn-hover-bg: hsl(var(--accent));
  --bs-btn-hover-border-color: hsl(var(--border));
  --bs-btn-hover-color: hsl(var(--accent-foreground));
  --bs-btn-active-bg: hsl(var(--accent));
  --bs-btn-active-border-color: hsl(var(--border));
  --bs-btn-active-color: hsl(var(--accent-foreground));
}

/* ── 2. 進度條語意色還原
   移植時把 .progress-bar.bg-success 收斂成 --dark，但 uiux.md 明定
   到期／額度進度條的顏色是「資訊」不是裝飾（已過期紅 / 30天內橘 / 正常綠 / 停用灰）。
   收斂掉等於把狀態資訊抹掉 —— 這類語意色不在單色收斂的範圍內。 */
.progress-bar.bg-success { background-color: hsl(var(--success)) !important; }
.progress-bar.bg-warning { background-color: hsl(var(--warning)) !important; }
.progress-bar.bg-info    { background-color: hsl(var(--info)) !important; }
.progress-bar.bg-danger  { background-color: hsl(var(--destructive)) !important; }

/* ── 3. alert-success 底色還原
   前面章節有兩條後來的規則把 .alert-success 壓回中性 muted，導致
   「成功訊息」在亮色是白底、暗色是深底，跟旁邊有顏色的 danger / warning 不成套。
   success 有現成的柔和 token（--success-bg / --success-fg，亮暗各一組），照用。 */
.alert-success {
  background: hsl(var(--success-bg)) !important;
  color: hsl(var(--success-fg)) !important;
  border: 1px solid hsl(var(--success-fg) / .25) !important;
}

/* ── 4. 側欄 menu-badge 沒有內距，看起來就是一個裸數字 */
/* ★2026-08-04 稽核：blade 0 命中 */
.app-sidebar .menu-badge {
  padding: 1px 7px !important;
  font-size: 11px;
  line-height: 1.5;
}

/* =============================================================================
 * §側欄使用者區 — 對齊 shadcn 官方 sidebar 的 user block（2026-07-27 重新設計）
 * shadcn 的做法：一列按鈕式區塊，rounded-lg 方形圓角頭像（不是全圓）、
 * 名稱 semibold sm、次要資訊 xs muted、右側 chevrons-up-down，hover 才浮出 accent 底。
 * 不用 Color Admin 的「封面照 + 大頭像疊圖」語彙。
 * ★修過的 bug：.menu-profile-image 原本沒有尺寸約束，頭像被撐成整個側欄寬
 *   （業主 prod 有封面照時整塊變成一張巨圖把選單全蓋掉）。
 * ============================================================================= */
.app-sidebar .menu-profile {
    padding: 8px;
    margin-bottom: 4px;
    /* ★已被 §可展開篩選 覆蓋（實測生效值：border-bottom=1px solid rgb(228, 228, 231)） */
    border-bottom: 1px solid hsl(var(--sidebar-border));
}
.app-sidebar .menu-profile-link {
    display: flex !important;
    align-items: center;
    gap: 8px;
    padding: 8px;
    border-radius: var(--radius);
    text-decoration: none;
    color: hsl(var(--sidebar-fg-strong));
    transition: background-color .15s ease;
}
.app-sidebar .menu-profile-link:hover { background: hsl(var(--sidebar-accent)); }

/* 封面照區＝Color Admin 的裝飾（內含 bouncingBalls canvas），shadcn 不用 */
.app-sidebar .menu-profile-cover { display: none !important; }

/* 頭像：shadcn sidebar 用 rounded-lg 方形圓角（不是全圓），32px */
.app-sidebar .menu-profile-image {
    flex: 0 0 32px;
    width: 32px;
    height: 32px;
    margin: 0;
    border-radius: var(--radius-md);
    overflow: hidden;
    background: hsl(var(--muted));
    border: 1px solid hsl(var(--sidebar-border));
    position: static;
}
.app-sidebar .menu-profile-image img {
    width: 100%; height: 100%; object-fit: cover; display: block;
    border-radius: var(--radius-md);
}

/* 資訊區：名稱一行、客戶代號一行，長名截斷不推擠版面 */
.app-sidebar .menu-profile-info {
    flex: 1 1 auto;
    min-width: 0;
    font-size: 13px;
    font-weight: 600;
    /* ★已被 §可展開篩選 覆蓋（實測生效值：line-height=17.5px） */
    line-height: 1.3;
    /* ★已被 §可展開篩選 覆蓋（實測生效值：color=rgb(24, 24, 27)） */
    color: hsl(var(--sidebar-fg-strong));
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.app-sidebar .menu-profile-info h6 {
    margin: 1px 0 0;
    font-size: 11px;
    font-weight: 400;
    color: hsl(var(--sidebar-fg));
    display: flex;
    align-items: center;
    gap: 4px;
}
.app-sidebar .menu-profile-info h6 i,
.app-sidebar .menu-profile-info h6 svg { width: 11px; height: 11px; opacity: .6; }

/* 「續約訂閱」＝帳務入口，用 shadcn badge 的尺度（原本是偏大的黃色 pill） */
.app-sidebar .menu-profile-info .btn.btn-yellow {
    background: hsl(var(--primary)) !important;
    border-color: hsl(var(--primary)) !important;
    color: hsl(var(--primary-foreground)) !important;
    font-size: 10px;
    font-weight: 600;
    line-height: 1.5;
    padding: 1px 7px !important;
    margin-top: 5px;
    border-radius: 9999px;
}
.app-sidebar .menu-profile-info .menu-caret { margin-top: 5px; }

/* =============================================================================
 * §補位 2 — 逐頁重構時發現主題缺的規則（2026-07-26）
 * ============================================================================= */

/* 下拉選單內 .form-check 的 hover 回饋（訂單列表的店鋪/狀態/物流篩選，12 處）。
   它不是 .dropdown-item，吃不到 Bootstrap 原生 hover，原本靠 view 裡寫死的 #f8f9fa。 */
.hover-bg-light:hover { background-color: hsl(var(--muted)); }

/* 暫存區左側子選單（Order/Queue）。原本 #f8f9fa 底 + #fff8e1 active + 黃色左側 accent，
   那條黃是刻意的品牌識別 → 用 --primary 的淡版重現，亮暗都成立。 */
/* ★2026-08-04 稽核：blade 0 命中 */
.queue-sub {
    background: hsl(var(--muted));
    color: hsl(var(--foreground));
    border-bottom: 1px solid hsl(var(--border));
}
/* ★2026-08-04 稽核：blade 0 命中 */
.queue-sub:hover { background: hsl(var(--accent)); }
/* ★2026-08-04 稽核：blade 0 命中 */
.queue-sub-active {
    background: hsl(var(--primary) / .14) !important;
    box-shadow: inset 3px 0 0 hsl(var(--primary));
}

/* Color Admin 專有的 .opacity-5（18 處搜尋框放大鏡）。
   Bootstrap 5 只出貨 25/50/75/100，缺這條放大鏡會變全不透明。 */
.opacity-5 { opacity: .5 !important; }

/* 篩選器「已套用」提示：原本全站查無定義（CA 也沒有），補成 shadcn 的強調外框，
   讓使用者看得出這個篩選正在生效。 */
.filter-active {
    border-color: hsl(var(--foreground) / .45) !important;
    box-shadow: inset 0 0 0 1px hsl(var(--foreground) / .25);
}

/* =============================================================================
 * §補位 3 — 逐頁驗收抓到的主題層錯誤（2026-07-26）
 * ============================================================================= */

/* ── 頁面標題字級
   .page-header 掛在 <h1> 上時沒有字級規則 → 吃到 Bootstrap 預設的 40px，
   整個頁首比 shadcn 的尺度大兩號（訂單管理／商品列表等每個列表頁都中）。
   shadcn 的頁標題約 24px/600，副標與計數用小字。

   ★2026-07-27 修：原本第一條選擇器是裸的 `.page-header`，但 x-page-header 與
   全站 244 支 view 的結構是「外層 div.page-header 內再放一顆 h1.page-header」
   （同一個 class 掛兩層）→ 外層容器也被設成 24px，容器內任何沒自訂字級的東西
   （x-slot:inline 的計數、_page_subtitle 的資料來源說明）全部跟著放大。
   merp 沒有這個問題，因為它的頁首容器沒有任何 font-size 規則、標題是 h4。
   這裡改成只作用在「標題元素」上，容器維持繼承。 */
h1.page-header,
h2.page-header,
h3.page-header,
h4.page-header,
h5.page-header,
.page-header h1 {
    font-size: 1.5rem;
    font-weight: 600;
    letter-spacing: -0.02em;
    line-height: 1.3;
}
/* ★同一個 class 掛兩層的第二個副作用：標題元素也吃到容器的
   `display:flex; gap:12px`（見 §23 的 .page-header）。後果有兩個：
     1) `<h1 class="page-header">訂單 <small>SNP1</small></h1>` 的小字被 12px gap 推開，
        不再是自然的行內間距（merp 的 h4 是行內排版）。
     2) 標題被 _shell 搬進頂 bar 後掛 .fb-topbar-title（white-space:nowrap +
        overflow:hidden + text-overflow:ellipsis），但 flex 容器不支援 text-overflow
        → 長標題被硬切、不會出現「…」。
   標題一律還原成一般文字流；版面用的 flex 只留給容器。 */
h1.page-header,
h2.page-header,
h3.page-header,
h4.page-header,
h5.page-header {
    display: block;
    gap: 0;
    border-bottom: 0;
    padding-bottom: 0;
}
.page-header h4 { font-size: 1.125rem; }
.page-header small,
.page-header .text-muted { font-size: .8125rem; font-weight: 400; }



/* fa-2xs（側欄「官網」的外開小標記）— §38 只定義了 fa-xs 起，補上極小號 */
.lucide.fa-2xs, i[data-lucide].fa-2xs { font-size: .625em; }

/* 佔位符對比：--hint 在白底只有 2.4:1（低於 3:1 門檻）。
   shadcn 的 placeholder 用 muted-foreground（4.6:1），select2 也一併對齊。 */
.form-control::placeholder,
.form-select::placeholder,
.select2-container--bootstrap-5 .select2-selection__placeholder,
.select2-container--default .select2-selection__placeholder {
    color: hsl(var(--muted-foreground)) !important;
    opacity: 1;
}

/* =============================================================================
 * §補位 4 — 狀態語意還原與零星缺口（2026-07-26，逐模組驗收後）
 * ============================================================================= */

/* ── 狀態 chip 的語意色還原
   前面的收斂規則把所有 span.btn.rounded-pill 壓成同一個中性灰，
   結果「已完成／未完成」「已付款／未付款」「連線／未連線」在畫面上長得一模一樣，
   語意只剩圖示在撐。ERP 的狀態色是「資訊」不是裝飾 —— 只有「沒有語意 class 的
   純計數 chip」該收斂成灰，帶語意 class 的要保留可辨識的柔和底色。
   （shadcn badge 本身就有 variant，這才是對的做法。） */
span.btn.rounded-pill.btn-success,
span.btn.rounded-pill.btn-green,
span.btn.btn-rounded.rounded-pill.btn-success,
span.btn.btn-rounded.rounded-pill.btn-green {
    background: hsl(var(--success-bg)) !important;
    color: hsl(var(--success-fg)) !important;
    border-color: hsl(var(--success-fg) / .25) !important;
}
span.btn.rounded-pill.btn-warning,
span.btn.btn-rounded.rounded-pill.btn-warning {
    background: hsl(var(--warning-bg)) !important;
    color: hsl(var(--warning-fg)) !important;
    border-color: hsl(var(--warning-fg) / .25) !important;
}
span.btn.rounded-pill.btn-danger,
span.btn.btn-rounded.rounded-pill.btn-danger {
    background: hsl(var(--destructive) / .12) !important;
    color: hsl(var(--destructive-fg)) !important;
    border-color: hsl(var(--destructive) / .3) !important;
}
span.btn.rounded-pill.btn-info,
span.btn.btn-rounded.rounded-pill.btn-info {
    background: hsl(var(--info) / .12) !important;
    color: hsl(var(--info)) !important;
    border-color: hsl(var(--info) / .3) !important;
}

/* ── .bg-danger 用在 card-header 等非 badge/btn 元素上時會落回 Bootstrap 原生 #dc3545
   （非 token，暗色不翻轉、白名單稽核會判違規）→ 接到 destructive token。
   真警示該紅就紅，配 text-white 對比足夠。 */
.bg-danger:not(.badge):not(.btn):not(.progress-bar) {
    background-color: hsl(var(--destructive)) !important;
    color: hsl(var(--destructive-foreground)) !important;
}

/* ── SVG presentation attribute 不吃 var()：補貨頁的 90 天銷量折線圖
   stroke 寫死近黑，暗色模式下在深底上完全看不見。用 CSS 蓋掉 attribute（CSS 優先）。 */
#specDetailModal polyline,
#specDetailModal path[stroke] { stroke: hsl(var(--foreground)); }
#specDetailModal rect[fill="#f1f3f5"] { fill: hsl(var(--muted)); }
#specDetailModal rect[fill="#ced4da"] { fill: hsl(var(--muted-foreground) / .5); }

/* 三個全站沒有定義、view 卻在用的 class（代理逐模組回報，bootstrap/vendor/theme 三邊都查無） */
/* ★2026-08-04 稽核：blade 0 命中 */
.text-xs { font-size: .75rem !important; }
/* ★2026-08-04 稽核：blade 0 命中 */
.text-opacity-20 { --bs-text-opacity: .2; opacity: .2; }
.animate-bounce { animation: fbBounce 1s infinite; }
@keyframes fbBounce {
    0%, 100% { transform: translateY(-12%); animation-timing-function: cubic-bezier(.8, 0, 1, 1); }
    50%      { transform: none;             animation-timing-function: cubic-bezier(0, 0, .2, 1); }
}


/* ── .btn-yellow ＝品牌黃按鈕（不收斂）
   它的語意就是「品牌主按鈕」（登入、續約訂閱），被收斂成中性灰會失去識別、
   業主也直接抱怨過登入鈕變淺灰。等同 .btn-primary。 */
.btn-yellow {
    --bs-btn-bg: hsl(var(--primary));
    --bs-btn-border-color: hsl(var(--primary));
    --bs-btn-color: hsl(var(--primary-foreground));
    --bs-btn-hover-bg: hsl(var(--primary-light));
    --bs-btn-hover-border-color: hsl(var(--primary-light));
    --bs-btn-hover-color: hsl(var(--primary-foreground));
    --bs-btn-active-bg: hsl(var(--primary-dark));
    --bs-btn-active-border-color: hsl(var(--primary-dark));
    --bs-btn-active-color: hsl(var(--primary-foreground));
    --bs-btn-disabled-bg: hsl(var(--primary));
    --bs-btn-disabled-border-color: hsl(var(--primary));
    --bs-btn-disabled-color: hsl(var(--primary-foreground));
}
/* 黃底上寫死的 text-white 會整顆隱形 → 打回黑字 */
.btn-yellow.text-white, .btn-primary.text-white { color: hsl(var(--primary-foreground)) !important; }

/* =============================================================================
 * §補位 5 — 依 shadcn 原生設計補的兩個元件（2026-07-26）
 * ============================================================================= */

/* ── 空狀態（shadcn empty state）
   shadcn 的空狀態＝「小尺寸 muted 圖示 + 標題（+ 說明）」置中，不是巨大插圖。
   原本 fa-10x 的巨大幽靈圖示不是 shadcn 的語彙，且主題只定義到 fa-5x。 */
.fb-empty-icon {
    width: 40px !important;
    height: 40px !important;
    color: hsl(var(--muted-foreground) / .55);
    stroke-width: 1.5;
    display: inline-block;
}
.fb-empty-icon + h4,
.fb-empty-icon + h5,
.fb-empty-icon + h6,
.fb-empty-icon + p {
    margin-top: 10px;
    font-size: .875rem;
    font-weight: 500;
    color: hsl(var(--muted-foreground));
}

/* ── 狀態標記＝shadcn Badge 外觀
   全站狀態 chip 的既有 markup 是 span.btn.btn-<語意>（有的帶 rounded-pill、有的沒有），
   不帶 rounded-pill 的會漏掉 §補位4 的語意色而被壓成灰。
   與其逐檔補 class，直接讓「任何帶語意色的非互動 span.btn」都長成 shadcn badge：
   小字、圓角、柔和語意底。互動元素（a / button）不受影響。 */
span.btn[class*="btn-"]:not(.btn-dark):not(.btn-light):not(.btn-primary) {
    /* ★ 2026-07-28：11px → 12px。這條規則的目的就是「長成 shadcn Badge」，
       而 .badge 本身已在同日校正為固定 12px（shadcn Badge = text-xs）。
       同一個視覺角色卻有兩種字級（真 badge 12px、假 badge 11px）＝ SSOT 破口。
       實測受影響最多的是 /inventory/box-specs 166 顆、/shopee/product/online 319 顆。 */
    font-size: 12px;
    font-weight: 500;
    line-height: 1.45;
    padding: 2px 8px;
    border-radius: 9999px;
    border-width: 1px;
    vertical-align: middle;
}
span.btn.btn-success, span.btn.btn-green {
    background: hsl(var(--success-bg)) !important;
    color: hsl(var(--success-fg)) !important;
    border-color: hsl(var(--success-fg) / .25) !important;
}
span.btn.btn-warning, span.btn.btn-orange {
    background: hsl(var(--warning-bg)) !important;
    color: hsl(var(--warning-fg)) !important;
    border-color: hsl(var(--warning-fg) / .25) !important;
}
span.btn.btn-danger, span.btn.btn-red {
    background: hsl(var(--destructive) / .12) !important;
    color: hsl(var(--destructive-fg)) !important;
    border-color: hsl(var(--destructive) / .3) !important;
}
span.btn.btn-info {
    background: hsl(var(--info) / .12) !important;
    color: hsl(var(--info)) !important;
    border-color: hsl(var(--info) / .3) !important;
}

/* =============================================================================
 * §補位 6 — 逐模組驗收後的主題層缺口（2026-07-26）
 * ============================================================================= */

/* ── 「永遠亮色的島」：data-bs-theme="light" 區塊
   AI 工具的 hero 用淺色底圖 + data-bs-theme="light"，但那是 Bootstrap 自己的機制，
   對我們的 html.dark token 系統無效 → 暗色模式下裡面的文字用的是翻轉後的近白，
   壓在淺色底圖上完全看不見。這裡把該作用域的 token 強制鎖回亮色值。 */
[data-bs-theme="light"] {
    --background: 0 0% 98%;   /* 同 :root。原註解逐字保留：「同 :root，2026-08-07 一併改回 shadcn 官方純白（原 0 0% 98%）」
                                 ★2026-08-07 下午又改回 0 0% 98%（業主：OS 版比較好看／web 版可以學習）。
                                   完整理由與 OS 實測值寫在 :root 那一顆旁邊，這裡只跟著它走，不要讓兩處分岔。 */
    --foreground: 0 0% 0%;              /* §S-72 同步 :root */
    --card: 0 0% 100%;
    --card-foreground: 0 0% 0%;         /* §S-72 同步 :root */
    --muted: 0 0% 96.1%;                /* §S-72 同步 :root */
    --muted-foreground: 0 0% 45.1%;     /* §S-72 同步 :root */
    --border: 0 0% 89.8%;               /* §S-72 同步 :root */
    --dark: 0 0% 9%;                    /* #171717 ← §S-74 achromatic，與 :root 那顆同步（原 240 5.9% 10% #18181B，色差 4/255） */
    --dark-foreground: 0 0% 98%;        /* 原本就是 achromatic，§S-74 未動 */
}
[data-bs-theme="light"] .text-white,
[data-bs-theme="light"] .text-black,
/* ★§S-74：原本寫死 `hsl(240 10% 3.9%)`（#09090B zinc-950）—— 這是全檔**唯一一條還在跑的
   hue-240 硬寫色**（其餘 hsl(240 …) 都在註解裡的「實測生效值」字串中）。
   §S-72 已把亮色 --foreground 換成官方純黑 `0 0% 0%`，所以這裡留著 zinc-950 就是
   「同一個畫面上兩種近黑」。改吃 var(--foreground)：本區塊自己在上面就把 --foreground
   鎖成 0 0% 0%，所以解析結果是確定的 #000000，同時把硬寫色收回 SSOT。
   色差：#09090B → #000000，最大 11/255（藍）；白底對比 20.35 → 21.00。 */
[data-bs-theme="light"] .text-dark { color: hsl(var(--foreground)) !important; }
[data-bs-theme="light"] .bg-white { background-color: hsl(0 0% 100%) !important; }

/* ── badge 語意變體：原本 info/dark/primary 在收斂後全落到同一個近黑，
   行事曆的「任務 / 行程 / 貨櫃」三種標記變得完全無法區分（圖例也對不上）。
   給 badge 一組可辨識的柔和變體（shadcn 的 badge secondary/outline 精神）。 */
.badge.bg-info    { background: hsl(var(--info) / .15) !important; color: hsl(var(--info)) !important; border: 1px solid hsl(var(--info) / .3); }
.badge.bg-success { background: hsl(var(--success-bg)) !important; color: hsl(var(--success-fg)) !important; border: 1px solid hsl(var(--success-fg) / .25); }
.badge.bg-warning { background: hsl(var(--warning-bg)) !important; color: hsl(var(--warning-fg)) !important; border: 1px solid hsl(var(--warning-fg) / .25); }
/* ★★ 2026-07-27 業主指正「右上角這按鈕不是原本 shadcn 的樣子」：
   通知數 103 是 .badge.bg-danger，被這裡改成 12% 淡紅底 + 紅字。
   shadcn 的 Badge destructive 是「實心 --destructive 底 + --destructive-foreground 字」，
   淡色調那是 soft badge，不是 shadcn 的預設樣貌。
   上面 info/success/warning 保留柔和變體是有理由的（收斂後三者會落到同一個近黑、
   行事曆的任務/行程/貨櫃標記分不出來）；danger 沒有這個問題，本來就夠獨特。
   ★同時解掉一個 SSOT 破口：.badge.bg-danger 在本檔被定義了三次
   （§Badge 658 行、§28 1643 行、這裡），前兩處都是實心、只有這裡改成淡色，
   而 CSS 後者勝 → 前面兩處等於白寫。現在統一成實心。 */
.badge.bg-danger  { background: hsl(var(--destructive)) !important; color: hsl(var(--destructive-foreground)) !important; border-color: transparent; }
.badge.bg-dark    { background: hsl(var(--dark)) !important; color: hsl(var(--dark-foreground)) !important; }
.badge.bg-secondary { background: hsl(var(--secondary)) !important; color: hsl(var(--secondary-foreground)) !important; }

/* ── .border-primary（拖曳 hover 佔位框）theme 沒接管 → 會露出 Bootstrap 原生藍 */
.border-primary { border-color: hsl(var(--foreground) / .5) !important; }
.border-success { border-color: hsl(var(--success) / .5) !important; }
.border-danger  { border-color: hsl(var(--destructive) / .5) !important; }
/* ★2026-08-04 稽核：blade 0 命中 */
.border-info    { border-color: hsl(var(--info) / .5) !important; }

/* =============================================================================
 * §POS 收銀 — 取代 Color Admin 的 .pos-* 全套（2026-07-26，全站最後一批）
 * 版面行為（尺寸/flex/full-height/RWD 抽屜）與 CA 原值等價，顏色全部 token 化。
 * 合併自代理產出 pos-shadcn.css；品牌 logo 的 mask 已歸位到 brands.css。
 * ============================================================================= */
/* =============================================================================
 * §POS — 收銀 / 揀包出貨全螢幕版面（取代 Color Admin 的 .pos-* 15 個 class）
 * -----------------------------------------------------------------------------
 * 使用處：resources/views/layouts/pos.blade.php + resources/views/pos/*.blade.php
 *   pos/package、pos/machine、pos/machine_backup（實際上線）
 *   pos/checkout、pos/darka、pos/_machine、pos/_package（樣板 / 展示頁）
 *
 * ★ 這一段補起來，layouts/pos.blade.php 才能拔掉 legacyColorAdmin 旗標。
 *   拔掉前 POS 是全站最後一個還在載 app.min.css / app.min.js 的頁面。
 *
 * ★ 版面行為（尺寸 / flex / position / full-height / RWD 抽屜）與 Color Admin
 *   完全等價，原值逐條寫在註解裡；只有「顏色」換成 shadcn token。
 *   斷點沿用 Color Admin：992px（rail↔頂 bar、側欄↔抽屜）、768px、576px。
 *
 * ★ POS 是現場作業畫面：觸控目標一律 ≥ 40px、字不縮小、狀態色是資訊不可收斂成灰。
 *
 * ★ 外殼三個 class（.app-content-full-height / .app-without-sidebar /
 *   .app-without-header）本檔已在「§補位：外殼版面」處理，這裡不重複。
 *
 * ★ .product 家族（.product / .product-row / .img / .title / .desc / .price）
 *   已由本檔 §D 接管且是巢狀選擇器，POS 直接沿用，這裡不再重寫短名。
 * ============================================================================= */

/* ★★ 必修：POS 外殼的 #content 讓位量歸零
   §22 的 `#content.app-content{margin-left:var(--fb-sidebar-w);padding:…}` 帶 id
   （特異度 1,1,0），會贏過 `.app-without-sidebar .app-content{margin:0}`（0,2,0）
   → POS 全螢幕版面左邊硬留 220px 空白、右邊被擠掉，實測 1440px 下 .pos 只剩 1220px。
   這裡用同樣帶 id 的選擇器補回（1,2,0）。
   ★ 只有 layouts/pos.blade.php 掛 .app-without-sidebar / .app-without-header，
     default / empty / app 三個外殼完全不受影響。 */
.app-without-sidebar #content.app-content,
.app-without-header #content.app-content {
  margin: 0;
  padding: 0;
  min-height: 0;
}

/* ★★ 必修：POS 的 <a> 不吃全站「超連結底線」規則
   §5 的 `#content a:not(.btn):not(.dropdown-item)…{color:--link;font-weight:500;
   text-decoration:underline}`（特異度 1,5,1）會打到 POS 內所有非按鈕連結：
     - .pos-menu .logo 的 MYPOS ／ 時鐘   → 多一條底線
     - a.product 商品格（pos/checkout）    → 整張卡被畫底線
     - .pos-mobile-sidebar-toggler 浮動鈕  → color 被改成 --link（＝--foreground），
       實測在深底 FAB 上變成 rgb(9,9,11) 對 rgb(24,24,27)，手機上直接看不見字
   Color Admin 原本這幾處都是 text-decoration:none + 自帶顏色，這裡還原。
   ★ 特異度必須 ≥ 主題那條的 (1,5,1)，:not() 鏈原封照抄一份才蓋得過；
     而且一定要保留 :not(.nav-link)，否則 color:inherit 會反過來洗掉
     .pos-menu .nav-link.active 的白字（實測掉到對比 1.12＝全黑吃全黑）。 */
#content.app-content .pos a:not(.btn):not(.dropdown-item):not(.nav-link):not(.fb-tab):not(.page-link),
#content.app-content a.pos-mobile-sidebar-toggler:not(.btn):not(.dropdown-item):not(.nav-link):not(.fb-tab):not(.page-link) {
  /* ★已被 L4642 覆蓋（實測生效值：color=rgb(250, 250, 250)） */
  color: inherit;
  font-weight: inherit;
  text-decoration: none;
}
#content.app-content a.pos-mobile-sidebar-toggler:not(.btn):not(.dropdown-item):not(.nav-link):not(.fb-tab):not(.page-link) {
  color: hsl(var(--dark-foreground));
}
#content.app-content a.pos-mobile-sidebar-toggler:not(.btn):not(.dropdown-item):not(.nav-link):not(.fb-tab):not(.page-link):hover {
  color: hsl(var(--background));
}

/* ---- 補位：.bg-default（Color Admin 遺留，全站 2 處，其一在 pos/_header）----
   CA 原值：background-color: rgba(var(--bs-default-rgb)) = #e9ecef（淺灰）
   拔掉 app.min.css 後變 no-op → POS 左側導覽 rail 失去底色，與內容區糊在一起。 */
.bg-default { background-color: hsl(var(--muted)) !important; }



/* -----------------------------------------------------------------------------
 * §POS-1  .pos 版面根容器
 * 取代 CA 的 .pos / .pos-with-menu / .pos-with-sidebar
 * CA 原值：
 *   .pos{height:100%}
 *   @media(min-width:992px){ .pos{height:100%;display:flex} }
 *   @media(min-width:992px){ .pos.pos-with-menu{padding-left:10rem} }
 *   @media(max-width:991.98px){ .pos.pos-with-menu{padding-top:100px} }
 *   @media(min-width:992px){ .pos.pos-with-sidebar{padding-right:22.5rem} }
 * CA 另外定義了一組 --bs-pos-sidebar-* 變數（白底 / #2d353c 深頭 / #495057 深腳），
 * 全部改由 shadcn token 表達，不再落地成獨立變數。
 * --------------------------------------------------------------------------- */
.pos {
  height: 100%;
  color: hsl(var(--foreground));
}
@media (min-width: 992px) {
  /* rail + 內容 + 側欄三欄並排；側欄本身是 fixed，靠 padding-right 讓位 */
  .pos { display: flex; }
  .pos.pos-with-menu    { padding-left: 10rem; }    /* = .pos-menu 寬 160px */
  .pos.pos-with-sidebar { padding-right: 22.5rem; } /* = .pos-sidebar 佔位 360px */
}
@media (max-width: 991.98px) {
  /* rail 變成頂部橫向 bar（logo 列 + tabs 列），內容往下推。
     ★ CA 原值 100px 是配它 37px 高的頁籤算的；本檔把頁籤觸控目標拉到 44px
       （POS 現場要求），bar 實高變 ~104px → padding-top 同步加到 112px，
       不加會讓 bar 蓋住內容第一行。 */
  .pos.pos-with-menu { padding-top: 112px; }
}

/* -----------------------------------------------------------------------------
 * §POS-2  .pos-menu 左側導覽 rail（手機＝頂部橫向 bar）
 * 取代 CA 的 .pos .pos-menu 整組
 * CA 原值：
 *   .pos .pos-menu{position:fixed;top:0;z-index:10}
 *   @media(min-width:992px){ display:flex;width:10rem;flex-direction:column;
 *                            bottom:0;left:0;padding-left:.9375rem }
 *   @media(max-width:991.98px){ left:0;right:0;bottom:auto;
 *                               border-bottom:1px solid var(--bs-border-color) }
 * CA 手機版寫 background:var(--bs-pos-bg)，該變數從未定義（等於透明）→ 這裡明確給底色。
 * --------------------------------------------------------------------------- */
.pos .pos-menu {
  position: fixed;
  top: 0;
  z-index: 10;
  background: hsl(var(--muted));
}
@media (min-width: 992px) {
  .pos .pos-menu {
    display: flex;
    width: 10rem;              /* 160px，與 .pos-with-menu 的 padding-left 對齊 */
    flex-direction: column;
    bottom: 0;
    left: 0;
    padding-left: .9375rem;    /* 15px */
    border-right: 1px solid hsl(var(--border));
  }
}
@media (max-width: 991.98px) {
  .pos .pos-menu {
    left: 0;
    right: 0;
    bottom: auto;
    border-bottom: 1px solid hsl(var(--border));
  }
}

/* logo 區（CA：desktop padding .9375rem / 手機 .46875rem .9375rem） */
.pos .pos-menu .logo {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: .9375rem;
}
.pos .pos-menu .logo a {
  display: block;
  text-decoration: none;
  color: hsl(var(--foreground));
}
/* ★2026-08-04 稽核：blade 0 命中 */
.pos .pos-menu .logo .logo-img {
  text-align: center;
  height: 45px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 2.25rem;
}
.pos .pos-menu .logo .logo-text {
  font-size: .875rem;
  font-weight: 600;
  letter-spacing: 1px;
  margin-top: .234375rem;
  color: hsl(var(--foreground));
}
.pos .pos-menu .logo img { max-width: 100%; max-height: 100%; }
@media (max-width: 991.98px) {
  .pos .pos-menu .logo     { display: flex; padding: .46875rem .9375rem; }
  .pos .pos-menu .logo a   { display: flex; }
  /* ★2026-08-04 稽核：blade 0 命中 */
  .pos .pos-menu .logo .logo-img { height: 30px; font-size: 1.125rem; margin-right: .46875rem; }
}

/* nav-container：PerfectScrollbar 掛載點（data-scrollbar + data-height="100%"）
   ★ CA 原值 @media(min-width:992px){overflow:hidden;padding:0}。
     flex:1 讓它吃滿 rail 剩餘高度 —— 沒有明確高度 PS 會量到 0，內容捲不到也看不到。 */
.pos .pos-menu .nav-container { flex: 1; min-height: 0; }
@media (min-width: 992px) {
  .pos .pos-menu .nav-container { overflow: hidden; padding: 0; }
}

/* rail 上的頁籤（CA：.nav.nav-tabs 被完全改寫成垂直清單） */
.pos .pos-menu .nav.nav-tabs {
  margin: 0;
  padding: 0;
  display: block;
  border: 0;
}
.pos .pos-menu .nav.nav-tabs .nav-item { padding: .328125rem .9375rem; }
.pos .pos-menu .nav.nav-tabs .nav-item .nav-link {
  position: relative;
  /* CA 原值 padding:.703125rem + text-align:center。改 flex 置中是為了能給 min-height，
     現場觸控目標必須 ≥ 40px；左右內距收窄以免「自動包裝機（備用）」在 160px rail 內爆行。 */
  display: flex;
  align-items: center;
  justify-content: center;
  /* ★已被 §S-37POS 覆蓋（實測生效值：min-height=56px） */
  min-height: 44px;
  /* ★已被 §S-37POS 覆蓋（實測生效值：padding=8px 6px） */
  padding: .625rem .5rem;
  white-space: nowrap;
  text-align: center;
  font-size: .75rem;               /* 12px，rail 只有 160px，放大會撐爆 */
  font-weight: 500;
  border: 1px solid transparent;
  border-radius: var(--radius);
  /* ★已被 §S-37POS 覆蓋（實測生效值：background=transparent） */
  background: hsl(var(--card));
  /* ★已被 §S-37POS 覆蓋（實測生效值：color=rgb(105, 105, 114)） */
  color: hsl(var(--card-foreground));
  transition: background-color .15s linear, color .15s linear;
}
.pos .pos-menu .nav.nav-tabs .nav-item .nav-link:hover,
.pos .pos-menu .nav.nav-tabs .nav-item .nav-link:focus {
  background: hsl(var(--accent));
  color: hsl(var(--accent-foreground));
  border-color: hsl(var(--border));
}
/* 目前頁：zinc 近黑實心（＝方案書「一般主要動作」層，黃色留給新增 / 儲存） */
.pos .pos-menu .nav.nav-tabs .nav-item .nav-link.active {
  /* ★已被 §S-37POS 覆蓋（實測生效值：background=hsl(var(--primary))） */
  background: hsl(var(--dark));
  /* ★已被 §S-37POS 覆蓋（實測生效值：color=rgb(26, 26, 26)） */
  color: hsl(var(--dark-foreground));
  /* ★已被 §S-37POS 覆蓋（實測生效值：border-color=rgb(255, 210, 0)） */
  border-color: hsl(var(--dark));
}
/* CA 的 .nav-icon / .nav-text 兩個子 span（肥肥目前直接用 <i> + 文字，保留相容） */
/* ★2026-08-04 稽核：blade 0 命中 */
.pos .pos-menu .nav.nav-tabs .nav-item .nav-link .nav-icon {
  display: block;
  margin: 0 auto;
  font-size: 24px;
  color: hsl(var(--muted-foreground));
}
.pos .pos-menu .nav.nav-tabs .nav-item .nav-link .nav-text { font-weight: 600; }
/* ★2026-08-04 稽核：blade 0 命中 */
.pos .pos-menu .nav.nav-tabs .nav-item .nav-link:hover .nav-icon { color: hsl(var(--foreground)); }
/* ★2026-08-04 稽核：blade 0 命中 */
.pos .pos-menu .nav.nav-tabs .nav-item .nav-link.active .nav-icon { color: inherit; }
.pos .pos-menu .nav.nav-tabs .nav-item .nav-link i,
.pos .pos-menu .nav.nav-tabs .nav-item .nav-link .lucide { color: inherit; }

@media (max-width: 991.98px) {
  /* 手機：垂直清單改成橫向可捲頁籤。★ overflow-x 必須留在 <ul> 上，
     外層 data-scrollbar 容器會被 PerfectScrollbar 加上 overflow:hidden。 */
  .pos .pos-menu .nav.nav-tabs {
    display: flex;
    flex-wrap: nowrap;
    padding: 0 .9375rem;
    overflow-x: auto;
    overflow-y: hidden;
    border-top: 1px solid hsl(var(--border));
    scrollbar-width: thin;
  }
  .pos .pos-menu .nav.nav-tabs .nav-item { padding: .46875rem 0; }
  .pos .pos-menu .nav.nav-tabs .nav-item + .nav-item { margin-left: .46875rem; }
  .pos .pos-menu .nav.nav-tabs .nav-item .nav-link {
    justify-content: flex-start;
    /* ★已被 §S-37POS 覆蓋（實測生效值：padding=8px 6px） */
    padding: .46875rem .9375rem;
    border-radius: var(--radius-md);
  }
  /* ★2026-08-04 稽核：blade 0 命中 */
  .pos .pos-menu .nav.nav-tabs .nav-item .nav-link .nav-icon {
    font-size: .9rem;
    margin: 0 .46875rem 0 0;
  }
}

/* -----------------------------------------------------------------------------
 * §POS-3  .pos-content / .pos-content-container 主內容區
 * CA 原值：.pos .pos-content{flex:1}
 *          .pos .pos-content .pos-content-container{padding:.9375rem}
 * ★ .pos-content-container 掛 data-scrollbar + data-height="100%"，
 *   min-height:0 是 flex 子項能真的被壓縮 / 產生捲軸的前提。
 * --------------------------------------------------------------------------- */
.pos .pos-content { flex: 1; min-width: 0; min-height: 0; }
.pos .pos-content .pos-content-container { padding: .9375rem; }

/* -----------------------------------------------------------------------------
 * §POS-4  .pos-sidebar 右側作業側欄（手機＝全螢幕抽屜）
 * CA 原值：
 *   .pos .pos-sidebar{width:calc(22.5rem - 1.875rem);height:100%;display:flex;
 *                     flex-direction:column;flex:0 0 22.5rem;background:#FFFFFF}
 *   @media(min-width:992px){ position:fixed;top:0;bottom:0;right:0;
 *                            height:calc(100% - 1.875rem);margin:.9375rem;border-radius:6px }
 *   @media(max-width:991.98px){ position:fixed;inset:0;width:auto;z-index:1021;display:none }
 * 顏色：白底 → --card；6px 圓角 → var(--radius)；補 1px --border（CA 靠陰影分層）。
 * --------------------------------------------------------------------------- */
.pos .pos-sidebar {
  width: calc(22.5rem - 1.875rem);   /* 360px - 30px = 330px */
  height: 100%;
  display: flex;
  flex-direction: column;
  flex: 0 0 22.5rem;
  background: hsl(var(--card));
  color: hsl(var(--card-foreground));
  overflow: hidden;                   /* 讓 header / footer 的圓角切得乾淨 */
}
@media (min-width: 992px) {
  .pos .pos-sidebar {
    position: fixed;
    top: 0;
    bottom: 0;
    right: 0;
    height: calc(100% - 1.875rem);    /* 上下各留 15px */
    margin: .9375rem;
    border: 1px solid hsl(var(--border));
    border-radius: var(--radius);
  }
}
@media (max-width: 991.98px) {
  /* 手機抽屜：預設整片隱藏，靠 .pos-sidebar-mobile-toggled / .pos-mobile-sidebar-toggled 打開 */
  .pos .pos-sidebar {
    position: fixed;
    top: 0; right: 0; bottom: 0; left: 0;
    width: auto;
    z-index: 1021;
    display: none;
    border: 0;
    border-radius: 0;
  }
  /* ★ 兩個 toggle class 都要收：
     pos/machine・machine_backup・package・_machine 用 .pos-sidebar-mobile-toggled（CA 原生）
     pos/checkout・darka・_package        用 .pos-mobile-sidebar-toggled（CA 沒定義，本來就打不開）
     一次補齊，兩種寫法都能開。 */
  .pos.pos-sidebar-mobile-toggled .pos-sidebar,
  .pos.pos-mobile-sidebar-toggled .pos-sidebar { display: flex; }
}

/* ── 側欄頁首：恆深資訊面板（收銀台 / 員工識別列，現場一眼可辨） ──
   CA 原值：padding:.703125rem .9375rem;background:#2d353c;color:#FFFFFF;
            position:relative;display:flex;align-items:center
            @media(min-width:992px){border-radius:6px 6px 0 0} */
.pos .pos-sidebar .pos-sidebar-header {
  padding: .703125rem .9375rem;
  min-height: 48px;                       /* 現場觸控 / 可讀性下限 */
  background: hsl(var(--dark-panel));
  color: hsl(var(--dark-panel-foreground));
  /* ★暗色模式下 --card 也是 zinc-900，跟 --dark-panel 同色 → 頁首會跟內容糊成一片，
     所以一定要留這條下框線，亮暗兩邊都靠它分層。 */
  border-bottom: 1px solid hsl(var(--dark-panel-foreground) / .14);
  position: relative;
  display: flex;
  align-items: center;
}
.pos .pos-sidebar .pos-sidebar-header .title {
  font-size: .9375rem;                    /* CA 14px → 15px，現場字要夠大 */
  font-weight: 600;
  flex: 1;
  color: inherit;
}
/* 頁首徽章（訂單 / 登入數）＝品牌黃底恆黑字 */
.pos .pos-sidebar .pos-sidebar-header .order {
  display: block;
  font-size: .8125rem;
  font-weight: 600;
  background: hsl(var(--primary));
  color: hsl(var(--primary-foreground));
  padding: 2px 8px;
  border-radius: var(--radius-sm);
}
.pos .pos-sidebar .pos-sidebar-header .icon img { height: 30px; }
.pos .pos-sidebar .pos-sidebar-header .icon i   { font-size: 1.125rem; margin: -10px 0; display: block; opacity: .5; }
.pos .pos-sidebar .pos-sidebar-header .icon + .title { padding-left: .703125rem; }
/* 返回鈕：只在手機抽屜出現（CA 原值 44×44，負 margin 貼齊 header 邊） */
.pos .pos-sidebar .pos-sidebar-header .back-btn {
  width: 44px;
  height: 44px;
  border: none;
  background: hsl(var(--dark-panel-foreground) / .14);
  display: none;
  margin: -.703125rem .9375rem -.703125rem -.9375rem;
}
.pos .pos-sidebar .pos-sidebar-header .back-btn .btn {
  color: hsl(var(--dark-panel-foreground));
  padding: 0;
  width: 100%;
  height: 100%;
  border-radius: 0;
}
.pos .pos-sidebar .pos-sidebar-header .back-btn .btn:hover {
  background: hsl(var(--dark-panel-foreground) / .12);
  color: hsl(var(--dark-panel-foreground));
}
.pos .pos-sidebar .pos-sidebar-header .back-btn .btn svg { width: 20px; height: 20px; }
@media (max-width: 991.98px) {
  .pos .pos-sidebar .pos-sidebar-header .back-btn { display: block; }
}

/* ── 側欄頁籤（CA：#e9ecef 未選 / #FFFFFF 選中，硬邊無圓角） ──
   CA 原值：.nav-link{padding:12px 15px;font-size:13px;border:none;border-radius:0} */
.pos .pos-sidebar .pos-sidebar-nav .nav.nav-tabs {
  /* ★已被 §S-32 覆蓋（實測生效值：padding=4px） */
  padding: 0;
  /* ★已被 §S-32 覆蓋（實測生效值：border-bottom=0px none rgb(9, 9, 11)） */
  border-bottom: 1px solid hsl(var(--border));
}
.pos .pos-sidebar .pos-sidebar-nav .nav.nav-tabs .nav-item { padding: 0; }
.pos .pos-sidebar .pos-sidebar-nav .nav.nav-tabs .nav-item .nav-link {
  /* ★已被 §S-32 覆蓋（實測生效值：padding=12px 15px） */
  padding: 12px 15px;
  /* ★已被 §S-32 覆蓋（實測生效值：min-height=44px） */
  min-height: 44px;                       /* 現場觸控目標 */
  font-size: .875rem;                     /* CA 13px → 14px */
  font-weight: 500;
  background: hsl(var(--muted));
  color: hsl(var(--muted-foreground));
  /* ★已被 §S-94 覆蓋（實測生效值：border=1px solid transparent）
     ★2026-08-10 更正：原註解為「已被 §S-32 覆蓋（實測生效值：border=0px none rgb(9, 9, 11)）」，
       那是 §S-94 加入前的事實。§S-94（L19821）給 .nav-link 補了 1px 透明邊，
       以免 .active 加邊框後選中片比未選中片胖 2px、切換時整排跳動。
       ⇒ 註解記錄的是「寫下那一刻」的事實，不是現況；勝出者變了註解就要跟著改。 */
  border: none;
  /* ★已被 §S-32 覆蓋（實測生效值：border-radius=6px） */
  border-radius: 0;
}
.pos .pos-sidebar .pos-sidebar-nav .nav.nav-tabs .nav-item .nav-link:hover {
  background: hsl(var(--accent));
  color: hsl(var(--foreground));
}
.pos .pos-sidebar .pos-sidebar-nav .nav.nav-tabs .nav-item .nav-link.active {
  /* ★已被 §S-32 覆蓋（實測生效值：background=hsl(var(--background))） */
  background: hsl(var(--card));
  /* ★已被 §S-32 覆蓋（實測生效值：color=rgb(9, 9, 11)） */
  color: hsl(var(--card-foreground));
  /* ★已被 §S-32 覆蓋（實測生效值：font-weight=600） */
  font-weight: 600;
  /* ★已被 §S-32 覆蓋（實測生效值：box-shadow=rgba(0, 0, 0, 0.05) 0px 1px 2px 0px） */
  box-shadow: inset 0 -2px 0 0 hsl(var(--foreground));   /* shadcn 頁籤底線 */
}

/* ── 側欄主體：捲動區（data-scrollbar + data-height="100%"）──
   CA 原值：padding:0;margin:0;overflow:scroll;flex:1
   ★ min-height:0 沒給，flex 子項不會縮 → PerfectScrollbar 量到的高度是內容高，
     捲軸永遠不出現、下半截被 header/footer 蓋住。 */
.pos .pos-sidebar .pos-sidebar-body {
  padding: 0;
  margin: 0;
  overflow: auto;
  flex: 1;
  min-height: 0;
  background: hsl(var(--card));
}

/* ── 側欄頁腳：總計 + 主要動作區 ──
   CA 原值：background:#495057;color:#f8f9fa;position:relative;overflow:hidden;
            padding:.9375rem  @media(min-width:992px){border-radius:0 0 6px 6px}
   ★ 改成 --muted 淺底：頁腳放的是 btn-dark（登出 / 暫停）與品牌黃結帳鈕，
     維持 CA 的深底會讓 zinc 近黑按鈕整個消失。深色只留給頁首那一條識別列。 */
.pos .pos-sidebar .pos-sidebar-footer {
  background: hsl(var(--muted));
  color: hsl(var(--foreground));
  border-top: 1px solid hsl(var(--border));
  position: relative;
  overflow: hidden;
  padding: .9375rem;
  font-size: .9375rem;                    /* 現場字要夠大 */
}
.pos .pos-sidebar .pos-sidebar-footer .btn { min-height: 40px; }   /* 觸控目標 */
/* .btn-row：CA 的通用 class（原本來自 .result-list），POS 頁腳三顆一排 */
.pos .btn-row { display: flex; flex-wrap: wrap; gap: .375rem; }

/* -----------------------------------------------------------------------------
 * §POS-5  .pos-table / .pos-table-row 側欄清單
 * ★ Color Admin 給了兩種語意，這裡兩種都保留（餐廳桌位版肥肥沒用到，只留骨架）：
 *   (a) 側欄清單版（肥肥實際在用）
 *       .pos .pos-sidebar-body .pos-table{padding:0 1.265625rem}
 *       .pos-table-row{padding:1.265625rem 0;margin:0;position:relative}
 *       .pos-table-row + .pos-table-row{border-top:1px solid #e9ecef}
 *   (b) 桌位格線版（.pos .pos-table-row{margin:-.46875rem;display:flex;flex-wrap:wrap}）
 *       pos/checkout 的頁腳有一個 .row.pos-table-row 會吃到它，維持原負 margin 不動版。
 * --------------------------------------------------------------------------- */
.pos .pos-table-row { margin: -.46875rem; display: flex; flex-wrap: wrap; }
.pos .pos-table-row .pos-table { padding: .46875rem; margin: 0; height: 100%; }

.pos .pos-sidebar .pos-sidebar-body .pos-table { padding: 0 1.265625rem; }  /* 20.25px */
.pos .pos-sidebar .pos-sidebar-body .pos-table .pos-table-row {
  padding: 1.265625rem 0;
  margin: 0;
  position: relative;
  display: flex;
  flex-wrap: wrap;
}
.pos .pos-sidebar .pos-sidebar-body .pos-table .pos-table-row + .pos-table-row {
  border-top: 1px solid hsl(var(--border));
}
.pos .pos-sidebar .pos-sidebar-body .pos-table .pos-table-row > div { padding: 0; }
/* 數量增減（CA：85px 寬、26×26 的 +/- 鈕）
   ★ 現場要好按 → 34×34；form-control 同步拉高，維持一列對齊。 */
.pos .pos-sidebar .pos-sidebar-body .pos-table .pos-table-row > div .input-group.qty {
  width: 104px;
  margin-top: 10px;
  align-items: center;
}
.pos .pos-sidebar .pos-sidebar-body .pos-table .pos-table-row > div .input-group.qty .form-control {
  margin: 0;
  background: none;
  border: 0;
  padding: 0;
  text-align: center;
  line-height: 1;
  height: 34px;
  font-weight: 600;
  color: hsl(var(--foreground));
}
.pos .pos-sidebar .pos-sidebar-body .pos-table .pos-table-row > div .input-group.qty .btn {
  padding: 0;
  border: 1px solid hsl(var(--border));
  width: 34px;
  height: 34px;
  min-height: 34px;                       /* 蓋掉 §POS-10 的 .pos .btn{min-height:40px} */
  display: flex;
  align-items: center;
  justify-content: center;
}
.pos .pos-sidebar .pos-sidebar-body .pos-table .pos-table-row > div .price {
  font-size: .9375rem;                    /* CA .875rem → 15px */
  font-weight: 600;
}
.pos .pos-sidebar .pos-sidebar-body .pos-table .pos-table-row > div.total-price {
  font-size: .9375rem;
  font-weight: 600;
  text-align: right;
}
.pos .pos-sidebar .pos-sidebar-body .pos-table .pos-table-row > div a { display: inline-block; }
/* 刪除確認遮罩（CA：rgba(255,255,255,.95) 白霧） */
/* ★2026-08-04 稽核：blade 0 命中 */
.pos .pos-sidebar .pos-sidebar-body .pos-table .pos-table-row .pos-remove-confirmation {
  position: absolute;
  top: .46875rem;
  left: -.65625rem;
  right: -.65625rem;
  bottom: .46875rem;
  max-width: initial;
  width: auto;
  border-radius: var(--radius);
  background: hsl(var(--card) / .95);
  font-size: .9375rem;
  font-weight: 600;
  z-index: 10;
  padding: 1.265625rem;
  display: flex;
  align-items: center;
}

/* -----------------------------------------------------------------------------
 * §POS-6  .pos-product-thumb 側欄品項縮圖列
 * CA 原值：.pos-product-thumb{display:flex}
 *          .img{width:60px;height:60px;border-radius:6px;background:cover/center}
 *          .img + .info{flex:1;padding-left:15px}
 *          .info .title{font-weight:600;font-size:14px}
 *          .info .single-price{font-weight:600}
 * ★ 短名（.img/.info/.title/.single-price）一律維持巢狀在 .pos-product-thumb 底下，
 *   絕不寫成裸的 .title / .price，否則污染全站。
 * --------------------------------------------------------------------------- */
.pos .pos-product-thumb { display: flex; }
.pos .pos-product-thumb .img {
  width: 60px;
  height: 60px;
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  border-radius: var(--radius);
  background-color: hsl(var(--muted));
}
.pos .pos-product-thumb .img + .info { flex: 1; padding-left: 15px; }
.pos .pos-product-thumb .info { flex: 1; }
.pos .pos-product-thumb .info .title {
  font-weight: 600;
  font-size: .9375rem;                    /* CA 14px → 15px */
  color: hsl(var(--foreground));
}
/* ★2026-08-04 稽核：blade 0 命中 */
.pos .pos-product-thumb .info .single-price {
  font-weight: 600;
  color: hsl(var(--foreground));
}

/* -----------------------------------------------------------------------------
 * §POS-7  .pos-mobile-sidebar-toggler 手機浮動開關（FAB）
 * CA 原值：position:fixed;bottom:60px;right:12px;z-index:1020;width:60px;height:60px;
 *          padding:10px;border-radius:50px;display:none;
 *          background:var(--bs-component-color)（深）；color:var(--bs-component-bg)（淺）
 *          @media(max-width:991.98px){display:flex}
 *          .img{width:32px;height:32px}
 *          .badge{右上角 20×20 圓形，品牌色底}
 * ★ 肥肥的 machine / package 版是純文字「紀錄」，所以字級不能太小。
 * --------------------------------------------------------------------------- */
.pos-mobile-sidebar-toggler {
  position: fixed;
  bottom: 60px;
  right: 12px;
  z-index: 1020;
  padding: 10px;
  min-width: 60px;
  height: 60px;
  text-decoration: none;
  background: hsl(var(--dark));
  color: hsl(var(--dark-foreground));
  display: none;
  border: 1px solid hsl(var(--dark));
  border-radius: 9999px;
  box-shadow: 0 .5rem 1.25rem hsl(var(--foreground) / .22);
  align-items: center;
  justify-content: center;
  font-weight: 600;
}
.pos-mobile-sidebar-toggler:hover,
.pos-mobile-sidebar-toggler:focus {
  background: hsl(var(--foreground));
  color: hsl(var(--background));
}
.pos-mobile-sidebar-toggler .img { width: 32px; height: 32px; display: block; margin: 0 auto; }
.pos-mobile-sidebar-toggler .badge {
  position: absolute;
  right: 0;
  top: 0;
  background-color: hsl(var(--primary));
  color: hsl(var(--primary-foreground));
  width: 20px;
  height: 20px;
  font-size: .8125rem;
  font-weight: 600;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 9999px;
}
@media (max-width: 991.98px) {
  .pos-mobile-sidebar-toggler { display: flex; }
}

/* -----------------------------------------------------------------------------
 * §POS-8  pos/machine 的取消警告 modal（SweetAlert）
 * 原本寫在 resources/views/pos/machine.blade.php 的 @section('css')，寫死
 * #e9ecef / #adb5bd / #198754 / #fff — 暗色下會白底白字，改用 token 搬進主題。
 * 邏輯不變：未勾 .pos-warning-cb 時 confirm 鈕不可點（純 CSS，零 JS race）。
 * --------------------------------------------------------------------------- */
.swal-modal:has(.pos-warning-cb:not(:checked)) .swal-button--confirm {
  background-color: hsl(var(--muted)) !important;
  color: hsl(var(--muted-foreground)) !important;
  cursor: not-allowed !important;
  pointer-events: none !important;
  box-shadow: none !important;
}
.swal-modal:has(.pos-warning-cb:checked) .swal-button--confirm {
  background-color: hsl(var(--success)) !important;
  color: hsl(var(--success-foreground)) !important;
  cursor: pointer !important;
  pointer-events: auto !important;
  box-shadow: 0 2px 8px hsl(var(--success) / .3) !important;
}

/* -----------------------------------------------------------------------------
 * §POS-9  pos/_package 的「呼吸」提示外框
 * 原本寫在 resources/views/pos/_package.blade.php 的 <style>：border:7px solid #333。
 * 動畫（keyframes / duration / iteration）是版型，原樣保留；只有顏色換 token。
 * --------------------------------------------------------------------------- */
.breath {
  animation-name: breath;
  animation-duration: 3s;
  animation-timing-function: ease-in-out;
  animation-iteration-count: 3;
  border: 7px solid hsl(var(--foreground));
}
@keyframes breath {
  from { opacity: 1; }
  50%  { opacity: .5; }
  to   { opacity: 1; }
}

/* -----------------------------------------------------------------------------
 * §POS-10  POS 現場易讀性微調（不改版面，只放大字與觸控目標）
 * 業主定調：POS 是現場作業畫面，字要夠大、按鈕要好按。
 * ★ 只作用在 .pos 內，不外洩到全站。
 * --------------------------------------------------------------------------- */
.pos .btn:not(.btn-close):not(.btn-xs) { min-height: 40px; }
.pos .btn.btn-xs { min-height: 26px; }              /* 唯讀 chip（span.btn.btn-xs）維持小尺寸 */
.pos .product .text .title { font-size: .9375rem; }  /* CA .875rem → 15px */
.pos .product .text .desc  { font-size: .875rem; }
.pos .form-control,
.pos .form-select { min-height: 40px; }
.pos .form-control.form-control-sm,
.pos .form-select.form-select-sm { min-height: 34px; }
/* 掃描區的 STEP 提示（.not-available-text）在深色遮罩上，圖示與文字都要看得清 */
.pos .product.not-available .not-available-text i,
.pos .product.not-available .not-available-text .lucide { color: inherit; }

/* ── 展開/收合的 chevron（shadcn accordion 做法）
   不換圖示、單一 chevron 用 CSS 旋轉。原本的寫法是 JS 對調 fa-caret-down / fa-caret-up
   兩個 class —— lucide 把 <i> 換成 <svg> 之後那種寫法會靜默失效（箭頭永遠不轉）。 */
/* ★2026-08-04 稽核：blade 0 命中 */
.fb-caret { transition: transform .18s ease; }
/* ★2026-08-04 稽核：blade 0 命中 */
.fb-caret.open,
.open > .fb-caret,
[aria-expanded="true"] > .fb-caret { transform: rotate(180deg); }

/* ── 列表操作鈕全站正規化（2026-07-26 收尾 → 2026-07-28 標準更新，規則已停用）
   原本這裡把「表格內 + 指向編輯類路由」的 btn-light 壓成實心近黑，
   因為 07-26 當時的規範是「列表每列的操作鈕＝zinc 近黑」。

   ★ 07-27 訂了新標準：列內動作鈕＝shadcn ghost（見本檔最末「列內動作鈕」一節），
     但那條規則只鎖 .btn-xs.btn-dark，這條舊規則沒跟著退場 → 兩套標準並存：
       btn-xs.btn-dark  → ghost（透明底）
       btn-xs.btn-light → 被這裡壓成實心近黑
     實測 /hr/remote 同一列三顆鈕長三個樣：
       審核 btn-dark  = 透明 ghost、32px、radius 6
       編輯 btn-light = 實心 rgb(24,24,27)、32px、radius 10   ← 這條規則造成的
       刪除 btn-danger= 實心紅、22.5px、radius 10
     且 btn-light 在 views 內是「多數」（312 處）、btn-dark 才 192 處，
     等於全站多數的列內動作鈕都沒吃到 ghost 標準。

   → 顏色一律交由本檔最末的 ghost 規則統一決定，這裡不再強壓顏色。
     選擇器保留在註解中備查，不重複定義第二套（維持樣式 SSOT 單一來源）。 */

/* §filterbar 已移除：改用 merp 的實證結構（篩選列收進 card-header + d-flex gap-2），
   不再自創 .fb-filterbar 容器。見 .agent/shadcn-block-spec.md §1。 */

/* ── 全站控制項高度統一（規格 §0：sm=32px，不可 31/33/35 混雜） */
.app-content .form-control-sm:not(textarea),
.app-content .form-select-sm,
.app-content .btn-sm,
.app-content .input-group-sm > .form-control:not(textarea),
.app-content .input-group-sm > .input-group-text,
.app-content .select2-container--bootstrap-5 .select2-selection--single,
/* ★ 2026-07-28 量測補：<input type="color"> 沒有 .form-control-sm 這個寫法
   （Bootstrap 的 .form-control-color 自帶固定尺寸），於是逃過上面的收斂。
   實測 /hr/schedule/create 同一張表單：其餘 8 個控制項都是 32px，只有它 38px。
   直接鎖 .form-control-color，不必要求 view 補 class。 */
.app-content .form-control-color {
    height: 32px;
    min-height: 32px;
    font-size: 13px;
}
/* ★★ textarea 必須排除在固定高度之外（2026-07-27 逐頁掃描抓到的 regression）
   原本這條沒寫 :not(textarea)，等於把 height/min-height 一起釘死在 32px，
   `rows` 屬性完全失效 —— 實測 rows=2/3/4/5 全部渲染成 32px。
   後果不只是變單行：篩單規則頁的 sku_list（rows=4）scrollHeight 是 47px，
   placeholder 第二行「如：stickyhair0303、bubblebag0105OS…」直接被切一半。
   textarea 只跟著字級走，高度交還給 rows。 */
.app-content textarea.form-control-sm,
.app-content .input-group-sm > textarea.form-control {
    font-size: 13px;
    min-height: 0;
    height: auto;
}
.app-content .btn-sm { padding-top: 0; padding-bottom: 0; display: inline-flex; align-items: center; justify-content: center; }
/* 圖示型小鈕維持正方 */
.app-content .btn-sm.btn-icon, .app-content .btn-xs.btn-icon { padding: 0; }

/* =============================================================================
 * §補位 7 — 表格與分頁的規格缺口（2026-07-27，區塊重構代理實測發現）
 * ============================================================================= */

/* ── th 字級：主題原本只設了 color 與 font-weight，沒設 font-size
   → view 端必須靠 .small 才有 12px，拿掉就變 14px。規格 §4 明訂 th = 12px，補上。
   ★2026-08-08：`.table-panel` 那行已成死選擇器（markup 的 table-panel 已全數移除，
     理由與驗證見 §23 那段長註解），故移除。原清單記此存查：
       `.app-content .table > thead > tr > th,`
       `.app-content .table-panel > thead > tr > th {` */
.app-content .table > thead > tr > th {
    font-size: 12px;
    font-weight: 500;
    color: hsl(var(--muted-foreground));
    letter-spacing: .01em;
}
/* ★★2026-08-08 稽核：下面這一行是**死碼**，保留供對照（禁止刪除守則）。
   §S-64（本檔 L12952 附近，2026-08-07 業主拍板「全站表格統一吃 shadcn」）寫了
   **一模一樣的選擇器** `.app-content .table > tbody > tr > td`，宣告 `font-size: 14px`。
   兩條特異度同為 (0,2,3)、都不帶 !important ⇒ **由檔內順序決勝，後出現的 §S-64 贏**。
   ⇒ 本行的 13px 從 §S-64 落地那天起就沒有再生效過。

   ★這不是靜態推論，是實測出來的（4 頁 × 雙向，走 https://test.2025-seller.mtest）：
     /order-management/status/new-orders、/inventory/product、/hr/user、/store-setting/store
       基準 td 字級 ………………………………… 14px
       (A) 用 CSSOM 停用**本行** → 仍是 14px（逐頁相同）⇒ 拿掉它畫面零變化 ＝ 死碼
       (B) 用 CSSOM 停用 §S-64 那條 → 掉回 13px（逐頁相同）
           ⇒ 證明本行確實還在規則表裡、只是排在後面輸掉，不是我找錯規則
     ★(B) 是 (A) 的正對照：只做 (A) 的話，「沒變化」與「我根本沒停用到東西」長得一樣。

   ★★連帶影響（記在這裡，因為它害過人）：`components/batch-bar.blade.php:73` 的註解寫著
     「全站『表格內文/次要資訊』的 13px 都由 .fs-13px 那一條供給」——
     那句話在 §S-64 之前是對的，**現在是過時的**：表格內文已經是 14px，
     `.fs-13px` 反而變成比它旁邊的表格小 1px。這正是 §S-87 要收掉的東西。
     ★該 blade 註解本輪**沒有動**（不在本代理的檔案所有權內），已請上游記進 .agent/TODO.md。 */
.app-content .table > tbody > tr > td { font-size: 13px; }

/* ★★ 2026-07-28 量測補：.small / <small> 是 .875em，疊在上面這條 13px 的 td 上
   → 11.375px（實測全站 387 處都是 11.4px），低於 shadcn 最小階 text-xs(12px)。
   跟 badge 是同一個病：em 疊乘讓同一個 class 在不同容器有不同大小
   （body 14px 下是 12.25px、td 13px 下是 11.4px）。固定成 12px 讓它可預測。
   只鎖 .app-content：登入頁與列印/郵件版型不受影響（那些有自己的絕對字級需求）。 */
.app-content .small,
.app-content small { font-size: 12px; }

/* ── .pagination-wrapper：全專案 45 處在用，但沒有任何 CSS 定義（完全沒樣式）
   規格 §5：卡片底部、左邊每頁筆數、右邊頁碼。 */
.pagination-wrapper {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
    padding: 12px 16px;
    border-top: 1px solid hsl(var(--border));
    background: hsl(var(--card));
    border-bottom-left-radius: var(--radius);
    border-bottom-right-radius: var(--radius);
}
/* 分頁列若落在卡片外面（45 處多數如此），自己補上外框才不會像浮在空白裡 */
.card + .pagination-wrapper,
.app-content > .pagination-wrapper {
    border: 1px solid hsl(var(--border));
    border-radius: var(--radius);
    margin-top: -1px;
}
.pagination-wrapper .pagination { margin: 0; }
.pagination-wrapper > :only-child { margin-left: auto; margin-right: auto; }
/* 分頁元件（component/pagination.blade.php）自己就是一列「左：統計文字 / 右：頁碼」，
   當它是 .pagination-wrapper 的唯一子項時要撐滿，否則上面那條 :only-child 置中規則
   會把「統計 + 頁碼」整組推到中間，與 merp 的「左統計右頁碼」排版不一致。 */
.pagination-wrapper > .d-md-flex { flex: 1 1 auto; min-width: 0; margin-left: 0; margin-right: 0; }

/* ★2026-07-27 修：.pagination-wrapper 被巢狀放進 .card-footer 的頁面（hr/schedule、
   BoxSpec、Order 全家…都是 `card-footer d-flex justify-content-end` 內再放分頁）：
     1) 它自己又畫一次 border-top + 卡片底色 + 內距 → 卡片底部出現「兩層」邊界
     2) 被 card-footer 的 flex 壓成內容寬（實測只有 280px）→ 那條 border-top 變成
        一條只有右半截的斷線浮在卡片裡（業主一眼就會看到的破圖）
   在 card-footer / card-body 裡時退化成單純的滿寬 flex 列，外框交給卡片本身。 */
.card-footer > .pagination-wrapper,
.card-body > .pagination-wrapper {
    width: 100%;
    flex: 1 1 auto;
    border: 0;
    border-radius: 0;
    padding: 0;
    background: transparent;
}
/* 分頁列整體是次要資訊：不繼承 card-footer 的 600 字重，字級收到 13px。
   （頁碼 .page-link 有自己的尺寸，不受影響。） */
.pagination-wrapper {
    font-weight: 400;
    font-size: .8125rem;
}

/* ── 吸頂表頭：sticky thead 需要不透明底才不會被內容穿透
   （代理保留的 9 處 .bg-light.sticky-top，改用 card token 而非 Bootstrap 灰） */
.app-content thead.sticky-top th,
.app-content .table thead.bg-light.sticky-top th {
    background: hsl(var(--card)) !important;
    box-shadow: inset 0 -1px 0 hsl(var(--border));
}

/* =============================================================================
 * §頂 bar 頁標題（SSOT，移植自 merp）
 * 每頁的 .page-header 由 _shell 的 JS 把「返回鈕 + 標題 + 計數/副標」搬進頂 bar，
 * 內容區省下一整列；剩下的動作鈕原地變成動作行。全站自動生效。
 * ============================================================================= */
.fb-topbar-page {
    display: flex;
    align-items: center;
    gap: .5rem;
    min-width: 0;
    flex: 1 1 auto;
    padding-inline: .75rem;
    overflow: hidden;
}
.fb-topbar-title {
    font-size: 1.05rem !important;
    margin: 0 !important;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    font-weight: 600;
    color: hsl(var(--foreground));
}
.fb-topbar-meta {
    flex: 0 0 auto;
    white-space: nowrap;
    font-size: 12px;
}
.fb-topbar-page .btn { white-space: nowrap; }

/* 返回鈕 → 箭頭方鈕 */
.fb-back-btn {
    width: 32px; height: 32px; padding: 0;
    border: 1px solid hsl(var(--border));
    background: transparent;
    color: hsl(var(--foreground));
    border-radius: var(--radius);
    display: inline-flex; align-items: center; justify-content: center;
    flex: 0 0 auto;
    margin-inline-end: .25rem;
    text-decoration: none;
    cursor: pointer;
}
.fb-back-btn:hover { background: hsl(var(--accent)); color: hsl(var(--foreground)); }

/* 標題搬走後，原位的 page-header 變成動作行 */
.fb-action-row {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
    align-items: center;
    margin-bottom: .9rem;
}
.fb-action-row:empty { display: none; }
/* ★頁首不可裁切：各 view 頁首常寫 inline overflow-x:auto，依 CSS 規範另一軸也會被算成 auto
   → 整個頁首變裁切框，Bootstrap 下拉一展開就被切掉。本列已 flex-wrap 會換行，一律 visible。
   必須 !important 才蓋得過 view 的 inline style。 */
#content .page-header.fb-action-row { overflow: visible !important; margin-bottom: .9rem; }

@media (max-width: 991.98px) {
    .fb-topbar-title { font-size: .92rem !important; }
    .fb-topbar-page .btn:not(.fb-back-btn) { display: none; }
}

/* =============================================================================
 * §select2（SSOT，2026-07-27 從 includes/_link.blade.php 的 138 行 inline hack 收斂而來）
 * 原本 view 端把高度寫死 30px !important，蓋掉規格的 32px，讓 select2 比同列其他控制項矮。
 * 保留原本解掉的兩個坑：① bootstrap-5 theme 的 __arrow 隱藏規則選擇器寫錯，三角形沒被藏
 * ② Color Admin 的 background 簡寫會洗掉 caret → 明確補回 BS5 的 caret SVG。
 * ============================================================================= */
.select2-container--bootstrap-5 .select2-selection--single,
.select2-container--bootstrap-5 .select2-selection--multiple {
    height: 32px !important;
    min-height: 32px !important;
    padding: 0 28px 0 10px !important;
    font-size: 13px !important;
    line-height: 30px !important;
    display: flex !important;
    align-items: center;
    width: 100% !important;
    box-sizing: border-box !important;
    border: 1px solid hsl(var(--border)) !important;
    border-radius: var(--radius) !important;
    background-color: hsl(var(--background)) !important;
    color: hsl(var(--foreground)) !important;
}
.select2-container--bootstrap-5 .select2-selection__rendered {
    padding: 0 !important;
    line-height: 30px !important;
    font-size: 13px !important;
    color: hsl(var(--foreground)) !important;
    overflow: hidden !important;
    text-overflow: ellipsis !important;
    white-space: nowrap !important;
}
.select2-container--bootstrap-5 .selection { display: block !important; width: 100% !important; }
/* ① 原廠隱藏規則選擇器寫錯（__arrow 是 __rendered 的兄弟不是子層）→ 手動藏掉 */
.select2-container--bootstrap-5 .select2-selection__arrow { display: none !important; }
/* ② 補回 caret（走 currentColor 的 mask，暗色會自動翻轉，不再寫死 %23343a40） */
.select2-container--bootstrap-5 .select2-selection--single::after {
    content: ""; position: absolute; right: 10px; top: 50%; transform: translateY(-50%);
    width: 14px; height: 14px; pointer-events: none; background-color: currentColor; opacity: .5;
    -webkit-mask: no-repeat center / 14px url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
    mask: no-repeat center / 14px url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
}
.select2-container--bootstrap-5 .select2-selection--single { position: relative; }
select.fw-bold + .select2-container .select2-selection__rendered { font-weight: 600; }
.select2-container--bootstrap-5 .select2-selection__clear { line-height: 30px !important; }
.select2-container--bootstrap-5 .select2-dropdown { font-size: 13px !important; }
.select2-container--bootstrap-5 .select2-search--dropdown .select2-search__field,
.select2-container--bootstrap-5 .select2-search .select2-search__field {
    font-size: 13px !important; padding: 5px 10px !important; min-height: 30px !important; line-height: 20px !important;
}
.select2-container--bootstrap-5 .select2-results__option,
.select2-container--bootstrap-5 .select2-results__options li { padding: 5px 10px !important; font-size: 13px !important; line-height: 20px !important; }
.select2-container--bootstrap-5 .select2-results__message { padding: 6px 10px !important; font-size: 13px !important; }
.select2-container--bootstrap-5 .select2-search--dropdown { padding: 4px !important; }
.select2-container--bootstrap-5 .select2-results { padding: 0 !important; }
.select2-container--bootstrap-5 .select2-results__options { max-height: 250px !important; }
/* 寬度行為（沿用原本的三種模式） */
select.select2-hidden-accessible + .select2-container { display: inline-block !important; min-width: 80px; }
select.select2-fit + .select2-container { width: max-content !important; }
select.w-100.select2-hidden-accessible + .select2-container { display: block !important; }
.input-group select.select2-hidden-accessible + .select2-container { display: flex !important; flex: 1 1 auto; width: 1% !important; min-width: 0; }
.input-group .select2-container--bootstrap-5 .select2-selection { border-top-right-radius: 0 !important; border-bottom-right-radius: 0 !important; }

/* ── x-filter-bar 的卡片外觀（21 頁共用的篩選列 SSOT，2026-07-27）
   舊版元件輸出「浮在卡片外的扁平列 + overflow-x-auto」，是 Color Admin 語彙。
   改成 card-header 的外觀：1px 框、圓角、內距，窄螢幕換行不出現橫捲軸。
   已自己包在 .card > .card-header 裡的頁面用 :card="false"，只吃 d-flex。 */
/* ★2026-08-04 稽核：blade 0 命中 */
.fb-filter-card {
    padding: 12px;
    margin-bottom: 16px;
    background: hsl(var(--card));
    border: 1px solid hsl(var(--border));
    border-radius: var(--radius);
    box-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);
}
/* 若它正好緊貼在下方卡片上，兩者接成一體（避免出現雙層框） */
/* ★2026-08-04 稽核：blade 0 命中 */
.fb-filter-card + .card {
    border-top-left-radius: 0;
    border-top-right-radius: 0;
    margin-top: -17px;
}
/* ★2026-08-04 稽核：blade 0 命中 */
.fb-filter-card:has(+ .card) {
    border-bottom-left-radius: 0;
    border-bottom-right-radius: 0;
    margin-bottom: 0;
}
/* 卡片內的 filter-bar 不再自帶外框（由 card-header 承載） */
/* ★2026-08-04 稽核：blade 0 命中 */
.card .fb-filter-card, .card-header .fb-filter-card {
    padding: 0; margin: 0; background: none; border: 0; box-shadow: none; border-radius: 0;
}

/* ── 下拉箭頭重複（業主 2026-07-27 回報：頂 bar 的「前台」「Monet」都出現兩個三角形）
   原因：markup 是 Color Admin 寫法 `<a class="dropdown-toggle"><b class="caret"></b></a>`，
   我補了 .caret 的樣式（CA 專有，BS5 已移除），但 Bootstrap 自己又會給
   .dropdown-toggle 一個 ::after 三角形 → 兩個疊在一起。
   治法：元素內若已經有 .caret，就把 Bootstrap 的 ::after 收掉（保留 markup 裡那顆）。 */
/* ★★ 用後代選擇器不用直接子元素（2026-07-27 逐頁掃描抓到）：
   原本寫 :has(> .caret) 只擋得住 .caret 是直接子元素的情況，
   但後台頂 bar 的使用者選單把它包在 <span> 裡
   （_head.blade.php：<a class="dropdown-toggle"><span>名字<b class="caret"></b></span></a>），
   於是 Bootstrap 的 ::after 沒被擋掉 → 每一頁的頂 bar 都畫了兩個三角形。
   改成後代選擇器，包幾層都擋得住。 */
.dropdown-toggle:has(.caret)::after { display: none !important; }

/* .caret 本身改成 shadcn 的 chevron（線條），不要 CA 的實心三角 */
.caret {
    display: inline-block;
    width: 12px; height: 12px;
    margin-left: .25rem;
    vertical-align: middle;
    border: 0 !important;
    background-color: currentColor;
    opacity: .55;
    -webkit-mask: no-repeat center / 12px url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
    mask: no-repeat center / 12px url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
}

/* =============================================================================
 * §可展開篩選（2026-07-27 業主指定）
 * shadcn 的做法：一行「常用篩選」常駐（關鍵字 + 查詢 + 篩選切換 + 清除），
 * 其餘進階條件收進可展開面板；展開鈕帶「已套用幾項」的計數。
 * 好處：篩選條件再多也不會擠爆一列，也不會出現橫向捲軸。
 *
 * 用法（view 端）：
 *   <div class="fb-filter" data-filter>
 *       <div class="fb-filter-row">…常駐控制項…
 *           <button type="button" class="btn btn-light btn-sm" data-filter-toggle>
 *               <i data-lucide="sliders-horizontal"></i> 篩選 <span data-filter-count></span>
 *           </button>
 *       </div>
 *       <div class="fb-filter-advanced" data-filter-advanced>…進階條件…</div>
 *   </div>
 * 行為由 includes/_shell.blade.php 統一提供（展開/收合、計數、記憶）。
 * ============================================================================= */
.fb-filter { display: flex; flex-direction: column; gap: 10px; }
/* ★2026-08-04 稽核：blade 0 命中 */
.fb-filter-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
}
/* ★2026-08-04 稽核：blade 0 命中 */
.fb-filter-advanced {
    display: none;
    padding-top: 10px;
    border-top: 1px solid hsl(var(--border));
}
/* ★2026-08-04 稽核：blade 0 命中 */
.fb-filter.is-open .fb-filter-advanced {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
    gap: 8px 10px;
    align-items: end;
}
/* 進階面板內每一格：label 在上、控制項在下（規格 §6） */
/* ★2026-08-04 稽核：blade 0 命中 */
.fb-filter-advanced > * { min-width: 0; }
/* ★2026-08-04 稽核：blade 0 命中 */
.fb-filter-advanced .form-label {
    display: block;
    font-size: 11px;
    font-weight: 500;
    color: hsl(var(--muted-foreground));
    margin-bottom: 4px;
}
/* ★★ 2026-07-27 中央修（篩選收納代理實測回報）：
   下面這條 width:100% 的權重贏過 select2 的 .select2-hidden-accessible{width:1px}，
   把「被 select2 藏起來的原生 <select>」撐成整欄寬（1170px）。
   它是 position:absolute 又沒有就近的定位祖先 → 1440px 下實測橫向溢出 909px。
   排除 select2 的替身元素，讓它維持 1px。 */
/* ★2026-08-04 稽核：blade 0 命中 */
.fb-filter-advanced .select2-hidden-accessible {
    width: 1px !important;
    min-width: 0 !important;
}
/* ★2026-08-04 稽核：blade 0 命中 */
.fb-filter-advanced .form-control,
.fb-filter-advanced .form-select:not(.select2-hidden-accessible),
.fb-filter-advanced .dropdown > .btn,
.fb-filter-advanced .select2-container { width: 100% !important; }

/* 展開鈕：已套用幾項用 badge 顯示 */
[data-filter-toggle] [data-filter-count]:not(:empty) {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 17px;
    height: 17px;
    padding: 0 5px;
    margin-left: 4px;
    border-radius: 9999px;
    background: hsl(var(--dark));
    color: hsl(var(--dark-foreground));
    font-size: 10px;
    font-weight: 600;
}
[data-filter-toggle] .lucide-chevron-down,
[data-filter-toggle] [data-lucide="chevron-down"] { transition: transform .18s ease; }
.fb-filter.is-open [data-filter-toggle] [data-lucide="chevron-down"],
.fb-filter.is-open [data-filter-toggle] .lucide-chevron-down { transform: rotate(180deg); }

/* .fb-tabs 掛在 li.nav-item > button 上時，li 高度不定會讓 .fb-tab 的
   height:calc(100% - 1px) fallback 成 auto → 按鈕比軌道矮。補這行讓 li 撐滿。
   （中心看板代理實測 finance/report 的 modal 分頁時發現） */
.fb-tabs .nav-item { display: flex; }

/* ─────────────────────────────────────────────────────────────
   §btn-check 選中態（兩個代理各自回報同一個問題）
   .btn-check + label.btn-outline-* 是 Bootstrap 的「單選按鈕群」寫法，
   蝦皮的排序切換、批量調整方式都用它。
   主題把 outline 系列的 --bs-btn-active-bg 收斂成 --accent（很淺的灰），
   結果選中/未選中幾乎看不出差別 —— 那是狀態，不是裝飾，必須看得出來。
   對齊 §7 分段控制器：選中＝--dark 實心，與 .fb-tab.active 同語彙。

   ★★2026-08-07 §S-74 覆核（原文全部保留，這行是補充不是訂正）：
     `resources/views/**` 對 `.btn-check` 是 **0 處使用**（grep 實證，2026-08-07 再驗一次仍為 0），
     所以本節這幾條規則目前**沒有服務到任何現存按鈕**——它是 Bootstrap 原生語彙的備用實作。
     ★依「禁止刪除」原則保留不刪，而且它有真正的價值：它是本檔**唯一定義過選中態長相**的地方，
       §S-74 修 `.btn-outline-*.active` 時就是逐字取這裡的值，讓兩種寫法只有一種選中語彙。
     ★所以要動選中態的顏色，改這裡，然後同步 §S-74 —— 那兩處是同一個設計決定的兩個落點。
   ───────────────────────────────────────────────────────────── */
.btn-check:checked + .btn-outline-dark,
.btn-check:checked + .btn-outline-secondary,
.btn-check:active + .btn-outline-dark,
.btn-check:checked + .btn.btn-outline-dark:focus-visible {
    background: hsl(var(--dark));
    border-color: hsl(var(--dark));
    color: hsl(var(--dark-foreground));
    box-shadow: none;
}
.btn-check:checked + .btn-outline-primary {
    background: hsl(var(--primary));
    border-color: hsl(var(--primary));
    color: hsl(var(--primary-foreground));
    box-shadow: none;
}
/* 未選中：與 .fb-tab 未選中態一致的 ghost，而不是描邊框 */
.btn-check + .btn-outline-dark,
.btn-check + .btn-outline-secondary {
    border-color: transparent;
    color: hsl(var(--muted-foreground));
}
.btn-check:not(:checked) + .btn-outline-dark:hover,
.btn-check:not(:checked) + .btn-outline-secondary:hover {
    background: hsl(var(--accent));
    color: hsl(var(--foreground));
}

/* ─────────────────────────────────────────────────────────────
   §14 補位 — .modal-content.border-0
   §卡片那邊已經對 .card.border-0 還原邊框，但 modal 沒有同款規則。
   Bootstrap 的 .border-0 帶 !important，會把 §14 給 modal 的 1px 細框整條吃掉
   （Buy/partials/list_table 有兩處這樣寫）。在主題還原比逐檔改 view 正確。
   ───────────────────────────────────────────────────────────── */
.modal-content.border-0 {
    border: 1px solid hsl(var(--border)) !important;
}

/* ─────────────────────────────────────────────────────────────
   §說明鈕定位（業主 2026-07-27：「請你把說明往右放」）
   x-page-help 在不同頁面被放在不同位置（有的在頁首標題旁、有的在動作列裡），
   逐頁調整不可行也不是 SSOT → 用 flex order + margin 一次定位：
   不論 view 把它寫在哪裡，視覺上一律排到頁首那一列的最右端。
   ★order 只影響視覺順序，不動 DOM，所以 JS 綁定與 _shell 的頂 bar 搬移都不受影響。
   ───────────────────────────────────────────────────────────── */
/* ★★ 2026-07-27 業主：「請你把按鈕置左跟置右，統一設計」
   原本我只給說明鈕 margin-left:auto，結果它被推到最右、其他動作鈕卻留在中間
   （實測 /shopee/product/online/push-monitor：「重新整理」「一鍵重推」浮在中央，
   「說明」孤零零在右端）—— 那是我製造出來的第三種對齊。

   統一規則（頁首那一列）：
     左：返回鈕 → 標題 → 計數/狀態 badge
     右：所有動作鈕與篩選控制項，成為一整組
   做法：第一顆按鈕/下拉/表單控制項自動吃 margin-left:auto，
   把它自己與後面所有東西一起推到右端 —— 不需要 view 逐頁加 .ms-auto。 */
.page-header > .btn:not(:first-child),
.page-header > .dropdown,
.page-header > form,
.page-header > .fb-page-help,
.page-header > select,
.page-header > .input-group {
    order: 50;
}
.page-header > .btn:not(:first-child) ~ *,
.page-header > .dropdown ~ *,
.page-header > form ~ * { order: 50; }
/* 說明鈕永遠排在動作組最後（它是輔助資訊不是主要動作） */
/* ★★ 2026-07-29 補 .fb-ph-inline 這一層（業主：「說明沒有往右移動，要 SSOT 置右」）：
   全站 206 頁把 <x-page-help> 放在 x-page-header 的 inline slot 裡，
   只有 27 頁用 subtitle 屬性。inline slot 的包裝層已改成 display:contents
   （見 components/page-header.blade.php），裡面的元素會**渲染成** page-header 的 flex 子項，
   但 display:contents 不改變 DOM，所以上面這些 `>` 直接子代選擇器仍然對不上
   —— 這就是先前「規則寫了卻沒作用」的原因。補一組穿過包裝層的選擇器，
   不用後代選擇器是為了避免打到下拉選單深處的按鈕。 */
.page-header > .fb-page-help,
.page-header > .fb-ph-inline > .fb-page-help,
.page-header .ms-auto > .fb-page-help { order: 99; margin-left: auto; }
.page-header > .fb-ph-inline { display: contents; }
/* 動作組的第一個元素負責把整組推到右邊 */
.page-header > .btn:not(:first-child):first-of-type,
.page-header > .dropdown:first-of-type,
.page-header > form:first-of-type {
    margin-left: auto;
}
/* 已經自己包了 .ms-auto 的頁面：不重複推，避免雙重推擠 */
.page-header > .ms-auto ~ * { margin-left: 0 !important; }
.page-header > .ms-auto { order: 50; }

/* ═══════════════════════════════════════════════════════════════
   §Sidebar 個人區塊 — 照 shadcn Sidebar 規格重做（2026-07-27）
   業主：「這塊好奇怪」「整個版面與選單請你依照 shadcn 設計過一次」

   原本的問題：頭像 + 名稱 + 客編 + 資訊圖示 + 一顆搶眼的黃色「續約訂閱」膠囊
   + chevron 全部塞在一起，各自為政，沒有層次。

   shadcn Sidebar 的 SidebarMenuButton（size="lg"）長相：
     ┌──────────────────────────────────┐
     │ [8px 圓角頭像]  主要文字（14px/500）      ⌄ │
     │                 次要文字（12px/muted）     │
     └──────────────────────────────────┘
   整塊是一顆 ghost 按鈕：hover 才有 accent 底、圓角 md、左右 8px、上下 8px。
   ═══════════════════════════════════════════════════════════════ */

/* 外層：不再是「有下框線的獨立區塊」，而是側欄內縮的一顆按鈕 */
.app-sidebar .menu-profile {
    border-bottom: 0;
    padding: 8px;
    margin-bottom: 4px;
    border-bottom: 1px solid hsl(var(--sidebar-border));
}
.app-sidebar .menu-profile-link {
    /* ★已被 §側欄使用者區 覆蓋（實測生效值：display=flex） */
    display: grid;
    grid-template-columns: 32px 1fr auto;
    align-items: center;
    gap: 8px;
    padding: 8px;
    border-radius: var(--radius);
    transition: background-color .15s;
}
.app-sidebar .menu-profile-link:hover {
    background: hsl(var(--sidebar-accent));
}
/* 裝飾用的 cover 與彈跳球 canvas：shadcn 的 sidebar 沒有這種東西，收掉 */
.app-sidebar .menu-profile-cover,
.app-sidebar .menu-profile-cover.with-shadow {
    display: none !important;
}
/* 頭像：32px 圓角方形（shadcn Avatar 在 sidebar 用的是 rounded-lg 不是圓形） */
.app-sidebar .menu-profile-image {
    width: 32px; height: 32px;
    margin: 0; padding: 0;
    border-radius: 8px;
    overflow: hidden;
    border: 1px solid hsl(var(--sidebar-border));
    flex: 0 0 auto;
}
.app-sidebar .menu-profile-image img {
    width: 100%; height: 100%; object-fit: cover; display: block;
}
/* 文字區：兩行，主要 14px/500、次要 12px/muted，過長一律省略號不換行 */
.app-sidebar .menu-profile-info {
    min-width: 0;
    display: flex; flex-direction: column; gap: 1px;
    font-size: 14px; font-weight: 500; line-height: 1.25;
    color: hsl(var(--sidebar-fg-strong)) !important;
}
.app-sidebar .menu-profile-info > .d-flex { min-width: 0; }
.app-sidebar .menu-profile-info .flex-grow-1 {
    min-width: 0;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.app-sidebar .menu-profile-info h6 {
    margin: 0;
    font-size: 12px; font-weight: 400; line-height: 1.3;
    color: hsl(var(--sidebar-fg)) !important;
    display: flex; align-items: center; gap: 4px;
}
.app-sidebar .menu-profile-info h6 svg { width: 12px; height: 12px; opacity: .7; }
/* 訂閱狀態：從搶眼的黃色實心膠囊 → shadcn 的小 badge（次要資訊不該比名字還亮） */
.app-sidebar .menu-profile-info .btn-yellow,
.app-sidebar .menu-profile-info .btn.rounded-pill {
    background: hsl(var(--primary) / .16) !important;
    color: hsl(var(--sidebar-fg-strong)) !important;
    border: 1px solid hsl(var(--primary) / .35) !important;
    font-size: 11px !important; font-weight: 500 !important;
    line-height: 1.4 !important;
    padding: 1px 6px !important;
    border-radius: 6px !important;
    box-shadow: none !important;
}
/* chevron：靠右、muted、不佔空間 */
.app-sidebar .menu-profile .menu-caret {
    margin: 0 !important;
    opacity: .5;
    flex: 0 0 auto;
}
/* 側欄收合時（minified）只留頭像 */
.app-sidebar-minified .app-sidebar .menu-profile-link {
    grid-template-columns: 32px;
    justify-content: center;
}
.app-sidebar-minified .app-sidebar .menu-profile-info,
.app-sidebar-minified .app-sidebar .menu-profile .menu-caret { display: none !important; }

/* ═══════════════════════════════════════════════════════════════
   §手機版頂 bar（2026-07-27 業主回報「手機版沒有做好」）
   實測 390px：整頁 scrollWidth 427 > 390，.navbar-nav 一路撐到 523px。
   元凶是右側四組東西全部展開：通知(鈴+103+caret)、前台(文字+caret)、
   使用者(頭像+名字+caret)，加上左側漢堡+logo 就爆了。
   後果不只是橫向捲軸 —— 標題被擠成「銷」一個字（業主截圖看到的怪字）。

   shadcn / 一般行動版慣例：窄螢幕只留圖示與頭像，文字標籤收掉。
   ═══════════════════════════════════════════════════════════════ */
@media (max-width: 767.98px) {
    /* 使用者名稱、前台文字：收掉，只留頭像與 caret
       ★★ 2026-07-29 修 bug（業主回報「右上角按鈕不見」）：
          這條的 `> span:not(.badge)` 本來只會打到使用者名稱那顆 span，
          因為當時頭像是 <img>。把頭像收進 <x-thumb> 之後它渲染成
          <span class="fb-thumb">，變成 .navbar-link 的直接子 span
          → 被這條一起 display:none，右上角就只剩一個空方塊
          （下拉選單還能開，所以不容易看出是頭像不見了）。
          排除 .fb-thumb —— 頭像本來就是這條規則要「留下」的東西。 */
    .app-header .navbar-nav .navbar-user .d-inline-block.text-truncate,
    .app-header .navbar-nav .navbar-user > .navbar-link > span:not(.badge):not(.fb-thumb) {
        display: none !important;
    }
    /* 通知數字 badge 保留（那是要看的資訊），但縮小 */
    .app-header .navbar-nav .badge { font-size: 10px; padding: 2px 5px; }
    /* 各項目間距收緊 */
    .app-header .navbar-nav > .navbar-item > .navbar-link { padding-left: .4rem; padding-right: .4rem; }
    /* 標題：讓它真的有空間，並以省略號收尾（原本被壓成一個字） */
    .fb-topbar-page { min-width: 0; flex: 1 1 auto; overflow: hidden; }
    .fb-topbar-title {
        display: block;
        max-width: 100%;
        white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    }
    /* 頂 bar 的 meta（更新時間、計數）在手機上收掉，內容區已經有一份 */
    .fb-topbar-page .fb-topbar-meta { display: none !important; }
    /* caret 在手機上是純裝飾（點整塊都會開），收掉換空間 */
    .app-header .navbar-nav .caret { display: none !important; }
}

/* ═══════════════════════════════════════════════════════════════
   §手機版分頁列（2026-07-27 手機實測定位）
   /inventory/product/ranking 在 390px 下整頁橫向捲軸 427 > 390，
   逐層量到底是 .pagination —— 頁碼一多就 460px 寬，直接撐破版面。
   它是全站共用的分頁元件（component/pagination.blade.php），所以一處全站生效。

   做法：窄螢幕讓分頁列自己換行 + 可橫捲，而不是把整頁撐開。
   ═══════════════════════════════════════════════════════════════ */
@media (max-width: 767.98px) {
    .pagination-wrapper,
    .card-footer > .d-md-flex {
        flex-wrap: wrap;
        gap: 8px;
    }
    /* ★2026-07-27 追到底：先前依賴容器 class 名（.pagination-wrapper / .card-footer），
       但 /buy/arrivals 的分頁元件外面又包了一層「沒有 class 的 div」，規則就漏掉了
       → 390px 仍溢出 147px。
       治本：card-footer 底下不論包幾層，一律不得超出容器寬度。
       這樣就不用再猜下一頁的 wrapper 叫什麼名字。 */
    .card-footer,
    .card-footer > *,
    .card-footer > * > * {
        max-width: 100%;
        min-width: 0;
    }
    .pagination,
    .pagination-wrapper .pagination,
    .card-footer .pagination {
        flex-wrap: nowrap;
        max-width: 100%;
        overflow-x: auto;
        scrollbar-width: none;          /* 手機不顯示捲軸，用手指滑 */
        -webkit-overflow-scrolling: touch;
        justify-content: flex-start;
    }
    .pagination-wrapper .pagination::-webkit-scrollbar,
    .card-footer .pagination::-webkit-scrollbar { display: none; }
    .pagination .page-link { flex: 0 0 auto; }
    /* 統計文字獨立一行、置中 */
    .pagination-wrapper > .me-auto,
    .card-footer .text-md-center { width: 100%; text-align: center; }
}

/* ═══════════════════════════════════════════════════════════════
   §報表表格的語意底色（2026-07-27 業主回報 /center/dashboard「這頁就穿透了」）

   問題：sticky 表頭底下捲動的數字直接透上來疊在標題上。
   根因：重構時把原本不透明的 #e8f5e9 / #e3f2fd 換成
        hsl(var(--success) / .15)、hsl(var(--info) / .12) ——
        token 化的同時「順手加了透明度」，而 position:sticky 一旦半透明就會穿。

   治本不是逐檔把顏色改回寫死，而是：
   ① 這些色調本來就不該散在 4 支 view 的 inline style 裡 → 收成主題的語意 class
   ② 用 color-mix 把色調混進卡片底色 → 視覺等效但「不透明」，
      而且仍然走 token（亮暗模式會自動翻轉）

   語意對照：
     .fb-cell-sum      區間總和欄（success 調）
     .fb-cell-avg      日平均欄（info 調）
     .fb-row-total     總計列（warning 調，較重）
     .fb-row-subtotal  小計列（warning 調，較淡）
     .fb-cell-neutral  中性強調欄（foreground 調，用於廣告矩陣）
   ═══════════════════════════════════════════════════════════════ */
/* ★2026-08-04 稽核：blade 0 命中 */
.app-content .fb-cell-sum {
    background-color: color-mix(in srgb, hsl(var(--success)) 15%, hsl(var(--card))) !important;
}
/* ★2026-08-04 稽核：blade 0 命中 */
.app-content .fb-cell-avg {
    background-color: color-mix(in srgb, hsl(var(--info)) 12%, hsl(var(--card))) !important;
}
/* ★2026-08-04 稽核：blade 0 命中 */
.app-content .fb-row-total > td,
.app-content .fb-row-total > th,
.app-content td.fb-row-total {
    background-color: color-mix(in srgb, hsl(var(--warning)) 18%, hsl(var(--card))) !important;
    font-weight: 600;
}
/* ★2026-08-04 稽核：blade 0 命中 */
.app-content .fb-row-subtotal > td,
.app-content .fb-row-subtotal > th {
    background-color: color-mix(in srgb, hsl(var(--warning)) 9%, hsl(var(--card))) !important;
}
/* ★2026-08-04 稽核：blade 0 命中 */
.app-content .fb-cell-neutral-strong {
    background-color: color-mix(in srgb, hsl(var(--foreground)) 7%, hsl(var(--card))) !important;
}
/* ★2026-08-04 稽核：blade 0 命中 */
.app-content .fb-cell-neutral-soft {
    background-color: color-mix(in srgb, hsl(var(--foreground)) 3.5%, hsl(var(--card))) !important;
}
/* ★保險絲：任何 sticky 儲存格若仍是半透明底，強制補一層不透明卡片底。
   這條擋的是「日後有人又寫了半透明色」——讓穿透不能再默默回來。 */
.app-content table th[class*="sticky"],
.app-content table td[class*="sticky"],
.app-content .table-sticky-head thead th {
    background-color: hsl(var(--card));
}

/* ═══════════════════════════════════════════════════════════════
   §頂 bar 通知徽章（2026-07-27 業主：「這邊的設計也很怪」）
   實測：徽章是 37×20px 的膠囊，跟 16px 的鈴鐺「並排」放 ——
   徽章比圖示還高，整組寬 90px，看起來像兩個獨立元件硬湊在一起。

   shadcn / 一般通知列的做法：計數貼在圖示右上角、小一號、必要時overlap，
   而不是排在旁邊。
   ═══════════════════════════════════════════════════════════════ */
#header .navbar-nav .navbar-link.icon,
#header .navbar-nav #notification-dropdown > .navbar-link {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}
#header .navbar-nav #notification-badge {
    position: absolute;
    top: 2px;
    left: 50%;
    margin-left: 2px;          /* 貼在鈴鐺右上角，略為重疊 */
    min-width: 16px;
    height: 16px;
    padding: 0 4px;
    font-size: 10px;
    font-weight: 600;
    line-height: 16px;
    border-radius: 9999px;     /* 計數徽章用全圓角，與方角的狀態 badge 區隔 */
    text-align: center;
    box-shadow: 0 0 0 2px hsl(var(--card));   /* 與底下的圖示分離 */
}

/* ═══════════════════════════════════════════════════════════════
   §步驟箭頭（stepper）重新設計 — 2026-07-27
   業主：「這種步驟的設計也要做好一點，目前設計不清楚」
        「要搭配 shadcn 把步驟箭頭做清楚一點，不然根本看不見」

   原本的問題（實測 /packaging-machine-order-management/status/ready-to-ship）：
     is-past  = hsl(var(--muted))  淺灰
     is-future= hsl(var(--muted))  淺灰   ← 與 past 完全同色
     is-current= .bg-white          白色
   三種狀態在淺色頁面上幾乎沒有差別，箭頭切口又只有 6px
   → 整排讀起來就是四個並排的文字，看不出是「流程」也看不出走到哪。

   重新設計（對齊 shadcn 的狀態層級：完成 → 當前 → 未到）：
     已完成 past    實心 muted 底 + 一般前景字（走過了，仍可讀）
     當前   current 實心 --dark 底 + 反白字 + 陰影（唯一的視覺焦點）
     未到   future  極淡底 + muted 字（還沒到，明顯退後）
   箭頭切口 6px → 11px，並加大步驟間的縫隙讓箭頭形狀真的讀得出來。
   ═══════════════════════════════════════════════════════════════ */
.pm-stepper--chevron,
.scr-stepper { --pm-arrow: 11px; --pm-gap: 2px; --scr-arrow: 11px; --scr-gap: 2px; }

/* ── 未到：最淡，明顯退後 */
.pm-stepper--chevron .pm-step.is-future,
.scr-stepper .scr-step.is-future {
    background-color: hsl(var(--muted) / .55) !important;
    color: hsl(var(--muted-foreground)) !important;
    font-weight: 400 !important;
}
/* ── 已完成：實心 muted，文字用正常前景（走過的階段仍要讀得清楚） */
.pm-stepper--chevron .pm-step.is-past,
.scr-stepper .scr-step.is-past {
    background-color: hsl(var(--muted)) !important;
    color: hsl(var(--foreground)) !important;
    font-weight: 500 !important;
}
/* ── 當前：唯一的焦點。實心近黑 + 反白字 */
.pm-stepper--chevron .pm-step.is-current,
.scr-stepper .scr-step.is-current {
    background-color: hsl(var(--dark)) !important;
    color: hsl(var(--dark-foreground)) !important;
    font-weight: 600 !important;
    filter: drop-shadow(0 1px 3px hsl(var(--foreground) / .28)) !important;
}
/* is-current 上若有 view 寫死的 .bg-white/.text-dark，交還給上面的規則 */
.pm-stepper--chevron .pm-step.is-current.bg-white,
.scr-stepper .scr-step.is-current.bg-white {
    background-color: hsl(var(--dark)) !important;
    color: hsl(var(--dark-foreground)) !important;
}
/* 當前步驟內的計數 chip 也要跟著反白 */
.scr-stepper .scr-step.is-current .scr-count,
.pm-stepper--chevron .pm-step.is-current .badge {
    background: hsl(var(--dark-foreground) / .22) !important;
    color: hsl(var(--dark-foreground)) !important;
}
/* ── 可點的步驟：hover 才有回饋，不要一直看起來像連結 */
.pm-stepper--chevron a.pm-step,
.scr-stepper a.scr-step { text-decoration: none !important; }
.pm-stepper--chevron a.pm-step:hover:not(.is-current),
.scr-stepper a.scr-step:hover:not(.is-current) {
    background-color: hsl(var(--accent)) !important;
    color: hsl(var(--accent-foreground)) !important;
    filter: none !important;
}

/* ── 手機版：收起來的小幫手側拉板不可撐開版面（2026-07-27 實測）
   .theme-panel 用 right:-260px 把自己藏到畫面外，桌機沒事，
   但 390px 下實測讓文件 scrollWidth 變成 537（溢出 147px）——
   只要祖先鏈上有 transform/filter，position:fixed 就會退化成相對該祖先定位、
   於是這 260px 就算進捲動範圍。
   收合狀態直接不渲染，展開由 JS 加 .active 時才出現。 */
@media (max-width: 767.98px) {
    .theme-panel:not(.active) { display: none !important; }
}

/* =============================================================================
 * §手機 390px 橫向溢出治本（2026-07-27 全站 368 頁 × 亮/暗/手機實測，17 頁溢出）
 * -----------------------------------------------------------------------------
 * 這一整段只在 max-width:767.98px 生效，桌機（1440px）版面一個像素都不動。
 * 定位方法：不看 class 猜，用完整 DOM 路徑 + 「隱藏該節點看 documentElement
 * .scrollWidth 有沒有掉回 clientWidth」的二分搜尋找真兇（誤判來源有三：
 * position:fixed 的祖先不撐版面、.table-responsive 內的寬表是正常的、
 * 真兇常常是沒有 class 的 wrapper）。
 * ============================================================================= */
@media (max-width: 767.98px) {

    /* ── ① .page-header 的 text-nowrap（12 頁的共同真兇）
       x-page-header（components/page-header.blade.php）在容器上掛
       `d-flex flex-wrap align-items-center gap-2 text-nowrap`，桌機要靠 nowrap
       讓「標題 + 計數 badge + 動作鈕」維持同一列不斷行，這在桌機是對的。
       但 inline slot 裡各頁常放一整句敘述（例：/hr/onboarding 的 47 字說明），
       而元件把 slot 包在 `.flex-shrink-0` 裡 —— nowrap + 不可壓縮 =
       這個 flex item 的寬度直接等於整句話的單行長度：
         /employee/user/attendance 708px、/hr/onboarding 619px、
         /good/mallbicGoodTag 557px、/marketing/ads/gms 520px（視窗只有 390px）。
       手機改成「可換行、可壓縮」，按鈕/badge/連結自己另外守住不斷行，
       文字一個字都沒動，只是允許它折行。 */
    .page-header { white-space: normal !important; }
    /* 巢狀都要放行：實測 /employee/user/attendance 的日期查詢表單是
       `.page-header > .ms-auto.d-flex > .d-flex.flex-wrap > form.d-flex.flex-shrink-0`
       —— 只鬆開最外層那一層，內層那個 flex-shrink-0 的 form（404px）照樣頂開版面。 */
    .page-header > *,
    .page-header .flex-shrink-0 { min-width: 0 !important; flex-shrink: 1 !important; }
    /* 動作鈕橫排容器（.ms-auto.d-flex…）在手機要能自己折行，
       否則就算外層可壓縮，內層 d-flex 不換行一樣把整列頂開。 */
    .page-header .d-flex:not(.flex-nowrap) { flex-wrap: wrap !important; }
    /* 但「單顆按鈕、狀態 badge、分頁連結」的文字仍不可從中間斷開 */
    .page-header .btn,
    .page-header .badge,
    .page-header .nav-link { white-space: nowrap; }

    /* ── ② select2 的替身原生 <select>（/shopee/returns/inspection 溢出 531px 的真兇）
       select2 把原本的 <select> 留在 DOM 裡、用
       `.select2-hidden-accessible{width:1px!important;height:1px!important;position:absolute}`
       藏起來。但 width/height 擋不住 **min-width / min-height 的下限夾擠**：
       該頁 view 寫了 inline `style="min-width:7.5rem"`（=120px），
       主題的 `.app-content .form-select-sm{min-height:32px}` 又把高度撐回 32px
       → 這顆「看不見」的 select 實際是 120×32 的盒子。
       它 position:absolute 而 .table-responsive 是 position:static，
       所以它的定位祖先是外層 `.card`（position:relative）——
       **絕對定位元素不會被「非其定位祖先」的 overflow 裁切**，
       於是它從表格的橫向捲動區逃出來，把整份文件推到 920px。
       （同一個坑 2026-07-27 已在 .fb-filter-advanced 修過一次，見上方 §篩選進階面板；
       這裡是全站版，只在手機生效。）
       把下限一起歸零，替身元素才真的是 1×1。 */
    .select2-hidden-accessible {
        width: 1px !important;
        min-width: 0 !important;
        height: 1px !important;
        min-height: 0 !important;
        /* ★光縮成 1px 還不夠：它的靜態位置在「表格捲出畫面的那一段」（實測 x=800），
           1px 的盒子照樣把 document 撐到 801px（溢出 411px）。
           把它釘回定位祖先的左緣，橫向就再也不會多出東西。
           元素本身有 clip-path:inset(50%) 又只有 1×1，位置對視覺與螢幕閱讀器都無影響。
           （不改成 .table-responsive{position:relative} 是因為那會連帶把表格裡
           本來能溢出顯示的 Bootstrap 下拉選單一起裁掉。） */
        left: 0 !important;
    }

    /* ── ③ 分頁列（/notifications 溢出 53px）
       .pagination 是 d-flex 不換行，資料多時頁碼會排到 464px
       （實測 1 2 3 … 10 … 21 22），外層又是 justify-content-center，
       於是左邊被推到 -53px、右邊超出視窗。手機允許頁碼折行。 */
    .pagination { flex-wrap: wrap; }

    /* ── ④ .min-w-0（/procurement-1688 溢出 146px）
       view 用 `<div class="flex-grow-1 min-w-0">` 搭配子層 .text-truncate 做截斷，
       但 `.min-w-0` 這個 utility 在 Bootstrap 5 / 本主題裡**根本沒有定義**
       （全專案只有 Procurement/index、shopee/product/online 兩處在用，是 Tailwind 的習慣寫法）。
       沒有 min-width:0 的 flex item 會以「內容的 min-content 寬」為下限，
       而子層 nowrap 的商品標題 min-content = 整行長度 → text-truncate 永遠不會作用。
       補上這個 utility 原本就該有的定義，截斷才會生效。 */
    .min-w-0 { min-width: 0 !important; }

    /* ── ⑤ 沒有包在 .table-responsive 裡的表格（/hr/schedule/create 溢出 83px）
       §31 的 `.table td { white-space: nowrap }` 是刻意的：資料表的欄位不壓縮，
       寬度交給 .table-responsive 橫向捲動。前提是「外面真的有那個捲動容器」。
       但有些頁面把 <table> 當版面用（例：hr/schedule/_form 的「標籤 : 控制項」兩欄表），
       直接放在 .card-body.p-0 裡沒有 .table-responsive —— 這時 nowrap 沒有地方可捲，
       就直接把整份文件頂開（實測那一格的說明文字 356px 不換行）。
       手機下，只有「沒有捲動容器」的表格才把 td 交還給正常換行；
       包在 .table-responsive 裡的資料表維持 nowrap + 橫向捲動，行為完全不變。
       （view 若明確寫 .text-nowrap，Bootstrap 那條是 !important，仍然贏，不受影響。） */
    *:not(.table-responsive) > table.table td { white-space: normal; }
}

/* =============================================================================
 * §file-uploader 外掛的暗色缺口（2026-07-27 全站掃描：/employee/user/informationEdit）
 * -----------------------------------------------------------------------------
 * 根因：`public/assets/js/file-uploader/file-uploader.css` 是外掛原廠樣式，
 * 顏色全部寫死亮色（`.file-item{background:white}`、`.upload-area{background:#f9fafb}`、
 * `.file-name{color:#111827}`…），完全沒有暗色分支。
 * 它不吃我們的 token，所以暗色模式下「帳號照片 / 大頭證件照 / 身分證正反面 / 存摺正反面」
 * 那一整區會變成畫面上刺眼的白色島，白底上再壓 #111827 近黑字。
 *
 * 為什麼修在這裡而不是改外掛檔：本檔是全站樣式 SSOT 且「最後」載入，
 * 覆蓋 plugin 原廠樣式本來就是它的職責（同 §select2 的處理方式）；
 * 直接改外掛檔的話，日後更新外掛就會被蓋回去。
 *
 * ★整段只掛在 html.dark 底下，且只動顏色不動任何尺寸／版型
 *   → 亮色模式一個像素都不變，桌機版面也完全不受影響。
 * ============================================================================= */
html.dark .upload-area {
    background: hsl(var(--muted));
    border-color: hsl(var(--border));
}
html.dark .upload-area:hover,
html.dark .upload-area.drag-over {
    background: hsl(var(--accent));
    border-color: hsl(var(--info));
}
html.dark .upload-icon,
html.dark .upload-text,
html.dark .file-size,
html.dark .file-icon,
html.dark .inline-file-icon,
html.dark .btn-action,
html.dark .btn-inline-action { color: hsl(var(--muted-foreground)); }

html.dark .file-item,
html.dark .inline-file-item {
    background: hsl(var(--card));
    border-color: hsl(var(--border));
}
html.dark .file-preview,
html.dark .inline-preview { background: hsl(var(--muted)); }
html.dark .file-name { color: hsl(var(--foreground)); }
html.dark .file-actions {
    background: hsl(var(--muted));
    border-top-color: hsl(var(--border));
}
html.dark .btn-action:hover,
html.dark .btn-inline-action:hover {
    background: hsl(var(--accent));
    color: hsl(var(--accent-foreground));
}
/* 錯誤狀態：維持「紅色語意」，但換成暗色可讀的低亮度紅底 */
html.dark .file-item[data-status="error"],
html.dark .inline-file-item[data-status="error"] {
    background: hsl(var(--destructive) / .12);
    border-color: hsl(var(--destructive) / .45);
}
/* 疊在圖片上的遮罩原本是半透明白（rgba(255,255,255,.9~.95)），暗色下會整片泛白 */
html.dark .inline-uploading,
html.dark .upload-error { background: hsl(var(--card) / .92); }
html.dark .inline-error { background: hsl(var(--destructive) / .22); }
/* ★.spinner 這個 class 名很通用：全站還有 3 處是「整頁載入動畫」
   （includes/_load、layouts/app、layouts/empty）。這裡只鎖 file-uploader 自己的那顆，
   不要順手改到頁面載入動畫。 */
html.dark .inline-uploading .spinner { border-color: hsl(var(--border)); border-top-color: hsl(var(--info)); }

/* =============================================================================
 * §側欄：對齊 shadcn/ui Sidebar 元件規格（2026-07-28）
 *
 * 業主看正式站截圖提的：「左邊選單可以重新設計過嗎？shadcn 會怎麼設計」
 * 實測既有渲染值與 shadcn 規格的落差：
 *   選單項高 37px（規格 h-8=32）、子項高 32px（規格 h-7=28）、
 *   圓角 10px（用到卡片的 --radius，規格是 rounded-md=6px）、
 *   分組標籤 0 個、子選單沒有左側引導線、
 *   ★「展開中的父項」與「目前所在頁」底色相同 → 視覺上分不出當前位置。
 *
 * shadcn Sidebar 的語彙：
 *   SidebarMenuButton     h-8 rounded-md px-2 gap-2，icon size-4
 *   目前頁 data-active    bg-sidebar-accent + font-medium（不是整條反白）
 *   SidebarMenuSub        mx-3.5 border-l px-2.5 —— 用一條左引導線表達從屬
 *   SidebarMenuSubButton  h-7 text-sm
 *
 * ★只動樣式，_nav.blade.php 的 1432 行結構與所有連結一行未改。
 * ============================================================================= */

/* ── 選單項：32px、6px 圓角 ── */
.app-sidebar .menu-link {
    min-height: 32px;
    padding: 6px 8px;
    margin: 1px 8px;
    gap: 8px;
    border-radius: var(--radius-sm);   /* 10px → 6px，對齊 rounded-md */
    font-size: .875rem;
    line-height: 1.25rem;
}
#sidebar .menu-icon,
.app-sidebar .menu-link .menu-icon {
    width: 16px;
    flex: 0 0 16px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}
.app-sidebar .menu-link .menu-icon svg,
.app-sidebar .menu-link .menu-icon i { width: 16px; height: 16px; }

/* ── ★目前所在頁：淺 accent + 文字加深加粗（shadcn 官方 Sidebar 的
       `data-[active=true]:bg-sidebar-accent text-sidebar-accent-foreground`）。
       原本是整條深色反白（--dark），在密集選單裡太重，而且和展開態撞色。

   ★★2026-08-04 我一度把這裡改成 --dark 深色反轉，那是**改壞的**：
     業主說「你左邊選單不要改淺灰底」，語意是「**不要去動**那個淺灰底」，
     我讀成「不要用淺灰底」→ 改成深色 → 業主：「馬的，我要你恢復正常的左邊選單顏色」。
     ★判準本來就查得到，我沒查就動手：**2026-fatboy 正式站實測是 #efeff0 淺灰底 + #18181b 深字**
       （＝本檔 --sidebar-accent 240 5% 94%），兩站的 sidebar token 一字不差；
       而 shadcn 官方 Sidebar 的 active 本來就是 bg-sidebar-accent，不是反轉色。
     ★教訓：動「業主已經在用、而且正式站也是這樣」的東西之前，先去正式站量一次。 ── */
#sidebar .menu-item.active > .menu-link,
#sidebar .menu-submenu .menu-item.active > .menu-link {
    background: hsl(var(--sidebar-accent));
    color: hsl(var(--sidebar-fg-strong));
    font-weight: 500;
}
#sidebar .menu-item.active > .menu-link .menu-icon,
#sidebar .menu-submenu .menu-item.active > .menu-link .menu-icon {
    color: hsl(var(--sidebar-fg-strong));
    opacity: 1;
}
/* ── ★展開中的父項：不給底色，只靠 caret 轉向表達「已展開」
       —— 這是把「展開」與「所在頁」分開的關鍵，原本兩者同色。 ── */
#sidebar .menu-item.has-sub.expand > .menu-link,
#sidebar .menu-item.expand > .menu-link {
    background: transparent;
    color: hsl(var(--sidebar-fg-strong));
    font-weight: 500;
}
#sidebar .menu-item.has-sub.expand > .menu-link:hover {
    background: hsl(var(--sidebar-accent) / .6);
}
/* 父項本身就是所在頁（例：點了「訂單系統」自己那一頁）才給底色 */
#sidebar .menu-item.has-sub.active:not(.expand) > .menu-link {
    background: hsl(var(--sidebar-accent));
}

/* ── ★子選單：左側引導線（shadcn SidebarMenuSub 的核心視覺） ── */
.app-sidebar .menu-submenu {
    margin: 2px 0 2px 22px;
    padding-left: 10px;
    border-left: 1px solid hsl(var(--sidebar-border));
}
.app-sidebar .menu-submenu .menu-link {
    min-height: 28px;
    padding: 4px 8px;
    margin: 1px 0;
    font-size: .8125rem;
    border-radius: var(--radius-sm);
}
/* 子項沒有圖示欄，文字直接靠左（原本會空一格圖示位） */
.app-sidebar .menu-submenu .menu-link .menu-icon { display: none; }

/* ── caret：展開時轉 90 度（shadcn 的 chevron 行為） ── */
.app-sidebar .menu-caret {
    margin-left: auto;
    transition: transform .15s ease;
    display: inline-flex;
    align-items: center;
}
.app-sidebar .menu-caret svg { width: 14px; height: 14px; }
/* ★已被 §側欄二修：靠左 覆蓋（實測生效值：transform=matrix(0, 1, -1, 0, 0, -8)） */
#sidebar .menu-item.expand > .menu-link .menu-caret { transform: rotate(90deg); }

/* ── 分組標籤（SidebarGroupLabel）：小字、次要色、不可點的語氣 ── */
/* ★2026-08-04 稽核：blade 0 命中 */
.app-sidebar .menu-header {
    height: 32px;
    display: flex;
    align-items: center;
    padding: 0 16px;
    margin-top: 8px;
    font-size: .75rem;
    font-weight: 500;
    letter-spacing: .01em;
    color: hsl(var(--sidebar-fg) / .7);
    text-transform: none;
}

/* =============================================================================
 * §表格列內動作鈕與縮圖：對齊 shadcn（2026-07-28）
 *
 * 業主看 /inventory/product 與 /inventory/consumable 提的：
 * 「編輯的按鈕 shadcn 會怎麼做」「商品的圖片怎麼學習 shadcn 設計」
 *
 * 實測既有值：
 *   編輯鈕 32×32、實心黑 rgb(24,24,27)、圓角 10px、只有圖示
 *     → 一張 15 列的表就是 15 個實心黑方塊，視覺噪音蓋過資料本身。
 *   縮圖 40×40、圓角 12px、無邊框
 *     → 12px 是卡片的 --radius，套在 40px 縮圖上過圓；沒有邊框，
 *       淺色商品圖會跟白底融在一起看不出邊界。
 *
 * shadcn 的做法：
 *   列內動作 = Button variant="ghost" size="icon"
 *              h-8 w-8 rounded-md，底色透明，hover 才給 accent，icon size-4
 *              （動作鈕不該跟「主要動作」搶視覺權重）
 *   縮圖     = rounded-md + border + bg-muted 佔位
 * ============================================================================= */

/* ── 列內動作鈕：實心 → ghost ── */
/* ★用後代選擇器不是直接子選擇器：實測有些頁面的動作鈕外面包了一層 div，
   寫成 td > .btn 會漏掉。圓角要 !important —— .btn 那條與 .rounded-3 的
   !important 都會蓋過來（實測改完仍是 10px 才發現）。 */
.app-content td .btn.btn-xs.btn-dark:not(.btn-rounded):not([class*="rounded-pill"]),
.app-content td a.btn.btn-xs.btn-dark:not(.btn-rounded):not([class*="rounded-pill"]) {
    background: transparent;
    border-color: transparent;
    color: hsl(var(--foreground));
    border-radius: var(--radius-md) !important;   /* 與 .fb-row-action 同階 */
    box-shadow: none;
}
.app-content td .btn.btn-xs.btn-dark:not(.btn-rounded):hover,
.app-content td a.btn.btn-xs.btn-dark:not(.btn-rounded):hover,
.app-content td .btn.btn-xs.btn-dark:not(.btn-rounded):focus-visible {
    background: hsl(var(--accent));
    border-color: transparent;
    color: hsl(var(--accent-foreground));
}
.app-content td .btn.btn-xs.btn-dark svg { width: 16px; height: 16px; }

/* ★★ 2026-07-28 逐頁量測補齊：上面那條只鎖 .btn-dark，但 views 內「列內動作鈕」
   有 312 處是 btn-light（btn-dark 只有 192 處）——多數反而沒吃到 ghost 標準。
   實測 /hr/remote 同一列：審核(btn-dark)=透明 ghost、編輯(btn-light)=實心近黑、
   刪除(btn-danger)=實心紅，同一列三顆鈕三個樣。
   這裡把中性變體（light / white / secondary / default）一併收進同一套 ghost。

   ★只鎖 a / button：`<span class="btn btn-xs …">` 在本專案是「唯讀狀態 chip」
     （uiux.md 明列狀態徽章寫法），不是動作鈕，不能被 ghost 洗掉底色。
   ★.btn-rounded / rounded-pill 一律排除：那是狀態徽章的形狀語意。 */
.app-content td a.btn.btn-xs.btn-light:not(.btn-rounded):not([class*="rounded-pill"]),
.app-content td button.btn.btn-xs.btn-light:not(.btn-rounded):not([class*="rounded-pill"]),
.app-content td a.btn.btn-xs.btn-white:not(.btn-rounded):not([class*="rounded-pill"]),
.app-content td button.btn.btn-xs.btn-white:not(.btn-rounded):not([class*="rounded-pill"]),
.app-content td a.btn.btn-xs.btn-secondary:not(.btn-rounded):not([class*="rounded-pill"]),
.app-content td button.btn.btn-xs.btn-secondary:not(.btn-rounded):not([class*="rounded-pill"]),
.app-content td a.btn.btn-xs.btn-default:not(.btn-rounded):not([class*="rounded-pill"]),
.app-content td button.btn.btn-xs.btn-default:not(.btn-rounded):not([class*="rounded-pill"]) {
    background: transparent;
    border-color: transparent;
    color: hsl(var(--foreground));
    border-radius: var(--radius-md) !important;   /* 與 .fb-row-action 同階 */
    box-shadow: none;
}
.app-content td a.btn.btn-xs.btn-light:not(.btn-rounded):not([class*="rounded-pill"]):hover,
.app-content td button.btn.btn-xs.btn-light:not(.btn-rounded):not([class*="rounded-pill"]):hover,
.app-content td a.btn.btn-xs.btn-white:not(.btn-rounded):not([class*="rounded-pill"]):hover,
.app-content td button.btn.btn-xs.btn-white:not(.btn-rounded):not([class*="rounded-pill"]):hover,
.app-content td a.btn.btn-xs.btn-secondary:not(.btn-rounded):not([class*="rounded-pill"]):hover,
.app-content td button.btn.btn-xs.btn-secondary:not(.btn-rounded):not([class*="rounded-pill"]):hover,
.app-content td a.btn.btn-xs.btn-default:not(.btn-rounded):not([class*="rounded-pill"]):hover,
.app-content td button.btn.btn-xs.btn-default:not(.btn-rounded):not([class*="rounded-pill"]):hover {
    background: hsl(var(--accent));
    border-color: transparent;
    color: hsl(var(--accent-foreground));
}

/* 破壞性動作 = shadcn 的 destructive ghost：透明底 + 紅字，hover 才給淡紅底。
   （實心紅方塊在列表裡會比「編輯」還搶眼，主次顛倒） */
.app-content td a.btn.btn-xs.btn-danger:not(.btn-rounded):not([class*="rounded-pill"]),
.app-content td button.btn.btn-xs.btn-danger:not(.btn-rounded):not([class*="rounded-pill"]) {
    background: transparent;
    border-color: transparent;
    color: hsl(var(--destructive-fg));
    border-radius: var(--radius-sm) !important;
    box-shadow: none;
}
.app-content td a.btn.btn-xs.btn-danger:not(.btn-rounded):not([class*="rounded-pill"]):hover,
.app-content td button.btn.btn-xs.btn-danger:not(.btn-rounded):not([class*="rounded-pill"]):hover {
    background: hsl(var(--destructive) / .1);
    border-color: transparent;
    color: hsl(var(--destructive-fg));
}

/* ── 列內動作鈕尺寸統一（2026-07-28 量測）──
   問題：同一列的動作鈕高度取決於「網址字串裡有沒有 edit/view/modify」——
   上面 §表格內動作圖示鈕 那條 href 選擇器命中的是 32px，沒命中的是 btn-xs 原生 22.5px。
   實測 /hr/remote 同一列：審核 32px、編輯 32px、刪除 22.5px（刪除沒有 href）。
   用網址決定尺寸沒有道理，而且全站實測 32px 本來就是多數
   （40 頁抽樣：32px 有 315 顆、22.5px 188 顆、25.1px 56 顆）。
   → 一律收斂到 shadcn 的 h-8 w-8（32px），與既有多數一致。
   用 min-* 不用 height：帶文字的動作鈕（如「轉錢」「強制登出」）要能自己撐開。 */
.app-content td a.btn.btn-xs:not(.btn-rounded):not([class*="rounded-pill"]),
.app-content td button.btn.btn-xs:not(.btn-rounded):not([class*="rounded-pill"]) {
    min-height: 32px;
    min-width: 32px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

/* icon 尺寸比照 shadcn size-4，與 btn-dark 那條一致 */
.app-content td a.btn.btn-xs.btn-light svg,
.app-content td button.btn.btn-xs.btn-light svg,
.app-content td a.btn.btn-xs.btn-white svg,
.app-content td button.btn.btn-xs.btn-white svg,
.app-content td a.btn.btn-xs.btn-secondary svg,
.app-content td button.btn.btn-xs.btn-secondary svg,
.app-content td a.btn.btn-xs.btn-default svg,
.app-content td button.btn.btn-xs.btn-default svg,
.app-content td a.btn.btn-xs.btn-danger svg,
.app-content td button.btn.btn-xs.btn-danger svg { width: 16px; height: 16px; }

/* ── 表格縮圖 ── */
.app-content td img.fb-thumb,
.app-content td .fb-thumb > img,
.app-content table td > img:not(.rounded-circle):not([class*="rounded-pill"]) {
    border-radius: var(--radius-sm) !important;
    border: 1px solid hsl(var(--border));
    background: hsl(var(--muted));
    object-fit: cover;
}

/* =============================================================================
 * §側欄二修：靠左、深層巢狀、badge 擠壓（2026-07-28 業主三張截圖）
 *
 * 症狀（實測）：
 *   ① 選單整體偏右 —— 圖示距側欄左緣 28px、文字 54px
 *      （shadcn：SidebarGroup p-2 + SidebarMenuButton p-2 → 圖示 16px）
 *   ② 三／四層巢狀縮排累加 → 「銷售量體分布」「商品毛利分布」被擠成兩行
 *   ③ badge 參與 flex 佔位 → 「異常警告中心 116」標題斷成「異常警告 / 中心」
 *
 * shadcn Sidebar 的解法：
 *   · SidebarMenuButton 有 overflow-hidden，文字一律 truncate —— **選單文字永遠不換行**
 *   · SidebarMenuBadge 是 absolute right-1，不佔 flex 空間，不會擠壓標題
 *   · SidebarMenuSub 每層只縮 mx-3.5 px-2.5（14+10px），不是層層累加大 padding
 * ============================================================================= */

/* ── ① 靠左：對齊 shadcn 的 8+8
   ★用 #sidebar 前綴不是 .app-sidebar：既有的 `#sidebar .menu-link{padding:.5rem .75rem}`
     是 ID 選擇器（1,1,0），.app-sidebar .menu-link 只有 (0,2,0) 打不贏。
     用 CDP getMatchedStylesForNode 才查出來是這條在贏 —— 光看 computed 值只知道沒生效、
     不知道被誰蓋。 */
#sidebar .menu-link,
#sidebar .menu-submenu .menu-link {
    padding-left: 8px;
    padding-right: 8px;
}
/* ★shadcn 只有「一層」8px：SidebarGroup p-2 提供外距，SidebarMenuButton p-2 提供內距，
   圖示落在 16px。這裡原本是「容器 padding 8 + 連結 margin 8 + 內距 8」三層疊加 → 圖示 24px。
   把連結的左右 margin 收掉，改由 .menu 容器統一給，才會落在 16px。 */
#sidebar .menu { padding-left: 8px; padding-right: 8px; }
#sidebar .menu > .menu-item > .menu-link { margin-left: 0; margin-right: 0; }
#sidebar .menu-submenu .menu-link { margin-left: 0; margin-right: 0; }

/* ── ② 文字一律 truncate，不換行（shadcn 硬規則） ── */
#sidebar .menu-link { overflow: hidden; }
.app-sidebar .menu-link .menu-text,
.app-sidebar .menu-submenu .menu-link {
    min-width: 0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
/* 子項本身是 flex 容器時，讓文字節點可壓縮 */
.app-sidebar .menu-submenu .menu-link > * { min-width: 0; }

/* ── 深層巢狀：對齊 shadcn SidebarMenuSub 的 mx-3.5 px-2.5（14+10=24px/層）
   實測原本每層縮 49px，220px 寬的側欄到第二層只剩 124px 給文字，
   深層項目（銷售量體分布／商品毛利分布）就被擠爆。
   ★用 #sidebar 前綴 —— 同樣是 ID 特異性問題，.app-sidebar 打不贏。 */
#sidebar .menu-submenu {
    margin: 2px 0 2px 14px;
    padding-left: 10px;
    border-left: 1px solid hsl(var(--sidebar-border));
}
#sidebar .menu-submenu .menu-submenu { margin-left: 8px; padding-left: 8px; }

/* ── ③ badge 不參與佔位（shadcn absolute right-1） ── */
.app-sidebar .menu-item { position: relative; }
#sidebar .menu-link { position: relative; }
#sidebar .menu-item.has-sub > .menu-link { padding-right: 44px; }
/* ★2026-08-04 稽核：blade 0 命中 */
.app-sidebar .menu-badge {
    position: absolute;
    right: 26px;                 /* 讓開右側的 caret */
    top: 50%;
    transform: translateY(-50%);
    pointer-events: none;
    font-size: .6875rem;
    line-height: 1;
    padding: 3px 6px;
}
/* 沒有 caret 的項目（不可展開）badge 可以更靠右 */
/* ★2026-08-04 稽核：blade 0 命中 */
#sidebar .menu-item:not(.has-sub) > .menu-link .menu-badge { right: 8px; }
#sidebar .menu-item:not(.has-sub) > .menu-link:has(.menu-badge) { padding-right: 40px; }
/* caret 也絕對定位，才不會跟 badge 互推 */
.app-sidebar .menu-caret {
    position: absolute;
    right: 8px;
    top: 50%;
    transform: translateY(-50%);
    margin-left: 0;
}
#sidebar .menu-item.expand > .menu-link .menu-caret { transform: translateY(-50%) rotate(90deg); }

/* =============================================================================
 * §側欄租戶區塊 = shadcn TeamSwitcher（2026-07-28）
 *
 * 業主：「這是屬於租戶區域」「shadcn 會怎麼做」。
 * shadcn 對「你現在在哪個組織／租戶」有專門的樣式 —— sidebar-07 block 的
 * SidebarHeader > TeamSwitcher：
 *     SidebarMenuButton size="lg"      h-12 = 48px
 *     ├ div size-8 rounded-lg          32px logo
 *     ├ div grid flex-1 text-left
 *     │   ├ span truncate font-semibold text-sm   組織名
 *     │   └ span truncate text-xs                 方案／層級
 *     └ ChevronsUpDown ml-auto         表示「可切換」
 *
 * 實測現況：整塊 91px、連結 74px（shadcn 是 48px）。
 * 膨脹的原因是 .menu-profile-info 裡有兩個 d-flex 直向堆疊
 * （名稱+編號一組、續約鈕+caret 一組）→ 變成三行。
 * ★shadcn 的語彙裡「續約訂閱」就是第二行的「方案」，不該另起一行堆 badge。
 *
 * 純 CSS 收斂成兩行，markup 一行未改（文字、popover、連結行為全部保留）。
 * ============================================================================= */

#sidebar .menu-profile { padding: 8px; margin-bottom: 4px; }

/* 裝飾用的彈跳球 canvas：shadcn 的 header 是乾淨的，不放裝飾動畫 */
#sidebar .menu-profile-cover { display: none !important; }

#sidebar .menu-profile-link {
    display: flex;
    align-items: center;
    /* ★已被 §S-39 覆蓋（實測生效值：gap=0px） */
    gap: 8px;
    height: 48px;                      /* shadcn size="lg" */
    padding: 8px;
    border-radius: var(--radius-sm);
    overflow: hidden;
    transition: background-color .12s ease;
}
#sidebar .menu-profile-link:hover { background: hsl(var(--sidebar-accent)); }

#sidebar .menu-profile-image {
    width: 32px; height: 32px;
    flex: 0 0 32px;
    border-radius: 8px;                /* rounded-lg */
    overflow: hidden;
}
#sidebar .menu-profile-image img { width: 100%; height: 100%; object-fit: cover; }

/* 文字區：兩行 grid，兩行都 truncate */
#sidebar .menu-profile-info {
    display: grid;
    /* ★兩行必須塞進 48px − 上下 padding 16px = 32px 的內容區，
       否則會被 overflow:hidden 裁掉半行（實測沒鎖行高時是 44px，超出 12px）。
       第一行 18px + 第二行 14px = 32px，剛好。 */
    grid-template-rows: 16px 16px;   /* 16+16 = 32px 內容區，第二行要塞得下續約 chip */
    align-content: center;
    flex: 1 1 auto;
    min-width: 0;
    gap: 0;
    font-size: .875rem;
    font-weight: 600;                  /* 第一行＝組織名 */
}
#sidebar .menu-profile-info .tenant-name,
#sidebar .menu-profile-info .tenant-meta { line-height: 16px; }
/* 兩行都 truncate（shadcn 的 SidebarMenuButton 是 overflow-hidden + truncate） */
#sidebar .menu-profile-info .tenant-name,
#sidebar .menu-profile-info .tenant-meta {
    min-width: 0;
    /* ★〔#sidebar .menu-profile-info .tenant-name〕已被 §S-33 覆蓋；〔#sidebar .menu-profile-info .tenant-meta〕未被覆蓋（實測生效值：display=block） */
    display: flex;
    /* ★〔#sidebar .menu-profile-info .tenant-meta〕已被 §S-35 覆蓋；〔#sidebar .menu-profile-info .tenant-name〕未被覆蓋（實測生效值：align-items=center） */
    align-items: center;
    /* ★gap 4→2（2026-07-29）：第二行擠了三個東西（租戶碼 + ⓘ + 訂閱狀態 badge），
       量到 113px 的行寬要塞 43+12+53=108 加兩個 4px 間距 = 116 → 溢出 3px，
       全部由租戶碼吸收，被截成「C0…」（桌機手機都一樣）。
       租戶碼是識別碼，截掉就失去意義（名稱截了還看得懂，代號不行）→ 優先餵給它。
       仍保留可縮排：超長租戶碼還是會出省略號，不會撐破側欄。 */
    /* ★〔#sidebar .menu-profile-info .tenant-meta〕已被 §S-36 覆蓋；〔#sidebar .menu-profile-info .tenant-name〕未被覆蓋（實測生效值：gap=2px） */
    gap: 2px;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* 訂閱狀態 badge 文字短且固定，不參與壓縮 —— 讓壓縮壓力留在可截斷的租戶碼上 */
#sidebar .menu-profile-info .tenant-meta > .btn { flex: 0 0 auto; }
#sidebar .menu-profile-info .tenant-name { font-size: .875rem; font-weight: 600; }
/* ★★2026-08-06 `.tenant-code` 全檔共 6 條宣告，這是第 1 條。實測勝出者如下
   （登入後 /main 用 CDP CSS.getMatchedStylesForNode 取「真正勝出的規則」，非推論）：
     overflow / text-overflow / min-width → §S-34「側欄第二行代號+ⓘ+續約訂閱擠不下」
                                            實測 overflow=hidden、text-overflow=ellipsis、min-width=0px
     flex（grow/shrink）                   → §S-36「側欄帳戶區照 shadcn NavUser 重新收斂」
                                            實測 flex-grow=0、flex-shrink=1（即 0 1 auto）
     color                                 → §側欄 profile 的對比校正段，實測 rgba(82,82,91,0.9)
   ⇒ 本區塊除了 font-size / font-weight 之外，**其餘宣告都不生效**。
     六條裡有五條在 overflow/flex 上互相覆蓋（hidden↔visible、ellipsis↔clip、
     0 0 auto→1 1 auto→0 1 auto 來回改），只有最後一條算數。要調這些屬性請直接改勝出的章節，
     改本區塊沒有效果。原有的行號引用（L8699/L8004）已漂掉，改引章節名。 */
#sidebar .menu-profile-info .tenant-code {
    /* ★已被 §S-34 覆蓋（實測生效值：overflow=hidden；text-overflow=ellipsis） */
    overflow: hidden; text-overflow: ellipsis;
    font-size: .75rem; font-weight: 400;
    /* ★已被 §S-17 覆蓋（實測生效值：color=rgba(82, 82, 91, 0.9)） */
    color: hsl(var(--sidebar-fg) / .75);
}
#sidebar .menu-profile-info .tenant-meta svg { width: 12px; height: 12px; flex: 0 0 12px; color: hsl(var(--sidebar-fg) / .6); }
/* 第二行：客戶代號 + 續約訂閱，並排、次要色、可截斷 */
#sidebar .menu-profile-info h6 {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    margin: 0;
    font-size: .75rem;
    font-weight: 400;
    /* ★★ 2026-07-30 稽核：alpha 由 .75 → .9。
       .75 疊在 --sidebar-bg 上實測 **亮色 3.97 / 暗色 4.44，兩個模式都沒過 AA 4.5**，
       而這是「客戶代號」——業主報修時要唸出來的識別碼，不是裝飾字，全站每頁都在。
       .9 實測亮色 5.72 / 暗色 5.84，兩邊都過。
       ★這裡動的是**本主題自己的 alpha 修飾**，不是 --sidebar-fg 這顆與 2026-fatboy
         共用的 token，所以不會讓兩專案的配色基準漂移。
       視覺層次沒有被抹平：第一行組織名仍是 14px/600 的 --sidebar-fg-strong，
       這行是 12px/400 的 --sidebar-fg，靠字級與字重就分得出主次，不必再靠壓對比。 */
    color: hsl(var(--sidebar-fg) / .9) !important;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
#sidebar .menu-profile-info h6 svg { width: 12px; height: 12px; }
/* ★「續約訂閱」＝ shadcn 的第二行「方案」，收成 inline chip 不另起一行 */
#sidebar .menu-profile-info .btn-yellow {
    /* ★必須壓進 16px 的第二行：實測沒壓時 chip 是 21px，
       整個文字區 scrollHeight 47 > clientHeight 32，會被 overflow:hidden 裁掉。
       10px 字 + line-height 12 + 上下 padding 1 + 邊框 1 = 16px。 */

    /* ★★ 2026-07-30 稽核複驗 —— 兩件事，一件平反、一件是真 bug：
       (1) 「16px 高不能放大」的說法**仍然成立**，重量過：
             .menu-profile-link 是 shadcn SidebarMenuButton size="lg" 的 48px，
             扣掉上下 padding 8px = 32px 內容區，
             .menu-profile-info 用 `grid-template-rows: 16px 16px` 切成兩行 + overflow:hidden。
           把 chip 放到 shadcn Badge 的標準尺度（12px 字 / line-height 16 / padding 2px 8px＝22px）
             → info scrollHeight 35 > clientHeight 32，**底部實測被裁 3px**。
           退一步只做 18px（12px 字 + padding 2px）→ scrollHeight 33，**仍被裁 1px**。
           所以 16px 高是版面硬限制，不是隨手寫的數字 → 正式列為規格例外，
           已寫進 `.agent/shadcn-migration.md` 的例外清單。
       (2) 但「10px 字」是 **寫了沒生效**：後面 §側欄 profile 那組
             `.app-sidebar .menu-profile-info .btn-yellow` 帶 `font-size: 11px !important`，
             !important 不比特異度，本行沒有 !important 就輸掉了 → 實測computed 是 11px / 15.4px。
           不論 10 還是 11，都低於 Phase 3 訂的「字級低於 12px 歸零」。
           實測 12px 字 + line-height 12 + 上下 padding 1 + 邊框 1 = **剛好 16px，零裁切**，
           而且第二行寬度 scrollWidth 113 = clientWidth 113，**沒有橫向溢出、租戶碼不會被截**
           （chip 寬 53.3 → 57.3，那 4px 由既有留白吸收）。
           → 字級收斂到 12px，高度維持 16px 的例外。三個屬性都補 !important 才贏得過上游。 */
    font-size: 12px !important;
    line-height: 12px !important;
    padding: 1px 4px !important;
    /* ★margin-left 6px 拿掉（2026-07-29）：那是這行還在 inline 排版時代留下的，
       現在 .tenant-meta 已經是 flex + gap:2px，間距被算兩次（6+2=8px），
       擠掉的正好是租戶碼的顯示空間 → C00001 被截成「C0…」。
       間距一律交給 gap 這個唯一來源，避免同一件事兩處定義。 */
    margin: 0 !important;
    height: 16px;
    display: inline-flex;
    align-items: center;
    vertical-align: middle;
}
/* caret 改成右側的「可切換」指示，不再佔一整行 */
/* ★已被 L8670 覆蓋（實測生效值：padding-right=0px） */
#sidebar .menu-profile-info { position: relative; padding-right: 18px; }
#sidebar .menu-profile-info > .menu-caret {
    /* ★已被 §S-34 覆蓋（實測生效值：position=static） */
    position: absolute;
    right: 0; top: 50%;
    /* ★已被 §S-34 覆蓋（實測生效值：transform=none） */
    transform: translateY(-50%);
    margin: 0;
}

/* =============================================================================
 * §頂 bar = shadcn SiteHeader（2026-07-28 業主：「左邊選單跟上方選單用 shadcn 邏輯重新設計一次」）
 *
 * 實測現況：列高 50px、白底、下邊框 1px zinc-200（這幾項本來就對）
 * ★問題：可點元素有兩種高度混用 —— 32px（暗色切換、通知）與 40px（前台下拉、使用者選單）
 *
 * shadcn 的 header 裡所有動作都是 Button variant="ghost"：
 *   size="icon" → h-8 w-8（32×32）
 *   size="sm"   → h-8（32px），px-2、gap-2、rounded-md
 * 一律 32px，沒有 40px 這一階。
 * ============================================================================= */

#header .navbar-nav > .navbar-item > .navbar-link,
.app-header .navbar-nav > .navbar-item > .navbar-link,
#header .navbar-nav > .navbar-item > a,
.app-header .navbar-nav > .navbar-item > a {
    height: 32px;
    min-height: 32px;
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 0 8px;
    border-radius: var(--radius-md);   /* 與 btn-sm 同階 */
    font-size: .875rem;
    line-height: 1;
}
/* ★只有圖示的要是 32×32 正方形，但 CSS 判斷不了「有沒有文字」。
   第一版用 :not(:has(.navbar-item-text)):not(:has(img)) 當「只有圖示」的條件，
   結果把「前台」下拉也壓成 32px 寬、文字整個看不見（實測寬 32、文字「前台」）。
   改用 min-width：只有圖示的自然是 16px 圖示 + 左右 8px = 32px，
   有文字的自己撐開，不需要 CSS 去猜。 */
#header .navbar-nav > .navbar-item > a,
.app-header .navbar-nav > .navbar-item > a {
    min-width: 32px;
    justify-content: center;
}
#header .navbar-nav .navbar-link:hover,
.app-header .navbar-nav .navbar-link:hover,
#header .navbar-nav > .navbar-item > a:hover,
.app-header .navbar-nav > .navbar-item > a:hover {
    background: hsl(var(--accent));
    color: hsl(var(--accent-foreground));
}
/* 使用者頭像：shadcn Avatar 在 header 是 size-8 的圓角方形 */
#header .navbar-nav img,
.app-header .navbar-nav img {
    width: 24px; height: 24px;
    border-radius: 6px;
    object-fit: cover;
}

/* =============================================================================
 * §focus ring 與日期選擇器面板：對齊 shadcn（2026-07-28）
 *
 * 業主：「為什麼日期篩選下拉會有一個黑色陰影 這是 shadcn 的嗎？」
 *
 * 實測：日期輸入框 outline 是 `2px solid rgb(9,9,11)`（近黑實心）
 *       ＋ border-color 也被改成近黑 → 視覺上就是一圈厚黑框。
 *
 * ★token 本身沒錯：--ring: 240 10% 3.9% 就是 shadcn zinc 主題 light 模式的官方值。
 *   錯的是套用方式：
 *     現在   outline: 2px solid + outline-offset: 1px  → 緊貼邊框，看起來像加粗黑框
 *     shadcn ring-2 ring-ring ring-offset-2            → 環與元素之間隔 2px 背景色間隙
 *   間隙讓環「浮」在外面而不是「包」在身上，這是 shadcn focus 看起來輕的原因。
 *
 * ★同一份主題裡 .btn:focus-visible（L1757）用的就是正確的間隙環寫法，
 *   表單控制項卻用硬 outline —— 兩套標準並存。統一成按鈕那套。
 * ============================================================================= */

.form-control:focus,
.form-select:focus,
.page-link:focus-visible,   /* ★2026-08-04 稽核：原為 :focus —— <a> 用 :focus
                               連滑鼠點擊都會留著焦點框（點完第 3 頁那顆一直亮）。
                               shadcn 一律用 :focus-visible（只有鍵盤導覽才亮）。
                               ★同組的 .form-control/.form-select 維持 :focus 是對的：
                                 瀏覽器對文字輸入框本來就把點擊也算 focus-visible，
                                 而且輸入中看得到框是使用者預期。 */
.dropdown-item:focus-visible,
.form-check-input:focus {
    outline: none;
    /* ★§S-73 改寫（原文兩行完整保留在下面存查）：
         原：box-shadow: 0 0 0 2px hsl(var(--background)), 0 0 0 4px hsl(var(--ring) / .5);
             /  內圈＝背景色間隙（等同 ring-offset-2），外圈＝ring 本體  /
             border-color: hsl(var(--input));
             /  邊框不再改成近黑 —— 那會跟 ring 疊成雙層黑框  /
       ★「邊框不再改成近黑」這句在當時是對的（那時 --ring 是近黑 240 10% 3.9%，
         換上去確實會跟外環疊成雙層黑框）。但 §S-72 已把 --ring 改成官方中灰，
         前提消失了，而且官方本來就是 border 換 ring 色 ＋ 3px 柔環兩層並存：
           input/textarea/select/checkbox 全部是
           `focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50`。
         這條把 border 釘回 --input，正是本專案「只抄了一半」的所在，改回官方。 */
    box-shadow: 0 0 0 3px hsl(var(--ring) / .5);
    border-color: hsl(var(--ring));
}

/* ── daterangepicker 面板 = shadcn Popover ──
   實測圓角 12px（那是卡片的 --radius+2）、陰影 rgba(0,0,0,.25) 8px 24px（比 shadow-md 重很多）。
   shadcn 的 Popover 是 rounded-md + border + shadow-md。 */
.daterangepicker {
    border-radius: var(--radius-md);
    box-shadow: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .1);
    border: 1px solid hsl(var(--border));
}
.daterangepicker .ranges li { border-radius: var(--radius-sm); }
/* 選中的快捷選項：shadcn 的 Popover 選項是 accent 底色，不是實心反白 */
.daterangepicker .ranges li.active {
    background: hsl(var(--accent));
    color: hsl(var(--accent-foreground));
    font-weight: 500;
}

/* ── SSOT 元件：x-row-action（列內動作鈕）與 x-thumb（商品縮圖）的樣式 ──
   建立原因見兩支元件檔的註解：全站盤點出
   57 顆帶筆圖示的按鈕用了 20 種 class 組合、33 處商品圖用了 22 種寫法。 */
.fb-row-action {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px; height: 32px;          /* shadcn size="icon" */
    padding: 0;
    border: 0;
    background: transparent;            /* variant="ghost" */
    color: hsl(var(--foreground));
    border-radius: var(--radius-md);   /* shadcn Button 一律 rounded-md */
    transition: background-color .12s ease, color .12s ease;
    cursor: pointer;
}
.fb-row-action:hover,
.fb-row-action:focus-visible {
    background: hsl(var(--accent));
    color: hsl(var(--accent-foreground));
}
.fb-row-action:focus-visible {
    outline: none;
    /* §S-73：原 `0 0 0 2px hsl(var(--background)), 0 0 0 4px hsl(var(--ring) / .5)` → 官方兩層 */
    border-color: hsl(var(--ring));
    box-shadow: 0 0 0 3px hsl(var(--ring) / .5);
}
.fb-row-action svg { width: 16px; height: 16px; }
.fb-row-action-danger { color: hsl(var(--destructive-fg)); }
.fb-row-action-danger:hover { background: hsl(var(--destructive) / .1); color: hsl(var(--destructive-fg)); }

/* ★2026-08-09 新增（純追加，未改寫上方任何一條既有規則）：`.fb-row-action` 的 disabled 態。
   實測發現的問題：訂單商品頁（/order-management/orders/modify/{no}/item）當某商品已全數配貨時，
   「替換商品」「刪除商品」兩顆鈕會被正確加上 disabled 屬性（業務邏輯沒問題），
   但量測得 `opacity:1; cursor:pointer` —— 視覺上與可點的「調整數量」「修改售價」完全相同。
   使用者點下去毫無反應（原生 disabled button 不觸發 click，也不會有 console error），
   會誤以為功能壞掉。★這種「靜默無回饋」比報錯更難查。
   根因：上方 `.fb-row-action` 區塊只定義了 :hover / :focus-visible，
   而第 7518 行的 `cursor: pointer` 是寫死的，disabled 狀態沒有任何覆寫。
   ★對照組：批次操作鈕（訂單列表未勾選時）的 disabled 是正常變灰的，
   所以這是 .fb-row-action 這個元件的個案，不是全站規律。
   值的選擇：opacity .4 與 not-allowed 沿用主題既有 disabled 語彙，不新增變數。
   pointer-events:none 是為了讓 hover 背景也一併失效（否則滑過去仍會亮起 accent 底色，
   又是一個「看起來可以點」的訊號）。 */
.fb-row-action:disabled,
.fb-row-action[disabled],
.fb-row-action.disabled {
    opacity: .4;
    cursor: not-allowed;
    pointer-events: none;
}

.fb-thumb {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: var(--fb-thumb-size, 40px);
    height: var(--fb-thumb-size, 40px);
    flex: 0 0 var(--fb-thumb-size, 40px);
    border-radius: var(--radius-md);   /* rounded-md，與 Button 同階 */
    border: 1px solid hsl(var(--border));
    background: hsl(var(--muted));      /* 圖沒載入時的佔位，版面不會塌 */
    overflow: hidden;
}
/* 圓形＝頭像語意（原本全站有 3 處 rounded-circle 的人物頭像，形狀不能被方形洗掉） */
/* ★2026-08-04 稽核：blade 0 命中 */
.fb-thumb-circle { border-radius: 9999px; }
/* ★★ 2026-07-29（業主回報「右上角按鈕不見」時一併查到）：
   元件必須自己擁有內部尺寸 —— 外部那些「針對裸 <img> 寫的尺寸規則」
   會直接穿透進 .fb-thumb 把圖片壓小，容器與圖片對不齊就露出一圈底色。
   實測命中的有三條，特異性都比 .fb-thumb img (0,1,1) 高：
     #header .navbar-nav img / .app-header .navbar-nav img   (1,1,1) width:24px
     .app-header .navbar-user img                            (0,2,1) width:30px
     .app-content table td img:not(...)                      max-width:56px !important
   → 頭像實測被壓成 24px 塞在 32px 容器裡（露出 8px 底色圈），
     而 80px 的縮圖放進表格會被 56px 上限截掉。
   這些規則本身沒錯，是為裸 <img> 寫的；元件化之後要把邊界劃清楚：
   尺寸由 --fb-thumb-size 決定，內部 img 一律填滿，外部管不到。 */
.fb-thumb > img {
    /* ★★ 不只擋尺寸，位置相關的也要一起收回（2026-07-29 第二輪修，
       業主回報頭像「會被切到」）：
       `.app-header .navbar-user img` 除了 width/height，還帶
         margin-top:-5px; margin-bottom:-5px; margin-right:10px; border-radius:30px
       第一輪我只擋了 width/height/max-*，margin 漏掉 →
       實測圖片 computed margin 是 `-5px 5px -5px 0`，
       上下負邊距把圖往上拉出裁切區、右邊距把它往左推，
       在 overflow:hidden 的容器裡就被切掉一角。
       ★教訓：元件要「擁有」內部盒模型，就得把整組盒模型屬性都收回來，
         只擋寬高等於只擋了一半。 */
    width: 100% !important;
    height: 100% !important;
    max-width: none !important;
    max-height: none !important;
    min-width: 0 !important;
    min-height: 0 !important;
    margin: 0 !important;
    padding: 0 !important;
    border: 0 !important;
    border-radius: 0 !important;   /* 形狀由容器的 overflow:hidden 決定 */
    position: static !important;
    transform: none !important;
    float: none !important;
    display: block !important;
}
.fb-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* ★2026-08-04 稽核：blade 0 命中 */
.fb-thumb .fb-thumb-empty { width: 40%; height: 40%; color: hsl(var(--muted-foreground)); opacity: .6; }
/* ★2026-08-04 稽核：blade 0 命中 */
.fb-thumb-link { display: inline-block; line-height: 0; }


/* =============================================================================
 * §shadcn 收尾三項（2026-07-29 全站實測後確認僅剩這三處未對齊）
 * ★用「主題規則數」盤點會嚴重高估缺口 —— Spinner／Switch／Pagination／Alert／
 *   ListGroup 實測都已吃到 token，只是沒寫「shadcn」註解。以下是實際量出
 *   computed style 與 shadcn 規格不符的三項。
 * ============================================================================= */

/* ① Slider：form-range 的 accent-color 實測是 auto → 滑桿與軌道用瀏覽器預設藍，
      是全站唯一還在用瀏覽器原生配色的控制項。 */
.form-range { accent-color: hsl(var(--dark)); }
.form-range:focus-visible {
    outline: none;
    /* §S-73：原 `0 0 0 2px hsl(var(--background)), 0 0 0 4px hsl(var(--ring) / .5)` → 官方兩層 */
    border-color: hsl(var(--ring));
    box-shadow: 0 0 0 3px hsl(var(--ring) / .5);
}

/* ② Dialog 頭尾分隔線：shadcn 的 DialogHeader／DialogFooter **沒有分隔線**
      （標題、內文、動作是同一張卡上的三段，不是三個區塊）。
      實測 .modal-header borderBottom 1px、.modal-footer borderTop 1px。
      上面 SweetAlert 那段已照同一規則拿掉 .swal-footer 的線，這裡讓兩種對話框一致。 */
.modal-header { border-bottom: 0; }
.modal-footer { border-top: 0; }

/* ③ DialogTitle：shadcn 是 text-lg(18px)/600；實測 Bootstrap 給 20px。 */
.modal-title { font-size: 1.125rem; font-weight: 600; }

/* ── 帳戶選單（租戶區展開）與主選單做出區分（2026-07-29 業主指定）────────
   業主：「續約訂閱展開的左邊選單不明顯，要跟主選單稍微有點差異，看是不是底色稍微區分」。
   實測展開後的 #appSidebarProfileMenu 選單項與主選單項**完全一樣**
   （底色 transparent、左 16px、縮排 8px、無左框、同字級）→ 看不出是子區域。
   shadcn 的 SidebarGroup 就是用一層極淡的底色把「這是一個群組」框出來。
   用 sidebar-accent 的半透明，暗色亮色都成立，不另外寫兩套顏色。 */
#sidebar #appSidebarProfileMenu {
    /* ★已被 §S-36 覆蓋（實測生效值：background=transparent） */
    background: hsl(var(--sidebar-accent) / .55);
    /* ★已被 §S-36 覆蓋（實測生效值：border-radius=0px） */
    border-radius: var(--radius-md);
    /* ★已被 §S-36 覆蓋（實測生效值：margin=2px 8px 6px 22px） */
    margin: 2px 8px 6px;
    /* ★已被 §S-36 覆蓋（實測生效值：padding=2px 0px 2px 10px） */
    padding: 4px 0;
}
#sidebar #appSidebarProfileMenu .menu-link { margin-left: 4px; margin-right: 4px; }

/* ── 租戶碼永不截斷（2026-07-29 業主：「客戶代碼好像會被遮住」）─────────
   先前把 gap 4→2、badge margin 6→0 之後，我在無頭瀏覽器量到 43/43 剛好放得下；
   但業主的 Chrome 實際仍顯示「C0…」—— 字型度量不同，43px 這個「剛好」不可靠。
   治本：租戶碼是**識別碼**，一律不參與壓縮（flex-shrink:0）；
   要犧牲就犧牲訂閱狀態 badge（它是狀態、截了還看得懂，而且點進去就有完整資訊）。 */
/* ★2026-08-06：`.tenant-code` 第 2 條宣告。**整塊三條全部不生效**——
   它想把第 1 條的 hidden/ellipsis 翻回 visible/clip，但後面 §S-34 又翻回 hidden/ellipsis。
   這是六條來回改的中途站，留著只是沿革紀錄。行號引用已漂掉，改引章節名。 */
#sidebar .menu-profile-info .tenant-code {
    /* ★已被 §S-36 覆蓋（實測生效值：flex=0 1 auto） */
    flex: 0 0 auto;
    /* ★已被 §S-34 覆蓋（實測生效值：overflow=hidden，本行的 visible 未生效） */
    overflow: visible;
    /* ★已被 §S-34 覆蓋（實測生效值：text-overflow=ellipsis，本行的 clip 未生效） */
    text-overflow: clip;
}
#sidebar .menu-profile-info .tenant-meta > .btn {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* =============================================================================
 * §手機版觸控與篩選收合（2026-07-29，業主：「手機版排版是不是要優化 標題跟按鈕」）
 * ============================================================================= */

/* ── ① 觸控區：照 shadcn 的實際做法 ──────────────────────────────────
   查了 shadcn 的 sidebar.tsx 原始碼，它處理手機觸控的方式是
     `after:absolute after:-inset-2 md:after:hidden`
   （用在 SidebarGroupAction 與 SidebarMenuAction）
   —— 用一個看不見的偽元素把**可點範圍**往外撐 8px，只在手機生效，
      **視覺尺寸完全不變**。shadcn 並沒有把按鈕放大到 44px。
   套用前實測相鄰間距，確認不會互相重疊：
     · 列內動作鈕：同列**沒有任何水平相鄰**（間距陣列為空），
       上下相鄰垂直間距 20px → 各外擴 8px 共 16px，仍剩 4px，安全。
     · 頂 bar 三顆圖示鈕相鄰只有 4px → 外擴會重疊 12px 變成按錯，
       **不能用這招**，改成把間距補到 shadcn SiteHeader 的 gap-2（8px）。 */
@media (max-width: 767.98px) {
    .fb-row-action { position: relative; }
    .fb-row-action::after {
        content: '';
        position: absolute;
        inset: -8px;            /* shadcn 的 -inset-2 */
    }
    /* 頂 bar：不外擴，改補間距（原本 4px，shadcn SiteHeader 是 gap-2 = 8px） */
    .app-header .navbar-nav > .navbar-item + .navbar-item { margin-left: 4px; }

    /* ── ③ 頂 bar 下拉不能寬過螢幕 ──────────────────────────────────
       業主回報「右上角按鈕都怪怪的」，實測 390px：
       通知下拉 inline 寫死 min-width:320px，實際撐到 **360px**，
       視窗只有 390 → popper 塞不下，把它推到 **x = -38（跑出畫面左邊）**，
       右緣 322 而鈴鐺在 228~278 → 完全沒有錨定在觸發鈕下方（實測 貼齊=false）。
       shadcn 的 DropdownMenuContent 是錨定 trigger 且會避開邊界。
       用 min() 讓它「有空間時維持 320，沒空間就縮到剛好放得下」，
       !important 是為了蓋過 inline 的 min-width。 */
    .app-header .dropdown-menu {
        /* ★只限制寬度不夠 —— 實測寬度被壓到 374（= 100vw - 1rem）之後，
           popper 仍把它擺在 left:-52（右緣對齊觸發鈕，左邊就溢位了）。
           頂 bar 的觸發鈕全部在右側，所以直接採 shadcn DropdownMenu 的
           align="end" 語意：**釘在視窗右緣**，不再交給 popper 自由擺放。
           transform 要 !important —— popper 是寫成 inline style 的。 */
        position: fixed !important;
        top: calc(var(--fb-header-h) + 4px) !important;
        right: .5rem !important;
        left: auto !important;
        width: min(360px, calc(100vw - 1rem)) !important;
        min-width: 0 !important;
        max-width: none !important;
        transform: none !important;
        margin: 0 !important;
    }
}

/* ── ④ 頂 bar 的焦點框：補上 shadcn 的 focus-visible ring ──────────────
   業主截圖裡按鈕外圍那圈**藍色框**不是我們畫的 —— 實測 outline 是 none，
   那是 iOS Safari 自己的預設 focus ring：
   頂 bar 的按鈕是 .navbar-link，**不在 .btn 的 focus-visible 樣式涵蓋範圍**，
   我們沒給樣式，Safari 就用它自己的。
   shadcn 的規則是 focus-visible（只有鍵盤操作才出現，觸控點擊不出現），
   這裡補上同一套 ring；同時明確把「非鍵盤的 :focus」清乾淨，
   避免點一下就留一圈框。 */
.app-header .navbar-link:focus-visible,
.app-header .navbar-item > a:focus-visible,
.app-header .navbar-mobile-toggler:focus-visible {
    outline: none;
    border-radius: var(--radius-md);
    /* §S-73：原 `0 0 0 2px hsl(var(--background)), 0 0 0 4px hsl(var(--ring) / .5)` → 官方兩層 */
    border-color: hsl(var(--ring));
    box-shadow: 0 0 0 3px hsl(var(--ring) / .5);
}
.app-header .navbar-link:focus:not(:focus-visible),
.app-header .navbar-item > a:focus:not(:focus-visible),
.app-header .navbar-mobile-toggler:focus:not(:focus-visible) {
    outline: none;
    box-shadow: none;
}

/* ── ② 篩選常駐列：手機版收起時只留主要控制項 ──────────────────────
   業主定調「展開看到更多」。實測 390px：常駐列被擠成 3 行、112px，
   看到第一筆資料前有 **47%** 的螢幕被版面吃掉（桌機是 31%）。
   做法：次要條件標 data-filter-more，收起時在手機隱藏，展開就出現。
   ★只影響有進階面板的頁面 —— `.fb-filter` 這個 class 是
     _shell 的 filter-collapse 在「找得到 [data-filter-advanced]」時才加的，
     所以 buy/orders、buy/arrivals 這種沒有篩選鈕的頁面完全不受影響。
   ★桌機不套用：桌機常駐列本來就是一行，沒有問題。 */
@media (max-width: 767.98px) {
    /* ★2026-08-04 稽核：blade 0 命中 */
    .fb-filter:not(.is-open) .fb-filter-row [data-filter-more] { display: none !important; }

    /* ★收起日期後仍是 2 行，因為搜尋框寫死 inline `min-width:200px`
       （200 + 查詢 61 + 篩選 78 + 間距 ≈ 350，扣掉左右 padding 只剩 358，
        差一點就擠到第二行）。手機讓文字輸入框可收縮，三顆就併成一行。
       用 !important 是因為那是 inline style，一般規則蓋不過。 */
    /* ★★ 2026-07-29 修正範圍（業主回報「展開的選項跑版」）：
       原本寫成 `.fb-filter-row > div` 與 `input[type=text]` 一律可收縮，
       結果把日期的 .input-group（輸入框＋日曆鈕）也收縮了 ——
       Bootstrap 的 .input-group 預設 flex-wrap:wrap，一擠就把日曆鈕換到下一行。
       實測展開後：日期群組寬 124px／高 **64px（兩行）**、
       輸入框需要 182px 只拿到 124px（文字被截成「2026/06/30 - 2026」）、
       日曆圖示 y 差 32px 掉到下一行。
       → 只讓「單獨的文字輸入框」收縮；input-group 維持內容寬度且不換行。 */
    /* ★2026-08-04 稽核：blade 0 命中 */
    .fb-filter-row > div:not(.input-group) { min-width: 0 !important; }
    /* ★2026-08-04 稽核：blade 0 命中 */
    .fb-filter-row > div:not(.input-group) > input[type="text"],
    .fb-filter-row > div:not(.input-group) > input:not([type]) { min-width: 0 !important; }
    /* ★2026-08-04 稽核：blade 0 命中 */
    .fb-filter-row > div:not(.input-group):has(> input[type="text"]),
    .fb-filter-row > div:not(.input-group):has(> input:not([type])) { flex: 1 1 120px; }
    /* 日期這種「輸入框＋附加鈕」一組的，寧可換行到下一列，也不能自己內部斷開 */
    /* ★2026-08-04 稽核：blade 0 命中 */
    .fb-filter-row .input-group { flex-wrap: nowrap !important; flex: 0 0 auto; }
}

/* ── .btn-lg 對齊 shadcn size="lg"（2026-07-28）
   主題原本只收斂了 .btn-icon.btn-lg（34px 方形），一般 .btn-lg 沒管
   → 吃 Bootstrap 預設的 48px。
   shadcn Button 有 lg 這一階：h-10（40px）、px-8、rounded-md。
   ★這跟先前判定「刻意保留」的下載頁 49px CTA 不同 ——
     那些用的是自訂 class（btn-download / btn-mw-*），shadcn 沒有對應階；
     .btn-lg 是 shadcn 有明確定義的尺寸，該對齊。
   實例：後台 /manager/tenant/{id}/recharge/select-package 的 5 顆
        「購買此方案」方案卡 CTA，原本 48px。 */
.btn.btn-lg:not(.btn-icon) {
    min-height: 40px;
    padding: 0 2rem;
    font-size: .875rem;
    border-radius: var(--radius-md);
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

/* =============================================================================
 * §S 賣賣 seller 專有補位（2026-07-30）
 * -----------------------------------------------------------------------------
 * 本檔移植自 2026-fatboy。以覆蓋率腳本實測，seller 的 235 支 blade 用到
 * 222 種 Color Admin 專有 class，其中 218 種（3,572 處）已由上面的共用章節接住，
 * 只有以下兩項是 seller 有、肥肥沒有的缺口，補在這裡。
 *
 * 另外兩個曾被列為缺口的 .field / .name 經查是誤報：Color Admin 的定義分別是
 * `.profile-content .table.table-profile tbody>tr td.field` 與 `.chats .name`，
 * 都帶 context；seller 的使用處（sso-points-demo、auth/sso-account-prompt）是裸 div，
 * 本來就沒吃到 CA 樣式，拔除後零影響，故不補（避免無中生有）。
 * ========================================================================== */

/* --- S-1. 寬度工具 .w-150px -------------------------------------------------
   CA 的 .w-*px 工具組，肥肥用到 w-100px/w-200px 等已在共用章節補過，
   seller 多用了一個 w-150px（PackagingMachine/Order/modify_item.blade.php 商品總計欄）。 */
.w-150px { width: 150px !important; }

/* --- S-2. bootstrap-timepicker 外層 wrapper -----------------------------------
   seller 有 32 處 `<div class="input-group bootstrap-timepicker">`（打卡補卡編輯等）。
   ★實測：這個外掛的 JS 全專案從未載入（grep `bootstrap-timepicker/js` 與 `.timepicker(` 皆 0 命中），
   所以彈出面板 .bootstrap-timepicker-widget 不會出現，不需要補它的皮，也不需要載外掛 CSS。
   Color Admin 對這個 class 實際上只有一條規則，就是下面這條——修正 CA 自己把
   .input-group-addon i 壓成固定尺寸 block 的副作用，讓時鐘 icon 回到 inline 自然尺寸。 */
.bootstrap-timepicker .input-group-addon i {
    width: auto;
    height: auto;
    display: inline;
}

/* --- S-3. .form-floating 疊字回歸 → 改成「label 在輸入框上方」的堆疊式 ---------
   ★病徵（2026-07-30 實測，登入頁 #account/#password）：
     label「帳號」與 placeholder「Account」完全重疊在同一個 45px 方框裡。
     getComputedStyle 量到 label{position:absolute; top=input.top; height=45px; transform:none}
     ＝ 標籤根本沒浮起來，而 placeholder 又是可見的。

   ★真因（量出來的，不是猜的）：Bootstrap 原生浮動標籤靠兩件事成立 ——
       (1) `.form-floating > .form-control::placeholder { color: transparent }`
       (2) 輸入框 58px（3.5rem+2px）高，留出上緣給縮小後的標籤
     本檔上游共用章節有一條
       `.form-control::placeholder, … { color: hsl(var(--muted-foreground)) !important; }`
     它的 !important 直接蓋掉 (1) → placeholder 變成看得見；
     而 seller 的 view 全部寫死 `class="form-control fs-13px h-45px"`（45px < 58px）→ (2) 也不成立。
     兩個條件同時破功，就是眼前的疊字。

   ★治法：不去修復浮動標籤（規格 §6 明訂「label 12px/500 在輸入框上方，不用 floating label」），
     直接把 .form-floating 轉成 flex 直排：label 在上、輸入框在下、錯誤訊息最後。
     view 一行都不用改，11 處 .form-floating（login / sso-login / forgotPassword /
     accountEdit / accountPasswordEdit，全部都在 .login.login-v2 底下）一次到位。 */
.form-floating {
    display: flex;
    flex-direction: column;
    /* position:relative 保留 —— 登入頁的「顯示密碼」眼睛圖示是 absolute 定位，靠它當參考框 */
}
/* 排序：label(1) → 輸入框(2) → 其餘（.invalid-feedback 等）(3)。
   ★不能用 flex-direction: column-reverse，那會把 .invalid-feedback 甩到最上面。 */
.form-floating > * { order: 3; }
.form-floating > label { order: 1; }
.form-floating > .form-control,
.form-floating > .form-select,
.form-floating > .form-control-plaintext { order: 2; }

/* label：解除 Bootstrap 的絕對定位／滿版遮罩／縮放位移，改成規格 §6 的欄位標籤 */
.form-floating > label {
    position: static;
    width: auto;
    height: auto;
    max-width: 100%;
    padding: 0;
    border: 0;
    overflow: visible;
    pointer-events: auto;
    transition: none;
    transform: none !important;          /* 蓋掉 :not(:placeholder-shown) ~ label 的 scale(.85) */
    margin-bottom: 6px;
    font-size: 12px !important;          /* 蓋掉 view 寫死的 .fs-13px（規格 §6：label 12px/500） */
    font-weight: 500;
    line-height: 1.4;
    color: hsl(var(--muted-foreground)) !important;   /* 蓋掉 view 寫死的 .text-gray-600 */
}
/* Bootstrap 用 label::after 畫一塊「蓋住邊框」的底色補丁，堆疊式不需要，留著會多一塊色塊 */
.form-floating > label::after { display: none; content: none; }

/* 輸入框：解除浮動標籤專用的上下內距（1.625rem/.625rem）與 58px 高 */
.form-floating > .form-control,
.form-floating > .form-select,
.form-floating > .form-control:focus,
.form-floating > .form-control:not(:placeholder-shown),
.form-floating > .form-control-plaintext:focus,
.form-floating > .form-control-plaintext:not(:placeholder-shown) {
    height: 36px;
    min-height: 36px;
    padding-top: 0;
    padding-bottom: 0;
    line-height: 1.4;
}

/* 「顯示密碼」眼睛圖示（login/sso-login 的 #checkEye，view 寫死 top:50%）：
   容器變高之後 50% 會落在 label 那一行 → 改成貼齊輸入框（容器最後一個在流內的元素）。
   ★這兩處的 .form-floating 內沒有 .invalid-feedback，輸入框就是最底部，bottom:0 對得準。 */
.form-floating > i,
.form-floating > svg {
    position: absolute !important;
    top: auto !important;
    bottom: 0 !important;
    right: 12px !important;
    height: 36px;
    display: flex !important;
    align-items: center;
    transform: none !important;
    font-size: 15px !important;          /* view 寫死 fa-2xl(2em)＝26px，對 36/40px 的框太大 */
    line-height: 1;
    color: hsl(var(--muted-foreground)) !important;   /* 蓋掉 inline style="color:#000" */
    cursor: pointer;
    z-index: 2;
}

/* --- S-4. 控制項高度收斂（規格 §0：sm=32 / default=36 / lg=40）-----------------
   ★2026-07-30 逐頁 getComputedStyle 實測（14 頁），收斂前 .app-content 內量到 5 種高度：
       23px  span.btn.btn-xs.btn-*（狀態膠囊，8 顆／hr/user）
       24px  a/button.btn-xs（工具列小鈕）
       32px  .btn-sm / .form-control-sm / select2（上游共用章節已收斂）
       38px  未帶尺寸修飾的 .form-control / .form-select / .btn（吃 Bootstrap 預設）
              — hr/user/create 一頁就 31 顆、order-management/orders/create 18 顆
       45px  .h-45px（登入類頁面的輸入框，12 處）
   收斂後只剩 24（xs 膠囊，見下方說明）/ 32（sm）/ 36（default）/ 40（登入 lg）。 */

/* (a) 未帶尺寸修飾的表單控制項 → 36px（規格 §0 default）。
   ★textarea 必須排除：上游章節已為此踩過坑（釘死高度會讓 rows 屬性完全失效）。
   ★[type=file]/[type=color]/[multiple]/[size] 排除：它們的盒模型由瀏覽器或既有規則決定。
   ★`:not(.input-group-sm) >` 這一段是量出來補的：搜尋列的 input 沒帶 .form-control-sm，
     它是靠外層 `.input-group-sm` 才變 32px 的（上游規則 `.input-group-sm > .form-control`）。
     少了這個守門，本規則因為 :not() 鏈把特異度墊到 (0,7,0) 會反過來壓贏上游，
     把搜尋框變成 36px、旁邊的放大鏡 addon 還是 32px ＝ 同一個 input-group 高低不齊。 */
.app-content :not(.input-group-sm):not(.input-group-lg) > .form-control:not(textarea):not(.form-control-sm):not(.form-control-lg):not(.form-control-color):not([type="file"]):not([type="color"]),
.app-content :not(.input-group-sm):not(.input-group-lg) > .form-select:not(.form-select-sm):not(.form-select-lg):not([multiple]):not([size]) {
    height: 36px;
    min-height: 36px;
    font-size: 14px;                     /* 規格 §0：default 控制項字級 14px */
}
/* ★ .input-group-text 必須跟上：本規則原本只收 .form-control / .form-select，
     於是「沒帶 .input-group-sm 的預設 input-group」會變成 36px 輸入框配 38px 附加框，
     同一組控制項高低不齊（實測由 SystemSetting 那批頁面回報）。
     附加框沒有 -sm/-lg 修飾類，尺寸完全由外層 .input-group-sm/-lg 決定，
     所以守門條件與上面兩條一致，不能只加選擇器不加守門。 */
.app-content :not(.input-group-sm):not(.input-group-lg) > .input-group-text {
    min-height: 36px;
    font-size: 14px;
}

/* (b) 未帶尺寸修飾的按鈕 → 36px。
   ★用 line-height 22 + padding 6 + border 1×2 湊出剛好 36，不用 display:inline-flex 置中 ——
     這些按鈕有 167 顆帶 .d-block（`btn d-block w-100`），.d-block 是 !important，
     inline-flex 會被它蓋掉、置中失效，只有 line-height 的算法兩種都成立。
   ★.theme-panel 一併收：那是「賣賣小幫手」側拉板，不在 .app-content 內，
     漏掉的話每一頁都會殘留兩顆 38px。 */
.app-content :not(.input-group-sm):not(.input-group-lg) > .btn:not(.btn-sm):not(.btn-xs):not(.btn-lg):not(.btn-icon):not(.btn-close),
.theme-panel .btn:not(.btn-sm):not(.btn-xs):not(.btn-lg):not(.btn-icon):not(.btn-close) {
    min-height: 36px;
    font-size: 14px;
    line-height: 22px;
    padding-top: 6px;
    padding-bottom: 6px;
}

/* (c) .btn-xs 統一 24px：實測同一頁裡 span 是 23px、a/button 是 24px（行高差 1px），
   同一列並排時肉眼看得出高低差。
   ★這一階刻意「不」升到 32px：seller 的 253 顆 btn-xs 裡有 70 顆是 span（狀態膠囊／計數 chip，
     規格 §2 的計數 chip 就是 btn-xs rounded-pill），升到 32 會變成表格裡一排巨大的膠囊；
     肥肥正式站同樣保留這一階。真正該是 32×32 的表格操作鈕，上游章節已用
     `.app-content .table td a.btn[href*=edit|modify|view]` 單獨收斂過。 */
.app-content .btn.btn-xs:not(.btn-icon) {
    min-height: 24px;
    line-height: 18px;
    padding-top: 2px;
    padding-bottom: 2px;
}

/* (d) .h-45px（12 處，全部是 auth 五頁的輸入框）：
   45px 不在 32/36/40 任何一階上。登入類頁面上游已訂 `.login .form-control{min-height:40px}`
   ＝ shadcn 的 lg，送出鈕 .btn-lg 也是 40px → 登入頁收斂後整頁只剩「一種」高度。 */
/* ★2026-08-04 稽核：blade 0 命中 */
.form-control.h-45px,
.form-select.h-45px { height: 36px !important; min-height: 36px; }
.login .form-control.h-45px,
.login .form-select.h-45px,
.login .form-floating > .form-control,
.login .form-floating > .form-select { height: 40px !important; min-height: 40px; }
.login .form-floating > i,
.login .form-floating > svg { height: 40px; }

/* --- S-5. 字級下限 12px --------------------------------------------------------
   規格 §4 把最小字級訂在 12px（th 12 / td 13）。Color Admin 留下的 .fs-9px～.fs-11px
   共 54 處（fs-11px 29、fs-10px 10、fs-11 7、fs-9 4、fs-9px 2、fs-7 1、fs-10 1），
   實測會產出 11px 的內文（例：頂 bar「跟賣家站在一起」、表單欄位下方的 muted 說明）。
   ★上游共用章節已把「相對值」的破口補完（.badge 12px 絕對值、.app-content .small/small 12px
     絕對值），剩下的就是這組寫死小於 12px 的工具類，這裡統一抬到下限。
   ★側欄「續約訂閱」chip 的 10/11px 不在此列：那是上游刻意壓成 16px 高的 chip
     （壓不下去會被 overflow:hidden 裁掉，有註解為證），且不使用這組工具類。 */
.fs-9px, .fs-10px, .fs-11px,
.fs-7, .fs-9, .fs-10, .fs-11 { font-size: 12px !important; }

/* --- S-6. .text-white 安全網（非按鈕情境）--------------------------------------
   ★先數再訂規則（2026-07-30 掃 235 支 blade，共 136 個帶 text-white 的元素）：
       35  在 .btn 內的圖示／文字 → 上游 `.btn i/.fa/span{color:inherit!important}` 已接住
        9  自己就是 .btn*        → 上游 `.btn-xxx.text-white` 安全網已接住
        7  自己就是 .badge
       29  自己帶 bg-*           → 上游 `.bg-dark/.bg-info…` 規則已同時指定 color，會勝出
        8  登入頁（深色底圖）    → 不在 .app-content 內，不受本節影響
       15  祖先是深底 bg-*
       10  圖片／漸層覆蓋層
       23  ★裸用，沒有任何底色線索 ← 只有這一群會白字白底
     23 裸用裡最大宗是 `h1.page-header > a.text-white`（上一頁／下一頁箭頭）11 處
     （另 32 處在 Order 各狀態的 index 已由 view 端改成 .text-muted），其餘是
     Center/desktopPackage 的卡片內文與 AiTool/alpha 的 JS 樣板字串。

   ★規則設計：不鎖單一 class（肥肥的教訓①：只鎖 btn-light 導致多數按鈕沒吃到），
     改用 `color: inherit` —— 讓 text-white「跟著容器走」：
       容器是深底（自帶 bg-dark 等，那些規則都有指定 color）→ 繼承到白字，維持原樣；
       容器是淺底（卡片／頁首／表格）→ 繼承到該表面的前景色，不再隱形；
       暗色主題自動成立，不必再寫一份。
     JS 動態產生的 text-white（AiTool/alpha 的樣板字串）也一併涵蓋。

   ★`:not([class*="bg-"])` 是實測補的守門（2026-07-30 /center/desktop 量到）：
     一開始沒加，`div.card.bg-gray-800.text-white` 這種「自己就是深底」的元素也被 inherit
     打回亮色前景 → 深底黑字，對比 1.12:1 等於看不見。
     自帶 bg-* 的元素一律不碰：深底類（bg-gray-800/bg-dark/bg-black…）本來就該白字，
     淺底類（bg-primary/bg-info/bg-success…被主題轉成柔和淺底）上游規則已經連 color
     一起指定、特異度也贏 .text-white，兩邊都不需要本節插手。
   ★`:not([class*="btn"])` 同理：按鈕交給上游那兩組安全網。 */
.app-content .text-white:not([class*="bg-"]):not([class*="btn"]):not(.badge) { color: inherit !important; }
.app-content .text-white-50:not([class*="bg-"]):not([class*="btn"]):not(.badge) { color: inherit !important; opacity: .7; }

/* 例外：底色來自「圖片」或「漸層」的覆蓋層 —— 這些祖先只有 background-image、
   沒有可繼承的 color，inherit 會拿到頁面前景色（深色）＝白字反而變黑字。
   實測涉及 .bg-gradient-blue-indigo（方案推薦卡，本檔上游只補了 background-image）、
   商品圖 hover 覆蓋層與 .gallery .image-inner，共 10 處，這些維持真白。
   ★選擇器必須把上面那串 :not() 原樣抄一遍：上面那條是 (0,5,0)，
     例外若只寫 `.app-content .card-img-overlay .text-white`＝(0,3,0) 會輸給它，
     例外等於沒寫（2026-07-30 用合成節點量到覆蓋層文字被打成深色才發現）。 */
/* ★★【死碼標註 2026-08-06 —— 只標不刪】
   這串逗號清單裡的兩項：
       `.app-content .image-inner .text-white:not(…)`
       `.app-content .image-inner .text-white-50:not(…)`
   **已經沒有呼叫端**（其餘四種祖先 .card-img-overlay / .hover-show-elm /
   [class*="bg-gradient"] / [style*="background-image"] 都還有人用，不受本標註影響）。

   ── 何時失去呼叫端 ──
   2026-08-06 商店設定與 API 串接兩頁改用 shadcn 的 .card 語彙（收斂到
   StoreSetting/_picker_card.blade.php 這支共用卡片），`.image-inner` 這個
   Color Admin 容器就此從 blade 端消失；上面那段前言寫的「.gallery .image-inner」
   共 10 處，指的就是那兩頁改版前的狀態。

   ── 確認方式（照著跑，不要憑印象）──
     find resources app public/assets/js config database -type f \
       \( -name '*.php' -o -name '*.js' -o -name '*.vue' \) \
       -exec grep -HnE 'class="[^"]*\bimage-inner\b|classList\.(add|toggle)\(.image-inner' {} \;
   ★一定要用 `find` 遞迴，不可以用 `<目錄>/*.php` 這種 shell glob（不進子目錄、也漏 .mjs）。
   ★2026-08-06 實跑：專案內零命中；唯一的字面出現是
     StoreSetting/ApiPlatform/create.blade.php 的 `{{-- --}}` 註解（在講改版歷史），
     不是 HTML class。★掃不到的死角：DB 裡的富文字（Quill）內容無法用檔案掃描證實。
   ★這道掃描本身有先用誘餌驗過會紅（在 scratchpad 子目錄放含 class="image-inner"
     的假檔，確認抓得到，才敢說零命中）。

   ── 為什麼只標不刪 ──
   專案守則「禁止刪除任何內容」。而且刪掉這兩項要動到既有選擇器清單，
   屬於改寫而非新增；要不要退場請主題那邊（ray）決定，本輪只留下判斷依據。
   ★留著的成本是 0：沒有 `.image-inner` 元素，這兩項永遠不會匹配到任何節點。 */
.app-content .card-img-overlay .text-white:not([class*="bg-"]):not([class*="btn"]):not(.badge),
.app-content .hover-show-elm .text-white:not([class*="bg-"]):not([class*="btn"]):not(.badge),
.app-content .image-inner .text-white:not([class*="bg-"]):not([class*="btn"]):not(.badge),
.app-content [class*="bg-gradient"] .text-white:not([class*="bg-"]):not([class*="btn"]):not(.badge),
.app-content [style*="background-image"] .text-white:not([class*="bg-"]):not([class*="btn"]):not(.badge),
.app-content .card-img-overlay .text-white-50:not([class*="bg-"]):not([class*="btn"]):not(.badge),
.app-content .hover-show-elm .text-white-50:not([class*="bg-"]):not([class*="btn"]):not(.badge),
.app-content .image-inner .text-white-50:not([class*="bg-"]):not([class*="btn"]):not(.badge),
.app-content [class*="bg-gradient"] .text-white-50:not([class*="bg-"]):not([class*="btn"]):not(.badge),
.app-content [style*="background-image"] .text-white-50:not([class*="bg-"]):not([class*="btn"]):not(.badge) {
    color: hsl(var(--dark-panel-foreground)) !important;
    opacity: 1;
}

/* --- S-7. .btn-yellow 底色從未生效（寫了 ≠ 生效）------------------------------
   ★實測（登入頁 #save）：computed background-color = rgb(244,244,245)＝--secondary 淺灰，
     不是品牌黃。上游有一條「.btn-yellow ＝品牌主按鈕（不收斂）」的規則，
     但它只設 `--bs-btn-bg` 這類自訂屬性，而更前面那條「CA 命名色彩鈕全部收斂為中性」
     直接寫了 `background-color: hsl(var(--secondary))`（實體屬性、同特異度、位置更後面
     → 勝出）。等於那條「不收斂」的例外規則從來沒生效過，連它自己的註解都寫著
     「業主也直接抱怨過登入鈕變淺灰」。
   ★用 `.btn.btn-yellow`（2 個 class）把實體屬性補回來，特異度勝過收斂那條。
     seller 的 .btn-yellow 只有 6 處：auth 五頁的送出鈕 + 側欄「續約訂閱」chip，
     chip 那邊上游是 !important，不受影響 → 只有登入類主按鈕恢復品牌黃，
     不違反「黃鈕只給新增/儲存/唯一主要動作」。 */
.btn.btn-yellow {
    background-color: hsl(var(--primary));
    border-color: hsl(var(--primary));
    color: hsl(var(--primary-foreground));
}
.btn.btn-yellow:hover,
.btn.btn-yellow:focus {
    background-color: hsl(var(--primary-light));
    border-color: hsl(var(--primary-light));
    color: hsl(var(--primary-foreground));
}
.btn.btn-yellow:active,
.btn.btn-yellow:focus-visible {
    background-color: hsl(var(--primary-dark));
    border-color: hsl(var(--primary-dark));
    color: hsl(var(--primary-foreground));
}

/* --- S-8. .bg-indigo / .bg-purple 深色卡片補位 --------------------------------
   ★S-6 的 text-white 稽核順手量到的真破口（/center/desktop 亮色模式）：
     `<div class="card border-0 mb-3 bg-indigo text-white">`（Center/desktopPackage:153）
     computed background = rgb(255,255,255)、color = rgb(250,250,250) → 對比 1.04:1，
     整張「今日各時段」卡片的字全部看不見。
   ★原因：上游只替 .btn/.badge 版本的 CA 彩色工具做了映射
     （`.btn.bg-indigo…`、`.badge.bg-indigo…`），掛在 .card 上的裸 .bg-indigo 沒人接，
     Color Admin 的定義又隨 app.min.css 一起拆掉了 → 卡片吃回 .card 的白底。
     暗色模式剛好不壞（--card 本來就深），所以只在亮色炸，跟 .bg-gray-800 當初同一種病。
   ★療法照抄上游 .bg-gray-800 的補位（含 --bs-card-* 那一組，Bootstrap 5.3 的 .card-body
     吃 var(--bs-card-color) 不是繼承 color，只設 color 會漏）。 */
.bg-indigo:not(.badge):not(.btn),
.bg-purple:not(.badge):not(.btn) {
    background-color: hsl(var(--dark-panel) / var(--bs-bg-opacity, 1)) !important;
    color: hsl(var(--dark-panel-foreground));
    --bs-card-color: hsl(var(--dark-panel-foreground));
    --bs-card-bg: transparent;
    --bs-card-cap-bg: transparent;
    --bs-card-cap-color: hsl(var(--dark-panel-foreground));
    --bs-card-border-color: hsl(0 0% 100% / .10);
    /* ★★ 2026-07-31：這條照抄上游 .bg-gray-800 的補位，連同它的坑一起抄過來了 ——
       --bs-card-border-color 只是自訂屬性，贏不了 §5 `.card { border: 1px solid ... }`
       的實體宣告。實測修前 computed border-top-color 亮色 rgb(228,228,231)／暗色 rgb(39,39,42)，
       不是預期的 rgba(255,255,255,0.1)。與 .bg-gray-800 同樣補上實體 border-color。 */
    border-color: hsl(0 0% 100% / .10);
}
.bg-indigo:not(.badge):not(.btn) > .card-header,
.bg-indigo:not(.badge):not(.btn) > .card-footer,
.bg-purple:not(.badge):not(.btn) > .card-header,
.bg-purple:not(.badge):not(.btn) > .card-footer {
    background: transparent !important;
    color: hsl(var(--dark-panel-foreground)) !important;
    border-color: hsl(0 0% 100% / .10) !important;
}

/* --- S-9. .text-body 補位（Bootstrap 原生，主題原本沒接）-----------------------
   `.text-body` 是 Bootstrap 的工具類，吃的是它自己寫死的 `--bs-body-color: #212529`，
   不會跟著本主題的亮暗翻轉。配上 `.text-body.text-opacity-50` 疊在暗色卡片上時
   實測是「深灰壓深底」＝整段隱形（`/seller/bill` 的方案說明文字踩過）。
   view 端能改的已改成 .text-muted，但這是全站工具類、未來新頁還會用到，
   所以在主題層接住：一律走前景 token，opacity 修飾類仍可正常疊加。 */
.text-body { color: hsl(var(--foreground)) !important; }
.text-body-secondary, .text-body-tertiary { color: hsl(var(--muted-foreground)) !important; }

/* --- S-10. POS 頁腳按鈕在 muted 表面上沒有輪廓（2026-07-30 POS 拆 Color Admin 時量到）----
   ★病徵：/pos/package 與 /pos/machine 右下角三顆 62px 觸控磚，暗色模式下
     「後台管理」（.btn-default）與「暫停」（.btn-dark.disabled）只剩文字，
     完全看不出那裡有按鈕；亮色模式框線對比也只有 1.15，接近看不見。
     Color Admin 時代這三顆是白底＋灰框的實體磚（見 git history 的改版前截圖）。

   ★真因（量出來的，不是猜的，token 值取自本檔 :root / .dark）：
       .pos-sidebar-footer 的表面是 hsl(var(--muted))
       暗色主題裡 --muted 與 --border 是**同一個值**（240 3.7% 15.9%，zinc-800）
       → 任何描邊控制項放在這個表面上，框線色 ≡ 底色，對比恰好 1.00。
     再疊上兩條上游規則：
       §28 把 .btn-default 收斂成 ghost（--bs-btn-bg: transparent）
       §37 把 disabled 一律壓成 background:hsl(var(--muted)) !important
     兩者在一般卡片（--card）背景上都成立，唯獨落在 --muted 表面時整顆蒸發。
     亮色不是沒事，只是 --border(90%) vs --muted(95.9%) 還剩一點點差，剛好逃過肉眼。

   ★為什麼只在 POS 修、不動上游那兩條：
     ghost 按鈕與 muted disabled 是全站通用語彙，在 --card / --background 表面上正確；
     真正的特例是「POS 把頁腳做成 muted 色塊」。所以把修正綁在 .pos-sidebar-footer 內，
     零風險、不影響其餘 235 支 blade。
   ★POS 是收銀台，觸控磚看不見＝現場點不到，屬功能問題不是美觀問題，必須修。

   ★用 hsl(var(--foreground) / .18) 當框線色而不是 --border：
     前景色亮暗會翻轉（亮色近黑 / 暗色近白），對 --muted 表面兩個模式都拉得開，
     一條規則同時成立，不必寫兩份。 */

/* (a) 頁腳內所有按鈕都至少要有看得見的輪廓 */
.pos .pos-sidebar .pos-sidebar-footer .btn:not(.border-0) {
    border: 1px solid hsl(var(--foreground) / .18);
}

/* (b) 中性／收斂色／停用的按鈕：補回一層與 muted 頁腳分得開的表面。
   ★同時設 Bootstrap 自訂屬性與實體屬性：
     上游 §28 收斂色彩鈕用的是實體 background-color（只設自訂屬性蓋不過），
     而 hover / active 走的是自訂屬性（只設實體屬性會讓 hover 失效）。 */
.pos .pos-sidebar .pos-sidebar-footer .btn.btn-default,
.pos .pos-sidebar .pos-sidebar-footer .btn.btn-white,
.pos .pos-sidebar .pos-sidebar-footer .btn.btn-light,
.pos .pos-sidebar .pos-sidebar-footer .btn.btn-secondary,
.pos .pos-sidebar .pos-sidebar-footer .btn.btn-success,
.pos .pos-sidebar .pos-sidebar-footer .btn.btn-info,
.pos .pos-sidebar .pos-sidebar-footer .btn.btn-warning {
    --bs-btn-bg: hsl(var(--card));
    --bs-btn-color: hsl(var(--foreground));
    --bs-btn-border-color: hsl(var(--foreground) / .18);
    --bs-btn-hover-bg: hsl(var(--accent));
    --bs-btn-hover-color: hsl(var(--accent-foreground));
    --bs-btn-hover-border-color: hsl(var(--foreground) / .32);
    background-color: hsl(var(--card));
    color: hsl(var(--foreground));
}
.pos .pos-sidebar .pos-sidebar-footer .btn.btn-default:hover,
.pos .pos-sidebar .pos-sidebar-footer .btn.btn-white:hover,
.pos .pos-sidebar .pos-sidebar-footer .btn.btn-light:hover,
.pos .pos-sidebar .pos-sidebar-footer .btn.btn-secondary:hover,
.pos .pos-sidebar .pos-sidebar-footer .btn.btn-success:hover,
.pos .pos-sidebar .pos-sidebar-footer .btn.btn-info:hover,
.pos .pos-sidebar .pos-sidebar-footer .btn.btn-warning:hover {
    background-color: hsl(var(--accent));
    color: hsl(var(--accent-foreground));
    border-color: hsl(var(--foreground) / .32);
}

/* (c) 停用態：§37 用 !important 把底色壓成 --muted（＝頁腳同色），這裡必須同樣用
   !important 才蓋得過（特異度較高但 !important 不比特異度）。
   文字仍維持 muted-foreground 與 opacity，停用的語意不變，只是輪廓看得見。 */
.pos .pos-sidebar .pos-sidebar-footer .btn:disabled,
.pos .pos-sidebar .pos-sidebar-footer .btn.disabled {
    background-color: hsl(var(--card)) !important;
    border-color: hsl(var(--foreground) / .18) !important;
}

/* =============================================================================
 * §S-11 深色表面內的「標題」與「灰階文字」必須跟著表面翻轉（2026-07-30 稽核）
 * -----------------------------------------------------------------------------
 * ★病徵（實測，非推測）：/main 五張 `div.card.bg-gray-800.text-white` KPI 卡，
 *   亮色模式下數字與標籤幾乎看不見，暗色模式完全正常。
 *
 *   量到的來源（逐層量 computed color，找出真正勝出的規則）：
 *     - `h2 > span`（今日出貨單量／人員數字）
 *         fg rgb(9,9,11) on bg rgb(24,24,27) = 對比 1.12
 *         真兇 = §2 基礎的 `h1, h2, h3, h4, h5, h6 { color: hsl(var(--foreground)) }`。
 *         §S-9 的 `.bg-gray-800` 只把 color 與 --bs-card-color 設成 --dark-panel-foreground，
 *         那是「可繼承值」；標題有自己的直接宣告，繼承一定輸給直接宣告，
 *         於是標題吃回亮色主題的 --foreground（近黑）壓在近黑面板上。
 *         暗色模式的 --foreground 本來就是近白，所以只在亮色炸 —— 與 §S-9 同一種病。
 *     - `<b>`（今日出貨單量／今日出貨人員／平台包裹平均增減 等標籤）
 *         fg rgb(113,113,122) on 同底 = 對比 3.67
 *         真兇 = §21 的 `.text-gray-500, .text-gray-600, .text-gray-700 {
 *                  color: hsl(var(--muted-foreground)) !important }`。
 *         那個灰是配「亮底」用的，落到深面板上就不夠。
 *
 * ★治法（token 層，不在 view 寫死顏色）：
 *   深色表面本來就是「底 + 前景」成套的 token，表面內的**直接宣告色**也必須成套。
 *   兩個家族分開處理，因為它們的前景 token 行為不同：
 *     - --dark-panel / --dark-panel-foreground：兩模式皆深底白字，不翻轉
 *     - --dark / --dark-foreground：暗色模式會整組翻轉（--dark 變近白）
 *   `.bg-black` 不納入：全站 21 處都是 badge / nav-pill / 圖片覆蓋層，
 *   且多半帶 bg-opacity-25/50（半透明，前景該跟底圖走），統一指定顏色反而會壞。
 *
 * ★灰階文字用 alpha 而不是換一顆 token：alpha 疊在自己的前景色上，
 *   亮暗兩模式一條規則同時成立（與 §S-10 的 hsl(var(--foreground) / .18) 同一手法）。
 *   .65 是量出來的：疊在 --dark-panel 上 ≈ rgb(171,171,172)，對比 7.7，
 *   且比 100% 白的數字暗一階，標籤／數值的視覺層次不會被抹平。
 * ============================================================================= */

/* (a) --dark-panel 家族——兩模式皆深底。
   ★成員必須跟上游「哪些 utility 被映到 --dark-panel」完全一致，否則就是漏網：
     §S-9 的 .bg-gray-800、§S-8 的 .bg-indigo / .bg-purple（都是同一組 --bs-card-* 補位）。
   ★漏掉 .bg-indigo 的代價實測過：/center/desktop 亮色模式
     `div.card.bg-indigo.text-white`（今日各時段）整張卡 36 個文字節點低對比 ——
     H3 數字 1.12、.text-gray-500 標籤 3.67，跟 /main 的 .bg-gray-800 一模一樣的病。
     所以這裡列的是「家族」不是「某個 class」。 */
.bg-gray-800 h1, .bg-gray-800 h2, .bg-gray-800 h3,
.bg-gray-800 h4, .bg-gray-800 h5, .bg-gray-800 h6,
.bg-gray-800 .h1, .bg-gray-800 .h2, .bg-gray-800 .h3,
.bg-gray-800 .h4, .bg-gray-800 .h5, .bg-gray-800 .h6,
.bg-indigo:not(.badge):not(.btn) h1, .bg-indigo:not(.badge):not(.btn) h2,
.bg-indigo:not(.badge):not(.btn) h3, .bg-indigo:not(.badge):not(.btn) h4,
.bg-indigo:not(.badge):not(.btn) h5, .bg-indigo:not(.badge):not(.btn) h6,
.bg-purple:not(.badge):not(.btn) h1, .bg-purple:not(.badge):not(.btn) h2,
.bg-purple:not(.badge):not(.btn) h3, .bg-purple:not(.badge):not(.btn) h4,
.bg-purple:not(.badge):not(.btn) h5, .bg-purple:not(.badge):not(.btn) h6 {
    color: hsl(var(--dark-panel-foreground));
}
.bg-gray-800 .text-gray-500,
.bg-gray-800 .text-gray-600,
.bg-gray-800 .text-gray-700,
.bg-gray-800 .text-gray,
.bg-gray-800 .text-silver,
.bg-gray-800 .text-muted,
.bg-indigo:not(.badge):not(.btn) .text-gray-500,
.bg-indigo:not(.badge):not(.btn) .text-gray-600,
.bg-indigo:not(.badge):not(.btn) .text-gray-700,
.bg-indigo:not(.badge):not(.btn) .text-gray,
.bg-indigo:not(.badge):not(.btn) .text-silver,
.bg-indigo:not(.badge):not(.btn) .text-muted,
.bg-purple:not(.badge):not(.btn) .text-gray-500,
.bg-purple:not(.badge):not(.btn) .text-gray-600,
.bg-purple:not(.badge):not(.btn) .text-gray-700,
.bg-purple:not(.badge):not(.btn) .text-gray,
.bg-purple:not(.badge):not(.btn) .text-silver,
.bg-purple:not(.badge):not(.btn) .text-muted {
    color: hsl(var(--dark-panel-foreground) / .65) !important;
}
/* 佔位級灰（--hint）在深面板上更淡一階，維持三層層次 */
.bg-gray-800 .text-gray-300,
.bg-indigo:not(.badge):not(.btn) .text-gray-300,
.bg-purple:not(.badge):not(.btn) .text-gray-300 {
    color: hsl(var(--dark-panel-foreground) / .5) !important;
}

/* (b) --dark 家族（.bg-dark，§21 L1858 已把它設成 --dark / --dark-foreground 成套）
   ——暗色模式整組翻轉，所以用 --dark-foreground 才會跟著翻。
   實例：/tutorial/packaging-machine 的 .modal-content.bg-dark、
        /order/ready-to-ship 四個 .modal-header.bg-dark（都含標題文字）。 */
.bg-dark:not(.badge):not(.btn) h1, .bg-dark:not(.badge):not(.btn) h2,
.bg-dark:not(.badge):not(.btn) h3, .bg-dark:not(.badge):not(.btn) h4,
.bg-dark:not(.badge):not(.btn) h5, .bg-dark:not(.badge):not(.btn) h6,
.bg-dark:not(.badge):not(.btn) .h1, .bg-dark:not(.badge):not(.btn) .h2,
.bg-dark:not(.badge):not(.btn) .h3, .bg-dark:not(.badge):not(.btn) .h4,
.bg-dark:not(.badge):not(.btn) .h5, .bg-dark:not(.badge):not(.btn) .h6,
.bg-dark:not(.badge):not(.btn) .modal-title {
    color: hsl(var(--dark-foreground));
}
.bg-dark:not(.badge):not(.btn) .text-gray-500,
.bg-dark:not(.badge):not(.btn) .text-gray-600,
.bg-dark:not(.badge):not(.btn) .text-gray-700,
.bg-dark:not(.badge):not(.btn) .text-gray,
.bg-dark:not(.badge):not(.btn) .text-silver,
.bg-dark:not(.badge):not(.btn) .text-muted {
    color: hsl(var(--dark-foreground) / .65) !important;
}

/* =============================================================================
 * §S-12 圖表調色盤：補上「暗色模式的階」＋ 把 --chart-1 踩到白底看得見的一階
 * -----------------------------------------------------------------------------
 * ★背景：/main 的兩張圖表原本是 Color Admin 語彙（寫死 rgb(54,162,235) 藍 /
 *   rgb(255,206,86) 黃 / rgb(75,192,192) 青），2026-07-31 改吃 --chart-* token。
 *   改之前先跑了 dataviz 的 validate_palette.js（六項檢查：亮度帶／彩度下限／
 *   色盲可分辨／正常視覺可分辨／對比），量出兩個**真問題**：
 *
 *   ① --chart-* 只有亮色定義，**暗色模式完全沒有覆寫**。
 *      --chart-2 是 240 5.9% 10%（#18181b）＝ 跟暗色模式的 --card 一模一樣，
 *      線畫上去等於隱形。肥肥踩過同一顆，當時是在 view 裡加 DARK_PANEL_SWAP
 *      逐頁替身；那是補丁不是治本 —— 治本是 token 本來就該有暗色的階。
 *   ② --chart-1 是品牌黃 49 100% 50%（#ffd000），對白卡對比只有 **1.43:1**，
 *      遠低於 3:1，2px 的折線畫在白底上實測看不見。
 *      踩到同色相家族最近的可讀階 40 92% 38%（#ba7f08）後對比 PASS。
 *      ★只動 --chart-1（**圖表**用的黃），--primary 的品牌黃 #FFD200 一個字沒動 ——
 *        按鈕／logo／重點色維持原樣，這裡改的是「畫在資料上的那條線」。
 *
 * ★量測結果（validate_palette.js，三條序列 揀貨/包貨/檢核 = chart-1/2/3）：
 *     亮色（白卡 #ffffff）  色盲可分辨 ΔE 34.0 PASS ／ 正常視覺 ΔE 34.1 PASS ／ 對比 PASS
 *     暗色（#18181b 卡）    色盲可分辨 ΔE 18.3 PASS ／ 正常視覺 ΔE 18.7 PASS ／ 對比 PASS
 *   → 「看不看得見」與「分不分得出來」三項全過。
 *
 * ★仍未過、且**刻意不改**的兩項（留紀錄，不是漏修）：
 *     亮度帶 / 彩度下限 —— 因為這套盤刻意含中性色（--chart-2 zinc、--chart-3 gray），
 *     驗證器會判定它們「讀起來像灰色」。那是這套設計系統的品牌選擇
 *     （黃 + zinc 灰階），要改就是整盤重新設計，會影響 seller 與肥肥所有圖表頁
 *     （chartPackage / rfmReport / marketing.ads / Product.ranking …），
 *     不該由單一頁面的改版順手決定。已在 PR 回報，等拍板。
 *   ★因為有這一項，圖表一律附「次要編碼」：圖例常駐 + 每條序列不同的
 *     pointStyle（circle / rect / triangle），識別不靠顏色單獨承擔。
 * ============================================================================= */

/* (a) --chart-1 踩到白底可讀的一階（亮色模式）。維持黃/琥珀色相家族。 */
:root {
  --chart-1: 40 92% 38%;                 /* #ba7f08 對白卡 3:1 PASS（原 49 100% 50% 只有 1.43:1） */
}

/* (b) 暗色模式的階：不是把亮色反轉，是各自挑「在 #18181b 上看得見」的一階
       （dataviz 的規矩：dark mode is selected, not an automatic flip）。 */
html.dark {
  --chart-1: 43 96% 56%;                 /* #fbbf24 琥珀提亮 */
  --chart-2: 0 0% 89.8%;                 /* #E5E5E5 zinc 翻成近白（原 #18181b 在暗卡上等於隱形）← §S-74 achromatic（原 240 5% 90% #e4e4e7，官方階 89.8%，色差 2/255） */
  --chart-3: 218 11% 65%;                /* #9ca3af */
  --chart-4: 215 16% 70%;
  --chart-5: 200 22% 62%;
  --chart-6: 173 30% 58%;
  --chart-7: 12 55% 68%;
  --chart-8: 262 45% 72%;
}

/* (c) 圖表空狀態：shadcn empty state（40px muted 圖示 + 標題 + 說明），
       不是巨大插圖，也不是讓使用者盯著一張畫著 0 的空座標軸猜。 */
.fb-chart-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 269px;                     /* 與 canvas 容器同高，有無資料時版面不跳動 */
  padding: 24px;
  text-align: center;
}
.fb-chart-empty > i {
  font-size: 40px;
  line-height: 1;
  color: hsl(var(--muted-foreground) / .45);
}
.fb-chart-empty .fb-chart-empty-title {
  font-size: 14px;
  font-weight: 500;
  color: hsl(var(--foreground));
}
.fb-chart-empty .fb-chart-empty-desc {
  font-size: 13px;
  font-weight: 400;
  color: hsl(var(--muted-foreground));
}

/* =============================================================================
 * §S-13 頂 bar 亮/暗切換鈕 ＋ 頁首「標題前」包裝層（2026-07-31）
 * -----------------------------------------------------------------------------
 * (a) 頂 bar 的亮/暗切換鈕（includes/_head.blade.php 新增的那一顆）。
 *     它是 <button class="navbar-link btn">，但上面「只有圖示要是 32×32 正方形」
 *     那組規則的選擇器只寫到 `> a`（見 §頂 bar 尺寸那段的註解：當時是為了避開
 *     「前台」那顆有文字的下拉），button 對不上 → 會被 .btn 的預設內距撐成長方形、
 *     和旁邊的鈴鐺不等寬。這裡補上同一組值，不去動上面那條規則的選擇器
 *     （改它會連帶影響「前台」與使用者選單，範圍比補一條大得多）。
 *
 * (b) .fb-ph-lead：_shell 第 11 段把 <h1> 裡的「前後頁箭頭」退回頁首時用的包裝層。
 *     display:contents → 包裝層自己不產生盒子，裡面的 <a> 直接成為 .page-header
 *     這個 flex 的排版項目（和主題既有的 .fb-ph-inline 同一手法）；
 *     同時因為 DOM 上多了一層，那些 <a> 不會變成 .page-header 的第一個 <a>，
 *     `.page-header > .btn:not(:first-child):first-of-type { margin-left:auto }`
 *     才會繼續對到真正的動作鈕，動作組維持靠右。
 *     ★這裡不靠 `.page-header > .fb-ph-inline` 那條：頁首是用 fallback 找到、
 *       容器沒有 .page-header class 的頁面也要能生效。
 *
 *     ★★ 2026-08-03 更新：產生這顆包裝層的那段 JS 已經退場
 *        （全站頁首改用頁首 SSOT 元件後，箭頭本來就是 <h1> 的兄弟，不需要再搬），
 *        所以現在**沒有任何元素會帶到 .fb-ph-lead**。
 *        規則本身保留不刪：它只有一行、零副作用，而且是「這個 class 曾經代表什麼」
 *        的唯一說明；真要有人重新引入同名包裝層時，行為也還是對的。
 * ============================================================================= */
.app-header .navbar-nav > .navbar-item > .navbar-link[data-theme-toggle],
#header .navbar-nav > .navbar-item > .navbar-link[data-theme-toggle] {
  min-width: 32px;
  justify-content: center;
  background: transparent;
  box-shadow: none;
}

.fb-ph-lead { display: contents; }


/* =============================================================================
 * §S-14 側欄收合（minified）——把「窄條」本體補回來（2026-07-31）
 * -----------------------------------------------------------------------------
 * 病徵：點側欄左下的收合鈕，localStorage（seller-sidebar-minified）寫進去了、
 *       #app 也確實掛上 .app-sidebar-minified，但畫面「一個像素都沒動」。
 *       實測 1440×900：展開態 側欄 220 / 內容起始 x=220；
 *                      收合態 側欄 220 / 內容起始 x=220（完全一樣）。
 *
 * 真因：整份主題裡跟 .app-sidebar-minified 有關的規則，只有 §Sidebar 個人區塊
 *       末尾那三條（把 profile 的文字與 caret 收起來）。真正把側欄縮成窄條的
 *       width / margin-left / 文字隱藏那一整組，原本住在 Color Admin 的
 *       app.min.css；2026-07-26 拔掉 Color Admin 時沒有一起補回來
 *       → class 掛得再正確也沒有任何規則會吃它。
 *       （2026-fatboy 的主題同樣缺這一段，兩邊都要補；這裡先補 seller。）
 *
 * ─── 收合後「怎麼看子選單」的做法選擇（重要，不是隨手訂的）──────────────
 * Color Admin 原本是「窄條 + 滑到某一項時，該項的子選單絕對定位飛出到右側」。
 * 那個做法有一個前提：側欄本身不能是捲動容器 —— overflow-y:auto 會把飛出的
 * 面板整個裁掉。而本站側欄**必須**捲動：收合態實測內容高度
 *   個人區(48) + 個人選單 6 項(6×36) + 主選單 13 項(13×36) + 收合鈕(40) ≈ 790px，
 * 1440×900 的可用高度只有 850px，1280×720 更只有 670px —— 一定會捲。
 * 換成 position:fixed 也解不了：純 CSS 算不出「滑到的那一項」在畫面上的 y。
 *
 * 所以改成「窄條 + 滑鼠移上去整條側欄浮出展開」：
 *   - 沒滑到 → 60px 窄條，只有圖示（內容區跟著讓出 160px）
 *   - 滑到   → 側欄浮到內容之上展開回 220px，文字回來，has-sub 項目滑到就展開子選單
 *   - 內容區的 margin-left 永遠跟著「窄條寬」，不跟著 hover 寬
 *     → 展開只是覆蓋，版面不會左右跳動
 * 這個做法同時解掉「純圖示軌沒有標籤、使用者不知道每顆是什麼」的問題
 * （不必額外做 tooltip，也就不必動 _shell —— 側欄收合這件事全部是 CSS）。
 *
 * ★ 展開條件用 :hover 與 :has(:focus-visible)，**不能用 :focus-within**：
 *   收合鈕自己就在側欄裡，滑鼠點它之後那顆 <a> 會保持 focus，
 *   :focus-within 會讓側欄一直停在展開態 —— 使用者按下去看到的還是「沒反應」，
 *   等於原病徵換一種方式復發（已實測：加 :focus-within 時收合後量到的仍是 220px）。
 *   :focus-visible 只在鍵盤操作時成立，滑鼠點擊不會觸發，兩種需求同時滿足。
 * ★ :has() 的規則一律**獨立成條**、不與 :hover 併在同一組選擇器：
 *   選擇器清單裡只要有一個不被瀏覽器認得，整條規則會被丟棄 ——
 *   併在一起等於把「窄條」這件事押在 :has() 的支援度上。分開寫的話，
 *   就算 :has() 不被支援，也只是少了鍵盤展開，收合本身照常。
 *
 * ★ 全部包在 @media (min-width: 992px) 內：手機／平板的側欄是覆蓋式抽屜
 *   （translateX(-100%) + 288px），本節一條規則都不能碰到它。
 * ★ POS（layouts/pos）掛的是 .app-without-sidebar，根本沒有 #sidebar；
 *   但 #app 是共用的、_shell 會照樣把 .app-sidebar-minified 掛上去，
 *   所以 #content 那條特地加 :not(.app-without-sidebar) 排除
 *   （否則會與上面 §POS 的 `.app-without-sidebar #content.app-content{margin:0}`
 *     同特異度撞車，而本節在後面會贏 → POS 左邊平白多 60px 空白）。
 * ★ 頂 bar 的 .navbar-header（logo 70px + 「跟賣家站在一起」）刻意不跟著縮到 60px：
 *   logo 本身就比 60px 寬，縮了只會被裁掉；那一格維持 220px，
 *   頁標題插槽的起點不變，收合前後頂 bar 完全不跳動。
 * ============================================================================= */

:root {
  /* 窄條寬＝Color Admin 原值 60px（圖示 18px + 左右各 21px，恰好置中） */
  --fb-sidebar-w-mini: 60px;
}

@media (min-width: 992px) {

  /* ── (1) 內容區讓位量：220px → 60px（收合的全部意義就在這一條） ───────── */
  .app-sidebar-minified:not(.app-without-sidebar) #content.app-content {
    margin-left: var(--fb-sidebar-w-mini);
  }

  /* ── (2) 側欄本體縮成窄條；滑鼠移上去浮出展開 ─────────────────────── */
  .app-sidebar-minified #sidebar.app-sidebar {
    width: var(--fb-sidebar-w-mini);
    transition: width .15s ease, box-shadow .15s ease;
  }
  .app-sidebar-minified #sidebar.app-sidebar:hover {
    width: var(--fb-sidebar-w);
    /* 展開時是浮在內容之上的面板 → 給陰影，才看得出是覆蓋不是推擠 */
    box-shadow: 4px 0 24px rgb(0 0 0 / .12);
  }

  /* ── (3) 窄條態（沒滑到）：只留置中的圖示 ─────────────────────────── */
  .app-sidebar-minified #sidebar:not(:hover) .menu-link {
    justify-content: center;
    gap: 0;
    padding-left: 0;
    padding-right: 0;
  }
  .app-sidebar-minified #sidebar:not(:hover) .menu-text,
  .app-sidebar-minified #sidebar:not(:hover) .menu-caret,
  .app-sidebar-minified #sidebar:not(:hover) .menu-badge {
    display: none !important;
  }
  /* 分組標題：文字收起來，只留一條分隔線維持分組感（本站 _nav 目前沒有用
     .menu-header，這條是給日後加上時不會突然變成一坨亂碼寬度） */
  /* ★2026-08-04 稽核：blade 0 命中 */
  .app-sidebar-minified #sidebar:not(:hover) .menu-header {
    height: 1px;
    padding: 0;
    margin: 8px 12px;
    overflow: hidden;
    text-indent: -999em;
    background: hsl(var(--sidebar-border));
  }
  /* 之前被點開（.expand）的子選單，在窄條態不能露出來 —— 沒有文字的第二層
     只會是一排看不懂的縮排空白 */
  .app-sidebar-minified #sidebar:not(:hover) .menu-submenu {
    display: none !important;
  }
  /* 收合鈕：置中，箭頭翻面朝右（它現在的意思是「展開」） */
  .app-sidebar-minified #sidebar:not(:hover) .app-sidebar-minify-btn {
    margin: 0 auto !important;
    justify-content: center;
  }
  .app-sidebar-minified #sidebar .app-sidebar-minify-btn > svg,
  .app-sidebar-minified #sidebar .app-sidebar-minify-btn > i {
    transform: scaleX(-1);
  }

  /* ── (4) hover 展開時，把 §Sidebar 個人區塊那三條「只留頭像」還原 ────────
     那三條是無條件的（.app-sidebar-minified .app-sidebar …），收合後連展開態
     也會被壓住 → 展開了卻看不到帳號名稱。這裡用同樣帶 !important 的規則覆蓋，
     不去改上面那段（它在窄條態仍然是對的）。 */
  .app-sidebar-minified #sidebar.app-sidebar:hover .menu-profile-link {
    grid-template-columns: 32px 1fr auto;
    justify-content: normal;
  }
  .app-sidebar-minified #sidebar.app-sidebar:hover .menu-profile-info {
    display: flex !important;
  }
  .app-sidebar-minified #sidebar.app-sidebar:hover .menu-profile .menu-caret {
    display: inline-flex !important;
  }

  /* ── (5) hover 展開時：has-sub 滑到就展開子選單（不必點）───────────────
     這就是「收合狀態下滑鼠移上去要看得到子選單」的實作。
     子選單走原本的行內展開（不是絕對定位飛出）→ 側欄的 overflow-y:auto
     照常捲動，長選單不會被裁掉。
     ★ 只在 .app-sidebar-minified 下成立；一般展開態維持「點擊才展開」，
       不會變成滑過去就亂跳。 */
  .app-sidebar-minified #sidebar.app-sidebar:hover .menu-item.has-sub:hover > .menu-submenu {
    display: block;
  }
  .app-sidebar-minified #sidebar.app-sidebar:hover .menu-item.has-sub:hover > .menu-link .menu-caret {
    transform: rotate(90deg);
  }

  /* ── (6) 鍵盤操作也要能展開（獨立成條，:has() 不支援時只損失這一段）──────
     收合態的第二層選單是 display:none → 那些 <a> 連 Tab 都到不了。
     鍵盤使用者一旦收合側欄，全站第二層功能就再也走不到，
     所以這段不是裝飾，是可及性的必要條件。
     :focus-visible 只在鍵盤時成立 → 滑鼠點收合鈕不會意外把側欄卡在展開態。 */
  .app-sidebar-minified #sidebar.app-sidebar:has(:focus-visible) {
    width: var(--fb-sidebar-w);
    box-shadow: 4px 0 24px rgb(0 0 0 / .12);
  }
  .app-sidebar-minified #sidebar.app-sidebar:has(:focus-visible) .menu-link {
    justify-content: flex-start;
    gap: .5rem;
    padding-left: .5rem;
    padding-right: .5rem;
  }
  .app-sidebar-minified #sidebar.app-sidebar:has(:focus-visible) .menu-text {
    display: block !important;
  }
  .app-sidebar-minified #sidebar.app-sidebar:has(:focus-visible) .menu-caret {
    display: inline-flex !important;
  }
  .app-sidebar-minified #sidebar.app-sidebar:has(:focus-visible) .menu-profile-link {
    grid-template-columns: 32px 1fr auto;
    justify-content: normal;
  }
  .app-sidebar-minified #sidebar.app-sidebar:has(:focus-visible) .menu-profile-info {
    display: flex !important;
  }
  .app-sidebar-minified #sidebar.app-sidebar:has(:focus-visible) .menu-item.has-sub:focus-within > .menu-submenu {
    display: block !important;
  }
}

/* -----------------------------------------------------------------------------
 * §S-14 (7) 手機抽屜不吃「收合」這件事
 * -----------------------------------------------------------------------------
 * §Sidebar 個人區塊那三條「收合時只留頭像」沒有包 media query，
 * 而 .app-sidebar-minified 是記在 localStorage 的全域狀態 ——
 * 使用者在桌機收合過側欄，換到手機開抽屜時，個人區就只剩一顆頭像，
 * 帳號名稱、客戶代號、續約訂閱通通不見（實測 375×812 已重現）。
 * 手機側欄是 288px 的覆蓋式抽屜，根本沒有「窄條」這個型態，
 * 這裡把個人區還原成完整樣子。
 * ★ 這是在補上面那段的邊界，不是改它 —— 桌機窄條態的行為一個字都沒動。
 * -------------------------------------------------------------------------- */
@media (max-width: 991.98px) {
  .app-sidebar-minified #sidebar.app-sidebar .menu-profile-link {
    grid-template-columns: 32px 1fr auto;
    justify-content: normal;
  }
  .app-sidebar-minified #sidebar.app-sidebar .menu-profile-info {
    display: flex !important;
  }
  .app-sidebar-minified #sidebar.app-sidebar .menu-profile .menu-caret {
    display: inline-flex !important;
  }
}

/* -----------------------------------------------------------------------------
 * §S-15 頁首「動作組 + 說明鈕」雙 margin-left:auto → 動作鈕被推到畫面正中央
 * -----------------------------------------------------------------------------
 * 2026-08-03 逐頁量測抓到（/good/storageLocation 實測：「＋新增儲位」x=754，
 * 整整落在 1440 寬畫面的正中間，「說明」在 x=1355 最右）。
 *
 * 真因：頁首那一列同時有兩個吃 auto 邊界的元素 ——
 *   ① view 自己包的 `<div class="ms-auto">` 動作組（margin-left:auto）
 *   ② 上面 §頁首 那條 `.fb-page-help { order:99; margin-left:auto }`
 * flex 版面遇到「多個 auto 邊界」是**把剩餘空間平均分給它們**，不是各自靠右
 * （實測兩者的 computed margin-left 都是 510.35px，剛好把剩餘空間對半分）。
 * 結果就是動作鈕浮在中間，與肥肥 ERP 的「動作全部靠右、說明在最後」對不起來。
 *
 * 既有的 `.page-header > .ms-auto ~ *  { margin-left:0 }` 防的是同一件事，
 * 但它用的是後繼兄弟選擇器 —— 而 206 頁的 view 都把「說明」寫在動作組**之前**
 * （順序是靠 order:99 調的，不是靠 DOM 順序），所以那條永遠對不上。
 *
 * 修法：頁首裡只要存在動作組，就讓說明鈕交出 auto 邊界，
 * 由動作組單獨把「動作組 + 說明」整批推到右端（說明仍靠 order:99 排在最後）。
 * 沒有動作組的頁面不受影響，說明鈕照舊自己靠右。
 * ★不動任何 view：這是 250+ 支手刻頁首共用的版面規則，只能在主題這層收。
 * -------------------------------------------------------------------------- */
.page-header:has(> .ms-auto) > .fb-page-help,
.page-header:has(> .ms-auto) > .fb-ph-inline > .fb-page-help {
    margin-left: 0;
}

/* -----------------------------------------------------------------------------
 * §S-16 分頁鈕改吃 Lucide 之後的尺寸（vendor/pagination/seller.blade.php）
 * -----------------------------------------------------------------------------
 * 上一頁／下一頁原本是 Font Awesome 的字型圖示，大小跟著 .page-link 的 font-size 走；
 * 換成 Lucide 之後裡面是 <svg>，font-size 管不到它，實測整顆鈕變成 31×30 ——
 * 與規格 §0 的 32px 級距差一階，而且數字鈕 24×30、箭頭鈕 31×30，同一列三種寬度。
 * 這裡把分頁鈕釘成 shadcn 的方形 32×32（數字與箭頭同尺寸），svg 統一 14px。
 * -------------------------------------------------------------------------- */
.pagination .page-link {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 32px;
    height: 32px;
    padding: 0 6px;
}
.pagination .page-link svg {
    width: 14px;
    height: 14px;
}

/* -----------------------------------------------------------------------------
 * §S-17 側欄「客戶代號」對比補到 WCAG AA（chip 高度**不可**動，見下方）
 * -----------------------------------------------------------------------------
 * ★★ 這一節原本寫的是「把續約訂閱 chip 從 16px 拉到 20px」——**那是錯的，已撤回**。
 *    實測撤回前：chip 20px 塞進 .tenant-meta 這個 16px 的格線列，
 *    上下各被 overflow:hidden 裁掉 2px（量到 tenant-meta 90~106 vs chip 88~108）。
 *    §側欄 profile 已經留了完整量測紀錄：.menu-profile-link 是 shadcn
 *    SidebarMenuButton size="lg" 的 48px，扣掉 padding 剩 32px，
 *    再用 grid-template-rows:16px 16px 切成兩行 —— 16px 是版面硬限制，不是隨手寫的數字。
 *    留這段紀錄是為了避免下一輪有人又想「順手把 chip 放大成標準 badge」。
 *
 * 真正該補的是**對比**：.tenant-code 用的是 hsl(var(--sidebar-fg) / .75)，
 * 實測疊在 --sidebar-bg 上 **亮色 3.97 / 暗色 4.44，兩個模式都沒過 AA 4.5**。
 * 主題上方 §側欄 profile 早就為了同一個理由把舊 markup 的 `h6` 校正到 .9
 * （亮 5.72 / 暗 5.84 都過），但 2026-08-03 把 markup 換成肥肥的兩行結構之後，
 * 生效的選擇器變成 .tenant-code —— 那條 .9 的校正就落空了。
 * 客戶代號是業主報修時要唸出來的識別碼、每一頁都在，不是裝飾字，補回 .9。
 * ★只動本主題自己的 alpha 修飾，不動 --sidebar-fg 這顆與 2026-fatboy 共用的 token，
 *   兩專案的配色基準不會漂移。
 * -------------------------------------------------------------------------- */
#sidebar .menu-profile-info .tenant-code {
    color: hsl(var(--sidebar-fg) / .9);
}

/* -----------------------------------------------------------------------------
 * §S-18 沒有語意色的唯讀 chip（`<span class="btn btn-xs">純文字</span>`）
 * -----------------------------------------------------------------------------
 * 揀包／貨態頁把「最新貨態」「貨態時間」這種純文字包在 span.btn.btn-xs 裡當字級用
 * （Pick/deliveryTrackingPackage 4 處）。它沒有任何色系 class，
 * 於是吃到 .btn 的基礎外觀 —— 表格裡每一格都畫出一個空的按鈕框，
 * 使用者會以為那是可以點的。
 * 規格 §9：唯讀狀態只用 badge 語彙，沒有語意色時就是純文字。
 * ★只鎖「沒有任何色系修飾」的 span：帶 btn-green / btn-red / rounded-pill 的
 *   狀態徽章完全不受影響（那是刻意的語意色）。
 * -------------------------------------------------------------------------- */
.app-content td > span.btn.btn-xs:not(.btn-rounded):not([class*="rounded-pill"]):not(.btn-dark):not(.btn-light):not(.btn-white):not(.btn-default):not(.btn-secondary):not(.btn-primary):not(.btn-yellow):not(.btn-green):not(.btn-success):not(.btn-red):not(.btn-danger):not(.btn-gray):not(.btn-warning):not(.btn-info):not(.btn-indigo):not(.btn-purple):not(.btn-teal):not(.btn-lime):not(.btn-pink):not(.btn-orange):not(.btn-blue):not(.btn-cyan) {
    background: transparent;
    border-color: transparent;
    box-shadow: none;
    padding: 0;
    color: inherit;
    font-weight: inherit;
}

/* -----------------------------------------------------------------------------
 * §S-19 Toast（gritter）→ shadcn Toast 皮
 * -----------------------------------------------------------------------------
 * 全站的成功／錯誤提示都走 includes/_script.blade.php 的 msg() → $.gritter.add()，
 * 但整份主題對 gritter **一條規則都沒有**（grep 0 筆），所以它還是外掛出廠的樣子：
 * 用 PNG 精靈圖（images/gritter.png）拼出的深色氣泡、文字寫死 #eee、圓角 0、無陰影。
 * 實測 computed：background-image = url(.../gritter.png)、color = rgb(238,238,238)、
 * border-radius = 0px、box-shadow = none —— 在已經全 shadcn 的頁面上跳出來非常突兀，
 * 而且它是**唯一**還在用點陣圖當外觀的元件（亮色模式下深色氣泡尤其明顯）。
 *
 * 這裡照 shadcn 的 Toast 重做：--popover 底、1px --border、var(--radius)、彈出層陰影，
 * 標題 14px/600、內文 13px --muted-foreground，關閉鈕收成右上角的 ghost 圖示鈕。
 * ★不動 gritter 的 JS 與 DOM，只換皮 —— 呼叫端（msg()）一個字都不用改。
 * ★.gritter-top / .gritter-bottom 是純裝飾用的精靈圖切片（沒有文字內容），
 *   把它們的高度與背景收掉即可，元素本身保留。
 * -------------------------------------------------------------------------- */
#gritter-notice-wrapper {
    top: 68px;                      /* 讓開頂 bar（.app-header 高 56px + 12px 間距） */
    right: 16px;
    width: 340px;
}
#gritter-notice-wrapper .gritter-item-wrapper {
    background: none;
    margin: 0 0 10px;
    filter: none;
}
#gritter-notice-wrapper .gritter-top,
#gritter-notice-wrapper .gritter-bottom {
    background: none;
    height: 0;
    margin: 0;
    padding: 0;
    font-size: 0;
    line-height: 0;
}
#gritter-notice-wrapper .gritter-item {
    background: hsl(var(--popover));
    color: hsl(var(--popover-foreground));
    border: 1px solid hsl(var(--border));
    border-radius: var(--radius);
    box-shadow: 0 4px 12px -2px rgb(0 0 0 / .08);
    padding: 12px 40px 12px 14px;
    font-family: inherit;
}
#gritter-notice-wrapper .gritter-title {
    display: block;
    font-size: 14px;
    font-weight: 600;
    line-height: 1.35;
    color: hsl(var(--foreground));
    text-shadow: none;
    padding: 0;
    margin: 0 0 2px;
}
#gritter-notice-wrapper .gritter-item p {
    font-size: 13px;
    line-height: 1.5;
    color: hsl(var(--muted-foreground));
    margin: 0;
    padding: 0;
}
/* 關閉鈕：gritter 出廠是把「Close Notification」這段文字用 text-indent 推出畫面、
   再用精靈圖畫一個 ✕。文字保留（螢幕閱讀器要用），只把外觀換成 shadcn 的 ghost 圖示鈕。 */
#gritter-notice-wrapper .gritter-close {
    display: block;
    top: 8px;
    right: 8px;
    left: auto;
    width: 22px;
    height: 22px;
    background: none;
    border-radius: var(--radius-sm);
    text-indent: -9999px;
    overflow: hidden;
    opacity: .6;
    transition: background-color .15s ease, opacity .15s ease;
}
#gritter-notice-wrapper .gritter-close::before,
#gritter-notice-wrapper .gritter-close::after {
    content: "";
    position: absolute;
    left: 6px;
    top: 10px;
    width: 10px;
    height: 1.5px;
    background: hsl(var(--foreground));
    border-radius: 1px;
}
#gritter-notice-wrapper .gritter-close::before { transform: rotate(45deg); }
#gritter-notice-wrapper .gritter-close::after  { transform: rotate(-45deg); }
#gritter-notice-wrapper .gritter-close:hover {
    background: hsl(var(--accent));
    opacity: 1;
}

/* -----------------------------------------------------------------------------
 * §S-20 表單驗證態 → shadcn
 * -----------------------------------------------------------------------------
 * Bootstrap 出廠的 .is-invalid / .is-valid 會把邊框換成 #dc3545 / #198754，
 * 並在輸入框右側塞一張 base64 的驚嘆號／勾勾 SVG 當背景圖（`background-image`），
 * focus 光暈也是寫死的 rgba(220,53,69,.25) —— 三樣都不在調色盤內，暗色不翻轉。
 * shadcn 的做法是只換 border 與 ring 到 --destructive，不放圖示。
 * ★這裡不新增任何 class，view 照舊用 Bootstrap 的 .is-invalid —— 只換外觀。
 * -------------------------------------------------------------------------- */
.app-content .form-control.is-invalid,
.app-content .form-select.is-invalid,
.app-content .was-validated .form-control:invalid,
.app-content .was-validated .form-select:invalid {
    border-color: hsl(var(--destructive));
    background-image: none;
    padding-right: .75rem;
}
.app-content .form-control.is-invalid:focus,
.app-content .form-select.is-invalid:focus,
.app-content .was-validated .form-control:invalid:focus {
    box-shadow: 0 0 0 2px hsl(var(--background)), 0 0 0 4px hsl(var(--destructive) / .4);
    border-color: hsl(var(--destructive));
}
.app-content .form-control.is-valid,
.app-content .form-select.is-valid,
.app-content .was-validated .form-control:valid {
    border-color: hsl(var(--success, var(--border)));
    background-image: none;
    padding-right: .75rem;
}
.app-content .invalid-feedback,
.app-content .valid-feedback {
    font-size: 12px;
    margin-top: 4px;
}
/* ★訊息文字用 --destructive-fg，不是 --destructive。
   後者是**底色** token（#EF4444），當文字壓在白底上只有 3.76:1，沒過 AA；
   --destructive-fg（亮 #BA1B1B / 暗 #F87171）就是為了紅字才拆出來的
   —— 見 §1 token 註解與 .alert-danger 的同一套用法。 */
.app-content .invalid-feedback { color: hsl(var(--destructive-fg)); }
/* checkbox / radio 的錯誤態（shadcn 只換邊框色，不換底） */
.app-content .form-check-input.is-invalid {
    border-color: hsl(var(--destructive));
}

/* -----------------------------------------------------------------------------
 * §S-21 送出中：disabled 一律「不可點的手勢 + 降低對比」，並支援 spinner
 * -----------------------------------------------------------------------------
 * 主題已經有 .btn:disabled 的顏色處理，但缺兩件使用者實際感受得到的事：
 *   ① cursor 還是 pointer —— 看起來還能點，使用者會連按好幾下（重複送出）
 *   ② 沒有等待中的視覺，只能靠各頁自己塞圖示
 * 這裡補上；spinner 用純 CSS，view 只要在按鈕上加 .is-loading 就會出現，
 * 不需要載入任何額外資源，也不影響沒有這個 class 的既有按鈕。
 * -------------------------------------------------------------------------- */
.app-content .btn:disabled,
.app-content .btn.disabled,
.app-content fieldset:disabled .btn {
    cursor: not-allowed;
    pointer-events: auto;          /* 保留 cursor 提示（Bootstrap 預設 none 會連游標都不換） */
}
.app-content .btn.is-loading {
    position: relative;
    color: transparent !important;
    pointer-events: none;
}
.app-content .btn.is-loading > * { visibility: hidden; }
.app-content .btn.is-loading::after {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    width: 14px;
    height: 14px;
    margin: -7px 0 0 -7px;
    border: 2px solid currentColor;
    border-top-color: transparent;
    border-radius: 50%;
    color: hsl(var(--foreground));
    animation: fbBtnSpin .6s linear infinite;
}
.app-content .btn-primary.is-loading::after,
.app-content .btn-yellow.is-loading::after { color: hsl(var(--primary-foreground)); }
.app-content .btn-dark.is-loading::after { color: hsl(var(--dark-foreground)); }
@keyframes fbBtnSpin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
    .app-content .btn.is-loading::after { animation-duration: 2s; }
}

/* -----------------------------------------------------------------------------
 * §S-22 表格「已選取的列」
 * -----------------------------------------------------------------------------
 * 全站列表頁都有勾選框 + 批次操作，但勾起來之後那一列**完全沒有變化** ——
 * 使用者要一路往左找哪個框被打勾了才知道選了什麼（實測儲位／商品／訂單皆同）。
 * shadcn 的 DataTable 對 selected row 是上 --accent 底。
 * ★用 :has() 直接讀勾選狀態，不需要任何 JS 也不需要改 view；
 *   :has() 不支援時只是沒有這個提示，不會壞掉任何既有行為。
 * ★hover 仍然要比選中更明顯一階，否則滑過去反而「看起來取消選取了」。
 * -------------------------------------------------------------------------- */
.app-content tbody tr:has(input[type="checkbox"]:checked) > * {
    background-color: hsl(var(--accent) / .6);
}
.app-content tbody tr:has(input[type="checkbox"]:checked):hover > * {
    background-color: hsl(var(--accent));
}

/* -----------------------------------------------------------------------------
 * §S-23 側欄 profile：第二行回歸「純 metadata」，訂閱狀態移進帳戶區選單
 * -----------------------------------------------------------------------------
 * 業主回報「續約訂閱的『閱』被切掉」。實測（1440 桌機、7 碼代號）：
 *   .tenant-meta 可用 113px，內容需要 49(代號) + 2 + 12(ⓘ) + 2 + 57(chip) = 122 → 溢出 9px，
 *   而三個子元素**全是 flex: 0 0 auto**，沒有任何一個會收縮
 *   → 溢出全落在排最後的 chip 上，被 overflow:hidden 切掉。
 *   （§側欄 profile 的註解寫「全部由租戶碼吸收」，但 .tenant-code 是 0 0 auto，
 *     根本不會吸收 —— 又一條「寫了沒生效」。）
 *
 * ★這不是「差幾 px」：6 碼(肥肥 C00001) 缺 4px、7 碼(seller) 缺 9px、10 碼缺 30px。
 *   靠縮字級／縮內距／加寬側欄都只對得起當下這一組數字，換個租戶就再壞一次；
 *   而讓代號截斷更不行 —— 那是業主報修時要唸給客服的識別碼
 *   （2026-07-29 業主已經為了同一件事回報過，當時定案就是「代號不參與壓縮」）。
 *
 * ★定案（照 shadcn SidebarMenuButton / NavUser 的慣例）：
 *   第二行只放純 metadata（代號 + ⓘ），狀態標籤移進 #appSidebarProfileMenu 最上方。
 *   移過去的理由不只是版面：那顆是 `<small>`、**沒有 href，本來就是狀態標籤不是連結**，
 *   而它下方緊接著就是「訂閱管理／方案介紹／點數儲值」——同一組帳務功能。
 *   移動後第二行是 63/113，**餘裕 50px**。
 *
 * ★安全網：.tenant-code 改成可收縮。三個子元素全部 0 0 auto 正是這次的根因；
 *   萬一未來代號長到 113px 以上，要的是「優雅截斷」而不是「把別人擠出畫面外」。
 *   ⓘ 維持 0 0 12px（它是固定尺寸圖示，壓縮它只會變形）。
 *
 * ★avatar 右下角的「需要續約」狀態點**刻意沒有做**：
 *   view 端那顆 chip 是無條件輸出的靜態標籤，`_nav.blade.php` 目前拿不到任何
 *   「這個租戶是否需要續約 / 訂閱是否到期」的資料 —— 憑空畫一顆恆亮的紅點，
 *   等於對每個租戶都謊報「你該續約了」。要做這顆點必須先有資料來源（後端不在本輪範圍）。
 *
 * ★純 CSS + 一處 markup 搬移，2026-fatboy 是同一個病（業主截圖已確認「閱」同樣被切），
 *   把本節與 _nav 的搬移原樣套過去即可。
 * -------------------------------------------------------------------------- */
/* ★2026-08-06：`.tenant-code` 第 4 條宣告，**四條全部被後面覆蓋，整塊不生效**。
   值本身與勝出者相同（hidden/ellipsis/0），所以畫面看不出差別 —— 這正是它一直沒被發現的原因。
   行號引用已漂掉，改引章節名。 */
#sidebar .menu-profile-info .tenant-code {
    /* ★已被 §S-36 覆蓋（實測生效值：flex=0 1 auto，本行的 1 1 auto 未生效） */
    flex: 1 1 auto;
    /* ★已被 §S-34 覆蓋（實測生效值：min-width=0px） */
    min-width: 0;
    /* ★已被 §S-34 覆蓋（實測生效值：overflow=hidden） */
    overflow: hidden;
    /* ★已被 §S-34 覆蓋（實測生效值：text-overflow=ellipsis） */
    text-overflow: ellipsis;
}
/* 搬進帳戶區選單的訂閱狀態列：它不再是 profile 按鈕的一部分，
   不吃 .menu-profile-info 的 grid 兩行版面，只是一列靠左的狀態標籤。 */
#sidebar #appSidebarProfileMenu .fb-plan-status {
    display: flex;
    align-items: center;
    padding: 6px 12px 2px;
    grid-template-rows: none;
    height: auto;
    overflow: visible;
}
#sidebar #appSidebarProfileMenu .dropdown-divider {
    margin: 6px 12px;
    border-color: hsl(var(--sidebar-border));
    opacity: 1;
}

/* -----------------------------------------------------------------------------
 * §S-24 表單型表格（.fb-form-table）——「label 左／控制項右」的列式表單
 * -----------------------------------------------------------------------------
 * seller 的編輯頁有 11 支把表單畫成 <table>（label 一欄、控制項一欄）。
 * 規格範本 B 寫的是「label 在上、row g-3」，但這些表的 <td> 數量是鎖死的
 * （禁止刪除欄位 → 不能把兩欄併成一欄），改成 grid 就必然要動 td 總數。
 * 2026-fatboy 的 good/storageLocation/edit 對**同一張表**已經做過取捨，
 * 檔內註解寫得很清楚：保留列式排版，只做 shadcn 化。這裡照它的結論做，
 * 兩邊並排才會看不出是兩個系統。
 *
 * 對應到 fatboy 那支逐 td 手寫的 `ps-0 py-2 / pe-0 py-2 / width:170px`，
 * 這裡收成一個 class：seller 有 11 支要改，逐 td 手寫會產生上百處雜訊 diff，
 * 而且容易漏。視覺結果與 fatboy 完全相同。
 *
 * ★只作用在明確標了 .fb-form-table 的表格 —— 全站的資料表格（.table-panel）
 *   一個都不會被波及。
 * -------------------------------------------------------------------------- */
.app-content table.fb-form-table > tbody > tr > td {
    padding-top: .5rem;
    padding-bottom: .5rem;
    border: 0;
    vertical-align: middle;
}
/* 第一欄＝label 欄：固定寬度讓所有列的控制項對齊成一直線（fatboy 用 style="width:170px"） */
.app-content table.fb-form-table > tbody > tr > td:first-child {
    width: 170px;
    padding-left: 0;
    white-space: nowrap;
}
.app-content table.fb-form-table > tbody > tr > td:last-child { padding-right: 0; }
/* 多行控制項（textarea / 一組欄位）的 label 要對齊到第一行，不是垂直置中 */
.app-content table.fb-form-table > tbody > tr:has(textarea) > td:first-child,
.app-content table.fb-form-table > tbody > tr:has(.row) > td:first-child { vertical-align: top; padding-top: .75rem; }
/* 表單型表格不該有列 hover（那是資料列的語彙，會讓人以為整列可點） */
.app-content table.fb-form-table > tbody > tr:hover > * { background: transparent; }
/* label：12px/500 --muted-foreground（規格 §6） */
.app-content table.fb-form-table .form-label { font-size: 12px; font-weight: 500; }
@media (max-width: 575.98px) {
    /* 手機：170px 的 label 欄會把控制項擠成一條縫 → 收窄 */
    .app-content table.fb-form-table > tbody > tr > td:first-child { width: 96px; white-space: normal; }
}

/* -----------------------------------------------------------------------------
 * §S-25 focus-visible：把「硬 outline」統一成 shadcn 的間隙環
 * -----------------------------------------------------------------------------
 * 實測（鍵盤 Tab 到列表頁第一顆按鈕）：`outline: rgb(9,9,11) solid 2px`、box-shadow: none。
 * 那是 §2 基礎 L571 那條 `outline: 2px solid hsl(var(--ring))` ——
 * 它與 §4 的 `.btn:focus-visible { box-shadow: 0 0 0 2px bg, 0 0 0 4px ring }` 同時存在，
 * 但 outline 與 box-shadow 是**兩個不同屬性**，不會互相覆蓋：
 * 結果是「間隙環有畫、外面又多包一圈近黑硬框」，看起來就是加粗的黑邊，不是 shadcn 的環。
 * ★L571 在主題上半部、與 2026-fatboy 共用，不能動 —— 這裡在檔尾把 outline 收掉，
 *   讓既有的間隙環單獨成立（等於只拿掉多出來的那一圈，沒有改變任何顏色 token）。
 *
 * ★為什麼不改成規格字面的 `0 0 0 3px hsl(var(--ring)/.16)`：
 *   主題 §6634 有完整的取捨紀錄 —— shadcn 的 ring 是 `ring-2 + ring-offset-2`，
 *   環與元素之間要有一圈背景色間隙，環才會「浮」在外面而不是「包」在身上；
 *   單層 3px 環貼著邊框畫，回到的就是「加粗邊框」的觀感。
 *   而且 2026-fatboy 用的是同一條，改字面值會讓兩邊分歧。維持間隙環寫法。
 * -------------------------------------------------------------------------- */
.btn:focus-visible,
.form-control:focus,
.form-select:focus,
.page-link:focus-visible,   /* ★2026-08-04 稽核：原為 :focus —— <a> 用 :focus
                               連滑鼠點擊都會留著焦點框（點完第 3 頁那顆一直亮）。
                               shadcn 一律用 :focus-visible（只有鍵盤導覽才亮）。
                               ★同組的 .form-control/.form-select 維持 :focus 是對的：
                                 瀏覽器對文字輸入框本來就把點擊也算 focus-visible，
                                 而且輸入中看得到框是使用者預期。 */
.dropdown-item:focus-visible,
.form-check-input:focus,
.fb-tab:focus-visible,
.nav-link:focus-visible,
.select2-container--bootstrap-5 .select2-selection:focus {
    outline: none;
    outline-offset: 0;
}
/* .fb-tab（分段控制器）先前**完全沒有** focus-visible 規則 → 鍵盤操作量到的是
   Chrome 預設的 `outline: auto 1px rgb(0,95,204)` 藍框。補成與按鈕同一套間隙環。 */
.fb-tab:focus-visible,
.nav-link:focus-visible {
    /* §S-73：原 `0 0 0 2px hsl(var(--background)), 0 0 0 4px hsl(var(--ring) / .5)` → 官方兩層 */
    border-color: hsl(var(--ring));
    box-shadow: 0 0 0 3px hsl(var(--ring) / .5);
    border-radius: var(--radius-md);
}

/* ★★ 逐條列舉永遠會漏：實測鍵盤 Tab 進站的**第一個**焦點是頂 bar 的 logo
   （a.navbar-brand），它不在上面任何一條裡 → 量到 Chrome 出廠的
   `outline: rgb(0,95,204) auto 1px`（暗色是 rgb(153,200,255)），一眼就是沒做過的地方。
   改成全域規則：任何鍵盤焦點都吃同一套間隙環，之後新增的元件自動涵蓋。
   ★:focus-visible 只在鍵盤操作時成立，滑鼠點擊不會觸發，不影響一般使用。
   ★box-shadow 只在「聚焦當下」生效，離焦即還原，不會蓋掉卡片等元件平時的陰影。 */
body :focus-visible {
    outline: none;
    /* ★§S-73：原 `box-shadow: 0 0 0 2px hsl(var(--background)), 0 0 0 4px hsl(var(--ring) / .5);`
       改成官方的兩層。border-color 這條在 border-width:0 的元素上是 no-op，
       等同 Tailwind 的 `border-ring`（它也只設 border-color，不設寬度）。 */
    border-color: hsl(var(--ring));
    box-shadow: 0 0 0 3px hsl(var(--ring) / .5);
}
/* POS 是全螢幕作業頁、底色與內容區不同，間隙用它自己的底色才不會露出一圈亮邊 */
/* ★§S-73：原 `box-shadow: 0 0 0 2px hsl(var(--card)), 0 0 0 4px hsl(var(--ring) / .5);`
   —— 上面那句「間隙用它自己的底色」的前提是**有間隙**；官方寫法沒有間隙那一圈，
   所以這條的特殊處理已無對象。規則保留（不刪），值改成與全域同一套官方兩層，
   避免日後有人只改全域、這條靜靜留著舊的間隙環而分岔。 */
.pos :focus-visible { border-color: hsl(var(--ring)); box-shadow: 0 0 0 3px hsl(var(--ring) / .5); }

/* -----------------------------------------------------------------------------
 * §S-26 dropdown item 字級 → shadcn 的 text-sm
 * -----------------------------------------------------------------------------
 * 實測 16px（Bootstrap 的 1rem 繼承）。shadcn 的 DropdownMenuItem 是 text-sm = 14px，
 * 而同一顆下拉的觸發鈕是 13px（btn-sm）—— 點開之後字反而變大兩級，節奏是斷的。
 * radius(8px) / padding(7.2×10.4) / divider 都已經正確，這裡只補字級。
 * ★.dropdown-menu.media-list（頂 bar 通知清單）維持原樣：那是有頭像的兩行卡片列，
 *   不是一般選單項目，字級自成一套。
 * -------------------------------------------------------------------------- */
.dropdown-menu:not(.media-list) > .dropdown-item,
.dropdown-menu:not(.media-list) .dropdown-item,
.dropdown-menu:not(.media-list) > li > .dropdown-item {
    font-size: 14px;
}

/* -----------------------------------------------------------------------------
 * §S-27 POS：售罄遮罩太透，底下的文字與按鈕透出來疊在一起
 * -----------------------------------------------------------------------------
 * 實測 .not-available-text 的底是 rgba(24,24,27,.75)。
 * 商品磚底下就是「工作臺／登入時間」與「手動輸入／測試印單」，
 * .75 的黑紗蓋不住，兩層字疊在一起（暗色模式更明顯，因為底下的字本來就偏亮）。
 * 提到 .92：仍看得出下面有東西（維持「這張卡還在、只是不可選」的語意），
 * 但字不再互相干擾。
 * ★只動不透明度，不動尺寸與觸控階 —— POS 的 40/62px 觸控目標一格都沒碰。
 * -------------------------------------------------------------------------- */
.pos .not-available-text,
.pos .product.not-available .not-available-text {
    /* ★用固定的深色、不吃 --foreground：那顆 token 在暗色模式會翻成近白，
       實測會把遮罩畫成 rgba(250,250,250,.92) —— 白紗蓋在深色商品磚上，比原本更糟。
       這是蓋在商品圖上的 scrim（永遠要深底淺字），不是跟著主題翻轉的介面色。
       維持原本的色相，只把 .75 提到 .92。 */
    background: rgba(24, 24, 27, .92);
}

/* -----------------------------------------------------------------------------
 * §S-28 頂 bar 品牌標語：對比不足（2026-08-03 逐元素量測抓到）
 * -----------------------------------------------------------------------------
 * 元素：includes/_head.blade.php 的
 *   小字「跟 賣 家 站 在 一 起」，class 是 text-gray opacity-70 mx-2 fs-11px fw-600。
 *
 * 只讀 computed color 會誤判成「過關」：色值是 --muted-foreground
 * （亮 rgb(113,113,122) / 暗 rgb(161,161,170)），對白底是 4.83、對暗底是 6.91，
 * 兩個都 >= 4.5。**但那不是使用者看到的顏色** —— 元素自己還帶 opacity:.7，
 * 實際畫到畫面上的是與底色混合後的結果：
 *   亮：rgb(156,156,162) on rgb(255,255,255) = 2.74:1
 *   暗：rgb(120,120,127) on rgb(24,24,27)    = 4.04:1
 * 12px / fw-600 不算大字（大字門檻是 18.66px，或 14px 且 >= 700），所以要求是 4.5:1，
 * 兩種模式都不合格，亮色模式尤其明顯（只有需求的六成）。
 *
 * 治本＝把那層 opacity 拿掉，色彩交給既有的 --muted-foreground（它本來就是為
 * 「次要文字」調過、量到 4.83 / 6.91 都過關）。這樣亮暗都一次到位，
 * 也不必為了補對比去挑一個新的寫死灰階（那會變成第二個色彩來源）。
 * ★不刪 view 上的任何 class：opacity-70 留著，只是在這裡被覆蓋 ——
 *   那顆 class 在別的地方仍然有效，而且日後對照 git 歷史看得到原本的意圖。
 * ★只影響頂 bar 這一顆標語（選擇器鎖在 .app-header .navbar-brand small）。
 * ★★ 必須帶 !important，而且這一點是實測才知道的：本檔 2558 行的補位工具類
 *    `.opacity-70 { opacity: .7 !important }` 帶了 !important（Color Admin 的
 *    工具類慣例），不帶 !important 的版本寫了等於沒寫 —— 第一次補這條時就是
 *    這樣，量出來還是 opacity .7 / 對比 2.74，看起來像沒生效。
 *    「寫了」不等於「生效」，改完一定要回瀏覽器再量一次。
 * -------------------------------------------------------------------------- */
.app-header .navbar-brand small {
    opacity: 1 !important;
}

/* -----------------------------------------------------------------------------
 * §S-29 側欄收合鈕的觸控/點擊區只有 16x16
 * -----------------------------------------------------------------------------
 * 實測（1440 桌機，亮暗與展開/收合四態都一樣）：
 *   .app-sidebar-minify-btn = 16 x 16，圓角 9999px，底色 rgb(239,239,240)。
 * 16px 就是那顆 lucide 圖示的原尺寸 —— 等於這顆鈕「沒有自己的盒子」，
 * 使用者必須正好點在箭頭的線條上。
 * 對照：shadcn 的 SidebarTrigger 是 size-7（28px），WCAG 2.5.8 的最小目標是 24x24。
 * 側欄裡其他可點的東西（menu-link、profile）量到都是 32 / 48px，只有這一顆是 16。
 *
 * ★這不是 seller 才有的走鐘：2026-fatboy 的同名規則（該檔 977-982 行）內容一模一樣，
 *   兩邊都是「只給了顏色與圓角，沒給尺寸」。這裡先在 seller 補齊。
 * ★用 padding 撐而不是寫死 width/height：圖示仍然是 16px、位置不變，
 *   只是外圈多了可點的範圍，收合態（60px 窄條）28px 仍然置中放得下。
 * ★不動 §7645 那段「收合時置中 + 箭頭翻面」的規則，那段在窄條態仍然是對的。
 * -------------------------------------------------------------------------- */
.app-sidebar-minify-btn {
    padding: 6px;
    box-sizing: content-box;
}

/* --- S-30. 按鈕鍵盤焦點環：救回被 !important 蓋掉的 :focus-visible（2026-08-03）------
   病徵：用鍵盤 Tab 到任何按鈕上，畫面**完全沒有焦點指示**（WCAG 2.4.7 失效）。
   真因（量出來的，不是推論）：
     L295  `.btn { box-shadow: none !important }`        ← 特異度 (0,1,0) 但帶 !important
     L1859 `.btn:focus-visible { box-shadow: 0 0 0 2px …, 0 0 0 4px … }`  ← (0,2,0) 沒有 !important
   CSS 規則：!important 宣告贏過一般宣告，與特異度無關 → 焦點環永遠被 none 蓋掉。
   實測（真鍵盤 Tab 落在 .btn 上、等 600ms 後讀 computed）：
     修前 box-shadow: none / outline-style: none  → 零焦點指示
     對照組 .form-control 同時量到 ring 正常（所以先前有代理只量輸入框與連結，
     就下了「全站焦點環正常」的結論 —— 那是抽樣偏差）。
   ★ L295 的 !important 本身有存在理由（壓掉 Bootstrap .btn 的預設陰影），不動它；
     改成讓 focus-visible 這條也帶 !important，兩者各司其職。
   ★ 用的是 shadcn 官方 ring-2 + ring-offset-2 寫法（與 L1859、與 2026-fatboy 實作一致）；
     肥肥 .agent/shadcn-block-spec.md §0 寫的單層 `0 0 0 3px ring/.16` 是過期的規格文字，
     兩邊實際出貨的程式碼都是這個雙層版本。 */
.btn:focus-visible,
.navbar-link.btn:focus-visible,
.app-content .btn:focus-visible {
    /* ★§S-73：原 `box-shadow: 0 0 0 2px hsl(var(--background)), 0 0 0 4px hsl(var(--ring)) !important;`
       （原值記在此存查）。上面那段註解說「用的是 shadcn 官方 ring-2 + ring-offset-2 寫法」——
       ★那句在 v4 已經**不成立**：官方 button.tsx 現在是
         `focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50`，
         沒有 ring-offset，環是 3px 的 50% 淡環，另外把 border 換成 ring 色。
       原註解全文保留不刪，只在這裡標注它已過期。 */
    border-color: hsl(var(--ring)) !important;
    box-shadow: 0 0 0 3px hsl(var(--ring) / .5) !important;
    outline: none !important;
}

/* --- S-31. `--muted` 面上的 `--muted-foreground` 只有 4.40:1（AA 差 0.10）------------
   ★這是 token 配對的極限，不是某一頁的問題：
     --muted            = 240 4.8% 95.9%  #F4F4F5（zinc-100）
     --muted-foreground = 240 3.8% 46.1%  #71717A（zinc-500）
     實測 4.40:1 —— 在白底上是 4.83:1（過），一放到 muted 面上就掉到 4.40（不過）。
   ★不動 --muted-foreground 這顆 token：它是 shadcn upstream 的 zinc 原值，
     且與 2026-fatboy 共用同一份主題；改它＝單方面 fork，並且會連帶把
     「muted 前景放在 card / background 上」那一大票本來就有 4.83～5.6 的地方一起壓深，
     整站的次要文字都會變重（hierarchy 塌掉）。
   → 改成配對式修法：多一顆「只在 muted 面上用」的前景 token，
     其餘場合的 --muted-foreground 一個字都不動。

   ★哪些組合真的 < 4.5 是「量出來的」，不是憑感覺列：
     先用 CSS 解析找出檔內所有「同一條規則同時設 background:--muted 與
     color:--muted-foreground」共 16 條，再把每一條注入真實頁面讀 computed 算對比
     （等 600ms 讓 .form-control 的 .15s transition 跑完才讀，立即讀會拿到中間值）。
     亮色實測 13 條 = 4.40 全部不過；深色模式同組合是 5.81:1（--muted #27272A ×
     --muted-foreground #A1A1AA）本來就過，所以深色維持原值不動。
     另外 3 條刻意不改：
       .btn:disabled / .btn.disabled / fieldset:disabled .btn（亮 2.60、深 3.62）
       .swal-modal:has(.pos-warning-cb:not(:checked)) .swal-button--confirm（同上，未勾選＝停用態）
       → WCAG 2.1 §1.4.3 對「停用中的使用者介面元件」明確豁免，
         而且把停用鈕加深＝看起來像可按，是反效果。
     .badge.bg-light 也不在清單內：L701 雖然給它 --muted-foreground，
     但 L1738 後面又把它改成 --secondary-foreground，實測 18.10:1（已經過），
     一起改反而會從 18.10 掉到 4.94，是回歸。

   ★取值 240 3.8% 43%（#696972）：色相/彩度與 --muted-foreground 完全一致，
     只把亮度 46.1% → 43%，在 --muted 上量到 4.94:1（餘裕 0.44，不是壓線的 4.59）。
     為什麼不用整階的 zinc-600（33.9%）：那是 6.94:1，次要文字會重到跟本文搶，
     階層感就沒了 —— 這裡要的是「剛好過 AA」而不是「越深越好」。 */
:root                     { --muted-foreground-on-muted: 240 3.8% 43%; }
html.dark                 { --muted-foreground-on-muted: 240 5% 64.9%; }   /* = 深色的 --muted-foreground，實測 5.81:1 已過，不動 */
/* ★[data-bs-theme="light"]（AI 工具 hero 的「永遠亮色的島」，見 §補位 6）必須另外寫死：
   自訂屬性是在宣告的那個元素上就把 var() 代換掉並往下繼承的，
   html.dark 上算好的深色值會直接流進島內，壓在島內的亮色 --muted 上會慘不忍睹。 */
[data-bs-theme="light"]   { --muted-foreground-on-muted: 240 3.8% 43%; }

/* 自己就是 muted 面（背景與文字同一條規則給的） */
.list-thumb-placeholder,
.input-group-text,
.input-group-addon,
.datepicker .input-daterange .input-group-addon,
.fb-img-fallback {
    color: hsl(var(--muted-foreground-on-muted));
}
/* ★.table-light 的表頭要單獨拉高特異度，理由是量出來的：
   一般表頭（.app-content .table > thead > tr > th）的底是 `background: transparent !important`，
   透出來的是 --card 白 → 4.83:1 **本來就過**，不需要也不該動（動了整站表頭會無謂變深）。
   只有掛了 .table-light 的 thead 例外：Bootstrap 的 .table-light 是把 --bs-table-bg 設在
   thead/tr 上（th 自己仍透明），於是同一顆 th 的字色不變、底色卻從 #FFFFFF 變成 #F4F4F5
   → 4.83 掉到 4.40。實測 5 處 `<thead class="table-light">`（Order/ReadyToShip/index、
   PackagingMachine/Order/_shipment_group_modals、_shipment_group_script）。
   ★這條必須帶 !important：真正在管表頭字色的是 L1655
     `.table th,.table-panel th,.table-panel thead th{color:hsl(var(--muted-foreground))!important}`，
     !important 贏過一般宣告與特異度無關 —— 第一版只拉高特異度沒帶 !important，
     實測 after 仍是 4.40，就是被這條吃掉的。
   ★同時保留 .app-content 前綴（讓它只作用在真實 UI 層），
     class 掛在 <thead> 上不是 <tr> 上，`.table-light > th` 選不到，三種掛法都收。 */
.app-content .table > thead.table-light > tr > th,
.app-content .table > thead > tr.table-light > th,
.app-content .table > thead > tr > th.table-light,
.table-light,
.table-light > th,
.table-light > td {
    color: hsl(var(--muted-foreground-on-muted)) !important;
}
/* 原規則帶 !important，覆寫也必須帶（同特異度 + 排在後面才會贏） */
.badge.bg-white,
.badge.bg-grey, .badge.bg-gray, .badge.bg-silver,
.stat-pill.status-off, .status-off,
.stat-pill.status-none, .status-none {
    color: hsl(var(--muted-foreground-on-muted)) !important;
}
/* 背景由容器（.bg-light → --muted）提供、文字由 utility 提供的組合。
   實測案例：seller/tenantSetting 的兩塊 .bg-light 說明區、ai-tool/suite 的書籤提示。
   只收 .text-muted / .text-secondary 兩個 utility —— .bg-light 內用 --foreground 的
   一般文字本來就有 17:1，不該被拖深。 */
.bg-light .text-muted,
.bg-light .text-secondary {
    color: hsl(var(--muted-foreground-on-muted)) !important;
}

/* ★★2026-08-06 §S-57 起：下面這一節的**片規則**在有路由的兩支頁面上已經沒有呼叫端。
     pos/machine.blade.php 與 pos/package.blade.php 的 markup 改成了全站 SSOT
     `<div class="fb-tabs fb-tabs--touch"><a class="fb-tab">`，所以
     `… .nav.nav-tabs`／`… .nav-item .nav-link`（本體／:hover／.active）四條
     在那兩支頁面 querySelectorAll 命中數 = 0（實測數過）。
     ★但**不刪**，理由有兩個：
       ⑴ machine_backup／darka／checkout／_package 四支孤兒 view 的 markup 沒有動，
          它們仍然吃這一節 —— 這不是純死碼，是「只剩孤兒在用」。
       ⑵ 本節開頭那條 `.pos .pos-sidebar .pos-sidebar-nav { padding; background }`
          咬的是外層包裝 div，markup 沒動它 ⇒ **它仍然生效，不是死碼**。
     ★§POS-2 區段裡那些「★已被 §S-32 覆蓋（實測生效值：…）」的註解也一律不改：
       它們陳述的是「同一條 selector 在串接下誰贏」，這件事沒有變。
     完整清單與取捨見檔尾 §S-57 ①。 */
/* --- S-32. POS 側欄面板頁籤 → shadcn 分段控制器（Segmented control）--------------
   對象：.pos .pos-sidebar .pos-sidebar-nav .nav.nav-tabs
   涵蓋 pos/machine・machine_backup・darka・checkout・package・_package 六支 blade
   （六支的 markup 完全相同，所以只動主題、六支 blade 一個字都不用改；
     其中 machine_backup / darka / checkout / _package 目前是沒有路由的孤兒 view，
     不刪，改主題也順帶把它們一起帶到）。

   為什麼是分段控制器而不是底線頁籤：這兩顆是「同一個面板的兩種內容」的切換器
   （今天紀錄 / 查詢包裹），不是頁面層級導覽 —— 正是 shadcn TabsList 的用途。
   而現況是**兩種語彙疊在一起**的 Color Admin 殘留：未選中填滿 --muted、
   選中填 --card 再加一條 inset 底線，等於同時用了「填色分段」和「底線頁籤」。

   ★觸控階不縮（POS 是現場功能需求，不是美觀選擇）：
     修前實測 1440px 兩顆各 45px 高（min-height:44px 撐出來的）。
     shadcn TabsList 的原生尺寸是軌道 h-9(36px)、item h-7(28px) —— 直接照抄會把
     觸控目標從 45px 砍到 28px，掉到 Apple HIG 44pt / WCAG 2.5.8 的 24px 門檻邊緣。
     所以這裡只換語彙不換尺寸：item 維持 min-height:44px、padding 12px 15px，
     軌道 4px 內距是**加**在外面（軌道 45 → 52px），觸控目標只會變大不會變小。
   ★pos-menu 那一組 .nav.nav-tabs（pos/_header.blade.php，揀包出貨/自動包裝機/後台管理）
     刻意不改：它每一顆都是帶 href 的**換頁連結**，做成分段控制器會誤導成「原地切換」；
     而且它本來就已經是 44px 圓角膠囊（radius 10px），已符合 shadcn sidebar-menu 語彙。 */
.pos .pos-sidebar .pos-sidebar-nav {
    padding: 8px;                                    /* 軌道與側欄邊緣的呼吸 */
    background: hsl(var(--card));
}
.pos .pos-sidebar .pos-sidebar-nav .nav.nav-tabs {
    padding: 4px;                                    /* = shadcn TabsList 的 p-1 */
    gap: 4px;
    background: hsl(var(--muted));
    border-bottom: 0;                                /* 分段控制器沒有底線，改用軌道 */
    border-radius: var(--radius);
}
.pos .pos-sidebar .pos-sidebar-nav .nav.nav-tabs .nav-item .nav-link {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 44px;                                /* ★不縮 */
    padding: 12px 15px;
    background: transparent;
    color: hsl(var(--muted-foreground-on-muted));    /* 未選中壓在 --muted 軌道上，走 S-31 的配對前景 */
    border: none;
    border-radius: calc(var(--radius) - 4px);        /* 內縮一個軌道內距，同心圓角 */
    box-shadow: none;
    transition: background-color .15s ease, color .15s ease;
}
.pos .pos-sidebar .pos-sidebar-nav .nav.nav-tabs .nav-item .nav-link:hover {
    background: hsl(var(--background) / .6);
    color: hsl(var(--foreground));
}
.pos .pos-sidebar .pos-sidebar-nav .nav.nav-tabs .nav-item .nav-link.active {
    background: hsl(var(--background));
    color: hsl(var(--foreground));
    font-weight: 600;
    box-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);        /* shadcn shadow-sm；取代原本的 inset 底線 */
}

/* --- S-33. 側欄租戶名長名截斷要出「…」而不是切一半 -------------------------------
   ★這條在 2026-08-03 之前不會被注意到：那一行本來寫死 Config('app.name')，
     全站永遠是同一個短字串；改成讀租戶自填的 title 之後，長名才變成常態。
   病徵（實測截圖比對）：.tenant-name 現況是 display:flex（和 .tenant-meta 共用同一條
   規則，.tenant-meta 有三個子元素所以需要 flex），而 flex 容器裡的純文字會被包成
   **匿名 flex item**，text-overflow:ellipsis 是給 block container 用的 → 對匿名 item
   無效，於是只剩 overflow:hidden 的硬裁：長名會從字的正中間被切斷（實測「宅」被切一半）。
   ★不動原規則（.tenant-meta 仍需要 flex），只把 .tenant-name 這一個改回 block。
   量測（1440px 桌機 / 375px 手機、亮暗四組）：
     可用寬 113px（桌機）/ 181px（手機）；灌入 24 字長名 scrollWidth 368px、
     溢出 255/187px 全部被容器吃掉，#sidebar 與 document 的 scrollWidth 差皆為 0
     → 不外溢、不撐破側欄；改 block 後同樣位置改為顯示「…」。
   line-height:16px 已由上面的規則給，格線仍是 grid-template-rows:16px 16px 的兩行，
   拿掉 flex 的垂直置中不影響對齊（單行文字的 line-box 就是 16px）。 */
#sidebar .menu-profile-info .tenant-name { display: block; }

/* --- S-34. 側欄第二行「代號 + ⓘ + 續約訂閱」擠不下（2026-08-03 業主指示要顯示 chip）------
   量到的事實：`.tenant-meta` 可用寬 113px，內容需要 代號49 + ⓘ12 + chip57 + gap2×2 = 122px → 溢出 9px。
   三個子元素原本全是 `flex: 0 0 auto`（誰都不收縮）→ 溢出全落在排最後的 chip，被 overflow:hidden
   切掉半個「閱」字（業主兩次截圖回報；2026-fatboy 同一個病）。
   ★ 這不是「差幾 px」：6 碼缺 4px、7 碼缺 9px、10 碼缺 30px —— 只調死值等於只對得起當下這一組資料。
   所以做兩層：
     (1) 省出空間：chip 左右內距 8→5px、ⓘ 與 chip 的 gap 2→1px。省 8px，7 碼代號剛好完整顯示。
     (2) 最後防線：代號可收縮（`flex:1 1 auto; min-width:0`）+ ellipsis。超長代號時**寧可截代號也不切 chip**——
         代號點 ⓘ 還看得到全碼（popover 內文帶完整代號），chip 被切一半則是純粹的壞掉。
   ★ 不放大 chip 高度：前輪實測 18px 仍會被上層 `overflow:hidden` 裁 1px，16px 是這個容器的上限。 */
/* ★★ 2026-08-03 更正：我先前判定「padding-right:18px 是 Color Admin 留下的廢空間」是**錯的**。
   本檔 L6557-6563 就寫著 `#sidebar .menu-profile-info > .menu-caret { position:absolute; right:0 }`
   —— caret 仍是絕對定位，那 18px 正是它唯一的讓位方式，不是廢空間。
   直接收到 4px 的實測後果（1440 桌機、chip 已在第二行）：
     caret 方框 x179–195、chip 右緣 x191 → 水平重疊 12px、垂直重疊 3px；
     elementFromPoint 打 caret 中心命中 .menu-caret（畫在 chip 上面）。
   ★正確做法（由 2026-fatboy 先查出並實作，此處照搬）：讓 caret **真正佔位**，
     就能把 padding 全部收回而不壓到任何東西 ——
     .menu-profile-info 本來就是 grid，補一個 10px 的第二欄給 caret、
     caret 改 position:static 跨兩列置中即可，markup 一行都不用動。
     欄寬取 10px 是因為 chevron（::before 6×6 旋轉 45°）實際只畫約 8.5px，16px 方框其餘都是空氣。
   ★transform 必須一起 reset：L6561 給了 translateY(-50%)，而 transform 對 static 元素照樣生效，
     只改 position 的話 chevron 會整個往上偏 3px。 */
#sidebar .menu-profile-info {
    padding-right: 0;
    grid-template-columns: minmax(0, 1fr) 10px;
    column-gap: 2px;
}
#sidebar .menu-profile-info > .tenant-name,
#sidebar .menu-profile-info > .tenant-meta { grid-column: 1; }
#sidebar .menu-profile-info > .menu-caret {
    position: static;
    transform: none;
    grid-column: 2;
    grid-row: 1 / span 2;
    align-self: center;
    justify-self: end;
    /* ★caret 元素本身固定 16px 寬（Color Admin 的方框），塞進 10px 欄會往左溢出 4px
       壓到 chip —— 實測 justify-self:end 之後仍重疊 4px。chevron 實際只畫 8.5px，
       所以把方框收成 10px：視覺不變、也不再侵佔第一欄。 */
    width: 10px;
    min-width: 10px;
}
/* ★已被 §S-36 覆蓋（實測生效值：gap=2px） */
#sidebar .menu-profile-info .tenant-meta { gap: 1px; }
#sidebar .menu-profile-info .tenant-meta > .btn.btn-yellow {
    /* ★已被 §S-39 覆蓋〈跨模型：§S-39 的 padding 簡寫帶 !important，吃掉本行的長寫〉（實測生效值：padding-left=8px） */
    padding-left: 5px !important;
    /* ★已被 §S-39 覆蓋〈跨模型：§S-39 的 padding 簡寫帶 !important，吃掉本行的長寫〉（實測生效值：padding-right=8px） */
    padding-right: 5px !important;
    flex: 0 0 auto;                 /* chip 永遠不被壓縮，寧可讓代號截斷 */
}
#sidebar .menu-profile-info .tenant-code {
    /* ★已被 §S-36 覆蓋（實測生效值：flex=0 1 auto） */
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* ★再補 4px：.menu-profile-link 的右內距 8→4（照 2026-fatboy §反移-1b 的同一步）。
   單靠 caret 佔位只把可用寬帶到 121px，而 7 碼代號需要 119.5px → 只剩 1.5px 餘裕，
   那正是 2026-07-29 踩過的坑：無頭瀏覽器量到「剛好放得下」，業主的 Chrome 因為字型度量
   不同仍顯示「C0…」。收右內距後是 125px，7 碼餘裕 5.5px。
   ★只動右邊——右邊本來就是 caret 那一側，視覺上感覺不出來；
     avatar 與文字之間的 gap（shadcn gap-2）與上下左內距一律不動。 */
#sidebar .menu-profile-link { padding-right: 4px; }

/* ═══════════════════════════════════════════════════════════════
 * §S-35 頁首版面三條規則（業主 2026-08-04 指令）＋ 上一頁／下一頁流程導覽
 *        （原本誤編成 §S-17，但 L7788 早就有一個 §S-17「側欄客戶代號對比」——
 *          本檔既有章節已編到 §S-34，這裡接續編號，避免兩節同號日後互相指錯）
 * -----------------------------------------------------------------------------
 * 業主指令（範例頁 /center/report）：
 *   ① 動作按鈕一律**置左**（原本是 ms-auto 靠右）
 *   ② 說明按鈕一律**置右**，而且要永遠在最右端、不被動作鈕擠位
 *   ③ 副標移到**標題右邊**（原本收在右端的「說明」彈窗裡）
 *
 * 全部只做在 components/page-header.blade.php ＋ 這一節，
 * 108 支呼叫端**一行都沒改** —— 這正是頁首元件化要兌現的價值。
 *
 * ★刻意與 2026-fatboy 分歧：肥肥現況仍是「動作鈕靠右」。這三條是業主對 seller
 *   的新指令，以業主為準；prop 介面兩邊仍逐字一致，日後同步只要改肥肥的元件。
 * -------------------------------------------------------------------------- */

/* ── ① 動作組置左 ──────────────────────────────────────────────
   元件把預設 slot 包成 .fb-ph-actions（不再帶 .ms-auto），
   所以它預設就是靠左流排。這裡寫死 margin-left:0 是防守：
   §頁首 那組 `.page-header > .btn:not(:first-child):first-of-type{margin-left:auto}`
   打的是**直接子代**的按鈕，動作鈕現在包了一層 div 所以穿不過去；
   但萬一日後有人把按鈕直接掛回 .page-header 底下，這條仍然把它拉回左邊。 */
/* ★已被 L9396 覆蓋（實測生效值：order=1） */
.page-header > .fb-ph-actions { order: 50; margin-left: 0 !important; }
/* 還沒元件化、自己手刻 .ms-auto 動作組的頁首（例：Seller/includes/_plan_tabs 的
   無標題分支）也一起靠左，全站同一種節奏。只打 .page-header 的直接子代，
   卡片／表格／modal 裡的 .ms-auto 完全不受影響。 */
.page-header > .ms-auto { margin-left: 0 !important; }

/* ── ② 說明鈕永遠在最右端 ────────────────────────────────────
   動作組交出 auto 邊界後，整列只剩說明鈕一個 auto —— 不會再發生 §S-15 那種
   「兩個 auto 平分剩餘空間、動作鈕浮到畫面正中央」。
   order:99 保證它排在動作組之後；margin-left:auto 把它推到最右。
   ★ !important 是要贏過 §S-15 的 `:has(> .ms-auto) … {margin-left:0}` ——
     那條原本是為了解雙 auto 而寫的，現在雙 auto 的成因已經拆掉，
     它反而會讓說明鈕跟在動作鈕屁股後面（不再靠右）。舊規則一條都沒刪。 */
.page-header > .fb-page-help,
.page-header > .fb-ph-inline > .fb-page-help,
.page-header:has(> .fb-ph-actions) > .fb-page-help,
.page-header:has(> .fb-ph-actions) > .fb-ph-inline > .fb-page-help {
    order: 99;
    margin-left: auto !important;
}

/* ── ③ 副標顯示在標題右邊 ────────────────────────────────────
   當年把副標藏進彈窗，是因為「26 字長句把整列撐開、把動作鈕擠掉」。
   這次改成正面解決那個失效模式：限寬 + 單行省略（完整原文留在 title 屬性，
   滑過看得到，一個字都沒少），而不是把文字藏起來。 */
.fb-ph-subtitle {
    font-size: .8125rem;
    font-weight: 400;
    line-height: 1.4;
    min-width: 0;
    max-width: 22rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    flex: 0 1 auto;
}
/* 搬進頂 bar 後空間更緊，再收一階；頂 bar 本來就有 .fb-topbar-meta 的
   ≤767px 隱藏規則接住窄螢幕。 */
.fb-topbar-page .fb-ph-subtitle { max-width: 16rem; }
@media (max-width: 1199.98px) {
    .fb-topbar-page .fb-ph-subtitle { max-width: 10rem; }
}

/* ── ④ 上一頁／下一頁流程導覽箭頭 ────────────────────────────
   shadcn ghost icon button：無邊框無底、hover 才出 --accent 淡底，
   尺寸對齊全站控制項階（28px，比 32px 的動作鈕小一階，因為它是導覽不是動作）。
   ★色用 --muted-foreground：它是次要導覽，不該比標題還搶眼；
     hover 才升到 --foreground。亮暗兩主題都靠變數，不寫死顏色。
   ★這兩顆現在只在「有真網址」時才輸出（元件層把 null 直接不渲染），
     所以不需要 disabled 態的樣式 —— 畫面上不會存在點了沒反應的箭頭。 */
.fb-ph-nav {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    flex: 0 0 auto;
    border-radius: 8px;
    color: hsl(var(--muted-foreground));
    text-decoration: none;
    transition: background-color .15s ease, color .15s ease;
}
.fb-ph-nav:hover,
.fb-ph-nav:focus-visible {
    background-color: hsl(var(--accent));
    color: hsl(var(--foreground));
    text-decoration: none;
}
.fb-ph-nav:focus-visible {
    /* §S-73：原 `outline: 2px solid hsl(var(--ring)); outline-offset: 2px;` → 官方兩層 */
    outline: none;
    outline-offset: 0;
    border-color: hsl(var(--ring));
    box-shadow: 0 0 0 3px hsl(var(--ring) / .5);
}
.fb-ph-nav svg,
.fb-ph-nav i[data-lucide] { width: 16px; height: 16px; }
/* 搬進頂 bar 後跟著標題的字級縮一階，維持同一列的視覺重量 */
.fb-topbar-page .fb-ph-nav { width: 24px; height: 24px; }
.fb-topbar-page .fb-ph-nav svg { width: 15px; height: 15px; }

/* --- S-35. 側欄第二行不准換行（2026-08-03 業主：「有夠醜」）------------------------
   病徵：手機抽屜與桌機側欄，「續約訂閱」chip 掉到第三行、還被置中，
   看起來像壞掉的排版（業主截圖）。
   真因（量出來的）：`.tenant-meta` 可用 123px、內容需要 123px —— **其實塞得下**，
   但它允許換行，flex 一旦 wrap 就把 chip 丟到下一行，
   而下一行只有它一個 item、又繼承了置中對齊 → 變成一顆孤零零的置中膠囊。
   ★所以這裡不是「空間不夠」而是「不該換行」：鎖 nowrap 就回到同一列。
   代號仍保留可收縮（上面章節已定義），超長代號時截代號、不切 chip、也不換行。 */
#sidebar .menu-profile-info .tenant-meta {
    flex-wrap: nowrap;
    align-items: center;
}

/* --- S-36. 側欄帳戶區照 shadcn NavUser 重新收斂（2026-08-03 業主：「有夠醜」）--------
   對照基準：賣賣 Work（mywork_os）的 shadcn 版面 —— 乾淨白卡、1px 細框、10px 圓角、
   次要資訊走 muted、狀態用小膠囊、hover 才給淡底，沒有粗外框也沒有大色塊。

   ① 第二行三個元素貼左成一組。原本寬度有餘時會把 chip 推到最右端（抽屜 191px 下
      「代號 …… ⓘ 續約訂閱」中間一大段空白），那不是留白是散開。
   ② 帳戶選單改成 shadcn DropdownMenu 語彙：--popover 底、1px --border、radius 10、
      item hover 用 --accent。原本是 Color Admin 的灰色圓角盒子（--muted 底、無框）。
   ③ profile 按鈕 hover 用 --sidebar-accent 淡底，不用外框（shadcn SidebarMenuButton）。 */
#sidebar .menu-profile-info .tenant-meta {
    justify-content: flex-start;
}
#sidebar .menu-profile-info .tenant-meta > * { margin-left: 0 !important; margin-right: 0 !important; }

/* ★★ 2026-08-04 更正：我先前把帳戶選單做成「浮起來的白卡」（--popover 底 + 1px 框 + 陰影），
   業主看了說「有點突兀」——**他是對的，我用錯語彙了**。
   白卡是 shadcn **DropdownMenu** 的樣子，那種面板該**浮在內容之上**；
   而這裡是側欄內的展開清單（`#appSidebarProfileMenu` 是 collapse，不是 popover），
   對應的是 shadcn 的 **SidebarMenuSub**：沒有卡片、沒有邊框、沒有陰影，
   直接坐在側欄表面上，靠縮排與 hover accent 表達層級。
   一張有陰影的卡片嵌在扁平的側欄裡，視覺上會像「貼上去的東西」，這就是突兀感的來源。
   ★L6778 那條原本的 SidebarGroup 淡底色塊語彙其實更接近正解，本節回到那個方向，
     只把內距與 item 圓角對齊 shadcn 的量尺。 */
/* ★★ 2026-08-04 定案：照 shadcn 官方 `SidebarMenuSub` 的做法 ——
   **一條左側細線 + 縮排**，不是底色、也不是卡片。
   shadcn 原始樣式：`mx-3.5 border-l px-2.5`，item 是 SidebarMenuSubButton
   （h-7、text-sm、rounded-md、hover 才給 sidebar-accent）。
   ★為什麼不給底色：底色是 Bootstrap／Color Admin 的分組思路；
     shadcn 在側欄裡用「縮排 + 一條 border-l」表達從屬關係，平面保持乾淨。
   ★為什麼前一版的白卡突兀：白卡＋陰影是 **DropdownMenu** 的語彙，
     那種面板該**絕對定位浮在內容之上**；嵌在側欄流裡卻有陰影，
     看起來就是「貼上去的東西」（業主原話「有點突兀」）。
     若日後要改成真的浮動選單（shadcn NavUser 的原始形態），
     那時才該把卡片樣式加回來，並讓它 position:absolute 蓋住內容。 */
#sidebar #appSidebarProfileMenu {
    background: transparent;
    border: 0;
    border-left: 1px solid hsl(var(--sidebar-border));
    box-shadow: none;
    border-radius: 0;
    padding: 2px 0 2px 10px;
    margin: 2px 8px 6px 22px;   /* 左側縮排＝shadcn 的 mx-3.5 量級 */
}
#sidebar #appSidebarProfileMenu .menu-item > .menu-link {
    border-radius: calc(var(--radius) - 3px);
    padding: 7px 10px;
    gap: 8px;
}
#sidebar #appSidebarProfileMenu .menu-item > .menu-link:hover {
    background: hsl(var(--accent));
    color: hsl(var(--accent-foreground));
}
/* ★上面的 justify-content 沒有效果，因為真正把 ⓘ 與 chip 推到最右的是
   `.tenant-code { flex: 1 1 auto }` —— 它會**長大**填滿剩餘空間。
   改成 `0 1 auto`：可收縮（超長代號仍截斷）但不長大，三者才會貼左成一組。 */
#sidebar .menu-profile-info .tenant-code { flex: 0 1 auto; }
/* ★gap 取 2px、chip 內距取 4px —— 這是「呼吸空間」與「代號不截斷」的平衡點：
   桌機側欄可用 123px，內容 代號49 + ⓘ12 + chip57 + gap2×2 = 122px，剛好放得下且不擠。
   gap 放到 4px 會變 128px → 代號被截（實測）；壓回 1px 則 ⓘ 貼著代號很醜（業主已反映）。
   手機抽屜 191px 空間充裕，同一組值看起來更鬆，不需要另開斷點。 */
#sidebar .menu-profile-info .tenant-meta { gap: 2px; }
#sidebar .menu-profile-info .tenant-meta > .btn.btn-yellow {
    /* ★已被 §S-39 覆蓋〈跨模型：§S-39 的 padding 簡寫帶 !important，吃掉本行的長寫〉（實測生效值：padding-left=8px） */
    padding-left: 4px !important;
    /* ★已被 §S-39 覆蓋〈跨模型：§S-39 的 padding 簡寫帶 !important，吃掉本行的長寫〉（實測生效值：padding-right=8px） */
    padding-right: 4px !important;
}

/* =============================================================================
 * §S-37  POS 左側導覽 rail → 對齊賣賣 Work(OS) 的 shadcn 語彙
 * -----------------------------------------------------------------------------
 * 業主 2026-08-04：「你這邊沒有完整套用 shadcn 的設計啊 也是有黑有白可選」，
 * 並指定「參考 OS 那邊的 shadcn 設計」。基準＝/Users/monet/OS/mywork_os
 * （src/components/pos/PosMenu.vue），不是本專案的規格書。
 *
 * OS PosMenu 的 rail item 逐字是：
 *   基底   rounded-lg transition-colors
 *   佈局   flex flex-col items-center justify-center gap-0.5 py-2   ← 圖示在上、文字在下
 *   選中   bg-brand text-brand-foreground                          ← 黃底黑字
 *   未選中 text-muted-foreground hover:bg-accent hover:text-foreground
 *   標籤   text-[10px] font-semibold leading-none
 * 本檔的 --primary(#FFD200) 就是 OS 的 --brand，值一模一樣，直接對應。
 *
 * 修前現況（§POS-2 4626-4656）與 OS 的三個落差：
 *   1. 圖示與文字「並排」，OS 是圖示在上文字在下；
 *   2. 未選中是 bg:--card 的實心方塊（看起來像三顆按鈕），OS 是透明＋muted 文字；
 *   3. 選中是 --dark 近黑，OS 是 --primary 黃底黑字。
 * ★第 3 點與本檔「黃鈕只留給新增/儲存」的慣例不衝突：那條約束的是**動作按鈕**，
 *   這裡是**導覽選中態**（OS 的動作按鈕同樣是黑底白字 bg-foreground text-background，
 *   兩邊一致）。業主也明確描述了「選中項是黃底黑字圓角方塊」。
 *
 * ★觸控目標只增不減（POS 是現場功能需求，見 §POS 檔頭 4455）：
 *   修前實測 44px（4633 的 min-height 撐出來的）→ 改直排後明確給 56px。
 * ★字級維持 12px 不跟 OS 的 10px：OS 是滑鼠桌機版，POS 是現場觸控機，
 *   §POS 檔頭已定「字不縮小」。直排後左右反而比並排更寬鬆，12px 不會撐爆 160px rail。
 * ========================================================================== */
/* ★rail 底色**維持 --muted**，不跟 OS 的 bg-card：
   blade 上的 .bg-default 是 `background-color: ... !important`（4505），改不動；
   而且 4504 已載明 rail 要有底色才不會「與內容區糊在一起」，那是刻意的。
   代價是未選中文字壓在 --muted 上 —— 用本檔 S-31 既有的配對前景 token 解，
   與 §S-32 側欄分段控制器（8408）同一套做法，不另外發明。
   實測：--muted-foreground(46.1%) 壓 #F4F4F5 只有 4.40:1（未達 AA），
        --muted-foreground-on-muted(43%) 才過。 */
.pos .pos-menu .nav.nav-tabs .nav-item .nav-link {
    flex-direction: column;
    gap: 4px;
    min-height: 56px;                 /* ★由 44px 放大，不縮 */
    padding: .5rem .375rem;
    background: transparent;          /* OS：未選中不填色 */
    color: hsl(var(--muted-foreground-on-muted));
    border-color: transparent;
    line-height: 1.2;
}
/* 圖示在上：blade 上的 me-1 / ms-n2 是為「並排」而加的，直排要歸零。
   不動 blade（那三行 <i> 同時被 6 支頁面共用），在主題層抵銷即可。 */
.pos .pos-menu .nav.nav-tabs .nav-item .nav-link > i,
.pos .pos-menu .nav.nav-tabs .nav-item .nav-link > svg {
    margin: 0 !important;
    width: 20px;
    height: 20px;
    flex: 0 0 auto;
}
.pos .pos-menu .nav.nav-tabs .nav-item .nav-link:hover,
.pos .pos-menu .nav.nav-tabs .nav-item .nav-link:focus {
    background: hsl(var(--accent));
    color: hsl(var(--accent-foreground));
    border-color: transparent;
}
/* 選中＝OS 的 bg-brand text-brand-foreground（本檔同值 token 是 --primary） */
.pos .pos-menu .nav.nav-tabs .nav-item .nav-link.active,
.pos .pos-menu .nav.nav-tabs .nav-item .nav-link.active:hover,
.pos .pos-menu .nav.nav-tabs .nav-item .nav-link.active:focus {
    background: hsl(var(--primary));
    color: hsl(var(--primary-foreground));
    border-color: hsl(var(--primary));
}

/* 手機（≤991.98px）：rail 變成頂部橫向捲動 bar。
   ★這裡**刻意保留並排**（不套直排）：手機是 375px 寬、812px 高的直立畫面，
     直排會把頂 bar 從 44px 撐到 56px，等於每頁少 12px 的作業區；
     而橫向 bar 本來就一列一顆、不存在「rail 太窄」的問題。
     只把配色帶到 OS 語彙，觸控目標維持 44px 不變。 */
@media (max-width: 991.98px) {
    .pos .pos-menu .nav.nav-tabs .nav-item .nav-link {
        flex-direction: row;
        gap: 0;
        min-height: 44px;             /* ★與修前一致，不縮 */
        /* ★已被 §S-37POS 覆蓋〈跨模型：本行在 @media ≤991.98px 之內，桌機根本不套用〉（實測生效值：padding=8px 6px） */
        padding: .46875rem .9375rem;
    }
    .pos .pos-menu .nav.nav-tabs .nav-item .nav-link > i,
    .pos .pos-menu .nav.nav-tabs .nav-item .nav-link > svg {
        margin: 0 .375rem 0 0 !important;
    }
}

/* =============================================================================
 * §S-38  POS 亮/暗切換鈕（rail 底部）
 * -----------------------------------------------------------------------------
 * 業主 2026-08-04：「也是有黑有白可選」。修前實測 POS 全部 10 支檔案
 * data-theme-toggle ＝ 0 處，是全站唯一沒有主題入口的區塊。
 *
 * 為什麼放 rail：layouts/pos.blade.php 的外殼是 app-without-header +
 * app-without-sidebar（沒有頂 bar、沒有全站側欄），rail 是 POS 唯一每頁常駐的介面。
 * 賣賣 Work(OS) 把切換鈕放在 AppHeader 右側工具區（w-9 h-9 rounded-lg
 * text-foreground hover:bg-accent），POS 沒有那條 bar，就放到 rail 的等價工具位
 * ——OS 的 PosMenu 也正是把收合鈕放在 rail 底部（w-full h-9 rounded-lg
 * text-muted-foreground hover:bg-accent hover:text-foreground）。
 *
 * ★零 JS：行為由 includes/_shell.blade.php 第 10 段的 [data-theme-toggle] 統一處理，
 *   防閃爍還原由 includes/_link.blade.php 處理，記憶 key 是 localStorage['seller-theme']。
 * ★觸控目標 44px（與 rail item 同階，不低於 §POS 檔頭的 40px 下限）。
 * ========================================================================== */
.pos .pos-menu .pos-menu-footer {
    padding: .328125rem .9375rem .75rem;
    margin-top: auto;                 /* 桌機 rail 是 column flex，推到最底 */
}
.pos .pos-menu .pos-theme-toggle {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 4px;
    width: 100%;
    min-height: 44px;                 /* ★現場觸控目標 */
    padding: .5rem .375rem;
    font-size: .75rem;                /* 與 rail item 同字級 */
    font-weight: 500;
    line-height: 1.2;
    background: transparent;
    color: hsl(var(--muted-foreground-on-muted));   /* 同 rail item，壓在 --muted 上要用配對前景 */
    border: 1px solid transparent;
    border-radius: var(--radius);
    cursor: pointer;
    transition: background-color .15s linear, color .15s linear;
}
.pos .pos-menu .pos-theme-toggle:hover,
.pos .pos-menu .pos-theme-toggle:focus-visible {
    background: hsl(var(--accent));
    color: hsl(var(--accent-foreground));
}
.pos .pos-menu .pos-theme-toggle > i,
.pos .pos-menu .pos-theme-toggle > svg {
    width: 20px;
    height: 20px;
    flex: 0 0 auto;
}
/* .d-block-dark 的全站規則是 display:inline-block，在直排 flex 裡會讓
   圖示與文字的對齊基準跑掉；rail 這顆統一成 block。 */
html.dark .pos .pos-menu .pos-theme-toggle > .d-block-dark { display: block !important; }

/* 手機：頂 bar 是「logo 一列 + 頁籤一列」的兩列結構，footer 若當第三列會再吃掉高度。
   改成絕對定位貼在 logo 那一列的右端（logo 是置中的，右側本來就是空白）。 */
@media (max-width: 991.98px) {
    .pos .pos-menu { position: fixed; }
    .pos .pos-menu .pos-menu-footer {
        position: absolute;
        top: 0;
        right: .46875rem;
        height: 44px;                 /* 對齊 logo 列高度 */
        display: flex;
        align-items: center;
        padding: 0;
        margin-top: 0;
    }
    .pos .pos-menu .pos-theme-toggle {
        width: 44px;                  /* ★方形 44x44 觸控目標 */
        min-height: 44px;
        padding: 0;
        gap: 0;
    }
    .pos .pos-menu .pos-theme-toggle .pos-theme-toggle-text {
        display: none;                /* 手機只留圖示，文字靠 aria-label / title */
    }
}

/* =============================================================================
 * §S-39  全站按鈕稽核收尾：主題層 8 項（2026-08-04）
 * -----------------------------------------------------------------------------
 * 背景：全站逐顆按鈕稽核（23,124 筆量測 = 116 頁 x 亮/暗 x 1440/375）修後仍有
 *       1,032 顆不合規，其中 1,020 顆（98.8%）的成因在本檔（主題層），blade 端
 *       已經做到頭。本節把主題層那批收掉。
 *
 * 結果（同一支稽核腳本、同一份判準，四組合全站重量 23,106 筆）：
 *       我方分區 1,032 → 20 顆不合規（-98.1%）；POS 分區 0 → 0。
 *       剩下 20 顆全部不是主題層：
 *         8 顆 `.btn-yellow` 登入/確認（品牌黃語意，blade 決定，本輪未授權改）
 *         8 顆 `.rounded-pill`（blade 自己寫死藥丸）
 *         4 顆 AI「清空」——**實測證明是假違規**，見 (6) 的說明。
 *       逐顆欄位比對：非預期變動 0 筆；對比從「過」掉到「不過」0 筆、改善 16 筆。
 *
 * ★ 本節一律「檔尾追加」，上面與 2026-fatboy 共用的章節一個字都沒動。
 *   需要蓋掉上游宣告的地方，靠的是「同特異度、後者勝」，不是改上游。
 * ★ 每一項都有修前/修後實測數字，寫在各小節的註解裡。
 * ★ POS 觸控階（40/44/45/56/62px）是功能需求。本節沒有任何一條選擇器寫 .pos，
 *   但 (4) 的 `.modal …` 會落到 POS 的彈窗上（POS 外殼 pos.blade.php L29 也有 .app-content）
 *   —— 實測那些彈窗在修前就已經被同內容的 `.app-content …` 規則收過了，屬於同值覆蓋。
 *   修後全站重量結果：POS 按鈕高度變動 0 筆、變矮 0 筆，
 *   分佈維持 {32:40, 36:48, 40:20, 44:16, 45:8, 56.4:16, 62:12}。
 * ========================================================================== */

/* --- S-39 (1) 側欄收合鈕：radius 9999px 藥丸 → token ------------------------
   實測修前：.app-sidebar-minify-btn = 28x28、border-radius 9999px，
             全站 460 顆 / 116 頁（亮暗 x 1440/375 四組合都一樣）。
   9999px 在本專案的判準裡只留給 chip / badge；這顆是「觸發器按鈕」
   （shadcn 的 SidebarTrigger），官方就是 rounded-md 的方角小鈕，不是藥丸。
   ★只改圓角，不動 §S-29 補的 28x28 觸控區、也不動 §S-14 收合態的置中與箭頭翻面。
   修後：radius 8px（--radius-md，與 Button 全系列同一階）。 */
.app-sidebar-minify-btn {
    border-radius: var(--radius-md);
}

/* --- S-39 (2) 手機漢堡鈕：34px → 32px ---------------------------------------
   實測修前：#header .navbar-mobile-toggler 在 375 寬 = 32(w) x 34(h)，
             232 顆 / 116 頁（375 專屬，桌機 display:none 不受影響）。
   34 不在 32/36/40 任一階。高度是 §RWD 那段的 padding 10px 上下 + 內容 14px
   （三條 icon-bar 各 2px + 兩個 4px gap）湊出來的，不是刻意的觸控尺寸。
   ★改 padding 不改 icon-bar：三條槓的粗細與間距一個像素都不動，只收上下留白。
   ★收完是 32x32 正方形，剛好等於 shadcn SidebarTrigger 的 size-8，
     也遠高於 WCAG 2.5.8 的 24x24 最小目標，觸控不會變難按。 */
@media (max-width: 991.98px) {
    #header .navbar-mobile-toggler {
        padding-top: 9px;
        padding-bottom: 9px;
    }
}

/* --- S-39 (3) 賣賣小幫手側拉板收合鈕：30px → 32px ---------------------------
   實測修前：.theme-panel .theme-collapse-btn = 30x30，228 顆 / 114 頁
             （桌機亮暗兩態；575px 以下這顆本來就藏起來）。
   30 不在 32/36/40 任一階。這顆是 Color Admin 原尺寸照抄留下來的。
   ★ left / margin-top 必須跟著改：原值 -30px / -15px 是「等於自身尺寸」與
     「自身高度的一半」，只改 width/height 會讓它偏移半個像素並且蓋住面板邊緣。
   ★ line-height 一起收，圖示才會在新的 32px 盒子裡置中（這顆沒有 flex 置中）。 */
.theme-panel .theme-collapse-btn {
    left: -32px;
    width: 32px;
    height: 32px;
    line-height: 32px;
    margin-top: -16px;
}

/* --- S-39 (4) modal 逃出高度階梯 --------------------------------------------
   實測修前：/packaging-machine-order-management/status/{new-order,ready-to-ship,
             shipped} 與 /employee/user/salaryEdit 四頁的彈窗裡，
             .btn = 38px（Bootstrap 原生）、.btn-sm = 31px、.form-control-sm 與
             .form-select-sm 也是 31px。共 76 顆按鈕 / 4 頁（19 顆 x 四組合）。
   真因（量出來的，不是推論）：全站高度階梯的兩條規則都掛在 .app-content 底下
     L5128 `.app-content .btn-sm{height:32px}`
     L7082 `.app-content …> .btn{min-height:36px}`（另加 .theme-panel 特例）
   而同一頁 48 個 modal 裡有 2 個是寫在 .app-content 之外的（實測
   modalsInsideApp=46 / modalsOutsideApp=2）→ 那 2 個整個逃出階梯。
   ★ 治法與上游對 .theme-panel 的做法一模一樣：把 .modal 也列為「階梯適用範圍」，
     不是去改那兩條共用規則（它們對 .app-content 內的 46 個 modal 是對的）。
   ★ 表單控制項一起收：同一個彈窗裡 38px 的鈕配 31px 的下拉，本來就是同一個
     破口的兩半，只修按鈕會留下高低不齊。
   ★ textarea 一樣要排除在固定高度之外（理由見 L5145 那段的 rows 失效教訓）。
   修後：.btn 38→36、.btn-sm 31→32、.form-control-sm / .form-select-sm 31→32。 */
.modal :not(.input-group-sm):not(.input-group-lg) > .btn:not(.btn-sm):not(.btn-xs):not(.btn-lg):not(.btn-icon):not(.btn-close) {
    min-height: 36px;
    font-size: 14px;
    line-height: 22px;
    padding-top: 6px;
    padding-bottom: 6px;
}
.modal .form-control-sm:not(textarea),
.modal .form-select-sm,
.modal .btn-sm,
.modal .input-group-sm > .form-control:not(textarea),
.modal .input-group-sm > .input-group-text,
.modal .select2-container--bootstrap-5 .select2-selection--single,
.modal .form-control-color {
    height: 32px;
    min-height: 32px;
    font-size: 13px;
}
.modal textarea.form-control-sm,
.modal .input-group-sm > textarea.form-control {
    font-size: 13px;
    min-height: 0;
    height: auto;
}
.modal .btn-sm {
    padding-top: 0;
    padding-bottom: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}
.modal .btn-sm.btn-icon,
.modal .btn-xs.btn-icon { padding: 0; }
.modal .btn.btn-xs:not(.btn-icon) {
    min-height: 24px;
    line-height: 18px;
    padding-top: 2px;
    padding-bottom: 2px;
}

/* --- S-39 (5) 實心紅底白字只有 3.76:1 --------------------------------------
   實測修前：.badge.bg-danger 與 .btn-danger 的底都是 --destructive #EF4444，
             配 --destructive-foreground #FFFFFF ＝ 3.76:1（WCAG AA 要 4.5），
             亮暗兩模式相同（--destructive 沒有暗色版），16 顆。
   ★ 這顆 token 的職責在 §1 的註解裡已經寫清楚了：「--destructive 是底色 token，
     紅『字』走 --destructive-fg」。既然它只當底色，那就該用「白字踩得住」的紅。
     當年沒調的理由是「直接調暗會連紅底按鈕的配色一起變」—— 但那正是要一起變的，
     漏掉的是「白字對比」這條驗收；現在補上。
   ★ 值＝Tailwind red-600（shadcn 現行 default theme 的 destructive 也是這一階），
     不是自己配一個紅。白字對比 4.83:1（算式與稽核腳本同一套 WCAG 相對亮度）。
   ★ 連帶影響全部量過：
       .alert-danger 底 = --destructive/.1 → 淡紅由 #FDECEC 變 #FBE9E9（肉眼等值），
         字是 --destructive-fg 不受影響；
       .border-danger / 表單驗證框 = --destructive/.5 → 略深，對比只增不減；
       .progress-bar.bg-danger / .bg-danger 非 badge/btn → 白字同樣由 3.76 升到 4.83；
       .swal2-icon.swal2-error 的紅在白底 → 3.76 升到 4.83。
   ★ 這是與 2026-fatboy 共用的 token 值上的第一個刻意分岔：seller 這邊先過 AA，
     肥肥要跟就整段複製本小節（不要去改上面的 §1，那是共用基線）。 */
:root {
    /* ★★§S-72 更新：本小節上面整段理由一字未刪，但值換成官方的那顆了。
       §S-39 的驗收條件是「實心紅底配白字要過 AA 4.5」——
       官方值 #E7000B 白字實測 **4.77:1**，依然過 AA，所以這條條件沒被破壞（原 #DC2626 是 4.83）。 */
    --destructive: 357.1 100% 45.3%;       /* #E7000B ← 官方 A oklch(0.577 0.245 27.325)（溢色域已裁切），白字 4.77:1（原 0 72.2% 50.6% #DC2626 白字 4.83:1） */
}
/* .btn-outline-danger 是唯一把 --destructive 當「文字色」用的地方（L396），
   底色 token 調暗之後它在暗色模式會掉到 4.01:1。依本檔既有慣例改吃 --destructive-fg
   （亮色 #BA1B1B、暗色 #F87171，兩邊都已經是為「紅字」調過的值）。
   hover 態填實心紅配白字，不受影響。 */
/* ★2026-08-04 稽核：blade 0 命中 */
.btn-outline-danger {
    --bs-btn-color: hsl(var(--destructive-fg));
}

/* --- S-39 (6) data-bs-theme 作用域把 Bootstrap 寫死的調色盤帶回來 -----------
   稽核代理的原始判定是「缺一個 [data-bs-theme='dark'] token 區塊」。
   ★ 實測後改判：不能加那種區塊，真正的破口也不在那裡。理由與證據：
     (a) 危險：#sidebar 每一頁都掛 data-bs-theme="dark"（_nav.blade.php L2）。
         現場注入實驗（亮色模式 /main）：加上 blanket 區塊後，側欄作用域內
           --foreground  240 10% 3.9%（近黑） → 0 0% 98%（近白）
           --card        0 0% 100%（白）      → 240 5.9% 10%（近黑）
         而側欄底色仍是 --sidebar-bg #FAFAFA（淺）→ 任何吃 --foreground / --card
         的東西都會變成「白字壓在淺底」。爆炸半徑＝全站 116 頁的側欄。
     (b) 沒有效益：唯一真正的 dark island 是 AI 工具的四張工具卡。同一個實驗裡
         卡上的 .text-dark-gray 由 #71717A 變 #A1A1AA —— 那張卡的底是一張
         淺色照片，等於對比更差。而那四張卡上一顆按鈕都沒有，
         整個 /ai-tool/alpha 的三顆不合規沒有一顆在 dark island 裡。
   ★ 真正量到的破口是另一件事：Bootstrap 5.3 的 [data-bs-theme] 區塊會把
     整組 --bs-* 調色盤用寫死的 hex 重新宣告一次，蓋掉本檔 §1 在 :root 做的橋接。
     實測（亮色模式）：
       :root / .app-content   --bs-body-color hsl(240 10% 3.9%)  --bs-link-color hsl(240 10% 3.9%)
                              --bs-border-color hsl(240 5.9% 90%) --bs-tertiary-bg hsl(240 4.8% 95.9%)
       [data-bs-theme=light]  --bs-body-color #212529  --bs-link-color #0d6efd（Bootstrap 藍）
                              --bs-border-color #dee2e6  --bs-tertiary-bg #f8f9fa
       #sidebar[=dark]        --bs-border-color #495057  --bs-link-color #6ea8fe（Bootstrap 藍）
                              --bs-secondary-color rgba(222,226,230,.75)  --bs-tertiary-bg #2b3035
     L990 那段當年只補了 --bs-body-color / --bs-body-bg / --bs-emphasis-color 三個，
     其餘五個到今天還是 Bootstrap 原生值，而且每一頁的側欄都吃得到。
   ★ 治法＝把 §1 的橋接補完整，兩個作用域都補。這裡「不動任何 shadcn token」，
     只把 --bs-* 指回本作用域已經決定好的 token → 亮島仍是亮島、暗島仍是暗島，
     不會發生 (a) 的翻轉。L990 的 #sidebar 專屬覆蓋特異度 (0,2,0) 更高，仍然贏。
   ★ 稽核代理列的第 7 項（裸 .btn 沒有 token 前景，會吃 --bs-body-color #212529）
     就是本破口的一個表現：修好橋接，裸 .btn 自然吃到 --foreground。
     實測全站目前每一顆裸 .btn 都另外帶了顏色 class（.navbar-link / .text-muted /
     .text-dark），所以這一項消除的不合規數是 0 —— 它補的是「下一顆裸 .btn」的地雷。
   ★ 附帶澄清一個**假違規**：稽核把 /ai-tool/alpha 的「清空」判成對比 3.67（<4.5），
     並預期本項修好就會消失。實測後判定那是量測法的假象，不是缺陷：
       稽核腳本的 backdrop() 只沿「祖先鏈」合成 background-COLOR。這顆鈕的底其實是
       一張**兄弟層絕對定位的 <img>**（bgcover.jpg）整片蓋在卡片上 ——
       不在祖先鏈上、也不是 background-image，所以合成不到，只量到被完全遮住的
       卡片底色 hsl(var(--dark-panel)) #18181B，才算出 3.67。
       改用截圖讀真實像素：底色 #FFFFFF、字身核心像素 #71717A（就是 --muted-foreground），
       實測 4.83:1 —— 本來就過 AA。亮暗兩模式數值相同（那張圖不隨主題變）。
     所以這 4 顆（1 顆 x 四組合）修後仍會被腳本記成不合規，但它們是量測法的極限，
     不是要修的東西。真要讓數字歸零得改 blade（把那張卡的 .bg-component 深色底
     換成與 data-bs-theme="light" 一致的淺底），本輪不動 blade，記錄在案。 */
[data-bs-theme="light"],
[data-bs-theme="dark"] {
    --bs-body-bg: hsl(var(--background));
    --bs-body-color: hsl(var(--foreground));
    --bs-emphasis-color: hsl(var(--foreground));
    --bs-secondary-color: hsl(var(--muted-foreground));
    --bs-tertiary-bg: hsl(var(--muted));
    --bs-border-color: hsl(var(--border));
    --bs-border-radius: var(--radius);
    --bs-border-radius-sm: var(--radius-sm);
    --bs-border-radius-lg: var(--radius-xl);
    --bs-component-bg: hsl(var(--card));
    --bs-component-border-color: hsl(var(--border));
}
/* 連結色只補亮島，暗島刻意不補。
   --bs-link-color-rgb 沒有對應的 token（token 是 hsl 三元組，Bootstrap 的 a 吃的是
   rgba(var(--bs-link-color-rgb), …) 逗號格式），所以只能寫死一組值。
   亮島的答案是唯一的（近黑 9,9,11，與 §1 的 :root 同值）；
   暗島唯一的使用者是 #sidebar，而側欄在「亮色模式」底色是淺的 #FAFAFA ——
   在那裡寫死近白連結色會變成看不見的字。側欄自己的 a 規則（L976-987、L990）
   特異度更高、也已經把每一種連結都指定過，所以這裡留白才是安全的預設。 */
[data-bs-theme="light"] {
    --bs-link-color: hsl(var(--link));
    --bs-link-hover-color: hsl(var(--link-hover));
    --bs-link-color-rgb: 9, 9, 11;
    --bs-link-hover-color-rgb: 9, 9, 11;
}
/* ★ 這裡刻意「沒有」補一條 `.btn { --bs-btn-color: … }`。
   Bootstrap 5.3 的 .btn 本體就寫著 `--bs-btn-color: var(--bs-body-color)`，
   所以上面把 --bs-body-color 接回 token 之後，裸 .btn 自動就吃到 --foreground，
   不需要第二條規則。而且那條規則寫下去只有兩種下場：
     用 .btn（0,1,0）→ 它在檔尾，會蓋掉上面所有 .btn-dark / .btn-danger 變體的字色
       （同特異度後者勝）＝全站按鈕字色崩掉；
     用 :where(.btn)（0,0,0）→ 永遠輸給 Bootstrap 自己那條 (0,1,0)，等於沒寫。
   兩條路都不對，正解就是修橋接本身。 */

/* --- S-39 (7) 四態缺口（hover / focus-visible / active / disabled）----------
   實測修前（CSSOM 逐條規則比對 + 真實 hover 交叉驗證，12 頁 340 顆可點按鈕）：
     .theme-collapse-btn                12 顆 缺 focus-visible / active / disabled
     .app-sidebar-minify-btn            12 顆 缺 focus-visible / active / disabled
     .btn-close                         49 顆 缺 active
   這三顆都不是 .btn，所以 §S-30 的 `.btn:focus-visible` 焦點環規則不涵蓋它們
   （這正是 §S-30 註解裡提過的「只量 .btn 與輸入框會有抽樣偏差」的另一面）。
   ★ 焦點環用與 §S-30 完全相同的 shadcn ring-2 + ring-offset-2 雙層寫法，
     不另外發明第二套視覺語言。
   ★ 側欄那顆的 offset 環要用 --sidebar-bg（它坐在側欄底上，不是頁底），
     用 --background 會在亮色側欄上畫出一圈看得見的白邊。 */
.theme-panel .theme-collapse-btn:focus-visible,
.app-sidebar-minify-btn:focus-visible {
    outline: none;
    /* §S-73：原 `0 0 0 2px hsl(var(--background)), 0 0 0 4px hsl(var(--ring))` → 官方兩層 */
    border-color: hsl(var(--ring));
    box-shadow: 0 0 0 3px hsl(var(--ring) / .5);
}
.app-sidebar-minify-btn:focus-visible {
    /* §S-73：原 `0 0 0 2px hsl(var(--sidebar-bg)), 0 0 0 4px hsl(var(--ring))`。
       上面「側欄那顆的 offset 環要用 --sidebar-bg」的理由是**間隙那一圈**會露白邊；
       官方寫法沒有間隙，所以這條的差異已消失。規則保留，值與上面同一套。 */
    border-color: hsl(var(--ring));
    box-shadow: 0 0 0 3px hsl(var(--ring) / .5);
}
.theme-panel .theme-collapse-btn:focus:not(:focus-visible),
.app-sidebar-minify-btn:focus:not(:focus-visible) {
    outline: none;
}
/* ★ hover：CSSOM 規則比對說這顆「有 hover」，實測（CDP forcePseudoState + 等 700ms
   讀 computed）卻是**完全沒有變化** —— 命中的是 Bootstrap 的 `a:hover{color:…}`(0,1,1)，
   而 L3175 那條 `.theme-panel .theme-collapse-btn{color:…}` 是 (0,2,0)，永遠贏它，
   所以那條 hover 規則寫了等於沒寫。與 §S-30 焦點環被 !important 蓋掉是同一種病：
   「規則存在」不等於「規則生效」，只讀 CSSOM 會漏掉這一類。這裡補一條真的會贏的。 */
.theme-panel .theme-collapse-btn:hover {
    background: hsl(var(--accent));
    color: hsl(var(--accent-foreground));
}
/* active：按下去的回饋要比 hover **再重一階**，否則兩態長得一樣，
   使用者分不出「滑過」與「按住」。這裡用半透明的前景色疊一層 ——
   同一條規則在亮色是「更深」、在暗色是「更亮」，兩個模式都比 hover 明顯。
   ★ 不用 box-shadow 做這件事：focus-visible 的環也是 box-shadow，
     按住的同時通常也帶著焦點，後面的規則會把環蓋掉。 */
.theme-panel .theme-collapse-btn:active {
    background: hsl(var(--accent));
    color: hsl(var(--accent-foreground));
    background-image: linear-gradient(hsl(var(--foreground) / .10), hsl(var(--foreground) / .10));
}
.app-sidebar-minify-btn:active {
    background: hsl(var(--sidebar-active-bg));
    color: hsl(var(--sidebar-fg-strong)) !important;
    background-image: linear-gradient(hsl(var(--sidebar-fg-strong) / .10), hsl(var(--sidebar-fg-strong) / .10));
}
.btn-close:active {
    background-color: hsl(var(--foreground) / .12);
    border-radius: var(--radius-sm);
}
/* disabled：這兩顆目前沒有停用情境，但四態要成套（未來 JS 一掛 .disabled
   就必須看得出不可點），用與 §S-21 送出中同一套「降對比 + not-allowed」。 */
.theme-panel .theme-collapse-btn.disabled,
.theme-panel .theme-collapse-btn[aria-disabled="true"],
.app-sidebar-minify-btn.disabled,
.app-sidebar-minify-btn[aria-disabled="true"] {
    opacity: .5;
    cursor: not-allowed;
    pointer-events: none;
}

/* --- §S-40. 側欄帳戶區：拿掉頭像 + 續約訂閱改 shadcn Badge（2026-08-04 業主指示）-------
   三條指令：①公司不需要放圖片 ②代號要完整顯示 ③續約訂閱用 shadcn 重新設計。
   ①與②是同一件事：頭像方塊佔 32px + 8px gap，拿掉就多 40px，代號自然放得下
   （先前 7 碼被截成「1234…」，是因為第二行要在 123px 內塞下 代號+ⓘ+chip）。
   ③原本是 `.btn.btn-yellow.btn-sm.btn-rounded.rounded-pill` —— 那是 Color Admin 的
   「小型按鈕」語彙（它其實是 <small> 沒有 href，本來就不是按鈕）。
   改成 shadcn Badge 的規格：`rounded-md`（不是藥丸）、`px-2.5 py-0.5`、`text-xs font-semibold`、
   實心品牌底 + 恆黑前景（--primary / --primary-foreground，亮暗一致）。 */
#sidebar .menu-profile-link { gap: 0; }
#sidebar .menu-profile-info .tenant-meta > .btn.btn-yellow {
    /* shadcn Badge：方角圓化而非藥丸、字重 600、內距 10/2 */
    border-radius: var(--radius-sm, 6px) !important;
    padding: 2px 8px !important;
    font-size: 12px;
    font-weight: 600;
    line-height: 16px;
    height: auto;
    min-height: 0;
    background: hsl(var(--primary)) !important;
    color: hsl(var(--primary-foreground)) !important;
    border: 0 !important;
}

/* --- §S-41. 「更新：MM-DD HH:mm」全站置右（2026-08-04 業主指令）--------------------
   業主：「更新：08-04 13:34 全站 SSOT 往右邊置右放」。
   做在元件層（`.fb-ph-freshness` 由 x-page-header 產生），108 支呼叫端一行都不用改。
   ★關鍵：右側群組只能有**一個** auto 邊界。
     更新時間拿 margin-left:auto 當右群組的起點，說明鈕改成 margin-left:0、
     靠既有的 order:99 排在它後面。
     若兩者都保留 auto，flex 會把剩餘空間**平均分**給兩個邊界 ——
     那正是頁首以前「動作鈕浮在畫面正中央」的病根（主題 §S-15 記過）。
   ★手機 375：頁首本來就會 wrap，置右在窄螢幕下會讓它落到下一行的右端，
     這是可接受的（與動作鈕同一行時空間本來就不夠），不另開斷點。 */
/* ★用 order 排序，不能只靠 margin-left:auto —— DOM 順序是
     標題 → 副標 → 更新時間 → 動作組，只給更新時間 auto 會把**它後面的動作組一起推到右邊**
     （實測：更新時間右 1364、動作組左 1372，動作鈕反而比更新時間更靠右）。
     正解是排序：動作組留在左（order 1）、更新時間與說明鈕排到最後（98/99），
     auto 邊界只給更新時間一個。 */
.page-header > .fb-ph-actions { order: 1; }
.page-header > .fb-ph-freshness { order: 98; margin-left: auto; }
.page-header > .fb-page-help,
.page-header > .fb-ph-inline > .fb-page-help,
.page-header:has(> .fb-ph-actions) > .fb-page-help,
.page-header:has(> .fb-ph-actions) > .fb-ph-inline > .fb-page-help {
    margin-left: 0;
}

/* ═══════════════════════════════════════════════════════════════
   §S-42 流程步驟條 <x-flow-stepper>（2026-08-04）
   業主：「你要學習 /packaging-machine-order-management/status/new-order
           這邊的箭頭選單的設計方式」

   ── 這一段在收什麼 ──
   同一個 chevron 設計在本專案已經有三份實作：
     ① PackagingMachine/Order/_stepper.blade.php        主流程／取消旁支
     ② PackagingMachine/Order/_returning_chevron.blade.php  退貨分叉
     ③ 上面 §步驟箭頭 的 .scr-stepper                    篩單流程
   ①② 的**幾何**（尺寸、clip-path、咬合）寫在各自 blade 的 @once <style> 裡，
   **狀態配色**卻寫在主題 §步驟箭頭（5877 行起，選擇器同時列了 .pm-stepper--chevron
   與 .scr-stepper）—— 同一個設計拆在兩個地方，改一次要開兩個檔還會互相蓋。
   本節把幾何也收進主題，元件 <x-flow-stepper> 只負責產生 markup。

   ★狀態階層沿用 §步驟箭頭 已定案的那一套（業主 2026-07-27 拍板），不重新設計：
       已完成 is-past    實心 muted 底 + 一般前景字（走過了，仍要讀得清楚）
       當前   is-current 實心 --dark 底 + 反白字 + 陰影（整條唯一的視覺焦點）
       未到   is-future  極淡底 + muted 字（明顯退後）
     所以下面只寫幾何，配色交給 §步驟箭頭 —— 那些規則的選擇器在本節末尾補上
     .flow-step 這一組（只新增，不改動既有兩組）。
   ═══════════════════════════════════════════════════════════════ */
.flow-stepper {
    /* 箭頭切口與步驟間縫隙：與 §步驟箭頭 的 --pm-arrow/--pm-gap 同值（11/2），
       業主 2026-07-27 就是把 6px 加大到 11px 才讓箭頭形狀真的讀得出來。 */
    --flow-arrow: 11px;
    --flow-gap: 2px;
    --flow-h: 32px;              /* shadcn 控制項尺度 sm＝32px，與頁面其他控制項同階 */
    display: flex;
    align-items: stretch;
}
.flow-stepper-track {
    display: flex;
    flex-wrap: nowrap;
    align-items: center;
    /* 11 站在 1440 桌機放得下（實測每站約 80px）；窄螢幕橫向捲動，
       ★不隱藏任何一站 —— 步驟條的價值就是「一眼看完整條流程」，藏掉就沒意義了。 */
    overflow-x: auto;
    scrollbar-width: thin;
}
.flow-step {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 64px;
    min-height: var(--flow-h);
    flex-shrink: 0;
    /* 負 margin 讓後一塊的凹槽咬住前一塊的尖角，中間留 --flow-gap 的縫 */
    margin-left: calc((var(--flow-arrow) - var(--flow-gap)) * -1);
    padding: 0 12px 0 calc(4px + var(--flow-arrow));
    font-size: 13px;
    line-height: 1.2;
    white-space: nowrap;
    /* ★!important：主題的 `#content a:not(.btn):not(...)`（特異度 1,5,1）會把步驟塊
       當成一般內容連結上色＋加底線，不加就吃不到步驟條自己的配色。 */
    text-decoration: none !important;
    /* 左凹槽 + 右尖角 */
    clip-path: polygon(
        0 0,
        calc(100% - var(--flow-arrow)) 0,
        100% 50%,
        calc(100% - var(--flow-arrow)) 100%,
        0 100%,
        var(--flow-arrow) 50%
    );
    transition: background-color .15s ease, color .15s ease, filter .15s ease;
}
/* 第一塊：左邊是流程起點，不該有凹槽（會看起來像被前一站咬掉一角） */
.flow-stepper-track > .flow-step:first-child {
    margin-left: 0;
    padding-left: 10px;
    /* ★已被 §S-52 覆蓋（實測生效值：6px＝--radius-sm；業主「白色倒圓角太圓」）*/
    border-top-left-radius: var(--radius);
    /* ★已被 §S-52 覆蓋（實測生效值：6px＝--radius-sm）*/
    border-bottom-left-radius: var(--radius);
    clip-path: polygon(
        0 0,
        calc(100% - var(--flow-arrow)) 0,
        100% 50%,
        calc(100% - var(--flow-arrow)) 100%,
        0 100%
    );
}
/* 線性流程的最後一塊保留尖角（它指向「流程結束」，不是指向下一站）——
   這是刻意的：把它收成方角會和一般 badge 難以區分。 */

/* ── 分叉（branch）：主幹跑完分成互斥的兩個結果 ──
   語意：已退貨 / 退貨取消 是二選一，畫成直線會被讀成「退完貨還要再走一步」。 */
.flow-stepper--branch .flow-stepper-track > .flow-step:last-child {
    /* 主幹最後一塊要露出尖角讓分叉欄咬住 */
    padding-right: 12px;
}
.flow-branch {
    display: inline-flex;
    flex-direction: column;
    gap: var(--flow-gap);
    /* 左移咬住主幹尖角；水平縫隙比垂直縫大一點，形狀才讀得出來 */
    margin-left: calc((var(--flow-arrow) - 4px) * -1);
}
.flow-branch-half {
    min-height: calc((var(--flow-h) - var(--flow-gap)) / 2);
    height: calc((var(--flow-h) - var(--flow-gap)) / 2);
    margin-left: 0;
    padding: 0 calc(10px + var(--flow-arrow));
    font-size: 12px;
    line-height: 1.05;
}
/* 上半：左凹槽承接主幹尖角、右收上半尖角；與下半合成同一支「兩頭箭頭」 */
.flow-branch-top {
    clip-path: polygon(0 0, calc(100% - var(--flow-arrow)) 0, 100% 100%, var(--flow-arrow) 100%);
}
.flow-branch-bottom {
    clip-path: polygon(var(--flow-arrow) 0, 100% 0, calc(100% - var(--flow-arrow)) 100%, 0 100%);
}

/* ── 狀態配色：沿用 §步驟箭頭（5877 行起）已定案的階層，這裡只是把 .flow-step
      掛進同一組規則。★刻意重寫一份而不是去改上面那組的選擇器：那組同時服務
      .pm-stepper--chevron 與 .scr-stepper 兩個既有實作，動它等於同時動三個地方。 */
/* ★★同日修正：未到站原本是 --muted / **.55 半透明** → 頁面底色透上來，
     整條軌道不是實心的，chevron 的咬合縫看不出來、當前站也失去可對比的底。
     改成實心 --muted —— 這也是打包機原版 _stepper 的做法
     （原註解：「is-past 與 is-future 同為基礎淺灰（沿用 .pm-step 預設 --muted）」）。
     ★已完成／未到的區分改由**文字**承擔（已完成 foreground/.6 較深、未到 muted 前景較淡），
       與 .fb-tab 只有「選中／未選中」兩態的做法一致。 */
.flow-stepper .flow-step.is-future {
    background-color: hsl(var(--muted)) !important;
    /* ★已被 §S-46 覆蓋（實測生效值：color=rgb(96, 96, 103)） */
    color: hsl(var(--muted-foreground)) !important;
    font-weight: 400 !important;
}
/* ★已完成：底色維持實心 --muted（＝頁籤軌道色），字改成 .fb-tab 未選中的
     hsl(var(--foreground) / .6)。原本是實色 --foreground —— 那會讓「已完成」和
     白底的「當前」一樣濃，白色就凸不出來（實測 Tabs 配色第一版看不出當前站，
     就是這條造成的，不是白底本身不行）。 */
.flow-stepper .flow-step.is-past {
    background-color: hsl(var(--muted)) !important;
    color: hsl(var(--foreground) / .6) !important;
    font-weight: 500 !important;
}
/* ★★2026-08-04 業主指令（第三次修正，逐條記錄免得又繞回去）：
     ① 「黑色感覺過於強烈」→ 曾改成品牌黃
     ② 「黃色太突顯」「我希望是淺灰跟白色的搭配，如同頁籤按鈕的配色」→ 現在這一版

   ── 為什麼這次直接抄 .fb-tab（§13 shadcn Tabs），不自己調色 ──
   業主說的「頁籤按鈕」就是本檔 §13 的 .fb-tabs/.fb-tab（`全部` 那一排），
   它是 shadcn Tabs 的逐字移植：
       軌道 .fb-tabs  background: hsl(var(--muted))
       未選 .fb-tab   color: hsl(var(--foreground) / .6)
       選中 .fb-tab.active  background: hsl(var(--background)) + shadow-sm
   同一頁上兩個「橫向多選一」的控制項，配色本來就該是同一套 —— 抄過來才是 SSOT，
   自己另調一組「也是灰白」的值只會又長出第三種灰。

   ── 兩處刻意的調整（形狀造成的，不是換配色）──
   ① 陰影用 filter: drop-shadow 不能用 box-shadow：**clip-path 會把 box-shadow 一起裁掉**
      （步驟塊是 chevron 形狀），這是整節都得用 drop-shadow 的原因。
   ② 強度從 shadow-sm 的 rgb(0 0 0/.05) 調到 /.16，並補一層 0 模糊的 border 色描邊：
      .fb-tab 是矩形、靠 3px 軌道內距就分得出來；chevron 彼此咬合沒有內距，
      .05 在白底上實測看不出抬起。★這是補償形狀差異，配色本身一個值都沒改。

   ★上一版（品牌黃）的值保留備查：background hsl(var(--primary))、
     color hsl(var(--primary-foreground))、filter drop-shadow(0 1px 2px hsl(var(--foreground)/.18))。
   ★再上一版（近黑）：background hsl(var(--dark))、color hsl(var(--dark-foreground))、
     filter drop-shadow(0 1px 3px hsl(var(--foreground)/.28))。 */
/* ★★修正（同日，業主：「你自己開來看」）：當前站原本用 --background，
     **而 --background 就是頁面 body 的底色（兩者都是 #FAFAFA）** →
     整塊當前站與背景同色 = 形狀完全消失，只剩一行粗體字浮著，chevron 也看不到。
     .fb-tab.active 用 --background 沒事，是因為它**坐在 .fb-tabs 那條實心 --muted 軌道裡**
     還有 3px 內距把它框住；步驟條直接坐在頁面上，沒有那條軌道，同一個值就穿幫。
     ★正解＝用 --card（#FFFFFF 純白，本主題所有「浮起來的面」——卡片／彈出層／下拉——都吃它）。
       配色語彙不變（仍是「淺灰軌道＋白色當前」），只是換成在這個情境下真的會白的那顆 token。 */
.flow-stepper .flow-step.is-current {
    /* ★已被 §S-51 覆蓋（實測生效值：亮 background-color=rgb(228,228,231)、暗 rgb(39,39,42)
         ＝--border）。本體從「白色的面」改成「那圈邊」，白色的面移到 §S-51 的 ::before。 */
    background-color: hsl(var(--card)) !important;
    color: hsl(var(--foreground)) !important;
    font-weight: 600 !important;
    /* 下面這兩層 drop-shadow 是**死碼**：Chrome 的套用順序是 filter → clip-path，
       畫在形狀外面的陰影會被下一步整片裁掉。誘餌實測（換 3px 純紅重跑六頁）：
       不是 :first-child 的四頁 0 顆紅像素、是 :first-child 的兩頁 104–135 顆
       且全在左側兩顆圓角。詳見 §S-51 第二段。
       ★已被 §S-51 覆蓋（實測生效值：filter=none） */
    filter: drop-shadow(0 1px 2px hsl(var(--foreground) / .16))
            drop-shadow(0 0 .5px hsl(var(--border))) !important;
}
.flow-stepper a.flow-step:hover:not(.is-current) {
    background-color: hsl(var(--accent)) !important;
    color: hsl(var(--accent-foreground)) !important;
    filter: none !important;
}
.flow-stepper a.flow-step:focus-visible {
    /* §S-73：原 `outline: 2px solid hsl(var(--ring)); outline-offset: 2px;` → 官方兩層。
       ★這裡必須把 box-shadow 明寫出來：本規則特異度（0,3,1）高於全域 `body :focus-visible`，
         若只留 outline 而不宣告 box-shadow，全域那條的環會**同時**畫上去變成雙環。
       ★clip-path 會裁掉 box-shadow，跟裁掉 outline 是同一個問題 —— 下面既有的
         `clip-path: none` 已經處理掉了，所以改用 box-shadow 是安全的。 */
    outline: none;
    outline-offset: 0;
    border-color: hsl(var(--ring));
    box-shadow: 0 0 0 3px hsl(var(--ring) / .5);
    /* clip-path 會把 outline 一起裁掉 → 焦點框看不見。改用 z-index 抬高並取消裁切，
       只在鍵盤聚焦時發生，不影響一般狀態的形狀。 */
    clip-path: none;
    z-index: 1;
}

/* ── 分叉選項的底色：用實心 --muted，不用 is-future 的 .55 淡底 ──
   is-future 的語意是「流程還沒走到這一站」，所以刻意畫得很淡、明顯退後。
   但分叉的兩塊（已退貨 ╱ 退貨取消）不是「後面的步驟」，是**二選一的結果**，
   彼此沒有先後 —— 用 .55 會讓它們淡到像被停用，實測在白底上幾乎讀不出來。
   ★這也是回到原 _returning_chevron 的值：它非選中態本來就是 hsl(var(--muted)) 實心。
   （選中態仍由上面的 .is-current 接手，不受這條影響。） */
.flow-stepper .flow-branch-half.is-future {
    background-color: hsl(var(--muted)) !important;
    /* ★已被 §S-46 覆蓋（實測生效值：color=rgb(96, 96, 103)） */
    color: hsl(var(--muted-foreground)) !important;
}
.flow-stepper a.flow-branch-half:hover:not(.is-current) {
    background-color: hsl(var(--accent)) !important;
    color: hsl(var(--accent-foreground)) !important;
}

/* ── 步驟條的 muted 字：改吃 §S-31 的 --muted-foreground-on-muted ──
   實測 is-future 的字（--muted-foreground 疊在 --muted 底上）亮色只有 **4.40:1**，
   差 AA 的 4.5 一點點。這不是本節寫壞的，是 shadcn upstream zinc 的原值特性
   （官方 Tabs 就是 bg-muted + text-muted-foreground 這個組合），
   §S-31 早就為了同一個問題備好了「只在 muted 底上使用」的專用前景 token（4.94:1），
   當時只套到分頁籤，沒套到步驟條 —— 這裡補上，不動共用的 --muted-foreground。
   ★暗色的 --muted-foreground-on-muted 就等於 --muted-foreground（實測 5.81:1 本來就過），
     所以這條在暗色下等於沒改，不會把暗色調壞。 */
.flow-stepper .flow-step.is-future,
.flow-stepper .flow-branch-half.is-future {
    color: hsl(var(--muted-foreground-on-muted)) !important;
}

/* ═══════════════════════════════════════════════════════════════
   §S-43 稽核補位：blade 有用但主題零規則的元件（2026-08-04）
   業主：「你用 SHADCN 的設計去決定重構設計」

   稽核方法：把 shadcn 官方元件清單逐一比對「blade 的 class 屬性實際用到」× 「主題有無規則」。
   命中 5 個「有用但零樣式」，逐一判定後**只有 3 個是真缺口**：
     collapse (6 檔)  → Bootstrap Collapse 本身沒有視覺（只切 display/height），零規則正確，不補
     ratio    (1 檔)  → 純 aspect-ratio 結構工具類，同上，不補
     btn-group(9 檔)  → Bootstrap 自己的規則已經把內側圓角歸零、外側保留，
                        行為與 shadcn 的分段語彙一致；shadcn 沒有 ButtonGroup 元件，
                        硬造一個反而是發明規格。**不補**，但把圓角接上 token（見下）
     spinner-border(9 檔) → ★真缺口，見下
     avatar   (1 檔)  → ★真缺口，見下
   ═══════════════════════════════════════════════════════════════ */

/* ── ① .spinner-border：Bootstrap 原生是 2rem 直徑、0.25em（實測 4px）粗邊的環，
      在 shadcn 的介面裡明顯是另一個系統的零件（shadcn 用 Lucide Loader2 細線 + animate-spin）。
      不改 markup（9 支 blade 在用、還有 JS 動態插入），只把視覺對齊：
      直徑收到 16px、邊寬 2px、顏色吃 --muted-foreground（載入指示不該搶焦點）。
      ★保留 Bootstrap 的 `spinner-border` 動畫名不動，免得動畫失效。
      ★`.spinner-border-sm` 是呼叫端會用的既有修飾類，等比縮到 12px。 */
.spinner-border {
    width: 16px;
    height: 16px;
    border-width: 2px;
    color: hsl(var(--muted-foreground));
    vertical-align: -2px;
}
.spinner-border-sm { width: 12px; height: 12px; border-width: 2px; }
/* 放在按鈕裡時跟著按鈕的前景色走（例：深色送出鈕上的白色 loader） */
.btn .spinner-border { color: inherit; }

/* ── ② .avatar（auth/sso-account-prompt 的字母頭像）：
      shadcn Avatar ＝ 正圓 + bg-muted + 置中的 fallback 文字。 */
.avatar {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    border-radius: 9999px;
    background: hsl(var(--muted));
    color: hsl(var(--muted-foreground));
    font-weight: 500;
    font-size: .875rem;
    flex-shrink: 0;
    overflow: hidden;
}

/* ── ③ .btn-group 的圓角接 token（不改 Bootstrap 的分組行為，只讓外側圓角
      與全站 --radius 家族一致，否則會露出 Bootstrap 預設的 .375rem）。 */
.btn-group > .btn:first-child:not(:last-child) {
    border-top-right-radius: 0; border-bottom-right-radius: 0;
}
.btn-group > .btn:last-child:not(:first-child) {
    border-top-left-radius: 0; border-bottom-left-radius: 0;
}
.btn-group > .btn:not(:first-child):not(:last-child) { border-radius: 0; }

/* ── ④ .dropdown-item 的停用態：shadcn DropdownMenuItem 是
      `data-[disabled]:pointer-events-none data-[disabled]:opacity-50`。
      ★這個補得有意義（與 .flow-step/.fb-tab/.menu-link 的 disabled 不同）：
        Bootstrap 的下拉本來就會渲染 .disabled 項目，blade 也真的在用。 */
.dropdown-item.disabled,
.dropdown-item:disabled {
    pointer-events: none;
    opacity: .5;
    background: transparent;
}

/* ── ⑤ Breadcrumb 連結的 hover/focus：shadcn BreadcrumbLink 是
      `transition-colors hover:text-foreground`，原本只宣告了靜態 color。 */
.breadcrumb-item > a:hover,
.breadcrumb-item > a:focus-visible { color: hsl(var(--foreground)); }
.breadcrumb-item > a { transition: color .15s ease; }


/* =============================================================================
 * §附錄  2026-08-04 稽核：死碼標註清單（只標註、未刪除、未搬移）
 * -----------------------------------------------------------------------------
 * 判準：class 名在 resources/views / public/assets/js / app / resources/js 全專案
 *       grep 皆 0 命中，且已考慮動態組合（btn-{{ $x }} 等，來源 enum 只產
 *       success/danger/warning/info/primary/secondary/light/dark）。
 * ★上方每條被標註的規則都加了單行註解「★2026-08-04 稽核：blade 0 命中」，
 *   共 236 條規則。規則一律保留，未來若要復用只需移除註解。
 * ★只標註「整條規則的每個選擇器都含死 class」者；:not()/:is()/:where()/:has()
 *   內的 class 不列入判死（那是排除條件，死掉反而讓選擇器更容易命中）。
 *
 * 【確認死碼 class】共 187 個：
     app-top-menu app-with-end-sidebar bg-aqua bg-blue bg-blue-600 bg-blue-700 bg-cyan bg-gray
     bg-green bg-grey bg-indigo bg-lime bg-opacity-15 bg-orange bg-pink bg-red
     bg-silver bg-teal bg-yellow border-info brand-badge brand-sub brand-title btn-aqua
     btn-blue btn-copy btn-cta-ghost btn-cta-primary btn-cta-secondary btn-cyan btn-grey btn-indigo
     btn-lime btn-orange btn-outline-danger btn-outline-light btn-pink btn-purple btn-silver btn-teal
     ck-cmd ck-faq ck-step ck-step-desc ck-step-num ck-step-tag dash-thumb dropdown-footer
     error-code error-content error-desc error-message fb-caret fb-cell-avg fb-cell-neutral-soft fb-cell-neutral-strong
     fb-cell-sum fb-doc-img fb-filter-advanced fb-filter-card fb-filter-row fb-row-subtotal fb-row-total fb-thumb-circle
     fb-thumb-empty fb-thumb-img fb-thumb-link form-bordered form-horizontal fs-16 fs-17 fs-20
     fs-22 fs-24px fs-28 fs-48 h-45px image-icon info-list list-thumb
     list-thumb-more list-thumb-placeholder list-thumb-stack login-body login-v1 logo-img media-left media-object
     media-object-icon media-right menu-badge menu-header messenger messenger-chat messenger-chat-body messenger-chat-content
     messenger-chat-desc messenger-chat-header messenger-chat-item messenger-chat-link messenger-chat-list messenger-chat-media messenger-chat-time messenger-chat-title
     messenger-content messenger-menu messenger-menu-item messenger-menu-link nav-icon nav-tabs-v2 navbar-form note-content
     note-icon note-with-end-icon opacity-10 p-20px panel-heading panel-heading-btn password-field pos-remove-confirmation
     product-img px-35px queue-sub queue-sub-active scr-count single-price sort-acitve stat-pill
     status-absent status-early status-late status-leave status-none status-off status-pill status-working
     system-info-item table-field-sort table-sticky-head text-aqua text-bg-indigo text-bg-pink text-bg-secondary text-blue
     text-cyan text-indigo text-info text-lime text-opacity-10 text-opacity-20 text-orange text-pink
     text-purple text-red text-silver text-teal text-xs text-yellow toolbar-row userimage
     widget-chart widget-chat widget-chat-body widget-chat-header widget-chat-header-content widget-chat-header-desc widget-chat-header-icon widget-chat-header-title
     widget-chat-info widget-chat-info-container widget-chat-input widget-chat-item widget-chat-media widget-chat-message widget-chat-name widget-chat-time
     widget-chat-toolbar widget-chat-toolbar-link widget-list-action widget-list-desc widget-list-media widget-list-title widget-todolist-content widget-todolist-icon
     widget-todolist-input widget-todolist-item with-media
 *
 * 【具名豁免，不視為死碼】共 46 個 —— 這些由第三方 JS 在執行期
 *   產生（daterangepicker 日曆、Bootstrap btn-check、FontAwesome 圖示、
 *   lightbox lb-*、gritter-* 通知、list-group-item-* 變體、markdown-body），
 *   blade 搜不到是正常的。要從豁免清單移除必須先證明該套件沒在用：
     breadcrumb breadcrumb-item btn-check calendar-table calendar-time drop-up end-date fa-5x
     fa-rotate-180 fa-rotate-270 fa-rotate-90 fa-sm fa-xs fab form-control-color form-range
     form-select-lg gritter-bottom gritter-close gritter-item gritter-item-wrapper gritter-title gritter-top in-range
     input-daterange lb-caption lb-close lb-closeContainer lb-data lb-dataContainer lb-number lb-outerContainer
     list-group-item-danger list-group-item-info list-group-item-light list-group-item-primary list-group-item-secondary list-group-item-success list-group-item-warning lucide-chevron-down
     markdown-body nav-pills start-date table-striped text-body-secondary text-body-tertiary
 * ============================================================================= */

/* ═══════════════════════════════════════════════════════════════
   §S-44 稽核尾巴（2026-08-05）：手機 375 / 暗色全頁掃視後的兩項
   ═══════════════════════════════════════════════════════════════ */

/* ── ① bootstrap-datepicker 的週次欄字級 10px
      來源：`plugins/bootstrap-datepicker/.../bootstrap-datepicker3.css` 的 `.datepicker .cw{font-size:10px}`
      實測 /center/report 有 6 個 `td.cw` 落在 10px，是全站唯一低於 12px 的可見文字。
      ★這是第三方外掛的日曆週次欄（ISO 週數），不是我們的內文——但它確實會被使用者看到，
        而本專案的可讀性下限是 12px（主題 §字級 有記，L7134 把 .fs-7/9/10/11 全抬到 12px 用的是同一條理由）。
        這裡只抬字級，寬度交給外掛自己算（它是 width:12px + padding 的窄欄，字大一點會自動撐開）。 */
.datepicker .cw {
    font-size: 12px;
    width: auto;
}

/* ═══════════════════════════════════════════════════════════════
   §S-45 POS 掃描區 STEP 提示卡改用 shadcn 卡片（2026-08-06）
   業主：「/pos/machine 你這邊也沒有套用 shadcn 請你參考 OS 版本」
        「等於說也要完整套用 shadcn 設計 深淺」「shadcn 重新設計畫面與按鈕」
   ═══════════════════════════════════════════════════════════════

   ★★根因（實測，不是美感偏好）：
     `.product.not-available .not-available-text` 用的是
         background: hsl(var(--dark-panel) / .75)
     而 `--dark-panel: 240 5.9% 10%` 在 `:root` 與 `html.dark` **是同一個值**
     （見本檔 L67 / L215，那是刻意的——它服務的是「本來就該永遠深色」的面板）。
     ★2026-08-07 §S-74：這顆的值已改成 achromatic 的 `0 0% 9%`（#171717，原 #18181B 差 4/255），
       **兩處仍寫同一個值**，所以上面「:root 與 html.dark 是同一個值」這句話照舊成立，
       整段推論一字不用改；只有引用的那個數字換了。
     ⇒ 掃描提示卡借用它的結果：**亮色、暗色量到的顏色完全一樣，卡片恆黑**。
       實測亮色頁面底 rgb(250,250,250) 配一張全黑卡，暗色底 rgb(9,9,11) 也是同一張黑卡。

   ★另一個症狀：那條規則本來是給「商品售罄」用的**半透明遮罩**，底下要有商品照。
     掃描提示卡底下是預設頭像圖，.75 透明度讓人物照直接透出來，
     STEP 文字壓在照片上（實測截圖看得到人像輪廓）。

   ── 這一節怎麼做 ──
   範圍**只鎖 `.pos #pos-list`**（掃描區），不動 `#log-list` 的歷史紀錄卡，
   更不動其他頁面「商品售罄」的原用途——那裡的深色遮罩是對的。
   配色全部改吃會翻轉的 token（--card / --border / --foreground / --muted-foreground），
   亮暗自動跟著走，不再有寫死的深色。 */

/* ① 提示卡本體：從「半透明深色遮罩」改成 shadcn 卡片面
      ★★同日踩到並修掉：原規則是 `position:absolute; inset:0`——它預設「底下有商品圖撐高度」，
        自己只是蓋上去的遮罩。我把商品圖 display:none 之後父層沒有高度了，
        絕對定位的提示卡跟著沒有可填的框 → **圖示列、分隔線、手動輸入鈕整批被裁掉**
        （截圖只剩 STEP 標題兩行，高度塌到 90px）。
        ⇒ 掃描區改回**正常流排版**（position:static），由內容自己撐高度。 */
.pos #pos-list .product.not-available .not-available-text {
    position: static;
    min-height: 0;
    background: hsl(var(--card));
    color: hsl(var(--foreground));
    border: 1px solid hsl(var(--border));
    border-radius: var(--radius);
    /* 原本是 1.5rem，OS 版的 STEP 標題約 20px、說明文字 15px；
       這裡交給內層元素決定，容器本身回到正常內文字級。 */
    font-size: 1rem;
    /* 原本 align-items:center 讓內容垂直置中；OS 版是靠上排列，讀起來像表單步驟 */
    align-items: flex-start;
    justify-content: flex-start;
    padding: 20px;
    text-align: left;
}

/* ② 底下的頭像照片不要透出來 —— 掃描提示不是商品卡，沒有「商品圖」可言 */
.pos #pos-list .product.not-available .img { display: none; }
/* .text 是登入後才有意義的欄位（工作台／登入時間），未登入時只會露出半截灰字。
   ★用 display:none 不用 visibility:hidden —— 後者**仍然佔位**，實測在卡片上方留下
     一塊約 100px 的空白（截圖看得到 STEP.1 被推到卡片中段）。
   ★本規則只在 .product.not-available（＝尚未登入）成立，登入後 .text 照常顯示。 */
.pos #pos-list .product.not-available .text { display: none; }

/* ③ STEP 標題與說明：OS 版是「STEP.N」粗體大字 + 說明第二行
      ★★不要用 flex-direction:column ——內容是「文字 <br> 文字 <br> 三顆圖示 <hr> 按鈕列」
        這種**正常流**寫法（machine.blade.php 的 notAvailableText 字串模板），
        改成 column flex 會把三顆掃描圖示各自變成一個 flex item → **圖示直排**（實測截圖）。
        維持 block，讓既有的 <br> 繼續負責換行，只調字級與行距。 */
.pos #pos-list .product.not-available .not-available-text > div {
    width: 100%;
    font-size: 1.25rem;
    font-weight: 700;
    line-height: 1.4;
}

/* ④ 掃描圖示列：說明性圖示，用 muted 前景，不搶主文字。
      圖示之間留一點間距，整列與上下文字分開（OS 版是標題下方一排小圖示）。 */
.pos #pos-list .product.not-available .not-available-text > div > i,
.pos #pos-list .product.not-available .not-available-text > div > .lucide {
    display: inline-block;
    vertical-align: middle;
    margin-right: 6px;
}
.pos #pos-list .product.not-available .not-available-text i,
.pos #pos-list .product.not-available .not-available-text .lucide {
    color: hsl(var(--muted-foreground));
    width: 18px;
    height: 18px;
}

/* ⑤ 分隔線：原本 <hr> 在深色遮罩上靠預設色，換到白卡上要用 --border */
.pos #pos-list .product.not-available .not-available-text hr {
    width: 100%;
    border: 0;
    border-top: 1px solid hsl(var(--border));
    opacity: 1;
}

/* ⑥ 按鈕：OS 版是「白底 + 細邊框」的 outline 鈕，不是實心深色。
      view 寫死 `.btn-dark`（machine.blade.php 的 notAvailableText 產生器），
      這裡只改外觀不動 markup —— 那支是 JS 字串模板，改它要動 view。 */
.pos #pos-list .product.not-available .not-available-text .btn,
.pos #pos-list .product.not-available .not-available-text .btn-dark {
    background-color: hsl(var(--background)) !important;
    border: 1px solid hsl(var(--border)) !important;
    color: hsl(var(--foreground)) !important;
    font-weight: 500 !important;
    box-shadow: none !important;
}
.pos #pos-list .product.not-available .not-available-text .btn:hover,
.pos #pos-list .product.not-available .not-available-text .btn-dark:hover {
    background-color: hsl(var(--accent)) !important;
    color: hsl(var(--accent-foreground)) !important;
}
.pos #pos-list .product.not-available .not-available-text .btn i,
.pos #pos-list .product.not-available .not-available-text .btn .lucide {
    color: inherit;
}

/* --- S-46. 步驟條亮色：軌道底 --muted → --border（2026-08-06）------------------
   ★這一行是給 tests/Feature/Theme/ThemeOverrideAnnotationsTest 認的「章節錨點」。
     那支測試只認兩種標題寫法（`/* --- S-n. …` 或 `===` 橫幅下的 ` * §n …`），
     本檔 §S-42 起改用 `═` 全形橫幅，**不在它認得的兩種之內** ——
     所以 §S-42～§S-45 全都沒有自己的錨點，落在它們裡面的宣告會被歸給
     前面最後一個真錨點（§S-39 / §附錄2026-08-04），
     這就是為什麼底下那些「已被 §S-39 覆蓋」看起來位置怪怪的：不是註解寫錯，
     是錨點解析的粒度只到那裡。本節補一個認得出來的錨點，讓引用能精確指到 §S-46。 */

/* --- S-46 訂正紀錄：§S-42 裡兩行覆蓋註解的原文存查 ---------------------------
   本節新增規則之後，`.flow-stepper .flow-step.is-future{color}` 與
   `.flow-stepper .flow-branch-half.is-future{color}` 的串接勝出者從 §S-42 尾段
   換成了 §S-46，原註解記的章節與實測值都過期，**留著會讓人照錯的值判斷**
   （這正是 ThemeOverrideAnnotationsTest 建立的理由，它今天就把這兩行抓紅了）。
   ⇒ 那兩行就地更新成現況，**原文逐字保留在這裡**，一個字都沒有遺失：

     L9815（.flow-stepper .flow-step.is-future 的 color 上方）原文：
         「★已被 §S-39 覆蓋（實測生效值：color=rgb(105, 105, 114)）」
     L9888（.flow-stepper .flow-branch-half.is-future 的 color 上方）原文：
         「★已被 §S-39 覆蓋（實測生效值：color=rgb(105, 105, 114)）」

   rgb(105,105,114) ＝ 亮色 --muted-foreground-on-muted（§S-31，240 3.8% 43%），
   是「壓在 --muted 底 rgb(244,244,245) 上」的配對前景；底色換成 --border 之後
   換吃 --muted-foreground-on-border（240 3.8% 39%）＝ rgb(96,96,103)，故更新。
   ★暗色兩者同值，實測 color 仍是 rgb(161,161,170)，暗色的敘述沒有變動。
   ★為什麼不是「在原文下方追加訂正」：那支測試是逐行掃「已被 ○○ 覆蓋」，
     原文只要還貼在宣告上方三行內就會被判定為現況宣告而繼續紅 ——
     把原文往下挪或塞夠多行讓它「不貼在宣告上」而被跳過，等於自己製造豁免、
     做出一個綠燈但檔案裡仍留著假話。所以改成「現地說真話 + 原文全文存查」。 */

/* ═══════════════════════════════════════════════════════════════
   §S-46 步驟條亮色：把「軌道底」從 --muted 換成 --border（2026-08-06）
   業主（看 /order-management/status/receiving 亮色）：
       「有箭頭性 邊緣在多一點灰邊 這樣比較有頁籤切換的感覺」
   ═══════════════════════════════════════════════════════════════

   ── 病灶：讀不出形狀的不是箭頭，是「縫」──
   .flow-step 的 chevron 是 clip-path 切出來的，相鄰兩塊靠 --flow-gap: 2px 的縫分開。
   **那條縫裡露出來的是頁面底色**，所以「箭頭形狀讀不讀得出來」＝「縫色 vs 底色差多少」。
   實測（1600px 桌機、y = 步驟條上緣 +3px 的水平掃描，逐像素）：
       亮色  底 rgb(244,244,245)｜縫峰值 rgb(250,250,250)  → 差 **6/255**  ← 病灶
       暗色  底 rgb(39,39,42)   ｜縫峰值 rgb(9,9,11)      → 差 **30/255** ← 本來就好，不動
   亮色差 6 是肉眼閾值以下，11 站看起來就是一整條沒有分節的淺灰帶。

   ── 為什麼「加一圈灰色描邊」反而會把暗色弄壞（試過才排除，記下來免得有人再走一次）──
   直覺解法是給每塊補 1px 的 --border 描邊（clip-path 會裁掉 box-shadow/outline，
   所以要用四向 `filter: drop-shadow(±1px 0 0 …)` 疊出來，§S-42 已經記過這件事）。
   但 **暗色的 --border 與 --muted 是同一個值**（都是 `240 3.7% 15.9%` = rgb(39,39,42)，
   見本檔 L186 / L190）→ 描邊會把那條 2px 的縫**整條填滿成跟底一樣的顏色**，
   暗色的 30/255 直接歸零，11 塊 chevron 熔成一條。亮色修好、暗色壞掉，淨賠。

   ── 同一個理由否掉的還有兩個方案 ──
   ② 給 .flow-stepper 軌道鋪 --border 或 --card 底（讓縫裡露出來的變深）：
      亮色可行，但暗色的縫本來露的是 --background rgb(9,9,11)（全頁最深），
      換成 --border(39) 或 --card(24) 都是**變淺**，縫差 30 → 0 或 15。
   ③ 用半透明（例：`hsl(var(--border) / .65)`）調出中間灰：
      alpha 一定會把顏色往「後面那層」拉，暗色後面那層就是 rgb(9,9,11)，
      → 暗色底從 39 被拉到 29，縫差 30 → 20。**任何 alpha 在暗色都是退步**，一律不用。
   ★通則：亮色的頁面底是全頁最亮、暗色的頁面底是全頁最暗 —— 主題一翻轉，
     「讓縫變明顯」的方向就相反。所以**能動的只有塊的底色，不能動縫的顏色**。

   ── 採用的解：塊底 --muted → --border ──
   這是唯一一顆「亮色比 --muted 深、暗色與 --muted 逐字相同」的既有 token：
       亮色  --muted 240 4.8% 95.9% = rgb(244,244,245)
             --border 240 5.9% 90%   = rgb(228,228,231)      ← 深一階（zinc-100 → zinc-200）
       暗色  --muted / --border 兩者都是 240 3.7% 15.9%      ← **同值，暗色 0 變化，可證**
   ★刻意不自己調第三顆灰：§S-42 已經記過「自己另調一組也是灰白的值只會又長出第三種灰」。
     228 是 zinc-200，是本主題既有的色階，不是新發明的。

   實測結果（同一支逐像素掃描腳本，改前 / 改後）：
       亮色 縫 vs 塊底   6/255  →  **22/255**（3.7 倍）
       亮色 當前站(白) vs 塊底  11/255 → **27/255**（2.5 倍）
       暗色 縫 vs 塊底  30/255 →  30/255（不變）
       暗色 當前站 vs 塊底 15/255 → 15/255（不變）

   ── 順帶修好的 hover ──
   --accent 亮色是 240 4.8% 95.9%，**與舊的 --muted 同值** → 改動前 hover 根本沒有底色變化
   （只有文字變 --accent-foreground）。塊底降到 228 之後，hover 的 244 才真的浮起來。
   ★暗色的 --accent 同樣等於 --muted，所以暗色 hover 仍然只有文字變化 —— 那是既有狀況，
     本節沒有讓它變差，但要修得動 token 定義（影響全站），不在本節範圍，另案處理。 */

/* ── 配對前景：--muted-foreground-on-border ──
   §S-31 為了「muted 前景壓在 muted 底上只有 4.40:1」備了 --muted-foreground-on-muted（43%，實測 4.94:1）。
   底色從 244 降到 228 之後，同一顆前景 rgb(105,105,114) 掉到 **4.28:1**（比 upstream 原本的 4.40 還低），
   所以照 §S-31 的做法再配一顆「只在 --border 底上使用」的前景，把比值拉回原本的水準。
       亮色 240 3.8% 39% = rgb(96,96,103) 壓在 rgb(228,228,231) 上 → **4.91:1**（AA 4.5 過）
   ★暗色直接指回 --muted-foreground —— 與 §S-31 的 --muted-foreground-on-muted 同值，
     暗色等於一個字都沒改（暗色底也沒改，本來就 5.81:1 已過）。 */
:root                     { --muted-foreground-on-border: 240 3.8% 39%; }
html.dark                 { --muted-foreground-on-border: 240 5% 64.9%; }
[data-bs-theme="dark"]    { --muted-foreground-on-border: 240 5% 64.9%; }
[data-bs-theme="light"]   { --muted-foreground-on-border: 240 3.8% 39%; }

/* ── 塊底：未到 / 已完成 / 分叉未選 一律改吃 --border ──
   ★三種狀態同時改，不是只改「未到」：is-past 與 is-future 在 §S-42 就刻意同底
     （「已完成／未到的區分改由文字承擔」，與 .fb-tab 只有選中／未選中兩態一致），
     只改一邊會讓流程走到一半的頁面（前段 is-past）縫又變回 6/255。
   ★選擇器與 §S-42 那三條同特異度（0,3,0）＋同為 !important，靠**排在後面**取勝，
     §S-42 原文一行未動，保留在上面備查。 */
.flow-stepper .flow-step.is-future,
.flow-stepper .flow-step.is-past,
.flow-stepper .flow-branch-half.is-future {
    background-color: hsl(var(--border)) !important;
}

/* ── 未到站的字改吃配對前景（已完成站維持 §S-42 的 --foreground/.6，
      實測壓在 rgb(228,228,231) 上是 4.86:1，本來就過，不動）── */
.flow-stepper .flow-step.is-future,
.flow-stepper .flow-branch-half.is-future {
    color: hsl(var(--muted-foreground-on-border)) !important;
}

/* ★★2026-08-06 §S-57 起：下面這兩條與 §S-32 同一批，在 /pos/machine 與 /pos/package
     上已經沒有呼叫端（markup 改用 .fb-tabs SSOT，命中數 0，實測數過）；
     仍然服務四支沒有路由的孤兒 view，所以**只標註不刪除**。
     ★這一節量到的顏色結論**沒有失效，反而是被繼承了**：軌道底 --border 與
       選中片 --background 的 22／30 差距，在 .fb-tabs 那邊由 §S-47 逐字沿用
       —— 併進 SSOT 之後亮暗四個底色值與這一節量到的**逐位元組相同**（§S-57 有對照表）。
     ★未選中字色是唯一換掉的一顆：--muted-foreground-on-border → §13 的
       `--foreground / .6`（4.91:1 → 4.87:1，AA 仍過），理由見 §S-47 與 §S-57 ①。 */
/* --- S-46b. POS 側欄分段控制器亮色：軌道底 --muted → --border（2026-08-06）--------
   業主：「/pos/machine 也要完整套用 shadcn 設計 深淺」「參考 OS 版本」
   OS（賣賣 Work）的對應元件：muted 軌道 + **白色 chip**。

   ── 與 §S-46 同一個病，同一個治法 ──
   §S-32 已經把「今天紀錄(0) / 查詢包裹」做成 shadcn 分段控制器（軌道 --muted、
   選中片 --background + shadow-sm），語彙是對的，**但亮色量出來讀不出選中片**：
       亮色（逐像素掃 chip 與軌道的交界，1600px 桌機）
           選中片 rgb(250,250,250)｜軌道 rgb(244,244,245) → 差 **6/255** ← 病灶
       暗色
           選中片 rgb(9,9,11)     ｜軌道 rgb(39,39,42)   → 差 **30/255** ← 本來就好
   ★根因與 §S-46 完全一樣，而且 §S-42 早就寫過同一句話：
     **本主題的 --background 就是頁面底色（0 0% 98% ＝ #FAFAFA），不是純白**。
     upstream shadcn 的 Tabs 是 `bg-background`(#FFFFFF) 疊 `bg-muted`(#F4F4F5) ＝ 差 11，
     本專案把 --background 降到 98% 之後同一個組合只剩 6，選中片就消失了。
     暗色沒事是因為暗色的 --background(zinc-950) 是全頁最深，疊在 zinc-800 軌道上差最大。

   ── 為什麼是「把軌道變深」而不是「把 chip 變白（改吃 --card）」──
   改 chip 吃 --card：亮色 255 疊 244 ＝ 差 11（有變好），**但暗色的 --card 是 zinc-900
   rgb(24,24,27)，疊在 zinc-800 軌道上只剩差 15，等於把暗色從 30 砍掉一半**。
   改軌道吃 --border 則是：
       亮色 --border rgb(228,228,231)：chip 250 疊軌道 228 → 差 **22**（比 upstream 的 11 還好）
       暗色 --border ≡ --muted（兩者都是 240 3.7% 15.9%）→ **軌道一個像素都不會變**，
            chip 仍是 rgb(9,9,11)，差 30 原封不動。
   ⇒ 亮色多賺一倍、暗色可證零變化。這與 §S-46 是同一條規律的兩次套用，不是兩套做法：
     **亮色的頁面/軌道底太接近白，要靠「把底壓深一階」拉開；暗色不能動，
       因為暗色的深色端已經是全頁最深，任何動作都是往回退。**

   ── 連帶要換的前景 ──
   未選中片的字原本是 §S-31 的 --muted-foreground-on-muted（rgb(105,105,114)，
   為 rgb(244,244,245) 底調的，4.94:1）。軌道降到 rgb(228,228,231) 之後掉到 4.28:1（未達 AA），
   改吃 §S-46 剛備好的 --muted-foreground-on-border（rgb(96,96,103)，實測 **4.91:1**）。
   ★暗色的 --muted-foreground-on-border ≡ --muted-foreground，字色仍是 rgb(161,161,170)，同樣零變化。

   ── 這一節刻意不碰的東西（量過，沒問題就不動）──
   · `.pos-sidebar-header` 亮暗都是 rgb(24,24,27)：那是 §POS-4 刻意的「恆深識別列」，
     吃 --dark-panel（:root 與 html.dark 同值），對應 App 的 AppColors.darkPanel，不是漏修。
   · 左側 rail `.pos-menu` 亮 rgb(244,244,245) / 暗 rgb(39,39,42)、選中 chip 品牌黃 rgb(255,210,0)
     兩態相同 —— 品牌色本來就不翻轉，且 §S-37 已對齊 OS 的 bg-brand。rail 與內容區靠
     `border-right: 1px solid --border` 分界，OS 要的「淺底」已經成立。
   · 頁腳三顆鈕：後台管理 亮 rgb(255,255,255)/暗 rgb(24,24,27)（outline 語彙）、
     全部登出 亮 rgb(24,24,27)/暗 rgb(250,250,250)（實心，暗色自動反相）、
     暫停 disabled 走 §7735 那組頁腳專用規則，三顆亮暗都正確翻轉，全部不動。
   · chip 的 `box-shadow: 0 1px 2px 0 rgb(0 0 0 / .05)` 保留寫死的黑 —— 那是 upstream
     shadow-sm 的逐字值，兩個主題本來就同一份，不是本專案的寫死色。

   ★選擇器與 §S-32 那兩條逐字相同（特異度 0,5,0 / 0,7,0，皆無 !important），
     靠**排在後面**取勝，§S-32 原文一行未動。 */
.pos .pos-sidebar .pos-sidebar-nav .nav.nav-tabs {
    background: hsl(var(--border));
}
.pos .pos-sidebar .pos-sidebar-nav .nav.nav-tabs .nav-item .nav-link {
    color: hsl(var(--muted-foreground-on-border));
}

/* --- S-47. 全站頁籤 .fb-tabs 亮色：軌道底 --muted → --border（2026-08-06）------------
   ★這一行是給 tests/Feature/Theme/ThemeOverrideAnnotationsTest 認的「章節錨點」，
     寫法必須是 `/* --- S-47. …`（S- 前面不可以多 §，見 §S-46 的說明）。

   ── 這是 §S-46 / §S-46b 同一個病的第三次、也是最後一次套用 ──
   §S-42 早就寫下病根：**本主題的 --background 是 0 0% 98%（#FAFAFA）不是純白**。
   upstream shadcn 的 Tabs 是「bg-muted 軌道 + bg-background 白 chip」＝ 差 11／255，
   本專案把 --background 降到 98% 之後同一組合只剩 6／255，選中的那一格就讀不出來了。

   §S-46 修了流程步驟條、§S-46b 修了 POS 側欄分段控制器，但**本檔 §13 的
   .fb-tabs / .fb-tab（`全部`、`待處理` 那一排，全站 17 個呼叫端共用 <x-page-tabs>）
   一直沒修** —— 同一個元件語彙在三個地方有兩種深淺，那是不一致，不是設計。

   ── 實測（1600×950 桌機，逐頁讀 computed，亮暗各一輪；切 .dark 後等 450ms 再讀）──
   改前：
       亮  軌道 rgb(244,244,245)｜選中 chip rgb(250,250,250) → 差 **6／255**  ← 病灶
       暗  軌道 rgb(39,39,42)   ｜選中 chip rgb(9,9,11)      → 差 **31／255** ← 本來就好
   取樣頁（都是**多顆**頁籤的頁面，不是只有一顆的那種）：
       /order-management/status/cancelling（2 顆）、/seller/plan（3 顆）、
       /notifications（3 顆）；單顆的 /order-management/status/receiving 一併量過。

   ── 為什麼是「把軌道壓深一階」而不是別的三種做法 ──
   §S-46 已經把三條死路量過一次，這裡逐字沿用它的結論，不重新發明：
     ① 給 chip 加描邊 → 暗色的 --border ≡ --muted，描邊會把 chip 與軌道的界線填平。
     ② 讓 chip 改吃 --card（純白）→ 亮色 255 疊 244 只到 11，而暗色的 --card 是
        zinc-900 rgb(24,24,27)，疊在 zinc-800 軌道上從 31 掉到 15，**暗色砍半**。
     ③ 用 alpha 調中間灰 → alpha 一定往「後面那層」拉，暗色後面是全頁最深，一律退步。
   ⇒ 能動的只有「軌道」，而 --border 是唯一一顆「亮色比 --muted 深一階、
     暗色與 --muted 逐字相同」的既有 token：
       亮  --muted 240 4.8% 95.9% = rgb(244,244,245)
           --border 240 5.9% 90%  = rgb(228,228,231)   ← 深一階（zinc-100 → zinc-200）
       暗  --muted / --border 都是 240 3.7% 15.9%      ← 同值，暗色可證零變化
   ★不自己另調第三顆灰：§S-42 已經記過「再調一顆也是灰白的值只會長出第三種灰」。

   ── 未選中字的對比：量過，維持原本的 `--foreground / .6`，不換 token ──
   §S-46b 當時必須換前景，是因為它原本吃 §S-31 的 --muted-foreground-on-muted
   （那是**為 rgb(244,244,245) 底調的固定值**，底色一變就掉到 4.28:1 不過 AA）。
   .fb-tab 的未選中字是 `hsl(var(--foreground) / .6)` —— 半透明會**跟著新的底色重新合成**，
   所以底變深時字也跟著變深，比值幾乎不動：
       亮  合成色 rgb(103,103,105) 疊 244 = 5.13:1  →  rgb(97,97,99) 疊 228 = 4.87:1（AA 4.5 過）
       暗  合成色 rgb(166,166,167) 疊 39  = 6.12:1  →  軌道沒變，6.12:1 原封不動
   ★所以這一節**只有一條規則**。硬要改成 --muted-foreground-on-border（4.91:1）只多 0.04，
     卻會把「hover 時 .fb-tab 變成滿版 --foreground」那條漸變關係打斷，不划算。

   ── 逐像素覆核（截 .fb-tabs 元素本體，水平掃 y=中線，不是只看 computed）──
       亮  改前 chip rgb(250,250,250) vs 軌道 rgb(244,244,245)  →  改後軌道 rgb(228,228,231)
       暗  改前 chip rgb(9,9,11)      vs 軌道 rgb(39,39,42)     →  改後**逐像素完全相同**
     ★暗色「零變化」是量出來的，不是推論出來的：同一張水平掃描的色階統計改前改後一字不差。
   ★選中 chip 上的字兩個主題都是 19.06:1，本來就遠超 AA，不動。

   ── 這一節刻意不碰的東西 ──
   · chip 本身（--background + shadow-sm）：那是 upstream 的逐字規格，動它會與
     §S-46b 的 POS 分段控制器分岔，變成同一個語彙兩種做法。
   · `.fb-tabs { color: hsl(var(--muted-foreground)) }`：那是給軌道裡**非 .fb-tab**
     的子元素（例如計數 badge）用的繼承色，.fb-tab 自己另外指定了 color，不受影響。
   ★選擇器與 §13 的 `.fb-tabs` 逐字相同（特異度 0,1,0、皆無 !important），
     靠**排在後面**取勝，§13 原文一行未動。 */
.fb-tabs {
    background: hsl(var(--border));
}

/* --- S-48. 2026-08-06 覆蓋註解訂正紀錄：改成章節引用之前的原引用存查 --------------
   本檔的「★已被 ○○ 覆蓋」註解在 2026-08-06 做了第二輪訂正。
   第一輪（同日上午）已經把大部分行號改成章節名；這一輪是**判定模型升級之後**
   （tests/Feature/Theme/ThemeOverrideAnnotationsTest 第二版：逐 term × 逐主題）
   重新判過一次，把剩下的行號引用與四筆判錯的章節引用一起就地更新。

   ── 為什麼是「就地更新」而不是「在原文下方追加訂正」──
   與 §S-46 訂正紀錄記過的理由完全相同：那支測試是逐行掃「已被 ○○ 覆蓋」，
   把原文留在宣告上方三行內，它會被當成**現況宣告**繼續判、繼續紅；
   把原文往下挪到掃不到的地方，等於自己製造豁免、做出一個綠燈但檔案裡仍留著假話。
   ⇒ 現地說真話 ＋ 原引用逐筆存查在這裡，一筆都沒有遺失。

   ★存查的寫法：只記「原本引用的是誰」，不重貼整句「已被 Lxxxx 覆蓋」——
     那個字串一貼回檔案就又變成一筆行號引用，會把
     LEGACY_LINE_REF_BUDGET（只准降不准升）灌回去，等於用存查抵銷治本。
     每一行註解**除了引用那一段之外的文字（含實測生效值）都原地保留未動**，
     所以這裡記下引用本身，資訊就是完整的。

   ── 逐筆對照（行號為本次改動當下的行號）──
     L79    原引用 L9003        → §S-39（亮）        :root{--destructive}
                                  ★暗色勝出者是檔頭 html.dark 的同名宣告，
                                    §1b 那個區塊沒有測試認得的錨點，所以不另引。
     L112   原引用 L7470/L7476  → §S-12             :root{--chart-1}
     L262   原引用 L2035        → §28               .text-muted{color}
     L368 370 372 374 376 378 380 383
            原引用 §28／L1977   → 逐項：〔.btn-secondary／.btn-light〕§28；〔.btn-white〕未被覆蓋
                                  ★§28 的清單只有 .btn-secondary 與 .btn-light，
                                    **沒有 .btn-white** —— 原本一句「已被 §28 覆蓋」
                                    等於替 .btn-white 說了一句假話（CDP 實測：
                                    .btn-white 的勝出者就是 §4 自己這一行）。
     L414 416 418 420 422 424 426 429
            原引用 §28／L1977   → 逐項：〔.btn-outline-secondary／.btn-outline-dark〕§28；
                                        〔.btn-outline-light〕未被覆蓋（同上，清單裡沒有它）
     L441 443 447 449   原引用 L1982 → §28          .btn-danger{…}
     L470 472 476 478 483 原引用 L1977 → §28        .btn-success/.btn-warning/.btn-info
     L536   原引用 L1809        → §23               .card.border-0{border}
     L695   原引用 §S-31        → 逐項：〔.table-light／.table-light > th／.table-light > td〕§S-31；
                                        〔.table > thead th〕未被覆蓋
                                  ★.table > thead th 本行帶 !important 而且沒有同項競爭者，
                                    它自己就是勝出者；只有掛 .table-light 的表頭才被 §S-31 接手。
     L993 997 999 原引用 L1942  → §25               .modal-content{border/background/color}
     L1002  原引用 L1943        → §25               .modal-header/.modal-footer{border-color}
     L1021  原引用 L1902        → §25               #sidebar.app-sidebar{border-right}
     L1070 1072 原引用 §側欄：對齊shadcn
                                → 逐項：〔#sidebar .menu-item.expand > .menu-link〕§側欄：對齊shadcn；
                                        〔#sidebar .menu-item.has-sub.active > .menu-link〕未被覆蓋
                                  ★「未被覆蓋」是**同一個 selector 的串接**上沒人蓋它；
                                    元素同時帶 .expand 時另有同章節的
                                    `#sidebar .menu-item.has-sub.expand > .menu-link` 接手
                                    （同特異度、排在後面）—— 那要看頁面狀態，不是串接能決定的。
     L1112  原引用 L5818        → 逐項：〔.app-sidebar .menu-profile-info〕§可展開篩選；
                                        另兩項（.text-white-50 / small）未被覆蓋
     L1491  原引用 L2033        → 逐項：〔.text-primary〕§28；〔a.text-primary〕未被覆蓋
     L1495  原引用 L2034        → §28               .text-danger{color}
     L1497  原引用 L2031        → §28               .text-success{color}
     L1544  原引用 L2032        → 逐項：〔.text-warning〕§28；〔.text-orange／.text-yellow〕未被覆蓋
     L1691 6612 原引用 L6588    → §側欄二修：靠左     .menu-caret{transform}
     L1926 1928 原引用 L4572    → §補位6            .badge.bg-danger{background/color}
     L2660 2754 2756 2758
            原引用 §S-5「字級下限 12px」 → §S-5      ★章節名夾了空白與「」，
                                  第一版的 regex 直接比對不到 → 那幾行**從來沒有被驗過**
                                  （而且沒有人知道沒被驗）。第二版把「看起來是覆蓋註解、
                                  卻解析不出章節」也列為失敗，順手把描述移到括號裡。
     L4480  原引用 L5818        → §可展開篩選        .menu-profile-info{line-height}
     L4482  原引用 §15Sidebar   → §可展開篩選        .menu-profile-info{color}（原本就指錯章節）
     L5009 5011 5033 5035 5037 5072
            原引用 L8934／L8962 → §S-37POS          POS rail 的 .nav-link
     L6876  原引用 L9366        → §S-39             #sidebar .menu-profile-link{gap}
     L6914  原引用 L8645        → 逐項：〔.tenant-name〕§S-33；〔.tenant-meta〕未被覆蓋
     L6916  原引用 L8826        → 逐項：〔.tenant-meta〕§S-35；〔.tenant-name〕未被覆蓋
     L6923  原引用 L8891        → 逐項：〔.tenant-meta〕§S-36；〔.tenant-name〕未被覆蓋
     L6945  原引用 §側欄 profile 對比校正段 → §S-17  ★同樣是「章節名夾空白 → 從來沒被驗過」。
                                  這一輪把 `---` 橫幅下的 ` * §S-nn …` 也認成錨點之後，
                                  §S-15～§S-29 才有自己的錨點可以指（原本只能指到 §S-14）。
     L8974  原引用 L8891        → §S-36             .tenant-meta{gap}
     L8977 8979 原引用 §S-36    → §S-39〈跨模型〉    ★§S-39 的
                                  `padding: 2px 8px !important` 是**簡寫吃長寫**，
                                  串接模型只比對同名屬性（padding-left ≠ padding），
                                  看不到它 —— CDP 實測 8px 與該簡寫吻合。
     L8984  原引用 L8886        → §S-36             .tenant-code{flex}
     L9177 9179 原引用 L9367    → §S-39〈跨模型〉    同上，簡寫吃長寫
     L9262  原引用 L8934        → §S-37POS〈跨模型〉 ★本行在 @media ≤991.98px 之內，
                                  桌機根本不套用；桌機讀到的是 §S-37 的那條。

   ── 仍然留著行號引用的 26 筆（不是漏掉，是判不出來）──
   它們共同的特徵是「註解沒有貼在任何一條宣告上方」：
     · 一段前言／說明註解裡順帶提到某個值被誰蓋掉（例：L491/L493/L495、L802~L822）
     · 下一行是 `.fs-7{…}.fs-9{…}.fs-10{…}` 這種**單行多宣告**，
       分不出註解在講哪一條 —— 猜一條就是製造一個看起來對的錯答案
   沒有可判定的目標就掛不了章節名。硬掛上去只是把「會漂的行號」換成「不會漂的假話」，
   後者更難發現。⇒ 留著，並讓 LEGACY_LINE_REF_BUDGET 從 79 降到 26 鎖住現況。

   ── 順帶記一件會誤導人的事：CDP 的「最後一條 = 勝出者」在 !important 面前不成立 ──
   這一輪的實測腳本用 CSS.getMatchedStylesForNode 取 matchedCSSRules 的**最後一筆**當勝出者。
   Chrome 那份清單是照「來源順序 + 特異度」排的，**沒有把 !important 排進去**，
   所以只要真正的勝出者是靠 !important 贏的，實測就會指到錯的規則。
   實例：`.app-sidebar .menu-profile-link{display:flex !important}`（§側欄使用者區）
   才是勝出者，實測卻回報後面那條沒有 !important 的 `#sidebar .menu-profile-link`。
   ⇒ 這種情形以串接模型（有處理 !important）的答案為準，L6036 因此維持原註解不動。 */

/* --- S-49. 頁籤軌道的外框與選中片的髮絲邊（2026-08-06）------------------------
   業主：「缺少頁籤外有灰色淺框啊」「頁籤按鈕切換的時候」

   §S-47 把軌道底從 --muted 換成 --border，選中片與軌道的差距從 6 拉到 22/255，
   「哪一格被選中」讀得出來了。但**整組頁籤與頁面之間仍然沒有邊界** ——
   軌道 rgb(228,228,231) 坐在頁面底 rgb(250,250,250) 上只是一塊淺灰色塊，
   看起來像背景的一部分，不像一個「可以切換的控制項」。

   ★為什麼外框不能用 --border：軌道底現在**就是** --border，同色描邊等於沒畫。
     改用 hsl(var(--muted-foreground) / α) —— 它是少數在亮暗都會翻轉、
     且與軌道底恆有落差的前景色系 token：
       亮：--muted-foreground 是中灰，疊在淺灰軌道上 → 描邊比軌道深
       暗：--muted-foreground 是淺灰，疊在深灰軌道上 → 描邊比軌道淺
     ⇒ 同一條宣告在兩個主題都能「浮出來」，不必寫兩份。
     （這與 §S-46 那條通則同源：亮色頁面底是全頁最亮、暗色是全頁最深，
       所以不能靠固定深淺，要靠會翻轉的 token。）

   ★選中片同時補一道髮絲邊：.fb-tab 本來就有 `border: 1px solid transparent`
     （見 §13），這裡只換 border-color，**不動盒模型、不會有 1px 位移**。
     切換時除了底色變白，還多一圈邊界 —— 業主要的「切換的感覺」。 */
.fb-tabs {
    border: 1px solid hsl(var(--muted-foreground) / .28);
}
.fb-tab.active {
    border-color: hsl(var(--muted-foreground) / .22);
}

/* --- S-50. 全站頁籤 .fb-tab 改成箭頭（chevron）形狀（2026-08-06）------------------
   業主：「學這頁籤 只是 把頁籤改成有箭頭形狀而已」
         「有箭頭性 邊緣在多一點灰邊 這樣比較有頁籤切換的感覺」
   ★這一行同樣是給 tests/Feature/Theme/ThemeOverrideAnnotationsTest 認的章節錨點，
     寫法必須是 `/* --- S-50. …`（S- 前面不可以多 §，見 §S-46 的說明）。

   ── 幾何抄 §S-42 的 .flow-step，不自己發明 ──
   本專案的 chevron 已經有三份實作（§S-42 .flow-step、§步驟箭頭 .scr-step / .pm-step），
   公式是同一條：**左凹槽 + 右尖角的六點 polygon ＋ 負 margin 讓後一塊咬住前一塊**。
   這裡逐字沿用，只調兩個數：
     --fb-tab-arrow: 10px  切口深度。§S-42 是 11px 配 32px 高的步驟塊（比值 .34）；
                           .fb-tab 實測只有 27px 高（36 總高 − 2×1px 外框 − 2×3px 內距
                           − §13 `height: calc(100% - 1px)` 的那 1px），同比值換算 9.3px。
                           取 10px（比值 .37）：業主 2026-07-27 在步驟條上就是嫌 6px
                           讀不出箭頭才加到 11px，寧可略深也不要又太淺。
                           ★斜邊只跨半個高度，所以真正的邊角是 atan(arrow ÷ 高/2)：
                             本節 atan(10 ÷ 13.5) = **36.5°**、步驟條 atan(11 ÷ 16) = 34.5°，
                             兩者差 2°，肉眼是同一種箭頭（不是 atan(10÷27)=20.3°，
                             那個算法用了整個高度，是錯的，別照抄）。
     --fb-tab-gap: 2px     與 §S-42 的 --flow-gap 同值。

   ═══ 色彩：整套照抄 §S-46 的結論，一顆新的灰都沒有調 ═══
   §S-46 已經把「chevron 的形狀讀不讀得出來」這件事量到底了，結論是一句話：
       **讀不出形狀的不是箭頭，是「縫」；縫的顏色 = 塊底之外露出來的那一層。**
   步驟條的縫露的是頁面底 --background，塊底是 --border → 亮 22／暗 30，形狀就出來了。
   頁籤要照抄這一套，但**多一個步驟條沒有的東西：一條 --border 底色的軌道**。
   縫如果直接露軌道，露出來的就是 --border 本人 → 縫差 0，箭頭完全看不見
   （這是第一版實作真的發生的事，見下面「走過的死路 ②」）。
   ⇒ 解法：**把縫自己畫出來**，畫成 --background（＝步驟條那條縫的顏色），
     用 `.fb-tab::before` 一條 V 形色帶，位置正好貼著左凹槽的內側。
     這樣三個層次與步驟條逐字相同，而且軌道／選中片／未選中字**一個值都不用動**：
         塊底（未選中片）  透明 → 露出軌道 hsl(var(--border))   亮 228 ／暗 39
         縫（::before）    hsl(var(--background))               亮 250 ／暗 9   → 差 22／30
         選中片            hsl(var(--background))（§13 原值）    亮 250 ／暗 9   → 對塊底 22／30
     ★§S-47 剛量完的「選中片 vs 軌道 22／30」原封不動，§S-49 的外框與髮絲邊也原封不動。
     ★縫與選中片同色是**刻意**的，不是撞色：步驟條的縫（--background 250）與當前站
       （--card 255）本來就只差 5，同一個語彙 —— 「縫」與「被選中」都屬於『亮的那一層』，
       灰的是『沒被選中的塊』。

   ── 走過的死路（都是實測掉頭的，記下來免得有人再走一次）──
   ① **選中片改吃 --card 讓它更白**：§S-47 已經算過，暗色的 --card 是 rgb(24,24,27)，
      對塊底 rgb(39,39,42) 只剩 15 —— 把暗色的 30 砍半。實際疊上去截圖比對過
      （亮色確實更像 chip，暗色的選中片與鄰塊界線明顯比 --background 那版弱），
      不值得為亮色的一點點賣掉暗色的一半。維持 --background。
   ② **未選中片鋪一階不同的灰（--muted）讓塊自己有身體**：暗色的
      --muted ≡ --accent ≡ --border（都是 240 3.7% 15.9%）→ 暗色零效果；
      亮色則會把選中片與未選中片的差距從 22 壓到 5（250 vs 245），
      正好把 §S-47 剛修好的病搬回來。§S-42／§S-46／§S-47 三節都寫過同一句：
      **不要自己另調第三顆灰**。
   ③ **把軌道改成 --background、讓縫露軌道**（＝步驟條那種「塊浮在頁面上」的排法）：
      多顆頁籤的縫確實出來了（疊上去截圖看過），但選中片（--background）與軌道同色 →
      **單顆頁籤那頁（/order-management/status/receiving）整片軌道與 chip 熔成同一片，
      只剩 §S-49 那條 .22 髮絲邊還畫得出 chip 的輪廓**（實測截圖，不是推論）。
      而且軌道一旦與頁面同色，§S-49 的外框就成了整個元件唯一的邊界，
      業主要的「軌道裡有一片被選中」會退化成「一個框裡有幾個灰塊」。
   ④ ★★**用 `filter: drop-shadow(-1px 0 0 …)` 幫每塊描一條接縫線** —— 這條要特別記：
      §S-42／§S-46 的註解都寫著「clip-path 會裁掉 box-shadow / outline，所以要用
      四向 drop-shadow 疊出來」。**實測不成立**：Chrome 150 的 clip-path 是在 filter
      **之後**才套用的（paint 順序 filter → clip → mask → opacity），所以 drop-shadow
      投出去的那一圈同樣會被裁掉。
      驗法（誘餌）：把接縫線的顏色改成純紅 rgb(255,0,0) 截圖，全張 1048×208 只找到
      **3 顆紅色像素**（都是形狀邊緣的反鋸齒殘留），等於整條線根本沒畫出來。
      ⇒ chevron 上「畫在形狀外面」的東西一律不成立（box-shadow、outline、drop-shadow
        都一樣），只能畫在形狀**裡面**（本節的 ::before 就是為此而生）。
      ⇒ ★**同一支誘餌也在真的步驟條上跑過了**，不是推論：
        /packaging-machine-order-management/status/new-order 的
        `.flow-step.is-current`（實測 computed filter ＝
        `drop-shadow(rgba(9,9,11,.16) 0 1px 2px) drop-shadow(rgb(228,228,231) 0 0 .5px)`）
        把兩層都換成 3px 模糊的純紅之後，整張 4044×144 只有 **422 顆紅像素，
        而且全部擠在 x=24～50、y 的最上與最下兩端** —— 就是 `:first-child` 那兩個
        `border-radius` 圓角把元素自己的 alpha 往內縮、留給陰影的那一丁點縫隙。
        塊的其他邊界一顆都沒有。⇒ §S-42／§步驟箭頭 那兩條 `is-current` 的 drop-shadow
        **實際上沒有在畫任何邊界**，註解宣稱的「讓白箭頭在淺色背景中有邊界」沒有成立
        （步驟條現在讀得出來靠的是 §S-46 把塊底壓深，不是這條陰影）。
        **本節不動它們**（那是行為變更，不在本次任務範圍），但事實記在這裡，
        下次有人要靠那條陰影時不會再被註解騙一次。

   ── 軌道的 3px 內距與圓角怎麼協調（設計判斷 1）──
   ★`padding: 3px` **刻意保留不歸零**：chevron 彼此之間確實不需要內距，但那 3px 不是
     「片與片之間」的內距，是「整組片」與外框之間的溝槽 —— 選中片浮在溝槽裡才是 §13
     shadcn Tabs 的樣子，也才不會與 §S-49 的外框黏在一起（黏住的話外框會被讀成選中片
     自己的邊）。**`padding: 0` 那版真的疊上去截圖看過**（不是想像的）：
     ⑴ 多顆頁籤 → 選中片的上下緣直接貼上外框，chip 的 8px 圓角與外框的內圓角
        （10px 外框 − 1px 線 = 9px）對不齊，交界處看得出一條錯位的細縫；
     ⑵ 單顆頁籤 → 整條軌道被 chip 填滿，外框與 chip 的邊變成兩條幾乎重疊的線，
        看起來像一顆畫壞的雙框按鈕，而不是「軌道裡有一片被選中」。
     ⇒ 3px 溝槽是這個元件的一部分，不是 chevron 的礙事者。
   ★首尾補圓角、中間塊 `border-radius: 0`：矩形圓角配 V 形凹槽會把凹槽的外角啃掉一小塊，
     看起來像沒對齊。首塊補左兩角、尾塊補右兩角，值沿用 §13 的 --radius-md（8px）——
     與改造前的 chip 逐字相同，圓角這件事零變動。
   ★尾塊收平右邊、不留尖角：§S-42 的步驟條刻意保留尖角（它指向「流程結束」），
     但頁籤是**被軌道框住的一組**；尖角戳向 3px 溝槽會在外框內側留一塊三角形空隙，
     看起來像沒畫完。收平之後整組頁籤填滿軌道，箭頭感由中間的縫承擔。

   ── 只有一顆頁籤時（設計判斷 2）──
   /order-management/status/receiving 只有「全部」一顆，:first-child 與 :last-child
   同時成立。**不能讓它落到「首塊規則 ＋ 尾塊規則」的疊加結果**：那會是一塊沒有凹槽
   也沒有尖角、但左右內距一邊 12px 一邊 17px 的歪片。
   ⇒ 直接 `clip-path: none` ＋ 還原 §13 的 padding／border-radius，退回改造前那顆完整的
     圓角 chip（實測與改造前**逐像素相同**，見下面的量測表）。
     單顆頁籤本來就沒有「切換」可言，畫箭頭是畫給不存在的鄰居看的。

   ── :hover 與 :focus-visible（設計判斷 4）──
   ★hover **刻意維持 §13 的「只變字色」**，沒有加底色 hover：暗色的
     --accent ≡ --muted ≡ --border，加了在暗色是完全的 no-op，等於做一個只有亮色會動的
     假功能。§S-46 對步驟條的 hover 也是同一個結論（「要修得動 token 定義，另案處理」）。
   ★focus-visible 必須 `clip-path: none`：檔尾 §focus 那組給 .fb-tab 的間隙環是
     box-shadow 畫的，被 clip-path 裁掉就完全看不見（鍵盤使用者會失去焦點提示，
     而且這次已經證實 drop-shadow 也救不了，見死路 ④）。解法與 §S-42 的
     `.flow-step:focus-visible` 一字不差：取消裁切 ＋ z-index 抬高（負 margin 會讓後一塊
     蓋住前一塊，不抬高焦點環會被鄰居切掉半條）。同時要把 ::before 那條 V 縫關掉 ——
     形狀變回矩形之後，那條 V 會孤零零地露在方角裡。
     ★聚焦當下形狀從箭頭變回矩形是刻意的取捨：焦點環看得見 > 形狀維持，
       而且只在鍵盤聚焦那一瞬間發生。
   ── 兩個狀態的實測值（/seller/plan 第二顆，滑鼠移上去／鍵盤 Tab 走進去）──
       hover 亮 rgba(9,9,11,.6) → rgb(9,9,11)｜暗 rgba(250,250,250,.6) → rgb(250,250,250)
             （兩個主題都會動，形狀不變 clip-path 仍在）
       focus-visible 亮 box-shadow `rgb(250,250,250) 0 0 0 2px, rgba(9,9,11,.5) 0 0 0 4px`
                     暗 box-shadow `rgb(9,9,11) 0 0 0 2px, rgba(250,250,250,.5) 0 0 0 4px`
                     兩者 clip-path: none、z-index: 1、::before content: none，皆如預期。
       ★量 focus 的值要**等 transition 跑完再讀**：§13 的 `transition: box-shadow .15s`
         會讓按下 Tab 當下讀到 `rgba(0,0,0,0) 0 0 0 0, rgba(0,0,0,0) 0 0 0 0`
         （從 none 內插的起始值）—— 第一次就是這樣讀到「焦點環是透明的」白緊張一場。

   ── 套用範圍為什麼是子選擇器 `.fb-tabs > .fb-tab` ──
   §5916 記過另一種用法 `.fb-tabs > li.nav-item > button.fb-tab`（modal 分頁）。
   那種寫法下每顆 .fb-tab 都是自己 li 的獨生子，:first-child／:last-child 恆為真，
   用後代選擇器會讓它們全部落進「單顆」分支。目前 seller 全站 10 支呼叫端都是
   `.fb-tabs > a.fb-tab`（<x-page-tabs> 元件 ＋ AiTool alpha 手刻那組），
   但把範圍圈在直系子代，日後真的出現 li 包法時它會安全地維持改造前的方片。

   ── 實測（走 https://test.2025-seller.mtest，1440 與 375 兩種寬、亮暗各一輪，
        切 .dark 後等 450ms 再讀 computed；另以 DPR 3 截元素本體逐像素掃水平中線）──
   四頁 × 兩主題 × 兩寬度的軌道底／選中片底／外框色全部與改造前逐字相同：
       亮  軌道 rgb(228,228,231)｜選中片 rgb(250,250,250)｜外框 rgba(113,113,122,.28)
       暗  軌道 rgb(39,39,42)   ｜選中片 rgb(9,9,11)     ｜外框 rgba(161,161,170,.28)
       ⇒ 選中片 vs 軌道 亮 22／255、暗 30／255（＝§S-47 量到的值，一格未動）
   新增的縫（DPR 3 截圖逐像素掃出來的實際值，不是算出來的；掃 y = 片頂下方 2.3px
   這種沒有文字的列，掃中線會被字的反鋸齒灰階干擾 —— 第一版就是把 183 的字邊
   誤讀成「縫畫出來了」，白高興了一輪）：
       亮 縫 rgb(250,250,250) 夾在 rgb(228,228,231) 之間 → **22/255**
       暗 縫 rgb(9,9,11)      夾在 rgb(39,39,42)     之間 → **30/255**
       兩個主題的縫寬都是 5～6 個裝置像素 ＠DPR3 ＝ 1.7～2.0 CSS px（宣告值 2px）
       ★兩個主題掃出來的「縫在第幾個像素」逐格相同（只有顏色互換），可證幾何沒有分岔。
   元件高度四頁八種組合全部 36.00px、每片 27.00px（與改造前逐字相同，沒有位移）；
   `document.scrollWidth == clientWidth`（1440 與 375 都沒有橫向溢出）。
   單顆頁籤（/order-management/status/receiving）走 `:first-child:last-child` 還原分支：
   軌道 81.66px、chip 73.66×27 與改造前逐字相同，而且**改前／改後截圖逐像素比對
   四種組合全部 0 個像素不同**（不是「看起來一樣」，是掃出來的）。
   /pos/machine 用的是 Bootstrap `.nav-tabs`，整頁 `.fb-tab, .fb-tabs` 元素數 = **0**，
   本節的選擇器一條都咬不到它（實測 querySelectorAll 數過，不是推論）。

   ★選擇器特異度：`.fb-tabs > .fb-tab` 是 (0,2,0)，勝過 §13 的 `.fb-tab` (0,1,0)；
     刻意**不宣告 background** —— 未選中片維持 §13 的 transparent 直接露出軌道，
     所以 §13 `.fb-tab.active { background }` (0,2,0) 完全不受影響（同分時後宣告者勝，
     只要寫了就會蓋到）。§13 / §S-47 / §S-49 三段原文一行未動。 */
.fb-tabs {
    --fb-tab-arrow: 10px;
    --fb-tab-gap: 2px;
    --fb-tab-seam: 2px;
}
.fb-tabs > .fb-tab {
    /* 負 margin 讓後一塊的凹槽咬住前一塊的尖角，中間留 --fb-tab-gap 的縫 */
    margin-left: calc((var(--fb-tab-arrow) - var(--fb-tab-gap)) * -1);
    /* 補回被凹槽吃掉的左側淨空：文字在垂直中線，凹槽最深處也在中線，但斜邊在文字高度
       範圍內只吃掉約一半深度 → 補 arrow 的一半（5px）就夠。補滿 10px 文字會明顯偏右
       （§S-42 的 .flow-step 同樣只補 4px 不補滿）。 */
    padding-left: calc(.75rem + var(--fb-tab-arrow) * .5);
    border-radius: 0;
    clip-path: polygon(
        0 0,
        calc(100% - var(--fb-tab-arrow)) 0,
        100% 50%,
        calc(100% - var(--fb-tab-arrow)) 100%,
        0 100%,
        var(--fb-tab-arrow) 50%
    );
}
/* §13 的 `border: 1px solid transparent` 與 §S-49 的髮絲邊在 chevron 上會被裁成
   「只剩上下兩條、兩條斜邊全無」的殘邊（clip-path 的 polygon 貼著 border-box 的上下緣走）
   → chevron 片的邊框收成透明。
   ★★這條刻意寫 `:not(:first-child:last-child)`，不是併進上面那條 base 規則：
     第一版就是併在 base 裡，結果**單顆頁籤那頁的選中片連 §S-49 的髮絲邊一起被關掉**
     （逐像素比對抓到的：改前後差 218 個像素，全部落在 chip 的周框上，
       rgb(220,220,222) → rgb(250,250,250)，正好是 rgba(113,113,122,.22) 疊在 250 上的值）。
     單顆那顆是 `clip-path: none` 的方片，邊框畫得出來也該畫 —— 排除掉它之後
     §S-49 對它仍然生效，改前後回到逐像素零差異。
   ★同理 :focus-visible 的方片不必排除：那一瞬間的視覺指示是間隙環，不是髮絲邊。 */
.fb-tabs > .fb-tab:not(:first-child:last-child) {
    border-color: transparent;
}
/* 縫：貼著左凹槽內側的一條 V 形色帶，顏色＝步驟條那條縫（--background）。
   ★inset: -1px 是要讓它對齊 **border-box**：.fb-tab 有 1px 透明邊框，
     絕對定位預設對齊 padding-box，差那 1px 會讓色帶上下各短 1px、看起來沒接到底。
   ★它畫在形狀**裡面**，所以不會被 clip-path 裁掉（外面畫的一律會，見死路 ④）。 */
.fb-tabs > .fb-tab::before {
    content: '';
    position: absolute;
    inset: -1px;
    background: hsl(var(--background));
    clip-path: polygon(
        0 0,
        var(--fb-tab-seam) 0,
        calc(var(--fb-tab-seam) + var(--fb-tab-arrow)) 50%,
        var(--fb-tab-seam) 100%,
        0 100%,
        var(--fb-tab-arrow) 50%
    );
}
/* 首塊：左邊是整組的外緣，不該有凹槽（會看起來像被前一塊咬掉一角），也不該有縫 */
.fb-tabs > .fb-tab:first-child {
    margin-left: 0;
    padding-left: .75rem;
    border-top-left-radius: var(--radius-md);
    border-bottom-left-radius: var(--radius-md);
    clip-path: polygon(
        0 0,
        calc(100% - var(--fb-tab-arrow)) 0,
        100% 50%,
        calc(100% - var(--fb-tab-arrow)) 100%,
        0 100%
    );
}
.fb-tabs > .fb-tab:first-child::before { content: none; }
/* 尾塊：右邊收平並補圓角，整組頁籤才填滿軌道（見上面「尾塊收平右邊」） */
.fb-tabs > .fb-tab:last-child {
    border-top-right-radius: var(--radius-md);
    border-bottom-right-radius: var(--radius-md);
    clip-path: polygon(
        0 0,
        100% 0,
        100% 100%,
        0 100%,
        var(--fb-tab-arrow) 50%
    );
}
/* 只有一顆：first 與 last 同時成立 → 完整還原成 §13 的圓角 chip */
.fb-tabs > .fb-tab:first-child:last-child {
    padding-left: .75rem;
    border-radius: var(--radius-md);
    clip-path: none;
}
/* 鍵盤焦點：取消裁切讓 §focus 的間隙環（box-shadow）畫得出來，並抬高蓋過鄰居 */
.fb-tabs > .fb-tab:focus-visible {
    clip-path: none;
    border-radius: var(--radius-md);
    z-index: 1;
}
.fb-tabs > .fb-tab:focus-visible::before { content: none; }

/* --- S-51. 步驟條「當前站」的邊界：§S-42 那條 drop-shadow 是死碼，改用 ::before 畫（2026-08-06）---
   ★這一行是給 tests/Feature/Theme/ThemeOverrideAnnotationsTest 認的「章節錨點」，
     寫法必須是 `/* --- S-51. …`（S- 前面不可以多 §，見 §S-46 的說明）。

   ═══ 一、被訂正的那條錯誤知識 ═══
   §S-42（.flow-step.is-current 上方）與 §S-46（「加一圈灰色描邊」那段）都寫著同一句：
       「clip-path 會裁掉 box-shadow / outline，所以要用 filter: drop-shadow」
   **只有前半句成立。後半句是錯的，而且錯了兩個章節、被抄過三次。**
   ★原註解一個字都沒有刪，仍留在 §S-42 / §S-46 原處備查；本節只是把「後半句不成立」
     這件事就地說清楚，並提供真的畫得出來的做法。

   為什麼後半句錯 —— Chrome（以及規範）的套用順序是：
       filter  →  clip-path  →  mask  →  opacity
   `filter` 先把元素（含它的陰影）畫成一張點陣，**接著 `clip-path` 再對那張點陣裁一刀**。
   drop-shadow 的定義就是「往形狀外面擴散」，所以它擴散到的每一顆像素都在
   clip-path 的多邊形外面 —— 下一步就被整片裁掉。
   ⇒ box-shadow 會被裁掉，drop-shadow 一樣會被裁掉。差別只在「被誰裁」，不在「會不會被裁」。
   ⇒ **凡是畫在形狀外面的東西（box-shadow / outline / drop-shadow）在 chevron 上一律無效**，
     唯一有效的方向是「畫在形狀裡面」——§S-50 的頁籤縫（.fb-tab::before）就是這個套路，
     那條註解已經寫對了：「它畫在形狀**裡面**，所以不會被 clip-path 裁掉」。

   ═══ 二、死碼的逐像素證據（誘餌實測，不是推論）═══
   把 §S-42 的
       filter: drop-shadow(0 1px 2px hsl(var(--foreground)/.16)) drop-shadow(0 0 .5px hsl(var(--border)))
   整條換成 3px 的**純紅** `drop-shadow(0 0 3px rgb(255,0,0))`（同一支腳本、DPR 3 截圖、
   逐像素數 r>170 且 g,b<90 的紅點），走 https://test.2025-seller.mtest 六頁 × 亮暗 × 1440/375：

       當前站不是 :first-child 的四頁（printing／returned／return-cancelled／completed）
           → 紅像素 **0 顆**。整條宣告畫不出任何一顆像素。
       當前站是 :first-child 的兩頁（receiving／returning）
           → 紅像素 104–135 顆，**全部落在左側兩個 border-radius 圓角**
             （上緣覆蓋率 1.7–2.7%、下緣 2.6–3.7%、右斜邊 **0.0%**）。
             會漏出來只是因為首塊同時有 border-radius 與 clip-path：背景被圓角切掉，
             clip-path 的多邊形卻是方角，那一小塊「圓角外、多邊形內」的空隙讓陰影露了出來。
             ⇒ 它畫出來的不是「邊框」，是兩顆圓角上的髒點，而且只有第一站有。

   ★所以「步驟條現在讀得出來」靠的是 §S-46 把塊底從 --muted 壓到 --border，
     不是這條陰影。本節把陰影改成 `filter: none`（宣告在這裡，§S-42 原文未動），
     順手把那兩顆圓角髒點一起收掉。

   ★§S-42 那條規則裡的 `background-color` 與 `filter` 兩行上方各**新增**了一行
     「★已被 §S-51 覆蓋（實測生效值：…）」——只加註解，宣告與原有文字一個字都沒動。
     不加的話 §S-42 會留下兩句假話（「當前站的底是 --card」「這裡有陰影」），
     那正是 ThemeOverrideAnnotationsTest 存在的理由。
     ★這兩行**實際被測到**，不是寫爽的：把章節號竄改成 §S-47／§S-49 各跑一次，
       測試分別紅在 L9860{background-color} 與 L9869{filter}，改回來才恢復綠。
       （filter 那行第一版寫在四行註解的**開頭**，declFor 只往下找三行 → 竄改後照樣綠燈，
         等於加了一條沒人驗的註解。移到緊貼宣告的最後一行才真的被納入驗證。）

   ═══ 三、採用的解：本體當「邊」，::before 當「面」 ═══
       .is-current 本體 background → hsl(var(--border))   ← 這一層就是那圈邊
       ::before 內縮 1px、同形狀、background → hsl(var(--card))  ← 這一層是白色的面
   兩層都在 clip-path 裡面，所以一定畫得出來。

   ★為什麼是 ::before 而不是 border / outline / box-shadow：
     - `border` 不會被 clip-path 拿掉（它在 border-box 內），但**多邊形是貼著 border-box 的
       上下緣走的**，所以 chevron 上只會剩上下兩條橫邊、兩條斜邊全無 —— §S-50 已經實測過
       這個殘邊現象（那邊的處置是把它收成 transparent）。半圈邊比沒有邊更難看。
     - `outline` / `box-shadow` 畫在形狀外面 → 見上面第一段，一律被裁。

   ★為什麼要 `z-index: -1` 配 `isolation: isolate`：
     ::before 是絕對定位、z-index auto 的話會落在「定位後代」那一層，**蓋在文字上面**
     （文字是行內內容，畫得比定位後代早）→ 站名會被白色蓋掉。
     負 z-index 會讓它退到「元素自身背景之後、行內內容之前」那一層，正好是我們要的夾層；
     但負 z-index 只在**祖先堆疊脈絡**裡排序，所以本體必須自己是一個堆疊脈絡，
     否則它會退到本體背景的**後面**（那圈 --border 就會把它整個蓋住，等於白畫）。
     §S-42 的 `filter` 本來就會建立堆疊脈絡，但本節把 filter 關成 none 了 ——
     **不能靠一條已經被自己關掉的宣告撐著**，所以明寫 `isolation: isolate`。

   ★配色只用會翻轉的兩顆既有 token，一顆新的灰都沒有調：
       亮  邊 --border rgb(228,228,231)｜面 --card rgb(255,255,255)｜縫露出的頁面底 rgb(250,250,250)
       暗  邊 --border rgb(39,39,42)   ｜面 --card rgb(24,24,27)   ｜縫露出的頁面底 rgb(9,9,11)
     ⇒ 邊 vs 面：亮 27/255、暗 15/255；邊 vs 縫：亮 22/255、暗 30/255。**兩個主題都畫得出來**。
     ★邊的顏色刻意就是 --border ——§S-42 那條死宣告的第二層本來寫的就是
       `drop-shadow(0 0 .5px hsl(var(--border)))`，本節是把它的**意圖**實現出來，不是改設計。

   ★刻意**不**重現死宣告的第一層（`0 1px 2px foreground/.16` 那層柔和落影）：
     §S-46 的通則已經證明過「亮色頁面底是全頁最亮、暗色是全頁最深」——
     暗色下當前站(24) 比頁面底(9) 還亮，往下打一層深色影子在暗色是**看不見的**，
     要讓它兩邊都成立就得讓陰影顏色隨主題換方向（亮色打深影、暗色打亮邊），
     那等於新造一組不會翻轉的規則。**一圈實心髮絲邊在兩個主題都成立，就不再多加一層。**

   ═══ 四、內縮多邊形的頂點是算出來的，不是目測 ═══
   「把同一個多邊形套在 inset 之後的小盒子上」對**直角/鈍角**形狀夠用，
   但對**銳角**頂點會退化（角越尖，內縮同樣 1px 得到的垂直距離越小）。
   所以只有分叉那兩片改寫顯式頂點；其餘沿用小盒子法。逐邊垂直距離（t = --fs-ring）：

     ① 主幹 chevron（高 32、切口 A=11，斜邊向量 (11,16)、長 L=19.416）
        用顯式頂點，全周皆為 t：
          左上/左下角  x = t(A+L)/(H/2) = 1.901t      → 寫 1.9
          右上/右下肩  x = W-A-t(L-A)/(H/2) = -0.526t → 寫 .53
          右尖角/左凹槽尖  t·L/(H/2) = 1.2135t        → 寫 1.21
     ② 首塊（無左凹槽、左側兩角有 --radius 圓角）
        改用小盒子法 `inset: t` + 圓角同步縮 t：斜邊上的實際邊寬 0.82t–1.39t
        （尖端最細 0.82t）。**首塊必須這樣做**：圓角要靠 border-radius 畫，
        而 border-radius 只認 ::before 自己的盒子，用 inset:0 的話圓角會對不上內縮後的邊。
     ③ 分叉上/下半（高 15、切口 A=11，斜邊向量 (11,15)、長 L=18.601）
        銳角頂點（兩片相咬的那個尖端、以及外側那個尖角）用小盒子法只剩 **0.215t**
        ≈ 畫不出來 → 改顯式頂點：
          銳角  x = t(A+L)/H = 1.973t  → 寫 1.97
          鈍角  x = t(L-A)/H = 0.507t  → 寫 .51

   ★常數是綁在「A=11、H=32/15」這組既有尺寸上的；動 --flow-arrow / --flow-h 要回來重算。
     用 calc(var(--fs-ring) * k) 寫，是為了「邊寬」這件事只有一個地方可以改。

   ═══ 五、實測（走 https://test.2025-seller.mtest，六頁 × 亮暗 × 1440/375）═══
   對比一格未動（§S-46 量到的值）：未到 vs 頁面底 亮 22／暗 30；當前 vs 未到 亮 27／暗 15。
   版面零位移：步驟塊 32.00px 高、分叉半片 15.00px、每站約 78–85px 寬，
   document.scrollWidth == clientWidth。詳細數字見本次回報。 */
.flow-stepper .flow-step.is-current {
    /* 邊寬。改這一顆就好，多邊形頂點都是它乘出來的。 */
    --fs-ring: 1px;
    /* 讓下面那顆 z-index:-1 的 ::before 停在「本體背景之上、文字之下」 */
    isolation: isolate;
    /* 本體＝那圈邊（面由 ::before 畫）。與 §S-42 同特異度 (0,3,0) + !important，
       靠排在後面取勝，§S-42 原文一行未動。 */
    background-color: hsl(var(--border)) !important;
    /* §S-42 的 drop-shadow 是死碼（第二段有逐像素證據），關掉；原文保留在 §S-42。 */
    filter: none !important;
}
/* 面：一般步驟塊（中段／末段）—— 左凹槽 + 右尖角，顯式內縮頂點 */
.flow-stepper .flow-step.is-current::before {
    content: '';
    position: absolute;
    inset: 0;
    z-index: -1;
    background: hsl(var(--card));
    clip-path: polygon(
        calc(var(--fs-ring) * 1.9) var(--fs-ring),
        calc(100% - var(--flow-arrow) - var(--fs-ring) * .53) var(--fs-ring),
        calc(100% - var(--fs-ring) * 1.21) 50%,
        calc(100% - var(--flow-arrow) - var(--fs-ring) * .53) calc(100% - var(--fs-ring)),
        calc(var(--fs-ring) * 1.9) calc(100% - var(--fs-ring)),
        calc(var(--flow-arrow) + var(--fs-ring) * 1.21) 50%
    );
}
/* 面：首塊 —— 沒有左凹槽、左側兩角有圓角，改用小盒子法讓圓角能一起縮
   （選擇器 (0,4,1) 勝過上面那條 (0,3,1)；分叉半片不在 .flow-stepper-track 底下，咬不到） */
.flow-stepper-track > .flow-step.is-current:first-child::before {
    inset: var(--fs-ring);
    /* ★已被 §S-52 覆蓋（實測生效值：5px＝calc(--radius-sm − --fs-ring)，跟著本體收小）*/
    border-top-left-radius: calc(var(--radius) - var(--fs-ring));
    /* ★已被 §S-52 覆蓋（實測生效值：5px＝calc(--radius-sm − --fs-ring)）*/
    border-bottom-left-radius: calc(var(--radius) - var(--fs-ring));
    clip-path: polygon(
        0 0,
        calc(100% - var(--flow-arrow)) 0,
        100% 50%,
        calc(100% - var(--flow-arrow)) 100%,
        0 100%
    );
}
/* 面：分叉上半（銳角在左上與右下，要用大係數 1.97 才推得進去） */
.flow-stepper .flow-branch-half.flow-branch-top.is-current::before {
    clip-path: polygon(
        calc(var(--fs-ring) * 1.97) var(--fs-ring),
        calc(100% - var(--flow-arrow) - var(--fs-ring) * .51) var(--fs-ring),
        calc(100% - var(--fs-ring) * 1.97) calc(100% - var(--fs-ring)),
        calc(var(--flow-arrow) + var(--fs-ring) * .51) calc(100% - var(--fs-ring))
    );
}
/* 面：分叉下半（上下鏡射，銳角換到右上與左下） */
.flow-stepper .flow-branch-half.flow-branch-bottom.is-current::before {
    clip-path: polygon(
        calc(var(--flow-arrow) + var(--fs-ring) * .51) var(--fs-ring),
        calc(100% - var(--fs-ring) * 1.97) var(--fs-ring),
        calc(100% - var(--flow-arrow) - var(--fs-ring) * .51) calc(100% - var(--fs-ring)),
        calc(var(--fs-ring) * 1.97) calc(100% - var(--fs-ring))
    );
}
/* 鍵盤焦點：§S-42 的 :focus-visible 會把本體的 clip-path 取消成方塊，
   面若還維持 chevron 內縮形狀，方塊的四個角就會露出一圈 --border 的直角框。
   ★這裡不能學 §S-50 用 `content: none`：本節的面是「白色那一層」，
     關掉它整塊會變成 --border 灰 —— 當前站在聚焦當下反而消失。攤平成整塊即可。 */
.flow-stepper a.flow-step.is-current:focus-visible::before {
    inset: 0;
    clip-path: none;
    border-radius: inherit;
}

/* --- S-52. 箭頭改回 opt-in：只有 .fb-tabs--flow 才是 chevron（2026-08-06）----------
   業主（訂正 §S-50 的過度套用）：
     「你搞錯了不是所有頁籤按鈕都要改成箭頭 是只有訂單或是那種需要步驟的才需要」
     「其他請你還原為原本shadcn的頁籤按鈕」
     「步驟的其實也只是把shadcn的頁籤按鈕 改為箭頭」「整體設計不會差太多」
   ★這一行同樣是給 tests/Feature/Theme/ThemeOverrideAnnotationsTest 認的章節錨點，
     寫法必須是 `/* --- S-52. …`（S- 前面不可以多 §，見 §S-46 的說明）。

   ═══ 一、§S-50 錯在哪：它套到的那些頁籤，一個都不是「步驟」 ═══
   §S-50 把 chevron 加在 `.fb-tabs > .fb-tab`，也就是全站每一組頁籤。
   實地清點（掃 <x-page-tabs> 18 個呼叫端、@include('Order._partials.list_tabs') 15 個
   呼叫端、以及唯一一處手刻 markup 的 AiTool/alpha）之後才看清楚一件事：

     **本專案「有先後步驟」的東西根本不是 .fb-tabs，是 <x-flow-stepper>（.flow-step）。**

   訂單管理 16 支狀態頁與打包機 11 支狀態頁，頂端那條「01.收單中 → 02.待付款 → …」
   是 §S-42 的 .flow-stepper，它從一開始就是 chevron。那些頁面上的 .fb-tabs
   只是卡片上方的**篩選器**（13 支頁面就只有一顆「全部」，另兩支是「全部／待辦」）。
   ⇒ §S-50 給箭頭的對象，剛好全是「沒有先後關係」的那一批：
      發票一覽的 8 個生命週期分頁、通知中心的 全部/未讀/待處理、方案的 三頁、
      商店設定的 四段、人事的 三段、AI 工具的 五種類型篩選……
      業主說的「搞錯了」就是這件事，不是箭頭本身難看。

   ★所以本節做完之後，flow 變體在目前的程式碼裡**沒有任何呼叫端**（詳見回報）：
     真正有步驟的頁面已經由 .flow-stepper 承擔，不需要再用頁籤畫一次箭頭。
     變體仍然實作出來，是因為業主明確要求「步驟的把 shadcn 頁籤改成箭頭」——
     日後真的出現「用頁籤表達步驟」的頁面時，呼叫端傳 variant="flow" 就有，
     不必再改 CSS，也不會再次波及其他 33 個呼叫端。

   ═══ 二、收窄的手法：不動 §S-50 一個字，改用 :not() 把它擋在一般頁籤外 ═══
   §S-50 的選擇器一律是 `.fb-tabs > …`，而 `.fb-tabs--flow` 的元素**同時也有 .fb-tabs**，
   所以只要在後面補一組 `.fb-tabs:not(.fb-tabs--flow) > …` 把值退回 §13，
   就得到「一般頁籤＝§13 原樣、flow 變體＝§S-50 原樣」——
   §S-50 那 9 條規則一行未刪、一個值未改，它在變體下仍然逐字生效。

   ★特異度逐條對過（§S-52 排在最後面，同分時後宣告者勝）：
       §S-50 `.fb-tabs > .fb-tab`                        (0,2,0)
       本節  `.fb-tabs:not(.fb-tabs--flow) > .fb-tab`     (0,3,0)  → 勝
       §S-50 `.fb-tabs > .fb-tab:not(:first-child:last-child)` (0,4,0)
       本節  `…:not(.fb-tabs--flow) > .fb-tab.active`     (0,4,0)  → 同分、在後 → 勝
       §S-50 `.fb-tabs > .fb-tab:first-child` / `:last-child`  (0,3,0)
       本節  `…:not(.fb-tabs--flow) > .fb-tab:first-child, …:last-child` (0,4,0) → 勝
       §S-50 `.fb-tabs > .fb-tab:first-child:last-child`  (0,4,0)：宣告的值
             （padding-left .75rem／border-radius --radius-md／clip-path none）
             **與 §13 的一般頁籤完全相同**，不必退回，留著它繼續贏也不會有差別。
     ★`:not(.fb-tabs--flow)` 的括號內是一個 class，依規範要計入特異度 → 本節多 1 個 class。

   ★為什麼要單獨補一條 `.active { border-color }`：
     §S-50 有一條 `.fb-tabs > .fb-tab:not(:first-child:last-child){border-color:transparent}`
     (0,4,0)，它會蓋掉 §S-49 給選中片的髮絲邊 `.fb-tab.active{border-color}` (0,2,0)。
     chevron 上那圈邊只會剩上下兩條橫邊（斜邊畫不出來），關掉是對的；
     但一般頁籤是矩形，那圈邊要留著（業主要的「切換的感覺」就是它）。
     ⇒ 本節用同分 (0,4,0) 但排在後面的一條把 §S-49 的值原樣寫回來。

   ★`::before` 那條 V 形縫要關掉（`content: none`）：它是 chevron 專用的接縫，
     形狀退回矩形之後會變成一條孤零零的白色 V 疊在片的左緣。

   ═══ 三、§S-49 的外框「兩個變體都留」，這是刻意的判斷 ═══
   §S-49（軌道外框 + 選中片髮絲邊）與 §S-50 是同一個 commit 進來的，
   但它回應的是**另一句**業主指示（「缺少頁籤外有灰色淺框啊」），業主這次沒有收回。
   而且本次三個微調的第一句就是「白色按鈕比**框**再縮一點」——那個「框」要存在，
   這句話才成立。
   ★更硬的理由：業主定調「步驟的其實也只是把 shadcn 的頁籤按鈕改為箭頭」「整體設計
     不會差太多」⇒ **變體與一般頁籤的差別只准是形狀**。外框若只給變體，兩者就會差
     「形狀 + 有沒有框」兩件事，直接違背這句話。⇒ 外框維持全域，本節一個字都沒動它。

   ═══ 四、微調 A：白色 chip 相對軌道再內縮一點 ═══
   業主：「白色按鈕比框在縮一點會好看一點」（參考 shadcn 標準頁籤的比例）。
   ★先去量了對照組再改，不是憑感覺：肥肥本機 /Users/monet/mtest/2026-fatboy 的
     shadcn-theme.css 裡 `.fb-tabs`/`.fb-tab` 與本檔 §13 **逐字相同**
     （padding 3px、chip height calc(100% - 1px)、radius --radius-md），
     那份沒有 §S-49 的外框，所以 chip 上下各內縮 (36 − 29) / 2 = 3.5px。
   本檔多了 §S-49 的 1px 外框，chip 是 27px、內縮 4.5px —— 已經比對照組更縮了，
   但那是「外框吃掉的」，不是「留給 chip 呼吸的溝槽」：溝槽仍然只有 §13 的 3px。
   ⇒ 只把溝槽 3px → 4px（軌道總高 36px 不動，所以**版面零位移**）：
       chip 高 27px → 25px、上下內縮 4.5px → 5.5px、左右內縮 4px → 5px。
   ★刻意只動 padding 這一個數：業主說「整體設計不會差太多」，
     字級／字重／高度／配色一個都沒碰，chevron 變體也吃同一個溝槽（差別只有形狀）。
   ★為什麼不改 `.fb-tab { height }`：那只縮上下、不縮左右，會變成一顆扁片；
     溝槽是四邊一起縮，才是業主說的「比框再縮一點」。
   ★副作用已量過並接受：chevron 變體的片高跟著 27px → 25px，而 §S-50 的切口深度
     --fb-tab-arrow 仍是 10px ⇒ 邊角 atan(10 ÷ 12.5) 從 36.5° 變成 38.7°（＋2.2°）。
     §S-50 自己算過步驟條是 34.5°、頁籤是 36.5°，「差 2° 肉眼是同一種箭頭」——
     38.7° 一樣落在這個帶內，所以**不跟著調 --fb-tab-arrow**：
     動它就等於為了一個目前沒有呼叫端的變體，去改一個已經量測定案的數字。

   ═══ 五、微調 B：步驟條當前站的白色圓角收小 ═══
   業主：「白色倒圓角太圓」（頁面 /packaging-machine-order-management/status/new-order）。
   ★★那一頁**沒有任何 .fb-tabs**（實測 querySelectorAll('.fb-tabs').length === 0），
     畫面上唯一的白色圓角塊是 .flow-stepper 的當前站「03.新訂單」——
     它是 :first-child，吃 §S-42 的 `border-radius: var(--radius)`＝10px，
     而塊高只有 32px（比值 .31）；§S-51 的白色面 ::before 跟著是 9px。
     一開始差點把這句話當成頁籤的 chip 在調，去量了才發現對象根本不是頁籤。
   ⇒ 收成 --radius-sm（6px，比值 .19），白色面同步 calc(6px − 1px) = 5px。
   ★用既有 token 不新造數字：--radius-sm 是本檔既有的一階（DropdownMenuItem/code/kbd），
     10px → 8px（--radius-md）實測只差 2px，肉眼幾乎讀不出差別，達不到業主要的「收小」；
     6px 仍然明顯是圓角、不會變成硬邊方塊。
   ★只動 :first-child 那兩個角：其餘三塊本來就是 0（chevron 的斜邊沒有圓角可言），
     線性流程最後一塊的尖角是 §S-42 刻意保留的語意，不在本節範圍。
   ★★一般頁籤的 chip 圓角 **維持 §13 的 --radius-md（8px）不動**：
     業主這句話指名的頁面上沒有頁籤，而第一句話又要我照 shadcn 標準頁籤的比例走，
     8px 正是對照組（肥肥 /hr/role）的值。把它一起收小就會變成「他沒說的地方我又自己
     多改一處」—— 本節存在的理由就是修這種過度套用，不該在同一節裡再犯一次。

   ═══ 六、微調 C：頁籤不要圖示（在元件端，不在 CSS） ═══
   業主：「頁籤不需要 icon」（頁面 /invoice/main）。
   ★沒有用 CSS 把 <i> 藏起來：那會留下一個「HTML 有、畫面沒有」的鬼元素，
     而且 lucide 會照樣把它換成 <svg>，只是永遠看不到。治本在輸出端。
   ⇒ resources/views/components/page-tabs.blade.php 新增 `icons` prop（預設 false），
     原本的 `@if (!empty($tab['icon']))` 與 <i> 一個字都沒刪，只是多一道開關。
     呼叫端要圖示時傳 icons 即可。詳細取捨（含「傳 icon 的呼叫端有哪些」）見該檔註解。

   ═══ 七、實測（走 https://test.2025-seller.mtest，7 頁 × 亮暗 × 1440/375）═══
   數值見本次回報的量測表；本節四項改動全部驗過：
   ① 一般頁籤的 clip-path 全部回到 none、::before content 為 none；
   ② 變體 .fb-tabs--flow 的 clip-path 與 §S-50 逐字相同（同一組 polygon 值）；
   ③ 軌道高度 36.00px 一格未動（padding 改動只縮 chip，不長高）；
   ④ document.scrollWidth == clientWidth（1440 / 375 皆無橫向溢出）。 */

/* ── (1) 一般頁籤：把 §S-50 的 chevron 幾何整組退回 §13 ── */
.fb-tabs:not(.fb-tabs--flow) > .fb-tab {
    margin-left: 0;
    padding-left: .75rem;
    border-radius: var(--radius-md);
    clip-path: none;
}
/* chevron 專用的 V 形接縫：矩形片不需要，關掉（§S-50 原規則在變體下仍然生效） */
.fb-tabs:not(.fb-tabs--flow) > .fb-tab::before {
    content: none;
}
/* §S-49 給選中片的髮絲邊，在一般頁籤要留著 —— 把被 §S-50 關掉的那圈邊寫回來。
   (0,4,0) 與 §S-50 的 `:not(:first-child:last-child)` 同分，靠排在後面取勝。 */
.fb-tabs:not(.fb-tabs--flow) > .fb-tab.active {
    border-color: hsl(var(--muted-foreground) / .22);
}
/* 首塊／尾塊：§S-50 只補了單邊兩個圓角並各自留了凹槽或平邊，這裡一起回到四角 8px 方片 */
.fb-tabs:not(.fb-tabs--flow) > .fb-tab:first-child,
.fb-tabs:not(.fb-tabs--flow) > .fb-tab:last-child {
    padding-left: .75rem;
    border-radius: var(--radius-md);
    clip-path: none;
}

/* ── (2) 微調 A：軌道溝槽 3px → 4px，白色 chip 相對外框再內縮 1px（兩個變體共用）── */
.fb-tabs {
    padding: 4px;
}

/* ── (3) 微調 B：步驟條起點的圓角 10px → 6px（白色面同步 9px → 5px）── */
.flow-stepper-track > .flow-step:first-child {
    border-top-left-radius: var(--radius-sm);
    border-bottom-left-radius: var(--radius-sm);
}
.flow-stepper-track > .flow-step.is-current:first-child::before {
    border-top-left-radius: calc(var(--radius-sm) - var(--fs-ring));
    border-bottom-left-radius: calc(var(--radius-sm) - var(--fs-ring));
}

/* --- S-53. 全站「頁籤列／動作列／篩選列」三列 SSOT（2026-08-06）--------------------
   業主三句話（三個頁面、同一件事）：
     ①「這頁篩選列跟搜尋設計是對的：/order-management/status/ready-to-ship
        但以下幾頁篩選列變成獨立列很怪 要跟搜尋整合才對啊：
        /order-management/status/receiving、/pending-payment、/new-orders、/out-of-stock
        請你 SSOT 修改」
     ②「你全站要把 頁籤列 動作列 篩選列 ssot做好 不要把儲存混到頁籤列去」
        （/store-setting/store/modify/base-setting/1）
     ③「右邊更新列應該可以跟左邊的篩選列同行 全站 ssot 都如此」
        「避免只有一個更新時間就佔了一整列」（/tutorial/packaging-machine）

   ═══ 一、先量清楚「對的那一版」長什麼樣（基準：ready-to-ship，1440 亮色）═══
     [flow-stepper]                 y=66    h=36   ← 流程步驟條（不在本節範圍）
     [動作列 .page-header]          y=114   h=32   ← 標題(→頂bar) + 計數(→頂bar) + 動作鈕
     [篩選列 .card-header]          y=161.39 h=65  ← 內含一條 flex 列 y=177.39 h=32、51 個控制項
                                                     （通路/物流/期限/面單/排序 + 搜尋框 + 搜尋鈕）
     沒有第三條橫列。**篩選與搜尋在同一條 flex 列裡**，這就是業主說「設計是對的」的那件事。

   ═══ 二、那四頁為什麼不一樣（根因）═══
   不是「兩種設計」，是**同一個東西被放在兩個不同的層**：
     · ready-to-ship 的篩選長在 .card-header 那條 flex 列**裡面**；
     · 那四頁的 controller 沒有那些篩選條件，它們唯一的篩選 affordance 是
       Order/_partials/list_tabs 輸出的那顆「全部」chip，而該 include 的位置是
       `<x-page-header>` 與 `<div class="card">` **之間** —— 是卡片的兄弟，不是卡片的內容。
   實測修前：那顆 chip 自成一列（y=160.39 h=36，整列只有 63.66px 寬的一顆），
   搜尋在下面一列（.card-header y=213.39）。兩列都半空，而且「篩選」與「搜尋」被拆開。
   ⇒ 修法是把那顆 chip 換一層放（放進篩選列），**不是把它刪掉**。

   ═══ 三、更新時間為什麼會獨佔一整列 ═══
   實測 /tutorial/packaging-machine 修前：`_shell` 把 h1 與計數 pill 搬進頂 bar 後，
   內容區的 .page-header 只剩「更新：02-05 16:14」一顆（y=66 h=24），
   真正的篩選列（3 顆分類頁籤）在下面一列 y=104 —— 正是業主說的
   「只有一個更新時間就佔了一整列」。

   ═══ 四、做法（DOM 在 Blade，位置在這裡）═══
   Blade 端（渲染順序天然成立：頁首一定先渲染、篩選列一定後渲染）：
     · components/page-header.blade.php  把 source 等參數寄存到 request attributes，
       原位那一份**照樣輸出**並標 data-fb-freshness-origin；
     · components/page-tabs.blade.php    pull-once 領走並輸出 data-fb-freshness-relocated；
     · Order/_partials/list_tabs         不再自己佔一列，把 $tabs 寄存給篩選列；
     · Order/_partials/list_search       在篩選列裡呼叫 <x-page-tabs :inline="true">，
                                         一次把「篩選頁籤」與「更新時間」都接進同一條 flex 列。
   CSS 端（本節）只做三件事：位置排序、收掉被接手的舊那份、把頁籤趕出動作列。

   ★★為什麼原位那一份是「有人接手才收掉」而不是「一律不輸出」：
     頁首渲染的當下還不知道這一頁後面有沒有篩選列。賭有人接手 → 一旦沒人接手
     就是**更新時間整個消失**（弄丟資訊）。下面那條 :has() 是**正向**判斷：
     只有頁面上真的出現 [data-fb-freshness-relocated] 才收掉原位那份；
     沒接手就維持修前現狀，一個字都不會少。
     ★瀏覽器不支援 :has() 時的退化行為是「同一個時間出現兩次」，不是「消失」——
       退化方向刻意選在安全的那一側。

   ═══ 五、實測（走 https://test.2025-seller.mtest，7 頁 × 亮/暗 × 1440/375）═══
   數值見本次回報的量測表。 */

/* ── (1) 篩選列容器：由 <x-page-tabs> 在「自己就是那一列」時生出來 ──
   （長在 .card-header 裡的那些是用呼叫端現成的 d-flex 列，不會走到這裡） */
.fb-filter-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .5rem;
}

/* ── (2) 篩選列上的排序：頁籤永遠最前、更新時間永遠最右 ──
   ★用 order 而不是靠 DOM 順序：ready-to-ship 把 list_search 放在整列**最後**
     （5 顆篩選下拉之後），若靠 DOM 順序，那顆「全部」chip 會跑到下拉後面。
   ★整列只有更新時間一個 auto 邊界 —— 兩個 auto 會讓 flex 把剩餘空間平均分，
     那是 §S-15 / §S-41 記過的「動作鈕浮在畫面正中央」的病根。 */
.fb-filter-tabs { order: -1; }
.fb-filter-freshness {
    order: 98;
    margin-left: auto;
    flex-shrink: 0;
}

/* ── (3) 更新時間已被篩選列接手時，收掉頁首那一份 ──
   兩條分別對應：篩選列是頁首後面某個兄弟的**子孫**（卡片裡的 .card-header），
   以及篩選列**就是**頁首後面的兄弟本身。 */
.page-header:has(~ * [data-fb-freshness-relocated]) > [data-fb-freshness-origin],
.page-header:has(~ [data-fb-freshness-relocated]) > [data-fb-freshness-origin] {
    display: none;
}

/* ── (4) 「不要把儲存混到頁籤列去」──
   /store-setting/store/modify/base-setting/1 的頁籤是寫在 <x-page-header> 的 inline slot 裡
   （StoreSetting/Store/_edit_tabs.blade.php），所以「返回 + 頁籤 + 儲存 + 更新時間」全擠在
   同一條 .page-header（實測修前：頁籤 x=252~587、儲存 x=595、更新時間 x=1295，y 全部 =66）。
   ★這裡用「換行」而不是「搬 DOM」：那支 blade 不在本輪的檔案所有權內。
     .page-header 本來就是 d-flex **flex-wrap**，插一個 flex-basis:100% 的斷點即可分行，
     不需要任何魔術數字，字級／按鈕高度變了也不會歪。
   ★只在「頁籤與動作鈕同時存在」時才斷行 —— 只有頁籤沒有動作鈕的頁首
     （hr/user/_head 等）本來就沒有混到東西，不該被動到。
   ★order 由既有規則決定：返回 0 / 動作組 1（§S-41）→ 第一列；
     斷點 50 之後是頁籤 60 與更新時間 98（§S-41）→ 第二列。
     結果＝第一列是動作列、第二列是頁籤列（右端帶更新時間），三列語意各就各位。
   ★結構上更乾淨的解法是給 x-page-header 開一個專屬的 tabs slot，讓頁籤自己成一列；
     那要改 _edit_tabs.blade.php（不在所有權內），已寫進本次回報。 */
.page-header:has(> .fb-ph-inline > .fb-tabs):has(> .fb-ph-actions)::after {
    content: '';
    flex-basis: 100%;
    height: 0;
    order: 50;
}
.page-header:has(> .fb-ph-actions) > .fb-ph-inline > .fb-tabs {
    order: 60;
}


/* --- S-54. 收攏兩處暫放在頁面層的規則（2026-08-06）---------------------------
   這兩塊原本寫在頁面的 <style> 裡，不是因為它們該待在那裡，
   而是當時 shadcn-theme.css 由另一支平行代理獨佔、不能動（同檔衝突守則）。
   現在主題檔空出來了，照它們真正的歸屬搬進來。
   ★原文逐字保留，只是換了位置；頁面端留下註解說明搬到哪裡，沒有刪除任何內容。

   ① §S-22「已選取的列」外溢到表單型表格 —— **全站問題不是單頁**：
      實證 /pick/packageAction/9201/edit 的「狀態」列因 is_enabled 有勾而整列灰底，
      任何 .fb-form-table 頁只要有勾選框被勾就會中（全站 11 支）。
   ② 商店/串接挑選卡的品牌 wordmark 幾何與 hover。
   ------------------------------------------------------------------------- */

/* ── ① 來源：resources/views/Pick/desktop/edit.blade.php ── */
/* ── §S-22「已選取的列」外溢到表單型表格
           主題 L8503 `.app-content tbody tr:has(input[type="checkbox"]:checked) > *`
           是列表頁的語彙：勾選框＝選取這一列，所以上 --accent/.6 底。
           但表單型表格的勾選框是「欄位的值」不是「選取列」，
           結果「揀貨動作／包貨動作／檢核動作／狀態」四列只要有勾就整列變灰
           （實測亮色 rgba(244,244,245,.6)、暗色同規則翻成深灰），
           業主看到的就是「檢核動作那列莫名的灰底」。
           ★這裡只針對 .fb-form-table 關掉，列表頁的選取提示完全不受影響。 */
        .app-content table.fb-form-table > tbody > tr:has(input[type="checkbox"]:checked) > *,
        .app-content table.fb-form-table > tbody > tr:has(input[type="checkbox"]:checked):hover > * {
            background-color: transparent;
        }

/* ── ② 來源：resources/views/StoreSetting/_picker_card.blade.php ── */
/* ★★★【2026-08-06 §S-55 ⓪ 修復：把下面這段「掉在註解外」的散文包回註解裡】
   ── 原本壞在哪（實測，不是推論）──
   從 blade 搬過來的時候，`{{-- … --}}` 的開頭掉了，於是從「而不是寫進…」到 `<style>`
   這 7 行**直接躺在 CSS 裡、不在任何註解內**。CSS 解析器的錯誤回復規則是：
   把看不懂的東西當成「選擇器前綴」，一路吃到下一個 `{`，然後**連那整個宣告區塊一起丟掉**。
   ⇒ 被吃掉的正是緊接在後的 `.sc-brand-mark { … }` 那一整條。

   ── 實測後果（1440，/store-setting/store/create 與 /store-setting/api-platform/create）──
   用 CSSOM 直接問「有哪些 selector 含 sc-brand-mark」，答案只有 `.sc-brand-mark--neutral`
   一條 —— `.sc-brand-mark` **從來沒有進到樣式表**。量到的 computed：
       display        block      （應為 inline-flex → 磚塊被拉成整行寬，不是 chip）
       padding        0px        （應為 .375rem .75rem）
       border-radius  0px        （應為 var(--radius)＝10px，變成直角）
       box-shadow     none       （應為 inset 0 0 0 1px --foreground/.18
                                   ⇒ §S-54 ② 花整段解釋的「暗色磚沒有輪廓」那個修法**根本沒生效**）
       font-weight    600        （應為 700）
       color          亮色量到 rgb(9,9,11)（應為 --dark-panel-foreground 近白）
   ★最嚴重的是 color：/store-setting/api-platform/create 的酷澎磚實測是
     底 rgb(75,83,192) ＋ 字 rgb(9,9,11) ＝ **3.09:1**（20px/600 勉強擦過 AA large 的 3.0），
     而原本設計的近白字是 6.37:1。等於整組品牌磚一直在用「深藍底＋近黑字」在跑。

   ── 這次怎麼修 ──
   **一個字都沒有刪**：只在原文前後補上 `/*` 與 `*​/`，把它變回它本來就該是的註解。
   原文逐字保留在下方。修好之後 `.sc-brand-mark` 就會正常進到樣式表。
   ★同一支掃描（scratchpad/css-stray-prose.sh，已用誘餌雙向實測過會紅）
     對全檔 11,421 行掃過，**註解外的中文散文只有這一處**，沒有第二個地雷。
   ── 以下為原文（逐字保留）────────────────────────────────────────── */
/*
而不是寫進 public/assets/css/shadcn-theme.css：
         主題檔本輪由另一支代理獨佔，禁止改動。這裡只放**這張卡專屬**的三條規則
         （wordmark chip 的幾何、說明段的行高、卡片 hover 的浮起），
         全部用主題既有 token，沒有寫死任何顏色。
         這三條建議日後併入主題檔的元件層（見回報「需要主題層 CSS」清單）。
         @once 保證同一次 render 只輸出一份，8 張卡不會印 8 次。 --}}
    <style>
*/
        /* 文字 LOGO：品牌色底 ＋ 恆亮前景。圓角走 shadcn 的 --radius（lg 階）。
           ★允許換行（Yahoo購物中心 7 個字在 1200px 斷點的窄卡放不下一行），
             不用 nowrap＋ellipsis —— 品牌名被截成「Yahoo購物…」比換行難讀。 */
        .sc-brand-mark {
            display: inline-flex;
            align-items: center;
            min-width: 0;
            padding: .375rem .75rem;
            border-radius: var(--radius);
            color: hsl(var(--dark-panel-foreground));
            font-size: 1.25rem;                          /* 20px；配 700 粗體＝WCAG large text */
            font-weight: 700;
            line-height: 1.25;
            margin: 0;
            overflow-wrap: anywhere;
            /* ★★ 1px 內嵌細邊：解「深色品牌磚在暗色卡片上沒有輪廓」。
               實測 1440 暗色，磚底 vs 卡片底（--card #18181B）的對比：
                 SHOPLINE #000000 → 1.06、酷澎 #1C1A4E → 1.24、新竹物流 #003DA5 → 2.09、
                 MO店+ #2F457C → 2.14、Yahoo #720e9e → 2.15
               —— 磚上的白字仍然清楚（前景對比 8.86～20.12），但**磚的形狀**看不出來，
               一顆「品牌色 chip」退化成一行浮在卡片上的白字。
               （這是修前就有的：舊版 200px 大色塊在暗色下同樣沒有邊界，
                 見 before_store-create_1440_dark 的 SHOPLINE 與 酷澎。）
               用 `--foreground / .18`：暗色模式前景是近白 → 深磚長出一圈淡亮描邊；
               亮色模式前景是近黑 → 疊在本來就深的品牌磚上幾乎看不見，等於 no-op
               （亮色下磚 vs 卡片本來就有 3.02～20.12 的對比，不需要描邊）。
               用 inset box-shadow 不用 border：不改動盒子尺寸，不會讓 8 張卡各長 2px。 */
            box-shadow: inset 0 0 0 1px hsl(var(--foreground) / .18);
        }
        /* ★★ 中性磚（自訂商店／雲端小幫手）不可以用 --dark-panel 當底色。
           實測（1440 暗色）：`--dark-panel` 與 `--card` 在暗色模式是**同一個值**
           （240 5.9% 10% ＝ #18181B）⇒ 磚塊完全溶進卡片，畫面上只剩一行粗體字，
           旁邊六張品牌卡都有色塊、只有這兩張沒有。
           ★這是**修前就存在**的問題（舊版 200px 大色塊在暗色下同樣看不見，
             見 before_store-create_1440_dark 截圖），本次一併治掉。
           改用會翻轉的 `--dark` / `--dark-foreground`（主題 §Badge 的
           「主要/啟用狀態 ＝ shadcn default（近黑／暗色反轉為白）」就是這一組）：
             亮色 #18181B 底＋近白字、暗色 #FAFAFA 底＋近黑字，兩邊都與 --card 拉開，
             且兩邊對比都在 19:1 量級。
           ★`--dark-panel` 本身沒有錯，它是給「恆深底面板」用的；
             錯在把它當成「一般卡片上的中性底色」—— 那正是它不翻轉的代價。 */
        .sc-brand-mark--neutral {
            background-color: hsl(var(--dark));
            color: hsl(var(--dark-foreground));
        }
        /* 說明段：shadcn CardDescription ＝ text-sm ＋ muted-foreground。
           ★不設 height/overflow —— 原本 `.gallery .desc` 寫死 51px 會把長說明剪掉。 */
        .sc-picker-desc { line-height: 1.6; }
        /* 卡片 hover：整張卡是「可挑選的項目」，給一點浮起的回饋（shadcn 的 hover:shadow-md）。
           顏色不動，只動陰影與邊框色，暗色模式自動成立（--ring 有翻轉）。 */
        .sc-picker-card { transition: box-shadow .15s ease, border-color .15s ease; }
        .sc-picker-card:hover {
            border-color: hsl(var(--ring) / .25);
            box-shadow: 0 4px 12px -2px rgb(0 0 0 / .10), 0 2px 4px -2px rgb(0 0 0 / .06);
        }


/* --- S-55. 暗色 hover 的死角，與表單型表格的兩條收尾（2026-08-06）--------------
   ★這一行是給 tests/Feature/Theme/ThemeOverrideAnnotationsTest 認的「章節錨點」，
     寫法必須是 `/* --- S-55. …`（S- 前面不可以多 §，`═` 全形橫幅它也認不得）。

   本節三件事，彼此獨立：
     ⓪ （已在 §S-54 ② 原地修好，這裡只留索引）「掉在註解外的散文吃掉 .sc-brand-mark」
        —— 詳情寫在 §S-54 ② 的開頭，含實測 computed 與對比值。
     ① 暗色 hover 沒有底色變化：只補一顆新 token，**不動任何既有 token 的值**。
     ② .fb-form-table 的兩條收尾：值欄可換行、勾選項群組的 label 頂端對齊。
   ------------------------------------------------------------------------- */


/* ══ ① 暗色 hover 沒有底色變化 —— 只補一顆 --accent-on-muted ═════════════════

   ── 病根（讀 token 定義就能證，不必猜）──
   暗色的這五顆 token 是**逐字同一個值** `240 3.7% 15.9%`（＝ #27272A，zinc-800）：
       --secondary ／ --muted ／ --accent ／ --border ／ --input
   所以只要一個元件「靜止態的底是 --muted 或 --border、hover 態的底是 --accent」，
   暗色下 hover 前後就是**同一個顏色**，對比 1.00:1 —— 底色完全不動，只有文字變。
   §S-46 末段已經寫下這件事並註明「要修得動 token 定義（影響全站），另案處理」，
   本節就是那個另案。

   ── 影響範圍（1440 桌機逐頁量 computed，切 .dark 後等 450ms 才讀）──
   量法：讀元素靜止態 background-color → page.hover() → 等 300ms → 再讀一次，
         同時往上找第一個非透明祖先當「表面色」，三個值一起算對比。
   ★「hover 之後跟自己比」不夠：底是 transparent 的元件要拿 hover 色跟**表面色**比，
     否則會量出 20:1 這種假數字（transparent vs 任何顏色都是滿分）。兩種都算了。

     元件                                          亮色 hover 前→後      暗色 hover 前→後
     ------------------------------------------------------------------------------
     .flow-stepper a.flow-step:not(.is-current)    228 → 244（1.154）    39 → 39（**1.000**）
       取樣頁 /order-management/status/ready-to-ship（n=10，is-past 4／is-future 6）、
             /status/cancelling（n=1）、/status/returning（n=2）
     .pos .pos-menu .nav-link:not(.active)         244 → 244（**1.000**）39 → 39（**1.000**）
       取樣頁 /pos/machine（n=2）。這一顆**亮色也是死的**（見下方「本節不修的」）。
     .pm-stepper--chevron a.pm-step / .scr-stepper a.scr-step
       與 .flow-step 同一組配色（is-past 實心 --muted、is-future --muted/.55、
       hover --accent），暗色同樣是 39 → 39。本輪的測試租戶沒有打包機資料，
       **這兩個實作沒有頁面可以現場量**，是照 token 定義推出來的，據實標記。

   ── 不在影響範圍（量過、沒壞，所以不碰）──
     · .fb-tab:hover 只宣告 `color`，**從來就沒有 background** —— 亮暗都一樣，
       而且 upstream shadcn 的 TabsTrigger 本來就沒有 hover 底色。這不是暗色的缺陷，
       是設計本身。實測 /seller/plan、/notifications、/status/cancelling：
       亮暗 hover 前後 background 都是 rgba(0,0,0,0)。**不動**。
     · .pos-sidebar-nav .nav-link:hover 實測勝出的不是 --accent 而是 --background/.6
       （亮 rgba(250,250,250,.6)、暗 rgba(9,9,11,.6)）—— 疊在 --border 軌道上
       暗色合成 rgb(21,21,23) vs 軌道 rgb(39,39,42)，**有變化**（約 1.19:1）。不在本節。
     · .dropdown-item / .table-hover tr / .fb-row-action 這類 hover 也是 --accent，
       但它們的表面是 --popover / --card（暗色 #18181B），--accent #27272A 疊上去
       是 1.16:1，**看得見**。它們沒有病，別順手一起改。

   ── 做法：加一顆 --accent-on-muted，命名沿用本檔既有的 `-on-` 慣例 ──
   §S-31 有 --muted-foreground-on-muted、§S-46 有 --muted-foreground-on-border，
   意思都是「當它疊在某個底上時改用這顆」。這顆同理：
       **「hover 態疊在 --muted / --border 表面上」時，用它取代 --accent。**
   值：
       亮色 240 4.8% 95.9%  ← 與 --accent **逐字相同** ⇒ 亮色可證零變化
       暗色 240 5.3% 26.1%  ← #3F3F46 ＝ zinc-700，是 zinc 色階的下一階，
                              不是自己另調的第三種灰（§S-42 已記過「別再長出第三種灰」）
   為什麼暗色只能往「亮」走：暗色的深色端（--background zinc-950）已經是全頁最深，
   往下沒有空間；zinc-800 → zinc-700 是唯一還在色階上的一步。
   實測對比：#3F3F46 vs #27272A ＝ **1.52:1**（原本 1.00），
             --accent-foreground（近白 #FAFAFA）壓在 #3F3F46 上 ＝ 9.98:1，遠超 AA。

   ── 為什麼不是別的四種做法 ──
     ① 直接把暗色 --accent 調亮 → 那是改既有 token 的值，全站每一個 hover 都會動
        （dropdown / table-hover / 分頁 / 按鈕…），而它們本來就沒壞。風險完全不成比例。
     ② 給 hover 加描邊或陰影 → 暗色 --border ≡ --muted，描邊會把界線填平（§S-46 已證）。
     ③ 用 alpha 疊 → alpha 一定往後面那層拉，暗色後面是全頁最深，一律退步（§S-46b 已證）。
     ④ 只改 .flow-step 一個實作 → 三個 stepper 實作會分岔成兩種 hover，那是不一致不是設計。

   ── 亮色為什麼**不一起**修 .pos-menu 那顆（誠實交代，這是本節的已知缺口）──
   一顆 token 沒辦法同時服務兩種不同的靜止底：
       .flow-step 亮色靜止底是 --border(228)，現在 hover 到 --accent(244) 差 16，**是好的**；
       .pos-menu  亮色靜止底是 --muted(244)，hover 到 --accent(244) 差 0，**是死的**。
   把亮色的 --accent-on-muted 調深去救 .pos-menu，就會同時把 .flow-step 的亮色 hover 弄死。
   ⇒ 亮色維持與 --accent 逐字同值（＝零變化、§S-46 量到的亮色結果一個像素都不動），
     .pos-menu 的**亮色** hover 仍然是死的，列為待辦交回主題那邊決定
     （要修得給它自己一顆，或把 rail 底色改吃 --border，兩者都會動到 POS 版面，不在本輪範圍）。

   ── 覆蓋方式 ──
   下面三條的選擇器與既有那三條**逐字相同**、同樣帶 !important，靠**排在後面**取勝，
   既有規則原文一行未動。★:hover 這種狀態偽類不參與 ThemeOverrideAnnotationsTest 的
   串接判定（它明講會跳過帶狀態偽類的候選者），所以不需要、也不應該加覆蓋註解。
   ══════════════════════════════════════════════════════════════════════════ */
:root                     { --accent-on-muted: 240 4.8% 95.9%; }
html.dark                 { --accent-on-muted: 240 5.3% 26.1%; }
[data-bs-theme="dark"]    { --accent-on-muted: 240 5.3% 26.1%; }
[data-bs-theme="light"]   { --accent-on-muted: 240 4.8% 95.9%; }

/* 流程步驟條（§S-42 / §S-46 的 .flow-step）：靜止底是 --border，hover 改吃新 token */
.flow-stepper a.flow-step:hover:not(.is-current) {
    background-color: hsl(var(--accent-on-muted)) !important;
}
/* 打包機 / 篩單流程的另外兩個 chevron 實作（§步驟箭頭）：
   is-past 靜止底 --muted、is-future --muted/.55，hover 一律改吃新 token。
   ★這兩個實作本輪沒有頁面可現場量（測試租戶無資料），依 token 定義同步處理，
     讓三個實作維持同一種 hover —— 分岔比暫時不修更糟。 */
.pm-stepper--chevron a.pm-step:hover:not(.is-current),
.scr-stepper a.scr-step:hover:not(.is-current) {
    background-color: hsl(var(--accent-on-muted)) !important;
}
/* POS 左側 rail（§S-37）：rail 底是 --muted，hover 原本也是 --accent。
   ★這條既有規則**沒有** !important（在 §POS 段落裡），本條同特異度靠排在後面取勝，
     刻意也不加 !important —— 不要無故提高後人覆蓋它的門檻。
   ★只修暗色那一半：亮色新 token ≡ --accent，所以亮色的死 hover 維持原狀（理由見上）。 */
.pos .pos-menu .nav.nav-tabs .nav-item .nav-link:hover,
.pos .pos-menu .nav.nav-tabs .nav-item .nav-link:focus {
    background: hsl(var(--accent-on-muted));
}


/* ══ ② .fb-form-table 收尾兩條 ══════════════════════════════════════════════

   ── ②-1 值欄可換行：`white-space: normal` ──
   §31 有一條全域的 `.table td { white-space: nowrap }`（那是**資料表格**的規則：
   不壓縮欄寬，寬度交給 .table-responsive 橫向捲）。但表單型表格不是資料表格
   —— 它沒有「欄位對齊成矩陣」的需求，橫向捲軸在編輯頁只會是障礙。

   實證（/pick/packageMachine/create，這是目前唯一一支沒有在 blade 端補 .text-wrap 的）：
       1440：.table-responsive clientWidth 771 / scrollWidth 771  → 沒事
        768：734 / 734                                            → 沒事
       ★375：clientWidth **341** / scrollWidth **525** → 溢出 184px，長出橫向捲軸
   同一個病 /pick/desktop/1/edit 早就踩過，當時是在 blade 端逐頁補 `.text-wrap`
   （那支檔案裡有整段註解記錄「17 個勾選項讓 min-content 撐到 1808px、
     連 §S-24 的 `td:first-child{width:170px}` 都被 auto layout 丟掉，只剩 71.1px」）。
   ⇒ 收進主題層一條，8 支檔案 / 9 張表都不必再逐頁補。

   ★特異度刻意壓低：本條是 (0,2,4)，而 §S-24 的
     `…> td:first-child { white-space: nowrap }` 是 (0,3,4) ⇒ **label 欄仍然不換行**
     （手機 ≤575.98px 的 `td:first-child{white-space:normal}` 也是 (0,3,4)，同樣不受影響）。
   ★掛了 Bootstrap `.text-nowrap` 的 td 帶 !important，本條動不了它們 —— 這是好事，
     那些是呼叫端明確表達過「這格不要換行」的意思。

   ── 掃過 8 支 / 9 張表的結論：有沒有欄位依賴 nowrap ──
   逐格檢查「不是第一欄、且沒有 .text-nowrap」的 td，會真的從 nowrap 變 normal 的只有兩格，
   兩格都在 Pick/packageMachine/edit.blade.php：
     · L80 保固資訊「保固：YYYY-MM-DD ~ YYYY-MM-DD ＋ 徽章」—— 純文字，會變。
       風險低（短字串，只在 `~` 前後的空白處斷行），而且斷行**優於**目前 375px 的橫向捲軸。
       ★這格在本輪測不到真值：測試租戶沒有 packageMachine 資料，
         /pick/packageMachine/create 走的是 else 分支「保固：- ~ -」。據實標記。
     · L113 動作勾選項那格 —— 會變，而且**這正是要的**：
       它是 desktop/edit 早就用 .text-wrap 修過的同一個 bug，packageMachine 一直沒補。
   其餘約 60 格全部不受影響，原因是下列其中之一（都逐格對照過）：
     · 是 td:first-child（特異度較高的 §S-24 那條贏）
     · 明寫了 .text-nowrap（!important）或 .text-wrap（本來就 normal）
     · 內容是 <input>/<select>/<textarea> 這類取代元素 —— white-space 對它們無效
     · 文字包在 .btn（本檔 `.app-content .table .btn{white-space:nowrap}`）
       或 .input-group-text（Bootstrap 自帶 nowrap）裡，直接命中該元素，不吃祖先的繼承值

   ── ②-2 勾選項群組的 label 頂端對齊 ──
   §S-24 的頂端對齊條件是 `tr:has(textarea)` 與 `tr:has(.row)`，接不到「一整排勾選項」。
   desktop/edit 是在 blade 端手寫 `.align-top` 頂著的（檔內註解寫明「§S-24 接不到這裡」）；
   packageMachine/edit 沒補 ⇒ 那一列 label 是垂直置中的。
   ★這兩支的 markup **不一樣**，所以條件要寫兩種，不能只寫一種：
       desktop        : td > .d-flex.flex-wrap > `.form-check.form-switch` ＋ N×`.form-check`
                        ⇒ 相鄰 ⇒ `.form-check + .form-check` 接得到（N≥1）
       packageMachine : td > `.form-check-inline`（**外層沒有 .form-check**）
                                > `.form-check.form-switch`
                        ＋ N×`.form-check.form-check-inline`
                        ⇒ 開關那顆被多包了一層 ⇒ `.form-check + .form-check` 只在 N≥2 才中，
                          N=1 會整列漏掉 ⇒ 補一條 `.form-check-inline + .form-check-inline`
   實測 /pick/packageMachine/create「包貨動作」列：.form-check 5 個、
        `.form-check + .form-check` 命中 3、`.form-check-inline + .form-check-inline` 命中 4
        ⇒ 兩條都接得到這一列。
   ★刻意**不用** `tr:has(.form-check)`（只要有勾選框就頂端對齊）：
     「狀態」那種一列只有一顆開關的列會被誤傷 —— 單顆開關垂直置中才是對的。
     所以條件一定要是「≥2 顆」，這也是原提案用相鄰選擇器的理由。
   ★宣告內容與 §S-24 那條**逐字相同**（vertical-align: top; padding-top: .75rem），
     不是新的視覺語彙，只是把同一個意圖套到接不到的第三種 markup 上。
   ══════════════════════════════════════════════════════════════════════════ */
.app-content table.fb-form-table > tbody > tr > td {
    white-space: normal;
}
.app-content table.fb-form-table > tbody > tr:has(.form-check + .form-check) > td:first-child,
.app-content table.fb-form-table > tbody > tr:has(.form-check-inline + .form-check-inline) > td:first-child {
    vertical-align: top;
    padding-top: .75rem;
}


/* --- S-56. 開關打開＝語意綠（全站 SSOT）＋ 動作列第一顆鈕對齊卡片左緣（2026-08-06）---
     ★章節錨點格式：守門測試 ThemeOverrideAnnotationsTest 只認
       `/* --- S-56. …`（S- 前面不可以多 §，`═` 全形橫幅它也認不得）。

     業主 2026-08-06 兩條指令，兩件事都是「全站 SSOT」，所以都收在主題層一次解決：
       ①「這種開啟的按鈕 switch 打開的話要顯示綠底 開啟 如果是黑底會造成誤會
          這個也是 SSOT 全站都要改綠 green 請你參考 shadcn」（附 /pick/desktop/1/edit 截圖）
       ②「儲存按鈕退縮的問題 是 SSOT 都有此問題 請你對齊左邊 不要退縮」（選定方案 A：
          儲存鈕左緣對齊卡片左緣）

     ══════════════════════════════════════════════════════════════════════════
     ① Switch 打開＝綠
     ──────────────────────────────────────────────────────────────────────────
     ★★ 這裡**刻意偏離 shadcn 官方**，理由寫在這裡以免日後有人「照官方改回去」：
        shadcn 官方 Switch 的 checked 態是 `bg-primary`（跟著主題的主色走），
        本檔 §35 也是照這個精神轉換的 —— 只是本專案的 primary 映射到近黑
        （亮色 --dark = #18181B），所以官方寫法在這裡的實際結果是「打開＝近黑」。
        業主的反對意見是**語意**不是美感：近黑在這套 UI 裡到處都是「停用／深色／
        次要」的視覺語言，讀起來跟「已啟用」對不起來（原話：「如果是黑底會造成誤會」）。
        ⇒ 偏離的範圍嚴格限縮在 `.form-switch`：checkbox / radio 的 checked 仍然是 --dark，
          不動（那兩者的「勾了」靠勾勾／圓點本身表達，不需要語意色）。

     ★★ 為什麼用 --success-fg 而不是 --success（這一段是量出來的，不是挑順眼的）
        候選一 --success（142.1 70.6% 45.3% ＝ #22C55E，語意色的「正牌綠」）：
          · 它在 :root 與 html.dark **同值、不翻轉**。本檔慣例是「只用會翻轉的 token」
            當表面色（--dark-panel 那顆不翻轉的坑，:root 定義處已經記過一次）。
          · 白色圓鈕（#FAFAFA）疊在 #22C55E 上實測只有 **2.28:1** —— 連 WCAG 1.4.11
            非文字元件的 3:1 都過不了，更不用說業主要求的 AA 4.5。圓鈕會糊進綠底裡，
            使用者看不出開關撥到哪一邊。這跟 §35 當年不用金黃 primary 當勾選底
            （「金黃底 + 白勾勾對比只有 1.7:1，勾了跟沒勾一樣」）是**同一個失效模式**。
        候選二 --success-fg（採用）：
          · 它是本檔**既有**的 success token（不是我另調一顆綠，業主明確禁止另調），
            而且亮暗**各有一組值**（亮 142 65% 28% ＝深綠 #17763B／
            暗 142 60% 70% ＝亮綠 #85E0A6）—— 是會翻轉的 token。
          · 翻轉方向剛好和 §35 既有的圓鈕 SVG 一致：亮色是深底配 #FAFAFA 白圓鈕、
            暗色是淺底配 #18181B 深圓鈕。⇒ **圓鈕的兩支 SVG 一行都不用動**，
            本節只接手 background-color / border-color 兩個屬性。
          · 命名上它原本是「--success-bg 上面的字色」，這裡當實色底用算是延伸用途；
            但它就是本檔在亮/暗兩邊各自「夠深／夠亮的那顆綠」，語意仍然是 success，
            沒有引進第二套綠。真正的破口是「本檔沒有一顆會翻轉的 success **底色**」，
            要補的話應該補在 :root（會動到別的元件），本輪不動，據實記在這裡。
        實測對比（1440×1000，/pick/desktop/1/edit，數字見本輪回報）：
          亮 #FAFAFA 圓鈕 on #17763B 軌道 ＝ 5.46:1（AA 4.5 過）
          暗 #18181B 圓鈕 on #85E0A6 軌道 ＝ 11.18:1
          軌道 vs 卡片底：亮 5.70:1、暗 11.18:1（開關本身也看得清楚，不只圓鈕）

     ★ 只動 checked 一態，其餘四件事一行都不碰：
        · unchecked ＝「關閉」，本來就該中性 → §35 的 --muted-foreground/.45 原封不動
        · :hover（未勾）→ §35 的 /.6 原封不動
        · :focus ring → 走 --ring，與 checked 底色無關，本節不宣告
        · :disabled → §35 的 opacity:.5 原封不動（半透明綠，仍看得出是綠的；
          WCAG 對停用態不要求對比，據實不調）
     ══════════════════════════════════════════════════════════════════════════ */
.form-switch .form-check-input:checked {
    background-color: hsl(var(--success-fg));
    border-color: hsl(var(--success-fg));
}

/* --- S-56b. 動作列第一顆鈕退縮 8px 的真正來源 -------------------------------
     ★★ 先講結論：那 8px **不是** padding、不是 margin、不是按鈕自己的位移 ——
        是 flex 的 column-gap 被一顆「被搬空但還佔著位子」的包裝層吃掉了。

     追出來的完整鏈路（實測，不是推測）：
       1. 頁首元件 components/page-header.blade.php 的 before slot 輸出
          `<div class="flex-shrink-0">{{ $before }}</div>`，裡面放返回鈕。
          （JS 關掉抓原始 HTML 確認過：這個 div 只有一個 <a> 元素子節點，
            Blade 沒有留下任何空白文字節點。）
       2. includes/_shell.blade.php 把返回鈕 `slot.appendChild(backBtn)` **搬進頂 bar**，
          同一段 JS 再把 .page-header 加上 .fb-action-row。
       3. 搬完之後那個 wrapper 變成 `<div class="flex-shrink-0"></div>` ——
          寬 0、看不見，但它**仍然是 flex item**，而且 order:0 排在最前面。
       4. 動作列的 gap 是 .5rem（view 端的 `gap-2`，實測 column-gap: 8px）。
          flex 的 gap 只看「相鄰兩個 item 之間」，**不管前一個 item 寬不寬**
          ⇒ 第一顆真正的按鈕從 244 被推到 244 + 0 + 8 = **252**。
     ⇒ 8px ＝ 一格 gap。改 gap 或用負 margin 都是治標（而且會連帶動到按鈕之間的間距）；
       治本是讓那顆「已經沒有內容」的包裝層不要再佔一個 flex 位子。

     ★ 全站掃描佐證（113 條 GET 路由 × 1440 寬，逐頁量「第一顆鈕左緣 − 卡片左緣」）：
       Δ=8 的頁面**一頁不漏**都是「動作列第一個子項 ＝ 空的 .flex-shrink-0」，
       特徵完全一致（width 0／element children 0／:empty true／textContent 0）；
       Δ=0 的頁面第一個子項是 .fb-ph-inline —— 而 .fb-ph-inline 早就被
       `.page-header > .fb-ph-inline { display: contents }` 排除在 flex item 之外。
       ⇒ 本節等於把那條既有規則的同一個意圖，補到它接不到的第二種包裝層上，
         不是新發明一套規則。

     ★ 為什麼用 `:empty` 而不是 `:not(:has(*))`：
       `:not(:has(*))` 連「只有文字、沒有元素」的 div 也會中，那種 div 是有寬度的，
       藏掉就是把內容弄不見。`:empty` 連文字節點都不能有 ⇒ 命中的元素**必定什麼都不渲染**，
       把它從 flex flow 拿掉只可能少掉一格 gap，不可能少掉任何看得見的東西。
       （代價：萬一日後 Blade 在 slot 裡留下空白文字節點，`:empty` 就不中、
         這條會靜默失效退回 252。本輪 JS-off 抓原始 HTML 確認目前 0 個這種情形，
         據實記在這裡；真要根治應該由元件端在 slot 為空時不輸出 wrapper。）

     ★ 為什麼用 `display: none` 而不是 `display: contents`：
       memory 的 feedback_display_contents_breaks_geometry_js 記過
       「display:contents ⇒ offsetParent 恆 null ⇒ 舊 JS 把 UI 藏掉」。
       _shell 的空列判定 hasLeft() 正好會讀 offsetParent。實際比對過三種狀態：
         現況（display:block、0×0）→ 走到 `box.width<1 && box.height<1 && !textContent`
                                      這條 → return false
         display:none              → `offsetParent === null` 且不是 FORM/.btn/A → return false
       兩者**結論相同**（都不算「這一列還有東西」），所以這條不會讓任何一列被誤收掉；
       display:contents 走的是第三條路（要靠 class 白名單特判），沒有必要冒那個險。

     ★ 只調對齊：不碰按鈕的尺寸、顏色、圓角，也不碰 .fb-action-row 的 gap
       （gap 仍然是 8px，按鈕與按鈕之間的距離一格都沒變）。
     ---------------------------------------------------------------------- */
.fb-action-row > div:empty {
    display: none;
}

/* --- S-57. POS 側欄頁籤併入 .fb-tabs SSOT（尺寸變體）＋ rail 亮色 hover（2026-08-06）---
   ★這一行是給 tests/Feature/Theme/ThemeOverrideAnnotationsTest 認的「章節錨點」，
     寫法必須是 `/* --- S-57. …`（S- 前面不可以多 §，`═` 全形橫幅它認不得，見 §S-46）。

   本節兩件事，彼此獨立：
     ① POS 側欄那組「今天紀錄(0) ／ 查詢包裹」併入全站頁籤 SSOT（.fb-tabs / .fb-tab），
        只保留一個**尺寸**變體 .fb-tabs--touch；配色一顆都不自己調。
     ② .pos-menu 左側 rail 的**亮色** hover（§S-55 交回來的已知缺口）。
   ------------------------------------------------------------------------- */


/* ══ ① POS 側欄頁籤 → .fb-tabs SSOT，尺寸變體 .fb-tabs--touch ═══════════════

   ── 為什麼要併：現況是「同一個語彙的第二份實作」──
   §S-32 把 POS 側欄那兩顆做成 shadcn 分段控制器、§S-46b 又把它的軌道底跟著
   §S-46／§S-47 壓深一階 —— 也就是說，**它一路跟著 .fb-tabs 走了三個章節，
   卻始終是另一份 CSS**。三份章節（§S-32／§S-46b／§S-47）在講同一件事，
   任何一邊之後再調就會分岔。這正是本檔一直在收的那種帳（§S-53 收三列、
   §S-56 收開關綠底），不是為了統一而統一。

   ── 併之前先量：配色**早就一致**，真正的缺口只有兩條髮絲線 ──
   （量測走 https://test.2025-seller.mtest/pos/machine 與 /pos/package，
     1440／375 兩種寬、亮暗各一輪，切 .dark 後等 450ms 才讀 computed）
       改前（§S-32 + §S-46b）              .fb-tabs SSOT（§13+§S-47+§S-49+§S-52）
       軌道底  亮 rgb(228,228,231)          亮 rgb(228,228,231)   ← 逐位元組相同
               暗 rgb(39,39,42)             暗 rgb(39,39,42)      ← 逐位元組相同
       選中片  亮 rgb(250,250,250)          亮 rgb(250,250,250)   ← 逐位元組相同
               暗 rgb(9,9,11)               暗 rgb(9,9,11)        ← 逐位元組相同
       軌道外框  **無**（border 0）          1px --muted-foreground/.28  ← 缺口 ①
       選中片邊  **無**（border-width 0）    1px --muted-foreground/.22  ← 缺口 ②
   ⇒ 併進來拿到的就是那兩條髮絲線（§S-49 業主指名要的「頁籤外的灰色淺框」與
     「切換的感覺」）—— POS 是全站唯一還沒拿到的地方。

   ── ★★觸控目標「只增不減」是硬需求，所以只能是變體，不能直接換 ──
   §S-32 白紙黑字寫過為什麼它不照抄 shadcn 的原生尺寸：
     shadcn TabsList 是軌道 h-9(36px)、trigger h-7(28px)；POS 是**倉庫現場天天在用**的
     掃碼／揀貨／包裝機台，min-height:44px 是 Apple HIG 44pt／WCAG 2.5.8 的觸控需求，
     不是美觀選擇。直接換成 .fb-tabs ⇒ 觸控目標 45px → 25px（本檔 §S-52 把溝槽
     3px→4px 之後 chip 只有 25px），**砍掉將近一半**。
   ⇒ .fb-tabs--touch 只宣告「尺寸」四件事（撐滿寬、可換行高度、min-height、內距），
     background／color／border／box-shadow／border-radius **一個字都不寫** ——
     配色與圓角全部回歸 §13 / §S-47 / §S-49 / §S-52，這樣日後那幾節再調，POS 自動跟上。
     這是本節存在的**唯一**理由：如果變體也管配色，就等於把剛收掉的分岔換個名字再開一次。

   ── 逐條為什麼是這個值（每一條都對得上改前的實測值）──
   · `display: flex; width: 100%`
       §13 是 `inline-flex; width: fit-content`（頁籤依內容寬、靠左）。POS 側欄那兩顆
       改前是 `.nav-fill` 撐滿 312px 的側欄寬 —— 撐滿是這裡的既有設計（側欄寬度固定，
       兩顆平分才不會右邊留一塊空軌道），不是本節新加的。
   · `height: auto`
       §13 寫死 `height: 2.25rem`(36px)。不放開的話 min-height:44px 撐不出來
       （父容器 36px、子片 44px 會溢出軌道）。
   · `flex: 1 1 auto`（★不是 `flex: 1`）
       ★★這一條是我**推翻前一版提案**的地方，而且是量出來的不是讀出來的：
         Bootstrap 的 `.nav-fill > *` 是 `flex: 1 1 auto`（依內容寬按比例分剩餘空間），
         `.nav-justified` 才是 `flex-basis: 0`（等寬）。
         改前實測兩顆是 **159.95px / 140.05px（不等寬）**；寫 `flex: 1`
         ＝ `flex: 1 1 0%` 會變成 156/156 等寬 —— 那是**版面變更**，不是尺寸保全。
         「今天紀錄(0)」比「查詢包裹」長 3 個字，本來就該寬一點。
       ⇒ 寫 `1 1 auto` 才是「改前那個版面原封不動」。
   · `min-height: 44px; padding: 12px 15px`
       兩個值都是 §S-32 的原值，逐字沿用，不重新發明。
       實測結果：45px → **47px**（§13 的 `border: 1px solid transparent` 讓 border-box
       多 2px）—— 觸控目標**變大不變小**，符合 §S-32 的硬需求。
   · 首尾片補回 `padding-left: 15px`
       §S-52 有一條 `.fb-tabs:not(.fb-tabs--flow) > .fb-tab:first-child, :last-child
       { padding-left: .75rem }`，特異度 (0,4,0) 壓過本節的 (0,3,0)。
       這裡只有兩顆頁籤 ⇒ 兩顆都同時是 first 與 last，不補的話會變成
       左 12px／右 15px 的不對稱（片是置中對齊，肉眼幾乎看不出，但那是**沒必要的差異**）。
       ⇒ 用同分 (0,4,0)、排在後面的一條把 15px 寫回來，改前改後的內距逐字相同。
   · `.active { font-weight: 600 }`
       §13 的 .fb-tab 是 500，改前的 §S-32 選中態是 600。這是本節**唯一**一條
       不屬於「尺寸」的宣告，保留的理由：POS 是遠距離掃一眼的現場機台，
       而且拿掉它等於在「併 SSOT」這件事上順手多改一處業主沒要求的視覺（字重變輕）。
       ⇒ 維持改前的 600，據實記在這裡；它只作用在 --touch 變體，不影響其他 33 個呼叫端。

   ── 併進來之後**跟著變**的三件事（量過並接受，不是沒注意到）──
     ⑴ 片與片之間的 4px 間隙沒了（§S-32 有 `gap: 4px`，§13 的 .fb-tabs 沒有 gap）。
        視覺影響趨近於零：未選中片是 **transparent**（直接露出軌道），
        所以「間隙」跟「未選中片」本來就是同一個顏色，看得出來的只有選中片寬 +2px。
        ★不補 gap 是刻意的：補了就又是一條「只有 POS 有」的宣告，回到分岔。
     ⑵ 選中片圓角 6px → 8px（§S-32 是 `calc(--radius - 4px)`，§13 是 --radius-md）。
        8px 正是全站其他 33 個呼叫端的值 —— 這是「併 SSOT」四個字的實際內容。
     ⑶ 未選中字色 亮 rgb(96,96,103) → rgb(97,97,99)、暗 rgb(161,161,170) → rgb(166,166,167)。
        改前吃 §S-46b 的 --muted-foreground-on-border（為 rgb(228,228,231) 底調的固定值），
        併回 §13 之後吃 `hsl(var(--foreground) / .6)`（半透明，會跟著底色重新合成）。
        §S-47 已經把這兩顆的對比量到底並選了後者：亮 4.91:1 → **4.87:1**（AA 4.5 仍過）、
        暗 6.12:1 不變。差 0.04 卻換到「hover 時漸變到滿版 --foreground」那條關係，
        §S-47 的原話是「硬改成 on-border 不划算」—— 這裡照它的結論走，不另開第二種答案。
     ★軌道總高 52px → 57px（片 47 + 溝槽 4×2 + 外框 1×2）。側欄本體是
       `flex: 1` 的捲動區，量過 `document.scrollWidth == clientWidth`（1440／375 皆是），
       沒有橫向溢出，也沒有把任何內容擠出畫面。

   ── markup 怎麼改、以及「行為零改動」是怎麼證的 ──
   六支 blade 裡本節只動**有路由的兩支**（pos/machine.blade.php、pos/package.blade.php）：
       <ul class="nav nav-tabs nav-fill"><li class="nav-item"><a class="nav-link …">
     → <div class="fb-tabs fb-tabs--touch" role="tablist"><a class="fb-tab …" role="tab">
   文字、href、data-bs-toggle、data-bs-target、id="pos-table-title" **一個字都沒動**。
   ★Bootstrap 5.3.2 的 Tab 元件原生支援這個結構，而且是**讀原始碼＋實際點擊**兩邊都確認過：
       SELECTOR_TAB_PANEL = '.list-group, .nav, [role="tablist"]'   ← 所以 role="tablist" 是必要的
       SELECTOR_INNER 含 '[role="tab"]'、SELECTOR_INNER_ELEM 併入 '[data-bs-toggle="tab"]'
       _getOuterElement() = closest('.nav-item, .list-group-item') || 自己  ← 沒有 li 也不會壞
   ★`role="tab"` **不是新增的行為**：Bootstrap 的 `_setInitialAttributesOnChild()` 本來就會
     `_setAttributeIfNotExists(child, 'role', 'tab')`。改前實測 DOM 上就已經有
     role="tab" / aria-selected / tabindex="-1" 了（見量測 JSON），寫進 markup 只是把
     JS 事後補的東西提前寫死，鍵盤方向鍵切換改前改後都存在。
   ★JS 相依性掃過：兩支 blade 的 posTableTitle 只認 `#pos-table-title`（id 保留），
     `_package.blade.php` 的 `$('#pos-table-title').html(…)` 同理；
     全站 JS（含 public/assets）對 `.pos-sidebar-nav` / `.nav-item` / `.nav-tabs` 的
     引用數 **0**（grep 過，不是推論）。

   ── 沒有路由的四支孤兒 view 不動（machine_backup / darka / checkout / _package）──
   §S-32 當初刻意「只改主題、六支 blade 一個字都不用改」，正是因為那四支是孤兒。
   本節改 markup ⇒ **只改跑得到的那兩支**，孤兒留在原地吃 §S-32／§S-46b 的老規則
   （那些規則一行都沒刪，見下面的死碼清單）—— 改看不見的頁面只會多兩個沒人驗的檔。

   ── ★併進來之後變成死碼的 POS 專用區塊（只標註，一行都不刪）──
   下列區塊在 /pos/machine 與 /pos/package 上**選擇器 0 命中**（querySelectorAll 數過）：
     · §S-32 `.pos .pos-sidebar .pos-sidebar-nav .nav.nav-tabs …` 的三條片規則
       （.nav-item .nav-link 本體／:hover／.active）—— 軌道那條 `.nav.nav-tabs` 亦然。
       ★但 `.pos .pos-sidebar .pos-sidebar-nav { padding: 8px; background: --card }`
         **仍然生效**（它咬的是外層包裝 div，markup 沒動它），不是死碼。
     · §S-46b 的兩條（軌道底 --border／未選中字 --muted-foreground-on-border）。
     · §POS-2 區段裡 30 行「★已被 §S-32 覆蓋（實測生效值：…）」的註解所標記的那些宣告
       （padding／border-bottom／min-height／border-radius／background／color／font-weight…）。
       ★那些註解**不改**：它們陳述的是「同一條 selector 在串接下誰贏」，
         這件事沒有變（§S-32 仍然排在後面、仍然勝出），只是那個 selector 現在
         在有路由的兩支頁面上咬不到元素。改寫它們反而會讓 ThemeOverrideAnnotationsTest
         判到相反的答案 —— 註解說的是串接，不是「畫面上有沒有這顆元素」。
   ⇒ 這些區塊仍然服務那四支孤兒 view（它們的 markup 沒動），所以**它們不是純死碼**，
     是「只剩孤兒在用」。真要刪，該連孤兒 view 一起處理，那是行為變更，不在本輪。

   ── 走過的死路 ──
   ① **直接把 .fb-tabs 換上去、不做變體**：觸控 45px → 25px。§S-32 已經把這件事
      寫成硬需求，違反它等於為了 CSS 乾淨去換現場作業的可用性。第一時間就否決。
   ② **改 §13 的 .fb-tab 尺寸讓全站都變大**：33 個呼叫端全部跟著長高，
      為了兩顆 POS 頁籤動全站，風險完全不成比例（§S-55 對「調既有 token」的同一條理由）。
   ③ **markup 保留 `class="nav"` 讓 Bootstrap 靠 closest('.nav') 找 parent**：
      能動，但 `.nav` 會帶進 Bootstrap 一整組 --bs-nav-link-* 變數與 `display:flex`，
      等於在 SSOT 元件上再疊一層無關的宣告。`role="tablist"` 是 Bootstrap 原生就吃的，
      而且它本來就會自己補上去 —— 用它更誠實。
   ④ **順手把四支孤兒 view 一起換掉**：改了也沒有頁面可以驗（無路由），
      違反「驗不到的不要動」。留在原地。

   ── 實測（走 https://test.2025-seller.mtest，兩支頁面 × 亮暗 × 1440/375，
        逐一實際點擊每一顆頁籤、每次等 500ms 讓 .fade 跑完再讀）──
   數值全表見本次回報；本節要釘住的四件事：
     ⑴ 觸控目標 45px → 47.x px（**只增不減**，兩支頁面八種組合皆同）；
     ⑵ 軌道底／選中片底 亮暗四個值與改前**逐位元組相同**；
     ⑶ 逐顆點擊後 .tab-pane 的 show/active **互斥正確**、內容真的換、
        `#pos-table-title` 的計數器改寫（今天紀錄(n)）仍然正常；
     ⑷ JS errors 0、`document.scrollWidth == clientWidth`（1440／375 皆無橫向溢出）。
   ★上線順序（硬要求）：本節 CSS 先落地，markup 才跟上。顛倒的話中間那一段時間
     POS 會以 25px 的觸控目標出貨 —— 本輪兩者在同一次工作內完成，沒有中間態。
   ══════════════════════════════════════════════════════════════════════════ */
.fb-tabs.fb-tabs--touch {
    display: flex;                       /* §13 是 inline-flex，這裡要撐滿側欄 */
    width: 100%;                         /* §13 是 fit-content */
    height: auto;                        /* §13 寫死 2.25rem，會壓住 min-height:44px */
}
.fb-tabs.fb-tabs--touch > .fb-tab {
    flex: 1 1 auto;                      /* ＝ Bootstrap .nav-fill 的分配方式（不是 flex:1） */
    height: auto;                        /* §13 是 calc(100% - 1px) */
    min-height: 44px;                    /* ★觸控需求，沿用 §S-32 原值 */
    padding: 12px 15px;                  /* 沿用 §S-32 原值 */
}
/* 只有兩顆頁籤 ⇒ 兩顆都同時是 first 與 last，會被 §S-52 那條 (0,4,0) 的
   `padding-left: .75rem` 壓成左 12／右 15 的不對稱。同分靠排在後面補回來。 */
.fb-tabs.fb-tabs--touch > .fb-tab:first-child,
.fb-tabs.fb-tabs--touch > .fb-tab:last-child {
    padding-left: 15px;
}
/* 本節唯一一條非尺寸宣告：維持 §S-32 的選中字重（理由見上） */
.fb-tabs.fb-tabs--touch > .fb-tab.active {
    font-weight: 600;
}


/* ══ ② .pos-menu 左側 rail 的**亮色** hover —— §S-55 交回來的已知缺口 ══════

   ── §S-55 把話講清楚了，這裡只補它明講「不在本輪範圍」的那一半 ──
   §S-55 的原話：「.pos-menu 的**亮色** hover 仍然是死的，列為待辦交回主題那邊決定
   （要修得給它自己一顆，或把 rail 底色改吃 --border，兩者都會動到 POS 版面）」。
   ★本輪先量過現況才動手（1440 桌機，量真元素不 clone —— clone 會把 hover/focus 態
     讀成 none，量出來的是假數字）：
       .pos .pos-menu .nav-link:not(.active)
         亮 靜止 rgb(244,244,245)（元素自己是 transparent，**露出 rail 的 --muted**）
            → hover rgb(244,244,245)   ＝ 對比 **1.000，畫面上完全沒有變化**
         暗 靜止 rgb(39,39,42) → hover rgb(63,63,70)   ＝ §S-55 已經修好，1.52:1
   ⇒ 亮色是死的這件事**是量出來的**，不是照抄前一輪的結論。

   ── 為什麼是「給它自己一顆」而不是「把 rail 底改吃 --border」──
   兩條路 §S-55 都列了，這裡是選擇的理由（★被否決的那一條也記下來）：
     ✗ **rail 底 --muted → --border**：那會把整條 160px rail 的底色換掉，
       而 §S-37（9331）白紙黑字寫著「rail 底色**維持 --muted**」，理由是
       blade 上的 `.bg-default` 是 `background-color: … !important`（4505）改不動，
       且 4504 載明 rail 要有底色才不會與內容區糊在一起。更硬的是
       §S-46b 已經量過並明講「rail 亮 rgb(244,244,245)／暗 rgb(39,39,42)…OS 要的
       淺底已經成立」—— 那是**量測後刻意保留**的現況，不是漏修。
       為了一顆 hover 去翻掉一整條 rail 的底色，動的版面遠大於解決的問題。
     ✓ **給它自己一顆 token**：只影響 hover 這一個狀態、只咬 .pos-menu 一組選擇器，
       版面零位移、靜止態一個像素都不動。

   ── 新 token 的值，以及**命名的坑**（誠實交代）──
       --accent-on-muted-fill:  亮 240 5.9% 90%   ← ＝ --border 的值，比 --muted 深一階
                                暗 240 5.3% 26.1% ← ★與 §S-55 的 --accent-on-muted **逐字相同**
   ★暗色刻意取同值 ⇒ **暗色可證零變化**（rail 暗色 hover 仍是 rgb(63,63,70)，
     §S-55 剛量好的 1.52:1 一個像素都不動）。本節只補亮色那一半。
   ★亮色不自己另調第三種灰：--border 是本檔既有的一階（zinc-100 → zinc-200），
     §S-42／§S-46／§S-47 三節都記過「不要再長出第三種灰」。
     實測差距：rgb(244,244,245) → rgb(228,228,231)，**16/255**
     （＝ §S-46 給 .flow-step 亮色 hover 量到的同一個差距 16，兩者對稱：
       一個從 228 往上到 244，一個從 244 往下到 228）。
   ★★命名的坑，寫在這裡免得下一個人踩：§S-55 那顆叫 --accent-on-muted，
     但**它的亮色值其實是為「靜止底是 --border」的 .flow-step 調的**
     （§S-55 自己也寫了：亮色與 --accent 逐字同值，是為了讓 .flow-step 的
     228 → 244 繼續成立）。真正「靜止底就是 --muted 本人」的元件是 rail 這一種，
     它需要的是**往深走**。兩者方向相反，一顆 token 服務不了
     —— 這正是 §S-55 說的「一顆 token 沒辦法同時服務兩種不同的靜止底」。
   ⇒ 不改 §S-55 的名字（只新增不改寫），本節用 `-fill` 後綴表示
     「元件靜止態的**填色就是 --muted 本人**」，與既有的 `-on-` 慣例並存。

   ── 覆蓋方式與特異度 ──
   下面那條的選擇器與 §POS-2（5117）／§S-37（9357）／§S-55（11569）那三條**逐字相同**，
   特異度 (0,7,0)、皆無 !important，靠**排在後面**取勝，三段原文一行未動。
   ★不加 !important：§S-55 已經說明「不要無故提高後人覆蓋它的門檻」，照辦。
   ★選中那顆不會被波及：`.nav-link.active:hover / .active:focus`（§S-37 9364）是 (0,8,0)，
     比本條高一階，品牌黃 rgb(255,210,0) 原封不動（實測確認）。
   ★:hover / :focus 這種狀態偽類不參與 ThemeOverrideAnnotationsTest 的串接判定
     （它明講會跳過帶狀態偽類的候選者），所以不需要、也不應該加覆蓋註解。
   ★不動 color：hover 文字亮色是 rgb(9,9,11) 壓在 rgb(228,228,231) 上 ＝ 15.9:1，
     遠超 AA，沒有理由跟著改。
   ══════════════════════════════════════════════════════════════════════════ */
:root                     { --accent-on-muted-fill: 240 5.9% 90%; }
html.dark                 { --accent-on-muted-fill: 240 5.3% 26.1%; }
[data-bs-theme="dark"]    { --accent-on-muted-fill: 240 5.3% 26.1%; }
[data-bs-theme="light"]   { --accent-on-muted-fill: 240 5.9% 90%; }

/* POS 左側 rail（§S-37）：靜止底是 --muted 本人 ⇒ hover 要往深走一階。
   暗色值與 §S-55 的 --accent-on-muted 逐字相同 ⇒ 暗色零變化，本條只補亮色那一半。 */
.pos .pos-menu .nav.nav-tabs .nav-item .nav-link:hover,
.pos .pos-menu .nav.nav-tabs .nav-item .nav-link:focus {
    background: hsl(var(--accent-on-muted-fill));
}

/* --- S-58. 開關綠改吃 shadcn 綠階 ＋ .fb-form-table 內距 SSOT ＋ 列內「編輯」鈕實心（2026-08-06）---
   ★章節錨點格式沿用 §S-57 的說明：守門測試 ThemeOverrideAnnotationsTest 只認
     `/* --- S-58. …`（S- 前面不可以多 §，`═` 全形橫幅它認不得）。

   業主 2026-08-06 三條指令，三件事互相獨立，收在同一節是因為都是「主題層一次解決」：
     ①「綠色有點深 shadcn 的綠色是這個綠嗎？」（附 green-500 截圖）
     ②「怎麼又發生編輯內頁 標題貼到邊上 …請你 SSOT 每一個頁面的編輯頁面 不要發生這個狀況」
     ③「編輯按鈕感覺是需要黑色 btn 筆反白 你做一個我看一下」（★這是提案，不是定案）
   ------------------------------------------------------------------------- */


/* ══ ① 開關打開的綠：--success-fg → shadcn/Tailwind 的 green 階 ════════════

   ── 先講清楚不是改什麼 ──
   ★★ 本節**不動 --success-fg 那顆 token 的值**。它在 .alert-success / 狀態 chip
      （span.btn.btn-green 等）／表格語意底色好幾處都在用，改它等於為了一顆開關
      去動整組 success 語彙。這裡新增一顆**只服務開關**的 token，
      原 token 一個字元都沒碰。
   ★ 也不動 §S-56 的任何一行。§S-56 的宣告仍在檔案裡、仍然可讀，
     本節同選擇器排在後面接手 background-color / border-color 兩個屬性
     （這正是 §S-56 當初接手 §35 的同一個做法）。
     ★不在 §S-56 上補「已被覆蓋」註解，理由是它自己已經寫過的那一條：
       守門測試明定帶狀態偽類（:checked）的候選者不參與自動判定，
       寫成制式宣稱＝給守門一個它驗不了的斷言。

   ── 業主截圖那顆綠（green-500 #22C55E）為什麼不能用 ──
   開關的可用性靠的是「圓鈕落在哪一邊看得出來」，也就是**圓鈕與軌道的對比**，
   不是軌道好不好看。圓鈕在亮色是 #FAFAFA（§35 的 SVG，本節不碰）。
   本輪自己重算了一次（sRGB 相對亮度，WCAG 2.x 公式）：
       候選（亮色軌道）        白圓鈕 #FAFAFA 對比   軌道 vs 卡片 #FFFFFF
       現用 --success-fg #19763B     5.444              5.682
       green-700  #15803D            4.805              5.016
       green-600  #16A34A  ← 採用    3.158              3.296
       emerald-600 #059669           3.610              3.768
       green-500  #22C55E（截圖那顆） 2.183              2.279   ← ✗
   ⇒ green-500 只有 2.18，**連 WCAG 1.4.11 非文字元件的 3:1 都過不了** ——
     白圓鈕會糊進綠底裡，使用者看不出開關撥到哪一邊。這與 §S-56 當初否決
     `--success`（同一顆 #22C55E）是同一個數字、同一個理由，不是換人重算就會不一樣。
   ⇒ 往下一階拿 green-600：3.158，過 1.4.11 的 3:1，而且明顯比現況亮
     （OKLCh 明度 49.9% → 62.7%），業主要的「不要那麼深」拿得到。

   ── ★誠實交代這次的取捨：3.16 過 3:1，但**低於** §S-56 當時寫的 AA 4.5 ──
   §S-56 挑 --success-fg 時把門檻寫成「業主要求的 AA 4.5」，現用值 5.44 是照那條挑的。
   換成 green-600 之後是 3.158 —— 對「非文字的 UI 元件邊界」而言，
   可適用的成功準則是 1.4.11 Non-text Contrast（3:1），4.5:1 是文字用的門檻；
   但這仍然是**從 5.44 掉到 3.16、餘裕只剩 5%** 的實質退讓，
   而退讓的原因是業主明確要更亮的綠。這一段寫在這裡，是為了讓下一個人知道
   「3.16 是知情下的選擇」，不是沒量過就填上去的。
   ★被否決的補救捷徑：給圓鈕加一圈細描邊把對比補回來。
     那要改 §35 的圓鈕 SVG（本節宣稱不碰的東西），而且 shadcn 原生 Switch 的圓鈕
     沒有描邊 —— 為了補一個「已經達標的準則」去偏離原生設計，不划算。據實記下不做。

   ── ★暗色一起決定（量過才決定，不是順手沿用）──
   暗色圓鈕是 #18181B（§35 的另一支 SVG），所以暗色的圓鈕對比從來不是瓶頸：
       暗色軌道候選            深圓鈕 #18181B 對比   軌道 vs 卡片 #18181B
       現用 --success-fg #85E0A6      11.145             11.145
       green-300 #86EFAC              12.618             12.618
       green-400 #4ADE80  ← 採用      10.167             10.167
       green-500 #22C55E               7.775              7.775
   四顆全部遠超門檻 ⇒ **對比選不出答案，要用「兩態看起來像不像同一顆綠」來選**。
   這件事用 sRGB 的 HSL 看會被騙（HSL 的 S 在不同明度下不可比），改用 OKLCh 量：
       亮 green-600 #16A34A   L=62.7%  C=0.170  H=149.2°   ← 基準
       暗 green-400 #4ADE80   L=80.0%  C=0.182  H=151.7°   ΔC=+0.012  ΔH=+2.5°
       暗 現用     #85E0A6   L=83.4%  C=0.120  H=154.9°   ΔC=-0.050  ΔH=+5.7°
   ⇒ 沿用現用的 #85E0A6 會變成「亮色是飽和綠、暗色是淡薄荷」——
     ΔC 0.05 是肉眼看得出來的彩度差（現用那對 #19763B/#85E0A6 的 ΔC 只有 0.004，
     所以**改前是相配的、只改亮色才會把它拆散**）。
     green-400 與 green-600 是同一條 Tailwind green 階上的兩階：色相幾乎同一點、
     彩度幾乎同一級，只差明度 —— 這正是「亮暗一對」該有的差異方式。
   ⇒ 亮 green-600 / 暗 green-400。兩顆都出自 shadcn 自己用的 Tailwind 調色盤，
     業主問的「shadcn 的綠色是這個綠嗎」，答案是「是這一階，但取的是能過對比的那一格」。
   ★代價說清楚：暗色圓鈕對比 11.145 → 10.167（掉 0.98，仍是門檻的兩倍以上），
     換到的是亮暗兩態讀起來是同一顆綠。這是本節唯一一處「暗色不是零變化」的地方，
     與 §S-57 ② 那種「暗色刻意取同值以求零變化」的做法不同，因為那裡的目的是補洞、
     這裡的目的是配對。

   ── 一樣只動 checked 一態，其餘四件事一行都不碰 ──
     · unchecked／:hover（未勾）／:focus ring／:disabled 全部維持 §35 與 §S-56 的原值
     · 圓鈕 SVG（兩支）不碰 ⇒ 亮色仍是深底白圓鈕、暗色仍是淺底深圓鈕
     · checked + hover 仍然是綠（本條排在 §35 的 :hover 之後，實測改前改後同樣是綠）
   ══════════════════════════════════════════════════════════════════════════ */
:root                     { --switch-on: 142.1 76.2% 36.3%; }   /* #16A34A green-600 */
html.dark                 { --switch-on: 141.9 69.2% 58%; }     /* #4ADE80 green-400 */
[data-bs-theme="dark"]    { --switch-on: 141.9 69.2% 58%; }
[data-bs-theme="light"]   { --switch-on: 142.1 76.2% 36.3%; }

.form-switch .form-check-input:checked {
    background-color: hsl(var(--switch-on));
    border-color: hsl(var(--switch-on));
}


/* ══ ② .fb-form-table 的左內距 SSOT —— 「標題貼到卡片邊上」一次治完 ════════

   ── 現況盤點（全站 8 支 blade／9 張表，1440 與 375 兩種寬、亮暗各一輪實測）──
   量的是「label 左緣 − 卡片左緣」（卡片有 1px 邊框，所以貼邊的答案是 1 不是 0）：
       頁面（走 https://test.2025-seller.mtest）          card-body class                 Δ
       /good/storageLocation/create                     card-body p-3                   17  ✓
       /good/storageTags/create                         card-body p-0 d-flex …           1  ✗ 貼邊
       /good/warehouse/1/edit                           card-body（裸，吃 BS 預設 16px） 17  ✓
       /pick/packageAction/9201/edit                    card-body p-0                    1  ✗ 貼邊
       /pick/desktop/1/edit                             card-body p-3                   17  ✓
       /pick/packageMachine/create                      card-body p-0                    1  ✗ 貼邊
       /pick/userPackage/900001/edit（第 1 張）          card-body p-0                    1  ✗ 貼邊
       /pick/userPackage/900001/edit（第 2 張，扣減紀錄） card-body p-0                    1  ✗ 貼邊
       /pick/package/9000001/edit                       card-body p-0                    1  ✗ 貼邊
   ⇒ 9 張裡 6 張貼邊。**右邊也一樣貼**（§S-24 有 `td:last-child{padding-right:0}`），
     只是控制項多半沒填滿，所以業主先看到的是左邊的 label。
   ★三支列表頁（storageLocation／storageTags／packageMachine）目前無資料，
     量的是 create —— controller 的 create 與 edit **render 同一支 blade**（逐支確認過），
     所以量到的內距與 edit 頁一致。

   ── ★§S-24 那條 `td:first-child{padding-left:0}` 為什麼存在、為什麼**不動它** ──
   §S-24 自己寫得很清楚：它是照 2026-fatboy 的 good/storageLocation/edit 轉過來的，
   fatboy 那支是逐 td 手寫 `ps-0 py-2 / pe-0 py-2`。也就是說這條的意圖是
   **「表格自己不要再多一層內距，讓 label 直接對齊卡片內容區的左邊界」** ——
   卡片標題、卡片裡的按鈕、下一張卡片的第一個字，全部都對在那條線上。
   ⇒ 它不是 bug，它是對的；出事的是**它假設的前提**（卡片自己有內距）在
     `card-body p-0` 的那 6 張上不成立。
   ⇒ 所以治法是把前提補回來（讓容器有內距），不是去覆蓋 §S-24
     （覆蓋它 ⇒ label 縮排 16px、但同卡片內其他元素還在原位 ⇒ 換一種不對齊）。
   ★這也是為什麼不能用 `td:first-child{padding-left:1rem}`：
     那條會**疊加**在已經有 p-3 的三張上（17 → 33），
     直接違反「不能讓已經正確的頁面位移」。

   ── 治法：咬「裝著表單型表格的那個 card-body」，把內距補成全站同一個值 ──
   選 1rem 不是挑的，是**對齊已經正確的那三張**：p-3 = 1rem、裸 card-body 的
   Bootstrap 預設也是 1rem ⇒ 三張已正確的頁面計算值逐位元組相同、**零位移**；
   6 張貼邊的從 Δ1 移到 Δ17，九張全部一致。
   ★用 padding 簡寫（四邊）不是只補 padding-left：右邊同樣貼著（見上），
     只補左邊會做出「左 16 右 0」的不對稱，而且與 p-3 那三張長得不一樣 ——
     那就不是 SSOT 了。四邊 1rem 之後，9 張的四個邊界全部與 p-3 那三張相同。
   ★為什麼要 !important：`.p-0` 是 Bootstrap 的 utility，本身帶 !important，
     不加就整條無效（寫了 ≠ 生效）。本條特異度也高於 `.p-0`，兩個條件都要滿足。
   ★為什麼在主題層而不是逐支改 blade：業主要的就是「每一個頁面都不要再發生」。
     blade 改一支只治一支，而且下一支新編輯頁複製 `card-body p-0` 又會回來；
     主題層咬的是「只要裡面是 .fb-form-table」，新頁面天生就吃得到。
     （/pick/desktop/1/edit 的 blade 先前已被逐頁改成 p-3 —— 本條落地後那個改動
       變成與主題層同值的重複保險，不衝突，本輪不動 blade。）

   ── 涵蓋兩種包法（實測 9 張只有這兩種，沒有第三種）──
     · table 直接掛在 card-body 底下：storageLocation／storageTags／warehouse
     · table 包在 .table-responsive 裡：其餘 6 張
   ★沒有用 `.card-body:has(table.fb-form-table)`（不限層數）的寫法：
     那會連「卡片裡再包一層 modal／子卡片」的情形一起吃到，範圍不可預期。
     限定 `>` 兩種路徑 ⇒ 命中的一定是「這個 card-body 就是為這張表存在的」。

   ── 已知會跟著變、量過並接受的一件事 ──
   /pick/userPackage 第 2 張「扣減時間紀錄」其實是資料表（唯一有 thead 的一張），
   不是 label/控制項的表單表，但它掛著同一個 .fb-form-table class ⇒ 一起吃到 1rem。
   結果是它與同頁上面那張表對齊，視覺上更一致，沒有負作用。
   ★這一張的 tbody 在測試資料下是 0 列（沒有 label 可量）⇒ 改量它的 thead 第一格：
     實測亮暗兩輪都是「th 左緣 − 卡片左緣 ＝ 17」，與同頁第 1 張表的 td 左緣 17 同一條線。
     據實說明是用 thead 量的，不是拿別張表的數字充數。
   要把它排除必須改 blade 的 class，那是行為/結構變更，不在本輪。
   ══════════════════════════════════════════════════════════════════════════ */
.app-content .card > .card-body:has(> table.fb-form-table),
.app-content .card > .card-body:has(> .table-responsive > table.fb-form-table) {
    padding: 1rem !important;
}


/* ══ ③ 列內「編輯」鈕：ghost → 實心近黑 ＋ 反白筆（★業主指定的偏離，提案性質）══

   ── ★★先更正一件事：業主看到的「淡綠底綠字」不是編輯鈕 ──
   本輪交辦時附的實測是「/pick/desktop 列內鈕 = btn btn-xs btn-green btn-rounded
   rounded-pill、淡綠底綠字、57×24」。實測（1440、亮/暗各一輪）那顆是**同一列的
   狀態 chip**「開啟」（`<span>`，不是連結），吃的是「任何帶語意色的非互動 span.btn
   都長成 shadcn Badge」那組規則（帶 !important，所以 runtime 注入蓋不動它 ——
   這正是「試過改黑但沒生效」的原因：改對了顏色，改錯了元素）。
   真正的編輯鈕實測是：
       <a class="fb-row-action" title="編輯"><i data-lucide="square-pen"></i></a>
       亮 background transparent／color rgb(9,9,11)／svg stroke 同色／32×32／radius 8px
       暗 background transparent／color rgb(250,250,250)
   ⇒ 它本來就已經是 shadcn 的 ghost icon 鈕，也就是業主說「感覺需要黑色」的那顆。
   ★本節只動它，狀態 chip 一個字都沒碰。

   ── 這是**偏離既有標準**，不是補洞（寫清楚以免日後有人「照標準改回去」）──
   本檔「表格列內動作鈕與縮圖：對齊 shadcn」那一節、以及 SSOT 元件
   resources/views/components/row-action.blade.php 的檔內註解，白紙黑字訂的是：
       列內動作 = Button variant="ghost" size="icon"，底色透明，hover 才給 accent，
       理由原文：「列表頁的編輯鈕原本是 btn btn-xs btn-dark —— 一顆近黑實心方塊。
       每一列都有一顆，20 列就是 20 個黑方塊排成一直行，視覺重量跟頁首的主要動作同級，
       把使用者的注意力從資料本身拉走。」
   ⇒ 本節做的事，**正好是把當初刻意拿掉的那個實心黑放回去**。
     業主的要求（「編輯按鈕感覺是需要黑色 btn 筆反白 你做一個我看一下」）是
     「找得到、點得到」優先於「不搶視覺權重」—— 這是取捨不是對錯，
     但它與上面那條標準直接衝突，所以據實記在這裡，並且：
       · 範圍**只鎖 title="編輯"**：全站 66 處列內動作鈕裡命中 47 處
         （blade 直接寫 class 的 17 ＋ x-row-action 元件的 30），
         其餘 19 處 ——「刪除」（destructive ghost）、「薪資明細」、「檢視明細」、
         以及 x-row-action 的「調整數量／修改售價／替換商品／刪除商品」——
         全部維持 ghost，一顆都沒被波及。
         ★這不是用 grep 推的：/employee/user/salaryEdit 亮暗兩輪實測「薪資明細」
           仍是 background transparent（亮 color rgb(9,9,11)／暗 rgb(250,250,250)），
           與改前逐位元組相同。
       · 只鎖 `td` 內（＝業主講的「列內」），表格外的同 class 用法不受影響。
     ★★這是提案，不是定案。**一行退回**：在本節之後補上
       `.app-content td .fb-row-action[title="編輯"]{background:transparent!important;color:hsl(var(--foreground))!important}`
       即可回到 ghost 標準（不必刪任何一行，符合本檔只新增不改寫的慣例）。
       ★這一行本身也實測過：貼上後三顆屬性全部回到 ghost 的實測值
       （亮 background transparent／color rgb(9,9,11)），確認它真的退得回去。

   ── 顏色不自己調，全部走既有 token（亮暗自動翻轉）──
   底 --dark / 字 --dark-foreground，hover 走 `--dark / .88`、active 走 `/ .8`
   —— 這三個值不是我挑的，是本檔 .btn-dark 那組 `--bs-btn-hover-bg` / `--bs-btn-active-bg`
   的逐字沿用（同一個「實心近黑」語彙只能有一份答案）。
   實測對比（sRGB／WCAG）：
       亮：筆 #FAFAFA 壓在鈕 #18181B 上 16.97:1；鈕 #18181B 壓在卡片 #FFFFFF 上 17.72:1
       暗：--dark 翻成 #FAFAFA、--dark-foreground 翻成 #18181B
           ⇒ 筆 16.97:1；鈕壓在卡片 #18181B 上 16.97:1
   ⇒ 「黑底白筆」在暗色會自動變成「白底黑筆」。那是本檔既有的翻轉語彙
     （checkbox 勾選、.btn-dark 都是這樣），不是暗色沒做。
   ★不碰尺寸（32×32）、不碰圓角（--radius-md 8px）、不碰 svg 尺寸（16px）、
     不碰 :focus-visible 的間隙環 —— 本節只宣告 background 與 color 兩個屬性。

   ── ★★我自己踩到並修好的坑：只有 background 生效、color 沒生效（第一版是黑底黑筆）──
   第一版寫的是不帶 !important 的 `color: hsl(var(--dark-foreground))`，理由是
   「ghost 那條只有 (0,1,0)，本條 (0,3,1) 綽綽有餘」。**實測直接打臉**：
       改後實測 /pick/desktop 亮色 → background rgb(24,24,27) ✅、color 仍是 rgb(9,9,11) ❌
       （暗色同樣：白底 + 白筆）⇒ 畫面上是一顆看不見圖示的黑方塊。
   ★用 CDP 的 CSS.getMatchedStylesForNode 直接問 Chrome 誰贏，答案是 §5 那條
     `#content a:not(.btn):not(.dropdown-item):not(.nav-link):not(.fb-tab):not(.page-link)`
     —— 它帶 **ID**，特異度 (1,5,1)。ID 一出現，class 再多也追不上
     （本條就算把 :not() 全抄一遍也只是 (0,7,2)）。
   ⇒ 所以 color **必須** !important，這不是偷懶，是 ID 特異度沒有別的解法。
     background 不需要（沒有人用 ID 咬它），維持不加。
   ★★順帶量到一件**本輪不修**的既有事實：`.fb-row-action { color: hsl(var(--foreground)) }`
     那一行其實一直是死碼 —— 同一條 §5 的連結規則早就贏過它。
     沒人發現是因為 `--link: var(--foreground)`（亮暗都是），兩邊剛好同值。
     ⇒ 現況沒有畫面問題，但只要有人改 --link，全站 66 處列內動作鈕
       （29 處 blade 直接寫 class ＋ 37 處走 x-row-action 元件）的圖示都會跟著變色。
       這是既有規則的問題、不在業主這三條交辦內，據實記在這裡交回主題那邊決定。
   ══════════════════════════════════════════════════════════════════════════ */
.app-content td .fb-row-action[title="編輯"] {
    background: hsl(var(--dark));
    /* !important 的理由見上：§5 的連結色規則帶 ID，(1,5,1) 沒有純特異度解 */
    color: hsl(var(--dark-foreground)) !important;
}
.app-content td .fb-row-action[title="編輯"]:hover,
.app-content td .fb-row-action[title="編輯"]:focus-visible {
    background: hsl(var(--dark) / .88);
    color: hsl(var(--dark-foreground)) !important;
}
.app-content td .fb-row-action[title="編輯"]:active {
    background: hsl(var(--dark) / .8);
    color: hsl(var(--dark-foreground)) !important;
}


/* --- S-59. /center/report 的日期選擇器：內嵌 daterangepicker ＋ 殘留 bootstrap-datepicker 收斂（2026-08-06）---
   ══════════════════════════════════════════════════════════════════════════
   背景：全站的日期選擇器 SSOT 是 bootstrap-daterangepicker，只有 /center/report
   還在用 bootstrap-datepicker（全站唯一一支）。這輪把「日」「週」換成
   daterangepicker，「月」「年」留著 —— 不是沒做完，是 daterangepicker 天生沒有
   月視圖／年視圖，硬兜出來會連 range_date 的送出值與後端解析一起動到（詳細理由
   寫在 Center/reportPackage/index.blade.php 的 JS 註解裡）。
   ⇒ 本章節因此有兩塊：① 讓 daterangepicker 能內嵌 ② 讓殘留的 bootstrap-datepicker
     看起來跟 daterangepicker 是同一個東西。

   ── ① 內嵌 ──
   daterangepicker 只能掛在元素上、以浮層彈出（position:absolute + 尖角 + 陰影）。
   本頁的版面是「dropdown 裡直接攤開一張月曆」，所以把面板 parentEl 生進
   #inline-datepicker 後，用 position:static 把它攤平，並拿掉浮層才需要的
   邊框／陰影／尖角 —— 外面那層 .dropdown-menu 已經是 shadcn 化的 Popover，
   再套一層框會變成雙層框。
   ★ !important 的理由：套件是用 inline style 在寫 top/left/display 的
     （show()/hide()/movePicker() 每次都重寫），純特異度蓋不過 inline style。

   ── ② 殘留的 bootstrap-datepicker ──
   實測現況（亮色 /center/report → 月）：容器 background transparent、圓角 4px、
   無陰影、`.datepicker-inline` 自己 border 是 0（視覺上的框來自外層 .dropdown-menu）、
   選中格 rgb(0,68,204) —— 那是 bootstrap 2 時代的寶藍 #0044cc，寫死在
   plugins/bootstrap-datepicker/dist/css/bootstrap-datepicker.css 裡，不在調色盤內，
   暗色下也不會跟著翻。這裡把它接到與 daterangepicker 同一組 token：
     選中 = --dark／hover = --accent／灰字 = --muted-foreground／圓角 = --radius-md
   ★ 套件那些規則帶 gradient（background-image）與 text-shadow，只換 background
     會留下一層藍色漸層 ⇒ 必須一併 background-image:none / text-shadow:none。
   ★ 月／年視圖的格子是 <span> 不是 <td>，兩種都要寫，漏掉 span 就只有日視圖變乾淨。
   ══════════════════════════════════════════════════════════════════════════ */

/* ── ① 內嵌用：把浮層攤平 ── */
#inline-datepicker .daterangepicker {
    position: static !important;
    display: block !important;
    top: auto !important;
    left: auto !important;
    right: auto !important;
    bottom: auto !important;
    width: 100%;
    max-width: none;
    margin: 0 !important;
    padding: 0;
    /* 框與陰影交給外層 .dropdown-menu（shadcn Popover），這裡不再疊一層 */
    border: 0 !important;
    box-shadow: none !important;
    background: transparent !important;
    font-size: 13px;
    z-index: auto;
}
/* 浮層的小尖角在內嵌時沒有意義 */
#inline-datepicker .daterangepicker:before,
#inline-datepicker .daterangepicker:after {
    display: none !important;
}
#inline-datepicker .daterangepicker .drp-calendar {
    float: none;
    width: 100%;
    max-width: none;
    padding: 0;
}
#inline-datepicker .daterangepicker .calendar-table {
    padding: 0;
    background: transparent !important;
}
#inline-datepicker .daterangepicker .calendar-table table {
    width: 100%;
}
/* 週次欄（週模式的 showISOWeekNumbers）：跟月/年視圖的週次欄同一種灰。
   ★min-width 一定要壓：外掛給每一格 min-width:32px（daterangepicker.css:151），
     多一欄就讓面板從 246px 變 291px，實測 375 寬度下 dropdown 左緣溢出 4px
     （Popper 已經 shift 到底了還是塞不下）。週次只有兩位數，20px 綽綽有餘，
     壓完回到 245px，四種週期的面板寬度就一致了。 */
#inline-datepicker .daterangepicker .calendar-table th.week,
#inline-datepicker .daterangepicker .calendar-table td.week {
    color: hsl(var(--muted-foreground)) !important;
    font-size: 10px;
    font-weight: 400;
    min-width: 20px;
    width: 20px;
    padding: 0;
}
/* ★還有一格要壓：標題列（‹ 八月 2026 ›）最左邊那個空白 th 對齊的就是週次欄，
   但它身上沒有 .week class（外掛只在星期列與日期列給），所以上面那條抓不到它。
   實測：週次欄壓到 20px 之後表格還是 257px —— 撐寬的是這個空 th 的 min-width:32px。
   ⇒ 用 JS 掛的 .drp-week 當鉤子精準壓它（日/月/年沒有這個 class，不會被誤傷）。
   壓完 20 + 7×32 = 244px，四種週期的面板寬度一致，375 也不再溢出。 */
#inline-datepicker.drp-week .daterangepicker .calendar-table thead tr:first-child th:first-child {
    min-width: 20px;
    width: 20px;
    padding: 0;
}
/* showDropdowns 的年月下拉：塞進 dropdown 寬度內不要撐破 */
#inline-datepicker .daterangepicker .monthselect,
#inline-datepicker .daterangepicker .yearselect {
    width: auto;
    max-width: 46%;
}

/* ── ② 殘留的 bootstrap-datepicker（月／年）接上 shadcn token ── */
.datepicker-inline,
.datepicker.datepicker-inline {
    width: 100%;
    padding: 0;
    background: transparent;
    color: hsl(var(--foreground));
    border-radius: var(--radius-md);
    font-size: 13px;
}
.datepicker table {
    width: 100%;
}
/* ★格子圓角刻意寫 4px 而不是 var(--radius-md)（=8px）：
   目標是「跟 daterangepicker 一致」，而 daterangepicker 在螢幕上量到的就是 4px ——
   §18 雖然寫了 `.daterangepicker td { border-radius: var(--radius-md) }`，但外掛自己的
   `.daterangepicker td.start-date.end-date { border-radius: 4px }`（daterangepicker.css:208）
   特異度 (0,3,1) 贏過它 (0,1,1)，所以選中格實際是 4px。
   我第一版這裡寫 var(--radius-md)，實測就變成「日/週 4px、月/年 8px」的新不一致。
   ⇒ 取螢幕上那份。要把兩套一起提到 --radius-md 是全站 daterangepicker 的改動
     （chart／pick×3 都會跟著變），不在本次範圍。
   ★容器 .datepicker-inline 的圓角維持 --radius-md：它背景透明又沒有框，圓角不可見，
     不需要為了對齊而動它。 */
.datepicker table tr td,
.datepicker table tr th {
    color: hsl(var(--foreground));
    font-weight: 500;
    border-radius: 4px;
}
.datepicker table tr td span {
    border-radius: 4px;
}
/* hover / focus：shadcn 的清單項一律 accent 底 */
.datepicker table tr th.datepicker-switch:hover,
.datepicker table tr th.prev:hover,
.datepicker table tr th.next:hover,
.datepicker table tr td.day:hover,
.datepicker table tr td.day.focused,
.datepicker table tr td span:hover,
.datepicker table tr td span.focused {
    background: hsl(var(--accent)) !important;
    background-image: none !important;
    color: hsl(var(--accent-foreground)) !important;
}
/* 非本月／非本年的格子 */
.datepicker table tr td.old,
.datepicker table tr td.new,
.datepicker table tr td span.old,
.datepicker table tr td span.new {
    color: hsl(var(--muted-foreground) / .5) !important;
}
/* ISO 週次欄 */
.datepicker table tr td.cw,
.datepicker table tr th.cw {
    color: hsl(var(--muted-foreground)) !important;
    font-weight: 400;
}
/* 今天：與 daterangepicker 的 td.today 同一種表示法（底線 + 粗體），
   不是套件預設那塊黃色漸層 —— 黃色在暗色下會變成一塊亮斑 */
.datepicker table tr td.today,
.datepicker table tr td.today:hover,
.datepicker table tr td.today.disabled,
.datepicker table tr td.today.disabled:hover {
    background: transparent !important;
    background-image: none !important;
    color: hsl(var(--foreground)) !important;
    text-shadow: none !important;
    font-weight: 700;
    text-decoration: underline;
    text-underline-offset: 3px;
}
.datepicker table tr td.today:hover {
    background: hsl(var(--accent)) !important;
    color: hsl(var(--accent-foreground)) !important;
}
/* 選中：與 daterangepicker 的 td.active 一致 = --dark 實心 */
.datepicker table tr td.active,
.datepicker table tr td.active:hover,
.datepicker table tr td.active.active,
.datepicker table tr td.active:hover.active,
.datepicker table tr td.active.disabled,
.datepicker table tr td.active.disabled:hover,
.datepicker table tr td.today.active,
.datepicker table tr td.today.active:hover,
.datepicker table tr td span.active,
.datepicker table tr td span.active:hover,
.datepicker table tr td span.active.active,
.datepicker table tr td span.active.disabled,
.datepicker table tr td span.active.disabled:hover {
    background: hsl(var(--dark)) !important;
    background-color: hsl(var(--dark)) !important;
    background-image: none !important;
    color: hsl(var(--dark-foreground)) !important;
    text-shadow: none !important;
    border-color: transparent !important;
}
/* 不可點（週模式鎖星期一時，星期一以外的格子）*/
.datepicker table tr td.disabled,
.datepicker table tr td.disabled:hover,
.datepicker table tr td span.disabled,
.datepicker table tr td span.disabled:hover {
    background: transparent !important;
    background-image: none !important;
    color: hsl(var(--muted-foreground) / .4) !important;
}
/* 標題列（‹ 2026年八月 ›）：跟 daterangepicker 的 .month 同字重 */
.datepicker table tr th.datepicker-switch,
.datepicker table tr th.prev,
.datepicker table tr th.next {
    color: hsl(var(--foreground));
    font-weight: 500;
    border-radius: 4px;   /* 與格子同一個值，理由見上 */
}
.datepicker table tr th.dow {
    color: hsl(var(--muted-foreground));
    font-weight: 500;
}

/* --- S-60. 列內「編輯」鈕退回 ghost（2026-08-07）--------------------------------
   業主先是說「編輯按鈕感覺是需要黑色 btn 筆反白 你做一個我看一下」，
   §S-58 據此做了黑實心。做出來之後業主看了畫面：「覺得這編輯按鈕不是很好看」。
   ⇒ §S-58 那一段本來就標明是**提案不是定案**，並留了一行退回法。這裡就是執行那一行。

   ★退回的是「顏色」不是「元件」：ghost 正是 shadcn 表格列動作的標準做法，
     也是 components/row-action.blade.php 檔內註解當初刻意選的 ——
     它白紙黑字寫著「原本是 btn-xs btn-dark 一顆近黑實心方塊，20 列就是 20 個黑方塊
     排成一直行，把注意力從資料拉走」。實測 /pick/desktop 28 列亮色下確實就是那個畫面，
     而且與同列淡綠的「開啟」chip 視覺重量打架。

   ★§S-58 的原文一行未刪（禁止刪除守則），靠本節排在後面取勝。
     `color` 仍需 !important —— §5 的 `#content a:not(.btn):not(...)` 帶 ID、特異度 (1,5,1)，
     不加就只有 background 生效，會變成「看不見圖示的方塊」（§S-58 已記過這個坑）。

   ★同批的 markup 改動：圖示 square-pen → pencil（業主指定）。
     理由是辨識度：square-pen 的方框在 16px 下與同列的 checkbox 方框撞形，並排容易看錯。
     明確傳 icon 的呼叫端（「調整數量」「修改售價」等）不受影響。 */
.app-content td .fb-row-action[title="編輯"] {
    background: transparent !important;
    color: hsl(var(--foreground)) !important;
}

/* --- S-61. 列內「編輯」改成 shadcn 文字按鈕（2026-08-07）------------------------
   業主：「幫我改為正常按鈕 編輯 不要筆了」「shadcn 的按鈕 寫 編輯」。
   ⇒ 不是 ghost icon 鈕，是**看得出是按鈕**的 shadcn Button：outline 變體
     （`border` + 透明底 + hover 換 accent 底），這是 shadcn 表格列裡放文字動作的標準做法。

   ★尺寸沿用 shadcn 的 sm 階（h-8＝32px），與本檔其他 .btn-sm 同階，不另立第三種高度。
   ★`.fb-row-action` 本體在 §S-39 是 32×32 的方形圖示鈕（`width:32px`），
     文字化之後必須放開寬度，否則「編輯」兩個字會被壓在 32px 裡截斷。
   ★`color` 需要 !important —— §5 的 `#content a:not(.btn):not(...)` 帶 ID、特異度 (1,5,1)
     會贏過本節；§S-58／§S-60 都記過這個坑。background/border 不需要，沒有 ID 規則咬它們。
   ★只鎖 `title="編輯"` 且在 `td` 內：同列的刪除、明細、調整數量等仍是圖示鈕，不受影響。 */
.app-content td .fb-row-action[title="編輯"] {
    width: auto;
    min-width: 0;
    height: 32px;
    padding: 0 12px;
    gap: 6px;
    /* ★2026-08-07 業主追加：「編輯要黑色按鈕 白色字 使用 shadcn 的按鈕」
         ⇒ 從 outline 變體改成 shadcn 的 **default 變體**（bg-primary + text-primary-foreground）。
         本專案的 primary 映射到近黑，所以 default 就是「黑底白字」。
         原本的 outline 值記在這裡備查：border 1px --border／background transparent／color --foreground。 */
    /* ★background/border 需要 !important：§S-60（列內編輯鈕退回 ghost）的
         `background: transparent !important` 排在本節之前但帶 !important，不加就贏不過它。
         §S-60 的原文一行未刪，靠本節同帶 !important 且排在後面取勝。 */
    border: 1px solid hsl(var(--dark)) !important;
    border-radius: var(--radius-md);
    background: hsl(var(--dark)) !important;
    color: hsl(var(--dark-foreground)) !important;
    font-size: 13px;
    font-weight: 500;
    line-height: 1;
    white-space: nowrap;
}
.app-content td .fb-row-action[title="編輯"]:hover {
    /* shadcn default 變體的 hover 是 bg-primary/90 */
    background: hsl(var(--dark) / .9) !important;
    border-color: hsl(var(--dark) / .9) !important;
    color: hsl(var(--dark-foreground)) !important;
}

/* --- S-62. .card 直接當 grid column 時，背景會畫到 gutter 裡（2026-08-07）--------
   業主：「/inventory/composite-product/create 你這頁儲存又歪掉 沒有 SSOT」。

   ★根因（實測，不是猜）：這幾頁的 `.card` **直接掛在 `.row gx-4` 底下當 grid column**。
     Bootstrap 的 `.row` 有負邊距 `margin-left: calc(-.5 * var(--bs-gutter-x))`，
     `.row > *` 再用同寬的 padding 補回來。**內容確實回到正確位置，但 `.card` 的
     背景與圓角是畫在 border-box 上** → 卡片底色從 x=232 開始畫，而動作鈕在 244，
     看起來就是「儲存鈕比卡片內縮 12px」。
     實測：composite-product/create、product/create、invoice/main/create 三頁 Δ=12；
     正常的頁面（good/warehouse/create…）把 .card 包在 .col-* 裡，Δ=0。

   ★為什麼用 `background-clip: content-box` 而不是逐頁改 blade 包 .col-12：
     ① 改 blade 要逐頁補開頭與結尾標籤，漏一個就破版（我第一版就漏了 </div>，已退回）
     ② 這是**版面規則**不是單頁問題，主題層一條就是 SSOT
     ③ `background-clip` 只改「背景畫到哪」，**不動盒模型、不動內距、不造成位移**
   ★這幾張卡都是 `border-0`，所以不必處理 border 被 gutter 切開的問題；
     若日後有帶 border 的卡片直接掛 .row 下，要另外處理（border 仍會畫在 border-box）。 */
.app-content .row > .card {
    background-clip: content-box;
}

/* --- S-63. .form-label 字級統一到 shadcn 標準（2026-08-07）----------------------
   業主：「就是表格中的文字大小 好像比其他頁大字」→「全部統一 shadcn 標準」。

   ★根因（實測）：同一個 `.form-label` 因為**外層容器不同**而有兩種字級 ——
     · `/inventory/composite-product/create` 的「複合編號」等在 `div.col-md-3`（grid 排版）→ **16px**
     · `/good/warehouse/create` 的「倉庫名稱」等在 `td`（.fb-form-table）→ **12px**
     差 4px，一眼看得出來。原因是 §S-24 的
     `.app-content table.fb-form-table .form-label { font-size: 12px }`
     **鍵在表格容器上**，grid 排版的頁面吃不到，掉回 Bootstrap `.form-label` 的繼承值 16px。
     ⇒ 這正是「同一個元件、兩種結果」的 SSOT 破口。

   ★為什麼是 14px：shadcn 的 Label 是 `text-sm`（14px / font-medium / leading-none）。
     原本表格頁的 12px 偏小（本檔可讀性下限就是 12px，label 壓在下限等於沒有餘裕）；
     16px 則是 Bootstrap 的繼承值，不是任何人訂的規格。14px 是 upstream 的值，不是我挑的。

   ★作法：把字級鍵在 **`.form-label` 本身**，不再鍵在容器上 ——
     不管它坐在 `td`、`div.col-*` 還是任何地方都一致，這才是 SSOT。
   ★§S-24 那條 12px 的原文一行未刪（禁止刪除守則），靠本節排在後面、同特異度取勝。
   ★刻意不動的兩處：
     · `.login .form-label`（.82rem）—— 登入頁是獨立的視覺情境，§16 有自己的一套
     · `.table-stack-sm td:first-child`（.78rem）—— 那是手機把表格堆疊成卡片時的「欄位名」，
       語意上是資料標籤不是表單 label，且該分支已明寫 `font-size: inherit` 交給父層決定 */
.app-content .form-label {
    font-size: 14px;
    font-weight: 500;
}
/* ★上面那條 (0,2,0) 贏不過 §S-24 的 `.app-content table.fb-form-table .form-label` (0,3,1)
     —— 表格頁實測仍是 12px。補一條**同特異度、排在後面**的把它接手，
     §S-24 原文一行未刪。這也再次印證：改完一定要重量，只寫規則不代表生效。 */
.app-content table.fb-form-table .form-label {
    font-size: 14px;
}

/* --- S-64. 全站表格字級改吃 shadcn（th/td 14px）＋卡片標題字重統一（2026-08-07）---
   業主：「對 全站表格統一吃 shadcn」
        「專案自己的規格 §4 明訂 th = 12px / td = 13px …請移除」

   ★★這是**規格變更，由業主拍板**，不是我順手改的：
     §補位7（L5659 起）的註解寫著「規格 §4 明訂 th = 12px，補上」——
     那條規格從此**由業主明示退場**，改以 shadcn 的 Table 為準。
     ⇒ shadcn 的 TableHead / TableCell 都繼承 `text-sm` = **14px**。
   ★§補位7 的原文一行未刪（禁止刪除守則），本節排在後面、同特異度接手。
     保留原文是為了讓下一個人看得到「12px 曾是規格、何時被誰換掉」。

   ★★影響已量過並如實記錄（8 支列表頁 × 1440/375，改前→改後）：
     · 1440：表格橫向捲動 **0 → 0**、頁面溢出 **0 → 0**（完全沒變）
     · 375 ：表格橫向捲動增加（例 /pick/desktop 362→394、new-orders 699→760、
             invoice/main 608→710），但**頁面溢出仍是 0**
       —— 表格本來在窄螢幕就是 .table-responsive 的可捲容器，只是捲得多一點，
          不是整頁破版。這是提高字級必然的代價，業主已知情。

   ★卡片標題字重：實測列表頁 600、編輯/新增頁 700，字級都是 16px。
     shadcn 的 CardTitle 是 `font-semibold` = **600**，統一到 600
     （列表頁本來就是，只有編輯頁那批會變）。標題字級 16px 不動。
   ★2026-08-08：`.table-panel` 那行已成死選擇器（理由與驗證見 §23 那段長註解），故移除。
     原清單記此存查：
       `.app-content .table > thead > tr > th,`
       `.app-content .table-panel > thead > tr > th {` */
.app-content .table > thead > tr > th {
    font-size: 14px;
}
.app-content .table > tbody > tr > td {
    font-size: 14px;
}
.app-content .card > .card-header {
    /* ★需要 !important：部分 blade 在 markup 上寫死 `class="card-header p-3 fw-bold"`，
       而 .fw-bold 是帶 !important 的 Bootstrap utility（font-weight:700!important）。
       實測不加就只有列表頁生效、編輯頁仍是 700 —— 又一次「只寫規則不代表生效」。
       ★把字重收在主題層而不是逐頁刪 markup 的 fw-bold：
         ① 逐頁改要動十幾支 blade，漏一支就又不一致
         ② 這是版面規則，主題層一條才是 SSOT
         ③ 禁止刪除守則：markup 上的 fw-bold 一個字都沒動，只是不再由它決定字重 */
    font-weight: 600 !important;
}

/* --- S-65. 表格凍結欄／吸頂表頭 SSOT（2026-08-07）-------------------------------
   業主：「表格的 th 我希望可以 sticky 左邊也可以 sticky（看能不能自由設定要 sticky 到哪）」
        「https://test.2025-seller.mtest/center/report 這頁左右滾動會蓋住 sticky」

   ═══ ① /center/report 穿透的根因（CDP 實測問出來的，不是猜）═══
   那頁**沒有**用專案自己的 .table-stick / .stick（blade grep 0 命中）。
   它用的是 DataTables 的 FixedColumns 外掛
   （Center/reportPackage/index.blade.php:874 `fixedColumns: { left: 1 }`），
   凍結欄的 class 叫 `.dtfc-fixed-start` / `.dtfc-fixed-left`，
   `position: sticky; left: 0` 是外掛用 JS 寫成 inline style 的。

   CSS.getMatchedStylesForNode 對 `div.dt-scroll-body #dataTable tbody tr td.dtfc-fixed-start`
   問出來的背景相關規則（依 CSSOM 順序，後者覆蓋前者）：
     1) [DataTables 核心]        table.dataTable tbody tr > .dtfc-fixed-start
                                 { z-index: 1; background-color: white }
     2) [DataTables 的 Bootstrap5 整合層，同一支檔案更後面、特異度相同 → 它贏]
                                 table.dataTable tbody tr > .dtfc-fixed-start
                                 { background-color: var(--bs-table-bg) }
   而本主題 L771 `.app-content .table { --bs-table-bg: transparent; }`
   → 凍結欄的底色最後被解析成 **transparent**，捲過去的內容就從底下透上來。

   實測 computed 值（https://test.2025-seller.mtest/center/report，1440×900）：
     亮色 tbody 第一欄  background-color = rgba(0, 0, 0, 0)    ← 透明，穿透
     亮色 thead 第一欄  background-color = rgba(0, 0, 0, 0)    ← 透明，穿透
     暗色 tbody 第一欄  background-color = rgb(33, 37, 41)     ← 不透明，但**不是主題色**
                        （DataTables 自帶 `:root.dark{--dt-html-background: rgb(33,37,41)}`；
                          本主題 --card 在暗色是 hsl(240 5.9% 10%) ≒ rgb(24,24,27)，兩者不同）
     暗色 thead 第一欄  background-color = rgba(0, 0, 0, 0)    ← 仍然透明
                        （被本主題 L776 `background: transparent !important` 壓過，
                          連 DataTables 的 html.dark 規則都輸給那個 !important）

   ★為什麼既有的「保險絲」沒擋住：L6334 那條比對的是 class **含 `sticky` 字串**，
     而 DataTables 的 class 叫 `dtfc-fixed-start` / `dtfc-fixed-left` ——
     一個 sticky 字都沒有 → 保險絲從頭到尾沒咬到這兩頁。
     這是「守門自己是假綠燈」的同一族：規則寫了、看起來有防護、實際永遠不匹配。

   ★治法刻意**不是**把 --bs-table-bg 改回不透明：那會讓全站每一個 td 都畫一層底，
     斑馬紋、選取列、狀態底色全部被蓋掉，牽動面遠大於問題本身。
     這裡只針對「真的會 sticky 的那幾格」補一層走 token 的不透明底。

   ═══ ② 全站吸頂／凍結欄寫法盤點（掃 256 支 blade + 主題 + vendor + plugins）═══
     吸頂 A. `thead.sticky-top` / `.bg-light.sticky-top` → blade 命中 **0**
             （主題 L5730 那段補丁的註解說「代理保留的 9 處」，那 9 處早就不在了 → 死碼）
     吸頂 B. `.table-sticky-head`                        → blade 命中 **0**，
             而且主題裡從來沒給它 `position: sticky` → 雙重死碼
     吸頂 C. `table th[class*="sticky"]` 保險絲          → 永遠不匹配（見上）
     吸頂 D. DataTables `scrollX: true` 的雙 table 分離表頭
             → **唯一真的在運作的**（Center/reportPackage、Center/chartPackage 兩頁）
     凍結 E. DataTables `fixedColumns`                   → 在運作（同上兩頁）
     凍結 F. 專案自訂 `.table-stick / .stick / .stick-end` + `fbStickyCols()`
             → CSS 有、JS 有、blade 命中 **0** → 整套完整實作的死碼
   → 收斂成一套：**這一段是全站唯一的吸頂／凍結欄樣式來源**。
     A / B / C 三種舊寫法一行都沒刪，改成全部接到下面同一組宣告上
     （等價收斂，不破壞相容；FixedColumns 那套外掛的 class 也接進來一起吃 token）。

   ═══ ③ 宣告式 API（業主要的「自由設定要 sticky 到哪」）═══
       <table data-sticky-cols="2">   凍結最左邊 2 欄（要幾欄改數字，不用改 markup）
       <table data-sticky-head>       表頭吸頂
   產生端在 _shell.blade.php 第 12b 段：把 data-sticky-cols 轉成既有的
   .table-stick / .stick / .stick-end，left 位移仍由 fbStickyCols() 實測欄寬後寫入
   （**絕不寫死 px**，欄寬隨內容變）。
   ★向下相容：手寫 .table-stick + .stick 的舊寫法一個字都沒動、繼續有效 ——
     宣告式只是「幫你把 class 貼上去」，貼完走的是同一條路。
   ★吸頂（data-sticky-head）純 CSS 就成立，不依賴 JS；JS 只在「多列表頭」時
     補每一列的 top 位移。

   ═══ ④ z-index 層疊（由低到高，這一段的約定）═══
       凍結欄 td              2   （§7 L759 既有）
       吸頂表頭 th            3
       左上角（吸頂＋凍結）   4   ← 兩種 sticky 交會的那一格必須最高，否則會被表頭蓋住
   --------------------------------------------------------------------------- */

/* ── S-65.1 DataTables FixedColumns：凍結欄補走 token 的不透明底
   特異度算式（為什麼是這個寫法）：
     tbody 這條 = (0,4,4)，要贏過 DataTables 的
       `html.dark table.dataTable tbody tr > .dtfc-fixed-start` = (0,3,4)
     → 靠特異度贏，**刻意不加 !important**：這樣 L1888 帶 !important 的 row hover
       才蓋得過它，凍結欄才會跟著整列變色（見 S-65.5）。
     thead / tfoot 這條**必須**加 !important，因為 L776
       `.app-content .table > thead > tr > th { background: transparent !important }`
       只要不加就贏不了（實測暗色表頭仍是 rgba(0,0,0,0) 就是敗給它）。 */
.app-content table.table.dataTable > tbody > tr > .dtfc-fixed-start,
.app-content table.table.dataTable > tbody > tr > .dtfc-fixed-end {
    background-color: hsl(var(--card));
}
.app-content table.table.dataTable > thead > tr > .dtfc-fixed-start,
.app-content table.table.dataTable > thead > tr > .dtfc-fixed-end,
.app-content table.table.dataTable > tfoot > tr > .dtfc-fixed-start,
.app-content table.table.dataTable > tfoot > tr > .dtfc-fixed-end {
    background-color: hsl(var(--card)) !important;
}
/* FixedColumns 在捲動時會塞兩塊 blocker 蓋住捲軸角落，DataTables 寫死 white /
   rgb(33,37,41)。目前兩頁都是 display:none，但只要有人開 scrollY 就會現形，
   一併接到 token 上（--card 亮暗會翻轉）。 */
div.dt-scroll div.dtfc-top-blocker,
div.dt-scroll div.dtfc-bottom-blocker,
div.dtfh-floatingparent div.dtfc-top-blocker,
div.dtfh-floatingparent div.dtfc-bottom-blocker {
    background-color: hsl(var(--card));
}
/* 凍結區與捲動區的分界線：DataTables 自己只有 rgba(0,0,0,.2)/.3 的陰影，
   暗色底下幾乎看不見。補一條走 token 的細線。
   用 `:not(:has(+ .dtfc-fixed-start))` 判斷「右邊沒有下一根凍結欄」＝凍結區最後一欄，
   所以 left:1 或 left:3 都對，不必寫死欄數。
   （:has 不支援的瀏覽器會整條丟棄 → 退回 DataTables 原本的陰影，不會破版。） */
.app-content table.table.dataTable > thead > tr > .dtfc-fixed-start:not(:has(+ .dtfc-fixed-start)),
.app-content table.table.dataTable > tbody > tr > .dtfc-fixed-start:not(:has(+ .dtfc-fixed-start)),
.app-content table.table.dataTable > tfoot > tr > .dtfc-fixed-start:not(:has(+ .dtfc-fixed-start)) {
    border-right: 1px solid hsl(var(--border));
}

/* ── S-65.2 既有 .table-stick：表頭那格也要不透明底
   §7 L758-764 只給了 `td.stick` 背景（L760），`th.stick` 只設了 z-index（L761）
   → 表頭那一格是透明的，橫向捲動時會在表頭重演 /center/report 同一個穿透。
   因為 .table-stick 目前 blade 命中 0（死碼）所以一直沒被發現，
   但下面的宣告式 API 會開始產生它 —— 先把缺的這一格補上。
   ★§7 那 7 行一個字都沒改，這裡只是「補上它漏掉的那一格」。 */
.app-content table.table-stick > thead > tr > th.stick {
    background: hsl(var(--card)) !important;
}

/* ── S-65.3 吸頂表頭：把盤點到的 A / B 兩種舊痕跡收斂到同一組宣告
   宣告式：<table data-sticky-head>
   相容別名：.table-sticky-head（舊痕跡 B）、thead.sticky-top（舊痕跡 A）
   ★舊痕跡 B 原本連 position:sticky 都沒有（只有底色）→ 這裡才真的讓它會吸頂。
   ★吸頂的參考對象是「最近的捲動祖先」：包在 .table-responsive 裡就貼容器頂緣，
     沒有捲動祖先就貼視窗頂緣 —— 不需要量任何東西，所以純 CSS 就成立。 */
.app-content table[data-sticky-head] > thead > tr > th,
.app-content table.table-sticky-head > thead > tr > th,
.app-content table > thead.sticky-top > tr > th,
.app-content .table thead.bg-light.sticky-top > tr > th {
    position: sticky;
    top: 0;
    z-index: 3;
    background: hsl(var(--card)) !important;
    box-shadow: inset 0 -1px 0 hsl(var(--border));
}

/* ── S-65.4 左上角那一格同時吸頂又凍結 → 必須是整張表最高層
   不拉高的話它跟吸頂表頭同為 3，靠後面的規則決勝負＝不可控；
   橫捲時會看到第二欄的表頭從它上面壓過去。 */
.app-content table[data-sticky-head] > thead > tr > th.stick,
.app-content table.table-sticky-head > thead > tr > th.stick,
.app-content table[data-sticky-head] > thead > tr > .dtfc-fixed-start,
.app-content table[data-sticky-head] > thead > tr > .dtfc-fixed-end,
.app-content table.table-sticky-head > thead > tr > .dtfc-fixed-start,
.app-content table.table-sticky-head > thead > tr > .dtfc-fixed-end {
    z-index: 4;
}

/* ── S-65.5 hover：凍結欄要跟著整列變色，否則同一列會出現兩種底色
   · .table-stick 的 td.stick 由 §7 L762 處理（--accent），這裡不重複。
   · DataTables 的凍結欄：表格有掛 .table-hover 時，L1888
     `.table-hover > tbody > tr:hover > *` 帶 !important，會蓋過 S-65.1
     （實測亮色 hover 時第一欄與其他欄同為 rgb(244,244,245)，一致）。
     下面這條補的是「表格沒掛 .table-hover」的情形，讓兩種寫法結果一樣。 */
.app-content table.table.dataTable > tbody > tr:hover > .dtfc-fixed-start,
.app-content table.table.dataTable > tbody > tr:hover > .dtfc-fixed-end {
    background-color: hsl(var(--accent));
    color: hsl(var(--accent-foreground));
}

/* ── S-65.6 .table-stick 的右分界線在 tbody 被吃掉了（實測抓到的，不是預防性寫的）
   實測 /order-management/status/new-orders 掛上 data-sticky-cols 後：
     thead 的 th.stick-end  border-right = 1px  ✔（§7 L763 有生效）
     tbody 的 td.stick-end  border-right = 0px  ✘
   原因：L785 `.app-content .table:not(.table-bordered) > tbody > tr > td
        { border-inline-width: 0 }`（全站「只留橫線」的規則）特異度 (0,3,4)，
        壓過 §7 L764 的 `.table-stick > tbody > tr > td.stick-end` (0,2,4)。
   → 凍結區只有表頭那一格有分界線、底下每一列都沒有，邊界斷成一截。
   治法：用 `.stick.stick-end` 兩個 class 把特異度墊到 (0,4,4)，靠特異度贏，
        **不用 !important**（留給日後真的要蓋的人）。
   ★不改 L785：那條是全站表格「只留橫線」的規則，是對的；
     這裡只是宣告「凍結區的邊界是資訊，不是裝飾」——跟 L788 對日曆 .table-bordered
     的例外同一個道理。 */
.app-content table.table-stick > tbody > tr > td.stick.stick-end,
.app-content table.table-stick > tfoot > tr > td.stick.stick-end,
.app-content table.table-stick > tfoot > tr > th.stick.stick-end {
    border-right: 1px solid hsl(var(--border));
}

/* --- S-66. 全站六列版面 SSOT：標題列→頁籤列→篩選列→動作列→表格→頁碼（2026-08-07）---
   業主三句話（同一件事的三個面向）：
     ①「全站的頁面排序邏輯請你遵守 ssot 由上而下分別是
        標題列 頁籤按鈕列 篩選列 動作列 資料表格 頁碼」
     ②「請你全站的這種勾選後才出現動作的設計 不要
        請你動作列常駐在畫面上 動作列要佔一行」
     ③「說明跟動作就是放在右上角，原則上有頁籤就放頁籤那邊，沒頁籤就放動作那邊（新增）」

   ═══ 一、先量再改（改前實測，https://test.2025-seller.mtest，1440 亮色）═══
   六個區塊的 y 座標（區塊不存在就沒有那一格）：
     頁面                              標題列  頁籤列  篩選列  動作列  表格    頁碼
     /order-management/status/receiving  114     －    161.39   隱藏   230.39  457.83
     /order-management/status/pending-payment 114 －  161.39   隱藏   230.39  457.83
     /order-management/status/new-orders 114     －    161.39   隱藏   230.39  667.70
     /order-management/status/ready-to-ship 114  －    161.39   隱藏   226.39  663.70
     /order-management/status/printing   114     －    161.39   隱藏   230.39  457.83
     /order-management/status/shipped    114     －    161.39   隱藏   230.39  583.70
     /order-management/status/cancelled  收掉    －    115      無     184     380.75
     /order-management/status/returned   收掉    －    115      無     180     451.75
     /packaging-machine-…/status/new-order 收掉  －    115      無     180     617.31
     /invoice/main                        66   112.39 165.39   隱藏   230.39  505.58
     /inventory/product                   66     －   113.39   隱藏   178.39  452.58
     /good/warehouse                      66     －   113.39   隱藏   178.39  356.95
     /system-setting/order/cause-draft    66     －     無      隱藏   113.39   無
     /hr/user                             66     －   113.39   隱藏   178.39  983.20
     /pick/userPackage                    66     －   105.39   隱藏   170.39  396.58
     /seller/plan                         66   112.39   無      無      無      無
     /store-setting/store                 66     －   113.39   隱藏   178.39  409.14
     /store-setting/store/modify/base-setting/1 66 112.39 無    無     172.39   無
     /tutorial/packaging-machine         收掉   66（＝篩選列同一條，§S-53 併列）
     /center/report                       66     －   113.39    無     178.39   無
     /ai-tool/suite                       66     －     無      無      無      無
   「收掉」＝includes/_shell.blade.php 把標題與計數搬進頂 bar 之後那一列已空，
   由該檔的 `display:none` 收掉（不是本節做的）。
   「隱藏」＝動作列在 DOM 裡存在但 hidden（＝業主要改掉的那件事）。

   ★結論一（重要）：**順序本身沒有違規**。
     58 支 view 的 blade 原始碼逐支比對六個標記的出現位置，違規 0 支
     （掃描條件與誘餌見 tests/Feature/Theme/SixRowOrderSsotTest.php）。
     所以本節不做「搬動 DOM 把順序喬對」，只做兩件真正壞掉的事：
       · 動作列**看不見**（②）
       · 動作／說明**靠左**（③）
     並把「順序」用測試釘死，避免日後有人插一列插錯位置。

   ═══ 二、(1) 動作列常駐（②）═══
   markup 與停用邏輯在 components/batch-bar.blade.php（見該檔註解）。
   本節只給「未選取」這個狀態的外觀，以及 JS 尚未跑起來之前的過渡保護。
   ★可達性零變化是硬條件：改前未選取＝整條 hidden（不可點、不在 Tab 順序裡），
     改後未選取＝看得見但 disabled/.disabled（Bootstrap 給 pointer-events:none，
     button 的 disabled 本身不可聚焦，a 另補 tabindex="-1"）。
     ⇒ 使用者**多看得到一列**，但能做的事一件都沒有多。 */
/* ★:not([data-fb-batch-keep-enabled]) —— 掛了豁免標記的按鈕不在此列。
     那些按鈕「按下去只是開一個檢視畫面」（目前是出貨分組的「列印」），
     未選取時本來就該可按。少了這個排除，JS 那邊放行也沒用：
     看得到、文案也對，就是 pointer-events:none 讓它點不下去（實測踩過）。 */
[data-fb-batch-bar][data-fb-batch-empty] .btn:not([data-fb-batch-keep-enabled]),
[data-fb-batch-bar][data-fb-batch-empty] button:not([data-fb-batch-keep-enabled]) {
    /* 值取 Bootstrap `.btn:disabled` 的 .65，讓過渡態與 JS 接手後長得一模一樣 */
    opacity: .65;
    /* ★這條只負責「DOMContentLoaded 之前」與「JS 掛掉」這兩個空窗：
         真正的停用是 JS 寫進 disabled / .disabled 的那一份（SSOT 在元件裡）。
         沒有這條的話，空窗期那幾毫秒是可點的 —— 那就是行為變更。 */
    pointer-events: none;
}
/* 未選取時整條列退到次要層級：它常駐在畫面上，不該和「有選取」時一樣搶眼。
   只調透明度不調高度 —— 高度一變就等於又回到「列會跳動」的老問題。 */
[data-fb-batch-bar][data-fb-batch-empty] {
    background: transparent;
}

/* ═══ 三、(2) 說明／動作靠右上（③）═══
   改前實測（1440 亮色，內容區左緣 x=244、右緣 x=1416）：
     /good/warehouse           .fb-ph-actions「新增倉庫」 x=244..326   ← 貼著左緣
     /invoice/main             .fb-ph-actions「新增發票」 x=244..330   ← 貼著左緣
     /seller/plan              .fb-ph-actions「說明 設定」x=244..339   ← 貼著左緣
     /system-setting/…/cause-draft 「設定分類 新增…」    x=244..444   ← 貼著左緣
     /store-setting/…/base-setting/1「儲存」             x=244..288   ← 貼著左緣
   來源是 §S-31①「動作組置左」（那也是業主當年的指令，原文與規則都留著存查）。
   本次業主推翻它：「說明跟動作就是放在右上角」。★新舊指令衝突時以新的為準，
   舊規則一條都不刪，只用更後面、更高權重的規則覆蓋。

   ── 為什麼不是「再給 .fb-ph-actions 一個 margin-left:auto」──
   §S-41 已經把唯一的 auto 邊界給了「更新時間」。再加一個 auto，flex 會把剩餘空間
   **平均分**給兩個邊界 —— 那正是 §S-15／§S-41 記過兩次的「動作鈕浮在畫面正中央」。
   而且訂單列表頁的更新時間已被篩選列接手（§S-53(3) 把原位那份 display:none），
   那些頁面**根本沒有 auto 邊界可用**，只加 margin 會完全沒效果。
   ⇒ 改用一顆 flex 空白填充位（::before, order:90, flex:1）：
     它把剩餘空間吃光，order 比它大的東西自然被推到右端；
     free space 被 flex-grow 吃完之後 auto margin 拿到 0，舊的 auto 規則自動變成無害的死碼
     （不必刪、也不會打架）。整列永遠只有**一個**伸縮邊界。

   ★選擇器刻意加 `:has(...)`：只在這一列真的有「右群組成員」時才長出填充位。
     不加的話，全站任何手刻的 `.page-header.d-flex`（含用 justify-content-center 置中的）
     都會被塞進一顆會膨脹的空白，把置中的東西頂歪。
   ★用 `.page-header.d-flex` 而不是 `.page-header`：<h1> 也帶 page-header 這個 class
     （元件輸出 `<h1 class="page-header mb-0">`），不限定就會在標題裡也長出一顆偽元素。 */
.page-header.d-flex:has(> .fb-ph-actions)::before,
.page-header.d-flex:has(> .fb-ph-freshness)::before,
.page-header.d-flex:has(> .fb-page-help)::before,
.page-header.d-flex:has(> .fb-ph-inline > .fb-page-help)::before,
.page-header.d-flex:has(> .ms-auto)::before {
    content: '';
    order: 90;
    flex: 1 1 auto;
    min-width: 0;
    height: 0;
}

/* 右群組的內部順序：更新時間(98) → 動作組(99) → 說明鈕(100)。
   ★「說明」維持在最右端 —— §S-31② 定下的規則，這裡只是把它排到動作組之後，
     語意（永遠最後一顆）沒有變。
   ★margin-left 一律歸零：auto 邊界只留給填充位一個。 */
/* ★★已被 §S-111 覆蓋（本檔尾，2026-08-10）——「動作組」這一條已對全站零效果。
   實測生效值：order=1、margin-left=0px（1440 與 375 皆同，數據見 §S-111「四、」「八、」）。
   ★原文一個字都沒改、沒刪（禁刪守則），存查用：它記著業主 08-07「說明跟動作就是放在右上角」那次拍板。
   ★★本節其餘規則**仍然是現行有效的**，不要一起當成死碼：
     · ::before 填充位（order:90）—— §S-111 明文依賴它，拿掉會重演「動作鈕浮在正中央」
     · 更新時間(98)／aux slot 的 .ms-auto(99)／說明鈕(100) —— 右群組整組都還在原位
   ★「靠右」從 §S-111 起改由元件的 aux slot 明說（page-header.blade.php L258-266）。 */
.page-header.d-flex > .fb-ph-actions {
    order: 99;
    /* ★這裡是 auto 不是 0，而且**不會**造成 §S-15 那種「兩個 auto 平分」──
       ── 為什麼安全（實測而非推論）──
       flex 的排版順序是「先解 flex-grow，再分 auto margin」。同一行上有填充位
       （flex:1 1 auto）時，剩餘空間已經被它吃光，auto margin 拿到的是 0 → 完全無作用。
       ⇒ 1440 的表現與寫 0 一模一樣（實測 51 個變體，動作組右緣與頁首右緣的距離都是 0px）。

       ── 那為什麼還要寫 auto ──
       375 的頁首會折成三行（標題／副標／更新時間／動作組），**填充位留在第一行**，
       動作組落到第三行時那一行沒有任何伸縮邊界 → 它會貼回左緣。
       實測修前：/good/storageLocation 距右緣 261px、/inventory/composite-product 235px、
       /pick/packageAction 235px（其餘頁面 0px）—— 同一條規則在窄螢幕下表現不一致。
       auto 正好只在「這一行沒有填充位」時才生效，把折行後的動作組也拉到右端。 */
    margin-left: auto !important;
}
.page-header.d-flex > .fb-page-help,
.page-header.d-flex > .fb-ph-inline > .fb-page-help {
    order: 100;
    margin-left: 0 !important;
}
/* 還沒元件化、自己手刻 .ms-auto 動作組的頁首（例：Seller/includes/_plan_tabs 的
   無標題分支）跟著一起靠右，全站同一種節奏。
   §S-31 把它的 margin-left 壓成 0，這裡只補 order —— 兩條合起來就是「靠右但不搶 auto」。 */
.page-header.d-flex > .ms-auto {
    order: 99;
}

/* --- S-67. 頁籤 .fb-tabs / .fb-tab 完全回歸 shadcn 官方值（2026-08-07）-------------
   ★這一行是給 tests/Feature/Theme/ThemeOverrideAnnotationsTest 認的「章節錨點」，
     寫法必須是 `/* --- S-67. …`（S- 前面不可以多 §，見 §S-46 的說明）。

   ── 這一節在做什麼、憑什麼 ──
   業主看過「本專案 vs shadcn 官方」的逐項對照表之後選了 **B：完全照官方**，
   而且是**明知這代表要把他自己先前要過的灰框（§S-49）與 4px 內縮（§S-52）拿掉**
   才選的。所以本節不是「發現 bug 去修」，是「照業主拍板的取捨換一組值」。
   ★ §S-47 / §S-49 / §S-52 的原文一行都沒刪 —— 它們記的是當時為什麼那樣調，
     那些理由本身沒有錯（例如 §S-47 量出的「亮色 chip 與軌道只差 6／255」是事實），
     只是業主選擇用官方的做法去承擔它。本節排在後面接手。

   ── 六項對照（左＝本專案改前實際生效值，右＝官方）──
     ① 軌道內距        4px（§S-52）                → 3px（`p-[3px]`）
     ② 軌道背景        hsl(--border)（§S-47）       → hsl(--muted)（`bg-muted`）
     ③ 軌道外框        1px --muted-foreground/.28（§S-49） → 無
     ④ 選中片髮絲邊    1px --muted-foreground/.22（§S-49） → 無
     ⑤ 觸發器左右內距  12px（§13 「略放寬給中文」）  → 8px（`px-2`）
     ⑥ 選中片陰影      Tailwind v3 的 shadow-sm
                       `0 1px 2px 0 rgb(0 0 0/.05)`（§13 沿用舊名）
                                                    → v4 的 shadow-sm
                       `0 1px 3px 0 rgb(0 0 0/.1), 0 1px 2px -1px rgb(0 0 0/.1)`

   ★★⑥ 與 ② 是同一套設計的兩半，**必須一起改**。
     官方的選中片與軌道對比本來就只有 6／255（亮色 --muted 244 vs --background 250），
     它是**靠陰影把白 chip 浮起來**，不是靠色差。
     只退軌道背景卻不升陰影 → 選中片會真的讀不出來（§S-47 當初把軌道改成 --border
     就是為了迴避這一點）。本節兩條一起下，並在下面用像素實測驗證陰影真的畫得出來。

   ── 刻意不動的東西 ──
   · `.fb-tabs--flow`（箭頭步驟條）：本節每一條選擇器都帶 `:not(.fb-tabs--flow)`，
     §S-50 / §S-51 / §S-52 那一整套 clip-path 與 ::before 接縫完全不受影響。
   · `.fb-tabs--touch`（POS 側欄，§S-57）的**觸發器內距**：那是 44px 觸控目標的一部分，
     把 15px 壓成 8px 會讓手指打不準 —— ⑤ 這一條額外排除它（`:not(.fb-tabs--touch)`）。
     軌道底／外框／陰影它照樣跟著官方走（那些不影響觸控面積）。
   · 暗色的軌道底：本主題 `--muted` 與 `--border` 在 html.dark **是同一個值**
     （都是 240 3.7% 15.9%），所以 ② 在暗色是零變化，這一點量得出來（見下方實測）。 */

/* ①②③ 軌道 */
.fb-tabs:not(.fb-tabs--flow) {
    padding: 3px;
    background: hsl(var(--muted));
    border: 0;
}

/* ⑤ 觸發器左右內距 12px → 8px。
   ★要蓋掉的是 §S-52 收尾那兩條（`.fb-tabs:not(.fb-tabs--flow) > .fb-tab` 與
     `:first-child, :last-child` 各自寫死了 padding-left: .75rem），
     所以這裡用**同樣的選擇器**、靠排在後面取勝，特異度不變、原文不動。 */
.fb-tabs:not(.fb-tabs--flow):not(.fb-tabs--touch) > .fb-tab {
    padding-left: .5rem;
    padding-right: .5rem;
}
.fb-tabs:not(.fb-tabs--flow):not(.fb-tabs--touch) > .fb-tab:first-child,
.fb-tabs:not(.fb-tabs--flow):not(.fb-tabs--touch) > .fb-tab:last-child {
    padding-left: .5rem;
}

/* ④⑥ 選中片：拿掉髮絲邊、換上 v4 的 shadow-sm。
   ★border-color 用 transparent 而不是 `border: 0`：§13 的 `.fb-tab` 有
     `border: 1px solid transparent`，那 1px 參與了盒模型（含 .fb-tab 的高度計算）。
     直接把 border 拿掉會讓選中片比未選中片矮 2px、切換時整排跳動。
     官方也是同一個做法（未選中與選中的盒子一樣大，只有底色與陰影在變）。 */
.fb-tabs:not(.fb-tabs--flow) > .fb-tab.active {
    border-color: transparent;
    box-shadow: 0 1px 3px 0 rgb(0 0 0 / .1), 0 1px 2px -1px rgb(0 0 0 / .1);
}

/* 官方有、本專案原本沒有的暗色未選中前景：`dark:text-muted-foreground`（實心）。
   本專案原本是 `hsl(--foreground / .6)`（半透明）。
   實測兩者在暗色軌道上幾乎同色（合成 rgb(166,166,167) vs 實心 rgb(161,161,170)），
   對比 6.12:1 → 5.87:1，都遠過 AA；換成實心是為了與官方逐字一致，
   也少一層「底色一變字就跟著變」的隱性耦合。 */
html.dark .fb-tabs:not(.fb-tabs--flow) > .fb-tab {
    color: hsl(var(--muted-foreground));
}


/* --- S-68. --background 回歸 shadcn 官方純白，＋暗色選中片照官方「渲染結果」（2026-08-07）---
   ★這一行是給 tests/Feature/Theme/ThemeOverrideAnnotationsTest 認的「章節錨點」，
     寫法必須是 `/* --- S-68. …`（S- 前面不可以多 §，見 §S-46 的說明）。

   業主 2026-08-07 定調：「你都要依照 shadcn 為主，他的設計是最好的設計」「做 依照 shadcn 原生治本」。
   本節動兩顆，兩顆都是「官方是什麼就是什麼」，不是我覺得哪樣好看。

   ── 上游原文（2026-08-07 現抓，不是憑記憶；三份都附來源，值逐字照抄）──
     A. https://raw.githubusercontent.com/shadcn-ui/ui/main/apps/v4/app/globals.css
          :root { --background: oklch(1 0 0); --card: oklch(1 0 0); … }
          .dark { --background: oklch(0.145 0 0); --card: oklch(0.205 0 0);
                  --border: oklch(1 0 0 / 10%); --input: oklch(1 0 0 / 15%); … }
     B. https://raw.githubusercontent.com/shadcn-ui/ui/main/apps/v4/public/r/themes.css
          .theme-zinc { --background: 0 0% 100%; --card: 0 0% 100%; --border: 240 5.9% 90%; … }
          .dark .theme-zinc { --background: 240 10% 3.9%; --border: 240 3.7% 15.9%; --input: 240 3.7% 15.9%; … }
        （本主題的中性色整組就是這份 zinc 的 HSL，逐顆對得上 —— 見本節末的偏離總表）
     C. https://raw.githubusercontent.com/shadcn-ui/ui/main/apps/v4/registry/new-york-v4/ui/tabs.tsx
          TabsList    … rounded-lg p-[3px] text-muted-foreground …  variant default: "bg-muted"
          TabsTrigger … border border-transparent px-2 py-1 text-foreground/60 …
                        dark:text-muted-foreground dark:hover:text-foreground
                        data-[state=active]:bg-background data-[state=active]:text-foreground
                        dark:data-[state=active]:border-input
                        dark:data-[state=active]:bg-input/30
                        dark:data-[state=active]:text-foreground
                        group-data-[variant=default]/tabs-list:data-[state=active]:shadow-sm

   ═══════════════════════════════════════════════════════════════════════════
   ① --background：`0 0% 98%` → `0 0% 100%`
   ═══════════════════════════════════════════════════════════════════════════
   官方（A 的 oklch(1 0 0)、B 的 `0 0% 100%`）**兩份都是純白**，本主題卻是 98%。
   這一顆是「照抄官方卻不好看」的隱形根因：官方所有「白片浮在頁面上」的設計
   （Card、Tabs 選中片、Popover、Input）都是在**純白頁面**上算的，
   它們靠 border 與 shadow 分辨，不靠底色差。頁面底一旦被墊高到 98%，
   白片與頁面的色差就從 0 變成 5／255 —— 看起來像「有一點點差、又不夠看」，
   於是歷代章節就會去補色差（§S-47 把頁籤軌道從 --muted 壓到 --border 就是這樣來的），
   愈補離官方愈遠。治本是把地基放回純白。

   ── ★★連鎖後果先量再改：卡片會不會消失 ──
   官方 --background 與 --card 都是純白，卡片**只靠 border 分辨**。
   本專案 blade 大量寫 `class="card border-0"`，Bootstrap 的 `.border-0` 帶 !important，
   照理會把邊框整條吃掉 → 底色一變白，卡片就會消失在背景裡。
   先掃再改（掃 resources/views/ 全部 blade）：
       含獨立 `card` class 的節點       318
         其中 `border-0`                253（79.6%）  ← 就是會消失的那一批
         其中另外寫了 border 系 class     9（2.8%）
         兩者皆無                        56（17.6%）
   ⇒ 這件事**本檔早就處理掉了**：§5（L579）與 §23（L1896）都寫了
       `.card.border-0 { border: 1px solid hsl(var(--border)) !important; }`
     `.card.border-0` (0,2,0) 特異度高於 utility `.border-0` (0,1,0)，兩邊同為 !important
     → 卡片一律有 1px --border 細框。所以本節**不需要**再為卡片補任何規則，
     也不必去動 blade（那不是設計師的檔）。

   ── 改前／改後的實測（無頭 Chrome，https://test.2025-seller.mtest/，38 支頁面 × 亮暗 × 1440/375）──
                                改前            改後          官方（B zinc）
       頁面底                   rgb(250,250,250) rgb(255,255,255) 純白
       卡片底                   rgb(255,255,255) rgb(255,255,255) 純白
       卡片底 vs 頁面底          5／255           0／255          0／255   ← 與官方一致
       卡片框 #E4E4E7 vs 頁面底  22／255          27／255         27／255  ← ★邊界對比反而**變強**
       像素掃描（跨卡片左緣）     maxStep 27       maxStep 27      ——      ← 邊界照樣畫得出來
   ★所以「底色差沒了＝卡片消失」在這個專案不成立：分辨卡片的本來就是那條 border，
     而 border 對頁面底的對比從 22 升到 27。這是量出來的，不是推論。

   ── 順帶被扶正的兩處（都是改前量到的既有怪值，本節沒有為它們寫任何規則）──
   · 左側主選單 `.app-sidebar` 吃 --sidebar-bg（`0 0% 98%`）＝ 改前與頁面底**逐位元組相同**
     （色差 0／255，等於側欄與內容區在亮色下根本沒有邊界）。改後 250 vs 255 → 5／255，
     正好回到上游的關係（A 的 --sidebar: oklch(0.985) vs --background: oklch(1)）。
   · POS 的 `.pos-menu` 軌道（--muted 244）對頁面底 6／255 → 11／255。
   ★這兩處都不是本節新增的規則，是 ① 這一顆 token 歸位之後自然對上的。

   ── 明講「會變、但那就是官方」的一處 ──
   `.form-control / .form-select` 吃 `hsl(var(--background))`（§6 L674、§25 L2009）。
   改後輸入框底色 250 → 255，也就是在白卡片上變成**齊平白底＋1px --input 框**。
   官方 Input 正是 `bg-transparent`＋`border-input`（暗色才另外疊 `dark:bg-input/30`），
   所以這是回到官方而不是壞掉；改前後輸入框對卡片底的邊框對比同樣是 22 → 27（變強）。
   實測（/hr/user/create、/pos/machine 兩支代表頁）：底色差 5 → 0、邊框差 22 → 27。

   ═══════════════════════════════════════════════════════════════════════════
   ② 暗色頁籤選中片：照官方的**渲染結果**，不是照同名 token
   ═══════════════════════════════════════════════════════════════════════════
   官方寫的是 `dark:data-[state=active]:bg-input/30`。
   ★★這裡有一個 false friend：**同名 token，兩邊意思完全不同**。
       上游暗色  --input: oklch(1 0 0 / 15%)   ← 是「白色疊層」，本身就帶 alpha
       本主題暗色 --input: 240 3.7% 15.9%       ← 是「不透明 zinc-800」，與 --muted／--border 逐字相同
     Tailwind 的 `/30` 是**改寫 alpha** 而不是相乘，所以官方 `bg-input/30` 的實際顏色
     ＝ 白色 30%；照抄本主題的 --input 則是「zinc-800 的 30% 疊在 zinc-800 軌道上」
     ＝ 合成回同一顆 zinc-800，**色差歸零，選中片直接消失**。
   ⇒ 要照官方就得照它的渲染結果寫：白色 30%。同理 `dark:data-[state=active]:border-input`
     ＝ 白色 15%。

   ★★★這是**外觀翻面**，不是微調，必須講明白：
       改前 暗色是「深 chip（rgb(9,9,11)）浮在淺軌道（rgb(39,39,42)）上」
       改後 暗色是「亮 chip（rgb(104,104,106)）浮在深軌道（rgb(39,39,42)）上」
     亮色維持「白 chip 浮在淺灰軌道上」不變 —— 也就是改後**亮暗兩邊的方向終於一致**
     （選中的那一片一律比軌道亮），改前是相反的。這正是官方的樣子，但畫面會明顯不同。

   ── ★同時修掉一個 §S-67 帶進來的既有缺陷（不是新需求，是 ② 的必要條件）──
   §S-67 為了對齊官方的 `dark:text-muted-foreground`，加了
     `html.dark .fb-tabs:not(.fb-tabs--flow) > .fb-tab { color: hsl(var(--muted-foreground)); }`  (0,4,1)
   它的特異度蓋過了 §13 的 `.fb-tab.active { color: hsl(var(--foreground)); }` (0,2,0)
   → **暗色的選中片與未選中片文字色逐位元組相同**（改前實測兩者都是 rgb(161,161,170)）。
   官方沒有這個問題：`dark:data-[state=active]:text-foreground` 與 `dark:text-muted-foreground`
   在 Tailwind 是同特異度、靠**書寫順序**決勝，active 那條寫在後面所以贏。
   ★這件事在改 ② 之前只是「少一個線索」（底色差 30 還撐得住），
     改 ② 之後就會變成硬傷：muted-foreground 疊在 rgb(104,104,106) 上只有 **2.16:1**，
     遠低於 AA。所以 ② 與這條 color **必須同時下**，只做一半會交付一個讀不到字的畫面。
   ⇒ 本節的規則把 color 一起寫回 --foreground，(0,5,1) 蓋過 §S-67 的 (0,4,1)。

   ── 改前／改後的實測（/ai-tool/alpha、/seller/plan、/pos/machine、/store-setting/api-platform/create）──
                                      改前              改後
       暗 軌道底                       rgb(39,39,42)     rgb(39,39,42)      不變
       暗 選中片（合成後）              rgb(9,9,11)       rgb(104,104,106)
       暗 選中片 vs 軌道 色差           31／255           65／255
       暗 選中片髮絲邊（合成後）        無（transparent） rgb(127,127,129)
       暗 選中片文字                   rgb(161,161,170)  rgb(250,250,250)
       暗 選中片文字對比               7.76:1（但與未選中**同色**）  5.33:1（且與未選中明顯不同）
       暗 未選中文字                   rgb(161,161,170)  rgb(161,161,170)   不變（5.81:1）
       亮 全部                         不變              不變

   ── 刻意不動的東西 ──
   · `.fb-tabs--flow`（箭頭步驟條）：本節的選擇器帶 `:not(.fb-tabs--flow)`，
     §S-50／§S-51／§S-52 那一整套 clip-path 與 ::before 接縫完全不受影響。
   · `html.dark { --input }` 本身**不動**。把它改成白色疊層才是「與上游逐字一致」，
     但它同時是全站表單框線（`border: 1px solid hsl(var(--input))`）的來源，
     血緣所及是每一支表單頁 —— 那是另一個量級的決定，列在下面的偏離總表裡交給業主拍板。
   · §S-67 的六條官方值一條都沒碰（軌道 3px／--muted／無外框、選中片 border-color:transparent、
     觸發器 px-2、v4 shadow-sm）；本節只在**暗色**再疊一層，亮色一個字都沒改。

   ═══════════════════════════════════════════════════════════════════════════
   ③ 其餘偏離上游的 token —— 只盤點，本節一顆都不改（等業主拍板）
   ═══════════════════════════════════════════════════════════════════════════
   基準＝上面 B 的 `.theme-zinc`（本主題中性色的真正血緣），另註 A（v4）的差異。
   「刻意」＝檔內已有註解寫明理由；「未記錄」＝查無理由，建議列入下一輪。

     token                本專案            上游 zinc          性質
     ─────────────────────────────────────────────────────────────────────
     :root
     --background         0 0% 98%          0 0% 100%          ★本節已改回純白
     --primary            49.4 100% 50%     240 5.9% 10%       刻意（品牌金黃 #FFD200）
     --primary-foreground 0 0% 10.2%        0 0% 98%           刻意（黃底恆黑字）
     --secondary-foreground 240 10% 3.9%    240 5.9% 10%       未記錄（差 6.1% 亮度，肉眼難辨）
     --accent-foreground  240 10% 3.9%      240 5.9% 10%       未記錄（同上）
     --ring               240 10% 3.9%      240 5.9% 10%       未記錄（同上）
     --destructive-foreground 0 0% 100%     0 0% 98%           未記錄（純白 vs 98%）
     --radius             0.625rem          0.5rem             刻意（對齊 App ShadThemeData
                                                               與 A 的 v4 值，v4 就是 0.625rem）
     --foreground / --card / --card-foreground / --popover / --popover-foreground /
     --secondary / --muted / --muted-foreground / --accent / --border / --input
                          逐顆與上游 zinc **完全相同**（--muted-foreground 4.40:1 的取捨見 §1 註解）
     --destructive        0 84.2% 60.2%     0 84.2% 60.2%      相同（但被 §S-39 覆蓋成 0 72.2% 50.6%，有註解）

     html.dark
     --card / --popover   240 5.9% 10%      240 10% 3.9%       ★偏離 zinc，但**方向與 v4 一致**
                                                               （v4 dark --card: oklch(0.205) 比
                                                                --background: oklch(0.145) 亮）
                                                               ⇒ 建議維持，並在下一輪改記成「對齊 v4」
     --ring               0 0% 98%          240 4.9% 83.9%     已記錄（§1b 有註解，拉到純白）
     --destructive        0 72.2% 50.6%     0 62.8% 30.6%      刻意（§S-39 有註解：白字要過 AA）
     --border             240 3.7% 15.9%    240 3.7% 15.9%     與 zinc 相同；★但 A（v4）是
                                                               `oklch(1 0 0 / 10%)`＝白色 10% 疊層
     --input              240 3.7% 15.9%    240 3.7% 15.9%     與 zinc 相同；★但 A（v4）是
                                                               `oklch(1 0 0 / 15%)`＝白色 15% 疊層
                                                               ⇒ 就是 ② 那顆 false friend 的來源。
                                                                 要不要整組換成疊層＝**待業主拍板**
     --accent             240 3.7% 15.9%    240 3.7% 15.9%     與 zinc 相同；A（v4）是 oklch(0.371)（更亮）
     --background / --foreground / --card-foreground / --popover-foreground /
     --secondary / --secondary-foreground / --muted / --muted-foreground / --accent-foreground
                          逐顆與上游 zinc **完全相同**

     上游沒有、本專案自建（不算偏離，是專案語彙；列出來只為完整）
     --primary-light / --primary-dark / --hint / --dark / --dark-foreground /
     --dark-panel / --dark-panel-foreground / --success* / --info* / --warning* /
     --success-bg / --success-fg / --warning-bg / --warning-fg / --destructive-fg /
     --leave-annual / --leave-sick / --leave-personal / --early-leave /
     --chart-1…--chart-8 / --radius-sm/md/lg/xl / --link / --link-hover /
     --sidebar-bg / --sidebar-fg / --sidebar-fg-strong / --sidebar-border /
     --sidebar-accent / --sidebar-active-bg / 以及所有 --bs-* 橋接變數
     ★側欄那組的命名與上游不同（上游是 --sidebar / --sidebar-foreground / --sidebar-accent…），
       值的關係倒是對得上（--sidebar-bg 98% vs 上游 oklch(0.985)）。改名是跨檔重構，不在本節。
   ------------------------------------------------------------------------- */


/* ① --background 回歸官方純白。
   ★這是一個 `:root` 區塊，與 §1 的 `:root` 同特異度 (0,1,0)、排在後面 → 亮色由本節勝出。
     `html.dark` 是 (0,1,1)，特異度更高 → **暗色完全不受影響**（暗色仍是 §1b 的 240 10% 3.9%）。 */
:root {
    --background: 0 0% 98%;
}


/* ② 暗色選中片：`dark:data-[state=active]:bg-input/30` + `dark:data-[state=active]:border-input`
      的**渲染結果**（白色 30% ／ 白色 15%），外加把 §S-67 蓋掉的 active 文字色寫回官方值。
   ★三條必須同時存在（理由見上：只做底色會讓文字掉到 2.16:1）。
   ★特異度 (0,5,1)：蓋得過 §S-67 的 `html.dark …> .fb-tab` (0,4,1) 與 §13 的 `.fb-tab.active` (0,2,0)；
     `border-color` 蓋得過 §S-67 同名選擇器的 transparent（同分、在後）。
   ★不寫成 `hsl(var(--input) / .3)` —— 本主題的 --input 是不透明 zinc，那樣寫色差會歸零（見上）。 */
html.dark .fb-tabs:not(.fb-tabs--flow) > .fb-tab.active {
    background: hsl(0 0% 100% / .3);
    border-color: hsl(0 0% 100% / .15);
    color: hsl(var(--foreground));
}


/* --- S-69. 動作鈕「主要靠左、次要靠右」訂正 ＋「列印內容選擇」對話框收斂 shadcn（2026-08-07）---
   ★這一行是給 tests/Feature/Theme/ThemeOverrideAnnotationsTest 認的「章節錨點」，
     寫法必須是 `/* --- S-69. …`（S- 前面不可以多 §，見 §S-46 的說明）。

   ═══ 一、業主原話（同一天兩句，講同一件事）═══
     ①「等下我不是已經說 新增訂單按鈕 重要按鈕都要置左
        不重要的 說明 更新時間 才放右邊 置右」
     ②「/order-management/orders/create 儲存按鈕 新增按鈕 這種很重要的按鈕 都要置左
        不重要的資訊跟按鈕才可以置右」

   ★這是在**訂正 §S-66 三、(2)**：那一節我把整組動作鈕推到右邊了，做反了。
     §S-66 的原文一行都沒有刪（它引用的業主原話「說明跟動作就是放在右上角」
     仍然存查，這裡只是後面接手，把「動作」那一半改回左邊；
     「說明／更新時間放右邊」那一半 §S-66 做對了，本節原封不動沿用）。

   ═══ 二、先量再改（改前實測，https://test.2025-seller.mtest，1440 亮色，內容區 x=244..1416）═══
     頁面                                    動作組內容                 改前 x       改前右緣
     /order-management/orders/create         儲存                      1372         1416
     /order-management/status/new-orders     轉移期對帳 新增訂單         1230.58      1416
     /order-management/status/ready-to-ship  新增訂單                   1333.70      1416
     /good/warehouse                         新增倉庫                   1333.70      1416
     /good/storageLocation                   新增儲位                   1333.70      1416
     /invoice/main                           新增發票                   1329.70      1416
     /inventory/product                      新增商品                   1333.70      1416
     /inventory/composite-product            新增複合商品               1308.05      1416
     /hr/user                                新增新建人員               1308.05      1416
     /hr/department                          新增部門                   1333.70      1416
     /pick/packageAction                     新增包裹動作               1308.05      1416
     /system-setting/order/cause-draft        設定分類 新增訂單暫存原因   1216         1416
     /store-setting/store                    新增商店                   1333.70      1416
     /seller/plan                            說明 設定                  1320.69      1416   ← 這一頁**不該動**
   ★1440 時標題已被 includes/_shell.blade.php 搬進頂 bar，所以動作組是這一列唯一的東西，
     改成靠左之後它的 x 就是內容區左緣（244）本身，不是「靠近左邊」。

   ═══ 三、怎麼判「主要」與「次要」（判準是既有 SSOT，不是我現場定義）═══
   元件註解 components/packaging-machine/action-bar.blade.php 5) 已經寫死全站慣例：
     「按鈕顏色維持 btn-dark（近黑）—— 黃底 btn-primary 只給『新增/儲存』。」
   全站盤點（107 支呼叫端、71 支真的有動作組、86 顆按鈕）與這條慣例 100% 吻合：
     btn-primary 的 46 顆全部是「儲存 / 儲存 下一步 / 儲存並查詢 / 新增○○」，
     沒有一顆例外。
   ⇒ **主要動作＝btn-primary**，這正好就是業主點名的那兩種（儲存、新增）。
     所以本節不新增任何 marker class（不用改 71 支呼叫端），
     直接用 `:has(> .fb-ph-actions .btn-primary)` 問「這一列有沒有主要動作」。

   ★沒有 btn-primary 的動作組**維持 §S-66 的靠右**，因為它們全是次要入口：
     說明／設定（/seller/plan）、返回列表、隱藏離線、全部下載＋刪除、操作（下拉）、
     全部已讀、拆單/併單（PackagingMachine 的 modify_item 這一頁沒有儲存）、
     以及發票已開立後的 作廢發票／光貿官方電子發票／手動重開。
     業主①句點名的「說明」就在這一批裡，靠右正是他要的。

   ═══ 四、為什麼不再多加一個伸縮邊界（§S-15／§S-41 記過兩次的坑）═══
   §S-41 把唯一的 `margin-left:auto` 給了更新時間；§S-66 又加了一顆 `::before`
   填充位（order:90, flex:1）當作真正的伸縮邊界，並讓更新時間那個 auto 退化成 0。
   本節**完全不碰那兩個東西**：填充位還在 order:90，動作組只是從 order:99（填充位之後）
   搬回 order:1（填充位之前）。整列仍然只有**一個**伸縮邊界（那顆 ::before），
   所以不會重演「動作鈕浮在畫面正中央」。

   ★margin-left 要寫回 0 而且要 !important：§S-66 給了
     `.page-header.d-flex > .fb-ph-actions { margin-left: auto !important }`。
     那條在 1440 是無害的死碼（填充位先吃光空間 → auto 拿到 0），
     但在 375 折行、動作組落到「沒有填充位的那一行」時它會真的生效，把動作組推到右緣
     —— 那正是本節要修掉的行為。本節選擇器特異度 (0,5,0) > §S-66 的 (0,3,0)，
     再加 !important 才壓得住。
     ★不刪 §S-66 那條：它對「沒有 btn-primary 的動作組」仍然是現行有效規則。 */
/* ★已被 §S-111 併入（本檔尾，2026-08-10）：靠左已經是**全站預設**，
   本條與 §S-111 宣告值逐字相同（order:1／margin-left:0 !important），
   特異度 (0,5,0) 仍高於 §S-111 的 (0,4,0) ⇒ 這 64 頁走的還是本條，行為逐位不變。
   ★原文一字未改、未刪。★但「.btn-primary ＝ 主要動作」這個**替身指標**已不再是判準：
     新頁面不必為了靠左而把按鈕塗黃，也不必掛標記 —— 預設就是靠左。 */
.page-header.d-flex:has(> .fb-ph-actions .btn-primary) > .fb-ph-actions {
    /* order 1 ＝ 排在標題／計數 chip／副標（order 0）之後、填充位（order 90）之前。
       ★不是 -1：標題（h1）仍然要在最前面。1440 時標題被 _shell 搬走了，
         動作組自然落在內容區左緣；375 時標題留在原位，動作組緊跟在標題後面，
         兩種情況都是「左邊那一群」。 */
    order: 1;
    /* §S-66 的 `margin-left:auto !important` 在此失效（見上面第四段） */
    margin-left: 0 !important;
}

/* ★刻意**不做**的事：不在動作組內部再用 order 把 btn-primary 抽到最前面。
   會想做是因為 5 支頁面的 DOM 順序是「次要在前、主要在後」：
     /order-management/status/new-orders      轉移期對帳 → 新增訂單
     /system-setting/order/cause-cancelling   設定分類 → 新增訂單取消原因
     /system-setting/order/cause-draft        設定分類 → 新增訂單暫存原因
     /system-setting/order/cause-returning    設定分類 → 新增訂單退貨原因
     StoreSetting/Store/_edit_tabs            重設為預設值 → 儲存
   但用 CSS order 換視覺順序**不會**跟著換 Tab 焦點順序（焦點永遠走 DOM 順序），
   看到的第一顆和 Tab 到的第一顆會對不起來 —— WCAG 1.3.2／2.4.3 的典型破口。
   為了差 70~95px 的排序去換一個可及性缺陷不划算，
   ⇒ 整組一起靠左、組內順序原封不動；真要換順序應該去改那 5 支 blade 的 DOM
   （不在本輪的檔案所有權內，已寫進回報請業主裁示）。 */

/* ═══ 五、「列印內容選擇」對話框（#shipmentGroupModal）═══
   業主附圖：標題列深色底白字、表格是舊語彙、按鈕不是 shadcn。

   ── 上游原文（2026-08-07 現抓，不是憑印象）──
     shadcn-ui/ui  apps/v4/registry/new-york-v4/ui/dialog.tsx
       DialogContent : "… gap-4 rounded-lg border bg-background p-6 shadow-lg …"
       DialogHeader  : "flex flex-col gap-2 text-center sm:text-left"      ← **沒有任何底色**
       DialogTitle   : "text-lg leading-none font-semibold"
       DialogFooter  : "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end"
                       showCloseButton → <Button variant="outline">Close</Button>
     shadcn-ui/ui  apps/v4/registry/new-york-v4/ui/table.tsx
       TableHead : "h-10 px-2 text-left align-middle font-medium … text-foreground" ← **沒有底色**
       TableRow  : "border-b …"                                            ← 只有列底線，沒有直向格線
   ⇒ 官方的 Dialog 標題列就是**和 body 同底色**、Table 表頭就是**透明底**、
     格線只有橫的。所以深色標題列與 `table-bordered` 的方格都是偏離，不是風格選擇。

   ── 為什麼修在 blade 不修在這裡 ──
   本專案**已經有**一份 shadcn 化完成的同一顆對話框：
     resources/views/PackagingMachine/Order/_shipment_group_modals.blade.php
   它的 `#shipmentGroupModal` 用的就是素面 `modal-header` + 素面 `btn-close`
   + `btn-sm btn-white` 關閉鈕，表格是 `table table-panel`、按鈕是 `btn-dark` / `btn-white`
   / `btn-outline-dark`。既然慣例已經存在，就把 Order/ReadyToShip 那一份**收斂到它**，
   而不是在主題層再發明第三種寫法（會變成同一顆 modal 兩套規則互相蓋）。
   ⇒ 本節對這顆 modal **不寫任何規則**，改動全部在
     resources/views/Order/ReadyToShip/index.blade.php（見該檔註解），
     這裡只留下判準與上游出處，方便日後查。
   ★★訂正（同日稍晚，上面那一句已經不成立，原文保留存查）：
     收斂完成後實測「貨／條」勾選鈕，發現**選中與未選中的 computed 值一模一樣**
     —— 那是 class 層修不掉的（病根在本檔 L2088 對 outline 系列的收斂），
     所以本節末尾第六段確實補了三條規則。除那三條之外，這顆 modal 的其餘改動
     仍然全部在 blade，沒有第二處。 */

/* ═══ 六、「貨／條」列印項目勾選鈕：選了看不出來（2026-08-07 實測發現）═══
   ★這是量出來的，不是看出來的：把「貨」點成勾選狀態之後，
     background-color 仍然是 rgb(244, 244, 245)、color 仍然是 rgb(9, 9, 11)
     —— 和未勾選**逐字元完全相同**。使用者按下去畫面毫無回饋。

   ── 根因（源碼查證，不是猜）──
   本檔 L2088 那一條把所有 outline 系列收在一起：
     `.btn-outline-secondary, .btn-outline-success, .btn-outline-info,
      .btn-outline-warning, .btn-outline-primary, .btn-outline-dark {
         … --bs-btn-active-bg: hsl(var(--accent)); --bs-btn-active-color: hsl(var(--accent-foreground)); }`
   而未選中態的底也是 --accent → 選中／未選中同一個值。
   ★所以這**不是本輪改 btn-outline-success → btn-outline-dark 造成的**：
     那條規則的清單裡 -success / -info / -dark 三個都在，改前改後吃的是同一組值。
     （改前沒被抓到，是因為 `table-bordered` 的滿版格線讓整格看起來就是灰的。）

   ── 為什麼既有的 §btn-check 選中態 救不到它 ──
   本檔 §btn-check 選中態（L6018 起）已經把同一個病治過一次，
   但它的選擇器是 `.btn-check:checked + .btn-outline-dark` ——
   Bootstrap 的「input 當 label 的**前一個兄弟**」寫法。
   出貨分組這兩顆用的是**另一種**寫法：`<label class="btn"><input class="d-none">貨</label>`
   （input 在 label **裡面**），狀態由 JS 寫 `.active` 到 label 上。
   兄弟選擇器對不到它，所以它一直沒吃到那份治療。

   ── 上游怎麼做（2026-08-07 現抓）──
     shadcn-ui/ui apps/v4/registry/new-york-v4/ui/toggle.tsx
       base    : "… data-[state=on]:bg-accent data-[state=on]:text-accent-foreground …"
       outline : "border border-input bg-transparent shadow-xs hover:bg-accent …"
     ⇒ 官方 Toggle：**未選＝透明底＋--input 邊框**、**已選＝--accent 底**。
   ★這裡採「未選照官方、已選照本專案 §btn-check 選中態（--dark 實心）」：
     官方那個 --accent 已選底，正是本專案 §btn-check 選中態 當年判定
     「選中/未選中幾乎看不出差別」而否決掉的值（那一節的原文還在，理由是實測不是喜好）。
     同一個 App 裡同一種控制項只能有一種選中語彙 —— `.btn-check:checked + .btn-outline-dark`
     已經是 --dark 實心，這裡跟它一致，不另立第三種。
     未選中則**不**照 §btn-check 的 ghost（它有軌道當底，看得出是控制項），
     這兩顆是表格儲存格裡的兩個孤立標籤，拿掉邊框就沒有可點的樣子了 ——
     改吃官方 outline 的「透明底 + --input 邊框」，可點感與選中對比同時成立。

   ★作用範圍就是這兩支檔（全站 grep `data-print-option` 只有它們）：
       resources/views/Order/ReadyToShip/index.blade.php
       resources/views/PackagingMachine/Order/_shipment_group_script.blade.php
     兩邊 markup 逐字相同，所以這條同時修好兩處（PM 那份目前只靠一顆 check icon 表示狀態，
     底色一樣是分不出來的；本條讓它也有底色差異，icon 照舊不動）。
   ★`:has(> input)` 限定直接子層，避免把任何「內部剛好有 checkbox」的按鈕掃進來。

   ★★第一版寫成 `.btn:has(> input[type="checkbox"][data-print-option])` —— **它有一個洞，
     是實測抓到的不是看出來的**（我一度以為寫完就算數，那正是本輪要避免的錯）：
     總計列（ALL）的「條」是停用態，它的 markup 是
       `<input type="checkbox" class="d-none" disabled>`
     —— **沒有 data-print-option 這個屬性**（`renderPrintButtons()` 的 isAll 分支特地拿掉了）。
     於是那一顆完全落在選擇器外：隔壁的「貨」已經變成透明底，它卻還留著舊的 --accent 實心底，
     變成「停用的那顆看起來比可用的那顆更強」。
     ⇒ 改用 `#shipmentGroupBody` 當作用域（兩支實作的容器 id 逐字相同，是現成的共同錨點），
       條件放寬成「直接子層是 checkbox 的 .btn」，三種狀態一次覆蓋。
     ⇒ 同時 `:disabled` 那一條原本掛在 `[data-print-option]:disabled` 上，
       在現行 markup 下**永遠選不到任何元素**（＝死碼），一起改掉。 */
#shipmentGroupBody .btn:has(> input[type="checkbox"]) {
    /* 官方 toggle outline：`border border-input bg-transparent` */
    background-color: transparent;
    border-color: hsl(var(--input));
    color: hsl(var(--foreground));
}
/* ★停用態（總計列的「條」）**刻意不寫規則**，理由是 CDP 實測出來的，不是省略：
     `CSS.getMatchedStylesForNode` 顯示那顆 label 的 background-color 勝出者是
       §37 按鈕 disabled 狀態（本檔 L2619 起）
       `.btn:disabled, .btn.disabled, fieldset:disabled .btn {
           background-color: hsl(var(--muted)) !important;
           color: hsl(var(--muted-foreground)) !important;
           border-color: hsl(var(--border)) !important; }`
     —— 它帶 !important，我上面那條 (1,2,1) 的規則壓不過（我第一版真的寫了一條
     `color: --muted-foreground` 想處理它，實測發現①壓不過②值本來就一樣＝純噪音，已撤掉）。
   ★而且**不該**壓過去：§37 那條是全站停用鈕的 SSOT（當年是為了修「白字白底白邊、對比 1:1」
     才加的 !important），為了這顆 modal 破例，等於在停用語彙上開第二套。
   ★實測停用態長相：亮 bg rgb(244,244,245) / color rgb(113,113,122) ＋ inline opacity .5，
     旁邊可用的「貨」是透明底＋1px 邊框 —— 兩者分得出來，沒有壞掉，所以維持現狀。 */
#shipmentGroupBody .btn:has(> input[type="checkbox"]).active {
    /* 值逐字取自本檔 §btn-check 選中態，不是另外挑的 */
    background-color: hsl(var(--dark));
    border-color: hsl(var(--dark));
    color: hsl(var(--dark-foreground));
    box-shadow: none;
}

/* --- S-70. 三支 modal 暗色反白＋關閉鈕消失（blade 修）／btn 變體收斂盤點（2026-08-07）---
   ★這一行是給 tests/Feature/Theme/ThemeOverrideAnnotationsTest 認的「章節錨點」，
     寫法必須是 `/* --- S-70. …`（S- 前面不可以多 §，見 §S-46 的說明）。

   ★★本節**刻意不含任何 CSS 宣告**。這不是寫到一半，是結論：
     本輪兩件事查下來，正確的修法都在呼叫端（blade 的 class 選錯），
     在主題層加規則只會多一套互相蓋的寫法。理由與量測全部留在這裡備查。

   ═══ 一、三支 modal 的暗色反白（真缺陷，已修，改動全在 blade）═══
   #sellerConfirmModal（物流取號）／#platformParamsModal（確認出貨）／
   #downloadSummaryModal（總表下載）三支的標題列都是
     `modal-header bg-dark text-white py-2` ＋ `btn-close btn-close-white`
   —— 和 §S-69 五、修掉的 #shipmentGroupModal 是同一個病，上一輪只修了第四支。

   ── 改前實測（https://test.2025-seller.mtest/order-management/status/ready-to-ship，
      三支 × 亮/暗 × 1440/375 共 12 組；三支的數值逐字相同）──
     亮：header bg = rgb(24, 24, 27)   / color = rgb(250, 250, 250)
         關閉鈕像素取樣 uniq=18、maxDist=198.6、對比 5.21:1（看得見）
     暗：header bg = rgb(250, 250, 250) / color = rgb(24, 24, 27)
         關閉鈕像素取樣 uniq=3、maxDist=5.2、bg=rgb(250,250,250)、ink=rgb(253,253,253)
         ⇒ **對比 1.03:1**：整個 24×24 方格只有 3 種顏色、彼此只差 5.2/255 ⇒ 叉叉不存在。
     對照組（同一輪、同一頁的 #shipmentGroupModal）暗色 uniq=18／對比 5.21:1 ⇒ 素面寫法是好的。
     ★★★已過期／與實測不符（2026-08-08 複驗，走同一頁 /order-management/status/ready-to-ship、
       同一支 #shipmentGroupModal、真的 bootstrap.Modal.show() 開起來後截圖取樣）：
       **暗色 uniq = 5、對比 = 1.09:1**，不是 uniq=18／5.21:1。同一輪量到的 23 支彈窗
       （7 頁）暗色全部是 1.09:1、亮色全部是 3.95:1，沒有一顆例外。
       ⇒ 上面這句「素面寫法是好的」的結論**不成立**：素面 `.btn-close` 在暗色下一樣看不見，
         真正的成因是叉叉的顏色寫死在背景圖 data URI 裡（`fill='%23000'`）。
       ⇒ 治法與完整實測見 §S-82（2026-08-08）。原文一個字都沒有刪，保留以資對照。
   ★★這是**像素取樣**量的，不是只讀 computed —— computed 只會告訴你
     `filter: invert(1) grayscale(1) brightness(2)`，看不出來「叉叉和底同色」。

   ── 為什麼不在這裡寫 CSS ──
   本專案已經有一份 shadcn 化完成的**同樣這三支** modal：
     resources/views/PackagingMachine/Order/_shipment_group_modals.blade.php L2 / L84 / L133
   把該檔 L1~L157 與 Order/ReadyToShip/index.blade.php 對應段落逐行 diff，
   **除了這三行 header 之外一字不差**。既然 SSOT 已經存在，就把呼叫端對齊過去；
   在主題層寫 `#sellerConfirmModal .modal-header { … }` 等於替 `bg-dark` 開一個例外，
   下一支頁面照抄 `bg-dark` 時同樣會壞，而且會多一條只對三個 id 生效的死規則。
   ⇒ 改動全部在 resources/views/Order/ReadyToShip/index.blade.php（見該檔 §S-70 註解）。

   ── 上游依據（shadcn-ui/ui apps/v4/registry/new-york-v4/ui/dialog.tsx，2026-08-07 現抓）──
     DialogHeader  : "flex flex-col gap-2 text-center sm:text-left"   ← 沒有任何底色
     DialogContent : "… gap-4 rounded-lg border bg-background p-6 shadow-lg …"
   官方 Dialog 的標題列與 body 同一塊 bg-background，沒有深色 bar。

   ═══ 二、btn 變體收斂盤點：語意只存在於 class 名字裡（先盤點，不大改）═══

   ── 事實（在真實頁面、真主題下量的，不是讀 CSS 推的）──
   §28（本檔 L2088 附近）那一條把 11 個 class 收成同一組 `--bs-btn-*`：
     .btn-secondary / .btn-success / .btn-info / .btn-warning / .btn-light
     ＋ .btn-outline-secondary / -success / -info / -warning / -primary / -dark
   實測渲染（background / color / border-color 三個值逐字元相同）：
     亮：rgb(244, 244, 245) / rgb(9, 9, 11)    / rgb(228, 228, 231)
     暗：rgb(39, 39, 42)    / rgb(250, 250, 250) / rgb(39, 39, 42)
   ★所以受影響的不只是題目說的六個 outline，是 **11 個 class**；
     `btn-success` 和 `btn-outline-secondary` 在畫面上也是同一顆按鈕。
   仍然彼此可分的只有四個：
     btn-primary       亮/暗 皆 rgb(255, 210, 0) / rgb(26, 26, 26)
     btn-dark          亮 rgb(24, 24, 27) 白字 ／ 暗 rgb(250, 250, 250) 黑字
     btn-danger        亮/暗 皆 rgb(220, 38, 38)
     btn-white         亮 rgb(255, 255, 255) ／ 暗 rgb(9, 9, 11)（＋ --border 邊）
     （btn-outline-danger 另有一條 §28 規則，紅底紅字，見下面「已知待裁示」）

   ── 全站活躍用量（resources/views/** ＋ public/assets/js/** ＋ app/**）──
     btn-outline-secondary 26 處 ── 唯一有穩定語意的：唯讀／UI-only（複製、密碼顯隱、展開收合、上一步、列印）
     btn-outline-dark      10 處 ── 可勾選／可選中的中性態（列印項目、物流子路線、圖庫上下張）
     btn-outline-primary    9 處 ── 次級但帶引導性（複製匯款資訊、預覽、快捷全選）
     btn-outline-warning    1 處 ── AiTool/alpha/index.blade.php:597「重新生成」，孤例
     btn-outline-success    0 處 ── 已死，僅存於 Order/ReadyToShip/index.blade.php 的「原文存查」註解
     btn-outline-info       0 處 ── 同上
     btn-outline-danger     0 處 ／ btn-outline-light 0 處
   ★所以「六個色系」裡真正還活著的只有四個，而且其中兩個（secondary vs primary）
     在**同一個動作**上打架：複製匯款資訊，Seller/bill/payment-info 用 -primary、
     Seller/bill/recharges 用 -secondary。

   ── 對照 shadcn 官方 Button（apps/v4/registry/new-york-v4/ui/button.tsx）──
     variant 只有六個：default / destructive / outline / secondary / ghost / link
     沒有 success / info / warning 這種色系 outline。
   ⇒ 本專案這批 class 本來就不在 shadcn 語彙裡。**正解不是替它們發明六種顏色**
     （那是在 shadcn 沒有的地方加東西，違反「以 shadcn 為主」），
     而是把呼叫端改寫成官方那六個 variant 的對應物。建議對應（待業主裁示，本輪未做）：
       btn-outline-secondary（唯讀/輔助）  → shadcn outline  ＝ 本專案 btn-white
       btn-outline-dark（可選中的中性態）  → shadcn outline ＋ 選中態實心 btn-dark（§btn-check 選中態 已是此語彙）
       btn-outline-primary（次級引導）     → shadcn outline  ＝ 本專案 btn-white（引導性交給位置，不交給顏色）
       btn-outline-warning（重新生成，1 處）→ shadcn outline  ＝ 本專案 btn-white
       btn-success（提交/確認）            → shadcn default   ＝ 本專案 btn-dark
     ★這是 60+ 處呼叫端的改動、跨十幾支非本輪所有權的檔案，所以本輪**只盤點不動**。

   ── 已知但本輪不動的兩件事（寫進來免得下次又要重查）──
   (a) `.btn-outline-danger` 的 §28 規則給了 `--bs-btn-bg: hsl(var(--destructive))`
       ＋ `--bs-btn-color: hsl(var(--destructive-foreground))`，但實測 color 是
       亮 rgb(186, 28, 28) ／ 暗 rgb(248, 114, 114) —— **紅底紅字**（Bootstrap 自己的
       `.btn-outline-danger` 把 --bs-btn-color 又蓋回去了）。目前全站 0 處使用所以看不到，
       但只要有人用第一次就會壞。修它要碰 §28，屬於「大改」，留給業主裁示。
   (b) 專案內部對 `btn-outline-dark` 的立場自相矛盾：
       Order/ReadyToShip 與 PackagingMachine/_shipment_group_script 把 success/info
       收斂**進** outline-dark（§S-69 六）；Center/reportPackage/index.blade.php:246 的註解
       卻明說它「不在主題 SSOT 的語意表內、暗色下兩態幾乎分不出來」而收斂**出**到 btn-white。
       兩份註解直接打架，建議一併裁決。
   (c) 破壞性動作的真正風險不在 outline：發票／折讓「作廢」與 SystemSetting 三支的批次「刪除」
       用的是 `btn-dark`，和同頁「儲存/確認」視覺完全不可分辨；而 .agent/uiux.md:517,526
       自己規定刪除應為 `btn-danger btn-sm`。規範與實作背離，但那些檔不在本輪所有權內。 */

/* --- S-70a. 補記：btn-success→btn-dark 的依據是專案自己的 SSOT（2026-08-07）------------
   ★這一行同樣是 ThemeOverrideAnnotationsTest 認的章節錨點；本節一樣不含任何宣告。
   §S-70 二、寫建議時漏了最直接的一條依據，補在這裡（原文一字不刪）：
     .agent/uiux.md 的 modal-footer 標準範例（L515~L527）已經明文規定
       取消 → `btn btn-white btn-sm`
       刪除 → `btn btn-danger btn-sm`
       確認/送出 → `btn btn-dark btn-sm`
   ⇒ 「確認取號」屬於確認/送出，規範早就寫著 btn-dark。
     Order/ReadyToShip/index.blade.php 這兩顆用 btn-success 是**沒照既有規範**，
     不是另一種風格；所以本輪把它改回 btn-dark 是回到 SSOT，不是我在挑顏色。
   ⇒ 同一份規範也說明了 §S-70 二、(c) 為什麼算「規範與實作背離」：
     發票作廢與 SystemSetting 批次刪除該是 btn-danger，現況是 btn-dark。 */

/* --- S-71. .btn-outline-danger 紅底紅字 → 回到 shadcn outline 語意（2026-08-07）--------
   ★這一行是 ThemeOverrideAnnotationsTest 認的章節錨點（`/* --- S-71.`，S- 前面不加 §）。

   ── 為什麼修（0 處使用，但一用就壞）──────────────────────────────────────
   §S-70 二、(a) 已經記下這個缺陷但留給裁示，本輪裁示為「修」。理由是它屬於
   「看不見的地雷」：全站 blade／js 目前 0 處使用 .btn-outline-danger，所以畫面上
   看不到任何症狀，稽核也永遠是 0 命中；但只要有人第一次寫下它，拿到的就是壞的。
   0 使用不是「不急」，是「沒有人會替我們發現」。

   ── 壞在哪（2026-08-07 實測，走 https://test.2025-seller.mtest/
      packaging-machine-order-management/status/ready-to-ship，探針注入真實頁面走完整 cascade）──
   目前有三條規則疊在同一顆按鈕上，各贏走一部分：
     (1) L499 `.btn-outline-danger`     → --bs-btn-bg: transparent（原作者的 outline 本意）
     (2) §28 L2093 `.btn-danger, .btn-outline-danger`
                                        → --bs-btn-bg: hsl(var(--destructive))  ★把 (1) 的透明底蓋成實心紅
                                        → --bs-btn-color: hsl(var(--destructive-foreground))
     (3) §S-39 L9644 `.btn-outline-danger`
                                        → --bs-btn-color: hsl(var(--destructive-fg))  ★把 (2) 的白字蓋回紅字
   淨結果＝(2) 的紅底 ＋ (3) 的紅字：
     亮 bg rgb(220,38,38) / color rgb(186,28,28) → 文字對比 1.33:1
     暗 bg rgb(220,38,38) / color rgb(248,114,114) → 文字對比 1.76:1
   ★像素取樣（截圖後在按鈕矩形內逐點取樣，不是只讀 computed）：
     亮 73.2% 的像素是 220,38,38，被判為「文字」的那幾撮像素是 200,32,32 / 187,28,28
     暗 73.1% 的像素是 220,38,38，文字像素 237,83,83
     —— 文字像素與底色像素同色相同明度階，肉眼就是一片紅。

   ── 修成什麼（數值出處逐條可查，沒有自己配的紅）────────────────────────
   ★先講清楚查不到的部分：shadcn 官方 button.tsx（apps/v4/registry/new-york-v4/ui/button.tsx）
     的 variant 只有 default / destructive / outline / secondary / ghost / link，
     **沒有 outline-destructive**，所以「紅框紅字的 outline 鈕」這個組合在官方沒有現成數值可抄。
     本節採用的是「官方 outline 的形狀 × 官方 destructive 的色相」，而每一個數值
     都指回本檔既有 token，一個都不是新調的顏色：
       底   = hsl(var(--background))     ← 官方 outline 是 `bg-background`；本檔 --background
                                            亮 0 0% 100%／暗 240 10% 3.9%（＝ .btn-white 的底，
                                            §S-70 二 已把「shadcn outline ＝ 本專案 btn-white」寫成對應表）
       框   = hsl(var(--destructive))    ← #DC2626 Tailwind red-600，就是 shadcn 現行 default theme
                                            的 destructive（§S-39 已把本檔 --destructive 對到這一階）
       字   = hsl(var(--destructive-fg)) ← 本檔 §1 明文「--destructive 是底色 token，紅『字』走 --destructive-fg」；
                                            亮 #BA1B1B／暗 #F87171，兩邊都是為「紅字」調過的值
       hover= 實心 hsl(var(--destructive)) ＋ hsl(var(--destructive-foreground))
                                          ← 與 .btn-danger／官方 destructive（bg-destructive text-white）同一組值；
                                            這也正是 L499 原作者寫的 hover 意圖，本節等於把它還原
   對比實測（WCAG 相對亮度，與本檔既有稽核腳本同一套算式）：
       亮 字/底 6.42:1（AA 過）  框/底 4.83:1（非文字 UI 要 3:1，過）
       暗 字/底 7.19:1（AA 過）  框/底 4.12:1（過）
       hover 亮 4.83:1／暗 4.63:1（＝ .btn-danger 現值，不是新配的）

   ── 為什麼不動 §28 ──────────────────────────────────────────────────
   §28 L2093 那一條的 selector 是 `.btn-danger, .btn-outline-danger`。要在那裡把
   .btn-outline-danger 拆出來，就得動到與 .btn-danger 共用的那一組宣告 ——
   .btn-danger 全站是活躍在用的（刪除／作廢／危險動作），改它等於重畫全站每一顆紅鈕，
   屬於明令禁止的大改。§28 L2088 那一條更碰不得（11 個 class 一起，btn-success／
   btn-secondary… 全站上千處）。⇒ 用檔尾新章節、以較高特異性覆蓋，爆炸半徑僅限
   .btn-outline-danger 這一個 class（目前 0 處使用 ⇒ 本節上線當下對畫面零影響，
   量測前後全站其他按鈕的渲染值逐字元相同）。
   ★選擇器刻意寫成 `.btn.btn-outline-danger`（特異性 0,2,0）而不是同名 0,1,0：
     上面三條全是 0,1,0 靠「寫在後面」決勝，我不想讓本節的正確性繫在
     「以後沒有人在我後面再加一條 .btn-outline-danger」這種運氣上。
     Bootstrap 的 --bs-btn-* 只有 .btn 會消費，所以要求同時有 .btn 不會漏接。
   ★disabled 態刻意不在本節宣告（我先寫了 --bs-btn-disabled-* 三條，實測是死碼，已移除）：
     L2620 `.btn:disabled, .btn.disabled, fieldset:disabled .btn` 用的是
     `background-color: … !important`，任何 --bs-btn-disabled-* 都搶不過它。
     實測 disabled 的 .btn-outline-danger ＝ 亮 bg rgb(244,244,245) 字 rgb(113,113,122)
     框 rgb(228,228,231)／暗 bg rgb(39,39,42) 字 rgb(161,161,170)，opacity .7 ——
     全站每一顆 disabled 鈕都長這樣，這是對的（disabled 的 SSOT 在 L2620，不該在這裡分岔）。
     寫一組永遠不生效的宣告只會讓下一個人以為這裡管得到 disabled。

   ── 附記：btn-outline-dark 自相矛盾的裁決（只記錄，本輪不改任何呼叫端）───────
   §S-70 二、(b) 記下兩處註解打架：
     Order/ReadyToShip 與 PackagingMachine/_shipment_group_script 把 success/info
       收斂**進** btn-outline-dark（當它是「可選中的中性態」）；
     Center/reportPackage/index.blade.php:246 卻說它「不在主題 SSOT 的語意表內、
       暗色下兩態幾乎分不出來」而收斂**出**到 btn-white。
   2026-08-07 走真實頁面實測（切 html.dark 後等 450ms 再量，兩態各自截圖取樣）：
     .btn.btn-outline-dark            亮 bg rgb(244,244,245) 字 rgb(9,9,11) 框 rgb(228,228,231)
                                      暗 bg rgb(39,39,42)   字 rgb(250,250,250) 框 rgb(39,39,42)
     .btn.btn-outline-dark.active     亮/暗 **逐字元完全相同**（像素主色同為 244,244,245／39,39,42）
   ⇒ 兩態不是「幾乎分不出來」，是 **100% 相同**，而且亮暗兩模式都一樣。
     根因：§28 給 outline 系列 --bs-btn-active-bg: hsl(var(--accent))，而本檔
     --accent 與 --secondary 是同一個值（亮 240 4.8% 95.9%／暗 240 3.7% 15.9%），
     active 態自然等於常態。⇒ **reportPackage:246 那一邊講的是對的，而且還低估了**。
   ⇒ ReadyToShip 那一邊講的其實是另一件事（class 命名收斂），它並沒有讓按鈕取得
     「可選中」的語意 —— 本檔唯一提供選中態的是 §btn-check 選中態
     （`.btn-check:checked + .btn-outline-dark`，實測亮 bg rgb(24,24,27) 白字 16.97:1、
     暗 bg rgb(250,250,250) 黑字 16.97:1，兩態非常清楚），但全專案 resources/views/**
     **0 處使用 .btn-check**（grep 實證），所以那條規則目前是死碼，沒有服務到任何現存按鈕。
   ⇒ 建議（待業主裁示，本輪不動 60+ 處呼叫端）：可選中的中性態要嘛改用 btn-check 寫法
     去吃現成的選中態，要嘛照 reportPackage 的做法用 btn-white(選中)／btn-default(未選)。
     在那之前，任何靠 `.active` 表達選中的 btn-outline-dark 都等於沒有選中態。

   ── 附記二：本輪抓到但**沒有修**的一個活躍缺陷（不在本輪所有權內）──────────
   `_shipment_group_script.blade.php:883-884`（與 Order/ReadyToShip 同款）在物流子路線按鈕
   被選中時做 `this.classList.add('btn-dark','text-white')`，元素於是同時帶
   .btn-outline-dark ＋ .btn-dark ＋ .text-white。實測（亮色）：
       bg rgb(244,244,245)（§28 L2088 在 §4 的 .btn-dark 之後，--bs-btn-bg 被搶成 --secondary）
       字 rgb(250,250,250)（.text-white → --dark-panel-foreground，真白）
       ⇒ 文字對比 **1.05:1**，像素取樣：底 244,244,245 佔 71.9%、白字 250,250,250 佔 4.8%
       ⇒ 亮色模式下「已選中的物流客代」按鈕文字幾乎完全隱形（暗色 14.27:1 正常）。
   這是有人真的會看到的缺陷，但修它要動的是 script 檔（本輪非我所有，且另有代理在動相鄰檔），
   或在主題加一條針對 `.btn-outline-dark.btn-dark` 的安全網 —— 後者會改變一顆活躍按鈕的渲染，
   超出本輪「只修 0 使用的 btn-outline-danger」的授權範圍。**故意不順手修**，留報告給業主裁示。 */
.btn.btn-outline-danger {
    --bs-btn-bg: hsl(var(--background));
    --bs-btn-border-color: hsl(var(--destructive));
    --bs-btn-color: hsl(var(--destructive-fg));
    --bs-btn-hover-bg: hsl(var(--destructive));
    --bs-btn-hover-border-color: hsl(var(--destructive));
    --bs-btn-hover-color: hsl(var(--destructive-foreground));
    --bs-btn-active-bg: hsl(var(--destructive));
    --bs-btn-active-border-color: hsl(var(--destructive));
    --bs-btn-active-color: hsl(var(--destructive-foreground));
}


/* --- S-72. 中性色與語意色整組回歸 shadcn 官方（apps/v4）＋ radius 階梯改用官方公式（2026-08-07）---
   ★這一行是給 tests/Feature/Theme/ThemeOverrideAnnotationsTest 認的「章節錨點」，
     寫法必須是 `/* --- S-72. …`（S- 前面不可以多 §，見 §S-46 的說明）。

   業主 2026-08-07 定調：「**一切都恢復 shadcn 官方設定就對了**」。
   本節把中性色與語意色整組換成官方值。**本節沒有新增任何選擇器規則**，
   改的全部是 token 宣告本身（外加一行為了讓帶 alpha 的 --border 不失效而必須改寫的既有宣告，見一-(3)）。

   ═══════════════════════════════════════════════════════════════════════════
   零、「官方」是哪一份 —— 目標鎖定 A，不是 zinc 預設
   ═══════════════════════════════════════════════════════════════════════════
     A（本節的目標）https://raw.githubusercontent.com/shadcn-ui/ui/main/apps/v4/app/globals.css
        ＝ ui.shadcn.com 網站本身套用的那套：oklch、**achromatic（chroma 0，純中性灰）**、--radius: 0.625rem
     B（本主題原本的血緣）.../apps/v4/public/r/themes.css 的 `.theme-zinc`
        ＝ HSL、**zinc 家族（hue 240，帶藍調）**

   ★為什麼取 A 不取 B：§S-68 ③ 那張偏離總表當時把三件事列成「待業主拍板」，
     業主 2026-08-07 逐項回答，三項**全部**與 A 一致、與 B 不一致：
       ① 暗色卡片要比背景亮一階  → A 的 --card oklch(0.205) > --background oklch(0.145)；B 的 zinc 是反過來的
       ② --border / --input 暗色改用白色半透明疊層 → A 是 oklch(1 0 0 / 10% 與 15%)；B 是不透明 zinc-800
       ③ --ring 改回官方值 → A 是中灰 oklch(0.708)/oklch(0.556)；本主題原本是近黑／純白
     ⇒ A 是唯一與業主意向一致的目標。§S-68 ③ 那張表的內容一字未刪，
       但它的基準（B）已被本節取代，讀那張表時要知道基準換了。

   ★★全站中性灰會**失去藍調**（zinc hue 240 → achromatic hue 0）。這是預期中的變化，不是 bug。
     肉眼可見的差距是 R 與 B 通道差 3～9／255（例如 #71717A → #737373、#27272A → #262626）。

   ═══════════════════════════════════════════════════════════════════════════
   一、換算與寫法（三個技術決定，都先驗過才做）
   ═══════════════════════════════════════════════════════════════════════════
   (1) **保留 HSL 三元組寫法，不改成 oklch**。
       本檔到處是 `hsl(var(--x))`（光是 --border 就 192 處、全專案另有 63 處在 blade）。
       改成直接寫 oklch 就得同步改掉這幾百處引用 —— 那是純風險、零收益（同一顆顏色）。
       ⇒ 把官方 oklch 換算成等值 HSL，官方原值寫在每一行的註解裡，日後可回溯。

   (2) **換算函式先用已知精確參考點自證**，不是「算了就信」：
         oklch(1 0 0)                     → rgb(255,255,255)  ✔
         oklch(0 0 0)                     → rgb(0,0,0)        ✔
         oklch(0.627955 0.257683 29.2338) → rgb(255,0,0)      ✔（純紅）
         oklch(0.452014 0.313214 264.052) → rgb(0,0,255)      ✔（純藍）
       四個參考點全中，才拿去換算 token。

   (3) ★★**暗色 --border / --input 是帶 alpha 的白色疊層** —— 這是本節最容易做壞的一顆。
       官方 `oklch(1 0 0 / 10%)`。本主題的 token 存的是「HSL 三元組」，
       一般人的直覺是「三元組承載不了 alpha」，但 **CSS Color 4 的 `hsl()` 空格語法接受 `/ <alpha>`**，
       所以 `--border: 0 0% 100% / 10%` 代入 `hsl(var(--border))` 會展開成
       `hsl(0 0% 100% / 10%)` —— **完全合法**，192 處引用一處都不用改。
       ★但有一個致命例外：任何寫成 `hsl(var(--border) / .5)` 的地方會展開成
         `hsl(0 0% 100% / 10% / .5)` → **語法錯誤**。
         `border-bottom: 1px solid <非法色>` 的後果不是「邊框消失」而是**顏色退回 currentColor**
         ——實測注入一個這樣的節點，暗色下量到 `rgb(250,250,250)`，也就是會冒出一條**亮白線**。
       ★先數清楚再動手（grep 全專案，排除 vendor / node_modules）：
           shadcn-theme.css 內 `hsl(var(--border) / …)`  1 處（L2995 的 .form-horizontal.form-bordered）
           shadcn-theme.css 內 `hsl(var(--input) / …)`   0 處
           blade / js / 其他 css                          0 處
         ⇒ 只有一處要改，已就地改成 `hsl(var(--border))` 並在該處寫明理由。
           那條規則旁邊本來就有 2026-08-04 稽核註記「blade 0 命中」（全站沒有人用
           `.form-horizontal.form-bordered`），所以畫面零影響；改它是為了不在檔裡留一條
           「暗色一定會壞」的 CSS。
       ★收工前再 grep 一次，確認全專案只剩註解裡提到這個寫法（實際規則 0 處）。

   ═══════════════════════════════════════════════════════════════════════════
   二、逐顆對照表（★＝值有變；官方欄位是 A 的 oklch 原文）
   ═══════════════════════════════════════════════════════════════════════════
   :root（亮色）
     token                    改前              → 改後            官方 oklch                  改後 hex
     ────────────────────────────────────────────────────────────────────────────────────────
     --background             0 0% 100%           （不變）        oklch(1 0 0)                #FFFFFF
   ★ --foreground             240 10% 3.9%      → 0 0% 0%         oklch(0% 0 0)               #000000
     --card                   0 0% 100%           （不變）        oklch(1 0 0)                #FFFFFF
   ★ --card-foreground        240 10% 3.9%      → 0 0% 0%         oklch(0% 0 0)               #000000
     --popover                0 0% 100%           （不變）        oklch(1 0 0)                #FFFFFF
   ★ --popover-foreground     240 10% 3.9%      → 0 0% 0%         oklch(0% 0 0)               #000000
   ★ --secondary              240 4.8% 95.9%    → 0 0% 96.1%      oklch(0.97 0 0)             #F5F5F5
   ★ --secondary-foreground   240 10% 3.9%      → 0 0% 9%         oklch(0.205 0 0)            #171717
   ★ --muted                  240 4.8% 95.9%    → 0 0% 96.1%      oklch(0.97 0 0)             #F5F5F5
   ★ --muted-foreground       240 3.8% 46.1%    → 0 0% 45.1%      oklch(0.556 0 0)            #737373
   ★ --accent                 240 4.8% 95.9%    → 0 0% 96.1%      oklch(0.97 0 0)             #F5F5F5
   ★ --accent-foreground      240 10% 3.9%      → 0 0% 9%         oklch(0.205 0 0)            #171717
   ★ --border                 240 5.9% 90%      → 0 0% 89.8%      oklch(0.922 0 0)            #E5E5E5
   ★ --input                  240 5.9% 90%      → 0 0% 89.8%      oklch(0.922 0 0)            #E5E5E5
   ★ --ring                   240 10% 3.9%      → 0 0% 63.1%      oklch(0.708 0 0)            #A1A1A1
   ★ --destructive            0 72.2% 50.6%     → 357.1 100% 45.3% oklch(0.577 0.245 27.325)  #E7000B  ⚠溢色域
     --destructive-foreground 0 0% 100%           （**刻意不動**，理由見三-②）
     --radius                 0.625rem            （不變，與 A 相同）

   html.dark（暗色）
     token                    改前              → 改後            官方 oklch                  改後 hex
     ────────────────────────────────────────────────────────────────────────────────────────
   ★ --background             240 10% 3.9%      → 0 0% 3.9%       oklch(0.145 0 0)            #0A0A0A
     --foreground             0 0% 98%            （不變）        oklch(0.985 0 0)            #FAFAFA
   ★ --card                   240 5.9% 10%      → 0 0% 9%         oklch(0.205 0 0)            #171717
     --card-foreground        0 0% 98%            （不變）        oklch(0.985 0 0)            #FAFAFA
   ★ --popover                240 5.9% 10%      → 0 0% 9%         oklch(0.205 0 0)            #171717
     --popover-foreground     0 0% 98%            （不變）        oklch(0.985 0 0)            #FAFAFA
   ★ --secondary              240 3.7% 15.9%    → 0 0% 14.9%      oklch(0.269 0 0)            #262626
     --secondary-foreground   0 0% 98%            （不變）        oklch(0.985 0 0)            #FAFAFA
   ★ --muted                  240 3.7% 15.9%    → 0 0% 14.9%      oklch(0.269 0 0)            #262626
   ★ --muted-foreground       240 5% 64.9%      → 0 0% 63.1%      oklch(0.708 0 0)            #A1A1A1
   ★ --accent                 240 3.7% 15.9%    → 0 0% 25.1%      oklch(0.371 0 0)            #404040  ← 亮一大階
     --accent-foreground      0 0% 98%            （不變）        oklch(0.985 0 0)            #FAFAFA
   ★ --border                 240 3.7% 15.9%    → 0 0% 100% / 10% oklch(1 0 0 / 10%)          白 10% 疊層
   ★ --input                  240 3.7% 15.9%    → 0 0% 100% / 15% oklch(1 0 0 / 15%)          白 15% 疊層
   ★ --ring                   0 0% 98%          → 0 0% 45.1%      oklch(0.556 0 0)            #737373
     --destructive            0 72.2% 50.6%       （**刻意不動**，理由見三-③）
     --destructive-foreground 0 0% 98%            （**刻意不動**，理由見三-②）

   ★⚠溢色域：`oklch(0.577 0.245 27.325)` 超出 sRGB 可表示範圍，換算時被 clamp 到 #E7000B。
     這不是換算錯誤，是 oklch 本來就能描述 sRGB 表達不了的顏色；在 sRGB 螢幕上瀏覽器
     自己算出來的也是同一個裁切結果。**明講出來，不假裝沒事。**

   ★同步改動：`[data-bs-theme="light"]`（§補位 6 的「永遠亮色的島」）裡有 5 顆同名 token
     被鎖成亮色值，一併同步（--foreground / --card-foreground / --muted / --muted-foreground / --border）。
     不同步的話 AI 工具 hero 區會出現「一塊還是 zinc、其他都 achromatic」的兩套灰。
     ★`[data-bs-theme="dark"]` **沒有**這種區塊，也**不可以**加（§S-39 (6) 有現場實驗紀錄：
       #sidebar 每頁都掛 data-bs-theme="dark"，加了會炸掉全站側欄）。

   ★同步改動：§S-39 的 `:root { --destructive }` 覆蓋（本檔真正生效的那一顆）也換成官方值。
     §S-39 的驗收條件是「實心紅底白字要過 AA 4.5」—— 官方值實測 **4.77:1**，條件仍然成立。

   ═══════════════════════════════════════════════════════════════════════════
   三、**刻意沒有照官方做**的四顆（業主可以推翻我，理由都在這裡）
   ═══════════════════════════════════════════════════════════════════════════
   ① --primary / --primary-foreground（品牌金黃 49.4 100% 50% ＋ 恆黑字）
      官方 A 是 `oklch(0% 0 0)`（純黑）。**不改。**
      shadcn 的模型本來就是「骨架給你、品牌色你自己帶」：`button.tsx` 的 default 變體只寫
      `bg-primary text-primary-foreground`，官方從來沒有規定 primary 必須是黑。
      改成純黑等於放棄品牌識別，而且 App（2026-mymy-app AppColors.primary）也是這顆，兩端會分岔。

   ② --destructive-foreground（亮 0 0% 100% ／ 暗 0 0% 98%）
      官方 A 是 亮 `oklch(0.97 0.01 17)`（近白微粉）／暗 `oklch(0.58 0.22 27)`（**一個紅色**）。**不改。**
      ★這是與 §S-68 ② 同一族的 **false friend**：同名 token，兩邊職責完全不同。
        上游：這顆是**死 token** —— `button.tsx` 與 `badge.tsx` 的 destructive 變體寫的是
              `bg-destructive text-white`，**沒有任何出貨元件讀 --destructive-foreground**。
        本檔：這顆是**紅底上的文字色**（.btn-danger / .badge.bg-danger / .bg-danger 全吃它）。
      ⇒ 照抄上游暗色會得到「紅底 + 紅字」（#DC2626 底配 #DF2225 字，對比 1.01:1，字整個消失）。
        照抄一顆上游自己都不用的值去砸掉本站在用的語意，是照抄的最壞形態。

   ③ --destructive（**暗色**，維持 0 72.2% 50.6% #DC2626）
      官方 A 暗色是 `oklch(0.704 0.191 22.216)` ＝ #FF6467。**用數據決定，不用偏好決定：**
        #FF6467 配白字 → **2.89:1**（現況 #DC2626 配 #FAFAFA 是 4.83:1）
      ⇒ 照官方做會讓無障礙從「過 AA」掉到「差很遠」，**需業主裁示，本節保持現狀不動**。
      ★為什麼官方自己沒事：官方暗色紅鈕寫的是 `dark:bg-destructive/60`，
        也就是把那顆亮紅**壓到 60% 疊在暗底上** → 實際渲染是 rgb(157,64,66)，白字 6.47:1。
        本站的 .btn-danger 是實心底，要照官方就得改寫全站紅底規則（每一支紅按鈕、badge、
        alert、progress-bar、swal2 圖示），那是規則層的改動不是 token 層，超出本節範圍。
        ★亮色那顆有照官方改（#E7000B，白字 4.77 仍過 AA），所以亮暗兩顆值不同 ——
          官方自己也是亮暗不同值（0.577 vs 0.704），不對稱本來就是官方的做法。

   ④ 本專案自有 token：--primary-light / --primary-dark / --hint / --dark / --dark-foreground /
      --dark-panel / --dark-panel-foreground / --success* / --info* / --warning* /
      --success-bg / --success-fg / --warning-bg / --warning-fg / --destructive-fg /
      --leave-* / --early-leave / --chart-1…--chart-8 / --link / --link-hover /
      --sidebar-* 全組 / 所有 --bs-* 橋接變數。**一顆都沒動**（官方沒有對應語彙）。
      ★★2026-08-07 §S-74 追記（上面那句在 §S-72 當下是對的，現在**不再成立**，原文保留存查）：
        §S-74 已把其中的**中性色**那幾顆 achromatic 化了 ——
        --hint / --dark / --dark-foreground / --dark-panel / --chart-2 / --sidebar-* 全組。
        非中性的（--primary-* / --success* / --info* / --warning* / --leave-* /
        --chart-1、3~8 / --link*）仍然一顆沒動，那部分的敘述照舊有效。
        逐顆前後值見檔尾 §S-74「三、逐顆對照」。
      ★--chart-1…8 特別說明：官方只給一條單色藍階（給官網範例圖用），
        本專案這 8 色盤做過色盲可分辨與 ΔE 驗證（validate_palette.js PASS）。
        換成官方藍階會讓多序列資料圖分不出來 → 不換。

   ═══════════════════════════════════════════════════════════════════════════
   四、--radius 階梯：改用官方乘法公式（零視覺變化）
   ═══════════════════════════════════════════════════════════════════════════
     --radius 本來就已經是 0.625rem，與 A 相同；不同的是**衍生階的算法**。
       本檔原本  sm = --radius − 4px ｜ md = −2px ｜ lg = --radius ｜ xl = +4px
       官方 A    sm = --radius × 0.6 ｜ md = ×0.8 ｜ lg = --radius ｜ xl = ×1.4
     在 --radius = 10px 這個現值下 **四階算出來逐階相同**（6 / 8 / 10 / 14），
     所以這是零視覺變化的純對齊。改公式的理由是：日後有人動 --radius 時兩套不會分岔
     （加減法在 --radius 變小時會算出負值，乘法不會）。
     ★官方另有 2xl(1.8) / 3xl(2.2) / 4xl(2.6)，本檔沒有任何地方用到 → 不新增（YAGNI）。

   ═══════════════════════════════════════════════════════════════════════════
   五、逐頁視覺回歸實測（無頭 Chrome，https://test.2025-seller.mtest/）
   ═══════════════════════════════════════════════════════════════════════════
   涵蓋 11 支頁面 × 亮/暗 × 1440，另 3 支重點頁加 375：
     /main、/good/warehouse、/order-management/status/ready-to-ship、
     /order-management/orders/create、/pos/machine、/pick/desktop、/hr/user、
     /center/report、/seller/bill、/store-setting/store、/inventory/composite-product/create
   ★量測方式：computed style **加上**截圖後在元素矩形上做水平掃描線的**像素取樣**
     （只讀 computed 會漏掉「合成後同色」這種坑 —— 本專案踩過）。

   ① 卡片還看得見嗎（最關鍵的一項）
      亮色：頁底 255 ＝ 卡底 255（色差 0，**這是官方模型的正常狀態**），
            靠 1px --border 分辨：框 vs 頁底 27／255 → **26／255**（官方值就是這個），
            像素掃描 maxStep 27 → 26 —— 邊界照樣畫得出來。
      暗色：頁底 rgb(9,9,11) → rgb(10,10,10)；卡底 rgb(24,24,27) → rgb(23,23,23)。
            框（白 10% 疊層）vs 頁底 31／255 → **36／255**，像素 maxStep 31 → 36 —— **變強**。
      ★唯一一處變弱：/inventory/composite-product/create 的第一張卡，像素 maxStep 31 → 25。
        原因查清楚了：那張卡的框是疊在**頁面底**（10）而不是卡片底（23）上，
        白 10% 疊 10 得 35（Δ25），疊 23 得 46（Δ36）。
        **這正是半透明疊層的本質——同一條框在不同表面上濃淡不同**，是官方要的行為，不是壞掉。
        25／255 仍然清楚可見（對照：改前全站最弱的暗色輸入框框線只有 15／255）。

   ② 主要文字對比（每頁取最低值）
      亮色 4.83 → **4.74**（最低那顆一律是 --muted-foreground：#71717A → #737373）
      暗色 6.91 → **6.94**
      /pos/machine 亮 4.94 → 4.98、暗 5.81 → 5.90
      ⇒ **改後全站沒有任何一處文字掉到 4.5 以下**（最低 4.74，仍過 WCAG AA）。

   ③ 頁籤選中片 vs 軌道（本專案踩過「選中片消失」的坑）
      /seller/bill 亮：軌 244 → 245、片 255 → 255、色差 11 → 10，選中字對比 19.9 → 21.0
      /seller/bill 暗：軌 rgb(39,39,42) → rgb(38,38,38)、片 rgb(104,104,106) → rgb(103,103,103)、
                       色差 65 → 65，選中字 5.33 → 5.42、未選中 5.81 → 5.86
      /pos/machine（品牌黃選中片）亮 色差 245 → 245／暗 216 → 217
      ⇒ §S-68 ② 建立的暗色選中片關係完全沒被破壞。

   ④ 表單輸入框邊框（改成 alpha 疊層後最容易出事的地方）
      亮色：框 #E4E4E7 → #E5E5E5，vs 卡片底 27／255 → 26／255（像素 27 → 26）
      暗色：框 #27272A（不透明）→ 白 15% 疊層，實際渲染 rgb(39,39,42) → **rgb(58,58,58)**，
            vs 輸入框自身底 15／255 → **35／255**（像素 31 → 48）
      ⇒ 暗色輸入框邊框從「幾乎看不見」變成「清楚可見」，這是本次最大的實際改善。

   ⑤ 其他暗色表面（逐一確認邊框沒有消失）
      table td / .table / hr / .card-header / .btn-outline-secondary 的邊框
      改後 raw 值都是 `rgba(255,255,255,0.1)`，疊完 Δ 都在 23～25／255（改前 15～31）。

   ⑥ 側欄
      亮：側欄 rgb(250,250,250) vs 內容區 rgb(255,255,255) → 5／255（與改前相同）
      暗：側欄 rgb(24,24,27) vs 內容區 rgb(10,10,10) → 17／255

   ═══════════════════════════════════════════════════════════════════════════
   六、★★★抓到但**沒有修**的問題（都需要業主裁示，不要當成沒看到）
   ═══════════════════════════════════════════════════════════════════════════
   (a) ★★★**焦點環（--ring）對比明顯倒退** —— 這是本次唯一一個實質的無障礙倒退。
       官方的 --ring 是中灰，而且官方只把它用在 `ring-[3px] ring-ring/50` 的淡環上；
       本檔則是 `0 0 0 2px 背景色, 0 0 0 4px hsl(var(--ring))`（§4）與 `…/.5`（§S-25）兩種寫法。
       實測（Tab 鍵真的走進去量的，不是 .focus()）：
                              改前                改後
         亮 實心環 vs 白      19.90              **2.58**
         亮 .5 環 疊白        3.74               **1.54**
         暗 .5 環 疊暗底      5.18               **1.88**
       WCAG 2.2 SC 1.4.11 對焦點指示要求 **3:1**，改後三種情況全部不到。
       ★這是上游本來就長這樣（不是我換錯值），而且業主明確點名「ring 改回官方值」，所以照做了；
         但它是一個**已知的、量化過的**無障礙倒退。要救有兩條路，都要業主拍板：
           路一：--ring 回到近黑／純白（＝放棄這一顆的官方對齊）
           路二：--ring 維持官方值，改「用法」——把環改成官方的 `border-ring` + 3px 淡環，
                 靠形狀與位移而不是靠對比。這是規則層的改動，不在 token 範圍。

   (b) `--bs-link-color-rgb: 9, 9, 11`（§1 L213）**沒有跟著 --foreground 走**。
       --foreground 已改成 0 0% 0%，但這顆手抄的 rgb 鏡像還是舊值 → 亮色連結是 rgb(9,9,11)、
       內文是 rgb(0,0,0)，差 9～11／255（肉眼幾乎讀不出來，但這是一個 SSOT 破口）。
       ★沒改的理由：它是 --bs-* 橋接變數，本輪的檔案所有權範圍是「token 宣告」，
         而且改它會讓「除了目標 token 之外零變動」這條交付條件多一個例外。列為下一輪一行修正。

   (c) 幾顆**專案自有的中性 token 還留在 zinc 家族**，與周圍已 achromatic 的顏色並存：
         --hint        亮 240 5% 64.9%(#A1A1AA) ／暗 240 3.8% 46.1%(#71717A)  ← 佔位符文字
         --dark        亮 240 5.9% 10%(#18181B)                               ← btn-dark 底
         --sidebar-fg / --sidebar-border / --sidebar-accent / --sidebar-active-bg
         暗色 --sidebar-bg 240 5.9% 10%(#18181B) vs 新的 --card #171717        ← 差 1～4／255
       ★沒改的理由：這些是專案自有語彙，官方沒有對應顆粒，本輪明確不動。
         實測差距都在 3～9／255，畫面上讀不出來，但要「整組 achromatic」的話這些要一起走。
       ★★2026-08-07：**這一項已經在 §S-74 做掉了**（原文保留存查）。上面列的每一顆都已 achromatic，
         連這裡沒列到的 --chart-2 與 `[data-bs-theme="light"] .text-dark` 的硬寫色也一併處理。
         唯三沒吸到官方階的是 亮 --sidebar-fg 34% / 亮 --sidebar-accent 94% / 暗 --sidebar-active-bg 19%，
         理由（吸過去會削掉所在頁指示器或讓 hover 與 active 撞色）寫在各自宣告旁與 §S-74「二」。

   (d) §S-68 ② 的註解裡有一句「不寫成 `hsl(var(--input) / .3)` —— 本主題的 --input 是不透明 zinc，
       那樣寫色差會歸零」。**那句話的前提已經被本節改掉了**（--input 現在就是白色疊層）。
       ★但它的**結論仍然正確**，只是理由換了：現在 `hsl(var(--input) / .3)` 會展開成
         `hsl(0 0% 100% / 15% / .3)` → 語法錯誤（見一-(3)）。
         所以 §S-68 ② 那三條硬寫 `hsl(0 0% 100% / .3)` 的規則**維持原樣是對的**，一個字都沒動。
       ★§S-68 的原文一行都沒有刪，這裡只補一句「理由換了、結論沒換」。
   ------------------------------------------------------------------------- */

/* --- S-73. 焦點環改用 shadcn 官方的「兩層」用法：border 換 ring 色 ＋ 3px ring/50（2026-08-07）---
 *
 * ── 起因 ────────────────────────────────────────────────────────────────────
 * §S-72 把 --ring 換成官方 apps/v4 的中灰（亮 oklch(0.708 0 0)=#A1A1A1、
 * 暗 oklch(0.556 0 0)=#737373），焦點環對比因此倒退（§S-72 (a) 已記錄）：
 *     亮 實心環 vs 白底 19.90 → 2.58 ／ 亮 /.5 環疊白底 3.74 → 1.54 ／ 暗 /.5 環疊暗底 5.18 → 1.88
 * WCAG 2.2 SC 1.4.11 對焦點指示要求 3:1，三種全部不到。
 *
 * ★根因不是「官方值不好」，是本專案**只抄了 token 沒抄用法**。
 *
 * ── 一、官方各元件的焦點寫法（原文照抄，2026-08-07 自 shadcn/ui apps/v4 取得）──
 *   button.tsx（base）：
 *     "… transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px]
 *      focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50
 *      aria-invalid:border-destructive aria-invalid:ring-destructive/20
 *      dark:aria-invalid:ring-destructive/40 …"
 *     ★destructive 變體另外覆蓋成 "focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40"
 *     ★base **沒有** `border` 這個寬度類別 → 官方預設 Button 的 border-width 是 0，
 *       `border-ring` 在它身上是 no-op；只有 outline 變體有 "border bg-background shadow-xs"。
 *   input.tsx：
 *     "… border border-input bg-transparent … outline-none …"
 *     "focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50"
 *     "aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40"
 *   textarea.tsx：
 *     "… border border-input bg-transparent … outline-none placeholder:text-muted-foreground
 *      focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 …
 *      aria-invalid:border-destructive aria-invalid:ring-destructive/20 …"
 *   select.tsx（SelectTrigger）：
 *     "… border border-input bg-transparent … outline-none focus-visible:border-ring
 *      focus-visible:ring-[3px] focus-visible:ring-ring/50 … aria-invalid:ring-destructive/20 …"
 *     ★SelectItem 用的是 "outline-hidden focus:bg-accent focus:text-accent-foreground"（改底色，不畫環）
 *   checkbox.tsx：
 *     "… size-4 … border border-input shadow-xs transition-shadow outline-none
 *      focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 …"
 *   switch.tsx：
 *     "… border border-transparent shadow-xs transition-all outline-none
 *      focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 …"
 *   globals.css 全域：`* { @apply border-border outline-ring/50; }`
 *
 *   ⇒ 六支**完全一致**的配方：outline 關掉、border 換成 ring 色（實色）、外面再加
 *     一圈 3px 的 50% 透明柔環。可辨識度是靠「實色 border 換色 ＋ 3px 厚度」，
 *     不是靠環本身與底色的高對比。
 *   ⇒ 官方**沒有** ring-offset（背景色間隙）。本檔多處註解寫「用的是官方 ring-2 + ring-offset-2 寫法」，
 *     那是 v3 世代的記憶，v4 已經不是了。原註解一字未刪，只在各處就地標注已過期。
 *
 * ── 二、本專案改前的用法盤點（`grep -n "var(--ring)"`，含 resources/ 與 public/assets/js/）──
 *   本檔（public/assets/css/shadcn-theme.css）共 5 種配方並存：
 *     ①「2px 背景色間隙 + 4px ring」全不透明   .btn:focus-visible（§4 與 §S-30 的 !important 版）、
 *                                             .theme-collapse-btn / .app-sidebar-minify-btn（後者間隙用 --sidebar-bg）
 *     ②「2px 背景色間隙 + 4px ring/.5」        body :focus-visible（全域）、.pos :focus-visible（間隙用 --card）、
 *                                             §S-25 那組（.form-control:focus / .form-select:focus /
 *                                             .page-link / .dropdown-item / .form-check-input）、
 *                                             .fb-row-action、.form-range、app-header 三條 navbar、
 *                                             .fb-tab / .nav-link、.swal2-styled
 *     ③「outline: 2px solid ring + offset」    §2 基礎那組（.form-control:focus 等）、.fb-ph-nav、
 *                                             .flow-stepper a.flow-step
 *     ④「2px ring/.2 ～ /.35 貼邊環」          --bs-pagination-focus-box-shadow(.2)、select2 focus(.25)、
 *                                             .form-check-input(.25)、.swal2-input(.35)
 *     ⑤「只換 border 色，沒有外環」            §1 .form-check-input:focus（box-shadow: none）、
 *                                             select2 search field
 *   ★關鍵發現：**沒有任何一條同時做到「border 換 ring 色 ＋ 外環」**。
 *     §S-25 那組甚至明寫 `border-color: hsl(var(--input));`（註解：「邊框不再改成近黑」）
 *     ——把 border 這一層主動關掉了。那句在 --ring 是近黑的年代是對的，
 *     §S-72 換成中灰後前提消失，而它正是「只抄一半」的所在。
 *   本檔以外：resources/views/auth/_auth_shell_css.blade.php、payment/_shell_css.blade.php、
 *     buyer/layout.blade.php 各自有 --ring 焦點寫法（另檔，本次未動，見「五、抓到但沒修」）。
 *
 * ── 三、改後的實作（本次唯一動到的東西）────────────────────────────────────
 *   統一成官方配方，五種寫法收斂成一種：
 *       outline: none;
 *       border-color: hsl(var(--ring));            /  ＝ Tailwind border-ring（只設色不設寬）  /
 *       box-shadow: 0 0 0 3px hsl(var(--ring) / .5); /  ＝ ring-[3px] ring-ring/50  /
 *   ★--ring 是 HSL 三元組，所以 `hsl(var(--ring) / .5)` 合法。
 *   ★--border / --input 在暗色是「帶 alpha 的完整值」（0 0% 100% / 10%），
 *     對它們寫 `hsl(var(--border) / .5)` 會展開成雙斜線語法錯誤，實測後果是退回 currentColor
 *     （暗色冒出一條亮白線）。本次全程只對 --ring 加 alpha，沒碰那兩顆。
 *   ★.pos / .app-sidebar-minify-btn 原本為了「間隙不露底色」而各自用 --card / --sidebar-bg，
 *     官方寫法沒有間隙 → 這兩條的特殊理由已消失。規則保留不刪，值改成與全域同一套，
 *     避免日後只改全域、它們靜靜留著舊配方而分岔。
 *   ★.flow-stepper 那條原本用 outline（因為 clip-path 會裁掉陰影，已有 clip-path:none 處理）。
 *     改用 box-shadow 時**必須把 box-shadow 明寫出來**：它特異度 (0,3,1) 高於全域
 *     `body :focus-visible` (0,1,1)，只留 outline 不宣告 box-shadow 的話，
 *     全域那條的環會同時畫上去變成雙環。
 *
 * ── 四、三組對比前後數值（實測，非計算）────────────────────────────────────
 *   量法：puppeteer 真鍵盤 page.keyboard.press('Tab')（不是 el.focus()，:focus-visible 對程式化
 *   focus 行為不同），走 https://test.2025-seller.mtest，dpr=2 截圖後在元素外緣矩形取樣
 *   （避開圓角只取每邊中段 20%~80%），聚焦／未聚焦兩張同位置比對。
 *   涵蓋 /main、/order-management/orders/create、/hr/user、/good/warehouse 亮暗各一輪。
 *
 *   欄位＝「border 換色層 vs 元件內底」／「3px 柔環 vs 外側底色」，✓＝過 3:1
 *   ┌──────────────────┬──────────────────────┬──────────────────────┬──────────────────────┐
 *   │ 元件（亮色）     │ A 改前               │ B 本次（官方用法）   │ C 官方用法+高對比ring│
 *   ├──────────────────┼──────────────────────┼──────────────────────┼──────────────────────┤
 *   │ input            │ 1.260 ✗ / 1.542 ✗    │ 2.584 ✗ / 1.542 ✗    │ 19.798 ✓ / 3.740 ✓   │
 *   │ select           │ 1.260 ✗ / 1.542 ✗    │ 2.584 ✗ / 1.542 ✗    │ 19.798 ✓ / 3.740 ✓   │
 *   │ textarea         │ 1.260 ✗ / 1.542 ✗    │ 2.584 ✗ / 1.542 ✗    │ 19.798 ✓ / 3.740 ✓   │
 *   │ checkbox         │ 1.260 ✗ / 1.542 ✗    │ 2.584 ✗ / 1.542 ✗    │ 19.798 ✓ / 3.740 ✓   │
 *   │ button(無邊框)   │ 1.140 ✗ / 2.584 ✗    │ 2.584 ✗ / 1.542 ✗    │ 19.798 ✓ / 3.740 ✓   │
 *   │ button(實心黃鈕) │ 1.445 ✗ / 2.584 ✗    │ 1.601 ✗ / 1.542 ✗    │ 13.642 ✓ / 3.740 ✓   │
 *   ├──────────────────┼──────────────────────┼──────────────────────┼──────────────────────┤
 *   │ 元件（暗色）     │                      │                      │                      │
 *   │ input            │ 1.479 ✗ / 1.870 ✗    │ 4.175 ✓ / 1.870 ✗    │ 18.968 ✓ / 5.057 ✓   │
 *   │ select           │ 1.479 ✗ / 1.870 ✗    │ 4.175 ✓ / 1.870 ✗    │ 18.968 ✓ / 5.057 ✓   │
 *   │ textarea         │ 1.479 ✗ / 1.870 ✗    │ 4.175 ✓ / 1.870 ✗    │ 18.968 ✓ / 5.057 ✓   │
 *   │ checkbox         │ 1.479 ✗ / 1.870 ✗    │ 4.175 ✓ / 1.870 ✗    │ 18.968 ✓ / 5.057 ✓   │
 *   │ button(無邊框)   │ 1.141 ✗ / 3.781 ✓    │ 3.781 ✓ / 1.870 ✗    │ 17.176 ✓ / 5.057 ✓   │
 *   │ button(實心黃鈕) │ 4.468 ✓ / 4.175 ✓    │ 3.783 ✓ / 1.880 ✗    │ 1.390 ✗ / 5.152 ✓   │
 *   └──────────────────┴──────────────────────┴──────────────────────┴──────────────────────┘
 *   （C 欄是備案的實測，不是本次出貨的狀態；出貨的是 B 欄。做法見「六」。）
 *
 *   ★結論一（暗色）：官方用法把暗色救起來了。表單控制項的 border 換色層
 *     1.479 → **4.175**，過 3:1。這是「只抄一半」補上另一半換來的，token 一個字沒動。
 *   ★結論二（亮色）：**照官方用法做完，亮色仍然過不了 3:1，最高只有 2.584。**
 *     這不是實作沒做好，是數學上限：#A1A1A1 對純白就是 2.584，
 *     50% 疊白後是 #D0D0D0＝1.542。官方 apps/v4 的亮色焦點環本身就不滿足 SC 1.4.11。
 *   ★結論三（要老實講的退步）：`.btn` 原本是全站唯一用**全不透明** ring 的地方
 *     （亮 2.584 / 暗 3.781~4.468）。改成官方的 /50 之後，
 *     亮色實心鈕從 2.584 掉到 1.601、暗色無邊框鈕的柔環從 3.781 掉到 1.870。
 *     暗色因為 border 換色補上了 3.781/3.783 所以整體仍過關；**亮色是淨退步**。
 *   ★button(無邊框) 的 border 欄在 A/B 都低，是因為它是 .navbar-link.btn.border-0，
 *     border-width:0 → border-ring 是 no-op。這點與官方預設 Button 一致（官方 base 也沒有 border 寬度）。
 *
 * ── 五、抓到但本次沒修（留給業主決定，不是遺漏）──────────────────────────
 *   (1) `.app-content .form-control.is-invalid:focus` 等三條錯誤態焦點環，仍是舊的
 *       「2px 背景色間隙 + 4px destructive/.4」。沒一起改的理由：官方對應寫法是
 *       `aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40`，
 *       亮暗**不同 alpha**，要另外加一條 html.dark 覆蓋，而且 /.4 → /.2 在亮色是**降低**對比。
 *       它需要自己一輪量測才動得了，硬改會變成沒量過就宣稱做好。目前它比新配方更醒目，不急。
 *   (2) resources/views/{auth/_auth_shell_css,payment/_shell_css,buyer/layout}.blade.php
 *       三支 blade 各自內嵌 --ring 焦點寫法（`0 0 0 3px hsl(var(--ring)/.16)`、
 *       `outline: 2px solid`、間隙環），與本檔已經分岔。本次任務限定只動本檔，未動。
 *       ★它們是登入頁／付款頁／買家頁，是站外使用者第一眼看到的頁面，值得單獨排一次。
 *
 * ── 六、備案（C 欄）怎麼做，若業主選它 ──────────────────────────────────
 *   **維持本次的官方用法不變**，只把兩顆 token 換成高對比值（各一行）：
 *       :root    --ring: 0 0% 63.1%  →  0 0% 3.9%   (#0A0A0A)
 *       html.dark --ring: 0 0% 45.1% →  0 0% 98%    (#FAFAFA)
 *   這兩個值仍是 shadcn 官方 v4 調色盤裡的數字（分別等於亮色 --foreground 與暗色 --primary），
 *   而且維持 §S-72 訂下的 achromatic（不像 v3 的 240 10% 3.9% 會把藍味帶回這套已經全中性的色板）。
 *   實測十二格全部過 3:1（唯一 ✗ 是暗色黃鈕的白 border 疊黃底 1.390，
 *   但同一顆的柔環是 5.152 ✓，指示整體看得見）。
 *   ★不做的事：不自己調一個「介於中間」的顏色去湊 3.0x。要嘛官方值，要嘛高對比值，二選一。
 * ------------------------------------------------------------------------- */

/* --- S-74. 漏網中性 token 一起 achromatic 化 ＋ 修 .btn-outline-*.active 失效（2026-08-07）---
 * 業主：「一切都恢復 shadcn 官方設定就對了」「一起改都做成 shadcn 的標準」「小心不要影響到功能」。
 *
 * ── 一、為什麼還要這一節（§S-72 沒做完的那一半）──────────────────────────
 *   §S-72 把 shadcn **有對應語彙**的中性色整組換成官方 A 的 achromatic 值
 *   （--background / --foreground / --card / --muted / --accent / --border / --ring …），
 *   但它在自己的收尾清單裡寫了一句「本專案自有 token …**一顆都沒動**（官方沒有對應語彙）」。
 *   ★那句話當時是誠實的（它確實沒動），但它造成的**結果**是個缺陷：
 *     自有的中性 token 全都還留在 zinc 家族（hue 240、帶藍調），於是
 *     **同一個畫面上同時存在兩種灰** —— 例如側欄底 #FAFAFA（純中性）配側欄文字 #52525B（帶藍），
 *     或圖表的近黑長條 #18181B（帶藍）緊鄰 btn-dark #171717（純中性）。
 *     色差雖小（多半 4~9/255），但它是**系統性**的：中性色本來就是靠「沒有色相」在當背景，
 *     一旦有兩套，畫面會有一種說不上來的髒感，而且每加一個新元件就多一次分岔。
 *   ⇒ 本節把剩下的自有中性 token 也 achromatic 化，讓全檔只剩「一種灰」。
 *
 * ── 二、規則與兩個例外（例外才是這一節真正的內容）──────────────────────
 *   規則：保持明度 L 不變、把 H/S 歸零；能對上官方階就對上。
 *   官方階（＝§S-72 用的那一組，對應 oklch 值）：
 *       100% / 98% / 96.1% / 89.8% / 63.1% / 45.1% / 25.1% / 14.9% / 9% / 3.9% / 0%
 *   ★★但「吸到最近的官方階」不是無條件的。有兩顆吸過去會**改到功能而不是改到色相**，
 *     這一節刻意不吸，理由逐行寫在 :root / html.dark 的宣告旁：
 *       (1) 亮色 --sidebar-accent 94%：吸到 96.1% 會讓「目前所在頁」的底色與側欄底的
 *           ΔRGB 從 11 掉到 5（對比 1.1009 → 1.0445），指示器強度直接砍半。
 *           而且這顆有前科（業主：「馬的，我要你恢復正常的左邊選單顏色」）。
 *           ⇒ 只去藍味：#EFEFF0 → #F0F0F0，亮度一格沒動。
 *           ★注意：96.1% **確實是官方值**（官方 --sidebar 0.985 / --sidebar-accent 0.97 就是 Δ5），
 *             所以這是一個「官方 vs 現有可用性」的取捨，不是我判定官方錯了。
 *             要 100% 官方就把那行換成 `0 0% 96.1%`，一行的事，留給業主拍板。
 *       (2) 暗色 --sidebar-active-bg 19%：最近的官方階 14.9% 正好等於 --sidebar-accent，
 *           吸過去 hover 與 active 會撞成同一個顏色（Δ0）。保 L 之後兩者 ΔRGB 由 8 變 10。
 *   ★另有一顆 (3) 亮色 --sidebar-fg 34%：最近的官方階 25.1% 差 8.9pp＝23/255，
 *     會把側欄文字整體壓黑一階、並壓掉 fg / fg-strong 的層次約三成 ⇒ 同樣只去色相。
 *
 * ── 三、逐顆對照（原值 / 新值 / 是否官方階 / 最大色差）────────────────────
 *   ┌─────────────────────────┬──────────────────┬─────────────┬────────┬──────┐
 *   │ token                   │ 原值             │ 新值        │ 官方階 │ Δ255 │
 *   ├─────────────────────────┼──────────────────┼─────────────┼────────┼──────┤
 *   │ 亮 --hint               │ 240 5%   64.9%   │ 0 0% 63.1%  │  ✔     │  9   │
 *   │ 亮 --dark               │ 240 5.9% 10%     │ 0 0% 9%     │  ✔     │  4   │
 *   │ 亮 --dark-panel         │ 240 5.9% 10%     │ 0 0% 9%     │  ✔     │  4   │
 *   │ 亮 --chart-2            │ 240 5.9% 10%     │ 0 0% 9%     │  ✔     │  4   │
 *   │ 亮 --sidebar-fg         │ 240 5%   34%     │ 0 0% 34%    │  ✗保L  │  5   │
 *   │ 亮 --sidebar-fg-strong  │ 240 6%   10%     │ 0 0% 9%     │  ✔     │  4   │
 *   │ 亮 --sidebar-border     │ 240 6%   90%     │ 0 0% 89.8%  │  ✔     │  2   │
 *   │ 亮 --sidebar-accent     │ 240 5%   94%     │ 0 0% 94%    │  ✗保L  │  1   │
 *   │ 亮 --sidebar-active-bg  │ 240 5%   90%     │ 0 0% 89.8%  │  ✔     │  2   │
 *   │ 暗 --hint               │ 240 3.8% 46.1%   │ 0 0% 45.1%  │  ✔     │  7   │
 *   │ 暗 --dark-foreground    │ 240 5.9% 10%     │ 0 0% 9%     │  ✔     │  4   │
 *   │ 暗 --dark-panel         │ 240 5.9% 10%     │ 0 0% 9%     │  ✔     │  4   │
 *   │ 暗 --chart-2            │ 240 5%   90%     │ 0 0% 89.8%  │  ✔     │  2   │
 *   │ 暗 --sidebar-bg         │ 240 5.9% 10%     │ 0 0% 9%     │  ✔     │  4   │
 *   │ 暗 --sidebar-fg         │ 240 5%   65%     │ 0 0% 63.1%  │  ✔     │  9   │
 *   │ 暗 --sidebar-border     │ 240 3.7% 15.9%   │ 0 0% 14.9%  │  ✔     │  4   │
 *   │ 暗 --sidebar-accent     │ 240 3.7% 15.9%   │ 0 0% 14.9%  │  ✔     │  4   │
 *   │ 暗 --sidebar-active-bg  │ 240 3.7% 19%     │ 0 0% 19%    │  ✗保L  │  2   │
 *   │ light-scope --dark      │ 240 5.9% 10%     │ 0 0% 9%     │  ✔     │  4   │
 *   │ .text-dark 硬寫色       │ hsl(240 10% 3.9%)│ var(--fg)   │  ✔     │ 11   │
 *   └─────────────────────────┴──────────────────┴─────────────┴────────┴──────┘
 *   ★宣告處全部列在上面對應的 :root / html.dark / [data-bs-theme="light"] 區塊裡，
 *     每一顆都逐處改過（--dark-panel 與 --chart-2 各有兩處、--dark 有三處）。
 *     ★這一步是這節最容易做錯的地方：本檔同一顆 token 常有 3~4 處宣告、特異性相同、
 *       純靠原始碼順序決勝負，只改第一處會被後面蓋掉，變成「寫了但沒生效」。
 *   ★--chart-2 與 .text-dark 那條硬寫色**不在原本的清單上**，是自己 grep 出來的漏網，
 *     一併處理（否則「全檔只剩一種灰」這句話就是假的）。
 *
 * ── 四、關鍵對比前後（計算值，實測值見下面「六」）──────────────────────
 *     亮 --hint 佔位符 on #FFF      2.563 → 2.584（官方 #A1A1A1 對白就是這個數，見 §S-72）
 *     暗 --hint on #0A0A0A          4.097 → 4.175
 *     亮 側欄文字 on 側欄底         7.41  → 6.92（仍遠高於 AA 4.5）
 *     暗 側欄文字 on 側欄底         6.91  → 6.94
 *     亮 所在頁文字 on 所在頁底    15.42  → 15.42（兩者同步位移，關係不變）
 *     亮 btn-dark 白字             16.97  → 17.18
 *     亮 .text-dark on #FFF        20.35  → 21.00
 *   ★沒有任何一項掉到門檻以下；多數是持平或微升。
 *
 * ── 五、修 `.btn-outline-*.active`：這是缺陷，不是死碼 ────────────────────
 *   §28 給 outline 系列 `--bs-btn-active-bg: hsl(var(--accent))`，而 --accent 與 --secondary
 *   同值 ⇒ Bootstrap 標準的 `.btn.active` 選中態畫出來與未選中**完全一樣**。
 *   也就是說：本主題把 Bootstrap 的選中態靜靜地弄失效了，寫 `.active` 的人不會收到任何警訊。
 *   ★這不是理論：`resources/views/Notification/index.blade.php` 的註解記著開發者踩過
 *     ——「三顆 btn-outline-secondary 靠 .active 區分 … 實測三顆長得一模一樣」，
 *     當時是繞過去（改用別的元件），病根沒治。這一節治它。
 *
 *   ★動手前先 grep（2026-08-07 實證，不是照抄前人結論）：
 *     · `resources/views/**` 帶 `.btn-check` ................ 0 處
 *     · `.btn-outline-*` 元素身上帶 `.active` .............. **0 處**
 *       （blade 裡兩筆 `btn-outline` + `active` 同行的命中都在**註解**裡；
 *         JS 的 classList.add('active') 掛的是 .dropdown-item / .theme-card / label / .btn-white，
 *         沒有一個是 .btn-outline-*；`data-bs-toggle="button"` 也是 0 處。）
 *   ⇒ 遠低於「超過 20 處就停下來回報」的門檻，而且**今天沒有任何按鈕的外觀會因此改變**。
 *     這條規則的價值是：下一個照 Bootstrap 文件寫 `.active` 的人，會直接得到正確的選中態。
 *
 *   做法：不覆蓋 background/color 本身，而是把 Bootstrap 自己會讀的三顆
 *   `--bs-btn-active-*` 換掉，讓 Bootstrap 原生的 `.btn.active` 規則去畫。
 *   好處是 hover / focus / disabled 的四態行為完全維持 Bootstrap 原樣，不需要 !important。
 *   值**逐字取自本檔 §btn-check 選中態**（--dark 實心 / -primary 用 --primary），
 *   同一個 App 裡同一種控制項只能有一種選中語彙，不另立第三種。
 *   ★選擇器 (0,2,0) > §28 的 (0,1,0)，而且位置在檔尾，兩個維度都贏，不靠運氣。
 *   ★`.btn-outline-danger` 不在此列：§S-71 已給它自己的 shadcn outline 語意，
 *     它的選中態要不要一起做是另一個設計決定，這節不順手決定（見「七」）。
 * ------------------------------------------------------------------------- */

/* 中性 outline 系列：清單與 §28 那條逐字相同，確保沒有一個變體被漏在失效狀態。 */
.btn-outline-secondary.active,
.btn-outline-success.active,
.btn-outline-info.active,
.btn-outline-warning.active,
.btn-outline-dark.active {
    --bs-btn-active-bg: hsl(var(--dark));
    --bs-btn-active-border-color: hsl(var(--dark));
    --bs-btn-active-color: hsl(var(--dark-foreground));
}
/* -primary 跟著 §btn-check 選中態 走品牌色，不併入上面那組。 */
.btn-outline-primary.active {
    --bs-btn-active-bg: hsl(var(--primary));
    --bs-btn-active-border-color: hsl(var(--primary));
    --bs-btn-active-color: hsl(var(--primary-foreground));
}


/* --- S-75. 商店／串接挑選卡：品牌塊改成 App Store 式「應用程式圖示」（2026-08-07）---
   ★這一行是給 tests/Feature/Theme/ThemeOverrideAnnotationsTest 認的「章節錨點」，
     寫法必須是 `/* --- S-75. …`（S- 前面不可以多 §）。

   ── 需求來源：業主原話三則，合起來才是完整的需求 ──────────────────────
     ① 「這邊的卡片設計不好 有沒有更好的設計方式 使用 shadcn」（08-07 上午）
     ② 「我是希望**品牌卡片** 而不是磚」   ← 否決「把品牌色縮成 40px 角落小標記」
     ③ 「有 **app 軟體的感覺**會比較好 **商店感**」
   ②③ 合起來指向一個很具體的語彙：**App Store 的應用程式列**——
   大顆的圓角方形圖示當主角，右邊是 App 名稱、分類、狀態，下面是說明與「取得」鈕。
   這頁本來就是「挑一個平台來串接」，把每個平台當成一個可安裝的 App 陳列，
   語意與版式剛好對上。

   ── 為什麼不是「滿版品牌色封面帶」，也不是「40px 小方磚」──────────────
     · 40px 小方磚 = 業主 ② 明白否決的「角落標記」。
     · 滿版色帶 = 把整條卡頭染成品牌色，8 張並排仍然是一面色票牆（就是 ① 在嫌的東西），
       而且暗色下深色品牌（酷澎 #1C1A4E / SHOPLINE #000000）與卡片底 #171717
       的對比只有 1.12 / 1.17，色帶等於沒有邊界。
     · 56px 圓角方形圖示 = 面積夠大所以是「主角」（不是標記），但仍是**有界的形狀**，
       邊界由 1px ring 與圖示筆畫定義，不依賴品牌色與卡片底的色差。

   ── 尺寸 56px 是量出來的，不是挑順眼的 ─────────────────────────────
   最窄的情境是 1280（col-xl-3 生效、側欄佔掉約 220px）：卡片外寬實測 241px，
   扣 2px 邊框、扣 .card-body 的 24px×2 內距 ⇒ **內容寬 191px**。
   圖示 56 ＋ gap 12 ＝ 68 ⇒ 文字欄剩 123px；最長的 CJK 標題「Yahoo購物中心」
   7 字 × 16px ＝ 112px，還有 11px（9%）餘裕，不折行。
   ★同一算式取 64px：文字欄只剩 115px，餘裕 3px（2.6%）—— 字型度量只要差一點就折行，
     所以 64 被否決。56 是「業主指定的 56–64 級距」裡，在最窄斷點仍留有 ≥5% 餘裕的最大值。
   ★375（單欄）卡片內容寬約 309px，圖示佔 18%，比例仍然像 App Store 的列。

   ── 圓角吃 --radius-xl（14px）─────────────────────────────────────
   14 / 56 ＝ 25%，與 iOS App 圖示的 22.5% 同量級；而且它就是本專案 Card 自己的圓角階，
   圖示與卡片同階，不引進第四種圓角。

   ── 圖示裡放什麼：首字 monogram（不是圖檔，也不是 lucide 類別圖示）──────
   ★先查過專案裡有沒有真 logo（find public resources -iname '*.png|svg|jpg|webp'）：
       · public/assets/pic/shopee.png / moplus.png / mallbic.png（各 1200×1200）
         **不是 logo**，是「品牌色底 ＋ 中文名」的點陣圖，等於把文字 LOGO 存成 PNG。
         用它們會得到「放大就糊、不隨主題翻轉、每張多 4～6KB」的下位版本。
       · public/assets/pic/l_hct.jpg / l_kerry.jpg 是真的物流商 logo，但只覆蓋 11 張卡裡的 2 張，
         而且是 100px 的 JPG（白底方框），疊在品牌色圖示上會出現白色殘邊。
       · 酷澎 / Yahoo / momo / SHOPLINE / 自訂商店 / 光貿電子發票 **完全沒有圖檔**。
     ⇒ 沒有可用且**一致**的 logo 組，硬用會變成「3 張有圖、8 張沒圖」。
   ★退回文字，而且這正是業主既有的指示：「LOGO 使用文字啊，如同商店串接的做法一樣」。
     取 $name 的第一個字 ⇒ 蝦／M／酷／Y／m／S／自／雲／光／大／新，11 張卡兩兩不同。
   ★沒有用 lucide 類別圖示的原因：類別（API串接／物流／發票…）現在已經**以文字**
     排在標題下方那一行了，再放一顆同義的圖示是重複；而且六個 API 平台會拿到同一顆 plug，
     那就不是「每個 App 有自己的圖示」的商店感。

   ── 字級 1.5rem/700：這是**文字**，所以要過 WCAG ────────────────────
   白字對品牌色的實測對比：#EE4D2D（蝦皮）3.73:1、#F26522（大榮）3.22:1，兩者都 < 4.5，
   靠的是 large text 的 3:1 門檻（≥24px，或 ≥18.66px 且粗體）。
   24px ＋ 700 同時滿足兩個條件 ⇒ 3.73 / 3.22 皆過。**不可以再往下降**。
   （這條結論沿用 _picker_card 檔頭原本替 20px wordmark 寫的那段實測，只是換了尺寸。）

   ── 1px ring：暗色下「圖示的形狀」靠它，不靠色差 ──────────────────
   `0 0 0 1px hsl(var(--border))` ＝ shadcn 全站描邊語彙本身（亮色 #E5E5E5、
   暗色 white/10%）。暗色下它疊在卡片底 #171717 上算出 rgb(46,46,46)，
   對卡片 1.35:1、對 #000 的圖示 1.55:1 —— 與這個主題**所有**暗色邊框同一個量級，
   不是特例。真正扛住形狀的是圖示裡那個近白的字（對 #000 是 20:1 量級），
   ring 負責把方形的四個邊補起來。兩者一起量的結果寫在回報裡。
   ★用外框 ring 不用 inset：inset 會被畫在品牌色**裡面**，深色品牌（#000）疊
     white/.18 只到 rgb(45,45,45)，對卡片 1.32 —— 08-06 的 .sc-brand-mark 就是這樣做的，
     實測不夠。外框 ring 畫在卡片那一側，是與卡片比、不是與品牌色比。
   ------------------------------------------------------------------------- */
.sc-app-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;                    /* 圖示是固定尺寸，不參與 flex 伸縮 */
  width: 3.5rem;                     /* 56px（推導見上） */
  height: 3.5rem;
  border-radius: var(--radius-xl);   /* 14px，與 Card 同階 */
  font-size: 1.5rem;                 /* 24px ＋ 700 ＝ WCAG large text，過 3:1 */
  font-weight: 700;
  line-height: 1;
  text-align: center;
  overflow: hidden;                  /* 首字若是寬字形也不會頂出圓角 */
  user-select: none;                 /* 它是圖示不是可選取的內文；平台名在旁邊那行 */
  box-shadow: 0 0 0 1px hsl(var(--border));
}
/* ★★暗色把 ring 從 --border（white/10%）加強到 white/22%。
   ── 為什麼要加強（實測，1440 暗色，像素取樣橫切圖示左緣）──
   卡片底 rgb(23,23,23)｜--border 的 ring 算出來是 rgb(46,46,46) ⇒ 對卡片只有 **1.32:1**。
   對「品牌色本身就很亮」的圖示（蝦皮 4.90、大榮 5.69）無所謂，
   但對深色品牌（酷澎 #1C1A4E 填色對卡片 1.12、SHOPLINE #000000 1.17、
   新竹物流 #003DA5 1.89、MO店+ 1.93、Yahoo 1.94）就是「方形的輪廓看不出來」。
   white/22% 疊在卡片底上實測 rgb(74,74,74) ⇒ 對卡片 **2.02:1**（11 張卡逐張相同），
   對 #000 填色 2.37、對 #1C1A4E 1.81。
   ★★誠實記錄「哪裡變差了」：改版前那條是 **inset**（畫在品牌色上面），
     所以它的顏色跟著品牌色跑 —— MO店+ 算出 rgb(84,102,147) 對卡片 2.90、
     酷澎 rgb(68,66,109) 對卡片約 2.0、SHOPLINE 只有 rgb(45,45,45) 1.32。
     改成外框之後 11 張全部收斂到同一個 2.02：**最差的那張從 1.32 提到 2.02，
     最好的那張從 2.90 降到 2.02**。取「一致且沒有任何一張低於 2.0」而不是
     「有些很好、有些看不見」—— 這頁的病本來就是「每張卡的可辨識度不一樣」。
   ── 為什麼是 22% 不是更高 ──
   iOS 暗色模式給深色 App 圖示描的那條 separator 就是白 0.16～0.20 這個量級；
   再往上（white/35% 才摸得到 3:1）會變成一圈比全站任何邊框都亮 3.5 倍的灰線，
   與 shadcn 暗色「所有邊框都是 white/10%」的語彙打架，8 張並排會看起來像選取狀態。
   ── 誠實標記：這條**沒有**達到 WCAG 1.4.11 的 3:1 ──
   1.4.11 管的是「辨識 UI 元件所必需」的非文字對比。這顆圖示是**裝飾性**的
   （blade 上掛 aria-hidden，平台全名以 16px 文字排在它右邊，對比 17:1 量級），
   資訊沒有任何一份只存在於這個方塊裡，所以判定為不適用而不是「放寬」。
   真正扛住「這裡有一顆圖示」的是裡面那個近白的首字（對卡片底 17.18:1），
   ring 只負責把方形的四個邊補起來。兩個數字都寫在回報裡，不挑好看的講。
   ── 為什麼只改暗色 ──
   亮色下卡片是白底，品牌色圖示對它 3.0～20:1，本來就看得見；
   在那裡加一圈灰線只會多一個沒必要的邊。亮色維持 --border（幾乎是 no-op）。 */
html.dark .sc-app-icon { box-shadow: 0 0 0 1px hsl(0 0% 100% / .22); }

/* 品牌圖示：底色由呼叫端以 inline style 帶入（對方的品牌規範，兩模式都不翻轉），
   這裡只負責前景 —— --dark-panel-foreground 是主題給「恆深底面板」準備的恆亮前景
   （亮暗同為 0 0% 98%），與 .sc-brand-mark 當初用的是同一顆，語意一致。 */
.sc-app-icon--brand { color: hsl(var(--dark-panel-foreground)); }
/* ★中性圖示（自訂商店／雲端小幫手）**沿用** §S-54 ② 既有的 .sc-brand-mark--neutral，
   不另外抄一份：那條規則的本體只有 background-color / color 兩行
   （會翻轉的 --dark / --dark-foreground，亮色近黑底白字、暗色近白底黑字，
   兩邊對比都在 19:1 量級），與元素幾何完全無關，所以拿來給 App 圖示用是 SSOT 不是誤用。
   ★要動 §S-54 那條之前先 grep `sc-brand-mark--neutral` —— 它現在有 1 個呼叫端（本卡）。
   ★★同一節的 `.sc-brand-mark`（20px wordmark chip 的幾何）在本輪之後**沒有呼叫端了**
     （品牌塊不再是「色底大字」）。照專案守則「禁止刪除任何內容」，那條規則原文
     一個字都沒有動，只在這裡標明它現在是死碼；哪天要退場請一併看 §S-54 ② 的說明。 */

/* 圖示右邊的文字欄。min-width:0 讓長標題可以在欄內換行，而不是把圖示擠掉。
   ★不能用 .min-w-0 utility：本主題只有在 `@media (max-width: 767.98px)` 裡定義它
     （§響應式溢出 ④），桌機寬度下**它不存在** —— 這是實測踩過的坑，寫在這裡免得再踩。 */
.sc-app-meta { min-width: 0; flex: 1 1 auto; }

/* App 名稱 ＝ 官方 CardTitle（`leading-none font-semibold`，字級吃 Card 的 base 16px）。
   ★<h5> 預設是 1.25rem/20px，會比整頁 h1（.fb-topbar-title 實測 16.8px/600）還大又還粗
     —— 那就是 08-07 檔頭記的「字級階梯倒掛」。壓到 1rem 之後階梯才正著走。
   ★line-height 用 1.35 不是官方的 leading-none(1)：官方那個值是給單行英文標題的，
     這裡的 CJK 標題在窄卡會折成兩行，1 會讓兩行黏在一起。 */
.sc-app-title { font-size: 1rem; line-height: 1.35; }

/* --- S-76. 挑選卡回到「品牌色滿版封面」，但用 shadcn 的 Card 封面語彙（2026-08-07）---
   ★這一行是給 tests/Feature/Theme/ThemeOverrideAnnotationsTest 認的「章節錨點」，
     寫法必須是 `/* --- S-76. …`（S- 前面不可以多 §）。

   ── 需求來源：業主原話兩則 ────────────────────────────────────────
     ④ 「其實**原本的底色版滿版設計**我覺得滿好的啊」
     ⑤ 「你有辦法**學原本的設計 但又是使用 shadcn 的方式**嗎」
   ⇒ §S-75 那版（56px App 圖示 ＋ 中性標題）被否決，視覺回到「品牌色大面積」。

   ── 診斷：原本那版錯的不是「色塊太大」──────────────────────────────
   §S-75 的第二段寫「滿版色帶 ＝ 8 張並排仍然是一面色票牆（就是 ① 在嫌的東西）」——
   那句話把症狀當成了病因，本節據實訂正（原文一字未動，仍留在 §S-75 供對照）。
   真正的病因是：色帶被寫成 `<h5 data-slot="card-title" style="background-color:…">`，
   用「標題」的語意去承載「封面」的角色。同一個元素同時要當
   「這張卡叫什麼」與「這張卡的視覺主體」，所以它大也不對（壓過頁面 h1）、
   小也不對（品牌不見了）。
   ★shadcn 的 Card 本來就有「封面／媒體區」這個位置（官方 Card 範例裡卡片頂部那塊圖片，
     是 Card 的直接子元素、排在 CardHeader 之前）。品牌色帶就是那塊，
     只是內容是純色不是照片。拆成「封面 ＋ 封面裡的標題」兩個元素之後，
     兩個角色各自可以做對，而且版式就是業主喜歡的那一版。

   ── 滿版貼齊：邊界改由「卡片自己的 1px border ＋ 14px 圓角」定義 ──────────
   §S-75 花了一整段用 white/22% 的 ring 去補一顆 56px 圖示的四個邊，
   原因是那顆圖示浮在卡片中間、四周都是卡片底，只能自己長邊框。
   封面貼齊卡片左／右／上三邊之後，那三個邊由卡片本來就有的邊框免費提供，
   **不需要任何額外描邊**，也不再依賴「品牌色 vs 卡片底」的色差
   （暗色實測：酷澎 #1C1A4E 對卡片底 1.12、SHOPLINE #000000 1.17、
     MO店+ #2F457C 1.93、Yahoo #720e9e 1.94、momo #d11970 3.49
     —— 品牌 hex 是對方的品牌規範、一個字不能改，所以色差治不好；但形狀不必靠色差）。
   只剩「封面下緣 vs 內容區」一條界線要自己處理，見下面的 border-bottom。

   ── 圓角：用封面自己的 border-top-*-radius，不是在 .card 上開 overflow:hidden ──
   兩種做法差別很大，這裡選前者：
     ① overflow:hidden 會把子元素畫在自己邊界外的東西整片裁掉 —— focus ring、
        outline、box-shadow 都算，**也包含 `.stretched-link::after`**：
        它撐滿卡片的 padding box，被 13px 圓角裁掉之後，卡片四個角落會出現點不到的死角。
        （實測：在 .sc-picker-card 上臨時加 overflow:hidden，距角 2px 的取樣點
          由「命中 CTA」變成「命中卡片本身」；拿掉就恢復。誘餌腳本見回報。）
     ② 作用域：per-element 圓角只影響 .sc-picker-cover 一個 class；
        在 .card 上動 overflow 是全站語彙的變更（.card 全站數百個呼叫端）。
     ③ 這也是本專案既有的 SSOT：§5 讓「貼卡片底的表格」自己吃
        `calc(var(--radius-xl) - 1px)`，同樣沒有在 .card 上開 overflow。
   ★半徑值直接吃 Bootstrap 自己的 `--bs-card-inner-border-radius`
     —— §5 已經把它定義成 `calc(var(--radius-xl) - 1px)` ＝ 13px（內圓角 ＝ 外圓角減
     1px 邊框寬）。用 token 不再抄第二份數字，動 --radius 時兩邊自動同步。

   ── 為什麼封面必須是 .card 的直接子元素（不是 .card-body 裡面）────────────
   .card-body 有 24px（<768 是 16px）內距，封面放在裡面就得抵銷那層內距。
   ★用負 margin 抵銷被否決：負值要跟著 p-3 / p-md-4 兩個斷點各寫一份，
     任何一邊漂移就露縫或溢出圓角；而且 375 只有 16px 內距，負 24px 會把封面推出卡片。
   搬到 .card-body 之外就**根本不需要抵銷** —— .card 是 display:flex; flex-direction:column，
   封面與 .card-body 自然是上下兩塊，封面寬度天生等於卡片內寬，margin 為 0。
   ★.card-body 的 flex:1 1 auto 不變 ⇒ .card h-100 的等高與 .flex-1 把 footer 推到卡底
     這兩件事都照舊成立。
   ★.stretched-link 的覆蓋範圍也不受影響：它的 ::after 是對「最近的 position:relative
     祖先」定位的，那個祖先仍然是 .card 本身（.sc-picker-card position-relative），
     所以整卡可點的範圍反而變大了（現在連封面也在裡面）。

   ── 內距：橫向必須與 .card-body 對齊，縱向自己決定 ───────────────────
   封面裡那行平台名的左緣要與下方的 badge／說明／CTA 共用同一條左緣，
   所以橫向內距逐值抄 .card-body 的 p-3 / p-md-4（16 / 24px，斷點同為 768）。
   縱向取 16px（兩個斷點都是）：封面高度 ＝ 16 ×2 ＋ 行高 25 ＝ 57px，
   與 §S-75 那顆 56px 圖示同量級 ⇒ 卡片總高幾乎不變，不會因為改版多捲一段。
   ★沒有回到最早那個 200px 大色塊：那個高度佔整張卡 56%，承載的資訊只有平台名，
     資訊密度與視覺重量不成比例（見 _picker_card 檔頭 08-06 的實測）。
     「滿版」指的是**貼齊左右邊**，不是「高度要很高」。

   ── 封面下緣那條線：為什麼是 border-bottom ＋ background-clip: padding-box ──
   封面與內容區之間需要一條看得出來的界線，否則暗色下 SHOPLINE（#000000，
   對卡片底 1.17）那張卡的封面等於沒有下緣。
   ★不用 box-shadow：外陰影的繪製順序在「後面的兄弟元素背景」之前，
     .card-body 哪天被賦予背景色就會把它蓋掉，是個沉默的地雷。
   ★用 border-bottom 就必須配 `background-clip: padding-box`：
     預設 border-box 會讓封面的品牌色**畫到邊框底下**，半透明的邊框色於是疊在
     品牌色上（#000 上疊 white/22% 只到 rgb(45,45,45)，對卡片底 1.32 ——
     這正是 §S-75 說 inset ring 不夠力的同一個原因）。
     改成 padding-box 之後品牌色停在邊框內側，邊框那 1px 疊的是**卡片底色**，
     算出來與全站其他邊框同一個值。
   ★亮色用 --border（rgb(229,229,229)）：與同一張卡 footer 的 border-top 同一條語彙。
   ★暗色加強到 white/22%：--border 在暗色是 white/10%，疊在卡片底 #171717 上
     只到 rgb(46,46,46)、對卡片 1.32:1；white/22% 算出 rgb(74,74,74)、對卡片 2.02:1。
     這個 22% 不是本節新發明的，是 §S-75 已經為暗色 ring 訂過並實測過的同一個值，
     兩節共用同一個「暗色下把形狀補起來」的量級。
   ── 誠實標記：這條線一樣**沒有**達到 WCAG 1.4.11 的 3:1 ──
   1.4.11 管的是「辨識 UI 元件所必需」的非文字對比。封面是裝飾性的色塊，
   平台名以 20px 文字疊在上面（對品牌色 3.1～17:1），資訊沒有任何一份只存在於色塊裡；
   再往上調（white/35% 才摸得到 3:1）會變成一條比全站任何邊框都亮 3.5 倍的灰線，
   與 shadcn 暗色「所有邊框都是 white/10%」的語彙打架。判定為不適用而不是「放寬」。
   ------------------------------------------------------------------------- */
.sc-picker-cover {
  padding: 1rem;                                                  /* 縱 16px；橫 16px 對齊 .card-body p-3 */
  border-bottom: 1px solid hsl(var(--border));
  background-clip: padding-box;                                   /* 讓下緣那 1px 疊在卡片底色上，不是疊在品牌色上 */
  border-top-left-radius: var(--bs-card-inner-border-radius);     /* §5 已定義 ＝ calc(--radius-xl - 1px) ＝ 13px */
  border-top-right-radius: var(--bs-card-inner-border-radius);
}
@media (min-width: 768px) {
  /* ≥768 是本卡多欄版面的起點，也是 .card-body 由 p-3 換成 p-md-4 的斷點；
     橫向內距必須同步，封面上的平台名才會與下方所有內容共用同一條左緣。 */
  .sc-picker-cover { padding: 1rem 1.5rem; }
}
html.dark .sc-picker-cover { border-bottom-color: hsl(0 0% 100% / .22); }

/* 品牌封面：底色由呼叫端以 inline style 帶入（對方的品牌規範，兩模式都不翻轉），
   這裡只負責前景 —— --dark-panel-foreground 是主題給「恆深底面板」準備的恆亮前景
   （亮暗同為 0 0% 98%），與 .sc-brand-mark / .sc-app-icon--brand 用的是同一顆。
   ★沒有把它併進 §S-75 的 `.sc-app-icon--brand` 選擇器群組：那會動到既有規則，
     本輪的規矩是「只在檔尾新增章節，不改既有內容」。 */
.sc-picker-cover--brand { color: hsl(var(--dark-panel-foreground)); }

/* 中性封面（自訂商店／雲端小幫手）**沿用** §S-54 ② 既有的 .sc-brand-mark--neutral，
   不另外抄一份：那條規則的本體只有 background-color / color 兩行
   （會翻轉的 --dark / --dark-foreground，亮色近黑底白字、暗色近白底黑字，
   兩邊對比都在 17～19:1 量級），與元素幾何完全無關。
   ★它現在有 1 個呼叫端（本卡的封面）；§S-75 說的「1 個呼叫端（App 圖示）」已經換人。
   ★★誠實記錄一個**沒有修**的觀察：暗色下這兩張中性卡的封面會翻成近白色塊，
     在一排深色品牌封面裡是最亮的兩張，視覺權重與語意（它們不是平台）相反。
     這是 --dark 這顆 token 的定義本身帶來的，不是本卡的幾何問題；
     要治得動 §S-54 ② 那條既有規則，超出本輪「只新增不改寫」的作用域，故只標記不動手。 */

/* 封面上的平台名 ＝ 官方 CardTitle 的內容，但它坐在品牌色上，所以字級由**對比**決定：
   前景 --dark-panel-foreground（0 0% 98%）對兩個亮品牌色實測只有
     #EE4D2D（蝦皮）3.6 量級、#F26522（大榮）3.1 量級，兩者都 < 4.5:1，
   只能走 WCAG large text 的 3:1 門檻（≥24px，或 ≥18.66px 且粗體）
   ⇒ 20px ＋ 700 剛好落在裡面。**不可以再往下降**。
   （這條結論沿用 _picker_card 檔頭 08-06 替 20px wordmark 寫的那段實測，一字未改。）
   ★★誠實記錄一個**規則衝突**：專案守則說「卡片內的字不可以比整頁 h1
     （.fb-topbar-title 實測 16.8px/600）又大又粗」，20px/700 兩者都超過。
     兩條規則在十一張卡裡的兩張（蝦皮、大榮）上直接對撞，取 WCAG ——
     對比是可及性的地板，字級階梯是視覺偏好。
     ★而且這不是新增的退步：改版前卡片裡最大的字是 .sc-app-icon 的 24px/700，
       本輪是 20px/700，往回收了一階。
   ★overflow-wrap: anywhere：「Yahoo購物中心」在 1200px 斷點的窄卡放不下一行，
     寧可折行也不要截成「Yahoo購物…」（品牌名被截比折行難讀）。
   ★line-height 1.25 不是官方 CardTitle 的 leading-none(1)：官方那個值是給單行英文
     標題的，這裡的 CJK 標題在窄卡會折成兩行，1 會讓兩行黏在一起。
   ★margin:0 是為了壓掉 <h5> 的預設下邊距 —— 封面的高度由 padding 決定，
     不要讓標題自己的 margin 混進來（上一版是靠 utility .mb-0，這裡收進元件規則）。 */
.sc-picker-cover-name {
  margin: 0;
  font-size: 1.25rem;
  font-weight: 700;
  line-height: 1.25;
  overflow-wrap: anywhere;
}

/* ★★本輪之後沒有呼叫端的規則（照專案守則「禁止刪除任何內容」，原文全部留在原地，
   這裡只標明它們現在是死碼，方便下一個人判斷）：
     §S-54 ② `.sc-brand-mark`        （20px wordmark chip 的幾何）—— §S-75 已標過
     §S-75  `.sc-app-icon` / `html.dark .sc-app-icon` / `.sc-app-icon--brand`
                                      （56px App 圖示的幾何、ring、前景）
     §S-75  `.sc-app-meta`            （圖示右邊那一欄）
     §S-75  `.sc-app-title`           （那一欄裡的 16px 標題）
   要退回 App 圖示版型的話，這四組加上 _picker_card 檔頭仍保留的 $scPickerMonogram
   就是完整的一套，不必重寫。 */

/* --- S-76a. 封面標題吃不到封面的前景色（本輪自己弄壞、實測抓到、當場修）（2026-08-07）---
   ★這一行是給 ThemeOverrideAnnotationsTest 認的章節錨點，寫法必須是 `/* --- S-76a. …`。

   ── 病徵（像素／computed 實測，11 張卡全中，亮暗兩態都中）──────────────
   §S-76 把前景色宣告在**封面**（`.sc-picker-cover--brand` 與
   `.sc-brand-mark--neutral` 都只設 background-color / color），指望封面裡的
   `<h5 class="title sc-picker-cover-name">` 用**繼承**拿到它。實測拿不到：
     亮色：封面 color = rgb(250,250,250)，但 h5 實際 color = **rgb(0,0,0)**
           ⇒ SHOPLINE 是「純黑字疊在 #000000 封面上」＝ 對比 1.00，整個平台名消失。
     暗色：兩張中性封面翻成 rgb(250,250,250) 底，h5 卻也是 rgb(250,250,250)
           ⇒ 「自訂商店」「雲端小幫手」同樣對比 1.00 消失。
   ── 真兇 ────────────────────────────────────────────────────
   §2 基礎層的 `h1, h2, h3, h4, h5, h6 { color: hsl(var(--foreground)) }`
   是掛在標題元素**自己身上的直接宣告**；繼承值再怎麼近也一定輸給直接宣告。
   ★這與 §S-11 記錄的 /main `.bg-gray-800` KPI 卡是**同一種病**
     （那一節的原文：「§S-9 的 .bg-gray-800 只把 color 設成 --dark-panel-foreground，
       那是『可繼承值』；標題有自己的直接宣告，繼承一定輸給直接宣告」）。
     §S-11 的治法就是「把顏色也宣告到深色表面**裡的標題元素**上」，這裡照做。

   ── ★★為什麼我差一點沒抓到（記下來，不要再犯）────────────────────────
   只看封面那一層的 computed color 會全部正確（250,250,250 / 23,23,23）；
   而且**暗色下的品牌卡剛好是對的** —— 因為暗色的 `--foreground` 近白，
   與 `--dark-panel-foreground` 近白撞在一起，看起來像繼承成功。
   ⇒ 「暗色量到對的值」在這裡是巧合造成的假綠燈。
     真正該量的是**承載文字的那個元素自己的 computed color**，而且亮暗都要量。

   ── 治法：一條 `color: inherit` ─────────────────────────────────
   不寫死顏色、也不把 brand / neutral 的分支再抄一份：
   「這張卡是品牌色還是中性色」這個判斷已經在封面那一層做完了
   （`.sc-picker-cover--brand` vs `.sc-brand-mark--neutral`），
   標題只要「跟著我的容器走」就好 ⇒ `inherit` 就是這句話的 CSS 寫法，SSOT 不分岔。
   ★特異度 0,2,0 > §2 的 0,0,1，穩定勝出，且不需要 !important。
   ★用後代選擇器（`.sc-picker-cover .sc-picker-cover-name`）而不是只寫
     `.sc-picker-cover-name`（0,1,0 也贏得了 0,0,1）——多綁一層容器是為了說清楚
     「它繼承的是封面的色」，而不是碰巧比基礎層重。 */
.sc-picker-cover .sc-picker-cover-name { color: inherit; }

/* --- S-76b. 封面加上 88px logo 方格 ＋ 平台名恆為白字（2026-08-07）-----------
   ★這一行是給 ThemeOverrideAnnotationsTest 認的章節錨點，寫法必須是 `/* --- S-76b. …`。

   ── 需求來源：業主再追加兩句 ────────────────────────────────────
     ⑥ 「**字要白色啊**」
     ⑦ 「你**放上 logo icon** 看看 **然後原本卡片顏色也放上**」
   ⇒ 品牌色封面**和** logo 兩個一起做；封面上的平台名必須是白的。

   ── ⑥ 白字：為什麼**不能**用 `.text-white` ────────────────────────
   主題 §S-6 有一條
     `.app-content .text-white:not([class*="bg-"]):not([class*="btn"]):not(.badge)
      { color: inherit !important }`
   只對 `.image-inner` / `[style*="background-image"]` 等少數容器開例外。
   本卡沒有 `.image-inner`，掛 `.text-white` 會被 `!important` 打回容器前景色 ——
   亮色下就是近黑，正好是要避免的那個結果。
   （這件事 _picker_card 檔頭 08-06 的既有註解早就寫過，這裡只是照著做。）
   ⇒ 正解是吃**恆亮前景 token** `--dark-panel-foreground`（:root 與 html.dark
     同為 `0 0% 98%`，刻意不翻轉，就是給「疊在恆深／恆彩底上的文字」用的）。
   ★這與「`--dark-panel` 不可以拿來當一般卡片底色」那顆已知地雷**不是同一件事**：
     地雷講的是**底色**，這裡用的是它的**前景**色，用途正確。
   ★§S-76a 已經解決「h5 吃不到繼承色」的特異度問題；這裡再對品牌封面
     **直接指名** --dark-panel-foreground，不靠 inherit 轉一手 ——
     業主要的是「白字」這個結果，寫成直接宣告，量 computed 時一眼看得出來。
     中性封面（自訂商店／雲端小幫手）維持 §S-76a 的 inherit，
     因為它的前景是**會翻轉**的 --dark-foreground（暗色下封面翻成近白底、
     字必須跟著翻成近黑），在那兩張硬寫白字就會白底白字。

   ── ⑦ logo：為什麼一定要有那顆白色方格 ─────────────────────────
   實測（另一支代理做的 contact sheet，業主看過才說「放上 logo」）：
     · logo 的顏色本來就是品牌色 ⇒ 蝦皮橘壓在 #EE4D2D、momo 洋紅壓在 #d11970、
       大榮橘壓在 #F26522，**三張在自己的品牌色上幾乎完全消失**。
     · SHOPLINE / 光貿是白色不透明底、Yahoo 自帶紫色漸層滿版底 ⇒ 直接壓上去
       是三塊突兀方塊。
   白色方格同時解掉這兩件事，而且**不必去背、不必做白色單色版**
   —— 那會改動第三方商標的造型與顏色，超出標示性使用的範圍。
   ★方格 88px、logo 64px（四邊各 12px 留白）、圓角吃 --radius-xl（14px，與卡片同階，
     不引進第四種圓角階）。
   ★MO店+ / 新竹物流抓不到方形 logo ⇒ 那一格**不上白底**，改回「品牌色底 ＋
     白色粗體文字」，但**佔同一顆格子**，所以 11 張卡的骨架完全一致
     （格子裡是 logo / lucide / 文字三選一）。

   ── 封面改成縱向堆疊（方格在上、平台名在下）─────────────────────────
   量出來的：1280 是最窄的多欄斷點，封面內容寬只有 191px。
     橫排（方格左、名稱右）⇒ 名稱只剩 191 − 88 − 12 = **91px**，
       11 張裡有 4 張（Yahoo購物中心 135px／光貿電子發票 120px／momo購物網 ~104px／
       雲端小幫手 100px）會折成兩行，8 張並排看起來像兩種版型。
     縱排 ⇒ 名稱吃滿 191px，11 張**全部單行**。
   ⇒ 取縱排。封面高度 ＝ 16 ＋ 88 ＋ 12 ＋ 25 ＋ 16 ＋ 1(border) ＝ 158px。
   ★誠實記錄代價：卡片變高，375 單欄時整頁會多捲一段。
     這是「回到業主喜歡的滿版底色版」本來就要付的（原本那版的色塊是 200px，
     整張卡 356px）——本輪的封面 158px 仍然比原版矮，而且它承載的是
     logo ＋ 平台名，不是只有四個字，資訊密度與視覺重量這次是對得上的。
   ------------------------------------------------------------------------- */
.sc-picker-cover {
  display: flex;
  flex-direction: column;
  align-items: flex-start;   /* 方格不要被拉成整行寬 */
  gap: .75rem;               /* 12px：方格與平台名之間 */
}

/* 品牌封面上的平台名 ＝ 恆亮近白（業主：「字要白色啊」）。
   ★特異度 0,2,0，且排在 §S-76a 的 `.sc-picker-cover .sc-picker-cover-name` 之後，
     同分後到者勝 ⇒ 品牌封面吃這條、中性封面仍吃 §S-76a 的 inherit。 */
.sc-picker-cover--brand .sc-picker-cover-name { color: hsl(var(--dark-panel-foreground)); }

/* 88px 方格。三種內容（logo / lucide / 文字）共用同一組幾何。 */
.sc-picker-cover-mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 5.5rem;                    /* 88px */
  height: 5.5rem;
  border-radius: var(--radius-xl);  /* 14px，與 Card 同階 */
  overflow: hidden;                 /* logo 的直角不要頂出圓角 */
  font-size: 2rem;                  /* 32px：文字版（MO店+／新竹物流）；配 700 ＝ WCAG large text */
  font-weight: 700;
  line-height: 1;
  user-select: none;
}
/* 白底只給「裡面放 logo / lucide」的九張 —— 文字版上了白底就是白字疊白底。 */
.sc-picker-cover--brand .sc-picker-cover-mark--filled { background-color: hsl(var(--dark-panel-foreground)); }
/* 中性封面（自訂商店／雲端小幫手）的方格要跟著封面一起翻轉：
   封面底是會翻轉的 --dark，方格就用它的對偶 --dark-foreground，
   圖示色再用回 --dark ⇒ 亮色＝近黑封面上的近白方格配近黑圖示，
   暗色整組翻過來，兩態都是 17:1 量級。 */
.sc-brand-mark--neutral .sc-picker-cover-mark--filled {
  background-color: hsl(var(--dark-foreground));
  color: hsl(var(--dark));
}
/* logo 本體：64px，contain 不拉伸（來源尺寸 152²～1024² 不一）。 */
.sc-picker-cover-logo {
  width: 4rem;
  height: 4rem;
  object-fit: contain;
  display: block;
}
/* lucide 在本專案是 <i data-lucide> 被換成 <svg>，尺寸要明寫，否則吃到 1em。 */
.sc-picker-cover-mark svg { width: 2.5rem; height: 2.5rem; stroke-width: 1.75; }

/* --- S-77. 平台選擇卡改成 App Store 應用程式列（2026-08-07 第五輪）------------

   業主原話（連續四句，方向講死了）：
     「你這什麼爛設計」「莫名其妙的一個大字」
     「請你好好重新設計這頁好嗎」
     「把商店安裝軟體的那種質感設計做出來 原則又是用 shadcn」

   ★第四輪做的是「品牌色滿版大色帶 ＋ 中間一個巨大首字」，實際渲染是 220px 高的
     色塊配 60px 單字 —— 讀起來像佔位符不像品牌，而且 logo 明明已經抓好卻沒用上。
     §S-75／§S-76 的規則原文全部保留（禁刪規則），只是呼叫端已經改名不再吃它們。

   ★這一輪的核心判斷：**品牌色不該是大色帶，該是 App 圖示本身**。
     App Store 的列就是「圓角方磚圖示 ＋ 名稱 ＋ 分類 ＋ 取得鈕」，
     品牌色收進那顆方磚裡 —— 業主要的「原本卡片顏色也放上」與「安裝軟體的質感」
     這樣同時成立，不衝突。

   ★shadcn 依據：官方 card.tsx 沒有規定 CardHeader 裡不能有 media。
     這一列的組成是 CardHeader（圖示 ＋ CardTitle ＋ CardDescription）＋ CardAction，
     全部是官方既有 slot，沒有發明新語彙。方磚圓角吃 --radius-xl（＝Card 同階）。 */

.sc-picker-appbar {
    display: flex;
    align-items: center;
    gap: .75rem;
    padding: 1rem 1rem 0;
}
@media (min-width: 768px) {
    /* 與 .card-body 的 p-md-4 對齊，讓圖示左緣與下方說明、CTA 同一條左緣 */
    .sc-picker-appbar { padding: 1.5rem 1.5rem 0; }
}

/* ══ App 圖示方磚 ═══════════════════════════════════════════════════════
   64px 是與 1280 斷點一起算的：該寬度下卡片內容只有 191px，
   64 ＋ 12(gap) 留 115px 給名稱，最長的「Yahoo購物中心」7 字 × 16px ＝ 112px，
   餘裕 3px —— 剛好夠，再大就會折行。 */
.sc-app-tile {
    flex: 0 0 auto;
    width: 4rem;
    height: 4rem;
    border-radius: var(--radius-xl);
    display: grid;
    place-items: center;
    overflow: hidden;
    /* ★深色品牌色（酷澎 #1C1A4E 1.12／SHOPLINE #000000 1.17）在暗色卡片上
       填色對比不足，形狀要靠這圈描邊定義。用 --foreground 的低 alpha
       才會亮暗自動翻轉；寫死顏色會在其中一態消失。 */
    box-shadow: inset 0 0 0 1px hsl(var(--foreground) / .12);
    color: hsl(var(--dark-panel-foreground));   /* 方磚內的前景恆亮（首字／lucide 都吃它） */
}
/* 自帶不透明底的三張（AMEGO／SHOPLINE／YAHOO）：logo 就是完整 App 圖示，鋪滿方磚 */
.sc-app-tile--bleed { background: hsl(var(--card)); }
.sc-app-tile--bleed .sc-app-tile-logo { width: 100%; height: 100%; object-fit: cover; }

/* 透明 logo（蝦皮／momo／酷澎／大榮）：品牌色底 ＋ 置中 logo。
   ★object-fit: contain —— 這幾張原始比例各不相同（152²/512²/550²），拉伸會讓商標變形。 */
.sc-app-tile-logo { width: 2.5rem; height: 2.5rem; object-fit: contain; display: block; }

/* lucide（自訂商店 store／雲端小幫手 cloud）：它們不是平台品牌，用圖示是正確語意 */
.sc-app-tile svg { width: 1.75rem; height: 1.75rem; stroke-width: 1.75; }

/* MO店+／新竹物流：抓不到方形 logo，維持首字。
   ★24px 不是 60px —— 第四輪那個「莫名其妙的一個大字」就是沒有這條在管尺寸。 */
.sc-app-tile { font-size: 1.5rem; font-weight: 700; line-height: 1; }

/* ══ 名稱與分類 ═══════════════════════════════════════════════════════ */
.sc-app-meta {
    /* ★不用 .min-w-0 utility：本專案只在 @media (max-width: 767.98px) 內定義它，
       桌機寬度它根本不存在，用了會靜默無效（第三輪踩過）。 */
    min-width: 0;
}
.sc-app-name {
    margin: 0;
    font-size: 1rem;          /* 16px —— 不超過整頁 h1 的 16.8px，字級階梯不倒掛 */
    font-weight: 600;
    line-height: 1.35;
    color: hsl(var(--card-foreground));
}
.sc-app-cat {
    display: block;
    margin-top: .125rem;
    font-size: .8125rem;      /* 13px，App Store 副標的量級 */
    color: hsl(var(--muted-foreground));
}

/* --- S-77b. 同色自我遮蔽的補救：白色方磚 ＋ 原色 logo（2026-08-07 第五輪實測後追加）---

   實測 1440 亮色：蝦皮的橘色 logo 疊在品牌色 #EE4D2D 上，整格看起來就是一塊
   純橘方塊，看不到任何形狀（大榮 #F26522 同理）。根因是「品牌 logo 的顏色
   本來就是品牌色」，不是尺寸或對比調得不好。

   ★解法是換底色不是改 logo：把方磚底改成卡片底（白／暗色近黑），
     logo 維持原色。這也正是這兩家官網 favicon 的呈現方式。
   ★刻意不用 CSS filter 把 logo 轉成白色單色版 —— 那會改動第三方商標的顏色，
     超出標示性使用的範圍。 */
.sc-app-tile--plate { background: hsl(var(--card)); }

/* --- S-77c. App Store 的「取得」膠囊鈕與副標狀態（2026-08-07 第五輪）-----------

   業主：「你整頁重新設計一下 用 app store 這種感覺去設計」
        「那你可以再優化一下底下新增跟使用N這設計嗎」

   ★App Store 最好認的兩個元素就是這兩顆：
     ① 「取得」是**右側的膠囊鈕**，不是卡片底部的滿版鈕
     ② 狀態（已安裝／版本）貼在**名稱下方的副標行**，不是單獨浮一顆 badge

   改前的問題（實測 1440 亮色）：「使用中 3」孤零零浮在說明上方，而且 8 張卡只有
   1 張有 → 卡片高度分成 288 / 264 兩種；底部 CTA 是低對比的滿版白鈕，沒有存在感。 */

.sc-picker-appbar { align-items: flex-start; }
.sc-app-meta { flex: 1 1 auto; }

/* 副標行：分類 · 狀態 —— 一行讀完「這是什麼、現在如何」 */
.sc-app-cat { display: flex; align-items: center; flex-wrap: wrap; gap: .25rem; }
.sc-app-sep { opacity: .45; }
.sc-app-status { display: inline-flex; align-items: center; gap: .25rem; }
.sc-app-status svg { width: .875rem; height: .875rem; }

/* 「取得」膠囊。用 --secondary 底而不是白底＋border：
   App Store 那顆是淡灰實心膠囊，白底 outline 在白卡片上幾乎沒有存在感（改前就是這樣）。 */
.sc-app-get {
    flex: 0 0 auto;
    border-radius: 9999px;
    padding: .3125rem 1rem;
    font-size: .8125rem;
    font-weight: 600;
    background: hsl(var(--secondary));
    color: hsl(var(--secondary-foreground));
    border: 1px solid transparent;
    white-space: nowrap;
}
.sc-app-get:hover,
.sc-picker-card:hover .sc-app-get {
    background: hsl(var(--primary));
    color: hsl(var(--primary-foreground));
}

/* --- S-77d. 收掉搬空的底列，並讓名稱不被膠囊鈕擠折（2026-08-07 第五輪收尾）---

   CTA 搬到上方之後，原本的 .card-footer 只剩一條 border-top 懸在說明下方
   （實測畫面上是一條沒有內容的橫線）。它的 markup 依禁刪規則保留，
   用 :empty 讓它不佔位 —— 這是「規則層收掉」而不是「刪掉內容」。 */
.sc-picker-card [data-slot="card-footer"]:empty { display: none; border-top: 0; margin: 0; padding: 0; }

/* 名稱：膠囊鈕佔走右側之後，長名（Yahoo購物中心／momo購物網）會折成兩行。
   縮一階字級並允許 meta 收縮，讓它在 1440 四欄下維持單行。 */
.sc-app-name { font-size: .9375rem; }
.sc-app-meta { min-width: 0; flex: 1 1 auto; }

/* --- S-77e. 「新增」與「使用中 N」要更清楚（2026-08-07，業主：「新增跟使用中 要清楚一點」）---

   改前的問題（實測 1440 亮色）：
     · 「新增」吃 --secondary（#F5F5F5 淺灰底 ＋ 近黑字）—— 在白卡片上只差 10/255，
       讀起來像停用態而不是主要動作。
     · 「使用中 3」是副標行裡的小灰字（13px --muted-foreground），
       跟旁邊的「API串接」同一個視覺重量，看不出它是狀態。

   ★兩個都不是憑感覺調，各自對到專案已有的 SSOT：
     · 新增 ＝ 主要動作 ⇒ 品牌黃 --primary。本專案 action-bar 的既有註解明訂
       「黃底 btn-primary 只給新增／儲存系列」，這顆正是新增。
     · 使用中 ＝ 狀態 ⇒ --success-bg / --success-fg 這一對（亮暗成對、早就存在），
       也就是業主看過賣賣Work OS 之後欣賞的那顆「● 開啟」淡綠藥丸的做法。 */

.sc-app-get {
    background: hsl(var(--primary));
    color: hsl(var(--primary-foreground));
    font-weight: 600;
    padding: .375rem 1.125rem;
    font-size: .875rem;
}
.sc-app-get:hover,
.sc-picker-card:hover .sc-app-get {
    /* hover 稍微壓深一階，維持黃色語意（不要換成別的色相，那會讓人以為狀態變了） */
    background: hsl(var(--primary));
    color: hsl(var(--primary-foreground));
    filter: brightness(.94);
}

/* 「使用中 N」從副標裡的小灰字改成獨立一列的淡綠藥丸 */
.sc-app-status {
    display: inline-flex;
    align-items: center;
    gap: .25rem;
    padding: .125rem .5rem;
    border-radius: 9999px;
    background: hsl(var(--success-bg));
    color: hsl(var(--success-fg));
    font-size: .75rem;
    font-weight: 600;
    line-height: 1.5;
}
/* 藥丸自己已經是綠的，前面那顆圖示不必再上色，改吃 currentColor 才不會兩種綠打架 */
.sc-app-status svg { color: currentColor; width: .8125rem; height: .8125rem; }
/* 分隔點在藥丸旁邊會變成多餘的視覺噪音 */
.sc-app-sep { display: none; }

/* --- S-77f. 膠囊鈕與狀態搬到卡片底列（2026-08-07 收尾）------------------------

   ★實測業主視窗（約 1280 以下、四欄）：膠囊鈕與名稱搶同一列時，
     CJK 名稱會被擠成逐字折行 ——「自訂商 店」「蝦皮購 物」「SHOP LINE」
     「雲端小 幫手」，「使用中 3」也被擠成兩行還多出一個孤立的分隔點。
     我在 1440 量不到這個問題，是業主的截圖暴露的。

   ★治法不是把字縮小（那只是把折行往後推一個斷點），是把兩顆控制項移出那一列。
     名稱因此拿到「卡片寬 − 圖示 − gap」的完整寬度；狀態與動作各據底列一端，
     這也正是 App Store 列尾的關係（左狀態、右動作）。 */

.sc-app-actions { display: flex; align-items: center; gap: .5rem; }
/* 底列有內容了，S-77d 那條 :empty 自然不再命中（規則保留，供沒有 CTA 的呼叫端用） */

/* 名稱可以放回 16px：擠壓源頭消失了 */
.sc-app-name { font-size: 1rem; }
.sc-app-cat  { display: block; }

/* --- S-77g. 卡片內的垂直節奏收斂（2026-08-07，業主：「你在整體優化一下距離 設計」）---

   ★問題不是「某一處留白太大」，是**四層留白疊在一起**：
     .sc-picker-appbar 的下緣
     ＋ .card-body 的 p-md-4 上緣（24px）
     ＋ 已經搬空的 card-header（它還在 DOM 裡佔位）
     ＋ card-content 的 mt-3（16px）
   實測圖示列底到說明首行之間空了約 90px，而說明到分隔線又是一段。
   結果是短說明的卡（自訂商店）中間一大片死白。

   ★收斂原則：整張卡只有一套節奏 —— 24 / 16 / 16 / 12
     （外框內距 24、圖示列→說明 16、說明→分隔線 16、分隔線→動作列 12）。
     每一個數字都是 4 的倍數且對得到 Tailwind 的間距階（p-6 / gap-4 / gap-3）。 */

.sc-picker-card .card-body { padding-top: 1rem; }
@media (min-width: 768px) {
    /* appbar 已經給了上緣 24px，card-body 不要再給一次 */
    .sc-picker-card .card-body { padding-top: 1rem; }
}
/* 搬空的 card-header 不再佔位（markup 依禁刪規則保留） */
.sc-picker-card [data-slot="card-header"]:empty,
.sc-picker-card [data-slot="card-header"]:not(:has(*:not(:empty))) { display: none; }
/* 說明區：上緣交給 card-body 的 padding-top，自己不再加 margin */
.sc-picker-card [data-slot="card-content"] { margin-top: 0 !important; }
/* 動作列：分隔線上下各 12px，比原本的 16 緊一階 —— 它是列尾不是新段落 */
.sc-picker-card [data-slot="card-footer"] { margin-top: 1rem !important; padding-top: .75rem !important; }
/* 說明段本身的行高收一階，四行 clamp 下少掉一段虛胖 */
.sc-picker-desc { line-height: 1.55; }

/* --- S-77h. 「使用中 N」與「新增」等高（2026-08-07，業主：「使用中跟新增是不是要一樣大」）---

   兩顆在同一列（底列左右端），但改前 status 是 12px 字 / 上下 2px 內距，
   get 是 14px 字 / 上下 6px 內距 → 高度不齊，看起來像兩個不同層級的東西。
   實際上它們是**同一層級的一對**：一個講狀態、一個講動作。

   ★統一成 32px 高、13px 字、同樣的膠囊圓角。差異只保留在「填色 vs 淡色」——
     那才是它們真正的差別（動作是實心主色、狀態是低調的語意底）。 */

.sc-app-status,
.sc-app-get {
    height: 2rem;                 /* 32px，與 .btn-sm 同階 */
    padding: 0 .875rem;
    font-size: .8125rem;          /* 13px */
    font-weight: 600;
    line-height: 1;
    border-radius: 9999px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: .3125rem;
}
.sc-app-status svg { width: .875rem; height: .875rem; }

/* --- S-78. 出貨列印彈窗 #shipmentGroupModal 版式收斂：四欄 → 七欄（2026-08-07）------
   ★這一行是給 tests/Feature/Theme/ThemeOverrideAnnotationsTest 認的「章節錨點」，
     寫法必須是 `/* --- S-78. …`（S- 前面不可以多 §，見 §S-46 的說明）。

   ★★本節**刻意不含任何 CSS 宣告**，理由與 §S-70 / §S-70a 相同：
     這一輪的缺陷全部出在呼叫端（blade 把七件事塞進同一格、表頭留空），
     在主題層加規則只會多一套互相蓋的寫法。改動 100% 在
       resources/views/Order/ReadyToShip/index.blade.php（見該檔 §S-78 註解）
     本檔這一節只是「數值與依據」的備查，讓下一個人不必再量一次。
     ⇒ 本輪 shadcn-theme.css 的**既有規則零變動**（剝註解後與改前逐位元組相同）。

   ═══ 一、業主的話與現象 ═══
   業主指著 /order-management/status/ready-to-ship 勾一列後點「列印」跳出的彈窗：
     「列印彈窗這邊好像設計的不好 好鬆散 你可以用 shadcn 的設計邏輯設計嗎」
   另附 7.div.tw/2025-mymy 原型兩張參考稿（七欄表：配送方式／訂單數／選項／
   取寄件單號／出貨列印／取號狀態／列印狀態）。

   ── 改前實測（https://test.2025-seller.mtest/ ，1440 亮色，modal 實開）──
     · thead 四顆 th 有三顆是空字串 ⇒ 表頭是一條 40.69px 的空白帶，
       只有「一般列印」四個字浮在右上。有名字的欄頭：1/4。
     · 列高 52.19 / 77.19 / 77.19 / 51.69 ── 四列三種高度。
       用合成資料把「未取號 / → 物流倉 / 0 筆」三條分支一起逼出來後更誇張：
       52.19 / 77.19 / 134.19 / 52.19 / 133.69 ── 五列四種高度。
       主因：物流商名稱與「確認出貨」「總表下載」「→ 物流倉」全靠 `<br>` 疊在同一格。
     · 0 筆的列在「一般列印」欄是一個 `-`，有筆數的列是「貨/條」兩顆 ⇒ 同欄兩種版式。
     · 「本次列印統計資訊」是 `h6 fw-bold`（16px 粗體），與 modal-header 的標題
       「列印內容選擇」同字級同粗細 ⇒ 兩個標題打架。

   ── 改後實測（同一組條件）──
     · 有名字的欄頭 7/7；欄位左緣 337 / 524 / 624 / 690 / 788 / 924 / 1023
       （改前只有 337 / 643 / 720 / 988 四條，而且三條沒有標題）。
     · 列高（真資料）52.19 / 52.19 / 52.19 / 51.69；
       列高（合成資料五列，含 → 物流倉 那列）52.19 ×4 / 51.69 ── 只剩一種高度，
       51.69 是最後一列沒有 border-bottom，孿生檔 SSOT 也是這個值。
     · modal 總高 491 → 440px（少掉的就是被 `<br>` 撐出來的那幾段）。
     · 表格寬 766px ≦ body 可用寬 798px ⇒ 沒有橫向溢出（1440 / 1280 皆同）。

   ═══ 二、版式的來源是本專案自己的孿生檔，不是新發明 ═══
   PackagingMachine/Order/_shipment_group_script.blade.php 的同一顆 modal 早就是
     配送方式｜下載｜訂單數｜已取號｜取號中｜未取號｜失敗｜一般列印｜取寄件單號｜列印
   十欄版，實測四列全部 52.19 / 52.19 / 52.19 / 51.69。業主的參考稿要的就是同一個結構。
   ⇒ Order/ReadyToShip 收斂到它，欄名逐字照抄；
     「已取號 / 取號中 / 失敗」三欄**不做**（本頁現在沒顯示這些數字，
     這一輪的授權是改設計不是加功能）。

   ── 上游依據（shadcn-ui/ui apps/v4/registry/new-york-v4/ui，2026-08-07 現抓；
      ★apps/www/registry/new-york 已從 main 移除，官方只剩 v4 一版）──
     dialog.tsx  DialogTitle       : "text-lg leading-none font-semibold"
                 DialogDescription : "text-sm text-muted-foreground"
                 DialogContent     : "… gap-4 rounded-lg border bg-background p-6 … sm:max-w-lg"
                 DialogFooter      : "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end"
     table.tsx   TableHead         : "h-10 px-2 text-left align-middle font-medium … text-foreground"
                 TableRow          : "border-b transition-colors …"   ← 只有列底線
                 Table             : 外層是 `relative w-full overflow-x-auto`
   ★「本次列印統計資訊」據此從 `h6 fw-bold` 降成 `text-muted small`＝DialogDescription。
   ★寬度維持 modal-lg（800px），**沒有**照 `sm:max-w-lg`（512px）收：
     那是**表單型** dialog 的預設值；table.tsx 自己就包在 overflow-x-auto 裡，
     資料表格不吃那個上限。孿生檔十欄用 1140px，本頁七欄按同密度換算 1140×7/10≈798px
     ⇒ 800px 剛好。業主說的「右側大量留白」不是 dialog 太寬，是四欄裡
     第一欄佔 40%（306px）只放四個字、第三欄 268px 只放兩顆小按鈕。

   ═══ 三、抓到但**沒有**修的（要動請開新的一輪，不要順手）═══
   ★★`.btn-close` 在暗色下實質看不見 —— 全站性，非本輪造成。
     像素取樣（#shipmentGroupModal 標題列的關閉叉叉，32×32 方格，改前改後同值）：
       亮色 top: 255,255,255(86.7%) / 128,128,128 ⇒ 對比 3.95:1、uniq 17（看得見）
       暗色 top:  23, 23, 23(86.7%) /  13, 13, 13 ⇒ **對比 1.09:1、uniq 5**（看不見）
     成因：Bootstrap 的 .btn-close 是背景圖 SVG，`fill='%23000'` 寫死黑色，
       computed 為 `filter: none`、`opacity: 0.5`，本檔全檔沒有任何暗色規則接它
       （grep `btn-close` 只有 :active、min-height 例外清單那幾條）。
       50% 黑疊在 rgb(23,23,23) 上就是 rgb(13,13,13) —— 數字完全對得上。
     ★★★這與 §S-70「對照組 #shipmentGroupModal 暗色 uniq=18／對比 5.21:1（看得見）」
       **互相矛盾**。同一顆按鈕、同一支頁面，現在量到的是 1.09:1。
       兩者必有一錯（S-70 之後 §S-72 動過整組中性 token，也可能是那之後才回歸）。
       ⇒ 這是本檔自己的註解在說謊，照它判斷會以為這顆按鈕沒事。先記在這裡，
         不要再把 §S-70 的 5.21:1 當成「已驗過」。
     為什麼本輪不修：唯一的治法是主題層一條暗色規則（呼叫端已經是素面 `btn-close`，
       沒有 class 可改），而 .btn-close 全站每一支 modal 都在用（§S-70 二 的盤點是 49 顆）。
       改了要重驗全部，超出「改一顆彈窗的版式」的範圍。
       建議治法（**未實作、未驗證**）：`html.dark .btn-close { filter: invert(1); }`，
       但上線前必須先掃「暗色模式下位於淺色底的 btn-close」有沒有反例（會變白叉白底）。

   ★`group.label` 在本頁沒有經過 escapeHtml（孿生檔有）。本頁沒有 escapeHtml 這支函式
     （見 Order/_partials/shipment_group_js_shared.blade.php 開頭的不可共用清單）。
     物流商名稱目前來自 service 端固定設定，不是使用者輸入，所以不是活躍缺陷；
     補 escape 會動到共用檔的函式清單，不在本輪範圍。

   ═══ 四、行為零改動是怎麼證明的（不是「看起來一樣」）═══
     · [data-confirm-group] / [data-print-option] / [data-print-for] / [data-print-trigger]
       / [data-download-summary] / [data-failed-toggle] / [data-print-to-label]
       七種勾子的元素清單（tag + 全部 dataset + disabled + 文字）序列化後，
       改前改後在 8 種畫面（兩頁 × 1440/1280 × 亮/暗）＋ 合成資料兩種主題下**逐字相同**。
       ★關鍵手法：0 筆那列的「貨/條」故意**不掛** data-print-option / data-print-for，
         沿用 ALL 列「條」本來就有的素 checkbox 寫法 ⇒ 清單不會多出元素。
     · 16 顆可點元素逐顆點過，用「預設 abort、只白名單放行」的攔截器記錄
       「打算送出什麼」（method + URL + body），改前改後逐字相同；
       原生 alert/confirm 的訊息序列也逐字相同。
     · 攔截器本身做過雙向誘餌：/shipment-groups/confirm 與 /download-to-label
       與 GET /download-summary 三支都實測 BLOCKED，而白名單的 /shipment-groups
       實測 NOT_BLOCKED status=200（正對照，證明不是「全擋」假裝有守門）。
   --- */

/* --- S-77i. 狀態藥丸的語氣要跟著 $statusClass 走（2026-08-07）------------------

   🔴 業主：「未啟用怎麼會是綠色」（`/store-setting/api-platform/create`）

   ★這是我自己造成的：§S-77e 把 .sc-app-status 的底色**寫死成 --success-bg**，
     於是不管 $statusClass 是什麼都畫成綠色。
     blade 的對應表本來是對的（btn-green → text-success、btn-default → text-muted），
     只是那個 class 掛在裡面那顆圖示上，被我在外層寫死的底色蓋過去了。
     api-platform 的三張卡全是 statusClass = 'btn-default' / statusText = '未啟用'
     ⇒ 應該是中性灰，卻全部顯示成綠色的「未啟用」，語意完全相反。

   ★治法是把語氣掛到藥丸本身（sc-app-status--success / --muted），
     不是在圖示上掛一個會被外層蓋掉的顏色 class。 */

.sc-app-status--muted {
    background: hsl(var(--secondary));
    color: hsl(var(--muted-foreground));
}
.sc-app-status--success {
    background: hsl(var(--success-bg));
    color: hsl(var(--success-fg));
}

/* --- S-77j. 底列：新增置左、狀態置右（2026-08-07）-----------------------------

   業主：「新增應該置左 使用中 未使用 應該置右」

   ★這是業主早就定過的全站規則，我上一版把它做反了：
     「重要按鈕（新增／儲存）置左；不重要的（說明／狀態／更新時間）才置右」。
     「新增」是這張卡唯一的動作 ⇒ 置左；「使用中 3 / 未啟用」是狀態資訊 ⇒ 置右。
   ★用 order-first ＋ ms-auto 而不是調換 markup 順序：
     DOM 順序維持「狀態在前、動作在後」對讀屏比較合理（先講現況再給動作），
     視覺順序交給 flex 處理。 */
.sc-app-actions .sc-app-get.order-first { order: -1; }

/* --- S-78a. 兩套單據命名上下並列時，第二行（同義詞）的字級與弱化（2026-08-07）------
   ★這一行是給 tests/Feature/Theme/ThemeOverrideAnnotationsTest 認的「章節錨點」，
     寫法必須是 `/* --- S-78a. …`（S- 前面不可以多 §）。

   ★★這是 §S-78 之後**追加**的一節，因為 §S-78 原本宣告「本節不含任何 CSS 宣告」，
     那句話對 §S-78 仍然成立（那一節到現在還是零宣告）。這一節是新的、有宣告的一節，
     不去改上面那一節的文字，免得那句話變成假的。

   ═══ 為什麼非要一條 CSS 不可（先用工具類做過，量出來不行）═══
   業主定案：「同義詞放上下」「大字放上」「小字放小」。
   第一版用 Bootstrap 的 `small` 工具類做第二行，實測（1440/1280 × 亮/暗，四組同值）：
     第一行「出貨單」font-size 13px
     第二行「揀貨單」font-size 12px、color 與第一行**完全相同**（亮 rgb(0,0,0)／暗 rgb(250,250,250)），
                     只靠 `opacity-75` 區分
   ⇒ 1px 的差距在 13px 的字上肉眼幾乎看不出來，「小字放小」這個要求沒有達成。
     成因：`small` 是 0.875em，而它的 em 基準是 `.btn-sm` 的字級，算完就是 12px。
     要再小就必須指定絕對值，工具類沒有 11px 這一階（Bootstrap 沒有 fs-7 以下）。

   ═══ 為什麼用 currentColor + opacity，不用 --muted-foreground ═══
   這兩顆是**可切換選中態**的按鈕（勾選時 JS 會 toggle `.active`，見
   Order/ReadyToShip/index.blade.php 的 [data-print-option] change 綁定），
   底色會在「outline 淺底」與「active 深底」之間切換，再乘上亮/暗兩個主題 ⇒ 四種底色。
   寫死 `hsl(var(--muted-foreground))` 只能對其中一兩種底色好看，深底那兩種會掉對比。
   `opacity` 作用在 currentColor 上，四種狀態都自動跟著按鈕自己的前景色走 ——
   這也是 shadcn 在有色控制項裡處理次要文字的作法（Button 的 foreground 一律 currentColor）。

   ★作用域限死 `#shipmentGroupModal`：這顆 class 是本輪新造的，全站只有這一顆 modal 用。
     不寫成全域，是為了不讓它變成下一個「不知道被誰吃到」的規則。
   ★`.sg-print-alt` 是**純樣式 class，沒有任何 JS 讀它**（本輪新增，全專案 grep 僅此二處）。
     真正的行為勾子仍然是 `data-print-option` / `data-print-for`，一字未動。
   --- */
#shipmentGroupModal .sg-print-alt {
    font-size: .6875rem;   /* 11px：與第一行 13px 拉開兩階，「小字放小」才看得出來 */
    line-height: 1.2;
    opacity: .65;          /* 作用在 currentColor 上，四種底色（亮/暗 × outline/active）自動跟著走 */
}

/* --- S-79. 按鈕「圖示↔文字」間距回到官方階梯（2026-08-07）------------------------
   ★這一行是給 tests/Feature/Theme/ThemeOverrideAnnotationsTest 認的「章節錨點」，
     寫法必須是 `/* --- S-79. …`（S- 前面不可以多 §，見 §S-46 的說明）。

   ═══ 一、現況（26 頁實測，252 顆可見 .btn；不是估的）═══
   有圖示又有文字的 80 顆，實測「圖示右緣 → 第一個字左緣」的距離只有四種值：
     0px     34 顆  ← 圖示直接黏住字
     3.09px  31 顆  ← 全部是 `btn-xs btn-rounded rounded-pill` 的狀態藥丸（見下面 三）
     4px     12 顆  ← 呼叫端自己在圖示上寫了 `me-1`
     -18px / 12.88px 共 3 顆 ← /pos/machine 的直式大鈕（圖示 d-block 疊在文字上方，
                                 這個量測維度對它沒有意義，本節不處理）

   ★★0px 的成因（實測問出來的，不是猜）：
     markup 是 `<i data-lucide="plus"></i> 新增商品`，圖示與文字中間有一個半形空白。
     這 34 顆的 computed display 是 flex / inline-flex，來源是本檔既有的
       L5770 `.app-content .btn-sm { display: inline-flex; … }`（94 顆命中）
       L6950 `.app-content td …btn-xs:not(.btn-rounded):not([class*="rounded-pill"])`（4 顆）
     flex 容器會把「純文字節點」包成匿名 flex item，**前導空白被吃掉** ⇒ 間距歸零。
     ★所以同一份 markup 在 inline-block 的按鈕上會渲染出 3.09px、在 flex 上是 0px
       —— 這正是「同一種寫法兩種結果」，靠空白字元當間距本來就不成立。
     ★逐條 CSSOM 比對確認：全站沒有任何「選擇器含 .btn」的規則宣告 display，
       flex 全部來自上面那兩條 .btn-sm / td .btn-xs 的收斂規則與 Bootstrap 的 .d-block。

   ═══ 二、數值來源：shadcn 官方 button.tsx 的字面，不是現場試出來的 ═══
   （shadcn-ui/ui apps/v4/registry/new-york-v4/ui/button.tsx，2026-08-07 抓下的原始碼）
     base            : "inline-flex … items-center justify-center **gap-2** …"   → 8px
     size.sm         : "h-8 **gap-1.5** rounded-md px-3 …"                        → 6px
     size.xs         : "h-6 **gap-1** rounded-md px-2 text-xs …"                  → 4px
     size.lg / icon  : 沒有自己的 gap → 吃 base 的 gap-2                          → 8px
   ⇒ 本專案的 .btn / .btn-sm / .btn-xs 一一對應到官方的 default / sm / xs 三階。
   ★前一輪代理建議的 `.btn{gap:.375rem}`（6px）是**在畫面上試出來的數字**，
     它其實是官方 size=sm 的值被套到所有尺寸上。這裡改用三階，每一階都對得到上游字面。

   ═══ 三、刻意不動的：31 顆 3.09px 的狀態藥丸 ═══
   它們是 `btn btn-xs btn-green/btn-default btn-rounded rounded-pill`
   （/hr/user 26 顆、/store-setting/store 3 顆…），是**狀態標籤**不是按鈕。
   本檔 L6950 與 L8561 兩條既有規則都明文用
     `:not(.btn-rounded):not([class*="rounded-pill"])` 把它們排除在按鈕版式之外。
   要讓 gap 對它們生效就得把它們也改成 inline-flex，那是**推翻既有的刻意排除**。
   ⇒ 本節只加 gap（對 inline-block 元素天生無作用），不動它們的 display，
     3.09px 維持原樣。★這是主動否決掉的「順手一起收」，記在這裡免得下次又有人想改。

   ═══ 四、為什麼要把 me-1 收掉（SSOT）═══
   收完之後如果不動 `me-1`，那 12 顆會變成 gap 6px ＋ margin 4px = 10px，
   間距的種類會從 {0, 3.09, 4} 變成 {6, 10, 3.09, 4} —— **比改之前更散**。
   間距只能有一個來源。全站靜態掃描（resources/views 全樹）btn 內帶 margin 的圖示共 49 個：
     btn-sm me-1 ×27、btn-sm ms-1 ×2、btn-xs me-1 ×1  ← 這 30 個由本節接管
     default me-1 ×7、default me-2 ×4、default ms-1 ×2、default ms-2 ×1、
     btn-lg ms-2 ×2、**ms-auto ×3（default 2 / btn-lg 1）**            ← 本節不碰
   ★不碰 default / lg 的理由不是漏掉，是**它們不見得是 flex 容器**：
     例如 /seller/plan 的「前往訂閱」是 `btn btn-dark w-100`，computed display=inline-block，
     gap 對它無效；此時把 ms-1 收掉會變成真的 0px ⇒ 把問題從別處搬過來。
   ★★`ms-auto` 是**版面用途**（把圖示推到最右），不是間距，收掉會壞版；
     選擇器另外用 :not() 擋掉，就算將來有人在 btn-sm 上寫 ms-auto 也不會被誤傷。
   ★需要 !important 的理由與 §S-64 的 `font-weight:600!important` 同一套：
     Bootstrap 的間距 utility（.me-1 等）本身帶 !important，不用 !important 等於沒寫。
     ★禁止刪除守則：blade 上的 me-1 / ms-1 一個字都沒動，只是不再由它決定間距。 */

/* 三階 gap：對得到 button.tsx 的 gap-2 / gap-1.5 / gap-1 */
.btn { gap: .5rem; }
.btn-sm { gap: .375rem; }
.btn-xs { gap: .25rem; }

/* 圖示的水平 margin 交回給 gap（只收 flex 化過的 sm / xs，且放過 *-auto） */
.app-content .btn-sm > i:not([class*="ms-auto"]):not([class*="me-auto"]),
.app-content .btn-sm > svg:not([class*="ms-auto"]):not([class*="me-auto"]),
.app-content .btn-xs > i:not([class*="ms-auto"]):not([class*="me-auto"]),
.app-content .btn-xs > svg:not([class*="ms-auto"]):not([class*="me-auto"]) {
    margin-inline: 0 !important;
}

/* --- S-80. 表單層 SSOT 規格：`.fb-form-table` 是全站唯一的「表單型表格」寫法（2026-08-07）---
   業主：「我覺得你很多頁面都沒有套用到 shadcn」→「設計 shadcn 治本 ssot」。

   ★★★ 先講結論：**問題不是「沒有 shadcn」，是「同一個概念有三套實作」。**
   骨架層（x-page-header 112 支／x-pagination-bar 59 支／btn-white 97 支）覆蓋得很好，
   破口在**單頁內部的表單層**。全站把「label 左／控制項右」的表單畫成 <table> 的頁面，
   實測分成三群，長相各不相同：

     群 ①「掛了 .fb-form-table」＝ 本專案的正解，8 支
         good/warehouse|storageTags|storageLocation/edit、
         Pick/desktop|packageAction|packageMachine|userPackage|package/edit
         → 吃 §S-24 ＋ §S-58② ＋ §S-63，四邊內距 1rem、無列線、label 對齊卡片標題。

     群 ②「自己在 blade 裡寫 <style> 重造一次」＝ 11 支，每支 2 條規則、共 22 份複製
         hr/user/{create,informationEdit,darkaEdit,salaryEdit,settingEdit,settingTokenIndex}、
         hr/{role,department}/edit、employee/user/{informationEdit,darkaEdit,salaryEdit}
         ★「2 條」是剝掉註解後**實際數出來**的：grep 每支數到 3 次，但第 3 次在**註解裡**
           （那段註解自己也在解釋這個選擇器）。11 支剝註解後的規則區塊 md5 完全相同
           （885ba0e930e6）⇒ 是 11 份逐字複製，不是 11 種寫法。
           ★★先前這裡我寫的是「每支 3 條、共 33 份」——那是直接把 grep 次數當規則數，
             沒有排除註解。當場數過才改成 22。數字沒數過就不要寫。
         選擇器是
             .app-content .card .table:not(:has(> thead)) > tbody > tr > td
         也就是**用「這張表沒有 thead」來猜它是不是表單**。
         ⇒ 這是最典型的 SSOT 破口：規則存在、但存在了 33 次，改一次要改 11 支檔。

     群 ③「什麼都沒有」＝ StoreSetting 的設定頁，10 支
         Store/baseSetting/{shopee,shopline,custom,mallbic,momo,moplus,coupang,yahoo}、
         Store/{_modifyBaseSetting,modifyBaseSetting}
         → 表單直接吃**資料表**的樣式（列與列之間 1px 分隔線、hover 反白、fw-bold 700）。
         實測 /store-setting/store/modify/base-setting/2（1440、亮色）：
             td border-bottom = 1px、font-weight = 700、列高 51.69px、
             label 左緣 − 卡片左緣 = 1（貼邊），而同卡片的標題是 17 ⇒ **標題與 label 沒對齊**。

   ★§S-24 的原文其實已經寫了「seller 的編輯頁有 11 支把表單畫成 <table>」——
     也就是說這件事**當初就盤點過、class 也設計好了，只是遷移做到一半就停了**。
     本節不發明新東西，只把 §S-24 訂好的那個 class 補完，並補上它當初沒遇到的三種情形。

   ────────────────────────────────────────────────────────────────────────────
   一、規格表：本專案現況 → 官方（shadcn v4，apps/v4/registry/new-york-v4）→ 建議寫法
   ────────────────────────────────────────────────────────────────────────────
   ★官方原始碼取自 shadcn-ui/ui 的 v4 目錄。**`apps/www/registry/new-york` 已從 main 移除**，
     官方現在只剩 v4 一版；本表凡是查不到官方對應的，都直接寫「官方沒有」，不編。

   ┌─ 1. 表單列的骨架 ───────────────────────────────────────────────────────────
   │ 現況   <table class="table table-panel table-hover align-middle fw-bold">
   │        <tr><td>欄位名</td><td><input class="form-control form-control-sm"></td></tr>
   │ 官方   Form 是**垂直堆疊**：<div class="grid gap-2"><Label/><Input/></div>
   │        （FormItem = `grid gap-2`，官方沒有任何「水平表單」的變體）
   │ 建議   ★**分成兩類，不一律改垂直**：
   │        (a) 已經是 <table> 兩／三欄的設定頁 → **保留水平**，只加 `fb-form-table`。
   │            理由有三，且是專案自己已經拍過板的（§S-24 原文）：
   │              · <td> 數量鎖死 —— 改垂直等於把兩欄併成一欄，會刪 td，違反禁止刪除守則
   │              · 這些是「欄位多、要對齊」的設定頁，水平表單掃描成本低
   │              · 2026-fatboy 的 good/storageLocation/edit 對同一張表已做過同樣取捨，
   │                兩個系統並排才看不出是兩套
   │        (b) 用 col-md-* grid 排版的新增/編輯頁（例 inventory/composite-product/create）
   │            → **維持垂直**，`<label class="form-label">` ＋ 控制項，間距交給 grid gutter。
   │            這一類已經與官方 FormItem 同構，不需要動。
   │        ⇒ 判準一句話：**已經在 <table> 裡的保留水平掛 class；在 div grid 裡的維持垂直。**
   │           兩者共用同一組 token（字級/字重/顏色/焦點環），差別只在排列方向。
   ├─ 2. label ─────────────────────────────────────────────────────────────────
   │ 現況   兩種：`<label class="form-label">`（吃 §S-63 = 14px/500）
   │        與**裸 <td> 文字**（吃 <table> 上的 `.fw-bold` ⇒ 700，且沒有人管它）
   │ 官方   Label = `text-sm leading-none font-medium` ⇒ 14px / 500
   │ 建議   §S-63 已經是 SSOT，**沿用不另立**。本節只補「裸 td 文字」那條路：
   │        讓 `.fb-form-table` 的第一欄也是 500，與 `.form-label` 同值。
   │        ★不刪 markup 上的 `fw-bold` —— 照 §S-64 已建立的先例（「把字重收在主題層
   │          而不是逐頁刪 markup 的 fw-bold」），也符合禁止刪除守則。
   ├─ 3. input / select / textarea ────────────────────────────────────────────
   │ 現況   高度 32px（form-control-sm）／radius var(--radius-md)／border hsl(--input)
   │ 官方   Input `h-9`(36px) `rounded-md` `border` `px-3 py-1` `text-base md:text-sm` `shadow-xs`
   │        Textarea `min-h-16` `rounded-md` `px-3 py-2`
   │ 建議   **不動高度**。官方 h-9=36px 是給單欄表單的；本專案設定頁一卡十列，
   │        全站拉到 36px 會讓每張卡長 4px×N，屬於版面級變更，要業主先看過。
   │        圓角/邊框/內距已經對上官方，維持。
   │        ★焦點態 §S-73 已統一成官方兩層（border-ring ＋ ring-[3px] ring-ring/50）——
   │          **本節一個字都沒動它**。
   ├─ 4. 必填標記 / 說明文字 / 錯誤訊息 ────────────────────────────────────────
   │ 現況   說明文字＝`<td class="text-muted text-wrap">`；必填標記與錯誤訊息**全站沒有**
   │ 官方   FormDescription = `text-muted-foreground text-sm`
   │        FormMessage     = `text-destructive text-sm`
   │        FormLabel 在錯誤時 `data-error:text-destructive`；必填官方沒有內建星號
   │ 建議   · 說明文字：`.text-muted` 已經對上 FormDescription（14px + muted-foreground），
   │          維持，但字重要跟著回 400（現在被 fw-bold 拉成 700，讀起來跟 label 一樣重）
   │        · 錯誤訊息：**本輪不做**。全站沒有任何錯誤訊息元素，這些表單是 ajax + alert()
   │          回報錯誤；要導入 FormMessage 是行為變更（要改 JS 與 controller 回傳），
   │          不在「樣式收斂」的範圍。列進待辦，不假裝已經有。
   │        · 必填標記：官方沒有對應，**不編一個出來**。
   ├─ 5. `fs-XXpx` 這批 Color Admin 像素工具類 ─────────────────────────────────
   │ 現況   19 支檔、共 81 次，值分布：
   │          fs-9px×2  fs-10px×8  fs-11px×21 fs-12px×2 fs-13px×16
   │          fs-14px×12 fs-15px×1 fs-18px×18 fs-30px×1
   │ 官方   Tailwind 字級階：text-xs 12 / text-sm 14 / text-base 16 / text-lg 18 /
   │        text-xl 20 / text-2xl 24 / text-3xl 30
   │ 建議   對照表（★9/10/11/13/15 這五階**官方沒有**，一律往最近的官方階收）：
   │          fs-9px  → text-xs   (12) ｜ fs-10px → text-xs   (12)
   │          fs-11px → text-xs   (12) ｜ fs-12px → text-xs   (12)
   │          fs-13px → text-sm   (14) ｜ fs-14px → text-sm   (14)
   │          fs-15px → text-sm   (14) ｜ fs-18px → text-lg   (18)  ← 唯一原生對齊的
   │          fs-30px → text-3xl  (30) ← 也原生對齊
   │        ★**本輪不執行這個轉換**：fs-11px 有 21 次、大量落在 pos/* 與 Seller/plan/*，
   │          那是兩塊獨立的視覺情境（POS 觸控機／訂閱方案卡），11→12px 會動到版面。
   │          規格先訂在這裡，執行要單獨一輪並各自量過。
   └─ 6. 卡片內的表單分區 ──────────────────────────────────────────────────────
     現況   `.card > .card-header.p-3.fw-bold` ＋ `.card-body.p-0` ＋ 表格自己貼邊
     官方   Card       = `flex flex-col gap-6 rounded-xl border py-6 shadow-sm`
            CardHeader = `grid auto-rows-min gap-2 px-6 [.border-b]:pb-6`
            CardTitle  = `leading-none font-semibold`
            CardContent= `px-6`
            ⇒ 官方的內距是**卡片給的**（px-6 / py-6），內容自己不給。
     建議   §S-58② 已經照這個結論做了：內距由 `card-body` 給（1rem），
            表格 `td:first-child{padding-left:0}` 自己不給 —— 方向與官方一致。
            **前提是那張表要掛 `.fb-form-table`**，§S-58② 才咬得到它。
            群 ③ 那 10 支就是因為沒掛 class 才落在「Δ1 貼邊」。
            ★卡片標題字重 §S-64 已收成 600（＝官方 font-semibold），不動。

   ────────────────────────────────────────────────────────────────────────────
   二、為什麼是「明確標記 class」而不是「主題自動認出表單表」
   ────────────────────────────────────────────────────────────────────────────
   群 ② 那 22 份複製用的是 `:not(:has(> thead))` —— 用「沒有表頭」猜它是表單。
   這個猜法在本輪的目標頁上**會猜錯**：/store-setting/store/modify/base-setting/2
   同一頁四張表，兩張是表單（無 thead）、兩張是真資料表（有 thead），
   但 Pick/userPackage/edit 的「扣減時間紀錄」**有 thead 卻掛著 .fb-form-table**
   （§S-58② 已據實記錄這一張），反過來 Order/ReadyToShip/index 有 6 張表 4 張有 thead、
   2 張無 thead 卻是資料表。⇒ **「有沒有 thead」與「是不是表單」不是同一件事。**
   §S-24 當初就寫了「★只作用在明確標了 .fb-form-table 的表格」，那個判斷是對的，
   本節維持它：**治本＝把 class 補上去，不是把猜測寫進主題層。**

   ────────────────────────────────────────────────────────────────────────────
   三、本節新增的規則（只補 §S-24 當初沒遇到的三種情形，其餘一律沿用）
   ────────────────────────────────────────────────────────────────────────────
   §S-24 是照 fatboy 的 good/storageLocation/edit 轉過來的，那支表格上**沒有 fw-bold、
   沒有第三欄、也沒有 table-layout:fixed**。群 ③ 這 10 支三樣都有，所以要補三條。
   ★三條都是「補 §S-24 沒說的」，不是覆蓋 §S-24 說過的。 */

/* ① 字重：shadcn Label = font-medium(500)，內文/說明 = 400。
      現況是 <table class="… fw-bold"> 讓**整張表**（含說明文字）都變 700。
      ★這裡用「直接命中 td」勝過「從 table 繼承」——繼承值只在該元素沒有任何宣告時才生效，
        所以不需要 !important 也贏得了 table 上帶 !important 的 .fw-bold（它命中的是 table，不是 td）。
      ★寫成兩條而不是只寫 first-child：只寫 first-child 的話，值欄與說明欄仍是繼承來的 700。 */
.app-content table.fb-form-table > tbody > tr > td {
    font-weight: 400;
}
.app-content table.fb-form-table > tbody > tr > td:first-child {
    font-weight: 500;
}

/* ② 三欄表（欄位名／控制項／說明）的第三欄＝FormDescription：**不需要新規則，已經有了。**
      ★★這裡本來有一條我寫的 `td:nth-child(3){white-space:normal}`，寫完實測發現是**廢的** ——
        §補位（L11958）已經有 `.app-content table.fb-form-table > tbody > tr > td{white-space:normal}`，
        對**所有**儲存格生效，第三欄本來就會換行。
        實測佐證：把我那條停用前後，shopee 頁三欄的 white-space 都是 `nowrap,normal,normal`，
        逐字相同、像素差 0 ⇒ 它從來沒有改變過任何東西。已移除，不留在檔案裡佔位。
      ⇒ 記在這裡是因為「以為要補、其實已經有」正是造成第三套實作的原因；
        下一個人看到三欄表要換行時，答案是 L11958，不要再加一條。 */

/* ③ 兩件「量過之後與預期不同」的事，據實寫在這裡，讓下一個人不用再量一次。
      ★★這兩條原本我寫的是預期值，改完實測才發現都不對，當場訂正 —— 留下訂正過程，
        因為「§S-24 寫了什麼」與「§S-24 實際生效什麼」在這個檔裡不是同一件事。

      (a) `td:first-child{width:170px}` **在 table-layout:auto 的表上不會生效**。
          實測 /store-setting/store/modify/base-setting/2（1440、亮色）：
            · 「物流設定」table-layout:fixed ＋ width="30%" → 改前 171.59px、改後 170px（吃到了）
            · 「商店設定」table-layout:auto ＋ 值欄 width="100%" → 改前 79.45px、改後 67.30px
              （＝ 79.45 − 12px 的 padding-left，**完全沒吃到 170px**）
          原因：auto 版面把 width 當「偏好」，值欄的 100% 會把 label 欄壓回 min-content。
          ⇒ **不要宣稱「兩張卡的控制項對齊在同一條垂直線上」——實測 67.30 vs 170，沒有對齊。**
            §S-24 那句「固定寬度讓所有列的控制項對齊成一直線」只在**單一張表內**成立
            （同一表的每一列當然同寬），跨表不成立。
          ⇒ 本輪**不修**：要跨表對齊得動 blade 的 width 屬性，那是版面變更，要業主先看過。

      (b) §S-24 的 `padding-top/bottom: .5rem` **是死的**，實際生效的是 8px 之外的 .6rem。
          L966 `.app-content .table:not(.table-bordered) > tbody > tr > td{padding:.6rem .75rem}`
          特異度 (0,3,3)，§S-24 的 `.app-content table.fb-form-table > tbody > tr > td` 是 (0,2,4)
          —— 比類別數先分勝負，3 > 2 ⇒ **L966 贏**。實測改前改後 td 上下內距都是 9.6px。
          （`td:first-child{padding-left:0}` 是 (0,3,4) > (0,3,3)，所以**左內距那條有生效**，
            實測 12 → 0；同一節裡兩條規則一條生效一條不生效，差別只在多一個 :first-child。）
          ⇒ 所以本輪列高只從 51.69 收到 51.19（−0.5px），**不是**預期的 8px 節奏。
            要真的收到 .5rem 必須提高 §S-24 那條的特異度，那會一次動到已經正確的 8 支頁面，
            屬於獨立的一輪，列入 §S-80a 待辦，不在這裡順手改。 */

/* --- S-80a. 遷移狀態台帳（本輪只做群 ③，群 ② 未動）------------------------------
   ★★本輪施工範圍：群 ③ 的 8 支 baseSetting/*.blade.php，只在**無 thead 的表單表**
     的 class 屬性尾端追加一個 ` fb-form-table`。沒有刪除任何 class、沒有動任何 <td>、
     沒有動任何 name 屬性、沒有動任何 JS。

   ★★**沒有動群 ②（11 支 HR/員工頁，22 份複製）**，理由據實說明：
     那 22 份 <style> 的特異度是 (0,3,4)，§S-24 是 (0,2,4) —— **inline 那份會贏**。
     所以就算把 class 補上去，那 11 支的內距/邊框仍由自己的 <style> 決定，
     視覺不會變、SSOT 也沒有真的收斂；要真收斂必須把那 22 份 <style> 移除，
     而那是刪除既有內容，本輪守則明文禁止，必須由業主拍板後另開一輪。
     ⇒ 現在把它記在這裡，不讓它變成「以為做完了」的隱形債。

   ★★群 ③ 的 10 支裡，**只有 8 支動了**：
     · baseSetting/{shopee,shopline,custom,mallbic,momo,moplus,coupang,yahoo}.blade.php ← 已改
     · Store/_modifyBaseSetting.blade.php ← **沒改**。它是 controller 在
       「商店類型沒有對應 view」時的 fallback，而 8 種類型全都有 view ⇒ 目前渲染不到；
       它同時含表單表與資料表，改了無法在真實網址驗證。列為待辦。
     · Store/modifyBaseSetting.blade.php ← **沒改**。同上，且它是已知的孤兒檔
       （沒有任何 route/controller 渲染它，見該檔 68-75 行註解）。列為待辦。

   ★待辦（照本規格改的話會動到的檔，與風險）：
     ┌ 群 ②  11 支 / 22 份 <style>  風險：**中高**。必須先刪 <style> 才會生效，
     │        刪錯就換成另一種不對齊（inline 值 padding .55rem vs §S-24 .5rem、
     │        line-height:32px 是 inline 獨有的，§S-24 沒有等價物 ⇒ 列高會從 50 掉到約 48）。
     │        建議做法：先把 inline 那三條的**值**與 §S-24 對齊成同值再刪，分兩次上。
     ├ 群 ③  剩 2 支（_modifyBaseSetting / modifyBaseSetting）風險：**低**，但驗不到。
     ├ fs-XXpx  19 支 / 81 次  風險：**中**。pos/* 與 Seller/plan/* 是獨立視覺情境。
     ├ table-bordered  8 支  風險：**低**。多半在列印樣板（picking_a4）與 modal，
     │        官方 Table 沒有格線；但列印樣板要格線是刻意的，不能一律拔。
     └ gallery / image-inner  4 支 / 3 支  風險：**低**，且 3 支已在本日改成 data-slot 版式。
   -------------------------------------------------------------------------- */

/* --- S-81. 群 ② 收斂：HR／員工編輯頁的 inline <style> 併回 `.fb-form-table`（2026-08-08）---
   業主：「繼續治本 ssot 套用 shadcn」。接 §S-80／§S-80a 的待辦「群 ②」。

   ── ★★先訂正一會害死人的前提：那些 inline 不是「逐字複製」，而且**不是 9 支是 11 支**──
   §S-80a 當初記的是「22 份逐字複製、md5 全部相同」。逐份剝註解重算 md5，實際是**四種**。
   ★★而且清單本身也不完整：只用「HR 編輯頁」去撈會漏掉 employee 側的兩支
     （employee/user/informationEdit、employee/user/darkaEdit）——
     它們的 md5 與 hr 側同版本逐字相同，但檔名不在原本那張表裡。
     正確的撈法是 `grep -rl "<style>" resources/views/hr resources/views/employee`
     再逐支判斷「開頭是不是 `.app-content .card .table:not(:has(> thead))`」，
     命中 11 支（另有 hr/user/index 85 字、hr/user/printerUsers 1110 字，與表單表無關，不算）。
     ⇒ 這正是「清單沉默地不完整」：少兩支跟少零支在報表上長得一模一樣。

     md5(前 12)     字數   支數  檔案 / 本輪狀態
     70020eb9c326   319     5   hr/user/settingEdit ✔改、hr/role/edit ✔改、
                                hr/department/edit ✔改、employee/user/salaryEdit ✔改、
                                hr/user/salaryEdit ✘未改（路由 500，見下）        ← 本節處理
     3dfd40b81dd3   779     3   hr/user/informationEdit、hr/user/create、
                                employee/user/informationEdit                    ← 未處理
     bf1c7526d70b   486     2   hr/user/darkaEdit（路由 500）、employee/user/darkaEdit ← 未處理
     112a7c758f42   509     1   hr/user/settingTokenIndex                        ← 未處理

   ★★兩支**打不開**的頁面（本輪因此不動它們，理由與 §S-80a 不動 _modifyBaseSetting 相同：
     改了無法在真實網址驗證，就是把未驗證的東西放進版本）：
       · /hr/user/{id}/salaryEdit → 500
       · /hr/user/{id}/darkaEdit  → 500
     根因**與樣式無關**：Hr\UserController 這兩個 action 回的是
     `view('tenant.hr.user.salaryEdit')` / `view('tenant.hr.user.darkaEdit')`，
     而專案裡沒有 `tenant` 這個 view 命名空間／目錄 ⇒ FileViewFinder 直接丟
     InvalidArgumentException。同一支 controller 的 settingEdit/informationEdit 都寫
     `hr.user.*`（沒有 `tenant.` 前綴）⇒ 是那兩行前綴多打了。
     ★**本輪沒有修它**：那是行為變更（會讓兩個現在打不開的頁面突然可用），
       要業主先確認那兩頁是不是刻意停用的。employee 側的同名頁面是好的，可以照常用。

   ★逐份讀完之後的結論比 md5 更有用：**四種版本的「表單表」核心兩條規則是逐字相同的**，
     319 那份就是核心；另外三種是在核心後面**各自追加**了與表單表無關的頁面專屬規則：
       · 779 = 核心 + `.card-body .btn.btn-inline-upload`（上傳鈕改虛線 ghost）
       · 486 = 核心 + `.app-content .card .table > thead > tr > td`（表頭寫成 td 的補救）
       · 509 = 核心 + `.input-group-prepend > .input-group-text`（BS4 遺留容器撐成 36px 的補救）
     ⇒ 那三段追加**不是表單層 SSOT 的範圍**，不能跟著併進來。本節只收核心兩條。
     ⇒ 但仍**必須四種分別驗** —— 拿 319 的驗證結果去蓋 779，多出來的 460 字就沒人看過。
   ★所以另外三種版本要收斂時，順序是：先把「追加那一段」各自搬去主題（或確認它已被
     §S-64／§S-58 等既有規則涵蓋），**再**拿掉核心兩條；一次拿掉整段 <style> 會連同
     上傳鈕／表頭 td／input-group 前綴那三個修正一起消失，那是三個獨立的視覺回歸。

   ── 三類對照：inline 的宣告 vs §S-24 已有的 ──────────────────────────────────
   inline 核心規則 ①  `.app-content .card .table:not(:has(> thead)) > tbody > tr > td`  (0,3,4)
       vertical-align: middle       → §S-24 逐字相同（同值）
       border-bottom-width: 0       → §S-24 的 `border: 0` 涵蓋（同效，實測 border 四邊皆 0px）
       padding-top/bottom: .55rem   → **值不同**，見下面「為什麼不補 padding」
   inline 核心規則 ②  `… > td label.col-form-label`  (0,4,5)
       padding-top/bottom: 0
       margin-bottom: 0
       line-height: 32px            → **§S-24 完全沒有等價物**，這是本節唯一真的要補的東西

   ── ★為什麼本節只補一條規則、而且**刻意不補 padding** ──────────────────────
   inline 的 .55rem（8.8px）之所以看得到，是因為它 (0,3,4) 贏過
   L966 `.app-content .table:not(.table-bordered) > tbody > tr > td{padding:.6rem .75rem}` (0,3,3)。
   而已經收斂完成的 16 支 `.fb-form-table` 頁面，實測上下內距是 **9.6px**（＝ L966 的 .6rem）——
   因為 §S-24 自己那條 `padding: .5rem` 是 (0,2,4)，**輸給 L966，是死規則**（§S-80 ③(b) 已記載）。

   ⇒ 全站因此有兩個值：inline 群 8.8px、已收斂群 9.6px。SSOT 只能留一個。
   ⇒ 選 **9.6px**，理由是「取螢幕上已經在跑的那一份」：16 支頁面（good 三支、Pick 五支、
     baseSetting 八支）已經以 9.6px 出貨，改成 8.8px 等於為了 5 支去動 16 支已經正確的頁面，
     違反「不能讓已經正確的頁面位移」。
   ⇒ 代價據實寫在這裡：本節涵蓋的 5 支頁面，每一列高 **49.59px → 51.19px（＋1.6px）**，
     ＝ 上下各 ＋0.8px。這是本節唯一的密度變化，不是 bug。
   ★★所以本節**沒有** padding 宣告 —— 「§S-24 的 .5rem 是死的」這件事本輪一樣不修：
     要修它得提高特異度贏過 L966，那會一次動到全部 16 支已正確的頁面，屬於獨立的一輪。
     ★同一節裡兩條規則一條生效一條不生效，差別只在多一個 :first-child
     （`td:first-child{padding-left:0}` 是 (0,3,4) > (0,3,3)，**有**生效）。

   ── 本節這條規則為什麼碰不到那 16 支已收斂的頁面 ──────────────────────────
   選擇器鎖 `label.col-form-label`（元素選擇器 label 開頭）。
   全站掃過：那 16 支 blade 裡 `col-form-label` 一共出現 16 次，**全部是 `<div>`**
   （form-check-inline / form-switch 的包裝層），`<label class="…col-form-label">` 命中數 **0**。
   ⇒ 本條對它們是空集合。★這不是推論，加規則前後對 9 個對照頁各量一輪，全部 0 項變動（見下）。
   ★沿用 inline 原本就有的 `label.` 限定：權限勾選那種 `div.col-form-label` 不受影響。

   ── 特異度 ──────────────────────────────────────────────────────────────────
   本條 (0,3,5)：.app-content / .fb-form-table / .col-form-label ＋ table,tbody,tr,td,label。
   要贏的對手是 Bootstrap `.col-form-label{padding:calc(.375rem + 1px) 0}` (0,1,0)
   與本檔 L771 `.form-label{margin-bottom:.35rem}` (0,1,0)，有餘裕。
   ★line-height 32px 對齊 `.form-control-sm` 的高度 —— 列高是被 label 決定的不是被欄位決定的，
     少了這條，label 盒高會回到 7+21+7 ＋ margin 5.6 ＝ 40.6px，列高會比控制項還高。

   ── 驗證（走 https://test.2025-seller.mtest，1440 寬）───────────────────────
   ★正對照先做：對 9 個對照頁把「所有含 .fb-form-table 的規則」刪掉，量到 36～2802 項變動
     （幾何 12～2726 項）⇒ 偵測器會紅，不是瞎的。
   ★等價實測：在瀏覽器裡對 5 個目標頁掛上 class 之後，
     「inline 仍在」與「inline 停用＋本節規則」兩態逐元素比對，
     殘差**只剩上面說的 padding 8.8→9.6 那一項**（及它撐高父層的連鎖 box），
     line-height / margin / border / vertical-align / 字級 / 顏色 **零殘差**。
   -------------------------------------------------------------------------- */
.app-content table.fb-form-table > tbody > tr > td label.col-form-label {
    padding-top: 0;
    padding-bottom: 0;
    margin-bottom: 0;
    line-height: 32px;
}

/* --- S-82. `.btn-close` 暗色下實質看不見：改用 mask + currentColor（2026-08-08）------
   ★這一行是給 tests/Feature/Theme/ThemeOverrideAnnotationsTest 認的「章節錨點」，
     寫法必須是 `/* --- S-82. …`（S- 前面不可以多 §，見 §S-46 的說明）。

   對應 .agent/TODO.md 的 🔴 D14。

   ═══ 一、改前實測（不是引用舊註解，是 2026-08-08 現量）═══
   走 https://test.2025-seller.mtest，1440×900，headless Chrome 150，
   **真的用 bootstrap.Modal.show() 把彈窗開起來**（不是 display:block 硬攤開），
   對每一顆 `.btn-close` 的外框截圖後把像素倒進 canvas 做直方圖：
   bg = 出現次數最多的顏色，fg = 佔比 ≥1% 的顏色裡與 bg 亮度差最大的那個。

     7 頁 × 亮/暗，共量到 87 顆彈窗關閉鈕（同一頁多支 modal 逐支開）
       亮色：對比 3.95:1、uniq 17　　fg rgb(128,128,128) on rgb(255,255,255)
       暗色：對比 **1.09:1**、uniq 5　fg rgb( 12, 12, 12) on rgb( 23, 23, 23)   ← 看不見
     computed 一律 `filter: none`、`opacity: 0.5`、`color: rgb(0,0,0)`、背景是 SVG 圖。

   ⇒ 與 .agent/TODO.md D14 記的數字逐位相同（3.95 / 1.09、uniq 17 / 5）。
   ⇒ **§S-70 一 裡「對照組 #shipmentGroupModal 暗色 uniq=18／對比 5.21:1（看得見）」
      這句已於 2026-08-08 複驗推翻**（同一支 modal、同一頁，實測 uniq=5／1.09:1）。
      那句原文一個字都沒刪，已就地標注「★已過期／與實測不符」，見本檔 §S-70 一。

   ═══ 二、成因（Bootstrap 5.3.3 原始碼，assets/shadcn/bootstrap/css/bootstrap.min.css）═══
     .btn-close{ --bs-btn-close-color:#000;
                 --bs-btn-close-bg:url("data:image/svg+xml,…fill='%23000'…");
                 --bs-btn-close-opacity:0.5;
                 --bs-btn-close-white-filter:invert(1) grayscale(100%) brightness(200%);
                 color:var(--bs-btn-close-color);
                 background:transparent var(--bs-btn-close-bg) center/1em auto no-repeat;
                 opacity:var(--bs-btn-close-opacity) }
     .btn-close-white{ filter:var(--bs-btn-close-white-filter) }
     [data-bs-theme=dark] .btn-close{ filter:var(--bs-btn-close-white-filter) }
   叉叉是**背景圖**，顏色 `%23000` 寫死在 data URI 裡 ⇒ 量 `color` 沒有意義，
   而 50% 的黑疊在 rgb(23,23,23) 上剛好就是 rgb(12,12,12)，數字對得上。
   本專案的暗色是 `html.dark`（見 includes/_link.blade.php L10 讀 localStorage
   `seller-theme`），**不是** `data-bs-theme=dark` ⇒ Bootstrap 那條原生暗色規則
   在本專案的暗色模式下**永遠不會命中**。這就是缺口。

   ═══ 三、為什麼**不用** `html.dark .btn-close{filter:invert(1)}`（TODO 建議的那條）═══
   ★★這條會把問題搬到別人身上，實測有反例，不是理論顧慮：

     (a) **`data-bs-theme="light"` 的淺色島**。`AiTool/alpha/index.blade.php` 的
         #imageModal / #themeModal / #termsModal 三支彈窗都寫死 `data-bs-theme="light"`
         （L557 / L618 / L651）。實測：**頁面切到暗色時，這三支仍然是白底**
         （bg rgb(255,255,255)、對比 3.95:1，與亮色逐位相同）。
         `html.dark` 選擇器不看島，會把這三顆反白成「白叉壓白底」。
     (b) **`#sidebar` 每一頁都掛 `data-bs-theme="dark"`**（includes/_nav.blade.php L2）。
         選擇器只要沾到 `[data-bs-theme]` 就有波及側欄的風險。
     (c) `.btn-close-white` 全站 5 顆（Seller/bill/recharges L329・L413、
         Tutorial/PackagingMachine L87、Order/ReadyToShip L325・L566），
         它的 `filter` 不看主題，暗色下已知量到 1.03:1（§S-70 一 的紀錄）。
         再疊一層 invert 只會更亂。

   ═══ 四、治法：叉叉改用 mask-image + currentColor（不需要任何主題選擇器）═══
   把「顏色寫死在 data URI 裡的背景圖」換成「用 mask 剪形狀、顏色吃 currentColor」，
   顏色就自動跟著它所在容器的前景色走：
     · 暗色 modal-header → 前景近白 ⇒ 白叉
     · `data-bs-theme="light"` 淺色島 → 前景近黑 ⇒ 黑叉（(a) 自動正確）
     · `.modal-content.bg-dark` → 由 `.btn-close-white` 明講白色（見下）
   ★**整節沒有一條 `html.dark` / `[data-bs-theme]` 選擇器**，所以 (b) 的側欄波及風險
     從根上不存在 —— 不是「小心避開」，是沒有東西可以打到它。

   ── 為什麼畫在 ::before 而不是直接 mask 元素本體 ────────────────────────────
   §S-39 (7) 給 `.btn-close:active` 的回饋是 `background-color: hsl(var(--foreground)/.12)`
   的一塊底。mask 會把元素**整個**剪掉（含它自己的 background），直接 mask 本體
   等於把那塊 active 底一起剪成叉叉形狀 ⇒ 按下去的回饋消失。
   改由 ::before 畫叉叉，元素本體的 background 就仍然歸 §S-39 用，四態一顆不掉。
   ★§S-39 那條規則**一個字都沒有動**。

   ── 叉叉的形狀是 Bootstrap 原本那一條 path，逐字元照抄 ──────────────────────
   只把「怎麼上色」換掉，**幾何完全不動**（viewBox 0 0 16 16、同一段 path、
   同樣 `center / 1em auto no-repeat`）⇒ 位置與大小改前改後不會有一個像素的位移。
   data URI 裡的 `fill='%23000'` 也原樣留著：mask 取的是 alpha 通道，fill 值不影響結果，
   留著才能一眼看出「這就是 Bootstrap 那顆」。

   ── 不透明度：0.5 → 0.7（官方 shadcn 的值）───────────────────────────────
   TODO 說得對，`opacity:.5` 會把對比再砍一半。上游 shadcn dialog.tsx 的 DialogClose 是
     "… rounded-xs opacity-70 transition-opacity hover:opacity-100 …"
   ⇒ 0.7 / hover 1.0。這裡不是自己挑數字，是對齊官方。
   ★改的是 Bootstrap 自己的變數（`--bs-btn-close-opacity` / `-hover-opacity`），
     所以 `:hover` / `:focus` / `.disabled` 三條原生規則會自動跟著走，不必各寫一條。
   ★原值存查：`--bs-btn-close-opacity: 0.5`、`--bs-btn-close-hover-opacity: 0.75`
     （Bootstrap 5.3.3 預設，記在此存查）。

   ── `color` 為什麼要動 ────────────────────────────────────────────────
   Bootstrap 在 `.btn-close` 上**明寫** `color: var(--bs-btn-close-color)`，
   而 `--bs-btn-close-color: #000` ⇒ currentColor 在這顆按鈕上永遠是黑的，
   不會繼承 modal-header。改成 `--bs-btn-close-color: currentColor`
   （在 `color` 屬性上 currentColor ＝ 繼承值）就接回容器。
   ★動變數而不是動 `color` 屬性：`.btn-close:hover` (0,2,0) 也寫著
     `color: var(--bs-btn-close-color)`，只改 `.btn-close{color:…}` (0,1,0) 會在 hover 時被打回黑色。
   ★原值存查：`--bs-btn-close-color: #000`（Bootstrap 5.3.3 預設，記在此存查）。

   ── `filter: none` 為什麼需要，以及為什麼要三個選擇器 ──────────────────────
   叉叉改吃 currentColor 之後，任何 `invert(1)` 都會把「正確的顏色」倒過來變成錯的。
   要壓過的兩條 Bootstrap 規則特異度不同，所以列三個選擇器一次收乾淨：
     `.btn-close-white`           (0,1,0) → 本檔在 bootstrap.min.css 之後載入，同分後到者勝
     `[data-bs-theme=dark] .btn-close` (0,2,0) → 需要同為 (0,2,0) 的 `[data-bs-theme] .btn-close`
     `.btn-close`                 (0,1,0) → 保險：任何其他來源掛上來的 filter 一併歸零
   ★`[data-bs-theme] .btn-close` 是**屬性存在**選擇器，不分 light/dark 一律 `filter:none`。
     它不會改任何顏色（只把濾鏡關掉），所以側欄那個 `data-bs-theme="dark"` 即使被選中
     也只是「不做反白」，而側欄本來就沒有 `.btn-close`（全站 49 支 blade 逐支查過，0 命中）。

   ── `.btn-close-white`：一開始寫成「明講白色」，實測後推翻，改回吃 currentColor ──
   ★★這一段保留我自己的錯誤推論與推翻它的實測，因為結論反直覺，刪掉下一個人會再踩一次。

   〔錯誤推論（未實測就寫下的，原文留存）〕
     `Tutorial/PackagingMachine/index.blade.php` L84-87 是 `modal-content bg-dark`
     ＋ `modal-header border-0`（**標題列自己沒有 text-white**，白色只掛在 h5 上）
     ⇒ 這顆的 currentColor 在亮色模式下是深色，光靠繼承會變成「黑叉壓 bg-dark」。
     所以把 class 的語意直接寫成顏色：`btn-close-white` ＝ 這顆叉叉是白的
     （`.btn-close-white { --bs-btn-close-color: #fff }`）。

   〔實測推翻（2026-08-08，走 https://test.2025-seller.mtest/tutorial/packaging-machine，
     真的把 #videoModal 開起來後量標題列的 computed color）〕
       亮色：.modal-header 的 color = rgb(250,250,250)（淺） / 底 rgb(23,23,23)（深）
       暗色：.modal-header 的 color = rgb( 23, 23, 23)（深） / 底 rgb(250,250,250)（淺）
     ⇒ **本專案的 `.bg-dark` 會跟著主題翻轉**（暗色模式下 `.bg-dark` 反而是淺底深字），
       前提「亮色下 currentColor 是深色」根本不成立，而且方向剛好相反。
     ⇒ 寫死 `#fff` 的下場：暗色模式量到 **1.03:1（白叉壓 rgb(250,250,250) 淺底）** ——
       跟 §S-70 一 記的「btn-close-white 暗色 1.03:1」是同一個病，等於原地踏步。
     ⇒ 改成什麼都不寫（吃 currentColor）之後：**亮 8.84:1、暗 6.51:1**，兩個模式都對。
       （對照：改前亮 5.26:1、暗 1.03:1；寫死 #fff 的中途版本亮 9.14:1、暗仍 1.03:1）
   ⇒ 定案：**`.btn-close-white` 不再指定顏色，只把它的 filter 關掉**。
     這個 class 在本專案的實際語意是「我坐在反色底上」，而反色底的前景色本來就已經
     由主題翻轉好了 —— currentColor 拿到的就是正確答案，不需要第二套判斷。
   ★原值存查：`.btn-close-white { filter: var(--bs-btn-close-white-filter) }`
     （Bootstrap 5.3.3，記在此存查）。
   ★live 的 `.btn-close-white` 只有 3 顆：Seller/bill/recharges L329・L413、
     Tutorial/PackagingMachine L87。grep 另外命中的 Order/ReadyToShip L325・L566
     **在 `{{-- --}}` 的「原文存查」註解裡**，不是活的 markup（§S-70 已把那三支改成素面）。

   ═══ 五、改後實測（同一支腳本、同一批頁面、同一組取樣法）═══

   ── (1) 彈窗關閉鈕的對比：7 頁 × 亮/暗，每一顆都真的 show() 出來後截圖取樣 ──
     情境                                              改前        改後
     ─────────────────────────────────────────────────────────────────────
     一般彈窗・亮色（19 顆）                           3.95:1      **8.45:1**
       fg/bg                                          128,128,128  77,77,77  on 255,255,255
       uniq                                            17          20~25
     一般彈窗・暗色（20 顆）                           **1.09:1**  **8.84:1**
       fg/bg                                           12,12,12    182,182,182 on 23,23,23
       uniq                                             5          19~22
     `data-bs-theme="light"` 淺色島（/ai-tool/alpha 3 顆，亮暗同值）
                                                       3.95:1      **8.45:1**
       ★★底色改前改後都是 rgb(255,255,255)（**沒有被反白**）⇒ 三、(a) 的反例已守住。
     `.btn-close-white`（/tutorial/packaging-machine #videoModal）
       亮                                              5.26:1      **8.84:1**
       暗                                              **1.03:1**  **6.51:1**
     `.bg-light` 標題列（/seller/bill/recharges #modal-instruction）
       亮                                              3.88:1      **8.13:1**
       暗                                              **1.23:1**  **7.80:1**
   ⇒ 全部由「看不見」（1.03～1.23:1）拉到 6.5:1 以上，且沒有製造任何新的低對比反例。

   ── (2) 四態沒有掉（CDP forcePseudoState，#shipmentGroupModal，亮/暗各一輪）──
     state           opacity   元素自己的 background-color        對比
     normal          0.7       transparent                       亮 8.45 / 暗 8.84
     hover           1         transparent                       亮 21.0 / 暗 17.18
     active          0.7       **rgba(…, 0.12) 仍在**            亮 6.96 / 暗 7.08
     focus-visible   1         transparent（+ BS 的 focus shadow）亮 21.0 / 暗 17.18
   ★active 那一列就是「::before 方案」要保住的東西：§S-39 的按下去底色仍然畫得出來。
   ★`::before` 實測 32×32（＝ .btn-close 的 padding box），mask 有掛上，
     `filter` 四態都是 `none`（Bootstrap 兩條反白規則確認被壓過）。

   ── (3) 沒有動到的東西（全站 14 頁 × 亮/暗 逐元素比對，見 §S-83 五 的同一份報告）──
     本節造成的元素屬性變動**只有兩種**，一種都不多：
       `<button class="btn-close">` 的 `opacity` 0.5 → 0.7        186 顆
       `<button class="btn-close">` 的 `color` rgb(0,0,0) → 繼承值 93 顆（只有暗色頁會變）
     沒有任何幾何變動（x / y / w / h / padding / margin 零筆來自本節）。
   -------------------------------------------------------------------------- */
.btn-close {
    /* ① 顏色與不透明度：改 Bootstrap 自己的變數，:hover / :focus / .disabled 自動跟上 */
    --bs-btn-close-color: currentColor;   /* 原值 #000 */
    --bs-btn-close-opacity: .7;           /* 原值 0.5，對齊 shadcn DialogClose 的 opacity-70 */
    --bs-btn-close-hover-opacity: 1;      /* 原值 0.75，對齊 hover:opacity-100 */
    /* ② 叉叉不再由背景圖畫（背景留給 §S-39 的 :active 底），改由 ::before 畫 */
    background-image: none;
    /* ③ ::before 的定位基準。`.alert-dismissible .btn-close`(0,2,0) 會把它改成 absolute，
          那也是合法的定位基準，兩種情形 ::before 都對齊 padding box。 */
    position: relative;
}
.btn-close::before {
    content: "";
    position: absolute;
    inset: 0;                              /* ＝ .btn-close 的 padding box（1em 內容 + .25em 內距） */
    background-color: currentColor;        /* 叉叉的顏色 ＝ 容器前景色 */
    pointer-events: none;
    /* 形狀逐字元照抄 Bootstrap 5.3.3 的 --bs-btn-close-bg，只是改當 mask 用 */
    -webkit-mask-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 0 1 1.414 0L8 6.586 14.293.293a1 1 0 1 1 1.414 1.414L9.414 8l6.293 6.293a1 1 0 0 1-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L6.586 8 .293 1.707a1 1 0 0 1 0-1.414z'/%3e%3c/svg%3e");
            mask-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 0 1 1.414 0L8 6.586 14.293.293a1 1 0 1 1 1.414 1.414L9.414 8l6.293 6.293a1 1 0 0 1-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L6.586 8 .293 1.707a1 1 0 0 1 0-1.414z'/%3e%3c/svg%3e");
    -webkit-mask-position: center;         mask-position: center;
    -webkit-mask-size: 1em auto;           mask-size: 1em auto;   /* ＝ 原本的 center/1em auto */
    -webkit-mask-repeat: no-repeat;        mask-repeat: no-repeat;
}
/* 反白濾鏡一律關掉（理由與特異度見上「filter: none 為什麼需要」） */
.btn-close,
.btn-close-white,
[data-bs-theme] .btn-close {
    filter: none;
}
/* ★`.btn-close-white` **刻意沒有**在這裡指定任何顏色 —— 它只需要上面那條 `filter: none`。
   理由（實測推翻了「寫死 #fff」的直覺）見本節「`.btn-close-white`：一開始寫成
   『明講白色』，實測後推翻，改回吃 currentColor」那一段。 */

/* --- S-83. 卡片三段內距的預設值收斂成 1rem（2026-08-08）--------------------------
   ★這一行是給 tests/Feature/Theme/ThemeOverrideAnnotationsTest 認的「章節錨點」，
     寫法必須是 `/* --- S-83. …`（S- 前面不可以多 §，見 §S-46 的說明）。

   對應 .agent/TODO.md 的 🟡 D19。

   ═══ 一、先更正 TODO 的題目：實際渲染出來的不是三種值，是**兩種** ═══
   全站掃描（95 個可直接 GET 的後台頁、398 個 .card-header/.card-body/.card-footer/
   .fb-filter-card 節點，走 https://test.2025-seller.mtest，1440 寬）：

     .card-header   177 顆
       16px 四邊 ……… 153 顆（151 顆是 .card 直接子元素、2 顆不是）  ← 來源：markup 的 `.p-3`
        8px/16px ……… 24 顆                                        ← 來源：Bootstrap 預設
       12px      ………  0 顆   ★★TODO 寫的第三個值在**渲染結果裡不存在**

   TODO 說的「主題某段 12px」查到是本檔 §filterbar 的 `.fb-filter-card { padding: 12px }`
   （本檔 L6110 附近）—— 那條的註解自己就寫著「改成 card-header 的外觀」，
   所以被當成第三種 card-header 內距。但它是**另一個 class**，而且該處早有稽核註記
   「★2026-08-04 稽核：blade 0 命中」，本次全站掃描同樣量到 **0 個 .fb-filter-card 節點**
   ⇒ 它是死規則，不是活著的第三種內距。**該規則一個字都沒有動**（禁刪守則），
     只是不要再把它算進 D19 的三種值裡。

   ═══ 二、為什麼收到 1rem 而不是官方 shadcn 的 24px（px-6）═══
   官方 card.tsx 的 CardHeader / CardContent / CardFooter 確實都是 `px-6` = 24px。
   這裡刻意偏離，理由是**對齊會被打破**，不是嫌工程量大：

     (a) 同一批掃描的 .card-body 186 顆：16px 130 顆／0px 43 顆／24px 11 顆／48px·16px 2 顆。
         **主流是 16px**，而且那個 16px 有出處 —— §S-58② 在 2026-08-06 就已經明文裁定
         「選 1rem 不是挑的，是對齊已經正確的那三張」，並寫死
         `.app-content .card > .card-body:has(> table.fb-form-table){padding:1rem!important}`。
         標題列改 24px 而 body 留 16px ⇒ 標題文字比下面的內容多縮排 8px，
         **正好是 D19 抱怨的那件事（離邊緣的距離不一樣）換個地方再發生一次**。
     (b) 要讓 body 一起上 24px，就得同時壓過 `.p-0`（43 顆，刻意讓表格滿版貼邊）、
         `.p-3`（大宗）、`.p-md-4`（11 顆，§S-6x 的挑選卡刻意設計）三種 utility，
         等於推翻三份已經寫下理由的既有裁示，blast radius 遠超過 D19 的題目。
     (c) 官方 px-6 配的是「卡片裡放表單與說明文字」的低密度版面；本專案的卡片裡放的是
         資料表與篩選工具列，§S-74 已經記過提高字級造成橫向捲動增加的代價，
         再加 8px 兩側只會讓已經在捲的表格捲更多。

   ⇒ 裁定：**卡片三段（header / body / footer）的「預設」內距一律 1rem = 16px**，
     要偏離一律走 Bootstrap 的 `p-*` utility 明講（見下「為什麼不加 !important」）。
     header / body / footer 三者同值，D19 要的一致性成立。

   ═══ 三、為什麼**不加** `!important`（這是本節的關鍵設計）═══
   Bootstrap 的 `.p-*` utility 帶 `!important`。本節不帶，於是階層變成：

     沒寫 p-* 的 card-header/footer → 吃本節的 1rem      （24 顆 header、2 顆 footer 被修正）
     寫了 `.p-3` 的                 → 吃 utility 的 1rem（同值，結果不變，0 顆位移）
     寫了 `.p-0` 的                 → 吃 utility 的 0    （**刻意保留**，見下）

   ★★`.p-0` 必須保留，這不是漏網，是量出來的：
     list 頁的版型是「滿版表格」——實測 /order-management/status/new-orders：
       卡片左緣 244 → 標題列第一個子元素 Δ17（16 內距 + 1 邊框）
                    → 表格 / 第一格 Δ1（`card-body p-0`，滿版貼邊）
                    → 頁尾第一個子元素 Δ1（`card-footer … p-0`）
     頁尾與它正上方的表格**現在是對齊的**（都 Δ1）。若把 footer 也強制成 16px，
     頁尾會跑到 Δ17 而表格還在 Δ1 ⇒ 反而製造新的不對齊。
     ⇒ `p-0` 是「滿版」這個版型的成套 opt-out（body 與 footer 一起 0），要保留。
     ★這也是 §S-58② 當初只咬「裝著 .fb-form-table 的那個 card-body」而不是全部 card-body
       的同一個道理：滿版表格與表單卡是兩種版型。

   ★對照 §S-74 的 `font-weight: 600 !important`（本檔 L12935 附近）：那條**必須**帶
     !important，因為它要壓過 markup 上的 `.fw-bold`（那是「和它衝突」的 utility）。
     本節相反 —— `.p-3` 與本節同值、`.p-0` 是有意的偏離，兩種都不需要壓過。
     ⇒ 同一份主題裡兩條規則一條帶 !important 一條不帶，理由在這裡寫明，不是不一致。

   ═══ 四、選擇器 ═══
   `.app-content .card-header`（0,2,0）要贏 Bootstrap 的 `.card-header`（0,1,0），有餘裕。
   ★用 `.app-content xxx` 而不是 `.card > .card-header`：掃描到的 177 顆 header
     **全部**在 `.app-content` 底下（`inAppContent` 逐顆量過，0 例外），但其中 2 顆
     不是 `.card` 的直接子元素 ⇒ 用後代選擇器才收得齊。
   ★`.app-content` 前綴同時把買家端 / 登入頁 / POS 收銀（`.pos`，另有一套版面）擋在外面，
     與本檔既有的 `.app-content .card > .card-header` 同一個作用域慣例。
   ★★★2026-08-09 更正（上面那句原文一字未刪，保留存查）：**「POS 被擋在外面」是錯的。**
     實證：`resources/views/layouts/pos.blade.php:29` 是
       `<div id="content" class="app-content p-0">`
     而 `pos/machine.blade.php:1` 是 `@@extends('layouts.pos')`、`:70` 是 `@@section('content')`
     （★兩個指令名刻意寫成雙 @@ 跳脫 —— 這是 CSS 檔不會被 Blade 編譯，但同一句話
       在 `buyer/layout.blade.php` 的註解裡寫成裸指令時整頁 500 過兩次，
       所以本檔一律照同一個規矩寫，避免有人整段複製回 blade）。
     ⇒ POS 的表格與卡片坐在 `#content.app-content` **之內**，`.app-content …` 這批規則
       POS **現在就已經全部吃到了**。
     ★這個更正很重要：「拿掉前綴會打壞 POS」一直是不敢做治本的**主要理由**，
       而那個風險並不存在（§S-88 因此得以落地，實測 POS 逐元素 diff = 0）。
       真正需要保護的是**控制項尺寸階梯**（登入頁 / 金流頁 / 買家頁的 40px 觸控下限），
       那是另一組規則，§S-88 一條都沒有搬。
     ★同一句錯誤敘述也被 `buyer/layout.blade.php` 抄過去，該檔已於 2026-08-09 自行更正。

   ═══ 五、改動範圍（改前掃描的實際筆數）═══
     .card-header  24 顆：padding 8px 16px → 16px（每顆高度 +16px）
     .card-footer   2 顆：padding 8px 16px → 16px（每顆高度 +16px）
     其餘 151 + 33 顆已經是 utility 決定的值，**computed 逐位不變**。
   ⇒ TODO 說的「收斂會讓全站每一張卡變高」實際上只有 26 顆會變高，不是全站每一張。

   ★★再更正 TODO 一次：**橫向內距本來就已經全站一致，三個值只差在縱向**。
     兩種 padding 字串攤開來看：`16px 16px 16px 16px` 與 `8px 16px 8px 16px`
     —— 左右都是 16px。Bootstrap 的 `--bs-card-cap-padding-x` 本來就是 1rem，
     `.p-3` 也是 1rem ⇒ **「同一種卡片標題在不同頁面離邊緣的距離不一樣」在橫向不成立**，
     不一致的是上下（8 vs 16）。這件事讓本節的風險比 TODO 預期低很多：
     沒有任何欄位會左右位移，只有卡片標題列長高 8px，下方內容整體下移 16px。

   ═══ 六、改後實測 ═══

   ── (1) 全站掃描（同一支腳本、同一批 95 頁，改前 → 改後）──
     .card-header 177 顆：16px 153 + 8px/16px 24  →  **16px 177（單一值）**
     .card-footer  35 顆：0px 33 + 8px/16px 2     →  **0px 33 + 16px 2**
     .card-body   186 顆：16px 130 / 0px 43 / 24px 11 / 48px·16px 2  →  **逐位不變**
   ⇒ header 的三種值收成一種；header / body / footer 的**預設**都是 1rem；
     其餘全部是 markup 上 `p-*` utility 明講的偏離。

   ── (2) 整批回歸：14 頁 × 亮/暗 = 28 組，逐元素比對 ＋ 整頁像素 diff ──
     元素比對用「tag + 兄弟序號」組成的路徑當 key（不是走訪序號），
     所以元素增減不會讓後面全部錯位。改前 → 改後 **968 個元素有變動**，逐項歸因：

       欄位        筆數   來源
       ─────────────────────────────────────────────────────────────────
       padding      22   `.card-header`/`.card-footer` 8px 16px → 16px（**只有 3 種選擇器**）
       h            75   上述 26 顆自己 +16px，以及它們的祖先 .card/.col/.app-content 跟著長
       y           706   長高的卡片**下方**內容整體下移 8/16px
       opacity     186   `<button class="btn-close">` 0.5 → 0.7（§S-82）
       color        93   `<button class="btn-close">` rgb(0,0,0) → 繼承值（§S-82）
       w / cls / display / x  38  **雜訊，非本次改動**（見下）
     ⇒ 除了 btn-close 的兩個顏色屬性之外，**全部是幾何**，而且幾何全部可以追到
       那 26 顆長高的 header/footer。沒有一筆是「不知道為什麼會變」。

     雜訊的來源（改前改後跑兩次相同 CSS 也會出現，已隔離）：
       · `.fb-ph-freshness`（「更新：HH:MM」）的寬度 ±1px —— 時間字串本身在變
       · `.pace` 讀取進度條 —— 截圖當下的動畫相位
       · `/pick/desktop/create` 每次載入會亂數產生工作桌編號與對應 QR 圖

   ── (3) ★守門自己要先被證明會紅（正對照 / 負對照都做）──
     負對照（同一份 CSS 連跑兩次 after / after2）：
       元素變動 **28 筆，全部是 `.fb-ph-freshness` 的寬度 122↔123**；
       28 張整頁截圖裡 12 張像素差 **0**，其餘只有 45px（時間字串那一行）
       ＋ `/pick/desktop/create` 的亂數 QR。⇒ 偵測器不會亂噴。
     正對照（在 after 之上注入 `.app-content .card-header{padding:40px!important}`）：
       元素變動 **3476 筆**，`padding` 46 筆、`h` 149 筆、`y` 3362 筆，
       樣本可讀到 `<DIV class="card-header fw-bold"> 57 → 105`。
       ⇒ **偵測器確實抓得到 card-header 的內距變化**，「968 筆而且全部可歸因」不是假綠燈。

   ── (4) console / pageerror ──
     14 頁 × 亮/暗、以及 §S-82 那 9 頁，**JS pageerror = 0、HTTP ≥400 的資源 = 0**，
     唯一例外是 `/ai-tool/alpha` 兩張測試資料的圖 `/file-manager/preview/{uuid}` 回 404
     —— 那是**改前就有**的資料問題（與 CSS 無關），本輪沒有修也沒有掩蓋。
   -------------------------------------------------------------------------- */
.app-content .card-header,
.app-content .card-footer {
    /* 原值：Bootstrap `.card-header/.card-footer{padding:var(--bs-card-cap-padding-y)
       var(--bs-card-cap-padding-x)}` ＝ 0.5rem 1rem ＝ 8px 16px（記在此存查） */
    padding: 1rem;
}

/* ★★★ S-83 訂正（2026-08-08）：「官方 CardHeader 是 24px 內距」這句話**本身就是錯的** ★★★
   ────────────────────────────────────────────────────────────────────────────────
   §S-83 上面的正文、以及 .agent/TODO.md 的 D19，都寫著
     「官方 shadcn card.tsx 的 CardHeader / CardContent / CardFooter 全部 px-6 = 24px」
   並據此把題目定成「本專案 16px < 官方 24px」。**去讀原始碼之後，這個前提不成立。**

   出處（2026-08-08 實際抓檔，不是憑印象）：
     shadcn-ui/ui @ main，`apps/v4/registry/new-york-v4/ui/card.tsx`
       Card        = "flex flex-col gap-6 rounded-xl border bg-card py-6 text-card-foreground shadow-sm"
       CardHeader  = "@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start
                      gap-2 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6"
       CardContent = "px-6"
       CardFooter  = "flex items-center px-6 [.border-t]:pt-6"

   ★★關鍵：`px-6` 是**只有左右**。CardHeader / CardContent / CardFooter
     **三者都沒有任何垂直內距**（除非帶 .border-b / .border-t 才補 pb-6 / pt-6）。
     垂直節奏是**卡片自己給的**：`Card{ py-6 }`（上下 24px）＋ `Card{ gap-6 }`
     （header/content/footer 三個 slot 之間 24px），子元素一律不給。
   ⇒ 「CardHeader 的 padding 應該是 24px（四邊）」這個值在 shadcn 裡**根本不存在**。
     拿它來「修」本專案，是拿一個誤讀的數字去改一個已經一致的東西。

   ═══ 那要不要改成「真正的官方模型」？——不要，而且理由跟原本 §S-83 的不一樣 ═══
   真正對齊官方＝把卡片改成 `.card{ padding: 24px 0; gap: 24px }`
   ＋ `.card-header/.card-body/.card-footer{ padding: 0 24px }`。那是**改盒模型**，
   不是改一個數字：2026-08-08 實測 35 頁（1440、亮/暗）共 67 顆 header、101 顆 body、
   9 顆 footer 全部要換一套內距來源，而且：
     · `.p-0`（實測 body 15 顆、footer 7 顆）是「滿版表格」版型的成套 opt-out，
       改用 gap 供給垂直間距之後，這些卡的 gap 會憑空多出 24px，滿版反而被推開。
     · `.p-md-4`（實測 body 8 顆，＝24px）是 §S-6x 挑選卡刻意設計，會被新規則洗掉。
   ⇒ blast radius 遠大於題目本身。

   ═══ 「那至少把橫向收成官方的 24px？」——技術上會裂成兩種值，比現在更糟 ═══
   Bootstrap 的 `.p-3` 是 `padding: 1rem !important`。主題若加一條不帶 !important 的
   橫向 24px，**帶 .p-3 的 header 仍是 16px、沒帶的變 24px** ⇒ 同一種卡片標題又出現
   兩種離邊距離，正好是 D19 當初在抱怨的那件事原地復發。
   要壓過就得帶 !important，那又會一併洗掉上面那三種**刻意**的 opt-out
   （`.p-0` / `.p-md-4` / `.py-5`），得寫成
   `:not(.p-0):not(.p-md-4):not(.py-5)` 這種三重排除的規則 —— 而付出這些之後，
   垂直方向**依然**不是官方的樣子（官方 header 垂直是 0）。代價高、結果還是不對。

   ⇒ **維持 §S-83 的裁定：卡片三段預設 1rem，不改 24px。**
     ★本段的價值在於：下一輪再有人拿「官方是 24px」來開票時，
       答案不是「工程量太大所以不做」，而是**那個 24px 讀錯了**。
   ★2026-08-08 實測補充 §S-83 的第五節：35 頁掃描 67 顆 .card-header
     **內距 100% 是 16px 單一值**（亮暗逐頁相同），§S-83 收斂後的狀態維持住了；
     另 `.fb-filter-card` 實測 **0 顆**，再次確認它是死選擇器（§S-83 第一節的結論成立）。 */

/* --- S-84. 群 ② 收尾：先安置三段「頁面專屬追加」，五支 inline <style> 才拿得掉（2026-08-08）---
   業主：「shadcn 也要完整套用」「治本 ssot」。接 §S-80／§S-80a／§S-81 的待辦「群 ②」。

   §S-81 已經把**核心兩條**（表單列、`label.col-form-label`）收進主題，但當時只拿掉了
   319 字那一版 5 支裡的 4 支。剩下 7 支拿不掉的原因不在核心，而在
   **三種版本各自在核心後面追加了一段與表單層無關的頁面專屬修正**：
   把整段 <style> 一次刪掉，會連同那三個修正一起消失 ＝ 三個各自獨立的視覺回歸。
   ⇒ 本節先把那三段各自安置好，那五支的 <style> 才拿得掉。順序不能顛倒。

   ═══ 零、先量「這三段追加影響多廣」——這決定了它們能不能搬進主題 ═══
   ★★本節唯一真正的風險是：把頁面層規則搬到主題層，等於讓它對**全站**生效。
     只要目標元素在別的頁面也存在，那些頁面就會被順手改掉 —— 沒人要求、也沒人看過。
   所以先靜態掃全站 blade（不是抽樣、不是憑印象）：
     · `.btn-inline-upload`        命中 **3 支 blade** ＝ 正好就是帶這段追加的那 3 支
     · `<td>` 寫在 `<thead>` 裡    命中 **2 支 blade** ＝ 正好就是 2 支 darkaEdit（各 9 顆，共 18）
     · `.input-group-prepend`      命中 **1 支 blade** ＝ 正好就是 settingTokenIndex（4 處）
   ⇒ 三段追加的目標在**其他頁面一顆都不存在**。搬進主題的外溢不是「風險低」，
     是**集合為空**。先量出這件事，才敢搬；沒量之前這三段只能留在頁面裡。
   ★瀏覽器端複驗（https://test.2025-seller.mtest，1440，5 頁）：
     `.btn-inline-upload` 全站 15 顆（3 頁 × 5 顆），`closest('.app-content')`
     **15/15 非 null** ⇒ 主題選擇器加 `.app-content` 前綴一顆都不會漏掉。

   ═══ 一、追加段 A：上傳鈕（779 字那版，3 支）→ 原封搬進本節 ═══
   來源：hr/user/informationEdit、hr/user/create、employee/user/informationEdit 的
   `.card-body .btn.btn-inline-upload`。**宣告一字未改**，只加了 `.app-content` 前綴
   （house 慣例：把買家端／登入頁／POS 收銀擋在作用域外；上面已實測 15/15 都在 .app-content 內）。
   ★這顆鈕的 markup 不是這三支 blade 寫的，是
     `public/assets/js/file-uploader/template/container-inline.tpl` 這個 JS 樣板產生的
     （`class="btn btn-dark btn-sm w-100 btn-inline-upload"`）。
     ⇒ 規則本來就**該**待在主題層：任何未來用到 file-uploader inline 版的頁面，
       現在天生就吃得到，不必再複製一次 <style>。這正是「治本 ssot」要的方向。
   ★特異度：本條 (0,4,0) > `.btn-dark` (0,1,0)，與原本 inline 的 (0,3,0) 同樣贏得過，
     搬家不改變勝負關係。實測 A（inline）↔ B（本節）兩態，這 15 顆的
     背景／字色／邊框色／border-style 逐位相同。

   ═══ 二、追加段 B：表頭寫成 <td>（486 字那版，2 支）→ **本節不加 CSS，改 markup td→th** ═══
   原追加段是 `.app-content .card .table > thead > tr > td { font-size:12px; font-weight:500;
   color:muted-foreground; letter-spacing:.01em; vertical-align:middle }`
   ——把「寫成 td 的表頭」手工補成表頭的長相。

   ★★**不把它搬進主題**，理由是它已經過期而且會製造第二套表頭：
     它寫死 `font-size:12px`，那是 §補位 7（L5903）當年的值；**§S-64（L12928）已經把
     `thead > tr > th` 改成 14px**。把 12px 搬進主題 ＝ 全站從此有兩種表頭
     （真 th 14px、假 td 12px），正是 §S-80 整輪在消滅的那個病。
   ★★也**不寫一條 `thead > tr > td` 去鏡射 th 的值**：那要把 th 的一整組值複製一份，
     以後改 th 就得記得改兩處 —— 又一個 SSOT 破口，只是換個位置。

   ⇒ 真正的根因在 markup：表頭儲存格被寫成 `<td>`。把它改回 `<th>`，
     **一條 CSS 都不必加**，這 9 顆自動吃到主題現有（與未來）所有表頭規則。
   ★安全性先查過才動：
     · 那 9 顆是純文字表頭（日期／星期／狀態／上班／下班／加班／請假／遲到／早退），
       **沒有 input、沒有 name/id/value** ⇒ 動不到任何表單資料。
     · 該 blade 的 script 區段對 `thead` 的引用數 **0**（grep 實測）⇒ 沒有 JS 依賴。
     · `<th>` 的 UA 預設 `text-align:center` 由 Bootstrap reboot 的 `th{text-align:inherit}`
       拉回 ⇒ 不會出現「表頭置中、資料列靠左」。（實測值見下表）
   ★這不是刪除：欄位數、標籤文字、屬性全部原封不動，只換元素種類。

   ★★代價據實寫在這裡（實測，employee/user/darkaEdit 亮色）：那 9 顆表頭
     `font-size 12→14px`、`line-height 18→21px`、`letter-spacing .12→.14px`、
     左右內距 `8→12px`、**表頭列高 34.5→40.69px**。
     ⇒ 這張出勤表的表頭會比現在大一號。**這是有意識的決定不是無感差異**：
       全站其他表頭本來就是 14px，收斂後是「終於一致」，不是「變大了」。

   ★只動 employee/user/darkaEdit 這一支。hr/user/darkaEdit 同樣有 9 顆，**沒有動**
     ——那個路由是 500（見 §S-81：controller 回 `view('tenant.hr.user.darkaEdit')`，
     專案沒有 `tenant` 這個命名空間），改了無法在真實網址驗證。列入待辦。

   ═══ 三、追加段 C：BS4 遺留 `.input-group-prepend`（509 字那版，1 支）→ 原封搬進本節 ═══
   來源：hr/user/settingTokenIndex。原因（原註解已寫對，這裡不重寫只確認）：
   Bootstrap 4 的 `.input-group-prepend` 容器在 BS5 已取消，但 markup 還留著，於是
   `.input-group-text` 的父層變成它而不是 `.input-group.input-group-sm`：
     ① 接不到 L5870 `.app-content .input-group-sm > .input-group-text`（32px，**直接子選擇器**）
     ② 反而命中 L7903 `:not(.input-group-sm):not(.input-group-lg) > .input-group-text{min-height:36px}`
   `.input-group` 是 align-items:stretch ⇒ 輸入框與「複製網址」鈕被這顆前綴一起撐到 36px。

   ★**選擇器與宣告一字未改**（原本就帶 `.app-content` 前綴），純粹換位置。
   ★★所以這條**是 no-op，不要期待它產生任何視覺變化** —— 實測該頁唯一那顆前綴，
     inline 態與本節態的 `min-height/padding/font-size/border-radius/height/背景/字色/邊框`
     **12 個屬性一字不差**。搬它的價值只有一個：SSOT（規則從此有一個家，不是躺在某支 blade 裡）。
     ★把 no-op 據實寫出來，是因為「搬完看起來沒變」很容易被誤讀成「搬失敗了」。
   ★真正的根因是 markup 那層多餘的 BS4 容器，拿掉它才是治本；但那是刪除既有 markup，
     本輪守則禁止 ⇒ 維持 CSS 補救，列入待辦。

   ═══ 四、五支頁面拿掉 <style> 之後的實測差異（同一次載入內切換狀態，1440，亮＋暗）═══
   ★★量法：**同一次載入內切換**，不是「改檔前跑一輪、改檔後跑一輪」。
     因為有其他 session 同時在改共用元件，跨時間比對會讓每個 key 都對不上、整份作廢。
     狀態 A ＝ inline 仍在；狀態 B ＝ 停用 inline ＋ 補 `.fb-form-table` ＋ 注入本節規則。

     頁面                              A→B 樣式項 / 幾何項    表單列高 A→B
     hr/user/settingTokenIndex              52 / 117        49.59 → 51.19
     employee/user/darkaEdit               402 / 1158       49.59 → 51.19
     hr/user/informationEdit               258 / 510        49.59 → 51.19
     hr/user/create                        312 / 504        49.59 → 51.19
     employee/user/informationEdit         306 / 453        49.59 → 51.19

   ★列高一律 **+1.6px**，與 §S-81 記載的完全一致（inline 的 `.55rem` 換成 L966 的 `.6rem`）。
     這是 §S-81 已經拍板的取捨：取「螢幕上已經在跑的那一份」＝ 16 支已收斂頁面的 9.6px，
     不為了 5 支去動 16 支已經正確的頁面。本節沿用，不重新開議。
   ★★**欄位清單零差異**：5 頁共 181 個 `input/select/textarea` 的
     `name/id/value/type/checked` 在 A、B 兩態 `JSON.stringify` 全等。
     td→th 那 9 顆也沒有動到任何表單值（它們本來就沒有 input）。

   ═══ 五、正對照：證明上面那些數字不是偵測器壞掉湊出來的 ═══
   ★★★**0 與「偵測器根本沒在看」長得一模一樣**，所以每一頁都做了正對照：
     在狀態 B 之上，把所有 stylesheet 裡 selectorText 含 `fb-form-table` 的規則
     全部 `deleteRule`（在 shadcn-theme.css 裡找到並刪掉 15 條），再量一次。
     settingToken 60/120、darkaEdit 468/655、hr-info 300/526、create 364/523、emp-info 403/478
     （樣式/幾何，亮暗同值）—— **沒有任何一頁是 0**，而且變動方向正好是 B 的逆向
     （padding 0→7px、border-bottom 0→1px、line-height 32→21px、列高 51.19→60.78）。
   ★暗色不是只等固定毫秒：切 `html.dark` 後反覆量色彩指紋，**連續 3 次全等**才開始正式量
     （亮色 3 輪穩定、暗色 6 輪）。A 亮 vs A 暗 有 1400–2500 項色彩差異 ⇒ 暗色確實生效。
   ★一份跨來源 sheet（jsdelivr 的 select2 主題）讀不到 cssRules（SecurityError），
     已用探針確認它與 `fb-form-table` 無關 ⇒ 正對照沒有漏刪。據實記在這裡。

   ═══ 六、順手撿到、但**沒有**在本節處理的 ═══
   · hr/user/create 有 2 顆元素底色 `rgba(245,245,245,.6)→透明`。**這不是本節造成的回歸，
     是 §S-54 ① 早就拍板的修正生效**：表單型表格的勾選框是「欄位的值」不是「選取列」，
     列表頁的選取灰底外溢到表單上，業主看到的就是「那列莫名的灰底」。
     §S-54 ① 只針對 `.fb-form-table` 關掉，列表頁的選取提示完全不受影響。
     ⇒ 那 2 顆會變透明，是**掛上 class 之後才終於吃到這個修正**，方向正確。
   · `hr/user/darkaEdit`、`hr/user/salaryEdit` 兩支路由 500，**本節完全沒有碰**。
     它們的 inline <style> 原樣留著（頁面打不開，那段 CSS 從來沒有機會生效）。
   · §S-24 的 `padding:.5rem` 仍然是死規則（輸給 L966 的 (0,3,3)）—— §S-80 ③(b)、§S-81
     都已記載，本節一樣不修：要修得提高特異度，會一次動到全部 16 支已正確的頁面。
   -------------------------------------------------------------------------- */

/* ① 追加段 A：上傳鈕改虛線 ghost。原文來自 3 支 blade 的 `.card-body .btn.btn-inline-upload`，
      宣告一字未改，只加 `.app-content` 前綴（實測 15/15 顆都在 .app-content 內）。
      shadcn 的檔案上傳是「一塊等著被填的虛線區」，不是實心主鈕 —— 大頭照／身分證正反／
      存摺正反共 5 張卡，每張一顆滿版實心黑鈕，會讓整頁最重的五塊黑色落在「附件」這種
      次要區塊，比真正的主要動作（儲存）還搶眼。 */
.app-content .card-body .btn.btn-inline-upload {
    --bs-btn-bg: transparent;
    --bs-btn-color: hsl(var(--muted-foreground));
    --bs-btn-border-color: hsl(var(--border));
    --bs-btn-hover-bg: hsl(var(--accent));
    --bs-btn-hover-color: hsl(var(--accent-foreground));
    --bs-btn-hover-border-color: hsl(var(--border));
    --bs-btn-active-bg: hsl(var(--accent));
    --bs-btn-active-color: hsl(var(--accent-foreground));
    --bs-btn-active-border-color: hsl(var(--border));
    border-style: dashed;
}

/* ② 追加段 C：BS4 遺留 `.input-group-prepend` 讓前綴接不到 -sm 尺寸，整組被撐成 36px。
      選擇器與宣告**一字未改**，純換位置（原本就帶 .app-content 前綴）。
      尺寸值照抄上游 `.input-group-sm > .input-group-text` 那條。
      ★要湊到 5 個 class 才贏得過 L7903 那條 (0,4,0)。
      ★★本條是 no-op（實測 12 個屬性一字不差），價值只在 SSOT，不要期待視覺變化。 */
.app-content .input-group.input-group-sm .input-group-prepend > .input-group-text {
    min-height: 32px;
    padding: .25rem .5rem;
    font-size: .875rem;
    border-radius: var(--bs-border-radius-sm);
}

/* ③ 追加段 B **刻意沒有對應的 CSS 規則** —— 治法是把 markup 的 `<td>` 改回 `<th>`，
      讓那 9 顆自動吃到主題現有的表頭規則。理由與實測見上面「二、追加段 B」。
      ★記在這裡是因為「找不到對應規則」很容易被下一個人誤判成「漏做了」。 */

/* --- S-85. 群 ② 完結：最後 2 支（hr/user 的 salaryEdit／darkaEdit）收斂（2026-08-08）------
   接 §S-80a 的遷移台帳與 §S-81／§S-84。業主：「修啊 你做完檢查好每一頁」「確保功能正常」。

   ★★★這一節**刻意不含任何 CSS 規則**。整輪新增規則數 = 0。
     記在這裡是因為「S-85 底下找不到選擇器」很容易被下一個人誤判成「漏做了」，
     跟 §S-84 三結尾的理由一樣。這兩支需要的東西主題早就有了，缺的只是把 markup 接上去。

   ═══ 一、為什麼這 2 支拖到最後才收 ═══
   不是難度問題，是**這兩頁先前根本打不開**：
   `app/Http/Controllers/Hr/UserController.php` 的 darkaEdit()／salaryEdit() 兩個 action
   把 view 名寫成 `tenant.hr.user.*`，但本專案**沒有 tenant 這個 view 命名空間或目錄**
   ⇒ FileViewFinder 直接丟 InvalidArgumentException，兩頁長期 500。
   同一支 controller 其他 5 個 action（index／create／settingEdit／informationEdit／
   settingTokenIndex）全部寫 `hr.user.*`，只有這兩行多了前綴 ⇒ 單純打錯字。
   view 名修正後兩頁實測 200、console 錯誤 0，才有辦法量測與驗收。

   ═══ 二、salaryEdit：純掛 .fb-form-table，零 CSS ═══
   本頁 2 張表單表（「基本資料」「薪資統計」）原本靠頁內 <style> 的
   `.app-content .card .table:not(:has(> thead))` 認人。那是「用有沒有表頭去猜這張表是不是表單」
   的隱性判準 —— 同一份意圖在主題裡已經有顯性的寫法 `.fb-form-table`（§S-24）。
   改成在 markup 明講，頁內那段 1992 位元組的 <style> 就整段不需要了。
   同頁另外 2 張真資料表（發薪明細 20 欄、明細 modal 3 欄）本來就是 <th>，不受影響。

   ═══ 三、darkaEdit：3 張表單表掛 .fb-form-table ＋ 表頭 <td> 改回 <th> ═══
   本頁的 <style> 比 salaryEdit 多一段「追加段 B」：選擇器
   `.app-content .card .table > thead > tr > td`，宣告 font-size:12px、font-weight:500、
   color:muted-foreground、letter-spacing:.01em、vertical-align:middle。
   那是給「記錄」表用的補救 —— 那張表的 9 顆表頭被寫成 <td> 而不是 <th>，
   吃不到主題的表頭規則，所以頁面自己補了一套。**那是第二套表頭實作。**
   ★治法不是把它搬進主題，是修根因：把 9 顆 <td> 改回 <th>，
     自動吃到主題現有與未來的所有表頭規則，一條 CSS 都不必加。
   ★做法與同版本、上一輪已收的 `employee/user/darkaEdit` 逐字一致。
   ★已知代價（前一輪就量過、本輪複驗一致）：那 9 顆表頭
     字級 12px → 14px、表頭列高 34.5px → 40.69px。
     那是「本頁自訂表頭」回歸「全站表頭」的差，是刻意的，不是回歸。

   ═══ 四、兩支的 <style> 原文都整段留在各自 Blade 註解裡存查 ═══
   禁止刪除守則。搬進註解前用程式掃過（salaryEdit 1992 位元組／darkaEdit 2593 位元組）：
   沒有提前關閉序列、沒有巢狀 Blade 註解起始、沒有字面元件標籤，且掃過整檔零控制字元。
   ★收工複驗以「<style> 整段必須落在註解範圍內」為斷言 —— 不是只掃 CSS 本體有沒有危險字串。
     理由：本輪第一次改就是**前言結尾多打了一組關閉序列**，註解在 <style> 之前就關掉了，
     CSS 還活著、還會多吐一段字面文字出來；而「只掃 CSS 本體」的檢查完全看不到這件事，
     因為出問題的是我自己新寫的前言，不是被存查的原文。守門要涵蓋自己新增的部分。

   ═══ 五、群 ② 到此收完 ═══
   §S-80a 台帳裡「群 ② 未動 / 11 支收到剩 2 支」的狀態到本節結清：11 支全數收斂。
   ★台帳原文一字未改（只能檔尾新增），現況以本節為準。 */

/* --- S-86. POS 兩頁（/pos/package 撿包・/pos/machine 包裝機）：側欄頁首脫離 --dark-panel
       ＋ 觸控目標補到現場規格（2026-08-08）------------------------------------------
     ★本節錨點寫法必須是 `/* --- S-86. …`（S- 前面不可以多 §，見 §S-46 的說明）。
     業主：「你的 POS 介面請參考賣賣 WORK」「主要還是用 shadcn 去設計」「重構設計，不要動到功能」。

   ── 依據的優先序（衝突時聽誰的）────────────────────────────────────────────────
     ① 元件語彙（Card / Badge / Button / Input 的結構與 class 字面）→ **shadcn 官方**
     ② token（顏色、圓角、陰影、間距階）→ 本檔既有（§S-72／§S-74 已整組對齊官方 achromatic）
     ③ 官方沒有規定、而 POS 現場需要的（觸控尺寸）→ **賣賣 Work(OS)**，並在下方寫明偏離理由
   ────────────────────────────────────────────────────────────────────────────

   ═══ 一、`.pos-sidebar-header` 不再吃 `--dark-panel` ═══

   ── 病徵（像素證據，不是讀 computed）──
     暗色下 `.pos-sidebar-header` 與側欄本體**完全同色**。實測（1440×900，
     x = 側欄右緣 −6px，逐列取樣真實截圖像素）：
       y=17..62  srgb(23,23,23)   ← 頁首
       y=63      srgb(55,55,55)   ← 唯一的分界（1px 下框線）
       y=64..70  srgb(23,23,23)   ← 頁籤區
     computed 對比 `.pos-sidebar-header` vs `.pos-sidebar` = **1.0000**（精確 1，不是約等於）。
     根因：`--dark-panel` 是本檔唯一刻意「不隨主題翻轉」的表面色，而暗色 `--card`
     在 §S-74 之後也是 `0 0% 9%` —— 兩顆逐字同值（:root 那份警告已寫過這件事）。
     ★亮色下同一條規則卻是 17.93:1（近黑塊配白側欄）。也就是說**同一個設計在兩個主題
       講兩句相反的話**：亮色說「這是一塊獨立識別列」，暗色說「這裡什麼都沒有」。
       要修的是這個不對稱，不是只把數字調高。

   ── 官方怎麼做（查證過的原字面，不是印象）──
     · `card.tsx` `Card`：`flex flex-col gap-6 rounded-xl border bg-card py-6 text-card-foreground shadow-sm`
     · `card.tsx` `CardHeader`：`… grid auto-rows-min grid-rows-[auto_auto] items-start gap-2 px-6
        has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6`
       ⇒ **官方的卡片頁首沒有底色**，分層靠 `border-b` ＋ `CardTitle` 的 `leading-none font-semibold`。
     · `card.tsx` `CardAction`：`col-start-2 row-span-2 row-start-1 self-start justify-self-end`
       ⇒ 頁首右側掛一顆動作/徽章是官方支援的槽位（我方 `#user-login-total` 就是這個角色）。
     · 官方**沒有任何**「恆深識別列」元件；`--dark-panel` 是本專案自創的 token。
     ★`sidebar.tsx`（官方 Sidebar 的 `SidebarHeader`）**不在 tokens 目錄，查不到**，
       所以本節不引用它、也不假裝知道它長什麼樣。下面的取捨完全由 `card.tsx` ＋ `table.tsx` 推得。

   ── 為什麼選 `--muted` 而不是 `--card` / `--accent`（三個都量過）──
     實測生效值：
       --card    亮 rgb(255,255,255) / 暗 rgb(23,23,23)
       --muted   亮 rgb(245,245,245) / 暗 rgb(38,38,38)
       --accent  亮 rgb(245,245,245) / 暗 rgb(64,64,64)
       --border  亮 rgb(229,229,229) / 暗 rgba(255,255,255,0.10)
     · **不用 `--card`**：側欄本體就是 `--card`，兩者同值 ⇒ 亮色也會變成 1.0000。
       那是把暗色的病複製到亮色，不是治病。（官方 CardHeader 之所以可以「無底色」，
       是因為官方那張卡片本身就浮在 `--background` 上、四周有 `border`＋`shadow-sm`；
       我方這條頁首是嵌在側欄**內部**、上下都是同一張表面，沒有那圈邊界可借。）
     · **不用 `--accent`**：查官方 `.tsx` 裡 `bg-accent` 的每一處用法 ——
       `button.tsx` ghost/outline 的 `hover:bg-accent`、`badge.tsx` 的 `[a&]:hover:bg-accent`、
       `dialog.tsx` 的 `data-[state=open]:bg-accent`、`select.tsx` 的 focus 態 ——
       **全部是「互動狀態」**，沒有一處拿它當靜態表面。拿 accent 當底色是誤用語意。
     · **用 `--muted`**：官方 `table.tsx` `TableFooter` = `border-t bg-muted/50 font-medium`，
       這就是「一條摘要／識別帶：次要表面 ＋ 一條邊 ＋ 稍重字重」的官方原型，
       與 `.pos-sidebar-header`（員工名 ＋ 登入時數徽章）的角色一致。
     結果（實測）：亮 245 on 255、暗 38 on 23，**兩個主題都是「同一種弱對比 ＋ 一條 --border」**，
     不再一邊 17.93 一邊 1.0000。★這裡刻意**不追求高對比數字**：官方的分層手段本來就是
     hairline，硬把它做成高對比色塊反而離官方更遠。要修的是不對稱，已經修掉。

   ── 連帶必須一起改的四條（不改就會製造新的 1.0000）──
     舊值用的是 `--dark-panel-foreground`（0 0% 98%，**兩模式同值的恆亮前景**）。
     底色一旦從近黑換成 muted，這些近白的東西在亮色下會整個消失：
       文字色、下框線、返回鈕底、返回鈕圖示色 —— 四條全部換成會翻轉的 token。
     ★`--border` 在暗色是 `0 0% 100% / 10%`（**自帶 alpha**）。本節只寫 `hsl(var(--border))`，
       **絕對不可以**再寫成 `hsl(var(--border) / .5)` —— 那會展開成雙斜線語法錯誤、整條被丟棄
       （§S-72 記過這個坑）。 */
.pos .pos-sidebar .pos-sidebar-header {
  background: hsl(var(--muted));
  color: hsl(var(--foreground));
  border-bottom-color: hsl(var(--border));
}
.pos .pos-sidebar .pos-sidebar-header .back-btn {
  background: hsl(var(--border));
}
.pos .pos-sidebar .pos-sidebar-header .back-btn .btn {
  color: hsl(var(--foreground));
}
.pos .pos-sidebar .pos-sidebar-header .back-btn .btn:hover {
  background: hsl(var(--accent));
  color: hsl(var(--accent-foreground));
}

/* 頁首徽章（`#user-login-total`）：圓角回到官方 Badge 的 `rounded-full`。
   官方 `badge.tsx` 原字面：`inline-flex w-fit shrink-0 items-center justify-center gap-1
   overflow-hidden rounded-full border border-transparent px-2 py-0.5 text-xs font-medium …`
     · `rounded-full`         → 這裡改（原本是 --radius-sm，與官方不同階）
     · `border border-transparent` → 這裡補（官方盒模型含 1px 透明邊，補上才不會與其他徽章差 2px）
     · `px-2 py-0.5` = 8px/2px → 本檔既有值已相同，不動
     · `text-xs`(12px) / `font-medium`(500) → **刻意不照官方**：POS 是現場畫面，
       §POS-10 的既有裁示是「字要夠大」，把 13px/600 降成 12px/500 與該裁示相反。
       這屬於「官方沒有規定的 POS 場景例外」那一格，維持現值並在此記錄。
   ★`bg-primary`＝金黃是業主已裁示保留的品牌色（shadcn 的模型本來就是骨架給你、品牌色自帶），
     不動。亮色下黃底對 muted 只有約 1.34:1 —— 這是全站所有 primary 徽章共同的既有取捨，
     不是本節新造的缺陷，也**不自己發明一圈官方沒有的描邊**去補（那才是偏離 shadcn）。 */
.pos .pos-sidebar .pos-sidebar-header .order {
  border: 1px solid transparent;
  border-radius: 9999px;
}

/* ═══ 二、觸控目標補到現場規格 ═══

   ── 這裡**刻意偏離 shadcn 官方**，理由寫在下面 ──
     官方 `button.tsx` 的尺寸階是給桌機滑鼠用的：
       default `h-9`(36px) / sm `h-8`(32) / lg `h-10`(40) / xs `h-6`(24) / icon `size-9`(36)
     官方 `input.tsx` 是 `h-9`(36px)。
     POS 是倉庫現場的觸控螢幕，作業員**戴手套、隔一段距離看**，36px 按不準。
     賣賣 Work(OS) 是同一個業務場景的既有實踐，原始碼註解寫明了這個理由，其階梯是：
       掃碼輸入框與主送出鈕 48px（`ScanPanel.vue:89,98`）／主要動作 44px／次要動作 36px
     ⇒ 本節採 OS 的 48/44，**只在 `.pos` 與 `.pos-manual-input-modal` 兩個範圍內生效**，
       不外洩到全站（全站仍是官方的 36/32/24 階）。

   ── (a) 主要動作 40 → 44px ──
     §POS-10 原本訂 40px（既不是官方階、也不是 OS 階，是中間值）。改成 OS 的 44。
     ★只增不減：`.btn-xs` 維持排除在外（見下）。 */
.pos .btn:not(.btn-close):not(.btn-xs) { min-height: 44px; }

/* ── (b) 「點我手動輸入」：實測只有 17px ──
     `.not-available-text` 裡那顆 `<a class="fw-normal fs-14px">` 是**沒有掃描槍時唯一的
     手動輸入入口**（§notAvailableText 的註解已記載它曾經連文字都被 lucide 吃掉）。
     它不是 `.btn`，所以完全沒被任何高度規則收到 —— 實測 `getBoundingClientRect().height`
     = **17px**（不是讀 CSS 宣告）。這是本頁最嚴重的觸控缺陷，比按鈕 40 vs 44 嚴重得多。
     ★用 `inline-flex` 而不是 `display:contents` / `block`：
       `display:contents` 會讓 `offsetParent` 恆為 null，本專案踩過（舊 JS 會把 UI 整個
       `display:none`）；`block` 會把它撐成整行、破壞置中。`inline-flex` 只長高、不改流向。
     ★文字、class（`fw-normal fs-14px`）、popover 屬性一個字都沒動，只加尺寸。 */
.pos .not-available-text a {
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .375rem;
  padding: 0 .75rem;
}

/* ── (c) 側欄「查詢包裹」的條碼輸入 ＋ 搜尋鈕 → 48px ──
     `#search`（textarea，placeholder「輸入條碼號碼，多組可用 , 分隔」）與 `#search-btn`
     是這一頁**唯一在版面上常駐的條碼輸入口**，對應 OS `ScanPanel.vue` 的 48px 那一階。
     兩顆同在一個 `.input-group` 裡，必須一起改，否則高低不齊。
     ★`#search` / `#search-btn` 是 JS 直接讀的識別碼（`$('#search-btn').click`、
       `$('#search').val()`），這裡**只加樣式、沒有改名**。 */
.pos .pos-sidebar #search,
.pos .pos-sidebar #search-btn { min-height: 48px; }

/* ── (d) 4 支手動輸入 modal → 48px（掃碼輸入框 ＋ 主送出鈕）──
     這 4 支 modal 寫在 `#pos` **之外**，所以 `.pos …` 的尺寸規則對它們完全不生效，
     它們吃的是全站 modal 階梯（實測 input 36px / 送出鈕 36px / addon 36px）。
     blade 那邊上一輪已經留好 `pos-manual-input-modal` 鉤子並把規則列進交付清單，這裡補上。
     ★特異度是量出來的，不是猜的：上游兩條分別是
         `.modal :not(.input-group-sm):not(.input-group-lg) > .btn:not()×5`
         `.app-content :not(.input-group-sm):not(.input-group-lg) > .form-control:not()×6`
       都是 9 個 class 級選擇器。這裡照抄同一條鏈、把最前面換成 `.pos-manual-input-modal`，
       特異度相同、位置更後 ⇒ 由來源順序取勝。
     ★`height: auto` 不可省：上游那條 `.form-control` 是 `height:36px` **和**
       `min-height:36px` 一起釘的，只提 `min-height` 的話 `height` 仍然把它壓在 36px
       —— 這正是「寫了 ≠ 生效」那一族的坑。
     ★`.btn-close` 不動（官方 DialogClose 是 `size-4` 的圖示鈕，不是觸控主目標）。 */
.pos-manual-input-modal :not(.input-group-sm):not(.input-group-lg) > .form-control:not(textarea):not(.form-control-sm):not(.form-control-lg):not(.form-control-color):not([type="file"]):not([type="color"]),
.pos-manual-input-modal :not(.input-group-sm):not(.input-group-lg) > .input-group-text,
.pos-manual-input-modal :not(.input-group-sm):not(.input-group-lg) > .btn:not(.btn-sm):not(.btn-xs):not(.btn-lg):not(.btn-icon):not(.btn-close) {
  min-height: 48px;
  height: auto;
}

/* ═══ 三、查證過但**這一輪沒有做**的事（留給下一輪，不要當成漏做） ═══

   (1) `.pos .btn.btn-xs { min-height: 26px }`（本檔 L5837）是**死宣告**。
       實測 computed `min-height` = 24px —— 被 `.app-content .btn.btn-xs:not(.btn-icon)`
       （L7947，特異度 (0,4,0) > (0,3,0) 且更後面）壓掉。
       ★**刻意不修**：24px 正好等於官方 `button.tsx` 的 `xs: "h-6"`（24px），
         也就是「生效值是對的、宣告值是錯的」。這些 `span.btn.btn-xs` 是唯讀狀態 chip
         （開始/完成時間），不是觸控目標，把它拉高只會讓卡片變高而沒有好處。
         原宣告一個字都沒刪，只在這裡記錄它不生效。

   (2) STEP 卡的排列**沒有改成 grid**。
       OS `Package.vue:1042` 的原字面是 `repeat(auto-fit, minmax(185px, 1fr))` / `gap: 1rem`，
       我方目前是 `div.product-row .product-container` 的百分比斷點（50/33.33/25%）。
       ★不照抄 185px 的理由：我方 STEP 卡裡有長按鈕列（「點我完成全部 N 個揀貨」單顆就
         超過 185px 寬），auto-fit 到 185px 會在 1440 排出 5 欄、每欄卡片內部橫向溢出。
         OS 的 185px 是照 OS 自己的卡片內容訂的，字面搬過來不成立。
       ★另一個理由：`div.product-row` 不只 POS 在用（`Center/inventoryStats/index.blade.php`
         也吃這條，本檔 L4711 已註明），動它會外溢到 POS 以外。
       要做的話應該是「量我方卡片的實際最小可用寬度，再訂自己的 minmax」，那是獨立一輪。

   (3) 側欄頁籤（`.fb-tabs`）的底色**沒有動**。
       官方 `TabsList` 是 `bg-muted`；我方 `.pos-sidebar-nav` 實測是 `--card` 色。
       §S-67 已裁示頁籤完全照官方，但 `.fb-tabs` 是全站共用元件（不屬本輪檔案所有權），
       而且本節剛把它上方的頁首改成 muted —— 兩塊同時 muted 會再糊在一起。
       這兩件事必須同一輪一起設計，本輪不單邊動它。

   (4) 手動輸入 modal 的**字級**沒有跟著 48px 一起放大（input 仍是 14px）。
       OS 的 48px 伴隨較大字級，但字級是獨立的視覺決策、會連動 placeholder 與 addon 寬度，
       本輪只動尺寸不動字級，列入下一輪。

   (5) `--dark-panel` 這顆 token **本身沒有動**。它還有其他使用者
       （`.swal2-container` 的遮罩、CA gray-800 對應等），本輪只把 POS 側欄頁首從它身上拔掉。
       :root 的那份警告仍然有效、一個字都沒刪。 */

/* --- S-87. 字級工具類的最後兩階脫離官方階梯：`.fs-13px` / `.fs-15px` → text-sm(14px)（2026-08-08）---
   ★這一行是給 tests/Feature/Theme/ThemeOverrideAnnotationsTest 認的「章節錨點」，
     寫法必須是 `/* --- S-87. …`（S- 前面不可以多 §，見 §S-46 的說明）。

   業主定調「都要依照 shadcn 為主」「治本 SSOT」。接 .agent/TODO.md 的
   「`fs-XXpx` 散在 19 支檔」，以及 §S-80 第五項那張已經訂好、但當時**刻意沒執行**的對照表。

   ═══ 一、先更正題目：需要動的不是 19 支檔，是**兩個宣告** ═══
   2026-08-08 全站實測（35 頁 × 亮/暗，1440，走 https://test.2025-seller.mtest），
   `fs-XXpx` 執行期命中 **153 個節點**，逐一讀 computed（不是讀 class 名）：

     class        節點   宣告值   **實際 computed**   在不在 Tailwind 階梯上
     ────────────────────────────────────────────────────────────────────
     .fs-11px      76     11px    **12px**            ✅ text-xs   ← §S-5 抬上去的
     .fs-9px       28      9px    **12px**            ✅ text-xs   ← §S-5 抬上去的
     .fs-10px       2     10px    **12px**            ✅ text-xs   ← §S-5 抬上去的
     ★上面三列的行尾**刻意避開**本檔那個「機器可讀的覆蓋註解」句型
       （就是 ThemeOverrideAnnotationsTest 在解析的那個：「已」「被」兩字連寫
        ＋ §章節名 ＋「覆蓋」）。那是**保留寫法**，不是普通中文：
       只要在任何一行註解裡出現，測試就會把它當成一筆「宣稱」去比對真實串接勝出者，
       章節名對不上、或後面沒接「覆蓋」二字，就直接報紅。
     ★★這是我自己踩到的，而且踩了兩次：
         第一次 —— 三列行尾用了那個句型（「…§S-5 抬上去」但前面帶了保留字），三條紅；
         第二次 —— 我把踩坑經過寫進註解時**照原樣引述了那個句型**，連同一個
                   當佔位符的假章節名，於是註解自己又變成兩筆待驗宣稱，再兩條紅。
       ⇒ 教訓：在這個檔案裡**連「引用」那個句型都會被當真**。要提它就像本段這樣拆開寫。
     .fs-12px       1     12px      12px             ✅ text-xs
     .fs-14px      13     14px      14px             ✅ text-sm
     .fs-18px       6     18px      18px             ✅ text-lg（6 顆全在 POS）
     .fs-13px      26     13px      13px             ❌ **官方階梯沒有 13**
     .fs-15px       1     15px      15px             ❌ **官方階梯沒有 15**

   ★★所以「19 支檔」這個數字會嚇到人，但其中 **106 個節點（fs-9/10/11px）早就已經是
     12px** —— §S-5「字級下限 12px」在 2026-07-30 就把它們抬上官方階了。
     真正還脫隊的只有 `.fs-13px`（26 節點）與 `.fs-15px`（1 節點）。
   ★連帶推翻 §S-80 第五項當時的風險評估：那裡寫「fs-11px 有 21 次、大量落在 pos/*，
     11→12px 會動到版面」——**它們已經是 12px 了，這個轉換早就發生過**，
     而且本次實測 fs-11px 落在 POS 的節點數是 **0**。那筆風險是虛驚。
   ★`.fs-30px` 不在本節範圍：它**不是主題的 class**，是
     `resources/views/AiTool/alpha/index.blade.php:12` 頁面自己 <style> 裡的宣告，
     而且 30px 本來就對齊 text-3xl。主題這邊沒有 `.fs-30px` 可改。

   ═══ 二、為什麼 13px 現在「非改不可」——是 §S-64 把它變成落單的 ═══
   `.fs-13px` 原本有正當性：舊規格 §4 明訂 td = 13px，這顆工具類是給
   「表格外但想跟表格內文同級」的次要文字用的（`components/batch-bar.blade.php:73`
   的註解就是這樣寫的：「全站表格內文/次要資訊的 13px 都由那一條供給」）。

   **但 §S-64（2026-08-07，業主拍板「全站表格統一吃 shadcn」）已經把 th/td 改成 14px。**
   實測 4 頁（new-orders / inventory-product / hr-user / store-setting-store）td 一律 14px。
   ⇒ `.fs-13px` 從那天起就不再「跟表格同級」，反而**比它旁邊的表格小 1px**。
     最清楚的證據是批次工具列：`/order-management/status/new-orders` 的
     「已選 0 筆」（`.text-muted.fs-13px`）量到 13px，而同一張卡裡其他文字節點
     **14px 有 24 個**。⇒ 13px 現在是這張卡裡唯一落單的字級。
   ★也就是說本節不是「把 Color Admin 遺產清掉」，是**把 §S-64 開的口收掉**。

   ═══ 三、★推翻一條看起來會擋路的「刻意偏離」註解（差點讓我不敢改）═══
   `resources/views/StoreSetting/_picker_card.blade.php:273` 寫著：
     「★字級取 13px（fs-13px）不是官方的 14px：下方長說明已經是 14px muted，
       兩段同級同色會讀成同一段。差一階之後『副標 → 說明』的層次才成立。」
   這看起來是一條有理由、不該被輾過的刻意設計。**但它已經過時了**：
     · 靜態：該檔 `fs-13px` 只出現 3 次，**3 次全部都在 Blade 註解裡**。
       同檔 L757 自己就記著「★★2026-08-07：字級 fs-13px → fs-14px，對齊官方
       CardDescription 的 text-sm」——那顆節點在第四輪已經換成 fs-14px 了。
     · 執行期：`/store-setting/store/create` 量到 `.sc-picker-card` **8 顆**、
       其中 `.fs-13px` 活節點 **0 顆**。
   ⇒ 那段「刻意 13px」描述的 markup **已經不存在**。不改的理由不成立。
   ★★這件事值得記下來：**過時的「刻意」註解比沒有註解更危險** ——
     它會讓下一個人以為有人想過、不敢動，於是缺陷永遠留著。
     判準是「靜態命中 ≠ 活節點」：一定要回瀏覽器數活的那一顆。
   ★該 blade 註解本輪**沒有動**（另一支代理正在改 blade，不在本代理檔案所有權內）。

   ═══ 四、為什麼是 14px 而不是 12px ═══
   官方階梯只有 text-xs(12) 與 text-sm(14)，13 落在正中間、兩邊等距，不能用「就近」決定。
   取 14 的依據是**用途**，不是距離：
     · 26 個 `.fs-13px` 節點全部是「說明文字 / 次要內文 / 清單項」，
       對應官方的 CardDescription、AlertDescription、FormDescription ——
       這三者在 shadcn 裡**都是 `text-sm`**（不是 text-xs）。
     · 它們的鄰居就是 14px：批次列同卡 14px×24、方案卡內文 13px×5 配 16px 標題。
       收到 14 是「跟鄰居對齊」，收到 12 會製造新的落差。
   `.fs-15px` 同理併入：全站唯一 1 顆，是
   `Seller/plan/subscription/index.blade.php:406` 的 `#confirmModalPlanInfo`
   （`class="fw-bold mb-2 fs-15px"`，JS 注入內容），正下方 L407 是
   `<ul class="mb-0 ps-3 fs-13px">`。兩者一起變 14px 之後，標題與清單**同級不同重**
   （`fw-bold` 保留）—— 這正是官方 AlertTitle(`font-medium`) 配
   AlertDescription(`text-sm`) 的作法：靠字重分層，不靠 1px 的字級差。

   ═══ 五、為什麼**全域**、不加 `.app-content` 前綴 ═══
   本檔的作用域慣例（§S-83 / §S-84）確實是加 `.app-content` 把買家端／登入頁／POS 擋在外面。
   **這一條刻意不遵守那個慣例**，理由是它的性質不同：那些是「版面規則」，
   本條是「字級階梯」—— 一個叫 `.fs-13px` 的工具類在不同區域算出不同大小，
   本身就是新的 SSOT 破口。§S-5（唯一的同類先例，把 9/10/11px 抬到 12px）
   也是全域無前綴，本條與它保持一致。
   ★★**我知道這會連帶動到 `.app-content` 以外的一處**，寫在這裡以免下一個人以為是漏網：
     `resources/views/auth/sso-login.blade.php` 的
     `<div class="alert alert-danger fs-13px mb-20px">`（SSO 登入失敗訊息）
     在登入殼層裡、不在 `.app-content` 內，它會跟著變成 14px。
     **這是有意識的決定，不是疏漏**：① 13→14 只有 1px，錯誤訊息本來就該好讀，無版面風險
     ② 登入殼層雖有自己的字級規格（§S-3 label 12px/500），但那是**針對 label**，
       沒有針對 alert 的規定 ③ 留一個例外，等於留一個「為什麼這裡不一樣」的坑給下一個人。

   ═══ 六、特異度與為什麼要 !important ═══
   原宣告 L3054 `.fs-13px{font-size:13px!important}`、L3056 `.fs-15px{font-size:15px!important}`
   都是 (0,1,0) **帶 !important**。本條同為 (0,1,0)，
   ⇒ 必須也帶 !important 才進得了同一個「important 層」比序，
     進去之後同特異度由**檔內順序**決勝，本條在檔尾 ⇒ 勝出。
   ★這與 §S-5 壓過 L3048-3052 的機制**完全相同**（同特異度 + 同 important + 排在後面）。
   ★**原宣告一個字都沒有動**（禁止刪除守則）：留著才看得到「13px 曾經是規格、何時被誰換掉」。
     §S-5 當初也是這樣處理的，並在原宣告上方加「★已被 §S-5 覆蓋」的標記。

   ═══ 七、改後實測（35 頁 × 亮/暗 = 70 組，與 out/before.json 逐項比對）═══
     · `.fs-13px` 26 節點：13px → **14px**（26/26，亮暗各一致）
     · `.fs-15px`  1 節點：15px → **14px**
     · 其餘 126 個 fs-XXpx 節點：**computed 逐位不變**
     · 卡片三段內距 177 顆、控制項高度 134 顆：**逐位不變**（本節不碰它們）
     · 新增水平捲軸 **0**、元素溢出容器 **0**
   ★正對照（在 after 之上注入 `.fs-13px{font-size:28px!important}`）：
     偵測器回報 26 個節點字級變動、並連帶抓到高度/位移變化
     ⇒ 證明「其餘 126 個沒變」不是偵測器沒在看。 */
.fs-13px,
.fs-15px {
    /* 原值：`.fs-13px{13px}`（L3054）、`.fs-15px{15px}`（L3056），兩者都仍在檔案裡，記此存查。
       改為 .875rem = 14px = Tailwind text-sm = shadcn CardDescription / AlertDescription 的字級。 */
    font-size: .875rem !important;
}

/* ═══════════════════════════════════════════════════════════════════════
   §S-70（2026-08-09）：「主要動作靠左」改用**語意標記**判定，不再只認顏色
   ───────────────────────────────────────────────────────────────────────
   ★業主指令（2026-08-09，在 /invoice/main/modify/1）：
       「作廢發票 手動重開按鈕 應該要遵守 ssot 主要按鈕放左邊 不重要的按鈕放右邊」

   ═══ 一、為什麼現行 §S-69 判不出這一頁 ═══
   §S-69（L13667）的選擇器是 `:has(> .fb-ph-actions .btn-primary)` ——
   它拿「動作組裡有沒有 btn-primary（品牌黃）」當「這組重不重要」的**替身指標**。
   在「新增／儲存」類頁面上這個替身成立（黃鈕本來就只給新增/儲存，見 L411 註解），
   但 /invoice/main/modify 的 ISSUED 檢視態裡，該頁唯一的主要動作是
   「作廢發票」（btn-dark）—— 它不是新增也不是儲存，照規格**不該**塗成品牌黃，
   於是整組被判成「不重要」，被 §S-66（L13295）推到右邊。

   ★這是替身指標的典型失效：想讓一顆按鈕靠左，就得先把它塗黃。
     版面（左右）與顏色（重要性）本來是兩件事，被綁在同一個 class 上。

   ═══ 二、為什麼不是把 §S-69 改成「一律靠左」═══
   ★★查過 git log，這件事業主在 2026-08-07 當天**連續拍板兩次**：
       ① 5a0c0f07（08-04）動作組一律靠左
       ② 7ca0eb23（08-07 10:18）業主推翻①：「說明跟動作就是放在右上角」→ 一律靠右
       ③ 0b37d8a3（08-07 12:06）業主再推翻②：「新增訂單按鈕 重要按鈕都要置左
          不重要的 說明 更新時間 才放右邊」→ 現行的「重要才靠左」
     ⇒「一律靠左」與「一律靠右」都已被業主明確否決過，本節**不重蹈覆轍**：
       維持「重要才靠左」這個判準不變，只是換一個更準的方式去問「重不重要」。

   ═══ 三、做法：新增 `[data-ph-primary]` 語意標記 ═══
   呼叫端在該頁的主要動作按鈕上標 `data-ph-primary`，即宣告「這組含主要動作」。
   顏色維持該按鈕原本該有的語意（作廢＝btn-dark，不必為了靠左而塗黃）。

   ═══ 四、為什麼是新增一條，而不是去改 §S-69 那條選擇器 ═══
   ★§S-69 一個字都沒動（禁止改寫既有規則的守則）：
     現行 64 支靠 btn-primary 判定靠左的頁面，走的仍然是原來那條，行為逐位不變。
   本條特異度與 §S-69 同為 (0,5,0)，同 important 層，**排在檔尾 ⇒ 同分時本條勝出**；
   但兩條的宣告值完全相同（order:1 / margin-left:0），所以就算兩條同時命中
   （一顆黃鈕 + 一顆標記鈕），結果也是同一個值，不存在互相打架的可能。

   ═══ 五、影響面（動手前已量測）═══
   全站 107 支 x-page-header 呼叫端中，目前 0 支帶 `data-ph-primary`
   ⇒ 本條在加上標記之前對所有現存頁面**零影響**，是純追加。
   ═══════════════════════════════════════════════════════════════════════ */
/* ★已被 §S-111 併入（本檔尾，2026-08-10）：靠左已經是**全站預設**，
   本條與 §S-111 宣告值逐字相同 ⇒ 已標 [data-ph-primary] 的呼叫端
   （Center/reportPackage/index、Invoice/Main/edit）行為逐位不變，不必回頭拆標記。
   ★原文一字未改、未刪。★新頁面**不需要**再標這個屬性了。 */
.page-header.d-flex:has(> .fb-ph-actions [data-ph-primary]) > .fb-ph-actions {
    /* 與 §S-69 同值：order 1 ＝ 標題（order 0）之後、填充位（order 90）之前 */
    order: 1;
    /* 壓住 §S-66 L13295 的 `margin-left:auto !important`（375 折行時它會真的生效） */
    margin-left: 0 !important;
}

/* --- S-88. 設計語彙脫離 `.app-content` 外殼：補一份無前綴宣告（2026-08-09）--------
   業主：「發票資訊詳細頁 應該可以文字小字一點」「排版太大了」——
        追下去發現不是那一頁的問題，是**整批設計語彙被關在後台外殼裡**。

   ═══ 一、破口：意圖是全站，實作卻鍵在容器上 ═══
   §S-64 的註解白紙黑字寫著業主拍板「**全站**表格統一吃 shadcn」「這是規格變更，由業主拍板」，
   §S-63 也寫著「把字級鍵在 `.form-label` **本身**，不再鍵在容器上 —— 這才是 SSOT」。
   但兩節的實作都帶了 `.app-content` 前綴，而 `.app-content` 是**後台外殼**
   （`layouts/default.blade.php:110` 的 `<div id="content" class="app-content">`）。
   買家端（`buyer/layout.blade.php`）、登入頁、錯誤頁都沒有這層外殼 ⇒ 一條都吃不到。

   ★量出來的差距（1440px，改動前，注入式探針逐項讀 getComputedStyle）：
       元素                     後台（.app-content 內）        公開頁（無外殼）
       .table > thead > tr > th 14px / 9.6px 12px / 透明底 /   16px / 8px / rgb(245,245,245) 灰底
                                rgb(115,115,115)
       .table > tbody > tr > td 14px / 9.6px 12px             16px / 8px
       .form-label              14px / 500                    16px / 500
       .small ・ <small>         12px                          14px
       .card-header/.card-footer padding 16px                  padding 8px 16px
       .invalid-feedback        12px / rgb(186,28,28)          14px / rgb(220,53,69)（白底 3.94:1，未過 AA）
     ⇒「公開頁每個字都比後台大 2px」是量得出來的，不是印象。

   ═══ 二、為什麼是「全域無前綴」，而且不需要發明新規範 ═══
   §S-87（本檔上方）已經把這個判準寫死過一次：
     「那些是**版面規則**，本條是**字級階梯** —— 一個叫 `.fs-13px` 的工具類
       在不同區域算出不同大小，本身就是新的 SSOT 破口。」
   「表格 th 在不同區域算出不同大小」與它同病同治。§S-5（把 9/10/11px 抬到 12px）
   與 §S-87 是本檔僅有的兩個同類先例，兩條都是全域無前綴，本節與它們一致。

   ★對照：§S-83 第四節寫的「`.app-content` 前綴是刻意把外面擋住」講的是**版面規則**
     （卡片內距在滿版表格版型下要 opt-out），那個判斷本身沒有錯；錯的是把
     「字級／字重／表頭語彙」也一起塞進同一個作用域裡。本節只搬設計語彙，不動版面規則。

   ═══ 三、★★刻意**不**納入的一組：控制項尺寸階梯（第二層另案）═══
   `.btn` / `.btn-sm` / `.btn-xs` / `.form-control` / `.form-control-sm` 的
   height / min-height / padding（§S-4 L7930/7954/7963/7978、§filterbar L5856/5894/5906/5907、
   §S-84 L16352）**一條都沒有搬**。
   ★理由是量到的、不是保守：公開頁的控制項現在是 **40px**，而後台那批是 32/36px。
     40px 不是漏網，是 `includes/_public_head.blade.php` 明文寫下的「觸控下限」
     （買家掃 QRCode 進來、登入頁、金流頁三處共用同一份判斷）。
     拿掉前綴 = 把 40px 打回 36px = 打壞觸控目標。
   ⇒ 尺寸階梯是**版面規則**（跟著使用情境走），字級與顏色才是**設計語彙**（全站唯一）。
     這條界線就是本節收與不收的判準。

   ═══ 四、作法與特異度（逐條算過，寫在每條旁邊）═══
   ★既有條文一字未刪、未改寫。本節是**純追加**，靠「同 important 層 + 檔內順序在後」
     或「特異度較高」勝出，與 §S-5 / §S-87 的機制相同。
   ★★關鍵不變量：**每一條無前綴宣告的每一個屬性，都有一條 `.app-content …` 的
     同屬性宣告、特異度嚴格更高**（前綴讓每條多一個 class = +1 個 b 值）。
     ⇒ `.app-content` 內（後台 127 支 blade **與 POS**）的計算值在數學上不可能改變，
       只有外殼之外的頁面會吃到本節。這不是「應該不會變」，是特異度算得出來的。
   ★POS 的說明見下面 §S-83 訂正段：POS 的 `#content` **就掛著 `.app-content`**
     （`layouts/pos.blade.php:29`），所以它一直都吃得到那批規則 ⇒ 本節對 POS 零影響。

   ═══ 五、改後實測（Puppeteer 逐節點 computed style 前後比對，走 https://test.2025-seller.mtest
        與 https://inv.2025-seller.mtest；23 個頁面情境含 1440 / 375 / 亮 / 暗）═══
     · 後台 6 頁（/main、/order-management/status/new-orders、/hr/user、/center/report、
       /inventory/product、/invoice/main）：`.app-content` **內**逐元素 diff = **0**
     · POS 2 頁（/pos/package、/pos/machine）：逐元素 diff = **0**（含 `.pos-*` 觸控尺寸）
     · 登入頁（/login，亮＋暗）、錯誤頁（404）：逐元素 diff = **0**
       —— 登入頁三顆控制項仍是 40px（帳號 / 密碼 input `min-height:40px`、
          登入鈕 `min-height:40px`），本節根本沒有宣告控制項尺寸，
          而且 `.login .form-control`(0,2,0) 本來就贏過任何無前綴宣告。
     · 金流頁（gateway-result / gateway-customer 的 3 顆 `.btn`）：
       **48.5px / 15px / `12px 28px`，前後逐位不變**。
       ★這兩支無法用 GET 直接打（需要真實付款流程），量法是把該頁真正會載入的四層
         CSS（bootstrap → 主題 → `includes/_public_head` → `payment/_shell_css`）
         在 https://api.2025-seller.mtest 這個真實 origin 上重建成等價文件再量。
       ★另有靜態佐證：`gateway.blade.php` / `gateway-customer` / `gateway-result` /
         `error.blade.php` 四支的 class 全集裡**沒有** `.table` / `.card-header` /
         `.card-footer` / `.form-label` / `.small` / `<small>` / `.invalid-feedback`
         —— 本節對金流頁的接觸面是空集合。
     · 買家 5 支（landing / query / by-invoice / result / reissue）：
         th / td …………… 16px → **14px**、padding `8px` → **`9.6px 12px`**
         表頭底 ………… rgb(255,255,255)（頁面自己那行）→ **transparent**（透出 --card，等價）
         表頭字距 ……… -0.176px → **0.14px**（`letter-spacing:.01em`，與後台同值）
         .form-label …… 16px → **14px**
         .small ………… 14px（卡外）／12.25px（卡內）→ **12px（單一值）**
         .card-header / .card-footer …… `8px 16px` → **`16px`**
       ★`.small` 那兩個值就是本節要收掉的漂移：`.875em` 疊在 `.container-wide .card{14px}`
         上是 12.25px、疊在繼承的 16px 上是 14px —— 同一個 class、同一頁、兩種大小。
     · 買家 h1.page-header：**20px 前後不變**（另一個 session 的
       `.site-main h1.page-header{1.25rem}`(0,2,1) 仍然贏；本節沒有、也不該用 !important 去壓它）。
     · 375px：頁面水平溢出 **0 → 0**；買家發票明細表在 `.table-responsive` 內
       由「不需捲動」變成「橫捲 37px」（表格 325 → 378 寬，容器 341），
       **品名欄反而變寬 164.6 → 181px、列高仍是單行 41.2px，沒有爆版**。
       ★這是刻意讓 `.table > :not(caption) > * > *{font-size:.85rem}`(0,1,1) 那條手機縮字
         被壓掉的結果（業主裁示「shadcn 怎麼做就怎麼做」，官方 Table 是全尺寸統一 text-sm、
         靠外層 overflow 橫捲而不是縮字），不是回歸。
     · 暗色（買家 result）：th 14px / 透明底 / rgb(161,161,161)、
       `.invalid-feedback` = rgb(248,114,114)（--destructive-fg 暗色）—— token 正常翻轉。

   ★★★**唯一一處後台會變的節點，如實記在這裡**（不是 0，不要當成 0）：
     `#notification-view-all`（頂 bar 通知下拉裡的「查看全部通知」，
      `class="dropdown-item text-center small"`）**在 `#header` 內、不在 `.app-content` 內**
      ⇒ 14px → **12px**（line-height 21 → 18）。
     它是全後台**唯一**一個落在外殼之外的 `.small`；其餘同 class 節點早就是 12px。
     ⇒ 這一格是往「全站 .small 單一值」收斂，不是回歸；但它確實是一處變動，
       只在下拉選單展開時可見。已回報，未特別保護（保護它等於重新製造本節要消滅的例外）。

   ★★正對照（守門誘餌，2026-08-09 實跑）：在改後狀態上注入
     `.card-header{padding:40px!important}` / `.table>tbody>tr>td{font-size:28px!important}` /
     `.small{font-size:31px!important}` / `.invalid-feedback{color:rgb(1,2,3)!important}` /
     `.form-label{font-size:33px!important}`，偵測器對**全部 23 個頁面情境**都報出變動
     （POS 36 筆、登入頁 46 筆、404 頁 24 筆、後台 /hr/user 776 筆…）
     ⇒ 上面那些「diff = 0」是偵測器有在看的情況下量到的 0，不是假綠燈。
   -------------------------------------------------------------------------- */

/* ── (1) §7b 表格 shadcn 化：去直框 / 去斑馬，只留橫線 + 透明表頭 + row hover ──
   原條文在本檔 L953-979（`.app-content` 變體），此處是同一組值的無前綴複本。
   ★每條的特異度都比對應的 `.app-content` 變體少一個 class ⇒ 後台仍由變體決定。 */
.table { /* (0,1,0) vs BS `.table`(0,1,0) 同分、本節在後 ⇒ 勝 */
  --bs-table-bg: transparent;
  border-color: hsl(var(--border));
  margin-bottom: 0;
}
/* 去掉 table-bordered 的直向框線，只保留橫線 —— (0,2,1) */
.table.table-bordered > :not(caption) > * > * { border-inline-width: 0; border-block-color: hsl(var(--border)); }
/* (0,2,0) */
.table > :not(:first-child) { border-top: 0; }
/* 表頭：透明底 + muted 字 + 細下框 —— (0,1,3)
   ★贏得過誰，逐條算過：
     · BS `.table > :not(caption) > * > *`(0,1,1) 的 padding ⇒ 勝
     · 本檔基底 `.table > thead th`(0,1,2)（`background: --muted !important`）⇒ 本條
       同樣帶 !important 而特異度更高 ⇒ 勝（灰底因此在公開頁也退場）
     · 本檔 §23 `.table th`(0,1,1) ⇒ 勝
     · `buyer/layout.blade.php` 的 `.table > thead th{background:--card !important}`(0,1,2)
       ⇒ 本條 (0,1,3) 帶 !important、特異度更高 ⇒ 勝（值等價：透明底透出 --card 白）
     · `.app-content .table > thead > tr > th`(0,2,3) ⇒ **輸**，後台維持原狀（這是要的） */
.table > thead > tr > th {
  background: transparent !important; color: hsl(var(--muted-foreground)); font-weight: 500;
  border-bottom: 1px solid hsl(var(--border)); border-top: 0;
  padding: .6rem .75rem; white-space: nowrap; vertical-align: middle;
}
/* 內容列：去斑馬、舒適 padding、垂直置中、細橫線 —— (0,2,2) / (0,2,3) */
.table.table-striped > tbody > tr > * { --bs-table-bg-type: transparent; --bs-table-accent-bg: transparent; }
.table:not(.table-bordered) > tbody > tr > td { border-color: hsl(var(--border)); border-inline-width: 0; padding: .6rem .75rem; vertical-align: middle; }
/* `.table-bordered` 必須留直線（原條文註解已說明：日曆／月排班表的格線是資訊不是裝飾）—— (0,2,3) */
.table.table-bordered > tbody > tr > td,
.table.table-bordered > thead > tr > th {
    border: 1px solid hsl(var(--border));
    padding: .6rem .75rem;
    vertical-align: middle;
}
.table > tbody > tr:last-child > td { border-bottom: 0; }   /* (0,2,3) */
.table-hover > tbody > tr:hover > * { background: hsl(var(--muted)); color: hsl(var(--foreground)); }   /* (0,2,2) */

/* ── (2) §補位 7 的表頭「非字級」那半（color / font-weight / letter-spacing）──
   原條文在本檔 L5909（字級那半已由 §S-64 接手，見該處長註解）。
   letter-spacing 在上面 (1) 沒給，補在這裡；color / font-weight 上面已同值宣告，
   為了讓「原條文有幾個屬性、這裡就有幾個」對得起來，一併寫齊（同值，無競爭）。 */
.table > thead > tr > th {   /* (0,1,3) */
    font-weight: 500;
    color: hsl(var(--muted-foreground));
    letter-spacing: .01em;
}

/* ── (3) §S-64 全站表格字級吃 shadcn（th/td 14px）＋卡片標題字重 600 ──
   原條文在本檔 L12988-13007。註解原文：「業主：對 全站表格統一吃 shadcn」
   ——「全站」在這一節之前只做到了 `.app-content` 內，本節把它補齊。 */
.table > thead > tr > th { font-size: 14px; }   /* (0,1,3) 勝 BS 繼承的 16px */
.table > tbody > tr > td { font-size: 14px; }   /* (0,1,3) */
/* (0,2,0) + !important —— 需要 !important 的理由與原條文相同：要壓過 markup 上
   `class="card-header p-3 fw-bold"` 的 `.fw-bold`（BS utility，font-weight:700!important，(0,1,0)）。
   ★`.app-content .card > .card-header`(0,3,0) 同樣帶 !important ⇒ 後台仍由它決定。 */
.card > .card-header { font-weight: 600 !important; }

/* ── (4) §S-63 `.form-label` 字級統一到 shadcn 標準（14px / 500）──
   原條文在本檔 L12953。註解原文：「把字級鍵在 `.form-label` 本身，不再鍵在容器上，
   這才是 SSOT」——本節讓這句話對公開頁也成立。
   ★(0,1,0)：勝 BS 的 `.form-label`(0,1,0)（同分、在後）與本檔 L771 的同名條（同上）；
     **輸給** `.login .form-label`(0,2,0)（L1445，登入頁 .82rem）與
     `.login.login-v2 .form-label`(0,3,0)（auth shell）⇒ 登入頁字級不變，
     這是 §S-63 註解裡「刻意不動登入頁」那條裁示的延續，不是漏網。 */
.form-label {
    font-size: 14px;
    font-weight: 500;
}

/* ── (5) §S-83 卡片三段內距預設 1rem ──
   原條文在本檔 L16156。★刻意**不帶** `!important`，與原條文同一個設計：
     沒寫 `p-*` 的 → 吃本條 1rem；寫了 `.p-3` 的 → 同值；寫了 `.p-0` 的 → 保留 0
     （「滿版表格」版型的成套 opt-out，原條文第三節有完整量測理由）。
   ★(0,1,0) 勝 BS `.card-header{padding:.5rem 1rem}`(0,1,0)（同分、在後）；
     輸給 `.app-content .card-header`(0,2,0) 與 `buyer/layout` 的
     `.container-wide .card-header{padding:1rem}`(0,2,0)（值相同，無視覺差）。 */
.card-header,
.card-footer {
    padding: 1rem;
}

/* ── (6) `.small` / `<small>` 固定 12px（絕對值，不是 .875em）──
   原條文在本檔 L5946（`.app-content` 變體），註解已寫明病灶：
   `.875em` 是**相對值**，父層字級一變它就漂。
   ★這一輪實測到的新證據（買家發票頁，同一個 `.small.text-muted`）：
       卡片內 …… 12.25px（BS `.small{.875em}` × `buyer/layout` 的 `.container-wide .card{14px}`）
       卡片外 …… 14px  （.875em × 繼承的 16px）
     同一個 class、同一頁、兩種大小 —— 正是 §S-87 說的「工具類在不同區域算出不同大小」。
     補絕對值 12px 一次收掉。
   ★`.small`(0,1,0) 與 `small`(0,0,1) 都在 BS 同名條之後 ⇒ 勝；
     `.app-content .small`(0,2,0) / `.app-content small`(0,1,1) ⇒ 後台不變。 */
.small,
small { font-size: 12px; }

/* ── (7) ★無障礙修正：`.invalid-feedback` 走 --destructive-fg ──
   原條文在本檔 L8875-8884（`.app-content` 變體）。
   ★這一條不是美觀問題：公開頁掉回 Bootstrap 出廠的 `#dc3545`＝rgb(220,53,69)，
     壓在白底實測 **3.94:1**，低於 WCAG AA 的 4.5。
     `--destructive-fg`（亮 #BA1B1B / 暗 #F87171）就是為了「紅**字**」拆出來的 token
     —— `--destructive` 是**底色**用的，見 §1 token 註解與 `.alert-danger` 的同一套用法。
   ★字級 12px 一併補齊：原條文的 `.invalid-feedback/.valid-feedback{font-size:12px;margin-top:4px}`
     與上面 (6) 的 `.small` 是同一階（shadcn text-xs），拆開會讓同一頁出現兩種小字。
   ★(0,1,0)：**輸給** auth shell 的
     `.login.login-v2 .login-content .invalid-feedback`(0,4,0)（13px / `--destructive`）
     ⇒ 登入頁不受本條影響，實測仍是 **rgb(231,0,11) / 13px**。
     ★但那**不是無障礙缺口**：rgb(231,0,11)（#E7000B ＝ --destructive 亮色）壓在白底
       實測對比 **4.77:1**，已過 WCAG AA 的 4.5（本檔 §S-39 的註解也記過同一個數字）。
       ⇒ 登入頁與本條的差異是「用哪一顆紅」的一致性問題，不是可讀性問題，
         所以更不該用 !important 去搶。那是登入頁自己 `<style>` 裡的既有宣告，
       不在本輪的檔案所有權內，也不該用 `!important` 去硬壓（會反過來破壞
       「頁面可以明講例外」這個本檔一貫的分層）。**已如實回報，留待另案。**
   ★`buyer/layout.blade.php:240` 早就自己補過同一行 color（「作用域補洞」），
     本節落地後那行變成同值重複宣告 —— 原文一字未動（禁刪守則），
     但它從此不再是唯一來源，日後可由業主決定要不要收掉。 */
.invalid-feedback,
.valid-feedback {
    font-size: 12px;
    margin-top: 4px;
}
.invalid-feedback { color: hsl(var(--destructive-fg)); }

/* --- S-89. 公開頁控制項尺寸階梯：五份頁面級拷貝收斂成單一來源（2026-08-09）--------
   接 §S-88「三、刻意不納入的一組：控制項尺寸階梯（第二層另案）」那條待辦。
   §S-88 收的是**設計語彙**（字級／字重／顏色，全站唯一值）；本節收的是**版面規則**
   （控制項尺寸階梯，跟著使用情境走：後台 36px、公開頁 40px）。
   兩者的界線就是 §S-88 第三節寫下的判準，本節沿用，一個字都沒有改。

   ═══ 一、破口：40px 是有意識的決定，但它有五份拷貝、而且已經漂開了 ═══
   `includes/_public_head.blade.php` 的檔頭自己就預言過：
     「每多一個公開頁就再抄一次，遲早會漂開」
   —— 2026-08-09 盤點，那句話已經應驗。同一個「公開頁控制項」概念散在五處：

     #  位置                                          .btn 實測高度
     1  includes/_public_head.blade.php:70-93          40px  （min-height:40 / 7px 16px / 14px / lh 24）
     2  auth/_auth_shell_css.blade.php:263-273         40px  （.form-control，height+min-height 都釘）
     3  auth/sso-account-prompt.blade.php:118-123      40px  （與 #1 **逐字相同**的又一份）
     4  payment/_shell_css.blade.php:220-236           48.5px（padding 12px 28px / 15px）★第三種值
     5  payment/gateway-result.blade.php:44-50         更高  （padding 14px 36px / 16px）★第五種值

   ★#4 / #5 是**金流頁既有的視覺**，不是漏網也不是錯誤，只是不在 32/36/40 三階上。
     把它們統一到 40px 是**行為變更**（按鈕會明顯變小），必須由業主拍板 ——
     本節因此**刻意設計成不會碰到它們**（見下面第三節的特異度算式），
     那兩處只在原檔加註記、值一個字都沒動。已回報，留待另案。

   ═══ 二、作用域為什麼是 `.fb-public` 這個共用 class ═══
   ★不用「拿掉 `.app-content` 前綴」（＝ §S-88 的做法）：那會把後台的 36px 打成 40px。
     控制項尺寸是版面規則，兩個區域**本來就該不同值**，全域無前綴在這裡是錯的解。
   ★不用 `body:not(:has(.app-content))`：`:has()` 要對整棵樹做反向匹配，
     而且「有沒有那層外殼」是**推論**出來的作用域 —— 讀 CSS 的人無從得知它涵蓋哪些頁。
     一個掛在 `<body>` 上、grep 得到的 class 是明講的，不是猜的。
   ★`.fb-` 前綴沿用本檔既有慣例（`.fb-ph-actions` / `.fb-row-action` / `.fb-login-brand`）。
   ★★關鍵不變量：`.fb-public` **只加在公開頁殼層的 `<body>`**，
     後台 `layouts/default.blade.php` 與 POS `layouts/pos.blade.php` 的 `<body>` 一個字都沒動
     ⇒ 本節的每一條選擇器在後台／POS 的 DOM 上**匹配不到任何元素**。
     這不是「應該不會影響」，是選擇器層級就不可能命中。

   ═══ 三、★★為什麼用 `:where(.fb-public)` 而不是 `.fb-public` ═══
   這是本節最要緊的一行設計，理由是**算出來的**：
   被收斂的那些宣告原本都寫在頁面的 `<style>` 裡，特異度是 **(0,1,0)**（`.btn` / `.form-control`）。
   如果這裡寫成 `.fb-public .btn`，特異度會變 **(0,2,0)** —— 是**提高**，於是會反過來壓贏：
       · `.btn.btn-icon`(0,2,0)（本檔 L3138，28×28 圓形圖示鈕）
         —— 同分但本節在後 ⇒ `layouts/empty.blade.php` 底部那顆
            `.btn.btn-icon.btn-circle.btn-scroll-to-top` 會被撐成 40px 的方塊。
       · `payment/_shell_css` 的 `.btn`(0,1,0) 與 `gateway-result` 的 `.btn`(0,1,0)
         —— 金流頁那兩種值會被強制收成 40px ＝ 上面第一節說的、不在授權範圍內的行為變更。
   `:where()` 的參數對特異度貢獻 **0**，所以 `:where(.fb-public) .btn` 仍是 **(0,1,0)**
   —— 與它取代掉的那些頁面級宣告**完全同分**。
   ⇒ 疊加順序退回「誰在後誰贏」，而本檔是由 `<link>` 載入、排在所有頁面 `<style>` **之前**
     ⇒ 頁面想例外，照樣蓋得過（#4 / #5 因此原封不動），
       頁面沒宣告的，就吃本節這一份唯一來源。
   ★這正是 `includes/_public_head` 檔頭原本寫的那句「本檔在各頁自己的 `<style>` 之前，
     所以個別頁面仍蓋得過這裡」—— 本節把同一個層級關係原樣搬進主題，沒有改變它。
   ★`:where()` / `:is()` 本檔已有 3 處先例，不是本節引進的新語法。

   ═══ 四、值的來源 ═══
   取 `includes/_public_head.blade.php:70-93` 那一份：它是最完整（btn / btn-sm / form-control
   ＋手機 16px 全都有）、也是被最多頁面 include 的一份（買家 7 支、SSO / 錯誤 4 支、金流 4 支、
   sso-demo 2 支）。值**逐位元組照抄**，含每一行算式註解。

   ═══ 五、40px 的理由（原文保留，出自 `_public_head` 檔頭）═══
   · 公開頁是「使用者在手機上掃 QRCode 進來」的情境，40px 是觸控下限。
   · 主題早就替同性質的公開頁（登入頁，同樣在 `.app-content` 之外）訂了同一階 ——
     `.login .form-control{min-height:40px}` / `.login .btn-dark{min-height:40px}`。
   · padding 用「湊足 40」的算式而不是靠 min-height 補差：min-height 只會往下墊，
     文字不會置中（38→40 會多 2px 在下緣）。
   ⇒ 這三句話原本在五個檔案裡各寫一次，現在只在這裡寫一次。 */

/* (a) 未帶尺寸修飾的按鈕 → 40px（spec §0 的 lg 階）。
   ★★`:where(:not(.btn-icon):not(.btn-close))` 這個守門是**量出來才補的**，不是照抄 §S-4：
     第一版沒有它，實測 `https://test.2025-seller.mtest/login` 未登入頁報出 2 筆差異，
     兩筆都在同一顆元素上 ——
       `<a class="btn btn-icon btn-circle btn-success btn-scroll-to-top">`
       （`layouts/empty.blade.php:33`，登入家族每一頁都有一顆）
       `min-height: 0px` → **40px**
     ★病因很細：本條靠 `:where()` 退回 (0,1,0)，`.btn.btn-icon`(0,2,0)（本檔 L3138）
       確實贏走了 padding / font-size / line-height ——
       **但 `.btn.btn-icon` 只宣告 `width:28px; height:28px`，它沒有 `min-height`**。
       沒有人跟本條搶，`min-height:40px` 就無條件生效，
       而 CSS 的 `min-height` 是**箝制** `height` 的：用值 = max(28, 40) = 40。
       ⇒ 那顆圓鈕會變成 28px 寬 × 40px 高的橢圓（`border-radius:28px` 撐不圓）。
       它平時 `display:none`（捲動才 `.show`），所以**截圖與 rect 高度都是 0**、
       看不出來 —— 這種只有逐屬性 computed style 比對才抓得到。
   ★為什麼守門寫成 `:where(:not(...))` 而不是 `:not(...)`：
     `:not()` 會把括號內的特異度算進去 ⇒ 本條變 (0,2,0)，就會反過來壓贏
     `.btn.btn-icon`(0,2,0)（同分、本節在後）與金流頁的 `.btn`(0,1,0)，
     一次破壞兩個本節刻意保住的東西。`:where()` 包起來則貢獻 0，維持 (0,1,0)。
   ★涵蓋範圍已 grep 確認：`.btn-icon` / `.btn-close` 在公開頁裡**只出現在登入家族**
     （auth 四支 + `layouts/empty.blade.php` 的捲動置頂鈕），
     買家 / 金流 / 錯誤頁 / sso-demo **0 命中** ⇒ 這個守門對它們是空集合、零影響（已實測 diff=0）。 */
:where(.fb-public) .btn:where(:not(.btn-icon):not(.btn-close)) {
    min-height: 40px;
    padding: 7px 16px;      /* 7 + 24(line-height) + 7 + 2(border) = 40 */
    font-size: 14px;        /* spec：控制項字級 default = 14px */
    line-height: 24px;
}
/* sm = 32px（spec：字級 13px、左右內距 10px） */
:where(.fb-public) .btn-sm {
    min-height: 32px;
    padding: 5px 10px;      /* 5 + 20 + 5 + 2 = 32 */
    font-size: 13px;
    line-height: 20px;
}
:where(.fb-public) .form-control {
    min-height: 40px;
    padding: 7px 12px;      /* spec：控制項左右內距 12px */
    font-size: 14px;
    line-height: 24px;
}
/* 手機 16px：iOS Safari 對 <16px 的輸入框會自動放大整頁（縮放後不會還原）。
   這是「防瀏覽器介入」不是設計字級，所以只加在手機斷點。 */
@media (max-width: 768px) {
    :where(.fb-public) .form-control { font-size: 16px; }
}

/* --- S-90. 買家「修改發票」選項卡（radio card）＋ 欄位收進選中的那張卡（2026-08-09）-----
   業主：「右邊這個 radio 選擇 下方出現相對應的輸入框 可以做現代一點嗎?
          shadcn 大概會怎麼設計這邊」／「你做看看」。
   對應 view：`buyer/invoice/_reissue_form.blade.php`（markup）
              `buyer/invoice/_reissue_form_script.blade.php`（載具區塊搬家）
   ★編號已定為 §S-90：本節與 §S-89（公開頁控制項尺寸階梯）是同日並行的兩輪，
     §S-89 已先落地本檔（L17032），本節依「接在最後一節之後」的約定編為 §S-90。

   ═══ 一、破口：8px 的小圓點，配上一個「離題」的輸入框 ═══
   改版前這四個選項是 Bootstrap 出廠的 `.form-check`：一顆 8px 小圓點 ＋ 一行字，
   真正可點的只有圓點與 label 那一行；而「手機條碼／自然人憑證／統編」的輸入框是
   **radio 群組下方的三個獨立兄弟元素**，選了哪一項、要填的欄位長在哪，
   全靠使用者自己把上下兩塊連起來。
   ★這頁的真實情境是**買家在店裡結完帳、用手機掃 QRCode 進來**（result 頁把表單內嵌到
     右欄，見 result.blade.php「開立統編 / 綁定載具」那張卡）——
     觸控目標與「選了就看到要填什麼」在這個情境下不是美觀問題，是能不能完成任務的問題。

   ═══ 二、做法：選項卡 ＋ progressive disclosure ═══
     ① 整張卡的標題列是一個 `<label>`（`.reissue-type-hit`），min-height 52px
        ⇒ 觸控目標遠大於 44px（WCAG 2.5.5 / Apple HIG 44pt）。
     ② 選中態＝邊框變色 ＋ 1px 外環（ring），不只是圓點被填滿。
     ③ **對應的輸入欄位收進被選中的那張卡內**（`.reissue-type-body` 插槽）。
   ★色票一顆都沒有新增：選中態用的 `--dark`（亮色近黑／暗色近白）就是本檔 §35 已經
     替 checked radio 訂好的那顆 —— §35 的註解寫得很清楚，金黃 `--primary` 配白點只有
     1.7:1「勾了跟沒勾一樣」，所以 checkbox/radio 一律走 `--dark`。
     選中的**卡框**與選中的**圓點**因此是同一個顏色，讀起來是同一件事。

   ═══ 三、`:has()` 只拿來做「錦上添花」，功能一律不押在它身上 ═══
   本節唯一用到 `:has()` 的地方是「選中卡的框色 / ring」與「焦點卡的框色」。
   ★欄位的顯示／隱藏**完全沒有改**，仍然是既有腳本直接讀寫區塊自己的 `.style.display`
     （view 端註解：這支檔在 7ec7ed40 才因為類似問題修過一次）。
   ★停用態（`.reissue-type-card--disabled`）由**伺服器端**依 `$isCurrent` 直接印出，
     不寫成 `:has(:disabled)` —— 停用卡牽涉到「（目前狀態，無法選擇）」那句的對比度，
     不能讓它取決於瀏覽器支不支援 `:has()`。
   ⇒ 萬一 `:has()` 不生效（Safari < 15.4 / Chrome < 105），使用者看到的只是「卡框沒變色」，
     而 radio 圓點仍然被填滿（§35 的 `:checked` 是純 CSS，任何瀏覽器都吃）、欄位仍然照常
     出現、表單仍然照常送出。**沒有任何功能會消失。**

   ═══ 四、停用卡刻意「不」加底色 —— 這是對比度的硬限制，不是漏做 ═══
   直覺會想給停用卡一層 `--muted`(#F5F5F5) 底。**不可以。**
   「（目前狀態，無法選擇）」那句吃的是 `.text-muted`(`--muted-foreground` #737373)，
   它在白底上是 4.74:1（2026-08-07 為了把它從 1.96:1 救回來，才特地把這個 span
   移出 `<label>`、避開 BS 的 `.form-check-input:disabled ~ .form-check-label{opacity:.5}`），
   一旦墊上 #F5F5F5 就掉到約 4.17:1，**等於把當初那次修正原地打回去**。
   ★WCAG 1.4.3 對「已停用控制項」有豁免，但那句是給人看的**說明文字**、不是控制項本身。
   ⇒ 停用外觀改用「虛線框 ＋ 標題轉灰 ＋ not-allowed 游標 ＋ 取消 hover 底」表達，
     卡片底色維持 `--card`，一個字的顏色都沒有動。
   ★同理，**選中卡也不加底色**：卡內收著 `#carrier_hint` / `#tax_id_hint` 兩條
     `.text-muted` 格式提示，墊底色會讓它們一起掉到門檻以下。
     hover 的 `--accent` 只上在標題列（`.reissue-type-hit`）—— 那一列只有前景色的標題，
     而且是短暫狀態，不影響任何 muted 文字。
   ★★★2026-08-09 後續：**停用卡現在有底色了**（業主：「目前狀態不是應該要黑框？或是黃框？」
     ／「已經選擇的差異要做出來」）。上面這一整段的**推論仍然成立、結論被取代**——
     取代的方式不是「無視對比度」，而是**連同文字色一起換掉**，
     所以第四節的原文一字未刪，請接著讀本節第六段（含實際算式）。

   ═══ 五、作用域：新 class、無前綴，刻意不掛 `.app-content` 也不掛 `.fb-public` ═══
   ★不能掛 `.app-content`：買家端沒有那層外殼（§S-83 / §S-88 都記過這件事），
     掛上去一條都吃不到。
   ★不掛 `.fb-public`：這幾顆 class 是本輪新造的、全站只有這一份表單在用，
     grep `reissue-type-` 就能窮舉；再加前綴只是讓選擇器變長，換不到任何隔離。
   ★與 §S-87 / §S-88 的「全域無前綴」先例一致。
   ------------------------------------------------------------------------------ */

.reissue-type-group {
    display: flex;
    flex-direction: column;
    gap: .5rem;
}

.reissue-type-card {
    border: 1px solid hsl(var(--border));
    border-radius: var(--radius-md);          /* 8px：選項列表用中階，卡片的 --radius-xl(14px) 在這個尺寸會太圓 */
    background: hsl(var(--card));
    overflow: hidden;                          /* 讓 hover 底色被卡片圓角裁掉，標題列本身就不必再寫一組 radius */
    transition: border-color .15s ease, box-shadow .15s ease;
}

/* 標題列＝可點區。整列都是 <label for="invoice_type_…">，不是只有那顆圓點。 */
.reissue-type-hit {
    display: flex;
    align-items: center;
    gap: .625rem;
    min-height: 52px;                          /* ≥ 44px 觸控目標（WCAG 2.5.5 / Apple HIG） */
    margin: 0;                                 /* 蓋掉可能存在的 label 預設下邊距 */
    padding: .75rem 1rem;
    cursor: pointer;
    font-weight: 400;                          /* 上方「變更為」是 .fw-semibold，選項本身維持一般字重 */
    user-select: none;                         /* 連點兩下不要選到文字（觸控裝置尤其明顯） */
    -webkit-tap-highlight-color: transparent;  /* 手機點擊時不要那層灰色閃底，卡框變色已是回饋 */
    transition: background-color .15s ease;
}

/* 原生 radio 保留可見：五態（未選 / hover / focus / checked / disabled）連暗色
   全部由本檔 §35 ＋ §S-73 負責，這裡只調尺寸與 flex 定位，不重畫任何一態。 */
.reissue-type-input {
    flex: 0 0 auto;
    width: 1rem;                               /* 釘 16px：BS 出廠是 1em，而本表單在 result 頁吃
                                                  `.container-wide .card{font-size:14px}`、在 reissue 獨立頁不吃
                                                  ⇒ 不釘死的話兩頁的圓點大小會不一樣 */
    height: 1rem;
    margin-top: 0;                             /* BS `.form-check-input{margin-top:.25em}` 是給非 flex 版面用的 */
}

.reissue-type-title {
    line-height: 1.4;
    color: hsl(var(--foreground));
}

/* hover：只上在標題列，且只給「可選」的卡 */
.reissue-type-card:not(.reissue-type-card--disabled) .reissue-type-hit:hover {
    background: hsl(var(--accent));
}

/* 選中態：邊框 ＋ 1px 外環。色票＝ §35 給 checked radio 的 --dark（亮近黑 / 暗近白）。
   ★box-shadow 畫在 overflow:hidden 的**外側**，不會被裁掉。 */
.reissue-type-card:has(.reissue-type-input:checked) {
    border-color: hsl(var(--dark));
    box-shadow: 0 0 0 1px hsl(var(--dark));
}

/* 鍵盤焦點：radio 自己的 3px `--ring` 環（§35 / §S-73）**仍然在**，這裡只再把卡框描深，
   讓「焦點目前落在哪一張卡」在小螢幕上一眼看得出來。
   ★刻意只動 border-color、不動 box-shadow：box-shadow 會與上面選中態的 ring 撞在
     同一個屬性上互相覆蓋，分開兩個屬性才能「已選中 ＋ 有焦點」同時看得到。
   ★放在選中態之後，讓焦點卡的框色贏過選中色（焦點是更即時的狀態）。 */
.reissue-type-card:has(.reissue-type-input:focus-visible) {
    border-color: hsl(var(--foreground));
}

/* 停用卡（＝目前發票狀態那一項）。底色維持 --card，理由見上方第四節。
   ★★2026-08-09 最終定案（第六節 (7)，業主看過視覺稿選定 B 案）之後，
     這裡的 `border-style: dashed` 是**唯一**被下面重新覆蓋的一項——B 案要求
     「目前卡」的邊框與一般卡完全相同（1px 實線 --border），所以下面只把
     border-style 撥回 solid；background／border-width／border-color 三項
     **完全沒有被覆蓋過**（第六節 (2)～(6) 的黃／黑版本都短暫覆蓋過，
     現在全部撤銷、回到本節這條原始宣告的樣子），實測生效值＝這裡的原文。
     `.reissue-type-title` 的顏色也一樣：下面完全沒有任何覆蓋，
     這一行的 `--muted-foreground` 就是實際生效值。 */
.reissue-type-card--disabled {
    border-style: dashed;  /* ★被下面第六節 (7) 撥回 solid，這裡的 dashed 不是生效值，原文保留存查 */
    cursor: not-allowed;
}
.reissue-type-card--disabled .reissue-type-hit { cursor: not-allowed; }
.reissue-type-card--disabled .reissue-type-title { color: hsl(var(--muted-foreground)); }

/* 「（目前狀態，無法選擇）」：獨佔一行，左緣對齊標題文字。
   ★`display:block` 讓它一定自己一行 —— 在 result 頁它坐在 col-lg-4 的窄欄裡，
     跟標題擠同一行必定折行，折了反而更難讀。
     （它是 `.reissue-type-card` 的子元素，而卡片本身是一般 block、不是 flex 容器，
      所以這裡用 display 而不是 flex-basis。）
   ★左內距 = 標題列左內距 1rem ＋ 圓點 1rem ＋ gap .625rem − 既有的 .ms-1(.25rem)。
     `.ms-1` 是這個 span 原本就有的 class，禁刪守則下保留，所以這裡把它扣掉。 */
.reissue-type-note {
    display: block;
    padding: 0 1rem .75rem calc(1rem + 1rem + .625rem - .25rem);
}
/* 有這句說明時，標題列的下留白讓給它，避免上下各留一份（否則卡片會虛胖約 12px）。
   ★同時解除 52px 的最小高度：那個值是為「觸控目標」訂的，而這張卡點不下去，
     留著只會讓文字在 52px 的框裡偏下 4px，並且把一張**不能選**的卡撐得跟能選的一樣高。 */
.reissue-type-card--disabled .reissue-type-hit {
    min-height: auto;
    padding-bottom: .25rem;
}

/* 插槽：永遠存在、永遠 display:block，只有左右內距、沒有上下內距
   ⇒ 裡面沒有可見內容時高度為 0，四張卡看起來都只有標題列。
   顯示／隱藏由腳本操作**區塊自己**的 .style.display，插槽不參與。 */
.reissue-type-body {
    padding: 0 1rem;
}

/* 收進卡內的欄位區塊（#block_carrier / #block_triple）。
   ★負的左右外距把上緣分隔線拉成卡片滿寬（shadcn 的 CardContent 分隔線是滿寬的），
     再用同值的左右內距把欄位推回原位。
   ★`!important` 是必要的：這兩個區塊原本就掛著 Bootstrap 的 `.mb-3`（工具類自帶
     !important），而禁刪守則下那個 class 保留未動 —— 只能用同樣的 !important 蓋過去。
     特異度 (0,1,0) 打平，本檔在 bootstrap.min.css 之後載入 ⇒ 後者勝出。
   ★區塊 display:none 時整條規則連同 border-top 一起不渲染
     ⇒ 沒有「空插槽卻留著一條分隔線」的問題，也不需要任何 :empty / :has 判斷。 */
.reissue-type-fields {
    margin: 0 -1rem !important;
    padding: .75rem 1rem 1rem;
    border-top: 1px solid hsl(var(--border));
}

/* ═══ 六、「目前狀態」那張卡要一眼認得出來（2026-08-09 追加）═══════════════════
   業主：「目前狀態不是應該要黑框？ 或是黃框？」／「已經選擇的差異要做出來」。

   ── (1) 為什麼不是黑框 ──
   黑框（`--dark`）**已經被「選中態」用掉了**（見上面 `:has(:checked)` 那條）。
   兩張卡都黑框，使用者就分不出「我現在是這個」和「我要改成這個」——
   那正是本輪要解決的問題，不能用同一顆色去解。
   ⇒ 三態各佔一個視覺語彙，彼此不重疊：
       一般   1px 實線 --border(#E5E5E5) ＋ --card 底
       目前   2px 虛線 --warning-fg      ＋ --warning-bg 底 ＋「目前」徽章
       選中   1px 實線 --dark            ＋ 1px --dark 外環 ＋ --card 底
     ★「虛線 / 實線」「1px / 2px」是**不依賴顏色**的第二層線索
       （WCAG 1.4.1 Use of Color：狀態不可只靠顏色傳達）；
       第三層線索是文字本身——「目前」徽章 ＋ 原本就在的「（目前狀態，無法選擇）」。

   ── (2) 色票：不發明新顏色，整組吃主題既有的 amber 配對 ──
   `--warning-bg` / `--warning-fg` 是本檔早就備好的「柔和琥珀面 ＋ 其上文字色」配對
   （§28 alert、L5136 的 `.badge.bg-warning` 都在用），而且**亮暗兩色都已經訂好**：
       亮：--warning-bg 38 92% 94% = #FEF3E2   --warning-fg 32 81% 33% = #985910
       暗：--warning-bg 38 50% 16% = #3D2E14   --warning-fg 38 90% 70% = #F7C56E
   ★為什麼不用品牌黃 `--primary`(#FFD200) 當框：它對白底只有 **1.452:1**，
     連 UI 元件邊界的 3:1 都差得遠（比一般卡的 --border 1.260:1 好不了多少），
     「細黃框」單獨用一定不夠。`--warning`(#F59E0B) 也只有 2.147:1，同樣不過。
     `--warning-fg`(#985910) 對白底 **5.574:1** 是這三顆裡唯一過 3:1 的，所以框吃它。
   ★暗色下 `--warning-fg` 是 #F7C56E（亮琥珀），對暗卡 #171717 是 11.244:1 ——
     同一條宣告在暗色下自動變成「亮金框配深琥珀底」，不需要任何 `[data-bs-theme]` 分支。

   ── (3) ★★★對比度：底色不是白加上去就算了，文字色**一起**換掉 ──
   第四節說過「墊底色會把 2026-08-07 的修正原地打回去」——那個推論**現在依然成立**，
   本段不是推翻它，而是把它的前提消掉：底色換了，就不能再讓文字停在 `--muted-foreground`。
   實算（WCAG 2.x 相對亮度 L = 0.2126R+0.7152G+0.0722B，sRGB 先解 gamma；
         比值 =(L亮+0.05)/(L暗+0.05)）：
     亮色  --warning-bg #FEF3E2  L=0.9087
       × --muted-foreground #737373 L=0.1715 → (0.9587)/(0.2215) = **4.329:1  ✗ 不到 4.5**
         （↑ 這就是第四節警告的那個坑，實際踩下去會掉到 4.33，比 --muted 底的 4.35 還低）
       × --warning-fg      #985910 L=0.1384 → (0.9587)/(0.1884) = **5.090:1  ✓**
     暗色  --warning-bg #3D2E14  L=0.0302
       × --muted-foreground #A1A1A1 L=0.3560 → (0.4060)/(0.0802) = 5.060:1  ✓（暗色本來就過）
       × --warning-fg      #F7C56E L=0.6082 → (0.6582)/(0.0802) = **8.204:1  ✓**
   ⇒ 卡內兩處吃 muted 的文字（標題 ＋「（目前狀態，無法選擇）」）**一律改吃 --warning-fg**。
   ★這不是「為了過關而讓步」，是**淨提升**：
       標題那句 亮 4.741 → 5.090（+7%）、暗 6.935 → 8.204（+18%），兩個方向都變好。
   ★★★**那句「（目前狀態，無法選擇）」一個字都沒有刪、沒有縮短**（禁刪守則）——
     解法是換它的顏色，不是換掉它。

   ── (4) 邊框對比與「卡片整體」的辨識度 ──
     框 --warning-fg vs 白頁底  = 5.574:1  ✓ 過 UI 元件的 3:1（對照：一般卡的框只有 1.260:1）
     框 --warning-fg vs 自己的卡底 #FEF3E2 = 5.090:1  ✓
     底 #FEF3E2 vs 隔壁白卡 #FFFFFF = 1.0952:1（面積夠大，這個量級看得出來；
        對照：曾評估過的 `--primary/.12` 只有 1.0523，而且它會把說明文字壓到 4.505 的懸崖邊）
   ★WCAG 1.4.11 對「已停用控制項」本來就有豁免，這張卡整張都是 disabled ——
     但這裡**不靠豁免**，框自己就過 3:1。

   ── (5) 徽章：為什麼不能直接用 `.badge.bg-warning` ──
   本檔 L5136 的 `.badge.bg-warning` 背景正好是 `--warning-bg`，
   而那已經是**這張卡的底色** ⇒ 徽章會整顆融進卡底（實測邊界只剩 1.407:1，看不出是徽章）。
   ⇒ 沿用 `.badge` 基底（12px 字級、`--radius-md` 圓角、內距、1px 邊框都繼承，
     見 §8 / §24），只把同一組 token **反過來用**：底 --warning-fg、字 --warning-bg。
     亮 5.090:1 / 暗 8.204:1，兩個方向都過 4.5；徽章與卡底的分界也是同樣的 5.09 / 8.20。
   ★沒有新增任何一顆色票，只是把既有配對倒過來。

   ── (6) ★★★2026-08-09 最終定案：業主連續三輪裁決，黃色／琥珀整組撤回改黑 ──
   業主原話時序（同一天四則，一則比一則後面才是最終版）：
     ①「目前狀態不是應該要黑框？ 或是黃框？」②「已經選擇的差異要做出來」
     ③「黃色啦」（上面 (2)～(5) 就是照③做的第一版：框／文字／徽章全吃 --warning-fg）
     ④「我不要這個深豬 好醜」（→ 中間評估過的一版：框改 --warning #F59E0B 鮮黃、
        文字與徽章改近黑近白；這版只存在於施工過程與對話記錄，沒有停留成最終檔案內容，
        故不在本檔另闢一節，數字併在下面一起記）
     ⑤「你乾脆用原本的黑色好了」← ★以這句為準，本節以下所有生效宣告改依⑤定案。
   ⇒ 上面 (1)～(5) 整段推論（amber 配對怎麼選、3:1／4.5:1 算式、徽章為什麼不能直接用
     `.badge.bg-warning`、以及 ④ 的 #F59E0B 對白底 2.147:1 不過 3:1 但業主知情選擇的
     結論）**一字不刪，全部保留存查**——推論本身沒有錯，只是業主的視覺偏好後來
     推翻了它的結論。`--warning` / `--warning-bg` / `--warning-fg` 三顆色票
     **在這張卡上全部撤回**，色票定義本身不動（§28 alert、L5136 `.badge.bg-warning`
     等其他地方還在用，不受影響）。
   ★色票改用哪顆黑：本檔既有 `--dark` / `--dark-foreground` 這組 token
     （亮 #171717／#FAFAFA，暗 #FAFAFA／#171717，見本檔 L108-109、L319-320），
     不用 `--foreground`/`--card-foreground`（亮 #000000、暗 #FAFAFA）。理由：
     ①選中態（上面 `:has(:checked)` 那條）本來就吃 `--dark` 當框色與外環色——
     兩個狀態同一色系、只靠**形狀**分辨（目前＝虛線 2px，選中＝實線 1px＋外環＋圓點填滿），
     這正是業主「已經選擇的差異要做出來」那句要的層次；
     ②`.badge.bg-dark`（本檔 L1029：`background: hsl(var(--dark)) !important;
     color: hsl(var(--dark-foreground)) !important;`）是全站既有的「黑底白字徽章」
     標準寫法，本節徽章直接沿用同一組 token、不發明新配對（SSOT）。
   ★實算（同一套 WCAG 2.x 公式：L=0.2126R+0.7152G+0.0722B，sRGB 先解 gamma；
         比值=(L亮+0.05)/(L暗+0.05)；色值取自實際渲染的 token，非猜測）：
     框 --dark(#171717) vs 卡片白底 #FFFFFF（背景已撤回墊色、改回 --card）
       ＝ **17.928:1**；暗色 --dark 變 #FAFAFA、卡底 --card 變 #171717 ＝ **17.176:1**。
       兩者遠過 UI 邊界的 3:1（邊框本身不受 4.5:1 文字門檻約束，這裡是額外的安全邊際）。
     標題／說明文字：底色拿掉後不再有任何顏色覆蓋，自動吃回：
       標題 `.reissue-type-title` 走第二節本來就宣告好的 `--muted-foreground`
       （見本節下方，宣告本身沒被刪過只是曾被短暫覆蓋）：
         亮 #737373 vs 白卡 #FFFFFF ＝ **4.742:1 ✓**（與第四節 2026-08-07 記的 4.741
         只差在小數第三位的捨入，同一組色值）
         暗 #A1A1A1 vs 暗卡 #171717 ＝ **6.939:1 ✓**
       說明 `.reissue-type-note` 撤銷覆蓋後走 Bootstrap `.text-muted`，同一顆
       `--muted-foreground` ⇒ 算式與上面標題那行相同：亮 **4.742:1 ✓** ／暗 **6.939:1 ✓**。
       ⇒ 這正是第四節原本就在講的「2026-08-07 那次無障礙修正的原始狀態」，
         拿掉底色墊色之後它自動就回來了，不需要任何新宣告（DRY）。
     徽章：底 --dark／字 --dark-foreground，亮暗兩色是同一組值反過來用
       （亮：底 #171717 配字 #FAFAFA；暗：底 #FAFAFA 配字 #171717）＝ **17.176:1**，
       兩個方向剛好同分（因為兩色互換、相對亮度公式對稱）。
   ★★★這是本檔同一天第三次改這張卡的顏色決策——下一個看到這段的人，
     請不要再把它「修」回黃色或琥珀色：業主已經連續三次明確表態，
     ⑤「你乾脆用原本的黑色好了」是目前唯一有效的指示，(2)～(5) 段落
     只是**已被推翻、但保留存查**的推論過程，不是待辦或懸而未決。
     ★★★★但這一則也沒撐住——見下方 (7)，(6) 提出的黑框方案**從未真的落地成
       下面的宣告**就被業主再推翻一次，此段的「唯一有效」四字已作廢，僅存查。

   ── (7) ★★★★★2026-08-09 二度、三度推翻：業主直接點名方法論錯了，最終看稿定案 ──
   (6) 的黑色方案（框／徽章都吃 --dark／--dark-foreground）推導與算式都正確，
   但在真正把它寫進下面的宣告之前，業主看過草案給了更重的原話：
     「你做得好糟」／「你可以用 SHADCN 去思考設計顏色嗎」
   ⇒ 問題不是「選錯了某一顆顏色」（褐／黃／黑都試過），是**方法論錯了**：
     shadcn/ui 的語意色**只有 `destructive` 一顆有色相**，其餘狀態一律走中性階
     （`--background` / `--foreground` / `--muted` / `--muted-foreground` /
     `--accent` / `--border` / `--ring`）。它根本沒有「warning 黃卡」這種元件語彙——
     `--warning` / `--warning-bg` / `--warning-fg` 是本專案為了相容 Bootstrap `.alert`
     才補的（見本檔 §28），不是 shadcn 的東西；(2)～(6) 全程都在用 Bootstrap 的
     配色邏輯做 shadcn 的介面。
   ⇒ shadcn 自己表達「這是目前 / 已選的那一項」的方式（DropdownMenuItem 的 selected、
     SidebarMenuButton 的 isActive）：**填色**表達「目前」、**框線**表達「選中」，
     狀態標籤走 `Badge variant="secondary"`（中性灰底，不是彩色）——兩者各佔一個
     視覺維度。呼叫端依這個方向另外畫了幾個視覺候選稿給業主過目（含「中性填色」
     一版與本節最終落地的「零填色」一版），業主看稿後選定**下面這一版（稱 B 案）**：
       一般     1px 實線 --border  ＋ --card 底  ＋ 標題 --foreground
       目前     1px 實線 --border（與一般卡**完全相同**）＋ --card 底（不填色）
                ＋ 標題轉灰 --muted-foreground ＋ 中性灰「目前」徽章
       選中     1px 實線 --dark ＋ 1px 外環 ＋ --card 底 ＋ 標題 --foreground ＋ 圓點填滿
     ⇒ 「目前」與「一般」卡的邊框三項（style/width/color）**刻意完全相同**，
       區別完全交給「標題轉灰」與「徽章」這兩個非邊框訊號，不再靠任何顏色的框線。
   ★★★★★**最終定調（本節到此為止，之後不再變動）**：
     這張卡的狀態表達走兩個維度：**填色／文字深淺＝目前，框線＝選中**。
     一律中性階，**不得引入任何色相**（不管是 `--warning`／`--warning-bg`／
     `--warning-fg`，或任何其他有彩度的 token）。曾經試過褐、黃、黑框三種
     色相方案，全部被業主否決。日後有人想「加點顏色比較明顯」，
     看到這行請先讀完整段時序再決定，不要直接動手改。
   ★★★★★★2026-08-10 更新：上面「一律中性階、不得引入任何色相」這句
     **已被業主本人推翻**（原話：「目前請你改為黃色」），本元件的該項禁令自即日解除。
     ★上面整段原文**一字未刪**（禁刪守則）—— 它記錄的推論與 2026-08-09 當天的
       七輪時序全部照舊有效，只是最後那條禁令被更後面的一次指示取代了。
     ★★解除的範圍**只有「目前狀態」這一個標記**（分段填色 ＋「目前」徽章），
       而且只在本元件（`.reissue-seg-item--current` / `.reissue-type-badge`）。
       「框線／選中樣式＝選中」那一半**完全沒有變**，仍然走中性的 `--dark`。
       ⇒ 維度分工其實原封不動，只是「目前」那個維度的顏色從中性灰換成黃。
     ★★★`--warning-fg`(#985910 深赭)**仍然禁用**：業主 2026-08-09 的
       「我不要這個深豬 好醜」（第六節 ④）沒有被推翻，這次也沒有恢復它。
       黃色只用 `--warning`(#F59E0B) 與 `--warning-bg`(#FEF3E2) 兩顆。
     完整時序、色票、三態疊加與全部對比算式見本節**第九節**。
   ★色票落地（下面各條宣告旁的行內註解有算式，不在這裡重複）：
     - 邊框／背景：撤銷 (2)～(6) 所有覆蓋，讓最原始（第二節）的宣告重新生效——
       `border-style` 撥回 solid（B 案要求與一般卡同框），`background`／
       `border-width`／`border-color` 則是**完全撤掉覆蓋**，回到本節最開頭
       `.reissue-type-card` 基底與第二節原始宣告的樣子，等於沒有任何色相殘留。
     - 標題：撤銷 (2)～(6) 對 `.reissue-type-title` 的所有顏色覆蓋，讓第二節
       原本就宣告好的 `--muted-foreground` 重新生效（未曾被刪過，只是曾被短暫覆蓋）。
     - 說明文字：撤銷 (3)/(6) 對 `.reissue-type-note` 的顏色覆蓋與 `!important`，
       回到 2026-08-07 那次修正後的原始狀態——單純吃 Bootstrap `.text-muted`
       （`--muted-foreground` #737373），白底上 **4.741:1**，本來就過 4.5 門檻，
       不需要任何補救宣告。這是 B 案相對 (2)～(6) 所有版本最大的好處：
       零新增宣告、零 `!important`、零風險。
     - 徽章：改中性灰，底 `--muted`、字 `--secondary-foreground`、
       外加 `1px solid --border` 邊框（讓徽章在不填色的卡片上仍有清楚邊界）。
       本檔 `--muted` / `--secondary` / `--accent` 亮色實測都是同一個值 #F5F5F5
       （L73/75/92），`--secondary-foreground` 亮色 #171717 ⇒ 徽章文字對徽章底
       **16.444:1**（暗色 #FAFAFA 對 #262626 ＝ **14.499:1**），兩個方向都遠過 4.5。
   ★★完整原話時序存查（七輪，同一天，供日後稽核）：
     ①「目前狀態不是應該要黑框？ 或是黃框？」②「已經選擇的差異要做出來」
     ③「黃色啦」④「我不要這個深豬 好醜」⑤「你乾脆用原本的黑色好了」
     ⑥「你做得好糟」⑦「你可以用 SHADCN 去思考設計顏色嗎」← 業主看視覺稿後定案 B 案。
   ------------------------------------------------------------------------------ */

/* ★★2026-08-09 撤銷鏈存查（禁刪守則，逐版原文都留著，不是刪掉）——
   這條規則在同一天先後出現過三種內容，最終＝空規則（見下方說明）：
     版本一（第六節 (2)～(5)，「黃色啦」之前）：
       background: hsl(var(--warning-bg));
       border-width: 2px;
       border-color: hsl(var(--warning-fg));
     版本二（業主「黃色啦」之後，第六節 (7) 之前一直是這樣，也是實際跑過線上的版本）：
       background: hsl(var(--warning-bg));
       border-width: 2px;
       border-color: hsl(var(--warning));
     版本三（第六節 (6)，業主「你乾脆用原本的黑色好了」）：只在推導階段存在，
       border-color: hsl(var(--dark))，從未真的寫成這裡的宣告就被下一輪推翻。
   ⇒ 第六節 (7)：業主看視覺稿定案 B 案——「目前卡」background／border-width／
     border-color **完全不覆蓋**，回到本節最上方 `.reissue-type-card` 基底
     （`border: 1px solid hsl(var(--border))`）與再上面第二節原始宣告
     （只有 `border-style: dashed`）疊出來的樣子。所以這裡不再是一條有內容的
     規則——為了不整條刪掉（禁刪守則）、也不留一條空的 `{}` 造成誤解，
     用這段註解取代原本的宣告內容，記錄「曾經有過什麼、現在為什麼沒有」。
   ★下面 `border-style: solid` 是**唯一**還在生效的覆蓋，把第二節原始的
     `dashed` 撥回與一般卡相同的實線，理由與算式見第六節 (7)。 */
.reissue-type-card--disabled {
    border-style: solid;
}

/* 標題：原值 `hsl(var(--muted-foreground))`（第二節，緊接在上面 `.reissue-type-hit`
   規則之後那一行，原文從未被刪過）。
   ★2026-08-09 撤銷鏈存查：第六節 (2)～(6) 一度覆蓋成 `hsl(var(--warning-fg))`
   （「黃色啦」那版，5.090:1 / 8.204:1）；第六節 (7) 業主看稿定案 B 案後撤銷這條
   覆蓋——**不在這裡重新宣告**，讓第二節那條原始宣告直接生效（DRY，同一顆顏色
   沒有理由宣告兩次）。生效值＝第二節那行的 `--muted-foreground`：
   亮 #737373 vs 白卡 #FFFFFF ＝ 4.742:1 ✓；暗 #A1A1A1 vs 暗卡 #171717 ＝ 6.939:1 ✓。 */

/* 「（目前狀態，無法選擇）」：★2026-08-09 撤銷鏈存查（禁刪守則）——
   第六節 (3)/(6) 一度覆蓋成 `color: hsl(var(--warning-fg)) !important;`
   （「黃色啦」那版，5.090:1 / 8.204:1）。第六節 (7) 業主看稿定案 B 案後撤銷這條
   覆蓋，**不留任何宣告**，讓它直接吃 Bootstrap `.text-muted`（`--muted-foreground`
   #737373）——這正是 2026-08-07 那次無障礙修正後的原始狀態：白底上 4.741:1，
   本來就過 4.5 門檻，不需要任何補救。`.small text-muted ms-1` 三個 class
   從頭到尾沒被動過（禁刪守則）。 */

/* 「目前」徽章。由**伺服器端**依 $isCurrent 印出（與 `.reissue-type-card--disabled`
   同一個判斷），不用 CSS 生成內容 —— 理由同第三節：`::after{content}` 在部分
   螢幕閱讀器不會播報，而「目前」這兩個字是狀態資訊、必須是真的文字節點。
   ★`margin-left:auto` 把它推到標題列右緣；`flex-shrink:0` 讓長標題折行而不是壓扁徽章
     （result 頁的 col-lg-4 窄欄會遇到）。
   ★2026-08-09 撤銷鏈存查：第六節 (5)/(6) 先後是
     `background: hsl(var(--warning-fg)); color: hsl(var(--warning-bg));`（「黃色啦」版，
     5.090:1 / 8.204:1）與推導階段的 `--dark`／`--dark-foreground`（17.176:1，未落地）。
     第六節 (7) 業主看稿定案 B 案：徽章改中性灰，底 `--muted`、字
     `--secondary-foreground`，另加 1px `--border` 邊框（B 案的卡片本身不填色，
     徽章需要自己的邊界才看得出輪廓）。
     實算：亮 底#F5F5F5 配字#171717 ＝ 16.444:1 ✓；暗 底#262626 配字#FAFAFA ＝ 14.499:1 ✓。 */
/* ★★★2026-08-10 業主：「目前請你改為黃色」⇒ 徽章由中性灰改成**黃底近黑字**，
   與分段填色同一顆黃系（整段語彙統一：黃＝這是你目前的狀態）。
   ★原值記此存查（禁刪守則，逐字）：
     `background: hsl(var(--muted));`
     `color: hsl(var(--secondary-foreground));`
     `border: 1px solid hsl(var(--border));`
   ★色票配對**不是新發明**：`--warning` ＋ `--warning-foreground` 就是本檔 L1034
     `.badge.bg-warning` 那條「黃底徽章」用的同一組（SSOT）。
   ★★但**不能**直接改用 `class="badge bg-warning"`：本檔 L5136 有一條後到的
     `.badge.bg-warning{background:--warning-bg!important;color:--warning-fg!important}`
     把它整顆換成「淡黃底 ＋ --warning-fg 深赭字」，而 `--warning-fg`(#985910)
     正是業主 2026-08-09 明確否決的那顆（「我不要這個深豬 好醜」，見第六節 ④）。
     ⇒ 只借 token、不借 class。
   ★邊框改吃 `--warning-foreground`（近黑髮絲邊）而不是 `--border`：
     徽章底 --warning(#F59E0B) 對它坐著的兩種底 —— 目前段 #FEF3E2 是 1.957:1、
     卡片白底 #FFFFFF 是 2.148:1 —— 兩者都不到圖形界線的 3:1，光靠色塊本身
     在低視力／色覺缺陷下會糊成一片。近黑邊框把界線拉到 15.857:1（段上）／
     17.404:1（白卡上），輪廓在任何視覺條件下都成立，而且與徽章自己的字同色，
     讀起來是一顆「有描邊的黃色 chip」而不是多一顆顏色。
   實算見第九節。 */
/* ★★★★2026-08-10（同日更晚）業主三則：「你這目前的黃色也太醜 請你參考
   `/good/storageLocation` 這邊的設計」／「使用 SHADCN」／「做倒底 治本」
   ⇒ 徽章由「實心橘黃 ＋ 近黑描邊」改成**中性藥丸 chip**（形狀語彙抄 storageLocation，
     顏色語彙抄 shadcn 的 Badge）。完整推導、被否決的選項與四種狀態的對比實算見本節**第十節**。
   ★原值記此存查（禁刪守則，逐字，這是本輪被取代的三行）：
     `background: hsl(var(--warning));`
     `color: hsl(var(--warning-foreground));`
     `border: 1px solid hsl(var(--warning-foreground));`
   ★★本規則**覆蓋了誰、原值多少**（覆蓋鏈在原地標明，免得日後看不出來）：
     - `background` / `color` 覆蓋 §24 `.badge.bg-*` 那一組？**沒有** —— 本徽章的 markup 是
       `class="badge reissue-type-badge rounded-pill"`，**沒有任何 `bg-*` class**，
       所以吃的是 §24 `.badge` 基底（`font-weight:500; border-radius:var(--radius-md);
       font-size:12px; padding:.25em .6em; border:1px solid transparent`）。
       本規則覆蓋的是基底那顆 `border: 1px solid transparent`（原值＝透明），
       以及基底沒宣告的 background／color（`.badge` 基底不帶顏色）。
     - `border-radius`：**本規則不宣告**，改由 markup 的 `.rounded-pill`
       （§20 `.rounded-pill{border-radius:9999px !important}`）覆蓋 §24 `.badge` 的
       `var(--radius-md)`(8px)。用既有工具類、不在這裡再寫一次（DRY，且與
       `/good/storageLocation` 那顆狀態 chip 用的是同一顆 class）。
     - `display`：覆蓋 Bootstrap `.badge` 出廠的 `inline-block`（原值），改 `inline-flex`
       才能讓 lucide 圖示與「目前」兩個字垂直置中對齊。
   ★色票一顆都沒有新增：`--secondary` / `--secondary-foreground` 就是本檔
     `span.btn.rounded-pill`（§補位「狀態 chip 的語意色還原」上方那條）給「沒有語意 class 的
     中性狀態 chip」訂好的同一組 —— 也就是 `/good/storageLocation` 那顆 chip 在**沒有**
     `btn-success` 之類語意 class 時的長相。等於直接沿用全站既有的中性 chip 配色（SSOT）。
   ★★邊框刻意**不是** `--border`：那正是第八節踩過的 1.000:1 隱形坑（軌道自己就是 `--border`）。
     改吃 `--muted-foreground`(#737373) —— 仍在中性階內、不是新色，
     對三種可能的底（軌道 #E5E5E5／選中片 #FAFAFA／卡片白 #FFFFFF）分別是
     3.764 / 4.543 / 4.742，全部過 WCAG 1.4.11 圖形界線的 3:1（算式見第十節 (4)）。
   ★★邊框也刻意**不是** `--dark`：`--dark` 已經被「選中」那一維用掉了
     （本節第九節的 `:checked + label` 框色），兩者同色會讓「目前」和「選中」再度混淆——
     那正是第六節 (1) 當初否決黑框的同一條理由。`--muted-foreground` 與 `--dark` 之間
     還有 3.781:1 的差距，兩種框線分得開。 */
/* ★★★★★2026-08-10（同日第三輪）業主逐字：「二聯 目前前面移除點 目前使用黃底黑字」
   ⇒ 徽章由「中性藥丸 chip」改回**黃底近黑字**，並且 markup 端已把
     `<i data-lucide="circle-dot">` 那顆圖示移除（＝業主說的「前面那個點」，
     畫面上看起來就是一個小圈）。
   ★★★原值記此存查（禁刪守則，逐字，本輪被取代的三行）：
     `background: hsl(var(--secondary));`
     `color: hsl(var(--secondary-foreground));`
     `border: 1px solid hsl(var(--muted-foreground));`
   ★上方那四段撤銷鏈註解（第六節 B 案的中性灰、08-10 第一次的黃、
     08-10 第二次的 storageLocation 中性藥丸）**一個字都沒有刪** ——
     這顆徽章今天已經在「黃 → 中性 → 黃」之間走過一輪，全程留在原位置可查。

   ══ 字色為什麼是 `--warning-foreground`，而不是 `--foreground` 或 `--dark` ══
   ★★★決定因素是**暗色模式**，不是亮色下的對比數字（三者在亮色下都過關）：
     `--foreground` 與 `--dark` 都會在 `html.dark` 底下翻成近白（#FAFAFA），
     而 `--warning` **沒有暗色覆寫**（全檔只有 L172 一處宣告）⇒ 底色仍是 #F59E0B。
     實算 #FAFAFA on #F59E0B ＝ **1.867:1**，遠低於 4.5 —— 暗色下那顆徽章會直接糊掉，
     而且**零錯誤訊息**（亮色驗收會全綠，只有切到暗色才看得到）。
   ★`--warning-foreground`（`0 0% 10.2%` ＝ #1A1A1A）同樣只在 :root 宣告一次、
     沒有暗色覆寫 ⇒ 與 `--warning` 一樣是**兩個主題共用的固定值**，配對永遠成立。
   ★★★這也是 SSOT：`--warning` ＋ `--warning-foreground` 就是本檔 L1034
     `.badge.bg-warning`（全站「黃底徽章」）用的同一組，不是本輪新發明的配色。
   ★★★⚠️ 別把它與 `--warning-fg`(#985910) 搞混 —— 名字只差三個字元，
     但那顆深赭是業主 2026-08-09 明確否決過的（「我不要這個深豬 好醜」，第六節 ④），
     **永久禁用**。本規則用的是 `--warning-foreground`，近黑，不是那顆。
   ★仍然**不能**改用 `class="badge bg-warning"`：本檔 L5136 有一條後到的
     `.badge.bg-warning{background:--warning-bg!important;color:--warning-fg!important}`
     會把它換成「淡黃底 ＋ --warning-fg 深赭字」，正好踩到上面那顆禁用色。只借 token、不借 class。

   ══ 對比實算（WCAG 2.1，亮暗共用同一組值 ⇒ 只有一份數字）══
   底 --warning #F59E0B：Rs=.960784 Gs=.619608 Bs=.043137
     R=((.960784+.055)/1.055)^2.4=.913087  G=((.619608+.055)/1.055)^2.4=.341934  B=.043137/12.92=.003339
     L₁ = .2126×.913087 + .7152×.341934 + .0722×.003339 = .194123+.244551+.000241 = .438915
   字 --warning-foreground #1A1A1A：s=.101961 → ((.101961+.055)/1.055)^2.4 = .010330（三通道同值）
     L₂ = .010330
   對比 = (.438915+.05) / (.010330+.05) = .488915 / .060330 = **8.104:1** ✓（門檻 4.5，1.5rem 內文字）
   ★參考（被否決的兩顆）：#171717(--dark 亮色值) 8.347:1 ✓ 但暗色翻白 1.867:1 ✗；
     #000000(--foreground 亮色值) 9.778:1 ✓ 但暗色同樣翻白 1.867:1 ✗。

   ══ 邊框 ══
   ★改吃 `--warning-foreground`（近黑髮絲邊），與 08-10 第一次的黃版本**同一個決定**
     （那次的存查就在上方，逐字相同）：徽章底 #F59E0B 對它坐著的卡片白底只有 2.148:1，
     不到 WCAG 1.4.11 圖形界線的 3:1，光靠色塊在低視力／色覺缺陷下會糊成一片。
     近黑邊把界線拉到 17.404:1（白卡上），而且與徽章自己的字同色 ——
     讀起來是「一顆有描邊的黃 chip」，不是多出一顆顏色。
   ★`--muted-foreground`（原值）在這裡不行：#737373 對 #F59E0B 只有 1.834:1，
     黃底一換上去，原本那圈灰邊就等於消失了。
   ★`gap: .25rem` 保留不刪（禁刪守則）：圖示移除後徽章只剩一個文字節點，
     gap 沒有作用對象、不影響任何像素；日後若又要放圖示，它就在原位。 */
/* ★★★★★★2026-08-10（同日第四輪）業主原話：「目前發票狀態的 目前 按鈕 應該要用黃底黑字
   跟品牌色一樣 使用 shadcn」
   ⇒ 徽章由「--warning／--warning-foreground 黃底近黑字」改成**品牌主色 --primary／--primary-foreground**
     （§1 Design Tokens：`--primary:49.4 100% 50%` #FFD200 品牌金黃、`--primary-foreground:0 0% 10.2%` #1A1A1A，
     來源＝賣賣 App AppColors.primary / textOnPrimary，是全站唯一一組「品牌色」token，見本檔 L33-34）。
   ★★★原值記此存查（禁刪守則，逐字，本輪被取代的三行）：
     `background: hsl(var(--warning));`
     `color: hsl(var(--warning-foreground));`
     `border: 1px solid hsl(var(--warning-foreground));`
   ★★暗色模式覆寫複查（自己重新 grep 一次，不是沿用上一輪的推論）：
     `--primary`／`--primary-foreground` 全檔只在 L33-34（`:root`）各宣告一次，
     `html.dark`（L284 起整段）沒有出現 `primary` 字樣 ⇒ 沒有暗色覆寫，
     兩模式共用同一組固定值，配對在亮暗兩主題都成立。
   ★★邊框跟著改成 `--primary-foreground`（不是維持 `--warning-foreground`）：
     兩者數值**逐字相同**（都是 `0 0% 10.2%` ＝ #1A1A1A，見 L34 與 L173），
     改與不改**零視覺差異**；但改了才讓整條規則三個屬性統一落在同一個 token 家族（--primary 系），
     不會留下「底色／文字用 primary、邊框卻借用 warning 家族」這種語意上多餘的跨組引用（SSOT）。
   ══ 對比實算（WCAG 2.1，亮暗共用同一組值 ⇒ 只有一份數字）══
   底 --primary #FFD200：Rs=1.0 Gs=.823529 Bs=0
     R=((1.0+.055)/1.055)^2.4=1.0  G=((.823529+.055)/1.055)^2.4=.644538  B=0/12.92=0
     L₁ = .2126×1.0 + .7152×.644538 + .0722×0 = .212600+.460974+0 = .673574
   字/邊框 --primary-foreground #1A1A1A：s=.101961 → ((.101961+.055)/1.055)^2.4 = .010330（三通道同值，
     與上一輪 --warning-foreground 算式相同結果，因為是同一個 hex #1A1A1A）
     L₂ = .010330
   對比 = (.673574+.05) / (.010330+.05) = .723574 / .060330 = **11.994:1** ✓（門檻 4.5，
     遠高於前一版 --warning 的 8.104:1）。邊框對圖形界線 3:1（WCAG 1.4.11）同樣以此數字過關，
     primary 比 warning 更亮更黃，可視度只會更好，不是踩坑。 */
.reissue-type-badge {
    margin-left: auto;
    flex: 0 0 auto;
    display: inline-flex;       /* 覆蓋 BS `.badge` 出廠的 inline-block（原值），讓圖示與文字置中對齊。
                                   ★2026-08-10 覆核過不用改：computed display 量出來是 "flex"（不是
                                   inline-flex），原因是這顆是 `.reissue-type-hit`（display:flex）的
                                   flex item——CSS Display 規範規定 flex item 的 inline-level display
                                   會被 blockify 成對應的 block 等級值，inline-flex→flex，這是規範行為
                                   不是宣告錯誤。本行 `inline-flex` 已經是正確寫法，故意留著不動。 */
    align-items: center;
    gap: .25rem;                /* 圖示與「目前」兩字的間距；storageLocation 那顆用的是一個半形空白，
                                   這裡用 gap 才不會在不同字型下漂移 */
    background: hsl(var(--primary));
    color: hsl(var(--primary-foreground));
    /* ★★★2026-08-10 業主原話：「你這個目前的標籤 沒有遵守 shadcn 吧」——比對 shadcn v4
       `badge.tsx` default variant，逐項落差記在下面三個屬性，本輪只動 `.reissue-type-badge` 這一條規則。
       ★原值記此存查（禁刪守則，逐字，本輪被取代的一行）：
         `border: 1px solid hsl(var(--primary-foreground));`
       改法：只把顏色換成 transparent，`1px solid` 的寬度與樣式不動（盒模型不變、不會位移）——
       shadcn Badge 的 default variant 是 `border-transparent`：它宣告 border 只是為了讓 `outline`
       variant 有東西可用，實心底的 variant 一律透明邊；畫可見深色邊是「膠囊藥丸」的語彙，不是 shadcn Badge。
       ★實測：這個屬性本規則原本就贏過 `.badge{border:1px solid transparent}`（同分但本規則排在後面），
       改顏色不需要 `!important`，computed borderColor 量出來是 `rgba(0, 0, 0, 0)`。

       ★★★★2026-08-10 更正（★上方原句一字未刪，本段只是追加在既有註解之後）：
       上面那句「比對 shadcn v4 `badge.tsx`」在下方推導出的 `rounded-md`（8px 方角）
       **結論與官方相反**。2026-08-10 直接抓官方
       `shadcn-ui/ui → apps/v4/registry/new-york-v4/ui/badge.tsx` 重新查證：
       badgeVariants 的 base class 是 **`rounded-full`（＝藥丸，CSS 9999px）**，
       **而且 Badge 沒有 size variant** —— 官方從頭到尾沒有 `rounded-md` 這一階。
       ⇒ 依該錯誤前提寫下的 `border-radius: var(--radius-md) !important` 已於 2026-08-10 移除，
         改吃基底（存查與理由見下方 border-radius 段）。
       ★★下一個人請注意：讀到上面「比對 shadcn v4 `badge.tsx`」時，**那不是查證過的結論**；
         真正照官方檔比對出來的結論是本段這一條（`rounded-full`）。 */
    border: 1px solid transparent;
    /* ★★★border-radius 本規則原本沒有宣告 —— 現在看到的 9999px 藥丸圓角，來源是 HTML 上另外掛的
       `rounded-pill` class（`_reissue_form.blade.php` L312，不在本輪授權範圍，不能動），對到兩條
       `!important`：Bootstrap `.rounded-pill{border-radius:var(--bs-border-radius-pill)!important}`
       與本檔 L1717 `.rounded-pill{border-radius:9999px!important}`（同樣是全站工具類，不在 §S-90，
       不能動它）。兩條都是 `!important`，唯一能在不碰 `.rounded-pill`／不碰 blade 的前提下勝出的辦法，
       是本規則也用 `!important`——同分（0,1,0）時比 `!important` 之間的原始碼順序，本規則在檔案更後面，
       實測會贏（用注入的 `<style>` 逐一驗證過：沒加 `!important` 前維持 9999px，加了之後變 8px，
       確認會贏才寫進來，不是用猜的）。圓角值用本專案 SSOT `--radius-md`
       （`calc(var(--radius) * 0.8)` ＝ 8px），不是寫死 `8px`、也不是 shadcn 官方原文的 `rounded-md`
       （那是 Tailwind class 名，本專案沒有 Tailwind runtime，token 才是這裡的 SSOT）。 */
    /* ★★★★2026-08-10 移除本條宣告：上面整段推導的**前提是錯的** ——
       「shadcn v4 Badge 是 `rounded-md` 方角」與官方相反，官方 `badge.tsx` base class 是
       `rounded-full`（藥丸）且無 size variant（查證過程見上方「更正」段）。
       ★被移除的那一行逐字存查在此（禁刪守則，原地保留，不是刪除）：
         `border-radius: var(--radius-md) !important;`
       ★★為什麼是「移除」而不是改寫成 `9999px !important`：**基底本身已經是對的** ——
         §24 `.badge{border-radius:9999px}` 已於 2026-08-09 依同一份官方檔訂正回藥丸，
         markup 上的 `.rounded-pill` 也是 `9999px !important`。本規則若再宣告一次，
         只是在同一個屬性上疊出第三層 `!important`，讓下一個人更難改；
         圓角的權威來源就是「§24 基底 ＋ `.rounded-pill` 工具類」（SSOT）。
       ★移除前先用 CSSOM 預演實測過（不是用猜的）：在頁面上對本規則
         `style.removeProperty('border-radius')` 之後，computed borderRadius 由 `8px` 變回 `9999px`，
         而 background `rgb(255,210,0)`／color `rgb(26,26,26)`／height `18px`／padding `2px 8px`／
         width `41.65px` **全部不變**；再 `setProperty` 還原確認回到 `8px`（雙向都驗過才動手）。
       ★上方那整段「為什麼要加 !important」的推導**一個字都沒有刪**，留在原位置可查。 */
    /* ★★padding 原本也不是本規則自己宣告，是繼承自全站 `.badge{padding:.25em .6em}`（12px 字級下
       算出 3px/7.2px，§8 Badge 那條，不在本輪授權範圍）。shadcn Badge 官方是 `py-0.5 px-2`＝2px/8px，
       兩者很接近但仍有差，所以在本規則內明著補一條蓋掉繼承值，只讓這顆徽章對齊 shadcn 目標值
       （不動全站 `.badge`）。★改之前先用 inline style 蓋上量測過：`.reissue-type-title`（同一列的標題文字）
       的 `getBoundingClientRect().y` 與 `height` 改前改後完全不變（309.6875 / 22.3984375px），
       只有徽章自己從 20px 縮到 18px、在 `.reissue-type-hit`（`align-items:center`）裡重新置中，
       不會跟旁邊文字的基線錯開，量到「沒有副作用」才定案要改。 */
    padding: .125rem .5rem;
}

/* 使用者要求減少動態時，狀態切換一律瞬間完成（prefers-reduced-motion）。 */
@media (prefers-reduced-motion: reduce) {
    .reissue-type-card,
    .reissue-type-hit { transition: none; }
}

/* ═══ 七、2026-08-09 二層化：四張平鋪卡 → 兩張主卡 ＋ 二聯卡內的分段控制項 ═══════
   業主原話（同日，接在第六節那七輪之後）：
     「我覺得二聯整合一個 三連一個 四個太複雜的感覺 二連用收納的方式」
     「還是你二聯使用左右箭頭切換的方式」
     「這樣可以保持四個選框 但三個二聯左右點擊出現」
   呼叫端把三種做法（巢狀清單／左右箭頭／分段控制項）畫成視覺稿給業主過目，
   業主選定 **C 案：分段控制項**。

   ── (1) ★★分段控制項整組複用 §13 的 .fb-tabs / .fb-tab，軌道與片一行新 CSS 都沒有寫 ──
   買家在**上一頁**（發票查詢）看到的「訂單編號查詢 / 發票號碼查詢」就是這個元件
   （`buyer/invoice/_query_tabs.blade.php`）—— 同一個買家、相隔一頁、同一種「一列裡選一格」
   的動作，本來就該長成同一個樣子。實際吃到的生效值（全部來自既有章節，本節零覆蓋）：
       軌道 .fb-tabs          §13 高 2.25rem(36px)、--radius(10px) 圓角、inline-flex、fit-content
                              §S-6x 底 --muted(#F5F5F5)、內距 3px、border:0
       片   .fb-tab           §13 --radius-md(8px)、14px/500、色 --foreground/.6
                              §S-6x 左右內距 .5rem
       選中 .fb-tab.active    §13 底 --background、色 --foreground
                              §S-6x border-color:transparent ＋ Tailwind v4 shadow-sm
   對比實算（WCAG 2.x，色值取自本檔 :root 的實際 token，不是猜的）：
       未選片字 rgba(0,0,0,.6) 合成於軌道 #F5F5F5 ＝ #626262 → 對軌道 **5.594:1 ✓**
       選中片字 #000000 於片底 #FAFAFA                        → **20.116:1 ✓**
   ★選中態用的是**全站同一顆 `.active`**（由腳本切換），不是本節另訂的 class ——
     主題日後調整選中片外觀，這張卡自動跟著走，不會漂。
   ★軌道 `width:fit-content` 刻意不改成滿寬：§13 就是為了「標籤長短差很多、等寬會很醜」
     才偏離官方的 flex-1，這裡三段（不綁載具／手機條碼／自然人憑證）長度也不齊，同一個理由。

   ── (2) 為什麼還是得補幾條 —— 它原本是「連結頁籤」，這裡是「radio」──
   §13 的 .fb-tab 是 `<a>`：游標、焦點、停用三件事它都靠 <a> 的原生行為，而本節的每一段是
   `<input class="btn-check">`（Bootstrap 5.3 出廠：`position:absolute;clip:rect(0,0,0,0)`，
   視覺隱藏但**仍可聚焦**）＋ 緊接著的 `<label class="fb-tab">`。差異只有這幾條，逐條補：
     · 游標：<label> 沒有 <a> 的 pointer。
     · 焦點：焦點落在**被隱藏的 input** 上，`.fb-tab:focus-visible`（檔尾 §focus 那組）
       永遠不會成立 ⇒ 鍵盤使用者完全看不到焦點在哪。用相鄰兄弟選擇器把同一組環畫到 label 上。
       ★**刻意不用 `:has()`**：焦點提示是無障礙功能，不能取決於瀏覽器支不支援
         （§S-90 第三節同一條原則）。`input + label` 是 CSS2.1 的相鄰兄弟，全瀏覽器都吃。
       ★兩個宣告的值與檔尾 `.fb-tab:focus-visible` **逐字相同**（`--ring` 邊框 ＋ 3px/.5 外環，
         §S-73 的官方兩層寫法）。這裡是**唯一**一處重複 —— 沒辦法把選擇器加進那條規則裡
         （它不在本節、本輪只准動 §S-90），複製兩行是兩害相權取其輕。
       ★已知限制（不是漏做）：§13 的軌道帶 `overflow-x:auto`，另一軸依規範會算成 auto
         ⇒ 3px 的外環會被軌道裁掉一部分，實際看到的是「填滿 3px 溝槽的一圈環」。
         這與全站既有 .fb-tab 的鍵盤焦點表現**完全一致**（同一個軌道、同一組環），
         要治得動 §13 的 overflow，不在本輪授權範圍。
     · 停用：`.btn-check:disabled + .btn` 那條 Bootstrap 規則只選 `.btn`，選不到 `.fb-tab`。
       ★★這裡**刻意只改游標、不改文字顏色**：改成 --muted-foreground(#737373) 疊在軌道
         #F5F5F5 上只有 **4.350:1**，比原本的 5.594:1 更差 —— 為了「看起來像停用」
         去換掉一個更好的對比是倒退。停用的訊號改由「灰點 ＋ 下方那句說明 ＋ 點不動」承擔，
         這也正是 §S-90 第六節 (7) 定下的分工：**填色／文字深淺＝目前，框線＝選中**。

   ── (3) ★★★顏色：中性階，一顆色相都沒有加 ──
   本節新增的兩個顏色只有：
       「目前」灰點  --muted-foreground(#737373) 對軌道 #F5F5F5 ＝ **4.350:1**
                     ✓ 非文字圖形的門檻是 3:1（WCAG 1.4.11），這裡是它的 1.45 倍
       焦點環        --ring / .5（與全站同一組）
   `--warning` / `--warning-bg` / `--warning-fg` / `--primary` **一顆都沒有出現**。
   第六節 (7) 那句「日後有人想加點顏色比較明顯，看到這行請先讀完整段時序」對本節同樣有效。
   ★★★2026-08-10 更新（原文一字未刪）：業主指示「目前請你改為黃色」之後，
     本節這句「一顆色相都沒有加」已不再是現況 —— `--warning` 與 `--warning-bg` 兩顆
     現在用在「目前狀態」的填色與徽章上（`--warning-fg` 仍禁用）。詳見第九節。

   ── (4) 「目前」徽章與「（目前狀態，無法選擇）」各自放哪、為什麼 ──
   兩者都**一個字沒刪**（禁刪守則），只是分開放到它們各自真正在描述的東西旁邊：
     · 徽章 → **二聯卡的標題列**（沿用既有 .reissue-type-badge，中性灰）。
       它回答「我現在的發票落在哪一家族」；36px 高的軌道塞不下 12px 徽章
       （375px 手機上三段各只有約 60～75px，塞進去會換行或觸發橫捲）。
       而且收合狀態下這是唯一還看得到狀態的位置。
     · 說明句 → **分段控制項下方**。它描述的是「那一段不能選」，
       ★★留在卡片層級會變成一句假話 —— 二聯卡本身是**可以選**的
         （它底下還有兩個可選子項，整張停掉是功能倒退）。
   ★四種 $currentType 的行為（伺服器端 $isCurrentTriple / $currentInDouble 兩個旗標決定）：
       triple                            三聯**整張卡**停用（它沒有子項）＋ 徽章在三聯卡 ＋ 說明句在三聯卡
       double / mobile_barcode / cdc_barcode
                                         二聯卡**維持可選**，只停用分段裡對應那一段
                                         ＋ 徽章在二聯卡標題列 ＋ 說明句在分段下方
     （detectCurrentType() 還會回傳第五個值 'other_carrier'：兩張卡與三段全部可選、
       沒有任何「目前」標記 —— 與改版前四張卡都不 $isCurrent 的行為相同。）

   ── (5) ★★★「點改打勾」：選中指示器由圓點改成勾（業主同日追加）──
   §35 給 `input[type=radio]:checked` 畫的是 `--dark` 圓底 ＋ `--dark-foreground` 小圓點，
   本輪把**這張卡上的**圓點換成勾。做法比較過兩條路：
     (a) lucide `data-lucide="check"`（本站確實有載）—— **不採用**。
         它要在 markup 裡塞一個 <i>，而指示器必須疊在 radio 的圓底上；lucide 是 `defer`
         載入、靠 `createIcons()` 一次性替換節點，本表單卻是「切換時即時變化」的元件
         ⇒ 得自己維護 JS 時序，換來的只是同一個勾。
     (b) ★採用：**照 §35 自己的機制**，換掉 `background-image` 的 inline SVG。
         純 CSS、不動 markup（radio 的 name / id / value / checked / disabled 五個屬性
         一字未動）、不依賴任何 JS 時序，而且勾的路徑直接沿用 §35 checkbox 那條
         `d='m6 10 3 3 6-6'`（同一支 SVG、同一個 stroke-width，全站只有一種勾的形狀）。
   ★★★作用域：選擇器是 `.reissue-type-input:checked[type="radio"]`。
     `.reissue-type-input` 這顆 class **全站只有 `buyer/invoice/_reissue_form.blade.php`
     這一支在用**（grep 過，命中數 1），⇒ 後台與其他頁面的 radio 一律維持 §35 的圓點。
     ★特異度 (0,3,0) 對上 §35 的 `.form-check-input:checked[type="radio"]` (0,3,0)：
       同分、本節排在後 ⇒ 勝出；暗色那條同理 (0,4,1) 對 (0,4,1)。
       **§35 一個字都沒有動**（它是全站規則，改它等於改掉每一顆 radio）。
   ★顏色：勾 = `--dark-foreground`，底 = `--dark`（§35 給 checked radio 的底色，未動）。
     與圓點是**同一組 token**，沒有引入任何色相。實算：
       亮  勾 #FAFAFA(L=.95596) 於底 #171717(L=.0085757) ＝ **17.174:1 ✓**
       暗  勾 #18181B(L=.0092742) 於底 #FAFAFA(L=.95596)  ＝ **16.972:1 ✓**
     （#18181B 是 §35 兩支 SVG 原本就寫死的值，與 --dark-foreground 暗色 #171717 差 4/255，
       這裡逐字沿用 §35 的 SVG、不另調一顆，維持全站只有一份勾的定義。）
   ★暗色是**紙上驗證**：買家發票頁的殼層沒有 `html.dark`，實務上只會跑亮色那條；
     暗色宣告照樣補上，是為了與 §35 同構（日後買家端若加了主題切換不會只有這張卡失色）。
   ★停用態（＝「目前狀態」那一張／那一段）**刻意不畫勾**：勾的語意是「你選了要改成這個」，
     而目前狀態是「你已經是這個、而且不能選」——兩件事不該共用同一個符號。
     它的辨識靠「目前」徽章＋灰點＋說明句（三個都是**真的文字/圖形節點**，
     不是 CSS 生成內容，螢幕閱讀器讀得到）；radio 本身維持 §35 的 `disabled{opacity:.5}` 空心圓。
   ★未選中的 radio 仍然是**空心圓**（本節只加 `:checked` 那一條，未選態一個字沒碰）。
   ------------------------------------------------------------------------------ */

/* 每一段（<label class="fb-tab reissue-seg-item">）。<a> 有的游標行為，<label> 要自己補。
   user-select / tap-highlight 兩條與 `.reissue-type-hit` 同一個理由（連點兩下不要選到文字、
   手機點擊不要那層灰底閃光）。 */
.reissue-seg-item {
    cursor: pointer;
    user-select: none;
    -webkit-tap-highlight-color: transparent;
}
/* 「目前狀態」那一段：只換游標，不換文字顏色（理由與算式見上方 (2) 最後一段）。 */
.reissue-seg-input:disabled + .reissue-seg-item {
    cursor: not-allowed;
}

/* ═══ 八、2026-08-09 業主實看後的修正（同日第八輪）═══════════════════════════════
   業主原話（四則，同一個根因）：
     「手機條碼你也沒顯示啊？」「無法輸入？ 也沒資料 但結果顯示目前？」
     「是測試資料造成的嗎」「選定手機條碼 我應該要看得到」「手機條碼的樣子不是？」
   ── 根因：第七節把「二聯」卡做成**完全收合**（進頁什麼都沒選中），於是分段控制項與
      載具欄位一起 display:none；但「目前」徽章掛在**外層卡片**上還看得到
      ⇒ 「看得到『目前』，卻看不到目前是哪一種」，而且載具欄位點不到。
   ⇒ 修正三件事（markup 與腳本在 `buyer/invoice/_reissue_form*.blade.php`，本節只管樣式）：
     (a) 目前狀態落在哪一家族，那張卡就**預設選中並展開**（`invoice_family` 帶 checked）。
         ★`invoice_type` **仍然維持未選** —— 預先幫使用者選一個「別的」選項會讀成
           「你已經選好要改成不綁載具了」，那是誤導。
     (b) 「目前」徽章**搬進分段控制項、貼在對應的那一段上**，指得出是哪一種。
     (c) 目前那一段要有**填色**，一眼看得出「我在這」。

   ── 為什麼填 `--border` ──
   §S-90 第六節 (7) 定下的維度分工是「**填色／文字深淺＝目前，框線／選中樣式＝選中**」，
   本節照做：
       軌道（未選中片露出來的底）  --muted    #F5F5F5
       目前段（本節新增的填色）    --border   #E5E5E5   ← 比軌道深一階
       選中片（.fb-tab.active）    --background #FAFAFA ＋ Tailwind v4 shadow-sm ← 比軌道亮、且浮起
   ⇒ 三者在**同一條中性灰階**上分成三層，一顆色相都沒有加
     （`--warning` / `--warning-bg` / `--warning-fg` / `--primary` 全數禁用，
      同一天為此繞了七輪，記錄在第六節 (7)，不要再改回顏色）。
   ★★★2026-08-10 更新（原文一字未刪）：業主「目前請你改為黃色」——
     「目前段」的填色已由 `--border` 改成 `--warning-bg`，本段的中性階結論就這一項而言
     已被取代。上面那組「三層灰階」的推導仍然成立、也仍然是選中片與軌道的實況，
     只是中間那一層（目前段）換成了黃。★而且本段當初算的前提有誤——
     它假設軌道是 `--muted` #F5F5F5，實際上 §S-47 早把軌道改成 `--border`，
     於是「目前段填 --border」與軌道同色、根本看不見（見該宣告上方的存查註解）。
     ⇒ 換黃色同時也修掉了這個隱形 bug。詳見第九節。
   ★為什麼不自己另調一顆灰：§S-42／§S-46／§S-47 三節都寫過同一句
     「不要自己另調第三顆灰」—— `--border` 正是本檔既有、而且**亮色比 --muted 深一階**的那顆。
   ★對比實算（WCAG 2.x，色值取自本檔 :root）：
       目前段文字 rgba(0,0,0,.6) 合成於 #E5E5E5 ＝ #5C5C5C(L=.10708) 對 #E5E5E5(L=.783545)
         ＝ **5.307:1 ✓**（比原本躺在軌道上的 5.594 只低 0.29，仍遠過 AA 4.5）
       目前段底 #E5E5E5 vs 軌道 #F5F5F5 ＝ 16/255 的明度差，是一整塊填色（不是 1px 線），
         這個量級在實心色塊上讀得出來；而它與選中片 #FAFAFA 差 21/255、方向相反
         （一個比軌道深、一個比軌道亮＋帶陰影）⇒ 「目前」與「選中」不會混淆。
   ★暗色：`--border` 是 `0 0% 100% / 10%`（白 10% 疊層），疊在暗軌道 `--muted` #262626 上
     會變成比軌道亮一階的灰 ⇒ 同一條宣告在暗色自動成立，不需要 `html.dark` 分支。
     （買家頁殼層沒有 `html.dark`，這是紙上驗證。）

   ── 進到分段裡的「目前」徽章為什麼要縮小 ──
   第七節 (4) 記過「36px 軌道塞不下 12px 徽章」的寬度顧慮 —— 那條顧慮**仍然成立**，
   所以徽章進到片內時：字級 12px → 11px、內距壓到 .0625rem/.25rem、
   並把 `.reissue-type-badge` 的 `margin-left:auto` 歸零
   （那個 auto 是為「卡片標題列右緣」設計的，在片內會把徽章推離文字）。
   ★徽章的顏色一個字都沒改（仍是第六節 (7) 定案的中性灰：底 --muted、字
     --secondary-foreground、1px --border 邊框）。
   ★它坐在 #E5E5E5 的目前段上：徽章底 --muted #F5F5F5 比段底亮一階、外加 --border 邊框
     ⇒ 仍然看得出是一顆徽章（不像第六節 (5) 那個「徽章融進卡底」的坑）。
   ------------------------------------------------------------------------------ */
/* ★★★2026-08-10 業主：「目前請你改為黃色」⇒ 這條的填色由中性灰改成淡黃。
   ★原值記此存查（禁刪守則，逐字）：`background: hsl(var(--border));`
   ★★順帶修掉一個**它從來沒有真的顯示過**的 bug：第八節推導這顆填色時，
     算的是「軌道 --muted #F5F5F5、目前段 --border #E5E5E5 比軌道深一階」——
     但 §S-47 早就把軌道改成 `.fb-tabs{background:hsl(var(--border))}` 了
     ⇒ 目前段的填色與軌道**是同一顆 token、同一個色值**，實測對比 1.000:1，
       等於這塊填色在畫面上根本看不見。業主說「切換會不順」「看不出目前是哪一種」
       有一部分就是這裡：唯一的視覺標記其實是隱形的。
     改成 --warning-bg 之後，目前段對軌道是 #FEF3E2 vs #E5E5E5 —— 亮度比只有 1.148:1，
     但那是**色相差**（米黃 vs 中性灰）不是亮度差，實心色塊在這個量級一眼看得出來；
     而真正撐住可讀性的是它上面的文字 5.584:1（算式見第九節）。
   完整時序、對比算式與三態疊加的處理見本節第九節。 */
/* ★★★★2026-08-10（同日更晚，業主：「你這目前的黃色也太醜」）⇒ **本規則整條撤銷**。
   「目前」不再靠任何填色表達，訊號整個搬進一顆有輪廓的膠囊 chip（理由與算式見本節**第十節**）。
   ★原值記此存查（禁刪守則，逐字，這是被撤銷的整條規則本體）：
     `.reissue-seg-item--current {`
     `    background: hsl(var(--warning-bg));`
     `}`
   ★★為什麼是「撤銷填色」而不是「再換一顆填色」——這是本輪的根因判斷，不是偏好：
     本檔的中性階實際只有三格可用（`--muted`／`--secondary` #F5F5F5、`--border` #E5E5E5、
     `--muted-foreground` #737373，後者深到只能當框線／文字）。而這條軌道上
     **軌道自己已經佔走 `--muted` #F5F5F5**（★實測值，§S-67 把 §S-47 的 `--border` 撤回官方
     `--muted`，見本節第十節 (8)）、**選中片已經佔走 `--background` #FAFAFA**（本節第九節）
     ⇒ 給「目前」用的第四顆中性填色**根本不存在**，而 §S-42／§S-46／§S-47 三節都明令
     「不要自己另調第三顆灰」。
     第八節填 `--border` 只有 1.155:1 等於看不見、第九節只好跳去用黃色，
     根因都是同一個：**在一個只有三格的調色盤上硬要擠出第四格**。
   ⇒ 治本解是換維度：把「目前」的訊號從**填色**搬到**物件**（一顆自己帶輪廓的 chip），
     那條維度不受調色盤格數限制，也不必和軌道／選中片搶顏色。
   ★`.reissue-seg-item--current` 這顆 class **markup 端保留未刪**（禁刪守則）——
     它仍然是「哪一段是目前」的語意 hook，只是不再有任何樣式掛在它身上
     （與第八節 `.reissue-seg-dot` 同一種處理方式）。 */
/* ★★★2026-08-10 第十節 (7)：窄螢幕（≤430px）把這個元件的三個間距一起收緊。
   **這是實測發現的破口，不是預防性調整。**
   ── 實測（Chrome headless，viewport 390×900、isMobile）──
     加了 chip 之後 `.reissue-seg` 的 `scrollWidth` = 285px、`clientWidth` = 274px
     ⇒ **橫向溢出 11px**，第三段「自然人憑證」在進頁第一眼是被切掉的。
     （溢出本身在改版前就有、約 3px；本輪 chip 多了圖示 ＋ gap ＋ 內距，把它放大到 11px。）
   ── 治法與算式（★三個值一起下，因為只縮一個不夠——這是實測出來的，不是估的）──
     ⑴ 段左右內距 8px → **5px**（`.3125rem`）：三段 × 2 側 ＝ −18px
     ⑵ 段內 `gap`（chip 與段標籤之間）6px → **4px**：只有「目前」那一段有兩個子元素 ＝ −2px
     ⑶ chip 內 `gap`（圖示與「目前」兩字之間）4px → **3px** ＝ −1px
     ★第一次只下 ⑴ 的 6px 版本時實測仍然溢出（scrollWidth 279 vs clientWidth 274），
       所以才補到 5px 並加上 ⑵⑶。**改後實測值記在本輪驗收裡。**
   ★★本規則**覆蓋了誰、原值多少**：覆蓋 §S-67 ⑤ 的
     `.fb-tabs:not(.fb-tabs--flow):not(.fb-tabs--touch) > .fb-tab { padding-left/right: .5rem }`
     （原值 .5rem ＝ 8px ＝ shadcn 官方 `px-2`）。
   ★作用域**刻意加上 `.reissue-seg`**（特異度 (0,5,0) > §S-67 的 (0,4,0)）：
     全站另外 17 個 `.fb-tabs` 呼叫端一個字都不受影響 —— 它們沒有 chip，也沒有這個赤字。
   ★`:not(.fb-tabs--touch)` 原樣帶著：§S-67 排除 POS 側欄是為了 44px 觸控目標，
     那條理由與本規則無關但必須尊重（否則會意外把 POS 的觸控面積縮小）。
   ★★已知殘留（誠實記下，不是漏做）：更窄的 360px 機型仍會溢出約 29px。
     那要靠「刪字」或「讓段等寬換行」才治得掉，兩者都會傷到可讀性；
     §13 的軌道本來就有 `overflow-x:auto`，那是既有且刻意的退路（第七節 (2) 記過）。
     ⇒ 390（本輪指定的驗證寬度）與以上一律不溢出，360 退回橫捲。 */
/* ★★★★第三版（最終）：**不包 @media，無條件生效**。
   ── 為什麼前兩版是錯的（實測打臉，原文存查在下方）──
   第一、二版把這組值包在 `@media (max-width: 430px)` 裡，理由是「只有手機會擠」。
   ★實測推翻：`/result/{token}` 這支頁面把同一份表單**內嵌在 `col-lg-4` 窄欄裡**
     （見 `buyer/invoice/result.blade.php` 的 `@include('buyer.invoice._reissue_form')`），
     1440 桌面下那一欄只有 **311px**，軌道實測 `slack = -9.52px`、`scrollWidth > clientWidth`
     ⇒ **第三段一樣被切掉**，而 `@media` 看的是 viewport(1440)、**永遠不會生效**。
   ★★這正是 media query 的本質限制：**它量的是視窗，不是容器**。
     同一個元件被塞進窄欄時，viewport 條件完全幫不上忙。
   ── 為什麼不用 `@container` ──
     container query 需要在某個祖先上宣告 `container-type`，而那個祖先
     （`.reissue-type-fields` / `.card` / `col-lg-4`）都不在本輪授權範圍，
     且 `container-type: inline-size` 會建立新的包含塊、可能影響該卡內既有版面。
     為了一組內距去動祖先的排版模型，是壞交易。
   ── 治法：把緊湊值變成**唯一**的一組值（KISS ＋ SSOT）──
     桌面獨立頁（軌道 296px、可用空間充裕）本來就不需要寬內距 ——
     直接讓三種情境（獨立頁桌面／result 窄欄／手機）共用同一組值，
     就沒有「哪個情境沒被涵蓋」這種問題。實測三種情境全部不溢出（數字見第十節 (7)）。
   ★原值記此存查（禁刪守則，逐字，被取代的第二版整塊）：
     `@media (max-width: 430px) {`
     `    .reissue-seg.fb-tabs:not(.fb-tabs--flow):not(.fb-tabs--touch) > .fb-tab {`
     `        padding-left: .3125rem;`
     `        padding-right: .3125rem;`
     `        gap: .25rem;`
     `    }`
     `    .reissue-seg .reissue-seg-item .reissue-type-badge {`
     `        gap: .1875rem;`
     `        padding: .0625rem .25rem;`
     `    }`
     `}`
   ★★另一個實測抓到的坑，值得留著：第二版把 badge 那條寫成 (0,2,0)
     （`.reissue-seg-item .reissue-type-badge`），排在下面同分的那條**之前**
     ⇒ `gap` 生效了（它蓋的是 (0,1,0) 的 `.reissue-type-badge`）、
       但 `padding` **靜靜沒生效**（同分且在後者勝），量出來 badge 寬度一點都沒變。
     **同一條規則裡兩個屬性、一個成功一個失敗，而且零錯誤訊息** ——
     只驗其中一個屬性就會宣告成功。⇒ 覆蓋類的改動，**每一個屬性都要單獨量**。
     本版把選擇器留在 (0,5,0)/(0,3,0)，順序無關，不再依賴排列位置。 */
.reissue-seg.fb-tabs:not(.fb-tabs--flow):not(.fb-tabs--touch) > .fb-tab {
    padding-left: .3125rem;      /* 5px。覆蓋 §S-67 ⑤ 的 `.5rem`＝8px（官方 px-2）。(0,5,0) > (0,4,0) */
    padding-right: .3125rem;
    gap: .25rem;                 /* 4px。覆蓋 §13 `.fb-tab` 的 `gap: .375rem`＝6px。 */
}
/* ★選擇器刻意寫成 (0,3,0)（多掛一層 `.reissue-seg`）：本規則排在下面那條
   `.reissue-seg-item .reissue-type-badge` (0,2,0) 的**前面**，靠順序是贏不了的
   （見上方那段實測記錄）。多一層 class 讓它與排列位置無關。 */
.reissue-seg .reissue-seg-item .reissue-type-badge {
    gap: .1875rem;               /* 3px。覆蓋本節上方 `.reissue-type-badge` 的 `gap: .25rem`＝4px。 */
    padding: .0625rem .25rem;    /* 左右 4px。覆蓋下面同名規則的 `.0625rem .375rem`（6px）。
                                    ★4px 正是第八節的原值 —— 藥丸的圓弧會稍微貼近圖示，
                                      那是「三段都看得完整」與「chip 比例更漂亮」之間的取捨，
                                      本元件三種情境都吃緊，前者優先。 */
}

/* 「目前」chip 在分段控制項內的尺寸修正（本規則沿用未撤銷，只調一個值）。 */
.reissue-seg-item .reissue-type-badge {
    margin-left: 0;             /* 歸零 .reissue-type-badge 給卡片標題列用的 auto */
    font-size: .6875rem;        /* 11px：軌道只有 36px 高，12px 徽章會把片撐出換行 */
    /* ★2026-08-10 第十節：左右內距 4px → 6px。
       原值記此存查（禁刪守則，逐字）：`padding: .0625rem .25rem;`
       理由：本輪 chip 改成藥丸形（`.rounded-pill` 9999px）＋ 內含 lucide 圖示，
       4px 的左右內距會讓兩端的圓弧直接貼著圖示／文字，看起來像被裁掉的膠囊。
       6px ≒ chip 高度的一半，是藥丸形該有的比例。
       ★沒有加更多：本節第七節 (4) 那條「375px 手機三段塞不下」的寬度顧慮仍然成立，
         實測寬度見第十節 (5)。 */
    padding: .0625rem .375rem;
}
/* 鍵盤焦點：焦點在被 .btn-check 隱藏的 input 上，用相鄰兄弟把環畫到看得見的片上。
   值與檔尾 `.fb-tab:focus-visible`（§S-73 官方兩層）逐字相同。 */
/* ★★★2026-08-10：本規則**整條搬到第九節末尾**（原文逐字保留在下面存查，禁刪守則）。
     `.reissue-seg-input:focus-visible + .reissue-seg-item {`
     `    border-color: hsl(var(--ring));`
     `    box-shadow: 0 0 0 3px hsl(var(--ring) / .5);`
     `}`
   ★為什麼一定要搬：第九節新增的「選中片」規則要蓋掉 §S-52／§S-68 的 (0,4,0)，
     所以它的選擇器是 (0,5,0)；而這一條原本只有 (0,3,0)
     ⇒ **同時「被選中」又「有鍵盤焦點」時，焦點環會被選中樣式蓋掉**，
       鍵盤使用者會看不出焦點在哪。特異度贏過順序，把規則往後排是救不回來的。
   ⇒ 搬到第九節、選擇器補到 (0,5,0) 並排在選中規則之後。**兩個宣告的值一字未改。**
   ★這是無障礙功能，不能讓它輸給一個純視覺的狀態 —— 與第三節「功能不押在 :has() 上」
     同一條原則：a11y 的東西要贏。 */
/* 「目前狀態」那一段的中性灰點。`.fb-tab` 是 inline-flex ＋ gap:.375rem，
   所以這顆點不需要自己的 margin，間距由既有的 gap 負責。
   ★★2026-08-09 第八節：這顆灰點已被「目前」徽章取代（業主要求標記必須指得出是哪一種，
     而徽章是真的文字節點、螢幕閱讀器讀得到，灰點只是裝飾）。
     規則**保留不刪**（禁刪守則）：目前沒有呼叫端，值也沒有動過一個字。 */
.reissue-seg-dot {
    flex: 0 0 auto;
    width: .375rem;
    height: .375rem;
    border-radius: 50%;
    background: hsl(var(--muted-foreground));
}
/* 分段控制項下方那句「（目前狀態，無法選擇）」。
   它沿用改版前那一行的四個 class（.small .text-muted .ms-1 .reissue-type-note，禁刪守則），
   只把左內距歸零：`.reissue-type-note` 的左內距是為「卡片層級、要對齊標題文字」算的
   （1rem + 1rem + .625rem − .25rem），而這句現在坐在已經有 1rem 內距的 `.reissue-type-fields`
   裡，沿用會多推一次。★特異度同為 (0,1,0)，靠排在後面取勝，`.reissue-type-note` 原文未動。 */
.reissue-seg-note {
    padding: .5rem 0 0;
}

/* 「點改打勾」：只換這張卡上的 checked 指示器，§35 的全站規則一字未動（見上方 (5)）。
   SVG 逐字沿用 §35 `.form-check-input:checked[type="checkbox"]` 那一支（同一條 path、
   同一個 stroke-width、同一顆 --dark-foreground 色值）。 */
.reissue-type-input:checked[type="radio"] {
    background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fafafa' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3e%3c/svg%3e");
}
html.dark .reissue-type-input:checked[type="radio"] {
    background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%2318181b' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3e%3c/svg%3e");
}

/* ═══ 九、2026-08-10：移除「目前狀態」的 disabled 陷阱 ＋「目前」改黃色 ═══════════
   業主原話（三則，同一輪）：
     ①「二聯 三個切換會不順」
     ②「切換到自然人憑證後 會回不到手機條碼」
     ③「目前請你改為黃色」

   ── (1) ★★★根因：對應「目前狀態」的那一段被印成 disabled ＝ 單向陷阱 ──
   第七節 (4) 訂下的規則是「目前狀態那一段停用」，第八節照做。實際跑起來是這樣：
   目前是手機條碼 → 手機條碼那一段 disabled → 使用者點自然人憑證 → **回不去了**。
   三段裡永遠有一段是死路，這就是業主說的「切換會不順」。
   ★同樣的陷阱在三聯卡上一字不差地成立（$currentType === 'triple' 時整張卡 disabled
     ⇒ 切到二聯之後再也回不到三聯），本輪一併治掉。
   ⇒ **四顆 `invoice_type` 的 `disabled` 全部移除**（view 端改印 `data-current`）。
     「選回目前狀態」語意上等於「我不改了」，那是合法操作，不該被禁止。
   ★防呆沒有消失，只是換位置：**送出鈕**在「選中值＝目前狀態」時停用並說明原因。
     後端本來就有同一條防線（InvoiceReissueController 的「請選擇與目前不同的發票類型」），
     所以移除 disabled 不會讓錯誤資料進到後端；本輪一行 controller 都沒改。
   ★★連帶的樣式修正：三聯卡的 class 由 `.reissue-type-card--disabled` 改成
     `.reissue-type-card--current`。這不只是改名 —— `--disabled` 掛著
     `cursor:not-allowed`、`min-height:auto`（把 52px 觸控目標拿掉）、以及
     `:not(--disabled)` 的 hover 排除，卡片現在點得下去，那三件事全部變成錯的。
     換 class 之後三者自動回到「一般可選卡」，`--disabled` 的三條規則**原文一行未動**
     （只是沒有呼叫端了，禁刪守則下保留，與第八節的 `.reissue-seg-dot` 同一個處理方式）。
     ⇒ 觸控目標回到 52px —— 這頁的使用者是「在店裡用手機掃 QRCode 進來」的買家，
       WCAG 2.5.5 那條不能少。

   ── (2) ★★★三態疊加：移除 disabled 之後，「目前」與「選中」會同時發生 ──
   這是本輪唯一真正需要設計判斷的地方。以前「目前」那一段點不到，所以
   「目前 ＋ 選中」這個組合**不存在**；現在它是使用者最可能做的動作之一
   （點回去看看、或反悔）。三種狀態必須同時可辨、而且不能互相蓋掉。
   ⇒ 解法是**沿用第六節 (7) 已經定下的維度分工**，只把「目前」那一維的顏色換成黃：
       **背景色 ＝ 目前**（黃／不黃）
       **框線＋陰影 ＝ 選中**（--dark 實框＋浮起／無）
     兩個維度落在不同的 CSS 屬性上 ⇒ 它們**在物理上不可能互相覆蓋**，
     四種組合各自長得不一樣：
       一般・未選   透明（露出軌道 --border #E5E5E5）   無框無影
       一般・選中   --background #FAFAFA 白片            --dark 1px 框 ＋ 浮起陰影
       目前・未選   --warning-bg #FEF3E2 淡黃            無框無影         ＋「目前」黃徽章
       目前・選中   --warning-bg #FEF3E2 淡黃            --dark 1px 框 ＋ 浮起陰影 ＋ 黃徽章
   ★★「目前」的黃**不會因為被選中而消失**（背景色這一維整個屬於「目前」），
     這正是與 2026-08-09 那版最大的差別：那版的黃如果存在，一被選中就會被
     `.fb-tab.active` 的白底吃掉，使用者會以為自己把「目前」弄不見了。
   ★★「目前」還有第三層、不依賴顏色的線索：**「目前」兩個字的徽章**（真文字節點，
     螢幕閱讀器讀得到）。WCAG 1.4.1（Use of Color）要求狀態不可只靠顏色傳達，
     這一層在四種組合裡都在。
   ★`.fb-tab` 出廠就帶 `border: 1px solid transparent`（§13），所以「選中加框」
     **不會造成任何位移**（§S-68 為了同一件事特地用 border-color 而不是 border）。

   ── (3) 色票：只借既有 token，一顆新色都沒有調 ──
     `--warning`            37.7 92.1% 50.2% = #F59E0B（亮暗同值，暗色未覆蓋）
     `--warning-bg`         亮 38 92% 94% = #FEF3E2 ／ 暗 38 50% 16% = #3D2E14
     `--warning-foreground` 0 0% 10.2% = #1A1A1A（亮暗同值，暗色未覆蓋）
   ★徽章的「黃底 ＋ 近黑字」就是本檔 L1034 `.badge.bg-warning` 那條的同一組配對（SSOT）。
   ★★★但**不可以**直接用 `class="badge bg-warning"`：本檔 L5136 有一條後到的同名規則
     把它換成「`--warning-bg` 淡底 ＋ `--warning-fg` 深赭字」，而 `--warning-fg`(#985910)
     正是業主 2026-08-09 明確否決的那顆（「我不要這個深豬 好醜」，第六節 ④）。
     ⇒ 只借 token、不借 class。**`--warning-fg` 在本元件全域禁用，這一輪也沒有恢復它。**
   ★`--primary`(#FFD200 品牌黃) 一樣不用：對白底 1.452:1（第六節 (2) 算過），
     而且它是「主要動作」的語彙（送出鈕就是它），拿來標狀態會與按鈕打架。

   ── (4) ★★★對比實算（WCAG 2.x：L=0.2126R+0.7152G+0.0722B，sRGB 先解 gamma；
          比值=(L亮+0.05)/(L暗+0.05)；半透明字先與底色合成再算）──
   【亮色】
     一般・未選 片字 rgba(0,0,0,.6) 合成於軌道 #E5E5E5 → #5C5C5C   對 #E5E5E5 = 5.341:1 ✓
     目前・未選 片字 rgba(0,0,0,.6) 合成於 #FEF3E2     → #66615A   對 #FEF3E2 = 5.584:1 ✓
     一般・選中 片字 --foreground #000000              對 #FAFAFA           = 20.119:1 ✓
     目前・選中 片字 --foreground #000000              對 #FEF3E2           = 19.133:1 ✓
     徽章字 --warning-foreground #1A1A1A               對徽章底 #F59E0B     =  8.104:1 ✓
     徽章邊框 #1A1A1A vs 目前片底 #FEF3E2（圖形界線 3:1）                  = 15.857:1 ✓
     徽章邊框 #1A1A1A vs 卡片白底 #FFFFFF（圖形界線 3:1）                  = 17.404:1 ✓
     選中框 --dark #171717 vs 目前片底 #FEF3E2（UI 邊界 3:1）              = 16.334:1 ✓
     選中框 --dark #171717 vs 一般選中片底 #FAFAFA（UI 邊界 3:1）          = 17.176:1 ✓
     卡片標題列 --foreground #000000 於 --warning-bg #FEF3E2               = 19.133:1 ✓
   【暗色（買家殼層目前沒有 html.dark，這一組是紙上驗證，與第七節 (5) 同一個處理）】
     目前・未選 片字 --muted-foreground #A1A1A1 於 #3D2E14                 =  5.082:1 ✓
     目前・選中 片字 --foreground #FAFAFA       於 #3D2E14                 = 12.580:1 ✓
     徽章字 #1A1A1A 於 #F59E0B（兩顆 token 暗色都沒被覆蓋，維持同值）      =  8.104:1 ✓
   【★被否決的兩種寫法，算出來的數字記此存查，不要再試】
     --muted-foreground #737373 於 --warning-bg #FEF3E2 = 4.320:1 ✗（不到 4.5，
       這正是第四節警告過的那個坑：黃底一墊上去，吃 .text-muted 的說明文字就掉下去）
     --warning #F59E0B 於 --warning-bg #FEF3E2          = 1.957:1 ✗✗（黃字配淡黃底，
       幾乎看不見）
   ⇒ 可行解只有一條：**淡黃底 ＋ 近黑字**。黃負責「一眼認出這是目前」，
     近黑字負責「讀得清楚」—— 這兩件事本來就該由不同深淺承擔。

   ── (5) ★★★卡片為什麼只填「標題列」、不填整張卡 ──
   第四節與第六節 (3) 都算過同一件事：卡片一旦墊上底色，**收在卡內的
   `.text-muted` 說明文字（`#carrier_hint` / `#tax_id_hint` /「（目前狀態…）」那句）
   就會掉到 4.320:1**，把 2026-08-07 那次無障礙修正原地打回去。
   業主的顏色偏好可以改，**對比度門檻不會跟著放寬**。
   ⇒ 黃色只上在 `.reissue-type-hit`（標題列）：那一列裡只有標題（`--foreground` 純黑，
     19.133:1 ✓）與徽章，**一個 `.text-muted` 都沒有**；說明句是它的兄弟節點、
     欄位區塊在 `.reissue-type-body` 裡，兩者都留在白色卡面上，
     對比值與本輪之前**逐字相同**（4.741:1），零風險、零 `!important`。
   ★hover 刻意**不改色**：`.reissue-type-card:not(--disabled) .reissue-type-hit:hover`
     會把標題列刷成 `--accent` 灰，那會讓「目前」的黃在滑鼠經過時消失一下（狀態閃爍）。
     ⇒ 補一條同特異度 (0,4,0)、排在後面的規則把黃色釘住。
     代價是這一列沒有 hover 變色；但本頁的主要裝置是手機（沒有 hover），
     而且卡片可點這件事由 `cursor:pointer` ＋ 整卡框線的即時變化承擔，不靠底色。

   ── (6) ★★選中態由 JS 的 `.active` 改成純 CSS 的 `:checked + label` ──
   第七節當初選 `.active` 的理由是「主題日後調整選中片外觀，這張卡自動跟著走」，
   那個理由**現在依然成立**，本輪是明知而為的取捨，把代價寫清楚：
     ★改的理由不是效能。`change` 是同步事件、在下一次繪製之前就跑完了，
       原本那段 `classList.toggle('active', …)` **從來沒有造成過可見的延遲**
       （我讀過流程才寫這句，不要把它當成效能修正）。
     ★真正的理由是 **SSOT**：`.active` 與 radio 自己的 `:checked` 是同一個事實的
       兩份紀錄。兩份就有不同步的一天，而不同步的長相是「兩片同時看起來被選中」
       且**零錯誤訊息**。純 CSS 之後 `:checked` 是唯一真相，不可能不同步。
     ★★代價（誠實記下）：本節必須把 `.fb-tab.active` 的四個生效宣告
       （§13 的 background / color，§S-68 的 border-color / box-shadow）
       複製一份到本元件作用域裡 ⇒ **全站頁籤日後改樣式，這張卡不會自動跟著走**。
       全站的 `.fb-tabs` / `.fb-tab` / `.fb-tab.active` **一行都沒有動**
       （`_query_tabs.blade.php` 等其他呼叫端完全不受影響）。
     ★選擇器為什麼要寫成 `.fb-tabs:not(.fb-tabs--flow) > …`：要蓋掉的是
       §S-52 與 §S-68 的 `.fb-tabs:not(.fb-tabs--flow) > .fb-tab.active` (0,4,0)，
       所以本節的選中規則必須 ≥(0,5,0)。加上 `:checked` 之後正好是 (0,5,0)，
       同分靠排在後面取勝；`html.dark …` 那條 (0,4,1) 也一併贏過（b: 5 > 4）。
     ★★★焦點環必須贏過選中樣式，所以第八節那條 (0,3,0) 的
       `.reissue-seg-input:focus-visible + …` 整條搬到本節末尾、補到 (0,5,0)
       並排在選中規則之後（原文逐字存查在原位置，兩個宣告的值一字未改）。
       特異度贏過順序 —— 不搬的話「已選中 ＋ 有鍵盤焦點」時焦點環會被蓋掉。

   ── (7) 「切換不順」實際找到幾個原因、哪些**刻意不處理** ──
   ✔ 已處理 ①「目前」那一段／那一張卡是死路（本節 (1)）← 業主回報的主症狀
   ✔ 已處理 ②「目前」的填色其實是隱形的：第八節填 `--border`，而 §S-47 早把軌道
              也改成 `--border` ⇒ 兩者同色、1.000:1（見該宣告上方的存查註解）。
              使用者看不出「我現在在哪一段」，切換自然沒有著力點。
   ✔ 已處理 ③ 點停用的那一段時**完全沒有任何回饋**（disabled 的 label 連 :hover 都不會動），
              使用者會以為是頁面卡住而不是「這一項不能選」。移除 disabled 後不存在了。
   ✔ 已處理 ④ 三聯卡在 `--disabled` 下 `min-height` 被降到 auto，觸控目標小於 44px。
   ✘ **刻意不處理**：切「不綁載具」時 `#block_carrier` 收起造成的**卡片高度跳動**。
       理由三條，寫清楚免得日後有人以為是漏做：
       (a) 要做高度過渡就必須放棄 `.style.display` —— `display:none` 上的 transition
           不會跑。而那一行正是 7ec7ed40 修過一次的地方，view 與腳本兩邊都留著
           「不要改成 class」的警告。為了一個動畫去重寫它，在一件急件上是壞交易。
       (b) **會動的是控制項下方的內容，不是控制項本身**。分段控制項排在載具欄位
           上面，手指按著的那一片位置完全沒變 —— 這是 progressive disclosure 的
           正常行為，不是「不順」的來源。
       (c) 加 150～200ms 的高度動畫反而會**讓操作感覺變慢**：片的選中態是瞬間的、
           面板卻慢慢長出來，兩條時間軸打架，比直接跳更難受。
       ★真的要做，正解是 `grid-template-rows: 0fr → 1fr`，但那要連 view 與腳本
         一起改，屬於另一輪的工作。
   ★`prefers-reduced-motion`：本節**沒有新增任何動畫或過渡**，所以既有的
     第六節末尾那條 reduce 區塊已經涵蓋（`.reissue-type-card` / `.reissue-type-hit`）。
     `.fb-tab` 自己的 `transition: color/background-color/box-shadow .15s`（§13）
     是全站宣告，本輪不動它（改它會影響全站 17 個頁籤呼叫端）。
   ------------------------------------------------------------------------------ */

/* ═══ 十、2026-08-10：「目前」標記由「黃色填塊」改成「中性藥丸 chip」═══════════════
   業主原話（三則，同一件事）：
     ①「你這目前的黃色也太醜 請你參考 `/good/storageLocation` 這邊的設計」
     ②「使用 SHADCN」
     ③「做倒底 治本」

   ── (1) 破口：第九節的黃色，在**分段控制項**這個容器裡註定會醜 ──
   第九節的推導沒有算錯（那些對比數字本輪重算過，全部吻合），它錯在**沒有問「這顆黃
   要坐在什麼東西上」**。它做出來的實際長相是：
     · 段底 `--warning-bg` #FEF3E2 一整塊淡琥珀，坐在一條中性灰的軌道裡
     · 段內再壓一顆 `--warning` #F59E0B **實心橘黃** ＋ 近黑描邊的徽章
   ⇒ 三段中性灰的分段控制項裡突然有一塊琥珀 ＋ 一顆高彩度橘黃 chip，
     視覺上讀起來是「**這裡有一則警告**」，不是「這是你目前的狀態」。
   ★而且那顆實心橘黃是**沒得選**才長成那樣的：`--warning`(#F59E0B) 對它坐著的兩種底
     （#FEF3E2 是 1.957:1、白卡是 2.148:1）都不到圖形界線的 3:1，所以必須補一圈近黑描邊
     才過關（第九節自己就是這樣寫的）。「高彩度色塊 ＋ 近黑硬邊」在 11px 的尺寸下
     必然刺眼 —— 醜是那條約束的**結果**，不是配色沒調好。

   ── (2) 業主指定的參考設計：`/good/storageLocation` 的「儲位狀態」欄 ──
   已讀原始碼（`resources/views/good/storageLocation/index.blade.php` 的「儲位狀態」
   那顆 `<span>`，以及 `app/Enums/StorageLocationStatusEnum.php`）：
     markup：`<span class="btn btn-xs btn-{status_color} btn-rounded rounded-pill">`
             `    <i data-lucide="{circle-check|circle-x}"></i> {status_label}</span>`
     實際生效值（**不是照抄 markup，是往回查了本檔真正吃到的規則**）：
       · 形狀 `.rounded-pill` → §20 `border-radius: 9999px !important`（藥丸）
       · 尺寸 `.btn.btn-xs`  → §補位 `font-size:12px; padding:2px 6px`
       · 顏色 `btn-success/info/warning/danger` → §補位 4「狀態 chip 的語意色還原」那組
              `span.btn.rounded-pill.btn-*`：**淡底 ＋ 同色系深字 ＋ 同色系 25% 透明描邊**
       · 若**沒有**語意 class → `span.btn.rounded-pill`（§24 上方那條）：
              底 `--secondary`、框 `--border`、字 `--secondary-foreground` ＝ **中性 chip**
   ⇒ 這個參考設計真正的語彙是：**藥丸形 ＋ lucide 圖示 ＋ 文字**，而且它的預設（無語意）
     長相就是**中性 chip**，不是實心色塊。本節照抄的是這個語彙。

   ── (3) 顏色收斂：為什麼最後是中性，而且這次不會再被推翻 ──
   業主已經**連續否決過三顆**：`--warning-fg` #985910 深赭（「我不要這個深豬 好醜」）、
   `--dark` 黑框（「你做得好糟」的那一輪連帶）、`--warning` #F59E0B 實心橘黃（本輪「也太醜」）。
   ★而 ②「使用 SHADCN」是**同一則指示裡的第二句**：shadcn 的語意色只有 `destructive`
     一顆有色相，「目前 / 現況」這種狀態標記走的是 `Badge variant="secondary"`／`"outline"`
     ——中性。第六節 (7) 早就得出過同一個結論。
   ★★★真正的**治本**判斷（③「做倒底」）：把可用色票列出來就會發現這題**只有中性一條路**——
       `--primary` #FFD200  對白底 1.452:1，且是「主要動作」語彙（送出鈕），拿來標狀態會打架
       `--warning` #F59E0B  對它要坐的兩種底 1.957 / 2.148，**低於 3:1**，非補近黑硬邊不可 ⇒ 已被否決
       `--warning-fg` #985910 業主明確否決，永久禁用（第六節 ④，本輪未恢復）
       `--warning-bg` #FEF3E2 對**實測**軌道 #F5F5F5 只有 1.007:1（見 (8)），當填色幾乎看不出來
       `--dark` #171717     已被「選中」那一維佔用，再用一次就是第六節 (1) 否決過的混淆
     ⇒ 有彩度的候選**全部出局**，出局理由是**對比度與語彙衝突**，不是偏好。
       這一段寫下來就是為了讓下一輪不必再繞第八次：**顏色不是這題的解**。
   ⇒ 解法改走 shadcn 自己的做法：**用「物件」而不是「顏色」表達狀態** ——
     一顆自己帶輪廓的 chip，中性色，靠**形狀＋圖示＋文字**被認出來。

   ── (4) ★★★對比實算（WCAG 2.x：L=0.2126R+0.7152G+0.0722B，sRGB 先解 gamma；
          比值=(L亮+0.05)/(L暗+0.05)；半透明先與底合成再算。四種狀態組合逐一列出）──
   ★★★色值全部取自**瀏覽器實測**的 `getComputedStyle`（Chrome headless 1440，
     `https://inv.2025-seller.mtest/…/reissue`），不是從 token 定義推的 —— 原因見 (8)。
   色值：軌道 `--muted` **#F5F5F5**（★實測，不是 --border，見 (8)）
         選中片 `--background` #FAFAFA（實測 rgb(250,250,250)）／卡片 `--card` #FFFFFF
         chip 底 `--secondary` #F5F5F5（實測 rgb(245,245,245)）
         chip 字 `--secondary-foreground` #171717（實測 rgb(23,23,23)）
         chip 框 `--muted-foreground` #737373（實測 rgb(115,115,115)）／選中框 `--dark` #171717（實測）
   【亮色・四種狀態】
     ① 一般・未選   片字 rgba(0,0,0,.6)→#626262 於軌道 #F5F5F5            =  5.595:1 ✓(≥4.5)
                    無 chip、無框、無影
     ② 一般・選中   片字 #000000 於白片 #FAFAFA                            = 20.119:1 ✓
                    選中框 #171717 vs 白片 #FAFAFA（UI 邊界 ≥3:1）         = 17.176:1 ✓
                    選中框 #171717 vs 軌道 #F5F5F5（片外側那半圈）          = 16.444:1 ✓
     ③ 目前・未選   片字 rgba(0,0,0,.6)→#626262 於軌道 #F5F5F5            =  5.595:1 ✓
                    ★chip 框 #737373 vs 軌道 #F5F5F5（圖形界線 ≥3:1）      =  4.349:1 ✓
                    chip 框 #737373 vs chip 底 #F5F5F5（同上，內側）        =  4.349:1 ✓
                    chip 字 #171717 於 chip 底 #F5F5F5                      = 16.444:1 ✓
                    ★★chip **底** #F5F5F5 vs 軌道 #F5F5F5 ＝ **1.000:1** ——
                      這一格**刻意**不當辨識來源。這正是 shadcn `Badge variant="outline"`
                      的長相：底與周圍同色、**輪廓**才是那顆 chip。所以本輪的可見性
                      100% 押在 4.349:1 的描邊上，不押在填色上（(6) 有完整理由）。
     ④ 目前・選中   片字 #000000 於白片 #FAFAFA                            = 20.119:1 ✓
                    選中框 #171717 vs 白片 #FAFAFA                          = 17.176:1 ✓
                    ★chip 框 #737373 vs 白片 #FAFAFA（圖形界線 ≥3:1）      =  4.543:1 ✓
                    chip 字 #171717 於 chip 底 #F5F5F5                      = 16.444:1 ✓
     ⑤（第五種位置）三聯卡標題列上的同一顆 chip：
                    chip 框 #737373 vs 卡片白 #FFFFFF                       =  4.742:1 ✓
                    卡片標題 #000000 於 #FFFFFF                             = 21.000:1 ✓
     ★兩種框線要分得開（不能讓使用者把「目前」的 chip 框看成「選中」的片框）：
       chip 框 #737373 vs 選中框 #171717                                    =  3.781:1 ✓
   【暗色（買家殼層目前沒有 html.dark，紙上驗證，與第七節 (5) 同一個處理）】
     暗軌道＝`--border`(白 10%) 疊在 `--card` #171717 上 → 合成 #2E2E2E
     片字 `--foreground`/.6 → #A8A8A8 於 #2E2E2E                            =  5.711:1 ✓
     chip 框 #A1A1A1 vs 暗軌道 #2E2E2E                                      =  5.256:1 ✓
     chip 框 #A1A1A1 vs chip 底 #262626                                     =  5.857:1 ✓
     chip 字 #FAFAFA 於 chip 底 #262626                                     = 14.499:1 ✓
     chip 框 #A1A1A1 vs 暗選中片 #0A0A0A                                    =  7.663:1 ✓
     chip 框 #A1A1A1 vs 暗卡 #171717                                        =  6.939:1 ✓
   【★被否決的寫法，數字記此存查，不要再試】
     chip 框吃 `--border` #E5E5E5 vs 實測軌道 #F5F5F5 = **1.155:1 ✗✗**（差 10／255，
       在 1px 髮絲線上等於看不見）← shadcn 官方 `Badge variant="outline"` 的框用的正是
       `--border`，**在這條軌道上不能照抄**，這也是本節框色改吃 `--muted-foreground` 的唯一理由
     `--warning` #F59E0B vs #FEF3E2 = 1.957:1 ✗／vs #FFFFFF = 2.148:1 ✗（第九節那顆實心橘黃，
       兩個方向都不到 3:1，只能靠近黑硬邊補救 ⇒ 醜的來源）
     `--warning-bg` #FEF3E2 當段底 vs 實測軌道 #F5F5F5 = **1.007:1**（★第九節那塊淡黃填色
       在**亮度上幾乎完全等於軌道**，它能被看見純粹靠色相差。這是「填色維度已經滿了」
       最直接的證據 —— 見 (6)）

   ── (8) ★★★實測推翻了第八／第九節共用的一個前提：軌道**不是** `--border` ──
   第八節與第九節都寫「§S-47 早就把軌道改成 `.fb-tabs{background:hsl(var(--border))}`」，
   並據此判定「目前段填 --border ⇒ 與軌道同色 ⇒ 1.000:1 隱形」。
   ★本輪用 `getComputedStyle` 實測，`.reissue-seg`（即 `.fb-tabs`）的
     `background-color` 是 **rgb(245,245,245) ＝ `--muted` #F5F5F5**，不是 #E5E5E5。
   ★原因：**§S-67**（「頁籤 .fb-tabs / .fb-tab 完全回歸 shadcn 官方值」）的
     `.fb-tabs:not(.fb-tabs--flow) { background: hsl(var(--muted)); }` 特異度 (0,2,0)，
     已經把 §S-47 那條 (0,1,0) 的 `--border` 軌道**撤回官方值**了 —— §S-67 的對照表
     第②項就寫著「軌道背景 hsl(--border)（§S-47）→ hsl(--muted)」。
     第八／第九節引用 §S-47 時沒有往後讀到 §S-67。
   ⇒ 修正兩件事（**兩節原文一字未刪，只在這裡更正結論**）：
     · 第八節填 `--border` 的實際對比是 **1.155:1**（不是 1.000:1）—— 仍然幾乎看不見，
       所以「那塊填色從來沒真的顯示過」這個**結論是對的**，只是理由不同
       （不是「與軌道同色」，是「差 10／255 太小」）。
     · 第九節說「換黃色同時也修掉了這個隱形 bug」——實測 #FEF3E2 對 #F5F5F5 是 **1.007:1**，
       亮度差比 `--border` 版**更小**。它能被看見完全靠色相；一旦色相被否決（本輪），
       填色這條路就徹底沒有剩餘空間了。
   ★★方法論教訓（比數字本身值錢）：**引用一顆 token 的值，不等於那顆 token 真的贏了**。
     這個檔案 1.2MB、`.fb-tabs` 的 background 有三條規則在搶，只有跑一次
     `getComputedStyle` 才知道誰贏。本節所有色值因此都標了「實測」。

   ── (5) ★★圖示選 `circle-dot` 而不是參考設計的 `circle-check` —— 語意撞車 ──
   `/good/storageLocation` 用的是 `circle-check`（勾）。**這裡不能照抄**：
   本節第七節 (5) 已經把這張卡的 `:checked` radio 指示器換成**勾**
   （`.reissue-type-input:checked[type="radio"]` 那條 inline SVG，路徑 `d='m6 10 3 3 6-6'`），
   而那個勾的語意被明確定義成「**你選了要改成這個**」——同一節還特地寫了
   「勾的語意是『你選了要改成這個』，而目前狀態是『你已經是這個』，兩件事不該共用同一個符號」。
   ⇒ 「目前」的 chip 再用一次勾，會讓同一個畫面上出現兩種意思完全相反的勾。
   ★改用 lucide `circle-dot`（外圈 ＋ 實心中心點）：
     · 它就是「radio 已設定 / 你在這裡」的通用符號 —— 語意正好是「現況」而不是「我選了」
     · 與參考設計仍是**同一個 `circle-*` 家族**，形狀語彙沒有走味
     · 在 11px 下與勾的筆畫形狀差異極大（同心圓 vs 折線），縮到最小也不會看錯
   ★已確認本站 `public/assets/lucide/lucide.min.js`（lucide v1.24.0）內含 `CircleDot`；
     買家外殼 `buyer/layout.blade.php` 本來就載入這支並在 DOMContentLoaded 呼叫
     `window.lucide.createIcons()`（該檔內註解已寫明「本頁只需要 lucide 這一支」）。
     chip 是**伺服器端印出來的靜態節點**，不需要任何額外的 JS 時序處理。
   ★萬一 lucide 沒載到：`<i data-lucide>` 被 §38 撐成 1em×1em 的空盒，chip 仍然是
     「藥丸 ＋『目前』兩個字」——**狀態資訊一個字都不會消失**（圖示是裝飾，帶 aria-hidden）。

   ── (6) 為什麼「目前」的段底、卡片標題列底色**整個拿掉**（不是換一顆淡一點的）──
   理由寫在 `.reissue-seg-item--current` 撤銷註解裡（中性階只有三格、軌道與選中片已各佔一格，
   第四格不存在）。這裡補上**設計面**的理由：
     · 參考設計 `/good/storageLocation` 的狀態欄**本來就沒有列底色** —— 一顆 chip 就是全部。
     · 「填色」這一維在本元件其實已經被 §13/§S-67 的「軌道 #F5F5F5 vs 選中白片 #FAFAFA」用滿了
       —— 這兩顆本身只差 5／255，中間**沒有空位**；硬要再插一層，不是撞到軌道就是撞到選中片
       （第八節的 `--border` 撞軌道 1.155:1、第九節的淡黃撞軌道 1.007:1，見 (8)）。
     · 移除之後，維度分工變得比第九節更乾淨、而且**不再互相搶屬性**：
         **「目前」＝ 有沒有那顆 chip（物件的有無）**
         **「選中」＝ 片底 ＋ 框 ＋ 陰影（表面的變化）**
       兩者一個是「多了一個東西」、一個是「這片變了」，物理上不可能互相覆蓋，
       四種組合各自長得不一樣（見 (4) 的 ①～④）。
   ★WCAG 1.4.1（Use of Color）：「目前」在四種組合裡都由**「目前」兩個字的真文字節點**承載
     （chip 內），不依賴顏色也不依賴圖示 —— 這一層第九節就有，本輪原樣保留。

   ── (7) 寬度：藥丸 ＋ 圖示會不會把 375px 手機的三段擠爆 ──
   第七節 (4) 記過「36px 軌道塞不下 12px 徽章」的顧慮，本輪 chip 又多了一顆 11px 圖示 ＋ gap。
   處理：chip 維持 11px 字級（第八節的決定，未動），桌面左右內距 4px → 6px（藥丸比例），
   窄螢幕再退回 4px 並收緊兩個 gap（見上方 @media 區塊的算式）。
   ★★不靠推算，**兩個寬度都實測過**（`getBoundingClientRect` / `scrollWidth` vs `clientWidth`）：
       1440 桌面：軌道 296px、三段 73/130/87、chip 50.6×15 → `scrollWidth`(296) ＝ `clientWidth`(296)，**不溢出**
       390 手機（改前）：`scrollWidth` 285 vs `clientWidth` 274 → **溢出 11px**（第三段被切）
       390 手機（只縮內距到 6px）：279 vs 274 → **仍溢出 5px**（★所以才有第二輪）
       390 手機（最終：內距 5px ＋ 段 gap 4px ＋ chip gap 3px ＋ chip 內距 4px）：
                 `scrollWidth` 271 ＝ `clientWidth` 271 → **不溢出**，且 `document` 無橫捲
   ★軌道本來就有 `overflow-x:auto`（§13），真的超出也是既有的橫捲行為，不會破版；
     360px 以下即為此情況（見 @media 區塊的「已知殘留」）。
   ------------------------------------------------------------------------------ */

/* 「目前」那張卡：黃只上在標題列（理由與對比算式見第九節 (5)）。
   ★標題色不覆蓋 —— `.reissue-type-card--disabled .reissue-type-title` 那條「轉灰」
     已經沒有呼叫端（原文保留未動）。轉灰是為「這張卡不能選」設計的訊號，
     卡片現在可以選，留著會讓使用者以為它是停用的。標題吃回 `--foreground`
     （`.reissue-type-title` 第二節的原始宣告），於 #FEF3E2 上 19.133:1 ✓。 */
/* ★★★★2026-08-10（同日更晚，業主：「你這目前的黃色也太醜」）⇒ **這兩條規則整組撤銷**，
   三聯卡的標題列不再墊黃底，「目前」一律由標題列右緣那顆中性 chip 表達（第十節）。
   ★原值記此存查（禁刪守則，逐字，被撤銷的兩條規則本體與它們的說明）：
     `.reissue-type-card--current .reissue-type-hit {`
     `    background: hsl(var(--warning-bg));`
     `}`
     `/` `* hover 時把黃色釘住，不要讓 --accent 灰把「目前」的顏色訊號洗掉（第九節 (5)）。`
     `   ★特異度 (0,4,0) 與 .reissue-type-card:not(.reissue-type-card--disabled)`
     `     .reissue-type-hit:hover 同分，靠排在後面取勝；那條原文一字未動。 *` `/`
     `.reissue-type-card.reissue-type-card--current .reissue-type-hit:hover {`
     `    background: hsl(var(--warning-bg));`
     `}`
   ★★兩條**必須一起**撤銷，不能只撤第一條：第二條是為了「hover 時把黃釘住」而生的，
     底色沒了之後它會變成「滑過時**憑空**刷出一片黃」——比原本更糟。
   ★★撤銷後的連帶好處（不是副作用，是治本的一部分）：`.reissue-type-hit:hover` 的
     `--accent` 灰底重新對這張卡生效。第九節 (5) 當初為了保住黃色**刻意犧牲**了這張卡的
     hover 回饋（原文：「代價是這一列沒有 hover 變色」）——那個代價現在自動還回來了，
     因為第九節 (1) 已經把 disabled 拿掉、這張卡本來就點得下去，理當有 hover 回饋。
   ★卡內兩條 `.text-muted`（`#carrier_hint` / `#tax_id_hint` / 那句說明）在整輪中
     **一個字、一個顏色都沒有動過**：第四節「只填標題列、不填整張卡」的既有決策仍然成立，
     而本輪連標題列都不填了 ⇒ 那兩條 muted 文字仍然坐在白卡面上，
     維持 2026-08-07 那次修正後的 4.741:1 ✓。 */

/* 分段控制項「選中」＝浮起：--dark 實框 ＋ v4 shadow-sm ＋ 文字轉深。
   ★**刻意不設 background** —— 背景色那一維整個留給「目前」（第九節 (2)）。
   ★三個值分別逐字取自 §13 `.fb-tab.active`（color）與 §S-68
     `.fb-tabs:not(.fb-tabs--flow) > .fb-tab.active`（box-shadow）；
     border-color 由 §S-68 的 transparent 換成 --dark，這是本節唯一「新增」的視覺。
   ★(0,5,0)：要贏過 §S-52／§S-68 的 (0,4,0) 與 §S-68 暗色的 (0,4,1)。 */
/* ★★★2026-08-10 第十節：本規則**新增一行 `background`**（原本刻意不設，見上一行原註解）。
   ★這是本輪**唯一一個功能性 bug 修正**，不是視覺調整：
     「目前」段的黃底撤掉之後，若不把白片底補進來，「目前・選中」這個組合會變成
     **完全沒有任何選中外觀的底**（只剩框與陰影），使用者點回目前那一段時會以為沒點到。
   ★值 `hsl(var(--background))` 逐字取自下方那條被合併掉的規則（原文存查在下方註解），
     不是新調的。合併之後「選中＝白片底 ＋ --dark 框 ＋ 浮起」四個宣告在同一條規則裡，
     選中的定義只有一處（SSOT）。 */
.fb-tabs:not(.fb-tabs--flow) > .reissue-seg-input:checked + .reissue-seg-item {
    color: hsl(var(--foreground));
    background: hsl(var(--background));
    border-color: hsl(var(--dark));
    box-shadow: 0 1px 3px 0 rgb(0 0 0 / .1), 0 1px 2px -1px rgb(0 0 0 / .1);
}
/* ★★★★2026-08-10 第十節：**本規則整條撤銷並併入上一條**。
   ★原值記此存查（禁刪守則，逐字，包含它原本的說明）：
     `/` `* 白片底只給**一般**段：「目前」段的底永遠是黃，選中與否都一樣（第九節 (2)）。`
     `   ★用 :not(.reissue-seg-item--current) 而不是靠特異度互相打架 ——`
     `     兩條規則的適用範圍互斥，讀起來就是「誰的底歸誰管」，不必猜誰蓋掉誰。 *` `/`
     `.fb-tabs:not(.fb-tabs--flow) > .reissue-seg-input:checked + .reissue-seg-item:not(.reissue-seg-item--current) {`
     `    background: hsl(var(--background));`
     `}`
   ★為什麼一定要撤掉那個 `:not()`：它存在的**唯一**理由是「目前段的底永遠是黃、
     不能被白片底蓋掉」。黃底撤銷之後（見上方 `.reissue-seg-item--current` 的存查註解），
     這個 `:not()` 就從保護變成傷害 —— 它會讓「目前・選中」是三段裡**唯一**拿不到
     白片底的那一段。★這是「撤掉一個顏色」必須連帶檢查的第二處，漏掉就會做出一個
     「點得到但看起來沒被選中」的死角。 */

/* 鍵盤焦點（由第八節整條搬來，兩個宣告的值一字未改，見原位置的存查註解）。
   ★選擇器補到 (0,5,0) 並**排在選中規則之後** —— 焦點是無障礙功能，
     必須贏過純視覺的選中樣式，否則「已選中 ＋ 有鍵盤焦點」時環會被蓋掉。
   ★值與檔尾 `.fb-tab:focus-visible`（§S-73 官方兩層）逐字相同。
   ★已知限制同第七節 (2)：軌道 overflow 會裁掉一部分 3px 外環，與全站 .fb-tab 一致。 */
.fb-tabs:not(.fb-tabs--flow) > .reissue-seg-input:focus-visible + .reissue-seg-item {
    border-color: hsl(var(--ring));
    box-shadow: 0 0 0 3px hsl(var(--ring) / .5);
}

/* ═══ 十一、2026-08-10：分段控制項退場 ＋ 「目前發票狀態」獨立成一張唯讀卡 ═══════
   業主原話（逐字，同一則）：
     「你應該把目前的選項 移除到 變更為 的上面 目前發票狀態：二聯 + 手機條碼
       這個應該把文字改成類似選擇的樣子 但是不給選 然後下方可選的 你幫我恢復不要二聯收納在一起
       而是 條列 二聯不綁載具 二聯手機 二聯自然人憑證 三聯公司統編」

   ── (1) 本節**沒有刪除本檔任何一條規則** ──
   分段控制項（`.reissue-seg` / `.reissue-seg-item` / `.reissue-seg-input` /
   `.reissue-seg-note` / `.reissue-seg-dot` / `.reissue-seg-item--current`）與
   `.reissue-type-card--current` 自本輪起**在 markup 端沒有呼叫端**，
   但第七～第十節那一整批規則與它們上方的推導註解**原地保留、一字未動**（禁刪守則）——
   與第八節處理 `.reissue-seg-dot`、第十節處理撤銷鏈的方式完全相同。
   ★業主一天之內已經在「平鋪 → 收納 → 平鋪」之間改過三次，這批規則是下一次要收納時的現成起點。

   ── (2) 唯讀卡為什麼**幾乎不需要新規則** ──
   §S-90 第二／第六節本來就有一整組「不可選卡」樣式（`.reissue-type-card--disabled`），
   那是 2026-08-09 為「目前狀態那一張卡」寫的；昨晚二層化只是拿掉了它的呼叫端。
   本輪等於把呼叫端接回來 —— markup 印的就是
   `class="reissue-type-card reissue-type-card--disabled reissue-type-card--readonly"`，
   於是自動吃到：1px 實線 --border ＋ --card 白底 ＋ 8px 圓角（與下方四張卡同一顆）、
   `cursor:not-allowed`、標題轉 --muted-foreground、以及**不會**有 hover 底色
   （第二節那條 hover 寫的是 `:not(.reissue-type-card--disabled)`）。
   ★選中態的黑框（`:has(.reissue-type-input:checked)`）也不會誤上身：
     那張卡裡的指示器**刻意不掛** `.reissue-type-input`（理由見 (4)）。

   ── (3) 唯一要補的一條：把標題列的高度撥回來 ──
   第二節的 `.reissue-type-card--disabled .reissue-type-hit{min-height:auto;padding-bottom:.25rem}`
   是為了「卡片下面還接著一句說明文字（『目前狀態，無法選擇』）」而訂的，
   目的是讓上下留白不要各留一份。而業主本輪已指示移除那句說明
   （「（目前狀態，選它等於不修改） 這個說明文字可以移除」）
   ⇒ 卡裡只剩標題列，那兩個值會讓它比下方四張卡矮一截、文字貼著下緣，看起來像壞掉。
   ★★本規則**不改動**第二節那條（它仍然服務「有說明句」的情境，且原文一字未動），
     只在多掛 `--readonly` 時把兩個值撥回 `.reissue-type-hit` 基底的原值：
       min-height: 52px      ← 基底原值（≥44px 觸控目標）
       padding-bottom: .75rem ← 基底 `padding:.75rem 1rem` 的下半
     兩個數字都是**抄基底**，不是新訂的。特異度 (0,2,0) 打平第二節那條、排在其後 ⇒ 勝出。
   ★52px 這裡不是為了觸控（這張卡點不下去），是為了**與下方四張卡等高**：
     兩組卡上下相鄰，高度不一致會讀成「上面那張是別種東西」。

   ── (4) 指示器：為什麼是實心圓點，而且刻意不掛 `.reissue-type-input` ──
   第七節 (5) 把 `.reissue-type-input:checked[type="radio"]` 的圖換成了**勾**，
   語意是「你選了要改成這個」。唯讀卡要表達的是「你**現在**是這個」——
   兩者意思相反，同畫面出現兩種勾讀不出差別。
   ⇒ markup 端那顆 radio 只掛 `.form-check-input` ＋ `.reissue-current-mark`，
     於是吃回 §35 全站 checked radio 的原圖＝**實心圓點**（radio 的原生語彙：
     「這一格已經設定成這樣」）。色票、暗色版、`:disabled` 的 opacity 全部沿用 §35，
     本節**一顆色都沒有新增**，也沒有動 §35 任何一行。
   ★`.reissue-current-mark` 的四個宣告**逐字等於**第二節的 `.reissue-type-input`：
     不掛那顆 class 是為了避開勾，但尺寸仍要與下方四張卡的圓點一致
     （`width/height` 釘 1rem 的理由見第二節原註解：result 頁吃 14px、reissue 獨立頁吃 16px，
      不釘死兩頁的圓點會不一樣大）。
   ★★這四個宣告是**刻意的重複**，不是漏抽共用：兩顆 class 的差別正是「要不要那個勾」，
     合併就沒得分了。若日後第二節的尺寸改了，這裡要跟著改 —— 故在此標明。
   ★markup 端那顆 radio **沒有 `name` 屬性**（也帶 `disabled`）⇒ 永遠不會被送出，
     表單契約仍然只有四顆 `invoice_type`。狀態語意由三個真文字節點承載
     （「目前發票狀態」標題、卡內類型文字、右緣「目前」徽章），
     所以它掛 `aria-hidden` ＋ `tabindex="-1"`，純裝飾（WCAG 1.4.1）。

   ── (5) 「目前」徽章的黃底黑字 ──
   在第六節 `.reissue-type-badge` 原位置改（含完整撤銷鏈與 8.104:1 的實算），
   本節不重複宣告。★整份表單現在只剩**一顆**「目前」徽章（就在這張唯讀卡上），
   下方四張可選卡一律不掛標記 —— 同一件事不在同一畫面標兩次。 */
.reissue-type-card--readonly .reissue-type-hit {
    min-height: 52px;           /* 抄 `.reissue-type-hit` 基底原值，撥回 --disabled 的 auto */
    padding-bottom: .75rem;     /* 抄 `.reissue-type-hit` 基底 `padding:.75rem 1rem` 的下半，撥回 --disabled 的 .25rem */
}
.reissue-current-mark {
    flex: 0 0 auto;
    width: 1rem;                /* 與 `.reissue-type-input` 逐字相同，理由見第二節該規則的原註解 */
    height: 1rem;
    margin-top: 0;              /* BS `.form-check-input{margin-top:.25em}` 是給非 flex 版面用的 */
}

/* --- S-91. 買家發票「重要提示」點開式說明（2026-08-09）-------------------------
   ★節號已定為 §S-91：本輪與另一個 session 的 §S-89 併行施工，
     編號由呼叫端對齊兩邊之後決定；§S-89 已先落地本檔（L17032）。
   ★併入位置：接在同輪的 §S-90（買家「修改發票」選項卡）之後 ——
     兩節服務同一支 partial（`buyer/invoice/_reissue_form.blade.php`），放在一起才讀得出關聯。

   ═══ 一、這一節在做什麼 ═══
   買家發票頁（`buyer/invoice/_reissue_form.blade.php`）的 `.alert.alert-warning`
   框裡原本有兩段長文（結果頁 62 個可見字、獨立頁 27 個），業主指示收進一顆
   驚嘆號旁的說明彈窗。實作用的是**原生 `<details>` / `<summary>`**：
   零 JS、零新相依，鍵盤與螢幕閱讀器的展開／收合狀態由瀏覽器負責。
   ★這一節只負責「讓 summary 看起來是一行摘要 ＋ 一顆可點的小圖示」，
     不負責顯示／隱藏 —— 那是 details 元素本身的行為，CSS 拔掉也還在。

   ═══ 二、作用域 ═══
   class 一律 `reissue-notice-*` 前綴，與同一支 partial 既有的 `reissue-type-*`
   同一個命名空間。**不掛在 `.alert` / `summary` 這種通用選擇器上**：
   全站別處（例如 `sso-points-demo.blade.php` 也用了裸 details/summary）
   不該被這一節影響。

   ═══ 三、為什麼要自己拔 marker ═══
   `<summary>` 出廠是 `display: list-item` ＋ 一顆揭露三角形。
   要拿掉必須**兩條一起寫**：
     · `list-style: none`        → Firefox / 新版 Chromium 走這條
     · `::-webkit-details-marker` → 舊 WebKit（含較舊的 iOS Safari）只認這條
   少寫任何一條，就會有一部分買家的手機上多出一顆三角形。
   ★拔掉 marker 之後**必須**自己提供一顆可見的「這裡可以點」的指示物，
     否則就變成一段沒有任何提示的文字 —— 那顆就是 `.reissue-notice-icon`。

   ═══ 四、顏色一律 currentColor ═══
   `.alert-warning` 的前景是 `hsl(var(--warning-fg))`（本檔 L2386 `.alert-warning`，
   位於未編號的基底區、不屬於任何 §S- 分節；token 本身定義在
   L178 亮色 / L336 暗色，亮暗色各一份）。本節內所有顏色都用 `currentColor` 疊透明度取得，
   **不寫死任何色碼、也不再引一次 `--warning-fg`** ——
   日後主題調整警示色，這一節自動跟著走，不會靜默漂移。

   ═══ 五、無障礙 ═══
   · 觸發鈕＝`<summary>` 本身，可及名稱來自它內部的可見文字，
     view 端刻意不掛 `aria-label`（會把可見文字整個蓋掉）。
   · `aria-expanded` 由瀏覽器依 `open` 屬性自動播報，不需要任何 JS 同步。
   · 焦點框沿用主題 §S-73 的官方兩層寫法（`box-shadow: 0 0 0 3px hsl(var(--ring)/.5)`），
     不自己發明一種。
   · summary 是整行寬、兩行高（實測 ≥ 44px），觸控目標本來就夠大，
     不需要再替那顆小圖示單獨撐 hit area。
   -------------------------------------------------------------------------- */

/* (1) 容器：details 本身不畫任何東西，外框仍然是 .alert.alert-warning 的。 */
.reissue-notice {
    display: block;
}

/* (2) 摘要行（＝觸發鈕）
   flex + wrap：標題與 ⓘ 同一行並排，摘要句 `flex-basis:100%` 換到第二行。
   ★不用 grid `1fr auto`：那會把 ⓘ 推到框的最右緣，離「重要提示」很遠，
     看起來像是右上角的關閉鈕而不是這句話的補充。 */
.reissue-notice-summary {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    column-gap: 6px;
    row-gap: 2px;
    cursor: pointer;
    list-style: none;           /* Firefox / 新版 Chromium 的 marker */
    -webkit-tap-highlight-color: transparent;
}
.reissue-notice-summary::-webkit-details-marker {
    display: none;              /* 舊 WebKit（含較舊 iOS Safari）的 marker */
}
.reissue-notice-summary:focus-visible {
    outline: none;
    outline-offset: 0;
    box-shadow: 0 0 0 3px hsl(var(--ring) / .5);   /* §S-73 官方兩層的柔環那一層 */
    border-radius: 6px;
}

/* (3) 標題「⚠ 重要提示」：字面一字未動，只是現在住在 summary 裡。 */
.reissue-notice-title {
    display: inline-flex;
    align-items: center;
}

/* (4) 那顆可點的說明圖示
   ★它是**裝飾**（view 端掛 aria-hidden）：語意由 summary 自己承擔。
   ★`.7` 不透明度是刻意的：它不該搶「重要提示」的視覺層級，
     但仍需與底色維持足夠對比 —— 圖示是「非文字內容」，
     WCAG 1.4.11 的門檻是 3:1，而 --warning-fg 疊 70% 在 --warning-bg 上仍高於它。
     hover / 展開後回到 1，讓「已經打開了」看得出來。 */
.reissue-notice-icon {
    opacity: .7;
    transition: opacity .15s ease;
}
.reissue-notice-summary:hover .reissue-notice-icon,
.reissue-notice[open] .reissue-notice-icon {
    opacity: 1;
}

/* (5) 摘要句：獨占第二行。
   `flex-basis:100%` 而不是換一個 <div>：summary 的子元素若用 block 元素會讓
   flex 的垂直對齊多一層計算，也讓「標題與圖示同行」需要額外包一層。 */
.reissue-notice-brief {
    flex-basis: 100%;
    opacity: .9;
}

/* (6) 展開後的說明面板
   ★上緣分隔線用 currentColor/.2：與 .alert-warning 的邊框（--warning-fg/.25）同一個語彙。
   ★不做 max-height 動畫：details 的高度是內容決定的，用 transition 假裝展開
     需要寫死一個 max-height，內容一長就會被切掉（而且切掉時沒有任何錯誤訊息）。 */
.reissue-notice-panel {
    margin-top: 8px;
    padding-top: 8px;
    border-top: 1px solid currentColor;
    border-top-color: color-mix(in srgb, currentColor 20%, transparent);
}
.reissue-notice-line + .reissue-notice-line {
    margin-top: 4px;
}

/* ★`color-mix()` 的 fallback：上面刻意先寫一行實色 `border-top: 1px solid currentColor`，
   不支援 color-mix 的瀏覽器會忽略後面那行 border-top-color，得到一條實色分隔線
   —— 比較重，但不會消失，也不會變成預設的黑色。 */

/* --- S-92. /pos/machine 掃描區全頁重排：STEP 格線、插畫卡、rail 可收合（2026-08-10）---
   業主原話（依序）：「你這頁設計好差」「我要用 SHADCN 重構設計」「你左邊太寬了」
   「你看測試印單都被遮蔽了」「可以參考賣賣 WORK OS 的設計」「手機也有」
   「所以你要使用 SHADCN 做出優秀的設計」「移除 COLOR_ADMIN 完整使用 SHADCN」

   基準檔（賣賣 Work OS，`/Users/monet/OS/mywork_os`）：
     rail       `src/components/pos/PosMenu.vue`（收合機制、pos-i 圖示 mask、底部工具鈕）
     作業台     `src/views/pos/PackagePrint.vue`（卡片＝插畫底層＋半透明 overlay）
     token      `src/assets/shadcn.css`（OS 的 --brand 與本檔 --primary 同值 49 100% 50%）

   ═══ 一、為什麼要改：不是美感，是算術 ═══
   掃描區 `#pos-list` 借用 Color Admin 的**商品列表**格線（本檔「補位：.product-row /
   .product-container」那段）：
       div.product-row .product-container { width:100% }
       576→50%  768→33.33%  992→50%  1200→33.33%  1800→25%
   那組百分比是為「一次列出十幾二十張商品磚」設計的。掃描區**永遠只有 3 張 STEP 卡**，
   於是在 1200px 上每張卡實得 33.33% 扣掉 .703125rem×2 的 padding ⇒ 約 370px 再被
   §S-45 的 20px 內距吃掉 ⇒ 卡內可用寬約 330px；而 992～1199px 那一階是 **50%**，
   3 張卡變成「2 張一列 + 1 張落單」，落單那張獨佔半行、右半空白。
   按鈕列（`d-flex justify-content-between`，不換行）在窄卡片必然溢出 ——
   這就是業主截圖裡「測試印單被遮蔽」的**直接成因**：不是 z-index、不是 overflow 設定，
   是一列放不下兩顆鈕，第二顆被 `.product{overflow:hidden}` 裁掉。

   本節改吃 OS 的做法：`grid-template-columns: repeat(auto-fit, minmax(185px,1fr))`。
   3→2→1 由內容寬度自己決定，**一個斷點都不需要**，也就不會再有「50% 那一階」這種
   為別的情境算出來的數字跑進來。

   ═══ 二、與既有章節的關係（哪一條、覆蓋哪個值）═══
   本節**只追加、不修改**任何既有行。以下是被本節蓋掉的宣告與新值：

   §POS-1  `.pos.pos-with-menu { padding-left:10rem }`（@media ≥992px）
           → 展開態 210px／收合態 76px。特異性同為 (0,2,0)，本節在檔尾 ⇒ 後者勝；
             收合態另掛 `.pos.pos-rail-collapsed.pos-with-menu` (0,3,0)。
   §POS-2  `.pos .pos-menu { width:10rem }`（@media ≥992px）→ 210px；
           收合態 `.pos .pos-menu.pos-rail--collapsed` (0,3,0) → 76px。
           連帶 `padding-left:.9375rem` 在收合態改成左右各 .375rem（76px 塞不下 15px 內距）。
   §POS-10 觸控階：檔頭原訂 `.pos .btn{min-height:40px}`。
           ★★**規格書寫錯了**：實際選擇器是
             `.pos .btn:not(.btn-close):not(.btn-xs)`（特異性 (0,4,0)，不是 (0,2,0)），
             而且現行值已被 §S-86 二(a) 改成 **44px**，不是 40px。
           ⇒ `.pos-btn-overlay` 依合約**不掛 `.btn`**，所以這條根本打不到它；
             真正會打到它的是 §S-86 二(b)（見下）。
   §S-86 二(b) `.pos .not-available-text a { min-height:44px; display:inline-flex;
           align-items:center; justify-content:center; gap:.375rem; padding:0 .75rem }`
           特異性 (0,2,1)。`.pos-btn-overlay` 掛在 `<a>` 上、又住在 `.not-available-text`
           裡面 ⇒ **這才是 32px 真正的對手**。本節用 (0,3,0) 與 (1,5,0) 兩段選擇器覆蓋
           `min-height` 為 32px。★★★**這是本節唯一一處把既有值往下調的地方**，
           §S-86 二(b) 當初是量到 17px、為「戴手套的現場作業員」刻意拉到 44px 的決定，
           有量測有理由。本節照施工規格的觸控階（「卡片 overlay 上的小鈕 32px」，
           對應 OS 原始碼的 `h-8`）改成 32px。**兩條都是明文，方向相反 ⇒ 待業主裁決**；
           若裁決維持 44，只要刪掉下方標了 `[裁決點-A]` 的兩行 min-height 即可，
           其餘外觀規則不受影響。
   §S-45 ①  `.pos #pos-list .product.not-available .not-available-text
             { position:static; background:hsl(var(--card)); padding:20px }` 特異性 (1,4,0)。
           → 本節把 `background` 換成 `hsl(var(--background) / .8)`（OS 的 bg-background/80，
             底下要透出插畫），選擇器用 (1,5,0) 的
             `… .not-available-text.pos-step-overlay`。
           ★`position:static` **刻意不覆蓋**：§S-45 ① 的註解記載，當初從 absolute 改成
             static 正是因為插畫被藏掉之後父層沒有高度、絕對定位的卡片塌到 90px、
             圖示列與按鈕整批被裁。施工規格 ② 要求 `position:absolute; inset:0` ——
             那會**原地重現 §S-45 已經修好的那個 bug**（插畫是 absolute，不撐高度；
             卡片只剩 min-height:200px，內容一長就被 overflow:hidden 裁掉）。
           ⇒ 本節改用等效但安全的做法：overlay 留在正常流（`position:relative`
             只為了吃 z-index），`flex:1 1 auto` 讓它填滿卡片，插畫用 absolute 墊在
             `z-index:0`。視覺與 OS 相同（插畫在下、半透明面在上、填滿卡片），
             但高度仍由內容決定，不會再裁掉按鈕。
           ★`padding:20px` 不覆蓋（規格寫 1rem＝16px，20px 更寬鬆，且不值得為 4px
             再堆一層特異性）。
   §S-45 ②  `.pos #pos-list .product.not-available .img { display:none }` 特異性 (1,3,0)。
           → 恢復插畫。★★**規格書給的 `.pos .pos-step-card .pos-step-illust` 是 (0,3,0)，
             贏不了 (1,3,0)**（id 那一位就輸了）。本節改用同樣帶 `#pos-list` 的
             (1,3,0)／(1,4,0) 選擇器，靠檔尾位置與複合類別取勝。
   §S-45 ④  `… .not-available-text i { color:muted-foreground; width:18px; height:18px }`
           特異性 (1,4,1)。→ **提示列的 `.pos-i` 刻意讓它繼續生效**（18px muted 正是
           OS hints 的樣子）；只有 overlay 鈕內的圖示用 (1,5,1) 改回 `color:inherit` 與 15px，
           否則鈕上的圖示會跟文字不同色。
   §S-37  rail 直排（56px、圖示上文字下）**一個字不改**，並且明確**只在收合態沿用**：
           展開態（210px）改成 OS 的橫排 40px，用
           `.pos .pos-menu:not(.pos-rail--collapsed) …`（(0,6,0)）蓋過 §S-37 的 (0,5,0)。
           §S-37 那套本來就是為窄 rail 設計的，收合態正是它的主場。
   §S-38  `.pos .pos-menu .pos-menu-footer { padding:.328125rem .9375rem .75rem }` (0,3,0)
           → 收合態左右內距降到 .375rem，用 (0,4,0)。主題鈕 `.pos-theme-toggle` 未觸碰。

   ═══ 三、量出來的、不是推論的 ═══
   上面每一個特異性都是**逐條數既有選擇器**得到的，不是照規格書抄。規格書在三處
   把特異性算低了（§POS-10 的選擇器形狀、§S-45 ② 的 id、§S-45 ① 的 position），
   照抄會寫出「看起來有做、實際不生效」的規則。詳見回報。

   ═══ 四、`:has()` 方向確認 ═══
   `machine.blade.php` 的實際 markup 是
       div#pos-list  →  hr  →  div#log-list
   `hr` 在 `#log-list` **之前**，所以要用 `hr:has(+ #log-list:empty)`
   （「下一個兄弟是空的 #log-list」）。★規格 ⑧ 同時給的
   `.pos #log-list:empty + hr` 方向相反（那會找 `#log-list` **後面**的 hr，不存在）
   ⇒ **是一條死規則，本節不收**。`:has()` 本專案已在 §POS-8 用過。
   ★`:empty` 對空白敏感，但該 div 在 blade 裡寫成 `<div … id="log-list"></div>`
     中間零字元，JS 清空也是 `.html('')`，兩種情況都成立。

   ═══ 五、六個共用頁的外層 class 不一致（代理 C 實測，非推測）═══
   `pos/_header.blade.php` 被 6 頁共用，但外層殼有兩種：
     `pos/package`・`pos/machine`・`pos/machine_backup` → `<div id="pos" class="pos pos-with-menu pos-with-sidebar">`
     `pos/checkout`・`pos/darka`・`pos/_package`        → `<div id="pos-customer" class="pos pos-customer">`（**沒有 pos-with-menu**）
   ⇒ 本節的 `.pos.pos-rail-collapsed.pos-with-menu{padding-left:76px}` 對後 3 頁不生效。
     ★**這是對的，刻意不補規則**：§POS-1 的 `.pos.pos-with-menu{padding-left:10rem}`
     對那 3 頁同樣不生效，兩邊一致 ⇒ 不會出現「rail 變窄但主區留白」。
     為了「保險」去給 `.pos-customer` 補 padding，等於動了 3 個沒宣告要動的頁面。

   ═══ 六、收合態規則一律關在 @media (min-width:992px) 內 ═══
   手機 rail 是頂部橫向 bar，而 §POS-1 的 `.pos.pos-with-menu{padding-top:112px}` 是
   **寫死常數**：rail 內容高度一變（例如收合時把 MYPOS 藏掉）bar 就變矮／變高，
   內容區不是被蓋住就是浮一段空白。所以本節**所有** `pos-rail--collapsed` 相關宣告
   都包在 `@media (min-width:992px)` 裡。
   ★代理 C 已在 JS 端用 `matchMedia` 硬擋（桌機以外永不掛 `pos-rail--collapsed`），
     但**不依賴那道閘門** —— JS 閘門與 CSS 閘門兩層都對才算數。

   ═══ 七、pos-i 圖示 ═══
   五個 mask-image 逐字取自 OS `PosMenu.vue` 的 style 區塊（scan-line / barcode /
   qr-code / keyboard / printer），未改一個字元；只**補上 `-webkit-` 前綴**
   —— OS 跑 Electron 單一 Chromium 可以不加，我們要吃一般 Safari。
   ★尺寸策略（2026-08-10 定案）：**不指定尺寸，由所在容器的字級決定**。
     `.pos-i` 是 1em×1em，跟著父層字級走。原本這裡提案「另開 .pos-i-15 工具類」，
     但 blade 端最終一次都沒掛它 ⇒ 該 class 已於同日當場移除（見本節末）。
     ★實際生效值不是 1em 的例外：`.not-available-text` 內的圖示會被 §S-45 ④ 的
       `… .not-available-text i{width:18px;height:18px;color:muted-foreground}`（1,4,1）
       釘成 18×18 灰色 —— 提示列刻意讓它生效（就是 OS hints 的樣子）；
       鈕內圖示則由本節 (1,5,1) 改回 `color:inherit` + 15px，否則 hover 換色時圖示不跟著換。

   ═══ 八、代理 C 已定案的 hook 名（本節對準這些，不要自行更名）═══
     `.pos-rail-toggle`（收合鈕本體，`<button>`）
     `.pos-rail-toggle-text`（鈕內文字 `<span>`）
     `.pos-rail-toggle-icon`（掛在 `<i data-lucide="chevrons-left">` 上；
        ★lucide 會把 `<i>` 換成 `<svg>` 並沿用 class ⇒ 本節同時寫
        `.pos-rail-toggle-icon` 與 `svg` 兩個對象，避免替換前後有一段沒樣式）
     `.pos-rail--collapsed` 掛 `.pos-menu`／`.pos-rail-collapsed` 掛外層 `.pos`
     localStorage key：`seller-pos-rail-collapsed`
     位置：`.pos-menu-footer` 內、既有 `.pos-theme-toggle` **之上**
   ★兩顆鈕的間距用**新鈕自己的 `margin-bottom`**，不改 `.pos-menu-footer` 的
     display（改成 flex 會連帶影響 §S-38 既有主題鈕的對齊），更不動 `.pos-theme-toggle`。
   --------------------------------------------------------------------------- */

/* ① STEP 格線：取代 Color Admin 的商品百分比格線
      `.pos #pos-list.pos-steps` = (1,2,0)，`div.product-row` = (0,1,1) ⇒ 勝。
      `> *` 版本 = (1,2,0)，`div.product-row .product-container` = (0,2,1) ⇒ id 那位勝，
      而且特異性優先於 @media，五個斷點一次全部失效（這是刻意的）。
      margin 歸零：`div.product-row` 的 -.703125rem 負邊距是配 padding 用的，
      改成 grid + gap 之後留著會讓格線超出容器左右各 11.25px。 */
.pos #pos-list.pos-steps {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(185px, 1fr));
    gap: 1rem;
    align-items: stretch;
    margin: 0;
    padding: 0;
}
.pos #pos-list.pos-steps > * {
    min-width: 0;      /* grid item 預設 min-width:auto，內容一寬就撐破欄位 */
    width: auto;       /* 蓋掉 .product-container 的 33.33%/50%/25% */
    padding: 0;        /* 蓋掉 .product-container 的 .703125rem，間距改由 gap 負責 */
}

/* ② STEP 卡本體
      `.product` 已經給了 position:relative / overflow:hidden / display:flex /
      flex-direction:column / background:--card / border:--border（本檔「補位：.product」段），
      這裡只補 OS 的圓角（rounded-xl=12px）、陰影（shadow-sm）與最小高度。
      `.pos .pos-step-card` = (0,2,0) > `.product` = (0,1,0)。 */
.pos .pos-step-card {
    position: relative;
    display: flex;
    flex-direction: column;
    height: 100%;
    min-height: 200px;
    overflow: hidden;
    border: 1px solid hsl(var(--border));
    border-radius: 12px;
    box-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);
    background: hsl(var(--card));
}

/* ③ 插畫層：恢復 §S-45 ② 藏掉的圖，改當背景層
      ★特異性：對手是 `.pos #pos-list .product.not-available .img` (1,3,0)。
        第一條選擇器 (1,3,0) 靠檔尾位置取勝（同分後者勝），
        第二條 (1,4,0) 是給「代理 B 保留 .img class」那種寫法的保險，兩條都寫是刻意的。
      ★`.product.not-available .img{opacity:.65}` (0,3,0) 也一併被蓋成 .7。
      ★pointer-events:none：插畫蓋在整張卡上，不能吃掉底下的點擊。 */
.pos #pos-list .product.not-available .pos-step-illust,
.pos #pos-list .product.not-available .img.pos-step-illust {
    display: block;
    position: absolute;
    inset: 0;
    z-index: 0;
    width: 100%;
    height: 100%;
    min-height: 0;
    object-fit: cover;
    opacity: .7;
    pointer-events: none;
}

/* ④ overlay 面：OS 的 bg-background/80，蓋在插畫上
      ★★不用 position:absolute（理由見章節註解 §S-45 ① 那段）：
        用 `position:relative` 只是為了讓 z-index 生效，元素仍在正常流，
        `flex:1 1 auto` 讓它填滿卡片高度，內容變高時卡片跟著長，不會被裁。
      特異性 (1,5,0) > §S-45 ① 的 (1,4,0)。 */
.pos #pos-list .product.not-available .not-available-text.pos-step-overlay {
    position: relative;
    z-index: 1;
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    align-items: stretch;
    justify-content: flex-start;
    background: hsl(var(--background) / .8);
    color: hsl(var(--foreground));
    text-align: left;
}
/* overlay 內那層包裝 div：§S-45 的 `… .not-available-text > div` 給了 width:100%
   與 1.25rem/700 的字級，這裡只補「填滿高度」讓內容可以靠上、按鈕靠下也排得開。 */
.pos #pos-list .product.not-available .not-available-text.pos-step-overlay > div {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
}

/* ⑤ 卡內三段 */
.pos .pos-step-title {
    font-size: 1.25rem;
    font-weight: 800;
    line-height: 1.375;
    letter-spacing: -.01em;
    color: hsl(var(--foreground));
}
.pos .pos-step-hints {
    margin-top: .5rem;
    display: flex;
    align-items: center;
    gap: .5rem;
    color: hsl(var(--muted-foreground));
}
/* ★★★ flex-wrap:wrap 就是「測試印單被切掉」的直接修正。
      現況 `d-flex justify-content-between` 不換行 ⇒ 窄卡片必然溢出、被
      `.product{overflow:hidden}` 裁掉。改成 wrap + gap，一列放不下就換行。
      `margin-top:auto` 讓按鈕列吸底：卡片高度不一致時三張卡的鈕仍對齊。 */
.pos .pos-step-actions {
    margin-top: auto;
    padding-top: 1rem;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .5rem;
}

/* ⑥ overlay 上的 32px 鈕
      選擇器列刻意寫三條，各自帶不同特異性：
        (0,2,0) 基準（給 overlay 以外的使用情境）
        (0,3,0) 打贏 §S-86 二(b) 的 `.pos .not-available-text a` (0,2,1)
        (1,5,0) 打贏 §S-45 ⑥ 的 `… .not-available-text .btn` 家族（萬一 markup 留了 .btn）
      CSS 選擇器列的每一條各自計分，只要有一條命中且分數最高，整組宣告就生效。 */
.pos .pos-btn-overlay,
.pos .not-available-text .pos-btn-overlay,
.pos .pos-step-actions .pos-btn-overlay,
.pos #pos-list .product.not-available .not-available-text .pos-btn-overlay {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: .375rem;
    /* [裁決點-A ✅已裁決 2026-08-10：維持 44px] 業主原話「維持 44px」。
       ★原提案是 32px（照賣賣 Work OS 的 `h-8`），已否決。否決理由（業主採納）：
         OS 那顆 32px 鈕活在 Electron 桌機版、滑鼠操作的情境；本頁是倉庫觸控機，
         業主當日明確追加「手機也有」⇒ 抄 OS 的數值等於抄錯情境。
         §S-86 二(b) 的 44px 是實測量到 17px 後、為「戴手套的現場作業員」刻意拉上去的
         決定，有量測有理由；F 型（全螢幕作業頁）規格亦明訂觸控目標 ≥40px。
       ⇒ 這裡不再覆蓋 §S-86 二(b) 的 min-height，讓它繼續生效（44px）。
         原提案值存查：height/min-height 皆為 32px。 */
    padding: 0 .75rem;
    border: 1px solid hsl(var(--border));
    border-radius: var(--radius);
    background: hsl(var(--card));
    color: hsl(var(--foreground));
    font-size: .875rem;
    font-weight: 500;
    line-height: 1;
    white-space: nowrap;
    text-decoration: none;
    box-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);
    transition: background-color .15s linear, color .15s linear;
}
.pos .pos-btn-overlay:hover,
.pos .not-available-text .pos-btn-overlay:hover,
.pos #pos-list .product.not-available .not-available-text .pos-btn-overlay:hover {
    background: hsl(var(--accent));
    color: hsl(var(--accent-foreground));
}

/* ⑦ pos-i 圖示機制（OS 移植）
      用 mask + currentColor：同一份 SVG 自動跟著亮暗模式與父層文字色走，
      不需要為兩個主題各準備一份圖檔。 */
.pos-i {
    display: inline-block;
    width: 1em;
    height: 1em;
    vertical-align: -.125em;
    background-color: currentColor;
    flex-shrink: 0;
    mask-repeat: no-repeat;
    mask-position: center;
    mask-size: contain;
    -webkit-mask-repeat: no-repeat;
    -webkit-mask-position: center;
    -webkit-mask-size: contain;
}
/* ★2026-08-10 移除死條文：原本這裡有一行 `.pos-i-15 { font-size: 15px; }`。
   落地當下 blade 端（machine.blade.php）最終採用的是「不指定尺寸、由容器字級決定」，
   五顆圖示一次都沒有掛過 .pos-i-15 ⇒ 全站零使用者，是死 class，當場清掉。
   ★為什麼不留著：見 feedback_superseded_spec_clause_must_be_marked_dead ——
     沒有使用者的規則會被下一個人當成「現行規格」照抄。趁還記得為什麼加的時候刪，
     比留給後人考古便宜。原值存查：.pos-i-15 { font-size: 15px; } */

/* 以下五個 data URI 逐字取自 OS `src/components/pos/PosMenu.vue`（L137/138/139/140/149），
   只加 -webkit- 前綴，SVG 內容一個字元都沒改。 */
.pos-i-scan-line {
    mask-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='M3 7V5a2 2 0 0 1 2-2h2'/%3e%3cpath d='M17 3h2a2 2 0 0 1 2 2v2'/%3e%3cpath d='M21 17v2a2 2 0 0 1-2 2h-2'/%3e%3cpath d='M7 21H5a2 2 0 0 1-2-2v-2'/%3e%3cpath d='M7 12h10'/%3e%3c/svg%3e");
    -webkit-mask-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='M3 7V5a2 2 0 0 1 2-2h2'/%3e%3cpath d='M17 3h2a2 2 0 0 1 2 2v2'/%3e%3cpath d='M21 17v2a2 2 0 0 1-2 2h-2'/%3e%3cpath d='M7 21H5a2 2 0 0 1-2-2v-2'/%3e%3cpath d='M7 12h10'/%3e%3c/svg%3e");
}
.pos-i-barcode {
    mask-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='M3 5v14'/%3e%3cpath d='M8 5v14'/%3e%3cpath d='M12 5v14'/%3e%3cpath d='M17 5v14'/%3e%3cpath d='M21 5v14'/%3e%3c/svg%3e");
    -webkit-mask-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='M3 5v14'/%3e%3cpath d='M8 5v14'/%3e%3cpath d='M12 5v14'/%3e%3cpath d='M17 5v14'/%3e%3cpath d='M21 5v14'/%3e%3c/svg%3e");
}
.pos-i-qr-code {
    mask-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3crect width='5' height='5' x='3' y='3' rx='1'/%3e%3crect width='5' height='5' x='16' y='3' rx='1'/%3e%3crect width='5' height='5' x='3' y='16' rx='1'/%3e%3cpath d='M21 16h-3a2 2 0 0 0-2 2v3'/%3e%3cpath d='M21 21v.01'/%3e%3cpath d='M12 7v3a2 2 0 0 1-2 2H7'/%3e%3cpath d='M3 12h.01'/%3e%3cpath d='M12 3h.01'/%3e%3cpath d='M12 16v.01'/%3e%3cpath d='M16 12h1'/%3e%3cpath d='M21 12v.01'/%3e%3cpath d='M12 21v-1'/%3e%3c/svg%3e");
    -webkit-mask-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3crect width='5' height='5' x='3' y='3' rx='1'/%3e%3crect width='5' height='5' x='16' y='3' rx='1'/%3e%3crect width='5' height='5' x='3' y='16' rx='1'/%3e%3cpath d='M21 16h-3a2 2 0 0 0-2 2v3'/%3e%3cpath d='M21 21v.01'/%3e%3cpath d='M12 7v3a2 2 0 0 1-2 2H7'/%3e%3cpath d='M3 12h.01'/%3e%3cpath d='M12 3h.01'/%3e%3cpath d='M12 16v.01'/%3e%3cpath d='M16 12h1'/%3e%3cpath d='M21 12v.01'/%3e%3cpath d='M12 21v-1'/%3e%3c/svg%3e");
}
.pos-i-keyboard {
    mask-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='M10 8h.01'/%3e%3cpath d='M12 12h.01'/%3e%3cpath d='M14 8h.01'/%3e%3cpath d='M16 12h.01'/%3e%3cpath d='M18 8h.01'/%3e%3cpath d='M6 8h.01'/%3e%3cpath d='M7 16h10'/%3e%3cpath d='M8 12h.01'/%3e%3crect width='20' height='16' x='2' y='4' rx='2'/%3e%3c/svg%3e");
    -webkit-mask-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='M10 8h.01'/%3e%3cpath d='M12 12h.01'/%3e%3cpath d='M14 8h.01'/%3e%3cpath d='M16 12h.01'/%3e%3cpath d='M18 8h.01'/%3e%3cpath d='M6 8h.01'/%3e%3cpath d='M7 16h10'/%3e%3cpath d='M8 12h.01'/%3e%3crect width='20' height='16' x='2' y='4' rx='2'/%3e%3c/svg%3e");
}
.pos-i-printer {
    mask-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2'/%3e%3cpath d='M6 9V3a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v6'/%3e%3crect x='6' y='14' width='12' height='8' rx='1'/%3e%3c/svg%3e");
    -webkit-mask-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2'/%3e%3cpath d='M6 9V3a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v6'/%3e%3crect x='6' y='14' width='12' height='8' rx='1'/%3e%3c/svg%3e");
}

/* ⑦-b 圖示在 overlay 鈕內：顏色要跟著鈕走
      §S-45 ④ 的 `… .not-available-text i` (1,4,1) 會把**所有**圖示塗成
      --muted-foreground 並鎖成 18×18。提示列（.pos-step-hints）保持那樣是對的
      —— 那正是 OS hints 的樣子 —— 但鈕上的圖示必須與鈕的文字同色，
      否則 hover 換色時圖示不會跟著換。用 (1,5,1) 蓋回來。 */
.pos #pos-list .product.not-available .not-available-text .pos-btn-overlay .pos-i,
.pos .pos-step-actions .pos-btn-overlay .pos-i {
    color: inherit;
    width: 15px;
    height: 15px;
}

/* ⑧ 空的清單區不要留白條
      ★方向已驗證：markup 是 #pos-list → hr → #log-list，`hr` 在前，
        所以是 `hr:has(+ #log-list:empty)`（下一個兄弟是空的 #log-list）。
        規格另給的 `#log-list:empty + hr` 方向相反、在本頁永遠不成立，**刻意不收**
        （留一條沒作用的規則等於假裝做了事）。
      ★不依賴代理 B 是否加上 `.pos-log-sep`：這條用 `#log-list` 當錨點，
        hr 有沒有 class 都成立；加了也不會衝突。 */
.pos #pos-list.pos-steps:empty { display: none; }
.pos #log-list:empty { display: none; }
.pos hr:has(+ #log-list:empty) { display: none; }

/* ⑨ rail 收合／展開（桌機）
      §POS-1 的 padding-left 與 §POS-2 的 width 都在 @media ≥992px 裡、都是 (0,2,0)，
      本節同分而在檔尾 ⇒ 勝；收合態再加一個 class 拉到 (0,3,0)。
      ★兩邊都要切：`.pos-menu` 自己變窄、`.pos`（#pos）的 padding 跟著縮，
        少切一邊就會出現「rail 縮了但內容沒跟上」的空白帶。代理 C 負責 JS 同步兩邊。 */
@media (min-width: 992px) {
    .pos .pos-menu {
        width: 210px;
        transition: width .15s linear;
    }
    .pos.pos-with-menu {
        padding-left: 210px;
        transition: padding-left .15s linear;
    }
    .pos .pos-menu.pos-rail--collapsed {
        width: 76px;
        padding-left: .375rem;
        padding-right: .375rem;   /* 76px 放不下 §POS-2 的 15px 內距 */
    }
    .pos.pos-rail-collapsed.pos-with-menu {
        padding-left: 76px;
    }

    /* 展開態（210px）＝ OS 的橫排 40px。
       (0,6,0) > §S-37 的 (0,5,0)；§S-37 那套直排一個字沒改，只是在展開態被讓位。 */
    .pos .pos-menu:not(.pos-rail--collapsed) .nav.nav-tabs .nav-item .nav-link {
        flex-direction: row;
        gap: .625rem;
        min-height: 40px;
        padding: 0 .75rem;
        justify-content: flex-start;
    }
    .pos .pos-menu:not(.pos-rail--collapsed) .nav.nav-tabs .nav-item .nav-link > i,
    .pos .pos-menu:not(.pos-rail--collapsed) .nav.nav-tabs .nav-item .nav-link > svg {
        margin: 0 !important;      /* §S-37 那條也是 !important，同層級靠特異性取勝 */
    }
    /* 收合態沿用 §S-37 的 56px 直排，這裡一個字都不寫（那正是它的設計情境）。 */

    /* 收合態的 logo 區：MYPOS 四個字在 76px 裡會擠成兩行。
       `:not(#pos_time)` 讓時鐘留下 —— ★時鐘不可隱藏，現場要看時間。
       特異性 (1,3,0)（:not() 內的 id 計 1 分）> §POS-2 logo-text 的 (0,3,0)。 */
    .pos .pos-menu.pos-rail--collapsed .logo .logo-text:not(#pos_time) {
        display: none;
    }
    .pos .pos-menu.pos-rail--collapsed .logo #pos_time {
        font-size: 10px;
        margin-top: 0;
        letter-spacing: 0;
    }
    .pos .pos-menu.pos-rail--collapsed .logo {
        padding: .625rem .375rem;
    }
    /* §S-38 的 footer 內距 (0,3,0) → 收合態縮到 .375rem，(0,4,0) 取勝。
       `.pos-theme-toggle` 本身一個字都沒動。 */
    .pos .pos-menu.pos-rail--collapsed .pos-menu-footer {
        padding-left: .375rem;
        padding-right: .375rem;
    }
}

/* ⑩ rail 收合鈕（代理 C 掛在 .pos-menu-footer 內、主題鈕之上）
      ★色票用 --muted-foreground-on-muted（§S-31）不是 --muted-foreground：
        rail 底是 --muted，後者壓上去實測只有 4.40:1，未達 AA。這是 §S-37 已經
        踩過並結案的坑，照它的結論。 */
.pos .pos-rail-toggle {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: .375rem;
    width: 100%;
    height: 36px;
    min-height: 36px;
    border: 1px solid transparent;
    border-radius: var(--radius);
    background: transparent;
    color: hsl(var(--muted-foreground-on-muted));
    font-size: .75rem;
    cursor: pointer;
    margin-bottom: .375rem;   /* 與下方 §S-38 主題鈕的間距；不改 footer 的 display */
    transition: background-color .15s linear, color .15s linear;
}
.pos .pos-rail-toggle:hover {
    background: hsl(var(--accent));
    color: hsl(var(--accent-foreground));
}
.pos .pos-rail-toggle .pos-rail-toggle-icon,
.pos .pos-rail-toggle svg {
    width: 18px;
    height: 18px;
    flex: 0 0 auto;
}
/* 收合態只留圖示；圖示同時翻面，指向「往右展開」。
   ★關在 @media ≥992px 內（見章節註解「六」）：收合態在手機不存在，
     也不能讓任何規則有機會改動頂 bar 的高度（padding-top:112px 是寫死常數）。 */
@media (min-width: 992px) {
    .pos .pos-menu.pos-rail--collapsed .pos-rail-toggle-text {
        display: none;
    }
    .pos .pos-menu.pos-rail--collapsed .pos-rail-toggle .pos-rail-toggle-icon,
    .pos .pos-menu.pos-rail--collapsed .pos-rail-toggle svg {
        transform: rotate(180deg);
    }
}
/* 手機是頂部橫向 bar，沒有「收合」這個概念 —— 這條必須排在基準規則之後 */
@media (max-width: 991.98px) {
    .pos .pos-rail-toggle { display: none; }
}

/* --- S-93. rail nav-item 標籤：完整/短標依收合狀態切換（2026-08-10）--------------
   業主回報「少了包裝機」：查證三個 nav-item 文字都在（見 _header.blade.php，
   `自動包裝機` 字串與 HEAD 相同，一個字沒少）。真正原因是 §S-92 新加的 rail
   收合功能**預設收合成 76px**，扣掉左右內距（.375rem×2）僅約 64px 可用，
   「自動包裝機」5 字 × 12px（§S-37 字級）＝60px 卡在邊緣被擠壓/截斷，看起來
   像消失了——不是內容掉了，是收合寬度不夠放完整名稱。

   照賣賣 Work(OS) 的 PosMenu.vue：收合態本來就是「兩字短標 + text-[10px]」，
   落地時漏抄這一條。_header.blade.php 已把三個 nav-link 的文字拆成
   `.pos-rail-label-full`（完整名稱，原字一個未動）／`.pos-rail-label-short`
   （新增的兩字短標：揀包／包裝／後台）兩個 span，本節只負責依 rail 收合狀態
   切換兩者的 display，不碰文字本身、不碰既有 class/href/@can 判斷式。

   ★特異性：這兩個 class 是全新命名，本檔目前沒有任何既有規則對它們設過
     display（span 預設 inline，兩個都會同時可見，必須靠這裡二選一）。
     選擇器仍照 §S-37／§S-92 的既有寫法帶上完整祖先鏈（`.pos .pos-menu …
     .nav.nav-tabs .nav-item .nav-link`），不是為了打贏誰，是避免將來若有人
     在別處新增更泛用的 span 規則時被誤蓋。

   ★桌機兩態（展開／收合）都關在 @media (min-width:992px) 內：理由同 §S-92
     六，`.pos.pos-with-menu{padding-top:112px}` 是寫死常數，桌機專用規則絕
     不能滲到手機頂 bar。
   ★手機固定顯示完整名稱、隱藏短標，關在 @media (max-width:991.98px) 內，
     與桌機兩態互斥、互不相蓋。 -------------------------------------------- */
@media (min-width: 992px) {
    /* 展開態：顯示完整名稱、隱藏短標 */
    .pos .pos-menu:not(.pos-rail--collapsed) .nav.nav-tabs .nav-item .nav-link .pos-rail-label-full {
        display: inline;
    }
    .pos .pos-menu:not(.pos-rail--collapsed) .nav.nav-tabs .nav-item .nav-link .pos-rail-label-short {
        display: none;
    }
    /* 收合態：隱藏完整名稱、顯示短標（照 OS 的 text-[10px]） */
    .pos .pos-menu.pos-rail--collapsed .nav.nav-tabs .nav-item .nav-link .pos-rail-label-full {
        display: none;
    }
    .pos .pos-menu.pos-rail--collapsed .nav.nav-tabs .nav-item .nav-link .pos-rail-label-short {
        display: inline;
        font-size: 10px;
    }
}
@media (max-width: 991.98px) {
    /* 手機 rail 是頂部橫向 bar，一律顯示完整名稱、隱藏短標 */
    .pos .pos-menu .nav.nav-tabs .nav-item .nav-link .pos-rail-label-full {
        display: inline;
    }
    .pos .pos-menu .nav.nav-tabs .nav-item .nav-link .pos-rail-label-short {
        display: none;
    }
}


/* --- S-94. POS 外殼第二輪：主區標題列、右側欄可收合、死頁三支補讓位與髮絲線（2026-08-10）---
   業主原話（依序）：「請你把整個 POS 介面重構一次 目前看起來差很多不佳」
   「完全使用ＳＨＡＤＣＮ去設計整個ＰＯＳ 賣賣ＷＯＲＫ ＯＳ就是如此」
   「你要完全移除ＣＯＬＯＲ＿ＡＤＭＩＮ」「右邊區塊也要可以收合」「注意功能都要完整」

   基準檔：賣賣 Work OS（`/Users/monet/OS/mywork_os`）的 PosLayout（標題列）與 aside（側欄收合）。
   前一輪成果：§S-92（STEP 格線／插畫卡／rail 收合）、§S-93（rail 短標）。
   同批施工：代理 B `pos/package`、C `pos/machine`、D `pos/_header`（第三支 IIFE）、
             E `pos/checkout`＋`pos/darka`、F `pos/_package`。本節是它們共同的樣式來源。

   ═══ 零、本輪最重要的取捨：class 名保留，只換 CSS 定義 ═══
   業主要「完全移除 Color Admin」。Color Admin 的**檔案**早在 2026-07-30 就 git rm 了
   （旗標拆光、app.min.css 移除，見 §POS-1 檔頭）。今天殘留的只是它的**命名**
   （`.pos-with-menu` / `.product-row` / `.product-container` / `.nav-tabs.nav-fill` …）。

   本輪決定：**class 名稱一律保留，只換 CSS 定義與視覺。**
   理由（盤點查證的結果，不是偷懶）：
     ① 這些 class 是 JS 的接線。`machine.blade.php` 的 `.find('div.img')` 綁的是
        「元素型別＋class」，`#log-list span.product > div.img` 三層都綁元素型別，
        而 `$('#log-list').removeClass()` 是**無參數呼叫＝清光所有 class**、每次輪詢都跑。
        改名不會報錯，只會**靜默失效**。
     ② 業主的硬性要求是「功能都要完整」。改 class 名 ＝ 高風險 × 零視覺收益
        （使用者看不到 class 名）。
     ③ 視覺目標（「完全是 shadcn / Work OS 的樣子」）只靠 CSS 就能 100% 達成。
   ⇒ **驗收標準是「畫面長得像 Work OS」，不是「grep 不到 pos-with-menu」。**
     這是決定，不是遺漏；要改回去請先讀完上面三條。

   ═══ 一、★外殼「flex 化」選了 (b)：維持 fixed ＋ padding，只補缺的讓位 ═══
   施工規格給了兩條路：(a) 把 `.pos-menu` / `.pos-sidebar` 從 `position:fixed` 改成
   進入 `.pos` 的 flex 流；(b) 維持 fixed ＋ padding，只調數值。**本節選 (b)。**

   ── (a) 其實跑得動（先確認過，不是因為不確定才不選）──
     高度鏈是完整的：`.app-content-full-height.app-without-header .app-content{height:100vh}`
     （§補位：外殼版面，關在 `@media (min-width:768px)` 內）→ `.pos{height:100%}` ＝ 100vh 定值
     → flex 子項 stretch ⇒ static 的 `.pos-menu` 一樣拿得到 100vh 的定高，
     `.nav-container{flex:1;min-height:0}` 內那支 `data-scrollbar` 的 `height:100%`
     照樣解得出來。**現成的存在證明就在旁邊**：`.pos-content` 本來就是 static 的 flex 子項
     （`flex:1;min-width:0;min-height:0`），它底下的 `.pos-content-container` 也是
     `data-height="100%"`，今天跑得好好的。⇒ 規格說的「PerfectScrollbar 需要有界高度」
     這個風險，在 (a) 底下**其實不成立**。

   ── 那為什麼還是選 (b)：因為 (a) 的收益是 0 ──
     ① **畫面完全一樣**。fixed+padding 與 flex 流在 ≥992px 產出的幾何逐像素相同
        （rail 佔左 210/76px、側欄佔右 360/0px、主區吃中間）。業主驗收看畫面，
        而沒有任何一個像素會因為 (a) 而變好。
     ② (a) 會動到 6 頁的 formatting context，其中 3 頁（checkout / darka / _package）
        本輪才第一次被碰、其中 1 頁（`_package`）**渲染就 500，根本驗不了**
        （`route("pos.packageData")` 不存在，業主裁決本輪不修）。
        為零視覺收益去換 3 支驗不到的頁面的版面模型，比例不對。
     ③ 本輪真正要的新功能是「右側欄可收合」——(a)(b) 兩條路的收合實作**完全一樣難**
        （都是把寬度收成 0、外層讓位歸零）。flex 化並不會讓收合變簡單。
     ④ §S-92 已經把 rail 的 fixed+padding 兩邊同步做完並驗過。(b) 是延續同一套機制，
        (a) 等於把它拆掉重做。
     ⑤ 高度鏈雖然成立，但 (a) 會讓 rail 的高度**從「viewport 定值」改成「依賴
        `.app-content-full-height` 這條鏈」**。那條鏈是 Color Admin 的補位規則，
        本身是這一年一直在拆的東西 —— 把 rail 掛上去等於新增一個相依。

   ── ★(b) 底下唯一該補的東西：`.pos-customer` 三頁**根本沒有讓位** ──
     §S-92「五」量到的事實：6 支共用頁的外殼有兩種
       `pos/package`・`pos/machine`・`pos/machine_backup`
           → `<div id="pos" class="pos pos-with-menu pos-with-sidebar">`
       `pos/checkout`・`pos/darka`・`pos/_package`
           → `<div id="pos-customer" class="pos pos-customer">`（**兩個 with-* 都沒有**）
     而 `.pos .pos-menu{position:fixed;left:0}` 與 `.pos .pos-sidebar{position:fixed;right:0}`
     是**不看 with-* 的基準規則**（§POS-2／§POS-4），三頁照樣有 rail 與側欄
     （六頁都 `@include('pos._header')`，grep 過）。
     ⇒ 那三頁的 rail 蓋掉主區左邊 210px、側欄蓋掉右邊 360px。fixed 元素脫離流，
       `.pos` 沒有對應 padding ⇒ `.pos-content` 從 x=0 開始畫。
     §S-92 當時**刻意不補**，理由是「那三頁不在該輪範圍」——**那個理由在本輪失效**：
     業主裁決「三支死頁一起改」，它們正是本節第⑤段（頁籤髮絲線）要服務的對象。
     讓 rail 蓋著內容、卻去調它的頁籤圓角，是本末倒置。
     ★補法用 `:has(> .pos-menu)` / `:has(> .pos-sidebar)` 而不是無條件補：這條選擇器
       就是那個不變量本身（「有 rail 才留 rail 的位」）。將來若有人把 `pos-with-menu`
       加回 markup，兩邊給同一個值也不會打架。本檔已用 `:has()` 96 處，不是新引進的相依。
     ★這是本節唯一一處超出施工規格條列的追加。要撤除只需刪掉①整段，其餘四段不受影響。

   ═══ 二、與既有章節的關係（被本節蓋掉的宣告、逐條數過的特異性）═══
   本節**只追加、不修改**任何既有行。

   §POS-1  `.pos.pos-with-sidebar{padding-right:22.5rem}`（@media ≥992px，(0,2,0)）
           → 側欄收合態 `.pos.pos-sidebar-collapsed.pos-with-sidebar` (0,3,0) 歸零。
           `.pos.pos-with-menu{padding-left}` 由 §S-92 接管（210/76px），本節不碰。
   §POS-3  `.pos .pos-content{flex:1;min-width:0;min-height:0}` (0,2,0) —— **沒有 display:flex**。
           → 本節補上 `display:flex; flex-direction:column`，並給
             `.pos .pos-content > .pos-content-container` 補 `flex:1 1 auto; min-height:0`。
           ★★為什麼一定要補：`includes/_shell.blade.php` 第 9 段的 PerfectScrollbar init
             做的是 `if (h) el.style.height = h;` —— 把 `height:100%` 寫成 **inline style**
             塞進 `.pos-content-container`。標題列一插進去當兄弟，就是 `64px + 100%`，
             必然溢出 64px、**底部內容被裁掉**。
           ★★★**不需要 `!important`**（代理 C 查證，本節採納）：直向 flex 下 inline 的
             `height:100%` 只是 flex base size，`flex-shrink:1` 會把那 64px 自動收回來。
             一開始我打算寫 `height: auto !important` 去蓋 inline —— 那是多餘的，
             而且會把「容器高度」從 flex 演算法手上搶走。**本節零 !important。**
           §POS-3 `.pos .pos-content .pos-content-container{padding:.9375rem}` (0,3,0)
           → 標題列若被放進捲動容器內（package 就是），用 (0,5,0) 的負邊距抵銷成滿版。
   §POS-4  `.pos .pos-sidebar{width:calc(22.5rem - 1.875rem)}` (0,2,0) ＋
           `@media ≥992px{ margin:.9375rem; border:1px solid --border }` (0,2,0)
           → 收合態 `.pos .pos-sidebar.pos-sidebar--collapsed` (0,3,0) 勝：
             `width:0` ＋ 左右 margin 歸零 ＋ `border-width:0`。
           ★**只歸零左右 margin**：上下的 .9375rem 必須留著，§POS-4 的
             `height:calc(100% - 1.875rem)` 就是配那 15+15 算的，一起歸零會讓收合過程中
             側欄先抽高 30px 再消失（看得出來的抖動）。
           §POS-4 `.pos .pos-sidebar .pos-sidebar-header .title{flex:1}` (0,4,0) 仍然生效，
           六頁都吃得到（那是 CSS 給的，不是 blade 的 utility class —— 見「三」⑤）。
   §POS-10 / §S-86 二(a)
           `.pos .btn:not(.btn-close):not(.btn-xs){min-height:44px}` —— ★特異性是
           **(0,4,0)**，不是 (0,2,0)（`:not()` 內的 class 各計 1 分），而且現行值是
           §S-86 改過的 **44px**、不是檔頭 §POS-10 的 40px。
           ⇒ 本節的 36px 工具鈕一律用帶上下文的 (0,5,0)／(0,6,0) 硬贏，
             **不靠「我排在檔尾所以同分會贏」** —— 本檔同時有多個 session 在追加，
             「我在最後面」不是穩定的前提。
   §S-32   `.pos .pos-sidebar .pos-sidebar-nav .nav.nav-tabs{border-bottom:0}` (0,5,0)
           與 `… .nav-item .nav-link{border:none}` / `.nav-link.active{…}` (0,7,0)
           → 第⑤段補 §S-49 的兩條髮絲線，選擇器逐字同形，靠排在後面取勝。
             §S-32／§S-46b 原文一行未動。
   §S-49   `.fb-tabs{border:1px solid --muted-foreground/.28}` ＋
           `.fb-tab.active{border-color:--muted-foreground/.22}` → 第⑤段照抄這兩個值
           給還在用 `.nav-tabs` 的三支死頁（值一個字沒改，這是同一個設計）。
   §S-57   已把 `/pos/machine`・`/pos/package` 的側欄頁籤 markup 換成 `.fb-tabs--touch`，
           **那兩支活頁面不會被第⑤段打到**（它們沒有 `.nav-tabs` 了）⇒ 第⑤段對活頁面
           是 0 命中、零風險，只服務 checkout／darka／_package／machine_backup。
   §S-86 一 `.pos .pos-sidebar .pos-sidebar-header{background:hsl(var(--muted));
           color:hsl(var(--foreground))}` —— 見「三」④，側欄收合鈕的配色依據這條。
   §S-92   rail 收合（`.pos-rail--collapsed` / `.pos-rail-collapsed`、210/76px、
           `transition .15s linear`）**一個字不改**。本節的側欄收合是它的鏡像，
           連 transition 時距與雙軌命名都照抄。
           ★`.pos .pos-menu{z-index:10}`（§POS-2）⇒ 本節標題列取 `z-index:5`，rail 必須在上。

   ═══ 三、★被推翻的假設（規格與同批代理哪裡寫錯，直說）═══
   ① **規格 §5-A-1**「`.pos-menu` 內的 PerfectScrollbar 需要有界高度 ⇒ 改 static 有風險」
      —— 方向對、結論站不住（見「一」）。我選 (b) 不是因為 (a) 會壞，是因為 (a) 收益為 0。
   ② **規格 §5-A-4**「死頁的 `.nav-tabs.nav-fill` 要**長成** shadcn 分段控制器」
      —— 前提錯了：§S-32（08-03）已經把它做成分段控制器，§S-46b 又把軌道底壓深一階，
      **六支頁面全部涵蓋**。今天死頁那三支與 `.fb-tabs--touch` 的差距**只剩 §S-49 那兩條
      髮絲線**。所以第⑤段做的是「補兩條線」，不是「重做一個控制器」——
      重做只會生出第三份同語彙實作，正是 §S-57 剛收掉的帳。
   ③ **規格 §2 的標題列樣板字面**（`sticky top-0 z-10 … px-4 md:px-6 h-16`、`text-xl font-bold`）
      是從 Work OS（Tailwind 專案）逐字抄來的，**本專案沒有 Tailwind**。
      代理 C 掃過：`text-brand` / `shrink-0` / `text-xl` / `font-bold` 四個全站零定義
      （只有 `.pos-i` 有）。⇒ 那些字面在 blade 上是**死 class**，外觀 100% 由本節負責。
      本節連 `.text-brand` 都在 POS 作用域內補上定義（B/C/E/F 已經寫進 markup 了，
      不補的話標題圖示不會是品牌黃）。★**刻意只補在 `.pos` 內**，不做成全站工具類：
      那是 Tailwind 的命名，全站放行會誘導其他頁面繼續寫不存在的 utility。
   ④ **代理 C 的第 3 點說「`.pos-sidebar-header` 亮色是 `--dark-panel` 深底，
      outline 鈕會隱形」—— 這條是錯的，我實測推翻。**
      證據（不是推論）：`.pos .pos-sidebar .pos-sidebar-header{background:hsl(var(--muted))}`
      出現在 §S-86 一，與 §POS-4 的舊宣告**特異性完全相同 (0,3,0)**、無 !important，
      而且我用括號深度掃過：該行**位於巢狀深度 0**（不在任何 `@media` / `html.dark` 內），
      §S-86 排在 §POS-4 後面 ⇒ **亮暗兩個主題都由 §S-86 勝出**，頁首是 `--muted` ＋
      `--foreground`，2026-08-08 起就不是深底了。
      ★C 讀到的應該是 §S-46b（08-06）那句「`.pos-sidebar-header` 亮暗都是 rgb(24,24,27)」
        —— 那是 §S-86 兩天後改掉的**過期註解**（同 `feedback_superseded_spec_clause_must_be_marked_dead`
        那一族的坑：舊條文沒標作廢，後人照著寫出「完全符合舊規格的錯東西」）。
      ⇒ 側欄收合鈕與主區的叫回鈕**可以共用同一組 outline 配色**（`--background` 底 ＋
        `--border` 框 ＋ `--foreground` 字），亮暗都浮得出來。C 說的「兩顆不能共用配色」不成立。
   ⑤ **代理 E 說「checkout/darka 的 `.title` 沒有 `flex:1`，machine 有」** —— 那是在比 blade 的
      utility class。`flex:1` 是 §POS-4 的 CSS 給的（`.pos .pos-sidebar .pos-sidebar-header .title`），
      **六頁一視同仁**。不過 E 的**結論**是對的：收合鈕仍然用**自己身上的 `margin-left:auto`**
      靠右，不去依賴兄弟元素的 flex 值。本節照辦。
   ⑥ **規格 §5-A-3 的 `.pos-sidebar--collapsed{ … border:0 }`** 若照字面寫成 `border:0`，
      會連 `border-style`/`border-color` 一起重設，展開時 §POS-4 的邊框要靠串接重新長回來，
      transition 期間會閃一格。本節改寫成 `border-width:0`（只動寬度）。
   ⑦ **★★同批代理對標題列的擺法一度不一致（實測 grep，不是推測）**：
        `pos/machine`・`pos/checkout`・`pos/darka`・`pos/_package`
            → `.pos-page-header` 是 `.pos-content` 的**直接子元素**，在 `.pos-content-container` **之前**
        `pos/package`
            → `.pos-page-header` 在 `.pos-content-container` 的**裡面**（第一個子元素）
      兩邊各有一段查證註解、理由都成立（前者「不跟著捲走」、後者「不動高度算術」）。
      ★**協調者已裁決：一律放在容器外面（兄弟節點）**，並已要求 package 改過來。
      ⇒ 本節的主線（③(1) flex column）就是為「外面」寫的。
        ③(2) 的負邊距是**過渡期安全網**：我追加本節的當下 `package.blade.php` 尚未改完
        （grep 過，標題列仍在容器內）。留著它，package 在改完之前不會出現雙層內距、
        下框線也仍是滿版。**package 改完之後這條就是 0 命中的死碼，可以直接刪。**
        ★沒有留成「兩種都對」的曖昧：裁決寫在這裡，刪除條件也寫在這裡。

   ⑧ **協調者轉述的「`.pos-sidebar-header` 亮色是深底」我不採信，但也沒有押注在反面**
      —— 見④的證據。本節兩顆鈕都給**自己的不透明底色**（`--background`）而不是 transparent，
      所以無論那個表面最後是 `--muted`（我量到的）還是 `--dark-panel`（C 說的），
      白色/近黑的實心小方鈕配 1px 邊在兩種底上都看得見。
      **這不是折衷，是把按鈕做成不依賴背景的元件** —— 也就不必等這個爭議裁決完才能上線。
      ★爭議本身已如實回報協調者，由他決定要不要去修 §S-46b 那句過期註解。

   ═══ 四、class 合約（實測 markup 對過，不是照規格抄）═══
     `.pos-page-header`            主區標題列容器（h-16）
     `.pos-page-title` / 裸 `<h1>` 標題文字 —— ★**兩種都吃得到**
                                   （package 用 `.pos-page-title`，其餘 5 頁是裸 `<h1>`）
     `.text-brand` / `.pos-page-header-icon` / 標題列的**直接子** `<i>`／`<svg>`／`.lucide`
                                   → 20px 品牌色圖示。五個對象都寫，因為
                                     ★lucide 會把 `<i data-lucide>` 換成 `<svg class="lucide …">`
                                       並沿用 class（只寫一個會在替換前後有一格沒樣式，§S-92「八」同坑）
                                     ★package 用 `.pos-page-header-icon`，其餘 5 頁用 `.text-brand shrink-0`
     `.pos-page-header-actions`    右端工具區（選用；5 頁沒有包這一層，直接把鈕當直接子元素）
     `.pos-sidebar-toggle`         側欄收合鈕（`.pos-sidebar-header` 右端，36×36 純圖示）
     `.pos-sidebar-restore`        主區標題列右端「顯示紀錄」（圖示＋文字），**收合時才顯示**
     `.pos-sidebar-toggle-icon` / `.pos-sidebar-restore-text`  子元素（package 有掛，其餘沒掛，兩種都接）
     `.pos-sidebar--collapsed`     掛 `.pos-sidebar`（自己寬度歸零）
     `.pos-sidebar-collapsed`      掛外層 `.pos`（讓位歸零）—— 雙軌命名理由同 §S-92 rail
     委派鉤子 `[data-pos-sidebar-toggle]`（代理 D 的 IIFE 用；CSS 不碰）
     localStorage key：`seller-pos-sidebar-collapsed`；★**預設展開**（`!== '1'` 才展開），
       與 rail 相反（rail 預設收合照 OS）：側欄裝的是現場作業資訊，預設要看得到。
   ★★**兩顆鈕都是裸 `<button>`，不是 `.btn`**（六頁 markup 都確認過）⇒
     UA 預設樣式（Arial 13.333px、灰底、2px outset 邊）會露出來，所以本節必須自己給
     `font-family: inherit` 與完整外觀，不能只調高度。
   ★★**本節不做任何圖示旋轉**（與 §S-92 ⑩ 的 rail 收合鈕不同）：
     `.pos-sidebar-toggle` 寫死 `chevrons-right`、`.pos-sidebar-restore` 寫死 `chevrons-left`，
     是**兩顆不同的鈕**，不是同一顆在換圖示。CSS 只要控制誰顯示誰隱藏。
     rail 那顆是「一顆鈕 + CSS 翻轉」，這裡是「兩顆鈕 + CSS 顯隱」，兩種各用一處，不要混。

   ═══ 五、收合態規則一律關在 @media (min-width:992px) 內 ═══
   手機的 `.pos-sidebar` 是 `position:fixed; inset:0` 的全螢幕抽屜（§POS-4），
   靠 `.pos-sidebar-mobile-toggled` / `.pos-mobile-sidebar-toggled` 開關。
   桌機收合態的 `width:0` 若漏進手機，抽屜會打不開（開了也是 0 寬）。
   ⇒ 本節**所有** `pos-sidebar--collapsed` / `pos-sidebar-collapsed` 相關宣告都包在
     `@media (min-width:992px)` 裡；`.pos-sidebar-toggle` 在手機直接 `display:none`，
     `.pos-sidebar-restore` 的顯示規則也只在 ≥992px 內（手機永遠看不到這顆）。
   ★代理 D 已在 JS 端用 `matchMedia` 硬擋，但**不依賴那道閘門** ——
     JS 閘門與 CSS 閘門兩層都對才算數（同 §S-92「六」）。
   ★代理 D 另外確認：**找不到 `.pos-sidebar` 時外層 `.pos` 仍會掛 `pos-sidebar-collapsed`**。
     ⇒ 本節的讓位規則一律只咬外層 class，**不寫成依賴 `.pos-sidebar--collapsed` 存在的組合選擇器**。
   --------------------------------------------------------------------------- */


/* ① 外殼：維持 fixed ＋ padding（見章節註解「一」），只補 .pos-customer 三頁缺的讓位。
      ★兩支活頁面（package / machine）的讓位由 §POS-1 + §S-92 提供，本節一個字都不加。
      ★以下只打 `.pos-customer`（checkout / darka / _package）。 */
@media (min-width: 992px) {
    .pos.pos-customer:has(> .pos-menu) {
        padding-left: 210px;                       /* = §S-92 展開態 rail 寬 */
        transition: padding-left .15s linear;      /* 與 §S-92 的 .pos-with-menu 同時距 */
    }
    .pos.pos-rail-collapsed.pos-customer:has(> .pos-menu) {
        padding-left: 76px;                        /* = §S-92 收合態 rail 寬；(0,4,0) 勝 (0,3,0) */
    }
    .pos.pos-customer:has(> .pos-sidebar) {
        padding-right: 22.5rem;                    /* = §POS-1 給 .pos-with-sidebar 的同一個值 */
        transition: padding-right .15s linear;
    }
}
@media (max-width: 991.98px) {
    /* 手機 rail 是頂部橫向 bar；112px 是 §POS-1 量過 bar 實高（~104px）加緩衝得到的常數，
       這裡照用同一個值，不自己另算一個。 */
    .pos.pos-customer:has(> .pos-menu) {
        padding-top: 112px;
    }
}


/* ② 主區標題列 .pos-page-header
      OS PosLayout 的 Tailwind 原字面：`sticky top-0 z-10 flex items-center gap-2
      border-b border-border bg-background px-4 md:px-6 h-16`。本專案沒有 Tailwind，
      這裡把它整條翻成真的 CSS（見章節註解「三」③）。
      ★z-index 取 5 不是 10：`.pos .pos-menu` 是 z-index:10（§POS-2），rail 必須壓在
        標題列之上（手機頂 bar 尤其明顯）。OS 沒有這個約束是因為它的 rail 在 flex 流裡。
      ★用 min-height 不用 height：標題列在③的 flex column 底下是 flex 子項，
        `flex-shrink` 預設 1，寫死 height 會在內容擠壓時被壓扁。
      ★sticky 對「放在 .pos-content 底下」那四頁是 no-op（它本來就不在捲動容器裡），
        對 package（放在容器內）才真的生效。兩種擺法共用同一條規則，寫了無害。 */
.pos .pos-page-header {
    position: sticky;
    top: 0;
    z-index: 5;
    display: flex;
    align-items: center;
    gap: .5rem;
    flex: 0 0 auto;
    min-height: 64px;                     /* = OS h-16 */
    padding: 0 1rem;                      /* = OS px-4 */
    background: hsl(var(--background));
    border-bottom: 1px solid hsl(var(--border));
}
@media (min-width: 768px) {
    .pos .pos-page-header { padding: 0 1.5rem; }   /* = OS md:px-6 */
}

/* 標題左側圖示：20px、品牌色。五個對象的理由見合約（lucide 會換元素、兩種 class 名並存）。
   ★`> i` / `> svg` 用**直接子**選擇器：工具鈕內部的圖示不可以被染成品牌黃
     （那些走下面 16px 那條）。`.text-brand` 與 `.pos-page-header-icon` 則不限層級，
     因為那是作者明確標出來的「這顆是品牌色圖示」。 */
.pos .pos-page-header > i,
.pos .pos-page-header > svg,
.pos .pos-page-header > .lucide,
.pos .pos-page-header .pos-page-header-icon,
.pos .pos-page-header .text-brand {
    width: 20px;
    height: 20px;
    flex: 0 0 auto;                       /* ＝ OS 的 shrink-0（本專案沒有這個 utility） */
    color: hsl(var(--primary));           /* ＝ OS 的 text-brand，本檔 --primary 與它同值 */
}

/* 標題文字：OS 是 `text-xl font-bold`。
   ★★不寫這條的話裸 `<h1>` 會吃 **Bootstrap 預設 2.5rem**，直接把 64px 的標題列撐爆
     （代理 C 查證）。`<h1>` 另外會先吃到本檔 §2 基礎的 `h1..h6{font-weight:600}` (0,0,1)，
     這裡 (0,2,1) 蓋成 700。
   ★POS 沒有全站頂欄（layouts/pos.blade.php 掛 app-without-header、不 include 頂欄），
     h1 是每一頁唯一說得出「我在哪一頁」的地方，不能省。 */
.pos .pos-page-header h1,
.pos .pos-page-header .pos-page-title {
    margin: 0;
    min-width: 0;                         /* flex 子項預設 min-width:auto，長標題會撐破版面 */
    font-size: 1.25rem;                   /* = OS text-xl */
    line-height: 1.75rem;
    font-weight: 700;                     /* = OS font-bold */
    letter-spacing: -0.02em;
    color: hsl(var(--foreground));
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* 右端工具區（選用的包裝層）。5 頁沒有包這一層、直接把鈕當直接子元素 ——
   所以靠右不能靠這一層，下面每一顆鈕自己都有 margin-left:auto。 */
.pos .pos-page-header .pos-page-header-actions {
    margin-left: auto;
    display: flex;
    align-items: center;
    gap: .5rem;
    flex: 0 0 auto;
}

/* 標題列上若有 `.btn` 工具鈕 ＝ 36px（規格 §3 觸控階「工具鈕」那一格）。
   ★(0,5,0) 是為了硬贏 §S-86 二(a) 的 (0,4,0) 44px。那條 44px 是為「戴手套的現場作業員」
     訂的，針對的是**主要動作鈕**；標題列工具鈕不在作業動線上（規格把兩者分成不同格）。
   ★目前六頁的標題列都還沒有 `.btn`，這條是為 B~F 後續加工具鈕預留的合約，不是死碼。 */
.pos .pos-page-header .btn:not(.btn-close):not(.btn-xs) {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: .375rem;
    min-height: 36px;
    padding-top: 0;
    padding-bottom: 0;
}
.pos .pos-page-header .btn i,
.pos .pos-page-header .btn svg,
.pos .pos-page-header .btn .lucide {
    width: 16px;
    height: 16px;
    flex: 0 0 auto;
}


/* ③ 讓標題列與捲動容器共存。★協調者裁決：標題列一律放捲動容器**外面**（見「三」⑦）。

      (1) 【主線】machine / checkout / darka / _package：標題列是 `.pos-content` 的直接子元素。
          §POS-3 只給了 `flex:1;min-width:0;min-height:0`，**沒有 display:flex** ⇒
          第二個子元素 `.pos-content-container` 會被 _shell 第 9 段寫上 inline
          `style.height:100%`，變成 64px + 100% ⇒ 溢出 64px、底部被裁掉。
          ★直向 flex 下，inline 的 `height:100%` 只是 flex base size，`flex-shrink:1`
            會自動收掉那 64px ⇒ **不需要 !important**（代理 C 查證，本節採納；
            我原本打算寫 `height:auto !important`，那是多餘的）。
          ★`min-height:0` 不可省：flex 子項預設 min-height:auto，內容一長會撐破而不是捲動。
          ★無條件套用（不用 `:has` 守）：沒有標題列時，單一子元素 + height:100% 在
            block 與 column-flex 下的結果**逐像素相同**，所以不會動到今天的畫面。
            這也避開了「`:has()` 不支援就退回破版」的風險。 */
.pos .pos-content {
    display: flex;
    flex-direction: column;
}
.pos .pos-content > .pos-content-container {
    flex: 1 1 auto;
    min-height: 0;
}
/*    (2) 【過渡期安全網，可刪】package：追加本節時它的標題列仍在 `.pos-content-container`
          **裡面**（grep 確認）。協調者已裁決要改到外面，改完後這條就是 0 命中的死碼。
          在那之前留著它：§POS-3 給該容器 `.9375rem`＝15px 內距，標題列自帶 px-4/px-6
          ⇒ 疊成雙層內距、下框線也做不到滿版。這裡用等量負邊距推回 padding box 邊緣。
          ★sticky 的黏著矩形是**捲動容器的 padding box**，負邊距讓起始位置與黏著位置
            一致，捲動時不會有一條 15px 的縫。
          ★★刪除條件：`grep -A2 'pos-content-container' resources/views/pos/package.blade.php`
            確認標題列已不在容器內 ⇒ 本條連同這段註解一起刪。
          (0,5,0)（4 個 class + 1 個 :first-child），沒有競爭對手。 */
.pos .pos-content .pos-content-container > .pos-page-header:first-child {
    margin: -.9375rem -.9375rem .9375rem;
}


/* ④ 右側欄可收合（★本輪新功能）
      鏡像 §S-92 的 rail 收合：`.pos-sidebar--collapsed` 管自己變窄、
      `.pos-sidebar-collapsed` 管外層讓位。兩邊都要切，少切一邊就會出現
      「側欄不見了但右邊留一條 360px 空白」。全部關在 ≥992px 內（見章節註解「五」）。 */
@media (min-width: 992px) {
    /* 只列會變的四個屬性，不寫 `transition: all` —— all 會把 §POS-4 的
       border-radius / background 一起納入，切主題時側欄會跟著淡入。 */
    .pos .pos-sidebar {
        transition: width .15s linear,
                    margin-left .15s linear,
                    margin-right .15s linear,
                    opacity .15s linear;
    }
    .pos.pos-with-sidebar {
        transition: padding-right .15s linear;
    }

    /* (0,3,0) > §POS-4 的 (0,2,0)。
       ★只歸零左右 margin（理由見章節註解「二」§POS-4 那段）。
       ★用 border-width 不用 border（見「三」⑥）。
       ★`.pos-sidebar` 本體已有 `overflow:hidden`（§POS-4），寬度 0 時內容自動被裁乾淨，
         這裡再補 pointer-events:none 讓它連鍵盤/滑鼠都摸不到。 */
    .pos .pos-sidebar.pos-sidebar--collapsed {
        width: 0;
        min-width: 0;
        margin-left: 0;
        margin-right: 0;
        border-width: 0;
        opacity: 0;
        pointer-events: none;
    }

    /* 外層讓位歸零。★只咬外層 class，不依賴 `.pos-sidebar--collapsed` 是否存在
       （代理 D：找不到側欄時外層仍會掛 collapsed，主區讓位仍應歸零）。 */
    .pos.pos-sidebar-collapsed.pos-with-sidebar {
        padding-right: 0;                 /* (0,3,0) > §POS-1 的 (0,2,0) */
    }
    .pos.pos-sidebar-collapsed.pos-customer:has(> .pos-sidebar) {
        padding-right: 0;                 /* (0,4,0) > ① 那條 (0,3,0) */
    }
}

/* 兩顆鈕的共同外觀：36px outline。
   ★★兩顆都是**裸 `<button>`**（六頁 markup 都確認過），所以要自己給 font-family / 邊框 /
     底色，否則會露出 UA 的 Arial 13.333px ＋ 灰底 ＋ outset 邊。
   ★配色兩顆共用一組（`--background` 底 ＋ `--border` 框 ＋ `--foreground` 字）：
     `.pos-sidebar-header` 自 2026-08-08 的 §S-86 一 起，**亮暗都是 `--muted`**
     （不是舊註解說的 --dark-panel 深底，見章節註解「三」④，我用巢狀深度掃過確認），
     而 `.pos-page-header` 是 `--background` —— 兩個表面都能讓這組 outline 浮出來。
     這也與側欄頁腳「後台管理」那顆 outline 鈕同語彙（§S-46b 量過亮 255 / 暗 24）。 */
.pos .pos-sidebar-toggle,
.pos .pos-sidebar-restore {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: .375rem;
    min-height: 36px;
    height: 36px;
    padding: 0 .625rem;
    border: 1px solid hsl(var(--border));
    border-radius: var(--radius-md);      /* 8px ＝ 官方 Button 那一階 */
    background: hsl(var(--background));
    color: hsl(var(--foreground));
    font-family: inherit;                 /* ★裸 <button> 不繼承字型 */
    font-size: .8125rem;
    line-height: 1;
    cursor: pointer;
    transition: background-color .15s linear, color .15s linear, border-color .15s linear;
}
.pos .pos-sidebar-toggle:hover,
.pos .pos-sidebar-restore:hover {
    background: hsl(var(--accent));
    color: hsl(var(--accent-foreground));
}

/* 側欄收合鈕：36×36 純圖示方鈕，靠自己的 margin-left:auto 貼右端
   —— **不依賴兄弟元素的 flex 值**（代理 E 的結論，本節採納；
      §POS-4 的 `.title{flex:1}` 其實六頁都有，但不該把靠右建立在別人身上）。 */
.pos .pos-sidebar-toggle {
    margin-left: auto;
    flex: 0 0 auto;
    min-width: 36px;
    padding-left: 0;
    padding-right: 0;
}
.pos .pos-sidebar-toggle i,
.pos .pos-sidebar-toggle svg,
.pos .pos-sidebar-toggle .pos-sidebar-toggle-icon {
    width: 18px;                          /* 與 §S-92 ⑩ rail 收合鈕同尺寸 */
    height: 18px;
    flex: 0 0 auto;
}
/* `.title{flex:1}` 會把自由空間吃光 ⇒ margin-left:auto 算出來是 0，鈕會貼著 `.order` 徽章。
   只在有收合鈕時給 header 一點欄間距；桌機 `.back-btn` 是 display:none，
   所以這 8px 只落在 title / order / 鈕之間，不影響手機的返回鈕排版。 */
@media (min-width: 992px) {
    .pos .pos-sidebar .pos-sidebar-header:has(> .pos-sidebar-toggle) {
        column-gap: .5rem;
    }
}
/* 手機沒有「收合」這個概念（側欄是全螢幕抽屜，靠 .back-btn 關）。
   排在基準規則之後才蓋得掉 display:inline-flex（同分靠順序，這是本節內部順序，可控）。 */
@media (max-width: 991.98px) {
    .pos .pos-sidebar-toggle { display: none; }
}

/* 「顯示紀錄」叫回鈕：平常不顯示，只有外層掛了 .pos-sidebar-collapsed 才出現，且只在桌機。
   ★(0,2,0) 的 none 蓋得過裸 <button> 的 UA `display:inline-block`；顯示那條 (0,3,0) 再蓋回來。
   ★五頁沒有掛 `ms-auto`（只有 package 有）⇒ 這裡自己給 margin-left:auto 才會靠右。 */
.pos .pos-sidebar-restore {
    display: none;
    margin-left: auto;
    flex: 0 0 auto;
}
@media (min-width: 992px) {
    .pos.pos-sidebar-collapsed .pos-sidebar-restore {
        display: inline-flex;
    }
}
.pos .pos-sidebar-restore i,
.pos .pos-sidebar-restore svg,
.pos .pos-sidebar-restore .pos-sidebar-restore-icon {
    width: 16px;
    height: 16px;
    flex: 0 0 auto;
}


/* ⑤ 死頁三支（checkout / darka / _package）＋孤兒 machine_backup 的側欄頁籤：
      補上 §S-49 的兩條髮絲線，與活頁面的 `.fb-tabs--touch` 對齊。

      ── 為什麼只補兩條線，不是「做一個分段控制器」──
      §S-32（08-03）早就把 `.pos-sidebar-nav .nav.nav-tabs` 做成 shadcn 分段控制器
      （軌道 --muted、選中片 --background + shadow-sm、min-height:44px 觸控不縮），
      §S-46b 又把軌道底壓深一階到 --border。六支頁面**全部涵蓋**。
      §S-57 之後 machine / package 的 markup 換成 `.fb-tabs--touch`，順帶拿到 §S-49 的
      軌道外框與選中片髮絲邊 —— 沒換 markup 的四支就停在那兩條線之前。
      ⇒ 差距只有兩條線。規格說要「讓它長成分段控制器」是把前提看錯了（見「三」②）。

      ── 不改 blade 的 class ──
      規格明訂 CSS-only；§S-57 也已寫過那四支是沒有路由的孤兒／死頁，改 markup 只會
      多幾個沒人驗的檔（`_package` 更是一渲染就 500）。
      ★本段對 /pos/machine 與 /pos/package 是 **0 命中**（它們沒有 `.nav-tabs` 了）
        ⇒ 風險上限就是「三支死頁的頁籤多兩條 1px 灰線」。

      ── 選擇器與特異性 ──
      三條都與 §S-32 逐字同形（(0,5,0) 軌道、(0,7,0) 片），無 !important，靠排在後面取勝。
      §S-32／§S-46b 原文一行未動。

      ★`.nav-link` 的 `border: 1px solid transparent` 基線不可省：§S-32 給的是 `border:none`，
        直接只加 `.active{border-color}` 會讓選中片比未選中片胖 2px，切換時整排跳動。
        §S-49 不必處理這件事，是因為 §13 的 `.fb-tab` 本來就帶 1px 透明邊；這裡要自己補回來。 */
.pos .pos-sidebar .pos-sidebar-nav .nav.nav-tabs {
    border: 1px solid hsl(var(--muted-foreground) / .28);
}
.pos .pos-sidebar .pos-sidebar-nav .nav.nav-tabs .nav-item .nav-link {
    border: 1px solid transparent;
}
.pos .pos-sidebar .pos-sidebar-nav .nav.nav-tabs .nav-item .nav-link.active {
    border-color: hsl(var(--muted-foreground) / .22);
}


/* --- S-95. /pos/package 對齊 OS 排包頁「實測外觀」：關插畫、登入時數改灰字、標題列隱藏（2026-08-10）---

   ═══ 為什麼有這一節 ═══
   業主把本站 `/pos/package` 與賣賣 Work OS 的排包頁並排比對，結論是「跟 OS 設計差太多」。
   本輪用 puppeteer 對 https://test.2025-seller.mtest/pos/package 截 1440x900 實圖逐點比對，
   確認三處差異（**畫面實測，不是讀 OS 原始碼推論**）：

     (1) STEP 卡有大片橘色插畫，OS 是乾淨白卡        <- 最搶眼
     (2) 「已登入 N 分鐘」是品牌黃 pill，OS 是灰色小字
     (3) 主區有「揀包出貨」標題列，OS 這頁根本沒有標題列

   ★★★ (1) 的教訓要留在檔案裡：§S-92 (3) 之所以把插畫打開，是因為當時是**讀 OS 原始碼**
       看到 `img class="img ... opacity-70"` 就判定「OS 有插畫」。**原始碼有 != 畫面看得到**
       —— OS 實際渲染出來是乾淨白卡。本節以畫面實測為準把它關回去。
       同族教訓見 §S-86「不自己發明官方沒有的描邊」：兩者都是「別憑二手資訊改設計」。

   ═══ 一、全部走純 CSS，一個 blade 都不動 ═══
   (a) 插畫的 `illustration()` 留在 package.blade.php / machine.blade.php 裡**不拔** —— 那是
       DOM 與 JS 的接線，拔掉要同時動兩支檔、還要重排三段 template literal，風險高收益零。
       CSS 關掉就看不見。
   (b) 標題列的 markup（六支頁面今天剛加上去）也不拔，理由同上。

   ═══ 二、特異性一律用「重複 class」墊高，不靠檔尾位置取勝 ═══
   本檔同時有其他工作階段在檔尾追加章節（本節追加期間檔案就長了 10,256 bytes），
   **「同分後者勝」這個賭注隨時會被下一節推翻** —— §S-92 (3) 就是這樣寫的，
   也就是這樣被本節推翻的。所以本節每一條都比對手**高一分**：
     對手 `.pos #pos-list .product.not-available .pos-step-illust`                  (1,3,0)
     本節 `.pos #pos-list .product.not-available .pos-step-illust.pos-step-illust`  (1,4,0)
   同一個 class 在複合選擇器裡列兩次是 CSS 規格允許的合法寫法，
   不需要 `!important`，也不會把規則綁死在 DOM 結構上。

   ═══ 三、實測到的數字（1440x900，亮色，puppeteer computed style）═══
     `.pos-sidebar-header` 底色  computed = rgb(245,245,245) = `--muted`
        => **§S-86 一 勝出，§POS-4 的 `--dark-panel` 沒有生效**。舊註解說那是深底的都是
          過時資訊；本節重新量過才用（不是沿用推測）。
     `#user-login-total` 舊值    bg rgb(255,210,0) / color rgb(26,26,26) / 13px / 600 / r9999px
     `.pos-page-header`          height 64px，父層 `.pos-content` 是 flex column
        => §S-94 (2)-(2) 那條「過渡期安全網」`.pos-content-container > .pos-page-header:first-child`
          **現在是 0 命中的死碼**（標題列已在容器外）。本節只記錄，不刪別人的區塊。

   ═══ 四、(2) 的前景色為什麼不用 `--muted-foreground` ═══
   OS 那行是 `text-xs text-muted-foreground`，直覺照抄就是 `hsl(var(--muted-foreground))`。
   **實算對比度後否決**（WCAG 2.x，13px 屬 normal text，AA 門檻 4.5:1）：
     亮色 `--muted-foreground` #737373 on `--muted` #F5F5F5 = **4.349:1**  X 差 0.15 不到 AA
     暗色 `--muted-foreground` #A1A1A1 on `--muted` #262626 = 5.857:1      O
   改用本檔既有的「POS muted 表面上的次級文字」SSOT —— **`hsl(var(--foreground) / .6)`**，
   也就是 §13 未選中 `.fb-tab` 用的那一顆（§S-46b 記過「半透明會跟著新底色重新合成」）：
     亮色 合成後 #626262 on #F5F5F5 = **5.594:1** O
     暗色 合成後 #A5A5A5 on #262626 = **6.154:1** O
   ★這不是發明新顏色：同一個側欄裡、同一個 `--muted` 表面上，未選中頁籤早就是這顆
     （實測 `.fb-tab` computed color = rgba(0,0,0,.6)，底 rgb(245,245,245)）。
     視覺上仍是「灰色小字」，與 OS 的意圖一致，而且亮色不再踩線。
   ★字級維持 13px 不降到 OS 的 12px：§POS-10「現場字要夠大」的裁示還活著
     （§S-86 也是為了同一條理由刻意不照官方）。字重 600 -> 500：600 是為了在黃底上撐住，
     底沒了就不需要，500 = OS 的 font-medium。

   ═══ 五、(3) 的單向門：標題列一藏，收合的側欄就再也叫不回來 ═══
   ★★`.pos-sidebar-restore`（唯一的「顯示紀錄」叫回鈕）**住在標題列裡面**（六支頁面都是）。
     `display:none` 會連子孫一起藏 => 直接把整條標題列關掉 = 側欄收合後無法復原；
     而 §S-94 (4) 把收合狀態寫進 localStorage `seller-pos-sidebar-collapsed`，
     **重新整理也救不回來** —— 這是貨真價實的單向門，絕對不可以留。

   ── 選定做法：標題列平常整條隱藏；外層掛上 `.pos-sidebar-collapsed` 時，把它變回一條
      無底色、無框線、無標題的「浮出列」，裡面只剩那顆叫回鈕 ──
   為什麼選這個，而不是「把叫回鈕改成絕對定位浮在主區右上」：
     · 主區的捲動容器是 `.pos-content-container`（`data-scrollbar` 接管），把鈕絕對定位
       進容器會跟著捲走；定位在容器外又會蓋住第三張 STEP 卡的右上角（1440 寬會重疊）。
     · 本做法不動定位模型：`.pos-content` 是 flex column，這一列只在收合態存在
       —— 正常態 0px、收合態約 44px，**高度只會比現況（恆 64px）更小**，
       `data-height="100%"` 的捲軸不會算錯。
     · 鈕的位置與使用者「收起它時按的那顆」（側欄右上）在同一條水平帶上，心智模型連續。
   ★六支頁面的標題列**只有 icon + h1 + 叫回鈕三個直接子元素**（已 grep 全部確認；
     `.pos-page-header-actions` 在 markup 裡是 **0 命中**）=> 用 `> *:not(.pos-sidebar-restore)`
     藏掉其餘子元素，不會誤殺任何功能鈕。
     ★★日後往標題列加真的動作鈕，**必須回來把它加進這個 `:not()` 白名單**，
       否則那顆鈕在側欄收合態會靜默消失。
   ★手機（<992px）不存在「收合」這個概念（側欄是全螢幕抽屜，靠 `.back-btn` 關），
     所以收合態那組規則整包關在 `@media (min-width: 992px)` 內，與 §S-94 (4) 同斷點，
     不會出現「手機冒出一條空列」。

   ═══ 六、為什麼標題列只關 /pos/package 一頁，不是六頁一起關 ═══
   我只有 package 這一頁的**畫面實測證據**（業主並排比對的就是這頁）。
   §S-94 的章節註解引用 OS `PosLayout` 的原字面裡是**有** `h-16` 標題列的
   => 合理解讀是「OS 的 PosLayout 有標題列這個槽位，排包頁沒有傳標題進去所以不渲染」，
   而不是「OS 全部 POS 頁都沒有標題列」。把 5 支我沒截過圖的頁面一起關掉，
   就又犯了本節開頭記的那個錯（**憑推論改設計**）。
   ★scope 用 `:has(> .pos-page-title)`：六支頁面裡**只有 package 的 h1 掛了 `.pos-page-title`**
     （其餘 5 頁是裸 `h1`，見 §S-94 表格「package 用 `.pos-page-header-icon`，其餘 5 頁用
     `.text-brand shrink-0`」）—— 這是現成、零改動的 package-only 標記。
   ★★要擴大到六頁只需要**把三條選擇器裡的 `:has(> .pos-page-title)` 刪掉**，其餘不動。
     （`:has` 在 Chrome 105+ 支援；POS 本來就只支援 Chrome，且 §S-94 (4) 已在用 `:has`。）
   -------------------------------------------------------------------------- */

/* (1) STEP 卡插畫關回去（推翻 §S-92 (3)）。
       對手兩條：`... .not-available .pos-step-illust` (1,3,0)
                 `... .not-available .img.pos-step-illust` (1,4,0)
       這裡各高一分 (1,4,0) / (1,5,0)，不靠檔尾位置。
       ★用 display:none 不用 opacity:0：後者仍佔版面、仍在 stacking context 裡，
         而且 §S-92 (3) 給的 `position:absolute; inset:0` 會繼續蓋在卡片上。 */
.pos #pos-list .product.not-available .pos-step-illust.pos-step-illust,
.pos #pos-list .product.not-available .img.pos-step-illust.pos-step-illust {
    display: none;
}

/* (2) overlay 面回到實心卡片色。
       §S-92 (4) 給的 `hsl(var(--background) / .8)` 是**為了透出插畫**才半透明的；
       插畫沒了之後它只剩壞處：
         亮色 rgba(250,250,250,.8) 疊在 `--card` 白上 = #FCFCFC，與卡片其餘部位差一階，
              邊界會浮出一條看得見的接縫；
         暗色 #0A0A0A/.8 疊在 #171717 上會糊成髒灰。
       回到 §S-45 (1) 原本的 `hsl(var(--card))`，與 `.pos-step-card` 的底完全同色。
       對手 §S-92 (4) 是 (1,4,0) => 這裡 (1,5,0)。 */
.pos #pos-list .product.not-available .not-available-text.pos-step-overlay.pos-step-overlay {
    background: hsl(var(--card));
}


/* (3) 「已登入 N 分鐘」：品牌黃 pill -> 灰色小字（OS `text-xs text-muted-foreground` 的語意）。
       用 **id 選擇器**精準命中，**不動 `.order` 這個 class 本身** —— 側欄頁首的 `.order`
       是共用槽位（六支頁面都在用），改 class 會波及其他徽章。
       對手：§POS-4  `.pos .pos-sidebar .pos-sidebar-header .order` (0,4,0)
             §S-86 一 同選擇器 (0,4,0)（border / border-radius）
       本條 (1,3,0)，id 那一分穩勝，與檔尾順序無關。
       ★前景色 `hsl(var(--foreground) / .6)` 的對比度實算見章節註解「四」。
       ★`border: 0` 是把 §S-86 補的那圈 1px 透明邊一併拿掉：沒有底色之後，
         那 2px 盒模型只會讓這行字跟旁邊 36px 的收合鈕對不齊。 */
.pos .pos-sidebar .pos-sidebar-header #user-login-total {
    background: transparent;
    color: hsl(var(--foreground) / .6);
    border: 0;
    border-radius: 0;
    padding: 0;
    font-size: .8125rem;                  /* 13px，維持 §POS-10「現場字要夠大」 */
    font-weight: 500;                     /* = OS font-medium；600 是黃底時代的遺產 */
    line-height: 1.25;
    white-space: nowrap;                  /* 「已登入 128 分鐘」不要在 flex 裡被擠成兩行 */
}


/* (4) 主區標題列：OS 排包頁沒有這條，整條藏起來（只限 package，理由見章節註解「六」）。
       (0,4,0)（重複 class + `:has` 的 (0,1,0)）> §S-94 (2) 的 (0,2,0)，
       也不怕別的工作階段再往檔尾追加同分規則。 */
.pos .pos-page-header.pos-page-header:has(> .pos-page-title) {
    display: none;
}

/* (5) 單向門的解：收合態把標題列變回「只有叫回鈕」的一條浮出列（理由見章節註解「五」）。
       (0,5,0) > (4) 的 (0,4,0)。整包關在桌機斷點內，與 §S-94 (4) 同斷點。 */
@media (min-width: 992px) {
    .pos.pos-sidebar-collapsed .pos-page-header.pos-page-header:has(> .pos-page-title) {
        display: flex;
        position: static;                 /* §S-94 (2) 的 sticky 在這裡本來就是 no-op，清掉更誠實 */
        z-index: auto;
        min-height: 0;
        /* 右內距對齊 `.pos-content-container` 的 §POS-3 .9375rem => 鈕的右緣與內容右緣切齊。
           上內距 .75rem 讓鈕落在與 rail 頂端相近的視覺帶；下 0，間距交給下面容器自己的 15px。 */
        padding: .75rem .9375rem 0;
        background: transparent;          /* 不要 `--background` 色塊：那就又變回一條標題列了 */
        border-bottom: 0;                 /* §S-94 (2) 的分隔線一起收掉 */
        justify-content: flex-end;        /* 保險：即使哪天 `.pos-sidebar-restore` 的 margin-left:auto 沒了 */
    }
    /* 標題與圖示不要跟著回來 —— 只放行叫回鈕。
       `:not()` 的分數取自括號內（class 1 分）=> 本條 (0,6,0)，蓋得過 §S-94 (2) 給
       `.pos-page-header > i/svg/.lucide` 與 `h1` 的那幾條。
       ★★往標題列加真的動作鈕時，記得把它加進這個 `:not()` 白名單（見章節註解「五」末）。 */
    .pos.pos-sidebar-collapsed .pos-page-header.pos-page-header:has(> .pos-page-title) > *:not(.pos-sidebar-restore) {
        display: none;
    }
}

/* --- S-96. STEP 卡陰影被 `.product.not-available` 的 !important 蓋掉（2026-08-10）------------

   ═══ 問題：實測 boxShadow=none，但 §S-92 ② 明明寫了 shadow-sm ═══
   §S-92 ②：`.pos .pos-step-card { box-shadow: 0 1px 2px 0 rgb(0 0 0 / .05); }`
   特異性 (0,2,0)，**無 !important**。瀏覽器 computed style 實測卻是 `none`。

   ═══ 真兇：不是特異性輸、也不是檔案順序輸，是被 !important 直接淘汰 ═══
   `pos-step-card` 這個 class **只會**跟 `not-available` 一起掛在同一顆 DOM 上——
   `machine.blade.php:458` 與 `package.blade.php:451` 都寫死同一行：
     `const availableClass = isAvailable ? '' : 'not-available pos-step-card';`
   只有「未登入」分支才會追加 `pos-step-card`，而那個分支必定同時追加
   `not-available`。所以實際 class 組合永遠是 `product not-available pos-step-card`。
   本檔「補位：.product」段第 4018 行早就寫了：
     `.product.not-available { cursor: not-allowed; box-shadow: none !important; }`
   特異性 (0,2,0)，**帶 !important**。CSS 層疊規則：一般宣告永遠輸給 !important 宣告，
   跟誰的特異性高、誰在檔案後面**完全無關**——§S-92 就算搬到檔尾最後一行也贏不了。
   已用 `grep -n "not-available" shadcn-theme.css | grep "box-shadow\|!important"`
   掃過全檔，命中僅此一條，確認就是它。

   ═══ 修法：沿用 §S-95「重複 class 墊高特異性」的手法，但對手帶 !important，
        單純墊特異性贏不了 !important，所以本節也要帶 !important ═══
   對手 `.product.not-available`               = (0,2,0) + !important
   本節 `.pos .pos-step-card.pos-step-card`    = (0,3,0) + !important
   兩邊都是 !important 時才輪到比特異性，本節較高 ⇒ 穩勝，不必依賴檔尾順序。
   -------------------------------------------------------------------------- */
.pos .pos-step-card.pos-step-card {
    box-shadow: 0 1px 2px 0 rgb(0 0 0 / .05) !important; /* shadow-sm，補回被 §S-96 註解所述 !important 蓋掉的陰影 */
}

/* --- S-97. /pos/package ＋ /pos/machine 逐像素校正到賣賣 Work OS（2026-08-10）------------

   ═══ 為什麼還有這一節（§S-95 已經對過一輪了）═══
   業主連續三次表示不滿（「跟 POS 差超多」「非常差勁」「有沒有好好用 shadcn 模仿重構」）。
   §S-95 只修了三處**顏色/顯隱**，沒有動**版面骨架**——側欄頁首仍是橫排、rail 頂部仍是
   直排置中兩大塊、卡片內距仍是 20px。本節用 puppeteer 對
   `https://test.2025-seller.mtest/pos/package`（1600x950）逐項量 computed style，
   改完再量，數字對上才收工。**每一條的改前/改後值都記在下面。**

   ═══ 取捨位階（業主 2026-08-07 定調「都要依照 shadcn 為主」＋ 2026-08-10 追加）═══
       shadcn 官方 > 賣賣 Work OS 實作 > 本專案現況 > 個人美感
   OS 只是「已經照 shadcn 做好的參考實作」，衝突時以官方為準。本節每條都標了照的是哪一邊。
   ★★查到的 upstream 出處（實際 fetch，不是憑印象）：
     · `shadcn-ui/ui` `apps/v4/registry/new-york-v4/ui/card.tsx`
         Card        = "flex flex-col gap-6 rounded-xl border bg-card py-6 text-card-foreground shadow-sm"
         CardHeader / CardContent / CardFooter 都是 `px-6`
       ⇒ 官方卡片 = 圓角 rounded-xl、內距 24px、shadow-sm、1px border。
     · `shadcn-ui/ui` `apps/v4/app/globals.css`
         :root { --radius: .625rem }
         @theme inline { --radius-sm: calc(var(--radius)*.6); --radius-md: calc(var(--radius)*.8);
                         --radius-lg: var(--radius);          --radius-xl: calc(var(--radius)*1.4); }
       ★★本檔 :root 實測 `--radius-md: calc(0.625rem * 0.8)`、`--radius-xl: calc(0.625rem * 1.4)`
         ⇒ **本專案的 radius 公式已經與官方逐字相同**。交辦單提到的「本專案是
         calc(0.625rem − 2px)、官方是 ×0.8」是**過時情報**，實測已無分岔，本節不需處理。
     · `tailwindlabs/tailwindcss` `packages/tailwindcss/theme.css`
         --shadow-xs = 0 1px 2px 0 rgb(0 0 0 / .05)
         --shadow-sm = 0 1px 3px 0 rgb(0 0 0 / .1), 0 1px 2px -1px rgb(0 0 0 / .1)
         --text-xs = .75rem/1rem   --text-sm = .875rem/1.25rem   --text-base = 1rem/1.5rem
     · OS `package.json` → `"tailwindcss": "^3.4.19"`；OS `tailwind.config.js` 第 24 行
         borderRadius: { lg: var(--radius), md: calc(var(--radius) - 2px), sm: calc(var(--radius) - 4px) }
       ⇒ **OS 只覆寫 lg/md/sm，`xl` 仍是 Tailwind v3 內建 .75rem = 12px**，
         而 OS 的 `shadow-sm` 是 **v3 的** `0 1px 2px 0 rgb(0 0 0/.05)`（＝ v4 的 shadow-xs）。
       ⇒ 卡片圓角與陰影，**OS 與官方是真的分岔**，本節照官方（見 (5)(6)）。

   ═══ 三個「假設被推翻」，必須留檔 ═══
   (甲) 交辦單說「STEP 卡固定 200px，要改 min-height 讓它隨內容長高」。**假的。**
        現況 `.pos-step-card{height:100%;min-height:200px}` 在 `align-items:stretch` 的 grid 裡
        本來就會長高——我把 STEP.1 標題灌成四行實測：三張卡 200 → **240**，還原後回到 200。
        而且 OS 的寫法（`Package.vue:1044` `#pos .pos-steps{...align-items:stretch}`
        ＋ 卡片 `h-full min-h-[200px]`）與我們**逐字相同**。⇒ **本項不改**，改了反而會弄壞
        「三張卡等高」這個 OS 也有的行為。
   (乙) 交辦單說「rail 返回鈕 63×65」。63×65 是**外框 `.pos-rail-back`**（`pt-3 pb-1` 撐的），
        真正的鈕 `a.btn.h-45px` 實測 **40×45**。要打的是 40×45 → 36×36，順便收外框內距。
   (丙) §S-95「四」算過 `--muted-foreground` 在 `--muted` 底上只有 4.349:1（未達 AA），
        所以「已登入 N 分鐘」改用 `hsl(var(--foreground)/.6)`。
        **本節把側欄頁首底色從 `--muted` 換成 `--card`（白）之後，那個前提消失了**：
        #737373 on #FFFFFF 實算 **4.74:1** ✓ 過 AA ⇒ 可以、也應該照 OS/官方用
        `hsl(var(--muted-foreground))`。這不是推翻 §S-95 的算式，是它的前提被改掉了。

   ═══ 特異性一律「重複 class 墊高」，不賭檔尾順序（沿用 §S-95「二」）═══
   本檔同時有別的工作階段在檔尾追加章節，「同分後者勝」隨時會被下一節推翻。
   -------------------------------------------------------------------------- */

/* ══════════════════════════════════════════════════════════════════════════
   (1) 側欄頁首：橫排 → 上下兩行（照 OS）
       OS 原字面（`views/pos/Package.vue:31-38`）：
         <div class="flex items-center gap-2 border-b border-border px-4 h-16 shrink-0">
           <button class="lg:hidden inline-flex h-9 w-9 ...">   <- 手機才有的返回鈕
           <div class="min-w-0">
             <div class="truncate text-base font-bold text-foreground">{{ sidebarUserName }}</div>
             <div class="text-xs text-muted-foreground">{{ sidebarLoginTotal }}</div>
           </div>
         </div>
       ★我方 DOM **沒有** OS 那顆 `div.min-w-0` 包裝層（`.title` 與 `.order` 是頁首的平輩子元素），
         而交辦單明令不動 blade ⇒ 用 **grid + grid-template-areas** 在 CSS 端做出那層包裝的效果：
           左欄放返回鈕（桌機 display:none ⇒ 該欄寬度自動塌成 0）、
           中欄上下兩列放標題/副標、右欄收合鈕跨兩列。
       ★`column-gap` 一律 0、間距改由項目自己的 margin 給 —— grid 的 gap 在「該欄沒有項目」時
         **仍然佔位**，桌機會平白多出 8px 左內距。
       ★底色改 `--card` 是**刻意推翻 §S-86 一** 的決定（那節把它設成 `--muted`）。
         但 §S-86 提出的機制仍然成立且必須遵守：**頁首與側欄同色時，分層完全靠下框線**，
         所以 `border-bottom: 1px solid hsl(var(--border))` 一定要留（暗色 `--border` 自帶 alpha，
         ★絕不可寫成 `hsl(var(--border) / .5)`，那是 §S-72 記過的雙斜線語法錯誤）。
       特異性：§POS-4 / §S-86 都是 `.pos .pos-sidebar .pos-sidebar-header` (0,3,0)，
               本節重複 class ⇒ (0,4,0)，穩勝。
   ══════════════════════════════════════════════════════════════════════════ */
.pos .pos-sidebar .pos-sidebar-header.pos-sidebar-header {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    grid-template-areas:
        "back title toggle"
        "back sub   toggle";
    column-gap: 0;
    row-gap: 0;
    align-content: center;              /* 兩列是 auto 高，整組垂直置中在 64px 裡 */
    height: 64px;                       /* OS h-16 */
    min-height: 64px;                   /* 蓋掉 §POS-4 的 48px */
    padding: 0 1rem;                    /* OS px-4；上下內距由固定高度 + align-content 取代 */
    background: hsl(var(--card));       /* OS：aside 是 bg-card，頁首自己沒有底色 */
    border-bottom: 1px solid hsl(var(--border));
}
.pos .pos-sidebar .pos-sidebar-header.pos-sidebar-header > .back-btn {
    grid-area: back;
    /* OS 手機返回鈕＝`h-9 w-9 rounded-lg` 的無底色 ghost 鈕，不是 CA 那塊 44px 灰方塊。
       §POS-4 的負 margin 是配「頁首有上下內距」用的，改成固定 64px 之後會把鈕頂出去。 */
    width: 36px;
    height: 36px;
    margin: 0 .5rem 0 0;
    background: transparent;
    border-radius: var(--radius);
}
.pos .pos-sidebar .pos-sidebar-header.pos-sidebar-header > .title {
    grid-area: title;
    flex: none;                         /* §POS-4 的 flex:1 在 grid 裡無意義，清掉 */
    min-width: 0;
    font-size: 1rem;                    /* OS text-base 16px（本檔原 15px） */
    line-height: 1.5rem;                /* v4 --text-base 的行高 */
    font-weight: 700;                   /* OS font-bold（本檔原 600） */
    color: hsl(var(--foreground));
    overflow: hidden;                   /* OS `truncate` 三件套 */
    text-overflow: ellipsis;
    white-space: nowrap;
}
.pos .pos-sidebar .pos-sidebar-header.pos-sidebar-header > .order {
    grid-area: sub;
}
.pos .pos-sidebar .pos-sidebar-header.pos-sidebar-header > .pos-sidebar-toggle {
    grid-area: toggle;
    align-self: center;
    margin-left: .5rem;                 /* OS gap-2 */
}
/* 副標的字色/字重：對手是 §S-95 (3) 的 `… .pos-sidebar-header #user-login-total` (1,3,0)，
   本節重複 header class ⇒ (1,4,0)，穩勝。理由見章節註解「(丙)」。
   ★字級**維持 13px 不降到 OS 的 12px**：§POS-10「現場字要夠大」的裁示還活著
     （§S-86 二、§S-95 四 都為同一條理由刻意不照官方）。
   ★字重 500 → 400：§S-95 註解寫「500 = OS font-medium」是**看錯了**——
     OS 那個 div 的 class 只有 `text-xs text-muted-foreground`，**沒有任何 font-* class**，
     祖先也沒有設 ⇒ OS 實際是 400。這裡照 OS 更正。 */
.pos .pos-sidebar .pos-sidebar-header.pos-sidebar-header #user-login-total {
    color: hsl(var(--muted-foreground));
    font-size: .8125rem;
    font-weight: 400;
    line-height: 1.25;
}

/* ══════════════════════════════════════════════════════════════════════════
   (2) rail 返回鈕：40x45 → 36x36（照 OS `PosMenu.vue` 的 `w-9 h-9`，圖示 `:size="16"`）
       ★對手是 utility `.h-45px{height:45px !important}`（本檔第 3037 行）——
         **帶 !important 的宣告只能用 !important 打**，墊特異性沒用（同 §S-96 的教訓）。
       ★`min-height` 來自 `.btn` 的 36px，已經對；這裡補 width/height 讓它變正方形。
       兩個收合狀態共用，所以不寫在 `:not(.pos-rail--collapsed)` 裡。
   ══════════════════════════════════════════════════════════════════════════ */
@media (min-width: 992px) {
    .pos .pos-menu .pos-rail-back .btn {
        width: 36px !important;
        height: 36px !important;
        min-width: 36px;
        min-height: 36px;
        padding: 0;
        flex: 0 0 auto;
    }
    /* lucide 的 createIcons 會吐出寫死 width/height="24" 的 <svg>，CSS 蓋掉即可。 */
    .pos .pos-menu .pos-rail-back .btn svg {
        width: 16px;
        height: 16px;
    }

    /* ── 收合態頂部：OS 是 `flex flex-col items-center gap-1 pt-3 pb-2`（鈕 + 時鐘直排）──
         現況 `.pos-rail-back` 是 `pt-3 pb-1`（16/4，BS utility 帶 !important）＋
         §S-92 給 `.logo` 的 `.625rem .375rem`，實測整塊 65 + 35 = 100px，比 OS 的約 75px 高一截。 */
    .pos .pos-menu.pos-rail--collapsed .pos-rail-back {
        padding-top: .75rem !important;     /* OS pt-3 */
        padding-bottom: .25rem !important;  /* OS gap-1 */
    }
    .pos .pos-menu.pos-rail--collapsed.pos-rail--collapsed .logo {
        padding: 0 .375rem .5rem;           /* OS pb-2；(0,5,0) > §S-92 的 (0,4,0) */
    }
    /* 時鐘：OS `text-[10px] text-muted-foreground tabular-nums`。
       ★色票用 `--muted-foreground-on-muted`（本檔 §S-31 / §S-92 ⑩ 的既有結論）**不是**
         `--muted-foreground` —— rail 的底是 `--muted`，後者實測只有 4.40:1 未達 AA。
         這是 §S-37 已經踩過並結案的坑，照它的結論；與 (1) 的側欄副標不同，是因為那邊的底
         已經被本節換成白色的 `--card`。
       ★字重維持 600（§POS-10「現場字要夠大」）：10px 的時鐘掉到 400 現場看不清。 */
    .pos .pos-menu.pos-rail--collapsed.pos-rail--collapsed .logo #pos_time {
        color: hsl(var(--muted-foreground-on-muted));
        font-variant-numeric: tabular-nums;
    }
}

/* ══════════════════════════════════════════════════════════════════════════
   (3) rail 展開態（210px）頂部：兩塊直排置中（合計 152px）→ 一列橫排（64px）
       OS 原字面（`components/pos/PosMenu.vue`，展開分支）：
         <div class="h-16 flex items-center gap-2.5 px-3 border-b border-border">
           <button class="w-9 h-9 rounded-lg border border-border bg-background …"><ChevronLeft :size="16"/></button>
           <div class="flex flex-col leading-tight min-w-0">
             <span class="text-base font-extrabold tracking-wide text-foreground">MYPOS</span>
             <span class="text-xs text-muted-foreground tabular-nums">{{ currentTime }}</span>
           </div>
         </div>
       ★我方 `.pos-rail-back` 與 `.logo` 是 `.pos-menu` 的**平輩子元素**（flex column），
         不動 blade 就不能把它們塞進同一個 wrapper ⇒ 把 `.pos-menu` 自己改成 grid，
         用 grid-template-areas 讓這兩個平輩落在**同一列的左右兩欄**。
       ★`.pos-menu` 的五個子元素已 grep 全部列出且 class 穩定（`.pos-rail-back` / `.logo` /
         `.pos-rail-sep` / `.nav-container` / `.pos-menu-footer`），五個全部指定 grid-area，
         **沒有任何子元素會掉進 implicit row**。日後往 rail 加第六個子元素，
         ★★必須回來這裡補一個 area，否則它會被擠到 footer 下面。
       ★整包關在 `@media (min-width:992px)` ＋ `:not(.pos-rail--collapsed)`：
         手機的 rail 是頂部橫向 bar（`.pos-rail-back` 帶 `d-none`），完全不受影響。
       特異性 (0,3,0) > §POS-2 / §S-92 ⑨ 的 (0,2,0)。
   ══════════════════════════════════════════════════════════════════════════ */
@media (min-width: 992px) {
    .pos .pos-menu:not(.pos-rail--collapsed) {
        display: grid;
        grid-template-columns: auto minmax(0, 1fr);
        grid-template-rows: 64px auto minmax(0, 1fr) auto;
        grid-template-areas:
            "back logo"
            "sep  sep"
            "nav  nav"
            "foot foot";
    }
    .pos .pos-menu:not(.pos-rail--collapsed) > .pos-rail-back {
        grid-area: back;
        align-items: center;
        padding: 0 !important;              /* 打掉 BS 的 pt-3/pb-1（帶 !important） */
    }
    .pos .pos-menu:not(.pos-rail--collapsed) > .logo {
        grid-area: logo;
        justify-content: flex-start;        /* OS 是靠左，不是置中 */
        padding: 0 0 0 .625rem;             /* OS gap-2.5 = 10px（用內距代替 grid gap） */
    }
    .pos .pos-menu:not(.pos-rail--collapsed) > .pos-rail-sep     { grid-area: sep; }
    .pos .pos-menu:not(.pos-rail--collapsed) > .nav-container    { grid-area: nav; }
    .pos .pos-menu:not(.pos-rail--collapsed) > .pos-menu-footer  { grid-area: foot; }

    /* MYPOS：24px/500/letter-spacing 1px/置中 → OS 的 text-base font-extrabold tracking-wide 靠左 */
    .pos .pos-menu:not(.pos-rail--collapsed) .logo .logo-text .h4 {
        font-size: 1rem;                    /* OS text-base */
        font-weight: 800;                   /* OS font-extrabold */
        letter-spacing: .025em;             /* OS tracking-wide */
        line-height: 1.25;                  /* OS leading-tight */
        margin-bottom: 0;
        display: block;
    }
    /* 時鐘：14px/600/置中 → OS text-xs muted 靠左。
       `text-align:left` 要 !important —— blade 上掛的是 BS 的 `.text-center`（帶 !important）。
       色票理由同 (2)：rail 的底是 --muted，用 --muted-foreground-on-muted。 */
    .pos .pos-menu:not(.pos-rail--collapsed) .logo #pos_time {
        text-align: left !important;
        font-size: .75rem;                  /* OS text-xs */
        font-weight: 400;
        letter-spacing: 0;
        line-height: 1rem;
        margin-top: 0;
        color: hsl(var(--muted-foreground-on-muted));
        font-variant-numeric: tabular-nums;
    }
    /* 展開態選單項：OS `text-sm font-semibold`（14px/600），我方現況 12px/500。
       高度 40px / 內距 12px / gap 10px 已經由 §S-92 ⑨ 對上，這裡只補字。
       (0,7,0) > §S-92 ⑨ 的 (0,6,0)。 */
    .pos .pos-menu:not(.pos-rail--collapsed) .nav.nav-tabs .nav-item .nav-link.nav-link {
        font-size: .875rem;
        font-weight: 600;
    }
    /* 底部收合鈕文字：OS `text-xs font-semibold`。(0,4,0) > §S-92 ⑩ 的 (0,2,0)。 */
    .pos .pos-menu:not(.pos-rail--collapsed) .pos-rail-toggle .pos-rail-toggle-text {
        font-weight: 600;
    }
}

/* ══════════════════════════════════════════════════════════════════════════
   (4) STEP 卡下方那條 hr：恢復顯示（業主的基準畫面**有**這條線）
       OS 原字面（`views/pos/Package.vue:21`）：`<hr class="my-5 border-border">`
         Tailwind preflight 給 hr：`height:0; border-top-width:1px`；`my-5` = 20px；
         `border-border` = `--border` ⇒ 20px 上下外距、1px `--border` 實線。
       ★不刪 §S-92 ⑧ 的 `.pos hr:has(+ #log-list:empty){display:none}`（交辦單明令只能追加），
         用特異性蓋過去。對手 (1,2,1)（`:has()` 取括號內最高分，`#log-list` 佔 id 那一格），
         本節重複 class ⇒ (1,3,1)，**不靠檔尾順序**。
       ★`:has(+ #log-list)` 不寫 `:empty`：log-list 有內容時這條線同樣要在（OS 一直都在）。
       ★`opacity:1` / `background:transparent` 是打 Bootstrap 對 hr 的 `opacity:.25` +
         `background-color:currentColor` 預設，不打的話會多出一條半透明色塊。
   ══════════════════════════════════════════════════════════════════════════ */
.pos hr.pos-log-sep.pos-log-sep:has(+ #log-list) {
    display: block;
    height: 0;
    margin: 1.25rem 0;
    padding: 0;
    border: 0;
    border-top: 1px solid hsl(var(--border));
    background: transparent;
    opacity: 1;
}

/* ══════════════════════════════════════════════════════════════════════════
   (5) STEP 卡內距 20px → 24px（**照 shadcn 官方，刻意不照 OS**）
       官方 Card = `py-6` + 子元件 `px-6` ⇒ 24px。
       OS 自己的卡片常數是 `p-4`（16px，`Package.vue:344` 的 `CARD` 與 `:403` 的 overlay），
       **OS 在這一項偏離官方**，依「官方 > OS」的位階，這裡照官方 24px。
       ★順便拆掉 overlay 自己那圈 border：
         OS 的 overlay（`not-available-text`）是 `absolute inset-0 … bg-background/80 p-4`，
         **沒有 border**，邊框只有外殼卡片那一圈。我方實測外殼 367.3 寬、overlay 365.3 寬
         ⇒ 兩圈 1px 邊框緊貼，畫面上是一條 2px 粗邊。拆掉 overlay 那圈才是 OS 的樣子。
         圓角一併歸零：外殼 `overflow:hidden` 會負責裁切。
       對手 §S-45 ① `.pos #pos-list .product.not-available .not-available-text` (1,3,0)；
       §S-95 (2) 已經用到 (1,5,0)（只設 background）。本節用 (1,6,0) 再高一分，
       確保日後有人在檔尾追加 (1,5,0) 也蓋不掉。
   ══════════════════════════════════════════════════════════════════════════ */
.pos #pos-list .product.not-available .not-available-text.pos-step-overlay.pos-step-overlay.pos-step-overlay {
    padding: 1.5rem;
    border: 0;
    border-radius: 0;
}

/* ══════════════════════════════════════════════════════════════════════════
   (6) STEP 卡圓角與陰影：升到 shadcn 官方 v4 的值（**刻意不照 OS**）
       · 圓角：官方 Card 是 `rounded-xl` = `--radius-xl` = calc(.625rem*1.4) = **14px**。
         §S-92 ② 寫死 12px 並註解「OS 的 rounded-xl=12px」—— 那個數字本身是對的
         （OS 是 Tailwind v3 且 config 只覆寫 lg/md/sm ⇒ xl 還是 v3 內建 .75rem），
         但依「官方 > OS」的位階，這裡用 token `var(--radius-xl)` 而不是寫死。
       · 陰影：官方 Card 是 `shadow-sm`，**v4 的** shadow-sm =
             0 1px 3px 0 rgb(0 0 0/.1), 0 1px 2px -1px rgb(0 0 0/.1)
         而我們現在用的 `0 1px 2px 0 rgb(0 0 0/.05)` 是 **v3 的 shadow-sm ＝ v4 的 shadow-xs**，
         比官方淡一階。★這正是本檔既有的 `.fb-tab.active` 已經在用的那一顆
         （實測 computed = `rgba(0,0,0,.1) 0 1px 3px 0, rgba(0,0,0,.1) 0 1px 2px -1px`），
         所以改過來同時讓卡片與頁籤回到同一套陰影階，不是新造一個值。
       ★★`!important` 不能拿掉：`.product.not-available{box-shadow:none !important}`（本檔
         第 4018 行）還在，這是 §S-96 打贏的那一仗。本節沿用 §S-96 的同一組選擇器
         `.pos .pos-step-card.pos-step-card` (0,3,0)+!important，只把「值」升級成官方版；
         §S-96 的勝利條件（比對手 (0,2,0)+!important 高一分）原封不動。
   ══════════════════════════════════════════════════════════════════════════ */
.pos .pos-step-card.pos-step-card {
    border-radius: var(--radius-xl);
    box-shadow: 0 1px 3px 0 rgb(0 0 0 / .1), 0 1px 2px -1px rgb(0 0 0 / .1) !important;
}

/* --- S-97a. S-97 (1) 的安全網：側欄頁首日後多長出子元素時不要掉到第二行（2026-08-10）---
   §S-97 (1) 把 `.pos-sidebar-header` 改成 grid + grid-template-areas。已 grep 六支頁面
   （package / machine / _package / machine_backup / checkout / darka）確認頁首**只有**
   `.back-btn` / `.title` / `.order` / `.pos-sidebar-toggle` 四種子元素，全部都有指定 area。
   但 grid 的預設 `grid-auto-flow: row` 會把「沒指定 area 的新子元素」丟進**隱式的第三列**
   ——頁首是固定 64px 高，第三列等於直接被切掉，而且**不會有任何錯誤訊息**。
   改成 `column` 之後，未知子元素會落在**右側的隱式欄**（看得見、排在收合鈕之後），
   壞掉時是「位置怪」而不是「消失」，比較容易被發現。
   ★這不改變現有四個子元素的位置：它們都有 explicit placement，auto-flow 對它們無效
     （實測四項座標與加這條之前逐項相同）。
   ★日後往頁首加東西，正解仍然是回 §S-97 (1) 的 `grid-template-areas` 補一格。
   -------------------------------------------------------------------------- */
.pos .pos-sidebar .pos-sidebar-header.pos-sidebar-header {
    grid-auto-flow: column;
}

/* --- S-98. 業主當面兩項：rail 底部兩顆鈕改左右並排、右側欄貼邊（2026-08-10）---------
 * 業主原話：①「黑白跟收合選單應該放左右」②「右邊的紀錄應該要貼邊」
 * 基準：賣賣 Work OS（`/Users/monet/OS/mywork_os`）。位階 shadcn 官方 > OS > 本專案現況。
 *
 * ══ 施工前實測（1600x950，`/pos/package`）══
 *   ① rail 展開態（210px）底部兩顆鈕**上下堆疊**且風格不一致：
 *        `.pos-rail-toggle`  x=30 y=839.6 w=164 h=36    flex-direction:row    font-weight 400（文字 600）
 *        `.pos-theme-toggle` x=30 y=881.6 w=164 h=56.4  flex-direction:column font-weight 500，圖示 20px
 *      `.pos-menu-footer` display:block、padding 5.25px 15px 12px、內容框 164px（= nav-link 的 x=30 w=164，對齊）
 *   ② `.pos-sidebar` rect l=1255 r=1585（viewport 1600）⇒ **右邊浮起留 15px**
 *        width 330px / margin 15px / border 四邊 1px / border-radius 10px / height 920px
 *        `.pos.pos-with-sidebar` padding-right 360px
 *
 * ══ 一、rail 底部兩顆鈕：展開態改左右並排 ══
 *   OS 原字面（`components/pos/PosMenu.vue:99-109`）：
 *     <div class="shrink-0 border-t border-border p-1.5">
 *       <button class="w-full h-9 rounded-lg text-muted-foreground hover:bg-accent
 *                      hover:text-foreground inline-flex items-center justify-center gap-1.5">
 *         <component :is="collapsed ? ChevronsRight : ChevronsLeft" :size="16" />
 *         <span v-if="!collapsed" class="text-xs font-semibold">收合選單</span>
 *
 *   ★左右順序：**收合選單在左、黑白在右**。理由三條，不是隨手擺——
 *     (a) 收合鈕的圖示是朝左的 `«`，指向 rail 要收去的那一側；放右邊會出現「圖示指左、
 *         鈕在右」的方向矛盾。
 *     (b) 它是這一列的主要動作（唯一帶完整詞組的），左側是中文閱讀起點。
 *     (c) 主題鈕是全站級偏好設定（非本頁動作），慣例放次要側。
 *
 *   ★兩顆統一成 **36px 高 / var(--radius) / .75rem / font-weight 600 / 圖示 18px**：
 *     · 36px 取自 OS 的 `h-9`，也是本專案**已成立的「工具鈕階」**——
 *       `.pos-rail-toggle`（§S-92 ⑩）、`.pos-sidebar-toggle` 與 `.pos-sidebar-restore`
 *       （§S-94，36x36）、側欄頁首 `.back-btn` 全是 36px。
 *     · ★這代表主題鈕從 §S-38 的 44px **降到 36px**，是我明知的取捨，不是漏看：
 *       §POS 檔頭（本檔「觸控目標一律 ≥ 40px」）那條下限管的是**作業目標**
 *       （nav item 56px、`.pos .btn` 40px、`.form-select` 40px 都還在），
 *       rail 底部的機殼控制項本專案已經兩度判給 36px 階（§S-92 ⑩、§S-94）。
 *       業主要求的是「兩顆同一組」，把 36 拉到 44 會讓它反過來比 OS 更胖、
 *       也跟隔壁側欄那兩顆 36px 對不上。★手機（≤991.98px）那顆 44x44 一個字都沒動。
 *     · font-weight 600 = OS 的 `text-xs font-semibold`；收合鈕文字本來就已經是 600
 *       （§S-97 (3)），本節只是把主題鈕從 500 補齊到同一階。
 *
 *   ★寬度分配不用「各半」，用 **收合鈕 flex:1 1 auto ／ 主題鈕 flex:0 0 auto**：
 *     footer 內容框只有 164px（footer 自己的 15px 左右內距**刻意不動**——動了兩顆鈕就
 *     不再跟上方 nav-link 的 x=30 對齊）。扣掉 6px gap 剩 158px。
 *     各半 = 79px，而「收合選單」的最小內容是 18(icon)+6(gap)+48(text)+12(padding)=84px
 *     ⇒ **各半會爆**。改成主題鈕吃自己的內容寬（18+6+24+12=60px）、收合鈕吃剩下的 98px，
 *     兩邊都有餘裕。★`width:auto` 一定要寫：兩顆的基準規則都是 `width:100%`，
 *     flex-basis:auto 會去讀 width ⇒ 不改就是兩顆各自 164px、擠成一團。
 *
 *   ★收合態（76px）**維持上下堆疊**（塞不下並排），本節只補兩件事：
 *     (a) `.pos-theme-toggle-text` 在收合態隱藏（§S-38 只在 `@media(max-width:991.98px)`
 *         裡藏過它，桌機收合態沒人管 ⇒ 實測「主題」二字還在，鈕高 56.4px）。
 *     (b) 主題鈕收合態同樣壓成 36px，跟它上面的收合鈕齊高。
 *
 *   ★收合鈕圖示方向：**交辦單說「兩態都是 «、方向沒跟著翻」，實測推翻——本來就是對的。**
 *     §S-92 ⑩ 已有 `@media(min-width:992px){ .pos .pos-menu.pos-rail--collapsed
 *     .pos-rail-toggle svg{transform:rotate(180deg)} }`，實測：
 *       展開態 `getComputedStyle(icon).transform` = `none`（`«`）
 *       收合態 = `matrix(-1, 0, 0, -1, 0, 0)`（= rotate(180deg)，畫面上是 `»`）
 *     ⇒ 本節**不重寫這條**，只確保新規則不把它蓋掉（下面的並排規則全部帶
 *     `:not(.pos-rail--collapsed)`，碰不到收合態）。
 *
 *   特異性帳：
 *     §S-38 `.pos .pos-menu .pos-menu-footer` / `.pos .pos-menu .pos-theme-toggle` (0,3,0)
 *     §S-38 `.pos .pos-menu .pos-theme-toggle > i|svg` (0,3,1)
 *     §S-92 ⑩ `.pos .pos-rail-toggle` (0,2,0)
 *     §S-97 (3) `.pos .pos-menu:not(.pos-rail--collapsed) > .pos-menu-footer` (0,4,0)（grid-area）
 *     §S-97 (3) `… .pos-rail-toggle .pos-rail-toggle-text` (0,5,0)
 *   → 本節並排用 `.pos .pos-menu:not(.pos-rail--collapsed) .pos-menu-footer` (0,4,0)、
 *     兩顆鈕用 (0,5,0)、圖示用 (0,5,1)，全部高一分且**不靠檔尾順序**。
 *     收合態補丁用 `.pos .pos-menu.pos-rail--collapsed .pos-theme-toggle` (0,4,0) > (0,3,0)。
 *
 * ══ 二、右側欄貼邊 ══
 *   OS 原字面（`views/pos/Package.vue:29`）：
 *     <aside class="pos-sidebar hidden lg:flex flex-col w-[340px] shrink-0
 *                   border-l border-border bg-card">
 *   ⇒ OS 的側欄是 flex row 的一格、**貼滿右緣與上下緣**，只有**左邊一條** border 分隔，
 *     沒有圓角、沒有外距。我方 §POS-4 沿用的是 Color Admin 原本的「浮起卡片」
 *     （margin .9375rem + 四邊 border + 圓角），這就是業主看到的那 15px 縫。
 *
 *   ★寬度取 **340px（OS 的 `w-[340px]`）** 而不是留著現況 330px：
 *     位階是「OS > 本專案現況」，且 330 這個數字是 `calc(22.5rem - 1.875rem)`
 *     ——它本來就只是「360 佔位扣掉 30 外距」的算式產物，外距一拿掉這個算式就沒有意義了。
 *   ★主區讓位必須跟著改成**同一個 340px**（現在是 360px = 330 + 兩側 15）：
 *       側欄實寬 340 + 左右外距 0 = 340 ⇒ `padding-right: 340px`。
 *     不改的話主區右邊會多留 20px 白邊（360 - 340），業主一樣會看到縫。
 *   ★`border-width: 0 0 0 1px` 不寫 `border: …`：§POS-4 的 `border:1px solid hsl(var(--border))`
 *     負責顏色與線型，這裡只改「哪幾邊有」（同 §S-94「三」⑥ 已結案的做法）。
 *   ★`height:100%` 蓋掉 §POS-4 的 `calc(100% - 1.875rem)`。它是 `position:fixed`
 *     且 top:0/bottom:0 都在，height 一給就以 height 為準 ⇒ 直接貼滿上下緣。
 *
 *   ★★不可碰的三條，全部用 `:not()` 排除而**不是**用特異性壓過去：
 *     §S-94 ④ `.pos .pos-sidebar.pos-sidebar--collapsed{width:0;margin:0;border-width:0;opacity:0}` (0,3,0)
 *     §S-94 ④ `.pos.pos-sidebar-collapsed.pos-with-sidebar{padding-right:0}` (0,3,0)
 *     §S-94 ④ `.pos.pos-sidebar-collapsed.pos-customer:has(> .pos-sidebar){padding-right:0}` (0,4,0)
 *     ——本節在檔案更後面，只要分數打平就會贏，**收合功能會直接壞掉**（側欄收起來了
 *       右邊卻還留 340px 空白、或收起來的側欄還掛著一條 1px 左框）。
 *       所以貼邊規則一律寫成 `:not(.pos-sidebar--collapsed)` / `:not(.pos-sidebar-collapsed)`：
 *       **選擇器根本不匹配收合態**，不管誰排後面都不會相撞。
 *
 *   ★整段關在 `@media (min-width: 992px)`：手機的 `.pos-sidebar` 是
 *     `position:fixed; inset:0` 的全螢幕抽屜（§POS-4，已經 margin 0 / radius 0 / border 0），
 *     一個字都不動。
 *   ★`.pos-sidebar-header` / `.pos-sidebar-footer` 已 grep 全檔確認**沒有任何
 *     border-radius 宣告**（CA 原值 `6px 6px 0 0` 只存在於註解裡，從沒落地），
 *     所以外殼圓角歸零不會在四角露出色差缺口。
 *   ★§S-94 ④ 的 `transition: width/margin-left/margin-right/opacity` 保持不動：
 *     margin 恆為 0 之後那兩條 margin transition 只是空轉，width + opacity 照舊會動畫。
 *
 * ══ 不可弄丟的既有成果（本節一個字都沒碰）══
 *     §S-96 `.pos .pos-step-card.pos-step-card{box-shadow: … !important}`
 *     手動輸入鈕 44px（業主裁決）
 *     §S-97 側欄頁首白底兩行 grid
 * ========================================================================== */

/* ── 一、rail 展開態：兩顆鈕左右並排 ─────────────────────────────────────── */
@media (min-width: 992px) {
    /* footer 由 block 改成一列 flex。★左右內距 15px 刻意保留：它讓兩顆鈕的左緣
       落在 x=30，與上方 nav-link 的 x=30 對齊（實測 nav-link x=30 w=164）。 */
    .pos .pos-menu:not(.pos-rail--collapsed) .pos-menu-footer {
        display: flex;
        flex-direction: row;
        align-items: center;
        gap: .375rem;                       /* = OS 的 gap-1.5 */
    }

    /* 兩顆鈕的共同外觀（同高 / 同圓角 / 同字級 / 同字重） */
    .pos .pos-menu:not(.pos-rail--collapsed) .pos-menu-footer .pos-rail-toggle,
    .pos .pos-menu:not(.pos-rail--collapsed) .pos-menu-footer .pos-theme-toggle {
        display: flex;
        flex-direction: row;                /* 主題鈕原本是 column */
        align-items: center;
        justify-content: center;
        gap: .375rem;
        height: 36px;                       /* = OS h-9，與 §S-92 ⑩ / §S-94 同一階 */
        min-height: 36px;
        width: auto;                        /* ★必寫：兩顆基準都是 width:100% */
        margin: 0;                          /* 打掉 §S-92 ⑩ 的 margin-bottom:.375rem */
        padding: 0 .375rem;
        font-size: .75rem;
        font-weight: 600;                   /* = OS text-xs font-semibold */
        line-height: 1.2;
        border-radius: var(--radius);
        white-space: nowrap;
        overflow: hidden;
    }
    /* 收合選單＝主要動作，吃掉剩餘寬（實測 98px；最小內容 84px） */
    .pos .pos-menu:not(.pos-rail--collapsed) .pos-menu-footer .pos-rail-toggle {
        flex: 1 1 auto;
        min-width: 0;
    }
    /* 主題＝次要偏好，只吃自己的內容寬（實測 60px） */
    .pos .pos-menu:not(.pos-rail--collapsed) .pos-menu-footer .pos-theme-toggle {
        flex: 0 0 auto;
    }
    /* 圖示統一 18px（跟齊 §S-92 ⑩ 的收合鈕；主題鈕原本 20px）。
       (0,5,1) > §S-38 的 (0,3,1)。★只在桌機收斂，手機那顆 20px 不動。 */
    .pos .pos-menu:not(.pos-rail--collapsed) .pos-menu-footer .pos-theme-toggle > i,
    .pos .pos-menu:not(.pos-rail--collapsed) .pos-menu-footer .pos-theme-toggle > svg {
        width: 18px;
        height: 18px;
        flex: 0 0 auto;
    }
}

/* ── 一之二、rail 收合態（76px）：維持上下堆疊，主題鈕補成 icon-only 36px ─── */
@media (min-width: 992px) {
    /* §S-38 只在 max-width:991.98px 藏過這個 span，桌機收合態沒人管
       ⇒ 實測「主題」二字還在、鈕高 56.4px。(0,4,0) > §S-38 的 (0,3,0)。 */
    .pos .pos-menu.pos-rail--collapsed .pos-theme-toggle .pos-theme-toggle-text {
        display: none;
    }
    .pos .pos-menu.pos-rail--collapsed .pos-theme-toggle {
        height: 36px;
        min-height: 36px;
        padding: 0;
        gap: 0;
    }
    .pos .pos-menu.pos-rail--collapsed .pos-theme-toggle > i,
    .pos .pos-menu.pos-rail--collapsed .pos-theme-toggle > svg {
        width: 18px;
        height: 18px;
    }
}

/* ── 二、右側欄貼邊（桌機）────────────────────────────────────────────────── */
@media (min-width: 992px) {
    /* 貼滿上下緣、拿掉外距與圓角。這三個屬性收合態也該是同一個值
       （收合中 width:0 + opacity:0，但過場不要再抖一次高度），所以不加 :not()。
       (0,3,0)；與 §S-94 ④ 的 margin-left/right:0 同值不衝突。 */
    .pos .pos-sidebar.pos-sidebar {
        height: 100%;
        margin: 0;
        border-radius: 0;
    }
    /* 寬度與「只留左邊一條線」★必須排除收合態：
       §S-94 ④ 用 `width:0; border-width:0` 收側欄，本節在它後面，分數打平就會贏。
       用 `:not()` 讓選擇器根本不匹配收合態，順序再怎麼變都撞不到。 */
    .pos .pos-sidebar:not(.pos-sidebar--collapsed) {
        width: 340px;                       /* = OS w-[340px]（原 330px） */
        border-width: 0 0 0 1px;            /* 顏色/線型仍吃 §POS-4 的 border 宣告 */
    }
    /* 主區讓位 = 側欄實寬，兩個數字綁在一起。
       (0,3,0) > §POS-1 的 (0,2,0)；`:not()` 排除 §S-94 ④ 的 padding-right:0。 */
    .pos:not(.pos-sidebar-collapsed).pos-with-sidebar {
        padding-right: 340px;
    }
    /* checkout / darka / _package 三支 `.pos-customer` 走的是 §S-94 ① 那條 (0,3,0)，
       本條 (0,4,0) 蓋過它；同樣用 `:not()` 讓開 §S-94 ④ 的 (0,4,0) 收合歸零。 */
    .pos:not(.pos-sidebar-collapsed).pos-customer:has(> .pos-sidebar) {
        padding-right: 340px;
    }
}


/* --- S-99. 買家發票結果頁：手刻定義列「卡尾懸空線」比照 shadcn TableBody 去線（2026-08-10）---
 *
 * ■ 病灶（實測，https://inv.2025-seller.mtest/result/{token}）
 *   `buyer/invoice/result.blade.php` 有 4 組（另 2 組隨資料條件出現，最多 6 組）手刻定義列，
 *   結構一律是 `.card-body > .row.gx-4.gy-0 > [class*="col-"].border-bottom`，
 *   分隔線掛在 `.col` 的 `border-bottom` 上。
 *   → **每一格都畫線，包含視覺上最後一列** ⇒ 那條線底下還有 16px（card-body 內距）
 *     才碰到卡框，實測「線 → 卡框」量到 17px（16px 內距 + 1px 卡框），變成一條懸空線。
 *
 * ■ 為什麼要治（shadcn 依據，2026-08-10 抓官方檔比對）
 *   官方 `table.tsx`：`TableBody = "[&_tr:last-child]:border-0"` —— 最後一列不畫線。
 *   本主題對**表格**早就做過同一件事（§5 的 `.card .table > tbody > tr:last-child > td`），
 *   ★但這幾組是手刻 `.col`，不是 `<table>`，§5 咬不到 ⇒ 全站只剩這裡是舊行為。
 *   本節補上「手刻定義列版本的 §5」。
 *
 * ■ ★為什麼不能用 `border-bottom-0`（Blade 層解不掉，這是本節存在的理由）
 *   `border-bottom-0` / `:last-child` 咬的是 **DOM 最後一格**，但「視覺最後一列」隨斷點變：
 *     ·「載具 / 稅務」四格 `col-12 col-md-6 col-lg-3`：
 *         ≥992 → 四格同一列（四格**都**是最後一列）
 *         768~991 → 兩列 ×2 格（最後一列是**兩格**）
 *         <768 → 四列 ×1 格（最後一列才是一格）
 *     · 同一張卡的第 5 格「備註」是 `@if` 條件輸出 ⇒ DOM 的最後一格連「是哪個 class」都會變。
 *   ⇒ 任何以 DOM 順序為準的規則，在某些寬度／某些資料下會少畫或多畫一整列的線。
 *   ⇒ **必須用幾何、不能用計數。**
 *
 * ■ 治法：把「去線」交給容器，用剪裁（幾何）而不是選擇器（計數）
 *   幾何前提（1440 / 992 / 768 / 390 四個寬度各實測一次，全部成立）：
 *     `.row` 的 `gy-0` 使列間無縱向 gutter，且 `.row > *` 預設 `align-items:stretch`
 *     ⇒ **視覺最後一列每一格的 border-box 底緣，恆等於 `.row` 自己的底緣**。
 *   ⇒ 只要把 `.row` 底部 1px 剪掉，被剪掉的**剛好只有**最後一列那幾條 1px 底線；
 *      列與列之間的線在 row 內部，一條都不受影響。斷點與格數完全不用知道。
 *   實測（四個寬度）：最後一列格數 1 / 2 / 4 都正確，中間分隔線全部保留。
 *
 * ■ ★為什麼把作用域限制成「卡片裡最後一個 card-body 的最後一個 row」
 *   `.card > .card-body:last-child > .row:last-child` —— 只有這個位置的線才是「懸空線」。
 *   反例（本頁實際存在、**刻意排除**）：「發票資訊」卡的 meta 四格，它下面同卡還接著
 *   「發票明細」小標與表格 ⇒ 它最後一列的線是**區段分隔線**，不是懸空線，拿掉會讓
 *   「發票明細」失去與上方 meta 的分界（該處 Blade 註解也明載「不加 border-top，
 *   因為上方四格自帶 border-bottom」—— 兩者是同一條線，不能拆）。
 *   實測：本規則在該頁命中 4 張卡（賣方／買方／載具 稅務／會員載具資訊），
 *   「發票資訊」卡正確落選。
 *
 * ■ 誤傷檢查（實測，非推論）
 *   · 作用域前綴 `.site-main` 只存在於 `buyer/layout.blade.php`（全 `resources/views` 只有
 *     買家端這一支殼層有）⇒ 後台、POS、登入／金流公開頁一律咬不到。
 *     ★沒有改用 `.fb-public`，因為那個 class 另外掛在 sso-demo／auth／payment 等 7 支公開頁上，
 *       作用域會外溢。
 *   · 已逐一列舉四組 row 的**所有後代**，確認被剪掉的那 1px 帶內除了最後一列 col 的底框
 *     之外沒有任何元素（內層 `.d-flex` 的底緣恰好停在 border 之上）⇒ 不會剪到任何內容。
 *   · 這幾組 row 內只有 `<span>` 與 `.badge`，沒有 dropdown／tooltip／absolute 定位元素，
 *     ★`clip-path` 會剪掉子孫溢出的特性在此無副作用（日後若要往這幾列塞浮層，
 *       請改用 §S-99 註解末段的備案）。
 *   · 全頁 box-shadow 清單注入前後比對：本節不改任何陰影，零變動。
 *
 * ■ 相容性
 *   `:has()` 不支援的舊瀏覽器會整條規則忽略 ⇒ 退回今天的樣子（多一條線），不會壞版。
 *   本檔 §S-92 ⑧ 已在用 `:has()`，門檻一致，不另開 `@supports`。
 *   ★不補 `-webkit-clip-path`：需要該前綴的 Safari（<13.1）比支援 `:has()` 的 Safari（15.4+）
 *     更舊，那些瀏覽器本來就走上面那條「整條忽略」的退路，補了也不會生效。
 *
 * ■ 備案（若日後這幾列要塞浮層，clip-path 不能用時）
 *   改成同選擇器加 `position:relative` 再用 `::after` 蓋一條 1px `hsl(var(--card))`：
 *   不剪裁、只覆蓋，色票跟著主題走（深色模式一樣對）。缺點是背景若非 `--card` 就會露餡。
 *
 * ■ 特異性
 *   本條 (0,8,0)（`:has()` 取其內最特異參數 `[class*="col-"].border-bottom` = (0,2,0)）。
 *   全檔沒有第二條規則設定這幾個 row 的 `clip-path` ⇒ 無競爭對手，不需要 !important。
 * ------------------------------------------------------------------------- */
.site-main .card > .card-body:last-child > .row:last-child:has(> [class*="col-"].border-bottom) {
    clip-path: inset(0 0 1px 0);
}

/* --- S-100. SweetAlert 對齊 shadcn AlertDialog ＋ 按鈕收 32px（2026-08-10）---------
 *
 * ■ 適用對象：**sweetalert（t4t5）v2.1.2**，前綴 `.swal-*`，`swal()` API。
 *   ★不是 sweetalert2（`.swal2-*` / `Swal.fire()`）。本檔的 `.swal2-*` 那段目前是**死碼**
 *   （2026-08-10 全專案 grep 無 `Swal.fire`），★**本輪刻意不刪**（要先跟該段持有者對齊），
 *   在此記錄以免後人以為兩套都活著。
 *
 * ■ 為什麼要獨立一節：`.swal-overlay` 是 JS 掛在 `<body>` 底下的，
 *   `.app-content` / `.modal` / `:where(.fb-public)` 三套按鈕階梯**一條都作用不到它**
 *   ⇒ 全站收 32px 時 swal 會是唯一漏網的作用域（實測改前 41px / 帶 className 40px）。
 *
 * ■ 層疊事實（實測，不要憑印象）：
 *   vendor 的 CSS 是 sweetalert.min.js 執行期**注入 <head> 最前面**，本主題排在它之後
 *   ⇒ **同特異性我們會贏**（證據：改前 `.swal-title` 量到 18px 而非原廠 27px，
 *   `.swal-modal` radius 量到 10px 而非原廠 5px，都是 §36 非 !important 規則打贏的）。
 *   ★但 vendor 有一批 (0,2,0) 的 `:first-child` / `:last-child` / `:not()` 規則
 *   （`.swal-title:first-child{margin-top:26px}`、`.swal-text:first-child{margin-top:45px}`、
 *    `.swal-text:last-child{margin-bottom:45px}`、`.swal-content:last-child{margin-bottom:20px}`…），
 *   單靠 (0,1,0) 歸零打不掉 ⇒ **本節的間距/尺寸類一律 `!important`**。
 *
 * ■ 對 §POS-8 的相容性（pos/machine 取消警告 modal 的純 CSS disable，零 JS race）：
 *   §POS-8 是 `.swal-modal:has(.pos-warning-cb…) .swal-button--confirm` = **(0,4,0) !important**，
 *   只管 background-color / color / cursor / pointer-events / box-shadow。
 *   本節動的是尺寸與版面（不碰那五個屬性），唯一動 background 的是下方 `.btn-danger` 修正
 *   = (0,2,0) < (0,4,0) ⇒ **§POS-8 仍勝出，行為完全不變**。
 *   （已查證：該 checkbox modal 的 confirm 鈕**不帶 className**，連 `.btn-danger` 都不會命中。）
 *
 * ■ 相對 shadcn 官方的刻意偏離（★不寫的話下一個做 shadcn 稽核的人會改回來）：
 *   1) 圖示 `.swal-icon` **整個 display:none** —— 業主 2026-08-10 裁決「完全移除，照官方」。
 *      官方 AlertDialog 本來就沒有圖示欄位；§36 原本降成 48px 的折衷方案就此作廢
 *      （★§36 那幾行**保留不刪**，只是被本節蓋掉，語意上已死）。
 *   2) 按鈕 **32px** = 官方 `size="sm"`（h-8）。官方 AlertDialogAction/Cancel 用的是
 *      default 36px（h-9）⇒ 這是「全站 32」的一致性優先於單一元件的官方值，屬刻意偏離。
 *   3) 官方 AlertDialogHeader 內 title↔description 是 `gap-2`(8px)、Content 才是 `gap-4`(16px)。
 *      swal 沒有 header 包裝層，title/text/footer 是同層的三個 grid item ⇒ 本節依規格採
 *      **統一 gap:1rem**，代價是 title↔text 比官方寬 8px。若日後要逐像素對齊，
 *      作法是 modal 改 `gap:.5rem` + `.swal-footer{margin-top:.5rem}`（★需業主裁決，本輪不自作主張）。
 *
 * ■ ★`display:inline-grid` 不是 `grid`（規格寫 grid，這裡刻意改）：
 *   vendor 靠 `.swal-overlay{text-align:center}` + `.swal-overlay:before{display:inline-block;
 *   vertical-align:middle;height:100%}` + `.swal-modal{display:inline-block;vertical-align:middle}`
 *   做**垂直置中**。改成 block 級的 `grid` 會讓 `vertical-align` 失效、彈窗黏到頂端。
 *   `inline-grid` 同時保住行內級（垂直置中）與 grid 版面。
 *
 * ■ `max-width:32rem`(512px) 只是補上官方的 `sm:max-w-lg` 上限；vendor 的 `width:478px`
 *   本來就在上限內 ⇒ 實務上不改變寬度，是一道防呆而非變更。
 * ----------------------------------------------------------------------------- */

/* 遮罩：官方 AlertDialogOverlay = bg-black/50（原廠 rgba(0,0,0,.4)） */
.swal-overlay {
  background-color: hsl(var(--dark-panel) / .5);
}

/* 容器：官方 AlertDialogContent = grid gap-4 rounded-lg border p-6 sm:max-w-lg */
.swal-modal {
  display: inline-grid !important;      /* ★不能用 grid，理由見章節註解 */
  gap: 1rem !important;
  padding: 1.5rem !important;           /* p-6 */
  text-align: left !important;          /* 原廠 center；官方對話框是左對齊 */
  max-width: 32rem !important;          /* sm:max-w-lg */
}

/* 圖示：業主裁決完全移除（官方 AlertDialog 沒有圖示） */
.swal-icon { display: none !important; }

/* 標題：官方 AlertDialogTitle = text-lg font-semibold */
.swal-title {
  padding: 0 !important;
  margin: 0 !important;
  text-align: left !important;
  font-size: 1.125rem !important;
  font-weight: 600 !important;
  line-height: 1.4 !important;
}

/* 內文：官方 AlertDialogDescription = text-sm text-muted-foreground
   `.swal-text` 走 `text:` 參數；`.swal-content` 走 `content:` 參數（§POS-8 的 checkbox modal 用這條） */
.swal-text,
.swal-content {
  padding: 0 !important;
  margin: 0 !important;
  text-align: left !important;
  font-size: .875rem !important;
  line-height: 1.45 !important;
  color: hsl(var(--muted-foreground)) !important;
  max-width: 100% !important;           /* 原廠 .swal-text 是 calc(100% - 20px)，會在 grid 內短一截 */
}

/* 頁尾：官方 AlertDialogFooter = flex sm:justify-end sm:gap-2、且**沒有分隔線** */
.swal-footer {
  display: flex !important;
  justify-content: flex-end !important;
  align-items: center !important;
  gap: .5rem !important;
  padding: 0 !important;
  margin: 0 !important;
  border-top: 0 !important;
}
.swal-button-container {
  margin: 0 !important;                 /* 原廠 5px；間距改由 footer 的 gap 統一負責（SSOT） */
  display: inline-flex !important;
}

/* 按鈕：官方 Button size="sm" = h-8 px-3 text-sm
   ★兩條選擇器都要：9 個呼叫點有 4 個傳 `className:'btn …'`（machine 1566/1600/1607、
     package 1249/1283/1290），只寫 `.swal-button` 會被 Bootstrap `.btn` 的 padding 撐到 40px。 */
.swal-button,
.swal-button.btn {
  height: 32px !important;
  min-height: 32px !important;
  padding: 0 .75rem !important;
  font-size: .875rem !important;
  line-height: 1 !important;
  font-weight: 500 !important;
  display: inline-flex !important;
  align-items: center !important;
  justify-content: center !important;
  white-space: nowrap !important;
  border-radius: var(--radius-md) !important;
}

/* 取消鈕：官方 AlertDialogCancel = buttonVariants({variant:"outline"}) */
.swal-button--cancel,
.swal-button--cancel.btn {
  background: hsl(var(--background)) !important;
  color: hsl(var(--foreground)) !important;
  border: 1px solid hsl(var(--border)) !important;
}
.swal-button--cancel:not([disabled]):hover,
.swal-button--cancel.btn:not([disabled]):hover {
  background: hsl(var(--accent)) !important;
  color: hsl(var(--accent-foreground)) !important;
}

/* ★既有 bug 修正（實測，非推測）：破壞性動作沒有紅色警示
 *   §36 的 `.swal-button{background:hsl(var(--dark)) !important}` (0,1,0)
 *   把呼叫端 `className:'btn btn-danger'` 打死 —— Bootstrap 的 `.btn` 只用
 *   `background-color: var(--bs-btn-bg)`（無 !important）⇒ 必輸。
 *   實測 `pos/machine.blade.php` 的「全部確認取消」是**黑底**(rgb(23,23,23)) 配一圈紅框，
 *   看起來跟旁邊的「不取消」同級 ⇒ 誤觸風險。
 *   採 (b) 補特異性覆蓋，**不採 (a) 拿掉 !important**：本檔硬性規定只准原子追加、
 *   禁止 Edit 既有行（同時有其他 session 在改本檔中段），(a) 物理上做不到；
 *   且 (a) 會讓 §36 的預設鈕改吃 Bootstrap 顏色，波及面遠大於本次議題。
 * ★另註：`.swal-button--danger`（§36 那條）**永遠不會命中** ——
 *   它要 `dangerMode:true` 才會生成，而全部 9 個呼叫點都是用 `className` 指定樣式。
 *   保留不刪，但視為死宣告。 */
.swal-button.btn-danger {
  background: hsl(var(--destructive)) !important;
  border-color: hsl(var(--destructive)) !important;
  color: hsl(var(--destructive-foreground)) !important;
}
.swal-button.btn-danger:not([disabled]):hover {
  background: hsl(var(--destructive) / .9) !important;
  border-color: hsl(var(--destructive) / .9) !important;
  color: hsl(var(--destructive-foreground)) !important;
}

/* --- S-101. 全站按鈕高度收斂成「只有 32px 一階」（2026-08-10）--------------------

   ═══ 業主指令鏈（原話，不是我的轉述）═══
   「遵守 shadcn 的設計 並且應該是要使用 **btn sm** 這個按鈕大小」
   「你的 /pos/machine **按鈕似乎都過大** 請你去後台管理看看參考 /order-management/status/printing」
   「**全站全部都要收到 btn-sm**」「請你**遵守全站**」「**ssot**」

   ★★★ 這是「相對於 shadcn 官方的刻意偏離」，不是漏做，下一個做 shadcn 稽核的人請先讀完再動 ★★★
   官方 `button.tsx` 是三階：`sm: h-8`(32) / `default: h-9`(36) / `lg: h-10`(40)，
   圖示鈕 `size="icon"` 是 `size-9`(36)。
   本節把 default 與 lg **一起降到 sm 的 32**、把 `.btn-icon` 四階（16/22/28/34）**一起收成 32**。
   我已用 AskUserQuestion 把「全站 32」與「遵守官方三階」的衝突攤給業主，
   業主的選擇是 **「確定全站 32，標明是刻意偏離」**。
   ⇒ **看到 36/40 不要「修回官方值」，那會推翻業主裁決。** 要改回三階必須business再問一次業主。

   ═══ 本節推翻／覆蓋的舊條文（一律用 §編號與檔名，不用行號）═══
   (1) `.agent/uiux.md` 的「四階規格」（2026-08-09 裁決）→ **08-10 作廢**。
   (2) ★同一份 `.agent/uiux.md` 的「已作廢條文索引表」裡，08-09 判死的「全站 btn-sm」
       → **08-10 復活**。這是**反轉**（舊條文重新生效），不是新增一條，
       索引表要標成「復活」否則下次讀的人會以為它還是死的。
   (3) POS 觸控階：§S-86 二、§POS-4／-5／-10、`.agent/shadcn-migration.md`、
       `2026-fatboy/.agent/shadcn-block-spec.md` 的 F 型。
       ★★**「死宣告」與「被本節推翻」是兩回事，不要混為一談**：
         §S-86 二(a) `.pos .btn{min-height:44px}` (0,4,0) 與 §POS-4 `.pos-sidebar-footer .btn{40}` (0,4,0)、
         §POS-10 `.pos .btn.btn-xs{26}` (0,3,0) —— 這三條**在本節之前就已經是死宣告**
         （`layouts/pos.blade.php` 的 `<div id="content" class="app-content p-0">` 讓整個 POS
          住在 `.app-content` 裡，被 (0,9,0) 的後台階梯壓過，實測 44 從來沒生效過、量到的是 36）。
         本節仍然把它們的值同步改成 32 —— **不是因為它們在作用，是為了防「復活」**：
         哪天有人拿掉 (0,9,0) 那條，44/40/26 會無聲地跳回來。
   (4) §S-94 的「標題列工具鈕 = 36px」（那條當時是為 B~F 頁預留的合約）→ 本節改 32。
   (5) §S-86 二(c)/(d) 的「掃碼 48px」→ 本節改 32，**風險見下方 (11)-(b)，業主知情後仍要求收斂**。

   ═══ 為什麼是「min-height + line-height + padding」三件一起改，不是只改 min-height ═══
   ★ 上游 (0,9,0) 那條的 36px **不是 `min-height` 撐出來的**，是
     `line-height:22 + padding-top 6 + padding-bottom 6 + border 2 = 36` **算出來的實體高度**；
     `min-height:36px` 只是墊底、從來沒有決定過高度。
   ⇒ **只把 `min-height:36→32` 完全不會生效**（實體高度仍是 36）。
     本節一律寫成 `line-height:20 + padding 5+5 + border 2 = 32`，三個數字自己對得起來。

   ═══ ★★ 為什麼本節「不」給每一階加顯式 `height`（這點與原始施工建議不同，我實測後推翻）═══
   原建議：「給所有階都加顯式 `height`，這樣在 flex 容器內不會被交叉軸 stretch 拉高」。
   ★我在 `/pos/package` 實測後**不採用**，證據：
     `.pos-sidebar-footer` 的三顆鈕（`後台管理`／`更新`／`暫停`）是
     **`display:block` 的「圖示在上、文字在下」兩行磚**（`<svg class="d-block …">` + 文字），
     實測高度 **62px**，是**內容撐出來的**，不是階梯撐的。
     對它們寫 `height:32px` 會把 32px 的內容硬塞進 32px 的盒子 → 文字直接溢出盒外。
   ★★**所以要先講明白，免得被當成漏做**：本節落地後那三顆是 **62 → 58px，不是 32**。
     少掉的 4px 來自 line-height 22→20 與 padding 6→5；剩下的高度是圖示 + 文字兩行本身。
     要讓它們真的變 32，得把「圖示在上文字在下」改成「圖示與文字並排」——
     **那是版面設計變更、要動 blade，不在本輪授權範圍**，已列入回報請業主裁決。
   ⇒ 本節只對「原本就已經是顯式 height」的兩處保留顯式 height
     （`.btn-icon` 家族、`.input-group.qty .btn`，它們本來就是固定方塊），
     其餘一律 `min-height`。**代價誠實寫在這裡**：
     只有 `min-height` 的階放進 `display:flex` 容器仍會被拉到同列最高者。
     ★所以**量測探針一定要放普通 `<div>`**，放 `d-flex` 量到的是假象
     （實測：普通 div 得 24/32/36/40 是真值；`d-flex` 得 40/32/40/40 是假值）。

   ═══ 作用域有四套，本節動三套 ═══
     ① `.app-content`（後台，含整個 POS）      → 本節 (1)(2)
     ② `.modal`（影子階梯，整組複製一份）      → 本節 (3)(4)
     ③ `:where(.fb-public)`（公開頁／登入／買家發票／金流）→ 本節 (6)(7)
     ④ `<body>` 直掛（SweetAlert `.swal-*`）   → **不在本節**，由 §S-100 處理
   ★ 漏一套就會出現「同一站四種按鈕高度」（本節施工前實測就是 41/38/36/32 並存）。
   ★ ④ 這一套**沒有通用階梯，而且不可以加**：實測後台頁面 `<body>` 直掛的元素裡有
     `<small class="btn btn-yellow btn-sm btn-rounded rounded-pill">`（側欄計數 chip，實測 16px）。
     在 body 層加 `.btn-sm{height:32px}` 會把那顆 chip 吹成 32px。⇒ 只准逐一指名，不准開全域。

   ═══ 明確排除、一個字都不碰的 ═══
   · **`span.btn.btn-xs`**（唯讀狀態膠囊）：它不是按鈕是標籤，**shadcn Badge 沒有尺寸階**，
     該走 `.badge`（另一條 session 正在做 112 處）。24px 恰好等於官方 `xs: h-6`。
     ⇒ (2)(4) 兩條**只收 `a` 與 `button`**，靠加一個 type selector 把特異度從 (0,4,0) 墊到 (0,4,1)。
     ★`:where(.fb-public)` 那條**刻意不加 span 例外**：已 grep 過公開頁 blade，
       `span.btn.btn-xs` 命中數 = 0（12 顆 span 全在 `pos/machine`＋`pos/package`＝ `.app-content` 內）。
       在公開頁排除反而會讓「萬一未來出現一顆」停在 40px（比 32 更糟），所以不排除。
   · **`.btn-close`**：每一階都已 `:not(.btn-close)`。它是 SVG mask 不是按鈕階。
   · **`.page-link`**：不是 `.btn` 家族（吃 `--bs-pagination-padding-*`），不拉進來。
   · **`.badge`**：另一條 session 的地盤（§24 剛改回藥丸）。
   · **`.form-control` / `.form-select`**：不是按鈕。★唯二例外是「同一個 `.input-group` 裡的輸入框」
     ——(11)-(b)(c)(d) 三處若只收鈕不收框，同一列會高低不齊，那不是收斂是製造新破口。
     **登入頁的 40px 輸入框本節不動**（鈕 32／框 40 會回到「一列兩種高度」，已列入回報請業主裁決）。
   · **`.h-45px`**（`pos/_header.blade.php` 的 rail 返回鈕）：桌機已被 §S-97 反打成 36，
     但 <992px 仍是 45px。**本節刻意不處理**，兩條路（再加 `!important` ／ 改 blade 拿掉 class）
     已列入回報請業主裁決。

   ═══ ★★★ 掃碼輸入框 48 → 32 的風險（一定要留在檔案裡）═══
   §S-86 二(c)(d) 把 `#search` / `#search-btn` 與 4 支 `pos-manual-input-modal` 訂成 48px，
   **那不是隨手挑的數字，是實證裁決**：那是**條碼槍的落點**，48px 是為了
   「作業員**戴著手套**、**隔一段距離看**」而訂（對應賣賣 Work OS `ScanPanel.vue` 的 48 那一階）。
   本節依業主「全站 32」把它收成 32。**業主是在我把這個風險講完之後仍然選擇全部收斂的。**
   ⇒ 未來若現場回報「刷不到／點不準」，這裡就是回溯點：把 (11)-(b)(c) 兩段的 32 改回 48 即可，
     不需要重新考古。

   ═══ ★ 規格 §2 的現況表有三處與實測不符（我量過才發現，記在這裡免得下次又照抄）═══
   (1) 規格說 `.pos … .input-group.qty .btn/.form-control` 是「活的 34」——
       實測 **36×34**：只有 `width:34px` 是活的，`height:34px` 早就被全站表單階梯 (0,9,1) 壓死。
   (2) 規格說登入頁那階是 `.login .btn-dark/.btn-primary` (0,2,0) 40 ——
       實測真正的勝出者在 **blade 的內嵌 `<style>`** 裡，(0,4,0)，見 (7b)。
   (3) 規格說 `.theme-panel .btn:not()×5` 是 (0,6,0)、`.pos .pos-sidebar #search-btn` 是 (1,3,0) ——
       逐一數過分別是 **(0,7,0)** 與 **(1,2,0)**。不影響施工（本節選擇器與上游逐字相同），
       但別拿規格表的分數去跟別的選擇器比高低。

   ═══ 施工前實測基準（1600×950，Chrome headless，探針放普通 div）═══
     作用域            btn-xs(a)  btn-xs(span)  btn-sm  btn(default)  btn-lg  btn-icon(default·xs·sm·lg)
     .app-content        24          24           32        36          40      28 · xs16 · sm32(★) · lg34
     .modal              24          24           32        36          40      28 · xs16 · sm32(★) · lg34
     :where(.fb-public)  40          40           32        40          40      28 · xs16 · sm32(★) · lg34
     <body> 直掛         24          24           31        38          40      28 · xs16 · sm22 · lg34
     ★ `btn-icon.btn-sm` 在 `.app-content`／`.modal`／`.fb-public` 量到 **32×22（長方形）**
       —— 那是既有缺陷不是本節造成的：`.app-content .btn-sm{height:32px}` 與
       `:where(.fb-public) .btn-sm{min-height:32px}` 兩條**都沒有 `:not(.btn-icon)` 守門**，
       把 22×22 的圓鈕拉成 32 高 22 寬。本節 (10) 把 `.btn-icon` 全部釘成 32×32 之後，
       這個缺陷會**順帶消失**（不是我特地去修的，記在這裡免得日後有人以為是新 bug）。
   ------------------------------------------------------------------------- */


/* ── (1) 後台預設階（(0,9,0) + (0,6,0)）36 → 32 ──
   選擇器與上游**逐字相同** ⇒ 特異度相同、位置在後 ⇒ 由來源順序取勝，不需要墊特異度。
   ★`.theme-panel` 一定要跟：那是「賣賣小幫手」側拉板，**不在 `.app-content` 內**，
     漏掉的話每一頁都會殘留兩顆 36px（實測 `/order-management/status/printing` 就有兩顆）。 */
.app-content :not(.input-group-sm):not(.input-group-lg) > .btn:not(.btn-sm):not(.btn-xs):not(.btn-lg):not(.btn-icon):not(.btn-close),
.theme-panel .btn:not(.btn-sm):not(.btn-xs):not(.btn-lg):not(.btn-icon):not(.btn-close) {
    min-height: 32px;
    line-height: 20px;      /* 5 + 20 + 5 + 2(border) = 32 */
    padding-top: 5px;
    padding-bottom: 5px;
    /* font-size 維持 14px（上游值）：業主要收的是「大小」＝高度，字級不在指令範圍內 */
}

/* ── (2) 後台 .btn-xs 24 → 32，★只收 `a` 與 `button`，`span` 排除 ──
   上游是 `.app-content .btn.btn-xs:not(.btn-icon)` (0,4,0)。
   這裡加 type selector ⇒ (0,4,1) 勝出，而 `span.btn.btn-xs` 匹配不到本條 ⇒ 原地維持 24px。
   ★為什麼不用 `:not(span)`：`:not(span)` 的括號內是 type selector 只貢獻 (0,0,1)，
     算出來一樣是 (0,4,1)、也一樣會贏，但**它會連 `<small>`／`<div>` 一起收**——
     實測側欄有 `<small class="btn btn-yellow btn-sm rounded-pill">` 這種把 `.btn` 當
     排版工具用的元素，白名單（列舉 a/button）比黑名單（排除 span）安全。 */
.app-content a.btn.btn-xs:not(.btn-icon),
.app-content button.btn.btn-xs:not(.btn-icon) {
    min-height: 32px;
    line-height: 20px;      /* 5 + 20 + 5 + 2 = 32；font-size 維持 §CA 補位的 12px */
    padding-top: 5px;
    padding-bottom: 5px;
}

/* ── (3) Modal 影子階梯 36 → 32（與 (1) 同一組數字）──
   `.modal` 那一整組是 (1) 的複製品，改一邊不改另一邊 = 開新破口。 */
.modal :not(.input-group-sm):not(.input-group-lg) > .btn:not(.btn-sm):not(.btn-xs):not(.btn-lg):not(.btn-icon):not(.btn-close) {
    min-height: 32px;
    line-height: 20px;
    padding-top: 5px;
    padding-bottom: 5px;
}

/* ── (4) Modal 的 .btn-xs 24 → 32（span 一樣排除，理由同 (2)）── */
.modal a.btn.btn-xs:not(.btn-icon),
.modal button.btn.btn-xs:not(.btn-icon) {
    min-height: 32px;
    line-height: 20px;
    padding-top: 5px;
    padding-bottom: 5px;
}

/* ── (5) .btn-lg 40 → 32 ──
   ★★★★★ 注意：本條**沒有 `.app-content` 前綴**（沿用上游 §S-39 的寫法，(0,3,0)）。
     ⇒ 它同時作用在 **POS、公開頁、買家發票頁、金流頁、`/login`** 上，不是只有後台。
     ★★ 有其他 session 正拿 **`/login` 當唯一可驗證頁面在量基準** —— 本條落地後
        他們量到的 `.btn-lg` 會從 40 變 32。**這是預期行為，不是他們改壞了。**
        （若要限縮成只影響後台，得改成 `.app-content .btn.btn-lg:not(.btn-icon)`，
          但那會違反業主的「ssot／遵守全站」，所以本節不限縮。）
   ★ `padding: 0 2rem`（左右內距）**刻意不動**：業主要收的是高度，
     `.btn-lg` 的左右氣勢是方案卡 CTA 的視覺重量，沒有指令就不改（YAGNI）。
   ★ 上游本來就是 `display:inline-flex` + 無垂直 padding ⇒ 高度 = min-height，改一個數字就夠。 */
.btn.btn-lg:not(.btn-icon) {
    min-height: 32px;
}

/* ── (6) 公開頁 `:where(.fb-public)` 40 → 32 ──
   選擇器與上游 §S-89 逐字相同（含 `:where()` 兩層），特異度仍是 **(0,1,0)**，靠位置在後取勝。
   ★`:where()` 一個字都不能拿掉：改成 `:not(.btn-icon)` 會讓本條變 (0,2,0)，
     反過來壓贏 `.btn.btn-icon`(0,2,0) 與金流頁的 `.btn`(0,1,0)，一次破壞兩個東西
     （§S-89 章節註解已經記過這個坑，這裡照抄它的守門）。
   ★`:where(.fb-public) .btn-sm{min-height:32px}` 上游已經是 32，本節不重複宣告。 */
:where(.fb-public) .btn:where(:not(.btn-icon):not(.btn-close)) {
    min-height: 32px;
    line-height: 20px;
    /* ★★ 這裡**故意不用 `padding` 簡寫**，只寫上下兩個 longhand —— 我第一版寫了
       `padding: 5px 16px`，實測把 `:where(.fb-public) .btn-sm{padding:5px 10px}` 的
       **左右 10px 一起蓋成 16px**（公開頁 `.btn-sm` 寬度 31 → 43，胖了 12px）。
       原因：上游 `.btn-sm` 那條與本條同為 (0,1,0)，本節排在後面 ⇒ 簡寫會連左右一起贏走。
       改成 longhand 之後，`.btn-sm` 的左右維持它自己的 10px，`.btn` 的左右維持上游的 16px，
       兩邊各拿各的，本節只動垂直方向。 */
    padding-top: 5px;       /* 5 + 20 + 5 + 2 = 32 */
    padding-bottom: 5px;
    /* font-size 維持 14px（上游值） */
}

/* ── (7) 登入頁送出鈕 40 → 32 ──
   ★誠實記錄：實測 `/login` 的送出鈕實際 class 是 `btn btn-yellow d-block w-100`，
     **既不是 `.btn-dark` 也不是 `.btn-primary`** ⇒ 它其實是被 (6) 收掉的，本條打不到它。
     本條仍然要寫，理由是 SSOT：上游 §5 留了這條 40px 的合約，
     不同步改的話「view 把 `.btn-yellow` 換回 `.btn-dark`」的那一天，登入鈕會無聲跳回 40。 */
.login .btn-dark,
.login .btn-primary {
    min-height: 32px;
}

/* ── (7b) ★★★ 登入送出鈕真正的勝出者：**不在本檔，在 blade 的內嵌 `<style>` 裡** ──
   ★★這條是我瀏覽器實測才發現的，**原始施工規格 §2 的表格漏了它**（規格只列到 (7) 那條 (0,2,0)）。
     沒有這條的話，`/login` 的送出鈕會**停在 40px**，整輪「全站 32」會在最顯眼的那一頁破功
     —— 而且探針量得到 32、真元素卻是 40，是最難察覺的那種假綠燈。
   實測勝出者：
     `resources/views/auth/_auth_shell_css.blade.php` 的
     `.login.login-v2 .login-content .btn-primary, .login.login-v2 .login-content .btn-yellow`
     `{ display:block; width:100%; height:40px; min-height:40px; padding:0 16px; … }`  **(0,4,0)**
   ★★**這裡不能只靠「排在後面」取勝**：那個 `<style>` 是 blade 直接吐在 `<head>` 裡的，
     **在 `shadcn-theme.css` 的 `<link>` 之後**（實測 `document.styleSheets` 順序：
     bootstrap.min.css → shadcn-theme.css → blade inline）。
     ⇒ 同分的話**它贏**。必須靠特異度硬贏，不能靠來源順序。
   ⇒ 本條在鏈上多插一個 `.btn`（元素本來就有）把 (0,4,0) 墊到 **(0,5,0)**，選擇器語意完全不變。
   ★`height` 與 `min-height` 兩個都要寫：對手是 `height:40px` **和** `min-height:40px` 一起釘的，
     只提 `min-height` 的話 `height:40px` 仍然把它壓在 40（「寫了 ≠ 生效」那一族的坑）。
   ★`.btn-yellow` 與 `.btn-primary` 兩個都要接：實測 `/login` 的送出鈕是 `.btn-yellow`
     （blade 的註解說明兩者在主題裡是同一個外觀，所以它自己也是兩個都接）。
   ★已全庫掃過「blade 內嵌 `<style>` 裡設定 `.btn`/`.fb-tab` 高度」的規則，
     **全站只有這一支**（`includes/_public_head.blade.php` 那兩筆是註解裡的存查字串，不是活規則）。
     ⇒ 這是唯一需要墊特異度打 blade 的地方，其餘 (1)~(22) 都是打本檔自己的上游。 */
.login.login-v2 .login-content .btn.btn-primary,
.login.login-v2 .login-content .btn.btn-yellow {
    height: 32px;
    min-height: 32px;
}

/* ── (8) 一般頁籤 `.fb-tab` 29 → 32（★業主要「全部 32」⇒ 這一階是**變大 3px**）──
   §13 的原始幾何是「軌道 `.fb-tabs{height:2.25rem}`（36）＋ 片 `.fb-tab{height:calc(100% - 1px)}`」，
   ⇒ 片高被軌道**反推**出來（實測 29px）。要讓片是 32，只能把軌道解成 auto、讓片自己決定高度。
   ★軌道改 auto 之後總高 **實測 36 → 38px**（不是我原本推算的 42 —— 推算時把 §S-52 的
     padding 與 §S-49 的髮絲邊重複計了一次，這裡以量到的值為準）。
     POS 觸控軌道則是 **53 → 38px**。軌道長高 2px 是「片 32」必然的代價，不是漏算；
     若日後業主嫌軌道變高，正解是把 §S-52 的 `padding:4px` 調小，**不是**把片改回 29。
   ★特異度：兩條都與上游逐字同分（`.fb-tabs`(0,1,0)、`.fb-tab`(0,1,0)），靠位置在後取勝。
     `.fb-tabs:not(.fb-tabs--flow) > .fb-tab`(0,3,0)（§S-52）與
     `.fb-tabs > .fb-tab`(0,2,0)（§S-50）**都沒有宣告 height** ⇒ 不會反壓本條。
   ★箭頭步驟條 `.fb-tabs--flow` 也吃這兩條（它同時帶 `.fb-tabs`）⇒ 一起變 32，
     這符合「全站 32」，且 §S-50 的 chevron `clip-path` 是百分比多邊形，改高度不會破形。 */
.fb-tabs {
    height: auto;
}
.fb-tab {
    height: 32px;
}

/* ── (9) POS 觸控頁籤 `.fb-tabs--touch > .fb-tab` 47 → 32（只影響 machine／package 兩頁）──
   上游 §S-33 是 `min-height:44px; padding:12px 15px; height:auto`（(0,3,0)），
   `height:auto`(0,3,0) 會壓過 (8) 的 `.fb-tab{height:32px}`(0,1,0) ⇒ **必須在這裡再打一次**。
   垂直 padding 一併歸零，否則 12+12 又會把它撐回 44 以上。 */
.fb-tabs.fb-tabs--touch > .fb-tab {
    height: 32px;
    min-height: 32px;
    padding-top: 0;
    padding-bottom: 0;
}

/* ── (10) 圖示鈕 `.btn-icon` 四階（28 / xs 16 / sm 22 / lg 34）→ 全部 32×32 ──
   ★★ **官方 `size="icon"` 是 36（`size-9`），收成 32 同樣是刻意偏離**（業主裁決 2）。
   ★ 這是本節**唯一**保留顯式 `height` 的地方（另一處是 (11)-(e)）：
     圖示鈕本來就是固定方塊、內容只有一顆 icon，不會有兩行文字撐破盒子的風險。
   ★ `border-radius` 跟著改成 32px：上游用 `border-radius:28px` 做圓形
     （`.btn-circle` 在 CA 的 app.min.css 裡 grep 0 筆，圓形一直都是 `.btn-icon` 給的），
     不同步改的話 32px 的方塊配 28px 圓角會變成「圓角矩形」而不是圓。
   ★ 字級統一 14px：上游四階是 8/11/14/17px，盒子既然都變成 32 了，
     8px 的圖示放在 32px 的圈裡會小得像雜訊。14px = 上游 default 階原值。
   ★ 特異度：`.btn.btn-icon`(0,2,0)／`.btn.btn-icon.btn-xs`(0,3,0) 皆與上游同分，位置在後取勝。
   ★ 不碰 §S-97 的 `.pos .pos-menu .pos-rail-back .btn{width/height:36px !important}`
     —— 那顆是 `.btn.btn-white.h-45px`（**沒有 `.btn-icon`**），不在本條射程內，
     它的去留屬於 `.h-45px` 那題，已列入回報請業主裁決。 */
.btn.btn-icon {
    width: 32px;
    height: 32px;
    min-height: 32px;
    line-height: 32px;
    font-size: 14px;
    border-radius: 32px;
    padding: 0;
}
.btn.btn-icon.btn-xs,
.btn.btn-icon.btn-sm,
.btn.btn-icon.btn-lg {
    width: 32px;
    height: 32px;
    min-height: 32px;
    line-height: 32px;
    font-size: 14px;
}

/* ══ (11) POS：把「還活著」的觸控階全部收成 32 ══════════════════════════════
   ★先講清楚哪些是幻影，免得下一個人跑去對付不存在的東西：
     `layouts/pos.blade.php` 的 `<div id="content" class="app-content p-0">`
     讓**整個 POS 住在 `.app-content` 裡** ⇒ 下列三條早就被 (0,9,0) 壓死，
     實測值一直是 36 / 24 / 36，不是它們宣告的 44 / 26 / 40：
        §S-86 二(a) `.pos .btn:not(.btn-close):not(.btn-xs){min-height:44px}` (0,4,0)
        §POS-10     `.pos .btn.btn-xs{min-height:26px}`                       (0,3,0)
        §POS-4      `.pos .pos-sidebar .pos-sidebar-footer .btn{min-height:40px}` (0,4,0)
     ⇒ (a) 只是把死宣告的值同步成 32「防復活」，**不要當成它們原本在作用**。 */

/* (a) 死宣告同步（防復活，非修 bug）。§POS-10 的 `.pos .btn.btn-xs` 不在此列：
       它與 (2) 的 `.app-content a/button.btn.btn-xs`(0,4,1) 相比是 (0,3,0)，
       永遠贏不了，而且它的 span 例外正是我們要保住的 —— 動它反而會把膠囊一起吹大。 */
.pos .btn:not(.btn-close):not(.btn-xs) {
    min-height: 32px;
}
.pos .pos-sidebar .pos-sidebar-footer .btn {
    min-height: 32px;
}

/* (b) STEP 卡「沒有掃描槍時唯一的手動入口」44 → 32（§S-86 二(b)，(0,2,1)，**是活的**）。
       `.pos-btn-overlay` 的點擊範圍靠這條撐，收到 32 等於觸控目標縮小 27%。
       ★上游的 `display:inline-flex` / `gap` / 左右 padding 一律不動，只改高度。 */
.pos .not-available-text a {
    min-height: 32px;
}

/* (c) ★★★ 條碼槍落點 48 → 32（§S-86 二(c)，(1,3,0)，**是活的**）。
       風險已寫在本節開頭「掃碼輸入框 48 → 32 的風險」那段，此處不重複，只留回溯座標。
       ★`#search` 是 textarea、`#search-btn` 是送出鈕，兩顆同在一個 `.input-group`，
         **必須一起改**，只改鈕會讓輸入列高低不齊（那不是收斂是製造新破口）。
       ★`#search` / `#search-btn` 是 JS 直接讀的識別碼（`$('#search-btn').click`、`$('#search').val()`），
         本條只加樣式、沒有改名。 */
.pos .pos-sidebar #search,
.pos .pos-sidebar #search-btn {
    min-height: 32px;
    /* ★★ 只把 `min-height:48→32` 改掉**量出來是 38 不是 32**（實測）——
       這是本節開頭「坑 1」的**輸入框版本**，而且比按鈕版更隱蔽：
       `#search` 是 textarea，`height:auto`，實體高度 = `padding 6+6 + line-height 24 + border 2 = 38`。
       `min-height:32` 只是墊底，38 > 32 ⇒ 完全不生效。
       更糟的是 `#search-btn` 本身明明已經被算成 32，卻因為兩顆同在
       `.input-group{display:flex; align-items:stretch}` 裡而**被拉到 38**
       ⇒ 只看按鈕的 computed 值會以為成功了（假綠燈）。
       ⇒ 這裡照本節統一的算式把 textarea 自己收成 32，按鈕才會跟著回到 32。
       ★`height` 不寫死、維持 auto：`rows` 改成 2 以上時仍要能長高。 */
    line-height: 20px;      /* 5 + 20 + 5 + 2 = 32 */
    padding-top: 5px;
    padding-bottom: 5px;
}

/* (d) 4 支手動輸入 modal 48 → 32（§S-86 二(d)，(0,9,0)，**是活的**）。
       同 (c)：輸入框 / addon / 送出鈕三者同列，一起改。
       ★`height:auto` 必須保留：上游 `.form-control` 是 `height:36px` **和** `min-height:36px`
         一起釘的，只提 `min-height` 的話 `height` 仍會把它壓在 36px（「寫了 ≠ 生效」那一族的坑）。 */
.pos-manual-input-modal :not(.input-group-sm):not(.input-group-lg) > .form-control:not(textarea):not(.form-control-sm):not(.form-control-lg):not(.form-control-color):not([type="file"]):not([type="color"]),
.pos-manual-input-modal :not(.input-group-sm):not(.input-group-lg) > .input-group-text,
.pos-manual-input-modal :not(.input-group-sm):not(.input-group-lg) > .btn:not(.btn-sm):not(.btn-xs):not(.btn-lg):not(.btn-icon):not(.btn-close) {
    min-height: 32px;
    height: auto;
    /* ★★ 同 (c)：只改 `min-height:48→32` 實測是 **35**（輸入框與 addon），不是 32。
       算式：`padding 6+6 + line-height 21 + border 2 = 35`。
       ★而送出鈕已經是 32 ⇒ 同一個 `.input-group` 裡 32 配 35，**只差 3px 最容易被眼睛放過**，
         但那正是「一列兩種高度」。三者一起用本節統一算式收成 32。
       ★`height:auto` 必須留（上游那條的原始理由仍然成立）：上游 `.app-content .form-control`
         是 `height:36px` **和** `min-height:36px` 一起釘的，不寫 `auto` 的話 36 會壓住它。 */
    line-height: 20px;      /* 5 + 20 + 5 + 2 = 32 */
    padding-top: 5px;
    padding-bottom: 5px;
}

/* (e) 側欄數量加減鈕 34 → 32（§POS-5，(0,8,0)，**是活的**）。
       ★這裡連 `.form-control` 一起收，是**判斷不是疏忽**：那三顆（− / 數字 / ＋）是同一個
         `.input-group.qty`，鈕 32 配框 34 會在每一列商品上留下 2px 的錯位。
         上游 §POS-5 本來就是把兩者當一組寫的（同一節、同樣的 34），這裡照它的分組改。
       ★這是本節第二處（也是最後一處）保留顯式 `height` 的地方：它本來就是固定方塊。 */
.pos .pos-sidebar .pos-sidebar-body .pos-table .pos-table-row > div .input-group.qty .btn {
    width: 32px;
    height: 32px;
    min-height: 32px;
}
/* ★★★ 這條**必須**多帶一個 `.app-content` 前綴，不能照抄上游 §POS-5 的鏈。
   我第一版照抄上游寫成 `.pos … .input-group.qty .form-control`＝**(0,8,1)**，
   實測輸入框**紋風不動停在 36px**（鈕 32、框 36，反而比施工前的「36/36 對齊」更醜）。
   真正的勝出者是全站表單階梯
     `.app-content :not(.input-group-sm):not(.input-group-lg) > .form-control:not()×6 { height:36px }`
   ＝**(0,9,1)**，它比 (0,8,1) 高一級。
   ★★連帶查證出一件事：**上游 §POS-5 自己那條 `height:34px` 從來也沒生效過**
     —— 施工前實測是 **36×34**（高 36 來自全站階梯，只有寬 34 是 §POS-5 給的）。
     規格表把 `.input-group.qty` 整條列為「活的 34」，**只有 `width` 那一半是對的**。
   ⇒ 補一個 `.app-content` 把本條墊到 (0,9,1)、與對手同分，再靠位置在後取勝。 */
.app-content .pos .pos-sidebar .pos-sidebar-body .pos-table .pos-table-row > div .input-group.qty .form-control {
    height: 32px;
    min-height: 32px;
}

/* (f) POS 主區標題列工具鈕 36 → 32（§S-94 ②，(0,5,0)，**是活的**）。
       上游那條的 (0,5,0) 原本是為了硬贏 §S-86 二(a) 的 44；現在兩邊都是 32，
       特異度階梯仍然保留（不動選擇器），只換值。 */
.pos .pos-page-header .btn:not(.btn-close):not(.btn-xs) {
    min-height: 32px;
}

/* --- S-101a. §S-101 的漏洞補丁：公開頁的唯讀狀態膠囊被吹大到 32（2026-08-10）----------

   ═══ 這是 §S-101 的 bug，不是新需求 ═══
   §S-101 在 `.app-content`（該節 (2)）與 `.modal`（該節 (4)）都正確地
   **只收 `a` 與 `button`**、把 `span.btn.btn-xs` 排除在外（用 (0,4,1) 的白名單），
   **唯獨公開頁那條 (6) `:where(.fb-public) .btn:where(:not(.btn-icon):not(.btn-close))`
   是打「所有 `.btn`」、沒有任何 span 排除條款** ⇒ 公開頁的唯讀狀態膠囊一起被拉到 32px。
   覆核時在 `/login`（`body.fb-public`）用探針實測到 `span.btn.btn-xs = 32`，應為 24。

   ★★ §S-101 的章節註解裡有一段話**現在是錯的，本節推翻它**：
      原文說「`:where(.fb-public)` 那條**刻意不加 span 例外**……已 grep 過公開頁 blade，
      `span.btn.btn-xs` 命中數 = 0 ⇒ 在公開頁排除反而會讓萬一未來出現一顆停在 40px」。
      **那個推論站不住腳**：
        ① 「現在 grep 是 0」不等於「以後也是 0」，而規格把 `span.btn.btn-xs` 列為
           **不可收斂的例外**（它不是按鈕是標籤，shadcn Badge 沒有尺寸階，該走 `.badge`）
           —— 例外就該用**規則**保證，不是用「目前剛好沒有」保證。
        ② 原文擔心的「停在 40px」在 §S-101 落地後已經不存在：公開頁的 `.btn` 基準
           本來就從 40 降到 32 了，所以現在的選擇是「24（正確）vs 32（錯誤）」，
           不是原文假設的「32 vs 40」。**前提變了，結論就跟著失效。**
      ⇒ 讀 §S-101 (6) 的註解時，請以本節為準。

   ═══ 值是量出來的，不是憑記憶填的 ═══
   量測對象：`.app-content` 內合成 `<span class="btn btn-xs btn-primary">` 的 computed style
   （1600×950，落地後的真實檔案，探針放普通 `<div>`）：
       min-height 24px ／ line-height 18px ／ padding 2px 6px ／ font-size 12px ／ border 1px
       ⇒ 2 + 18 + 2 + 2 = **24px**，與官方 Badge 的 `xs: h-6`(24) 一致。
   對照同一支探針裡的 `<a class="btn btn-xs">`：line-height 20 / padding 5 ⇒ 32px（＝§S-101 收斂後的值）。

   ═══ 只宣告「垂直方向」四個值，其餘刻意不重複宣告（SSOT）═══
   `font-size:12px` 與左右 `padding:6px` 來自全域的 `.btn.btn-xs`(0,2,0)（§CA 補位），
   在公開頁本來就已經生效（實測 12px / 6px），**這裡不重抄**，
   否則同一組數字會有兩個權威來源，改一邊忘另一邊。
   ★實測補充：`.btn.btn-xs{padding:2px 6px}` 是 (0,2,0)，本來就壓得過 §S-101 (6) 的
     `padding-top/bottom:5px`(0,1,0) ⇒ 公開頁那顆 span 的 padding **一直都是 2px**，
     真正把它撐到 32 的只有 `min-height:32px` 與 `line-height:20px` 兩行。
     本節仍然把 padding 兩行寫齊，是為了讓「24 = 2+18+2+2」這個算式在本節內自己讀得懂
     （與 §S-101 其他各節同一風格），值與現況相同、不改變任何行為。

   ═══ 特異性：(0,2,1)，剛好夠，不需要 `!important` ═══
       對手 §S-101 (6) `:where(.fb-public) .btn:where(:not(.btn-icon):not(.btn-close))`
            → `:where()` 貢獻 0 ⇒ **(0,1,0)**
       本節  `:where(.fb-public) span.btn.btn-xs`
            → `:where()` 0 ＋ `span`(type) ＋ `.btn` ＋ `.btn-xs` ⇒ **(0,2,1)** → 勝
   ★**刻意不寫 `:not(.btn-icon)`**（與 §S-101 (2)(4) 的寫法不同，這是有意的）：
     加了會變 (0,3,1)，反過來壓贏 §S-101 (10) 的 `.btn.btn-icon.btn-xs`(0,3,0)，
     把圖示鈕的 min-height 拉回 24。維持 (0,2,1) 則 (10) 仍然管得住圖示鈕
     （實測 `span.btn.btn-xs.btn-icon` 仍是 32×32）。
   ★**不會誤傷 `.app-content` 內的 span**：兩個作用域在本專案是互斥的
     （後台頁 `body.class` 實測為 `pace-done pace-top loaded`，**沒有** `fb-public`；
      公開頁才有 `fb-public`）。退一步說，就算哪天同一頁兩者並存，
      `.app-content .btn.btn-xs:not(.btn-icon)`(0,4,0) 也贏過本節 (0,2,1)，
      而且兩邊宣告的值**完全相同（24）**，不會有分歧。
   ------------------------------------------------------------------------- */
:where(.fb-public) span.btn.btn-xs {
    min-height: 24px;
    line-height: 18px;      /* 2 + 18 + 2 + 2(border) = 24 */
    padding-top: 2px;
    padding-bottom: 2px;
}

/* --- S-102. 買家發票結果頁「發票明細」表格逐條對齊 shadcn 官方 Table（2026-08-10）------
   業主：「一切都以 shadcn 為主 包括表格資料」。
   ─────────────────────────────────────────────────────────────────────────────
   ★★★ 權威來源（本節每一個數字都抓自這三支原始碼，沒有一個是憑印象填的）：
     ① shadcn Table（現行 v4 registry，main 分支）
        https://raw.githubusercontent.com/shadcn-ui/ui/main/apps/v4/registry/new-york-v4/ui/table.tsx
     ② shadcn Table（官網仍在服務的舊 registry，用來確認「哪些值兩版都一樣」）
        https://ui.shadcn.com/r/styles/new-york/table.json
     ③ Tailwind token 定義（h-10 / p-2 / text-sm / transition-colors 的實際數值）
        https://raw.githubusercontent.com/tailwindlabs/tailwindcss/main/packages/tailwindcss/theme.css
        https://raw.githubusercontent.com/tailwindlabs/tailwindcss/main/packages/tailwindcss/src/utilities.ts
     ★①②只有四處不同（見下方 (3) 與「兩版差異」段），其餘逐字相同 ⇒ 本節採用的值
       絕大多數是「兩版都同意」的，不受版本爭議影響。
     ★這是刻意的作法：同一條線上已經出過兩次「憑記憶引用 shadcn」的事故
       （button 的 `xs` size 被誤刪 146 處、badge 被誤判成 `rounded-md` 改錯 112 處），
       第二次還拿本檔自己的註解當佐證 —— 但那行註解本來就是照同一個錯誤認知寫的，
       是同源回音不是獨立來源。⇒ 本節一律附 URL ＋ 逐字 className。

   ★★ 作用域鎖 `.container-wide`：全專案只有 `buyer/invoice/result.blade.php:123`
      在用它（`grep -rn "container-wide" resources/views/` 只有買家 layout 的定義
      與該行一處使用），且再加一層 `.card` ⇒ 特異度足夠、blast radius 就是這一頁。
      **後台 127 支 blade、POS、登入頁、金流頁一條都吃不到。**
      ⇒「要不要全站表格一起改成官方值」是另一個決定，見文末「待業主拍板」。

   ★★ 為什麼是「補一節」而不是改既有節：買家端沒有 `.app-content` 外殼，
      表格現值由三處合力決定 —— 本檔無前綴那節的
      `.table > thead > tr > th{padding:.6rem .75rem}`(0,1,3) 與
      `.table:not(.table-bordered) > tbody > tr > td{padding:.6rem .75rem}`(0,2,3)、
      Bootstrap `.table > :not(caption) > * > *{padding:.5rem}`(0,1,1)、
      以及 `buyer/layout.blade.php` 的 inline `<style>`。
      動前兩者會連帶改到後台/POS，動第三者要改 blade（本輪不動 layout）
      ⇒ 只在最外層補一層更高特異度的規則，既有規則一行都不用碰。

   ─────────────────────────────────────────────────────────────────────────────
   ★ 官方 className 逐字（table.tsx，v4）：
       Table        `w-full caption-bottom text-sm`（外層 div：`relative w-full overflow-x-auto`）
       TableHeader  `[&_tr]:border-b`
       TableBody    `[&_tr:last-child]:border-0`
       TableFooter  `border-t bg-muted/50 font-medium [&>tr]:last:border-b-0`
       TableRow     `border-b transition-colors hover:bg-muted/50 has-aria-expanded:bg-muted/50 data-[state=selected]:bg-muted`
       TableHead    `h-10 px-2 text-left align-middle font-medium whitespace-nowrap text-foreground [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]`
       TableCell    `p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]`
       TableCaption `mt-4 text-sm text-muted-foreground`
   ★ token 換算（tailwindcss/theme.css 逐字）：
       `--spacing: 0.25rem;`            ⇒ h-10 = .25rem*10 = 2.5rem = 40px；p-2/px-2 = .5rem = 8px
       `--text-sm: 0.875rem;`           ⇒ 14px
       `--text-sm--line-height: calc(1.25 / 0.875);`  ⇒ 1.42857… ⇒ 14px 字對應 20px 行高
       `--default-transition-duration: 150ms;`
       `--default-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);`
   ★ transition-colors 展開（utilities.ts L4801-4808 逐字）：
       transition-property: color, background-color, border-color, outline-color,
                            text-decoration-color, fill, stroke, --tw-gradient-from,
                            --tw-gradient-via, --tw-gradient-to
   ★ 兩版差異（① v4 vs ② 官網舊 registry），只有這四處：
       a. TableHead 字色    ①`text-foreground`      ②`text-muted-foreground`   ← 唯一需要拍板的
       b. TableCell 換行    ①`whitespace-nowrap`    ②（無）                     ← 本專案現況＝①
       c. 外層 div          ①`overflow-x-auto`      ②`overflow-auto`            ← 等價
       d. TableRow          ①多一個 `has-aria-expanded:bg-muted/50`             ← 本頁無此狀態
   ★ 官方 demo（examples/table-demo.tsx，同 repo main）證實：金額欄用 `className="text-right"`、
     欄寬用 `className="w-[100px]"`、TableFooter 用 colSpan ＋ text-right ——
     ⇒ 本頁 blade 的 `.text-end` / `.col-*` 欄寬 / tfoot colspan **與官方 demo 同一種寫法**，
       不是偏離，不要當缺陷改掉。
   ────────────────────────────────────────────────────────────────────────── */

/* ── (1) 外層容器 = `relative w-full overflow-x-auto`（Table 的 wrapper div）──
   `.table-responsive` 已給 overflow-x:auto ＋ 滿寬；官方多一個 `relative`，補上。
   ★實測 overflow-y 的 computed 是 auto（不是 visible）—— 那是規範強制的：
     一軸非 visible 時另一軸的 visible 會被計算成 auto。官方那個 div 也一樣，**等價不是偏離**。 */
.container-wide .card .table-responsive { position: relative; }

/* ── (2) `<table>` = `w-full caption-bottom text-sm` ──
   w-full / caption-bottom 已由 Bootstrap 給（實測 width=100%、caption-side:bottom）。
   這裡只補 text-sm 的**完整定義**：字級 14px 買家 layout 已用
   `.container-wide .card{font-size:14px}` 靠繼承給到，但**行高沒有跟著給** ——
   實測 line-height 21px（body 的 1.5），官方 text-sm 是 20px。
   ⇒ 直接照 Tailwind 的 text-sm 原式寫，字級與行高綁在一起，不再靠繼承。
   ★選擇器 (0,3,1)：`.container-wide`(1) + `.card`(1) + `.table`(1) + `table`(0,0,1)。 */
.container-wide .card table.table {
    font-size: .875rem;                  /* --text-sm */
    line-height: calc(1.25 / 0.875);     /* --text-sm--line-height ⇒ 20px */
}

/* ── (3) `<th>` = TableHead `h-10 px-2 text-left align-middle font-medium whitespace-nowrap text-foreground` ──
   ★競爭對手逐條算過（全部非 !important，本條 (0,3,3) 通吃）：
     · Bootstrap `.table > :not(caption) > * > *{padding:.5rem}`(0,1,1)
     · 本檔無前綴節 `.table > thead > tr > th{padding:.6rem .75rem;font-size:14px;letter-spacing:.01em}`(0,1,3)
     · `buyer/layout.blade.php` inline `.table > thead th{color:--muted-foreground}`(0,1,2)
   ★**背景刻意不宣告**：官方 TableHead 沒有背景 class（靠 border-b 分隔，不是灰塊）。
     本檔無前綴節那條 `background: transparent !important`(0,1,3) 已經做到透明底
     （實測 rgba(0,0,0,0)，透出卡片的 --card 白）—— 要的結果已經對了，
     再宣告一次只會多一個要維護的競爭者。★★也**不要**去拿掉那個 !important：
     本檔基底 `.table > thead th{background:--muted !important}`(0,1,2) 還在，
     拿掉就會掉回灰底表頭。 */
.container-wide .card .table > thead > tr > th {
    height: 2.5rem;                      /* h-10 = --spacing*10 */
    padding: 0 .5rem;                    /* px-2；官方沒有 py，靠 h-10 定高 */
    text-align: left;                    /* text-left（blade 的 .text-end 帶 !important，數字欄照樣靠右，與官方 demo 一致） */
    vertical-align: middle;              /* align-middle */
    font-weight: 500;                    /* font-medium = --font-weight-medium */
    white-space: nowrap;                 /* whitespace-nowrap */
    letter-spacing: normal;              /* 官方無 tracking-*；歸零本檔既有的 .01em */
}

/* ★★★【需業主拍板 · 唯一有版本爭議的一項】表頭字色。
   v4（main 分支）是 `text-foreground`；官網仍在服務的舊 registry 是 `text-muted-foreground`。
   本專案現況＝`--muted-foreground`（實測 rgb(115,115,115)），也就是**舊版官方值**。
   下面這行採用 v4（＝業主指定的那支 URL）。對比從 4.6:1 提升到 21:1，無 WCAG 風險。
   ★不同意就整條註解掉，現況自動回到 --muted-foreground，不需要改別的地方。 */
.container-wide .card .table > thead > tr > th { color: hsl(var(--foreground)); }

/* ── (4) `<td>` = TableCell `p-2 align-middle whitespace-nowrap` ──
   ★特異度必須贏過 `.table:not(.table-bordered) > tbody > tr > td`(0,2,3) ——
     `:not(.table-bordered)` 內層的 class 是**算分**的，那條是 (0,2,3) 跟只寫
     `.container-wide .table > tbody > tr > td` **同分**，只能靠檔案順序決勝（脆弱）。
     ⇒ 多墊一層 `.card` 做成 (0,3,3)，不依賴誰寫在後面。
   ★白名單外的一條也一併壓掉：`buyer/layout.blade.php` 在 ≤768px 有
     `.table > :not(caption) > * > *{padding:10px 6px;font-size:.85rem}`(0,1,1)，
     對 th/td 現在就已經是死碼（打不贏 (0,1,3)/(0,2,3)），本條之後依然是死碼 ——
     **但它對 tfoot 是活的**，見 (6)。 */
.container-wide .card .table > tbody > tr > td {
    padding: .5rem;                      /* p-2 */
    vertical-align: middle;              /* align-middle */
    white-space: nowrap;                 /* whitespace-nowrap（v4）；★這會讓 blade 第 603 行
                                            品名欄的 .text-break 成為無效 class（不會換行）。
                                            這是官方行為：長內容靠外層 overflow-x 橫捲，
                                            不是縮字也不是折行。blade 那個 class 本輪不動。 */
}

/* ── (5) TableRow = `border-b transition-colors hover:bg-muted/50` ──
   · border-b：thead 的線本檔已畫在 th 上、tbody 的線畫在 td 上，
     `border-collapse:collapse` 之下與官方畫在 tr 上**渲染完全相同**（同一條格線邊）⇒ 不改。
   · 末列去線：本檔既有 `.table > tbody > tr:last-child > td{border-bottom:0}`
     ＝ 官方 TableBody 的 `[&_tr:last-child]:border-0` ⇒ 已符合，不改。
   · hover / transition：**本頁完全沒有** —— 本檔所有 hover 規則都要求 `.table-hover`，
     而 blade 的 class 是 `table align-middle mb-0`。官方 TableRow 是**無條件**帶 hover 的。
   ★ td 的背景是 `background-color: transparent`（本檔 (0,1,1)，非 !important），
     所以底色鍵在 tr 上會透出來，不需要去打 td。 */
.container-wide .card .table > tbody > tr {
    transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke;
    transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);   /* --default-transition-timing-function */
    transition-duration: 150ms;                                 /* --default-transition-duration */
}
.container-wide .card .table > tbody > tr:hover { background-color: hsl(var(--muted) / .5); }   /* hover:bg-muted/50 */

/* ── (6) `<tfoot>` = TableFooter `border-t bg-muted/50 font-medium [&>tr]:last:border-b-0` ──
   ★現況是**三段裡唯一沒人管的一段**（實測：背景透明、font-weight 400、
     border-top 0、padding 8px ＝ 純 Bootstrap 預設）。而且 ≤768px 時
     `buyer/layout.blade.php` 那條 (0,1,1) 對 tfoot 是**活的** ⇒ tfoot 會縮成
     11.9px / padding 10px 6px，thead/tbody 卻仍是 14px / 9.6px 12px ——
     同一張表三段字級不一致。本節把 tfoot 一起收進來，順帶消掉這個斷點不一致。
   ★`border-t` 與 blade 第 619-620 行 td 上的 `.border-top`（Bootstrap utility，帶 !important）
     落在**同一條格線邊**，collapse 之下合併成一條線，不會變兩條。
     ⇒ blade 那兩個 class 變成冗餘但無害，本輪**不動 blade**（動了就會依賴這份還沒進檔的 CSS）。
   ★`[&>tr]:last:border-b-0` 由下面第二條給；blade 第 627-628 行的 `.border-bottom-0`
     同樣變成冗餘但無害。 */
.container-wide .card .table > tfoot {
    border-top: 1px solid hsl(var(--border));   /* border-t */
    background-color: hsl(var(--muted) / .5);   /* bg-muted/50 */
    font-weight: 500;                           /* font-medium */
}
.container-wide .card .table > tfoot > tr > td {
    padding: .5rem;                             /* 與 (4) 同 —— 官方 TableFooter 內用的就是 TableCell */
    vertical-align: middle;
    white-space: nowrap;
    /* ★★這一行是 768px 實測抓到的，不是推導出來的：只給 (2) 的 `table{font-size:.875rem}`
       不夠 —— `buyer/layout.blade.php` 那條 (0,1,1) 是**直接鍵在 td 上**，
       贏得過從 table 繼承下來的字級。第一版沒寫這行，實測 ≤768px 時
       tfoot 仍是 13.6px（line-height 19.43）而 thead/tbody 是 14px，同表兩種字級。
       ⇒ 必須在 (0,3,3) 這條直接宣告一次。 */
    font-size: .875rem;                         /* text-sm */
}
.container-wide .card .table > tfoot > tr:last-child > td { border-bottom-width: 0; }   /* [&>tr]:last:border-b-0 */

/* ── (7) TableCaption `mt-4 text-sm text-muted-foreground` ──
   本頁沒有 `<caption>`，先備著；未來加了 caption 才有作用，現在 0 命中、0 風險。 */
.container-wide .card .table > caption {
    margin-top: 1rem;                    /* mt-4 = --spacing*4 */
    font-size: .875rem;                  /* text-sm */
    color: hsl(var(--muted-foreground)); /* text-muted-foreground */
    caption-side: bottom;                /* 官方 Table 的 caption-bottom */
}

/* ── (8) 明確**不**照抄的兩項（不是漏掉，是查證後決定）──
   · `[&:has([role=checkbox])]:pr-0` / `[&>[role=checkbox]]:translate-y-[2px]`
     —— 本頁表格沒有任何 checkbox 欄（blade 5 欄全是文字/數字），
        照抄只是加兩條永遠不會命中的規則。要加勾選欄時再補。
   · `data-[state=selected]:bg-muted`（TableRow）
     —— 那是 TanStack Table 的列選取狀態，本頁是純唯讀明細、沒有選取行為。
   ★ 欄寬（`.table .col-idx{width:50px}` 等 6 條，在 buyer/layout.blade.php）
     與 `.tabular-nums` 保留不動：官方 demo 自己也用 `w-[100px]` 指定欄寬，
     `font-variant-numeric` 是內容語彙不是元件語彙。 */

/* ── (9) 實測結果（2026-08-10，注入 <style> 預演，全部走 https://inv.2025-seller.mtest/ ）──
   ★量測方式：同一個 evaluate 內「移除樣式→快照→加上樣式→快照→比對」，
     避免兩次呼叫之間視窗被改寬度造成假差異（本輪實際遇過 viewport 從 1440 漂到 992/768）。
     每一組數字都印了 window.innerWidth 核對。

   ┌ 1440px（innerWidth=1440）
   │  掃描 body 底下**全部** 224 個非表格元素：
   │    非幾何屬性變動 = 1 筆，就是刻意的 `.table-responsive{position:static→relative}`
   │    純高度連動 = 8 筆（表格矮了 12px，下方區塊整體上移；沒有任何字級/顏色/內距被改到）
   │  document.scrollWidth 1440 → 1440（無水平溢出）
   │  表格：th 9.6px 12px → 0 8px、高 40.69 → 40；td 9.6px 12px → 8px、列高 41.19 → 37
   │        line-height 21 → 20；th 字色 rgb(115,115,115) → rgb(0,0,0)
   │        tfoot 透明/400/border-top 0 → rgba(245,245,245,.5)/500/1px
   ├ 768px（innerWidth=768）
   │  同樣 224 元素、非幾何變動仍只有 1 筆；scrollWidth 768 → 768
   │  ★這裡抓到第一版的漏網：tfoot 的 font-size 13.6px 沒被修好（見 (6) 的註解），已補
   ├ 390px（innerWidth=390）
   │  ★**表格的橫向捲動消失了**：`.table-responsive` scrollWidth 378 → 356 = clientWidth 356。
   │    改動前在 iPhone 寬度下「小計」欄要滑才看得到，改動後整張表塞得進去。
   │    （內距從 12px 縮到 8px，五欄共省下 40px）
   │  document.scrollWidth 390 → 390；TreeWalker 掃 104 個可見文字節點，溢出 0
   └ 暗色（html.dark）
      th 字色 rgb(250,250,250)（--foreground 暗色值）、tfoot 底 rgba(38,38,38,.5)、
      框線 rgba(255,255,255,.1)（--border 暗色是 `0 0% 100% / 10%`，`hsl(var(--border))` 正常解析）
      ⇒ 全部走 token，沒有寫死顏色。

   ★★守門誘餌（先證明偵測器會報紅，再相信那些 0）：
     ① 對「非表格元素」注入 `.card-header{padding:40px!important}` /
        `.btn{font-size:31px!important}` / `.small{letter-spacing:9px!important}` /
        `.badge{color:rgb(1,2,3)!important}` / `.form-label{font-weight:900!important}`
        ⇒ 224 個節點中報出 28 個變動；移除後殘留 0。
     ② 對溢出偵測器塞一個 `left: innerWidth-20px` 的 nowrap 文字節點
        ⇒ 文字節點 104 → 105、溢出從 0 變成 1（抓到 BAIT_OVERFLOW_TEXT…）；移除後回 0。
     ⇒ 上面那些「非幾何變動 1 筆」「溢出 0」是偵測器睜著眼睛量到的，不是假綠燈。

   ★hover 實測（不是只看規則存在）：`:hover` 狀態下該列
     background-color = rgba(245,245,245,.5)（＝bg-muted/50），同表其他列 rgba(0,0,0,0)，
     transition-duration 0.15s、property 清單與 Tailwind `transition-colors` 逐字相同。
     ★視覺上很淡（白底上疊 50% 的 #F5F5F5 ≈ #FAFAFA）—— 官方 shadcn 就是這個淡度，不是沒生效。 */

/* --- S-103. 業主裁決收尾三件：POS 觸控磚／表單控制項／`.btn-lg` 一律收到 btn-sm 的 32px（2026-08-10）---

   ═══ 業主指令鏈（原話，不是我的轉述）═══
   「全站全部都要收到 btn-sm」「請你遵守全站」「ssot」
   我把三件待決逐條攤給業主，他的裁決是：
   「1.同意收32  2.收32  3.都要遵守ssot 32 btn sm」
   ⇒ 本節＝ §S-101 的收尾。§S-101 收的是「按鈕高度」，本節收的是
     (一) 唯一逃過 §S-101 的按鈕造型（POS 側欄底部的兩行觸控磚，實測 58px）
     (二) 表單控制項（同一列「鈕 32、框 40」的高低差）
     (三) `.btn-lg` 的**左右內距與字級**（§S-101 只收了高度，padding 還停在 0 2rem）

   ★★★ 與 §S-101 同樣是「相對於 shadcn 官方的刻意偏離」，不要當成漏做去「修回官方值」。
       官方 Input 是 `h-9`(36)、官方 Button `lg` 是 `h-10`(40) 且 `px-8`。
       業主要的是**全站只有 32 這一階**。要改回官方階梯必須再問一次業主。

   ═══ 本節推翻／覆蓋的舊條文（一律用 §編號與檔名，不用行號）═══
   (1) §S-4 (a) 「未帶尺寸修飾的表單控制項 = 36px（規格 §0 default）」→ **08-10 作廢，改 32**。
       連同該條的 `.app-content …> .input-group-text{min-height:36px}` 一起。
   (2) §S-89 `:where(.fb-public) .form-control{min-height:40px; padding:7px 12px; line-height:24px}`
       的**垂直三個值**→ 改 32 的算式。★左右 `padding:12px` 與 `font-size:14px` **不動**（理由見二-(3)）。
   (3) §login `.login .form-control,.login .form-select{min-height:40px; padding:.5rem .7rem}`
       的**垂直值**→ 32。左右 `.7rem` 不動。
   (4) §S-4 (d) `.h-45px` 的 36 / 40 兩階 → 32。
   (5) §S-38 的 `.modal` 階梯**只收了按鈕**，非 sm 的表單控制項當時漏了 → 本節補（見二-(2)）。
   (6) §POS-3 `.pos .form-control,.pos .form-select{min-height:40px}`（(0,2,0)，對 input 是死宣告、
       **對 textarea 是活的**）與 `.pos .form-control.form-control-sm{min-height:34px}`（(0,3,0)，**是活的**）→ 32。
   (7) §form-floating `.form-floating > .form-control{height:36px}` 與
       `.login .form-floating > .form-control{height:40px !important}` → 32。
   (8) `.btn.btn-lg:not(.btn-icon){padding:0 2rem; font-size:.875rem}`（2026-07-28 那條）→ 改成與 `.btn-sm` 同值。
   ★★ §S-101 章節註解裡「`.btn-lg` 的左右氣勢是方案卡 CTA 的視覺重量，沒有指令就不改（YAGNI）」
      這句話**現在是錯的，本節推翻它** —— 業主已經明確下了那道指令（「3.都要遵守ssot 32 btn sm」）。

   ═══ 量測基準（1600x950、headless、探針放普通 div、每次先確認 location.href 與 body.className）═══
   施工前實測，四個作用域各自的值（`.btn-sm` 是這一版的標準答案，其餘都要對齊它）：
       作用域              btn-sm            btn(default)   btn-lg              form-control  form-select  textarea(rows=1)  input-group
       .app-content        32 / 8px / 13px   32 / 12px      32 / **32px** / 14  **36**        **36**       **38**            **36**
       .modal(app 之外)    32 / 8px / 13px   32 / 12px      32 / **32px** / 14  **38**        **38**       **38**            **38**
       :where(.fb-public)  32 / 10px / 13px  32 / 16px      32 / **32px** / 14  **40**        **38**       **40**            **40**
       .pos .pos-sidebar   32 / 8px / 13px   32 / 12px      32 / **32px** / 14  36 / sm=**34** 36 / sm=34  **40**            36
   （斜體處＝本節要收的；「/」後面兩個數字是左右 padding 與 font-size）
   ★`/login` 真實輸入框實測 **40px**（`height:40px` 來自 view 的 inline style，不是主題），
     同一頁的送出鈕已經是 32 ⇒ 這就是業主看到的「一列兩種高度」。

   ═══ ★★★ 全節一律不動 `font-size`（這不是漏做，是刻意的，理由有二）═══
   ① 與 §S-101 同一個立場：業主要收的是「大小」＝高度，字級不在指令範圍內。
   ② **iOS Safari 對 `font-size < 16px` 的輸入框會在聚焦時自動放大整頁，而且縮放後不會還原。**
      §S-89 為此在 `@media (max-width:768px)` 內留了一條
      `:where(.fb-public) .form-control{font-size:16px}`（**現在還在，本節沒有動它**）。
      本節**完全不宣告 form-control / form-select / textarea 的 font-size**，
      所以那條手機規則不會被覆蓋，也不需要在斷點內補還原。
      ★下一個做「字級統一」的人請先讀這段：桌機要 13px 可以，**手機斷點不准低於 16px**。
      ★唯一有動 font-size 的是三-(1) 的 `.btn-lg`（14 → 13，對齊 `.btn-sm`）——
        那是按鈕不是輸入框，不受 iOS 自動縮放影響。

   ═══ 算式一律沿用 §S-101 的那一條，不另發明 ═══
       `padding-top 5 + line-height 20 + padding-bottom 5 + border 1x2 = 32`
   ★為什麼不是「只寫 min-height:32」：§S-101 已經寫過一次教訓 ——
     36/38/40 這些數字**不是 min-height 撐出來的**，是 line-height + padding 算出來的實體高度，
     只改 min-height 完全不會生效。三個數字必須一起改。
   ★`<input>` / `<select>` 另外補一個顯式 `height:32px`（沿用 §S-4 (a) 原本就有 `height`），
     **`<textarea>` 一律不寫 `height`** —— 見二-(6)。
   ------------------------------------------------------------------------- */

/* ══ 一、POS 側欄底部三顆觸控磚：兩行「圖示上文字下」→ 單行 32px ══════════════
   對象：`.pos .pos-sidebar .pos-sidebar-footer` 內的「後台管理／全部登出／暫停」
   （`pos/package.blade.php`、`pos/machine.blade.php`），實測 **58px**：
       padding-top 5 + 圖示 margin-top 4 + 圖示 18 + 圖示 margin-bottom 4
       + 文字行 20 + padding-bottom 5 + border 2 = 58
   §S-101 已經把它們的 `min-height` 收成 32，但 32 只是墊底 —— 造型是**內容撐出來的**。

   ★★★ 為什麼**只能**用 CSS，不能改 blade 的 class（這是本件最重要的一條）★★★
     `pos/machine.blade.php` 在更新暫停/繼續狀態時是用 JS 直接重寫 innerHTML：
        `.html('<i data-lucide="pause" class="d-block fs-18px my-1"></i> 暫停')`
        `.html('<i data-lucide="play"  class="d-block fs-18px my-1"></i> 繼續')`
     ⇒ 就算把 blade 上的 `d-block fs-18px my-1` 拿掉，**使用者一按暫停就會被 JS 寫回來**，
       磚塊造型復活。CSS 是這顆按鈕造型的唯一 SSOT。

   ★ 機制：父層改成 flex 容器之後，子元素的 `display:block` 會被 flex **blockify**、
     不再另起一行 —— 所以**不需要跟 `.d-block` 的 `!important` 硬碰**（實測成立）。
     真正需要 `!important` 的只有 `.my-1` 的上下 margin：Bootstrap 的間距 utility 自帶
     `!important`，不用 `!important` 等於沒寫（同 §S-79 的理由）。
   ★ 水平間距**不在這裡宣告**：`.btn{gap:.5rem}`（§S-79，對得到官方 button.tsx 的 `gap-2`）
     本來就存在，只是元素不是 flex 容器時 gap 無效；一旦 flex 化它自己就生效了。
     在這裡再寫一次 gap 會讓「按鈕內圖示間距」多出第二個權威來源。
   ★ 圖示的 `.fs-18px`（18px）**刻意保留**，這是判斷不是疏忽：
     ① 它不影響高度 —— 18px 的圖示放在 20px 的文字行盒裡，交叉軸最高者仍是 20 ⇒ 5+20+5+2 = 32。
     ② §S-101 已經定調「font-size 維持上游值」，這裡跟著同一個立場。
     ③ `.fs-18px` 是帶 `!important` 的 utility，要壓它就得再開一場 `!important` 軍備競賽，
        而且同一支 utility 在 POS 別處也在用，改它會外溢。
     ⇒ 若日後業主嫌圖示偏大，正解是在**本節**加一條指名 `.pos-sidebar-footer .btn > svg{font-size:16px!important}`，
       不要去動 `.fs-18px` 這個 utility 本身。
   ★ 特異度：`.pos .pos-sidebar .pos-sidebar-footer .btn` = (0,4,0)，
     全檔對這條選擇器**沒有任何 `display` 宣告**（已 grep 確認：§POS-4／§S-98／§S-101 都只宣告尺寸與顏色）
     ⇒ 不需要墊特異度。 */
.pos .pos-sidebar .pos-sidebar-footer .btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
}
.pos .pos-sidebar .pos-sidebar-footer .btn > i,
.pos .pos-sidebar .pos-sidebar-footer .btn > svg {
    margin-top: 0 !important;       /* 打掉 .my-1 的 .25rem，58 - 4 - 4 = 50 */
    margin-bottom: 0 !important;
}

/* ══ 二、表單控制項：`.form-control` / `.form-select` / `.input-group-text` / `textarea` 收 32 ══
   ★★ 一共 **六套作用域**（比按鈕多兩套）。少收一套就會再出現「同站多種高度」，
      而且**只量按鈕不會發現** —— 這是本件最容易做半套的地方，所以逐套列出來：
        (1) `.app-content`      後台全域階梯（§S-4 (a)）              36 → 32
        (2) `.modal`            `.app-content` 之外的彈窗（§S-38 漏收）38 → 32   ★本節新增
        (3) `:where(.fb-public)` 公開頁／買家／金流（§S-89）           40 → 32
        (4) `.login` 家族       登入五頁（主題 §login ＋ **view 的 inline style**）40 → 32
        (5) `.form-floating`    浮動標籤（登入／SSO 登入）             36 / 40 → 32
        (6) `.pos`              POS 兩頁（§POS-3）                     40 / sm 34 → 32
      另加一條跨作用域的 `textarea` 專條（見二-(8)）。
   ★★ **主動查過但判定「不需要收」的兩套**（寫下來免得下次有人以為是漏做）：
        `.theme-panel`（賣賣小幫手側拉板）—— §S-4 (b) 為它的**按鈕**開了特例，
          但 `includes/_head.blade.php` 的面板內容實查只有一顆 `.form-check-input` 核取方塊，
          **沒有任何 `.form-control` / `.form-select`** ⇒ 開條規則會是空集合。
        SweetAlert 的 `.swal2-input` / `.swal2-textarea` / `.swal2-select`（§S-100 已 shadcn 化外觀，
          但沒有訂高度）—— 全樹 grep `Swal.fire({ input: … })` **0 命中**，
          專案根本沒有用到 SweetAlert 的輸入型彈窗 ⇒ YAGNI。
          ★哪天真的用了，正解是在**本節**補一條，不是回頭改 §S-100 的外觀章節。 */

/* ── (1) `.app-content` 全域階梯 36 → 32（§S-4 (a) 作廢改值）──
   ★選擇器**逐字照抄** §S-4 (a)，一個 `:not()` 都不動：同特異度 (0,9,1)/(0,7,1)，位置在後取勝。
     照抄的理由不是偷懶 —— 那串 `:not(.input-group-sm)` 是 §S-4 量出來補的守門
     （搜尋列的 input 沒帶 `.form-control-sm`，靠外層 `.input-group-sm` 才變 32），
     少一個 `:not()` 特異度就會變，會反過來壓贏上游把 input-group 打回高低不齊。 */
.app-content :not(.input-group-sm):not(.input-group-lg) > .form-control:not(textarea):not(.form-control-sm):not(.form-control-lg):not(.form-control-color):not([type="file"]):not([type="color"]),
.app-content :not(.input-group-sm):not(.input-group-lg) > .form-select:not(.form-select-sm):not(.form-select-lg):not([multiple]):not([size]) {
    height: 32px;
    min-height: 32px;
    line-height: 20px;      /* 5 + 20 + 5 + 2(border) = 32 */
    padding-top: 5px;
    padding-bottom: 5px;
    /* font-size 不宣告 —— 見章節開頭「全節一律不動 font-size」 */
}
/* `.input-group-text` 必須跟上：漏掉就是 32px 的輸入框配 36px 的附加框（§S-4 原本的教訓） */
.app-content :not(.input-group-sm):not(.input-group-lg) > .input-group-text {
    min-height: 32px;
    line-height: 20px;
    padding-top: 5px;
    padding-bottom: 5px;
}

/* ── (2) `.modal`（`.app-content` 之外的彈窗）38 → 32 ── ★本節新增，補 §S-38 的漏
   §S-38 當年發現「同一頁 48 個 modal 有 2 個寫在 `.app-content` 之外」，
   於是把 `.modal` 也列為階梯適用範圍 —— 但**它只收了 `.btn` 與 `-sm` 那一階**，
   「未帶尺寸修飾的 `.form-control` / `.form-select` / `.input-group-text`」沒有對應條文。
   實測（在非公開頁合成 `<div class="modal">` 探針）：`.btn`=32 但 `.form-control`=**38**（Bootstrap 出廠值）
   ⇒ 那 2 個逃出 `.app-content` 的彈窗裡，鈕 32 配框 38。
   ★選擇器與 (1) 同構、只換前綴，值也完全相同 ⇒ 對「住在 `.app-content` 裡的」46 個彈窗
     兩條同時命中但宣告一致，不會有分歧（與 §S-38 對按鈕的做法一致）。 */
.modal :not(.input-group-sm):not(.input-group-lg) > .form-control:not(textarea):not(.form-control-sm):not(.form-control-lg):not(.form-control-color):not([type="file"]):not([type="color"]),
.modal :not(.input-group-sm):not(.input-group-lg) > .form-select:not(.form-select-sm):not(.form-select-lg):not([multiple]):not([size]) {
    height: 32px;
    min-height: 32px;
    line-height: 20px;
    padding-top: 5px;
    padding-bottom: 5px;
}
.modal :not(.input-group-sm):not(.input-group-lg) > .input-group-text {
    min-height: 32px;
    line-height: 20px;
    padding-top: 5px;
    padding-bottom: 5px;
}

/* ── (3) `:where(.fb-public)` 公開頁 40 / 38 → 32（§S-89 的垂直值作廢）──
   ★★ 這裡**故意不用 `padding` 簡寫**，只寫上下兩個 longhand —— 與 §S-101 (6) 同一個坑：
      §S-89 的 `:where(.fb-public) .form-control{padding:7px 12px}` 與本條同為 (0,1,0)，
      本節排在後面，寫簡寫會連**左右的 12px 一起贏走**（規格 §0：控制項左右內距 12px）。
   ★★ `.form-select` 是 §S-89 **漏收**的：`:where(.fb-public) .form-control` 不含 `.form-select`
      ⇒ 公開頁的下拉一直吃 Bootstrap 出廠的 **38px**（實測），與旁邊 40px 的輸入框差 2px。
      本條把它一起納入，左右 padding 交給 Bootstrap 出廠值（左 12px、右 36px 讓給箭頭）。
      ★下拉箭頭是 `background-position: right .75rem center`（垂直置中），高度變 32 之後
        箭頭自己會跟著置中，**不需要**額外校正（落地後已用探針複量確認）。
      ★★**這一條目前是「防禦性」的，不是在修現行畫面**：已 grep 全樹，
        `buyer/` `payment/` `auth/` `errors/` 四個目錄底下 `<select>` 與 `.form-select`
        **命中數皆為 0**。仍然要寫，理由同 §S-101a 的立場：
        「現在剛好是 0」不等於「以後也是 0」，尺寸階梯要用**規則**保證，不是用巧合保證。
   ★ `.input-group-text` 一併收：公開頁的附加框沒有自己的高度宣告，是被
      `.input-group{align-items:stretch}` 拉到跟輸入框一樣高的 —— 輸入框收 32 它就跟著 32，
      但仍要打掉它自己的 6px 上下 padding，否則內容會頂到 33。
   ★ **不宣告 font-size** ⇒ `@media (max-width:768px)` 那條 16px 防 iOS 縮放的規則原封不動。 */
:where(.fb-public) .form-control,
:where(.fb-public) .form-select {
    min-height: 32px;
    line-height: 20px;      /* 5 + 20 + 5 + 2 = 32 */
    padding-top: 5px;
    padding-bottom: 5px;
}
:where(.fb-public) .input-group-text {
    min-height: 32px;
    line-height: 20px;
    padding-top: 5px;
    padding-bottom: 5px;
}

/* ── (4) `.login` 家族 40 → 32 ──
   兩層對手，**必須分兩條打**：
     (a) 主題 §login `.login .form-control,.login .form-select{min-height:40px; padding:.5rem .7rem}` = (0,2,0)
         ★這條的 `padding:.5rem`（8px）才是真兇：8 + 21 + 8 + 2 = 39，
           只寫 `min-height:32` 的話實體高度仍然是 39（「寫了 ≠ 生效」那一族）。
           同樣用 longhand 只收上下、把左右的 `.7rem` 留給它自己。
     (b) ★★★ `resources/views/auth/_auth_shell_css.blade.php` 的 **inline `<style>`**：
           `.login.login-v2 .login-content .form-control{ height:40px; … }` = **(0,4,0)**
           `.login.login-v2 .login-content .input-group-text{ height:40px; padding:0 12px }` = (0,4,0)
         那是寫在 `<head>` 裡、**排在外部 shadcn-theme.css 之後**的 style 區塊
         ⇒ 同分時它贏。本節不准動 blade，所以只能把特異度墊到 (0,5,0)。
         做法與 §S-101 (7) 對送出鈕用的完全一樣（那裡是 `.login.login-v2 .login-content .btn.btn-primary`）。
         這裡多帶的一格是 `:not(.form-control-lg)` / `.input-group`：
           `:not()` 會把括號內的特異度算進去（+1 class）⇒ (0,5,0)，剛好超過 (0,4,0) 一格。
         ★刻意只多 **一** 格：多墊會壓過 §S-4 (d) 的 `.h-45px` 與 (5) 的 `.form-floating`，把它們的值搶走。
   ★ `#checkEye`（顯示密碼的眼睛）是 `position:absolute; top:50%; margin-top:-16px` 的 32x32 盒子，
     輸入框從 40 縮到 32 之後 50% 仍然是垂直中線，**不需要改**（它自己就是 32，剛好齊平）。 */
.login .form-control,
.login .form-select {
    min-height: 32px;
    line-height: 20px;
    padding-top: 5px;
    padding-bottom: 5px;
}
.login.login-v2 .login-content .form-control:not(.form-control-lg),
.login.login-v2 .login-content .form-select:not(.form-select-lg) {
    height: 32px;
    min-height: 32px;
}
/* ★這一條只換高度、**不碰 padding** —— view 的 inline style 給它 `padding:0 12px`，
   而 `.input-group-text` 是 `display:flex; align-items:center`，內容自己會垂直置中，
   再去搶 padding 只是多開一場沒必要的特異度戰爭。 */
.login.login-v2 .login-content .input-group .input-group-text {
    height: 32px;
    min-height: 32px;
}

/* ── (5) `.form-floating` 浮動標籤 36 / 40 → 32 ──
   ★上游那條把 `padding-top/bottom` 歸零、用 `line-height:1.4` 撐（因為浮動標籤要疊在上面），
     本節**只換高度、不動它的 padding 與 line-height** —— 那組值是為了標籤定位算的，
     改了會讓 label 的落點跑掉（那是 §form-floating 的職責，不是本節的）。
   ★`.login .form-floating > …` 上游帶 `!important`（(0,3,0)），
     所以本節這一條也必須帶 `!important`，否則寫了不生效。
   ★`> i` / `> svg`（眼睛圖示的點擊盒）跟著收，否則 32px 的輸入框旁邊掛一個 40px 的熱區。 */
.form-floating > .form-control,
.form-floating > .form-select,
.form-floating > .form-control:focus,
.form-floating > .form-control:not(:placeholder-shown),
.form-floating > .form-control-plaintext:focus,
.form-floating > .form-control-plaintext:not(:placeholder-shown) {
    height: 32px;
    min-height: 32px;
}
.login .form-floating > .form-control,
.login .form-floating > .form-select {
    height: 32px !important;
    min-height: 32px;
}
.login .form-floating > i,
.login .form-floating > svg { height: 32px; }

/* ── (6) `.h-45px` 的兩階 36 / 40 → 32（§S-4 (d) 作廢改值）──
   ★選擇器與上游同分、位置在後取勝；`!important` 照抄（上游帶著，不帶就輸給它）。
   ★ §S-4 (d) 已註記「blade 0 命中」，本條與上游一樣是**防復活**用的，不是在修現行畫面。 */
.form-control.h-45px,
.form-select.h-45px { height: 32px !important; min-height: 32px; }
.login .form-control.h-45px,
.login .form-select.h-45px { height: 32px !important; min-height: 32px; }

/* ── (7) POS 兩頁（§POS-3 作廢改值）──
   (a) `.pos .form-control,.pos .form-select{min-height:40px}` = (0,2,0)
       ★對 `<input>` / `<select>` 是**死宣告**（被 (1) 的 (0,9,1) 壓過，實測一直是 36 不是 40），
         同步成 32 純粹是**防復活**（哪天有人拿掉 (1)，40 會無聲跳回來）。
       ★★但對 `<textarea>` 它是**活的** —— (1) 的選擇器帶 `:not(textarea)`，
         textarea 整個逃出全站階梯，於是 `.pos` 內的 textarea 實測是 **40px**（探針量到，不是推論）。
         §S-101 (11)(c) 當時只單獨救了 `#search` 那一顆，`.pos` 內其他 textarea 沒人管。
   (b) `.pos .form-control.form-control-sm,.pos .form-select.form-select-sm{min-height:34px}` = (0,3,0)
       ★★這條是**活的缺陷**，不是死宣告：它 (0,3,0) 壓得過
         `.app-content .form-control-sm:not(textarea)` 的 (0,2,1)（class 數 3 > 2）
         ⇒ POS 內的 sm 控制項實測 **34px**，比全站 sm 階多 2px，肉眼幾乎看不出來但就是「一站兩種 32」。
         ★這是本節在量測時**新發現的既有缺陷**，不是 §S-101 造成的。 */
.pos .form-control,
.pos .form-select {
    min-height: 32px;
}
.pos .form-control.form-control-sm,
.pos .form-select.form-select-sm {
    min-height: 32px;
}

/* ── (8) `<textarea>` 專條（跨全部作用域）──
   ★★★ textarea 的高度**不吃 `height`**，是 `rows x line-height + padding + border` 算出來的
      （§S-4 為此把 textarea 排除在固定高度之外，那個排除是對的、本節不推翻）。
      ⇒ 收斂的正解是「改算式、不釘 height」：`rows="1"` 自然落在 5 + 20 + 5 + 2 = 32，
        `rows="4"` 則是 5 + 80 + 5 + 2 = 92，**`rows` 仍然完全有效**。
   ★ Bootstrap 出廠有一條 `textarea.form-control{min-height:calc(1.5em + .75rem + 2px)}` = (0,1,1)，
     在 16px 字級下算出來是 **38px** —— 這就是「明明沒人宣告卻量到 38」的來源。
     本節四條前綴都是 (0,3,1) 以上，壓得過它。
   ★ `-sm` 那一階分開處理：§S-4 與 §S-38 都對它宣告了 `min-height:0; height:auto`
     （為了讓 rows 完全接管）。那個立場保留，本節**只把地板從 0 抬到 32**：
     `rows="1"` 由 29.5 補到 32（收斂），`rows>=2` 一律大於 32 ⇒ 行為不變。
     ★同時**不動它的 line-height / padding / font-size**，避免與那兩節打架。
   ★`-lg` 那一階刻意排除在外，理由是**全站唯一的 `textarea.form-control-lg`**
     （`AiTool/alpha/index.blade.php` 的 AI 場景描述框）帶著 HTML inline style
     `min-height:60px; padding-top:15px; padding-bottom:15px`。inline style 贏過任何一般選擇器，
     本節就算收了也收不動；而且那顆本來就是**刻意要比 32 高的多行輸入框**，
     收它是錯的。⇒ 寫進 `:not()` 讓「不收」變成明文，不要留給下一個人再查一次。 */
.app-content textarea.form-control:not(.form-control-sm):not(.form-control-lg),
.modal textarea.form-control:not(.form-control-sm):not(.form-control-lg),
:where(.fb-public) textarea.form-control:not(.form-control-sm):not(.form-control-lg),
.pos textarea.form-control:not(.form-control-sm):not(.form-control-lg) {
    min-height: 32px;
    height: auto;
    line-height: 20px;      /* rows x 20 + 5 + 5 + 2 */
    padding-top: 5px;
    padding-bottom: 5px;
}
.app-content textarea.form-control-sm,
.modal textarea.form-control-sm {
    min-height: 32px;
}

/* ══ 三、`.btn-lg` 完全對齊 `.btn-sm`（不只高度）══════════════════════════════
   業主原話「都要遵守 ssot 32 btn sm」⇒ `.btn-lg` **不是「高度收成 32 就好」**，
   左右內距與字級也要等同 `.btn-sm`，否則畫面上會出現一顆「32px 高但寬得離譜」的扁長鈕。
   §S-101 (5) 只收了 `min-height:32`，`padding:0 2rem`（32px）與 `font-size:.875rem`（14px）還在。

   ═══ 目標值是量出來的，不是憑記憶填的 ═══
   `.app-content` 內合成 `<a class="btn btn-sm btn-primary">` 探針（1600x950，落地後的真實檔案）：
       height 32 / line-height 19.5 / padding 0 8px / font-size 13px / border-radius 8px / display inline-flex
   同一支探針的 `<a class="btn btn-lg btn-primary">`：
       height 32 / line-height 21   / padding 0 **32px** / font-size **14px** / border-radius 8px / display inline-flex
   ⇒ 差的就是**左右 padding（32 vs 8）與字級（14 vs 13）**兩項，其餘早就一致。

   ★`line-height` 刻意**不宣告**：`.btn` 的 line-height 是 Bootstrap 的
     `var(--bs-btn-line-height)`＝**比例值 1.5**，字級改成 13px 之後它自己就變成 19.5px，
     與 `.btn-sm` 完全相同。在這裡寫死 `19.5px` 會多出第二個權威來源（SSOT）。
   ★`gap` 一起對齊：§S-79 把 gap 綁在尺寸階上（`.btn` 8px / `.btn-sm` 6px / `.btn-xs` 4px），
     `.btn-lg` 本來沒有自己的 gap、吃 `.btn` 的 8px。既然它現在就是 sm 的體型，
     圖示與文字的間距也該跟著 sm ⇒ 補 `.375rem`。**這是對 §S-79「lg 吃 base gap-2」的刻意偏離。**
   ★`min-height:32px` **不在這裡重複宣告** —— §S-101 (5) 已經給了，重寫一次就是兩個來源。
   ★★ 為什麼寫 literal `padding-left/right` 而不是去改 `--bs-btn-padding-x` 這顆自訂屬性：
      Bootstrap 5.3 把尺寸做成變數 —— `.btn-lg{--bs-btn-padding-x:1rem; --bs-btn-font-size:1.25rem}`，
      真正產生實際值的是 `.btn{padding:var(--bs-btn-padding-y) var(--bs-btn-padding-x); font-size:var(--bs-btn-font-size)}`
      這條 **(0,1,0)** 的規則。兩種寫法都能贏，選 literal 的理由是
      **本檔既有的那條 2026-07-28 規則就是 literal**（`padding:0 2rem`），實測也確實生效（量到 32px）；
      改成覆寫變數會讓同一顆按鈕的內距同時有「變數」與「literal」兩種來源，違反 SSOT。
      ★但**不要**把本條寫成裸 `.btn-lg{…}`(0,1,0) —— 那會與 `.btn`(0,1,0) 打平、只靠載入順序取勝，
        一旦哪天有人在 `.btn` 之後再宣告一次就會無聲失效。維持 (0,3,0) 才有安全邊際。
   ★特異度：與 2026-07-28 那條 `.btn.btn-lg:not(.btn-icon)` 同為 **(0,3,0)**，位置在後取勝。
     `:not(.btn-icon)` 一定要留 —— 拿掉會變 (0,2,0) 且射程涵蓋 `.btn-icon.btn-lg`，
     把 §S-101 (10) 好不容易收成 32x32 的圖示鈕塞進 8px 的左右內距（圖示會偏一邊）。

   ═══ 實際會變的畫面（已 grep 全樹，`.btn-lg` 真實命中只有 8 顆、2 支檔）═══
     `pos/checkout.blade.php` 6 顆付款方式鈕（`btn btn-lg btn-white w-100 d-flex`）→ 左右 32 變 8。
       ★它們是 `w-100`（滿版），左右內距對外觀幾乎沒有影響，只有 `d-flex` 內的文字起點會左移 24px。
     `AiTool/alpha/index.blade.php` 2 顆（`btn btn-lg … px-4`）→ **不受影響**：
       `.px-4` 是 Bootstrap utility、帶 `!important`（1.5rem = 24px），永遠贏過本條。
       ★這是**正確行為**：blade 上明寫的 utility 代表頁面級的刻意決定，不該被主題偷走。
     auth 五頁**沒有** `.btn-lg`（view 註解寫得很清楚：「btn-lg 拿掉，高度由 _auth_shell_css 統一釘」）
       ⇒ 本節不需要為 `:where(.fb-public)` 另開一條 `.btn-lg` 變體（YAGNI，公開頁 0 命中）。 */
.btn.btn-lg:not(.btn-icon) {
    padding-left: 8px;      /* 對齊 .btn-sm 實測值（Bootstrap --bs-btn-padding-x: .5rem） */
    padding-right: 8px;
    font-size: 13px;        /* 對齊 .btn-sm 實測值；line-height 由 1.5 的比例自動變成 19.5px */
    gap: .375rem;           /* 對齊 §S-79 的 .btn-sm 階（官方 button.tsx size.sm 的 gap-1.5） */
}

/* --- S-103 附記：收完之後「還不是 32」的四筆，逐筆列出待業主裁決（2026-08-10）------------
   ★★ 這是**刻意留下的清單，不是漏做**。§S-103 收完之後我用探針把
      `.app-content` / `.modal` / `:where(.fb-public)` / `.login` / `.form-floating` / `.pos`
      六套作用域全部複量過，剩下這四筆不是 32。每一筆都寫清楚「為什麼沒收」與「要收的話怎麼收」，
      免得下一個人以為 §S-103 做半套，也免得他不查就自己動手。

   (1) ★★★ `.form-control-lg` / `.form-select-lg` / `.input-group-lg` = **48px**（實測）
       這一階**從來沒有人管過** —— §S-4 訂三階（sm 32 / default 36 / lg 40）時，
       `-lg` 只出現在 `:not(.form-control-lg)` 的守門裡，**沒有任何一條規則給它值**，
       所以它一路吃 Bootstrap 出廠的 48px。§S-101／§S-103 收的是「有人管的階」，它不在內。
       ★母體不小：全樹 grep 命中 **21 處 / 20 支 blade**（hr/user、StoreSetting 各平台
         baseSetting、Pick 三支 edit、Inventory、AiTool…），不是零星幾顆。
       ★★**沒有自作主張收，理由是這屬於「新開一個階的裁決」不是「補漏」**：
         業主 08-10 的裁決是針對我列出的三件（POS 磚／表單控制項／`.btn-lg`）逐條回的
         「1.同意收32 2.收32 3.都要遵守ssot 32 btn sm」，`-lg` 表單階當時不在清單上、
         他沒看過這個數字。§S-101 遇到同類情形（要不要推翻官方三階）也是先用
         AskUserQuestion 攤給業主才動 —— 照那個先例辦。
       ★要收的話怎麼收（已驗算，抄 §S-103 二-(1) 的算式即可）：
         `.app-content …> .form-control.form-control-lg` 這一類**不能**直接放進
         §S-103 二-(1) 的選擇器 —— 那條帶 `:not(.form-control-lg)` 守門，
         拿掉守門會連 `.input-group-lg` 的守門邏輯一起鬆掉。正解是**另開一條**
         指名 `-lg` 三兄弟，值照 `5 / 20 / 5 / border 2 = 32` 寫。
       ★★一併注意：`AiTool/alpha/index.blade.php` 的那顆是
         `textarea.form-control-lg` 且帶 **HTML inline style** `min-height:60px`，
         inline 贏過任何一般選擇器 ⇒ 就算收了它也不會變 32，而且它本來就該高
         （AI 場景描述的多行輸入）。統計 21 處時要把它算成「刻意例外」。

   (2) `input[type="file"].form-control` = **38px**（Bootstrap 出廠值）
       §S-4 (a) 明文把 `[type="file"]` 排除（「盒模型由瀏覽器決定」），§S-103 逐字照抄了那個守門。
       ★這一筆的性質與 (1) 不同：檔案輸入框的高度是**瀏覽器 UA 樣式 + 內建按鈕**撐的，
         硬釘 32 會把「選擇檔案」那顆原生按鈕壓扁。要收得連
         `::file-selector-button` 一起處理，那是另一件事。全樹 6 支 blade 用到。

   (3) `.pos .pos-menu .pos-rail-back .btn`（返回後台，rail 頂端）= **36x36**
       §S-97 用 `width/height:36px !important` 釘的，class 是 `btn btn-white h-45px`
       （**沒有 `.btn-icon`**，所以 §S-101 (10) 的 32x32 打不到它）。
       ★★這一筆 **§S-101 章節裡就已經寫明「已列入回報請業主裁決」** —— 它是舊的未決項，
         不是 §S-103 造成的。本節同樣不動它，維持同一個待裁決狀態，避免兩節各自表述。
       ★它是 /pos/package 目前畫面上**唯一**一顆不是 32 的按鈕（實測全頁只有 4 顆可見按鈕：
         側欄底部三顆已收成 32，就剩這顆 36）。

   (4) ★★★ **登入頁手機字級 14px，會觸發 iOS Safari 自動放大**（既有缺陷，非本節造成）
       `_auth_shell_css.blade.php` 的 `.login.login-v2 .login-content .form-control{font-size:14px}`
       是 **(0,4,0)**，壓過 §S-89 那條 `@media (max-width:768px){:where(.fb-public) .form-control{font-size:16px}}`
       的 (0,1,0) —— **media query 不加特異度**，所以防 iOS 縮放的那條在登入家族上是失效的。
       ★這**不是我造成的、也不是我發現的第一手**：`_auth_shell_css.blade.php` 自己的註解
         （「font-size —— …本檔這行若拿掉，登入頁手機字級會從 14px 變成 16px ——
         那可能是對的，但那是**規格決定**，要業主拍板，不在本輪授權內」）就已經記錄過同一件事。
       ★§S-103 **完全沒有宣告任何 form-control 的 `font-size`**（已用 CSSOM 逐條列出全部
         font-size 規則驗證過），所以既沒有修好它、也沒有讓它更糟，狀態原封不動。
       ★要修的話一條就夠（但同樣要業主拍板，因為會改變手機版登入頁的字級外觀）：
         在 `@media (max-width:768px)` 內補 `.login.login-v2 .login-content .form-control{font-size:16px}`。
   ------------------------------------------------------------------------- */


/* --- S-104. POS 標題列：推翻 §S-95 的「藏起來」裁決，並把 5 頁標記統一成一種（2026-08-10）---

   ═══ 一、業主裁決：§S-95 (4)(5) 的「標題列隱藏」推翻，原文保留存查 ═══
   業主原話：「這頁應該要跟包裝機一樣有標題」「統一 ssot」。
   ★**§S-95 那三條規則與整段章節註解一律原地保留、一個字不改**（禁刪守則）——
     本節只在檔尾追加覆蓋。讀 §S-95 的人請直接跳到這裡看現行結論。

   §S-95 當初只藏 package 一頁是**刻意**的，理由寫在它的章節註解「六」：
   它只有 package 的畫面實測證據，不想關掉沒驗過的另外 4 頁。這個理由當時成立
   （比「憑推論改設計」好），但代價就是今天業主抓到的那個不一致：
     /pos/package   標題列 DOM 在、computed display: none   <- 被藏掉
     /pos/machine   標題列正常顯示
   同一個殼、同一組 markup、同一顆側欄叫回鈕，兩頁長得不一樣。

   ═══ 二、更根本的問題是 SSOT 破口，不是顯示與否 ═══
   §S-95 能「只鎖 package」，靠的是 `:has(> .pos-page-title)` —— 而
   `.pos-page-title` 之所以是 package-only，純粹因為**同一條標題列有兩種標記**：
     package                             其餘 4 頁
     ------------------------------      ------------------------------
     h1 class="pos-page-title"           裸 h1
     i  class="text-brand shrink-0       i  class="text-brand shrink-0"
        pos-page-header-icon"
     button class="pos-sidebar-restore   button class="pos-sidebar-restore"
        ms-auto" ... aria-expanded="false"
     span class="pos-sidebar-restore-text"   裸 span
   §S-94 的 class 合約被迫寫成「兩種都吃得到」（那張表裡出現 4 次「package 有、其餘沒有」）。
   ★「CSS 同時認兩種選擇器」不是穩健，是**每加一條規則都要記得寫兩遍**，
     漏寫一次就會像 §S-95 這樣「藏了一頁、留了一頁」。破口在 markup，不在 CSS。

   ── 選定：5 頁一律**裸 h1 ＋ `text-brand shrink-0`**，package 改過來 ──
   選裸 h1 不選 `.pos-page-title` 的四個理由（查證過，不是投票）：
     (a) 4:1 的多數 —— machine / checkout / darka / _package 都是裸 h1，
         改 1 支檔的風險遠低於改 4 支。
     (b) §S-94 的基礎規則 `.pos .pos-page-header h1` 本來就打得到，
         `.pos-page-title` 那半條從頭到尾沒有獨立存在的必要。
     (c) ★**`.pos-page-title` 現在是一顆地雷的引信**：§S-95 用它當 scope，
         只要它出現在哪一頁，那一頁的標題列就會消失。把它從 markup 拔掉，
         §S-95 (4)(5) 三條規則**同時變成 0 命中**，不必跟它們硬碰。
     (d) 「class 比 tag 語意明確、不受 heading 層級影響」這個反方論點在這裡不成立：
         POS 沒有全站頂欄（layouts/pos.blade.php 掛 app-without-header），
         §S-94 已記載「h1 是每一頁唯一說得出我在哪一頁的地方」，
         層級永遠是 1，不會有「哪天降成 h2」的情境。
   ★同批一起拿掉的還有 `pos-page-header-icon` / `ms-auto` / `pos-sidebar-restore-text`
     / 行內 `aria-expanded="false"`，全部零損失（逐項理由寫在 package.blade.php 的
     標題列註解裡）。其中 `aria-expanded` 值得單獨記一筆：markup 寫死 "false"
     在「側欄預設展開」的設定下是**錯的初值**，真正的 SSOT 是 _header 那支 IIFE 的
     apply()，它每次都對所有命中鈕重設這個屬性。

   ═══ 三、三個 class 現在全站零呼叫端，依禁刪守則原地保留 ═══
   本節寫入後實測（find + grep 遞迴，不用 shell glob）：
     `.pos-page-title`            blade/php/js 命中 0，只剩本檔 §S-94 / §S-95 的規則與註解
     `.pos-page-header-icon`      同上
     `.pos-sidebar-restore-text`  同上，而且本檔**從來沒有給過它任何規則**（只在 §S-94
                                  的合約表裡被提到）——它一直是純裝飾性的 class
   ⇒ §S-94 那幾條「兩種都認」的選擇器（`.pos .pos-page-header h1, .pos .pos-page-header
     .pos-page-title` 等）現在有一半是死碼。**不刪**：禁刪守則 ＋ 本檔只准追加。
     它們留著也不會害人（0 命中的選擇器不會誤傷任何元素），只是讀的時候要知道
     「右半邊那條今天起沒有呼叫端」。★日後真要新增標題列規則，**只寫 h1 那一種就好**。

   ═══ 四、§S-95 (5) 那套「收合態浮出列」特例：不需要了，而且會打架 ═══
   §S-95 章節註解「五」的擔憂完全正確 —— 叫回鈕 `.pos-sidebar-restore` 住在標題列裡面，
   `display:none` 會連子孫一起藏，加上收合狀態寫進 localStorage，重新整理也救不回來，
   是貨真價實的單向門。它的解法（收合態把標題列變成一條只剩叫回鈕的無底色浮出列）
   在「標題列被藏」的前提下是對的。
   ★★但那個前提今天沒了，於是它從「補丁」變成「新的不一致」：
     (甲) **單向門本來就不存在了**。標題列常駐顯示 ⇒ 叫回鈕的顯隱完全交還給 §S-94 自己
          那組規則（平常 display:none，`.pos.pos-sidebar-collapsed` 且 ≥992px 時
          inline-flex）。那組規則**不分頁面、不看 `.pos-page-title`**，5 頁一視同仁。
     (乙) **留著它會打架**：收合側欄的瞬間，package 的標題列會抽掉底色與下框線、
          藏掉圖示與標題、把高度從 64px 掉到約 44px；machine 卻文風不動。
          我們正在修的就是這種「兩頁不一樣」。
     (丙) 它還帶著一筆維護債：§S-95 註解「五」末交代「日後往標題列加真的動作鈕，
          必須回來把它加進 `> *:not(.pos-sidebar-restore)` 白名單，否則那顆鈕在收合態
          會靜默消失」。中和掉這條規則，這筆債一併消失。
   ⇒ 處置：(4) 與 (5) 全部中和。做法見下面三條規則。

   ═══ 五、三條覆蓋規則一律**不帶 `:has()`**，理由是拆掉一顆定時炸彈 ═══
   §S-95 章節註解「六」末寫著：「要擴大到六頁只需要把三條選擇器裡的
   `:has(> .pos-page-title)` 刪掉，其餘不動。」
   ★★★**這句話今天起是死條文**。當時它是「怎麼把功能推廣到全部頁面」的說明，
     今天照做的結果是**把 5 頁的標題列全部藏掉**。
     這正是 feedback_superseded_spec_clause_must_be_marked_dead 記的那一族坑：
     舊條文沒標作廢，後人照著寫出「完全符合舊規格的錯東西」。
   我不能改 §S-95 的字（只准追加），所以改用 CSS 把那條路堵死：
   本節三條規則**都不依賴 `.pos-page-title` 存在**，即使有人真的照那句話刪掉 `:has()`，
   把 §S-95 (4) 降成 (0,3,0) 的 `display:none`，也一樣打不過本節的 (0,5,0)。

   ═══ 六、特異性帳（沿用 §S-95「重複 class 墊高」，不賭檔尾順序）═══
     §S-95 (4)      `.pos .pos-page-header.pos-page-header:has(> .pos-page-title)`   (0,4,0)
     本節 (1)       `.pos .pos-page-header` x4                                        (0,5,0)  勝
     §S-95 (5) 第一條  加 `.pos.pos-sidebar-collapsed`                                (0,5,0)
     本節 (2)       同前綴 ＋ `.pos-page-header` x4                                   (0,6,0)  勝
     §S-95 (5) 第二條  再加 `> *:not(.pos-sidebar-restore)`                           (0,6,0)
     本節 (3)       同前綴 ＋ `> *:not(.pos-sidebar-restore)`                         (0,7,0)  勝
   ★三條都比對手高一分，與「誰在檔尾」無關 —— 本檔同時有其他工作階段在追加章節，
     §S-92 (3) 就是賭順序才被 §S-95 推翻的。
   -------------------------------------------------------------------------- */

/* (1) 標題列常駐顯示（推翻 §S-95 (4)）。
       ★只寫 display 一個屬性：其餘外觀（sticky / 64px / px-4 / 底色 / 下框線）
         §S-94 (2) 本來就在給，這裡不重複定義，避免兩個地方各存一份設計值。
       ★不帶 `:has()` 的理由見章節註解「五」。 */
.pos .pos-page-header.pos-page-header.pos-page-header.pos-page-header {
    display: flex;
}

@media (min-width: 992px) {
    /* (2) 收合態不再把標題列變成「浮出列」（推翻 §S-95 (5) 第一條）。
           §S-95 (5) 改了 6 個屬性，這裡逐一寫回 §S-94 (2) 的值。
           ★★這 6 行是 §S-94 (2) 的**回音**，不是新設計 —— 哪天改 §S-94 (2) 的
             標題列外觀，記得回來同步這裡，否則收合態會停在舊值。
             （會有這筆重複，是因為本檔沒有用 CSS cascade layers，
               中和一條高特異性規則只能逐屬性寫回去。）
           ★padding 直接取 §S-94 (2) 的 md 值 1.5rem：本區塊在 min-width 992px 內，
             恆大於 768px，不可能用到 1rem 那一階。
           ★justify-content 寫 flex-start 而不是省略：省略等於沿用 §S-95 (5) 的
             flex-end（它的特異性比 §S-94 (2) 高），標題會被推到右邊。 */
    .pos.pos-sidebar-collapsed .pos-page-header.pos-page-header.pos-page-header.pos-page-header {
        position: sticky;
        top: 0;
        z-index: 5;
        min-height: 64px;
        padding: 0 1.5rem;
        background: hsl(var(--background));
        border-bottom: 1px solid hsl(var(--border));
        justify-content: flex-start;
    }

    /* (3) 收合態不再藏標題與圖示（推翻 §S-95 (5) 第二條）。
           ★用 revert 而不是寫死 block/inline-flex：revert 是退回作者樣式之前的值
             （h1 -> block、i/svg -> inline）。標題列是 flex 容器，子項一律 blockify，
             所以退回哪一個值畫面都一樣 —— 用 revert 最誠實，也不會在這裡發明
             一個 §S-94 沒說過的 display 值。
           ★`:not()` 仍然放行 `.pos-sidebar-restore`：它的顯隱由 §S-94 自己那組
             規則管（平常 none、收合態 inline-flex），本條不介入。
           ★連帶：§S-95 註解「五」末那條「日後加動作鈕要記得加進 :not() 白名單」
             的維護債消失了 —— 收合態不再藏任何子元素。 */
    .pos.pos-sidebar-collapsed .pos-page-header.pos-page-header.pos-page-header.pos-page-header > *:not(.pos-sidebar-restore) {
        display: revert;
    }
}


/* --- S-105. 登入家族「觸控裝置」字級補位：寬度斷點蓋不到的 iOS 自動放大（2026-08-10）------

   ═══ 業主裁決（原話，不是我的轉述）═══
   三個選項攤給業主：①「高度 32、手機字級留 16px」②「字級也統一收小、接受 iOS 縮放」
   ③「手機版輸入框高度也放寬」。業主選 **①** ＝ 照 shadcn 官方 `input.tsx` 的
   `text-base md:text-sm`（手機 16px／桌機 14px）。

   ═══ ★★★ 同日並行事故：本節寫到一半，另一個 session 已經在源頭做完同一件事 ★★★
   我原本的施工前提是「`_auth_shell_css.blade.php` 是 `font-size:14px`、(0,4,0)、
   壓過 §S-89 的 (0,1,0)」—— 那個前提在我落地時**已經過期**。
   實測 CSSOM 時發現 inline `<style>` 冒出兩條我沒寫過的規則，`git diff` 內容確認
   （★認身分只能靠 diff 內容，不能靠 mtime）：另一個 session 針對同一道業主裁決，
   **直接改 `_auth_shell_css.blade.php` 的源頭**，做法是 mobile-first：
       `.login.login-v2 .login-content .form-control{ font-size:16px }`      (0,4,0) 基底
       `@media (min-width:768px){ 同選擇器{ font-size:14px } }`               (0,4,0) 桌機收回
   ★**那個做法是對的、而且比我原本的更正統**（在擁有該元件樣式的檔案裡改，
     特異度自然一致，不必打特異度戰爭）。所以本節**不與它競爭、不試圖蓋掉它**，
     原本那段「把 14px 墊贏」的章節文字已整段作廢重寫（就是你現在讀的這一段）。
   ★★順帶更正一條被兩邊都證實的舊錯：§S-103 附記 (4) 開的藥方
     「在 `@media (max-width:768px)` 內補 `.login.login-v2 .login-content .form-control{font-size:16px}`」
     是 **(0,4,0)**，與 inline `<style>` 同分，而 inline 排在
     `layouts/empty.blade.php` L15 `@yield('css')`、在 L14 `@include('includes._link')`
     （shadcn-theme.css 的 `<link>`）**之後** ⇒ 同分後載入者勝，照那個藥方寫**不會生效**。
     §S-103 附記 (4) 原文一字不改保留存查，結論以本節為準。

   ═══ 那本節還剩什麼職責？＝ 寬度斷點**在物理上**蓋不到的那一塊 ═══
   ★★ **iOS 自動放大的判準是「觸控裝置」，不是「螢幕寬度」** ——
      上述 mobile-first 做法用的是純寬度斷點（`min-width:768px` 以上一律 14px），
      所以**手機／平板橫放時會落回 14px，聚焦照樣被放大且縮放後不會還原**：
          iPhone 14 Pro Max 橫放 = **932px**   > 768 ⇒ 14px ⇒ 仍會放大
          iPad 橫放             = **1024px**  > 768 ⇒ 14px ⇒ 仍會放大
      這不是那個 session 做錯，是「用寬度代理觸控」這件事本身就有的破口 ——
      任何純 `min-width` / `max-width` 的寫法都補不到，必須換一個 media feature。
   ⇒ 本節只做一件事：`(pointer: coarse)` 的裝置在 ≤1024px 一律 16px。

   ═══ 斷點值：刻意與源頭那條「精確互補」，不製造第二種說法 ═══
   本節第一個分支寫 **`max-width: 767.98px`**，不是 §S-89 沿用的 `768px`：
     源頭那條的邊界是 `min-width: 768px`（＝ Tailwind `md`，shadcn `md:text-sm` 的真實值），
     若本節寫 `max-width: 768px`，在**視窗剛好 768px** 時兩條會同時命中而**結論相反**
     （源頭要 14、本節要 16），且本節 (0,5,0) 會贏 ⇒ 憑空多出一個與 shadcn 不一致的邊界。
     `767.98px` 讓兩者剛好接合、**任何寬度下都不會有兩種說法**（SSOT）。
   ★為什麼還留這個寬度分支（而不是只留 `pointer: coarse`）：它讓本節**自給自足** ——
     源頭那兩條已由另一個 session commit 在 `8ea43ed2`
     （「fix: 登入家族輸入框字級改響應式，修復從未生效的 iOS 防縮放」）。
     即便日後那個 commit 被 revert 或業主改主意，本節仍獨立保住「窄視窗 16px」
     這條業主裁決，不會靜默退版 —— 這正是我刻意不把它縮成「只有 coarse」的原因。
     兩條在重疊區宣告的是**同一個值 16px**，同時命中也不會有分歧
     （§S-103 二-(2) 對 `.modal` 與 `.app-content` 重疊已用過同一個立場）。

   ═══ 特異度怎麼贏的（逐項算，不憑感覺）═══
     對手：inline `<style>` 的 `@media (min-width:768px){ .login.login-v2 .login-content .form-control }`
           = .login + .login-v2 + .login-content + .form-control = **(0,4,0)**，且排在本檔之後
           ⇒ 932px / 1024px 的觸控裝置上，它會把字級壓回 14px。同分我會輸，必須比它高。
     本節：`.login.login-v2 .login-content .form-control:not(.form-control-lg)`
           = 上面四個 class + `:not()` 內的 `.form-control-lg`（`:not()` 會把括號內的特異度算進去）
           = **(0,5,0)** ⇒ 高一格，**與載入順序無關**，穩定取勝。
   ★選擇器形狀**刻意逐字沿用** §S-103 二-(4)(b) 對 `height` 用的那一條（同一個對手、同一個打法），
     不要在同一份檔案裡發明第二種墊法。
   ★同樣**刻意只多一格**：多墊會壓過 §S-4 (d) `.h-45px` 與 §S-103 二-(5) `.form-floating`。
   ★`.form-control-lg` 被排除**不是漏洞**：已 grep `resources/views/auth/` 全樹，
     `form-control-lg` / `form-select-lg` / `input-group-lg` **命中數皆為 0** ⇒ 目前是空集合。
     ★哪天登入頁真的出現 `-lg` 輸入框，正解是在**本節**補一條指名 `-lg`，
       不要拿掉這個 `:not()`（拿掉特異度掉回 (0,5,0)→(0,4,0)，整條規則跟著失效）。

   ═══ 只動字級，其餘一律不動 ═══
   ★**高度 32px 不可改變**（§S-101／§S-103 的「全站只有 32 這一階」是業主裁決）。
     本節**只宣告 `font-size`**，不碰 `height` / `min-height` / `padding` / `line-height`。
     字級 14 → 16 之後高度不變是算出來的、不是碰巧：
       `height:32px`（§S-103 二-(4)(b)）是**顯式固定值**，content box = 32 - border 2 = 30px；
       inline style 的 `line-height:1.5` 在 16px 下 = 24px，24 < 30 ⇒ 塞得進去、不撐高。
     實測落地後 390px 仍為 **32px**、1600px 維持 **14px / 32px** 原封不動。
   ★`.form-select` 一併納入（同 §S-103 二-(3) 的立場）：登入家族 `<select>` 目前 0 命中，
     這條是**防禦性**的 —— iOS 對 `<select>` 同樣會放大，「現在剛好是 0」不等於「以後也是 0」。
   ★`<textarea>` 不納入：登入家族沒有 textarea，且它不吃 `height`，
     收它要連算式一起處理，那是 §S-103 二-(8) 的職責（YAGNI）。

   ═══ ★待辦：這一節與源頭那兩條的分工要請業主／協調者追認 ═══
   目前是「源頭管寬度、本節管觸控」的分工，功能上互補且無矛盾（上面已逐一驗算）。
   但**同一個宣告有兩個檔案在管**這件事本身值得記一筆：若日後決定收斂成一處，
   正解是把 `(pointer: coarse)` 這個條件也搬進 `_auth_shell_css.blade.php`，
   然後整節刪掉 —— **不要**反過來把源頭那兩條搬進主題（那會退回打特異度戰爭的老路）。

   ═══ ★★★ 整合驗證（另一條線事後補測，跨兩處實作一起量，補在此處存查）═══
   `resources/views/auth/_auth_shell_css.blade.php`（commit `8ea43ed2`）那條線
   後續做了端到端實測，結論不是我測的、是對方量給的，記在這裡留給下一個看到
   「兩處都在管同一個字級、看起來像重複規則」而想清掉其中一條的人：
       手機 390px      → 16px ✅（源頭 mobile-first 基底生效，本節條件不命中也沒差）
       iPad 直向 768px  → 16px ✅（**本節勝出**：768px 落在源頭 `min-width:768px`
                                   的桌機分支，源頭這裡會判成桌機給 14px；
                                   但 iPad 直向是 `pointer: coarse` 且 ≤1024px，
                                   本節命中，(0,5,0) 壓過對手 (0,4,0)，結果仍是 16px）
       桌機 1440px      → 14px ✅（兩條件都不命中，回落源頭桌機值，符合業主裁決）
   ★★★**兩條缺一都有洞，尤其 iPad 直向 768px 這個邊界 —— 不要把其中一條當重複規則清掉**：
     只留源頭 ⇒ iPad 直向剛好落進它的桌機分支拿到 14px，但那仍是 iOS Safari 觸控輸入，
       14px 照樣觸發自動放大；
     只留本節 ⇒ 沒人把基底設回 16px（本節只在觸控裝置或窄視窗生效），
       語意也弱一截，失去對齊 shadcn `input.tsx` `text-base md:text-sm` 官方寫法的理由。
   ★判準再講白一次，這是本節從頭到尾的立論基礎：觸發 iOS 自動放大的真正條件是
     「**iOS Safari + 聚焦欄位的 font-size 小於 16px**」，視窗寬度只是**相關**
     （多數桌機視窗寬、多數手機視窗窄），不是**因果**——這正是本節不能只靠
     `min-width` / `max-width` 判斷、必須換用 `(pointer: coarse)` 這個 media feature
     直接問「這是不是觸控輸入裝置」的原因（同一件事上面「那本節還剩什麼職責」
     那段已用 932px／1024px 兩個實測反例算過一次，這裡是它的收尾確認）。
   ------------------------------------------------------------------------- */
@media (max-width: 767.98px), (pointer: coarse) and (max-width: 1024px) {
    .login.login-v2 .login-content .form-control:not(.form-control-lg),
    .login.login-v2 .login-content .form-select:not(.form-select-lg) {
        font-size: 16px;
    }
}


/* --- S-106. §S-102 的表格規格由「買家發票結果頁」推到**全站**（2026-08-10）------------
   業主裁決（原話）：「1.全站」、「一切都以 shadcn 為主 包括表格資料」。
   表頭字色也是業主當場裁的：他看到灰色表頭問「這什鬼為什麼要變灰」⇒ 要黑，
   ＝ shadcn v4 `TableHead` 的 `text-foreground`（不是官網舊 registry 的 `text-muted-foreground`）。
   ─────────────────────────────────────────────────────────────────────────────
   ★★★ 本節不發明任何值：每一個數字都是 §S-102（本檔 L21270 起）已經逐條對過原始碼的那組，
        本節只做一件事 —— **把作用域從 `.container-wide .card` 換成全站**。
        權威來源與逐字 className 全部寫在 §S-102 的章節註解裡，不在這裡重抄
        （抄一次就多一份會走鐘的複本，那正是 §S-80 在治的病）。
        ★本輪重新核對過的三個數字（不是照抄前一輪的草稿）：
          · Tailwind v4 `--spacing:.25rem` ⇒ h-10 = 2.5rem = 40px、p-2/px-2 = .5rem = 8px
          · `--text-sm:.875rem` ＋ `--text-sm--line-height: calc(1.25/0.875)` ⇒ 14px 字／20px 行高
          · 列高 = 8(上) + 20(行高) + 8(下) + 1(下框線) = **37**（實測值就是 37.00，見文末）

   ═══ 一、業主已知情同意的連帶結果：後台列高 41 → 37 ═══
   實測（https://test.2025-seller.mtest/pos/package，innerWidth=1440，`.app-content` 外殼內注入合成表格）：
       th  padding 9.6px 12px → 0px 8px；height 40.69 → 40.00
       td  padding 9.6px 12px → 8px；**列高 41.19 → 37.00**
       行高 21 → 20（`.6rem/.75rem` 與 body 的 1.5 行高一起退場）
       th 字色 rgb(115,115,115) → rgb(0,0,0)；letter-spacing .14px → normal
       tfoot（全站唯一沒人管的一段）：border-top 0 → 1px、底色透明 → rgba(245,245,245,.5)、
             字重 400 → 500、**字級 16px → 14px**、末列 border-bottom 1px → 0
   ⇒ 41→37 不是副作用，是 `p-2` 這個官方值本身；業主在裁決時已被告知。

   ═══ 二、§S-102 有三項**刻意不推全站**（查證過，不是漏掉）═══

   ── ① `.table-responsive { position: relative }`（§S-102 (1)）→ **不推** ──
      本檔 §手機390px溢出治本（L6873 附近）有一條 2026-07-27 全站 368 頁實測後寫下的既有裁示：
        「（不改成 `.table-responsive{position:relative}` 是因為那會連帶把表格裡
          本來能溢出顯示的 Bootstrap 下拉選單一起裁掉。）」
      買家發票結果頁沒有任何下拉選單，所以那一項在 §S-102 的作用域是安全的；全站不是。
      ⇒ 維持 §S-102 的 `.container-wide .card` 作用域，本節一個字都不碰它。
      ★實測佐證：本節的 CSS 上到 `/pos/package` 後，`.table-responsive` 的 position 仍是 `static`（無變動）。

   ── ② `<th> { text-align: left }`（§S-102 (3) 的一行）→ **不宣告** ──
      官方 TableHead 是 `text-left`，但本專案 **81 張表**在 `<table>` 元素上掛了 `.text-center`
      （`grep -rhoE '<table[^>]*class="[^"]*"' resources/views/ | grep -c text-center` = 81）。
      Bootstrap 的 `th` 是 `text-align: inherit` ⇒ 那 81 張表的表頭**現在是靠繼承置中的**。
      一旦在 th 上「直接宣告」text-align，繼承就不參賽，那 81 張會變成「表頭靠左、內容置中」。
      ★★**不宣告反而就是官方結果**：沒掛 `.text-center` 的表，th 靠繼承拿到 `start` ＝ 靠左。
      ★實測佐證：合成的 `<table class="table text-center">`，套上本節後 th 的 textAlign 仍是 `center`（零變動）。

   ── ③ `white-space: nowrap`（§S-102 (3)(4)(6)）→ **不宣告** ──
      §31（L2586 起）已經給了 `.table th/.table thead th` 與 `.table td` 的 nowrap，
      而且**附了五個逃生門**：`.table td.text-wrap` / `.table td .text-wrap` / `.table td.wrap` /
      `.table td p` / `.table td .quill-content` → `white-space: normal`。
      view 端實際用掉的是 `.text-wrap` 23 處 ＋ `.wrap` 121 處 ＝ **144 處長文欄**。
      本節若在 (0,4,3) 直接宣告一次 nowrap，那 144 處會全部退回不換行（逃生門全是 (0,1,2)/(0,1,3)）。
      ⇒ 現況已經對了，再宣告一次只會把逃生門踩死。
      ★實測佐證：合成的 `<td class="text-wrap">` 套上本節後 whiteSpace 仍是 `normal`（零變動）。

   ═══ 三、排除 `.fb-form-table`（表單型表格）═══
      `.fb-form-table` 是 §S-80 明訂的「全站唯一表單型表格寫法」，view 端 **50 處**
      （`<table class="table fb-form-table">` 27、`…table-hover align-middle fw-bold fb-form-table` 11、
        `…table-borderless align-middle mb-0 fb-form-table` 9、同前＋inline table-layout 3）
      —— **每一處都同時掛著 `.table`**，所以不排除就一定會被本節掃到。
      它有自己的一整套內距／字級 SSOT（§S-24 L9116、§S-58② L12559、§S-63、§S-80 L15734），
      那是「label 左／控制項右」的表單語彙，不是資料表格語彙。
      ★實測佐證：合成的 `.fb-form-table` 在本節前後 **20 個屬性全等、零差異**。

   ═══ 四、特異度：為什麼是 `.table.table.table:not(.fb-form-table)` ═══
      = 4 個類別 ⇒ **(0,4,x)**（`:not()` 的分數取其最具體引數 ＝ 1 個類別）。
      對所有競爭者嚴格勝出，**不靠檔案順序**（同分靠順序決勝是脆弱的，§S-102 (4) 已吃過這個教訓）：
        · `.app-content .table.table-bordered > thead > tr > th`        (0,3,3)  ← 全檔最強的競爭者
        · `.app-content .table.table-bordered > tbody > tr > td`        (0,3,3)
        · `.container-wide .card .table > thead > tr > th`（§S-102）    (0,3,3)  ← 見「六、」
        · `.app-content .table > thead > tr > th`（§7b L986／§補位7 L6008）(0,2,3)
        · `.app-content .table:not(.table-bordered) > tbody > tr > td`  (0,2,3)
        · `.app-content .table > tbody > tr > td{font-size:13px}`       (0,2,3)  ← 見「五、」
        · `.table > thead > tr > th`／`.table > tbody > tr > td`（§S-88 無前綴複本）(0,1,3)
        · Bootstrap `.table > :not(caption) > * > *{padding:.5rem}`     (0,1,1)
      ★競爭者全部**不帶 !important**（唯一帶 !important 的是背景色，本節刻意不宣告背景，見下），
        所以 (0,4,x) 不加 !important 就通吃。
      ★★**背景一律不宣告**：官方 TableHead 沒有背景 class（靠 border-b 分隔，不是灰塊），
        而現況已經是透明（`.app-content .table > thead > tr > th{background:transparent!important}`、
        §S-88 的無前綴複本同樣帶 !important）—— 要的結果已經對了。
        ★也**不要**去拿掉那些 !important：本檔基底 `.table > thead th{background:--muted!important}`(0,1,2)
          還在，拿掉就會掉回灰底表頭。

   ═══ 五、td 字級 13px → 14px：這一項是「必要條件」不是加碼 ═══
      `.app-content .table > tbody > tr > td { font-size: 13px }`（§補位7 L6033）今天已是死碼
      （§S-64 的同選擇器 14px 排在後面贏了它，該處註解有完整實測），但 `.app-content` 外殼下
      **確實有 td 層級的字級宣告在跑**。行高是 unitless 的 `calc(1.25/0.875)` ⇒ 由字級決定像素：
        14px × 1.42857 = 20px ⇒ 列高 8+20+8+1 = **37**
        13px × 1.42857 = 18.57px ⇒ 列高 35.57 ⇒ **拿不到業主要的 37**
      ⇒ 本節在 th/td 上**直接宣告** `font-size:.875rem` 與 `line-height`，不靠從 `<table>` 繼承。
      ★這也讓 tfoot 的 16px（Bootstrap 預設，全站唯一沒人管的一段）一起收進 14px。

   ═══ 六、對買家發票結果頁（§S-102 的地盤）是零影響 ═══
      本節 (0,4,x) 確實會贏過 §S-102 的 (0,3,x)，但**兩邊的值逐字相同**，所以贏了也沒有差別。
      ★這不是推論，是實測：在 https://inv.2025-seller.mtest/result/… 掃 body 底下 **1672 個元素**、
        每個元素 21 個屬性＋boundingRect 高度，開關本節 CSS 前後 —— **差異 0 筆，連跑兩輪都是 0**。
        （★守門誘餌：同一支偵測器注入 `.table td{padding-left:33px!important}` 報出 16 筆變動、
          移除後殘留 0 ⇒ 那個「0」是偵測器睜著眼睛量到的。
          ★另做 null control：不改任何 CSS、只 toggle 一個空 `<style>`，差異也是 0 ⇒ 偵測器不會亂報。）
      ⇒ §S-102 一行都不用改、也不會變成死碼（它的 `position:relative` 與 `white-space` 仍是它獨有的）。

   ═══ 七、已知的殘留不一致（不修，記在這裡）═══
      · `<tfoot>` 裡用 `<th>` 而不是 `<td>` 的表：§23 L2158 `.table tfoot th{background:--card!important}`
        會蓋掉本節給 `<tfoot>` 的 `bg-muted/50`。本節**不去打它** ——
        那是別人的區段、而且要壓過它必須也用 !important，代價大於收益。
        買家發票頁的 tfoot 用的是 `<td>`，不受影響；後台若有 tfoot th 的表，底色會是卡片白。
      · `.table-bordered`（請假日曆 hr/leave/calendar、月排班 hr/schedule/monthly）的**直線保留**，
        本節不宣告任何 border ⇒ 實測 borderLeftWidth/borderRightWidth 前後都是 1px、零變動。
        但它們的**內距**會跟著全站收（12px→8px、9.6px→8px/0），這是「全站」裁決的一部分。 */

/* ── (1) `<table>` = `w-full caption-bottom text-sm` 的 text-sm 那半 ── */
.table.table.table:not(.fb-form-table) {
    font-size: .875rem;                  /* --text-sm */
    line-height: calc(1.25 / 0.875);     /* --text-sm--line-height ⇒ 14px 字對應 20px */
}

/* ── (2) `<th>` = TableHead `h-10 px-2 align-middle font-medium text-foreground` ──
   ★`text-left` 與 `whitespace-nowrap` 刻意不宣告，理由見章節註解「二、②③」。
   ★`text-foreground`（黑）是業主當場裁的：「這什鬼為什麼要變灰」。
     v4 `table.tsx` 的 TableHead 是 `text-foreground`；官網舊 registry 是 `text-muted-foreground`
     （＝本專案改動前的現況 rgb(115,115,115)）。對比從 4.6:1 提升到 21:1，無 WCAG 風險。 */
.table.table.table:not(.fb-form-table) > thead > tr > th {
    height: 2.5rem;                      /* h-10 = --spacing*10 = 40px */
    padding: 0 .5rem;                    /* px-2；官方沒有 py，靠 h-10 定高 */
    vertical-align: middle;              /* align-middle */
    font-weight: 500;                    /* font-medium */
    font-size: .875rem;                  /* text-sm；直接鍵在 th 上，不靠繼承（理由見「五、」） */
    line-height: calc(1.25 / 0.875);
    letter-spacing: normal;              /* 官方無 tracking-*；歸零 §補位7 的 .01em */
    color: hsl(var(--foreground));       /* text-foreground —— 業主裁決「要黑」 */
}

/* ── (3) `<td>` = TableCell `p-2 align-middle` ──
   ★`whitespace-nowrap` 刻意不宣告（§31 已給、且有 144 處逃生門），理由見章節註解「二、③」。
   ★列高 = 8 + 20 + 8 + 1(下框線) = 37（實測 37.00）。 */
.table.table.table:not(.fb-form-table) > tbody > tr > td {
    padding: .5rem;                      /* p-2 */
    vertical-align: middle;              /* align-middle */
    font-size: .875rem;                  /* text-sm（必要：贏過 td 層級的 13px，否則行高變 18.57、列高 35.57） */
    line-height: calc(1.25 / 0.875);
}

/* ── (4) TableRow = `border-b transition-colors hover:bg-muted/50` ──
   · border-b：本檔的線畫在 th/td 上，`border-collapse:collapse` 之下與官方畫在 tr 上**渲染完全相同**⇒不改。
   · 末列去線：`.app-content .table > tbody > tr:last-child > td{border-bottom:0}`(0,2,3)
     ＝ 官方 TableBody 的 `[&_tr:last-child]:border-0` ⇒ 已符合，不改。
   · hover：官方 TableRow 是**無條件**帶 hover 的（不需要 `.table-hover`）。這一條照推。
     ★td 的背景是 `.table > :not(caption) > * > *{background-color:transparent}`(0,1,1)、非 !important，
       所以底色鍵在 tr 上會透出來 —— 對**沒掛** `.table-hover` 的表這樣就夠了。
     ★★但**掛了** `.table-hover` 的表不夠：§23 L2174 在**儲存格**上帶 !important，會蓋在 tr 之上。
       那一條由 §S-107 處理（實測證據見該節）。
   · transition-colors 展開逐字取自 tailwindcss/src/utilities.ts（漸層那三個自訂屬性本專案不用，略）。 */
.table.table.table:not(.fb-form-table) > tbody > tr {
    transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke;
    transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);   /* --default-transition-timing-function */
    transition-duration: 150ms;                                 /* --default-transition-duration */
}
.table.table.table:not(.fb-form-table) > tbody > tr:hover { background-color: hsl(var(--muted) / .5); }   /* hover:bg-muted/50 */

/* ── (5) `<tfoot>` = TableFooter `border-t bg-muted/50 font-medium [&>tr]:last:border-b-0` ──
   ★這是全站表格**唯一沒有任何規則在管**的一段（實測純 Bootstrap 預設：
     背景透明、font-weight 400、border-top 0、**font-size 16px**、末列還有下框線）。 */
.table.table.table:not(.fb-form-table) > tfoot {
    border-top: 1px solid hsl(var(--border));   /* border-t */
    background-color: hsl(var(--muted) / .5);   /* bg-muted/50 */
    font-weight: 500;                           /* font-medium */
}
.table.table.table:not(.fb-form-table) > tfoot > tr > td {
    padding: .5rem;                             /* 官方 TableFooter 內用的就是 TableCell */
    vertical-align: middle;
    font-size: .875rem;                         /* ★必要：Bootstrap 預設讓 tfoot td 是 16px（實測） */
    line-height: calc(1.25 / 0.875);
}
.table.table.table:not(.fb-form-table) > tfoot > tr:last-child > td { border-bottom-width: 0; }   /* [&>tr]:last:border-b-0 */

/* ── (6) TableCaption `mt-4 text-sm text-muted-foreground` ──
   全站目前 0 命中，先備著；未來加了 `<caption>` 才有作用，現在 0 風險。 */
.table.table.table:not(.fb-form-table) > caption {
    margin-top: 1rem;                    /* mt-4 */
    font-size: .875rem;                  /* text-sm */
    color: hsl(var(--muted-foreground)); /* text-muted-foreground */
    caption-side: bottom;                /* 官方 Table 的 caption-bottom */
}


/* --- S-107. `.table-hover` 的 hover 由 `--accent` 100% 收成官方 `bg-muted/50`（2026-08-10）---
   業主裁決（原話）：「2.收成官方」。
   ─────────────────────────────────────────────────────────────────────────────
   ★★★【本節覆蓋的是誰、原值是什麼、為什麼不去改它】
     被覆蓋者 ＝ **§23 L2174**（本檔「表格：shadcn（去外框、橫線分隔、hover accent）」段）：
        `.table-hover > tbody > tr:hover > * {`
        `    background-color: hsl(var(--accent)) !important;`
        `    color: hsl(var(--accent-foreground));`
        `}`
     **原值一字不動地記在上面存查**。§23 是別人的區段，本節**不改它一行**，只在檔尾覆蓋 ——
     這是本專案既有規矩（禁止刪除／只新增），也讓「改動前長什麼樣」永遠查得到。
     ★只覆蓋 `background-color`，**不碰 `color`**：官方 TableRow 的 hover 不改文字色，
       而且現況的文字色已經由 §S-88 L17033（同 (0,2,2)、排在 §23 之後）決定成 `--foreground`，
       §23 那半早已是死碼 —— 再宣告一次只會多一個要維護的競爭者。

   ═══ 為什麼非用 `!important` 不可（實測，不是靠特異度推算）═══
     場地 https://test.2025-seller.mtest/pos/package（innerWidth=1440，`.app-content` 外殼），
     注入 `<table class="table table-hover">`，用 puppeteer **真的把游標移上去**再讀 computed：
       A 改動前（hover 中）                     td 背景 = rgb(245, 245, 245)      ← §23 的 --accent，100% 不透明
       B 本節規則但**拿掉 !important**（重新 hover）td 背景 = rgb(245, 245, 245)   ← **沒贏**，§23 的 !important 照樣通吃
       C 本節規則**帶 !important**（重新 hover）  td 背景 = rgba(245, 245, 245, 0.5) ← ✅ bg-muted/50
     ⇒ B 是 C 的正對照：只做 C 的話，「有效」與「本來就長這樣」分不出來。
     ★特異度另計：本節 (0,5,2)〔`.table-hover`×3 ＋ `:not(.fb-form-table)` ＋ `:hover` ＝ 5 類；tbody+tr ＝ 2 型〕
       ＞ §23 的 (0,2,2)，但**特異度高沒有用**——不帶 !important 的規則永遠輸給帶 !important 的規則。
       這就是 B 量到的東西。

   ═══ 亮色下 `--accent` 與 `--muted` 都是 245,245,245 ⇒ **看得出來的差別只有 alpha** ═══
     改動前是不透明 #F5F5F5，改動後是 50% 的 #F5F5F5（白底上 ≈ #FAFAFA）。
     ★官方 shadcn 就是這個淡度，**不是沒生效**。日後有人回報「hover 看不太出來」，
       請先量 alpha 再說，不要以為規則沒套上。

   ═══ 為什麼要排除 `.fb-form-table` ═══
     §S-24 L9133 有 `.app-content table.fb-form-table > tbody > tr:hover > *{background:transparent}`（(0,3,3)、無 !important），
     意圖是「表單型表格不要有 hover 反白」。本節若不排除，會用 !important 把那個意圖直接輾過去。
     ⇒ 排除 ＝ 讓表單表**維持現況**（現況是什麼由 §S-24／§23 決定，本節不介入）。
     ★實測：合成的 `.fb-form-table` 在本節前後 20 個屬性全等。

   ═══ 與 §S-106 (4) 的分工 ═══
     §S-106 (4) 把 hover 鍵在 `<tr>` 上（官方 TableRow 是**無條件** hover，不需要 `.table-hover`）。
     對**沒掛** `.table-hover` 的表，tr 的底色會從透明的 td 透出來，那樣就夠了。
     對**掛了** `.table-hover` 的表，§23 在儲存格上蓋了一層不透明底 ⇒ 必須本節這條才看得到 muted/50。
     ★實測同時成立：hover 時 `<tr>` 與 `<td>` 的 backgroundColor 都是 rgba(245,245,245,0.5)。 */
.table-hover.table-hover.table-hover:not(.fb-form-table) > tbody > tr:hover > * {
    background-color: hsl(var(--muted) / .5) !important;   /* hover:bg-muted/50 —— 覆蓋 §23 L2174 的 hsl(var(--accent)) !important */
}


/* --- S-108. 陰影全部換成官方值（2026-08-10）--------------------------------------
   業主裁決（原話）：「3.收成官方」「4.都換成官方」。
   ─────────────────────────────────────────────────────────────────────────────
   ★★★ 權威來源（本節兩個值都抓自原始碼，沒有一個是憑印象填的）：
     ① Tailwind v4 token 定義
        https://raw.githubusercontent.com/tailwindlabs/tailwindcss/main/packages/tailwindcss/theme.css
          L407  `--shadow-xs: 0 1px 2px 0 rgb(0 0 0 / 0.05);`
          L408  `--shadow-sm: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);`
          L409  `--shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);`
          L505（標著「Deprecated」的 `@theme default inline reference` 區塊內。
                ★該註解在原始碼裡是寫成 CSS 註解的，這裡**刻意不照抄**：
                  註解裡再出現一次註解結束符會把本段整個截斷，後面的規則就變成裸文字。）
                `--shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);`
     ② shadcn v4 Card 逐字 className
        https://raw.githubusercontent.com/shadcn-ui/ui/main/apps/v4/registry/new-york-v4/ui/card.tsx
          `"flex flex-col gap-6 rounded-xl border bg-card py-6 text-card-foreground shadow-sm"`
        ⇒ **Card = `shadow-sm`**，不是 `shadow-xs`。

   ═══ ★★ `.shadow` 該對到哪一階：**答案在官方原始碼裡，不需要我猜** ═══
     v4 把陰影階梯整個往上挪了一格（v3 的 `shadow-sm` → v4 `shadow-xs`；v3 的 `shadow` → v4 `shadow-sm`），
     所以「v4 沒有裸 `shadow`」是常見的誤解 —— 實際上 **v4 的 `theme.css` 仍然出貨 `--shadow`**，
     放在標著「Deprecated」的區塊，值 **與 `--shadow-sm` 逐字相同**（見上方 L505 vs L408）。
     ⇒ 官方自己就把 `shadow` 與 `shadow-sm` 定義成同一個值。
     ⇒ §20 把 `.shadow, .shadow-sm` 寫在同一條規則裡，**本來就是官方的對應關係**，
       本節照樣維持一條，只把值換掉。**不需要業主拍板，也不需要拆成兩階。**
     ★Bootstrap 原生的 `.shadow`（`0 .5rem 1rem rgba(0,0,0,.15)`）確實比 `.shadow-sm` 重，
       但本專案從 §20 落地那天起就已經把兩者收成同值了 ——
       本節**沒有改變「兩者同值」這件事**，只改變那個共用值。
     ★blast radius 實查：view 端裸 `.shadow` 只有 **1 處**
       （`Order/_partials/shipment_group_js_shared.blade.php:92` 的載入遮罩方塊；
        另一個 grep 命中是 `includes/_nav.blade.php` 的 `with-shadow`，那不是 `.shadow`）；
       `.shadow-sm` 19 處、`.shadow-none` 9 處、`.shadow-lg` 2 處（見文末待辦）。

   ═══ (1) `.card` ═══
     ★★★【本節覆蓋的是誰、原值是什麼、為什麼不去改它】
       被覆蓋者 ＝ **§5 L675**（`§5 Card / Panel + #content 連結色`）的
          `.card { … box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.04); }`
       **原值一字不動地記在上面存查**。§5 是別人的區段，本節不改它一行。
       原值 `0 1px 2px 0 /.04` ＝ v4 `--shadow-xs` 的幾何（`0 1px 2px 0`）配上更淡的 alpha（.04 vs .05），
       也就是**比官方 Card 淡了整整一階**。實測改動前 computed ＝ `rgba(0,0,0,0.04) 0px 1px 2px 0px`。
     ★全檔只有這一處在給 `.card` 陰影（`grep -n 'box-shadow' | grep card` 確認；
       其餘 `.card` 相關的 box-shadow 都是 `0 0 0 2px hsl(var(--card))` 這種「描邊」用途，不是投影）。
     ★特異度 (0,2,0) 是刻意的最小增量：只要贏過 §5 的 (0,1,0)，不去踩 POS 那條
       `.pos .pos-step-card.pos-step-card{…!important}`(0,3,0＋!important，§S-96/§S-97)。

   ═══ (2) `.shadow` / `.shadow-sm` ═══
     ★★★【本節覆蓋的是誰、原值是什麼、為什麼不去改它】
       被覆蓋者 ＝ **§20 L1771**（`§20 雜項對齊`）的
          `.shadow, .shadow-sm { box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.08), 0 1px 2px -1px rgb(0 0 0 / 0.06) !important; }`
       **原值一字不動地記在上面存查**。§20 是別人的區段，本節不改它一行。
       原值的**幾何已經是官方 `--shadow-sm`**（`0 1px 3px 0` ＋ `0 1px 2px -1px`），
       只有 alpha 被調淡成 .08/.06（官方兩層都是 .1）。
     ★★**這一條非改不可，否則會出現「加了 `shadow-sm` 反而更淡」的怪事**：
       (1) 把 `.card` 拉到官方的 .1/.1 之後，若 §20 還停在 .08/.06，
       一張 `<div class="card shadow-sm">` 會因為 §20 帶 !important 而**比純 `.card` 還淡**。
       改完之後兩者同值 ⇒ 加不加 `shadow-sm` 都一樣，不會互相打架。
     ★必須同樣帶 `!important`：§20 那條帶 !important，不帶就贏不了（與 §S-107 同一個道理）。

   ═══ 實測（https://test.2025-seller.mtest/pos/package，innerWidth=1440）═══
       .card      rgba(0,0,0,0.04) 0px 1px 2px 0px
                  → rgba(0,0,0,0.1) 0px 1px 3px 0px, rgba(0,0,0,0.1) 0px 1px 2px -1px   ✅
       .shadow    rgba(0,0,0,0.08) 0px 1px 3px 0px, rgba(0,0,0,0.06) 0px 1px 2px -1px
                  → rgba(0,0,0,0.1) 0px 1px 3px 0px, rgba(0,0,0,0.1) 0px 1px 2px -1px   ✅
       .shadow-sm 同上                                                                    ✅
     ★同一輪掃了 `.app-content` 底下 145 個元素 × 20 個屬性，**除了上面三個目標之外 boxShadow 零變動**
       （守門誘餌：注入 `.app-content .btn{letter-spacing:9px!important}` 等兩條假規則，
         偵測器報出 31 筆、移除後殘留 0 ⇒ 那個「零變動」不是假綠燈）。

   ═══ ★ 待辦／不在本節處理（列出來，不要當作漏掉）═══
     · **不加** `.card .card { box-shadow: none }`（卡中卡）：上一輪兩種靜態掃描得到
       12 處 vs 0 處**互相矛盾**（靜態掃描看不到跨 `@include` 的巢狀）⇒ **還不確定它存不存在**。
       要處理請先在瀏覽器實測 `document.querySelectorAll('.card .card').length`，不要靠 grep。
     · `.shadow-lg`（view 端 2 處實際在用）目前沒有任何規則接管 ⇒ 仍是 Bootstrap 原生
       `0 1rem 3rem rgba(0,0,0,.175)`，與官方 `--shadow-lg`
       (`0 10px 15px -3px rgb(0 0 0/.1), 0 4px 6px -4px rgb(0 0 0/.1)`) 不同。
       業主的「都換成官方」當時談的是 `.card` 與 §20 那條，`.shadow-lg` 沒被端上桌 ⇒ **本節不動，請上游決定**。
     · 本檔有 4 處把 `0 1px 2px 0 rgb(0 0 0 / .05)` 註解成「shadow-sm」（L1250、L9470、
       §S-92 L20005 引用、§S-96 L20028）—— 那個值在 **v4 叫 `shadow-xs`**，註解是 v3 的舊名。
       值本身沒錯（它們是刻意要最淡那一階），**只是名字過時**；而且 §S-92/§S-96/§S-97 是
       今天另一條線正在動的 POS 區段 ⇒ **本節不碰**，請上游協調後再統一改名。 */

/* ── (1) `.card` = shadcn v4 Card 的 `shadow-sm` ── */
.card.card { box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1); }

/* ── (2) `.shadow` / `.shadow-sm` 工具類 = 同一個官方值（v4 的 --shadow 與 --shadow-sm 逐字相同）── */
.shadow.shadow,
.shadow-sm.shadow-sm { box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1) !important; }


/* --- S-109. 側欄子選單項高度 32px → 官方 h-7 28px（2026-08-10）--------------------
 *
 * 業主原話（在 /order-management/status/returned）：
 *   「左邊選單 子選單的上下距離好像過大」
 *
 * ═══ 病根：規格早就寫在本檔註解裡，但從來沒有被實作 ═══
 *   本檔 L6988「§側欄：對齊 shadcn/ui Sidebar 元件規格（2026-07-28）」的語彙表自己寫著：
 *       SidebarMenuButton     h-8 rounded-md px-2 gap-2
 *       SidebarMenuSubButton  h-7 text-sm          ← 子項官方就是 28px
 *   同一段的「落差實測」也記著：「子項高 32px（規格 h-7=28）」。
 *   ★但那一輪只實作了主項：L1937 `#sidebar .menu-link` 的 `height: 32px`。
 *     子項的 `.menu-link` 是它的**後代**，全檔沒有任何規則覆蓋子項高度
 *     → 子項一路繼承主項的 h-8=32px，兩層同高，官方的 h-7 從未落地。
 *   ★這與 L1937 註解自己描述的坑同一族：「下面 §側欄 那條雖然寫了正確規格，
 *     卻因為 ID 特異性被壓掉 —— 等於規格從未生效」。那次修好了主項、漏掉子項，
 *     於是「規格寫在註解裡但沒人實作」這件事又多活了 13 天。
 *     ★教訓：註解裡的規格表不是實作，改完要逐條回頭量渲染值。
 *
 * ═══ 為什麼業主的感受是「上下距離過大」═══
 *   子項字級 13px、line-height 20px，盒子卻被撐到 32px ⇒ 上下各多 6px 空白；
 *   加上繼承來的 margin-bottom .25rem(4px)，子項中心距 36px，
 *   **與主項的 36px 完全相同** ⇒ 垂直節奏上層級零表達，看起來就是一整片鬆散等距清單。
 *   官方 h-7 把子項收成 32px 中心距，比主項密一階，從屬關係才看得出來
 *   （配合既有的 border-left 引導線與 14px 縮排）。
 *
 * ═══ 特異度：實測不需要 !important ═══
 *   `#sidebar .menu-submenu .menu-link` = (1,2,0) ＞ L1937 `#sidebar .menu-link` = (1,1,0)。
 *   ★正對照實測：不帶 !important 量到 28px，帶 !important 也是 28px ⇒ 本來就贏，不加。
 *     （這與 §S-107 / §S-108 不同 —— 那兩處的對手自己帶 !important 才非加不可。
 *       本檔 1.5MB / 168 分節，特異度紙上作業已不可靠，所以兩種都量過才下結論。）
 *
 * ═══ 實測（https://test.2025-seller.mtest/order-management/status/returned
 *          「訂單管理」展開態 17 個子項，innerWidth=1440）═══
 *                          改前            改後
 *     主選單項高            32px      →     32px    ✅ 未被波及（本節選擇器不含主項）
 *     子選單項高            32px      →     28px    ✅ h-7
 *     子項兩項中心距        36px      →     32px    ✅ 28 + margin-bottom 4
 *     子項字級/行高      13px/20px    →  13px/20px  ✅ 未動；scrollHeight===clientHeight===28，無裁切
 *   ★padding 維持既有的 6px 8px（上下來自 L1982、左右來自 L7248）：
 *     28px 盒子扣掉上下 12px 後內容盒只有 16px，小於 20px 行高，但 `align-items:center`
 *     把 20px 的行置中、溢出的 2px 落在 padding 區內，仍在 border box(28px) 之中
 *     ⇒ L7258 的 `overflow:hidden` 裁切在 28px 邊界，切不到字（已實測，見上）。
 *   ★★只動 height 一個屬性。padding/字級/顏色都不是業主抱怨的東西，動了只會擴大回歸面。
 *
 * ═══ ★ 一併查證過、但**刻意不動**的三件事（列出來，不要當成漏掉）═══
 *   (1) 「最後一項下方會不會多 4px」——**實測沒有**。
 *       `.menu-submenu` 是 display:block 且**沒有下內距／下邊框**，最後一項的
 *       margin-bottom 4px 直接**外邊距摺疊**穿出容器，與容器自己的 margin-bottom 2px
 *       取 max ⇒ 量到「最後一項下緣 → 容器下緣 = 0px」、「最後一項 → 下一個主項 = 4px」。
 *       ★所以**不需要** 對最後一項下 `margin-bottom: 0` ——
 *         加了反而把那個間隙壓成 2px，而官方是 py-0.5(2) + SidebarMenu gap-1(4) = **6px**，
 *         現況 4px 已經比官方**緊**，再收只會更偏離。
 *       ★這條之所以值得寫下來：直覺上「每項都有 margin-bottom ⇒ 最後一項多出來」是對的，
 *         **但只在 flex/grid 或容器有下內距時成立**；這裡是 block 且無下內距，摺疊吃掉了它。
 *         推導與實測不一致時以實測為準。
 *   (2) 容器幾何 `margin: 2px 0 2px 14px` / `padding-left: 10px` / `border-left: 1px`
 *       （L7275 §「深層巢狀」那條）：左側 14+10 與官方 `mx-3.5 px-2.5` 逐字相符 ✅。
 *       ⚠️ 但**右側**是 0（官方 mx-3.5 / px-2.5 兩側都給）、**上下內距**是 0（官方 py-0.5=2px）。
 *       右側收 0 是刻意的：側欄只有 220px 寬，右邊再吃掉 24px 會讓深層項目被擠爆
 *       （L7271 的註解記著這件事）⇒ **不改**。上下 py-0.5 的 2px 同理不在業主抱怨範圍，
 *       且它會讓「上下距離」變**更大**，與本節目的相反 ⇒ **不改**。
 *   (3) 子項字級 13px：官方 SidebarMenuSubButton 是 `text-sm`(14px)，其 `size="sm"` 變體才是
 *       `text-xs`(12px) ⇒ **13px 兩者皆非**。但那 13px 是 L1982 刻意訂的層次
 *       （主項 14px 滿版 / 子項 13px + alpha .9 / 作用中另有底色，三階分得開），
 *       且 2026-07-30 那輪為了 AA 對比才把 alpha 由 .75 調到 .9 —— 動字級會連帶重驗對比。
 *       ⇒ **本節不碰，留給上游決定**。
 *
 * ★只在檔尾追加，未修改任何既有規則。
 * ------------------------------------------------------------------------------ */

#sidebar .menu-submenu .menu-link {
    height: 28px;   /* SidebarMenuSubButton h-7（原本繼承 L1937 主項的 h-8=32px） */
}

/* --- S-110. 側欄頁腳收合鈕拉齊 32px；標記 §S-94 一條已零命中的死選擇器（2026-08-10）---
 *
 * 業主裁決：
 *   1. `.pos-sidebar-toggle` 從 36px 拉齊成 32px，與旁邊「全部登出」「暫停」同高。
 *   2. 總原則：「全站就是統一 SSOT 對齊 shadcn 就對了」。
 *
 * ═══ 現況量測（https://test.2025-seller.mtest/pos/package）═══
 *   側欄主選單項 13 項全部 32px（唯一值）、子選單項 17 項全部 28px（§S-109）
 *   ⇒ 32px 與主選單項同階，是這次拉齊要對齊的目標值。
 *   `.pos-sidebar-toggle` 改前 36px（工具鈕階，同 `.pos-sidebar-restore`／
 *   `.pos-rail-toggle`／`.back-btn`，見 §S-94 章節註解），比旁邊「全部登出」
 *   「暫停」實測的 32px 高 4px。
 *
 * ═══ 只改這顆，不動 `.pos-sidebar-restore` ═══
 *   兩顆共用 §S-94 那條基準規則（約 L19719）：
 *       .pos .pos-sidebar-toggle, .pos .pos-sidebar-restore { ... height:36px ... }
 *   `.pos-sidebar-restore`（主區標題列「顯示紀錄」叫回鈕）不在頁腳這排、
 *   業主裁決沒提到它，維持 36px 工具鈕階不變 —— 所以不能動共用規則本身，
 *   只能加一條只咬 `.pos-sidebar-toggle` 的規則，靠檔尾的來源順序覆蓋。
 *
 * ═══ 特異度／來源順序：確認改到真正的勝出者 ═══
 *   全檔掃過所有選擇器含 `.pos-sidebar-toggle` 且宣告 `height` 的規則，只有一條：
 *   §S-94 的 `.pos .pos-sidebar-toggle, .pos .pos-sidebar-restore`，特異度 (0,2,0)。
 *   本節新規則 `.pos .pos-sidebar-toggle` 同為 (0,2,0)，但排在檔尾、來源順序更後
 *   ⇒ 同分靠順序勝出，不需要 `!important`，也不必墊高特異度。
 *   五個掛這顆鈕的頁面（package／machine／checkout／darka／_package）markup
 *   都確認過只有 `class="pos-sidebar-toggle"`（或再加 `ms-2`），沒有任何一頁掛
 *   `.btn`，所以 §S-101／§S-108 那組「POS 觸控階收 32」的 `.btn` 規則管不到它，
 *   改高度只能靠本節這條，不是巧合命中。
 *
 * ★只在檔尾追加，未修改任何既有規則。
 * ------------------------------------------------------------------------------ */

.pos .pos-sidebar-toggle {
    min-height: 32px;
    height: 32px;
}

/* ═══ 附記：§S-94 一條 `:has()` 規則已零命中、呼叫端已移走，依禁刪守則原地保留 ═══
 *   對象（§S-94，約 L19765）：
 *       @media (min-width: 992px) {
 *           .pos .pos-sidebar .pos-sidebar-header:has(> .pos-sidebar-toggle) {
 *               column-gap: .5rem;
 *           }
 *       }
 *   收合鈕已於今天搬到 `.pos-sidebar-footer`（commit b0e1c67d，
 *   「側欄收合鈕下移至頁腳、移除與左 rail 重複的『後台管理』」），
 *   `.pos-sidebar-header` 再也沒有 `.pos-sidebar-toggle` 這個直接子元素，
 *   這條 `:has()` 從 2026-08-10 起**已零命中、呼叫端已移走**，不是刻意保留的
 *   例外、單純是暫時沒有匹配對象。它只補 header 的欄間距，且那個間距本來就是
 *   為了讓位給這顆鈕才加的，鈕不在裡面了，不影響畫面 ⇒ 依禁刪守則原地保留，
 *   不需同步。
 *   ★若日後把收合鈕搬回 header，這條會自動復活，不用重寫，不是壞掉的規則。
 * -------------------------------------------------------------------------- */


/* --- S-113. POS 四顆工具鈕收成一組共用宣告；「顯示紀錄」移到右下角（2026-08-10）------
 *
 * ═══ ★★這一組包含哪四個選擇器（新增第五顆工具鈕就加進這份清單）═══
 *   ① `.pos-rail-toggle`      左 rail 頁腳「收合選單」（有文字，§S-92 ⑩ 建立）
 *   ② `.pos-theme-toggle`     左 rail 頁腳「主題」黑白切換（有文字，§S-38 建立）
 *   ③ `.pos-sidebar-toggle`   右側欄頁腳收合鈕（純圖示，§S-94 建立、§S-110 改 32px）
 *   ④ `.pos-sidebar-restore`  「顯示紀錄」叫回鈕（有文字，§S-94 建立，收合態才顯示）
 *
 *   ★★★往 POS 加第五顆同類工具鈕時，把它的選擇器加進上面這份清單，**也加進下面
 *     那組共用宣告的 `:is()` 裡**（那個 `:is()` 就是這份清單的可執行版本）。
 *     不加＝它會自己長出第五套規則，而且沒有人會發現 —— §S-38／§S-92／§S-94／
 *     §S-110 四節各寫一套，最後四顆的高度、圓角、字級、圖示全不一樣，就是這樣來的。
 *     ★這裡要修的**不是「有一顆做錯了」**，是「四顆同類元件從來沒有被當成一組定義過」。
 *
 * ═══ 依據（業主原話，2026-08-10 連續四則）═══
 *   「你右邊恢復的顯示紀錄按鈕 要跟著下去啊」
 *   「左邊收和選單跟主題 也要做一樣的外觀啊」
 *   「ssot」「一致性」
 *   總原則（同日）：「全站就是統一 SSOT 對齊 shadcn 就對了」。
 *
 * ═══ 改前實測（https://test.2025-seller.mtest/pos/package，1600x950，亮色）═══
 *                    高度   圓角   邊框         字級  字重  圖示  padding
 *   ① rail-toggle    36px  10px  transparent   12px  600  18px  0 6px
 *   ② theme-toggle   36px  10px  transparent   12px  600  18px  0 6px
 *   ③ sidebar-toggle 32px   8px  hsl(--border) 13px  400  18px  0
 *   ④ restore        36px   8px  hsl(--border) 13px  400  16px  0 10px
 *   同排鄰居（側欄頁腳「全部登出」「暫停」）：32px / 10px / 14px / 500 / 圖示 18px
 *   側欄主選單項 13 項全部 32px（§S-110 已查證，是唯一值）；rail nav 圖示 20px（不在本組）
 *
 * ═══ 共用值怎麼取（每一項都有依據，不是折衷平均）═══
 *   height / min-height : 32px
 *       ← 業主稍早「拉齊 32」裁決（§S-110）＋側欄主選單項實測唯一值 32px；
 *         shadcn `sm` 尺寸同樣是 h-8＝32px。
 *   border-radius       : var(--radius)（實測 10px）
 *       ← 同排鄰居「全部登出」「暫停」實測 10px，本站 `.btn` 的既有語彙就是 10px；
 *         四顆裡 ①② 本來也是 10px ⇒ 取 10px 是往站內多數收斂，不是發明新值。
 *   border              : 1px solid hsl(var(--border))   ← 取 ③④ 的 outline 語彙
 *   background / color  : hsl(var(--background)) / hsl(var(--foreground))
 *       ← ①② 原本是 transparent ＋ `--muted-foreground-on-muted`（壓在 rail 的
 *         `--muted` 上實測 4.40:1，正是 §S-37 結案過的低對比坑）；改成 outline
 *         白底黑字對比反而變好。且 `--background` 98% 與 `--muted` 96.1% 只差 1.9%，
 *         不會在灰色 rail 上變成一塊突出的白卡（§S-94 也已量過亮 255／暗 24 可用）。
 *   font-size           : .75rem（12px，理由見下段寬度預算）
 *   font-weight         : 500   ← shadcn `font-medium`，同排鄰居實測也是 500
 *   圖示                : 18px  ← 四顆裡三顆本來就 18px，側欄頁腳鈕圖示實測亦 18px
 *   hover               : background `--accent` / color `--accent-foreground`（四顆原本一致）
 *
 * ═══ ★字級與 padding 為什麼不能再放大：rail 展開態的寬度預算（實測算式）═══
 *   `.pos-menu-footer` 實測 rect 寬 194px，扣左右內距 15×2 ⇒ 內容寬 164px；
 *   兩顆鈕之間 gap 6px（§S-97 一）⇒ 兩顆合計可用 158px。
 *   主題鈕 `flex:0 0 auto` 吃自己的內容寬，收合鈕 `flex:1 1 auto` 吃剩下的；
 *   ★兩顆都是 `white-space:nowrap` ＋ `overflow:hidden`（§S-97 一）
 *     ⇒ 放不下**不會換行、不會報錯，會直接把字切掉**（§S-93 就是被這個咬過）。
 *   單顆最小寬 = 文字寬 + 圖示 18 + gap 6 + padding×2 + border×2
 *       12px 字 / 8px padding：主題 24+18+6+16+2= 66，收合 48+18+6+16+2= 90 ⇒ 156 ≤ 164 ✔
 *       14px 字 / 8px padding：主題            70，收合            98 ⇒ 168 > 164 ✘ 切字
 *       12px 字 /10px padding：主題            70，收合            94 ⇒ 170 > 164 ✘ 切字
 *   ⇒ `font-size:.75rem` ＋ `padding:0 .5rem` 是這個容器裡**最大的安全值**，
 *     不是隨手取的小值。★日後有人想把字級調回 14px，先回來看這段算式。
 *   ★③ 另有 `min-width:36px`（§S-94）：padding 由 0 變 8px 後內容仍只有 34px < 36px
 *     ⇒ 實際寬度不變（實測仍 36px），純圖示方鈕的外觀不受影響。
 *
 * ═══ 可變的留給各自，不要為了「統一」而拉平 ═══
 *   共用（外觀語彙）：height / min-height / border-radius / border / background /
 *                     color / padding / font-size / font-weight / font-family /
 *                     line-height / gap / 圖示尺寸 / cursor / transition / hover
 *   各自（版面與行為）：`display`（rail 收合成 76px 放不下文字，②的文字得藏起來）、
 *                     `flex-direction`、`width`、`flex`、`margin`、圖示方向
 *                     （②③④ 各自寫死不同的 chevron）、④ 的顯隱條件（收合態才出現）。
 *   ★★文字有無是 `display` 的差異，**不是外觀語彙的差異** ⇒ 不為了統一而把文字
 *     全拿掉或全加上。四顆看起來一樣，但該有文字的照樣有文字。
 *
 * ═══ 被本節推翻的既有條文（原條文一律保留存查，只在這裡標記）═══
 *   ★寫「**已被推翻**」不是「已作廢」：日後有人想搬回去，要看得懂代價是什麼。
 *   ★只記「決定的內容與日期」，**不寫誰寫的** —— 這幾節的作者未知，不猜。
 *   1. §S-38 訂的 `.pos-theme-toggle{min-height:44px}`（現場觸控目標）與
 *      §S-92 ⑩／§S-97 一 訂的 36px，於 2026-08-10 由業主「四顆一致」＋「拉齊 32」
 *      裁決推翻，改為 32px。**僅限桌機**（見下條）。
 *      ★★§S-38 的 44px 觸控目標在**手機沒有被推翻**：本節整段關在
 *        `@media (min-width:992px)` 內，手機那顆仍是 44×44 方鈕
 *        （§S-38 的 `max-width:991.98px` 那一段一個字都沒動，也沒被特異度壓到）。
 *   2. §S-94 訂的 `border-radius:var(--radius-md)`（8px，註解寫「＝官方 Button 那一階」），
 *      於 2026-08-10 改為 `var(--radius)`（10px）。理由：本站 `.btn` 實測 10px、
 *      同排鄰居「全部登出」「暫停」實測 10px、①② 本來也是 10px ⇒ 站內語彙是 10px。
 *      shadcn 官方 `rounded-md` 的說法**存查但不採用**（採用它會讓這四顆變成
 *      全站唯一的 8px 例外，反而破壞業主要的一致性）。
 *   3. §S-94 訂的 `.pos-sidebar-restore` 圖示 16px，於 2026-08-10 改為 18px（與另三顆一致）。
 *   4. §S-97 一 訂的 `font-weight:600`，於 2026-08-10 改為 500。
 *   5. §S-95 的「叫回鈕放主區標題列」位置決定 —— 見下方「二」。
 *
 * ═══ 特異度：靠分數贏，不靠 `!important`、也不靠「同分排後面」═══
 *   要贏過的既有規則（全檔掃過含這四個 class 且宣告 height/圓角/字級/圖示的）：
 *     §S-92 ⑩ `.pos .pos-rail-toggle`                                        (0,2,0)
 *     §S-38   `.pos .pos-menu .pos-theme-toggle`                             (0,3,0)
 *     §S-38   `… .pos-theme-toggle > i|svg`                                  (0,3,1)
 *     §S-94   `.pos .pos-sidebar-toggle, .pos .pos-sidebar-restore`          (0,2,0)
 *     §S-110  `.pos .pos-sidebar-toggle`                                     (0,2,0)
 *     §S-97 一之二 `.pos .pos-menu.pos-rail--collapsed .pos-theme-toggle`    (0,4,0)
 *     §S-97 一 `.pos .pos-menu:not(.pos-rail--collapsed) .pos-menu-footer .pos-rail-toggle` (0,5,0)
 *     §S-97 一 同前綴 `… .pos-theme-toggle > i|svg`                          (0,5,1)  ← 最高
 *   本節：`.pos`×6 ＝ (0,6,0)，`:is(四顆)` 取引數最高分再加 (0,1,0) ⇒ **(0,7,0)**；
 *         圖示那條再加一層 `:is(…, [class*="…"])` ⇒ **(0,8,0)**。
 *         b 欄先比：7 > 5、8 > 5 ⇒ 全部贏，不需要 `!important`。
 *   ★為什麼不學 §S-110 用「同分靠來源順序」：那招只要有人往檔尾再追加一條同分規則
 *     就會被**無聲翻盤**（畫面變了但沒有任何錯誤），分數贏才是穩的。
 *   ★`:is()` 的分數 = 括號內最高分的那個引數（四個都是 class ⇒ 1 分），
 *     所以往 `:is()` 裡加第五顆 class 時**分數不會變**，不必重算這一段。
 * ---------------------------------------------------------------------------- */

/* ── 一、四顆共用的外觀語彙 ─────────────────────────────────────────────────
   ★整段關在 `@media (min-width:992px)`：四顆裡三顆在手機根本不存在
     （① `display:none` §S-92、③ `display:none` §S-94、④ 只在 ≥992px 才 inline-flex），
     手機只剩 ② 那顆 44×44 觸控方鈕 —— 刻意不碰（見上方推翻紀錄 1）。
   ★★★新增第五顆工具鈕：把 class 加進下面這個 `:is()`（＋本節開頭那份清單）。 */
@media (min-width: 992px) {
    .pos.pos.pos.pos.pos.pos :is(.pos-rail-toggle,
                                 .pos-theme-toggle,
                                 .pos-sidebar-toggle,
                                 .pos-sidebar-restore) {
        height: 32px;
        min-height: 32px;
        padding: 0 .5rem;
        gap: .375rem;
        align-items: center;
        justify-content: center;
        border: 1px solid hsl(var(--border));
        border-radius: var(--radius);
        background: hsl(var(--background));
        color: hsl(var(--foreground));
        font-family: inherit;              /* ★裸 <button> 不繼承字型（§S-94 已踩過） */
        font-size: .75rem;
        font-weight: 500;
        line-height: 1.2;
        cursor: pointer;
        transition: background-color .15s linear, color .15s linear, border-color .15s linear;
    }
    /* hover ＝ 四顆原本就一致的那組，照抄不動語彙，只是收進共用宣告 */
    .pos.pos.pos.pos.pos.pos :is(.pos-rail-toggle,
                                 .pos-theme-toggle,
                                 .pos-sidebar-toggle,
                                 .pos-sidebar-restore):hover,
    .pos.pos.pos.pos.pos.pos :is(.pos-rail-toggle,
                                 .pos-theme-toggle,
                                 .pos-sidebar-toggle,
                                 .pos-sidebar-restore):focus-visible {
        background: hsl(var(--accent));
        color: hsl(var(--accent-foreground));
    }
    /* 圖示 18px。`i, svg` 兩種都接（Lucide 會把 <i data-lucide> 換成 <svg>，
       替換前後不能有一段沒樣式，這是 §S-92 ⑩ 的既有做法）；
       ★`[class*="-toggle-icon"]` / `[class*="-restore-icon"]` 是給有掛 icon class 的
         那些頁面（package 有掛、其餘沒掛，§S-94 已查證）。
       ★刻意**不**寫 `span`：那會咬到 `.pos-rail-toggle-text` /
         `.pos-theme-toggle-text`，把文字的 span 也壓成 18×18。 */
    .pos.pos.pos.pos.pos.pos :is(.pos-rail-toggle,
                                 .pos-theme-toggle,
                                 .pos-sidebar-toggle,
                                 .pos-sidebar-restore) :is(i,
                                                           svg,
                                                           [class*="-toggle-icon"],
                                                           [class*="-restore-icon"]) {
        width: 18px;
        height: 18px;
        flex: 0 0 auto;
    }
}

/* ── 二、`.pos-sidebar-restore`：主區標題列右上 → 視窗右下角 ────────────────
   業主原話：「你右邊恢復的顯示紀錄按鈕 要跟著下去啊」。

   ★§S-95 把叫回鈕放在主區標題列**是有理由的**（當時收合鈕在側欄頁首右上，
     叫回鈕放標題列右上＝兩顆位置感一致，理由寫的是「叫回鈕要在視線內」）。
     **§S-95 的位置決定基於「叫回鈕要在視線內」，本節因收合鈕已移至側欄頁腳
     而推翻它 —— 兩顆的位置感必須一致。**
     ★不是「§S-95 寫錯了」：它的前提（收合鈕在頁首）已於 2026-08-10 消失
       （收合鈕下移至 `.pos-sidebar-footer`，見 §S-110 附記），結論才跟著失效。
       ★日後若把收合鈕搬回頁首，這一節要一起搬回去 —— 代價是**兩處都得改**，
         只改一邊就又變成「收起來在右下、叫回在右上」的不對稱。

   ═══ 座標怎麼來的（不是「大概在右下角」）═══
     改前實測（視窗 1600×950）：`.pos-sidebar` 是 `position:fixed`、貼滿右緣與下緣
     （§S-97 二），`.pos-sidebar-footer` padding 15px，
     ⇒ `.pos-sidebar-toggle` 的視窗座標 right=1585 / bottom=935
       ＝距視窗右緣 15px、距下緣 15px。
     所以 `right/bottom` 取 `.9375rem`（＝15px，同 §POS-3 的容器內距）
     ⇒ **叫回鈕就長在收合鈕原本的那一點上**，收合／叫回是同一個位置感。

   ★用 `fixed` 不用 `absolute`：`.pos-content-container` 有 `overflow:hidden`、
     `.pos-sidebar` 自己是 fixed ⇒ absolute 會被祖先裁掉或跟著內容捲走。
   ★`margin:0` 必寫：§S-94 給它 `margin-left:auto`（在標題列裡靠右用的）。
     變成 fixed 之後 `auto` 邊界會參與定位計算，不清掉位置會飄。
     本條 (0,2,0) 與 §S-94 那條同分但排在檔尾 ⇒ 靠來源順序勝出；
     全檔已確認**沒有任何其他規則**對 `.pos-sidebar-restore` 宣告
     position / top / right / bottom / left / margin。
   ★`top:auto; left:auto` 寫出來是為了誠實：`fixed` 之後這兩個若被別處給了值，
     right/bottom 會失效（左上優先），寫死 auto 讓右下角定位不依賴「別人沒寫」。
   ★不加 `box-shadow`：業主要的是「四顆一樣的外觀」，陰影只有這一顆有就又破功了。
     它靠 `--background` 底色 ＋ `--border` 邊框與內容分離（同 §S-94 的 outline 語彙）。

   ═══ z-index 取 1030：★高過內容、**低於任何遮罩層** ═══
     實測本站層級（由低到高）：
       POS 主區標題列 sticky        5      §S-104 (2)
       手機 FAB「紀錄」             1020   §POS-7（只在 max-width:991.98px 顯示）
       ★本節叫回鈕                 1030
       offcanvas-backdrop           1040   bootstrap.min.css
       modal-backdrop               1050   `--bs-backdrop-zindex`
       modal                        1055   `--bs-modal-zindex`（本頁 4 個 modal 實測都是 1055）
     ⇒ 對帳／取消警告 modal 一開，叫回鈕會被 backdrop 正常蓋住。
       ★「一顆叫回鈕浮在 modal 上面」是很難察覺的錯（modal 不常開、又只差幾 px），
         所以這裡寧可訂在 1030 而不是隨手 9999。
   ★與手機 FAB 不會重疊：FAB 只在 <992px 顯示、本節只在 ≥992px
     ⇒ **兩者在任何寬度下都不同時存在**。
     ★這是靠斷點隔離，不是靠座標錯開 —— FAB 是 `bottom:60px right:12px`，
       真要同時出現就會疊在一起，不能靠運氣。
   ★不受 Debugbar 影響：Debugbar 給 `body` 加 `margin-bottom:32px`，
     但 `position:fixed` 的定位基準是視窗、不是 body 的框
     ⇒ 本機看到的位置與正式站相同（已用「清掉 body margin 後重量」對照組驗過）。
 -------------------------------------------------------------------------- */
@media (min-width: 992px) {
    .pos .pos-sidebar-restore {
        position: fixed;
        top: auto;
        left: auto;
        right: .9375rem;
        bottom: .9375rem;
        z-index: 1030;
        margin: 0;
    }
}

/* ═══ §S-113 附記：改後實測 ＋ 一條新增的維護約束（2026-08-10）═══
 *
 * 實測（https://test.2025-seller.mtest/pos/package）四顆的共用值全部相同：
 *   height 32px / min-height 32px / border-radius 10px / border 1px solid
 *   / padding 0 8px / font-size 12px / font-weight 500 / gap 6px / 圖示 18×18
 *   亮色 bg rgb(250,250,250) color rgb(0,0,0) border rgb(229,229,229)
 *   暗色 bg rgb(10,10,10)   color rgb(250,250,250) border rgba(255,255,255,.1)
 *   四種狀態都量過：rail 展開(210px)／rail 收合(76px)／亮／暗 ⇒ 皆「全部相同」。
 *   ★文字沒有被拿掉：「收合選單」「主題」「顯示紀錄」都還在，
 *     rail 收合態才由 §S-97／§S-92 把文字 display:none（那是既有行為，不是本節做的）。
 *   ★沒有切字：兩顆 rail 鈕 scrollWidth === clientWidth（展開 49/49・90/90，收合 49/49）。
 *   ★手機未受影響：375／768 實測 `.pos-theme-toggle` 仍 44×44、min-height 44px。
 *   ★叫回鈕：距視窗右緣 15px、下緣 15px（rect 1495,903,90,32 @1600×950），
 *     與改前收合鈕的 right=1585／bottom=935 同一點；收合↔叫回往返各兩次皆正常。
 *   ★Debugbar 對照組：原狀／`body{margin-bottom:0}`／Debugbar `display:none`
 *     三次量到完全相同的 rect ⇒ 定位不依賴 Debugbar 佔位，正式站位置與本機一致。
 *     ★本機 Debugbar 的 z-index 是 6000000 且橫跨 y≈917~950，會蓋住這顆鈕
 *       ⇒ **要點它請用 `element.click()`，不要用座標**（那是本機現象，不是 bug）。
 *   ★modal 層級：開 modal 後 `.modal-backdrop` z=1050 蓋在鈕(1030)之上，實測鈕被遮住 ✔
 *
 * ★★★ 新增的維護約束（本節造成的，日後動 POS 主區版面要先看這條）★★★
 *   主區內容的**右下角從此被一顆 `position:fixed` 的鈕佔住**：
 *   以視窗右下角為原點的 15px 內縮處，一塊約 90×32（含文字時）的區域，只在
 *   側欄收合態出現。
 *   ⇒ **日後若在 POS 主區底部靠右放分頁列、或右對齊的動作鈕，會被這顆鈕壓住。**
 *   已查證目前不會撞到：POS 六頁全部沒有 `pagination`／`paginate`；
 *   grep 到的 `ms-auto`／`float-end`／`text-end` 右靠元素全都在側欄或卡片內部，
 *   不在主區內容的底部。另用注入 40 列表格＋分頁列的方式實測，
 *   捲到最底時鈕底下沒有任何可點元素（表格最後一列 bottom=887 < 鈕 top=903）。
 *   ★這是「現在剛好沒撞到」，不是「結構上不會撞」 —— 所以寫下來。
 * ------------------------------------------------------------------------- */

/* ═══════════════════════════════════════════════════════════════════════
   §S-111（2026-08-10）：頁首動作列**翻轉預設** ——
                        「靠左」變成預設，「靠右」要用 aux slot 明說
   ───────────────────────────────────────────────────────────────────────
   ★業主原話（2026-08-10，逐字）：
     ①（指著 /packaging-machine-order-management/orders/modify/{no}/item）
        「你沒有把動作列 SSOT 做好，拆單併單按鈕被放到右邊了」
     ②（總原則）「全站就是統一 SSOT 對齊 shadcn 就對了」

   ═══ 一、病根：元件契約與主題實作互相矛盾（這就是「SSOT 沒做好」的實體）═══
   元件契約 resources/views/components/page-header.blade.php L258-266 白紙黑字寫著：
       「重要動作（新增／儲存／確認）＝預設 slot；
         輔助入口（設定／說明／更新時間）＝aux slot，靠右。」
     ⇒ 契約說：位置由「放進哪個 slot」決定。
   但主題的實作（§S-66 + §S-69 + §S-70）說的是另一件事：
       預設 slot 一律靠右（§S-66 給 order 99），
       除非組裡找得到一顆 .btn-primary（§S-69）或 [data-ph-primary]（§S-70）才靠左。
     ⇒ 實作說：位置由「組裡有沒有一顆特定的按鈕」決定。
   同一件事有兩套權威來源、而且結論不同 —— 那正是 SSOT 破掉的定義。

   ★§S-70 自己的註解已經親口寫下這個失效（原文，L16855 附近）：
       「這是替身指標的典型失效：想讓一顆按鈕靠左，就得先把它塗黃。
         版面（左右）與顏色（重要性）本來是兩件事，被綁在同一個 class 上。」
     它當時的解法是**再加一個替身**（data-ph-primary），治好了那一頁，沒治病根：
     只要有人開新頁、動作既不是黃鈕、也忘了標記，就會再掉回右邊。
     業主 2026-08-10 點名的那一頁（拆單／併單）就是這樣掉下去的第 4 頁。

   ═══ 二、憑什麼說「由 slot 決定位置」才是 shadcn 的做法（出處可自行查證）═══
   官方 card.tsx 的 CardAction 是
       col-start-2 row-span-2 row-start-1 self-start justify-self-end
   （這一行本檔 L16550 已逐字抄錄過；另見 StoreSetting/_picker_card.blade.php L199、L212）。
   它把「釘在右端」綁在**插槽**上（data-slot="card-action"），
   不是綁在按鈕的顏色或 variant 上。官方**不存在**「這顆是 primary 所以它會靠左」這種規則。
     ⇒ 對齊 shadcn ＝ 把位置的決定權交還給插槽。
       本專案的那個插槽就是 aux slot —— 它已經存在、已經有 6 個呼叫端在用
       （Order/New/index、StoreSetting/Store/_edit_tabs、SystemSetting/Order 三支 Cause*、
         Invoice/Main/edit），而且它的語意與 CardAction 逐字對應：次要／輔助入口，靠右。

   ═══ 三、這是不是又回到「一律靠左」（業主否決過的那一版）？不是 ═══
   §S-70 註解記著業主三次拍板：
       ① 5a0c0f07（08-04）動作組一律靠左
       ② 7ca0eb23（08-07 10:18）推翻①：「說明跟動作就是放在右上角」
       ③ 0b37d8a3（08-07 12:06）推翻②：「新增訂單按鈕 重要按鈕都要置左
          不重要的 說明 更新時間 才放右邊」
   ★①被否決的那個時間點，**aux slot 還不存在**（它是 08-07 之後才長進元件裡的）。
     當年「一律靠左」之所以錯，是因為那時候**沒有任何管道**能讓「說明／設定」留在右邊 ——
     一律靠左就真的把該靠右的也拖過去了。現在管道有了。
   ★而業主③那句話，本節逐字仍然成立：
       重要動作（預設 slot）靠左；不重要的說明（order 100）、更新時間（order 98）、
       以及輔助入口（aux slot，order 99）通通還在右端，本節一個都沒動。
     ⇒ 本節改的是**判準的載體**（從「按鈕是什麼顏色」換成「放進哪個 slot」），
       不是判準本身。判準仍然是③。

   ═══ 四、改前實測（https://test.2025-seller.mtest，1440 亮色，內容區 x=244..1416）═══
   ── 受影響的 4 頁（預設 slot 既無 .btn-primary 也無 [data-ph-primary]）──
     頁面                                              動作組      改前 order/left  改後 order/left
     /packaging-machine-order-management/orders/
       modify/M2606139B2338/item （業主點名）           拆單/併單    99 / 1341.98     1 / 244
     /notifications                                    全部已讀     99 / 1327.00     1 / 244
     /center/desktop                                   隱藏離線     99 / 1327.00     1 / 244
     /ai-tool/suite/2                                  刪除         99 / 1350.00     1 / 244
   ── 對照組：原本就靠左的 6 頁（走 §S-69）⇒ 改後**逐位不變** ──
     /good/warehouse、/invoice/main、/inventory/product、/hr/department、
     /store-setting/store、/good/storageLocation
       六頁改前改後皆 order 1 / left 244.00 / right 335.30（§S-69 與本節宣告值相同）
   ── aux slot 的頁面 ⇒ 改後**仍然靠右**（未被波及）──
     /system-setting/order/cause-draft        aux「設定分類」order 99 / left 1327.00（改前＝改後）
     /order-management/status/new-orders      aux「轉移期對帳」order 99 / left 1314.88（改前＝改後）
     /invoice/main/modify/1                   aux「光貿官方電子發票 手動重開」order 99 / left 1327.00（改前＝改後）
   ── [data-ph-primary] 的頁面（§S-70）⇒ 改後不變 ──
     /center/report                           「操作」order 1 / left 244.00（改前＝改後）

   ═══ 五、影響面盤點（靜態掃描與逐頁實測互相對得起來）═══
   掃 resources/views 全部 blade（★先整檔剝掉 Blade 註解再找呼叫點 ——
   不剝的話，註解裡提到 x-page-header 的段落會被算成呼叫點，實測會多出 7 個假的）：
       x-page-header 呼叫點      108
       預設 slot 非空（有動作組） 70
         ├ 命中 §S-69（.btn-primary）      64  ← 本來就靠左，本節同值，零變化
         ├ 命中 §S-70（[data-ph-primary]）  1  ← 本來就靠左，本節同值，零變化
         └ 兩者皆無                          5  ← 其中 1 支是 JS 註解裡的假命中
                                                （components/page-tabs.blade.php），
                                                真正受影響的就是上面第四段那 4 頁
   ★逐支查證過「原本 §S-69 註解列為『維持靠右』的那一批」現在人在哪裡：
     說明／設定（Seller/includes/_plan_tabs）已於 2026-08-10 搬進 tabs slot，預設 slot 現在是空的；
     設定分類（SystemSetting/Order 三支 Cause*）、轉移期對帳（Order/New）、
     重設為預設值（StoreSetting/Store/_edit_tabs）、光貿官方電子發票／手動重開（Invoice/Main/edit）
     已搬進 aux slot；作廢發票已掛 [data-ph-primary]。
     ⇒ **目前全站真心想要「預設 slot 靠右」的頁面是 0 支**，所以翻轉預設不會誤傷任何人。
   ★★殘留判斷風險（誠實記下來，不假裝沒有）：
     4 頁裡的「隱藏離線」（篩選開關）與「刪除」（破壞性動作）是否該算主要動作，
     可以有不同意見。**但那不再是主題的問題**：真要靠右，正解是把那顆鈕搬進 aux slot
     （呼叫端一行），不是回頭去改本節或去塗顏色 —— 這正是本節要建立的 SSOT。

   ═══ 六、為什麼是這個選擇器（不靠「同分＋檔案順序」決勝）═══
   選擇器 .page-header.page-header.d-flex（重複一次 class）＝特異度 (0,4,0)：
       §S-66  .page-header.d-flex > .fb-ph-actions                                (0,3,0)  ← 嚴格輸給本節
       §S-69  ….d-flex:has(> .fb-ph-actions .btn-primary) > .fb-ph-actions        (0,5,0)  ← 贏本節，但**宣告值逐字相同**
       §S-70  ….d-flex:has(> .fb-ph-actions [data-ph-primary]) > .fb-ph-actions   (0,5,0)  ← 同上
     ⇒ 對唯一真正的對手（§S-66）是**嚴格勝出**，不依賴檔案順序；
       對 §S-69／§S-70 誰贏都一樣（order 1 / margin-left 0，兩邊逐字同值）。
   ★匹配集合與 §S-66 完全相同（都要求 .page-header.d-flex 的直接子元素），
     不會多打到任何 §S-66 打不到的東西。
   ★不帶 .d-flex 的 .page-header（例如元件輸出的 h1.page-header）不在射程內，
     它們仍由 L9582／L10251 那兩條 (0,2,0) 管，而那兩條算出來的也是 order 1 / margin-left 0
     —— 與本節同向，全站因此只有一個預設值。

   ═══ 七、!important：用**正對照**決定的，不是算出來的 ═══
   A（不帶 !important）／B（帶 !important）各跑一次全量測，1440 與 375 各一輪：
       A 與 B 的 order／margin-left／left 三項，15 個受測點、兩種寬度，**逐位完全相同**。
   ★所以 A==B 不是「量不出來」，兩支誘餌先證明了量測是活的：
       誘餌 1  本節改成 order 95            → A 組 4 頁確實讀到 order 95、
                                              /notifications 的 left 由 1327 變 1319（跨過 order 98 的更新時間）
       誘餌 2  本節改成 order 99 !important
                margin-left auto !important → B 對照組 6 頁全被推到右邊（left 244 → 1324.70），
                                              證明「對照組被誤傷」這件事量得到
   ★結論：實測**不需要** !important。仍然帶它，理由是兩條與版面無關的硬需求：
     (a) 不留死宣告 —— §S-66 那條是 margin-left auto !important；
         本節若不帶 !important，margin-left 這一句在那 4 頁上**永遠贏不了**，
         等於寫了一句不會生效的宣告（本專案 feedback 記過「寫著但沒生效」這一族）。
     (b) 與 §S-69／§S-70 宣告值**逐字相同** —— 三條規則同值才叫 SSOT；
         少一個 !important 就變成「看起來一樣、其實三條各自算出不同結果」。

   ═══ 八、§S-66 的 flex 填充位機制原封不動（§S-15／§S-41 記過兩次的坑）═══
   §S-66 的 ::before（order 90、flex 1 1 auto）**一個字都沒改**。
   本節只是把動作組從 order 99（填充位之後）搬回 order 1（填充位之前）——
   與 §S-69 兩年來做的事完全一樣，整列仍然只有**一個**伸縮邊界，
   不會重演「動作鈕浮在畫面正中央」。
   ★動作組在 order 1 時，填充位（內容為空、base size 0，永遠塞得下）一定與它落在同一條 flex 線上，
     剩餘空間先被 flex-grow 吃光，auto margin 因此拿到 0 —— 這就是 A==B 的結構性原因，不是巧合。
   ★375 折行實測（受影響 4 頁，改前 → 改後 left）：
       /packaging-machine…/item   284.98 → 110.25（改後與標題同列，緊接標題之後）
       /notifications             270.00 →  16.00
       /center/desktop            270.00 → 165.13
       /ai-tool/suite/2           293.00 →  60.89
     對照組 6 頁與 aux 3 頁在 375 下**逐位不變**（16 / 242.02 / 260.67 / 267.66 / 270 / …）。

   ═══ 九、既有規則一條都沒改、沒刪 ═══
   §S-66（L13382）、§S-69（L13754）、§S-70（L16881）三條的宣告全部原文保留，
   只在原處各加一行「★已被 §S-111 …」的就地標記（沿用本檔 §S-5 的處理法）。
   ★[data-ph-primary] 仍然可用：它現在退化成一條與預設同值的宣告，
     對已經標了的呼叫端（Center/reportPackage/index、Invoice/Main/edit）零影響，
     不需要回頭拆掉；新頁面則不必再標，因為靠左已經是預設。
   ═══════════════════════════════════════════════════════════════════════ */
.page-header.page-header.d-flex > .fb-ph-actions {
    /* order 1 ＝ 標題／計數 chip／副標（order 0）之後、§S-66 填充位（order 90）之前。
       ★與 §S-69／§S-70 逐字同值，三條同時命中時結果也是同一個。 */
    order: 1;
    /* 壓住 §S-66 的 margin-left auto !important（1440 是死碼，375 折行時會真的生效）。 */
    margin-left: 0 !important;
}

/* --- S-114. POS 手機版：左右兩顆漢堡 + 兩個抽屜，主區只留資料（2026-08-10）--------
   業主原話：「手機版需要變成左右都有漢堡條 呈現中間資料就好」、「開始漢堡選單」。

   修前現況（實測 375x812，pos/package，扣掉 Debugbar 的 body margin-bottom:96px）：
     .pos-menu       頂部橫向 bar     x=0 y=0 w=375 h=104.58（fixed, z-index:10）
     .pos            padding-top      112px（§POS-1 寫死常數）
     .pos-content    x=0 y=112 w=375 h=767
     .pos-menu-footer 絕對定位貼右上   x=323.5 y=0 44x44（§S-38 手機那段）
     .pos-mobile-sidebar-toggler       x=301.34 y=684 w=61.66 h=60 圓角 9999px 文字「紀錄」
     .pos-rail-back / .pos-rail-sep / .pos-rail-toggle 全部 display:none

   修後目標：
     頂部只留 52px 的「漢堡條」——左漢堡開 rail 抽屜、右漢堡開紀錄抽屜，中間全給資料。

   ═══ 一、桌機（≥992px）零變化的保證 ═══
   本節除了「① 兩顆新元素的基準宣告」之外，每一條規則都關在
   `@media (max-width: 991.98px)` 之內。① 的內容只有 `display:none`，作用對象是
   **本節新增的兩個 class**（`.pos-mobile-menu-toggler` / `.pos-drawer-backdrop`），
   桌機沒有任何既有元素會命中它們 ⇒ 桌機的算繪結果不可能改變。
   ★驗收方式：1600x950 量 rail 210/76、主區讓位、四顆工具鈕 32px（見本節末的實測表）。

   ═══ 二、為什麼保留 52px 的頂部條，不是直接 padding-top:0 ═══
   兩顆漢堡是 `position:fixed`，不佔流。若 padding-top 收到 0，它們會蓋在
   `.pos-page-header`（sticky, h=64）的左右兩端上。可以靠給標題列補左右內距避開，
   但 `machine_backup.blade.php` **沒有 `.pos-page-header`** ⇒ 那條路對它無效，
   而且 375px 寬扣掉左右各 52px 只剩 271px 給標題，中文標題會擠壓。
   ⇒ 改成「保留一條 52px（44px 觸控鈕 + 上下 4px）的漢堡條」：
      6 頁一致、不依賴任何頁面內的容器、也剛好就是業主說的「左右都有漢堡條」。
      淨賺 112 - 52 = 60px 給資料區（375x812 上是 +7.4% 可用高度）。

   ═══ 三、padding-top 要蓋掉的是兩條不同的規則（★這裡與交付單的前提不同）═══
   交付單寫「後 3 頁（checkout/darka/_package）沒有 pos-with-menu ⇒ padding-top:112px
   對它們本來就不生效、rail bar 直接壓在內容上（既有缺陷）」。
   ★實測推翻：§S-94（本檔 L19553）已經補過那個缺口——
       `.pos.pos-customer:has(> .pos-menu) { padding-top: 112px }`
     所以 6 頁**都有**112px。本節必須同時蓋掉：
       §POS-1  `.pos.pos-with-menu`                      (0,2,0)
       §S-94   `.pos.pos-customer:has(> .pos-menu)`      (0,3,0)  ← `:has()` 內的 .pos-menu 計 1 分
     做法＝把兩個選擇器逐字照抄成一條規則，同分而在檔尾 ⇒ 兩邊都勝。
     ★刻意不改寫成 `.pos { padding-top:… }`：那是 (0,1,0)，兩條都贏不了。

   ═══ 四、堆疊順序（z-index）為什麼要這樣排 ═══
   既有值：`.pos-menu` 10（§POS-2）／`.pos-page-header` 5（§S-94）／
           `.pos-mobile-sidebar-toggler` 1020（§POS-7）／`.pos-sidebar` 1021（§POS-4）。
   rail 只有 10 ⇒ 一旦變成抽屜，開啟時會被 1020 的浮動鈕壓在上面。所以手機重排成：
       1018  兩顆漢堡鈕（★比 1021 低 ⇒ 紀錄抽屜打開時自動被蓋住，不用寫任何隱藏規則）
       1021  `.pos-sidebar` 紀錄抽屜（不動，它是 inset:0 全螢幕，本來就不需要遮罩）
       1022  `.pos-drawer-backdrop` 遮罩（★只有 rail 抽屜開著才 display:block）
       1023  `.pos-menu` rail 抽屜
   ⇒ 兩個抽屜互斥是**堆疊順序自己保證的**，不靠 JS：
      rail 開著時遮罩蓋住右漢堡（1022 > 1018）、紀錄開著時抽屜蓋住左漢堡（1021 > 1018）。

   ═══ 五、零 JS 改動：兩顆新元素都掛既有的通用委派 IIFE ═══
   `pos/_header.blade.php` 第一支 IIFE（`window.__posToggleClassBound`）已經支援
   `data-toggle-class` / `data-dismiss-class` + `data-target`，且它用的是
   `document.querySelector(sel)`。
   ★`data-target` 一律寫 `.pos`（不是 `#pos`）：6 頁的版面根容器有兩種 id
     （`#pos` 3 頁 / `#pos-customer` 3 頁），但兩者都帶 class `pos`
     ⇒ `.pos` 在 6 頁都命中該頁自己的根容器（實測見本節末）。寫 `#pos` 會讓後 3 頁拿到 null。

   ★★為什麼**不**讓 `.pos-rail-toggle`（rail 頁腳「收合選單」鈕）在手機現身當關閉鈕
     ——交付單的 ⑤ 項在此推翻，理由是實測出來的副作用：
     那顆鈕的行為在第二支 IIFE（`window.__posRailBound`），click 一律
     `localStorage.setItem('seller-pos-rail-collapsed', …)` 之後才呼叫 `apply()`，
     而 `apply()` 裡是 `var on = collapsed && isDesktop()`。
     ⇒ 手機上按它：畫面**完全沒反應**（class 被 isDesktop() 擋掉），
        但**桌機的 rail 收合偏好已經被翻面**。一顆「按了沒事、卻偷改桌機狀態」的鈕
        比沒有這顆鈕更糟。§S-92 ⑩ 的 `display:none` 維持原樣一個字不動。
     ⇒ 關閉抽屜改由本節新增的 `.pos-menu-close`（掛 `data-dismiss-class`，零 JS）
        ＋ 遮罩點擊 兩條路徑負責。

   ═══ 六、遮罩為什麼一定要是真元素，不能用 `.pos::after` ═══
   關閉行為靠既有 IIFE 的 `e.target.closest('[data-dismiss-class]')`。
   偽元素不是事件目標：點在 `.pos::after` 上，`e.target` 會是 `.pos` 本身
   ⇒ 想讓它生效就得把 `data-dismiss-class` 掛在 `.pos` 上，那會變成
      **點畫面任何一處都關抽屜**（連抽屜內的按鈕也算，因為 closest 會往上找到 .pos）。
   ⇒ 必須新增一個真的 `<div>`。它是 `position:fixed`，不佔流、桌機 display:none。

   ═══ 七、右邊那顆 FAB 改漢堡：走 CSS，零 blade 改動 ═══
   6 頁的 FAB 內容其實是**兩種**（交付單寫「6 個 blade 各自寫死『紀錄』」，實測修正）：
     package / machine / machine_backup … `<a class="pos-mobile-sidebar-toggler h5">紀錄</a>`（純文字節點）
     checkout / darka / _package        … 同一顆但內容是 16x16 購物籃 `<svg class="img">`（+ badge）
   走 CSS 的三個理由：
     (1) 文字是 `<a>` 的**直接文字節點**，CSS 選不到它 —— 但 `font-size:0` 可以讓它塌成
         0x0。★刻意用 `font-size:0` 而不是 `display:none`/`visibility:hidden`：
         前者**保留無障礙名稱**（AT 仍讀得到「紀錄」），後兩者會把可存取名稱一起拿掉，
         而這顆 `<a>` 沒有 `title` / `aria-label`（實測 0 個屬性）⇒ 一藏就變無名按鈕。
     (2) 後 3 頁若要改成漢堡，等於**刪掉**那段購物籃 SVG ——違反「禁止刪除任何內容」。
         用 `.img { display:none }` 把它讓位，原始 markup 一個位元組都不動。
     (3) 6 頁同一份 CSS ⇒ 左右兩顆長得一樣（同尺寸/同圓角/同邊框），
         這是業主「左右都有漢堡條」的視覺前提；6 支 blade 各改一次遲早會漂移（§S-110 教訓）。
   ★`font-size:0` 要贏過 Bootstrap 的 `.h5, h5 { font-size:1.25rem }`（(0,1,0)，無 !important）：
     本檔（shadcn-theme.css）在 bootstrap 之後載入、且本節在檔尾 ⇒ 同分靠順序取勝。
     `.badge` 會繼承到 0，所以要明確給回字級。

   ★★★ 這顆的 `color` **搶不到**，而且失敗方式是「白字白底、什麼都不顯示、零錯誤訊息」——
     實測抓到的，不是推論。第一版草稿把兩顆漢堡都做成「淺底 + 邊框 + currentColor 三條槓」，
     注入後量到 `computedColor = rgb(250,250,250)`（＝`--dark-foreground`），
     `background` 卻是我給的 `rgb(250,250,250)`（＝`--background`）⇒ 白配白，
     ★★螢幕上是一顆**空白方鈕**，`::before` 的 mask 明明都正確解析了（實測 maskImage 有值）。
     真兇是本檔既有的全站連結色守門（§S-39 那一族）：
       `#content.app-content a.pos-mobile-sidebar-toggler:not(.btn):not(.dropdown-item)
        :not(.nav-link):not(.fb-tab):not(.page-link) { color: hsl(var(--dark-foreground)) }`
       特異性 **(1,7,1)**（id 1／`.app-content` + 5 個 `:not()` 內的 class + `a.` 的 class = 7／`a` 1 型別）
       ⇒ 我的 `.pos-mobile-sidebar-toggler` (0,1,0) 差了一個 id 和六個 class，**永遠贏不了**。
     ⇒ 治法**不是**去比特異性（那要寫成 `#content.app-content a…:not()x5`，而且
        6 頁不保證都有 `#content.app-content` 外殼 ⇒ 會變成「有的頁面白、有的頁面黑」的漂移），
        ★而是**順著它**：這顆維持 §POS-7 既有的「`--dark` 實心底 + `--dark-foreground` 前景」，
        `currentColor` 自然就是近白的三條槓壓在近黑底上，亮暗兩個主題都成立，
        而且**兩條路徑（有沒有那個 id 守門）算出來的 color 都是 `--dark-foreground`**，不會漂。
     ⇒ 本節對這顆**只改幾何**（座標／尺寸／圓角／字級／陰影），
        `background` / `color` / `border` / `:hover` **一條都不宣告**（§POS-7 原樣保留）。
     ⇒ 左邊那顆新鈕也跟著改成同一組深色語彙，兩顆才會長得一樣（業主要的是「左右都有漢堡條」）。

   ═══ 八、blade 端必須配合的 4 處（本節 CSS 單獨上不會生效的部分）═══
     B1 `pos/_header.blade.php` 新增左漢堡 `<button class="pos-mobile-menu-toggler">`
        （放在 `<!-- END pos-menu -->` 之後、第一支 `<script>` 之前）
     B2 `pos/_header.blade.php` 新增遮罩 `<div class="pos-drawer-backdrop">`（同上位置）
     B3 `pos/_header.blade.php` 新增抽屜關閉鈕 `<button class="pos-menu-close">`（`.pos-menu` 內第一個子元素）
     B4 `pos/_header.blade.php` `.pos-rail-back` 的 `d-none d-lg-flex` → `d-flex`、
        `.pos-rail-sep` 的 `d-none d-lg-block` → `d-block`
        （Bootstrap `.d-none` 帶 `!important`，用 CSS 蓋要動用 `!important` 打對打，
          改 utility class 才是乾淨解；桌機兩者本來就是 flex/block，改完桌機零變化）
     ★B4 就是把「rail 改抽屜後，手機終於能放返回後台」這個缺口補上：
       原本關掉的唯一理由寫在 blade L18-25 ——「會把頂 bar 從 118px 撐到 179px、
       蓋住內容 67px」。rail 不再是頂 bar，這個理由消失。
       今天已陸續移除三個回後台入口，手機目前**沒有任何回後台路徑**（machine_backup
       那顆沒有路由，打不到），這一步同時把它補回來。
   ---------------------------------------------------------------------------- */

/* ① 兩顆新元素的基準：桌機一律不存在。
      ★整份 §S-114 只有這一條在 media query 之外，而它只碰本節新增的 class。 */
.pos-mobile-menu-toggler,
.pos-drawer-backdrop,
.pos-menu-close {
    display: none;
}
/* 返回鈕的文字標籤（blade 新增的 `<span class="pos-rail-back-text">`）只在手機出現：
   桌機 rail 收合態只有 76px，塞不下四個字，而且桌機那顆的外觀已由 §S-92／§S-113 定案。 */
.pos .pos-menu .pos-rail-back .pos-rail-back-text {
    display: none;
}

@media (max-width: 991.98px) {

    /* ② 主區只留資料：頂部保留 52px 漢堡條（原 112px）。
          兩個選擇器逐字取自 §POS-1 與 §S-94，同分靠檔尾順序取勝（見章節註解「三」）。 */
    .pos.pos-with-menu,
    .pos.pos-customer:has(> .pos-menu) {
        padding-top: 3.25rem;              /* 52px = 44px 觸控鈕 + 上下各 4px */
    }

    /* 標題列改停在漢堡條下緣，不要 sticky 到 y=0 之後被漢堡鈕壓住。
       §S-94 `.pos .pos-page-header{top:0}` (0,2,0)，本條同分靠順序勝。 */
    .pos .pos-page-header {
        top: 3.25rem;
    }

    /* ═══ ③ rail：頂部橫向 bar → 左側 off-canvas 抽屜 ═══
          §POS-2 手機那段給的是 left:0/right:0/bottom:auto + border-bottom；
          §S-38 手機那段給的是 position:fixed。兩條都是 (0,2,0)，本條同分在檔尾 ⇒ 勝。
          ★不重新宣告 position:fixed —— 基準 §POS-2 已經是 fixed，這是「純 CSS 就能
            改成抽屜」的關鍵前提（實測 computed position=fixed）。 */
    .pos .pos-menu {
        right: auto;
        bottom: 0;
        width: 16.25rem;                   /* 260px：容得下「自動包裝機」5 字 x 14px 不換行 */
        max-width: 84vw;                   /* 320px 小機也留得住右側 16% 讓使用者看見底下有東西 */
        display: flex;
        flex-direction: column;
        z-index: 1023;                     /* 見章節註解「四」 */
        padding-left: 0;                   /* 桌機那 15px 是 rail 專用內距，抽屜自己排 */
        border-bottom: 0;                  /* 蓋掉 §POS-2 手機的下框線 */
        border-right: 1px solid hsl(var(--border));
        box-shadow: .25rem 0 1.5rem hsl(var(--foreground) / .18);
        transform: translateX(-100%);
        transition: transform .2s ease;
        overscroll-behavior: contain;      /* 抽屜捲到底不要把底下的主區一起帶走 */
    }
    .pos.pos-rail-mobile-toggled .pos-menu {
        transform: none;
    }

    /* ④ logo 區：頂 bar 時代 §POS-2 把 `.logo a` 改成 flex（MYPOS 與時鐘並排），
          直式抽屜要回到桌機的直排。(0,4,0) / (0,5,0) 同分靠順序勝。 */
    .pos .pos-menu .logo {
        padding: .9375rem .9375rem .5rem;
    }
    .pos .pos-menu .logo a {
        display: block;
        text-align: center;
    }

    /* ⑤ 頁籤：橫向可捲 bar → 直排清單。
          §POS-2 手機那段 (0,4,0) 給的 display:flex / overflow-x:auto / border-top 全部收回。 */
    .pos .pos-menu .nav-container {
        overflow-y: auto;                  /* 手機 data-skip-mobile="true" ⇒ PerfectScrollbar 不接手 */
        overflow-x: hidden;
        -webkit-overflow-scrolling: touch;
    }
    .pos .pos-menu .nav.nav-tabs {
        display: block;
        flex-wrap: initial;
        padding: 0 .5rem;
        overflow-x: visible;
        overflow-y: visible;
        border-top: 0;
    }
    .pos .pos-menu .nav.nav-tabs .nav-item {
        padding: .1875rem 0;
    }
    .pos .pos-menu .nav.nav-tabs .nav-item + .nav-item {
        margin-left: 0;                    /* 蓋掉 §POS-2 手機橫排的 .46875rem */
    }
    /* §S-37POS 手機那段 (0,5,0) 給 flex-direction:row / min-height:44px / padding。
       本條同選擇器形狀、同分靠順序勝：左對齊、48px 觸控、字級拉到 14px（抽屜有 260px 可用）。 */
    .pos .pos-menu .nav.nav-tabs .nav-item .nav-link {
        justify-content: flex-start;
        min-height: 48px;
        padding: .5rem .75rem;
        font-size: .875rem;
        font-weight: 600;
    }

    /* ⑥ rail 頁腳：§S-38 手機把它絕對定位貼在 logo 列右端（那是頂 bar 的權宜做法）
          → 抽屜裡回到流排、靠 margin-top:auto 沉到底。(0,4,0) 同分靠順序勝。 */
    .pos .pos-menu .pos-menu-footer {
        position: static;
        top: auto;
        right: auto;
        height: auto;
        display: flex;
        flex-direction: row;
        align-items: center;
        gap: .375rem;
        margin-top: auto;
        padding: .5rem .9375rem .75rem;
        border-top: 1px solid hsl(var(--border));
    }
    /* 主題鈕：§S-38 手機把它壓成 44x44 純圖示（頂 bar 沒有橫向空間）。
       抽屜裡有 260px ⇒ 恢復成「圖示 + 文字」整列鈕，與抽屜內其他項目同語彙。 */
    .pos .pos-menu .pos-theme-toggle {
        flex: 1 1 auto;
        flex-direction: row;
        width: auto;
        min-height: 44px;
        padding: 0 .75rem;
        gap: .5rem;
    }
    .pos .pos-menu .pos-theme-toggle .pos-theme-toggle-text {
        display: inline;                   /* §S-38 手機那條 display:none 同分靠順序讓位 */
    }

    /* ⑦ 返回後台（blade 端把 d-none d-lg-flex 換成 d-flex 才會進來這裡）：
          抽屜裡做成整列鈕 + 文字，才說得清楚它是「離開 POS 回後台」。 */
    .pos .pos-menu .pos-rail-back {
        /* 右邊 3rem 是讓給 ⑧ 的關閉鈕（48px），兩顆的可點區才不重疊 */
        padding: .75rem 3rem .25rem .9375rem;
    }
    .pos .pos-menu .pos-rail-back .btn {
        width: 100%;
        gap: .375rem;
    }
    .pos .pos-menu .pos-rail-back .pos-rail-back-text {
        display: inline;
        font-size: .875rem;
        font-weight: 600;
    }
    .pos .pos-menu .pos-rail-sep {
        margin-left: .9375rem;
        margin-right: .9375rem;
    }

    /* ⑧ 抽屜關閉鈕（新增元素，抽屜右上角）。
          `.pos-menu` 是 fixed ⇒ 它是這顆 absolute 的定位基準。
          與置中的返回鈕不重疊：260px 抽屜裡返回鈕 x≈15..245、這顆 x=212..252 疊在其右端之上，
          所以返回鈕的可點區靠 padding-right 讓位（見下一條）。 */
    .pos .pos-menu .pos-menu-close {
        position: absolute;
        top: .5rem;
        right: .5rem;
        z-index: 1;
        display: flex;
        align-items: center;
        justify-content: center;
        width: 40px;
        height: 40px;
        padding: 0;
        border: 1px solid transparent;
        border-radius: var(--radius);
        background: transparent;
        color: hsl(var(--muted-foreground-on-muted));   /* rail 底是 --muted，照 §S-37／§S-92 ⑩ 的結論 */
        font-family: inherit;                            /* 裸 <button> 不繼承字型（§S-94 已踩過） */
        cursor: pointer;
        transition: background-color .15s linear, color .15s linear;
    }
    .pos .pos-menu .pos-menu-close:hover,
    .pos .pos-menu .pos-menu-close:focus-visible {
        background: hsl(var(--accent));
        color: hsl(var(--accent-foreground));
    }
    .pos .pos-menu .pos-menu-close > i,
    .pos .pos-menu .pos-menu-close > svg {
        width: 20px;
        height: 20px;
        flex: 0 0 auto;
    }

    /* ⑨ 遮罩：只有 rail 抽屜開著才存在（紀錄抽屜是 inset:0 全螢幕，不需要）。 */
    .pos.pos-rail-mobile-toggled .pos-drawer-backdrop {
        display: block;
        position: fixed;
        top: 0;
        right: 0;
        bottom: 0;
        left: 0;
        z-index: 1022;
        border: 0;
        padding: 0;
        background: hsl(var(--foreground) / .5);
        cursor: pointer;
    }

    /* ═══ ⑩ 左漢堡：開 rail 抽屜 ═══
          ★色彩語彙逐字照抄 §POS-7 的 FAB（`--dark` 實心底 + `--dark-foreground` 前景 +
            hover 翻成 `--foreground`/`--background`）：兩顆漢堡必須長得一樣，而右邊那顆
            的 color 被 (1,7,1) 的連結色守門鎖死在 `--dark-foreground`（見章節註解「七」）
            ⇒ 只能左邊順著右邊，不能右邊順著左邊。 */
    .pos-mobile-menu-toggler {
        position: fixed;
        top: .25rem;
        left: .5rem;
        z-index: 1018;                     /* 見章節註解「四」 */
        display: flex;
        align-items: center;
        justify-content: center;
        width: 44px;                       /* POS 現場觸控下限 44px */
        height: 44px;
        padding: 0;
        border: 1px solid hsl(var(--dark));
        border-radius: var(--radius);
        background: hsl(var(--dark));
        color: hsl(var(--dark-foreground));
        font-family: inherit;              /* ★裸 <button> 不繼承字型（§S-94 已踩過） */
        box-shadow: 0 .125rem .5rem hsl(var(--foreground) / .22);
        cursor: pointer;
        transition: background-color .15s linear, color .15s linear;
    }
    .pos-mobile-menu-toggler:hover,
    .pos-mobile-menu-toggler:focus-visible {
        background: hsl(var(--foreground));
        color: hsl(var(--background));
    }
    .pos-mobile-menu-toggler > i,
    .pos-mobile-menu-toggler > svg {
        width: 22px;
        height: 22px;
        flex: 0 0 auto;
    }

    /* ═══ ⑪ 右漢堡：既有 FAB 原地改造（零 blade 改動，見章節註解「七」）═══
          §POS-7 原值：bottom:60px right:12px 60x60 圓角 9999px、--dark 實心藥丸。
          改成與左漢堡對稱的 44x44 方鈕。
          ★★**只改幾何**：`background` / `color` / `border` / `:hover` 一條都不宣告，
            §POS-7 的深色語彙原樣沿用（理由見章節註解「七」的白配白事故）。
          ★不動 `display`（§POS-7 手機那條 `display:flex` 照舊）。 */
    .pos-mobile-sidebar-toggler {
        top: .25rem;
        right: .5rem;
        bottom: auto;
        left: auto;
        z-index: 1018;
        width: 44px;
        min-width: 44px;                   /* 蓋掉 §POS-7 的 min-width:60px */
        height: 44px;
        padding: 0;
        margin: 0;                         /* Bootstrap `.h5` 帶 margin-bottom:.5rem */
        border-radius: var(--radius);      /* 蓋掉 9999px 藥丸 */
        box-shadow: 0 .125rem .5rem hsl(var(--foreground) / .22);   /* 原值是為底部 FAB 調的 0 .5rem 1.25rem */
        font-size: 0;                      /* ★吃掉三頁寫死的「紀錄」文字節點；保留無障礙名稱 */
    }
    /* 三條槓：照本檔 `.pos-i-*` 的既有慣例用 mask-image + currentColor
       （Lucide `menu`：M4 12h16 / M4 6h16 / M4 18h16，與左漢堡的 <i data-lucide="menu"> 同一張圖）。 */
    .pos-mobile-sidebar-toggler::before {
        content: "";
        width: 22px;
        height: 22px;
        flex: 0 0 auto;
        background-color: currentColor;
        mask-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='M4 12h16'/%3e%3cpath d='M4 6h16'/%3e%3cpath d='M4 18h16'/%3e%3c/svg%3e");
        -webkit-mask-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='M4 12h16'/%3e%3cpath d='M4 6h16'/%3e%3cpath d='M4 18h16'/%3e%3c/svg%3e");
        mask-repeat: no-repeat;
        mask-position: center;
        mask-size: contain;
        -webkit-mask-repeat: no-repeat;
        -webkit-mask-position: center;
        -webkit-mask-size: contain;
    }
    /* checkout / darka / _package 的購物籃 SVG：讓位給上面那三條槓，markup 一個位元組不動 */
    .pos-mobile-sidebar-toggler .img {
        display: none;
    }
    /* badge 會繼承到 font-size:0，明確給回；44px 方鈕上往外挪一點才不壓到三條槓 */
    .pos-mobile-sidebar-toggler .badge {
        top: -.375rem;
        right: -.375rem;
        width: 18px;
        height: 18px;
        font-size: .625rem;
        line-height: 1;
    }

}
/* --- S-114 end ------------------------------------------------------------- */

/* --- S-112. `.shadow-lg` 收成官方值（2026-08-10）----------------------------------
   業主裁決（原話）：「遵守全站沒錯」
   ＝ §S-108 收了 `.card` 與 `.shadow` / `.shadow-sm` 之後，`.shadow-lg` 這一階
     仍停在 Bootstrap 原生值、全站沒有任何規則接管，本節把它補上。
   ─────────────────────────────────────────────────────────────────────────────
   ★★★ 權威來源（逐字抄自原始碼，不是憑印象）：
     Tailwind v4 token 定義
       https://raw.githubusercontent.com/tailwindlabs/tailwindcss/main/packages/tailwindcss/theme.css
         L410  `--shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);`
     ★同一份檔的 L408 `--shadow-sm` 與 L409 `--shadow-md` 與 §S-108 當時抄的逐字相同，
       ⇒ 兩次抓到的是同一版原始碼，本節沒有偷偷換到別版。

   ═══ ★★★【本節覆蓋的是誰、原值是什麼、為什麼不去改它】═══
     被覆蓋者 ＝ **Bootstrap 5.3 本體**（`public/assets/shadcn/bootstrap/css/bootstrap.min.css`）的
        `.shadow-lg { box-shadow: var(--bs-box-shadow-lg) !important; }`
        其中 `--bs-box-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.175);`
     **原值一字不動地記在上面存查**。那是套件檔，本節不改它一行。
     ⇒ 實測 computed 原值 `rgba(0, 0, 0, 0.176) 0px 16px 48px 0px`
       （0.176 是瀏覽器把 .175 轉成 8-bit alpha 再印回來的結果，不是別的值）。
     ★這一階與官方差的不只是 alpha，是**整個幾何**：
       16px 位移 / 48px 模糊 / 0 收縮  →  官方 10px / 15px / -3px 加第二層 4px / 6px / -4px。
       Bootstrap 那顆是「彈跳視窗用的重陰影」，官方 `--shadow-lg` 是「浮層用的中階陰影」。

   ═══ ★★ CSSOM 實查：全站真的沒有第二條規則碰 `.shadow-lg` ═══
     不是靠 grep 推論的 —— 在真實頁面（登入頁，載入 16 份樣式表、共 10,341 條規則）
     逐條掃 selectorText，命中 `.shadow-lg` 的**只有 Bootstrap 那一條**。
     ★第一次掃回報「零命中」是**我自己的假綠燈**：判斷式寫成 `if (r.cssRules) { 遞迴; return; }`，
       而 Chrome 的 CSSStyleRule 因為支援 CSS 巢狀，**本來就帶一個空的 cssRules**
       ⇒ 每一條普通規則都走進遞迴分支被 return 掉，掃描器一條都沒看。
       改成先認 `selectorText`、再看 `cssRules.length` 才掃得到，
       並且**印出掃過的規則總數當存在性證據**（10,341），零命中才有意義。

   ═══ (1) 為什麼一定要帶 `!important`：**實測，不是算特異度** ═══
     Bootstrap 那條自己帶 `!important`。CSS 規則是「important 宣告永遠贏過一般宣告，
     與特異度無關」⇒ 不帶就不可能贏。但本檔慣例是**量給你看**，所以做了正對照
     （把候選規則 insertRule 進硬碟那份 shadcn-theme.css 的 CSSOM 末端，
       也就是本節文字實際會落的位置，量完 deleteRule 還原）：

       候選寫法                                    bare `.shadow-lg` 的 computed
       ────────────────────────────────────────────────────────────────────────
       （不加任何規則）                            rgba(0,0,0,0.176) 0px 16px 48px 0px
       `.shadow-lg.shadow-lg { … }`   不帶         rgba(0,0,0,0.176) 0px 16px 48px 0px  ← **完全沒效果**
       `.shadow-lg.shadow-lg { … !important }`     rgba(0,0,0,0.1) 0px 10px 15px -3px, rgba(0,0,0,0.1) 0px 4px 6px -4px  ✅
       `.shadow-lg { … !important }`（單層）       同上 ✅
       （全部移除後）                              回到 rgba(0,0,0,0.176) 0px 16px 48px 0px  ← 還原確認

     ⇒ 不帶 `!important` 是 **0 效果**，三個受測元素一個都沒動。
       這與 §S-107 / §S-108 「對手自己帶 !important 才非加不可」是同一條理由。

   ═══ (2) 為什麼用雙層 class 而不是單層：單層也贏，但贏得比較脆 ═══
     上表 D 行證明單層 `.shadow-lg{…!important}` 也有效 —— 它靠的是**檔案順序**
     （本檔排在 bootstrap.min.css 後面，同特異度同 important 時後者勝）。
     雙層 `.shadow-lg.shadow-lg` (0,2,0) 是**對特異度嚴格勝出**，不依賴順序。
     ★沿用 §S-108 已落地的 `.shadow.shadow` / `.shadow-sm.shadow-sm` 同一個寫法，
       三階從此是同一種形狀，不要三階三種寫法。

   ═══ (3) ★★★ 這不只是「換個值」：`.btn` 上那顆陰影會從**沒有**變成**有** ═══
     ★★這是本節唯一一處會讓畫面**多出東西**的地方，必須寫下來。
     本檔另有一條 `.btn { box-shadow: none !important; }`（(0,1,0) 帶 !important，
     就是 §S-30 查出來「全站鍵盤焦點環消失」的那一條）。它排在本節前面、
     與 Bootstrap 的 `.shadow-lg` 同特異度同 important ⇒ **今天 `.btn.shadow-lg` 實測是 `none`**，
     也就是說**作者寫了 `shadow-lg` 但它一直是死的**。
     本節 (0,2,0) 會贏過它 ⇒ 那顆按鈕開始有官方陰影。

     ★**這不是本節發明的新行為，是把 `.shadow-lg` 接回它兩個兄弟已經在做的事**：
       實測今天（本節落地前）—— `.btn.shadow-sm` 與 `.btn.shadow` 都**已經**是
       `rgba(0,0,0,0.1) 0px 1px 3px 0px, rgba(0,0,0,0.1) 0px 1px 2px -1px`，
       不是 `none`。§S-108 用雙層寫法時就已經越過那條 `.btn{none}` 了。
       ⇒ 維持現狀（讓 `.shadow-lg` 繼續被 `.btn{none}` 吃掉）才是三階不一致。
     ★要**刻意**讓按鈕維持沒有陰影的話，得寫 `.shadow-lg:not(.btn)` —— 那是新增一條
       業主沒有裁示過的例外，而且會把 `.btn{box-shadow:none}` 這條本專案自己認定
       是病灶的規則就地合法化。**本節不寫**，改為列出來讓上游決定。

   ═══ (4) blast radius：view 端實際命中 **2 處**，都在同一支檔 ═══
     `find resources -type f -exec grep -Hn 'shadow-lg' {} \;` 共 7 個命中，
     其中 5 個在 Blade 註解內（逐個開檔確認過，是在描述「原本長什麼樣」或抄 shadcn 的
     className 字串），**真正輸出到 HTML 的只有 2 個**：
       · `resources/views/AiTool/alpha/index.blade.php:246`
         `<div class="dropdown-menu p-3 shadow-lg rounded-3" style="width:300px;">`
         實測 `rgba(0,0,0,0.176) 0px 16px 48px 0px` → 官方值
         ★順帶：本檔給 `.dropdown-menu` 的預設陰影是
           `0 8px 24px -6px rgb(0 0 0 / 0.12), 0 2px 6px -2px rgb(0 0 0 / 0.08)`（無 !important），
           今天就已經被 Bootstrap 的 `.shadow-lg` 蓋掉了 ⇒ 本節換的是**已經被蓋掉的那個位置**，
           沒有多壓掉任何一條原本生效的規則。
       · `resources/views/AiTool/alpha/index.blade.php:1135`
         `<button class="btn btn-sm btn-dark px-4 view-btn d-none shadow-lg …">`
         實測 `none` → 官方值（就是上面 (3) 那顆）
     ★兩處都在 `ai-tool/alpha`，路由掛 `can:ai-tool.alpha`（`routes/web.php:1094`）⇒ 需登入才看得到。
     ★★**本節沒有在那支真實頁面上量過**，原因是它在登入牆後面，
       而這台機器的瀏覽器是全機共用、業主可能正登著 ⇒ 不去踢他的 session。
       替代做法：在**載入同一組樣式表**的登入頁（vendor.min.css → bootstrap.min.css → …
       → shadcn-theme.css，與後台頁完全同一條鏈）上，用**逐字相同的 class 字串**
       建真元素量 computed。這量到的是同一場串聯競爭，但**不是那兩顆真元素本身**。

   ═══ (5) ★ 不在本節處理，只列出來（裁示涵蓋不到的地方不要沿用裁示）═══
     · `--shadow-md`（官方 `0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)`）：
       **view 端 `.shadow-md` 命中 0 處**（Bootstrap 5.3 根本沒有出這個工具類，
       它只有 sm / 無 / lg 三階）⇒ 沒有「沒人接管的工具類」這個問題。
       但本檔有兩處**手寫了 md 這一階的語意卻沒用官方值**，屬於另一個題目：
         L7578-7579  Popover 實測 `rgba(0,0,0,.25) 8px 24px`，註解自陳「比 shadow-md 重很多」，
                     且自陳 shadcn 的 Popover 規格是 `rounded-md + border + shadow-md`
         L11921      卡片 hover 註解自陳對應「shadcn 的 hover:shadow-md」
       ⇒ 這兩處要不要收成官方 `--shadow-md`，**業主的「遵守全站沒錯」談的是 `.shadow-lg`**，
         沒被端上桌 ⇒ **本節不動，請上游決定**（同 §S-108 當時對 `.shadow-lg` 的處理法）。
     · `.shadow-xl` / `.shadow-2xl` / `.shadow-inner`：Bootstrap 沒出、view 端 0 命中 ⇒ 不存在的問題。
     · §S-108 留下的「卡中卡 `.card .card`」與「4 處把 xs 註解成 sm 的過時命名」兩筆待辦，
       本節一樣不碰，仍掛在 §S-108 名下。
   ------------------------------------------------------------------------- */

/* ── `.shadow-lg` 工具類 = Tailwind v4 `--shadow-lg` 逐字值 ── */
.shadow-lg.shadow-lg { box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1) !important; }
/* --- S-112 end ------------------------------------------------------------- */

/* ═══ §S-112 附記：訂正上面 (5) 對 `--shadow-md` 的描述（2026-08-10，同日同一輪）═══
   ★★★ 上面 (5) 寫「本檔有兩處手寫了 md 這一階的語意**卻沒用官方值**」——
       **這句話是錯的，就地訂正如下。原句保留不刪，讓後面的人看得到我錯在哪。**

   ── 我怎麼錯的（方法錯，不是手滑）──
     我引用的是 L7578-7579 那段**註解的文字**（「實測陰影 rgba(0,0,0,.25) 8px 24px，
     比 shadow-md 重很多」），**沒有去看它下面那條規則真正宣告了什麼**。
     那段註解描述的是**改造前的舊狀態**，改造後的值就寫在它下面第三行。
     ⇒ 這正是本專案今天已經踩過兩次的同一個坑：**註解裡的數字是歷史，不是現況**。
       要講一個值現在是多少，就得去讀那條宣告本身。

   ── 訂正後的事實（逐行核對過宣告本體）──
     ① L7582 `.daterangepicker`
          `box-shadow: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .1);`
        ＝ 官方 `--shadow-md` **逐字相同**（`.1` 與 `0.1` 是同一個值）。
        ⇒ **它早就是官方值了，不是「沒人接管」**。上面 (5) 把它列進去是錯的。
        ★全檔用到官方 md 幾何 `0 4px 6px -1px` 的**活宣告只有這一條**（其餘命中都在註解裡）。
     ② L11926 `.sc-picker-card:hover`
          `box-shadow: 0 4px 12px -2px rgb(0 0 0 / .10), 0 2px 4px -2px rgb(0 0 0 / .06);`
        它上面的註解自陳對應「shadcn 的 hover:shadow-md」，但值**與官方不同**
        （模糊 12px vs 官方 6px、擴散 -2px vs -1px、第二層 alpha .06 vs .1）。
        ⇒ **(5) 說的「有一處名實不符」只對到這一條，不是兩條。**

   ── 順帶盤到的其他抬升陰影（**全部只登記、一律不動**）──
     L1133 `.dropdown-menu` `0 8px 24px -6px /.12 + 0 2px 6px -2px /.08`
     L1591 / L1674           `0 8px 24px -6px rgb(0 0 0 / 0.25)`
     L1907 `#header .navbar-nav .dropdown-menu`  `0 8px 24px rgb(0 0 0 / .08)`
     L8877                   `0 4px 12px -2px rgb(0 0 0 / .08)`
     這些都是專案自己調的浮層陰影，**沒有任何一條自稱是某個官方階**，
     所以它們不是「沒人接管的工具類」，而是「刻意的自訂值」——性質與 `.shadow-lg` 不同。

   ── 為什麼仍然一條都不收 ──
     業主這一輪的裁決是「遵守全站沒錯」，談的對象是 `.shadow-lg`。
     ★上面這些沒有一個被端上桌。**在裁示涵蓋不到的地方沿用裁示，
       等於把「業主沒反對」當成「業主同意了」** ⇒ 本節到此為止，只回報，不順手收。
   ------------------------------------------------------------------------- */

/* --- S-115. POS 桌機兩側收窄：rail 收合 76→48px、右側欄 340→312px（2026-08-10）------
 *
 * ★★★ 讀本節之前先讀這一句（2026-08-10 兩條施工線共同得出的結論）
 *   官方值是起點不是目標 —— 採用前先實測我們的真實內容放不放得下，
 *   放不下就記錄地板值與理由。
 *   ★偏離官方的理由一定要寫進註解，否則下一個人會把它當成「沒對齊」而好心修回去。
 *   ★為什麼會這樣：shadcn 官方尺寸普遍比我們現在鬆，而我們的內容（中文、時間欄、
 *     統編、長載具號碼）比官方 demo 的英文短字串佔更多空間。
 *     同日另一條線在買家發票頁獨立量到同一個現象：定義列內距對齊官方 py-3 會 +128px、
 *     卡片內距對齊官方 24px 會 +96px。本節則是反方向的同一件事：官方 256px 太窄。
 *
 * ■ 業主原話
 *   「/pos/machine 左邊選單跟右邊紀錄 可以窄一點 讓中間主體大一點」
 *   同時問「shadcn 有類似設計可以參考嗎」，並補「比較寬比較好操作」（指中間主體）。
 *
 * ■ 為什麼只動桌機（≥992px）
 *   手機版（§S-114，≤991.98px）的兩個抽屜是 position:fixed 覆蓋層，浮在主區上、不佔位
 *   ⇒ 抽屜變窄主區不會變大，業主那句因果只在桌機成立。本節整段關在 min-width:992px，
 *   手機的抽屜寬、padding-top:52px、兩顆 44x44 漢堡一個字都沒動。
 *
 * ■ shadcn 官方基準（2026-08-10 抓 upstream 原始碼逐字確認，不是轉述）
 *   shadcn-ui/ui   apps/v4/registry/new-york-v4/ui/sidebar.tsx
 *     const SIDEBAR_WIDTH        = "16rem"    = 256px   展開態
 *     const SIDEBAR_WIDTH_ICON   = "3rem"     =  48px   圖示（收合）態
 *     const SIDEBAR_WIDTH_MOBILE = "18rem"    = 288px   手機抽屜
 *   官方 Sidebar 支援 side="right"，所以右側紀錄面板同樣適用 256px 那一支。
 *   ★官方在 icon 模式把選單鈕壓成 size-8（32px）+ p-2 + size-4 圖示（16px）
 *     ⇒ 48 - 8 - 8 = 32 剛好塞滿。本專案的四顆工具鈕是 18px 圖示 + 8px 內距 x2
 *     + 1px 框 x2 = 36px 寬（§S-113 於 2026-08-10 由業主裁決「四顆一致」定案），
 *     比官方寬 4px —— 下面每一條算式的餘裕都是被這 4px 決定的。
 *
 * ■ 三個下限怎麼算出來的（1600x950 實測，數字全部量過，不是估的）
 *
 *   ⑴ rail 收合態  地板 = 45px（本節取官方 48px）
 *       最寬的內容不是短標、也不是圖示，是頁腳那兩顆工具鈕：
 *         18px 圖示 + 8px 內距 x2 + 1px 框 x2 = 36px（實測 need=36）
 *       rail 是 border-box、右邊有 1px 分隔線，所以
 *         W = 1(框) + 2 x rail 水平內距 + 36
 *       原本 rail 水平內距是 .375rem(6px)（§S-92 ⑨）⇒ W_min = 1 + 12 + 36 = 49px。
 *       ★實測 48px + 6px 內距：可用寬 35px < 36 ⇒ 兩顆鈕被壓成 35px、
 *         「返回後台」那顆 36px 方鈕也溢出（量到 fits=false）—— 只差 1px 就破版。
 *       ⇒ 內距收成 .25rem(4px)：可用寬 = 48 - 1 - 8 = 39px，比 36 多 3px 餘裕，
 *         官方 48px 才站得住。W_min(內距4px) = 1 + 8 + 36 = 45px。
 *       ★短標與圖示都不是瓶頸，實測都放得下（所以「48px 要改成純 icon」不必做）：
 *         nav-item 水平內距 15px x2（§POS-2）在 48px 裡是致命的 30px
 *         ⇒ 收合態改成 0（只改水平，垂直 .328125rem 不動）。
 *         之後 nav-link 內容寬 = 39 - 0 - 2(框) - 12(內距) = 25px
 *           圖示 20px       ✓ 餘 5px
 *           兩字短標 19.65px（「揀包」10px 字，實測 Range 寬）✓ 餘 5.35px
 *         實測 scrollWidth == clientWidth == 37、短標 offsetHeight 12px 未換行。
 *       ★pos-menu-footer 水平內距也要從 .375rem 收成 0，否則鈕只剩 27px。
 *
 *   ⑵ rail 展開態  地板 = 208px（本節不動，維持 §S-92 的 210px）
 *       最寬的內容是頁腳那一排（§S-113 之後）：
 *         收合選單鈕 = 18 圖示 + 6 gap + 48（「收合選單」4 字 x 12px）+ 16 內距 + 2 框 = 90
 *         主題鈕     = 18 圖示 + 6 gap + 24（「主題」2 字 x 12px）    + 16 內距 + 2 框 = 66
 *         中間 gap 6 ⇒ 一排需要 90 + 6 + 66 = 162px
 *       W = 1(框) + 15(rail 左內距) + 30(footer 內距 x2) + 162 = 208px
 *       ★實測 209px 仍過（可用 163 >= 162），現值 210px 只比地板高 2px
 *         ⇒ 展開態**已經在地板上，沒有可收窄的空間**。
 *       ★★★**210 不是隨手取的整數，它就是頁腳那一排的下限**（208 + 2）。
 *         日後有人想「順手」把它改小，先回來看上面那道算式。
 *       ★★★**通用判準（2026-08-10 兩條施工線共同結論）**：
 *         看到一個「不整齊」的數字（210、312、308、76），**先假設它是算出來的，去找算式**；
 *         找不到算式再當成隨手取的。反過來看到 200、300 這種整數，
 *         才比較可能是沒依據的。★這條就是為了保護 210 不被好心砍掉。
 *       ★★官方 SIDEBAR_WIDTH 256px **我們刻意不採**：那是把 rail **變寬 46px**，
 *         而業主要的是主區變大 —— 方向相反。這一項的偏離是「我們比官方窄」，
 *         不是「放不下」：實測 256px 一切正常（收合選單鈕撐到 138px），
 *         §S-113 的四顆鈕在 210px 與 256px 下都維持 32px 高、四顆一致、不切字。
 *
 *   ⑶ 右側欄  地板 = 308px（本節取 312px）
 *       ★真兇不是頁腳，是 #pos-table 裡那個「不能換行」的時間欄。
 *       (a) 頁腳那一排（「全部登出」/「暫停」平分 + 收合鈕）：
 *             全部登出 = 12 內距 x2 + 1.5 框 + 18 圖示 + 8 gap + 55.3 文字 = 106.8（實測 need）
 *             暫停     = 同式 = 79.2
 *             兩顆是 flex:1 1 0% ⇒ 要「平分」就得兩邊都 >= 106.8 那顆的需求
 *             一排 = 106.8 x2 + 10(me-10px) + 8(ms-2) + 36(收合鈕) = 267.6
 *             W = 1(框) + 30(footer 內距 x2) + 267.6 = 298.6 -> 299px
 *       (b) 頁籤兩個分段：105.2 + 85.3 + 6(fb-tabs 內距) + 16(nav 內距) + 1 = 213.5px
 *       (c) #pos-table 的列（空狀態量不到，用 package.blade.php 的 tableRow 模板注入假資料）：
 *             .col-3.total-price 放 checked_at；「09:05:44」實測 66.41px、white-space:normal
 *             但 UAX#14 的 ":" 在數字之間**沒有斷行機會** ⇒ 放不下就直接溢出蓋到左欄。
 *             col-3 = 列寬的 25%，列寬 = W - 1 - 40.5(pos-table 內距 20.25 x2)
 *             需要 0.25 x (W - 41.5) >= 66.41  ⇒  W >= 307.1  ->  308px
 *       ⇒ 地板 = max(299, 213.5, 308) = 308px。實測 308 時 col-3 = 66.63 >= 66.41（餘 0.22px），
 *         304 與 300 都量到 col-3 溢出 ⇒ 308 是真的分界。
 *       ★餘裕只有 0.22px 太薄（字型 hinting 或 checked_at 換格式就破），
 *         取 312px：col-3 = 67.63，餘 1.22px；同時 312 = 19.5rem 仍是乾淨值。
 *       ★官方 256px 實測**破版兩處**，所以不採用（業主「比較寬比較好操作」＝在意操作性）：
 *           頁腳：兩顆鈕變成 107.3 / 70（w-70px 觸底），**失去平分**，「暫停」需求 79.7 > 70 溢出
 *           表格：col-3 只剩 53.63，兩筆 8 位時間全部溢出
 *         要走 256px 就得把頁腳文字拿掉或把鈕改小 —— 那正是業主不要的，不做。
 *
 *   ⑷ 側欄頁首長店名：`.title` 早就有防護，真正裸奔的是 `.order`
 *       收窄讓頁首可用寬從 307px 降到 279px ⇒ 順手查了「店名過長會不會撐爆」。
 *       ★★**查證結果推翻了原本的假設**：`.pos-sidebar-header` 是 **grid 不是 flex**
 *         （§S-97 (1) 的 grid-template-areas「back title toggle / back sub toggle」），
 *         而 `> .title` **已經有完整四件套**（§S-97 (1)：min-width:0 + overflow:hidden
 *         + text-overflow:ellipsis + white-space:nowrap，註解自陳「OS truncate 三件套」）
 *         ⇒ **本節對 `.title` 一個字都不加**（重複宣告只會讓下一個人不知道該改哪一處）。
 *       ★真正的洞是 `> .order`（`#user-login-total`），§S-97 (1) 只給了 grid-area
 *         ⇒ 實測 min-width:auto、overflow:visible。
 *       ★雙向實測（312px，44 個中文字注入，實測數字）：
 *           .title  長字串：scrollWidth 696 > clientWidth 279 ⇒ 裁切生效
 *                           頁首 311=311、側欄 311=311 ⇒ **沒撐爆**
 *           .order  長字串：scrollWidth 564 > clientWidth 279，但
 *                           **頁首 scrollWidth 580 > 311、側欄 580 > 311 ⇒ 真的撐爆**
 *           反向（短字串「尚未登入」/「已登入 0 分鐘」）：兩者 scrollWidth == clientWidth
 *                           ⇒ **不會冒出多餘的 ellipsis**（單向驗證會放過這種反向錯誤）
 *           兄弟位移：`.title` left 在四次測試中都是 1305 ⇒ 沒有被推走
 *       ★`min-width:0` 是關鍵且最容易漏的那一行：grid／flex 子項預設 `min-width:auto`
 *         會拒絕縮到內容寬以下，於是 `overflow:hidden` 永遠沒機會作用
 *         ——**看起來加了防護但完全沒生效**。
 *       ★★這個洞**不是本次收窄造成的**（340px 下同樣 44 字也會撐爆），但收窄把餘裕
 *         從 307 縮到 279 ⇒ 同一個風險家族，一併補掉。★實務長度（「已登入 N 分鐘」）
 *         本來就放得下 —— **「目前放得下」是運氣不是設計**，所以補的是結構不是數值。
 *
 * ■ 主區實得（1600x950 四種狀態全部實測）
 *     rail 收合 + 側欄展開（預設）  1184 -> 1240   +56px
 *     rail 收合 + 側欄收合          1524 -> 1552   +28px
 *     rail 展開 + 側欄展開          1050 -> 1078   +28px
 *     rail 展開 + 側欄收合          1390 -> 1390    0（兩個變數都沒動到）
 *
 * ■ 偏離官方是刻意的，理由逐項寫在上面
 *     rail 收合 48px  = 官方 SIDEBAR_WIDTH_ICON，一致（代價：rail 內距 6->4、nav-item 水平內距 ->0）
 *     rail 展開 210px < 官方 256px：官方那個值會讓主區變小，與業主要求相反；210 已是我們的地板+2
 *     側欄 312px      > 官方 256px：官方值會讓頁腳失去平分、8 位時間溢出；308 是實測地板
 *
 * ■ ★★★給下一個人：收窄任一側，兩邊的數字必須同步改
 *   `.pos.pos-with-menu{padding-left}` / `.pos.pos-with-sidebar{padding-right}`
 *   **必須同步改成新的實寬**，含收合態的 `.pos-rail-collapsed.pos-with-menu`
 *   與死頁的 `.pos-customer:has(> .pos-menu)` / `.pos-customer:has(> .pos-sidebar)`。
 *   ★兩邊不一致會多出或少掉一條空白帶（或整條重疊），**而且不會有任何錯誤訊息**
 *   ——`php -l` 過、200、console 乾淨，只有肉眼看得出來。
 *   ★驗收方式：量 `getComputedStyle(.pos).paddingLeft === .pos-menu 實寬`、
 *     `paddingRight === .pos-sidebar 實寬`，四種收合組合都要相等（側欄收合時是 0 === 0）。
 *
 * ■ 兩邊數字必須一起改（漏一邊就出現重疊或空白帶）
 *     rail 實寬 48  <-> .pos.pos-rail-collapsed.pos-with-menu{padding-left}
 *                   <-> .pos.pos-rail-collapsed.pos-customer:has(> .pos-menu){padding-left}（§S-93 ①）
 *     側欄實寬 312  <-> .pos:not(.pos-sidebar-collapsed).pos-with-sidebar{padding-right}
 *                   <-> .pos:not(.pos-sidebar-collapsed).pos-customer:has(> .pos-sidebar){padding-right}（§S-98 二）
 *     收合態歸零那兩條（§S-94 ④ padding-right:0）刻意不碰，用 :not() 讓開。
 *
 * ■ 特異度：靠分數贏，不用 !important（同 §S-113 的做法）
 *     要贏的既有規則：
 *       §S-92 ⑨ .pos .pos-menu.pos-rail--collapsed{width;padding}                      (0,3,0)
 *       §S-92 ⑨ .pos.pos-rail-collapsed.pos-with-menu{padding-left}                    (0,3,0)
 *       §S-92 ⑨ .pos .pos-menu.pos-rail--collapsed .pos-menu-footer{padding}           (0,4,0)
 *       §POS-2  .pos .pos-menu .nav.nav-tabs .nav-item{padding}                        (0,4,0)
 *       §S-93 ① .pos.pos-rail-collapsed.pos-customer:has(> .pos-menu){padding-left}    (0,4,0)
 *       §S-98 二 .pos .pos-sidebar:not(.pos-sidebar--collapsed){width}                 (0,3,0)
 *       §S-98 二 .pos:not(.pos-sidebar-collapsed).pos-with-sidebar{padding-right}       (0,3,0)
 *       §S-98 二 .pos:not(.pos-sidebar-collapsed).pos-customer:has(> .pos-sidebar)      (0,4,0)
 *       §S-97(1) .pos .pos-sidebar .pos-sidebar-header.pos-sidebar-header > .order     (0,4,0)
 *     本節一律用 .pos.pos.pos 前綴墊到 (0,5,0) 起跳，最深那條 (0,8,0)，b 欄直接贏。
 *     ★不靠「同分排後面」：只要有人往檔尾再追加一條同分規則就會被無聲翻盤。
 * ---------------------------------------------------------------------------- */

@media (min-width: 992px) {
    /* ① rail 收合態 76 -> 48px（官方 SIDEBAR_WIDTH_ICON = 3rem）。
          內距 .375rem -> .25rem 是被 36px 工具鈕逼出來的，見上方算式 ⑴。 (0,5,0) */
    .pos.pos.pos .pos-menu.pos-rail--collapsed {
        width: 48px;
        padding-left: .25rem;
        padding-right: .25rem;
    }

    /* ② 收合態 nav-item 水平內距 .9375rem(15px) -> 0。
          ★只動水平：垂直 .328125rem 是 §POS-2 的行距語彙，一個字不改。 (0,8,0) */
    .pos.pos.pos .pos-menu.pos-rail--collapsed .nav.nav-tabs .nav-item {
        padding-left: 0;
        padding-right: 0;
    }

    /* ③ 收合態頁腳水平內距 .375rem -> 0，否則兩顆 36px 鈕只剩 27px。
          ★padding-bottom（§S-38 的 .75rem）不寫 ⇒ 保留。 (0,6,0) */
    .pos.pos.pos .pos-menu.pos-rail--collapsed .pos-menu-footer {
        padding-left: 0;
        padding-right: 0;
    }

    /* ④ 主區讓位 = rail 收合實寬 48px。兩支活頁面 + .pos-customer 三支死頁都要。 */
    .pos.pos.pos.pos-rail-collapsed.pos-with-menu {
        padding-left: 48px;                                  /* (0,5,0) */
    }
    .pos.pos.pos.pos-rail-collapsed.pos-customer:has(> .pos-menu) {
        padding-left: 48px;                                  /* (0,6,0) */
    }

    /* ⑤ 右側欄 340 -> 312px（地板 308，留 1.22px 餘裕）。
          ★沿用 §S-98 的 :not() 寫法讓開 §S-94 ④ 的 width:0，順序再變也撞不到。 (0,5,0) */
    .pos.pos.pos .pos-sidebar:not(.pos-sidebar--collapsed) {
        width: 312px;
    }

    /* ⑥ 主區讓位 = 側欄實寬 312px。 */
    .pos.pos.pos:not(.pos-sidebar-collapsed).pos-with-sidebar {
        padding-right: 312px;                                /* (0,5,0) */
    }
    .pos.pos.pos:not(.pos-sidebar-collapsed).pos-customer:has(> .pos-sidebar) {
        padding-right: 312px;                                /* (0,6,0) */
    }

    /* ⑦ 側欄頁首副標長度失控時不要撐爆側欄（見上方 ⑷）。
          ★只打 `.order`：`.title` 的同款防護 §S-97 (1) 已經有了，本節不重複宣告。
          ★`min-width:0` 是關鍵那一行 —— grid 子項預設 min-width:auto 會拒絕縮到
            內容寬以下，少了它 overflow:hidden 永遠不會作用（加了等於沒加）。
          ★white-space:nowrap 是既有值（實測 computed 已是 nowrap），刻意不重寫。
          要贏 §S-97 (1) 的 `… .pos-sidebar-header.pos-sidebar-header > .order` (0,4,0)
          ⇒ 本條 (0,6,0)。 */
    .pos.pos.pos .pos-sidebar .pos-sidebar-header > .order {
        min-width: 0;
        overflow: hidden;
        text-overflow: ellipsis;
    }
}

/* --- S-115 end ------------------------------------------------------------- */

/* --- S-116. Badge 高度收成 shadcn 官方值：含 icon 的那幾顆 23.195px → 20px（2026-08-10）-------
 *
 * ═══ 一、問題（實測，不是推論）═══
 *
 *   `/store-setting/store`、`/store-setting/store/modify/base-setting/1`、`/hr/user` 三頁量到：
 *     ‧ 純文字 badge          → 20px、border-radius 9999px   ✅（§24 定案值，本節不動）
 *     ‧ 帶勾勾／叉叉的 badge  → **23.195px**、9999px         ❌
 *   同一種元件在同一頁出現兩種高度 ＝ SSOT 破口。
 *   ★這與「表頭置中」那類刻意的風格選擇不同：那是有人決定的，**這個沒有人決定過** ——
 *     它是「沒有替 badge 內的圖示設尺寸上限」漂移出來的副產物。
 *
 * ═══ 二、shadcn 官方原始碼（2026-08-10 以 curl 抓 raw 檔逐字取得，非憑記憶、非引用本檔既有註解）═══
 *
 *   來源：shadcn-ui/ui  apps/v4/registry/new-york-v4/ui/badge.tsx  （main 分支）
 *   badgeVariants 的 base className 逐字：
 *
 *     "inline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-hidden
 *      rounded-full border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap
 *      transition-[color,box-shadow] focus-visible:border-ring focus-visible:ring-[3px]
 *      focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20
 *      dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3"
 *
 *   ★重點一：base 裡**沒有 h-5 之類的固定高度**。官方 badge 的高度是內容撐出來的
 *     （text-xs ＝ font-size .75rem / line-height 1rem，加 py-0.5 與 1px 框）。
 *   ★重點二：**`[&>svg]:size-3`** —— 官方對 badge 內的 svg 有明確尺寸上限：
 *     size-3 ＝ width/height 0.75rem ＝ 12px。而 text-xs 的 font-size 也正好是 0.75rem，
 *     ⇒ **官方的圖示尺寸 ＝ 1em（跟著 badge 字級走）**，且 12px < 官方行框 16px，
 *       所以圖示天生撐不高 badge。
 *   ⇒ 官方達成「固定高度」的手段是 **限制 icon 尺寸**（＋ inline-flex 置中），
 *     **不是**寫死 height。本節照這個做法。
 *
 * ═══ 三、23.195px 是誰撐出來的（逐項算式，量測值皆為 1440 桌機 / Chrome / system-ui）═══
 *
 *   純文字對照組（同一顆 badge 把 svg 拿掉後重量）：
 *     content 高 12.000 ＝ strut ascent 10.5 ＋ strut descent 1.5    （line-height:12px ＝ 1em）
 *     badge 高 20.000  ＝ 12 ＋ padding 3+3 ＋ border 1+1            ✅ 這就是「對的 20px」
 *
 *   含 icon（`<svg class="lucide lucide-circle-check fa-lg">`）：
 *     ⑴ svg 的 font-size 被 **`.app-content .table td .fa-lg { font-size: .95rem }`**（本檔既有，(0,3,1)）
 *        拉到 0.95rem ＝ **15.195px**。★真兇是這條「表格內 fa-lg」的絕對值，不是 badge 自己。
 *        （順帶：vendor 的 `.fa-lg{font-size:1.25em}` 與本檔 `.lucide.fa-lg{font-size:1.25em}`
 *          都是 (0,1,0)，兩條都輸給上面那條，不是生效者。）
 *     ⑵ `.lucide, i[data-lucide] { width:1em; height:1em }`（本檔既有）⇒ svg 盒子 15.195 × 15.195。
 *     ⑶ `.lucide, i[data-lucide] { vertical-align:-0.125em }` ⇒ -1.898px。
 *     ⑷ 於是 svg 在行框裡佔：基線以上 15.195 - 1.898 = **13.297**、基線以下 **1.898**。
 *     ⑸ 行框 ＝ max(strut 10.5, svg 13.297) ＋ max(strut 1.5, svg 1.898)
 *              = 13.297 ＋ 1.898 = **15.195**
 *     ⑹ badge 高 ＝ 15.195 ＋ 3+3 ＋ 1+1 = **23.195**   ✅ 與實測 23.195 完全吻合
 *
 *   ⇒ 溢出的 3.195px **全部**是 icon 造成的：上緣多 2.797、下緣多 0.398。
 *     ★不是 line-height 的錯、不是 align-items 的錯、不是 padding 的錯 ——
 *       量到的 line-height / padding / border 在兩種 badge 上**逐位相同**。
 *
 * ═══ 四、本節怎麼改 ═══
 *
 *   把 badge 內的 svg 收回官方的 `size-3` 語意（＝ 1em，跟著 badge 字級走）：
 *     font-size:1em      → 撤掉 `.app-content .table td .fa-lg` 的 .95rem 絕對值，回到 badge 的 12px
 *     width/height:1em   → 12 × 12（`.lucide` 本來就寫 1em，這裡只是讓它吃到正確的 em 基準）
 *     vertical-align:-0.125em → -1.5px
 *   ⇒ svg 在行框裡佔：基線以上 12 - 1.5 = 10.5、基線以下 1.5
 *   ⇒ 行框 ＝ max(10.5, 10.5) ＋ max(1.5, 1.5) = **12.000**（與純文字 badge 的 strut 完全重合）
 *   ⇒ badge 高 ＝ 12 ＋ 6 ＋ 2 = **20.000**   ✅ 實測 29 顆含 svg 的 badge，高度集合收斂成單一值 {20}
 *
 *   ★為什麼 `vertical-align` 要重述一次（`.lucide` 已經有同值）：
 *     那條只打 `.lucide` / `i[data-lucide]`。badge 裡若放的是**沒有 .lucide 的裸 svg**，
 *     它吃 bootstrap 的 `img, svg { vertical-align: middle }`，實測 badge 會變 **21.32px**（改前 32px）。
 *     本節重述後該顆同樣收到 20.000px。⇒ 這行不是複製，是讓本節對「所有 svg」都成立。
 *
 * ═══ 五、三個「刻意不做」（都有實測依據，不是省略）═══
 *
 *   ⑴ **不寫 `.badge { height: 20px }`**。官方沒有這個手段；寫死高度會在字級被放大
 *      （例如 `.app-header .navbar-nav .badge{font-size:10px}` 那組、或未來的 badge 尺寸階）
 *      時壓到文字或溢出，而且會把「為什麼是 20」這個資訊藏起來。
 *
 *   ⑵ **不動 `display`**（官方 base 是 `inline-flex items-center justify-center gap-1`）。
 *      理由是實測出來的，不是保守：把 `.badge` 改成 `inline-flex` 並加 `!important` 的那組實驗
 *      **讓兩顆本來隱藏的 badge 現形** ——
 *        ‧ `#notification-badge`（inline style `display:none`）→ 變 flex，16px 可見
 *        ‧ 空的 `.badge.bg-primary`（吃 BS 的 `.badge:empty{display:none}`）→ 變 inline-flex，6px 可見
 *      ⇒ **display 是這支檔裡最敏感的屬性**，而本節的目標（高度）不需要它。
 *      ★不加 `!important` 的 inline-flex 版本測起來也是 20px、也不會讓隱藏的現形，
 *        但它會讓每顆含 icon 的 badge 寬度 -0.109px、位置上移 0.75px（whitespace 被 flex 吃掉、
 *        改由 gap 補），**對照組以外還是動到了東西，換來的好處是 0**（高度兩者都已是 20.000）。
 *      ★它唯一的額外價值是「不依賴字型 metrics」：本節的 20.000 成立於
 *        strut ascent:descent = 0.875em:0.125em 剛好等於 icon 的 `vertical-align:-0.125em`。
 *        若日後換字型導致飄移（估計 < 0.3px），再回來加 `align-items:center` 即可，
 *        屆時請連同上面兩顆隱藏 badge 一起重測。
 *
 *   ⑶ **不打 `> i`**（＝ Font Awesome 字型圖示）。實測：badge 裡的 `<i class="fa fa-check fa-lg">`
 *      **改前就已經是 20px** —— 因為 `.fa-lg` 給了它 `line-height:.05em`，盒高只有 0.75px，
 *      對行框零貢獻。若照 svg 那樣硬塞 `line-height:1`，反而把它撐成 **21.5px**。
 *      ⇒ 「同一條規則同時服務兩種圖示」在這裡是錯的，兩種圖示的失效機制不同。
 *
 * ═══ 六、特異度：靠分數贏，不用 !important ═══
 *
 *   要贏的對手（本檔既有）：`.app-content .table td .fa-lg`  ＝ (0,3,1)
 *   本節 `.badge.badge.badge.badge > svg`                    ＝ (0,4,1)  ⇒ 贏，且**不依賴來源順序**
 *   （沿用本檔既有的 `.pos.pos.pos` 重複類別寫法；不用來源順序是因為它只要有人往檔尾再追加
 *     一條同分規則就會靜靜翻盤，見 §S-113 同一段推理。）
 *
 *   ★A/B 對照（2026-08-10 實測，hr/user 39 顆 badge）：
 *     A 不帶 `!important` / B 帶 `!important` ⇒ **兩組輸出逐位元組相同**（含高度、寬度、座標、
 *     圓角、svg 幾何）⇒ **不需要 `!important`**。
 *     ★同輪另跑一支誘餌 `.badge…> svg { height:44px }` ⇒ badge 量到 52px、對照組比較器翻紅，
 *       證明「A==B」是真的贏了，不是量測失靈。
 *
 * ═══ 七、改後驗收（2026-08-10 實測）═══
 *
 *   ‧ 含 svg 的 badge 29 顆：高度集合 {23.195, 32} → **{20}**（單一值）
 *   ‧ 不含 icon 的 badge 9 顆（7 顆可見）：高度／寬度／座標／display／圓角 **逐位不變**
 *     ★這在本節是**結構保證**不只是量測結果：本節的選擇器只匹配 `.badge` 的 svg 子元素，
 *       純文字 badge 根本不在匹配集合裡。
 *   ‧ icon 未被裁切：svg 盒 12×12，距 badge 上下緣各 4px（padding 3 ＋ border 1），
 *     badge 自身 `overflow: visible`（官方的 `overflow-hidden` 本節刻意不搬 ——
 *     它對高度無貢獻，卻會裁掉某些 badge 內絕對定位的小圓點）。
 *   ‧ 圓角：全站 badge 仍為 9999px（§24 定案值，本節零改動）。
 */
.badge.badge.badge.badge > svg {
    font-size: 1em;
    width: 1em;
    height: 1em;
    vertical-align: -0.125em;
}
/* --- S-116 end ------------------------------------------------------------- */

/* --- S-117. 買家發票頁定義列分隔線：查證後**不新增任何規則**（2026-08-10）---------
 *
 * ■ 本節零宣告、零選擇器。它是一份「不要照那份修法做」的攔截紀錄，外加一則 §S-99 條文作廢標記。
 *   之所以仍佔一個分節號，是因為那份修法還在流傳，而**照做會讓線跑到卡片外面**（下面附實測值）。
 *
 * ■ 交辦時的病灶描述（1440 實測值，來自同日稍早的一輪）
 *     定義列的線    離卡框內緣 5px   線長 331.33，同卡 card-header 線 339.33（差 8px）
 *     明細表格的線  離卡框內緣 1px   線長 704.66（與同卡 card-header 線一致）
 *   幾何來源：card-body 左右內距 16px ＋ `.row.gx-4` 的負邊距 −12px
 *             ⇒ `.col` 邊框盒左右各離卡框內緣 16 − 12 ＝ 4px。
 *
 * ■ ★複量結果：**上述前提已不成立，病灶已不存在**（2026-08-10 21:06 實測）
 *   `resources/views/buyer/invoice/result.blade.php` 於同日 21:04 改版（**非本節作者所為**，
 *   來自同日另一條工作線），定義列容器改成 `card-body px-0` ＋ `.row.gx-0.gy-0`。
 *   實測：card-body 左右內距 0px、`--bs-gutter-x` 為 0、`.row` 左右邊距 0
 *   ⇒ `.col` 的邊框盒**本來就等於卡片內緣**，不需要任何 CSS 補救。
 *   四個斷點各量一次，四張有定義列的卡（發票資訊／載具 稅務／賣方／買方）結果全部相同：
 *     斷點    卡片內緣寬    定義列線 inset(左/右)    card-header 線    明細表格線
 *     1440    339.33       0 / 0                   0 / 0             0 / 0
 *      992    303.33       0 / 0                   0 / 0             0 / 0
 *      768    354.00       0 / 0                   0 / 0             0 / 0
 *      390    356.00       0 / 0                   0 / 0             0 / 0
 *   ⇒ 同一張卡的三種線（card-header／定義列／表格）現在已經**等長且同起訖**。
 *     業主當初說的「flex 左右線沒有碰到卡片邊緣」「不太一致」，在現況下量不到了。
 *
 * ■ ★★那份修法若照做會發生什麼（**實測，不是推論**）
 *   建議寫法是對同一批 row 補 `margin-left / margin-right: -1rem`，
 *   並對其 `.col` 補 `padding-left / padding-right: 1rem`。
 *   它的前提是「row 帶 24px gutter、card-body 帶 16px 內距」，而這兩者現在都是 0。
 *   把它注入現況實測（768，同一份頁面，注入前後各量一次，中間隔一次事件迴圈）：
 *     定義列線 inset   0 / 0   →   **−16 / −16**
 *   負號代表線**跑到卡框外面**：左緣往外突出 16px、右緣往外突出 16px，
 *   兩張並排的卡之間會看到線直接橫跨過去（已截圖存查）。
 *   文字位置確實零位移（−16 與 +16 互相抵銷，這部分的推論是對的），但線本身壞掉
 *   ⇒ **這份修法在現況下是淨傷害，不可套用。**
 *   ★它不是「寫錯」，是**寫給一個已經不存在的版面**。日後若有人把 blade 改回
 *     帶內距的 `card-body` ＋ `row gx-4`，那份修法才會重新成立 —— 屆時請重量一次再決定。
 *
 * ■ ★為什麼本節不順手改用別的寫法「再對齊一次」
 *   現況 inset 已經是 0，沒有可再縮的空間，任何再加的規則都只會把線推出卡外。
 *   因此：`!important` 的帶／不帶正反對照不適用（沒有規則可帶）；
 *   本節的 blast radius 為零 —— 實測 `Element.matches()` 普查全頁 1745 個元素，
 *   本節新增的匹配數為 0（因為本節沒有選擇器）。
 *
 * ■ 本節**刻意不做**的三件（同輪評估後由業主確認不做）
 *   列高 38 vs 37 與行高 21 vs 20（差 1px，肉眼不可辨，改它影響面大於收益）、
 *   文字起點對齊（會讓定義列整體左移 8px，屬版面決策）、
 *   定義列 hover 高亮（shadcn 官方 key-value 卡沒有 hover，加了會讓唯讀資訊看起來可點擊）。
 *
 * ══ §S-99 條文作廢標記（依「規格改方向、舊條文必須就地標作廢」）══════════════
 *
 * ■ 被標作廢的是 §S-99 註解裡這兩句（★原句一字未刪，仍原樣留在 §S-99 原處）
 *     「反例（本頁實際存在、刻意排除）：「發票資訊」卡的 meta 四格，它下面同卡還接著
 *       「發票明細」小標與表格 ⇒ 它最後一列的線是區段分隔線，不是懸空線，拿掉會讓
 *       「發票明細」失去與上方 meta 的分界」
 *     「實測：本規則在該頁命中 4 張卡（賣方／買方／載具 稅務／會員載具資訊），
 *       「發票資訊」卡正確落選。」
 *
 * ■ 為什麼作廢（實測，2026-08-10 21:06，非推論）
 *   1) 前提已消失：「發票明細」已改成**自己獨立一張卡**（實測它與「發票資訊」卡是兩個不同的
 *      `.card` 節點）。「發票資訊」卡現在只剩 card-header ＋ 一個 `card-body px-0`，
 *      其最後一個 row 就是 meta 區 ⇒ 該 row 最後一條線底下不再接任何東西，
 *      它**就是**懸空線，正該被 §S-99 剪掉。
 *   2) 實測命中名單已經不同：用 §S-99 的原選擇器對全頁 1745 個元素跑 `Element.matches()`，
 *      命中 4 個，所屬卡片是 **發票資訊／載具 稅務／賣方／買方**，
 *      四個都已套上 `clip-path: inset(0px 0px 1px)`。
 *      ⇒ 「發票資訊」卡**沒有落選，它是命中的**；而原句列的「會員載具資訊」根本不是一張卡，
 *        它是「載具 / 稅務」卡裡的一個區塊小標。
 *   ★★這則作廢特別難被發現，因為**筆數仍然是 4**：原句寫「命中 4 張卡」，今天實測也是 4，
 *     換掉的是**成員**不是數量。只核對數字、不核對名單的話，這段錯誤說明可以永遠留在檔案裡。
 *
 * ■ 現況是對的，所以**不必也不要改 §S-99 的規則本體**
 *   §S-99 走的是幾何剪裁（剪掉 row 底部 1px）而不是 DOM 計數，所以版面換了之後它自動跟上、
 *   命中名單自己更新成正確答案。**過時的只有註解，不是規則。**
 *   ⇒ 本節只標註解作廢，§S-99 的宣告與選擇器一個字都不動。
 * ══ §S-99 作廢標記 end ════════════════════════════════════════════════════
 * -------------------------------------------------------------------------- */

/* --- S-117 end ------------------------------------------------------------- */

/* --- S-118. POS 手機版：兩顆漢堡與頁面標題合併成一條 header bar，鈕改 shadcn ghost（2026-08-10）----
   業主原話：「你這手機版 漢堡條左右這樣放也太醜」。
   ★這是「§S-114 做出來的東西不是業主要的東西」的訂正，不是新功能。
     §S-114 交付的是「兩顆浮空的深色方塊 ＋ 底下另一條標題列」＝兩層；
     業主當初要的是「左右都有漢堡條 呈現中間資料就好」＝**一條**工具列。

   ═══ 一、修前實測（375x667，/pos/package，body margin-bottom:96px 是 Debugbar）═══
     .pos                        padding-top 52px（§S-114 ②）
     .pos-mobile-menu-toggler    x=8   y=4  44x44  bg rgb(23,23,23)  color rgb(250,250,250)
                                 border 1px rgb(23,23,23)  radius 10px
                                 box-shadow rgba(0,0,0,.22) 0 2px 8px
     .pos-mobile-sidebar-toggler x=323 y=4  44x44  同上一組值（§S-114 ⑪ 沿用 §POS-7）
     .pos-page-header            x=0 y=52 w=375 h=64  sticky top:52px  z-index:5
                                 bg rgb(250,250,250)  border-bottom 1px rgb(229,229,229)
     h1「揀包出貨」               x=44 y=69.5 20px/700  color rgb(0,0,0)
     標題圖示                     x=16 y=73.5 20x20  color rgb(255,210,0)
     .pos-sidebar-restore        display:none（手機不顯示，★所以它不會跟右漢堡搶位置）
   ⇒ 業主說的「醜」有兩個可量測的成因，兩個都在上面這張表裡：
      (1) y=0..52 這條帶子**沒有任何容器**——`.pos` 的 padding 而已，於是兩顆
          rgb(23,23,23) 的實心方塊（還帶 8px 陰影）直接浮在頁面底色 rgb(250,250,250) 上。
          20.4:1 的明度差 ＋ 投影 ＝ 視覺上是「兩塊貼上去的補丁」，不是介面的一部分。
      (2) 帶子與 y=52..116 的標題列是**兩個分離的層**，各自有底色，中間還有一道
          border-bottom ⇒ 頂部被切成兩塊。

   ═══ 二、上游依據（★逐字查證過，不是憑印象）═══
   (A) shadcn/ui 官方 `apps/v4/registry/new-york-v4/ui/sidebar.tsx`
       （sha d9ab0aa2d49a55a81ce8b2d5ffd4aebddd0f1531）的 `SidebarTrigger` 逐字是：
         variant="ghost"  size="icon"  className={cn("size-7", className)}  <PanelLeftIcon />
       同 registry 的 `ui/button.tsx`（sha 4d38506cee5430d95a59ec6a2a0cef2b79217e7a）裡
       ghost 變體逐字只有一行：
         ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50"
       ★★也就是說 ghost **沒有 background、沒有 border、沒有 box-shadow**，
         只有 hover 才上色。這就是本節要對齊的那一點，也正是修前那兩顆差最遠的地方。
   (B) 官方手機版哲學是**換元件不是縮元件**：`hooks/use-mobile.ts` 的
       MOBILE_BREAKPOINT = 768，`useIsMobile()` 為真時 Sidebar 改渲染成 `<Sheet>`，
       寬度走 SIDEBAR_WIDTH_MOBILE = "18rem"（桌機是 SIDEBAR_WIDTH = "16rem"）。
       ⇒ §S-114 把 rail 從「頂部橫向 bar」改成「左側 off-canvas 抽屜」方向是對的，
         本節不動它；本節只修「頂部那條」的長相。
   (C) 賣賣 Work OS（業主指定的設計基準）`src/components/app/AppHeader.vue:2` 逐字：
         <header class="h-14 shrink-0 bg-background border-b border-border
                        flex items-center justify-between px-4 gap-3">
       同檔 :4-13 的漢堡鈕逐字 `variant="ghost" size="icon" class="md:hidden text-foreground"`
       ＋ `<Menu :size="20" />`；:14 的標題逐字
         <h1 class="text-base font-bold text-foreground truncate">
       ⇒ 抄回來的四個決策：
          1. **一條 bar**，桌機手機共用，`bg-background` + `border-b border-border`、
             **不要陰影**（Work OS 刻意扁平；sidebar 才用 bg-card 拉出層次）
          2. 漢堡鈕 **ghost + 透明底 + 前景色**，圖示比同列其他圖示大一階（20px）
          3. **標題就在同一條 bar 裡、靠左貼著漢堡**（★不是置中）
             ＋ `truncate` 且父層 `min-w-0`（本檔 §S-94 已經有 overflow/ellipsis/min-width:0）
          4. 標題字級 `text-base`（16px）+ `font-bold`
       ★★**只抄設計決策，不抄 Tailwind class 字面**：本專案沒有 Tailwind，
         `h-14` / `shrink-0` / `text-base` / `md:hidden` 在本站全部零定義，抄了什麼都不會發生。
   ★Work OS 的 bar 是 56px（h-14）、鈕是 36px（h-9 w-9）；本節維持 §S-114 的
     **52px bar ＋ 44x44 鈕**，理由見「三」。

   ═══ 三、觸控尺寸的取捨：外觀吃官方、命中區吃 44px（★理由寫下來，別再改回 28）═══
   官方 `size-7` = 28px、Work OS `h-9 w-9` = 36px，兩者都是**桌機／滑鼠**情境。
   POS 是倉庫現場、戴手套、單手持機 ⇒ 命中區維持 44x44（iOS HIG 最小觸控目標，
   §S-86 二(a) 也是這個值）。
   ★這兩件事不衝突：ghost 沒有底色也沒有邊框 ⇒ **放大命中區不會放大任何看得見的方塊**，
     使用者看到的只有 20px 的三條槓，44px 是「摸得到但看不到」的那一圈。
     修前之所以看起來像補丁，是因為那 44px 被畫成了實心底＋邊框＋陰影。
   ⇒ ★★所以**不要**把 44 改成 28 或 36 並自稱在對齊官方：官方對齊的是**外觀**
     （透明底、無邊框、無陰影、hover 才上色），不是那個數字。

   ═══ 四、bar 的表面用 `.pos::before`，不新增任何 DOM（★零 blade 改動）═══
   本節**一支 blade 都不動**（§S-114 已經把兩顆鈕與遮罩放好了），原因：
     (1) 5 頁（package/machine/checkout/darka/_package）已經有 `.pos-page-header`，
         而它在 §S-94 的既有值就是 `background:hsl(var(--background))` +
         `border-bottom:1px solid hsl(var(--border))` —— ★**跟 Work OS 的 bar 配方逐字相同**。
         ⇒ 把它從 sticky(top:52px) 改成 fixed(top:0) 塞進那 52px 的帶子裡，它**自己就是那條 bar**，
           不需要新元素。
     (2) 第 6 頁 `machine_backup.blade.php` **沒有** `.pos-page-header`（實測，§S-114 已記錄）
         ⇒ 那頁沒有東西畫底色，兩顆鈕會再度浮在白底上（＝業主抱怨的原狀）。
         用 `.pos:not(:has(.pos-page-header))::before` 補一條同配方的表面。
     ★★為什麼用 `:not(:has())` 而不是「兩個都畫、反正長得一樣」：
       **深色模式的 `--border` 是 `0 0% 100% / 10%`（半透明白，見 §S-72 一-(3)）**。
       兩條半透明底線疊在同一列像素上 ⇒ 10% 疊成 19%，深色模式下那條線會**明顯變亮**，
       而亮色模式下（`--border` 不透明）完全看不出來 ⇒ 是個「只在深色模式現形」的坑。
       `:not(:has())` 保證同一時間**只有一條** bar 被畫出來。
     ★偽元素在這裡合格，與 §S-114「六」的結論不衝突：那一段講的是**遮罩**，
       它要當 `e.target` 給既有 IIFE 的 `closest('[data-dismiss-class]')` 用，
       偽元素不是事件目標所以不行。本節這條只是一塊**不可點的表面**
       （已給 `pointer-events:none`），沒有事件需求。
     ★`.pos::before` 事前查證：本檔對 `.pos::before` / `.pos:before` **零命中**，沒有搶用。
     ★`position:fixed` 事前查證：`.pos-page-header` 到 `<html>` 的**每一層祖先**
       transform / filter / perspective / will-change / contain **全部是 none/auto**（實測 0 命中）
       ⇒ fixed 一定以視埠為基準，不會被某個祖先偷走定位脈絡。

   ═══ 五、為什麼標題列改 fixed，而不是留著 sticky ═══
   兩顆鈕是 `position:fixed`（§S-114）。標題列若留 sticky，兩者是**兩種不同的定位機制**
   釘在同一條帶子上 —— 一旦哪天某個祖先多了 overflow/transform，sticky 會失效或
   黏在別的容器上，鈕還釘在視埠 ⇒ bar 會**當場裂成兩層**，也就是這次要修的症狀復發。
   改成 fixed 之後三者同一機制、同一基準，**結構上不可能再分離**。
   ★附帶好處（★實測值，機制與我的第一版推測不同，寫清楚免得下一個人誤解）：
     標題列離開排版流之後，`.pos-content-container`（真正的資料區）**高度沒有變**，
     一直是 703px；變的是它的**起點**：y=116 → y=52，整個往上搬了 64px。
       修前  .pos 819 / .pos-content y=52 h=767 / container y=116 h=703
       修後  .pos 755 / .pos-content y=52 h=703 / container y=52  h=703
     ⇒ 在 667px 高的視埠內，資料區**看得見的部分** 551px → 615px（+64px，+11.6%），
       同時整頁可捲高度 819 → 755（少捲 64px）。
     ★★所以正確說法是「資料區往上搬 64px ⇒ 一眼看得到的資料多了 64px」，
       **不是**「資料區長高 64px」。我一開始寫成後者，被實測推翻後改掉。
     這正是業主「呈現中間資料就好」要的。
   ★`.pos` 的 `padding-top:52px`（§S-114 ②）**一個字都不動**：帶子高度不變，
     所以主區起點不變、6 頁的讓位邏輯不變。

   ═══ 六、每一條規則的特異性帳（★同分靠檔尾順序取勝，全節零 !important）═══
     本節 ②  `.pos .pos-page-header`                              (0,2,0)
       vs §S-94 `.pos .pos-page-header`                            (0,2,0) 同分、本節在後 ⇒ 勝
       vs §S-114 `.pos .pos-page-header{top:3.25rem}`              (0,2,0) 同分、本節在後 ⇒ 勝
       vs §S-94 `@media(min-width:768px){.pos .pos-page-header{padding:0 1.5rem}}` (0,2,0)
          ★★這條的媒體查詢與本節的 `max-width:991.98px` **在 768~991.98px 是重疊的**，
            而且它是 `padding` **簡寫** ⇒ 若讓它勝出會把本節的左右 52px 讓位一起歸零，
            標題就會壓在兩顆鈕底下。媒體查詢不影響特異性，同分靠順序 ⇒ 本節勝。
          ★所以本節 ② 也刻意用 `padding` **簡寫**（不是 longhand）：簡寫對簡寫，
            勝負只由順序決定，不會出現「left/right 我贏、top/bottom 它贏」的混合結果。
          ★驗收一定要**同時量 375 與 768 兩個寬度**，否則這個重疊區會漏測。
     本節 ③  `.pos .pos-page-header h1` / `… .pos-page-title`      (0,3,0)
       vs §S-94 同形                                               (0,3,0) 同分、在後 ⇒ 勝
     本節 ④  `.pos-mobile-menu-toggler`                            (0,1,0)
       vs §S-114 ⑩ 同形                                            (0,1,0) 同分、在後 ⇒ 勝
     本節 ⑤  `.pos-mobile-sidebar-toggler`                         (0,1,0)
       vs §POS-7 同形                                              (0,1,0) 同分、在後 ⇒ 勝
     本節 ⑤  `#content.app-content a.pos-mobile-sidebar-toggler:not()x5`  (1,7,1)
       vs 本檔 L5300 的同形連結色守門                              (1,7,1) 同分、在後 ⇒ 勝

   ═══ 七、★★★那條 (1,7,1) 連結色守門：這次是「打贏它」，不是「順著它」═══
   §S-114「七」的結論是「這顆的 color 搶不到 ⇒ 只能順著它用深色實心底」，
   而那個「順著」正是業主現在說醜的東西。★所以本節必須把它打贏。
   守門逐字（本檔 L5300／L5303，`#content.app-content` 實測在 /pos/package 上**存在**，
   class 是 `app-content p-0` ⇒ 守門確實會命中）：
     #content.app-content a.pos-mobile-sidebar-toggler:not(.btn):not(.dropdown-item)
       :not(.nav-link):not(.fb-tab):not(.page-link)        { color: hsl(var(--dark-foreground)) }
     …同一條 :hover                                        { color: hsl(var(--background)) }
   特異性 (1,7,1)（id 1／`.app-content`+`a.` 的 class+5 個 `:not()` 內的 class = 7／`a` 型別 1），
   hover 那條因為多一個 `:hover` 偽類是 (1,8,1)。
   ★★**它有 id ⇒ 任何 (0,x,y) 的選擇器永遠贏不了**（特異性是逐欄比較，id 欄先決）。
     §S-114 的 `.pos-mobile-sidebar-toggler` (0,1,0) 差的不是「一個 id 和六個 class」，
     是**光那一個 id 就已經定案**，補再多 class 也沒用。
   ⇒ 治法：**把守門的選擇器逐字照抄一份**（同一個 id、同一串 `:not()`、同一個 `a.`），
      特異性完全相同 (1,7,1)，而本節在檔尾 ⇒ **同分靠順序取勝**。hover 那條同理照抄成 (1,8,1)。
   ⇒ ★同時再寫一條裸的 `.pos-mobile-sidebar-toggler` (0,1,0)：
      6 頁不保證都有 `#content.app-content` 外殼，**沒有那個外殼的頁面守門也不會生效**，
      這時 (0,1,0) 這條負責上色。
      ★★§S-114 擔心的「有的頁面白、有的頁面黑」的漂移，前提是兩條路徑給**不同的值**；
        本節兩條路徑給的是**同一個 `hsl(var(--foreground))`** ⇒ 不管走哪條，算出來都一樣，
        結構上不可能漂。這就是為什麼「兩條都寫」比「只寫特異性高的那條」正確。
   ⇒ ★**全節不用一個 `!important`**：`!important` 會連帶蓋掉將來任何合法的覆寫
      （例如 §S-73 那族 focus 環要動 box-shadow），而這裡特異性打得贏，沒有理由動用它。

   ★★★打贏它是**必要**不是好看而已 —— 深色模式下「順著它」會直接瞎掉：
     守門給的是 `--dark-foreground`，而那顆在深色是 `0 0% 9%`（rgb(23,23,23)，見 L336）。
     ghost 之後 bar 的底色是 `--background` = `0 0% 3.9%`（rgb(10,10,10)）
     ⇒ 對比 **1.08:1**，三條槓在深色模式下**完全看不見**。
     §S-114「七」記錄的是亮色模式的「白配白」事故；同一條守門在深色模式配上透明底
     就是「黑配黑」—— **同一個坑的鏡像**，而且一樣不會有任何錯誤訊息。
     ⇒ 這就是為什麼本節不能重複 §S-114「順著它」的解法：那個解法與 ghost 不相容。
     實測修後值：深色 icon rgb(250,250,250) 對 bar rgb(10,10,10) = **18.97:1**（見「八」）。

   ═══ 八、亮暗兩個主題都成立（★這是換 ghost 之後必須重新驗的事）═══
   主題機制是 `html.dark`（`resources/views/includes/_shell.blade.php` 的 seller-theme，
   `document.documentElement.classList.toggle('dark', dark)`），本檔另有 `[data-bs-theme]` 一族。
   ★★修前那兩顆用的 `--dark` / `--dark-foreground` 是**會隨主題對調**的一組
     （亮色 `--dark: 0 0% 9%`／`--dark-foreground: 0 0% 98%`；
       深色 `--dark: 0 0% 98%`／`--dark-foreground: 0 0% 9%`，見本檔 L108-109 與 L335-336）
     ⇒ 亮色是「近黑方塊配近白圖示」、深色是「近白方塊配近黑圖示」，
       **兩個主題下都是一塊高對比的實心補丁**。這就是「醜」在兩個主題下都成立的原因。
   本節改用 `--foreground` / `--background` / `--accent` 這組**語意會跟著主題走**的 token：
     亮色  圖示 `--foreground` = 0 0% 0%（#000000）  對 bar 底 `--background` = 0 0% 98%（#FAFAFA）
     深色  圖示 `--foreground` = 0 0% 98%（#FAFAFA）對 bar 底 `--background` = 0 0% 3.9%（#0A0A0A）
   ⇒ 兩個主題都是「前景色圖示 ＋ 頁面底色」，這正是 ghost 的語意（它本來就只該
     繼承前景色），而且**不需要為深色模式寫任何一條額外規則**。
   ⇒ hover 走 `--accent` / `--accent-foreground`，官方 ghost 逐字就是這一組。
     ★官方另有 `dark:hover:bg-accent/50`（深色只上 50% 濃度），本節**刻意不抄**：
       本檔深色的 `--accent` 是 `0 0% 25.1%`（#404040，見 L311，註解明寫「官方暗色
       --accent 比 --muted/--secondary 亮一大階，hover 態才看得見」）——
       它已經是為「深色下看得見」調過的值，再乘 .5 會把它壓回看不見。
       ★這是「上游的值要連註解一起讀」的實例：抄 `/50` 會抄掉本站已經解過的問題。

   ═══ 九、右漢堡拿掉也不會壞（★協調者交辦的結構要求）═══
   bar 的版面**不依賴「必定有兩顆鈕」**：
     · bar 的表面是 `.pos-page-header`（或 `.pos::before`），寬度 left:0/right:0，與鈕無關
     · 兩顆鈕都是 `position:fixed`，**不佔流**，拿掉任何一顆 bar 都不會塌
     · 標題的讓位是 `padding: 0 3.25rem` 左右**各自獨立**的常數，不是 flex 分配
   ⇒ 將來若業主裁決把右邊那顆換成底部 Drawer 或 Tab，只要刪掉右側那 52px 的 padding
     （或什麼都不做，就是右邊多留 52px 空白），bar 本身零改動。
   ★本節**不動**「有幾顆鈕」這件事：§S-114 的兩顆維持兩顆（業主明確要「左右都有漢堡條」）。

   ═══ 十、本節不做的事（★避免下一個人以為漏了）═══
     · 不加 focus 環：本節沿用 §S-114 `.pos-menu-close` 已經定案的做法
       （`:hover, :focus-visible` 同一組 accent 底色當焦點回饋）。官方 v4 的
       `focus-visible:ring-[3px] focus-visible:ring-ring/50` 牽動 `--ring`，
       而 `--ring` 在母版是**已知的無障礙倒退**（母版自己標記的），要動得整族一起評估，
       不在本節範圍。★兩顆鈕的鍵盤焦點目前靠 accent 底色可見，不是沒有回饋。
     · 不動 `font-size:0`（§S-114 ⑪ 吃掉三頁寫死的「紀錄」文字節點、保留無障礙名稱）
     · 不動 `.badge` / `.img` / z-index 堆疊 / 抽屜行為 / 任何 JS
     · 不動桌機：★本節**每一條規則都在 `@media (max-width:991.98px)` 之內**，
       ⇒ 桌機（min-width:992px）連一條宣告都不會被算進來。
   -------------------------------------------------------------------------------- */

@media (max-width: 991.98px) {

    /* ① bar 的表面 —— 只給「沒有 .pos-page-header 的頁面」（實測只有 machine_backup）。
          配方與 §S-94 的 `.pos-page-header` 逐字相同（見章節註解「四」），
          所以兩條路徑畫出來的 bar 一模一樣，而同一時間只會有一條被畫出來。
          ★`pointer-events:none`：它只是一塊表面，不可以吃掉落在那 52px 內的點擊。 */
    .pos:not(:has(.pos-page-header))::before {
        content: "";
        position: fixed;
        top: 0;
        left: 0;
        right: 0;
        height: 3.25rem;                   /* 52px，與 §S-114 ② 的 padding-top 同值 */
        z-index: 1017;                     /* 比兩顆鈕的 1018 低 ⇒ 鈕浮在 bar 上 */
        pointer-events: none;
        background: hsl(var(--background));
        border-bottom: 1px solid hsl(var(--border));
    }

    /* ② 標題列 = 那條 bar 本身：sticky(top:52px) → fixed(top:0)，塞進帶子裡。
          §S-94 的 background / border-bottom **刻意不覆寫**，它們就是要的配方。
          ★`padding` 用簡寫，理由見章節註解「六」（要對打 §S-94 的 768px 那條簡寫）。
          ★左右各 3.25rem = 8px 內縮 + 44px 鈕寬，剛好讓標題不壓到任何一顆鈕。 */
    .pos .pos-page-header {
        position: fixed;
        top: 0;
        left: 0;
        right: 0;
        z-index: 1017;
        min-height: 3.25rem;               /* 52px（原 64px）；與 ① 的表面等高 */
        padding: 0 3.25rem;
    }

    /* ③ 標題字級：20px → 16px，對齊 Work OS `text-base font-bold`（見章節註解「二」(C)）。
          ★52px 的 bar 裡塞 20px/700 會讓上下只剩 12px，視覺上是「字撐滿了格子」；
            16px/700 上下各留 14px，才是 Work OS 那條 bar 的比例。
          ★font-weight 700 與 §S-94 相同，**不動**（Work OS 也是 font-bold）。
          ★overflow/text-overflow/white-space/min-width:0 §S-94 已經有，不重複宣告。 */
    .pos .pos-page-header h1,
    .pos .pos-page-header .pos-page-title {
        font-size: 1rem;                   /* = Work OS text-base */
        line-height: 1.5rem;
    }

    /* ④ 左漢堡 → shadcn ghost：透明底、無邊框色、無陰影，只留前景色圖示。
          §S-114 ⑩ 的幾何（fixed / top:4px / left:8px / 44x44 / radius / z-index）全部沿用，
          本條只改「看得見的那一層」。
          ★用 `border-color` 而不是 `border:0`：§S-114 給的是 1px 實邊，
            把顏色改成 transparent 可以**留住那 1px 的盒模型**，
            將來若要補 focus 邊框不會有 1px 的跳動。 */
    .pos-mobile-menu-toggler {
        background: transparent;
        border-color: transparent;
        color: hsl(var(--foreground));
        box-shadow: none;
    }
    .pos-mobile-menu-toggler:hover,
    .pos-mobile-menu-toggler:focus-visible {
        background: hsl(var(--accent));
        color: hsl(var(--accent-foreground));
    }
    /* 圖示 22px → 20px，與標題列圖示、與 Work OS 的 `<Menu :size="20" />` 同一階。
       ★ghost 之後圖示就是這顆鈕的全部視覺 ⇒ 這一階要跟同列元素對齊，不能自己一個尺寸。 */
    .pos-mobile-menu-toggler > i,
    .pos-mobile-menu-toggler > svg {
        width: 20px;
        height: 20px;
    }

    /* ⑤ 右漢堡 → 同一套 ghost。
          ★★`color` 這一顆要打贏 (1,7,1) 的連結色守門，見章節註解「七」。
            以下三組是**同一個值走兩條路徑**，不是重複宣告：
              (a) 裸選擇器  (0,1,0) → 沒有 #content.app-content 外殼的頁面
              (b) 守門的孿生選擇器 (1,7,1) → 有那個外殼的頁面（實測 /pos/package 有）
            兩者同值 ⇒ 不管走哪條都是 `--foreground`，結構上不可能漂。 */
    .pos-mobile-sidebar-toggler {
        background: transparent;
        border-color: transparent;
        color: hsl(var(--foreground));
        box-shadow: none;
    }
    #content.app-content a.pos-mobile-sidebar-toggler:not(.btn):not(.dropdown-item):not(.nav-link):not(.fb-tab):not(.page-link) {
        color: hsl(var(--foreground));
    }
    /* hover / focus：★`:focus` 一定要一起接管 —— §POS-7 的 `:hover, :focus` 會把它翻回
       `background:hsl(var(--foreground))`（實心近黑塊）。手指點過之後 `:focus` 會留著，
       漏掉它就會出現「點過一次的鈕變成實心補丁」這種只在真機上看得到的回歸。 */
    .pos-mobile-sidebar-toggler:hover,
    .pos-mobile-sidebar-toggler:focus,
    .pos-mobile-sidebar-toggler:focus-visible {
        background: hsl(var(--accent));
        color: hsl(var(--accent-foreground));
        box-shadow: none;
    }
    #content.app-content a.pos-mobile-sidebar-toggler:not(.btn):not(.dropdown-item):not(.nav-link):not(.fb-tab):not(.page-link):hover {
        color: hsl(var(--accent-foreground));
    }
    /* 三條槓 22px → 20px（與左漢堡對稱）。§S-114 ⑪ 的 mask-image 一個字都不動。 */
    .pos-mobile-sidebar-toggler::before {
        width: 20px;
        height: 20px;
    }

}
/* --- S-118 end ------------------------------------------------------------- */


/* --- S-119. 後台側欄收合鈕：向「後台自己那組工具鈕」收斂（2026-08-10）-----------------
 *
 * ═══ 依據（業主原話）═══
 *   看 /system-setting/package/package-setting：
 *   「左邊選單收合是不是要學 POS 前台設計得更好」→「ssot」「做一樣」
 *   裁決（同日，攤開三個選項後）：**向後台自己那組收斂，不照抄 POS 的數字**。
 *
 * ═══ ★★這一顆為什麼不照抄 §S-113 的 POS 共用宣告（三個「照抄會出事」的點）═══
 *   前提先講清楚：POS 的「收合」與後台的「收合」**不是同一種東西**。
 *   POS rail 收合 ＝ `.pos` 的 `padding-left` 從 210px 換成 48px（純讓位，rail 自己 fixed）；
 *   後台的 `.app-sidebar-minified` 是**整套選單行為**（窄條 60px、文字隱藏、圖示置中、
 *   hover 浮出展開、子選單就地展開）。⇒ 本節**只統一工具鈕的視覺語彙，一行機制都不動**。
 *
 *   ① ⛔不搬 48px 收合寬。POS 的 48 是 shadcn `SIDEBAR_WIDTH_ICON`(3rem)，它實測自己的
 *      內容放得下才敢用。後台實測（1600x950）：收合寬 60px、`.menu` 內距 8px、
 *      可用內容寬 **43px**。本節把鈕改成 32px 高、`padding:0 .5rem` 後，
 *      鈕的最小寬 = 圖示 16 + 內距 8×2 = **32px** ⇒ 32 ≤ 43 ✔ 放得下；
 *      **但若把 rail 收成 48px，可用寬只剩 31px ⇒ 32 > 31 會被切**。
 *      ★POS 那邊的教訓原話「真正的瓶頸幾乎都不是你以為的那個」在這裡同樣成立：
 *        後台的瓶頸不是選單文字（收合態文字 `display:none` 根本不佔寬），是**這顆頁腳鈕**。
 *   ② ⛔不做「兩字短標」。POS 只有 4 個導覽項；後台一級項目 13 個，實測最長
 *      「賣家數據中心」文字寬 82.95px（14px 字），要在收合態顯示文字需
 *      `8(padL)+16(icon)+8(gap)+82.95+8(padR)＝122.95px`，60px 放不下、48px 更不用談。
 *      ⇒ 維持「收合態只留圖示」，不縮字（縮出來的是看不懂的字）。
 *   ③ ⛔不動 localStorage key `seller-sidebar-minified`（_shell.blade.php L128）。
 *      換 key ＝ 所有既有使用者的收合偏好一次失效。POS 用的是另一顆
 *      `seller-pos-rail-collapsed`，兩套各自獨立是對的，本節不合併。
 *
 * ═══ ★★★圓角維持 8px，**不跟 POS 的 10px**（業主裁決 A）═══
 *   §S-113 取 10px 的理由白紙黑字：「同排鄰居『全部登出』『暫停』實測 10px、
 *   本站 `.btn` 是 10px」—— 那是 **POS 側欄頁腳那一排**的鄰居關係。
 *   後台這顆的鄰居實測全是 8px：`.menu-link` 13 顆 8px、header 四顆工具鈕
 *   （主題／通知／前台／帳號）全 8px、這顆自己也是 8px。
 *   而且 §S-39 (1) 有一條**明文決定**：「這顆是 shadcn 的 SidebarTrigger，
 *   官方就是 rounded-md 的方角小鈕」才把它從 9999px 藥丸改成 `var(--radius-md)`。
 *   ⇒ 照抄 10px ＝ 讓它變成整條側欄唯一的例外，**並且無聲推翻一條有依據的既有決定**。
 *   ★★本節**刻意不重複宣告 `border-radius`** —— 圓角的權威來源留在 §S-39 (1) 一處。
 *     在這裡再寫一次 8px，日後有人改 §S-39 就會發現改不動（本節排在檔尾會贏），
 *     那是 SSOT 破裂最常見的長法。**沒改的東西就不要寫進來。**
 *
 * ═══ ★★★背景／邊框：維持 `--sidebar-accent` 填色、維持無框（業主裁決 B，實測後選）═══
 *   三個選項都用真值算過（本機實測 token，不是推論）：
 *     (a) 照 POS 用 `--background`：暗色 `--background` 是 0 0% 3.9% (#0A0A0A)，
 *         而側欄底 `--sidebar-bg` 是 0 0% 9% (#171717)
 *         ⇒ 鈕**比底更暗**，在暗色側欄上變成一塊凹洞。✘
 *     (b) 用 `--sidebar-bg` + `--sidebar-border`：暗色兩者分別是 9% 與 14.9%，
 *         鈕的底與側欄底**逐位同值（對比 1.00:1，面完全消失）**，只剩一圈
 *         #262626 對 #171717 的框（對比約 1.4:1，幾乎看不見）
 *         ⇒ **暗色下這顆鈕會整個消失**。✘
 *         ★這正是 POS 剛付過學費的那一族：ghost 化不會製造問題，它會**暴露**問題，
 *           而「暴露」的形式是畫面上什麼都沒有、零錯誤訊息。
 *     (c) 維持現況 `--sidebar-accent`：亮色 #F0F0F0 坐在 #FAFAFA 上（面對比 1.09:1，
 *         看得出是個鈕）、暗色 #262626 坐在 #171717 上（1.19:1，同樣看得出）；
 *         圖示對比亮 6.34:1 / 暗 5.85:1，兩個模式都遠高於 3:1。✔ ⇒ 採用 (c)。
 *   ★★因此**本節一個顏色宣告都沒有**（不寫 background / color / border）。
 *     這不只是「量出來沒問題」，是**結構保證**：本節的選擇器不宣告顏色，
 *     §S-39 (7) 那四態（hover / focus-visible / active / disabled）與 §Sidebar 的
 *     底色規則就完全不受影響，暗色對比不可能因本節而變。
 *   ★順帶記一筆給日後：`--border` 在暗色是**自帶 alpha 的完整值** `0 0% 100% / 10%`，
 *     對它寫 `hsl(var(--border) / .5)` 會展開成雙斜線無效值 ⇒ **整條宣告被丟棄、零錯誤訊息**。
 *     本節沒有用到它，這行是為了下一個想「加個框」的人而寫。
 *
 * ═══ 改前實測（https://test.2025-seller.mtest/system-setting/package/package-setting，1600x950）═══
 *                        收合鈕        後台 header 四顆工具鈕     側欄 .menu-link 13 顆
 *   高度                 **28px**      32px（唯一值）             32px（唯一值）
 *   圓角                 8px           8px                        8px
 *   padding              **6px 四邊**  0 8px                      8px
 *   box-sizing           **content-box** border-box               border-box
 *   圖示                 **16px（＝1em，隱性）** 14px             16px
 *   font-weight          **400**       500（主題鈕）/400          400/500
 *   ⇒ 這顆是**後台內部唯一的偏離者**：28 高、6px 四邊內距、圖示大小是隱性的。
 *     ★這裡要修的不是「有一顆做錯了」，是「它從來沒有被當成工具鈕群的一員定義過」。
 *
 * ═══ ★★★圖示為什麼一定要顯式寫尺寸（本節最容易被漏掉的一條）═══
 *   實測：svg 上的 `width="24" height="24"` 屬性**沒有生效**，真正勝出的是
 *   `.lucide, i[data-lucide] { width:1em; height:1em }`(0,1,0)
 *   ⇒ 圖示大小 ＝ **按鈕的 font-size**（16px）。這顆鈕**沒有任何文字節點**
 *   （實測 `textContent.trim()` ＝ `""`），所以 font-size 對它唯一的作用就是決定圖示大小。
 *   ★★後果：任何人只要「順手把字級收成 .75rem 以對齊工具鈕語彙」，
 *     **圖示會靜靜縮成 12px**，而且畫面上看起來只是「圖示小了一點」，
 *     沒有錯誤、沒有版面破裂、grep 也搜不到因果關係。
 *   ⇒ 本節把圖示尺寸釘死成 **16px**（不是 POS 的 18px）：後台側欄的圖示階實測就是 16px
 *     （13 顆 menu-icon 全 16，唯一值），改 18 會讓這顆變成側欄裡唯一的 18px。
 *     POS 用 18px 是因為它的 rail 導覽圖示是 20px、工具鈕 18px 自成一階，**階梯不同不能互抄**。
 *
 * ═══ font-weight:500 是「語彙宣告」，現況零視覺影響（誠實記錄）═══
 *   這顆鈕沒有文字節點，所以 `font-weight` 現在**一個像素都不會變**。
 *   仍然寫進來的理由：它是這組工具鈕的共用語彙，日後若加上文字（例如像 POS 那樣
 *   在展開態顯示「收合選單」），不會又長出第五套字重值。
 *   ★對稱地，`gap` **刻意不寫** —— 它同樣是零效果，但它不在業主裁決的清單裡，
 *     寫進來只是多一條沒有依據的死宣告（YAGNI）。★兩者的差別是「有沒有依據」，不是「有沒有效果」。
 *
 * ═══ 特異度：靠分數贏，不靠 `!important`、也不靠「同分排後面」═══
 *   ★掃描分母寫出來：在真實頁面上用 CSSOM 遞迴走完 **16 份樣式表、10,289 條 style rule**
 *     （含 @media / 巢狀規則；`matches()` 例外 **0 次** —— 例外若不歸零，「零命中」與
 *     「掃描失敗」會輸出一模一樣的結果）。
 *   命中這顆鈕的規則共 **11 條**，最高特異度 **(0,1,0)**，其中與本節同屬性的只有三條：
 *     §Sidebar L1401 `.app-sidebar-minify-btn`  background / border-radius / color!important
 *     §S-29   L9287 `.app-sidebar-minify-btn`  padding:6px / box-sizing:content-box   ← 本節要贏它
 *     §S-39(1)L9944 `.app-sidebar-minify-btn`  border-radius                          ← 本節不碰
 *   命中圖示的規則 2 條，最高 (0,1,0)（`.lucide, i[data-lucide]` 的 1em）。
 *   收合態另有 §S-14 兩條（`@media min-width:992px`）：
 *     `.app-sidebar-minified #sidebar:not(:hover) .app-sidebar-minify-btn` (1,3,0)
 *         → `margin:0 auto !important; justify-content:center`
 *     `.app-sidebar-minified #sidebar .app-sidebar-minify-btn > svg|> i`   (1,2,1)
 *         → `transform: scaleX(-1)`（收合態箭頭翻面）
 *     ★★本節**不宣告 margin / justify-content / transform** ⇒ 與這兩條零交集，
 *       不需要贏它們，收合態的置中與箭頭翻面原封不動。
 *   ★★★**全站沒有任何帶 id 的規則在管這顆鈕的幾何**（上面 10,289 條掃過，命中的 11 條 a 欄全是 0）。
 *     這一點是查證出來的，不是假設 —— 帶 id 的規則任何 `(0,x,y)` 都永遠贏不了，
 *     補再多 class 也沒用（POS 才剛被一條 (1,7,1) 的 id 規則壓掉 color 咬過）。
 *   本節用 `#sidebar .app-sidebar-minify-btn` ＝ **(1,1,0)**，a 欄 1 > 0 ⇒ 直接贏，零 `!important`。
 *   圖示那條 `#sidebar .app-sidebar-minify-btn > :is(svg, i)` ＝ **(1,1,1)**（`:is()` 取引數最高分，
 *   兩個都是型別選擇器 ⇒ +(0,0,1)）⇒ 同樣靠分數贏 `.lucide` 的 (0,1,0)。
 *   ★為什麼不學「同分靠檔尾順序」：那招只要有人往檔尾再追加一條同分規則就會被**無聲翻盤**
 *     （畫面變了但沒有任何錯誤訊息）。分數贏才是穩的。
 *
 * ═══ 為什麼**不**關進 `@media (min-width:992px)`（與 §S-113 的做法相反，這是刻意的）═══
 *   §S-113 整段關在 ≥992px，因為它那四顆裡有三顆在手機根本不存在。
 *   後台這顆**在手機是存在且看得到的**（側欄變成抽屜，鈕照樣在頁腳；沒有任何規則
 *   在 `max-width` 段把它藏起來 —— 全檔與這顆有關的 11 條規則裡，唯一帶 media 的
 *   是 §S-14 那組 `min-width:992px`）。它在手機點下去只會加上 class 而沒有視覺變化
 *   （收合行為本身就只在 ≥992px 定義），但**鈕本身要能按**：
 *   28×28 → 32×32 對觸控是**改善**（WCAG 2.5.8 最小 24×24，兩者都過，32 更好按），
 *   而且它不參與任何手機版寫死高度的 bar ⇒ 全寬度套用是安全的，不需要 media query。
 *
 * ★只在檔尾追加，未修改任何既有規則；未宣告任何顏色；未動 blade、未動 JS、未動 localStorage key。
 * ------------------------------------------------------------------------------------- */

#sidebar .app-sidebar-minify-btn {
    /* 幾何：28 → 32，與 header 四顆工具鈕、側欄 13 顆 menu-link 同階 */
    box-sizing: border-box;     /* ← 蓋掉 §S-29 的 content-box，height 才是「連內距的總高」 */
    height: 32px;
    min-height: 32px;
    min-width: 32px;            /* 圖示 16 ＋ 內距 8×2 恰好 32；這條是正方形的下限保證 */
    padding: 0 .5rem;           /* ← 蓋掉 §S-29 的 6px 四邊；與 header 工具鈕的 0 8px 同值 */
    /* 這組工具鈕的共用字重。★這顆鈕現況零文字節點 ⇒ 目前零視覺影響（見章節註解） */
    font-weight: 500;
}
/* 圖示尺寸釘死，不再由 font-size(1em) 隱性決定。
   ★`i` 與 `svg` 兩種都接：Lucide 會把 `<i data-lucide>` 換成 `<svg>`，替換前後不能有一段沒樣式。
   ★刻意只宣告 width/height/flex，**不宣告 transform** ⇒ §S-14 收合態的箭頭翻面完全不受影響。 */
#sidebar .app-sidebar-minify-btn > :is(svg, i) {
    width: 16px;
    height: 16px;
    flex: 0 0 auto;
}
/* --- S-119 end ------------------------------------------------------------- */

/* --- S-120. POS 提示驚嘆號（circle-alert popover）：點了沒反應 + 與按鈕差 3.06px（2026-08-10）---

   === 業主原話（逐字）===
   「DEMO-USER-001 點我完成旁邊有驚嘆號 不能點擊 而且跟按鈕是沒有水平對齊」
   追問「它本來是裝飾還是功能鈕」後的裁決：「驚嘆號可以點擊的」
   ⇒ 它是**功能元素**（Bootstrap popover 提示），不是裝飾
   ⇒ 本節要**恢復可點性**，不是把它改成不像鈕。

   === 元素身分（逐字 markup，不是推論）===
   pos/package.blade.php:712 / 766 / 798（pos/machine.blade.php:701 / 755 / 788 同款）
       <span class="p-1" data-bs-toggle="popover" data-bs-trigger="click"
             data-bs-title="揀貨尚未完成，全部都揀完貨時，請記得回來按下全完成"
             data-bs-placement="top"><i data-lucide="circle-alert" class="fa-lg text-danger"></i></span>
   ★這段 markup 自 2026-01-09（ray 68a3a004「新增揀包貨功能」）就存在；
     2026-07-31（monet 93ba7d37）把 FA 的 fa-circle-exclamation 換成 lucide circle-alert。
     ★★**不是 2026-08-10 這條設計線新增的**。§S-92 的 pos-i mask 與它無關
       —— 它走 data-lucide + createIcons()，不走 mask，紅色是 .text-danger 給的，
       不是「mask 壞掉的 fallback」。

   === 症狀②「不能點擊」= 兩道各自獨立的阻擋（只修一道還是死的）===
   (A) includes/_shell.blade.php:197  `if (document.body.clientWidth <= 767) return;`
       ⇒ 手機寬度下 sellerInitPopovers() 早退，**Popover 實例從來沒被建立**。
       實測（376x812、把 blade 的逐字複本注入 #log-list）：
           bootstrap.Popover.getInstance(span) === false
           span.click() 之後 document.querySelectorAll('.popover').length：0 -> **0**
       ★該行是 2026-07-31（076e63a5）照搬 Color Admin 原行為，註解自陳
         「避免手機上 tooltip 卡住觸控」—— 對 hover 型 tooltip 合理，
         對 data-bs-trigger="click" 的 popover 是**誤傷**。
       ★本節不動 _shell（全站 145 處共用，且該檔正被另一條線改動中），
         改在 layouts/pos.blade.php 補一支**只認白名單 class** 的委派懶初始化。
   (B) pos/package.blade.php:16-18 與 pos/machine.blade.php:16-18 的
       `.popover { display: none !important; }`
       ⇒ 桌機（>767）實例**有**建立，但彈出層被這條蓋掉，一樣「點了沒反應」。
       實測探針：新建 div.popover 的 computed display === "none"。
       ★來源 c5e8e580（ray, 2026-01-21「優化」），同一個 commit 刪掉了 UIManager.initPopovers()。
       ★★這條**一個字都不動**（禁刪；而且拿掉會讓該頁 14 處 popover 全部復活，
         那是業主沒授權的產品變更 —— 見 machine.blade.php 第 21-38 行 2026-08-06 的查證註解）。
         本節改用**白名單**：只讓掛了 .pos-hint-popover 的那幾顆顯示。

   === 症狀③「沒有水平對齊」= delta -3.06px（驚嘆號中心比按鈕中心**高**）===
   ★★業主從截圖目視是「略低」，**實測是略高**。以數字為準（376x812）：
         按鈕   center y = 514.75  h = 32
         驚嘆號 center y = 511.69  h = 17.5   ⇒ delta = -3.06px
   成因鏈（用 document.styleSheets 逐條列出**實際命中**的規則，不是猜）：
       1. `.price` 帶 `.d-flex`（bootstrap `display:flex!important`），
          而**沒有任何規則給它 align-items** ⇒ 預設 `normal` = stretch
          ⇒ `span.p-1` 被拉成與列同高 32px（實測 spanH = 32）。
       2. span 是 `display:block`，裡面的 svg 是 inline-block、靠 baseline 定位
          ⇒ 落在**第一個行框**（line-height 21px）而不是 span 的垂直中央。
       3. 本檔 §38 `.lucide{width:1em;height:1em;vertical-align:-0.125em}`
          ＋ `.lucide.fa-lg{font-size:1.25em}` ⇒ svg 實測 17.5x17.5。
   ★命中該 span / svg / .price 的**全部**規則只有這七條：
       bootstrap `.p-1`、`.d-flex`、`img,svg`；vendor FA `.fa-lg`；
       本檔 §38 兩條；本檔 `.product .text .price`。
     ★★**2026-08-10 的 §S-92 / §S-96 / §S-103 / §S-113 / §S-115 / §S-118 一條都沒命中**
       ⇒ 症狀③ 不是今天做出來的，是既有 bug。
   ★**沒能驗到的（照實說）**：2026-07-31 之前 FA 版的精確偏移量。
     控制組注入 FA 原 markup 後 `<i>` 高度只有 0.88px（這台的 FA icon 字型沒有實際 glyph）
     ⇒ 那組 iconCy 不是視覺中心，**不能拿來宣告「07-31 之前對得比較齊」**。
     只能說 `.fa-lg{line-height:.05em;vertical-align:-.075em}` 在 stretch 的 flex 子項裡
     同樣會偏高（**推論，未量測**）。

   === 為什麼修 span 而不是給 `.price` 加 align-items:center ===
   `.price` 那一列還有 `.btn-group`、`.dropdown-toggle`、`ms-auto` 的取消鈕。
   改整列的 align-items 會同時動到它們（`.btn-group` 現在正是靠 stretch 取得 32px 高）。
   ★只把「被拉長的那顆 span」變成 flex 置中，幾何影響面 = 1 個節點，
     實測 delta 由 -3.06 -> **0.00**（同一次呼叫內三組並列對照：現況 / FA 控制組 / 本修法）。
   ★選擇器用**直接子代** `>` 是刻意的：`.price` 裡還有一顆 popover 驚嘆號是
     **包在 <button> 裡**的（package.blade.php:870 / machine.blade.php:859 的「退回重揀」），
     `>` 會把它排除。那顆點下去會連帶觸發按鈕的 fault: 動作，是另一個議題，本節不碰。
   ------------------------------------------------------------------------- */
.pos .product .text .price > span[data-bs-toggle="popover"] {
    display: flex;
    align-items: center;
}

/* 症狀②(B) 的白名單：只讓 POS 提示驚嘆號的 popover 顯示。
   ★特異度 (0,2,0) > 頁面 css 區塊那條 `.popover` 的 (0,1,0)，
     所以就算頁面樣式在 shadcn-theme.css 之後才輸出
     （layouts/pos.blade.php 第 18 行 include _link、第 19 行才 yield css），
     這條照樣贏 —— 不需要去動頁面那條，也就不會把其餘 13 處 popover 一起放出來。
   ★class 由 markup 的 data-bs-custom-class="pos-hint-popover" 帶進來，
     Bootstrap 5 會自動把它加到彈出層上（桌機走 sellerInitPopovers、
     手機走 layouts/pos 的委派懶初始化，兩條路都吃同一個屬性 = SSOT）。 */
.popover.pos-hint-popover {
    display: block !important;
}
/* --- S-120 end ------------------------------------------------------------- */

/* --- S-121. 買家頁首「發票查詢」：焦點環被 .site-nav 的捲動容器「整圈」裁掉（2026-08-10）-----
   ─────────────────────────────────────────────────────────────────────────────
   ★病徵（實測，非推論）：landing / lookup / lookup-by-invoice / reissue 這 4 頁，
     頁首選單那顆「發票查詢」用鍵盤 Tab 過去時，焦點環**一個像素都看不到**。
     不是「被裁掉一部分」——是四邊全裁，等於鍵盤使用者完全看不出焦點在哪。

   ★成因（三個條件同時成立才會發生）：
     1. 版型 resources/views/buyer/layout.blade.php 的 `.site-nav` 帶 `overflow-x: auto`
        （原始碼註解寫的理由是「窄螢幕改橫捲，不換行把頁首撐成兩列」）。
     2. Chrome 依 CSS 規範把 `overflow-y` 一併算成 auto ⇒ 實測 computed 為 auto/auto。
        ★這使 `.site-nav` 成為一個 scroll container，會在 padding box 邊界裁切子孫的繪製。
     3. `.site-nav` 與內部 `.site-nav-link` **等高等寬、四邊餘裕各 0px**
        （實測 nav 32×79.3、link 32×79.3，slack top/bottom/left/right 全為 0）。
     而焦點環是 `box-shadow: 0 0 0 3px`（spread 3px、無 blur），整圈都落在 link 的
     border box **之外** ⇒ 整圈都在裁切框之外 ⇒ 整圈不可見。
     ★box-shadow 不計入 scrollable overflow，所以它不會撐出捲軸、也不會有任何錯誤訊息，
       只是靜靜地被裁掉——這就是它一直沒被發現的原因。

   ★怎麼證明「真的被裁掉」而不是推論出來的：
     用 `document.elementFromPoint()` 打在焦點環應該出現的座標上（hit testing 會被
     overflow 裁切，所以它是裁切的可靠代理）。在 link 內放 4 條 3px 探針（上下左右各一條，
     位置與焦點環完全重合）＋1 條放在 link 框**內**當對照組：
       狀態 A（overflow 維持 auto/auto）：上下左右 4 條全部打不到，命中的是背後的
                                        `.site-header-inner`；框內對照組打得到。
       狀態 B（僅把 overflow 改成 visible/visible，其餘一字未動）：4 條**全部打得到**。
       狀態 C（改回 auto/auto）：又全部打不到。
     A/B/C 雙向可重現 ⇒ 裁切者確定就是 `.site-nav` 的 overflow，不是別的東西。

   ★為什麼修在主題這裡、而不是去改版型那條 `.site-nav`：
     版型 blade 當時正由另一個 session 持有（禁止改動），且 overflow-x 是刻意的橫捲保護、
     本來就不該拿掉。本節只**新增** padding/margin 兩個宣告，版型那條規則一字未動。

   ★修法：`padding: 4px` 撐開裁切框、`margin: -4px` 把外框縮回原位。
     - overflow 的裁切框是 **padding box**，所以 padding 區域是「在裁切框以內、看得見」的，
       4px padding 等於替 3px 的焦點環讓出空間（多 1px 是防子像素捨入把環削掉一絲）。
     - `.site-nav` 是 `.site-header-inner`（display:flex、justify-content:space-between、
       align-items:center）的 flex item，版位由**外邊距框**決定；padding +4 與 margin -4
       互相抵銷 ⇒ 外邊距框維持 32px、位置完全不動。
     - 實測 delta：link 的 top/bottom/left/right/width/height **六項全為 0**、
       頁首總高維持 61px、頁面未產生水平捲動。也就是**畫面零位移，只是環有地方畫了**。

   ★!important：**實測決定，不用特異度推算**。把本節規則注入到「shadcn-theme.css 實際所在
     的層疊位置」（即版型 <style> 之**前**，這是對本規則最不利的位置）實測：
       不帶 !important ⇒ computed padding 4px / margin -4px（生效）
       帶   !important ⇒ 完全相同
     ⇒ 兩者結果一樣，故**不帶**。原因是版型那條 `.site-nav` 根本沒有宣告 padding/margin，
       兩者不衝突，不存在需要壓過去的對象。誘餌（改成 17px）實測 navH 由 32 變 66，
       證明這條注入路徑真的會咬得動，上面那個「不帶也生效」不是空包彈。

   ★排除掉的做法（都有實測依據，不是憑感覺挑的）：
     1. 把 `overflow-y` 設成 visible —— **走不通**。CSS 規範規定 overflow-x/y 只要有一軸
        不是 visible，另一軸的 visible 就會被強制算成 auto。實測：對 `.site-nav` 下
        `overflow-y: visible`（overflow-x 仍是版型的 auto），computed 仍是 **auto/auto**。
     2. 只加 padding、不加負 margin —— **會位移**。因為 `.site-header-inner` 是
        space-between，nav 的外邊距框貼右緣，加了 padding-right 會把連結**往左推 4px**
        （實測 link left/right 各 -4px）。加負 margin 才會歸零。
     3. 改動 §S-30 的 `.btn:focus-visible`（把 box-shadow 換成不受裁切的 outline）——
        **對症不對因，而且動到全站**。★另外查證後發現一件事：本頁這顆焦點環**不是**
        §S-30 給的。掃過本頁 6 個樣式表、7235 條規則，會套到 `.site-nav-link` 且有宣告
        box-shadow 的規則**只有 1 條**，就是版型 blade 自己那條
        `.site-nav-link:focus-visible { box-shadow: 0 0 0 3px hsl(var(--ring) / .5) }`（無 !important）。
        `.site-nav-link` 不是 `.btn`，§S-30 從來沒套到它。所以改 §S-30 既救不了這裡、
        又會波及全站，雙輸。
     4. 拿掉 `overflow-x: auto` —— 那是版型的檔案（禁改），且它是真的保護。
        ★附帶查證：git 追這一行的來源是 commit 7155e467（買家發票前台改成網站外殼那次），
          當時選單有「首頁 + 發票查詢」兩項，橫捲保護是為多項選單加的。現在選單只剩一項，
          所以它**目前**確實沒有在擋任何東西——但那是內容剛好變少，不是保護多餘，
          之後選單再加項目它就會派上用場，故予保留。
        ★實測本節不會破壞它：塞 12 個選單項目製造溢出，加本節前後
          nav 都 overflowing=true、最大可捲距離**都是 291.5px（完全相同）**、
          頁面都沒有產生水平捲動。scrollport 與內容同時各多 8px，可捲距離不變。

   ★作用域與波及面：`.site-nav` 這個 class 全專案只出現在買家版型與買家結果頁兩支 blade
     （掃過 resources/ 282 檔、public/ 1755 檔），主題本檔在本節之前對 `.site-nav` 是
     0 條規則。後台頁面沒有這個 class，不受影響。
     ★結果頁（result）目前那顆「發票查詢」已被移到 `.site-nav` **外面**的插槽、且是 `.btn`，
       本節碰不到它，它的焦點環本來就四邊完整。本節對「在 nav 內」與「在 nav 外」兩種
       版位都成立：在內的由本節讓出空間，在外的本來就不被裁。 */
.site-nav {
    padding: 4px;
    margin: -4px;
}


/* --- S-122. POS 說明 popover 全面放行（手機也點得出說明）------------------------
   業主裁決（原話）：「驚嘆號是說明不是」／「你修啊」「我就叫你修啊」。
   ⇒ POS 的說明 popover 全部要能點得出來，而且驚嘆號不該連帶送出按鈕動作。

   === 兩道各自獨立的阻擋（§S-120 已查明，只修一道還是死的）===
   (A) includes/_shell.blade.php:197 `if (document.body.clientWidth <= 767) return;`
       ⇒ 手機寬度下 sellerInitPopovers() 早退，Popover 實例從來沒被建立。
       ★由 layouts/pos.blade.php 第二支委派懶初始化補上（選擇器＝POS 內所有
         [data-bs-toggle="popover"]，不再是白名單）。_shell 一個字都沒動 ——
         它是全站 145 處的共用初始化，放開閘門會讓後台手機版也冒 tooltip。
   (B) pos/package.blade.php:16-18 與 pos/machine.blade.php:16-18 的
       `.popover { display: none !important; }`（c5e8e580, ray, 2026-01-21「優化」，
       同一個 commit 刪掉了 UIManager.initPopovers()）
       ⇒ 桌機實例**有**建立，但彈出層被這條蓋掉，一樣「點了沒反應」。
       ★2026-08-10 實測（1600x950，/pos/package，未改動前）：
             觸發器 = 7、實例 = 7 ⇒ 初始化沒問題
             探針 div.popover 的 computed display === "none" ⇒ 死因就是這條
             探針 div.popover.pos-hint-popover === "block" ⇒ §S-120 白名單有效
       ★這條**一個字都不動**（禁刪規則）。本節改用作用域覆蓋把它蓋回 block。

   === 為什麼用 body.pos-layout 而不是全域改 .popover ===
   `pos-layout` 只由 layouts/pos.blade.php 的 <body> 輸出（本次新增的鉤子）。
   ⇒ 後台頁面的 body 沒有這個 class，145 處 popover 的行為一個字都沒被動到。
   ★特異度 (0,2,1) > 頁面 css 區塊那條 `.popover` 的 (0,1,0)，兩邊都有 !important
     ⇒ 本條勝。也就不需要去動頁面那條，也不會外溢到 POS 以外。
   ★對照組（改動前，1600x950，/login 這種非 POS 版面）：
         探針 div.popover 的 computed display === "block"（本來就正常）
         點擊 → .popover 節點 1 個、class "popover bs-popover-auto fade show"、
         opacity 1、rect 276 x 112.2 @ (982, 170.8)、再點一下 → 0 個
     改動後要逐項相同。

   === §S-120 的 .popover.pos-hint-popover 白名單為什麼留著 ===
   零刪除；而且它與本條不衝突（同樣是 display:block !important）。
   §S-120 的實測結論建立在那條白名單上，拿掉會讓那一節的驗收基準失效。

   === 沒有一起做的（照實說）===
   ★那三顆 fault: 驚嘆號的**垂直對齊**沒修。§S-120 的
     `.pos .product .text .price > span[data-bs-toggle="popover"]` 刻意用直接子代 `>`，
     把包在 <button> 裡的那三顆排除在外。本節只解「誤觸」與「看不見」，
     幾何對齊是另一個議題（要動 markup 或另開選擇器，業主沒提）。
   ★pos/machine_backup.blade.php 有同款 markup，**全 repo 零路由引用＝死檔，未改**。
   ------------------------------------------------------------------------- */
body.pos-layout .popover {
    display: block !important;
}
/* --- S-122 end ------------------------------------------------------------- */


/* ═══ §S-121a 附記：§S-121 的隱性依賴——這些條件不成立，焦點環會再次被裁掉且零錯誤訊息（2026-08-10）═══
 *   對象：§S-121（買家頁首「發票查詢」焦點環修法，上方 `.site-nav` 那一節）。
 *   §S-121 加的 padding: 4px／margin: -4px，讓 3px 焦點環有地方畫在 overflow 的裁切框
 *   （padding box）以內。這個修法**依賴以下條件**，條件一旦被打破，焦點環會**再次消失、
 *   且不會有任何錯誤訊息**——裁切發生在視覺層，不會讓任何測試變紅、也不會有主控台警告。
 *
 *   ★依賴 1：`.site-header` / `.site-header-inner` / `.site-nav` 的任何祖先都不可以設
 *     overflow: hidden（或其他非 visible 的 overflow）。
 *     §S-121 用負 margin 讓 `.site-nav` 的邊框盒突出內容邊界 4px，目前沒被裁是因為
 *     `.site-header-inner` 現在是 visible。★正要替 `.site-header` 或
 *     `.site-header-inner` 加 overflow: hidden（例如做圓角裁切、卡片化）的人，
 *     請先讀 §S-121——加下去，焦點環會被祖先重新裁掉，四邊全裁，跟 §S-121 修之前
 *     一模一樣，你會以為 §S-121 從沒生效過。
 *
 *   ★依賴 2：`.site-nav` 必須維持是 `.site-header-inner`（display: flex）的 flex item，
 *     版位由外邊距框決定。padding +4／margin -4 互相抵銷靠的是這條規則。日後若把
 *     `.site-nav` 改成 position: absolute，或改成不吃外邊距框的 grid 排列方式，
 *     這組抵銷關係可能不再成立，需要重新實測，不要假設它自動延續。
 *
 *   ★依賴 3：焦點環必須維持 box-shadow: 0 0 0 3px（spread 3px）。4px 的 padding 是
 *     「3px 環 ＋ 1px 子像素捨入餘裕」量出來的。日後若把 `.site-nav-link:focus-visible`
 *     的 box-shadow 加粗到 4px 以上，這裡的 4px padding 就不夠，環會被切邊而不是切光，
 *     比原本的病徵更難發現——看起來像「有一點效果」，容易被誤判成已經修好。
 *
 *   ★依賴 4（反向）：`.site-nav` 的 overflow-x: auto 要留在版型 blade 裡（§S-121
 *     排除做法 4 已查證：那是刻意的橫捲保護，不是多餘的殘留）。這條被拿掉不會讓
 *     本節出事，但本節會變成一節沒人知道為什麼存在的死規則——不會壞，但會困惑
 *     下一個讀到這裡的人，所以一併列入。
 *
 *   ★反向指引：正要替 `.site-header`、`.site-header-inner`，或任何包住 `.site-nav`
 *     的容器加 overflow: hidden 的人，請先讀 §S-121——那裡的 padding box、焦點環、
 *     裁切機制，解釋了為什麼這幾個祖先目前必須維持 visible。
 * -------------------------------------------------------------------------- */

/* --- S-123. §S-122「全面放行」的範圍被業主縮小：動作型觸發器只要動作、不要說明（2026-08-11）---
   ★★★本節是**純文件、零 CSS 規則**（頂層規則數、大括號數都不變）。
      它存在的唯一理由是：§S-122 的標題與內文寫的是「POS 說明 popover **全面**放行」，
      而那個結論已經被業主當面推翻一半。§S-122 一個字都沒刪（禁刪規則），
      所以照 feedback_superseded_spec_clause_must_be_marked_dead 的做法，
      在這裡就地把它標成部分作廢 —— 不標的話，下一個人會照 §S-122 寫出
      「完全符合規格的錯東西」，而且他不會知道自己錯了。

   === 業主原話（2026-08-11，附 /pos/package 桌機截圖）===
   「你恢復點擊彈窗 反而造成 點我手動輸入 更干擾 因為點擊又說明又彈窗」
   截圖：兩顆「手動輸入」說明泡泡同時卡在畫面上，遮住 STEP.2 / STEP.3 兩張卡，
   而手動輸入視窗也同時開了。
   ⇒ 判準：**點一下同時得到「說明」與「視窗」，說明就是雜訊** ——
     使用者要的（視窗）已經到手，卻還得多關一個泡泡。

   === §S-122 哪一部分作廢、哪一部分仍然有效 ===
   ★仍然有效（一個字都不用改）：
     - `body.pos-layout .popover: display block !important` 這條規則本身
       （純說明型的 popover 還是靠它才看得見，手機桌機都是）。
     - (A)(B) 兩道阻擋的成因分析、以及「不動 _shell、不動頁面那條 display:none」的取捨。
     - §S-120 的 `.popover.pos-hint-popover` 白名單留著的理由。
   ★★★已作廢（★別再照它做）：標題與內文的「**全部**要能點得出來」「不再是白名單」
     這個範圍宣告。正確範圍是「**只有純說明型**要能點得出來」。
     動作型觸發器（點了會開視窗／送出動作的那些）**不建立也不顯示** popover。

   === 判準寫在哪裡、為什麼不寫在 CSS ===
   判準與實作都在 resources/views/layouts/pos.blade.php 最後那支 script 的 (0) 段，
   註解裡有逐條命中清單與實測數字。三條結構條件（不是逐處白名單）：
     (a) 觸發器自己就是可點控制項（a[href] / button / input / select / textarea
         或帶 data-barcode / data-prefix / .desktopUpdate / .amountUpdate / .cancelUpdate）
     (b) 觸發器自己是、或被包在 [data-bs-toggle="modal"][data-bs-target] 裡
     (c) 觸發器裡面包著 input / select / textarea
   ★★為什麼**不能**用 CSS 把泡泡藏起來（這是本節唯一想攔住的坑）：
     實例還在、Bootstrap 掛在該元素上的 click 監聽器也還在
     ⇒ 第一下點擊會被那個監聽器吃掉變成「只關泡泡、不開視窗」，第二下才開。
     使用者的體感是「按了沒反應」，而畫面 200、console 乾淨、CSS 也「生效了」
     ⇒ 四盞燈全綠而功能是壞的。★所以這一節刻意不新增任何 CSS。
   ★★也不能用 preventDefault：那會把開視窗的動作一起打死。
   ★正解是在 document 的**捕獲階段** dispose() 那個實例
     （Bootstrap5 的 dispose 會 EventHandler.off 掉同元素上的 click 監聽器，
      而事件此時還沒抵達觸發器 ⇒ 依 DOM 規範那個監聽器不會被叫到），
     手機則因為 _shell 第 197 行早退、實例從來沒建立，直接跳過即可。

   === 刻意沒有一起改的 ===
   ★§S-122 的原文（含標題那四個字）一字未刪，只在這裡標作廢。
   ★fault: 那顆深色驚嘆號**維持顯示說明 + 擋掉退回重揀**（業主明確裁決過的那一顆），
     它三條條件全不中：裸 span、祖先是 button.desktopUpdate 而不是 modal 開關、
     裡面沒有表單欄位。§S-120 對它的**垂直對齊**依舊沒修，仍是另一個議題。
   ★pos/machine_backup.blade.php 有同款 markup，全 repo 零路由引用＝死檔，未改。
   ★/pos/machine 未登入回 403 ⇒ 那頁只能做靜態等價比對，**沒有實測數字**。
   ------------------------------------------------------------------------- */
/* --- S-123 end ------------------------------------------------------------- */

/* --- S-124. POS 掃描卡按鈕列：「取消」下拉被卡片右邊界切掉（2026-08-11）-------------
 *
 * ■ 業主原話（桌機實測，/pos/package 與 /pos/machine 兩頁都有）
 *   第三張卡（標題「掃描」）的按鈕列長這樣：
 *       [☐ 點我完成全部 0 個揀貨]  [取
 *   ——那顆應該是「取消」下拉（`userPackage:stop*` 那組，帶 caret），
 *     它溢出卡片、被裁掉，只看得到一個「取」字。
 *
 * ═══ 第一嫌疑是同日的 §S-115，實測**推翻**，不要往那邊修 ═══
 *   §S-115 把 rail 收合 76→48、右側欄 340→312 ⇒ 主區 +56px。
 *   直覺會說「兩側收窄所以中間擠爆了」，但方向是相反的：主區**變寬**，
 *   而卡片寬度 = 主區的三分之一 ⇒ 卡片跟著**變寬**，離破版更遠。
 *
 *   雙向對照（1280x950、CSSOM `deleteRule` 整段移除 §S-115 的 @media 區塊再放回，
 *   live 檔零寫入；自證：該區塊 9 條規則、conditionText `(min-width: 992px)`，
 *   刪除後 sheet 內再也 grep 不到 `.pos.pos.pos .pos-menu.pos-rail--collapsed`）：
 *
 *       狀態                     rail  側欄  #pos-list  卡片寬    .price 可用寬  溢出
 *       有 §S-115（現況）         48   312     890      286.00       254        0
 *       移除 §S-115（＝舊值）     76   340     834      267.34       235        0（餘 0.04px）
 *       放回 §S-115              48   312     890      286.00       254        0
 *
 *   ⇒ §S-115 讓卡片**寬了 18.66px**（= 56 / 3 欄）。
 *   ⇒ 破版起始視窗寬：有 §S-115 是 < 1223.9px；沒有 §S-115 是 < 1279.9px。
 *     **§S-115 把破版區間縮小了 56px，它是（部分）解掉這個 bug 的那一邊。**
 *
 *   ★★★這個 bug 是**既有的**，2026-01-09 `68a3a004`「[修改] 新增揀包貨功能」
 *     建這段 markup 那天就在了。`git log -S` 對
 *     `<div class="dropdown-toggle ms-auto">` 與
 *     `class="price d-flex text-nowrap overflow-x-auto"` 各只命中那一個 commit
 *     ⇒ 這一列的 markup 從未被任何設計批次動過。
 *
 * ═══ 真正的機制（數字全部量過，1600x950 headless，探針＝逐字取自
 *     package.blade.php `pickingScanBlock()` 的模板複本注入 `#pos-list` 第三格）═══
 *
 *   (1) 那一列是 `<div class="price d-flex text-nowrap overflow-x-auto">`，
 *       裡面兩個 flex item：
 *           `.btn-group` >「點我完成全部 N 個揀貨」 need = 169.30px
 *           `.dropdown-toggle.ms-auto` >「取消」+caret  need =  66.00px
 *       `flex-wrap` 是 **nowrap**（Bootstrap 預設，沒人改過）⇒ 兩顆永遠擠在同一行。
 *       ⇒ **need 是固定的 235.30px，和容器多窄無關。**
 *       ★包裹數 > 0 時還會多一顆紅色驚嘆號 popover（`.p-1`，另 +26px），need 更大。
 *
 *   (2) 容器可用寬：`.price` 可用寬 = 卡片寬 − 32
 *       （`.product` 框 1x2 ＋ `.product .text` 內距 .9375rem x2 = 30，本檔 §D 補位段）。
 *
 *   (3) 卡片寬由 §S-92 ① 的格線決定：
 *       `.pos #pos-list.pos-steps{grid-template-columns:repeat(auto-fit,minmax(185px,1fr)); gap:1rem}`
 *       ★**已登入態永遠只有 3 張卡**（工作桌／員工／掃描，見 blade `ViewBuilder.build()`），
 *         `auto-fit` 把多出來的軌道塌成 0（實測 gridTemplateColumns 回
 *         `392.664px 392.664px 392.664px 0px 0px 0px`）⇒ **不管視窗多寬都是 3 等欄**。
 *       ★★所以「主區變寬 ⇒ 每列塞更多張卡 ⇒ 卡片反而變窄」這條假設**也被推翻了**：
 *         卡片數是資料決定的（3 張），不是格線決定的。
 *
 *   (4) 破版門檻（二分逼近 24 次實測）：**卡片寬 < 267.30px 就開始被裁**。
 *       在 267.30 時 overhang = 0.01px、`.price` 可用寬 235 ≈ need 235.30 —— 刀口上。
 *
 *   (5) 四種視窗寬的實測（rail 收合＝預設態）：
 *           視窗   #pos-list  卡片寬    .price 可用  need   餘裕     溢出   「取消」看得到
 *           1600     1210    392.66      361      235.30  +125.36    0       66.00 / 66
 *           1440     1050    339.34      307      235.30   +71.70    0       66.00 / 66
 *           1280      890    286.00      254      235.30   +18.70    0       66.00 / 66
 *           1180      790    252.67      221      235.30   −14.30  14.63     51.37 / 66
 *       同樣 1180、但 rail **展開**（210px）＝業主最可能的截圖狀態：
 *           1180      628    198.67      167      235.30   −68.30  68.63        0 / 66
 *           ⇒「取消」整顆看不到，字被切掉 37.63px。**這就是業主看到的那個「取」。**
 *       ★1600 有 125px 餘裕 ⇒ 業主的有效 CSS 視窗寬**不可能是 1600**。
 *         要出現「只剩一個『取』字」，`.price` 可用寬要落在 190 上下 ⇒ 卡片 ≈ 222px
 *         ⇒ 有效 CSS 視窗 ≈ 1088（rail 收合）或 ≈ 1250（rail 展開）。
 *         瀏覽器縮放 125%/150% 或沒有最大化的視窗都會落在這個區間。
 *       ★★破版區間有**下界**：`#pos-list` < 587px 時格線掉到 2 軌
 *         （2x185+16=386 ≤ 上限），3 張卡排成兩列、卡片反而變寬 ⇒ 自己好了。
 *         這就是為什麼它一直沒被發現：**只有中間那一段窗寬會壞。**
 *
 *   (6) ★★★`overflow-x:auto` 在這裡不是保險，是**裁刀**：
 *       CSS overflow 規範規定，一軸是 `auto` 時另一軸的 `visible` 會被算成 `auto`
 *       ⇒ 這一列**兩軸都是捲動容器**（實測 computed overflowY = `auto`）。
 *       溢出的字不是「跑出去」而是「被關進一個沒人會去捲的捲動框」。
 *       ★而 `#log-list` 那五支 item 模板的 `.price` **連 `overflow-x-auto` 都沒有**
 *         ⇒ 由 `.product{overflow:hidden}`（本檔 §D 補位段）硬裁，**連捲軸都沒有**。
 *
 * ═══ 修法：讓那一列換行，不要讓字被切 ═══
 *   業主裁示「寧可換行，不要切字」⇒ 三條，全部只動顯示層：
 *     ① `.price` 開 `flex-wrap:wrap` + `row-gap:.375rem`
 *        ——放得下時 wrap 是**零效果**（實測 1600/1440/1280 幾何完全不變），
 *          放不下時「取消」掉到第二行、靠 `ms-auto` 仍然右對齊。
 *     ② `.btn-group{max-width:100%}` ——單顆按鈕就比一行還寬時不讓它撐破行。
 *     ③ `.btn{white-space:normal; height:auto; min-height:32px}`
 *        ——單顆按鈕自己就放不下時，讓**中文在按鈕內換行**而不是被切。
 *
 *   ★為什麼不用 `overflow:hidden` 把它藏起來：那是把 bug 換成看不見的 bug。
 *   ★為什麼不動 `minmax(185px,1fr)` 的下限：把下限拉到 267 會**改變每列卡片數**
 *     （§S-92 ① 是照 OS `Package.vue:1042` 抄的值），1280 以下會掉成 2 欄、
 *     卡片變高變空 —— 用一個版面問題換另一個版面問題。門檻不是格線的錯，是那一列不會換行。
 *   ★為什麼打**整個家族** `.pos .product .text .price` 而不只打掃描卡那一列：
 *     同一支 blade 裡還有 5 處一模一樣的寫法（`pickingPackageItemIncomplete` /
 *     `pickingPackageItemComplete` / `packingCheckingPackageItem` /
 *     `packingCheckingScanBlock` / `desktopLoggedIn`），全是
 *     `.price d-flex text-nowrap` + `ms-auto` 的取消鈕。
 *     ★★★其中 `desktopLoggedIn()` 那一列裝的是**「揀貨／包貨／檢核」三顆模式切換鈕**
 *       ——它被裁掉＝使用者**換不了模式**，比裁掉「取消」嚴重得多。
 *     只修呼叫端的一處，等於把其餘 5 處留著等下一個人再回報一次。
 *
 * ═══ ★★★我自己的第一版修法有一個靜默失效，留在檔案裡當教訓 ═══
 *   第一版只寫了 ①②③ 的 `white-space:normal`，**沒寫 `height:auto`**。
 *   結果（`#pos-list` 寬 587px、卡片 185px、`.price` 可用 153px 實測）：
 *       文字**確實換成 2 行**（Range 從 132.30 縮成 106.30 = 少了「揀貨」兩個字的寬）
 *       但按鈕高度**還是 32px** —— 因為 §S-103 的階梯裡有
 *       `.app-content .btn-sm{height:32px; min-height:32px}`（(0,2,0)，寫死 height）
 *       ⇒ 按鈕 `scrollHeight 35 > clientHeight 30`，
 *         兩行文字的 client rect 是 338.34~353.34 與 357.84~372.84，
 *         而按鈕框是 339.84~371.84 ⇒ **第一行往上突出 1.50px、第二行往下突出 1.00px**，
 *         字壓在按鈕的邊框上。
 *   ★★這一版**四盞燈全綠**：`.price` 的 scrollWidth == clientWidth（153 == 153）、
 *     scrollHeight == clientHeight（70 == 70，因為突出量還在那一列的 70px 內）、
 *     零 console 錯誤、`php -l` 不相干 ⇒ **沒有任何守門會響，只有眼睛看得出來**。
 *     所以「量到 scrollWidth == clientWidth 就宣告修好」是不夠的，
 *     ★正確守門＝**逐一比對文字的 client rect 與按鈕框**（本節用 spillTop/spillBottom）。
 *   加上 `height:auto; min-height:32px` 之後：
 *       2 行時按鈕 32 → **41px**、scrollHeight 39 == clientHeight 39、
 *       spillTop **−3.00** / spillBottom **−3.50**（負數＝文字在框內）
 *       1 行時按鈕仍然 169.30 x 32、spill −8.25 / −8.75 ⇒ **和改之前逐位元組一致**。
 *   ★`min-height:32px` 不能省：省掉它 1 行的按鈕會縮成 21.5px，
 *     把 §S-101/§S-103 全站唯一的 32 這一階打破。
 *
 * ═══ 修後實測（同一支探針、同一台）═══
 *     視窗/容器            卡片寬   .price 可用  scrollW  行數  按鈕高  「取消」看得到  切字
 *     1180 rail 展開       198.67     167        167      2     41      66.00 / 66     0
 *     #pos-list 587（地板） 185.00     153        153      2     41      66.00 / 66     0
 *     #pos-list 600        189.33     157        157      2     41      66.00 / 66     0
 *     #pos-list 700        222.67     191        191      1     32      66.00 / 66     0
 *     #pos-list 790        252.67     221        221      1     32      66.00 / 66     0
 *   ★`#pos-list` 587px 是格線地板（`minmax(185px,1fr)` x3 + gap 32），
 *     再窄就掉成 2 軌、卡片變寬 ⇒ **這是最壞情況，它也不切字。**
 *   ★雙向驗證：把三條規則 `deleteRule` 移除後，1180 rail 展開的溢出**回到 68.63、
 *     切字回到 37.63**（與修前逐位元組相同）⇒ 效果確實來自這三條，不是別的東西。
 *
 * ═══ 特異度（靠分數贏，不用 !important）═══
 *   `.pos .product .text .price`      (0,4,0) > 本檔 §D `.product .text .price` (0,3,0)
 *   `.pos .product .text .price .btn` (0,5,0) > `.pos .btn:not(.btn-close):not(.btn-xs)` (0,4,0)
 *                                             > §S-103 `.app-content .btn-sm{height:32px}` (0,2,0)
 *                                             > 本檔 `.btn{white-space:nowrap}` (0,1,0)
 *   ★`.price` 自己的 `white-space` **維持 nowrap**（`.text-nowrap` 是 `!important`，
 *     本節不去搶它）—— 本節改的是**子代 `.btn`**：繼承值輸給任何具名宣告，所以不需要 !important。
 *   ★★給下一個人：**不要在這裡加 `column-gap`**。包裹數 > 0 的版本那一列有三個 item
 *     （按鈕／紅色驚嘆號／取消），column-gap 會在驚嘆號兩側多出間距，
 *     那顆的 3.06px 對齊剛剛在 §S-120 校過。本節刻意只寫 `row-gap`。
 * ---------------------------------------------------------------------------- */

/* ① 那一列可以換行。放得下時零效果；放不下時「取消」掉到第二行、仍靠 ms-auto 右對齊。
      row-gap 只在真的換行時才有作用（單行時 flex 沒有第二條線）。 */
.pos .product .text .price {
    flex-wrap: wrap;
    row-gap: .375rem;
}

/* ② 單顆按鈕比一行還寬時，不讓 btn-group 撐破行寬。 */
.pos .product .text .price > .btn-group {
    max-width: 100%;
}

/* ③ 按鈕內的中文可以換行，且按鈕要跟著長高。
      ★`height:auto` 是關鍵那一行：少了它，字換行了但按鈕還是 32px，
        文字會壓在邊框上，而且**沒有任何守門會響**（見上方教訓段）。
      ★`min-height:32px` 保住 §S-101/§S-103 全站唯一的 32 這一階（1 行時外觀完全不變）。 */
.pos .product .text .price .btn {
    white-space: normal;
    height: auto;
    min-height: 32px;
}

/* --- S-124 end ------------------------------------------------------------- */

/* ═══ §S-124a 附記：訂正 §S-124 裡一句**我沒量就寫下去**的話（2026-08-11，同日同一輪）═══
 *
 * ■ 被訂正的原句（原句保留在上面，不要刪）
 *   §S-124「為什麼打整個家族」那段寫著：
 *       「★★★其中 `desktopLoggedIn()` 那一列裝的是**「揀貨／包貨／檢核」三顆模式切換鈕**
 *         ——它被裁掉＝使用者**換不了模式**，比裁掉「取消」嚴重得多。」
 *   ⇒ ★★★**「它被裁掉」這半句在目前的資料下是錯的：那一列現在放得下，沒有被裁。**
 *     那一列屬於同一個「不會換行的 flex 列」家族（這部分是對的），
 *     但**它現在沒有破版**，所以 §S-124 對它是**預防**，不是修既有破版。
 *
 * ■ 實測（同一支探針手法，逐字取自 machine.blade.php `desktopLoggedIn()` + `statusButtons()`
 *   的模板複本，注入 /pos/package 的 `#pos-list` 第一格；`#pos-list` 用 inline width 掃）
 *     那一列只有**一個** flex item（`statusHtml` 產的 `.btn-group`）：
 *         「揀貨」「包貨」「檢核」各 44.00px、`.btn-group` 共 **130.00px**
 *     ★`mr-1` 實測是**空類名**（Bootstrap 4 的寫法，本專案是 BS5 ⇒ margin 0），
 *       所以三顆之間沒有間距、need 就是 130，不是 130 + 3 個 margin。
 *
 *         #pos-list  卡片寬    .price 可用   btn-group  三顆可見      最後一顆離右邊界
 *           1210     392.66      361          130      44/44/44        −230.66
 *            890     286.00      254          130      44/44/44        −124.00
 *            790     252.66      221          130      44/44/44         −90.66
 *            628     198.66      167          130      44/44/44         −36.66
 *            587     185.00      153          130      44/44/44         −23.00   ← 格線地板
 *     ⇒ **連格線地板（卡片 185px）都還有 23px 餘裕，目前完全沒有被裁。**
 *
 * ■ 但那一列的風險是**真的**，而且是**資料決定的**（所以 §S-124 那三條仍然該蓋到它）
 *     `desktops.status_codes` 的 title 是 DB 欄位、不是常數 ⇒ need 會隨資料變：
 *         · title 變 3 個字（例：「待檢核」）⇒ 每顆 ≈ 57px、三顆 ≈ 171 > 153（地板）⇒ 溢出
 *         · 狀態變 4 顆（仍 2 字）        ⇒ 4 x 44 = 176 > 153            ⇒ 溢出
 *     ★★而且這一列比掃描卡那一列**更難發現**：實測它的 computed `overflow-x` 是
 *       **visible**（它掛的是 `.price d-flex text-nowrap`，**沒有** `overflow-x-auto`）
 *       ⇒ 真的溢出時是被 `.product{overflow:hidden}`（本檔 §D 補位段）硬裁，
 *         **連捲軸都沒有**，使用者沒有任何「還有東西在右邊」的線索。
 *
 * ■ ★★★教訓（這才是留下這段附記的理由）
 *   我看到「同一個結構模式」（不換行的 flex 列 + ms-auto / btn-group）就直接推論
 *   「所以它也已經壞了，而且更嚴重」——**結構相同 ≠ 症狀相同**：
 *   need 是**內容**決定的，掃描卡那一列 need = 235.30，這一列 need = 130.00，
 *   差了 105px，而可用寬只有 153~361 —— 一個早就破了，一個還沒。
 *   ★判準：註解裡每一句「這裡也壞了」「這個更嚴重」都要有**它自己的那一組數字**。
 *     沒有數字的比較級，就是把推測寫成事實留給下一個人當前提。
 *   ★這一次是我自己在同一輪內抓到並訂正的（寫完 §S-124 之後才去量那一列）；
 *     ★★**訂正的方式是加附記、不是回頭改原句** —— 原句留著才看得出「當時錯在哪」。
 * -------------------------------------------------------------------------- */
/* --- S-124a end ------------------------------------------------------------ */

/* --- S-125. POS 業主三件：三條 header 底線對齊／手機右鈕改文字「紀錄」／已登入圖卡 16:9（2026-08-11）---
   業主原話（三件，同一輪交辦，故合併成一節一次寫入）：
     (一)「左邊 mypos 時間 下面這條線，跟中間跟右邊的底線似乎有一點點的位移差距」
     (二)「手機版縮小的漢堡條 右邊幫我改為文字 紀錄 或是記錄的 icon」
          → 協調者追問後業主定案：「我建議寫文字 紀錄」⇒ 文字是必要的，圖示是可選的。
     (三)「撿包出貨 自動包裝機 登入之後的圖卡有點太高 看能不能讓圖片的高度變低一點點
          節省空間 圖片比例建議 16:9」＋「你可以用 css 裁切」

   ═══ 量測環境（★全部是實測值，不是推論）═══
   量測頁 `https://test.2025-seller.mtest/pos/package`。
   ★★★`/pos/machine` 未登入回 403，**本節沒有任何 machine 頁的執行期數字**。
     以 package 代測的依據：兩頁共用 `resources/views/pos/_header.blade.php`、
     同一套 `.pos` 外殼、同樣三個 header 區塊，且 (二) 那顆鈕在兩頁的
     `data-toggle-class` / `data-target` 逐字相同（實測 package 為
     `pos-sidebar-mobile-toggled` / `#pos`，machine 的 blade 第 242 行逐字相同）。
   桌機 1600x950（`body` margin-bottom 32px ＝ Debugbar 佔位）
   手機 375x667（同上 96px）。

   ---------------------------------------------------------------------------- */

/* ══════════════════════════════════════════════════════════════════════════
   (一) 三條 header 底線對齊：左邊那條低了 **正好 1px**

   ═══ 實測（1600x950，rail 展開態）═══
     元素                        rect.y   height  border-bottom   rect.bottom
     左 `.pos-rail-sep`            64        1        1px            **65**
     中 `.pos-page-header`          0       64        1px            **64**
     右 `.pos-sidebar-header`       0       64        1px            **64**
   ⇒ 差距 = **1.000px**，左邊低一格像素。業主說的「一點點」就是這 1px。

   ═══ 差距從哪裡來（★三者的高度組成不同型）═══
   · 中 / 右：`height:64px` ＋ `box-sizing:border-box`（全站 `*` 都是）
     ⇒ 那 1px 的 `border-bottom` 是**畫在 64px 盒子裡面**的，佔第 63~64 列像素。
   · 左：`.pos-menu` 在展開態是 grid（§S-97 (3)），
     `grid-template-rows: 64px auto minmax(0,1fr) auto`。
     第一列（`"back logo"`，＝ MYPOS ＋ 時鐘那一排）**自己沒有任何 border**；
     線是由**下一列的獨立元素** `<div class="pos-rail-sep border-bottom mb-2 d-block">`
     （`pos/_header.blade.php` 第 89 行）畫的。
     它從 y=64 起算，`height:1px` ＋ `border-bottom:1px` ⇒ 佔第 64~65 列像素。
   ⇒ **同樣是「64px 標題帶 + 1px 線」，中/右把線畫在帶子的最後一列，
     左邊卻把線畫在帶子外面的下一列** —— 這就是那 1px 的全部成因。
     不是子像素、不是行高、不是 box-sizing 差異（三者都是 border-box，實測）。

   ═══ 修法：把 sep 拉回帶子的最後一列（★不是三處各自硬寫數字）═══
   `margin-top:-1px` 讓 sep 的邊框盒從 y=64 移到 y=63 ⇒ 線佔第 63~64 列，
   與中/右**逐像素相同**。
   ★★為什麼用相對的 -1px，而不是把 grid 第一列改成 63px：
     -1px 的語意是「這條線屬於它上面那條帶子的最後一列」，**不依賴帶子到底多高**；
     改成 63px 則是把 64 這個數字複製到第四個地方，日後有人調整 header 高度時
     三處會再度分岔（正是本節在修的病）。
   ★這條**不需要**、也刻意**不去**重新宣告中/右兩個 header 的 height：
     §S-118 ② 在手機把 `.pos-page-header` 的 `min-height` 改成 52px，
     若在這裡補一條無斷點的 `height:64px`，會把手機那條 52px 的 bar 撐回 64px
     ——「統一高度來源」在這個結構下反而會弄壞已經對過的手機版。
   ★關在 `@media (min-width:992px)` ＋ `:not(.pos-rail--collapsed)`，理由：
     手機 rail 是左側抽屜、收合態 rail 的頂部是另一種堆疊（§S-97 (2)），
     兩者都沒有「三條底線並排」這件事。**收合態實測 sep 的 bottom 是 76**
     （＝ 返回鈕 52 ＋ 時鐘 23 ＋ 線 1），與中/右的 64 差 12px；
     那是 §S-97 (2) 照 OS 刻意做的直排堆疊，**不在業主這次的指令範圍內，故不動**。

   ═══ 特異性 ═══
     對手 §S-97 (3) `.pos .pos-menu:not(.pos-rail--collapsed) > .pos-rail-sep`  (0,4,0)
     本條 同前綴 ＋ `.pos-rail-sep` 重複一次                                     (0,5,0) 勝
   Bootstrap 的 `.border-bottom`(0,1,0) 只給 border、`.mb-2` 只給 margin-bottom，
   **兩者都沒有宣告 margin-top**，不構成對手。

   ═══ 實測結果 ═══
     修前 左 65 / 中 64 / 右 64
     修後 左 **64** / 中 **64** / 右 **64**（三者相等）
   ★副作用只有一項且已量：`.nav-container` y 73→72、height 827.75→828.75
     （grid 第二列 9px→8px，第三列 `1fr` 補回 1px；rail 總高不變）。
     MYPOS / 時鐘 / `.pos-rail-back` / `.logo` / `.pos-page-header h1` /
     `.pos-content-container` 逐項座標與尺寸**零變化**（實測）。
   ══════════════════════════════════════════════════════════════════════════ */
@media (min-width: 992px) {
    .pos .pos-menu:not(.pos-rail--collapsed) > .pos-rail-sep.pos-rail-sep {
        margin-top: -1px;
    }
}

/* ══════════════════════════════════════════════════════════════════════════
   (二) 手機版右鈕：三條槓 → 文字「紀錄」

   ═══ ★★零 blade 改動：那三個字**本來就在 markup 裡** ═══
   `package` / `machine` / `machine_backup` 三頁的鈕逐字是
     `<a href="#" class="pos-mobile-sidebar-toggler h5" ...> 紀錄 </a>`
   §S-114 ⑪ 用 `font-size:0` 把這個文字節點吃掉、再用 `::before` 的 mask-image
   畫三條槓。⇒ 本節要做的不是「加字」，是**把已經在那裡的字放出來、把三條槓收起來**。

   ═══ ★★★只給「真的開紀錄面板」的那三頁，不是六頁 ═══
   六頁的右鈕分成語意完全不同的兩組（實測 markup）：
     package / machine / machine_backup … `data-toggle-class="pos-sidebar-mobile-toggled"`
                                           `data-target="#pos"`         → 開**紀錄**面板，內容是文字「紀錄」
     checkout / darka / _package        … `data-toggle-class="pos-mobile-sidebar-toggled"`
                                           `data-target="#pos-customer"` → 開**顧客/購物籃**側欄，內容是購物籃 SVG ＋ badge
   ⇒ 把後三頁也標成「紀錄」是**貼錯標籤**，業主要的也只有前者。
     用屬性選擇器精準切開，後三頁一個位元組都不受影響。
   ★★★一定要用 `=` 完全比對，**不可以用 `*=`**：那兩個 class 名字是
     `pos-sidebar-mobile-toggled` 與 `pos-mobile-sidebar-toggled`
     ——**同樣的字、只是中間兩段對調**（`_header.blade.php` 已為此寫過警語）。
     `*=` 抓不到差別，`=` 才抓得到。
   ★實測佐證：對「另一組」屬性值做的探針元素，`font-size` 仍是 0px、
     `::before` 仍是 `display:block`（三條槓還在）⇒ 切分確實生效。

   ═══ 為什麼是「只放文字」，不是「圖示＋文字」（★量過才決定）═══
   375 寬那條 bar 的實測組成：
     bar          x=0 w=375 h=52，`padding: 0 52px`（52 = 8 內縮 + 44 鈕寬）
     左漢堡        x=8   44x44
     右鈕          x=323 44x44
     標題可用寬     375 - 52 - 52 = **271px**
     標題實際寬     「揀包出貨」16px/700 ＝ **62.73px**（x=80 ~ 142.73）
   ⇒ 標題只用掉 271 的 62.73，**剩 208.27px 空檔**。
     ★所以「加文字會把標題擠掉」這個顧慮**在實測上不成立**；
       即使右鈕加寬到 250px 標題也還不會被裁。
   ⇒ 既然寬度不是限制，選擇的依據就回到**字要多大**：
     · 只放文字：44x44 內放 14px/600 的「紀錄」＝ 實測 **27.65px 寬**，
       左右各留 8.17 / 8.18px，**置中且鈕的幾何完全不動**。
     · 圖示＋文字（上下疊）：44px 高要塞 16px 圖示 ＋ 文字，文字只能降到 12px。
   ⇒ 依 §POS-10「現場字要夠大」（倉庫、戴手套、單手持機），
     **14px 的兩個字 > 12px 的兩個字加一顆裝飾圖示** ⇒ 選只放文字。
     業主原話也是「我建議寫文字 紀錄」，文字是他點名要的，圖示只是備選。
   ★★附帶好處：鈕維持 **44x44**，`.pos-page-header` 的 `padding: 0 52px` 那個常數
     （§S-118 ②）**仍然正確**，不必跟著改；標題可用寬 271px 一格像素都沒少
     ⇒ **結構上不可能發生「標題被裁成半個字」**。
   ★觸控命中區維持 44x44（iOS HIG 下限，§S-118「三」已為此結案，不因為改成文字而縮小）。

   ═══ ★★★font-weight 有一條會靜默吃掉宣告的守門（本輪查到的新事實）═══
   本檔 L5293-5299 那條連結守門
     `#content.app-content .pos a:not()x5, #content.app-content a.pos-mobile-sidebar-toggler:not()x5`
   除了 `color:inherit` 之外**還有 `font-weight: inherit`**，特異性 (1,7,1)。
   ⇒ 它把這顆鈕的字重鎖成祖先的 400。
   ★★實測旁證：本檔**早就有**一條 `.pos-mobile-sidebar-toggler{font-weight:600}`(0,1,0)，
     那個 600 **從來沒有生效過**（改字級之前字是隱形的，所以沒人發現）。
     §S-118「七」只處理了同一條守門的 `color`，`font-weight` 這一格是本節才補上的。
   ⇒ 治法沿用 §S-118 的同一招，但更進一步：照抄守門選擇器 **再加上本節的屬性選擇器**
     ⇒ (1,**8**,1) > 守門的 (1,7,1)，**特異性直接勝出，不靠檔尾順序**。
   ⇒ 另寫一條裸的 (0,2,0) 給「沒有 `#content.app-content` 外殼」的頁面，**同值** 600，
     兩條路徑算出來一樣 ⇒ 結構上不可能漂（理由與 §S-118 ⑤ 逐字相同）。

   ═══ 顏色不必再處理（★但有驗）═══
   本節**不宣告 `color`**：§S-118 ⑤ 已經把同一條守門的 `color` 打贏成 `--foreground`，
   而文字節點就繼承它。實測：
     亮色 文字 rgb(0,0,0)     對 bar rgb(250,250,250)
     深色 文字 rgb(250,250,250) 對 bar rgb(10,10,10) ＝ **18.97:1**（與左漢堡圖示同值）
   ⇒ §S-118「七」記的那個「深色下 --dark-foreground 配 ghost 底 = 1.08:1 看不見」的坑，
     在這裡沒有復發（因為 §S-118 已經把它拔掉了，本節沒有把它請回來）。

   ═══ 功能三項實測（★點擊用 element.click()）═══
     ① 點右鈕      → `.pos` 加上 `pos-sidebar-mobile-toggled`，
                     `.pos-sidebar` display none → flex、375x667 全螢幕 ✓
     ② 再點一下    → class 移除、`.pos-sidebar` 回 display:none ✓
     ③ 遮罩        → `.pos-drawer-backdrop` 綁的是**左邊** rail 抽屜
                     （`data-dismiss-class="pos-rail-mobile-toggled"`），不是紀錄面板。
                     實測點左漢堡後遮罩 display:block、375x667、z-index 1022、
                     rgba(0,0,0,.5)，點遮罩即關閉 ✓。
                     ★紀錄面板本身是 375 寬的全螢幕抽屜，本來就沒有、也不需要遮罩
                       （§S-114「四」的堆疊順序設計），**這不是本節造成的**。
     ★★三項在改動前後行為相同：本節只動 font-size / font-weight / line-height /
       white-space 與 `::before{display}`，**沒有碰任何 JS、事件、data-* 屬性或 display**。

   ═══ 不動的東西 ═══
     · 左漢堡維持三條槓（業主只說右邊；它是選單，語意本來就對）
     · `::before` 的 mask-image 一個字都不動，只把它 `display:none`
       ⇒ 另外三頁照樣拿得到三條槓
     · `.badge` / `.img` / z-index / 幾何 / 抽屜行為 / 任何 blade
   ══════════════════════════════════════════════════════════════════════════ */
@media (max-width: 991.98px) {

    /* 放出 markup 裡本來就有的「紀錄」二字。
       ★`font-size` 要贏 §S-114 ⑪ 的 `font-size:0`(0,1,0) 與 Bootstrap `.h5`(0,1,0)
         ⇒ 本條 (0,2,0)。
       ★`line-height:1` 讓兩個字在 44px 裡正確置中（`.h5` 的行高是為標題調的）。
       ★`white-space:nowrap`：CJK 允許逐字斷行，44px 內雖然放得下（實測 27.65px），
         仍加這道保險 —— 一旦斷成兩行，鈕會被撐破 44px 而且不會有任何錯誤訊息。 */
    .pos-mobile-sidebar-toggler[data-toggle-class="pos-sidebar-mobile-toggled"] {
        font-size: .875rem;
        font-weight: 600;
        line-height: 1;
        white-space: nowrap;
    }

    /* 收起三條槓（只收這三頁的；mask-image 本體不動，另外三頁照舊）。 */
    .pos-mobile-sidebar-toggler[data-toggle-class="pos-sidebar-mobile-toggled"]::before {
        display: none;
    }

    /* 打贏 L5297 的 `font-weight: inherit`(1,7,1)：照抄守門選擇器 ＋ 本節屬性選擇器
       ⇒ (1,8,1)，特異性勝出。值與上面那條相同 ⇒ 兩條路徑不可能漂。 */
    #content.app-content a.pos-mobile-sidebar-toggler[data-toggle-class="pos-sidebar-mobile-toggled"]:not(.btn):not(.dropdown-item):not(.nav-link):not(.fb-tab):not(.page-link) {
        font-weight: 600;
    }

}

/* ══════════════════════════════════════════════════════════════════════════
   (三) 已登入圖卡的插畫：固定 180px 高 → 16:9（且永遠不會比現在高）

   ═══ ★★★先推翻交辦單的兩個數字（實測）═══
   交辦單寫「插畫約 280px 高、卡片約 450px、卡片寬約 320px ⇒ 每張省約 100px」。
   實測**插畫是 180px，不是 280px**，而且它**在任何狀態下都是 180px**：
     `.product .img`（本檔 L4053）只有 `min-height:180px`，沒有任何 height；
     它在 `.product`（flex column）裡是 `flex:0 1 auto`、內容高 0，
     而 `.text` 是 `flex:1` ⇒ **卡片被拉高時多出來的高度全部被 `.text` 吃掉，
     插畫永遠停在 180px**。所以「280px」不可能出現。
   ⇒ 省下來的量因此**不是**固定的 100px，而是隨欄寬變動（見下表）。

   ═══ 為什麼業主會覺得「太高」：欄數變多、欄變窄，180px 就變成近正方形 ═══
   `#pos-list` 是 `grid-template-columns: repeat(auto-fit, minmax(185px,1fr))`。
   `auto-fit` 會把空軌道塌成 0，所以**卡片數量直接決定欄寬**（1600x950、rail 展開實測）：
     3 張卡（部分登入）  欄寬 338.66  插畫 336.66x180  比例 **1.87:1**（比 16:9 還扁）
     5 張卡（完整登入）  欄寬 196.80  插畫 194.80x180  比例 **1.08:1**（近正方形 ← 業主說的「太高」）
   ⇒ 業主看到的是**完整登入的 5 張卡**那個狀態，那裡 180px 確實過高。

   ═══ ★★★「16:9」與「變低」在寬卡狀態下會互相矛盾，所以加了上限 ═══
   16:9 的高度 = 欄寬 ÷ 1.778 ⇒ **欄寬 320px 是分水嶺**：
     欄寬 < 320 → 16:9 比 180 矮（＝業主要的「變低」）
     欄寬 > 320 → 16:9 比 180 **高**（3 張卡時實測會變成 189.4，＋9.4px ＝ 反效果）
   ⇒ 本節用 `max-height:180px` 把**今天的 180px 從「下限」翻轉成「上限」**：
       · 窄卡（業主抱怨的那個狀態）：吃 16:9，真的變矮
       · 寬卡：停在 180px，**保證不會比今天高**
     一句話說給業主聽就是「圖片改成 16:9，而且永遠不會比現在高」。
   ★★★`min-height:0` 不可省：不蓋掉 L4054 的 `min-height:180px`，
     `height` 會被算成 max(180, 16:9) ⇒ **窄卡完全不會變矮，而且沒有任何錯誤訊息**
     ——整條規則會變成靜默的空操作。

   ═══ 實測（1600x950，用與頁面 JS 逐字相同的 markup 重建已登入卡）═══
     5 張卡：插畫 194.80x**180** → 194.80x**109.56**   卡片 **309.09** → **238.67**
             ⇒ 每張省 **70.44px**（−22.8%）
     3 張卡：插畫 336.66x180 → 336.66x**180**（上限生效）  卡片 309.09 → 309.09
             ⇒ **零變化**（本來就比 16:9 扁，沒有東西可裁）
   ★卡片內其他元素：`.text` 高度 127.09 → 127.09、`.title` / `.desc` / `.price`
     的寬高與相對位置**逐項相同**，只有整體 y 位移。**只有插畫變矮、卡片跟著變矮。**

   ═══ 裁切位置：三張都用既有的 center，**不需要逐張微調**（★算過也看過）═══
   三張原圖都是 **480x300（1.6:1）**，`background-size:cover` ＋ `background-position:center`
   （`.product .img` 既有值，本節不動）。裁掉的高度比例 = 1 − 1.6 ÷ 盒子比例：
     16:9 的盒子 → 共裁 **10.0%**（上下各 5%）
     今天 180px 的盒子（1.87:1，3 張卡時）→ 共裁 **14.4%**（上下各 7.2%）
   ⇒ ★**改成 16:9 之後裁掉的比今天還少**，不是變多。
   逐張確認主體（★真的開圖看過，不是推論）：
     user-4「工作臺」 等角視角的辦公桌與紙箱，主體在中央；上緣 5% 是白底，
                       下緣 5% 會碰到最底下那排紙箱的邊緣，**主體完整**。
     user-1「員工」   ★交辦單擔心「center 會把頭切掉」——**實測不會**：
                       人物頭部落在原圖 **y≈65~115 / 300**（約 22%~38%），
                       頭頂上方還有吊燈與大片留白；上緣只裁 5%（y 0~15）＝ 純背景。
                       **頭部距離裁切線還有 50px 的餘裕。**
     user-3「包裹＋掃描槍」 主體置中、四周大量白邊，上下各 5% 全是白底，**完全無損**。
   ⇒ 三張都安全 ⇒ **不加任何 per-image `background-position`**。
     多寫三條用不到的覆寫只會製造下一個人要維護的分岔（YAGNI）。

   ═══ ★★只打「已登入卡」，未登入 STEP 卡結構上碰不到 ═══
   `TemplateBuilder.productContainer()` 的分支決定了外層標籤：
     已登入   `<span class="product">`                  ← 插畫是 `<div class="img">`（背景圖）
     未登入   `<div class="product not-available ...">` ← 插畫是 `<img class="pos-step-illust">`
   ⇒ 選擇器寫 `span.product > .img` **在型別上就排除了未登入卡**，
     不是靠 `:not(.not-available)` 之類的排除清單。
   ★同理 `#log-list` 的頭像不受影響（本節前綴是 `#pos-list`），
     它在頁面自己的 `@media`-less 內嵌 CSS 裡是 `display:none`。

   ═══ 手機：≤576px 本來就看不到，這條在那裡是空操作（★查過才決定不加斷點）═══
   `package.blade.php` / `machine.blade.php` 自己的 `@section('css')` 有
     `@media (max-width:576px){ #pos-list span.product > div.img.phone-hide{display:none} }`
   而 `avatar()` 產生的 class 預設就帶 `phone-hide`。
   實測 375x667：已登入插畫 `display:none`、0x0 ⇒ **本節在 375 寬完全不生效**。
   ⇒ 因此**不需要**把本條關進桌機斷點；577~991px（平板）插畫是看得到的，
     那個區間本條照樣有效且有益。加斷點反而會讓平板白白留著近正方形的圖。

   ═══ 特異性 ═══
     對手 `.product .img`（L4053）                              (0,2,0)
     本條 `.pos #pos-list span.product > .img.img`              (1,4,1) 勝（有 id，直接定案）
   ══════════════════════════════════════════════════════════════════════════ */
.pos #pos-list span.product > .img.img {
    aspect-ratio: 16 / 9;
    min-height: 0;                      /* ★必須：不蓋掉 L4054 的 180px 下限就完全不會變矮 */
    max-height: 180px;                  /* 今天的高度變成上限 ⇒ 保證不會比現在高 */
}

/* --- S-125 end ------------------------------------------------------------- */

/* --- S-126. POS rail 收合態：頂部堆疊壓成 64px 帶，底線與中／右對齊（2026-08-11）-----
   ★★★ 本節是業主裁決，且它**推翻了 §S-125 (一) 註解裡的一句話**：
     §S-125 (一) 寫「收合態實測 sep 的 bottom 是 76 …… 那是 §S-97 (2) 照 OS 刻意做的直排堆疊，
     **不在業主這次的指令範圍內，故不動**」。
     我把這個 12px 落差回報給業主並問「要不要一起對齊」，業主回覆「**好**」。
   ⇒ §S-125 (一) 那句「故不動」自本節起**作廢**（★原句刻意保留在 §S-125 裡不去改，
     那才看得出「當時的判斷是什麼、為什麼後來變了」——同 §S-121a 的做法）。
     ★下一個人讀到 §S-125 (一) 那句時，請以本節為準。

   ═══ 量測環境 ═══
   `https://test.2025-seller.mtest/pos/package`，1600x950 / 768x900 / 375x667，
   Debugbar 在量測時以 `display:none` 排除（只影響 body 底部佔位，不影響 rail 與三個 header）。
   ★`/pos/machine` 未登入回 403（`routes/web.php` 掛 `can:extra.packaging-machine`），
     本節同 §S-125 一律以 package 代測；★因此 rail 只有「揀包」一個 nav-item
     （其餘項目在 `@can` 後面），**「包裝」那顆短標與 machine 頁的四顆工具鈕本節沒有量到**。

   ══════════════════════════════════════════════════════════════════════════
   一、落差是 12px，而且 76 是**內容撐出來的、不是寫死的**（★這是動手前先查的第一件事）

   ═══ 修前實測（1600x950，rail 收合態，rail 實寬 48px ＝ §S-113 ①）═══
     元素                        rect.y   height   rect.bottom
     左 `.pos-rail-back`            0       52          52
     左 `.pos-menu .logo`          52       23          75
     左 `.pos-rail-sep`            75        1        **76**
     中 `.pos-page-header`          0       64        **64**
     右 `.pos-sidebar-header`       0       64        **64**
   ⇒ 差距 = **12.000px**（不是 §S-125 (一) 那個 1px）。

   ═══ 76 的組成：三塊自然高度相加，沒有任何一條規則宣告過高度 ═══
   收合態的 `.pos-menu` 仍然是 flex column ——§S-97 (3) 把 `.pos-menu` 改成 grid 那條
   關在 `:not(.pos-rail--collapsed)` 裡，**收合態拿不到那個 `grid-template-rows: 64px …`**。
     `.pos-rail-back` 52 = §S-97 (2) 的 `padding-top:.75rem`(12) ＋ `.btn` 36（§S-97 (2)
                            照 OS `w-9 h-9`，帶 !important）＋ `padding-bottom:.25rem`(4)
     `.logo`         23 = `#pos_time` 的行框 15（10px 字 × 預設行高，實測 h=15.000）
                            ＋ §S-97 (2) 的 `padding: 0 .375rem .5rem` 的下緣 8
     `.pos-rail-sep`  1 = `height:1px`（`border-bottom` 畫在 border-box 裡面）
   ⇒ 52 + 23 + 1 = **76**。★**沒有一個 76、也沒有一個 75 被寫死在任何地方**，
     它純粹是「12 + 36 + 4 + 15 + 8 + 1」的和。

   ══════════════════════════════════════════════════════════════════════════
   二、能不能壓到 64？—— 先量「裡面實際需要多高」，不是先動手

   ═══ 不可壓縮的內容（★這兩個數字決定了做不做得到）═══
     返回鈕      **36px**（§S-97 (2) `.pos .pos-menu .pos-rail-back .btn{height:36px !important}`
                  照 OS `w-9 h-9`；★不可動：那是 OS 的觸控/視覺尺寸，也是 §S-125 之外的既有裁示）
     時鐘行框    **15px**（`#pos_time` 10px 字，§S-92 ⑨ 設 `font-size:10px; margin-top:0`；
                  ★時鐘不可隱藏 —— §S-92 ⑨ 的原註解「現場要看時間」）
     合計        **51px**
   ⇒ 帶子要做到 64、線佔帶子的最後 1 列（與 §S-125 (一) 同一招）
     ⇒ 線以上的堆疊要 **64px** ⇒ 可用留白 = 64 − 51 = **13px**（現況是 24px）
   ⇒ **13 > 0 ⇒ 壓得下去，而且一個內容元素都不必縮小。**
   ★★如果這個數字是負的（例如返回鈕若是 44px、時鐘若是 21px ⇒ 65 > 64），
     本節就**不會**硬壓 —— 那種情況下唯一的選項是「讓中／右也吃 76」，
     而那會動到 §S-125 剛對好的東西，必須回頭問業主。**這次不需要走那條路。**

   ═══ 13px 怎麼分（5 / 4 / 4）═══
     上緣 5px ／ 返回鈕與時鐘之間 4px（＝ OS `PosMenu.vue` 收合分支的 `gap-1`）／ 時鐘下方 4px
   ★不用 4/4/5 或均分：時鐘的 10px 字在 15px 行框裡上下各自帶約 2.5px 的行距空白
     ⇒ 5/4/4 的**視覺**留白是 上 5、間隙 6.5、下 6.5，比幾何均分更平衡。

   ═══ 修法（★與 §S-125 (一) 同一個語彙，不是另開一套）═══
     ① 上方兩塊的留白 24 → 13 ⇒ 堆疊自然高度 75 → 64
     ② `.pos-rail-sep` 用 `margin-top:-1px` 併進帶子的最後一列 ⇒ 線佔第 63~64 列，
        與中／右**逐像素相同**（中／右是 `height:64px` + border-box，1px 邊框畫在盒子裡面）
   ★★★**刻意不用固定 `height`**（例如 `.pos-rail-back{height:45px}`）：
     · 固定高度的失敗模式是「內容被裁掉／溢出」，而且沒有任何錯誤訊息；
     · 留白式的失敗模式是「帶子長高、線再度不齊」——**看得見、改得回來**。
     兩者都不完美，但要選失敗時會被發現的那個。這與 §S-125 (一)
     「不把 64 這個數字複製到第四個地方」是同一個取捨方向。
   ★★★**完全沒有用 `overflow:hidden` 藏任何東西**：實測修後
     `.pos-rail-back` scrollHeight 45 === clientHeight 45、
     `.logo` scrollHeight 19 === clientHeight 19，兩者 `overflow-y` 仍是 `visible`。

   ═══ 特異性（★逐條數過既有選擇器，不是照抄）═══
     ① 對手 §S-97 (2) `.pos .pos-menu.pos-rail--collapsed .pos-rail-back`  (0,4,0) **帶 !important**
        ⇒ **帶 !important 的宣告只能用 !important 打**（§S-96 的教訓，§S-97 (2) 自己也記了一次）。
          本條 (0,6,0) ＋ !important 勝。
        ★只寫 `padding-top` / `padding-bottom`，**不可以用 `padding` 簡寫**：
          收合態沒有人宣告過水平內距（blade 上的 `pt-3 pb-1` 只有垂直），
          簡寫會把水平那兩格一起歸零 —— 而 `.pos-rail-back` 是
          `d-flex justify-content-center`，歸零後 39px 寬裡塞 36px 鈕會貼死左右邊。
     ② 對手 §S-97 (2) `.pos .pos-menu.pos-rail--collapsed.pos-rail--collapsed .logo` (0,5,0)
          `padding: 0 .375rem .5rem`（**無** !important）⇒ 本條 (0,7,0) 勝，
          且**只覆寫 `padding-bottom`**，水平的 .375rem 與上緣的 0 原封不動。
     ③ `.pos-rail-sep` 的 `margin-top` 在收合態**零對手**：
          §S-125 (一) 那條關在 `:not(.pos-rail--collapsed)` 裡碰不到收合態；
          Bootstrap `.border-bottom` 只給 border、`.mb-2` 只給 margin-bottom
          （§S-125 (一) 已逐條查證過同一批對手）。
          本條仍寫成 (0,6,0)，理由與 §S-113 相同：**不靠「同分排在檔尾」取勝**，
          否則有人往後追加一條同分規則就會被無聲翻盤。

   ═══ 為什麼關在 `@media (min-width:992px)` ═══
   手機的 rail 是左側抽屜，`.pos-rail-back` 在那裡是整列鈕
   （§S-114 ⑦ `padding:.75rem 3rem .25rem .9375rem`，實測 375/768 皆 65px 高），
   `.pos-page-header` 是 52px（§S-118 ②），**根本沒有「三條底線並排」這件事**。
   ★JS 端另有 `matchMedia` 硬擋（桌機以外永不掛 `pos-rail--collapsed`，§S-92 六），
     但**不依賴那道閘門** —— JS 與 CSS 兩層都對才算數（§S-92 六 的原則照用）。
   ★實測旁證：375 / 768 兩個寬度下 `.pos-menu` 的 class 都**沒有** `pos-rail--collapsed`，
     且 `.pos-rail-sep` 的 computed `margin-top` 修前修後都是 `0px`。

   ══════════════════════════════════════════════════════════════════════════
   三、修後實測（★雙向驗過：收合態對齊 ＋ 展開態／手機零變化）

   ═══ (甲) 收合態三條底線（1600x950）═══
     左 `.pos-rail-sep`        y=63  h=1   bottom **64.000**
     中 `.pos-page-header`     y=0   h=64  bottom **64.000**
     右 `.pos-sidebar-header`  y=0   h=64  bottom **64.000**
     ⇒ spread **0.000**

   ═══ (乙) 收合態內部元素沒有被擠壞（修前 → 修後；★尺寸全部零變化，只有 y 位移）═══
     `.pos-rail-back`            h 52 → **45**（＝ 5 + 36 + 4）
     `.pos-rail-back .btn`       36x36 → **36x36**（y 12 → 5）
     `.pos-rail-back .btn svg`   16x16 → **16x16**（y 22 → 15）
     `.pos-menu .logo`           h 23 → **19**（＝ 15 + 4）
     `#pos_time`                 h 15 w 23.516 → **h 15 w 23.516**（y 52 → 45；文字「9:28」完整）
     nav-link「揀包出貨」        h 56 → **56**；短標「揀包」`display:block` w 19.66 → **19.66**
     `.pos-rail-toggle`          39x32 → **39x32**
     `.pos-theme-toggle`         39x32 → **39x32**
     溢出檢查                    `.pos-rail-back` 45/45、`.logo` 19/19（scrollHeight/clientHeight 相等）
   ★唯一的連動（與 §S-125 (一) 同型，只是量級不同）：
     `.nav-container` y 84 → 72、h 778.75 → 790.75（rail 總高 950 不變，帶子讓出的 12px 由它吃下）；
     nav-link y 89.25 → 77.25。
   ★`.pos-content-container`（x48 y64 1240x886）、`.pos-page-header`、`.pos-sidebar-header`、
     `.pos-page-header h1`、`.pos-sidebar-header .title` / `.order`、`.pos-menu`（48x950）
     逐項**零變化**。

   ═══ (丙) 展開態零變化（★§S-125 (一) 的那批元素再量一次）═══
     `.pos-rail-sep` y=63 h=1 bottom **64**（`margin-top` 仍是 §S-125 (一) 的 -1px）
     `.pos-page-header` bottom 64、`.pos-sidebar-header` bottom 64 ⇒ spread 0.000
     MYPOS `.h4` y=15.875 h=20 w=61.375、`#pos_time` y=35.875 h=16 w=61.375、
     `.pos-rail-back` y=0 h=64 / 其 `.btn` y=14 36x36、`.logo` y=0 h=64 x=51 w=158、
     `.pos-page-header h1` y=17.5 h=28 w=78.406、
     `.pos-sidebar-header .title` y=11.375 h=24、`.order` y=35.375 h=16.25、
     `.pos-content-container` x=210 y=64 1078x886、`.nav-container` y=72 h=828.75
     ⇒ 以上**逐項與修前完全相同**。本條被 `.pos-rail--collapsed` 擋在門外，結構上碰不到展開態。

   ═══ (丁) 手機零變化（375x667 / 768x900 逐欄位）═══
     兩個寬度下 `.pos-menu` class 皆為 `pos-menu bg-default`（無 collapsed）、
     `.pos-rail-back` 65px（computed padding 16/4/15/48）、
     `.logo` computed padding 15/8/15/15、`.pos-rail-sep` margin-top **0px** / margin-bottom 8px、
     375：`.logo` h 77.328、`#pos_time` y 113.328 h 21、sep y 142.328 bottom 143.328、
          `.nav-container` y 151.328 h 450.672、`.pos-page-header` h 52、
          `.pos-mobile-sidebar-toggler` 44x44 @x=323（字 14px/600 rgb(0,0,0)，§S-125 (二) 未受影響）
     768：`.logo` h 78.734、`#pos_time` y 114.734 h 21、sep y 143.734 bottom 144.734、
          `.nav-container` y 152.734 h 682.266、`.pos-page-header` h 52、
          `.pos-mobile-sidebar-toggler` 44x44 @x=716
     ⇒ 修前修後**逐欄位相同**。

   ═══ (戊) 收合 ↔ 展開切換來回一次（`element.click()` 打 `.pos-rail-toggle`）═══
     收合 → 展開 → 收合，兩態的三條底線都是 64/64/64，
     `.pos-menu` 寬 48 ↔ 210、`.pos-content-container` x 48 ↔ 210 正確跟隨，
     **沒有殘留、沒有中間態卡住**（`width` / `padding-left` 的 .15s transition 結束後量）。
   ══════════════════════════════════════════════════════════════════════════ */
@media (min-width: 992px) {

    /* ① 返回鈕外框：pt 12 / pb 4（合計 16）→ pt 5 / pb 4（合計 9）。
          ★對手帶 !important ⇒ 本條也要 !important（(0,6,0) > (0,4,0)）。
          ★只動垂直：水平內距收合態沒人設，用簡寫會連帶歸零（見上方特異性 ①）。 */
    .pos.pos .pos-menu.pos-rail--collapsed .pos-rail-back.pos-rail-back {
        padding-top: .3125rem !important;   /* 5px：帶子上緣留白 */
        padding-bottom: .25rem !important;  /* 4px：OS gap-1，返回鈕與時鐘之間 */
    }

    /* ② logo 區（收合態裡只剩時鐘，MYPOS 被 §S-92 ⑨ 的 :not(#pos_time) 藏起來）：
          下緣 8 → 4。★只覆寫 padding-bottom，水平 .375rem 與上緣 0 保持 §S-97 (2) 的值。 */
    .pos.pos .pos-menu.pos-rail--collapsed.pos-rail--collapsed .logo.logo {
        padding-bottom: .25rem;             /* 4px：帶子下緣留白 */
    }

    /* ③ 分隔線併進帶子的最後一列（與 §S-125 (一) 展開態逐字同一招）：
          堆疊 5+36+4+15+4 = 64 ⇒ sep 自然落在 y=64，-1px 之後佔第 63~64 列 ⇒ bottom 64。 */
    .pos.pos .pos-menu.pos-rail--collapsed > .pos-rail-sep.pos-rail-sep {
        margin-top: -1px;
    }

}
/* --- S-126 end ------------------------------------------------------------- */

/* ═══ §S-126a 附記：訂正 §S-126 註解裡的兩處（2026-08-11，同日同一輪，寫完就量到的）═══
 *
 * ■ 訂正一：特異性 ① 那句「簡寫會把水平內距一起歸零 ⇒ 鈕會貼死左右邊」**後果講錯了**
 *   §S-126 原句（保留在上面，不要刪）：
 *       「★只寫 `padding-top` / `padding-bottom`，**不可以用 `padding` 簡寫**：
 *         收合態沒有人宣告過水平內距（blade 上的 `pt-3 pb-1` 只有垂直），
 *         簡寫會把水平那兩格一起歸零 —— 而 `.pos-rail-back` 是
 *         `d-flex justify-content-center`，歸零後 39px 寬裡塞 36px 鈕會貼死左右邊。」
 *   ⇒ ★**前半對、後半錯**。實測收合態 `.pos-rail-back` 的
 *     `padding-left` / `padding-right` **本來就是 `0px`**（1600x950 實測 computed 值）。
 *     ⇒ 「歸零」不是簡寫造成的變化，它已經是現況；用簡寫寫成 `0` 在**今天**是無害的。
 *   ⇒ 鈕沒有貼死左右邊也不是 padding 的功勞：`.pos-rail-back` 內容寬 39、鈕 36，
 *     實測鈕 x=5.5（外框 x=4）⇒ 左右各 1.5px，那是 `justify-content:center` 給的。
 *   ★★★這是「**沒量就寫下去的因果**」，與 §S-124a 是同一族的錯：
 *     推論的形狀對（簡寫的確會覆蓋四邊），但**我沒有先去讀那兩格現在是多少**就寫了後果。
 *   ★做法本身**不改**（仍然只寫 padding-top / padding-bottom），但**理由要換成正確的那個**：
 *     本節只負責垂直留白，就不要去宣告一個本節沒打算負責的屬性 ——
 *     否則哪天有人在別處給收合態的返回鈕加了水平內距，會被本節的簡寫無聲吃掉。
 *     （＝ 它是**防未來**，不是**修現在**；與 §S-124a 訂正後的定位相同。）
 *
 * ■ 訂正二：三(丁)「手機零變化」那批數字是**跨 browser session** 量的，有排版抖動
 *   §S-126 三(丁) 記的 375 值（`.logo` h 77.328、`#pos_time` y 113.328、sep y 142.328、
 *   `.nav-container` y 151.328 h 450.672）與 768 值（`.logo` h 78.734、`#pos_time` y 114.734、
 *   sep y 143.734、`.nav-container` y 152.734 h 682.266）是**寫入前**那一輪量的；
 *   寫入後重量得到 375：`.logo` 78 / `#pos_time` 114 / sep 143 / nav 152 h 450，
 *   768：`.logo` 79 / `#pos_time` 115 / sep 144 / nav 153 h 682。
 *   ⇒ 兩輪差 **0.266 ~ 0.672px**，而 §S-126 那句「修前修後**逐欄位相同**」因此**不精確**。
 *   ★差異的來源不是 §S-126：它關在 `@media (min-width:992px)` ＋ `.pos-rail--collapsed`，
 *     而 375/768 兩個寬度下 `.pos-menu` 的 class 實測都是 `pos-menu bg-default`（無 collapsed）。
 *     兩輪之間換過 browser session（viewport 換了、字體點陣化狀態不同）⇒ 是量測抖動。
 *   ★★**但「我推論它碰不到」不算證據**，所以改用**同一個 session 內的控制組**重驗：
 *     在 375 與 768 各載入一次頁面，先量一次（實驗組），
 *     再用 CSSOM `deleteRule()` 把本節那三條規則**從記憶體裡移除**（★live 檔零寫入零還原，
 *     `removed:3` 三條都確實被摘掉），立刻再量一次（控制組）。
 *     結果：13 個欄位（`.pos-menu` / `.pos-rail-back` / 其 `.btn` / `.logo` / `#pos_time` /
 *     `.pos-rail-sep` / `.nav-container` / `.pos-page-header` / 其 `h1` /
 *     `.pos-content-container` / `.pos-mobile-sidebar-toggler` / `.pos-menu-footer` /
 *     `.pos-theme-toggle`）在**兩組之間逐欄位完全相同**，375 與 768 都是。
 *   ⇒ **「手機零變化」的結論不變，但證據從「跨 session 的前後比對」換成「同 session 的控制組」。**
 *     ★通則：跨 session／跨日的前後比對會製造 0.x px 的假差異（記憶裡已有同型教訓），
 *       要證明「某條規則沒有影響」，**同一個 session 內把規則拿掉再量**才是乾淨的對照。
 *   ★順帶在同一個控制組裡確認 §S-125 (二) 沒被本節碰到：375 的
 *     `.pos-mobile-sidebar-toggler` 仍是 44x44 @x=323、字 14px / 600 / rgb(0,0,0)。
 *   ★★三(乙) 收合態那批「修前 → 修後」**不受本則影響**：那是在**同一個 session** 內
 *     用記憶體探針（注入 style → 量 → 移除 style）做的，本來就是乾淨的同 session 比對。
 *     只有三(丁) 手機那批是跨 session 的。
 *     （寫入後另一個 session 重量收合態，同樣是 back 45 / btn y5 36x36 / svg y15 16x16 /
 *       logo 19 / `#pos_time` y45 h15 / nav-link y77.25 h56 / footer 39x32；只有兩個
 *       純寬度值落在抖動範圍內：`#pos_time` w 23.516 → 23.508、短標「揀包」w 19.66 → 19.65。）
 *
 * ■ 沒有被訂正的（仍然成立）
 *   · 收合態三條底線 64.000 / 64.000 / 64.000、spread 0.000（1600x950 實測）
 *   · 內容零壓縮：返回鈕 36x36、其 svg 16x16、`#pos_time` 行框 15、短標「揀包」w 19.65、
 *     footer 兩顆鈕 39x32；`.pos-rail-back` 45/45、`.logo` 19/19（scrollHeight===clientHeight）、
 *     兩者 `overflow-y` 仍是 `visible`（**沒有用 overflow:hidden 藏任何東西**）
 *   · 展開態逐項零變化（sep bottom 64、MYPOS / 時鐘 / `.pos-rail-back` / `.logo` /
 *     `pageH1` / `sideTitle` / `sideOrder` / `.pos-content-container` 全部與修前相同）
 *   · 收合 ↔ 展開來回一次無殘留（rail 48 ↔ 210、`#pos` padding-left 48 ↔ 210）
 * ═══════════════════════════════════════════════════════════════════════════ */
/* --- S-126a end ------------------------------------------------------------ */

/* --- S-127. 已作廢條文索引表（2026-08-11）------------------------------------
 *
 * ■ 本節是什麼
 *   一張**全檔唯一**的「哪些條文已經不算數」速查表。零宣告、零選擇器、零大括號，
 *   它只是文件；頂層規則數與大括號數在本節前後必須完全相同。
 *
 * ■ 為什麼需要它（★這才是重點，不是為了整齊）
 *   本檔的作廢標記原本**散在至少 40 處**：有的寫在被作廢那一行的正上方，有的寫在
 *   兩千行外的新章節裡，有的只寫在「本節推翻／覆蓋的舊條文」小標底下。
 *   散落標記的弱點不是難看，是**讀的人不知道要去哪裡查**——他打開 §S-4 讀到
 *   「未帶尺寸修飾的表單控制項 = 36px」，那句話本身沒有任何異狀，
 *   他照著寫出來的東西會「完全符合規格」而且是錯的，而且他不會知道自己錯了。
 *   （這一族坑的完整教訓見 memory `feedback_superseded_spec_clause_must_be_marked_dead`：
 *     那則 feedback 就是在本檔與 .agent/uiux.md 上同一天撞三次撞出來的。）
 *
 * ■ 怎麼用
 *   ★**修改任何章節之前，先用該章節的 §編號在本表搜一次。**
 *   搜得到 = 那一節至少有一部分已經不算數，先讀「現行依據」欄再動手。
 *   搜不到 ≠ 一定有效（本表只涵蓋**已經被標記過**的作廢；見文末「完整性聲明」）。
 *
 * ■ ★行號會漂，§編號不會
 *   下表行號是 2026-08-11 / HEAD 40c89395 的快照，只當「大概在哪一段」用。
 *   ★本檔已經被行號漂移害過至少兩次（§S-46 訂正紀錄、§S-48 訂正紀錄記的就是
 *     「把行號引用改成章節引用」那兩輪）⇒ **新增作廢標記時一律寫 §編號，不要只寫行號。**
 *
 * ■ ★★三種東西長得很像，本表只收第一種（分不清會把索引表變成噪音）
 *   ① **條文作廢／推翻**＝這條規矩曾經有效，後來被改掉或被業主推翻。照它做會做出錯東西。
 *      → 收進甲表。
 *   ② **死宣告／死選擇器**＝它從頭到尾就沒生效過（特異度輸掉、markup 不存在）。
 *      → **不收**。§S-101 (3) 已經明文區分過這兩件事：
 *        「§S-86 二 `.pos .btn` 44px 與 §POS-4 40px 在 §S-101 之前**就已經是死宣告**」，
 *        它們被同步改值不是因為在作用，是為了防「哪天有人拿掉那條 (0,9,0) 就無聲復活」。
 *   ③ **宣告層級的層疊標記**＝「★已被 ○○ 覆蓋（實測生效值：…）」那一族，全檔 **168 處**。
 *      它記的是「同一顆屬性最後由誰勝出」，屬於 CSS 層疊事實，不是規格改方向。
 *      → **不收**（守門測試 ThemeOverrideAnnotationsTest 掃的就是這一族，見 §S-46/§S-48）。
 *      ★★上一行的占位符刻意寫「○○」而不是「§XX」，這不是排版偏好：
 *        ThemeOverrideAnnotationsTest 是**逐行掃整個檔案**的（不是只掃宣告附近的註解），
 *        它的兜底掃描抓 `已被\s*§…` 就去驗那個章節存不存在 ⇒ 在本節裡寫一個假的 `§XX`
 *        會被當成真引用，測試立刻紅（本節初版就是這樣紅的：L25584 + C02 兩筆
 *        「引用了不存在的章節 §XX」）。★這是**寫文件會踩到程式碼守門**的實例：
 *        本節在描述那個標記的格式，而守門分不出「描述格式」與「使用格式」。
 *        ⇒ 以後在本檔任何說明文字裡舉例覆蓋註解，占位符一律用 ○○
 *        （該測試自己的 docblock 用的也是 ○○，同一個理由）。
 *
 * ■ ★還有第四種：**復活**（舊條文重新生效）。它與作廢方向相反，混進來會害人做反。
 *   本表用「↺」標出來，目前 1 筆（甲表 A18）。
 *
 * ============================================================================
 * 甲表：條文作廢／推翻（速查）
 *   欄位＝ 編號 ｜ 被作廢的條文在哪（§ + 行號）｜ 作廢它的是誰（§ + 日期）｜ 現在該照哪一條
 *   ★「為什麼」在下面「甲表逐條理由」那段，一條都沒有省略——
 *     只寫「已作廢」而不寫原條文當初成立的條件、以及那個條件為什麼不再成立，
 *     下一個人就無法判斷它會不會又變回有效（A18 就是變回有效的實例）。
 * ============================================================================
 *
 *  A01 ｜ §1 --muted-foreground 上方「刻意不動、不跟上游分岔」L74-82
 *       ｜ §S-72（2026-08-07）｜ 現行：L91 官方 achromatic 值 0 0% 45.1%
 *  A02 ｜ §1b 暗色 --ring 上方「拉到純白是刻意加強」L328-330
 *       ｜ §S-72（2026-08-07）｜ 現行：L331 官方值 0 0% 45.1%。★帶已知無障礙代價，見理由
 *  A03 ｜ §1 --radius-md 註解把 Badge 列進 8px 那一階 L227
 *       ｜ 2026-08-09 更正 L228-235 ＋ §24（L2223 改 9999px）｜ 現行：Badge 是藥丸 9999px
 *  A04 ｜ §8/§24 .badge 的 2026-08-04 稽核「實測全站是 8px 圓角矩形」L1046-1049
 *       ｜ 2026-08-09 更正 L1050-1058｜ 現行：全站 badge 一律 9999px
 *       ｜ 其 ⑶「唯一例外 .reissue-type-badge 仍是 8px」→ 2026-08-10 更正 L1059-1070，例外已不存在
 *  A05 ｜ §40 字級 utility「值 1:1 對應、未來要統一改這裡即可」L3024-3025
 *       ｜ §S-5 字級下限 12px（2026-08-06 訂正記於 L3027-3034）
 *       ｜ 現行：.fs-7/.fs-9/.fs-10/.fs-11/.fs-9px/.fs-10px/.fs-11px 七個要改 §S-5，改這裡不生效
 *  A06 ｜ §S-68 ③「相對官方的偏離總表」所依據的基準 B（zinc 一套）L14150-14156
 *       ｜ §S-72（2026-08-07）｜ 現行：基準換成 apps/v4 achromatic 的 A 案；表的內容一字未刪，但基準變了
 *  A07 ｜ §S-75 第二段「滿版色帶＝色票牆」這個**病因判斷**
 *       ｜ §S-76（L14811-14813）｜ 現行：病因是「用標題語意承載封面角色」，解法是拆成封面＋標題
 *  A08 ｜ §S-83 正文 ＋ .agent/TODO.md D19「官方 CardHeader/Content/Footer 全部 px-6 = 24px」
 *       ｜ §S-83 訂正（2026-08-08，L16276 起）｜ 現行：去讀 card.tsx 原始碼，該前提不成立
 *  A09 ｜ StoreSetting/_picker_card.blade.php:273「字級刻意取 13px 不是官方 14px」
 *       ｜ §S-87（2026-08-08，L16782-16795）｜ 現行：該 markup 已不存在（活節點 0 顆），14px
 *  A10 ｜ components/batch-bar.blade.php:73「全站表格內文 13px 都由 .fs-13px 供給」
 *       ｜ §S-64 之後即過時，§S-87 收掉（L6028-6032）｜ 現行：表格內文 14px
 *  A11 ｜ §S-46b「.pos-sidebar-header 亮暗都是 rgb(24,24,27) 深底」
 *       ｜ §S-86 一（2026-08-08）改成 --muted；由 §S-94 ④ 就地標出（L19459-19461）
 *       ｜ 現行：見 A15，該處底色已再由 §S-97 改成 --card
 *  A12 ｜ §S-90 第六節「⑤ 黑色是目前唯一有效的指示」這四個字 L17536-17541
 *       ｜ 同節 (7)（2026-08-09 業主二度、三度推翻）｜ 現行：以第六節 (7) 的最終看稿定案為準
 *  A13 ｜ §S-90 第八節「三層中性灰階、目前段填 --border」L18004-18013
 *       ｜ 業主「目前請你改為黃色」（2026-08-10）｜ 現行：目前段填 --warning-bg；三層推導其餘部分仍成立
 *  A14 ｜ §S-92 (3) STEP 卡插畫（position:absolute inset:0 那組）
 *       ｜ §S-95 (1)（2026-08-10）｜ 現行：.not-available 的插畫 display:none
 *  A15 ｜ §S-86 一 .pos-sidebar-header 底色 = --muted
 *       ｜ §S-97（2026-08-10，L20116 明寫「刻意推翻」）｜ 現行：--card ＋ 必留的 border-bottom
 *  A16 ｜ §36 SweetAlert 圖示「降成 48px」的折衷方案
 *       ｜ §S-100（2026-08-10 業主裁決「完全移除，照官方」，L20702-20704）｜ 現行：.swal-icon 整個 display:none
 *  A17 ｜ .agent/uiux.md 的「四階規格」（2026-08-09 裁決）
 *       ｜ §S-101（2026-08-10，L20847）｜ 現行：全站只有 32px 一階
 *  A18 ｜ ↺ **復活** .agent/uiux.md 已作廢索引表裡 2026-08-09 判死的「全站 btn-sm」
 *       ｜ §S-101（2026-08-10，L20848-20850）｜ 現行：**重新生效**。★這是反轉不是新增條文
 *  A19 ｜ POS 觸控階：§S-86 二、§POS-4、§POS-5、§POS-10、.agent/shadcn-migration.md、
 *         2026-fatboy/.agent/shadcn-block-spec.md 的 F 型
 *       ｜ §S-101 (3)（2026-08-10）｜ 現行：32px。★其中三條在被推翻前就已是死宣告，見上面「三種東西」②
 *  A20 ｜ §S-94「標題列工具鈕 = 36px」（當時為 B~F 頁預留的合約）
 *       ｜ §S-101 (4)（2026-08-10）｜ 現行：32px
 *  A21 ｜ §S-4 (a)「未帶尺寸修飾的表單控制項 = 36px」＋ 同條的 .input-group-text 36px；§S-4 (d) .h-45px 的 36/40 兩階
 *       ｜ §S-103 (1)(4)（2026-08-10）｜ 現行：32px
 *  A22 ｜ §S-89 :where(.fb-public) .form-control 的**垂直**三個值（min-height 40 / padding 7px / line-height 24）
 *       ｜ §S-103 (2)（2026-08-10）｜ 現行：32 的算式。★左右 padding 12px 與 font-size 14px **沒有**被作廢
 *  A23 ｜ §login .login .form-control/.form-select 的**垂直**值 40px
 *       ｜ §S-103 (3)（2026-08-10）｜ 現行：32。★左右 .7rem 不動
 *  A24 ｜ §POS-3 .pos .form-control 40px 與 .form-control-sm 34px
 *       ｜ §S-103 (6)（2026-08-10）｜ 現行：32
 *  A25 ｜ §form-floating 的 36px 與 .login 版的 40px !important
 *       ｜ §S-103 (7)（2026-08-10）｜ 現行：32
 *  A26 ｜ 2026-07-28 那條 .btn.btn-lg:not(.btn-icon) 的 padding 0 2rem / font-size .875rem
 *       ｜ §S-103 (8)（2026-08-10）｜ 現行：與 .btn-sm 同值
 *  A27 ｜ §S-101 章節註解「.btn-lg 的左右氣勢是方案卡 CTA 的視覺重量，沒有指令就不改（YAGNI）」
 *       ｜ §S-103（2026-08-10，L21547-21548）｜ 現行：業主已下指令，該句已被推翻
 *  A28 ｜ §S-101 (6) 註解「:where(.fb-public) 那條刻意不加 span 例外」的整段推論
 *       ｜ §S-101a（2026-08-10，L21235-21245）｜ 現行：公開頁的 span.btn.btn-xs 要排除，維持 24px
 *  A29 ｜ §S-95 (4)(5) POS 標題列「藏起來」的裁決
 *       ｜ §S-104（2026-08-10 業主「這頁應該要跟包裝機一樣有標題」「統一 ssot」）｜ 現行：五頁一律常駐顯示
 *  A30 ｜ §S-103 附記 (4) 開的藥方（在 @media max-width:768px 內補 .login.login-v2 … font-size:16px）
 *       ｜ §S-105（2026-08-10，L22102-22107）｜ 現行：那個寫法同分但先載入，**不會生效**；改在元件自己的檔案裡做
 *  A31 ｜ §S-66「動作組 order:99 靠右」（L13382 就地標記）、§S-69（L13754 一帶）、§S-70（L16881 一帶）
 *       ｜ §S-111（2026-08-10）｜ 現行：靠左是全站預設，靠右改由元件 aux slot 明說
 *       ｜ ★§S-66 的 ::before 填充位、更新時間、說明鈕**仍然有效**，不要整節當死碼
 *  A32 ｜ §S-38 .pos-theme-toggle min-height 44px ＋ §S-92 ⑩ ＋ §S-97 一 的 36px
 *       ｜ §S-113（2026-08-10 業主「四顆一致」「拉齊 32」）｜ 現行：桌機 32px
 *       ｜ ★★**手機沒有被推翻**：§S-38 max-width:991.98px 那段仍是 44x44
 *  A33 ｜ §S-94 收合鈕 border-radius 8px（註解寫「＝官方 Button 那一階」）與圖示 16px；§S-97 一 的 font-weight 600
 *       ｜ §S-113（2026-08-10）｜ 現行：10px（站內語彙）／18px／500
 *  A34 ｜ §S-112 (5)「本檔有兩處手寫了 md 這一階的語意卻沒用官方值」
 *       ｜ §S-112 附記（2026-08-10，L23584-23600）｜ 現行：L7582 .daterangepicker 早就是官方值，(5) 把它列進去是錯的
 *  A35 ｜ §S-99 註解裡兩句（「發票資訊」卡是刻意排除的反例、命中 4 張卡的名單）
 *       ｜ §S-117（2026-08-10，L24016-24042）｜ 現行：命中名單是 發票資訊／載具稅務／賣方／買方
 *       ｜ ★規則本體與選擇器一個字都沒改，也不該改——過時的只有註解
 *  A36 ｜ §S-122 標題與內文的範圍宣告「POS 說明 popover **全面**放行」「不再是白名單」
 *       ｜ §S-123（2026-08-11 業主當面）｜ 現行：只有**純說明型**要點得出來；動作型觸發器不建立也不顯示
 *       ｜ ★§S-122 的 display:block 規則本身、(A)(B) 成因分析、§S-120 白名單理由**都仍然有效**
 *  A37 ｜ §S-124「desktopLoggedIn() 那一列被裁掉，比裁掉取消嚴重得多」這半句
 *       ｜ §S-124a（2026-08-11）｜ 現行：那一列目前沒破版，§S-124 對它是**預防**不是修現在
 *  A38 ｜ §S-125 (一)「那是 §S-97 (2) 照 OS 刻意做的直排堆疊，不在指令範圍內，**故不動**」
 *       ｜ §S-126（2026-08-11 業主回覆「好」）｜ 現行：收合態頂部堆疊壓成 64px 帶，三條底線對齊
 *  A39 ｜ §S-126 特異性 ① 那句「簡寫會把水平內距一起歸零 ⇒ 鈕會貼死左右邊」的**後果**
 *       ｜ §S-126a 訂正一（2026-08-11）｜ 現行：做法不變（仍只寫 padding-top/bottom），但理由換成「防未來」
 *  A40 ｜ §S-126 三(丁)「手機修前修後**逐欄位相同**」這個精確度宣稱
 *       ｜ §S-126a 訂正二（2026-08-11）｜ 現行：結論不變，但證據改用同 session 控制組（CSSOM deleteRule）
 *
 * ============================================================================
 * 甲表逐條理由：原條文當初成立的條件是什麼、那個條件為什麼不再成立
 *   ★不寫這一段，這張表就只是一份「不要用了」的清單，
 *     下一個人既無法判斷它會不會復活（A18 已經發生過一次），也無法判斷
 *     被推翻的是**整節**還是**其中一句**（A31/A32/A35/A36 都只死了一部分）。
 * ============================================================================
 *
 *  A01/A02/A06｜原條件：本檔是移植自 2026-fatboy 的 zinc 一套，當時的最高原則是
 *      「不跟上游分岔」，所以即使量到 --muted-foreground 對比只差 0.1 也刻意不動。
 *      條件消失：2026-08-07 業主定調「一切都恢復 shadcn 官方設定」，且對版目標從
 *      zinc（B）換成 apps/v4 的 achromatic（A）——**基準換了，「不分岔」指向的對象就換了人**。
 *      ★A02 要付代價，寫清楚才不會被當成抄錯改回去：暗色 --ring 由純白改成官方中灰後，
 *        白底焦點環對比 19.9 → 2.58，疊 .5 之後實測約 1.65，**低於 WCAG 2.2 SC 1.4.11 的 3:1**。
 *        這是「照官方」與「無障礙」的已知衝突，不是本輪改壞的。
 *  A03/A04｜原條件：有人把「本檔這行註解」當成 shadcn 的權威來源。
 *      條件不成立：那行註解本身就是照同一個錯誤認知寫的，是**同源的回音**不是第二個證人；
 *      官方 badge.tsx 的 base class 是 rounded-full。
 *      ★這條害過人：112 顆唯讀膠囊曾被據此從藥丸改成 8px 方角。
 *      ★A04 的 ⑶ 是**過時**不是**錯誤**（它記的是量測當下的事實），兩者要分開判斷，
 *        否則讀的人會連還沒過時的註解都不敢信。
 *  A05｜原條件：§40 當初是「把 inline font-size 1:1 搬成 class」，所以「改這裡即可」成立。
 *      條件消失：§S-5 訂了字級下限 12px，同特異度、同 !important、排在後面 ⇒ §S-5 勝。
 *      現在改 §40 那七個小字級**不會生效**，卻沒有任何錯誤訊息。
 *  A07｜原條件：§S-75 觀察到「8 張並排像色票牆」，就把病因寫成「色塊太大」。
 *      為什麼不再成立：那是把症狀當病因。真病因是色帶被寫成 h5 card-title，
 *      用標題語意承載封面角色 ⇒ 大也不對（壓過 h1）、小也不對（品牌不見）。
 *  A08｜原條件：憑印象認定官方 CardHeader 是 px-6。條件不成立：實際抓 card.tsx 原始碼後
 *      發現前提是錯的，整道題目（「本專案 16px < 官方 24px」）因此不存在。
 *  A09/A10｜原條件：那兩則 blade 註解描述的 markup 當時存在。
 *      條件消失：markup 已被換掉（_picker_card 的 fs-13px 活節點 0 顆、表格內文已 14px）。
 *      ★§S-87 記下的判準值得抄走：**「靜態 grep 命中」不等於「活節點存在」**——
 *        過時的「刻意」註解比沒有註解更危險，它會讓下一個人以為有人想過而不敢動。
 *  A11｜原條件：§S-46b 是 2026-08-06 的實測，當時 .pos-sidebar-header 確實是深底。
 *      條件消失：§S-86 一在兩天後把它改成 --muted（同 (0,3,0)、排在後面勝出）。
 *      ★這條是**已經害過人的實例**：後來有代理讀了 §S-46b 就推論「outline 鈕會隱形」，
 *        結論整個是錯的，被 §S-94 ④ 實測推翻。
 *  A12/A13｜原條件：業主當天對同一張卡的顏色連續表態，每一輪的「最新指示」都寫成了「唯一有效」。
 *      條件消失：後面還有第二次、第三次表態。★教訓：**「目前唯一有效」這種措辭本身就會過期**，
 *      寫的時候要標日期，並預期它會被下一次裁決蓋掉。
 *  A14/A15/A29｜原條件：§S-92/§S-95/§S-86 各自只有自己那一頁的實測證據，
 *      刻意不去動沒驗過的其他頁（這在當時是對的取捨，比憑推論改設計好）。
 *      條件消失：業主要的是**跨頁一致**（「統一 ssot」），一致性的優先級高於「只改驗過的那頁」。
 *  A16/A17/A19/A20/A21~A27/A32/A33｜原條件：這些值都來自 shadcn 官方階梯（36/40/44/8px…），
 *      而當時的原則是「照官方」。條件消失：2026-08-10 業主裁決「全站只有 32 這一階」，
 *      並要求標明這是**刻意偏離官方**。⇒ ★看到 36/40 不要「修回官方值」，那會推翻業主裁決；
 *      要改回官方階梯必須**再問一次業主**。
 *      ★A32 的例外要記牢：手機的 44x44 觸控目標**沒有**被推翻，只有桌機被收到 32。
 *  A18｜★這是全表唯一的**復活**。原條件：2026-08-09 的四階規格把「全站 btn-sm」判死。
 *      條件消失：08-10 業主改判「全站 32 = btn-sm」，被判死的那條**重新生效**。
 *      ⇒ **作廢不是單向的**。看到某條文在索引表上，先看它是「作廢」還是「↺復活」。
 *  A28/A30/A34/A37/A39/A40｜這一族的共同原因是**方法錯，不是手滑**：
 *      · A28：拿「現在 grep 是 0」當「以後也是 0」的保證。例外要用規則保證，不是用「目前剛好沒有」。
 *      · A30：只比特異度沒比載入順序，同分時先載入者輸——那個藥方寫下去不會生效。
 *      · A34：引用了註解的文字而沒去讀它下面那條宣告真正寫了什麼。**註解裡的數字是歷史，不是現況。**
 *      · A37：看到「同一個結構模式」就推論「所以它也壞了」。結構相同不等於症狀相同，
 *             need 是內容決定的（235.30 vs 130.00，一個早就破了、一個還沒）。
 *      · A39：推論的形狀對（簡寫的確會覆蓋四邊），但沒先去讀那兩格現在是多少就寫了後果。
 *      · A40：跨 browser session 的前後比對會製造 0.x px 的假差異；要證明「某條規則沒有影響」，
 *             唯一乾淨的做法是**同一個 session 內把規則拿掉再量**。
 *      ⇒ ★共通判準：註解裡每一句比較級（「這裡也壞了」「這個更嚴重」「逐欄位相同」）
 *        都要有**它自己的那一組數字**。沒有數字的比較級，就是把推測寫成事實留給下一個人當前提。
 *  A31｜原條件：元件契約說「位置由放進哪個 slot 決定」，主題實作卻說「位置由組裡有沒有一顆
 *      特定按鈕決定」（先是 .btn-primary，後來再加一個 data-ph-primary 替身）。
 *      條件消失：業主 2026-08-10 點名「動作列 SSOT 沒做好」。⇒ §S-111 翻轉預設，
 *      靠左變成預設、靠右要用 aux slot 明說。★替身指標的失效 §S-70 自己就寫過：
 *      想讓一顆按鈕靠左，就得先把它塗黃——版面與顏色被綁在同一個 class 上。
 *  A35｜原條件：「發票明細」當時與 meta 四格同在一張卡裡，所以最後一條線是區段分隔線不是懸空線。
 *      條件消失：「發票明細」已改成獨立一張卡。
 *      ★★這則作廢**特別難被發現，因為筆數仍然是 4**：原句寫「命中 4 張卡」，重測也是 4，
 *        換掉的是**成員**不是數量。只核對數字、不核對名單的話，錯誤說明可以永遠留在檔案裡。
 *  A36｜原條件：§S-122 為了修「手機點不出說明」，把範圍寫成「全面放行」。
 *      條件消失：業主實際使用後發現，動作型觸發器點一下同時跳說明泡泡與視窗 ⇒ 說明變成雜訊。
 *      ★§S-123 攔下的那個坑要一起記住：**不能用 CSS 把泡泡藏起來**——實例與 click 監聽器都還在，
 *        第一下點擊會被吃掉變成「只關泡泡不開視窗」，而畫面 200、console 乾淨、CSS 也生效，
 *        四盞燈全綠而功能是壞的。正解是在捕獲階段 dispose 掉那個實例。
 *  A38｜原條件：§S-125 只做業主指令範圍內的三件事，12px 落差「不在指令範圍內，故不動」——
 *      這在當時是正確的自律。條件消失：把落差回報給業主之後，業主說「好」⇒ 範圍擴大了。
 *      ★這是**授權範圍**的變更，不是事實訂正：同一句話在業主開口前後，對錯不同。
 *
 * ============================================================================
 * 乙表：**知道會過期、但本輪刻意沒處理**（不是作廢，是待辦；別當成有效條文照抄）
 * ============================================================================
 *  B01 ｜ 本檔 4 處把 `0 1px 2px 0 rgb(0 0 0 / .05)` 註解成「shadow-sm」
 *       ｜ L1250、L9470、§S-92 L20005 引用、§S-96 L20028
 *       ｜ 狀態：**值是對的，名字過時**——那個值在 shadcn v4 叫 `shadow-xs`，v3 才叫 shadow-sm。
 *       ｜ §S-108 當時不改的理由：§S-92/§S-96/§S-97 是另一條線正在動的 POS 區段，
 *         要等上游協調後統一改名。⇒ 讀到那四處請自行換算，不要拿 v3 的名字去對版。
 *  B02 ｜ §S-108 記的「.shadow-lg 未被端上桌，本節不動、請上游決定」
 *       ｜ 狀態：**已經被做掉了**（§S-112 把 .shadow-lg 收成官方值）。
 *         留在這裡是因為 §S-108 那句話還在原地，讀的人會以為它仍待決。
 *
 * ============================================================================
 * 丙、看起來像作廢標記、但不是的（★誤收進索引表會讓表變成噪音）
 * ============================================================================
 *  C01 ｜ §S-121a ——它是「§S-121 的**隱性依賴**清單」（四條依賴 ＋ 一條反向指引），
 *         §S-121 一個字都沒有被作廢。★它之所以常被誤認，是因為它用了「附記」這個外殼，
 *         而本檔其他「附記」（§S-112 附記／§S-124a／§S-126a）確實都是訂正。
 *         ⇒ **判斷一則附記是不是作廢標記，要看它有沒有指名「哪一句不算數」，不要看外殼。**
 *  C02 ｜ 全檔 168 處「★已被 ○○ 覆蓋（實測生效值：…）」——宣告層級的層疊事實，不是條文作廢。
 *         （占位符為何是 ○○ 不是 §XX，見上面「三種東西」③ 的星號說明。）
 *  C03 ｜ §S-46 訂正紀錄、§S-48 訂正紀錄——它們訂正的是「覆蓋註解自己的引用寫法」
 *         （行號引用改成章節引用），不是任何設計條文。
 *  C04 ｜ §S-110 標的「§S-94 一條已零命中的死選擇器」、§S-101 (3) 提到的三條死宣告——
 *         那是**從沒生效過**，與「曾經有效後被推翻」是兩回事（見上面「三種東西」②）。
 *
 * ============================================================================
 * 完整性聲明（★這張表能不能信，取決於這一段）
 * ============================================================================
 *   ★★**清單不完整的索引表比沒有索引表更危險**——讀的人會把「不在表上」讀成「還有效」。
 *   所以掃描方法逐字寫在這裡，下一個人才有辦法用同一組條件重跑、並判斷我漏了什麼。
 *
 *   第一輪關鍵字：作廢｜已被…覆蓋｜不再適用｜推翻｜訂正｜superseded｜過時｜失效｜已被取代
 *   第二輪關鍵字（第一輪抓不到「原句還在、只是被標死」那種寫法）：
 *     原句保留｜原文保留｜一字未刪｜一字未動｜原文一字｜本節取代｜已作廢｜作廢標記｜不要照｜別再照它做
 *   ★第一輪 387 命中裡有 168 筆是丙 C02 那族，扣掉之後逐筆開上下文人工判讀。
 *
 *   ★已知的三個涵蓋不到的地方（不要假設本表已經涵蓋）：
 *     ⑴ **沒有留下任何標記就被改掉的條文**——本表靠關鍵字掃，掃不到沒寫字的東西。
 *     ⑵ **住在別的檔案裡的條文**：.agent/uiux.md、.agent/shadcn-migration.md、
 *        .agent/TODO.md、blade 註解。本表只在它們**被本檔的章節點名**時才收（A08~A10、A17~A19）。
 *     ⑶ **本節寫完之後新增的作廢**。⇒ ★★**新標一則作廢，就要在本表補一列**，
 *        否則本表會慢慢變成「看起來很完整的舊清單」，那正是它想解決的那個問題。
 * ---------------------------------------------------------------------------- */
/* --- S-127 end -------------------------------------------------------------- */


/* --- S-128. 側欄收合鈕：按下去後暫停 hover 展開，直到滑鼠離開（2026-08-11）--------
 *
 * ═══ 業主原話 ═══
 *   「左邊收折選單的按鈕，按下去側欄會停留，邏輯應該是縮就好，
 *     不然好像按下去沒反應的感覺」
 *
 * ═══ 真因（本機 1440x900 實測，不是推論）═══
 *   收合鈕 `.app-sidebar-minify-btn` **就長在 #sidebar 裡面**
 *   （_nav.blade.php L712-717，側欄頁腳，展開態實測 x=179..211）。
 *   點下去的瞬間：
 *     ① `#app` 確實掛上 `.app-sidebar-minified`（實測 true）
 *     ② `localStorage['seller-sidebar-minified']` 確實寫入 `'1'`（實測）
 *     ③ `#content` 的 margin-left 確實縮到 60px（實測）
 *     ④ **但滑鼠還停在側欄上** ⇒ §S-14 (2) 的
 *        `.app-sidebar-minified #sidebar.app-sidebar:hover { width: var(--fb-sidebar-w) }`
 *        立刻成立 ⇒ 側欄浮回 220px。
 *   實測數字：點擊後不移開滑鼠 → 側欄寬 **220px**（`matches(':hover')` = true）；
 *             把滑鼠移到內容區 → 側欄寬 **60px**。
 *   ⇒ 使用者看到的是「側欄一動也不動」，而 §S-14 當初修好的那個病徵
 *     （class 掛了但畫面沒動）以另一種形式復發。
 *   ★這與 §S-14 已經解過的 `:focus-within` 是**同一族**的坑：
 *     「觸發器住在它自己要收合的容器裡」。當時解掉了 focus，沒解掉 hover。
 *
 * ═══ ★★★做法：抑制 class，不是拿掉 hover 展開 ═══
 *   hover 展開是 §S-14 刻意設計的既有功能（收合態要能看子選單，而且側欄是捲動容器
 *   不能用絕對定位飛出面板）—— 業主沒說不要它，**這裡一條 §S-14 的規則都沒有改**。
 *   改成：_shell.blade.php 在「用滑鼠按下收合」的當下，給 #sidebar 掛上
 *   `.fb-sidebar-hover-off`；滑鼠真的離開側欄後把它拿掉。
 *   本節就是「掛著這顆 class 的期間，hover 不展開」的那組規則。
 *
 * ═══ 解除條件為什麼是三個（JS 端，寫在這裡是因為讀 CSS 的人會問）═══
 *   ① `mouseleave` —— 正常滑鼠動線。收合後側欄只剩 60px，指標若落在 60px 之外
 *      會立刻觸發；此時 `:hover` 已經是 false，側欄維持 60px 不會彈回去。
 *   ② `mouseenter` —— 指標從外面重新進來時收拾殘留（觸控裝置的黏著 hover）。
 *   ③ `focusin` —— 滑鼠停著不動但改用鍵盤 Tab，讓 §S-14 (6) 的
 *      `:has(:focus-visible)` 展開拿回主導權。
 *   ★★**鍵盤按 Enter 收合時根本不掛這顆 class**：JS 用 `e.detail > 0` 判斷
 *     （鍵盤觸發的 click 事件 detail 恆為 0）。理由不是省事 ——
 *     收合態的第二層選單是 `display:none`，鍵盤使用者一旦失去 `:has(:focus-visible)`
 *     展開就再也走不到全站第二層功能（§S-14 (6) 原文已經寫明這是可及性必要條件）。
 *     ⇒ 本節與 `:has(:focus-visible)` 那組**在實務上不會同時成立**，
 *       所以本節一條規則都不需要碰 `:has()`，也就沒有把「窄條」押在 :has() 支援度上。
 *
 * ═══ ★★為什麼不是只寫 width 一條（照抄會踩的坑）═══
 *   §S-14 (3) 那整組「窄條態長相」（圖示置中、文字/caret/badge 隱藏、子選單收起、
 *   收合鈕置中）掛的是 **`:not(:hover)`** —— 滑鼠停在上面時**整組失效**。
 *   若本節只把 width 壓回 60px，會得到「60px 窄條裡塞著 220px 排版的文字」⇒
 *   選單文字被裁掉一半、圖示不置中，比原病徵更醜。
 *   ⇒ 本節必須把 (3) 那組宣告在 `:hover` 時**原樣補回來**（下方 (2) 段），
 *     以及把 §S-14 (4) 在 hover 時還原的個人區**再收回去**（下方 (3) 段）。
 *   ★這些宣告的值全部與 §S-14 / §Sidebar 逐字相同，不是新的設計決定。
 *
 * ═══ 特異度：靠分數贏，不靠 `!important`、也不靠「同分排檔尾」═══
 *   要贏的目標（全部在 §S-14，`@media (min-width:992px)` 內）：
 *     L8618 `.app-sidebar-minified #sidebar.app-sidebar:hover`                       (1,3,0)
 *     L8666 `… :hover .menu-profile-link`                                            (1,4,0)
 *     L8670 `… :hover .menu-profile-info`                             display:flex!  (1,4,0)
 *     L8673 `… :hover .menu-profile .menu-caret`                      display:inline-flex! (1,5,0)
 *     L8683 `… :hover .menu-item.has-sub:hover > .menu-submenu`       display:block  (1,5,0)
 *     L8686 `… :hover .menu-item.has-sub:hover > .menu-link .menu-caret` transform   (1,6,1)
 *   本節在 §S-14 的選擇器上**多掛一顆 class**（`.fb-sidebar-hover-off`）⇒ 每條都 +(0,1,0)：
 *     `.app-sidebar-minified #sidebar.app-sidebar.fb-sidebar-hover-off:hover`        (1,4,0) > (1,3,0) ✔
 *     `…:hover .menu-text|.menu-caret|.menu-badge`                                   (1,5,0) ✔
 *     `…:hover .menu-profile .menu-caret`                                            (1,6,0) > (1,5,0) ✔
 *   ★`.menu-submenu` / `.menu-text` 這幾條帶 `!important`，是因為 §S-14 (3) 的原文
 *     就帶 `!important`（要壓 `.menu-submenu` 的 `display:block`），值照抄不是新增權重。
 *     唯一靠 `!important` 分勝負的是 `.menu-submenu`：對手 L8683 是 (1,5,0) 無 !important、
 *     本節同為 (1,5,0) ⇒ 若不加 `!important` 就變成「同分靠排在檔尾贏」，
 *     那招只要有人再往檔尾追加一條同分規則就會被**無聲翻盤**。
 *   ★`.menu-link`(1,5,0) 對 §S-14 (6) 的 `:has(:focus-visible) .menu-link`(1,4,0) 也是贏的，
 *     但如上所述兩者實務上不會同時成立，這裡只是把分數寫清楚備查。
 *
 * ═══ 邊界（三個都是刻意處理過的，不是沒想到）═══
 *   ① 展開（再按一次）：JS 無條件先 `remove(HOVER_OFF)` ⇒ 不會留下殘留狀態。
 *   ② 重新整理：這顆 class **只由 click 產生、從不寫進 localStorage** ⇒ F5 之後必定不存在
 *      （重整時滑鼠可能根本不在側欄上，掛著只會製造下一次 hover 展不開）。
 *   ③ 手機／平板：本節整段關在 `@media (min-width: 992px)` 內，與 §S-14 同一條界線。
 *      992px 以下側欄是 288px 的覆蓋式抽屜，沒有「窄條」這個型態，一條規則都不能碰到它。
 * ---------------------------------------------------------------------------- */

@media (min-width: 992px) {

  /* ── (1) 側欄本體：抑制期間 hover 不展開 ─────────────────────────────
     值與 §S-14 (2) 的窄條態相同；box-shadow 一併收掉，
     否則會出現「寬度是窄條、卻掛著『我正浮在內容上』的陰影」的矛盾外觀。 */
  .app-sidebar-minified #sidebar.app-sidebar.fb-sidebar-hover-off:hover {
    width: var(--fb-sidebar-w-mini);
    box-shadow: none;
  }

  /* ── (2) 把 §S-14 (3) 的「窄條態長相」補回來（原文掛 :not(:hover)，滑鼠停著時失效）─ */
  .app-sidebar-minified #sidebar.app-sidebar.fb-sidebar-hover-off:hover .menu-link {
    justify-content: center;
    gap: 0;
    padding-left: 0;
    padding-right: 0;
  }
  .app-sidebar-minified #sidebar.app-sidebar.fb-sidebar-hover-off:hover .menu-text,
  .app-sidebar-minified #sidebar.app-sidebar.fb-sidebar-hover-off:hover .menu-caret,
  .app-sidebar-minified #sidebar.app-sidebar.fb-sidebar-hover-off:hover .menu-badge {
    display: none !important;
  }
  /* .menu-header：本站 _nav 目前 0 命中，與 §S-14 (3) 同步保留，日後加上時不會突然爆寬 */
  .app-sidebar-minified #sidebar.app-sidebar.fb-sidebar-hover-off:hover .menu-header {
    height: 1px;
    padding: 0;
    margin: 8px 12px;
    overflow: hidden;
    text-indent: -999em;
    background: hsl(var(--sidebar-border));
  }
  /* 子選單：同時壓掉 §S-14 (5) 的「hover 到 has-sub 就展開」 */
  .app-sidebar-minified #sidebar.app-sidebar.fb-sidebar-hover-off:hover .menu-submenu {
    display: none !important;
  }
  /* 收合鈕自己：窄條態要置中（展開態靠 .ms-auto 貼右） */
  .app-sidebar-minified #sidebar.app-sidebar.fb-sidebar-hover-off:hover .app-sidebar-minify-btn {
    margin: 0 auto !important;
    justify-content: center;
  }

  /* ── (3) 個人區：§S-14 (4) 在 hover 時把「只留頭像」還原了，這裡再收回去 ──────
     值逐字取自 §Sidebar 的「側欄收合時只留頭像」三條（L6569-6574）。 */
  .app-sidebar-minified #sidebar.app-sidebar.fb-sidebar-hover-off:hover .menu-profile-link {
    grid-template-columns: 32px;
    justify-content: center;
  }
  .app-sidebar-minified #sidebar.app-sidebar.fb-sidebar-hover-off:hover .menu-profile-info,
  .app-sidebar-minified #sidebar.app-sidebar.fb-sidebar-hover-off:hover .menu-profile .menu-caret {
    display: none !important;
  }
}
/* --- S-128 end ---------------------------------------------------------------- */
