/* ============================================================
   Mei-Ling Chen — personal site
   Cream paper canvas · Inter (Latin) + Noto Sans TC (漢字)
   One family for 漢字 — hierarchy comes from weight (400 → 700 → 900), not
   from a second face. No 明體 anywhere.
   級數以中文為基準（見 :root 的 --fs-*）：內文 16px、漢字最小 14px、
   全部整數 px、漢字不吃負字距。負字距只給 var(--latin) 的四個元素。
   Signature: the highlighter mark (.mark)
   ============================================================ */

:root {
  /* — ink & paper — */
  --cream:    #f6f2e8;   /* page canvas */
  --sand:     #efede6;   /* recessed sections, ghost-button hover */
  --white:    #ffffff;   /* elevated cards, nav pill */
  --ink:      #2c2e2a;
  /* 次要文字。v2 給的 #80827f 在 Sandstone 上只有 2.85:1，讀不了；
     壓深到這一階後，cream / sandstone / white / teal-tint 四個底色全部 ≥4.5:1。 */
  --stone:    #50554f;
  /* 分行細線。v2 的 Hairline Mist #d5d5d4 是冷灰，在 Sandstone 上只有 1.08:1
     （形同消失），在 cream 上也才 1.31:1。改成跟紙同一個暖色系並壓深一階。 */
  --hairline: #b7b0a2;

  /* — teal: the structural hue, three steps —
     teal      · strokes, borders, dots, small blocks — never large fills
     teal-deep · large fills only (contact band) — white text on top
     teal-tint · the only chromatic reading surface (v3: #dcefe9 → #d3e6df) */
  --teal:      #5cb2a0;
  --teal-deep: #267563;
  --teal-tint: #d3e6df;

  /* — ochre: the action hue (v3) —
     整頁只花在「要讀者動作」的地方，一個畫面最多一次實心填色。這裡是兩處：
     06 的「開始一段對話」實心 CTA、Contact 帶上 email 的底線。
     案例／經歷的 tag 外框刻意留在細線色——tag 不是動作，染黃會把訊號稀釋掉。 */
  --ochre:      #e9c64e;

  /* — type — */
  /* Latin face first (no CJK coverage, so 漢字 fall through), CJK face second,
     generic family last. Never put system-ui / -apple-system ahead of the CJK
     face — PingFang would claim every 漢字 and Noto would never render. */
  --sans:  'Inter', 'Noto Sans TC', 'PingFang TC', 'Heiti TC', 'Microsoft JhengHei', sans-serif;
  --display: var(--sans);
  --latin: 'Inter', 'Helvetica Neue', Arial, sans-serif;

  /* 文字層級：16px 內文、19–21px 項目標題，大字留給首頁與核心提問。
     中文保留非負字距，以行高、字重及群組間距建立閱讀層次。 */
  --fs-micro: .8125rem;
  --fs-meta: .9375rem;
  --fs-sm: 1rem;
  --fs-body: 1.0625rem;
  --fs-eyebrow: 17px;
  --fs-eyebrow-latin: 14px;
  --fs-head: clamp(1.25rem, 1.5vw, 1.375rem);
  --fs-sub: clamp(21px, 1.8vw, 24px);
  --fs-display: clamp(1.5rem, 2.1vw, 1.875rem);
  --fs-hero: clamp(26px, 4.8vw, 60px);
  --fs-mega: clamp(30px, 3.5vw, 44px);

  --lh-body: 2;
  --lh-compact: 1.9;
  --lh-head: 1.5;
  --lh-display: 1.4;
  --lh-hero: 1.4;

  /* 字距：漢字沒有左右側邊留白，負字距會讓相鄰兩字的筆畫直接相黏
     （字重 900 的大標最明顯）。所以漢字一律 0 或正值，負字距只留給
     var(--latin) 的元素——hero 英文、數字、email。 */
  --track-zh:      .015em;
  --track-zh-head: .01em;

  /* — reading rhythm — */
  --measure: 36em;
  --space-paragraph: 1em;
  --space-related: 8px;

  /* — rhythm — */
  --page: 1200px;
  --gutter-w: 178px;
  --gutter-gap: clamp(28px, 4vw, 76px);
  --pad-x: clamp(20px, 5vw, 48px);
  --section-y: clamp(52px, 6vw, 88px);
  --r: 50px;
  --r-sm: 12px;

  /* — motion —
     一條曲線走完全站（沿用 hero 入場那條），長度只有三階：
     tap 是手指按下去的即時回饋、ui 是元件狀態切換、rise 是 scroll reveal。
     位移固定 14px，跟 hero 載入同一個數字——整頁只講一種進場語氣。 */
  --ease:     cubic-bezier(.22,.61,.36,1);
  --dur-tap:  .16s;
  --dur-ui:   .26s;
  --dur-rise: .62s;
  --rise-y:   14px;
}

/* ---------- reset ---------- */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
body, h1, h2, h3, h4, h5, p, ul, ol, dl, dd, figure, blockquote { margin: 0; padding: 0; }
ul, ol { list-style: none; }
img, svg { display: block; max-width: 100%; }
a { color: inherit; }

/* 橫向裁切掛在 <main> 上，不在 <html> 也不在 <body>。
   收斂場（.vfield）刻意破出版心：正常流那一版的插圖用負的 margin-right
   推到紙的外面。不裁的話它會讓整頁多出一條真的可以橫向捲的軸。

   為什麼是 clip 而不是 hidden：hidden 會讓元素變成捲動容器，裡面的
   position: sticky 就改成相對它的 scrollport 計算而不是相對視窗——
   而這一頁有 sticky 的 .eyebrow（每一節的標籤都靠它）。
   clip 只裁不捲，sticky 完全不受影響。

   為什麼掛在 <main>：<main> 是滿版、沒有內距的，所以裁切線正好落在螢幕邊，
   破出去的字被螢幕裁掉、而不是在版心邊被切一刀。掛在 <html> 或 <body> 上
   實測都攔不住文件層級的橫向捲動（Chrome 傳給 viewport 的規則不吃 clip，
   而 body 的 overflow 根本不傳）。 */
main { overflow-x: clip; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }

body {
  background: var(--cream);
  color: var(--ink);
  font-family: var(--sans);
  font-weight: 400;
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  letter-spacing: var(--track-zh);
  /* 讓最後一行不要只剩一兩個漢字 */
  text-wrap: pretty;
  /* CJK / Latin mixing niceties — progressive, ignored where unsupported */
  text-spacing-trim: space-first;
  text-autospace: normal;
  -webkit-font-smoothing: antialiased;
}

p + p { margin-top: var(--space-paragraph); }

.skip {
  position: absolute; left: -9999px;
  background: var(--ink); color: var(--cream);
  padding: 12px 20px; border-radius: var(--r-sm); z-index: 100;
}
.skip:focus { left: 16px; top: 16px; }

:focus-visible {
  outline: 2px solid var(--focus, var(--ink));
  outline-offset: 3px;
  border-radius: 4px;
}

/* ============================================================
   THE SIGNATURE — highlighter mark
   手繪感來自 SVG path：一條等距的正弦波帶，上下緣同相位平移，所以厚度固定、
   只有整條筆觸在抖。這裡的 SVG 只畫「一個週期」，橫向靠 repeat-x 鋪滿，
   而 background-size 的寬度寫成 1em ⇒ 波長恆等於一個漢字，跟著字級縮放，
   不管畫在四個字還是二十個字上，抖動的疏密都一樣。
   波不是「畫進來」的，是一直在流：animation 讓底圖每 --mark-wave 秒橫移
   一個波長。linear ＋ 首尾同高同切線（波峰對波峰）＝ 無縫循環，所以看不到
   起點也沒有折返的頓點。速度刻意壓在 2–5px/s，比眼睛掃字慢一個量級，
   是餘光才會注意到的呼吸，不會把視線從句子上拉走。
   .mark / .mark--b 是同一條波相位差 180°，相鄰兩筆不會長得一樣。
   ============================================================ */
mark.mark {
  --mark-wave: 5s;
  color: inherit;
  background-color: transparent;
  background-image: url("data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 viewBox=%270 0 40 24%27 preserveAspectRatio=%27none%27%3E%3Cpath d=%27M0 9.5C7 9.5 13 1.5 20 1.5S33 9.5 40 9.5L40 22.5C33 22.5 27 14.5 20 14.5S7 22.5 0 22.5Z%27 fill=%27%235cb2a0%27/%3E%3C/svg%3E");
  background-repeat: repeat-x;
  /* 螢光筆畫在文字下緣之外，只當底線用，不覆蓋字身 */
  background-position: 0 100%;
  background-size: 1em .34em;
  animation: mark-wave var(--mark-wave) linear infinite;
  padding-inline: .06em;
  padding-bottom: .16em;
}
mark.mark--b {
  background-image: url("data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 viewBox=%270 0 40 24%27 preserveAspectRatio=%27none%27%3E%3Cpath d=%27M0 1.5C7 1.5 13 9.5 20 9.5S33 1.5 40 1.5L40 14.5C33 14.5 27 22.5 20 22.5S7 14.5 0 14.5Z%27 fill=%27%235cb2a0%27/%3E%3C/svg%3E");
}

/* 同一個 duration 在不同字級上是不同的視覺速度（位移量是 em）。
   字級大的兩處把時間一起放大，讓全站每一筆都維持在 2–5px/s。 */
.hero__fixed mark.mark      { --mark-wave: 14s; }
.vfield__say mark.mark      { --mark-wave: 10s; }

@keyframes mark-wave {
  from { background-position-x: 0; }
  to   { background-position-x: -1em; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    transition-delay: 0s !important;
    scroll-behavior: auto !important;
  }
}

/* ============================================================
   MOTION — scroll reveal
   捲到就上浮 14px 進場，跟 hero 載入同一個位移、同一條曲線：
   整頁只有一種進場語氣，不另外加淡出、縮放、位移方向的變化。
   規則只有兩條——
   ① 一個區塊是一個手勢：段落、引言、卡片各自整塊進場，不逐字逐行拆
   ② 列表交給每一列自己：案例、經歷、文章這種長列表由各列進場，
      同時進到畫面的才錯開（錯開量由 JS 依「這一批有幾個」給，見 main.js），
      慢慢捲的時候一列一列各自進場，不會有假的等待感
   進場一次就不再收回（unobserve）。捲回去不會重播——重播會讓頁面顯得不安分。
   ============================================================ */

/* 初始狀態只在 .js 成立；沒有 JS 就是一頁正常的靜態網站。
   :where() 裡的那一串是「交給列自己進場」的容器，容器本身不進場，
   否則容器與列會疊成兩次動作。
   ⚠️ 一定要用 :where() 不能用 :not(.a):not(.b)——後者每一個 :not() 都會
   累加特異性（九個就是 0,10,0），會壓過下面的 .is-in，元素進場了也不會顯示。
   :where() 的特異性是 0，這一整串維持在 0,2,0，跟 .is-in 同級，
   .is-in 寫在後面所以贏。 */
.js .eyebrow,
.js .col > *:where(:not(.threads, .cases, .cv, .talks, .cards, .posts, .reach-stages, .teaching)),
.js .threads > *,
.js .cases > *,
.js .cv > *,
.js .teaching__header,
.js .teaching__topics > *,
.js .teaching__examples-head,
.js .talks > *,
.js .cards > *,
.js .posts > *,
.js .reach-stages > *,
.js .contact__head,
.js .contact__social,
.js .contact__act > * {
  opacity: 0;
  transform: translateY(var(--rise-y));
  transition: opacity var(--dur-rise) var(--ease),
              transform var(--dur-rise) var(--ease);
}

/* ⚠️ 這條一定要贏過上面所有的初始狀態，否則元素進場了也不會顯示。
   上面每一條都刻意維持在 (0,2,0)：容器一律用 `> *` 而不是 `> li`
   （元素選擇器會多 (0,0,1)），排除清單一律用 :where(:not(…))
   （`:not(.a):not(.b)` 每一個都會累加，九個就是 (0,10,0)）。
   這裡再用 :root 墊到 (0,3,0) 當保險：日後有人加了帶元素名的選擇器，
   進場也不會整段消失。 */
:root.js .is-in {
  opacity: 1;
  transform: none;
}

/* 表單連結同時吃 reveal（opacity/transform）與自己的 hover（線的粗細）。
   上面那條 reveal 的 transition 簡寫特異性比 .contact__mail 高，
   不一起寫回來的話 hover 會沒有補間。 */
.js .contact__act > .contact__mail {
  transition: opacity var(--dur-rise) var(--ease),
              transform var(--dur-rise) var(--ease),
              border-bottom-width var(--dur-ui) var(--ease),
              padding-bottom var(--dur-ui) var(--ease);
}

/* Hero 不吃這套：它有自己的載入動畫，且不在 .col 裡。
   MOMENT 01（.vfield）也不吃：那一塊的出場就是 --p 本身，
   再疊一層 reveal 等於同一件事講兩個手勢。 */

/* ---- 微互動只給真正可以按的東西 ----
   經歷、講座這些列不是連結，所以不給 hover 狀態：
   對不能點的東西回應滑鼠，等於教使用者一個不存在的功能。
   案例列是例外——它是 <summary>，真的可以按，所以有 hover。
   會動的部分全部包在 @media (hover: hover) 裡，觸控裝置點完不會卡住 hover 樣式。 */
.nav__mark { transition: transform var(--dur-ui) var(--ease); }
.btn__dot  { transition: transform var(--dur-ui) var(--ease); }
.contact__social a { transition: background-color var(--dur-ui) ease, color var(--dur-ui) ease, transform var(--dur-ui) var(--ease); }

@media (hover: hover) {
  /* 品牌標是螢光筆的筆頭，hover 時像被拿起來一樣偏一點 */
  .nav__brand:hover .nav__mark { transform: scale(1.08) rotate(-5deg); }
  /* 按鈕右端那顆圓點是「可點性提示」，所以回應手勢的是它，不是整顆按鈕 */
  .btn:hover .btn__dot { transform: scale(1.32); }
  /* 整塊摘要都可按，但回應手勢的是展開提示那一行，不是整塊變色——
     一整列跟著亮會讓人以為自己按到了什麼別的東西。 */
}
/* 按下去要回到平面，手指才有「壓到」的感覺 */
.btn:active { transform: translateY(0); }
.btn:active .btn__dot { transform: scale(.92); }

/* 導覽：捲到哪一節，那一項底下就有一道 teal 短線（JS 上 aria-current）。
   用 ::after 而不是加粗或換底色，字才不會在捲動中跳動。 */
.nav__links a { position: relative; }
.nav__links a::after {
  content: "";
  position: absolute;
  left: 13px; right: 13px; bottom: 5px;
  height: 2px; border-radius: 2px;
  background: var(--teal);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform var(--dur-ui) var(--ease);
}
.nav__links a[aria-current]::after { transform: none; }
/* 桌機版看不到 .nav__links 裡的 Contact，所以由右端的 ghost CTA 接手指示 */
.nav__cta { transition: background-color var(--dur-ui) ease; }
.nav__cta[aria-current] { background: var(--sand); }

/* 漢堡：三條線本來就變成 X，只是沒有補間 */
.nav__bars,
.nav__bars::before,
.nav__bars::after {
  transition: transform var(--dur-ui) var(--ease),
              top var(--dur-ui) var(--ease),
              background-color var(--dur-tap) linear;
}


/* ============================================================
   LAYOUT
   ============================================================ */
.wrap {
  width: 100%;
  max-width: var(--page);
  margin-inline: auto;
  padding-inline: var(--pad-x);
}

.section { padding-block: var(--section-y); }
.section, .contact, .hero, #main, #vfield-label { scroll-margin-top: 104px; }
.section--tint { background: var(--sand); }

.layout {
  display: grid;
  grid-template-columns: var(--gutter-w) minmax(0, 1fr);
  gap: var(--gutter-gap);
}
.gutter { position: relative; }

/* section label — sticky, so you always know where you are */
.eyebrow {
  position: sticky;
  top: 116px;
  font-size: var(--fs-eyebrow);
  line-height: 1.7;
  /* 漢字 500 而不是 600：Noto Sans TC 每個字重都是一支獨立的 face，
     只為這一行小標多載一支不划算。拉丁那半（.eyebrow > span，走 --latin）
     留在 600——Inter 很輕，而全大寫的英文標籤需要那一階才站得住。 */
  font-weight: 500;
  color: var(--ink);
  letter-spacing: var(--track-zh);
}
.eyebrow > span {
  display: block;
  font-family: var(--latin);
  font-size: var(--fs-eyebrow-latin);
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ink);
  padding-bottom: 12px;
  margin-bottom: 10px;
  border-bottom: 1px solid var(--hairline);
}

.col { min-width: 0; max-width: none; }

/* Section copy shares the content column edge with lists and rules. */
.lead,
.col > p,
.threads > li > p,
.case__line,
.cv__what > p,
.care__note,
.post__note,
.contact__body { max-width: 100%; }

/* ============================================================
   CHAPTER TRANSITIONS — 首頁的四個章節
   首頁不是八個等重的 section，是四章：

     01 Worldview      Hero → 我關心什麼
     02 Evidence       精選案例 → 我的經歷（含教學與分享）
     03 Collaboration  什麼情況適合找我 → 服務項目
     04 Ongoing        我的觀點 → Contact

   原本的問題不是哪一節不好看，是「每一個交界都長得一樣」：任何兩節之間都是
   2 × --section-y 的等距留白，底色又照 cream / sand 機械交替，所以捲下去
   讀得出「換了一個區塊」，讀不出「翻了一章」。

   所以章際交界只用兩種東西，而且都不是裝飾：

     → 02  .vfield        一整片滿版的收斂場。五個領域站在同一圈上，讀完之後
                          一起收回正中央那顆圓，插圖再從圓裡破出來。
                          破的是「內容住在 1200px 裡」
     → 03  .section--ink  翻到紙的背面。第三章是全頁唯一一次換人稱
                          （我 → 你），所以也是唯一一次換到 ink 那張紙
     → 04  .turn          只剩一個章節碼的短停頓。第四章是回到原本那張紙，
                          不需要跟離開時一樣用力

   換紙是敘事不是裝飾：

     cream      預設的紙。第一、二、四章都印在上面
     ink        只有 05 那一節。它是「翻到背面」＝敘事主體換成「你」的那一刻，
                所以整頁只能有一次
     sand       第三章落下來的地方（06 服務項目）。ink 是翻頁本身，
                sand 是翻過去之後的那一頁——同一章的兩張紙
     teal-deep  只有 Contact 那一塊。它是「最後一個動作」

   線的三個層級（改任何一條之前先確認自己在哪一級）：
     滿版（不受 --page 限制）   = 章際交界，由整片收斂場與整節 ink 承擔
     版心寬                     = 同一章之內的跨頁（.hero__rule、.section--cont）
     hairline 分行              = 列表的列（.cases、.cv、.talks、.reach-stages…）

   關掉 JS、或 prefers-reduced-motion 之下，四個章節的結構完全不變——
   停頓、紙、章節碼、線都不依賴 motion。
   ============================================================ */

/* 章內交界：上一節維持自己的下留白，下一節收到一半。
   .hero + .section 也算章內——Hero 與「我關心什麼」是同一個開場，
   Hero 尾端那條版心寬的 .hero__rule 已經是它們之間的跨頁分隔。 */
.section + .section,
.hero + .section { padding-top: calc(var(--section-y) * .5); }

/* 章際交界前的最後一節：下留白也收一半，讓停頓幾乎整段落在新的那一章裡。
   讀起來的順序是「紙先換 → 然後才是那段安靜」，不是「先空一大段 → 再換紙」。 */
.section:has(+ .turn),
.section:has(+ .vfield),
.section:has(+ .section--ink) { padding-bottom: calc(var(--section-y) * .5); }

/* 兩種章際交界自己就帶足了間距，所以下一節只補一口氣。 */
.turn + .section { padding-top: 0; }
.vfield + .section { padding-top: calc(var(--section-y) * .55); }

.turn {
  --turn-y: clamp(56px, 7vw, 104px);
  padding-block: var(--turn-y);
}
/* 04：紙換回 cream。回到原本那張紙，不需要跟離開時一樣用力，
   所以第四章的交界是全頁最短的一次停頓。 */
.turn--soft { --turn-y: clamp(40px, 5vw, 74px); }

/* ---- 翻到背面（第三章的開場） ----
   ink #2c2e2a 不是第三個色相，是本來的文字色翻成紙。整頁只能有這一節，
   而且只能是敘事主體從「我」換成「你」的那一節。

   層級色在 .wrap 上一次換算完——這一段之內 --ink 就是 cream，
   --stone 與 --hairline 是 cream 的兩階透明度。所以 .eyebrow、研究階段、
   focus outline 全部沿用原本的規則就自動反白，
   不需要為深底再寫一份。（不能寫在 .section--ink 自己身上：
   那條 background: var(--ink) 會先吃到被改掉的值。） */
.section--ink { background: var(--ink); color: var(--cream); }
.section--ink > .wrap {
  --ink: var(--cream);
  --stone: rgba(246, 242, 232, .72);
  --hairline: rgba(246, 242, 232, .3);
}
/* 換紙的那一節留白給滿：紙的邊自己就是章際交界，不需要額外的停頓帶。 */
.section--ink { padding-block: calc(var(--section-y) * 1.25); }
.section + .section--ink { padding-top: calc(var(--section-y) * 1.25); }
.section--ink + .section { padding-top: var(--section-y); }

/* 同一章的第二節。桌機給一條版心寬的細線當跨頁分隔（比章際那一級弱），
   留白對半分在線的兩側，讓 04 → 05 讀起來是「同一章換一口氣」，
   而不是兩個各自獨立的區塊。 */
.section--cont > .wrap {
  border-top: 1px solid var(--hairline);
  padding-top: calc(var(--section-y) * .5);
}

/* ============================================================
   NAV — floating white pill
   ============================================================ */
.nav-shell {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 50;
  padding: 16px var(--pad-x);
  pointer-events: none;
}
.nav {
  pointer-events: auto;
  width: 100%;
  max-width: var(--page);
  margin-inline: auto;
  background: var(--white);
  border-radius: var(--r);
  padding: 9px 9px 9px 20px;
  display: flex;
  align-items: center;
  gap: 8px;
}
.nav__brand {
  display: flex; align-items: center; gap: 9px;
  text-decoration: none; margin-right: auto;
}
.nav__mark {
  width: 22px; height: 22px; border-radius: 7px;
  background: var(--teal);
  flex: none;
  position: relative;
}
.nav__mark::after {
  content: ""; position: absolute; inset: 7px 5px auto 5px;
  height: 2px; border-radius: 2px; background: var(--ink);
}
.nav__name {
  font-family: var(--latin);
  font-size: var(--fs-sm); font-weight: 500; letter-spacing: -.01em;
  white-space: nowrap;
}
.nav__links { display: flex; align-items: center; gap: 2px; }
.nav__links-cta { display: none; }
.nav__links a {
  display: block;
  padding: 8px 13px;
  font-size: var(--fs-sm);
  font-weight: 500;
  text-decoration: none;
  border-radius: var(--r);
  white-space: nowrap;
  transition: background-color .2s ease;
}
.nav__links a:hover { background: var(--cream); }

.nav__toggle {
  display: none;
  width: 44px; height: 44px; flex: none;
  border-radius: 50%;
  background: var(--teal);
  place-items: center;
}
.nav__bars, .nav__bars::before, .nav__bars::after {
  display: block; width: 15px; height: 1.6px; border-radius: 2px;
  background: var(--ink);
}
.nav__bars { position: relative; }
.nav__bars::before, .nav__bars::after { content: ""; position: absolute; left: 0; }
.nav__bars::before { top: -5px; }
.nav__bars::after  { top: 5px; }
.nav__toggle[aria-expanded="true"] .nav__bars { background: transparent; }
.nav__toggle[aria-expanded="true"] .nav__bars::before { top: 0; transform: rotate(45deg); }
.nav__toggle[aria-expanded="true"] .nav__bars::after  { top: 0; transform: rotate(-45deg); }

/* ============================================================
   BUTTONS
   ============================================================ */
.btn {
  display: inline-flex; align-items: center; gap: 9px;
  padding: 11px 17px 11px 20px;
  border-radius: var(--r);
  font-size: var(--fs-sm); font-weight: 500;
  text-decoration: none;
  white-space: nowrap;
  transition: transform .2s ease, background-color .2s ease;
}
.btn:hover { transform: translateY(-1px); }
.btn__dot {
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--teal); flex: none;
}
.btn__dot--ink { background: var(--ink); }
.btn--ghost { background: var(--cream); color: var(--ink); }
.btn--ghost:hover { background: var(--sand); }
.nav__cta { background: var(--cream); }
/* 全站唯一一顆實心 CTA。ink on ochre = 8.26:1。 */
.btn--ochre { background: var(--ochre); color: var(--ink); }
.btn--ochre:hover { background: #ddb638; }

/* ============================================================
   01 HERO
   ============================================================ */
/* Hero 幾乎佔滿一屏：section 自己是縱向 flex，中間那格（.hero__grid）吃掉剩下的
   高度，底線留在最後一列。min-height 用 svh 而不是 vh——手機工具列收起來時
   vh 會突然變高、把版面往下拉一截。 */
.hero {
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  padding-block: clamp(104px, 9vw, 140px) 0;
}

.hero__grid {
  flex: 1;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(280px, 46%);
  column-gap: clamp(30px, 4vw, 76px);
  row-gap: clamp(32px, 4.5vw, 72px);
  align-items: end;
  /* 兩列的內容比一屏矮的時候，整組往中間收，而不是黏在 padding 下緣。 */
  align-content: center;
}

/* 問句吃掉整幅寬度，第二列才是左邊的自我介紹＋右邊的插圖——
   讓那句大提問先講完話，介紹才接著小聲說。 */
.hero__intro { grid-column: 1 / -1; }
.hero__display { font-weight: 400; }


/* 大提問前面的引子：級數壓到 eyebrow 那一階、字重退回 500、顏色退成 stone，
   才不會跟下面 74px 的問句搶——它是說話前的吸氣，不是第二個標題。 */
.hero__kicker {
  display: block;
  font-size: var(--fs-eyebrow);
  line-height: 1.7;
  font-weight: 500;
  letter-spacing: var(--track-zh);
  color: var(--stone);
  margin-bottom: clamp(8px, .9vw, 14px);
}

/* 兩行同一級、同一重、同一個行高，中間也不加 margin——這樣它讀起來是
   「一句話換行」，不是「大標＋副標」。只有前半段包螢光筆（它是不動的那一半）。 */
.hero__fixed {
  display: block;
  font-family: var(--display);
  font-weight: 900;
  font-size: var(--fs-hero);
  line-height: var(--lh-hero);
  letter-spacing: var(--track-zh-head);
}

/* 六句全寫在 DOM 裡、用 grid 疊在同一格（都是 1 / 1）。
   列高由最高的那一句決定，所以不管換到哪一句、也不管某一句在窄螢幕折成兩行，
   Hero 的高度都是固定的，不靠 JS 量測。不再另外設 max-width——字級本身就是照
   「最長那句要停成一行」算出來的，窄到 420px 以下才折兩行，折了也已經算進列高。 */
.hero__rotator {
  display: grid;
  font-family: var(--display);
  font-weight: 900;
  font-size: var(--fs-hero);
  line-height: var(--lh-hero);
  letter-spacing: var(--track-zh-head);
}

.hero__phrase { display: inline-block; max-width: 100%; vertical-align: top; }

/* 進場跟全站同一個手勢：上浮 14px 淡入。
   退場則是「原地淡出」（.is-leaving 把 transform 歸零）——兩句都在動會變成
   跑馬燈，一進一靜才讀得出「換了一句」而不是「在跑畫面」。 */
.hero__line {
  grid-area: 1 / 1;
  opacity: 0;
  visibility: hidden;
  transform: translateY(var(--rise-y));
  text-wrap: balance;
  transition: opacity var(--dur-rise) var(--ease),
              transform var(--dur-rise) var(--ease),
              visibility 0s linear var(--dur-rise);
}
.hero__line.is-active {
  opacity: 1;
  visibility: visible;
  transform: none;
  transition: opacity var(--dur-rise) var(--ease),
              transform var(--dur-rise) var(--ease),
              visibility 0s;
}
.hero__line.is-leaving {
  opacity: 0;
  visibility: hidden;
  transform: none;
}

/* 沒有 JS 就不輪播，直接顯示第一句（而不是一排透明的字）。 */
html:not(.js) .hero__line { display: none; }
html:not(.js) .hero__line:first-child {
  display: block;
  opacity: 1; visibility: visible; transform: none;
}

/* 減少動態：停止自動換句，拿掉位移。
   （上面全站的 reduce 區塊已經把 transition-duration 歸零，這裡只需要把起點的
   translateY 拿掉，否則它會變成一次瞬間的跳位。） */
@media (prefers-reduced-motion: reduce) {
  .hero__line { transform: none; }
}

/* 首頁短介紹與內文同級；桌面欄寬 390px，減少不必要的斷行。
   align-self: start 讓它貼著第 2 列的頂端開始，而不是跟插圖齊底——大提問
   佔掉上半屏之後，這一段的第一行剛好落在第一屏的中線上（.hero__grid 的
   align-content: center 讓整組居中，所以中線位置跟著視窗高度自己走）。
   原本為了跟插圖齊底而補的 padding-bottom 也就不需要了。 */
.hero__body {
  grid-column: 1;
  align-self: start;
  max-width: 30em;
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  color: var(--stone);
}
/* 自我介紹以項目標題級數與深色字建立開頭層次。 */
.hero__id {
  font-size: var(--fs-head);
  line-height: var(--lh-head);
  font-weight: 500;
  color: var(--ink);
}
/* 頭銜優先整組換行，放大文字後仍可在組內換行。 */
.hero__role { display: inline-block; max-width: 100%; }
.hero__body > p + p { margin-top: 1em; }
/* 桌面貓與介紹齊底；尾巴可超出 SVG，底部分隔線預留 48px。 */
/* 這張手繪的人物是橫幅（784 / 558 ≈ 1.4），不是原本那隻近乎正方的貓，
   所以尺寸改由「高度」決定：寬度取到 21vw 時高度剛好落在 210px 上下，
   跟右欄那三行自我介紹的高度同一階——這一列的高度還是由文字決定，
   換插圖沒有把 Hero 撐高。
   插圖本身朝右（往版面內側跳），不需要像貓那樣鏡像。 */
.hero__figure {
  grid-column: 2;
  justify-self: end;
  display: block;
  width: min(100%, clamp(300px, 36vw, 520px));
  height: auto;
  /* align-items: end 只讓兩欄的盒子齊底，但左欄最後一行字的下方還有 padding
     跟行距。補回這一段，插圖底部才跟那行字收在同一條線上。 */
  margin-bottom: .6em;
}

.hero__rule {
  max-width: var(--page);
  margin: clamp(32px, 4vw, 48px) auto 0;
  height: 1px;
  background: var(--hairline);
}

/* page-load: quiet, one gesture only */
.hero__kicker, .hero__fixed, .hero__rotator, .hero__body, .hero__figure {
  animation: rise .8s cubic-bezier(.22,.61,.36,1) backwards;
}
.hero__kicker  { animation-delay: 0s; }
.hero__fixed   { animation-delay: .06s; }
.hero__rotator { animation-delay: .18s; }
.hero__body    { animation-delay: .3s; }
@keyframes rise {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: none; }
}

/* 插圖排在最後進場（先讀完字，人物才出現），落定後就開始緩慢上下漂浮。
   兩個動畫串在同一條 animation 上：rise 的 .42s 延遲加 .8s 播完，float 才
   在 1.3s 接手；float 的 0% 正好是 translateY(0)，跟 rise 的終點同一個位置，
   所以交棒時不會跳一下。
   幅度只有 10px（比全站進場的 14px 再小一階）、9 秒一趟、ease-in-out 讓上下
   兩端各停一拍——是呼吸，不是彈跳。
   reduce 由全站那條 animation-iteration-count: 1 收掉，這裡不用再寫一次。 */
.hero__figure {
  animation: rise .8s cubic-bezier(.22,.61,.36,1) .42s backwards,
             float 9s ease-in-out 1.3s infinite;
}
@keyframes float {
  from { transform: translateY(0); }
  50%  { transform: translateY(-10px); }
  to   { transform: translateY(0); }
}

/* ============================================================
   SHARED TEXT PIECES
   ============================================================ */
.lead {
  /* 級數和行高都跟內文一樣——引言的身分靠位置（每個 section 的第一段）
     和下方那道 margin 撐，不靠大一號的字。 */
  margin-bottom: clamp(24px, 2.8vw, 36px);
}

.sub {
  font-family: var(--display);
  font-size: var(--fs-sub);
  font-weight: 700;
  line-height: var(--lh-head);
  margin-top: clamp(36px, 4vw, 52px);
  margin-bottom: 16px;
  padding-top: 16px;
  border-top: 1px solid var(--hairline);
}

.tags { display: flex; flex-wrap: wrap; gap: 7px; margin-top: 16px; }
.tags > * {
  font-size: var(--fs-meta);
  line-height: 1.45;
  padding: 5px 12px;
  border: 1px solid var(--hairline);
  border-radius: var(--r);
  color: var(--stone);
  max-width: 100%;
  white-space: normal;
  overflow-wrap: anywhere;
}

.link {
  text-decoration: none;
  border-bottom: 1px solid var(--stone);
  padding-bottom: 1px;
}
.link:hover { border-bottom-color: var(--ink); }

/* ============================================================
   02 CARE
   ============================================================ */
.care__turn { margin-top: 1.6em; color: var(--stone); }

.threads {
  margin-top: clamp(28px, 3vw, 40px);
  border-top: 1px solid var(--hairline);
}
.threads > li {
  padding-block: clamp(20px, 2vw, 26px);
  border-bottom: 1px solid var(--hairline);
}
.threads h3 {
  font-family: var(--display);
  font-size: var(--fs-head);
  font-weight: 700;
  line-height: var(--lh-head);
}
.threads__sub {
  font-family: var(--sans);
  font-size: var(--fs-meta);
  /* 疏排的小標。.09em 在 12px 上是刻意的，放到 14px 會散開，收一階。 */
  letter-spacing: .06em;
  color: var(--stone);
  margin-top: 8px;
  line-height: 1.65;
}
.threads > li > p:last-child { margin-top: 16px; line-height: var(--lh-compact); }

/* 桌面版轉成三直欄。三則關心的事是並列的，不是有先後的清單——
   單欄時橫線把它們讀成順序，並排之後改用直線分隔，語意才對。
   li 用 subgrid 借外層的三條 row：標題不管折成一行還是兩行，
   三欄的小標與內文都會落在同一條基線上。 */
@media (min-width: 1101px) {
  .threads {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    /* h3 / __sub / 內文，三列讓 li 用 subgrid 對齊 */
    grid-template-rows: auto auto auto;
    border-top: 1px solid var(--ink);
    max-width: none;
  }
  /* .col 的 800px 是給單欄長文的行寬上限。三欄並排時每欄只分到它的三分之一，
     這條規則反而在扣寬度，所以 care 這一節放掉容器上限，撐滿 .layout 右欄
     （1200px 頁寬下是 850px）。上方那幾段內文各自有 44em／22em 的行寬上限，
     不會跟著變長；左緣仍與整欄對齊，版面軸線不動。 */
  #care .col { max-width: none; }
  .threads > li {
    display: grid;
    grid-template-rows: subgrid;
    grid-row: span 3;
    border-bottom: 0;
    padding-block: clamp(20px, 2vw, 26px) 0;
    /* 欄間留白收到 20px：直線已經在分欄了，不需要再靠大留白分。
       最後一欄不留右內距，讓文字一路走到版心邊。 */
    padding-right: clamp(12px, 1.6vw, 20px);
  }
  .threads > li:last-child { padding-right: 0; }
  .threads > li + li {
    border-left: 1px solid var(--hairline);
    padding-left: clamp(12px, 1.6vw, 20px);
  }
  /* 44em 是內文的行寬上限，三欄下不會生效，但留著會讓人以為還有第二層限制。 */
  .threads > li > p { max-width: none; }
}

/* ============================================================
   MOMENT 01 · 收斂場（.vfield）
   「走過的領域」與「第一章 → 第二章」是同一件事，所以是同一塊，
   不是一份索引後面再接一個轉場帶。

   同一份資料有兩種狀態，兩種都要能讀：

   ① 正常流（沒有 JS ／ reduced-motion ／ ≤900px ／ 矮視窗）
      左邊一條 hairline 的縱軸串起五站，每一站是「領域圖示 ＋ 領域 ＋ 關鍵字」，
      下面接那一句，再下面是破出右邊界的插圖。
      它本身是完整的設計，不是退化畫面——只是把那一圈拉直。
   ② 桌機 ＋ JS ＋ 允許動態
      同一條軸彎成一整圈，圍著畫面正中央那顆白色的圓。五站依序出現，
      讀完之後一起被帶回圓心、退掉，圓裡的「我曾走過的研究現場」換成那一句，
      然後插圖從圓的右下角浮出來、壓出圓外。

   幾何（見 index.html 的註解與下面 :nth-child 那五行）：
   一切都是 --u 的倍數，--u ＝ min(1cqh, .62cqw)。
     圓   21u ・ 圈 30u ・ 一站的欄寬 32u ・ 插圖 33u 寬
   為什麼要兩個軸一起夾：只看高度，寬螢幕會把左右兩站甩出畫面；只看寬度，
   矮螢幕會把最上與最下那兩塊擠出上下邊。夾住之後整組構圖只是等比縮放。

   五站不平分一圈：四站分在左右兩側（±25° / 155° / 205°），第五站在正下方。
   平分會有一站落在圓的正上方，那一塊得整個掛在圓上面，在 768 高的筆電上
   會頂到浮動的導覽列。現在圓的正上方是一段乾淨的空弧。

   前段時間軸（--story = --p / .46；--p 由 JS 寫入，見 main.js 的 1b）：
     .00 – .30   五站依序進場（每站錯開 .04，各自走 .14）
     .30 – .62   停住，這段是留給讀的
     .62 – .84   五站回到圓心、退掉；那一圈跟著淡出
     .62 – .72   圓裡的提問退場
     .74 – .86   圓裡的那一句進場
     .82 – .96   插圖從圓的右下角浮出來（那一句不動，它一直在圓心）
   桌機完整跑道：.46 – .624 八個需求依序浮現，.624 – .74 停留閱讀；
   .74 – .901 同一組色紙原位轉成策略，.901 – 1 留給策略閱讀。
   窄／矮視窗、無 JS 或減少動態效果時省略需求／策略，只保留結語與插圖。
   ============================================================ */
.vfield { --p: 0; }

/* ---- ① 正常流：拉直的縱軸索引 ---- */
.vfield__stage {
  width: 100%;
  max-width: var(--page);
  margin-inline: auto;
  padding-inline: var(--pad-x);
  padding-block: calc(var(--section-y) * .5) 0;
}
/* 軌道與圓只在桌機的場裡出現；正常流不需要它們（軸就是 .vfield__list::before）。 */
.vfield__orbit,
.vfield__core { display: none; }

.vfield__label {
  font-size: var(--fs-sub);
  font-weight: 700;
  line-height: 1.5;
  letter-spacing: var(--track-zh-head);
  color: var(--ink);
  margin-bottom: clamp(22px, 2vw, 30px);
}

/* 索引與下面那句話共用 --measure，右緣切在同一條線上——它們是同一個敘事。 */
.vfield__list {
  position: relative;
  max-width: var(--measure);
  padding-left: clamp(22px, 2.4vw, 30px);
}
/* 縱軸。頭尾各縮 .9em 讓它停在第一顆與最後一顆圓點上，不會多出兩截。 */
.vfield__list::before {
  content: "";
  position: absolute;
  left: 5px;
  top: .9em;
  bottom: .9em;
  width: 1px;
  background: var(--hairline);
}

.vfield__cluster {
  position: relative;
  padding-block: clamp(11px, 1.3vw, 15px);
  /* 圖示 36px，加上與文字之間的 12px 間距。 */
  padding-left: 48px;
}
.vfield__cluster + .vfield__cluster { border-top: 1px solid var(--hairline); }
/* 軸上的站。cream 的外環讓圓點壓過軸線，不是疊在上面。 */
.vfield__cluster::before {
  content: "";
  position: absolute;
  left: -.5px;
  top: .9em;
  width: 11px;
  height: 11px;
  margin-left: calc(-1 * clamp(22px, 2.4vw, 30px));
  border-radius: 50%;
  background: var(--teal);
  box-shadow: 0 0 0 4px var(--cream);
}
/* 五枚領域圖示共用灰綠描邊、青綠重點與薄荷填色。 */
.vfield__icon {
  position: absolute;
  left: 0;
  top: clamp(11px, 1.3vw, 15px);
  display: block;
  width: 36px;
  height: 36px;
}
.vfield__name {
  font-size: var(--fs-body);
  font-weight: 700;
  line-height: 1.6;
  letter-spacing: var(--track-zh-head);
}
.vfield__keys {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  row-gap: 6px;
  min-width: 0;
  margin-top: 2px;
  font-size: var(--fs-meta);
  line-height: 1.6;
  color: var(--stone);
}
.vfield__keys > span { white-space: nowrap; }
/* 間隔點掛在前一個關鍵字的尾巴（::after 而不是 ::before）：掛在後面那個字的
   頭上時，折行會讓下一行以一顆孤點開頭。 */
.vfield__keys > span:not(:last-child)::after {
  content: "·";
  /* 分隔符是排版裝置不是內容。支援 alt 語法的瀏覽器讓讀屏軟體跳過它。 */
  content: "·" / "";
  color: var(--hairline);
  padding-inline: .9em;
}

/* 沿用 Hero 之外唯一的「宣言」級數：--fs-display / 900。
   它是這一章收束的那一句，不是第三個標題。 */
.vfield__say {
  max-width: var(--measure);
  margin-top: clamp(24px, 2.8vw, 34px);
  font-family: var(--display);
  font-size: var(--fs-display);
  font-weight: 900;
  line-height: 1.6;
  letter-spacing: var(--track-zh-head);
}
/* 兩行是寫死的（見 HTML 的註解）。每一行自己是 block，行內那顆 mark 維持
   inline-block——螢光筆的底圖才只鋪到字尾，不會拉滿整列。 */
.vfield__say-l { display: block; }
.vfield__say-l > mark { display: inline-block; }

/* 正常流的插圖：接在那一句下面，往右破出版心。
   ≤900px 時 stage 就是滿版，負的 margin-right 正好把它推到螢幕邊被裁掉
   （裁切在 main 的 overflow-x: clip）——破框在手機上一樣成立。 */
.vfield__fig {
  width: min(56%, 320px);
  height: auto;
  /* 不往上壓那一句：正常流沒有那顆白圓可以疊，壓上去只會撞到「需求」的字尾。
     這一版的破框是右邊界——負的 margin-right 把它推到紙的外面。 */
  margin: clamp(6px, 1.2vw, 16px) calc(-1 * var(--pad-x)) 0 auto;
}

/* 需求／策略只在大螢幕動畫中顯示，不再提供靜態對照表。 */
.vfield__needs { display: none; list-style: none; }
.vfield__need {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  align-items: center;
  gap: 14px;
  min-width: 0;
  padding: 18px 0;
  border-top: 1px solid var(--hairline);
  color: var(--ink);
  font-family: var(--sans);
  font-size: var(--fs-body);
  font-weight: 400;
  line-height: 1.75;
  text-align: left;
}
.vfield__need:nth-last-child(-n + 2) { border-bottom: 1px solid var(--hairline); }
.vfield__need-source { color: var(--stone); }
.vfield__need-arrow { color: var(--teal-deep); }
.vfield__need-strategy { font-weight: 700; }

/* 正常流以結語與小幅插圖收尾。 */
.vfield__stage {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 180px;
  column-gap: 40px;
  align-items: center;
}
.vfield__label, .vfield__list { grid-column: 1 / -1; }
.vfield__say {
  grid-column: 1;
  grid-row: 3;
  margin: 48px 0 32px;
  font-size: clamp(26px, 3.2vw, 38px);
  line-height: 1.55;
}
.vfield__fig {
  grid-column: 2;
  grid-row: 3;
  justify-self: end;
  width: 156px;
  height: auto;
  margin: 40px 0 24px;
}
@media (max-width: 720px) {
  .vfield__stage { grid-template-columns: minmax(0, 1fr) 100px; column-gap: 16px; }
  .vfield__say { font-size: clamp(22px, 4.3vw, 30px); }
  .vfield__say-l > mark { display: inline; }
  .vfield__fig { width: 100px; }
}
@media (max-width: 420px) {
  .vfield__stage { grid-template-columns: minmax(0, 1fr); }
  .vfield__say { grid-column: 1; grid-row: 3; margin: 36px 0 16px; font-size: 24px; }
  .vfield__fig { grid-column: 1; grid-row: 4; width: 100px; margin: 0 0 24px; }
}

/* ---- ② 桌機的場：滿版、pin 一屏、繞著圓收回去 ----
   --p 只在這一段有意義。stage 用 container-type: size 是為了讓幾何能用
   cqh / cqw：換成 vh 會被捲軸寬度影響，而 % 是相對元素自己、算不出
   「離圓心多遠」。
   ⚠️ container-type: size 的容器一定要給 height（不能只給 min-height），
      否則 Chrome 會把 cqh 回報成 0，整組 clamp 塌成下限、構圖散掉。
   ⚠️ --u 只能在 stage 的「子孫」身上取值。容器單位在容器自己身上會往外
      找上一層容器（這裡沒有 ⇒ 視窗），所以 stage 自己的屬性不准用 --u。
   1200px × 768px 以下改用正常流，避免縮小文字：再矮就放不下「圓 ＋ 上下兩排文字」，那時正常流那一版
   讀起來反而完整。 */
@media (prefers-reduced-motion: no-preference)
   and (min-width: 75em) and (min-height: 48em) {
  .js .vfield {
    position: relative;
    height: 500svh;              /* 延長需求與策略的閱讀時間，前段領域動畫速度維持原樣 */
    --story: clamp(0, calc(var(--p) / .46), 1);
    --strategy: clamp(0, calc((var(--p) - .74) / .10), 1);
    /* 收斂量。五站進完場（.30）之後，停留到 .62 才開始收。 */
    --c: clamp(0, calc((var(--story) - .62) / .22), 1);
    /* 插圖浮出來的量。它同時驅動那一句往上讓位。 */
    --fig: clamp(0, calc((var(--story) - .82) / .14), 1);
  }
  .js .vfield__stage {
    display: block; /* Regular reading layout below this breakpoint uses grid. */
    position: sticky;
    top: 0;
    height: 100svh;
    max-width: none;
    margin: 0;
    padding: 0;
    overflow: hidden;            /* 插圖刻意壓到構圖外緣，超出的部分裁掉 */
    container-type: size;
    /* 構圖單位。高度與寬度一起夾（見上面的註解）。 */
    --u: min(1cqh, .62cqw);
    --core-r: calc(21 * var(--u));
    --arc-r:  calc(30 * var(--u));
    /* 構圖的中心。五站裡只有 04 掛在圓的正下方，整組因此重心偏下；
       把圓往上提 4u 之後上下留白才一樣寬——圓看起來還是在正中央。 */
    --cy: calc(50cqh - 4 * var(--u));
  }

  /* 那一圈：一個完整的細線圓，圓心跟白圓同一點。五站全部站在它外面
     （最近的一站離線 2.6u），所以它讀起來是一條軌道，不是一個框。
     收乾之後幾乎退光——五站都回到圓心了，路徑就不需要留在畫面上。 */
  .js .vfield__orbit {
    display: block;
    position: absolute;
    left: 50%;
    top: var(--cy);
    width: calc(var(--arc-r) * 2);
    height: calc(var(--arc-r) * 2);
    margin: calc(var(--arc-r) * -1) 0 0 calc(var(--arc-r) * -1);
    border: 1px solid var(--hairline);
    border-radius: 50%;
    opacity: calc(1 - .88 * var(--c));
  }

  /* 圓：整頁唯一一次讓白紙變成一個量體，站在畫面正中央。收乾時微微張開
     （1 → 1.05），像是把五站吸進去之後撐大了一點——只有 5%。 */
  .js .vfield__core {
    display: block;
    position: absolute;
    left: 50%;
    top: var(--cy);
    width: calc(var(--core-r) * 2);
    height: calc(var(--core-r) * 2);
    margin: calc(var(--core-r) * -1) 0 0 calc(var(--core-r) * -1);
    border-radius: 50%;
    background: var(--white);
    transform: scale(calc(1 + .05 * var(--c)));
  }

  /* 圓裡的兩個狀態疊在同一格：先是提問，收乾之後換成那一句。
     兩者共用同一組定位，所以是「同一個位置換了內容」，不是「多出一句話」。 */
  .js .vfield__label,
  .js .vfield__say {
    position: absolute;
    left: 50%;
    top: var(--cy);
    width: calc(39 * var(--u));  /* 圓內徑 42u，兩邊各留 1.5u */
    margin: 0;
    max-width: none;
    text-align: center;
  }
  /* 提問借用那一句的級數與字重——同一個圓、同一種聲音，只是內容換了。 */
  .js .vfield__label {
    font-family: var(--display);
    font-size: clamp(21px, calc(4 * var(--u)), 44px);
    font-weight: 700;
    line-height: 1.5;
    letter-spacing: var(--track-zh-head);
    color: var(--ink);
    --out: clamp(0, calc((var(--story) - .62) / .10), 1);
    opacity: calc(1 - var(--out));
    transform: translate(-50%, -50%);
  }
  .js .vfield__say {
    font-size: clamp(19px, calc(3.6 * var(--u)), 36px);
    line-height: 1.55;
    /* 提問完全退掉（.72）之後才開始出現，所以讀起來是「收乾了才剩下這句」，
       不是「一邊收一邊有人在旁邊講話」。 */
    --in: clamp(0, calc((var(--story) - .74) / .12), 1);
    opacity: var(--in);
    /* 它從頭到尾都釘在圓心：這一句就是這一整塊的答案，位置不讓給任何東西。
       讓路的是插圖（見下面 .vfield__fig 的座標）。 */
    transform: translate(-50%, calc(-50% + (1 - var(--in)) * 14px));
  }
  /* 中央文字留在原位，螢光筆與字色把閱讀重心從第一句交給第二句。 */
  .js .vfield__say-l:first-child { --emphasis: calc(1 - var(--strategy)); }
  .js .vfield__say-l:last-child { --emphasis: var(--strategy); }
  .js .vfield__say-l > mark {
    color: color-mix(in srgb, var(--ink) calc(100% * var(--emphasis)), var(--stone));
    background-size: 1em calc(.34em * var(--emphasis));
  }

  /* 插圖：壓在白圓的右下角，右緣與下緣都刻意超出去（破框）。
     身體本身是留空的，白圓的邊線會從它身上穿過去——參考圖就是這樣，
     不是沒對齊。z 序靠 DOM 順序：它排在 core 後面，自然疊在圓上面。 */
  .js .vfield__fig {
    position: absolute;
    /* 座標是從那一句反推的：它有兩行、共 11.2u 高，置中在圓心 ⇒ 字的底線在
       +6u。插圖的框從 +3u 開始，但左側 30% 的範圍要到 +14u 才有筆畫（那裡是
       手臂外緣），所以框雖然疊到字的那一列，畫面上不會碰到字。
       往右移到 +10u 也是同一件事：字最寬的那一行只到 ±18u。 */
    left: calc(50% + 10 * var(--u));
    top: calc(var(--cy) + 3 * var(--u));
    width: calc(33 * var(--u));   /* 高 38u：圓下緣 +21u，它一路長到 +41u */
    height: auto;
    margin: 0;
    opacity: var(--fig);
    /* 進場沿用全站唯一的那個手勢（上浮 14px），只是改由捲動位置驅動。 */
    transform: translateY(calc((1 - var(--fig)) * 14px));
  }

  /* 先左右交錯浮現需求，再於原位淡出需求、淡入策略。
     同一張色紙與同一個位置，grid 疊住兩面並預留文字高度，轉化不跳版。 */
  .js .vfield__needs {
    position: absolute;
    inset: 0;
    display: block;
    max-width: none;
    margin: 0;
    pointer-events: none;
  }
  .js .vfield__core { z-index: 1; }
  .js .vfield__say,
  .js .vfield__fig { z-index: 2; }
  .js .vfield__label,
  .js .vfield__list { z-index: 1; }
  .js .vfield__need {
    --burst: clamp(0, calc((var(--p) - .46 - var(--n) * .012) / .08), 1);
    --turn: clamp(0, calc((var(--p) - .74 - var(--n) * .013) / .07), 1);
    --t: calc(var(--burst) - 1);
    --spread: calc(1 + 2.7 * var(--t) * var(--t) * var(--t) + 1.7 * var(--t) * var(--t));
    position: absolute;
    left: 50%;
    top: var(--cy);
    width: max-content;
    padding: calc(1.4 * var(--u)) calc(2.7 * var(--u));
    font-size: clamp(17px, calc(2.35 * var(--u)), 25px);
    opacity: clamp(0, calc(var(--burst) * 4), 1);
    transform:
      translate(calc(-50% + var(--x) * var(--u) * var(--spread)),
                calc(-50% + var(--y) * var(--u) * var(--spread)))
      scale(calc(.35 + .65 * var(--spread)));
  }
  .js .vfield__need-source,
  .js .vfield__need-strategy {
    grid-area: 1 / 1;
    align-self: center;
  }
  .js .vfield__need-source {
    opacity: clamp(0, calc(1 - var(--turn) / .45), 1);
    transform: translateY(calc(-6px * var(--turn)));
  }
  .js .vfield__need-strategy {
    opacity: clamp(0, calc((var(--turn) - .55) / .45), 1);
    transform: translateY(calc(6px * (1 - var(--turn))));
  }
  /* 箭頭只從畫面隱藏，讀屏仍能循序讀到「需求 → 策略」。 */
  .js .vfield__need-arrow {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }

  .js .vfield__list { position: absolute; inset: 0; max-width: none; padding: 0; }
  .js .vfield__list::before { display: none; }

  /* 一站。left / top 落在那一圈上，再用 --ax / --ay 決定「哪一角貼在那個點」
     （0 ＝ 左／上緣，.5 ＝ 置中，1 ＝ 右／下緣），--gx / --gy 是離開圓心方向
     的那一點間距。所以每一塊都是往外長的，沒有一塊會壓到圈裡面。 */
  .js .vfield__cluster {
    position: absolute;
    left: calc(50% + var(--ox) * var(--u));
    top:  calc(var(--cy) + var(--oy) * var(--u));
    /* 欄寬：再窄一階，五個關鍵字就會斷成三行、行尾還掛著分隔點。 */
    width: calc(36 * var(--u));
    max-width: none;
    padding: 0;
    border: 0;
    text-align: var(--al);
    /* 每一站自己的進場進度：錯開 .04、各自走 .14，最後一站在 .30 完成。 */
    --a: clamp(0, calc((var(--story) - var(--i) * .04) / .14), 1);
    /* 縮放的定點就是它在圈上的那個點，收斂時才是「被吸回圓心」而不是
       「一邊縮一邊漂」。 */
    transform-origin:
      calc(var(--ax) * 100% - var(--gx) * var(--u))
      calc(var(--ay) * 100% - var(--gy) * var(--u));
    /* 三段疊在同一個 transform：錨點 ＋ 進場上浮 ＋ 回到圓心。
       進場與收斂不會重疊，所以可以直接相加。 */
    transform:
      translate(calc(var(--ax) * -100% + var(--gx) * var(--u)
                     - var(--ox) * var(--c) * var(--u)),
                calc(var(--ay) * -100% + var(--gy) * var(--u)
                     - var(--oy) * var(--c) * var(--u)
                     + (1 - var(--a)) * 14px))
      scale(calc(1 - .4 * var(--c)));
    /* 退得比位移快：那一句是 900 字重的 ink，底下不能還有一團讀得出來的小字。 */
    opacity: calc(var(--a) * (1 - min(1, calc(var(--c) * 1.5))));
  }

  /* 五站的座標，順時針：左上 → 右上 → 右下 → 正下 → 左下。
     --ox / --oy 就是 30×cos(θ) / 30×sin(θ)（θ 以三點鐘為 0°、順時針為正）。
     改角度要連 --ox / --oy 一起改，否則點會離開那一圈。

     為什麼四站擠在 ±25° 而不是平分一圈：平分會有一站落在圓的正上方，
     那一塊得整個掛在圓上面，在 768 高的筆電上會頂到浮動的導覽列。
     現在四站分在左右兩側、只有一站在正下方，整組要的高度少掉四分之一，
     圓的正上方剩下一段乾淨的空弧。 */
  .js .vfield__cluster:nth-child(1) {   /* 205°  左上 */
    --i: 0; --ox: -27.19; --oy: -12.68;
    --ax: 1; --ay: .5; --gx: -3.2; --gy: 0; --al: right;
  }
  .js .vfield__cluster:nth-child(2) {   /* 335°  右上 */
    --i: 1; --ox: 27.19; --oy: -12.68;
    --ax: 0; --ay: .5; --gx: 3.2; --gy: 0; --al: left;
  }
  .js .vfield__cluster:nth-child(3) {   /*  25°  右下 */
    --i: 2; --ox: 27.19; --oy: 12.68;
    --ax: 0; --ay: .5; --gx: 3.2; --gy: 0; --al: left;
  }
  .js .vfield__cluster:nth-child(4) {   /*  90°  正下 */
    --i: 3; --ox: 0; --oy: 30;
    --ax: .5; --ay: 0; --gx: 0; --gy: 2.6; --al: center;
  }
  .js .vfield__cluster:nth-child(5) {   /* 155°  左下 */
    --i: 4; --ox: -27.19; --oy: 12.68;
    --ax: 1; --ay: .5; --gx: -3.2; --gy: 0; --al: right;
  }
  /* 右側靠左、左側靠右的那兩塊，關鍵字那一列要跟著同一邊收齊。 */
  .js .vfield__cluster:nth-child(1) .vfield__keys,
  .js .vfield__cluster:nth-child(5) .vfield__keys { justify-content: flex-end; }
  .js .vfield__cluster:nth-child(4) .vfield__keys { justify-content: center; }

  .js .vfield__cluster + .vfield__cluster { border-top: 0; }
  /* 圈上的那顆點：位置就是「錨點反推回去」，所以永遠正好落在圓周上。 */
  .js .vfield__cluster::before {
    left: calc(var(--ax) * 100% - var(--gx) * var(--u));
    top:  calc(var(--ay) * 100% - var(--gy) * var(--u));
    margin: -5.5px 0 0 -5.5px;
  }
  /* 桌機圖示獨立一行，依各站的文字方向靠左、靠右或置中。 */
  .js .vfield__icon {
    position: static;
    transform: none;
    width: clamp(32px, calc(4.4 * var(--u)), 44px);
    height: clamp(32px, calc(4.4 * var(--u)), 44px);
    margin: 0 0 6px;
  }
  .js .vfield__cluster:nth-child(1) .vfield__icon,
  .js .vfield__cluster:nth-child(5) .vfield__icon { margin-left: auto; }
  .js .vfield__cluster:nth-child(4) .vfield__icon { margin-inline: auto; }
  .js .vfield__name { font-size: clamp(1.125rem, calc(2.2 * var(--u)), 1.375rem); }
  .js .vfield__keys { font-size: clamp(.9375rem, calc(1.7 * var(--u)), 1rem); line-height: 1.75; }
  .js .vfield__keys > span:not(:last-child)::after { padding-inline: .55em; }
  /* Restore the original bubble geometry only in the animated desktop scene. */
  .js .vfield__need {
    --paper: var(--teal-tint);
    grid-template-columns: none;
    grid-template-rows: none;
    gap: 0;
    justify-items: center;
    align-content: center;
    max-width: 100%;
    border: 0;
    border-radius: 48% 52% 45% 55% / 55% 43% 57% 45%;
    background: var(--paper);
    font-weight: 500;
    line-height: 1.6;
    text-align: center;
  }
  .js .vfield__need-source { color: var(--ink); }
  .js .vfield__need:nth-child(1) { --n: 0; --x: -32; --y: -30; --paper: #f0d7ce; }
  .js .vfield__need:nth-child(2) { --n: 1; --x: 28; --y: -31; }
  .js .vfield__need:nth-child(3) { --n: 2; --x: -58; --y: -12; }
  .js .vfield__need:nth-child(4) { --n: 3; --x: 57; --y: -14; --paper: #f3d873; }
  .js .vfield__need:nth-child(5) { --n: 4; --x: -51; --y: 9; --paper: #f3d873; }
  .js .vfield__need:nth-child(6) { --n: 5; --x: 57; --y: 7; --paper: #f0d7ce; }
  .js .vfield__need:nth-child(7) { --n: 6; --x: -39; --y: 33; --paper: #e9e2d7; }
  .js .vfield__need:nth-child(8) { --n: 7; --x: 61; --y: 30; }

}

/* ============================================================
   03 SELECTED WORK — paper collection cards + reading dialog
   Native details remains the fallback when dialog is unavailable.
   ============================================================ */
.cases {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  align-items: start;
  width: min(100%, 980px);
  justify-self: center;
  gap: 24px;
  margin-top: 24px;
  list-style: none;
}
.case {
  --case-gap: 24px;
  --card-angle: -1.2deg;
  --card-paper: #efede6;
  scroll-margin-top: 120px;
  perspective: 1400px;
  min-width: 0;
}
.case:nth-child(3) { --card-angle: 1deg; --card-paper: #e5eee7; padding-top: 10px; }
.case:nth-child(4) { --card-angle: -.6deg; --card-paper: #eee8dc; }
.case__head {
  display: flex;
  flex-direction: column;
  position: relative;
  min-height: 380px;
  padding: 24px;
  border: 1px solid var(--hairline);
  border-radius: 10px;
  background: var(--card-paper);
  box-shadow: 0 3px 0 rgba(44, 46, 42, .05), 0 12px 24px -22px rgba(44, 46, 42, .28);
  color: var(--ink);
  cursor: pointer;
  list-style: none;
  transform: rotate(var(--card-angle));
  backface-visibility: hidden;
  transition: transform .32s var(--ease), box-shadow .32s var(--ease), border-color .32s ease;
}
.case__head::-webkit-details-marker { display: none; }
.case__head::before {
  content: '';
  position: absolute;
  inset: 10px;
  border: 1px solid rgba(80, 85, 79, .16);
  border-radius: 4px;
  pointer-events: none;
}
.case__head:focus-visible { outline: 3px solid var(--teal-deep); outline-offset: 6px; }
.case__top { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: baseline; gap: 10px; }
.case__no, .case__topic {
  font-family: var(--latin);
  font-size: 13px;
  font-weight: 500;
  letter-spacing: .12em;
  color: var(--stone);
}
.case__art {
  width: 96px;
  height: 96px;
  margin: 22px auto 20px;
  object-fit: contain;
  transition: transform .4s var(--ease);
}
.case__q {
  font-family: var(--display);
  font-size: clamp(22px, 2vw, 26px);
  font-weight: 700;
  line-height: 1.5;
  letter-spacing: var(--track-zh-head);
  text-wrap: balance;
  margin-bottom: 12px;
}
.case__head .case__line { display: none; }
.case__disc[open] > .case__head .case__line { display: block; font-size: var(--fs-body); margin-bottom: 12px; }
.case__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-top: auto;
  padding-top: 16px;
}
.case__category { font-size: 14px; color: var(--stone); }
.case__more {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 42px;
  width: 42px;
  height: 42px;
  border: 1px solid var(--hairline);
  border-radius: 50%;
  font-size: 26px;
  line-height: 1;
  color: var(--teal-deep);
  transition: transform .32s var(--ease), background .32s ease, border-color .32s ease;
}
@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  .case__head:hover {
    transform: translateY(-9px) rotate(0deg) rotateY(-3deg);
    border-color: var(--teal-deep);
    box-shadow: 0 20px 38px -20px rgba(38, 117, 99, .3);
  }
  .case__head:hover .case__art { transform: translateY(-5px) rotate(5deg); }
  .case__head:hover .case__more { transform: rotate(45deg); background: var(--ochre); border-color: var(--ochre); }
}
.case.is-opening .case__head { transform: rotateY(90deg) scale(.97); }
.case.is-active .case__head { border-color: var(--teal-deep); }
.case__wrap { margin-top: 28px; }
.case__detail { --k: 104px; --case-gap: 24px; }
.case__detail > .tags { margin: 0 0 20px; }

.case-modal {
  /* showModal() places this full-screen overlay in the browser's top layer. */
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  max-width: none;
  max-height: none;
  margin: 0;
  padding: 24px;
  border: 0;
  background: transparent;
  color: var(--ink);
  overflow: hidden;
  perspective: 1800px;
}
.case-modal[open] { display: grid; place-items: center; }
.case-modal::backdrop { background: rgba(27, 37, 30, .6); backdrop-filter: blur(6px); }
.case-modal__panel {
  width: min(880px, 100%);
  max-height: min(860px, calc(100dvh - 64px));
  overflow: auto;
  overscroll-behavior: contain;
  border: 1px solid var(--hairline);
  border-radius: 14px;
  background: var(--cream);
  box-shadow: 0 32px 100px rgba(12, 25, 18, .4);
  backface-visibility: hidden;
  transform-origin: center;
}
.case-modal[open] .case-modal__panel { animation: case-unfold .5s cubic-bezier(.16, .7, .25, 1) both; }
.case-modal[open]::backdrop { animation: case-backdrop .3s ease both; }
.case-modal.is-closing .case-modal__panel { animation: case-fold .18s ease both; }
.case-modal.is-closing { pointer-events: none; }
.case-modal__bar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  position: sticky;
  top: 0;
  z-index: 2;
  gap: 16px;
  padding: 14px 24px;
  background: var(--cream);
  border-bottom: 1px solid var(--hairline);
}
.case-modal__index { font-family: var(--latin); font-size: 13px; letter-spacing: .14em; color: var(--stone); }
.case-modal__close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 48px;
  width: 48px;
  height: 48px;
  padding: 0;
  border: 1px solid var(--hairline);
  border-radius: 50%;
  background: var(--cream);
  font-family: var(--latin);
  font-size: 32px;
  font-weight: 400;
  line-height: 1;
  color: var(--ink);
  cursor: pointer;
}
.case-modal__close:hover { background: var(--teal-tint); }
.case-modal__close:focus-visible { outline: 3px solid var(--teal-deep); outline-offset: 3px; }
.case-modal__body { padding: clamp(24px, 5vw, 56px); }
.case-modal__title { font-size: clamp(26px, 4vw, 38px); line-height: 1.55; margin-bottom: 20px; text-wrap: balance; }
.case-modal__intro { color: var(--stone); margin-bottom: 28px; max-width: var(--measure); }
.case-modal__intro p + p { margin-top: 10px; }
.case-modal .case__wrap { margin-top: 0; }
.case-modal .case__detail { padding: 0; }
.case-modal .beat--turn { background: var(--teal-tint); padding: 22px; border-radius: 4px; }
@keyframes case-unfold {
  from { transform: rotateY(-90deg) scale(.88); }
  to { transform: rotateY(0) scale(1); }
}
@keyframes case-fold { to { opacity: 0; transform: perspective(1400px) rotateY(12deg) scale(.98); } }
@keyframes case-backdrop { from { opacity: 0; } to { opacity: 1; } }
@media (max-width: 1000px) {
  .cases { gap: 20px; }
  .case__head { min-height: 370px; padding: 22px; }
  .case__topic { font-size: 12px; letter-spacing: .06em; }
  .case__art { width: 96px; height: 96px; }
  .case__q { font-size: 22px; }
}
@media (max-width: 760px) {
  .cases { grid-template-columns: minmax(0, 1fr); gap: 22px; width: min(100%, 400px); justify-self: center; margin-top: 12px; }
  .case:nth-child(3) { padding-top: 0; }
  .case__head { min-height: 340px; padding: 24px; }
  .case__art { width: 88px; height: 88px; margin-block: 18px; }
  .case__q { font-size: 24px; }
  .case-modal { padding: 12px; }
  .case-modal__panel { max-height: calc(100dvh - 24px); }
  .case-modal__bar { padding: 10px 18px; }
  .case-modal .beat { grid-template-columns: minmax(0, 1fr); }
  .case-modal .case__quote { padding-left: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .case__head, .case.is-opening .case__head { transform: none; }
  .case-modal[open] .case-modal__panel, .case-modal.is-closing .case-modal__panel { animation: none; }
}

/* The lead case links to a complete report; the remaining cards open dialogs. */
.case--featured { grid-column: 1 / -1; --card-angle: 0deg; --card-paper: #faf7ef; }
.case__head--featured {
  display: grid;
  grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr);
  column-gap: 32px;
  min-height: 300px;
  padding: 28px;
  text-decoration: none;
  border-color: var(--hairline);
}
.case__head--featured .case__top { grid-column: 1 / -1; margin-bottom: 18px; }
.case__featured-label {
  font-family: var(--latin);
  font-size: 13px;
  font-weight: 500;
  letter-spacing: .13em;
  color: var(--teal-deep);
}
.case__featured-copy { grid-column: 1; align-self: center; }
.case__head--featured .case__q { font-size: clamp(28px, 2.7vw, 34px); line-height: 1.45; margin-bottom: 12px; }
.case__featured-desc { max-width: 30em; font-size: var(--fs-body); line-height: 1.85; color: var(--stone); }
.case__head--featured .case__art {
  grid-column: 2;
  grid-row: 2 / 4;
  width: 100%;
  height: auto;
  max-height: 200px;
  margin: 0;
  align-self: center;
}
.case__head--featured .case__foot { grid-column: 1; justify-content: flex-start; gap: 18px; padding-top: 18px; }
.case__report-cta { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 12px; font-size: 16px; font-weight: 700; }
.case__new-tab { font-size: 14px; font-weight: 400; color: var(--stone); }
@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  .case__head--featured:hover { transform: translateY(-6px); }
  .case__head--featured:hover .case__art { transform: translateY(-5px) rotate(1deg); }
  .case__head--featured:hover .case__more { transform: translate(3px, -3px); }
}
@media (max-width: 760px) {
  .case__head--featured { grid-template-columns: minmax(0, 1fr); min-height: 0; gap: 0; padding: 24px; }
  .case__head--featured .case__top { margin-bottom: 18px; }
  .case__head--featured .case__art { grid-column: 1; grid-row: 2; width: 100%; height: auto; max-height: 150px; margin: 0 auto 18px; }
  .case__head--featured .case__q { font-size: clamp(26px, 6.5vw, 30px); }
  .case__head--featured .case__foot { padding-top: 18px; }
}

.case__meta {
  font-size: var(--fs-meta);
  line-height: 1.7;
  color: var(--stone);
  padding-bottom: 14px;
  margin-bottom: clamp(14px, 1.6vw, 20px);
  border-bottom: 1px solid var(--hairline);
}

/* 標題在左、內容在右，跟 About 的年份／職稱同一種兩欄關係，
   欄寬窄一階（104 vs 148），因為這裡已經先縮排過一次。 */
.beat {
  display: grid;
  grid-template-columns: var(--k) minmax(0, 1fr);
  gap: 4px var(--case-gap);
}
.beat + .beat { margin-top: clamp(16px, 1.8vw, 22px); }
.beat__k {
  font-size: var(--fs-meta);
  font-weight: 500;
  color: var(--stone);
  padding-top: .45em;
}
.beat__v { margin: 0; max-width: var(--measure); }
/* 「翻轉」是這一站的重點，所以標題升到 ink 與字重 700，
   而且每一則案例的螢光筆只畫在這一格——一則一筆，不重複。 */
.beat--turn .beat__k { color: var(--ink); font-weight: 700; }

.case__quote {
  margin-top: clamp(22px, 2.4vw, 30px);
  padding-left: calc(var(--k) + var(--case-gap));
}
.case__quote p {
  font-family: var(--display);
  font-size: var(--fs-head);
  font-weight: 500;
  line-height: 1.75;
  letter-spacing: var(--track-zh-head);
  max-width: var(--measure);
}
.case__quote cite {
  display: block;
  margin-top: 8px;
  font-size: var(--fs-meta);
  font-style: normal;
  color: var(--stone);
}


/* Research stories extend the existing collection cards and paper dialog.
   All diagrams use real text in reading order; connector lines are decorative. */
.case--story .case__head { min-height: 0; aspect-ratio: 5 / 7; padding: 22px; }
.case--story .case__top { margin-bottom: 0; }
.case--story .case__art { width: 64px; height: 64px; flex-shrink: 0; margin: 12px auto 16px; }
.case--story .case__topic { font-size: .8125rem; letter-spacing: .07em; }
.case--story .case__q { text-wrap: pretty; }
.case--story .case__head .case__line {
  display: block;
  font-size: 15px;
  line-height: 1.9;
  color: var(--stone);
  margin-bottom: 8px;
}
.case--story .case__line br { display: none; }
.case--story .case__foot { align-items: flex-end; gap: 8px; padding-top: 8px; }
.case--story .tags { margin: 0; gap: 6px; }
.case--story .tags > span { font-size: 13px; padding: 3px 8px; }
.case--story .case__more { flex-basis: 36px; width: 36px; height: 36px; font-size: 22px; }
.research-story { font-size: var(--fs-body); line-height: 1.95; }
.research-story p { max-width: var(--measure); }
.story__opening > p + p, .story__reframe > p + p, .story__findings > p + p { margin-top: 1em; }
.story__prelude, .story__label { font-size: var(--fs-meta); font-weight: 500; color: var(--stone); }
.story__opening .story__assumption {
  border-left: 1px solid var(--hairline);
  padding: 8px 0 8px 22px;
  margin-block: 16px 28px;
  font-size: clamp(1.125rem, 2vw, 1.375rem);
  line-height: 1.85;
  color: var(--stone);
  max-width: 28em;
}
.story__figure { margin-block: 40px 48px; padding-block: 24px; border-block: 1px solid var(--hairline); }
.story__figure > figcaption { margin-bottom: 28px; }
.story__figure h4 { font-size: var(--fs-head); font-weight: 700; line-height: 1.6; }
.story__reframe { position: relative; padding-top: 24px; margin-top: 40px; }
.story__reframe::before { content: ''; position: absolute; top: 0; left: 0; width: 48px; height: 3px; background: var(--teal); }
.story__reframe h3 { font-size: clamp(1.375rem, 2.5vw, 1.75rem); line-height: 1.65; margin-bottom: 22px; text-wrap: pretty; }
.story__questions { margin-block: 24px; padding-left: 22px; border-left: 2px solid var(--teal); }
.story__questions p + p { margin-top: 6px; }
.story__takeaway { font-size: clamp(1.25rem, 2.5vw, 1.625rem); line-height: 1.8; font-weight: 700; padding-block: 40px; text-wrap: pretty; }
.story__metadata { display: grid; grid-template-columns: 1fr 1fr 1.25fr; gap: 24px; padding-top: 24px; border-top: 1px solid var(--hairline); color: var(--stone); font-size: var(--fs-meta); line-height: 1.85; }
.story__metadata dt { font-weight: 500; margin-bottom: 10px; }
.story__metadata dd { margin: 0; }
.story__findings { padding-left: 22px; border-left: 1px solid var(--hairline); }
.story__findings h3 { margin-bottom: 16px; }

/* Different reasons connect to a shared relationship, not a one-to-one mapping. */
.constellation__branches { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 28px; position: relative; padding-bottom: 28px; }
.constellation__field { position: relative; min-width: 0; padding-bottom: 24px; }
.constellation__field h4 { display: flex; gap: 10px; align-items: center; margin-bottom: 20px; }
.constellation__field h4::before { content: ''; width: 9px; height: 9px; border: 2px solid var(--teal-deep); border-radius: 50%; flex-shrink: 0; }
.constellation__reasons { padding-left: 18px; border-left: 1px solid var(--teal); }
.constellation__reasons p { position: relative; font-size: var(--fs-sm); line-height: 1.85; }
.constellation__reasons p + p { margin-top: 18px; }
.constellation__reasons p::before { content: ''; position: absolute; left: -18px; top: .9em; width: 10px; border-top: 1px solid var(--teal); }
.constellation__field::after { content: ''; position: absolute; bottom: -28px; height: 40px; left: 50%; border-left: 1px solid var(--teal); }
.constellation__branches::after { content: ''; position: absolute; bottom: 0; left: calc((100% - 56px) / 6); right: calc((100% - 56px) / 6); border-top: 1px solid var(--teal); }
.constellation__relationship { position: relative; width: fit-content; max-width: 100%; margin: 32px auto 0; padding: 20px 30px; border: 1px solid var(--teal); border-radius: 50%; text-align: center; }
.constellation__relationship::before { content: ''; position: absolute; left: 50%; top: -33px; height: 32px; border-left: 1px solid var(--teal); }
.constellation__relationship > span { font-size: var(--fs-meta); color: var(--stone); }
.constellation__relationship p { margin-top: 6px; font-size: var(--fs-head); font-weight: 700; }

/* Two complementary roles join the same service journey. */
.service-roles__pair { display: grid; grid-template-columns: minmax(0, 1fr) 40px minmax(0, 1fr); gap: 12px; position: relative; padding-bottom: 28px; }
.service-roles__role { display: flex; flex-direction: column; min-width: 0; padding: 22px; border-top: 2px solid var(--teal); background: var(--sand); border-radius: var(--r-sm); }
.service-roles__role h4 { margin-bottom: 18px; }
.service-roles__role ul { list-style: none; margin-block: 10px 24px; }
.service-roles__role li { padding-block: 6px; border-bottom: 1px solid var(--hairline); }
.service-roles__value { margin-top: auto; }
.service-roles__value h5 { font-size: var(--fs-meta); font-weight: 500; color: var(--stone); margin-bottom: 8px; }
.service-roles__exchange { align-self: center; justify-self: center; font-size: 30px; color: var(--teal-deep); }
.service-roles__pair::after { content: ''; position: absolute; bottom: 0; left: 25%; right: 25%; height: 28px; border: 1px solid var(--teal); border-top: 0; border-radius: 0 0 12px 12px; }
.service-roles__shared { position: relative; width: fit-content; max-width: 100%; margin: 28px auto 0; text-align: center; padding: 16px 30px; border: 1px solid var(--teal); border-radius: var(--r-sm); }
.service-roles__shared::before { content: ''; position: absolute; top: -29px; left: 50%; height: 28px; border-left: 1px solid var(--teal); }
.service-roles__shared p { font-size: var(--fs-head); font-weight: 700; }
.service-roles__shared span { font-size: var(--fs-sm); color: var(--stone); }

/* The growing scope is carried by expanding widths, independent of animation. */
.system-layers__map { list-style: none; display: grid; gap: 26px; }
.system-layers__map li { position: relative; display: flex; align-items: baseline; gap: 18px; width: 64%; margin-inline: auto; padding: 18px 22px; border: 1px solid var(--hairline); border-left: 2px solid var(--teal); border-radius: var(--r-sm); background: var(--sand); }
.system-layers__map li:nth-child(2) { width: 73%; }
.system-layers__map li:nth-child(3) { width: 82%; }
.system-layers__map li:nth-child(4) { width: 91%; }
.system-layers__map li:nth-child(5) { width: 100%; }
.system-layers__map li + li::before { content: '↓'; position: absolute; top: -27px; left: 50%; transform: translateX(-50%); line-height: 26px; color: var(--teal-deep); }
.system-layers__number { font-family: var(--latin); font-size: var(--fs-meta); color: var(--stone); }
.system-layers__map li:first-child { background: transparent; }
.system-layers__map li:last-child { border-color: var(--teal); }

/* Native details fallback gets the full reading width on desktop, too. */
.case--story:has(.case__disc[open]) { grid-column: 1 / -1; }
.case--story .case__disc[open] .case__detail { padding: 24px 12px; }
@media (max-width: 760px) {
  .case--story .case__head { aspect-ratio: auto; }
  .case--story .case__art { width: 64px; height: 64px; margin: 12px auto 16px; }
  .case-modal__index { font-size: .75rem; letter-spacing: .06em; }
}
@media (max-width: 600px) {
  .story__metadata { grid-template-columns: minmax(0, 1fr); gap: 20px; }
  .story__metadata dt { margin-bottom: 4px; }
  .story__figure { margin-block: 32px; padding-block: 22px; }
  .constellation__branches { grid-template-columns: minmax(0, 1fr); gap: 26px; margin-left: 8px; padding: 0 0 0 22px; border-left: 1px solid var(--teal); }
  .constellation__field { padding-bottom: 0; }
  .constellation__field h4 { margin-bottom: 12px; }
  .constellation__field::after { left: -23px; top: 15px; bottom: auto; width: 16px; height: 0; border-left: 0; border-top: 1px solid var(--teal); }
  .constellation__reasons p + p { margin-top: 10px; }
  .constellation__branches::after { left: -1px; right: 50%; bottom: -18px; height: 18px; border: 1px solid var(--teal); border-top: 0; border-right: 0; }
  .constellation__relationship { margin-top: 48px; padding: 20px 16px; border-radius: 42px; }
  .constellation__relationship p { font-size: 1.125rem; }
  .service-roles__pair { grid-template-columns: minmax(0, 1fr); padding: 0 0 0 18px; border-left: 1px solid var(--teal); gap: 10px; }
  .service-roles__role { padding: 20px; position: relative; }
  .service-roles__role::before { content: ''; position: absolute; top: 26px; left: -19px; width: 18px; border-top: 1px solid var(--teal); }
  .service-roles__exchange { transform: rotate(90deg); }
  .service-roles__pair::after { left: -1px; right: 50%; bottom: -18px; height: 18px; border-right: 0; border-radius: 0; }
  .service-roles__shared { margin-top: 46px; }
  .system-layers__map li { padding: 16px 12px; gap: 10px; }
  .system-layers__map li:nth-child(1) { width: 84%; }
  .system-layers__map li:nth-child(2) { width: 88%; }
  .system-layers__map li:nth-child(3) { width: 92%; }
  .system-layers__map li:nth-child(4) { width: 96%; }
}


/* ============================================================
   04 ABOUT
   ============================================================ */
/* About 是 interlude，所以這三個數字降兩階：上線從 ink 換成 hairline
   （ink 上線在這個站是「主清單」的記號，colophon 不該拿），
   級數從 --fs-mega（30–44px）收到 26–34px，內距收三成。 */
.counters {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  border-top: 1px solid var(--hairline);
  margin-bottom: 0;
}
.counters__item {
  padding: clamp(14px, 1.8vw, 20px) clamp(10px, 1.6vw, 20px) clamp(16px, 2.2vw, 24px) 0;
}
.counters__item + .counters__item {
  border-left: 1px solid var(--hairline);
  padding-left: clamp(14px, 2vw, 26px);
}
.counters__n {
  font-family: var(--latin);
  font-size: clamp(26px, 2.6vw, 34px);
  font-weight: 500;
  line-height: 1;
  letter-spacing: -.05em;
}
.counters__n span { color: var(--teal); }
.counters dd {
  margin: 10px 0 0;
  font-size: var(--fs-meta);
  line-height: 1.6;
  color: var(--stone);
}

.cv { border-top: 1px solid var(--hairline); }
.cv__row {
  display: grid;
  grid-template-columns: 148px minmax(0, 1fr);
  gap: 4px clamp(12px, 2vw, 24px);
  padding-block: clamp(20px, 2.2vw, 28px);
  border-bottom: 1px solid var(--hairline);
}
.cv__when {
  font-family: var(--latin);
  font-size: var(--fs-meta); font-weight: 500;
  letter-spacing: .04em;
  color: var(--stone);
  padding-top: .5em;
}
.cv__what h4 {
  font-family: var(--display);
  font-size: var(--fs-head);
  font-weight: 700;
  line-height: var(--lh-head);
  margin-bottom: var(--space-related);
}

/* 索引與這段文字是同一個敘事：上方離 .threads 是 clamp(36,4vw,52)（換一組），
   這裡只留 clamp(20,2.4vw,28)，讓它讀起來是那 27 個關鍵字的收束句。 */
.care__note {
  margin-top: clamp(20px, 2.4vw, 28px);
  font-size: var(--fs-body);
}

/* 教學主題與其實例共用同一個容器，實例標題低於區塊標題一級。 */
.teaching {
  margin-top: clamp(44px, 5vw, 64px);
  scroll-margin-top: 104px;
}
.teaching__header h3 {
  font-size: var(--fs-sub);
  line-height: var(--lh-head);
  font-weight: 700;
  padding-bottom: 16px;
  border-bottom: 1px solid var(--hairline);
  margin-bottom: 16px;
}
.teaching__header p { max-width: none; }
/* A shared reading surface separates topics from the introduction and examples. */
.teaching__topics {
  display: grid;
  gap: 24px;
  margin-block: clamp(28px, 3.4vw, 40px) clamp(36px, 4.2vw, 52px);
  padding: clamp(24px, 3vw, 36px);
  background: var(--white);
  border-radius: clamp(20px, 2.6vw, 30px);
}
.teaching__topics > li { min-width: 0; }
.teaching__topics h4 { font-size: 18px; line-height: 1.5; font-weight: 700; }
.teaching__topics p { font-size: var(--fs-sm); line-height: var(--lh-compact); color: var(--stone); margin-top: 8px; }
.teaching__examples-head { padding-top: 0; }
.teaching__examples-head h4 { font-size: 18px; line-height: 1.5; font-weight: 700; }
.teaching__examples-head p { margin-top: 6px; font-size: var(--fs-meta); color: var(--stone); }
.teaching .talks { border-top: 0; margin-top: 8px; }
.teaching .talk:last-child { border-bottom: 0; padding-bottom: 0; }
@media (min-width: 1101px) {
  .teaching__topics { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .teaching__topics > li + li { border-left: 1px solid var(--hairline); padding-left: 24px; }
}
@media (max-width: 1100px) {
  .teaching__topics > li + li { border-top: 1px solid var(--hairline); padding-top: 24px; }
}
@media (min-width: 721px) {
  .teaching .talk { grid-template-columns: 128px minmax(0, 1fr); }
  .teaching .talk__cover { grid-row: span 2; }
  .teaching .talk__meta { grid-column: 2; flex-direction: row; flex-wrap: wrap; gap: 12px 20px; }
}

/* 跟 .cv / .teach 同一條列表節奏（細線分隔、同一組 padding），差別在三欄：
   封面 · 講題 · meta+外連。meta 欄寬 112px，留出更多空間給長講題。 */
.talks { border-top: 1px solid var(--hairline); margin-top: 8px; }
.talk {
  display: grid;
  grid-template-columns: clamp(140px, 14vw, 168px) minmax(0, 1fr) 112px;
  gap: 8px clamp(16px, 2.2vw, 28px);
  align-items: start;
  padding-block: clamp(18px, 2vw, 26px);
  border-bottom: 1px solid var(--hairline);
}

/* 封面固定 2:1，四張一致——來源比例不同的圖靠 object-fit 置中裁掉，
   不讓列高被某一張圖撐開。圓角用 --r-sm：這是縮圖不是卡片，
   吃 --r 的 50px 會跟 .card 搶同一個語彙。 */
.talk__cover {
  display: block;
  aspect-ratio: 2 / 1;
  border-radius: var(--r-sm);
  overflow: hidden;
  background: var(--sand);
  /* 白底的簡報截圖直接貼在 cream 上會浮起來，用一道細線收邊。 */
  box-shadow: inset 0 0 0 1px var(--hairline);
}
.talk__cover img {
  width: 100%; height: 100%;
  object-fit: cover;
  transition: transform var(--dur-ui) var(--ease);
}
/* 觸控裝置點完會卡住 hover 樣式，所以位移一律關在 hover: hover 裡
   ——沿用 .nav / .card 那幾處的同一個做法。 */
@media (hover: hover) {
  .talk:hover .talk__cover img { transform: scale(1.03); }
}

.talk h5 {
  font-family: var(--display);
  font-size: var(--fs-head);
  font-weight: 700;
  line-height: var(--lh-head);
  letter-spacing: var(--track-zh-head);
  text-wrap: pretty;
}
.talk__where {
  margin-top: 6px;
  font-size: var(--fs-meta);
  line-height: 1.6;
  color: var(--stone);
}

/* 右欄兩行：日期在上、外連在下。日期吃 --latin（負字距、數字等寬對齊）。
   行高壓到 1.5、padding-top .3em，是為了讓日期的第一行字頂對上左欄標題的
   第一行字頂——兩欄字級不同，靠 half-leading 相減補出來的值。 */
.talk__meta {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 12px;
  line-height: 1.5;
}
.talk__when {
  font-family: var(--latin);
  font-size: var(--fs-meta);
  font-weight: 500;
  letter-spacing: .04em;
  color: var(--stone);
  padding-top: .3em;
}
/* 箭頭跟文字一起吃 .link 的底線，是「這個連結會離站」的訊號。
   不做 hover 位移——底線在，位移會讓箭頭飄出線外。 */
.talk__link {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  font-size: var(--fs-meta);
  white-space: nowrap;
}
.talk__arrow { margin-left: .4em; }

@media (prefers-reduced-motion: reduce) {
  .talk:hover .talk__cover img { transform: none; }
  .post:hover .post__cover img { transform: none; }
}

/* ============================================================
   05 WHEN TO REACH OUT
   ============================================================ */
/* Four research entry points, on the site's existing ink paper. */
.reach-lead {
  margin: 0 0 28px;
  font-size: var(--fs-body);
  line-height: var(--lh-compact);
  color: var(--stone);
}
/* Match the eyebrow label's line box + bottom padding. */
@media (min-width: 901px) {
  #reach .col::before {
    content: "";
    display: block;
    margin-top: calc(var(--fs-eyebrow-latin) * 1.7 + 12px);
    margin-bottom: 24px;
    border-top: 1px solid var(--hairline);
  }
}
@media (max-width: 900px) {
  .reach-stages { border-top: 1px solid var(--hairline); }
}
.reach-stage { border-bottom: 1px solid var(--hairline); }
.reach-stage__head {
  list-style: none;
  display: grid;
  grid-template-columns: 34px 66px minmax(0, 1fr) 36px;
  gap: 16px;
  align-items: center;
  min-height: 100px;
  padding-block: 24px;
  cursor: pointer;
}
.reach-stage__head::-webkit-details-marker { display: none; }
.reach-stage__num {
  font-family: var(--latin);
  font-size: 1.125rem;
  font-weight: 400;
  color: var(--stone);
}
.reach-stage__label { font-size: var(--fs-meta); color: var(--stone); }
.reach-stage__question {
  font-size: var(--fs-head);
  font-weight: 500;
  line-height: 1.65;
  text-wrap: pretty;
}
.reach-stage__toggle {
  position: relative;
  width: 36px; height: 36px;
  border: 1px solid var(--hairline);
  border-radius: 50%;
  transition: background var(--dur-ui) var(--ease), border-color var(--dur-ui) var(--ease);
}
.reach-stage__toggle::before,
.reach-stage__toggle::after {
  content: "";
  position: absolute;
  width: 12px; height: 1px;
  top: 50%; left: 50%;
  background: currentColor;
  transform: translate(-50%, -50%);
}
.reach-stage__toggle::after {
  transform: translate(-50%, -50%) rotate(90deg);
  transition: transform var(--dur-ui) var(--ease);
}
.reach-stage[open] .reach-stage__toggle::after { transform: translate(-50%, -50%) rotate(0deg); }
.reach-stage[open] .reach-stage__toggle { border-color: var(--teal); background: rgba(92, 178, 160, .12); }
.reach-stage[open] .reach-stage__num { color: var(--teal); }
.reach-stage__panel {
  margin: 0 0 24px 50px;
  padding: clamp(24px, 2.8vw, 36px);
  border-radius: 18px;
  background: rgba(246, 242, 232, .055);
}
.reach-stage__panel > p {
  max-width: var(--measure);
  color: var(--stone);
  font-size: var(--fs-body);
  line-height: var(--lh-compact);
}
.reach-stage__tags { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 22px; }
.reach-stage__tags > li {
  border: 1px solid var(--hairline);
  border-radius: var(--r);
  padding: 5px 13px;
  font-size: var(--fs-meta);
  line-height: 1.7;
}
.reach-note {
  margin-top: 26px;
  color: var(--stone);
  font-size: var(--fs-meta);
  line-height: var(--lh-compact);
}
@media (hover: hover) {
  .reach-stage__head:hover .reach-stage__toggle { background: rgba(92, 178, 160, .18); border-color: var(--teal); }
}
@media (max-width: 540px) {
  .reach-stage__head {
    grid-template-columns: 28px minmax(0, 1fr) 36px;
    gap: 6px 10px;
    padding-block: 22px;
  }
  .reach-stage__num { grid-column: 1; grid-row: 1; font-size: var(--fs-meta); }
  .reach-stage__label { grid-column: 2; grid-row: 1; }
  .reach-stage__question { grid-column: 1 / 3; grid-row: 2; font-size: 1.25rem; }
  .reach-stage__toggle { grid-column: 3; grid-row: 1 / 3; }
  .reach-stage__panel { margin-left: 0; padding: 22px 20px; }
  .reach-stage__tags { gap: 8px 6px; }
  .reach-stage__tags > li { padding-inline: 11px; }
}

/* ============================================================
   06 WORK WITH ME
   ============================================================ */
.cards {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(14px, 1.6vw, 20px);
}
.card {
  background: var(--white);
  border-radius: var(--r);
  padding: clamp(24px, 2.4vw, 30px);
}
.card h3 {
  font-family: var(--display);
  font-size: var(--fs-head);
  font-weight: 700;
  line-height: var(--lh-head);
  margin-bottom: 14px;
}
.card p { font-size: var(--fs-sm); line-height: var(--lh-compact); }
/* 跟 .card p 同級數。漢字差 1px 看不出來，只會多出一階沒用的級數；
   這一行的層級交給 stone 色與上緣細線。 */
.card__how {
  margin-top: 16px;
  padding-top: 12px;
  border-top: 1px solid var(--hairline);
  color: var(--stone);
}

.services__close {
  display: flex; flex-wrap: wrap; align-items: center; gap: 16px 22px;
  margin-top: clamp(32px, 4vw, 46px);
}

/* ============================================================
   07 QUESTIONS I EXPLORE
   ============================================================ */
/* 四篇文章的列表。三欄跟 .talks 同一套：封面 · 標題 · 外連——
   同一頁裡「一列帶縮圖的外部連結」只用一種形狀，不為了這一節再發明一個。
   差別有兩處：封面比例（og:image 是 16:9，講座封面是 2:1），
   以及右欄不放來源站名——Substack / Medium 對讀者不是分類，
   而且外連的箭頭已經說完「這會離站」這件事了。 */
.posts {
  margin-top: 8px;
  border-top: 1px solid var(--hairline);
}
.post {
  display: grid;
  grid-template-columns: clamp(140px, 14vw, 168px) minmax(0, 1fr) 92px;
  gap: 8px clamp(16px, 2.2vw, 28px);
  /* 一列之內三個元素高度差很大（封面固定、標題一到兩行、外連只有一行），
     所以對齊的基準是中線而不是頂線：四列的封面、內文、外連各自置中之後，
     中線落在同一個高度，列高不同也不會讓右邊的外連看起來忽高忽低。 */
  align-items: center;
  padding-block: clamp(18px, 2vw, 26px);
  border-bottom: 1px solid var(--hairline);
}

/* 封面來自各篇的 og:image，四張來源比例不一（1200×628、945×?），
   靠固定 16:9 + object-fit 置中裁齊，不讓列高被某一張圖撐開。
   圓角用 --r-sm：這是縮圖不是卡片，跟 .talk__cover 同一個判斷。 */
.post__cover {
  display: block;
  aspect-ratio: 16 / 9;
  border-radius: var(--r-sm);
  overflow: hidden;
  background: var(--sand);
  /* 白底或淺色的文章封面貼在 cream 上會浮起來，用一道細線收邊。 */
  box-shadow: inset 0 0 0 1px var(--hairline);
}
.post__cover img {
  width: 100%; height: 100%;
  object-fit: cover;
  transition: transform var(--dur-ui) var(--ease);
}
@media (hover: hover) {
  .post:hover .post__cover img { transform: scale(1.03); }
}

/* 分類不做成藥丸標籤——那是另一套語彙。用一道 12px 的 teal 短線起頭，
   跟導覽的當前指示線是同一個手勢：短線＝「這是哪一類」。
   兩篇同類的文章因此在捲動時自己對齊成一組。 */
.post__tag {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: var(--fs-micro);
  /* 500 而不是 600：辨識靠 --teal-deep 的顏色與前面那道 12px 短線，不靠字重。
     漢字端因此不需要載 Noto Sans TC 600（見 DESIGN.md 的字型載入）。 */
  font-weight: 500;
  line-height: 1.5;
  letter-spacing: var(--track-zh);
  color: var(--teal-deep);
  margin-bottom: 6px;
}
.post__tag::before {
  content: "";
  flex: none;
  width: 12px; height: 2px;
  border-radius: 1px;
  background: var(--teal);
}

.post h3 {
  font-family: var(--display);
  font-size: var(--fs-head);
  font-weight: 700;
  line-height: var(--lh-head);
  letter-spacing: var(--track-zh-head);
  text-wrap: pretty;
}
/* 標題底下那一句。它跟 .talk__where 在列裡是同一個角色（標題的補述，
   不是段落），所以吃同一個 --fs-meta，不跟著 .card p 那組升到 --fs-body。 */
.post__note {
  margin-top: 6px;
  font-size: var(--fs-meta);
  line-height: 1.6;
  color: var(--stone);
  text-wrap: pretty;
}

/* 右欄只有一行外連，靠 .post 的 align-items: center 落在列的中線上。
   44px 是觸控區的下限，字在裡面置中——觸控區是往上下兩邊長的，
   不是掛在字的下方。 */
.post__meta {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 12px;
  line-height: 1.5;
}
.post__link {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  font-size: var(--fs-meta);
  white-space: nowrap;
  /* .link 的底線是 border-bottom，會被 44px 的觸控區推到盒底，
     變成一條離字 11px 的浮線。這裡換成 text-decoration：
     線跟著字走，盒子多高都無所謂。粗細與位置調成跟 border 版一樣。 */
  border-bottom: 0;
  padding-bottom: 0;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-decoration-color: var(--stone);
  text-underline-offset: 3px;
}
.post__link:hover { text-decoration-color: var(--ink); }
.post__arrow { margin-left: .4em; }

/* 列表的收尾行。清單最後一列的 hairline 已經把它關起來了，
   所以這一行不再加線，只用 stone 退到補述的層級。
   角色跟 .services__close 一樣（一句話＋一個出口），
   差別在出口是外連文字連結而不是藥丸按鈕。
   min-height 給連結而不給整行：觸控區往字的上下長，行距不被撐開。 */
.posts__close {
  margin-top: clamp(14px, 1.6vw, 20px);
  font-size: var(--fs-meta);
  line-height: 1.6;
  color: var(--stone);
}
/* 出口直接借 .post__link：它已經把 44px 觸控區與 text-decoration 底線
   的關係處理好了（border-bottom 會被觸控區推成浮線），這裡只補顏色——
   在 stone 的句子裡，出口要回到 ink 才看得出是可點的那一段。 */
.posts__close .post__link { color: var(--ink); }

/* ============================================================
   08 CONTACT — the one loud surface
   ============================================================ */
/* 這一節不再滿版出血：teal-deep 收進一塊圓角矩形，
   讓最後一個動作在 cream 紙面上被「框」起來，
   圓角沿用 .card / .sub-card 的 --r，維持同一套語彙。 */
.contact {
  color: var(--white);
  /* Contact 是第四章的第二節（07「我的觀點」→ Contact），所以上緣吃的是
     章內交界那一階，跟 04 對 03、06 對 05 同一個值。它不另外畫版心寬的細線——
     teal 面板本身已經是這一節的事件。 */
  padding-block: calc(var(--section-y) * .5) clamp(22px, 2.6vw, 40px);
}
/* 這一節刻意不吃 --page 的 1200px 限制：面板幾乎滿版，只留一道窄邊
   讓圓角看得出來，色塊的重量才撐得住整頁最後一個動作。 */
.contact > .wrap {
  max-width: none;
  padding-inline: clamp(12px, 1.6vw, 24px);
}
.contact__panel {
  --focus: var(--white);
  background: var(--teal-deep);
  border-radius: var(--r);
  /* 垂直內距比水平的小一階：兩欄之後面板變寬變扁，
     上下再留 5.6vw 會把剛省下來的高度又還回去。 */
  padding: clamp(34px, 4vw, 60px) clamp(24px, 3.6vw, 60px);
}
/* 問句在左、回答與動作在右。眉標橫跨兩欄當這一節的抬頭，
   標題與右欄因此從同一條線起跑，面板高度 = 較高的那一欄，不是兩欄相加。

     ┌ Contact 聯繫我 ─────────────────┐
     │ 有興趣和我        如果你願意…    │
     │ 進一步聊聊嗎？    你不需要…      │
     │                   填寫表單 ↗     │
     │ FB  IG  Threads                  │
     └──────────────────────────────────┘

   右欄跨第 2、3 兩列，社群 align-self: end 落在第 3 列底部——
   兩欄因此收在同一條底線上，標題底下那塊空白有東西收邊。 */
.contact__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  column-gap: clamp(32px, 4.4vw, 72px);
  row-gap: clamp(24px, 3vw, 34px);
}
.contact__grid > .eyebrow--inline {
  grid-column: 1 / -1;
  /* 底線是標在「Contact」那個字下面的，不是一條分隔線：
     不收 justify-self，格線會把它拉成滿欄寬。 */
  justify-self: start;
  margin-bottom: 0;
}
.contact__grid > .contact__head { grid-column: 1; grid-row: 2; }
.contact__grid > .contact__act  { grid-column: 2; grid-row: 2 / span 2; }
.contact__grid > .contact__social {
  grid-column: 1; grid-row: 3;
  align-self: end;
  /* 間距交給格線的 row-gap，自己不再帶 margin。 */
  margin-top: 0;
}
/* 右欄自己不排間距，交給裡面每一件東西原本的 margin-top，
   只把第一段的那一份收掉——它要跟左邊的標題對齊在同一條起跑線上。 */
.contact__act > :first-child { margin-top: 0; }
.eyebrow--inline {
  position: static;
  display: inline-block;
  margin-bottom: clamp(26px, 3.4vw, 40px);
  color: var(--white);
}
.eyebrow--inline > span {
  color: var(--white);
  border-bottom-color: rgba(255,255,255,.34);
}

.contact__head {
  font-family: var(--display);
  font-size: var(--fs-mega);
  font-weight: 900;
  line-height: var(--lh-display);
  letter-spacing: var(--track-zh-head);
}
.contact__body + .contact__body {
  margin-top: clamp(12px, 1.4vw, 18px);
}
.contact__body {
  margin-top: clamp(20px, 2.4vw, 28px);
  font-weight: 500;
  /* ch 是「0」的寬度（半形），量不到漢字：44ch 實際只有 22 個漢字寬。
     全站的行寬一律用 em，1em = 1 個漢字。 */
  max-width: 24em;
}
.contact__mail {
  display: inline-block;
  margin-top: clamp(26px, 3.2vw, 38px);
  font-family: var(--latin);
  font-size: var(--fs-display);
  font-weight: 500;
  letter-spacing: -.035em;
  text-decoration: none;
  /* Deep Teal 帶上唯一的暖色標記，落在這一頁最後一個動作上。
     ochre 對 deep teal 只有 2.09:1，所以線加粗到 3px 才站得住。 */
  border-bottom: 3px solid var(--ochre);
  padding-bottom: 6px;
  word-break: break-word;
  transition: border-bottom-width var(--dur-ui) var(--ease),
              padding-bottom var(--dur-ui) var(--ease);
}
/* hover 時筆壓重一階（3 → 6px）。padding 同步減掉，總高不變，
   下面的社群藥丸才不會被推動。不用淡出——ochre 是全站唯一的暖色標記。 */
@media (hover: hover) {
  .contact__mail:hover { border-bottom-width: 6px; padding-bottom: 3px; }
}
/* 出口換成中文的表單連結：--latin 的 -.035em 是給西文字母收縫的，
   套在漢字上會把字擠在一起，所以字族與字距都換回中文那一套。 */
.contact__mail--zh {
  font-family: var(--display);
  letter-spacing: var(--track-zh);
}

/* 社群是次要出口：藥丸只有 1px 的白框，重量刻意壓在表單連結之下，
   同一塊面板裡才只有一個主要動作。 */
.contact__social {
  display: flex; flex-wrap: wrap; gap: 8px;
}
.contact__social a {
  display: block;
  padding: 10px 19px;
  border: 1px solid rgba(255,255,255,.55);
  border-radius: var(--r);
  font-size: var(--fs-meta); font-weight: 500;
  text-decoration: none;
  transition: background-color .2s ease, color .2s ease;
}
.contact__social a:hover { background: var(--white); color: var(--teal-deep); }

/* ============================================================
   FOOTER
   ============================================================ */
.foot { padding-block: 34px 44px; background: var(--cream); }
.foot__in {
  display: flex; flex-wrap: wrap; gap: 10px 24px;
  justify-content: space-between; align-items: baseline;
  font-size: var(--fs-meta); color: var(--stone);
}
.foot__q {
  font-family: var(--latin);
  letter-spacing: .02em;
}

/* ============================================================
   RESPONSIVE
   ============================================================ */
@media (max-width: 1080px) {
  :root { --gutter-w: 132px; }
}

@media (max-width: 900px) {
  .layout { grid-template-columns: minmax(0, 1fr); gap: 18px; }
  /* 單欄之後 row-gap 同時是「眉標→標題」與「標題→內文」的距離，
     取兩者原本值的中間，順序仍是 DOM 順序，不用改結構。 */
  .contact__grid {
    grid-template-columns: minmax(0, 1fr);
    row-gap: clamp(20px, 3vw, 28px);
  }
  .contact__grid > .contact__head,
  .contact__grid > .contact__act,
  .contact__grid > .contact__social {
    grid-column: auto; grid-row: auto;
  }
  .col { max-width: none; }
  .eyebrow {
    position: static;
    display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 12px;
  }
  .eyebrow > span {
    border-bottom: 0; padding-bottom: 0; margin-bottom: 0;
  }
  /* 單欄章節入口：英文輔標、中文主標、底線與內容間距共同建立層級。 */
  .section .layout { gap: 28px; }
  .section .gutter {
    padding-bottom: 20px;
    border-bottom: 1px solid var(--hairline);
  }
  .section .eyebrow {
    display: block;
    font-size: 24px;
    font-weight: 700;
    line-height: 1.5;
  }
  .section .eyebrow::before {
    content: "";
    display: block;
    width: 32px;
    height: 4px;
    margin-bottom: 16px;
    border-radius: 2px;
    background: var(--teal);
  }
  .section .eyebrow > span {
    margin-bottom: 8px;
    font-size: var(--fs-micro);
    line-height: 1.5;
    font-weight: 500;
    color: var(--stone);
    letter-spacing: .08em;
  }

  /* ---- 手機的章節感不是桌機縮小 ----
     單欄之下，每一節的章節入口本來長得一模一樣（teal 短線 ＋ 12px 英文 ＋
     24px 中文 ＋ 一條分隔線），八個一樣的入口正好是「讀不出章節」的來源。
     手機的分工改成：

       章節第一節  teal 短線 ＋ 24px 標題   → 這裡開了一章
       章節第二節  沒有短線 ＋ --fs-sub 標題 → 還在同一章裡

     章際交界則靠滿版的紙邊（03、04）與滿版細線（02）——單欄下那是唯一
     真的橫貫整個螢幕的東西，比任何留白都讀得出來。 */

  /* 桌機那條版心寬的細線在單欄下等於滿版，會跟章際那一級撞在一起，所以手機不畫。 */
  .section--cont > .wrap { border-top: 0; padding-top: 0; }
  /* 32 × 4px 的 teal 短線保留給「開一章」的那一節。 */
  .section--cont .eyebrow::before { display: none; }
  /* 標題降一階（24px → --fs-sub 21px）：它是這一章的第二段，不是新的一章。
     語意仍然是 h2，降的只有視覺份量。 */
  .section--cont .eyebrow { font-size: var(--fs-sub); }

  /* 插圖跟介紹仍然並排（跟桌機同一個關係，也不會多長出一列把 Hero 撐高），
     只是左欄改成按比例縮：右欄至少還有 2/3 的寬度給那三段文字。 */
  .hero__grid { grid-template-columns: minmax(0, 1fr) minmax(0, 42%); gap: 28px; }
  .hero__intro { grid-column: 1 / -1; }
  .hero__figure { grid-column: 2; width: 100%; max-width: none; margin: 0 0 .6em; }
  .hero__body { grid-column: 1; margin-top: 0; max-width: 28em; }
}

/* 導覽在筆電窄視窗先收成選單，避免文字與品牌互相擠壓。 */
@media (max-width: 1100px) {
  .nav__links {
    display: none;
    position: absolute;
    inset: calc(100% + 8px) 0 auto 0;
    flex-direction: column;
    align-items: stretch;
    background: var(--white);
    border-radius: 26px;
    padding: 12px;
    gap: 0;
    max-height: calc(100dvh - 108px);
    overflow-y: auto;
    overscroll-behavior: contain;
  }
  .nav { position: relative; }
  .nav__links.is-open { display: flex; }

  /* 選單展開／收合都補間。display 用 allow-discrete + @starting-style 帶，
     不支援的瀏覽器就是瞬間開關，功能不受影響。 */
  .nav__links {
    opacity: 0;
    transform: translateY(-6px);
    transform-origin: top center;
    transition: opacity var(--dur-ui) var(--ease),
                transform var(--dur-ui) var(--ease),
                display var(--dur-ui) var(--ease) allow-discrete;
  }
  .nav__links.is-open { opacity: 1; transform: none; }
  @starting-style {
    .nav__links.is-open { opacity: 0; transform: translateY(-6px); }
  }
  .nav__links a { padding: 13px 16px; font-size: 17px; }
  .nav__links-cta { display: block; margin-top: 6px; padding-top: 6px; border-top: 1px solid var(--hairline); }
  .nav__toggle { display: grid; }
  .nav__cta { display: none; }
}

@media (max-width: 720px) {
  /* 手機維持 17px 內文，密度由欄數與間距調整。 */
  :root { --fs-body: 1.0625rem; }
  .cards { grid-template-columns: minmax(0, 1fr); }
  .card { border-radius: 34px; }
  .contact__panel { border-radius: 34px; }
  .cv__row { grid-template-columns: minmax(0, 1fr); gap: 6px; }
  .cv__when { padding-top: 0; }
  /* 窄螢幕：封面留在左邊（它是這一列的辨識點），meta 掉到講題下方橫排一行。 */
  .talk {
    grid-template-columns: clamp(110px, 30vw, 150px) minmax(0, 1fr);
    gap: 10px clamp(14px, 4vw, 20px);
  }
  .talk__meta {
    flex-wrap: wrap;
    grid-column: 2;
    flex-direction: row; align-items: baseline; gap: 16px;
  }
  .talk__when { padding-top: 0; }
  .post {
    grid-template-columns: clamp(110px, 30vw, 150px) minmax(0, 1fr);
    gap: 10px clamp(14px, 4vw, 20px);
  }
  .post__meta {
    flex-wrap: wrap;
    grid-column: 2;
    flex-direction: row; align-items: baseline; gap: 16px;
  }
  .beat { grid-template-columns: minmax(0, 1fr); gap: 4px; }
  .beat__k { padding-top: 0; }
  .case__quote { padding-left: 0; }
  .counters { grid-template-columns: minmax(0, 1fr); }
  .counters__item + .counters__item {
    border-left: 0; padding-left: 0;
    border-top: 1px solid var(--hairline);
  }
  .counters__item { padding-right: 0; }
  .nav { padding-left: 16px; }
}

/* 縮圖與日期先讓出空間，保持標題的閱讀寬度。 */
@media (min-width: 721px) and (max-width: 1100px) {
  .talk { grid-template-columns: 150px minmax(0, 1fr); }
  .talk__cover { grid-row: span 2; }
  .talk__meta { grid-column: 2; flex-direction: row; flex-wrap: wrap; gap: 12px 20px; }
  .talk__when { padding-top: 0; }
  .post { grid-template-columns: 150px minmax(0, 1fr); }
  .post__cover { grid-row: span 2; }
  .post__meta { grid-column: 2; flex-direction: row; flex-wrap: wrap; gap: 12px 20px; }
}

@media (max-width: 540px) {
  .talk { grid-template-columns: minmax(0, 1fr); gap: 16px; }
  .talk__cover { width: min(200px, 100%); }
  .talk__meta { grid-column: 1; gap: 12px 20px; }
  .talk h5 { max-width: 24em; }
  .post { grid-template-columns: minmax(0, 1fr); gap: 16px; }
  .post__cover { width: min(200px, 100%); }
  .post__meta { grid-column: 1; gap: 12px 20px; }
  .post h3 { max-width: 24em; }
}

@media (max-width: 400px) {
  .nav__name { font-size: 14px; }
}

/* 手機首頁仍保留插圖，但壓小一階、縮短介紹之前的空白。 */
@media (max-width: 540px) {
  /* 單欄時「幾乎滿屏」會把字撐得太散：高度交回內容決定。 */
  .hero { min-height: 0; }
  :root { --fs-hero: clamp(1.875rem, 7.8vw, 2.5rem); }
  .hero__grid { grid-template-columns: minmax(0, 1fr); row-gap: 0; align-content: start; }
  .hero__intro { margin-bottom: 24px; }
  .hero__figure {
    grid-column: 1;
    justify-self: start;
    width: min(280px, 68vw);
    margin: 20px 0 22px;
  }
  .hero__body { grid-column: 1; max-width: none; }
}

/* 短數據能並排比較，不必各佔一整列。320px 保留原本直排。 */
@media (min-width: 380px) and (max-width: 720px) {
  .counters { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .counters__item { padding: 20px 8px 20px 0; }
  .counters__item + .counters__item {
    border-top: 0;
    border-left: 1px solid var(--hairline);
    padding-left: 10px;
  }
  .counters__n { font-size: 30px; }
}


/* ============================================================
   ABOUT 的 colophon 密度
   About 不需要成為第二個大型 feature section：前面的案例與合作已經證明
   專業，它的任務只是「如果你開始對這個人有興趣，這裡讓你再靠近一點」。
   所以這一節是全頁刻意最安靜的一塊——沒有滿版、沒有巨大字、沒有色塊，
   只有一格作者像、三個小號數字、兩列經歷、教學三欄。
   節奏上它接在案例那一節之後、ink 那一章之前：兩片重的東西之間一定要有
   一段輕的，contrast 才會讓翻到背面那一下有力量。
   ============================================================ */
.colophon { display: grid; gap: clamp(18px, 2.4vw, 28px); }
/* 整站唯一一張照片。裁成正圓——這一節走的是 colophon（作者欄）的構圖，
   圓形頭像本來就是作者欄的語彙，跟版面上其他方角的卡片區隔開來。
   width/height 寫在標籤上（800×800）讓它在載入前就佔好位置，不推版。
   object-fit 是保險：換一張非正方的照片時構圖不會跑掉。 */
.colophon__fig {
  max-width: 210px;
  aspect-ratio: 1 / 1;
  height: auto;
  object-fit: cover;
  border-radius: 50%;
  background: var(--sand);
}
@media (min-width: 901px) {
  .colophon {
    grid-template-columns: clamp(146px, 16vw, 200px) minmax(0, 1fr);
    column-gap: clamp(24px, 3vw, 44px);
    align-items: start;
  }
  .colophon__fig { max-width: none; width: 100%; }
}
#about .lead { margin-bottom: clamp(14px, 1.8vw, 20px); }
#about .sub { font-size: var(--fs-head); margin-top: clamp(28px, 3.2vw, 40px); }
#about .cv__what p,
#about .teaching__header p { font-size: var(--fs-sm); line-height: var(--lh-compact); }
#about .cv__what h4 { font-size: var(--fs-head); }
#about .teaching__header h3 { font-size: var(--fs-head); }


/* 閱讀補強：短行長文、窄螢幕可換行，以及穩定的章節起點。 */
.hero__role { display: block; }
.hero__id { line-height: 1.65; }
.case__line { line-height: 1.9; }
.card p { font-size: var(--fs-body); line-height: 1.9; }
.teaching__topics p, #about .cv__what p, #about .teaching__header p { font-size: var(--fs-sm); }
.vfield__keys { column-gap: .15em; }
.vfield__keys > span { max-width: 100%; white-space: normal; overflow-wrap: anywhere; }
@media (min-width: 541px) and (max-width: 720px) {
  .hero__grid { grid-template-columns: minmax(0, 1fr); }
  .hero__body { grid-column: 1; max-width: 36em; }
  .hero__figure { grid-column: 1; justify-self: end; width: min(60%, 300px); }
}
@media (max-width: 720px) {
  .vfield__cluster { padding-block: 16px; }
  .vfield__icon { top: 16px; }
  .vfield__keys { margin-top: 6px; row-gap: 4px; }
  .beat + .beat { margin-top: 24px; }
  .beat__k { font-weight: 700; color: var(--ink); margin-bottom: 4px; }
  .case__foot { gap: 14px; }
  .case__more { min-height: 44px; display: inline-flex; align-items: center; }
}

/* Review refinements: shared content edges and restrained subscriptions. */
.counters__n { font-variant-numeric: tabular-nums; }
.counters__n .counters__value { color: inherit; }

.teaching .talk__body { min-width: 0; }
.teaching .talk__meta {
  display: grid;
  grid-template-columns: 7.5rem max-content;
  align-items: center;
  gap: 8px 20px;
  margin: 0;
}
.teaching .talk__when { padding: 0; line-height: 1.5; }
.teaching .talk__link { justify-self: start; }
@media (min-width: 541px) {
  .teaching .talk { grid-template-columns: clamp(110px, 12vw, 160px) minmax(0, 1fr); gap: 8px 24px; }
  .teaching .talk__cover { grid-column: 1; grid-row: 1 / 3; }
  .teaching .talk__body { grid-column: 2; grid-row: 1; }
  .teaching .talk__meta { grid-column: 2; grid-row: 2; }
}
@media (max-width: 540px) {
  .teaching .talk { grid-template-columns: minmax(0, 1fr); }
  .teaching .talk__meta { grid-column: 1; grid-template-columns: 7rem max-content; gap: 8px 12px; }
}

/* 電子報保留細框線，使用較小內文與輕量標題。 */
.newsletters {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0;
  margin-top: clamp(32px, 4vw, 48px);
  border-block: 1px solid var(--hairline);
}
.sub-card {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 8px;
  min-width: 0;
  padding: 24px 28px 24px 0;
}
.sub-card + .sub-card { border-left: 1px solid var(--hairline); padding-left: 28px; padding-right: 0; }
.sub-card h3 {
  margin: 0;
  font-size: var(--fs-body);
  font-weight: 500;
  line-height: 1.6;
}
.sub-card p {
  margin: 0;
  max-width: 30em;
  font-size: .9375rem;
  line-height: 1.8;
  color: var(--stone);
}
.sub-card__link {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 44px;
  margin-top: auto;
  border: 0;
  padding: 0;
  text-decoration: underline;
  text-decoration-color: var(--hairline);
  text-underline-offset: 4px;
  font-size: var(--fs-meta);
  font-weight: 400;
  color: var(--stone);
}
.sub-card__link:hover, .sub-card__link:focus-visible {
  color: var(--ink);
  text-decoration-color: currentColor;
}
@media (max-width: 640px) {
  .newsletters { grid-template-columns: minmax(0, 1fr); }
  .sub-card { padding: 20px 0; }
  .sub-card + .sub-card { border-left: 0; border-top: 1px solid var(--hairline); padding: 20px 0; }
}
