/* ══════════════════════════════════════════════════════════════════
   個人英文單字工具 · 樣式
   深淺雙主題（跟隨系統）。美式復古三色：深藍 #1C223A／米 #E5D4B6／橙 #F44E1C。
   強調色只有一處：橙＝「差一點」（near_miss），本工具的招牌狀態。

   兩版是同一套色相的一體兩面，不是各配各的：
     暗版＝深藍底、米字、橙塊（＝參考海報本身的長相）
     亮版＝米底、深藍字、橙塊（翻轉）

   ⚠️ 招牌色的 token 仍叫 `--amber*`。值已經從琥珀換成橙，但名字沒改——
   全站 47 處引用分散在三個 css 檔，改名只買到命名精確，卻要冒漏改一處
   就少一塊顏色的風險。要改名請一次改完並實機掃過三個主題狀態。
   ══════════════════════════════════════════════════════════════════ */

/* ── 設計變數 ───────────────────────────────────────────────── */
:root {
  color-scheme: light dark;

  --font-ui: -apple-system, BlinkMacSystemFont, "Helvetica Neue",
             "PingFang TC", "Heiti TC", sans-serif;
  --font-serif: "Iowan Old Style", "Palatino Linotype", Palatino,
                "Palatino LT STD", Georgia, "Times New Roman",
                "Songti TC", serif;
  --font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  /* 揭曉的正解字級：與例句（.sentence）同級。
     正解是要讀的內容，不是註解——比句子小反而要重新對焦（V 2026-07-27）。
     練習、程度評估、辨析卡三個畫面共用這一個值，改這裡就全部跟著動。
     判定短語（答對了／不對，再試一次／差一點）不吃這個 token，維持 15px：
     那是提示語不是正解，拉大會跟正解搶。 */
  --answer-size: 32px;

  /* 字級階梯（V 2026-07-29：「字有點小」）。全站只有這七階，地板 13px。
     改版前有 21 種字級、130 個宣告裡 72 個在 12.5px 以下——階數多就守不住，
     每個時期各寫各的，最後長出 10px 的側欄項目名。
     需要「更輕」的層級時用顏色與字重去分，**不要再往下砍字級**。 */
  --fs-xs:   13px;   /* 標籤、計數、輔助說明——這是地板 */
  --fs-sm:   15px;   /* 次要內文 */
  --fs-base: 17px;   /* 內文基準（body） */
  --fs-lg:   20px;   /* 母語說法、輸入框 */
  --fs-xl:   24px;   /* 區塊標題 */
  --fs-hero: 32px;   /* ＝ --answer-size，例句與正解 */

  /* 中性色：暖米底、深藍字。明度是解出來的不是挑的。

     ⚠️ 每個前景 token 都對**最不利的那個面**求解，不是對 --bg：
       淺色 → --bg-sunk（86%，最暗的面，側欄與頁尾用它）
       深色 → --surface-2（22%，最亮的面）
     中間調的字在越接近自己明度的底上對比越低，所以「對 --bg 算過就好」
     是錯的——2026-07-29 就這樣漏掉 25 種元件（側欄項目名 3.68:1、
     推薦度星星 3.06:1）。**改任何一個面的明度，前景全部要重算。** */
  --bg:         hsl(38 45% 93%);      /* 米的淡版。原色 #E5D4B6 當整頁底太重 */
  --bg-sunk:    hsl(38 47% 86%);      /* 側欄、頁尾這類下沉面 */
  --surface:    hsl(38 55% 97%);      /* 卡片。帶暖，不用純白才不會跟米底打架 */
  --surface-2:  hsl(38 40% 90%);
  --line:       hsl(228 18% 55%);     /* 3.0:1 —— 舊版 1.29:1 等於看不見 */
  --line-soft:  hsl(228 16% 78%);     /* 1.9:1 —— 舊版 1.12:1 */
  --ink:        hsl(228 35% 22%);     /* 12.0:1 深藍 #1C223A 的可讀版 */
  --ink-soft:   hsl(228 20% 32%);     /* 7.0:1 */
  --ink-faint:  hsl(228 16% 43%);     /* 4.5:1 —— 舊版 3.27:1 不到內文標準 */

  /* 招牌色：橙＝差一點。名字仍是 amber，見檔頭說明。
     ⚠️ 橙在米底上只有 2.42:1，**不可以拿來當內文顏色**——
     海報裡的橙是大色塊不是字。--amber 是壓深過的版本（4.6:1）才給文字用，
     --amber-line 是原色 #F44E1C，只做填色、外框與強調塊。 */
  --amber:      hsl(14 91% 38%);
  --amber-line: hsl(14 91% 53%);
  --amber-soft: hsl(14 80% 92%);

  /* 揭曉的正解專用綠（V 2026-08-02：「不夠明顯，要更亮的綠」）。
     ⚠️ **亮版與暗版要往相反方向調**——在米底上「更亮」＝對比更低＝更看不清楚。
     一路提亮到最鮮那版對比只剩 2.26:1，連大字的 3:1 都過不了。
     所以亮版走的是**拉飽和、明度不動**：45% → 70% 飽和，對比 5.30 → 4.24:1，
     看起來鮮很多而且還守得住內文的 4.5:1（底線與小字共用這顆）。
     不動全站的 --green：那顆給判定文字、統計、軌跡格用，它們是小字。 */
  --green-answer: hsl(150 70% 30%);

  /* 語意色：對著暖底重算，並把紅往紫紅移，跟橙拉開色相 */
  --green:      hsl(150 45% 30%);     /* 4.5:1 */
  --green-line: hsl(150 45% 38%);
  --green-soft: hsl(150 40% 91%);
  --red:        hsl(352 60% 45%);     /* 5.6:1，色相 352 離橙的 14 夠遠 */
  --red-line:   hsl(352 60% 50%);
  --red-soft:   hsl(352 60% 93%);

  --focus:      hsl(228 45% 40%);     /* 焦點環改用深藍，不再是紫 */

  --cal-1: hsl(38 55% 32%);
  --cal-4: hsl(228 35% 30%);

  --radius: 10px;
  --radius-lg: 16px;
  --shadow: 0 1px 2px hsl(252 30% 20% / .05),
            0 8px 24px -14px hsl(252 40% 25% / .18);

  /* 動態：全域訊息列的進出場（transitions.dev 22-toast 的原始調校值）。
     進場比退場慢——「到達」要從容，「離開」不必拖。這個不對稱是刻意的。 */
  --toast-open: 350ms;
  --toast-close: 250ms;
  --toast-distance: 16px;
  --toast-blur: 2px;
  --toast-scale: 0.97;
  --toast-ease: cubic-bezier(0.22, 1, 0.36, 1);

  /* 動態：單字小卡。桌面是點擊觸發的，所以 17-tooltip 原本那 80ms 意圖延遲
     不套用——那是 hover 防誤觸用的，在點擊上只會變遲鈍。 */
  --wt-in: 150ms;
  --wt-out: 50ms;
  --wt-scale: 0.98;
  --wt-in-ease: ease-out;
  --wt-out-ease: ease-out;
  /* 手機底部抽屜（07-panel-reveal） */
  --wt-sheet-open: 400ms;
  --wt-sheet-close: 350ms;
  --wt-sheet-blur: 2px;
  --wt-sheet-ease: cubic-bezier(0.22, 1, 0.36, 1);

  /* 動態：重打框打錯的回饋（transitions.dev 12-error-state-shake）。
     四段不對稱位移——右、左、過衝、歸位；過衝那一段是它比對稱來回好的原因。
     ⚠️ keyframes 的百分比停點是「各段累積時間 ÷ 總長」算出來的固定值，
     改 dur-a / dur-b 的比例就要回去重算，否則段落會跟總長對不上。 */
  --shake-distance: 6px;
  --shake-overshoot: 4px;
  --shake-dur-a: 80ms;
  --shake-dur-b: 60ms;
  --shake-ease: cubic-bezier(0.22, 1, 0.36, 1);
  --revert-hold: 3000ms;   /* 紅框與提示停留多久（夠讀完那句話） */
  --revert-dur: 280ms;

  /* 動態：文法批改的等待提示（transitions.dev 15-shimmer-text ＋
     04-text-states-swap）。字色是 V 2026-07-30 在四個候選裡挑的「丙」：
     底色走 --focus，亮帶走比 --ink 再深（亮版）／再亮（暗版）一格的墨。
     ⚠️ --shimmer-highlight 的兩個值是色票裡沒有的極端值，**只給這個元件用**，
     不要升格成通用 token——它們是為了讓亮帶掃得出來才存在的。
     亮版底色 7.11:1、暗版 9.79:1（對 --bg 實算）。比 --ink 的 11.9 低，
     這是 V 拍板時知道並接受的取捨：他要的是「顏色不一樣」。 */
  --shimmer-base: var(--focus);
  --shimmer-highlight: hsl(228 35% 10%);
  --shimmer-dur: 2000ms;
  --shimmer-band: 400%;
  --shimmer-ease: linear;
  --text-swap-dur: 150ms;
  --text-swap-translate-y: 4px;
  --text-swap-blur: 2px;
  --text-swap-ease: ease-in-out;
  --gwait-in: 250ms;
  --gwait-distance: 8px;
  --gwait-ease: cubic-bezier(0.22, 1, 0.36, 1);

  /* 資料長條專用的填色。**不要拿文字用的 --green/--amber/--red 來鋪長條**：
     那三個是照 4.5:1 的文字標準調的，鋪成大面積會過亮搶眼
     （V 2026-07-29 回報程度評估的頻率層長條）。大色塊不需要那麼高的強度，
     長度本身已經在表達數值了。招牌橙尤其要留給「差一點」那一個狀態。 */
  --green-fill: hsl(150 30% 45%);
  --amber-fill: hsl(28 45% 58%);
  --red-fill:   hsl(352 32% 62%);
}

@media (prefers-color-scheme: dark) {
  :root {
    /* 暗版＝參考海報本身：深藍底、米字、橙塊 */
    --bg:         hsl(228 35% 13%);   /* 比 #1C223A 再壓深一點當整頁底 */
    --bg-sunk:    hsl(228 38% 9%);
    --surface:    hsl(228 30% 18%);   /* ≈ #1C223A 原色，卡片浮在頁面上 */
    --surface-2:  hsl(228 28% 22%);
    --line:       hsl(228 16% 52%);   /* 3.0:1 */
    --line-soft:  hsl(228 18% 26%);
    --ink:        hsl(38 45% 82%);    /* 12.0:1 —— 幾乎就是米 #E5D4B6 */
    --ink-soft:   hsl(38 22% 72%);    /* 7.0:1 */
    --ink-faint:  hsl(38 14% 58%);    /* 4.5:1 */

    --amber:      hsl(14 91% 62%);    /* 暗底上要提亮才讀得到 */
    --amber-line: hsl(14 91% 53%);    /* 原色 #F44E1C */
    --amber-soft: hsl(14 45% 19%);

    --green:      hsl(150 45% 46%);
    --green-line: hsl(150 40% 40%);
    --green-soft: hsl(150 30% 15%);
    /* 深藍底上越亮對比越高，可以放心提亮：5.93 → 9.13:1。
       再上去（75–85% 飽和）會開始有螢光感，跟這套復古三色不合。 */
    --green-answer: hsl(150 62% 56%);
    --red:        hsl(352 60% 68%);
    --red-line:   hsl(352 55% 50%);
    --red-soft:   hsl(352 35% 18%);

    --focus:      hsl(38 60% 70%);    /* 暗底上焦點環用米，不用深藍 */

    --cal-1: hsl(38 45% 51%);
    --cal-4: hsl(38 45% 72%);

    --shadow: 0 1px 2px hsl(228 45% 3% / .5),
              0 10px 28px -16px hsl(228 45% 3% / .75);
    /* 等待提示的亮帶：暗底上要往上提才掃得出來（亮版是往下壓）。
       --shimmer-base 不必在這裡覆蓋——它寫的是 var(--focus)，
       上面剛把 --focus 換成米色，它就跟著翻了。 */
    --shimmer-highlight: hsl(38 45% 95%);
    --green-fill: hsl(150 28% 40%);
    --amber-fill: hsl(28 40% 48%);
    --red-fill:   hsl(352 28% 48%);
  }
}

/* ── 基礎 ───────────────────────────────────────────────────── */
* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  max-width: 100%;
  overflow-x: hidden;          /* 不允許橫向捲動 */
}
/* clip 不會建立捲動容器，才不會弄壞側欄的 position: sticky */
@supports (overflow-x: clip) {
  html, body { overflow-x: clip; }
}

body {
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-ui);
  font-size: 17px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

[hidden] { display: none !important; }

button {
  font: inherit;
  color: inherit;
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
}

input, textarea, select {
  font: inherit;
  color: inherit;
}

a { color: var(--focus); }

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

.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* ── 版面 ───────────────────────────────────────────────────── */
/* ── 導覽在上、內容在下（V 2026-08-02 拍板）─────────────────────────
   原本是 `display:flex` 的左右兩欄（78px 側欄 ＋ 內容）。手機上那 78px
   吃掉螢幕的 19%，句子被擠成四行；200 句實測，側欄整條讓開有 76.5% 的
   句子少一行，而只瘦成 52px 只有 24.5%——所以是整條讓開，不是瘦身。

   ⚠️ **`min-height` 不能換成 `height`**：主頁、單字庫、數據那幾個畫面靠
   body 捲動，寫死高度它們就捲不動了。只有練習與文法是「一個視窗高的
   三段式版面」，它們自己用 calc 扣掉導覽列（見 #screen-practice）。 */
.app {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  min-height: var(--vvh, 100dvh);
}

/* sticky top:0 ＝ 內容捲動時導覽列留在畫面上，跟從前側欄 sticky 是同一個用意。
   z-index 40 在 Gina 召喚鍵（55）之下：召喚鍵是浮在內容上的，不該被導覽蓋掉。 */
.topnav {
  flex: 0 0 auto;
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 4px;
  padding: 6px 16px;
  background: var(--bg-sunk);
  border-bottom: 1px solid var(--line-soft);
  position: sticky;
  top: 0;
  z-index: 40;
}

/* 招牌是按鈕（點了回單字練習），但不該長得像按鈕——
   平常維持原本的靜態外觀，只在 hover／focus 時提示可點 */
.topnav__mark {
  font-family: var(--font-serif);
  font-size: 17px;
  font-style: italic;
  letter-spacing: .01em;
  color: var(--ink-soft);
  /* 橫排：跟導覽鍵之間留一段，不再是直欄時代的 margin-bottom */
  margin-right: 12px;
  padding: 4px 8px;
  border-radius: 6px;
  white-space: nowrap;
  user-select: none;
  transition: color .15s ease, background-color .15s ease;
}
.topnav__mark:hover { color: var(--ink); background: var(--surface-2); }

/* 本機 dev 版的記號（V 2026-07-31）。只在非 HTTPS 出現，見 app.js 的 init()。
   刻意**不用招牌橙**（--amber*）——那個顏色全站只代表 near_miss（見檔頭），
   多一個用途就少一分辨識度。這裡要的是「看得到」，不是「跳出來」。 */
.topnav__dev {
  font-size: var(--fs-xs);
  font-weight: 600;
  letter-spacing: .1em;
  line-height: 1;
  padding: 3px 6px;
  border-radius: 4px;
  color: var(--ink-faint);
  background: var(--surface-2);
  border: 1px solid var(--line-soft);
  user-select: none;
}
/* 有記號時，招牌與記號要黏成一組：招牌原本的 12px 讓給記號的右緣 */
body.is-dev .topnav__mark { margin-right: 6px; }
body.is-dev .topnav__dev  { margin-right: 12px; }

/* 主題與帳號推到最右。直欄時代靠 .acct-btn 的 margin-top:auto，
   橫排改由這一層負責，兩顆自己不再帶 auto margin（見下面覆寫）。 */
.topnav__tail {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 8px;
}

.empty-note { color: var(--ink-faint); font-size: 15px; }

/* 標籤常駐在 icon **下方**——取代原本的 title 屬性（瀏覽器原生 tooltip 延遲
   0.5–2 秒且時間不固定，是「慢又不一致」的根因）。V 2026-08-02 指定橫排之後
   文字仍然放圖示下面、不要移到右側，所以 flex-direction 維持 column。

   寬度改成內容撐開＋44px 觸控下限：直欄時代寫死 62px，橫排時五顆鍵要跟
   招牌、主題、帳號共用一列，寫死寬度在 402px 會擠爆。 */
.nav-btn {
  min-width: 44px;
  min-height: 44px;
  padding: 4px 8px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  border-radius: var(--radius);
  color: var(--ink-faint);
  position: relative;
  transition: background-color .15s ease, color .15s ease;
}
.nav-btn svg { width: 20px; height: 20px; display: block; }
.nav-btn__label {
  font-size: 13px;
  line-height: 1;
  letter-spacing: .03em;
  white-space: nowrap;
}
.nav-btn:hover { color: var(--ink-soft); background: var(--surface-2); }
.nav-btn[aria-current="page"] {
  color: var(--ink);
  background: var(--surface);
  box-shadow: inset 0 0 0 1px var(--line-soft);
}
/* 目前這一頁的指示條。直欄時代貼在右緣（豎線指向內容區），
   橫排要改成貼在**底緣**（橫線指向下方的內容區）——方向的語意要跟著版型走。 */
.nav-btn[aria-current="page"]::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: -3px;          /* 貼在鍵的下緣內側；再往下會被導覽列的邊框切掉 */
  transform: translateX(-50%);
  width: 20px;
  height: 2px;
  border-radius: 2px;
  background: var(--ink-soft);
}

/* 窄螢幕把導覽列擠進一列：402px 要放招牌＋五顆 44px 的鍵＋主題（登入模式再多
   一顆帳號）。實測不收緊會溢出畫面右緣——最右那顆被切掉一半。
   收緊的只有間距與招牌字級，**44px 觸控下限不動**。 */
@media (max-width: 520px) {
  .topnav { padding: 6px 8px; gap: 2px; }
  .topnav__mark { font-size: 15px; margin-right: 4px; padding: 4px 6px; }
  .topnav__tail { gap: 4px; }
}

/* 文字只在 ≥834px 出現（V 2026-08-02 指定：iPad 直向的寬度）。
   402px 放不下五組「單字練習」這種四字標籤＋招牌＋主題＋帳號，
   所以窄螢幕純圖示——**無障礙不受影響**，.nav-btn 本來就自帶 aria-label。 */
.nav-btn__label { display: none; }
@media (min-width: 834px) {
  .nav-btn__label { display: block; }
  .nav-btn { padding: 5px 12px; }
}

.main {
  flex: 1 1 auto;
  min-width: 0;
  min-height: 0;      /* 沒有這行，練習畫面的內部捲動會被撐破 */
  display: flex;
  flex-direction: column;
}

.wrap {
  width: 100%;
  max-width: 720px;
  margin: 0 auto;
  padding: 0 24px;
}

.screen { flex: 1 1 auto; display: flex; flex-direction: column; }

.screen-head {
  padding-top: 40px;
  padding-bottom: 8px;
}
.screen-head h1 {
  font-size: 17px;
  font-weight: 600;
  letter-spacing: .04em;
  margin: 0 0 4px;
}
.screen-head p {
  margin: 0;
  color: var(--ink-faint);
  font-size: 13px;
}

/* ── 全域訊息列 ─────────────────────────────────────────────── */
/* 置中改用 margin-inline: auto，不再用 translateX(-50%)：
   transform 要整個讓給進出場動畫，兩邊搶同一個屬性會把 toast 甩到畫面左邊。 */
.toast {
  position: fixed;
  left: 0;
  right: 0;
  /* 讓開頁尾（V 2026-08-01 回報：提示疊在「往後／看答案」上，375px 更是整個
     埋進 240px 高的頁尾裡）。--foot-h 是 app.js 量出來的「現在看得到的那個
     頁尾」有多高，沒有頁尾的畫面是 0，加上去就退回原本的 28px。 */
  bottom: calc(var(--foot-h, 0px) + 28px);
  margin-inline: auto;
  width: fit-content;
  max-width: min(560px, calc(100vw - 96px));
  background: var(--surface);
  color: var(--ink);
  border: 1px solid var(--red-line);
  border-left-width: 3px;
  border-radius: var(--radius);
  padding: 10px 16px;
  box-shadow: var(--shadow);
  font-size: 13px;
  z-index: 50;

  /* 這一段是收起狀態，用的是退場時鐘（250ms）；
     加上 .is-open 才切成進場時鐘（350ms）。一個 class 就拿到開關不對稱。 */
  opacity: 0;
  transform: translateY(var(--toast-distance)) scale(var(--toast-scale));
  filter: blur(var(--toast-blur));
  will-change: transform, opacity, filter;
  transition:
    opacity var(--toast-close) var(--toast-ease),
    transform var(--toast-close) var(--toast-ease),
    filter var(--toast-close) var(--toast-ease);
}
.toast.is-open {
  opacity: 1;
  transform: translateY(0) scale(1);
  filter: blur(0);
  transition:
    opacity var(--toast-open) var(--toast-ease),
    transform var(--toast-open) var(--toast-ease),
    filter var(--toast-open) var(--toast-ease);
}
@media (prefers-reduced-motion: reduce) {
  .toast { transition: none !important; }
}

/* ══════════════════════════════════════════════════════════════
   練習畫面
   ══════════════════════════════════════════════════════════════ */
.practice {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
}

/* 頂部：五點熟練度 */
.practice-top {
  padding-top: 28px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
  min-height: 40px;
}

.mastery { display: flex; align-items: center; gap: 7px; }
.mastery__dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--line);
  transition: background-color .25s ease;
}
.mastery__dot.is-on { background: var(--ink-soft); }

.practice-top__meta {
  font-size: 13px;
  color: var(--ink-faint);
  letter-spacing: .04em;
}

/* 卡片本體 — A 版「讀物」排版：句子靠左當主角，字義降級為旁註。
   原本置中＋上下大量留白，空白沒有作用；改成有目的的閱讀版心。 */
.card-area {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  /* 內容短 → 置中；內容長到滿出來 → 自動退回貼齊上緣。
     沒有 safe 的話，置中會把溢出的部分同時推到上下兩側，而上面那半段
     在捲動原點之外（scrollTop 已經是 0 卻還在畫面上方），永遠捲不到——
     揭曉後內容一長，句子本身就被切掉了（V 2026-07-27 回報）。
     兩行都留著：不支援 safe 的瀏覽器至少還是置中。 */
  justify-content: center;
  justify-content: safe center;
  align-items: stretch;
  padding: 8px 0 24px;
}

.card {
  width: 100%;
  text-align: left;
}

/* B 版警示列：同一個錯誤重複出現時才顯示。
   這是 Lingvist 從來沒給過的回饋——commemorate 錯 14 次都打同一個字，五個月沒人告訴他。 */
.repeat-warn {
  display: flex;
  align-items: baseline;
  gap: 8px;
  margin: 0 0 22px;
  padding: 9px 14px;
  border-left: 2px solid var(--amber-line);
  border-radius: 0 var(--radius) var(--radius) 0;
  background: var(--amber-soft);
  color: var(--amber);
  font-size: 13px;
  line-height: 1.6;
}
.repeat-warn b { font-weight: 600; }
.repeat-warn__n {
  flex: 0 0 auto;
  font-variant-numeric: tabular-nums;
  font-size: 13px;
  letter-spacing: .06em;
  opacity: .8;
}

.card__meta {
  display: flex;
  align-items: baseline;
  gap: 12px;
  margin-top: 18px;
  flex-wrap: wrap;
}
.card__gloss {
  font-size: 15px;
  font-style: italic;
  color: var(--ink-soft);
  letter-spacing: .02em;
}
/* 詞性掛在中文提示前面：`n. 樹皮`。用正體、比中文淡一階——
   它是標記不是內容，不該跟提示本身搶。 */
.card__pos {
  font-style: normal;
  font-size: 13px;
  color: var(--ink-faint);
  margin-right: 6px;
  letter-spacing: 0;
}
/* 其它義項：揭曉後才出現，比提示再小一階、再淡一階。
   它是補充，讀完提示才輪到它。 */
.card__others {
  flex-basis: 100%;      /* 自己換行：它是提示的下一行，不是並排的另一塊 */
  font-size: 13px;
  font-style: italic;
  color: var(--ink-faint);
  letter-spacing: .02em;
}
.card__kind {
  display: inline-block;
  font-size: 13px;
  letter-spacing: .08em;
  color: var(--ink-faint);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 1px 9px;
}

/* ── 標籤 chips ────────────────────────────────────────────
   兩段曝光：答題前只掛狀態標籤（.chips 在 practice-top），
   屬性標籤等揭曉後才進 .chips--attr。分開放是因為前者會洩題。 */
.chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  align-items: center;
}
.chips:empty { display: none; }
.chips--attr { margin-top: 4px; justify-content: center; }

.chip {
  font-size: 13px;
  letter-spacing: .05em;
  color: var(--ink-faint);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 1px 9px;
  background: var(--surface);
  white-space: nowrap;
}
/* 熟悉度標籤：中性但比屬性標籤重一階，因為它才是答題時該注意的 */
.chip--state { color: var(--ink-soft); border-color: var(--ink-faint); }
/* 風險：琥珀是本工具的招牌色，用在「這張要留意」剛好 */
.chip--warn {
  color: var(--amber);
  border-color: var(--amber-line);
  background: var(--amber-soft);
}
/* 來源：跟屬性區分開，讓「這個字打哪來的」自成一類 */
.chip--src {
  color: var(--focus);
  border-color: var(--focus);
  background: color-mix(in srgb, var(--focus) 6%, transparent);
}
.chip--act {
  cursor: pointer;
  color: var(--ink-soft);
}
.chip--act:hover { color: var(--ink); border-color: var(--ink-faint); }

/* ── 回看模式 ──────────────────────────────────────────────
   虛線框＋紫色狀態列，讓「我不在作答」一眼可辨。 */
.lookbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  border: 1px dashed var(--focus);
  background: color-mix(in srgb, var(--focus) 5%, transparent);
  border-radius: var(--radius);
  padding: 7px 14px;
  margin-bottom: 22px;
  font-size: 13px;
  color: var(--focus);
}
.lookbar__keys {
  color: var(--ink-faint);
  font-family: var(--font-mono);
  font-size: 13px;
}
.look-typed { font-size: 13px; color: var(--ink-soft); margin-top: 10px; }
.look-typed .d-bad {
  color: var(--red);
  text-decoration: line-through;
  text-decoration-thickness: 1px;
}
.look-answer {
  font-family: var(--font-serif);
  border-bottom: 2px solid var(--green-line);
  padding: 0 .12em 2px;
}

.sentence {
  font-family: var(--font-serif);
  font-size: 32px;
  line-height: 1.95;
  color: var(--ink);
  margin: 0;
  max-width: 30em;
  overflow-wrap: break-word;
  text-wrap: pretty;
}

/* 挖空的 inline input：寬度只由使用者打的字撐開，不洩漏答案長度 */
.blank {
  font-family: var(--font-serif);
  font-size: inherit;
  line-height: 1.2;
  color: var(--ink);
  background: transparent;
  border: 0;
  border-bottom: 2px solid var(--line);
  border-radius: 0;
  padding: 0 .12em 2px;
  margin: 0 .04em;
  width: 7ch;
  text-align: center;
  outline: none;
  transition: border-color .18s ease, color .18s ease, background-color .18s ease;
}
.blank::placeholder { color: transparent; }
.blank:focus { border-bottom-color: var(--ink-soft); }
.blank:focus-visible { outline: none; box-shadow: 0 2px 0 var(--focus); }

.blank.is-near {
  color: var(--amber);
  border-bottom-color: var(--amber-line);
  background: var(--amber-soft);
}
.blank.is-good {
  color: var(--green-answer);
  border-bottom-color: var(--green-answer);
}
.blank.is-bad {
  color: var(--red);
  border-bottom-color: var(--red-line);
  text-decoration: line-through;
  text-decoration-thickness: 1px;
}

/* 揭曉後填回句子的正解 */
.answer-inline {
  font-family: var(--font-serif);
  color: var(--green-answer);
  border-bottom: 2px solid var(--green-answer);
  padding: 0 .12em 2px;
}
.answer-inline .d-bad { color: var(--red); font-weight: 600; }

/* 回饋區 */
.feedback {
  min-height: 96px;
  margin-top: 30px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
}

.hint {
  font-size: 15px;
  line-height: 1.7;
  border-radius: var(--radius);
  max-width: 30em;
}
/* 判定短語（答對了／不對再試一次）不加內距：它是中文翻譯的標頭，
   要貼著下面那句、左緣也要跟句子切齊，不是浮在空白裡的一塊。 */
.hint.is-good, .hint.is-bad { padding: 0; }
.hint.is-near {
  color: var(--amber);
  background: var(--amber-soft);
  border: 1px solid var(--amber-line);
  padding: 8px 14px;   /* 只有這一狀態有底色，才需要內距撐開 */
}
.hint.is-good { color: var(--green); }
.hint.is-bad { color: var(--red); }

.reveal {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  max-width: 34em;
}
/* 整句中文：答對時它就是這一格唯一的內容，不是註解（V 2026-07-27 回報：
   離「答對了」太遠、字又淡，視覺上找不到）。拉到 18px、吃滿 --ink，
   跟上面的判定短語（14px 綠字）拉開層級，但仍在正解 27px 之下。 */
.reveal__zh {
  font-size: 20px;
  color: var(--ink);
  line-height: 1.75;
}
.reveal__typed {
  font-family: var(--font-mono);
  font-size: 13px;
  color: var(--ink-faint);
  letter-spacing: .01em;
}
.reveal__typed .d-bad {
  color: var(--red);
  text-decoration: line-through;
  text-decoration-thickness: 1px;
}
/* 「你打的 X 也對，這張卡教的是 Y」（V 2026-08-01）。
   它不是提示也不是錯誤，是一句補充事實——所以走 ink-faint 這一層，
   跟下面「這樣說也可以」的註解同級，不跟 20px 的中譯搶。
   兩個字用 serif 撐出來，掃過去就知道是在比哪兩個字。 */
.reveal__also {
  font-size: 13px;
  line-height: 1.7;
  color: var(--ink-faint);
  max-width: 34em;
}
.reveal__also b {
  font-family: var(--font-serif);
  font-size: 15px;
  font-weight: 400;
  color: var(--ink-soft);
}
.reveal__answer {
  font-family: var(--font-serif);
  font-size: var(--answer-size);
  color: var(--ink);
}
.reveal__answer .d-bad { color: var(--red); font-weight: 600; }

/* 答完後的兩個去處：再聽一次 / 繼續。
   答對不再自動翻頁（V 2026-07-26），所以這一排要一眼看得到。 */
.answer-actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }

/* 三顆語音鍵自成一組，才搬得動（辨析卡會把它整組移到提問列上方）。
   間距與換行行為照抄 .answer-actions，搬走前後在一般卡上看起來一模一樣。 */
.answer-actions__audio { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; position: relative; }

/* 搬到卡片裡（句子底下）時要有自己的上下距離；留在頁尾時由 .answer-actions
   的 gap 管，所以只給 #card 底下的那份。 */
#card > .answer-actions__audio { margin: 14px 0 2px; }

/* 「還不能聽」的就地提示。全域 toast 釘在頁尾，而這三顆在辨析卡已經搬到
   句子底下——中間差了整個畫面高度，V 2026-08-01 回報「下方提示不明顯」。
   回饋要出現在動作發生的地方，所以這一則不走 toast，貼著按鈕自己講。

   視覺語言（底色／紅左邊框／圓角／陰影）刻意跟 .toast 一致，是同一種東西。

   ⚠️ **不再自動淡出**（V 2026-08-02，iPhone 實測）。它最常講的那件事是
   「去哪裡下載語音」＝三個要照著做的步驟，2.4 秒讀不完。改成按 ✕ 或換卡
   才關，所以右上角有一顆真的關閉鍵。

   ⚠️ **一般卡改成佔位（in-flow），只有辨析卡維持絕對定位**（同一批改動）。
   從前一律絕對定位，理由是「不要把下面的東西往下推」，而它 2.4 秒就消失，
   蓋住一下無所謂。現在它會一直待著——實測在 402px 上整塊蓋住「照著上面
   打一次就放你走」那格重打輸入框，而那正是使用者接下來非做不可的事。
   一般卡的這則**只在揭曉後才可能出現**（答題前按語音鍵得到的是別句話），
   所以佔位不會製造「揭曉時跳動」，往下推是安全的。
   辨析卡不能比照：那裡三顆在**答題前**就擺著反灰，佔位會把提問與選項
   往下推，正是 §5.20／`0391eae` 修掉的那種跳動。 */
.audio-hint {
  flex: 1 0 100%;         /* 父層是 flex 列，要自己整條佔一行 */
  margin-top: 8px;
  max-width: 100%;
  background: var(--surface);
  color: var(--ink);
  border: 1px solid var(--red-line);
  border-left-width: 3px;
  border-radius: var(--radius);
  padding: 9px 44px 10px 12px;   /* 右邊留給 ✕ */
  box-shadow: var(--shadow);
  font-size: 13px;
  line-height: 1.5;
  position: relative;     /* ✕ 靠它定位 */
  z-index: 3;

  opacity: 0;
  transform: translateY(calc(var(--toast-distance) * -0.5)) scale(var(--toast-scale));
  filter: blur(var(--toast-blur));
  transition:
    opacity var(--toast-close) var(--toast-ease),
    transform var(--toast-close) var(--toast-ease),
    filter var(--toast-close) var(--toast-ease);
}
.audio-hint.is-open {
  opacity: 1;
  transform: translateY(0) scale(1);
  filter: blur(0);
  transition:
    opacity var(--toast-open) var(--toast-ease),
    transform var(--toast-open) var(--toast-ease),
    filter var(--toast-open) var(--toast-ease);
}
@media (prefers-reduced-motion: reduce) {
  .audio-hint { transition: none !important; }
}

/* 辨析卡：維持絕對定位、不佔位（理由見上面那段的第二個 ⚠️）。
   pointer-events 也維持 none——浮在選項上面時不可以吃掉點擊，
   只有 ✕ 例外（.audio-hint__x 自己開回 auto）。 */
#contrast-mount .audio-hint {
  position: absolute;
  top: 100%;
  left: 0;
  margin-top: 6px;
  pointer-events: none;
}

/* 內容分層：先講「怎麼回事」，再講「為什麼寧可沒聲音」，最後才是照著做的步驟。
   步驟一定要排成清單——擠成一段的話，使用者離開 app 去設定時記不住第幾步。 */
.audio-hint__body { display: block; }
.audio-hint__title { display: block; font-weight: 400; color: var(--ink); }
.audio-hint__why,
.audio-hint__note { display: block; color: var(--ink-soft); font-size: 12.5px; }
.audio-hint__why { margin-top: 2px; }
.audio-hint__note { margin-top: 6px; }
.audio-hint__steps {
  margin: 6px 0 0;
  padding-left: 18px;
  color: var(--ink);
  font-size: 12.5px;
}
.audio-hint__steps li { margin: 1px 0; }

/* 唯一吃得到指標事件的部分。44px 是觸控下限，不因為它是「一顆小 ✕」就縮水。 */
.audio-hint__x {
  position: absolute;
  top: 4px;
  right: 4px;
  width: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: transparent;
  color: var(--ink-faint);
  font-size: 13px;
  line-height: 1;
  cursor: pointer;
  pointer-events: auto;
}
.audio-hint__x:hover { color: var(--ink); border-color: var(--focus); }
@media (pointer: coarse) {
  .audio-hint__x { width: 44px; height: 44px; }
  .audio-hint { padding-right: 56px; }
}

/* ── 標籤說明小卡（V 2026-08-02）────────────────────────────────────
   點得到的標籤（.chip--tap）長得跟原本一樣，只多一條虛線底提示「這顆可以點」。
   全部用 --line 這種既有 token，不新增顏色。 */
.chip--tap {
  cursor: pointer;
  font: inherit;
  font-size: 12px;
  border-bottom-style: dashed;
  transition: color .15s ease, border-color .15s ease;
}
.chip--tap:hover { color: var(--ink); border-color: var(--focus); }
.chip--tap[aria-expanded="true"] { color: var(--ink); border-color: var(--focus); }
@media (pointer: coarse) {
  /* 觸控下限。標籤本身矮，靠上下 padding 撐開自己的點擊盒，
     不用 min-height——那會把整列標籤的高度一起拉高。 */
  .chip--tap { padding-top: 10px; padding-bottom: 10px; }
}

.chip-pop {
  position: fixed;
  z-index: 58;              /* 在 Gina 召喚鍵（55）之上、遮罩（59）之下 */
  max-width: min(330px, calc(100vw - 16px));
  padding: 10px 13px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  box-shadow: var(--shadow);
  font-size: 12.5px;
  line-height: 1.75;
  color: var(--ink);
}
.chip-pop__t { display: block; font-weight: 400; margin-bottom: 3px; }
.chip-pop__b { display: block; color: var(--ink-soft); font-size: 12px; }

/* 這台裝置根本沒有可用語音（§5.18 不退回 compact）時，佔位但不畫出來——
   高度照留所以揭曉時不會跳，但也不給一個之後不會兌現的承諾。 */
.answer-actions__audio.is-blank { visibility: hidden; }

.continue-btn {
  font-size: 13px;
  color: var(--ink-soft);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 5px 18px;
  background: var(--surface);
  transition: border-color .15s ease, color .15s ease;
}
.continue-btn:hover { color: var(--ink); border-color: var(--ink-faint); }

.replay-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  color: var(--ink-faint);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 5px 14px;
  background: transparent;
  transition: border-color .15s ease, color .15s ease;
}
.replay-btn:hover { color: var(--ink-soft); border-color: var(--ink-faint); }

/* 還不能按（辨析卡答題前）。**刻意不用 disabled 屬性**：disabled 的按鈕
   不發 click 事件，點下去連「為什麼還不能按」都說不出口——而說得出口
   正是這顆灰按鈕存在的理由。所以只做視覺上的反灰，事件照收，
   由 playSentence()／playWord() 那條既有的路吐出提示。 */
.replay-btn[aria-disabled="true"] {
  color: var(--ink-faint);
  border-color: var(--line);
  opacity: .45;
  cursor: default;
}
.replay-btn[aria-disabled="true"]:hover { color: var(--ink-faint); border-color: var(--line); }
.replay-btn svg { width: 15px; height: 15px; flex: 0 0 auto; }

/* 熱鍵只印給有實體鍵盤的人看——跟上面 .foot-key 同一條規矩（V 2026-08-01）。
   起因是辨析卡把這三顆搬到提問列上方之後，375px 上「慢速再聽一次 ⌘⇧'」
   一顆就撐滿一行、三顆疊成三列，把選項往下推了 127px；而 iPad／iPhone 上
   那個 ⌘ 根本按不出來，等於用最貴的版面印一行沒人用得到的字。
   藏掉的只有提示，**熱鍵本身照樣有效**。 */
.replay-btn kbd { display: none; }
@media (hover: hover) and (pointer: fine) {
  .replay-btn kbd { display: inline-block; }
}
/* 正在唸：讓他知道現在是機器在講，不是他該按第二次 */
.replay-btn.is-playing { color: var(--green); border-color: var(--green); }
.replay-btn.is-playing svg { animation: replay-pulse 1.1s ease-in-out infinite; }
@keyframes replay-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .35; } }


/* 頑固卡辨義說明 */
.leech {
  width: 100%;
  max-width: 34em;
  text-align: left;
  background: var(--surface);
  border: 1px solid var(--line);
  border-left: 3px solid var(--amber-line);
  border-radius: var(--radius);
  padding: 14px 18px;
  box-shadow: var(--shadow);
}
.leech__title {
  font-size: 13px;
  letter-spacing: .06em;
  color: var(--amber);
  margin-bottom: 6px;
}
.leech__body {
  font-size: 17px;
  line-height: 1.85;
  color: var(--ink);
}
.leech__body em {
  font-family: var(--font-serif);
  font-style: italic;
}
.leech__close {
  margin-top: 10px;
  font-size: 13px;
  color: var(--ink-faint);
}

/* 底部列 */
.practice-foot {
  border-top: 1px solid var(--line-soft);
  background: var(--bg);
  padding: 12px 0 18px;
}
.practice-foot__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
}

.progress { min-width: 160px; flex: 1 1 200px; }
.progress__label {
  font-size: 13px;
  color: var(--ink-faint);
  display: flex;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 6px;
}
/* ── 今日軌跡：一格一題 ────────────────────────────────────
   取代原本的單線進度條。一個元件同時給進度感與歷史，
   ⌘PageUp／PageDown 就是在這條上移動——快捷鍵要有看得見的對應物。 */
.track-wrap { position: relative; }
.track {
  display: flex;
  gap: 2px;
  align-items: flex-end;
  height: 14px;
  /* 上下各撐 10px 的抓握區再用負 margin 收回來：視覺還是 14px 高，
     但手指（和滑鼠）有 34px 可以抓。touch-action 必須關掉，
     否則瀏覽器會先把橫向拖曳判成捲動，拖到一半被搶走。 */
  box-sizing: content-box;
  padding: 10px 0;
  margin: -10px 0;
  touch-action: none;
  cursor: pointer;
}

/* 停留／拖曳時浮出那一題的單字。原生 title tooltip 延遲 0.5–2 秒
   且時間不固定，拖曳時完全跟不上，所以自己畫一個。 */
.track-tip {
  position: absolute;
  bottom: calc(100% + 10px);
  transform: translateX(-50%);
  z-index: 20;
  white-space: nowrap;
  pointer-events: none;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 7px;
  padding: 4px 10px;
  font-size: 13px;
  color: var(--ink-soft);
  box-shadow: var(--shadow);
}
.seg {
  flex: 1 1 auto;
  min-width: 2px;
  height: 8px;
  border-radius: 2px;
  background: var(--line-soft);
  border: 0;
  padding: 0;
  transition: height .15s ease, background-color .15s ease;
}
/* 三個顏色只講學習狀況，不講題型（V 2026-07-31）：
   綠＝一次答對　黃＝提醒後改對　紅＝沒有自己答對（含看了答案）。
   顏色語意的完整說明在 app.js 的 segClass()。
   舊的 .seg--revealed（看了答案專用的黃）與 .seg--contrast（辨析卡的紫）
   已移除——前者併進紅，後者是題型標記而且會蓋掉判定。 */
.seg--ok       { background: var(--green-line); }
.seg--bad      { background: var(--red-line); }
.seg--rescued  { background: var(--amber-line); }
.seg--done     { cursor: pointer; }
.seg--done:hover { height: 12px; }
/* 當前這題：最高、最深，一眼看得出「我在這裡」 */
.seg--current {
  height: 14px;
  background: var(--ink);
  border-radius: 3px;
}
/* 正在回看的那一格：空心紫框，跟「當前」分得開 */
.seg--looking {
  height: 14px;
  background: var(--surface);
  border: 2px solid var(--focus);
  border-radius: 3px;
}
.seg:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }

.speak-row { display: flex; gap: 8px; }

kbd {
  font-family: var(--font-mono);
  font-size: 13px;
  color: var(--ink-faint);
  border: 1px solid var(--line);
  border-bottom-width: 2px;
  border-radius: 4px;
  padding: 0 4px;
  background: var(--surface-2);
}

/* 頁尾軟鍵（soft keys）：送出／答案／提示／上一題／下一題。
   2026-07-26 這排原本只是文字提示，且 ≤720px 直接整排隱藏——
   隱藏掉的不只是提示，「答案」與「上一題」（當年叫看答案／回看）的唯一入口
   也跟著消失，
   手機上等於沒有這兩個功能。改成真按鈕，熱鍵印在自己身上（V 2026-07-27）。 */
.foot-keys {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
}
.foot-key {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 30px;
  padding: 4px 10px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--surface);
  color: var(--ink-faint);
  font-size: 13px;
  white-space: nowrap;
  transition: color .15s ease, border-color .15s ease;
}
.foot-key:hover { color: var(--ink); border-color: var(--focus); }
/* 反灰（目前只有「下一題」用）。**不是 disabled 屬性**——那種按鈕連 click
   都不發，點下去說不出「為什麼不能按」。這裡只降對比、照樣可點，
   由 stepLook() 講「先做完這一題，才會有下一題」。 */
.foot-key.is-off { opacity: .42; }
.foot-key.is-off:hover { color: var(--ink-faint); border-color: var(--line); }
/* 熱鍵只印給有實體鍵盤的人看。
   （2026-08-02：「跳過」整顆拿掉了，它那條 Esc 提示跟著消失。） */
.foot-key kbd { display: none; }
@media (hover: hover) and (pointer: fine) {
  .foot-key kbd { display: inline-block; }
}

/* ── 五顆排不進一列：窄螢幕收起「送出」（V 2026-08-02 拍板走甲）─────────
   iPhone 17（402px）實測：扣掉 78px 導航軌與 16px×2 內距，這一排只剩 292px。
   四顆 274px 剛好，第五顆一定掉到第二列——而靠右對齊讓剩下的自由空間全部
   堆在左邊，看起來就像「空了一個位子卻把送出擠下去」（V 回報）。

   收的是送出，因為**作答時它是多餘的**：軟鍵盤的 ↵ 與它走同一條 doPrimary()。
   收起來之後四顆一列、頁尾少 52px，那 52px 直接還給鍵盤彈出時的句子（見
   .kb-open 那段）。

   例外是回看：那時它改叫「回到現在」，而手機沒有 Esc，那是唯一的出口鍵，
   所以 .is-look（app.js 的 syncFootKeys 掛上去）要把它放回來。回看時
   提示／答案本來就收起來，三顆仍是一列。

   斷點用 640px 跟 .wrap 的內距同一個，不用 pointer: coarse——
   有實體鍵盤的窄視窗桌機同樣按得到 Enter，沒有理由長出另一套版面。
   ⚠️ 要排在 .foot-key 的 display: inline-flex 之後才蓋得掉它。 */
@media (max-width: 640px) {
  #key-submit { display: none; }
  .foot-keys.is-look #key-submit { display: inline-flex; }
}
/* 極窄螢幕（≤400px）：頁尾不准吃掉半個畫面 ────────────────────────
   320px 實測（760px 高的視窗，單字練習）：頁尾原本 375px＝畫面的 49%，
   .foot-keys 自己 201px、五顆鍵各佔一行。原因是熱鍵提示——「往後 ⌘PageDown」
   整顆撐到 134px，而這裡可用寬度只有 210px（左邊 78px 導航軌先吃掉 24%），
   所以一行放不下第二顆。頁尾一高，Gina 召喚鍵吃的 --foot-h 就把它推進卡片區，
   那是 QA 看到「按鈕蓋住填答框」的真正來路——不是 FAB 定位錯，是頁尾太高。
   改完 240px＝32%，FAB 回到頁尾上方。

   刻意不做的兩件事：
   · 不縮 44px 觸控下限（下面 pointer: coarse 那條）——那是無障礙下限。
   · 不藏「在這個裝置上沒有別條路可以走」的鍵。2026-07-26 曾經 ≤720px 整排
     隱藏，結果「答案」與「上一題」在手機上等於沒有入口，是刻意修掉的回歸。
     ⚠️ 2026-08-02 收起「送出」不違反這條，兩個條件都成立才收：
     (a) 它在同一個裝置上有等價入口——軟鍵盤的 ↵ 與它走同一條 doPrimary()；
     (b) 一旦沒有等價入口就放回來——回看時它是唯一的出口鍵（手機沒有 Esc），
         所以 .is-look 會把它顯示回來。當年被藏掉的那兩顆兩個條件都不成立。
   所以改的只有熱鍵提示與間距：≤400px 印不下 ⌘PageDown 這種字，
   熱鍵本身照樣有效，只是不印出來。

   **順序有意義，不要往前搬**：這一段必須同時排在上面 hover/fine 那條
   （才蓋得掉 kbd 的 display）與下面 pointer: coarse 那條（才蓋得掉 padding）
   之後。三段選擇器權重相同，純靠後者勝出。

   已知限制：真手機（pointer: coarse）熱鍵本來就藏著，所以這段省不到那 125px，
   實測 270px → 268px，幾乎沒動。那邊的高度是「五顆 44px 的鍵在 210px 裡排不成
   一行」造成的：五顆就算縮到最小也要 264px，塞不進 210px，2 列是硬下限。
   要真的降下來得先讓 78px 導航軌在窄螢幕收起來——那是導航的設計決定，
   不在這個修正的範圍內。 */

/* 觸控：吃滿 44px 最小點擊區 */
@media (pointer: coarse) {
  .foot-key { min-height: 44px; padding: 0 14px; font-size: 13px; }
}

/* 每日題數＋語音口音：桌機照舊並排，≤400px 收進「設定」鍵後面。
   它們是設定不是每題的控制項，卻在 320px 各自佔掉一整列（29px＋25px）。
   實測頁尾 268px → 235px（35% → 31%），是導航軌那條路的 1.5 倍效果，
   而且不必把五個導航變成純圖示。 */
.foot-set { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
#key-set { display: none; }           /* 只在極窄螢幕才需要這顆 */

@media (max-width: 400px) {
  .foot-key kbd { display: none; }
  /* 窄視窗的桌機也一樣印不下（真手機走 pointer: coarse 那條，本來就藏著）。
     位置跟 .foot-key 一起，是為了守住上面那段註解講的順序：這一段必須排在
     hover/fine 那條之後才蓋得掉它。 */
  .replay-btn kbd { display: none; }
  .foot-keys { gap: 6px; }
  .foot-key { padding: 0 10px; gap: 0; }

  #key-set { display: inline-flex; }
  .foot-set { display: none; }
  .foot-set.is-open { display: flex; width: 100%; }

  /* 2026-07-31 這裡曾把 78px 的導航軌瘦成 52px 純圖示。2026-08-02 導覽整條
     搬到頂部之後它一點寬都不佔了，那段規則連同 `.nav-btn { width: 48px }`
     一起作廢——標籤的顯示條件改由上面 `@media (min-width: 834px)` 決定，
     不再跟這個斷點綁在一起（V 指定 834＝iPad 直向寬）。 */
}

/* ── 觸控裝置：主要動作靠右，拇指才碰得到（V 2026-07-31）──────────────
   iPad 834px 實測：送出原本在整排**最左邊**，右緣距螢幕右側 658px——
   單手拿著平板時右拇指根本搆不到，而最右邊那顆反而是「下一題」這種次要導航。
   手機也一樣：拇指的自然落點在右下角，那裡該放最常按的東西。

   兩件事一起做才有效：整排靠右對齊，以及把送出排到最後。
   只做對齊的話送出仍然是那一排的第一顆，還是離右緣最遠。
   換行時也對：靠右對齊讓每一列都貼右緣，送出在最後一列的最右＝右下角。

   上一題／下一題維持彼此的自然順序（上一題在前），只是整組讓到左邊去。
   回看模式下 syncFootKeys() 會把送出換成「回到現在」——它仍然是主要動作，
   仍然在最右，語意一致。

   **所有寬度都套，不只觸控**（V 2026-07-31 更正）。第一版鎖在 pointer: coarse，
   理由是「桌機滑鼠搆得到，由左至右＝由主到次讀起來自然」——那個理由讓同一個
   工具在兩種裝置上長出兩套邏輯，V 否決了：版面可以隨 RWD 調整，但主要動作
   在哪一側這種**原則**必須一致。
   而且文法畫面本來就是對的——#gr-submit 一直排在 .gr-foot__spacer 之後、
   在所有寬度都靠右。所以練習畫面才是那個例外，不是桌機需要例外。

   只涵蓋「頁尾動作列」這個脈絡（練習＋文法）。主頁卡片的「再練一輪」與
   單字庫的「加入」是不同模式（卡片 CTA、緊接輸入框的表單鍵），硬套會把
   「再練一輪」推到「一組 N 張」右邊，反而不合理——V 2026-07-31 確認不動。 */
.practice-foot .foot-keys { justify-content: flex-end; }
#key-back   { order: 1; }
#key-fwd    { order: 2; }
#key-reveal { order: 3; }
#key-submit { order: 4; }   /* 2026-08-02：#key-stuck 已移除，序號往前補 */

/* ── 觸控：44px 下限要套滿，不能只套軟鍵（V 2026-07-31 review）────────
   上面那條 `@media (pointer: coarse) { .foot-key { min-height: 44px } }`
   從 2026-07-27 就在，但它只管軟鍵。iPhone 390px 實測，其他互動元素全部漏掉：
     填答框 133×33 ／ 主題鍵 20×20 ／ 語音三顆 ×36 ／ 跳過 74×31 ／ 招牌 52×31
   填答框是其中最要緊的——**iOS Safari 上 focus() 沒有使用者手勢不會叫出鍵盤**，
   所以每一題他都得手動點那個框，那不是備援路徑而是主要路徑。 */
@media (pointer: coarse) {
  /* 填答框：靠上下 padding 撐開，不用 min-height——它是句子裡的 inline 元素，
     給高度會把整行的行高一起拉高。padding 撐開的是自己的點擊盒。 */
  .blank { padding-top: 6px; padding-bottom: 8px; }

  .replay-btn { min-height: 44px; }
  .topnav__mark { min-height: 44px; }

  /* 軌跡格：50 格擠在 210px 裡，每格只有 4px 寬——**橫向精度是補不回來的**
     （那正是 bindTrack 做拖曳掃描的原因）。這裡只擴大縱向落點：手指落在
     軌跡那一條帶上就算數，不必壓在 8px 高的細線上。
     用 ::after 而不是改 height，才不會把格子本身畫粗、破壞疏密的視覺訊息。 */
  .seg { position: relative; }
  .seg::after {
    content: '';
    position: absolute;
    inset: -14px 0;          /* 只上下長，左右不長：長了會蓋到隔壁格 */
  }

}

/* 空狀態 / 完成 */
.empty {
  text-align: center;
  padding: 60px 0;
  color: var(--ink-soft);
}
.empty h2 {
  font-family: var(--font-serif);
  font-size: 24px;
  font-weight: 400;
  margin: 0 0 10px;
  color: var(--ink);
}
.empty p { margin: 0 0 20px; font-size: 15px; }

/* ══════════════════════════════════════════════════════════════
   共用元件
   ══════════════════════════════════════════════════════════════ */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 13px;
  border: 1px solid var(--line);
  background: var(--surface);
  color: var(--ink);
  border-radius: var(--radius);
  padding: 7px 16px;
  transition: border-color .15s ease, background-color .15s ease;
}
.btn:hover { border-color: var(--ink-faint); }
.btn:disabled { opacity: .5; cursor: default; }
.btn--primary {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--bg);
}
.btn--primary:hover { opacity: .88; border-color: var(--ink); }

.field {
  width: 100%;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 8px 12px;
  color: var(--ink);
}
.field:focus { outline: none; border-color: var(--focus); box-shadow: 0 0 0 3px hsl(252 62% 54% / .14); }
textarea.field {
  min-height: 190px;
  resize: vertical;
  font-family: var(--font-serif);
  font-size: 17px;
  line-height: 1.9;
}

.row { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.grow { flex: 1 1 200px; min-width: 0; }
.muted { color: var(--ink-faint); font-size: 13px; }
.mono { font-family: var(--font-mono); }
.serif { font-family: var(--font-serif); }

.table-wrap {
  overflow-x: auto;
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  background: var(--surface);
  -webkit-overflow-scrolling: touch;
}
table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
  white-space: nowrap;
}
th, td {
  text-align: left;
  padding: 8px 12px;
  border-bottom: 1px solid var(--line-soft);
}
th {
  font-weight: 600;
  font-size: 13px;
  letter-spacing: .06em;
  color: var(--ink-faint);
  background: var(--surface-2);
  position: sticky;
  top: 0;
}
tbody tr:last-child td { border-bottom: 0; }
tbody tr:hover td { background: var(--surface-2); }
td.lemma { font-family: var(--font-serif); font-size: 15px; }
td.num { text-align: right; font-variant-numeric: tabular-nums; }

.pill {
  display: inline-block;
  font-size: 13px;
  border-radius: 999px;
  padding: 1px 8px;
  border: 1px solid var(--line);
  color: var(--ink-faint);
}
.pill--good { color: var(--green); border-color: var(--green-line); background: var(--green-soft); }
.pill--bad  { color: var(--red);   border-color: var(--red-line);   background: var(--red-soft); }

/* ── 加字畫面 ───────────────────────────────────────────────── */
.add-results { margin-top: 20px; display: flex; flex-direction: column; gap: 6px; }
.add-result {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 7px 12px;
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  background: var(--surface);
  font-size: 13px;
}
.add-result__word { font-family: var(--font-serif); font-size: 15px; }

/* ── 數據畫面 ───────────────────────────────────────────────── */
.stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(158px, 1fr));
  gap: 12px;
  margin-top: 8px;
}
.stat {
  background: var(--surface);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-lg);
  padding: 16px 18px;
}
.stat--hero { border-color: var(--amber-line); }
.stat__label {
  font-size: 13px;
  color: var(--ink-faint);
  margin-bottom: 6px;
  letter-spacing: .03em;
}
.stat__value {
  font-size: 24px;
  font-variant-numeric: tabular-nums;
  line-height: 1.2;
  color: var(--ink);
}
.stat--hero .stat__value { color: var(--amber); }
.stat__note { font-size: 13px; color: var(--ink-faint); margin-top: 5px; }

.section { margin-top: 34px; }
.section > h2 {
  font-size: 13px;
  font-weight: 600;
  letter-spacing: .05em;
  color: var(--ink-soft);
  margin: 0 0 10px;
}

/* ── 每週學習量折線（V 2026-07-27 拍板，取代日曆熱力圖）────────────
   色票沿用 --cal-*：淡（--cal-1）＝Lingvist 匯入、亮（--cal-4）＝本系統，
   跟舊熱力圖「淡色＝匯入」是同一套語意，所以 designsystem 的色票不必動。
   SVG 用 viewBox 縮放，窄螢幕自己等比縮，不需要橫向捲動容器。 */
.weekly { width: 100%; }
.weekly svg { display: block; width: 100%; height: auto; overflow: visible; }
.weekly .grid { stroke: var(--line); stroke-width: 1; opacity: .55; }
.weekly .ylab,
.weekly .xlab { fill: var(--ink-faint); font-size: 13px; font-family: var(--font-ui); }
.weekly .ylab { text-anchor: end; }
.weekly .xlab { text-anchor: middle; }
.weekly .ln {
  stroke: var(--cal-1);
  stroke-width: 1.8;
  stroke-linecap: round;
  fill: none;
}
.weekly .ln--own { stroke: var(--cal-4); stroke-width: 2.2; }
/* 本週還沒過完：虛線＋空心點，免得末端那一段看起來像退步 */
.weekly .ln--partial { stroke-dasharray: 3 3; }
.weekly .dot { fill: var(--cal-1); }
.weekly .dot--partial { fill: var(--surface); stroke: var(--cal-4); stroke-width: 2; }
.weekly .hit { fill: transparent; }

.chart-legend {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  color: var(--ink-faint);
  margin-top: 8px;
  flex-wrap: wrap;
}
/* 圖例的線段樣本：用 border-top 畫，跟 SVG 裡的線同色 */
.chart-legend .k {
  display: inline-block;
  width: 22px;
  height: 0;
  border-top: 2px solid var(--cal-1);
  margin: 0 2px;
}
.chart-legend .k--own { border-top-color: var(--cal-4); }

.leech-item {
  padding: 10px 14px;
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  background: var(--surface);
  margin-bottom: 6px;
  font-size: 13px;
  display: flex;
  align-items: baseline;
  gap: 10px;
  flex-wrap: wrap;
}
.leech-item__lemma { font-family: var(--font-serif); font-size: 15px; }
.leech-item__wrong { color: var(--red); font-family: var(--font-mono); font-size: 13px; }

/* ── 載入中 ─────────────────────────────────────────────────── */
.loading {
  text-align: center;
  color: var(--ink-faint);
  font-size: 13px;
  padding: 48px 0;
}

/* ── 窄螢幕 ─────────────────────────────────────────────────── */
@media (max-width: 640px) {
  .wrap { padding: 0 16px; }
  .sentence { font-size: 24px; line-height: 1.95; }
  .practice-foot__row { justify-content: center; }
}

/* ── 尊重減少動態偏好 ───────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
}


/* ── 語音選擇（頁尾）──────────────────────────────────────────
   句子語音以現場合成為主，可切口音／性別／年齡，或每張隨機。 */
.voice-pick { display: flex; align-items: center; gap: 7px; }
.voice-pick select {
  font-size: 13px;
  color: var(--ink-soft);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 3px 8px;
  max-width: 148px;
}
.voice-now {
  font-size: 13px;
  color: var(--ink-faint);
  min-width: 8ch;
  font-variant-numeric: tabular-nums;
}
@media (max-width: 720px) { .voice-now { display: none; } }


/* 頁尾在加入語音選擇後元素變多，重新安排避免擠壓 */
.practice-foot__row {
  flex-wrap: wrap;
  row-gap: 10px;
}
.speak-row {
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}
/* 可以縮：不然四顆軟鍵會撐成一條不換行的長列，窄螢幕直接溢出到畫面外。
   min-width:0 是必要的——flex 子項預設 min-width:auto，光給 flex-shrink 不會縮。 */
.foot-keys { flex: 1 1 auto; min-width: 0; }
.voice-pick { flex: 0 1 auto; min-width: 0; }
@media (max-width: 900px) {
  .practice-foot__row { flex-direction: column; align-items: stretch; }
  .speak-row { justify-content: flex-start; }
  /* 橫排時 flex-basis 200px 是寬度，直排時它會變成「高度」——
     頁尾因此憑空多出 200px 空白。直排就交回內容高度。 */
  .progress { flex: 0 0 auto; }
}


/* ── 答錯後的重打區 ───────────────────────────────────────
   只按 Enter 跳過等於沒學到；手打一次才有產出練習。

   要看得見，但不是一塊告示牌（V 2026-07-27 兩輪回報）：原本標籤 12.5px
   又是最淡的灰、貼在頁尾像註解，看不到就會一直按 Enter 想翻頁；
   後來整片鋪琥珀底色又太大、邊緣被切。現在只靠字級與琥珀色的標籤／框線
   來喊人——琥珀在這個 app 裡＝「等你處理」，跟綠（對）、紅（錯）分得開，
   也不會跟 Gina 的紫搶。 */
.retype {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 9px;
  margin-top: 16px;
}
/* 入場動畫掛在一次性的 class 上，不是 .retype 本身：
   放在 .retype 的話，晃動結束後移掉 is-nudge 會讓 animation 屬性換回
   retype-in，整塊又淡入一次（看起來像閃了一下）。 */
.retype.is-in { animation: retype-in .3s ease both; }
/* 底色與內距寫在下面的 #screen-practice #retype——那條規則用 id×2
   把這區黏在畫面底部，優先權比 .retype 高，視覺屬性放這裡會被它蓋掉。
   兩處要一起看。 */
.retype__label {
  font-size: 15px;
  font-weight: 600;
  color: var(--amber);
}
.retype__input {
  font-family: var(--font-serif);
  font-size: 24px;
  color: var(--ink);
  background: var(--surface);
  border: 1.5px solid var(--amber-line);
  border-radius: 8px;
  padding: 6px 12px;
  min-width: 16ch;
  /* input 的預設寬度是 size=20 個字，24px 襯線體在 375px 的手機上會直接
     衝出畫面右緣（實測超出 29px）。16ch 塞得下，所以夾住上限就夠。 */
  max-width: 100%;
  transition: border-color .15s ease, color .15s ease;
}
.retype__input:focus { outline: none; border-color: var(--amber); }
/* 沒有 is-ok 的綠框：打對的回饋是按了 Enter 之後翻頁，不是邊打邊變色。 */
/* 打錯時邊框轉紅，停 --revert-hold 後自己淡回琥珀（開始打字則立刻取消）。
   在這之前紅框只有「減少動態效果」模式才看得到，一般模式打錯是沒有顏色回饋的。 */
.retype__input.is-error {
  border-color: var(--red-line);
  transition: border-color var(--revert-dur) ease-out;
}
/* 提示只在出錯時才有字，所以固定用紅色，不必再切 class；
   淡入淡出交給 .retype.is-error，文字本身由 app.js 設定與清除。 */
.retype__tip {
  font-size: 13px; color: var(--red); min-height: 1em;
  opacity: 0;
  transition: opacity var(--revert-dur) ease-out;
}
.retype.is-error .retype__tip { opacity: 1; }

.retype__input.is-shake,
.retype.is-nudge {
  animation: retype-shake
    calc(var(--shake-dur-a) * 2 + var(--shake-dur-b) * 2) linear;
}
/* 每個停點自帶 timing-function，四段各自跑自己的曲線。
   停點＝累積時間 ÷ 總長（80,80,60,60 → 280ms）：28.57 / 57.14 / 78.57 / 100。
   改段長比例要一起重算，見 :root 的註解。 */
@keyframes retype-shake {
  0%      { transform: translateX(0);                                animation-timing-function: var(--shake-ease); }
  28.57%  { transform: translateX(var(--shake-distance));            animation-timing-function: var(--shake-ease); }
  57.14%  { transform: translateX(calc(var(--shake-distance) * -1)); animation-timing-function: var(--shake-ease); }
  78.57%  { transform: translateX(var(--shake-overshoot));           animation-timing-function: var(--shake-ease); }
  100%    { transform: translateX(0); }
}
@keyframes retype-in {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .retype.is-in { animation: none; }
  .retype__input.is-shake,
  .retype.is-nudge { animation: none !important; transform: none !important; }
  /* 不晃動不代表不給回饋：紅框與提示照樣出現，只是不再淡入淡出 */
  .retype__input.is-error, .retype__tip { transition: none; }
  /* Enter 被擋下時的替代回饋 */
  .retype.is-nudge { box-shadow: 0 0 0 2px var(--amber); }
}


/* ── 詞形資訊（揭曉後）─────────────────────────────────────── */
.morph {
  margin-top: 16px;
  padding-top: 14px;
  border-top: 1px solid var(--line-soft);
  display: flex;
  flex-direction: column;
  gap: 9px;
  text-align: left;
}
.morph__head { display: flex; align-items: baseline; gap: 9px; flex-wrap: wrap; }
.morph__lemma { font-family: var(--font-serif); font-size: 20px; font-weight: 600; }
.morph__pos {
  font-size: 13px; font-style: italic; color: var(--ink-soft);
}
.morph__cefr {
  font-size: 13px; letter-spacing: .08em; color: var(--ink-faint);
  border: 1px solid var(--line); border-radius: 999px; padding: 1px 7px;
}
.morph__forms { display: flex; gap: 14px; flex-wrap: wrap; }
.morph__form { display: flex; align-items: baseline; gap: 5px; }
.morph__form-k { font-size: 13px; color: var(--ink-faint); }
.morph__form-v { font-family: var(--font-serif); font-size: 15px; color: var(--ink-soft); }
.morph__usage { display: flex; align-items: baseline; gap: 7px; flex-wrap: wrap; }
.morph__usage-k { font-size: 13px; color: var(--ink-faint); flex: 0 0 auto; }
.morph__chip {
  font-family: var(--font-serif);
  font-size: 13px;
  color: var(--ink-soft);
  background: var(--surface-2);
  border: 1px solid var(--line-soft);
  border-radius: 5px;
  padding: 1px 7px;
}
.morph__note { font-size: 13px; color: var(--ink-faint); line-height: 1.65; }

/* 揭曉區與詞形資訊靠左，跟句子的閱讀版心對齊
   （原本置中，跟 A 版靠左排版對不上） */
.feedback { align-items: flex-start; text-align: left; }
/* align-items 也要跟著改：只設 text-align 的話，flex 子元素仍吃基本規則的
   align-items:center，中文翻譯會被推到版心中央，跟上面的「答對了」對不齊。 */
.reveal { text-align: left; align-items: flex-start; width: 100%; max-width: 34em; }
.morph { width: 100%; max-width: 34em; }
/* 面板跟句子的閱讀版心切齊，不要橫貫整個畫面 */
.retype { align-self: flex-start; width: 100%; max-width: 34em; }


/* ── 延伸例句（揭曉後）────────────────────────────────────────
   同一個字換成 ACG／電影／影集情境的第二句，幫這個字黏得住。 */
.extra-ex {
  margin-top: 16px;
  padding: 12px 0 0;
  border-top: 1px solid var(--line-soft);
  width: 100%;
  max-width: 34em;
  text-align: left;
}
.extra-ex__head { display: flex; align-items: baseline; gap: 9px; margin-bottom: 6px; }
.extra-ex__k { font-size: 13px; color: var(--ink-faint); letter-spacing: .04em; }
.extra-ex__src {
  font-size: 13px; color: var(--ink-soft);
  border: 1px solid var(--line); border-radius: 999px; padding: 1px 8px;
}
.extra-ex__en {
  margin: 0 0 4px;
  font-size: 17px;
  line-height: 1.75;
  color: var(--ink-soft);
}
.extra-ex__zh { margin: 0; font-size: 13px; color: var(--ink-faint); line-height: 1.7; }


/* ── 每日目標設定（頁尾）───────────────────────────────────── */
.goal-pick { display: flex; align-items: center; gap: 5px; }
.goal-pick__k { font-size: 13px; color: var(--ink-faint); }
.goal-pick input {
  width: 4.2em;
  font-size: 13px;
  text-align: center;
  color: var(--ink-soft);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 3px 6px;
  font-variant-numeric: tabular-nums;
}
.goal-pick input:focus { outline: none; border-color: var(--ink-faint); color: var(--ink); }

/* ── 今日結束的數據卡 ─────────────────────────────────────── */
.daily-sum {
  margin: 22px auto 0;
  max-width: 30em;
  text-align: left;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface);
  padding: 20px 22px;
}
.daily-sum__row {
  display: flex; align-items: baseline; justify-content: space-between;
  padding: 7px 0; border-bottom: 1px solid var(--line-soft);
}
.daily-sum__row:last-child { border-bottom: 0; }
.daily-sum__k { font-size: 13px; color: var(--ink-faint); }
.daily-sum__v {
  font-family: var(--font-serif); font-size: 20px; color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.daily-sum__v.is-hero { color: var(--amber); }
.daily-sum__note { margin-top: 12px; font-size: 13px; color: var(--ink-faint); line-height: 1.7; }


/* ── 熟練度：藍（生疏）→ 青 → 綠（熟）漸層 ────────────────────
   琥珀維持只代表「差一點」，不拿來當熟練度，避免一個顏色兩種意思。 */
.mastery__label { font-size: 13px; color: var(--ink-faint); letter-spacing: .04em; }
.mastery__dot.is-on.lv1 { background: hsl(220 62% 58%); }
.mastery__dot.is-on.lv2 { background: hsl(200 58% 52%); }
.mastery__dot.is-on.lv3 { background: hsl(178 48% 44%); }
.mastery__dot.is-on.lv4 { background: hsl(160 44% 44%); }
.mastery__dot.is-on.lv5 { background: hsl(148 46% 48%); }
.mastery[title]:hover { cursor: help; }

/* ── 為什麼（詞形／辨析說明）───────────────────────────────── */
/* 說明框搬到中文提示的右邊（V 2026-07-28）。它補充的是「這裡為什麼是這個
   樣子」，貼著提示看才連得起來；原本掉在回饋區裡，跟提示隔著一整段。
   margin-top 拿掉——同一列上不需要，靠 .card__meta 的 baseline 對齊。
   放不下時 .card__meta 的 flex-wrap 會讓它自己掉到下一行。 */
.why {
  padding: 8px 12px;
  border-left: 2px solid var(--line);
  background: var(--surface-2);
  border-radius: 0 var(--radius) var(--radius) 0;
  font-size: 13px;
  line-height: 1.7;
  color: var(--ink-soft);
  max-width: 34em;
  text-align: left;
}
/* 這樣說也可以（V 2026-07-28）：揭曉後列出同一句還能用哪些字、語感差在哪。
   刻意不做成卡片框——它是正解的註腳，不是另一則獨立內容。 */
.alts { text-align: left; max-width: 34em; }
.alts__k {
  display: block;
  font-size: 13px; letter-spacing: .06em;
  color: var(--ink-faint); margin-bottom: 6px;
}
.alts__row {
  display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap;
  padding: 3px 0;
}
.alts__w { font-size: 17px; color: var(--ink-soft); }
.alts__note { font-size: 13px; line-height: 1.7; color: var(--ink-faint); }

.why__ask {
  font-size: 13px;
  color: var(--focus);
  background: none;
  border: 0;
  border-bottom: 1px solid transparent;
  padding: 0;
}
.why__ask:hover { border-bottom-color: var(--focus); }
.why__loading { color: var(--ink-faint); font-size: 13px; }

/* 一組張數（收工畫面）*/
.round-pick { display: inline-flex; align-items: center; gap: 5px; }
.round-pick input {
  width: 4.2em; font-size: 13px; text-align: center;
  color: var(--ink-soft); background: var(--surface);
  border: 1px solid var(--line); border-radius: 999px; padding: 3px 6px;
  font-variant-numeric: tabular-nums;
}

/* 單字庫裡的熟練度點點：小一號 */
.mastery--inline { display: inline-flex; gap: 4px; }
.mastery--inline .mastery__dot { width: 6px; height: 6px; }

/* ── 單字庫：資料表格用寬版心，不要跟閱讀版心共用 720px ──────────
   V 2026-07-27 回報「版面夠寬但左右看不完」——根因就是這裡。 */
.wrap--wide { max-width: 1180px; }

/* 加字列（已從獨立畫面併入單字庫）*/
.addbar {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 14px;
  padding: 12px 14px;
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  background: var(--surface-2);
}
.addbar .muted { font-size: 13px; white-space: nowrap; }
.add-status__dup { color: var(--red); }

/* 批次操作列：選了東西才出現 */
.bulkbar {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 10px;
  padding: 8px 12px;
  border: 1px solid var(--focus);
  border-radius: var(--radius);
  background: var(--surface-2);
}
.bulkbar__n { font-size: 13px; color: var(--ink); margin-right: 4px; }
.btn--sm { font-size: 13px; padding: 4px 12px; }
.btn--danger { color: var(--red); border-color: var(--red-line); }
.btn--danger:hover { background: var(--red-soft); }
.btn--ghost { color: var(--ink-faint); border-color: transparent; }

/* 勾選欄 */
.col-pick { width: 34px; text-align: center; }
.col-pick input { cursor: pointer; accent-color: var(--focus); }
tr.is-picked td { background: var(--surface-2); }
tr.is-suspended td:not(.col-pick) { opacity: .45; }

/* 中文欄裡的詞性標記 */
.pos-tag {
  margin-left: 7px;
  font-size: 13px;
  font-style: italic;
  color: var(--ink-faint);
}

/* 表格：讓中文與單字欄可以換行，其餘維持 nowrap */
#list-table td.lemma, #list-table td.gloss { white-space: normal; }
#list-table td.gloss { min-width: 13em; }

/* ── 頁尾常駐在視窗底部（V 2026-07-27）──────────────────────────
   揭曉後的內容很長（說明＋延伸例句＋詞形＋重打＋語音鍵），
   原本頁尾會被推到摺線以下，要捲很久才看得到進度與快捷鍵。

   做法：練習畫面固定成一個視窗高的三段式版面——
     practice-top（不動）／ card-area（自己捲）／ practice-foot（不動）
   內容再長也只捲中間那段，頁尾永遠在，也不會蓋住任何內容。

   先前試過 position: sticky，但 .practice 沒有可停靠的高度範圍，
   捲到底時頁尾反而跟著捲走了（2026-07-27 實測）。 */
/* --vvh：iOS 軟鍵盤彈出時由 app.js 的 fitPracticeViewport() 寫進來。
   沒有鍵盤時這個變數不存在，直接吃 100dvh。四個吃滿版高度的地方
   （.app / .topnav / #screen-practice / .grammar）走同一個變數，
   只縮其中一個會讓側欄或外框繼續撐到螢幕底。 */
/* --topnav-h 由 app.js 量出來寫進 <html>（跟 --foot-h 同一套做法）。
   導覽列從側欄搬到頂部之後，這個畫面不能再吃滿 100dvh——那會多出一個
   導覽列的高度、把頁尾頂到摺線以下。鍵盤彈出時導覽列整條收起來，
   量到的 --topnav-h 自然變 0，這裡不必再寫一條特例。 */
#screen-practice {
  height: 100vh;
  height: calc(var(--vvh, 100dvh) - var(--topnav-h, 0px));
  overflow: hidden;
}
#screen-practice .practice-top,
#screen-practice .practice-foot { flex: 0 0 auto; }

/* ── 卡片靠上錨定：揭曉時句子不要跳（V 2026-07-31）────────────────────
   .card-area 原本是 `justify-content: safe center`：內容短就垂直置中，
   一旦長到超過容器，safe 會退回靠上——揭曉的瞬間正好跨過那條線，
   於是句子整個彈上去。**那一刻正是要把自己打的字跟正解對照的時候**，
   眼睛卻得重新找位置。實測跳動：iPad 321px／iPhone 100px／桌機 136px。

   **所有尺寸都套，不分裝置**。第一版只做觸控、桌機留著垂直置中，
   V 否決同一個工具長出兩套邏輯（跟送出靠右那次是同一條原則）。

   跳動歸零一定伴隨句子上移，這是本質的：置中的短內容與靠上的長內容
   位置本來就不同，那個差就是跳動。所以出題狀態的句子會移到「揭曉後它
   會在的位置」——桌機因此比從前高約 96px。上留白 6vh 是為了不貼頂：
   實測 iPad 124px／iPhone 91px／桌機 101px。

   這也順帶回答了「iPad 直向 70% 是空的」——空白現在全部落在句子**下方**，
   也就是揭曉內容要長出來的地方。不是浪費，是預留。 */
#screen-practice .card-area {
  justify-content: flex-start;
  padding-top: 6vh;
}
#screen-practice .card-area {
  flex: 1 1 auto;
  min-height: 0;          /* 沒有這行，flex 子元素不會讓內部捲動生效 */
  overflow-y: auto;
  overscroll-behavior: contain;
}
/* 捲動區的上下緣給一點呼吸，內容不要貼著邊界被切 */
#screen-practice .card-area {
  --area-pad-b: 20px;
  padding-bottom: var(--area-pad-b);
}

/* ── 軟鍵盤彈出：把打字當下用不到的東西讓出來（V 2026-08-02 拍板 A＋C）──
   `.kb-open` 由 app.js 的 fitPracticeViewport() 掛在 <html> 上，跟 --vvh
   同一個判準（可視區比版面視窗矮 80px 以上＝鍵盤真的佔了位置）。

   為什麼要做：iPhone 17 實測，鍵盤彈出後可視區只剩 428px，而頁尾 224px
   ＋ 熟練度那列 50px 就吃掉 274px，句子只剩 153px——四行的句子放不下，
   使用者正在填的那一格連同前後文一起被切掉（V 2026-08-02 回報）。

   收的四樣都是「這一刻用不到」的：
     · 熟練度／狀態標籤——答題前的參考資訊，不是打字時要看的
     · 進度條與今日軌跡——回顧用的
     · 跳過／每日題數／語音口音——設定與逃生口，收鍵盤再按也不遲
     · 6vh 的上留白——那是為了揭曉後的內容預留的，鍵盤開著時還沒到那一步
   **軟鍵那一列留著**：提示／答案／上一題／下一題正是打字當下會按的。
   量出來句子從 153px 回到 359px。

   鍵盤一收 .kb-open 就拿掉，四樣全部回來——是讓位，不是永久拿掉。
   ⚠️ 選擇器要壓得過 ≤400px 那段的 `#key-set`（ID 選擇器），所以帶 id 寫。 */
/* 導覽列也收：打字當下按不到它，而它佔的是最稀缺的高度（V 2026-08-02）。
   收掉之後 app.js 量到的 --topnav-h 變 0，#screen-practice 自然拿回整個可視區。 */
html.kb-open .topnav { display: none; }
html.kb-open #screen-practice .practice-top { display: none; }
html.kb-open #screen-practice .card-area { padding-top: 0; }
html.kb-open .practice-foot .progress { display: none; }
html.kb-open .practice-foot .foot-set,
html.kb-open .practice-foot #key-set { display: none; }
html.kb-open .practice-foot { padding: 8px 0 10px; }

/* ── 重打輸入框釘在捲動區底部（V 2026-07-27 第二輪回報）────────────
   揭曉後 focus() 會讓瀏覽器為了露出輸入框把整區捲到底，句子當場被切掉；
   而且每打一個字，游標還會再把畫面往下拉一次（實測 scrollTop 0 → 46）。
   把輸入框釘在底部之後它永遠在畫面上，瀏覽器就沒有理由捲動，
   句子可以一直留在最上面。app.js 那邊同時改成 focus({preventScroll:true})，
   兩個要一起看。

   width:100% 是必要的：.feedback 是 align-items:flex-start 的 flex 直排，
   不給寬度的話這條只有文字那麼寬，右邊的延伸例句會從縫裡穿過去。

   bottom 補上 -1 × card-area 的 padding-bottom：sticky 對齊的是捲動區的
   內容邊緣而不是可視邊緣，差的正好是那段 padding，不補就會露出一條縫。 */
#screen-practice #retype {
  position: sticky;
  bottom: calc(-1 * var(--area-pad-b));
  z-index: 2;
  width: 100%;
  /* 左右不留內距：左緣要跟句子、揭曉區切齊。
     底部補滿 --area-pad-b，否則 sticky 的負偏移會露出一條縫。 */
  padding: 12px 0 calc(var(--area-pad-b) + 4px);
  /* 跟頁面同色 —— 這是釘在底部的一條輸入列，不是一塊告示牌。
     曾經整片鋪 --amber-soft，但它只有 34em 寬又貼著捲動區底部，
     右緣與下緣都被切斷，看起來像一塊沒切乾淨的黃色方塊（V 2026-07-27）。
     要被看見靠的是 24px 的輸入框與琥珀色標籤，不是滿版底色；
     sticky 要遮住底下捲過去的內容，所以底色仍必須是不透明的 --bg。 */
  background: var(--bg);
  box-shadow: 0 -1px 0 var(--line-soft);
}

/* ── 單字庫的推薦度（五顆星）────────────────────────────────────
   星數＝這個字對 V 的實用度（工作／興趣領域加權），不是難度。
   顏色跟熟練度那套區隔開：熟練度是藍→綠的冷色，這裡用中性到暖的一階，
   避免兩個五格指標在同一列裡被看成同一件事。 */
.stars { display: inline-flex; gap: 1px; font-size: 13px; letter-spacing: 0; }
.stars--none { color: var(--ink-faint); font-size: 13px; }
/* 空星用 --line（3:1，非文字元素的標準），實星一律吃達 4.5:1 的前景 token。
   --amber-line 是填色用的原色橙，對底只有 3.4:1，不能拿來畫這麼小的字元。 */
.star { color: var(--line); }
.star.is-on { color: var(--ink-soft); }
.stars--lv4 .star.is-on { color: var(--ink); }
.stars--lv5 .star.is-on { color: var(--amber); }
#list-table td.utility { white-space: nowrap; }

/* ══════════════════════════════════════════════════════════════════
   主題切換（V 2026-07-27）
   原本只跟系統走（@media prefers-color-scheme）。加上站內切換：
     <html> 沒有 data-theme        → 跟系統（維持原本行為）
     <html data-theme="light">     → 強制淺色
     <html data-theme="dark">      → 強制深色

   刻意**不去改上面那段 media query**，只在檔尾追加覆寫——
   media query 不加權重，:root[data-theme] 是 (0,2,0) 一定壓得過 :root，
   所以系統是深色時 data-theme="light" 也蓋得住。
   這樣既不動核心變數段，也不會跟並行編輯這個檔的人撞在同一行。
   ══════════════════════════════════════════════════════════════════ */
:root[data-theme="dark"] {
  color-scheme: dark;
  --bg: hsl(228 35% 13%);
  --bg-sunk: hsl(228 38% 9%);
  --surface: hsl(228 30% 18%);
  --surface-2: hsl(228 28% 22%);
  --line: hsl(228 16% 52%);
  --line-soft: hsl(228 18% 26%);
  --ink: hsl(38 45% 82%);
  --ink-soft: hsl(38 22% 72%);
  --ink-faint: hsl(38 14% 58%);
  --amber: hsl(14 91% 62%);
  --amber-line: hsl(14 91% 53%);
  --amber-soft: hsl(14 45% 19%);
  --green: hsl(150 45% 46%);
  --green-line: hsl(150 40% 40%);
  --green-soft: hsl(150 30% 15%);
  --green-answer: hsl(150 62% 56%);
  --red: hsl(352 60% 68%);
  --red-line: hsl(352 55% 50%);
  --red-soft: hsl(352 35% 18%);
  --focus: hsl(38 60% 70%);
  --cal-1: hsl(38 45% 51%);
  --cal-4: hsl(38 45% 72%);
    --green-fill: hsl(150 28% 40%);
    --amber-fill: hsl(28 40% 48%);
    --red-fill:   hsl(352 28% 48%);
}
:root[data-theme="light"] {
  color-scheme: light;
  --bg: hsl(38 45% 93%);
  --bg-sunk: hsl(38 47% 86%);
  --surface: hsl(38 55% 97%);
  --surface-2: hsl(38 40% 90%);
  --line: hsl(228 18% 55%);
  --line-soft: hsl(228 16% 78%);
  --ink: hsl(228 35% 22%);
  --ink-soft: hsl(228 20% 32%);
  --ink-faint: hsl(228 16% 43%);
  --amber: hsl(14 91% 38%);
  --amber-line: hsl(14 91% 53%);
  --amber-soft: hsl(14 80% 92%);
  --green: hsl(150 45% 30%);
  --green-line: hsl(150 45% 38%);
  --green-soft: hsl(150 40% 91%);
  --green-answer: hsl(150 70% 30%);
  --red: hsl(352 60% 45%);
  --red-line: hsl(352 60% 50%);
  --red-soft: hsl(352 60% 93%);
  --focus: hsl(228 45% 40%);
  --cal-1: hsl(38 55% 32%);
  --cal-4: hsl(228 35% 30%);
  /* 資料長條專用的填色。**不要拿文字用的 --green/--amber/--red 來鋪長條**：
     那三個是照 4.5:1 的文字標準調的，鋪成大面積會過亮搶眼
     （V 2026-07-29 回報程度評估的頻率層長條）。大色塊不需要那麼高的強度，
     長度本身已經在表達數值了。招牌橙尤其要留給「差一點」那一個狀態。 */
  --green-fill: hsl(150 30% 45%);
  --amber-fill: hsl(28 45% 58%);
  --red-fill:   hsl(352 32% 62%);
}

/* ── 側欄底部的主題切換（V 2026-07-31 改版）────────────────────────
   一顆鍵顯示當前主題，點擊向上展開清單。原本是三顆 20×20 並排，
   太小（觸控下限 44px 差一倍以上）而且橫排塞不下更多選項。

   清單向**上**開：這顆鍵貼在側欄最底部，向下沒有空間。
   position: absolute 定錨在 .theme-pick（relative），寬度可以超出 52px 的
   窄側欄——.topnav 沒有設 overflow，不會被裁掉。
   z-index 56：疊在 Gina 召喚鍵（55）之上、遮罩（59）與面板（60）之下，
   所以面板開著的時候這個清單不會蓋在它上面。 */
.theme-pick {
  position: relative;
}
/* 登入身分／登出。跟主題鍵同一組視覺語言（44px 圓鍵、純圖示），
   因為它們都住在 52px 寬的側欄底部，行為也都是「點一下才展開資訊」。 */
.acct-btn {
  width: 44px; height: 44px;
  display: grid; place-items: center;
  border-radius: 999px;
  background: var(--surface-2);
  box-shadow: inset 0 0 0 1px var(--line-soft);
  color: var(--ink-soft);
  margin-top: auto;              /* 底部那組的第一顆，由它把整組推下去 */
  margin-bottom: 8px;
  transition: background-color .15s ease, color .15s ease;
}
/* 橫排：推到右邊那件事交給 .topnav__tail，這兩顆自己不再帶 auto margin。
   留著的話 margin-top:auto 在 row 方向失效、margin-bottom:8px 會把它們壓歪。 */
.acct-btn { margin: 0; }
.acct-btn:hover { color: var(--ink); background: var(--surface); }
.acct-btn:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
/* 沒開登入模式時這顆是 hidden（＝display:none），身上的 auto margin 跟著失效，
   底部那一格要交還給主題鍵，不然主題鍵會浮回導覽鍵旁邊。
   兩顆都掛 auto 不行——free space 會被兩個 auto margin 對半分，
   帳號鍵會停在側欄中間。 */
.acct-btn[hidden] + .theme-pick { margin-top: 0; }

.theme-pick__now {
  width: 44px; height: 44px;
  display: grid; place-items: center;
  border-radius: 999px;
  background: var(--surface-2);
  box-shadow: inset 0 0 0 1px var(--line-soft);
  color: var(--ink-soft);
  transition: background-color .15s ease, color .15s ease;
}
.theme-pick__now:hover { color: var(--ink); background: var(--surface); }
.theme-pick__now[aria-expanded="true"] {
  background: var(--surface);
  color: var(--ink);
  box-shadow: inset 0 0 0 1px var(--focus);
}
.theme-pick__icon { font-size: 17px; line-height: 1; }

/* 橫排改成**向下**開、靠右對齊：這顆鍵現在在頂列最右，向上沒有空間，
   而 left:0 會讓清單溢出畫面右緣。 */
.theme-pick__menu {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  z-index: 56;
  min-width: 132px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 4px;
  border-radius: var(--radius);
  border: 1px solid var(--line);
  background: var(--surface);
  box-shadow: var(--shadow);
}
.theme-pick__item {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 44px;              /* 清單項一律吃滿觸控下限，桌機也不縮 */
  padding: 0 10px;
  border-radius: 8px;
  color: var(--ink-soft);
  font-size: 13px;
  white-space: nowrap;
  transition: background-color .15s ease, color .15s ease;
}
.theme-pick__item:hover { background: var(--surface-2); color: var(--ink); }
.theme-pick__item[aria-pressed="true"] { color: var(--ink); font-weight: 600; }
.theme-pick__ico { font-size: 15px; line-height: 1; width: 1em; text-align: center; }
/* 當前項左側的圓點：不用打勾，因為之後加彩色主題時圓點可以直接吃該主題的色 */
.theme-pick__dot {
  width: 8px; height: 8px;
  border-radius: 50%;
  flex: 0 0 auto;
  background: transparent;
  box-shadow: inset 0 0 0 1px var(--line);
}
.theme-pick__item[aria-pressed="true"] .theme-pick__dot {
  background: var(--focus);
  box-shadow: none;
}

/* ── 配圖（揭曉後）──────────────────────────────────────────────
   V 2026-07-27：「搞錯 Cabinet 和 Wardrobe，有照片就好記憶。」
   兩張並排時左邊是這個字（✓）、右邊是容易搞混的那個（✗）。 */
.shots {
  display: flex;
  gap: 14px;
  flex-wrap: wrap;
  margin-top: 16px;
  width: 100%;
  max-width: 34em;
}
/* 單張圖時**不要**撐滿閱讀版心。
   `.shots` 的 max-width 是 34em，字級從 15px 拉到 17px 之後那是 578px，
   配圖鎖 4:3 就變成 576×432，直接把下面的語音鍵推出畫面 18px
   （V 2026-07-29 回報「按鈕不見了」，熱鍵還能用就是因為按鈕還在、只是在畫面外）。
   em 寬度會跟著字級一起長，這種連動很容易漏掉。
   300px 也才回到 _rules.md §7 當初假設的量級（「約 250px 寬」）。 */
.shot {
  margin: 0;
  flex: 0 1 260px;
  max-width: 300px;
  min-width: 0;
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--surface);
}
.shot img {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  background: var(--bg-sunk);
}
/* 「不是這個」那張刻意壓低存在感——它是反例，不該比正解搶眼 */
.shot--no img { filter: grayscale(.55) brightness(.82); }
.shot__cap {
  display: flex; align-items: baseline; gap: 6px;
  padding: 7px 10px 2px;
  font-size: 13px;
}
/* 勾與叉要一眼看得到是哪一張（V 2026-08-01）：跟旁邊的字同級時太細，
   在縮圖下方幾乎糊成一團。放大到 17px 並加粗——符號字通常沒有粗體字重，
   合成粗體不一定生效，所以再補一道 text-stroke 把筆畫實際撐厚。
   line-height 壓成 1，字放大了也不會把整條說明推高。 */
.shot__mark {
  font-size: 17px;
  font-weight: 700;
  line-height: 1;
  -webkit-text-stroke: .6px currentColor;
  color: var(--green);
}
.shot--no .shot__mark { color: var(--red); }
.shot__word { font-family: var(--font-serif); color: var(--ink); }
.shot--no .shot__word { color: var(--ink-faint); }
.shot__by {
  display: block;
  padding: 0 10px 8px;
  font-size: 13px;
  color: var(--ink-faint);
  text-decoration: none;
}
.shot__by:hover { text-decoration: underline; }

/* ══════════════════════════════════════════════════════════════════
   點句子裡的單字 → 查義小卡（V 2026-07-27）
   ------------------------------------------------------------------
   行為與狀態見 web/wordtap.js。這裡不定義任何新色票，全部取用 :root。
   窄螢幕（≤560px）自動從錨定小卡切成底部抽屜——375px 放不下 266px 的
   浮動卡又不遮住句子，而且手指的落點誤差比滑鼠大。
   ══════════════════════════════════════════════════════════════════ */

/* 可點的字：平常只有極輕的底線，滑過才亮起來。
   整句都是可點狀態會變成雜訊，所以預設幾乎看不出來。 */
.wt-w {
  border-radius: 3px;
  padding: 0 .06em;
  cursor: pointer;
  box-decoration-break: clone;
  -webkit-box-decoration-break: clone;
  transition: background-color .12s ease;
}
.wt-w:hover { background: var(--surface-2); }
.wt-w.is-on {
  background: color-mix(in srgb, var(--focus) 22%, transparent);
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--focus) 34%, transparent);
}
@supports not (color: color-mix(in srgb, red 50%, blue)) {
  .wt-w.is-on { background: var(--surface-2); box-shadow: 0 0 0 1px var(--line); }
}

/* ── 小卡本體 ───────────────────────────────────────────────── */
.wt-pop {
  position: fixed;
  z-index: 60;
  width: 266px;
  max-width: calc(100vw - 20px);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
  padding: 14px 16px 13px;
  font-size: 13px;
  line-height: 1.6;
}
/* 片語卡（V 2026-07-28）：點 took 時右邊再給一張 take up。
   同一個彈出層裡兩塊面板，不是兩個各自定位的浮層——並排這件事交給 flex，
   翻面與邊界處理仍然只有一份。窄畫面自動改成上下堆疊。 */
.wt-panes { display: flex; gap: 14px; align-items: flex-start; }
.wt-panel { flex: 1 1 0; min-width: 0; }
.wt-pop--pair { width: 546px; }
.wt-panel--phr {
  border-left: 1px solid var(--line);
  padding-left: 14px;
}
.wt-panel__k {
  font-size: 13px; letter-spacing: .06em;
  color: var(--ink-faint); margin-bottom: 6px;
}
@media (max-width: 620px) {
  .wt-pop--pair { width: 266px; }
  .wt-panes { flex-direction: column; gap: 12px; }
  .wt-panel--phr {
    border-left: 0; padding-left: 0;
    border-top: 1px solid var(--line); padding-top: 12px;
  }
}

.wt-arrow {
  position: absolute;
  width: 11px; height: 11px;
  top: -6px;
  background: var(--surface);
  border-left: 1px solid var(--line);
  border-top: 1px solid var(--line);
  transform: rotate(45deg);
}
.wt-pop.is-up .wt-arrow {
  top: auto; bottom: -6px;
  border: 0;
  border-right: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}
.wt-grab { display: none; }

.wt-top { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.wt-word {
  font-family: var(--font-serif);
  font-size: 20px;
  line-height: 1.3;
  color: var(--ink);
  overflow-wrap: anywhere;
}
.wt-pos {
  font-size: 13px;
  color: var(--ink-faint);
  border: 1px solid var(--line);
  border-radius: 4px;
  padding: 0 5px;
  line-height: 16px;
}
.wt-base { font-size: 13px; color: var(--ink-faint); }
.wt-gloss { font-size: 15px; color: var(--ink); margin-top: 7px; line-height: 1.5; }
.wt-note { font-size: 13px; color: var(--ink-soft); margin-top: 6px; line-height: 1.7; }

.wt-src {
  display: flex; align-items: center; gap: 6px;
  font-size: 13px; color: var(--ink-faint); margin-top: 9px;
}
.wt-dot {
  width: 4px; height: 4px; border-radius: 50%;
  background: var(--ink-faint); flex: 0 0 auto;
}
.wt-src--bank .wt-dot, .wt-src--done .wt-dot { background: var(--green); }
.wt-src--gen .wt-dot { background: var(--focus); }
.wt-src--wait .wt-dot { animation: wt-pulse 1.1s ease-in-out infinite; }
@keyframes wt-pulse { 0%, 100% { opacity: .3; } 50% { opacity: 1; } }

/* 推薦度：畫在「＋ 加入單字庫」的正上方——做這個決定的地方就在按鈕旁邊。
   只在還沒收進單字庫時出現（已經在庫裡的字看的是熟練度，不是值不值得收）。 */
.wt-util {
  display: flex; align-items: center; flex-wrap: wrap; gap: 6px;
  font-size: 13px; color: var(--ink-faint);
  margin-top: 9px;
}
.wt-util__k { letter-spacing: .02em; }
.wt-util__v { color: var(--ink-soft); }
/* 熟練度是藍→綠的學習狀態，推薦度是「值不值得收」——不同的問題，不共用顏色 */
.wt-dots--util i.on { background: var(--ink-soft); }
.wt-tag {
  font-size: 13px; line-height: 15px;
  padding: 0 5px; border-radius: 4px;
  border: 1px solid var(--line); color: var(--ink-faint);
}

.wt-dots { display: inline-flex; gap: 3px; margin-left: 2px; }
.wt-dots i {
  width: 4px; height: 4px; border-radius: 50%;
  background: var(--line);
}
.wt-dots i.on { background: var(--green); }

/* 讀取中的骨架條 */
.wt-skel {
  display: inline-block;
  height: .72em;
  border-radius: 3px;
  background: linear-gradient(90deg, var(--surface-2), var(--line-soft), var(--surface-2));
  background-size: 200% 100%;
  animation: wt-shim 1.3s linear infinite;
}
@keyframes wt-shim { from { background-position: 200% 0; } to { background-position: -200% 0; } }

/* ── 加入單字庫 ─────────────────────────────────────────────── */
.wt-btn {
  margin-top: 12px;
  width: 100%;
  display: flex; align-items: center; justify-content: center; gap: 6px;
  height: 32px;
  border-radius: 8px;
  font-size: 13px;
  letter-spacing: .02em;
  border: 1px solid var(--focus);
  color: var(--focus);
  background: transparent;
  transition: background-color .12s ease;
}
.wt-btn:hover:not(:disabled) { background: var(--surface-2); }
/* 已在庫裡：反灰、按不下去。這是 V 指定的狀態。 */
.wt-btn.is-done, .wt-btn:disabled {
  border-color: var(--line);
  color: var(--ink-faint);
  background: var(--surface-2);
  cursor: default;
}
.wt-msg { margin-top: 8px; font-size: 13px; color: var(--red); }

/* ── 窄螢幕：底部抽屜 ───────────────────────────────────────── */
.wt-scrim {
  position: fixed;
  inset: 0;
  z-index: 55;
  background: hsl(252 30% 4% / .38);
}
.wt-pop--sheet {
  left: 0; right: 0; bottom: 0; top: auto;
  width: auto; max-width: none;
  border-radius: 18px 18px 0 0;
  border-left: 0; border-right: 0; border-bottom: 0;
  padding: 10px 18px calc(20px + env(safe-area-inset-bottom, 0px));
  box-shadow: 0 -12px 30px -20px hsl(252 40% 2% / .9);
}
.wt-pop--sheet .wt-arrow { display: none; }
.wt-pop--sheet .wt-grab {
  display: block;
  width: 34px; height: 4px;
  border-radius: 999px;
  background: var(--line);
  margin: 0 auto 12px;
}
.wt-pop--sheet .wt-btn { height: 40px; font-size: 13px; }

/* ── 小卡的進出場 ─────────────────────────────────────────────
   桌面＝transitions.dev 17-tooltip；手機抽屜＝07-panel-reveal。
   上面全是版面規則，這一段只管動態，兩邊刻意不混在一起寫。
   小卡的位置是 place() 用 left/top 給的，transform 完全空著，所以動畫可以獨佔它。 */
.wt-pop {
  opacity: 0;
  transform: scale(var(--wt-scale));
  /* 從被點的那個字長出來，不是從卡片中線。origin-x 由 place() 算箭頭時一併給。 */
  transform-origin: var(--wt-origin-x, 50%) 0;
  will-change: transform, opacity;
  transition:
    opacity var(--wt-out) var(--wt-out-ease),
    transform var(--wt-out) var(--wt-out-ease);
}
/* 翻到字上方時，長出來的起點也要跟著翻到卡片底部 */
.wt-pop.is-up { transform-origin: var(--wt-origin-x, 50%) 100%; }
.wt-pop.is-open {
  opacity: 1;
  transform: scale(1);
  transition:
    opacity var(--wt-in) var(--wt-in-ease),
    transform var(--wt-in) var(--wt-in-ease);
}

/* 抽屜要整片從畫面外滑進來，所以位移用 100% 而不是固定 px */
.wt-pop--sheet {
  transform: translateY(100%);
  filter: blur(var(--wt-sheet-blur));
  will-change: transform, opacity, filter;
  transition:
    opacity var(--wt-sheet-close) var(--wt-sheet-ease),
    transform var(--wt-sheet-close) var(--wt-sheet-ease),
    filter var(--wt-sheet-close) var(--wt-sheet-ease);
}
.wt-pop--sheet.is-open {
  transform: translateY(0);
  filter: blur(0);
  transition:
    opacity var(--wt-sheet-open) var(--wt-sheet-ease),
    transform var(--wt-sheet-open) var(--wt-sheet-ease),
    filter var(--wt-sheet-open) var(--wt-sheet-ease);
}
.wt-scrim {
  opacity: 0;
  transition: opacity var(--wt-sheet-close) var(--wt-sheet-ease);
}
.wt-scrim.is-open {
  opacity: 1;
  transition-duration: var(--wt-sheet-open);
}

@media (prefers-reduced-motion: reduce) {
  .wt-skel, .wt-src--wait .wt-dot { animation: none; }
  .wt-pop, .wt-scrim { transition: none !important; }
}

/* ══════════════════════════════════════════════════════════════
   文法練習（V 2026-07-27）
   ------------------------------------------------------------------
   版面刻意跟練習畫面同一套：滿版高度、內容區可捲、操作列釘在底部。
   兩個畫面的肌肉記憶要能互通，不要為了新功能另創一種佈局。
   ══════════════════════════════════════════════════════════════ */
.grammar {
  height: 100vh;
  /* 跟 #screen-practice 同一條算式：導覽列搬到頂部之後要扣掉它 */
  height: calc(var(--vvh, 100dvh) - var(--topnav-h, 0px));
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.gr-area {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  justify-content: safe center;
  padding: 24px 24px 28px;
}
.gr-loading { color: var(--ink-faint); font-size: 15px; }

.gr-card { width: 100%; max-width: 34em; margin: 0 auto; }

.gr-tags { display: flex; gap: 8px; margin-bottom: 10px; min-height: 20px; }
.gr-tag {
  font-size: 13px;
  letter-spacing: .04em;
  color: var(--ink-faint);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 1px 9px;
}
/* 只說「之前錯過」，不說錯在哪——說了等於在答題前給提示（同 §5.7） */
.gr-tag--again { color: var(--amber); border-color: var(--amber-line); }

/* 中文題目是這個畫面的主角，字級對齊練習畫面的例句 */
.gr-zh {
  font-family: var(--font-serif);
  font-size: var(--answer-size);
  line-height: 1.55;
  color: var(--ink);
  margin: 0 0 18px;
}

.gr-input {
  width: 100%;
  font-family: var(--font-serif);
  font-size: 20px;
  line-height: 1.6;
  color: var(--ink);
  background: var(--surface);
  border: 1.5px solid var(--line);
  border-radius: var(--radius);
  padding: 10px 14px;
  resize: vertical;
  transition: border-color .15s ease;
}
.gr-input:focus { outline: none; border-color: var(--focus); }
.gr-input:disabled { background: var(--bg-sunk); color: var(--ink-soft); }

/* ── 批改中的等待提示（V 2026-07-30 拍板「B ＋ 丙」）──────────────
   批改走 Opus，中位 7.8 秒、偶爾 12 秒。在這段時間裡，畫面上原本唯一的
   變化是頁尾那顆鍵變成「批改中…」——視線焦點在自己剛打的句子上，回饋卻在
   最右下角，等於沒有回饋。

   刻意跟 .gr-result 用同一組 margin-top／padding-top／上框線：判定就從
   這個槽換上來，眼睛不用重新找位置。
   主句吃 --fs-lg（20px）而不是判定短語的 15px：等待期間畫面上沒有正解，
   它是那一刻唯一該看的東西，所以不受 §5.15「提示語不跟正解搶」的約束。 */
.gr-wait {
  margin-top: 18px;
  padding-top: 16px;
  border-top: 1px solid var(--line-soft);
  opacity: 0;
  transform: translateY(var(--gwait-distance));
  transition: opacity var(--gwait-in) var(--gwait-ease),
              transform var(--gwait-in) var(--gwait-ease);
}
.gr-wait.is-open { opacity: 1; transform: translateY(0); }
.gr-wait__line { font-size: var(--fs-lg); line-height: 1.5; margin: 0; }
/* 副句用 --ink-soft 而不是 --ink-faint：它要能讀，不是註腳 */
.gr-wait__note {
  font-size: var(--fs-xs);
  line-height: 1.7;
  color: var(--ink-soft);
  margin: 8px 0 0;
}

/* transitions.dev · 15-shimmer-text —— 原封不動，只有顏色接本站 token。
   兩層構造：底層是正常的字（--shimmer-base），::before 用 attr(data-text)
   複製同一串字、把漸層 clip 到字上，再掃 background-position。
   ⚠️ 換字時 textContent 與 data-text 必須一起改，漏一邊會兩層字疊著。 */
.t-shimmer {
  position: relative;
  display: inline-block;
  color: var(--shimmer-base);
}
.t-shimmer::before {
  content: attr(data-text);
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image: linear-gradient(
    90deg,
    transparent          0%,
    transparent         40%,
    var(--shimmer-highlight) 50%,
    transparent         60%,
    transparent        100%
  );
  background-size: var(--shimmer-band) 100%;
  background-repeat: no-repeat;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  -webkit-text-fill-color: transparent;
  animation: t-shimmer var(--shimmer-dur) var(--shimmer-ease) infinite;
}
@keyframes t-shimmer {
  0%   { background-position: 100% 0; }
  100% { background-position: 0% 0; }
}

/* transitions.dev · 04-text-states-swap —— 換句用。舊句往上淡出帶模糊，
   新句從下面回到原位。三段式由 grammar.js 驅動，中間那次 reflow 不能省。 */
.t-text-swap {
  display: inline-block;
  transform: translateY(0);
  filter: blur(0);
  opacity: 1;
  transition:
    transform var(--text-swap-dur) var(--text-swap-ease),
    filter    var(--text-swap-dur) var(--text-swap-ease),
    opacity   var(--text-swap-dur) var(--text-swap-ease);
  will-change: transform, filter, opacity;
}
.t-text-swap.is-exit {
  transform: translateY(calc(var(--text-swap-translate-y) * -1));
  filter: blur(var(--text-swap-blur));
  opacity: 0;
}
.t-text-swap.is-enter-start {
  transform: translateY(var(--text-swap-translate-y));
  filter: blur(var(--text-swap-blur));
  opacity: 0;
  transition: none;
}

@media (prefers-reduced-motion: reduce) {
  .gr-wait { transition: none !important; }
  .t-shimmer::before { animation: none !important; }
  .t-text-swap { transition: none !important; }
}

/* ── 批改結果 ── */
.gr-result {
  margin-top: 18px;
  padding-top: 16px;
  border-top: 1px solid var(--line-soft);
}
.gr-verdict { font-size: 15px; font-weight: 600; margin: 0 0 12px; }
.gr-result.is-good .gr-verdict { color: var(--green); }
.gr-result.is-near .gr-verdict { color: var(--amber); }
.gr-result.is-bad  .gr-verdict { color: var(--red); }

.gr-err {
  margin: 0 0 14px;
  padding: 10px 14px;
  background: var(--surface-2);
  border-left: 3px solid var(--red-line);
  border-radius: var(--radius);
}
.gr-err__dim {
  font-size: 13px;
  letter-spacing: .04em;
  color: var(--red);
}
.gr-err__fix {
  font-family: var(--font-serif);
  font-size: 20px;
  margin: 4px 0 0;
}
.gr-err__was { color: var(--ink-faint); text-decoration-color: var(--red-line); }
.gr-err__why { font-size: 13px; color: var(--ink-soft); margin: 6px 0 0; line-height: 1.7; }

/* 母語說法跟參考譯是兩件事：前者是「實際會怎麼講」，後者只是對照用 */
.gr-native {
  margin-top: 16px;
  padding: 12px 14px;
  background: var(--green-soft);
  border-left: 3px solid var(--green-line);
  border-radius: var(--radius);
}
.gr-native__head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.gr-native__label { font-size: 13px; letter-spacing: .04em; color: var(--green); }
/* 掛在母語例句列的讀音鍵，比工具列上的原尺寸小一號 */
.gr-native .replay-btn { padding: 3px 10px; font-size: 13px; }
.gr-native .replay-btn svg { width: 13px; height: 13px; }
.gr-native__en {
  font-family: var(--font-serif);
  font-size: 20px;
  margin: 4px 0 0;
  color: var(--ink);
}
.gr-native__note { font-size: 13px; color: var(--ink-soft); margin: 6px 0 0; line-height: 1.7; }

.gr-ref { font-size: 13px; color: var(--ink-faint); margin: 12px 0 0; }
.gr-again { font-size: 13px; color: var(--amber); margin: 10px 0 0; }

/* ── 收工畫面 ── */
.gr-done { width: 100%; max-width: 30em; margin: 0 auto; }
.gr-done__head { font-size: 17px; margin: 0 0 16px; }
.gr-done__rows {
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-lg);
  background: var(--surface);
  padding: 4px 18px;
  margin-bottom: 18px;
}
.gr-done__row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  padding: 11px 0;
  border-bottom: 1px solid var(--line-soft);
  font-size: 15px;
  color: var(--ink-soft);
}
.gr-done__row:last-child { border-bottom: 0; }
.gr-done__row b { font-family: var(--font-serif); font-size: 20px; color: var(--ink); }

/* ── 一輪結束的檢討（V 2026-07-28）──
   統計那張表講「做了多少」，這一塊講「哪裡弱」。刻意不進上面那個表格框：
   表格是數字，這裡是話，混在一起兩邊都難讀。 */
.gr-review { margin-bottom: 18px; text-align: left; }
.gr-review__weak {
  display: flex; align-items: center; flex-wrap: wrap; gap: 8px;
  margin-bottom: 12px;
}
.gr-review__k { font-size: 13px; color: var(--ink-faint); }
.gr-review__dim {
  display: inline-flex; align-items: baseline; gap: 4px;
  font-size: 13px; line-height: 20px;
  padding: 0 8px; border-radius: 999px;
  border: 1px solid var(--amber-line); background: var(--amber-soft);
  color: var(--amber);
}
.gr-review__dim i { font-style: normal; opacity: .75; }
.gr-review__wait { font-size: 13px; color: var(--ink-faint); margin: 0; }
.gr-review__sum {
  font-size: 15px; line-height: 1.75; color: var(--ink); margin: 0 0 8px;
}
.gr-review__focus {
  margin: 0; padding-left: 1.2em;
  font-size: 13px; line-height: 1.8; color: var(--ink-soft);
}

/* ── 底部操作列 ── */
.gr-foot {
  flex: 0 0 auto;
  border-top: 1px solid var(--line-soft);
  background: var(--bg);
}
/* 一定要能換行：375px 的手機塞不下「今日 X／Y ＋ 兩顆回看鍵 ＋ 每日題數
   ＋ 送出」，不換行的話會擠成一行一個字並互相疊住（QA 2026-07-29 實測）。
   單字練習的頁尾本來就是 wrap，這裡漏掉了。 */
.gr-foot__in {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px 14px;
  padding-top: 12px;
  padding-bottom: 12px;
}
/* 撐開器只在放得下的時候起作用；換行之後它會自己佔一行，所以收掉 */
.gr-foot__spacer { flex: 1 1 auto; }
.gr-foot__count { font-size: 13px; color: var(--ink-faint); white-space: nowrap; }
.gr-foot__goal {
  font-size: 13px;
  color: var(--ink-faint);
  white-space: nowrap;
  display: inline-flex;
  align-items: center;
}
@media (max-width: 640px) {
  /* 窄螢幕：計數自己一行，操作鍵排下面，撐開器不參與 */
  .gr-foot__count { flex: 1 0 100%; }
  .gr-foot__spacer { display: none; }
  .gr-foot__in { gap: 8px 10px; }
  .gr-btn { margin-left: auto; }
}
.gr-foot__goal input {
  width: 4.5em;
  margin: 0 6px;
  padding: 4px 8px;
  font-size: 13px;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 8px;
}
.gr-btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 8px 18px;
  font-size: 15px;
  color: var(--surface);
  background: var(--ink);
  border-radius: 999px;
}
.gr-btn:disabled { opacity: .5; cursor: default; }
.gr-btn kbd {
  font-family: var(--font-mono);
  font-size: 13px;
  opacity: .65;
}

/* ══════════════════════════════════════════════════════════════
   主頁：今日總覽（V 2026-07-27）
   ------------------------------------------------------------------
   招牌點下去的地方。單字與文法並排，因為這是一天的全貌；
   收工小結只有這一份，練習畫面不再自己畫一塊。
   ══════════════════════════════════════════════════════════════ */
/* 捲動跟單字庫／數據同一套：內容長就讓視窗捲，畫面自己不另開捲軸 */
/* 兩張卡並排要比閱讀版心寬一點，但別寬到像資料表格 */
.wrap--home { max-width: 900px; padding-bottom: 64px; }

.home__head { padding: 56px 0 26px; text-align: center; }
.home__title {
  font-family: var(--font-serif);
  font-size: 32px;
  font-weight: 400;
  margin: 0 0 8px;
  color: var(--ink);
}
.home__sub { margin: 0; font-size: 13px; color: var(--ink-faint); }

.home__grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
  align-items: start;
}
@media (max-width: 760px) {
  .home__grid { grid-template-columns: 1fr; }
  .home__head { padding-top: 32px; }
}

.home-card {
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface);
  padding: 18px 22px 20px;
  display: flex;
  flex-direction: column;
}
.home-card__h {
  font-size: 13px;
  font-weight: 600;
  letter-spacing: .08em;
  color: var(--ink-faint);
  margin: 0 0 6px;
}
.home-card__act {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-top: 16px;
}
.home-card__off { margin: 10px 0; font-size: 13px; color: var(--ink-faint); }

/* 主頁裡的數據列不要再包一層框——外面那張卡已經是框了 */
.daily-sum--flat {
  margin: 0;
  max-width: none;
  border: 0;
  border-radius: 0;
  background: none;
  padding: 0;
}

/* 今日軌跡：練習畫面那條畫的是「本輪」，這條畫的是一整天 */
.home__track { margin-top: 24px; }
.home__track-k {
  font-size: 13px;
  letter-spacing: .06em;
  color: var(--ink-faint);
  margin-bottom: 8px;
}
.home__note {
  margin: 22px 0 0;
  font-size: 13px;
  line-height: 1.75;
  color: var(--ink-faint);
  text-align: center;
}

/* 站在主頁時招牌要亮著——它現在是一個真的目的地，不只是裝飾 */
.topnav__mark[aria-current] { color: var(--ink); background: var(--surface-2); }

/* 收工那一句。跟副標分開一行：副標是事實（日期、連續天數），
   這一行是觀察，語氣不同就該有自己的位置。
   琥珀色是招牌色——它只在真的有話講時才出現，所以值得用它。 */
.home__remark {
  font-size: var(--fs-sm);
  line-height: 1.7;
  color: var(--amber);
  margin: 10px auto 0;
  max-width: 30em;
}

/* 查義小卡的發音鍵。只有圖示不放字——小卡很窄，四個中文字會把詞性
   與「句中作 X」擠到換行（V 2026-07-29 指定用 icon）。
   對齊用 align-self: center：.wt-top 是 baseline 對齊，圖示沒有基線，
   不覆寫會被吊在字的下緣。 */
.wt-speak {
  align-self: center;
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border-radius: 8px;
  color: var(--ink-faint);
  transition: color .15s ease, background-color .15s ease;
}
.wt-speak svg { width: 17px; height: 17px; display: block; }
.wt-speak:hover { color: var(--ink); background: var(--surface-2); }
.wt-speak.is-playing { color: var(--amber); background: var(--amber-soft); }

/* 查義小卡的句型區塊。把 note_zh 裡的句型抽出來單獨框起來——
   V 2026-07-29：點 managed 時 note 已經寫了「manage to do sth＝…」，
   資訊是對的，但被當成一段文字，看不出那是一個要整組記的單位。
   用琥珀左條：跟「差一點」同一個招牌色，語意上都是「這裡要留意」。 */
.wt-pat {
  margin-top: 8px;
  padding: 8px 11px;
  background: var(--amber-soft);
  border-left: 3px solid var(--amber-line);
  border-radius: 8px;
}
.wt-pat__k {
  font-size: var(--fs-xs);
  letter-spacing: .06em;
  color: var(--amber);
  margin-right: 8px;
}
.wt-pat__v {
  font-family: var(--font-mono);
  font-size: var(--fs-sm);
  color: var(--ink);
  overflow-wrap: anywhere;
}
.wt-pat__zh { font-size: var(--fs-xs); color: var(--ink-soft); margin-top: 4px; line-height: 1.7; }
