/* 色彩 token 一律在 brand.css，這裡不再定義任何顏色值。
   新增樣式請用 --ui-* （介面語意）或 --brand-*（識別裝飾），不要寫死 hex。

   本檔管的是「尺度」：字級、間距、容器寬度、圓角。這些不隨 CI 改變（CI 管的是識別，
   不是版面節奏），所以與 brand.css 分開。CI 的「標準字設計」2026-09-02 換上時
   也只換了 --font-sans 那一行，字級比例沒動。 */
:root {
  color-scheme: light;

  /* 字體。2026-09-02 起換成 CI 手冊的標準字（Paul 拍板「能上線的都上線」）：
     中文 **源樣黑體 GenYoGothic2 TW**（SIL OFL 1.1，自架切片，見 src/styles/fonts-genyo.css
     與 scripts/build-genyo-webfont.py），英文 **Arimo**（Apache 2.0，手冊原寫 Arial、
     設計端 2026-09-02 改指定的同度量開源替身，走 @fontsource-variable/arimo）。
     2026-08-13 到 09-01 之間用的是 Noto Sans TC Variable，那是標準字定案前的過渡。

     ── 為什麼自載而不是系統字（2026-08-13 的結論，換字型後仍成立）──
     「未切片的完整字重 5–9 MB，LCP 直接爆掉」對「整包載入」成立，但 unicode-range 切片
     正是解法：瀏覽器只下載頁面實際用到的切片（每片數十 KB），且 font-display: swap 讓
     LCP 先用下方系統字 fallback 繪製，字型到位後才換。源樣黑體的切片借用 fontsource
     替 Noto Sans TC 切的那 105 段（依繁中字頻），三個字重各約 2.5 MB、101 片。

     堆疊順序：**Arimo 放最前、源樣黑體第二**——Arimo 只有拉丁字形，英數字由它接走，
     CJK 落到源樣黑體；反過來排的話源樣黑體自帶的拉丁字形會先接走英數字，Arimo 永遠
     用不到。其後是原系統字 fallback：拉丁排前、CJK 墊後。
     Windows 一定要有 'Microsoft JhengHei'（微軟正黑體）。
     ⚠️ 不可用 'Microsoft YaHei'（微軟雅黑）—— 簡體字型，繁中會出現異體字。
     ⚠️ 源樣黑體沒有 600 字重：M（500）在 fonts-genyo.css 裡宣告成 500–600，
     所以 font-weight: 600 會落到 M 而不是 B，這是刻意的（B 太重，當半粗體會跳）。 */
  --font-sans: 'Arimo Variable', 'GenYoGothic2 TW', -apple-system, BlinkMacSystemFont, 'Segoe UI',
    Roboto, 'PingFang TC', 'Microsoft JhengHei', 'Hiragino Sans', system-ui, sans-serif;

  /* 標題／大數字的襯線顯示層（2026-08-13 對標研究結論，Paul 授權拍板）：
     charity: water 與 D&DEPARTMENT 的 display 都是襯線 regular 字重，
     CJK 等價語彙是明朝體。只給 h1／h2 與 OrgFigures 大數字用，
     h3 以下維持黑體 —— 顯示層是尺度的語彙，塞進小標題只會變舊報紙。 */
  --font-display: 'Noto Serif TC Variable', 'Noto Serif TC', 'Songti TC', serif;

  /* 記錄用等寬字。序號（01–05）、「規劃中」標籤、日期走這一支。
     用途是把「這是一份登記簿」講出來，同時等寬數字讓 01–05 垂直對齊，
     比例字的 1 會比 0 窄、五個序號會左右晃。CI 換標準字時這一行不用動
     （標準字管的是中文識別，不管數字表格）。 */
  --font-num: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, 'Roboto Mono', monospace;

  /* 字級：用 clamp 做流體縮放，手機到桌機之間連續變化，不需要斷點各寫一次。
     倍率約 1.2（小標到內文）到 1.33（大標），CJK 在小尺寸需要比拉丁文更大的字級才清楚，
     所以 --text-sm 保守設在 0.9rem 而不是常見的 0.875rem。 */
  --text-xs: 0.8rem;
  --text-sm: 0.9rem;
  --text-base: 1rem;
  --text-lg: clamp(1.05rem, 0.5vw + 0.95rem, 1.15rem);
  --text-xl: clamp(1.2rem, 1vw + 1rem, 1.4rem);
  --text-2xl: clamp(1.45rem, 1.6vw + 1.1rem, 1.8rem);
  --text-3xl: clamp(1.75rem, 2.6vw + 1.2rem, 2.4rem);

  /* 間距：4px 基準的等比尺度。寫版面時一律用這組，不要出現隨手的 13px、22px。 */
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-5: 1.5rem;
  --space-6: 2rem;
  --space-7: 3rem;
  --space-8: 4rem;

  /* 容器。--measure 是閱讀行寬：CJK 一行約 32-40 字最好讀，太寬眼睛回行會找錯行。 */
  --container: 72rem;
  --measure: 44rem;
  /* 版面外距。**全站只有這一個外距**，頁首、內容、頁尾都對齊它。
     閱讀行寬（--measure）不由容器負責，改由 .prose 自己限制 ——
     寬標題、窄內文，是編輯設計的常規做法。

     ── 為什麼是「外距」不是「欄寬」（2026-08-26 理事長拍板）──
     這裡原本是 `--col: 58rem`：內容欄封頂在 928px、置中。理事長看了 preview
     之後指定改成 Loftwork 的模型 —— **內容寬不封頂**，
     內容寬 ＝ 視窗寬 − 兩側外距，螢幕多寬版面就多寬。

     實測 loftwork.com/jp/ 的外距：1440 → 100px、1920 → 120px。
     clamp(1rem, 7vw, 7.5rem) 在這兩點分別是 100.8 與 120，對得上。
     ⚠️ 7.5rem 封的是**外距**、不是內容寬。內容寬仍然不封頂 ——
     那就是「不封頂」這個決定的字面意思，不要改成 max-width 把它封回去。

     ⚠️ 頁首／頁尾用的是**同一個** --gutter，不是自己另一個較小的值。
     Loftwork 本尊是三層（1920 上 logo 48／內容 120／內文 156），
     但我們照抄那一層會踩到下面 ① 記的那個症狀，而收斂成單一外距不會，
     視覺結果（卡片牆變寬）兩種做法都達成。**寬的那個好處我們要，錯開的風險不要。**

     ⚠️ 三個實測教訓，都不是看程式碼看得出來的：
     ① 曾經做成「首頁寬欄、內頁窄欄」兩種狀態。結果頁首對齊寬欄、內頁 main 是窄欄，
        字標與 h1 差 112px，頁首像浮在版面外面。**橫槓與內容錯開就會這樣**，
        改成流體之後這條依然成立，只是錯開的來源會變成「兩個不同的外距」。
     ② 改成單一變數後，英文版頁首在窄欄下**差 0.5px** 就換行
        （字標 266.5 + 導覽 268 + 語言 90 + 兩個間距 32 = 656.5，可用 656）。
        兩種欄寬一存在，就永遠有一邊是臨界值。收斂成一個值才真的解掉。
        流體之後這個臨界點只會往更寬跑（可用寬度變大），所以風險是降低不是升高。
     ③ 手機端 7vw 比原本的 16px 大（375 → 26px）。那是刻意的：
        同一條規則從手機長到桌機，不必為窄螢幕另開一個斷點。 */
  --gutter: clamp(var(--space-4), 7vw, 7.5rem);

  --radius-sm: 6px;
  --radius: 10px;
  --radius-lg: 16px;

  /* 觸控目標最小尺寸。WCAG 2.5.8 要求 24px，實務上手指要 44px 才不會誤觸，
     協會受眾年齡分布廣，取大的。 */
  --tap: 44px;
}

* {
  box-sizing: border-box;
}

/* hidden 屬性一律要真的看不見。

   ⚠️ `!important` 在這一檔是唯一一處，而且是必要的：瀏覽器預設樣式表給
   `[hidden]` 的是 `display: none`，權重最低。只要元素身上任何一條 class 規則
   自己設了 display（`.v-offer { display: grid }`、`.v-numbers { display: grid }`
   都是），那條 class 就贏，於是 `el.hidden = true` 設得下去、屬性也真的在
   DOM 裡，但東西照樣整個顯示在畫面上。

   這個坑踩過兩次（數字卡輪替、站內搜尋過濾），兩次的症狀都一樣：
   狀態全對、測試全綠、DOM 檢查也全對，只有眼睛看得出壞掉。
   所以不再逐處補 `.foo[hidden]`，改成全站一條，讓它不可能再發生。 */
[hidden] {
  display: none !important;
}

/* 內文連結。沒有這條的話會落回瀏覽器預設的 #0000EE，那個藍跟品牌青放在一起會打架，
   而且它不在任何 token 底下、CI 換色時不會跟著變。底線保留：協會的受眾年齡分布廣，
   只靠顏色區分連結對色覺障礙者不友善（WCAG 1.4.1 不能只用顏色傳達資訊）。 */
/* ⚠️ 底色改成 --paper 之後，--ui-primary #008484 對紙只有 4.23、不足 4.5（實算）。
   連結是坐在紙上的小字，所以改指 --ui-ink-teal #007a7a（對紙 4.83）。
   --ui-primary 的值沒動，它仍是按鈕底色（白字對它 4.53）。 */
a {
  color: var(--ui-ink-teal);
  text-underline-offset: 0.15em;
}

a:hover,
a:focus-visible {
  color: var(--ui-primary-strong); /* #006b6b，對紙 6.09 */
}

body {
  margin: 0;
  font-family: var(--font-sans);
  font-size: var(--text-base);
  /* 墨不是中性黑、紙不是純白：兩者都在品牌青那條軸上（見 brand.css 的說明與對比值）。
     全站沒有一滴中性灰。 */
  color: var(--ink);
  background: var(--paper);

  /* 內文 1.8：CJK 的行高要比拉丁文的 1.4–1.5 明顯高。
     標題在 1.28–1.35，兩者拉開約 1.4 倍 —— 層級是靠行高建立的，不只靠字級。 */
  line-height: 1.8;

  /* CJK 標點的字身框是全形，「，」「。」右側會留下大片空洞，整段看起來像漏字。
     halt（horizontal alternates）讓標點改用實際寬度的替代字形，把空洞收掉。
     繁中慣例是標點**字身框置中**（與日文的偏下不同），系統字型已處理，這裡不覆寫。
     拉丁字型沒有這個 feature，宣告了也不會有副作用。 */
  font-feature-settings: 'halt' 1;

  /* 斷行：中文可在任意字元換行、英文單字不可切斷。
     ⚠️ 不要用 word-break: break-all —— 它會把英文單字從中間切開
     （實測有網站踩過，長 URL 是唯一該用它的場合，而那用 overflow-wrap 就夠了）。 */
  word-break: normal;
  overflow-wrap: break-word;

  -webkit-text-size-adjust: 100%; /* 防 iOS 橫置時自動放大字級打亂版面 */
}

/* 標題節奏。用 text-wrap: balance 讓標題斷行平均，中文長標題尤其明顯。

   ── display 角色的做法（2026-08-13 改版）──
   h1／h2 走 --font-display（明朝體 regular）：對比靠字型與尺寸承載、不靠加粗，
   對標站（Kazimir 400、Amiri 400）皆同此手法。字距維持 CJK 正字距 ——
   那是匾額、公文、展覽標題的語彙，讀起來正式而從容；公部門網站的通病
   恰恰相反，是**預設字距的粗黑體擠在一起**。h3 以下維持黑體 600（內文層節奏）。
   四語共用同一個做法：日文同樣吃寬字距，英文在 0.06em 只是變從容、不會變形。 */
h1, h2, h3 {
  line-height: 1.35;
  text-wrap: balance;
  margin-block: 0 var(--space-3);
  font-weight: 600;
}

h1, h2 {
  font-family: var(--font-display);
  font-weight: 400;
}

h1 { font-size: var(--text-3xl); letter-spacing: 0.04em; line-height: 1.28; }
h2 { font-size: var(--text-2xl); margin-block-start: var(--space-6); letter-spacing: 0.06em; }
h3 { font-size: var(--text-xl); margin-block-start: var(--space-5); letter-spacing: 0.04em; }

/* 英文頁的標題改負字距。
   正字距是 CJK 的語彙（匾額、公文），套到拉丁大標上會鬆散、像沒排過。
   拉丁大字級本來就需要收緊（光學補償：字級愈大，字間視覺空隙愈明顯）。

   ⚠️ CSS 選不到「這幾個字是漢字、那幾個是拉丁」，只能依語系分。
   所以中文句子裡夾的英數字（「願景協會 2.0」的 2.0）仍吃中文的正字距。
   那是刻意的取捨：混排時以主要語言為準，比逐字包 <span> 實際得多。 */
html[lang^='en'] h1 { letter-spacing: -0.015em; }
html[lang^='en'] h2 { letter-spacing: -0.01em; }
html[lang^='en'] h3 { letter-spacing: -0.01em; }

/* 頁面標題上方的區塊色塊。顏色由 <html data-section> 決定（Base.astro 從路徑推導），
   所以理念／文章／影音／活動／會員／隱私權每一頁都自動帶自己的顏色，不必逐頁改元件。
   沒有對應區塊的頁面落在 brand.css 的 --section 預設值（青）上，不會變成看不見的空色塊。 */
main h1::before {
  content: '';
  display: block;
  width: 40px;
  height: 6px;
  border-radius: 2px;
  background: var(--section);
  margin-bottom: var(--space-4);
}

p { margin-block: 0 var(--space-4); }

/* 鍵盤焦點。瀏覽器預設的 outline 在深色按鈕上幾乎看不見，
   統一成品牌色外框加位移，鍵盤使用者才知道自己在哪。 */
:where(a, button, input, select, textarea, [tabindex]):focus-visible {
  outline: 2px solid var(--ui-primary);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

/* 跳到主要內容（Base.astro 裡 body 的第一個元素）。平常推到畫面上緣之外，拿到焦點才落回左上角。
   不能用 display:none 或 visibility:hidden 藏：那兩個會把它移出 Tab 順序，鍵盤就再也走不到它。
   青底白字：白字對 --ui-primary 是 4.52，過 AA（brand.css 的註記）。
   z-index 要高過帳號選單的 20，焦點落在它身上時不能被任何東西蓋住。
   沒有 transition，所以不用另外處理 prefers-reduced-motion。 */
.skip-link {
  position: absolute;
  top: var(--space-2);
  left: var(--space-2);
  z-index: 30;
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-sm);
  background: var(--ui-primary);
  color: var(--bg);
  font-weight: 600;
  text-decoration: none;
  transform: translateY(calc(-100% - var(--space-4)));
}
/* 字色要再寫一次：上面的 a:focus-visible 會把連結字改成 --ui-primary-strong，落在青底上就看不見了。 */
.skip-link:focus {
  transform: none;
  color: var(--bg);
}
/* main 的 tabindex="-1" 只是為了接住跳轉後的焦點，它不是可以操作的元件。
   不關掉的話，上面的 :focus-visible 外框會把整片內文框起來。 */
#main:focus {
  outline: none;
}

/* 尊重使用者的動態偏好設定（前庭疾患、暈動症） */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ══ Header ══
   三種版型，斷點都是量出來的（各自的理由記在下方各個 media query）：
     ≤33.99rem         三列：品牌／導覽／工具區
     34rem 到單列門檻  兩列：品牌＋工具區在上、導覽在下（＝這裡的基本規則）
     單列門檻起        一列：品牌／導覽／工具區
   單列門檻分語系：中文 72rem、其他語系 80rem（英日的導覽與工具區比較寬，見下方單列那段）。
   工具區 ＝ 會員入口 ＋ 語言切換器（見 .site-header__utils）。
   實測（2026-08-06、375px）：單列在手機上會把「願景協會」拆成兩行、導覽折成兩排，
   沒有水平溢出但視覺是壞的。導覽的項數在中文字寬下硬塞單列必爆。 */
.site-header {
  display: grid;
  grid-template-areas:
    'brand utils'
    'nav   nav';
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: var(--space-2) var(--space-4);
  /* 界線滿版、字標對齊內容欄。橫槓（border-bottom）滿版，padding 把內文推到內容欄左緣。

     ⚠️ 左右外距吃 --gutter，**而且手機也要吃**。這一行原本只寫在
     @media (min-width: 48rem) 裡，手機端維持 --space-4（16px），
     容器改成流體之前兩邊剛好都是 16 所以看不出來；改流體之後
     375px 上字標在 x=16、內容在 x=26，橫槓與內容錯開 10px
     —— 就是 --gutter 註解 ① 記的那個症狀，只是搬到手機端重演一次。
     （2026-08-26 實測抓到。） */
  padding: var(--space-3) var(--gutter);
  border-bottom: 1px solid var(--border);
}

.site-header__brand {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  grid-area: brand;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-weight: 600;
  /* 手機用 --text-base：英文字標「Vision Association」有 18 個字，
     配上補到 --tap 的語言切換器，在 390px 會把頁首推到 399px（實測水平溢出）。
     桌機在下面的 media query 放大回 --text-xl。 */
  font-size: var(--text-base);
  /* 字標維持青（那是 logo 上兩行字的顏色），但用對紙底達標的 --ui-ink-teal（4.83）
     而不是 --ui-primary（對紙只有 4.23）。視覺上仍是同一個青。 */
  color: var(--ui-ink-teal);
  letter-spacing: 0.08em;
  text-decoration: none;
  white-space: nowrap; /* 協會名不可斷行，斷了就不像標誌 */
  min-height: var(--tap); /* 量出來只有 29.2px，補到 44 */
}

/* 標語「分享資源，創造未來」。只在首頁渲染（Base.astro，Paul 2026-08-28 指定），
   住在品牌連結裡、排在協會名右邊，用細分隔線隔開。

   預設藏起來、夠寬才顯示，每個語系各一條門檻。頁首的斷點全是量出來的
   （尺見下方單列那段），標語擠進 brand 會把單列頁首的所需寬度往上推，四語推的量不同。
   標語本徵寬（--text-sm、字距 0.06em，含分隔線與間距）：中文約 162px、日文約 254px、
   英文約 299px。

   2026-09-28 首頁逐 4px 實測（1100 到 1720），標語顯示時導覽第一個不折行的視窗寬，
   「登入前／登入後」：
                浮動捲軸      傳統捲軸（約 17px）
     zh-Hant    1248／1284    1268／1304
     zh-Hans    1264／1304    1284／1324
     ja         1496／1520    1516／1540
     en         1516／1576    1536／1596
   登入後比較寬，理由見 button.site-header__member 那段。
   門檻取最壞的一格（登入後＋傳統捲軸）再留 36 到 44px：
     中文 85rem（1360）、日文 99rem（1584）、英文 102rem（1632）。

   ⚠️ 舊門檻 76／88／94rem 是 2026-08-28 用本徵寬換算訂的（所需視窗寬
   zh-Hant 1146、zh-Hans 1164、ja 1379、en 1453），比上表未登入、浮動捲軸的實測
   低 63 到 117px，所以中文 1216 起、日文 1408 起、英文 1504 起，標語一顯示導覽就折行。
   換算為什麼差這麼多沒有查到，以實測表為準。
   症狀跟 2026-08-28 那次一樣：標語自己看起來完全正常，壞掉的是它旁邊的導覽，
   所以從標語身上看不出成因。

   門檻以下寧缺勿壞，頁尾本來就有同一句；兩列／三列頁首全藏。
   每條顯示規則都掛語系前綴，沒列到的語系（含日後新增）一律不顯示。
   每個語系的標語門檻不得低於該語系的單列門檻（兩列頁首沒有位置放標語），
   由 tests/header-breakpoints.test.mjs 檢查。 */
.site-header__tagline {
  display: none;
  font-size: var(--text-sm);
  font-weight: 400;
  letter-spacing: 0.06em;
  color: var(--ink-muted); /* 對紙 7.09；跟協會名同色會讀成名字的一部分 */
  border-left: 1px solid var(--border);
  padding-left: var(--space-3);
  margin-left: var(--space-1);
}
@media (min-width: 85rem) {
  html[lang^='zh'] .site-header__tagline {
    display: inline-block;
  }
}
@media (min-width: 99rem) {
  html[lang^='ja'] .site-header__tagline {
    display: inline-block;
  }
}
@media (min-width: 102rem) {
  html[lang^='en'] .site-header__tagline {
    display: inline-block;
  }
}

.site-header__nav {
  grid-area: nav;
  display: flex;
  /* ⚠️ nowrap 是刻意的，別「順手」改回 wrap。
     手機上導覽單獨一列、可左右滑 —— 那是這個 header 原本就登記的做法
     （CLAUDE.md 版面尺度那節）。原本寫的是 wrap，只是五項舊導覽（理念／文章／
     影音／活動／會員中心，共 12 字）從來沒長到需要折行，所以沒人發現兩者矛盾。
     2026-08-09 換成五項四字的新導覽（共 20 字）之後，繁中折兩排、日文也折兩排，
     一口氣吃掉 48px —— 而首頁的硬要求是「身分聲明＋入口磚不超過一個螢幕」。
     桌機在下面的 media query 改回 wrap 當安全網（那裡不左右滑）。 */
  flex-wrap: nowrap;
  gap: var(--space-1) var(--space-4);
  overflow-x: auto;
  scrollbar-width: none;
  /* ⚠️ grid 子項的自動最小尺寸是 min-content，而導覽項目都是 white-space: nowrap，
     所以這一格會被撐到 342px、把整頁推寬到 358（320px 英文版實測）。
     overflow-x: auto 本來就該讓它變 0，但要補這一行才可靠。
     沒有這行，症狀是「只有英文版、只有極窄機會左右晃」——很難從版面猜到原因。 */
  min-width: 0;
}
.site-header__nav::-webkit-scrollbar { display: none; }

.site-header__nav a {
  margin-left: 0;
  /* --fg（中性 #1a1a1a）是全站唯一一處會跟青墨打架的近黑，改指 --ink */
  color: var(--ink);
  text-decoration: none;
  font-size: var(--text-sm);
  letter-spacing: 0.06em;
  white-space: nowrap;
  /* 觸控目標：文字連結本身高度不足，用 padding 撐到可靠的點擊面積 */
  padding-block: var(--space-2);
  min-height: var(--tap);
  display: inline-flex;
  align-items: center;
  position: relative;
}

.site-header__nav a:hover {
  color: var(--ui-ink-teal);
}

/* 目前所在的區塊：用該區塊自己的顏色畫一條底線。
   顏色不是靠 aria-current 的樣式硬寫，而是讀該連結上的 data-section，
   所以「文章」的底線是桃色、「活動」是紫色，跟頁內標題色塊同一套。
   底線之外還有 aria-current 本身（讀屏軟體讀得到），不是只靠顏色傳達。 */
.site-header__nav a[aria-current]::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: 6px;
  height: 2px;
  border-radius: 1px;
  background: var(--section);
}

/* ── 頁首右上角的工具區：會員入口 ＋ 語言切換器 ── */
.site-header__utils {
  grid-area: utils;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  /* grid 子項的自動最小尺寸是 min-content，裡面全是 nowrap 的東西。
     不寫這行，極窄機會被它撐出水平溢出（同 .site-header__nav 那條的成因）。 */
  min-width: 0;
}

/* 會員入口。做成外框式的小控制項，不是純文字連結：
   右上角本來就有四個長相一致的語言連結，再多一個同樣的文字會被讀成第五個語言。
   外框讓它讀成「一個可以按的東西」。
   ⚠️ 不填滿底色。填滿之後它會是整個頁首最重的一塊，而頁首的主角是協會名與導覽。
   ⚠️ 文字色用 --ui-ink-teal 不是 --ui-primary：後者是對「白底」調的，
   坐在紙底上只有 4.23、不足內文的 4.5（見 brand.css 的警告）。 */
.site-header__member {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  white-space: nowrap;
  font-size: var(--text-sm);
  letter-spacing: 0.06em;
  text-decoration: none;
  color: var(--ui-ink-teal);
  border: 1px solid var(--ui-ink-teal);
  border-radius: var(--radius-sm);
  padding: var(--space-1) var(--space-3);
  min-height: var(--tap);
}

.site-header__member:hover {
  background: var(--ui-primary-strong);
  border-color: var(--ui-primary-strong);
  color: var(--ui-on-fill);
}

/* 已登入時 HeaderMember 把同一顆會員入口從 <a> 換成 <button>，
   class 與盒模型完全不變，只是改成觸發選單、不直接導頁。
   ⚠️ 但裡面的字不一樣：登入→會員中心（Log in→Member area、ログイン→会員ページ），
   按鈕因此寬 19 到 51px（2026-09-28 實測）。寬度不隨使用者姓名變，姓名只出現在下拉選單。
   所以**頁首的斷點要用登入後的寬度量**，只量未登入會把門檻訂得太低。
   <button> 有一份 <a> 沒有的 UA 預設樣式（字型不繼承、有底色、有 UA 邊框），
   這裡重置掉，讓它視覺上與未登入的連結版本無法分辨。 */
button.site-header__member {
  font: inherit;
  letter-spacing: inherit;
  background: none;
  appearance: none;
  cursor: pointer;
}

/* 選單的定位錨點。寬度給 0、不參與 flex 分配，
   讓 .site-header__utils 的寬度計算跟未登入時的 <a> 完全一樣。 */
.site-header__member-wrap {
  position: relative;
  display: flex;
  min-width: 0;
}

/* 帳號選單：登入後按下會員入口彈出的浮層，顯示身分／登入管道／登出。
   絕對定位、脫離 .site-header__utils 的寬度流，所以不會影響頁首斷點。
   ⚠️ 全站不用 box-shadow（同 .auth-button 那條規則），用邊框＋紙色做出層次。 */
.site-header__member-menu {
  position: absolute;
  top: calc(100% + var(--space-2));
  right: 0;
  z-index: 20;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  min-width: 11rem;
  max-width: 16rem;
  padding: var(--space-3);
  background: var(--paper);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}

.site-header__member-menu-name {
  margin: 0;
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.site-header__member-menu-provider {
  margin: 0 0 var(--space-1);
  font-size: var(--text-sm);
  color: var(--ink-muted);
}

.site-header__member-menu-link,
.site-header__member-menu-logout {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap);
  padding: var(--space-1) var(--space-2);
  font: inherit;
  font-size: var(--text-sm);
  text-align: left;
  text-decoration: none;
  color: var(--ink);
  background: none;
  border: none;
  border-radius: var(--radius-sm);
  cursor: pointer;
}

.site-header__member-menu-link:hover,
.site-header__member-menu-logout:hover {
  background: var(--ui-primary-strong);
  color: var(--ui-on-fill);
}

/* ⚠️ 單列頁首的斷點分語系：中文 72rem、其他語系 80rem，不是版面其他地方用的 48rem。
   **兩個數字都是量出來的，不要為了「跟其他斷點一致」改成同一個值。**

   斷點要對齊「這個語系裝不裝得下」。量法（2026-08-26 訂，之後沿用同一把尺）：
   在 1920 的無折行狀態下取三塊的**本徵寬**：品牌、導覽（各項寬度相加＋項間距，
   不是 scrollWidth：導覽是 1fr 軌，scrollWidth 量到的是被撐開後的軌寬）、工具區，
   加上兩道 header 間距（各 16px），再除以 0.86 換算成所需視窗寬
   （容器是流體的，兩側各 7vw）。傳統捲軸（Windows，或 Mac 設成一律顯示捲軸）
   佔掉約 17px 版面寬，media query 卻照看整個視窗寬，所以分子要再加 17。

   2026-09-28 首頁與聯絡頁逐 4px 實測（1100 到 1720），導覽第一個不折行的視窗寬，
   「登入前／登入後」：
            浮動捲軸      傳統捲軸
     en     1172／1228    1188／1248
     ja     1204／1224    1224／1244
     中文   1152 起全程不折行（兩種捲軸、登入前後皆同）
   換算與實測差在 4px 內。登入後比較寬，理由見 button.site-header__member 那段。
   ⚠️ 2026-08-28 記在這裡的換算值（en 1094、ja 1092、zh-Hans 983、zh-Hant 966）
   與實測對不上，原因沒有查到，以上面的實測表為準。

   原本全語系共用 72rem（1152），在英日不夠：1152 到 1244 之間導覽會折成兩行。
   中文維持 72rem：最緊的是簡中登入後＋傳統捲軸，換算約需 1133，只留約 19px；
   實測 1152 不折行，但中文導覽再加一項或加字，會最先在這裡壞。
   其他語系抬到 80rem（1280）：最壞的一格英文留 32px、日文留 36px
   （浮動捲軸時 52px、56px）。
   ⚠️ 不要壓到實測剛好成立的寬度：字體在不同平台的量測會差幾 px，日文假名靠平台的
   後援字型、差得可能更多，臨界值會變成「某些機器上就是壞的」。

   兩段宣告必須完全相同，只差中文那段每個選擇器前面的 html[lang^='zh']，
   由 tests/header-breakpoints.test.mjs 比對。
   ⚠️ 80rem 那段與 .site-header 基本規則同權重 (0,1,0)，必須寫在基本規則之後
   （同下方 33.99rem 那段的警告）。沒掛中文前綴的語系（含日後新增）一律走 80rem：
   寧可多一段兩列，不要折行。

   門檻以下回到兩列頁首（品牌＋工具區在上、導覽在下），導覽自己可以左右滑。
   單列硬塞的話導覽會折成兩行：不會溢出、也不會壞
   （.site-header__nav 的 flex-wrap 是刻意留的安全網），但那比兩列難看。 */
@media (min-width: 72rem) {
  html[lang^='zh'] .site-header {
    grid-template-areas: 'brand nav utils';
    grid-template-columns: auto 1fr auto;
  }
  html[lang^='zh'] .site-header__nav {
    justify-content: center;
    overflow-x: visible;
    flex-wrap: wrap;
  }
  html[lang^='zh'] .site-header__brand { font-size: var(--text-xl); }
}
@media (min-width: 80rem) {
  .site-header {
    grid-template-areas: 'brand nav utils';
    grid-template-columns: auto 1fr auto;
    /* 外距不寫在這裡：基礎規則已經吃 --gutter，手機到桌機同一個值。
       第一輪截圖踩過的是「字標 x=30、內容欄 x=280」那種錯開，
       容器改成流體之後 main 與這條橫槓吃同一個變數，左緣天生對齊，
       不必再用 calc 去追一個置中欄的邊，也不需要為桌機另寫一份。 */
  }
  .site-header__nav {
    justify-content: center;
    overflow-x: visible;
    /* 桌機不左右滑，所以折行是這裡的安全網（極端字級設定下才會用到）。 */
    flex-wrap: wrap;
  }
  .site-header__brand { font-size: var(--text-xl); }
}

/* 手機（≤543px）：頁首拆成三列，工具區自己一列。

   原本只有極窄機（≤367px）需要這一段：英文字標「Vision Association」不可斷行
   ＋ 補到 --tap 的語言切換器，在 320px 下必然水平溢出（358 > 320）。
   2026-08-27 工具區多了會員入口之後，溢出的區間一路擴大到一般手機。
   在真實視窗（iframe，不是 off-screen 容器 —— 後者量不到 media query 與 vw）
   逐寬掃出來的兩列頁首水平溢出量：
     en  375:71　390:58　414:35　430:20　480 起 0
     ja  375:27　390:13　414 起 0
     zh-Hant / zh-Hans 全程 0
   英文要到 480 才收得住，所以上界訂在 33.99rem（543.98），留約 64px 餘裕。
   ⚠️ 同樣不要壓到剛好 480：字體在不同平台會差幾 px。

   代價是手機頁首從 121px 變 173px。這是加一顆會員入口的實際成本，
   不是可以靠縮字級省掉的東西 —— 三個要素（字標、會員入口、四語切換）
   各自都已經是最小可用尺寸，而 --tap 44px 是無障礙下限、不能為了版面讓步。
   ⚠️ 別改成「手機只留標誌圖形、把協會名藏起來」來擠出空間：
   本會沒有靠圖形就被認出來的知名度，藏掉名字等於手機上沒有識別。

   ⚠️ 這一段必須放在 .site-header 基本規則**之後**。同權重 (0,1,0) 時後寫的贏，
   寫在前面會被基本規則的 grid-template-columns 蓋掉而靜默失效
   （踩過一次：溢出數字完全沒變，看起來像 media query 沒生效，其實是被覆蓋）。 */
@media (max-width: 33.99rem) {
  .site-header {
    grid-template-areas: 'brand' 'nav' 'utils';
    grid-template-columns: minmax(0, 1fr);
  }

  /* .site-header__member-menu 預設 right: 0，是為了對齊 34rem 以上兩列／單列
     頁首裡 utils 貼右邊界的版型。手機版 utils 自己是一整列、內容靠左排
     （member 入口在語言切換器前面），right: 0 會把選單錨在 x≈116px 那個
     視覺上不存在的「右邊界」，選單往左長 11rem 直接切出螢幕
     （2026-08-29 實測：left 量到 -59.75px）。改成 left: 0 貼會員入口本身
     的左緣，同一列右邊還有語言切換器留的空間，選單長多寬都在視窗內。 */
  .site-header__member-menu {
    right: auto;
    left: 0;
  }
}

/* 手機的導覽間距收到 space-3。
   量出來的（2026-08-09，五項新導覽上線後）：390px 下五項的 scrollWidth 是 369，
   可用寬度 358 —— 差 11px，最後一項會從字的中間被切掉，看起來像壞掉而不是
   「可以往右滑」。間距 16→12 省下 16px 之後，繁中與日文在 390px 完整顯示。

   ⚠️ **加了第六項之後，390px 已經沒有任何一語塞得下了**（2026-08-28 實測：
   navScrollW 繁中 426／簡中 442／日文 456／英文 390，可用寬度 335）。
   那不是回歸，是這個 header 原本就登記的行為 —— 導覽在窄機可左右滑，
   四語在該寬度的水平溢出量全部是 0。收窄間距仍然值得留著：它讓「要滑多少」
   少一截，而且 480 起簡中與繁中就不必滑了。
   ⚠️ 別為了讓它「剛好塞下」去縮短譯文或縮字級：導覽是這一頁最常被點的東西。

   ⚠️ 上界停在 47.99rem，**不跟著兩列頁首的上界一起往上搬**。
   兩列頁首延伸到單列門檻（2026-08-26 起 60rem，2026-08-27 加會員入口後 66rem，
   2026-08-28 加第六項導覽後 72rem，2026-09-28 起英日等其他語系 80rem），
   48rem 到單列門檻這一段因此是「兩列版型 ＋ 寬間距」。
   看起來像漏掉，實際上是對的：768px 四語實測 navScrollW 都是 660、等於可用寬度，
   單行不折、不需要滑。這一段的空間本來就夠，收窄間距只會讓導覽變擠。
   收窄是給 390px 那種真的滑不完的情境用的。 */
@media (max-width: 47.99rem) {
  .site-header__nav {
    gap: var(--space-1) var(--space-3);
  }
}

/* ⚠️ 沒有 max-width 是刻意的，不是漏寫（2026-08-26 理事長拍板，理由見 --gutter）。
   內容寬 ＝ 視窗寬 − 兩側 --gutter，螢幕多寬版面就多寬。
   要把某一塊收回可讀行寬，用 --measure 限制那一塊自己（.prose 就是這樣做的），
   不要在這裡加 max-width 把整站封回去。 */
main {
  padding: var(--space-7) var(--gutter);
  min-height: 60vh;
}

.site-footer {
  color: var(--ink-muted);
  font-size: var(--text-sm);
  padding: var(--space-6) var(--space-4);
  border-top: 1px solid var(--border);
  margin-top: var(--space-8);
  letter-spacing: 0.02em;
}

.site-footer p {
  margin-block: 0;
}

/* 與頁首同一個做法：界線滿版、文字對齊內容欄的左緣。
   桌機的 media query 拿掉了：--gutter 本身就是流體的，手機到桌機同一條規則。 */
.site-footer {
  padding-inline: var(--gutter);
}

/* 所有可點元素共用的最小觸控高度。協會受眾年齡分布廣，按鈕太小會誤觸。 */
.auth-button,
.card__action,
.member-panel__form button,
.admin__form button,
.admin__roster > button,
.chat-widget__input-row button {
  min-height: var(--tap);
  cursor: pointer;
  transition: background-color 0.15s ease, border-color 0.15s ease;
}

.auth-button:hover,
.card__action:not(:disabled):hover,
.member-panel__form button:hover,
.admin__form button:hover,
.admin__roster > button:hover {
  background: var(--ui-primary-strong);
  border-color: var(--ui-primary-strong);
}

.auth-button {
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-sm);
  border: 1px solid var(--accent);
  background: var(--accent);
  color: var(--ui-on-fill);
  cursor: pointer;
}

.auth-button--loading {
  border: none;
  color: var(--muted);
}

/* 登入後的那顆（名字＋登出）。它不是行動呼籲，只是「你是誰」的狀態列，
   實心主色會讓它變成整頁最搶眼的元素、看起來像要人按的 CTA（2026-08-28 會員中心診斷）。
   外框低調式；hover 也不走共用的主色實底，登出不該長得像主要動作。 */
.auth-button--session {
  background: transparent;
  border-color: var(--border);
  color: var(--ink-muted);
}
.auth-button--session:hover {
  background: transparent;
  border-color: var(--ink-muted);
  color: var(--ink);
}

/* 登入管道有三家（LINE／Google／Facebook），窄螢幕上一顆一行、寬螢幕上排成一列。
   刻意不套各家的官方品牌色：三顆不同顏色的按鈕並排會變成頁面上最吵的東西，
   而它們只是登入方式、不是這一頁的主角。文字已經寫明是哪一家。 */
.auth-providers {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

/* 第二、三顆用外框式。文字色用 --ui-ink-teal 不是 --ui-primary：
   後者是對「白底」調的，坐在紙底上只有 4.23、不足內文的 4.5（見 brand.css 的警告）。 */
.auth-providers .auth-button:not(:first-child) {
  background: transparent;
  color: var(--ui-ink-teal);
}

.auth-providers .auth-button:not(:first-child):hover {
  background: var(--ui-primary-strong);
  color: var(--ui-on-fill);
}

.chat-widget {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--space-4);
  margin-top: var(--space-6);
}

.chat-widget__log {
  min-height: 4rem;
  max-height: 16rem;
  overflow-y: auto;
}

.chat-widget__turn {
  margin: var(--space-2) 0;
}

.chat-widget__turn--user {
  font-weight: 600;
}

.chat-widget__input-row {
  display: flex;
  gap: var(--space-2);
  margin-top: var(--space-3);
}

.chat-widget__input-row input {
  flex: 1;
  padding: var(--space-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}

/* ── 語言切換器 ── */
.lang-switcher {
  display: flex;
  gap: var(--space-2);
  font-size: var(--text-sm);
}
/* 語言切換：量出來每一顆只有 24.5px 高。受眾含長者，補到 --tap。
   高度給滿 44，寬度用 padding 撐到 ~32：四顆各給 44 寬會讓英文版頁首
   在 390px 下擠爆字標（字標 nowrap 不會縮）。高度是拇指命中率的主要維度，
   先把它補滿，寬度取「不犧牲版面」的最大值。 */
.lang-switcher__item {
  color: var(--ink-muted);
  text-decoration: none;
  letter-spacing: 0.04em;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--tap);
  padding-inline: var(--space-1);
  min-width: 24px; /* WCAG 2.5.8 的最小邊長。單一個「繁」字只有 23px，差 1px */
}

@media (min-width: 48rem) {
  .lang-switcher__item { padding-inline: var(--space-2); }
}
/* --accent（= --ui-primary）對紙底 4.23 不足，換成對紙達標的 4.83 */
.lang-switcher__item.is-active {
  color: var(--ui-ink-teal);
  font-weight: 600;
}

/* ── 站內搜尋（活動與合作方案）── */
/* 標籤留在畫面上，不只靠 placeholder。placeholder 一開始打字就消失，
   而協會受眾年齡分布廣：看不到「這格是做什麼的」時，打到一半忘了的人回不去。
   （WCAG 3.3.2 講的也是這件事。） */
.search-box {
  margin: 0 0 var(--space-6);
}
.search-box__label {
  display: block;
  margin-bottom: var(--space-2);
  color: var(--ink-muted);
  font-size: var(--text-sm);
}
.search-box__row {
  display: flex;
  align-items: stretch;
  gap: var(--space-2);
  max-width: var(--measure);
}
.search-box__input {
  flex: 1 1 auto;
  min-width: 0; /* flex 子項預設 min-width:auto，窄螢幕上輸入框會撐破容器 */
  min-height: var(--tap);
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg);
  color: var(--ink);
  font: inherit;
}
/* WebKit 會自己在 type=search 右側畫一顆 ✕。留著就有兩個清除鈕，
   而它只有十來像素、遠低於 --tap，長者與觸控使用者按不到——
   等於畫面上比較顯眼的那顆是比較難按的那顆。 */
.search-box__input::-webkit-search-cancel-button {
  display: none;
}
.search-box__clear {
  flex: 0 0 auto;
  min-height: var(--tap);
  padding: var(--space-2) var(--space-4);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg);
  color: var(--ink-muted);
  font: inherit;
  cursor: pointer;
}
.search-box__clear:hover {
  border-color: var(--ui-primary);
  color: var(--ui-primary);
}

/* 搜尋零命中時的訊息。用 --ink-muted 而不是 --ui-warning：
   搜不到東西是正常結果、不是待辦事項，不需要被強調成一件要處理的事。 */
.search-empty {
  max-width: var(--measure);
  margin: 0 0 var(--space-5);
  color: var(--ink-muted);
}

/* ── 活動 ── */
/* ⚠️ 活動卡片本體的樣式**不在這裡**，它與文章／影音走同一組 .card
   （見下方「內容」那一節）。曾經有一份 .event-card 各寫一遍色脊、圓角與內距，
   數值抄得一樣但會各自漂移；卡片上要加一個版位就得改兩處，而漏掉的永遠是這一處
   —— React 檔不在 .astro 的 grep 射程內。這裡只留活動**獨有**的東西：報名鈕與已報名徽章。 */
.card__action {
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-sm);
  border: 1px solid var(--accent);
  background: var(--accent);
  color: var(--ui-on-fill);
  cursor: pointer;
}
.card__action:disabled {
  opacity: 0.6;
  cursor: default;
}
.card__badge {
  margin-left: var(--space-3);
  color: var(--section-ink); /* 各區塊自己的介面墨，全部 ≥4.5 對紙 */
  font-size: var(--text-sm);
}
.event-list__error {
  color: var(--ui-danger);
}

/* ── 會員中心 ── */
/* 整個面板收回閱讀行寬。這一頁的內容天生窄（表單 input 320px、卡片、清單），
   攤滿桌機的 1100px 只會得到「內容擠左、右邊一大片空白」的斷裂感（2026-08-28 實測診斷）。 */
.member-panel,
.member-line-gate {
  max-width: var(--measure);
}
.member-panel section {
  margin-bottom: var(--space-6);
}
/* 會員卡之外的區塊沒有自己的框，只靠 h2 相隔會全部糊成一片。
   用分隔線給每一段一個明確的起點；字級降一級——四個 h2 全用頁級的
   --text-2xl 時，區段標題跟頁面標題搶同一個層級。 */
.member-panel > section + section {
  border-top: 1px solid var(--border);
  padding-top: var(--space-5);
}
.member-panel section h2 {
  margin-top: 0;
  font-size: var(--text-xl);
}
.member-panel__form label {
  display: block;
  margin-bottom: var(--space-3);
}
.member-panel__form input {
  display: block;
  width: 100%;
  max-width: 320px;
  padding: var(--space-2) var(--space-3);
  margin-top: var(--space-1);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}
.member-panel__form button {
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-sm);
  border: 1px solid var(--accent);
  background: var(--accent);
  color: var(--ui-on-fill);
  cursor: pointer;
}
.member-panel__readonly {
  color: var(--muted);
  font-size: var(--text-sm);
}
.member-panel__error {
  color: var(--ui-danger);
}
/* 欄位下方的說明與錯誤都是 block，否則會黏在 input 右邊、在窄螢幕上擠成一團。
   錯誤只靠紅色不夠（WCAG 1.4.1），所以訊息本身就是文字，不是只把框變紅。 */
.member-panel__form .member-panel__hint,
.member-panel__form .member-panel__error {
  display: block;
  margin-top: var(--space-1);
  font-size: var(--text-sm);
}
.member-panel__hint {
  color: var(--muted);
}
.member-panel__form input[aria-invalid='true'] {
  border-color: var(--ui-danger);
}
.member-panel__events {
  padding-left: 1.2rem;
}

/* 回報轉帳：備註是 textarea，沿用 input 的框線與寬度；紀錄清單與撤回鈕。 */
.member-panel__form textarea {
  display: block;
  width: 100%;
  max-width: 320px;
  padding: var(--space-2) var(--space-3);
  margin-top: var(--space-1);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font: inherit;
}
.member-fee__bank {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: var(--space-1) var(--space-3);
  margin: 0 0 var(--space-4);
}
.member-fee__bank dt {
  color: var(--muted);
}
.member-fee__bank dd {
  margin: 0;
}
/* 帳號是要被照抄或複製的：等寬數字，一點就全選。 */
.member-fee__account {
  display: block;
  font-size: var(--text-lg);
  font-variant-numeric: tabular-nums;
  user-select: all;
}
.member-fee__subtitle {
  font-size: var(--text-lg);
  margin-bottom: var(--space-2);
}
.member-fee__list {
  padding-left: 1.2rem;
}
.member-fee__list li {
  margin-bottom: var(--space-2);
}
.member-fee__reason {
  display: block;
  color: var(--muted);
  font-size: var(--text-sm);
}
.member-fee__withdraw {
  min-height: var(--tap);
  padding: var(--space-1) var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: transparent;
  color: inherit;
  cursor: pointer;
}

/* 會員卡：編號與條碼。編號要能在店裡念出來，所以放大、加字距。
   品牌 tint 底＋主色頂線，讓它在四個區塊裡讀得出「這一塊是卡」；
   h2「會員編號」降成眉標——標籤不該比編號本身搶眼，編號才是主角。 */
.member-card {
  border: 1px solid var(--brand-teal-tint);
  border-top: 3px solid var(--ui-primary);
  border-radius: var(--radius);
  padding: var(--space-5);
  background: var(--ui-primary-tint);
}
/* 選擇器多寫一層 .member-panel 不是贅字：上面的 .member-panel section h2
   是 (0,1,2)，單寫 .member-card h2 (0,1,1) 會輸給它，眉標樣式整組失效
   （2026-08-28 實測踩過，量出來還是 --text-xl）。 */
.member-panel .member-card h2 {
  margin: 0;
  font-size: var(--text-sm);
  font-weight: 600;
  letter-spacing: 0.12em;
  color: var(--ui-ink-teal);
}
.member-card__no {
  font-size: var(--text-2xl);
  font-weight: 700;
  /* 等寬字：編號裡有 2/Z、5/S 這類形近字，比例字型下更容易念錯。
     字元集已經拿掉了 0/O 與 1/I/L（見 worker/src/consent.js），
     剩下的靠字型再擋一層。 */
  font-family: ui-monospace, 'SFMono-Regular', Menlo, monospace;
  letter-spacing: 0.08em;
  margin: var(--space-1) 0 var(--space-2);
}
/* 付費身分：付費有效時加粗，讓店家一眼分得出來。不靠顏色區分（WCAG 1.4.1），
   文字本身就寫了「付費」「免費」。 */
.member-card__tier {
  font-size: var(--text-sm);
  margin: var(--space-1) 0;
}
.member-card__tier[data-paid='true'] {
  font-weight: 700;
}
.member-card__meta,
.member-card__hint {
  /* tint 底比紙底亮度略低，--muted 在上面會掉到 AA 邊緣，用更深的 --ink-muted */
  color: var(--ink-muted);
  font-size: var(--text-sm);
}
/* 卡內的展開／收合鈕：不吃瀏覽器預設的灰色小鈕（在 tint 底上像個異物）。
   次要外框式——它只是開關，不是這張卡要人做的事。 */
.member-card__toggle {
  min-height: var(--tap);
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-sm);
  border: 1px solid var(--ui-ink-teal);
  background: transparent;
  color: var(--ui-ink-teal);
  cursor: pointer;
  transition: background-color 0.15s ease, color 0.15s ease;
}
.member-card__toggle:hover {
  background: var(--ui-primary-strong);
  border-color: var(--ui-primary-strong);
  color: var(--ui-on-fill);
}
.member-card__code {
  margin-top: var(--space-3);
}
/* 條碼尺寸：手機上要夠大才掃得到，但不該撐滿整個視窗。
   固定寬度而非百分比——QR 的模組是整數格，被縮到非整數倍時
   邊緣會被反鋸齒糊掉，那正是掃不到的典型原因。 */
.member-code {
  display: block;
  width: 232px;   /* 29 格 × 8 px */
  height: auto;
  max-width: 100%;
  background: var(--qr-paper); /* 這片背景就是 QR 的紙底，跟 MemberCode.tsx 裡那塊 <rect> 同一個 token */
  padding: 0;
}

.member-subs {
  list-style: none;
  padding: 0;
  margin: 0 0 var(--space-2);
}
.member-subs li {
  /* 勾選框的觸控目標。協會受眾年齡分布廣，取 --tap 的下限而不是瀏覽器預設。 */
  min-height: var(--tap);
  display: flex;
  align-items: center;
}
.member-panel__note {
  color: var(--muted);
  font-size: var(--text-sm);
}
/* 綁定回跳的成功訊息。用綠不用青：它回答的是「剛才那個動作成不成功」，
   跟這一頁的品牌色系是兩件事。 */
.member-panel__bind-success {
  color: var(--ui-success);
}

/* ── 未入會的引導畫面（綁定 LINE／加好友） ── */
/* 跟會員卡同一套卡片語言：這兩個畫面各自就是「下一步只有一件事」的卡。 */
.member-line-gate {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--space-5);
  background: var(--surface);
}
.member-line-gate h2 {
  margin-top: 0;
  font-size: var(--text-xl);
}
/* 主要動作（開始綁定、加入好友連結）：實心主色，一張卡只有一顆。
   __add 是 <a> 做成鈕形——底線規則管的是行文連結（只靠顏色辨識才不友善），
   鈕形有底色與形狀，不靠底線。 */
.member-line-gate__primary,
.member-line-gate__add {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap);
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-sm);
  border: 1px solid var(--accent);
  background: var(--accent);
  color: var(--ui-on-fill);
  cursor: pointer;
  text-decoration: none;
  transition: background-color 0.15s ease, border-color 0.15s ease;
}
.member-line-gate__primary:hover,
.member-line-gate__add:hover {
  background: var(--ui-primary-strong);
  border-color: var(--ui-primary-strong);
  color: var(--ui-on-fill);
}
/* 次要動作（我加好了，重新確認）：外框式，跟主要動作分出主從。 */
.member-line-gate__secondary {
  min-height: var(--tap);
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-sm);
  border: 1px solid var(--ui-ink-teal);
  background: transparent;
  color: var(--ui-ink-teal);
  cursor: pointer;
  transition: background-color 0.15s ease, color 0.15s ease;
}
.member-line-gate__secondary:hover {
  background: var(--ui-primary-strong);
  border-color: var(--ui-primary-strong);
  color: var(--ui-on-fill);
}

/* ── 入會邀請 ── */
.join-prompt {
  /* 頁內版收回閱讀行寬（橫幅版在下面覆寫回主容器寬）。 */
  max-width: var(--measure);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--space-4);
  background: var(--surface);
}
/* 橫幅版：跟在頁首後面、內容之前，寬度對齊主容器。 */
.join-prompt--banner {
  max-width: var(--container);
  margin: var(--space-4) auto 0;
}
.join-prompt h2 {
  margin-top: 0;
}
.join-prompt__note {
  color: var(--muted);
  font-size: var(--text-sm);
}
.join-prompt__optin {
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: var(--space-3);
  margin: var(--space-3) 0;
}
.join-prompt__optin label {
  display: flex;
  align-items: center;
  min-height: var(--tap);
}
.join-prompt__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  align-items: center;
}
.join-prompt__actions button {
  min-height: var(--tap);
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-sm);
  border: 1px solid var(--accent);
  background: var(--accent);
  color: var(--ui-on-fill);
  cursor: pointer;
}
/* 「稍後再說」是次要動作，做成低調的邊框鈕。
   兩顆都是實心的話，人會分不出哪一顆才是這張表單要他做的事。 */
.join-prompt__later {
  background: transparent !important;
  color: var(--ui-primary) !important;
  border-color: var(--border) !important;
}
.join-prompt__error {
  color: var(--ui-danger);
}
.join-prompt__no {
  font-size: var(--text-xl);
  font-weight: 700;
  font-family: ui-monospace, 'SFMono-Regular', Menlo, monospace;
  letter-spacing: 0.08em;
}

.unsub__current {
  padding-left: 1.2rem;
}
.unsub__note {
  color: var(--muted);
  font-size: var(--text-sm);
}

/* ── AI 客服來源標示 ── */
.chat-widget__source {
  color: var(--muted);
  font-size: var(--text-xs);
}

.home-links a {
  margin-right: 0.2rem;
}

/* ── 營運後台 ── */
.admin section {
  margin-bottom: var(--space-7);
}
.admin__message {
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-sm);
  background: var(--ui-primary-tint);
  border: 1px solid var(--border);
}
/* 要管理員停一下再動手的提示（例如名冊登記年費時，這個人已有待對帳的轉帳回報）。
   黃底深墨字：--ink 對 --brand-gold-tint 遠超 AA；靠文字與左框區分，不只靠顏色。 */
.admin__warn {
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-sm);
  background: var(--brand-gold-tint);
  border: 1px solid var(--border);
  border-left: 4px solid var(--brand-gold);
}
/* 譯文編輯區掛在表格底下、不在 <form> 裡，所以要一起列進來。
   複製一份規則的話，兩邊的欄位早晚會長得不一樣。 */
.admin__form label,
.admin__i18n label {
  display: block;
  margin-bottom: var(--space-3);
}
.admin__form input,
.admin__form textarea,
.admin__form select,
.admin__i18n input,
.admin__i18n textarea {
  display: block;
  width: 100%;
  max-width: 420px;
  padding: var(--space-2) var(--space-3);
  margin-top: var(--space-1);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font: inherit;
}
.admin__row {
  display: flex;
  gap: var(--space-4);
  flex-wrap: wrap;
}
.admin__form button,
.admin__roster > button {
  padding: var(--space-2) var(--space-5);
  border-radius: var(--radius-sm);
  border: 1px solid var(--accent);
  background: var(--accent);
  color: var(--ui-on-fill);
  cursor: pointer;
}
.admin__hint,
.admin-hint {
  color: var(--muted);
  font-size: var(--text-sm);
}
.admin__table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--text-sm);
}
.admin__table th,
.admin__table td {
  text-align: left;
  padding: var(--space-2) var(--space-3);
  border-bottom: 1px solid var(--border);
  vertical-align: top;
}
.admin__table th {
  color: var(--muted);
  font-weight: 600;
}
/* 只給讀屏軟體的標籤。用 display:none 或 visibility:hidden 都會讓它一起消失在
   無障礙樹上，等於沒有標籤——那正是這個 class 要避免的事。 */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ── 後台會員名冊 ── */
.admin__counts {
  color: var(--muted);
  font-size: var(--text-sm);
}
.admin__counts strong {
  color: var(--ink);
  font-size: var(--text-base);
}
.admin__counts-sep {
  margin: 0 var(--space-2);
}
.admin__filters {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  align-items: center;
  margin-bottom: var(--space-3);
}
.admin__check {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: var(--tap);
}
/* 表單裡的勾選框與單選鈕：上面 .admin__form input 是給文字欄位的（block、滿寬），
   套到這兩種會變成框在上一行、字在下一行。年費登記面板是第一個在 .admin__form 裡用它們的地方。 */
.admin__form .admin__check {
  display: flex;
  margin-bottom: 0;
}
.admin__form .admin__check input {
  display: inline-block;
  width: auto;
  margin-top: 0;
}
.admin__search input {
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  min-width: min(20rem, 100%);
  min-height: var(--tap);
}
.admin__truncated {
  color: var(--ui-danger);
  font-size: var(--text-sm);
}
.admin__empty {
  color: var(--muted);
}
/* 名冊有十欄，窄螢幕塞不下。讓表格自己橫向捲，不要讓整個頁面橫捲。 */
.admin__scroll {
  overflow-x: auto;
}
.admin__scroll .admin__table {
  min-width: 60rem;
}
/* 欄數少的表（勾稽表、活動、報名名單、LINE 好友異動）套名冊那 60rem 會在桌機被撐出無謂的橫捲。
   ⚠️ 但仍要有 min-width：拿掉的話窄螢幕下每一欄會被壓成一個字一行，
   而 table 的 width:100% 只是下限、內容更寬時照樣撐爆容器——所以
   「包進 .admin__scroll」與「給一個 min-width」是同一件事的兩半，不能只做一半。 */
.admin__scroll .admin__table--narrow {
  min-width: 34rem;
}
.admin__member-no {
  font-family: var(--font-mono, ui-monospace, monospace);
  letter-spacing: 0.05em;
  white-space: nowrap;
}
.admin__muted {
  color: var(--muted);
}
.admin__role,
.admin__tag {
  display: inline-block;
  margin-left: var(--space-2);
  padding: 0 var(--space-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font-size: var(--text-xs);
  white-space: nowrap;
}
.admin__tag {
  margin-left: 0;
  margin-right: var(--space-1);
}

/* 說明文字：擺在數字底下，講出那個數字代表什麼、要不要處理。
   單獨一個數字沒人知道該不該行動。 */
.admin__note {
  color: var(--muted);
  font-size: var(--text-sm);
  margin-top: var(--space-2);
}
.admin__subhead {
  font-size: var(--text-base);
  margin-top: var(--space-5);
  margin-bottom: var(--space-2);
}
.admin__reconcile {
  margin-bottom: var(--space-5);
}
/* 合計列要與明細分得開——勾稽的動作就是拿它跟上面幾列核對。 */
.admin__table tfoot td {
  border-top: 2px solid var(--border);
  font-weight: 600;
}

/* LINE 綁定與好友狀態的四態標籤。
   ⚠️ 不可以只靠顏色分辨（WCAG 1.4.1，協會受眾年齡分布廣），
   所以每一格都直接寫出狀態名稱，顏色與框線只是輔助。 */
.admin__line {
  display: inline-block;
  padding: 0 var(--space-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font-size: var(--text-xs);
  white-space: nowrap;
}
.admin__line--ok {
  border-color: var(--ui-primary);
  color: var(--ui-primary);
}
.admin__line--warn {
  border-color: var(--ui-danger);
  color: var(--ui-danger);
}
/* 「未綁定」與「未查核」都是「還不知道／還沒發生」，用虛線框與灰字，
   視覺上與確定的狀態（好友／非好友）分開。 */
.admin__line--muted {
  border-style: dashed;
  color: var(--muted);
}

.admin__slug {
  color: var(--muted);
  font-size: var(--text-xs);
}
.admin__roster {
  margin-top: var(--space-5);
  padding-top: 1rem;
  border-top: 2px solid var(--border);
}

/* ── 後台的譯文欄位 ──
   語系預設收合。多數活動只會填繁中，四語 × 三欄全部攤開會把新增表單拉長三倍，
   常用欄位反而要捲動才找得到。 */
.admin__fieldset {
  margin: 0 0 var(--space-4);
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}
.admin__fieldset legend {
  padding: 0 var(--space-2);
  color: var(--muted);
  font-size: var(--text-sm);
}
.admin__i18n-lang {
  margin-bottom: var(--space-2);
}
/* ⚠️ 不要給 summary 設 display:flex 或 grid——Chrome 會因此吃掉展開三角形，
   而後台唯一在說「這裡可以打開」的視覺線索就是那個三角形。
   要撐到觸控下限（--tap）就用 padding，不要換 display。 */
.admin__i18n-lang summary {
  cursor: pointer;
  padding: calc((var(--tap) - 1.7em) / 2) 0;
  line-height: 1.7;
}
/* 用 --ui-warning（品牌金壓深，4.52:1）而不是 --muted：
   「還沒翻」是要被看見的待辦，灰掉就等於沒說。 */
.admin__i18n-todo {
  color: var(--ui-warning);
  font-size: var(--text-sm);
  margin-inline-start: var(--space-2);
}
/* 每語一顆小方塊，直接看得出「缺哪語」，不是只給一個籠統的完成度文字。
   done 用 --ui-success（品牌綠壓深，6.85:1），未完成維持 --muted 中性色——
   「未翻」不是錯誤，不必用警示色，跟上面 .admin__i18n-todo 的待辦提示是不同語境。 */
.admin__i18n-chips {
  display: inline-flex;
  gap: var(--space-1);
  vertical-align: middle;
}
.admin__i18n-chip {
  display: inline-block;
  min-width: 1.6em;
  padding: 0 var(--space-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--muted);
  font-size: var(--text-xs);
  line-height: 1.6;
  text-align: center;
}
.admin__i18n-chip--done {
  border-color: var(--ui-success);
  color: var(--ui-success);
  font-weight: 600;
}
/* 動作欄現在有兩顆按鈕，換行時不要黏在一起 */
.admin__table td button + button {
  margin-inline-start: var(--space-2);
}
/* 事件表格動作欄（報名名單／譯文兩顆按鈕並排），窄螢幕改直排避免擠壓成一團。 */
.admin__actions {
  white-space: nowrap;
}
@media (max-width: 30rem) {
  .admin__actions {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-2);
    white-space: normal;
  }
  .admin__actions button + button {
    margin-inline-start: 0;
  }
}

/* ── 後台儀表板：卡片、總覽數字、表格內數值對齊、操作按鈕 ──
   Paul 要的是 BI 質感，不是布告板：五個區塊各自成卡、開頭一排總覽數字、
   數字欄位靠右對齊等寬字體、危險操作用警示色而不是跟其他按鈕一樣的實心填色。
   刻意不比照 .member-card 的品牌色調——那是對外的會員識別，後台要的是中性、
   不分區塊上色（見 CLAUDE.md「品牌色」節：一區一色的彩虹配色是明確禁用的）。 */
.admin__card {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: var(--space-5);
}
.admin__card-head {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-3);
  margin-bottom: var(--space-4);
  padding-bottom: var(--space-3);
  border-bottom: 1px solid var(--border);
}
.admin__card-head h2 {
  margin-block: 0;
}
.admin__card-count {
  color: var(--muted);
  font-size: var(--text-sm);
}
.admin__card-count strong {
  color: var(--ink);
  font-family: var(--font-num);
  font-variant-numeric: tabular-nums;
  font-weight: 600;
}
/* 1px 縫隙當格線的 grid 技巧：欄數隨寬度 auto-fit，不必靠 :last-child／
   :nth-last-child 去掉多餘邊框——那類寫法在 auto-fit 換行時會斷，是那種
   「改了不會報錯、要縮放視窗才看得出來」的 bug。 */
.admin__summary {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(8.5rem, 1fr));
  gap: 1px;
  background: var(--border);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  overflow: hidden;
  margin-bottom: var(--space-6);
}
.admin__summary-item {
  background: var(--bg);
  padding: var(--space-4);
}
/* 唯一可點的那張摘要卡（舊會員名單入口）：整張是連結，蓋掉 a 的預設底線／
   色調，靠背景色變化與 hover/focus 給出「這張可以按」的線索，而不是像行內
   文字連結那樣靠底線——連結一律保留底線那條規則管的是散文裡的文字連結，
   這裡是卡片／按鈕語意，用同一套視覺語言判斷不到。 */
.admin__summary-item--link {
  display: block;
  color: inherit;
  text-decoration: none;
  cursor: pointer;
  transition: background-color 0.15s ease;
}
.admin__summary-item--link:hover,
.admin__summary-item--link:focus-visible {
  background: var(--surface);
}
.admin__summary-num {
  display: block;
  font-family: var(--font-num);
  font-variant-numeric: tabular-nums;
  font-size: var(--text-2xl);
  color: var(--ink);
  line-height: 1.2;
}
.admin__summary-label {
  display: block;
  margin-top: var(--space-1);
  color: var(--muted);
  font-size: var(--text-sm);
}
/* 數字／日期欄靠右對齊，跟 --font-num 的等寬數字搭配，掃視時才能對得整齊。
   兩個 class 疊起來刷 specificity（0,2,0），保證贏過上面 .admin__table td 的
   (0,1,1)——單一 .admin__num 選不到、順序也救不回來。 */
.admin__table .admin__num {
  text-align: right;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
/* 斑馬紋：偶數列疊一層 --surface（跟卡片背景 --bg、頁面背景 --paper 三個
   非常接近的白互不相同），純靠 CSS 巢狀選出來，不必在資料上加索引。 */
.admin__table tbody tr:nth-child(even) td {
  background: var(--surface);
}
/* 名冊操作欄目前是唯二的裸 <button>（設為管理員／取消管理員），全站沒有
   全域 button reset，不給樣式就是瀏覽器原生外觀——那正是「布告板感」的來源之一。
   兩者共用中性外框，只有「設為管理員」疊 --caution：那是會擴大權限的操作，
   要跟「取消管理員」一眼分得出來，但不做成彈出視窗——保留在原欄位、
   confirm() 照舊。 */
.admin__action {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--tap);
  padding: 0 var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg);
  color: var(--ink);
  font: inherit;
  font-size: var(--text-sm);
  cursor: pointer;
  transition: background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}
.admin__action:hover {
  border-color: var(--ui-primary-strong);
}
.admin__action--caution {
  border-color: var(--ui-warning);
  color: var(--ui-warning);
  font-weight: 600;
}
.admin__action--caution:hover {
  background: var(--ui-warning);
  color: var(--ui-on-fill);
}

.breadcrumb {
  font-size: var(--text-sm);
}
.breadcrumb a {
  color: var(--muted);
  text-decoration: none;
}
.event-detail h1 {
  margin-bottom: var(--space-1);
}

/* ── 後台：網站流量 ──
   參考 paulkuo.tw/dashboard 的流量區塊。圖表刻意手刻（SVG 折線 + CSS 橫條），
   不引入圖表 library：這一張圖用不到 library 的九成功能，卻要每個開後台的人
   多下載數十 KB。色彩沿用介面層 token，不另開一套「圖表色盤」——
   後台的其他區塊都是中性的，只有這裡跳出兩個品牌色會顯得像另一個網站。 */
.traffic__ranges {
  margin-inline-start: auto;
  display: flex;
  gap: 1px;
  background: var(--border);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  overflow: hidden;
}
.traffic__range {
  appearance: none;
  border: 0;
  background: var(--bg);
  color: var(--muted);
  font: inherit;
  font-size: var(--text-sm);
  padding: var(--space-2) var(--space-3);
  cursor: pointer;
}
.traffic__range--on {
  background: var(--ui-primary);
  color: var(--bg);
}

.traffic__figures {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(9.5rem, 1fr));
  gap: var(--space-4);
  margin-bottom: var(--space-5);
}
.traffic__num {
  display: block;
  font-family: var(--font-num);
  font-variant-numeric: tabular-nums;
  font-size: var(--text-2xl);
  line-height: 1.2;
  color: var(--ink);
}
.traffic__label {
  display: block;
  margin-top: var(--space-1);
  color: var(--muted);
  font-size: var(--text-sm);
}
.traffic__sub,
.traffic__delta {
  display: block;
  margin-top: var(--space-1);
  font-size: var(--text-xs);
  color: var(--muted);
}
/* 成長率上下用色 + 箭頭：只靠顏色區分方向對色覺障礙者不成立（WCAG 1.4.1），
   所以箭頭是必要的，不是裝飾。持平與「無從比較」都不上色也不給箭頭。 */
.traffic__delta--up {
  color: var(--ui-success);
}
.traffic__delta--down {
  color: var(--ui-danger);
}
.traffic__delta--up::before {
  content: '▲ ';
}
.traffic__delta--down::before {
  content: '▼ ';
}
.traffic__vs {
  color: var(--muted);
  margin-inline-start: var(--space-2);
}

.traffic__chart {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--space-2);
  margin: 0;
}
/* 座標軸文字是 HTML 不是 SVG <text>：SVG 整張等比縮放時字會跟著縮，
   手機上只會看起來「有點糊」而不會有任何錯誤訊息。 */
.traffic__axis-y,
.traffic__axis-x {
  font-family: var(--font-num);
  font-variant-numeric: tabular-nums;
  font-size: var(--text-xs);
  color: var(--muted);
}
.traffic__axis-y {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  align-items: flex-end;
  height: 11rem;
  line-height: 1;
}
/* 首尾兩個刻度各往外推半個字高，數字才會對齊格線本身而不是格線下方 */
.traffic__axis-y span:first-child {
  margin-top: -0.5em;
}
.traffic__axis-y span:last-child {
  margin-bottom: -0.5em;
}
.traffic__axis-x {
  display: flex;
  justify-content: space-between;
  margin-top: var(--space-2);
}
/* preserveAspectRatio="none" 讓折線橫向填滿容器；代價是筆畫會被非等比拉伸，
   所以線條一律加 vector-effect: non-scaling-stroke（否則寬螢幕上線條會變細）。 */
.traffic__svg {
  display: block;
  width: 100%;
  height: 11rem;
  overflow: visible;
}
.traffic__grid {
  stroke: var(--border);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
}
.traffic__area {
  fill: var(--ui-primary-tint);
  stroke: none;
}
.traffic__line {
  fill: none;
  stroke-width: 2;
  stroke-linejoin: round;
  stroke-linecap: round;
  vector-effect: non-scaling-stroke;
}
.traffic__line--pv {
  stroke: var(--ui-primary);
}
.traffic__line--uv {
  stroke: var(--ui-accent-alt);
}
.traffic__line--partial {
  stroke-dasharray: 4 4;
}
.traffic__legend {
  grid-column: 2;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-4);
  margin-top: var(--space-3);
  font-size: var(--text-sm);
}
.traffic__key {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
}
.traffic__key::before {
  content: '';
  width: 1.5rem;
  border-top: 2px solid currentColor;
}
.traffic__key--pv {
  color: var(--ui-primary);
}
.traffic__key--uv {
  color: var(--ui-accent-alt);
}
.traffic__key--partial {
  color: var(--muted);
}
.traffic__key--partial::before {
  border-top-style: dashed;
}

/* ⚠️ min() 不可省：minmax 的第一個值是硬下限，容器比它窄時 track 仍會是 18rem，
   直接把卡片撐爆（實測 320px 的 iPhone SE：後台可用寬只剩 226px，橫向溢出 15px）。
   包一層 min(…, 100%) 之後，窄容器改用 100%，寬容器維持原本的雙欄自動折行。 */
.traffic__lists {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(18rem, 100%), 1fr));
  gap: var(--space-5);
  margin-top: var(--space-6);
}
.traffic__lists h3 {
  font-size: var(--text-base);
  margin-block: 0 var(--space-3);
}
.traffic__bars {
  list-style: none;
  margin: 0;
  padding: 0;
}
/* 三欄：標籤、長條、數字。標籤 minmax(0,1fr) 才會在長路徑時省略而不是撐爆格線
   （auto 會依內容擴張，長條就被擠沒了）。 */
.traffic__bars li {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 6rem auto;
  align-items: center;
  gap: var(--space-3);
  padding-block: var(--space-2);
}
.traffic__bars li + li {
  border-top: 1px solid var(--border);
}
.traffic__bar-label {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--text-sm);
}
.traffic__bar-track {
  height: 0.5rem;
  background: var(--surface);
  border-radius: var(--radius-sm);
  overflow: hidden;
}
.traffic__bar-fill {
  display: block;
  height: 100%;
  background: var(--ui-primary);
  border-radius: var(--radius-sm);
}
.traffic__bar-fill--alt {
  background: var(--ui-accent-alt);
}
.traffic__bar-num {
  font-family: var(--font-num);
  font-variant-numeric: tabular-nums;
  font-size: var(--text-sm);
  color: var(--muted);
}

.traffic__notes {
  margin-top: var(--space-6);
  padding-top: var(--space-4);
  border-top: 1px solid var(--border);
  font-size: var(--text-sm);
  color: var(--muted);
}
.traffic__notes summary {
  cursor: pointer;
  color: var(--ink-muted);
}
.traffic__notes ul {
  margin-block: var(--space-3) 0;
  padding-inline-start: var(--space-5);
}
.traffic__notes li + li {
  margin-top: var(--space-2);
}
.traffic__notes strong {
  color: var(--ink-muted);
}

@media (max-width: 30rem) {
  /* 窄螢幕把長條列縮成兩欄：6rem 的長條加上數字欄，標籤會被壓到只剩幾個字。 */
  .traffic__bars li {
    grid-template-columns: minmax(0, 1fr) auto;
  }
  .traffic__bar-track {
    display: none;
  }
}

/* ── 內容（articles / media / pages）──────────────────────────────── */
/* 這幾個 class（.card / .empty / .notice）刻意留在 base.css 而不是各頁元件裡：
   影音、活動、會員、隱私權那幾頁共用它們，升級這裡就能一起帶上來，
   點進去不會看到兩種設計。 */
.card-list {
  list-style: none;
  padding: 0;
  margin: var(--space-6) 0 0;
  display: grid;
  /* 分列靠色脊與留白，所以留白要夠：活動卡帶按鈕，8px 會擠成一團。
     文章卡也一起放寬 —— CJK 在密排下更需要卡與卡之間的呼吸。 */
  gap: var(--space-4);
}
/* 不用橫向分隔線。分列靠色脊與留白 ——「制度感」不等於髮絲線格子。 */
.card {
  border-bottom: 0;
  border-left: 6px solid var(--section);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  /* 同上：有內容的東西留在紙上，白色保留給「還沒有內容」的版位 */
  /* ⚠️ 內距在 .card__text，不在這裡：卡片上方要放版位（19:10）時，
     圖必須齊到卡片邊緣，內距留在外框會讓圖縮一圈、與色脊之間浮一條紙色。 */
}
.card__text {
  padding: var(--space-4) var(--space-5);
}
.card__title {
  margin: 0 0 0.35rem;
  font-size: var(--text-lg);
  font-weight: 600;
  letter-spacing: 0.03em;
}
.card__title a {
  color: inherit;
  text-decoration: none;
}
.card__title a:hover {
  text-decoration: underline;
}
.card__meta {
  margin: 0;
  color: var(--ink-muted);
  font-family: var(--font-num);
  /* 0.9rem 不是 0.8：活動卡的 meta 除了日期還有地點與名額（實文字），
     等寬字排 CJK 會落到中文 fallback，12.8px 對協會的受眾太小。 */
  font-size: var(--text-sm);
  letter-spacing: 0.06em;
}
.card__body {
  margin: var(--space-2) 0 0;
  color: var(--ink-muted);
}
/* 活動卡底下的報名鈕那一列。內文段落的預設上下邊距在這裡太鬆。 */
.card__actions {
  margin: var(--space-4) 0 0;
}

/* 導覽用的卡片（永續生活、加入我們底下的入口），不是內容卡片。
   ⚠️ 底線是必要的，不是裝飾：內容卡片的標題不畫底線沒問題（整張卡明顯是一則文章），
   但這幾張卡「整頁只有它們」，不畫底線就跟標題長得一模一樣，讀者不知道那是連結。
   CLAUDE.md 也登記過「連結一律保留底線」—— 協會受眾年齡分布廣，
   只靠顏色（這裡連顏色都沒有）區分連結對色覺障礙者不友善（WCAG 1.4.1）。
   實拍 /living/ 與 /join/ 才看出來的。 */
.card--entry .card__title a {
  text-decoration: underline;
  text-underline-offset: 0.2em;
}

/* ══ 狀態幾何：實心＝已建置，斜線＝已預留、尚未填入 ══════════════════
   斜線來自製圖慣例：圖面上「提案中、尚未施作」的部分打斜線。
   所以「規劃中」與「還沒有內容」在這裡長得不像錯誤訊息，
   長得像圖面上一塊已經畫好邊界、還沒填進去的區域。
   兩個狀態、一套語彙，用在四個地方：進度尺、清單脊、規劃中說明框、空狀態。

   ⚠️ 兩個填色 class（.fill-solid / .fill-hatch）定義在**本檔最末**，不是這裡。
   理由是權重：它們與 .v-rail__seg、.v-row__spine 這些結構 class 同權重（0,1,0），
   同權重時後寫的贏。放最後才不會被結構樣式的 background 蓋掉。
   對應地，結構 class 一律不寫 background。 */

/* 空狀態＝已預留的槽位，不是空白，也不是壞掉。 */
.empty {
  position: relative;
  display: block;
  margin: var(--space-6) 0 0;
  padding: var(--space-6) var(--space-5) var(--space-6) calc(var(--space-5) + var(--rail-h));
  background: var(--bg);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  color: var(--ink-muted);
  font-size: var(--text-lg); /* 受眾含長者：狀態說明不縮小，反而放大 */
  /* 給它一個實際的版位高度。第二輪截圖看出來的：一條 60px 的細帶讀起來像殘留物，
     一塊有高度的區域才讀得出「這裡已經畫好邊界、等內容進來」。 */
  min-height: 7.5rem;
  display: flex;
  align-items: center;
}
.empty::before {
  content: '';
  position: absolute;
  inset-block: 0;
  inset-inline-start: 0;
  width: var(--rail-h);
  background-color: var(--section-tint);
  background-image: repeating-linear-gradient(
    45deg,
    var(--section) 0 var(--hatch-w),
    transparent var(--hatch-w) calc(var(--hatch-w) * 2)
  );
}

/* 「規劃中」標籤。等寬字＋寬字距＝這是一個登記過的狀態，不是一句抱歉。
   底是該區塊的淺色、字是該區塊的介面墨，五組都算過：
   青 4.72／桃 4.83／金 4.60／紫 5.80／綠 6.21，全部 ≥4.5。 */
.badge-planned {
  display: inline-block;
  font-family: var(--font-num);
  font-size: var(--text-xs);
  letter-spacing: 0.14em;
  line-height: 1.6;
  color: var(--section-ink);
  background: var(--section-tint);
  border-radius: 3px;
  padding: 0.1rem 0.55rem;
  white-space: nowrap;
}
.prose {
  line-height: 1.75;
  /* 閱讀行寬在這裡限制，不在容器。CJK 一行約 32-40 字最好讀。
     標題與版面furniture 仍然對齊 --col，寬標題配窄內文。 */
  max-width: var(--measure);
}
.prose h1 {
  margin-bottom: var(--space-1);
}
.prose img,
.prose .cover {
  max-width: 100%;
  height: auto;
  border-radius: var(--radius-sm);
}
.tags {
  margin-top: var(--space-6);
}
.tag {
  display: inline-block;
  margin-right: 0.4rem;
  padding: var(--space-1) var(--space-2);
  border: 1px solid var(--border);
  border-radius: 999px;
  color: var(--muted);
  font-size: var(--text-xs);
}
/* 「這一頁還沒有你選的語言」這類提示。與空狀態同一套語彙：白底＋斜線脊。

   ⚠️ 這裡**不用 --measure**，而是讓框自己收到內容寬（fit-content）。
   原本跟 .prose 一樣釘在 44rem，但兩者的處境不同：.prose 是一整頁的內文，
   長時間閱讀需要行寬保護；這一段是一兩句的開場白，下面接的是滿版的卡片格線。
   釘死 44rem 的結果是繁中那句只有 626px 的字被撐進 704px 的框、
   英文那句 1117px 被折成兩行，兩種語言各壞一種（Paul 2026-08-27 指出英文那一種）。

   fit-content 讓每一語自己決定：繁中的框收到 626px（比原本更貼），
   英文在寬螢幕上一行排完，視窗窄到裝不下時照樣回頭換行。
   max-width: 100% 是那個上限——沒有它，長字串會把版面推寬。 */
.notice {
  position: relative;
  width: fit-content;
  max-width: 100%;
  margin: 0 0 var(--space-5);
  padding: var(--space-4) var(--space-4) var(--space-4) calc(var(--space-4) + var(--rail-h));
  border-left: 0;
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  background: var(--bg);
  color: var(--ink-muted);
  font-size: var(--text-base); /* 受眾含長者：不縮到 sm */
}
.notice::before {
  content: '';
  position: absolute;
  inset-block: 0;
  inset-inline-start: 0;
  width: var(--rail-h);
  background-color: var(--section-tint);
  background-image: repeating-linear-gradient(
    45deg,
    var(--section) 0 var(--hatch-w),
    transparent var(--hatch-w) calc(var(--hatch-w) * 2)
  );
}

/* ── 影音 lite 嵌入（點了才載入 iframe）───────────────────────────── */
.media-embed {
  position: relative;
  margin: var(--space-5) 0;
  aspect-ratio: 16 / 9;
  background: var(--media-embed-bg);
  border-radius: var(--radius-sm);
  overflow: hidden;
}
/* 絕對定位填滿，不要用 height: 100%：外框的高度來自 aspect-ratio，
   百分比高度在這條鏈上解析不到，圖會照原始比例撐成 4:3 而在上方露出黑邊
   （2026-08-01 用瀏覽器量出來的：外框 672x378、圖卻是 672x504）。 */
.media-embed__play {
  position: absolute;
  inset: 0;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
}
/* 選擇器刻意帶上外框：`.prose img { height: auto }` 的權重比單一 class 高，
   影音頁的封面就在 .prose 裡面，不提高權重的話 height 會被蓋成 auto、圖片撐成 4:3。 */
.media-embed .media-embed__poster {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.media-embed__icon {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--ui-on-fill);
  font-size: 2.5rem;
  text-shadow: 0 1px 6px rgba(0, 0, 0, 0.6);
}
.media-embed__frame {
  width: 100%;
  height: 100%;
  border: 0;
  display: block;
}

/* 引言：品牌核心句這類獨立成塊的句子。用金／綠的細線當錨點，不放引號、不斜體，
   CJK 斜體是合成出來的、會糊。 */
.prose blockquote {
  margin: 1.6rem 0;
  padding: 0.15rem 0 0.15rem 1.1rem;
  border-left: 3px solid var(--section); /* 跟著該頁的區塊色，不再固定青 */
  font-size: 1.12rem;
  line-height: 1.7;
}
.prose blockquote p {
  margin: 0;
}
.site-footer__links {
  margin: 0;
}
/* 頁尾法務連結：量出來 20px 高。這裡橫向空間充足，高度直接給滿 --tap。 */
.site-footer__links a {
  color: var(--ink-muted);
  display: inline-flex;
  align-items: center;
  min-height: var(--tap);
}
.site-footer__links {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

/* 標誌圖形。高度釘死、寬度自適應 —— 釘寬度會在換檔那天把圖壓扁，
   而那種變形不容易第一眼看出來。

   ⚠️ 頁首放的是**標誌圖形**、協會名仍是文字節點，不是手冊的橫式組合。
   橫式組合把「願景協會」四個字包在圖裡，在 28px 高之下那四個字只剩幾個像素高，
   而且它們是圖、放大不會變清楚、也選不起來、讀屏軟體讀不到。
   要用組合檔的場合是名片與招牌，不是網頁頁首。 */
.site-header__mark {
  height: 28px;
  width: auto;
  display: block;
}

/* ══════════════════════════════════════════════════════════════════════
   首頁：五色進程尺
   ══════════════════════════════════════════════════════════════════════
   版面概念一句話：把 logo 的五色環拆成一把尺 —— 橫著放是頁面頂端的進度尺，
   立起來就是下方清單的脊。實心＝已建置，斜線＝已預留、尚未填入。

   尺是總表、脊是明細，兩者永遠一致，因為都由同一份資料（SECTIONS + isPlanned）
   算出來，沒有任何一處是手寫死的。

   class 一律 v- 前綴：這一區的規則不會跟既有的 .card / .notice / .section 撞名。
   ────────────────────────────────────────────────────────────────────── */

.v-hero {
  padding-block: var(--space-5) 0;
}

/* 標語。字距 0.32em 是這一頁最寬的字距 —— 一行八個字，拉開了才有「被鐫上去」的感覺。 */
.v-hero__eyebrow {
  margin: 0 0 var(--space-4);
  font-size: var(--text-base);
  font-weight: 600;
  letter-spacing: 0.32em;
  color: var(--ui-ink-teal); /* 對紙 4.83 */
}

/* 關掉 main h1::before 的區塊色塊：首頁的色彩由下方那把尺承載，
   權重 (0,1,1) 勝過 main h1::before 的 (0,0,2)，不必 !important。 */
.v-hero__title {
  margin: 0 0 var(--space-5);
  font-size: clamp(2.3rem, 5vw, 3.6rem);
  font-weight: 600;
  letter-spacing: 0.04em;
  line-height: 1.18;
}
.v-hero__title::before {
  content: none;
}

.v-hero__lead {
  margin: 0 0 var(--space-6);
  max-width: 34rem;
  font-size: var(--text-lg);
  color: var(--ink-muted); /* 對紙 7.09 */
}

/* ── 進度尺 ──
   五段等寬，段間留 4px。留白是刻意的：連成一條會讀成漸層色帶，
   分開才讀成「五個各自獨立的區塊」，也才對得上標誌裡五個各自獨立的人。 */
.v-rail {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 4px;
  margin: 0;
  padding: 0;
  list-style: none;
}
.v-rail__seg {
  height: var(--rail-h);
  border-radius: 2px;
}

/* ── 進程清單 ── */
.v-ledger {
  display: grid;
  gap: 4px; /* 與尺同值，兩者才讀成同一把尺 */
  margin: var(--space-7) 0 0;
  padding: 0;
  list-style: none;
}

.v-row {
  position: relative;
  display: grid;
  grid-template-columns: 3.5rem 1fr auto;
  column-gap: var(--space-4);
  /* 左側留出脊的寬度。脊用絕對定位而不是 grid 欄：
     grid 欄只到內容盒，上下 padding 會在脊的兩端留白，五條脊就接不成一條連續的邊
     （第一輪截圖實測：96px 的列裡脊只有 48px，整個「立起來的尺」讀不出來）。
     .empty / .notice / PlannedNotice 的脊也是同一個做法，四處一致。 */
  /* 右側不留 padding：狀態欄的右緣要對齊上方那把尺的右端。
     留了 24px 就差那 24px，兩者讀不成同一把尺。 */
  padding: var(--space-5) 0 var(--space-5) calc(var(--rail-h) + var(--space-4));
  /* 底色刻意留在紙上、不做白卡。全站白色只有一個意思：**這一格還沒有內容**
     （規劃中說明框、空狀態）。已建置的列若也用白，白就退化成裝飾，
     下面那塊「規劃中」的白就不再說得出話。
     少一層卡片，色脊也才真的成為這一頁唯一的分節裝置。 */
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  text-decoration: none;
  color: inherit;
  /* 整列是連結。列高遠大於 --tap，對長者與觸控友善。 */
  min-height: 104px;
  align-content: center;
}
.v-row:hover {
  background: var(--section-tint); /* 墨對五個淺色底 14.45–15.67，全部遠超標 */
}
.v-row:hover .v-row__name {
  color: var(--section-ink);
}

.v-row__spine {
  position: absolute;
  inset-block: 0;
  inset-inline-start: 0;
  width: var(--rail-h);
}

/* 序號。1.75rem 的等寬數字、用該列自己的區塊墨 —— 這是**目錄編號**不是步驟編號，
   進度由填色承載、不由數字承載，所以它不必看起來像流程圖的 step 1。 */
.v-row__num {
  grid-column: 1;
  grid-row: 1;
  font-family: var(--font-num);
  font-size: 1.75rem;
  font-weight: 400;
  line-height: 1.15;
  letter-spacing: 0.01em;
  color: var(--section-ink);
}

.v-row__body {
  grid-column: 2;
  grid-row: 1;
  min-width: 0;
}

.v-row__name {
  display: block;
  margin: 0;
  font-size: clamp(1.3rem, 2.1vw, 1.6rem);
  font-weight: 600;
  letter-spacing: 0.1em;
  line-height: 1.3;
}

.v-row__sub {
  display: block;
  margin: 0.4rem 0 0;
  color: var(--ink-muted);
}

/* 右欄＝狀態欄。第三輪截圖抓到的：狀態塞在名稱底下時，右半邊整片空著，
   那顆孤零零的「規劃中」浮在遠處像沒放好；移到狀態欄之後，
   左邊讀名稱、右邊讀狀態，才是一份登記簿該有的讀法。 */
.v-row__state {
  grid-column: 3;
  grid-row: 1;
  align-self: center;
  justify-self: end;
  max-width: 20rem;
  text-align: right;
}

/* 空狀態句子。只在該 collection 真的 0 筆時才渲染（見 StageLedger），
   內容進來就自動消失。字級不縮到 1rem 以下，受眾含長者。 */
.v-row__status {
  display: block;
  color: var(--ink-muted);
  font-size: var(--text-base);
}

/* ── AI 客服等附加區塊 ──
   h2 的 margin-block-start: var(--space-6) 會疊在區塊 margin 上，
   區塊裡的第一個 h2 歸零。(0,2,1) 勝過 h2 的 (0,0,1)。 */
.v-block {
  margin-top: var(--space-8);
}
.v-block > h2:first-child {
  margin-block-start: 0;
}

/* ══════════════════════════════════════════════════════════════════════
   分流式首頁（2026-08-09 理事長拍板的資訊架構）
   ══════════════════════════════════════════════════════════════════════
   區塊順序由 src/components/vision/home-layout.ts 的 HOME_BLOCKS 決定，
   所以這裡的間距**不能綁在特定區塊上**（那等於把順序又寫死一次）：
   節奏用「相鄰兄弟」規則給，誰排在誰後面都成立。
   ────────────────────────────────────────────────────────────────────── */
.v-home > * + * {
  margin-top: var(--space-8);
}
/* 每個區塊的第一個 h2 歸零：間距由上面那條相鄰規則給，
   不歸零會與 h2 自己的 margin-block-start 疊成兩倍。 */
.v-home > * > h2:first-child {
  margin-block-start: 0;
}

/* 身分聲明與入口磚是**同一屏的一個單位**，所以這兩塊之間收緊到 space-6，
   不吃上面那條 space-8 的通則。⚠️ 這是「兩者相鄰時」的規則，不是寫死順序：
   有人在 home-layout.ts 把它們拆開，這條就自動不生效、回到通則。 */
.v-home > .v-ident + .v-gates {
  margin-top: var(--space-6);
}

/* ── ① 身分聲明 ──
   理事長：「介紹慈善協會的不要太長。」三行 ＋ 一個連結，完整介紹在 /about/。
   ⚠️ 不再自帶上方 padding：main 本身已有 --space-7 的上緣，兩者相加在 390px
   會直接吃掉「一個螢幕」預算的十分之一（第一輪實拍量到的）。 */
.v-ident {
  padding-block: 0;
}

/* 協會全名。**刻意不用 hero 原本的 clamp(2.3rem, 5vw, 3.6rem)**：
   「社團法人桃園市願景協會」十一個字，在 390px 用那個級數會斷成兩到三行，
   身分聲明 ＋ 入口磚就超過一個螢幕（這一版的硬要求）。
   名字站得穩靠的是位置與字距，不是把它撐到最大。 */
.v-ident__name {
  margin: 0;
  font-size: clamp(1.5rem, 3.4vw, 2.2rem);
  font-weight: 600;
  letter-spacing: 0.08em;
  line-height: 1.3;
}
/* 關掉 main h1::before 的色塊：首頁的顏色由入口磚與進度尺承載，
   權重 (0,1,1) 勝過 main h1::before 的 (0,0,2)，不必 !important。 */
.v-ident__name::before {
  content: none;
}

/* .v-ident__claim 已刪（2026-08-28）：核心主張搬到主視覺 overlay 上
   （樣式在 HomeHero.astro 的 scoped <style>，.v-keyvis__claim）。 */

.v-ident__more {
  margin: var(--space-4) 0 0;
}
.v-ident__more a {
  /* 觸控目標：受眾年齡分布廣，文字連結本身高度不足，撐到 --tap */
  display: inline-flex;
  align-items: center;
  gap: 0.35em; /* 箭頭與文字之間。放在 CSS 而不是文字裡的空白，那個會被 CJK 排版收掉 */
  min-height: var(--tap);
  color: var(--ui-ink-teal); /* 對紙 4.83，AA ✓ */
  font-weight: 600;
  letter-spacing: 0.06em;
}

/* ── ② 大方向入口 ──
   理事長：「把各個大方向的項目區隔開來就對了⋯⋯重點放在商家。」

   ⚠️ 重點磚不靠填底色、也不做白卡片，只靠**面積、字級、上緣色條厚度**。
   兩條既有紀律擋在前面（見 docs/design-direction-進程尺.md）：
     「五色只出現在結構位置（左緣、頂端），從不填滿背景」
     「全站白色只有一個意思：這一格還沒有內容」
   塗成一整塊桃紅它會變成廣告，做成整面最大的一塊它才是入口。

   色條在**頂端**而不是左緣，是為了跟下方進程清單（左緣色脊）分開讀：
   一個是門、一個是登記簿，兩者列的是同五個區塊，形狀不同才不會讀成重複。
   兩個位置都在既有語彙裡（「左緣、頂端」）。

   ⚠️ 第一輪實拍的教訓（兩條，都改掉了）：
   1. 重點磚原本靠「跨兩列的高度」取得份量，結果磚裡有一大塊空白，
      名稱被 flex-end 推到底部、離自己的色條很遠 —— 讀起來不是「大入口」，
      是「色條後面漏了東西」。份量改由**滿版寬度 ＋ 字級 ＋ 色條厚度**承載，
      不靠留白撐高。
   2. 名稱一律緊貼自己的色條。色條與名稱之間留太多空氣，色條就會被讀成
      上一塊的結尾而不是這一塊的開頭。 */
.v-gates {
  display: grid;
  padding: 0;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-6) var(--space-5);
  /* ⚠️ 間距從 4px 改成 space-6／space-5（2026-08-27）。
     4px 原本是「與進度尺、進程清單同一把尺」—— 但那兩塊已經不在首頁的
     HOME_BLOCKS 裡了，同尺的對象不存在，剩下的只有五塊擠在一起。
     入口之間要看得出是五個各自的門，靠的是它們中間的空氣。
     ⚠️ 不要在這裡寫 margin：它與上面 .v-home 的相鄰規則同權重 (0,1,0)、
     而且寫在後面會贏，區塊之間的節奏會整個消失（第一輪就是這樣，
     入口磚整個貼在身分聲明底下）。 */
}

/* 一格入口的骨架：色標 → 名稱（＋規劃中標籤）→ 一句說明 → 箭頭。

   ⚠️ 色標是**一小段**，不是整條上緣邊框（2026-08-27 改）。
   舊版每一格都畫一條與格子等寬的實心色條，而全站區塊色只有一個（青），
   結果首頁上是五條等寬的青色橫槓由上往下疊 —— Paul 的評語是
   「視覺跟 loftwork 不搭，也沒有美感」，而問題正在這裡：五條一樣的槓
   讀起來是線框稿的分隔線，不是五個入口。改成短色標之後，色仍然只出現在
   結構位置（頂端），份量卻回到字與留白身上。

   ⚠️ 名稱一律緊貼自己的色標（row-gap 只給 space-2）。色標與名稱之間留太多空氣，
   色標就會被讀成上一塊的結尾而不是這一塊的開頭。 */
.v-gate {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas:
    'mark mark'
    'body go'
    'desc desc';
  align-items: center;
  column-gap: var(--space-3);
  row-gap: var(--space-2);
  min-height: 64px; /* 遠大於 --tap 44px */
  /* 左內距為 0，名稱才對得齊 --gutter 的左緣；hover 底色要的呼吸空間
     用負外距換回來（見下方 .v-gate:hover）。 */
  padding: 0 var(--space-3) var(--space-4) 0;
  border-radius: var(--radius-sm);
  text-decoration: none;
  color: inherit;
}

.v-gate__mark {
  grid-area: mark;
  width: 2.5rem;
  height: 6px;
  background: var(--section);
}

.v-gate__body {
  grid-area: body;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
  min-width: 0;
}

/* 一句說明用的是 SECTIONS 的長名（home.link.*），四語都已經有，不新增文案。
   ⚠️ 這一組字串原本只給首頁的進程清單用，而進程清單已不在 HOME_BLOCKS 裡，
   所以這裡不是把同一句話印兩次。要把進程清單加回首頁時，先確認兩處的分工。 */
.v-gate__desc {
  grid-area: desc;
  color: var(--ink-muted);
  font-size: var(--text-sm);
  letter-spacing: 0.04em;
  line-height: 1.6;
}

.v-gate__go {
  grid-area: go;
  color: var(--ui-ink-teal); /* 對紙 4.83，AA ✓ */
  font-size: 1.15rem;
  line-height: 1;
  transition: transform 0.18s ease;
}

/* 重點磚。色標加厚加長（與進度尺同厚 --rail-h），字級跳兩階。 */
.v-gate--lead .v-gate__mark {
  width: 4.5rem;
  height: var(--rail-h);
}

.v-gate__name {
  font-size: clamp(1.15rem, 2vw, 1.3rem);
  font-weight: 600;
  letter-spacing: 0.1em;
  line-height: 1.3;
}

/* 英文版收緊字距。與 h1/h2/h3 同一條理由（見本檔上方那段）：正字距是 CJK 的語彙，
   套到拉丁字上會鬆散得像沒排過 —— 實拍 /en/ 首頁時「M e r c h a n t s」就是那個樣子。
   .v-row__name 是既有的進程清單，同一頁上兩者字距不一致會更明顯，一起收。 */
html[lang^='en'] .v-gate__name,
html[lang^='en'] .v-row__name {
  letter-spacing: 0.01em;
}
.v-gate--lead .v-gate__name {
  font-size: clamp(1.75rem, 4.4vw, 2.4rem);
}

/* hover：底色是該區塊的淺色，名稱換成該區塊的介面墨。
   介面墨對自己的淺色底：青 4.72／桃 4.83／金 4.60／紫 5.80／綠 6.21，全部 ≥4.5
   （與 .badge-planned 同一組配對，數值在 base.css 那一段已登記過）。
   左側的負外距讓底色比文字往左多出 space-3，文字本身仍然對齊 --gutter。 */
.v-gate:hover {
  background: var(--section-tint);
  padding-left: var(--space-3);
  margin-left: calc(-1 * var(--space-3));
}
.v-gate:hover .v-gate__name {
  color: var(--section-ink);
}
.v-gate:hover .v-gate__go {
  transform: translateX(4px);
}

@media (min-width: 48rem) {
  /* 桌機：重點磚吃滿一整列，其餘四個排成 2×2（≥75rem 再攤成一排四欄）。
     位置照 DOM 順序，而順序是資料（HOME_GATEWAYS 的第一個就是重點）。

     為什麼不是「左欄一個高磚、右欄疊兩個」（理事長那份文件裡的草圖）：
     試過，跨兩列的高磚裡有一大片空白，讀起來像漏了東西而不像重點
     （第一輪實拍）。滿版一列同樣做得出壓倒性的權重，而且沒有空洞。

     ⚠️ 滿版的重點磚在寬螢幕上會變成「一個詞 ＋ 一公尺的白」，那正是
     2026-08-27 被指出來的空洞感。所以它在這裡改成三欄的一列：
     名稱靠左、說明靠右、箭頭收尾，寬度是被用掉的、不是被剩下的。 */
  .v-gates {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  .v-gate:nth-child(1) {
    grid-column: 1 / -1;
  }
  .v-gate--lead {
    grid-template-columns: minmax(0, auto) minmax(0, 1fr) auto;
    grid-template-areas:
      'mark mark mark'
      'body desc go';
    column-gap: var(--space-6);
  }
  .v-gate--lead .v-gate__desc {
    justify-self: end;
    text-align: right;
  }
}

@media (min-width: 75rem) {
  /* 1200 起四個次要入口攤成一排。兩欄在這個寬度下每格超過 500px，
     一行四個字的名稱後面拖著半個螢幕的空白 —— 與重點磚同一個病。 */
  .v-gates {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
}

/* ── ③ 進度尺 ＋ 進程清單 ──
   標題與尺之間收緊：尺是這個標題的答案，不是下一個段落。 */
.v-progress > h2 {
  margin-block-end: var(--space-4);
}

@media (max-width: 47.99rem) {
  .v-hero__eyebrow {
    letter-spacing: 0.2em; /* 手機欄寬窄，0.32em 會把標語擠成兩行 */
  }
  .v-rail {
    gap: 3px;
  }
  .v-rail__seg {
    height: 8px;
  }
  .v-row {
    grid-template-columns: 2.25rem 1fr;
    column-gap: var(--space-3);
    padding: var(--space-4) 0 var(--space-4) calc(6px + var(--space-3));
    min-height: 0;
  }
  .v-row__spine {
    width: 6px;
  }
  .v-row__num {
    font-size: 1.25rem;
    line-height: 1.4;
  }
  /* 手機沒有並排狀態欄的寬度：整格掉到名稱下一行、改左對齊，
     而不是把名稱擠成兩行。 */
  .v-row__state {
    grid-column: 2;
    grid-row: 2;
    justify-self: start;
    text-align: left;
    max-width: none;
    margin-top: var(--space-2);
  }
}

/* ══════════════════════════════════════════════════════════════════════
   填色狀態 —— 必須是本檔最後兩條規則
   ══════════════════════════════════════════════════════════════════════
   與結構 class 同權重 (0,1,0)，靠「後寫的贏」勝出。往下再加任何規則以前，
   先確認它不會給 .v-rail__seg / .v-row__spine 之類元素設 background。

   ⚠️ 低對比豁免（實算，非推測）：--brand-teal 對紙 2.77、--brand-gold 對紙 2.00，
   都低於 3:1。這兩個色票在本站只當純裝飾／狀態幾何，而它們承載的狀態
   **另有文字承載**（「規劃中」badge、空狀態句子），不是唯一線索 ——
   符合 WCAG 1.4.11 對純裝飾與有文字替代元件的排除。
   反過來說：一旦拿去當文字色就是違規，所以文字一律走 --ui-ink-*。 */
.fill-solid {
  background-color: var(--section);
  background-image: none;
}
.fill-hatch {
  background-color: var(--section-tint);
  background-image: repeating-linear-gradient(
    45deg,
    var(--section) 0 var(--hatch-w),
    transparent var(--hatch-w) calc(var(--hatch-w) * 2)
  );
}

/* ── 頁尾法定登記資料 ──
   全部欄位有值才渲染（見 src/config/org.ts）。用 <dl> 而不是 <p>：
   「立案字號 → 台內團字第 X 號」本來就是名詞與定義的配對，語意標記讓
   螢幕閱讀器唸得出關聯，也讓資料看起來像登記謄本而不是一段小字。 */
.site-footer__legal {
  /* 橫排流動，不是每列一組。原本是兩欄 grid，三組就佔三行、還置中在
     其他頁尾內容的旁邊，讀起來像一塊獨立的表格而不是頁尾的一行小字。
     法定登記資料要公開、但不該是頁尾最大的東西（Paul 2026-08-27）。
     窄螢幕靠 wrap 自然折，是「排不下才折」不是「一組一行」。 */
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  /* 直的是折行後的列距、橫的是組與組之間。組**內**的 dt→dd 由下面那條 div 管，
     間距刻意比組間窄，「名詞—值」的配對才讀得出來。 */
  gap: var(--space-1) var(--space-4);
  margin: var(--space-4) 0 0;
  padding-top: var(--space-3);
  border-top: 1px solid var(--border);
  font-size: var(--text-xs);
}
/* 每組包一層 div（HTML5 允許 dl > div > dt+dd）。沒有這層的話 dt 與 dd 是各自
   獨立的 flex item，窄螢幕折行時會被拆到不同列——2026-08-27 實測 375px：
   「許可機關」留在行尾、「桃園市政府」掉到下一行開頭，讀起來像兩件事。 */
.site-footer__legal > div {
  display: flex;
  align-items: baseline;
  gap: var(--space-1);
}
.site-footer__legal dt {
  color: var(--ink-muted);
  white-space: nowrap;
  letter-spacing: 0.04em;
}
.site-footer__legal dd {
  margin: 0;
  /* 字號是要被抄寫與查證的，用等寬字讓數字對齊、也讓它讀起來像登記資料 */
  font-family: var(--font-num);
  letter-spacing: 0.02em;
  white-space: nowrap;
}

/* ── 頁尾社群 ──
   只渲染有網址的（2026-08-28 起，見 src/config/org.ts 的 ORG_SOCIAL——
   原本的灰字佔位讀起來像壞掉的連結，已拿掉）。 */
.site-footer__social {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-4);
  margin: var(--space-2) 0 0;
  padding: 0;
  list-style: none;
  font-size: var(--text-sm);
}
.site-footer__social a {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap);
  color: var(--ink-muted);
}

/* ── 頁尾最底一列 ──
   法務連結靠左、技術署名靠右。窄螢幕 wrap 成上下兩列，各自貼齊左緣。 */
.site-footer__strip {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0 var(--space-4);
  margin-top: var(--space-2);
}
.site-footer__powered {
  margin: 0;
  font-size: var(--text-xs);
  color: var(--ink-muted);
}
.site-footer__powered a {
  color: inherit;
}

/* ══════════════════════════════════════════════════════════════════════
   理念層：核心主張 / 信念三段 / 同心四層 / 清醒生活三軸 / 加入我們
   ══════════════════════════════════════════════════════════════════════
   2026-08-09 追加。只在本檔**末端追加**，上面既有的規則一行沒動。

   ⚠️ 承上面那條不變式：`.fill-solid` / `.fill-hatch` 靠「同權重後寫的贏」勝出，
   本段寫在它們之後，所以**本段不得對 .v-rail__seg / .v-row__spine 設 background**
   （下一個往這裡追加規則的人也一樣）。本段唯一一個 background 是 .v-sunbeam
   （理念頁的金色頁首帶，2026-08-27 追加），它是自己的 class、碰不到那兩個。
   其餘顏色一律走 fill / stroke / border-color。

   ── 顏色紀律 ──
   全段只用該頁的區塊色（--section 幾何、--section-ink 文字），不把五個品牌色
   分派給四層或三軸。五色是**五個網站區塊**的分派（青理念／桃文章／金影音／
   紫活動／綠會員），借去當別的分類軸會讓金色那一層被讀成「影音」。
   層級與並列靠半徑、線寬、級數承載，不靠換色。

   ── 對比（對紙 #f4f8f7，沿用 brand.css 已算過的值，沒有引入新組合）──
     --ink        #0f1e1d  16.03  AAA ✓   核心主張、對象、願景句、信念末句
     --ink-muted  #4c5654   7.09  AA  ✓   三軸的實踐句、信念前兩句
     --section-ink（理念頁＝#007a7a）4.83 AA ✓  序號與動詞
       （本元件若用在其他區塊頁，--section-ink 是該區塊的介面墨，
         五組分別為 4.83／5.36／4.72／6.37／6.40，全部 ≥4.5）
     --section（#00a8a8）2.77：低於 3:1，只當**純裝飾幾何**（同心圓的線與芯、
       三軸的色脊）。四層與三軸的內容全部另有實文字承載，SVG 一律 aria-hidden，
       顏色不是唯一線索，與 brand.css 登記的豁免同一條。

   ── 字級 ──
   受眾含長者：本段沒有任何一處內文小於 1rem（--text-base）。序號雖是標記，
   仍給 1rem 而不是 --text-xs。
   ────────────────────────────────────────────────────────────────────── */

/* 區塊的第一個 h2 歸零：間距由區塊自己的 margin-top 決定，
   否則會與 h2 的 margin-block-start 疊成兩倍。 */
.v-creed-block > h2:first-child,
.v-aware > h2:first-child,
.v-song > h2:first-child,
.v-join > h2:first-child {
  margin-block-start: 0;
}

/* ── 金色頁首帶（.v-sunbeam）──
   Paul 2026-08-27 指定的「陽光風格」開場：整頁唯一的暖色面，托住標題與核心主張。

   ⚠️ **這不是「每一節一個顏色」。** 那個做法 Paul 2026-08-10 否決過，連「只加一兩格」
   的折衷版一起否決。這一條是**整頁的開場面**，帶以下全部仍然只有青（--section）。
   下一個想「那別的區塊也給一個顏色吧」的人，先回去看那天的決定。

   ── 顏色與對比（對 --brand-gold-tint #f8f4e7，實算）──
     --ink        #0f1e1d  15.60  AAA ✓  標題與核心主張
     --ink-muted  #4c5654   6.90  AA  ✓  （目前這一帶沒用到，留著給日後補副標）
   --brand-gold-tint 在本檔之前是**定義了但全站沒有人用**的 token，
   它當初就是為這種淺色面留的，這裡是第一個用它的地方。

   ⚠️ h1 上方那條區塊色塊（main h1::before）刻意留青、沒有跟著改金。
   那條色塊全站一致地代表「你在哪一區」；跟著頁面配色跑，它就不再是索引，
   而金色在五色分派裡是**影音**，改成金會直接說錯話。

   ⚠️ margin-top 是負的，用來抵消 main 的上緣 padding，讓帶子貼齊頁首。
   與首頁 `.v-home > .v-keyvis:first-child` 同一個做法。理念頁的頁首帶恆為第一塊，
   所以不必綁 :first-child；哪天它不再是第一塊，這一行要跟著拿掉，
   否則會在頁面中間吃掉一段間距。 */
.v-sunbeam {
  background: var(--brand-gold-tint);
  margin-top: calc(-1 * var(--space-7));
  padding-block: clamp(var(--space-7), 8vw, calc(var(--space-8) * 1.5));
}
/* 帶子是全幅的（.v-bleed），字要收回 --gutter 才會跟頁首、頁尾、下方內容切齊。 */
.v-sunbeam__inner {
  padding-inline: var(--gutter);
}
.v-sunbeam h1 {
  margin-bottom: 0;
}

/* ── 核心主張 ──
   整頁唯一一句放大的話。其餘全部留在內文級數：兩句都放大就沒有主張了。 */
/* ⚠️ 上下留白是實拍改出來的：第一版 margin-top 0，主張緊貼在 h1 底下，
   兩者級數又接近（h1 2.4rem／主張 2rem），整段讀成一個兩行的標題、
   主張就不再是主張。給它自己的空氣之後才站得起來。 */
.v-claim {
  margin: var(--space-6) 0 var(--space-7);
  max-width: 26em;
  font-size: clamp(1.4rem, 3.2vw, 2rem);
  font-weight: 600;
  letter-spacing: 0.05em;
  line-height: 1.5;
  color: var(--ink);
}
/* 在金色頁首帶裡，上下留白改由帶子自己的 padding 給，主張只留與標題之間那一段。
   (0,2,0) 勝過 .v-claim 的 (0,1,0)。 */
.v-sunbeam .v-claim {
  margin: var(--space-5) 0 0;
}
/* 與 h1/h2/h3 同一個理由：正字距是 CJK 的語彙，拉丁大字級要收緊。 */
html[lang^='en'] .v-claim {
  letter-spacing: -0.005em;
}

/* ── 信念三段 ──
   自律 → 流行 → 文化 是遞進，順序不可換。遞進靠字色與級數承載，不加圖形：
   三行話不需要三個色塊，加了就變成把並列的東西畫成清單。
   前兩段是被否定的狀態（次要墨），末段是結論（主墨、加大）。
   ⚠️ margin-top 是 2026-08-27 補的：核心主張搬進金色頁首帶之後，
   原本靠 .v-claim 的下緣撐開的距離消失了，不補會直接黏在帶子底下。 */
.v-creed-block {
  margin-top: var(--space-8);
}
.v-creed {
  list-style: none;
  margin: var(--space-5) 0 0;
  padding: 0;
  display: grid;
  gap: var(--space-3);
  max-width: var(--measure);
}
.v-creed li {
  font-size: var(--text-lg);
  color: var(--ink-muted);
}
.v-creed li.is-last {
  margin-top: var(--space-2);
  font-size: var(--text-xl);
  font-weight: 600;
  color: var(--ink);
}
/* 寬螢幕上排成「兩個前提併排 → 結論橫跨」（2026-08-28 視覺診斷的
   「理念頁右側大片留白」）。理念頁在 1440 上容器寬 1238px，而這三句都很短
   （繁中最長 24 字 ≈ 480px），靠左疊成三行時右邊會空掉六成，
   讀起來像版面壞了而不是像留白。

   ⚠️ 為什麼不是置中：全站左對齊，把這一塊置中它就會跟頁首標誌、導覽、
   頁尾對不齊 —— 與 .longread 那段記下的判斷同一條（「那比空白更像壞掉」）。
   這裡走的是同一段列的第三條路：**讓內容自己把寬度用掉**。

   ⚠️ 為什麼是 2+1、不是三等分：前兩句是被否定的狀態、第三句是結論
   （見上面「遞進靠字色與級數承載」）。三等分會把它們畫成三個並列的項目，
   正好是這一塊的註解明文要避開的事。2+1 的閱讀順序仍是
   自律 → 流行 → 文化，遞進沒有被打斷。

   ⚠️ max-width 要解除：--measure（44rem）是**單欄行寬**，留著它兩欄就分不開。
   結論句改由自己收在 --measure 內，行寬保護沒有掉。

   ⚠️ 斷點 64rem：在這個寬度容器約 880px，兩欄各約 416px，
   繁中前提句（16 字 ≈ 272px）一行放得下；英文（60 字元）折成兩行，
   兩欄等高、仍然成立。再窄就會出現「一欄一行、一欄兩行」的碎裂。 */
@media (min-width: 64rem) {
  .v-creed {
    max-width: none;
    grid-template-columns: 1fr 1fr;
    column-gap: var(--space-7);
    align-items: start;
  }
  .v-creed li.is-last {
    grid-column: 1 / -1;
    margin-top: var(--space-5);
    max-width: var(--measure);
  }
}

/* ── 同心四層的照片敘事列（.v-stories / .v-story）──────────────────────
   2026-08-27 取代原本的純幾何徽章版（.v-scopes / .v-scope__glyph，已刪）。
   Paul 指定理念頁改陽光風格：四層願景改成明亮的實景照片。

   ── 為什麼不是首頁那組兩欄卡牆 ──
   同四張圖、同一組 .v-scope__* 內文 class，但排法刻意不同：首頁要掃讀（緊湊兩欄），
   理念頁要讀（一列一層、左右交替的大圖）。複製一份首頁的排版過來，兩頁會變成同一頁。

   ── 為什麼整塊封在 --container（72rem）──
   容器 2026-08-26 起不封頂，1920 上內容寬約 105rem。不封的話圖欄會一路長到
   57rem 以上，而右欄的願景句雖有 --measure 保護、圖卻會大到讓一列塞不進一個螢幕，
   四層就讀不成一個序列。封在 72rem、靠左（不置中），左緣仍與頁首、頁尾切齊。

   ── 顏色 ──
   只有青（--section 幾何、--section-ink 序號與動詞）。金色只出現在頁首帶，
   見上面 .v-sunbeam 那條警告。 */
.v-stories-block {
  margin-top: var(--space-8);
}
/* 大徽章＝總表，放在區段標題左邊當這一塊的標記。
   h2 的 margin-block-start 在這裡要歸零：間距由 .v-stories-block 自己給，
   否則會疊成兩倍。(0,2,1) 勝過 h2 的 (0,0,1)。 */
.v-stories__head {
  display: flex;
  align-items: center;
  gap: var(--space-4);
}
.v-stories__head > h2 {
  margin-block: 0;
}
.v-stories__emblem {
  flex: none;
  width: clamp(2.75rem, 7vw, 4rem);
  height: auto;
  display: block;
}
.v-stories__ring {
  fill: none;
  stroke: var(--section);
  stroke-width: 1.5;
}
.v-stories__core {
  fill: var(--section);
}

.v-stories {
  list-style: none;
  margin: var(--space-7) 0 0;
  padding: 0;
  max-width: var(--container);
  display: grid;
  gap: var(--space-8);
}

/* 手機：圖在上、字在下，單欄。 */
.v-story {
  display: grid;
  gap: var(--space-5);
}

/* 小徽章＝明細。只畫到該層為止，四個由上而下長大（累積正是這個結構的語意：
   每一層都包含前一層）。
   ⚠️ class 名刻意與大徽章的 .v-stories__ring / __core 拉開（多了 -glyph）。
   兩者原本只差幾個字母，改其中一邊的線寬很容易改到另一邊，而畫面只會「幾乎對」，
   截圖看不出來。
   ⚠️ 別改回「四圈都畫、只加粗當期那一圈」：實拍過，四個徽章遠看一模一樣。 */
.v-story__glyph {
  width: 2.25rem;
  height: 2.25rem;
  display: block;
  margin-bottom: var(--space-3);
}
.v-story__glyph-ring {
  fill: none;
  stroke: var(--section);
  stroke-width: 1.5;
}
.v-story__glyph-core {
  fill: var(--section);
}

/* ── 四層的內文（.v-scope__*）────────────────────────────────────────────
   ⚠️ 這一組是**理念頁與首頁共用的**（ScopeStory 與 HomePillars 都掛它）。
   兩頁的排法不同、內文的字級與字距相同，所以只有一份。
   要調某一頁的間距，用那一頁自己的外層 class 加權重（.v-pillar .v-scope__head
   就是這樣做的），不要在這裡改，會同時動到兩頁。 */
.v-scope__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-1) var(--space-3);
  margin: 0;
}
.v-scope__num {
  font-family: var(--font-num);
  font-size: var(--text-base);
  letter-spacing: 0.06em;
  color: var(--section-ink);
}
.v-scope__who {
  font-size: var(--text-lg);
  font-weight: 600;
  letter-spacing: 0.08em;
}
/* 動詞（覺醒／傳承／動員／共存）用寬字距與區塊墨與對象拉開層級，
   不用「｜」分隔號：四語裡那個全形符號的表現不一致，靠顏色與字距分更穩。 */
.v-scope__verb {
  font-size: var(--text-lg);
  font-weight: 600;
  letter-spacing: 0.18em;
  color: var(--section-ink);
}
.v-scope__vision {
  margin: 0.3rem 0 0;
  max-width: var(--measure);
}
/* 2026-09-03：每一層底下的「讀這一篇」。四層與清醒生活三軸共用同一個尺寸級距，
   刻意比願景句小一階 —— 它是出口不是內容，跟正文同級會搶讀。
   ⚠️ 這一組（.v-scope__*）是理念頁與首頁共用的，改這裡兩頁一起動。 */
.v-scope__more {
  margin: var(--space-2) 0 0;
  font-size: var(--text-sm);
}

/* 英文版收掉寬字距。與 base.css 對 h1/h2/h3 的處置同一個理由：
   正向寬字距是 CJK 的語彙（匾額、公文），套到拉丁字上會散開。
   ⚠️ 實拍才看得出來：0.18em 的動詞在英文版渲染成「A w a k e n i n g」，
   中文與日文版同一條規則卻完全正常。動詞仍留一點餘裕（0.04em），
   它要跟左邊的對象在視覺上分開，只是不必分到 CJK 那個程度。 */
html[lang^='en'] .v-scope__who,
html[lang^='en'] .v-aware__q {
  letter-spacing: -0.005em;
}
html[lang^='en'] .v-scope__verb {
  letter-spacing: 0.04em;
}

@media (min-width: 48rem) {
  /* 圖 1.2 : 字 1。圖略寬是刻意的：這一塊的主角是照片，
     而右欄的願景句本來就被 --measure 封在可讀行寬內，多給也用不到。
     ⚠️ 欄寬比例與 ScopeStory.astro 的 sizes 綁在一起，改一邊要改另一邊。 */
  .v-story {
    grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr);
    column-gap: var(--space-7);
    align-items: center;
  }
  /* 偶數列圖文對調。
     ⚠️ 用 grid-column 指定位置、不用 order：DOM 順序永遠是「圖 → 字」，
     讀螢幕與鍵盤的順序四列一致，只有視覺左右換邊。order 會連朗讀順序一起換。

     ⚠️ 欄寬比例也要跟著鏡射，不能只換位置。只換位置的話圖會掉進 1fr 那一欄，
     結果是**四張照片一大一小交替**（1280 上實測 574px／478px）—— 版面看起來
     只是「有點不整齊」，不會有任何錯誤訊息。 */
  .v-story:nth-child(even) {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.2fr);
  }
  .v-story:nth-child(even) > .v-story__media {
    grid-column: 2;
    grid-row: 1;
  }
  .v-story:nth-child(even) > .v-story__body {
    grid-column: 1;
    grid-row: 1;
  }
}

/* ── 清醒生活三軸 ──
   並列、非序列，所以不編號。沿用全站唯一的分節裝置：色脊。
   實心＝這件事是真的在做（斜線的意思是「已預留、尚未填入」，這裡用了會說謊）。 */
.v-aware {
  margin-top: var(--space-7);
}
/* 理念頁的段距整體拉開一階（陽光風格，2026-08-27）。
   ⚠️ AwareLiving 首頁也在用，所以不動 .v-aware 本身，改綁在
   .v-stories-block 之後 —— 那個 class 只有理念頁有，首頁的節奏不受影響。 */
.v-stories-block + .v-aware {
  margin-top: var(--space-8);
}
.v-aware__list {
  list-style: none;
  margin: var(--space-5) 0 0;
  padding: 0;
  display: grid;
  gap: var(--space-4);
}
.v-aware__item {
  border-left: 6px solid var(--section);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  padding: var(--space-2) var(--space-4) var(--space-3);
}
.v-aware__q {
  margin: 0;
  font-size: var(--text-lg);
  font-weight: 600;
  letter-spacing: 0.08em;
}
.v-aware__a {
  margin: 0.3rem 0 0;
  color: var(--ink-muted);
}
/* 2026-09-03 三軸加圖（BI 交件）。圖在上、字在下：Paul 要的是先看到圖，
   不是左右對稱的橫幅。圖不切齊色脊、留著卡片內距，色脊才還讀得出是一條脊。
   .v-aware__media 的比例鎖在 AwareLiving.astro 的 scoped style（那是
   astro:assets 的原尺寸覆寫，跟著元件走）。 */
.v-aware__media + .v-aware__q {
  margin-top: var(--space-3);
}
/* 延伸閱讀。三條軸的連結文字相同，可及名稱靠 .visually-hidden 的軸名區分，
   見 AwareLiving.astro。 */
.v-aware__more {
  margin: var(--space-2) 0 0;
  font-size: var(--text-sm);
}
@media (min-width: 48rem) {
  .v-aware__list {
    grid-template-columns: repeat(3, 1fr);
    gap: var(--space-5);
  }
}

/* ── 首頁最新內容 teaser（HomeLatest.astro，2026-08-28）──
   卡片格線整組沿用 .card-list--media，這裡只補活動一行與 more 連結。
   樣式刻意住在這裡而不是元件的 <style>：全空時元件輸出空字串，
   base.css 的規則沒人匹配也不佔頁面重量，不必把守門搬去 HomeContent。 */
.v-latest__event {
  margin: var(--space-4) 0 0;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-1) var(--space-3);
}
.v-latest__event-label {
  font-size: var(--text-sm);
  font-weight: 700;
  color: var(--ui-primary);
  letter-spacing: 0.06em;
}
.v-latest__event time {
  font-size: var(--text-sm);
  color: var(--ink-muted);
  font-family: var(--font-num);
}
.v-latest__more {
  margin: var(--space-3) 0 0;
  font-size: var(--text-sm);
}

/* ── 首頁願景行動 teaser（HomeActions.astro，2026-09-03）──
   卡片格線整組沿用 .card-list--media，這裡只補入口連結那一行。

   空狀態用的是 .notice（已預留、尚未填入），它自己帶斜線左帶與內距，
   這裡不再加規則 —— 稿子進來之後那個 .notice 會消失，若在這裡替它
   寫了樣式，就會留下一條沒人匹配的規則。 */
.v-actions__more {
  margin: var(--space-3) 0 0;
  font-size: var(--text-sm);
}

/* ── 長文兩欄（文章詳情頁）────────────────────────────────────────
   ══ 為什麼長文頁需要自己的版面 ══

   2026-08-26 理事長拍板「內容寬不封頂」之後，長文頁成為全站唯一一種
   **填不滿版面**的頁型：正文有生理上的行寬上限（--measure），
   所以 1920 的螢幕上，704px 的正文欄右邊會空掉 976px —— 58% 的版面。
   Paul 2026-08-27 回報的就是這個。

   三條路只有一條走得通：
   - 放寬 --measure → 拿可讀性換版面，CJK 一行超過 40 字眼睛回行會找錯行。
   - 把正文置中 → 全站左對齊，置中會變成頁面上唯一一種對齊方式
     （NumberCell.astro 拒絕置中數字卡是同一個理由），而且會跟頁首頁尾的
     品牌／導覽對不齊 —— 那比空白更像壞掉。
   - **右邊放東西** → 就是這裡。

   ⚠️ 這裡沒有在 main 上加 max-width，收窄的是這個區塊自己 ——
   正是 --gutter 那段註解明文背書的作法（「.prose 就是這樣做的」）。

   ⚠️ 斷點 80rem 不是隨手取的：左欄 44rem + 欄距 3rem ＝ 47rem，
   再加兩側 --gutter，低於這個寬度右欄會窄到放不下一張像樣的圖。
   80rem 以下維持單欄，順序照 DOM（正文在側欄之前）。 */
.longread {
  /* 局部收窄閱讀行寬（2026-08-28 視覺診斷）：全域 --measure 44rem 在
     17px 的內文字級下約 41–44 個 CJK 字／行，貼著自家規範（32–40 字）的
     上限之外。長文頁是全站唯一整頁都在讀的地方，收到 40rem（約 38 字）。
     ⚠️ 用 scoped custom property 蓋掉、不動全域 token：其他版位
     （卡片格、入口磚）拿 --measure 當版面寬，不是拿它當行寬。
     上面 80rem 斷點註解的數學（左欄 + 3rem 欄距）在 40rem 下仍成立。 */
  --measure: 40rem;
  display: grid;
  gap: 0;
}
.longread__head h1 {
  margin-bottom: var(--space-1);
}
/* 封面圖在這裡，不在 .prose 裡面，所以 `.prose img` 那條規則管不到它。
   影音頁的封面仍在 .prose 內，兩邊各自有規則、不共用。 */
.longread__media {
  margin: 0 0 var(--space-5);
}
.longread__media .cover {
  display: block;
  width: 100%;
  height: auto;
  border-radius: var(--radius-sm);
}
.longread__side .tags {
  margin-top: 0;
}

@media (min-width: 80rem) {
  .longread {
    grid-template-columns: minmax(0, var(--measure)) minmax(0, 1fr);
    grid-template-areas:
      'crumb crumb'
      'head  side'
      'body  side';
    column-gap: var(--space-7);
  }
  .longread__crumb {
    grid-area: crumb;
  }
  .longread__head {
    grid-area: head;
  }
  .longread__body {
    grid-area: body;
  }
  /* 側欄跨 head + body 兩列，所以圖片頂端與標題頂端齊平 ——
     第一屏因此是「標題＋正文起頭」配「一張大圖」，不是一欄字配一片空白。
     側欄內容（約 900px）比正文（約 2500px）短，下半部仍會留白，
     但那是讀者注意力已經在文字上的位置。
     ⚠️ 不做 sticky：側欄高度接近視窗高，sticky 會 stick 住一個看不完的區塊，
     捲動時下半部永遠露不出來，比不 sticky 更糟。 */
  .longread__side {
    grid-area: side;
  }
}

/* LINE 分享。長成按鈕而不是文字連結：分享是動作不是導覽，
   而且要吃 --tap 的觸控目標下限（協會受眾年齡分布廣，取大的）。
   顏色走 --ui-primary，不用 LINE 的品牌綠 —— 一顆外部品牌色的按鈕
   會在側欄裡比協會自己的東西更搶眼，喧賓奪主。 */
.share {
  margin: var(--space-5) 0 0;
}
.share-line {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--tap);
  padding: 0 var(--space-4);
  border: 1px solid var(--ui-primary);
  border-radius: var(--radius-sm);
  /* 字用 --ui-ink-teal，跟全站連結（上面的 a 規則）同一個理由：
     --ui-primary 疊在頁面的 --paper 底上只有 4.22，不到 4.5。
     邊框不是文字，3:1 就夠，維持 --ui-primary。 */
  color: var(--ui-ink-teal);
  font-size: var(--text-sm);
  /* 全站連結保留底線（WCAG 1.4.1），但這顆已有邊框承擔「可按」的訊號 */
  text-decoration: none;
}
.share-line:hover,
.share-line:focus-visible {
  background: var(--ui-primary);
  color: var(--bg);
}

/* 理念專文詳情頁（/about/<slug>/）的導言。用 frontmatter 的 description，
   與 <meta description> 同一句：那句本來就是「這一篇在講什麼」的一行摘要，
   在頁面上再寫一句不同的，兩句就會各自漂移。 */
.v-principle__lead {
  margin: var(--space-3) 0 0;
  max-width: var(--measure);
  color: var(--ink-muted);
}

/* 理念專文底部的七篇總表沿用 .related 的視覺（同一種「這一區之外還有」的語意）。
   多的只有一條：目前這一篇不加底線、用實墨與粗體標出來。
   ⚠️ 不是只靠顏色與粗細 —— aria-current="page" 本身讀屏軟體讀得到（WCAG 1.4.1）。 */
.related__link[aria-current='page'] {
  color: var(--ink);
  font-weight: 600;
  text-decoration: none;
}

/* 延伸閱讀。卡片式的框在這裡是多餘的 —— 側欄本身已經是一個區域，
   再加框會變成框中框。用一條上緣線分隔，與頁尾、.v-longform 同一個做法。 */
.related {
  margin-top: var(--space-6);
  padding-top: var(--space-4);
  border-top: 1px solid var(--border);
}
.related__heading {
  margin: 0 0 var(--space-3);
  font-size: var(--text-sm);
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--muted);
}
.related__list {
  margin: 0;
  padding: 0;
  list-style: none;
}
.related__item + .related__item {
  margin-top: var(--space-4);
}
.related__link {
  display: block;
  /* 側欄在寬螢幕可能比正文欄還寬，標題一行拉太長讀起來像跑馬燈。
     收在閱讀行寬內，與 .prose 同一個理由。 */
  max-width: var(--measure);
  line-height: 1.5;
}
.related__date {
  display: block;
  margin-top: var(--space-1);
  color: var(--muted);
  font-size: var(--text-xs);
}

/* ── 長文敘事（pages collection 的 about.md）──
   結構層在上、長文在下。中間給一條界線是因為兩者是**不同顆粒度的同一件事**，
   讀者需要知道下面是全文不是新章節。這是全站唯一一條橫線，
   與頁尾同一個做法（1px --border），不擴散到列表分隔。 */
.v-longform {
  margin-top: var(--space-8);
  padding-top: var(--space-6);
  border-top: 1px solid var(--border);
}
/* ⚠️ 內文第一個元素的上方 margin 一律歸零，否則它會**穿過 .prose 溢出來**。
   .prose 自己沒有 padding 也沒有 border，所以裡面第一個 h2 的 margin-top（32px）
   不會留在框內，而是與 .v-longform 的外緣合併、疊在它自己的 padding-top 上。
   /join/ 因此在「會員中心」與「網站會員是什麼」之間空出 154px（2026-08-28 Paul 回報，
   實測 64 margin ＋ 32 padding ＋ 1 border ＋ 32 逃出來的 h2 margin）。
   ⚠️ 症狀是「太空曠」而不是錯誤：三個數字各自都合理，只有加起來才不對，
   所以看程式碼找不到兇手，要量 proseTop === h2Top 才看得出 margin 逃走了。

   ⚠️ 刻意**只綁在 .v-longform 底下**，不寫成全站的 `.prose > :first-child`。
   /privacy/ 與 /terms/（PageContent.astro）靠的正是這個 margin 撐開 h1 與內文的距離
   —— 那裡沒有容器 padding 可以疊，歸零會讓標題黏住第一段。 */
.v-longform .prose > :first-child {
  margin-block-start: 0;
}

/* ══════════════════════════════════════════════════════════════════════
   協會的歌（.v-song）── 音檔 ＋ 歌詞單
   ══════════════════════════════════════════════════════════════════════
   2026-08-28 Paul 交來歌與詞，指定放進理念頁。設計判斷寫在 SongSheet.astro
   檔頭（為什麼沒有色底、為什麼語言標記只出現在轉換處、為什麼用原生播放器），
   這裡只記與**版面 token** 有關的三條。

   ① 色脊用 --section（品牌青，對紙 2.77）標副歌。
      這是 brand.css 登記過的低對比豁免：它只是裝飾幾何，副歌這件事**另有文字承載**
      （旁邊那顆「副歌」標籤，色值 --section-ink 4.83 AA ✓），顏色不是唯一線索。
      ⚠️ 不要把脊的顏色拿去染歌詞本身 —— 那一刻它就變成文字色，豁免不成立。

   ② 歌名用 --font-display（明朝體）。
      字體那段規則寫的是「h3 以下維持黑體，顯示層塞進小標題只會變舊報紙」——
      歌名不是小標題：它在 --text-2xl（1.45–1.8rem），正是那條規則允許的尺度，
      而它是一個**作品的名字**，用顯示層是它本來的語彙。

   ③ 行距 2 比全站的 1.7 更鬆，是刻意的。
      歌詞是一行一句、句與句之間有停頓的東西，用內文行距排會黏成一整塊散文。
      ⚠️ 行內的全形空格（　）是作者的斷句，不要在資料層「清理」掉。 */
.v-song {
  margin-top: var(--space-8);
}

/* 播放器。紙底（--paper）上的白面（--bg），不加外框 —— 與圖片版位同一個判斷：
   兩個底色本來就分得開，全站也沒有第二處用框線圍出區域。 */
.v-song__player {
  margin-top: var(--space-5);
  padding: var(--space-5);
  max-width: var(--measure);
  background: var(--bg);
  border-radius: var(--radius);
}
.v-song__name {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--text-2xl);
  letter-spacing: 0.05em;
  color: var(--ink);
}
/* 署名 ＋ AI 揭露。用 --ink（不是 --ink-muted）：這兩件事是陳述不是附註，
   壓灰等於一邊寫一邊希望沒人讀到。窄螢幕靠 flex-wrap 折行，分隔點跟著前半段走。 */
.v-song__credit {
  display: flex;
  flex-wrap: wrap;
  margin: var(--space-3) 0 0;
  font-size: var(--text-sm);
  color: var(--ink);
}
.v-song__sep {
  margin: 0 var(--space-1);
  color: var(--ink-muted);
}
.v-song__meta {
  margin: var(--space-2) 0 var(--space-4);
  font-size: var(--text-sm);
  color: var(--ink-muted);
}
.v-song__time {
  font-family: var(--font-num);
}
/* 原生 controls 的寬度預設是固定值（各家不同），不撐滿會在卡片裡偏左一小塊。 */
.v-song__audio {
  display: block;
  width: 100%;
}
.v-song__note {
  margin: var(--space-4) 0 0;
  font-size: var(--text-sm);
  color: var(--ink-muted);
}
.v-song__dl {
  margin: var(--space-2) 0 0;
  font-size: var(--text-sm);
}

.v-song__lyrics-title {
  margin-top: var(--space-7);
}
.v-song__sheet {
  margin-top: var(--space-5);
}
/* 標記列：語言轉換與副歌。兩者可能同時出現（第十段既換回華語又是副歌）。 */
.v-song__tags {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin: 0 0 var(--space-2);
}
.v-song__tag {
  font-family: var(--font-num);
  font-size: var(--text-sm);
  letter-spacing: 0.08em;
  color: var(--ink-muted);
}
.v-song__tag--chorus {
  color: var(--section-ink);
  font-weight: 600;
}
.v-song__stanza {
  margin: 0;
  font-size: var(--text-lg);
  line-height: 2;
  color: var(--ink);
}
/* 副歌：字重＋色脊，不加大級數。理念頁「整頁唯一一句放大的話」是核心主張，
   不是這裡（見 .v-claim 那段）。 */
.v-song__stanza.is-chorus {
  padding-left: var(--space-4);
  border-left: 3px solid var(--section);
  font-weight: 600;
}
.v-song__block {
  margin-bottom: var(--space-6);
}
.v-song__block:last-child {
  margin-bottom: 0;
}

/* 兩欄歌詞單。與 .v-creed 那段同一條判斷（內頁在 1440 上容器寬 1238px，
   而歌詞每行只有十來個字，靠左疊成一長條時右邊會空掉六成）。
   ⚠️ 用 columns 不用 grid：columns 的閱讀順序是「左欄由上到下，再跳右欄」，
   那正是歌詞單的讀法；grid 會排成橫向優先，把歌的順序打散。
   ⚠️ break-inside 必須擋住，否則一個段落會被切成兩半、跨欄接續，
   而歌詞被切開時讀者看不出那是同一段。 */
@media (min-width: 64rem) {
  .v-song__sheet {
    columns: 2;
    column-gap: var(--space-8);
  }
  .v-song__block {
    break-inside: avoid;
  }
}

/* ── 加入我們 ──
   ⚠️ 這裡原本寫「刻意沒有按鈕」。2026-08-28 補上了（會員系統已上線，
   前提消失），按鈕的樣式在 CtaLink.astro，不在這裡 —— 那一顆是三個內頁共用的。 */
.v-join {
  margin-top: var(--space-8);
}
.v-join p {
  margin: 0;
  max-width: var(--measure);
  font-size: var(--text-lg);
}

/* ══════════════════════════════════════════════════════════════════════
   圖片版位佔位（<SlotPlaceholder>）
   ══════════════════════════════════════════════════════════════════════
   2026-08-26 追加，配合「全站套 Loftwork 版型、缺圖用空白示意圖替代」。

   ── 為什麼長得跟 .empty 一模一樣 ──
   因為狀態就是同一個：白底（--bg）＝這一格還沒有內容，左緣 45° 斜線＝已預留、
   尚未填入。頁面底是 --paper（#f4f8f7）、版位是 --bg（#ffffff），兩者天然分得開，
   所以不必再給它外框 —— 全站沒有第二個地方用框線圍出區域，這裡也不開這個頭。

   ── 高度從哪裡來 ──
   `aspect-ratio: var(--slot-ratio)`，值由元件用 inline style 餵進來（來源是
   image-slots.ts，那份對應 docs/圖片規格_2026-08-26.md）。比例寫在資料裡不寫在
   CSS 裡，是因為「這一格要交什麼尺寸」是內容決策，不是版面決策。
   ⚠️ 版位內不要用 height: 100% 的子元素 —— 外框高度來自 aspect-ratio，
   百分比高度解析不到（.media-embed 那條註解量過實際數字）。

   ── 顏色紀律 ──
   本段沒有任何裸色碼，也不對 .v-rail__seg / .v-row__spine 設 background，
   符合 .fill-solid / .fill-hatch 那條「後寫的不得蓋掉填色狀態」的不變式。 */
.v-slot {
  position: relative;
  aspect-ratio: var(--slot-ratio, 16 / 9);
  /* 比例算出來的高度可能很矮（2.65:1 在窄螢幕上只有幾十 px），
     給一個下限讓標示還讀得到；一塊讀不出自己是什麼的佔位圖沒有用。 */
  min-height: 4.5rem;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-4) var(--space-4) var(--space-4) calc(var(--space-4) + var(--rail-h));
  background: var(--bg);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  overflow: hidden;
}
.v-slot::before {
  content: '';
  position: absolute;
  inset-block: 0;
  inset-inline-start: 0;
  width: var(--rail-h);
  background-color: var(--section-tint);
  background-image: repeating-linear-gradient(
    45deg,
    var(--section) 0 var(--hatch-w),
    transparent var(--hatch-w) calc(var(--hatch-w) * 2)
  );
}
.v-slot__tag {
  margin: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.35rem;
  text-align: center;
}
.v-slot__name {
  color: var(--ink-muted);
  font-size: var(--text-sm);
  letter-spacing: 0.08em;
}
/* 交件規格用等寬字＋寬字距：跟頁尾的立案字號、「規劃中」標籤同一個做法 ——
   這是要被抄下來交給設計端的資料，不是一句說明。 */
.v-slot__spec {
  color: var(--section-ink);
  font-family: var(--font-num);
  font-size: var(--text-xs);
  letter-spacing: 0.14em;
}

/* ══════════════════════════════════════════════════════════════════════
   商家牆（MerchantWall.astro）
   ══════════════════════════════════════════════════════════════════════
   Loftwork /jp/finding 六格 mosaic 的對標
   （規劃：docs/特約商家呈現規劃_Loftwork對標_2026-08-27.md）。

   ── hover 為什麼不是 Loftwork 那套 ──
   原版是 #FFF100 flood + box-shadow。全站不用 box-shadow，
   Paul 2026-08-27 核可的替代：品牌青 12% 淡染 + 商家名底線。
   淡染用 color-mix 從 token 調，不另立色票（NO-RAW-HEX 那條紀律）。

   ── 卡片連結為什麼沒有整卡底線 ──
   全站「連結保留底線」管的是行文裡的連結（只靠顏色分不出哪些字能點）。
   這裡整張卡是一個連結、格線本身就是可點的訊號，卡內每行字都畫底線
   讀起來像六張卡全在強調。底線在 hover／focus 時回到商家名上，
   鍵盤使用者另有全站的 :focus-visible 外框。 */
.v-merchants {
  margin-block: var(--space-6);
}
.v-merchants__premise {
  color: var(--ink-muted);
  margin-block: 0 var(--space-4);
}
.v-merchants__grid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  /* 58rem 容器下自然落成 3 欄，窄一點 2 欄、手機 1 欄，不另寫斷點 */
  grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
  gap: var(--space-4);
}
.v-merchant__card {
  position: relative;
  display: block;
  color: inherit;
  text-decoration: none;
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0; /* 跟著 .v-slot 的形狀 */
  overflow: hidden;
}
/* 淡染層：::after 蓋整張卡。12% 只罩一層色，覆蓋文字的可讀性不受影響。
   只有 a（有官網的卡）有 hover —— 不能點的卡連淡染都不給，不假裝能點。 */
a.v-merchant__card::after {
  content: '';
  position: absolute;
  inset: 0;
  background: color-mix(in srgb, var(--brand-teal) 12%, transparent);
  opacity: 0;
  pointer-events: none;
}
a.v-merchant__card:hover::after,
a.v-merchant__card:focus-visible::after {
  opacity: 1;
}
a.v-merchant__card:hover .v-merchant__name,
a.v-merchant__card:focus-visible .v-merchant__name {
  text-decoration: underline;
  text-underline-offset: 0.15em;
}
/* 版位在卡裡不需要自己的圓角（卡已經 overflow: hidden 統一裁形） */
.v-merchant__media {
  border-radius: 0;
}
/* 真圖：鎖 4:3（與 pillar 版位同比例，有圖沒圖的卡等高）。
   object-fit: cover 是給非 4:3 來源的保險 —— 劍湖山那張在商家補交原圖前
   是 493×370（低於規格），交件圖其他六張都已裁成正 4:3。 */
img.v-merchant__media {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 4 / 3;
  object-fit: cover;
}
.v-merchant__kind {
  position: absolute;
  /* 版位左緣有一條 --rail-h 寬的斜線脊，分類籤要讓開它 */
  top: var(--space-2);
  inset-inline-start: calc(var(--rail-h) + var(--space-2));
  background: var(--bg);
  color: var(--ui-primary);
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: 0.06em;
  padding: 0.15em 0.7em;
  border-radius: 999px;
}
/* 名稱與所在地：HTML 文字壓在深墨 scrim 上 —— 不是壓在圖檔上，
   所以「圖上不壓字」管的交件圖檔仍然乾淨，四語共用同一張圖。
   scrim 從 --ink 調透明度，白字（--bg）落在最深那一段，
   真圖進來之後對比照樣成立，不必逐張圖挑字色。

   ⚠️ 中間那個 55% 停點是刻意的（2026-08-28 視覺診斷）：兩停點的
   linear-gradient 是等速淡出，名稱的**上緣**（兩行名稱時尤其）落在
   已經快透明的區段，圖亮的地方白字上半截會洗掉。加一個中段停點把
   漸層改成「下半段厚、上半段才收掉」，名稱整行都站在夠深的底上，
   漸層頂端仍然是透明的，不會變成一塊硬邊色板。 */
.v-merchant__scrim {
  position: absolute;
  inset-inline: 0;
  bottom: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  padding: var(--space-6) var(--space-3) var(--space-3) calc(var(--rail-h) + var(--space-3));
  background: linear-gradient(
    to top,
    color-mix(in srgb, var(--ink) 82%, transparent),
    color-mix(in srgb, var(--ink) 62%, transparent) 55%,
    transparent
  );
  color: var(--bg);
}
.v-merchant__name {
  font-size: var(--text-lg);
  font-weight: 700;
  line-height: 1.35;
  text-wrap: balance;
}
.v-merchant__city {
  font-size: var(--text-sm);
}
/* 主打短句：照片正下方的一塊優惠牌，整張卡上唯一一眼看得到的數字。

   ⚠️ 2026-09-13 從「一行青字」改成「淺青底＋左色標的牌子」。第一版（09-12 上線）
   是 --text-lg 的青字：跟圖上的商家名同字級，只比 offers 大 1.2 倍，而且就排在
   同樣是青色圓點的清單上面，讀起來像清單的第一條。Paul 看線上版回報「跟之前沒有不同」
   —— 連核可這個改動的人都分不出來，訪客更不會。所以不是再大一點的問題，是要換成
   另一種東西：一塊有底色的面，字級比商家名高一階（--text-xl）。

   ⚠️ 字色是 --ui-primary-strong，**不是** --ui-primary。第一版用 --ui-primary、
   註解寫「對紙底 4.52:1」是算錯的：4.52 是對白底，本頁的底是 --paper，
   實際只有 4.22，而 17–18px 的粗體還不到 WCAG 的大字門檻（18.66px），
   要 4.5 才算過。--ui-primary-strong 對淺青底 5.33、對紙 5.94，任何字級都過。

   左色標沿用進程尺的 --rail-h 與 .v-gate--lead 的重點磚語彙，用的是品牌青本色
   （裝飾、不承載文字，所以 2.38 的對比不構成問題）。形狀跟卡片一樣左方右圓。
   內文起點是 --rail-h + --space-3，與圖上 scrim 裡的商家名同一條左緣。
   ⚠️ 這一層與 offers 的區分不只靠顏色 —— 底色面、字級、字重、位置都不同（WCAG 1.4.1）。

   09-12 那條 `.v-merchant__headline + .v-merchant__offers`（把清單收近到 --space-1）
   這次刻意拿掉，不要補回來：當時短句是沒有底的一行字，要貼近清單才讀得出是同一組；
   現在牌子自己有底色，清單離底邊只剩 4px 會顯得擠。圖、牌、清單之間一律 --space-2。 */
.v-merchant__headline {
  margin: var(--space-2) 0 0;
  padding: var(--space-2) var(--space-3);
  border-inline-start: var(--rail-h) solid var(--brand-teal);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  background: var(--ui-primary-tint);
  color: var(--ui-primary-strong);
  font-size: var(--text-xl);
  font-weight: 700;
  line-height: 1.35;
  text-wrap: balance;
}
/* 短句的一段（MerchantWall 依「・」「·」切的）。inline-block 讓換行先落在兩項優惠之間；
   一段自己放不下一行時仍會在段內換行，不會溢出。

   ⚠️ 不要拿掉這一層。沒有它，balance 會為了兩行等長斷在詞中間，而且畫面不會報錯：
   09-13 實測四語七家有八句斷壞，例如「泡湯 85 折／起」「15% 引／き」，
   英文則是「·」落到第二行行首。 */
.v-merchant__headline-part {
  display: inline-block;
}
/* 優惠逐條列在卡片下方（摘自各家特約合約）。跟卡片同格、不進 scrim：
   最多四條、是要讀的內容，壓在圖上會跟名稱搶同一塊漸層。
   marker 用 --ui-primary 的小方塊感由預設圓點代替即可，不另造符號。 */
.v-merchant__offers {
  list-style: disc;
  margin: var(--space-2) 0 0;
  padding-inline-start: 1.2em;
  font-size: var(--text-sm);
  display: grid;
  gap: var(--space-1);
}
.v-merchant__offers li::marker {
  color: var(--ui-primary);
}
/* 共通條款一句（不得併用、連假不適用…）：管整面牆，所以在牆尾、不逐卡重複 */
.v-merchants__terms {
  color: var(--ink-muted);
  font-size: var(--text-sm);
  margin-block: var(--space-4) 0;
}
/* 牆尾給訪客的入會指路。與條款同一個字級與灰度：它是牆的註腳、不是行動按鈕
   （頁尾那顆 CtaLink 才是，而且那顆是給商家的）。裡面的連結沿用全域 a 的
   底線與 --ui-ink-teal，不另外造樣式 —— 連結保留底線是全站規則。 */
.v-merchants__join {
  color: var(--ink-muted);
  font-size: var(--text-sm);
  margin-block: var(--space-2) 0;
}

/* ══════════════════════════════════════════════════════════════════════
   推薦區：合作組織的成員名冊（.v-orgs）
   ══════════════════════════════════════════════════════════════════════
   兩區都是照片卡（2026-08-27 Paul 指定：「每多按一層，就會減少一層的觸及」），
   所以區別改由**名字擺哪裡**承擔，不再靠有圖沒圖：

     特約牆  名字壓在圖上的深墨 scrim 裡，卡下方逐條列優惠 → 讀成「權益」
     推薦卡  名字排在圖的下方，卡下方一句介紹、沒有優惠     → 讀成「介紹」

   圖下排字借的是文章列表與合作方案（.v-offer）的形狀，不是新造一種卡。

   ⚠️ 兩者之間留 --space-8（比區塊間常用的 --space-6 大一級）。
   間距在這裡是語意：黏太近會讀成同一份名單的延續，而那正是要避免的誤解。 */
.v-orgs {
  margin-block: var(--space-8) var(--space-6);
}
.v-orgs__lead {
  color: var(--ink-muted);
  max-width: var(--measure);
  margin-block: var(--space-3) 0;
}
.v-org {
  margin-block-start: var(--space-6);
}
.v-org__name {
  font-size: var(--text-lg);
  margin-block: 0;
}
.v-org__relation {
  color: var(--ink-muted);
  font-size: var(--text-sm);
  max-width: var(--measure);
  margin-block: var(--space-1) 0;
}
.v-org__group {
  margin-block-start: var(--space-5);
}
/* 區名是分組標籤，不是標題階層的主角：壓小、壓成青墨、加一條細線收邊。
   細線讓「這一區從哪裡開始」在數百筆之後仍然找得到。 */
.v-org__district {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  font-size: var(--text-sm);
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--ui-ink-teal);
  border-block-end: 1px solid var(--border);
  padding-block-end: var(--space-1);
  margin-block: 0;
}
/* 家數：等寬數字，區名右邊。壓成次要色，讓區名仍然是那一行的主角。 */
.v-org__count {
  font-family: var(--font-num);
  font-variant-numeric: tabular-nums;
  font-weight: 400;
  color: var(--ink-muted);
}
/* auto-fill 而不是固定欄數：一家的區與四十家的區用同一段 CSS，
   窄螢幕自然落成一欄。15rem 與特約牆同一個值 —— 兩種卡會在同一頁上下相鄰，
   欄寬不同的話會讀成版面沒對齊，而不是讀成兩種卡。 */
.v-org__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
  gap: var(--space-4);
  margin-block-start: var(--space-3);
}
.v-member {
  display: flex;
  flex-direction: column;
}
.v-member__card {
  display: block;
  color: inherit;
  text-decoration: none;
}
/* 圖框自己是定位脈絡，淡染層只罩圖、不罩下方的名字與介紹。
   （特約牆的淡染罩整張卡是因為它的字本來就在圖上。） */
.v-member__frame {
  position: relative;
  display: block;
  overflow: hidden;
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0; /* 跟著 .v-slot 的形狀 */
}
.v-member__media {
  border-radius: 0;
}
/* 鎖 4:3（pillar 版位），有圖沒圖的卡等高。
   object-fit: cover 是給非 4:3 來源的保險：照片由各家民宿提供，
   交件尺寸不在我們的控制範圍內。 */
img.v-member__media {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 4 / 3;
  object-fit: cover;
}
/* 只有 a（有連結的卡）有 hover —— 不能點的卡連淡染都不給，不假裝能點。
   全站不用 box-shadow，所以 hover 是品牌青淡染 ＋ 名字底線。 */
a.v-member__card .v-member__frame::after {
  content: '';
  position: absolute;
  inset: 0;
  background: color-mix(in srgb, var(--brand-teal) 12%, transparent);
  opacity: 0;
  pointer-events: none;
}
a.v-member__card:hover .v-member__frame::after,
a.v-member__card:focus-visible .v-member__frame::after {
  opacity: 1;
}
/* min-height 給的是觸控目標（--tap 44px，理由見檔頭的版面尺度那節）。
   圖本身已經遠大於 44px，這一行保的是圖還沒載入時那一瞬間的可點面積。 */
.v-member__name {
  display: flex;
  align-items: center;
  min-height: var(--tap);
  margin-block-start: var(--space-2);
  font-size: var(--text-base);
  font-weight: 700;
  line-height: 1.4;
  text-wrap: balance;
}
a.v-member__card:hover .v-member__name,
a.v-member__card:focus-visible .v-member__name {
  text-decoration: underline;
}
/* 一句介紹：由各家民宿自己寫，我方不代筆。壓成次要色，主角仍是名字與照片。 */
.v-member__intro {
  color: var(--ink-muted);
  font-size: var(--text-sm);
  margin-block: var(--space-1) 0;
}
.v-member__nolink {
  color: var(--ink-muted);
  font-size: var(--text-xs);
  margin-block: var(--space-1) 0;
}

/* ══════════════════════════════════════════════════════════════════════
   全幅掙脫（.v-bleed）
   ══════════════════════════════════════════════════════════════════════
   main 是 58rem 的置中欄，主視覺與橫幅帶要跨過它、貼到視窗兩緣。

   `margin-inline: calc(50% - 50vw)` 的 50% 是「容器寬」的一半、50vw 是「視窗寬」
   的一半，兩者相減正好是容器左緣到視窗左緣的距離 —— 因為容器本來就置中，同一個
   值在兩側都成立，不必知道 main 的 padding 是多少。

   ⚠️ **`body` 的 overflow-x: clip 是這一段的另一半，不可分開。** 100vw 不含捲軸寬，
   桌機上這個盒子會比可視區寬約 15px，不夾住就會多出一條整頁的水平捲軸。
   用 `clip` 不用 `hidden` 是刻意的：`hidden` 會讓 body 變成捲動容器，連帶讓
   position: sticky 失效、並強制另一軸變成 auto。`clip` 只是切掉、不建立捲動容器，
   所以頁首導覽自己的 overflow-x: auto（窄螢幕左右滑那條）完全不受影響。

   ⚠️ 代價要知道：這一行同時會把**別的地方**的水平溢出也一起藏掉，
   `document.documentElement.scrollWidth` 從此不再是可靠的溢出偵測器。
   之後量「這一頁有沒有撐寬」要改量個別元素的 getBoundingClientRect().right
   有沒有超過 innerWidth，別再看文件層的 scrollWidth（會永遠說沒事）。
   ────────────────────────────────────────────────────────────────────── */
body {
  overflow-x: clip;
}

.v-bleed {
  width: 100vw;
  max-width: none;
  margin-inline: calc(50% - 50vw);
}
/* 貼到視窗兩緣的東西不留圓角：圓角是「這是一塊卡片」的訊號，滿版的圖不是卡片。 */
.v-bleed .v-slot {
  border-radius: 0;
}

/* ── 主視覺 ──
   桌機 16:9、手機 9:19.5，**兩張圖不是兩種裁法**（docs/圖片規格_2026-08-26.md）。

   ⚠️ class 是 .v-keyvis 不是 .v-hero。上面 915 行那個 .v-hero 是舊版頁面標頭的
   block（`__title` / `__lead` 已經沒有 markup 在用，但 `__eyebrow` 還活著、
   現在住在 .v-ident 裡），它帶著 `padding-block: var(--space-5) 0`。
   第一版真的取名 .v-hero，結果主視覺頂端無聲多出 24px —— 畫面上只是「好像有點空」，
   看不出是撞名。要合併這兩個名字得連 .v-ident 那邊一起改，不在這次的範圍。

   ⚠️ 桌機／手機的切換不在這個檔案裡，在 HomeHero.astro 的 <picture><source media>。
   佔位期間曾經是「兩個版位都渲染、用 media query 藏掉一個」，那組
   `.v-slot.v-keyvis__desktop` / `__mobile` 規則已於 2026-08-27 隨真圖上線刪除。
   要回到 CSS 切換的話得先解掉它當初被換掉的原因：display: none 會讓兩張 2× 大圖
   都被下載，手機付兩份流量。 */

/* 主視覺排第一時貼齊頁首：抵消 main 的上緣 padding。
   ⚠️ 綁在 :first-child 而不是 .v-keyvis 本身 —— 順序由 HOME_BLOCKS 決定，
   有人把主視覺移到中間時這條要自動失效，否則會在頁面中間吃掉一段間距。 */
.v-home > .v-keyvis:first-child {
  margin-top: calc(-1 * var(--space-7));
}
/* 主視覺與身分聲明是同一個開場，收緊到 space-6（與身分聲明→入口磚同一個處理）。
   ⚠️ 'identity' 2026-08-28 起不在 HOME_BLOCKS 裡（見 home-layout.ts），
   這條目前不匹配任何節點 —— 與 .v-ident + .v-gates 同一個處理：相鄰規則
   本來就是「相鄰時才生效」，identity 回來的那天它自動復工，刻意留著。 */
.v-home > .v-keyvis + .v-ident {
  margin-top: var(--space-6);
}

/* ── 同心四層的服務卡 ──
   桌機兩欄。**不做三欄或四欄**：58rem 分四欄每張只剩約 210px，
   「讓有意識的生活方式，成為一代傳一代的家族基因。」在那個寬度會排成六行以上。
   內文的 class 沿用理念頁的 .v-scope__*（理由見 HomePillars.astro 檔頭）。 */
.v-pillars__list {
  list-style: none;
  margin: var(--space-6) 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-6) var(--space-5);
}
@media (min-width: 48rem) {
  /* ⚠️ 欄寬封在 34rem、不是 1fr（2026-08-26，容器改成流體之後補的）。
     容器不封頂 ＝ 兩欄會各自跟著視窗一路長。1920 上實測一欄 828px、
     卡片內文一行 52 個中文字 —— 超過本檔 --measure（44rem）也超過
     「CJK 一行 32–40 字」那條，眼睛回行會找錯行。
     34rem（544px）是量出來的：一行 34 字落在那個區間，
     而且剛好等於 1920 上文章卡的寬度，整頁只有一種卡片尺度。

     ⚠️ 封的是**這一塊**、不是容器。左緣仍然靠 justify-content: start 對齊 --gutter，
     頁首、卡片牆、頁尾的左邊界不會跟這裡錯開 —— 那正是 --gutter 註解 ① 記的症狀。
     窄視窗下 minmax 會自己縮回去，實測：1440 → 544、1024 → 428、928 → 387、375 → 323。
     （928 上比改流體之前的 428 更窄，因為外距從 16px 變成 7vw＝65px。
     這是流體外距的正常結果，不是縮過頭。） */
  .v-pillars__list {
    grid-template-columns: repeat(2, minmax(0, 34rem));
    justify-content: start;
  }
}
/* 1400 起改成一排四張。
   ⚠️ 這與上面那條「不做四欄」的但書不衝突，因為它算的是**舊的固定容器**：
   58rem 分四欄每張只剩約 210px，那時的結論是對的。容器改成流體之後，
   1400 的內容寬是 1204px，四欄各 286px；1920 是 1680px，四欄各 405px ——
   「讓有意識的生活方式，成為一代傳一代的家族基因。」在 286px 是兩行，不是六行。

   為什麼一定要處理：兩欄封在 34rem，1920 上兩欄用掉 1112px、右邊空 568px，
   而它上下的區塊都是滿版的。Paul 2026-08-27：「我們的願景這一頁面⋯⋯右邊是空的。」
   空的不是留白，是對不齊。

   ⚠️ 為什麼是四欄不是三欄：四層是四項，三欄會落單一張。
   也不用 auto-fit —— 剛好四項時它會在某些寬度自己排成 3＋1。

   ⚠️ 上限也算過：交件圖是 1200×900，四欄最寬 405px，2 倍螢幕要 810px，
   還在原尺寸內、不會放大糊掉。欄再寬就會超過。 */
@media (min-width: 87.5rem) {
  .v-pillars__list {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
}
/* (0,2,0) 蓋過 .v-scope__head 的 margin: 0 —— 那一組在理念頁緊貼徽章，
   在這裡要與上方的圖分開。 */
.v-pillar .v-scope__head {
  margin-top: var(--space-4);
}

/* ── 列表卡上的版位（19:10）──────────────────────────────────────────
   活動與文章列表的卡片頂端。**只有帶圖的清單加 .card-list--media**：
   /join/ 的入口卡沒有圖，硬套多欄會把兩三張導覽卡攤成一排空格。

   ── 為什麼是三欄，而且是寫死的三欄 ──
   單欄清單裡的 19:10 在 58rem 欄寬下每張圖就是 488px 高，20 篇文章疊起來
   將近一萬 px；那不是「圖很大」，是清單失去清單的功能。

   曾經寫成 `repeat(auto-fill, minmax(18rem, 1fr))`，讓欄數自己算。實際落地是
   **兩欄**：main 的左右內距各吃掉 1.5rem，可用寬度剩 880px，三欄需要
   3×288＋2×gap＝896px，差 16px 就掉回兩欄。這種「差一點點」不會報錯，
   只會安靜地少一欄。2026-08-26 理事長看 Loftwork 的三欄後指定要三欄，
   所以欄數改成明講的：手機一欄、40rem 起兩欄、56rem 起三欄。

   ⚠️ 三欄的斷點是量出來的，不是套慣例的「桌機 = 64rem」。
   **斷點要對齊「卡片在這個寬度還讀不讀得動」，不是對齊某個裝置級距。**
   原本寫 64rem（1024px）純粹是照慣例，2026-08-26 理事長回報看到兩欄才發現。

   容器改成流體（見本檔頂端 --gutter）之後，卡寬隨視窗一路長：
     896px → 卡寬 240px　　1024 → 277　　1280 → 351　　1440 → 397　　1920 → 544
   56rem（896px）是底線。實測那一格：卡寬 240px、20 篇標題有 11 篇吃到三行 ——
   已經在可讀的邊緣，但還沒過線（三行標題在中文卡片牆是正常的）。
   再往下就是平板直式寬度，兩欄本來就比較合適，所以底線停在這裡。

   ⚠️ 不要為了讓卡片寬一點去改 --gutter。外距是全站共用的，
   動它會連頁首、頁尾、每一頁的內容左緣一起動。

   ⚠️ 這一段必須排在 .v-slot 後面：兩者同為 (0,1,0)，靠「後寫的贏」覆蓋
   內距與圓角。搬到上面的內容區去會安靜地失效。 */
.card-list--media {
  grid-template-columns: 1fr;
  /* 列距比欄距大。三欄並排時，欄與欄之間有卡片自己的邊界可讀，
     上下兩列之間沒有，靠留白分。 */
  gap: var(--space-7) var(--space-5);
}
@media (min-width: 40rem) {
  .card-list--media {
    grid-template-columns: repeat(2, 1fr);
  }
}
@media (min-width: 56rem) {
  .card-list--media {
    grid-template-columns: repeat(3, 1fr);
  }
}

/* ── 三欄卡片的外框：拿掉，讓圖自己當邊界 ──────────────────────────
   .card 平常有一條 6px 色脊 ＋ 右側圓角，那是單欄清單用來分列的手法
   （見上面「不用橫向分隔線」）。三欄之後分列的工作由格線與留白接手，
   色脊留著會變成三條並排的直線，跟版面的欄位線互相干擾。

   Loftwork 的列表也是這個做法：圖齊邊、文字齊圖的左緣、卡片本身不畫框。
   2026-08-26 理事長指定套用它的版面，這是套用的一部分，不是順手簡化。

   ⚠️ 只在 .card-list--media 底下拿掉。/join/ 的入口卡仍然要色脊 ——
   那幾張卡整頁只有它們，沒有圖也沒有格線，拿掉框就沒有東西告訴讀者那是一張卡。 */
.card-list--media .card {
  border-left: 0;
  border-radius: 0;
}
.card-list--media .card__text {
  /* 文字齊圖的左緣。沒有色脊就不需要替它讓開左內距。 */
  padding: var(--space-3) 0 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
/* 日期提到標題上面。Card.astro 的 DOM 順序是「標題→meta→內文」，
   那個順序對螢幕閱讀器是對的（先聽到這是哪一篇，再聽到什麼時候）。
   視覺上 Loftwork 是標籤在前、標題在後，用 order 換位就好，不必動元件。 */
.card-list--media .card__meta {
  order: -1;
  align-self: start;
  background: var(--section-tint);
  color: var(--section-ink);
  padding: 0.15rem var(--space-2);
  border-radius: var(--radius-sm);
  font-size: var(--text-xs);
}
.card-list--media .card__title {
  margin: 0;
  line-height: 1.45;
}
.card-list--media .card__body {
  margin: 0;
  font-size: var(--text-sm);
}
.card-list--media .card__actions {
  margin: var(--space-2) 0 0;
}
/* 文章卡的標籤列（#永續觀點 #看懂永續）。
   ⚠️ class 刻意不叫 card__tags：card__* 有一份 Astro／React 契約
   （tests/card-contract.test.mjs），新開一個名字會要求活動卡也長出標籤，
   而活動在 D1 裡沒有標籤欄位。 */
.v-tags {
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1) var(--space-3);
  font-size: var(--text-xs);
  color: var(--section-ink);
  letter-spacing: 0.04em;
}

.card__media {
  /* 卡片的圓角只在右側（左邊是色脊）。版位貼齊卡片頂端，
     所以留右上那一顆，右下歸下面的文字區。 */
  border-radius: 0 var(--radius-sm) 0 0;
  /* .v-slot 的內距左邊多留了一條脊的寬度，這裡不需要（見下） */
  padding-inline-start: var(--space-4);
}
/* ⚠️ 版位自己的斜線脊在卡片裡要收掉：.card 已經有一條 6px 實心色脊，
   再並一條 10px 斜線脊（總共 16px、而且只有卡片上半段有）讀起來像渲染壞掉，
   不像兩個狀態。「這一格還沒有內容」在卡片裡由白底講 —— 卡片其餘部分是紙色，
   白色那塊自己就跳出來，加上框裡那行交件規格，訊息完整。
   狀態幾何的三個詞（實心／斜線／白）本來就允許用白色表達「還沒有內容」。 */
.card__media::before {
  content: none;
}

/* 同一個版位換成真圖時（文章列表的封面）。
   刻意用 img.card__media 而不是另開一個 .card__cover：卡片的 class 有一份
   Astro／React 契約（tests/card-contract.test.mjs），新開的 card__ 名字會被
   要求活動卡也跟上，而活動在 D1 裡根本沒有封面欄位。這裡要講的本來就不是
   「另一種版位」，是「這一格這次是張圖」，用選擇器說剛好準確。
   權重 (0,1,1) 高於上面的 .card__media (0,1,0)，覆蓋順序不靠先後。

   ⚠️ 內距要歸零：上面那條 padding-inline-start 是給佔位圖裡的文字留的，
   留在 <img> 上會在圖左邊露出一條紙色，看起來像圖沒對齊。
   高度鎖回同一個 19:10，有圖與缺圖的卡片才排得齊；16:9 多出來的 6%
   由 object-fit 從中央裁掉，不是壓扁。height: auto 是必要的：
   astro:assets 會把原圖的 width/height 寫進標籤，不蓋掉的話瀏覽器
   照 16:9 算高度，圖載入前後版面會跳一次。 */
img.card__media {
  padding-inline-start: 0;
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 19 / 10;
  object-fit: cover;
}

/* ── 全幅橫幅帶在內頁的間距 ────────────────────────────────────────────
   首頁的節奏是 `.v-home > * + *` 那條相鄰規則給的（見上面「分流式首頁」）。
   內頁沒有 .v-home 這層容器，所以同一支 <BleedBanner> 掉進 /about/、
   /merchants/、/join/、/living/ 時**上方會完全沒有間距**，直接黏在前一塊底下。
   間距因此綁在元件自己身上，與 .v-longform／.v-join 同一個寫法。

   ⚠️ 只寫 margin-top、不寫 margin-block。橫幅帶在三個內頁是**最後一塊**
   （長文都還是 draft，正式產物裡它下面沒有東西了），補一個下緣會與 main 自己的
   --space-7 底部內距相加成 7rem 的空白 —— 而那段空白在畫面上讀起來像頁面壞了、
   不像留白。下方要留距離時，由下一塊自己的 margin-top 給（.v-join 就是這樣）。

   ⚠️ 首頁不受影響：這條與 `.v-home > * + *` 同權重 (0,1,0)、寫在後面會贏，
   但值一模一樣，而橫幅帶在 HOME_BLOCKS 裡既不是第一塊也不是最後一塊。
   有人把它移到首頁第一塊時，這條會讓它多出一段上緣 —— 那時候要處理的是
   `.v-home > .v-keyvis:first-child` 那種抵消寫法，不是把這條刪掉。 */
.v-banner {
  margin-top: var(--space-8);
}

/* ── 合作方案（活動與報名頁的 ezfly 路跑優惠）────────────────────────────
   對照組是合作方原始文宣：三欄銷售表、漸層底、驚嘆號、「超值！」。
   協會的 CIS 明確排除緊迫感與大聲的行動呼籲，所以這裡只借它的資訊結構、
   不借它的音量。做法是把整段當成一份規格表來排，而不是當成廣告版位：

   · 版面比照文章列表：一列三格、圖在上、說明文字在照片底下
     （Paul 2026-08-27 看過線上版後指定）。原本是整列一格的兩欄規格表，
     格線與色脊的做法都跟著換掉了，理由寫在下面各段。
   · 價格用 --font-num 等寬數字並開 tabular-nums，三張卡並排時
     **同一列的數字對得起來**。這是整段唯一的視覺重點：讀者真正在比的就是這一欄。
   · 費用內含逐條列出、注意事項不縮寫也不藏起來。文宣把保額限制放在最小的字級，
     我們反過來給它跟其他條目一樣的字級 —— 它是法令來的限制，不是免責裝飾。
   ⚠️ 這一段不出現按鈕。報名在合作方總機，畫面上不該長出可以按的東西。 */
.v-offers {
  margin-top: var(--space-8);
}
.v-offers > h2 {
  margin-block-start: 0; /* (0,2,1) 勝過 h2 的 (0,0,1)，同 .v-block */
}
.v-offers__lead {
  margin: var(--space-3) 0 0;
  max-width: 34rem;
  color: var(--ink-muted);
}

/* ── 格線：與文章列表同一組（Paul 2026-08-27 指定「一行三家」）─────────
   斷點刻意抄 .card-list--media 的那三段（手機一欄、40rem 兩欄、56rem 三欄），
   不另立一套：兩者在視窗變窄時要同時換欄，否則同一個網站會有兩種折法。
   ⚠️ 那組斷點是量出來的，理由在 .card-list--media 的檔頭 ——
   要動之前先讀那一段，不要順手改成慣例的 64rem。

   ⚠️ 這裡不用 .card-list--media 本身，是因為卡片內容不同（沒有連結標題、
   多一份價格表與費用內含）。共用的是**格線的量測結果**，不是卡片的樣式。 */
.v-offers__list {
  display: grid;
  grid-template-columns: 1fr;
  /* 列距比欄距大，同 .card-list--media：欄與欄之間有卡片自己的邊界可讀，
     上下兩列之間沒有，靠留白分。 */
  gap: var(--space-7) var(--space-5);
  margin: var(--space-6) 0 0;
  padding: 0;
  list-style: none;
}
@media (min-width: 40rem) {
  .v-offers__list {
    grid-template-columns: repeat(2, 1fr);
  }
}
@media (min-width: 56rem) {
  .v-offers__list {
    grid-template-columns: repeat(3, 1fr);
  }
}

/* 一張卡＝圖在上、字在下。
   ⚠️ 色脊拿掉了（連同 .v-offer__spine 那個 span）。理由與 .card-list--media
   拿掉 .card 色脊的那一段完全相同：三欄之後分列的工作由格線與留白接手，
   三條並排的直脊會跟版面的欄位線互相干擾。單欄清單才需要色脊分列。

   ⚠️ display 一旦不是 none 就會贏過瀏覽器預設樣式表給 [hidden] 的 display:none，
   搜尋過濾靠的是本檔頂端那條 `[hidden] { display: none !important }`。
   那條規則是這一段能被搜尋濾掉的必要條件（tests/search-contract.test.mjs 鎖著）。 */
.v-offer {
  display: flex;
  flex-direction: column;
}
/* 不是連結，所以沒有 :hover。卡會亮起來就等於在說「這裡可以按」，
   而按下去沒有地方可去。 */

/* 文字齊圖的左緣。沒有色脊就不需要替它讓開左內距，同 .card-list--media。 */
.v-offer__text {
  padding: var(--space-3) 0 0;
}

/* 小標。出發／折扣／報名這類欄位名，一律走這個字級與字距，
   讀起來像表格欄位名而不是像文案。 */
.v-offer__label {
  display: inline-block;
  margin-inline-end: var(--space-3);
  font-size: var(--text-sm);
  letter-spacing: 0.12em;
  color: var(--ink-muted);
}
.v-offer__subhead {
  margin: 0 0 var(--space-2);
  font-size: var(--text-sm);
  letter-spacing: 0.12em;
  color: var(--ink-muted);
}

/* 目的地圖。缺圖時同一個 class 掛在 <SlotPlaceholder> 上，兩者佔一樣的位置，
   所以「圖還沒到」不會讓下面的資訊跳動。比例交給版位規格（19:10）決定，
   這裡只管外距與裁切方式。 */
.v-offer__media {
  margin: 0;
}
.v-offer__media img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 19 / 10;
  object-fit: cover;
  border-radius: var(--radius-sm);
}

.v-offer__when {
  margin: 0;
}
.v-offer__when time {
  font-family: var(--font-num);
  letter-spacing: 0.02em;
}
.v-offer__where {
  margin: var(--space-3) 0 0;
  font-size: var(--text-sm);
  letter-spacing: 0.16em;
  color: var(--section-ink);
}
.v-offer__name {
  margin: var(--space-1) 0 0;
  /* 與文章卡的標題同字級（--text-lg）。三欄之後卡片剩約 280–540px 寬，
     --text-xl 在那個寬度會讓多數賽事名吃到三行，把下面的價格表推出視線外。 */
  font-size: var(--text-lg);
  line-height: 1.45;
}

/* 價格表。dt／dd 併成一列，數字靠右，三場才比得起來。 */
.v-offer__prices {
  display: grid;
  gap: var(--space-2);
  margin: var(--space-5) 0 0;
  /* 原本有 max-width: 22rem，那是整列一格時用來收住價格表的。
     三欄之後卡片本身就是那個限制，再留著會讓價格列的底線比卡片短一截，
     看起來像沒對齊。 */
}
.v-offer__price {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-4);
  /* 組別與價格之間拉一條細線，眼睛才跨得過去。用的是內文墨的極淡版本，
     不是新的顏色（NO-RAW-HEX：色只從 brand.css 來）。 */
  border-bottom: 1px solid var(--border);
  padding-bottom: var(--space-2);
}
.v-offer__price dt {
  color: var(--ink-muted);
  font-size: var(--text-sm);
  letter-spacing: 0.08em;
  /* 觸控面積用的定位脈絡（見下一條）。 */
  position: relative;
}
/* 組別名稱是外連（2026-08-27，理由見 OfferList.astro 檔頭）。
   顏色與底線由全站的 a 規則給，這裡只處理**手指按得到**。

   ⚠️ 不能用 padding 或 min-height 把它撐大：這一列是 baseline 對齊的 flex，
   撐高 dt 會把 baseline 往上推，組別與價格就不再落在同一條線上 ——
   而價格對齊正是這一段唯一的視覺重點。所以擴的是一塊看不見的覆蓋層，
   版面完全不動。

   ⚠️ 上下各 8px 是**算出來的上限**，不是隨手取的：相鄰兩列之間只有
   padding-bottom(--space-2) ＋ 格線 gap(--space-2) ＝ 16px。再擴大一點，
   上下兩條不同連結的感應區就會疊在一起，手指按到哪一條變成看運氣，
   那比面積小更糟。 */
.v-offer__price dt a::after {
  content: '';
  position: absolute;
  inset-block: calc(var(--space-2) * -1);
  inset-inline: calc(var(--space-1) * -1);
}
.v-offer__price dd {
  margin: 0;
  white-space: nowrap;
}
.v-offer__num {
  font-family: var(--font-num);
  font-variant-numeric: tabular-nums;
  font-size: var(--text-lg);
  letter-spacing: 0.01em;
}
.v-offer__from {
  margin-inline-start: var(--space-2);
  font-size: var(--text-xs);
  color: var(--ink-muted);
}

.v-offer__discount {
  margin: var(--space-4) 0 0;
}
.v-offer__deadline {
  display: block;
  margin-top: var(--space-1);
  font-size: var(--text-sm);
  color: var(--ink-muted);
}
.v-offer__deadline time {
  font-family: var(--font-num);
}

.v-offer__detail {
  margin-top: var(--space-5);
}
.v-offer__includes {
  margin: 0 0 var(--space-5);
  padding-inline-start: var(--space-4);
}
.v-offer__includes li + li {
  margin-top: var(--space-2);
}
.v-offer__includes:last-child {
  margin-bottom: 0;
}

/* 注意事項。左邊一條線把它與費用內含分開，字級不縮 ——
   它是法令來的限制，讀者付錢前有權利用一樣的字級讀到它。 */
.v-offer__caution {
  margin: 0;
  padding-inline-start: var(--space-4);
  border-inline-start: 2px solid var(--section);
  color: var(--ink-muted);
}

/* 報名窗口。整段唯一可以互動的東西是那支電話。 */
.v-offers__contact {
  margin-top: var(--space-6);
  padding-top: var(--space-5);
  border-top: 1px solid var(--border);
}
.v-offers__partner {
  margin: 0;
}
.v-offers__partnerName {
  margin-inline-end: var(--space-3);
}
.v-offers__tel {
  font-family: var(--font-num);
  font-size: var(--text-lg);
  letter-spacing: 0.02em;
  white-space: nowrap;
}
.v-offers__agents {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-5);
  margin: var(--space-3) 0 0;
  padding: 0;
  list-style: none;
  color: var(--ink-muted);
}
.v-offers__ext {
  margin-inline-start: var(--space-2);
  font-family: var(--font-num);
  font-variant-numeric: tabular-nums;
  font-size: var(--text-sm);
}
.v-offers__note {
  margin: var(--space-5) 0 0;
  font-size: var(--text-sm);
  color: var(--ink-muted);
}
.v-offers__src {
  display: block;
  margin-top: var(--space-1);
}

/* ══════════════════════════════════════════════════════════════════════
   數字說桃園 —— 數字牆與首頁的今日數字
   ══════════════════════════════════════════════════════════════════════
   2026-08-26 追加。同樣只在本檔**末端追加**，上面既有的規則一行沒動。

   ⚠️ 承本檔的填色不變式：`.v-num__rail` 與 `.v-num__blank` 身上掛的是
   `.fill-solid` / `.fill-hatch`，所以**本段不得對這兩個 class 設 background**。
   本段只給它們尺寸（高度、寬度、圓角），顏色完全交給那兩條規則決定 ——
   在這裡補一句 background 會把「已查證／待填」的視覺差異整個抹平，
   而版面看起來完全正常，是那種要盯很久才會發現的壞法。

   ── 為什麼不照 docx 的社群卡做 ──
   來源文件是 30 張社群圖卡：置中的巨大數字、八個主題各一個顏色。
   兩件事在本站行不通：
   ① **區塊色全站恆定**（2026-08-10 Paul 拿掉分類配色，brand.css 有勿回復的註記）。
      八個主題色會把那個決定整個推翻，而且會讓「主題」看起來比「區塊」更上位。
   ② 全站的閱讀節奏是**左對齊**（進度尺、清單脊、入口磚都是）。
      一整面置中的卡片會讀成另一個網站貼進來的東西。
   所以主題不配色、只當一行小字；差異靠**狀態幾何**承載 ——
   實心 ＝ 已查證，斜線 ＝ 還沒有數字。那是本站既有的語彙，不是為這一頁新發明的。

   ── 為什麼數字用襯線 ──
   沿用 OrgFigures 的處理（--font-display + tabular-nums + --section）：
   等寬數字讓一整面的數字在垂直方向對得齊，明朝體讓它讀起來像被記錄下來的事實，
   而不是廣告上的價格。 */

.v-numbers__lead {
  margin: var(--space-3) 0 0;
  max-width: var(--measure);
  color: var(--ink-muted);
}

/* 圖例。說明實心與斜線各代表什麼 —— 狀態幾何要有文字解釋，
   否則它對第一次來的人只是裝飾（也是 WCAG 1.4.1 的要求：
   不能只用視覺形式傳達資訊）。 */
.v-numbers__legend {
  margin: var(--space-5) 0 0;
  font-size: var(--text-sm);
  color: var(--ink-muted);
}

.v-numbers {
  display: grid;
  gap: var(--space-6) var(--space-5);
  margin: var(--space-5) 0 0;
  padding: 0;
  list-style: none;
}

@media (min-width: 40rem) {
  .v-numbers {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}
@media (min-width: 56rem) {
  .v-numbers {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}
/* 容器不封頂（見 --gutter 的說明），所以寬螢幕真的會一路變寬。
   一格的內容比文章卡短得多，四欄、五欄之後每格仍有約 300px，
   不會把標題擠成一個字一行。 */
@media (min-width: 80rem) {
  .v-numbers {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
}
@media (min-width: 104rem) {
  .v-numbers {
    grid-template-columns: repeat(5, minmax(0, 1fr));
  }
}

/* 一格。flex column ＋ height:100% 讓同一列的橫槓對齊上緣、出處對齊下緣，
   中間的字自己浮動 —— 一整列讀起來才像同一把尺上的刻度，而不是各自為政的卡片。 */
.v-num {
  display: flex;
  flex-direction: column;
  height: 100%;
  min-width: 0;
}

/* 上緣橫槓。與 .v-gate--lead 同厚（--rail-h），兩者是同一種「這一格的狀態」記號。
   ⚠️ 只給高度，background 由 .fill-solid / .fill-hatch 決定，見本段開頭。 */
.v-num__rail {
  display: block;
  height: var(--rail-h);
  border-radius: 2px;
}

.v-num__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-2) var(--space-3);
  margin: var(--space-3) 0 0;
  font-size: var(--text-xs);
  line-height: 1.6;
  color: var(--ink-muted);
}

/* 01–29。等寬字讓序號在多欄之間垂直對齊，比例字的 1 會比 0 窄、整列會左右晃。 */
.v-num__index {
  font-family: var(--font-num);
  letter-spacing: 0.1em;
}

.v-num__theme {
  letter-spacing: 0.06em;
}

/* 狀態標籤（待查／需接即時資料）。把行高壓成 1，讓它連同上下 padding 仍矮於
   一行 --text-xs 文字（12.8 + 3.2 = 16 < 20.48px）。
   ⚠️ 沒有這條的話，帶標籤的那些格子第一行會高 2.1px（1440 實測），
   底下的單位與標題整排跟著往下 —— 同一列裡有數字、有斜線的時候看得出來。
   只在這裡壓，不動 .badge-planned 本體：那個 class 在別處是獨立成行的。 */
.v-num__head .badge-planned {
  line-height: 1;
}

/* 大數字。--font-display ＋ tabular-nums ＋ --section，與 OrgFigures 同一套處理。
   line-height: 1 是必要的：這個字級用 body 的 1.8 會在數字上下各留一大塊空白，
   一整面看起來像行距壞掉。 */
.v-num__value {
  margin: var(--space-2) 0 0;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(2rem, 3.4vw + 0.8rem, 3rem);
  line-height: 1;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
  color: var(--section);
  overflow-wrap: anywhere;
}

/* 還沒有數字的那一格。畫一塊與數字同高的斜線，而不是留白或補「—」：
   留白讀起來像壞掉，「—」讀起來像「這一項是零」。斜線在本站的語彙裡
   明確是「已預留、尚未填入」。 */
/* ⚠️ min-height: 1em 不是保險，是對齊的必要條件：.v-num__value 的高度就是
   字級（line-height: 1），而斜線塊只有 0.75em。少了這一行，待填的那些格子
   底下的標籤與標題會整排往上跑 12px（1440 實測），一面牆看起來像沒對齊。 */
.v-num__value--blank {
  display: flex;
  align-items: center;
  min-height: 1em;
}
.v-num__blank {
  display: block;
  width: 100%;
  max-width: 8rem;
  height: 0.75em;
  border-radius: 2px;
}

.v-num__label {
  margin: var(--space-2) 0 0;
  font-size: var(--text-sm);
  line-height: 1.6;
}
.v-num__period {
  margin-inline-start: var(--space-2);
  font-family: var(--font-num);
  font-variant-numeric: tabular-nums;
  font-size: var(--text-xs);
  color: var(--ink-muted);
}

.v-num__title {
  margin: var(--space-3) 0 0;
  font-size: var(--text-base);
  line-height: 1.55;
}

.v-num__copy {
  margin: var(--space-2) 0 0;
  font-size: var(--text-sm);
  color: var(--ink-muted);
}

/* 撐開剩餘空間的 spacer（理由見 NumberCell.astro）。 */
.v-num__spacer {
  flex: 1 1 auto;
  min-height: var(--space-4);
}

/* 出處。被 spacer 壓到格子底部，所以同一列的出處對齊成一條線。
   ⚠️ 這一行不是可有可無的裝飾：協會網站上的每一個數字都要說得出從哪來，
   schema 的 source 是必填欄位，這裡是它唯一的出口。 */
.v-num__source {
  margin: 0;
  padding-top: var(--space-2);
  border-top: 1px solid var(--border);
  font-size: var(--text-xs);
  line-height: 1.6;
  color: var(--ink-muted);
}
.v-num__source-label {
  margin-inline-end: var(--space-2);
  letter-spacing: 0.06em;
}

/* ── 首頁的今日數字 ── */
.v-numbers-home__title {
  margin: 0;
}
.v-numbers-home__lead {
  margin: var(--space-3) 0 0;
  max-width: var(--measure);
  color: var(--ink-muted);
}
.v-numbers-home__more {
  margin: var(--space-5) 0 0;
}

/* 輪替用的清單。15 張卡全部在 HTML 裡，靠 hidden 只顯示今天那一張。
   隱藏靠檔頭那條全站的 `[hidden] { display: none !important }`——
   .v-numbers 是 grid，沒有那條會 15 張卡全部同時出現。 */

/* 單張時不切多欄 —— 只有一格的 grid 撐滿整個版面寬，
   數字會孤零零地漂在左上角。限制在一個閱讀行寬內。 */
.v-numbers--rotate {
  grid-template-columns: minmax(0, 1fr);
  max-width: var(--measure);
  margin-top: var(--space-6);
}

/* 首頁那一張放大一級：它是這個區塊的主體，不是一整面裡的一格。 */
.v-num--lead .v-num__value {
  font-size: clamp(2.6rem, 6vw + 0.8rem, 4.2rem);
}
.v-num--lead .v-num__title {
  font-size: var(--text-xl);
}
.v-num--lead .v-num__blank {
  max-width: 12rem;
}

/* 首頁那一張的上緣橫槓收成一小段（2026-08-27）。
   牆上的橫槓要維持等格寬 —— 一整列並排才讀得出「同一把尺上的刻度」，
   那是它存在的理由。但首頁只有**一張**卡，等寬的結果是版面上出現一條
   704px 長的實心色條，而全站區塊色只有一個值，那條色條旁邊沒有任何
   對照物，只會讀成線框稿的分隔線（與入口磚同一個病，見 .v-gate 那一段）。
   ⚠️ 尺寸刻意與 .v-gate--lead .v-gate__mark 完全相同：首頁上兩個「重點記號」
   一樣大，才看得出是同一套語彙，而不是兩處各自隨手訂的長度。 */
.v-num--lead .v-num__rail {
  width: 4.5rem;
}

/* 寬螢幕上左右分工：左邊是這一區塊在講什麼，右邊是今天那一張。
   ⚠️ 不是為了「填滿」而分欄。單欄版在 1920 上是一張 704px 的卡靠左、
   右邊 900 多 px 全白，而那片白不屬於任何東西 —— 讀者看到的是版面沒做完，
   不是留白。分成兩欄之後，右邊那片寬度變成卡片自己的欄，白就有了歸屬。
   ⚠️ 卡片仍然封在 --measure 內：出處與內文是句子，跟著欄寬一路變寬會超過
   可讀行寬。欄寬與內容寬是兩件事，這裡刻意讓它們不相等。 */
@media (min-width: 64rem) {
  .v-numbers-home {
    display: grid;
    /* 明確三列（標題／導言／看全部），`-1` 才有東西可以指 ——
       隱式列沒有 -1 這條線，寫了會靜靜地只佔一列。 */
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    /* ⚠️ 最後一列吃掉剩餘高度（`1fr`），不是三列都 auto。
       卡片跨滿三列時，多出來的高度會被**平均分**給 auto 列 ——
       結果是標題與導言之間憑空多出一段縫，看起來像忘了設 margin。
       把餘裕集中到最後一列，上面兩列就緊貼著內容。 */
    grid-template-rows: auto auto 1fr;
    column-gap: var(--space-8);
    align-items: start;
  }
  .v-numbers--rotate {
    grid-column: 2;
    grid-row: 1 / -1;
    max-width: var(--measure);
    margin-top: 0;
  }
}

/* ══════════════════════════════════════════════════════════════════════
   聯絡我們
   ══════════════════════════════════════════════════════════════════════
   2026-08-28 追加（Paul 指定參考 iWIN 的聯絡頁）。同樣只在本檔**末端追加**，
   上面既有的規則一行沒動。版面的取捨記在 components/pages/ContactContent.astro。

   兩欄：資訊欄（左）＋ 標誌面板（右）。
   ⚠️ 用 grid-template-areas 而不是靠 DOM 順序排。DOM 上資訊欄在前、面板在後，
   手機直向堆疊時有用的東西才會在上面；桌機再把面板指派到右欄。
   反過來寫（面板排前面、手機用 order 推下去）在讀屏與鍵盤 Tab 順序上是錯的：
   那兩者跟著 DOM 走，不跟著 order 走。 */
.v-contact__lead {
  max-width: var(--measure);
  color: var(--ink-muted);
  margin-bottom: var(--space-6);
}

.v-contact {
  display: grid;
  grid-template-areas: 'channels' 'mark';
  gap: var(--space-6);
  align-items: start;
}

.v-contact__channels {
  grid-area: channels;
  max-width: var(--measure);
}
.v-contact__channels dl {
  margin: 0;
  display: grid;
  gap: var(--space-5);
}

/* 一項＝一個標題加一到兩行值。用 <dl> 而不是一串 <p>：「諮詢專線 → 03-…」
   本來就是名詞與定義的配對，與頁尾法定揭露同一個判斷。
   每組包一層 div（HTML5 允許 dl > div > dt+dd），沒有這層的話 dt 與 dd 是
   各自獨立的 grid item，會被 gap 拆成看不出配對的兩列。 */
.v-contact__item {
  padding-left: var(--space-4);
  /* 左邊一條細規線代替 iWIN 的圓形圖示。圖示要嘛外連字型、要嘛自己畫五個 SVG，
     而它們在這裡只承擔「這是一組」的功能 —— 一條線就夠，而且不會有掃不出來的
     語意問題（圖示若沒有替代文字，讀屏使用者會聽到一連串空白）。 */
  border-left: 2px solid var(--border);
}
.v-contact__item dt {
  font-size: var(--text-sm);
  font-weight: 600;
  letter-spacing: 0.06em;
  color: var(--ink-muted);
  margin-bottom: var(--space-2);
}
.v-contact__item dd {
  margin: 0;
}
/* 電話與信箱是這一頁真正要被讀到、被點到的東西，字級明顯大於標題。
   等寬字讓電話號碼的數字對齊，也讓它讀起來像可以抄下來的東西。 */
.v-contact__value {
  font-size: var(--text-xl);
  font-family: var(--font-num);
  letter-spacing: 0.02em;
  line-height: 1.4;
}
.v-contact__value a {
  /* 連結一律保留底線（CLAUDE.md 品牌色那節：只靠顏色區分連結對色覺障礙者不友善）。 */
  color: var(--ui-ink-teal);
  min-height: var(--tap);
  display: inline-flex;
  align-items: center;
}
.v-contact__note {
  margin-top: var(--space-2);
  font-size: var(--text-sm);
  color: var(--ink-muted);
  max-width: var(--measure);
}

/* 標誌面板 —— 取代參考頁面的人物插畫（理由見 ContactContent.astro 檔頭）。 */
.v-contact__mark {
  grid-area: mark;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-7) var(--space-5);
  background: var(--brand-teal-tint);
  border-radius: var(--radius-lg);
  text-align: center;
}
.v-contact__mark img {
  width: 140px;
  height: auto;
}
.v-contact__mark p {
  margin: 0;
  font-size: var(--text-sm);
  letter-spacing: 0.12em;
  color: var(--ui-ink-teal);
}

/* ⚠️ `.v-contact__social` 的樣式已於 2026-08-28 一併刪除。那一區與頁尾的社群列重複，
   Paul 指示拿掉（理由寫在 ContactContent.astro 檔尾）。樣式跟著走是刻意的：
   留著沒有人用的規則，下一個人會以為那一區還在、只是暫時沒渲染。 */

/* 兩欄從 52rem 起。門檻不是拍腦袋的：資訊欄要放得下「桃園市中壢區中山東路
   二段 556 號 4 樓」不斷成三行，面板本身固定 140px 標誌＋左右內距約 260px，
   兩欄加間距在 832px 以下會把地址擠成窄柱。手機與平板直接直向堆疊。 */
@media (min-width: 52rem) {
  .v-contact {
    grid-template-areas: 'channels mark';
    grid-template-columns: minmax(0, 1fr) minmax(16rem, 20rem);
    gap: var(--space-8);
  }
}

/* ── 官方 LINE 帳號 ────────────────────────────────────────────────────
   Paul 2026-08-28 指定：ID、加好友網址、QR Code 三樣都要在這一頁。
   排成「文字欄＋QR」兩欄，窄螢幕直向堆疊。

   ⚠️ 刻意不用 LINE 的品牌綠當按鈕底色。一來那是別人的識別色，二來寫進來就是
   一個寫死的 hex（NO-RAW-HEX 會擋），三來 #06C755 對白字只有 2.2:1、根本不合規
   —— 官方素材靠的是綠底配深色 logo，不是綠底白字。這裡用本站的 --ui-primary。 */
.v-contact__line {
  margin-top: var(--space-8);
  padding-top: var(--space-5);
  border-top: 1px solid var(--border);
}
.v-contact__line h2 {
  margin-top: 0;
}
.v-contact__line-body {
  margin-top: var(--space-4);
  display: grid;
  gap: var(--space-5);
  align-items: start;
}

/* ID 是要被「看著抄進 LINE 搜尋框」的東西，所以等寬字、字級放大，
   跟電話號碼同一個處理。 */
.v-contact__line-id {
  margin: var(--space-4) 0 0;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-2) var(--space-3);
}
.v-contact__line-id span {
  font-size: var(--text-sm);
  color: var(--ink-muted);
}
.v-contact__line-id b {
  font-family: var(--font-num);
  font-size: var(--text-xl);
  letter-spacing: 0.02em;
}

.v-contact__line-cta {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--tap);
  margin-top: var(--space-4);
  padding: 0 var(--space-6);
  border-radius: var(--radius);
  background: var(--ui-primary);
  /* 純白字對 --ui-primary 是 4.52，過 AA；--surface 帶一點綠，只有 4.30。 */
  color: var(--bg);
  font-weight: 600;
  /* 實心按鈕，形狀本身已經標示可點，與社群那一區同一個判斷。 */
  text-decoration: none;
}
.v-contact__line-cta:hover,
.v-contact__line-cta:focus-visible {
  background: var(--ui-primary-strong);
  /* 字色要在這裡再寫一次。上面那條只有一個 class，權重 (0,1,0)，
     輸給全域的 a:hover／a:focus-visible (0,1,1)；這條 (0,2,0) 才壓得過。
     少了這行，字會變成 --ui-primary-strong，跟底色同色，按鈕只剩一塊色塊。 */
  color: var(--bg);
}

/* ⚠️ QR 的白底與四個模組的靜區烤在圖檔裡（見 src/config/org.ts）。
   邊框畫在圖的外面、不要用 padding 以外的方式壓進去，也不要加圓角裁切：
   靜區被吃掉就掃不到，而畫面上那張 QR 看起來完全正常。 */
.v-contact__line-qr {
  width: 160px;
  height: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  justify-self: start;
}

@media (min-width: 40rem) {
  .v-contact__line-body {
    grid-template-columns: minmax(0, 1fr) auto;
    gap: var(--space-7);
  }
  .v-contact__line-qr {
    width: 180px;
  }
}
