/* 台灣手語辨識 — 公開展示介面
   ─────────────────────────────────────────────────────────
   三個設計約束（使用者訂的）：
     一、立體感：用新擬物風格——同色系的表面，靠雙向陰影做出凸起與凹陷。
     二、黑白：不用彩色。主要動作用純黑，其餘靠明暗與陰影分層。
     三、整頁一屏：電腦上不用捲動就能看完。因此版面用 100dvh 撐滿，
         鏡頭區吃掉剩餘空間，其餘元素固定高度。

   新擬物有一個已知的無障礙問題：對比太低。這裡的處理是——
   表面與陰影走柔和路線，但**所有文字與可點擊元件維持高對比**，
   不讓風格犧牲可讀性。 */

:root {
  /* 拉丁字型排在中文前面：字型是逐字元挑選的，數字與英文由 San Francisco／
     Segoe UI 畫，中文再穿透到後面的中文字型。這樣 tabular-nums 才會生效
     （中文字型多半沒有這個特性），英數也不會被中文字型畫得又大又粗。
     刻意不放 system-ui：簡體中文介面的 Windows 上它會解析成 Microsoft YaHei，
     會蓋過 lang 屬性把整頁變成簡體字形。 */
  --font-sans:
    -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial,
    "PingFang TC",
    "Microsoft JhengHei UI", "Microsoft JhengHei", "微軟正黑體",
    "Noto Sans TC", "Noto Sans CJK TC",
    sans-serif;

  color-scheme: light;

  /* 參考圖的作法不是玻璃，是「純白實心容器 + 很散的柔和陰影」。
     容器不透明、沒有邊框，靠陰影和明度差分層；
     作用中的元素用純黑，其餘一律低調。 */
  --bg: #FFFFFF;
  --surface: #FFFFFF;
  --inset: #F4F3F0;      /* 容器內的凹槽／未作用的小方塊 */
  --hi: #FFFFFF;
  --lo: #CDCBC6;

  /* 玻璃 */
  /* 玻璃需要背後有東西才折射得出來，所以背景不能是純色。
     底色刻意壓到中灰：玻璃是亮的，背景太亮就看不出它浮在上面。
     全部是灰階，維持黑白。 */
  --page-bg: none;   /* 平的淺灰。參考的是「白卡片放在平底上」，不再用漸層 */

  /* 純白容器。陰影刻意很散（20px 以上的模糊、很低的不透明度）——
     參考圖的柔軟感來自「大範圍淡陰影」，銳利的陰影會變成便宜的卡片感。 */
  --glass-bg: #FFFFFF;
  --glass-line: rgba(20,20,26,.08);   /* 極細邊線切出邊界，陰影就可以很輕 */
  --glass-blur: none;
  /* 上緣那道白線是整個效果的關鍵——沒有它就只是「模糊的半透明方塊」，
     不會被讀成玻璃。下緣的微光是玻璃厚度，外陰影讓它浮起來。 */
  --glass-shadow:
    0 1px 2px rgba(18,18,24,.04),
    0 8px 20px rgba(18,18,24,.06);
  --glass-shadow-sm:
    0 1px 2px rgba(18,18,24,.05),
    0 3px 8px rgba(18,18,24,.05);
  /* 黑色元素的陰影要更深一點，才看得出它是「壓在白色上面」的 */
  --ink-shadow:
    0 6px 14px rgba(18,18,24,.28),
    0 2px 5px rgba(18,18,24,.18);

  --text: #1B1A17;
  --muted: #62605A;
  --ink: #1E1D1A;         /* 作用中：墨色 */
  --ink-on: #FFFFFF;
  --warn: #8A5300;

  /* 鏡頭與字幕永遠深色，不跟主題切換。
     字幕類介面一律深底白字，這在聽障輔具上是驗證過的做法。 */
  --stage-bg: #101215;
  --caption-bg: rgba(12, 14, 17, .86);
  --caption-line: rgba(255, 255, 255, .12);
  --caption-text: #FFFFFF;
  --caption-sub: #A8B0BA;

  --sidebar-track-width: clamp(360px, 27vw, 400px);
  --sidebar-width: clamp(320px, 23vw, 340px);
  --settings-row-height: 44px;
  --settings-row-gap: 12px;
  --r: 24px;
  --r-sm: 16px;
  --r-pill: 999px;

  /* 玻璃珠：立體的鈕，坐在一條凹槽裡。
     三個部分缺一不可——上緣的白色高光（光從上面來）、
     由上往下微微變暗的漸層（珠子的厚度）、下方的外陰影（它浮起來）。
     按下去時三個全部反過來：陰影收進內側、位置下沉一點點。 */
  --bead: linear-gradient(180deg, #FFFFFF 0%, #F4F3F0 100%);
  --bead-shadow:
    inset 0 1px 0 rgba(255,255,255,.95),
    inset 0 -1px 0 rgba(20,20,26,.05),
    0 1px 1px rgba(20,20,26,.05),
    0 3px 6px rgba(20,20,26,.09),
    0 8px 16px rgba(20,20,26,.07);
  --bead-press:
    inset 0 2px 5px rgba(20,20,26,.18),
    inset 0 -1px 0 rgba(255,255,255,.55),
    0 1px 1px rgba(20,20,26,.05);
  /* 凹槽：珠子坐進去的那條溝 */
  --track-bg: #E8E6E2;
  --track-shadow:
    inset 0 2px 4px rgba(20,20,26,.11),
    inset 0 -1px 0 rgba(255,255,255,.85);
  /* 墨色珠（主按鈕、按下狀態） */
  --bead-ink: linear-gradient(180deg, #3C3B36 0%, #1E1D1A 100%);
  --bead-ink-shadow:
    inset 0 1px 0 rgba(255,255,255,.18),
    inset 0 -1px 0 rgba(0,0,0,.40),
    0 2px 4px rgba(18,18,24,.28),
    0 8px 18px rgba(18,18,24,.26);
  --bead-ink-press:
    inset 0 3px 7px rgba(0,0,0,.55),
    inset 0 -1px 0 rgba(255,255,255,.10);

  /* 凸起／凹陷。距離刻意小——大陰影在密集版面上會糊成一片 */
  --up: 5px 5px 10px var(--lo), -5px -5px 10px var(--hi);
  --up-sm: 3px 3px 6px var(--lo), -3px -3px 6px var(--hi);
  --in: inset 3px 3px 6px var(--lo), inset -3px -3px 6px var(--hi);

  font-family: var(--font-sans);
  font-size: 100%;   /* 不寫死 px，使用者自己調整的瀏覽器字級才有效 */
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --bg: #2D2D2B;         /* 中性偏暖的炭灰。原本 #2A2C2F 藍多於紅，整片會偏藍 */
    --surface: #403F3C;    /* 按鈕與詞塊：比卡片再亮一階 */
    --inset: #262624;      /* 凹槽比卡片暗一階 */   /* 凹槽。淺色是 #F2F2F4，深色沒定義的話會沿用它，整片刺眼 */
    --hi: #4A4A47;
    --lo: #222220;
    --text: #F2F1EF;
    --muted: #A8A5A0;
    --ink: #F2F1EF;
    --ink-on: #232322;
    --warn: #F0B860;
    /* 深色版：光暈壓得很淡，底色是深灰。整體邏輯一樣——
       背景要有結構，玻璃才有東西可以折射。 */
    --page-bg: none;
    /* 深色的玻璃：高光要壓很淡，不然會像塑膠貼紙 */
    --glass-bg: #363634;   /* 實心卡片，比底亮一階 */
    --glass-line: rgba(255,255,255,.075);
    --glass-shadow:
    inset 0 1px 0 rgba(255,255,255,.06),
    0 1px 2px rgba(0,0,0,.30),
    0 12px 28px rgba(0,0,0,.28);
    --glass-shadow-sm:
    inset 0 1px 0 rgba(255,255,255,.06),
    0 2px 8px rgba(0,0,0,.30);

  /* 玻璃珠（深色）：光一樣從上面來，只是高光要壓很淡，不然像塑膠貼紙 */
  --bead: linear-gradient(180deg, #4A4946 0%, #3A3936 100%);
  --bead-shadow:
    inset 0 1px 0 rgba(255,255,255,.12),
    inset 0 -1px 0 rgba(0,0,0,.35),
    0 1px 1px rgba(0,0,0,.30),
    0 3px 7px rgba(0,0,0,.35),
    0 8px 18px rgba(0,0,0,.30);
  --bead-press:
    inset 0 2px 6px rgba(0,0,0,.55),
    inset 0 -1px 0 rgba(255,255,255,.07),
    0 1px 1px rgba(0,0,0,.25);
  --track-bg: #232321;
  --track-shadow:
    inset 0 2px 5px rgba(0,0,0,.50),
    inset 0 -1px 0 rgba(255,255,255,.06);
  --bead-ink: linear-gradient(180deg, #FFFFFF 0%, #DCDBD7 100%);
  --bead-ink-shadow:
    inset 0 1px 0 rgba(255,255,255,.9),
    inset 0 -1px 0 rgba(0,0,0,.14),
    0 2px 4px rgba(0,0,0,.40),
    0 8px 18px rgba(0,0,0,.34);
  --bead-ink-press:
    inset 0 3px 7px rgba(0,0,0,.30),
    inset 0 -1px 0 rgba(255,255,255,.55);
  }
}
:root[data-theme="dark"] {
  color-scheme: dark;
  --bg: #2D2D2B;         /* 中性偏暖的炭灰。原本 #2A2C2F 藍多於紅，整片會偏藍 */ --surface: #403F3C;    /* 按鈕與詞塊：比卡片再亮一階 */
  --inset: #262624;      /* 凹槽比卡片暗一階 */   /* 凹槽。淺色是 #F2F2F4，深色沒定義的話會沿用它，整片刺眼 */ --hi: #4A4A47; --lo: #222220;
  --text: #F2F1EF; --muted: #A8A5A0; --ink: #F2F1EF; --ink-on: #232322;
  --warn: #F0B860;
  --page-bg: none;
  --glass-bg: #363634;   /* 實心卡片，比底亮一階 */
  --glass-line: rgba(255,255,255,.075);
  --glass-shadow:
    inset 0 1px 0 rgba(255,255,255,.06),
    0 1px 2px rgba(0,0,0,.30),
    0 12px 28px rgba(0,0,0,.28);
  --glass-shadow-sm:
    inset 0 1px 0 rgba(255,255,255,.06),
    0 2px 8px rgba(0,0,0,.30);

  /* 玻璃珠（深色）：光一樣從上面來，只是高光要壓很淡，不然像塑膠貼紙 */
  --bead: linear-gradient(180deg, #4A4946 0%, #3A3936 100%);
  --bead-shadow:
    inset 0 1px 0 rgba(255,255,255,.12),
    inset 0 -1px 0 rgba(0,0,0,.35),
    0 1px 1px rgba(0,0,0,.30),
    0 3px 7px rgba(0,0,0,.35),
    0 8px 18px rgba(0,0,0,.30);
  --bead-press:
    inset 0 2px 6px rgba(0,0,0,.55),
    inset 0 -1px 0 rgba(255,255,255,.07),
    0 1px 1px rgba(0,0,0,.25);
  --track-bg: #232321;
  --track-shadow:
    inset 0 2px 5px rgba(0,0,0,.50),
    inset 0 -1px 0 rgba(255,255,255,.06);
  --bead-ink: linear-gradient(180deg, #FFFFFF 0%, #DCDBD7 100%);
  --bead-ink-shadow:
    inset 0 1px 0 rgba(255,255,255,.9),
    inset 0 -1px 0 rgba(0,0,0,.14),
    0 2px 4px rgba(0,0,0,.40),
    0 8px 18px rgba(0,0,0,.34);
  --bead-ink-press:
    inset 0 3px 7px rgba(0,0,0,.30),
    inset 0 -1px 0 rgba(255,255,255,.55);
}

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

/* 作者樣式裡的 display 會蓋過瀏覽器給 [hidden] 的 display:none。
   .btn 是 inline-flex、.live-badge 是 flex，所以標了 hidden 還是會顯示出來。
   這一條把它擋回去。 */
[hidden] { display: none !important; }

html, body { height: 100%; }
body {
  margin: 0;
  /* 幾道很淡的灰階光暈。玻璃需要背後有東西才折射得出來——
     背景若是純色，模糊完還是同一個顏色，效果等於零。
     全部是灰階，維持黑白。 */
  /* 底色要單獨寫一次。--page-bg 最後一層雖然是不透明的漸層，
     但那是<圖層>不是 background-color：列印、強制色彩模式、
     以及漸層還沒畫出來的第一瞬間，背景會是白的而字是淺色。 */
  background-color: var(--bg);
  background-image: var(--page-bg);
  /* 尺寸要另外寫。把 "0 0 / 14px 14px" 塞進 --page-bg 裡是 background 簡寫的語法，
     用 background-image 套的話整個值不合法，算出來會是 none（點紋整片不見）。 */
  background-size: var(--page-bg-size, auto);
  background-attachment: fixed;
  color: var(--text);
  font-size: 0.9375rem;      /* 15px：不大不小，長時間看不累 */
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  display: flex; flex-direction: column;
  height: 100dvh;
  overflow: hidden;          /* 整頁一屏，不捲動 */
}

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

/* ── 頂列 ─────────────────────────────────────────── */
.topbar {
  flex: 0 0 auto;
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px; padding: 10px 18px;
  /* 跟下面的內容對齊。原本沒有寬度上限，大螢幕上標題會飄到最左、
     按鈕飄到最右，跟 1240px 的內容區對不起來。 */
  max-width: 1500px; margin: 0 auto; width: 100%;
}
/* 頂列的按鈕在觸控裝置上要有 44×44 的熱區（視覺大小不變）。 */
@media (pointer: coarse) {
  .icon-btn, .seg-btn { position: relative; }
  .icon-btn::after, .seg-btn::after {
    content: ""; position: absolute; left: 50%; top: 50%;
    transform: translate(-50%, -50%);
    width: max(100%, 44px); height: max(100%, 44px);
  }
  .topbar-actions { gap: 10px; }
  .seg {
  border: 1px solid var(--glass-line); gap: 6px; }
}
.brand { display: flex; align-items: center; gap: 10px; color: var(--text); }
.brand h1 { margin: 0; font-size: 1.0625rem; font-weight: 700; letter-spacing: .01em; }
.topbar-actions { display: flex; align-items: center; gap: 10px; }

.seg {
  border: 1px solid var(--glass-line);
  display: flex; align-items: center; gap: 3px;
  padding: 4px; border-radius: var(--r-pill);
  background: var(--surface);
  box-shadow: var(--glass-shadow-sm);
}
.seg-label { font-size: .8125rem; color: var(--muted); padding: 0 8px 0 10px; }
.seg-btn {
  appearance: none; border: 0; cursor: pointer;
  font: inherit; font-size: .8125rem; color: var(--muted);
  padding: 6px 14px; border-radius: var(--r-pill);
  background: transparent;
}
.seg-btn.is-on {
  color: var(--ink-on); background: var(--ink);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.16), var(--ink-shadow);
}
.icon-btn {
  border: 1px solid var(--glass-line);
  appearance: none; cursor: pointer;
  width: 38px; height: 38px; border-radius: var(--r-pill);
  display: grid; place-items: center; color: var(--text);
  background: var(--glass-bg);
  box-shadow: var(--glass-shadow-sm);
}
.icon-btn:active { box-shadow: var(--in); }

/* ── 版面 ─────────────────────────────────────────── */
.layout {
  flex: 1 1 auto; min-height: 0;
  display: grid;
  /* 左邊是畫面、右邊是操作（選詞、看示範／自己比、句子）。
     右欄 320px：句子卡的兩顆按鈕與詞塊在這個寬度剛好不用擠。 */
  /* 整頁寬度上限 1680（原本 1240）：4:3 的畫面在 1080 高的螢幕上需要約 1270 寬才能用滿高度，
     1240 會讓畫面被寬度卡住、上下各留一截空白，右欄卻是滿高的——兩邊對不齊。
     右欄寬度隨螢幕縮放（260～340），小一點的桌機才不會被右欄吃掉太多。 */
  grid-template-columns: minmax(0, 1fr) var(--sidebar-track-width);
  grid-template-rows: minmax(0, 1fr);
  grid-template-areas: "stage side";
  gap: 14px;
  padding: 0 18px 10px;
  max-width: 1500px; width: 100%; margin: 0 auto;
}
.stage { grid-area: stage; min-height: 0; display: flex; flex-direction: column; gap: 10px; }
.side  {
  grid-area: side; display: flex; flex-direction: column; gap: 12px; min-height: 0;
  /* 側欄內容比欄位高時自己捲，不要把整頁撐開——整頁一屏顯示完是這個版面的前提。
     捲軸<要顯示>：整頁是 overflow:hidden，所以頁面本身不能捲；
     捲軸被藏起來的話，滑鼠使用者沒有任何線索知道下面還有東西。
     （細捲軸，不搶畫面。鍵盤入口在 index.html 那邊用 tabindex 給。） */
  overflow-y: auto; scrollbar-width: thin;
  scrollbar-color: var(--muted) transparent;
}
.side::-webkit-scrollbar { width: 6px; }
.side::-webkit-scrollbar-thumb { background: var(--muted); border-radius: 999px; }

/* 框的容器：吃掉整個可用高度，並把自己設成尺寸容器（container-type: size），
   這樣框可以用 cqh / cqw 拿到「可用的高與寬」，算出貼合畫面比例的尺寸——
   不用估頂列與按鈕的高度、不用 JS、也不會有量了又改、改了又量的循環。
   手機版（≤900px）不能這樣做：那裡的列高是 auto，尺寸容器會把自己塌成 0。 */
.frame-box {
  flex: 1 1 auto; min-height: 0;
  container-type: size;
  display: flex; align-items: center; justify-content: center;
}
/* 桌機：右欄要跟左邊的畫面一樣高。選詞卡用自己的高度，
   「辨識出的句子」把剩下的高度吃掉（句子長了本來就需要那個空間）。
   否則大螢幕上右下角整片是空的，兩欄對不齊。 */
@media (min-width: 901px) {
  .layout { padding-bottom: var(--topbar-height, 64px); }
  .viewport { transform: translateX(var(--frame-shift, 0px)); }
  .side { transform: translate(calc(-1 * var(--sidebar-shift, 0px)), calc((var(--settings-row-height) + var(--settings-row-gap)) / 2)); }
  .topbar-actions { transform: translate(calc(-1 * var(--sidebar-shift, 0px)), var(--settings-shift, 0px)); }
  .side .sentence { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }
  .side .sentence .strip { flex: 1 1 auto; max-height: none; min-height: 120px; }
  .side .sentence .strip { align-content: flex-start; }   /* 詞塊靠上排，不要在高框裡垂直置中 */
}

/* ── 鏡頭 ─────────────────────────────────────────── */
.viewport {
  position: relative;
  flex: 1 1 auto; min-height: 180px;
  /* 框要跟畫面一樣寬，不要左右留黑邊。
     原本框撐滿整欄（寬度由視窗決定、高度由剩餘空間決定），
     4:3 的鏡頭或 16:9 的教學影片放進一個約 2:1 的框，contain 就一定留黑邊。
     用 cover 填滿會裁掉上下——手語最不能裁的就是手。
     所以反過來：框<不要> stretch，寬度由「高度 × 畫面比例」算出來、置中。
     比例由 app.js 依當下顯示的那一層（鏡頭或教學影片）設定；還沒開鏡頭前用 4:3。
     手機版另有規則（那邊高度不是定值，要用 max-width 算）。 */
  aspect-ratio: 4 / 3;
  /* 高度 = 可用高度與「可用寬度換算成的高度」取小；寬度由比例推出。
     這就是 object-fit: contain 的行為，只是套在框上而不是影像上。
     --cam-ar 與 aspect-ratio 都由 app.js 的 applyFrameRatio() 依顯示中的那一層設定。 */
  flex: none;
  height: min(100cqh, calc(100cqw / var(--cam-ar, 1.3333)));
  width: auto; max-width: 100%;
  background: var(--stage-bg);
  border: 1px solid rgba(255,255,255,.10);
  border-radius: var(--r);
  overflow: hidden;
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.14),
    0 16px 40px rgba(18,20,24,.24),
    0 3px 10px rgba(18,20,24,.14);
}
#camera, #overlay {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: contain;          /* 4:3 的影像放進任意比例的框，留黑邊不變形 */
}
#camera {
  transform: scaleX(-1);        /* 鏡像顯示：使用者看自己要像照鏡子 */
}
#overlay {
  pointer-events: none;
  /* 這裡<不要>加 scaleX(-1)。骨架的座標已經是鏡像過的了——
     app.js 的 frameLoop 先把畫面翻轉才送進 MediaPipe（為了跟訓練資料同方向，
     抽關鍵點.py 也是這樣做的），所以回來的 x 已經在鏡像後的座標系裡。
     再用 CSS 翻一次等於翻兩次，骨架會跟影片左右相反：
     舉右手，影片裡的手在右邊，骨架卻畫在左邊。
     影片只翻一次（上面那條規則），所以兩者一致的作法是這一層不翻。 */
  transform: none;
}

.stage-bar {
  /* 要在教學示範層（z-index 2）之上，否則切到示範時分頁按鈕會被蓋掉 */
  position: absolute; inset: 10px 10px auto 10px; z-index: 5;
  display: flex; justify-content: space-between;
  font-size: .75rem; color: var(--caption-sub);
}
.stage-bar .label { opacity: .75; }

.placeholder {
  position: absolute; inset: 0; z-index: 1;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 10px; text-align: center; color: var(--caption-sub); padding: 20px;
}
.placeholder-title { margin: 0; font-size: 1.0625rem; font-weight: 700; color: var(--caption-text); }
.placeholder-sub { margin: 0; font-size: .875rem; line-height: 1.65; }

/* ── 即時辨識 ─────────────────────────────────────── */
.live-badge {
  position: absolute; inset: auto 12px 12px 12px; z-index: 3;
  display: flex; flex-direction: column; gap: 8px;
  padding: 12px 16px;
  background: var(--caption-bg);
  backdrop-filter: blur(12px);
  border: 1px solid var(--caption-line);
  border-radius: var(--r-sm);
  color: var(--caption-text);
}
.live-main { display: flex; align-items: baseline; gap: 12px; }
.live-state {
  font-size: .75rem; color: var(--caption-sub); white-space: nowrap;
  padding: 3px 9px; border-radius: var(--r-pill); background: rgba(255,255,255,.08);
  /* 空間不夠時先犧牲狀態標籤，不要犧牲辨識出的詞。
     原本反過來：375px 的手機上「我們兩個」被截成「我們兩…」，
     而旁邊那個最不重要的提示一個字都沒少。 */
  flex: 0 10 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis;
}
.live-badge strong {
  font-size: clamp(24px, 3.4vw, 34px);
  line-height: 1.15; font-weight: 700; color: var(--caption-text);
  /* 不可省略：flex 會把這一項壓到最小內容寬度，
     而中文每個字都能當斷行點，「開心」會被折成兩行。 */
  white-space: nowrap; flex: 0 1 auto; min-width: 0;
  overflow: hidden; text-overflow: ellipsis;
}
.live-score {
  font-size: .9375rem; color: var(--caption-sub);
  font-variant-numeric: tabular-nums; flex: 0 0 auto;
}
.live-badge.is-stable { border-color: rgba(255,255,255,.4); }
.live-badge.is-idle strong { color: var(--caption-sub); font-weight: 600; }

/* 第二、三名。模型不確定時正確答案常常在這裡；
   只顯示第一名會讓使用者以為系統壞了，列出候選才誠實。 */
.live-alts { display: flex; flex-direction: column; gap: 4px; margin: 0; padding: 0; list-style: none; }
/* 空的時候保留一列高度，不要 display:none——候選出現／消失會讓卡片長高縮短，看起來就是在跳。 */
.live-alts { min-height: 26px; }
.live-alts:empty { visibility: hidden; }
.live-alts li {
  position: relative; overflow: hidden;
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 4px 10px; border-radius: 7px;
  background: rgba(255,255,255,.07);
  font-size: .8125rem; color: var(--caption-sub);
}
/* 長條用背景層畫，文字永遠壓在上面不會被推開 */
.live-alts li::before {
  content: ""; position: absolute; inset: 0 auto 0 0;
  width: var(--bar, 0%); background: rgba(255,255,255,.14);
}
.live-alts .alt-word, .live-alts .alt-score { position: relative; }
.live-alts .alt-word { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.live-alts .alt-score { font-variant-numeric: tabular-nums; flex: 0 0 auto; }

/* ── 按鈕 ─────────────────────────────────────────── */
/* 「關閉鏡頭」：在兩顆模式鈕下面，整列寬、安靜。收尾動作不該跟主動作一樣搶眼。 */
.mode-stop { margin-top: 8px; width: 100%; }
.btn {
  appearance: none; border: 1px solid var(--glass-line); cursor: pointer; font: inherit; font-weight: 600;
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  padding: 11px 20px; border-radius: var(--r-pill); min-height: 44px;
  background: var(--surface);
  color: var(--text);
  box-shadow: var(--glass-shadow-sm);
  transition: box-shadow .14s ease, transform .12s ease;
}
.btn:hover { transform: translateY(-1px); }
.btn:active { transform: translateY(0); box-shadow: 0 1px 3px rgba(24,24,30,.14); }
.btn:disabled { opacity: .42; cursor: not-allowed; transform: none; }
/* 主要按鈕是實心黑，但保留上緣高光——那道白線是玻璃的識別特徵，
   純黑沒有它會顯得扁平，跟旁邊的玻璃元件對不起來。 */
/* 作用中：純黑。上緣一道很淡的內高光，讓它看起來微微隆起——
   參考圖裡黑色按鈕的立體感就是這樣來的，不是靠邊框。 */
.btn-primary {
  border-color: transparent;
  flex: 1 1 auto;
  background: var(--ink);
  color: var(--ink-on);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.16), var(--ink-shadow);
}
.btn-primary:active { filter: brightness(1.2); }
.btn.full { width: 100%; }
.btn-sm { padding: 7px 14px; min-height: 36px; font-size: .8125rem; }
/* 「清除」會刪掉整句。它不該跟「唸出整句」長得一樣吸引人。
   （刪掉之後可以按「復原」，所以不用再加一層確認對話框。） */
.btn-quiet { color: var(--muted); box-shadow: none; background: transparent; }
.btn-quiet:hover { background: var(--surface); box-shadow: var(--glass-shadow-sm); }

/* 鏡頭下方不顯示任何狀態文字；錯誤只出現在畫面內。 */
.status { position: absolute; z-index: 7; top: 38px; left: 14px; right: 14px; margin: 0; padding: 10px 12px; border-radius: 10px; background: var(--caption-bg); color: var(--caption-text); font-size: .875rem; line-height: 1.5; max-height: calc(100% - 54px); overflow-y: auto; }
.status.is-error { color: var(--caption-text); font-weight: 600; }
.status:empty { display: none; }

/* ── 側欄卡片 ─────────────────────────────────────── */
.card {
  background: var(--glass-bg);
  border: 1px solid var(--glass-line);
  border-radius: var(--r);
  padding: 13px 14px;
  box-shadow: var(--glass-shadow);
}
.card h2 { margin: 0 0 11px; font-size: .9375rem; font-weight: 700; }
.field-label { display: block; font-size: .875rem; color: var(--muted); margin-bottom: 7px; }

select {
  appearance: none; width: 100%; font: inherit; font-size: .875rem;
  color: var(--text); background: var(--inset);
  border: 0; border-radius: var(--r-sm); padding: 11px 34px 11px 14px;
  cursor: pointer;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5l5-5' fill='none' stroke='%23565B64' stroke-width='1.8' stroke-linecap='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 13px center;
}
.fine { font-size: .8125rem; line-height: 1.7; color: var(--muted); }
/* 「看示範／我要比比看」兩顆並排。按下去的那個變成墨色，表示畫面現在是它。 */
.mode { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-top: 10px; }
.mode.is-single { grid-template-columns: 1fr; }
.mode .btn { padding-inline: 8px; }
.mode .btn.is-on, .mode .btn.is-on:hover {
  border-color: transparent;
  background: var(--ink); color: var(--ink-on);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.16), var(--ink-shadow);
}
.guide-video { width: 100%; border-radius: var(--r-sm); background: var(--stage-bg); display: block; margin-bottom: 8px; }
.hint { margin: 9px 0 0; font-size: .875rem; line-height: 1.7; color: var(--muted); }

/* 準確率。原本右欄大半空白，放實測數字既填版面，
   也讓第一次打開的人立刻知道這個東西的能力邊界。 */
/* 換人的數字擺最上面、字最大。那是這個東西實際上能不能用的答案。
   全部單色——不用顏色暗示「這個比較差」，那會引導讀者看別的數字。 */

/* 側欄的「哪些詞好認」。側欄只有 208px 寬，所以標籤放上面、詞排下面，
   不要左右分欄——分欄會把詞擠成一欄兩個字。 */

/* ── 句子 ─────────────────────────────────────────── */
.sentence {
  background: var(--glass-bg);
  border: 1px solid var(--glass-line);
  border-radius: var(--r);
  padding: 11px 14px;
  box-shadow: var(--glass-shadow);
}
.sentence-head {
  display: flex; flex-direction: column; align-items: stretch;
  gap: 8px; margin-bottom: 10px;
}
.sentence-actions { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
/* 右欄只有 320px（橫放手機更窄），兩顆並排的按鈕左右內距收一點，「唸出整句」才不會折成兩行 */
.sentence-actions .btn { padding-inline: 8px; white-space: nowrap; }
.sentence-head h2 { margin: 0; font-size: .9375rem; font-weight: 700; }
.sentence-head { margin-bottom: 8px; }
.switch { display: inline-flex; align-items: center; gap: 7px; font-size: .8125rem; color: var(--muted); cursor: pointer; }
.switch input { width: 17px; height: 17px; accent-color: var(--ink); cursor: pointer; }

.strip {
  min-height: 50px; border-radius: var(--r-sm);
  padding: 10px 12px;
  /* 凹槽要明顯比卡片暗。原本兩者同明度，整塊糊成一片灰。
     深色模式下 --inset 與詞卡的 --surface 是同一個色碼，凹槽感整片消失，
     所以這裡另外加一道內陰影把層次做出來——不依賴兩個顏色剛好不同。 */
  background: var(--inset);
  box-shadow: inset 0 1px 2px rgba(0,0,0,.18);
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
  overflow-y: auto; max-height: 84px;
}
.strip-empty { margin: 0; color: var(--muted); font-size: .9375rem; }
/* 鏡頭框下面那一行「只認得 25 個詞…」。小、安靜，但一直在。 */
.stage-hint { margin: 0; text-align: center; }
.chip {
  border: 1px solid var(--glass-line);
  appearance: none; cursor: pointer; font: inherit;
  display: inline-flex; align-items: center; gap: 2px;
  font-size: 1.125rem; font-weight: 700; color: var(--text);
  padding: 5px 6px 5px 14px; border-radius: var(--r-pill);
  background: var(--surface);
  box-shadow: var(--glass-shadow-sm);
  transition: transform .12s ease, box-shadow .12s ease;
}
.chip:hover { transform: translateY(-1px); }
.chip.is-playing {
  background: var(--ink); color: var(--ink-on);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.16), var(--ink-shadow);
}
/* 詞本身是一顆按鈕（按了再唸一次）。清掉瀏覽器預設的按鈕外觀，
   讓它看起來還是詞卡裡的字，但鍵盤 Tab 得到、報讀器說得出來。 */
.chip-word {
  appearance: none; border: 0; background: none; cursor: pointer;
  font: inherit; color: inherit; padding: 0 3px; margin-left: -3px;
  border-radius: 6px;          /* 鍵盤聚焦時的外框才不會是個直角方塊 */
}
/* 每個詞自己的刪除鈕。原本只有「清除全部」，
   比錯一個詞就得整句重來——那在示範的時候很難看。 */
.chip-x {
  appearance: none; border: 0; cursor: pointer; font: inherit;
  width: 22px; height: 22px; border-radius: 999px;
  display: grid; place-items: center;
  font-size: .8125rem; line-height: 1; color: var(--muted);
  background: var(--inset);
}
/* 刪除鈕的 hover 直接填滿紅底白字。
   原本是淡紅底 + 深紅字（#8E1F18），深色模式下底色被卡片的深灰壓掉，
   對比只剩 1.67:1（門檻 4.5:1）——想按刪除的時候那個 × 剛好看不見。
   填滿之後白字對 #B2261E 是 6.6:1，兩個模式、播放中的詞卡都成立。 */
.chip-x:hover, .chip-x:focus-visible { background: #B2261E; color: #FFF; }
/* 觸控裝置上把刪除鈕的可點範圍撐到 44×44（視覺大小不變）。
   原本只有 22×22，而旁邊 20px 就是「再聽一次」的文字，手機上很容易按錯：
   想刪掉一個詞，結果它唸給你聽。 */
.chip-x { position: relative; }
@media (pointer: coarse) {
  .chip { min-height: 44px; }
  .chip-x { width: 28px; height: 28px; }
  .chip-x::before {
    /* 往上下與右邊長，<不要>往左長進詞裡：原本四邊都 -8px，
       於是點「起床」的右緣會直接把它刪掉。示範途中誤刪一個已辨識的詞，
       比按了沒反應更難看。 */
    content: ''; position: absolute; inset: -8px -6px -8px 0; border-radius: 999px;
  }
}
.chip.is-playing .chip-x { background: rgba(255,255,255,.18); color: rgba(255,255,255,.85); }

/* ── 頁尾 ─────────────────────────────────────────── */
.foot { flex: 0 0 auto; padding: 0 22px 10px; max-width: 1500px; width: 100%; margin: 0 auto; }
.panel summary {
  cursor: pointer; font-size: .8125rem; color: var(--muted);
  list-style: none; display: inline-flex; align-items: center; gap: 6px;
  padding: 5px 13px; border-radius: var(--r-pill);
  background: var(--glass-bg);
  box-shadow: var(--glass-shadow-sm);
}
.panel summary::-webkit-details-marker { display: none; }
.panel summary::before { content: "？"; font-weight: 700; }
/* 展開時浮起來蓋在內容上，不把版面推長——整頁一屏的前提不能破 */
.panel[open] { position: relative; }
.panel[open] .panel-body {
  position: absolute; bottom: 38px; left: 0; right: 0; z-index: 20;
  max-height: 62vh; overflow-y: auto;
  background: var(--glass-bg);
  border-radius: var(--r);
  padding: 16px 18px; box-shadow: var(--glass-shadow);
  font-size: .8125rem; line-height: 1.7; color: var(--muted);
}
.panel-body p { margin: 0 0 9px; }
.panel-body p:last-child { margin-bottom: 0; }
.panel-body strong { color: var(--text); }
.credit { margin-top: 12px; padding-top: 11px; border-top: 1px solid var(--lo); }
a { color: var(--text); text-decoration: underline; text-underline-offset: 2px; }

.noscript { padding: 20px; text-align: center; color: var(--warn); }

/* ── 鍵盤焦點 ─────────────────────────────────────── */
/* 不要在這裡寫 border-radius——它會蓋掉元件自己的圓角，
   鍵盤聚焦時膠囊鈕與圓形鈕會突然變成方角。outline 本來就會沿著元件的圓角走。 */
:focus-visible { outline: 2px solid var(--text); outline-offset: 2px; }

/* ── 窄螢幕：放棄一屏，改成正常捲動 ──────────────── */
@media (max-width: 900px) {
  body { height: auto; min-height: 100dvh; overflow: auto; }

  /* 手機上辨識卡片原本是絕對定位壓在鏡頭畫面下半部，蓋掉約一半的預覽——
     手比低一點就看不到自己，而那正是使用者最需要看到的時候。
     改成排在鏡頭框下面自成一列。候選清單也收起來，卡片才維持一列高。 */
  /* 卡片留在鏡頭框底部（絕對定位），但候選詞改成單行之後只剩約一列高，
     在直式手機的 3:4 框裡只蓋到最下面約兩成，手比低一點仍看得到自己。
     （先前試過 position: static，結果它跑到框的最上面跟「未開啟」標籤疊在一起——
      因為框裡的影片與畫布都是絕對定位，卡片是唯一在正常流裡的東西。） */
  .live-badge { padding: 8px 12px; gap: 6px; }
  /* 候選詞在手機上不藏，改成單行橫排：
     模型每次推論本來就有前幾名，看到「你 / 老師」並列，使用者立刻懂兩個手勢很像——
     這是這一次、這個人、這個動作的真實情況，比任何預存的準確率數字有用，也不會過期。
     單行、可橫向捲動，卡片不會長高、不會再蓋到鏡頭。 */
  .live-alts {
    display: flex; flex-direction: row; flex-wrap: nowrap; gap: 6px;
    overflow-x: auto; -webkit-overflow-scrolling: touch; scrollbar-width: none;
  }
  .live-alts::-webkit-scrollbar { display: none; }
  .live-alts li { flex: 0 0 auto; padding: 3px 10px 3px 8px; }
  .live-alts li::before { display: none; }
  .live-alts .alt-word { max-width: 8em; }
  /* 鏡頭框的長寬比由 syncDimensions() 依實際串流設定（iOS 直式是 3:4），
     這裡只限制高度，橫放時才不會把整個螢幕吃掉。 */
  /* 鏡頭框的高度上限。原本 60svh 太高：加上頂列、按鈕與提示之後，
     「辨識出的句子」會被推到第一屏之外——而那正是使用者要看的結果。
     42svh 之下，iPhone 直式（812~932pt）第一屏就同時看得到鏡頭與句子。
     橫放時 svh 很小，改用 vmin 當第二道上限，免得鏡頭縮成一條細線。 */
  .viewport {
    max-height: min(42svh, 56vmin);
    /* 框的寬度收到跟串流一樣的比例，左右就不會各留一片黑。
       --cam-ar 由 app.js 的 syncDimensions() 依實際串流設定（iOS 直式是 3:4）。 */
    max-width: calc(min(42svh, 56vmin) * var(--cam-ar, 1.3333));
    /* width: 100% 不能省。這是直排 flex 的項目，一旦加了 margin-inline: auto
       就不再 stretch，寬度會變成「跟著內容算」——而框裡的影片與畫布都是
       絕對定位，內容寬度等於 0，於是整個框連同 aspect-ratio 一起塌成 2px。 */
    width: 100%; margin-inline: auto;
  }
  /* 句子區原本卡在 84px，比到第 4 個詞就看不到最新的。 */
  .strip { max-height: 132px; }
  .layout {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto;
    /* 手機：畫面在上，選詞與兩顆按鈕緊接在下面，句子再下面（都在 .side 裡） */
    grid-template-areas: "stage" "side";
    padding: 0 14px 20px; gap: 12px;
    /* 手機的 .layout 會被撐到整個視窗高（body min-height:100dvh、flex:1），
       而格線的列預設 align-content: stretch 會把多出來的高度平分給兩列——
       結果畫面下面、選詞卡上面憑空多出幾十像素的空白。列靠上排就好。 */
    align-content: start;
  }
  .frame-box { container-type: normal; display: block; flex: 0 0 auto; }
  .viewport { aspect-ratio: 4 / 3; min-height: 0; height: auto; }
  /* 375px 寬時標題與「女聲／男聲」會各自被擠成兩行，很難看也難按。
     標題縮一級、語音切換的字不換行、間距收緊，一行就放得下。 */
  .topbar { padding: 10px 14px; gap: 8px; }
  .brand h1 { font-size: .9375rem; white-space: nowrap; }
  .topbar-actions { gap: 6px; }
  .seg {
  border: 1px solid var(--glass-line); gap: 2px; padding: 3px; }
  .seg-label { display: none; }          /* 「語音」兩個字在手機上是多的，按鈕本身已經寫了女聲／男聲 */
  .seg-btn { padding: 6px 10px; white-space: nowrap; }
  .foot { padding: 0 14px 22px; }
  .btn-primary {
  border-color: transparent; min-height: 50px; }
  .panel[open] .panel-body { position: static; max-height: none; margin-top: 10px; }
}
/* 橫放時高度是稀缺資源、寬度有剩。維持單欄的話鏡頭會縮成一條細線，
   而「辨識出的句子」同樣掉到折線外——轉橫是使用者覺得能救場的動作，
   在這裡反而更糟。改成兩欄，把剩下的寬度用掉。 */
@media (max-width: 900px) and (orientation: landscape) {
  body { height: 100dvh; overflow: hidden; }
  .layout {
    grid-template-columns: minmax(0, 1fr) minmax(200px, 34%);
    /* 句子在上、用自己的高度；側欄拿剩下的並自己捲。
       反過來寫（側欄 auto）的話，側欄三張卡加起來遠高於橫放的 390px，
       1fr 那一列就會被壓成 0，句子整塊塌掉疊在側欄上面。 */
    grid-template-rows: minmax(0, 1fr);
    grid-template-areas: "stage side";
    gap: 10px;
  }
  .side { overflow-y: auto; }
  .viewport {
    max-height: none; height: auto;
    /* 橫放時框會被拉到整欄寬（約 529px），但影像只有 4:3——
       人只佔中間三分之一，左右各一大片黑。把框的寬度收回「這個高度該有的寬度」。
       減去的 205px 是頂列、按鈕、狀態列與間距的總和（量出來的）；
       估太小只是鏡頭略小一點，估太大才會留黑邊，所以刻意往小的方向估。 */
    max-width: calc((100svh - 205px) * var(--cam-ar, 1.3333));
    width: 100%; margin-inline: auto;
  }
  .strip { max-height: 72px; }
}

@media (max-width: 560px) {
  .seg-label { display: none; }
  .sentence-head { flex-direction: column; align-items: stretch; }
  .sentence-actions { display: grid; grid-template-columns: 1fr 1fr; gap: 9px; }
  .sentence-actions .switch { grid-column: 1 / -1; }
}
@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; animation: none !important; }
}


/* ── 分頁：看示範 / 我要比比看 ─────────────────────
   兩者是先後發生的，共用同一塊大畫面。教學影片放在側欄只有 208px 寬，
   看不清楚手形——而手形正是學手語唯一要看清楚的東西。 */

/* ── 教學示範層 ───────────────────────────────── */
.guide-layer { position: absolute; inset: 0; z-index: 2; background: var(--stage-bg); }
#guide-video, #guide-canvas {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: contain;
  /* 這裡<不翻轉>。教學影片的檔案本身就是鏡像的（跟鏡頭畫面同方向，
     使用者照著比像照鏡子），骨架也是從那個檔案抽的——
     影片、骨架、Finder 裡點開看到的，全部是同一個方向，沒有隱藏的翻轉。
     舊做法是檔案不鏡像、播放時 scaleX(-1)，結果骨架被翻了兩次而跟影片相反。 */
}
#guide-canvas { pointer-events: none; }
.guide-tools {
  position: absolute; inset: auto 12px 12px auto; z-index: 3;
  display: flex; gap: 7px;
}
.pill {
  appearance: none; border: 1px solid var(--caption-line); cursor: pointer;
  font: inherit; font-size: .75rem; color: var(--caption-sub);
  padding: 5px 12px; border-radius: var(--r-pill);
  background: var(--caption-bg); backdrop-filter: blur(10px);
}
.pill[aria-pressed="true"] { color: #14161A; background: #FFFFFF; border-color: transparent; font-weight: 600; }
.guide-empty {
  position: absolute; inset: 0; z-index: 3; margin: 0;
  display: flex; align-items: center; justify-content: center;
  color: var(--caption-sub); font-size: .9375rem;
}


/* 實心按鈕（主要動作、已選的音色）在深色主題要翻過來：
   漸層寫死成深色、文字卻用會跟著主題翻轉的變數，
   在深色模式下會變成深底深字，整顆按鈕消失。 */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .btn-primary,
  :root:not([data-theme="light"]) .seg-btn.is-on {
    /* 只留文字色。底與陰影交給「玻璃珠」那組規則（--bead-ink 在深色下本來就是淺色的），
       在這裡寫死會把立體感蓋掉、又變回一塊平的色板。 */
    color: var(--ink-on);
  }
}
:root[data-theme="dark"] .btn-primary,
:root[data-theme="dark"] .seg-btn.is-on {
  color: var(--ink-on);
}


/* 深色模式：白色的元件要跟著變深，否則會是一塊發光的板子。
   --inset 與 --surface 在深色下已經重新定義，這裡處理寫死成白色的部分。 */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .chip { background: var(--surface); color: var(--text); }
  :root:not([data-theme="light"]) .chip.is-playing { background: #D7D9DE; color: #16181C; }
  /* × 的顏色在深色下不能用 --muted：疊在 12% 白的底上只有 3.97:1（門檻 4.5）。 */
  :root:not([data-theme="light"]) .chip-x { background: rgba(255,255,255,.12); color: #C3C9D2; }
}
:root[data-theme="dark"] .chip { background: var(--surface); color: var(--text); }
:root[data-theme="dark"] .chip.is-playing { background: #D7D9DE; color: #16181C; }
:root[data-theme="dark"] .chip-x { background: rgba(255,255,255,.12); color: #C3C9D2; }


/* 「關於」改成由右上角的 ⓘ 開啟的浮層。
   原本是頁尾一顆浮著的膠囊，在一屏版面裡很搶眼而且沒必要常駐。
   裡面的教材來源標示與開源授權是引用義務，不能整個刪掉，只能換位置。 */
.sheet {
  position: fixed; inset: 0; z-index: 50;
  display: grid; place-items: center;
  padding: 24px;
  background: rgba(16,18,22,.42);
  backdrop-filter: blur(3px);
}
.sheet-card {
  border: 1px solid var(--glass-line);
  width: min(560px, 100%); max-height: 78vh;
  display: flex; flex-direction: column;
  background: var(--surface); border-radius: var(--r);
  box-shadow: 0 24px 60px rgba(16,18,22,.34), 0 6px 16px rgba(16,18,22,.18);
}
.sheet-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; padding: 16px 16px 10px;
}
.sheet-head h2 { margin: 0; font-size: 1rem; font-weight: 700; }
.sheet-body {
  overflow-y: auto; padding: 0 18px 18px;
  font-size: .875rem; line-height: 1.75; color: var(--muted);
}
.sheet-body p { margin: 0 0 10px; }
.sheet-body p:last-child { margin-bottom: 0; }
.sheet-body strong { color: var(--text); }


/* ── 互動回饋 ───────────────────────────────────────
   按鈕：懸停微微浮起、按下去縮一點並把陰影收掉（真的有「按到」的感覺）、
   按下的瞬間從中心暈開一圈（純 CSS，不用 JS）。
   詞塊：辨識到一個詞的時候彈進來，不是憑空出現。
   減少動態（prefers-reduced-motion）時上面那條 * { animation:none } 會把這些全部關掉。 */
.btn, .icon-btn, .seg-btn, .chip, .chip-x, select {
  transition: transform .12s ease, box-shadow .16s ease, background-color .16s ease,
              color .16s ease, border-color .16s ease, filter .12s ease;
}
.btn { position: relative; overflow: hidden; }
.btn:hover:not(:disabled) { transform: translateY(-1px); box-shadow: 0 2px 4px rgba(18,18,24,.06), 0 8px 18px rgba(18,18,24,.10); }
.btn:active:not(:disabled) { transform: translateY(1px) scale(.985); box-shadow: 0 1px 2px rgba(18,18,24,.08); transition-duration: .06s; }
.btn-primary:hover:not(:disabled), .mode .btn.is-on:hover { box-shadow: 0 8px 20px rgba(18,18,24,.30), 0 2px 5px rgba(18,18,24,.18); }
.btn-primary:active:not(:disabled), .mode .btn.is-on:active { filter: brightness(1.15); box-shadow: 0 1px 3px rgba(18,18,24,.25); }
/* 按下時從中心暈開的一圈 */
.btn::after {
  content: ""; position: absolute; inset: 0; border-radius: inherit; pointer-events: none;
  background: radial-gradient(circle at center, rgba(20,20,26,.14), transparent 62%);
  opacity: 0; transform: scale(.6); transition: opacity .35s ease, transform .35s ease;
}
.btn:active::after { opacity: 1; transform: scale(1.6); transition: none; }
.btn-primary::after, .mode .btn.is-on::after { background: radial-gradient(circle at center, rgba(255,255,255,.22), transparent 62%); }
.btn:disabled { transform: none; }

.icon-btn:hover { transform: translateY(-1px); }
.icon-btn:active { transform: scale(.94); }
.seg-btn:active { transform: scale(.96); }
.chip-x:active { transform: scale(.88); }
select:hover { filter: brightness(.985); }
select:active { transform: scale(.995); }

/* 辨識到的詞彈進句子裡 */
@keyframes chip-pop {
  from { transform: translateY(6px) scale(.85); opacity: 0; }
  60%  { transform: translateY(-1px) scale(1.03); opacity: 1; }
  to   { transform: none; opacity: 1; }
}
.chip { animation: chip-pop .22s cubic-bezier(.2,.8,.3,1.1) both; }
.chip:active { transform: scale(.97); }

/* 「已記錄」那一下：卡片邊框亮一下再退回去 */
@keyframes badge-flash { from { box-shadow: 0 0 0 0 rgba(255,255,255,.55); } to { box-shadow: 0 0 0 10px rgba(255,255,255,0); } }
.live-badge.is-stable { animation: badge-flash .5s ease-out; }


/* ── 玻璃珠 ─────────────────────────────────────────
   所有可以按的東西都是「坐在凹槽裡的立體珠子」。
   按下去 = 珠子沉進凹槽：外陰影收掉、換成內陰影、整顆往下 1px。
   淺色深色共用同一組規則，差別只在上面那幾個變數。 */
.btn, .icon-btn, .chip {
  background: var(--bead);
  box-shadow: var(--bead-shadow);
  border-color: transparent;
}
.btn:hover:not(:disabled), .icon-btn:hover, .chip:hover {
  transform: translateY(-1px);
  box-shadow: var(--bead-shadow), 0 12px 22px rgba(20,20,26,.10);
}
.btn:active:not(:disabled), .icon-btn:active, .chip:active, .seg-btn:active {
  transform: translateY(1px) scale(.99);
  box-shadow: var(--bead-press);
  transition-duration: .06s;
}
/* 墨色珠：主按鈕與「按下中」的模式鈕 */
.btn-primary, .mode .btn.is-on, .mode .btn.is-on:hover, .chip.is-playing, .seg-btn.is-on {
  background: var(--bead-ink);
  box-shadow: var(--bead-ink-shadow);
}
.btn-primary:hover:not(:disabled), .mode .btn.is-on:hover {
  transform: translateY(-1px);
  box-shadow: var(--bead-ink-shadow), 0 14px 26px rgba(18,18,24,.22);
}
.btn-primary:active:not(:disabled), .mode .btn.is-on:active, .chip.is-playing:active, .seg-btn.is-on:active {
  transform: translateY(1px) scale(.99);
  box-shadow: var(--bead-ink-press);
  filter: none;
}
/* 凹槽：兩顆模式鈕與語音切換各坐在一條溝裡 */
.mode, .seg, .sentence-actions {
  background: var(--track-bg);
  box-shadow: var(--track-shadow);
  border: 0;
  border-radius: var(--r-pill);
  padding: 5px;
  gap: 5px;
}
.sentence-actions { border-radius: var(--r-pill); }
/* 安靜的按鈕（清除、復原）不做成珠子——它們不是主動作 */
.btn-quiet, .btn-quiet:hover { background: none; box-shadow: none; transform: none; }
.btn-quiet:active { transform: translateY(1px); box-shadow: var(--bead-press); background: var(--track-bg); }

/* 「關閉鏡頭」是隱私控制——鏡頭燈還亮著的時候，它必須找得到。
   原本跟「清除」共用最安靜的樣式，結果在深色下幾乎看不見。
   改成有外框、文字用正常色：看得見，但沒有實心底，所以還是不會搶「暫停」的戲。 */
.mode-stop {
  margin-top: 10px;
  color: var(--text);
  background: none;
  border: 1px solid var(--muted);
  box-shadow: none;
}
.mode-stop:hover { background: var(--track-bg); box-shadow: none; transform: none; border-color: var(--text); }
.mode-stop:active { transform: translateY(1px); box-shadow: var(--bead-press); background: var(--track-bg); }
/* 停用的按鈕不要立體——看得出來按不下去 */
.btn:disabled { background: none; box-shadow: inset 0 1px 3px rgba(20,20,26,.08); transform: none; }

/* 四組控制與下方右欄共用寬度。 */
.topbar-actions { gap: 6px; }
.header-tools { display: flex; align-items: center; gap: 6px; flex: 0 0 auto; }
.topbar-actions .seg { min-width: 0; height: var(--settings-row-height); padding: 4px; gap: 3px; }
.header-tools .icon-btn { width: 44px; height: var(--settings-row-height); flex: 0 0 44px; }
.topbar-actions .seg-btn { flex: 1 1 auto; align-self: stretch; white-space: nowrap; padding: 0 4px; }
.voice-choice { flex: 1 1 80px; }
.speech-choice { flex: 1.5 1 132px; }
#guide-word { font-weight: 700; }
@media (min-width: 901px) {
  .topbar-actions { width: var(--sidebar-width); flex: 0 0 var(--sidebar-width); }
}
@media (max-width: 640px) {
  .topbar { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 10px 8px; }
  .brand { grid-area: 1 / 1; gap: 6px; }
  .brand h1 { font-size: .875rem; }
  .topbar-actions { display: contents; }
  .header-tools { grid-area: 1 / 2; justify-self: end; }
  .voice-choice { grid-area: 2 / 1; }
  .speech-choice { grid-area: 2 / 2; }
  .topbar-actions .seg-btn { padding-inline: 6px; }
}

/* 設定列加上兩張卡片合計與鏡頭同高；設定列占用的高度由卡片區扣除。 */
@media (min-width: 901px) {
  .side { width: var(--sidebar-width); height: calc(var(--viewport-height, 100%) - var(--settings-row-height) - var(--settings-row-gap)); max-height: 100%; justify-self: end; align-self: center; display: grid; grid-template-rows: minmax(max-content, 1fr) minmax(150px, 3fr); }
  .side > .guide, .side > .sentence { min-height: 0; }
  .side > .guide { display: flex; flex-direction: column; justify-content: safe center; padding-block: 20px; }
  .side > .guide .mode { margin-top: 12px; }
  .side .sentence .strip { min-height: 0; }
}
