/* ==========================================================================
   components.css — 元件層樣式（app.css 頂端 design tokens 為唯一依據）
   拆檔原因：app.css 加上本檔內容原本單檔會超過 400 行規範，依 §0 拆檔規則
   切開；tokens 只放 app.css 一份，本檔純消費變數不重複定義。
   ========================================================================== */

/* -- 通用工具：畫面上不顯示但螢幕報讀器讀得到（R15 分數 SVG 動畫用，數字被
   包進 <svg><text> 動畫效果的元素，另外補一個純文字版供無障礙輔助工具讀）-- */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* -- Stats bar (今日頁) -- */
.stats-row { display: flex; gap: var(--space-3); margin-bottom: var(--space-5); }
.stat-tile { flex: 1; text-align: center; }
.stat-tile .stat-value { font-size: var(--font-size-2xl); font-weight: 700; color: var(--color-brand-600); }
.stat-tile .stat-label { font-size: var(--font-size-xs); color: var(--color-text-muted); margin-top: 2px; }
/* 第十一輪待辦③：「今天還有 N 張到期」改成完整語句（不是大數字＋小標籤兩截），
   N>0 時可點（cursor:pointer 是行內樣式，見 today.js statsHtml），disabled
   （N=0）時退回一般文字外觀，不留死游標提示。 */
.stat-due-text { font-size: var(--font-size-sm); color: var(--color-text); }
.stat-due-text strong { font-size: var(--font-size-2xl); color: var(--color-brand-600); font-weight: 700; }
.stat-tile-due:disabled { cursor: default; }
.stat-due-sub { font-size: var(--font-size-xs); color: var(--color-success-600); margin-top: 2px; }
.sync-banner {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  background: var(--color-warn-bg);
  color: var(--color-text);
  border-radius: var(--radius-md);
  padding: var(--space-2) var(--space-4);
  font-size: var(--font-size-sm);
  margin-bottom: var(--space-4);
}
.offline-banner {
  background: var(--color-text-faint);
  color: var(--color-surface);
  text-align: center;
  font-size: var(--font-size-xs);
  padding: var(--space-1);
  max-width: var(--app-max-width);
  margin: 0 auto;
}

/* -- R15：自動更新提示改中央 modal（原第十四輪 §4-1 的頂部橫幅淘汰，見
   index.html／app.js 同段落註解）。版面比照既有 .mini-card-overlay／
   .mini-card-panel（fixed 全螢幕置中遮罩＋卡片），z-index 刻意比 mini-card
   （80）／toast（100）都高——這是全站層級的提示，任何畫面疊著別的 overlay
   時都要蓋得過去。 -- */
.update-modal {
  position: fixed;
  inset: 0;
  background: var(--color-overlay);
  z-index: 200;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-5);
}
/* P0 事故真兇（2026-07-10）：上面的 display:flex 蓋過 HTML hidden 屬性的
   UA 樣式（author style 永遠贏 UA style），造成彈窗「永遠顯示、關不掉」，
   從 R14 橫幅時代就存在。第四輪待辦50 已有同型教訓（[hidden] 要明講）。 */
.update-modal[hidden] { display: none !important; }
.update-modal-panel {
  background: var(--color-surface-raised);
  border-radius: var(--radius-lg);
  border: var(--border-width) solid var(--color-border);
  box-shadow: var(--shadow-lg);
  padding: var(--space-6) var(--space-5);
  width: 100%;
  max-width: 320px;
  text-align: center;
}
.update-modal-title { font-size: var(--font-size-lg); font-weight: 700; color: var(--color-text); margin: 0 0 var(--space-2); }
.update-modal-status {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  font-size: var(--font-size-sm);
  color: var(--color-text-muted);
  margin: 0 0 var(--space-4);
}
.update-modal-spinner {
  flex: none;
  width: 16px;
  height: 16px;
  border: 2px solid var(--color-border-strong);
  border-top-color: var(--color-brand-600);
  border-radius: 50%;
  animation: update-modal-spin 0.8s linear infinite;
}
@keyframes update-modal-spin {
  to { transform: rotate(360deg); }
}
.update-modal-actions { display: flex; gap: var(--space-3); margin-top: var(--space-4); }
.update-modal-actions .btn { flex: 1; }

/* -- 卡片畫面版面（快修包 B-2：390x844 一屏內完整呈現，不靠捲動）--
   今日複習／加練模式的整個畫面用 .card-screen 包住：banner／提示文字 flex:none，
   翻轉卡撐滿剩餘高度（flex:1），操作鍵固定在底部，讓卡片內容有完整可用高度、
   不必再靠 .flip-face 的 overflow-y:auto 兜底捲動。 */
.card-screen {
  display: flex;
  flex-direction: column;
  min-height: calc(100dvh - var(--tabbar-height));
}
.card-screen > * { flex: none; }
/* 第十六輪回饋第1項：position:relative——換卡時舊卡會被 card-flip.js
   detachToOverlay() 暫時抽成 position:absolute; inset:0（見該檔案註解），
   inset:0 要相對「這個容器」定位，才會精準疊在新卡（仍是正常 flex:1 版位）
   上方，不會跑版/跳動。新卡本身維持原本的 flex 版位不受影響。 */
.card-screen .card-slot { flex: 1; min-height: 0; display: flex; flex-direction: column; position: relative; }
.card-screen .flip-card-wrap { flex: 1; min-height: 0; }

/* -- Flip Card（3D 翻轉） -- */
/* 第十二輪待辦③：滑開卡片式手勢用 JS 直接操作 transform/opacity/transition
   （見 card-flip.js），will-change 只是效能提示，不影響版面。第十六輪回饋
   第1項：疊層滑出的舊卡會被 JS 動態設 position:absolute/inset:0/margin:0
   （見 card-flip.js detachToOverlay），跟這裡的預設樣式不衝突（JS inline
   style 特異度天生贏過這條 class 規則）。 */
.flip-card-wrap { perspective: 1200px; margin-bottom: var(--space-4); display: flex; will-change: transform; }
.flip-card {
  position: relative;
  flex: 1;
  min-height: 280px;
  transform-style: preserve-3d;
  transition: transform 500ms cubic-bezier(0.4, 0.2, 0.2, 1);
}
.flip-card.is-flipped { transform: rotateY(180deg); }
.flip-face {
  position: absolute;
  inset: 0;
  backface-visibility: hidden;
  border-radius: var(--radius-lg);
  background: var(--color-surface);
  border: var(--border-width) solid var(--color-border);
  box-shadow: var(--shadow-md);
  padding: var(--space-5) var(--space-5);
  overflow-y: auto;
}
.flip-face-front {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-4);
  text-align: center;
}
.flip-face-back { transform: rotateY(180deg); display: flex; flex-direction: column; }
.flip-hint { color: var(--color-text-faint); font-size: var(--font-size-xs); }
.headword-text { font-size: var(--font-size-3xl); font-weight: var(--font-weight-heading); word-break: break-word; }
.kk-text { color: var(--color-text-muted); font-size: var(--font-size-lg); }

/* -- 卡片背面／詞庫詳情共用「頭部」：第一行拼字、第二行 KK＋發音鍵（快修包 B-1） -- */
.card-header-word { font-size: var(--font-size-2xl); margin-bottom: var(--space-2); }
.card-header-kk-row { display: flex; align-items: center; gap: var(--space-2); margin-bottom: var(--space-3); }
.card-header-kk-row .kk-text { margin: 0; }

/* -- Card back content blocks（複習卡背面／詞庫詳情共用） -- */
.back-section { margin-bottom: var(--space-3); }
.back-section-title { font-size: var(--font-size-xs); text-transform: uppercase; letter-spacing: 0.04em; color: var(--color-text-faint); margin-bottom: var(--space-1); }
/* 第四輪回饋第 5 條：詞性變化 chips 明確靠左（flex 預設值本來就是
   flex-start，這裡寫明避免日後有人在別處疊加 justify-content 覆寫掉）。 */
.pos-chip-row { display: flex; flex-wrap: wrap; justify-content: flex-start; gap: var(--space-2); }
.pos-chip { background: var(--color-brand-50); color: var(--color-brand-700); border-radius: var(--radius-full); padding: 2px var(--space-3); font-size: var(--font-size-sm); }
/* 待辦回饋第五輪第 32 項／第六輪第 39-2 項：.pos-chip-row 容器本身跟上方
   中譯文字其實已經同左緣（實測 flex-start 沒問題）；使用者看到的「沒有完全
   靠左」其實是 .pos-chip 自己的藥丸造型左右 padding（var(--space-3)=12px）
   讓「文字」比純文字內縮一截。第五輪只把第一顆 chip（:first-child）往左
   拉，結果換行後第二行以後的 chip 沒有跟著對齊（第六輪截圖抓到：換行後的
   brittlest 沒對齊第一行）——flex-wrap 每一行的「第一格」都是各自那一行的
   起點，不是只有 DOM 上真正的 :first-child。改成整個容器（.meaning-pos-forms）
   往左位移，每一行的起點都共用同一個容器左緣，所有換行後的 chip 文字就會
   對齊。藥丸底色一樣會稍微超出容器左緣一點點，比文字沒切齊更不違和。不動
   .pos-chip 本身／renderPosFormsHtml 獨立區塊（詞庫詳情舊版介面用）沿用原樣。
   實際規則併到下面 .meaning-group 附近的 .meaning-pos-forms（跟 margin-top
   合併成一條），這裡只留說明避免重複宣告。 */
.meaning-line { margin-bottom: var(--space-1); }
.meaning-pos { color: var(--color-brand-600); font-weight: 600; margin-right: var(--space-2); }
/* 待辦回饋第六輪第 39-1 項：.example-item 改用 grid（欄1＝文字內容、欄2＝
   來源chip+發音鍵，欄2寬度固定 auto），讓英文句子（第1列）與下面中文翻譯
   （第2列）共用同一個「欄1」寬度與左緣——比原本中譯是獨立區塊、天生吃滿
   整行寬度（含發音鍵佔用的視覺右側空間）更準確，不需要靠量測或猜測 padding
   去湊。align-items:center 讓第1列（英文句子＋actions）垂直置中對齊；第2列
   （中譯）只有一個 grid item 不受影響。 */
.example-item {
  padding: var(--space-2) 0;
  border-top: 1px solid var(--color-border);
  display: grid;
  grid-template-columns: 1fr auto;
  column-gap: var(--space-2);
  align-items: center;
}
.example-item:first-child { border-top: none; padding-top: 0; }
.example-en { display: flex; align-items: flex-start; gap: var(--space-2); }
.example-zh { color: var(--color-text-muted); font-size: var(--font-size-sm); margin-top: 2px; }
.source-tag { font-size: 10px; padding: 1px 6px; border-radius: var(--radius-full); background: var(--color-muted-bg); color: var(--color-text-muted); flex: none; }
/* 待辦回饋第五輪第 32 項：來源 chip＋發音鍵一起改回英文例句「右側」（取消
   上一輪把來源 chip 移到下方獨立一行的做法），.example-en-content 現在只
   放句子本身，從容器左緣開始（不用再跟 tag 搶同一行空間）；tag／發音鍵包
   進 .example-en-actions，固定寬度貼右。
   第十一輪待辦⑦：actions 欄由左右並排改上下堆疊（來源 chip 在上、發音鍵在
   下），欄位變窄，讓 .example-en-content 的例句文字欄相應變寬。
   align-items:center 讓兩個子元素（寬度不同：chip 較窄、圓形發音鍵較寬）在
   這個窄欄內水平置中，不需要另外量測寬度寫死；grid 版面（見下方
   .example-en-aligned）裡這欄仍是 grid-row 1 的第 2 個 item，跟例句文字同一
   列，不影響中英左緣對齊（那是靠 grid-column:1 共用欄位達成，跟這欄寬度
   無關）。 */
.example-en-content { flex: 1; min-width: 0; }
.example-en-actions { display: flex; flex-direction: column; align-items: center; gap: var(--space-1); flex: none; }
.note-box { background: var(--color-warn-bg); border-radius: var(--radius-md); padding: var(--space-3); font-size: var(--font-size-sm); }
.examples-extra { margin-top: var(--space-1); }
.examples-extra[hidden] { display: none; }
/* 展開的第一句仍要跟上面已顯示的那句有分隔線——它是 .examples-extra 這個
   子容器裡的 :first-child，:first-child 選取器在自己的父層下重新計算，
   會被 .example-item:first-child 的「無上框線」規則誤命中，這裡補回來。 */
.examples-extra .example-item:first-child { border-top: 1px solid var(--color-border); padding-top: var(--space-2); }

/* -- 加練模式標記（快修包 C-4）：不影響 SRS 排程的提示 -- */
.practice-badge {
  display: inline-flex;
  align-self: flex-start;
  background: var(--color-warn-bg);
  color: var(--color-text);
  font-size: var(--font-size-xs);
  font-weight: 700;
  padding: 3px var(--space-3);
  border-radius: var(--radius-full);
  margin-bottom: var(--space-2);
}

/* -- 第十一輪待辦②：複習卡右滑「回看上一張」的唯讀回顧模式標記，跟
   .practice-badge 同一套視覺語言（沿用同一個 warn-bg 底色系），但語意不同
   （練習模式 vs. 回顧已作答的舊卡）所以獨立一個 class，方便日後各自調整。 -- */
.peek-badge {
  display: inline-flex;
  align-self: flex-start;
  background: var(--color-muted-bg);
  color: var(--color-text-muted);
  font-size: var(--font-size-xs);
  font-weight: 700;
  padding: 3px var(--space-3);
  border-radius: var(--radius-full);
  margin-bottom: var(--space-2);
}
.review-peek-hint { text-align: center; width: 100%; margin: 0; }

/* -- 第十一輪待辦⑧：複習完成畫面「今天複習過的字」區塊（低調，跟主要動作
   「再複習一輪／返回首頁」拉開距離，用上緣分隔線區隔）。 -- */
.reviewed-today-section {
  margin-top: var(--space-6);
  padding-top: var(--space-4);
  border-top: var(--border-width) solid var(--color-border);
}
.reviewed-today-list { margin-top: var(--space-2); }

/* -- Review answer buttons --
   第十五輪回饋第3項：使用者原話「下方不記得、記得留的空也稍微大了一點，
   可以再縮減」——縮小按鈕列本身的間距與按鈕高度（不動 .btn 共用規則，只在
   複習作答鈕這個情境覆寫），省下的高度直接讓上面的翻轉卡變大：
     gap        舊 var(--space-3)=12px → 新 var(--space-2)=8px
     btn 高度   舊 52px → 新 46px（仍高於全域 --touch-target=44px 最小觸控
                目標，沒有低於無障礙門檻）
   跟 .card-screen .review-actions 的上下 margin 縮減（見下方該規則的新舊
   值）合起來才是「省下的空間」全貌——這裡只管按鈕本身，外距在下面那條。 */
.review-actions { display: flex; gap: var(--space-2); }
.review-actions .btn { min-height: 46px; font-size: var(--font-size-lg); }
.speak-btn { background: var(--color-brand-50); color: var(--color-brand-600); border-radius: var(--radius-full); width: 40px; height: 40px; border: none; flex: none; display: inline-flex; align-items: center; justify-content: center; }

/* -- Wordbank list -- */
.wordbank-item { display: flex; align-items: center; gap: var(--space-3); padding: var(--space-3) 0; border-bottom: 1px solid var(--color-border); cursor: pointer; }
.wordbank-item:last-child { border-bottom: none; }
.wordbank-item-main { flex: 1; min-width: 0; display: block; }
.wordbank-item .headword-text { font-size: var(--font-size-lg); }
/* 待辦回饋第 18 條：長片語（如 "learn (something) at one's mother's knee"）
   headword 逐字元斷行修正——一般字詞在空白處斷行即可，只有真的一整個超長
   單字（沒有空白）才允許在字內斷開，避免撐破版面。 */
.headword-text-browse {
  display: block;
  width: 100%;
  word-break: normal;
  overflow-wrap: break-word;
  white-space: normal;
}
/* 旗標 chips（草稿/片語/AWL/牛津/BSL 等）獨立一行，不跟 headword 文字擠在
   同一行內聯——避免長片語＋多個 badge 一起把欄寬撐爆而觸發逐字斷行。 */
.headword-flags { display: flex; flex-wrap: wrap; gap: var(--space-1); margin-top: var(--space-1); }
.wordbank-meaning-preview { color: var(--color-text-muted); font-size: var(--font-size-sm); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.hint-text { color: var(--color-text-faint); font-size: var(--font-size-xs); margin-bottom: var(--space-4); }
.section-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: var(--space-3); }

/* -- Exam -- */
.progress-bar { height: 6px; border-radius: var(--radius-full); background: var(--color-muted-bg); overflow: hidden; margin-bottom: var(--space-5); }
.progress-bar-fill { height: 100%; background: var(--color-brand-600); transition: width var(--transition-base); }
.choice-btn { display: block; width: 100%; text-align: left; padding: var(--space-4); border-radius: var(--radius-md); border: var(--border-width) solid var(--color-border-strong); background: var(--color-surface); margin-bottom: var(--space-3); min-height: var(--touch-target); }
.choice-btn.is-selected { border-color: var(--color-brand-600); background: var(--color-brand-50); }
.choice-btn.is-correct { border-color: var(--color-success-500); background: var(--color-success-bg); }
.choice-btn.is-wrong { border-color: var(--color-danger-500); background: var(--color-danger-bg); }
.cloze-sentence { font-size: var(--font-size-lg); margin-bottom: var(--space-5); line-height: 1.8; }
/* R14 §2-1：漏字底線只留 border-bottom 這一條（exam-question.js 已把塞進
   span 裡的字元從「______」改成不可見的 nbsp，避免文字底線＋border-bottom
   疊成兩條線）。 */
.cloze-blank { border-bottom: 2px solid var(--color-brand-500); padding: 0 var(--space-2); font-weight: 700; }
.listen-play-btn { width: 96px; height: 96px; border-radius: var(--radius-full); background: var(--color-brand-600); color: #fff; border: none; display: flex; align-items: center; justify-content: center; margin: var(--space-6) auto; }
.listen-play-btn svg { width: 36px; height: 36px; fill: #fff; }
.wrong-answer { color: var(--color-danger-600); }
.wrong-correct { color: var(--color-success-600); }

/* -- R15：紅筆改考卷分數改「被寫出來」動畫 ----------------------------------
   數字與底線都改進同一個 <svg>，用 stroke-dasharray/stroke-dashoffset 做
   「被紅筆一筆一筆寫出來」的描邊動畫：<text> 套 stroke-dasharray 是瀏覽器
   既有行為（每個字元 glyph 各自重新起算 dash pattern，不是整串文字共用一條
   路徑），不需要 JS 量測路徑長度（<text> 沒有 getTotalLength()，<path> 才有）
   就能穩定動出效果——dasharray 300／260 是刻意抓比實際字元/底線路徑長度
   更大的安全值：dashoffset 從同值動到 0 時，可見的 dash 段長度＝min(已走過
   距離, 實際路徑長度)，安全值夠大保證最後一定完整覆蓋整條路徑、不會半途
   斷在還沒畫完的地方。動畫尾聲 fill-opacity 補一段 0→1，讓數字從「描邊」
   收斂成「實心紅字」（原本設計就是實心手寫體，這裡是動畫終態跟舊版對齊，
   不是新增視覺）。
   顏色沿用 --color-danger-500/600——這兩個 token 本來就依四組主題×明暗個別
   調過對比（見 app.css 深色模式 tokens 段），這裡不用另外處理深色主題對比。
   reduced-motion：完全吃 app.css 既有全域規則（*,*::before,*::after 的
   animation-duration 強制 0.01ms !important），本檔每個動畫都帶 forwards
   fill-mode，0.01ms 幾乎瞬間跑完停在終態＝直接看到靜態最終畫面，不需要在
   這裡另外寫一份 @media (prefers-reduced-motion) 覆寫。 */
.exam-score-mark { width: fit-content; margin: var(--space-6) auto 0; text-align: center; }
.exam-score-svg { display: block; width: 220px; height: 132px; margin: 0 auto; overflow: visible; }
.exam-score-svg-num {
  font-family: "Snell Roundhand", "Bradley Hand", "Noteworthy", "Comic Sans MS", cursive;
  font-size: 74px;
  font-weight: 700;
  fill: var(--color-danger-600);
  fill-opacity: 0;
  stroke: var(--color-danger-600);
  stroke-width: 2.5;
  stroke-linejoin: round;
  stroke-dasharray: 300;
  stroke-dashoffset: 300;
  transform: rotate(-4deg);
  transform-origin: 100px 60px;
  animation: exam-score-draw 0.8s ease-out forwards, exam-score-fill 0.8s ease-out forwards;
}
.exam-score-underline-path {
  fill: none;
  stroke: var(--color-danger-500);
  stroke-width: 5;
  stroke-linecap: round;
  stroke-dasharray: 260;
  stroke-dashoffset: 260;
  animation: exam-score-draw 0.4s ease-out 0.7s forwards;
}
@keyframes exam-score-draw {
  to { stroke-dashoffset: 0; }
}
@keyframes exam-score-fill {
  0%, 65% { fill-opacity: 0; }
  100% { fill-opacity: 1; }
}
/* -- R15：評語改蓋章樣式＋蓋下動畫（紅框感＋落地頓一下）。延遲 1.05s 起跳，
   等分數描邊（0.8s）＋底線（0.7s 起 0.4s＝1.1s 結束）大致寫完才蓋章，模擬
   「先寫完分數才蓋評語章」的先後順序。animation-fill-mode:both 讓延遲期間
   維持 0% 那幀（縮到很大、透明），不會在等待時先閃一下終態。 -- */
.exam-score-comment { width: fit-content; margin: var(--space-3) auto var(--space-5); text-align: center; }
.exam-score-comment-text {
  display: inline-block;
  padding: var(--space-2) var(--space-5);
  border: 3px solid var(--color-danger-600);
  border-radius: var(--radius-md);
  background: var(--color-danger-bg);
  color: var(--color-danger-600);
  font-size: var(--font-size-lg);
  font-weight: 800;
  letter-spacing: 0.02em;
  position: relative;
  animation: exam-stamp-drop 0.55s cubic-bezier(0.22, 0.61, 0.36, 1) 1.05s both;
}
.exam-score-comment-text::after {
  content: "";
  position: absolute;
  inset: 3px;
  border: 1px solid var(--color-danger-600);
  border-radius: calc(var(--radius-md) - 3px);
  opacity: 0.6;
  pointer-events: none;
}
@keyframes exam-stamp-drop {
  0% { transform: scale(2.6) rotate(3deg); opacity: 0; }
  60% { transform: scale(0.94) rotate(-8deg); opacity: 1; }
  80% { transform: scale(1.06) rotate(-5deg); }
  100% { transform: scale(1) rotate(-6deg); }
}

/* -- R14 §2-2：各題綠勾／紅叉列表（結果頁）與 §2-3 題目解析頁共用的列樣式 -- */
.exam-q-list { margin-bottom: var(--space-5); }
.exam-q-row,
.exam-explain-row {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-3) 0;
  border-bottom: 1px solid var(--color-border);
  cursor: pointer;
}
.exam-q-row:last-child,
.exam-explain-row:last-child { border-bottom: none; }
.exam-q-body { flex: 1; min-width: 0; }
.exam-q-detail { margin-top: var(--space-1); font-size: var(--font-size-sm); }
.exam-q-mark { flex: none; width: 24px; height: 24px; display: inline-flex; align-items: center; justify-content: center; }
.exam-q-mark svg { width: 20px; height: 20px; }
.exam-q-row.is-correct .exam-q-mark,
.exam-explain-row.is-correct .exam-q-mark { color: var(--color-success-500); }
.exam-q-row.is-wrong .exam-q-mark,
.exam-explain-row.is-wrong .exam-q-mark { color: var(--color-danger-500); }

/* -- R14 §2-3 題目解析頁：每列不像 .exam-q-row 那樣整列可點（列內有播放發音
   按鈕），改成獨立區塊排版，不用 flex 橫排。 -- */
.exam-explain-row { display: block; }
.exam-explain-row .hint-text { display: flex; align-items: center; gap: var(--space-2); margin-bottom: var(--space-2); }
.exam-explain-prompt { margin-bottom: var(--space-2); font-size: var(--font-size-lg); }
.exam-explain-play { width: 36px; height: 36px; display: inline-flex; align-items: center; justify-content: center; background: var(--color-brand-600); border-radius: var(--radius-full); border: none; padding: 0; vertical-align: middle; }
.exam-explain-play svg { width: 16px; height: 16px; fill: #fff; }

/* -- R14 §2-4：本週最常錯單字列表 -- */
.exam-weekly-list { list-style: none; margin: 0; padding: 0; }
.exam-weekly-item { display: flex; align-items: center; justify-content: space-between; padding: var(--space-3) 0; border-bottom: 1px solid var(--color-border); cursor: pointer; }
.exam-weekly-item:last-child { border-bottom: none; }
.exam-weekly-count { color: var(--color-text-muted); font-size: var(--font-size-sm); }

/* 第十六輪回饋③：結果頁「本週最常錯」跟上面返回/題目解析按鈕列太接近，
   小標也不夠明顯——這裡跟按鈕列拉開 24px（--space-6）級距；小標比照
   .pos-subhead 的短橫線裝飾手法，但字重/字級/顏色更強（粗體＋品牌色），
   確保比「字義」「例句」那組小標更搶眼一階。 */
.exam-weekly-section { margin-top: var(--space-6); }
.exam-weekly-title {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--font-size-sm);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--color-brand-600);
  margin-bottom: var(--space-2);
}
.exam-weekly-title::before {
  content: "";
  display: inline-block;
  width: 18px;
  height: 2px;
  background: var(--color-brand-500);
  flex: none;
}

/* -- R14 §2-3：結果頁（含題目解析子頁）移除 review.js 外層的「複習｜翻卡
   複習｜小考」模式切換列。#review-header 是 review.js 掛的節點，這裡不動
   review.js 本身，靠 exam.js 切 body class 來控制顯示（見 exam.js
   setResultHeaderHidden 說明）。 -- */
body.exam-result-active #review-header { display: none; }

/* -- Placeholder（M5b 開發中） -- */
.placeholder-box {
  text-align: center;
  padding: var(--space-10) var(--space-4);
  color: var(--color-text-muted);
}

/* -- 功能包：韋氏英釋並列（見 card-render.js renderMwDefHtml）
   待辦回饋第六輪第 38 條：原本字級 --font-size-sm（0.875rem）偏小，加大一級到
   --font-size-base（1rem），跟中譯內文（.meaning-zh-line 沒有另外設字級、
   吃 body 預設 --font-size-base）同級；行高原本吃 body 的 --line-height-base
   （1.6），字級加大後行距略微拉開到 1.7 讓多行釋義好讀一點。 -- */
.mw-def-block { margin-top: var(--space-2); font-size: var(--font-size-base); line-height: 1.7; color: var(--color-text-muted); }
.mw-def-group { margin-bottom: var(--space-2); }
.mw-def-group:last-child { margin-bottom: 0; }
.mw-def-line { margin-top: 2px; }

/* -- 功能包：相似字/反義字/相關片語 chips -- */
.related-group { margin-bottom: var(--space-3); }
.related-group:last-child { margin-bottom: 0; }
.related-group-label { font-size: var(--font-size-xs); color: var(--color-text-faint); margin-bottom: var(--space-1); }
.related-chip-row { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.related-chip {
  min-height: 32px;
  padding: 0 var(--space-3);
  border-radius: var(--radius-full);
  border: var(--border-width) solid var(--color-border-strong);
  background: var(--color-surface);
  font-size: var(--font-size-sm);
  color: var(--color-text);
}
.related-chip-syn { border-color: var(--color-success-500); color: var(--color-success-600); }
.related-chip-ant { border-color: var(--color-danger-500); color: var(--color-danger-600); }
.related-chip-phrase { border-color: var(--color-brand-500); color: var(--color-brand-600); }

/* 快修包項目B：相似字/反義字 chip 上「已在詞庫」低調小圓點標註，避免重複
   把同一個字加兩次。刻意不用文字（chip 空間有限），顏色沿用 success 色階但
   比 syn 邊框更飽和一點，跟一般 chip 邊框有可辨識的差異。 */
.related-chip.is-known { border-style: dashed; }
.known-dot {
  display: inline-block;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--color-success-500);
  margin-left: var(--space-1);
  vertical-align: middle;
}

/* -- 功能包：迷你卡（點相關詞 chip 彈出的即查小卡，見 mini-word-card.js）--
   疊在既有 .sheet-overlay（z-index 60）之上，因為常常是從詳情頁的 sheet 裡
   點 chip 觸發的。 */
.mini-card-overlay {
  position: fixed;
  inset: 0;
  background: var(--color-overlay);
  z-index: 80;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-5);
}
.mini-card-panel {
  background: var(--color-surface-raised);
  border-radius: var(--radius-lg);
  border: var(--border-width) solid var(--color-border);
  box-shadow: var(--shadow-lg);
  padding: var(--space-5);
  width: 100%;
  max-width: 320px;
  max-height: 80dvh;
  overflow-y: auto;
}
.mini-card-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: var(--space-3); }
/* 快修包項目C：中譯預覽放在英文釋義上方，字級跟一般 hint-text 一樣但顏色
   深一點（比純提示文字重要，是使用者最想先看到的資訊），跟後面的 KK/英釋
   留一點呼吸空間。 */
.mini-zh-preview { color: var(--color-text); margin: 0 0 var(--space-2); }
/* 快修包項目B：迷你卡開啟時如果這個字已經在詞庫，開頭就提醒一行，避免使用者
   看完介紹才發現白按了「產生完整卡片」。 */
.mini-known-note { color: var(--color-success-600); margin: 0 0 var(--space-2); }

/* -- 待辦回饋第 3 條：選字背「加入 N 字」浮動固定在畫面底部，避開 tab bar -- */
.bb-add-btn-fixed {
  position: fixed;
  left: 50%;
  bottom: calc(var(--tabbar-height) + var(--space-3));
  transform: translateX(-50%);
  width: calc(100% - var(--space-8));
  max-width: calc(var(--app-max-width) - var(--space-8));
  z-index: 45;
  box-shadow: var(--shadow-lg);
}
/* .btn 在 app.css 設了 display: inline-flex（author 樣式），瀏覽器內建的
   `[hidden] { display: none }` 是 UA 樣式，cascade 優先序輸給任何 author
   樣式（不論先後／specificity），所以只設 hidden 屬性不夠，這裡要明講
   display:none 才會真的隱藏（第七輪回饋任務1）。 */
.bb-add-btn-fixed[hidden] { display: none; }
/* 按鈕改浮動後，清單底部留一塊等高空間，避免最後幾筆被蓋住 -- */
/* 第七輪回饋任務1：按鈕現在只在有勾選時才顯示（見 builtin-browse.js
   updateAddBtn），沒勾選時按鈕 hidden、這裡的留白也一起收掉，避免清單
   平白多出一塊空白；勾了第一個字，updateAddBtn 幫這個 spacer 加上
   .is-visible，留白才出現，讓捲到最底時最後一列不會被浮鈕蓋住。 */
.bb-add-btn-spacer { height: 0; transition: height var(--transition-fast, 0.15s); }
.bb-add-btn-spacer.is-visible { height: calc(var(--touch-target) + var(--space-6)); }

/* -- 功能包：進階字庫擴充包（AWL/Oxford/BSL）彩色小標，見 builtin-browse.js -- */
.tag-badge { display: inline-flex; align-items: center; padding: 1px var(--space-2); border-radius: var(--radius-full); font-size: 10px; font-weight: 700; margin-left: var(--space-1); }
.tag-badge-awl { background: var(--color-brand-100); color: var(--color-brand-700); }
.tag-badge-oxford_b2, .tag-badge-oxford_c1 { background: var(--color-warn-bg); color: var(--color-warn-500); }
.tag-badge-bsl { background: var(--color-danger-bg); color: var(--color-danger-600); }

/* -- Modal / sheet（詳情、待確認流程用全螢幕覆蓋） -- */
.sheet-overlay {
  position: fixed;
  inset: 0;
  background: var(--color-overlay);
  z-index: 60;
  display: flex;
  align-items: flex-end;
}
.sheet-panel {
  background: var(--color-bg);
  width: 100%;
  max-width: var(--app-max-width);
  margin: 0 auto;
  max-height: 92dvh;
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  overflow-y: auto;
  padding: var(--space-5);
  padding-bottom: calc(var(--space-8) + env(safe-area-inset-bottom));
  /* 待辦回饋第五輪第 35 項：面板捲到頂/底再繼續拖曳（rubber-band）時，擋住
     捲動鏈接到背景文件——iOS Safari 16+ 才支援，舊版本不生效，所以
     card-sheet.js 另外還做了 body 鎖定當第二層保險（見該檔案註記）。 */
  overscroll-behavior: contain;
  /* 第十二輪待辦⑪：跟手下滑關閉手勢用 JS 直接操作 transform/opacity/transition
     （見 card-sheet.js），will-change 只是效能提示，不影響版面。 */
  will-change: transform, opacity;
}
.sheet-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: var(--space-4); }
/* 第四輪回饋第 1 條：標題文字現在多半是空字串（headword 已改由卡片內容裡的
   粗體大字負責，見 card-sheet.js resolveSheetTitle），h2 仍保留在 DOM 裡是為
   了讓 justify-content:space-between 在「有/沒有標題文字」兩種情況下都維持
   叉叉固定在右側，不必為了有沒有文字改版面。 */
.sheet-title { margin: 0; font-size: 1.125rem; font-weight: 700; min-width: 0; }
/* 待辦回饋第五輪第 32 項：headword 沒被自訂標題取代時，直接把 headword 搬到
   標題列跟叉叉同一列（.sheet-header 本來就是 flex＋space-between＋
   align-items:center，天生就會把這兩者垂直置中對齊、左右分佔兩端），取代
   內容區另外重畫一次 headword 中間空一大截的排法；只需要放大字級／字重蓋
   過預設的 .sheet-title 樣式即可，不需要另外調整 flex 排列。 */
.sheet-title-headword {
  font-size: var(--font-size-2xl);
  font-weight: var(--font-weight-heading);
  word-break: break-word;
  overflow-wrap: break-word;
}
/* 第四輪回饋第 2 條：叉叉在標題列偏上——實測是 .btn-icon 預設 inline-block、
   裡面的 SVG 是 inline 元素，baseline 對齊會讓圖示本身在圓形按鈕裡偏上；
   只在 sheet-header 這個情境內把按鈕本身也改成置中 flex，不動全域 .btn-icon
   （其他地方也在用，見 mini-word-card.js／youglish.js／batch-paste.js）。 */
.sheet-header .btn-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  align-self: center;
  flex: none;
}
/* 第四輪回饋第 3 條：旗標 chips 這一行跟下面 back-section 內容共用同一個
   左緣——容器本身不額外加左邊距／縮排，跟 .card-header-word／.back-section
   一樣都是 data-content 底下貼齊左邊的區塊層級元素。 */
.detail-flag-row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); margin: var(--space-2) 0; }

/* 第十六輪回饋②：「立即精譯」鈕跟旗標 chip／英釋暫代徽章併進同一列
   （.detail-flag-row）後，改用這個樣式取代原本的 .btn-ghost——.btn-ghost
   border-color 是透明的，混在旗標 chip 之間不容易看出「這是一顆按鈕」；
   這裡明確給邊框＋品牌色文字，維持跟 .chip／.pos-chip 同高度的藥丸感但
   保留按鈕語意（有邊框、可點擊感明顯）。 */
.btn-retranslate {
  background: var(--color-surface);
  color: var(--color-brand-600);
  border-color: var(--color-brand-500);
}

/* -- 詞庫頁分類 chips（待辦回饋2）：「影片」分類第三階段才開放，disabled 狀態
   需要視覺上明顯不可點，不動 app.css 既有 .chip 規則，疊加新 class -- */
.wb-category-chip:disabled {
  opacity: 0.45;
  cursor: not-allowed;
  filter: grayscale(60%);
}

/* -- 待辦回饋第四輪：待確認清單併入「待確認」分類 chip，數量顯示在 chip 上。
   本專案雙主題明暗由 [data-appearance] 屬性決定、不吃系統 prefers-color-scheme
   （見 app.css 開頭說明，這裡不重複加那個已被拿掉的機制）；深色兩套主題
   （book/doodle）背景色不同，用 rgba(255,255,255,.2) 疊加在 .chip 既有底色上
   即可維持可讀對比，不必分別對 book/doodle 兩色系各寫一份。 -- */
.wb-chip-count {
  display: inline-block;
  margin-left: var(--space-1, 4px);
  padding: 0 6px;
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.12);
  font-size: 0.8em;
  line-height: 1.4;
}
:root[data-appearance="dark"] .wb-chip-count {
  background: rgba(255, 255, 255, 0.2);
}

/* -- 第31項／第六輪第42項：詞庫頁版面重構，入口按鈕排（手動加字/直接選字/
   主題選字）三個並列。第31項當時用 flex-wrap:wrap 讓 390px 螢幕不溢出，但
   使用者實測發現三顆會直接換成三行、不好看；第42項改成強制單行：縮小字級與
   左右 padding（四個中文字在 0.8125rem＋8px padding 下量測可放進 390px 三等
   分寬度）、gap 縮小、加 min-width:0 讓 flex 子項可以正常壓縮不溢出容器。不動
   .btn-row 既有規則，只在 .wb-entry-row 底下覆寫，避免影響其他用到 .btn-row
   的地方（例如待確認清單的全部入庫/全部刪除）。另外跟上方分類 chips 之間用
   margin-top＋一條淡分隔線做出低調的視覺區隔（使用者要求簡潔、不要太搶眼）。 -- */
.wb-entry-row {
  flex-wrap: nowrap;
  gap: var(--space-2);
  margin-top: var(--space-4);
  padding-top: var(--space-3);
  border-top: var(--border-width) solid var(--color-border);
}
.wb-entry-row .btn {
  flex: 1 1 0;
  min-width: 0;
  padding: 0 var(--space-1, 4px);
  font-size: 0.8125rem;
  white-space: nowrap;
}

/* -- 待辦回饋第 5 條：相似字/反義字/相關片語 chip 旁的小發音鍵 -- */
.related-chip-item {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
}
.speak-btn-sm {
  width: 28px;
  height: 28px;
}
.speak-btn-sm svg {
  width: 14px;
  height: 14px;
}

/* -- 定稿版逐詞性字義段＋集中例句段（card-render.js buildPosSectionsHtml，
   複習卡背面／詞庫詳情／迷你卡三處共用；2026-07-10 下午取代 R14 §4-3
   「例句掛在對應詞性區塊內」的排法，使用者已看過 supplement 示意圖點頭）。
   每個詞性各自一個區塊，依序：詞性標籤（.pos-block-header，第十六輪回饋①
   改純英文縮寫如「n.」，不再顯示中文「名詞」——集中例句段的分組小字
   「n. 名詞例句」不受影響仍是中英並列，見 card-render.js posAbbrHtml 註解）
   → 該詞性詞性變化緊貼其下（.pos-block-forms，一行小字）→「字義」小標
   （.pos-subhead）→ 韋氏英釋分行（沿用既有 .mw-def-line）→ 中譯（緊貼英釋
   正下方、弱化綠色系，.pos-block-zh）。區塊之間用上緣分隔線區隔，第一個
   不用（跟 .example-item 同套做法）。例句移出各詞性區塊，改在全部詞性講完
   後集中一段，見下方「集中例句段」。 -- */
.pos-blocks { margin-bottom: var(--space-3); }
.pos-block {
  padding-top: var(--space-3);
  border-top: 1px solid var(--color-border);
  margin-bottom: var(--space-3);
}
.pos-block:first-child {
  padding-top: 0;
  border-top: none;
}
.pos-block:last-child { margin-bottom: 0; }
/* 第十六輪回饋①：詞性標籤原本粗體＋品牌色，使用者反應「一直被它吸走注意力，
   畫面不平衡」——字重降級（拿掉粗體）、字級降一階（sm→xs）、改用弱化色
   （--color-text-faint，跟 .pos-subhead／.back-section-title 同一套弱化色），
   讓字義內容（英釋／中譯）成為視覺主角，詞性標籤退居次要提示。 */
.pos-block-header {
  color: var(--color-text-faint);
  font-weight: 500;
  font-size: var(--font-size-xs);
  margin-bottom: var(--space-1);
}
/* 詞性變化小字（如「複數 supplements｜過去式 supplemented」）緊貼詞性標籤
   下方，字級比詞性標籤本身再小一階、顏色弱化，跟下面的「字義」小標拉開
   視覺層次。 */
.pos-block-forms {
  color: var(--color-text-faint);
  font-size: var(--font-size-xs);
  margin-bottom: var(--space-2);
}
.pos-block .mw-def-block { margin-top: 0; }
/* 中譯緊貼韋氏英釋正下方、弱化綠色系（語意色跟「記得」按鈕同一組
   --color-success-*，但降不透明度弱化，不要跟按鈕一樣搶眼）。 */
.pos-block-zh {
  color: var(--color-success-600);
  opacity: 0.85;
  font-size: var(--font-size-sm);
  margin-top: var(--space-1);
}

/* 「字義」／「例句」小標共用樣式：小型大寫間距＋短橫線裝飾（定稿示意圖
   樣式）。「字義」小標每個詞性區塊各自一個；「例句」小標整張卡只有一個，
   放在集中例句段最前面。 */
.pos-subhead {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--font-size-xs);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--color-text-faint);
  margin: var(--space-2) 0 var(--space-1);
}
.pos-subhead::before {
  content: "";
  display: inline-block;
  width: 14px;
  height: 1px;
  background: var(--color-border-strong);
  flex: none;
}

/* -- 集中例句段：全部詞性字義講完後，按詞性分組列出（見 card-render.js
   renderExampleGroupsHtml）。同詞性例句之間不加分隔線——蓋掉 .example-item
   預設的上緣分隔線，只在 .pos-examples-group 這個新容器範圍內生效，不改動
   .example-item 本身的選擇器／規則。不同詞性組之間用虛線分隔
   （.pos-examples-group-dashed，第一組不用）；只有一組時（目前資料管線下
   examples 永遠只會歸到單一詞性，見 buildPosSectionsHtml 開發註解）不顯示
   組別小字，也就不會出現虛線。 -- */
.pos-examples-groups { margin-bottom: var(--space-3); }
.pos-examples-group { margin-bottom: var(--space-2); }
.pos-examples-group:last-child { margin-bottom: 0; }
.pos-examples-group-dashed {
  border-top: 1px dashed var(--color-border-strong);
  padding-top: var(--space-2);
  margin-top: var(--space-2);
}
.pos-examples-group-label {
  color: var(--color-brand-600);
  font-weight: 600;
  font-size: var(--font-size-xs);
  margin-bottom: var(--space-1);
}
.pos-examples-group .example-item { border-top: none; padding-top: var(--space-2); }
.pos-examples-group .example-item:first-child { padding-top: 0; }

/* -- 待辦回饋第 8 條：同詞性意思合併＋詞性變化就近對照（renderMeaningHtml）--
   不動舊版 .meaning-line／.meaning-pos（renderPosFormsHtml 獨立區塊仍在用），
   新結構改用 .meaning-group 包住同一詞性的所有意思＋（若有）該詞性的變化。 */
.meaning-group { margin-bottom: var(--space-3); }
.meaning-group:last-child { margin-bottom: 0; }
.meaning-zh-list { margin-top: 2px; }
.meaning-zh-line { margin-bottom: 2px; }
.meaning-zh-line:last-child { margin-bottom: 0; }
/* margin-left negative offset 說明見上方 .pos-chip-row 附近第六輪第 39-2 項
   的長註解——整個容器往左位移，讓每一行換行後的第一顆 chip 都對齊左緣。 */
.meaning-pos-forms { margin-top: var(--space-1); margin-left: calc(-1 * var(--space-3)); }
.meaning-forms-leftover-title { margin-top: var(--space-2); }

/* -- 待辦回饋第 19 條：例句英文垂直置中對齊發音鍵。
   待辦回饋第五輪第 32 項：發音鍵／來源 chip 改到右側後，.example-en-content
   （英文句子）跟下面 .example-zh（中譯）曾經天生都貼容器左緣，一度以為不需
   要再位移——但第六輪截圖證實中譯仍比英文句子寬（因為英文句子那一列還要
   跟 actions 共用寬度，中譯那一列卻獨立吃滿整個 .example-item 寬度）。
   第六輪第 39-1 項改法：.example-item 本身改成 2 欄 grid（見上方
   .example-item 規則），.example-en-aligned 用 display:contents 讓它的兩個
   子元素（.example-en-content／.example-en-actions）直接變成 grid 的第 1 列
   兩個 item；.example-zh-aligned 明確放到欄 1、第 2 列。這樣英文句子與中譯
   永遠共用同一欄寬度／左右緣，不必再靠 padding 湊數字。 -- */
.example-en-aligned {
  display: contents;
}
.example-en-aligned .example-en-content {
  grid-column: 1;
  grid-row: 1;
}
.example-en-aligned .example-en-actions {
  grid-column: 2;
  grid-row: 1;
}
.example-zh-aligned {
  grid-column: 1;
  grid-row: 2;
  padding-left: 0;
}

/* -- 待辦回饋第四輪：迷你卡（mini-word-card.js）相似字/反義字 chips 文字偏上緣
   修正。根因：正式卡片（card-render.js renderRelatedHtml）的 chip 是
   <button>，瀏覽器對 button 有內建的內容置中行為；迷你卡的 chip 是純
   <span>（不可點擊、pointer-events:none），span 沒有這個內建行為，在
   .related-chip-row（display:flex）裡被撐到 min-height 32px 後，文字仍停在
   自己 box 的頂端。只加這個新 class 疊在共用 .related-chip 上（不改共用
   selector 本身），讓 span 自己也變成 flex 容器、把內文垂直置中。 -- */
.mini-related-chip {
  display: inline-flex;
  align-items: center;
}

/* -- 待辦回饋第四輪第28項：分頁重構 --
   1) 詞庫「直接選字」「主題選字」子頁的返回鍵＋標題列——跟現有 .section-header
      語意不同（那個是 space-between 左右分散兩顆按鈕），這裡要返回鍵和標題
      同一側、貼在一起，所以另開新 class，不動 .section-header。
   2) 「複習」頁合併翻卡／小考模式後，翻卡模式的 .card-screen 不再是最外層
      畫面（上面多了複習頁自己的標題＋模式切換鈕），原本 min-height:
      calc(100dvh - tabbar 高度) 的算法沒扣掉這段新增高度，卡片畫面會被
      推出一屏之外要捲動。用 review.js 動態量測寫入的 --review-header-extra
      CSS 變數扣掉這段高度（沒設定時預設 0，不影響其他還在用 .card-screen
      的地方，例如加練模式）。
      第十五輪回饋第2/3項：公式本身（100dvh − tabbar − --review-header-extra
      − --space-8）不用改——review.js 拿掉模式切換 chip-row 後，
      --review-header-extra 這個動態量測值自動變小（原本量到「標題列＋
      chip-row」的高度，現在只量到「標題列」，實測約少 40-56px，視主題
      字級／是否換行而定），卡片可用高度直接吃到這段差額，不需要另外調整
      這裡的算式。 */
.select-page-header {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
}
#review-mode-slot .card-screen {
  /* 注意：下方第六輪新增的同選擇器 height 規則有扣 --space-8（.view 底部
     padding），這條 min-height 也必須同步扣——min-height 沒扣的話瀏覽器取
     max(height, min-height)，整頁仍會多出 32px 捲動（verifier 抓到的回歸）。 */
  min-height: calc(100dvh - var(--tabbar-height) - var(--review-header-extra, 0px) - var(--space-8));
}

/* -- 待辦回饋第33條（直接選字三個補強，任務2：隱藏單字）--
   toast 帶「復原」按鈕的變體。基底 .toast 在 app.css（不可動），這裡疊加
   排版；容器 #toast-container 是 pointer-events:none（純資訊 toast 不擋
   點擊），這個變體要能點按鈕，整條 toast 開 pointer-events:auto。 */
.toast-with-action {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  pointer-events: auto;
}
.toast-action-btn {
  flex: none;
  background: transparent;
  border: none;
  color: var(--color-brand-300);
  font-weight: 600;
  font-size: var(--font-size-sm);
  padding: 0;
  cursor: pointer;
}

/* 任務2：選字背每列的隱藏（刪除）鈕——低調、置於列尾，不搶焦點。 */
.bb-hide-btn {
  flex: none;
  background: transparent;
  border: none;
  color: var(--color-text-muted, #8a8a8a);
  font-size: 1.1rem;
  line-height: 1;
  padding: var(--space-2);
  cursor: pointer;
}
.bb-hide-btn:active {
  color: var(--color-danger-600, #c0392b);
}

/* 選字背清單同步顯示的簡短中譯（ecdict 來源，查無則不顯示，不硬湊資料）。 */
.bb-zh-preview {
  color: var(--color-text-muted, #8a8a8a);
}

/* 任務3／第十輪待辦⑤：一鍵回頂浮動按鈕。原本只有選字背 .bb-scroll-top-btn，
   詞庫頁（wordbank.js）也要同一顆元件，改共用 .scroll-top-btn；選字背多一顆
   浮動的「加入 N 字」按鈕（.bb-add-btn-fixed 底部 = tabbar-height + space-3，
   滿版寬度、高度 = --touch-target），回頂鈕要再往上位移一段避開，用
   .scroll-top-btn--raised 修飾類別處理；詞庫頁沒有那顆按鈕，用基本款
   （貼在 tabbar 正上方）即可。 */
.scroll-top-btn {
  position: fixed;
  right: var(--space-4);
  bottom: calc(var(--tabbar-height) + var(--space-3));
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: none;
  background: var(--color-brand-600);
  color: #fff;
  font-size: 1.2rem;
  box-shadow: var(--shadow-lg);
  z-index: 50;
  display: none;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}
.scroll-top-btn.is-visible {
  display: flex;
}
.scroll-top-btn--raised {
  bottom: calc(var(--tabbar-height) + var(--space-3) + var(--touch-target) + var(--space-3));
}

/* -- 待辦回饋第六輪第43/44項：複習流程整合＋翻卡一頁式固定版面 --
   43) 設定畫面（模式單選＋範圍複選＋張數）沿用既有 .field/.chip-row/.chip，
       不必新增樣式；進行中畫面頂端「今日待複習 N」倒數＋（隨機模式才有的）
       「不影響排程」badge 並排一行，長標籤或窄螢幕時允許換行。
   44) .card-screen 原本只有 min-height（見上方原始規則），理論上讓
       flip-face 自己的 overflow-y:auto 撐滿捲動，但巢狀 flex 鏈在部分裝置上
       （尤其 dvh 隨網址列增減、或卡片內容特別長時）會讓整個頁面而不是卡片
       本身捲動，導致操作鍵被推到畫面最底、版面高度也跟著不固定。這裡疊加
       明確的 height（同一組運算式，含 --review-header-extra）＋
       overflow:hidden，強制任何溢出只能發生在 .flip-face 內部——後面的規則
       跟前面的 .card-screen／#review-mode-slot .card-screen 選擇器特異度相同，
       依 CSS 來源順序覆蓋，等於是「新增規則去覆寫」而不是改動原本那行。 */
.review-session-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-2);
  /* 第十五輪回饋第3項：字卡盡可能大——這一列（倒數／位置/badge）跟下面的
     .review-actions 一樣是 .card-screen 內 flex:none 的固定高度區塊，縮小它
     的下緣留白也直接讓 flex:1 的 .card-slot 多吃到一點高度。舊 var(--space-4)
     =16px → 新 var(--space-3)=12px。 */
  margin-bottom: var(--space-3);
}
.review-countdown { margin-bottom: 0; }
/* 第十二輪待辦③：「第 X 張／共 Y 張」計數，跟倒數同一行，次要資訊淡一點。 */
.review-position { margin-bottom: 0; color: var(--color-text-faint); }
.card-screen {
  height: calc(100dvh - var(--tabbar-height));
  overflow: hidden;
}
#review-mode-slot .card-screen {
  /* 再扣掉外層 .view 的底部 padding（app.css .view 的 --space-8），否則
     整頁會多出等量的可捲動高度（第六輪驗收 FAIL 修正：876px vs 844px）。 */
  height: calc(100dvh - var(--tabbar-height) - var(--review-header-extra, 0px) - var(--space-8));
}
.card-screen .flip-face {
  overscroll-behavior: contain;
}
/* 第十五輪回饋第3項：作答鈕列的上下留白縮減——
     margin-top    舊 var(--space-4)=16px → 新 var(--space-2)=8px
     margin-bottom 舊 var(--space-2)=8px  → 新 0
   跟上面 .review-actions 本身的 gap／btn 高度縮減、.review-session-header
   margin-bottom 縮減三處合計，是這輪「字卡盡可能大」在不改高度公式前提下
   能讓卡片本體多吃到的實際空間。 */
.card-screen .review-actions {
  margin-top: var(--space-2);
  margin-bottom: 0;
}

/* -- 功能一（2026-07-09）：中譯旁「翻譯已確認」小綠勾——低調、小號，見
   card-render.js isTranslationVerified／verifiedBadgeHtml。 -- */
.verified-badge {
  display: inline-block;
  margin-left: var(--space-1);
  color: var(--color-success-600);
  font-size: var(--font-size-xs);
  font-weight: 700;
  vertical-align: middle;
}
.detail-verified-row {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  margin-bottom: var(--space-2);
}
.detail-verified-row .hint-text { margin: 0; }

/* -- 功能二（2026-07-09）：例句／韋氏英釋單字點查。使用者明確要求平常外觀
   必須是普通文字——.word-tap 刻意不加任何底線／變色／框線，只有點擊瞬間
   靠 .word-tap-flash 給短暫背景高亮回饋（見 card-render.js wrapClickableWords／
   wireWordLookup）。 -- */
.word-tap {
}
.word-tap-flash {
  background: var(--color-brand-100);
  border-radius: 3px;
  transition: background 0.4s ease;
}

/* -- 功能三（2026-07-09）：複習頁「每日動力區」（today.js motivationHtml）。
   低調不花俏：沒有動畫迴圈、沒有音效，達標只在文字顏色上給一點回饋。 -- */
.motivation-section {
  margin-top: var(--space-6);
  padding-top: var(--space-5);
  border-top: var(--border-width) solid var(--color-border);
}
.motivation-streak {
  text-align: center;
  font-size: var(--font-size-lg);
  font-weight: 700;
  margin-bottom: var(--space-4);
}
.motivation-streak-num { color: var(--color-brand-600); }
/* 第十輪待辦①：進度環改成 <button>（點了可以續接進行中的複習），這裡重置
   瀏覽器內建的 button 樣式（邊框/底色/字型/游標），讓外觀跟原本的純 <div>
   完全一樣，只是多了可點擊的語意與游標提示。 */
.motivation-ring-wrap {
  display: flex;
  flex-direction: column;
  align-items: center;
  margin-bottom: var(--space-5);
  border: none;
  background: none;
  padding: 0;
  font: inherit;
  color: inherit;
  cursor: pointer;
  width: 100%;
}
.motivation-ring { width: 100px; height: 100px; }
.motivation-ring-label {
  margin-top: var(--space-2);
  font-size: var(--font-size-sm);
  color: var(--color-text-muted);
}
.motivation-ring-wrap.is-complete .motivation-ring-label {
  color: var(--color-success-600);
  font-weight: 700;
}
.motivation-milestones { display: flex; flex-direction: column; gap: var(--space-2); }
.milestone-row { display: flex; align-items: center; gap: var(--space-2); }
.milestone-row-label { font-size: var(--font-size-xs); color: var(--color-text-faint); width: 64px; flex: none; }
.milestone-badge-row { display: flex; gap: var(--space-1); flex-wrap: wrap; }
.milestone-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 40px;
  height: 24px;
  padding: 0 var(--space-2);
  border-radius: var(--radius-full);
  font-size: var(--font-size-xs);
  background: var(--color-muted-bg);
  color: var(--color-text-faint);
}
.milestone-badge.is-earned {
  background: var(--color-success-bg);
  color: var(--color-success-600);
  font-weight: 600;
}
/* 第十一輪待辦④：里程碑改顯示「已達成最高一顆＋下一個目標」兩顆（見
   today.js milestoneRowHtml／store.js computeMilestoneBadges），中間補一個
   箭頭指示方向；下一個目標的徽章沿用未達成 .milestone-badge 的灰底樣式，
   額外拉開一點跟已達成徽章的視覺差異（虛線框，暗示「還沒到」）。 */
.milestone-arrow { color: var(--color-text-faint); font-size: var(--font-size-xs); }
.milestone-badge-next { border: 1px dashed var(--color-border); background: transparent; }

/* -- 第十輪待辦③：搜尋框清除叉叉（詞庫／直接選字共用）。輸入框本身的
   .input 已有右側 padding 給音量／圖示類元件用嗎？沒有，這裡額外把輸入框
   右側 padding 讓出空間給叉叉，避免文字被蓋住；叉叉本身是 .field 內絕對
   定位，跟旁邊排序選單各自獨立（.search-field-row 用 flex 把兩者並排）。 -- */
.search-field-row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-bottom: var(--space-4);
}
.search-field-row .search-field {
  flex: 1;
  min-width: 0;
}
.search-field {
  position: relative;
}
.search-field .input {
  padding-right: calc(var(--space-4) + 28px);
}
.search-clear-btn {
  position: absolute;
  right: var(--space-2);
  top: 50%;
  transform: translateY(-50%);
  width: 28px;
  height: 28px;
  border: none;
  background: transparent;
  color: var(--color-text-faint);
  font-size: 1.1rem;
  line-height: 1;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}
.search-clear-btn[hidden] {
  display: none;
}
/* 第十輪待辦⑤：排序選單——低調、跟搜尋框同高，不搶焦點，寬度依內容自動，
   不強制吃滿剩餘空間（避免選項文字較短時選單過寬看起來空洞）。 */
.wb-sort-select {
  flex: none;
  width: auto;
  min-height: var(--touch-target);
  padding: var(--space-2) var(--space-3);
  font-size: var(--font-size-sm);
}

/* -- 第十輪待辦④：收藏鈕（☆/★）。低調——沒有底色/邊框，跟旁邊的發音鍵
   （.speak-btn，有圓形底色）刻意做出視覺差異，符合交辦「低調」的要求；
   未收藏是空心星星（灰階），收藏後變實心星星＋琥珀色，跟 .verified-badge
   的成功綠有區別（收藏是個人標記，不是「翻譯已確認」那種品質狀態）。 -- */
.favorite-btn {
  width: 40px;
  height: 40px;
  border: none;
  background: transparent;
  color: var(--color-text-faint);
  font-size: 1.3rem;
  line-height: 1;
  cursor: pointer;
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.favorite-btn.is-favorite {
  color: #d97706;
}
.favorite-btn:disabled {
  opacity: 0.5;
  cursor: default;
}
/* 詞庫列表項小星星標記（第十輪待辦④「列表項也顯示小星星標記」）。 */
.wb-fav-star {
  color: #d97706;
  margin-right: var(--space-1);
  font-size: 0.9em;
}

/* -- 第十四輪 §3-1／R15 §5 改版：詞庫／直接選字格狀檢視（wordbank.js／
   builtin-browse.js 共用）。套在既有 <ul id="wb-list"|"bb-list"> 上（只切
   class，容器 id/元素不變）；.grid-cell 只是 <li> 語意包裝（沿用全域
   ul{list-style:none} reset，不需要額外樣式）。
   R15 §5：原本用 CSS Grid 固定欄寬（auto-fill minmax(84px,1fr)）＋
   overflow-wrap/word-break，長單字會被欄寬硬擠斷行（使用者原話「不要讓
   單一單字被迫分段呈現」）。改用 flexbox + flex-wrap：每個 .grid-item 預設
   flex:0 0 auto＋white-space:nowrap，格子寬度跟著文字內容走（=「格子自動
   加寬」），文字本身不斷行；配合既有 gridFontClass（card-render.js，字元
   長度>12/20 分別縮到 sm/xs 字級）把過長單字的字級先縮小，兩個手段合力讓
   絕大多數字典單字（含 internationalization 這種 20 字元長字）在常見手機
   寬度下單行完整顯示、不強迫換行。
   片語（headword 含空白，見 wordbank.js/builtin-browse.js 判斷邏輯）維持
   可以換行：.grid-item-phrase 覆寫成 flex:1 1 auto＋white-space:normal＋
   overflow-wrap/word-break，讓片語仍可以在單字間的空白處換行、不會撐爆
   版面（片語本來就比較長，允許換行才不會逼出超寬格子）。
   直接選字的格狀框額外有 .is-selected 勾選態（跟清單檢視的 checkbox 打勾
   是同一件事）。 -- */
.card-grid-list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}
.grid-item {
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  flex: 0 0 auto;
  min-width: 56px;
  min-height: 56px;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-md);
  border: var(--border-width) solid var(--color-border-strong);
  background: var(--color-surface);
  color: var(--color-text);
  font-size: var(--font-size-sm);
  font-weight: 600;
  white-space: nowrap;
  line-height: 1.3;
}
.grid-item.grid-item-phrase {
  flex: 1 1 auto;
  max-width: 100%;
  white-space: normal;
  overflow-wrap: break-word;
  word-break: break-word;
}
.grid-item.grid-item-sm { font-size: 0.75rem; }
.grid-item.grid-item-xs { font-size: 0.6875rem; }
.grid-item.is-selected {
  background: var(--color-brand-600);
  border-color: var(--color-brand-600);
  color: #fff;
}
:root[data-appearance="dark"] .grid-item.is-selected {
  color: var(--color-bg);
}

/* -- 第十四輪 §3-2：排序鈕主題色 inline SVG 細線箭頭（見 wordbank.js
   sortDirectionIconHtml）。原生 <select>/<option> 不能塞 <svg>（見程式端長
   註解），這個圖示掛在 <select> 外側，只在「加入時間」的兩個時間排序選項
   （新到舊／舊到新）時出現，stroke="currentColor" 天生跟隨這個 span 的文字
   顏色（沿用跟排序選單同色階，不用另外指定顏色 token）。 -- */
.wb-sort-dir-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  color: var(--color-text-muted);
}

/* -- 第十五輪回饋第2項複習分頁入口畫面（review.js showPicker）曾用過的
   「翻卡複習／小考」兩張大卡片（.review-entry-grid／.review-entry-card／
   .review-entry-title／.review-entry-desc）——第十六輪回饋第2項已改回頂部
   「複習｜小考」切換 chip-row（沿用既有 .chip-row／.chip，不需要新樣式，
   見 review.js headerContentHtml），這組卡片樣式不再被引用，整組移除，不留
   死 CSS。 */

/* -- 第十六輪回饋第4項：複習完成畫面四顆鈕（review.js renderComplete）--
   使用者原話「再翻一次跟清單檢視按鈕那麼小，整個畫面超不平衡」——原本
   「來個新回合／返回首頁」是全寬大按鈕、「再翻一次／清單檢視」卻擠在同一
   橫排用 .btn-sm，四顆鈕高矮寬窄都不一樣。改 2×2 等寬網格：四顆鈕統一
   尺寸，主次分明改靠顏色（來個新回合＝btn-primary 品牌色）區分，不是靠
   尺寸差異；disabled 狀態（今天沒複習過時「再翻一次」「清單檢視」不可按）
   一樣佔滿等寬格子，不會讓版面忽大忽小。 */
.complete-actions-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-3);
  margin-top: var(--space-4);
}
.complete-actions-grid .btn {
  width: 100%;
  margin: 0;
}
