/* ── Self-hosted fonts ── */
@font-face {
  font-family: 'Shippori Mincho';
  font-style: normal; font-weight: 400; font-display: swap;
  src: url('fonts/ShipporiMincho-Regular.ttf') format('truetype');
}
@font-face {
  font-family: 'Shippori Mincho';
  font-style: normal; font-weight: 500; font-display: swap;
  src: url('fonts/ShipporiMincho-Medium.ttf') format('truetype');
}
@font-face {
  font-family: 'Shippori Mincho';
  font-style: normal; font-weight: 600 700; font-display: swap;
  src: url('fonts/ShipporiMincho-SemiBold.ttf') format('truetype');
}
@font-face {
  font-family: 'Noto Serif JP';
  font-style: normal; font-weight: 100 900; font-display: swap;
  src: url('fonts/NotoSerifJP-VF.ttf') format('truetype-variations');
}
@font-face {
  font-family: 'Noto Sans JP';
  font-style: normal; font-weight: 100 900; font-display: swap;
  src: url('fonts/NotoSansJP-VF.ttf') format('truetype-variations');
}
@font-face {
  font-family: 'Noto Serif TC';
  font-style: normal; font-weight: 100 900; font-display: swap;
  src: url('fonts/NotoSerifTC-VF.ttf') format('truetype-variations');
}
@font-face {
  font-family: 'Noto Sans TC';
  font-style: normal; font-weight: 100 900; font-display: swap;
  src: url('fonts/NotoSansTC-VF.ttf') format('truetype-variations');
}
@font-face {
  font-family: 'Newsreader';
  font-style: normal; font-weight: 200 800; font-display: swap;
  src: url('fonts/Newsreader-VF.ttf') format('truetype-variations');
}
@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal; font-weight: 100 800; font-display: swap;
  src: url('fonts/JetBrainsMono-VF.ttf') format('truetype-variations');
}

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

:root,
:root[data-theme="dark"] {
  color-scheme: dark;

  /* ── Background ── */
  --bg-base:     #16130F;
  --bg-surface:  #1F1B16;
  --bg-subtle:   #1A1612;
  --bg-elevated: #2A241D;

  /* ── Border ── */
  --border-default: #3E342B;
  --border-subtle:  #2D2620;

  /* ── Text ── */
  --text-primary:   #F5F1EA;
  --text-secondary: #B8AFA1;
  --text-muted:     #7A7268;

  /* ── Accent ── */
  --accent:            #FF6B4A;
  --accent-hover:      #FF856B;
  --accent-soft:       rgba(255, 107, 74, 0.12);
  --accent-soft-hover: rgba(255, 107, 74, 0.18);
  --accent-border:     rgba(255, 107, 74, 0.30); /* intentional: from article-learning prototype; used by suggestion-card hover / romaji-card-speak / level-badge--matched */
  --accent-highlight:  rgba(255, 107, 74, 0.25); /* D-2B: SnippetMark bg — heavier than accent-soft to pop against card bg */
  --accent-ink:        #FFFFFF;

  /* ── Status ── */
  --status-correct:           #4FB892;
  --status-correct-bg:        rgba(79, 184, 146, 0.10);
  --status-partial:           #E5A24E;
  --status-partial-bg:        rgba(229, 162, 78, 0.10);
  --status-partial-bg-subtle: rgba(229, 162, 78, 0.04); /* intentional: D-4 Phase 3 token; conj-card-header amber 4% tint */
  --status-partial-border:    rgba(229, 162, 78, 0.35); /* intentional: D-4 Phase 3 token; verb-mode composer border */
  --status-wrong:             #C7553F;
  --status-wrong-bg:          rgba(199, 85, 63, 0.10);

  /* ── Shadow ── */
  --shadow-card:     0 1px 2px rgba(0,0,0,0.30), 0 8px 24px rgba(0,0,0,0.20);
  --shadow-misc-1:   0 2px 14px rgba(35,26,15,0.08);
  --shadow-misc-2:   0 2px 16px rgba(35,26,15,0.2);
  --shadow-elevated: 0 4px 24px rgba(0,0,0,0.32), 0 16px 40px rgba(0,0,0,0.28);

  /* ── Glass ── */
  --glass-hairline:   rgba(255,255,255,0.10);
  --glass-bg:         rgba(31, 27, 22, 0.72);
  --glass-divider:    rgba(255,255,255,0.18);
  --glass-hover:      rgba(255,255,255,0.22);
  --glass-border:     rgba(255, 255, 255, 0.06);
  --overlay-warm:     rgba(35,26,15,0.4);
  --shadow-glass:     0 8px 32px rgba(0,0,0,0.32), inset 0 1px 0 rgba(255,255,255,0.04);
  --bg-hover-overlay: rgba(255,255,255,0.04); /* intentional: D-3 Phase 3 token; tooltip-btn hover */

  /* ── Radius ── */
  --radius-sm:    6px;
  --radius-md:    12px;
  --radius-lg:    16px;
  --radius-xl:    24px;
  --radius-full:  9999px;
  --radius-misc-1: 4px;
  --radius-pill: 20px;
  --radius-misc-3: 8px;

  /* ── Font family ── */
  --font-jp-sans:    'Noto Sans JP', 'Hiragino Sans', sans-serif;
  --font-jp-serif:   'Shippori Mincho', 'Noto Serif JP', 'Hiragino Mincho ProN', 'Yu Mincho', serif;
  --font-zh-sans:    'Noto Sans TC', 'PingFang TC', 'Microsoft JhengHei', sans-serif;
  --font-zh-serif:   'Noto Serif TC', 'Source Han Serif TC', 'Songti TC', serif;
  --font-en-display: 'Newsreader', 'GT Sectra', Georgia, serif;
  --font-en-mono:    'JetBrains Mono', 'IBM Plex Mono', ui-monospace, monospace;

  /* ── Type scale ── */
  --text-display:  48px;
  --text-h1:       32px;
  --text-h2:       24px;
  --text-h3:       19px;
  --text-body-lg:  17px;
  --text-body:     15px;
  --text-sm:       13px;
  --text-xs:       11px;

  /* ── Misc text sizes (需人工確認) ── */
  --text-misc-10: 38px;
  --text-misc-11: 9px;

  /* ── Line heights ── */
  --lh-display:  1.15;
  --lh-h1:       1.25;
  --lh-h2:       1.30;
  --lh-h3:       1.40;
  --lh-body-lg:  1.70;
  --lh-body:     1.60;
  --lh-tight:    1.50;
  --lh-label:    1.40;

  /* ── Spacing ── */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 24px;
  --space-6: 32px;
  --space-7: 48px;
  --space-8: 64px;
  --space-9: 96px;

  /* ── Component ── */
  /* 按鈕水平內距。刻意不在 spacing scale 上（最近的是 space-3=12 / space-4=16）——
     設計的 ActionBtn 與既有多數按鈕都是 14px，硬塞進 scale 會讓全站按鈕位移 2px。
     收斂為元件層 token，服務 .btn-secondary / .btn-ghost。 */
  --btn-pad-x: 14px;
  /* 設計系統的 Button 尺寸階梯（Primitives.jsx）：
       sm  height 32 / font 13（= --text-sm，按鈕預設）
       md  height 40 / font 14（modal footer 用）
     14px 同樣不在 type scale 上（13 / 15），故收為元件層 token。
     height 直接寫在 size 變體裡 —— 規則 12 禁的是 font-size / spacing / radius，不含 height。 */
  --btn-font-md: 14px;

  /* ── Motion ── */
  --duration-fast:  120ms;
  --duration-base:  200ms;
  --duration-entry: 240ms;
  --duration-slow:    320ms;
  --duration-breath: 1400ms;   /* looping breathing dots (題庫 generating) */
  --ease-entry:     cubic-bezier(0.2, 0, 0, 1);
  --ease-out:       cubic-bezier(0.16, 1, 0.3, 1);

  /* ── Misc color tokens ── */
  --color-context-btn-hover: #8a5010;  /* paywall save/copy btn hover */
  --color-misc-19: #f0c060;            /* paywall cmd code text */
  --tag-blue-bg:    rgba(59, 130, 246, 0.15);
  --tag-blue-text:  #93c5fd;
  --tag-green-bg:   rgba(34, 197, 94, 0.15);
  --tag-green-text: #86efac;
  --tag-orange-bg:  rgba(251, 146, 60, 0.15);
  --tag-orange-text: #fdba74;
}

:root[data-theme="light"] {
  color-scheme: light;

  /* ── Background ── */
  --bg-base:     #FAF8F4;
  --bg-surface:  #FFFFFF;
  --bg-subtle:   #F2EEE7;
  --bg-elevated: #FFFFFF;

  /* ── Border ── */
  --border-default: #D6CFC2;
  --border-subtle:  #E8E2D8;

  /* ── Text ── */
  --text-primary:   #1A1612;
  --text-secondary: #5C544A;
  --text-muted:     #8A8378;

  /* ── Accent ── */
  --accent:            #D94F30;
  --accent-hover:      #C7401F;
  --accent-soft:       #FCEEE8;
  --accent-soft-hover: rgba(217, 79, 48, 0.15);
  --accent-border:     rgba(217, 79, 48, 0.30);
  --accent-highlight:  rgba(199, 122, 31, 0.20); /* D-2B: SnippetMark bg light mode */
  --accent-ink:        #FFFFFF;

  /* ── Status ── */
  --status-correct:           #2D7D5F;
  --status-correct-bg:        #E8F4EE;
  --status-partial:           #C77A1F;
  --status-partial-bg:        #FAF0DE;
  --status-partial-bg-subtle: rgba(199, 122, 31, 0.06);
  --status-partial-border:    rgba(199, 122, 31, 0.30);
  --status-wrong:             #9F3722;
  --status-wrong-bg:          #F7E5DF;

  /* ── Shadow ── */
  --shadow-card:     0 1px 2px rgba(0,0,0,0.04), 0 4px 12px rgba(0,0,0,0.04);
  --shadow-misc-1:   0 2px 14px rgba(0,0,0,0.04);
  --shadow-misc-2:   0 2px 16px rgba(0,0,0,0.08);
  --shadow-elevated: 0 4px 24px rgba(0,0,0,0.08), 0 16px 40px rgba(0,0,0,0.06);

  /* ── Glass ── */
  --glass-hairline:   rgba(0,0,0,0.08);
  --glass-bg:         rgba(255, 255, 255, 0.65);
  --glass-divider:    rgba(0,0,0,0.12);
  --glass-hover:      rgba(0,0,0,0.06);
  --glass-border:     rgba(0, 0, 0, 0.06);
  --overlay-warm:     rgba(26,22,18,0.35);
  --shadow-glass:     0 8px 32px rgba(0,0,0,0.08), inset 0 1px 0 rgba(255,255,255,0.80);
  --bg-hover-overlay: rgba(0,0,0,0.04);

  /* ── Misc color tokens ── */
  --color-context-btn-hover: #9A5F00;
  --color-misc-19:           #8B5200;
  --tag-blue-text:           #1d4ed8;
  --tag-green-text:          #15803d;
  --tag-orange-text:         #c2410c;
}

/* ── Glass utility — Phase 2 shared overlay token
   Source: docs/design-system/prototype/article-learning/index.html className="glass"
   Scenes: FAB / context banner / selection tooltip / dropdown / modal scrim ── */
.glass {
  background: var(--glass-bg);
  backdrop-filter: blur(20px) saturate(180%);
  -webkit-backdrop-filter: blur(20px) saturate(180%);
  border: 1px solid var(--glass-border);
  box-shadow: var(--shadow-glass);
}

body {
  font-family: var(--font-jp-sans);
  background: var(--bg-base);
  color: var(--text-primary);
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  -webkit-font-smoothing: antialiased;
  font-size: var(--text-body);
  transition: background-color var(--duration-base) ease, color var(--duration-base) ease;
}

/* ── Header ── */
/* ── Navbar ── */
.navbar {
  height: 64px;
  background: var(--bg-surface);
  border-bottom: 1px solid var(--glass-hairline);
  display: flex;
  align-items: center;
  padding: 0 24px;
  gap: 0;
  position: sticky;
  top: 0;
  z-index: 10;
  flex-shrink: 0;
}
.nav-brand {
  display: flex;
  align-items: center;
  gap: 10px;
  text-decoration: none;
  flex-shrink: 0;
}
.nav-mark {
  width: 28px;
  height: 28px;
  flex-shrink: 0;
  display: block;
}
.nav-wordmark {
  display: inline-flex;
  align-items: baseline;
  gap: 8px;
}
.nav-wordmark-ja {
  font-family: var(--font-zh-serif);
  font-size: 22px;
  font-weight: 500;
  color: var(--text-primary);
  letter-spacing: -0.01em;
  line-height: 1;
}
.nav-wordmark-romaji {
  font-family: var(--font-en-display);
  font-size: 16px;
  font-weight: 400;
  font-style: italic;
  color: var(--text-secondary);
  letter-spacing: 0.02em;
  line-height: 1;
}
.nav-divider {
  width: 1px;
  height: 22px;
  background: var(--border-subtle);
  margin: 0 20px;
  flex-shrink: 0;
}
.nav-tabs {
  display: flex;
  align-items: stretch;
  height: 100%;
  gap: 0;
}
.nav-right {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 4px;
}
.nav-level {
  font-family: var(--font-en-display);
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-secondary);
  padding: 5px 11px;
  border: 1px solid var(--border-default);
  border-radius: var(--radius-sm);
  margin-right: 6px;
  line-height: 1;
}
.nav-icon-btn {
  width: 34px;
  height: 34px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: none;
  color: var(--text-secondary);
  cursor: pointer;
  border-radius: var(--radius-sm);
  transition: background var(--duration-base), color var(--duration-base);
}
.nav-icon-btn:hover {
  background: var(--bg-subtle);
  color: var(--text-primary);
}
.nav-icon-btn svg { width: 18px; height: 18px; }

/* ── 帳號 avatar + dropdown（登入後 navbar，Google OAuth v1，規格 §4）── */
/* avatar 觸發鈕複用 .nav-icon-btn（34×34、radius、hover 完全一致，規格 §4.1「以現行實作為準」）。 */
.nav-account { position: relative; }
.nav-avatar-btn { padding: 0; }
.nav-avatar { position: relative; width: 26px; height: 26px; border-radius: 50%;
              display: flex; align-items: center; justify-content: center; }
.nav-avatar-img { position: absolute; inset: 0; width: 26px; height: 26px; border-radius: 50%; object-fit: cover; }
.nav-avatar-fallback { width: 26px; height: 26px; border-radius: 50%; background: var(--bg-subtle);
                       border: 1px solid var(--border-default); display: flex; align-items: center;
                       justify-content: center; font-family: var(--font-zh-serif); font-weight: 500;
                       font-size: 12px; color: var(--text-secondary); line-height: 1; }
/* 開啟態＝其他 icon 按鈕的 hover（複用 --bg-subtle），保持到關閉。 */
.nav-icon-btn.is-open { background: var(--bg-subtle); color: var(--text-primary); }

.nav-account-menu { position: absolute; top: calc(100% + 10px); right: 0; min-width: 148px; padding: 6px;
                    border-radius: var(--radius-md); background: var(--glass-bg);
                    backdrop-filter: blur(20px) saturate(180%); -webkit-backdrop-filter: blur(20px) saturate(180%);
                    border: 1px solid var(--glass-border); box-shadow: var(--shadow-glass); z-index: 40;
                    display: none; transform-origin: top right; }
.nav-account-menu.open { display: block; animation: accountMenuPop var(--duration-base) var(--ease-out); }
@keyframes accountMenuPop { from { opacity: 0; transform: scale(0.96); } to { opacity: 1; transform: scale(1); } }
.nav-account-item { width: 100%; display: flex; align-items: center; gap: 10px; padding: 10px 12px;
                    background: transparent; border: 0; border-radius: 8px; cursor: pointer;
                    font-family: var(--font-zh-sans); font-size: 13.5px; color: var(--text-primary);
                    line-height: 1; text-align: left; }
.nav-account-item:hover { background: var(--bg-subtle); }
.nav-account-item svg { width: 15px; height: 15px; color: var(--text-secondary); flex-shrink: 0; }

/* ── 認證流程落地進場「紙頁上移」（取代規格 §2 的 splash）──
   splash 原定位是「session 建立中的等待畫面」，但 session 在 /auth/callback 已於伺服器端建好，
   splash render 出來時無事可等 = 純粹自己加的 1.6s 延遲（且呼吸階段在 1.6s 跳轉下從未播出）。
   改為把過場做成**目的頁的進場**：發生在頁面本來就要載入的時間內，零額外等待。
   只在認證落地時播一次（session 一次性旗標 auth_landing），一般重整不播。
   只動內容區、navbar 保持穩定 —— 否則每次進站整條列都在動。 */
/* 淡入與位移**刻意拆開**：opacity 320ms 先完成，位移續跑到 800ms ——
   先看得見紙、再看著它落下。兩者同步的話只會是普通淡入，做不出紙張感。 */
@keyframes pageFade { from { opacity: 0; } to { opacity: 1; } }
@keyframes pageRise { from { transform: translateY(20px); } to { transform: none; } }
body.page-enter .tab-panel.active > main {
  /* intentional: 設計系統的 --ease-entry(0.2,0,0,1) / --ease-out(0.16,1,0.3,1) 都是 swift-out
     曲線（前 25% 就走完 61%~83%），適合選單彈出、不適合「從靜止落下」。實測現用曲線在 420ms
     裡 200ms 就實質結束，後段只移動 1.5px（看不見）。此處需要慢起步 + 長尾，無對應 token，
     故用字面值；來源：紙頁上移手感對照 mock，使用者選定 B 案。 */
  animation: pageFade 320ms cubic-bezier(0.4, 0, 0.2, 1) both,
             pageRise 800ms cubic-bezier(0.45, 0.05, 0.15, 1) both;
}

/* ── 法務頁（隱私權政策 / 使用條款，OAuth v1）— 單欄 680px、ZH serif 長讀 ──
   **nav 與品牌 lockup 不在這裡** —— 法務頁直接複用 app 既有的 `.navbar` + `.nav-brand` /
   `.nav-mark` / `.nav-wordmark(-ja/-romaji)`（見上方 Navbar 一節）。
   原本移植 prototype legal.css 時另寫了一份 `.legal-nav .brand/.ja/.romaji`，立刻與 app 走鐘：
   日々 字體 Shippori Mincho vs var(--font-zh-serif)、mark→日々 12 vs 10、日々→hibi 12 vs 8。
   **勿再為法務頁另寫品牌樣式**；品牌 chrome 全站只有一份。 */
.legal { max-width: 680px; margin: 0 auto; padding: 72px 28px 96px; }
.legal h1 { font-family: var(--font-zh-serif); font-size: var(--text-h1); font-weight: 500; }
.legal .meta { font-family: var(--font-zh-sans); font-size: var(--text-sm); color: var(--text-muted); margin-top: 12px; }
.legal .lead { font-family: var(--font-zh-serif); font-size: var(--text-body-lg); line-height: 1.9; color: var(--text-secondary); margin: 28px 0 0; text-wrap: pretty; }
.legal section { margin-top: 48px; }
.legal h2 { font-family: var(--font-zh-serif); font-size: var(--text-h3); font-weight: 500; margin-bottom: 16px; }
.legal p { font-family: var(--font-zh-sans); font-size: var(--text-body); line-height: 1.85; color: var(--text-secondary); margin: 0 0 14px; }
.legal p strong { color: var(--text-primary); font-weight: 500; }
.legal a { color: var(--accent); text-decoration: none; }
.legal a:hover { text-decoration: underline; text-underline-offset: 3px; }
.legal-foot { margin-top: 72px; padding-top: 24px; border-top: 1px solid var(--border-subtle); display: flex; align-items: center; gap: 16px; font-family: var(--font-zh-sans); font-size: var(--text-sm); }
.legal-foot a { color: var(--text-muted); text-decoration: none; }
.legal-foot a:hover { color: var(--text-primary); }
.legal-foot .dot { width: 1px; height: 12px; background: var(--border-subtle); }

/* ═══════════════════════════════════════════════════════════════════════════
   共用按鈕系統 — .btn-secondary（outlined）/ .btn-ghost（無框）

   收斂自六份幾乎相同的規則：
     outlined  #reload-btn · .followup-btn · .qb-action-btn
     ghost     .reveal-btn · .retry-btn · .qb-action-btn--subtle

   語意 class（.reveal-btn 等）保留，但只當 JS 的行為 hook，不再帶樣式 ——
   樣式與行為分離，所以 app.js 的 querySelector 完全不用動。
   佈局（如 .followup-btn 的 margin-left: auto）屬於「使用處」，不進元件。
   ═══════════════════════════════════════════════════════════════════════════ */

.btn-secondary,
.btn-ghost,
.btn-primary {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  flex-shrink: 0;
  height: 32px;
  padding: 0 var(--btn-pad-x);
  border-radius: var(--radius-md);
  background-color: transparent;
  font-family: var(--font-zh-sans);
  font-size: var(--text-sm);
  font-weight: 400;
  line-height: 1;
  white-space: nowrap;
  cursor: pointer;
  transition: background-color var(--duration-base) var(--ease-out),
              border-color var(--duration-base) var(--ease-out),
              color var(--duration-base) var(--ease-out),
              transform var(--duration-base) var(--ease-out);
}
/* Icon 尺寸依設計系統的 Button 基底（Primitives.jsx: <Icon size={16} />）。
   收斂前是不一致的：#reload-btn 吃 HTML 的 width="16"，而 .followup-btn /
   .retry-btn 被 CSS 壓成 12px。全部回到 16px。 */
.btn-secondary svg,
.btn-ghost svg,
.btn-primary svg { width: 16px; height: 16px; flex-shrink: 0; }

.btn-secondary:disabled,
.btn-ghost:disabled,
.btn-primary:disabled { opacity: 0.45; cursor: default; }

/* Size ladder — the base is sm (32 / 13). md is what modal footers use.
   （設計系統 Primitives.jsx 的 Button：heights = {sm:32, md:40, lg:48}） */
.btn--md {
  height: 40px;
  font-size: var(--btn-font-md);
}

/* Outlined */
.btn-secondary {
  border: 1px solid var(--border-default);
  color: var(--text-primary);
}
.btn-secondary:hover:not(:disabled) {
  border-color: var(--text-secondary);
  background-color: var(--bg-subtle);
}

/* Ghost — transparent border keeps the box the same size as .btn-secondary,
   so the two never jump against each other when sitting in the same row. */
.btn-ghost {
  border: 1px solid transparent;
  color: var(--text-muted);
}
.btn-ghost:hover:not(:disabled) {
  color: var(--text-secondary);
  background-color: var(--bg-subtle);
}

/* Louder ghost — 「參考回答」比「重新回答」在語意上更主要。這是層級，不是不一致。 */
.btn-ghost--strong { color: var(--text-secondary); }
.btn-ghost--strong:hover:not(:disabled) { color: var(--text-primary); }

/* Quieter secondary — modal footer 的取消鈕（「先不要」「取消」）。仍是 outlined，
   但字色退一階：它在對話框裡是「不要做那件事」，不該和主鈕爭注意力。 */
.btn-secondary--quiet { color: var(--text-secondary); }
.btn-secondary--quiet:hover:not(:disabled) { color: var(--text-primary); }

/* Primary — 唯一填 accent 的按鈕。modal 的確認鈕。 */
.btn-primary {
  border: 1px solid transparent;
  background-color: var(--accent);
  color: var(--accent-ink);
  font-weight: 500;
}
/* 主鈕 hover 微上推（設計 BankControls.jsx 的 ConfirmModal 主鈕）。只有 primary
   有 —— 取消鈕（outlined）不上推。transform 已在基底 transition 列表，同步過渡。 */
.btn-primary:hover:not(:disabled) {
  background-color: var(--accent-hover);
  transform: translateY(-1px);
}

/* ── Settings Modal ── */
/* ── Settings Modal ── */
.modal-overlay {
  display: none;
  position: fixed;
  inset: 0;
  background: var(--overlay-warm);
  z-index: 100;
  align-items: center;
  justify-content: center;
  padding: 24px;
  backdrop-filter: blur(6px) saturate(140%);
  -webkit-backdrop-filter: blur(6px) saturate(140%);
}
.modal-overlay.open {
  display: flex;
  animation: settingsScrim var(--duration-entry) var(--ease-entry) both;
}
.modal {
  background: var(--bg-elevated);
  border-radius: var(--radius-xl);
  width: 100%;
  max-width: 560px;
  box-shadow: var(--shadow-elevated);
  overflow: hidden;
  border: 1px solid var(--border-subtle);
  display: flex;
  flex-direction: column;
  animation: settingsPop var(--duration-entry) var(--ease-entry) both;
}
/* 確認型對話框 —— 只有一段文字與兩顆按鈕，560px 會空得發慌。設計的 ConfirmModal
   是 480px。設定 modal 內容多得多（等級五宮格 + textarea），維持 560px。 */
.modal--compact { max-width: 480px; }
.modal-header {
  padding: 24px 28px 18px;
  border-bottom: 1px solid var(--border-subtle);
}
/* 中文優先（docs/design-handoff/o1/CLAUDE.md 硬規則）：eyebrow 一律中文、用
   var(--font-zh-sans)，不得把 font-en-display + uppercase 套在中文字串上。
   這條規則以前沒被遵守 —— 因為當時唯一的 eyebrow 是英文的 SETTINGS，套
   Newsreader + 大寫 + accent 橘完全合理。題庫的「更換來源」進來之後就錯了。
   設計的 eyebrow 是安靜的 text-muted，不是 accent。 */
.modal-eyebrow {
  font-family: var(--font-zh-sans);
  font-size: var(--text-xs);
  letter-spacing: 0.1em;
  color: var(--text-muted);
  margin: 0;
  font-weight: 500;
}
.modal-title {
  font-family: var(--font-zh-serif);
  /* 設計是 21px —— 不在 type scale 上（19 / 24）。取最近的 --text-h3，2px 的差異
     視覺上很輕微，不值得為單一用途再開一個 token。 */
  font-size: var(--text-h3);
  color: var(--text-primary);
  margin: 4px 0;
  font-weight: 500;
  line-height: 1.3;
}
.modal-subtitle {
  font-family: var(--font-zh-sans);
  font-size: var(--text-sm);
  color: var(--text-muted);
  margin: 0;
  line-height: 1.55;
}
.modal-body {
  padding: 24px 28px 8px;
  display: flex;
  flex-direction: column;
  gap: 24px;
}
.modal-section {}
.modal-field-label {
  display: flex;
  align-items: baseline;
  gap: 8px;
  margin-bottom: 12px;
}
.modal-field-label label {
  font-family: var(--font-zh-sans);
  font-size: var(--text-sm);
  font-weight: 500;
  color: var(--text-primary);
  letter-spacing: 0.01em;
}
.modal-field-optional {
  font-family: var(--font-zh-sans);
  font-size: var(--text-xs);
  color: var(--text-muted);
  font-weight: 400;
}
.modal-field-hint {
  font-family: var(--font-zh-sans);
  font-size: var(--text-xs);
  color: var(--text-muted);
  margin: 0 0 10px 2px;
  line-height: 1.5;
}
.level-btns {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 6px;
}
.level-btn {
  border: 1px solid var(--border-default);
  background: var(--bg-subtle);
  border-radius: var(--radius-md);
  padding: 12px 0;
  font-size: var(--text-body-lg);
  font-weight: 500;
  cursor: pointer;
  transition: all 120ms var(--ease-out);
  color: var(--text-secondary);
  font-family: var(--font-en-display);
  letter-spacing: 0.04em;
  line-height: 1;
}
.level-btn:hover {
  background: var(--bg-elevated);
  border-color: var(--text-secondary);
  color: var(--text-primary);
}
.level-btn.selected {
  border: 2px solid var(--accent);
  background: var(--accent-soft);
  color: var(--accent);
  font-weight: 600;
}
.level-desc {
  margin: 12px 2px 0;
  font-family: var(--font-zh-sans);
  font-size: var(--text-sm);
  color: var(--text-secondary);
  line-height: 1.55;
}
.level-desc-label {
  font-family: var(--font-en-display);
  font-size: var(--text-xs);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--accent);
  margin-right: 8px;
}
.level-change-notice {
  padding: 10px 12px;
  border-radius: var(--radius-md);
  background: var(--status-partial-bg);
  border: 1px solid var(--status-partial);
  display: flex;
  align-items: flex-start;
  gap: 10px;
  font-family: var(--font-zh-sans);
  font-size: var(--text-sm);
  color: var(--text-secondary);
  line-height: 1.55;
  max-height: 0;
  overflow: hidden;
  opacity: 0;
  margin-top: 0;
  transition: max-height 380ms var(--ease-entry),
              opacity 280ms ease 80ms,
              margin-top 380ms var(--ease-entry);
}
.level-change-notice.is-visible {
  max-height: 80px;
  opacity: 1;
  margin-top: 12px;
}
.notice-icon {
  color: var(--status-partial);
  flex: 0 0 auto;
  margin-top: 1px;
}
.notice-accent {
  color: var(--accent);
  font-weight: 500;
}
.modal-textarea {
  width: 100%;
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
  padding: 12px 14px;
  font-size: var(--text-body);
  font-family: var(--font-zh-serif);
  resize: vertical;
  min-height: 92px;
  outline: none;
  transition: border-color var(--duration-base);
  background: var(--bg-subtle);
  color: var(--text-primary);
  line-height: 1.65;
}
.modal-textarea:focus {
  border-color: var(--accent);
}
.modal-footer {
  padding: 18px 28px 22px;
  margin-top: 8px;
  border-top: 1px solid var(--border-subtle);
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 8px;
}
/* .modal-cancel-btn → .btn-secondary .btn-secondary--quiet .btn--md
   .modal-save-btn   → .btn-primary .btn--md
   （見「共用按鈕系統」。兩個 class 只剩 JS 行為 hook 的身分。）

   收斂前它們是第三份重複的按鈕 CSS，而且與設計不符：取消鈕沒有邊框（設計是
   outlined）、兩顆都是 34px/15px（設計是 40px/14px）。 */
@keyframes settingsScrim {
  from { opacity: 0; }
  to   { opacity: 1; }
}
@keyframes settingsPop {
  from { opacity: 0; transform: translateY(8px) scale(0.98); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}
@keyframes settingsDescFade {
  from { opacity: 0; transform: translateY(2px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* ── Main Layout ──
   app 的左右兩欄版面規則**只掛 `.tab-panel > main`**（見下方 ARTICLE LEARNING PANEL 前一節）。
   這裡原本有一條**未限定範圍**的 `main { display:grid; 60fr 40fr; … }`，與 scoped 那條逐字相同
   （9 條宣告零差異），對 app 完全多餘（三個 <main> 都是 .tab-panel 直接子元素），卻會**認領任何
   新增的獨立 <main> 頁面** —— 法務頁 `<main class="legal">` 就被它排成左右交錯的兩欄。
   **勿再新增無範圍限定的 `main` 版面規則**；獨立頁面請自帶容器樣式。 */

/* ── Left: Exercises Panel ── */
.exercises-panel {
  display: flex;
  flex-direction: column;
}

.panel-header {
  padding: 16px 20px 12px;
  border-bottom: 1px solid var(--border-default);
  flex-shrink: 0;
  position: relative;
}
.exercises-panel .panel-header {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 16px;
  padding: 20px 20px 20px 24px;
  border-bottom: 1px solid var(--border-subtle);
}
.panel-header-left { display: flex; flex-direction: column; }

/* ── Practice Header — h1 title ── */
.practice-header__title {
  font-family: var(--font-zh-serif);
  font-size: 30px; /* intentional: D-1A prototype value */
  font-weight: 500;
  line-height: 1.2;
  color: var(--text-primary);
  margin: 0;
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 14px;
  letter-spacing: 0.01em;
}
.practice-header__divider {
  font-family: var(--font-en-display);
  font-size: 28px; /* intentional: D-1A prototype value */
  font-weight: 300;
  color: var(--text-muted);
  letter-spacing: 0.02em;
}
.practice-header__num {
  font-family: var(--font-en-display);
  font-size: 30px; /* intentional: D-1A prototype value */
  font-weight: 400;
  color: var(--text-primary);
  font-feature-settings: "lnum" 1;
  letter-spacing: -0.01em;
}

/* ── Practice Header — date row ── */
.practice-header__date {
  font-family: var(--font-zh-sans);
  font-size: 13px; /* intentional: D-1A prototype value */
  line-height: 1.5;
  color: var(--text-muted);
  margin: 14px 0 0;
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 20px;
  opacity: 0;
  transition: opacity 240ms ease-out;
}
.practice-header__date-label {
  font-family: var(--font-en-display);
  letter-spacing: 0.06em;
  color: var(--text-muted);
}

/* ── Chat Panel Header (scoped — does not affect exercises panel) ── */
.chat-panel .panel-header,
.article-chat-panel .panel-header {
  background: var(--bg-surface);
  padding: 20px 24px 16px;
  border-bottom: 1px solid var(--border-subtle);
}
.chat-panel-eyebrow {
  font-family: var(--font-en-display);
  font-size: 11px; /* intentional: D-2A prototype value */
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--accent);
  font-weight: 500;
  margin: 0 0 6px;
}
.chat-panel .chat-panel-heading,
.article-chat-panel .chat-panel-heading {
  font-family: var(--font-zh-serif);
  font-size: 22px; /* intentional: D-2A prototype value */
  font-weight: 500;
  color: var(--text-primary);
  margin: 0 0 4px;
  display: flex;
  align-items: baseline;
  gap: 12px;
}
.chat-panel-heading__divider {
  font-family: var(--font-en-display);
  font-size: 14px; /* intentional: D-2A prototype value */
  font-weight: 300;
  color: var(--text-muted);
  letter-spacing: 0.02em;
}
.chat-panel-heading__sub {
  font-family: var(--font-zh-serif);
  font-size: 16px; /* intentional: D-2A prototype value */
  font-weight: 400;
  color: var(--text-secondary);
}
.chat-panel-subtitle {
  font-family: var(--font-zh-sans);
  font-size: 12.5px; /* intentional: D-2A prototype value */
  color: var(--text-muted);
  margin: 0;
  line-height: 1.6;
}

.exercises-list {
  padding: 16px 20px;
  display: flex;
  flex-direction: column;
  gap: 12px;
  flex: 1;
}

/* ── Exercise Card ── */
.exercise-card {
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  padding: 22px 24px 20px;
  position: relative;
  transition: background 240ms var(--ease-entry), border-color var(--duration-fast) var(--ease-out);
}
.exercise-card:hover { border-color: var(--border-default); }
.exercise-card.correct { background: var(--status-correct-bg); }
.exercise-card.partial { background: var(--status-partial-bg); }
.exercise-card.wrong   { background: var(--status-wrong-bg); }

.exercise-card-bar {
  position: absolute;
  left: 0; top: 0; bottom: 0;
  width: 4px;
  background: transparent;
  border-top-left-radius: var(--radius-lg);
  border-bottom-left-radius: var(--radius-lg);
  pointer-events: none;
  transition: background 240ms var(--ease-entry);
}
.exercise-card.correct .exercise-card-bar { background: var(--status-correct); }
.exercise-card.partial .exercise-card-bar { background: var(--status-partial); }
.exercise-card.wrong   .exercise-card-bar { background: var(--status-wrong); }

.card-header {
  display: flex;
  align-items: baseline;
  gap: 10px;
  margin-bottom: 16px;
}
.card-num {
  font-family: var(--font-en-display);
  /* intentional: structural value between --text-h1 (32) and --text-body-lg (17);
     en-display numeral hierarchy for practice card. See Phase 1 D-1. */
  font-size: 28px;
  font-weight: 400;
  line-height: 1;
  color: var(--text-primary);
  font-feature-settings: "lnum" 1, "tnum" 1;
  min-width: 32px;
  flex: 0 0 auto;
  height: 32px;
  display: flex;
  align-items: center;
}
.card-divider {
  width: 1px;
  height: 18px;
  background: var(--border-subtle);
  flex: 0 0 auto;
  align-self: center;
}
.card-meta-group {
  display: flex;
  align-items: baseline;
  gap: 4px;
  flex: 0 0 auto;
}
.card-meta-label {
  font-family: var(--font-zh-sans);
  font-size: var(--text-xs);
  letter-spacing: 0.04em;
  color: var(--text-muted);
  flex: 0 0 auto;
  line-height: 1;
}
.card-meta-dot {
  font-family: var(--font-zh-sans);
  font-size: var(--text-xs);
  color: var(--text-muted);
  opacity: 0.5;
  flex: 0 0 auto;
  line-height: 1;
}
.card-level-badge {
  font-family: var(--font-en-display);
  font-size: var(--text-xs);
  letter-spacing: 0.08em;
  color: var(--text-muted);
  flex: 0 0 auto;
  line-height: 1;
}
.card-header-action {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 10px;
  flex: 0 0 auto;
  align-self: center;
}
.card-status-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 32px;
  padding: 0 12px;
  font-family: var(--font-zh-sans);
  font-size: var(--text-sm);
}
.card-status-pill svg { width: 14px; height: 14px; flex: 0 0 auto; }
.exercise-card.correct .card-status-pill { color: var(--status-correct); }
.exercise-card.partial .card-status-pill { color: var(--status-partial); }
.exercise-card.wrong   .card-status-pill { color: var(--status-wrong); }
.exercise-card .feedback-box .card-status-pill {
  padding: 0;
  height: auto;
  margin-bottom: 8px;
}
.exercise-card .feedback-box .card-status-pill[hidden] { display: none; }
.comp-graded-body .card-status-pill { padding: 0; height: auto; margin-bottom: 8px; }

.card-question {
  font-family: var(--font-zh-serif);
  /* intentional: 1px above --text-body-lg (17), maintains prompt hierarchy
     in practice card. Mirrors ReadingCard kanji-row decision (Overlay D-6).
     See Phase 1 D-2. */
  font-size: 18px;
  line-height: 1.6;
  color: var(--text-primary);
  letter-spacing: 0.005em;
  text-wrap: pretty;
  margin: 0 0 16px;
}
.card-input-row {
  display: flex;
  gap: 8px;
  align-items: stretch;
}
.card-input-row .answer-input {
  height: 44px;
  padding: 0 16px;
}
.answer-input {
  flex: 1;
  background: var(--bg-base);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
  font-family: var(--font-jp-sans);
  font-size: var(--text-body);
  color: var(--text-primary);
  outline: 0;
  transition: border-color var(--duration-fast) var(--ease-out);
}
.answer-input:focus { border-color: var(--accent); }
.answer-input:disabled { background: transparent; opacity: 0.85; cursor: default; }

.check-btn {
  height: 44px;
  padding: 0 20px;
  background: var(--accent);
  color: var(--accent-ink);
  border: 1px solid transparent;
  border-radius: var(--radius-md);
  font-family: var(--font-zh-sans);
  font-size: var(--text-sm);
  cursor: pointer;
  transition: background var(--duration-fast) var(--ease-out);
  flex: 0 0 auto;
  white-space: nowrap;
}
.check-btn:hover { background: var(--accent-hover); }
.check-btn:disabled { opacity: 0.45; cursor: default; }
.check-btn[hidden] { display: none; }

/* .reveal-btn 的樣式已收斂進 .btn-ghost--strong（見「共用按鈕系統」）；
   這個 class 只剩 JS 行為 hook 的身分。 */

.feedback-box {
  margin: 0 16px 0 52px;
  padding: 10px 12px;
  border-radius: var(--radius-sm);
  font-size: var(--text-sm);
  line-height: 1.6;
  display: none;
}
.exercise-card .feedback-box {
  margin: 18px 0 0;
  padding: 18px 0 0;
  border-radius: 0;
  display: block;
}
.exercise-card .feedback-box[hidden] { display: none; }
.feedback-box.correct { background: var(--status-correct-bg); color: var(--status-correct); border-left: 3px solid var(--status-correct); }
.feedback-box.partial  { background: var(--status-partial-bg); color: var(--status-partial); border-left: 3px solid var(--status-partial); }
.feedback-box.wrong    { background: var(--status-wrong-bg); color: var(--status-wrong); border-left: 3px solid var(--status-wrong); }

.feedback-divider,
.answer-reveal-divider {
  display: block;
  width: 100%;
  height: 1px;
  background: var(--border-subtle);
  margin-bottom: 14px;
}
.feedback-text {
  display: block;
  font-family: var(--font-zh-serif);
  font-size: var(--text-sm);
  line-height: 1.7;
  color: var(--text-secondary);
  text-wrap: pretty;
}

.card-footer {
  display: flex;
  gap: 10px;
  margin-top: 16px;
  align-items: center;
}
.card-footer[hidden] { display: none; }

/* .retry-btn → .btn-ghost，.followup-btn → .btn-secondary（見「共用按鈕系統」）。
   兩個 class 只剩 JS 行為 hook 的身分。

   ⑤ margin-left: auto 是「把追問鈕推到卡片右側」的佈局意圖，不是按鈕本身的樣式 ——
   所以它歸使用處（.card-footer）管，元件不帶它。否則題庫、理解題等其他地方一用
   .followup-btn 就會莫名其妙被推到右邊。 */
.card-footer .followup-btn { margin-left: auto; }

/* ── Chat context banner (exercises tab) — mirrors .selection-context-bar glass layout ── */
.context-banner {
  display: none;
  margin-bottom: 8px;
  border-radius: var(--radius-md);
  padding: 10px 12px 10px 14px;
  align-items: center;
  gap: 10px;
}
.context-banner.active { display: flex; }
/* exercises banner text is Chinese → zh-serif */
.context-banner .sel-context-text { font-family: var(--font-zh-serif); }

/* ── Chat context cards (QuestionContextCard / SelectionContextCard) ── */
.msg.context-msg { align-self: stretch; max-width: 100%; }

/* QuestionContextCard */
.question-ctx-card {
  border: 1px solid var(--accent);
  background: var(--accent-soft);
  border-radius: var(--radius-md); /* D-1B: radius-md 12px */
  padding: 12px 16px 4px;
  width: 100%;
}
.question-ctx-header {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 10px;
}
.question-ctx-header__label {
  font-family: var(--font-zh-sans);
  font-size: 11.5px; /* intentional: matches banner eyebrow convention */
  font-weight: 600;
  color: var(--accent);
  letter-spacing: 0.04em;
}
.question-ctx-header__sep { opacity: 0.4; color: var(--accent); }
.question-ctx-header__num {
  font-family: var(--font-zh-sans);
  font-size: 11.5px; /* intentional */
  font-weight: 400;
  color: var(--accent);
  letter-spacing: 0.04em;
  opacity: 0.92;
}
.question-ctx-lede {
  font-family: var(--font-zh-serif);
  font-size: var(--text-sm);
  line-height: 1.65;
  color: var(--text-primary);
  margin: 0 0 10px;
}
.question-ctx-diff {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding-left: 10px;
  border-left: 2px solid var(--accent);
  margin: 0 0 4px;
}
.question-ctx-diff-label {
  font-family: var(--font-zh-sans);
  font-size: 10.5px; /* intentional: between text-xs 11px and smaller */
  font-weight: 500;
  letter-spacing: 0.08em;
  color: var(--text-muted);
  margin: 0 0 2px;
}
.question-ctx-diff-value {
  font-family: var(--font-jp-serif);
  font-size: 13.5px; /* intentional */
  line-height: 1.6;
  margin: 0;
  letter-spacing: 0.02em;
}
.question-ctx-diff-value--muted  { color: var(--text-secondary); }
.question-ctx-diff-value--primary { color: var(--text-primary); }
.question-ctx-disclosure {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-top: 10px;
  padding: 6px 0;
  background: transparent;
  border: 0;
  color: var(--text-muted);
  font-family: var(--font-zh-sans);
  font-size: 12px;
  letter-spacing: 0.02em;
  cursor: pointer;
  width: 100%;
  justify-content: flex-start;
}
.question-ctx-chevron {
  transition: transform 180ms ease;
  flex-shrink: 0;
}
.question-ctx-chevron--open { transform: rotate(90deg); }
.question-ctx-disclose-body {
  padding: 8px 0 10px;
  border-top: 1px dashed var(--border-default);
  margin-top: 2px;
  animation: ctxExpand 200ms var(--ease-entry) both;
}
.question-ctx-disclose-text {
  font-family: var(--font-zh-serif);
  font-size: 13px;
  line-height: 1.75;
  color: var(--text-secondary);
  margin: 0;
  letter-spacing: 0.005em;
}

/* SelectionContextCard */
.selection-ctx-card {
  border: 1px solid var(--accent);
  background: var(--accent-soft);
  border-radius: var(--radius-md); /* D-1B: consistent with QuestionContextCard */
  padding: 10px 14px;
  width: 100%;
}
.selection-ctx-inner {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  row-gap: 4px;
}
.selection-ctx-eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
  font-family: var(--font-zh-sans);
  font-size: 11.5px; /* intentional */
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--accent);
  line-height: 1;
}
.selection-ctx-snippet {
  flex: 1;
  min-width: 0;
  font-size: 14.5px; /* intentional */
  line-height: 1.6;
  color: var(--text-primary);
  font-weight: 500;
}
.selection-ctx-snippet[lang="ja"] { font-family: var(--font-jp-serif); letter-spacing: 0.02em; }
.selection-ctx-snippet[lang="zh-Hant"] { font-family: var(--font-zh-serif); letter-spacing: 0.005em; }

@keyframes ctxExpand {
  from { opacity: 0; transform: translateY(-2px); }
  to   { opacity: 1; transform: translateY(0); }
}

.answer-reveal {
  margin-top: 18px;
  padding-top: 18px;
}
.answer-reveal[hidden] { display: none; }
.answer-reveal-label {
  display: block;
  font-family: var(--font-zh-sans);
  font-size: var(--text-xs);
  letter-spacing: 0.04em;
  color: var(--text-muted);
  margin-bottom: 6px;
}
.answer-reveal-text {
  display: block;
  font-family: var(--font-jp-serif);
  font-size: var(--text-h3);
  color: var(--text-primary);
  letter-spacing: 0.02em;
  line-height: 1.6;
  margin-bottom: 4px;
}
.answer-reveal-reading {
  display: block;
  font-family: var(--font-jp-sans);
  font-size: var(--text-sm);
  color: var(--text-muted);
}

/* ── Loading / Error States ── */
.loading-placeholder {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  min-height: 60vh;
  color: var(--text-secondary);
  gap: 12px;
}
.spinner {
  width: 36px; height: 36px;
  border: 2.5px solid var(--border-default);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: spin 0.85s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* ── Practice Loading Simple (LS-1 / LS-2) ── */
.practice-loading-simple {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  font-family: var(--font-zh-sans);
  font-size: var(--text-sm);
  font-weight: 500;
  letter-spacing: 0.08em;
  color: var(--accent);
}
.practice-loading-simple__spinner {
  flex-shrink: 0;
  animation: stagedSpin 900ms linear infinite;
}

/* ── Staged Loading Utility (LS-3 練習題生成 + LS-4 理解題生成 共用) ── */
.staged-loading {
  padding: var(--space-7) var(--space-7) var(--space-8);
  display: flex;
  flex-direction: column;
  min-height: 520px;
}
.staged-loading__eyebrow {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 18px;
  font-family: var(--font-zh-sans);
  font-size: var(--text-sm);
  font-weight: 500;
  letter-spacing: 0.08em;
  color: var(--accent);
}
.staged-loading__eyebrow-spinner {
  flex-shrink: 0;
  animation: stagedSpin 900ms linear infinite;
}
.staged-loading__title {
  font-family: var(--font-zh-serif);
  font-size: var(--text-h2);
  line-height: var(--lh-h2);
  font-weight: 500;
  color: var(--text-primary);
  margin: 0 0 10px;
  letter-spacing: -0.005em;
}
.staged-loading__lede {
  font-family: var(--font-zh-sans);
  font-size: var(--text-sm);
  color: var(--text-muted);
  margin: 0 0 36px;
  max-width: 440px;
  line-height: var(--lh-body-lg);
}
.staged-loading__steps {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  max-width: 520px;
}
.staged-loading__step {
  display: grid;
  grid-template-columns: 28px 1fr;
  column-gap: 14px;
  position: relative;
  padding: 10px 14px;
  border-radius: var(--radius-md);
  background: transparent;
  transition: background var(--duration-slow) var(--ease-out);
  animation: stagedStepIn var(--duration-slow) var(--ease-entry) both;
}
.staged-loading__step--active {
  background: var(--accent-soft);
}
.staged-loading__step-marker-col {
  grid-column: 1;
  grid-row: 1 / span 2;
  position: relative;
  display: flex;
  justify-content: center;
}
.staged-loading__connector {
  position: absolute;
  top: 22px;
  bottom: -14px;
  left: 50%;
  width: 1px;
  transform: translateX(-50%);
  background: var(--border-subtle);
}
.staged-loading__step-marker {
  display: none;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  flex-shrink: 0;
  margin-top: 2px;
  align-items: center;
  justify-content: center;
}
.staged-loading__step--pending .staged-loading__step-marker--pending {
  display: inline-flex;
  border: 1.5px solid var(--border-default);
  background: transparent;
}
.staged-loading__step--active .staged-loading__step-marker--active {
  display: inline-flex;
  border: 1.5px solid var(--accent);
  animation: stagedRingPulse 1600ms var(--ease-out) infinite;
}
.staged-loading__step--done .staged-loading__step-marker--done {
  display: inline-flex;
  background: var(--status-correct);
  color: var(--bg-base);
}
.staged-loading__step-marker-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--accent);
}
.staged-loading__step-content {
  grid-column: 2;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}
.staged-loading__step-label-row {
  display: flex;
  align-items: center;
  gap: 10px;
}
.staged-loading__step-label {
  font-family: var(--font-zh-sans);
  font-size: var(--text-body);
  font-weight: 500;
  letter-spacing: 0.01em;
  color: var(--text-secondary);
}
.staged-loading__step--active .staged-loading__step-label {
  font-weight: 600;
  color: var(--text-primary);
}
.staged-loading__step--done .staged-loading__step-label {
  color: var(--text-muted);
}
.staged-loading__step-pulse {
  display: none;
  gap: 3px;
  align-items: center;
}
.staged-loading__step--active .staged-loading__step-pulse {
  display: inline-flex;
}
.staged-loading__step-pulse span {
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: var(--accent);
  animation: stagedPulse 1200ms ease-in-out infinite;
}
.staged-loading__step-pulse span:nth-child(2) { animation-delay: 180ms; }
.staged-loading__step-pulse span:nth-child(3) { animation-delay: 360ms; }
.staged-loading__step-done-badge {
  display: none;
  font-family: var(--font-zh-sans);
  font-size: var(--text-xs);
  letter-spacing: 0.08em;
  color: var(--status-correct);
  font-weight: 500;
}
.staged-loading__step--done .staged-loading__step-done-badge {
  display: inline;
}
.staged-loading__step-sub {
  margin: 0;
  font-family: var(--font-zh-sans);
  font-size: var(--text-sm);
  line-height: var(--lh-tight);
  color: var(--text-muted);
}
.staged-loading__step--active .staged-loading__step-sub {
  color: var(--text-secondary);
}
.staged-loading__step--done .staged-loading__step-sub {
  opacity: 0.65;
}
.staged-loading__step-bar {
  display: none;
  margin-top: var(--space-2);
  height: 3px;
  border-radius: var(--radius-full);
  width: 60%;
  background: linear-gradient(
    90deg,
    var(--accent-soft) 0%,
    var(--accent)      50%,
    var(--accent-soft) 100%
  );
  background-size: 200% 100%;
  animation: stagedBarShimmer 1600ms ease-in-out infinite;
}
.staged-loading__step--active .staged-loading__step-bar {
  display: block;
}
.staged-loading__coach-voice {
  margin-top: var(--space-6);
  display: flex;
  align-items: center;
  gap: 10px;
  height: 20px;
}
.staged-loading__coach-dot {
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: var(--accent);
  opacity: 0.7;
  flex-shrink: 0;
  animation: stagedPulse 1400ms ease-in-out infinite;
}
.staged-loading__coach-text {
  margin: 0;
  font-family: var(--font-zh-serif);
  font-size: var(--text-sm);
  color: var(--text-secondary);
  font-style: italic;
  animation: stagedCoachIn var(--duration-entry) var(--ease-entry) both;
}

/* ── Staged Loading Keyframes ── */
@keyframes stagedSpin {
  to { transform: rotate(360deg); }
}
@keyframes stagedStepIn {
  from { opacity: 0; transform: translateX(-6px); }
  to   { opacity: 1; transform: translateX(0); }
}
@keyframes stagedRingPulse {
  0%, 100% { box-shadow: 0 0 0 0 var(--accent-soft); }
  50%      { box-shadow: 0 0 0 6px transparent; }
}
@keyframes stagedPulse {
  0%, 100% { transform: scale(1);   opacity: 0.7; }
  50%      { transform: scale(1.6); opacity: 0.25; }
}
@keyframes stagedBarShimmer {
  0%   { background-position: -200% 0; }
  100% { background-position:  200% 0; }
}
@keyframes stagedCoachIn {
  from { opacity: 0; transform: translateY(4px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* ── Right: Q&A Chat Panel ── */
.chat-panel {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: 16px; /* intentional: D-1A prototype value */
  box-shadow: var(--shadow-card);
  position: sticky;   /* 也是 .coach-drop-overlay(inset:0) 的 containing block；勿改成 relative（見 §拖放覆蓋層註解） */
  top: 80px;          /* sticky 吸附偏移；position 一旦被打成 relative，這裡會變成向下位移 80px → 面板偏下 */
  height: calc(100vh - 112px);
  min-height: 640px;
}

.chat-reset-btn {
  display: none;
  position: absolute;
  top: 18px;
  right: 18px;
  padding: 6px 12px;
  border-radius: var(--radius-full);
  font-family: var(--font-zh-sans);
  font-size: var(--text-xs);
  color: var(--text-primary);
  letter-spacing: 0.02em;
  align-items: center;
  gap: 6px;
  cursor: pointer;
  transition: opacity var(--duration-base) var(--ease-out);
  white-space: nowrap;
}
.chat-reset-btn.visible { display: inline-flex; }
.chat-reset-btn:hover { opacity: 0.75; }

.chat-messages {
  flex: 1;
  overflow-y: auto;
  padding: 18px 22px; /* intentional: D-6B prototype value (was 20px) */
  display: flex;
  flex-direction: column;
  gap: 24px; /* intentional: D-6B prototype value — both ChatPanel.jsx prototypes use 24px */
}

.msg {
  display: flex;
  gap: 10px;
}
.msg.user      { align-self: flex-end;   flex-direction: row-reverse; max-width: 80%; }  /* intentional: D-6B user max-width 80% per prototype */
.msg.assistant { align-self: flex-start; max-width: 92%; }

.avatar {
  width: 28px; height: 28px; /* intentional: D-6B prototype value (was 30px) */
  border-radius: 50%;
  border: 1px solid var(--border-default);
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  flex: 0 0 28px;
  margin-top: 2px; /* intentional: D-6B CoachPanel.jsx prototype marginTop:2 */
}
.msg.assistant .avatar { color: var(--accent); } /* intentional: D-6B prototype + reply-actions-spec v1.0 — accent 橘色 */

.bubble {
  padding: 10px 14px;
  border-radius: var(--radius-lg);
  max-width: 100%;
}
.msg.user .bubble {
  background: var(--accent-soft); /* intentional: D-6B D-B, accent-soft per prototype (was accent) */
  color: var(--text-primary);
  border-radius: 16px 16px 6px 16px; /* intentional: D-6B D-C, chat bubble cut corner per prototype */
  font-family: var(--font-zh-sans);
  font-size: 14px; /* intentional: D-6B D-F, prototype value; --text-sm=13px --text-body=15px, no exact token */
  line-height: 1.55; /* intentional: D-6B, user bubble tighter than assistant (1.7); prototype explicit value */
}
.msg.assistant .bubble {
  padding: 0; /* intentional: D-6B prose wrapper has no padding in prototype; user bubble keeps padding */
}

.chat-wait-dots {
  display: inline-flex;
  gap: 3px;
  align-items: center;
  padding: 2px 0;
}
.chat-wait-dots span {
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: var(--accent);
  animation: stagedPulse 1200ms ease-in-out infinite;
}
.chat-wait-dots span:nth-child(2) { animation-delay: 180ms; }
.chat-wait-dots span:nth-child(3) { animation-delay: 360ms; }

/* .chat-welcome removed — article panel now uses .chat-empty (see EmptyStates refactor) */
/* .coach-welcome is the exercise panel component, still uses chat-welcome class as a DOM hook */

/* Zero padding from .chat-messages when CoachWelcome is the sole child */
.chat-messages:has(> .coach-welcome:only-child) {
  padding: 0;
}

/* CoachWelcome panel-fill — overrides .chat-welcome card styling */
.coach-welcome {
  background: transparent;
  border: none;
  border-radius: 0;
  box-shadow: none;
  text-align: left;
  flex: 1;
  display: flex;
  flex-direction: column;
  padding: var(--space-6) 28px var(--space-5);
}
.coach-welcome h3 {
  font-family: var(--font-zh-serif);
  font-size: var(--text-h3);
  color: var(--text-primary);
  font-weight: 500;
  letter-spacing: 0.005em;
  margin: 0 0 12px;
}
.coach-welcome > p {
  font-family: var(--font-zh-serif);
  font-size: var(--text-sm);
  color: var(--text-secondary);
  line-height: 1.85;
  text-wrap: pretty;
}

/* Coach mark */
.welcome-coach-mark {
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: 1px solid var(--border-default);
  background: var(--bg-subtle);
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 18px;
  color: var(--accent);
  flex-shrink: 0;
}

/* R1: Skeleton grid */
.suggestion-skeleton-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-2);
  margin-top: 28px;
}
.suggestion-skeleton {
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  padding: 14px 14px 16px;
  min-height: 78px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.skeleton-line {
  height: 11px;
  border-radius: var(--radius-misc-1);
  background: linear-gradient(90deg, var(--bg-subtle) 0%, var(--border-subtle) 50%, var(--bg-subtle) 100%);
  background-size: 200% 100%;
  animation: stagedBarShimmer 1800ms ease-in-out infinite;
}

/* R1: Analyzing hint pinned to bottom */
.welcome-analyzing-hint {
  margin-top: auto;
  padding-top: var(--space-5);
  display: flex;
  align-items: center;
  gap: 10px;
  font-family: var(--font-zh-sans);
  font-size: var(--text-sm);
  color: var(--text-muted);
}
.welcome-analyzing-dot {
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--text-muted);
  flex-shrink: 0;
  animation: stagedPulse 1400ms ease-in-out infinite;
}

/* R2: Curriculum summary paragraphs */
.curriculum-summary {
  font-family: var(--font-zh-serif);
  font-size: var(--text-sm);
  color: var(--text-secondary);
  line-height: 1.85;
  text-wrap: pretty;
  margin-bottom: 6px;
}
.curriculum-summary:last-of-type { margin-bottom: 0; }

/* R2: Suggestion card grid */
.suggestion-card-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-2);
  margin-top: 28px;
}
.suggestion-card {
  display: block;
  width: 100%;
  text-align: left;
  background: var(--bg-subtle);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  padding: 12px 14px 13px;
  min-height: 78px;
  font-family: var(--font-zh-serif);
  font-size: var(--text-sm);
  line-height: 1.55;
  color: var(--text-secondary);
  text-wrap: pretty;
  cursor: pointer;
  transition: background 160ms var(--ease-out), border-color 160ms var(--ease-out), color 160ms var(--ease-out);
  animation: stagedCoachIn 360ms var(--ease-entry) both;
}
.suggestion-card:hover {
  background: var(--accent-soft);
  border-color: var(--accent-border);
  color: var(--text-primary);
}
.suggestion-card:disabled,
.suggestion-card:disabled:hover {
  opacity: 0.4;
  cursor: default;
  background: var(--bg-subtle);
  border-color: var(--border-subtle);
  color: var(--text-secondary);
}

/* R2 entry fade-in */
.coach-welcome.welcome-ready {
  animation: welcomeFadeIn 240ms var(--ease-entry) both;
}
@keyframes welcomeFadeIn {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* R2: Refresh footer pinned to bottom */
.welcome-footer {
  margin-top: auto;
  padding-top: var(--space-5);
}
.curriculum-refresh-btn {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 6px 12px;
  background: transparent;
  border: 0;
  border-radius: var(--radius-misc-3);
  color: var(--text-muted);
  font-family: var(--font-zh-sans);
  font-size: var(--text-sm);
  letter-spacing: 0.02em;
  cursor: pointer;
  transition: color 120ms var(--ease-out), background 120ms var(--ease-out);
}
.curriculum-refresh-btn:hover {
  color: var(--text-secondary);
  background: var(--bg-subtle);
}

/* .rec-sites / .rec-site-* removed — superseded by .site-card system (see EmptyStates refactor) */

/* ── Mode Switcher (segmented pill) ── */
.mode-switcher {
  display: inline-flex;
  align-self: flex-start;
  background: var(--bg-subtle);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-full);
  padding: 3px;
  gap: 2px;
}
.mode-switcher-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 12px;
  border-radius: var(--radius-full);
  border: 0;
  cursor: pointer;
  background: transparent;
  color: var(--text-secondary);
  font-family: var(--font-zh-sans);
  font-size: 12.5px; /* intentional: D-4A prototype value */
  font-weight: 400;
  line-height: 1;
  box-shadow: none;
  transition: all 200ms ease;
  white-space: nowrap;
}
.mode-switcher-btn.is-active {
  background: var(--bg-surface);
  font-weight: 500;
  box-shadow: 0 1px 2px rgba(0,0,0,0.20); /* intentional: D-4A prototype active tab shadow */
}
.mode-switcher-btn.is-active.is-qa   { color: var(--accent); }
.mode-switcher-btn.is-active.is-conj { color: var(--status-partial); }

/* ── Composer Wrapper ── */
.composer-wrapper {
  display: flex;
  align-items: center;
  gap: 6px;
  background: var(--bg-subtle);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md); /* 12px, intentional: D-5A prototype value */
  padding: 6px 8px 6px 8px;
  transition: border-color 200ms ease;
}
/* Verb mode: amber border on wrapper, amber send button, hide image upload */
.composer-wrapper.verb-mode {
  border-color: var(--status-partial-border);
}
.composer-wrapper.verb-mode #send-btn,
.composer-wrapper.verb-mode #article-send-btn {
  background: var(--status-partial);
}
.composer-wrapper.verb-mode .img-upload-btn {
  display: none;
}

/* ── Conjugation Card (in chat area) ── */
.conj-card {
  background: var(--bg-base);
  border: 1px solid var(--border-default);
  /* intentional: left border 3px amber (status-partial) editorial tool-output accent; right/top/bottom 1px default. See Stage 2.5-C D-1. */
  border-left: 3px solid var(--status-partial);
  border-radius: 10px;
  overflow: hidden;
  /* intentional: flex-shrink 0 — overflow:hidden sets min-height:0 on flex children, allowing card to collapse to 0px when chat-messages is full. See Stage 2.5-C D-1. */
  flex-shrink: 0;
  box-shadow: var(--shadow-card);
}
.conj-card-header {
  padding: 14px 18px 12px;
  border-bottom: 1px solid var(--border-subtle);
  background: var(--status-partial-bg-subtle);
}
.conj-header-top {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 8px;
}
.conj-tool-icon {
  color: var(--status-partial);
  display: flex;
  align-items: center;
  flex-shrink: 0;
}
.conj-tool-label {
  font-family: var(--font-zh-sans);
  font-size: 11.5px;
  letter-spacing: 0.2em;
  color: var(--status-partial);
  font-weight: 500;
}
.conj-type {
  font-family: var(--font-en-display);
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-left: auto;
}
.conj-header-info {
  display: flex;
  align-items: baseline;
  gap: 12px;
  flex-wrap: wrap;
}
.conj-verb {
  font-family: var(--font-jp-serif);
  font-size: 22px;
  font-weight: 500;
  color: var(--text-primary);
  line-height: 1.2;
}
.conj-reading {
  font-family: var(--font-jp-sans);
  font-size: 13px;
  color: var(--text-secondary);
  letter-spacing: 0.04em;
}
.conj-input-hint {
  font-family: var(--font-zh-sans);
  font-size: 12px;
  color: var(--text-muted);
}
.conj-input-hint .conj-input-form {
  font-family: var(--font-jp-serif);
  color: var(--text-secondary);
}
.conj-input-hint .conj-matched-label {
  color: var(--status-partial);
  font-weight: 500;
}
.conj-groups {
  padding: 4px 18px 16px;
  display: flex;
  flex-direction: column;
}
.conj-group {
  margin-top: 14px;
}
.conj-group-label {
  font-family: var(--font-en-display);
  font-size: 10.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin: 0 0 8px;
  font-weight: 500;
  display: flex;
  align-items: center;
  gap: 8px;
}
.conj-group-label::before {
  content: '';
  display: inline-block;
  width: 10px;
  height: 1px;
  background: var(--border-default);
}
.conj-rows {
  display: grid;
  grid-template-columns: 78px 1fr auto;
  gap: 0;
}
.conj-row {
  display: contents;
}
.conj-row > * {
  padding: 7px 10px;
  align-self: baseline;
}
/* Highlight: matched conjugation form */
.conj-row.is-highlight > * {
  background: var(--status-partial-bg);
}
.conj-row.is-highlight .conj-label { color: var(--status-partial); font-weight: 500; }
.conj-row.is-highlight .conj-value { font-weight: 500; }
/* Left indicator bar for highlight row */
.conj-row.is-highlight .conj-label {
  border-left: 2px solid var(--status-partial);
  padding-left: 8px;
}
.conj-label {
  font-family: var(--font-zh-sans);
  font-size: 11.5px;
  color: var(--text-muted);
  white-space: nowrap;
}
.conj-value {
  font-family: var(--font-jp-serif);
  font-size: 15px;
  color: var(--text-primary);
}
.conj-kana {
  font-family: var(--font-jp-sans);
  font-size: 11.5px;
  color: var(--text-muted);
  letter-spacing: 0.04em;
  text-align: right;
}

/* ── Chat Input ── */
.chat-input-area {
  padding: 12px 16px 16px;
  border-top: 1px solid var(--border-default);
  background: var(--bg-surface);
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
#chat-input, #article-chat-input {
  flex: 1;
  border: 0;
  outline: 0;
  resize: none;
  padding: 8px 4px;
  font-size: 14.5px; /* intentional: D-5A prototype value */
  font-family: var(--font-zh-serif); /* QA mode default: zh-serif per prototype D-5A */
  min-height: 40px;
  max-height: 120px;
  background: transparent;
  color: var(--text-primary);
  line-height: 1.6;
  transition: font-family 120ms;
}
/* Conjugate mode: jp-serif font + extra left padding */
.composer-wrapper.verb-mode #chat-input,
.composer-wrapper.verb-mode #article-chat-input {
  font-family: var(--font-jp-serif);
  padding: 8px 4px 8px 12px; /* intentional: D-5A extra left pad in conjugate mode */
}
#send-btn, #article-send-btn {
  background: var(--accent);
  color: var(--accent-ink);
  border: none;
  border-radius: 8px; /* intentional: D-6A prototype value, between --radius-sm(6) and --radius-md(12) */
  width: 34px; height: 34px;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: background-color 200ms ease;
  flex-shrink: 0;
}
#send-btn:hover, #article-send-btn:hover { background: var(--accent-hover); }
#send-btn:disabled, #article-send-btn:disabled { opacity: 0.45; cursor: default; }

/* ── API Key Warning ── */
.api-warning {
  background: var(--status-partial-bg);
  border: 1px solid var(--status-partial);
  border-radius: var(--radius-md);
  padding: 10px 14px;
  font-size: var(--text-sm);
  color: var(--status-partial);
  margin: 14px;
  display: none;
}

/* ── Scrollbar ── */
::-webkit-scrollbar { width: 5px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--border-default); border-radius: 3px; }
::-webkit-scrollbar-thumb:hover { background: var(--bg-elevated); }

/* ── Responsive ── */
@media (max-width: 768px) {
  main,
  .tab-panel > main {
    grid-template-columns: 1fr;
    gap: 0;
    padding: 16px 16px 40px;
  }
  .chat-panel,
  .article-chat-panel {
    position: static;
    height: auto;
    min-height: 480px;
  }
}

/* ══════════════════════════════════════════
   TAB NAVIGATION
══════════════════════════════════════════ */
.tab-btn {
  background: transparent;
  border: none;
  border-bottom: 2px solid transparent;
  padding: 0 16px;
  height: 100%;
  display: flex;
  align-items: center;
  gap: 6px;
  font-family: var(--font-zh-sans);
  font-size: var(--text-sm);
  font-weight: 400;
  letter-spacing: 0;
  color: var(--text-secondary);
  cursor: pointer;
  transition: color var(--duration-base), border-color var(--duration-base), font-weight var(--duration-base);
}
.tab-btn:hover { color: var(--text-primary); }
.tab-btn.active {
  color: var(--text-primary);
  font-weight: 500;
  letter-spacing: 0.04em;
  border-bottom-color: var(--accent);
}
.tab-icon {
  width: 15px;
  height: 15px;
  flex-shrink: 0;
}

.tab-panel { display: none; }
.tab-panel.active { display: flex; flex-direction: column; }

.tab-panel > main {
  display: grid;
  grid-template-columns: minmax(0, 60fr) minmax(0, 40fr);
  gap: 32px;
  align-items: flex-start;
  max-width: 1280px;
  width: 100%;
  margin: 0 auto;
  padding: 32px 32px 80px;
}

/* ══════════════════════════════════════════
   ARTICLE LEARNING PANEL
══════════════════════════════════════════ */
.article-panel {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: 16px; /* intentional: matches chat-panel D-1A */
  box-shadow: var(--shadow-card);
  min-height: calc(100vh - 112px); /* matches article-chat-panel height formula */
}

/* URL input bar — page-level chrome, shown only on article tab via .url-bar-visible */
.url-input-bar {
  display: none;
  background: var(--bg-base);
  border-bottom: 1px solid var(--border-default); /* intentional: border-default (not border-subtle per prototype); stronger separation desired. See UrlBar D-2. */
}
.url-input-bar.url-bar-visible {
  display: block;
}
/* Compact mode: S2/S3/S4 (phase !== S1) — triggered by analyzeUrl() start */
.url-input-bar.url-bar-compact .url-input-bar__inner {
  padding: 8px var(--space-6);
}
.url-input-bar.url-bar-compact .url-input-wrapper {
  height: 30px;
  border-radius: 8px; /* intentional: 8px compact (vs radius-md 12px full); matches prototype compact spec. See UrlBar D-8. */
  padding: 0 10px;
}
.url-input-bar.url-bar-compact .url-input-bar__label {
  font-size: 11.5px; /* intentional: 11.5px compact (vs 12.5px full); matches prototype compact spec. See UrlBar D-8. */
}
.url-input-bar.url-bar-compact #url-input {
  font-size: 12px; /* intentional: 12px compact (vs --text-sm 13px full); matches prototype compact spec. See UrlBar D-8. */
}
.url-input-bar.url-bar-compact #analyze-btn {
  height: 32px;
  padding: 0 14px;
}
.url-input-bar__inner {
  max-width: 1280px;
  margin: 0 auto;
  padding: 14px var(--space-6); /* intentional: 14px vertical (between --space-3 12px and --space-4 16px); matches prototype breathing. See UrlBar D-9. */
  display: flex;
  align-items: center;
  gap: 12px;
}
.url-input-bar__label {
  font-family: var(--font-zh-sans);
  font-size: 12.5px; /* intentional: 12.5px (not --text-sm 13px); matches prototype exactly. See UrlBar D-3a. */
  color: var(--text-muted);
  letter-spacing: 0.02em;
  flex: 0 0 auto;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 120px;
}
.url-input-wrapper {
  flex: 1;
  display: flex;
  align-items: center;
  gap: 10px;
  background: var(--bg-subtle);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
  padding: 0 14px;
  height: 40px;
  transition: border-color var(--duration-base), box-shadow var(--duration-base);
}
.url-input-wrapper:focus-within {
  border-color: var(--accent);
}
.url-input-search-icon {
  flex: 0 0 auto;
  width: 15px;
  height: 15px;
  color: var(--text-muted);
  stroke: currentColor;
  fill: none;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
#url-input {
  flex: 1;
  border: none;
  outline: none;
  background: transparent;
  color: var(--text-primary);
  font-size: var(--text-sm);
  font-family: var(--font-en-mono);
  letter-spacing: 0.01em;
}
#analyze-btn {
  background: var(--accent);
  color: var(--accent-ink);
  border: 1px solid transparent;
  border-radius: var(--radius-md);
  height: 40px;
  padding: 0 20px;
  font-size: var(--text-sm);
  font-weight: 400;
  font-family: var(--font-jp-sans); /* intentional: font-jp-sans (not font-zh-sans per prototype); app-wide button convention. See UrlBar D-6. */
  cursor: pointer;
  white-space: nowrap;
  transition: background var(--duration-base);
  letter-spacing: 0.02em;
  display: flex;
  align-items: center;
  gap: 6px;
}
#analyze-btn:hover { background: var(--accent-hover); }
#analyze-btn:disabled {
  opacity: 0.6; /* intentional: 0.6 (aligns with prototype loading state, was 0.45). See UrlBar D-7. */
  pointer-events: none;
}
.parse-spinner {
  display: inline-flex;
  width: 13px;
  height: 13px;
  flex: 0 0 auto;
}
.parse-spinner svg {
  animation: spin 900ms linear infinite;
  stroke: currentColor;
  fill: none;
  stroke-width: 2.4;
  stroke-linecap: round;
}

/* Article body */
.article-body {
  padding: 22px 26px;
  font-size: var(--text-sm);
  line-height: 1.85;
  position: relative;
  flex: 1;
  display: flex;
  flex-direction: column;
}
/* .article-body table/th/td removed — superseded by .prose table rules. See AnalysisChunks D-3/D-4. */

/* ============================================================
   PROSE — Markdown content system
   Three sizes: .prose-sm (13/1.5) · .prose (15/1.7) · .prose-lg (17/1.8)
   Used by: #analysis-panel (.prose-lg) · AI chat replies (.prose.ai-prose)
   ============================================================ */

.prose, .prose-sm, .prose-lg {
  font-family: var(--font-zh-serif);
  color: var(--text-primary);
}
.prose-sm { font-size: var(--text-sm); line-height: var(--lh-tight); }
.prose    { font-size: var(--text-body); line-height: var(--lh-body-lg); }
/* intentional: line-height 1.8 above --lh-body-lg (1.7); long-form reading needs extra breathing room. See Phase 2 prose D-2. */
.prose-lg { font-size: var(--text-body-lg); line-height: 1.8; }

.prose[data-lang="ja"], .prose-sm[data-lang="ja"], .prose-lg[data-lang="ja"],
.prose[lang="ja"],      .prose-sm[lang="ja"],      .prose-lg[lang="ja"] {
  font-family: var(--font-jp-serif);
}

/* Paragraph */
.prose p, .prose-sm p, .prose-lg p {
  margin: 0 0 1em 0;
  text-indent: 0;
}
.prose p:last-child, .prose-sm p:last-child, .prose-lg p:last-child { margin-bottom: 0; }

/* Strong */
.prose strong, .prose-sm strong, .prose-lg strong {
  font-weight: 600;
  color: var(--text-primary);
}
.prose .callout strong, .prose-sm .callout strong, .prose-lg .callout strong {
  color: var(--accent);
}
.prose blockquote strong, .prose-sm blockquote strong, .prose-lg blockquote strong,
.prose blockquote code,   .prose-sm blockquote code,   .prose-lg blockquote code {
  color: inherit;
  background: transparent;
  padding: 0;
}

/* Inline code */
.prose code, .prose-sm code, .prose-lg code {
  font-family: var(--font-en-mono);
  font-size: 0.9em;
  background: var(--bg-subtle);
  padding: 2px 6px;
  border-radius: var(--radius-misc-1);
  color: var(--status-partial);
}

/* Blockquote */
.prose blockquote, .prose-sm blockquote, .prose-lg blockquote {
  margin: 1em 0;
  padding: 2px 0 2px var(--space-4);
  border-left: 2px solid var(--accent);
  font-style: italic;
  color: var(--text-secondary);
}
.prose blockquote[data-lang="ja"], .prose-sm blockquote[data-lang="ja"], .prose-lg blockquote[data-lang="ja"] {
  font-family: var(--font-jp-serif);
  font-style: normal;
}

/* Unordered list */
.prose ul, .prose-sm ul, .prose-lg ul {
  list-style: none;
  padding-left: 0;
  margin: 0 0 1em 0;
}
.prose ul li, .prose-sm ul li, .prose-lg ul li {
  position: relative;
  padding-left: 1.4em;
  margin-bottom: 4px;
}
.prose ul li::before, .prose-sm ul li::before, .prose-lg ul li::before {
  content: '・';
  position: absolute;
  left: 0.2em;
  color: var(--text-secondary);
  font-family: var(--font-zh-serif);
}

/* Ordered list */
.prose ol, .prose-sm ol, .prose-lg ol {
  list-style: none;
  padding-left: 0;
  margin: 0 0 1em 0;
  counter-reset: prose-counter;
}
.prose ol li, .prose-sm ol li, .prose-lg ol li {
  position: relative;
  padding-left: 2em;
  margin-bottom: 4px;
  counter-increment: prose-counter;
}
.prose ol li::before, .prose-sm ol li::before, .prose-lg ol li::before {
  content: counter(prose-counter) '.';
  position: absolute;
  left: 0;
  font-family: var(--font-en-display);
  font-weight: 500;
  color: var(--text-secondary);
}

/* Headings inside prose */
/* intentional: h3 font-size 18px between --text-body-lg (17) and --text-h3 (19); section-header weight for prose. See Phase 2 prose D-2. */
.prose h3, .prose-sm h3, .prose-lg h3 {
  font-family: var(--font-zh-serif);
  font-size: 18px;
  font-weight: 500;
  margin: 1.4em 0 0.5em;
  color: var(--text-primary);
  display: flex;
  align-items: center;
  gap: 10px;
}
.prose h3.with-rule::before, .prose-sm h3.with-rule::before, .prose-lg h3.with-rule::before {
  content: '';
  display: inline-block;
  width: 18px;
  height: 2px;
  background: var(--accent);
  flex: 0 0 18px;
}
/* intentional: article analysis (.prose-lg) h3 always shows accent rule for stronger section hierarchy;
   chat (.prose) keeps .with-rule opt-in to avoid decorating every grammar explanation heading. */
.prose-lg h3::before {
  content: '';
  display: inline-block;
  width: 18px;
  height: 2px;
  background: var(--accent);
  flex: 0 0 18px;
}
/* intentional: forward-compatible hook; AI prompt currently outputs ## not #.
   Article title is injected via JS (_injectAnalysisHeader). See Analysis 基底 D-5. */
.prose h1, .prose-sm h1, .prose-lg h1 {
  font-family: var(--font-jp-serif);
  font-size: 26px; /* intentional: editorial jp-serif article title; distinct from --text-h1 (32px). See Analysis 基底 D-1. */
  font-weight: 600;
  line-height: 1.4;
  letter-spacing: 0.005em;
  color: var(--text-primary);
  margin: 0 0 12px;
}
/* intentional: jp-serif 18px for Pattern head (句型名稱); affects all .prose h4. See AnalysisChunks D-6 B. */
.prose h4, .prose-sm h4, .prose-lg h4 {
  font-family: var(--font-jp-serif);
  font-size: 18px;
  font-weight: 600;
  margin: 1.2em 0 0.4em;
  color: var(--text-primary);
  letter-spacing: 0.01em;
}
/* intentional: prose h2 not in prototype spec (prototype uses SectionHeader component);
   app補 h2 because markdown ## renders as <h2> vs prototype SectionHeader element — architecture gap;
   long-term solution is AI outputting structured JSON → SectionHeader component. See Phase 2 prose D-8. */
.prose h2, .prose-sm h2, .prose-lg h2 {
  font-family: var(--font-zh-serif);
  font-size: 22px;
  font-weight: 500;
  margin: 1.6em 0 0.6em;
  color: var(--text-primary);
}

/* ── SectionHeader (generated by renderMarkdown from ## 01 一、 pattern) ── */
.prose-section-header {
  display: flex;
  align-items: baseline;
  gap: var(--space-4); /* 16px */
  /* intentional: marginTop 40px — no token; structural breathing between sections. See AnalysisChunks D-1. */
  margin-top: 40px;
  /* intentional: marginBottom 18px — between --space-4(16) and --space-5(24). See AnalysisChunks D-1. */
  margin-bottom: 18px;
  padding-top: 8px;
}
.section-num {
  font-family: var(--font-en-display);
  /* intentional: 30px en-display decorative size; distinct from --text-h1(32px). See AnalysisChunks D-1. */
  font-size: 30px;
  color: var(--accent);
  font-weight: 400;
  line-height: 1;
  letter-spacing: -0.01em;
  flex-shrink: 0;
}
.prose-section-header h2 {
  font-family: var(--font-zh-serif);
  /* intentional: 22px section title; matches prototype SectionHeader h2. See AnalysisChunks D-1. */
  font-size: 22px;
  line-height: 1.3;
  font-weight: 500;
  color: var(--text-primary);
  margin: 0;
}

/* JP inline span (generated by renderMarkdown from 《》 marker) */
.jp-inline {
  font-family: var(--font-jp-serif);
  font-weight: 500;
  letter-spacing: 0.01em;
}

/* Table */
.prose table, .prose-sm table, .prose-lg table {
  width: 100%;
  border-collapse: collapse;
  font-family: var(--font-zh-sans);
  /* intentional: 14px table content; between --text-sm(13) and --text-body(15). See AnalysisChunks D-3. */
  font-size: 14px;
  line-height: 1.6;
  margin: 0 0 1em;
}
.prose thead tr, .prose-sm thead tr, .prose-lg thead tr {
  background: var(--bg-subtle);
  border-bottom: 1px solid var(--border-default);
}
.prose th, .prose-sm th, .prose-lg th {
  text-align: left;
  padding: 12px 14px;
  font-family: var(--font-en-display);
  /* intentional: 11px uppercase editorial header; no token match. See AnalysisChunks D-3. */
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-secondary);
  font-weight: 500;
}
.prose td, .prose-sm td, .prose-lg td {
  padding: 12px 14px;
  border-bottom: 1px solid var(--border-subtle);
}
.prose tbody tr:nth-child(odd), .prose-sm tbody tr:nth-child(odd), .prose-lg tbody tr:nth-child(odd) {
  background: var(--bg-subtle);
}

/* Hairline divider */
.prose hr, .prose-sm hr, .prose-lg hr {
  border: 0;
  height: 1px;
  background: var(--border-subtle);
  margin: 1.4em var(--space-6);
}

/* Links */
.prose a, .prose-sm a, .prose-lg a {
  color: var(--accent);
  text-decoration: none;
}
.prose a:hover, .prose-sm a:hover, .prose-lg a:hover {
  text-decoration: underline;
  text-underline-offset: 3px;
}
.prose a[href^="http"]::after, .prose-sm a[href^="http"]::after, .prose-lg a[href^="http"]::after {
  content: '';
  display: inline-block;
  width: 0.8em; height: 0.8em;
  margin-left: 0.25em;
  vertical-align: -0.05em;
  background-color: currentColor;
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path d='M7 17L17 7M9 7h8v8'/></svg>") center/contain no-repeat;
          mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path d='M7 17L17 7M9 7h8v8'/></svg>") center/contain no-repeat;
}

/* JP example block */
.jp-example { margin: 0.6em 0; }
.jp-example .main {
  font-family: var(--font-jp-serif);
  font-size: 1em;
  line-height: 1.5;
  color: var(--text-primary);
}
.jp-example .reading {
  font-family: var(--font-jp-sans);
  font-size: 0.85em;
  line-height: 1.4;
  color: var(--text-muted);
  margin-top: 2px;
  letter-spacing: 0.04em;
}
.jp-example .gloss {
  font-family: var(--font-zh-serif);
  font-size: 0.9em;
  color: var(--text-secondary);
  margin-top: 2px;
}
/* Label icon hook for jp-example (D-4) */
.prose .jp-example .label-icon,
.prose-lg .jp-example .label-icon,
.prose-sm .jp-example .label-icon {
  display: inline-block;
  width: 13px;
  height: 13px;
  vertical-align: -2px;
  margin-right: 4px;
}

/* Compare callout */
.compare {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-3);
  margin: 1em 0;
  /* intentional: padding 14px between --space-3 (12) and --space-4 (16); compare block visual breathing. See Phase 2 prose D-2. */
  padding: 14px;
  background: var(--bg-subtle);
  border-radius: var(--radius-md);
  border: 1px solid var(--border-subtle);
}
/* intentional: item gap 6px structural gap between compare item tag and content. See Phase 2 prose D-2. */
.compare .item { display: flex; flex-direction: column; gap: 6px; }
.compare .item .tag {
  font-family: var(--font-en-display);
  font-size: var(--text-xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  align-self: flex-start;
}
.compare .item.good .tag { color: var(--status-correct); }
.compare .item.bad  .tag { color: var(--status-wrong); }
.compare .item .main {
  font-family: var(--font-jp-serif);
  font-size: 1em;
  color: var(--text-primary);
  line-height: 1.5;
}
.compare .item .reading {
  font-family: var(--font-jp-sans);
  font-size: 0.82em;
  color: var(--text-muted);
  letter-spacing: 0.04em;
}
.compare .item .gloss {
  font-family: var(--font-zh-serif);
  font-size: 0.88em;
  color: var(--text-secondary);
}

/* Emoji normalization */
.prose .emoji-status { font-style: normal; font-size: 1em; }
.prose .emoji-correct { color: var(--status-correct); }
.prose .emoji-partial { color: var(--status-partial); }
.prose .emoji-wrong   { color: var(--status-wrong);   }
.prose .traffic-light { font-size: 0.85em; vertical-align: 0.05em; }

/* Inline icon-with-text helper */
.prose .icon-tag, .prose-sm .icon-tag, .prose-lg .icon-tag {
  display: inline-flex; align-items: center; gap: 6px; color: var(--accent);
  font-family: var(--font-zh-sans); font-weight: 500; font-size: 0.95em;
}
.prose .icon-tag svg, .prose-sm .icon-tag svg, .prose-lg .icon-tag svg { flex: 0 0 auto; }

/* AI reply prose variant */
/* intentional: font-size 14px between --text-sm (13) and --text-body (15); AI reply compact reading. See Phase 2 prose D-2. */
/* intentional: line-height 1.75 tighter than prose-lg (1.8) but looser than prose (1.7); AI reply readability balance. See Phase 2 prose D-2. */
.ai-prose {
  font-size: 14px;
  line-height: 1.75;
}
/* intentional: ai-prose h3 always shows accent rule (opt-in with-rule is for generic .prose only).
   Stage 2.5-C: AI responses now open with ### headings; rule makes them visually anchored. See Stage 2.5-C D-2. */
.ai-prose h3::before {
  content: '';
  display: inline-block;
  width: 18px;
  height: 2px;
  background: var(--accent);
  flex: 0 0 18px;
}
.bubble.ai-prose {
  /* intentional: remove top padding so first h3 aligns with avatar.
     .bubble has padding:10px 14px; 10px top was pushing h3 down, misaligning ::before with avatar. */
  padding-top: 0;
}
.bubble.ai-prose > p:empty {
  /* intentional: renderMarkdown wraps output in <p>; browser auto-closes <p> before block <h3>,
     creating an empty <p></p> with margin-bottom:1em that pushes h3 down. Hide it to fix alignment. */
  display: none;
}
.bubble.ai-prose h3 {
  /* intentional: (0,2,1) beats .prose h3 (0,1,1); margin-top:0 overrides 1.4em so h3 starts flush.
     align-items:flex-start overrides center so ::before stays at first-line level when h3 wraps. */
  margin-top: 0;
  margin-bottom: 0.6em;
  align-items: flex-start;
}
.bubble.ai-prose h3::before {
  /* intentional: margin-top centers the 2px dash within the first line when align-items is flex-start.
     h3 inherits line-height:1.75 from .ai-prose → line box = 18px × 1.75 = 31.5px → center at 15.75px.
     margin-top:15px puts ::before center at y=16px, matching avatar center (margin-top:2px + 14px = 16px).
     With bubble padding-top:0, h3 top is flush with .msg top, so this aligns perfectly. */
  margin-top: 15px;
}

/* .article-placeholder removed — #article-body now uses .analysis-empty (see EmptyStates refactor) */

/* ── Paywall card ── */
.paywall-card {
  background: var(--status-partial-bg);
  border: 1.5px solid var(--status-partial);
  border-radius: var(--radius-lg);
  padding: 28px 24px;
  max-width: 460px;
  margin: 32px auto;
}
.paywall-icon { font-size: var(--text-h1); text-align: center; margin-bottom: 10px; }
.paywall-card h3 {
  font-family: var(--font-jp-sans);
  font-size: var(--text-body);
  font-weight: 700;
  color: var(--text-primary);
  text-align: center;
  margin-bottom: 12px;
  letter-spacing: 0.04em;
}
.paywall-card > p {
  font-size: var(--text-sm);
  color: var(--text-secondary);
  line-height: 1.75;
  margin-bottom: 14px;
}
.paywall-steps {
  font-size: var(--text-sm);
  color: var(--text-secondary);
  line-height: 1.9;
  padding-left: 18px;
  margin-bottom: 18px;
}
.paywall-steps li { margin-bottom: 2px; }
.paywall-steps kbd {
  background: var(--bg-subtle);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-misc-1);
  padding: 1px 6px;
  font-size: var(--text-xs);
  font-family: var(--font-en-mono);
}
.paywall-cookie-input {
  width: 100%;
  border: 1.5px solid var(--status-partial);
  border-radius: var(--radius-md);
  padding: 10px 12px;
  font-size: var(--text-sm);
  font-family: var(--font-en-mono);
  resize: vertical;
  min-height: 72px;
  max-height: 140px;
  background: var(--bg-surface);
  color: var(--text-primary);
  outline: none;
  margin-bottom: 12px;
  transition: border-color var(--duration-base);
}
.paywall-cookie-input:focus { border-color: var(--status-partial); }
.paywall-cookie-input::placeholder { color: var(--text-muted); font-family: inherit; }
.paywall-actions {
  display: flex;
  gap: 10px;
  align-items: center;
  flex-wrap: wrap;
}
.paywall-save-btn {
  background: var(--status-partial);
  color: var(--accent-ink);
  border: none;
  border-radius: var(--radius-sm);
  padding: 9px 18px;
  font-size: var(--text-sm);
  font-weight: 600;
  font-family: var(--font-jp-sans);
  cursor: pointer;
  transition: background var(--duration-base);
  white-space: nowrap;
}
.paywall-save-btn:hover { background: var(--color-context-btn-hover); }
.paywall-save-btn:disabled { opacity: 0.45; cursor: default; }
.paywall-domain-tag {
  font-size: var(--text-sm);
  color: var(--text-secondary);
  background: var(--bg-subtle);
  border-radius: var(--radius-pill);
  padding: 4px 12px;
  border: 1px solid var(--border-default);
}
.paywall-hint {
  margin-top: 12px;
  font-size: var(--text-xs);
  color: var(--status-partial);
  line-height: 1.6;
}
.paywall-step-block {
  display: flex;
  gap: 12px;
  align-items: flex-start;
  margin-bottom: 16px;
}
.paywall-step-num {
  flex-shrink: 0;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: var(--status-partial);
  color: var(--accent-ink);
  font-size: var(--text-sm);
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-top: 2px;
}
.paywall-step-body {
  flex: 1;
  font-size: var(--text-sm);
  line-height: 1.75;
  color: var(--text-secondary);
}
.paywall-step-body strong { color: var(--text-primary); }
.paywall-cmd-row {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 8px 0 4px;
  background: var(--bg-subtle);
  border-radius: var(--radius-sm);
  padding: 8px 12px;
}
.paywall-cmd-row code {
  flex: 1;
  font-family: var(--font-en-mono);
  font-size: var(--text-sm);
  color: var(--color-misc-19);
  background: none;
  border: none;
  padding: 0;
  user-select: all;
}
.paywall-copy-btn {
  flex-shrink: 0;
  background: var(--status-partial);
  color: var(--accent-ink);
  border: none;
  border-radius: var(--radius-misc-1);
  padding: 4px 10px;
  font-size: var(--text-xs);
  font-family: var(--font-jp-sans);
  cursor: pointer;
  white-space: nowrap;
  transition: background var(--duration-fast);
}
.paywall-copy-btn:hover { background: var(--color-context-btn-hover); }
.paywall-visual-hint { margin: 10px 0 4px; }
.paywall-visual-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 5px;
}
.paywall-tag {
  border-radius: var(--radius-sm);
  padding: 2px 8px;
  font-size: var(--text-xs);
  font-weight: 600;
  white-space: nowrap;
}
.tag-blue   { background: var(--tag-blue-bg);   color: var(--tag-blue-text); }
.tag-green  { background: var(--tag-green-bg);  color: var(--tag-green-text); }
.tag-orange { background: var(--tag-orange-bg); color: var(--tag-orange-text); }
.tag-red    { background: var(--accent-soft); color: var(--accent); }
.paywall-arrow { color: var(--status-partial); font-size: var(--text-sm); }
.paywall-icon-inline {
  width: 14px;
  height: 14px;
  vertical-align: -2px;
  display: inline-block;
  color: var(--text-secondary);
}
.paywall-ext-link {
  color: var(--accent);
  text-decoration: underline;
  font-size: var(--text-sm);
  display: inline-block;
  margin-top: 4px;
}
.paywall-ext-link:hover { color: var(--accent-hover); }
.paywall-manual-section {
  margin-top: 18px;
  border-top: 1px solid var(--border-subtle);
  padding-top: 14px;
}
.paywall-manual-toggle {
  font-size: var(--text-sm);
  color: var(--text-muted);
  cursor: pointer;
  user-select: none;
  list-style: none;
}
.paywall-manual-toggle::-webkit-details-marker { display: none; }
.paywall-manual-toggle::before { content: "▶ "; font-size: var(--text-xs); }
details[open] .paywall-manual-toggle::before { content: "▼ "; }
.paywall-manual-body { margin-top: 12px; }
.paywall-manual-note {
  font-size: var(--text-xs);
  color: var(--text-muted);
  line-height: 1.7;
  margin-bottom: 10px;
}
.paywall-save-manual-btn {
  background: var(--bg-subtle);
  color: var(--text-secondary);
  border: 1.5px solid var(--border-default);
  border-radius: var(--radius-sm);
  padding: 8px 16px;
  font-size: var(--text-sm);
  font-weight: 600;
  font-family: var(--font-jp-sans);
  cursor: pointer;
  transition: background var(--duration-base);
  white-space: nowrap;
}
.paywall-save-manual-btn:hover { background: var(--bg-elevated); }
.paywall-save-manual-btn:disabled { opacity: 0.45; cursor: default; }

/* Text selection tooltip */
.selection-tooltip {
  position: fixed;
  background: var(--glass-bg);
  border: 1px solid var(--glass-border);
  border-radius: 10px; /* hardcode: D-7 known gap (--radius-md app=10px vs design-system=12px) */
  padding: 4px;
  display: none;
  align-items: stretch;
  gap: 0;
  z-index: 100;
  box-shadow: var(--shadow-glass);
  backdrop-filter: blur(20px) saturate(180%);
  -webkit-backdrop-filter: blur(20px) saturate(180%);
}
.tooltip-btn {
  background: transparent;
  border: 0;
  padding: 7px 12px;
  /* intentional: tooltip-internal radius, between --radius-sm (6) and
     --radius-md (12); chosen to stay tighter than the tooltip container's
     own 10px. See Phase 1 D-6. */
  border-radius: 8px;
  font-family: var(--font-zh-sans); /* F-1: zh-sans replaces jp-sans */
  font-size: var(--text-sm);
  line-height: 1;
  color: var(--text-primary);
  font-weight: 400;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  white-space: nowrap;
  cursor: pointer;
  transition: background var(--duration-fast), color var(--duration-fast);
}
.tooltip-btn:hover { background: var(--bg-hover-overlay); }
.tooltip-btn--primary {
  color: var(--accent);
  font-weight: 500;
}
.tooltip-btn.active {
  background: var(--accent-soft);
  color: var(--accent);
  font-weight: 500;
}
.tooltip-btn svg { width: 14px; height: 14px; flex-shrink: 0; }
.tooltip-divider {
  width: 1px;
  background: var(--glass-border);
  align-self: stretch;
  margin: 4px 0;
  flex-shrink: 0;
}
/* 記憶訣竅按鈕：容器 class 控制（jp-only 沿用既有 JS 方式，word-only 用 CSS）*/
.selection-tooltip:not(.tooltip-word-only) .tooltip-btn[data-action="mnemonic"],
.selection-tooltip:not(.tooltip-word-only) .tooltip-word-only-divider {
  display: none;
}
/* 群組分隔線：AI 對話組 ↔ 快速查詢組，左右各 4px 留白 */
.tooltip-divider.group-sep {
  margin: 4px 4px;
}
.tooltip-caret {
  position: absolute;
  left: 50%;
  bottom: -5px;
  transform: translateX(-50%) rotate(45deg);
  width: 8px;
  height: 8px;
  background: var(--glass-bg);
  border-right: 1px solid var(--glass-border);
  border-bottom: 1px solid var(--glass-border);
  backdrop-filter: blur(20px) saturate(180%);
  -webkit-backdrop-filter: blur(20px) saturate(180%);
}

/* Chinese-to-Japanese translate card */
.translate-card {
  position: fixed;
  background: var(--glass-bg);
  border: 1px solid var(--glass-border);
  border-radius: 10px; /* hardcode: D-7 */
  padding: 12px 16px;
  z-index: 6;
  box-shadow: var(--shadow-glass);
  backdrop-filter: blur(20px) saturate(180%);
  -webkit-backdrop-filter: blur(20px) saturate(180%);
  display: none;
  flex-direction: column;
  gap: 4px;
  min-width: 200px;
  max-width: 320px;
}
.translate-card.visible { display: flex; }
.translate-card-row-1 {
  display: flex;
  align-items: flex-start;
  gap: 12px;
}
.translate-card-eyebrow {
  flex: 1;
  margin: 0;
  font-family: var(--font-zh-sans);
  font-size: var(--text-xs);
  color: var(--text-muted);
  letter-spacing: 0.04em;
  line-height: 1.4;
}
.translate-card-speak {
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: var(--accent-soft);
  border: 1px solid var(--accent-border);
  color: var(--accent);
  cursor: pointer;
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-top: 1px;
  transition: background var(--duration-fast);
}
.translate-card-speak:hover { background: var(--accent-soft-hover); }
.translate-card-speak svg { width: 13px; height: 13px; }
.translate-card-eyebrow-plain {
  margin: 0;
  font-family: var(--font-zh-sans);
  font-size: var(--text-xs);
  color: var(--text-muted);
  letter-spacing: 0.04em;
  line-height: 1.4;
}
.translate-card-original {
  font-family: var(--font-zh-serif); /* F-2 */
  font-size: var(--text-sm);
  color: var(--text-muted);
  letter-spacing: 0.02em;
  line-height: 1.4;
  margin: 0 0 4px;
}
.translate-card-result {
  font-family: var(--font-jp-serif); /* F-2 */
  font-size: var(--text-body-lg);
  color: var(--text-primary);
  letter-spacing: 0.02em;
  line-height: 1.5;
  margin: 0;
}
.translate-card-reading {
  margin: 0;
  font-family: var(--font-jp-sans);
  font-size: var(--text-xs);
  color: var(--text-secondary);
  letter-spacing: 0.04em;
  line-height: 1.4;
}
.translate-caret {
  position: absolute;
  left: 50%;
  bottom: -5px;
  transform: translateX(-50%) rotate(45deg);
  width: 8px;
  height: 8px;
  background: var(--glass-bg);
  border-right: 1px solid var(--glass-border);
  border-bottom: 1px solid var(--glass-border);
  backdrop-filter: blur(20px) saturate(180%);
  -webkit-backdrop-filter: blur(20px) saturate(180%);
}

/* Romaji result card */
.romaji-card {
  position: fixed;
  background: var(--glass-bg);
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-lg);
  padding: 14px 16px;
  z-index: 110;
  box-shadow: var(--shadow-glass);
  backdrop-filter: blur(20px) saturate(180%);
  -webkit-backdrop-filter: blur(20px) saturate(180%);
  display: none;
  flex-direction: column;
  gap: 6px;
  max-width: 360px;
}
.romaji-card.visible { display: flex; }
.romaji-card-row-1 {
  display: flex;
  align-items: flex-start;
  gap: 12px;
}
.romaji-card-original {
  flex: 1;
  margin: 0;
  font-family: var(--font-jp-serif); /* F-4 */
  font-size: 18px; /* hardcode: intentional hero hierarchy vs translate-card 17px, see D-6 */
  font-weight: 500;
  color: var(--text-primary);
  line-height: 1.4;
}
.romaji-card-hiragana {
  margin: 0;
  font-family: var(--font-jp-sans);
  font-size: var(--text-sm);
  color: var(--text-secondary);
  letter-spacing: 0.04em;
  line-height: 1.4;
}
.romaji-card-reading {
  margin: 0;
  font-family: var(--font-en-mono); /* F-3 */
  font-size: var(--text-xs);
  color: var(--text-muted);
  letter-spacing: 0.02em;
  line-height: 1.4;
}
.romaji-card-speak {
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: var(--accent-soft);
  border: 1px solid var(--accent-border);
  color: var(--accent);
  cursor: pointer;
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-top: 1px;
  transition: background var(--duration-fast);
}
.romaji-card-speak:hover { background: var(--accent-soft-hover); }
.romaji-card-speak svg { width: 13px; height: 13px; }
.romaji-caret {
  position: absolute;
  left: 50%;
  bottom: -5px;
  transform: translateX(-50%) rotate(45deg);
  width: 8px;
  height: 8px;
  background: var(--glass-bg);
  border-right: 1px solid var(--glass-border);
  border-bottom: 1px solid var(--glass-border);
  backdrop-filter: blur(20px) saturate(180%);
  -webkit-backdrop-filter: blur(20px) saturate(180%);
}

/* Analysis CTA (D-5 B: injected inside #analysis-panel by _injectAnalysisCta()) */
.analysis-cta {
  margin-top: 40px;
}
.analysis-cta__hr {
  border: none;
  border-top: 1px solid var(--border-default);
  margin: 0 0 20px;
}
.analysis-cta__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
}
.analysis-cta__desc {
  flex: 1;
  margin: 0;
  font-family: var(--font-zh-serif);
  font-size: 14px; /* intentional: matches prototype, between --text-sm/base */
  color: var(--text-muted);
  line-height: 1.5;
  min-width: 180px;
  /* max-width: 380px omitted — prototype value assumes wider panel; app panel is narrower and
     clips the text to two lines. Re-evaluate when article-panel width is adjusted. */
}
.analysis-cta__action {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-shrink: 0;
}
.analysis-cta__status {
  font-size: var(--text-sm);
  color: var(--text-secondary);
}
.analysis-cta-btn {
  background: var(--accent);
  color: var(--accent-ink);
  border: 1px solid transparent;
  border-radius: 12px; /* intentional: matches AnalysisFooterCta prototype, between --radius-sm/md */
  padding: 0 22px;
  height: 44px;
  font-size: 14px; /* intentional: prototype hardcode, between --text-sm/base */
  font-weight: 400;
  font-family: var(--font-zh-sans);
  cursor: pointer;
  transition: background var(--duration-base), color var(--duration-base), border-color var(--duration-base);
  display: flex;
  align-items: center;
  gap: 10px;
  flex-shrink: 0;
}
.analysis-cta-btn:hover { background: var(--accent-hover); }
.analysis-cta-btn:disabled { opacity: 0.45; cursor: default; }
.analysis-cta-btn.is-done,
.analysis-cta-btn.is-imported {
  background: var(--bg-subtle);
  color: var(--text-primary);
  border-color: var(--border-default);
}
.analysis-cta-btn.is-done:hover,
.analysis-cta-btn.is-imported:hover { background: var(--bg-surface); }
.analysis-cta-btn svg { width: 14px; height: 14px; }

/* Article sources chip bar ─────────────────────────────────────────────────── */

/* Base: hidden when no mode class (non-article tabs) */
.sources-bar {
  position: relative;
  background: var(--bg-subtle);
  border-bottom: none;
  max-height: 0;
  opacity: 0;
  overflow: hidden;
  flex-shrink: 0;
  transition: max-height var(--duration-entry) var(--ease-out), opacity var(--duration-fast) ease, border-color var(--duration-entry);
}

/* ── Collapsed mode ── */
.sources-bar.mode-collapsed {
  max-height: 0;
  opacity: 0;
  border-bottom: none;
  overflow: hidden;
}
.sources-bar.mode-collapsed.has-sources {
  max-height: 200px;
  opacity: 1;
  border-bottom: 1px solid var(--border-subtle);
}
.sources-bar.mode-collapsed.peek-open { overflow: visible; }

/* ── Full mode ── always visible regardless of has-sources */
.sources-bar.mode-full.has-sources {
  max-height: 200px;
  opacity: 1;
  border-bottom: 1px solid var(--border-subtle);
}

/* ── Mode visibility toggles ── */
.sources-bar.mode-full .sources-strip-inner { display: none; }
.sources-bar.mode-full .sources-peek { display: none; }
.sources-bar.mode-full .sources-full-inner { display: block; }
.sources-bar.mode-collapsed .sources-full-inner { display: none; }
.sources-bar.mode-collapsed .sources-strip-inner { display: block; }

/* ── Collapsed: strip inner ── */
.sources-strip-inner {
  max-width: 1280px;
  margin: 0 auto;
  padding: 0 var(--space-6);
}
.sources-strip {
  width: 100%;
  height: 36px;
  background: transparent;
  border: 0;
  padding: 0;
  cursor: default;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  font-family: inherit;
  text-align: left;
  color: inherit;
}
.sources-bar.mode-collapsed.has-sources .sources-strip { cursor: pointer; }

/* ── Full mode: inner layout ── */
.sources-full-inner {
  max-width: 1280px;
  margin: 0 auto;
  padding: 12px var(--space-6);
}
.sources-full-row {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 30px;
}

/* ── Empty hint (full mode, chips=0) ── */
.sources-empty-hint {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 1;
  min-width: 0;
}
.sources-empty-hairline {
  width: 14px;
  height: 1px;
  background: var(--border-default);
  flex: 0 0 14px;
}
.sources-empty-text {
  font-family: var(--font-zh-sans);
  font-size: var(--text-xs);
  color: var(--text-muted);
  line-height: 1.5;
}

/* ── Full mode chips scroll row ── */
.sources-full-chips {
  flex: 1;
  display: flex;
  gap: 8px;
  overflow-x: hidden;
  flex-wrap: nowrap;
  scrollbar-width: none;
  min-width: 0;
}
.sources-full-chips::-webkit-scrollbar { display: none; }
/* Expanded: chips wrap to second row */
.sources-bar.full-expanded .sources-full-chips {
  flex-wrap: wrap;
  overflow-x: visible;
  row-gap: 8px;
}

/* ── +N overflow indicator ── */
.sources-overflow-indicator {
  font-family: var(--font-en-display);
  font-size: var(--text-xs);
  letter-spacing: 0.08em;
  color: var(--text-muted);
  align-self: center;
  flex: 0 0 auto;
  white-space: nowrap;
}

/* ── Full mode expand toggle ── */
.sources-full-expand {
  width: 28px;
  height: 28px;
  border-radius: var(--radius-misc-3);
  background: transparent;
  border: 1px solid var(--border-subtle);
  color: var(--text-secondary);
  cursor: pointer;
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  transition: border-color var(--duration-fast) var(--ease-out), color var(--duration-fast) var(--ease-out);
}
.sources-full-expand:hover {
  border-color: var(--border-default);
  color: var(--text-primary);
}
.sources-full-expand svg { transition: transform var(--duration-base) var(--ease-out); }
.sources-bar.full-expanded .sources-full-expand svg { transform: rotate(180deg); }

/* ── Shared: label + count (both modes) ── */
.sources-label {
  font-family: var(--font-en-display);
  font-size: var(--text-xs);
  letter-spacing: 0.12em;
  font-weight: 500;
  color: var(--text-muted);
  white-space: nowrap;
  flex: 0 0 auto;
}
.sources-bar.has-sources .sources-label { color: var(--text-secondary); }
.sources-count {
  font-size: var(--text-xs);
  font-weight: 500;
  border: 1px solid var(--border-default);
  border-radius: var(--radius-full);
  white-space: nowrap;
  background: transparent;
  flex: 0 0 auto;
}
/* Collapsed count: padding 1px 7px */
.sources-strip-inner .sources-count { padding: 1px 7px; }
/* Full mode count: padding 2px 8px */
.sources-full-row .sources-count { padding: 2px 8px; }
.sources-count-num {
  font-family: var(--font-en-display);
  font-feature-settings: "lnum" 1, "tnum" 1;
  color: var(--text-muted);
}
.sources-count-unit {
  font-family: var(--font-zh-sans);
  margin-left: 2px;
  color: var(--text-muted);
}
.sources-bar.has-sources .sources-count { border-color: var(--accent); }
.sources-bar.has-sources .sources-count-num { color: var(--accent); }
.sources-bar.has-sources .sources-count-unit { color: var(--accent); }

/* ── Collapsed: summary + expand hint ── */
.sources-summary {
  font-family: var(--font-zh-sans);
  font-size: var(--text-xs);
  color: var(--text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  flex: 1;
  min-width: 0;
}
.sources-expand-hint {
  margin-left: auto;
  font-family: var(--font-en-display);
  font-size: var(--text-xs);
  letter-spacing: 0.08em;
  color: var(--text-muted);
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex: 0 0 auto;
  white-space: nowrap;
}
.sources-expand-chevron { transition: transform var(--duration-base) var(--ease-out); }
.sources-strip[aria-expanded="true"] .sources-expand-chevron { transform: rotate(180deg); }

/* ── Peek popover (collapsed mode) ── */
.sources-peek {
  display: none;
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  background: var(--bg-subtle);
  border-bottom: 1px solid var(--border-subtle);
  box-shadow: var(--shadow-card);
  z-index: 20;
}
.sources-peek.open { display: block; }
.sources-peek-inner {
  max-width: 1280px;
  margin: 0 auto;
  padding: var(--space-3) var(--space-6);
}
.sources-peek-chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

/* ── Chip ── */
.source-chip {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: 5px 6px 5px 12px;
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-full);
  max-width: 260px;
  transition: border-color var(--duration-fast);
  cursor: pointer;
}
.source-chip--entering { animation: chipEnter var(--duration-entry) var(--ease-out) both; }
@keyframes chipEnter {
  from { clip-path: inset(0 100% 0 0 round 999px); }
  to   { clip-path: inset(0 0% 0 0 round 999px); }
}
.source-chip:hover { border-color: var(--border-default); }
.source-chip--active,
.source-chip--active:hover { border-color: var(--accent); }
.source-chip--active .source-chip-star { color: var(--accent); }
.source-chip-star { color: var(--text-muted); flex: 0 0 auto; }
.source-chip-body {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  min-width: 0;
}
.source-chip-title {
  font-family: var(--font-zh-sans);
  font-size: var(--text-sm);
  color: var(--text-primary);
  max-width: 150px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  cursor: pointer;
}
.source-chip-date {
  font-family: var(--font-en-display);
  font-size: var(--text-xs);
  letter-spacing: 0.06em;
  color: var(--text-muted);
  flex: 0 0 auto;
  font-feature-settings: "lnum" 1, "tnum" 1;
}
.source-chip-del {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: transparent;
  border: 0;
  cursor: pointer;
  color: var(--text-muted);
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  padding: 0;
  transition: all var(--duration-fast) var(--ease-out);
}
.source-chip-del:hover {
  background: var(--bg-base);
  color: var(--text-primary);
}

/* Article chat panel */
.article-chat-panel {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: 16px; /* intentional: D-1A prototype value */
  box-shadow: var(--shadow-card);
  position: sticky;   /* 也是 .coach-drop-overlay(inset:0) 的 containing block；勿改成 relative（見 §拖放覆蓋層註解） */
  top: 80px;          /* sticky 吸附偏移；position 一旦被打成 relative，這裡會變成向下位移 80px → 面板偏下 */
  height: calc(100vh - 112px);
  min-height: 640px;
}

/* ── Selection context bar ── */
.selection-context-bar {
  display: none;
  margin-bottom: 8px;
  /* glass: intentional D-1A —吃 .chat-input-area 的 18px horizontal padding */
  border-radius: var(--radius-md); /* intentional: 12px, matches prototype */
  padding: 10px 12px 10px 14px;   /* intentional: D-1A, left extra 2px for eyebrow balance */
  align-items: center;
  gap: 10px;
}
.selection-context-bar.active { display: flex; }
.sel-context-eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: var(--font-en-display);
  font-size: 10.5px; /* intentional D-1A: prototype value, tighter than --text-xs 11px */
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--accent);
  font-weight: 500;
  flex: 0 0 auto;
}
.sel-context-eyebrow svg { flex-shrink: 0; }
.sel-context-text {
  flex: 1;
  font-family: var(--font-jp-serif);
  font-size: 13.5px; /* intentional D-1A: prototype value, jp-serif body */
  color: var(--text-primary);
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 3; /* intentional: max 3-line clamp per spec */
  -webkit-box-orient: vertical;
}
.sel-clear-btn {
  width: 22px; height: 22px;       /* intentional D-1A: prototype value */
  border-radius: 50%;
  background: transparent; border: none;
  color: var(--text-muted); cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  flex: 0 0 auto; padding: 0;
  transition: color var(--duration-fast);
}
.sel-clear-btn:hover { color: var(--text-primary); }
/* Remove composer border-top when context bar is visible — intentional D-3A */
.chat-input-area.has-context { border-top: 0; }

/* ── Image upload button (D-7A: transparent + no border, flush inside composer-wrapper) ── */
.img-upload-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 40px; /* intentional: enlarged from prototype 34px for usability */
  height: 40px;
  flex-shrink: 0;
  background: transparent;
  border: 0;
  border-radius: 8px;
  cursor: pointer;
  color: var(--text-muted);
  transition: color var(--duration-fast);
}
.img-upload-btn:hover { color: var(--text-secondary); }

/* ── Coach 截圖附件（O-2/O-3，數值照設計交付 §三）── */

/* 待送出附件列（composer 上方、ModeSwitcher 下方）。多圖 flex-wrap。 */
.pending-strip { display: flex; gap: 8px; flex-wrap: wrap; margin: 0 0 8px; }
.pending-thumb { position: relative; flex: 0 0 auto; }
.pending-thumb img {
  height: 56px; width: auto; max-width: 120px;
  object-fit: cover; display: block;
  border-radius: 10px; border: 1px solid var(--border-default);
}
.pending-thumb__x {
  position: absolute; top: -6px; right: -6px;
  width: 18px; height: 18px; border-radius: 9999px;
  background: var(--bg-base); border: 1px solid var(--border-default);
  color: var(--text-secondary); cursor: pointer; padding: 0;
  display: flex; align-items: center; justify-content: center;
}
.pending-thumb__x:hover { color: var(--text-primary); }

/* 訊息內縮圖（user bubble）。有圖時 bubble padding 由 .has-image 調整。 */
.msg.user .bubble.has-image { padding: 10px 10px 12px; }
.msg-image-row { display: flex; gap: 8px; flex-wrap: wrap; }
.msg-image-row + .msg-text { padding: 0 6px; margin-top: 10px; }
.msg-image {
  max-width: 220px; max-height: 150px; width: auto; height: auto;
  display: block; border-radius: 10px;
  border: 1px solid var(--border-default); cursor: zoom-in;
}

/* 拖放覆蓋層（印章式）。玻璃屬 modal scrim 類，允許清單內。 */
.coach-drop-overlay {
  position: absolute; inset: 0; z-index: 30;
  display: none; flex-direction: column; align-items: center; justify-content: center;
  gap: 16px; pointer-events: none;
  background: var(--glass-bg);
  backdrop-filter: blur(20px) saturate(180%);
  -webkit-backdrop-filter: blur(20px) saturate(180%);
  border-radius: 16px;
}
.chat-panel.is-dragover .coach-drop-overlay,
.article-chat-panel.is-dragover .coach-drop-overlay { display: flex; animation: ctxExpand 160ms ease-out both; }
.coach-drop-stamp {
  width: 72px; height: 72px; border-radius: 14px;
  border: 2.5px solid var(--accent); color: var(--accent);
  display: flex; align-items: center; justify-content: center;
  transform: rotate(-4deg);
  box-shadow: 0 0 0 4px color-mix(in oklch, var(--accent) 10%, transparent);
  animation: stampDrop 220ms cubic-bezier(0.2, 0, 0, 1) both;
}
.coach-drop-main { font-family: var(--font-zh-sans); font-size: 14px; font-weight: 500; letter-spacing: 0.03em; color: var(--text-primary); }
.coach-drop-sub  { font-family: var(--font-zh-sans); font-size: 12px; color: var(--text-muted); margin-top: -8px; }
@keyframes stampDrop {
  from { transform: scale(1.25) rotate(-4deg); opacity: 0; }
  to   { transform: scale(1) rotate(0); opacity: 1; }
}
/* 拖放覆蓋層（.coach-drop-overlay，position:absolute; inset:0）的 containing block
   由面板的 `position: sticky` 提供（sticky 是定位值，一樣建立 containing block）——
   **不要**在此另加 `position: relative`：它會蓋掉 sticky，殘留的 top:80px 會變成向下位移
   80px，整個面板偏下。規格 §三.3「全面板」的覆蓋範圍靠 sticky 就成立。 */

/* Lightbox（點縮圖放大）。scrim = 允許的玻璃面。 */
.img-lightbox {
  position: fixed; inset: 0; z-index: 200;
  display: none; align-items: center; justify-content: center;
  background: rgba(12, 10, 8, 0.62);
  backdrop-filter: blur(20px) saturate(180%);
  -webkit-backdrop-filter: blur(20px) saturate(180%);
  cursor: zoom-out;
}
.img-lightbox.open { display: flex; animation: ctxExpand 200ms var(--ease-entry, ease-out) both; }
.img-lightbox img {
  max-width: 86%; max-height: 86%;
  border-radius: 12px; border: 1px solid var(--glass-border); box-shadow: var(--shadow-glass);
}
.img-lightbox__close {
  position: absolute; top: 22px; right: 26px;
  width: 40px; height: 40px; border-radius: 50%;
  border: 1px solid var(--border-default); background: var(--bg-elevated);
  color: var(--text-secondary); cursor: pointer; font-size: 22px; line-height: 1;
  display: flex; align-items: center; justify-content: center;
}
.img-lightbox__close:hover { color: var(--text-primary); }

/* 超量 / 非圖檔提示（CTA 回饋，不靜默截斷） */
.toast {
  position: fixed; left: 50%; bottom: 32px; transform: translateX(-50%) translateY(8px);
  z-index: 210; padding: 9px 16px; border-radius: var(--radius-md);
  background: var(--bg-elevated); border: 1px solid var(--border-default);
  color: var(--text-primary); font-family: var(--font-zh-sans); font-size: 13px;
  box-shadow: var(--shadow-card); opacity: 0; pointer-events: none;
  transition: opacity var(--duration-base), transform var(--duration-base);
}
.toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }

/* ── Article Body Two-Tab Layout ── */
.article-body--tabbed {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  padding: 0;
}
.article-body-tabs {
  display: flex;
  border-bottom: 2px solid var(--border-default);
  background: var(--bg-surface);
  flex-shrink: 0;
}
.article-body-tab {
  padding: 10px 20px;
  font-size: var(--text-sm);
  font-weight: 600;
  font-family: var(--font-jp-sans);
  background: none;
  border: none;
  cursor: pointer;
  color: var(--text-secondary);
  border-bottom: 2px solid transparent;
  margin-bottom: -2px;
  transition: color var(--duration-fast), border-color var(--duration-fast);
  letter-spacing: 0.02em;
}
.article-body-tab:hover { color: var(--text-primary); }
.article-body-tab.active {
  color: var(--accent);
  border-bottom-color: var(--accent);
}
.article-body-panel {
  display: none;
  flex: 1;
  overflow-y: auto;
  padding: 22px 26px;
}
.article-body-panel.active { display: flex; flex-direction: column; }
#analysis-panel { padding: 36px 44px; }

/* ── Comprehension Card — isolated namespace (.comp-card) ── */
.comp-card {
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  padding: 22px 24px 20px; /* intentional: matches exercise-card structural value */
  box-shadow: var(--shadow-card);
  position: relative;
  transition: background 240ms cubic-bezier(0.2,0,0,1), border-color 120ms var(--ease-out);
}
.comp-card:hover { border-color: var(--border-default); }
.comp-card.correct { background: var(--status-correct-bg); }
.comp-card.partial { background: var(--status-partial-bg); }
.comp-card.wrong   { background: var(--status-wrong-bg); }

/* Left status bar — absolute 4px, mirrors .exercise-card-bar */
.comp-card-bar {
  position: absolute;
  left: 0; top: 0; bottom: 0;
  width: 4px;
  background: transparent;
  border-top-left-radius: var(--radius-lg);
  border-bottom-left-radius: var(--radius-lg);
  pointer-events: none;
  transition: background 240ms cubic-bezier(0.2,0,0,1);
}
.comp-card.correct .comp-card-bar { background: var(--status-correct); }
.comp-card.partial .comp-card-bar { background: var(--status-partial); }
.comp-card.wrong   .comp-card-bar { background: var(--status-wrong); }

/* Status pill colour — scoped to comp-card states */
.comp-card.correct .card-status-pill { color: var(--status-correct); }
.comp-card.partial .card-status-pill { color: var(--status-partial); }
.comp-card.wrong   .card-status-pill { color: var(--status-wrong); }

/* Prompt text */
.comp-prompt {
  font-family: var(--font-zh-serif);
  /* intentional: 17px zh-serif prompt, matches prototype ComprehensionList D-prompt spec */
  font-size: 17px;
  line-height: 1.65;
  color: var(--text-primary);
  margin: 0 0 14px;
  letter-spacing: 0.005em;
  text-wrap: pretty;
}

/* Textarea + 批改 row */
.comp-input-row {
  display: flex;
  gap: 10px;
  align-items: flex-end;
}

/* Textarea — D-5: override global .answer-input via .comp-answer-input modifier */
.comp-answer-input {
  resize: vertical;
  min-height: 64px;
  max-height: 160px;
  font-family: var(--font-zh-serif);
  padding: 10px 14px;
  border-radius: var(--radius-md);
}
.comp-answer-input:disabled { background: transparent; opacity: 0.85; cursor: default; }

/* Pre-grade answer reveal panel — neutral colours (not amber) */
.comp-reveal-panel {
  margin-top: 14px;
  padding: 14px 16px;
  background: var(--bg-subtle);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  animation: compRevealIn 220ms var(--ease-entry) both;
}
.comp-reveal-panel[hidden] { display: none; }

/* Graded body */
.comp-graded-body {
  margin-top: 18px;
  padding-top: 18px;
  border-top: 1px solid var(--border-subtle);
}
.comp-graded-body[hidden] { display: none; }

/* Shared label above reference block */
.comp-reveal-label {
  display: block;
  font-family: var(--font-zh-sans);
  /* intentional: 11.5px compact label, pre-grade panel; 12px graded body — use same class, close enough */
  font-size: var(--text-xs);
  letter-spacing: 0.06em;
  color: var(--text-muted);
  font-weight: 500;
  margin: 0 0 8px;
}

/* ReferenceBlock — jp-serif + zh-serif two-line structure (D-4) */
.comp-ref-jp {
  font-family: var(--font-jp-serif);
  /* intentional: 16px jp-serif reference quote, prototype D-4 ReferenceBlock spec */
  font-size: 16px;
  line-height: 1.65;
  color: var(--text-primary);
  margin: 0 0 6px;
  letter-spacing: 0.02em;
}
.comp-ref-zh {
  font-family: var(--font-zh-serif);
  /* intentional: 13.5px inter-language bridge value between --text-sm and --text-body, prototype D-4 */
  font-size: 13.5px;
  line-height: 1.6;
  color: var(--text-muted);
  margin: 0;
}

/* Feedback text — D-3: renamed from .feedback-box, no status bg */
.comp-feedback {
  font-family: var(--font-zh-serif);
  font-size: var(--text-body);
  line-height: 1.75;
  color: var(--text-secondary);
  margin: 14px 0 0;
  text-wrap: pretty;
}

/* compRevealIn animation */
@keyframes compRevealIn {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* ── Comprehension exercise list ── */
.comprehension-list {
  display: flex;
  flex-direction: column;
  gap: 14px;
  /* intentional: panel has padding 22px 26px; +4px aligns top breathing space with left/right */
  padding-top: 4px;
}

/* ── QA Note footer + button (ghost, low-weight) — per reply-actions-spec v1.0 ── */
.qa-note-footer {
  display: flex;
  gap: 6px;
  margin-top: 14px;
  padding-top: 10px;
  border-top: 1px dashed var(--border-subtle);
}
.qa-note-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 8px;
  background: transparent;
  border: 0;
  border-radius: 6px;
  color: var(--text-secondary);
  font-family: var(--font-zh-sans);
  font-size: 12px;
  line-height: 1;
  cursor: pointer;
  transition: all var(--duration-fast);
}
.qa-note-btn:hover { background: var(--bg-subtle); }
.qa-note-btn.saved { color: var(--text-muted); cursor: default; }

/* ── 記憶訣竅 角度切換 chips ── */
.mnemonic-chips {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-1);
  margin-top: var(--space-2);
  padding-top: var(--space-2);
  border-top: 1px solid var(--border-subtle);
}
.mnemonic-chips__label {
  font-family: var(--font-zh-sans);
  font-size: 11.5px;
  color: var(--text-muted);
  flex-shrink: 0;
}
.chip {
  background: transparent;
  border: 1px solid var(--border-default);
  border-radius: 999px;
  padding: 3px 11px;
  font-family: var(--font-zh-sans);
  font-size: 12px;
  color: var(--text-secondary);
  cursor: pointer;
  transition: border-color var(--duration-fast), color var(--duration-fast), background var(--duration-fast);
  line-height: 1.5;
}
.chip:hover {
  border-color: var(--accent);
  color: var(--accent);
  background: var(--accent-soft);
}

/* ══════════════════════════════════════════════════════════════
   Phase 2 Stage 2 — EmptyStates
   AnalysisEmpty / AnalysisStreaming / ChatEmpty + SiteCard
   ══════════════════════════════════════════════════════════════ */

/* ── Keyframes ── */
@keyframes pulseDot {
  0%, 100% { transform: scale(1);   opacity: 1;   }
  50%      { transform: scale(1.5); opacity: 0.4; }
}
@keyframes caretBlink {
  0%, 49%   { opacity: 1; }
  50%, 100% { opacity: 0; }
}
@keyframes streamFadeIn {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: translateY(0); }
}
@keyframes siteCardIn {
  from { opacity: 0; transform: translateY(4px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* ── AnalysisEmpty ── */
.analysis-empty {
  min-height: 720px;
  display: flex;
  flex-direction: column;
}
.analysis-empty__content {
  flex: 1;
  /* intentional: top/bottom padding 88px/96px — no token in scale for 88px;
     96px = var(--space-9). Bottom uses token; top is intentional structural value.
     See EmptyStates audit Section 5-A. */
  padding: 88px var(--space-8) 96px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
}
.analysis-empty__eyebrow {
  display: flex;
  align-items: center;
  /* intentional: gap 14px and marginBottom 28px — no tokens; structural proportional values.
     See EmptyStates audit Section 5-A. */
  gap: 14px;
  margin-bottom: 28px;
}
.analysis-empty__eyebrow-label {
  font-family: var(--font-zh-sans);
  /* intentional: 12.5px — between --text-xs (11) and --text-sm (13); deliberate lightweight
     editorial label size. See EmptyStates D-4 and audit Section 5-A. */
  font-size: 12.5px;
  /* intentional: letterSpacing 0.32em — editorial wide-tracking for eyebrow. No token. */
  letter-spacing: 0.32em;
  color: var(--accent);
  font-weight: 500;
}
.analysis-empty__eyebrow-line {
  flex: 1;
  height: 1px;
  /* intentional: width 80px and height 1px — decorative hairline, not a structural token. */
  width: 80px;
  background: var(--border-default);
}
.analysis-empty__h1 {
  font-family: var(--font-jp-serif);
  /* intentional: jp-serif applied to ZH content for editorial tone;
     matches prototype landing card design. See EmptyStates D-4-F1. */
  font-size: var(--text-h1);
  /* intentional: lineHeight 1.45 — wider than --lh-h1 (1.25) for jp-serif breathing room.
     Matches prototype spec. See EmptyStates audit Section 5-A. */
  line-height: 1.45;
  font-weight: 500;
  color: var(--text-primary);
  /* intentional: margin-bottom 18px — structural value between --space-4 (16) and --space-5 (24). */
  margin: 0 0 18px;
  /* intentional: letterSpacing 0.005em — subtle jp-serif tracking, no token. */
  letter-spacing: 0.005em;
  max-width: 540px;
  text-wrap: pretty;
}
.analysis-empty__p {
  font-family: var(--font-zh-serif);
  /* intentional: fontSize 16px — prototype spec; nearest token is --text-body-lg (17px).
     Intentional 1px undercut for slightly lighter weight. See EmptyStates audit Section 5-A. */
  font-size: 16px;
  /* intentional: lineHeight 1.75 — close to --lh-body-lg (1.70) + 0.05. Prototype spec.
     See EmptyStates audit Section 5-A. */
  line-height: 1.75;
  color: var(--text-secondary);
  margin: 0;
  max-width: 460px;
  text-wrap: pretty;
}
.analysis-empty__hint {
  display: flex;
  align-items: center;
  gap: 10px;
  /* intentional: marginTop 56px — between --space-7 (48) and --space-8 (64). Structural. */
  margin-top: 56px;
  font-family: var(--font-zh-sans);
  /* intentional: 12.5px — same editorial small size as eyebrow. See EmptyStates audit Section 5-A. */
  font-size: 12.5px;
  color: var(--text-muted);
}
.analysis-empty__arrow {
  /* intentional: ArrowUp inline SVG — one-shot decorative, not promoting to Primitives.jsx.
     See EmptyStates D-3 and audit Section 6. */
  color: var(--accent);
  flex-shrink: 0;
}

/* ── ComprehensionEmpty (initial state of comprehension-panel during article streaming) ── */
.comp-empty {
  display: flex;
  flex-direction: column;
  flex: 1;
}
.comp-empty__content {
  flex: 1;
  padding: 88px var(--space-8) 96px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
}
.comp-empty__eyebrow {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-bottom: 28px;
}
.comp-empty__eyebrow-label {
  font-family: var(--font-zh-sans);
  /* intentional: 12.5px — same editorial eyebrow size as AnalysisEmpty. */
  font-size: 12.5px;
  letter-spacing: 0.32em;
  color: var(--accent);
  font-weight: 500;
}
.comp-empty__eyebrow-line {
  flex: 1;
  height: 1px;
  width: 80px;
  background: var(--border-default);
}
.comp-empty__h1 {
  font-family: var(--font-jp-serif);
  font-size: var(--text-h1);
  line-height: 1.45;
  font-weight: 500;
  color: var(--text-primary);
  margin: 0 0 18px;
  letter-spacing: 0.005em;
  max-width: 540px;
  text-wrap: pretty;
}
.comp-empty__p {
  font-family: var(--font-zh-serif);
  font-size: 16px;
  line-height: 1.75;
  color: var(--text-secondary);
  margin: 0;
  max-width: 460px;
  text-wrap: pretty;
}

/* ── ArticleNoRecord (S1: URL entered, no cached analysis) ── */
.article-no-record {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 14px;
  text-align: center;
  padding: 32px 24px;
}
.article-no-record__icon {
  color: var(--border-default);
  flex-shrink: 0;
}
.article-no-record__title {
  font-family: var(--font-zh-sans);
  font-size: var(--text-sm);
  font-weight: 500;
  color: var(--text-secondary);
  margin: 0;
  letter-spacing: 0.02em;
}
.article-no-record__hint {
  font-family: var(--font-zh-sans);
  /* intentional: 12.5px — matches editorial small size used across empty states. */
  font-size: 12.5px;
  color: var(--text-muted);
  margin: 0;
  line-height: 1.65;
}
.article-no-record__hint strong {
  color: var(--text-secondary);
  font-weight: 500;
}

/* ── ArticleErrState (system errors in article-body, comprehension-panel, exercises-list) ── */
.article-err-state {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  text-align: center;
  padding: 32px 24px;
}
.article-err-state__icon {
  color: var(--status-wrong);
  opacity: 0.7;
  flex-shrink: 0;
}
.article-err-state__title {
  font-family: var(--font-zh-sans);
  font-size: var(--text-sm);
  font-weight: 500;
  color: var(--text-secondary);
  margin: 0;
}
.article-err-state__hint {
  font-family: var(--font-zh-sans);
  /* intentional: 12.5px — matches editorial small size used across empty states. */
  font-size: 12.5px;
  color: var(--text-muted);
  margin: 0;
  line-height: 1.65;
}

/* ── AnalysisStreaming (overlay inside #analysis-panel) ── */
/* intentional: .analysis-streaming as an overlay inside the analysis panel;
   the outer card chrome (bg-surface, border, radius-lg) lives on the article-body--tabbed
   container and will be addressed in Stage 2 Analysis 基底 round. See EmptyStates audit 3-B. */
.streaming-header {
  display: flex;
  align-items: center;
  gap: 12px;
  /* intentional: padding 18px 36px — structural proportional value; no exact token match. */
  padding: 18px 36px;
  border-bottom: 1px solid var(--border-subtle);
}
.streaming-header__label {
  font-family: var(--font-zh-sans);
  /* intentional: 12.5px — matches AnalysisEmpty eyebrow small editorial size. */
  font-size: 12.5px;
  /* intentional: letterSpacing 0.08em — subtle tracking for accent label. */
  letter-spacing: 0.08em;
  color: var(--accent);
  font-weight: 500;
}
.streaming-header__status {
  font-family: var(--font-zh-sans);
  font-size: 12.5px;
  color: var(--text-muted);
}
.analysis-streaming__chunks {
  /* intentional: padding 32px 44px 40px — structural proportional value; top = var(--space-6). */
  padding: 32px 44px 40px;
}
/* When article-analysis-header is injected, streaming-header is removed;
   header's margin-bottom owns the gap — cancel the duplicate top padding. */
.article-analysis-header + .analysis-streaming__chunks {
  padding-top: 0;
}

/* ── Analysis Header (injected by _injectAnalysisHeader) ── */
.article-analysis-header {
  margin-bottom: 0;
}
.article-analysis-header h1 {
  font-family: var(--font-jp-serif);
  font-size: 26px; /* intentional: editorial jp-serif article title; distinct from --text-h1 (32px). See Analysis 基底 D-1. */
  font-weight: 600;
  line-height: 1.4;
  letter-spacing: 0.005em;
  color: var(--text-primary);
  margin: 0 0 12px;
}
.article-title-hash {
  color: var(--accent);
  margin-right: 10px;
}
/* meta row 全列同一字級 var(--text-sm)。先前這裡是 12.5px 的 intentional literal
   （「介於 --text-xs 11 與 --text-sm 13 之間」），domain 又另外用 12px、icon 有 13 與 12
   兩種 —— 一行裡四種尺寸。統一為 13 之後正好落在既有 token 上，四個 literal 全部退場，
   回到專案規則「禁止 px font-size，一律用 token」。 */
.analysis-meta-row {
  display: flex;
  align-items: center;
  gap: 14px; /* intentional: prototype gap 14, between --space-3 (12) and --space-4 (16) */
  flex-wrap: wrap;
  font-family: var(--font-zh-sans);
  font-size: var(--text-sm);
  color: var(--text-muted);
}
.meta-date { font-family: var(--font-en-display); }
.meta-dot {
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: var(--text-muted);
  display: inline-block;
  flex-shrink: 0;
}
/* ══ Meta action ══════════════════════════════════════════════════════════
   analysis meta row 裡每一個可點元素的**唯一**樣式來源（重新生成 / 生成朗讀音檔 /
   前往文章來源，以及未來新增的任何一個）。

   **規則（新增按鈕時必須遵守，有測試把關）：**
     1. meta row 內任何 <button> / <a> 都要帶 .meta-action —— 語意 class
        （.analysis-regen-btn 等）只作辨識用，不承載樣式
     2. icon 一律 13px，且**禁止宣告 color** —— 繼承 currentColor 才會與文字連動
     3. 狀態差異只能加 .is-* 修飾詞，不得為單一按鈕另開一條完整規則

   為什麼要收斂：先前三個按鈕各自一條規則、11 條宣告一字不差地重複三遍。複製時多寫
   一條沒人會發現 —— 「生成朗讀音檔」的 icon 橘色（`svg { color: var(--accent) }`）
   就是這樣長出來的：靜止時 icon 是橘的、文字是灰的，同一顆按鈕兩個顏色。 */
.meta-action {
  background: transparent;
  border: none;
  padding: 0;
  cursor: pointer;
  color: var(--text-secondary);
  font-family: var(--font-zh-sans);
  font-size: var(--text-sm);
  display: inline-flex;
  align-items: center;
  gap: 5px;
  text-decoration: none;   /* 「前往文章來源」是 <a>；<button> 上無作用 */
  transition: color var(--duration-base);
}
.meta-action:hover { color: var(--accent); }
/* 不宣告 color —— icon 必須恆等於文字色。宣告了就會脫鉤（見上方註解） */
.meta-action svg { flex-shrink: 0; }

/* login 態：重新生成是唯一出路，高亮成明確去向（handoff 05）。
   不需要 :hover 覆寫 —— base hover 已是 accent，與此處同色。 */
.meta-action.is-highlight {
  background: var(--accent-soft);
  border: 1px solid var(--accent);
  border-radius: var(--radius-sm);
  padding: 3px 9px; /* intentional: prototype highlight padding */
  color: var(--accent);
}

/* 「前往文章來源」是 <a>，樣式全走 .meta-action —— domain 文字已移除，但 JS 的
   domain 變數仍保留（needs_login 態的 strip 要顯示「請先登入 <domain>」）。 */

/* ── 朗讀入口（meta row，僅 ungenerated 態）── */
/* slot 只是 JS 注入用的錨點，不該存在於佈局中。display:contents 讓它自身不生成 box，
   內部的 meta-dot 與按鈕直接成為 .analysis-meta-row 的 flex 子項 —— 這才是 prototype
   的結構（設計用 React Fragment 把 Dot 與 MetaAction 攤平成兄弟，不含包裹節點）。
   不這樣做的話：(1) 點與按鈕退回 inline 排版，只剩一個空白字元的間距、且靠基線對齊而非
   置中；(2) 入口不顯示時，空的 slot 仍佔一個 flex 子項，前後各吃一份 gap。 */
.analysis-meta-tts { display: contents; }
/* 「生成朗讀音檔」樣式全走 .meta-action。先前這裡另有一條
   `svg { color: var(--accent) }`，讓 icon 在靜止時是橘的、文字是灰的 —— 同一顆按鈕
   兩個顏色。已移除：icon 一律繼承 currentColor。 */

/* ── 朗讀播放器 strip（O-4）──
   「安靜的錄音筆」：accent 只出現在播放鈕填色與已播進度，全畫面紅覆蓋 < 5%。 */
.tts-strip {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  background: var(--bg-subtle);
  border: 1px solid var(--border-subtle);
  border-radius: 14px; /* intentional: prototype strip radius，介於 --radius-md/lg */
  padding: 13px 16px; /* intentional: prototype strip padding */
  /* 上 22px：prototype 的 metaRow→strip 間距。設計裡是靠 MetaRow 的 margin-bottom:22
     與 strip 的 margin-top:4 摺疊達成（max=22）；本專案 .analysis-meta-row 的
     margin-bottom 是 0，鄰居不供給，故由 strip 自帶。不可照抄 prototype 的 4px。
     下 0：下方緊接 .prose-lg，其 h2 自帶 1.6em(=35.2px) margin-top（.prose/.prose-sm/
     .prose-lg 共用的排版節奏，全站文章適用）。再給 margin-bottom 會疊加成 61px；
     此處交由既有 prose 節奏負責，不重複宣告、也不覆寫共用排版。 */
  margin: 22px 0 0;
}

/* 生成中 */
.tts-strip--generating {
  flex-direction: column;
  align-items: stretch;
  gap: var(--space-3);
  padding: var(--space-4) 18px; /* intentional: prototype */
}
.tts-gen__head { display: flex; align-items: center; gap: 10px; }
.tts-gen__dot {
  width: 8px;
  height: 8px;
  border-radius: var(--radius-full);
  background: var(--accent);
  flex: 0 0 auto;
}
.tts-gen__text {
  font-family: var(--font-zh-sans);
  font-size: 13.5px; /* intentional: prototype strip 內文 */
  color: var(--text-secondary);
  white-space: nowrap;
}
.tts-num { font-family: var(--font-en-mono); font-variant-numeric: tabular-nums; }
.tts-gen__track {
  position: relative;
  height: 4px;
  border-radius: var(--radius-full);
  background: var(--border-subtle);
  overflow: hidden;
}
/* 進度條：寬度由後端的**真實進度**驅動（已合成字數 ÷ 全文字數），不是計時動畫。
   補間時長 = 輪詢間隔（--tts-poll-ms 由 JS 依 TTS_POLL_MS 設定，單一真相源在 JS）：
   新進度一進來就用「整整一個輪詢週期」等速爬過去，下一次輪詢剛好接上 → 幾乎連續的
   緩慢爬升。若用一般的狀態切換時長（如 --duration-slow 0.32s），進度條會靜止 2.7 秒
   再暴衝 20 幾個百分點 —— 那是「靜止＋爆發」，與品牌的「慢而安靜」相反。
   timing 必須是 linear：ease-out 會快起步慢收尾，每格開頭仍是一次小暴衝。 */
.tts-gen__fill {
  position: absolute;
  inset: 0 auto 0 0;
  width: 0%;
  background: var(--accent);
  border-radius: var(--radius-full);
  transition: width var(--tts-poll-ms, 3000ms) linear;
}

/* 生成中動畫：慢而安靜，無高頻 spinner（handoff §6） */
@keyframes breatheDot { 0%, 100% { opacity: 0.35; } 50% { opacity: 1; } }
.breathe-dot { animation: breatheDot 2600ms ease-in-out infinite; }
@keyframes genFill { 0% { width: 6%; } 100% { width: 88%; } }
@keyframes genBreathe { 0%, 100% { opacity: 0.5; } 50% { opacity: 0.95; } }
/* 降級：拿不到真實進度時（例如未來擴成多機/多 worker，polling 打到非生成端）
   才退回原本的安撫式不定量動畫。有真實進度時不掛這個 class。 */
.tts-gen__fill.is-indeterminate {
  animation: genFill 22s cubic-bezier(0.16, 1, 0.3, 1) forwards, genBreathe 2800ms ease-in-out infinite;
  transition: none;
}
@media (prefers-reduced-motion: reduce) {
  .breathe-dot { animation: none; }
  /* 真實進度是「資訊」不是「動效」—— reduced-motion 下照常顯示，只拿掉補間滑動。
     不定量動畫才是純裝飾，故仍全停並定格 45%（handoff §6）。 */
  .tts-gen__fill { transition: none; }
  .tts-gen__fill.is-indeterminate { animation: none; width: 45%; }
  /* 認證落地的「紙頁上移」是純過場裝飾（800ms 整塊內容位移），reduced-motion 下直接定格。 */
  body.page-enter .tab-panel.active > main { animation: none; }
}

/* 就緒播放器 */
.tts-play-btn {
  flex: 0 0 auto;
  width: 44px;
  height: 44px;
  border-radius: var(--radius-full);
  border: 0;
  cursor: pointer;
  background: var(--accent);
  color: var(--accent-ink);
  display: flex;
  align-items: center;
  justify-content: center;
  transition: filter var(--duration-base);
}
.tts-play-btn:active { filter: brightness(0.97); }
.tts-play-btn svg { width: 20px; height: 20px; fill: currentColor; stroke: none; }
.tts-icon-play { margin-left: 2px; } /* intentional: 三角形視覺置中補償 */
/* 播放/暫停是同一顆鈕的兩個狀態，由 .is-playing 單一真相源決定顯哪個 icon。
   刻意不用 [hidden] 屬性：SVGElement 沒有 hidden 的 IDL 反射（svg.hidden = x 碰不到
   content 屬性），且本專案無全域 [hidden] 規則 —— 兩者疊加會讓兩個 icon 同時出現。 */
.tts-play-btn .tts-icon-pause { display: none; }
.tts-play-btn.is-playing .tts-icon-play { display: none; }
.tts-play-btn.is-playing .tts-icon-pause { display: block; }
.tts-time {
  font-family: var(--font-en-mono);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.01em;
  font-size: var(--text-sm);
  flex: 0 0 auto;
  min-width: 34px; /* intentional: 容納 m:ss 不跳動 */
}
.tts-time--current { color: var(--text-primary); }
.tts-time--total { color: var(--text-muted); }
.tts-track {
  flex: 1;
  height: 20px;
  display: flex;
  align-items: center;
  cursor: pointer;
  touch-action: none;
}
.tts-track__rail {
  position: relative;
  width: 100%;
  height: 4px;
  border-radius: var(--radius-full);
  background: var(--border-default);
}
.tts-track__played {
  position: absolute;
  inset: 0 auto 0 0;
  width: 0%;
  background: var(--accent);
  border-radius: var(--radius-full);
}
.tts-track__thumb {
  position: absolute;
  top: 50%;
  left: 0%;
  transform: translate(-50%, -50%);
  width: 11px;
  height: 11px;
  border-radius: var(--radius-full);
  background: var(--accent);
  box-shadow: 0 0 0 3px var(--bg-subtle);
  transition: width var(--duration-base) var(--ease-out), height var(--duration-base) var(--ease-out);
}
.tts-track:hover .tts-track__thumb { width: 13px; height: 13px; }
.tts-divider {
  width: 1px;
  height: 22px;
  background: var(--border-subtle);
  flex: 0 0 auto;
}
.tts-speed {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  padding: 2px;
  background: var(--bg-base);
  border: 1px solid var(--border-subtle);
  border-radius: 10px; /* intentional: prototype segmented 外框，介於 --radius-sm (6) 與 --radius-md (12) */
  flex: 0 0 auto;
}
.tts-speed__btn {
  font-family: var(--font-en-mono);
  font-variant-numeric: tabular-nums;
  font-size: 12px; /* intentional: prototype segmented 標籤，介於 --text-xs (11) 與 --text-sm (13) */
  line-height: 1;
  cursor: pointer;
  padding: 5px 9px; /* intentional: prototype segmented padding */
  border-radius: 8px; /* intentional: prototype segmented 選中格，介於 --radius-sm (6) 與 --radius-md (12) */
  border: 1px solid transparent;
  background: transparent;
  color: var(--text-muted);
  transition: color var(--duration-base), background var(--duration-base);
}
.tts-speed__btn.is-active {
  background: var(--bg-surface);
  border-color: var(--border-default);
  color: var(--text-primary);
  font-weight: 600;
}
.tts-audio { display: none; }

/* 失敗（一般） */
.tts-fail__icon { color: var(--status-wrong); flex: 0 0 auto; display: flex; }
.tts-fail__icon svg {
  width: 17px; height: 17px; fill: none; stroke: currentColor;
  stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round;
}
.tts-fail__text {
  flex: 1;
  font-family: var(--font-zh-sans);
  font-size: 13.5px; /* intentional: prototype strip 內文 */
  color: var(--text-secondary);
  line-height: 1.5;
}
.tts-retry svg { width: 14px; height: 14px; flex-shrink: 0; }

/* 失敗（需要登入）：無重試鈕，導向 meta row 的重新生成 */
.tts-strip--login { align-items: flex-start; gap: 13px; padding: 15px 17px; } /* intentional: prototype */
.tts-login__icon { color: var(--text-muted); flex: 0 0 auto; display: flex; margin-top: 1px; }
.tts-login__icon svg {
  width: 17px; height: 17px; fill: none; stroke: currentColor;
  stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round;
}
.tts-login__text {
  margin: 0;
  flex: 1;
  font-family: var(--font-zh-sans);
  font-size: 13.5px;
  color: var(--text-secondary);
  line-height: 1.65;
}
.tts-login__domain {
  font-family: var(--font-en-mono);
  font-size: 12.5px;
  color: var(--text-secondary);
}
.tts-login__pointer {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  color: var(--accent);
  font-weight: 500;
}
.tts-login__pointer svg {
  width: 12px; height: 12px; fill: none; stroke: currentColor;
  stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round;
}

/* ── URL 列並行入口：視覺明顯次於主按鈕「解析文章」 ── */
.tts-parallel {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  cursor: pointer;
  flex: 0 0 auto;
  font-family: var(--font-zh-sans);
  font-size: 12.5px; /* intentional: prototype，明顯小於主按鈕 */
  color: var(--text-muted);
  transition: color var(--duration-base);
}
.tts-parallel:has(.tts-parallel__input:checked) { color: var(--text-secondary); }
.tts-parallel__input {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
}
.tts-parallel__box {
  width: 16px;
  height: 16px;
  border-radius: 5px; /* intentional: prototype checkbox radius */
  flex: 0 0 auto;
  border: 1.5px solid var(--border-default);
  background: transparent;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: all var(--duration-fast) var(--ease-out);
}
.tts-parallel__box svg {
  width: 11px; height: 11px; fill: none; stroke: var(--accent-ink);
  stroke-width: 3; stroke-linecap: round; stroke-linejoin: round;
  opacity: 0;
}
.tts-parallel__input:checked + .tts-parallel__box {
  border-color: var(--accent);
  background: var(--accent);
}
.tts-parallel__input:checked + .tts-parallel__box svg { opacity: 1; }
.tts-parallel__input:focus-visible + .tts-parallel__box { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ── PulseDot ── */
.pulse-dot {
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--accent);
  flex: 0 0 auto;
  animation: pulseDot 1400ms ease-in-out infinite;
}

/* ── StreamingCaret ── */
/* intentional: named .streaming-caret to avoid conceptual collision with
   .tooltip-caret / .translate-caret / .romaji-caret (all arrow/triangle carets).
   This is a text cursor blink, different semantic. See EmptyStates audit Section 7-B. */
.streaming-caret {
  display: inline-block;
  /* intentional: width 0.5em — scales with font size so caret weight stays proportional at both prose-lg (17px) and ai-prose (14px). */
  width: 0.5em;
  height: 1.1em;
  background: var(--accent);
  vertical-align: text-bottom;
  /* intentional: marginLeft 2px — tight adhesion to text, no token for 2px gap. */
  margin-left: 2px;
  animation: caretBlink 900ms steps(2, start) infinite;
}

/* ── ChatEmpty ── */
.chat-empty {
  flex: 1;
  display: flex;
  flex-direction: column;
  padding: 32px 28px 24px;
}
.chat-empty__avatar {
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: 1px solid var(--border-default);
  background: var(--bg-subtle);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--accent);
  margin-bottom: 18px;
  flex-shrink: 0;
}
.chat-empty__title {
  font-family: var(--font-zh-serif);
  /* intentional: fontSize 20px — prototype spec; --text-h3 is 19px. +1px for CoachWelcome-like
     panel greeting weight. See EmptyStates audit Section 5-D. */
  font-size: 20px;
  font-weight: 500;
  color: var(--text-primary);
  margin: 0 0 12px;
  /* intentional: letterSpacing 0.005em — mirrors .coach-welcome h3 tracking. */
  letter-spacing: 0.005em;
}
.chat-empty__body {
  font-family: var(--font-zh-serif);
  /* intentional: fontSize 14.5px — prototype spec; --text-body is 15px. -0.5px for compact
     secondary copy. See EmptyStates audit Section 5-D. */
  font-size: 14.5px;
  /* intentional: lineHeight 1.85 — prototype spec; no token match. Generous for zh-serif. */
  line-height: 1.85;
  color: var(--text-secondary);
  margin: 0;
  text-wrap: pretty;
}
.chat-empty__section-header {
  margin-top: 28px;
  margin-bottom: 12px;
  display: flex;
  align-items: baseline;
  gap: 10px;
}
.chat-empty__section-eyebrow {
  font-family: var(--font-en-display);
  /* intentional: en-display + uppercase + letterSpacing applied to ZH content
     for editorial eyebrow style; ZH chars fallback to serif gracefully.
     See EmptyStates D-4-F2. */
  font-size: var(--text-xs);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-secondary);
  font-weight: 500;
  white-space: nowrap;
}
.chat-empty__section-hairline {
  flex: 1;
  height: 1px;
  background: var(--border-subtle);
}
.chat-empty__section-level {
  font-family: var(--font-en-display);
  /* intentional: en-display applied to mixed ZH-EN content;
     matches prototype level label style. See EmptyStates D-4-F3. */
  /* intentional: fontSize 10.5px — prototype spec; slightly smaller than eyebrow (11px)
     for typographic layer distinction. See EmptyStates audit Section 5-D. */
  font-size: 10.5px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-muted);
  white-space: nowrap;
}
.chat-empty__site-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.chat-empty__expand-btn {
  align-self: flex-start;
  margin-top: 14px;
  padding: 4px;
  background: transparent;
  border: 0;
  cursor: pointer;
  font-family: var(--font-zh-sans);
  font-size: var(--text-xs);
  color: var(--text-secondary);
  display: inline-flex;
  align-items: center;
  gap: 6px;
  transition: color var(--duration-fast) var(--ease-out);
}
.chat-empty__expand-btn:hover { color: var(--accent); }
.chat-empty__chevron {
  transition: transform var(--duration-base) var(--ease-out);
}
.chat-empty__hint-area {
  margin-top: auto;
  padding-top: 18px;
  border-top: 1px solid var(--border-subtle);
}
.chat-empty__hint {
  margin: 14px 0 0;
  font-family: var(--font-zh-sans);
  /* intentional: 12.5px — matches AnalysisEmpty hint / AnalysisStreaming header small size. */
  font-size: 12.5px;
  line-height: 1.65;
  color: var(--text-muted);
  text-wrap: pretty;
}

/* ── ChatBusy ── */
.chat-busy {
  flex: 1;
  display: flex;
  flex-direction: column;
  padding: 32px 28px 24px;
}
.chat-busy__avatar-wrap {
  position: relative;
  width: 44px;
  height: 44px;
  margin-bottom: 18px;
  flex-shrink: 0;
}
.chat-busy__avatar {
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: 1px solid var(--border-default);
  background: var(--bg-subtle);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--accent);
}
.chat-busy__halo {
  position: absolute;
  inset: -1px;
  border-radius: 50%;
  border: 1.5px solid var(--accent);
  animation: coachHalo 1800ms ease-out infinite;
}
.chat-busy__title {
  font-family: var(--font-zh-serif);
  /* intentional: 20px — mirrors ChatEmpty title size. */
  font-size: 20px;
  font-weight: 500;
  color: var(--text-primary);
  margin: 0 0 10px;
  letter-spacing: 0.005em;
}
.chat-busy__body {
  font-family: var(--font-zh-serif);
  /* intentional: 14.5px / 1.85 — mirrors ChatEmpty body spec. */
  font-size: 14.5px;
  line-height: 1.85;
  color: var(--text-secondary);
  margin: 0;
  text-wrap: pretty;
}
.chat-busy__section-header {
  margin-top: 32px;
  margin-bottom: 12px;
  display: flex;
  align-items: baseline;
  gap: 10px;
}
.chat-busy__section-eyebrow {
  font-family: var(--font-en-display);
  font-size: var(--text-xs);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-secondary);
  font-weight: 500;
  white-space: nowrap;
}
.chat-busy__section-hairline {
  flex: 1;
  height: 1px;
  background: var(--border-subtle);
}
.chat-busy__ability-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.chat-busy__ability-card {
  border: 1px solid var(--border-subtle);
  background: var(--bg-subtle);
  border-radius: 12px;
  padding: 12px 14px;
  display: grid;
  grid-template-columns: 28px 1fr;
  gap: 12px;
  align-items: start;
  animation: abilityIn 360ms var(--ease-entry) both;
}
.chat-busy__ability-icon {
  width: 28px;
  height: 28px;
  border-radius: 8px;
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  color: var(--text-secondary);
  display: flex;
  align-items: center;
  justify-content: center;
  margin-top: 1px;
  flex-shrink: 0;
}
.chat-busy__ability-title {
  font-family: var(--font-zh-sans);
  font-size: 13px;
  font-weight: 500;
  color: var(--text-primary);
  margin-bottom: 4px;
  letter-spacing: 0.005em;
}
.chat-busy__ability-example {
  font-family: var(--font-zh-serif);
  /* intentional: 12.5px — matches ChatBusy prototype spec. */
  font-size: 12.5px;
  color: var(--text-muted);
  line-height: 1.6;
  text-wrap: pretty;
}
.chat-busy__ability-example[lang="ja"] {
  font-family: var(--font-jp-serif);
}
.chat-busy__ability-ex-label {
  font-family: var(--font-en-display);
  /* intentional: 10.5px — smaller than body for EX label hierarchy. */
  font-size: 10.5px;
  letter-spacing: 0.16em;
  color: var(--text-muted);
  margin-right: 6px;
  opacity: 0.7;
}
@keyframes coachHalo {
  0%   { opacity: 0.9; transform: scale(1);    }
  70%  { opacity: 0;   transform: scale(1.55); }
  100% { opacity: 0;   transform: scale(1.55); }
}
@keyframes abilityIn {
  from { opacity: 0; transform: translateY(4px); }
  to   { opacity: 1; transform: translateY(0);   }
}

/* ── SiteCard ── */
.site-card-wrap {
  list-style: none;
  animation: siteCardIn 320ms var(--ease-entry) both;
}
.site-card {
  display: block;
  /* intentional: padding 12px 14px 13px — compact card inset; no exact token match. */
  padding: 12px 14px 13px;
  background: var(--bg-subtle);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  text-decoration: none;
  color: inherit;
  transition: background var(--duration-base) var(--ease-out),
              border-color var(--duration-base) var(--ease-out);
}
.site-card:hover {
  background: var(--bg-elevated);
  border-color: var(--border-default);
}
.site-card__header {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 6px;
}
.site-card__name {
  font-family: var(--font-zh-serif);
  /* intentional: fontSize 14px — prototype spec; --text-sm is 13px. +1px for site name
     as content heading within card. */
  font-size: 14px;
  font-weight: 500;
  color: var(--text-primary);
  letter-spacing: 0.005em;
  line-height: 1.3;
}
.site-card__spacer { flex: 1; }
.site-card__external-arrow {
  color: var(--text-muted);
  flex-shrink: 0;
  transition: color var(--duration-fast) var(--ease-out),
              transform var(--duration-fast) var(--ease-out);
}
.site-card:hover .site-card__external-arrow {
  color: var(--accent);
  transform: translate(1px, -1px);
}
.site-card__desc {
  margin: 0;
  font-family: var(--font-zh-sans);
  font-size: var(--text-xs);
  line-height: 1.55;
  color: var(--text-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ── LevelBadge ── */
.level-badge {
  display: inline-flex;
  align-items: center;
  padding: 2px 7px;
  border-radius: var(--radius-misc-1);
  font-family: var(--font-en-display);
  /* intentional: fontSize 10.5px — prototype spec; slightly below --text-xs (11px). */
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: 0.04em;
  line-height: 1.4;
  border: 1px solid;
  /* default (unmatched): status-correct palette */
  background: var(--status-correct-bg);
  color: var(--status-correct);
  /* intentional: border-color rgba(79,184,146,0.22) — status-correct derived alpha;
     no token in scale for status-* border variants. See EmptyStates D-5. */
  border-color: rgba(79, 184, 146, 0.22);
}
.level-badge--matched {
  background: var(--accent-soft);
  color: var(--accent);
  border-color: var(--accent-border);
}

/* ── CategoryTag ── */
.category-tag {
  display: inline-flex;
  align-items: center;
  padding: 2px 7px;
  border-radius: var(--radius-misc-1);
  background: transparent;
  color: var(--text-secondary);
  font-family: var(--font-zh-sans);
  font-size: var(--text-xs);
  border: 1px solid var(--border-subtle);
  line-height: 1.4;
}


/* ════════════════════════════════════════════════════════════════════════════
   VocGroup — Section 03 字彙分組（Category Icons × Grammar Badges）
   ════════════════════════════════════════════════════════════════════════════ */

/* ── Category Badge (CatBadge) ── */
.cat-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--accent-soft);
  color: var(--accent);
  flex-shrink: 0;
}
.cat-badge--md { width: 28px; height: 28px; border-radius: 6px; }
.cat-badge--md svg { width: 18px; height: 18px; }
.cat-badge--sm { width: 18px; height: 18px; border-radius: 4px; }
.cat-badge--sm svg { width: 12px; height: 12px; }

/* ── Grammar Badge (GramBadge) ── */
.gram-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--accent-soft);
  color: var(--accent);
  font-family: var(--font-jp-serif);
  font-weight: 600;
  line-height: 1;
  flex-shrink: 0;
}
.gram-badge--md { width: 28px; height: 28px; border-radius: 6px; font-size: 14px; padding-bottom: 1px; }
.gram-badge--sm { width: 18px; height: 18px; border-radius: 4px; font-size: 10.5px; }

/* ── VocGroup 容器 ── */
.voc-section { margin-top: 4px; }

.voc-section__intro {
  font-family: var(--font-zh-serif);
  font-size: var(--text-sm);
  color: var(--text-secondary);
  margin: 0 0 20px;
  line-height: var(--lh-body-lg);
}
.voc-section__intro-muted { color: var(--text-muted); }

/* ── 單一分組 ── */
.voc-group { margin-bottom: 28px; }
.voc-group:last-child { margin-bottom: 0; }

.voc-group__header {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 15px;
}
.voc-group__label {
  font-family: var(--font-zh-serif);
  font-size: var(--text-body);
  font-weight: 500;
  color: var(--text-primary);
}
.voc-group__kicker {
  font-family: var(--font-en-display);
  font-size: 9px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.voc-group__sep {
  flex: 1;
  height: 1px;
  background: var(--border-subtle);
  margin: 0 6px;
  align-self: center;
}
.voc-group__count {
  font-family: var(--font-en-mono);
  font-size: 11px;
  color: var(--text-muted);
}

/* ── Column header（只在第一組出現）── */
.voc-col-header {
  display: grid;
  grid-template-columns: var(--voc-cols, 22% 24% 38% 16%);
  padding: 5px 12px 5px 38px;
  border-bottom: 1px solid var(--border-default);
}
.voc-col-header__cell {
  font-family: var(--font-zh-sans);
  font-size: 11.5px;
  color: var(--text-muted);
}

/* ── Word rows ── */
.voc-list {
  list-style: none;
  margin: 0;
  padding: 0;
  border-top: 1px solid var(--border-subtle);
}
/* intentional: override .prose-lg ul li flex + ::before bullet for vocab grid rows */
.prose-lg .voc-list .voc-row,
.voc-list .voc-row {
  display: grid;
  grid-template-columns: var(--voc-cols, 22% 24% 38% 16%);
  align-items: baseline;
  padding: 8px 12px 8px 38px;
  border-bottom: 1px solid var(--border-subtle);
  gap: 0;
}
.prose-lg .voc-list .voc-row::before,
.voc-list .voc-row::before { display: none; }
.voc-list .voc-row:last-child { border-bottom: none; }
.voc-list .voc-row:nth-child(odd) { background: var(--bg-subtle); }

.voc-row__jp {
  font-family: var(--font-jp-serif);
  font-size: var(--text-body);
  color: var(--text-primary);
}
.voc-row__kana {
  font-family: var(--font-jp-serif);
  font-size: var(--text-sm);
  color: var(--text-muted);
}
.voc-row__zh {
  font-family: var(--font-zh-serif);
  font-size: var(--text-sm);
  color: var(--text-primary);
}
.voc-row__pos {
  font-family: var(--font-zh-sans);
  font-size: 11px;
  color: var(--text-primary);
}

/* ── Section 03 loading / error wrapper ── */
.voc-loading {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 20px 0 4px;
  color: var(--text-muted);
  font-family: var(--font-zh-sans);
  font-size: var(--text-sm);
}
.voc-loading__spinner {
  width: 14px;
  height: 14px;
  border: 2px solid var(--border-subtle);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: voc-spin 0.8s linear infinite;
  flex-shrink: 0;
}
@keyframes voc-spin { to { transform: rotate(360deg); } }

/* ═══════════════════════════════════════════════════════════════════════════
   題庫（O-1 Stage 1）
   Row / preview-card / marker / generating. Dark + light via existing tokens.
   ═══════════════════════════════════════════════════════════════════════════ */

/* The display rules below would otherwise beat the HTML hidden attribute, leaving
   every collapsed action strip / stamp / spinner on screen. Scoped to 題庫 so the
   rest of the app keeps its per-class [hidden] rules. */
#tab-bank [hidden] { display: none !important; }

/* 種類標籤（課程／文章／筆記）— 設計 Primitives.jsx → Tag：填色、無框、矮、安靜。
   刻意不掛在 .chip 底下：.chip 是可點擊的互動元件（cursor: pointer，hover 整個
   變 accent），這裡是永遠不能點的靜態標籤。繼承它會把兩者綁死，也會讓標籤跟同一
   列的 .qb-marker（外框 + accent 的狀態蓋章）撞成同一種視覺語言。
   Tag 填色 / Marker 外框 —— 這個對比是設計刻意的層次，不能抹平。
   （REFACTOR_AUDIT 2.2 把設計誤述為「方角邊框」，該項裁決已作廢） */
.qb-kind {
  display: inline-flex;
  align-items: center;
  padding: 2px var(--space-2);
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background: var(--bg-subtle);
  color: var(--text-secondary);
  font-family: var(--font-zh-sans);
  font-size: var(--text-xs);
  line-height: 1.5;
  white-space: nowrap;
}

/* The two existing tabs are 60/40 two-column grids; 題庫 is a single column.
   Without this it would be squeezed into the left 60% with dead space right. */
.tab-panel > main.main--single {
  grid-template-columns: minmax(0, 1fr);
  max-width: 1080px;
  padding: var(--space-7) var(--space-6) var(--space-9);
}

/* ── Page header ── */
.qb-header { margin-bottom: var(--space-6); }
.qb-eyebrow {
  font-family: var(--font-zh-sans);
  font-size: var(--text-xs);
  letter-spacing: 0.1em;
  font-weight: 500;
  color: var(--text-muted);
  margin: 0 0 var(--space-2);
}
.qb-title {
  font-family: var(--font-zh-serif);
  font-size: var(--text-h1);
  font-weight: 500;
  letter-spacing: 0.01em;
  color: var(--text-primary);
  margin: 0;
}
.qb-lede {
  font-family: var(--font-zh-sans);
  font-size: var(--text-sm);
  line-height: 1.7;
  color: var(--text-secondary);
  max-width: 560px;
  margin: var(--space-3) 0 0;
}

/* ── Preview cards ── */
.qb-preview-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-5);
  align-items: stretch;
  margin-bottom: var(--space-5);   /* 設計 20px；不在 scale 上，取最近的 24px */
}
.qb-preview {
  display: flex;
  flex-direction: column;
  position: relative;
  min-height: 260px;
  padding: var(--space-5);
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
}
.qb-preview__eyebrow {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
}
.qb-preview__when {
  font-family: var(--font-zh-sans);
  font-size: var(--text-xs);
  font-weight: 500;
  letter-spacing: 0.08em;
  color: var(--text-secondary);
}
.qb-preview__date {
  font-family: var(--font-en-display);
  font-size: var(--text-xs);
  letter-spacing: 0.06em;
  color: var(--text-muted);
  font-feature-settings: "lnum" 1, "tnum" 1;
}
.qb-preview__body {
  flex: 1;
  display: flex;
  flex-direction: column;
  animation: qbFade var(--duration-entry) var(--ease-entry) both;
}
.qb-preview__material {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-bottom: var(--space-1);
}
.qb-preview__material-label {
  font-family: var(--font-zh-sans);
  font-size: var(--text-xs);
  letter-spacing: 0.06em;
  color: var(--text-muted);
}
.qb-preview__source {
  font-family: var(--font-jp-serif);
  font-size: var(--text-h3);
  font-weight: 500;
  line-height: 1.4;
  letter-spacing: 0.01em;
  color: var(--text-primary);
  margin-bottom: var(--space-4);
}
.qb-preview__source.is-notes { font-family: var(--font-zh-serif); }

.qb-preview__focus { margin-bottom: var(--space-4); }
.qb-preview__focus-label {
  font-family: var(--font-zh-sans);
  font-size: var(--text-xs);
  letter-spacing: 0.06em;
  color: var(--text-muted);
  margin-bottom: var(--space-2);
}
.qb-preview__focus-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}
.qb-focus-item {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
}
.qb-focus-item__num {
  font-family: var(--font-en-display);
  font-size: var(--text-xs);
  color: var(--accent);
  opacity: 0.7;
  font-feature-settings: "lnum" 1;
  flex: 0 0 auto;
}
.qb-focus-item__text {
  font-family: var(--font-jp-sans);
  font-size: var(--text-sm);
  color: var(--text-primary);
  line-height: 1.5;
}

.qb-preview__foot { margin-top: auto; }
.qb-preview__review {
  font-family: var(--font-zh-sans);
  font-size: var(--text-xs);
  line-height: 1.6;
  color: var(--text-muted);
  margin: 0;
}
.qb-preview__review-label {
  letter-spacing: 0.04em;
  margin-right: var(--space-2);
}
.qb-cancel-btn {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-3);
  padding: 0;
  background: transparent;
  border: 0;
  cursor: pointer;
  font-family: var(--font-zh-sans);
  font-size: var(--text-xs);
  letter-spacing: 0.02em;
  color: var(--text-secondary);
  transition: color var(--duration-fast) var(--ease-out);
}
.qb-cancel-btn:hover { color: var(--text-primary); }
.qb-cancel-btn svg { width: 13px; height: 13px; flex: 0 0 auto; }

/* ── Generating (today card only) ── */
.qb-generating {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  padding: var(--space-5) 0;
  animation: qbFade var(--duration-entry) var(--ease-out) both;
}
.qb-breath {
  display: inline-flex;
  gap: var(--space-2);
  margin-bottom: var(--space-4);
}
.qb-breath span {
  width: 7px;
  height: 7px;
  border-radius: var(--radius-full);
  background: var(--accent);
  animation: qbBreath var(--duration-breath) ease-in-out infinite;
}
.qb-breath span:nth-child(2) { animation-delay: var(--duration-entry); }
.qb-breath span:nth-child(3) { animation-delay: calc(var(--duration-entry) * 2); }
.qb-generating__title {
  font-family: var(--font-zh-serif);
  font-size: var(--text-body-lg);
  letter-spacing: 0.01em;
  color: var(--text-primary);
  margin: 0 0 var(--space-2);
}
.qb-generating__lede {
  font-family: var(--font-zh-sans);
  font-size: var(--text-xs);
  line-height: 1.7;
  color: var(--text-muted);
  max-width: 300px;
  margin: 0;
}

/* ── Position marker ── */
.qb-marker {
  display: inline-flex;
  align-items: baseline;
  flex: 0 0 auto;
  padding: 3px var(--space-2);
  border: 1px solid var(--accent-soft);
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--accent);
  font-family: var(--font-zh-sans);
  font-size: var(--text-xs);
  font-weight: 500;
  letter-spacing: 0.02em;
  white-space: nowrap;
}
.qb-marker--strong {
  border-color: var(--accent);
  background: var(--accent-soft);
}

/* ── Syllabus ── */
.qb-level { margin-bottom: var(--space-8); }   /* 設計 64px = --space-8 */
.qb-level__header {
  display: flex;
  align-items: baseline;
  gap: var(--space-3);
  padding-bottom: var(--space-3);
  margin-bottom: var(--space-5);
  border-bottom: 1px solid var(--border-default);
}
.qb-level__name {
  font-family: var(--font-en-display);
  font-size: var(--text-h1);
  font-weight: 500;
  letter-spacing: 0.02em;
  line-height: 1;
  color: var(--text-primary);
}
.qb-level__tag {
  font-family: var(--font-zh-sans);
  font-size: var(--text-xs);
  letter-spacing: 0.04em;
  color: var(--text-muted);
}

.qb-subsection { margin-bottom: var(--space-6); }
.qb-subsection__head {
  display: flex;
  align-items: baseline;
  gap: var(--space-3);
  margin-bottom: var(--space-3);
  padding-left: 2px;
}
.qb-subsection__label {
  font-family: var(--font-zh-sans);
  font-size: var(--text-sm);
  font-weight: 500;
  letter-spacing: 0.06em;
  color: var(--text-secondary);
}
.qb-subsection__note {
  font-family: var(--font-zh-sans);
  font-size: var(--text-xs);
  color: var(--text-muted);
}
.qb-subsection__rows {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

/* ── Row ── */
.qb-row {
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  background: var(--bg-surface);
  overflow: hidden;
  transition: border-color var(--duration-fast) var(--ease-out);
}
.qb-row:hover,
.qb-row.is-expanded { border-color: var(--border-default); }

.qb-row__head {
  width: 100%;
  display: flex;
  align-items: flex-start;
  gap: var(--space-4);
  padding: var(--space-4);
  text-align: left;
  background: transparent;
  border: 0;
  cursor: pointer;
}
.qb-row__glyph {
  width: 16px;
  height: 16px;
  flex: 0 0 auto;
  margin-top: 3px;
  color: var(--text-muted);
}
.qb-row__main {
  flex: 1;
  min-width: 0;
  display: block;
}
.qb-row__title-line {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--space-2);
}
.qb-row__title {
  font-family: var(--font-jp-serif);
  font-size: var(--text-body-lg);
  font-weight: 500;
  line-height: 1.4;
  letter-spacing: 0.01em;
  color: var(--text-primary);
}
/* pdf 課程序號「第 N 課」：灰色小字，低調。
   刻意 muted（非 accent）—— 上方今日/明日小卡的文法重點用 accent「01 02 03」，
   課次若也用同色同格式會混淆兩者語意。muted + 中文「第 N 課」雙重區隔。 */
.qb-row__num {
  font-family: var(--font-zh-sans);
  font-size: var(--text-sm);
  color: var(--text-muted);
  align-self: center;
  white-space: nowrap;
  flex: 0 0 auto;
}
.qb-row__count {
  font-family: var(--font-zh-sans);
  font-size: var(--text-xs);
  color: var(--text-muted);
}
.qb-row__src {
  font-family: var(--font-en-display);
  font-size: var(--text-xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.qb-row__topics {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1) var(--space-3);
  margin-top: var(--space-2);
}
.qb-row__topics:empty { display: none; }
.qb-topic {
  font-family: var(--font-jp-sans);
  font-size: var(--text-xs);
  letter-spacing: 0.01em;
  color: var(--text-secondary);
}
.qb-row__last {
  display: block;
  margin-top: var(--space-2);
  font-family: var(--font-zh-sans);
  font-size: var(--text-xs);
  letter-spacing: 0.02em;
  color: var(--text-muted);
}
.qb-row__right {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex: 0 0 auto;
  padding-top: 1px;
}
.qb-row__chevron {
  width: 15px;
  height: 15px;
  flex: 0 0 auto;
  color: var(--text-muted);
  transition: transform var(--duration-base) var(--ease-out);
}
.qb-row.is-expanded .qb-row__chevron { transform: rotate(180deg); }

/* ── Inline action strip ── */
.qb-row__actions {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  border-top: 1px solid var(--border-subtle);
  animation: qbExpand var(--duration-base) var(--ease-entry) both;
}
/* 題庫動作列的按鈕直接用共用系統：指定今日/明日 → .btn-secondary，
   移出題庫 → .btn-ghost。原本的 .qb-action-btn / --subtle 已收斂掉。 */
/* 設計的 ActionBtn 有 0.02em 字距 —— 但每日練習的 followup/reveal/retry 都沒有，
   所以字距只屬於題庫動作列，加在這裡的 scope，不污染共用的 .btn-secondary。 */
.qb-row__actions .qb-action-btn { letter-spacing: 0.02em; }
.qb-action-hint {
  font-family: var(--font-zh-sans);
  font-size: var(--text-xs);
  color: var(--text-muted);
}
.qb-action-divider {
  width: 1px;
  height: 18px;
  margin: 0 2px;
  background: var(--border-subtle);
}

/* ── Confirm modal body ── */
/* 兩段緊湊排列 —— 設計的確認框段間距是 8px，既有 .modal-body 的 gap(24px)
   是為設定表單設計的，對確認框太鬆。compact 目前只有題庫確認框在用。 */
.modal--compact .modal-body { gap: var(--space-2); }
.qb-modal-note {
  font-family: var(--font-zh-sans);
  font-size: var(--text-sm);
  line-height: 1.7;
  color: var(--text-muted);
  margin: 0;
}
.qb-modal-lede {
  font-family: var(--font-zh-sans);
  font-size: var(--text-sm);
  line-height: 1.75;
  color: var(--text-secondary);
  /* 段間距交給 .modal-body 的 flex gap；自己不帶 margin，否則會與 gap 疊加。 */
  margin: 0;
}
.qb-modal-lede .qb-modal-source {
  font-family: var(--font-jp-serif);
  color: var(--text-primary);
}

/* ── Empty state ── */
.qb-empty {
  padding: var(--space-7) 0;
  text-align: center;
  font-family: var(--font-zh-sans);
  font-size: var(--text-sm);
  color: var(--text-muted);
}

@keyframes qbFade {
  from { opacity: 0; transform: translateY(4px); }
  to   { opacity: 1; transform: translateY(0); }
}
@keyframes qbExpand {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: translateY(0); }
}
@keyframes qbBreath {
  0%, 100% { opacity: 0.3; }
  50%      { opacity: 1; }
}

@media (max-width: 768px) {
  .qb-preview-grid { grid-template-columns: 1fr; }
}
