* { box-sizing: border-box; }

body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
  /* Not `normal`: CJK falls back to a font with taller metrics, so the layout
     would grow when the language changes. 1.25 is about `normal` in English. */
  line-height: 1.25;
  background: #0a0a0a;
  color: #f5f5f5;
  /* 100vh on iOS Safari returns the "large viewport" — the height with URL
     bar / tab bar hidden — so on first load the body extends below the
     actually-visible area and the bottom of the settings panel gets clipped
     behind the iOS chrome. 100dvh tracks the chrome state dynamically.
     100vh kept as a fallback for browsers that don't support dvh. */
  height: 100vh;
  height: 100dvh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  /* Safe-area insets push content out of notch / Dynamic Island / home
     indicator zones when running as a fullscreen PWA. Body-level handles
     left+right (landscape notch); top/bottom are handled per-edge below. */
  padding-left: env(safe-area-inset-left);
  padding-right: env(safe-area-inset-right);
}

/* ───────── Controls bar ───────── */

#controls {
  flex: 0 0 auto;
  /* padding-top respects iOS status bar / Dynamic Island intrusion in
     PWA fullscreen mode. Fallback to the original 8px when no inset. */
  padding: max(8px, env(safe-area-inset-top)) 14px 8px;
  display: flex;
  gap: 10px;
  align-items: center;
  border-bottom: 1px solid #222;
}

.spacer { flex: 1; }

/* Form controls don't inherit line-height by default. */
button, input, select, textarea { line-height: inherit; }

button {
  padding: 6px 12px;
  background: #2563eb;
  color: #e8e8e8;
  border: 0;
  border-radius: 6px;
  cursor: pointer;
  font-size: 13px;
  font-family: inherit;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  white-space: nowrap;
}
button:hover { background: #1d4ed8; }
button.recording { background: #991b1b; }
button.recording:hover { background: #7f1d1d; }
button.secondary {
  background: transparent;
  color: #888;
  border: 1px solid #2a2a2a;
}
button.secondary:hover { background: #1a1a1a; color: #ccc; }

/* Uniform icon size + tight line-height across all toolbar buttons.
   With labels hidden (narrow / subtitle mode), each button's height ends
   up determined solely by `.icon` line-box — so they must match or the
   buttons render at different heights. */
button .icon { line-height: 1; }
/* `display: inline-flex` above would otherwise beat the hidden attribute. */
#pip[hidden] { display: none; }
#pip.active { color: #60a5fa; border-color: #1e3a8a; }

#input-source,
#asr-backend,
#translate-backend {
  background: #161616;
  color: #ddd;
  border: 1px solid #2a2a2a;
  border-radius: 6px;
  padding: 5px 8px;
  font-size: 13px;
  font-family: inherit;
  cursor: pointer;
}
#input-source:disabled,
#asr-backend:disabled,
#translate-backend:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

/* Source icon over the select's left padding; clicks go through to it. */
#input-source-picker {
  position: relative;
  display: inline-flex;
  align-items: center;
  color: #ddd;
}
#input-source-picker .icon {
  position: absolute;
  left: 9px;
  font-size: 13px;
  pointer-events: none;
}
#input-source-picker:not([data-source="mic"]) .icon-mic,
#input-source-picker:not([data-source="screen"]) .icon-screen { display: none; }
#input-source-picker:has(select:disabled) .icon { opacity: 0.5; }
#input-source { padding-left: 26px; }

#status {
  color: #999;
  font-size: 13px;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

#vu {
  width: 80px;
  height: 8px;
  background: #1a1a1a;
  border-radius: 4px;
  overflow: hidden;
  flex-shrink: 0;
}
#vu-fill {
  height: 100%;
  width: 0%;
  background: linear-gradient(90deg, #16a34a 0%, #16a34a 60%, #f59e0b 75%, #dc2626 95%);
  transition: width 60ms linear;
}

.badge {
  font-size: 11px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  color: #888;
  background: #161616;
  border: 1px solid #262626;
  padding: 3px 7px;
  border-radius: 4px;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.badge.active { color: #f5f5f5; border-color: #2563eb; }
.badge.warn   { color: #fbbf24; border-color: #b45309; }
.badge.error  { color: #ef4444; border-color: #991b1b; }
/* Diagnostic badges (mic peak / asr session / translation counter) are
   hidden by default — useful for development but visual noise for most
   users. Settings → "Debug mode" toggles body.debug-mode to reveal them. */
body:not(.debug-mode) #controls .badge { display: none; }

/* Source-language input: <select> in normal mode (11-language intersection
   of all hint-accepting backends), free-text <input> with extra suggestions
   in debug mode (qwen-local's extra langs, Whisper's 70+ pass-through). */
body:not(.debug-mode) #source-lang-input { display: none; }
body.debug-mode #source-lang-select { display: none; }

/* ───────── Settings panel (two-column grid) ───────── */

/* Settings-open is a page-like view: the panel fills the remaining
   viewport, caption-stack + history are hidden. The `body.settings-open`
   class is toggled by JS alongside the panel's `hidden` attribute.
   The visible card is .settings-form, top-aligned at a fixed readable
   width. It never outgrows the panel: when the viewport is too short, the
   tab bar stays put and the active tab scrolls inside the card. */
#settings-panel {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  padding: 20px 12px;
}
#settings-panel[hidden] { display: none; }
body.settings-open #caption-stack,
body.settings-open #history { display: none; }

.settings-form {
  width: 100%;
  max-width: 680px;
  min-height: 0;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  border: 1px solid #222;
  border-radius: 10px;
  background: #0f0f0f;
  padding: 6px 20px 20px;
}

.settings-tabs {
  flex: none;
  display: flex;
  gap: 4px;
  border-bottom: 1px solid #222;
  margin-bottom: 18px;
  overflow-x: auto;
}
.settings-tabs button {
  background: transparent;
  color: #888;
  border-radius: 0;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  padding: 10px 12px;
  font-size: 13px;
}
.settings-tabs button:hover { background: transparent; color: #ccc; }
.settings-tabs button.active { color: #f0f0f0; border-bottom-color: #2563eb; }

/* All tabs share one grid cell, so the card keeps the tallest tab's height
   and doesn't resize when switching tabs. Inactive tabs are
   visibility:hidden, which also takes them out of focus and clicks.
   When the card is squeezed, the 1fr row takes the space left and each tab
   scrolls on its own, so a short tab has nothing to scroll. */
.settings-tab-body {
  flex: 0 1 auto;
  min-height: 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: minmax(0, 1fr);
}
.settings-tab {
  grid-area: 1 / 1;
  min-height: 0;
  overflow-y: auto;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 18px 16px;
  align-content: start;
}
.settings-tab:not(.active) { visibility: hidden; }

#settings-panel .row {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
}
#settings-panel .row.span2 { grid-column: 1 / -1; }
/* The display rules above (and button's inline-flex) would otherwise beat
   the hidden attribute. */
#settings-panel [hidden] { display: none; }

#settings-panel label {
  color: #ddd;
  font-size: 13px;
  font-weight: 500;
}
#settings-panel .optional {
  margin-left: 4px;
  color: #666;
  font-size: 11px;
  font-weight: normal;
}
#settings-panel .hint {
  margin: 0;
  color: #777;
  font-size: 12px;
  font-weight: normal;
  line-height: 1.45;
}
#settings-panel .muted { color: #666; font-weight: normal; }

#settings-panel input,
#settings-panel textarea,
#settings-panel select {
  background: #161616;
  color: #f0f0f0;
  border: 1px solid #2a2a2a;
  border-radius: 6px;
  padding: 7px 10px;
  font-family: inherit;
  font-size: 13px;
  line-height: 1.4;
  resize: vertical;
}
#settings-panel input::placeholder,
#settings-panel textarea::placeholder { color: #555; }
/* Native <select> rendering adds extra vertical padding for the dropdown
   arrow chrome, making it visibly taller than sibling <input>s even with
   matching padding. Strip the native widget and draw our own chevron so all
   three selects (asr-backend / translate-backend / source-lang-select) line
   up with the inputs. Inline SVG keeps it self-contained. */
#settings-panel select {
  appearance: none;
  -webkit-appearance: none;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'><path d='M1 1l4 4 4-4' stroke='%23888' stroke-width='1.5' fill='none' stroke-linecap='round' stroke-linejoin='round'/></svg>");
  background-repeat: no-repeat;
  background-position: right 10px center;
  padding-right: 28px;
}
#settings-panel input:focus,
#settings-panel textarea:focus,
#settings-panel select:focus {
  outline: none;
  border-color: #2563eb;
}
#settings-panel input:disabled,
#settings-panel textarea:disabled,
#settings-panel select:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}

.hint-scene-row {
  display: flex;
  gap: 8px;
  align-items: stretch;
}
.hint-scene-row input {
  flex: 1;
  min-width: 0;
}
.hint-scene-row button {
  white-space: nowrap;
  font-size: 12px;
  padding: 4px 12px;
}

#hint-status { font-size: 12px; }
#hint-status:empty { display: none; }
#hint-status.ok { color: #16a34a; }
#hint-status.err { color: #ef4444; }
#hint-status.busy { color: #fbbf24; }

#settings-panel .asr-model-hint {
  display: flex;
  align-items: center;
  gap: 8px;
  color: #fbbf24;
}
#settings-panel .asr-model-hint.err { color: #ef4444; }
#settings-panel .asr-model-hint.info { color: #777; }
#settings-panel .asr-model-hint[hidden] { display: none; }
.asr-model-hint button {
  font-size: 12px;
  padding: 3px 10px;
}

.slider-row {
  display: flex;
  align-items: center;
  gap: 10px;
}
.slider-end {
  color: #666;
  font-size: 11px;
  white-space: nowrap;
}
/* Thin dark track instead of the native bright one, which stood out against
   the dark cards. One color: it's a trade-off between two ends, not an amount. */
#settings-panel input[type="range"] {
  -webkit-appearance: none;
  appearance: none;
  flex: 1;
  min-width: 0;
  height: 4px;
  padding: 0;
  border: 0;
  border-radius: 2px;
  background: #333;
  cursor: pointer;
}
#settings-panel input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: #e5e5e5;
  box-shadow: 0 0 0 3px #141414;
}
#settings-panel input[type="range"]::-moz-range-thumb {
  width: 12px;
  height: 12px;
  border: 0;
  border-radius: 50%;
  background: #e5e5e5;
  box-shadow: 0 0 0 3px #141414;
}
#settings-panel input[type="range"]:disabled { opacity: 0.5; }

#settings-panel .context-card {
  gap: 18px;
  padding: 14px;
  border: 1px solid #222;
  border-radius: 8px;
  background: #141414;
}
#settings-panel .context-note { color: #f59e0b; }

/* Session fields the picked backends don't use. */
#settings-panel .row.unused { opacity: 0.45; }
#settings-panel .row.unused :disabled { opacity: 1; }

/* A titled group inside a tab, divided from the fields above it. */
#settings-panel .settings-section {
  margin-top: 6px;
  padding-top: 18px;
  border-top: 1px solid #222;
}
#settings-panel .section-title {
  margin: 0;
  color: #ddd;
  font-size: 13px;
  font-weight: 600;
}
.model-chunk {
  grid-column: 1 / -1;
  margin-top: 8px;
}
.slider-value {
  min-width: 3.2em;
  color: #ddd;
  font-size: 12px;
  text-align: right;
  font-variant-numeric: tabular-nums;
}
#models-list,
#mt-models-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.model-item {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas:
    "name   btn"
    "status btn"
    "bar    bar";
  align-items: center;
  gap: 2px 12px;
  padding: 10px 12px;
  border: 1px solid #222;
  border-radius: 8px;
  background: #141414;
}
.model-name { grid-area: name; color: #eee; font-size: 13px; font-weight: 500; }
.model-status {
  grid-area: status;
  color: #777;
  font-size: 12px;
  overflow-wrap: anywhere;
}
#settings-panel .model-status.err { color: #ef4444; }
.model-item button {
  grid-area: btn;
  font-size: 12px;
  padding: 5px 12px;
}
.model-bar {
  grid-area: bar;
  margin-top: 8px;
  height: 4px;
  border-radius: 2px;
  background: #222;
  overflow: hidden;
}
.model-bar > div {
  height: 100%;
  width: 0;
  background: #2563eb;
  transition: width 0.3s;
}
#models-dir {
  margin-top: 4px;
  font-size: 11px;
  color: #555;
  overflow-wrap: anywhere;
}

.toggle-row {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  cursor: pointer;
}
.toggle-row > span {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
#settings-panel .toggle-row input[type="checkbox"] {
  margin: 2px 0 0;
  width: 15px;
  height: 15px;
  accent-color: #2563eb;
  cursor: pointer;
}
.toggle-title { color: #ddd; }

/* ───────── Caption stack (current + prev) ───────── */

/* Two zones, same size, stacked at the bottom of the viewport. Prev =
   just-finalized (dim, no badge) on top; Current = LIVE (blue tint + LIVE
   badge) at the very bottom. When a sentence finalizes, prev gets the new
   content with a slide-up-from-below animation, looking like the LIVE
   card "scrolled up" into the prev slot. */
#caption-stack {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  overflow: hidden;   /* clip prev's translateY(100%) start state during animation */
  margin-top: auto;
  /* Home indicator (iPhone) sits at the bottom and can overlap the LIVE
     caption when running as a fullscreen PWA. Push the stack up by that
     inset. The current caption-zone's own bottom padding is on top of this. */
  padding-bottom: env(safe-area-inset-bottom);
}

.caption-zone {
  padding: 14px 16px 16px;
  position: relative;
}

/* CURRENT (LIVE) zone */
.caption-zone.live {
  background: linear-gradient(180deg, #0e1422 0%, #0c111c 100%);
  border-bottom: 1px solid #1a1a1a;
}
.caption-zone.live::before {
  content: "LIVE";
  position: absolute;
  top: 6px;
  right: 12px;
  font-size: 9px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  color: #60a5fa;
  letter-spacing: 1.5px;
  font-weight: 600;
  opacity: 0.7;
}

/* PREV zone — same size as current but dim and no LIVE badge. */
.caption-zone.prev {
  background: #0a0d12;
  border-bottom: 1px solid #1a1a1a;
}
.caption-zone.prev .translation {
  color: #d8d8d8;   /* a touch dimmer than live's pure white */
}

.caption-zone[hidden] { display: none; }

/* The slide-in: prev starts visually at current's position (translateY 100%
   of its own height — i.e., one slot below, where the LIVE card sits) and
   settles up into its slot, while morphing from the live blue gradient to
   the dim prev background. Looks like a card sliding up from below. */
.caption-zone.prev.entering {
  /* z-index lifts prev above current during slide-in: prev starts at
     translateY(100%) which lands it at current's position, and without
     z-index current (next sibling, naturally on top) would obscure it. */
  z-index: 2;
  animation: prev-shift-in 350ms cubic-bezier(0.2, 0.6, 0.2, 1);
}
@keyframes prev-shift-in {
  from {
    transform: translateY(100%);
    background: linear-gradient(180deg, #0e1422 0%, #0c111c 100%);
  }
  to {
    transform: translateY(0);
    background: #0a0d12;
  }
}

.line { line-height: 1.4; word-wrap: break-word; }

.transcript {
  font-size: 14px;
  color: #94a3b8;
  margin-bottom: 6px;
  min-height: 1.4em;
}

.latest {
  margin: 8px 0 0;
  padding-top: 6px;
  border-top: 1px dashed #1f2937;
  opacity: 0.85;
}
.latest[hidden] { display: none; }

.translation {
  font-size: 28px;
  color: #e8e8e8;
  font-weight: 600;
  min-height: 1.4em;
  letter-spacing: 0.01em;
  line-height: 1.35;
}

/* Passthrough mode: the server mirrors src → dst, so the small transcript
   line and the big translation line would carry identical text. Hide the
   redundant transcript so the user sees the source as one prominent line. */
body[data-translate-backend="none"] .transcript,
body[data-translate-backend="none"] .history-item .src {
  display: none;
}

/* ───────── Pop-out (Document Picture-in-Picture) ───────── */

/* The PiP window loads this stylesheet too; the main page shows a note in
   the stack's place. */
#pip-note {
  flex: 0 0 auto;
  margin-top: auto;
  padding: 14px 16px calc(14px + env(safe-area-inset-bottom));
  color: #777;
  font-size: 13px;
  display: flex;
  align-items: center;
  gap: 12px;
}
#pip-note[hidden] { display: none; }
body.welcome-shown #pip-note { display: none; }

/* ───────── History (takes the rest, scrollable) ───────── */

/* History is the ARCHIVED region. Recessed background + a soft top
   inset shadow give it a "dropped" look relative to the captions card. */
#history {
  flex: 1 1 0;
  min-height: 0;
  overflow-y: auto;
  padding: 8px 16px 16px;
  background: #050505;
  box-shadow: inset 0 6px 8px -6px rgba(0, 0, 0, 0.7);
  /* Normal column + append() keeps DOM order chronological, so selecting
     top-to-bottom and copying produces oldest→newest in the clipboard
     (column-reverse + prepend used to invert that). margin-top:auto on
     the first item anchors the stack to the bottom — items grow upward
     until they overflow, at which point JS scrolls to the bottom on
     append. Avoids the Chrome justify-content:flex-end overflow clip bug. */
  display: flex;
  flex-direction: column;
}
#history > .history-item:first-child { margin-top: auto; }

.history-item {
  padding: 7px 0;
  border-bottom: 1px solid #161616;
  opacity: 0.85;
}
.history-item:last-child { border-bottom: 0; }
.history-item .src {
  color: #555;
  font-size: 12px;
  margin-bottom: 2px;
  line-height: 1.4;
  word-wrap: break-word;
}
.history-item .dst {
  color: #a8a8a8;
  font-size: 14px;
  line-height: 1.4;
  word-wrap: break-word;
  font-weight: 400;
  transition: background-color 1000ms ease-out, color 1000ms ease-out, box-shadow 1000ms ease-out;
  border-radius: 3px;
  padding: 1px 4px;
  margin: 0 -4px;
}

/* prev_revised highlight — when paired-translation revises a sentence,
   we briefly tint its translation so the user notices the change. The
   target may be in the dedicated PREV zone (most common) or in the
   history list (if it's already been pushed out of prev). Same look in
   both, just selected via different elements in JS. */
.caption-zone.prev .translation.revised,
.history-item .dst.revised {
  background-color: rgba(37, 99, 235, 0.55);
  color: #fff;
  box-shadow: 0 0 0 1px rgba(96, 165, 250, 0.7);
  border-radius: 3px;
  padding: 1px 4px;
  margin: 0 -4px;
  transition: none;
}
.caption-zone.prev .translation.revised-faded,
.history-item .dst.revised-faded {
  background-color: rgba(37, 99, 235, 0.18);
  color: #fff;
  border-radius: 3px;
  padding: 1px 4px;
  margin: 0 -4px;
  transition: background-color 1000ms ease-out, color 1000ms ease-out;
}

/* ───────── Large caption mode ───────── */

/* Toggled by Settings → "Large captions". JS picks the default once from
   UA (mobile/tablet → on, desktop → off); after that localStorage wins.
   Bigger fonts for stand-mode use (phone on desk, watching from 1-2m). */
body.font-large .translation { font-size: 38px; }
body.font-large .transcript { font-size: 16px; }
body.font-large .caption-zone { padding: 18px 18px 20px; }
body.font-large .history-item .dst { font-size: 16px; }

/* ───────── Welcome card (idle, nothing captioned yet) ───────── */

/* Takes the history + caption area until the first caption, so a new user
   sees what to do (or what's missing) before pressing Start. */
#welcome {
  flex: 1 1 0;
  min-height: 0;
  overflow-y: auto;
  padding: 24px 16px;
  display: flex;
  flex-direction: column;
}
#welcome[hidden],
body.settings-open #welcome { display: none; }
body.welcome-shown #history,
body.welcome-shown #caption-stack { display: none; }

.welcome-card {
  margin: auto;
  width: 100%;
  max-width: 560px;
  border: 1px solid #222;
  border-radius: 10px;
  background: #0f0f0f;
  padding: 20px 22px;
  color: #ccc;
  font-size: 14px;
  line-height: 1.55;
}
.welcome-card h2 {
  margin: 0 0 10px;
  color: #f0f0f0;
  font-size: 17px;
  font-weight: 600;
}
.welcome-card p { margin: 0 0 10px; }
.welcome-card p:last-child { margin-bottom: 0; }
.welcome-card ol,
.welcome-card ul { margin: 0 0 12px; padding-left: 20px; }
.welcome-card li { margin: 4px 0; }
.welcome-card b { color: #eee; font-weight: 600; }
.welcome-card code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12.5px;
  color: #ddd;
  background: #1a1a1a;
  padding: 1px 5px;
  border-radius: 4px;
}
.welcome-card .muted { color: #777; font-size: 13px; }
.welcome-models {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.welcome-models + p { margin-top: 12px; }
.welcome-card .model-bar[hidden] { display: none; }
.welcome-card .model-status.err { color: #ef4444; }
.welcome-card .tag {
  margin-left: 8px;
  color: #60a5fa;
  font-size: 11px;
  font-weight: normal;
}
.welcome-card .link {
  padding: 0;
  border: 0;
  background: none;
  color: #60a5fa;
  font-size: inherit;
  display: inline;
}
.welcome-card .link:hover { background: none; text-decoration: underline; }

@media (max-height: 350px) {
  #welcome { display: none; }
  body.welcome-shown #caption-stack { display: flex; }
}

/* ───────── History auto-hide when there's no room for ≥2 rows ───────── */

/* Below these thresholds the rest of the layout (controls + caption stack)
   eats so much of the viewport that history can't show even 2 useful
   rows. Per-mode thresholds: Large mode's bigger captions eat more, so
   it needs more total height before history is worth showing. */
@media (max-height: 350px) {
  #history { display: none; }
}
@media (max-height: 450px) {
  body.font-large #history { display: none; }
}

/* ───────── Subtitle mode (≤200px height): extreme override ───────── */

/* Used for "place this window as a subtitle strip under a video" — height
   is the limiting factor and even Small font is too tall. Force a compact
   set of values regardless of .font-large. */
@media (max-height: 200px) {
  #controls { padding: 4px 10px; gap: 6px; }
  #controls #input-source { padding: 4px 6px 4px 24px; font-size: 12px; }
  #input-source-picker .icon { left: 7px; font-size: 12px; }
  #status { font-size: 12px; }
  #vu { width: 50px; height: 6px; }
  .caption-zone { padding: 9px 14px; background: transparent; }
  .caption-zone.live::before { display: none; }
  /* Override both the default and the Large-mode font sizes — these are
     more specific than `body.font-large` by source order (defined later). */
  .transcript,
  body.font-large .transcript { font-size: 13px; margin-bottom: 4px; }
  .translation,
  body.font-large .translation { font-size: 20px; }
  #settings-panel { padding: 6px 8px; }
  .settings-form { padding: 2px 10px 10px; }
  .settings-tabs { margin-bottom: 8px; }
  .settings-tab { gap: 8px 10px; }
}

/* ───────── Responsive: narrow width ───────── */

@media (max-width: 600px) {
  #settings-panel { padding: 12px 8px; }
  .settings-form { padding: 4px 14px 16px; }
  .settings-tab { grid-template-columns: minmax(0, 1fr); }
  .settings-tab .row.span2 { grid-column: 1; }
  #controls .badge { font-size: 10px; padding: 2px 5px; }
}

/* ───────── Responsive: icon-only toolbar (narrow OR very short) ───────── */

/* On narrow screens (real iOS Safari + "Larger Text" makes "Settings"/
   "Clear" labels push controls off the viewport) AND on very short
   screens (subtitle mode), collapse Start / Settings / Clear buttons to
   their leading icon. */
@media (max-width: 600px), (max-height: 200px) {
  #toggle .label,
  #settings-toggle .label,
  #pip .label,
  #clear .label { display: none; }
  /* Glyphs and SVG icons differ in height; pin the buttons to their natural
     labelled height (6×2 padding + ~13px line). */
  #controls button { min-height: 28px; }
}
